pax_global_header00006660000000000000000000000064135650536040014521gustar00rootroot0000000000000052 comment=30e7c225cd510400eacd41d0a83e013b835a8ece z3-z3-4.8.7/000077500000000000000000000000001356505360400124275ustar00rootroot00000000000000z3-z3-4.8.7/.dockerignore000066400000000000000000000000471356505360400151040ustar00rootroot00000000000000**/*.swp **/*.pyc .git **/*.Dockerfile z3-z3-4.8.7/.gitattributes000066400000000000000000000002121356505360400153150ustar00rootroot00000000000000# Set default behaviour, in case users don't have core.autocrlf set. * text=auto src/api/dotnet/Properties/AssemblyInfo.cs text eol=crlf z3-z3-4.8.7/.gitignore000066400000000000000000000025731356505360400144260ustar00rootroot00000000000000*~ *.pyc *.pyo # Ignore callgrind files callgrind.out.* # .hpp files are automatically generated *.hpp .z3-trace # OCaml generated files *.a *.cma *.cmo *.cmi *.cmxa ocamlz3 # Java generated files *.class *.jar # Emacs temp files \#*\# # Directories with generated code and documentation release/* build/* build-dist/* dist/* doc/html/* # GTAGS generated files src/GPATH src/GRTAGS src/GSYMS src/GTAGS src/HTML/* # CSCOPE files src/cscope.in.out src/cscope.out src/cscope.po.out ncscope.out # CEDET files .cproject .project # Commonly used directories for code bld_dbg/* bld_rel/* bld_dbg_x64/* bld_rel_x64/* .vscode # Auto generated files. config.log config.status install_tactic.cpp mem_initializer.cpp gparams_register_modules.cpp scripts/config-debug.mk scripts/config-release.mk src/api/api_commands.cpp src/api/api_log_macros.h src/api/api_log_macros.cpp src/api/dll/api_dll.def src/api/dotnet/Enumerations.cs src/api/dotnet/Native.cs src/api/dotnet/Properties/AssemblyInfo.cs src/api/dotnet/Microsoft.Z3.xml src/api/python/z3/z3consts.py src/api/python/z3/z3core.py src/ast/pattern/database.h src/util/version.h src/api/java/Native.cpp src/api/java/Native.java src/api/java/enumerations/*.java src/api/ml/z3native_stubs.c src/api/ml/z3native.ml src/api/ml/z3enums.ml src/api/ml/z3native.mli src/api/ml/z3enums.mli src/api/ml/z3.mllib *.bak doc/api doc/code .vs examples/**/obj CMakeSettings.json z3-z3-4.8.7/.travis.yml000066400000000000000000000117611356505360400145460ustar00rootroot00000000000000cache: # This persistent cache is used to cache the building of # docker base images. directories: - $DOCKER_TRAVIS_CI_CACHE_DIR sudo: required language: cpp services: - docker env: global: # This environment variable tells the `travis_ci_linux_entry_point.sh` # script to look for a cached Docker image. - DOCKER_TRAVIS_CI_CACHE_DIR=$HOME/.cache/docker # Configurations matrix: ############################################################################### # Ubuntu 16.04 LTS ############################################################################### # 64-bit UBSan Debug build - LINUX_BASE=ubuntu_16.04 C_COMPILER=/usr/bin/clang-3.9 CXX_COMPILER=/usr/bin/clang++-3.9 TARGET_ARCH=x86_64 Z3_BUILD_TYPE=Debug UBSAN_BUILD=1 RUN_UNIT_TESTS=SKIP # 64-bit ASan Debug build - LINUX_BASE=ubuntu_16.04 C_COMPILER=/usr/bin/clang-3.9 CXX_COMPILER=/usr/bin/clang++-3.9 TARGET_ARCH=x86_64 Z3_BUILD_TYPE=Debug ASAN_BUILD=1 RUN_UNIT_TESTS=SKIP ASAN_DSO=/usr/lib/clang/3.9/lib/linux/libclang_rt.asan-x86_64.so # Build for running unit tests under ASan/UBSan # FIXME: We should really be doing a debug build but the unit tests run too # slowly when we do that. - LINUX_BASE=ubuntu_16.04 C_COMPILER=/usr/bin/clang-3.9 CXX_COMPILER=/usr/bin/clang++-3.9 TARGET_ARCH=x86_64 Z3_BUILD_TYPE=RelWithDebInfo ASAN_BUILD=1 RUN_UNIT_TESTS=BUILD_AND_RUN ASAN_DSO=/usr/lib/clang/3.9/lib/linux/libclang_rt.asan-x86_64.so UBSAN_BUILD=1 RUN_API_EXAMPLES=0 RUN_SYSTEM_TESTS=0 DOTNET_BINDINGS=0 JAVA_BINDINGS=0 PYTHON_BINDINGS=0 # 64-bit GCC 5.4 RelWithDebInfo - LINUX_BASE=ubuntu_16.04 C_COMPILER=/usr/bin/gcc-5 CXX_COMPILER=/usr/bin/g++-5 TARGET_ARCH=x86_64 Z3_BUILD_TYPE=RelWithDebInfo # 64-bit Clang 3.9 RelWithDebInfo - LINUX_BASE=ubuntu_16.04 C_COMPILER=/usr/bin/clang-3.9 CXX_COMPILER=/usr/bin/clang++-3.9 TARGET_ARCH=x86_64 Z3_BUILD_TYPE=RelWithDebInfo # Debug builds # # Note the unit tests for the debug builds are compiled but **not** # executed. This is because the debug build of unit tests takes a large # amount of time to execute compared to the optimized builds. The hope is # that just running the optimized unit tests is sufficient. # # 64-bit GCC 5.4 Debug - LINUX_BASE=ubuntu_16.04 C_COMPILER=/usr/bin/gcc-5 CXX_COMPILER=/usr/bin/g++-5 TARGET_ARCH=x86_64 Z3_BUILD_TYPE=Debug RUN_UNIT_TESTS=BUILD_ONLY # 64-bit Clang Debug - LINUX_BASE=ubuntu_16.04 C_COMPILER=/usr/bin/clang-3.9 CXX_COMPILER=/usr/bin/clang++-3.9 TARGET_ARCH=x86_64 Z3_BUILD_TYPE=Debug RUN_UNIT_TESTS=BUILD_ONLY # 32-bit GCC 5.4 RelWithDebInfo - LINUX_BASE=ubuntu32_16.04 C_COMPILER=/usr/bin/gcc-5 CXX_COMPILER=/usr/bin/g++-5 TARGET_ARCH=i686 Z3_BUILD_TYPE=RelWithDebInfo # Both of the two configurations below build the docs because the current # implementation uses python as part of the building process. # TODO: Teach one of the configurations to upload built docs somewhere. # Test with Python 3 and API docs - LINUX_BASE=ubuntu_16.04 PYTHON_EXECUTABLE=/usr/bin/python3 BUILD_DOCS=1 # Test with LibGMP and API docs - LINUX_BASE=ubuntu_16.04 TARGET_ARCH=x86_64 USE_LIBGMP=1 BUILD_DOCS=1 PYTHON_EXECUTABLE=/usr/bin/python2.7 # Unix Makefile generator build - LINUX_BASE=ubuntu_16.04 C_COMPILER=/usr/bin/gcc-5 CXX_COMPILER=/usr/bin/g++-5 TARGET_ARCH=x86_64 Z3_CMAKE_GENERATOR="Unix Makefiles" # LTO build - LINUX_BASE=ubuntu_16.04 C_COMPILER=/usr/bin/gcc-5 CXX_COMPILER=/usr/bin/g++-5 TARGET_ARCH=x86_64 USE_LTO=1 # Static build. Note we have disable building the bindings because they won't work with a static libz3 - LINUX_BASE=ubuntu_16.04 C_COMPILER=/usr/bin/gcc-5 CXX_COMPILER=/usr/bin/g++-5 TARGET_ARCH=x86_64 Z3_STATIC_BUILD=1 DOTNET_BINDINGS=0 JAVA_BINDINGS=0 PYTHON_BINDINGS=0 ############################################################################### # Ubuntu 14.04 LTS ############################################################################### # GCC 4.8 # 64-bit GCC 4.8 RelWithDebInfo - LINUX_BASE=ubuntu_14.04 C_COMPILER=/usr/bin/gcc-4.8 CXX_COMPILER=/usr/bin/g++-4.8 TARGET_ARCH=x86_64 Z3_BUILD_TYPE=RelWithDebInfo # 64-bit GCC 4.8 Debug - LINUX_BASE=ubuntu_14.04 C_COMPILER=/usr/bin/gcc-4.8 CXX_COMPILER=/usr/bin/g++-4.8 TARGET_ARCH=x86_64 Z3_BUILD_TYPE=Debug RUN_UNIT_TESTS=BUILD_ONLY # macOS (a.k.a OSX) support matrix: include: # For now just test a single configuration. macOS builds on TravisCI are # very slow so we should keep the number of configurations we test on this # OS to a minimum. - os: osx osx_image: xcode8.3 env: Z3_BUILD_TYPE=RelWithDebInfo DOTNET_BINDINGS=0 script: # Use `travis_wait` when doing LTO builds because this configuration will # have long link times during which it will not show any output which # TravisCI might kill due to perceived inactivity. - if [ "X${USE_LTO}" = "X1" ]; then travis_wait 55 contrib/ci/scripts/travis_ci_entry_point.sh || exit 1; else contrib/ci/scripts/travis_ci_entry_point.sh || exit 1; fi z3-z3-4.8.7/CMakeLists.txt000066400000000000000000000655121356505360400152000ustar00rootroot00000000000000# Enforce some CMake policies cmake_minimum_required(VERSION 3.4) if (POLICY CMP0054) cmake_policy(SET CMP0054 NEW) endif() if (POLICY CMP0042) # Enable `MACOSX_RPATH` by default. cmake_policy(SET CMP0042 NEW) endif() set(CMAKE_USER_MAKE_RULES_OVERRIDE_CXX "${CMAKE_CURRENT_SOURCE_DIR}/cmake/cxx_compiler_flags_overrides.cmake") project(Z3 VERSION 4.8.7.0 LANGUAGES CXX) ################################################################################ # Project version ################################################################################ set(Z3_FULL_VERSION_STR "${Z3_VERSION}") # Note this might be modified message(STATUS "Z3 version ${Z3_VERSION}") ################################################################################ # Set various useful variables depending on CMake version ################################################################################ set(ADD_CUSTOM_COMMAND_USES_TERMINAL_ARG "USES_TERMINAL") set(ADD_CUSTOM_TARGET_USES_TERMINAL_ARG "USES_TERMINAL") ################################################################################ # Message for polluted source tree sanity checks ################################################################################ set(z3_polluted_tree_msg " should not exist and is polluting the source tree." " It is likely that this file came from the Python build system which" " generates files inside the source tree. This is bad practice and the CMake" " build system is setup to make sure that the source tree is clean during" " its configure step. If you are using git you can remove all untracked files" " using ``git clean -fx``. Be careful when doing this. You should probably use" " this with ``-n`` first to check which file(s) would be removed." ) ################################################################################ # Sanity check - Disallow building in source ################################################################################ if ("${PROJECT_SOURCE_DIR}" STREQUAL "${PROJECT_BINARY_DIR}") message(FATAL_ERROR "In source builds are not allowed. You should invoke " "CMake from a different directory.") endif() ################################################################################ # Add our CMake module directory to the list of module search directories ################################################################################ list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/modules") ################################################################################ # Handle git hash and description ################################################################################ include(${PROJECT_SOURCE_DIR}/cmake/git_utils.cmake) macro(disable_git_describe) message(WARNING "Disabling Z3_INCLUDE_GIT_DESCRIBE") set(Z3_INCLUDE_GIT_DESCRIBE OFF CACHE BOOL "Include git describe output in version output" FORCE) endmacro() macro(disable_git_hash) message(WARNING "Disabling Z3_INCLUDE_GIT_HASH") set(Z3_INCLUDE_GIT_HASH OFF CACHE BOOL "Include git hash in version output" FORCE) unset(Z3GITHASH) # Used in configure_file() endmacro() option(Z3_INCLUDE_GIT_HASH "Include git hash in version output" ON) option(Z3_INCLUDE_GIT_DESCRIBE "Include git describe output in version output" ON) set(GIT_DIR "${PROJECT_SOURCE_DIR}/.git") if (EXISTS "${GIT_DIR}") # Try to make CMake configure depend on the current git HEAD so that # a re-configure is triggered when the HEAD changes. add_git_dir_dependency("${PROJECT_SOURCE_DIR}" ADD_GIT_DEP_SUCCESS) if (ADD_GIT_DEP_SUCCESS) if (Z3_INCLUDE_GIT_HASH) get_git_head_hash("${GIT_DIR}" Z3GITHASH) if (NOT Z3GITHASH) message(WARNING "Failed to get Git hash") disable_git_hash() endif() message(STATUS "Using Git hash in version output: ${Z3GITHASH}") # This mimics the behaviour of the old build system. set(Z3_FULL_VERSION_STR "${Z3_FULL_VERSION_STR} ${Z3GITHASH}") else() message(STATUS "Not using Git hash in version output") unset(Z3GITHASH) # Used in configure_file() endif() if (Z3_INCLUDE_GIT_DESCRIBE) get_git_head_describe("${GIT_DIR}" Z3_GIT_DESCRIPTION) if (NOT Z3_GIT_DESCRIPTION) message(WARNING "Failed to get Git description") disable_git_describe() endif() message(STATUS "Using Git description in version output: ${Z3_GIT_DESCRIPTION}") # This mimics the behaviour of the old build system. set(Z3_FULL_VERSION_STR "${Z3_FULL_VERSION_STR} ${Z3_GIT_DESCRIPTION}") else() message(STATUS "Not including git descrption in version") endif() else() message(WARNING "Failed to add git dependency.") disable_git_describe() disable_git_hash() endif() else() message(STATUS "Failed to find git directory.") disable_git_describe() disable_git_hash() endif() ################################################################################ # Useful CMake functions/Macros ################################################################################ include(CheckCXXSourceCompiles) ################################################################################ # Compiler flags for Z3 components. # Subsequent commands will append to this ################################################################################ set(Z3_COMPONENT_CXX_DEFINES "") set(Z3_COMPONENT_CXX_FLAGS "") set(Z3_COMPONENT_EXTRA_INCLUDE_DIRS "") set(Z3_DEPENDENT_LIBS "") set(Z3_DEPENDENT_EXTRA_CXX_LINK_FLAGS "") ################################################################################ # Build type ################################################################################ message(STATUS "CMake generator: ${CMAKE_GENERATOR}") set(available_build_types Debug Release RelWithDebInfo MinSizeRel) if (DEFINED CMAKE_CONFIGURATION_TYPES) # Multi-configuration build (e.g. Visual Studio and Xcode). Here # CMAKE_BUILD_TYPE doesn't matter message(STATUS "Available configurations: ${CMAKE_CONFIGURATION_TYPES}") else() # Single configuration generator (e.g. Unix Makefiles, Ninja) if(NOT CMAKE_BUILD_TYPE) message(STATUS "CMAKE_BUILD_TYPE is not set. Setting default") message(STATUS "The available build types are: ${available_build_types}") set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE String "Options are ${available_build_types}" FORCE) # Provide drop down menu options in cmake-gui set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS ${available_build_types}) endif() message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") # Check the selected build type is valid list(FIND available_build_types "${CMAKE_BUILD_TYPE}" _build_type_index) if ("${_build_type_index}" EQUAL "-1") message(FATAL_ERROR "\"${CMAKE_BUILD_TYPE}\" is an invalid build type.\n" "Use one of the following build types ${available_build_types}") endif() endif() # CMAKE_BUILD_TYPE has no meaning for multi-configuration generators # (e.g. Visual Studio) so use generator expressions instead to add # the right definitions when doing a particular build type. # # Note for some reason we have to leave off ``-D`` here otherwise # we get ``-D-DZ3DEBUG`` passed to the compiler list(APPEND Z3_COMPONENT_CXX_DEFINES $<$:Z3DEBUG>) list(APPEND Z3_COMPONENT_CXX_DEFINES $<$:_EXTERNAL_RELEASE>) list(APPEND Z3_COMPONENT_CXX_DEFINES $<$:_EXTERNAL_RELEASE>) ################################################################################ # Find Python ################################################################################ find_package(PythonInterp REQUIRED) message(STATUS "PYTHON_EXECUTABLE: ${PYTHON_EXECUTABLE}") ################################################################################ # Target architecture detection ################################################################################ include(${PROJECT_SOURCE_DIR}/cmake/target_arch_detect.cmake) detect_target_architecture(TARGET_ARCHITECTURE) message(STATUS "Detected target architecture: ${TARGET_ARCHITECTURE}") ################################################################################ # Function for detecting C++ compiler flag support ################################################################################ include(${PROJECT_SOURCE_DIR}/cmake/z3_add_cxx_flag.cmake) ################################################################################ # C++ language version ################################################################################ set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) ################################################################################ # Platform detection ################################################################################ if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") message(STATUS "Platform: Linux") list(APPEND Z3_COMPONENT_CXX_DEFINES "-D_LINUX_") if ("${TARGET_ARCHITECTURE}" STREQUAL "x86_64") list(APPEND Z3_COMPONENT_CXX_DEFINES "-D_USE_THREAD_LOCAL") endif() elseif ("${CMAKE_SYSTEM_NAME}" MATCHES "GNU") message(STATUS "Platform: GNU/Hurd") list(APPEND Z3_COMPONENT_CXX_DEFINES "-D_HURD_") elseif ("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin") # Does macOS really not need any special flags? message(STATUS "Platform: Darwin") elseif ("${CMAKE_SYSTEM_NAME}" MATCHES "FreeBSD") message(STATUS "Platform: FreeBSD") list(APPEND Z3_COMPONENT_CXX_DEFINES "-D_FREEBSD_") elseif ("${CMAKE_SYSTEM_NAME}" MATCHES "NetBSD") message(STATUS "Platform: NetBSD") list(APPEND Z3_COMPONENT_CXX_DEFINES "-D_NetBSD_") elseif ("${CMAKE_SYSTEM_NAME}" MATCHES "OpenBSD") message(STATUS "Platform: OpenBSD") list(APPEND Z3_COMPONENT_CXX_DEFINES "-D_OPENBSD_") elseif (CYGWIN) message(STATUS "Platform: Cygwin") list(APPEND Z3_COMPONENT_CXX_DEFINES "-D_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() list(APPEND Z3_COMPONENT_EXTRA_INCLUDE_DIRS "${PROJECT_BINARY_DIR}/src" "${PROJECT_SOURCE_DIR}/src" ) ################################################################################ # GNU multiple precision library support ################################################################################ option(Z3_USE_LIB_GMP "Use GNU Multiple Precision Library" OFF) if (Z3_USE_LIB_GMP) # Because this is off by default we will make the configure fail if libgmp # can't be found find_package(GMP REQUIRED) message(STATUS "Using libgmp") list(APPEND Z3_DEPENDENT_LIBS ${GMP_C_LIBRARIES}) list(APPEND Z3_COMPONENT_EXTRA_INCLUDE_DIRS ${GMP_INCLUDE_DIRS}) list(APPEND Z3_COMPONENT_CXX_DEFINES "-D_MP_GMP") else() list(APPEND Z3_COMPONENT_CXX_DEFINES "-D_MP_INTERNAL") message(STATUS "Not using libgmp") endif() ################################################################################ # API Log sync ################################################################################ option(Z3_API_LOG_SYNC "Use locking when logging Z3 API calls (experimental)" OFF ) if (Z3_API_LOG_SYNC) list(APPEND Z3_COMPONENT_CXX_DEFINES "-DZ3_LOG_SYNC") message(STATUS "Using Z3_API_LOG_SYNC") else() message(STATUS "Not using Z3_API_LOG_SYNC") endif() ################################################################################ # Thread safe or not? ################################################################################ option(SINGLE_THREADED "Non-thread-safe build" OFF ) if (SINGLE_THREADED) list(APPEND Z3_COMPONENT_CXX_DEFINES "-DSINGLE_THREAD") message(STATUS "Non-thread-safe build") else() message(STATUS "Thread-safe build") endif() ################################################################################ # FP math ################################################################################ # FIXME: Support ARM "-mfpu=vfp -mfloat-abi=hard" if (("${TARGET_ARCHITECTURE}" STREQUAL "x86_64") OR ("${TARGET_ARCHITECTURE}" STREQUAL "i686")) if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Intel")) if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Intel") # Intel's compiler requires linking with libiomp5 list(APPEND Z3_DEPENDENT_LIBS "iomp5") endif() set(SSE_FLAGS "-mfpmath=sse" "-msse" "-msse2") elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Intel") set(SSE_FLAGS "-mfpmath=sse" "-msse" "-msse2") # Intel's compiler requires linking with libiomp5 list(APPEND Z3_DEPENDENT_LIBS "iomp5") elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") set(SSE_FLAGS "/arch:SSE2") else() message(FATAL_ERROR "Unknown compiler ${CMAKE_CXX_COMPILER_ID}") endif() CHECK_CXX_COMPILER_FLAG("${SSE_FLAGS}" HAS_SSE2) if (HAS_SSE2) list(APPEND Z3_COMPONENT_CXX_FLAGS ${SSE_FLAGS}) endif() unset(SSE_FLAGS) endif() ################################################################################ # Threading support ################################################################################ find_package(Threads) list(APPEND Z3_DEPENDENT_LIBS ${CMAKE_THREAD_LIBS_INIT}) ################################################################################ # Compiler warnings ################################################################################ include(${PROJECT_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). ################################################################################ if (UNIX AND CMAKE_GENERATOR STREQUAL "Ninja") if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fcolor-diagnostics") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fcolor-diagnostics") endif() # if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdiagnostics-color") # set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fdiagnostics-color") # endif() endif() ################################################################################ # Option to control what type of library we build ################################################################################ option(Z3_BUILD_LIBZ3_SHARED "Build libz3 as a shared library if true, otherwise build a static library" ON) ################################################################################ # Symbol visibility ################################################################################ if (NOT MSVC) z3_add_cxx_flag("-fvisibility=hidden" REQUIRED) endif() ################################################################################ # Tracing ################################################################################ option(Z3_ENABLE_TRACING_FOR_NON_DEBUG "Enable tracing in non-debug builds." OFF) if (Z3_ENABLE_TRACING_FOR_NON_DEBUG) list(APPEND Z3_COMPONENT_CXX_DEFINES "-D_TRACE") else() # Tracing is always enabled in debug builds list(APPEND Z3_COMPONENT_CXX_DEFINES $<$:_TRACE>) endif() ################################################################################ # Position independent code ################################################################################ # This is required because code built in the components will end up in a shared # library. If not building a shared library ``-fPIC`` isn't needed and would add # unnecessary overhead. if (Z3_BUILD_LIBZ3_SHARED) # Avoid adding -fPIC compiler switch if we compile with MSVC (which does not # support the flag) or if we target Windows, which generally does not use # position independent code for native code shared libraries (DLLs). if (NOT (MSVC OR MINGW OR WIN32)) z3_add_cxx_flag("-fPIC" REQUIRED) endif() endif() ################################################################################ # Link time optimization ################################################################################ include(${PROJECT_SOURCE_DIR}/cmake/compiler_lto.cmake) ################################################################################ # Control flow integrity ################################################################################ option(Z3_ENABLE_CFI "Enable control flow integrity checking" OFF) if (Z3_ENABLE_CFI) set(build_types_with_cfi "RELEASE" "RELWITHDEBINFO") if (NOT Z3_LINK_TIME_OPTIMIZATION) message(FATAL_ERROR "Cannot enable control flow integrity checking without link-time optimization." "You should set Z3_LINK_TIME_OPTIMIZATION to ON or Z3_ENABLE_CFI to OFF.") endif() if (DEFINED CMAKE_CONFIGURATION_TYPES) # Multi configuration generator message(STATUS "Note CFI is only enabled for the following configurations: ${build_types_with_cfi}") # No need for else because this is the same as the set that LTO requires. endif() if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") z3_add_cxx_flag("-fsanitize=cfi" REQUIRED) z3_add_cxx_flag("-fsanitize-cfi-cross-dso" REQUIRED) elseif ("x${CMAKE_CXX_COMPILER_ID}" STREQUAL "xMSVC") z3_add_cxx_flag("/guard:cf" REQUIRED) message(STATUS "Enabling CFI for MSVC") foreach (_build_type ${build_types_with_cfi}) message(STATUS "Enabling CFI for MSVC") string(APPEND CMAKE_EXE_LINKER_FLAGS_${_build_type} " /GUARD:CF") string(APPEND CMAKE_SHARED_LINKER_FLAGS_${_build_type} " /GUARD:CF") endforeach() else() message(FATAL_ERROR "Can't enable control flow integrity for compiler \"${CMAKE_CXX_COMPILER_ID}\"." "You should set Z3_ENABLE_CFI to OFF or use Clang or MSVC to compile.") endif() endif() ################################################################################ # MSVC specific flags inherited from old build system ################################################################################ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") include(${PROJECT_SOURCE_DIR}/cmake/msvc_legacy_quirks.cmake) endif() ################################################################################ # Pass /RELEASE to the linker so that checksums in PE files are calculated. ################################################################################ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") string(APPEND CMAKE_EXE_LINKER_FLAGS " /RELEASE") string(APPEND CMAKE_SHARED_LINKER_FLAGS " /RELEASE") endif() ################################################################################ # Report default CMake flags ################################################################################ # This is mainly for debugging. message(STATUS "CMAKE_CXX_FLAGS: \"${CMAKE_CXX_FLAGS}\"") message(STATUS "CMAKE_EXE_LINKER_FLAGS: \"${CMAKE_EXE_LINKER_FLAGS}\"") message(STATUS "CMAKE_STATIC_LINKER_FLAGS: \"${CMAKE_STATIC_LINKER_FLAGS}\"") message(STATUS "CMAKE_SHARED_LINKER_FLAGS: \"${CMAKE_SHARED_LINKER_FLAGS}\"") if (DEFINED CMAKE_CONFIGURATION_TYPES) # Multi configuration generator string(TOUPPER "${available_build_types}" build_types_to_report) else() # Single configuration generator string(TOUPPER "${CMAKE_BUILD_TYPE}" build_types_to_report) endif() foreach (_build_type ${build_types_to_report}) message(STATUS "CMAKE_CXX_FLAGS_${_build_type}: \"${CMAKE_CXX_FLAGS_${_build_type}}\"") message(STATUS "CMAKE_EXE_LINKER_FLAGS_${_build_type}: \"${CMAKE_EXE_LINKER_FLAGS_${_build_type}}\"") message(STATUS "CMAKE_SHARED_LINKER_FLAGS_${_build_type}: \"${CMAKE_SHARED_LINKER_FLAGS_${_build_type}}\"") message(STATUS "CMAKE_STATIC_LINKER_FLAGS_${_build_type}: \"${CMAKE_STATIC_LINKER_FLAGS_${_build_type}}\"") endforeach() ################################################################################ # Report Z3_COMPONENT flags ################################################################################ message(STATUS "Z3_COMPONENT_CXX_DEFINES: ${Z3_COMPONENT_CXX_DEFINES}") message(STATUS "Z3_COMPONENT_CXX_FLAGS: ${Z3_COMPONENT_CXX_FLAGS}") message(STATUS "Z3_DEPENDENT_LIBS: ${Z3_DEPENDENT_LIBS}") message(STATUS "Z3_COMPONENT_EXTRA_INCLUDE_DIRS: ${Z3_COMPONENT_EXTRA_INCLUDE_DIRS}") message(STATUS "Z3_DEPENDENT_EXTRA_CXX_LINK_FLAGS: ${Z3_DEPENDENT_EXTRA_CXX_LINK_FLAGS}") ################################################################################ # Z3 installation locations ################################################################################ include(GNUInstallDirs) set(CMAKE_INSTALL_PKGCONFIGDIR "${CMAKE_INSTALL_LIBDIR}/pkgconfig" CACHE PATH "Directory to install pkgconfig files" ) set(CMAKE_INSTALL_Z3_CMAKE_PACKAGE_DIR "${CMAKE_INSTALL_LIBDIR}/cmake/z3" CACHE PATH "Directory to install Z3 CMake package files" ) message(STATUS "CMAKE_INSTALL_LIBDIR: \"${CMAKE_INSTALL_LIBDIR}\"") message(STATUS "CMAKE_INSTALL_BINDIR: \"${CMAKE_INSTALL_BINDIR}\"") message(STATUS "CMAKE_INSTALL_INCLUDEDIR: \"${CMAKE_INSTALL_INCLUDEDIR}\"") message(STATUS "CMAKE_INSTALL_PKGCONFIGDIR: \"${CMAKE_INSTALL_PKGCONFIGDIR}\"") message(STATUS "CMAKE_INSTALL_Z3_CMAKE_PACKAGE_DIR: \"${CMAKE_INSTALL_Z3_CMAKE_PACKAGE_DIR}\"") ################################################################################ # Uninstall rule ################################################################################ configure_file( "${PROJECT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" @ONLY ) # Target needs to be declared before the components so that they can add # dependencies to this target so they can run their own custom uninstall rules. add_custom_target(uninstall COMMAND "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" COMMENT "Uninstalling..." ${ADD_CUSTOM_COMMAND_USES_TERMINAL_ARG} VERBATIM ) ################################################################################ # CMake build file locations ################################################################################ # To mimic the python build system output these into the root of the build # directory set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}") set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}") set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}") ################################################################################ # Extra dependencies for build rules that use the Python infrastructure to # generate files used for Z3's build. Changes to these files will trigger # a rebuild of all the generated files. ################################################################################ # Note: ``update_api.py`` is deliberately not here because it not used # to generate every generated file. The targets that need it list it explicitly. set(Z3_GENERATED_FILE_EXTRA_DEPENDENCIES "${PROJECT_SOURCE_DIR}/scripts/mk_genfile_common.py" ) ################################################################################ # Z3 components, library and executables ################################################################################ include(${PROJECT_SOURCE_DIR}/cmake/z3_add_component.cmake) include(${PROJECT_SOURCE_DIR}/cmake/z3_append_linker_flag_list_to_target.cmake) add_subdirectory(src) ################################################################################ # Create `Z3Config.cmake` and related files for the build tree so clients can # use Z3 via CMake. ################################################################################ include(CMakePackageConfigHelpers) export(EXPORT Z3_EXPORTED_TARGETS NAMESPACE z3:: FILE "${PROJECT_BINARY_DIR}/Z3Targets.cmake" ) set(Z3_FIRST_PACKAGE_INCLUDE_DIR "${PROJECT_BINARY_DIR}/src/api") set(Z3_SECOND_PACKAGE_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/src/api") set(Z3_CXX_PACKAGE_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/src/api/c++") set(AUTO_GEN_MSG "Automatically generated. DO NOT EDIT") set(CONFIG_FILE_TYPE "build tree") configure_package_config_file("${PROJECT_SOURCE_DIR}/cmake/Z3Config.cmake.in" "Z3Config.cmake" INSTALL_DESTINATION "${PROJECT_BINARY_DIR}" PATH_VARS Z3_FIRST_PACKAGE_INCLUDE_DIR Z3_SECOND_PACKAGE_INCLUDE_DIR Z3_CXX_PACKAGE_INCLUDE_DIR ) unset(Z3_FIRST_PACKAGE_INCLUDE_DIR) unset(Z3_SECOND_PACKAGE_INCLUDE_DIR) unset(Z3_CXX_PACKAGE_INCLUDE_DIR) unset(AUTO_GEN_MSG) unset(CONFIG_FILE_TYPE) write_basic_package_version_file("${PROJECT_BINARY_DIR}/Z3ConfigVersion.cmake" COMPATIBILITY SameMajorVersion ) ################################################################################ # Create `Z3Config.cmake` and related files for install tree so clients can use # Z3 via CMake. ################################################################################ install(EXPORT Z3_EXPORTED_TARGETS FILE "Z3Targets.cmake" NAMESPACE z3:: DESTINATION "${CMAKE_INSTALL_Z3_CMAKE_PACKAGE_DIR}" ) set(Z3_INSTALL_TREE_CMAKE_CONFIG_FILE "${PROJECT_BINARY_DIR}/cmake/Z3Config.cmake") set(Z3_FIRST_PACKAGE_INCLUDE_DIR "${CMAKE_INSTALL_INCLUDEDIR}") set(Z3_SECOND_INCLUDE_DIR "") set(Z3_CXX_PACKAGE_INCLUDE_DIR "") set(AUTO_GEN_MSG "Automatically generated. DO NOT EDIT") set(CONFIG_FILE_TYPE "install tree") # We use `configure_package_config_file()` to try and create CMake files # that are re-locatable so that it doesn't matter if the files aren't placed # in the original install prefix. configure_package_config_file("${PROJECT_SOURCE_DIR}/cmake/Z3Config.cmake.in" "${Z3_INSTALL_TREE_CMAKE_CONFIG_FILE}" INSTALL_DESTINATION "${CMAKE_INSTALL_Z3_CMAKE_PACKAGE_DIR}" PATH_VARS Z3_FIRST_PACKAGE_INCLUDE_DIR ) unset(Z3_FIRST_PACKAGE_INCLUDE_DIR) unset(Z3_SECOND_PACKAGE_INCLUDE_DIR) unset(Z3_CXX_PACKAGE_INCLUDE_DIR) unset(AUTO_GEN_MSG) unset(CONFIG_FILE_TYPE) # Add install rule to install ${Z3_INSTALL_TREE_CMAKE_CONFIG_FILE} install( FILES "${Z3_INSTALL_TREE_CMAKE_CONFIG_FILE}" DESTINATION "${CMAKE_INSTALL_Z3_CMAKE_PACKAGE_DIR}" ) # Add install rule to install ${PROJECT_BINARY_DIR}/Z3ConfigVersion.cmake install( FILES "${PROJECT_BINARY_DIR}/Z3ConfigVersion.cmake" DESTINATION "${CMAKE_INSTALL_Z3_CMAKE_PACKAGE_DIR}" ) ################################################################################ # Examples ################################################################################ option(Z3_ENABLE_EXAMPLE_TARGETS "Build Z3 api examples" ON) if (Z3_ENABLE_EXAMPLE_TARGETS) add_subdirectory(examples) endif() ################################################################################ # Documentation ################################################################################ option(Z3_BUILD_DOCUMENTATION "Build API documentation" OFF) if (Z3_BUILD_DOCUMENTATION) message(STATUS "Building documentation enabled") add_subdirectory(doc) else() message(STATUS "Building documentation disabled") endif() z3-z3-4.8.7/LICENSE.txt000066400000000000000000000021101356505360400142440ustar00rootroot00000000000000Z3 Copyright (c) Microsoft Corporation All rights reserved. MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.z3-z3-4.8.7/README-CMake.md000066400000000000000000000423021356505360400146650ustar00rootroot00000000000000# Z3's CMake build system [CMake](https://cmake.org/) is a "meta build system" that reads a description of the project written in the ``CMakeLists.txt`` files and emits a build system for that project of your choice using one of CMake's "generators". This allows CMake to support many different platforms and build tools. You can run ``cmake --help`` to see the list of supported "generators" on your platform. Example generators include "UNIX Makefiles" and "Visual Studio 12 2013". ## Getting started ### Fixing a polluted source tree If you have never used the python build system you can skip this step. The existing Python build system creates generated source files in the source tree. The CMake build system will refuse to work if it detects this so you need to clean your source tree first. To do this run the following in the root of the repository ``` git clean -nx src ``` This will list everything that will be removed. If you are happy with this then run. ``` git clean -fx src ``` which will remove the generated source files. ### Unix Makefiles Run the following in the top level directory of the Z3 repository. ``` mkdir build cd build cmake -G "Unix Makefiles" ../ make -j4 # Replace 4 with an appropriate number ``` Note that on some platforms "Unix Makefiles" is the default generator so on those platforms you don't need to pass ``-G "Unix Makefiles"`` command line option to ``cmake``. Note there is nothing special about the ``build`` directory name here. You can call it whatever you like. Note the "Unix Makefile" generator is a "single" configuration generator which means you pick the build type (e.g. ``Debug``, ``Release``) when you invoke CMake. You can set the build type by passing it to the ``cmake`` invocation like so: ``` cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release ../ ``` See the section on "Build Types" for the different CMake build types. If you wish to use a different compiler set the CXX and CC environment variables passed to cmake. This must be done at the very first invocation to ``cmake`` in the build directory because once configuration has happened the compiler is fixed. If you want to use a different compiler to the one you have already configured you either need to make a new build directory or delete the contents of the current build directory and start again. For example to use clang the ``cmake `` line would be ``` CC=clang CXX=clang++ cmake ../ ``` Note that CMake build will detect the target architecture that compiler is set up to build for and the generated build system will build for that architecture. If there is a way to tell your compiler to build for a different architecture via compiler flags then you can set the ``CFLAGS`` and ``CXXFLAGS`` environment variables to have the build target that architecture. For example if you are on a x86_64 machine and you want to do a 32-bit build and have a multilib version of GCC you can run ``cmake`` like this ``` CFLAGS="-m32" CXXFLAGS="-m32" CC=gcc CXX=g++ cmake ../ ``` Note like with the ``CC`` and ``CXX`` flags this must be done on the very first invocation to CMake in the build directory. ### Ninja [Ninja](https://ninja-build.org/) is a simple build system that is built for speed. It can be significantly faster than "UNIX Makefile"s because it is not a recursive build system and thus doesn't create a new process everytime it traverses into a directory. Ninja is particularly appropriate if you want fast incremental building. Basic usage is as follows: ``` mkdir build cd build cmake -G "Ninja" ../ ninja ``` Note the discussion of the ``CC``, ``CXX``, ``CFLAGS`` and ``CXXFLAGS`` for "Unix Makefiles" also applies here. Note also that like the "Unix Makefiles" generator, the "Ninja" generator is a single configuration generator so you pick the build type when you invoke ``cmake`` by passing ``CMAKE_BUILD_TYPE=`` to ``cmake``. See the section on "Build Types". Note that Ninja runs in parallel by default. Use the ``-j`` flag to change this. Note that Ninja also runs on Windows. You just need to run ``cmake`` in an environment where the compiler can be found. If you have Visual Studio installed it typically ships with a "Developer Command Prompt Window" that you can use which has the environment variables setup for you. ### NMake NMake is a build system that ships with Visual Studio. You are advised to use Ninja instead which is significantly faster due to supporting concurrent builds. However CMake does support NMake if you wish to use it. Note that NMake is a single configuration generator so you must set ``CMAKE_BUILD_TYPE`` to set the build type. Basic usage: 1. Launch the "Developer Command Prompt Windows" 2. Change to the root of the Z3 repository ``` mkdir build cd build cmake -G "NMake Makefiles" ../ nmake ``` ### Visual Studio For the Visual Studio generators you need to know which version of Visual Studio you wish to use and also what architecture you want to build for. We'll use the ``cmake-gui`` here as it is easier to pick the right generator but this can be scripted if need be. Here are the basic steps: 1. Create an empty build directory 2. Start the cmake-gui program 3. Set "where is the source code" to the root of the Z3 project repository. You can do this by pressing the "Browse Source..." button and picking the directory. 4. Set "where to build the binaries" to the empty build directory you just created. You can do this by pressing the "Browse build..." button and picking the directory. 5. Press the "Configure" button 6. A window will appear asking you to pick the generator to use. Pick the generator that matches the version of Visual Studio you are using. Note also that some of the generator names contain ``Win64`` (e.g. ``Visual Studio 12 2013 Win64``) this indicates a x86 64-bit build. Generator names without this (e.g. ``Visual Studio 12 2013``) are x86 32-bit build. 7. Press the "Finish" button and wait for CMake to finish it's first configure. 8. A set of configuration options will appear which will affect various aspects of the build. Change them as you desire. If you change a set of options press the "Configure" again. Additional options may appear when you do this. 9. When you have finished changing configuration options press the "Generate" button. 10. When generation is done close cmake-gui. 11. In the build directory open the generated ``Z3.sln`` solution file created by CMake with Visual Studio. 12. In Visual Studio pick the build type (e.g. ``Debug``, ``Release``) you want. 13. Click "BUILD > Build Solution". Note that unlike the "Unix Makefile" and "Ninja" generators the Visual Studio generators are multi-configuration generators which means you don't set the build type when invoking CMake. Instead you set the build type inside Visual Studio. See the "Build Type" section for more information. ### General workflow The general workflow when using CMake is the following 1. Create a new build directory 2. Configure the project 3. Generate the build system 4. Invoke the build system to build the project To perform steps 2 and 3 you can choose from three different tools * cmake * ccmake * cmake-gui ``cmake`` is a command line tool and is what you should use if you are writing a script to build Z3. This tool performs steps 1 and 2 in one go without user interaction. The ``ccmake`` and ``cmake-gui`` tools are more interactive and allow you to change various options. In both these tools the basic steps to follow are: 1. Configure. 2. Change any options you wish. Everytime you change a set of options You should configure again. This may cause new options to appear 3. Generate. For information see https://cmake.org/runningcmake/ Note when invoking CMake you give it the path to the source directory. This is the top-level directory in the Z3 repository containing a ``CMakeLists.txt``. That file should contain the line ``project(Z3 C CXX)``. If you give it a path deeper into the Z3 repository (e.g. the ``src`` directory) the configure step will fail. ## Build Types Several build types are supported. * Release * Debug * RelWithDebInfo * MinSizeRel For the single configuration generators (e.g. "Unix Makefile" and "Ninja") you set the build type when invoking ``cmake`` by passing ``-DCMAKE_BUILD_TYPE=`` where ```` is one of the build types specified above. For multi-configuration generators (e.g. Visual Studio) you don't set the build type when invoking CMake and instead set the build type within Visual Studio itself. ## Useful options The following useful options can be passed to CMake whilst configuring. * ``CMAKE_BUILD_TYPE`` - STRING. The build type to use. Only relevant for single configuration generators (e.g. "Unix Makefile" and "Ninja"). * ``CMAKE_INSTALL_BINDIR`` - STRING. The path to install z3 binaries (relative to ``CMAKE_INSTALL_PREFIX``), e.g. ``bin``. * ``CMAKE_INSTALL_INCLUDEDIR`` - STRING. The path to install z3 include files (relative to ``CMAKE_INSTALL_PREFIX``), e.g. ``include``. * ``CMAKE_INSTALL_LIBDIR`` - STRING. The path to install z3 libraries (relative to ``CMAKE_INSTALL_PREFIX``), e.g. ``lib``. * ``CMAKE_INSTALL_PREFIX`` - STRING. The install prefix to use (e.g. ``/usr/local/``). * ``CMAKE_INSTALL_PKGCONFIGDIR`` - STRING. The path to install pkgconfig files. * ``CMAKE_INSTALL_PYTHON_PKG_DIR`` - STRING. The path to install the z3 python bindings. This can be relative (to ``CMAKE_INSTALL_PREFIX``) or absolute. * ``CMAKE_INSTALL_Z3_CMAKE_PACKAGE_DIR`` - STRING. The path to install CMake package files (e.g. ``/usr/lib/cmake/z3``). * ``CMAKE_INSTALL_API_BINDINGS_DOC`` - STRING. The path to install documentation for API bindings. * ``Z3_ENABLE_TRACING_FOR_NON_DEBUG`` - BOOL. If set to ``TRUE`` enable tracing in non-debug builds, if set to ``FALSE`` disable tracing in non-debug builds. Note in debug builds tracing is always enabled. * ``Z3_BUILD_LIBZ3_SHARED`` - BOOL. If set to ``TRUE`` build libz3 as a shared library otherwise build as a static library. * ``Z3_ENABLE_EXAMPLE_TARGETS`` - BOOL. If set to ``TRUE`` add the build targets for building the API examples. * ``Z3_USE_LIB_GMP`` - BOOL. If set to ``TRUE`` use the GNU multiple precision library. If set to ``FALSE`` use an internal implementation. * ``Z3_PYTHON_EXECUTABLE`` - STRING. The python executable to use during the build. * ``Z3_BUILD_PYTHON_BINDINGS`` - BOOL. If set to ``TRUE`` then Z3's python bindings will be built. * ``Z3_INSTALL_PYTHON_BINDINGS`` - BOOL. If set to ``TRUE`` and ``Z3_BUILD_PYTHON_BINDINGS`` is ``TRUE`` then running the ``install`` target will install Z3's Python bindings. * ``Z3_BUILD_DOTNET_BINDINGS`` - BOOL. If set to ``TRUE`` then Z3's .NET bindings will be built. * ``Z3_INSTALL_DOTNET_BINDINGS`` - BOOL. If set to ``TRUE`` and ``Z3_BUILD_DOTNET_BINDINGS`` is ``TRUE`` then running the ``install`` target will install Z3's .NET bindings. * ``Z3_DOTNET_CSC_EXECUTABLE`` - STRING. The path to the C# compiler to use. Only relevant if ``Z3_BUILD_DOTNET_BINDINGS`` is set to ``TRUE``. * ``Z3_DOTNET_GACUTIL_EXECUTABLE`` - STRING. The path to the gacutil program to use. Only relevant if ``BUILD_DOTNET_BINDINGS`` is set to ``TRUE``. * ``Z3_BUILD_JAVA_BINDINGS`` - BOOL. If set to ``TRUE`` then Z3's Java bindings will be built. * ``Z3_INSTALL_JAVA_BINDINGS`` - BOOL. If set to ``TRUE`` and ``Z3_BUILD_JAVA_BINDINGS`` is ``TRUE`` then running the ``install`` target will install Z3's Java bindings. * ``Z3_JAVA_JAR_INSTALLDIR`` - STRING. The path to directory to install the Z3 Java ``.jar`` file. This path should be relative to ``CMAKE_INSTALL_PREFIX``. * ``Z3_JAVA_JNI_LIB_INSTALLDIRR`` - STRING. The path to directory to install the Z3 Java JNI bridge library. This path should be relative to ``CMAKE_INSTALL_PREFIX``. * ``Z3_INCLUDE_GIT_DESCRIBE`` - BOOL. If set to ``TRUE`` and the source tree of Z3 is a git repository then the output of ``git describe`` will be included in the build. * ``Z3_INCLUDE_GIT_HASH`` - BOOL. If set to ``TRUE`` and the source tree of Z3 is a git repository then the git hash will be included in the build. * ``Z3_BUILD_DOCUMENTATION`` - BOOL. If set to ``TRUE`` then documentation for the API bindings can be built by invoking the ``api_docs`` target. * ``Z3_INSTALL_API_BINDINGS_DOCUMENTATION`` - BOOL. If set to ``TRUE`` and ``Z3_BUILD_DOCUMENTATION` is ``TRUE`` then documentation for API bindings will be installed when running the ``install`` target. * ``Z3_ALWAYS_BUILD_DOCS`` - BOOL. If set to ``TRUE`` and ``Z3_BUILD_DOCUMENTATION`` is ``TRUE`` then documentation for API bindings will always be built. Disabling this is useful for faster incremental builds. The documentation can be manually built by invoking the ``api_docs`` target. * ``Z3_LINK_TIME_OPTIMIZATION`` - BOOL. If set to ``TRUE`` link time optimization will be enabled. * ``Z3_ENABLE_CFI`` - BOOL. If set to ``TRUE`` will enable Control Flow Integrity security checks. This is only supported by MSVC and Clang and will fail on other compilers. This requires Z3_LINK_TIME_OPTIMIZATION to also be enabled. * ``Z3_API_LOG_SYNC`` - BOOL. If set to ``TRUE`` will enable experimental API log sync feature. * ``WARNINGS_AS_ERRORS`` - STRING. If set to ``TRUE`` compiler warnings will be treated as errors. If set to ``False`` compiler warnings will not be treated as errors. If set to ``SERIOUS_ONLY`` a subset of compiler warnings will be treated as errors. * ``Z3_C_EXAMPLES_FORCE_CXX_LINKER`` - BOOL. If set to ``TRUE`` the C API examples will request that the C++ linker is used rather than the C linker. On the command line these can be passed to ``cmake`` using the ``-D`` option. In ``ccmake`` and ``cmake-gui`` these can be set in the user interface. Example ``` cmake -DCMAKE_BUILD_TYPE=Release -DZ3_ENABLE_TRACING_FOR_NON_DEBUG=FALSE ../ ``` ## Z3 API Bindings Z3 exposes various language bindings for its API. Below are some notes on building and/or installing these bindings when building Z3 with CMake. ### Java bindings The CMake build uses the ``FindJava`` and ``FindJNI`` cmake modules to detect the installation of Java. If CMake fails to find your installation of Java set the ``JAVA_HOME`` environment variable when invoking CMake so that it points at the correct location. For example ``` JAVA_HOME=/usr/lib/jvm/default cmake -DBUILD_JAVA_BINDINGS=ON ../ ``` Note that the built ``.jar`` file is named ``com.microsoft.z3-VERSION.jar`` where ``VERSION`` is the Z3 version. Under non Windows systems a symbolic link named ``com.microsoft.z3.jar`` is provided. This symbolic link is not created when building under Windows. ## Developer/packager notes These notes are help developers and packagers of Z3. ### Install/Uninstall Install and uninstall targets are supported. Use ``CMAKE_INSTALL_PREFIX`` to set the install prefix. If you also need need to control which directories are used for install set the documented ``CMAKE_INSTALL_*`` options. To install run ``` make install ``` To uninstall run ``` make uninstall ``` Note that ``DESTDIR`` is supported for [staged installs](https://www.gnu.org/prep/standards/html_node/DESTDIR.html). To install ``` mkdir staged make install DESTDIR=/full/path/to/staged/ ``` to uninstall ``` make uninstall DESTDIR=/full/path/to/staged ``` The above also works for Ninja but ``DESTDIR`` must be an environment variable instead. ### Examining invoked commands If you are using the "UNIX Makefiles" generator and want to see exactly the commands that are being run you can pass ``VERBOSE=1`` to make. ``` make VERBOSE=1 ``` If you are using Ninja you can use the ``-v`` flag. ### Additional targets To see the list of targets run ``` make help ``` There are a few special targets: * ``clean`` all the built targets in the current directory and below * ``edit_cache`` will invoke one of the CMake tools (depending on which is available) to let you change configuration options. * ``rebuild_cache`` will reinvoke ``cmake`` for the project. * ``api_docs`` will build the documentation for the API bindings. ### Setting build type specific flags The build system supports single configuration and multi-configuration generators. This means it is not possible to know the build type at configure time and therefore ``${CMAKE_BUILD_TYPE}`` should not be conditionally used to set compiler flags or definitions. Instead you should use Generator expressions which are evaluated by the generator. For example ``` $<$:Z3DEBUG> ``` If the build type at build time is ``Debug`` this evaluates to ``Z3DEBUG`` but evaluates to nothing for all other configurations. You can see examples of this in the ``CMakeLists.txt`` files. ### File-globbing It is tempting use file-globbing in ``CMakeLists.txt`` to find a set for files matching a pattern and use them as the sources to build a target. This however is a bad idea because it prevents CMake from knowing when it needs to rerun itself. This is why source file names are explicitly listed in the ``CMakeLists.txt`` so that when changes are made the source files used to build a target automatically triggers a rerun of CMake. Long story short. Don't use file globbing. ### Serious warning flags By default the `WARNINGS_AS_ERRORS` flag is set to `SERIOUS_ONLY` which means some warnings will be treated as errors. These warnings are controlled by the relevant `*_WARNINGS_AS_ERRORS` list defined in `cmake/compiler_warnings.cmake`. Additional warnings should only be added here if the warnings has no false positives. z3-z3-4.8.7/README.md000066400000000000000000000151231356505360400137100ustar00rootroot00000000000000# Z3 Z3 is a theorem prover from Microsoft Research. It is licensed under the [MIT license](LICENSE.txt). If you are not familiar with Z3, you can start [here](https://github.com/Z3Prover/z3/wiki#background). Pre-built binaries for stable and nightly releases are available from [here](https://github.com/Z3Prover/z3/releases). Z3 can be built using [Visual Studio][1], a [Makefile][2] or using [CMake][3]. It provides [bindings for several programming languages][4]. See the [release notes](RELEASE_NOTES) for notes on various stable releases of Z3. ## Build status | Azure Pipelines | TravisCI | | --------------- | -------- | [![Build Status](https://z3build.visualstudio.com/Z3Build/_apis/build/status/Z3Build-CI?branchName=master)](https://z3build.visualstudio.com/Z3Build/_build/latest?definitionId=10) | [![Build Status](https://travis-ci.org/Z3Prover/z3.svg?branch=master)](https://travis-ci.org/Z3Prover/z3) [1]: #building-z3-on-windows-using-visual-studio-command-prompt [2]: #building-z3-using-make-and-gccclang [3]: #building-z3-using-cmake [4]: #z3-bindings ## Building Z3 on Windows using Visual Studio Command Prompt 32-bit builds, start with: ```bash python scripts/mk_make.py ``` or instead, for a 64-bit build: ```bash python scripts/mk_make.py -x ``` then: ```bash cd build nmake ``` ## Building Z3 using make and GCC/Clang Execute: ```bash python scripts/mk_make.py cd build make sudo make install ``` Note by default ``g++`` is used as the C++ compiler if it is available. If you would prefer to use Clang change the ``mk_make.py`` invocation to: ```bash CXX=clang++ CC=clang python scripts/mk_make.py ``` Note that Clang < 3.7 does not support OpenMP. You can also build Z3 for Windows using Cygwin and the Mingw-w64 cross-compiler. To configure that case correctly, make sure to use Cygwin's own python and not some Windows installation of Python. For a 64 bit build (from Cygwin64), configure Z3's sources with ```bash CXX=x86_64-w64-mingw32-g++ CC=x86_64-w64-mingw32-gcc AR=x86_64-w64-mingw32-ar python scripts/mk_make.py ``` A 32 bit build should work similarly (but is untested); the same is true for 32/64 bit builds from within Cygwin32. By default, it will install z3 executable at ``PREFIX/bin``, libraries at ``PREFIX/lib``, and include files at ``PREFIX/include``, where ``PREFIX`` installation prefix if inferred by the ``mk_make.py`` script. It is usually ``/usr`` for most Linux distros, and ``/usr/local`` for FreeBSD and macOS. Use the ``--prefix=`` command line option to change the install prefix. For example: ```bash python scripts/mk_make.py --prefix=/home/leo cd build make make install ``` To uninstall Z3, use ```bash sudo make uninstall ``` To clean Z3 you can delete the build directory and run the ``mk_make.py`` script again. ## Building Z3 using CMake Z3 has a build system using CMake. Read the [README-CMake.md](README-CMake.md) file for details. It is recommended for most build tasks, except for building OCaml bindings. ## Z3 bindings Z3 has bindings for various programming languages. ### ``.NET`` You can install a nuget package for the latest release Z3 from [nuget.org](https://www.nuget.org/packages/Microsoft.Z3.x64/). Use the ``--dotnet`` command line flag with ``mk_make.py`` to enable building these. On non-windows platforms [mono](http://www.mono-project.com/) is required. On these platforms the location of the C# compiler and gac utility need to be known. You can set these as follows if they aren't detected automatically. For example: ```bash CSC=/usr/bin/csc GACUTIL=/usr/bin/gacutil python scripts/mk_make.py --dotnet ``` Note for very old versions of Mono (e.g. ``2.10``) you may need to set ``CSC`` to ``/usr/bin/dmcs``. Note that when ``make install`` is executed on non-windows platforms the GAC utility is used to install ``Microsoft.Z3.dll`` into the [GAC](http://www.mono-project.com/docs/advanced/assemblies-and-the-gac/) as the ``Microsoft.Z3.Sharp`` package. During install a [pkg-config](http://www.freedesktop.org/wiki/Software/pkg-config/) file (``Microsoft.Z3.Sharp.pc``) is also installed which allows the [MonoDevelop](http://www.monodevelop.com/) IDE to find the bindings. Running ``make uninstall`` will remove the dll from the GAC and the ``pkg-config`` file. See [``examples/dotnet``](examples/dotnet) for examples. ### ``C`` These are always enabled. See [``examples/c``](examples/c) for examples. ### ``C++`` These are always enabled. See [``examples/c++``](examples/c++) for examples. ### ``Java`` Use the ``--java`` command line flag with ``mk_make.py`` to enable building these. See [``examples/java``](examples/java) for examples. ### ``OCaml`` Use the ``--ml`` command line flag with ``mk_make.py`` to enable building these. See [``examples/ml``](examples/ml) for examples. ### ``Python`` You can install the Python wrapper for Z3 for the latest release from pypi using the command ```bash pip install z3-solver ``` Use the ``--python`` command line flag with ``mk_make.py`` to enable building these. Note that is required on certain platforms that the Python package directory (``site-packages`` on most distributions and ``dist-packages`` on Debian based distributions) live under the install prefix. If you use a non standard prefix you can use the ``--pypkgdir`` option to change the Python package directory used for installation. For example: ```bash python scripts/mk_make.py --prefix=/home/leo --python --pypkgdir=/home/leo/lib/python-2.7/site-packages ``` If you do need to install to a non standard prefix a better approach is to use a [Python virtual environment](https://virtualenv.readthedocs.org/en/latest/) and install Z3 there. Python packages also work for Python3. Under Windows, recall to build inside the Visual C++ native command build environment. Note that the ``build/python/z3`` directory should be accessible from where python is used with Z3 and it depends on ``libz3.dll`` to be in the path. ```bash virtualenv venv source venv/bin/activate python scripts/mk_make.py --python cd build make make install # You will find Z3 and the Python bindings installed in the virtual environment venv/bin/z3 -h ... python -c 'import z3; print(z3.get_version_string())' ... ``` See [``examples/python``](examples/python) for examples. ### ``Web Assembly`` [WebAssembly](https://github.com/cpitclaudel/z3.wasm) bindings are provided by Clément Pit-Claudel. ## System Overview ![System Diagram](https://github.com/Z3Prover/doc/blob/master/programmingz3/images/Z3Overall.jpg) ## Interfaces * Default input format is [SMTLIB2](http://smtlib.cs.uiowa.edu) * Other native foreign function interfaces: * C * C++ * Python * Java * C# * OCaml z3-z3-4.8.7/RELEASE_NOTES000066400000000000000000001250231356505360400144050ustar00rootroot00000000000000RELEASE NOTES Version 4.8.next ================ - Planned features - rewritten NIA (non-linear integer arithmetic) core solver - self-contained character theory, direct support for UTF8, Unicode character sets - recursive function representation without hoisting ite expressions. Issue #2601 - specialized solver support for QF_ABV and ABV based on lazy SMT and dual reduction - model-based interpolation for quantifier-free UF, arithmetic, arrays - circuit optimization techniques for BV pre-processing Version 4.8.7 ============= - New features - setting parameter on solver over the API by solver.smtlib2_log= enables tracing calls into the solver as SMTLIB2 commands. It traces, assert, push, pop, check_sat, get_consequences. - Notes - various bug fixes - remove model_compress. Use model.compact - print weights with quantifiers when weight is != 1 Version 4.8.6 ============= - Notes - various bug fixes - built in support for PIP, thanks to Audrey Dutcher - VS compilation mode including misc flags for managed packages Version 4.8.5 ============= - Notes - various bug fixes 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 - Native handling of recursive function definitions, thanks to Simon Cruanes - PB rounding based option for conflict resolution when reasoning about PB constraints. - Access to numeral constants as a double from the native API. - Notes - fixes several bugs discovered since the 4.8.1 release. Version 4.8.2 ============= - Post-Release. Version 4.8.1 ============= - Release. Bug-fix for 4.8.0 Version 4.8.0 ============= - New requirements: - A breaking change to the API is that parsers for SMT-LIB2 formulas return a vector of formulas as opposed to a conjunction of formulas. The vector of formulas correspond to the set of "assert" instructions in the SMT-LIB input. - New features - A parallel mode is available for select theories, including QF_BV. By setting parallel.enable=true Z3 will spawn a number of worker threads proportional to the number of available CPU cores to apply cube and conquer solving on the goal. - The SAT solver by default handle cardinality and PB constraints using a custom plugin that operates directly on cardinality and PB constraints. - A "cube" interface is exposed over the solver API. - Model conversion is first class over the textual API, such that subgoals created from running a solver can be passed in text files and a model for the original formula can be recreated from the result. - This has also led to changes in how models are tracked over tactic subgoals. The API for extracting models from apply_result have been replaced. - An optional mode handles xor constraints using a custom xor propagator. It is off by default and its value not demonstrated. - The SAT solver includes new inprocessing techniques that are available during simplification. It performs asymmetric tautology elimination by default, and one can turn on more powerful inprocessing techniques (known as ACCE, ABCE, CCE). Asymmetric branching also uses features introduced in Lingeling by exploiting binary implication graphs. Use sat.acce=true to enable the full repertoire of inprocessing methods. By default, clauses that are "eliminated" by acce are tagged as lemmas (redundant) and are garbage collected if their glue level is high. - Substantial overhaul of the spacer horn clause engine. - Added basic features to support Lambda bindings. - Added model compression to eliminate local function definitions in models when inlining them does not incur substantial overhead. The old behavior, where models are left uncompressed can be replayed by setting the top-level parameter model_compress=false. - Integration of a new solver for linear integer arithmetic and mixed linear integer arithmetic by Lev Nachmanson. It incorporates several improvements to QF_LIA solving based on . using a better LP engine, which is already the foundation for QF_LRA . including cuts based on Hermite Normal Form (thanks to approaches described in "cuts from proofs" and "cutting the mix"). . extracting integer solutions from LP solutions by tightening bounds selectively. We use a generalization of Bromberger and Weidenbach that allows avoiding selected bounds tightenings (https://easychair.org/publications/paper/qGfG). It solves significantly more problems in the QF_LIA category and may at this point also be the best solver for your problem as well. The new solver is enabled only for select SMT-LIB logics. These include QF_LIA, QF_IDL, and QF_UFLIA. Other theories (still) use the legacy solver for arithmetic. You can enable the new solver by setting the parameter smt.arith.solver=6 to give it a spin. - Removed features: - interpolation API - duality engine for constrained Horn clauses. - pdr engine for constrained Horn clauses. The engine's functionality has been folded into spacer as one of optional strategies. - long deprecated API functions have been removed from z3_api.h Version 4.7.1 ============= - New requirements: - uses stdbool and stdint as part of z3. - New features: - none - Removed features: - none - Notes: This is a minor release prior to a set of planned major updates. It uses minor version 7 to indicate that the use of stdbool and stdint are breaking changes to consumers of the C-based API. Version 4.6.0 ============= - New requirements: - C++11 capable compiler to build Z3. - C++ API now requires C++11 or newer. - New features (including): - A new string solver from University of Waterloo - A new linear real arithmetic solver - Changed behavior for optimization commands from the SMT2 command-line interface. Objective values are no longer printed by default. They can be retrieved by issuing the command (get-objectives). Pareto front objectives are accessed by issuing multiple (check-sat) calls until it returns unsat. - Removed features: - Removed support for SMT-LIB 1.x Version 4.5.0 ============= - New features: - New theories of strings and sequences. - Consequence finding API "get-consequences" to compute set of consequences modulo hard constraints and set of assumptions. Optimized implementations provided for finite domains (QF_FD) and for most SMT logics. - CMake build system (thanks @delcypher). - New API functions, including accessing assertions, parsing SMT-LIB benchmarks. - Updated and improved OCaml API (thanks @martin-neuhaeusser). - Updated and improved Java API (thanks @cheshire). - New resource limit facilities to avoid non-deterministic timeout behaviour. You can enable it from the command-line using the switch rlimit=. - New bit-vector simplification and ackermannization tactics (thanks @MikolasJanota, @nunoplopes). - QSAT: a new solver for satisfiability of quantified arithmetic formulas. See: Bjorner, Janota: Playing with Quantified Satisfaction, LPAR 2016. This is the new default solver for logics LIA, LRA, NRA. It furthermore can be applied as a tactic on quantified formulas using algebraic data-types (but excluding selector sub-terms because Z3 does not specify the semantics of applying a selector to a non-matching constructor term). - A specialized logic QF_FD and associated incremental solver (that supports push/pop). The QF_FD domain comprises of bit-vectors, enumeration data-types used only in equalities, and bounded integers: Integers used in QF_FD problems have to be constrained by a finite bound. - Queries in the fixedpoint engine are now function symbols and not formulas with free variables. This makes the association of free variables in the answers unambiguous. To emulate queries over compound formulas, introduce a fresh predicate whose arguments are the relevant free variables in the formula and add a rule that uses the fresh predicate in the head and formula in the body. - Minimization of unsat cores is available as an option for the SAT and SMT cores. By setting smt.core.minimize=true resp. sat.core.minimize=true cores produced by these modules are minimized. - A multitude of bugs has been fixed. Version 4.4.1 ============= - This release marks the transition to the new GitHub fork & pull model; the unstable and contrib branches will be retired with all new contributions going into the master branch directly. - A multitude of bugs has been fixed. - New Feature: Support for optimization queries. The SMT-LIB2 command language is augmented by three commands (maximize ), (minimize [:weight ] [:id ]). The programmatic API also contains a dedicated context for solving optimization queries. The TACAS 2015 tool paper by Bjorner, Dung and Fleckenstein describes additional details and the online tutorial on http://rise4fun.com/z3opt illustrates some uses. Version 4.4.0 ============= - New feature: Support for the theory of floating-point numbers. This comes in the form of logics (QF_FP and QF_FPBV), tactics (qffp and qffpbv), as well as a theory plugin that allows theory combinations. Z3 supports the official SMT theory definition of FP (see http://smtlib.cs.uiowa.edu/theories/FloatingPoint.smt2) in SMT2 files, as well as all APIs. - New feature: Stochastic local search engine for bit-vector formulas (see the qfbv-sls tactic). See also: Froehlich, Biere, Wintersteiger, Hamadi: Stochastic Local Search for Satisfiability Modulo Theories, AAAI 2015. - Upgrade: This release includes a brand new OCaml/ML API that is much better integrated with the build system, and hopefully also easier to use than the previous one. - Fixed various bugs reported by Marc Brockschmidt, Venkatesh-Prasad Ranganath, Enric Carbonell, Morgan Deters, Tom Ball, Malte Schwerhoff, Amir Ebrahimi, Codeplex users rsas, clockish, Heizmann, susmitj, steimann, and Stackoverflow users user297886. Version 4.3.2 ============= - Added preliminary support for the theory of floating point numbers (tactics qffpa, qffpabv, and logics QF_FPA, QF_FPABV). - Added the interpolation features of iZ3, which are now integrated into Z3. - Fixed a multitude of bugs and inconsistencies that were reported to us either in person, by email, or on Codeplex. Of those that we do have records of, we would like to express our gratitude to: Vladimir Klebanov, Konrad Jamrozik, Nuno Lopes, Carsten Ruetz, Esteban Pavese, Tomer Weiss, Ilya Mironov, Gabriele Paganelli, Levent Erkok, Fabian Emmes, David Cok, Etienne Kneuss, Arlen Cox, Matt Lewis, Carsten Otto, Paul Jackson, David Monniaux, Markus Rabe, Martin Pluecker, Jasmin Blanchette, Jules Villard, Andrew Gacek, George Karpenkov, Joerg Pfaehler, and Pablo Aledo as well as the following Codeplex users that either reported bugs or took part in discussions: xor88, parno, gario, Bauna, GManNickG, hanwentao, dinu09, fhowar, Cici, chinissai, barak_cohen, tvalentyn, krikunts, sukyoung, daramos, snedunuri, rajtendulkar, sonertari, nick8325, dvitek, amdragon, Beatgodes, dmonniaux, nickolai, DameNingen, mangpo, ttsiodras, blurium, sbrickey, pcodemod, indranilsaha, apanda, hougaardj, yoff, EfForEffort, Ansotegui, scottgw, viorelpreoteasa, idudka, c2855337, gario, jnfoster, omarmrivas, switicus, vosandi, foens, yzwwf, Heizmann, znajem, ilyagri, hougaardj, cliguda, rgrig, 92c849c1ccc707173, edmcman, cipher1024, MichaelvW, hellok, n00b42, ic3guy, Adorf, tvcsantos, zilongwang, Elarnon, immspw, jbridge99, danliew, zverlov, petross, jmh93, dradorf, fniksic, Heyji, cxcfan, henningg, wxlfrank, rvprasad, MovGP0, jackie1015, cowang, ffaghih, sanpra1989, gzchenyin, baitman, xjtulixiangyang, andreis, trucnguyenlam, erizzi, hanhchi, qsp, windypan, vadave, gradanne, SamWot, gsingh93, manjeetdahiya, zverlov, RaLa, and regehr. - New parameter setting infrastructure. Now, it is possible to set parameter for Z3 internal modules. Several parameter names changed. Execute `z3 -p` for the new parameter list. - Added get_version() and get_version_string() to Z3Py - Added support for FreeBSD. Z3 can be compiled on FreeBSD using g++. - Added support for Python 3.x. - Reverted to `(set-option :global-decls false)` as the default. In Z3 4.3.0 and Z3 4.3.1, this option was set to true. Thanks to Julien Henry for reporting this problem. - Added `doc` directory and scripts for automatically generating the API documentation. - Removed 'autoconf' dependency. We do not need to execute 'autoconf' and './configure' anymore to build Z3. - Fixed incorrect result returned by Z3_solver_get_num_scopes. (Thanks to Herman Venter). This bug was introduced in Z3 4.3.0 - Java bindings. To enable them, we must use the option `--java` when executing the `mk_make.py` script. Example: `python scripts/mk_make.py --java` - Fixed crash when parsing incorrect formulas. The crash was introduced when support for "arithmetic coercions" was added in Z3 4.3.0. - Added new option to mk_make to allow users to specify where python bindings (Z3Py) will be installed. (Thanks to Dejan Jovanovic for reporting the problem). - Fixed crash reported at http://z3.codeplex.com/workitem/10 - Removed auxiliary constants created by the nnf tactic from Z3 models. - Fixed problem in the pretty printer. It was not introducing quotes for attribute names such as |foo:10|. - Fixed bug when using assumptions (Thanks to Philippe Suter and Etienne Kneuss) Consider the following example: (assert F) (check-sat a) (check-sat) If 'F' is unstatisfiable independently of the assumption 'a', and the inconsistency can be detected by just performing propagation, Then, version <= 4.3.1 may return unsat sat instead of unsat unsat We say may because 'F' may have other unsatisfiable cores. - Fixed bug reported at http://stackoverflow.com/questions/13923316/unprintable-solver-model - Fixed timers on Linux and FreeBSD. - Fixed crash reported at http://z3.codeplex.com/workitem/11. - Fixed bug reported at http://stackoverflow.com/questions/14307692/unknown-when-using-defs - Relax check_logic procedure. Now, it accepts coercions (to_real) automatically introduced by Z3. (Thanks to Paul Jackson). This is a fix for http://z3.codeplex.com/workitem/19. - Fixed http://stackoverflow.com/questions/14524316/z3-4-3-get-complete-model. - Fixed bugs in the C++ API (Thanks to Andrey Kupriyanov). - Fixed bug reported at http://z3.codeplex.com/workitem/23 (Thanks to Paul Jackson). - Fixed bug reported at http://stackoverflow.com/questions/15226944/segmentation-fault-in-z3 (Thanks to Tianhai Liu). Version 4.3.1 ============= - Added support for compiling Z3 using clang++ on Linux and OSX - Added missing compilation option (-D _EXTERNAL_RELEASE) in release mode. Version 4.3.0 ============= - Fixed bug during model construction reported by Heizmann (http://z3.codeplex.com/workitem/5) - Remark: We skipped version 4.2 due to a mistake when releasing 4.1.2. Version 4.1.2 was accidentally tagged as 4.2. Thanks to Claude Marche for reporting this issue. From now on, we are also officially moving to a 3 number naming convention for version numbers. The idea is to have more frequent releases containing bug fixes. - The Z3 codebase was reorganized, we also have a new build system. In all platforms, we need Python 2.7.x installed. On Windows, you can build using Visual Studio Command Prompt. On Linux, OSX, Cygwin, you can build using g++. See README for compilation instructions. - Removed tactic mip. It was based on code that was deleted during the code reorganization. - Fixed compilation problems with clang/llvm. Many thanks to Xi Wang for finding the problem, and suggesting the fix. - Now, Z3 automatically adds arithmetic coercions: to_real and to_int. Option (set-option :int-real-coercions false) disables this feature. If SMTLIB2_COMPLIANT=true in the command line, then :int-real-coercions is also set to false. - SMTLIB2_COMPLIANT is false by default. Use command line option SMTLIB2_COMPLIANT=true to enable it back. - Added "make install" and "make uninstall" to Makefile.in. - Added "make install-z3py" and "make uninstall-z3py" to Makefile.in. - Fixed crash/bug in the simplifier. The crash occurred when option ":sort-sums true" was used. - Added "--with-python=" option to configure script. - Cleaned c++, maxsat, test_mapi examples. - Move RELEASE_NOTES files to source code distribution. - Removed unnecessary files from source code distribution. - Removed unnecessary compilation modes from z3-prover.sln. - Added Xor procedure to Z3Py. - Z3 by default switches to an incremental solver when a Solver object is used to solve many queries. In the this version, we switch back to the tactic framework if the incremental solver returns "unknown". - Allow negative numerals in the SMT 2.0 frontend. That is, Z3 SMT 2.0 parser now accepts numerals such as "-2". It is not needed to encode them as "(- 2)" anymore. The parser still accepts -foo as a symbol. That is, it is *not* a shorthand for (- foo). This feature is disabled when SMTLIB2_COMPLIANT=true is set in the command line. - Now, Z3 can be compiled inside cygwin using gcc. - Fixed bug in the unsat core generation. First source code release (October 2, 2012) =========================================== - Fixed bug in Z3Py. The method that builds Z3 applications could crash if one of the arguments have to be "casted" into the correct sort (Thanks to Dennis Yurichev). - Fixed bug in datatype theory (Thanks to Ayrat). - Fixed bug in the definition of MkEmptySet and MkFullSet in the .Net API. - Display warning message and ignore option CASE_SPLIT=3,4 or 5 when auto configuration is enabled (AUTO_CONFIG=true) (Thanks Tobias from StackOverflow). - Made the predicates <, <=, > and >= chainable as defined in the SMT 2.0 standard (Thanks to Matthias Weiler). - Added missing Z3_decl_kind's for datatypes: Z3_OP_DT_CONSTRUCTOR, Z3_OP_DT_ACCESSOR, Z3_OP_DT_RECOGNISER. - Added support for numbers in scientific notation at Z3_ast Z3_mk_numeral(__in Z3_context c, __in Z3_string numeral, __in Z3_sort ty). - New builtin symbols in the arithmetic theory: pi, euler, sin, cos, tan, asin, acos, atan, sinh, cosh, tanh, asinh, acosh, atanh. The first two are constants, and the others are unary functions. These symbols are not available if a SMT 2.0 logic is specified (e.g., QF_LRA, QF_NRA, QF_LIA, etc) because these symbols are not defined in these logics. That is, the new symbols are only available if the logic is not specified. Version 4.1 =========== - New OCAML API (Many thanks to Josh Berdine) - CodeContracts in the .NET API (Many thanks to Francesco Logozzo). Users can now check whether they are using the .NET API correctly using Clousot. - Added option :error-behavior. The default value is continued-execution. Now, users can force the Z3 SMT 2.0 frontend to exit whenever an error is reported. They just have to use the command (set-option :error-behavior immediate-exit). - Fixed bug in term-if-then-else elimination (Thanks to Artur Niewiadomski). - Fixed bug in difference logic detection code (Thanks to Dejan Jovanovic). - Fixed bug in the pseudo-boolean preprocessor (Thanks to Adrien Champion). - Fixed bug in bvsmod preprocessing rules (Thanks to Dejan Jovanovic). - Fixed bug in Tactic tseitin-cnf (Thanks to Georg Hofferek). - Added missing simplification step in nlsat. - Fixed bug in model construction for linear real arithmetic (Thanks to Marcello Bersani). - Fixed bug in preprocessor that eliminated rational powers (e.g., (^ x (/ 1.0 2.0))), the bug affected only problems where the denominator was even (Thanks to Johannes Eriksson). - Fixed bug in the k-th root operation in the algebraic number package. The result was correct, but the resulting polynomial could be incorrectly tagged as minimal and trigger nontermination on comparison operations. (Thanks to Johannes Eriksson). - Fixed bug affecting problems containing patterns with n-ary arithmetic terms such as (p (+ x y 2)). This bug was introduced in Z3 4.0. (Thanks to Paul Jackson). - Fixed crash when running out of memory. - Fixed crash reported by Alex Summers. The crash was happening on scripts that contain quantifiers, and use boolean formulas inside terms. - Fixed crash in the MBQI module (Thanks to Stephan Falke). - Fixed bug in the E-matching engine. It was missing instances of multi-patterns (Thanks Alex Summers). - Fixed bug in Z3Py pretty printer. - The pattern inference module does not generate warning messages by default anymore. This module was responsible for producing messages such as: "WARNING: failed to find a pattern for quantifier (quantifier id: k!199)". The option PI_WARNINGS=true can be used to enable these warning messages. - Added missing return statements in z3++.h (Thanks to Daniel Neider). - Removed support for TPTP5 and Simplify input formats. - Removed support for Z3 (low-level) input format. It is still available in the API. - Removed support for "SMT 1.5" input format (aka .smtc files). This was a hybrid input format that was implemented while the SMT 2.0 standard was being designed. Users should move to SMT 2.0 format. Note that SMT 1.0 format is still available. - Made tseitin-cnf tactic more "user friendly". It automatically applies required transformations needed to eliminate operators such as: and, distinct, etc. - Implemented new PSC (principal subresultant coefficient) algorithm. This was one of the bottlenecks in the new nlsat solver/tactic. Version 4.0 =========== Z3 4.0 is a major release. The main new features are: - New C API, and it is backwards compatible, but several methods are marked as deprecated. In the new API, many solvers can be created in the same context. It also includes support for user defined strategies using Tactics. It also exposes a new interface for browsing models. - A thin C++ layer around the C API that illustrates how to leverage reference counting of ast objects. Several examples can be found in the directory 'examples/c++'. - New .NET API together with updated version of the legacy .NET API. The new .NET API supports the new features, Tactics, Solvers, Goals, and integration of with reference counting. Terms and sorts life-times no longer requires a scoping discipline. - Z3Py: Python interface for Z3. It covers all main features in the Z3 API. - NLSAT solver for nonlinear arithmetic. - The PDR algorithm in muZ. - iZ3: an interpolating theorem prover built on top of Z3 (\ref iz3documentation). iZ3 is only available for Windows and Linux. - New logging infrastructure. Z3 logs are used to record every Z3 API call performed by your application. If you find a bug, just the log need to be sent to the Z3 team. The following APIs were removed: Z3_trace_to_file, Z3_trace_to_stderr, Z3_trace_to_stdout, Z3_trace_off. The APIs: Z3_open_log, Z3_append_log and Z3_close_log do not receive a Z3_context anymore. When creating a log, you must invoke Z3_open_log before any other Z3 function call. The new logs are much more precise. However, they still have two limitations. They are not useful for logging applications that use callbacks (e.g., theory plugins) because the log interpreter does not have access to these callbacks. They are not precise for applications that are using multiple threads for processing multiple Z3 contexts. - Z3 (for Linux and OSX) does not depend on GMP anymore. - Z3 1.x backwards compatibility macros are defined in z3_v1.h. If you still use them, you have to explicitly include this file. - Fixed all bugs reported at Stackoverflow. Temporarily disabled features: - User theories cannot be used with the new Solver API yet. Users may still use them with the deprecated solver API. - Parallel Z3 is also disabled in this release. However, we have parallel combinators for creating strategies (See tutorial). The two features above will return in future releases. Here is a list of all deprecated functions. Version 3.2 =========== This is a bug-fix refresh that fixes reported problems with 3.1. - Added support for chainable and right associative attributes. - Fixed model generation for QBVF (aka UFBV) logic. Now, Z3 officially supports the logics BV and UFBV. These are essentially QF_BV and QF_UFBV with quantifiers. - Fixed bug in eval and get-value commands. Thanks to Levent Erkok. - Fixed performance bug that was affecting VCC and Slayer. Thanks to Michal Moskal. - Fixed time measurement on Linux. Thanks to Ayrat Khalimov. - Fixed bug in destructive equality resolution (DER=true). - Fixed bug in map operator in the theory of arrays. Thanks to Shaz Quadeer. - Improved OCaml build scripts for Windows. Thanks to Josh Berdine. - Fixed crash in MBQI (when Real variables were used). - Fixed bugs in quantifier elimination. Thanks to Josh Berdine. - Fixed crash when an invalid datatype declaration is used. - Fixed bug in the SMT2 parser. - Fixed crash in quick checker for quantified formulas. Thanks to Swen Jacobs. - Fixed bug in the bvsmod simplifier. Thanks to Trevor Hansen. - New APIs: \c Z3_substitute and \c Z3_substitute_vars. - Fixed crash in MBQI. Thanks to Dejan Jovanovic. Version 3.1 =========== This is a bug-fix refresh that fixes reported problems with 3.0. - Fixed a bug in model generation. Thanks to Arlen Cox and Gordon Fraser. - Fixed a bug in Z3_check_assumptions that prevented it from being used between satisfiable instances. Thanks to Krystof Hoder. - Fixed two bugs in quantifier elimination. Thanks to Josh Berdine. - Fixed bugs in the preprocessor. - Fixed performance bug in MBQI. Thanks to Kathryn Stolee. - Improved strategy for QBVF (aka UFBV) logic. - Added support for negative assumptions in the check-sat command. Version 3.0 =========== - Fully compliant SMT-LIB 2.0 (SMT2) front-end. The old front-end is still available (command line option -smtc). The Z3 Guide describes the new front-end. - Parametric inductive datatypes, and parametric user defined types. - New SAT solver. Z3 can also read dimacs input formulas. - New Bitvector (QF_BV) solver. The new solver is only available when using the new SMT2 front-end. - Major performance improvements. - New preprocessing stack. - Performance improvements for linear and nonlinear arithmetic. The improvements are only available when using the SMT2 front-end. - Added API for parsing SMT2 files. - Fixed bug in AUTO_CONFIG=true. Thanks to Alberto Griggio. - Fixed bug in the Z3 simplifier cache. It was not being reset during backtracking. Thanks to Alberto Griggio. - Fixed many other bugs reported by users. - Improved model-based quantifier instantiation (MBQI). - New solver for Quantified Bitvector Logic (QBVF). - Z3 checks the user specified logic. - TPTP 5 front-end. Version 2.19 ============ - In the SMT-LIB 1.0 frontend, Z3 will only display the model when requested by the user (MODEL=true). - Fixed bug in the variable elimination preprocessor. Thanks to Alberto Griggio. - Fixed bug in the expression strong simplifier. Thanks to Marko. - Fixed bug in the Z3 auto configuration mode. Thanks to Vladimir Klebanov. - Fixed bug when model generation is used in the context of user-defined-theories. Thanks to Philippe Suter. - Fixed bug in quantifier elimination procedure. Thanks to Mikkel Larsen Pedersen. - Improved speed of Z3 lexer for SMT-LIB frontend. - Added a sample under examples/fixedpoints to illustrate using the API for pluggable relations. - Added an API method \c Z3_get_param_value for retrieving a configuration value given a configuration parameter name. Version 2.18 ============ - Z3 has a new mode for solving fixed-point queries. It allows formulating Datalogish queries combined with constraints. Try it online. - Fixed bug that affects the array theory over the API using RELEVANCY=0. Thanks to Josh Berdine. Version 2.17 ============ - Z3 has new model finding capabilities for Quantified SMT formulas. The new features are enabled with MBQI=true. (Model Based Quantifier Instantiation). MBQI implements a counter-example based refinement loop, where candidate models are built and checked. When the model checking step fails, it creates new quantifier instantiations. The models are returned as simple functional programs. The new feature is also a decision procedure for many known decidable fragments such as: EPR (Effectively Propositional), Bradley&Manna&Sipma's Array Property Fragment (VMCAI'06), Almost Uninterpreted Fragment (Complete instantiation for quantified SMT formulas, CAV'09), McPeak&Necula's list fragment (CAV'05), QBVF (Quantified Bit-Vector Formulas FMCAD'10), to cite a few. MBQI is useful for checking the consistency of background axiomatizations, synthesizing functions, and building real counterexamples for verification tools. Users can constrain the search space by providing templates for function symbols, and constraints on the size of the universe and range of functions. - Fixed bug in the command (simplify [expr]) SMT-LIB 2.0 frontend. - New model pretty printer. The old style is still available (option MODEL_V2=true). Z3 1.x style is also available (option MODEL_V1=true). - Removed \c ARRAY_PROPERTY option. It is subsumed by MBQI=true. - Z3 uses the (set-logic [name]) to configure itself. - Assumptions can be provided to the \c check-sat command. The command (check-sat [assumptions]) checks the satisfiability of the logical context modulo the given set of assumptions. The assumptions must be Boolean constants or the negation of Boolean constants. When the logical context is unsatisfiable modulo the given assumptions, Z3 will display a subset of the \c assumptions that contributed to the conflict. Lemmas learned during the execution of \c check-sat are preserved. - Added command (echo [string]) to the SMT-LIB 2.0 frontend. - Z3 models explicitly include an interpretation for uninterpreted sorts. The interpretation is presented using the \c define-sort primitive. For example, \code (define-sort S e_1 ... e_n) \endcode states that the interpretation of the uninterpreted sort S is finite, and its universe is composed by values \c e_1, ..., \c e_n. - Options \c WARNING and \c VERBOSE can be set in the SMT-LIB 2.0 frontend using the commands (set-option WARNING ) (set-option VERBOSE ). - Fixed unintentional side-effects in the Z3 pretty printer. Thanks to Swen Jacobs. - Added interpreted constants of the form as-array[f]. The constants are used in models produced by Z3 to encode the interpretation of arrays. The following axiom scheme axiomatizes the new constants: \code (forall (x1 S1) ... (xn Sn) (= (select as-array[f] x1 ... xn) (f x1 ... xn))) \endcode - Fixed bug in the option MACRO_FINDER=true. - Fixed bug in the (eval [expr]) command in the SMT-LIB 2.0 frontend. - Soundness bug in solver for array property fragment. Thanks to Trevor Hansen. Version 2.16 ============ The following bugs are fixed in this release: - Bugs in quantifier elimination. Thanks to Mikkel Larsen Pedersen. - Crash in non-linear arithmetic. Thanks to Trevor Hansen. - Unsoundness in mixed integer-linear version using to_real. Thanks to Hirai. - A crash and bugs in check_assumptions feature. Thanks to Akash Lal and Shaz Qadeer. Version 2.15 ============ The following bugs are fixed in this release: - A bug in the quantifier elimination that affects nested alternating quantifiers that cannot be fully eliminated. - A crash in proof generation. Thanks to Sascha Boehme. Version 2.14 ============ The following bugs are fixed in this release: - A crash in arithmetic simplification. Thanks to Trevor Hansen. - An unsoundness bug in the quantifier elimination. It affects the equivalence of answers that are computed in some cases. - Incorrect printing of parameters and other values in SMT-LIB2 mode. Thanks to Tjark Weber. Version 2.13 ============ The following bugs are fixed in this release: - Soundness bug in solver for array property fragment. Thanks to Trevor Hansen. - Soundness bug introduced in macro expansion utilities. Thanks to Wintersteiger. - Incorrect handling of QF_NRA. Thanks to Trevor Hansen. - Mixup between SMT2 and SMT1 pretty printing formats. Thanks to Alvin Cheung and Tjark Weber. Version 2.12 ============ News: - Philippe Suter made a JNI binding available. There is also an existing Python binding by Sascha Boehme. See \ref contrib. The following features are added in this release: - Enable check_assumptions without enclosing push/pop. This resolves the limitation described in \ref sub_release_limitations_2_0. - Expose coefficients used in arithmetical proofs. - Allow quantified theory axioms. The following bugs are fixed in this release: - Fixes to the SMT-LIB 2.0 pretty printing mode. - Detect miss-annotated SMT-LIB benchmarks to avoid crashes when using the wrong solvers. Thanks to Trevor Hansen. - A digression in the managed API from 2.10 when passing null parameters. - Crash/incorrect handling of inequalities over the reals during quantifier elimination. Thanks to Mikkel Larsen Pedersen. - Bug in destructive equality resolution. Thanks to Sascha Boehme. - Bug in initialization for x64_mt executable on SMT benchmarks. Thanks to Alvin Cheung. Version 2.11 ============ The following features are added in this release: - SMT-LIB 2.0 parsing support for (! ..) in quantifiers and (_ ..). - Allow passing strings to function and sort declarations in the .NET Theory builders. - Add a parameter to the proof construct for theory lemmas to indicate which theory provided the lemma. - More detailed proof production in rewrite steps. The following bugs are fixed in this release: - A bug in BV propagation. Thanks to Trevor Hansen. Version 2.10 ============ The following bugs are fixed in this release: - Inconsistent printing of integer and real types from the low level and SMT-LIB pretty printers. Thanks to Sascha Boehme. - Missing relevancy propagation and memory smash in user-theory plugins. Thanks to Stan Rosenberg. Version 2.9 =========== The following bugs are fixed in this release: - Incorrect constant folding of extraction for large bit-vectors. Thanks to Alvin. - Z3 crashed when using patterns that are variables. Thanks to Michael Emmi. - Unsound array property fragment handling of non-integer types. Thanks to Juergen Christ. - The quantifier elimination procedure for data-types has been replaced. Thanks to Josh Berdine. - Refresh 2.9.1: Add missing AssumeEq to the .NET managed API. Thanks to Stan Rosenberg. Version 2.8 =========== The following features have been added: - User theories: The user can add theory solvers that get invoked by Z3's core during search. See also \ref theory_plugin_ex. - SMT2 features: parse smt2 let bindings. The following bugs are fixed in this release: - Incorrect semantics of constant folding for (bvsmod 0 x), where x is positive, incorrect constant folding for bvsdiv, incorrect simplification of bvnor, bvnand, incorrect compilation of bvshl when using a shift amount that evaluates to the length of the bit-vector. Thanks to Trevor Hansen and Robert Brummayer. - Incorrect NNF conversion in linear quantifier elimination routines. Thanks to Josh Berdine. - Missing constant folding of extraction for large bit-vectors. Thanks to Alvin. - Missing APIs for bvredand and bvredor. Version 2.7 =========== The following features have been added: - Partial support for SMT-LIB 2.0 format: Added declare-fun, define-fun, declare-sort, define-sort, get-value - Added coercion function to_int and testing function is_int. To coerce from reals to integers and to test whether a real is an integer. The function to_real was already supported. - Added Z3_repeat to create the repetition of bit-vectors. The following bugs are fixed in this release: - Incorrect semantics of constant folding for bvsmod. - Incorrect semantics of constant folding for div/mod. Thanks to Sascha Boehme. - Non-termination problem associated with option LOOKAHEAD=true. It gets set for QF_UF in auto-configuration mode. Thanks to Pierre-Christophe Bué. - Incorrect axioms created for injective functions. Thanks to Sascha Boehme. - Stack overflow during simplification of large nested bit-vector terms. Thanks to David Molnar. - Crash in unsat-core generation when enabling SOLVER=true. Thanks to Lucas Cordeiro. - Unlimited cache growth while simplifying bit-vectors. Thanks to Eric Landers. - Crash when solving array property formulas using non-standard array operators. Thanks to Sascha Boehme. Version 2.6 =========== This release fixes a few bugs. Thanks to Marko Kääramees for reporting a bug in the strong context simplifier and to Josh Berdine. This release also introduces some new preprocessing features: - More efficient destructive equality resolution DER=true. - DISTRIBUTE_FORALL=true (distributes universal quantifiers over conjunctions, this transformation may affect pattern inference). - Rewriter that uses universally quantified equations PRE_DEMODULATOR=true (yes, the option name is not good, we will change it in a future release). - REDUCE_ARGS=true (this transformation is essentially a partial ackermannization for functions where a particular argument is always an interpreted value). - Better support for macro detection (a macro is a universally quantified formula of the form Forall X. F(X) = T[X]). We also change the option name, now it is called MACRO_FINDER=true. - ELIM_QUANTIFIERS=true enables quantifier elimination methods. Previous variants called QUANT_ARITH are deprecated. Version 2.5 =========== This release introduces the following features: - STRONG_CONTEXT_SIMPLIFIER=true allows simplifying sub-formulas to true/false depending on context-dependent information. The approach that we use is described on the Microsoft Z3 forum. - Some parameter values can be updated over the API. This functionality is called Z3_update_param_value in the C API. This is particularly useful for turning the strong context simplifier on and off. It also fixes bugs reported by Enric Rodríguez Carbonell, Nuno Lopes, Josh Berdine, Ethan Jackson, Rob Quigley and Lucas Cordeiro. Version 2.4 =========== This release introduces the following features: - Labeled literals for the SMT-LIB format. The Simplify format has supported labeled formulas to simplify displaying counter-examples. Section \ref smtlib_labels explains how labels are now supported in the SMT-LIB format. - Preliminary support for SMT-LIB2 It fixes the following bugs: - Bug in non-linear arithmetic routines. - Crash observed a class of modular integer arithmetic formulas. - Incomplete saturation leading to incorrectly sat labeling. - Crash in the bit-vector procedure when using int2bv and bv2int. Thanks to Michal Moskal, Sascha Boehme and Ethan Jackson. Version 2.3 =========== This release introduces the following features: - F# Quotation utilities. The release contains a new directory 'utils'. It contains utilities built on top of Z3. The main one is support for translating F# quoted expressions into Z3 formulas. - QUANT_ARITH configuration. Complete quantifier-elimination simplification for linear real and linear integer arithmetic. QUANT_ARITH=1 uses Ferrante/Rackhoff for reals and Cooper's method for integers. QUANT_ARITH=2 uses Fourier-Motzkin for reals and the Omega test for integers. It fixes the following bugs: - Incorrect simplification of map over store in the extended array theory. Reported by Catalin Hritcu. - Incomplete handling of equality propagation with constant arrays. Reported by Catalin Hritcu. - Crash in bit-vector theory. - Incorrectness in proof reconstruction for quantifier manipulation. Thanks to Catalin Hritcu, Nikolai Tillmann and Sascha Boehme. Version 2.2 =========== This release fixes minor bugs. It introduces some additional features in the SMT-LIB front-end to make it easier to parse new operators in the theory of arrays. These are described in \ref smtlibext. Version 2.1 =========== This is a bug fix release. Many thanks to Robert Brummayer, Carine Pascal, François Remy, Rajesh K Karmani, Roberto Lublinerman and numerous others for their feedback and bug reports. Version 2.0 =========== - Parallel Z3. Thanks to Christoph Wintersteiger there is a binary supporting running multiple instances of Z3 from different threads, but more interestingly, also making use of multiple cores for a single formula. - Check Assumptions. The binary API exposes a new call #Z3_check_assumptions, which allows passing in additional assumptions while checking for consistency of the already asserted formulas. The API function returns a subset of the assumptions that were used in an unsatisfiable core. It also returns an optional proof object. - Proof Objects. The #Z3_check_assumptions returns a proof object if the configuration flag PROOF_MODE is set to 1 or 2. - Partial support for non-linear arithmetic. The support uses support for computing Groebner bases. It allows solving some, but far from all, formulas using polynomials over the reals. Uses should be aware that the support for non-linear arithmetic (over the reals) is not complete in Z3. - Recursive data-types. The theory of well-founded recursive data-types is supported over the binary APIs. It supports ground satisfiability checking for tuples, enumeration types (scalars), lists and mutually recursive data-types. z3-z3-4.8.7/azure-pipelines.yml000066400000000000000000000072541356505360400162760ustar00rootroot00000000000000 variables: cmakeStdArgs: '-DZ3_BUILD_DOTNET_BINDINGS=True -DZ3_BUILD_JAVA_BINDINGS=True -DZ3_BUILD_PYTHON_BINDINGS=True -G "Ninja" ../' asanEnv: 'CXXFLAGS="${CXXFLAGS} -fsanitize=address -fno-omit-frame-pointer" CFLAGS="${CFLAGS} -fsanitize=address -fno-omit-frame-pointer"' ubsanEnv: 'CXXFLAGS="${CXXFLAGS} -fsanitize=undefined" CFLAGS="${CFLAGS} -fsanitize=undefined"' jobs: - job: "LinuxPythonDebug" displayName: "Ubuntu build - python make - debug" pool: vmImage: "Ubuntu-16.04" strategy: matrix: MT: cmdLine: 'python scripts/mk_make.py -d --java --dotnet' ST: cmdLine: './configure --single-threaded' steps: - script: $(cmdLine) - script: | set -e cd build make -j3 make -j3 examples make -j3 test-z3 cd .. - template: scripts/test-z3.yml - template: scripts/test-regressions.yml - template: scripts/generate-doc.yml # ./cpp_example # ./c_example # TBD: # test python bindings # build documentation # Asan, ubsan, msan - job: "LinuxCMake" displayName: "Ubuntu build - cmake" pool: vmImage: "Ubuntu-16.04" strategy: matrix: debugClang: cmdLine: 'CC=clang CXX=clang++ cmake $(cmakeStdArgs)' runUnitTest: 'True' releaseClang: cmdLine: 'CC=clang CXX=clang++ cmake -DCMAKE_BUILD_TYPE=Release $(cmakeStdArgs)' runUnitTest: 'True' debugGcc: cmdLine: 'CC=gcc CXX=g++ cmake $(cmakeStdArgs)' runUnitTest: 'True' releaseSTGcc: cmdLine: 'CC=gcc CXX=g++ cmake -DCMAKE_BUILD_TYPE=Release -DSINGLE_THREADED=ON $(cmakeStdArgs)' runUnitTest: 'True' # gccX86: # cmdLine: 'CXXFLAGS="${CXXFLAGS} -m32" CFLAGS="${CFLAGS} -m32" CC=gcc-5 CXX=g++-5 cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo $(cmakeStdArgs)' # runUnitTest: 'True' # asan: # cmdLine: '$(asanEnv) cmake $(cmakeStdArgs)' # runUnitTest: 'False' steps: - script: sudo apt-get install ninja-build - script: | set -e mkdir build cd build $(cmdLine) ninja ninja test-z3 cd .. - script: | cd build ./test-z3 -a cd .. condition: eq(variables['runUnitTest'], 'True') - template: scripts/test-examples-cmake.yml # - template: scripts/test-jupyter.yml # - template: scripts/test-java-cmake.yml - template: scripts/test-regressions.yml - job: "Windows2017" displayName: "Windows 2017 build" pool: vmImage: "vs2017-win2016" steps: - script: scripts\vsts-vs2017.cmd x64 - job: "Windows2017ARM64" displayName: "Windows 2017 ARM64 build" pool: vmImage: "vs2017-win2016" steps: - script: scripts\vsts-vs2017.cmd amd64_arm64 - job: "MacOS" displayName: "MacOS build" pool: vmImage: "macOS-10.14" steps: - script: python scripts/mk_make.py -d --java --dotnet - script: | set -e cd build make -j3 make -j3 examples make -j3 test-z3 ./cpp_example ./c_example cd .. - template: scripts/test-z3.yml - template: scripts/test-regressions.yml - job: "MacOSCMake" displayName: "MacOS build with CMake" pool: vmImage: "macOS-10.14" steps: - script: brew install ninja - script: | set -e mkdir build cd build CC=clang CXX=clang++ cmake -DZ3_BUILD_JAVA_BINDINGS=True -DZ3_BUILD_PYTHON_BINDINGS=True -DZ3_BUILD_DOTNET_BINDINGS=False -G "Ninja" ../ ninja ninja test-z3 cd .. - template: scripts/test-z3.yml - template: scripts/test-examples-cmake.yml - template: scripts/test-regressions.yml # - template: scripts/test-java-cmake.yml z3-z3-4.8.7/cmake/000077500000000000000000000000001356505360400135075ustar00rootroot00000000000000z3-z3-4.8.7/cmake/Z3Config.cmake.in000066400000000000000000000024711356505360400165440ustar00rootroot00000000000000################################################################################ # @AUTO_GEN_MSG@ # # This file is intended to be consumed by clients who wish to use Z3 from CMake. # It can be used by doing `find_package(Z3 config)` from within a # `CMakeLists.txt` file. If CMake doesn't find this package automatically you # can give it a hint by passing `-DZ3_DIR=` to the CMake invocation where # `` is the path to the directory containing this file. # # This file was built for the @CONFIG_FILE_TYPE@. ################################################################################ # Exported targets include("${CMAKE_CURRENT_LIST_DIR}/Z3Targets.cmake") @PACKAGE_INIT@ # Version information set(Z3_VERSION_MAJOR @Z3_VERSION_MAJOR@) set(Z3_VERSION_MINOR @Z3_VERSION_MINOR@) set(Z3_VERSION_PATCH @Z3_VERSION_PATCH@) set(Z3_VERSION_TWEAK @Z3_VERSION_TWEAK@) set(Z3_VERSION_STRING "${Z3_VERSION_MAJOR}.${Z3_VERSION_MINOR}.${Z3_VERSION_PATCH}.${Z3_VERSION_TWEAK}") # NOTE: We can't use `set_and_check()` here because this a list of paths. # List of include directories set(Z3_C_INCLUDE_DIRS @PACKAGE_Z3_FIRST_PACKAGE_INCLUDE_DIR@ @PACKAGE_Z3_SECOND_PACKAGE_INCLUDE_DIR@) set(Z3_CXX_INCLUDE_DIRS @PACKAGE_Z3_CXX_PACKAGE_INCLUDE_DIR@ ${Z3_C_INCLUDE_DIRS}) # List of libraries to link against set(Z3_LIBRARIES "z3::libz3") z3-z3-4.8.7/cmake/cmake_uninstall.cmake.in000066400000000000000000000016561356505360400202770ustar00rootroot00000000000000if(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") message(FATAL_ERROR "Cannot find install manifest: " "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") endif() file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) string(REGEX REPLACE "\n" ";" files "${files}") foreach(file ${files}) set(_full_file_path "$ENV{DESTDIR}${file}") message(STATUS "Uninstalling ${_full_file_path}") if(IS_SYMLINK "${_full_file_path}" OR EXISTS "${_full_file_path}") # We could use ``file(REMOVE ...)`` here but then we wouldn't # know if the removal failed. execute_process(COMMAND "@CMAKE_COMMAND@" "-E" "remove" "${_full_file_path}" RESULT_VARIABLE rm_retval ) if(NOT "${rm_retval}" STREQUAL 0) message(FATAL_ERROR "Problem when removing \"${_full_file_path}\"") endif() else() message(STATUS "File \"${_full_file_path}\" does not exist.") endif() endforeach() z3-z3-4.8.7/cmake/compiler_lto.cmake000066400000000000000000000044461356505360400172110ustar00rootroot00000000000000option(Z3_LINK_TIME_OPTIMIZATION "Use link time optimiziation" OFF) if (Z3_LINK_TIME_OPTIMIZATION) message(STATUS "LTO enabled") set(build_types_with_lto "RELEASE" "RELWITHDEBINFO") if (DEFINED CMAKE_CONFIGURATION_TYPES) # Multi configuration generator message(STATUS "Note LTO is only enabled for the following configurations: ${build_types_with_lto}") else() # Single configuration generator string(TOUPPER "${CMAKE_BUILD_TYPE}" _build_type_upper) list(FIND build_types_with_lto "${_build_type_upper}" _index) if ("${_index}" EQUAL -1) message(FATAL_ERROR "Configuration ${CMAKE_BUILD_TYPE} does not support LTO." "You should set Z3_LINK_TIME_OPTIMIZATION to OFF.") endif() endif() set(_lto_compiler_flag "") set(_lto_linker_flag "") if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU")) set(_lto_compiler_flag "-flto") set(_lto_linker_flag "-flto") elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") set(_lto_compiler_flag "/GL") set(_lto_linker_flag "/LTCG") else() message(FATAL_ERROR "Can't enable LTO for compiler \"${CMAKE_CXX_COMPILER_ID}\"." "You should set Z3_LINK_TIME_OPTIMIZATION to OFF.") endif() CHECK_CXX_COMPILER_FLAG("${_lto_compiler_flag}" HAS_LTO) if (NOT HAS_LTO) message(FATAL_ERROR "Compiler does not support LTO") endif() foreach (_config ${build_types_with_lto}) # Set flags compiler and linker flags globally rather than using # `Z3_COMPONENT_CXX_FLAGS` and `Z3_DEPENDENT_EXTRA_CXX_LINK_FLAGS` # respectively. We need per configuration compiler and linker flags. The # `LINK_FLAGS` property (which we populate with # `Z3_DEPENDENT_EXTRA_CXX_LINK_FLAGS`) doesn't seem to support generator # expressions so we can't do `$<$:${_lto_linker_flag}>`. set(CMAKE_CXX_FLAGS_${_config} "${CMAKE_CXX_FLAGS_${_config}} ${_lto_compiler_flag}") set(CMAKE_EXE_LINKER_FLAGS_${_config} "${CMAKE_EXE_LINKER_FLAGS_${_config}} ${_lto_linker_flag}") set(CMAKE_SHARED_LINKER_FLAGS_${_config} "${CMAKE_SHARED_LINKER_FLAGS_${_config}} ${_lto_linker_flag}") set(CMAKE_STATIC_LINKER_FLAGS_${_config} "${CMAKE_STATIC_LINKER_FLAGS_${_config}} ${_lto_linker_flag}") endforeach() else() message(STATUS "LTO disabled") endif() z3-z3-4.8.7/cmake/compiler_warnings.cmake000066400000000000000000000121311356505360400202310ustar00rootroot00000000000000################################################################################ # Compiler warning flags ################################################################################ # These are passed to relevant compiler provided they are supported set(GCC_AND_CLANG_WARNINGS "-Wall" ) set(GCC_ONLY_WARNINGS "") set(CLANG_ONLY_WARNINGS "") set(MSVC_WARNINGS "/W3") ################################################################################ # Serious warnings ################################################################################ # This declares the flags that are passed to the compiler when # `WARNINGS_AS_ERRORS` is set to `SERIOUS_ONLY`. Only flags that are supported # by the compiler are used. # # In effect this a "whitelist" approach where we explicitly tell the compiler # which warnings we want to be treated as errors. The alternative would be a # "blacklist" approach where we ask the compiler to treat all warnings are # treated as errors but then we explicitly list which warnings which should be # allowed. # # The "whitelist" approach seems simpiler because we can incrementally add # warnings we "think are serious". # TODO: Add more warnings that are considered serious enough that we should # treat them as errors. set(GCC_AND_CLANG_WARNINGS_AS_ERRORS # https://clang.llvm.org/docs/DiagnosticsReference.html#wodr "-Werror=odr" ) set(GCC_WARNINGS_AS_ERRORS "" ) set(CLANG_WARNINGS_AS_ERRORS # https://clang.llvm.org/docs/DiagnosticsReference.html#wdelete-non-virtual-dtor "-Werror=delete-non-virtual-dtor" # https://clang.llvm.org/docs/DiagnosticsReference.html#woverloaded-virtual "-Werror=overloaded-virtual" ) ################################################################################ # Test warning/error flags ################################################################################ set(WARNING_FLAGS_TO_CHECK "") set(WARNING_AS_ERROR_FLAGS_TO_CHECK "") if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") list(APPEND WARNING_FLAGS_TO_CHECK ${GCC_AND_CLANG_WARNINGS}) list(APPEND WARNING_FLAGS_TO_CHECK ${GCC_ONLY_WARNINGS}) list(APPEND WARNING_AS_ERROR_FLAGS_TO_CHECK ${GCC_AND_CLANG_WARNINGS_AS_ERRORS}) list(APPEND WARNING_AS_ERROR_FLAGS_TO_CHECK ${GCC_WARNINGS_AS_ERRORS}) elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") list(APPEND WARNING_FLAGS_TO_CHECK ${GCC_AND_CLANG_WARNINGS}) list(APPEND WARNING_FLAGS_TO_CHECK ${CLANG_ONLY_WARNINGS}) list(APPEND WARNING_AS_ERROR_FLAGS_TO_CHECK ${GCC_AND_CLANG_WARNINGS_AS_ERRORS}) list(APPEND WARNING_AS_ERROR_FLAGS_TO_CHECK ${CLANG_WARNINGS_AS_ERRORS}) elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") list(APPEND WARNING_FLAGS_TO_CHECK ${MSVC_WARNINGS}) # CMake's default flags include /W3 already so remove them if # they already exist. if ("${CMAKE_CXX_FLAGS}" MATCHES "/W3") string(REPLACE "/W3" "" _cmake_cxx_flags_remove_w3 "${CMAKE_CXX_FLAGS}") set(CMAKE_CXX_FLAGS "${_cmake_cxx_flags_remove_w3}" CACHE STRING "" FORCE) endif() else() message(AUTHOR_WARNING "Unknown compiler") endif() # Loop through flags and use the ones which the compiler supports foreach (flag ${WARNING_FLAGS_TO_CHECK}) z3_add_cxx_flag("${flag}") endforeach() # TODO: Remove this eventually. # Detect legacy `WARNINGS_AS_ERRORS` boolean option and covert to new # to new option type. get_property( WARNINGS_AS_ERRORS_CACHE_VAR_TYPE CACHE WARNINGS_AS_ERRORS PROPERTY TYPE ) if ("${WARNINGS_AS_ERRORS_CACHE_VAR_TYPE}" STREQUAL "BOOL") message(WARNING "Detected legacy WARNINGS_AS_ERRORS option. Upgrading") set(WARNINGS_AS_ERRORS_DEFAULT "${WARNINGS_AS_ERRORS}") # Delete old entry unset(WARNINGS_AS_ERRORS CACHE) else() set(WARNINGS_AS_ERRORS_DEFAULT "SERIOUS_ONLY") endif() set(WARNINGS_AS_ERRORS ${WARNINGS_AS_ERRORS_DEFAULT} CACHE STRING "Treat warnings as errors. ON, OFF, or SERIOUS_ONLY" ) # Set GUI options set_property( CACHE WARNINGS_AS_ERRORS PROPERTY STRINGS "ON;OFF;SERIOUS_ONLY" ) if ("${WARNINGS_AS_ERRORS}" STREQUAL "ON") message(STATUS "Treating compiler warnings as errors") if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU")) list(APPEND Z3_COMPONENT_CXX_FLAGS "-Werror") elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") list(APPEND Z3_COMPONENT_CXX_FLAGS "/WX") else() message(AUTHOR_WARNING "Unknown compiler") endif() elseif ("${WARNINGS_AS_ERRORS}" STREQUAL "SERIOUS_ONLY") message(STATUS "Treating only serious compiler warnings as errors") # Loop through the flags foreach (flag ${WARNING_AS_ERROR_FLAGS_TO_CHECK}) # Add globally because some flags need to be passed at link time. z3_add_cxx_flag("${flag}" GLOBAL) endforeach() elseif ("${WARNINGS_AS_ERRORS}" STREQUAL "OFF") message(STATUS "Not treating compiler warnings as errors") if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") # Warnings as errors is off by default for MSVC so setting this # is not necessary but this duplicates the behaviour of the old # build system. list(APPEND Z3_COMPONENT_CXX_FLAGS "/WX-") endif() else() message(FATAL_ERROR "WARNINGS_AS_ERRORS set to unsupported value \"${WARNINGS_AS_ERRORS}\"" ) endif() z3-z3-4.8.7/cmake/cxx_compiler_flags_overrides.cmake000066400000000000000000000014031356505360400224410ustar00rootroot00000000000000# This file overrides the default compiler flags for CMake's built-in # configurations (CMAKE_BUILD_TYPE). Most compiler flags should not be set here. # The main purpose is to have very fine grained control of the compiler flags. # We only override the defaults for Clang and GCC right now. # CMake's MSVC logic is complicated so for now it's better to just inherit CMake's defaults. if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU")) # Taken from Modules/Compiler/GNU.cmake set(CMAKE_CXX_FLAGS_INIT "") set(CMAKE_CXX_FLAGS_DEBUG_INIT "-g -O0") set(CMAKE_CXX_FLAGS_MINSIZEREL_INIT "-Os -DNDEBUG") set(CMAKE_CXX_FLAGS_RELEASE_INIT "-O3 -DNDEBUG") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO_INIT "-O2 -g -DNDEBUG") endif() z3-z3-4.8.7/cmake/git_utils.cmake000066400000000000000000000174261356505360400165260ustar00rootroot00000000000000# add_git_dir_dependency(GIT_DIR SUCCESS_VAR) # # Adds a configure time dependency on the git directory such that if the HEAD # of the git directory changes CMake will be forced to re-run. This useful # for fetching the current git hash and including it in the build. # # `GIT_DOT_FILE` is the path to the git directory (i.e. the `.git` directory) or # `.git` file used by a git worktree. # `SUCCESS_VAR` is the name of the variable to set. It will be set to TRUE # if the dependency was successfully added and FALSE otherwise. function(add_git_dir_dependency GIT_DOT_FILE SUCCESS_VAR) if (NOT "${ARGC}" EQUAL 2) message(FATAL_ERROR "Invalid number (${ARGC}) of arguments") endif() if (NOT IS_ABSOLUTE "${GIT_DOT_FILE}") message(FATAL_ERROR "GIT_DOT_FILE (\"${GIT_DOT_FILE}\") is not an absolute path") endif() if (NOT EXISTS "${GIT_DOT_FILE}") message(FATAL_ERROR "GIT_DOT_FILE (\"${GIT_DOT_FILE}\") does not exist") endif() if (NOT IS_DIRECTORY "${GIT_DOT_FILE}") # Might be a git worktree. In this case we need parse out the worktree # git directory file(READ "${GIT_DOT_FILE}" GIT_DOT_FILE_DATA LIMIT 512) string(STRIP "${GIT_DOT_FILE_DATA}" GIT_DOT_FILE_DATA_STRIPPED) if ("${GIT_DOT_FILE_DATA_STRIPPED}" MATCHES "^gitdir:[ ]*(.+)$") # Git worktree message(STATUS "Found git worktree") set(GIT_WORKTREE_DIR "${CMAKE_MATCH_1}") set(GIT_HEAD_FILE "${GIT_WORKTREE_DIR}/HEAD") # Figure out where real git directory lives set(GIT_COMMON_DIR_FILE "${GIT_WORKTREE_DIR}/commondir") if (NOT EXISTS "${GIT_COMMON_DIR_FILE}") message(FATAL_ERROR "Found git worktree dir but could not find \"${GIT_COMMON_DIR_FILE}\"") endif() file(READ "${GIT_COMMON_DIR_FILE}" GIT_COMMON_DIR_FILE_DATA LIMIT 512) string(STRIP "${GIT_COMMON_DIR_FILE_DATA}" GIT_COMMON_DIR_FILE_DATA_STRIPPED) get_filename_component(GIT_DIR "${GIT_WORKTREE_DIR}/${GIT_COMMON_DIR_FILE_DATA_STRIPPED}" ABSOLUTE) if (NOT IS_DIRECTORY "${GIT_DIR}") message(FATAL_ERROR "Failed to compute path to git directory from git worktree") endif() else() message(FATAL_ERROR "GIT_DOT_FILE (\"${GIT_DOT_FILE}\") is not a directory or a pointer to git worktree directory") endif() else() # Just a normal `.git` directory message(STATUS "Found simple git working directory") set(GIT_HEAD_FILE "${GIT_DOT_FILE}/HEAD") set(GIT_DIR "${GIT_DOT_FILE}") endif() message(STATUS "Found git directory \"${GIT_DIR}\"") if (NOT EXISTS "${GIT_HEAD_FILE}") message(AUTHOR_WARNING "Git head file \"${GIT_HEAD_FILE}\" cannot be found") set(${SUCCESS_VAR} FALSE PARENT_SCOPE) return() endif() # List of files in the git tree that CMake configuration should depend on set(GIT_FILE_DEPS "${GIT_HEAD_FILE}") # Examine the HEAD and workout what additional dependencies there are. file(READ "${GIT_HEAD_FILE}" GIT_HEAD_DATA LIMIT 128) string(STRIP "${GIT_HEAD_DATA}" GIT_HEAD_DATA_STRIPPED) if ("${GIT_HEAD_DATA_STRIPPED}" MATCHES "^ref:[ ]*(.+)$") # HEAD points at a reference. set(GIT_REF "${CMAKE_MATCH_1}") if (EXISTS "${GIT_DIR}/${GIT_REF}") # Unpacked reference. The file contains the commit hash # so add a dependency on this file so that if we stay on this # reference (i.e. branch) but change commit CMake will be forced # to reconfigure. list(APPEND GIT_FILE_DEPS "${GIT_DIR}/${GIT_REF}") elseif(EXISTS "${GIT_DIR}/packed-refs") # The ref must be packed (see `man git-pack-refs`). list(APPEND GIT_FILE_DEPS "${GIT_DIR}/packed-refs") else() # Fail message(AUTHOR_WARNING "Unhandled git reference") set(${SUCCESS_VAR} FALSE PARENT_SCOPE) return() endif() else() # Detached HEAD. # No other dependencies needed endif() # FIXME: # This is the directory we will copy (via `configure_file()`) git files # into. This is a hack. It would be better to use the # `CMAKE_CONFIGURE_DEPENDS` directory property but that feature is not # available in CMake 2.8.12. So we use `configure_file()` to effectively # do the same thing. When the source file to `configure_file()` changes # it will trigger a re-run of CMake. set(GIT_CMAKE_FILES_DIR "${CMAKE_CURRENT_BINARY_DIR}/git_cmake_files") file(MAKE_DIRECTORY "${GIT_CMAKE_FILES_DIR}") foreach (git_dependency ${GIT_FILE_DEPS}) message(STATUS "Adding git dependency \"${git_dependency}\"") configure_file( "${git_dependency}" "${GIT_CMAKE_FILES_DIR}" COPYONLY ) endforeach() set(${SUCCESS_VAR} TRUE PARENT_SCOPE) endfunction() # get_git_head_hash(GIT_DOT_FILE OUTPUT_VAR) # # Retrieve the current commit hash for a git working directory where # `GIT_DOT_FILE` is the `.git` directory or `.git` pointer file in a git # worktree in the root of the git working directory. # # `OUTPUT_VAR` should be the name of the variable to put the result in. If this # function fails then either a fatal error will be raised or `OUTPUT_VAR` will # contain a string with the suffix `NOTFOUND` which can be used in CMake `if()` # commands. function(get_git_head_hash GIT_DOT_FILE OUTPUT_VAR) if (NOT "${ARGC}" EQUAL 2) message(FATAL_ERROR "Invalid number of arguments") endif() if (NOT EXISTS "${GIT_DOT_FILE}") message(FATAL_ERROR "\"${GIT_DOT_FILE}\" does not exist") endif() if (NOT IS_ABSOLUTE "${GIT_DOT_FILE}") message(FATAL_ERROR \""${GIT_DOT_FILE}\" is not an absolute path") endif() find_package(Git) # NOTE: Use `GIT_FOUND` rather than `Git_FOUND` which was only # available in CMake >= 3.5 if (NOT GIT_FOUND) set(${OUTPUT_VAR} "GIT-NOTFOUND" PARENT_SCOPE) return() endif() get_filename_component(GIT_WORKING_DIR "${GIT_DOT_FILE}" DIRECTORY) execute_process( COMMAND "${GIT_EXECUTABLE}" "rev-parse" "-q" # Quiet "HEAD" WORKING_DIRECTORY "${GIT_WORKING_DIR}" RESULT_VARIABLE GIT_EXIT_CODE OUTPUT_VARIABLE Z3_GIT_HASH OUTPUT_STRIP_TRAILING_WHITESPACE ) if (NOT "${GIT_EXIT_CODE}" EQUAL 0) message(WARNING "Failed to execute git") set(${OUTPUT_VAR} NOTFOUND PARENT_SCOPE) return() endif() set(${OUTPUT_VAR} "${Z3_GIT_HASH}" PARENT_SCOPE) endfunction() # get_git_head_describe(GIT_DOT_FILE OUTPUT_VAR) # # Retrieve the output of `git describe` for a git working directory where # `GIT_DOT_FILE` is the `.git` directory or `.git` pointer file in a git # worktree in the root of the git working directory. # # `OUTPUT_VAR` should be the name of the variable to put the result in. If this # function fails then either a fatal error will be raised or `OUTPUT_VAR` will # contain a string with the suffix `NOTFOUND` which can be used in CMake `if()` # commands. function(get_git_head_describe GIT_DOT_FILE OUTPUT_VAR) if (NOT "${ARGC}" EQUAL 2) message(FATAL_ERROR "Invalid number of arguments") endif() if (NOT EXISTS "${GIT_DOT_FILE}") message(FATAL_ERROR "\"${GIT_DOT_FILE}\" does not exist") endif() if (NOT IS_ABSOLUTE "${GIT_DOT_FILE}") message(FATAL_ERROR \""${GIT_DOT_FILE}\" is not an absolute path") endif() find_package(Git) # NOTE: Use `GIT_FOUND` rather than `Git_FOUND` which was only # available in CMake >= 3.5 if (NOT GIT_FOUND) set(${OUTPUT_VAR} "GIT-NOTFOUND" PARENT_SCOPE) return() endif() get_filename_component(GIT_WORKING_DIR "${GIT_DOT_FILE}" DIRECTORY) execute_process( COMMAND "${GIT_EXECUTABLE}" "describe" "--long" WORKING_DIRECTORY "${GIT_WORKING_DIR}" RESULT_VARIABLE GIT_EXIT_CODE OUTPUT_VARIABLE Z3_GIT_DESCRIPTION OUTPUT_STRIP_TRAILING_WHITESPACE ) if (NOT "${GIT_EXIT_CODE}" EQUAL 0) message(WARNING "Failed to execute git") set(${OUTPUT_VAR} NOTFOUND PARENT_SCOPE) return() endif() set(${OUTPUT_VAR} "${Z3_GIT_DESCRIPTION}" PARENT_SCOPE) endfunction() z3-z3-4.8.7/cmake/modules/000077500000000000000000000000001356505360400151575ustar00rootroot00000000000000z3-z3-4.8.7/cmake/modules/DotnetImports.props.in000066400000000000000000000004311356505360400214620ustar00rootroot00000000000000 ${_DN_OUTPUT_PATH}/ ${_DN_XPLAT_LIB_DIR}/ ${_DN_VERSION} ${_DN_CUSTOM_BUILDPROPS} z3-z3-4.8.7/cmake/modules/FindDotnet.cmake000066400000000000000000000457701356505360400202340ustar00rootroot00000000000000#.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 ${PROJECT_BINARY_DIR}/tools) IF(NUGET_EXE) MESSAGE("-- Found nuget: ${NUGET_EXE}") ELSE() SET(NUGET_EXE ${PROJECT_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 ${PROJECT_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 ${PROJECT_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 "${PROJECT_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 ${PROJECT_BINARY_DIR}) SET(_DN_XPLAT_LIB_DIR ${PROJECT_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) z3-z3-4.8.7/cmake/modules/FindGMP.cmake000066400000000000000000000036011356505360400174050ustar00rootroot00000000000000# Tries to find an install of the GNU multiple precision library # # Once done this will define # GMP_FOUND - BOOL: System has the GMP library installed # GMP_INCLUDE_DIRS - LIST:The GMP include directories # GMP_C_LIBRARIES - LIST:The libraries needed to use GMP via it's C interface # GMP_CXX_LIBRARIES - LIST:The libraries needed to use GMP via it's C++ interface include(FindPackageHandleStandardArgs) # Try to find libraries find_library(GMP_C_LIBRARIES NAMES gmp DOC "GMP C libraries" ) if (GMP_C_LIBRARIES) message(STATUS "Found GMP C library: \"${GMP_C_LIBRARIES}\"") else() message(STATUS "Could not find GMP C library") endif() find_library(GMP_CXX_LIBRARIES NAMES gmpxx DOC "GMP C++ libraries" ) if (GMP_CXX_LIBRARIES) message(STATUS "Found GMP C++ library: \"${GMP_CXX_LIBRARIES}\"") else() message(STATUS "Could not find GMP C++ library") endif() # Try to find headers find_path(GMP_C_INCLUDES NAMES gmp.h DOC "GMP C header" ) if (GMP_C_INCLUDES) message(STATUS "Found GMP C include path: \"${GMP_C_INCLUDES}\"") else() message(STATUS "Could not find GMP C include path") endif() find_path(GMP_CXX_INCLUDES NAMES gmpxx.h DOC "GMP C++ header" ) if (GMP_CXX_INCLUDES) message(STATUS "Found GMP C++ include path: \"${GMP_CXX_INCLUDES}\"") else() message(STATUS "Could not find GMP C++ include path") endif() if (GMP_C_LIBRARIES AND GMP_CXX_LIBRARIES AND GMP_C_INCLUDES AND GMP_CXX_INCLUDES) set(GMP_INCLUDE_DIRS "${GMP_C_INCLUDES}" "${GMP_CXX_INCLUDES}") list(REMOVE_DUPLICATES GMP_INCLUDE_DIRS) message(STATUS "Found GMP") else() message(STATUS "Could not find GMP") endif() # TODO: We should check we can link some simple code against libgmp and libgmpxx # Handle QUIET and REQUIRED and check the necessary variables were set and if so # set ``GMP_FOUND`` find_package_handle_standard_args(GMP DEFAULT_MSG GMP_INCLUDE_DIRS GMP_C_LIBRARIES GMP_CXX_LIBRARIES) z3-z3-4.8.7/cmake/msvc_legacy_quirks.cmake000066400000000000000000000177201356505360400204120ustar00rootroot00000000000000# This file ether sets or notes various compiler and linker flags for MSVC that # were defined by the old python/Makefile based build system but # don't obviously belong in the other sections in the CMake build system. ################################################################################ # Compiler definitions ################################################################################ # FIXME: All the commented out defines should be removed once # we are confident it is correct to not set them. set(Z3_MSVC_LEGACY_DEFINES # Don't set `_DEBUG`. The old build system sets this but this # is wrong. MSVC will set this depending on which runtime is being used. # See https://msdn.microsoft.com/en-us/library/b0084kay.aspx # _DEBUG # The old build system only set `UNICODE` and `_UNICODE` for x86_64 release. # That seems completely wrong so set it for all configurations. # According to https://blogs.msdn.microsoft.com/oldnewthing/20040212-00/?p=40643/ # `UNICODE` affects Windows headers and `_UNICODE` affects C runtime header files. # There is some discussion of this define at https://msdn.microsoft.com/en-us/library/dybsewaf.aspx UNICODE _UNICODE ) if ("${TARGET_ARCHITECTURE}" STREQUAL "x86_64") list(APPEND Z3_MSVC_LEGACY_DEFINES "" # Don't set `_LIB`. The old build system sets this for x86_64 release # build. This flag doesn't seem to be documented but a stackoverflow # post hints that this is usually set when building a static library. # See http://stackoverflow.com/questions/35034683/how-to-tell-if-current-project-is-dll-or-static-lib # This seems wrong give that the old build system set this regardless # whether or not libz3 was static or shared so its probably best # to not set for now. #$<$:_LIB> #$<$:_LIB> # Don't set `_CONSOLE`. The old build system sets for all configurations # except x86_64 release. It seems ( https://codeyarns.com/2010/12/02/visual-c-windows-and-console-subsystems/ ) # that `_CONSOLE` used to be defined by older Visual C++ environments. # Setting this undocumented option seems like a bad idea so let's not do it. #$<$ #$<$ # Don't set `ASYNC_COMMANDS`. The old build system sets this for x86_64 # release but this macro does not appear to be used anywhere and is not # documented so don't set it for now. #$<$:ASYNC_COMMANDS> #$<$:ASYNC_COMMANDS> ) else() list(APPEND Z3_MSVC_LEGACY_DEFINES "" # Don't set `_CONSOLE`. See reasoning above. #_CONSOLE ) endif() # Note we don't set WIN32 or _WINDOWS because # CMake provides that for us. As a sanity check make sure the option # is present. if (NOT "${CMAKE_CXX_FLAGS}" MATCHES "/D[ ]*WIN32") message(FATAL_ERROR "\"/D WIN32\" is missing") endif() if (NOT "${CMAKE_CXX_FLAGS}" MATCHES "/D[ ]*_WINDOWS") message(FATAL_ERROR "\"/D _WINDOWS\" is missing") endif() list(APPEND Z3_COMPONENT_CXX_DEFINES ${Z3_MSVC_LEGACY_DEFINES}) ################################################################################ # Compiler flags ################################################################################ # FIXME: We might want to move this out somewhere else if we decide # we want to set `-fno-omit-frame-pointer` for gcc/clang. # No omit frame pointer set(NO_OMIT_FRAME_POINTER_MSVC_FLAG "/Oy-") CHECK_CXX_COMPILER_FLAG(${NO_OMIT_FRAME_POINTER_MSVC_FLAG} HAS_MSVC_NO_OMIT_FRAME_POINTER) if (NOT HAS_MSVC_NO_OMIT_FRAME_POINTER) message(FATAL_ERROR "${NO_OMIT_FRAME_POINTER_MSVC_FLAG} flag not supported") endif() # FIXME: This doesn't make a huge amount of sense but the old # build system kept the frame pointer for all configurations # except x86_64 release (I don't know why the frame pointer # is kept for i686 release). if ("${TARGET_ARCHITECTURE}" STREQUAL "x86_64") list(APPEND Z3_COMPONENT_CXX_FLAGS $<$:${NO_OMIT_FRAME_POINTER_MSVC_FLAG}> $<$:${NO_OMIT_FRAME_POINTER_MSVC_FLAG}> ) else() list(APPEND Z3_COMPONENT_CXX_FLAGS ${NO_OMIT_FRAME_POINTER_MSVC_FLAG}) endif() if (("${TARGET_ARCHITECTURE}" STREQUAL "x86_64") OR ("${TARGET_ARCHITECTURE}" STREQUAL "i686")) # Use __cdecl calling convention. Apparently this is MSVC's default # but the old build system set it so for completeness set it too. # See https://msdn.microsoft.com/en-us/library/46t77ak2.aspx z3_add_cxx_flag("/Gd" REQUIRED) endif() z3_add_cxx_flag("/EHsc" REQUIRED) ################################################################################ # Linker flags ################################################################################ # By default CMake enables incremental linking for Debug and RelWithDebInfo # builds. The old build system disables it for all builds so try to do the same # by changing all configurations if necessary string(TOUPPER "${available_build_types}" _build_types_as_upper) foreach (_build_type ${_build_types_as_upper}) foreach (t EXE SHARED STATIC) set(_replacement "/INCREMENTAL:NO") # Remove any existing incremental flags string(REGEX REPLACE "/INCREMENTAL:YES" "${_replacement}" _replaced_linker_flags "${CMAKE_${t}_LINKER_FLAGS_${_build_type}}") string(REGEX REPLACE "(/INCREMENTAL$)|(/INCREMENTAL )" "${_replacement} " _replaced_linker_flags "${_replaced_linker_flags}") if (NOT "${_replaced_linker_flags}" MATCHES "${_replacement}") # Flag not present. Add it string(APPEND _replaced_linker_flags " ${_replacement}") endif() set(CMAKE_${t}_LINKER_FLAGS_${_build_type} "${_replaced_linker_flags}") endforeach() endforeach() # The original build system passes `/STACK:` to the linker. # This size comes from the original build system. # FIXME: What is the rationale behind this? set(STACK_SIZE_MSVC_LINKER 8388608) # MSVC documentation (https://msdn.microsoft.com/en-us/library/35yc2tc3.aspx) # says this only matters for executables which is why this is not being # set for CMAKE_SHARED_LINKER_FLAGS or CMAKE_STATIC_LINKER_FLAGS. string(APPEND CMAKE_EXE_LINKER_FLAGS " /STACK:${STACK_SIZE_MSVC_LINKER}") # The original build system passes `/SUBSYSTEM:` to the linker where `` # depends on what is being linked. Where `` is `CONSOLE` for executables # and `WINDOWS` for shard libraries. # We don't need to pass `/SUBSYSTEM:CONSOLE` because CMake will do this for # us when building executables because we don't pass the `WIN32` argument to # `add_executable()`. # FIXME: We probably don't need this. https://msdn.microsoft.com/en-us/library/fcc1zstk.aspx # suggests that `/SUBSYSTEM:` only matters for executables. string(APPEND CMAKE_SHARED_LINKER_FLAGS " /SUBSYSTEM:WINDOWS") # FIXME: The following linker flags are weird. They are set in all configurations # in the old build system except release x86_64. We try to emulate this here but # this is likely the wrong thing to do. foreach (_build_type ${_build_types_as_upper}) if ("${TARGET_ARCHITECTURE}" STREQUAL "x86_64" AND ("${_build_type}" STREQUAL "RELEASE" OR "${_build_type}" STREQUAL "RELWITHDEBINFO") ) message(AUTHOR_WARNING "Skipping legacy linker MSVC options for x86_64 ${_build_type}") else() # Linker optimizations. # See https://msdn.microsoft.com/en-us/library/bxwfs976.aspx string(APPEND CMAKE_EXE_LINKER_FLAGS_${_build_type} " /OPT:REF /OPT:ICF") string(APPEND CMAKE_SHARED_LINKER_FLAGS_${_build_type} " /OPT:REF /OPT:ICF") # FIXME: This is not necessary. This is MSVC's default. # See https://msdn.microsoft.com/en-us/library/b1kw34cb.aspx string(APPEND CMAKE_EXE_LINKER_FLAGS_${_build_type} " /TLBID:1") string(APPEND CMAKE_SHARED_LINKER_FLAGS_${_build_type} " /TLBID:1") # FIXME: This is not necessary. This is MSVC's default. # Indicate that the executable is compatible with DEP # See https://msdn.microsoft.com/en-us/library/ms235442.aspx string(APPEND CMAKE_EXE_LINKER_FLAGS_${_build_type} " /NXCOMPAT") endif() endforeach() z3-z3-4.8.7/cmake/target_arch_detect.cmake000066400000000000000000000016211356505360400203240ustar00rootroot00000000000000############################################################################### # Target detection # # We abuse the compiler preprocessor to work out what target the compiler is # building for. The nice thing about this approach is that we'll detect the # right target even if we are using a cross compiler. ############################################################################### function(detect_target_architecture OUTPUT_VAR) try_run(run_result compile_result "${PROJECT_BINARY_DIR}" "${PROJECT_SOURCE_DIR}/cmake/target_arch_detect.cpp" COMPILE_OUTPUT_VARIABLE compiler_output ) if (compile_result) message(FATAL_ERROR "Expected compile to fail") endif() string(REGEX MATCH "CMAKE_TARGET_ARCH_([a-zA-Z0-9_]+)" arch "${compiler_output}") # Strip out prefix string(REPLACE "CMAKE_TARGET_ARCH_" "" arch "${arch}") set(${OUTPUT_VAR} "${arch}" PARENT_SCOPE) endfunction() z3-z3-4.8.7/cmake/target_arch_detect.cpp000066400000000000000000000004771356505360400200360ustar00rootroot00000000000000// This is used by the CMake build to detect // what architecture the compiler is targeting. // TODO: Add more targets here #if defined(__i386__) || defined(_M_IX86) #error CMAKE_TARGET_ARCH_i686 #elif defined(__x86_64__) || defined(_M_X64) #error CMAKE_TARGET_ARCH_x86_64 #else #error CMAKE_TARGET_ARCH_unknown #endif z3-z3-4.8.7/cmake/z3_add_component.cmake000066400000000000000000000356511356505360400177510ustar00rootroot00000000000000include(CMakeParseArguments) define_property(GLOBAL PROPERTY Z3_LIBZ3_COMPONENTS BRIEF_DOCS "List of Z3 components to use in libz3" FULL_DOCS "List of Z3 components to use in libz3") function(z3_expand_dependencies output_var) if (ARGC LESS 2) message(FATAL_ERROR "Invalid number of arguments") endif() # Remaining args should be component names set(_expanded_deps ${ARGN}) set(_old_number_of_deps 0) list(LENGTH _expanded_deps _number_of_deps) while (_number_of_deps GREATER _old_number_of_deps) set(_old_number_of_deps "${_number_of_deps}") # Loop over the known dependencies and retrieve their dependencies set(_old_expanded_deps ${_expanded_deps}) foreach (dependency ${_old_expanded_deps}) get_property(_depdeps GLOBAL PROPERTY Z3_${dependency}_DEPS) list(APPEND _expanded_deps ${_depdeps}) unset(_depdeps) endforeach() list(REMOVE_DUPLICATES _expanded_deps) list(LENGTH _expanded_deps _number_of_deps) endwhile() set(${output_var} ${_expanded_deps} PARENT_SCOPE) endfunction() function(z3_add_component_dependencies_to_target target_name) if (ARGC LESS 2) message(FATAL_ERROR "Invalid number of arguments") endif() if (NOT (TARGET ${target_name})) message(FATAL_ERROR "Target \"${target_name}\" does not exist") endif() # Remaining args should be component names set(_expanded_deps ${ARGN}) foreach (dependency ${_expanded_deps}) # Ensure this component's dependencies are built before this component. # This is important because we might need the generated header files in # other components. add_dependencies(${target_name} ${dependency}) endforeach() endfunction() # z3_add_component(component_name # [NOT_LIBZ3_COMPONENT] # SOURCES source1 [source2...] # [COMPONENT_DEPENDENCIES component1 [component2...]] # [PYG_FILES pygfile1 [pygfile2...]] # [TACTIC_HEADERS header_file1 [header_file2...]] # [EXTRA_REGISTER_MODULE_HEADERS header_file1 [header_file2...]] # [MEMORY_INIT_FINALIZER_HEADERS header_file1 [header_file2...]] # ) # # Declares a Z3 component (as a CMake "object library") with target name # ``component_name``. # # The option ``NOT_LIBZ3_COMPONENT`` declares that the # component should not be included in libz3. If this is not specified # the component will be included in libz3. # # The mandatory ``SOURCES`` keyword should be followed by the source files # (including any files generated at build or configure time) that are should be # included in the component. It is not necessary to list header files here as # CMake infers header file dependencies unless that header file is generated at # build time. # # The optional ``COMPONENT_DEPENDENCIES`` keyword should be followed by a list of # components that ``component_name`` should depend on. The components listed here # must have already been declared using ``z3_add_component()``. Listing components # here causes them to be built before ``component_name``. It also currently causes # the include directories used by the transistive closure of the dependencies # to be added to the list of include directories used to build ``component_name``. # # The optional ``PYG_FILES`` keyword should be followed by a list of one or # more ``.pyg`` files that should used to be generate # ``_params.hpp`` header files used by the ``component_name``. # This generated file will automatically be scanned for the register module # declarations (i.e. ``REG_PARAMS()``, ``REG_MODULE_PARAMS()``, and # ``REG_MODULE_DESCRIPTION()``). # # The optional ``TACTIC_HEADERS`` keyword should be followed by a list of one or # more header files that declare a tactic and/or a probe that is part of this # component (see ``ADD_TACTIC()`` and ``ADD_PROBE()``). # # The optional ``EXTRA_REGISTER_MODULE_HEADERS`` keyword should be followed by a list # of one or more header files that contain module registration declarations. # NOTE: The header files generated from ``.pyg`` files don't need to be included. # # The optional ``MEMORY_INIT_FINALIZER_HEADERS`` keyword should be followed by a list # of one or more header files that contain memory initializer/finalizer declarations # (i.e. ``ADD_INITIALIZER()`` or ``ADD_FINALIZER()``). macro(z3_add_component component_name) CMAKE_PARSE_ARGUMENTS("Z3_MOD" "NOT_LIBZ3_COMPONENT" "" "SOURCES;COMPONENT_DEPENDENCIES;PYG_FILES;TACTIC_HEADERS;EXTRA_REGISTER_MODULE_HEADERS;MEMORY_INIT_FINALIZER_HEADERS" ${ARGN}) message(STATUS "Adding component ${component_name}") # Note: We don't check the sources exist here because # they might be generated files that don't exist yet. set(_list_generated_headers "") set_property(GLOBAL PROPERTY Z3_${component_name}_REGISTER_MODULE_HEADERS "") foreach (pyg_file ${Z3_MOD_PYG_FILES}) set(_full_pyg_file_path "${CMAKE_CURRENT_SOURCE_DIR}/${pyg_file}") if (NOT (EXISTS "${_full_pyg_file_path}")) message(FATAL_ERROR "\"${_full_pyg_file_path}\" does not exist") endif() string(REPLACE ".pyg" ".hpp" _output_file "${pyg_file}") if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${_output_file}") message(FATAL_ERROR "\"${CMAKE_CURRENT_SOURCE_DIR}/${_output_file}\" " ${z3_polluted_tree_msg} ) endif() set(_full_output_file_path "${CMAKE_CURRENT_BINARY_DIR}/${_output_file}") message(STATUS "Adding rule to generate \"${_output_file}\"") add_custom_command(OUTPUT "${_output_file}" COMMAND "${PYTHON_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/scripts/pyg2hpp.py" "${_full_pyg_file_path}" "${CMAKE_CURRENT_BINARY_DIR}" MAIN_DEPENDENCY "${_full_pyg_file_path}" DEPENDS "${PROJECT_SOURCE_DIR}/scripts/pyg2hpp.py" ${Z3_GENERATED_FILE_EXTRA_DEPENDENCIES} COMMENT "Generating \"${_full_output_file_path}\" from \"${pyg_file}\"" WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" ${ADD_CUSTOM_COMMAND_USES_TERMINAL_ARG} VERBATIM ) list(APPEND _list_generated_headers "${_full_output_file_path}") # FIXME: This implicit dependency of a generated file depending on # generated files was inherited from the old build system. # Typically generated headers contain `REG_PARAMS()`, `REG_MODULE_PARAMS()` # and `REG_MODULE_DESCRIPTION()` declarations so add to the list of # header files to scan. set_property(GLOBAL APPEND PROPERTY Z3_${component_name}_REGISTER_MODULE_HEADERS "${_full_output_file_path}" ) endforeach() unset(_full_include_dir_path) unset(_full_output_file_path) unset(_output_file) # Add tactic/probe headers to global property set_property(GLOBAL PROPERTY Z3_${component_name}_TACTIC_HEADERS "") foreach (tactic_header ${Z3_MOD_TACTIC_HEADERS}) set(_full_tactic_header_file_path "${CMAKE_CURRENT_SOURCE_DIR}/${tactic_header}") if (NOT (EXISTS "${_full_tactic_header_file_path}")) message(FATAL_ERROR "\"${_full_tactic_header_file_path}\" does not exist") endif() set_property(GLOBAL APPEND PROPERTY Z3_${component_name}_TACTIC_HEADERS "${_full_tactic_header_file_path}" ) endforeach() unset(_full_tactic_header_file_path) # Add additional register module headers foreach (extra_register_module_header ${Z3_MOD_EXTRA_REGISTER_MODULE_HEADERS}) set(_full_extra_register_module_header_path "${CMAKE_CURRENT_SOURCE_DIR}/${extra_register_module_header}" ) if (NOT (EXISTS "${_full_extra_register_module_header_path}")) message(FATAL_ERROR "\"${_full_extra_register_module_header_path}\" does not exist") endif() set_property(GLOBAL APPEND PROPERTY Z3_${component_name}_REGISTER_MODULE_HEADERS "${_full_extra_register_module_header_path}" ) endforeach() unset(_full_extra_register_module_header) # Add memory initializer/finalizer headers to global property set_property(GLOBAL PROPERTY Z3_${component_name}_MEM_INIT_FINALIZER_HEADERS "") foreach (memory_init_finalizer_header ${Z3_MOD_MEMORY_INIT_FINALIZER_HEADERS}) set(_full_memory_init_finalizer_header_path "${CMAKE_CURRENT_SOURCE_DIR}/${memory_init_finalizer_header}") if (NOT (EXISTS "${_full_memory_init_finalizer_header_path}")) message(FATAL_ERROR "\"${_full_memory_init_finalizer_header_path}\" does not exist") endif() set_property(GLOBAL APPEND PROPERTY Z3_${component_name}_MEM_INIT_FINALIZER_HEADERS "${_full_memory_init_finalizer_header_path}" ) endforeach() unset(_full_memory_init_finalizer_header_path) # Using "object" libraries here means we have a convenient # name to refer to a component in CMake but we don't actually # create a static/library from them. This allows us to easily # build a static or dynamic library from the object libraries # on all platforms. Is this added flexibility worth the linking # overhead it adds? add_library(${component_name} OBJECT ${Z3_MOD_SOURCES} ${_list_generated_headers}) unset(_list_generated_headers) # Add definitions foreach (define ${Z3_COMPONENT_CXX_DEFINES}) target_compile_definitions(${component_name} PRIVATE ${define}) endforeach() # Add compiler flags foreach (flag ${Z3_COMPONENT_CXX_FLAGS}) target_compile_options(${component_name} PRIVATE ${flag}) endforeach() # It's unfortunate that we have to manage dependencies ourselves. # # If we weren't building "object" libraries we could use # ``` # target_link_libraries(${component_name} INTERFACE ${Z3_MOD_COMPONENT_DEPENDENCIES}) # ``` # but we can't do that with "object" libraries. set_property(GLOBAL PROPERTY Z3_${component_name}_DEPS "") # Record this component's dependencies foreach (dependency ${Z3_MOD_COMPONENT_DEPENDENCIES}) if (NOT (TARGET ${dependency})) message(FATAL_ERROR "Component \"${component_name}\" depends on a non existent component \"${dependency}\"") endif() set_property(GLOBAL APPEND PROPERTY Z3_${component_name}_DEPS "${dependency}") endforeach() # Determine all the components that this component depends on set(_expanded_deps "") if (DEFINED Z3_MOD_COMPONENT_DEPENDENCIES) z3_expand_dependencies(_expanded_deps ${Z3_MOD_COMPONENT_DEPENDENCIES}) z3_add_component_dependencies_to_target(${component_name} ${_expanded_deps}) endif() #message(STATUS "Component \"${component_name}\" has the following dependencies ${_expanded_deps}") # Add any extra include directories foreach (extra_include ${Z3_COMPONENT_EXTRA_INCLUDE_DIRS}) target_include_directories(${component_name} PRIVATE "${extra_include}") endforeach() if (NOT Z3_MOD_NOT_LIBZ3_COMPONENT) # Add this component to the global list of Z3 components for libz3 set_property(GLOBAL APPEND PROPERTY Z3_LIBZ3_COMPONENTS ${component_name}) endif() endmacro() macro(z3_add_install_tactic_rule) # Arguments should be component names to use if (ARGC LESS 1) message(FATAL_ERROR "There should be at least one component") endif() if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/install_tactic.cpp") message(FATAL_ERROR "\"${CMAKE_CURRENT_SOURCE_DIR}/install_tactic.cpp\"" ${z3_polluted_tree_msg} ) endif() z3_expand_dependencies(_expanded_components ${ARGN}) # Get header files that declare tactics/probes set(_tactic_header_files "") foreach (dependency ${_expanded_components}) get_property(_component_tactic_header_files GLOBAL PROPERTY Z3_${dependency}_TACTIC_HEADERS ) list(APPEND _tactic_header_files "${_component_tactic_header_files}") endforeach() unset(_component_tactic_header_files) string(REPLACE ";" "\n" _tactic_header_files "${_tactic_header_files}") file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/install_tactic.deps" ${_tactic_header_files}) add_custom_command(OUTPUT "install_tactic.cpp" COMMAND "${PYTHON_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/scripts/mk_install_tactic_cpp.py" "${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/install_tactic.deps" DEPENDS "${PROJECT_SOURCE_DIR}/scripts/mk_install_tactic_cpp.py" ${Z3_GENERATED_FILE_EXTRA_DEPENDENCIES} "${CMAKE_CURRENT_BINARY_DIR}/install_tactic.deps" COMMENT "Generating \"${CMAKE_CURRENT_BINARY_DIR}/install_tactic.cpp\"" ${ADD_CUSTOM_COMMAND_USES_TERMINAL_ARG} VERBATIM ) unset(_expanded_components) unset(_tactic_header_files) endmacro() macro(z3_add_memory_initializer_rule) # Arguments should be component names to use if (ARGC LESS 1) message(FATAL_ERROR "There should be at least one component") endif() if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/mem_initializer.cpp") message(FATAL_ERROR "\"${CMAKE_CURRENT_SOURCE_DIR}/mem_initializer.cpp\"" ${z3_polluted_tree_msg} ) endif() z3_expand_dependencies(_expanded_components ${ARGN}) # Get header files that declare initializers and finalizers set(_mem_init_finalize_headers "") foreach (dependency ${_expanded_components}) get_property(_dep_mem_init_finalize_headers GLOBAL PROPERTY Z3_${dependency}_MEM_INIT_FINALIZER_HEADERS ) list(APPEND _mem_init_finalize_headers ${_dep_mem_init_finalize_headers}) endforeach() add_custom_command(OUTPUT "mem_initializer.cpp" COMMAND "${PYTHON_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/scripts/mk_mem_initializer_cpp.py" "${CMAKE_CURRENT_BINARY_DIR}" ${_mem_init_finalize_headers} DEPENDS "${PROJECT_SOURCE_DIR}/scripts/mk_mem_initializer_cpp.py" ${Z3_GENERATED_FILE_EXTRA_DEPENDENCIES} ${_mem_init_finalize_headers} COMMENT "Generating \"${CMAKE_CURRENT_BINARY_DIR}/mem_initializer.cpp\"" ${ADD_CUSTOM_COMMAND_USES_TERMINAL_ARG} VERBATIM ) unset(_mem_init_finalize_headers) unset(_expanded_components) endmacro() macro(z3_add_gparams_register_modules_rule) # Arguments should be component names to use if (ARGC LESS 1) message(FATAL_ERROR "There should be at least one component") endif() if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/gparams_register_modules.cpp") message(FATAL_ERROR "\"${CMAKE_CURRENT_SOURCE_DIR}/gparams_register_modules.cpp\"" ${z3_polluted_tree_msg} ) endif() z3_expand_dependencies(_expanded_components ${ARGN}) # Get the list of header files to parse set(_register_module_header_files "") foreach (dependency ${_expanded_components}) get_property(_component_register_module_header_files GLOBAL PROPERTY Z3_${dependency}_REGISTER_MODULE_HEADERS) list(APPEND _register_module_header_files ${_component_register_module_header_files}) endforeach() unset(_component_register_module_header_files) add_custom_command(OUTPUT "gparams_register_modules.cpp" COMMAND "${PYTHON_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/scripts/mk_gparams_register_modules_cpp.py" "${CMAKE_CURRENT_BINARY_DIR}" ${_register_module_header_files} DEPENDS "${PROJECT_SOURCE_DIR}/scripts/mk_gparams_register_modules_cpp.py" ${Z3_GENERATED_FILE_EXTRA_DEPENDENCIES} ${_register_module_header_files} COMMENT "Generating \"${CMAKE_CURRENT_BINARY_DIR}/gparams_register_modules.cpp\"" ${ADD_CUSTOM_COMMAND_USES_TERMINAL_ARG} VERBATIM ) unset(_expanded_components) unset(_register_module_header_files) endmacro() z3-z3-4.8.7/cmake/z3_add_cxx_flag.cmake000066400000000000000000000024151356505360400175320ustar00rootroot00000000000000include(CheckCXXCompilerFlag) include(CMakeParseArguments) function(z3_add_cxx_flag flag) CMAKE_PARSE_ARGUMENTS(z3_add_flag "REQUIRED;GLOBAL" "" "" ${ARGN}) string(REPLACE "-" "_" SANITIZED_FLAG_NAME "${flag}") string(REPLACE "/" "_" SANITIZED_FLAG_NAME "${SANITIZED_FLAG_NAME}") string(REPLACE "=" "_" SANITIZED_FLAG_NAME "${SANITIZED_FLAG_NAME}") string(REPLACE " " "_" SANITIZED_FLAG_NAME "${SANITIZED_FLAG_NAME}") string(REPLACE ":" "_" SANITIZED_FLAG_NAME "${SANITIZED_FLAG_NAME}") string(REPLACE "+" "_" SANITIZED_FLAG_NAME "${SANITIZED_FLAG_NAME}") unset(HAS_${SANITIZED_FLAG_NAME}) CHECK_CXX_COMPILER_FLAG("${flag}" HAS_${SANITIZED_FLAG_NAME}) if (z3_add_flag_REQUIRED AND NOT HAS_${SANITIZED_FLAG_NAME}) message(FATAL_ERROR "The flag \"${flag}\" is required but your C++ compiler doesn't support it") endif() if (HAS_${SANITIZED_FLAG_NAME}) message(STATUS "C++ compiler supports ${flag}") if (z3_add_flag_GLOBAL) # Set globally set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${flag} " PARENT_SCOPE) else() list(APPEND Z3_COMPONENT_CXX_FLAGS "${flag}") set(Z3_COMPONENT_CXX_FLAGS "${Z3_COMPONENT_CXX_FLAGS}" PARENT_SCOPE) endif() else() message(STATUS "C++ compiler does not support ${flag}") endif() endfunction() z3-z3-4.8.7/cmake/z3_append_linker_flag_list_to_target.cmake000066400000000000000000000014321356505360400240340ustar00rootroot00000000000000# The LINK_FLAGS property of a target in CMake is unfortunately a string and # not a list. This function takes a list of linker flags and iterates through # them to append them as strings to the ``LINK_FLAGS`` property of # the specified target. # E.g. # z3_append_linker_flag_list_to_target(mytarget "-static") function(z3_append_linker_flag_list_to_target target) if (NOT (TARGET "${target}")) message(FATAL_ERROR "Specified target \"${target}\" is not a target") endif() foreach(flag ${ARGN}) #message(STATUS "Appending link flag \"${flag}\" to target ${target}") # Note that space inside the quoted string is required so that the flags # are space separated. set_property(TARGET ${target} APPEND_STRING PROPERTY LINK_FLAGS " ${flag}") endforeach() endfunction() z3-z3-4.8.7/configure000077500000000000000000000006421356505360400143400ustar00rootroot00000000000000#!/bin/sh if test -z $PYTHON; then PYTHON=python fi if ! which $PYTHON > /dev/null; then echo "'$PYTHON' not found. Try to set the environment variable PYTHON." exit 1 fi if ! $PYTHON -c "print('testing')" > /dev/null ; then echo "'$PYTHON' failed to execute basic test script. Try to set the environment variable PYTHON with a working Python interpreter." exit 1 fi $PYTHON scripts/mk_make.py "$@" z3-z3-4.8.7/contrib/000077500000000000000000000000001356505360400140675ustar00rootroot00000000000000z3-z3-4.8.7/contrib/ci/000077500000000000000000000000001356505360400144625ustar00rootroot00000000000000z3-z3-4.8.7/contrib/ci/Dockerfiles/000077500000000000000000000000001356505360400167145ustar00rootroot00000000000000z3-z3-4.8.7/contrib/ci/Dockerfiles/z3_base_ubuntu32_16.04.Dockerfile000066400000000000000000000027461356505360400245430ustar00rootroot00000000000000# This base image is not officially supported by Docker it # is generated by running # ``` # ./update.sh xenial # ``` # from git@github.com:daald/docker-brew-ubuntu-core-32bit.git # at commit 34ea593b40b423755b7d46b6c8c89fc8162ea74b # # We could actually store the image generated by this Dockerfile # rather than just the bare image. However given we have a TravisCI # cache I'm not sure if it faster to use the TravisCI cache or to # download from DockerHub everytime. FROM z3prover/ubuntu32:16.04 RUN apt-get update && \ apt-get -y --no-install-recommends install \ binutils \ clang \ clang-3.9 \ cmake \ doxygen \ default-jdk \ gcc \ gcc-5 \ git \ graphviz \ g++ \ g++ \ libgmp-dev \ libgomp1 \ libomp5 \ libomp-dev \ llvm-3.9 \ make \ ninja-build \ python3 \ python3-setuptools \ python2.7 \ python-setuptools \ sudo # Create `user` user for container with password `user`. and give it # password-less sudo access RUN useradd -m user && \ echo user:user | chpasswd && \ cp /etc/sudoers /etc/sudoers.bak && \ echo 'user ALL=(root) NOPASSWD: ALL' >> /etc/sudoers USER user WORKDIR /home/user # 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 z3-z3-4.8.7/contrib/ci/Dockerfiles/z3_base_ubuntu_14.04.Dockerfile000066400000000000000000000025251356505360400243670ustar00rootroot00000000000000FROM ubuntu:14.04 RUN apt-get update && \ apt-get -y --no-install-recommends install \ apt-transport-https \ binutils \ clang-3.9 \ curl \ doxygen \ default-jdk \ gcc-multilib \ gcc-4.8-multilib \ git \ graphviz \ g++-multilib \ g++-4.8-multilib \ libgmp-dev \ libgomp1 \ lib32gomp1 \ llvm-3.9 \ make \ 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 && \ echo user:user | chpasswd && \ cp /etc/sudoers /etc/sudoers.bak && \ 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 z3-z3-4.8.7/contrib/ci/Dockerfiles/z3_base_ubuntu_16.04.Dockerfile000066400000000000000000000023251356505360400243670ustar00rootroot00000000000000FROM 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 \ gcc-5-multilib \ git \ graphviz \ g++-multilib \ g++-5-multilib \ libgmp-dev \ libgomp1 \ libomp5 \ libomp-dev \ llvm-3.9 \ make \ ninja-build \ python3 \ python3-setuptools \ python2.7 \ 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 && \ echo user:user | chpasswd && \ cp /etc/sudoers /etc/sudoers.bak && \ 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 z3-z3-4.8.7/contrib/ci/Dockerfiles/z3_build.Dockerfile000066400000000000000000000070501356505360400224220ustar00rootroot00000000000000ARG DOCKER_IMAGE_BASE FROM ${DOCKER_IMAGE_BASE} # Build arguments. This can be changed when invoking # `docker build`. ARG ASAN_BUILD ARG ASAN_DSO ARG BUILD_DOCS ARG CC ARG CXX ARG DOTNET_BINDINGS ARG JAVA_BINDINGS ARG NO_SUPPRESS_OUTPUT ARG PYTHON_BINDINGS ARG PYTHON_EXECUTABLE=/usr/bin/python2.7 ARG RUN_API_EXAMPLES ARG RUN_SYSTEM_TESTS ARG RUN_UNIT_TESTS ARG SANITIZER_PRINT_SUPPRESSIONS ARG TARGET_ARCH ARG TEST_INSTALL ARG UBSAN_BUILD ARG Z3_USE_LIBGMP ARG USE_LTO ARG Z3_SRC_DIR=/home/user/z3_src ARG Z3_BUILD_TYPE ARG Z3_CMAKE_GENERATOR ARG Z3_INSTALL_PREFIX ARG Z3_STATIC_BUILD ARG Z3_SYSTEM_TEST_GIT_REVISION ARG Z3_WARNINGS_AS_ERRORS ARG Z3_VERBOSE_BUILD_OUTPUT ENV \ ASAN_BUILD=${ASAN_BUILD} \ ASAN_DSO=${ASAN_DSO} \ BUILD_DOCS=${BUILD_DOCS} \ CC=${CC} \ CXX=${CXX} \ DOTNET_BINDINGS=${DOTNET_BINDINGS} \ JAVA_BINDINGS=${JAVA_BINDINGS} \ NO_SUPPRESS_OUTPUT=${NO_SUPPRESS_OUTPUT} \ PYTHON_BINDINGS=${PYTHON_BINDINGS} \ PYTHON_EXECUTABLE=${PYTHON_EXECUTABLE} \ SANITIZER_PRINT_SUPPRESSIONS=${SANITIZER_PRINT_SUPPRESSIONS} \ RUN_API_EXAMPLES=${RUN_API_EXAMPLES} \ RUN_SYSTEM_TESTS=${RUN_SYSTEM_TESTS} \ RUN_UNIT_TESTS=${RUN_UNIT_TESTS} \ TARGET_ARCH=${TARGET_ARCH} \ TEST_INSTALL=${TEST_INSTALL} \ UBSAN_BUILD=${UBSAN_BUILD} \ Z3_USE_LIBGMP=${Z3_USE_LIBGMP} \ USE_LTO=${USE_LTO} \ Z3_SRC_DIR=${Z3_SRC_DIR} \ Z3_BUILD_DIR=/home/user/z3_build \ Z3_BUILD_TYPE=${Z3_BUILD_TYPE} \ Z3_CMAKE_GENERATOR=${Z3_CMAKE_GENERATOR} \ Z3_VERBOSE_BUILD_OUTPUT=${Z3_VERBOSE_BUILD_OUTPUT} \ Z3_STATIC_BUILD=${Z3_STATIC_BUILD} \ Z3_SYSTEM_TEST_DIR=/home/user/z3_system_test \ Z3_SYSTEM_TEST_GIT_REVISION=${Z3_SYSTEM_TEST_GIT_REVISION} \ Z3_WARNINGS_AS_ERRORS=${Z3_WARNINGS_AS_ERRORS} \ Z3_INSTALL_PREFIX=${Z3_INSTALL_PREFIX} # We add context across incrementally to maximal cache reuse # Build Z3 RUN mkdir -p "${Z3_SRC_DIR}" && \ mkdir -p "${Z3_SRC_DIR}/contrib/ci/scripts" && \ mkdir -p "${Z3_SRC_DIR}/contrib/suppressions/sanitizers" # Deliberately leave out `contrib` ADD /cmake ${Z3_SRC_DIR}/cmake/ ADD /doc ${Z3_SRC_DIR}/doc/ ADD /examples ${Z3_SRC_DIR}/examples/ ADD /scripts ${Z3_SRC_DIR}/scripts/ ADD /src ${Z3_SRC_DIR}/src/ ADD *.txt *.md RELEASE_NOTES ${Z3_SRC_DIR}/ ADD \ /contrib/ci/scripts/build_z3_cmake.sh \ /contrib/ci/scripts/ci_defaults.sh \ /contrib/ci/scripts/set_compiler_flags.sh \ /contrib/ci/scripts/set_generator_args.sh \ ${Z3_SRC_DIR}/contrib/ci/scripts/ RUN ${Z3_SRC_DIR}/contrib/ci/scripts/build_z3_cmake.sh # Test building docs ADD \ /contrib/ci/scripts/test_z3_docs.sh \ /contrib/ci/scripts/run_quiet.sh \ ${Z3_SRC_DIR}/contrib/ci/scripts/ RUN ${Z3_SRC_DIR}/contrib/ci/scripts/test_z3_docs.sh # Test examples ADD \ /contrib/ci/scripts/test_z3_examples_cmake.sh \ /contrib/ci/scripts/sanitizer_env.sh \ ${Z3_SRC_DIR}/contrib/ci/scripts/ ADD \ /contrib/suppressions/sanitizers/asan.txt \ /contrib/suppressions/sanitizers/lsan.txt \ /contrib/suppressions/sanitizers/ubsan.txt \ ${Z3_SRC_DIR}/contrib/suppressions/sanitizers/ RUN ${Z3_SRC_DIR}/contrib/ci/scripts/test_z3_examples_cmake.sh # Run unit tests ADD \ /contrib/ci/scripts/test_z3_unit_tests_cmake.sh \ ${Z3_SRC_DIR}/contrib/ci/scripts/ RUN ${Z3_SRC_DIR}/contrib/ci/scripts/test_z3_unit_tests_cmake.sh # Run system tests ADD \ /contrib/ci/scripts/test_z3_system_tests.sh \ ${Z3_SRC_DIR}/contrib/ci/scripts/ RUN ${Z3_SRC_DIR}/contrib/ci/scripts/test_z3_system_tests.sh # Test install ADD \ /contrib/ci/scripts/test_z3_install_cmake.sh \ ${Z3_SRC_DIR}/contrib/ci/scripts/ RUN ${Z3_SRC_DIR}/contrib/ci/scripts/test_z3_install_cmake.sh z3-z3-4.8.7/contrib/ci/README.md000066400000000000000000000142611356505360400157450ustar00rootroot00000000000000# Continuous integration scripts ## TravisCI For testing on Linux and macOS we use [TravisCI](https://travis-ci.org/) TravisCI consumes the `.travis.yml` file in the root of the repository to tell it how to build and test Z3. However the logic for building and test Z3 is kept out of this file and instead in a set of scripts in `scripts/`. This avoids coupling the build to TravisCI tightly so we can migrate to another service if required in the future. The scripts rely on a set of environment variables to control the configuration of the build. The `.travis.yml` declares a list of configuration with each configuration setting different environment variables. Note that the build scripts currently only support Z3 built with CMake. Support for building Z3 using the older Python/Makefile build system might be added in the future. ### Configuration variables * `ASAN_BUILD` - Do [AddressSanitizer](https://github.com/google/sanitizers/wiki/AddressSanitizer) build (`0` or `1`) * `BUILD_DOCS` - Build API documentation (`0` or `1`) * `C_COMPILER` - Path to C Compiler * `CXX_COMPILER` - Path to C++ Compiler * `DOTNET_BINDINGS` - Build and test .NET API bindings (`0` or `1`) * `JAVA_BINDINGS` - Build and test Java API bindings (`0` or `1`) * `NO_SUPPRESS_OUTPUT` - Don't suppress output of some commands (`0` or `1`) * `PYTHON_BINDINGS` - Build and test Python API bindings (`0` or `1`) * `RUN_API_EXAMPLES` - Build and run API examples (`0` or `1`) * `RUN_SYSTEM_TESTS` - Run system tests (`0` or `1`) * `RUN_UNIT_TESTS` - Run unit tests (`BUILD_ONLY` or `BUILD_AND_RUN` or `SKIP`) * `SANITIZER_PRINT_SUPPRESSIONS` - Show ASan/UBSan suppressions (`0` or `1`) * `TARGET_ARCH` - Target architecture (`x86_64` or `i686`) * `TEST_INSTALL` - Test running `install` target (`0` or `1`) * `UBSAN_BUILD` - Do [UndefinedBehaviourSanitizer](https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html) build (`0` or `1`) * `Z3_USE_LIBGMP` - Use [GNU multiple precision library](https://gmplib.org/) (`0` or `1`) * `USE_LTO` - Link binaries using link time optimization (`0` or `1`) * `Z3_BUILD_TYPE` - CMake build type (`RelWithDebInfo`, `Release`, `Debug`, or `MinSizeRel`) * `Z3_CMAKE_GENERATOR` - CMake generator (`Ninja` or `Unix Makefiles`) * `Z3_VERBOSE_BUILD_OUTPUT` - Show compile commands in CMake builds (`0` or `1`) * `Z3_STATIC_BUILD` - Build Z3 binaries and libraries statically (`0` or `1`) * `Z3_SYSTEM_TEST_GIT_REVISION` - Git revision of [z3test](https://github.com/Z3Prover/z3test). If empty lastest revision will be used. * `Z3_WARNINGS_AS_ERRORS` - Set the `WARNINGS_AS_ERRORS` CMake option passed to Z3 (`OFF`, `ON`, or `SERIOUS_ONLY`) ### Linux For Linux we use Docker to perform the build so that it easily reproducible on a local machine and so that we can avoid depending on TravisCI's environment and instead use a Linux distribution of our choice. The `scripts/travis_ci_linux_entry_point.sh` script 1. Creates a base image containing all the dependencies needed to build and test Z3 2. Builds and tests Z3 using the base image propagating configuration environment variables (if set) into the build using the `--build-arg` argument of the `docker run` command. The default values of the configuration environment variables can be found in [`scripts/ci_defaults.sh`](scripts/ci_defaults.sh). #### Linux specific configuration variables * `LINUX_BASE` - The base docker image identifier to use (`ubuntu_16.04`, `ubuntu32_16.04`, or `ubuntu_14.04`). #### Reproducing a build locally A build can be reproduced locally by using the `scripts/travis_ci_linux_entry_point.sh` script and setting the appropriate environment variable. For example lets say we wanted to reproduce the build below. ```yaml - LINUX_BASE=ubuntu_16.04 C_COMPILER=/usr/bin/gcc-5 CXX_COMPILER=/usr/bin/g++-5 TARGET_ARCH=x86_64 Z3_BUILD_TYPE=RelWithDebInfo ``` This can be done by running the command ```bash LINUX_BASE=ubuntu_16.04 C_COMPILER=/usr/bin/gcc-5 CXX_COMPILER=/usr/bin/g++-5 TARGET_ARCH=x86_64 Z3_BUILD_TYPE=RelWithDebInfo scripts/travis_ci_linux_entry_point.sh ``` The `docker build` command which we use internally supports caching. What this means in practice is that re-running the above command will re-use successfully completed stages of the build provided they haven't changed. This requires that the `Dockerfiles/z3_build.Dockerfile` is carefully crafted to avoid invalidating the cache when unrelated files sent to the build context change. #### TravisCI docker image cache To improve build times the Z3 base docker images are cached using [TravisCI's cache directory feature](https://docs.travis-ci.com/user/caching). If the `DOCKER_TRAVIS_CI_CACHE_DIR` environment variable is set (see `.travis.yml`) then the directory pointed to by the environment variable is used as a cache for Docker images. The logic for this can be found in `scripts/travis_ci_linux_entry_point.sh`. The build time improvements are rather modest (~ 2 minutes) and the cache is rather large due to TravisCI giving each configuration its own cache. So this feature might be removed in the future. It may be better to just build the base image once (outside of TravisCI), upload it to [DockerHub](https://hub.docker.com/) and have the build pull down the pre-built image every time. An [organization](https://hub.docker.com/u/z3prover/) has been created on DockerHub for this. ### macOS For macOS we execute directly on TravisCI's macOS environment. The entry point for the TravisCI builds is the [`scripts/travis_ci_osx_entry_point.sh`](scripts/travis_ci_osx_entry_point.sh) scripts. #### macOS specific configuration variables * `MACOS_SKIP_DEPS_UPDATE` - If set to `1` installing the necessary build dependencies is skipped. This is useful for local testing if the dependencies are already installed. * `MACOS_UPDATE_CMAKE` - If set to `1` the installed version of CMake will be upgraded. #### Reproducing a build locally To reproduce a build (e.g. like the one shown below) ```yaml - os: osx osx_image: xcode8.3 env: Z3_BUILD_TYPE=RelWithDebInfo ``` Run the following: ```bash TRAVIS_BUILD_DIR=$(pwd) \ Z3_BUILD_TYPE=RelWithDebInfo \ contrib/ci/scripts/travis_ci_osx_entry_point.sh ``` Note this assumes that the current working directory is the root of the Z3 git repository. z3-z3-4.8.7/contrib/ci/maintainers.txt000066400000000000000000000000471356505360400175360ustar00rootroot00000000000000# Maintainers - Dan Liew (@delcypher) z3-z3-4.8.7/contrib/ci/scripts/000077500000000000000000000000001356505360400161515ustar00rootroot00000000000000z3-z3-4.8.7/contrib/ci/scripts/build_z3_cmake.sh000077500000000000000000000067051356505360400213730ustar00rootroot00000000000000#!/bin/bash # This script builds Z3 SCRIPT_DIR="$( cd ${BASH_SOURCE[0]%/*} ; echo $PWD )" set -x set -e set -o pipefail : ${Z3_SRC_DIR?"Z3_SRC_DIR must be specified"} : ${Z3_BUILD_DIR?"Z3_BUILD_DIR must be specified"} : ${Z3_BUILD_TYPE?"Z3_BUILD_TYPE must be specified"} : ${Z3_CMAKE_GENERATOR?"Z3_CMAKE_GENERATOR must be specified"} : ${Z3_STATIC_BUILD?"Z3_STATIC_BUILD must be specified"} : ${Z3_USE_LIBGMP?"Z3_USE_LIBGMP must be specified"} : ${BUILD_DOCS?"BUILD_DOCS must be specified"} : ${PYTHON_EXECUTABLE?"PYTHON_EXECUTABLE must be specified"} : ${PYTHON_BINDINGS?"PYTHON_BINDINGS must be specified"} : ${DOTNET_BINDINGS?"DOTNET_BINDINGS must be specified"} : ${JAVA_BINDINGS?"JAVA_BINDINGS must be specified"} : ${USE_LTO?"USE_LTO must be specified"} : ${Z3_INSTALL_PREFIX?"Z3_INSTALL_PREFIX must be specified"} : ${Z3_WARNINGS_AS_ERRORS?"Z3_WARNINGS_AS_ERRORS must be specified"} : ${UBSAN_BUILD?"UBSAN_BUILD must be specified"} ADDITIONAL_Z3_OPTS=() # Static or dynamic libz3 if [ "X${Z3_STATIC_BUILD}" = "X1" ]; then ADDITIONAL_Z3_OPTS+=('-DZ3_BUILD_LIBZ3_SHARED=OFF') else ADDITIONAL_Z3_OPTS+=('-DZ3_BUILD_LIBZ3_SHARED=ON') fi # Use LibGMP? if [ "X${Z3_USE_LIBGMP}" = "X1" ]; then ADDITIONAL_Z3_OPTS+=('-DZ3_USE_LIB_GMP=ON') else ADDITIONAL_Z3_OPTS+=('-DZ3_USE_LIB_GMP=OFF') fi # Use link time optimziation? if [ "X${USE_LTO}" = "X1" ]; then ADDITIONAL_Z3_OPTS+=('-DZ3_LINK_TIME_OPTIMIZATION=ON') else ADDITIONAL_Z3_OPTS+=('-DZ3_LINK_TIME_OPTIMIZATION=OFF') fi # Build API docs? if [ "X${BUILD_DOCS}" = "X1" ]; then ADDITIONAL_Z3_OPTS+=( \ '-DZ3_BUILD_DOCUMENTATION=ON' \ '-DZ3_ALWAYS_BUILD_DOCS=OFF' \ ) else ADDITIONAL_Z3_OPTS+=('-DZ3_BUILD_DOCUMENTATION=OFF') fi # Python bindings? if [ "X${PYTHON_BINDINGS}" = "X1" ]; then ADDITIONAL_Z3_OPTS+=( \ '-DZ3_BUILD_PYTHON_BINDINGS=ON' \ '-DZ3_INSTALL_PYTHON_BINDINGS=ON' \ ) else ADDITIONAL_Z3_OPTS+=( \ '-DZ3_BUILD_PYTHON_BINDINGS=OFF' \ '-DZ3_INSTALL_PYTHON_BINDINGS=OFF' \ ) fi # .NET bindings? if [ "X${DOTNET_BINDINGS}" = "X1" ]; then ADDITIONAL_Z3_OPTS+=( \ '-DZ3_BUILD_DOTNET_BINDINGS=ON' \ '-DZ3_INSTALL_DOTNET_BINDINGS=ON' \ ) else ADDITIONAL_Z3_OPTS+=( \ '-DZ3_BUILD_DOTNET_BINDINGS=OFF' \ '-DZ3_INSTALL_DOTNET_BINDINGS=OFF' \ ) fi # Java bindings? if [ "X${JAVA_BINDINGS}" = "X1" ]; then ADDITIONAL_Z3_OPTS+=( \ '-DZ3_BUILD_JAVA_BINDINGS=ON' \ '-DZ3_INSTALL_JAVA_BINDINGS=ON' \ ) else ADDITIONAL_Z3_OPTS+=( \ '-DZ3_BUILD_JAVA_BINDINGS=OFF' \ '-DZ3_INSTALL_JAVA_BINDINGS=OFF' \ ) fi # Set compiler flags source ${SCRIPT_DIR}/set_compiler_flags.sh if [ "X${UBSAN_BUILD}" = "X1" ]; then # HACK: When building with UBSan the C++ linker # must be used to avoid the following linker errors. # undefined reference to `__ubsan_vptr_type_cache' # undefined reference to `__ubsan_handle_dynamic_type_cache_miss' ADDITIONAL_Z3_OPTS+=( \ '-DZ3_C_EXAMPLES_FORCE_CXX_LINKER=ON' \ ) fi # Sanity check if [ ! -e "${Z3_SRC_DIR}/CMakeLists.txt" ]; then echo "Z3_SRC_DIR is invalid" exit 1 fi # Make build tree mkdir -p "${Z3_BUILD_DIR}" cd "${Z3_BUILD_DIR}" # Configure cmake \ -G "${Z3_CMAKE_GENERATOR}" \ -DCMAKE_BUILD_TYPE=${Z3_BUILD_TYPE} \ -DPYTHON_EXECUTABLE=${PYTHON_EXECUTABLE} \ -DCMAKE_INSTALL_PREFIX=${Z3_INSTALL_PREFIX} \ -DWARNINGS_AS_ERRORS=${Z3_WARNINGS_AS_ERRORS} \ "${ADDITIONAL_Z3_OPTS[@]}" \ "${Z3_SRC_DIR}" # Build source ${SCRIPT_DIR}/set_generator_args.sh cmake --build $(pwd) "${GENERATOR_ARGS[@]}" z3-z3-4.8.7/contrib/ci/scripts/ci_defaults.sh000066400000000000000000000037571356505360400210030ustar00rootroot00000000000000# This file should be sourced by other scripts # and not executed directly # Set CI build defaults export ASAN_BUILD="${ASAN_BUILD:-0}" export BUILD_DOCS="${BUILD_DOCS:-0}" export DOTNET_BINDINGS="${DOTNET_BINDINGS:-1}" export JAVA_BINDINGS="${JAVA_BINDINGS:-1}" export NO_SUPPRESS_OUTPUT="${NO_SUPPRESS_OUTPUT:-0}" export PYTHON_BINDINGS="${PYTHON_BINDINGS:-1}" export RUN_API_EXAMPLES="${RUN_API_EXAMPLES:-1}" export RUN_SYSTEM_TESTS="${RUN_SYSTEM_TESTS:-1}" export RUN_UNIT_TESTS="${RUN_UNIT_TESTS:-BUILD_AND_RUN}" # Don't print suppressions by default because that breaks the Z3 # regression tests because they don't expect them to appear in Z3's # output. export SANITIZER_PRINT_SUPPRESSIONS="${SANITIZER_PRINT_SUPPRESSIONS:-0}" export TARGET_ARCH="${TARGET_ARCH:-x86_64}" export TEST_INSTALL="${TEST_INSTALL:-1}" export UBSAN_BUILD="${UBSAN_BUILD:-0}" export Z3_USE_LIBGMP="${Z3_USE_LIBGMP:-0}" export USE_LTO="${USE_LTO:-0}" export Z3_BUILD_TYPE="${Z3_BUILD_TYPE:-RelWithDebInfo}" export Z3_CMAKE_GENERATOR="${Z3_CMAKE_GENERATOR:-Ninja}" export Z3_STATIC_BUILD="${Z3_STATIC_BUILD:-0}" # Default is blank which means get latest revision export Z3_SYSTEM_TEST_GIT_REVISION="${Z3_SYSTEM_TEST_GIT_REVISION:-}" export Z3_WARNINGS_AS_ERRORS="${Z3_WARNINGS_AS_ERRORS:-SERIOUS_ONLY}" export Z3_VERBOSE_BUILD_OUTPUT="${Z3_VERBOSE_BUILD_OUTPUT:-0}" # Platform specific defaults PLATFORM="$(uname -s)" case "${PLATFORM}" in Linux*) export C_COMPILER="${C_COMPILER:-gcc}" export CXX_COMPILER="${CXX_COMPILER:-g++}" export Z3_INSTALL_PREFIX="${Z3_INSTALL_PREFIX:-/usr}" ;; Darwin*) export C_COMPILER="${C_COMPILER:-clang}" export CXX_COMPILER="${CXX_COMPILER:-clang++}" export Z3_INSTALL_PREFIX="${Z3_INSTALL_PREFIX:-/usr/local}" ;; *) echo "Unknown platform \"${PLATFORM}\"" exit 1 ;; esac unset PLATFORM # NOTE: The following variables are not set here because # they are specific to the CI implementation # PYTHON_EXECUTABLE # ASAN_DSO # Z3_SRC_DIR # Z3_BUILD_DIR # Z3_SYSTEM_TEST_DIR z3-z3-4.8.7/contrib/ci/scripts/install_deps_osx.sh000077500000000000000000000016551356505360400220710ustar00rootroot00000000000000#!/bin/bash SCRIPT_DIR="$( cd ${BASH_SOURCE[0]%/*} ; echo $PWD )" . ${SCRIPT_DIR}/run_quiet.sh set -x set -e set -o pipefail run_quiet brew update export HOMEBREW_NO_AUTO_UPDATE=1 function brew_install_or_upgrade() { if brew ls --versions "$1" > /dev/null 2>&1 ; then brew upgrade "$1" else brew install "$1" fi } # FIXME: We should fix the versions of dependencies used # so that we have reproducible builds. # HACK: Just use CMake version in TravisCI for now if [ "X${MACOS_UPDATE_CMAKE}" = "X1" ]; then brew_install_or_upgrade cmake fi if [ "X${Z3_CMAKE_GENERATOR}" = "XNinja" ]; then brew_install_or_upgrade ninja fi if [ "X${Z3_USE_LIBGMP}" = "X1" ]; then brew_install_or_upgrade gmp fi if [ "X${BUILD_DOCS}" = "X1" ]; then brew_install_or_upgrade doxygen fi if [ "X${DOTNET_BINDINGS}" = "X1" ]; then brew_install_or_upgrade mono fi if [ "X${JAVA_BINDINGS}" = "X1" ]; then brew cask install java fi z3-z3-4.8.7/contrib/ci/scripts/run_quiet.sh000066400000000000000000000021071356505360400205200ustar00rootroot00000000000000# Simple wrapper function that runs a command suppressing # it's output. However it's output will be shown in the # case that `NO_SUPPRESS_OUTPUT` is set to `1` or the command # fails. # # The use case for this trying to avoid large logs on TravisCI function run_quiet() { if [ "X${NO_SUPPRESS_OUTPUT}" = "X1" ]; then "${@}" else OLD_SETTINGS="$-" set +x set +e TMP_DIR="${TMP_DIR:-/tmp/}" STDOUT="${TMP_DIR}/$$.stdout" STDERR="${TMP_DIR}/$$.stderr" "${@}" > "${STDOUT}" 2> "${STDERR}" EXIT_STATUS="$?" if [ "${EXIT_STATUS}" -ne 0 ]; then echo "Command \"$@\" failed" echo "EXIT CODE: ${EXIT_STATUS}" echo "STDOUT" echo "" echo "\`\`\`" cat ${STDOUT} echo "\`\`\`" echo "" echo "STDERR" echo "" echo "\`\`\`" cat ${STDERR} echo "\`\`\`" echo "" fi # Clean up rm "${STDOUT}" "${STDERR}" [ "$( echo "${OLD_SETTINGS}" | grep -c 'e')" != "0" ] && set -e [ "$( echo "${OLD_SETTINGS}" | grep -c 'x')" != "0" ] && set -x return ${EXIT_STATUS} fi } z3-z3-4.8.7/contrib/ci/scripts/sanitizer_env.sh000066400000000000000000000056751356505360400214020ustar00rootroot00000000000000# This script is intended to be included by other # scripts and should not be executed directly : ${Z3_SRC_DIR?"Z3_SRC_DIR must be specified"} : ${ASAN_BUILD?"ASAN_BUILD must be specified"} : ${UBSAN_BUILD?"UBSAN_BUILD must be specified"} if [ "X${ASAN_BUILD}" = "X1" ]; then # Use suppression files export LSAN_OPTIONS="suppressions=${Z3_SRC_DIR}/contrib/suppressions/sanitizers/lsan.txt" # NOTE: If you get bad stacktraces try using `fast_unwind_on_malloc=0` # NOTE: `malloc_context_size` controls size of recorded stacktrace for allocations. # If the reported stacktraces appear incomplete try increasing the value. export ASAN_OPTIONS="malloc_context_size=100,suppressions=${Z3_SRC_DIR}/contrib/suppressions/sanitizers/asan.txt" : ${SANITIZER_PRINT_SUPPRESSIONS?"SANITIZER_PRINT_SUPPRESSIONS must be specified"} if [ "X${SANITIZER_PRINT_SUPPRESSIONS}" = "X1" ]; then export LSAN_OPTIONS="${LSAN_OPTIONS},print_suppressions=1" export ASAN_OPTIONS="${ASAN_OPTIONS},print_suppressions=1" else export LSAN_OPTIONS="${LSAN_OPTIONS},print_suppressions=0" export ASAN_OPTIONS="${ASAN_OPTIONS},print_suppressions=0" fi : ${ASAN_SYMBOLIZER_PATH?"ASAN_SYMBOLIZER_PATH must be specified"} # Run command without checking for leaks function run_no_lsan() { ASAN_OPTIONS="${ASAN_OPTIONS},detect_leaks=0" "${@}" } # Check path to ASan DSO : ${ASAN_DSO?"ASAN_DSO must be specified"} if [ ! -e "${ASAN_DSO}" ]; then echo "ASAN_DSO (${ASAN_DSO}) does not exist" exit 1 fi # FIXME: We'll need to refactor this when we can do UBSan builds # against a UBSan DSO. function run_non_native_binding() { # We need to preload the ASan DSO that libz3 # will have undefined references to. # Don't run leak checking because we get lots reported leaks # in the language runtime (e.g. python). PLATFORM="$(uname -s)" case "${PLATFORM}" in Linux*) LD_PRELOAD="${ASAN_DSO}" run_no_lsan "${@}" ;; Darwin*) DYLD_INSERT_LIBRARIES="${ASAN_DSO}" run_no_lsan "${@}" ;; *) echo "Unknown platform \"${PLATFORM}\"" exit 1 ;; esac unset PLATFORM } else # In non-ASan build just run directly function run_no_lsan() { "${@}" } function run_non_native_binding() { "${@}" } fi if [ "X${UBSAN_BUILD}" = "X1" ]; then # `halt_on_error=1,abort_on_error=1` means that on the first UBSan error # the program will terminate by calling `abort(). Without this UBSan will # allow execution to continue. We also use a suppression file. export UBSAN_OPTIONS="halt_on_error=1,abort_on_error=1,suppressions=${Z3_SRC_DIR}/contrib/suppressions/sanitizers/ubsan.txt" : ${SANITIZER_PRINT_SUPPRESSIONS?"SANITIZER_PRINT_SUPPRESSIONS must be specified"} if [ "X${SANITIZER_PRINT_SUPPRESSIONS}" = "X1" ]; then export UBSAN_OPTIONS="${UBSAN_OPTIONS},print_suppressions=1" else export UBSAN_OPTIONS="${UBSAN_OPTIONS},print_suppressions=0" fi fi z3-z3-4.8.7/contrib/ci/scripts/set_compiler_flags.sh000066400000000000000000000020511356505360400223440ustar00rootroot00000000000000# This script should is intended to be included by other # scripts and should not be executed directly : ${TARGET_ARCH?"TARGET_ARCH must be specified"} : ${ASAN_BUILD?"ASAN_BUILD must be specified"} : ${UBSAN_BUILD?"UBSAN_BUILD must be specified"} : ${CC?"CC must be specified"} : ${CXX?"CXX must be specified"} case ${TARGET_ARCH} in x86_64) CXXFLAGS="${CXXFLAGS} -m64" CFLAGS="${CFLAGS} -m64" ;; i686) CXXFLAGS="${CXXFLAGS} -m32" CFLAGS="${CFLAGS} -m32" ;; *) echo "Unknown arch \"${TARGET_ARCH}\"" exit 1 esac if [ "X${ASAN_BUILD}" = "X1" ]; then CXXFLAGS="${CXXFLAGS} -fsanitize=address -fno-omit-frame-pointer" CFLAGS="${CFLAGS} -fsanitize=address -fno-omit-frame-pointer" fi if [ "X${UBSAN_BUILD}" = "X1" ]; then CXXFLAGS="${CXXFLAGS} -fsanitize=undefined" CFLAGS="${CFLAGS} -fsanitize=undefined" fi # Report flags echo "CXXFLAGS: ${CXXFLAGS}" echo "CFLAGS: ${CFLAGS}" # Report compiler echo "CC: ${CC}" ${CC} --version echo "CXX: ${CXX}" ${CXX} --version # Export the values export CFLAGS export CXXFLAGS z3-z3-4.8.7/contrib/ci/scripts/set_generator_args.sh000066400000000000000000000012051356505360400223600ustar00rootroot00000000000000# This script is intended to be included by other # scripts and should not be executed directly : ${Z3_CMAKE_GENERATOR?"Z3_CMAKE_GENERATOR must be specified"} : ${Z3_VERBOSE_BUILD_OUTPUT?"Z3_VERBOSE_BUILD_OUTPUT must be specified"} GENERATOR_ARGS=('--') if [ "${Z3_CMAKE_GENERATOR}" = "Unix Makefiles" ]; then GENERATOR_ARGS+=("-j$(nproc)") if [ "X${Z3_VERBOSE_BUILD_OUTPUT}" = "X1" ]; then GENERATOR_ARGS+=("VERBOSE=1") fi elif [ "${Z3_CMAKE_GENERATOR}" = "Ninja" ]; then if [ "X${Z3_VERBOSE_BUILD_OUTPUT}" = "X1" ]; then GENERATOR_ARGS+=("-v") fi else echo "Unknown CMake generator \"${Z3_CMAKE_GENERATOR}\"" exit 1 fi z3-z3-4.8.7/contrib/ci/scripts/test_z3_docs.sh000077500000000000000000000010531356505360400211120ustar00rootroot00000000000000#!/bin/bash SCRIPT_DIR="$( cd ${BASH_SOURCE[0]%/*} ; echo $PWD )" . ${SCRIPT_DIR}/run_quiet.sh set -x set -e set -o pipefail : ${Z3_BUILD_DIR?"Z3_BUILD_DIR must be specified"} : ${BUILD_DOCS?"BUILD_DOCS must be specified"} # Set CMake generator args source ${SCRIPT_DIR}/set_generator_args.sh cd "${Z3_BUILD_DIR}" # Generate documentation if [ "X${BUILD_DOCS}" = "X1" ]; then # TODO: Make quiet once we've fixed the build run_quiet cmake --build $(pwd) --target api_docs "${GENERATOR_ARGS[@]}" fi # TODO: Test or perhaps deploy the built docs? z3-z3-4.8.7/contrib/ci/scripts/test_z3_examples_cmake.sh000077500000000000000000000103601356505360400231410ustar00rootroot00000000000000#!/bin/bash # This script tests Z3 SCRIPT_DIR="$( cd ${BASH_SOURCE[0]%/*} ; echo $PWD )" . ${SCRIPT_DIR}/run_quiet.sh set -x set -e set -o pipefail : ${Z3_SRC_DIR?"Z3_SRC_DIR must be specified"} : ${Z3_BUILD_DIR?"Z3_BUILD_DIR must be specified"} : ${PYTHON_BINDINGS?"PYTHON_BINDINGS must be specified"} : ${PYTHON_EXECUTABLE?"PYTHON_EXECUTABLE must be specified"} : ${DOTNET_BINDINGS?"DOTNET_BINDINGS must be specified"} : ${JAVA_BINDINGS?"JAVA_BINDINGS must be specified"} : ${UBSAN_BUILD?"UBSAN_BUILD must be specified"} : ${RUN_API_EXAMPLES?"RUN_API_EXAMPLES must be specified"} if [ "X${RUN_API_EXAMPLES}" = "X0" ]; then echo "Skipping run of API examples" exit 0 fi # Set compiler flags source ${SCRIPT_DIR}/set_compiler_flags.sh # Set CMake generator args source ${SCRIPT_DIR}/set_generator_args.sh # Sanitizer environment variables source ${SCRIPT_DIR}/sanitizer_env.sh cd "${Z3_BUILD_DIR}" # Build and run C example cmake --build $(pwd) --target c_example "${GENERATOR_ARGS[@]}" run_quiet examples/c_example_build_dir/c_example # Build and run C++ example cmake --build $(pwd) --target cpp_example "${GENERATOR_ARGS[@]}" run_quiet examples/cpp_example_build_dir/cpp_example # Build and run tptp5 example cmake --build $(pwd) --target z3_tptp5 "${GENERATOR_ARGS[@]}" # FIXME: Do something more useful with example run_quiet examples/tptp_build_dir/z3_tptp5 -help # Build an run c_maxsat_example cmake --build $(pwd) --target c_maxsat_example "${GENERATOR_ARGS[@]}" # FIXME: It is known that the maxsat example leaks memory and the # the Z3 developers have stated this is "wontfix". # See https://github.com/Z3Prover/z3/issues/1299 run_no_lsan \ run_quiet \ examples/c_maxsat_example_build_dir/c_maxsat_example \ ${Z3_SRC_DIR}/examples/maxsat/ex.smt if [ "X${UBSAN_BUILD}" = "X1" ]; then # FIXME: We really need libz3 to link against a shared UBSan runtime. # Right now we link against the static runtime which breaks all the # non-native language bindings. echo "FIXME: Can't run other examples when building with UBSan" exit 0 fi if [ "X${PYTHON_BINDINGS}" = "X1" ]; then # Run python examples # `all_interval_series.py` produces a lot of output so just throw # away output. # TODO: This example is slow should we remove it from testing? if [ "X${ASAN_BUILD}" = "X1" -a "X${Z3_BUILD_TYPE}" = "XDebug" ]; then # Too slow when doing ASan Debug build echo "Skipping all_interval_series.py under ASan Debug build" else run_quiet run_non_native_binding ${PYTHON_EXECUTABLE} python/all_interval_series.py fi run_quiet run_non_native_binding ${PYTHON_EXECUTABLE} python/complex.py run_quiet run_non_native_binding ${PYTHON_EXECUTABLE} python/example.py # FIXME: `hamiltonian.py` example is disabled because its too slow. #${PYTHON_EXECUTABLE} python/hamiltonian.py run_quiet run_non_native_binding ${PYTHON_EXECUTABLE} python/marco.py run_quiet run_non_native_binding ${PYTHON_EXECUTABLE} python/mss.py run_quiet run_non_native_binding ${PYTHON_EXECUTABLE} python/socrates.py run_quiet run_non_native_binding ${PYTHON_EXECUTABLE} python/visitor.py run_quiet run_non_native_binding ${PYTHON_EXECUTABLE} python/z3test.py fi if [ "X${DOTNET_BINDINGS}" = "X1" ]; then # Run .NET example 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 # Build Java example # FIXME: Move compilation step into CMake target mkdir -p examples/java cp ${Z3_SRC_DIR}/examples/java/JavaExample.java examples/java/ javac examples/java/JavaExample.java -classpath com.microsoft.z3.jar # Run Java example if [ "$(uname)" = "Darwin" ]; then # macOS export DYLD_LIBRARY_PATH=$(pwd):${DYLD_LIBRARY_PATH} else # Assume Linux for now export LD_LIBRARY_PATH=$(pwd):${LD_LIBRARY_PATH} fi if [ "X${ASAN_BUILD}" = "X1" ]; then # The JVM seems to crash (SEGV) if we pre-load ASan # so don't run it for now. echo "Skipping JavaExample under ASan build" else run_quiet \ run_non_native_binding \ java -cp .:examples/java:com.microsoft.z3.jar JavaExample fi fi z3-z3-4.8.7/contrib/ci/scripts/test_z3_install_cmake.sh000077500000000000000000000007501356505360400227730ustar00rootroot00000000000000#!/bin/bash SCRIPT_DIR="$( cd ${BASH_SOURCE[0]%/*} ; echo $PWD )" set -x set -e set -o pipefail : ${TEST_INSTALL?"TEST_INSTALL must be specified"} : ${Z3_BUILD_DIR?"Z3_BUILD_DIR must be specified"} if [ "X${TEST_INSTALL}" != "X1" ]; then echo "Skipping install" exit 0 fi # Set CMake generator args source ${SCRIPT_DIR}/set_generator_args.sh cd "${Z3_BUILD_DIR}" sudo cmake --build $(pwd) --target install "${GENERATOR_ARGS[@]}" # TODO: Test the installed version in some way z3-z3-4.8.7/contrib/ci/scripts/test_z3_system_tests.sh000077500000000000000000000045431356505360400227370ustar00rootroot00000000000000#!/bin/bash set -x set -e set -o pipefail : ${Z3_BUILD_DIR?"Z3_BUILD_DIR must be specified"} : ${Z3_BUILD_TYPE?"Z3_BUILD_TYPE must be specified"} : ${RUN_SYSTEM_TESTS?"RUN_SYSTEM_TESTS must be speicifed"} : ${PYTHON_BINDINGS?"PYTHON_BINDINGS must be specified"} : ${PYTHON_EXECUTABLE?"PYTHON_EXECUTABLE must be specified"} : ${Z3_SYSTEM_TEST_DIR?"Z3_SYSTEM_TEST_DIR must be specified"} : ${UBSAN_BUILD?"UBSAN_BUILD must be specified"} if [ "X${RUN_SYSTEM_TESTS}" != "X1" ]; then echo "Skipping system tests" exit 0 fi # Sanitizer environment variables SCRIPT_DIR="$( cd ${BASH_SOURCE[0]%/*} ; echo $PWD )" source ${SCRIPT_DIR}/sanitizer_env.sh Z3_EXE="${Z3_BUILD_DIR}/z3" Z3_LIB_DIR="${Z3_BUILD_DIR}" # Set value if not already defined externally Z3_SYSTEM_TEST_GIT_URL="${Z3_GIT_URL:-https://github.com/Z3Prover/z3test.git}" # Clone repo to destination mkdir -p "${Z3_SYSTEM_TEST_DIR}" git clone "${Z3_SYSTEM_TEST_GIT_URL}" "${Z3_SYSTEM_TEST_DIR}" cd "${Z3_SYSTEM_TEST_DIR}" if [ -n "${Z3_SYSTEM_TEST_GIT_REVISION}" ]; then # If a particular revision is requested then check it out. # This is useful for reproducible builds git checkout "${Z3_SYSTEM_TEST_GIT_REVISION}" fi ############################################################################### # Run system tests ############################################################################### # SMTLIBv2 tests ${PYTHON_EXECUTABLE} scripts/test_benchmarks.py "${Z3_EXE}" regressions/smt2 ${PYTHON_EXECUTABLE} scripts/test_benchmarks.py "${Z3_EXE}" regressions/smt2-extra if [ "X${Z3_BUILD_TYPE}" = "XDebug" ]; then ${PYTHON_EXECUTABLE} scripts/test_benchmarks.py "${Z3_EXE}" regressions/smt2-debug fi if [ "X${PYTHON_BINDINGS}" = "X1" ]; then # Run python binding tests if [ "X${UBSAN_BUILD}" = "X1" ]; then # FIXME: We need to build libz3 with a shared UBSan runtime for the bindings # to work. echo "FIXME: Skipping python binding tests when building with UBSan" elif [ "X${ASAN_BUILD}" = "X1" ]; then # FIXME: The `test_pyscripts.py` doesn't propagate LD_PRELOAD # so under ASan the tests fail to run # to work. echo "FIXME: Skipping python binding tests when building with ASan" else run_non_native_binding ${PYTHON_EXECUTABLE} scripts/test_pyscripts.py "${Z3_LIB_DIR}" regressions/python/ fi fi # FIXME: Run `scripts/test_cs.py` once it has been modified to support mono z3-z3-4.8.7/contrib/ci/scripts/test_z3_unit_tests_cmake.sh000077500000000000000000000017041356505360400235260ustar00rootroot00000000000000#!/bin/bash SCRIPT_DIR="$( cd ${BASH_SOURCE[0]%/*} ; echo $PWD )" . ${SCRIPT_DIR}/run_quiet.sh set -x set -e set -o pipefail : ${Z3_BUILD_DIR?"Z3_BUILD_DIR must be specified"} : ${RUN_UNIT_TESTS?"RUN_UNIT_TESTS must be specified"} # Set CMake generator args source ${SCRIPT_DIR}/set_generator_args.sh # Sanitizer environment variables source ${SCRIPT_DIR}/sanitizer_env.sh cd "${Z3_BUILD_DIR}" function build_unit_tests() { # Build internal tests cmake --build $(pwd) --target test-z3 "${GENERATOR_ARGS[@]}" } function run_unit_tests() { # Run all tests that don't require arguments run_quiet ./test-z3 /a } case "${RUN_UNIT_TESTS}" in BUILD_AND_RUN) build_unit_tests run_unit_tests ;; BUILD_ONLY) build_unit_tests ;; SKIP) echo "RUN_UNIT_TESTS set to \"${RUN_UNIT_TESTS}\" so skipping build and run" exit 0 ;; *) echo "Error: RUN_UNIT_TESTS set to unhandled value \"${RUN_UNIT_TESTS}\"" exit 1 ;; esac z3-z3-4.8.7/contrib/ci/scripts/travis_ci_entry_point.sh000077500000000000000000000006101356505360400231220ustar00rootroot00000000000000#!/bin/bash SCRIPT_DIR="$( cd ${BASH_SOURCE[0]%/*} ; echo $PWD )" set -x set -e set -o pipefail : ${TRAVIS_OS_NAME?"TRAVIS_OS_NAME should be set"} if [ "${TRAVIS_OS_NAME}" = "osx" ]; then ${SCRIPT_DIR}/travis_ci_osx_entry_point.sh elif [ "${TRAVIS_OS_NAME}" = "linux" ]; then ${SCRIPT_DIR}/travis_ci_linux_entry_point.sh else echo "Unsupported OS \"${TRAVIS_OS_NAME}\"" exit 1 fi z3-z3-4.8.7/contrib/ci/scripts/travis_ci_linux_entry_point.sh000077500000000000000000000146031356505360400243500ustar00rootroot00000000000000#!/bin/bash SCRIPT_DIR="$( cd ${BASH_SOURCE[0]%/*} ; echo $PWD )" set -x set -e set -o pipefail DOCKER_FILE_DIR="$(cd ${SCRIPT_DIR}/../Dockerfiles; echo $PWD)" : ${LINUX_BASE?"LINUX_BASE must be specified"} # Sanity check. Current working directory should be repo root if [ ! -f "./README.md" ]; then echo "Current working directory should be repo root" exit 1 fi # Get defaults source "${SCRIPT_DIR}/ci_defaults.sh" BUILD_OPTS=() # Pass Docker build arguments if [ -n "${Z3_BUILD_TYPE}" ]; then BUILD_OPTS+=("--build-arg" "Z3_BUILD_TYPE=${Z3_BUILD_TYPE}") fi if [ -n "${Z3_CMAKE_GENERATOR}" ]; then BUILD_OPTS+=("--build-arg" "Z3_CMAKE_GENERATOR=${Z3_CMAKE_GENERATOR}") fi if [ -n "${Z3_USE_LIBGMP}" ]; then BUILD_OPTS+=("--build-arg" "Z3_USE_LIBGMP=${Z3_USE_LIBGMP}") fi if [ -n "${BUILD_DOCS}" ]; then BUILD_OPTS+=("--build-arg" "BUILD_DOCS=${BUILD_DOCS}") fi if [ -n "${PYTHON_EXECUTABLE}" ]; then BUILD_OPTS+=("--build-arg" "PYTHON_EXECUTABLE=${PYTHON_EXECUTABLE}") fi if [ -n "${PYTHON_BINDINGS}" ]; then BUILD_OPTS+=("--build-arg" "PYTHON_BINDINGS=${PYTHON_BINDINGS}") fi if [ -n "${DOTNET_BINDINGS}" ]; then BUILD_OPTS+=("--build-arg" "DOTNET_BINDINGS=${DOTNET_BINDINGS}") fi if [ -n "${JAVA_BINDINGS}" ]; then BUILD_OPTS+=("--build-arg" "JAVA_BINDINGS=${JAVA_BINDINGS}") fi if [ -n "${USE_LTO}" ]; then BUILD_OPTS+=("--build-arg" "USE_LTO=${USE_LTO}") fi if [ -n "${Z3_INSTALL_PREFIX}" ]; then BUILD_OPTS+=("--build-arg" "Z3_INSTALL_PREFIX=${Z3_INSTALL_PREFIX}") fi # TravisCI reserves CC for itself so use a different name if [ -n "${C_COMPILER}" ]; then BUILD_OPTS+=("--build-arg" "CC=${C_COMPILER}") fi # TravisCI reserves CXX for itself so use a different name if [ -n "${CXX_COMPILER}" ]; then BUILD_OPTS+=("--build-arg" "CXX=${CXX_COMPILER}") fi if [ -n "${TARGET_ARCH}" ]; then BUILD_OPTS+=("--build-arg" "TARGET_ARCH=${TARGET_ARCH}") fi if [ -n "${ASAN_BUILD}" ]; then BUILD_OPTS+=("--build-arg" "ASAN_BUILD=${ASAN_BUILD}") fi if [ -n "${ASAN_DSO}" ]; then BUILD_OPTS+=("--build-arg" "ASAN_DSO=${ASAN_DSO}") fi if [ -n "${SANITIZER_PRINT_SUPPRESSIONS}" ]; then BUILD_OPTS+=("--build-arg" "SANITIZER_PRINT_SUPPRESSIONS=${SANITIZER_PRINT_SUPPRESSIONS}") fi if [ -n "${UBSAN_BUILD}" ]; then BUILD_OPTS+=("--build-arg" "UBSAN_BUILD=${UBSAN_BUILD}") fi if [ -n "${TEST_INSTALL}" ]; then BUILD_OPTS+=("--build-arg" "TEST_INSTALL=${TEST_INSTALL}") fi if [ -n "${RUN_API_EXAMPLES}" ]; then BUILD_OPTS+=("--build-arg" "RUN_API_EXAMPLES=${RUN_API_EXAMPLES}") fi if [ -n "${RUN_SYSTEM_TESTS}" ]; then BUILD_OPTS+=("--build-arg" "RUN_SYSTEM_TESTS=${RUN_SYSTEM_TESTS}") fi if [ -n "${Z3_SYSTEM_TEST_GIT_REVISION}" ]; then BUILD_OPTS+=( \ "--build-arg" \ "Z3_SYSTEM_TEST_GIT_REVISION=${Z3_SYSTEM_TEST_GIT_REVISION}" \ ) fi if [ -n "${RUN_UNIT_TESTS}" ]; then BUILD_OPTS+=("--build-arg" "RUN_UNIT_TESTS=${RUN_UNIT_TESTS}") fi if [ -n "${Z3_VERBOSE_BUILD_OUTPUT}" ]; then BUILD_OPTS+=( \ "--build-arg" \ "Z3_VERBOSE_BUILD_OUTPUT=${Z3_VERBOSE_BUILD_OUTPUT}" \ ) fi if [ -n "${Z3_STATIC_BUILD}" ]; then BUILD_OPTS+=("--build-arg" "Z3_STATIC_BUILD=${Z3_STATIC_BUILD}") fi if [ -n "${NO_SUPPRESS_OUTPUT}" ]; then BUILD_OPTS+=( \ "--build-arg" \ "NO_SUPPRESS_OUTPUT=${NO_SUPPRESS_OUTPUT}" \ ) fi if [ -n "${Z3_WARNINGS_AS_ERRORS}" ]; then BUILD_OPTS+=( \ "--build-arg" \ "Z3_WARNINGS_AS_ERRORS=${Z3_WARNINGS_AS_ERRORS}" \ ) fi case ${LINUX_BASE} in ubuntu_14.04) BASE_DOCKER_FILE="${DOCKER_FILE_DIR}/z3_base_ubuntu_14.04.Dockerfile" BASE_DOCKER_IMAGE_NAME="z3_base_ubuntu:14.04" ;; ubuntu_16.04) BASE_DOCKER_FILE="${DOCKER_FILE_DIR}/z3_base_ubuntu_16.04.Dockerfile" BASE_DOCKER_IMAGE_NAME="z3_base_ubuntu:16.04" ;; ubuntu32_16.04) BASE_DOCKER_FILE="${DOCKER_FILE_DIR}/z3_base_ubuntu32_16.04.Dockerfile" BASE_DOCKER_IMAGE_NAME="z3_base_ubuntu32:16.04" ;; *) echo "Unknown Linux base ${LINUX_BASE}" exit 1 ;; esac # Initially assume that we need to build the base Docker image MUST_BUILD=1 # Travis CI persistent cache. # # This inspired by http://rundef.com/fast-travis-ci-docker-build . # The idea is to cache the built image for subsequent builds to # reduce build time. if [ -n "${DOCKER_TRAVIS_CI_CACHE_DIR}" ]; then CHECKSUM_FILE="${DOCKER_TRAVIS_CI_CACHE_DIR}/${BASE_DOCKER_IMAGE_NAME}.chksum" CACHED_DOCKER_IMAGE="${DOCKER_TRAVIS_CI_CACHE_DIR}/${BASE_DOCKER_IMAGE_NAME}.gz" if [ -f "${CACHED_DOCKER_IMAGE}" ]; then # There's a cached image to use. Check the checksums of the Dockerfile # match. If they don't that implies we need to build a fresh image. if [ -f "${CHECKSUM_FILE}" ]; then CURRENT_DOCKERFILE_CHECKSUM=$(sha256sum "${BASE_DOCKER_FILE}" | awk '{ print $1 }') CACHED_DOCKERFILE_CHECKSUM=$(cat "${CHECKSUM_FILE}") if [ "X${CURRENT_DOCKERFILE_CHECKSUM}" = "X${CACHED_DOCKERFILE_CHECKSUM}" ]; then # Load the cached image MUST_BUILD=0 gunzip --stdout "${CACHED_DOCKER_IMAGE}" | docker load fi fi fi fi if [ "${MUST_BUILD}" -eq 1 ]; then # The base image contains all the dependencies we want to build # Z3. docker build -t "${BASE_DOCKER_IMAGE_NAME}" - < "${BASE_DOCKER_FILE}" if [ -n "${DOCKER_TRAVIS_CI_CACHE_DIR}" ]; then # Write image and checksum to cache docker save "${BASE_DOCKER_IMAGE_NAME}" | \ gzip > "${CACHED_DOCKER_IMAGE}" sha256sum "${BASE_DOCKER_FILE}" | awk '{ print $1 }' > \ "${CHECKSUM_FILE}" fi fi DOCKER_MAJOR_VERSION=$(docker info --format '{{.ServerVersion}}' | sed 's/^\([0-9]\+\)\.\([0-9]\+\).*$/\1/') DOCKER_MINOR_VERSION=$(docker info --format '{{.ServerVersion}}' | sed 's/^\([0-9]\+\)\.\([0-9]\+\).*$/\2/') DOCKER_BUILD_FILE="${DOCKER_FILE_DIR}/z3_build.Dockerfile" if [ "${DOCKER_MAJOR_VERSION}${DOCKER_MINOR_VERSION}" -lt 1705 ]; then # Workaround limitation in older Docker versions where the FROM # command cannot be parameterized with an ARG. sed \ -e '/^ARG DOCKER_IMAGE_BASE/d' \ -e 's/${DOCKER_IMAGE_BASE}/'"${BASE_DOCKER_IMAGE_NAME}/" \ "${DOCKER_BUILD_FILE}" > "${DOCKER_BUILD_FILE}.patched" DOCKER_BUILD_FILE="${DOCKER_BUILD_FILE}.patched" else # This feature landed in Docker 17.05 # See https://github.com/moby/moby/pull/31352 BUILD_OPTS+=( \ "--build-arg" \ "DOCKER_IMAGE_BASE=${BASE_DOCKER_IMAGE_NAME}" \ ) fi # Now build Z3 and test it using the created base image docker build \ -f "${DOCKER_BUILD_FILE}" \ "${BUILD_OPTS[@]}" \ . z3-z3-4.8.7/contrib/ci/scripts/travis_ci_osx_entry_point.sh000077500000000000000000000024151356505360400240200ustar00rootroot00000000000000#!/bin/bash SCRIPT_DIR="$( cd ${BASH_SOURCE[0]%/*} ; echo $PWD )" set -x set -e set -o pipefail # Get defaults source "${SCRIPT_DIR}/ci_defaults.sh" if [ -z "${TRAVIS_BUILD_DIR}" ]; then echo "TRAVIS_BUILD_DIR must be set to root of Z3 repository" exit 1 fi if [ ! -d "${TRAVIS_BUILD_DIR}" ]; then echo "TRAVIS_BUILD_DIR must be a directory" exit 1 fi # These variables are specific to the macOS TravisCI # implementation and are not set in `ci_defaults.sh`. export PYTHON_EXECUTABLE="${PYTHON_EXECUTABLE:-$(which python)}" export Z3_SRC_DIR="${TRAVIS_BUILD_DIR}" export Z3_BUILD_DIR="${Z3_SRC_DIR}/build" export Z3_SYSTEM_TEST_DIR="${Z3_SRC_DIR}/z3_system_test" # Overwrite whatever what set in TravisCI export CC="${C_COMPILER}" export CXX="${CXX_COMPILER}" if [ "X${MACOS_SKIP_DEPS_UPDATE}" = "X1" ]; then # This is just for local testing to avoid updating echo "Skipping dependency update" else "${SCRIPT_DIR}/install_deps_osx.sh" fi # Build Z3 "${SCRIPT_DIR}/build_z3_cmake.sh" # Test building docs "${SCRIPT_DIR}/test_z3_docs.sh" # Test examples "${SCRIPT_DIR}/test_z3_examples_cmake.sh" # Run unit tests "${SCRIPT_DIR}/test_z3_unit_tests_cmake.sh" # Run system tests "${SCRIPT_DIR}/test_z3_system_tests.sh" # Test install "${SCRIPT_DIR}/test_z3_install_cmake.sh" z3-z3-4.8.7/contrib/cmake/000077500000000000000000000000001356505360400151475ustar00rootroot00000000000000z3-z3-4.8.7/contrib/cmake/bootstrap.py000077500000000000000000000025021356505360400175400ustar00rootroot00000000000000#!/usr/bin/env python """ This script is an artifact of compromise that was made when the CMake build system was first introduced (see #461). This script now does nothing. It remains only to not break out-of-tree scripts that build Z3 using CMake. Eventually this script will be removed. """ import argparse import logging import os import pprint import shutil import sys def main(args): logging.basicConfig(level=logging.INFO) parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('mode', choices=['create', 'remove'], help='The mode to use') parser.add_argument("-l","--log-level", type=str, default="info", dest="log_level", choices=['debug','info','warning','error'] ) parser.add_argument("-H", "--hard-link", action='store_true', default=False, dest='hard_link', help='When using the create mode create hard links instead of copies' ) pargs = parser.parse_args(args) logLevel = getattr(logging, pargs.log_level.upper(),None) logging.basicConfig(level=logLevel) logging.warning('Use of this script is deprecated. The script will be removed in the future') logging.warning('Action "{}" ignored'.format(pargs.mode)) if pargs.hard_link: logging.warning('Hard link option ignored') return 0 if __name__ == '__main__': sys.exit(main(sys.argv[1:])) z3-z3-4.8.7/contrib/cmake/maintainers.txt000066400000000000000000000000471356505360400202230ustar00rootroot00000000000000# Maintainers - Dan Liew (@delcypher) z3-z3-4.8.7/contrib/cmake/src/000077500000000000000000000000001356505360400157365ustar00rootroot00000000000000z3-z3-4.8.7/contrib/cmake/src/test/000077500000000000000000000000001356505360400167155ustar00rootroot00000000000000z3-z3-4.8.7/contrib/cmake/src/test/lp/000077500000000000000000000000001356505360400173305ustar00rootroot00000000000000z3-z3-4.8.7/contrib/cmake/src/test/lp/CMakeLists.txt000066400000000000000000000007601356505360400220730ustar00rootroot00000000000000add_executable(lp_tst lp_main.cpp lp.cpp $ $ $ $ ) target_compile_definitions(lp_tst PRIVATE ${Z3_COMPONENT_CXX_DEFINES}) target_compile_options(lp_tst PRIVATE ${Z3_COMPONENT_CXX_FLAGS}) target_include_directories(lp_tst PRIVATE ${Z3_COMPONENT_EXTRA_INCLUDE_DIRS}) target_link_libraries(lp_tst PRIVATE ${Z3_DEPENDENT_LIBS}) z3_append_linker_flag_list_to_target(lp_tst ${Z3_DEPENDENT_EXTRA_CXX_LINK_FLAGS}) z3-z3-4.8.7/contrib/qprofdiff/000077500000000000000000000000001356505360400160475ustar00rootroot00000000000000z3-z3-4.8.7/contrib/qprofdiff/Makefile000066400000000000000000000001471356505360400175110ustar00rootroot00000000000000qprofdiff: main.cpp $(CXX) $(CXXFLAGS) main.cpp -o qprofdiff all: qprofdiff clean: rm -f qprofdiff z3-z3-4.8.7/contrib/qprofdiff/main.cpp000066400000000000000000000210401356505360400174740ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: main.cpp Abstract: Main file for qprofdiff. Author: Christoph M. Wintersteiger (cwinter) Revision History: --*/ #include #include #include #include #include #include #include #include #include using namespace std; set options; // Profile format: // [quantifier_instances] qname : num_instances : max_generation : max_cost_s const string prefix = "[quantifier_instances]"; unsigned prefix_len = prefix.length(); typedef struct { unsigned num_instances, max_generation, max_cost; } map_entry; string trim(string str) { size_t linx = str.find_first_not_of(' '); size_t rinx = str.find_last_not_of(' '); return str.substr(linx, rinx-linx+1); } int parse(string const & filename, map & data) { ifstream fs(filename.c_str()); if (!fs.is_open()) { cout << "Can't open file '" << filename << "'" << endl; return ENOENT; } string qid; string tokens[4]; unsigned cur_token = 0; while (!fs.eof()) { string line; getline(fs, line); if (line.substr(0, prefix_len) == prefix) { line = trim(line.substr(prefix_len)); size_t from = 0, ti = 0; for (size_t inx = line.find(':', from); inx != string::npos; inx = line.find(':', from)) { tokens[ti] = trim(line.substr(from, inx-from)); from = inx+1; ti++; } if (from != line.length() && ti < 4) tokens[ti] = trim(line.substr(from)); qid = tokens[0]; if (data.find(qid) == data.end()) { map_entry & entry = data[qid]; 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 = max(entry.max_generation, (unsigned)atoi(tokens[2].c_str())); entry.max_cost = max(entry.max_cost, (unsigned)atoi(tokens[3].c_str())); } } fs.close(); return 0; } void display_data(map & data) { for (map::iterator it = data.begin(); it != data.end(); it++) cout << it->first << ": " << it->second.num_instances << ", " << it->second.max_generation << ", " << it->second.max_cost << endl; } typedef struct { int d_num_instances, d_max_generation, d_max_cost; bool left_only, right_only; } diff_entry; typedef struct { string qid; diff_entry e; } diff_item; #define DIFF_LT(X) bool diff_item_lt_ ## X (diff_item const & l, diff_item const & r) { \ int l_lt_r = l.e.d_ ## X < r.e.d_ ## X; \ int l_eq_r = l.e.d_ ## X == r.e.d_ ## X; \ return \ l.e.left_only ? (r.e.left_only ? ((l_eq_r) ? l.qid < r.qid : l_lt_r) : false) : \ l.e.right_only ? (r.e.right_only ? ((l_eq_r) ? l.qid < r.qid : l_lt_r) : true) : \ r.e.right_only ? false : \ r.e.left_only ? true : \ l_lt_r; \ } DIFF_LT(num_instances) DIFF_LT(max_generation) DIFF_LT(max_cost) int indicate(diff_entry const & e, bool suppress_unchanged) { if (e.left_only) { cout << "< "; return INT_MIN; } else if (e.right_only) { cout << "> "; return INT_MAX; } else { int const & delta = (options.find("-si") != options.end()) ? e.d_num_instances : (options.find("-sg") != options.end()) ? e.d_max_generation : (options.find("-sc") != options.end()) ? e.d_max_cost : e.d_num_instances; if (delta < 0) cout << "+ "; else if (delta > 0) cout << "- "; else if (delta == 0 && !suppress_unchanged) cout << "= "; return delta; } } void diff(map & left, map & right) { map diff_data; for (map::const_iterator lit = left.begin(); lit != left.end(); lit++) { string const & qid = lit->first; map_entry const & lentry = lit->second; map::const_iterator rit = right.find(qid); if (rit != right.end()) { map_entry const & rentry = rit->second; diff_entry & de = diff_data[qid]; de.left_only = de.right_only = false; de.d_num_instances = lentry.num_instances - rentry.num_instances; de.d_max_generation = lentry.max_generation - rentry.max_generation; de.d_max_cost = lentry.max_cost - rentry.max_cost; } else { diff_entry & de = diff_data[qid]; de.left_only = true; de.right_only = false; de.d_num_instances = lentry.num_instances; de.d_max_generation = lentry.max_generation; de.d_max_cost = lentry.max_cost; } } for (map::const_iterator rit = right.begin(); rit != right.end(); rit++) { string const & qid = rit->first; map_entry const & rentry = rit->second; map::const_iterator lit = left.find(qid); if (lit == left.end()) { diff_entry & de = diff_data[qid]; de.left_only = false; de.right_only = true; de.d_num_instances = -(int)rentry.num_instances; de.d_max_generation = -(int)rentry.max_generation; de.d_max_cost = -(int)rentry.max_cost; } } vector flat_data; for (map::const_iterator it = diff_data.begin(); it != diff_data.end(); it++) { flat_data.push_back(diff_item()); flat_data.back().qid = it->first; flat_data.back().e = it->second; } stable_sort(flat_data.begin(), flat_data.end(), 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); bool suppress_unchanged = options.find("-n") != options.end(); for (vector::const_iterator it = flat_data.begin(); it != flat_data.end(); it++) { diff_item const & d = *it; string const & qid = d.qid; diff_entry const & e = d.e; int delta = indicate(e, suppress_unchanged); if (!(delta == 0 && suppress_unchanged)) cout << qid << " (" << (e.d_num_instances > 0 ? "" : "+") << -e.d_num_instances << " inst., " << (e.d_max_generation > 0 ? "" : "+") << -e.d_max_generation << " max. gen., " << (e.d_max_cost > 0 ? "" : "+") << -e.d_max_cost << " max. cost)" << endl; } } void display_usage() { cout << "Usage: qprofdiff [options] " << endl; cout << "Options:" << endl; cout << " -n Suppress unchanged items" << endl; cout << " -si Sort by difference in number of instances" << endl; cout << " -sg Sort by difference in max. generation" << endl; cout << " -sc Sort by difference in max. cost" << endl; } int main(int argc, char ** argv) { char * filename1 = 0; char * filename2 = 0; for (int i = 1; i < argc; i++) { int len = string(argv[i]).length(); if (len > 1 && argv[i][0] == '-') { options.insert(string(argv[i])); } else if (filename1 == 0) filename1 = argv[i]; else if (filename2 == 0) filename2 = argv[i]; else { cout << "Invalid argument: " << argv[i] << endl << endl; display_usage(); return EINVAL; } } if (filename1 == 0 || filename2 == 0) { cout << "Two filenames required." << endl << endl; display_usage(); return EINVAL; } cout << "Comparing " << filename1 << " to " << filename2 << endl; map data1, data2; int r = parse(filename1, data1); if (r != 0) return r; r = parse(filename2, data2); if (r != 0) return r; // display_data(data1); // display_data(data2); diff(data1, data2); return 0; } z3-z3-4.8.7/contrib/qprofdiff/maintainers.txt000066400000000000000000000001241356505360400211170ustar00rootroot00000000000000# Maintainers - Christoph M. Wintersteiger (@wintersteiger, cwinter@microsoft.com) z3-z3-4.8.7/contrib/qprofdiff/qprofdiff.vcxproj000066400000000000000000000151761356505360400214560ustar00rootroot00000000000000 Debug Win32 Release Win32 Debug x64 Release x64 15.0 {96E7E3EF-4162-474D-BD32-C702632AAF2B} qprofdiff 8.1 Application true v141 NotSet Application false v141 true MultiByte Application true v141 MultiByte Application false v141 true MultiByte $(IncludePath) $(LibraryPath) $(IncludePath) $(LibraryPath) Level3 Disabled true MultiThreadedDebugDLL ..\..\src\util;%(AdditionalIncludeDirectories) ProgramDatabase $(LibraryPath);%(AdditionalLibraryDirectories) Level3 Disabled true ..\..\src\util;%(AdditionalIncludeDirectories) Level3 MaxSpeed true true true ..\..\src\util;%(AdditionalIncludeDirectories) true true Level3 MaxSpeed true true true ..\..\src\util;%(AdditionalIncludeDirectories) true true z3-z3-4.8.7/contrib/qprofdiff/qprofdiff.vcxproj.filters000066400000000000000000000016471356505360400231230ustar00rootroot00000000000000 {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;hm;inl;inc;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms Source Files z3-z3-4.8.7/contrib/suppressions/000077500000000000000000000000001356505360400166445ustar00rootroot00000000000000z3-z3-4.8.7/contrib/suppressions/README.md000066400000000000000000000002741356505360400201260ustar00rootroot00000000000000# Suppression files This directory contains suppression files used by various program analysis tools. Suppression files tell a program analysis tool to suppress various warnings/errors. z3-z3-4.8.7/contrib/suppressions/maintainers.txt000066400000000000000000000000471356505360400217200ustar00rootroot00000000000000# Maintainers - Dan Liew (@delcypher) z3-z3-4.8.7/contrib/suppressions/sanitizers/000077500000000000000000000000001356505360400210375ustar00rootroot00000000000000z3-z3-4.8.7/contrib/suppressions/sanitizers/README.md000066400000000000000000000001571356505360400223210ustar00rootroot00000000000000# Sanitizer suppression files This directory contains files used to suppress ASan/LSan/UBSan warnings/errors. z3-z3-4.8.7/contrib/suppressions/sanitizers/asan.txt000066400000000000000000000000441356505360400225200ustar00rootroot00000000000000# AddressSanitizer suppression file z3-z3-4.8.7/contrib/suppressions/sanitizers/lsan.txt000066400000000000000000000002051356505360400225320ustar00rootroot00000000000000# LeakSanitizer suppression file # Ignore Clang OpenMP leaks. # See https://github.com/Z3Prover/z3/issues/1308 leak:___kmp_allocate z3-z3-4.8.7/contrib/suppressions/sanitizers/ubsan.txt000066400000000000000000000003621356505360400227110ustar00rootroot00000000000000# UndefinedBehavior sanitizer suppression file # FIXME: UBSan doesn't usually have false positives so we need to fix all of these! # Occurs when running tptp example # See https://github.com/Z3Prover/z3/issues/964 null:rational.h null:mpq.h z3-z3-4.8.7/doc/000077500000000000000000000000001356505360400131745ustar00rootroot00000000000000z3-z3-4.8.7/doc/CMakeLists.txt000066400000000000000000000057371356505360400157500ustar00rootroot00000000000000find_package(Doxygen REQUIRED) message(STATUS "DOXYGEN_EXECUTABLE: \"${DOXYGEN_EXECUTABLE}\"") message(STATUS "DOXYGEN_VERSION: \"${DOXYGEN_VERSION}\"") set(DOC_DEST_DIR "${CMAKE_CURRENT_BINARY_DIR}/api") set(DOC_TEMP_DIR "${CMAKE_CURRENT_BINARY_DIR}/temp") set(MK_API_DOC_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/mk_api_doc.py") set(PYTHON_API_OPTIONS "") set(DOTNET_API_OPTIONS "") set(JAVA_API_OPTIONS "") SET(DOC_EXTRA_DEPENDS "") if (BUILD_PYTHON_BINDINGS) # FIXME: Don't hard code this path list(APPEND PYTHON_API_OPTIONS "--z3py-package-path" "${PROJECT_BINARY_DIR}/python/z3") list(APPEND DOC_EXTRA_DEPENDS "build_z3_python_bindings") else() list(APPEND PYTHON_API_OPTIONS "--no-z3py") endif() if (BUILD_DOTNET_BINDINGS) # FIXME: Don't hard code these paths list(APPEND DOTNET_API_OPTIONS "--dotnet-search-paths" "${PROJECT_SOURCE_DIR}/src/api/dotnet" "${PROJECT_BINARY_DIR}/src/api/dotnet" ) list(APPEND DOC_EXTRA_DEPENDS "build_z3_dotnet_bindings") else() list(APPEND DOTNET_API_OPTIONS "--no-dotnet") endif() if (BUILD_JAVA_BINDINGS) # FIXME: Don't hard code these paths list(APPEND JAVA_API_OPTIONS "--java-search-paths" "${PROJECT_SOURCE_DIR}/src/api/java" "${PROJECT_BINARY_DIR}/src/api/java" ) list(APPEND DOC_EXTRA_DEPENDS "build_z3_java_bindings") else() list(APPEND JAVA_API_OPTIONS "--no-java") endif() option(Z3_ALWAYS_BUILD_DOCS "Always build documentation for API bindings" ON) if (Z3_ALWAYS_BUILD_DOCS) set(ALWAYS_BUILD_DOCS_ARG "ALL") else() set(ALWAYS_BUILD_DOCS_ARG "") # FIXME: This sucks but there doesn't seem to be a way to make the top level # install target depend on the `api_docs` target. message(WARNING "Building documentation for API bindings is not part of the" " all target. This may result in stale files being installed when running" " the install target. You should run the api_docs target before running" " the install target. Alternatively Set Z3_ALWAYS_BUILD_DOCS to ON to" " automatically build documentation when running the install target." ) endif() add_custom_target(api_docs ${ALWAYS_BUILD_DOCS_ARG} COMMAND "${PYTHON_EXECUTABLE}" "${MK_API_DOC_SCRIPT}" --build "${PROJECT_BINARY_DIR}" --doxygen-executable "${DOXYGEN_EXECUTABLE}" --output-dir "${DOC_DEST_DIR}" --temp-dir "${DOC_TEMP_DIR}" ${PYTHON_API_OPTIONS} ${DOTNET_API_OPTIONS} ${JAVA_API_OPTIONS} DEPENDS ${DOC_EXTRA_DEPENDS} COMMENT "Generating documentation" ${ADD_CUSTOM_TARGET_USES_TERMINAL_ARG} ) # Remove generated documentation when running `clean` target. set_property(DIRECTORY APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES "${DOC_DEST_DIR}" ) option(Z3_INSTALL_API_BINDINGS_DOCUMENTATION "Install documentation for API bindings" ON) set(CMAKE_INSTALL_API_BINDINGS_DOC "${CMAKE_INSTALL_DOCDIR}" CACHE PATH "Path to install documentation for API bindings" ) if (Z3_INSTALL_API_BINDINGS_DOCUMENTATION) install( DIRECTORY "${DOC_DEST_DIR}" DESTINATION "${CMAKE_INSTALL_API_BINDINGS_DOC}" ) endif() z3-z3-4.8.7/doc/README000066400000000000000000000011211356505360400140470ustar00rootroot00000000000000API documentation ----------------- To generate the API documentation for the C, C++, .NET, Java and Python APIs, we must execute python mk_api_doc.py We must have doxygen installed in our system. The documentation will be stored in the subdirectory './api/html'. The main file is './api/html/index.html' Code documentation ------------------ To generate documentation for the Z3 code, we must execute doxygen z3code.dox We must also have dot installed in our system. The documentation will be store in the subdirectory './code/html'. The main file is './code/html/index.html' z3-z3-4.8.7/doc/design_recfuns.md000066400000000000000000000076531356505360400165270ustar00rootroot00000000000000# Design for handling recursive functions Main source of inspiration is [Sutter, Köksal & Kuncak 2011], as implemented in Leon, but the main differences is that we should unroll function definitions directly from the inside of Z3, in a backtracking way. Termination and fairness are ensured by iterative-deepening on the maximum number of unrollings in a given branch. ## Unfolding The idea is that every function definition `f(x1…xn) := rhs[x1…xn]` is compiled into: - a list of cases `A_f_i[x1…xn] => f(x1…xn) = rhs_i[x1…xn]`. When `A_f_i[t1…tn]` becomes true in the model, `f(t1…tn)` is said to be *unfolded* and the clause `A_f_i[t1…tn] => f(t1…tn) = rhs_i[t1…tn]` is added as an auxiliary clause. - a list of constraints `Γ_f_i[x1…xn] <=> A_f_i[x1…xn]` that states when `A_f_i[x1…xn]` should be true, depending on inputs `x1…xn`. For every term `f(t1…tn)` present in congruence closure, we immediately add all the `Γ_f_i[t1…tn] <=> A_f_i[t1…tn]` as auxiliary clauses (maybe during internalization of `f(t1…tn)`?). where each `A_f_i[x1…xn]` is a special new predicate representing the given case of `f`, and `rhs_i` does not contain any `ite`. We assume pattern matching has been compiled to `ite` beforehand. For example, `fact(n) := if n<2 then 1 else n * fact(n-1)` is compiled into: - `A_fact_0[n] => fact(n) = 1` - `A_fact_1[n] => fact(n) = n * fact(n-1)` - `A_fact_0[n] <=> n < 2` - `A_fact_1[n] <=> ¬(n < 2)` The 2 first clauses are only added when `A_fact_0[t]` is true (respectively `A_fact_1[t]` is true). The 2 other clauses are added as soon as `fact(t)` is internalized. ## Termination To ensure termination, we define variables: - `unfold_depth: int` - `current_max_unfold_depth: int` - `global_max_unfold_depth: int` and a special literal `[max_depth=$n]` for each `n:int`. Solving is done under the local assumption `[max_depth=$current_max_unfold_depth]` (this should be handled in some outer loop, e.g. in a custom tactic). Whenever `A_f_i[t1…tn]` becomes true (for any `f`), we increment `unfold_depth`. If `unfold_depth > current_max_unfold_depth`, then the conflict clause `[max_depth=$current_max_unfold_depth] => Γ => false` where `Γ` is the conjunction of all `A_f_i[t1…tn]` true in the trail. For non-recursive functions, we don't have to increment `unfold_depth`. Some other functions that are known If the solver answers "SAT", we have a model. Otherwise, if `[max_depth=$current_max_unfold_depth]` is part of the unsat-core, then we increase `current_max_unfold_depth`. If `current_max_unfold_depth == global_max_unfold_depth` then we report "UNKNOWN" (reached global depth limit), otherwise we can try to `solve()` again with the new assumption (higher depth limit). ## Tactic there should be a parametrized tactic `funrec(t, n)` where `t` is the tactic used to solve (under assumption that depth is limited to `current_max_unfold_depth`) and `n` is an integer that is assigned to `global_max_unfold_depth`. This way, to try and find models for a problem with recursive functions + LIA, one could use something like `(funrec (then simplify dl smt) 100)`. ## Expected benefits This addition to Z3 would bring many benefits compared to current alternatives (Leon, quantifiers, …) - should be very fast and lightweight (compared to Leon or quantifiers). In particular, every function call is very lightweight even compared to Leon (no need for full model building, followed by unsat core extraction) - possibility of answering "SAT" for any `QF_*` fragment + recursive functions - makes `define-funs-rec` a first-class citizen of the language, usable to model user-defined theories or to analyze functional programs directly ## Optimizations - maybe `C_f_i` literals should never be decided on (they can always be propagated). Even stronger: they should not be part of conflicts? (i.e. tune conflict resolution to always resolve these literals away, disregarding their level) z3-z3-4.8.7/doc/mk_api_doc.py000066400000000000000000000304501356505360400156350ustar00rootroot00000000000000# Copyright (c) Microsoft Corporation 2015 """ Z3 API documentation generator script """ import argparse import os import shutil import re import getopt import pydoc import sys import subprocess import shutil ML_ENABLED=False BUILD_DIR='../build' DOXYGEN_EXE='doxygen' TEMP_DIR=os.path.join(os.getcwd(), 'tmp') OUTPUT_DIRECTORY=os.path.join(os.getcwd(), 'api') Z3PY_PACKAGE_PATH='../src/api/python/z3' Z3PY_ENABLED=True DOTNET_ENABLED=True JAVA_ENABLED=True DOTNET_API_SEARCH_PATHS=['../src/api/dotnet'] JAVA_API_SEARCH_PATHS=['../src/api/java'] SCRIPT_DIR=os.path.abspath(os.path.dirname(__file__)) def parse_options(): global ML_ENABLED, BUILD_DIR, DOXYGEN_EXE, TEMP_DIR, OUTPUT_DIRECTORY global Z3PY_PACKAGE_PATH, Z3PY_ENABLED, DOTNET_ENABLED, JAVA_ENABLED global DOTNET_API_SEARCH_PATHS, JAVA_API_SEARCH_PATHS parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('-b', '--build', default=BUILD_DIR, help='Directory where Z3 is built (default: %(default)s)', ) parser.add_argument('--ml', action='store_true', default=False, help='Include ML/OCaml API documentation' ) parser.add_argument('--doxygen-executable', dest='doxygen_executable', default=DOXYGEN_EXE, help='Doxygen executable to use (default: %(default)s)', ) parser.add_argument('--temp-dir', dest='temp_dir', default=TEMP_DIR, help='Path to directory to use as temporary directory. ' '(default: %(default)s)', ) parser.add_argument('--output-dir', dest='output_dir', default=OUTPUT_DIRECTORY, help='Path to output directory (default: %(default)s)', ) parser.add_argument('--z3py-package-path', dest='z3py_package_path', default=Z3PY_PACKAGE_PATH, help='Path to directory containing Z3py package (default: %(default)s)', ) # FIXME: I would prefer not to have negative options (i.e. `--z3py` # instead of `--no-z3py`) but historically these bindings have been on by # default so we have options to disable generating documentation for these # bindings rather than enable them. parser.add_argument('--no-z3py', dest='no_z3py', action='store_true', default=False, help='Do not generate documentation for Python bindings', ) parser.add_argument('--no-dotnet', dest='no_dotnet', action='store_true', default=False, help='Do not generate documentation for .NET bindings', ) parser.add_argument('--no-java', dest='no_java', action='store_true', default=False, help='Do not generate documentation for Java bindings', ) parser.add_argument('--dotnet-search-paths', dest='dotnet_search_paths', nargs='+', default=DOTNET_API_SEARCH_PATHS, help='Specify one or more path to look for .NET files (default: %(default)s).', ) parser.add_argument('--java-search-paths', dest='java_search_paths', nargs='+', default=JAVA_API_SEARCH_PATHS, help='Specify one or more paths to look for Java files (default: %(default)s).', ) pargs = parser.parse_args() ML_ENABLED = pargs.ml BUILD_DIR = pargs.build DOXYGEN_EXE = pargs.doxygen_executable TEMP_DIR = pargs.temp_dir OUTPUT_DIRECTORY = pargs.output_dir Z3PY_PACKAGE_PATH = pargs.z3py_package_path Z3PY_ENABLED = not pargs.no_z3py DOTNET_ENABLED = not pargs.no_dotnet JAVA_ENABLED = not pargs.no_java DOTNET_API_SEARCH_PATHS = pargs.dotnet_search_paths JAVA_API_SEARCH_PATHS = pargs.java_search_paths if Z3PY_ENABLED: if not os.path.exists(Z3PY_PACKAGE_PATH): raise Exception('"{}" does not exist'.format(Z3PY_PACKAGE_PATH)) if not os.path.basename(Z3PY_PACKAGE_PATH) == 'z3': raise Exception('"{}" does not end with "z3"'.format(Z3PY_PACKAGE_PATH)) return def mk_dir(d): if not os.path.exists(d): os.makedirs(d) # Eliminate def_API, extra_API, and def_Type directives from file 'inf'. # The result is stored in 'outf'. def cleanup_API(inf, outf): pat1 = re.compile(".*def_API.*") pat2 = re.compile(".*extra_API.*") pat3 = re.compile(r".*def_Type\(.*") _inf = open(inf, 'r') _outf = open(outf, 'w') for line in _inf: if not pat1.match(line) and not pat2.match(line) and not pat3.match(line): _outf.write(line) def configure_file(template_file_path, output_file_path, substitutions): """ Read a template file ``template_file_path``, perform substitutions found in the ``substitutions`` dictionary and write the result to the output file ``output_file_path``. The template file should contain zero or more template strings of the form ``@NAME@``. The substitutions dictionary maps old strings (without the ``@`` symbols) to their replacements. """ assert isinstance(template_file_path, str) assert isinstance(output_file_path, str) assert isinstance(substitutions, dict) assert len(template_file_path) > 0 assert len(output_file_path) > 0 print("Generating {} from {}".format(output_file_path, template_file_path)) if not os.path.exists(template_file_path): raise Exception('Could not find template file "{}"'.format(template_file_path)) # Read whole template file into string template_string = None with open(template_file_path, 'r') as f: template_string = f.read() # Do replacements for (old_string, replacement) in substitutions.items(): template_string = template_string.replace('@{}@'.format(old_string), replacement) # Write the string to the file with open(output_file_path, 'w') as f: f.write(template_string) try: parse_options() print("Creating temporary directory \"{}\"".format(TEMP_DIR)) mk_dir(TEMP_DIR) # Short-hand for path to temporary file def temp_path(path): return os.path.join(TEMP_DIR, path) # Short-hand for path to file in `doc` directory def doc_path(path): return os.path.join(SCRIPT_DIR, path) # Create configuration file from template doxygen_config_substitutions = { 'OUTPUT_DIRECTORY': OUTPUT_DIRECTORY, 'TEMP_DIR': TEMP_DIR, 'CXX_API_SEARCH_PATHS': doc_path('../src/api/c++'), } if Z3PY_ENABLED: print("Z3Py documentation enabled") doxygen_config_substitutions['PYTHON_API_FILES'] = 'z3*.py' else: print("Z3Py documentation disabled") doxygen_config_substitutions['PYTHON_API_FILES'] = '' if DOTNET_ENABLED: print(".NET documentation enabled") doxygen_config_substitutions['DOTNET_API_FILES'] = '*.cs' dotnet_api_search_path_str = "" for p in DOTNET_API_SEARCH_PATHS: # Quote path so that paths with spaces are handled correctly dotnet_api_search_path_str += "\"{}\" ".format(p) doxygen_config_substitutions['DOTNET_API_SEARCH_PATHS'] = dotnet_api_search_path_str else: print(".NET documentation disabled") doxygen_config_substitutions['DOTNET_API_FILES'] = '' doxygen_config_substitutions['DOTNET_API_SEARCH_PATHS'] = '' if JAVA_ENABLED: print("Java documentation enabled") doxygen_config_substitutions['JAVA_API_FILES'] = '*.java' java_api_search_path_str = "" for p in JAVA_API_SEARCH_PATHS: # Quote path so that paths with spaces are handled correctly java_api_search_path_str += "\"{}\" ".format(p) doxygen_config_substitutions['JAVA_API_SEARCH_PATHS'] = java_api_search_path_str else: print("Java documentation disabled") doxygen_config_substitutions['JAVA_API_FILES'] = '' doxygen_config_substitutions['JAVA_API_SEARCH_PATHS'] = '' doxygen_config_file = temp_path('z3api.cfg') configure_file( doc_path('z3api.cfg.in'), doxygen_config_file, doxygen_config_substitutions) website_dox_substitutions = {} bullet_point_prefix='\n - ' if Z3PY_ENABLED: print("Python documentation enabled") website_dox_substitutions['PYTHON_API'] = ( '{prefix}Python API ' '(also available in pydoc format)' ).format( prefix=bullet_point_prefix) else: print("Python documentation disabled") website_dox_substitutions['PYTHON_API'] = '' if DOTNET_ENABLED: website_dox_substitutions['DOTNET_API'] = ( '{prefix}' '' '.NET API').format( prefix=bullet_point_prefix) else: website_dox_substitutions['DOTNET_API'] = '' if JAVA_ENABLED: website_dox_substitutions['JAVA_API'] = ( '{prefix}' 'Java API').format( prefix=bullet_point_prefix) else: website_dox_substitutions['JAVA_API'] = '' if ML_ENABLED: website_dox_substitutions['OCAML_API'] = ( '{prefix}ML/OCaml API' ).format( prefix=bullet_point_prefix) else: website_dox_substitutions['OCAML_API'] = '' configure_file( doc_path('website.dox.in'), temp_path('website.dox'), website_dox_substitutions) mk_dir(os.path.join(OUTPUT_DIRECTORY, 'html')) if Z3PY_ENABLED: shutil.copyfile(doc_path('../src/api/python/z3/z3.py'), temp_path('z3py.py')) cleanup_API(doc_path('../src/api/z3_api.h'), temp_path('z3_api.h')) cleanup_API(doc_path('../src/api/z3_ast_containers.h'), temp_path('z3_ast_containers.h')) cleanup_API(doc_path('../src/api/z3_algebraic.h'), temp_path('z3_algebraic.h')) cleanup_API(doc_path('../src/api/z3_polynomial.h'), temp_path('z3_polynomial.h')) cleanup_API(doc_path('../src/api/z3_rcf.h'), temp_path('z3_rcf.h')) cleanup_API(doc_path('../src/api/z3_fixedpoint.h'), temp_path('z3_fixedpoint.h')) cleanup_API(doc_path('../src/api/z3_optimization.h'), temp_path('z3_optimization.h')) cleanup_API(doc_path('../src/api/z3_fpa.h'), temp_path('z3_fpa.h')) print("Removed annotations from z3_api.h.") try: if subprocess.call([DOXYGEN_EXE, doxygen_config_file]) != 0: print("ERROR: doxygen returned nonzero return code") exit(1) except: print("ERROR: failed to execute 'doxygen', make sure doxygen (http://www.doxygen.org) is available in your system.") exit(1) print("Generated Doxygen based documentation") shutil.rmtree(os.path.realpath(TEMP_DIR)) print("Removed temporary directory \"{}\"".format(TEMP_DIR)) if Z3PY_ENABLED: # Put z3py at the beginning of the search path to try to avoid picking up # an installed copy of Z3py. sys.path.insert(0, os.path.dirname(Z3PY_PACKAGE_PATH)) if sys.version < '3': import __builtin__ __builtin__.Z3_LIB_DIRS = [ BUILD_DIR ] else: import builtins builtins.Z3_LIB_DIRS = [ BUILD_DIR ] for modulename in ( 'z3', 'z3.z3consts', 'z3.z3core', 'z3.z3num', 'z3.z3poly', 'z3.z3printer', 'z3.z3rcf', 'z3.z3types', 'z3.z3util', ): pydoc.writedoc(modulename) doc = modulename + '.html' shutil.move(doc, os.path.join(OUTPUT_DIRECTORY, 'html', doc)) print("Generated pydoc Z3Py documentation.") if ML_ENABLED: ml_output_dir = os.path.join(OUTPUT_DIRECTORY, 'html', 'ml') mk_dir(ml_output_dir) if subprocess.call(['ocamldoc', '-html', '-d', ml_output_dir, '-sort', '-hide', 'Z3', '-I', '%s/api/ml' % BUILD_DIR, '%s/api/ml/z3enums.mli' % BUILD_DIR, '%s/api/ml/z3.mli' % BUILD_DIR]) != 0: print("ERROR: ocamldoc failed.") exit(1) print("Generated ML/OCaml documentation.") print("Documentation was successfully generated at subdirectory '{}'.".format(OUTPUT_DIRECTORY)) except Exception: exctype, value = sys.exc_info()[:2] print("ERROR: failed to generate documentation: %s" % value) exit(1) z3-z3-4.8.7/doc/website.dox.in000066400000000000000000000010371356505360400157600ustar00rootroot00000000000000/** \mainpage An Efficient Theorem Prover Z3 is a high-performance theorem prover being developed at Microsoft Research. The Z3 website is at http://github.com/z3prover.. This website hosts the automatically generated documentation for the Z3 APIs. - \ref capi - \ref cppapi @DOTNET_API@ @JAVA_API@ @PYTHON_API@ @OCAML_API@ - Try Z3 online at RiSE4Fun. */ z3-z3-4.8.7/doc/z3api.cfg.in000066400000000000000000002325371356505360400153240ustar00rootroot00000000000000# Doxyfile 1.8.2 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" "). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # http://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or sequence of words) that should # identify the project. Note that if you do not use Doxywizard you need # to put quotes around the project name if it contains spaces. PROJECT_NAME = Z3 # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer # a quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = # With the PROJECT_LOGO tag one can specify an logo or icon that is # included in the documentation. The maximum height of the logo should not # exceed 55 pixels and the maximum width should not exceed 200 pixels. # Doxygen will copy the logo to the output directory. PROJECT_LOGO = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = "@OUTPUT_DIRECTORY@" # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English # messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, # Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, # Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = "The $name class " \ "The $name widget " \ "The $name file " \ is \ provides \ specifies \ contains \ represents \ a \ an \ the # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = YES # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = YES # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. Note that you specify absolute paths here, but also # relative paths, which will be relative from the directory where doxygen is # started. STRIP_FROM_PATH = ".." # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful if your file system # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = NO # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = "beginfaq=
    " \ "faq{2}=
  • \1

    \2
  • " \ "endfaq=
" \ "cmdopt{1}=\arg /\1" \ "ext{1}=.\1" \ "ty{1}=\1" \ "emph{1}=\1" \ "extdoc{2}=\2" \ "nicebox{1}=
\1
" \ "ccode{1}=\1" \ "zframe=" # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding # "class=itcl::class" will allow you to use the command class in the # itcl::class meaning. TCL_SUBST = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = YES # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for # VHDL. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, # and language is one of the parsers supported by doxygen: IDL, Java, # Javascript, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, # C++. For instance to make doxygen treat .inc files as Fortran files (default # is PHP), and .f files as C (default is Fortran), use: inc=Fortran f=C. Note # that for custom extensions you also need to set FILE_PATTERNS otherwise the # files are not read by doxygen. EXTENSION_MAPPING = # If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all # comments according to the Markdown format, which allows for more readable # documentation. See http://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you # can mix doxygen, HTML, and XML commands with Markdown formatting. # Disable only in case of backward compatibilities issues. MARKDOWN_SUPPORT = YES # When enabled doxygen tries to link words that correspond to documented classes, # or namespaces to their corresponding documentation. Such a link can be # prevented in individual cases by by putting a % sign in front of the word or # globally by setting AUTOLINK_SUPPORT to NO. AUTOLINK_SUPPORT = YES # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also makes the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate getter and setter methods for a property. Setting this option to YES (the default) will make doxygen replace the get and set methods by a property in the documentation. This will only work if the methods are indeed getting or setting a simple type. If this is not the case, or you want to show the methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and # unions are shown inside the group in which they are included (e.g. using # @ingroup) instead of on a separate page (for HTML and Man pages) or # section (for LaTeX and RTF). INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and # unions with only public data fields will be shown inline in the documentation # of the scope in which they are defined (i.e. file, namespace, or group # documentation), provided this scope is documented. If set to NO (the default), # structs, classes, and unions are shown on a separate page (for HTML and Man # pages) or section (for LaTeX and RTF). INLINE_SIMPLE_STRUCTS = NO # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum # is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. TYPEDEF_HIDES_STRUCT = NO # The SYMBOL_CACHE_SIZE determines the size of the internal cache use to # determine which symbols to keep in memory and which to flush to disk. # When the cache is full, less often used symbols will be written to disk. # For small to medium size projects (<1000 input files) the default value is # probably good enough. For larger projects a too small cache size can cause # doxygen to be busy swapping symbols to and from disk most of the time # causing a significant performance penalty. # If the system has enough physical memory increasing the cache will improve the # performance by keeping more symbols in memory. Note that the value works on # a logarithmic scale so increasing the size by one will roughly double the # memory usage. The cache size is given by this formula: # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols. # SYMBOL_CACHE_SIZE = 0 # Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be # set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given # their name and scope. Since this can be an expensive process and often the # same symbol appear multiple times in the code, doxygen keeps a cache of # pre-resolved symbols. If the cache is too small doxygen will become slower. # If the cache is too large, memory is wasted. The cache size is given by this # formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols. LOOKUP_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_PACKAGE tag is set to YES all members with package or internal # scope will be included in the documentation. EXTRACT_PACKAGE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = NO # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base # name of the file that contains the anonymous namespace. By default # anonymous namespaces are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = YES # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = NO # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = YES # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = NO # If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen # will list include files with double quotes in the documentation # rather than with sharp brackets. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen # will sort the (brief and detailed) documentation of class members so that # constructors and destructors are listed first. If set to NO (the default) # the constructors will appear in the respective orders defined by # SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. # This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO # and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to # do proper type resolution of all parameters of a function it will reject a # match between the prototype and the implementation of a member function even # if there is only one candidate or it is obvious which candidate to choose # by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen # will still accept a match between prototype and implementation in such cases. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = NO # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = NO # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = NO # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or macro consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and macros in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = NO # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. # This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. # You can optionally specify a file name after the option, if omitted # DoxygenLayout.xml will be used as the name of the layout file. LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files # containing the references data. This must be a list of .bib files. The # .bib extension is automatically appended if omitted. Using this command # requires the bibtex tool to be installed. See also # http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style # of the bibliography can be controlled using LATEX_BIB_STYLE. To use this # feature you need bibtex and perl available in the search path. CITE_BIB_FILES = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = NO # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # The WARN_NO_PARAMDOC option can be enabled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text " # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = "@TEMP_DIR@" \ "@CXX_API_SEARCH_PATHS@" \ @DOTNET_API_SEARCH_PATHS@ @JAVA_API_SEARCH_PATHS@ # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh # *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py # *.f90 *.f *.for *.vhd *.vhdl FILE_PATTERNS = website.dox \ z3_api.h \ z3_algebraic.h \ z3_ast_containers.h \ z3_fixedpoint.h \ z3_fpa.h \ z3_interp.h \ z3_optimization.h \ z3_polynomial.h \ z3_rcf.h \ z3++.h \ @PYTHON_API_FILES@ @DOTNET_API_FILES@ @JAVA_API_FILES@ # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # Note that relative paths are relative to the directory from which doxygen is # run. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = * # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. # If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. # Doxygen will compare the file name with each pattern and apply the # filter if there is a match. # The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty or if # non of the patterns match the file name, INPUT_FILTER is applied. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) # and it is also possible to disable source filtering for a specific pattern # using *.ext= (so without naming a filter). This option only has effect when # FILTER_SOURCE_FILES is enabled. FILTER_SOURCE_PATTERNS = #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = YES # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = YES # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C, C++ and Fortran comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. # Otherwise they will link to the documentation. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = NO #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = NO # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. Note that when using a custom header you are responsible # for the proper inclusion of any scripts and style sheets that doxygen # needs, which is dependent on the configuration options used. # It is advised to generate a default header using "doxygen -w html # header.html footer.html stylesheet.css YourConfigFile" and then modify # that header. Note that the header is subject to change so you typically # have to redo this when upgrading to a newer version of doxygen or when # changing the value of configuration settings such as GENERATE_TREEVIEW! HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If left blank doxygen will # generate a default style sheet. Note that it is recommended to use # HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this # tag will in the future become obsolete. HTML_STYLESHEET = # The HTML_EXTRA_STYLESHEET tag can be used to specify an additional # user-defined cascading style sheet that is included after the standard # style sheets created by doxygen. Using this option one can overrule # certain style aspects. This is preferred over using HTML_STYLESHEET # since it does not replace the standard style sheet and is therefore more # robust against future updates. Doxygen will copy the style sheet file to # the output directory. HTML_EXTRA_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that # the files will be copied as-is; there are no commands or markers available. HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. # Doxygen will adjust the colors in the style sheet and background images # according to this color. Hue is specified as an angle on a colorwheel, # see http://en.wikipedia.org/wiki/Hue for more information. # For instance the value 0 represents red, 60 is yellow, 120 is green, # 180 is cyan, 240 is blue, 300 purple, and 360 is red again. # The allowed range is 0 to 359. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of # the colors in the HTML output. For a value of 0 the output will use # grayscales only. A value of 255 will produce the most vivid colors. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to # the luminance component of the colors in the HTML output. Values below # 100 gradually make the output lighter, whereas values above 100 make # the output darker. The value divided by 100 is the actual gamma applied, # so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, # and 100 does not change the gamma. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting # this to NO can help when comparing the output of multiple runs. HTML_TIMESTAMP = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. HTML_DYNAMIC_SECTIONS = YES # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of # entries shown in the various tree structured indices initially; the user # can expand and collapse entries dynamically later on. Doxygen will expand # the tree to such a level that at most the specified number of entries are # visible (unless a fully collapsed tree already exceeds this amount). # So setting the number of entries 1 will produce a full collapsed tree by # default. 0 is a special value representing an infinite number of entries # and will result in a full expanded tree by default. HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # feed. A documentation feed provides an umbrella under which multiple # documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that # should uniquely identify the documentation set bundle. This should be a # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project # When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely # identify the documentation publisher. This should be a reverse domain-name # style string, e.g. com.mycompany.MyDocSet.documentation. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated # that can be used as input for Qt's qhelpgenerator to generate a # Qt Compressed Help (.qch) of the generated HTML documentation. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can # be used to specify the file name of the resulting .qch file. # The path specified is relative to the HTML output folder. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#namespace QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = doc # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to # add. For more information please see # http://doc.trolltech.com/qthelpproject.html#custom-filters QHP_CUST_FILTER_NAME = # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see # # Qt Help Project / Custom Filters. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's # filter section matches. # # Qt Help Project / Filter Attributes. QHP_SECT_FILTER_ATTRS = # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can # be used to specify the location of Qt's qhelpgenerator. # If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files # will be generated, which together with the HTML files, form an Eclipse help # plugin. To install this plugin and make it available under the help contents # menu in Eclipse, the contents of the directory containing the HTML and XML # files needs to be copied into the plugins directory of eclipse. The name of # the directory within the plugins directory should be the same as # the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before # the help appears. GENERATE_ECLIPSEHELP = NO # A unique identifier for the eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have # this name. ECLIPSE_DOC_ID = org.doxygen.Project # The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) # at top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. Since the tabs have the same information as the # navigation tree you can set this option to NO if you already set # GENERATE_TREEVIEW to YES. DISABLE_INDEX = YES # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to YES, a side panel will be generated # containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). # Windows users are probably better off using the HTML help feature. # Since the tree basically has the same information as the tab index you # could consider to set DISABLE_INDEX to NO when enabling this option. GENERATE_TREEVIEW = NO # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values # (range [0,1..20]) that doxygen will group on one line in the generated HTML # documentation. Note that a value of 0 will completely suppress the enum # values from appearing in the overview section. ENUM_VALUES_PER_LINE = 4 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 # When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open # links to external symbols imported via tag files in a separate window. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # when you change the font size after a successful doxygen run you need # to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are # not supported properly for IE 6.0, but are supported on all modern browsers. # Note that when changing this option you need to delete any form_*.png files # in the HTML output before the changes have effect. FORMULA_TRANSPARENT = YES # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax # (see http://www.mathjax.org) which uses client side Javascript for the # rendering instead of using prerendered bitmaps. Use this if you do not # have LaTeX installed or if you want to formulas look prettier in the HTML # output. When enabled you may also need to install MathJax separately and # configure the path to it using the MATHJAX_RELPATH option. USE_MATHJAX = NO # When MathJax is enabled you need to specify the location relative to the # HTML output directory using the MATHJAX_RELPATH option. The destination # directory should contain the MathJax.js script. For instance, if the mathjax # directory is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to # the MathJax Content Delivery Network so you can quickly see the result without # installing MathJax. # However, it is strongly recommended to install a local # copy of MathJax from http://www.mathjax.org before deployment. MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest # The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension # names that should be enabled during MathJax rendering. MATHJAX_EXTENSIONS = # When the SEARCHENGINE tag is enabled doxygen will generate a search box # for the HTML output. The underlying search engine uses javascript # and DHTML and should work on any modern browser. Note that when using # HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets # (GENERATE_DOCSET) there is already a search function so this one should # typically be disabled. For large projects the javascript based search engine # can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. SEARCHENGINE = YES # When the SERVER_BASED_SEARCH tag is enabled the search engine will be # implemented using a PHP enabled web server instead of at the web client # using Javascript. Doxygen will generate the search PHP script and index # file to put on the web server. The advantage of the server # based approach is that it scales better to large projects and allows # full text search. The disadvantages are that it is more difficult to setup # and does not have live searching capabilities. SERVER_BASED_SEARCH = NO #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. # Note that when enabling USE_PDFLATEX this option is only used for # generating bitmaps for formulas in the HTML output, but not in the # Makefile that is written to the output directory. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4wide # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for # the generated latex document. The footer should contain everything after # the last chapter. If it is left blank doxygen will generate a # standard footer. Notice: only use this tag if you know what you are doing! LATEX_FOOTER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = NO # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = YES # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO # If LATEX_SOURCE_CODE is set to YES then doxygen will include # source code with syntax highlighting in the LaTeX output. # Note that which sources are shown also depends on other settings # such as SOURCE_BROWSER. LATEX_SOURCE_CODE = NO # The LATEX_BIB_STYLE tag can be used to specify the style to use for the # bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See # http://en.wikipedia.org/wiki/BibTeX for more info. LATEX_BIB_STYLE = plain #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load style sheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. # XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. # XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. # This is useful # if you want to understand what is going on. # On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = YES # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = YES # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # pointed to by INCLUDE_PATH will be searched when a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = Z3_ast_opt:=Z3_ast \ Z3_bool_opt:=Z3_bool \ Z3_func_interp_opt:=Z3_func_interp \ Z3_model_opt:=Z3_model \ __out_opt:=__out # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition that # overrules the definition found in the source code. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all references to function-like macros # that are alone on a line, have an all uppercase name, and do not end with a # semicolon, because these will confuse the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. For each # tag file the location of the external documentation should be added. The # format of a tag file without this location is as follows: # # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths # or URLs. Note that each tag file must have a unique name (where the name does # NOT include the path). If a tag file is not located in the directory in which # doxygen is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = YES # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option also works with HAVE_DOT disabled, but it is recommended to # install and use dot, since it yields more powerful graphs. CLASS_DIAGRAMS = YES # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = NO # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = YES # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is # allowed to run in parallel. When set to 0 (the default) doxygen will # base this on the number of processors available in the system. You can set it # explicitly to a value larger than 0 to get control over the balance # between CPU load and processing speed. DOT_NUM_THREADS = 0 # By default doxygen will use the Helvetica font for all dot files that # doxygen generates. When you want a differently looking font you can specify # the font name using DOT_FONTNAME. You need to make sure dot is able to find # the font, which can be done by putting it in a standard location or by setting # the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the # directory containing the font. # DOT_FONTNAME = FreeSans # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 10 # By default doxygen will tell dot to use the Helvetica font. # If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to # set the path where dot can find it. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = NO # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = NO # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If the UML_LOOK tag is enabled, the fields and methods are shown inside # the class node. If there are many fields or methods and many nodes the # graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS # threshold limits the number of items for each type to make the size more # manageable. Set this to 0 for no limit. Note that the threshold may be # exceeded by 50% before the limit is enforced. UML_LIMIT_NUM_FIELDS = 10 # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = NO # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will generate a graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = NO # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are svg, png, jpg, or gif. # If left blank png will be used. If you choose svg you need to set # HTML_FILE_EXTENSION to xhtml in order to make the SVG files # visible in IE 9+ (other browsers do not have this requirement). DOT_IMAGE_FORMAT = png # If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to # enable generation of interactive SVG images that allow zooming and panning. # Note that this requires a modern browser other than Internet Explorer. # Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you # need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files # visible. Older versions of IE do not have SVG support. INTERACTIVE_SVG = NO # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The MSCFILE_DIRS tag can be used to specify one or more directories that # contain msc files that are included in the documentation (see the # \mscfile command). MSCFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 1000 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, because dot on Windows does not # seem to support this out of the box. Warning: Depending on the platform used, # enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = NO # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = NO z3-z3-4.8.7/doc/z3code.dox000066400000000000000000001261411356505360400151040ustar00rootroot00000000000000# Doxyfile 1.6.3 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project # # All text after a hash (#) is considered a comment and will be ignored # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" ") #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = Z3 PROJECT_NUMBER = OUTPUT_DIRECTORY = code CREATE_SUBDIRS = NO OUTPUT_LANGUAGE = English BRIEF_MEMBER_DESC = YES REPEAT_BRIEF = YES ABBREVIATE_BRIEF = "The $name class " \ "The $name widget " \ "The $name file " \ is \ provides \ specifies \ contains \ represents \ a \ an \ the # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = YES INLINE_INHERITED_MEMB = NO FULL_PATH_NAMES = YES STRIP_FROM_PATH = ".." STRIP_FROM_INC_PATH = SHORT_NAMES = NO JAVADOC_AUTOBRIEF = NO QT_AUTOBRIEF = NO MULTILINE_CPP_IS_BRIEF = NO INHERIT_DOCS = YES SEPARATE_MEMBER_PAGES = NO TAB_SIZE = 4 ALIASES = "beginfaq=
    " \ "faq{2}=
  • \1

    \2
  • " \ "endfaq=
" \ "cmdopt{1}=\arg /\1" \ "ext{1}=.\1" \ "ty{1}=\1" \ "emph{1}=\1" \ "extdoc{2}=\2" \ "nicebox{1}=
\1
" \ "ccode{1}=\1" \ "zframe=" OPTIMIZE_OUTPUT_FOR_C = YES OPTIMIZE_OUTPUT_JAVA = NO OPTIMIZE_FOR_FORTRAN = NO OPTIMIZE_OUTPUT_VHDL = NO # Doxygen has a built-in mapping, but you can override or extend it using this tag. # The format is ext=language, where ext is a file extension, and language is one of # the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP, # Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat # .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran), # use: inc=Fortran f=C. Note that for custom extensions you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. EXTENSION_MAPPING = BUILTIN_STL_SUPPORT = NO CPP_CLI_SUPPORT = NO SIP_SUPPORT = NO IDL_PROPERTY_SUPPORT = YES DISTRIBUTE_GROUP_DOC = NO SUBGROUPING = YES TYPEDEF_HIDES_STRUCT = NO SYMBOL_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- EXTRACT_ALL = YES EXTRACT_PRIVATE = YES EXTRACT_STATIC = YES EXTRACT_LOCAL_CLASSES = YES EXTRACT_LOCAL_METHODS = YES EXTRACT_ANON_NSPACES = YES HIDE_UNDOC_MEMBERS = NO HIDE_UNDOC_CLASSES = NO HIDE_FRIEND_COMPOUNDS = NO HIDE_IN_BODY_DOCS = NO INTERNAL_DOCS = YES CASE_SENSE_NAMES = NO # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = YES SHOW_INCLUDE_FILES = YES FORCE_LOCAL_INCLUDES = NO INLINE_INFO = YES SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the (brief and detailed) documentation of class members so that constructors and destructors are listed first. If set to NO (the default) the constructors will appear in the respective orders defined by SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or define consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and defines in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = NO # If the sources in your project are distributed over multiple directories # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy # in the documentation. The default is NO. SHOW_DIRECTORIES = NO # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. # This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by # doxygen. The layout file controls the global structure of the generated output files # in an output format independent way. The create the layout file that represents # doxygen's defaults, run doxygen with the -l option. You can optionally specify a # file name after the option, if omitted DoxygenLayout.xml will be used as the name # of the layout file. LAYOUT_FILE = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be abled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text " # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = ../src # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx # *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 FILE_PATTERNS = *.cpp *.h # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix filesystem feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = * # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. # If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. # Doxygen will compare the file name with each pattern and apply the # filter if there is a match. # The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER # is applied to all files. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = YES # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = YES # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = YES # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. # Otherwise they will link to the documentation. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = NO #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = NO # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # stylesheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting # this to NO can help when comparing the output of multiple runs. HTML_TIMESTAMP = YES # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. For this to work a browser that supports # JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). HTML_DYNAMIC_SECTIONS = YES # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # feed. A documentation feed provides an umbrella under which multiple # documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that # should uniquely identify the documentation set bundle. This should be a # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER # are set, an additional index file will be generated that can be used as input for # Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated # HTML documentation. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can # be used to specify the file name of the resulting .qch file. # The path specified is relative to the HTML output folder. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#namespace QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = doc # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add. # For more information please see # http://doc.trolltech.com/qthelpproject.html#custom-filters QHP_CUST_FILTER_NAME = # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see # Qt Help Project / Custom Filters. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's # filter section matches. # Qt Help Project / Filter Attributes. QHP_SECT_FILTER_ATTRS = # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can # be used to specify the location of Qt's qhelpgenerator. # If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files # will be generated, which together with the HTML files, form an Eclipse help # plugin. To install this plugin and make it available under the help contents # menu in Eclipse, the contents of the directory containing the HTML and XML # files needs to be copied into the plugins directory of eclipse. The name of # the directory within the plugins directory should be the same as # the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before the help appears. GENERATE_ECLIPSEHELP = NO # A unique identifier for the eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have # this name. ECLIPSE_DOC_ID = org.doxygen.Project # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = YES # This tag can be used to set the number of enum values (range [1..20]) # that doxygen will group on one line in the generated HTML documentation. ENUM_VALUES_PER_LINE = 4 # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to YES, a side panel will be generated # containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). # Windows users are probably better off using the HTML help feature. GENERATE_TREEVIEW = YES # By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, # and Class Hierarchy pages using a tree view instead of an ordered list. USE_INLINE_TREES = YES # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # when you change the font size after a successful doxygen run you need # to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. FORMULA_FONTSIZE = 10 # When the SEARCHENGINE tag is enabled doxygen will generate a search box for the HTML output. The underlying search engine uses javascript # and DHTML and should work on any modern browser. Note that when using HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) there is already a search function so this one should # typically be disabled. For large projects the javascript based search engine # can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. SEARCHENGINE = YES # When the SERVER_BASED_SEARCH tag is enabled the search engine will be implemented using a PHP enabled web server instead of at the web client using Javascript. Doxygen will generate the search PHP script and index # file to put on the web server. The advantage of the server based approach is that it scales better to large projects and allows full text search. The disadvances is that it is more difficult to setup # and does not have live searching capabilities. SERVER_BASED_SEARCH = NO #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- GENERATE_LATEX = NO LATEX_OUTPUT = latex LATEX_CMD_NAME = latex MAKEINDEX_CMD_NAME = makeindex COMPACT_LATEX = NO PAPER_TYPE = a4wide EXTRA_PACKAGES = LATEX_HEADER = PDF_HYPERLINKS = NO USE_PDFLATEX = YES LATEX_BATCHMODE = NO LATEX_HIDE_INDICES = NO LATEX_SOURCE_CODE = NO GENERATE_RTF = NO RTF_OUTPUT = rtf COMPACT_RTF = NO RTF_HYPERLINKS = NO RTF_STYLESHEET_FILE = RTF_EXTENSIONS_FILE = GENERATE_MAN = NO MAN_OUTPUT = man MAN_EXTENSION = .3 MAN_LINKS = NO GENERATE_XML = NO XML_OUTPUT = xml XML_SCHEMA = XML_DTD = XML_PROGRAMLISTING = YES GENERATE_AUTOGEN_DEF = NO GENERATE_PERLMOD = NO PERLMOD_LATEX = NO PERLMOD_PRETTY = YES PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = YES # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = YES # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # in the INCLUDE_PATH (see below) will be search if a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED =Z3_ast_opt:=Z3_ast Z3_bool_opt:=Z3_bool Z3_func_interp_opt:=Z3_func_interp Z3_model_opt:=Z3_model __out_opt:=__out # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition. EXPAND_AS_DEFINED = # Z3_ast_opt Z3_bool_opt Z3_func_interp_opt Z3_model_opt __out_opt # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all function-like macros that are alone # on a line, have an all uppercase name, and do not end with a semicolon. Such # function macros are typically used for boiler-plate code, and will confuse # the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: # # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option is superseded by the HAVE_DOT option below. This is only a # fallback. It is recommended to install and use dot, since it yields more # powerful graphs. CLASS_DIAGRAMS = YES # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = NO # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = YES # By default doxygen will write a font called FreeSans.ttf to the output # directory and reference it in all dot files that doxygen generates. This # font does not include all possible unicode characters however, so when you need # these (or just want a differently looking font) you can specify the font name # using DOT_FONTNAME. You need need to make sure dot is able to find the font, # which can be done by putting it in a standard location or by setting the # DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory # containing the font. DOT_FONTNAME = FreeSans # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 10 # By default doxygen will tell dot to use the output directory to look for the # FreeSans.ttf font (which doxygen will put there itself). If you specify a # different font using DOT_FONTNAME you can set the path where dot # can find it using this tag. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = NO # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = YES # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = YES # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = YES # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are png, jpg, or gif # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 1000 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, because dot on Windows does not # seem to support this out of the box. Warning: Depending on the platform used, # enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = NO # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES z3-z3-4.8.7/examples/000077500000000000000000000000001356505360400142455ustar00rootroot00000000000000z3-z3-4.8.7/examples/CMakeLists.txt000066400000000000000000000106661356505360400170160ustar00rootroot00000000000000include(ExternalProject) # Unfortunately `BUILD_ALWAYS` only seems to be supported with the version of ExternalProject # that shipped with CMake >= 3.1. if (("${CMAKE_VERSION}" VERSION_EQUAL "3.1") OR ("${CMAKE_VERSION}" VERSION_GREATER "3.1")) set(EXTERNAL_PROJECT_BUILD_ALWAYS_ARG BUILD_ALWAYS 1) else() set(EXTERNAL_PROJECT_BUILD_ALWAYS_ARG "") endif() option(Z3_C_EXAMPLES_FORCE_CXX_LINKER "Force C++ linker when building C example projects" OFF) if (Z3_C_EXAMPLES_FORCE_CXX_LINKER) # HACK: This is a workaround for UBSan. message(STATUS "Forcing C++ linker to be used when building example C projects") set(EXTERNAL_C_PROJ_USE_CXX_LINKER_ARG "-DFORCE_CXX_LINKER=ON" ) else() set(EXTERNAL_C_PROJ_USE_CXX_LINKER_ARG "") endif() if (DEFINED CMAKE_CONFIGURATION_TYPES) message(WARNING "Cannot set built type of external project when building with a " "multi-configuration generator") set(EXTERNAL_PROJECT_CMAKE_BUILD_TYPE_ARG "") else() set(EXTERNAL_PROJECT_CMAKE_BUILD_TYPE_ARG "-DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE}" ) endif() ################################################################################ # Build example project using libz3's C API as an external project ################################################################################ ExternalProject_Add(c_example DEPENDS libz3 # Configure step SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/c" CMAKE_ARGS "-DZ3_DIR=${PROJECT_BINARY_DIR}" "${EXTERNAL_C_PROJ_USE_CXX_LINKER_ARG}" "${EXTERNAL_PROJECT_CMAKE_BUILD_TYPE_ARG}" # Build step ${EXTERNAL_PROJECT_BUILD_ALWAYS_ARG} BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/c_example_build_dir" # Install Step INSTALL_COMMAND "${CMAKE_COMMAND}" -E echo "" # Dummy command ) set_target_properties(c_example PROPERTIES EXCLUDE_FROM_ALL TRUE) ################################################################################ # Build maxsat example project using libz3's C API as an external project ################################################################################ ExternalProject_Add(c_maxsat_example DEPENDS libz3 # Configure step SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/maxsat" CMAKE_ARGS "-DZ3_DIR=${PROJECT_BINARY_DIR}" "${EXTERNAL_C_PROJ_USE_CXX_LINKER_ARG}" "${EXTERNAL_PROJECT_CMAKE_BUILD_TYPE_ARG}" # Build step ${EXTERNAL_PROJECT_BUILD_ALWAYS_ARG} BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/c_maxsat_example_build_dir" # Install Step INSTALL_COMMAND "${CMAKE_COMMAND}" -E echo "" # Dummy command ) set_target_properties(c_maxsat_example PROPERTIES EXCLUDE_FROM_ALL TRUE) ################################################################################ # Build example project using libz3's C++ API as an external project ################################################################################ ExternalProject_Add(cpp_example DEPENDS libz3 # Configure step SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/c++" CMAKE_ARGS "-DZ3_DIR=${PROJECT_BINARY_DIR}" "${EXTERNAL_PROJECT_CMAKE_BUILD_TYPE_ARG}" # Build step ${EXTERNAL_PROJECT_BUILD_ALWAYS_ARG} BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/cpp_example_build_dir" # Install Step INSTALL_COMMAND "${CMAKE_COMMAND}" -E echo "" # Dummy command ) set_target_properties(cpp_example PROPERTIES EXCLUDE_FROM_ALL TRUE) ################################################################################ # Build example tptp5 project using libz3's C++ API as an external project ################################################################################ ExternalProject_Add(z3_tptp5 DEPENDS libz3 # Configure step SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/tptp" CMAKE_ARGS "-DZ3_DIR=${PROJECT_BINARY_DIR}" "${EXTERNAL_PROJECT_CMAKE_BUILD_TYPE_ARG}" # Build step ${EXTERNAL_PROJECT_BUILD_ALWAYS_ARG} BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/tptp_build_dir" # Install Step INSTALL_COMMAND "${CMAKE_COMMAND}" -E echo "" # Dummy command ) set_target_properties(z3_tptp5 PROPERTIES EXCLUDE_FROM_ALL TRUE) ################################################################################ # Build Python examples ################################################################################ if (Z3_BUILD_PYTHON_BINDINGS) add_subdirectory(python) endif() ################################################################################ # Build dotnet examples ################################################################################ if (Z3_BUILD_DOTNET_BINDINGS) add_subdirectory(dotnet) endif()z3-z3-4.8.7/examples/c++/000077500000000000000000000000001356505360400146155ustar00rootroot00000000000000z3-z3-4.8.7/examples/c++/CMakeLists.txt000066400000000000000000000031621356505360400173570ustar00rootroot00000000000000################################################################################ # Example C++ project ################################################################################ project(Z3_C_EXAMPLE CXX) cmake_minimum_required(VERSION 3.4) find_package(Z3 REQUIRED CONFIG # `NO_DEFAULT_PATH` is set so that -DZ3_DIR has to be passed to find Z3. # This should prevent us from accidentally picking up an installed # copy of Z3. This is here to benefit Z3's build system when building # this project. When making your own project you probably shouldn't # use this option. NO_DEFAULT_PATH ) ################################################################################ # Z3 C++ API bindings require C++11 ################################################################################ set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) message(STATUS "Z3_FOUND: ${Z3_FOUND}") message(STATUS "Found Z3 ${Z3_VERSION_STRING}") message(STATUS "Z3_DIR: ${Z3_DIR}") add_executable(cpp_example example.cpp) target_include_directories(cpp_example PRIVATE ${Z3_CXX_INCLUDE_DIRS}) target_link_libraries(cpp_example PRIVATE ${Z3_LIBRARIES}) if ("${CMAKE_SYSTEM_NAME}" MATCHES "[Ww]indows") # On Windows we need to copy the Z3 libraries # into the same directory as the executable # so that they can be found. foreach (z3_lib ${Z3_LIBRARIES}) message(STATUS "Adding copy rule for ${z3_lib}") add_custom_command(TARGET cpp_example POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different $ $ ) endforeach() endif() z3-z3-4.8.7/examples/c++/README000066400000000000000000000006411356505360400154760ustar00rootroot00000000000000Small example using the c++ bindings. To build the example execute make examples in the build directory. This command will create the executable cpp_example. On Windows, you can just execute it. On macOS and Linux, you must install z3 first using sudo make install OR update LD_LIBRARY_PATH (Linux) or DYLD_LIBRARY_PATH (macOS) with the build directory. You need that to be able to find the Z3 shared library. z3-z3-4.8.7/examples/c++/example.cpp000066400000000000000000001167161356505360400167700ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include #include"z3++.h" using namespace z3; /** Demonstration of how Z3 can be used to prove validity of De Morgan's Duality Law: {e not(x and y) <-> (not x) or ( not y) } */ void demorgan() { std::cout << "de-Morgan example\n"; context c; expr x = c.bool_const("x"); expr y = c.bool_const("y"); expr conjecture = (!(x && y)) == (!x || !y); solver s(c); // adding the negation of the conjecture as a constraint. s.add(!conjecture); std::cout << s << "\n"; std::cout << s.to_smt2() << "\n"; switch (s.check()) { case unsat: std::cout << "de-Morgan is valid\n"; break; case sat: std::cout << "de-Morgan is not valid\n"; break; case unknown: std::cout << "unknown\n"; break; } } /** \brief Find a model for x >= 1 and y < x + 3. */ void find_model_example1() { std::cout << "find_model_example1\n"; context c; expr x = c.int_const("x"); expr y = c.int_const("y"); solver s(c); s.add(x >= 1); s.add(y < x + 3); std::cout << s.check() << "\n"; model m = s.get_model(); std::cout << m << "\n"; // traversing the model for (unsigned i = 0; i < m.size(); i++) { func_decl v = m[i]; // this problem contains only constants assert(v.arity() == 0); std::cout << v.name() << " = " << m.get_const_interp(v) << "\n"; } // we can evaluate expressions in the model. std::cout << "x + y + 1 = " << m.eval(x + y + 1) << "\n"; } /** \brief Prove x = y implies g(x) = g(y), and disprove x = y implies g(g(x)) = g(y). This function demonstrates how to create uninterpreted types and functions. */ void prove_example1() { std::cout << "prove_example1\n"; context c; expr x = c.int_const("x"); expr y = c.int_const("y"); sort I = c.int_sort(); func_decl g = function("g", I, I); solver s(c); expr conjecture1 = implies(x == y, g(x) == g(y)); std::cout << "conjecture 1\n" << conjecture1 << "\n"; s.add(!conjecture1); if (s.check() == unsat) std::cout << "proved" << "\n"; else std::cout << "failed to prove" << "\n"; s.reset(); // remove all assertions from solver s expr conjecture2 = implies(x == y, g(g(x)) == g(y)); std::cout << "conjecture 2\n" << conjecture2 << "\n"; s.add(!conjecture2); if (s.check() == unsat) { std::cout << "proved" << "\n"; } else { std::cout << "failed to prove" << "\n"; model m = s.get_model(); std::cout << "counterexample:\n" << m << "\n"; std::cout << "g(g(x)) = " << m.eval(g(g(x))) << "\n"; std::cout << "g(y) = " << m.eval(g(y)) << "\n"; } } /** \brief Prove not(g(g(x) - g(y)) = g(z)), x + z <= y <= x implies z < 0 . Then, show that z < -1 is not implied. This example demonstrates how to combine uninterpreted functions and arithmetic. */ void prove_example2() { std::cout << "prove_example1\n"; context c; expr x = c.int_const("x"); expr y = c.int_const("y"); expr z = c.int_const("z"); sort I = c.int_sort(); func_decl g = function("g", I, I); expr conjecture1 = implies(g(g(x) - g(y)) != g(z) && x + z <= y && y <= x, z < 0); solver s(c); s.add(!conjecture1); std::cout << "conjecture 1:\n" << conjecture1 << "\n"; if (s.check() == unsat) std::cout << "proved" << "\n"; else std::cout << "failed to prove" << "\n"; expr conjecture2 = implies(g(g(x) - g(y)) != g(z) && x + z <= y && y <= x, z < -1); s.reset(); s.add(!conjecture2); std::cout << "conjecture 2:\n" << conjecture2 << "\n"; if (s.check() == unsat) { std::cout << "proved" << "\n"; } else { std::cout << "failed to prove" << "\n"; std::cout << "counterexample:\n" << s.get_model() << "\n"; } } /** \brief Nonlinear arithmetic example 1 */ void nonlinear_example1() { std::cout << "nonlinear example 1\n"; config cfg; cfg.set("auto_config", true); context c(cfg); expr x = c.real_const("x"); expr y = c.real_const("y"); expr z = c.real_const("z"); solver s(c); s.add(x*x + y*y == 1); // x^2 + y^2 == 1 s.add(x*x*x + z*z*z < c.real_val("1/2")); // x^3 + z^3 < 1/2 s.add(z != 0); std::cout << s.check() << "\n"; model m = s.get_model(); std::cout << m << "\n"; set_param("pp.decimal", true); // set decimal notation std::cout << "model in decimal notation\n"; std::cout << m << "\n"; set_param("pp.decimal-precision", 50); // increase number of decimal places to 50. std::cout << "model using 50 decimal places\n"; std::cout << m << "\n"; set_param("pp.decimal", false); // disable decimal notation } /** \brief Simple function that tries to prove the given conjecture using the following steps: 1- create a solver 2- assert the negation of the conjecture 3- checks if the result is unsat. */ void prove(expr conjecture) { context & c = conjecture.ctx(); solver s(c); s.add(!conjecture); std::cout << "conjecture:\n" << conjecture << "\n"; if (s.check() == unsat) { std::cout << "proved" << "\n"; } else { std::cout << "failed to prove" << "\n"; std::cout << "counterexample:\n" << s.get_model() << "\n"; } } /** \brief Simple bit-vector example. This example disproves that x - 10 <= 0 IFF x <= 10 for (32-bit) machine integers */ void bitvector_example1() { std::cout << "bitvector example 1\n"; context c; expr x = c.bv_const("x", 32); // using signed <= prove((x - 10 <= 0) == (x <= 10)); // using unsigned <= prove(ule(x - 10, 0) == ule(x, 10)); expr y = c.bv_const("y", 32); prove(implies(concat(x, y) == concat(y, x), x == y)); } /** \brief Find x and y such that: x ^ y - 103 == x * y */ void bitvector_example2() { std::cout << "bitvector example 2\n"; context c; expr x = c.bv_const("x", 32); expr y = c.bv_const("y", 32); solver s(c); // In C++, the operator == has higher precedence than ^. s.add((x ^ y) - 103 == x * y); std::cout << s << "\n"; std::cout << s.check() << "\n"; std::cout << s.get_model() << "\n"; } /** \brief Mixing C and C++ APIs. */ void capi_example() { std::cout << "capi example\n"; context c; expr x = c.bv_const("x", 32); expr y = c.bv_const("y", 32); // Invoking a C API function, and wrapping the result using an expr object. expr r = to_expr(c, Z3_mk_bvsrem(c, x, y)); std::cout << "r: " << r << "\n"; } /** \brief Demonstrate how to evaluate expressions in a model. */ void eval_example1() { std::cout << "eval example 1\n"; context c; expr x = c.int_const("x"); expr y = c.int_const("y"); solver s(c); /* assert x < y */ s.add(x < y); /* assert x > 2 */ s.add(x > 2); std::cout << s.check() << "\n"; model m = s.get_model(); std::cout << "Model:\n" << m << "\n"; std::cout << "x+y = " << m.eval(x+y) << "\n"; } /** \brief Several contexts can be used simultaneously. */ void two_contexts_example1() { std::cout << "two contexts example 1\n"; context c1, c2; expr x = c1.int_const("x"); expr n = x + 1; // We cannot mix expressions from different contexts, but we can copy // an expression from one context to another. // The following statement copies the expression n from c1 to c2. expr n1 = to_expr(c2, Z3_translate(c1, n, c2)); std::cout << n1 << "\n"; } /** \brief Demonstrates how to catch API usage errors. */ void error_example() { std::cout << "error example\n"; context c; expr x = c.bool_const("x"); // Error using the C API can be detected using Z3_get_error_code. // The next call fails because x is a constant. //Z3_ast arg = Z3_get_app_arg(c, x, 0); if (Z3_get_error_code(c) != Z3_OK) { std::cout << "last call failed.\n"; } // The C++ layer converts API usage errors into exceptions. try { // The next call fails because x is a Boolean. expr n = x + 1; } catch (exception ex) { std::cout << "failed: " << ex << "\n"; } // The functions to_expr, to_sort and to_func_decl also convert C API errors into exceptions. try { expr arg = to_expr(c, Z3_get_app_arg(c, x, 0)); } catch (exception ex) { std::cout << "failed: " << ex << "\n"; } } /** \brief Demonstrate different ways of creating rational numbers: decimal and fractional representations. */ void numeral_example() { std::cout << "numeral example\n"; context c; expr n1 = c.real_val("1/2"); expr n2 = c.real_val("0.5"); expr n3 = c.real_val(1, 2); std::cout << n1 << " " << n2 << " " << n3 << "\n"; prove(n1 == n2 && n1 == n3); n1 = c.real_val("-1/3"); n2 = c.real_val("-0.3333333333333333333333333333333333"); std::cout << n1 << " " << n2 << "\n"; prove(n1 != n2); } /** \brief Test ite-term (if-then-else terms). */ void ite_example() { std::cout << "if-then-else example\n"; context c; expr f = c.bool_val(false); expr one = c.int_val(1); expr zero = c.int_val(0); expr ite = to_expr(c, Z3_mk_ite(c, f, one, zero)); std::cout << "term: " << ite << "\n"; } void ite_example2() { std::cout << "if-then-else example2\n"; context c; expr b = c.bool_const("b"); expr x = c.int_const("x"); expr y = c.int_const("y"); std::cout << (ite(b, x, y) > 0) << "\n"; } /** \brief Small example using quantifiers. */ void quantifier_example() { std::cout << "quantifier example\n"; context c; expr x = c.int_const("x"); expr y = c.int_const("y"); sort I = c.int_sort(); func_decl f = function("f", I, I, I); solver s(c); // making sure model based quantifier instantiation is enabled. params p(c); p.set("mbqi", true); s.set(p); s.add(forall(x, y, f(x, y) >= 0)); expr a = c.int_const("a"); s.add(f(a, a) < a); std::cout << s << "\n"; std::cout << s.check() << "\n"; std::cout << s.get_model() << "\n"; s.add(a < 0); std::cout << s.check() << "\n"; } /** \brief Unsat core example */ void unsat_core_example1() { std::cout << "unsat core example1\n"; context c; // We use answer literals to track assertions. // An answer literal is essentially a fresh Boolean marker // that is used to track an assertion. // For example, if we want to track assertion F, we // create a fresh Boolean variable p and assert (p => F) // Then we provide p as an argument for the check method. expr p1 = c.bool_const("p1"); expr p2 = c.bool_const("p2"); expr p3 = c.bool_const("p3"); expr x = c.int_const("x"); expr y = c.int_const("y"); solver s(c); s.add(implies(p1, x > 10)); s.add(implies(p1, y > x)); s.add(implies(p2, y < 5)); s.add(implies(p3, y > 0)); expr assumptions[3] = { p1, p2, p3 }; std::cout << s.check(3, assumptions) << "\n"; expr_vector core = s.unsat_core(); std::cout << core << "\n"; std::cout << "size: " << core.size() << "\n"; for (unsigned i = 0; i < core.size(); i++) { std::cout << core[i] << "\n"; } // Trying again without p2 expr assumptions2[2] = { p1, p3 }; std::cout << s.check(2, assumptions2) << "\n"; } /** \brief Unsat core example 2 */ void unsat_core_example2() { std::cout << "unsat core example 2\n"; context c; // The answer literal mechanism, described in the previous example, // tracks assertions. An assertion can be a complicated // formula containing containing the conjunction of many subformulas. expr p1 = c.bool_const("p1"); expr x = c.int_const("x"); expr y = c.int_const("y"); solver s(c); expr F = x > 10 && y > x && y < 5 && y > 0; s.add(implies(p1, F)); expr assumptions[1] = { p1 }; std::cout << s.check(1, assumptions) << "\n"; expr_vector core = s.unsat_core(); std::cout << core << "\n"; std::cout << "size: " << core.size() << "\n"; for (unsigned i = 0; i < core.size(); i++) { std::cout << core[i] << "\n"; } // The core is not very informative, since p1 is tracking the formula F // that is a conjunction of subformulas. // Now, we use the following piece of code to break this conjunction // into individual subformulas. First, we flat the conjunctions by // using the method simplify. std::vector qs; // auxiliary vector used to store new answer literals. assert(F.is_app()); // I'm assuming F is an application. if (F.decl().decl_kind() == Z3_OP_AND) { // F is a conjunction std::cout << "F num. args (before simplify): " << F.num_args() << "\n"; F = F.simplify(); std::cout << "F num. args (after simplify): " << F.num_args() << "\n"; for (unsigned i = 0; i < F.num_args(); i++) { std::cout << "Creating answer literal q" << i << " for " << F.arg(i) << "\n"; std::stringstream qname; qname << "q" << i; expr qi = c.bool_const(qname.str().c_str()); // create a new answer literal s.add(implies(qi, F.arg(i))); qs.push_back(qi); } } // The solver s already contains p1 => F // To disable F, we add (not p1) as an additional assumption qs.push_back(!p1); std::cout << s.check(static_cast(qs.size()), &qs[0]) << "\n"; expr_vector core2 = s.unsat_core(); std::cout << core2 << "\n"; std::cout << "size: " << core2.size() << "\n"; for (unsigned i = 0; i < core2.size(); i++) { std::cout << core2[i] << "\n"; } } /** \brief Unsat core example 3 */ void unsat_core_example3() { // Extract unsat core using tracked assertions std::cout << "unsat core example 3\n"; context c; expr x = c.int_const("x"); expr y = c.int_const("y"); solver s(c); // enabling unsat core tracking params p(c); p.set("unsat_core", true); s.set(p); // The following assertion will not be tracked. s.add(x > 0); // The following assertion will be tracked using Boolean variable p1. // The C++ wrapper will automatically create the Boolean variable. s.add(y > 0, "p1"); // Asserting other tracked assertions. s.add(x < 10, "p2"); s.add(y < 0, "p3"); std::cout << s.check() << "\n"; std::cout << s.unsat_core() << "\n"; } void tactic_example1() { /* Z3 implements a methodology for orchestrating reasoning engines where "big" symbolic reasoning steps are represented as functions known as tactics, and tactics are composed using combinators known as tacticals. Tactics process sets of formulas called Goals. When a tactic is applied to some goal G, four different outcomes are possible. The tactic succeeds in showing G to be satisfiable (i.e., feasible); succeeds in showing G to be unsatisfiable (i.e., infeasible); produces a sequence of subgoals; or fails. When reducing a goal G to a sequence of subgoals G1, ..., Gn, we face the problem of model conversion. A model converter construct a model for G using a model for some subgoal Gi. In this example, we create a goal g consisting of three formulas, and a tactic t composed of two built-in tactics: simplify and solve-eqs. The tactic simplify apply transformations equivalent to the ones found in the command simplify. The tactic solver-eqs eliminate variables using Gaussian elimination. Actually, solve-eqs is not restricted only to linear arithmetic. It can also eliminate arbitrary variables. Then, sequential composition combinator & applies simplify to the input goal and solve-eqs to each subgoal produced by simplify. In this example, only one subgoal is produced. */ std::cout << "tactic example 1\n"; context c; expr x = c.real_const("x"); expr y = c.real_const("y"); goal g(c); g.add(x > 0); g.add(y > 0); g.add(x == y + 2); std::cout << g << "\n"; tactic t1(c, "simplify"); tactic t2(c, "solve-eqs"); tactic t = t1 & t2; apply_result r = t(g); std::cout << r << "\n"; } void tactic_example2() { /* In Z3, we say a clause is any constraint of the form (f_1 || ... || f_n). The tactic split-clause will select a clause in the input goal, and split it n subgoals. One for each subformula f_i. */ std::cout << "tactic example 2\n"; context c; expr x = c.real_const("x"); expr y = c.real_const("y"); goal g(c); g.add(x < 0 || x > 0); g.add(x == y + 1); g.add(y < 0); tactic t(c, "split-clause"); apply_result r = t(g); for (unsigned i = 0; i < r.size(); i++) { std::cout << "subgoal " << i << "\n" << r[i] << "\n"; } } void tactic_example3() { /* - The choice combinator t | s first applies t to the given goal, if it fails then returns the result of s applied to the given goal. - repeat(t) Keep applying the given tactic until no subgoal is modified by it. - repeat(t, n) Keep applying the given tactic until no subgoal is modified by it, or the number of iterations is greater than n. - try_for(t, ms) Apply tactic t to the input goal, if it does not return in ms milliseconds, it fails. - with(t, params) Apply the given tactic using the given parameters. */ std::cout << "tactic example 3\n"; context c; expr x = c.real_const("x"); expr y = c.real_const("y"); expr z = c.real_const("z"); goal g(c); g.add(x == 0 || x == 1); g.add(y == 0 || y == 1); g.add(z == 0 || z == 1); g.add(x + y + z > 2); // split all clauses tactic split_all = repeat(tactic(c, "split-clause") | tactic(c, "skip")); std::cout << split_all(g) << "\n"; tactic split_at_most_2 = repeat(tactic(c, "split-clause") | tactic(c, "skip"), 1); std::cout << split_at_most_2(g) << "\n"; // In the tactic split_solver, the tactic solve-eqs discharges all but one goal. // Note that, this tactic generates one goal: the empty goal which is trivially satisfiable (i.e., feasible) tactic split_solve = split_all & tactic(c, "solve-eqs"); std::cout << split_solve(g) << "\n"; } void tactic_example4() { /* A tactic can be converted into a solver object using the method mk_solver(). If the tactic produces the empty goal, then the associated solver returns sat. If the tactic produces a single goal containing False, then the solver returns unsat. Otherwise, it returns unknown. In this example, the tactic t implements a basic bit-vector solver using equation solving, bit-blasting, and a propositional SAT solver. We use the combinator `with` to configure our little solver. We also include the tactic `aig` which tries to compress Boolean formulas using And-Inverted Graphs. */ std::cout << "tactic example 4\n"; context c; params p(c); p.set("mul2concat", true); tactic t = with(tactic(c, "simplify"), p) & tactic(c, "solve-eqs") & tactic(c, "bit-blast") & tactic(c, "aig") & tactic(c, "sat"); solver s = t.mk_solver(); expr x = c.bv_const("x", 16); expr y = c.bv_const("y", 16); s.add(x*32 + y == 13); // In C++, the operator < has higher precedence than &. s.add((x & y) < 10); s.add(y > -100); std::cout << s.check() << "\n"; model m = s.get_model(); std::cout << m << "\n"; std::cout << "x*32 + y = " << m.eval(x*32 + y) << "\n"; std::cout << "x & y = " << m.eval(x & y) << "\n"; } void tactic_example5() { /* The tactic smt wraps the main solver in Z3 as a tactic. */ std::cout << "tactic example 5\n"; context c; expr x = c.int_const("x"); expr y = c.int_const("y"); solver s = tactic(c, "smt").mk_solver(); s.add(x > y + 1); std::cout << s.check() << "\n"; std::cout << s.get_model() << "\n"; } void tactic_example6() { /* In this example, we show how to implement a solver for integer arithmetic using SAT. The solver is complete only for problems where every variable has a lower and upper bound. */ std::cout << "tactic example 6\n"; context c; params p(c); p.set("arith_lhs", true); p.set("som", true); // sum-of-monomials normal form solver s = (with(tactic(c, "simplify"), p) & tactic(c, "normalize-bounds") & tactic(c, "lia2pb") & tactic(c, "pb2bv") & tactic(c, "bit-blast") & tactic(c, "sat")).mk_solver(); expr x = c.int_const("x"); expr y = c.int_const("y"); expr z = c.int_const("z"); s.add(x > 0 && x < 10); s.add(y > 0 && y < 10); s.add(z > 0 && z < 10); s.add(3*y + 2*x == z); std::cout << s.check() << "\n"; std::cout << s.get_model() << "\n"; s.reset(); s.add(3*y + 2*x == z); std::cout << s.check() << "\n"; } void tactic_example7() { /* Tactics can be combined with solvers. For example, we can apply a tactic to a goal, produced a set of subgoals, then select one of the subgoals and solve it using a solver. This example demonstrates how to do that, and how to use model converters to convert a model for a subgoal into a model for the original goal. */ std::cout << "tactic example 7\n"; context c; tactic t = tactic(c, "simplify") & tactic(c, "normalize-bounds") & tactic(c, "solve-eqs"); expr x = c.int_const("x"); expr y = c.int_const("y"); expr z = c.int_const("z"); goal g(c); g.add(x > 10); g.add(y == x + 3); g.add(z > y); apply_result r = t(g); // r contains only one subgoal std::cout << r << "\n"; solver s(c); goal subgoal = r[0]; for (unsigned i = 0; i < subgoal.size(); i++) { s.add(subgoal[i]); } std::cout << s.check() << "\n"; model m = s.get_model(); std::cout << "model for subgoal:\n" << m << "\n"; std::cout << "model for original goal:\n" << subgoal.convert_model(m) << "\n"; } void tactic_example8() { /* Probes (aka formula measures) are evaluated over goals. Boolean expressions over them can be built using relational operators and Boolean connectives. The tactic fail_if(cond) fails if the given goal does not satisfy the condition cond. Many numeric and Boolean measures are available in Z3. In this example, we build a simple tactic using fail_if. It also shows that a probe can be applied directly to a goal. */ std::cout << "tactic example 8\n"; context c; expr x = c.int_const("x"); expr y = c.int_const("y"); expr z = c.int_const("z"); goal g(c); g.add(x + y + z > 0); probe p(c, "num-consts"); std::cout << "num-consts: " << p(g) << "\n"; tactic t = fail_if(p > 2); try { t(g); } catch (exception) { std::cout << "tactic failed...\n"; } std::cout << "trying again...\n"; g.reset(); g.add(x + y > 0); std::cout << t(g) << "\n"; } void tactic_example9() { /* The combinator (tactical) cond(p, t1, t2) is a shorthand for: (fail_if(p) & t1) | t2 The combinator when(p, t) is a shorthand for: cond(p, t, tactic(c, "skip")) The tactic skip just returns the input goal. This example demonstrates how to use the cond combinator. */ std::cout << "tactic example 9\n"; context c; expr x = c.int_const("x"); expr y = c.int_const("y"); expr z = c.int_const("z"); goal g(c); g.add(x*x - y*y >= 0); probe p(c, "num-consts"); tactic t = cond(p > 2, tactic(c, "simplify"), tactic(c, "factor")); std::cout << t(g) << "\n"; g.reset(); g.add(x + x + y + z >= 0); g.add(x*x - y*y >= 0); std::cout << t(g) << "\n"; } void tactic_qe() { std::cout << "tactic example using quantifier elimination\n"; context c; // Create a solver using "qe" and "smt" tactics solver s = (tactic(c, "qe") & tactic(c, "smt")).mk_solver(); expr a = c.int_const("a"); expr b = c.int_const("b"); expr x = c.int_const("x"); expr f = implies(x <= a, x < b); expr qf = forall(x, f); std::cout << qf << "\n"; s.add(qf); std::cout << s.check() << "\n"; std::cout << s.get_model() << "\n"; } void visit(std::vector& visited, expr const & e) { if (visited.size() <= e.id()) { visited.resize(e.id()+1, false); } if (visited[e.id()]) { return; } visited[e.id()] = true; if (e.is_app()) { unsigned num = e.num_args(); for (unsigned i = 0; i < num; i++) { visit(visited, e.arg(i)); } // do something // Example: print the visited expression func_decl f = e.decl(); std::cout << "application of " << f.name() << ": " << e << "\n"; } else if (e.is_quantifier()) { visit(visited, e.body()); // do something } else { assert(e.is_var()); // do something } } void tst_visit() { std::cout << "visit example\n"; context c; // only one of the occurrences of x*x is visited // because they are the same subterms expr x = c.int_const("x"); expr y = c.int_const("y"); expr z = c.int_const("z"); expr f = x*x + x*x - y*y >= 0; std::vector visited; visit(visited, f); } void tst_numeral() { std::cout << "numeral example\n"; context c; expr x = c.real_val("1/3"); double d = 0; if (!x.is_numeral(d)) { std::cout << x << " is not recognized as a numeral\n"; return; } std::cout << x << " is " << d << "\n"; } void incremental_example1() { std::cout << "incremental example1\n"; context c; expr x = c.int_const("x"); solver s(c); s.add(x > 0); std::cout << s.check() << "\n"; // We can add more formulas to the solver s.add(x < 0); // and, invoke s.check() again... std::cout << s.check() << "\n"; } void incremental_example2() { // In this example, we show how push() and pop() can be used // to remove formulas added to the solver. std::cout << "incremental example2\n"; context c; expr x = c.int_const("x"); solver s(c); s.add(x > 0); std::cout << s.check() << "\n"; // push() creates a backtracking point (aka a snapshot). s.push(); // We can add more formulas to the solver s.add(x < 0); // and, invoke s.check() again... std::cout << s.check() << "\n"; // pop() will remove all formulas added between this pop() and the matching push() s.pop(); // The context is satisfiable again std::cout << s.check() << "\n"; // and contains only x > 0 std::cout << s << "\n"; } void incremental_example3() { // In this example, we show how to use assumptions to "remove" // formulas added to a solver. Actually, we disable them. std::cout << "incremental example3\n"; context c; expr x = c.int_const("x"); solver s(c); s.add(x > 0); std::cout << s.check() << "\n"; // Now, suppose we want to add x < 0 to the solver, but we also want // to be able to disable it later. // To do that, we create an auxiliary Boolean variable expr b = c.bool_const("b"); // and, assert (b implies x < 0) s.add(implies(b, x < 0)); // Now, we check whether s is satisfiable under the assumption "b" is true. expr_vector a1(c); a1.push_back(b); std::cout << s.check(a1) << "\n"; // To "disable" (x > 0), we may just ask with the assumption "not b" or not provide any assumption. std::cout << s.check() << "\n"; expr_vector a2(c); a2.push_back(!b); std::cout << s.check(a2) << "\n"; } void enum_sort_example() { std::cout << "enumeration sort example\n"; context ctx; const char * enum_names[] = { "a", "b", "c" }; func_decl_vector enum_consts(ctx); func_decl_vector enum_testers(ctx); sort s = ctx.enumeration_sort("enumT", 3, enum_names, enum_consts, enum_testers); // enum_consts[0] is a func_decl of arity 0. // we convert it to an expression using the operator() expr a = enum_consts[0](); expr b = enum_consts[1](); expr x = ctx.constant("x", s); expr test = (x==a) && (x==b); std::cout << "1: " << test << std::endl; tactic qe(ctx, "ctx-solver-simplify"); goal g(ctx); g.add(test); expr res(ctx); apply_result result_of_elimination = qe.apply(g); goal result_goal = result_of_elimination[0]; std::cout << "2: " << result_goal.as_expr() << std::endl; } void tuple_example() { std::cout << "tuple example\n"; context ctx; const char * names[] = { "first", "second" }; sort sorts[2] = { ctx.int_sort(), ctx.bool_sort() }; func_decl_vector projs(ctx); func_decl pair = ctx.tuple_sort("pair", 2, names, sorts, projs); sorts[1] = pair.range(); func_decl pair2 = ctx.tuple_sort("pair2", 2, names, sorts, projs); std::cout << pair2 << "\n"; } void expr_vector_example() { std::cout << "expr_vector example\n"; context c; const unsigned N = 10; expr_vector x(c); for (unsigned i = 0; i < N; i++) { std::stringstream x_name; x_name << "x_" << i; x.push_back(c.int_const(x_name.str().c_str())); } solver s(c); for (unsigned i = 0; i < N; i++) { s.add(x[i] >= 1); } std::cout << s << "\n" << "solving...\n" << s.check() << "\n"; model m = s.get_model(); std::cout << "solution\n" << m; } void exists_expr_vector_example() { std::cout << "exists expr_vector example\n"; context c; const unsigned N = 10; expr_vector xs(c); expr x(c); expr b(c); b = c.bool_val(true); for (unsigned i = 0; i < N; i++) { std::stringstream x_name; x_name << "x_" << i; x = c.int_const(x_name.str().c_str()); xs.push_back(x); b = b && x >= 0; } expr ex(c); ex = exists(xs, b); std::cout << ex << std::endl; } void substitute_example() { std::cout << "substitute example\n"; context c; expr x(c); x = c.int_const("x"); expr f(c); f = (x == 2) || (x == 1); std::cout << f << std::endl; expr two(c), three(c); two = c.int_val(2); three = c.int_val(3); Z3_ast from[] = { two }; Z3_ast to[] = { three }; expr new_f(c); new_f = to_expr(c, Z3_substitute(c, f, 1, from, to)); std::cout << new_f << std::endl; } void opt_example() { context c; optimize opt(c); params p(c); p.set("priority",c.str_symbol("pareto")); opt.set(p); expr x = c.int_const("x"); expr y = c.int_const("y"); opt.add(10 >= x && x >= 0); opt.add(10 >= y && y >= 0); opt.add(x + y <= 11); optimize::handle h1 = opt.maximize(x); optimize::handle h2 = opt.maximize(y); while (true) { if (sat == opt.check()) { std::cout << x << ": " << opt.lower(h1) << " " << y << ": " << opt.lower(h2) << "\n"; } else { break; } } } void extract_example() { std::cout << "extract example\n"; context c; expr x(c); x = c.constant("x", c.bv_sort(32)); expr y = x.extract(21, 10); std::cout << y << " " << y.hi() << " " << y.lo() << "\n"; } void sudoku_example() { std::cout << "sudoku example\n"; context c; // 9x9 matrix of integer variables expr_vector x(c); for (unsigned i = 0; i < 9; ++i) for (unsigned j = 0; j < 9; ++j) { std::stringstream x_name; x_name << "x_" << i << '_' << j; x.push_back(c.int_const(x_name.str().c_str())); } solver s(c); // each cell contains a value in {1, ..., 9} for (unsigned i = 0; i < 9; ++i) for (unsigned j = 0; j < 9; ++j) { s.add(x[i * 9 + j] >= 1 && x[i * 9 + j] <= 9); } // each row contains a digit at most once for (unsigned i = 0; i < 9; ++i) { expr_vector t(c); for (unsigned j = 0; j < 9; ++j) t.push_back(x[i * 9 + j]); s.add(distinct(t)); } // each column contains a digit at most once for (unsigned j = 0; j < 9; ++j) { expr_vector t(c); for (unsigned i = 0; i < 9; ++i) t.push_back(x[i * 9 + j]); s.add(distinct(t)); } // each 3x3 square contains a digit at most once for (unsigned i0 = 0; i0 < 3; i0++) { for (unsigned j0 = 0; j0 < 3; j0++) { expr_vector t(c); for (unsigned i = 0; i < 3; i++) for (unsigned j = 0; j < 3; j++) t.push_back(x[(i0 * 3 + i) * 9 + j0 * 3 + j]); s.add(distinct(t)); } } // sudoku instance, we use '0' for empty cells int instance[9][9] = {{0,0,0,0,9,4,0,3,0}, {0,0,0,5,1,0,0,0,7}, {0,8,9,0,0,0,0,4,0}, {0,0,0,0,0,0,2,0,8}, {0,6,0,2,0,1,0,5,0}, {1,0,2,0,0,0,0,0,0}, {0,7,0,0,0,0,5,2,0}, {9,0,0,0,6,5,0,0,0}, {0,4,0,9,7,0,0,0,0}}; for (unsigned i = 0; i < 9; i++) for (unsigned j = 0; j < 9; j++) if (instance[i][j] != 0) s.add(x[i * 9 + j] == instance[i][j]); std::cout << s.check() << std::endl; std::cout << s << std::endl; model m = s.get_model(); for (unsigned i = 0; i < 9; ++i) { for (unsigned j = 0; j < 9; ++j) std::cout << m.eval(x[i * 9 + j]); std::cout << '\n'; } } void param_descrs_example() { std::cout << "parameter description example\n"; context c; param_descrs p = param_descrs::simplify_param_descrs(c); std::cout << p << "\n"; unsigned sz = p.size(); for (unsigned i = 0; i < sz; ++i) { symbol nm = p.name(i); char const* kind = "other"; Z3_param_kind k = p.kind(nm); if (k == Z3_PK_UINT) kind = "uint"; if (k == Z3_PK_BOOL) kind = "bool"; std::cout << nm << ": " << p.documentation(nm) << " kind: " << kind << "\n"; } } void consequence_example() { std::cout << "consequence example\n"; context c; expr A = c.bool_const("a"); expr B = c.bool_const("b"); expr C = c.bool_const("c"); solver s(c); s.add(implies(A, B)); s.add(implies(B, C)); expr_vector assumptions(c), vars(c), consequences(c); assumptions.push_back(!C); vars.push_back(A); vars.push_back(B); vars.push_back(C); std::cout << s.consequences(assumptions, vars, consequences) << "\n"; std::cout << consequences << "\n"; } static void parse_example() { std::cout << "parse example\n"; context c; sort_vector sorts(c); func_decl_vector decls(c); sort B = c.bool_sort(); decls.push_back(c.function("a", 0, 0, B)); expr_vector a = c.parse_string("(assert a)", sorts, decls); std::cout << a << "\n"; // 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; // construct empty model model m(c); // create constants "a", "b" and get their func_decl expr a = c.int_const("a"); expr b = c.int_const("b"); func_decl a_decl = a.decl(); func_decl b_decl = b.decl(); // create numerals to be used in model expr zero_numeral = c.int_val(0); expr one_numeral = c.int_val(1); // add assignment to model m.add_const_interp(a_decl, zero_numeral); m.add_const_interp(b_decl, one_numeral); // evaluate a + b < 2 in the model std::cout << m.eval(a + b < 2)<< std::endl; } void recfun_example() { std::cout << "recfun example\n"; context c; expr x = c.int_const("x"); expr y = c.int_const("y"); expr b = c.bool_const("b"); sort I = c.int_sort(); sort B = c.bool_sort(); func_decl f = recfun("f", I, B, I); expr_vector args(c); args.push_back(x); args.push_back(b); c.recdef(f, args, ite(b, x, f(x + 1, !b))); prove(f(x,c.bool_val(false)) > x); } static void string_values() { context c; expr s = c.string_val("abc\n\n\0\0", 7); std::cout << s << "\n"; std::string s1 = s.get_string(); std::cout << s1 << "\n"; } expr MakeStringConstant(context* context, std::string value) { return context->string_val(value.c_str()); } expr MakeStringFunction(context* c, std::string s) { sort sort = c->string_sort(); symbol name = c->str_symbol(s.c_str()); return c->constant(name, sort); } static void string_issue_2298() { context c; solver s(c); s.push(); expr func1 = MakeStringFunction(&c, "func1"); expr func2 = MakeStringFunction(&c, "func2"); expr abc = MakeStringConstant(&c, "abc"); expr cde = MakeStringConstant(&c, "cde"); expr length = func1.length(); expr five = c.int_val(5); s.add(func2 == abc || func1 == cde); s.add(func2 == abc || func2 == cde); s.add(length <= five); s.check(); s.pop(); } int main() { try { demorgan(); std::cout << "\n"; find_model_example1(); std::cout << "\n"; prove_example1(); std::cout << "\n"; prove_example2(); std::cout << "\n"; nonlinear_example1(); std::cout << "\n"; bitvector_example1(); std::cout << "\n"; bitvector_example2(); std::cout << "\n"; capi_example(); std::cout << "\n"; eval_example1(); std::cout << "\n"; two_contexts_example1(); std::cout << "\n"; error_example(); std::cout << "\n"; numeral_example(); std::cout << "\n"; ite_example(); std::cout << "\n"; ite_example2(); std::cout << "\n"; quantifier_example(); std::cout << "\n"; unsat_core_example1(); std::cout << "\n"; unsat_core_example2(); std::cout << "\n"; unsat_core_example3(); std::cout << "\n"; tactic_example1(); std::cout << "\n"; tactic_example2(); std::cout << "\n"; tactic_example3(); std::cout << "\n"; tactic_example4(); std::cout << "\n"; tactic_example5(); std::cout << "\n"; tactic_example6(); std::cout << "\n"; tactic_example7(); std::cout << "\n"; tactic_example8(); std::cout << "\n"; tactic_example9(); std::cout << "\n"; tactic_qe(); std::cout << "\n"; tst_visit(); std::cout << "\n"; tst_numeral(); std::cout << "\n"; incremental_example1(); std::cout << "\n"; incremental_example2(); std::cout << "\n"; incremental_example3(); std::cout << "\n"; enum_sort_example(); std::cout << "\n"; tuple_example(); std::cout << "\n"; expr_vector_example(); std::cout << "\n"; exists_expr_vector_example(); std::cout << "\n"; substitute_example(); std::cout << "\n"; opt_example(); std::cout << "\n"; extract_example(); std::cout << "\n"; param_descrs_example(); std::cout << "\n"; 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"; string_values(); std::cout << "\n"; string_issue_2298(); std::cout << "\n"; std::cout << "done\n"; } catch (exception & ex) { std::cout << "unexpected error: " << ex << "\n"; } return 0; } z3-z3-4.8.7/examples/c/000077500000000000000000000000001356505360400144675ustar00rootroot00000000000000z3-z3-4.8.7/examples/c/CMakeLists.txt000066400000000000000000000037351356505360400172370ustar00rootroot00000000000000################################################################################ # Example C project ################################################################################ # NOTE: Even though this is a C project, libz3 uses C++. When using libz3 # as a static library if we don't configure this project to also support # C++ we will use the C linker rather than the C++ linker and will not link # the C++ standard library in resulting in a link failure. project(Z3_C_EXAMPLE C CXX) cmake_minimum_required(VERSION 3.4) set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_C_STANDARD 99) set(CMAKE_C_EXTENSIONS OFF) find_package(Z3 REQUIRED CONFIG # `NO_DEFAULT_PATH` is set so that -DZ3_DIR has to be passed to find Z3. # This should prevent us from accidentally picking up an installed # copy of Z3. This is here to benefit Z3's build system when building # this project. When making your own project you probably shouldn't # use this option. NO_DEFAULT_PATH ) message(STATUS "Z3_FOUND: ${Z3_FOUND}") message(STATUS "Found Z3 ${Z3_VERSION_STRING}") message(STATUS "Z3_DIR: ${Z3_DIR}") add_executable(c_example test_capi.c) option(FORCE_CXX_LINKER "Force linker with C++ linker" OFF) if (FORCE_CXX_LINKER) # This is a hack for avoiding UBSan linking errors message(STATUS "Forcing use of C++ linker") set_target_properties(c_example PROPERTIES LINKER_LANGUAGE CXX ) endif() target_include_directories(c_example PRIVATE ${Z3_C_INCLUDE_DIRS}) target_link_libraries(c_example PRIVATE ${Z3_LIBRARIES}) if ("${CMAKE_SYSTEM_NAME}" MATCHES "[Ww]indows") # On Windows we need to copy the Z3 libraries # into the same directory as the executable # so that they can be found. foreach (z3_lib ${Z3_LIBRARIES}) message(STATUS "Adding copy rule for ${z3_lib}") add_custom_command(TARGET c_example POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different $ $ ) endforeach() endif() z3-z3-4.8.7/examples/c/README000066400000000000000000000006361356505360400153540ustar00rootroot00000000000000Small example using the C bindings. To build the example execute make examples in the build directory. This command will create the executable c_example. On Windows, you can just execute it. On macOS and Linux, you must install z3 first using sudo make install OR update LD_LIBRARY_PATH (Linux) or DYLD_LIBRARY_PATH (macOS) with the build directory. You need that to be able to find the Z3 shared library. z3-z3-4.8.7/examples/c/test_capi.c000066400000000000000000002650441356505360400166210ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include #include #include #include #include #include #define LOG_Z3_CALLS #ifdef LOG_Z3_CALLS #define LOG_MSG(msg) Z3_append_log(msg) #else #define LOG_MSG(msg) ((void)0) #endif /** \defgroup capi_ex C API examples */ /*@{*/ /** @name Auxiliary Functions */ /*@{*/ /** \brief exit gracefully in case of error. */ void exitf(const char* message) { fprintf(stderr,"BUG: %s.\n", message); exit(1); } /** \brief exit if unreachable code was reached. */ void unreachable() { exitf("unreachable code was reached"); } /** \brief Simpler error handler. */ void error_handler(Z3_context c, Z3_error_code e) { printf("Error code: %d\n", e); exitf("incorrect use of Z3"); } static jmp_buf g_catch_buffer; /** \brief Low tech exceptions. In high-level programming languages, an error handler can throw an exception. */ void throw_z3_error(Z3_context c, Z3_error_code e) { longjmp(g_catch_buffer, e); } /** \brief Error handling that depends on checking an error code on the context. */ void nothrow_z3_error(Z3_context c, Z3_error_code e) { // no-op } /** \brief Create a logical context. Enable model construction. Other configuration parameters can be passed in the cfg variable. Also enable tracing to stderr and register custom error handler. */ Z3_context mk_context_custom(Z3_config cfg, Z3_error_handler err) { Z3_context ctx; Z3_set_param_value(cfg, "model", "true"); ctx = Z3_mk_context(cfg); Z3_set_error_handler(ctx, err); return ctx; } Z3_solver mk_solver(Z3_context ctx) { Z3_solver s = Z3_mk_solver(ctx); Z3_solver_inc_ref(ctx, s); return s; } void del_solver(Z3_context ctx, Z3_solver s) { Z3_solver_dec_ref(ctx, s); } /** \brief Create a logical context. Enable model construction only. Also enable tracing to stderr and register standard error handler. */ Z3_context mk_context() { Z3_config cfg; Z3_context ctx; cfg = Z3_mk_config(); ctx = mk_context_custom(cfg, error_handler); Z3_del_config(cfg); return ctx; } /** \brief Create a logical context. Enable fine-grained proof construction. Enable model construction. Also enable tracing to stderr and register standard error handler. */ Z3_context mk_proof_context() { Z3_config cfg = Z3_mk_config(); Z3_context ctx; Z3_set_param_value(cfg, "proof", "true"); ctx = mk_context_custom(cfg, throw_z3_error); Z3_del_config(cfg); return ctx; } /** \brief Create a variable using the given name and type. */ Z3_ast mk_var(Z3_context ctx, const char * name, Z3_sort ty) { Z3_symbol s = Z3_mk_string_symbol(ctx, name); return Z3_mk_const(ctx, s, ty); } /** \brief Create a boolean variable using the given name. */ Z3_ast mk_bool_var(Z3_context ctx, const char * name) { Z3_sort ty = Z3_mk_bool_sort(ctx); return mk_var(ctx, name, ty); } /** \brief Create an integer variable using the given name. */ Z3_ast mk_int_var(Z3_context ctx, const char * name) { Z3_sort ty = Z3_mk_int_sort(ctx); return mk_var(ctx, name, ty); } /** \brief Create a Z3 integer node using a C int. */ Z3_ast mk_int(Z3_context ctx, int v) { Z3_sort ty = Z3_mk_int_sort(ctx); return Z3_mk_int(ctx, v, ty); } /** \brief Create a real variable using the given name. */ Z3_ast mk_real_var(Z3_context ctx, const char * name) { Z3_sort ty = Z3_mk_real_sort(ctx); return mk_var(ctx, name, ty); } /** \brief Create the unary function application: (f x). */ Z3_ast mk_unary_app(Z3_context ctx, Z3_func_decl f, Z3_ast x) { Z3_ast args[1] = {x}; return Z3_mk_app(ctx, f, 1, args); } /** \brief Create the binary function application: (f x y). */ Z3_ast mk_binary_app(Z3_context ctx, Z3_func_decl f, Z3_ast x, Z3_ast y) { Z3_ast args[2] = {x, y}; return Z3_mk_app(ctx, f, 2, args); } /** \brief Check whether the logical context is satisfiable, and compare the result with the expected result. If the context is satisfiable, then display the model. */ void check(Z3_context ctx, Z3_solver s, Z3_lbool expected_result) { Z3_model m = 0; Z3_lbool result = Z3_solver_check(ctx, s); switch (result) { case Z3_L_FALSE: printf("unsat\n"); break; case Z3_L_UNDEF: printf("unknown\n"); m = Z3_solver_get_model(ctx, s); if (m) Z3_model_inc_ref(ctx, m); printf("potential model:\n%s\n", Z3_model_to_string(ctx, m)); break; case Z3_L_TRUE: m = Z3_solver_get_model(ctx, s); if (m) Z3_model_inc_ref(ctx, m); printf("sat\n%s\n", Z3_model_to_string(ctx, m)); break; } if (result != expected_result) { exitf("unexpected result"); } if (m) Z3_model_dec_ref(ctx, m); } /** \brief Prove that the constraints already asserted into the logical context implies the given formula. The result of the proof is displayed. Z3 is a satisfiability checker. So, one can prove \c f by showing that (not f) is unsatisfiable. The context \c ctx is not modified by this function. */ void prove(Z3_context ctx, Z3_solver s, Z3_ast f, bool is_valid) { Z3_model m = 0; Z3_ast not_f; /* save the current state of the context */ Z3_solver_push(ctx, s); not_f = Z3_mk_not(ctx, f); Z3_solver_assert(ctx, s, not_f); switch (Z3_solver_check(ctx, s)) { case Z3_L_FALSE: /* proved */ printf("valid\n"); if (!is_valid) { exitf("unexpected result"); } break; case Z3_L_UNDEF: /* Z3 failed to prove/disprove f. */ printf("unknown\n"); m = Z3_solver_get_model(ctx, s); if (m != 0) { Z3_model_inc_ref(ctx, m); /* m should be viewed as a potential counterexample. */ printf("potential counterexample:\n%s\n", Z3_model_to_string(ctx, m)); } if (is_valid) { exitf("unexpected result"); } break; case Z3_L_TRUE: /* disproved */ printf("invalid\n"); m = Z3_solver_get_model(ctx, s); if (m) { Z3_model_inc_ref(ctx, m); /* the model returned by Z3 is a counterexample */ printf("counterexample:\n%s\n", Z3_model_to_string(ctx, m)); } if (is_valid) { exitf("unexpected result"); } break; } if (m) Z3_model_dec_ref(ctx, m); /* restore scope */ Z3_solver_pop(ctx, s, 1); } /** \brief Assert the axiom: function f is injective in the i-th argument. The following axiom is asserted into the logical context: \code forall (x_0, ..., x_n) finv(f(x_0, ..., x_i, ..., x_{n-1})) = x_i \endcode Where, \c finv is a fresh function declaration. */ void assert_inj_axiom(Z3_context ctx, Z3_solver s, Z3_func_decl f, unsigned i) { unsigned sz, j; Z3_sort finv_domain, finv_range; Z3_func_decl finv; Z3_sort * types; /* types of the quantified variables */ Z3_symbol * names; /* names of the quantified variables */ Z3_ast * xs; /* arguments for the application f(x_0, ..., x_i, ..., x_{n-1}) */ Z3_ast x_i, fxs, finv_fxs, eq; Z3_pattern p; Z3_ast q; sz = Z3_get_domain_size(ctx, f); if (i >= sz) { exitf("failed to create inj axiom"); } /* declare the i-th inverse of f: finv */ finv_domain = Z3_get_range(ctx, f); finv_range = Z3_get_domain(ctx, f, i); finv = Z3_mk_fresh_func_decl(ctx, "inv", 1, &finv_domain, finv_range); /* allocate temporary arrays */ types = (Z3_sort *) malloc(sizeof(Z3_sort) * sz); names = (Z3_symbol *) malloc(sizeof(Z3_symbol) * sz); xs = (Z3_ast *) malloc(sizeof(Z3_ast) * sz); /* fill types, names and xs */ for (j = 0; j < sz; j++) { types[j] = Z3_get_domain(ctx, f, j); }; for (j = 0; j < sz; j++) { names[j] = Z3_mk_int_symbol(ctx, j); }; for (j = 0; j < sz; j++) { xs[j] = Z3_mk_bound(ctx, j, types[j]); }; x_i = xs[i]; /* create f(x_0, ..., x_i, ..., x_{n-1}) */ fxs = Z3_mk_app(ctx, f, sz, xs); /* create f_inv(f(x_0, ..., x_i, ..., x_{n-1})) */ finv_fxs = mk_unary_app(ctx, finv, fxs); /* create finv(f(x_0, ..., x_i, ..., x_{n-1})) = x_i */ eq = Z3_mk_eq(ctx, finv_fxs, x_i); /* use f(x_0, ..., x_i, ..., x_{n-1}) as the pattern for the quantifier */ p = Z3_mk_pattern(ctx, 1, &fxs); printf("pattern: %s\n", Z3_pattern_to_string(ctx, p)); printf("\n"); /* create & assert quantifier */ q = Z3_mk_forall(ctx, 0, /* using default weight */ 1, /* number of patterns */ &p, /* address of the "array" of patterns */ sz, /* number of quantified variables */ types, names, eq); printf("assert axiom:\n%s\n", Z3_ast_to_string(ctx, q)); Z3_solver_assert(ctx, s, q); /* free temporary arrays */ free(types); free(names); free(xs); } /** \brief Assert the axiom: function f is commutative. This example uses the SMT-LIB parser to simplify the axiom construction. */ void assert_comm_axiom(Z3_context ctx, Z3_solver s, Z3_func_decl f) { Z3_sort t; Z3_symbol f_name, t_name; Z3_ast_vector q; unsigned i; t = Z3_get_range(ctx, f); if (Z3_get_domain_size(ctx, f) != 2 || Z3_get_domain(ctx, f, 0) != t || Z3_get_domain(ctx, f, 1) != t) { exitf("function must be binary, and argument types must be equal to return type"); } /* Inside the parser, function f will be referenced using the symbol 'f'. */ f_name = Z3_mk_string_symbol(ctx, "f"); /* Inside the parser, type t will be referenced using the symbol 'T'. */ t_name = Z3_mk_string_symbol(ctx, "T"); q = Z3_parse_smtlib2_string(ctx, "(assert (forall ((x T) (y T)) (= (f x y) (f y x))))", 1, &t_name, &t, 1, &f_name, &f); printf("assert axiom:\n%s\n", Z3_ast_vector_to_string(ctx, q)); for (i = 0; i < Z3_ast_vector_size(ctx, q); ++i) { Z3_solver_assert(ctx, s, Z3_ast_vector_get(ctx, q, i)); } } /** \brief Z3 does not support explicitly tuple updates. They can be easily implemented as macros. The argument \c t must have tuple type. A tuple update is a new tuple where field \c i has value \c new_val, and all other fields have the value of the respective field of \c t. update(t, i, new_val) is equivalent to mk_tuple(proj_0(t), ..., new_val, ..., proj_n(t)) */ Z3_ast mk_tuple_update(Z3_context c, Z3_ast t, unsigned i, Z3_ast new_val) { Z3_sort ty; Z3_func_decl mk_tuple_decl; unsigned num_fields, j; Z3_ast * new_fields; Z3_ast result; ty = Z3_get_sort(c, t); if (Z3_get_sort_kind(c, ty) != Z3_DATATYPE_SORT) { exitf("argument must be a tuple"); } num_fields = Z3_get_tuple_sort_num_fields(c, ty); if (i >= num_fields) { exitf("invalid tuple update, index is too big"); } new_fields = (Z3_ast*) malloc(sizeof(Z3_ast) * num_fields); for (j = 0; j < num_fields; j++) { if (i == j) { /* use new_val at position i */ new_fields[j] = new_val; } else { /* use field j of t */ Z3_func_decl proj_decl = Z3_get_tuple_sort_field_decl(c, ty, j); new_fields[j] = mk_unary_app(c, proj_decl, t); } } mk_tuple_decl = Z3_get_tuple_sort_mk_decl(c, ty); result = Z3_mk_app(c, mk_tuple_decl, num_fields, new_fields); free(new_fields); return result; } /** \brief Display a symbol in the given output stream. */ void display_symbol(Z3_context c, FILE * out, Z3_symbol s) { switch (Z3_get_symbol_kind(c, s)) { case Z3_INT_SYMBOL: fprintf(out, "#%d", Z3_get_symbol_int(c, s)); break; case Z3_STRING_SYMBOL: fprintf(out, "%s", Z3_get_symbol_string(c, s)); break; default: unreachable(); } } /** \brief Display the given type. */ void display_sort(Z3_context c, FILE * out, Z3_sort ty) { switch (Z3_get_sort_kind(c, ty)) { case Z3_UNINTERPRETED_SORT: display_symbol(c, out, Z3_get_sort_name(c, ty)); break; case Z3_BOOL_SORT: fprintf(out, "bool"); break; case Z3_INT_SORT: fprintf(out, "int"); break; case Z3_REAL_SORT: fprintf(out, "real"); break; case Z3_BV_SORT: fprintf(out, "bv%d", Z3_get_bv_sort_size(c, ty)); break; case Z3_ARRAY_SORT: fprintf(out, "["); display_sort(c, out, Z3_get_array_sort_domain(c, ty)); fprintf(out, "->"); display_sort(c, out, Z3_get_array_sort_range(c, ty)); fprintf(out, "]"); break; case Z3_DATATYPE_SORT: if (Z3_get_datatype_sort_num_constructors(c, ty) != 1) { fprintf(out, "%s", Z3_sort_to_string(c,ty)); break; } { unsigned num_fields = Z3_get_tuple_sort_num_fields(c, ty); unsigned i; fprintf(out, "("); for (i = 0; i < num_fields; i++) { Z3_func_decl field = Z3_get_tuple_sort_field_decl(c, ty, i); if (i > 0) { fprintf(out, ", "); } display_sort(c, out, Z3_get_range(c, field)); } fprintf(out, ")"); break; } default: fprintf(out, "unknown["); display_symbol(c, out, Z3_get_sort_name(c, ty)); fprintf(out, "]"); break; } } /** \brief Custom ast pretty printer. This function demonstrates how to use the API to navigate terms. */ void display_ast(Z3_context c, FILE * out, Z3_ast v) { switch (Z3_get_ast_kind(c, v)) { case Z3_NUMERAL_AST: { Z3_sort t; fprintf(out, "%s", Z3_get_numeral_string(c, v)); t = Z3_get_sort(c, v); fprintf(out, ":"); display_sort(c, out, t); break; } case Z3_APP_AST: { unsigned i; Z3_app app = Z3_to_app(c, v); unsigned num_fields = Z3_get_app_num_args(c, app); Z3_func_decl d = Z3_get_app_decl(c, app); fprintf(out, "%s", Z3_func_decl_to_string(c, d)); if (num_fields > 0) { fprintf(out, "["); for (i = 0; i < num_fields; i++) { if (i > 0) { fprintf(out, ", "); } display_ast(c, out, Z3_get_app_arg(c, app, i)); } fprintf(out, "]"); } break; } case Z3_QUANTIFIER_AST: { fprintf(out, "quantifier"); ; } default: fprintf(out, "#unknown"); } } /** \brief Custom function interpretations pretty printer. */ void display_function_interpretations(Z3_context c, FILE * out, Z3_model m) { unsigned num_functions, i; fprintf(out, "function interpretations:\n"); num_functions = Z3_model_get_num_funcs(c, m); for (i = 0; i < num_functions; i++) { Z3_func_decl fdecl; Z3_symbol name; Z3_ast func_else; unsigned num_entries = 0, j; Z3_func_interp_opt finterp; fdecl = Z3_model_get_func_decl(c, m, i); finterp = Z3_model_get_func_interp(c, m, fdecl); Z3_func_interp_inc_ref(c, finterp); name = Z3_get_decl_name(c, fdecl); display_symbol(c, out, name); fprintf(out, " = {"); if (finterp) num_entries = Z3_func_interp_get_num_entries(c, finterp); for (j = 0; j < num_entries; j++) { unsigned num_args, k; Z3_func_entry fentry = Z3_func_interp_get_entry(c, finterp, j); Z3_func_entry_inc_ref(c, fentry); if (j > 0) { fprintf(out, ", "); } num_args = Z3_func_entry_get_num_args(c, fentry); fprintf(out, "("); for (k = 0; k < num_args; k++) { if (k > 0) { fprintf(out, ", "); } display_ast(c, out, Z3_func_entry_get_arg(c, fentry, k)); } fprintf(out, "|->"); display_ast(c, out, Z3_func_entry_get_value(c, fentry)); fprintf(out, ")"); Z3_func_entry_dec_ref(c, fentry); } if (num_entries > 0) { fprintf(out, ", "); } fprintf(out, "(else|->"); func_else = Z3_func_interp_get_else(c, finterp); display_ast(c, out, func_else); fprintf(out, ")}\n"); Z3_func_interp_dec_ref(c, finterp); } } /** \brief Custom model pretty printer. */ void display_model(Z3_context c, FILE * out, Z3_model m) { unsigned num_constants; unsigned i; if (!m) return; num_constants = Z3_model_get_num_consts(c, m); for (i = 0; i < num_constants; i++) { Z3_symbol name; Z3_func_decl cnst = Z3_model_get_const_decl(c, m, i); Z3_ast a, v; bool ok; name = Z3_get_decl_name(c, cnst); display_symbol(c, out, name); fprintf(out, " = "); a = Z3_mk_app(c, cnst, 0, 0); v = a; ok = Z3_model_eval(c, m, a, 1, &v); (void)ok; display_ast(c, out, v); fprintf(out, "\n"); } display_function_interpretations(c, out, m); } /** \brief Similar to #check, but uses #display_model instead of #Z3_model_to_string. */ void check2(Z3_context ctx, Z3_solver s, Z3_lbool expected_result) { Z3_model m = 0; Z3_lbool result = Z3_solver_check(ctx, s); switch (result) { case Z3_L_FALSE: printf("unsat\n"); break; case Z3_L_UNDEF: printf("unknown\n"); printf("potential model:\n"); m = Z3_solver_get_model(ctx, s); if (m) Z3_model_inc_ref(ctx, m); display_model(ctx, stdout, m); break; case Z3_L_TRUE: printf("sat\n"); m = Z3_solver_get_model(ctx, s); if (m) Z3_model_inc_ref(ctx, m); display_model(ctx, stdout, m); break; } if (result != expected_result) { exitf("unexpected result"); } if (m) Z3_model_dec_ref(ctx, m); } /** \brief Display Z3 version in the standard output. */ void display_version() { unsigned major, minor, build, revision; Z3_get_version(&major, &minor, &build, &revision); printf("Z3 %d.%d.%d.%d\n", major, minor, build, revision); } /*@}*/ /** @name Examples */ /*@{*/ /** \brief "Hello world" example: create a Z3 logical context, and delete it. */ void simple_example() { Z3_context ctx; LOG_MSG("simple_example"); printf("\nsimple_example\n"); ctx = mk_context(); /* delete logical context */ Z3_del_context(ctx); } /** Demonstration of how Z3 can be used to prove validity of De Morgan's Duality Law: {e not(x and y) <-> (not x) or ( not y) } */ void demorgan() { Z3_config cfg; Z3_context ctx; Z3_solver s; Z3_sort bool_sort; Z3_symbol symbol_x, symbol_y; Z3_ast x, y, not_x, not_y, x_and_y, ls, rs, conjecture, negated_conjecture; Z3_ast args[2]; printf("\nDeMorgan\n"); LOG_MSG("DeMorgan"); cfg = Z3_mk_config(); ctx = Z3_mk_context(cfg); Z3_del_config(cfg); bool_sort = Z3_mk_bool_sort(ctx); symbol_x = Z3_mk_int_symbol(ctx, 0); symbol_y = Z3_mk_int_symbol(ctx, 1); x = Z3_mk_const(ctx, symbol_x, bool_sort); y = Z3_mk_const(ctx, symbol_y, bool_sort); /* De Morgan - with a negation around */ /* !(!(x && y) <-> (!x || !y)) */ not_x = Z3_mk_not(ctx, x); not_y = Z3_mk_not(ctx, y); args[0] = x; args[1] = y; x_and_y = Z3_mk_and(ctx, 2, args); ls = Z3_mk_not(ctx, x_and_y); args[0] = not_x; args[1] = not_y; rs = Z3_mk_or(ctx, 2, args); conjecture = Z3_mk_iff(ctx, ls, rs); negated_conjecture = Z3_mk_not(ctx, conjecture); s = mk_solver(ctx); Z3_solver_assert(ctx, s, negated_conjecture); switch (Z3_solver_check(ctx, s)) { case Z3_L_FALSE: /* The negated conjecture was unsatisfiable, hence the conjecture is valid */ printf("DeMorgan is valid\n"); break; case Z3_L_UNDEF: /* Check returned undef */ printf("Undef\n"); break; case Z3_L_TRUE: /* The negated conjecture was satisfiable, hence the conjecture is not valid */ printf("DeMorgan is not valid\n"); break; } del_solver(ctx, s); Z3_del_context(ctx); } /** \brief Find a model for x xor y. */ void find_model_example1() { Z3_context ctx; Z3_ast x, y, x_xor_y; Z3_solver s; printf("\nfind_model_example1\n"); LOG_MSG("find_model_example1"); ctx = mk_context(); s = mk_solver(ctx); x = mk_bool_var(ctx, "x"); y = mk_bool_var(ctx, "y"); x_xor_y = Z3_mk_xor(ctx, x, y); Z3_solver_assert(ctx, s, x_xor_y); printf("model for: x xor y\n"); check(ctx, s, Z3_L_TRUE); del_solver(ctx, s); Z3_del_context(ctx); } /** \brief Find a model for x < y + 1, x > 2. Then, assert not(x = y), and find another model. */ void find_model_example2() { Z3_context ctx; Z3_ast x, y, one, two, y_plus_one; Z3_ast x_eq_y; Z3_ast args[2]; Z3_ast c1, c2, c3; Z3_solver s; printf("\nfind_model_example2\n"); LOG_MSG("find_model_example2"); ctx = mk_context(); s = mk_solver(ctx); x = mk_int_var(ctx, "x"); y = mk_int_var(ctx, "y"); one = mk_int(ctx, 1); two = mk_int(ctx, 2); args[0] = y; args[1] = one; y_plus_one = Z3_mk_add(ctx, 2, args); c1 = Z3_mk_lt(ctx, x, y_plus_one); c2 = Z3_mk_gt(ctx, x, two); Z3_solver_assert(ctx, s, c1); Z3_solver_assert(ctx, s, c2); printf("model for: x < y + 1, x > 2\n"); check(ctx, s, Z3_L_TRUE); /* assert not(x = y) */ x_eq_y = Z3_mk_eq(ctx, x, y); c3 = Z3_mk_not(ctx, x_eq_y); Z3_solver_assert(ctx, s,c3); printf("model for: x < y + 1, x > 2, not(x = y)\n"); check(ctx, s, Z3_L_TRUE); del_solver(ctx, s); Z3_del_context(ctx); } /** \brief Prove x = y implies g(x) = g(y), and disprove x = y implies g(g(x)) = g(y). This function demonstrates how to create uninterpreted types and functions. */ void prove_example1() { Z3_context ctx; Z3_solver s; Z3_symbol U_name, g_name, x_name, y_name; Z3_sort U; Z3_sort g_domain[1]; Z3_func_decl g; Z3_ast x, y, gx, ggx, gy; Z3_ast eq, f; printf("\nprove_example1\n"); LOG_MSG("prove_example1"); ctx = mk_context(); s = mk_solver(ctx); /* create uninterpreted type. */ U_name = Z3_mk_string_symbol(ctx, "U"); U = Z3_mk_uninterpreted_sort(ctx, U_name); /* declare function g */ g_name = Z3_mk_string_symbol(ctx, "g"); g_domain[0] = U; g = Z3_mk_func_decl(ctx, g_name, 1, g_domain, U); /* create x and y */ x_name = Z3_mk_string_symbol(ctx, "x"); y_name = Z3_mk_string_symbol(ctx, "y"); x = Z3_mk_const(ctx, x_name, U); y = Z3_mk_const(ctx, y_name, U); /* create g(x), g(y) */ gx = mk_unary_app(ctx, g, x); gy = mk_unary_app(ctx, g, y); /* assert x = y */ eq = Z3_mk_eq(ctx, x, y); Z3_solver_assert(ctx, s, eq); /* prove g(x) = g(y) */ f = Z3_mk_eq(ctx, gx, gy); printf("prove: x = y implies g(x) = g(y)\n"); prove(ctx, s, f, true); /* create g(g(x)) */ ggx = mk_unary_app(ctx, g, gx); /* disprove g(g(x)) = g(y) */ f = Z3_mk_eq(ctx, ggx, gy); printf("disprove: x = y implies g(g(x)) = g(y)\n"); prove(ctx, s, f, false); del_solver(ctx, s); Z3_del_context(ctx); } /** \brief Prove not(g(g(x) - g(y)) = g(z)), x + z <= y <= x implies z < 0 . Then, show that z < -1 is not implied. This example demonstrates how to combine uninterpreted functions and arithmetic. */ void prove_example2() { Z3_context ctx; Z3_solver s; Z3_sort int_sort; Z3_symbol g_name; Z3_sort g_domain[1]; Z3_func_decl g; Z3_ast x, y, z, zero, minus_one, x_plus_z, gx, gy, gz, gx_gy, ggx_gy; Z3_ast args[2]; Z3_ast eq, c1, c2, c3, f; printf("\nprove_example2\n"); LOG_MSG("prove_example2"); ctx = mk_context(); s = mk_solver(ctx); /* declare function g */ int_sort = Z3_mk_int_sort(ctx); g_name = Z3_mk_string_symbol(ctx, "g"); g_domain[0] = int_sort; g = Z3_mk_func_decl(ctx, g_name, 1, g_domain, int_sort); /* create x, y, and z */ x = mk_int_var(ctx, "x"); y = mk_int_var(ctx, "y"); z = mk_int_var(ctx, "z"); /* create gx, gy, gz */ gx = mk_unary_app(ctx, g, x); gy = mk_unary_app(ctx, g, y); gz = mk_unary_app(ctx, g, z); /* create zero */ zero = mk_int(ctx, 0); /* assert not(g(g(x) - g(y)) = g(z)) */ args[0] = gx; args[1] = gy; gx_gy = Z3_mk_sub(ctx, 2, args); ggx_gy = mk_unary_app(ctx, g, gx_gy); eq = Z3_mk_eq(ctx, ggx_gy, gz); c1 = Z3_mk_not(ctx, eq); Z3_solver_assert(ctx, s, c1); /* assert x + z <= y */ args[0] = x; args[1] = z; x_plus_z = Z3_mk_add(ctx, 2, args); c2 = Z3_mk_le(ctx, x_plus_z, y); Z3_solver_assert(ctx, s, c2); /* assert y <= x */ c3 = Z3_mk_le(ctx, y, x); Z3_solver_assert(ctx, s, c3); /* prove z < 0 */ f = Z3_mk_lt(ctx, z, zero); printf("prove: not(g(g(x) - g(y)) = g(z)), x + z <= y <= x implies z < 0\n"); prove(ctx, s, f, true); /* disprove z < -1 */ minus_one = mk_int(ctx, -1); f = Z3_mk_lt(ctx, z, minus_one); printf("disprove: not(g(g(x) - g(y)) = g(z)), x + z <= y <= x implies z < -1\n"); prove(ctx, s, f, false); del_solver(ctx, s); Z3_del_context(ctx); } /** \brief Show how push & pop can be used to create "backtracking" points. This example also demonstrates how big numbers can be created in Z3. */ void push_pop_example1() { Z3_context ctx; Z3_solver s; Z3_sort int_sort; Z3_symbol x_sym, y_sym; Z3_ast x, y, big_number, three; Z3_ast c1, c2, c3; printf("\npush_pop_example1\n"); LOG_MSG("push_pop_example1"); ctx = mk_context(); s = mk_solver(ctx); /* create a big number */ int_sort = Z3_mk_int_sort(ctx); big_number = Z3_mk_numeral(ctx, "1000000000000000000000000000000000000000000000000000000", int_sort); /* create number 3 */ three = Z3_mk_numeral(ctx, "3", int_sort); /* create x */ x_sym = Z3_mk_string_symbol(ctx, "x"); x = Z3_mk_const(ctx, x_sym, int_sort); /* assert x >= "big number" */ c1 = Z3_mk_ge(ctx, x, big_number); printf("assert: x >= 'big number'\n"); Z3_solver_assert(ctx, s, c1); /* create a backtracking point */ printf("push\n"); Z3_solver_push(ctx, s); printf("number of scopes: %d\n", Z3_solver_get_num_scopes(ctx, s)); /* assert x <= 3 */ c2 = Z3_mk_le(ctx, x, three); printf("assert: x <= 3\n"); Z3_solver_assert(ctx, s, c2); /* context is inconsistent at this point */ check2(ctx, s, Z3_L_FALSE); /* backtrack: the constraint x <= 3 will be removed, since it was asserted after the last Z3_solver_push. */ printf("pop\n"); Z3_solver_pop(ctx, s, 1); printf("number of scopes: %d\n", Z3_solver_get_num_scopes(ctx, s)); /* the context is consistent again. */ check2(ctx, s, Z3_L_TRUE); /* new constraints can be asserted... */ /* create y */ y_sym = Z3_mk_string_symbol(ctx, "y"); y = Z3_mk_const(ctx, y_sym, int_sort); /* assert y > x */ c3 = Z3_mk_gt(ctx, y, x); printf("assert: y > x\n"); Z3_solver_assert(ctx, s, c3); /* the context is still consistent. */ check2(ctx, s, Z3_L_TRUE); del_solver(ctx, s); Z3_del_context(ctx); } /** \brief Prove that f(x, y) = f(w, v) implies y = v when \c f is injective in the second argument. \sa assert_inj_axiom. */ void quantifier_example1() { Z3_config cfg; Z3_context ctx; Z3_solver s; Z3_sort int_sort; Z3_symbol f_name; Z3_sort f_domain[2]; Z3_func_decl f; Z3_ast x, y, w, v, fxy, fwv; Z3_ast p1, p2, p3, not_p3; printf("\nquantifier_example1\n"); LOG_MSG("quantifier_example1"); cfg = Z3_mk_config(); /* If quantified formulas are asserted in a logical context, then Z3 may return L_UNDEF. In this case, the model produced by Z3 should be viewed as a potential/candidate model. */ /* The current model finder for quantified formulas cannot handle injectivity. So, we are limiting the number of iterations to avoid a long "wait". */ Z3_global_param_set("smt.mbqi.max_iterations", "10"); ctx = mk_context_custom(cfg, error_handler); Z3_del_config(cfg); s = mk_solver(ctx); /* declare function f */ int_sort = Z3_mk_int_sort(ctx); f_name = Z3_mk_string_symbol(ctx, "f"); f_domain[0] = int_sort; f_domain[1] = int_sort; f = Z3_mk_func_decl(ctx, f_name, 2, f_domain, int_sort); /* assert that f is injective in the second argument. */ assert_inj_axiom(ctx, s, f, 1); /* create x, y, v, w, fxy, fwv */ x = mk_int_var(ctx, "x"); y = mk_int_var(ctx, "y"); v = mk_int_var(ctx, "v"); w = mk_int_var(ctx, "w"); fxy = mk_binary_app(ctx, f, x, y); fwv = mk_binary_app(ctx, f, w, v); /* assert f(x, y) = f(w, v) */ p1 = Z3_mk_eq(ctx, fxy, fwv); Z3_solver_assert(ctx, s, p1); /* prove f(x, y) = f(w, v) implies y = v */ p2 = Z3_mk_eq(ctx, y, v); printf("prove: f(x, y) = f(w, v) implies y = v\n"); prove(ctx, s, p2, true); /* disprove f(x, y) = f(w, v) implies x = w */ /* using check2 instead of prove */ p3 = Z3_mk_eq(ctx, x, w); not_p3 = Z3_mk_not(ctx, p3); Z3_solver_assert(ctx, s, not_p3); printf("disprove: f(x, y) = f(w, v) implies x = w\n"); printf("that is: not(f(x, y) = f(w, v) implies x = w) is satisfiable\n"); check2(ctx, s, Z3_L_UNDEF); printf("reason for last failure: %s\n", Z3_solver_get_reason_unknown(ctx, s)); del_solver(ctx, s); Z3_del_context(ctx); /* reset global parameters set by this function */ Z3_global_param_reset_all(); } /** \brief Prove store(a1, i1, v1) = store(a2, i2, v2) implies (i1 = i3 or i2 = i3 or select(a1, i3) = select(a2, i3)). This example demonstrates how to use the array theory. */ void array_example1() { Z3_context ctx = mk_context(); Z3_solver s = mk_solver(ctx); Z3_sort int_sort, array_sort; Z3_ast a1, a2, i1, v1, i2, v2, i3; Z3_ast st1, st2, sel1, sel2; Z3_ast antecedent, consequent; Z3_ast ds[3]; Z3_ast thm; printf("\narray_example1\n"); LOG_MSG("array_example1"); int_sort = Z3_mk_int_sort(ctx); array_sort = Z3_mk_array_sort(ctx, int_sort, int_sort); a1 = mk_var(ctx, "a1", array_sort); a2 = mk_var(ctx, "a2", array_sort); i1 = mk_var(ctx, "i1", int_sort); i2 = mk_var(ctx, "i2", int_sort); i3 = mk_var(ctx, "i3", int_sort); v1 = mk_var(ctx, "v1", int_sort); v2 = mk_var(ctx, "v2", int_sort); st1 = Z3_mk_store(ctx, a1, i1, v1); st2 = Z3_mk_store(ctx, a2, i2, v2); sel1 = Z3_mk_select(ctx, a1, i3); sel2 = Z3_mk_select(ctx, a2, i3); /* create antecedent */ antecedent = Z3_mk_eq(ctx, st1, st2); /* create consequent: i1 = i3 or i2 = i3 or select(a1, i3) = select(a2, i3) */ ds[0] = Z3_mk_eq(ctx, i1, i3); ds[1] = Z3_mk_eq(ctx, i2, i3); ds[2] = Z3_mk_eq(ctx, sel1, sel2); consequent = Z3_mk_or(ctx, 3, ds); /* prove store(a1, i1, v1) = store(a2, i2, v2) implies (i1 = i3 or i2 = i3 or select(a1, i3) = select(a2, i3)) */ thm = Z3_mk_implies(ctx, antecedent, consequent); printf("prove: store(a1, i1, v1) = store(a2, i2, v2) implies (i1 = i3 or i2 = i3 or select(a1, i3) = select(a2, i3))\n"); printf("%s\n", Z3_ast_to_string(ctx, thm)); prove(ctx, s, thm, true); del_solver(ctx, s); Z3_del_context(ctx); } /** \brief Show that distinct(a_0, ... , a_n) is unsatisfiable when \c a_i's are arrays from boolean to boolean and n > 4. This example also shows how to use the \c distinct construct. */ void array_example2() { Z3_context ctx; Z3_solver s; Z3_sort bool_sort, array_sort; Z3_ast a[5]; Z3_ast d; unsigned i, n; printf("\narray_example2\n"); LOG_MSG("array_example2"); for (n = 2; n <= 5; n++) { printf("n = %d\n", n); ctx = mk_context(); s = mk_solver(ctx); bool_sort = Z3_mk_bool_sort(ctx); array_sort = Z3_mk_array_sort(ctx, bool_sort, bool_sort); /* create arrays */ for (i = 0; i < n; i++) { Z3_symbol s = Z3_mk_int_symbol(ctx, i); a[i] = Z3_mk_const(ctx, s, array_sort); } /* assert distinct(a[0], ..., a[n]) */ d = Z3_mk_distinct(ctx, n, a); printf("%s\n", Z3_ast_to_string(ctx, d)); Z3_solver_assert(ctx, s, d); /* context is satisfiable if n < 5 */ check2(ctx, s, n < 5 ? Z3_L_TRUE : Z3_L_FALSE); del_solver(ctx, s); Z3_del_context(ctx); } } /** \brief Simple array type construction/deconstruction example. */ void array_example3() { Z3_context ctx = mk_context(); Z3_solver s = mk_solver(ctx); Z3_sort bool_sort, int_sort, array_sort; Z3_sort domain, range; printf("\narray_example3\n"); LOG_MSG("array_example3"); bool_sort = Z3_mk_bool_sort(ctx); int_sort = Z3_mk_int_sort(ctx); array_sort = Z3_mk_array_sort(ctx, int_sort, bool_sort); if (Z3_get_sort_kind(ctx, array_sort) != Z3_ARRAY_SORT) { exitf("type must be an array type"); } domain = Z3_get_array_sort_domain(ctx, array_sort); range = Z3_get_array_sort_range(ctx, array_sort); printf("domain: "); display_sort(ctx, stdout, domain); printf("\n"); printf("range: "); display_sort(ctx, stdout, range); printf("\n"); if (int_sort != domain || bool_sort != range) { exitf("invalid array type"); } del_solver(ctx, s); Z3_del_context(ctx); } /** \brief Simple tuple type example. It creates a tuple that is a pair of real numbers. */ void tuple_example1() { Z3_context ctx = mk_context(); Z3_solver s = mk_solver(ctx); Z3_sort real_sort, pair_sort; Z3_symbol mk_tuple_name; Z3_func_decl mk_tuple_decl; Z3_symbol proj_names[2]; Z3_sort proj_sorts[2]; Z3_func_decl proj_decls[2]; Z3_func_decl get_x_decl, get_y_decl; printf("\ntuple_example1\n"); LOG_MSG("tuple_example1"); real_sort = Z3_mk_real_sort(ctx); /* Create pair (tuple) type */ mk_tuple_name = Z3_mk_string_symbol(ctx, "mk_pair"); proj_names[0] = Z3_mk_string_symbol(ctx, "get_x"); proj_names[1] = Z3_mk_string_symbol(ctx, "get_y"); proj_sorts[0] = real_sort; proj_sorts[1] = real_sort; /* Z3_mk_tuple_sort will set mk_tuple_decl and proj_decls */ pair_sort = Z3_mk_tuple_sort(ctx, mk_tuple_name, 2, proj_names, proj_sorts, &mk_tuple_decl, proj_decls); get_x_decl = proj_decls[0]; /* function that extracts the first element of a tuple. */ get_y_decl = proj_decls[1]; /* function that extracts the second element of a tuple. */ printf("tuple_sort: "); display_sort(ctx, stdout, pair_sort); printf("\n"); { /* prove that get_x(mk_pair(x,y)) == 1 implies x = 1*/ Z3_ast app1, app2, x, y, one; Z3_ast eq1, eq2, eq3, thm; x = mk_real_var(ctx, "x"); y = mk_real_var(ctx, "y"); app1 = mk_binary_app(ctx, mk_tuple_decl, x, y); app2 = mk_unary_app(ctx, get_x_decl, app1); one = Z3_mk_numeral(ctx, "1", real_sort); eq1 = Z3_mk_eq(ctx, app2, one); eq2 = Z3_mk_eq(ctx, x, one); thm = Z3_mk_implies(ctx, eq1, eq2); printf("prove: get_x(mk_pair(x, y)) = 1 implies x = 1\n"); prove(ctx, s, thm, true); /* disprove that get_x(mk_pair(x,y)) == 1 implies y = 1*/ eq3 = Z3_mk_eq(ctx, y, one); thm = Z3_mk_implies(ctx, eq1, eq3); printf("disprove: get_x(mk_pair(x, y)) = 1 implies y = 1\n"); prove(ctx, s, thm, false); } { /* prove that get_x(p1) = get_x(p2) and get_y(p1) = get_y(p2) implies p1 = p2 */ Z3_ast p1, p2, x1, x2, y1, y2; Z3_ast antecedents[2]; Z3_ast antecedent, consequent, thm; p1 = mk_var(ctx, "p1", pair_sort); p2 = mk_var(ctx, "p2", pair_sort); x1 = mk_unary_app(ctx, get_x_decl, p1); y1 = mk_unary_app(ctx, get_y_decl, p1); x2 = mk_unary_app(ctx, get_x_decl, p2); y2 = mk_unary_app(ctx, get_y_decl, p2); antecedents[0] = Z3_mk_eq(ctx, x1, x2); antecedents[1] = Z3_mk_eq(ctx, y1, y2); antecedent = Z3_mk_and(ctx, 2, antecedents); consequent = Z3_mk_eq(ctx, p1, p2); thm = Z3_mk_implies(ctx, antecedent, consequent); printf("prove: get_x(p1) = get_x(p2) and get_y(p1) = get_y(p2) implies p1 = p2\n"); prove(ctx, s, thm, true); /* disprove that get_x(p1) = get_x(p2) implies p1 = p2 */ thm = Z3_mk_implies(ctx, antecedents[0], consequent); printf("disprove: get_x(p1) = get_x(p2) implies p1 = p2\n"); prove(ctx, s, thm, false); } { /* demonstrate how to use the mk_tuple_update function */ /* prove that p2 = update(p1, 0, 10) implies get_x(p2) = 10 */ Z3_ast p1, p2, ten, updt, x, y; Z3_ast antecedent, consequent, thm; p1 = mk_var(ctx, "p1", pair_sort); p2 = mk_var(ctx, "p2", pair_sort); ten = Z3_mk_numeral(ctx, "10", real_sort); updt = mk_tuple_update(ctx, p1, 0, ten); antecedent = Z3_mk_eq(ctx, p2, updt); x = mk_unary_app(ctx, get_x_decl, p2); consequent = Z3_mk_eq(ctx, x, ten); thm = Z3_mk_implies(ctx, antecedent, consequent); printf("prove: p2 = update(p1, 0, 10) implies get_x(p2) = 10\n"); prove(ctx, s, thm, true); /* disprove that p2 = update(p1, 0, 10) implies get_y(p2) = 10 */ y = mk_unary_app(ctx, get_y_decl, p2); consequent = Z3_mk_eq(ctx, y, ten); thm = Z3_mk_implies(ctx, antecedent, consequent); printf("disprove: p2 = update(p1, 0, 10) implies get_y(p2) = 10\n"); prove(ctx, s, thm, false); } del_solver(ctx, s); Z3_del_context(ctx); } /** \brief Simple bit-vector example. This example disproves that x - 10 <= 0 IFF x <= 10 for (32-bit) machine integers */ void bitvector_example1() { Z3_context ctx = mk_context(); Z3_solver s = mk_solver(ctx); Z3_sort bv_sort; Z3_ast x, zero, ten, x_minus_ten, c1, c2, thm; printf("\nbitvector_example1\n"); LOG_MSG("bitvector_example1"); bv_sort = Z3_mk_bv_sort(ctx, 32); x = mk_var(ctx, "x", bv_sort); zero = Z3_mk_numeral(ctx, "0", bv_sort); ten = Z3_mk_numeral(ctx, "10", bv_sort); x_minus_ten = Z3_mk_bvsub(ctx, x, ten); /* bvsle is signed less than or equal to */ c1 = Z3_mk_bvsle(ctx, x, ten); c2 = Z3_mk_bvsle(ctx, x_minus_ten, zero); thm = Z3_mk_iff(ctx, c1, c2); printf("disprove: x - 10 <= 0 IFF x <= 10 for (32-bit) machine integers\n"); prove(ctx, s, thm, false); del_solver(ctx, s); Z3_del_context(ctx); } /** \brief Find x and y such that: x ^ y - 103 == x * y */ void bitvector_example2() { Z3_context ctx = mk_context(); Z3_solver s = mk_solver(ctx); /* construct x ^ y - 103 == x * y */ Z3_sort bv_sort = Z3_mk_bv_sort(ctx, 32); Z3_ast x = mk_var(ctx, "x", bv_sort); Z3_ast y = mk_var(ctx, "y", bv_sort); Z3_ast x_xor_y = Z3_mk_bvxor(ctx, x, y); Z3_ast c103 = Z3_mk_numeral(ctx, "103", bv_sort); Z3_ast lhs = Z3_mk_bvsub(ctx, x_xor_y, c103); Z3_ast rhs = Z3_mk_bvmul(ctx, x, y); Z3_ast ctr = Z3_mk_eq(ctx, lhs, rhs); printf("\nbitvector_example2\n"); LOG_MSG("bitvector_example2"); printf("find values of x and y, such that x ^ y - 103 == x * y\n"); /* add the constraint x ^ y - 103 == x * y<\tt> to the logical context */ Z3_solver_assert(ctx, s, ctr); /* find a model (i.e., values for x an y that satisfy the constraint */ check(ctx, s, Z3_L_TRUE); del_solver(ctx, s); Z3_del_context(ctx); } /** \brief Demonstrate how to use #Z3_eval. */ void eval_example1() { Z3_context ctx = mk_context(); Z3_solver s = mk_solver(ctx); Z3_ast x, y, two; Z3_ast c1, c2; Z3_model m = 0; printf("\neval_example1\n"); LOG_MSG("eval_example1"); x = mk_int_var(ctx, "x"); y = mk_int_var(ctx, "y"); two = mk_int(ctx, 2); /* assert x < y */ c1 = Z3_mk_lt(ctx, x, y); Z3_solver_assert(ctx, s, c1); /* assert x > 2 */ c2 = Z3_mk_gt(ctx, x, two); Z3_solver_assert(ctx, s, c2); /* find model for the constraints above */ if (Z3_solver_check(ctx, s) == Z3_L_TRUE) { Z3_ast x_plus_y; Z3_ast args[2] = {x, y}; Z3_ast v; m = Z3_solver_get_model(ctx, s); if (m) Z3_model_inc_ref(ctx, m); printf("MODEL:\n%s", Z3_model_to_string(ctx, m)); x_plus_y = Z3_mk_add(ctx, 2, args); printf("\nevaluating x+y\n"); if (Z3_model_eval(ctx, m, x_plus_y, 1, &v)) { printf("result = "); display_ast(ctx, stdout, v); printf("\n"); } else { exitf("failed to evaluate: x+y"); } } else { exitf("the constraints are satisfiable"); } if (m) Z3_model_dec_ref(ctx, m); del_solver(ctx, s); Z3_del_context(ctx); } /** \brief Several logical context can be used simultaneously. */ void two_contexts_example1() { Z3_context ctx1, ctx2; Z3_ast x1, x2; printf("\ntwo_contexts_example1\n"); LOG_MSG("two_contexts_example1"); /* using the same (default) configuration to initialized both logical contexts. */ ctx1 = mk_context(); ctx2 = mk_context(); x1 = Z3_mk_const(ctx1, Z3_mk_int_symbol(ctx1,0), Z3_mk_bool_sort(ctx1)); x2 = Z3_mk_const(ctx2, Z3_mk_int_symbol(ctx2,0), Z3_mk_bool_sort(ctx2)); Z3_del_context(ctx1); /* ctx2 can still be used. */ printf("%s\n", Z3_ast_to_string(ctx2, x2)); Z3_del_context(ctx2); } /** \brief Demonstrates how error codes can be read instead of registering an error handler. */ void error_code_example1() { Z3_config cfg; Z3_context ctx; Z3_solver s; Z3_ast x; Z3_model m; Z3_ast v; const char * str; printf("\nerror_code_example1\n"); LOG_MSG("error_code_example11"); /* Do not register an error handler, as we want to use Z3_get_error_code manually */ cfg = Z3_mk_config(); ctx = mk_context_custom(cfg, NULL); Z3_del_config(cfg); s = mk_solver(ctx); x = mk_bool_var(ctx, "x"); Z3_solver_assert(ctx, s, x); if (Z3_solver_check(ctx, s) != Z3_L_TRUE) { exitf("unexpected result"); } m = Z3_solver_get_model(ctx, s); if (m) Z3_model_inc_ref(ctx, m); if (!Z3_model_eval(ctx, m, x, 1, &v)) { exitf("did not obtain value for declaration.\n"); } if (Z3_get_error_code(ctx) == Z3_OK) { printf("last call succeeded.\n"); } /* The following call will fail since the value of x is a boolean */ str = Z3_get_numeral_string(ctx, v); (void)str; if (Z3_get_error_code(ctx) != Z3_OK) { printf("last call failed.\n"); } if (m) Z3_model_dec_ref(ctx, m); del_solver(ctx, s); Z3_del_context(ctx); } /** \brief Demonstrates how error handlers can be used. */ void error_code_example2() { Z3_config cfg; Z3_context ctx = NULL; Z3_error_code e; printf("\nerror_code_example2\n"); LOG_MSG("error_code_example2"); if (1) { Z3_ast x, y, app; cfg = Z3_mk_config(); ctx = mk_context_custom(cfg, nothrow_z3_error); Z3_del_config(cfg); x = mk_int_var(ctx, "x"); y = mk_bool_var(ctx, "y"); printf("before Z3_mk_iff\n"); /* the next call will produce an error */ app = Z3_mk_iff(ctx, x, y); (void)app; e = Z3_get_error_code(ctx); if (e != Z3_OK) goto err; unreachable(); Z3_del_context(ctx); } else { err: printf("Z3 error: %s.\n", Z3_get_error_msg(ctx, e)); if (ctx != NULL) { Z3_del_context(ctx); } } } /** \brief Demonstrates how to initialize the parser symbol table. */ void parser_example2() { Z3_context ctx = mk_context(); Z3_solver s = mk_solver(ctx); Z3_ast x, y; Z3_symbol names[2]; Z3_func_decl decls[2]; Z3_ast_vector f; unsigned i; printf("\nparser_example2\n"); LOG_MSG("parser_example2"); /* Z3_enable_arithmetic doesn't need to be invoked in this example because it will be implicitly invoked by mk_int_var. */ x = mk_int_var(ctx, "x"); decls[0] = Z3_get_app_decl(ctx, Z3_to_app(ctx, x)); y = mk_int_var(ctx, "y"); decls[1] = Z3_get_app_decl(ctx, Z3_to_app(ctx, y)); names[0] = Z3_mk_string_symbol(ctx, "a"); names[1] = Z3_mk_string_symbol(ctx, "b"); f = Z3_parse_smtlib2_string(ctx, "(assert (> a b))", 0, 0, 0, /* 'x' and 'y' declarations are inserted as 'a' and 'b' into the parser symbol table. */ 2, names, decls); printf("formula: %s\n", Z3_ast_vector_to_string(ctx, f)); printf("assert axiom:\n%s\n", Z3_ast_vector_to_string(ctx, f)); for (i = 0; i < Z3_ast_vector_size(ctx, f); ++i) { Z3_solver_assert(ctx, s, Z3_ast_vector_get(ctx, f, i)); } check(ctx, s, Z3_L_TRUE); del_solver(ctx, s); Z3_del_context(ctx); } /** \brief Demonstrates how to initialize the parser symbol table. */ void parser_example3() { Z3_config cfg; Z3_context ctx; Z3_solver s; Z3_sort int_sort; Z3_symbol g_name; Z3_sort g_domain[2]; Z3_func_decl g; Z3_ast_vector thm; printf("\nparser_example3\n"); LOG_MSG("parser_example3"); cfg = Z3_mk_config(); /* See quantifier_example1 */ Z3_set_param_value(cfg, "model", "true"); ctx = mk_context_custom(cfg, error_handler); Z3_del_config(cfg); s = mk_solver(ctx); /* declare function g */ int_sort = Z3_mk_int_sort(ctx); g_name = Z3_mk_string_symbol(ctx, "g"); g_domain[0] = int_sort; g_domain[1] = int_sort; g = Z3_mk_func_decl(ctx, g_name, 2, g_domain, int_sort); assert_comm_axiom(ctx, s, g); thm = Z3_parse_smtlib2_string(ctx, "(assert (forall ((x Int) (y Int)) (=> (= x y) (= (g x 0) (g 0 y)))))", 0, 0, 0, 1, &g_name, &g); printf("formula: %s\n", Z3_ast_vector_to_string(ctx, thm)); prove(ctx, s, Z3_ast_vector_get(ctx, thm, 0), true); del_solver(ctx, s); Z3_del_context(ctx); } /** \brief Demonstrates how to handle parser errors using Z3 error handling support. */ void parser_example5() { Z3_config cfg; Z3_context ctx = NULL; Z3_solver s = NULL; Z3_error_code e; printf("\nparser_example5\n"); LOG_MSG("parser_example5"); if (1) { cfg = Z3_mk_config(); ctx = mk_context_custom(cfg, nothrow_z3_error); s = mk_solver(ctx); Z3_del_config(cfg); Z3_parse_smtlib2_string(ctx, /* the following string has a parsing error: missing parenthesis */ "(declare-const x Int) declare-const y Int) (assert (and (> x y) (> x 0)))", 0, 0, 0, 0, 0, 0); e = Z3_get_error_code(ctx); if (e != Z3_OK) goto err; unreachable(); del_solver(ctx, s); Z3_del_context(ctx); } else { err: printf("Z3 error: %s.\n", Z3_get_error_msg(ctx, e)); if (ctx != NULL) { del_solver(ctx, s); Z3_del_context(ctx); } } } /** \brief Demonstrate different ways of creating rational numbers: decimal and fractional representations. */ void numeral_example() { Z3_context ctx; Z3_solver s; Z3_ast n1, n2; Z3_sort real_ty; printf("\nnumeral_example\n"); LOG_MSG("numeral_example"); ctx = mk_context(); s = mk_solver(ctx); real_ty = Z3_mk_real_sort(ctx); n1 = Z3_mk_numeral(ctx, "1/2", real_ty); n2 = Z3_mk_numeral(ctx, "0.5", real_ty); printf("Numerals n1:%s", Z3_ast_to_string(ctx, n1)); printf(" n2:%s\n", Z3_ast_to_string(ctx, n2)); prove(ctx, s, Z3_mk_eq(ctx, n1, n2), true); n1 = Z3_mk_numeral(ctx, "-1/3", real_ty); n2 = Z3_mk_numeral(ctx, "-0.33333333333333333333333333333333333333333333333333", real_ty); printf("Numerals n1:%s", Z3_ast_to_string(ctx, n1)); printf(" n2:%s\n", Z3_ast_to_string(ctx, n2)); prove(ctx, s, Z3_mk_not(ctx, Z3_mk_eq(ctx, n1, n2)), true); del_solver(ctx, s); Z3_del_context(ctx); } /** \brief Test ite-term (if-then-else terms). */ void ite_example() { Z3_context ctx; Z3_ast f, one, zero, ite; printf("\nite_example\n"); LOG_MSG("ite_example"); ctx = mk_context(); f = Z3_mk_false(ctx); one = mk_int(ctx, 1); zero = mk_int(ctx, 0); ite = Z3_mk_ite(ctx, f, one, zero); printf("term: %s\n", Z3_ast_to_string(ctx, ite)); /* delete logical context */ Z3_del_context(ctx); } /** \brief Create an enumeration data type. */ void enum_example() { Z3_context ctx = mk_context(); Z3_solver s = mk_solver(ctx); Z3_sort fruit; Z3_symbol name = Z3_mk_string_symbol(ctx, "fruit"); Z3_symbol enum_names[3]; Z3_func_decl enum_consts[3]; Z3_func_decl enum_testers[3]; Z3_ast apple, orange, banana, fruity; Z3_ast ors[3]; printf("\nenum_example\n"); LOG_MSG("enum_example"); enum_names[0] = Z3_mk_string_symbol(ctx,"apple"); enum_names[1] = Z3_mk_string_symbol(ctx,"banana"); enum_names[2] = Z3_mk_string_symbol(ctx,"orange"); fruit = Z3_mk_enumeration_sort(ctx, name, 3, enum_names, enum_consts, enum_testers); printf("%s\n", Z3_func_decl_to_string(ctx, enum_consts[0])); printf("%s\n", Z3_func_decl_to_string(ctx, enum_consts[1])); printf("%s\n", Z3_func_decl_to_string(ctx, enum_consts[2])); printf("%s\n", Z3_func_decl_to_string(ctx, enum_testers[0])); printf("%s\n", Z3_func_decl_to_string(ctx, enum_testers[1])); printf("%s\n", Z3_func_decl_to_string(ctx, enum_testers[2])); apple = Z3_mk_app(ctx, enum_consts[0], 0, 0); banana = Z3_mk_app(ctx, enum_consts[1], 0, 0); orange = Z3_mk_app(ctx, enum_consts[2], 0, 0); /* Apples are different from oranges */ prove(ctx, s, Z3_mk_not(ctx, Z3_mk_eq(ctx, apple, orange)), true); /* Apples pass the apple test */ prove(ctx, s, Z3_mk_app(ctx, enum_testers[0], 1, &apple), true); /* Oranges fail the apple test */ prove(ctx, s, Z3_mk_app(ctx, enum_testers[0], 1, &orange), false); prove(ctx, s, Z3_mk_not(ctx, Z3_mk_app(ctx, enum_testers[0], 1, &orange)), true); fruity = mk_var(ctx, "fruity", fruit); /* If something is fruity, then it is an apple, banana, or orange */ ors[0] = Z3_mk_eq(ctx, fruity, apple); ors[1] = Z3_mk_eq(ctx, fruity, banana); ors[2] = Z3_mk_eq(ctx, fruity, orange); prove(ctx, s, Z3_mk_or(ctx, 3, ors), true); /* delete logical context */ del_solver(ctx, s); Z3_del_context(ctx); } /** \brief Create a list datatype. */ void list_example() { Z3_context ctx = mk_context(); Z3_solver s = mk_solver(ctx); Z3_sort int_ty, int_list; Z3_func_decl nil_decl, is_nil_decl, cons_decl, is_cons_decl, head_decl, tail_decl; Z3_ast nil, l1, l2, x, y, u, v, fml, fml1; Z3_ast ors[2]; printf("\nlist_example\n"); LOG_MSG("list_example"); int_ty = Z3_mk_int_sort(ctx); int_list = Z3_mk_list_sort(ctx, Z3_mk_string_symbol(ctx, "int_list"), int_ty, &nil_decl, &is_nil_decl, &cons_decl, &is_cons_decl, &head_decl, &tail_decl); nil = Z3_mk_app(ctx, nil_decl, 0, 0); l1 = mk_binary_app(ctx, cons_decl, mk_int(ctx, 1), nil); l2 = mk_binary_app(ctx, cons_decl, mk_int(ctx, 2), nil); /* nil != cons(1, nil) */ prove(ctx, s, Z3_mk_not(ctx, Z3_mk_eq(ctx, nil, l1)), true); /* cons(2,nil) != cons(1, nil) */ prove(ctx, s, Z3_mk_not(ctx, Z3_mk_eq(ctx, l1, l2)), true); /* cons(x,nil) = cons(y, nil) => x = y */ x = mk_var(ctx, "x", int_ty); y = mk_var(ctx, "y", int_ty); l1 = mk_binary_app(ctx, cons_decl, x, nil); l2 = mk_binary_app(ctx, cons_decl, y, nil); prove(ctx, s, Z3_mk_implies(ctx, Z3_mk_eq(ctx,l1,l2), Z3_mk_eq(ctx, x, y)), true); /* cons(x,u) = cons(x, v) => u = v */ u = mk_var(ctx, "u", int_list); v = mk_var(ctx, "v", int_list); l1 = mk_binary_app(ctx, cons_decl, x, u); l2 = mk_binary_app(ctx, cons_decl, y, v); prove(ctx, s, Z3_mk_implies(ctx, Z3_mk_eq(ctx,l1,l2), Z3_mk_eq(ctx, u, v)), true); prove(ctx, s, Z3_mk_implies(ctx, Z3_mk_eq(ctx,l1,l2), Z3_mk_eq(ctx, x, y)), true); /* is_nil(u) or is_cons(u) */ ors[0] = Z3_mk_app(ctx, is_nil_decl, 1, &u); ors[1] = Z3_mk_app(ctx, is_cons_decl, 1, &u); prove(ctx, s, Z3_mk_or(ctx, 2, ors), true); /* occurs check u != cons(x,u) */ prove(ctx, s, Z3_mk_not(ctx, Z3_mk_eq(ctx, u, l1)), true); /* destructors: is_cons(u) => u = cons(head(u),tail(u)) */ fml1 = Z3_mk_eq(ctx, u, mk_binary_app(ctx, cons_decl, mk_unary_app(ctx, head_decl, u), mk_unary_app(ctx, tail_decl, u))); fml = Z3_mk_implies(ctx, Z3_mk_app(ctx, is_cons_decl, 1, &u), fml1); printf("Formula %s\n", Z3_ast_to_string(ctx, fml)); prove(ctx, s, fml, true); prove(ctx, s, fml1, false); /* delete logical context */ del_solver(ctx, s); Z3_del_context(ctx); } /** \brief Create a binary tree datatype. */ void tree_example() { Z3_context ctx = mk_context(); Z3_solver s = mk_solver(ctx); Z3_sort cell; Z3_func_decl nil_decl, is_nil_decl, cons_decl, is_cons_decl, car_decl, cdr_decl; Z3_ast nil, l1, l2, x, y, u, v, fml, fml1; Z3_symbol head_tail[2] = { Z3_mk_string_symbol(ctx, "car"), Z3_mk_string_symbol(ctx, "cdr") }; Z3_sort sorts[2] = { 0, 0 }; unsigned sort_refs[2] = { 0, 0 }; Z3_constructor nil_con, cons_con; Z3_constructor constructors[2]; Z3_func_decl cons_accessors[2]; Z3_ast ors[2]; printf("\ntree_example\n"); LOG_MSG("tree_example"); nil_con = Z3_mk_constructor(ctx, Z3_mk_string_symbol(ctx, "nil"), Z3_mk_string_symbol(ctx, "is_nil"), 0, 0, 0, 0); cons_con = Z3_mk_constructor(ctx, Z3_mk_string_symbol(ctx, "cons"), Z3_mk_string_symbol(ctx, "is_cons"), 2, head_tail, sorts, sort_refs); constructors[0] = nil_con; constructors[1] = cons_con; cell = Z3_mk_datatype(ctx, Z3_mk_string_symbol(ctx, "cell"), 2, constructors); Z3_query_constructor(ctx, nil_con, 0, &nil_decl, &is_nil_decl, 0); Z3_query_constructor(ctx, cons_con, 2, &cons_decl, &is_cons_decl, cons_accessors); car_decl = cons_accessors[0]; cdr_decl = cons_accessors[1]; Z3_del_constructor(ctx,nil_con); Z3_del_constructor(ctx,cons_con); nil = Z3_mk_app(ctx, nil_decl, 0, 0); l1 = mk_binary_app(ctx, cons_decl, nil, nil); l2 = mk_binary_app(ctx, cons_decl, l1, nil); /* nil != cons(nil, nil) */ prove(ctx, s, Z3_mk_not(ctx, Z3_mk_eq(ctx, nil, l1)), true); /* cons(x,u) = cons(x, v) => u = v */ u = mk_var(ctx, "u", cell); v = mk_var(ctx, "v", cell); x = mk_var(ctx, "x", cell); y = mk_var(ctx, "y", cell); l1 = mk_binary_app(ctx, cons_decl, x, u); l2 = mk_binary_app(ctx, cons_decl, y, v); prove(ctx, s, Z3_mk_implies(ctx, Z3_mk_eq(ctx,l1,l2), Z3_mk_eq(ctx, u, v)), true); prove(ctx, s, Z3_mk_implies(ctx, Z3_mk_eq(ctx,l1,l2), Z3_mk_eq(ctx, x, y)), true); /* is_nil(u) or is_cons(u) */ ors[0] = Z3_mk_app(ctx, is_nil_decl, 1, &u); ors[1] = Z3_mk_app(ctx, is_cons_decl, 1, &u); prove(ctx, s, Z3_mk_or(ctx, 2, ors), true); /* occurs check u != cons(x,u) */ prove(ctx, s, Z3_mk_not(ctx, Z3_mk_eq(ctx, u, l1)), true); /* destructors: is_cons(u) => u = cons(car(u),cdr(u)) */ fml1 = Z3_mk_eq(ctx, u, mk_binary_app(ctx, cons_decl, mk_unary_app(ctx, car_decl, u), mk_unary_app(ctx, cdr_decl, u))); fml = Z3_mk_implies(ctx, Z3_mk_app(ctx, is_cons_decl, 1, &u), fml1); printf("Formula %s\n", Z3_ast_to_string(ctx, fml)); prove(ctx, s, fml, true); prove(ctx, s, fml1, false); /* delete logical context */ del_solver(ctx, s); Z3_del_context(ctx); } /** \brief Create a forest of trees. forest ::= nil | cons(tree, forest) tree ::= nil | cons(forest, forest) */ void forest_example() { Z3_context ctx = mk_context(); Z3_solver s = mk_solver(ctx); Z3_sort tree, forest; Z3_func_decl nil1_decl, is_nil1_decl, cons1_decl, is_cons1_decl, car1_decl, cdr1_decl; Z3_func_decl nil2_decl, is_nil2_decl, cons2_decl, is_cons2_decl, car2_decl, cdr2_decl; Z3_ast nil1, nil2, t1, t2, t3, t4, f1, f2, f3, l1, l2, x, y, u, v; Z3_symbol head_tail[2] = { Z3_mk_string_symbol(ctx, "car"), Z3_mk_string_symbol(ctx, "cdr") }; Z3_sort sorts[2] = { 0, 0 }; unsigned sort_refs[2] = { 0, 0 }; Z3_constructor nil1_con, cons1_con, nil2_con, cons2_con; Z3_constructor constructors1[2], constructors2[2]; Z3_func_decl cons_accessors[2]; Z3_ast ors[2]; Z3_constructor_list clist1, clist2; Z3_constructor_list clists[2]; Z3_symbol sort_names[2] = { Z3_mk_string_symbol(ctx, "forest"), Z3_mk_string_symbol(ctx, "tree") }; printf("\nforest_example\n"); LOG_MSG("forest_example"); /* build a forest */ nil1_con = Z3_mk_constructor(ctx, Z3_mk_string_symbol(ctx, "nil1"), Z3_mk_string_symbol(ctx, "is_nil1"), 0, 0, 0, 0); sort_refs[0] = 1; /* the car of a forest is a tree */ sort_refs[1] = 0; cons1_con = Z3_mk_constructor(ctx, Z3_mk_string_symbol(ctx, "cons1"), Z3_mk_string_symbol(ctx, "is_cons1"), 2, head_tail, sorts, sort_refs); constructors1[0] = nil1_con; constructors1[1] = cons1_con; /* build a tree */ nil2_con = Z3_mk_constructor(ctx, Z3_mk_string_symbol(ctx, "nil2"), Z3_mk_string_symbol(ctx, "is_nil2"),0, 0, 0, 0); sort_refs[0] = 0; /* both branches of a tree are forests */ sort_refs[1] = 0; cons2_con = Z3_mk_constructor(ctx, Z3_mk_string_symbol(ctx, "cons2"), Z3_mk_string_symbol(ctx, "is_cons2"),2, head_tail, sorts, sort_refs); constructors2[0] = nil2_con; constructors2[1] = cons2_con; clist1 = Z3_mk_constructor_list(ctx, 2, constructors1); clist2 = Z3_mk_constructor_list(ctx, 2, constructors2); clists[0] = clist1; clists[1] = clist2; Z3_mk_datatypes(ctx, 2, sort_names, sorts, clists); forest = sorts[0]; tree = sorts[1]; Z3_query_constructor(ctx, nil1_con, 0, &nil1_decl, &is_nil1_decl, 0); Z3_query_constructor(ctx, cons1_con, 2, &cons1_decl, &is_cons1_decl, cons_accessors); car1_decl = cons_accessors[0]; cdr1_decl = cons_accessors[1]; Z3_query_constructor(ctx, nil2_con, 0, &nil2_decl, &is_nil2_decl, 0); Z3_query_constructor(ctx, cons2_con, 2, &cons2_decl, &is_cons2_decl, cons_accessors); car2_decl = cons_accessors[0]; cdr2_decl = cons_accessors[1]; (void)cdr2_decl; (void)car2_decl; (void)car1_decl; (void)cdr1_decl; Z3_del_constructor_list(ctx, clist1); Z3_del_constructor_list(ctx, clist2); Z3_del_constructor(ctx,nil1_con); Z3_del_constructor(ctx,cons1_con); Z3_del_constructor(ctx,nil2_con); Z3_del_constructor(ctx,cons2_con); nil1 = Z3_mk_app(ctx, nil1_decl, 0, 0); nil2 = Z3_mk_app(ctx, nil2_decl, 0, 0); f1 = mk_binary_app(ctx, cons1_decl, nil2, nil1); t1 = mk_binary_app(ctx, cons2_decl, nil1, nil1); t2 = mk_binary_app(ctx, cons2_decl, f1, nil1); t3 = mk_binary_app(ctx, cons2_decl, f1, f1); t4 = mk_binary_app(ctx, cons2_decl, nil1, f1); f2 = mk_binary_app(ctx, cons1_decl, t1, nil1); f3 = mk_binary_app(ctx, cons1_decl, t1, f1); (void)f3; (void)f2; (void)t4; (void)t2; /* nil != cons(nil,nil) */ prove(ctx, s, Z3_mk_not(ctx, Z3_mk_eq(ctx, nil1, f1)), true); prove(ctx, s, Z3_mk_not(ctx, Z3_mk_eq(ctx, nil2, t1)), true); /* cons(x,u) = cons(x, v) => u = v */ u = mk_var(ctx, "u", forest); v = mk_var(ctx, "v", forest); x = mk_var(ctx, "x", tree); y = mk_var(ctx, "y", tree); l1 = mk_binary_app(ctx, cons1_decl, x, u); l2 = mk_binary_app(ctx, cons1_decl, y, v); prove(ctx, s, Z3_mk_implies(ctx, Z3_mk_eq(ctx,l1,l2), Z3_mk_eq(ctx, u, v)), true); prove(ctx, s, Z3_mk_implies(ctx, Z3_mk_eq(ctx,l1,l2), Z3_mk_eq(ctx, x, y)), true); /* is_nil(u) or is_cons(u) */ ors[0] = Z3_mk_app(ctx, is_nil1_decl, 1, &u); ors[1] = Z3_mk_app(ctx, is_cons1_decl, 1, &u); prove(ctx, s, Z3_mk_or(ctx, 2, ors), true); /* occurs check u != cons(x,u) */ prove(ctx, s, Z3_mk_not(ctx, Z3_mk_eq(ctx, u, l1)), true); /* delete logical context */ del_solver(ctx, s); Z3_del_context(ctx); } /** \brief Create a binary tree datatype of the form BinTree ::= nil | node(value : Int, left : BinTree, right : BinTree) */ void binary_tree_example() { Z3_context ctx = mk_context(); Z3_solver s = mk_solver(ctx); Z3_sort cell; Z3_func_decl nil_decl, /* nil : BinTree (constructor) */ is_nil_decl, /* is_nil : BinTree -> Bool (tester, return true if the given BinTree is a nil) */ node_decl, /* node : Int, BinTree, BinTree -> BinTree (constructor) */ is_node_decl, /* is_node : BinTree -> Bool (tester, return true if the given BinTree is a node) */ value_decl, /* value : BinTree -> Int (accessor for nodes) */ left_decl, /* left : BinTree -> BinTree (accessor for nodes, retrieves the left child of a node) */ right_decl; /* right : BinTree -> BinTree (accessor for nodes, retrieves the right child of a node) */ Z3_symbol node_accessor_names[3] = { Z3_mk_string_symbol(ctx, "value"), Z3_mk_string_symbol(ctx, "left"), Z3_mk_string_symbol(ctx, "right") }; Z3_sort node_accessor_sorts[3] = { Z3_mk_int_sort(ctx), 0, 0 }; unsigned node_accessor_sort_refs[3] = { 0, 0, 0 }; Z3_constructor nil_con, node_con; Z3_constructor constructors[2]; Z3_func_decl node_accessors[3]; printf("\nbinary_tree_example\n"); LOG_MSG("binary_tree_example"); /* nil_con and node_con are auxiliary datastructures used to create the new recursive datatype BinTree */ nil_con = Z3_mk_constructor(ctx, Z3_mk_string_symbol(ctx, "nil"), Z3_mk_string_symbol(ctx, "is-nil"), 0, 0, 0, 0); node_con = Z3_mk_constructor(ctx, Z3_mk_string_symbol(ctx, "node"), Z3_mk_string_symbol(ctx, "is-cons"), 3, node_accessor_names, node_accessor_sorts, node_accessor_sort_refs); constructors[0] = nil_con; constructors[1] = node_con; /* create the new recursive datatype */ cell = Z3_mk_datatype(ctx, Z3_mk_string_symbol(ctx, "BinTree"), 2, constructors); (void)cell; /* retrieve the new declarations: constructors (nil_decl, node_decl), testers (is_nil_decl, is_cons_del), and accessors (value_decl, left_decl, right_decl */ Z3_query_constructor(ctx, nil_con, 0, &nil_decl, &is_nil_decl, 0); Z3_query_constructor(ctx, node_con, 3, &node_decl, &is_node_decl, node_accessors); value_decl = node_accessors[0]; left_decl = node_accessors[1]; right_decl = node_accessors[2]; /* delete auxiliary/helper structures */ Z3_del_constructor(ctx, nil_con); Z3_del_constructor(ctx, node_con); /* small example using the recursive datatype BinTree */ { /* create nil */ Z3_ast nil = Z3_mk_app(ctx, nil_decl, 0, 0); /* create node1 ::= node(10, nil, nil) */ Z3_ast args1[3] = { mk_int(ctx, 10), nil, nil }; Z3_ast node1 = Z3_mk_app(ctx, node_decl, 3, args1); /* create node2 ::= node(30, node1, nil) */ Z3_ast args2[3] = { mk_int(ctx, 30), node1, nil }; Z3_ast node2 = Z3_mk_app(ctx, node_decl, 3, args2); /* create node3 ::= node(20, node2, node1); */ Z3_ast args3[3] = { mk_int(ctx, 20), node2, node1 }; Z3_ast node3 = Z3_mk_app(ctx, node_decl, 3, args3); /* prove that nil != node1 */ prove(ctx, s, Z3_mk_not(ctx, Z3_mk_eq(ctx, nil, node1)), true); /* prove that nil = left(node1) */ prove(ctx, s, Z3_mk_eq(ctx, nil, mk_unary_app(ctx, left_decl, node1)), true); /* prove that node1 = right(node3) */ prove(ctx, s, Z3_mk_eq(ctx, node1, mk_unary_app(ctx, right_decl, node3)), true); /* prove that !is-nil(node2) */ prove(ctx, s, Z3_mk_not(ctx, mk_unary_app(ctx, is_nil_decl, node2)), true); /* prove that value(node2) >= 0 */ prove(ctx, s, Z3_mk_ge(ctx, mk_unary_app(ctx, value_decl, node2), mk_int(ctx, 0)), true); } /* delete logical context */ del_solver(ctx, s); Z3_del_context(ctx); } /** \brief Prove a theorem and extract, and print the proof. This example illustrates the use of #Z3_check_assumptions. */ void unsat_core_and_proof_example() { Z3_context ctx = mk_proof_context(); Z3_solver s = mk_solver(ctx); Z3_ast pa = mk_bool_var(ctx, "PredA"); Z3_ast pb = mk_bool_var(ctx, "PredB"); Z3_ast pc = mk_bool_var(ctx, "PredC"); Z3_ast pd = mk_bool_var(ctx, "PredD"); Z3_ast p1 = mk_bool_var(ctx, "P1"); Z3_ast p2 = mk_bool_var(ctx, "P2"); Z3_ast p3 = mk_bool_var(ctx, "P3"); Z3_ast p4 = mk_bool_var(ctx, "P4"); Z3_ast assumptions[4] = { Z3_mk_not(ctx, p1), Z3_mk_not(ctx, p2), Z3_mk_not(ctx, p3), Z3_mk_not(ctx, p4) }; Z3_ast args1[3] = { pa, pb, pc }; Z3_ast f1 = Z3_mk_and(ctx, 3, args1); Z3_ast args2[3] = { pa, Z3_mk_not(ctx, pb), pc }; Z3_ast f2 = Z3_mk_and(ctx, 3, args2); Z3_ast args3[2] = { Z3_mk_not(ctx, pa), Z3_mk_not(ctx, pc) }; Z3_ast f3 = Z3_mk_or(ctx, 2, args3); Z3_ast f4 = pd; Z3_ast g1[2] = { f1, p1 }; Z3_ast g2[2] = { f2, p2 }; Z3_ast g3[2] = { f3, p3 }; Z3_ast g4[2] = { f4, p4 }; Z3_lbool result; Z3_ast proof; Z3_model m = 0; unsigned i; Z3_ast_vector core; printf("\nunsat_core_and_proof_example\n"); LOG_MSG("unsat_core_and_proof_example"); Z3_solver_assert(ctx, s, Z3_mk_or(ctx, 2, g1)); Z3_solver_assert(ctx, s, Z3_mk_or(ctx, 2, g2)); Z3_solver_assert(ctx, s, Z3_mk_or(ctx, 2, g3)); Z3_solver_assert(ctx, s, Z3_mk_or(ctx, 2, g4)); result = Z3_solver_check_assumptions(ctx, s, 4, assumptions); switch (result) { case Z3_L_FALSE: core = Z3_solver_get_unsat_core(ctx, s); proof = Z3_solver_get_proof(ctx, s); printf("unsat\n"); printf("proof: %s\n", Z3_ast_to_string(ctx, proof)); printf("\ncore:\n"); for (i = 0; i < Z3_ast_vector_size(ctx, core); ++i) { printf("%s\n", Z3_ast_to_string(ctx, Z3_ast_vector_get(ctx, core, i))); } printf("\n"); break; case Z3_L_UNDEF: printf("unknown\n"); printf("potential model:\n"); m = Z3_solver_get_model(ctx, s); if (m) Z3_model_inc_ref(ctx, m); display_model(ctx, stdout, m); break; case Z3_L_TRUE: printf("sat\n"); m = Z3_solver_get_model(ctx, s); if (m) Z3_model_inc_ref(ctx, m); display_model(ctx, stdout, m); break; } /* delete logical context */ if (m) Z3_model_dec_ref(ctx, m); del_solver(ctx, s); Z3_del_context(ctx); } #define MAX_RETRACTABLE_ASSERTIONS 1024 /** \brief Very simple logical context wrapper with support for retractable constraints. A retractable constraint can be "removed" without using push/pop. */ typedef struct { Z3_context m_context; Z3_solver m_solver; // IMPORTANT: the fields m_answer_literals, m_retracted and m_num_answer_literals must be saved/restored // if push/pop operations are performed on m_context. Z3_ast m_answer_literals[MAX_RETRACTABLE_ASSERTIONS]; bool m_retracted[MAX_RETRACTABLE_ASSERTIONS]; // true if the assertion was retracted. unsigned m_num_answer_literals; } Z3_ext_context_struct; typedef Z3_ext_context_struct * Z3_ext_context; /** \brief Create a logical context wrapper with support for retractable constraints. */ Z3_ext_context mk_ext_context() { Z3_ext_context ctx = (Z3_ext_context) malloc(sizeof(Z3_ext_context_struct)); ctx->m_context = mk_context(); ctx->m_solver = mk_solver(ctx->m_context); ctx->m_num_answer_literals = 0; return ctx; } /** \brief Delete the given logical context wrapper. */ void del_ext_context(Z3_ext_context ctx) { del_solver(ctx->m_context, ctx->m_solver); Z3_del_context(ctx->m_context); free(ctx); } /** \brief Create a retractable constraint. \return An id that can be used to retract/reassert the constraint. */ unsigned assert_retractable_cnstr(Z3_ext_context ctx, Z3_ast c) { unsigned result; Z3_sort ty; Z3_ast ans_lit; Z3_ast args[2]; if (ctx->m_num_answer_literals == MAX_RETRACTABLE_ASSERTIONS) { exitf("maximum number of retractable constraints was exceeded."); } ty = Z3_mk_bool_sort(ctx->m_context); ans_lit = Z3_mk_fresh_const(ctx->m_context, "k", ty); result = ctx->m_num_answer_literals; ctx->m_answer_literals[result] = ans_lit; ctx->m_retracted[result] = false; ctx->m_num_answer_literals++; // assert: c OR (not ans_lit) args[0] = c; args[1] = Z3_mk_not(ctx->m_context, ans_lit); Z3_solver_assert(ctx->m_context, ctx->m_solver, Z3_mk_or(ctx->m_context, 2, args)); return result; } /** \brief Retract an constraint asserted using #assert_retractable_cnstr. */ void retract_cnstr(Z3_ext_context ctx, unsigned id) { if (id >= ctx->m_num_answer_literals) { exitf("invalid constraint id."); } ctx->m_retracted[id] = true; } /** \brief Reassert a constraint retracted using #retract_cnstr. */ void reassert_cnstr(Z3_ext_context ctx, unsigned id) { if (id >= ctx->m_num_answer_literals) { exitf("invalid constraint id."); } ctx->m_retracted[id] = false; } /** \brief Check whether the logical context wrapper with support for retractable assertions is feasible or not. */ Z3_lbool ext_check(Z3_ext_context ctx) { Z3_lbool result; unsigned num_assumptions = 0; Z3_ast assumptions[MAX_RETRACTABLE_ASSERTIONS]; Z3_ast_vector core; unsigned core_size; unsigned i; for (i = 0; i < ctx->m_num_answer_literals; i++) { if (ctx->m_retracted[i] == false) { // Since the answer literal was not retracted, we added it as an assumption. // Recall that we assert (C \/ (not ans_lit)). Therefore, adding ans_lit as an assumption has the effect of "asserting" C. // If the constraint was "retracted" (ctx->m_retracted[i] == Z3_true), then we don't really need to add (not ans_lit) as an assumption. assumptions[num_assumptions] = ctx->m_answer_literals[i]; num_assumptions ++; } } result = Z3_solver_check_assumptions(ctx->m_context, ctx->m_solver, num_assumptions, assumptions); if (result == Z3_L_FALSE) { // Display the UNSAT core printf("unsat core: "); core = Z3_solver_get_unsat_core(ctx->m_context, ctx->m_solver); core_size = Z3_ast_vector_size(ctx->m_context, core); for (i = 0; i < core_size; i++) { // In this example, we display the core based on the assertion ids. unsigned j; for (j = 0; j < ctx->m_num_answer_literals; j++) { if (Z3_ast_vector_get(ctx->m_context, core, i) == ctx->m_answer_literals[j]) { printf("%d ", j); break; } } if (j == ctx->m_num_answer_literals) { exitf("bug in Z3, the core contains something that is not an assumption."); } } printf("\n"); } return result; } /** \brief Simple example using the functions: #mk_ext_context, #assert_retractable_cnstr, #retract_cnstr, #reassert_cnstr and #del_ext_context. */ void incremental_example1() { Z3_ext_context ext_ctx = mk_ext_context(); Z3_context ctx = ext_ctx->m_context; Z3_ast x, y, z, two, one; unsigned c1, c2, c3, c4; Z3_lbool result; printf("\nincremental_example1\n"); LOG_MSG("incremental_example1"); x = mk_int_var(ctx, "x"); y = mk_int_var(ctx, "y"); z = mk_int_var(ctx, "z"); two = mk_int(ctx, 2); one = mk_int(ctx, 1); /* assert x < y */ c1 = assert_retractable_cnstr(ext_ctx, Z3_mk_lt(ctx, x, y)); /* assert x = z */ c2 = assert_retractable_cnstr(ext_ctx, Z3_mk_eq(ctx, x, z)); /* assert x > 2 */ c3 = assert_retractable_cnstr(ext_ctx, Z3_mk_gt(ctx, x, two)); /* assert y < 1 */ c4 = assert_retractable_cnstr(ext_ctx, Z3_mk_lt(ctx, y, one)); (void)c1; result = ext_check(ext_ctx); if (result != Z3_L_FALSE) exitf("bug in Z3"); printf("unsat\n"); retract_cnstr(ext_ctx, c4); result = ext_check(ext_ctx); if (result != Z3_L_TRUE) exitf("bug in Z3"); printf("sat\n"); reassert_cnstr(ext_ctx, c4); result = ext_check(ext_ctx); if (result != Z3_L_FALSE) exitf("bug in Z3"); printf("unsat\n"); retract_cnstr(ext_ctx, c2); result = ext_check(ext_ctx); if (result != Z3_L_FALSE) exitf("bug in Z3"); printf("unsat\n"); retract_cnstr(ext_ctx, c3); result = ext_check(ext_ctx); if (result != Z3_L_TRUE) exitf("bug in Z3"); printf("sat\n"); del_ext_context(ext_ctx); } /** \brief Simple example showing how to use reference counters in Z3 to manage memory efficiently. */ void reference_counter_example() { Z3_config cfg; Z3_context ctx; Z3_solver s; Z3_sort ty; Z3_ast x, y, x_xor_y; Z3_symbol sx, sy; printf("\nreference_counter_example\n"); LOG_MSG("reference_counter_example"); cfg = Z3_mk_config(); Z3_set_param_value(cfg, "model", "true"); // Create a Z3 context where the user is responsible for managing // Z3_ast reference counters. ctx = Z3_mk_context_rc(cfg); Z3_del_config(cfg); s = mk_solver(ctx); Z3_solver_inc_ref(ctx, s); ty = Z3_mk_bool_sort(ctx); Z3_inc_ref(ctx, Z3_sort_to_ast(ctx, ty)); // Z3_sort_to_ast(ty) is just syntax sugar for ((Z3_ast) ty) sx = Z3_mk_string_symbol(ctx, "x"); // Z3_symbol is not a Z3_ast. No reference counting is needed. x = Z3_mk_const(ctx, sx, ty); Z3_inc_ref(ctx, x); sy = Z3_mk_string_symbol(ctx, "y"); y = Z3_mk_const(ctx, sy, ty); Z3_inc_ref(ctx, y); // ty is not needed anymore. Z3_dec_ref(ctx, Z3_sort_to_ast(ctx, ty)); x_xor_y = Z3_mk_xor(ctx, x, y); Z3_inc_ref(ctx, x_xor_y); // x and y are not needed anymore. Z3_dec_ref(ctx, x); Z3_dec_ref(ctx, y); Z3_solver_assert(ctx, s, x_xor_y); // x_or_y is not needed anymore. Z3_dec_ref(ctx, x_xor_y); printf("model for: x xor y\n"); check(ctx, s, Z3_L_TRUE); // Test push & pop Z3_solver_push(ctx, s); Z3_solver_pop(ctx, s, 1); Z3_solver_dec_ref(ctx, s); del_solver(ctx, s); Z3_del_context(ctx); } /** \brief Demonstrates how to use SMT2 parser. */ void smt2parser_example() { Z3_context ctx; Z3_ast_vector fs; printf("\nsmt2parser_example\n"); LOG_MSG("smt2parser_example"); ctx = mk_context(); fs = Z3_parse_smtlib2_string(ctx, "(declare-fun a () (_ BitVec 8)) (assert (bvuge a #x10)) (assert (bvule a #xf0))", 0, 0, 0, 0, 0, 0); Z3_ast_vector_inc_ref(ctx, fs); printf("formulas: %s\n", Z3_ast_vector_to_string(ctx, fs)); Z3_ast_vector_dec_ref(ctx, fs); Z3_del_context(ctx); } /** \brief Demonstrates how to use the function \c Z3_substitute to replace subexpressions in a Z3 AST. */ void substitute_example() { Z3_context ctx; Z3_sort int_ty; Z3_ast a, b; Z3_func_decl f; Z3_func_decl g; Z3_ast fab, ga, ffabga, r; printf("\nsubstitute_example\n"); LOG_MSG("substitute_example"); ctx = mk_context(); int_ty = Z3_mk_int_sort(ctx); a = mk_int_var(ctx,"a"); b = mk_int_var(ctx,"b"); { Z3_sort f_domain[2] = { int_ty, int_ty }; f = Z3_mk_func_decl(ctx, Z3_mk_string_symbol(ctx, "f"), 2, f_domain, int_ty); } g = Z3_mk_func_decl(ctx, Z3_mk_string_symbol(ctx, "g"), 1, &int_ty, int_ty); { Z3_ast args[2] = { a, b }; fab = Z3_mk_app(ctx, f, 2, args); } ga = Z3_mk_app(ctx, g, 1, &a); { Z3_ast args[2] = { fab, ga }; ffabga = Z3_mk_app(ctx, f, 2, args); } // Replace b -> 0, g(a) -> 1 in f(f(a, b), g(a)) { Z3_ast zero = Z3_mk_numeral(ctx, "0", int_ty); Z3_ast one = Z3_mk_numeral(ctx, "1", int_ty); Z3_ast from[2] = { b, ga }; Z3_ast to[2] = { zero, one }; r = Z3_substitute(ctx, ffabga, 2, from, to); } // Display r printf("substitution result: %s\n", Z3_ast_to_string(ctx, r)); Z3_del_context(ctx); } /** \brief Demonstrates how to use the function \c Z3_substitute_vars to replace (free) variables with expressions in a Z3 AST. */ void substitute_vars_example() { Z3_context ctx; Z3_sort int_ty; Z3_ast x0, x1; Z3_ast a, b, gb; Z3_func_decl f; Z3_func_decl g; Z3_ast f01, ff010, r; printf("\nsubstitute_vars_example\n"); LOG_MSG("substitute_vars_example"); ctx = mk_context(); int_ty = Z3_mk_int_sort(ctx); x0 = Z3_mk_bound(ctx, 0, int_ty); x1 = Z3_mk_bound(ctx, 1, int_ty); { Z3_sort f_domain[2] = { int_ty, int_ty }; f = Z3_mk_func_decl(ctx, Z3_mk_string_symbol(ctx, "f"), 2, f_domain, int_ty); } g = Z3_mk_func_decl(ctx, Z3_mk_string_symbol(ctx, "g"), 1, &int_ty, int_ty); { Z3_ast args[2] = { x0, x1 }; f01 = Z3_mk_app(ctx, f, 2, args); } { Z3_ast args[2] = { f01, x0 }; ff010 = Z3_mk_app(ctx, f, 2, args); } a = mk_int_var(ctx, "a"); b = mk_int_var(ctx, "b"); gb = Z3_mk_app(ctx, g, 1, &b); // Replace x0 -> a, x1 -> g(b) in f(f(x0,x1),x0) { Z3_ast to[2] = { a, gb }; r = Z3_substitute_vars(ctx, ff010, 2, to); } // Display r printf("substitution result: %s\n", Z3_ast_to_string(ctx, r)); Z3_del_context(ctx); } /** \brief Demonstrates some basic features of the FloatingPoint theory. */ void fpa_example() { Z3_config cfg; Z3_context ctx; Z3_solver s; Z3_sort double_sort, rm_sort; Z3_symbol s_rm, s_x, s_y, s_x_plus_y; Z3_ast rm, x, y, n, x_plus_y, c1, c2, c3, c4, c5; Z3_ast args[2], args2[2], and_args[3], args3[3]; printf("\nFPA-example\n"); LOG_MSG("FPA-example"); cfg = Z3_mk_config(); ctx = Z3_mk_context(cfg); s = mk_solver(ctx); Z3_del_config(cfg); double_sort = Z3_mk_fpa_sort(ctx, 11, 53); rm_sort = Z3_mk_fpa_rounding_mode_sort(ctx); // Show that there are x, y s.t. (x + y) = 42.0 (with rounding mode). s_rm = Z3_mk_string_symbol(ctx, "rm"); rm = Z3_mk_const(ctx, s_rm, rm_sort); s_x = Z3_mk_string_symbol(ctx, "x"); s_y = Z3_mk_string_symbol(ctx, "y"); x = Z3_mk_const(ctx, s_x, double_sort); y = Z3_mk_const(ctx, s_y, double_sort); n = Z3_mk_fpa_numeral_double(ctx, 42.0, double_sort); s_x_plus_y = Z3_mk_string_symbol(ctx, "x_plus_y"); x_plus_y = Z3_mk_const(ctx, s_x_plus_y, double_sort); c1 = Z3_mk_eq(ctx, x_plus_y, Z3_mk_fpa_add(ctx, rm, x, y)); args[0] = c1; args[1] = Z3_mk_eq(ctx, x_plus_y, n); c2 = Z3_mk_and(ctx, 2, (Z3_ast*)&args); args2[0] = c2; args2[1] = Z3_mk_not(ctx, Z3_mk_eq(ctx, rm, Z3_mk_fpa_rtz(ctx))); c3 = Z3_mk_and(ctx, 2, (Z3_ast*)&args2); and_args[0] = Z3_mk_not(ctx, Z3_mk_fpa_is_zero(ctx, y)); and_args[1] = Z3_mk_not(ctx, Z3_mk_fpa_is_nan(ctx, y)); and_args[2] = Z3_mk_not(ctx, Z3_mk_fpa_is_infinite(ctx, y)); args3[0] = c3; args3[1] = Z3_mk_and(ctx, 3, and_args); c4 = Z3_mk_and(ctx, 2, (Z3_ast*)&args3); printf("c4: %s\n", Z3_ast_to_string(ctx, c4)); Z3_solver_push(ctx, s); Z3_solver_assert(ctx, s, c4); check(ctx, s, Z3_L_TRUE); Z3_solver_pop(ctx, s, 1); // Show that the following are equal: // (fp #b0 #b10000000001 #xc000000000000) // ((_ to_fp 11 53) #x401c000000000000)) // ((_ to_fp 11 53) RTZ 1.75 2))) // ((_ to_fp 11 53) RTZ 7.0))) Z3_solver_push(ctx, s); c1 = Z3_mk_fpa_fp(ctx, Z3_mk_numeral(ctx, "0", Z3_mk_bv_sort(ctx, 1)), Z3_mk_numeral(ctx, "1025", Z3_mk_bv_sort(ctx, 11)), Z3_mk_numeral(ctx, "3377699720527872", Z3_mk_bv_sort(ctx, 52))); c2 = Z3_mk_fpa_to_fp_bv(ctx, Z3_mk_numeral(ctx, "4619567317775286272", Z3_mk_bv_sort(ctx, 64)), Z3_mk_fpa_sort(ctx, 11, 53)); c3 = Z3_mk_fpa_to_fp_int_real(ctx, Z3_mk_fpa_rtz(ctx), Z3_mk_numeral(ctx, "2", Z3_mk_int_sort(ctx)), /* exponent */ Z3_mk_numeral(ctx, "1.75", Z3_mk_real_sort(ctx)), /* significand */ Z3_mk_fpa_sort(ctx, 11, 53)); c4 = Z3_mk_fpa_to_fp_real(ctx, Z3_mk_fpa_rtz(ctx), Z3_mk_numeral(ctx, "7.0", Z3_mk_real_sort(ctx)), Z3_mk_fpa_sort(ctx, 11, 53)); args3[0] = Z3_mk_eq(ctx, c1, c2); args3[1] = Z3_mk_eq(ctx, c1, c3); args3[2] = Z3_mk_eq(ctx, c1, c4); c5 = Z3_mk_and(ctx, 3, args3); printf("c5: %s\n", Z3_ast_to_string(ctx, c5)); Z3_solver_assert(ctx, s, c5); check(ctx, s, Z3_L_TRUE); Z3_solver_pop(ctx, s, 1); del_solver(ctx, s); Z3_del_context(ctx); } /** \brief Demonstrates some basic features of model construction */ void mk_model_example() { Z3_context ctx; Z3_model m; Z3_sort intSort; Z3_symbol aSymbol, bSymbol, cSymbol; Z3_func_decl aFuncDecl, bFuncDecl, cFuncDecl; Z3_ast aApp, bApp, cApp; Z3_sort int2intArraySort; Z3_ast zeroNumeral, oneNumeral, twoNumeral, threeNumeral, fourNumeral; Z3_sort arrayDomain[1]; Z3_func_decl cAsFuncDecl; Z3_func_interp cAsFuncInterp; Z3_ast_vector zeroArgs; Z3_ast_vector oneArgs; Z3_ast cFuncDeclAsArray; Z3_string modelAsString; printf("\nmk_model_example\n"); ctx = mk_context(); // Construct empty model m = Z3_mk_model(ctx); Z3_model_inc_ref(ctx, m); // Create constants "a" and "b" intSort = Z3_mk_int_sort(ctx); aSymbol = Z3_mk_string_symbol(ctx, "a"); aFuncDecl = Z3_mk_func_decl(ctx, aSymbol, /*domain_size=*/0, /*domain=*/NULL, /*range=*/intSort); aApp = Z3_mk_app(ctx, aFuncDecl, /*num_args=*/0, /*args=*/NULL); bSymbol = Z3_mk_string_symbol(ctx, "b"); bFuncDecl = Z3_mk_func_decl(ctx, bSymbol, /*domain_size=*/0, /*domain=*/NULL, /*range=*/intSort); bApp = Z3_mk_app(ctx, bFuncDecl, /*num_args=*/0, /*args=*/NULL); // Create array "c" that maps int to int. cSymbol = Z3_mk_string_symbol(ctx, "c"); int2intArraySort = Z3_mk_array_sort(ctx, /*domain=*/intSort, /*range=*/intSort); cFuncDecl = Z3_mk_func_decl(ctx, cSymbol, /*domain_size=*/0, /*domain=*/NULL, /*range=*/int2intArraySort); cApp = Z3_mk_app(ctx, cFuncDecl, /*num_args=*/0, /*args=*/NULL); // Create numerals to be used in model zeroNumeral = Z3_mk_int(ctx, 0, intSort); oneNumeral = Z3_mk_int(ctx, 1, intSort); twoNumeral = Z3_mk_int(ctx, 2, intSort); threeNumeral = Z3_mk_int(ctx, 3, intSort); fourNumeral = Z3_mk_int(ctx, 4, intSort); // Add assignments to model // a == 1 Z3_add_const_interp(ctx, m, aFuncDecl, oneNumeral); // b == 2 Z3_add_const_interp(ctx, m, bFuncDecl, twoNumeral); // Create a fresh function that represents // reading from array. arrayDomain[0] = intSort; cAsFuncDecl = Z3_mk_fresh_func_decl(ctx, /*prefix=*/"", /*domain_size*/ 1, /*domain=*/arrayDomain, /*sort=*/intSort); // Create function interpretation with default // value of "0". cAsFuncInterp = Z3_add_func_interp(ctx, m, cAsFuncDecl, /*default_value=*/zeroNumeral); Z3_func_interp_inc_ref(ctx, cAsFuncInterp); // Add [0] = 3 zeroArgs = Z3_mk_ast_vector(ctx); Z3_ast_vector_inc_ref(ctx, zeroArgs); Z3_ast_vector_push(ctx, zeroArgs, zeroNumeral); Z3_func_interp_add_entry(ctx, cAsFuncInterp, zeroArgs, threeNumeral); // Add [1] = 4 oneArgs = Z3_mk_ast_vector(ctx); Z3_ast_vector_inc_ref(ctx, oneArgs); Z3_ast_vector_push(ctx, oneArgs, oneNumeral); Z3_func_interp_add_entry(ctx, cAsFuncInterp, oneArgs, fourNumeral); // Now use the `(_ as_array)` to associate // the `cAsFuncInterp` with the `cFuncDecl` // in the model cFuncDeclAsArray = Z3_mk_as_array(ctx, cAsFuncDecl); Z3_add_const_interp(ctx, m, cFuncDecl, cFuncDeclAsArray); // Print the model modelAsString = Z3_model_to_string(ctx, m); printf("Model:\n%s\n", modelAsString); // Check the interpretations we expect to be present // are. { Z3_func_decl expectedInterpretations[3] = {aFuncDecl, bFuncDecl, cFuncDecl}; int index; for (index = 0; index < sizeof(expectedInterpretations) / sizeof(Z3_func_decl); ++index) { Z3_func_decl d = expectedInterpretations[index]; if (Z3_model_has_interp(ctx, m, d)) { printf("Found interpretation for \"%s\"\n", Z3_ast_to_string(ctx, Z3_func_decl_to_ast(ctx, d))); } else { printf("Missing interpretation"); exit(1); } } } { // Evaluate a + b under model Z3_ast addArgs[] = {aApp, bApp}; Z3_ast aPlusB = Z3_mk_add(ctx, /*num_args=*/2, /*args=*/addArgs); Z3_ast aPlusBEval = NULL; bool aPlusBEvalSuccess = Z3_model_eval(ctx, m, aPlusB, /*model_completion=*/false, &aPlusBEval); if (aPlusBEvalSuccess != true) { printf("Failed to evaluate model\n"); exit(1); } { int aPlusBValue = 0; bool getAPlusBValueSuccess = Z3_get_numeral_int(ctx, aPlusBEval, &aPlusBValue); if (getAPlusBValueSuccess != true) { printf("Failed to get integer value for a+b\n"); exit(1); } printf("Evaluated a + b = %d\n", aPlusBValue); if (aPlusBValue != 3) { printf("a+b did not evaluate to expected value\n"); exit(1); } } } { // Evaluate c[0] + c[1] + c[2] under model Z3_ast c0 = Z3_mk_select(ctx, cApp, zeroNumeral); Z3_ast c1 = Z3_mk_select(ctx, cApp, oneNumeral); Z3_ast c2 = Z3_mk_select(ctx, cApp, twoNumeral); Z3_ast arrayAddArgs[] = {c0, c1, c2}; Z3_ast arrayAdd = Z3_mk_add(ctx, /*num_args=*/3, /*args=*/arrayAddArgs); Z3_ast arrayAddEval = NULL; bool arrayAddEvalSuccess = Z3_model_eval(ctx, m, arrayAdd, /*model_completion=*/false, &arrayAddEval); if (arrayAddEvalSuccess != true) { printf("Failed to evaluate model\n"); exit(1); } { int arrayAddValue = 0; bool getArrayAddValueSuccess = Z3_get_numeral_int(ctx, arrayAddEval, &arrayAddValue); if (getArrayAddValueSuccess != true) { printf("Failed to get integer value for c[0] + c[1] + c[2]\n"); exit(1); } printf("Evaluated c[0] + c[1] + c[2] = %d\n", arrayAddValue); if (arrayAddValue != 7) { printf("c[0] + c[1] + c[2] did not evaluate to expected value\n"); exit(1); } } } Z3_ast_vector_dec_ref(ctx, oneArgs); Z3_ast_vector_dec_ref(ctx, zeroArgs); Z3_func_interp_dec_ref(ctx, cAsFuncInterp); Z3_model_dec_ref(ctx, m); Z3_del_context(ctx); } /*@}*/ /*@}*/ int main() { #ifdef LOG_Z3_CALLS Z3_open_log("z3.log"); #endif display_version(); simple_example(); demorgan(); find_model_example1(); find_model_example2(); prove_example1(); prove_example2(); push_pop_example1(); quantifier_example1(); array_example1(); array_example2(); array_example3(); tuple_example1(); bitvector_example1(); bitvector_example2(); eval_example1(); two_contexts_example1(); error_code_example1(); error_code_example2(); parser_example2(); parser_example3(); parser_example5(); numeral_example(); ite_example(); list_example(); tree_example(); forest_example(); binary_tree_example(); enum_example(); unsat_core_and_proof_example(); incremental_example1(); reference_counter_example(); smt2parser_example(); substitute_example(); substitute_vars_example(); fpa_example(); mk_model_example(); return 0; } z3-z3-4.8.7/examples/dotnet/000077500000000000000000000000001356505360400155425ustar00rootroot00000000000000z3-z3-4.8.7/examples/dotnet/CMakeLists.txt000066400000000000000000000024341356505360400203050ustar00rootroot00000000000000find_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 ${PROJECT_BINARY_DIR}/libz3.so) set(z3_dotnet_test_manual_copy_deps ${PROJECT_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} ${PROJECT_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 ${PROJECT_BINARY_DIR}/dotnet/netcoreapp2.0/dotnet.deps.json ) add_dependencies(z3_dotnet_test_manual_copy_assembly_hack BUILD_dotnet) endif() z3-z3-4.8.7/examples/dotnet/Program.cs000066400000000000000000002426561356505360400175170ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: Program.cs Abstract: Z3 Managed API: Example program Author: Christoph Wintersteiger (cwinter) 2012-03-16 Notes: --*/ using System; using System.Collections; using System.Collections.Generic; using Microsoft.Z3; namespace test_mapi { class Program { class TestFailedException : Exception { public TestFailedException() : base("Check FAILED") { } }; /// /// Create axiom: function f is injective in the i-th argument. /// /// /// The following axiom is produced: /// /// forall (x_0, ..., x_n) finv(f(x_0, ..., x_i, ..., x_{n-1})) = x_i /// /// Where, finvis a fresh function declaration. /// public static BoolExpr InjAxiom(Context ctx, FuncDecl f, int i) { Sort[] domain = f.Domain; uint sz = f.DomainSize; if (i >= sz) { Console.WriteLine("failed to create inj axiom"); return null; } /* declare the i-th inverse of f: finv */ Sort finv_domain = f.Range; Sort finv_range = domain[i]; FuncDecl finv = ctx.MkFuncDecl("f_fresh", finv_domain, finv_range); /* allocate temporary arrays */ Expr[] xs = new Expr[sz]; Symbol[] names = new Symbol[sz]; Sort[] types = new Sort[sz]; /* fill types, names and xs */ for (uint j = 0; j < sz; j++) { types[j] = domain[j]; names[j] = ctx.MkSymbol(String.Format("x_{0}", j)); xs[j] = ctx.MkBound(j, types[j]); } Expr x_i = xs[i]; /* create f(x_0, ..., x_i, ..., x_{n-1}) */ Expr fxs = f[xs]; /* create f_inv(f(x_0, ..., x_i, ..., x_{n-1})) */ Expr finv_fxs = finv[fxs]; /* create finv(f(x_0, ..., x_i, ..., x_{n-1})) = x_i */ Expr eq = ctx.MkEq(finv_fxs, x_i); /* use f(x_0, ..., x_i, ..., x_{n-1}) as the pattern for the quantifier */ Pattern p = ctx.MkPattern(new Expr[] { fxs }); /* create & assert quantifier */ BoolExpr q = ctx.MkForall( types, /* types of quantified variables */ names, /* names of quantified variables */ eq, 1, new Pattern[] { p } /* patterns */); return q; } /// /// Create axiom: function f is injective in the i-th argument. /// /// /// The following axiom is produced: /// /// forall (x_0, ..., x_n) finv(f(x_0, ..., x_i, ..., x_{n-1})) = x_i /// /// Where, finvis a fresh function declaration. /// public static BoolExpr InjAxiomAbs(Context ctx, FuncDecl f, int i) { Sort[] domain = f.Domain; uint sz = f.DomainSize; if (i >= sz) { Console.WriteLine("failed to create inj axiom"); return null; } /* declare the i-th inverse of f: finv */ Sort finv_domain = f.Range; Sort finv_range = domain[i]; FuncDecl finv = ctx.MkFuncDecl("f_fresh", finv_domain, finv_range); /* allocate temporary arrays */ Expr[] xs = new Expr[sz]; /* fill types, names and xs */ for (uint j = 0; j < sz; j++) { xs[j] = ctx.MkConst(String.Format("x_{0}", j), domain[j]); } Expr x_i = xs[i]; /* create f(x_0, ..., x_i, ..., x_{n-1}) */ Expr fxs = f[xs]; /* create f_inv(f(x_0, ..., x_i, ..., x_{n-1})) */ Expr finv_fxs = finv[fxs]; /* create finv(f(x_0, ..., x_i, ..., x_{n-1})) = x_i */ Expr eq = ctx.MkEq(finv_fxs, x_i); /* use f(x_0, ..., x_i, ..., x_{n-1}) as the pattern for the quantifier */ Pattern p = ctx.MkPattern(new Expr[] { fxs }); /* create & assert quantifier */ BoolExpr q = ctx.MkForall( xs, /* types of quantified variables */ eq, /* names of quantified variables */ 1, new Pattern[] { p } /* patterns */); return q; } /// /// Assert the axiom: function f is commutative. /// /// /// This example uses the SMT-LIB parser to simplify the axiom construction. /// private static BoolExpr CommAxiom(Context ctx, FuncDecl f) { Sort t = f.Range; Sort[] dom = f.Domain; if (dom.Length != 2 || !t.Equals(dom[0]) || !t.Equals(dom[1])) { Console.WriteLine("{0} {1} {2} {3}", dom.Length, dom[0], dom[1], t); throw new Exception("function must be binary, and argument types must be equal to return type"); } string bench = string.Format("(assert (forall ((x {0}) (y {1})) (= ({2} x y) ({3} y x))))", t.Name, t.Name, f.Name, f.Name); return ctx.ParseSMTLIB2String(bench, new Symbol[] { t.Name }, new Sort[] { t }, new Symbol[] { f.Name }, new FuncDecl[] { f })[0]; } /// /// "Hello world" example: create a Z3 logical context, and delete it. /// public static void SimpleExample() { Console.WriteLine("SimpleExample"); using (Context ctx = new Context()) { /* do something with the context */ /* be kind to dispose manually and not wait for the GC. */ ctx.Dispose(); } } static Model Check(Context ctx, BoolExpr f, Status sat) { Solver s = ctx.MkSolver(); s.Assert(f); if (s.Check() != sat) throw new TestFailedException(); if (sat == Status.SATISFIABLE) return s.Model; else return null; } static void SolveTactical(Context ctx, Tactic t, Goal g, Status sat) { Solver s = ctx.MkSolver(t); Console.WriteLine("\nTactical solver: " + s); foreach (BoolExpr a in g.Formulas) s.Assert(a); Console.WriteLine("Solver: " + s); if (s.Check() != sat) throw new TestFailedException(); } static ApplyResult ApplyTactic(Context ctx, Tactic t, Goal g) { Console.WriteLine("\nGoal: " + g); ApplyResult res = t.Apply(g); Console.WriteLine("Application result: " + res); Status q = Status.UNKNOWN; foreach (Goal sg in res.Subgoals) if (sg.IsDecidedSat) q = Status.SATISFIABLE; else if (sg.IsDecidedUnsat) q = Status.UNSATISFIABLE; switch (q) { case Status.UNKNOWN: Console.WriteLine("Tactic result: Undecided"); break; case Status.SATISFIABLE: Console.WriteLine("Tactic result: SAT"); break; case Status.UNSATISFIABLE: Console.WriteLine("Tactic result: UNSAT"); break; } return res; } static void Prove(Context ctx, BoolExpr f, bool useMBQI = false, params BoolExpr[] assumptions) { Console.WriteLine("Proving: " + f); Solver s = ctx.MkSolver(); Params p = ctx.MkParams(); p.Add("mbqi", useMBQI); s.Parameters = p; foreach (BoolExpr a in assumptions) s.Assert(a); s.Assert(ctx.MkNot(f)); Status q = s.Check(); switch (q) { case Status.UNKNOWN: Console.WriteLine("Unknown because: " + s.ReasonUnknown); break; case Status.SATISFIABLE: throw new TestFailedException(); case Status.UNSATISFIABLE: Console.WriteLine("OK, proof: " + s.Proof); break; } } static void Disprove(Context ctx, BoolExpr f, bool useMBQI = false, params BoolExpr[] assumptions) { Console.WriteLine("Disproving: " + f); Solver s = ctx.MkSolver(); Params p = ctx.MkParams(); p.Add("mbqi", useMBQI); s.Parameters = p; foreach (BoolExpr a in assumptions) s.Assert(a); s.Assert(ctx.MkNot(f)); Status q = s.Check(); switch (q) { case Status.UNKNOWN: Console.WriteLine("Unknown because: " + s.ReasonUnknown); break; case Status.SATISFIABLE: Console.WriteLine("OK, model: " + s.Model); break; case Status.UNSATISFIABLE: throw new TestFailedException(); } } static void ModelConverterTest(Context ctx) { Console.WriteLine("ModelConverterTest"); ArithExpr xr = (ArithExpr)ctx.MkConst(ctx.MkSymbol("x"), ctx.MkRealSort()); ArithExpr yr = (ArithExpr)ctx.MkConst(ctx.MkSymbol("y"), ctx.MkRealSort()); Goal g4 = ctx.MkGoal(true); g4.Assert(ctx.MkGt(xr, ctx.MkReal(10, 1))); g4.Assert(ctx.MkEq(yr, ctx.MkAdd(xr, ctx.MkReal(1, 1)))); g4.Assert(ctx.MkGt(yr, ctx.MkReal(1, 1))); ApplyResult ar = ApplyTactic(ctx, ctx.MkTactic("simplify"), g4); if (ar.NumSubgoals == 1 && (ar.Subgoals[0].IsDecidedSat || ar.Subgoals[0].IsDecidedUnsat)) throw new TestFailedException(); ar = ApplyTactic(ctx, ctx.AndThen(ctx.MkTactic("simplify"), ctx.MkTactic("solve-eqs")), g4); if (ar.NumSubgoals == 1 && (ar.Subgoals[0].IsDecidedSat || ar.Subgoals[0].IsDecidedUnsat)) throw new TestFailedException(); Solver s = ctx.MkSolver(); foreach (BoolExpr e in ar.Subgoals[0].Formulas) s.Assert(e); Status q = s.Check(); Console.WriteLine("Solver says: " + q); Console.WriteLine("Model: \n" + s.Model); if (q != Status.SATISFIABLE) throw new TestFailedException(); } /// /// A simple array example. /// /// static void ArrayExample1(Context ctx) { Console.WriteLine("ArrayExample1"); Goal g = ctx.MkGoal(true); ArraySort asort = ctx.MkArraySort(ctx.IntSort, ctx.MkBitVecSort(32)); ArrayExpr aex = (ArrayExpr)ctx.MkConst(ctx.MkSymbol("MyArray"), asort); Expr sel = ctx.MkSelect(aex, ctx.MkInt(0)); g.Assert(ctx.MkEq(sel, ctx.MkBV(42, 32))); Symbol xs = ctx.MkSymbol("x"); IntExpr xc = (IntExpr)ctx.MkConst(xs, ctx.IntSort); Symbol fname = ctx.MkSymbol("f"); Sort[] domain = { ctx.IntSort }; FuncDecl fd = ctx.MkFuncDecl(fname, domain, ctx.IntSort); Expr[] fargs = { ctx.MkConst(xs, ctx.IntSort) }; IntExpr fapp = (IntExpr)ctx.MkApp(fd, fargs); g.Assert(ctx.MkEq(ctx.MkAdd(xc, fapp), ctx.MkInt(123))); Solver s = ctx.MkSolver(); foreach (BoolExpr a in g.Formulas) s.Assert(a); Console.WriteLine("Solver: " + s); Status q = s.Check(); Console.WriteLine("Status: " + q); if (q != Status.SATISFIABLE) throw new TestFailedException(); Console.WriteLine("Model = " + s.Model); Console.WriteLine("Interpretation of MyArray:\n" + s.Model.ConstInterp(aex.FuncDecl)); Console.WriteLine("Interpretation of x:\n" + s.Model.ConstInterp(xc)); Console.WriteLine("Interpretation of f:\n" + s.Model.FuncInterp(fd)); Console.WriteLine("Interpretation of MyArray as Term:\n" + s.Model.ConstInterp(aex.FuncDecl)); } /// /// Prove store(a1, i1, v1) = store(a2, i2, v2) implies (i1 = i3 or i2 = i3 or select(a1, i3) = select(a2, i3)). /// /// This example demonstrates how to use the array theory. public static void ArrayExample2(Context ctx) { Console.WriteLine("ArrayExample2"); Sort int_type = ctx.IntSort; Sort array_type = ctx.MkArraySort(int_type, int_type); ArrayExpr a1 = (ArrayExpr)ctx.MkConst("a1", array_type); ArrayExpr a2 = ctx.MkArrayConst("a2", int_type, int_type); Expr i1 = ctx.MkConst("i1", int_type); Expr i2 = ctx.MkConst("i2", int_type); Expr i3 = ctx.MkConst("i3", int_type); Expr v1 = ctx.MkConst("v1", int_type); Expr v2 = ctx.MkConst("v2", int_type); Expr st1 = ctx.MkStore(a1, i1, v1); Expr st2 = ctx.MkStore(a2, i2, v2); Expr sel1 = ctx.MkSelect(a1, i3); Expr sel2 = ctx.MkSelect(a2, i3); /* create antecedent */ BoolExpr antecedent = ctx.MkEq(st1, st2); /* create consequent: i1 = i3 or i2 = i3 or select(a1, i3) = select(a2, i3) */ BoolExpr consequent = ctx.MkOr(new BoolExpr[] { ctx.MkEq(i1, i3), ctx.MkEq(i2, i3), ctx.MkEq(sel1, sel2) }); /* prove store(a1, i1, v1) = store(a2, i2, v2) implies (i1 = i3 or i2 = i3 or select(a1, i3) = select(a2, i3)) */ BoolExpr thm = ctx.MkImplies(antecedent, consequent); Console.WriteLine("prove: store(a1, i1, v1) = store(a2, i2, v2) implies (i1 = i3 or i2 = i3 or select(a1, i3) = select(a2, i3))"); Console.WriteLine("{0}", (thm)); Prove(ctx, thm); } /// /// Show that distinct(a_0, ... , a_n) is /// unsatisfiable when a_i's are arrays from boolean to /// boolean and n > 4. /// /// This example also shows how to use the distinct construct. public static void ArrayExample3(Context ctx) { Console.WriteLine("ArrayExample3"); for (int n = 2; n <= 5; n++) { Console.WriteLine("n = {0}", n); Sort bool_type = ctx.MkBoolSort(); Sort array_type = ctx.MkArraySort(bool_type, bool_type); Expr[] a = new Expr[n]; /* create arrays */ for (int i = 0; i < n; i++) { a[i] = ctx.MkConst(String.Format("array_{0}", i), array_type); } /* assert distinct(a[0], ..., a[n]) */ BoolExpr d = ctx.MkDistinct(a); Console.WriteLine("{0}", (d)); /* context is satisfiable if n < 5 */ Model model = Check(ctx, d, n < 5 ? Status.SATISFIABLE : Status.UNSATISFIABLE); if (n < 5) { for (int i = 0; i < n; i++) { Console.WriteLine("{0} = {1}", a[i], model.Evaluate(a[i])); } } } } /// /// Sudoku solving example. /// static void SudokuExample(Context ctx) { Console.WriteLine("SudokuExample"); // 9x9 matrix of integer variables IntExpr[][] X = new IntExpr[9][]; for (uint i = 0; i < 9; i++) { X[i] = new IntExpr[9]; for (uint j = 0; j < 9; j++) X[i][j] = (IntExpr)ctx.MkConst(ctx.MkSymbol("x_" + (i + 1) + "_" + (j + 1)), ctx.IntSort); } // each cell contains a value in {1, ..., 9} Expr[][] cells_c = new Expr[9][]; for (uint i = 0; i < 9; i++) { cells_c[i] = new BoolExpr[9]; for (uint j = 0; j < 9; j++) cells_c[i][j] = ctx.MkAnd(ctx.MkLe(ctx.MkInt(1), X[i][j]), ctx.MkLe(X[i][j], ctx.MkInt(9))); } // each row contains a digit at most once BoolExpr[] rows_c = new BoolExpr[9]; for (uint i = 0; i < 9; i++) rows_c[i] = ctx.MkDistinct(X[i]); // each column contains a digit at most once BoolExpr[] cols_c = new BoolExpr[9]; for (uint j = 0; j < 9; j++) { IntExpr[] column = new IntExpr[9]; for (uint i = 0; i < 9; i++) column[i] = X[i][j]; cols_c[j] = ctx.MkDistinct(column); } // each 3x3 square contains a digit at most once BoolExpr[][] sq_c = new BoolExpr[3][]; for (uint i0 = 0; i0 < 3; i0++) { sq_c[i0] = new BoolExpr[3]; for (uint j0 = 0; j0 < 3; j0++) { IntExpr[] square = new IntExpr[9]; for (uint i = 0; i < 3; i++) for (uint j = 0; j < 3; j++) square[3 * i + j] = X[3 * i0 + i][3 * j0 + j]; sq_c[i0][j0] = ctx.MkDistinct(square); } } BoolExpr sudoku_c = ctx.MkTrue(); foreach (BoolExpr[] t in cells_c) sudoku_c = ctx.MkAnd(ctx.MkAnd(t), sudoku_c); sudoku_c = ctx.MkAnd(ctx.MkAnd(rows_c), sudoku_c); sudoku_c = ctx.MkAnd(ctx.MkAnd(cols_c), sudoku_c); foreach (BoolExpr[] t in sq_c) sudoku_c = ctx.MkAnd(ctx.MkAnd(t), sudoku_c); // sudoku instance, we use '0' for empty cells int[,] instance = {{0,0,0,0,9,4,0,3,0}, {0,0,0,5,1,0,0,0,7}, {0,8,9,0,0,0,0,4,0}, {0,0,0,0,0,0,2,0,8}, {0,6,0,2,0,1,0,5,0}, {1,0,2,0,0,0,0,0,0}, {0,7,0,0,0,0,5,2,0}, {9,0,0,0,6,5,0,0,0}, {0,4,0,9,7,0,0,0,0}}; BoolExpr instance_c = ctx.MkTrue(); for (uint i = 0; i < 9; i++) for (uint j = 0; j < 9; j++) instance_c = ctx.MkAnd(instance_c, (BoolExpr) ctx.MkITE(ctx.MkEq(ctx.MkInt(instance[i, j]), ctx.MkInt(0)), ctx.MkTrue(), ctx.MkEq(X[i][j], ctx.MkInt(instance[i, j])))); Solver s = ctx.MkSolver(); s.Assert(sudoku_c); s.Assert(instance_c); if (s.Check() == Status.SATISFIABLE) { Model m = s.Model; Expr[,] R = new Expr[9, 9]; for (uint i = 0; i < 9; i++) for (uint j = 0; j < 9; j++) R[i, j] = m.Evaluate(X[i][j]); Console.WriteLine("Sudoku solution:"); for (uint i = 0; i < 9; i++) { for (uint j = 0; j < 9; j++) Console.Write(" " + R[i, j]); Console.WriteLine(); } } else { Console.WriteLine("Failed to solve sudoku"); throw new TestFailedException(); } } /// /// A basic example of how to use quantifiers. /// static void QuantifierExample1(Context ctx) { Console.WriteLine("QuantifierExample"); Sort[] types = new Sort[3]; IntExpr[] xs = new IntExpr[3]; Symbol[] names = new Symbol[3]; IntExpr[] vars = new IntExpr[3]; for (uint j = 0; j < 3; j++) { types[j] = ctx.IntSort; names[j] = ctx.MkSymbol(String.Format("x_{0}", j)); xs[j] = (IntExpr)ctx.MkConst(names[j], types[j]); vars[j] = (IntExpr)ctx.MkBound(2 - j, types[j]); // <-- vars reversed! } Expr body_vars = ctx.MkAnd(ctx.MkEq(ctx.MkAdd(vars[0], ctx.MkInt(1)), ctx.MkInt(2)), ctx.MkEq(ctx.MkAdd(vars[1], ctx.MkInt(2)), ctx.MkAdd(vars[2], ctx.MkInt(3)))); Expr body_const = ctx.MkAnd(ctx.MkEq(ctx.MkAdd(xs[0], ctx.MkInt(1)), ctx.MkInt(2)), ctx.MkEq(ctx.MkAdd(xs[1], ctx.MkInt(2)), ctx.MkAdd(xs[2], ctx.MkInt(3)))); Expr x = ctx.MkForall(types, names, body_vars, 1, null, null, ctx.MkSymbol("Q1"), ctx.MkSymbol("skid1")); Console.WriteLine("Quantifier X: " + x.ToString()); Expr y = ctx.MkForall(xs, body_const, 1, null, null, ctx.MkSymbol("Q2"), ctx.MkSymbol("skid2")); Console.WriteLine("Quantifier Y: " + y.ToString()); } static void QuantifierExample2(Context ctx) { Console.WriteLine("QuantifierExample2"); Expr q1, q2; FuncDecl f = ctx.MkFuncDecl("f", ctx.IntSort, ctx.IntSort); FuncDecl g = ctx.MkFuncDecl("g", ctx.IntSort, ctx.IntSort); // Quantifier with Exprs as the bound variables. { Expr x = ctx.MkConst("x", ctx.IntSort); Expr y = ctx.MkConst("y", ctx.IntSort); Expr f_x = ctx.MkApp(f, x); Expr f_y = ctx.MkApp(f, y); Expr g_y = ctx.MkApp(g, y); Expr[] no_pats = new Expr[] { f_y }; Expr[] bound = new Expr[2] { x, y }; Expr body = ctx.MkAnd(ctx.MkEq(f_x, f_y), ctx.MkEq(f_y, g_y)); q1 = ctx.MkForall(bound, body, 1, null, no_pats, ctx.MkSymbol("q"), ctx.MkSymbol("sk")); Console.WriteLine("{0}", q1); } // Quantifier with de-Bruijn indices. { Expr x = ctx.MkBound(1, ctx.IntSort); Expr y = ctx.MkBound(0, ctx.IntSort); Expr f_x = ctx.MkApp(f, x); Expr f_y = ctx.MkApp(f, y); Expr g_y = ctx.MkApp(g, y); Expr[] no_pats = new Expr[] { f_y }; Symbol[] names = new Symbol[] { ctx.MkSymbol("x"), ctx.MkSymbol("y") }; Sort[] sorts = new Sort[] { ctx.IntSort, ctx.IntSort }; Expr body = ctx.MkAnd(ctx.MkEq(f_x, f_y), ctx.MkEq(f_y, g_y)); q2 = ctx.MkForall(sorts, names, body, 1, null, // pats, no_pats, ctx.MkSymbol("q"), ctx.MkSymbol("sk") ); Console.WriteLine("{0}", q2); } Console.WriteLine("{0}", (q1.Equals(q2))); } /// /// Prove that f(x, y) = f(w, v) implies y = v when /// f is injective in the second argument. /// public static void QuantifierExample3(Context ctx) { Console.WriteLine("QuantifierExample3"); /* If quantified formulas are asserted in a logical context, then the model produced by Z3 should be viewed as a potential model. */ /* declare function f */ Sort I = ctx.IntSort; FuncDecl f = ctx.MkFuncDecl("f", new Sort[] { I, I }, I); /* f is injective in the second argument. */ BoolExpr inj = InjAxiom(ctx, f, 1); /* create x, y, v, w, fxy, fwv */ Expr x = ctx.MkIntConst("x"); Expr y = ctx.MkIntConst("y"); Expr v = ctx.MkIntConst("v"); Expr w = ctx.MkIntConst("w"); Expr fxy = ctx.MkApp(f, x, y); Expr fwv = ctx.MkApp(f, w, v); /* f(x, y) = f(w, v) */ BoolExpr p1 = ctx.MkEq(fxy, fwv); /* prove f(x, y) = f(w, v) implies y = v */ BoolExpr p2 = ctx.MkEq(y, v); Prove(ctx, p2, false, inj, p1); /* disprove f(x, y) = f(w, v) implies x = w */ BoolExpr p3 = ctx.MkEq(x, w); Disprove(ctx, p3, false, inj, p1); } /// /// Prove that f(x, y) = f(w, v) implies y = v when /// f is injective in the second argument. /// public static void QuantifierExample4(Context ctx) { Console.WriteLine("QuantifierExample4"); /* If quantified formulas are asserted in a logical context, then the model produced by Z3 should be viewed as a potential model. */ /* declare function f */ Sort I = ctx.IntSort; FuncDecl f = ctx.MkFuncDecl("f", new Sort[] { I, I }, I); /* f is injective in the second argument. */ BoolExpr inj = InjAxiomAbs(ctx, f, 1); /* create x, y, v, w, fxy, fwv */ Expr x = ctx.MkIntConst("x"); Expr y = ctx.MkIntConst("y"); Expr v = ctx.MkIntConst("v"); Expr w = ctx.MkIntConst("w"); Expr fxy = ctx.MkApp(f, x, y); Expr fwv = ctx.MkApp(f, w, v); /* f(x, y) = f(w, v) */ BoolExpr p1 = ctx.MkEq(fxy, fwv); /* prove f(x, y) = f(w, v) implies y = v */ BoolExpr p2 = ctx.MkEq(y, v); Prove(ctx, p2, false, inj, p1); /* disprove f(x, y) = f(w, v) implies x = w */ BoolExpr p3 = ctx.MkEq(x, w); Disprove(ctx, p3, false, inj, p1); } /// /// Some basic tests. /// static void BasicTests(Context ctx) { Console.WriteLine("BasicTests"); Symbol fname = ctx.MkSymbol("f"); Symbol x = ctx.MkSymbol("x"); Symbol y = ctx.MkSymbol("y"); Sort bs = ctx.MkBoolSort(); Sort[] domain = { bs, bs }; FuncDecl f = ctx.MkFuncDecl(fname, domain, bs); Expr fapp = ctx.MkApp(f, ctx.MkConst(x, bs), ctx.MkConst(y, bs)); Expr[] fargs2 = { ctx.MkFreshConst("cp", bs) }; Sort[] domain2 = { bs }; Expr fapp2 = ctx.MkApp(ctx.MkFreshFuncDecl("fp", domain2, bs), fargs2); BoolExpr trivial_eq = ctx.MkEq(fapp, fapp); BoolExpr nontrivial_eq = ctx.MkEq(fapp, fapp2); Goal g = ctx.MkGoal(true); g.Assert(trivial_eq); g.Assert(nontrivial_eq); Console.WriteLine("Goal: " + g); Solver solver = ctx.MkSolver(); foreach (BoolExpr a in g.Formulas) solver.Assert(a); if (solver.Check() != Status.SATISFIABLE) throw new TestFailedException(); ApplyResult ar = ApplyTactic(ctx, ctx.MkTactic("simplify"), g); if (ar.NumSubgoals == 1 && (ar.Subgoals[0].IsDecidedSat || ar.Subgoals[0].IsDecidedUnsat)) throw new TestFailedException(); ar = ApplyTactic(ctx, ctx.MkTactic("smt"), g); if (ar.NumSubgoals != 1 || !ar.Subgoals[0].IsDecidedSat) throw new TestFailedException(); g.Assert(ctx.MkEq(ctx.MkNumeral(1, ctx.MkBitVecSort(32)), ctx.MkNumeral(2, ctx.MkBitVecSort(32)))); ar = ApplyTactic(ctx, ctx.MkTactic("smt"), g); if (ar.NumSubgoals != 1 || !ar.Subgoals[0].IsDecidedUnsat) throw new TestFailedException(); Goal g2 = ctx.MkGoal(true, true); ar = ApplyTactic(ctx, ctx.MkTactic("smt"), g2); if (ar.NumSubgoals != 1 || !ar.Subgoals[0].IsDecidedSat) throw new TestFailedException(); g2 = ctx.MkGoal(true, true); g2.Assert(ctx.MkFalse()); ar = ApplyTactic(ctx, ctx.MkTactic("smt"), g2); if (ar.NumSubgoals != 1 || !ar.Subgoals[0].IsDecidedUnsat) throw new TestFailedException(); Goal g3 = ctx.MkGoal(true, true); Expr xc = ctx.MkConst(ctx.MkSymbol("x"), ctx.IntSort); Expr yc = ctx.MkConst(ctx.MkSymbol("y"), ctx.IntSort); g3.Assert(ctx.MkEq(xc, ctx.MkNumeral(1, ctx.IntSort))); g3.Assert(ctx.MkEq(yc, ctx.MkNumeral(2, ctx.IntSort))); BoolExpr constr = ctx.MkEq(xc, yc); g3.Assert(constr); ar = ApplyTactic(ctx, ctx.MkTactic("smt"), g3); if (ar.NumSubgoals != 1 || !ar.Subgoals[0].IsDecidedUnsat) throw new TestFailedException(); ModelConverterTest(ctx); // Real num/den test. RatNum rn = ctx.MkReal(42, 43); Expr inum = rn.Numerator; Expr iden = rn.Denominator; Console.WriteLine("Numerator: " + inum + " Denominator: " + iden); if (inum.ToString() != "42" || iden.ToString() != "43") throw new TestFailedException(); if (rn.ToDecimalString(3) != "0.976?") throw new TestFailedException(); BigIntCheck(ctx, ctx.MkReal("-1231231232/234234333")); BigIntCheck(ctx, ctx.MkReal("-123123234234234234231232/234234333")); BigIntCheck(ctx, ctx.MkReal("-234234333")); BigIntCheck(ctx, ctx.MkReal("234234333/2")); #if !FRAMEWORK_LT_4 string bn = "1234567890987654321"; if (ctx.MkInt(bn).BigInteger.ToString() != bn) throw new TestFailedException(); if (ctx.MkBV(bn, 128).BigInteger.ToString() != bn) throw new TestFailedException(); if (ctx.MkBV(bn, 32).BigInteger.ToString() == bn) throw new TestFailedException(); #endif // Error handling test. try { IntExpr i = ctx.MkInt("1/2"); throw new TestFailedException(); // unreachable } catch (Z3Exception) { } } /// /// Some basic expression casting tests. /// static void CastingTest(Context ctx) { Console.WriteLine("CastingTest"); Sort[] domain = { ctx.BoolSort, ctx.BoolSort }; FuncDecl f = ctx.MkFuncDecl("f", domain, ctx.BoolSort); AST upcast = ctx.MkFuncDecl(ctx.MkSymbol("q"), domain, ctx.BoolSort); try { FuncDecl downcast = (FuncDecl)f; // OK } catch (InvalidCastException) { throw new TestFailedException(); } try { Expr uc = (Expr)upcast; throw new TestFailedException(); // should not be reachable! } catch (InvalidCastException) { } Symbol s = ctx.MkSymbol(42); IntSymbol si = s as IntSymbol; if (si == null) throw new TestFailedException(); try { IntSymbol si2 = (IntSymbol)s; } catch (InvalidCastException) { throw new TestFailedException(); } s = ctx.MkSymbol("abc"); StringSymbol ss = s as StringSymbol; if (ss == null) throw new TestFailedException(); try { StringSymbol ss2 = (StringSymbol)s; } catch (InvalidCastException) { throw new TestFailedException(); } try { IntSymbol si2 = (IntSymbol)s; throw new TestFailedException(); // unreachable } catch { } Sort srt = ctx.MkBitVecSort(32); BitVecSort bvs = null; try { bvs = (BitVecSort)srt; } catch (InvalidCastException) { throw new TestFailedException(); } if (bvs.Size != 32) throw new TestFailedException(); Expr q = ctx.MkAdd(ctx.MkInt(1), ctx.MkInt(2)); Expr q2 = q.Args[1]; Sort qs = q2.Sort; if (qs as IntSort == null) throw new TestFailedException(); try { IntSort isrt = (IntSort)qs; } catch (InvalidCastException) { throw new TestFailedException(); } AST a = ctx.MkInt(42); Expr ae = a as Expr; if (ae == null) throw new TestFailedException(); ArithExpr aae = a as ArithExpr; if (aae == null) throw new TestFailedException(); IntExpr aie = a as IntExpr; if (aie == null) throw new TestFailedException(); IntNum ain = a as IntNum; if (ain == null) throw new TestFailedException(); Expr[][] earr = new Expr[2][]; earr[0] = new Expr[2]; earr[1] = new Expr[2]; earr[0][0] = ctx.MkTrue(); earr[0][1] = ctx.MkTrue(); earr[1][0] = ctx.MkFalse(); earr[1][1] = ctx.MkFalse(); foreach (Expr[] ea in earr) foreach (Expr e in ea) { try { Expr ns = ctx.MkNot((BoolExpr)e); BoolExpr ens = (BoolExpr)ns; } catch (InvalidCastException) { throw new TestFailedException(); } } } /// /// Shows how to read an SMT2 file. /// static void SMT2FileTest(string filename) { System.DateTime before = System.DateTime.Now; Console.WriteLine("SMT2 File test "); System.GC.Collect(); using (Context ctx = new Context(new Dictionary() { { "MODEL", "true" } })) { BoolExpr[] fmls = ctx.ParseSMTLIB2File(filename); BoolExpr a = ctx.MkAnd(fmls); Console.WriteLine("SMT2 file read time: " + (System.DateTime.Now - before).TotalSeconds + " sec"); // Iterate over the formula. Queue q = new Queue(); q.Enqueue(a); uint cnt = 0; while (q.Count > 0) { AST cur = (AST)q.Dequeue(); cnt++; // This here ... if (cur is Expr) if (!(cur.IsVar)) foreach (Expr c in ((Expr)cur).Args) q.Enqueue(c); // Takes the same time as this: //switch ((cur).ASTKind) //{ // case Z3_ast_kind.Z3_APP_AST: // foreach (Expr e in ((Expr)cur).Args) // q.Enqueue(e); // break; // case Z3_ast_kind.Z3_QUANTIFIER_AST: // q.Enqueue(((Quantifier)cur).Args[0]); // break; // case Z3_ast_kind.Z3_VAR_AST: break; // case Z3_ast_kind.Z3_NUMERAL_AST: break; // case Z3_ast_kind.Z3_FUNC_DECL_AST: break; // case Z3_ast_kind.Z3_SORT_AST: break; //} } Console.WriteLine(cnt + " ASTs"); } Console.WriteLine("SMT2 file test took " + (System.DateTime.Now - before).TotalSeconds + " sec"); } /// /// Shows how to use Solver(logic) /// /// static void LogicExample(Context ctx) { Console.WriteLine("LogicTest"); Microsoft.Z3.Global.ToggleWarningMessages(true); BitVecSort bvs = ctx.MkBitVecSort(32); Expr x = ctx.MkConst("x", bvs); Expr y = ctx.MkConst("y", bvs); BoolExpr eq = ctx.MkEq(x, y); // Use a solver for QF_BV Solver s = ctx.MkSolver("QF_BV"); s.Assert(eq); Status res = s.Check(); Console.WriteLine("solver result: " + res); // Or perhaps a tactic for QF_BV Goal g = ctx.MkGoal(true); g.Assert(eq); Tactic t = ctx.MkTactic("qfbv"); ApplyResult ar = t.Apply(g); Console.WriteLine("tactic result: " + ar); if (ar.NumSubgoals != 1 || !ar.Subgoals[0].IsDecidedSat) throw new TestFailedException(); } /// /// Demonstrates how to use the ParOr tactic. /// static void ParOrExample(Context ctx) { Console.WriteLine("ParOrExample"); BitVecSort bvs = ctx.MkBitVecSort(32); Expr x = ctx.MkConst("x", bvs); Expr y = ctx.MkConst("y", bvs); BoolExpr q = ctx.MkEq(x, y); Goal g = ctx.MkGoal(true); g.Assert(q); Tactic t1 = ctx.MkTactic("qfbv"); Tactic t2 = ctx.MkTactic("qfbv"); Tactic p = ctx.ParOr(t1, t2); ApplyResult ar = p.Apply(g); if (ar.NumSubgoals != 1 || !ar.Subgoals[0].IsDecidedSat) throw new TestFailedException(); } static void BigIntCheck(Context ctx, RatNum r) { #if !FRAMEWORK_LT_4 Console.WriteLine("Num: " + r.BigIntNumerator); Console.WriteLine("Den: " + r.BigIntDenominator); #endif } /// /// Find a model for x xor y. /// public static void FindModelExample1(Context ctx) { Console.WriteLine("FindModelExample1"); BoolExpr x = ctx.MkBoolConst("x"); BoolExpr y = ctx.MkBoolConst("y"); BoolExpr x_xor_y = ctx.MkXor(x, y); Model model = Check(ctx, x_xor_y, Status.SATISFIABLE); Console.WriteLine("x = {0}, y = {1}", model.Evaluate(x), model.Evaluate(y)); } /// /// Find a model for x < y + 1, x > 2. /// Then, assert not(x = y), and find another model. /// public static void FindModelExample2(Context ctx) { Console.WriteLine("FindModelExample2"); IntExpr x = ctx.MkIntConst("x"); IntExpr y = ctx.MkIntConst("y"); IntExpr one = ctx.MkInt(1); IntExpr two = ctx.MkInt(2); ArithExpr y_plus_one = ctx.MkAdd(y, one); BoolExpr c1 = ctx.MkLt(x, y_plus_one); BoolExpr c2 = ctx.MkGt(x, two); BoolExpr q = ctx.MkAnd(c1, c2); Console.WriteLine("model for: x < y + 1, x > 2"); Model model = Check(ctx, q, Status.SATISFIABLE); Console.WriteLine("x = {0}, y = {1}", (model.Evaluate(x)), (model.Evaluate(y))); /* assert not(x = y) */ BoolExpr x_eq_y = ctx.MkEq(x, y); BoolExpr c3 = ctx.MkNot(x_eq_y); q = ctx.MkAnd(q, c3); Console.WriteLine("model for: x < y + 1, x > 2, not(x = y)"); model = Check(ctx, q, Status.SATISFIABLE); Console.WriteLine("x = {0}, y = {1}", (model.Evaluate(x)), (model.Evaluate(y))); } /// /// Prove x = y implies g(x) = g(y), and /// disprove x = y implies g(g(x)) = g(y). /// /// This function demonstrates how to create uninterpreted /// types and functions. public static void ProveExample1(Context ctx) { Console.WriteLine("ProveExample1"); /* create uninterpreted type. */ Sort U = ctx.MkUninterpretedSort(ctx.MkSymbol("U")); /* declare function g */ FuncDecl g = ctx.MkFuncDecl("g", U, U); /* create x and y */ Expr x = ctx.MkConst("x", U); Expr y = ctx.MkConst("y", U); /* create g(x), g(y) */ Expr gx = g[x]; Expr gy = g[y]; /* assert x = y */ BoolExpr eq = ctx.MkEq(x, y); /* prove g(x) = g(y) */ BoolExpr f = ctx.MkEq(gx, gy); Console.WriteLine("prove: x = y implies g(x) = g(y)"); Prove(ctx, ctx.MkImplies(eq, f)); /* create g(g(x)) */ Expr ggx = g[gx]; /* disprove g(g(x)) = g(y) */ f = ctx.MkEq(ggx, gy); Console.WriteLine("disprove: x = y implies g(g(x)) = g(y)"); Disprove(ctx, ctx.MkImplies(eq, f)); /* Print the model using the custom model printer */ Model m = Check(ctx, ctx.MkNot(f), Status.SATISFIABLE); Console.WriteLine(m); } /// /// Prove not(g(g(x) - g(y)) = g(z)), x + z <= y <= x implies z < 0 . /// Then, show that z < -1 is not implied. /// /// This example demonstrates how to combine uninterpreted functions /// and arithmetic. public static void ProveExample2(Context ctx) { Console.WriteLine("ProveExample2"); /* declare function g */ Sort I = ctx.IntSort; FuncDecl g = ctx.MkFuncDecl("g", I, I); /* create x, y, and z */ IntExpr x = ctx.MkIntConst("x"); IntExpr y = ctx.MkIntConst("y"); IntExpr z = ctx.MkIntConst("z"); /* create gx, gy, gz */ Expr gx = ctx.MkApp(g, x); Expr gy = ctx.MkApp(g, y); Expr gz = ctx.MkApp(g, z); /* create zero */ IntExpr zero = ctx.MkInt(0); /* assert not(g(g(x) - g(y)) = g(z)) */ ArithExpr gx_gy = ctx.MkSub((IntExpr)gx, (IntExpr)gy); Expr ggx_gy = ctx.MkApp(g, gx_gy); BoolExpr eq = ctx.MkEq(ggx_gy, gz); BoolExpr c1 = ctx.MkNot(eq); /* assert x + z <= y */ ArithExpr x_plus_z = ctx.MkAdd(x, z); BoolExpr c2 = ctx.MkLe(x_plus_z, y); /* assert y <= x */ BoolExpr c3 = ctx.MkLe(y, x); /* prove z < 0 */ BoolExpr f = ctx.MkLt(z, zero); Console.WriteLine("prove: not(g(g(x) - g(y)) = g(z)), x + z <= y <= x implies z < 0"); Prove(ctx, f, false, c1, c2, c3); /* disprove z < -1 */ IntExpr minus_one = ctx.MkInt(-1); f = ctx.MkLt(z, minus_one); Console.WriteLine("disprove: not(g(g(x) - g(y)) = g(z)), x + z <= y <= x implies z < -1"); Disprove(ctx, f, false, c1, c2, c3); } /// /// Show how push & pop can be used to create "backtracking" points. /// /// This example also demonstrates how big numbers can be /// created in ctx. public static void PushPopExample1(Context ctx) { Console.WriteLine("PushPopExample1"); /* create a big number */ IntSort int_type = ctx.IntSort; IntExpr big_number = ctx.MkInt("1000000000000000000000000000000000000000000000000000000"); /* create number 3 */ IntExpr three = (IntExpr)ctx.MkNumeral("3", int_type); /* create x */ IntExpr x = ctx.MkIntConst("x"); Solver solver = ctx.MkSolver(); /* assert x >= "big number" */ BoolExpr c1 = ctx.MkGe(x, big_number); Console.WriteLine("assert: x >= 'big number'"); solver.Assert(c1); /* create a backtracking point */ Console.WriteLine("push"); solver.Push(); /* assert x <= 3 */ BoolExpr c2 = ctx.MkLe(x, three); Console.WriteLine("assert: x <= 3"); solver.Assert(c2); /* context is inconsistent at this point */ if (solver.Check() != Status.UNSATISFIABLE) throw new TestFailedException(); /* backtrack: the constraint x <= 3 will be removed, since it was asserted after the last ctx.Push. */ Console.WriteLine("pop"); solver.Pop(1); /* the context is consistent again. */ if (solver.Check() != Status.SATISFIABLE) throw new TestFailedException(); /* new constraints can be asserted... */ /* create y */ IntExpr y = ctx.MkIntConst("y"); /* assert y > x */ BoolExpr c3 = ctx.MkGt(y, x); Console.WriteLine("assert: y > x"); solver.Assert(c3); /* the context is still consistent. */ if (solver.Check() != Status.SATISFIABLE) throw new TestFailedException(); } /// /// Tuples. /// /// Check that the projection of a tuple /// returns the corresponding element. public static void TupleExample(Context ctx) { Console.WriteLine("TupleExample"); Sort int_type = ctx.IntSort; TupleSort tuple = ctx.MkTupleSort( ctx.MkSymbol("mk_tuple"), // name of tuple constructor new Symbol[] { ctx.MkSymbol("first"), ctx.MkSymbol("second") }, // names of projection operators new Sort[] { int_type, int_type } // types of projection operators ); FuncDecl first = tuple.FieldDecls[0]; // declarations are for projections // FuncDecl second = tuple.FieldDecls[1]; Expr x = ctx.MkConst("x", int_type); Expr y = ctx.MkConst("y", int_type); Expr n1 = tuple.MkDecl[x, y]; Expr n2 = first[n1]; BoolExpr n3 = ctx.MkEq(x, n2); Console.WriteLine("Tuple example: {0}", n3); Prove(ctx, n3); } /// /// Simple bit-vector example. /// /// /// This example disproves that x - 10 <= 0 IFF x <= 10 for (32-bit) machine integers /// public static void BitvectorExample1(Context ctx) { Console.WriteLine("BitvectorExample1"); Sort bv_type = ctx.MkBitVecSort(32); BitVecExpr x = (BitVecExpr)ctx.MkConst("x", bv_type); BitVecNum zero = (BitVecNum)ctx.MkNumeral("0", bv_type); BitVecNum ten = ctx.MkBV(10, 32); BitVecExpr x_minus_ten = ctx.MkBVSub(x, ten); /* bvsle is signed less than or equal to */ BoolExpr c1 = ctx.MkBVSLE(x, ten); BoolExpr c2 = ctx.MkBVSLE(x_minus_ten, zero); BoolExpr thm = ctx.MkIff(c1, c2); Console.WriteLine("disprove: x - 10 <= 0 IFF x <= 10 for (32-bit) machine integers"); Disprove(ctx, thm); } /// /// Find x and y such that: x ^ y - 103 == x * y /// public static void BitvectorExample2(Context ctx) { Console.WriteLine("BitvectorExample2"); /* construct x ^ y - 103 == x * y */ Sort bv_type = ctx.MkBitVecSort(32); BitVecExpr x = ctx.MkBVConst("x", 32); BitVecExpr y = ctx.MkBVConst("y", 32); BitVecExpr x_xor_y = ctx.MkBVXOR(x, y); BitVecExpr c103 = (BitVecNum)ctx.MkNumeral("103", bv_type); BitVecExpr lhs = ctx.MkBVSub(x_xor_y, c103); BitVecExpr rhs = ctx.MkBVMul(x, y); BoolExpr ctr = ctx.MkEq(lhs, rhs); Console.WriteLine("find values of x and y, such that x ^ y - 103 == x * y"); /* find a model (i.e., values for x an y that satisfy the constraint */ Model m = Check(ctx, ctr, Status.SATISFIABLE); Console.WriteLine(m); } /// /// Demonstrates how to use the SMTLIB parser. /// public static void ParserExample1(Context ctx) { Console.WriteLine("ParserExample1"); var fmls = ctx.ParseSMTLIB2String("(declare-const x Int) (declare-const y Int) (assert (> x y)) (assert (> x 0))"); var fml = ctx.MkAnd(fmls); Console.WriteLine("formula {0}", fml); Model m = Check(ctx, fml, Status.SATISFIABLE); } /// /// Demonstrates how to initialize the parser symbol table. /// public static void ParserExample2(Context ctx) { Console.WriteLine("ParserExample2"); Symbol[] declNames = { ctx.MkSymbol("a"), ctx.MkSymbol("b") }; FuncDecl a = ctx.MkConstDecl(declNames[0], ctx.MkIntSort()); FuncDecl b = ctx.MkConstDecl(declNames[1], ctx.MkIntSort()); FuncDecl[] decls = new FuncDecl[] { a, b }; BoolExpr f = ctx.ParseSMTLIB2String("(assert (> a b))", null, null, declNames, decls)[0]; Console.WriteLine("formula: {0}", f); Check(ctx, f, Status.SATISFIABLE); } /// /// Demonstrates how to initialize the parser symbol table. /// public static void ParserExample3(Context ctx) { Console.WriteLine("ParserExample3"); /* declare function g */ Sort I = ctx.MkIntSort(); FuncDecl g = ctx.MkFuncDecl("g", new Sort[] { I, I }, I); BoolExpr ca = CommAxiom(ctx, g); BoolExpr thm = ctx.ParseSMTLIB2String("(assert (forall ((x Int) (y Int)) (=> (= x y) (= (gg x 0) (gg 0 y)))))", null, null, new Symbol[] { ctx.MkSymbol("gg") }, new FuncDecl[] { g })[0]; Console.WriteLine("formula: {0}", thm); Prove(ctx, thm, false, ca); } /// /// Demonstrates how to handle parser errors using Z3 error handling support. /// /// public static void ParserExample5(Context ctx) { Console.WriteLine("ParserExample5"); try { ctx.ParseSMTLIB2String( /* the following string has a parsing error: missing parenthesis */ "(declare-const x Int (declare-const y Int)) (assert (> x y))"); } catch (Z3Exception e) { Console.WriteLine("Z3 error: {0}", e); } } /// /// Create an ite-Expr (if-then-else Exprs). /// public static void ITEExample(Context ctx) { Console.WriteLine("ITEExample"); BoolExpr f = ctx.MkFalse(); Expr one = ctx.MkInt(1); Expr zero = ctx.MkInt(0); Expr ite = ctx.MkITE(f, one, zero); Console.WriteLine("Expr: {0}", ite); } /// /// Create an enumeration data type. /// public static void EnumExample(Context ctx) { Console.WriteLine("EnumExample"); Symbol name = ctx.MkSymbol("fruit"); EnumSort fruit = ctx.MkEnumSort(ctx.MkSymbol("fruit"), new Symbol[] { ctx.MkSymbol("apple"), ctx.MkSymbol("banana"), ctx.MkSymbol("orange") }); Console.WriteLine("{0}", (fruit.Consts[0])); Console.WriteLine("{0}", (fruit.Consts[1])); Console.WriteLine("{0}", (fruit.Consts[2])); Console.WriteLine("{0}", (fruit.TesterDecls[0])); Console.WriteLine("{0}", (fruit.TesterDecls[1])); Console.WriteLine("{0}", (fruit.TesterDecls[2])); Expr apple = fruit.Consts[0]; Expr banana = fruit.Consts[1]; Expr orange = fruit.Consts[2]; /* Apples are different from oranges */ Prove(ctx, ctx.MkNot(ctx.MkEq(apple, orange))); /* Apples pass the apple test */ Prove(ctx, (BoolExpr)ctx.MkApp(fruit.TesterDecls[0], apple)); /* Oranges fail the apple test */ Disprove(ctx, (BoolExpr)ctx.MkApp(fruit.TesterDecls[0], orange)); Prove(ctx, (BoolExpr)ctx.MkNot((BoolExpr)ctx.MkApp(fruit.TesterDecls[0], orange))); Expr fruity = ctx.MkConst("fruity", fruit); /* If something is fruity, then it is an apple, banana, or orange */ Prove(ctx, ctx.MkOr(new BoolExpr[] { ctx.MkEq(fruity, apple), ctx.MkEq(fruity, banana), ctx.MkEq(fruity, orange) })); } /// /// Create a list datatype. /// public static void ListExample(Context ctx) { Console.WriteLine("ListExample"); Sort int_ty; ListSort int_list; Expr nil, l1, l2, x, y, u, v; BoolExpr fml, fml1; int_ty = ctx.MkIntSort(); int_list = ctx.MkListSort(ctx.MkSymbol("int_list"), int_ty); nil = ctx.MkConst(int_list.NilDecl); l1 = ctx.MkApp(int_list.ConsDecl, ctx.MkInt(1), nil); l2 = ctx.MkApp(int_list.ConsDecl, ctx.MkInt(2), nil); /* nil != cons(1, nil) */ Prove(ctx, ctx.MkNot(ctx.MkEq(nil, l1))); /* cons(2,nil) != cons(1, nil) */ Prove(ctx, ctx.MkNot(ctx.MkEq(l1, l2))); /* cons(x,nil) = cons(y, nil) => x = y */ x = ctx.MkConst("x", int_ty); y = ctx.MkConst("y", int_ty); l1 = ctx.MkApp(int_list.ConsDecl, x, nil); l2 = ctx.MkApp(int_list.ConsDecl, y, nil); Prove(ctx, ctx.MkImplies(ctx.MkEq(l1, l2), ctx.MkEq(x, y))); /* cons(x,u) = cons(x, v) => u = v */ u = ctx.MkConst("u", int_list); v = ctx.MkConst("v", int_list); l1 = ctx.MkApp(int_list.ConsDecl, x, u); l2 = ctx.MkApp(int_list.ConsDecl, y, v); Prove(ctx, ctx.MkImplies(ctx.MkEq(l1, l2), ctx.MkEq(u, v))); Prove(ctx, ctx.MkImplies(ctx.MkEq(l1, l2), ctx.MkEq(x, y))); /* is_nil(u) or is_cons(u) */ Prove(ctx, ctx.MkOr((BoolExpr)ctx.MkApp(int_list.IsNilDecl, u), (BoolExpr)ctx.MkApp(int_list.IsConsDecl, u))); /* occurs check u != cons(x,u) */ Prove(ctx, ctx.MkNot(ctx.MkEq(u, l1))); /* destructors: is_cons(u) => u = cons(head(u),tail(u)) */ fml1 = ctx.MkEq(u, ctx.MkApp(int_list.ConsDecl, ctx.MkApp(int_list.HeadDecl, u), ctx.MkApp(int_list.TailDecl, u))); fml = ctx.MkImplies((BoolExpr)ctx.MkApp(int_list.IsConsDecl, u), fml1); Console.WriteLine("Formula {0}", fml); Prove(ctx, fml); Disprove(ctx, fml1); } /// /// Create a binary tree datatype. /// public static void TreeExample(Context ctx) { Console.WriteLine("TreeExample"); Sort cell; FuncDecl nil_decl, is_nil_decl, cons_decl, is_cons_decl, car_decl, cdr_decl; Expr nil, l1, l2, x, y, u, v; BoolExpr fml, fml1; string[] head_tail = new string[] { "car", "cdr" }; Sort[] sorts = new Sort[] { null, null }; uint[] sort_refs = new uint[] { 0, 0 }; Constructor nil_con, cons_con; nil_con = ctx.MkConstructor("nil", "is_nil", null, null, null); cons_con = ctx.MkConstructor("cons", "is_cons", head_tail, sorts, sort_refs); Constructor[] constructors = new Constructor[] { nil_con, cons_con }; cell = ctx.MkDatatypeSort("cell", constructors); nil_decl = nil_con.ConstructorDecl; is_nil_decl = nil_con.TesterDecl; cons_decl = cons_con.ConstructorDecl; is_cons_decl = cons_con.TesterDecl; FuncDecl[] cons_accessors = cons_con.AccessorDecls; car_decl = cons_accessors[0]; cdr_decl = cons_accessors[1]; nil = ctx.MkConst(nil_decl); l1 = ctx.MkApp(cons_decl, nil, nil); l2 = ctx.MkApp(cons_decl, l1, nil); /* nil != cons(nil, nil) */ Prove(ctx, ctx.MkNot(ctx.MkEq(nil, l1))); /* cons(x,u) = cons(x, v) => u = v */ u = ctx.MkConst("u", cell); v = ctx.MkConst("v", cell); x = ctx.MkConst("x", cell); y = ctx.MkConst("y", cell); l1 = ctx.MkApp(cons_decl, x, u); l2 = ctx.MkApp(cons_decl, y, v); Prove(ctx, ctx.MkImplies(ctx.MkEq(l1, l2), ctx.MkEq(u, v))); Prove(ctx, ctx.MkImplies(ctx.MkEq(l1, l2), ctx.MkEq(x, y))); /* is_nil(u) or is_cons(u) */ Prove(ctx, ctx.MkOr((BoolExpr)ctx.MkApp(is_nil_decl, u), (BoolExpr)ctx.MkApp(is_cons_decl, u))); /* occurs check u != cons(x,u) */ Prove(ctx, ctx.MkNot(ctx.MkEq(u, l1))); /* destructors: is_cons(u) => u = cons(car(u),cdr(u)) */ fml1 = ctx.MkEq(u, ctx.MkApp(cons_decl, ctx.MkApp(car_decl, u), ctx.MkApp(cdr_decl, u))); fml = ctx.MkImplies((BoolExpr)ctx.MkApp(is_cons_decl, u), fml1); Console.WriteLine("Formula {0}", fml); Prove(ctx, fml); Disprove(ctx, fml1); } /// /// Create a forest of trees. /// /// /// forest ::= nil | cons(tree, forest) /// tree ::= nil | cons(forest, forest) /// public static void ForestExample(Context ctx) { Console.WriteLine("ForestExample"); Sort tree, forest; FuncDecl nil1_decl, is_nil1_decl, cons1_decl, is_cons1_decl, car1_decl, cdr1_decl; FuncDecl nil2_decl, is_nil2_decl, cons2_decl, is_cons2_decl, car2_decl, cdr2_decl; Expr nil1, nil2, t1, t2, t3, t4, f1, f2, f3, l1, l2, x, y, u, v; // // Declare the names of the accessors for cons. // Then declare the sorts of the accessors. // For this example, all sorts refer to the new types 'forest' and 'tree' // being declared, so we pass in null for both sorts1 and sorts2. // On the other hand, the sort_refs arrays contain the indices of the // two new sorts being declared. The first element in sort1_refs // points to 'tree', which has index 1, the second element in sort1_refs array // points to 'forest', which has index 0. // Symbol[] head_tail1 = new Symbol[] { ctx.MkSymbol("head"), ctx.MkSymbol("tail") }; Sort[] sorts1 = new Sort[] { null, null }; uint[] sort1_refs = new uint[] { 1, 0 }; // the first item points to a tree, the second to a forest Symbol[] head_tail2 = new Symbol[] { ctx.MkSymbol("car"), ctx.MkSymbol("cdr") }; Sort[] sorts2 = new Sort[] { null, null }; uint[] sort2_refs = new uint[] { 0, 0 }; // both items point to the forest datatype. Constructor nil1_con, cons1_con, nil2_con, cons2_con; Constructor[] constructors1 = new Constructor[2], constructors2 = new Constructor[2]; Symbol[] sort_names = { ctx.MkSymbol("forest"), ctx.MkSymbol("tree") }; /* build a forest */ nil1_con = ctx.MkConstructor(ctx.MkSymbol("nil"), ctx.MkSymbol("is_nil"), null, null, null); cons1_con = ctx.MkConstructor(ctx.MkSymbol("cons1"), ctx.MkSymbol("is_cons1"), head_tail1, sorts1, sort1_refs); constructors1[0] = nil1_con; constructors1[1] = cons1_con; /* build a tree */ nil2_con = ctx.MkConstructor(ctx.MkSymbol("nil2"), ctx.MkSymbol("is_nil2"), null, null, null); cons2_con = ctx.MkConstructor(ctx.MkSymbol("cons2"), ctx.MkSymbol("is_cons2"), head_tail2, sorts2, sort2_refs); constructors2[0] = nil2_con; constructors2[1] = cons2_con; Constructor[][] clists = new Constructor[][] { constructors1, constructors2 }; Sort[] sorts = ctx.MkDatatypeSorts(sort_names, clists); forest = sorts[0]; tree = sorts[1]; // // Now that the datatype has been created. // Query the constructors for the constructor // functions, testers, and field accessors. // nil1_decl = nil1_con.ConstructorDecl; is_nil1_decl = nil1_con.TesterDecl; cons1_decl = cons1_con.ConstructorDecl; is_cons1_decl = cons1_con.TesterDecl; FuncDecl[] cons1_accessors = cons1_con.AccessorDecls; car1_decl = cons1_accessors[0]; cdr1_decl = cons1_accessors[1]; nil2_decl = nil2_con.ConstructorDecl; is_nil2_decl = nil2_con.TesterDecl; cons2_decl = cons2_con.ConstructorDecl; is_cons2_decl = cons2_con.TesterDecl; FuncDecl[] cons2_accessors = cons2_con.AccessorDecls; car2_decl = cons2_accessors[0]; cdr2_decl = cons2_accessors[1]; nil1 = ctx.MkConst(nil1_decl); nil2 = ctx.MkConst(nil2_decl); f1 = ctx.MkApp(cons1_decl, nil2, nil1); t1 = ctx.MkApp(cons2_decl, nil1, nil1); t2 = ctx.MkApp(cons2_decl, f1, nil1); t3 = ctx.MkApp(cons2_decl, f1, f1); t4 = ctx.MkApp(cons2_decl, nil1, f1); f2 = ctx.MkApp(cons1_decl, t1, nil1); f3 = ctx.MkApp(cons1_decl, t1, f1); /* nil != cons(nil,nil) */ Prove(ctx, ctx.MkNot(ctx.MkEq(nil1, f1))); Prove(ctx, ctx.MkNot(ctx.MkEq(nil2, t1))); /* cons(x,u) = cons(x, v) => u = v */ u = ctx.MkConst("u", forest); v = ctx.MkConst("v", forest); x = ctx.MkConst("x", tree); y = ctx.MkConst("y", tree); l1 = ctx.MkApp(cons1_decl, x, u); l2 = ctx.MkApp(cons1_decl, y, v); Prove(ctx, ctx.MkImplies(ctx.MkEq(l1, l2), ctx.MkEq(u, v))); Prove(ctx, ctx.MkImplies(ctx.MkEq(l1, l2), ctx.MkEq(x, y))); /* is_nil(u) or is_cons(u) */ Prove(ctx, ctx.MkOr((BoolExpr)ctx.MkApp(is_nil1_decl, u), (BoolExpr)ctx.MkApp(is_cons1_decl, u))); /* occurs check u != cons(x,u) */ Prove(ctx, ctx.MkNot(ctx.MkEq(u, l1))); } /// /// Demonstrate how to use #Eval. /// public static void EvalExample1(Context ctx) { Console.WriteLine("EvalExample1"); IntExpr x = ctx.MkIntConst("x"); IntExpr y = ctx.MkIntConst("y"); IntExpr two = ctx.MkInt(2); Solver solver = ctx.MkSolver(); /* assert x < y */ solver.Assert(ctx.MkLt(x, y)); /* assert x > 2 */ solver.Assert(ctx.MkGt(x, two)); /* find model for the constraints above */ Model model = null; if (Status.SATISFIABLE == solver.Check()) { model = solver.Model; Console.WriteLine("{0}", model); Console.WriteLine("\nevaluating x+y"); Expr v = model.Evaluate(ctx.MkAdd(x, y)); if (v != null) { Console.WriteLine("result = {0}", (v)); } else { Console.WriteLine("Failed to evaluate: x+y"); } } else { Console.WriteLine("BUG, the constraints are satisfiable."); } } /// /// Demonstrate how to use #Eval on tuples. /// public static void EvalExample2(Context ctx) { Console.WriteLine("EvalExample2"); Sort int_type = ctx.IntSort; TupleSort tuple = ctx.MkTupleSort( ctx.MkSymbol("mk_tuple"), // name of tuple constructor new Symbol[] { ctx.MkSymbol("first"), ctx.MkSymbol("second") }, // names of projection operators new Sort[] { int_type, int_type } // types of projection operators ); FuncDecl first = tuple.FieldDecls[0]; // declarations are for projections FuncDecl second = tuple.FieldDecls[1]; Expr tup1 = ctx.MkConst("t1", tuple); Expr tup2 = ctx.MkConst("t2", tuple); Solver solver = ctx.MkSolver(); /* assert tup1 != tup2 */ solver.Assert(ctx.MkNot(ctx.MkEq(tup1, tup2))); /* assert first tup1 = first tup2 */ solver.Assert(ctx.MkEq(ctx.MkApp(first, tup1), ctx.MkApp(first, tup2))); /* find model for the constraints above */ Model model = null; if (Status.SATISFIABLE == solver.Check()) { model = solver.Model; Console.WriteLine("{0}", model); Console.WriteLine("evaluating tup1 {0}", (model.Evaluate(tup1))); Console.WriteLine("evaluating tup2 {0}", (model.Evaluate(tup2))); Console.WriteLine("evaluating second(tup2) {0}", (model.Evaluate(ctx.MkApp(second, tup2)))); } else { Console.WriteLine("BUG, the constraints are satisfiable."); } } /// /// Demonstrate how to use Pushand Popto /// control the size of models. /// /// Note: this test is specialized to 32-bit bitvectors. public static void CheckSmall(Context ctx, Solver solver, BitVecExpr[] to_minimize) { Sort bv32 = ctx.MkBitVecSort(32); int num_Exprs = to_minimize.Length; UInt32[] upper = new UInt32[num_Exprs]; UInt32[] lower = new UInt32[num_Exprs]; BitVecExpr[] values = new BitVecExpr[num_Exprs]; for (int i = 0; i < upper.Length; ++i) { upper[i] = UInt32.MaxValue; lower[i] = 0; } bool some_work = true; int last_index = -1; UInt32 last_upper = 0; while (some_work) { solver.Push(); bool check_is_sat = true; while (check_is_sat && some_work) { // Assert all feasible bounds. for (int i = 0; i < num_Exprs; ++i) { solver.Assert(ctx.MkBVULE(to_minimize[i], ctx.MkBV(upper[i], 32))); } check_is_sat = Status.SATISFIABLE == solver.Check(); if (!check_is_sat) { if (last_index != -1) { lower[last_index] = last_upper + 1; } break; } Console.WriteLine("{0}", solver.Model); // narrow the bounds based on the current model. for (int i = 0; i < num_Exprs; ++i) { Expr v = solver.Model.Evaluate(to_minimize[i]); UInt64 ui = ((BitVecNum)v).UInt64; if (ui < upper[i]) { upper[i] = (UInt32)ui; } Console.WriteLine("{0} {1} {2}", i, lower[i], upper[i]); } // find a new bound to add some_work = false; last_index = 0; for (int i = 0; i < num_Exprs; ++i) { if (lower[i] < upper[i]) { last_upper = (upper[i] + lower[i]) / 2; last_index = i; solver.Assert(ctx.MkBVULE(to_minimize[i], ctx.MkBV(last_upper, 32))); some_work = true; break; } } } solver.Pop(); } } /// /// Reduced-size model generation example. /// public static void FindSmallModelExample(Context ctx) { Console.WriteLine("FindSmallModelExample"); BitVecExpr x = ctx.MkBVConst("x", 32); BitVecExpr y = ctx.MkBVConst("y", 32); BitVecExpr z = ctx.MkBVConst("z", 32); Solver solver = ctx.MkSolver(); solver.Assert(ctx.MkBVULE(x, ctx.MkBVAdd(y, z))); CheckSmall(ctx, solver, new BitVecExpr[] { x, y, z }); } /// /// Simplifier example. /// public static void SimplifierExample(Context ctx) { Console.WriteLine("SimplifierExample"); IntExpr x = ctx.MkIntConst("x"); IntExpr y = ctx.MkIntConst("y"); IntExpr z = ctx.MkIntConst("z"); IntExpr u = ctx.MkIntConst("u"); Expr t1 = ctx.MkAdd(x, ctx.MkSub(y, ctx.MkAdd(x, z))); Expr t2 = t1.Simplify(); Console.WriteLine("{0} -> {1}", (t1), (t2)); } /// /// Extract unsatisfiable core example /// public static void UnsatCoreAndProofExample(Context ctx) { Console.WriteLine("UnsatCoreAndProofExample"); Solver solver = ctx.MkSolver(); BoolExpr pa = ctx.MkBoolConst("PredA"); BoolExpr pb = ctx.MkBoolConst("PredB"); BoolExpr pc = ctx.MkBoolConst("PredC"); BoolExpr pd = ctx.MkBoolConst("PredD"); BoolExpr p1 = ctx.MkBoolConst("P1"); BoolExpr p2 = ctx.MkBoolConst("P2"); BoolExpr p3 = ctx.MkBoolConst("P3"); BoolExpr p4 = ctx.MkBoolConst("P4"); Expr[] assumptions = new Expr[] { ctx.MkNot(p1), ctx.MkNot(p2), ctx.MkNot(p3), ctx.MkNot(p4) }; BoolExpr f1 = ctx.MkAnd(new BoolExpr[] { pa, pb, pc }); BoolExpr f2 = ctx.MkAnd(new BoolExpr[] { pa, ctx.MkNot(pb), pc }); BoolExpr f3 = ctx.MkOr(ctx.MkNot(pa), ctx.MkNot(pc)); BoolExpr f4 = pd; solver.Assert(ctx.MkOr(f1, p1)); solver.Assert(ctx.MkOr(f2, p2)); solver.Assert(ctx.MkOr(f3, p3)); solver.Assert(ctx.MkOr(f4, p4)); Status result = solver.Check(assumptions); if (result == Status.UNSATISFIABLE) { Console.WriteLine("unsat"); Console.WriteLine("proof: {0}", solver.Proof); Console.WriteLine("core: "); foreach (Expr c in solver.UnsatCore) { Console.WriteLine("{0}", c); } } } /// /// Extract unsatisfiable core example with AssertAndTrack /// public static void UnsatCoreAndProofExample2(Context ctx) { Console.WriteLine("UnsatCoreAndProofExample2"); Solver solver = ctx.MkSolver(); BoolExpr pa = ctx.MkBoolConst("PredA"); BoolExpr pb = ctx.MkBoolConst("PredB"); BoolExpr pc = ctx.MkBoolConst("PredC"); BoolExpr pd = ctx.MkBoolConst("PredD"); BoolExpr f1 = ctx.MkAnd(new BoolExpr[] { pa, pb, pc }); BoolExpr f2 = ctx.MkAnd(new BoolExpr[] { pa, ctx.MkNot(pb), pc }); BoolExpr f3 = ctx.MkOr(ctx.MkNot(pa), ctx.MkNot(pc)); BoolExpr f4 = pd; BoolExpr p1 = ctx.MkBoolConst("P1"); BoolExpr p2 = ctx.MkBoolConst("P2"); BoolExpr p3 = ctx.MkBoolConst("P3"); BoolExpr p4 = ctx.MkBoolConst("P4"); solver.AssertAndTrack(f1, p1); solver.AssertAndTrack(f2, p2); solver.AssertAndTrack(f3, p3); solver.AssertAndTrack(f4, p4); Status result = solver.Check(); if (result == Status.UNSATISFIABLE) { Console.WriteLine("unsat"); Console.WriteLine("core: "); foreach (Expr c in solver.UnsatCore) { Console.WriteLine("{0}", c); } } } public static void FiniteDomainExample(Context ctx) { Console.WriteLine("FiniteDomainExample"); FiniteDomainSort s = ctx.MkFiniteDomainSort("S", 10); FiniteDomainSort t = ctx.MkFiniteDomainSort("T", 10); FiniteDomainNum s1 = (FiniteDomainNum)ctx.MkNumeral(1, s); FiniteDomainNum t1 = (FiniteDomainNum)ctx.MkNumeral(1, t); Console.WriteLine("{0} of size {1}", s, s.Size); Console.WriteLine("{0} of size {1}", t, t.Size); Console.WriteLine("{0}", s1); Console.WriteLine("{0}", t1); Console.WriteLine("{0}", s1.Int); Console.WriteLine("{0}", t1.Int); // But you cannot mix numerals of different sorts // even if the size of their domains are the same: // Console.WriteLine("{0}", ctx.MkEq(s1, t1)); } public static void FloatingPointExample1(Context ctx) { Console.WriteLine("FloatingPointExample1"); FPSort s = ctx.MkFPSort(11, 53); Console.WriteLine("Sort: {0}", s); FPNum x = (FPNum)ctx.MkNumeral("-1e1", s); /* -1 * 10^1 = -10 */ FPNum y = (FPNum)ctx.MkNumeral("-10", s); /* -10 */ FPNum z = (FPNum)ctx.MkNumeral("-1.25p3", s); /* -1.25 * 2^3 = -1.25 * 8 = -10 */ Console.WriteLine("x={0}; y={1}; z={2}", x.ToString(), y.ToString(), z.ToString()); BoolExpr a = ctx.MkAnd(ctx.MkFPEq(x, y), ctx.MkFPEq(y, z)); Check(ctx, ctx.MkNot(a), Status.UNSATISFIABLE); /* nothing is equal to NaN according to floating-point * equality, so NaN == k should be unsatisfiable. */ FPExpr k = (FPExpr)ctx.MkConst("x", s); FPExpr nan = ctx.MkFPNaN(s); /* solver that runs the default tactic for QF_FP. */ Solver slvr = ctx.MkSolver("QF_FP"); slvr.Add(ctx.MkFPEq(nan, k)); if (slvr.Check() != Status.UNSATISFIABLE) throw new TestFailedException(); Console.WriteLine("OK, unsat:" + Environment.NewLine + slvr); /* NaN is equal to NaN according to normal equality. */ slvr = ctx.MkSolver("QF_FP"); slvr.Add(ctx.MkEq(nan, nan)); if (slvr.Check() != Status.SATISFIABLE) throw new TestFailedException(); Console.WriteLine("OK, sat:" + Environment.NewLine + slvr); /* Let's prove -1e1 * -1.25e3 == +100 */ x = (FPNum)ctx.MkNumeral("-1e1", s); y = (FPNum)ctx.MkNumeral("-1.25p3", s); FPExpr x_plus_y = (FPExpr)ctx.MkConst("x_plus_y", s); FPNum r = (FPNum)ctx.MkNumeral("100", s); slvr = ctx.MkSolver("QF_FP"); slvr.Add(ctx.MkEq(x_plus_y, ctx.MkFPMul(ctx.MkFPRoundNearestTiesToAway(), x, y))); slvr.Add(ctx.MkNot(ctx.MkFPEq(x_plus_y, r))); if (slvr.Check() != Status.UNSATISFIABLE) throw new TestFailedException(); Console.WriteLine("OK, unsat:" + Environment.NewLine + slvr); } public static void FloatingPointExample2(Context ctx) { Console.WriteLine("FloatingPointExample2"); FPSort double_sort = ctx.MkFPSort(11, 53); FPRMSort rm_sort = ctx.MkFPRoundingModeSort(); FPRMExpr rm = (FPRMExpr)ctx.MkConst(ctx.MkSymbol("rm"), rm_sort); BitVecExpr x = (BitVecExpr)ctx.MkConst(ctx.MkSymbol("x"), ctx.MkBitVecSort(64)); FPExpr y = (FPExpr)ctx.MkConst(ctx.MkSymbol("y"), double_sort); FPExpr fp_val = ctx.MkFP(42, double_sort); BoolExpr c1 = ctx.MkEq(y, fp_val); BoolExpr c2 = ctx.MkEq(x, ctx.MkFPToBV(rm, y, 64, false)); BoolExpr c3 = ctx.MkEq(x, ctx.MkBV(42, 64)); BoolExpr c4 = ctx.MkEq(ctx.MkNumeral(42, ctx.RealSort), ctx.MkFPToReal(fp_val)); BoolExpr c5 = ctx.MkAnd(c1, c2, c3, c4); Console.WriteLine("c5 = " + c5); /* Generic solver */ Solver s = ctx.MkSolver(); s.Assert(c5); Console.WriteLine(s); if (s.Check() != Status.SATISFIABLE) throw new TestFailedException(); Console.WriteLine("OK, model: {0}", s.Model.ToString()); } public static void TranslationExample() { Context ctx1 = new Context(); Context ctx2 = new Context(); Sort s1 = ctx1.IntSort; Sort s2 = ctx2.IntSort; Sort s3 = s1.Translate(ctx2); Console.WriteLine(s1 == s2); Console.WriteLine(s1.Equals(s2)); Console.WriteLine(s2.Equals(s3)); Console.WriteLine(s1.Equals(s3)); Expr e1 = ctx1.MkIntConst("e1"); Expr e2 = ctx2.MkIntConst("e1"); Expr e3 = e1.Translate(ctx2); Console.WriteLine(e1 == e2); Console.WriteLine(e1.Equals(e2)); Console.WriteLine(e2.Equals(e3)); Console.WriteLine(e1.Equals(e3)); } static void Main(string[] args) { try { Microsoft.Z3.Global.ToggleWarningMessages(true); Log.Open("test.log"); Console.Write("Z3 Major Version: "); Console.WriteLine(Microsoft.Z3.Version.Major.ToString()); Console.Write("Z3 Full Version: "); Console.WriteLine(Microsoft.Z3.Version.ToString()); Console.Write("Z3 Full Version String: "); Console.WriteLine(Microsoft.Z3.Version.FullVersion); SimpleExample(); // These examples need model generation turned on. using (Context ctx = new Context(new Dictionary() { { "model", "true" } })) { BasicTests(ctx); CastingTest(ctx); SudokuExample(ctx); QuantifierExample1(ctx); QuantifierExample2(ctx); LogicExample(ctx); ParOrExample(ctx); FindModelExample1(ctx); FindModelExample2(ctx); PushPopExample1(ctx); ArrayExample1(ctx); ArrayExample3(ctx); BitvectorExample1(ctx); BitvectorExample2(ctx); ParserExample1(ctx); ParserExample2(ctx); ParserExample5(ctx); ITEExample(ctx); EvalExample1(ctx); EvalExample2(ctx); FindSmallModelExample(ctx); SimplifierExample(ctx); FiniteDomainExample(ctx); FloatingPointExample1(ctx); FloatingPointExample2(ctx); } // These examples need proof generation turned on. using (Context ctx = new Context(new Dictionary() { { "proof", "true" } })) { ProveExample1(ctx); ProveExample2(ctx); ArrayExample2(ctx); TupleExample(ctx); ParserExample3(ctx); EnumExample(ctx); ListExample(ctx); TreeExample(ctx); ForestExample(ctx); UnsatCoreAndProofExample(ctx); UnsatCoreAndProofExample2(ctx); } // These examples need proof generation turned on and auto-config set to false. using (Context ctx = new Context(new Dictionary() { {"proof", "true" }, {"auto-config", "false" } })) { QuantifierExample3(ctx); QuantifierExample4(ctx); } TranslationExample(); Log.Close(); if (Log.isOpen()) Console.WriteLine("Log is still open!"); } catch (Z3Exception ex) { Console.WriteLine("Z3 Managed Exception: " + ex.Message); Console.WriteLine("Stack trace: " + ex.StackTrace); } catch (TestFailedException ex) { Console.WriteLine("TEST CASE FAILED: " + ex.Message); } } } } z3-z3-4.8.7/examples/dotnet/README000066400000000000000000000002221356505360400164160ustar00rootroot00000000000000Small example using the .Net bindings. To build the example execute make examples in the build directory. It will create a .net core 2.0 app. z3-z3-4.8.7/examples/dotnet/dotnet.csproj000066400000000000000000000004221356505360400202570ustar00rootroot00000000000000 Exe netcoreapp2.0 z3-z3-4.8.7/examples/java/000077500000000000000000000000001356505360400151665ustar00rootroot00000000000000z3-z3-4.8.7/examples/java/JavaExample.java000066400000000000000000002374561356505360400202470ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: Program.java Abstract: Z3 Java API: Example program Author: Christoph Wintersteiger (cwinter) 2012-11-27 Notes: --*/ import java.util.*; import com.microsoft.z3.*; class JavaExample { @SuppressWarnings("serial") class TestFailedException extends Exception { public TestFailedException() { super("Check FAILED"); } }; // / Create axiom: function f is injective in the i-th argument. // / // / The following axiom is produced: // / // / forall (x_0, ..., x_n) finv(f(x_0, ..., x_i, ..., x_{n-1})) = x_i // / // / Where, finvis a fresh function declaration. public BoolExpr injAxiom(Context ctx, FuncDecl f, int i) { Sort[] domain = f.getDomain(); int sz = f.getDomainSize(); if (i >= sz) { System.out.println("failed to create inj axiom"); return null; } /* declare the i-th inverse of f: finv */ Sort finv_domain = f.getRange(); Sort finv_range = domain[i]; FuncDecl finv = ctx.mkFuncDecl("f_fresh", finv_domain, finv_range); /* allocate temporary arrays */ Expr[] xs = new Expr[sz]; Symbol[] names = new Symbol[sz]; Sort[] types = new Sort[sz]; /* fill types, names and xs */ for (int j = 0; j < sz; j++) { types[j] = domain[j]; names[j] = ctx.mkSymbol("x_" + Integer.toString(j)); xs[j] = ctx.mkBound(j, types[j]); } Expr x_i = xs[i]; /* create f(x_0, ..., x_i, ..., x_{n-1}) */ Expr fxs = f.apply(xs); /* create f_inv(f(x_0, ..., x_i, ..., x_{n-1})) */ Expr finv_fxs = finv.apply(fxs); /* create finv(f(x_0, ..., x_i, ..., x_{n-1})) = x_i */ Expr eq = ctx.mkEq(finv_fxs, x_i); /* use f(x_0, ..., x_i, ..., x_{n-1}) as the pattern for the quantifier */ Pattern p = ctx.mkPattern(fxs); /* create & assert quantifier */ BoolExpr q = ctx.mkForall(types, /* types of quantified variables */ names, /* names of quantified variables */ eq, 1, new Pattern[] { p } /* patterns */, null, null, null); return q; } // / Create axiom: function f is injective in the i-th argument. // / // / The following axiom is produced: // / // / forall (x_0, ..., x_n) finv(f(x_0, ..., x_i, ..., x_{n-1})) = x_i // / // / Where, finvis a fresh function declaration. public BoolExpr injAxiomAbs(Context ctx, FuncDecl f, int i) { Sort[] domain = f.getDomain(); int sz = f.getDomainSize(); if (i >= sz) { System.out.println("failed to create inj axiom"); return null; } /* declare the i-th inverse of f: finv */ Sort finv_domain = f.getRange(); Sort finv_range = domain[i]; FuncDecl finv = ctx.mkFuncDecl("f_fresh", finv_domain, finv_range); /* allocate temporary arrays */ Expr[] xs = new Expr[sz]; /* fill types, names and xs */ for (int j = 0; j < sz; j++) { xs[j] = ctx.mkConst("x_" + Integer.toString(j), domain[j]); } Expr x_i = xs[i]; /* create f(x_0, ..., x_i, ..., x_{n-1}) */ Expr fxs = f.apply(xs); /* create f_inv(f(x_0, ..., x_i, ..., x_{n-1})) */ Expr finv_fxs = finv.apply(fxs); /* create finv(f(x_0, ..., x_i, ..., x_{n-1})) = x_i */ Expr eq = ctx.mkEq(finv_fxs, x_i); /* use f(x_0, ..., x_i, ..., x_{n-1}) as the pattern for the quantifier */ Pattern p = ctx.mkPattern(fxs); /* create & assert quantifier */ BoolExpr q = ctx.mkForall(xs, /* types of quantified variables */ eq, /* names of quantified variables */ 1, new Pattern[] { p } /* patterns */, null, null, null); return q; } // / Assert the axiom: function f is commutative. // / // / This example uses the SMT-LIB parser to simplify the axiom // construction. // / private BoolExpr commAxiom(Context ctx, FuncDecl f) throws Exception { Sort t = f.getRange(); Sort[] dom = f.getDomain(); if (dom.length != 2 || !t.equals(dom[0]) || !t.equals(dom[1])) { System.out.println(Integer.toString(dom.length) + " " + dom[0].toString() + " " + dom[1].toString() + " " + t.toString()); throw new Exception( "function must be binary, and argument types must be equal to return type"); } String bench = "(assert (forall (x " + t.getName() + ") (y " + t.getName() + ") (= (" + f.getName() + " x y) (" + f.getName() + " y x))))"; return ctx.parseSMTLIB2String(bench, new Symbol[] { t.getName() }, new Sort[] { t }, new Symbol[] { f.getName() }, new FuncDecl[] { f })[0]; } // / "Hello world" example: create a Z3 logical context, and delete it. public void simpleExample() { System.out.println("SimpleExample"); Log.append("SimpleExample"); { Context ctx = new Context(); /* do something with the context */ /* be kind to dispose manually and not wait for the GC. */ ctx.close(); } } Model check(Context ctx, BoolExpr f, Status sat) throws TestFailedException { Solver s = ctx.mkSolver(); s.add(f); if (s.check() != sat) throw new TestFailedException(); if (sat == Status.SATISFIABLE) return s.getModel(); else return null; } void solveTactical(Context ctx, Tactic t, Goal g, Status sat) throws TestFailedException { Solver s = ctx.mkSolver(t); System.out.println("\nTactical solver: " + s); for (BoolExpr a : g.getFormulas()) s.add(a); System.out.println("Solver: " + s); if (s.check() != sat) throw new TestFailedException(); } ApplyResult applyTactic(Context ctx, Tactic t, Goal g) { System.out.println("\nGoal: " + g); ApplyResult res = t.apply(g); System.out.println("Application result: " + res); Status q = Status.UNKNOWN; for (Goal sg : res.getSubgoals()) if (sg.isDecidedSat()) q = Status.SATISFIABLE; else if (sg.isDecidedUnsat()) q = Status.UNSATISFIABLE; switch (q) { case UNKNOWN: System.out.println("Tactic result: Undecided"); break; case SATISFIABLE: System.out.println("Tactic result: SAT"); break; case UNSATISFIABLE: System.out.println("Tactic result: UNSAT"); break; } return res; } void prove(Context ctx, BoolExpr f, boolean useMBQI) throws TestFailedException { BoolExpr[] assumptions = new BoolExpr[0]; prove(ctx, f, useMBQI, assumptions); } void prove(Context ctx, BoolExpr f, boolean useMBQI, BoolExpr... assumptions) throws TestFailedException { System.out.println("Proving: " + f); Solver s = ctx.mkSolver(); Params p = ctx.mkParams(); p.add("mbqi", useMBQI); s.setParameters(p); for (BoolExpr a : assumptions) s.add(a); s.add(ctx.mkNot(f)); Status q = s.check(); switch (q) { case UNKNOWN: System.out.println("Unknown because: " + s.getReasonUnknown()); break; case SATISFIABLE: throw new TestFailedException(); case UNSATISFIABLE: System.out.println("OK, proof: " + s.getProof()); break; } } void disprove(Context ctx, BoolExpr f, boolean useMBQI) throws TestFailedException { BoolExpr[] a = {}; disprove(ctx, f, useMBQI, a); } void disprove(Context ctx, BoolExpr f, boolean useMBQI, BoolExpr... assumptions) throws TestFailedException { System.out.println("Disproving: " + f); Solver s = ctx.mkSolver(); Params p = ctx.mkParams(); p.add("mbqi", useMBQI); s.setParameters(p); for (BoolExpr a : assumptions) s.add(a); s.add(ctx.mkNot(f)); Status q = s.check(); switch (q) { case UNKNOWN: System.out.println("Unknown because: " + s.getReasonUnknown()); break; case SATISFIABLE: System.out.println("OK, model: " + s.getModel()); break; case UNSATISFIABLE: throw new TestFailedException(); } } void modelConverterTest(Context ctx) throws TestFailedException { System.out.println("ModelConverterTest"); ArithExpr xr = (ArithExpr) ctx.mkConst(ctx.mkSymbol("x"), ctx.mkRealSort()); ArithExpr yr = (ArithExpr) ctx.mkConst(ctx.mkSymbol("y"), ctx.mkRealSort()); Goal g4 = ctx.mkGoal(true, false, false); g4.add(ctx.mkGt(xr, ctx.mkReal(10, 1))); g4.add(ctx.mkEq(yr, ctx.mkAdd(xr, ctx.mkReal(1, 1)))); g4.add(ctx.mkGt(yr, ctx.mkReal(1, 1))); ApplyResult ar = applyTactic(ctx, ctx.mkTactic("simplify"), g4); if (ar.getNumSubgoals() == 1 && (ar.getSubgoals()[0].isDecidedSat() || ar.getSubgoals()[0] .isDecidedUnsat())) throw new TestFailedException(); ar = applyTactic(ctx, ctx.andThen(ctx.mkTactic("simplify"), ctx.mkTactic("solve-eqs")), g4); if (ar.getNumSubgoals() == 1 && (ar.getSubgoals()[0].isDecidedSat() || ar.getSubgoals()[0] .isDecidedUnsat())) throw new TestFailedException(); Solver s = ctx.mkSolver(); for (BoolExpr e : ar.getSubgoals()[0].getFormulas()) s.add(e); Status q = s.check(); System.out.println("Solver says: " + q); System.out.println("Model: \n" + s.getModel()); if (q != Status.SATISFIABLE) throw new TestFailedException(); } // / A simple array example. void arrayExample1(Context ctx) throws TestFailedException { System.out.println("ArrayExample1"); Log.append("ArrayExample1"); Goal g = ctx.mkGoal(true, false, false); ArraySort asort = ctx.mkArraySort(ctx.getIntSort(), ctx.mkBitVecSort(32)); ArrayExpr aex = (ArrayExpr) ctx.mkConst(ctx.mkSymbol("MyArray"), asort); Expr sel = ctx.mkSelect(aex, ctx.mkInt(0)); g.add(ctx.mkEq(sel, ctx.mkBV(42, 32))); Symbol xs = ctx.mkSymbol("x"); IntExpr xc = (IntExpr) ctx.mkConst(xs, ctx.getIntSort()); Symbol fname = ctx.mkSymbol("f"); Sort[] domain = { ctx.getIntSort() }; FuncDecl fd = ctx.mkFuncDecl(fname, domain, ctx.getIntSort()); Expr[] fargs = { ctx.mkConst(xs, ctx.getIntSort()) }; IntExpr fapp = (IntExpr) ctx.mkApp(fd, fargs); g.add(ctx.mkEq(ctx.mkAdd(xc, fapp), ctx.mkInt(123))); Solver s = ctx.mkSolver(); for (BoolExpr a : g.getFormulas()) s.add(a); System.out.println("Solver: " + s); Status q = s.check(); System.out.println("Status: " + q); if (q != Status.SATISFIABLE) throw new TestFailedException(); System.out.println("Model = " + s.getModel()); System.out.println("Interpretation of MyArray:\n" + s.getModel().getFuncInterp(aex.getFuncDecl())); System.out.println("Interpretation of x:\n" + s.getModel().getConstInterp(xc)); System.out.println("Interpretation of f:\n" + s.getModel().getFuncInterp(fd)); System.out.println("Interpretation of MyArray as Term:\n" + s.getModel().getFuncInterp(aex.getFuncDecl())); } // / Prove store(a1, i1, v1) = store(a2, i2, v2) implies (i1 = i3 or i2 // = i3 or select(a1, i3) = select(a2, i3)). // / This example demonstrates how to use the array // theory. public void arrayExample2(Context ctx) throws TestFailedException { System.out.println("ArrayExample2"); Log.append("ArrayExample2"); Sort int_type = ctx.getIntSort(); Sort array_type = ctx.mkArraySort(int_type, int_type); ArrayExpr a1 = (ArrayExpr) ctx.mkConst("a1", array_type); ArrayExpr a2 = ctx.mkArrayConst("a2", int_type, int_type); Expr i1 = ctx.mkConst("i1", int_type); Expr i2 = ctx.mkConst("i2", int_type); Expr i3 = ctx.mkConst("i3", int_type); Expr v1 = ctx.mkConst("v1", int_type); Expr v2 = ctx.mkConst("v2", int_type); Expr st1 = ctx.mkStore(a1, i1, v1); Expr st2 = ctx.mkStore(a2, i2, v2); Expr sel1 = ctx.mkSelect(a1, i3); Expr sel2 = ctx.mkSelect(a2, i3); /* create antecedent */ BoolExpr antecedent = ctx.mkEq(st1, st2); /* * create consequent: i1 = i3 or i2 = i3 or select(a1, i3) = select(a2, * i3) */ BoolExpr consequent = ctx.mkOr(ctx.mkEq(i1, i3), ctx.mkEq(i2, i3), ctx.mkEq(sel1, sel2)); /* * prove store(a1, i1, v1) = store(a2, i2, v2) implies (i1 = i3 or i2 = * i3 or select(a1, i3) = select(a2, i3)) */ BoolExpr thm = ctx.mkImplies(antecedent, consequent); System.out .println("prove: store(a1, i1, v1) = store(a2, i2, v2) implies (i1 = i3 or i2 = i3 or select(a1, i3) = select(a2, i3))"); System.out.println(thm); prove(ctx, thm, false); } // / Show that distinct(a_0, ... , a_n) is // / unsatisfiable when a_i's are arrays from boolean to // / boolean and n > 4. // / This example also shows how to use the distinct // construct. public void arrayExample3(Context ctx) throws TestFailedException { System.out.println("ArrayExample3"); Log.append("ArrayExample2"); for (int n = 2; n <= 5; n++) { System.out.println("n = " + Integer.toString(n)); Sort bool_type = ctx.mkBoolSort(); Sort array_type = ctx.mkArraySort(bool_type, bool_type); Expr[] a = new Expr[n]; /* create arrays */ for (int i = 0; i < n; i++) { a[i] = ctx.mkConst("array_" + Integer.toString(i), array_type); } /* assert distinct(a[0], ..., a[n]) */ BoolExpr d = ctx.mkDistinct(a); System.out.println(d); /* context is satisfiable if n < 5 */ Model model = check(ctx, d, n < 5 ? Status.SATISFIABLE : Status.UNSATISFIABLE); if (n < 5) { for (int i = 0; i < n; i++) { System.out.println(a[i].toString() + " = " + model.evaluate(a[i], false)); } } } } // / Sudoku solving example. void sudokuExample(Context ctx) throws TestFailedException { System.out.println("SudokuExample"); Log.append("SudokuExample"); // 9x9 matrix of integer variables IntExpr[][] X = new IntExpr[9][]; for (int i = 0; i < 9; i++) { X[i] = new IntExpr[9]; for (int j = 0; j < 9; j++) X[i][j] = (IntExpr) ctx.mkConst( ctx.mkSymbol("x_" + (i + 1) + "_" + (j + 1)), ctx.getIntSort()); } // each cell contains a value in {1, ..., 9} BoolExpr[][] cells_c = new BoolExpr[9][]; for (int i = 0; i < 9; i++) { cells_c[i] = new BoolExpr[9]; for (int j = 0; j < 9; j++) cells_c[i][j] = ctx.mkAnd(ctx.mkLe(ctx.mkInt(1), X[i][j]), ctx.mkLe(X[i][j], ctx.mkInt(9))); } // each row contains a digit at most once BoolExpr[] rows_c = new BoolExpr[9]; for (int i = 0; i < 9; i++) rows_c[i] = ctx.mkDistinct(X[i]); // each column contains a digit at most once BoolExpr[] cols_c = new BoolExpr[9]; for (int j = 0; j < 9; j++) { IntExpr[] col = new IntExpr[9]; for (int i = 0; i < 9; i++) { col[i] = X[i][j]; } cols_c[j] = ctx.mkDistinct(col); } // each 3x3 square contains a digit at most once BoolExpr[][] sq_c = new BoolExpr[3][]; for (int i0 = 0; i0 < 3; i0++) { sq_c[i0] = new BoolExpr[3]; for (int j0 = 0; j0 < 3; j0++) { IntExpr[] square = new IntExpr[9]; for (int i = 0; i < 3; i++) for (int j = 0; j < 3; j++) square[3 * i + j] = X[3 * i0 + i][3 * j0 + j]; sq_c[i0][j0] = ctx.mkDistinct(square); } } BoolExpr sudoku_c = ctx.mkTrue(); for (BoolExpr[] t : cells_c) sudoku_c = ctx.mkAnd(ctx.mkAnd(t), sudoku_c); sudoku_c = ctx.mkAnd(ctx.mkAnd(rows_c), sudoku_c); sudoku_c = ctx.mkAnd(ctx.mkAnd(cols_c), sudoku_c); for (BoolExpr[] t : sq_c) sudoku_c = ctx.mkAnd(ctx.mkAnd(t), sudoku_c); // sudoku instance, we use '0' for empty cells int[][] instance = { { 0, 0, 0, 0, 9, 4, 0, 3, 0 }, { 0, 0, 0, 5, 1, 0, 0, 0, 7 }, { 0, 8, 9, 0, 0, 0, 0, 4, 0 }, { 0, 0, 0, 0, 0, 0, 2, 0, 8 }, { 0, 6, 0, 2, 0, 1, 0, 5, 0 }, { 1, 0, 2, 0, 0, 0, 0, 0, 0 }, { 0, 7, 0, 0, 0, 0, 5, 2, 0 }, { 9, 0, 0, 0, 6, 5, 0, 0, 0 }, { 0, 4, 0, 9, 7, 0, 0, 0, 0 } }; BoolExpr instance_c = ctx.mkTrue(); for (int i = 0; i < 9; i++) for (int j = 0; j < 9; j++) if (0 != instance[i][j]) instance_c = ctx.mkAnd( instance_c, ctx.mkEq(X[i][j], ctx.mkInt(instance[i][j]))); Solver s = ctx.mkSolver(); s.add(sudoku_c); s.add(instance_c); if (s.check() == Status.SATISFIABLE) { Model m = s.getModel(); Expr[][] R = new Expr[9][9]; for (int i = 0; i < 9; i++) for (int j = 0; j < 9; j++) R[i][j] = m.evaluate(X[i][j], false); System.out.println("Sudoku solution:"); for (int i = 0; i < 9; i++) { for (int j = 0; j < 9; j++) System.out.print(" " + R[i][j]); System.out.println(); } } else { System.out.println("Failed to solve sudoku"); throw new TestFailedException(); } } // / A basic example of how to use quantifiers. void quantifierExample1(Context ctx) { System.out.println("QuantifierExample"); Log.append("QuantifierExample"); Sort[] types = new Sort[3]; IntExpr[] xs = new IntExpr[3]; Symbol[] names = new Symbol[3]; IntExpr[] vars = new IntExpr[3]; for (int j = 0; j < 3; j++) { types[j] = ctx.getIntSort(); names[j] = ctx.mkSymbol("x_" + Integer.toString(j)); xs[j] = (IntExpr) ctx.mkConst(names[j], types[j]); vars[j] = (IntExpr) ctx.mkBound(2 - j, types[j]); // <-- vars // reversed! } Expr body_vars = ctx.mkAnd( ctx.mkEq(ctx.mkAdd(vars[0], ctx.mkInt(1)), ctx.mkInt(2)), ctx.mkEq(ctx.mkAdd(vars[1], ctx.mkInt(2)), ctx.mkAdd(vars[2], ctx.mkInt(3)))); Expr body_const = ctx.mkAnd( ctx.mkEq(ctx.mkAdd(xs[0], ctx.mkInt(1)), ctx.mkInt(2)), ctx.mkEq(ctx.mkAdd(xs[1], ctx.mkInt(2)), ctx.mkAdd(xs[2], ctx.mkInt(3)))); Expr x = ctx.mkForall(types, names, body_vars, 1, null, null, ctx.mkSymbol("Q1"), ctx.mkSymbol("skid1")); System.out.println("Quantifier X: " + x.toString()); Expr y = ctx.mkForall(xs, body_const, 1, null, null, ctx.mkSymbol("Q2"), ctx.mkSymbol("skid2")); System.out.println("Quantifier Y: " + y.toString()); } void quantifierExample2(Context ctx) { System.out.println("QuantifierExample2"); Log.append("QuantifierExample2"); Expr q1, q2; FuncDecl f = ctx.mkFuncDecl("f", ctx.getIntSort(), ctx.getIntSort()); FuncDecl g = ctx.mkFuncDecl("g", ctx.getIntSort(), ctx.getIntSort()); // Quantifier with Exprs as the bound variables. { Expr x = ctx.mkConst("x", ctx.getIntSort()); Expr y = ctx.mkConst("y", ctx.getIntSort()); Expr f_x = ctx.mkApp(f, x); Expr f_y = ctx.mkApp(f, y); Expr g_y = ctx.mkApp(g, y); @SuppressWarnings("unused") Pattern[] pats = new Pattern[] { ctx.mkPattern(f_x, g_y) }; Expr[] no_pats = new Expr[] { f_y }; Expr[] bound = new Expr[] { x, y }; Expr body = ctx.mkAnd(ctx.mkEq(f_x, f_y), ctx.mkEq(f_y, g_y)); q1 = ctx.mkForall(bound, body, 1, null, no_pats, ctx.mkSymbol("q"), ctx.mkSymbol("sk")); System.out.println(q1); } // Quantifier with de-Bruijn indices. { Expr x = ctx.mkBound(1, ctx.getIntSort()); Expr y = ctx.mkBound(0, ctx.getIntSort()); Expr f_x = ctx.mkApp(f, x); Expr f_y = ctx.mkApp(f, y); Expr g_y = ctx.mkApp(g, y); @SuppressWarnings("unused") Pattern[] pats = new Pattern[] { ctx.mkPattern(f_x, g_y) }; Expr[] no_pats = new Expr[] { f_y }; Symbol[] names = new Symbol[] { ctx.mkSymbol("x"), ctx.mkSymbol("y") }; Sort[] sorts = new Sort[] { ctx.getIntSort(), ctx.getIntSort() }; Expr body = ctx.mkAnd(ctx.mkEq(f_x, f_y), ctx.mkEq(f_y, g_y)); q2 = ctx.mkForall(sorts, names, body, 1, null, // pats, no_pats, ctx.mkSymbol("q"), ctx.mkSymbol("sk")); System.out.println(q2); } System.out.println(q1.equals(q2)); } // / Prove that f(x, y) = f(w, v) implies y = v when // / f is injective in the second argument. public void quantifierExample3(Context ctx) throws TestFailedException { System.out.println("QuantifierExample3"); Log.append("QuantifierExample3"); /* * If quantified formulas are asserted in a logical context, then the * model produced by Z3 should be viewed as a potential model. */ /* declare function f */ Sort I = ctx.getIntSort(); FuncDecl f = ctx.mkFuncDecl("f", new Sort[] { I, I }, I); /* f is injective in the second argument. */ BoolExpr inj = injAxiom(ctx, f, 1); /* create x, y, v, w, fxy, fwv */ Expr x = ctx.mkIntConst("x"); Expr y = ctx.mkIntConst("y"); Expr v = ctx.mkIntConst("v"); Expr w = ctx.mkIntConst("w"); Expr fxy = ctx.mkApp(f, x, y); Expr fwv = ctx.mkApp(f, w, v); /* f(x, y) = f(w, v) */ BoolExpr p1 = ctx.mkEq(fxy, fwv); /* prove f(x, y) = f(w, v) implies y = v */ BoolExpr p2 = ctx.mkEq(y, v); prove(ctx, p2, false, inj, p1); /* disprove f(x, y) = f(w, v) implies x = w */ BoolExpr p3 = ctx.mkEq(x, w); disprove(ctx, p3, false, inj, p1); } // / Prove that f(x, y) = f(w, v) implies y = v when // / f is injective in the second argument. public void quantifierExample4(Context ctx) throws TestFailedException { System.out.println("QuantifierExample4"); Log.append("QuantifierExample4"); /* * If quantified formulas are asserted in a logical context, then the * model produced by Z3 should be viewed as a potential model. */ /* declare function f */ Sort I = ctx.getIntSort(); FuncDecl f = ctx.mkFuncDecl("f", new Sort[] { I, I }, I); /* f is injective in the second argument. */ BoolExpr inj = injAxiomAbs(ctx, f, 1); /* create x, y, v, w, fxy, fwv */ Expr x = ctx.mkIntConst("x"); Expr y = ctx.mkIntConst("y"); Expr v = ctx.mkIntConst("v"); Expr w = ctx.mkIntConst("w"); Expr fxy = ctx.mkApp(f, x, y); Expr fwv = ctx.mkApp(f, w, v); /* f(x, y) = f(w, v) */ BoolExpr p1 = ctx.mkEq(fxy, fwv); /* prove f(x, y) = f(w, v) implies y = v */ BoolExpr p2 = ctx.mkEq(y, v); prove(ctx, p2, false, inj, p1); /* disprove f(x, y) = f(w, v) implies x = w */ BoolExpr p3 = ctx.mkEq(x, w); disprove(ctx, p3, false, inj, p1); } // / Some basic tests. void basicTests(Context ctx) throws TestFailedException { System.out.println("BasicTests"); Symbol fname = ctx.mkSymbol("f"); Symbol x = ctx.mkSymbol("x"); Symbol y = ctx.mkSymbol("y"); Sort bs = ctx.mkBoolSort(); Sort[] domain = { bs, bs }; FuncDecl f = ctx.mkFuncDecl(fname, domain, bs); Expr fapp = ctx.mkApp(f, ctx.mkConst(x, bs), ctx.mkConst(y, bs)); Expr[] fargs2 = { ctx.mkFreshConst("cp", bs) }; Sort[] domain2 = { bs }; Expr fapp2 = ctx.mkApp(ctx.mkFreshFuncDecl("fp", domain2, bs), fargs2); BoolExpr trivial_eq = ctx.mkEq(fapp, fapp); BoolExpr nontrivial_eq = ctx.mkEq(fapp, fapp2); Goal g = ctx.mkGoal(true, false, false); g.add(trivial_eq); g.add(nontrivial_eq); System.out.println("Goal: " + g); Solver solver = ctx.mkSolver(); for (BoolExpr a : g.getFormulas()) solver.add(a); if (solver.check() != Status.SATISFIABLE) throw new TestFailedException(); ApplyResult ar = applyTactic(ctx, ctx.mkTactic("simplify"), g); if (ar.getNumSubgoals() == 1 && (ar.getSubgoals()[0].isDecidedSat() || ar.getSubgoals()[0] .isDecidedUnsat())) throw new TestFailedException(); ar = applyTactic(ctx, ctx.mkTactic("smt"), g); if (ar.getNumSubgoals() != 1 || !ar.getSubgoals()[0].isDecidedSat()) throw new TestFailedException(); g.add(ctx.mkEq(ctx.mkNumeral(1, ctx.mkBitVecSort(32)), ctx.mkNumeral(2, ctx.mkBitVecSort(32)))); ar = applyTactic(ctx, ctx.mkTactic("smt"), g); if (ar.getNumSubgoals() != 1 || !ar.getSubgoals()[0].isDecidedUnsat()) throw new TestFailedException(); Goal g2 = ctx.mkGoal(true, true, false); ar = applyTactic(ctx, ctx.mkTactic("smt"), g2); if (ar.getNumSubgoals() != 1 || !ar.getSubgoals()[0].isDecidedSat()) throw new TestFailedException(); g2 = ctx.mkGoal(true, true, false); g2.add(ctx.mkFalse()); ar = applyTactic(ctx, ctx.mkTactic("smt"), g2); if (ar.getNumSubgoals() != 1 || !ar.getSubgoals()[0].isDecidedUnsat()) throw new TestFailedException(); Goal g3 = ctx.mkGoal(true, true, false); Expr xc = ctx.mkConst(ctx.mkSymbol("x"), ctx.getIntSort()); Expr yc = ctx.mkConst(ctx.mkSymbol("y"), ctx.getIntSort()); g3.add(ctx.mkEq(xc, ctx.mkNumeral(1, ctx.getIntSort()))); g3.add(ctx.mkEq(yc, ctx.mkNumeral(2, ctx.getIntSort()))); BoolExpr constr = ctx.mkEq(xc, yc); g3.add(constr); ar = applyTactic(ctx, ctx.mkTactic("smt"), g3); if (ar.getNumSubgoals() != 1 || !ar.getSubgoals()[0].isDecidedUnsat()) throw new TestFailedException(); modelConverterTest(ctx); // Real num/den test. RatNum rn = ctx.mkReal(42, 43); Expr inum = rn.getNumerator(); Expr iden = rn.getDenominator(); System.out.println("Numerator: " + inum + " Denominator: " + iden); if (!inum.toString().equals("42") || !iden.toString().equals("43")) throw new TestFailedException(); if (!rn.toDecimalString(3).toString().equals("0.976?")) throw new TestFailedException(); bigIntCheck(ctx, ctx.mkReal("-1231231232/234234333")); bigIntCheck(ctx, ctx.mkReal("-123123234234234234231232/234234333")); bigIntCheck(ctx, ctx.mkReal("-234234333")); bigIntCheck(ctx, ctx.mkReal("234234333/2")); String bn = "1234567890987654321"; if (!ctx.mkInt(bn).getBigInteger().toString().equals(bn)) throw new TestFailedException(); if (!ctx.mkBV(bn, 128).getBigInteger().toString().equals(bn)) throw new TestFailedException(); if (ctx.mkBV(bn, 32).getBigInteger().toString().equals(bn)) throw new TestFailedException(); // Error handling test. try { @SuppressWarnings("unused") IntExpr i = ctx.mkInt("1/2"); throw new TestFailedException(); // unreachable } catch (Z3Exception e) { } } // / Some basic expression casting tests. void castingTest(Context ctx) throws TestFailedException { System.out.println("CastingTest"); Sort[] domain = { ctx.getBoolSort(), ctx.getBoolSort() }; FuncDecl f = ctx.mkFuncDecl("f", domain, ctx.getBoolSort()); AST upcast = ctx.mkFuncDecl(ctx.mkSymbol("q"), domain, ctx.getBoolSort()); try { @SuppressWarnings("unused") FuncDecl downcast = (FuncDecl) f; // OK } catch (ClassCastException e) { throw new TestFailedException(); } try { @SuppressWarnings("unused") Expr uc = (Expr) upcast; throw new TestFailedException(); // should not be reachable! } catch (ClassCastException e) { } Symbol s = ctx.mkSymbol(42); IntSymbol si = (s.getClass() == IntSymbol.class) ? (IntSymbol) s : null; if (si == null) throw new TestFailedException(); try { @SuppressWarnings("unused") IntSymbol si2 = (IntSymbol) s; } catch (ClassCastException e) { throw new TestFailedException(); } s = ctx.mkSymbol("abc"); StringSymbol ss = (s.getClass() == StringSymbol.class) ? (StringSymbol) s : null; if (ss == null) throw new TestFailedException(); try { @SuppressWarnings("unused") StringSymbol ss2 = (StringSymbol) s; } catch (ClassCastException e) { throw new TestFailedException(); } try { @SuppressWarnings("unused") IntSymbol si2 = (IntSymbol) s; throw new TestFailedException(); // unreachable } catch (Exception e) { } Sort srt = ctx.mkBitVecSort(32); BitVecSort bvs = null; try { bvs = (BitVecSort) srt; } catch (ClassCastException e) { throw new TestFailedException(); } if (bvs.getSize() != 32) throw new TestFailedException(); Expr q = ctx.mkAdd(ctx.mkInt(1), ctx.mkInt(2)); Expr q2 = q.getArgs()[1]; Sort qs = q2.getSort(); if (qs.getClass() != IntSort.class) throw new TestFailedException(); try { @SuppressWarnings("unused") IntSort isrt = (IntSort) qs; } catch (ClassCastException e) { throw new TestFailedException(); } AST a = ctx.mkInt(42); try { Expr.class.cast(a); } catch (ClassCastException e) { throw new TestFailedException(); } try { ArithExpr.class.cast(a); } catch (ClassCastException e) { throw new TestFailedException(); } try { IntExpr.class.cast(a); } catch (ClassCastException e) { throw new TestFailedException(); } try { IntNum.class.cast(a); } catch (ClassCastException e) { throw new TestFailedException(); } Expr[][] earr = new Expr[2][]; earr[0] = new Expr[2]; earr[1] = new Expr[2]; earr[0][0] = ctx.mkTrue(); earr[0][1] = ctx.mkTrue(); earr[1][0] = ctx.mkFalse(); earr[1][1] = ctx.mkFalse(); for (Expr[] ea : earr) for (Expr e : ea) { try { Expr ns = ctx.mkNot((BoolExpr) e); @SuppressWarnings("unused") BoolExpr ens = (BoolExpr) ns; } catch (ClassCastException ex) { throw new TestFailedException(); } } } // / Shows how to read an SMT2 file. void smt2FileTest(String filename) { Date before = new Date(); System.out.println("SMT2 File test "); System.gc(); { HashMap cfg = new HashMap(); cfg.put("model", "true"); Context ctx = new Context(cfg); BoolExpr a = ctx.mkAnd(ctx.parseSMTLIB2File(filename, null, null, null, null)); long t_diff = ((new Date()).getTime() - before.getTime()) / 1000; System.out.println("SMT2 file read time: " + t_diff + " sec"); // Iterate over the formula. LinkedList q = new LinkedList(); q.add(a); int cnt = 0; while (q.size() > 0) { AST cur = (AST) q.removeFirst(); cnt++; if (cur.getClass() == Expr.class) if (!(cur.isVar())) for (Expr c : ((Expr) cur).getArgs()) q.add(c); } System.out.println(cnt + " ASTs"); } long t_diff = ((new Date()).getTime() - before.getTime()) / 1000; System.out.println("SMT2 file test took " + t_diff + " sec"); } // / Shows how to use Solver(logic) // / @param ctx void logicExample(Context ctx) throws TestFailedException { System.out.println("LogicTest"); Log.append("LogicTest"); com.microsoft.z3.Global.ToggleWarningMessages(true); BitVecSort bvs = ctx.mkBitVecSort(32); Expr x = ctx.mkConst("x", bvs); Expr y = ctx.mkConst("y", bvs); BoolExpr eq = ctx.mkEq(x, y); // Use a solver for QF_BV Solver s = ctx.mkSolver("QF_BV"); s.add(eq); Status res = s.check(); System.out.println("solver result: " + res); // Or perhaps a tactic for QF_BV Goal g = ctx.mkGoal(true, false, false); g.add(eq); Tactic t = ctx.mkTactic("qfbv"); ApplyResult ar = t.apply(g); System.out.println("tactic result: " + ar); if (ar.getNumSubgoals() != 1 || !ar.getSubgoals()[0].isDecidedSat()) throw new TestFailedException(); } // / Demonstrates how to use the ParOr tactic. void parOrExample(Context ctx) throws TestFailedException { System.out.println("ParOrExample"); Log.append("ParOrExample"); BitVecSort bvs = ctx.mkBitVecSort(32); Expr x = ctx.mkConst("x", bvs); Expr y = ctx.mkConst("y", bvs); BoolExpr q = ctx.mkEq(x, y); Goal g = ctx.mkGoal(true, false, false); g.add(q); Tactic t1 = ctx.mkTactic("qfbv"); Tactic t2 = ctx.mkTactic("qfbv"); Tactic p = ctx.parOr(t1, t2); ApplyResult ar = p.apply(g); if (ar.getNumSubgoals() != 1 || !ar.getSubgoals()[0].isDecidedSat()) throw new TestFailedException(); } void bigIntCheck(Context ctx, RatNum r) { System.out.println("Num: " + r.getBigIntNumerator()); System.out.println("Den: " + r.getBigIntDenominator()); } // / Find a model for x xor y. public void findModelExample1(Context ctx) throws TestFailedException { System.out.println("FindModelExample1"); Log.append("FindModelExample1"); BoolExpr x = ctx.mkBoolConst("x"); BoolExpr y = ctx.mkBoolConst("y"); BoolExpr x_xor_y = ctx.mkXor(x, y); Model model = check(ctx, x_xor_y, Status.SATISFIABLE); System.out.println("x = " + model.evaluate(x, false) + ", y = " + model.evaluate(y, false)); } // / Find a model for x < y + 1, x > 2. // / Then, assert not(x = y), and find another model. public void findModelExample2(Context ctx) throws TestFailedException { System.out.println("FindModelExample2"); Log.append("FindModelExample2"); IntExpr x = ctx.mkIntConst("x"); IntExpr y = ctx.mkIntConst("y"); IntExpr one = ctx.mkInt(1); IntExpr two = ctx.mkInt(2); ArithExpr y_plus_one = ctx.mkAdd(y, one); BoolExpr c1 = ctx.mkLt(x, y_plus_one); BoolExpr c2 = ctx.mkGt(x, two); BoolExpr q = ctx.mkAnd(c1, c2); System.out.println("model for: x < y + 1, x > 2"); Model model = check(ctx, q, Status.SATISFIABLE); System.out.println("x = " + model.evaluate(x, false) + ", y =" + model.evaluate(y, false)); /* assert not(x = y) */ BoolExpr x_eq_y = ctx.mkEq(x, y); BoolExpr c3 = ctx.mkNot(x_eq_y); q = ctx.mkAnd(q, c3); System.out.println("model for: x < y + 1, x > 2, not(x = y)"); model = check(ctx, q, Status.SATISFIABLE); System.out.println("x = " + model.evaluate(x, false) + ", y = " + model.evaluate(y, false)); } // / Prove x = y implies g(x) = g(y), and // / disprove x = y implies g(g(x)) = g(y). // / This function demonstrates how to create uninterpreted // / types and functions. public void proveExample1(Context ctx) throws TestFailedException { System.out.println("ProveExample1"); Log.append("ProveExample1"); /* create uninterpreted type. */ Sort U = ctx.mkUninterpretedSort(ctx.mkSymbol("U")); /* declare function g */ FuncDecl g = ctx.mkFuncDecl("g", U, U); /* create x and y */ Expr x = ctx.mkConst("x", U); Expr y = ctx.mkConst("y", U); /* create g(x), g(y) */ Expr gx = g.apply(x); Expr gy = g.apply(y); /* assert x = y */ BoolExpr eq = ctx.mkEq(x, y); /* prove g(x) = g(y) */ BoolExpr f = ctx.mkEq(gx, gy); System.out.println("prove: x = y implies g(x) = g(y)"); prove(ctx, ctx.mkImplies(eq, f), false); /* create g(g(x)) */ Expr ggx = g.apply(gx); /* disprove g(g(x)) = g(y) */ f = ctx.mkEq(ggx, gy); System.out.println("disprove: x = y implies g(g(x)) = g(y)"); disprove(ctx, ctx.mkImplies(eq, f), false); /* Print the model using the custom model printer */ Model m = check(ctx, ctx.mkNot(f), Status.SATISFIABLE); System.out.println(m); } // / Prove not(g(g(x) - g(y)) = g(z)), x + z <= y <= x implies z < 0 // . // / Then, show that z < -1 is not implied. // / This example demonstrates how to combine uninterpreted // functions // / and arithmetic. public void proveExample2(Context ctx) throws TestFailedException { System.out.println("ProveExample2"); Log.append("ProveExample2"); /* declare function g */ Sort I = ctx.getIntSort(); FuncDecl g = ctx.mkFuncDecl("g", I, I); /* create x, y, and z */ IntExpr x = ctx.mkIntConst("x"); IntExpr y = ctx.mkIntConst("y"); IntExpr z = ctx.mkIntConst("z"); /* create gx, gy, gz */ Expr gx = ctx.mkApp(g, x); Expr gy = ctx.mkApp(g, y); Expr gz = ctx.mkApp(g, z); /* create zero */ IntExpr zero = ctx.mkInt(0); /* assert not(g(g(x) - g(y)) = g(z)) */ ArithExpr gx_gy = ctx.mkSub((IntExpr) gx, (IntExpr) gy); Expr ggx_gy = ctx.mkApp(g, gx_gy); BoolExpr eq = ctx.mkEq(ggx_gy, gz); BoolExpr c1 = ctx.mkNot(eq); /* assert x + z <= y */ ArithExpr x_plus_z = ctx.mkAdd(x, z); BoolExpr c2 = ctx.mkLe(x_plus_z, y); /* assert y <= x */ BoolExpr c3 = ctx.mkLe(y, x); /* prove z < 0 */ BoolExpr f = ctx.mkLt(z, zero); System.out .println("prove: not(g(g(x) - g(y)) = g(z)), x + z <= y <= x implies z < 0"); prove(ctx, f, false, c1, c2, c3); /* disprove z < -1 */ IntExpr minus_one = ctx.mkInt(-1); f = ctx.mkLt(z, minus_one); System.out .println("disprove: not(g(g(x) - g(y)) = g(z)), x + z <= y <= x implies z < -1"); disprove(ctx, f, false, c1, c2, c3); } // / Show how push & pop can be used to create "backtracking" points. // / This example also demonstrates how big numbers can be // / created in ctx. public void pushPopExample1(Context ctx) throws TestFailedException { System.out.println("PushPopExample1"); Log.append("PushPopExample1"); /* create a big number */ IntSort int_type = ctx.getIntSort(); IntExpr big_number = ctx .mkInt("1000000000000000000000000000000000000000000000000000000"); /* create number 3 */ IntExpr three = (IntExpr) ctx.mkNumeral("3", int_type); /* create x */ IntExpr x = ctx.mkIntConst("x"); Solver solver = ctx.mkSolver(); /* assert x >= "big number" */ BoolExpr c1 = ctx.mkGe(x, big_number); System.out.println("assert: x >= 'big number'"); solver.add(c1); /* create a backtracking point */ System.out.println("push"); solver.push(); /* assert x <= 3 */ BoolExpr c2 = ctx.mkLe(x, three); System.out.println("assert: x <= 3"); solver.add(c2); /* context is inconsistent at this point */ if (solver.check() != Status.UNSATISFIABLE) throw new TestFailedException(); /* * backtrack: the constraint x <= 3 will be removed, since it was * asserted after the last ctx.Push. */ System.out.println("pop"); solver.pop(1); /* the context is consistent again. */ if (solver.check() != Status.SATISFIABLE) throw new TestFailedException(); /* new constraints can be asserted... */ /* create y */ IntExpr y = ctx.mkIntConst("y"); /* assert y > x */ BoolExpr c3 = ctx.mkGt(y, x); System.out.println("assert: y > x"); solver.add(c3); /* the context is still consistent. */ if (solver.check() != Status.SATISFIABLE) throw new TestFailedException(); } // / Tuples. // / Check that the projection of a tuple // / returns the corresponding element. public void tupleExample(Context ctx) throws TestFailedException { System.out.println("TupleExample"); Log.append("TupleExample"); Sort int_type = ctx.getIntSort(); TupleSort tuple = ctx.mkTupleSort(ctx.mkSymbol("mk_tuple"), // name of // tuple // constructor new Symbol[] { ctx.mkSymbol("first"), ctx.mkSymbol("second") }, // names // of // projection // operators new Sort[] { int_type, int_type } // types of projection // operators ); FuncDecl first = tuple.getFieldDecls()[0]; // declarations are for // projections @SuppressWarnings("unused") FuncDecl second = tuple.getFieldDecls()[1]; Expr x = ctx.mkConst("x", int_type); Expr y = ctx.mkConst("y", int_type); Expr n1 = tuple.mkDecl().apply(x, y); Expr n2 = first.apply(n1); BoolExpr n3 = ctx.mkEq(x, n2); System.out.println("Tuple example: " + n3); prove(ctx, n3, false); } // / Simple bit-vector example. // / // / This example disproves that x - 10 <= 0 IFF x <= 10 for (32-bit) // machine integers // / public void bitvectorExample1(Context ctx) throws TestFailedException { System.out.println("BitvectorExample1"); Log.append("BitvectorExample1"); Sort bv_type = ctx.mkBitVecSort(32); BitVecExpr x = (BitVecExpr) ctx.mkConst("x", bv_type); BitVecNum zero = (BitVecNum) ctx.mkNumeral("0", bv_type); BitVecNum ten = ctx.mkBV(10, 32); BitVecExpr x_minus_ten = ctx.mkBVSub(x, ten); /* bvsle is signed less than or equal to */ BoolExpr c1 = ctx.mkBVSLE(x, ten); BoolExpr c2 = ctx.mkBVSLE(x_minus_ten, zero); BoolExpr thm = ctx.mkIff(c1, c2); System.out .println("disprove: x - 10 <= 0 IFF x <= 10 for (32-bit) machine integers"); disprove(ctx, thm, false); } // / Find x and y such that: x ^ y - 103 == x * y public void bitvectorExample2(Context ctx) throws TestFailedException { System.out.println("BitvectorExample2"); Log.append("BitvectorExample2"); /* construct x ^ y - 103 == x * y */ Sort bv_type = ctx.mkBitVecSort(32); BitVecExpr x = ctx.mkBVConst("x", 32); BitVecExpr y = ctx.mkBVConst("y", 32); BitVecExpr x_xor_y = ctx.mkBVXOR(x, y); BitVecExpr c103 = (BitVecNum) ctx.mkNumeral("103", bv_type); BitVecExpr lhs = ctx.mkBVSub(x_xor_y, c103); BitVecExpr rhs = ctx.mkBVMul(x, y); BoolExpr ctr = ctx.mkEq(lhs, rhs); System.out .println("find values of x and y, such that x ^ y - 103 == x * y"); /* find a model (i.e., values for x an y that satisfy the constraint */ Model m = check(ctx, ctr, Status.SATISFIABLE); System.out.println(m); } // / Demonstrates how to use the SMTLIB parser. public void parserExample1(Context ctx) throws TestFailedException { System.out.println("ParserExample1"); Log.append("ParserExample1"); BoolExpr f = ctx.parseSMTLIB2String( "(declare-const x Int) (declare-const y Int) (assert (and (> x y) (> x 0)))", null, null, null, null)[0]; System.out.println("formula " + f); @SuppressWarnings("unused") Model m = check(ctx, f, Status.SATISFIABLE); } // / Demonstrates how to initialize the parser symbol table. public void parserExample2(Context ctx) throws TestFailedException { System.out.println("ParserExample2"); Log.append("ParserExample2"); Symbol[] declNames = { ctx.mkSymbol("a"), ctx.mkSymbol("b") }; FuncDecl a = ctx.mkConstDecl(declNames[0], ctx.mkIntSort()); FuncDecl b = ctx.mkConstDecl(declNames[1], ctx.mkIntSort()); FuncDecl[] decls = new FuncDecl[] { a, b }; BoolExpr f = ctx.parseSMTLIB2String("(assert (> a b))", null, null, declNames, decls)[0]; System.out.println("formula: " + f); check(ctx, f, Status.SATISFIABLE); } // / Demonstrates how to initialize the parser symbol table. public void parserExample3(Context ctx) throws Exception { System.out.println("ParserExample3"); Log.append("ParserExample3"); /* declare function g */ Sort I = ctx.mkIntSort(); FuncDecl g = ctx.mkFuncDecl("g", new Sort[] { I, I }, I); BoolExpr ca = commAxiom(ctx, g); BoolExpr thm = ctx.parseSMTLIB2String( "(declare-fun (Int Int) Int) (assert (forall ((x Int) (y Int)) (=> (= x y) (= (gg x 0) (gg 0 y)))))", null, null, new Symbol[] { ctx.mkSymbol("gg") }, new FuncDecl[] { g })[0]; System.out.println("formula: " + thm); prove(ctx, thm, false, ca); } // / Demonstrates how to handle parser errors using Z3 error handling // support. // / public void parserExample5(Context ctx) { System.out.println("ParserExample5"); try { ctx.parseSMTLIB2String( /* * the following string has a parsing error: missing * parenthesis */ "(declare-const x Int (declare-const y Int)) (assert (> x y))", null, null, null, null); } catch (Z3Exception e) { System.out.println("Z3 error: " + e); } } // / Create an ite-Expr (if-then-else Exprs). public void iteExample(Context ctx) { System.out.println("ITEExample"); Log.append("ITEExample"); BoolExpr f = ctx.mkFalse(); Expr one = ctx.mkInt(1); Expr zero = ctx.mkInt(0); Expr ite = ctx.mkITE(f, one, zero); System.out.println("Expr: " + ite); } // / Create an enumeration data type. public void enumExample(Context ctx) throws TestFailedException { System.out.println("EnumExample"); Log.append("EnumExample"); Symbol name = ctx.mkSymbol("fruit"); EnumSort fruit = ctx.mkEnumSort(name, ctx.mkSymbol("apple"), ctx.mkSymbol("banana"), ctx.mkSymbol("orange")); System.out.println((fruit.getConsts()[0])); System.out.println((fruit.getConsts()[1])); System.out.println((fruit.getConsts()[2])); System.out.println((fruit.getTesterDecls()[0])); System.out.println((fruit.getTesterDecls()[1])); System.out.println((fruit.getTesterDecls()[2])); Expr apple = fruit.getConsts()[0]; Expr banana = fruit.getConsts()[1]; Expr orange = fruit.getConsts()[2]; /* Apples are different from oranges */ prove(ctx, ctx.mkNot(ctx.mkEq(apple, orange)), false); /* Apples pass the apple test */ prove(ctx, (BoolExpr) ctx.mkApp(fruit.getTesterDecls()[0], apple), false); /* Oranges fail the apple test */ disprove(ctx, (BoolExpr) ctx.mkApp(fruit.getTesterDecls()[0], orange), false); prove(ctx, (BoolExpr) ctx.mkNot((BoolExpr) ctx.mkApp( fruit.getTesterDecls()[0], orange)), false); Expr fruity = ctx.mkConst("fruity", fruit); /* If something is fruity, then it is an apple, banana, or orange */ prove(ctx, ctx.mkOr(ctx.mkEq(fruity, apple), ctx.mkEq(fruity, banana), ctx.mkEq(fruity, orange)), false); } // / Create a list datatype. public void listExample(Context ctx) throws TestFailedException { System.out.println("ListExample"); Log.append("ListExample"); Sort int_ty; ListSort int_list; Expr nil, l1, l2, x, y, u, v; BoolExpr fml, fml1; int_ty = ctx.mkIntSort(); int_list = ctx.mkListSort(ctx.mkSymbol("int_list"), int_ty); nil = ctx.mkConst(int_list.getNilDecl()); l1 = ctx.mkApp(int_list.getConsDecl(), ctx.mkInt(1), nil); l2 = ctx.mkApp(int_list.getConsDecl(), ctx.mkInt(2), nil); /* nil != cons(1, nil) */ prove(ctx, ctx.mkNot(ctx.mkEq(nil, l1)), false); /* cons(2,nil) != cons(1, nil) */ prove(ctx, ctx.mkNot(ctx.mkEq(l1, l2)), false); /* cons(x,nil) = cons(y, nil) => x = y */ x = ctx.mkConst("x", int_ty); y = ctx.mkConst("y", int_ty); l1 = ctx.mkApp(int_list.getConsDecl(), x, nil); l2 = ctx.mkApp(int_list.getConsDecl(), y, nil); prove(ctx, ctx.mkImplies(ctx.mkEq(l1, l2), ctx.mkEq(x, y)), false); /* cons(x,u) = cons(x, v) => u = v */ u = ctx.mkConst("u", int_list); v = ctx.mkConst("v", int_list); l1 = ctx.mkApp(int_list.getConsDecl(), x, u); l2 = ctx.mkApp(int_list.getConsDecl(), y, v); prove(ctx, ctx.mkImplies(ctx.mkEq(l1, l2), ctx.mkEq(u, v)), false); prove(ctx, ctx.mkImplies(ctx.mkEq(l1, l2), ctx.mkEq(x, y)), false); /* is_nil(u) or is_cons(u) */ prove(ctx, ctx.mkOr((BoolExpr) ctx.mkApp(int_list.getIsNilDecl(), u), (BoolExpr) ctx.mkApp(int_list.getIsConsDecl(), u)), false); /* occurs check u != cons(x,u) */ prove(ctx, ctx.mkNot(ctx.mkEq(u, l1)), false); /* destructors: is_cons(u) => u = cons(head(u),tail(u)) */ fml1 = ctx.mkEq( u, ctx.mkApp(int_list.getConsDecl(), ctx.mkApp(int_list.getHeadDecl(), u), ctx.mkApp(int_list.getTailDecl(), u))); fml = ctx.mkImplies((BoolExpr) ctx.mkApp(int_list.getIsConsDecl(), u), fml1); System.out.println("Formula " + fml); prove(ctx, fml, false); disprove(ctx, fml1, false); } // / Create a binary tree datatype. public void treeExample(Context ctx) throws TestFailedException { System.out.println("TreeExample"); Log.append("TreeExample"); Sort cell; FuncDecl nil_decl, is_nil_decl, cons_decl, is_cons_decl, car_decl, cdr_decl; Expr nil, l1, l2, x, y, u, v; BoolExpr fml, fml1; String[] head_tail = new String[] { "car", "cdr" }; Sort[] sorts = new Sort[] { null, null }; int[] sort_refs = new int[] { 0, 0 }; Constructor nil_con, cons_con; nil_con = ctx.mkConstructor("nil", "is_nil", null, null, null); cons_con = ctx.mkConstructor("cons", "is_cons", head_tail, sorts, sort_refs); Constructor[] constructors = new Constructor[] { nil_con, cons_con }; cell = ctx.mkDatatypeSort("cell", constructors); nil_decl = nil_con.ConstructorDecl(); is_nil_decl = nil_con.getTesterDecl(); cons_decl = cons_con.ConstructorDecl(); is_cons_decl = cons_con.getTesterDecl(); FuncDecl[] cons_accessors = cons_con.getAccessorDecls(); car_decl = cons_accessors[0]; cdr_decl = cons_accessors[1]; nil = ctx.mkConst(nil_decl); l1 = ctx.mkApp(cons_decl, nil, nil); l2 = ctx.mkApp(cons_decl, l1, nil); /* nil != cons(nil, nil) */ prove(ctx, ctx.mkNot(ctx.mkEq(nil, l1)), false); /* cons(x,u) = cons(x, v) => u = v */ u = ctx.mkConst("u", cell); v = ctx.mkConst("v", cell); x = ctx.mkConst("x", cell); y = ctx.mkConst("y", cell); l1 = ctx.mkApp(cons_decl, x, u); l2 = ctx.mkApp(cons_decl, y, v); prove(ctx, ctx.mkImplies(ctx.mkEq(l1, l2), ctx.mkEq(u, v)), false); prove(ctx, ctx.mkImplies(ctx.mkEq(l1, l2), ctx.mkEq(x, y)), false); /* is_nil(u) or is_cons(u) */ prove(ctx, ctx.mkOr((BoolExpr) ctx.mkApp(is_nil_decl, u), (BoolExpr) ctx.mkApp(is_cons_decl, u)), false); /* occurs check u != cons(x,u) */ prove(ctx, ctx.mkNot(ctx.mkEq(u, l1)), false); /* destructors: is_cons(u) => u = cons(car(u),cdr(u)) */ fml1 = ctx.mkEq( u, ctx.mkApp(cons_decl, ctx.mkApp(car_decl, u), ctx.mkApp(cdr_decl, u))); fml = ctx.mkImplies((BoolExpr) ctx.mkApp(is_cons_decl, u), fml1); System.out.println("Formula " + fml); prove(ctx, fml, false); disprove(ctx, fml1, false); } // / Create a forest of trees. // / // / forest ::= nil | cons(tree, forest) // / tree ::= nil | cons(forest, forest) // / public void forestExample(Context ctx) throws TestFailedException { System.out.println("ForestExample"); Log.append("ForestExample"); Sort tree, forest; @SuppressWarnings("unused") FuncDecl nil1_decl, is_nil1_decl, cons1_decl, is_cons1_decl, car1_decl, cdr1_decl; @SuppressWarnings("unused") FuncDecl nil2_decl, is_nil2_decl, cons2_decl, is_cons2_decl, car2_decl, cdr2_decl; @SuppressWarnings("unused") Expr nil1, nil2, t1, t2, t3, t4, f1, f2, f3, l1, l2, x, y, u, v; // // Declare the names of the accessors for cons. // Then declare the sorts of the accessors. // For this example, all sorts refer to the new types 'forest' and // 'tree' // being declared, so we pass in null for both sorts1 and sorts2. // On the other hand, the sort_refs arrays contain the indices of the // two new sorts being declared. The first element in sort1_refs // points to 'tree', which has index 1, the second element in sort1_refs // array // points to 'forest', which has index 0. // Symbol[] head_tail1 = new Symbol[] { ctx.mkSymbol("head"), ctx.mkSymbol("tail") }; Sort[] sorts1 = new Sort[] { null, null }; int[] sort1_refs = new int[] { 1, 0 }; // the first item points to a // tree, the second to a forest Symbol[] head_tail2 = new Symbol[] { ctx.mkSymbol("car"), ctx.mkSymbol("cdr") }; Sort[] sorts2 = new Sort[] { null, null }; int[] sort2_refs = new int[] { 0, 0 }; // both items point to the forest // datatype. Constructor nil1_con, cons1_con, nil2_con, cons2_con; Constructor[] constructors1 = new Constructor[2], constructors2 = new Constructor[2]; Symbol[] sort_names = { ctx.mkSymbol("forest"), ctx.mkSymbol("tree") }; /* build a forest */ nil1_con = ctx.mkConstructor(ctx.mkSymbol("nil"), ctx.mkSymbol("is_nil"), null, null, null); cons1_con = ctx.mkConstructor(ctx.mkSymbol("cons1"), ctx.mkSymbol("is_cons1"), head_tail1, sorts1, sort1_refs); constructors1[0] = nil1_con; constructors1[1] = cons1_con; /* build a tree */ nil2_con = ctx.mkConstructor(ctx.mkSymbol("nil2"), ctx.mkSymbol("is_nil2"), null, null, null); cons2_con = ctx.mkConstructor(ctx.mkSymbol("cons2"), ctx.mkSymbol("is_cons2"), head_tail2, sorts2, sort2_refs); constructors2[0] = nil2_con; constructors2[1] = cons2_con; Constructor[][] clists = new Constructor[][] { constructors1, constructors2 }; Sort[] sorts = ctx.mkDatatypeSorts(sort_names, clists); forest = sorts[0]; tree = sorts[1]; // // Now that the datatype has been created. // Query the constructors for the constructor // functions, testers, and field accessors. // nil1_decl = nil1_con.ConstructorDecl(); is_nil1_decl = nil1_con.getTesterDecl(); cons1_decl = cons1_con.ConstructorDecl(); is_cons1_decl = cons1_con.getTesterDecl(); FuncDecl[] cons1_accessors = cons1_con.getAccessorDecls(); car1_decl = cons1_accessors[0]; cdr1_decl = cons1_accessors[1]; nil2_decl = nil2_con.ConstructorDecl(); is_nil2_decl = nil2_con.getTesterDecl(); cons2_decl = cons2_con.ConstructorDecl(); is_cons2_decl = cons2_con.getTesterDecl(); FuncDecl[] cons2_accessors = cons2_con.getAccessorDecls(); car2_decl = cons2_accessors[0]; cdr2_decl = cons2_accessors[1]; nil1 = ctx.mkConst(nil1_decl); nil2 = ctx.mkConst(nil2_decl); f1 = ctx.mkApp(cons1_decl, nil2, nil1); t1 = ctx.mkApp(cons2_decl, nil1, nil1); t2 = ctx.mkApp(cons2_decl, f1, nil1); t3 = ctx.mkApp(cons2_decl, f1, f1); t4 = ctx.mkApp(cons2_decl, nil1, f1); f2 = ctx.mkApp(cons1_decl, t1, nil1); f3 = ctx.mkApp(cons1_decl, t1, f1); /* nil != cons(nil,nil) */ prove(ctx, ctx.mkNot(ctx.mkEq(nil1, f1)), false); prove(ctx, ctx.mkNot(ctx.mkEq(nil2, t1)), false); /* cons(x,u) = cons(x, v) => u = v */ u = ctx.mkConst("u", forest); v = ctx.mkConst("v", forest); x = ctx.mkConst("x", tree); y = ctx.mkConst("y", tree); l1 = ctx.mkApp(cons1_decl, x, u); l2 = ctx.mkApp(cons1_decl, y, v); prove(ctx, ctx.mkImplies(ctx.mkEq(l1, l2), ctx.mkEq(u, v)), false); prove(ctx, ctx.mkImplies(ctx.mkEq(l1, l2), ctx.mkEq(x, y)), false); /* is_nil(u) or is_cons(u) */ prove(ctx, ctx.mkOr((BoolExpr) ctx.mkApp(is_nil1_decl, u), (BoolExpr) ctx.mkApp(is_cons1_decl, u)), false); /* occurs check u != cons(x,u) */ prove(ctx, ctx.mkNot(ctx.mkEq(u, l1)), false); } // / Demonstrate how to use #Eval. public void evalExample1(Context ctx) { System.out.println("EvalExample1"); Log.append("EvalExample1"); IntExpr x = ctx.mkIntConst("x"); IntExpr y = ctx.mkIntConst("y"); IntExpr two = ctx.mkInt(2); Solver solver = ctx.mkSolver(); /* assert x < y */ solver.add(ctx.mkLt(x, y)); /* assert x > 2 */ solver.add(ctx.mkGt(x, two)); /* find model for the constraints above */ Model model = null; if (Status.SATISFIABLE == solver.check()) { model = solver.getModel(); System.out.println(model); System.out.println("\nevaluating x+y"); Expr v = model.evaluate(ctx.mkAdd(x, y), false); if (v != null) { System.out.println("result = " + (v)); } else { System.out.println("Failed to evaluate: x+y"); } } else { System.out.println("BUG, the constraints are satisfiable."); } } // / Demonstrate how to use #Eval on tuples. public void evalExample2(Context ctx) { System.out.println("EvalExample2"); Log.append("EvalExample2"); Sort int_type = ctx.getIntSort(); TupleSort tuple = ctx.mkTupleSort(ctx.mkSymbol("mk_tuple"), // name of // tuple // constructor new Symbol[] { ctx.mkSymbol("first"), ctx.mkSymbol("second") }, // names // of // projection // operators new Sort[] { int_type, int_type } // types of projection // operators ); FuncDecl first = tuple.getFieldDecls()[0]; // declarations are for // projections FuncDecl second = tuple.getFieldDecls()[1]; Expr tup1 = ctx.mkConst("t1", tuple); Expr tup2 = ctx.mkConst("t2", tuple); Solver solver = ctx.mkSolver(); /* assert tup1 != tup2 */ solver.add(ctx.mkNot(ctx.mkEq(tup1, tup2))); /* assert first tup1 = first tup2 */ solver.add(ctx.mkEq(ctx.mkApp(first, tup1), ctx.mkApp(first, tup2))); /* find model for the constraints above */ Model model = null; if (Status.SATISFIABLE == solver.check()) { model = solver.getModel(); System.out.println(model); System.out.println("evaluating tup1 " + (model.evaluate(tup1, false))); System.out.println("evaluating tup2 " + (model.evaluate(tup2, false))); System.out.println("evaluating second(tup2) " + (model.evaluate(ctx.mkApp(second, tup2), false))); } else { System.out.println("BUG, the constraints are satisfiable."); } } // / Demonstrate how to use Pushand Popto // / control the size of models. // / Note: this test is specialized to 32-bit bitvectors. public void checkSmall(Context ctx, Solver solver, BitVecExpr... to_minimize) { int num_Exprs = to_minimize.length; int[] upper = new int[num_Exprs]; int[] lower = new int[num_Exprs]; for (int i = 0; i < upper.length; ++i) { upper[i] = Integer.MAX_VALUE; lower[i] = 0; } boolean some_work = true; int last_index = -1; int last_upper = 0; while (some_work) { solver.push(); boolean check_is_sat = true; while (check_is_sat && some_work) { // Assert all feasible bounds. for (int i = 0; i < num_Exprs; ++i) { solver.add(ctx.mkBVULE(to_minimize[i], ctx.mkBV(upper[i], 32))); } check_is_sat = Status.SATISFIABLE == solver.check(); if (!check_is_sat) { if (last_index != -1) { lower[last_index] = last_upper + 1; } break; } System.out.println(solver.getModel()); // narrow the bounds based on the current model. for (int i = 0; i < num_Exprs; ++i) { Expr v = solver.getModel().evaluate(to_minimize[i], false); int ui = ((BitVecNum) v).getInt(); if (ui < upper[i]) { upper[i] = (int) ui; } System.out.println(i + " " + lower[i] + " " + upper[i]); } // find a new bound to add some_work = false; last_index = 0; for (int i = 0; i < num_Exprs; ++i) { if (lower[i] < upper[i]) { last_upper = (upper[i] + lower[i]) / 2; last_index = i; solver.add(ctx.mkBVULE(to_minimize[i], ctx.mkBV(last_upper, 32))); some_work = true; break; } } } solver.pop(); } } // / Reduced-size model generation example. public void findSmallModelExample(Context ctx) { System.out.println("FindSmallModelExample"); Log.append("FindSmallModelExample"); BitVecExpr x = ctx.mkBVConst("x", 32); BitVecExpr y = ctx.mkBVConst("y", 32); BitVecExpr z = ctx.mkBVConst("z", 32); Solver solver = ctx.mkSolver(); solver.add(ctx.mkBVULE(x, ctx.mkBVAdd(y, z))); checkSmall(ctx, solver, x, y, z); } // / Simplifier example. public void simplifierExample(Context ctx) { System.out.println("SimplifierExample"); Log.append("SimplifierExample"); IntExpr x = ctx.mkIntConst("x"); IntExpr y = ctx.mkIntConst("y"); IntExpr z = ctx.mkIntConst("z"); @SuppressWarnings("unused") IntExpr u = ctx.mkIntConst("u"); Expr t1 = ctx.mkAdd(x, ctx.mkSub(y, ctx.mkAdd(x, z))); Expr t2 = t1.simplify(); System.out.println((t1) + " -> " + (t2)); } // / Extract unsatisfiable core example public void unsatCoreAndProofExample(Context ctx) { System.out.println("UnsatCoreAndProofExample"); Log.append("UnsatCoreAndProofExample"); Solver solver = ctx.mkSolver(); BoolExpr pa = ctx.mkBoolConst("PredA"); BoolExpr pb = ctx.mkBoolConst("PredB"); BoolExpr pc = ctx.mkBoolConst("PredC"); BoolExpr pd = ctx.mkBoolConst("PredD"); BoolExpr p1 = ctx.mkBoolConst("P1"); BoolExpr p2 = ctx.mkBoolConst("P2"); BoolExpr p3 = ctx.mkBoolConst("P3"); BoolExpr p4 = ctx.mkBoolConst("P4"); BoolExpr[] assumptions = new BoolExpr[] { ctx.mkNot(p1), ctx.mkNot(p2), ctx.mkNot(p3), ctx.mkNot(p4) }; BoolExpr f1 = ctx.mkAnd(pa, pb, pc); BoolExpr f2 = ctx.mkAnd(pa, ctx.mkNot(pb), pc); BoolExpr f3 = ctx.mkOr(ctx.mkNot(pa), ctx.mkNot(pc)); BoolExpr f4 = pd; solver.add(ctx.mkOr(f1, p1)); solver.add(ctx.mkOr(f2, p2)); solver.add(ctx.mkOr(f3, p3)); solver.add(ctx.mkOr(f4, p4)); Status result = solver.check(assumptions); if (result == Status.UNSATISFIABLE) { System.out.println("unsat"); System.out.println("proof: " + solver.getProof()); System.out.println("core: "); for (Expr c : solver.getUnsatCore()) { System.out.println(c); } } } /// Extract unsatisfiable core example with AssertAndTrack public void unsatCoreAndProofExample2(Context ctx) { System.out.println("UnsatCoreAndProofExample2"); Log.append("UnsatCoreAndProofExample2"); Solver solver = ctx.mkSolver(); BoolExpr pa = ctx.mkBoolConst("PredA"); BoolExpr pb = ctx.mkBoolConst("PredB"); BoolExpr pc = ctx.mkBoolConst("PredC"); BoolExpr pd = ctx.mkBoolConst("PredD"); BoolExpr f1 = ctx.mkAnd(new BoolExpr[] { pa, pb, pc }); BoolExpr f2 = ctx.mkAnd(new BoolExpr[] { pa, ctx.mkNot(pb), pc }); BoolExpr f3 = ctx.mkOr(ctx.mkNot(pa), ctx.mkNot(pc)); BoolExpr f4 = pd; BoolExpr p1 = ctx.mkBoolConst("P1"); BoolExpr p2 = ctx.mkBoolConst("P2"); BoolExpr p3 = ctx.mkBoolConst("P3"); BoolExpr p4 = ctx.mkBoolConst("P4"); solver.assertAndTrack(f1, p1); solver.assertAndTrack(f2, p2); solver.assertAndTrack(f3, p3); solver.assertAndTrack(f4, p4); Status result = solver.check(); if (result == Status.UNSATISFIABLE) { System.out.println("unsat"); System.out.println("core: "); for (Expr c : solver.getUnsatCore()) { System.out.println(c); } } } public void finiteDomainExample(Context ctx) { System.out.println("FiniteDomainExample"); Log.append("FiniteDomainExample"); FiniteDomainSort s = ctx.mkFiniteDomainSort("S", 10); FiniteDomainSort t = ctx.mkFiniteDomainSort("T", 10); FiniteDomainNum s1 = (FiniteDomainNum)ctx.mkNumeral(1, s); FiniteDomainNum t1 = (FiniteDomainNum)ctx.mkNumeral(1, t); System.out.println(s); System.out.println(t); System.out.println(s1); System.out.println(t1); System.out.println(s1.getInt()); System.out.println(t1.getInt()); // But you cannot mix numerals of different sorts // even if the size of their domains are the same: // System.out.println(ctx.mkEq(s1, t1)); } public void floatingPointExample1(Context ctx) throws TestFailedException { System.out.println("FloatingPointExample1"); Log.append("FloatingPointExample1"); FPSort s = ctx.mkFPSort(11, 53); System.out.println("Sort: " + s); FPNum x = (FPNum)ctx.mkNumeral("-1e1", s); /* -1 * 10^1 = -10 */ FPNum y = (FPNum)ctx.mkNumeral("-10", s); /* -10 */ FPNum z = (FPNum)ctx.mkNumeral("-1.25p3", s); /* -1.25 * 2^3 = -1.25 * 8 = -10 */ System.out.println("x=" + x.toString() + "; y=" + y.toString() + "; z=" + z.toString()); BoolExpr a = ctx.mkAnd(ctx.mkFPEq(x, y), ctx.mkFPEq(y, z)); check(ctx, ctx.mkNot(a), Status.UNSATISFIABLE); /* nothing is equal to NaN according to floating-point * equality, so NaN == k should be unsatisfiable. */ FPExpr k = (FPExpr)ctx.mkConst("x", s); FPExpr nan = ctx.mkFPNaN(s); /* solver that runs the default tactic for QF_FP. */ Solver slvr = ctx.mkSolver("QF_FP"); slvr.add(ctx.mkFPEq(nan, k)); if (slvr.check() != Status.UNSATISFIABLE) throw new TestFailedException(); System.out.println("OK, unsat:" + System.getProperty("line.separator") + slvr); /* NaN is equal to NaN according to normal equality. */ slvr = ctx.mkSolver("QF_FP"); slvr.add(ctx.mkEq(nan, nan)); if (slvr.check() != Status.SATISFIABLE) throw new TestFailedException(); System.out.println("OK, sat:" + System.getProperty("line.separator") + slvr); /* Let's prove -1e1 * -1.25e3 == +100 */ x = (FPNum)ctx.mkNumeral("-1e1", s); y = (FPNum)ctx.mkNumeral("-1.25p3", s); FPExpr x_plus_y = (FPExpr)ctx.mkConst("x_plus_y", s); FPNum r = (FPNum)ctx.mkNumeral("100", s); slvr = ctx.mkSolver("QF_FP"); slvr.add(ctx.mkEq(x_plus_y, ctx.mkFPMul(ctx.mkFPRoundNearestTiesToAway(), x, y))); slvr.add(ctx.mkNot(ctx.mkFPEq(x_plus_y, r))); if (slvr.check() != Status.UNSATISFIABLE) throw new TestFailedException(); System.out.println("OK, unsat:" + System.getProperty("line.separator") + slvr); } public void floatingPointExample2(Context ctx) throws TestFailedException { System.out.println("FloatingPointExample2"); Log.append("FloatingPointExample2"); FPSort double_sort = ctx.mkFPSort(11, 53); FPRMSort rm_sort = ctx.mkFPRoundingModeSort(); FPRMExpr rm = (FPRMExpr)ctx.mkConst(ctx.mkSymbol("rm"), rm_sort); BitVecExpr x = (BitVecExpr)ctx.mkConst(ctx.mkSymbol("x"), ctx.mkBitVecSort(64)); FPExpr y = (FPExpr)ctx.mkConst(ctx.mkSymbol("y"), double_sort); FPExpr fp_val = ctx.mkFP(42, double_sort); BoolExpr c1 = ctx.mkEq(y, fp_val); BoolExpr c2 = ctx.mkEq(x, ctx.mkFPToBV(rm, y, 64, false)); BoolExpr c3 = ctx.mkEq(x, ctx.mkBV(42, 64)); BoolExpr c4 = ctx.mkEq(ctx.mkNumeral(42, ctx.getRealSort()), ctx.mkFPToReal(fp_val)); BoolExpr c5 = ctx.mkAnd(c1, c2, c3, c4); System.out.println("c5 = " + c5); /* Generic solver */ Solver s = ctx.mkSolver(); s.add(c5); if (s.check() != Status.SATISFIABLE) throw new TestFailedException(); System.out.println("OK, model: " + s.getModel().toString()); } public void optimizeExample(Context ctx) { System.out.println("Opt"); Optimize opt = ctx.mkOptimize(); // Set constraints. IntExpr xExp = ctx.mkIntConst("x"); IntExpr yExp = ctx.mkIntConst("y"); opt.Add(ctx.mkEq(ctx.mkAdd(xExp, yExp), ctx.mkInt(10)), ctx.mkGe(xExp, ctx.mkInt(0)), ctx.mkGe(yExp, ctx.mkInt(0))); // Set objectives. Optimize.Handle mx = opt.MkMaximize(xExp); Optimize.Handle my = opt.MkMaximize(yExp); System.out.println(opt.Check()); System.out.println(mx); System.out.println(my); } public void translationExample() { Context ctx1 = new Context(); Context ctx2 = new Context(); Sort s1 = ctx1.getIntSort(); Sort s2 = ctx2.getIntSort(); Sort s3 = s1.translate(ctx2); System.out.println(s1 == s2); System.out.println(s1.equals(s2)); System.out.println(s2.equals(s3)); System.out.println(s1.equals(s3)); Expr e1 = ctx1.mkIntConst("e1"); Expr e2 = ctx2.mkIntConst("e1"); Expr e3 = e1.translate(ctx2); System.out.println(e1 == e2); System.out.println(e1.equals(e2)); System.out.println(e2.equals(e3)); System.out.println(e1.equals(e3)); } public static void main(String[] args) { JavaExample p = new JavaExample(); try { com.microsoft.z3.Global.ToggleWarningMessages(true); Log.open("test.log"); System.out.print("Z3 Major Version: "); System.out.println(Version.getMajor()); System.out.print("Z3 Full Version: "); System.out.println(Version.getString()); System.out.print("Z3 Full Version String: "); System.out.println(Version.getFullVersion()); p.simpleExample(); { // These examples need model generation turned on. HashMap cfg = new HashMap(); cfg.put("model", "true"); Context ctx = new Context(cfg); p.optimizeExample(ctx); p.basicTests(ctx); p.castingTest(ctx); p.sudokuExample(ctx); p.quantifierExample1(ctx); p.quantifierExample2(ctx); p.logicExample(ctx); p.parOrExample(ctx); p.findModelExample1(ctx); p.findModelExample2(ctx); p.pushPopExample1(ctx); p.arrayExample1(ctx); p.arrayExample3(ctx); p.bitvectorExample1(ctx); p.bitvectorExample2(ctx); p.parserExample1(ctx); p.parserExample2(ctx); p.parserExample5(ctx); p.iteExample(ctx); p.evalExample1(ctx); p.evalExample2(ctx); p.findSmallModelExample(ctx); p.simplifierExample(ctx); p.finiteDomainExample(ctx); p.floatingPointExample1(ctx); // core dumps: p.floatingPointExample2(ctx); } { // These examples need proof generation turned on. HashMap cfg = new HashMap(); cfg.put("proof", "true"); Context ctx = new Context(cfg); p.proveExample1(ctx); p.proveExample2(ctx); p.arrayExample2(ctx); p.tupleExample(ctx); // throws p.parserExample3(ctx); p.enumExample(ctx); p.listExample(ctx); p.treeExample(ctx); p.forestExample(ctx); p.unsatCoreAndProofExample(ctx); p.unsatCoreAndProofExample2(ctx); } { // These examples need proof generation turned on and auto-config // set to false. HashMap cfg = new HashMap(); cfg.put("proof", "true"); cfg.put("auto-config", "false"); Context ctx = new Context(cfg); p.quantifierExample3(ctx); p.quantifierExample4(ctx); } p.translationExample(); Log.close(); if (Log.isOpen()) System.out.println("Log is still open!"); } catch (Z3Exception ex) { System.out.println("Z3 Managed Exception: " + ex.getMessage()); System.out.println("Stack trace: "); ex.printStackTrace(System.out); } catch (TestFailedException ex) { System.out.println("TEST CASE FAILED: " + ex.getMessage()); System.out.println("Stack trace: "); ex.printStackTrace(System.out); } catch (Exception ex) { System.out.println("Unknown Exception: " + ex.getMessage()); System.out.println("Stack trace: "); ex.printStackTrace(System.out); } } } z3-z3-4.8.7/examples/java/README000066400000000000000000000010421356505360400160430ustar00rootroot00000000000000A small example using the Z3 Java bindings. To build the example, configure Z3 with the --java option to scripts/mk_make.py, build via make examples in the build directory. It will create JavaExample.class in the build directory, which can be run on Windows via java -cp com.microsoft.z3.jar;. JavaExample On Linux and FreeBSD, we must use LD_LIBRARY_PATH=. java -cp com.microsoft.z3.jar:. JavaExample On macOS, the corresponding option is DYLD_LIBRARY_PATH: DYLD_LIBRARY_PATH=. java -cp com.microsoft.z3.jar:. JavaExample z3-z3-4.8.7/examples/maxsat/000077500000000000000000000000001356505360400155425ustar00rootroot00000000000000z3-z3-4.8.7/examples/maxsat/CMakeLists.txt000066400000000000000000000036671356505360400203160ustar00rootroot00000000000000################################################################################ # Example maxsat project ################################################################################ # NOTE: Even though this is a C project, libz3 uses C++. When using libz3 # as a static library if we don't configure this project to also support # C++ we will use the C linker rather than the C++ linker and will not link # the C++ standard library in resulting in a link failure. project(Z3_C_MAXSAT_EXAMPLE C CXX) cmake_minimum_required(VERSION 3.4) find_package(Z3 REQUIRED CONFIG # `NO_DEFAULT_PATH` is set so that -DZ3_DIR has to be passed to find Z3. # This should prevent us from accidentally picking up an installed # copy of Z3. This is here to benefit Z3's build system when building # this project. When making your own project you probably shouldn't # use this option. NO_DEFAULT_PATH ) message(STATUS "Z3_FOUND: ${Z3_FOUND}") message(STATUS "Found Z3 ${Z3_VERSION_STRING}") message(STATUS "Z3_DIR: ${Z3_DIR}") add_executable(c_maxsat_example maxsat.c) target_include_directories(c_maxsat_example PRIVATE ${Z3_C_INCLUDE_DIRS}) target_link_libraries(c_maxsat_example PRIVATE ${Z3_LIBRARIES}) option(FORCE_CXX_LINKER "Force linker with C++ linker" OFF) if (FORCE_CXX_LINKER) # This is a hack for avoiding UBSan linking errors message(STATUS "Forcing use of C++ linker") set_target_properties(c_maxsat_example PROPERTIES LINKER_LANGUAGE CXX ) endif() if ("${CMAKE_SYSTEM_NAME}" MATCHES "[Ww]indows") # On Windows we need to copy the Z3 libraries # into the same directory as the executable # so that they can be found. foreach (z3_lib ${Z3_LIBRARIES}) message(STATUS "Adding copy rule for ${z3_lib}") add_custom_command(TARGET c_maxsat_example POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different $ $ ) endforeach() endif() z3-z3-4.8.7/examples/maxsat/README000066400000000000000000000010031356505360400164140ustar00rootroot00000000000000Small example using the C bindings. To build the example execute make examples in the build directory. This command will create the executable maxsat. On Windows, you can just execute it. On macOS and Linux, you must install z3 first using sudo make install OR update LD_LIBRARY_PATH (Linux) or DYLD_LIBRARY_PATH (macOS) with the build directory. You need that to be able to find the Z3 shared library. This directory contains a test file (ex.smt) that can be used as input for the maxsat test application. z3-z3-4.8.7/examples/maxsat/ex.smt000066400000000000000000000003671356505360400167110ustar00rootroot00000000000000(declare-const x Int) (declare-const y Int) (declare-const z Int) (assert-soft (> x 0)) (assert-soft (<= x -1)) (assert-soft (or (> x 0) (< y 1))) (assert-soft (> y 2)) (assert-soft (> y 3)) (assert-soft (<= y -1)) (assert (= z (+ x y))) z3-z3-4.8.7/examples/maxsat/maxsat.c000066400000000000000000000476561356505360400172250ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ /* Simple MAXSAT solver on top of the Z3 API. */ #include #include #include #include /** \defgroup maxsat_ex MaxSAT/MaxSMT examples */ /*@{*/ /** \brief Exit gracefully in case of error. */ void error(char * msg) { fprintf(stderr, "%s\n", msg); exit(1); } /** \brief Create a logical context. Enable model construction only. */ Z3_context mk_context() { Z3_config cfg; Z3_context ctx; cfg = Z3_mk_config(); Z3_set_param_value(cfg, "MODEL", "true"); ctx = Z3_mk_context(cfg); Z3_del_config(cfg); return ctx; } /** \brief Create a variable using the given name and type. */ Z3_ast mk_var(Z3_context ctx, const char * name, Z3_sort ty) { Z3_symbol s = Z3_mk_string_symbol(ctx, name); return Z3_mk_const(ctx, s, ty); } /** \brief Create a boolean variable using the given name. */ Z3_ast mk_bool_var(Z3_context ctx, const char * name) { Z3_sort ty = Z3_mk_bool_sort(ctx); return mk_var(ctx, name, ty); } /** \brief Create a fresh boolean variable. */ Z3_ast mk_fresh_bool_var(Z3_context ctx) { return Z3_mk_fresh_const(ctx, "k", Z3_mk_bool_sort(ctx)); } /** \brief Create an array with \c num_vars fresh boolean variables. */ Z3_ast * mk_fresh_bool_var_array(Z3_context ctx, unsigned num_vars) { Z3_ast * result = (Z3_ast *) malloc(sizeof(Z3_ast) * num_vars); unsigned i; for (i = 0; i < num_vars; i++) { result[i] = mk_fresh_bool_var(ctx); } return result; } /** \brief Delete array of boolean variables. */ void del_bool_var_array(Z3_ast * arr) { free(arr); } /** \brief Create a binary OR. */ Z3_ast mk_binary_or(Z3_context ctx, Z3_ast in_1, Z3_ast in_2) { Z3_ast args[2] = { in_1, in_2 }; return Z3_mk_or(ctx, 2, args); } /** \brief Create a ternary OR. */ Z3_ast mk_ternary_or(Z3_context ctx, Z3_ast in_1, Z3_ast in_2, Z3_ast in_3) { Z3_ast args[3] = { in_1, in_2, in_3 }; return Z3_mk_or(ctx, 3, args); } /** \brief Create a binary AND. */ Z3_ast mk_binary_and(Z3_context ctx, Z3_ast in_1, Z3_ast in_2) { Z3_ast args[2] = { in_1, in_2 }; return Z3_mk_and(ctx, 2, args); } /** \brief Free the given array of cnstrs that was allocated using get_hard_constraints or get_soft_constraints. */ void free_cnstr_array(Z3_ast * cnstrs) { free(cnstrs); } /** \brief Assert hard constraints stored in the given array. */ void assert_hard_constraints(Z3_context ctx, Z3_solver s, unsigned num_cnstrs, Z3_ast * cnstrs) { unsigned i; for (i = 0; i < num_cnstrs; i++) { Z3_solver_assert(ctx, s, cnstrs[i]); } } /** \brief Assert soft constraints stored in the given array. This function will assert each soft-constraint C_i as (C_i or k_i) where k_i is a fresh boolean variable. It will also return an array containing these fresh variables. */ Z3_ast * assert_soft_constraints(Z3_context ctx, Z3_solver s, unsigned num_cnstrs, Z3_ast * cnstrs) { unsigned i; Z3_ast * aux_vars; aux_vars = mk_fresh_bool_var_array(ctx, num_cnstrs); for (i = 0; i < num_cnstrs; i++) { Z3_ast assumption = cnstrs[i]; Z3_solver_assert(ctx, s, mk_binary_or(ctx, assumption, aux_vars[i])); } return aux_vars; } /** \brief Create a full adder with inputs \c in_1, \c in_2 and \c cin. The output of the full adder is stored in \c out, and the carry in \c c_out. */ void mk_full_adder(Z3_context ctx, Z3_ast in_1, Z3_ast in_2, Z3_ast cin, Z3_ast * out, Z3_ast * cout) { *cout = mk_ternary_or(ctx, mk_binary_and(ctx, in_1, in_2), mk_binary_and(ctx, in_1, cin), mk_binary_and(ctx, in_2, cin)); *out = Z3_mk_xor(ctx, Z3_mk_xor(ctx, in_1, in_2), cin); } /** \brief Create an adder for inputs of size \c num_bits. The arguments \c in1 and \c in2 are arrays of bits of size \c num_bits. \remark \c result must be an array of size \c num_bits + 1. */ void mk_adder(Z3_context ctx, unsigned num_bits, Z3_ast * in_1, Z3_ast * in_2, Z3_ast * result) { Z3_ast cin, cout, out; unsigned i; cin = Z3_mk_false(ctx); for (i = 0; i < num_bits; i++) { mk_full_adder(ctx, in_1[i], in_2[i], cin, &out, &cout); result[i] = out; cin = cout; } result[num_bits] = cout; } /** \brief Given \c num_ins "numbers" of size \c num_bits stored in \c in. Create floor(num_ins/2) adder circuits. Each circuit is adding two consecutive "numbers". The numbers are stored one after the next in the array \c in. That is, the array \c in has size num_bits * num_ins. Return an array of bits containing \c ceil(num_ins/2) numbers of size \c (num_bits + 1). If num_ins/2 is not an integer, then the last "number" in the output, is the last "number" in \c in with an appended "zero". */ void mk_adder_pairs(Z3_context ctx, unsigned num_bits, unsigned num_ins, Z3_ast * in, unsigned * out_num_ins, Z3_ast * out) { unsigned out_num_bits = num_bits + 1; unsigned i = 0; Z3_ast * _in = in; Z3_ast * _out = out; *out_num_ins = (num_ins % 2 == 0) ? (num_ins / 2) : (num_ins / 2) + 1; for (i = 0; i < num_ins / 2; i++) { mk_adder(ctx, num_bits, _in, _in + num_bits, _out); _in += num_bits; _in += num_bits; _out += out_num_bits; } if (num_ins % 2 != 0) { for (i = 0; i < num_bits; i++) { _out[i] = _in[i]; } _out[num_bits] = Z3_mk_false(ctx); } } /** \brief Create a counter circuit to count the number of "ones" in lits. The function returns an array of bits (i.e. boolean expressions) containing the output of the circuit. The size of the array is stored in out_sz. */ Z3_ast * mk_counter_circuit(Z3_context ctx, unsigned n, Z3_ast * lits, unsigned * out_sz) { unsigned num_ins = n; unsigned num_bits = 1; Z3_ast * aux_1; Z3_ast * aux_2; if (n == 0) return 0; aux_1 = (Z3_ast *) malloc(sizeof(Z3_ast) * (n + 1)); aux_2 = (Z3_ast *) malloc(sizeof(Z3_ast) * (n + 1)); memcpy(aux_1, lits, sizeof(Z3_ast) * n); while (num_ins > 1) { unsigned new_num_ins; Z3_ast * tmp; mk_adder_pairs(ctx, num_bits, num_ins, aux_1, &new_num_ins, aux_2); num_ins = new_num_ins; num_bits++; #ifdef MAXSAT_DEBUG { unsigned i; printf("num_bits: %d, num_ins: %d \n", num_bits, num_ins); for (i = 0; i < num_ins * num_bits; i++) { printf("bit %d:\n%s\n", i, Z3_ast_to_string(ctx, aux_2[i])); } printf("-----------\n"); } #endif tmp = aux_1; aux_1 = aux_2; aux_2 = tmp; } *out_sz = num_bits; free(aux_2); return aux_1; } /** \brief Return the \c idx bit of \c val. */ int get_bit(unsigned val, unsigned idx) { unsigned mask = 1 << (idx & 31); return (val & mask) != 0; } /** \brief Given an integer val encoded in n bits (boolean variables), assert the constraint that val <= k. */ void assert_le_k(Z3_context ctx, Z3_solver s, unsigned n, Z3_ast * val, unsigned k) { Z3_ast i1, i2, not_val, out; unsigned idx; not_val = Z3_mk_not(ctx, val[0]); if (get_bit(k, 0)) out = Z3_mk_true(ctx); else out = not_val; for (idx = 1; idx < n; idx++) { not_val = Z3_mk_not(ctx, val[idx]); if (get_bit(k, idx)) { i1 = not_val; i2 = out; } else { i1 = Z3_mk_false(ctx); i2 = Z3_mk_false(ctx); } out = mk_ternary_or(ctx, i1, i2, mk_binary_and(ctx, not_val, out)); } // printf("at-most-k:\n%s\n", Z3_ast_to_string(ctx, out)); Z3_solver_assert(ctx, s, out); } /** \brief Assert that at most \c k literals in \c lits can be true, where \c n is the number of literals in lits. We use a simple encoding using an adder (counter). An interesting exercise consists in implementing more sophisticated encodings. */ void assert_at_most_k(Z3_context ctx, Z3_solver s, unsigned n, Z3_ast * lits, unsigned k) { Z3_ast * counter_bits; unsigned counter_bits_sz; if (k >= n || n <= 1) return; /* nothing to be done */ counter_bits = mk_counter_circuit(ctx, n, lits, &counter_bits_sz); assert_le_k(ctx, s, counter_bits_sz, counter_bits, k); del_bool_var_array(counter_bits); } /** \brief Assert that at most one literal in \c lits can be true, where \c n is the number of literals in lits. */ void assert_at_most_one(Z3_context ctx, Z3_solver s, unsigned n, Z3_ast * lits) { assert_at_most_k(ctx, s, n, lits, 1); } Z3_solver mk_solver(Z3_context ctx) { Z3_solver r = Z3_mk_solver(ctx); Z3_solver_inc_ref(ctx, r); return r; } /** \brief Small test for the at-most-one constraint. */ void tst_at_most_one() { Z3_context ctx = mk_context(); Z3_solver s = mk_solver(ctx); Z3_ast k1 = mk_bool_var(ctx, "k1"); Z3_ast k2 = mk_bool_var(ctx, "k2"); Z3_ast k3 = mk_bool_var(ctx, "k3"); Z3_ast k4 = mk_bool_var(ctx, "k4"); Z3_ast k5 = mk_bool_var(ctx, "k5"); Z3_ast k6 = mk_bool_var(ctx, "k6"); Z3_ast args1[5] = { k1, k2, k3, k4, k5 }; Z3_ast args2[3] = { k4, k5, k6 }; Z3_model m = 0; Z3_lbool result; printf("testing at-most-one constraint\n"); assert_at_most_one(ctx, s, 5, args1); assert_at_most_one(ctx, s, 3, args2); printf("it must be sat...\n"); result = Z3_solver_check(ctx, s); if (result != Z3_L_TRUE) error("BUG"); m = Z3_solver_get_model(ctx, s); Z3_model_inc_ref(ctx, m); printf("model:\n%s\n", Z3_model_to_string(ctx, m)); Z3_model_dec_ref(ctx, m); Z3_solver_assert(ctx, s, mk_binary_or(ctx, k2, k3)); Z3_solver_assert(ctx, s, mk_binary_or(ctx, k1, k6)); printf("it must be sat...\n"); result = Z3_solver_check(ctx, s); if (result != Z3_L_TRUE) error("BUG"); m = Z3_solver_get_model(ctx, s); Z3_model_inc_ref(ctx, m); printf("model:\n%s\n", Z3_model_to_string(ctx, m)); Z3_solver_assert(ctx, s, mk_binary_or(ctx, k4, k5)); printf("it must be unsat...\n"); result = Z3_solver_check(ctx, s); if (result != Z3_L_FALSE) error("BUG"); Z3_model_dec_ref(ctx, m); Z3_solver_dec_ref(ctx, s); Z3_del_context(ctx); } /** \brief Return the number of soft-constraints that were disable by the given model. A soft-constraint was disabled if the associated auxiliary variable was assigned to true. */ unsigned get_num_disabled_soft_constraints(Z3_context ctx, Z3_model m, unsigned num_soft_cnstrs, Z3_ast * aux_vars) { unsigned i; unsigned num_disabled = 0; Z3_ast t = Z3_mk_true(ctx); for (i = 0; i < num_soft_cnstrs; i++) { Z3_ast val; if (Z3_model_eval(ctx, m, aux_vars[i], 1, &val) == true) { // printf("%s", Z3_ast_to_string(ctx, aux_vars[i])); // printf(" -> %s\n", Z3_ast_to_string(ctx, val)); if (Z3_is_eq_ast(ctx, val, t)) { num_disabled++; } } } return num_disabled; } /** \brief Naive maxsat procedure based on linear search and at-most-k constraint. Return the number of soft-constraints that can be satisfied. Return -1 if the hard-constraints cannot be satisfied. That is, the formula cannot be satisfied even if all soft-constraints are ignored. Exercise: use binary search to implement MaxSAT. Hint: you will need to use an answer literal to retract the at-most-k constraint. */ int naive_maxsat(Z3_context ctx, Z3_solver s, unsigned num_hard_cnstrs, Z3_ast * hard_cnstrs, unsigned num_soft_cnstrs, Z3_ast * soft_cnstrs) { Z3_ast * aux_vars; Z3_lbool is_sat; unsigned k; assert_hard_constraints(ctx, s, num_hard_cnstrs, hard_cnstrs); printf("checking whether hard constraints are satisfiable...\n"); is_sat = Z3_solver_check(ctx, s); if (is_sat == Z3_L_FALSE) { // It is not possible to make the formula satisfiable even when ignoring all soft constraints. return -1; } if (num_soft_cnstrs == 0) return 0; // nothing to be done... aux_vars = assert_soft_constraints(ctx, s, num_soft_cnstrs, soft_cnstrs); // Perform linear search. k = num_soft_cnstrs - 1; for (;;) { Z3_model m; unsigned num_disabled; // at most k soft-constraints can be ignored. printf("checking whether at-most %d soft-constraints can be ignored.\n", k); assert_at_most_k(ctx, s, num_soft_cnstrs, aux_vars, k); is_sat = Z3_solver_check(ctx, s); if (is_sat == Z3_L_FALSE) { printf("unsat\n"); return num_soft_cnstrs - k - 1; } m = Z3_solver_get_model(ctx, s); Z3_model_inc_ref(ctx, m); num_disabled = get_num_disabled_soft_constraints(ctx, m, num_soft_cnstrs, aux_vars); Z3_model_dec_ref(ctx, m); if (num_disabled > k) { error("BUG"); } printf("sat\n"); k = num_disabled; if (k == 0) { // it was possible to satisfy all soft-constraints. return num_soft_cnstrs; } k--; } } /** \brief Implement one step of the Fu&Malik algorithm. See fu_malik_maxsat function for more details. Input: soft constraints + aux-vars (aka answer literals) Output: done/not-done when not done return updated set of soft-constraints and aux-vars. - if SAT --> terminates - if UNSAT * compute unsat core * add blocking variable to soft-constraints in the core - replace soft-constraint with the one with the blocking variable - we should also add an aux-var - replace aux-var with a new one * add at-most-one constraint with blocking */ int fu_malik_maxsat_step(Z3_context ctx, Z3_solver s, unsigned num_soft_cnstrs, Z3_ast * soft_cnstrs, Z3_ast * aux_vars) { // create assumptions Z3_ast * assumptions = (Z3_ast*) malloc(sizeof(Z3_ast) * num_soft_cnstrs); Z3_lbool is_sat; Z3_ast_vector core; unsigned core_size; unsigned i = 0; unsigned k = 0; Z3_ast * block_vars; for (i = 0; i < num_soft_cnstrs; i++) { // Recall that we asserted (soft_cnstrs[i] \/ aux_vars[i]) // So using (NOT aux_vars[i]) as an assumption we are actually forcing the soft_cnstrs[i] to be considered. assumptions[i] = Z3_mk_not(ctx, aux_vars[i]); } is_sat = Z3_solver_check_assumptions(ctx, s, num_soft_cnstrs, assumptions); if (is_sat != Z3_L_FALSE) { return 1; // done } else { core = Z3_solver_get_unsat_core(ctx, s); Z3_ast_vector_inc_ref(ctx, core); core_size = Z3_ast_vector_size(ctx, core); block_vars = (Z3_ast*) malloc(sizeof(Z3_ast) * core_size); k = 0; // update soft-constraints and aux_vars for (i = 0; i < num_soft_cnstrs; i++) { unsigned j; // check whether assumption[i] is in the core or not for (j = 0; j < core_size; j++) { if (assumptions[i] == Z3_ast_vector_get(ctx, core, j)) break; } if (j < core_size) { // assumption[i] is in the unsat core... so soft_cnstrs[i] is in the unsat core Z3_ast block_var = mk_fresh_bool_var(ctx); Z3_ast new_aux_var = mk_fresh_bool_var(ctx); soft_cnstrs[i] = mk_binary_or(ctx, soft_cnstrs[i], block_var); aux_vars[i] = new_aux_var; block_vars[k] = block_var; k++; // Add new constraint containing the block variable. // Note that we are using the new auxiliary variable to be able to use it as an assumption. Z3_solver_assert(ctx, s, mk_binary_or(ctx, soft_cnstrs[i], new_aux_var)); } } assert_at_most_one(ctx, s, k, block_vars); Z3_ast_vector_dec_ref(ctx, core); return 0; // not done. } } /** \brief Fu & Malik procedure for MaxSAT. This procedure is based on unsat core extraction and the at-most-one constraint. Return the number of soft-constraints that can be satisfied. Return -1 if the hard-constraints cannot be satisfied. That is, the formula cannot be satisfied even if all soft-constraints are ignored. For more information on the Fu & Malik procedure: Z. Fu and S. Malik, On solving the partial MAX-SAT problem, in International Conference on Theory and Applications of Satisfiability Testing, 2006. */ int fu_malik_maxsat(Z3_context ctx, Z3_solver s, unsigned num_hard_cnstrs, Z3_ast * hard_cnstrs, unsigned num_soft_cnstrs, Z3_ast * soft_cnstrs) { Z3_ast * aux_vars; Z3_lbool is_sat; unsigned k; assert_hard_constraints(ctx, s, num_hard_cnstrs, hard_cnstrs); printf("checking whether hard constraints are satisfiable...\n"); is_sat = Z3_solver_check(ctx, s); if (is_sat == Z3_L_FALSE) { // It is not possible to make the formula satisfiable even when ignoring all soft constraints. return -1; } if (num_soft_cnstrs == 0) return 0; // nothing to be done... /* Fu&Malik algorithm is based on UNSAT-core extraction. We accomplish that using auxiliary variables (aka answer literals). */ aux_vars = assert_soft_constraints(ctx, s, num_soft_cnstrs, soft_cnstrs); k = 0; for (;;) { printf("iteration %d\n", k); if (fu_malik_maxsat_step(ctx, s, num_soft_cnstrs, soft_cnstrs, aux_vars)) { return num_soft_cnstrs - k; } k++; } } #define NAIVE_MAXSAT 0 #define FU_MALIK_MAXSAT 1 /** \brief Finds the maximal number of assumptions that can be satisfied. An assumption is any formula preceded with the :assumption keyword. "Hard" constraints can be supported by using the :formula keyword. Input: file in SMT-LIB format, and MaxSAT algorithm to be used: 0 - Naive, 1 - Fu&Malik's algo. Output: the maximum number of assumptions that can be satisfied. */ int smtlib_maxsat(char * file_name, int approach) { Z3_context ctx; Z3_solver s; unsigned i; Z3_optimize opt; unsigned num_hard_cnstrs, num_soft_cnstrs; Z3_ast * hard_cnstrs, * soft_cnstrs; Z3_ast_vector hard, objs; Z3_app soft; unsigned result = 0; ctx = mk_context(); s = mk_solver(ctx); opt = Z3_mk_optimize(ctx); Z3_optimize_inc_ref(ctx, opt); Z3_optimize_from_file(ctx, opt, file_name); hard = Z3_optimize_get_assertions(ctx, opt); Z3_ast_vector_inc_ref(ctx, hard); num_hard_cnstrs = Z3_ast_vector_size(ctx, hard); hard_cnstrs = (Z3_ast *) malloc(sizeof(Z3_ast) * (num_hard_cnstrs)); for (i = 0; i < num_hard_cnstrs; i++) { hard_cnstrs[i] = Z3_ast_vector_get(ctx, hard, i); } objs = Z3_optimize_get_objectives(ctx, opt); Z3_ast_vector_inc_ref(ctx, objs); // soft constraints are stored in a single objective which is a sum // of if-then-else expressions. soft = Z3_to_app(ctx, Z3_ast_vector_get(ctx, objs, 0)); num_soft_cnstrs = Z3_get_app_num_args(ctx, soft); soft_cnstrs = (Z3_ast *) malloc(sizeof(Z3_ast) * (num_soft_cnstrs)); for (i = 0; i < num_soft_cnstrs; ++i) { soft_cnstrs[i] = Z3_get_app_arg(ctx, Z3_to_app(ctx, Z3_get_app_arg(ctx, soft, i)), 0); } switch (approach) { case NAIVE_MAXSAT: result = naive_maxsat(ctx, s, num_hard_cnstrs, hard_cnstrs, num_soft_cnstrs, soft_cnstrs); break; case FU_MALIK_MAXSAT: result = fu_malik_maxsat(ctx, s, num_hard_cnstrs, hard_cnstrs, num_soft_cnstrs, soft_cnstrs); break; default: /* Exercise: implement your own MaxSAT algorithm.*/ error("Not implemented yet."); break; } free_cnstr_array(hard_cnstrs); free_cnstr_array(soft_cnstrs); Z3_solver_dec_ref(ctx, s); Z3_optimize_dec_ref(ctx, opt); return result; } int main(int argc, char * argv[]) { #if 1 int r; if (argc != 2) { error("usage: maxsat [filename.smt]."); } r = smtlib_maxsat(argv[1], FU_MALIK_MAXSAT); printf("result: %d\n", r); #else tst_at_most_one(); #endif return 0; } /*@}*/ z3-z3-4.8.7/examples/ml/000077500000000000000000000000001356505360400146555ustar00rootroot00000000000000z3-z3-4.8.7/examples/ml/README000066400000000000000000000014351356505360400155400ustar00rootroot00000000000000Small example using the Z3 ML bindings. To build the example execute make examples in the build directory. This will create ml_example and ml_example.byte in the build directory, which can be run in the build directory by calling LD_LIBRARY_PATH=. ./ml_example or LD_LIBRARY_PATH=. ./ml_example.byte for the byte-code version. If Z3 was installed into the ocamlfind package repository (see src/api/ml/README), then we can also compile this example as follows: ocamlfind ocamlc -o ml_example.byte -package Z3 -linkpkg ml_example.ml or ocamlfind ocamlopt -o ml_example -package Z3 -linkpkg ml_example.ml Note that the resulting binaries depend on the shared z3 library (libz3.dll/.so/.dylb), which needs to be in the PATH (Windows), LD_LIBRARY_PATH (Linux), or DYLD_LIBRARY_PATH (macOS). z3-z3-4.8.7/examples/ml/ml_example.ml000066400000000000000000000315021356505360400173330ustar00rootroot00000000000000(* Copyright (C) 2012 Microsoft Corporation Author: CM Wintersteiger (cwinter) 2012-12-17 *) open Z3 open Z3.Symbol open Z3.Sort open Z3.Expr open Z3.Boolean open Z3.FuncDecl open Z3.Goal open Z3.Tactic open Z3.Tactic.ApplyResult open Z3.Probe open Z3.Solver open Z3.Arithmetic open Z3.Arithmetic.Integer open Z3.Arithmetic.Real open Z3.BitVector exception TestFailedException of string (** Model Converter test *) let model_converter_test ( ctx : context ) = Printf.printf "ModelConverterTest\n"; let xr = (Expr.mk_const ctx (Symbol.mk_string ctx "x") (Real.mk_sort ctx)) in let yr = (Expr.mk_const ctx (Symbol.mk_string ctx "y") (Real.mk_sort ctx)) in let g4 = (mk_goal ctx true false false ) in (Goal.add g4 [ (mk_gt ctx xr (Real.mk_numeral_nd ctx 10 1)) ]) ; (Goal.add g4 [ (mk_eq ctx yr (Arithmetic.mk_add ctx [ xr; (Real.mk_numeral_nd ctx 1 1) ])) ]) ; (Goal.add g4 [ (mk_gt ctx yr (Real.mk_numeral_nd ctx 1 1)) ]) ; ( let ar = (Tactic.apply (mk_tactic ctx "simplify") g4 None) in if ((get_num_subgoals ar) == 1 && ((is_decided_sat (get_subgoal ar 0)) || (is_decided_unsat (get_subgoal ar 0)))) then raise (TestFailedException "") else Printf.printf "Test passed.\n" ); ( let ar = (Tactic.apply (and_then ctx (mk_tactic ctx ("simplify")) (mk_tactic ctx "solve-eqs") []) g4 None) in if ((get_num_subgoals ar) == 1 && ((is_decided_sat (get_subgoal ar 0)) || (is_decided_unsat (get_subgoal ar 0)))) then raise (TestFailedException "") else Printf.printf "Test passed.\n" ; let solver = (mk_solver ctx None) in let f e = (Solver.add solver [ e ]) in ignore (List.map f (get_formulas (get_subgoal ar 0))) ; let q = (check solver []) in if q != SATISFIABLE then raise (TestFailedException "") else let m = (get_model solver) in match m with | None -> raise (TestFailedException "") | Some (m) -> Printf.printf "Solver says: %s\n" (string_of_status q) ; Printf.printf "Model: \n%s\n" (Model.to_string m) ) (** Some basic tests. *) let basic_tests ( ctx : context ) = Printf.printf "BasicTests\n" ; let fname = (mk_string ctx "f") in let x = (mk_string ctx "x") in let y = (mk_string ctx "y") in let bs = (Boolean.mk_sort ctx) in let domain = [ bs; bs ] in let f = (FuncDecl.mk_func_decl ctx fname domain bs) in let fapp = (mk_app ctx f [ (Expr.mk_const ctx x bs); (Expr.mk_const ctx y bs) ]) in let fargs2 = [ (mk_fresh_const ctx "cp" bs) ] in let domain2 = [ bs ] in let fapp2 = (mk_app ctx (mk_fresh_func_decl ctx "fp" domain2 bs) fargs2) in let trivial_eq = (mk_eq ctx fapp fapp) in let nontrivial_eq = (mk_eq ctx fapp fapp2) in let g = (mk_goal ctx true false false) in (Goal.add g [ trivial_eq ]) ; (Goal.add g [ nontrivial_eq ]) ; Printf.printf "%s\n" ("Goal: " ^ (Goal.to_string g)) ; ( let solver = (mk_solver ctx None) in (List.iter (fun a -> (Solver.add solver [ a ])) (get_formulas g)) ; if (check solver []) != SATISFIABLE then raise (TestFailedException "") else Printf.printf "Test passed.\n" ); ( let ar = (Tactic.apply (mk_tactic ctx "simplify") g None) in if ((get_num_subgoals ar) == 1 && ((is_decided_sat (get_subgoal ar 0)) || (is_decided_unsat (get_subgoal ar 0)))) then raise (TestFailedException "") else Printf.printf "Test passed.\n" ); ( let ar = (Tactic.apply (mk_tactic ctx "smt") g None) in if ((get_num_subgoals ar) == 1 && (not (is_decided_sat (get_subgoal ar 0)))) then raise (TestFailedException "") else Printf.printf "Test passed.\n" ); (Goal.add g [ (mk_eq ctx (mk_numeral_int ctx 1 (BitVector.mk_sort ctx 32)) (mk_numeral_int ctx 2 (BitVector.mk_sort ctx 32))) ] ) ; ( let ar = (Tactic.apply (mk_tactic ctx "smt") g None) in if ((get_num_subgoals ar) == 1 && (not (is_decided_unsat (get_subgoal ar 0)))) then raise (TestFailedException "") else Printf.printf "Test passed.\n" ); ( let g2 = (mk_goal ctx true true false) in let ar = (Tactic.apply (mk_tactic ctx "smt") g2 None) in if ((get_num_subgoals ar) == 1 && (not (is_decided_sat (get_subgoal ar 0)))) then raise (TestFailedException "") else Printf.printf "Test passed.\n" ); ( let g2 = (mk_goal ctx true true false) in (Goal.add g2 [ (Boolean.mk_false ctx) ]) ; let ar = (Tactic.apply (mk_tactic ctx "smt") g2 None) in if ((get_num_subgoals ar) == 1 && (not (is_decided_unsat (get_subgoal ar 0)))) then raise (TestFailedException "") else Printf.printf "Test passed.\n" ); ( let g3 = (mk_goal ctx true true false) in let xc = (Expr.mk_const ctx (Symbol.mk_string ctx "x") (Integer.mk_sort ctx)) in let yc = (Expr.mk_const ctx (Symbol.mk_string ctx "y") (Integer.mk_sort ctx)) in (Goal.add g3 [ (mk_eq ctx xc (mk_numeral_int ctx 1 (Integer.mk_sort ctx))) ]) ; (Goal.add g3 [ (mk_eq ctx yc (mk_numeral_int ctx 2 (Integer.mk_sort ctx))) ]) ; let constr = (mk_eq ctx xc yc) in (Goal.add g3 [ constr ] ) ; let ar = (Tactic.apply (mk_tactic ctx "smt") g3 None) in if ((get_num_subgoals ar) == 1 && (not (is_decided_unsat (get_subgoal ar 0)))) then raise (TestFailedException "") else Printf.printf "Test passed.\n" ) ; model_converter_test ctx ; (* Real num/den test. *) let rn = Real.mk_numeral_nd ctx 42 43 in let inum = (get_numerator rn) in let iden = get_denominator rn in Printf.printf "Numerator: %s Denominator: %s\n" (Real.numeral_to_string inum) (Real.numeral_to_string iden) ; if ((Real.numeral_to_string inum) <> "42" || (Real.numeral_to_string iden) <> "43") then raise (TestFailedException "") else Printf.printf "Test passed.\n" ; if ((to_decimal_string rn 3) <> "0.976?") then raise (TestFailedException "") else Printf.printf "Test passed.\n" ; if (to_decimal_string (Real.mk_numeral_s ctx "-1231231232/234234333") 5 <> "-5.25640?") then raise (TestFailedException "") else if (to_decimal_string (Real.mk_numeral_s ctx "-123123234234234234231232/234234333") 5 <> "-525641278361333.28170?") then raise (TestFailedException "") else if (to_decimal_string (Real.mk_numeral_s ctx "-234234333") 5 <> "-234234333") then raise (TestFailedException "") else if (to_decimal_string (Real.mk_numeral_s ctx "234234333/2") 5 <> "117117166.5") then raise (TestFailedException "") ; (* Error handling test. *) try ( let i = Integer.mk_numeral_s ctx "1/2" in Printf.printf "%s\n" (Expr.to_string i) ; raise (TestFailedException "check") ) with Z3.Error(_) -> ( Printf.printf "Exception caught, OK.\n" ) (** A basic example of how to use quantifiers. **) let quantifier_example1 ( ctx : context ) = Printf.printf "QuantifierExample\n" ; let is = (Integer.mk_sort ctx) in let types = [ is; is; is ] in let names = [ (Symbol.mk_string ctx "x_0"); (Symbol.mk_string ctx "x_1"); (Symbol.mk_string ctx "x_2") ] in let vars = [ (Quantifier.mk_bound ctx 2 (List.nth types 0)); (Quantifier.mk_bound ctx 2 (List.nth types 1)); (Quantifier.mk_bound ctx 2 (List.nth types 2)) ] in let xs = [ (Integer.mk_const ctx (List.nth names 0)); (Integer.mk_const ctx (List.nth names 1)); (Integer.mk_const ctx (List.nth names 2)) ] in let body_vars = (Boolean.mk_and ctx [ (mk_eq ctx (Arithmetic.mk_add ctx [ (List.nth vars 0) ; (Integer.mk_numeral_i ctx 1)]) (Integer.mk_numeral_i ctx 2)) ; (mk_eq ctx (Arithmetic.mk_add ctx [ (List.nth vars 1); (Integer.mk_numeral_i ctx 2)]) (Arithmetic.mk_add ctx [ (List.nth vars 2); (Integer.mk_numeral_i ctx 3)])) ]) in let body_const = (Boolean.mk_and ctx [ (mk_eq ctx (Arithmetic.mk_add ctx [ (List.nth xs 0); (Integer.mk_numeral_i ctx 1)]) (Integer.mk_numeral_i ctx 2)) ; (mk_eq ctx (Arithmetic.mk_add ctx [ (List.nth xs 1); (Integer.mk_numeral_i ctx 2)]) (Arithmetic.mk_add ctx [ (List.nth xs 2); (Integer.mk_numeral_i ctx 3)])) ]) in let x = (Quantifier.mk_forall ctx types names body_vars (Some 1) [] [] (Some (Symbol.mk_string ctx "Q1")) (Some (Symbol.mk_string ctx "skid1"))) in Printf.printf "Quantifier X: %s\n" (Quantifier.to_string x) ; let y = (Quantifier.mk_forall_const ctx xs body_const (Some 1) [] [] (Some (Symbol.mk_string ctx "Q2")) (Some (Symbol.mk_string ctx "skid2"))) in Printf.printf "Quantifier Y: %s\n" (Quantifier.to_string y) ; if (is_true (Quantifier.expr_of_quantifier x)) then raise (TestFailedException "") (* unreachable *) else if (is_false (Quantifier.expr_of_quantifier x)) then raise (TestFailedException "") (* unreachable *) else if (is_const (Quantifier.expr_of_quantifier x)) then raise (TestFailedException "") (* unreachable *) open Z3.FloatingPoint (** A basic example of floating point arithmetic **) let fpa_example ( ctx : context ) = Printf.printf "FPAExample\n" ; (* let str = ref "" in *) (* (read_line ()) ; *) let double_sort = (FloatingPoint.mk_sort_double ctx) in let rm_sort = (FloatingPoint.RoundingMode.mk_sort ctx) in (** Show that there are x, y s.t. (x + y) = 42.0 (with rounding mode). *) let s_rm = (mk_string ctx "rm") in let rm = (mk_const ctx s_rm rm_sort) in let s_x = (mk_string ctx "x") in let s_y = (mk_string ctx "y") in let x = (mk_const ctx s_x double_sort) in let y = (mk_const ctx s_y double_sort)in let n = (FloatingPoint.mk_numeral_f ctx 42.0 double_sort) in let s_x_plus_y = (mk_string ctx "x_plus_y") in let x_plus_y = (mk_const ctx s_x_plus_y double_sort) in let c1 = (mk_eq ctx x_plus_y (mk_add ctx rm x y)) in let args = [ c1 ; (mk_eq ctx x_plus_y n) ] in let c2 = (Boolean.mk_and ctx args) in let args2 = [ c2 ; (Boolean.mk_not ctx (Boolean.mk_eq ctx rm (RoundingMode.mk_rtz ctx))) ] in let c3 = (Boolean.mk_and ctx args2) in let and_args = [ (Boolean.mk_not ctx (mk_is_zero ctx y)) ; (Boolean.mk_not ctx (mk_is_nan ctx y)) ; (Boolean.mk_not ctx (mk_is_infinite ctx y)) ] in let args3 = [ c3 ; (Boolean.mk_and ctx and_args) ] in let c4 = (Boolean.mk_and ctx args3) in (Printf.printf "c4: %s\n" (Expr.to_string c4)) ; ( let solver = (mk_solver ctx None) in (Solver.add solver [ c4 ]) ; if (check solver []) != SATISFIABLE then raise (TestFailedException "") else Printf.printf "Test passed.\n" ); (* Show that the following are equal: *) (* (fp #b0 #b10000000001 #xc000000000000) *) (* ((_ to_fp 11 53) #x401c000000000000)) *) (* ((_ to_fp 11 53) RTZ 1.75 2))) *) (* ((_ to_fp 11 53) RTZ 7.0))) *) let c1 = (mk_fp ctx (mk_numeral_string ctx "0" (BitVector.mk_sort ctx 1)) (mk_numeral_string ctx "1025" (BitVector.mk_sort ctx 11)) (mk_numeral_string ctx "3377699720527872" (BitVector.mk_sort ctx 52))) in let c2 = (mk_to_fp_bv ctx (mk_numeral_string ctx "4619567317775286272" (BitVector.mk_sort ctx 64)) (mk_sort ctx 11 53)) in let c3 = (mk_to_fp_int_real ctx (RoundingMode.mk_rtz ctx) (mk_numeral_string ctx "2" (Integer.mk_sort ctx)) (mk_numeral_string ctx "1.75" (Real.mk_sort ctx)) (FloatingPoint.mk_sort ctx 11 53)) in let c4 = (mk_to_fp_real ctx (RoundingMode.mk_rtz ctx) (mk_numeral_string ctx "7.0" (Real.mk_sort ctx)) (FloatingPoint.mk_sort ctx 11 53)) in let args3 = [ (mk_eq ctx c1 c2) ; (mk_eq ctx c1 c3) ; (mk_eq ctx c1 c4) ] in let c5 = (Boolean.mk_and ctx args3) in (Printf.printf "c5: %s\n" (Expr.to_string c5)) ; ( let solver = (mk_solver ctx None) in (Solver.add solver [ c5 ]) ; if (check solver []) != SATISFIABLE then raise (TestFailedException "") else Printf.printf "Test passed.\n" ) let _ = try ( if not (Log.open_ "z3.log") then raise (TestFailedException "Log couldn't be opened.") else ( Printf.printf "Running Z3 version %s\n" Version.to_string ; Printf.printf "Z3 full version string: %s\n" Version.full_version ; let cfg = [("model", "true"); ("proof", "false")] in let ctx = (mk_context cfg) in let is = (Symbol.mk_int ctx 42) in let ss = (Symbol.mk_string ctx "mySymbol") in let bs = (Boolean.mk_sort ctx) in let ints = (Integer.mk_sort ctx) in let rs = (Real.mk_sort ctx) in let v = (Arithmetic.Integer.mk_numeral_i ctx 8000000000) in Printf.printf "int symbol: %s\n" (Symbol.to_string is); Printf.printf "string symbol: %s\n" (Symbol.to_string ss); Printf.printf "bool sort: %s\n" (Sort.to_string bs); Printf.printf "int sort: %s\n" (Sort.to_string ints); Printf.printf "real sort: %s\n" (Sort.to_string rs); Printf.printf "integer: %s\n" (Expr.to_string v); basic_tests ctx ; quantifier_example1 ctx ; fpa_example ctx ; Printf.printf "Disposing...\n"; Gc.full_major () ); Printf.printf "Exiting.\n" ; exit 0 ) with Error(msg) -> ( Printf.printf "Z3 EXCEPTION: %s\n" msg ; exit 1 ) ;; z3-z3-4.8.7/examples/msf/000077500000000000000000000000001356505360400150325ustar00rootroot00000000000000z3-z3-4.8.7/examples/msf/README000066400000000000000000000020251356505360400157110ustar00rootroot00000000000000In order to use Z3 MSF plugin, follow the following steps: 1. Compile latest Z3 .NET API (from any branch consisting of opt features) and copy 'libz3.dll' and 'Microsoft.Z3.dll' to the folder of 'Z3MSFPlugin.sln'. 2. Retrieve 'Microsoft.Solver.Foundation.dll' from http://archive.msdn.microsoft.com/solverfoundation/Release/ProjectReleases.aspx?ReleaseId=1799, preferably using DLL only version. Copy 'Microsoft.Solver.Foundation.dll' to the folder of 'Z3MSFPlugin.sln' 3. Build 'Z3MSFPlugin.sln'. Note that you have to compile using x86 target for Microsoft.Z3.dll 32-bit and x64 target for Microsoft.Z3.dll 64-bit. The solution consists of a plugin project, a test project with a few simple test cases and a command line projects for external OML, MPS and SMPS models. To retrieve SMT2 models which are native to Z3, set the logging file in corresponding directives or solver params e.g. var p = new Z3MILPParams(); p.SMT2LogFile = "model.smt2"; For more details, check out the commands in 'Validator\Program.cs'. Enjoy! z3-z3-4.8.7/examples/msf/SolverFoundation.Plugin.Z3.Tests/000077500000000000000000000000001356505360400231645ustar00rootroot00000000000000z3-z3-4.8.7/examples/msf/SolverFoundation.Plugin.Z3.Tests/App.config000066400000000000000000000070131356505360400250740ustar00rootroot00000000000000
z3-z3-4.8.7/examples/msf/SolverFoundation.Plugin.Z3.Tests/Properties/000077500000000000000000000000001356505360400253205ustar00rootroot00000000000000z3-z3-4.8.7/examples/msf/SolverFoundation.Plugin.Z3.Tests/Properties/AssemblyInfo.cs000066400000000000000000000026401356505360400302440ustar00rootroot00000000000000using 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("SolverFoundation.Plugin.Z3.Tests")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("SolverFoundation.Plugin.Z3.Tests")] [assembly: AssemblyCopyright("Copyright © 2013")] [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("27657eee-ca7b-4996-a905-86a3f4584988")] // 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")] z3-z3-4.8.7/examples/msf/SolverFoundation.Plugin.Z3.Tests/ServiceTests.cs000066400000000000000000000051501356505360400261370ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ using System; using System.Collections.Generic; using System.Linq; using System.Text; using Microsoft.SolverFoundation.Common; using Microsoft.SolverFoundation.Solvers; using Microsoft.SolverFoundation.Services; using Microsoft.SolverFoundation.Plugin.Z3; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Microsoft.SolverFoundation.Plugin.Z3.Tests { [TestClass] public class ServiceTests { [TestInitialize] public void SetUp() { SolverContext context = SolverContext.GetContext(); context.ClearModel(); } private void TestService1(Directive directive) { SolverContext context = SolverContext.GetContext(); Model model = context.CreateModel(); Decision x1 = new Decision(Domain.RealRange(0, 2), "x1"); Decision x2 = new Decision(Domain.RealRange(0, 2), "x2"); Decision z = new Decision(Domain.IntegerRange(0, 1), "z"); model.AddDecisions(x1, x2, z); model.AddConstraint("Row0", x1 - z <= 1); model.AddConstraint("Row1", x2 + z <= 2); Goal goal = model.AddGoal("Goal0", GoalKind.Maximize, x1 + x2); Solution solution = context.Solve(directive); Assert.IsTrue(goal.ToInt32() == 3); context.ClearModel(); } private void TestService2(Directive directive) { SolverContext context = SolverContext.GetContext(); Model model = context.CreateModel(); Decision x1 = new Decision(Domain.RealNonnegative, "x1"); Decision x2 = new Decision(Domain.RealNonnegative, "x2"); Decision z = new Decision(Domain.IntegerRange(0, 1), "z"); Rational M = 100; model.AddDecisions(x1, x2, z); model.AddConstraint("Row0", x1 + x2 >= 1); model.AddConstraint("Row1", x1 - z * 100 <= 0); model.AddConstraint("Row2", x2 + z * 100 <= 100); Goal goal = model.AddGoal("Goal0", GoalKind.Maximize, x1 + x2); Solution solution = context.Solve(directive); Assert.IsTrue(goal.ToInt32() == 100); context.ClearModel(); } [TestMethod] public void TestService1() { TestService1(new Z3MILPDirective()); TestService1(new Z3TermDirective()); } [TestMethod] public void TestService2() { TestService2(new Z3MILPDirective()); TestService2(new Z3TermDirective()); } } } z3-z3-4.8.7/examples/msf/SolverFoundation.Plugin.Z3.Tests/SolverFoundation.Plugin.Z3.Tests.csproj000066400000000000000000000062041356505360400325420ustar00rootroot00000000000000 Debug AnyCPU {280AEE2F-1FDB-4A27-BE37-14DC154C873B} Library Properties Microsoft.SolverFoundation.Plugin.Z3.Tests SolverFoundation.Plugin.Z3.Tests v4.0 512 true full false bin\Debug\ DEBUG;TRACE prompt 4 x86 pdbonly true bin\Release\ TRACE prompt 4 x86 ..\Microsoft.Solver.Foundation.dll {7340e664-f648-4ff7-89b2-f4da424996d3} SolverFoundation.Plugin.Z3 z3-z3-4.8.7/examples/msf/SolverFoundation.Plugin.Z3.Tests/SolverTests.cs000066400000000000000000000102661356505360400260150ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ using System; using System.Collections.Generic; using System.Linq; using System.Text; using Microsoft.SolverFoundation.Common; using Microsoft.SolverFoundation.Solvers; using Microsoft.SolverFoundation.Services; using Microsoft.SolverFoundation.Plugin.Z3; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Microsoft.SolverFoundation.Plugin.Z3.Tests { [TestClass] public class SolverTests { [TestMethod] public void TestMILPSolver1() { var solver = new Z3MILPSolver(); int goal; solver.AddRow("goal", out goal); int x1, x2, z; // 0 <= x1 <= 2 solver.AddVariable("x1", out x1); solver.SetBounds(x1, 0, 2); // 0 <= x2 <= 2 solver.AddVariable("x2", out x2); solver.SetBounds(x2, 0, 2); // z is an integer in [0,1] solver.AddVariable("z", out z); solver.SetIntegrality(z, true); solver.SetBounds(z, 0, 1); //max x1 + x2 solver.SetCoefficient(goal, x1, 1); solver.SetCoefficient(goal, x2, 1); solver.AddGoal(goal, 1, false); // 0 <= x1 -z <= 1 int row1; solver.AddRow("rowI1", out row1); solver.SetBounds(row1, 0, 1); solver.SetCoefficient(row1, x1, 1); solver.SetCoefficient(row1, z, -1); // 0 <= x2 + z <= 2 int row2; solver.AddRow("rowI2", out row2); solver.SetBounds(row2, 0, 2); solver.SetCoefficient(row2, x2, 1); solver.SetCoefficient(row2, z, 1); var p = new Z3MILPParams(); solver.Solve(p); Assert.IsTrue(solver.Result == LinearResult.Optimal); Assert.AreEqual(solver.GetValue(x1), 2 * Rational.One); Assert.AreEqual(solver.GetValue(x2), Rational.One); Assert.AreEqual(solver.GetValue(z), Rational.One); Assert.AreEqual(solver.GetValue(goal), 3 * Rational.One); } [TestMethod] public void TestMILPSolver2() { var solver = new Z3MILPSolver(); int goal, extraGoal; Rational M = 100; solver.AddRow("goal", out goal); int x1, x2, z; // 0 <= x1 <= 100 solver.AddVariable("x1", out x1); solver.SetBounds(x1, 0, M); // 0 <= x2 <= 100 solver.AddVariable("x2", out x2); solver.SetBounds(x2, 0, M); // z is an integer in [0,1] solver.AddVariable("z", out z); solver.SetIntegrality(z, true); solver.SetBounds(z, 0, 1); solver.SetCoefficient(goal, x1, 1); solver.SetCoefficient(goal, x2, 2); solver.AddGoal(goal, 1, false); solver.AddRow("extraGoal", out extraGoal); solver.SetCoefficient(extraGoal, x1, 2); solver.SetCoefficient(extraGoal, x2, 1); solver.AddGoal(extraGoal, 2, false); // x1 + x2 >= 1 int row; solver.AddRow("row", out row); solver.SetBounds(row, 1, Rational.PositiveInfinity); solver.SetCoefficient(row, x1, 1); solver.SetCoefficient(row, x2, 1); // x1 - M*z <= 0 int row1; solver.AddRow("rowI1", out row1); solver.SetBounds(row1, Rational.NegativeInfinity, 0); solver.SetCoefficient(row1, x1, 1); solver.SetCoefficient(row1, z, -M); // x2 - M* (1-z) <= 0 int row2; solver.AddRow("rowI2", out row2); solver.SetBounds(row2, Rational.NegativeInfinity, M); solver.SetCoefficient(row2, x2, 1); solver.SetCoefficient(row2, z, M); var p = new Z3MILPParams(); p.OptKind = OptimizationKind.BoundingBox; solver.Solve(p); Assert.IsTrue(solver.Result == LinearResult.Optimal); Assert.AreEqual(solver.GetValue(goal), 200 * Rational.One); Assert.AreEqual(solver.GetValue(extraGoal), 200 * Rational.One); } } } z3-z3-4.8.7/examples/msf/SolverFoundation.Plugin.Z3/000077500000000000000000000000001356505360400220635ustar00rootroot00000000000000z3-z3-4.8.7/examples/msf/SolverFoundation.Plugin.Z3/AbortWorker.cs000066400000000000000000000053501356505360400246560ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using Microsoft.Z3; namespace Microsoft.SolverFoundation.Plugin.Z3 { /// /// Thread that will wait until the query abort function returns true or /// the stop method is called. If the abort function returns true at some /// point it will issue a softCancel() call to Z3. /// internal class AbortWorker { #region Private Members /// The Z3 solver private Microsoft.Z3.Context _context; /// The abort function to use to check if we are aborted private Func _QueryAbortFunction; /// Flag indicating that worker should stop private bool _stop = false; /// Flag indicating that we have been sent an abort signal private bool _aborted = false; #endregion Private Members #region Construction /// /// Worker constructor taking a Z3 instance and a function to periodically /// check for aborts. /// /// Z3 instance /// method to call to check for aborts public AbortWorker(Context context, Func queryAbortFunction) { _context = context; _QueryAbortFunction = queryAbortFunction; } #endregion Construction #region Public Methods /// /// Stop the abort worker. /// public void Stop() { _stop = true; } /// /// Is true if we have been aborted. /// public bool Aborted { get { return _aborted; } } /// /// Starts the abort worker. The worker checks the abort method /// periodically until either it is stopped by a call to the Stop() /// method or it gets an abort signal. In the latter case it will /// issue a soft abort signal to Z3. /// public void Start() { // We go until someone stops us _stop = false; while (!_stop && !_QueryAbortFunction()) { // Wait for a while Thread.Sleep(10); } // If we were stopped on abort, cancel z3 if (!_stop) { _context.Interrupt(); _aborted = true; } } #endregion Public Methods } } z3-z3-4.8.7/examples/msf/SolverFoundation.Plugin.Z3/App.config000066400000000000000000000070131356505360400237730ustar00rootroot00000000000000
z3-z3-4.8.7/examples/msf/SolverFoundation.Plugin.Z3/Properties/000077500000000000000000000000001356505360400242175ustar00rootroot00000000000000z3-z3-4.8.7/examples/msf/SolverFoundation.Plugin.Z3/Properties/AssemblyInfo.cs000066400000000000000000000026461356505360400271510ustar00rootroot00000000000000using 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("SolverFoundation.Plugin.Z3")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Microsoft")] [assembly: AssemblyProduct("SolverFoundation.Plugin.Z3")] [assembly: AssemblyCopyright("Copyright © Microsoft 2010")] [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("ed1476c0-96de-4d2c-983d-3888b140c3ad")] // 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")] z3-z3-4.8.7/examples/msf/SolverFoundation.Plugin.Z3/SolverFoundation.Plugin.Z3.csproj000066400000000000000000000143071356505360400303430ustar00rootroot00000000000000 Debug AnyCPU 9.0.30729 2.0 {7340E664-F648-4FF7-89B2-F4DA424996D3} Library Properties Microsoft.SolverFoundation.Plugin.Z3 SolverFoundation.Plugin.Z3 v4.0 512 false publish\ true Disk false Foreground 7 Days false false true 0 1.0.0.%2a false false true true full false bin\Debug\ DEBUG;TRACE prompt 4 AllRules.ruleset pdbonly true bin\Release\ TRACE prompt 4 AllRules.ruleset bin\commercial\ TRACE true pdbonly AnyCPU prompt bin\commercial_64\ TRACE true pdbonly AnyCPU prompt true bin\x86\Debug\ DEBUG;TRACE full x86 prompt AllRules.ruleset bin\x86\Release\ TRACE true pdbonly x86 prompt AllRules.ruleset bin\x86\commercial\ TRACE true pdbonly x86 prompt AllRules.ruleset bin\x86\commercial_64\ TRACE true pdbonly x86 prompt ..\Microsoft.Solver.Foundation.dll False ..\Microsoft.Z3.dll z3-z3-4.8.7/examples/msf/SolverFoundation.Plugin.Z3/Utils.cs000066400000000000000000000073301356505360400235150ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Diagnostics; using Microsoft.Z3; using Microsoft.SolverFoundation.Common; namespace Microsoft.SolverFoundation.Plugin.Z3 { public class Utils { /// /// Returns the Z3 term corresponding to the MSF rational number. /// /// The MSF rational /// The Z3 term public static ArithExpr GetNumeral(Context context, Rational rational, Sort sort = null) { try { sort = rational.IsInteger() ? ((Sort)context.IntSort) : (sort == null ? (Sort)context.RealSort : sort); return (ArithExpr)context.MkNumeral(rational.ToString(), sort); } catch (Z3Exception e) { Console.Error.WriteLine("Conversion of {0} failed:\n {1}", rational, e); throw new NotSupportedException(); } } private static long BASE = 10 ^ 18; private static Rational ToRational(System.Numerics.BigInteger bi) { if (System.Numerics.BigInteger.Abs(bi) <= BASE) { return (Rational)((long)bi); } return BASE * ToRational(bi / BASE) + ToRational(bi % BASE); } public static Rational ToRational(IntNum i) { return ToRational(i.BigInteger); } public static Rational ToRational(RatNum r) { return ToRational(r.BigIntNumerator) / ToRational(r.BigIntDenominator); } public static Rational ToRational(Expr expr) { Debug.Assert(expr is ArithExpr, "Only accept ArithExpr for now."); var e = expr as ArithExpr; if (e is IntNum) { Debug.Assert(expr.IsIntNum, "Number should be an integer."); return ToRational(expr as IntNum); } if (e is RatNum) { Debug.Assert(expr.IsRatNum, "Number should be a rational."); return ToRational(expr as RatNum); } if (e.IsAdd) { Rational r = Rational.Zero; foreach (var arg in e.Args) { r += ToRational(arg); } return r; } if (e.IsMul) { Rational r = Rational.One; foreach (var arg in e.Args) { r *= ToRational(arg); } return r; } if (e.IsUMinus) { return -ToRational(e.Args[0]); } if (e.IsDiv) { return ToRational(e.Args[0]) / ToRational(e.Args[1]); } if (e.IsSub) { Rational r = ToRational(e.Args[0]); for (int i = 1; i < e.Args.Length; ++i) { r -= ToRational(e.Args[i]); } return r; } if (e.IsConst && e.FuncDecl.Name.ToString() == "oo") { return Rational.PositiveInfinity; } if (e.IsConst && e.FuncDecl.Name.ToString() == "epsilon") { return Rational.One/Rational.PositiveInfinity; } Debug.Assert(false, "Should not happen"); return Rational.One; } } } z3-z3-4.8.7/examples/msf/SolverFoundation.Plugin.Z3/Z3BaseDirective.cs000066400000000000000000000047371356505360400253530ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ using System; using System.Text; using Microsoft.SolverFoundation.Services; namespace Microsoft.SolverFoundation.Plugin.Z3 { /// /// Combining objective functions /// public enum OptimizationKind { Lexicographic, BoundingBox, ParetoOptimal }; /// /// Algorithm for solving cardinality constraints /// public enum CardinalityAlgorithm { FuMalik, CoreMaxSAT } /// /// Algorithm for solving pseudo-boolean constraints /// public enum PseudoBooleanAlgorithm { WeightedMaxSAT, IterativeWeightedMaxSAT, BisectionWeightedMaxSAT, WeightedPartialMaxSAT2 } /// /// Strategy for solving arithmetic optimization /// public enum ArithmeticStrategy { Basic, Farkas } public abstract class Z3BaseDirective : Directive { protected OptimizationKind _optKind; protected CardinalityAlgorithm _cardAlgorithm; protected PseudoBooleanAlgorithm _pboAlgorithm; protected ArithmeticStrategy _arithStrategy; protected string _smt2LogFile; public Z3BaseDirective() { Arithmetic = Arithmetic.Exact; } public OptimizationKind OptKind { get { return _optKind; } set { _optKind = value; } } public CardinalityAlgorithm CardinalityAlgorithm { get { return _cardAlgorithm; } set { _cardAlgorithm = value; } } public PseudoBooleanAlgorithm PseudoBooleanAlgorithm { get { return _pboAlgorithm; } set { _pboAlgorithm = value; } } public ArithmeticStrategy ArithmeticStrategy { get { return _arithStrategy; } set { _arithStrategy = value; } } public string SMT2LogFile { get { return _smt2LogFile; } set { _smt2LogFile = value; } } public override string ToString() { var sb = new StringBuilder(); sb.Append(this.GetType().Name); sb.Append("("); sb.AppendFormat("OptKind: {0}, ", _optKind); sb.AppendFormat("SMT2LogFile: {0}", _smt2LogFile); sb.Append(")"); return sb.ToString(); } } } z3-z3-4.8.7/examples/msf/SolverFoundation.Plugin.Z3/Z3BaseParams.cs000066400000000000000000000055001356505360400246450ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ using Microsoft.SolverFoundation.Services; using System; namespace Microsoft.SolverFoundation.Plugin.Z3 { /// /// Implementation of the solver parameters for Z3 /// public class Z3BaseParams : ISolverParameters { #region Private Members /// The abort method we can call (defaults to always false) protected Func _queryAbortFunction = delegate() { return false; }; /// The directive to use protected Directive _directive = null; protected OptimizationKind _optKind; protected CardinalityAlgorithm _cardAlgorithm; protected PseudoBooleanAlgorithm _pboAlgorithm; protected ArithmeticStrategy _arithStrategy; protected string _smt2LogFile; #endregion Private Members #region Construction public Z3BaseParams() { } public Z3BaseParams(Directive directive) { _directive = directive; var z3Directive = directive as Z3BaseDirective; if (z3Directive != null) { _optKind = z3Directive.OptKind; _cardAlgorithm = z3Directive.CardinalityAlgorithm; _pboAlgorithm = z3Directive.PseudoBooleanAlgorithm; _arithStrategy = z3Directive.ArithmeticStrategy; _smt2LogFile = z3Directive.SMT2LogFile; } } public Z3BaseParams(Func queryAbortFunction) { _queryAbortFunction = queryAbortFunction; } public Z3BaseParams(Z3BaseParams z3Parameters) { _queryAbortFunction = z3Parameters._queryAbortFunction; } #endregion Construction #region ISolverParameters Members /// /// Getter for the abort method /// public Func QueryAbort { get { return _queryAbortFunction; } set { _queryAbortFunction = value; } } public OptimizationKind OptKind { get { return _optKind; } set { _optKind = value; } } public CardinalityAlgorithm CardinalityAlgorithm { get { return _cardAlgorithm; } set { _cardAlgorithm = value; } } public PseudoBooleanAlgorithm PseudoBooleanAlgorithm { get { return _pboAlgorithm; } set { _pboAlgorithm = value; } } public ArithmeticStrategy ArithmeticStrategy { get { return _arithStrategy; } set { _arithStrategy = value; } } public string SMT2LogFile { get { return _smt2LogFile; } set { _smt2LogFile = value; } } #endregion } }z3-z3-4.8.7/examples/msf/SolverFoundation.Plugin.Z3/Z3BaseSolver.cs000066400000000000000000000340601356505360400246770ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ using System; using System.Collections.Generic; using System.Threading; using System.IO; using System.Linq; using System.Text; using System.Diagnostics; using Microsoft.Z3; using Microsoft.SolverFoundation.Common; using Microsoft.SolverFoundation.Services; namespace Microsoft.SolverFoundation.Plugin.Z3 { internal enum Z3Result { Optimal, LocalOptimal, Feasible, Interrupted, Infeasible } /// /// The basic solver class to take care of transformation from an MSF instance to an Z3 instance /// internal class Z3BaseSolver { /// Representing MSF model private IRowVariableModel _model; /// The Z3 solver we are currently using private Context _context = null; /// Default optimization solver private Optimize _optSolver = null; /// Marks when we are inside the Solve() method private bool _isSolving = false; /// A map from MSF variable ids to Z3 variables private Dictionary _variables = new Dictionary(); /// A map from MSF variable ids to Z3 goal ids private Dictionary _goals = new Dictionary(); internal Z3BaseSolver(IRowVariableModel model) { _model = model; } internal Context Context { get { return _context; } } internal Dictionary Variables { get { return _variables; } } internal Dictionary Goals { get { return _goals; } } /// /// Destructs a currently active Z3 solver and the associated data. /// internal void DestructSolver(bool checkInSolve) { if (_context != null) { if (checkInSolve && !_isSolving) { _variables.Clear(); if (!_isSolving) { _optSolver.Dispose(); _context.Dispose(); } } else { Console.Error.WriteLine("Z3 destruction is invoked while in Solving phase."); } } } /// /// Constructs a Z3 solver to be used. /// internal void ConstructSolver(Z3BaseParams parameters) { // If Z3 is there already, kill it if (_context != null) { DestructSolver(false); } _context = new Context(); _optSolver = _context.MkOptimize(); var p = _context.MkParams(); switch (parameters.OptKind) { case OptimizationKind.BoundingBox: p.Add("priority", _context.MkSymbol("box")); break; case OptimizationKind.Lexicographic: p.Add("priority", _context.MkSymbol("lex")); break; case OptimizationKind.ParetoOptimal: p.Add("priority", _context.MkSymbol("pareto")); break; default: Debug.Assert(false, String.Format("Unknown optimization option {0}", parameters.OptKind)); break; } switch (parameters.CardinalityAlgorithm) { case CardinalityAlgorithm.FuMalik: p.Add("maxsat_engine", _context.MkSymbol("fu_malik")); break; case CardinalityAlgorithm.CoreMaxSAT: p.Add("maxsat_engine", _context.MkSymbol("core_maxsat")); break; default: Debug.Assert(false, String.Format("Unknown cardinality algorithm option {0}", parameters.CardinalityAlgorithm)); break; } switch (parameters.PseudoBooleanAlgorithm) { case PseudoBooleanAlgorithm.WeightedMaxSAT: p.Add("wmaxsat_engine", _context.MkSymbol("wmax")); break; case PseudoBooleanAlgorithm.IterativeWeightedMaxSAT: p.Add("wmaxsat_engine", _context.MkSymbol("iwmax")); break; case PseudoBooleanAlgorithm.BisectionWeightedMaxSAT: p.Add("wmaxsat_engine", _context.MkSymbol("bwmax")); break; case PseudoBooleanAlgorithm.WeightedPartialMaxSAT2: p.Add("wmaxsat_engine", _context.MkSymbol("wpm2")); break; default: Debug.Assert(false, String.Format("Unknown pseudo-boolean algorithm option {0}", parameters.PseudoBooleanAlgorithm)); break; } switch (parameters.ArithmeticStrategy) { case ArithmeticStrategy.Basic: p.Add("engine", _context.MkSymbol("basic")); break; case ArithmeticStrategy.Farkas: p.Add("engine", _context.MkSymbol("farkas")); break; default: Debug.Assert(false, String.Format("Unknown arithmetic strategy option {0}", parameters.ArithmeticStrategy)); break; } _optSolver.Parameters = p; } internal ArithExpr GetVariable(int vid) { Expr variable; if (!_variables.TryGetValue(vid, out variable)) { AddVariable(vid); variable = _variables[vid]; } return (ArithExpr)variable; } internal void AssertBool(BoolExpr row) { _optSolver.Assert(row); } internal void AssertArith(int vid, ArithExpr variable) { // Get the bounds on the row Rational lower, upper; _model.GetBounds(vid, out lower, out upper); // Case of equality if (lower == upper) { // Create the equality term Expr eqConst = GetNumeral(lower, variable.Sort); BoolExpr constraint = _context.MkEq(eqConst, variable); // Assert the constraint _optSolver.Assert(constraint); } else { // If upper bound is finite assert the upper bound constraint if (lower.IsFinite) { // Create the lower Bound constraint ArithExpr lowerTerm = GetNumeral(lower, variable.Sort); BoolExpr constraint = _context.MkLe(lowerTerm, variable); // Assert the constraint _optSolver.Assert(constraint); } // If lower bound is finite assert the lower bound constraint if (upper.IsFinite) { // Create the upper bound constraint ArithExpr upperTerm = GetNumeral(upper, variable.Sort); BoolExpr constraint = _context.MkGe(upperTerm, variable); // Assert the constraint _optSolver.Assert(constraint); } } } /// /// Adds a MSF variable with the corresponding assertion to the Z3 variables. /// /// The MSF id of the variable internal void AddVariable(int vid) { // Is the variable an integer bool isInteger = _model.GetIntegrality(vid); // Construct the sort we will be using Sort sort = isInteger ? (Sort)_context.IntSort : (Sort)_context.RealSort; // Get the variable key object key = _model.GetKeyFromIndex(vid); // Try to construct the name string name; if (key != null) name = String.Format("x_{0}_{1}", key, vid); else name = String.Format("x_{0}", vid); ArithExpr variable = (ArithExpr)_context.MkConst(name, sort); // Create the variable and add it to the map Debug.Assert(!_variables.ContainsKey(vid), "Variable names should be unique."); _variables.Add(vid, variable); AssertArith(vid, variable); } internal ArithExpr GetNumeral(Rational rational, Sort sort = null) { return Utils.GetNumeral(_context, rational, sort); } internal void Solve(Z3BaseParams parameters, IEnumerable modelGoals, Action addRow, Func mkGoalRow, Action setResult) { _variables.Clear(); _goals.Clear(); try { // Mark that we are in solving phase _isSolving = true; // Construct Z3 ConstructSolver(parameters); // Add all the variables foreach (int vid in _model.VariableIndices) { AddVariable(vid); } // Add all the rows foreach (int rid in _model.RowIndices) { addRow(rid); } // Add enabled goals to optimization problem foreach (IGoal g in modelGoals) { if (!g.Enabled) continue; ArithExpr gr = mkGoalRow(g.Index); if (g.Minimize) _goals.Add(g, _optSolver.MkMinimize(gr)); else _goals.Add(g, _optSolver.MkMaximize(gr)); } if (_goals.Any() && parameters.SMT2LogFile != null) { Debug.WriteLine("Dumping SMT2 benchmark to log file..."); File.WriteAllText(parameters.SMT2LogFile, _optSolver.ToString()); } bool aborted = parameters.QueryAbort(); if (!aborted) { // Start the abort thread AbortWorker abortWorker = new AbortWorker(_context, parameters.QueryAbort); Thread abortThread = new Thread(abortWorker.Start); abortThread.Start(); // Now solve the problem Status status = _optSolver.Check(); // Stop the abort thread abortWorker.Stop(); abortThread.Join(); switch (status) { case Status.SATISFIABLE: Microsoft.Z3.Model model = _optSolver.Model; Debug.Assert(model != null, "Should be able to get Z3 model."); // Remember the solution values foreach (KeyValuePair pair in _variables) { var value = Utils.ToRational(model.Eval(pair.Value, true)); _model.SetValue(pair.Key, value); } // Remember all objective values foreach (var pair in _goals) { var optimalValue = Utils.ToRational(pair.Value.Upper); _model.SetValue(pair.Key.Index, optimalValue); } model.Dispose(); setResult(_goals.Any() ? Z3Result.Optimal : Z3Result.Feasible); break; case Status.UNSATISFIABLE: setResult(Z3Result.Infeasible); break; case Status.UNKNOWN: if (abortWorker.Aborted) { Microsoft.Z3.Model subOptimalModel = _optSolver.Model; if (subOptimalModel != null && subOptimalModel.NumConsts != 0) { // Remember the solution values foreach (KeyValuePair pair in _variables) { var value = Utils.ToRational(subOptimalModel.Eval(pair.Value, true)); _model.SetValue(pair.Key, value); } // Remember all objective values foreach (var pair in _goals) { var optimalValue = Utils.ToRational(pair.Value.Upper); _model.SetValue(pair.Key.Index, optimalValue); } subOptimalModel.Dispose(); setResult(Z3Result.LocalOptimal); } else setResult(Z3Result.Infeasible); } else setResult(Z3Result.Interrupted); break; default: Debug.Assert(false, "Unrecognized Z3 Status"); break; } } } finally { _isSolving = false; } // Now kill Z3 DestructSolver(true); } } } z3-z3-4.8.7/examples/msf/SolverFoundation.Plugin.Z3/Z3MILPDirective.cs000066400000000000000000000003451356505360400252310ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ using Microsoft.SolverFoundation.Services; using System; namespace Microsoft.SolverFoundation.Plugin.Z3 { public class Z3MILPDirective : Z3BaseDirective { } } z3-z3-4.8.7/examples/msf/SolverFoundation.Plugin.Z3/Z3MILPParams.cs000066400000000000000000000011071356505360400245330ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ using Microsoft.SolverFoundation.Services; using System; namespace Microsoft.SolverFoundation.Plugin.Z3 { public class Z3MILPParams : Z3BaseParams { // Need these constructors for reflection done by plugin model public Z3MILPParams() : base() { } public Z3MILPParams(Directive directive) : base(directive) { } public Z3MILPParams(Func queryAbortFunction) : base(queryAbortFunction) { } public Z3MILPParams(Z3BaseParams z3Parameters) : base (z3Parameters) { } } }z3-z3-4.8.7/examples/msf/SolverFoundation.Plugin.Z3/Z3MILPSolver.cs000066400000000000000000000172011356505360400245640ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.IO; using Microsoft.Z3; using Microsoft.SolverFoundation.Common; using Microsoft.SolverFoundation.Services; using Microsoft.SolverFoundation.Plugin; namespace Microsoft.SolverFoundation.Plugin.Z3 { /// /// The class is implementation of the MSF mixed linear programming solver /// using the Microsoft Z3 solver as the backend. /// public class Z3MILPSolver : LinearModel, ILinearSolver, ILinearSolution, IReportProvider { #region Private members private LinearResult _result; private LinearSolutionQuality _solutionQuality; private Z3BaseSolver _solver; #endregion Private members #region Solver construction and destruction /// Constructor that initializes the base classes public Z3MILPSolver() : base(null) { _result = LinearResult.Feasible; _solver = new Z3BaseSolver(this); } /// Constructor that initializes the base classes public Z3MILPSolver(ISolverEnvironment context) : this() { } /// /// Shutdown can be called when when the solver is not active, i.e. /// when it is done with Solve() or it has gracefully returns from Solve() /// after an abort. /// public void Shutdown() { _solver.DestructSolver(true); } #endregion Solver construction and destruction #region Obtaining information about the solution public ILinearSolverReport GetReport(LinearSolverReportType reportType) { // We don't support sensitivity report return null; } #endregion Obtaining information about the solution #region Construction of the problem /// /// Get corresponding Z3 formula of a MSF row. /// /// The MSF row id private ArithExpr MkGoalRow(int rid) { // Start with the 0 term List row = new List(); // Now, add all the entries of this row foreach (LinearEntry entry in GetRowEntries(rid)) { // Get the variable and constant in the row ArithExpr e = _solver.GetVariable(entry.Index); if (!entry.Value.IsOne) { e = _solver.Context.MkMul(_solver.GetNumeral(entry.Value, e.Sort), e); } row.Add(e); } switch (row.Count) { case 0: return _solver.GetNumeral(new Rational()); case 1: return row[0]; default: return _solver.Context.MkAdd(row.ToArray()); } } /// /// Adds a MSF row to the Z3 assertions. /// /// The MSF row id private void AddRow(int rid) { // Start with the 0 term ArithExpr row = MkGoalRow(rid); _solver.AssertArith(rid, row); } /// /// Set results based on internal solver status /// private void SetResult(Z3Result status) { switch (status) { case Z3Result.Optimal: _result = LinearResult.Optimal; _solutionQuality = LinearSolutionQuality.Exact; break; case Z3Result.LocalOptimal: _result = LinearResult.Feasible; _solutionQuality = LinearSolutionQuality.Approximate; break; case Z3Result.Feasible: _result = LinearResult.Feasible; _solutionQuality = LinearSolutionQuality.Exact; break; case Z3Result.Infeasible: _result = LinearResult.InfeasiblePrimal; _solutionQuality = LinearSolutionQuality.None; break; case Z3Result.Interrupted: _result = LinearResult.Interrupted; _solutionQuality = LinearSolutionQuality.None; break; default: Debug.Assert(false, "Unrecognized Z3 Result"); break; } } #endregion Construction of the problem #region Solving the problem /// /// Starts solving the problem using the Z3 solver. /// /// Parameters to the solver /// The solution to the problem public ILinearSolution Solve(ISolverParameters parameters) { // Get the Z3 parameters var z3Params = parameters as Z3BaseParams; Debug.Assert(z3Params != null, "Parameters should be an instance of Z3BaseParams."); _solver.Solve(z3Params, Goals, AddRow, MkGoalRow, SetResult); return this; } #endregion Solving the problem #region ILinearSolution Members public Rational GetSolutionValue(int goalIndex) { var goal = Goals.ElementAt(goalIndex); Debug.Assert(goal != null, "Goal should be an element of the goal list."); return GetValue(goal.Index); } public void GetSolvedGoal(int goalIndex, out object key, out int vid, out bool minimize, out bool optimal) { var goal = Goals.ElementAt(goalIndex); Debug.Assert(goal != null, "Goal should be an element of the goal list."); key = goal.Key; vid = goal.Index; minimize = goal.Minimize; optimal = _result == LinearResult.Optimal; } // LpResult is LP relaxation assignment. public LinearResult LpResult { get { return _result; } } public Rational MipBestBound { get { Debug.Assert(GoalCount > 0, "MipBestBound is only applicable for optimization instances."); return GetSolutionValue(0); } } public LinearResult MipResult { get { return _result; } } public LinearResult Result { get { return _result; } } public LinearSolutionQuality SolutionQuality { get { return _solutionQuality; } } public int SolvedGoalCount { get { return GoalCount; } } #endregion public Report GetReport(SolverContext context, Solution solution, SolutionMapping solutionMapping) { LinearSolutionMapping lpSolutionMapping = solutionMapping as LinearSolutionMapping; if (lpSolutionMapping == null && solutionMapping != null) throw new ArgumentException("solutionMapping is not a LinearSolutionMapping", "solutionMapping"); return new Z3LinearSolverReport(context, this, solution, lpSolutionMapping); } } /// /// Class implementing the LinearReport. /// public class Z3LinearSolverReport : LinearReport { public Z3LinearSolverReport(SolverContext context, ISolver solver, Solution solution, LinearSolutionMapping solutionMapping) : base(context, solver, solution, solutionMapping) { } } } z3-z3-4.8.7/examples/msf/SolverFoundation.Plugin.Z3/Z3TermDirective.cs000066400000000000000000000003451356505360400253770ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ using Microsoft.SolverFoundation.Services; using System; namespace Microsoft.SolverFoundation.Plugin.Z3 { public class Z3TermDirective : Z3BaseDirective { } } z3-z3-4.8.7/examples/msf/SolverFoundation.Plugin.Z3/Z3TermParams.cs000066400000000000000000000007761356505360400247140ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ using Microsoft.SolverFoundation.Services; using System; namespace Microsoft.SolverFoundation.Plugin.Z3 { public class Z3TermParams : Z3BaseParams { public Z3TermParams() : base() { } public Z3TermParams(Directive directive) : base(directive) { } public Z3TermParams(Func queryAbortFunction) : base(queryAbortFunction) { } public Z3TermParams(Z3BaseParams z3Parameters) : base(z3Parameters) { } } }z3-z3-4.8.7/examples/msf/SolverFoundation.Plugin.Z3/Z3TermSolver.cs000066400000000000000000000374621356505360400247450ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ using System; using System.Threading; using System.Globalization; using System.Collections.Generic; using Microsoft.SolverFoundation.Common; using Microsoft.SolverFoundation.Properties; using Microsoft.SolverFoundation.Solvers; using Microsoft.SolverFoundation.Services; using Microsoft.Z3; using System.Linq; using System.Diagnostics; using System.IO; namespace Microsoft.SolverFoundation.Plugin.Z3 { /// /// The class is implementation of the MSF constraint solver /// using the Microsoft Z3 solver as the backend. /// This solver supports Int, Real constraints and their arbitrary boolean combinations. /// public class Z3TermSolver : TermModel, ITermSolver, INonlinearSolution, IReportProvider { private NonlinearResult _result; private Z3BaseSolver _solver; /// Constructor that initializes the base classes public Z3TermSolver() : base(null) { _solver = new Z3BaseSolver(this); } /// Constructor that initializes the base classes public Z3TermSolver(ISolverEnvironment context) : this() { } /// /// Shutdown can be called when when the solver is not active, i.e. /// when it is done with Solve() or it has gracefully returns from Solve() /// after an abort. /// public void Shutdown() { _solver.DestructSolver(true); } private BoolExpr MkBool(int rid) { var context = _solver.Context; if (IsConstant(rid)) { Rational lower, upper; GetBounds(rid, out lower, out upper); Debug.Assert(lower == upper); if (lower.IsZero) return context.MkFalse(); return context.MkTrue(); } if (IsOperation(rid)) { BoolExpr[] children; ArithExpr[] operands; TermModelOperation op = GetOperation(rid); switch(op) { case TermModelOperation.And: Debug.Assert(GetOperandCount(rid) >= 2, "Conjunction requires at least two operands."); children = (GetOperands(rid)).Select(x => MkBool(x)).ToArray(); return context.MkAnd(children); case TermModelOperation.Or: Debug.Assert(GetOperandCount(rid) >= 2, "Disjunction requires at least two operands."); children = (GetOperands(rid)).Select(x => MkBool(x)).ToArray(); return context.MkOr(children); case TermModelOperation.Not: Debug.Assert(GetOperandCount(rid) == 1, "Negation is unary."); return context.MkNot(MkBool(GetOperand(rid, 0))); case TermModelOperation.If: Debug.Assert(GetOperandCount(rid) == 3, "If is ternary."); BoolExpr b = MkBool(GetOperand(rid, 0)); Expr x1 = MkBool(GetOperand(rid, 1)); Expr x2 = MkBool(GetOperand(rid, 2)); return (BoolExpr)context.MkITE(b, x1, x2); case TermModelOperation.Unequal: Debug.Assert(GetOperandCount(rid) >= 2, "Distinct should have at least two operands."); return context.MkDistinct((GetOperands(rid)).Select(x => MkTerm(x)).ToArray()); case TermModelOperation.Greater: case TermModelOperation.Less: case TermModelOperation.GreaterEqual: case TermModelOperation.LessEqual: case TermModelOperation.Equal: Debug.Assert(GetOperandCount(rid) >= 2, "Comparison should have at least two operands."); operands = (GetOperands(rid)).Select(x => MkTerm(x)).ToArray(); return ReduceComparison(GetOperation(rid), operands); case TermModelOperation.Identity: Debug.Assert(GetOperandCount(rid) == 1, "Identity takes exactly one operand."); return MkBool(GetOperand(rid, 0)); default: return context.MkEq(MkTerm(rid), _solver.GetNumeral(Rational.One)); } } return context.MkEq(MkTerm(rid), _solver.GetNumeral(Rational.One)); } private ArithExpr MkBoolToArith(BoolExpr e) { var context = _solver.Context; return (ArithExpr)context.MkITE(e, _solver.GetNumeral(Rational.One), _solver.GetNumeral(Rational.Zero)); } private ArithExpr MkTerm(int rid) { var context = _solver.Context; if (IsConstant(rid)) { Rational lower, upper; GetBounds(rid, out lower, out upper); Debug.Assert(lower == upper); return _solver.GetNumeral(lower); } else if (IsOperation(rid)) { ArithExpr[] operands; TermModelOperation op = GetOperation(rid); switch(op) { case TermModelOperation.And: case TermModelOperation.Or: case TermModelOperation.Not: case TermModelOperation.Unequal: case TermModelOperation.Greater: case TermModelOperation.Less: case TermModelOperation.GreaterEqual: case TermModelOperation.LessEqual: case TermModelOperation.Equal: return MkBoolToArith(MkBool(rid)); case TermModelOperation.If: Debug.Assert(GetOperandCount(rid) == 3, "If is ternary."); BoolExpr b = MkBool(GetOperand(rid, 0)); Expr x1 = MkTerm(GetOperand(rid, 1)); Expr x2 = MkTerm(GetOperand(rid, 2)); return (ArithExpr)context.MkITE(b, x1, x2); case TermModelOperation.Plus: Debug.Assert(GetOperandCount(rid) >= 2, "Plus takes at least two operands."); operands = (GetOperands(rid)).Select(x => MkTerm(x)).ToArray(); return context.MkAdd(operands); case TermModelOperation.Minus: Debug.Assert(GetOperandCount(rid) == 1, "Minus takes exactly one operand."); return context.MkUnaryMinus(MkTerm(GetOperand(rid, 0))); case TermModelOperation.Times: Debug.Assert(GetOperandCount(rid) >= 2, "Times requires at least two operands."); operands = (GetOperands(rid)).Select(x => MkTerm(x)).ToArray(); return context.MkMul(operands); case TermModelOperation.Identity: Debug.Assert(GetOperandCount(rid) == 1, "Identity takes exactly one operand."); return MkTerm(GetOperand(rid, 0)); case TermModelOperation.Abs: Debug.Assert(GetOperandCount(rid) == 1, "Abs takes exactly one operand."); ArithExpr e = MkTerm(GetOperand(rid, 0)); ArithExpr minusE = context.MkUnaryMinus(e); ArithExpr zero = _solver.GetNumeral(Rational.Zero); return (ArithExpr)context.MkITE(context.MkGe(e, zero), e, minusE); default: Console.Error.WriteLine("{0} operation isn't supported.", op); throw new NotSupportedException(); } } else { return _solver.GetVariable(rid); } } private BoolExpr ReduceComparison(TermModelOperation type, ArithExpr[] operands) { var context = _solver.Context; Debug.Assert(operands.Length >= 2); Func mkComparison; switch (type) { case TermModelOperation.Greater: mkComparison = (x, y) => context.MkGt(x, y); break; case TermModelOperation.Less: mkComparison = (x, y) => context.MkLt(x, y); break; case TermModelOperation.GreaterEqual: mkComparison = (x, y) => context.MkGe(x, y); break; case TermModelOperation.LessEqual: mkComparison = (x, y) => context.MkLe(x, y); break; case TermModelOperation.Equal: mkComparison = (x, y) => context.MkEq(x, y); break; default: throw new NotSupportedException(); } BoolExpr current = mkComparison(operands[0], operands[1]); for (int i = 1; i < operands.Length - 1; ++i) current = context.MkAnd(current, mkComparison(operands[i], operands[i + 1])); return current; } private bool IsBoolRow(int rid) { Rational lower, upper; GetBounds(rid, out lower, out upper); return lower == upper && lower.IsOne && IsBoolTerm(rid); } private bool IsBoolTerm(int rid) { if (IsConstant(rid)) { Rational lower, upper; GetBounds(rid, out lower, out upper); Debug.Assert(lower == upper); return lower.IsOne || lower.IsZero; } if (IsOperation(rid)) { TermModelOperation op = GetOperation(rid); switch (op) { case TermModelOperation.And: case TermModelOperation.Or: case TermModelOperation.Not: case TermModelOperation.LessEqual: case TermModelOperation.Less: case TermModelOperation.Greater: case TermModelOperation.GreaterEqual: case TermModelOperation.Unequal: case TermModelOperation.Equal: return true; case TermModelOperation.If: return IsBoolTerm(GetOperand(rid, 1)) && IsBoolTerm(GetOperand(rid, 2)); case TermModelOperation.Identity: return IsBoolTerm(GetOperand(rid, 0)); default: return false; } } return false; } /// /// Adds a MSF row to the Z3 assertions. /// /// The MSF row id private void AddRow(int rid) { if (IsConstant(rid)) return; if (IsBoolRow(rid)) { _solver.AssertBool(MkBool(rid)); return; } // Start with the 0 term ArithExpr row = MkTerm(rid); _solver.AssertArith(rid, row); } private TermModelOperation[] _supportedOperations = { TermModelOperation.And, TermModelOperation.Or, TermModelOperation.Not, TermModelOperation.Unequal, TermModelOperation.Greater, TermModelOperation.Less, TermModelOperation.GreaterEqual, TermModelOperation.LessEqual, TermModelOperation.Equal, TermModelOperation.If, TermModelOperation.Plus, TermModelOperation.Minus, TermModelOperation.Times, TermModelOperation.Identity, TermModelOperation.Abs }; /// /// Gets the operations supported by the solver. /// /// All the TermModelOperations supported by the solver. public IEnumerable SupportedOperations { get { return _supportedOperations; } } /// /// Set results based on internal solver status /// private void SetResult(Z3Result status) { switch (status) { case Z3Result.Optimal: _result = NonlinearResult.Optimal; break; case Z3Result.LocalOptimal: _result = NonlinearResult.LocalOptimal; break; case Z3Result.Feasible: _result = NonlinearResult.Feasible; break; case Z3Result.Infeasible: _result = NonlinearResult.Infeasible; break; case Z3Result.Interrupted: _result = NonlinearResult.Interrupted; break; default: Debug.Assert(false, "Unrecognized Z3 Result"); break; } } /// /// Starts solving the problem using the Z3 solver. /// /// Parameters to the solver /// The solution to the problem public INonlinearSolution Solve(ISolverParameters parameters) { // Get the Z3 parameters var z3Params = parameters as Z3BaseParams; Debug.Assert(z3Params != null, "Parameters should be an instance of Z3BaseParams."); _solver.Solve(z3Params, Goals, AddRow, MkTerm, SetResult); return this; } double INonlinearSolution.GetValue(int vid) { Debug.Assert(_solver.Variables.ContainsKey(vid), "This index should correspond to a variable."); return GetValue(vid).ToDouble(); } public int SolvedGoalCount { get { return GoalCount; } } public double GetSolutionValue(int goalIndex) { var goal = Goals.ElementAt(goalIndex); Debug.Assert(goal != null, "Goal should be an element of the goal list."); return GetValue(goal.Index).ToDouble(); } public void GetSolvedGoal(int goalIndex, out object key, out int vid, out bool minimize, out bool optimal) { var goal = Goals.ElementAt(goalIndex); Debug.Assert(goal != null, "Goal should be an element of the goal list."); key = goal.Key; vid = goal.Index; minimize = goal.Minimize; optimal = _result == NonlinearResult.Optimal; } public NonlinearResult Result { get { return _result; } } public Report GetReport(SolverContext context, Solution solution, SolutionMapping solutionMapping) { PluginSolutionMapping pluginSolutionMapping = solutionMapping as PluginSolutionMapping; if (pluginSolutionMapping == null && solutionMapping != null) throw new ArgumentException("solutionMapping is not a LinearSolutionMapping", "solutionMapping"); return new Z3TermSolverReport(context, this, solution, pluginSolutionMapping); } } public class Z3TermSolverReport : Report { public Z3TermSolverReport(SolverContext context, ISolver solver, Solution solution, PluginSolutionMapping pluginSolutionMapping) : base(context, solver, solution, pluginSolutionMapping) { } } } z3-z3-4.8.7/examples/msf/Validator/000077500000000000000000000000001356505360400167575ustar00rootroot00000000000000z3-z3-4.8.7/examples/msf/Validator/App.config000066400000000000000000000070131356505360400206670ustar00rootroot00000000000000
z3-z3-4.8.7/examples/msf/Validator/MicrosoftSolverFoundationForExcel.dll.config000066400000000000000000000065471356505360400275530ustar00rootroot00000000000000
z3-z3-4.8.7/examples/msf/Validator/Program.cs000066400000000000000000000160371356505360400207240ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ using System; using System.IO; using System.Linq; using System.Collections.Generic; using Microsoft.SolverFoundation.Common; using Microsoft.SolverFoundation.Solvers; using Microsoft.SolverFoundation.Plugin.Z3; using Microsoft.SolverFoundation.Services; using System.Text; namespace Validator { class Program { static void LoadModel(SolverContext context, string fileName) { string ext = Path.GetExtension(fileName).ToLower(); if (ext == ".mps") { context.LoadModel(FileFormat.MPS, Path.GetFullPath(fileName)); } else if (ext == ".smps") { context.LoadModel(FileFormat.SMPS, Path.GetFullPath(fileName)); } else if (ext == ".oml") { context.LoadModel(FileFormat.OML, Path.GetFullPath(fileName)); } else { throw new NotSupportedException("This file format hasn't been supported."); } } static void ExecuteZ3(string fileName, Z3BaseDirective directive) { SolverContext context = SolverContext.GetContext(); try { LoadModel(context, fileName); Solution solution = context.Solve(directive); Report report = solution.GetReport(); Console.Write("{0}", report); } catch (Exception e) { Console.WriteLine("Skipping unsolvable instance in {0} with error message '{1}'.", fileName, e.Message); } finally { context.ClearModel(); } } static void ConvertToSMT2(string fileName, Z3BaseDirective directive) { SolverContext context = SolverContext.GetContext(); try { LoadModel(context, fileName); if (context.CurrentModel.Goals.Any()) { directive.SMT2LogFile = Path.ChangeExtension(fileName, ".smt2"); context.Solve(() => true, directive); } } catch (Exception e) { Console.WriteLine("Skipping unconvertable instance in {0} with error message '{1}'.", fileName, e.Message); } finally { context.ClearModel(); } } static void ValidateZ3(string fileName, Z3BaseDirective directive) { SolverContext context = SolverContext.GetContext(); try { LoadModel(context, fileName); if (context.CurrentModel.Goals.Any()) { var msfDirective = (directive is Z3MILPDirective) ? (Directive)new MixedIntegerProgrammingDirective() { TimeLimit = 10000 } : (Directive)new Directive() { TimeLimit = 10000 }; var sol1 = context.Solve(msfDirective); Console.WriteLine("Solved the model using MSF."); Console.Write("{0}", sol1.GetReport()); var expectedGoals = sol1.Goals.Select(x => x.ToDouble()); context.ClearModel(); context.LoadModel(FileFormat.OML, Path.GetFullPath(fileName)); directive.SMT2LogFile = Path.ChangeExtension(fileName, ".smt2"); var sol2 = context.Solve(directive); //Console.Write("{0}", sol2.GetReport()); var actualGoals = sol2.Goals.Select(x => x.ToDouble()); Console.WriteLine("Solved the model using Z3."); var goalPairs = expectedGoals.Zip(actualGoals, (expected, actual) => new { expected, actual }).ToArray(); bool validated = goalPairs.All(p => Math.Abs(p.expected - p.actual) <= 0.0001); if (validated) { Console.WriteLine("INFO: Two solvers give approximately the same results."); } else { Console.Error.WriteLine("ERROR: Discrepancy found between results."); if (!validated && File.Exists(directive.SMT2LogFile)) { var sb = new StringBuilder(); for(int i = 0; i < goalPairs.Length; i++) { sb.AppendFormat("\n(echo \"Goal {0}: actual |-> {1:0.0000}, expected |-> {2:0.0000}\")", i + 1, goalPairs[i].actual, goalPairs[i].expected); } Console.Error.WriteLine(sb.ToString()); File.AppendAllText(directive.SMT2LogFile, sb.ToString()); } } } else { Console.WriteLine("Ignoring this instance without having any goal."); } } catch (Exception e) { Console.WriteLine("Skipping unsolvable instance in {0} with error message '{1}'.", fileName, e.Message); } finally { context.ClearModel(); } } static void Main(string[] args) { Z3BaseDirective directive = new Z3MILPDirective(); for (int i = 0; i < args.Length; ++i) { if (args[i] == "-s" || args[i] == "-solve") { ExecuteZ3(args[i + 1], directive); return; } if (args[i] == "-c" || args[i] == "-convert") { ConvertToSMT2(args[i + 1], directive); return; } if (args[i] == "-v" || args[i] == "-validate") { ValidateZ3(args[i + 1], directive); return; } if (args[i] == "-t" || args[i] == "-term") { directive = new Z3TermDirective(); } } if (args.Length > 0) { ExecuteZ3(args[0], directive); return; } Console.WriteLine(@" Validator is a simple command line to migrate benchmarks from OML, MPS and SMPS to SMT2 formats. Commands: -solve : solving the model using Z3 -convert : converting the model into SMT2 format -validate : validating by comparing results between Z3 and MSF solvers -term : change the default Z3 MILP solver to Z3 Term solver where is any file with OML, MPS or SMPS extension. Examples: Validator.exe -convert model.mps Validator.exe -term -solve model.oml "); } } } z3-z3-4.8.7/examples/msf/Validator/Properties/000077500000000000000000000000001356505360400211135ustar00rootroot00000000000000z3-z3-4.8.7/examples/msf/Validator/Properties/AssemblyInfo.cs000066400000000000000000000026061356505360400240410ustar00rootroot00000000000000using 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("testSolver")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Microsoft")] [assembly: AssemblyProduct("testSolver")] [assembly: AssemblyCopyright("Copyright © Microsoft 2009")] [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("c03c1084-d119-483f-80fe-c639eae75959")] // 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")] z3-z3-4.8.7/examples/msf/Validator/Validator.csproj000066400000000000000000000124011356505360400221240ustar00rootroot00000000000000 Debug AnyCPU 9.0.21022 2.0 {54835857-129F-44C9-B529-A42158647B36} Exe Properties Validator Validator v4.0 512 publish\ true Disk false Foreground 7 Days false false true 0 1.0.0.%2a false false true true full false bin\Debug\ DEBUG;TRACE prompt 4 pdbonly true bin\Release\ TRACE prompt 4 true bin\x64\Debug\ DEBUG;TRACE full x86 true GlobalSuppressions.cs prompt bin\x64\Release\ TRACE true pdbonly x64 true GlobalSuppressions.cs prompt true bin\x86\Debug\ DEBUG;TRACE full x86 prompt MinimumRecommendedRules.ruleset bin\x86\Release\ TRACE true pdbonly x86 prompt MinimumRecommendedRules.ruleset ..\Microsoft.Solver.Foundation.dll {7340e664-f648-4ff7-89b2-f4da424996d3} SolverFoundation.Plugin.Z3 z3-z3-4.8.7/examples/msf/Z3MSFPlugin.sln000066400000000000000000000215261356505360400175770ustar00rootroot00000000000000 Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2012 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SolverFoundation.Plugin.Z3", "SolverFoundation.Plugin.Z3\SolverFoundation.Plugin.Z3.csproj", "{7340E664-F648-4FF7-89B2-F4DA424996D3}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SolverFoundation.Plugin.Z3.Tests", "SolverFoundation.Plugin.Z3.Tests\SolverFoundation.Plugin.Z3.Tests.csproj", "{280AEE2F-1FDB-4A27-BE37-14DC154C873B}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Validator", "Validator\Validator.csproj", "{54835857-129F-44C9-B529-A42158647B36}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F1E99540-BA5E-46DF-9E29-6146A309CD18}" ProjectSection(SolutionItems) = preProject README = README EndProjectSection EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution commercial_64|Any CPU = commercial_64|Any CPU commercial_64|Mixed Platforms = commercial_64|Mixed Platforms commercial_64|x64 = commercial_64|x64 commercial_64|x86 = commercial_64|x86 commercial|Any CPU = commercial|Any CPU commercial|Mixed Platforms = commercial|Mixed Platforms commercial|x64 = commercial|x64 commercial|x86 = commercial|x86 Debug|Any CPU = Debug|Any CPU Debug|Mixed Platforms = Debug|Mixed Platforms Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU Release|Mixed Platforms = Release|Mixed Platforms Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {7340E664-F648-4FF7-89B2-F4DA424996D3}.commercial_64|Any CPU.ActiveCfg = commercial_64|Any CPU {7340E664-F648-4FF7-89B2-F4DA424996D3}.commercial_64|Any CPU.Build.0 = commercial_64|Any CPU {7340E664-F648-4FF7-89B2-F4DA424996D3}.commercial_64|Mixed Platforms.ActiveCfg = commercial_64|x86 {7340E664-F648-4FF7-89B2-F4DA424996D3}.commercial_64|Mixed Platforms.Build.0 = commercial_64|x86 {7340E664-F648-4FF7-89B2-F4DA424996D3}.commercial_64|x64.ActiveCfg = commercial_64|Any CPU {7340E664-F648-4FF7-89B2-F4DA424996D3}.commercial_64|x86.ActiveCfg = commercial_64|x86 {7340E664-F648-4FF7-89B2-F4DA424996D3}.commercial_64|x86.Build.0 = commercial_64|x86 {7340E664-F648-4FF7-89B2-F4DA424996D3}.commercial|Any CPU.ActiveCfg = commercial|Any CPU {7340E664-F648-4FF7-89B2-F4DA424996D3}.commercial|Any CPU.Build.0 = commercial|Any CPU {7340E664-F648-4FF7-89B2-F4DA424996D3}.commercial|Mixed Platforms.ActiveCfg = commercial|x86 {7340E664-F648-4FF7-89B2-F4DA424996D3}.commercial|Mixed Platforms.Build.0 = commercial|x86 {7340E664-F648-4FF7-89B2-F4DA424996D3}.commercial|x64.ActiveCfg = commercial|Any CPU {7340E664-F648-4FF7-89B2-F4DA424996D3}.commercial|x86.ActiveCfg = commercial|x86 {7340E664-F648-4FF7-89B2-F4DA424996D3}.commercial|x86.Build.0 = commercial|x86 {7340E664-F648-4FF7-89B2-F4DA424996D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7340E664-F648-4FF7-89B2-F4DA424996D3}.Debug|Any CPU.Build.0 = Debug|Any CPU {7340E664-F648-4FF7-89B2-F4DA424996D3}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 {7340E664-F648-4FF7-89B2-F4DA424996D3}.Debug|Mixed Platforms.Build.0 = Debug|x86 {7340E664-F648-4FF7-89B2-F4DA424996D3}.Debug|x64.ActiveCfg = Debug|Any CPU {7340E664-F648-4FF7-89B2-F4DA424996D3}.Debug|x86.ActiveCfg = Debug|x86 {7340E664-F648-4FF7-89B2-F4DA424996D3}.Debug|x86.Build.0 = Debug|x86 {7340E664-F648-4FF7-89B2-F4DA424996D3}.Release|Any CPU.ActiveCfg = Release|Any CPU {7340E664-F648-4FF7-89B2-F4DA424996D3}.Release|Any CPU.Build.0 = Release|Any CPU {7340E664-F648-4FF7-89B2-F4DA424996D3}.Release|Mixed Platforms.ActiveCfg = Release|x86 {7340E664-F648-4FF7-89B2-F4DA424996D3}.Release|Mixed Platforms.Build.0 = Release|x86 {7340E664-F648-4FF7-89B2-F4DA424996D3}.Release|x64.ActiveCfg = Release|Any CPU {7340E664-F648-4FF7-89B2-F4DA424996D3}.Release|x86.ActiveCfg = Release|x86 {7340E664-F648-4FF7-89B2-F4DA424996D3}.Release|x86.Build.0 = Release|x86 {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.commercial_64|Any CPU.ActiveCfg = Release|Any CPU {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.commercial_64|Any CPU.Build.0 = Release|Any CPU {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.commercial_64|Mixed Platforms.ActiveCfg = Release|Any CPU {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.commercial_64|Mixed Platforms.Build.0 = Release|Any CPU {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.commercial_64|x64.ActiveCfg = Release|Any CPU {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.commercial_64|x86.ActiveCfg = Release|Any CPU {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.commercial|Any CPU.ActiveCfg = Release|Any CPU {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.commercial|Any CPU.Build.0 = Release|Any CPU {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.commercial|Mixed Platforms.ActiveCfg = Release|Any CPU {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.commercial|Mixed Platforms.Build.0 = Release|Any CPU {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.commercial|x64.ActiveCfg = Release|Any CPU {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.commercial|x86.ActiveCfg = Release|Any CPU {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.Debug|Any CPU.Build.0 = Debug|Any CPU {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.Debug|x64.ActiveCfg = Debug|Any CPU {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.Debug|x86.ActiveCfg = Debug|Any CPU {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.Debug|x86.Build.0 = Debug|Any CPU {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.Release|Any CPU.ActiveCfg = Release|Any CPU {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.Release|Any CPU.Build.0 = Release|Any CPU {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.Release|Mixed Platforms.Build.0 = Release|Any CPU {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.Release|x64.ActiveCfg = Release|Any CPU {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.Release|x86.ActiveCfg = Release|Any CPU {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.Release|x86.Build.0 = Release|Any CPU {54835857-129F-44C9-B529-A42158647B36}.commercial_64|Any CPU.ActiveCfg = Release|Any CPU {54835857-129F-44C9-B529-A42158647B36}.commercial_64|Any CPU.Build.0 = Release|Any CPU {54835857-129F-44C9-B529-A42158647B36}.commercial_64|Mixed Platforms.ActiveCfg = Release|x86 {54835857-129F-44C9-B529-A42158647B36}.commercial_64|Mixed Platforms.Build.0 = Release|x86 {54835857-129F-44C9-B529-A42158647B36}.commercial_64|x64.ActiveCfg = Release|x64 {54835857-129F-44C9-B529-A42158647B36}.commercial_64|x64.Build.0 = Release|x64 {54835857-129F-44C9-B529-A42158647B36}.commercial_64|x86.ActiveCfg = Release|x86 {54835857-129F-44C9-B529-A42158647B36}.commercial_64|x86.Build.0 = Release|x86 {54835857-129F-44C9-B529-A42158647B36}.commercial|Any CPU.ActiveCfg = Release|Any CPU {54835857-129F-44C9-B529-A42158647B36}.commercial|Any CPU.Build.0 = Release|Any CPU {54835857-129F-44C9-B529-A42158647B36}.commercial|Mixed Platforms.ActiveCfg = Release|x86 {54835857-129F-44C9-B529-A42158647B36}.commercial|Mixed Platforms.Build.0 = Release|x86 {54835857-129F-44C9-B529-A42158647B36}.commercial|x64.ActiveCfg = Release|x64 {54835857-129F-44C9-B529-A42158647B36}.commercial|x64.Build.0 = Release|x64 {54835857-129F-44C9-B529-A42158647B36}.commercial|x86.ActiveCfg = Release|x86 {54835857-129F-44C9-B529-A42158647B36}.commercial|x86.Build.0 = Release|x86 {54835857-129F-44C9-B529-A42158647B36}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {54835857-129F-44C9-B529-A42158647B36}.Debug|Any CPU.Build.0 = Debug|Any CPU {54835857-129F-44C9-B529-A42158647B36}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 {54835857-129F-44C9-B529-A42158647B36}.Debug|Mixed Platforms.Build.0 = Debug|x86 {54835857-129F-44C9-B529-A42158647B36}.Debug|x64.ActiveCfg = Debug|x64 {54835857-129F-44C9-B529-A42158647B36}.Debug|x64.Build.0 = Debug|x64 {54835857-129F-44C9-B529-A42158647B36}.Debug|x86.ActiveCfg = Debug|x86 {54835857-129F-44C9-B529-A42158647B36}.Debug|x86.Build.0 = Debug|x86 {54835857-129F-44C9-B529-A42158647B36}.Release|Any CPU.ActiveCfg = Release|Any CPU {54835857-129F-44C9-B529-A42158647B36}.Release|Any CPU.Build.0 = Release|Any CPU {54835857-129F-44C9-B529-A42158647B36}.Release|Mixed Platforms.ActiveCfg = Release|x86 {54835857-129F-44C9-B529-A42158647B36}.Release|Mixed Platforms.Build.0 = Release|x86 {54835857-129F-44C9-B529-A42158647B36}.Release|x64.ActiveCfg = Release|x64 {54835857-129F-44C9-B529-A42158647B36}.Release|x64.Build.0 = Release|x64 {54835857-129F-44C9-B529-A42158647B36}.Release|x86.ActiveCfg = Release|x86 {54835857-129F-44C9-B529-A42158647B36}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal z3-z3-4.8.7/examples/python/000077500000000000000000000000001356505360400155665ustar00rootroot00000000000000z3-z3-4.8.7/examples/python/CMakeLists.txt000066400000000000000000000020561356505360400203310ustar00rootroot00000000000000set(python_example_files all_interval_series.py complex/complex.py example.py hamiltonian/hamiltonian.py mus/marco.py mus/mss.py socrates.py visitor.py ) set(z3py_bindings_build_dest "${PROJECT_BINARY_DIR}/python") set(build_z3_python_examples_target_depends "") foreach (example_file ${python_example_files}) add_custom_command(OUTPUT "${z3py_bindings_build_dest}/${example_file}" COMMAND "${CMAKE_COMMAND}" "-E" "copy" "${CMAKE_CURRENT_SOURCE_DIR}/${example_file}" # We flatten the hierarchy so that all python files have # the `z3` directory in their directory so that their import # statements "just work". "${z3py_bindings_build_dest}/" DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${example_file}" COMMENT "Copying \"${example_file}\" to ${z3py_bindings_build_dest}/${example_file}" ) list(APPEND build_z3_python_examples_target_depends "${z3py_bindings_build_dest}/${example_file}") endforeach() add_custom_target(build_z3_python_examples ALL DEPENDS ${build_z3_python_examples_target_depends} ) z3-z3-4.8.7/examples/python/README000066400000000000000000000002341356505360400164450ustar00rootroot00000000000000The example is copied to the build directory during configuration. You can execute it using python example.py in the build directory after you build Z3.z3-z3-4.8.7/examples/python/all_interval_series.py000066400000000000000000000045271356505360400221760ustar00rootroot00000000000000# Copyright Microsoft Research 2016 # The following script finds sequences of length n-1 of # integers 0,..,n-1 such that the difference of the n-1 # adjacent entries fall in the range 0,..,n-1 # This is known as the "The All-Interval Series Problem" # See http://www.csplib.org/Problems/prob007/ from __future__ import print_function from z3 import * import time set_option("sat.gc.burst", False) # disable GC at every search. It is wasteful for these small queries. def diff_at_j_is_i(xs, j, i): assert(0 <= j and j + 1 < len(xs)) assert(1 <= i and i < len(xs)) return Or([ And(xs[j][k], xs[j+1][k-i]) for k in range(i,len(xs))] + [ And(xs[j][k], xs[j+1][k+i]) for k in range(0,len(xs)-i)]) def ais(n): xij = [ [ Bool("x_%d_%d" % (i,j)) for j in range(n)] for i in range(n) ] s = SolverFor("QF_FD") # Optionally replace by (slower) default solver if using # more then just finite domains (Booleans, Bit-vectors, enumeration types # and bounded integers) # s = Solver() for i in range(n): s.add(AtMost(xij[i] + [1])) s.add(Or(xij[i])) for j in range(n): xi = [ xij[i][j] for i in range(n) ] s.add(AtMost(xi + [1])) s.add(Or(xi)) dji = [ [ diff_at_j_is_i(xij, j, i + 1) for i in range(n-1)] for j in range(n-1) ] for j in range(n-1): s.add(AtMost(dji[j] + [1])) s.add(Or(dji[j])) for i in range(n-1): dj = [dji[j][i] for j in range(n-1)] s.add(AtMost(dj + [1])) s.add(Or(dj)) return s, xij def process_model(s, xij, n): # x_ij integer i is at position j # d_ij difference between integer at position j, j+1 is i # sum_j d_ij = 1 i = 1,...,n-1 # sum_j x_ij = 1 # sum_i x_ij = 1 m = s.model() block = [] values = [] for i in range(n): k = -1 for j in range(n): if is_true(m.eval(xij[i][j])): assert(k == -1) block += [xij[i][j]] k = j values += [k] print(values) sys.stdout.flush() return block def all_models(n): count = 0 s, xij = ais(n) start = time.clock() while sat == s.check(): block = process_model(s, xij, n) s.add(Not(And(block))) count += 1 print(s.statistics()) print(time.clock() - start) print(count) set_option(verbose=1) all_models(12) z3-z3-4.8.7/examples/python/complex/000077500000000000000000000000001356505360400172355ustar00rootroot00000000000000z3-z3-4.8.7/examples/python/complex/complex.py000066400000000000000000000070141356505360400212600ustar00rootroot00000000000000############################################ # Copyright (c) 2012 Microsoft Corporation # # Complex numbers in Z3 # See http://research.microsoft.com/en-us/um/people/leonardo/blog/2013/01/26/complex.html # # Author: Leonardo de Moura (leonardo) ############################################ from __future__ import print_function import sys if sys.version_info.major >= 3: from functools import reduce from z3 import * def _to_complex(a): if isinstance(a, ComplexExpr): return a else: return ComplexExpr(a, RealVal(0)) def _is_zero(a): return (isinstance(a, int) and a == 0) or (is_rational_value(a) and a.numerator_as_long() == 0) class ComplexExpr: def __init__(self, r, i): self.r = r self.i = i def __add__(self, other): other = _to_complex(other) return ComplexExpr(self.r + other.r, self.i + other.i) def __radd__(self, other): other = _to_complex(other) return ComplexExpr(other.r + self.r, other.i + self.i) def __sub__(self, other): other = _to_complex(other) return ComplexExpr(self.r - other.r, self.i - other.i) def __rsub__(self, other): other = _to_complex(other) return ComplexExpr(other.r - self.r, other.i - self.i) def __mul__(self, other): other = _to_complex(other) return ComplexExpr(self.r*other.r - self.i*other.i, self.r*other.i + self.i*other.r) def __rmul__(self, other): other = _to_complex(other) return ComplexExpr(other.r*self.r - other.i*self.i, other.i*self.r + other.r*self.i) def __pow__(self, k): if k == 0: return ComplexExpr(1, 0) if k == 1: return self if k < 0: return (self ** (-k)).inv() return reduce(lambda x, y: x * y, [self for _ in range(k)], ComplexExpr(1, 0)) def inv(self): den = self.r*self.r + self.i*self.i return ComplexExpr(self.r/den, -self.i/den) def __div__(self, other): inv_other = _to_complex(other).inv() return self.__mul__(inv_other) if sys.version_info.major >= 3: # In python 3 the meaning of the '/' operator # was changed. def __truediv__(self, other): return self.__div__(other) def __rdiv__(self, other): other = _to_complex(other) return self.inv().__mul__(other) def __eq__(self, other): other = _to_complex(other) return And(self.r == other.r, self.i == other.i) def __neq__(self, other): return Not(self.__eq__(other)) def simplify(self): return ComplexExpr(simplify(self.r), simplify(self.i)) def repr_i(self): if is_rational_value(self.i): return "%s*I" % self.i else: return "(%s)*I" % str(self.i) def __repr__(self): if _is_zero(self.i): return str(self.r) elif _is_zero(self.r): return self.repr_i() else: return "%s + %s" % (self.r, self.repr_i()) def Complex(a): return ComplexExpr(Real('%s.r' % a), Real('%s.i' % a)) I = ComplexExpr(RealVal(0), RealVal(1)) def evaluate_cexpr(m, e): return ComplexExpr(m[e.r], m[e.i]) x = Complex("x") s = Tactic('qfnra-nlsat').solver() s.add(x*x == -2) print(s) print(s.check()) m = s.model() print('x = %s' % evaluate_cexpr(m, x)) print((evaluate_cexpr(m,x)*evaluate_cexpr(m,x)).simplify()) s.add(x.i != -1) print(s) print(s.check()) print(s.model()) s.add(x.i != 1) print(s.check()) # print(s.model()) print(((3 + I) ** 2)/(5 - I)) print(((3 + I) ** -3)/(5 - I)) z3-z3-4.8.7/examples/python/data/000077500000000000000000000000001356505360400164775ustar00rootroot00000000000000z3-z3-4.8.7/examples/python/data/horn1.smt2000066400000000000000000000031161356505360400203360ustar00rootroot00000000000000(declare-rel Goal (Bool Bool Bool Bool Bool Bool Bool Bool Bool Bool Bool Bool)) (declare-rel Invariant (Bool Bool Bool Bool Bool Bool Bool Bool Bool Bool Bool Bool)) (declare-var A Bool) (declare-var B Bool) (declare-var C Bool) (declare-var D Bool) (declare-var E Bool) (declare-var F Bool) (declare-var G Bool) (declare-var H Bool) (declare-var I Bool) (declare-var J Bool) (declare-var K Bool) (declare-var L Bool) (declare-var M Bool) (declare-var N Bool) (declare-var O Bool) (declare-var P Bool) (declare-var Q Bool) (declare-var R Bool) (declare-var S Bool) (declare-var T Bool) (declare-var U Bool) (declare-var V Bool) (declare-var W Bool) (declare-var X Bool) (rule (=> (not (or L K J I H G F E D C B A)) (Invariant L K J I H G F E D C B A))) (rule (let ((a!1 (and (Invariant X W V U T S R Q P O N M) (=> (not (and true)) (not F)) (=> (not (and true)) (not E)) (=> (not (and W)) (not D)) (=> (not (and W)) (not C)) (=> (not (and U)) (not B)) (=> (not (and U)) (not A)) (= L (xor F X)) (= K (xor E W)) (= J (xor D V)) (= I (xor C U)) (= H (xor B T)) (= G (xor A S)) (=> D (not E)) (=> C (not E)) (=> B (not C)) (=> A (not C)) ((_ at-most 5) L K J I H G)))) (=> a!1 (Invariant L K J I H G F E D C B A)))) (rule (=> (and (Invariant L K J I H G F E D C B A) L (not K) J (not I) H G) (Goal L K J I H G F E D C B A))) (query Goal)z3-z3-4.8.7/examples/python/data/horn2.smt2000066400000000000000000000026111356505360400203360ustar00rootroot00000000000000(declare-rel Invariant (Bool Bool Bool Bool Bool Bool Bool Bool Bool Bool)) (declare-rel Goal (Bool Bool Bool Bool Bool Bool Bool Bool Bool Bool)) (declare-var A Bool) (declare-var B Bool) (declare-var C Bool) (declare-var D Bool) (declare-var E Bool) (declare-var F Bool) (declare-var G Bool) (declare-var H Bool) (declare-var I Bool) (declare-var J Bool) (declare-var K Bool) (declare-var L Bool) (declare-var M Bool) (declare-var N Bool) (declare-var O Bool) (declare-var P Bool) (declare-var Q Bool) (declare-var R Bool) (declare-var S Bool) (declare-var T Bool) (rule (=> (not (or J I H G F E D C B A)) (Invariant J I H G F E D C B A))) (rule (let ((a!1 (and (Invariant T S R Q P O N M L K) (=> (not (and true)) (not E)) (=> (not (and T)) (not D)) (=> (not (and S)) (not C)) (=> (not (and R)) (not B)) (=> (not (and Q)) (not A)) (= J (xor E T)) (= I (xor D S)) (= H (xor C R)) (= G (xor B Q)) (= F (xor A P)) (=> D (not E)) (=> C (not D)) (=> B (not C)) (=> A (not B)) ((_ at-most 3) J I H G F)))) (=> a!1 (Invariant J I H G F E D C B A)))) (rule (=> (and (Invariant J I H G F E D C B A) (not J) (not I) (not H) (not G) F) (Goal J I H G F E D C B A))) (query Goal) z3-z3-4.8.7/examples/python/data/horn3.smt2000066400000000000000000000006471356505360400203460ustar00rootroot00000000000000(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) z3-z3-4.8.7/examples/python/data/horn4.smt2000066400000000000000000000052641356505360400203470ustar00rootroot00000000000000(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) z3-z3-4.8.7/examples/python/data/horn5.smt2000066400000000000000000000010751356505360400203440ustar00rootroot00000000000000(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) z3-z3-4.8.7/examples/python/data/horn6.smt2000066400000000000000000000211211356505360400203370ustar00rootroot00000000000000(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) z3-z3-4.8.7/examples/python/example.py000066400000000000000000000020141356505360400175700ustar00rootroot00000000000000# Copyright (c) Microsoft Corporation 2015, 2016 # The Z3 Python API requires libz3.dll/.so/.dylib in the # PATH/LD_LIBRARY_PATH/DYLD_LIBRARY_PATH # 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). # If you obtained example.py as part of our binary release zip files, # which you unzipped into a directory called `MYZ3', then follow these # instructions to run the example: # Running this example on Windows: # set PATH=%PATH%;MYZ3\bin # set PYTHONPATH=MYZ3\bin\python # python example.py # Running this example on Linux: # export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:MYZ3/bin # export PYTHONPATH=MYZ3/bin/python # python example.py # Running this example on macOS: # export DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH:MYZ3/bin # export PYTHONPATH=MYZ3/bin/python # python example.py from z3 import * x = Real('x') y = Real('y') s = Solver() s.add(x + y > 5, x > 1, y > 1) print(s.check()) print(s.model()) z3-z3-4.8.7/examples/python/hamiltonian/000077500000000000000000000000001356505360400200715ustar00rootroot00000000000000z3-z3-4.8.7/examples/python/hamiltonian/hamiltonian.py000066400000000000000000000055751356505360400227620ustar00rootroot00000000000000############################################ # Copyright (c) Microsoft Corporation. All Rights Reserved. # # Check if the given graph has a Hamiltonian cycle. # # Author: Ganesh Gopalakrishnan ganesh@cs.utah.edu ############################################ from z3 import * def gencon(gr): """ Input a graph as an adjacency list, e.g. {0:[1,2], 1:[2], 2:[1,0]}. Produces solver to check if the given graph has a Hamiltonian cycle. Query the solver using s.check() and if sat, then s.model() spells out the cycle. Two example graphs from http://en.wikipedia.org/wiki/Hamiltonian_path are tested. ======================================================= Explanation: Generate a list of Int vars. Constrain the first Int var ("Node 0") to be 0. Pick a node i, and attempt to number all nodes reachable from i to have a number one higher (mod L) than assigned to node i (use an Or constraint). ======================================================= """ L = len(gr) cv = [Int('cv%s'%i) for i in range(L)] s = Solver() s.add(cv[0]==0) for i in range(L): s.add(Or([cv[j]==(cv[i]+1)%L for j in gr[i]])) return s def examples(): # Example Graphs: The Dodecahedral graph from http://en.wikipedia.org/wiki/Hamiltonian_path grdodec = { 0: [1, 4, 5], 1: [0, 7, 2], 2: [1, 9, 3], 3: [2, 11, 4], 4: [3, 13, 0], 5: [0, 14, 6], 6: [5, 16, 7], 7: [6, 8, 1], 8: [7, 17, 9], 9: [8, 10, 2], 10: [9, 18, 11], 11: [10, 3, 12], 12: [11, 19, 13], 13: [12, 14, 4], 14: [13, 15, 5], 15: [14, 16, 19], 16: [6, 17, 15], 17: [16, 8, 18], 18: [10, 19, 17], 19: [18, 12, 15] } import pprint pp = pprint.PrettyPrinter(indent=4) pp.pprint(grdodec) sdodec=gencon(grdodec) print(sdodec.check()) print(sdodec.model()) # ======================================================= # See http://en.wikipedia.org/wiki/Hamiltonian_path for the Herschel graph # being the smallest possible polyhedral graph that does not have a Hamiltonian # cycle. # grherschel = { 0: [1, 9, 10, 7], 1: [0, 8, 2], 2: [1, 9, 3], 3: [2, 8, 4], 4: [3, 9, 10, 5], 5: [4, 8, 6], 6: [5, 10, 7], 7: [6, 8, 0], 8: [1, 3, 5, 7], 9: [2, 0, 4], 10: [6, 4, 0] } pp.pprint(grherschel) sherschel=gencon(grherschel) print(sherschel.check()) # ======================================================= if __name__ == "__main__": examples() z3-z3-4.8.7/examples/python/mini_ic3.py000066400000000000000000000271001356505360400176320ustar00rootroot00000000000000from z3 import * import heapq # 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) class Goal: def __init__(self, cube, parent, level): self.level = level self.cube = cube self.parent = parent 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 class MiniIC3: 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) 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)) 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) # Check if there are two states next to each other that have the same clauses. def is_valid(self): i = 1 while i + 1 < len(self.states): if not (self.states[i].R - self.states[i+1].R): return And(prune(self.states[i].R)) i += 1 return None 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) # Determine if there is a cube for the current state # that is potentially reachable. def unfold(self): core = [] self.s_bad.push() R = self.R(len(self.states)-1) self.s_bad.add(R) 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() core = [c for c in core if c in set(cube)] self.s_good.pop() self.s_bad.pop() return is_sat, core # 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 0 until i def assert_clause(self, i, clause): for j in range(i + 1): self.states[j].add(clause) # minimize cube that is core of Dual solver. # this assumes that props & cube => Trans 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 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)) # A state s0 and level f0 such that # not(s0) is f0-1 inductive def ic3_blocked(self, s0, f0): self.push_heap(Goal(self.next(s0), None, f0)) while self.goals: f, g = heapq.heappop(self.goals) sys.stdout.write("%d." % f) sys.stdout.flush() # Not(g.cube) is f-1 invariant if f == 0: print("") return g cube, f, is_sat = self.is_inductive(f, g.cube) if is_sat == unsat: self.block_cube(f, self.prev(cube)) if f < f0: self.push_heap(Goal(g.cube, g.parent, f + 1)) elif is_sat == sat: self.push_heap(Goal(cube, g, f - 1)) self.push_heap(g) else: return is_sat print("") return None # 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 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 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) return cube, f, is_sat 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.ic3_blocked(cube, level) if cex is not None: return cex else: return is_sat def test(file): h2t = Horn2Transitions() h2t.parse(file) mp = MiniIC3(h2t.init, h2t.trans, h2t.goal, h2t.xs, h2t.inputs, h2t.xns) result = mp.run() if isinstance(result, Goal): g = result print("Trace") while g: print(g.level, g.cube) g = g.parent return if isinstance(result, ExprRef): print("Invariant:\n%s " % result) return print(result) test("data/horn1.smt2") test("data/horn2.smt2") test("data/horn3.smt2") test("data/horn4.smt2") test("data/horn5.smt2") # test("data/horn6.smt2") # takes long time to finish z3-z3-4.8.7/examples/python/mini_quip.py000066400000000000000000000617501356505360400201430ustar00rootroot00000000000000from 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 z3-z3-4.8.7/examples/python/mus/000077500000000000000000000000001356505360400163725ustar00rootroot00000000000000z3-z3-4.8.7/examples/python/mus/marco.py000066400000000000000000000150571356505360400200550ustar00rootroot00000000000000############################################ # Copyright (c) 2016 Microsoft Corporation # # Basic core and correction set enumeration. # # Author: Nikolaj Bjorner (nbjorner) ############################################ """ Enumeration of Minimal Unsatisfiable Cores and Maximal Satisfying Subsets This tutorial illustrates how to use Z3 for extracting all minimal unsatisfiable cores together with all maximal satisfying subsets. Origin The algorithm that we describe next represents the essence of the core extraction procedure by Liffiton and Malik and independently by Previti and Marques-Silva: Enumerating Infeasibility: Finding Multiple MUSes Quickly Mark H. Liffiton and Ammar Malik in Proc. 10th International Conference on Integration of Artificial Intelligence (AI) and Operations Research (OR) techniques in Constraint Programming (CPAIOR-2013), 160-175, May 2013. Partial MUS Enumeration Alessandro Previti, Joao Marques-Silva in Proc. AAAI-2013 July 2013 Z3py Features This implementation contains no tuning. It was contributed by Mark Liffiton and it is a simplification of one of the versions available from his Marco Polo Web site. It illustrates the following features of Z3's Python-based API: 1. Using assumptions to track unsatisfiable cores. 2. Using multiple solvers and passing constraints between them. 3. Calling the C-based API from Python. Not all API functions are supported over the Python wrappers. This example shows how to get a unique integer identifier of an AST, which can be used as a key in a hash-table. Idea of the Algorithm The main idea of the algorithm is to maintain two logical contexts and exchange information between them: 1. The MapSolver is used to enumerate sets of clauses that are not already supersets of an existing unsatisfiable core and not already a subset of a maximal satisfying assignment. The MapSolver uses one unique atomic predicate per soft clause, so it enumerates sets of atomic predicates. For each minimal unsatisfiable core, say, represented by predicates p1, p2, p5, the MapSolver contains the clause !p1 | !p2 | !p5. For each maximal satisfiable subset, say, represented by predicats p2, p3, p5, the MapSolver contains a clause corresponding to the disjunction of all literals not in the maximal satisfiable subset, p1 | p4 | p6. 2. The SubsetSolver contains a set of soft clauses (clauses with the unique indicator atom occurring negated). The MapSolver feeds it a set of clauses (the indicator atoms). Recall that these are not already a superset of an existing minimal unsatisfiable core, or a subset of a maximal satisfying assignment. If asserting these atoms makes the SubsetSolver context infeasible, then it finds a minimal unsatisfiable subset corresponding to these atoms. If asserting the atoms is consistent with the SubsetSolver, then it extends this set of atoms maximally to a satisfying set. """ from z3 import * def main(): x, y = Reals('x y') constraints = [x > 2, x < 1, x < 0, Or(x + y > 0, y < 0), Or(y >= 0, x >= 0), Or(y < 0, x < 0), Or(y > 0, x < 0)] csolver = SubsetSolver(constraints) msolver = MapSolver(n=csolver.n) for orig, lits in enumerate_sets(csolver, msolver): output = "%s %s" % (orig, lits) print(output) def get_id(x): return Z3_get_ast_id(x.ctx.ref(),x.as_ast()) class SubsetSolver: constraints = [] n = 0 s = Solver() varcache = {} idcache = {} def __init__(self, constraints): self.constraints = constraints self.n = len(constraints) for i in range(self.n): self.s.add(Implies(self.c_var(i), constraints[i])) def c_var(self, i): if i not in self.varcache: v = Bool(str(self.constraints[abs(i)])) self.idcache[get_id(v)] = abs(i) if i >= 0: self.varcache[i] = v else: self.varcache[i] = Not(v) return self.varcache[i] def check_subset(self, seed): assumptions = self.to_c_lits(seed) return (self.s.check(assumptions) == sat) def to_c_lits(self, seed): return [self.c_var(i) for i in seed] def complement(self, aset): return set(range(self.n)).difference(aset) def seed_from_core(self): core = self.s.unsat_core() return [self.idcache[get_id(x)] for x in core] def shrink(self, seed): current = set(seed) for i in seed: if i not in current: continue current.remove(i) if not self.check_subset(current): current = set(self.seed_from_core()) else: current.add(i) return current def grow(self, seed): current = seed for i in self.complement(current): current.append(i) if not self.check_subset(current): current.pop() return current class MapSolver: def __init__(self, n): """Initialization. Args: n: The number of constraints to map. """ self.solver = Solver() self.n = n self.all_n = set(range(n)) # used in complement fairly frequently def next_seed(self): """Get the seed from the current model, if there is one. Returns: A seed as an array of 0-based constraint indexes. """ if self.solver.check() == unsat: return None seed = self.all_n.copy() # default to all True for "high bias" model = self.solver.model() for x in model: if is_false(model[x]): seed.remove(int(x.name())) return list(seed) def complement(self, aset): """Return the complement of a given set w.r.t. the set of mapped constraints.""" return self.all_n.difference(aset) def block_down(self, frompoint): """Block down from a given set.""" comp = self.complement(frompoint) self.solver.add( Or( [Bool(str(i)) for i in comp] ) ) def block_up(self, frompoint): """Block up from a given set.""" self.solver.add( Or( [Not(Bool(str(i))) for i in frompoint] ) ) def enumerate_sets(csolver, map): """Basic MUS/MCS enumeration, as a simple example.""" while True: seed = map.next_seed() if seed is None: return if csolver.check_subset(seed): MSS = csolver.grow(seed) yield ("MSS", csolver.to_c_lits(MSS)) map.block_down(MSS) else: MUS = csolver.shrink(seed) yield ("MUS", csolver.to_c_lits(MUS)) map.block_up(MUS) main() z3-z3-4.8.7/examples/python/mus/mss.py000066400000000000000000000122141356505360400175460ustar00rootroot00000000000000############################################ # Copyright (c) 2016 Microsoft Corporation # # MSS enumeration based on maximal resolution. # # Author: Nikolaj Bjorner (nbjorner) ############################################ """ The following is a procedure for enumerating maximal satisfying subsets. It uses maximal resolution to eliminate cores from the state space. Whenever the hard constraints are satisfiable, it finds a model that satisfies the maximal number of soft constraints. During this process it collects the set of cores that are encountered. It then reduces the set of soft constraints using max-resolution in the style of [Narodytska & Bacchus, AAAI'14]. In other words, let F1, ..., F_k be a core among the soft constraints F1,...,F_n Replace F1,.., F_k by F1 or F2, F3 or (F2 & F1), F4 or (F3 & (F2 & F1)), ..., F_k or (F_{k-1} & (...)) Optionally, add the core ~F1 or ... or ~F_k to F The current model M satisfies the new set F, F1,...,F_{n-1} if the core is minimal. Whenever we modify the soft constraints by the core reduction any assignment to the reduced set satisfies a k-1 of the original soft constraints. """ from z3 import * def main(): x, y = Reals('x y') soft_constraints = [x > 2, x < 1, x < 0, Or(x + y > 0, y < 0), Or(y >= 0, x >= 0), Or(y < 0, x < 0), Or(y > 0, x < 0)] hard_constraints = BoolVal(True) solver = MSSSolver(hard_constraints, soft_constraints) for lits in enumerate_sets(solver): print("%s" % lits) def enumerate_sets(solver): while True: if sat == solver.s.check(): MSS = solver.grow() yield MSS else: break class MSSSolver: s = Solver() varcache = {} idcache = {} def __init__(self, hard, soft): self.n = len(soft) self.soft = soft self.s.add(hard) self.soft_vars = set([self.c_var(i) for i in range(self.n)]) self.orig_soft_vars = set([self.c_var(i) for i in range(self.n)]) self.s.add([(self.c_var(i) == soft[i]) for i in range(self.n)]) def c_var(self, i): if i not in self.varcache: v = Bool(str(self.soft[abs(i)])) self.idcache[v] = abs(i) if i >= 0: self.varcache[i] = v else: self.varcache[i] = Not(v) return self.varcache[i] # Retrieve the latest model # Add formulas that are true in the model to # the current mss def update_unknown(self): self.model = self.s.model() new_unknown = set([]) for x in self.unknown: if is_true(self.model[x]): self.mss.append(x) else: new_unknown.add(x) self.unknown = new_unknown # Create a name, propositional atom, # for formula 'fml' and return the name. def add_def(self, fml): name = Bool("%s" % fml) self.s.add(name == fml) return name # replace Fs := f0, f1, f2, .. by # Or(f1, f0), Or(f2, And(f1, f0)), Or(f3, And(f2, And(f1, f0))), ... def relax_core(self, Fs): assert(Fs <= self.soft_vars) prefix = BoolVal(True) self.soft_vars -= Fs Fs = [ f for f in Fs ] for i in range(len(Fs)-1): prefix = self.add_def(And(Fs[i], prefix)) self.soft_vars.add(self.add_def(Or(prefix, Fs[i+1]))) # Resolve literals from the core that # are 'explained', e.g., implied by # other literals. def resolve_core(self, core): new_core = set([]) for x in core: if x in self.mcs_explain: new_core |= self.mcs_explain[x] else: new_core.add(x) return new_core # Given a current satisfiable state # Extract an MSS, and ensure that currently # encountered cores are avoided in next iterations # by weakening the set of literals that are # examined in next iterations. # Strengthen the solver state by enforcing that # an element from the MCS is encountered. def grow(self): self.mss = [] self.mcs = [] self.nmcs = [] self.mcs_explain = {} self.unknown = self.soft_vars self.update_unknown() cores = [] while len(self.unknown) > 0: x = self.unknown.pop() is_sat = self.s.check(self.mss + [x] + self.nmcs) if is_sat == sat: self.mss.append(x) self.update_unknown() elif is_sat == unsat: core = self.s.unsat_core() core = self.resolve_core(core) self.mcs_explain[Not(x)] = {y for y in core if not eq(x,y)} self.mcs.append(x) self.nmcs.append(Not(x)) cores += [core] else: print("solver returned %s" % is_sat) exit() mss = [x for x in self.orig_soft_vars if is_true(self.model[x])] mcs = [x for x in self.orig_soft_vars if not is_true(self.model[x])] self.s.add(Or(mcs)) core_literals = set([]) cores.sort(key=lambda element: len(element)) for core in cores: if len(core & core_literals) == 0: self.relax_core(core) core_literals |= core return mss main() z3-z3-4.8.7/examples/python/parallel.py000066400000000000000000000015211356505360400177330ustar00rootroot00000000000000from z3 import * from multiprocessing.pool import ThreadPool from copy import deepcopy pool = ThreadPool(8) x = Int('x') assert x.ctx == main_ctx() def calculate(x, n, ctx): """ Do a simple computation with a context""" assert x.ctx == ctx assert x.ctx != main_ctx() # Parallel creation of z3 object condition = And(x < 2, x > n, ctx) # Parallel solving solver = Solver(ctx=ctx) solver.add(condition) solver.check() for i in range(100): # Create new context for the computation # Note that we need to do this sequentially, as parallel access to the current context or its objects # will result in a segfault i_context = Context() x_i = deepcopy(x).translate(i_context) # Kick off parallel computation pool.apply_async(calculate, [x_i, i, i_context]) pool.close() pool.join() z3-z3-4.8.7/examples/python/rc2.py000066400000000000000000000115071356505360400166320ustar00rootroot00000000000000# RC2 algorithm # basic version with some optimizations # - process soft constraints in order of highest values first. # - extract multiple cores, not just one # - use built-in cardinality constraints, cheap core minimization. # # See also https://github.com/pysathq/pysat and papers in CP 2014, JSAT 2015. from z3 import * from z3 import * def tt(s, f): return is_true(s.model().eval(f)) def add(Ws, f, w): Ws[f] = w + (Ws[f] if f in Ws else 0) def sub(Ws, f, w): w1 = Ws[f] if w1 > w: Ws[f] = w1 - w else: del(Ws[f]) class RC2: def __init__(self, s): self.bounds = {} self.names = {} self.solver = s self.solver.set("sat.cardinality.solver", True) self.solver.set("sat.core.minimize", True) self.solver.set("sat.core.minimize_partial", True) def at_most(self, S, k): fml = simplify(AtMost(S + [k])) if fml in self.names: return self.names[fml] name = Bool("%s" % fml) self.solver.add(Implies(name, fml)) self.bounds[name] = (S, k) self.names[fml] = name return name def print_cost(self): print("cost [", self.min_cost, ":", self.max_cost, "]") def update_max_cost(self): self.max_cost = min(self.max_cost, self.get_cost()) self.print_cost() # sort W, and incrementally add elements of W # in sorted order to prefer cores with high weight. def check(self, Ws): def compare(fw): f, w = fw return -w ws = sorted([(k,Ws[k]) for k in Ws], key = compare) i = 0 while i < len(ws): j = i # increment j until making 5% progress or exhausting equal weight entries while (j < len(ws) and ws[j][1] == ws[i][1]) or (i > 0 and (j - i)*20 < len(ws)): j += 1 i = j r = self.solver.check([ws[j][0] for j in range(i)]) if r == sat: self.update_max_cost() else: return r return sat def get_cost(self): return sum(self.Ws0[c] for c in self.Ws0 if not tt(self.solver, c)) # Retrieve independent cores from Ws def get_cores(self, Ws): cores = [] while unsat == self.check(Ws): core = list(self.solver.unsat_core()) print (self.solver.statistics()) if not core: return unsat w = min([Ws[c] for c in core]) for f in core: sub(Ws, f, w) cores += [(core, w)] self.update_max_cost() return cores # Add new soft constraints to replace core # with weight w. Allow to weaken at most # one element of core. Elements that are # cardinality constraints are weakened by # increasing their bounds. Non-cardinality # constraints are weakened to "true". They # correspond to the constraint Not(s) <= 0, # so weakening produces Not(s) <= 1, which # is a tautology. def update_bounds(self, Ws, core, w): for f in core: if f in self.bounds: S, k = self.bounds[f] if k + 1 < len(S): add(Ws, self.at_most(S, k + 1), w) add(Ws, self.at_most([mk_not(f) for f in core], 1), w) # Ws are weighted soft constraints # Whenever there is an unsatisfiable core over ws # increase the limit of each soft constraint from a bound # and create a soft constraint that limits the number of # increased bounds to be at most one. def maxsat(self, Ws): self.min_cost = 0 self.max_cost = sum(Ws[c] for c in Ws) self.Ws0 = Ws.copy() while True: cores = self.get_cores(Ws) if not cores: break if cores == unsat: return unsat for (core, w) in cores: self.min_cost += w self.print_cost() self.update_bounds(Ws, core, w) return self.min_cost, { f for f in self.Ws0 if not tt(self.solver, f) } def from_file(self, file): opt = Optimize() opt.from_file(file) self.solver.add(opt.assertions()) obj = opt.objectives()[0] Ws = {} for f in obj.children(): assert(f.arg(1).as_long() == 0) add(Ws, f.arg(0), f.arg(2).as_long()) return self.maxsat(Ws) def from_formulas(self, hard, soft): self.solver.add(hard) Ws = {} for f, cost in soft: add(Ws, f, cost) return self.maxsat(Ws) def main(file): s = SolverFor("QF_FD") rc2 = RC2(s) set_param(verbose=0) cost, falses = rc2.from_file(file) print(cost) print(s.statistics()) if len(sys.argv) > 1: main(sys.argv[1]) # main() z3-z3-4.8.7/examples/python/socrates.py000066400000000000000000000014311356505360400177620ustar00rootroot00000000000000############################################ # Copyright (c) Microsoft Corporation. All Rights Reserved. # # all humans are mortal # Socrates is a human # so Socrates mortal ############################################ from z3 import * Object = DeclareSort('Object') Human = Function('Human', Object, BoolSort()) Mortal = Function('Mortal', Object, BoolSort()) # a well known philosopher socrates = Const('socrates', Object) # free variables used in forall must be declared Const in python x = Const('x', Object) axioms = [ForAll([x], Implies(Human(x), Mortal(x))), Human(socrates)] s = Solver() s.add(axioms) print(s.check()) # prints sat so axioms are coherent # classical refutation s.add(Not(Mortal(socrates))) print(s.check()) # prints unsat so socrates is Mortal z3-z3-4.8.7/examples/python/trafficjam.py000066400000000000000000000065521356505360400202560ustar00rootroot00000000000000from z3 import * class Car(): def __init__(self, is_vertical, base_pos, length, start, color): self.is_vertical = is_vertical self.base = base_pos self.length = length self.start = start self.color = color def __eq__(self, other): return self.color == other.color def __ne__(self, other): return self.color != other.color dimension = 6 red_car = Car(False, 2, 2, 3, "red") cars = [ Car(True, 0, 3, 0, 'yellow'), Car(False, 3, 3, 0, 'blue'), Car(False, 5, 2, 0, "brown"), Car(False, 0, 2, 1, "lgreen"), Car(True, 1, 2, 1, "light blue"), Car(True, 2, 2, 1, "pink"), Car(True, 2, 2, 4, "dark green"), red_car, Car(True, 3, 2, 3, "purple"), Car(False, 5, 2, 3, "light yellow"), Car(True, 4, 2, 0, "orange"), Car(False, 4, 2, 4, "black"), Car(True, 5, 3, 1, "light purple") ] num_cars = len(cars) B = BoolSort() bv3 = BitVecSort(3) state = Function('state', [ bv3 for c in cars] + [B]) def num(i): return BitVecVal(i,bv3) def bound(i): return Const(cars[i].color, bv3) fp = Fixedpoint() fp.set("fp.engine","datalog") fp.set("datalog.generate_explanations",True) fp.declare_var([bound(i) for i in range(num_cars)]) fp.register_relation(state) def mk_state(car, value): return state([ (num(value) if (cars[i] == car) else bound(i)) for i in range(num_cars)]) def mk_transition(row, col, i0, j, car0): body = [mk_state(car0, i0)] for index in range(num_cars): car = cars[index] if car0 != car: if car.is_vertical and car.base == col: for i in range(dimension): if i <= row and row < i + car.length and i + car.length <= dimension: body += [bound(index) != num(i)] if car.base == row and not car.is_vertical: for i in range(dimension): if i <= col and col < i + car.length and i + car.length <= dimension: body += [bound(index) != num(i)] s = "%s %d->%d" % (car0.color, i0, j) fp.rule(mk_state(car0, j), body, s) def move_down(i, car): free_row = i + car.length if free_row < dimension: mk_transition(free_row, car.base, i, i + 1, car) def move_up(i, car): free_row = i - 1 if 0 <= free_row and i + car.length <= dimension: mk_transition(free_row, car.base, i, i - 1, car) def move_left(i, car): free_col = i - 1; if 0 <= free_col and i + car.length <= dimension: mk_transition(car.base, free_col, i, i - 1, car) def move_right(i, car): free_col = car.length + i if free_col < dimension: mk_transition(car.base, free_col, i, i + 1, car) # Initial state: fp.fact(state([num(cars[i].start) for i in range(num_cars)])) # Transitions: for car in cars: for i in range(dimension): if car.is_vertical: move_down(i, car) move_up(i, car) else: move_left(i, car) move_right(i, car) def get_instructions(ans): lastAnd = ans.arg(0).children()[-1] trace = lastAnd.children()[1] while trace.num_args() > 0: print(trace.decl()) trace = trace.children()[-1] goal = state([ (num(4) if cars[i] == red_car else bound(i)) for i in range(num_cars)]) fp.query(goal) get_instructions(fp.get_answer()) del goal del state del fp z3-z3-4.8.7/examples/python/tutorial/000077500000000000000000000000001356505360400174315ustar00rootroot00000000000000z3-z3-4.8.7/examples/python/tutorial/html/000077500000000000000000000000001356505360400203755ustar00rootroot00000000000000z3-z3-4.8.7/examples/python/tutorial/html/advanced-examples.htm000066400000000000000000002466531356505360400245100ustar00rootroot00000000000000 Z3Py Advanced

Advanced Topics

Please send feedback, comments and/or corrections to leonardo@microsoft.com. Your comments are very valuable.

Expressions, Sorts and Declarations

In Z3, expressions, sorts and declarations are called ASTs. ASTs are directed acyclic graphs. Every expression has a sort (aka type). The method sort() retrieves the sort of an expression.

x = Int('x')
y = Real('y')
print (x + 1).sort()
print (y + 1).sort()
print (x >= 2).sort()

The function eq(n1, n2) returns True if n1 and n2 are the same AST. This is a structural test.

x, y = Ints('x y')
print eq(x + y, x + y)
print eq(x + y, y + x)
n = x + y
print eq(n, x + y)
# x2 is eq to x
x2 = Int('x') 
print eq(x, x2)
# the integer variable x is not equal to 
# the real variable x
print eq(Int('x'), Real('x'))

The method hash() returns a hashcode for an AST node. If eq(n1, n2) returns True, then n1.hash() is equal to n2.hash().

x = Int('x')
print (x + 1).hash()
print (1 + x).hash()
print x.sort().hash()

Z3 expressions can be divided in three basic groups: applications, quantifiers and bounded/free variables. Applications are all you need if your problems do not contain universal/existential quantifiers. Although we say Int('x') is an integer "variable", it is technically an integer constant, and internally is represented as a function application with 0 arguments. Every application is associated with a declaration and contains 0 or more arguments. The method decl() returns the declaration associated with an application. The method num_args() returns the number of arguments of an application, and arg(i) one of the arguments. The function is_expr(n) returns True if n is an expression. Similarly is_app(n) (is_func_decl(n)) returns True if n is an application (declaration).

x = Int('x')
print "is expression: ", is_expr(x)
n = x + 1
print "is application:", is_app(n)
print "decl:          ", n.decl()
print "num args:      ", n.num_args()
for i in range(n.num_args()):
    print "arg(", i, ") ->", n.arg(i)

Declarations have names, they are retrieved using the method name(). A (function) declaration has an arity, a domain and range sorts.

x   = Int('x')
x_d = x.decl()
print "is_expr(x_d):     ", is_expr(x_d)
print "is_func_decl(x_d):", is_func_decl(x_d)
print "x_d.name():       ", x_d.name()
print "x_d.range():      ", x_d.range()
print "x_d.arity():      ", x_d.arity()
# x_d() creates an application with 0 arguments using x_d.
print "eq(x_d(), x):     ", eq(x_d(), x)
print "\n"
# f is a function from (Int, Real) to Bool
f   = Function('f', IntSort(), RealSort(), BoolSort())
print "f.name():         ", f.name()
print "f.range():        ", f.range()
print "f.arity():        ", f.arity()
for i in range(f.arity()):
    print "domain(", i, "): ", f.domain(i)
# f(x, x) creates an application with 2 arguments using f.
print f(x, x)
print eq(f(x, x).decl(), f)

The built-in declarations are identified using their kind. The kind is retrieved using the method kind(). The complete list of built-in declarations can be found in the file z3consts.py (z3_api.h) in the Z3 distribution.

x, y = Ints('x y')
print (x + y).decl().kind() == Z3_OP_ADD
print (x + y).decl().kind() == Z3_OP_SUB

The following example demonstrates how to substitute sub-expressions in Z3 expressions.

x, y = Ints('x y')
f    = Function('f', IntSort(), IntSort(), IntSort())
g    = Function('g', IntSort(), IntSort())
n    = f(f(g(x), g(g(x))), g(g(y)))
print n
# substitute g(g(x)) with y and g(y) with x + 1
print substitute(n, (g(g(x)), y), (g(y), x + 1))

The function Const(name, sort) declares a constant (aka variable) of the given sort. For example, the functions Int(name) and Real(name) are shorthands for Const(name, IntSort()) and Const(name, RealSort()).

x = Const('x', IntSort())
print eq(x, Int('x'))

a, b = Consts('a b', BoolSort())
print And(a, b)

Arrays

As part of formulating a programme of a mathematical theory of computation McCarthy proposed a basic theory of arrays as characterized by the select-store axioms. The expression Select(a, i) returns the value stored at position i of the array a; and Store(a, i, v) returns a new array identical to a, but on position i it contains the value v. In Z3Py, we can also write Select(a, i) as a[i].

# Use I as an alias for IntSort()
I = IntSort()
# A is an array from integer to integer
A = Array('A', I, I)
x = Int('x')
print A[x]
print Select(A, x)
print Store(A, x, 10)
print simplify(Select(Store(A, 2, x+1), 2))

By default, Z3 assumes that arrays are extensional over select. In other words, Z3 also enforces that if two arrays agree on all positions, then the arrays are equal.

Z3 also contains various extensions for operations on arrays that remain decidable and amenable to efficient saturation procedures (here efficient means, with an NP-complete satisfiability complexity). We describe these extensions in the following using a collection of examples. Additional background on these extensions is available in the paper Generalized and Efficient Array Decision Procedures.

Arrays in Z3 are used to model unbounded or very large arrays. Arrays should not be used to model small finite collections of values. It is usually much more efficient to create different variables using list comprehensions.

# We want an array with 3 elements.
# 1. Bad solution
X = Array('x', IntSort(), IntSort())
# Example using the array
print X[0] + X[1] + X[2] >=0

# 2. More efficient solution
X = IntVector('x', 3)
print X[0] + X[1] + X[2] >= 0
print Sum(X) >= 0

Select and Store

Let us first check a basic property of arrays. Suppose A is an array of integers, then the constraints A[x] == x, Store(A, x, y) == A are satisfiable for an array that contains an index x that maps to x, and when x == y. We can solve these constraints.

A = Array('A', IntSort(), IntSort())
x, y = Ints('x y')
solve(A[x] == x, Store(A, x, y) == A)

The interpretation/solution for array variables is very similar to the one used for functions.

The problem becomes unsatisfiable/infeasible if we add the constraint x != y.

A = Array('A', IntSort(), IntSort())
x, y = Ints('x y')
solve(A[x] == x, Store(A, x, y) == A, x != y)

Constant arrays

The array that maps all indices to some fixed value can be specified in Z3Py using the K(s, v) construct where s is a sort/type and v is an expression. K(s, v) returns a array that maps any value of s into v. The following example defines a constant array containing only ones.

AllOne = K(IntSort(), 1)
a, i = Ints('a i')
solve(a == AllOne[i])
# The following constraints do not have a solution
solve(a == AllOne[i], a != 1)

Datatypes

Algebraic datatypes, known from programming languages such as ML, offer a convenient way for specifying common data structures. Records and tuples are special cases of algebraic datatypes, and so are scalars (enumeration types). But algebraic datatypes are more general. They can be used to specify finite lists, trees and other recursive structures.

The following example demonstrates how to declare a List in Z3Py. It is more verbose than using the SMT 2.0 front-end, but much simpler than using the Z3 C API. It consists of two phases. First, we have to declare the new datatype, its constructors and accessors. The function Datatype('List') declares a "placeholder" that will contain the constructors and accessors declarations. The method declare(cname, (aname, sort)+) declares a constructor named cname with the given accessors. Each accessor has an associated sort or a reference to the datatypes being declared. For example, declare('cons', ('car', IntSort()), ('cdr', List)) declares the constructor named cons that builds a new List using an integer and a List. It also declares the accessors car and cdr. The accessor car extracts the integer of a cons cell, and cdr the list of a cons cell. After all constructors were declared, we use the method create() to create the actual datatype in Z3. Z3Py makes the new Z3 declarations and constants available as slots of the new object.

# Declare a List of integers
List = Datatype('List')
# Constructor cons: (Int, List) -> List
List.declare('cons', ('car', IntSort()), ('cdr', List))
# Constructor nil: List
List.declare('nil')
# Create the datatype
List = List.create()
print is_sort(List)
cons = List.cons
car  = List.car
cdr  = List.cdr
nil  = List.nil
# cons, car and cdr are function declarations, and nil a constant
print is_func_decl(cons)
print is_expr(nil)

l1 = cons(10, cons(20, nil))
print l1
print simplify(cdr(l1))
print simplify(car(l1))
print simplify(l1 == nil)

The following example demonstrates how to define a Python function that given a sort creates a list of the given sort.

def DeclareList(sort):
    List = Datatype('List_of_%s' % sort.name())
    List.declare('cons', ('car', sort), ('cdr', List))
    List.declare('nil')
    return List.create()

IntList     = DeclareList(IntSort())
RealList    = DeclareList(RealSort())
IntListList = DeclareList(IntList)

l1 = IntList.cons(10, IntList.nil)
print l1
print IntListList.cons(l1, IntListList.cons(l1, IntListList.nil))
print RealList.cons("1/3", RealList.nil)

print l1.sort()

The example above demonstrates that Z3 supports operator overloading. There are several functions named cons, but they are different since they receive and/or return values of different sorts. Note that it is not necessary to use a different sort name for each instance of the sort list. That is, the expression 'List_of_%s' % sort.name() is not necessary, we use it just to provide more meaningful names.

As described above enumeration types are a special case of algebraic datatypes. The following example declares an enumeration type consisting of three values: red, green and blue.

Color = Datatype('Color')
Color.declare('red')
Color.declare('green')
Color.declare('blue')
Color = Color.create()

print is_expr(Color.green)
print Color.green == Color.blue
print simplify(Color.green == Color.blue)

# Let c be a constant of sort Color
c = Const('c', Color)
# Then, c must be red, green or blue
prove(Or(c == Color.green, 
         c == Color.blue,
         c == Color.red))

Z3Py also provides the following shorthand for declaring enumeration sorts.

Color, (red, green, blue) = EnumSort('Color', ('red', 'green', 'blue'))

print green == blue
print simplify(green == blue)

c = Const('c', Color)
solve(c != green, c != blue)

Mutually recursive datatypes can also be declared. The only difference is that we use the function CreateDatatypes instead of the method create() to create the mutually recursive datatypes.

TreeList = Datatype('TreeList')
Tree     = Datatype('Tree')
Tree.declare('leaf', ('val', IntSort()))
Tree.declare('node', ('left', TreeList), ('right', TreeList))
TreeList.declare('nil')
TreeList.declare('cons', ('car', Tree), ('cdr', TreeList))

Tree, TreeList = CreateDatatypes(Tree, TreeList)

t1  = Tree.leaf(10)
tl1 = TreeList.cons(t1, TreeList.nil)
t2  = Tree.node(tl1, TreeList.nil)
print t2
print simplify(Tree.val(t1))

t1, t2, t3 = Consts('t1 t2 t3', TreeList)

solve(Distinct(t1, t2, t3))

Uninterpreted Sorts

Function and constant symbols in pure first-order logic are uninterpreted or free, which means that no a priori interpretation is attached. This is in contrast to arithmetic operators such as + and - that have a fixed standard interpretation. Uninterpreted functions and constants are maximally flexible; they allow any interpretation that is consistent with the constraints over the function or constant.

To illustrate uninterpreted functions and constants let us introduce an (uninterpreted) sort A, and the constants x, y ranging over A. Finally let f be an uninterpreted function that takes one argument of sort A and results in a value of sort A. The example illustrates how one can force an interpretation where f applied twice to x results in x again, but f applied once to x is different from x.

A    = DeclareSort('A')
x, y = Consts('x y', A)
f    = Function('f', A, A)

s    = Solver()
s.add(f(f(x)) == x, f(x) == y, x != y)

print s.check()
m = s.model()
print m
print "interpretation assigned to A:"
print m[A]

The resulting model introduces abstract values for the elements in A, because the sort A is uninterpreted. The interpretation for f in the model toggles between the two values for x and y, which are different. The expression m[A] returns the interpretation (universe) for the uninterpreted sort A in the model m.

Quantifiers

Z3 is can solve quantifier-free problems containing arithmetic, bit-vector, Booleans, arrays, functions and datatypes. Z3 also accepts and can work with formulas that use quantifiers. It is no longer a decision procedure for such formulas in general (and for good reasons, as there can be no decision procedure for first-order logic).

f = Function('f', IntSort(), IntSort(), IntSort())
x, y = Ints('x y')
print ForAll([x, y], f(x, y) == 0)
print Exists(x, f(x, x) >= 0)

a, b = Ints('a b')
solve(ForAll(x, f(x, x) == 0), f(a, b) == 1)

Nevertheless, Z3 is often able to handle formulas involving quantifiers. It uses several approaches to handle quantifiers. The most prolific approach is using pattern-based quantifier instantiation. This approach allows instantiating quantified formulas with ground terms that appear in the current search context based on pattern annotations on quantifiers. Z3 also contains a model-based quantifier instantiation component that uses a model construction to find good terms to instantiate quantifiers with; and Z3 also handles many decidable fragments.

Note that in the previous example the constants x and y were used to create quantified formulas. This is a "trick" for simplifying the construction of quantified formulas in Z3Py. Internally, these constants are replaced by bounded variables. The next example demonstrates that. The method body() retrieves the quantified expression. In the resultant formula the bounded variables are free. The function Var(index, sort) creates a bounded/free variable with the given index and sort.

f = Function('f', IntSort(), IntSort(), IntSort())
x, y = Ints('x y')
f = ForAll([x, y], f(x, y) == 0)
print f.body()
v1 = f.body().arg(0).arg(0)
print v1
print eq(v1, Var(1, IntSort()))

Modeling with Quantifiers

Suppose we want to model an object oriented type system with single inheritance. We would need a predicate for sub-typing. Sub-typing should be a partial order, and respect single inheritance. For some built-in type constructors, such as for array_of, sub-typing should be monotone.

Type     = DeclareSort('Type')
subtype  = Function('subtype', Type, Type, BoolSort())
array_of = Function('array_of', Type, Type)
root     = Const('root', Type)

x, y, z  = Consts('x y z', Type)

axioms = [ ForAll(x, subtype(x, x)),
           ForAll([x, y, z], Implies(And(subtype(x, y), subtype(y, z)),
                                     subtype(x, z))),
           ForAll([x, y], Implies(And(subtype(x, y), subtype(y, x)),
                                  x == y)),
           ForAll([x, y, z], Implies(And(subtype(x, y), subtype(x, z)),
                                     Or(subtype(y, z), subtype(z, y)))),
           ForAll([x, y], Implies(subtype(x, y),
                                  subtype(array_of(x), array_of(y)))),
           
           ForAll(x, subtype(root, x))
           ]
s = Solver()
s.add(axioms)
print s
print s.check()
print "Interpretation for Type:"
print s.model()[Type]
print "Model:"
print s.model()

Patterns

The Stanford Pascal verifier and the subsequent Simplify theorem prover pioneered the use of pattern-based quantifier instantiation. The basic idea behind pattern-based quantifier instantiation is in a sense straight-forward: Annotate a quantified formula using a pattern that contains all the bound variables. So a pattern is an expression (that does not contain binding operations, such as quantifiers) that contains variables bound by a quantifier. Then instantiate the quantifier whenever a term that matches the pattern is created during search. This is a conceptually easy starting point, but there are several subtleties that are important.

In the following example, the first two options make sure that Model-based quantifier instantiation engine is disabled. We also annotate the quantified formula with the pattern f(g(x)). Since there is no ground instance of this pattern, the quantifier is not instantiated, and Z3 fails to show that the formula is unsatisfiable.

f = Function('f', IntSort(), IntSort())
g = Function('g', IntSort(), IntSort())
a, b, c = Ints('a b c')
x = Int('x')

s = Solver()
s.set(auto_config=False, mbqi=False)

s.add( ForAll(x, f(g(x)) == x, patterns = [f(g(x))]),
       g(a) == c,
       g(b) == c,
       a != b )

# Display solver state using internal format
print s.sexpr()
print s.check()

When the more permissive pattern g(x) is used. Z3 proves the formula to be unsatisfiable. More restrictive patterns minimize the number of instantiations (and potentially improve performance), but they may also make Z3 "less complete".

f = Function('f', IntSort(), IntSort())
g = Function('g', IntSort(), IntSort())
a, b, c = Ints('a b c')
x = Int('x')

s = Solver()
s.set(auto_config=False, mbqi=False)

s.add( ForAll(x, f(g(x)) == x, patterns = [g(x)]),
       g(a) == c,
       g(b) == c,
       a != b )

# Display solver state using internal format
print s.sexpr()
print s.check()

Some patterns may also create long instantiation chains. Consider the following assertion.

ForAll([x, y], Implies(subtype(x, y),
                       subtype(array_of(x), array_of(y))),
       patterns=[subtype(x, y)])

The axiom gets instantiated whenever there is some ground term of the form subtype(s, t). The instantiation causes a fresh ground term subtype(array_of(s), array_of(t)), which enables a new instantiation. This undesirable situation is called a matching loop. Z3 uses many heuristics to break matching loops.

Before elaborating on the subtleties, we should address an important first question. What defines the terms that are created during search? In the context of most SMT solvers, and of the Simplify theorem prover, terms exist as part of the input formula, they are of course also created by instantiating quantifiers, but terms are also implicitly created when equalities are asserted. The last point means that terms are considered up to congruence and pattern matching takes place modulo ground equalities. We call the matching problem E-matching. For example, if we have the following equalities:

f = Function('f', IntSort(), IntSort())
g = Function('g', IntSort(), IntSort())
a, b, c = Ints('a b c')
x = Int('x')

s = Solver()
s.set(auto_config=False, mbqi=False)

s.add( ForAll(x, f(g(x)) == x, patterns = [f(g(x))]),
       a == g(b),
       b == c,
       f(a) != c )

print s.check()

The terms f(a) and f(g(b)) are equal modulo the equalities. The pattern f(g(x)) can be matched and x bound to b and the equality f(g(b)) == b is deduced.

While E-matching is an NP-complete problem, the main sources of overhead in larger verification problems comes from matching thousands of patterns in the context of an evolving set of terms and equalities. Z3 integrates an efficient E-matching engine using term indexing techniques.

Multi-patterns

In some cases, there is no pattern that contains all bound variables and does not contain interpreted symbols. In these cases, we use multi-patterns. In the following example, the quantified formula states that f is injective. This quantified formula is annotated with the multi-pattern MultiPattern(f(x), f(y)).

A = DeclareSort('A')
B = DeclareSort('B')
f = Function('f', A, B)
a1, a2 = Consts('a1 a2', A)
b      = Const('b', B)
x,  y  = Consts('x y', A)

s = Solver()
s.add(a1 != a2,
      f(a1) == b,
      f(a2) == b,
      ForAll([x, y], Implies(f(x) == f(y), x == y),
             patterns=[MultiPattern(f(x), f(y))])
      )
print s.check()

The quantified formula is instantiated for every pair of occurrences of f. A simple trick allows formulating injectivity of f in such a way that only a linear number of instantiations is required. The trick is to realize that f is injective if and only if it has a partial inverse.

A = DeclareSort('A')
B = DeclareSort('B')
f = Function('f', A, B)
finv = Function('finv', B, A)
a1, a2 = Consts('a1 a2', A)
b      = Const('b', B)
x,  y  = Consts('x y', A)

s = Solver()
s.add(a1 != a2,
      f(a1) == b,
      f(a2) == b,
      ForAll(x, finv(f(x)) == x)
      )
print s.check()

Other attributes

In Z3Py, the following additional attributes are supported: qid (quantifier identifier for debugging), weight (hint to the quantifier instantiation module: "more weight equals less instances"), no_patterns (expressions that should not be used as patterns, skid (identifier prefix used to create skolem constants/functions.

Multiple Solvers

In Z3Py and Z3 4.0 multiple solvers can be simultaneously used. It is also very easy to copy assertions/formulas from one solver to another.

x, y = Ints('x y')
s1 = Solver()
s1.add(x > 10, y > 10)
s2 = Solver()
# solver s2 is empty
print s2
# copy assertions from s1 to s2
s2.add(s1.assertions())
print s2

Unsat Cores and Soft Constraints

Z3Py also supports unsat core extraction. The basic idea is to use assumptions, that is, auxiliary propositional variables that we want to track. Assumptions are also available in the Z3 SMT 2.0 frontend, and in other Z3 front-ends. They are used to extract unsatisfiable cores. They may be also used to "retract" constraints. Note that, assumptions are not really soft constraints, but they can be used to implement them.

p1, p2, p3 = Bools('p1 p2 p3')
x, y = Ints('x y')
# We assert Implies(p, C) to track constraint C using p
s = Solver()
s.add(Implies(p1, x > 10),
      Implies(p1, y > x),
      Implies(p2, y < 5),
      Implies(p3, y > 0))
print s
# Check satisfiability assuming p1, p2, p3 are true
print s.check(p1, p2, p3)
print s.unsat_core()

# Try again retracting p2
print s.check(p1, p3)
print s.model()

The example above also shows that a Boolean variable (p1) can be used to track more than one constraint. Note that Z3 does not guarantee that the unsat cores are minimal.

Formatter

Z3Py uses a formatter (aka pretty printer) for displaying formulas, expressions, solvers, and other Z3 objects. The formatter supports many configuration options. The command set_option(html_mode=False) makes all formulas and expressions to be displayed in Z3Py notation.

x = Int('x')
y = Int('y')
print x**2 + y**2 >= 1
set_option(html_mode=False)
print x**2 + y**2 >= 1

By default, Z3Py will truncate the output if the object being displayed is too big. Z3Py uses … to denote the output is truncated. The following configuration options can be set to control the behavior of Z3Py's formatter:

  • max_depth Maximal expression depth. Deep expressions are replaced with ….
  • max_args Maximal number of arguments to display per node.
  • rational_to_decimal Display rationals as decimals if True.
  • precision Maximal number of decimal places for numbers being displayed in decimal notation.
  • max_lines Maximal number of lines to be displayed.
  • max_width Maximal line width (this is a suggestion to Z3Py).
  • max_indent Maximal indentation.
x = IntVector('x', 20)
y = IntVector('y', 20)
f = And(Sum(x) >= 0, Sum(y) >= 0)

set_option(max_args=5)
print "\ntest 1:"
print f

print "\ntest 2:"
set_option(max_args=100, max_lines=10)
print f

print "\ntest 3:"
set_option(max_width=300)
print f
z3-z3-4.8.7/examples/python/tutorial/html/examples/000077500000000000000000000000001356505360400222135ustar00rootroot00000000000000z3-z3-4.8.7/examples/python/tutorial/html/examples/queens.png000066400000000000000000000162771356505360400242360ustar00rootroot00000000000000‰PNG  IHDRÕÓVžsRGB®ÎégAMA± üa pHYsÃÃÇo¨dTIDATx^í{PU×õÇ™Ú?Ú"(Qyˆ<.ŠDƒhL5$UQc¢BFQÔµ‰j‡Xi I$’4t(m¦3„šIÚ2 IS$IÓÇØ¿Ú鯙ÖigJG:máû»kã%„ß½îsö]÷æÀ]Îìaß³×Z®söþìµÏ>ç®ù'W@®ëûœµ_7A(¿=Ÿ`”`œ ýÁ8ú?ä|ÌÆgÐúç&LÃ$´NcðÕ ˜r>f“¾'@ T Õî ì*]‰Ÿ–®²á¬Á!c÷|Lå9|µbÃÔ?»zLžõuñ?­^ý÷f¢huÞ?›§ zy.¦§¸Ë©ùLeÖŒ©ªx>ÿüÌ=˜2åKÈžwúž]§tç'Ä)[éÉñè­\«d­ »ƒÉTžÃW+6Lý³«'P1CE0¸RâQSSŒ¤8uŸDòlÑ"Õ[åŸmLÌŽ*žNûàì%snçH”"ÝôÄY¨®®‚kÞ|ä¶-P™m§õ‘‡l©›wÒøÙÑÇžZ{ÎT€üúf¤"¨"#"pྑû§Ï­qG´U¨NÇJ·.T:$KŸI—>ß½`&Þ8v—,ÿüœíFSy‰T~vÔX°ÆvÂON|]qlóÂÑHU²g’ãoS˺ƃKQøÀfU¨Nº®ä8nß6 é’ ²5Ö¶•¥‡Œé ²«Çá«vý2•¨E™ØÓ‘’8[=‹¢èsòÉ#(Ú¾/º7.NlÎÄ…çžS…ê´Û·aýj%C²¤CºdÃíd£Â¿U…)$võªAEA÷Tm¯¿Ž»²F¡êééÁºœlZ‘ªS¡úÆå©jCÂé.Ùß©Vfe»ƒÉTžÃW+6Lý³«'Pª ËÓÐÛÛ‹m›7`EF¬fxx)‰˜6õkPeÆ´pÌOŠÇÐÐ’!YÒ!]²!Pù¡tçíB£“¨Õ¡û\hllĵk×Ô½ÑîG‹Ôõ>}ªL}& úûûU½¬ô¤j#úL:¤K6*Êòsñ\íV–2ãýõìøå.ÍALL ¢¢¢Xwþ&&& ))qäXl¬ûï %C²Kg#<<5g TL×xÒÙ‘HÅÔaÞžƒüàÈruºººpõêU j¿¯sãÆ %ÛÙÙ©t„îîn´´´ŒF«ÊÊJP¡HU__¯ÚHæðã´[Ç—=_­ØàZéìXñ…CFçW»,ÿ¼ü£h—Ë…ââbTTT ®®NE¦ÖÖVU¨NǨdrrr0ó³weùÇ­¸ ÑÙ¨ ½»¿x—vƒb¼@Jb¼ÏosÌÚVlèW»_8d¸üÕÙ¨ ÕöU騭­Åõë×-ƒE²E…;ðÌö‘÷%RI¤’çTc@ wøÖ,™‡¬ùñHˆ‹AÂì™HsßêöÌTäÞ¾@ªÓ±„Ù·)’Í[œŒ÷*V TžôtQǤ]"ÕÐiôEEÊUñ®*TûåE]Gr,…¬ØÐùÁÕnÅ.uvª/*]§èÚ9˜:?¸Ú­øÂ!Ãå¯ÎŽ@%Pù\®ëW;0Vlpù«³ã*W»• Á!Ãå¯Î‡¯Vlèüàj·â ‡ —¿:;¾Z²áíëô:ç¸Ú-9ÈI¸üÕÙ‘ó1Û¥Ó]W®ö`õD*Æ·7‚Õi\ƒLgÇ©çãô̾•@åØ×¡&jf_J r,T5³¯@%P9ªÉ’ÙW ¨ÕdÊì+P TŽ€j2eö¨*G@E;‘“%³¯@%P9*k2dö¨*GA52û T•£ ¢h5Ñ3û T•ã šè™}*ÊqPMô̾•@å8¨&zf_J rT=³¯@%P9*Ú¬˜È™}*Ê‘PMä̾•@åH¨(ZMÔ̾•@åH¨&rf_J r$T9³¯@%P9ª‰œÙW ¨ ÕDÎì+P TŽ„Ê[Rš‰’ÙW ¨& TºìOºö`e‡¨*Š!·äç€õ–L3XDëf®v9IrIc€k<éìH.uÆYJw±¹Úe’pø$!‘êÿwÐ;«ð§7«-ÿΖgsA£³#P TŽýU‰ÿ}ü*NíÛŠC…÷âïï¾ üüÙ¥È^èr— U§cß4âͺ£ø°ùÔ-AÓÁÀÕ.P TŽ…ªùì^(Ù‡ïž?§vmT~–íÝ‚ææf455¡|ßu¬îéG°µ`#î̽ï½ZêØóá†kÐÙáöÛ—=\í!uOõvý1o[‹šÊµG±æžUxàþ­¨<²CûË[ÏaάÌ™£êtìàÎ|Ú‰ãÇÐpú1u¬§±TÙzç¥ã£quŠÎΤ„Œ÷µ·º6ºëÊÕ2P |ð¤ÏÇåË—áJM-ý¨v¬ÏEXXþÓ÷Ê(ñn¨¨x:¨ÿí øò”)X¶( ƒ½ J7c~’²•™–‚ýò%%ËÕ):;•,ÿ±\"¹RQSSƒ¬ôyê>‰g[e‰‚êÏ]5£~Îuÿ 5ÏàýÇ{/*™÷rÑsµ -ÕÕÕÈÊHÁ¿{_¨üxÞ7é&‰PÚýûíŸÁ¦U‹ ÿýh$RT‘‘8³«úüÏ÷ë‘éŽhT¨®î©N>¬tH–>“.}Þè¶õûŽoËòÏ (* TŒë_Ý2‡«}l§ýáJÄù' G¡:P²©ÉsÕ²îÝWžÆÃ;T…ꤻÈ*Ü1 é’ ²5Ö6—¿:;“n2Ž)¹§òs†Ó >OûØ MQffl4ÒR’Ô³(Š>¥'Žá‘¢èªû&.+Â÷.\P…ê´ÛW°!_É,é.ÙðD;}«þø+'PÉ=•#î©ÆDºjk{ëîZ< UOO¶¬]Ž¢wƒêT¨^¸a¥Úð@¥tܺdcüàö«ú•@å8¨v|c%z{{±}Ûä¯ÈRÀ cAF*¢¦E```@•˜Ó>?CCCJ†dI‡tɆ@•ϲã9é&‰PÚ¨ðtÞ3߯ÆF\»vMÝïÞ¥.ÙӧÔgª¿¿_ÕËËÜ{ÝÿH†>“é’ J ò¶º™çTchûÎ~DFD`YîRÄÄÄ ** ÑÑшU“‘œ”ô¹c$C²9K#<<íU*¦{b‰TŒ;5Vï!ü•ßi}ß?£¢NWW®^½ŠÁÁAÏäâóï7”lgg§Òý]ÇYJ òºü ÉHElì+Æ•+WÐÞÞŽ‹/¢ªªJ•òòrU<Ÿ©dúúúPR¼'÷x½Oô~«ú“nfgœ¨eKi¦Ó FozèãFDENE^^ÊÊÊT î½6ttt¨Bu:Fm$C²3¦G€t½ÙÔùÁÕ.PÉîŸãvÿ<ƒòÒ©GqôÈatww£¥¥e4ZUVV‚ EªúúzÕF2GÆ¥òG{>ܰqM:;Ü~û²§óƒ«=d—mæÆÅÂår¡¸¸¨««S‘©µµUªÓ1j#™œœ¤$Ä TÌ+ ŠqýË53èìxë4z9ö ûõ$»ÿRo¾‰!Ë?žíty÷(”:¸Ú½Pòà:ÔÖÖâúõë–¹"Ù‡Š ÑT±Gî©£•D*F°¸ ÑÙñÖiô_AÞ2Ü‘•Žä„Ùî2.÷w£îÈ^ˆ;—.Q…êtŒÚH†d7­ÎÅߺŸ¨*ß©ÝBñ _3#}Q‘rUüõuªP}ì—u3ªn®v\í\þêìpù«³£óƒ«=d7*t`ÒÎÕ):;&¾™èèüàj7ñÍD‡Ë_j’,gM™NG7x¸Úu~pµsù«³#iŸåÞ@Ò>3N¬jðvO¥#‘«kÒÙáòWgGçW»Î®v.uv¸üÕÙÑùÁÖ.PÉó_ƒ‘mi"®v»çcš©X"•,ÿB~ùÇ©X ¨B*îLÅ•@rP:S±@%P…TÁÈT,P T!U02 TUHA¥^äp¦bJ  9¨¬@f*¨ª„*™Š**$¡¢h¨LÅ•@²P*S±@%P…,TÊT,P T! U 2 TUÈB¨LÅ•@²PÑfE 2 TUHCˆLÅ•@ÒPQ´âÎT,P T! U 2 TUHCˆLÅ•@ÒP"S±@%P…4TÈT,P T! •·d0þf*¨*JòþÙÿ=®W:;vS`™Êëüàj7õÏ®—¿:;vý2–—(°©¯‹­ëT®vãζ9#sù«³3éÎG ¨d’àK¨JÈ„ú‚®Ò•øié*Û?7éfB›Çôüu†«ÝÔ?»z\þêì8ªO«×cÿ½™(Z÷Ïæ)ˆ^ÞŸ‹…é)î2OÕéØojòñž%h}bÙ-A³{ñMåu›«ÝÔ?»z\þêìØõËT^çW»#¡:·sJöîÁùš<¶.SS’Ÿ‰ææ×ÐÔÔˆ7pt¬l[6 6ä#wIšŽ€æ­˜v‚]=®NÑٱ뗩¼Î®vSÿìêqù«³ã¨.•,Å«2Ðrh$â<ïŽ>«W®Àý[ p´`‘:öÎé»1+:RªÓ±ÂÕ.Úñ§žÄ·ÌRÇ^{|™²Õà¶é9y»ßT^w±¹ÚMý³«Çå¯ÎŽ]¿Låu~pµáPýêÜZ¤Ï›‹Ë—/#51´ô£“ËÏž…°°0|R5ò™Ê¬SUñ|þù™{0eÊ—=ï6ô=»NéÎOˆS¶Ò“ãÑ[¹VÉšv‚]=®NÑٱ뗩¼Î®vSÿìêqù«³ó…CE0¸RâQSSŒ¤8uŸDN?[´HAõVùg³c§ƒŠç¤>8»FÉœÛ9¥H7=qª««àš7¹m Tæ»›ºÁÃÕnSy.uv¾p¨ÈÁ=µ÷,œ©ùõÍHEPEFDàÀ}#÷Ož[ãŽh ªPŽ•n]¨tH–>“.}¾{ÁL¼qì.Yþù¹K¨<\í¦ØÕãòWgÇP‘“?9ñuÄ±Í G#UÉž]HŽ¿M-ë.Eá›U¡:鸒ãP¸}Û(T¤K6ÈÖØ·{ñMåu›«ÝÔ?»z\þêìØõËT^çW»c ¢(;c:Rg«gQ}N>yEÛ·âE÷ÆÅ‰Í™¸ðÜsªPvû6¬_­dH–tH—lx¢ç"™v‚]=®NÑٱ뗩¼Î®vSÿìêqù«³ã¨ÈQº§j{ýuÜ••0 UOOÖå¤`ÓŠ4P Õ7.OU¨H‡tÉÆø“¶{ñMåu›«ÝÔ?»z\þêìØõËT^çW»£ Ú°< ½½½Ø¶yVdÄ*`†‡‡‘‘’ˆiS¿†UfL Çü¤x )’%Ò%•ùæÄØË5ÈtvL!±«§óƒ«ÝQPºÏ…ÆÆF\»vMÝí~´HùwúT™úL@õ÷÷«zYéIÕF2ô™tH—lT•7ภÑÙqTž¿Ü¥9ˆ‰‰ATT¢££ë®ÓßÄÄ$%%Ž‹uÿ¡dHvÉâl„‡‡£æálÊÏ]?Ï€Ô ®v»ÇTžË_GAõƒ#ËUÔéêêÂÕ«W1888úƯ¯Ê7”lgg§Òýñ˜­tÙ¨ð/béW»)$võ¸üÕÙqTäìîu.ìso¥_¹rííí¸xñ"ªªªT)//WÅó™ÚH¦¯¯Å»Añú‘gZ²üó&‰Tþ}ÄqP}Z³‘á_A^^ÊÊÊT hkkCGG‡*T§cÔF2$;}êWAºP–nFæj·qLå¹üÕÙqTäð™m™8røº»»ÑÒÒ2­*++A…"U}}½j#™ÃO<ŽÓn_'kÚ võt›«Ý®_¦ò\þêì˜úgWOçW»ã ¢h—Ë…ââbTTT ®®NE¦ÖÖVU¨NǨdrrr0ó³weùÇ­¸™ÎŽ]8Låu~pµ;*z9vñ.íÅx”ÄxŸß 6í»z\¢³c×/Sy\í¦þÙÕãòWgÇqPm_•ŽÚÚZ\¿~Ý2X$[T¸ÏlyoP"•D*yN5z‡oÍ’yÈš„¸$Ìž‰4÷÷­nÏLEîí T¡:K˜}›’!Ù¼ÅÉx¯bµ@ÅôŒJ6*Ìw©¼Eú¢"åªx× ªýò¢63´[-9t~pµÛ]ö˜Êsù«³cêŸ]=\í*OÖîÅ7•÷×O«ú¦þÙÕ³ê¿rvý2•÷×O«úc³zÑý•3TvõüõÓª¾]¿Lå­ú㯜W¨ü5jUßôâØÕ³ê¿rvý2•÷×O«ú¦þÙÕ³ê¿rvý2–¿IUØØ­6·ªoì´ÍèbÕåä|Ìvý½îVõƒÕ?©BàW?$³ïÈNž@e3É®\¾Êå!™}}o… T•Ï™Õ×rG2ûÞúÙ’@%Pi¡’̾öÐ TÕ-¡’̾ö€’{*F &ëë6’ÙW ’wò' Ï=•döµ–,ÿ0­>Ï0• V§õO2ûZ+Xý#Ï©&øs*Éì+PIŽs†<>’Jf_k`I¤b|ž‹hºœ³«¬Nï—dö¨ä'nüœ0ÆC%™}*Š*Éì+P TÌPIf_J b†Š–ƒ’ÙWV°îyeK}‚o©{î¯$³¯@Ôï·ØÝÅ3•ÖLèË?Éì+o©íKc¦ØÕû"¡’̾©$Rùq_å vÉì+P TÌPIf_J b†J2û T3TÞ–„’Ù÷ó ëžW¶Ô'É–ºÝM+òÁ„V|á ÖùT•üXž«¯ zK¦,¢9f+6ä|$ÉePÓ*TfΨVç‘I¬Ï8®½,?PðÇÎ*üéÍjÛs­8È!#ƒÐáƒ{ùåÃÇX²bÃTÿûøUœÚ·‡ ïÅßß}AAô³K'½Ðå.ªNdž?iÄ›uGñaó©[‚fÅAJ rìò¯ùì^(Ù‡ïž?§vmTÀ”íÝ‚ææf455¡|ßu¬îéG°µ`#î̽ï½ZªM ÉέlT•c z»þŠ·­ÅMe ŒÎÚ£XsÏ*ƒM«+@þûÑH¤"¨"##pfÿVõùŸï×#ÓѨP]ÝS|Xé,}&]ú¼Ñmë÷ß–åŸ@Qä¨&0TÔyx£RqþÉÂQ¨”ìEjò\µ¬{÷•§ñðÎU¡:é,rG£‡ wŒBEºdƒl²ü“åߤœ$t)ÊÌŒFZJ’zEѧôÄ1óøýhllĵk×Ô½Qñî]jÕxæô)õ™€êïïWõò2÷Ã^÷?’¡Ï¤CºdC ¨BâîžJíø}g?"#"°,w)bbb…èèhÄÆÆª¿I‰‰HNJúÜ1’!Ùœ%‹Žöª•Ÿ»~ò0Û,BýÞ T}ß?£¢NWW®^½ŠÁÁAÏ;ƒ>ÿÞ¸qCÉvvv*Ýßuœ¨ªÐøñ?+PéÇÛ„ûŠqåÊ´··ãâÅ‹¨ªªR¥¼¼\Ïgj#™¾¾>”ïÆÉ=^Ÿ­ÈF…ÙrPî©Ì"VÐÆ›U¨†>nDTäTäå塬¬LAÕÐÐàÞÙkCGG‡*T§cÔF2$;czH×Û@ÚIN¶¯Èù=Úx³ AqéÔ£8zä0º»»ÑÒÒ2­*++A…"U}}½j#™£‡ãRù£ò–:Ó²Oî©Ì"”#ï©È)Š6sãbár¹P\\ŒŠŠ ÔÕÕ©ÈÔÚÚª Õ鵑LNNRâ*ê–/OOºå¬ÕHE/Çt¿žd÷_êÍ71dùgvÿ[Гm9kª’ס¶¶ׯ_·ÌÉ>TTˆ¦Š=rOÅ­&Ý̪PÑ;|yËpGV:’f»Ë¸Üߺ#{!î\ºDªÓ1j#’Ý´:ë~^ ¨B'šÕHåmv¤/*R®Š¿þ¢Nªýò¢nF ÚnÌd› å|&Ïí•Ù}–Ýël*/ýcØ?þD*ÓÎ ú§ÌìΞÙ'[ÿxƒJf(Ãj² 9£ÉHr©ËBèl i’¨*6¨$SñÍ7>dùg¶Ô 凲’©Xóº”@%Pùº‡öµ%™Š*IÚi¸Äõ@%™Šm¾È+‘J"Õ­"•d*¶ m†TÕ­ ’LÅ•¼ch¸Ô»ÕÆ‹d*¶ –D*‰TV6*$S± °*Ê T’©X ’œí ËÀñ[ê’©Ø"X©$RY‰T˜d*¨ä'{üŒVã#•d*¨*f¨$S±@%P1C%™Š*Š*ÉT,P T€J2[Kvÿd÷ÏêîŸgãB2Ë[êò–ºaÄòöÕÉT,‘J-%ç†Y4ö•d*¨*Ã(åë·x%S±@%P1C%™Š*Š*ÉT,P TA€J’ªŽM¶ÔÍnâC9›’¿ןlI^¿Noù·rDP®€\ŸW L®\¹¼WàÿœŠ÷Ñ D¶IEND®B`‚z3-z3-4.8.7/examples/python/tutorial/html/examples/sudoku.png000066400000000000000000000300711356505360400242340ustar00rootroot00000000000000‰PNG  IHDRNO"'ÌsRGB®ÎégAMA± üa pHYsÃÃÇo¨d/ÎIDATx^í]íqÝ8Ü¿ûGUWN@¥t)\ JÁ)l .ep)(§°)l —‚Î-òˆä|`ø4ÀkV©¬] H {¦9‚į<ˆ DÀ…À.k"@ˆÀ+…“N@ˆp"@átFs"@ˆ…“>@ˆp"p!œýõ×ëþóþL‚Áýýýë—/_Þ~ð;¹«í»ä«6?GñmlÇ…p¢áüÁb@ Ð„@)œ }€>àð³pþë_ÿ*Qú=>>¾â§JZ©?(Ñ[…€ß+`T àQ©?äK/Õ+ñ lñeNièœ;M5ÿþýû+~ª•ú#§VÈWßCÈ×qäTÂ=­ÔŸ½ø:œãd 2#7«JŽ?K FpÎjC¾ö‘¤pfyÙ$wÄÄáºOÅ@ô¢äÄäËÏ3΀Vr4–ê:ä‹¥ºî%} fœQä:íˆ Ĩ;ñF§#7C|1ãÔy¼°˜ØÀ°ÒšT‡sœ:­ä‹¥ºî% •ŒN(ùb… { Kõ(Fæv Äsñ¿ÿýïÛzÔ¶~kéžžžÂKÒ²ùúßÿþ÷úðððÖ?ï±ÚîŸþyÅ«ˆÿþ÷¿/øzyyñÂófŸÍW¨¿qŽs½MÛˆM®ûTQ| H2{¯þÊ÷…­‹ögïü_¿~} kšÝJ \åñ_ÀÊ{dóå½¾´§pŽ Gát¡u|)š8d38þþûï¨7“‰ö§7hdÃR \Àü0^E8ÁMMü \pãk|I.ñ7ϑɗçº=[ ç(‚¢ý Ä&×}ª>RP–91ÊdÏéOïüðm†åélWNdþ¸à²=psk7ˆ¨çÈâËsÍ=[ gŠ¿Î1±‰ÃuŸ*‚tP´ïÈrZ0z²˜Hz×oYT[žãlXñÍ̳ør;n§…3E § ňã[ƒ¬e|ž¹³H¶mbÙ²§Ö_ Âh•ŒÓ2~+§[ 3øòòÂŒ3 ±ƒóÌ@ì`ؽDkµLÇSþEú#×ÊNˆv›Ç³G U„Só/Yx¸ÂyGùÒúæù;3NZŠí Ä&×}ª>rî°=Ú^¢eXÙ6ÒŸÖ×l}“SÎc·+<Ó*Nw¸í7qüÄn¼ŸªRVÉ`d í-9’s‹žùž¾Û>Q8/# Xµõ­ Ÿ ËÇF4€çz›¶#˜Ø·S­"œ²´C†÷íÛ·Ë[¤°zE+ÊWêÞç½}h¼¯ÂWÏ·ë8!¢À°MoX}?Ê—õü; §-–êChE»FrˆX¦t­RchËmzOáÜwà%«ÌqzÄ3ê?CN»Ó˜Â™ˆê Ä&×}ª|y"»”%¯Í“5Áò¬åôöG¾RÙ[£@(œº[Hñ¼ö*½w6 § '“•7M' ­\úm!ˆõöÑ óòÕ²ÍÞ+„GÿOëÇJS+–±n§_,m`ãåËzÞˆ…3‚ÚN›ˆM®ûTgá#ßFñS.Ã?ëq_Ökoí(œQä:íf 6q¸îSEð‘†z¢-ÓÏ*ýn]8-¯\beDÉsœî0o Äñ«îŸ¡RVÉ8åBsd'² Äïr¼\ˆnáù ¾n]8·¢(ov¨ðwùõ¤½—®U!Xü¤gÃŒ3Š3N7rQ¡Ò–#µÏ—y;íÏÑun]8-ßNFXýà™V9«BðúL³§pF‘£pº‘*Ê:™a¢,ÄÃOÖ";=ÒŸ½Áߺp—¶^SŠK[ûŠÿ'¿Ñéq¢3øò\_ÚR8£ÈQ8ÝÈUrüY27ȉ È×>˜ÎEm•9ÎDz.NUIÈ—Îô |q{`G¢£JŽÏŒS'|1ãÔ½$Á¢’£1ƒÑ %_ÇUÂg–3N=î˜q:1b ú3'Ä©æäËÏ…3à‚•§N ùbÆ©{Iß‚‡¢ÈuÚU Äûûû÷·3ð{…£>ÕJ?ò¥{h%ÿ çãããûûÃm0Ÿñïóóó+~>ãÚ½kVêÏÝÝÝ»pâ÷ UÂxTêùú®úh%¾ ½/r–êÎ>É•ˆe Έäk.¾BÂÙÛ2@O´ó-*¥î,ýt~É×>F,ÕçòŸP©Náì“\Iøph@ÔGqžE%®–˜P8ý®’£Q8ubÉŸªë^§êQŒÌíˆ D³³l y£Ó‘›!¾¸ŽSçñÂbbÃJkR ŸYJ¿4ð'"_û ±T8Ô^“JŽÆ F'–|±Bн„¥z#s;"Ñì,,ÕÝPÍ_,Õݴα}i`XiM*9>KuVòÅR]÷’‹JŽ6RªË]µ=Ä=°eâÓ¶Óð\k›ÙŸ‘~ í_£×žaê)z£Û~…^ógüÝrpŽÓ‚’Ñf•@Ä8,fu²_&>ÍqÔtÍ2û3Ò § ½_ζ mŬ*õg$ƒiÙ\ö‹YøÈcLýl•ÕŸ‘>´¶#|e\¿wŽJøœÍWÃ{XYfœ”Œ6•m$á<È&«gÛü ýÂîˆ26RÃŒ3T%>S8Û›ÿY §)ƒ]%GΧ§§7q²Þ} Ð exGSÖkWϨFøÁà¨m%>K8±CjÛ9Õ³]1…3Ñë*9ÚH ¶¶¸gQ|q¶¶íßè6¼r<ÑþdbÂRÝŽæ|yKt/.G²óùny±n¼5Î&J϶N+êq»L¾â½ÈÏ8Gæ6[o(œŒ.Tª[áˆ,BÏ D §•©¸]&_ñ^ä g«–pÃ÷>Ü”c pf0Já4¡˜ˆNäCF™| u$1¾ÚË_¿~ê…s¾+9Z´TG;üà!ÑÞùÏÞžÒ”™øP85´ÇÿžÉ×xoÆ¿- —Úù·¥¯N JF›JŽ6"œÚ¢à6¹~ôä½Y&>N£S˜eò5Ð÷¦£ý‘5GÊttˆÂ™Áhb)‘Õ¨pJç²#ygÆC!)šž"`\£Ž/±¡pfyÊþy2ùÊèíhPžg}¼†Â™ÁèB¹½›î}—Žã½k:>…3ÑY §ÊäËp9Õd´?mífÆÇk(œ*]vƒQbíWÒ-£g;32Oy(ïªëØG,FùŠ\SkSÉŸG+9/yÅr‹…SóÇß+9Q'Ž|cT ŸQáÔ½ÁgAáôáuh]ÉÑ(œ:±ä‹Â©{Iß‚ÂE®ÓŽÈ@Œºot:r3Ŀǩóxa1±a¥5©„Ï,¥_ø‘¯}Иqj¯I%Gc£K¾X!è^ÂR=Š‘¹‘hv–!ot:r3ÄKuÇÒ¥úýýýûk‘ø½ÂQÉñ«•êäK÷ÐJþ*Õ/6Ïjƒºæ¿ÏÏϯø¹æ5®U©?wwwï‰ß+`T àQ©?äë»ê£•ø‚ö¾×p˜qR8û$W"–8W ’¯¹ø 'ÒÔ G¥Ô¥Ÿîäk#–êsùO¨T§pöI®$ |ذF ê£8Ï¢’?WKL(œ‰~WÉÑ(œ:±ä‹« t/ár¤(Fæv D¢ÙY¸É Õ ñÅåHnZs¿7¸ü‡&Ì8ugD}çYT‡¥z"Ï$vL §îh•ü‡|­Á3NÇ "Kõ€Û¼5¡pêÈÍ_NG §£JŽ?Kéç„8Õœ|ù+ºå…Û>|ûöíþ}á9FmûÕõ‘þ0ƒÑåK¿‚ÝâøBÌ=<<¼½q9fàkiámÿ‘½=u"û.G‰…Caã³½¾àÿ{÷I¹…@ŒŸlåkôº½ö·ÀWÛ,Ây†9Îq|ì=ÒDÿ"ël›ŽaG)`Þ½I"ýÁpå5á\èôU”g/è[D‡«tM£|^÷…SîžJá<ÃçŒ8>6k™Ds{ÈM®±ý­t(ôí(¨PæX §ŽTÄô³Æ,VæK&+#Û:ÏÀײ¥º,‡÷\ÜbÓk!VÌÞÜ*2ÐÖ'k¼r Ƥé²U„¯¬koϳ2_­¢Ât…ó,rœ7âøR÷ö¿¦pZ¯Õ¦¬s¯+¢ÃEM#þ“uí[Î&–­z£pžåAŽóF_+Õ1ØÈ½F©nÎv×¶ö‰Â©;RÄô³Æ,Vä«Ånú-I¡pÆü#µUÄñ·O°qGl¤nŸ¶{Æ``‘þȧûí¡Ð$ôÏ*°­íŠ˜êÓ* Lá£r^SâEá<Ã{œçŒ8¾ƽ'ÔÒÆóÆN¤?mÈmô§½’ƒp·åGÍá¬k9W D§K¸ÌGør]È`¼_ò•JTK½ƒÂip†³M"Ž/…i\Y>[…*:ÇiÁH.Ê·n[²B Z°±‰øÏÈõŽÚ®ÀWË6^îýÍŠé |Ýôxé}Š}¦pÊ%RÖ x…@´TÔn†@ŒŽ-£ çÝØ¿J ZÀËå?˜«±^GÃy‘Ýjí䢽,yU¾¬ØGì4Ü#猶Y%¾´ñ³T׺Âß#ޝ-€G·ñ{#Øú¦N4㔆z¢)ÓÑ—[ Ä7‹øÏÈõV/Õ-ØP8-(lq|YNô¾Ž$ áï{‹Ò{C‹ôG.Þ®E‰.ךʅÄ´N ¡Ø úYc·Â…3æ©­"B…hËÚr kYÜuV žÖ{´¾ÜJ Ž8T”¯‘kîµ½¾(œgxóœ#ŽßÖln? –ßèôti¤?m±»Ì0Ñ7dÀž¬—Âigl„/ûUl–N§øZöáNOÜbbã£oY Œ¦RnE8G¼h¾(œ†g 60¬´&•ð¡pê´’¯}ŒöntNݯ.,*93@òuŒQ%|f¹ÑQ8õ¸£p:1b ú3'Ä©æäËÏ…3à‚•§N ùbÆ©{Iß‚¥z¹N;"1êN¼ÑéÈÍ_‡ç—/_ÞÞLùìŸÇÇÇWü|v?Úõ+õçÏ?ÿ|û ¿WÀ¨>À£RÈ—®'•ø‚¶5©òÃ;‡Âéýú íÿøðÑâA<èëø…óÇ70éÐÄ€>@ðø€Y8Yª÷ËŠJ¥K¿¹J?ò5_¡RÝú1]}ºwÌ¢Òdñ,ëÌÆkM¾üË[ÆkM¾ü|q9RÀç*9ŸÒê’/®‚н„Ë‘¢™Û1ˆfgÙòF§#7C|1ãÔy¼°˜ØÀ°ÒšT‡S+:­ä‹¥ºî% •ŒN(ùb… { Kõ(Fæv D¢ÙYXª»¡š!¾Xª»iå÷5È*9>Ku­Zþ< _Nݯ8ÇéĈÂéŸ3ó@ŒíUä q·]°·•÷X‰/Œ;;H|°Û6oÄ8½?òáEìÀ¾’£qŽS'v¾ Û-`¶o¾@$®µ‡–Ž|Ì"ÊÚÉ-izoyv³Eï)œ1»­¢Ä&váýTNÕUø’\Ë=ªÚ¾ZM(®±k«ŽzÜ"ÂnM4{»ÚÊŽgCD gœG–êNì"Žï¼„˼R¢7:CFˆfï[^{2«JøDç8QŠ7|Pªo‰DÔzP8­Hì*9Z4 à ›TÂ'ˆáÁ+ £|IQÜ›ÇDæ)³NëVàK–å{ã¶ØlÛR8­^d°«ähÑ@4 3lR ŸÕ„Sû~DDVàKŽ{ïÆÁ†Â–ˆ•©»_˜—CÖy4?'3·‡˜_•ð‰Þè´Rýååå=g©nv\ÃJŽFáÔ¹½¾nyŽs»âX´Ìsû´"j=˜qZ‘2ØÝJ  èšTÂ'šÁDÇ®µ;ëF'3*Q!Çd6 ‘l7˜‘¯GQ8½­’£Q8ubWàëhmbïoÚ;íµJøD…íäð^Þï•R8õø2[Tr4 §NÛ |Q8užaѲËíŸ'2 µoYq2Ù­ˆ¦*á3’Á‡ØŒ7:ÕJþÃŒSçËl1±æÁœ`X  §N0ùÚLj©ûÙ¢’£1ƒÑi#_ÇUÂg–ßÒã£Q8uÉ…S÷’¾3Î(rv DbÔx£Ó‘›!¾˜qê<2ãtbTÉñg)ýœ§š“/Îq¦:ÔÞÉ*9ÚýýýûÂ_ü^ᨄO5á$_º‡VòŸP©þøøøÚñ™ÿ>??¿âç3û ¯]©?wwwï‰ß+`T àQ©?äë»ê£•ø‚¶5´ò¥‚ÃRÂÙ'¹± Ĺ‘|ÍÅWH8=¯mé xÜ¢RêÎÒOç‘|ícÄR}.ÿ •êÎ>É•„Oi×D}çYTòçj‰ …3Ñï*9…S'–|qù˜î%} g¹N;"1êN¼ÑéÈÍ_\Ç©óxa1±a¥5©„Ï,¥_ø‘¯}Иqj¯I%Gc£K¾X!è^ÂR=Š‘¹‘hv–!ot:r3ÄKuG–êNŒ*9>Ku<òÅR½‹öÁþÓò«ÐøŸÚ•-#ƒò0“á”)Tö0Ý·Ìì…ÓÇȪñµlÆÙ…¹ïèR4‘q¶W.}¡qi)T£}É΀)œ>F(œ>¼R­#Ø–Ò½ñ²Â:ÅhƉ鋖•C4=Ç4r#|içù{f(œ?™€ßáçè;r*LK`ªV,7Wª{ßÅö¼6–ˆ#‚ÐX{µ´w ùfU¦hfgx£ød÷‡Âù[8ÅÑŠ¹Ø3g>C|-[ªoEE~ý‡Ë‘~“Àó´Ó*b•ŸÂ©³ák;Í#3Ïí×µ<^³ùÒGlqs'àŸ•ë-~Çÿ‹,¹‰8Ú({í£¥ú{ÿÖR+;™qþöDé{G~ã­hfˆ¯¥3Î&žX¯¹ý,H÷”³ÍÁhBMáÔêÿÂù—Þî˜;ç»ê1ÿJmUé”ÁŒÍ8G¯{Ôž|í£C¾tÏ«ä?7YªëÅ,f 66²œV•ðáNç”|ùotË—êºÛø-*93?òuŒQ%|f¹ÑQ8õ¸»°¨ähN@òEáÔ½¤oÁR=Š\§‘u'Þètäfˆ/fœ:Ì8UrüYJ?'Ä©æä‹sœ©µw²JŽvÿ¾á~¯pT§šp’/ÝC+ùO¨T||¼Ø¬ª êšÿ>??¿âçš×<ºV¥þÜÝݽ '~¯€Q%|€G¥þ¯ïªVâ Ø{¥ù°T§pöI®D,q®@$_sñNÏkvz·¨”º³ôÓy$_û±TŸËB¥:…³Or%aàSÚ5QÅy•ü¹ZbBáLô»JŽFáÔ‰%_\>¦{Iß‚ÂE®ÓŽÈ@Œºot:r3Ä×qê<^XÌ@l`XiM*á3Ké—~àDäk4fœ‡ÚkRÉјÁèÄ’/Vº—°TbdnÇ@d šecÈŽÜ ñÅR]ç‘¥º£JŽÏR]'|,ÕÛÞ$:}û«Ûûj6¶ðˆìiÎ F÷¬JþC¾ÖàëôŒ³9Š×ú‰½W GÛV`ÛìÐi=ˆ:RNN­è^RhŽSnAíxµRk¤?Rää®›Û]!žø–ƒÂ©£Dá¤pê^ò‰ÂÙ¶Ý…(l7G‹v|D¨F®yÔ6ˆÞ}Þ±Áœå pê(EøÒϳ _:n3ð•ZªcÀ{e¨×Ú¥:n& ›½íR‘e6d–ƒ¨£4C ê£8Ï¢>Õ¥«¬ã„ l?k6ºj5 £ýi©½ÿ/o<–P¡pê(Uòµ_©g çOTð$ây´—»Ì81Õa9ˆ:JÎcŒ*áMLt/ˆY\%ã¤pÆÈi­d9Ï9Î1,eëJÂÀÎë |1ãÔy¼°8ƒØ———ó›{ó ÛÎ0uÏàK¿jß‚|éÈÍÀ…SçñTál+äC!®ã rÐd†@̱ïl•ða©þ‹;Îqî;1æ;ñô¼a„ÅñÑÄ™™Áè"QIÈ×|1ãÔyLÏ8!Žò "<:zhtÔE¢N …ó£Jø0ãdÆÙõÖöî>²Ld›‘÷Óå‰)œN g#>U"×i½CKÑDÆi}­’çyQ¾Æ®ÚoÍŽê |±T×yL)Õñ¨ÍgB4­O͵î15„^ß_ÊÐ-Ï· _:ÆÎñáÐOG‘<ÉMœ—¸F ê£8Ï¢’PqŽ“sœ<½ ÜÓÓSjP8u8+ ùZƒ/–ê:)¥úÑ78{ÓÞiob êR81ª„3Nfœ¼•© ÜY•„7:åø:=ãÔaÒ-*9ËQGõ< òµ-…S÷»JþóiË‘t˜t‹J@R8É—Ž…s£JñNáarÓvb‡ë>U%|x£Óé#_þKuݯR.cjÂÒO‡©’0¯5ø¢pê³Üè3Î/_¾¼½™òÙ?¯øùì~´ëWêÏŸþùþv~¯€Q%|€G¥þ/]O*ñ lK åÚêCáô®=¤ý»»|bC˜Û(œ?>éF'&ôú€ÇÌÂÉR½_VT*%XúÍUú‘¯¹ø •êÖ÷¥õéù1 N^ûŸú!>Öš|‘¯ªä?\?Â$À»Ð«äø³<¥uœlL¾ü7:®ã 8a%Gãr$@òÅåHº—ô-˜qF‘ë´c 2£îÄŽÜ ñÅŒSçñÂbbÃJkR –ê:­ä‹¥ºî% •ŒN(ùb… { Kõ(Fæv D¢ÙY6†¼ÑéÈÍ_Ë–êÒA­‹\uJZÌ@¬u,gØUÂg%¾°É_¶/gàƒm¯·ñ†\ÿúë¯Ðn®•üçæQ8ÏDÛ9+9~†0ØFm³É8k%á„C ú„-±ÿþûo8¿¬*ùÏÍ §•© îŽÖcb­c9î>+ '2;ˆTö‹)Q¾¤¨ ~þùçŸ7w¿øï&¨Ïö7‹¿Eûc9·×†ÂÙA¬íuŽ»¦ç˜XÏx²m+á³’p61ª œÈ"›0î%R<¿~ýjv³JþCáÜІ; î„ ÅRÂì¥'Vrü•„óéééÍ_=Õ‘…Þ_RQ²÷ĘÌ:-}™…¯ei$EJôvΈ£iý‰þ}dÎ,zM­]%|f D Sü½qJ)óˆðeÍ~åü§µÏ‘þXÏíµcÆ){yyy»><<,ûÔÏë ™ö•%ál"|!\ðßöÿb4rDøÂµÐ‡£kÊŒ}µ‘þXÏíµ£pþB eEs¸k:š—0«=3N©Q…\ŠÔ¦˜zO³! {¥óÞ5·sœ«'ÿ=“Xk‰q4¤ÌþŒBGáÔ\¯íR$¹FsôÒ žžã |ZUq‡Ð{ÄüŒþxð¶Ì8 òÚÝäDˆŽ-£]%|V)Õ¥íUJrM¥Ç¿3ùBŒm—"­øðõ¦5B=ó-=!Ét´Q¡bÆ©#¸_˜/Ä8ŽD¨­óD–çÉ:³ðÁõå4„Ü+š³ÜènJ8Gç6[ˆf9šòº…SÇèVøú¬å?G™í"΢Ï(œº?›-2¿Ý½ó-Ì8Í4½fðå¿ê~‹Jý9ûFwíå?2ËElEßO—ìÍÀ×Ídœmñ°ç †½PœØLáñž«>³d0^Œ÷ì¯)œR4‘qz^«<o%ÿ¹é‡Cr)&ÙGˆãHûJø¬"œ`üù¯| ÒóZf„/ù ¢éyj®ùV¤?Ú9£¿iá”wÆ ‚g 6ê(í*á³’pj/mȧٞ9Æ_í;èSFL±Tψ¼Í9"ÄÊS <ÏüªÌh2!:{Î,Ò×Jø¬"œÛ²XfžÛ¯]ã£5ÍïŽ0±V'=î>«'ÆaùÆ,l¼`„/ëwA›ÝÙSgøñs9†åÉyOù2ûäõYŽd9o$-çÚTêÏh…ÐûÚ:ƒk¿«NáÜxã(±QçžE¨VÊ`Îàª>ÕúÃøÒ½n†Ýò§N“ßbbý£ÊkQ  §Î+ùÚÇè¦ç8u×ñYTr4f0:wäë£JøÌr£cÆ©ÇÝ…E%G£pê’/ §î%} fœQä:íˆ Ĩ;ñF§#7C|1ãÔydÆéĨ’ãÏRú9!N5'_œãLu¨½“Ur´ûûû÷íð{…£>Õ„“|éZÉB¥úãããÛ7?ûçùùù?ŸÝvýJý¹»»{Nü^£JøJý!_ºžTâ Ø[ÄXªS8û$W"–8W ’¯¹ø §ç5)=[TJÝYúé<’¯}ŒXªÏå?¡RÂÙ'¹’0ð)í¨â<‹Jþ\-1¡p&ú]%G£pêÄ’/.Ó½¤oAáŒ"×iÇ@d F݉7:¹â‹ë8u/,f 60¬´&•ð™¥ôK?p"òµ3΀Cí5©ähÌ`tbÉ+ÝKXªG12·c 2Íβ1äNGn†øb©®óÈR݉Q%Çg©®“G¾Š”êÛ¯T·/Tƒ ÈAbýÄFpÎj3ʶ~À6'Ø;§½µß£_ðöG}ñÜŠ3N©(_ú™ýW™ã„£Igï9[dߟJ@2ƒÑo„/luÒöˆêù6ÞóÑþ &˜íïÖ>Q8u¤¢|égö[\E8¥hÂÁÛõÙïÞæ•€¤pêÎå þÒDÿ¶ 7ä¶S)„êZþÓv–Ì|„Âyžÿègö[œ.œrûÒ½-C['\#ˆ¢ë"ãJýY)¥8ö¦u®í?mŸr §#8Lgˆ¯´‡C2€÷æ2‘Q´ÒÆ3_U HfœzdDø’¾±'TÈ4›ÿ Š±‘þàÜHp½ÈôÒ^ßVºÑYñ÷ÚEùò^ÇbzÆ)炎:ÔJ1Ï\U% )œº»Eøúöí›é¦zÍo ô-ë pêHFüG?kÌ¢Œp¶yPük=*IáÔY‹ðÕ²;ctõÅ^Ï"ýÁ¹šH£=²NL1µÿÝÇœÂyŽÿègYœ.œòIh{(´íêvy‡u(QÇ·žßkW©?«¢ø@Y¾]Žñ‚y_ÒWžòC@=}Z…//û_žó{lON9±¿7'Ô&Û½K8*ÉŒSw»_rªG>hÜ.BÖç™ßŒòµ]Š$E{»Jdïah) ç9þ£Ÿ5fqºpÊÉ}Ü¡1/ÔîÄp4)¬Î‰+âV !FM Û‚øfñôdy!—¢ödÊŒØ:½@áÔ}?—~Ö˜Åé‰ne pz9…ÿ¶•€Œf0Ö±zíV D)œ{åÍ×óÀ&â?HÐî(»µ,ÁÛò¹ _^?õØGøòœßc{áD‡àppp9‘._—“Yƒu•€¤pê¬Eø’¹—MBÄä+˜zO~ZDúc9÷¶Ê²´¡pê(Å—~åK‹« çQç¤ã{WòÌ@Ì$6r®¬6¾ÚÍ/¬ËÞäX"ý±báí…SGöL¾ô«´(!œrÞȳ¨¸NÝõ"|5¥pêøf[DøÊîõntÞ~Ÿ.œmNèh’\ÎQyžŒ’Ø}ºWÉ`ÚŠ dž{‡,=¯íFü¸âçè½øHµ _^òØGøòœßc{ºpʉòž(Fœ¬ °Ì8u·‹ð%ýòœí슥ÌÑ|¹¼Îú 1…óÿÑϳ8]81¡/¿l#ïÔø]."¶.Ý pêd¯ˆr,¥ö"Åg,G’‰nJF¿¤hzÞ‚[‰/Ý;c‘oìJz«Ó…]Ж#ÉO…é]þmQ Hfœ:sQ¾ägåö¾ƒ ‘òLóŒð%ƒf¯?°ñ¬)¥pžç?ú™ýWNt«-v—&œ]fÞîGÑ{«}¥þ¬ˆ½ål-ŒÓZoyák»›úÒv4ˆôg5¾¬1ã±áËs‹íÕ„ÓÒ¯M% G2ï¸-ö D¥JþC¾Öà+í{œ:q‹JŽOáÔy$_ûQ8çòfœ:_f‹JÂÀ@Ôi#_ÇUÂg–Ä„§w•©H¾(œº—ô-˜qF‘ë´c 2£îÄŽÜ ñÅŒSç‘§£JŽ?Kéç„8Õœ|ùç¤)œ¬äh÷÷÷ï_ ÂïŽJøTNò¥{h%ÿ •êïŸäjƒùŒŸŸŸ_ñó×î]³RîîîÞ…¿WÀ¨>À£RÈ×wÕG+ñ l/?ÈÐfœÎ>É•ˆe Έäk.¾B©}âKOºs,*¥î,ýtNÉ×>F,ÕçòŸP©Náì“\Iø”v@ÔGqžE%®–˜P8ý®’£Q8ubÉ—é^Ò· pF‘ë´c 2£îÄŽÜ ñÅåH:3VZ“JøÌRú¥8ùÚgÀ¡öšTr4f0:±ä‹‚î%,Õ£™Û1ˆfgÙòF§#7C|±T×yd©îĨ’ã³T×É#_,Õu/ùa­° Þˆ•-#ƒÙ~å¼}áãŒ|,ÛTl·¯°ö-Òë¹½v|y¯©ÙWÂg–ÝMfœr›bÍ©z¯äh#ˆ¶5ÙÛOÿß³›dÃ*‚…3â‰9m"|å\¹–Jýáá_m7”‹8À ÄZÆ%E7“¶«$ö’s´·øµo,­_A_…/ §›Jø0ãŒ0¸Ó&‹X‚ÜDî–Kuyyzzê"ßD Óž#‹¯í5¿}ûö–{¶ã%=øfÛžÅW´Ÿ•úÃŒó‹-Ã’{bGÈXm\Ò!öæ2‘¶2Þ³£ãøÈ­ƒ¯µ=°†aäï#S+‘ëYÚœÁ—åº{6•úsóÂÙIJe+M"Ï@¬6.9¯ydÛ2t”òÖã |"%zëïý±b±µ£pêÈÍÀ×M<Â]ÛDpP8ÿ0aвtOyœíø?L4þôðûm‘Ýϵ)œ~´fàkyáD µ¬I–¤·.œr®·=Úº8°³f¦²m¦ãË¥cžé‚³úã—-˜qêfú~µc‹›-Õ÷J¼[N¹$kï µœ ö-Æ4BäIúŠ‚†{ôï|E¯ÝkW©?75ÇIáÌqã¶ ÈûÎz†ã·iÏúѽQgô'Ñׯ²Ž<ðÊêOµD ZnJ8-Nuë¥:JtMPä"ÏÛ:Úy5~ä2(ï{ò³f0&gþ}”¯ì¾Uê…sÃî­ §|0ÔÅh™ž‘1Ⱦ–éýÉÎqêhR8uŒLgyëÂ)—ú`Qfvø].—/Xå«eºY¥ìh,c¶ÚP8u¤fàkɇC:5|åiË‘ ž‘'Ú£ŽßDÛóé¸#ÎGûcñ'« …SGj¾(œ:3kV[ì.3L¼—ÑÚ{S;÷>rŠ "ÚœãÔØ¹üû_þ«é-*õ‡sœ:_f‹ˆ5æÃJøpŽS'˜|ícDáÔýÇlQÉÑXúé´‘¯cŒ*á3ËîfKu=Üö-*9…Sg’|Q8u/é[0ãŒ"×iÇ@d F݉7:¹â‹§ÎãÒ‡ÃW›TrüYJ?Ô Èç8Ot¯ß§®ähÌ`tÊÉ+ÝKKu,QA`~öÏããã+~>»íú•úóåË—÷qà÷ UÂxTêùÒõ¤_r™ž|!ã°T÷~]ˆö¿÷ñ!Ä‚>°–P8ìÍM§&ôú€Ç…oŒT(÷ؽ¤F÷÷÷¯(ÿðƒß‰› ·Ï‰|ÕæçÈ/ä+À¥zt•íˆ ·‚…óV˜æ8‰HCàÿ¾¯˜ˆvw‘\IEND®B`‚z3-z3-4.8.7/examples/python/tutorial/html/fixpoint-examples.htm000066400000000000000000002414031356505360400245670ustar00rootroot00000000000000 Z3Py Fixedpoints

Fixedpoints

This tutorial illustrates uses of Z3's fixedpoint engine. The following papers μZ - An Efficient Engine for Fixed-Points with Constraints. (CAV 2011) and Generalized Property Directed Reachability (SAT 2012) describe some of the main features of the engine.

Please send feedback, comments and/or corrections to nbjorner@microsoft.com.

Introduction

This tutorial covers some of the fixedpoint utilities available with Z3. The main features are a basic Datalog engine, an engine with relational algebra and an engine based on a generalization of the Property Directed Reachability algorithm.

Basic Datalog

The default fixed-point engine is a bottom-up Datalog engine. It works with finite relations and uses finite table representations as hash tables as the default way to represent finite relations.

Relations, rules and queries

The first example illustrates how to declare relations, rules and how to pose queries.
fp = Fixedpoint()

a, b, c = Bools('a b c')

fp.register_relation(a.decl(), b.decl(), c.decl())
fp.rule(a,b)
fp.rule(b,c)
fp.set(engine='datalog')

print "current set of rules\n", fp
print fp.query(a)

fp.fact(c)
print "updated set of rules\n", fp
print fp.query(a)
print fp.get_answer()
The example illustrates some of the basic constructs.
  fp = Fixedpoint()
creates a context for fixed-point computation.
 fp.register_relation(a.decl(), b.decl(), c.decl())
Register the relations a, b, c as recursively defined.
 fp.rule(a,b)
Create the rule that a follows from b. In general you can create a rule with multiple premises and a name using the format
 fp.rule(head,[body1,...,bodyN],name)
The name is optional. It is used for tracking the rule in derivation proofs. Continuing with the example, a is false unless b is established.
 fp.query(a)
Asks if a can be derived. The rules so far say that a follows if b is established and that b follows if c is established. But nothing establishes c and b is also not established, so a cannot be derived.
 fp.fact(c)
Add a fact (shorthand for fp.rule(c,True)). Now it is the case that a can be derived.

Explanations

It is also possible to get an explanation for a derived query. For the finite Datalog engine, an explanation is a trace that provides information of how a fact was derived. The explanation is an expression whose function symbols are Horn rules and facts used in the derivation.

fp = Fixedpoint()

a, b, c = Bools('a b c')

fp.register_relation(a.decl(), b.decl(), c.decl())
fp.rule(a,b)
fp.rule(b,c)
fp.fact(c)
fp.set(generate_explanations=True, engine='datalog')
print fp.query(a)
print fp.get_answer()

Relations with arguments

Relations can take arguments. We illustrate relations with arguments using edges and paths in a graph.

fp = Fixedpoint()
fp.set(engine='datalog')

s = BitVecSort(3)
edge = Function('edge', s, s, BoolSort())
path = Function('path', s, s, BoolSort())
a = Const('a',s)
b = Const('b',s)
c = Const('c',s)

fp.register_relation(path,edge)
fp.declare_var(a,b,c)
fp.rule(path(a,b), edge(a,b))
fp.rule(path(a,c), [edge(a,b),path(b,c)])

v1 = BitVecVal(1,s)
v2 = BitVecVal(2,s)
v3 = BitVecVal(3,s)
v4 = BitVecVal(4,s)

fp.fact(edge(v1,v2))
fp.fact(edge(v1,v3))
fp.fact(edge(v2,v4))

print "current set of rules", fp


print fp.query(path(v1,v4)), "yes we can reach v4 from v1"

print fp.query(path(v3,v4)), "no we cannot reach v4 from v3"
The example uses the declaration
 fp.declare_var(a,b,c)
to instrument the fixed-point engine that a, b, c should be treated as variables when they appear in rules. Think of the convention as they way bound variables are passed to quantifiers in Z3Py.

Rush Hour

A more entertaining example of using the basic fixed point engine is to solve the Rush Hour puzzle. The puzzle is about moving a red car out of a gridlock. We have encoded a configuration and compiled a set of rules that encode the legal moves of the cars given the configuration. Other configurations can be tested by changing the parameters passed to the constructor for Car. We have encoded the configuration from an online puzzle you can solve manually, or cheat on by asking Z3.

class Car():
    def __init__(self, is_vertical, base_pos, length, start, color):
	self.is_vertical = is_vertical
	self.base = base_pos
	self.length = length
	self.start = start
	self.color = color

    def __eq__(self, other):
	return self.color == other.color

    def __ne__(self, other):
	return self.color != other.color

dimension = 6

red_car = Car(False, 2, 2, 3, "red")
cars = [
    Car(True, 0, 3, 0, 'yellow'),
    Car(False, 3, 3, 0, 'blue'),
    Car(False, 5, 2, 0, "brown"),
    Car(False, 0, 2, 1, "lgreen"),
    Car(True,  1, 2, 1, "light blue"),
    Car(True,  2, 2, 1, "pink"),
    Car(True,  2, 2, 4, "dark green"),
    red_car,
    Car(True,  3, 2, 3, "purple"),
    Car(False, 5, 2, 3, "light yellow"),
    Car(True,  4, 2, 0, "orange"),
    Car(False, 4, 2, 4, "black"),
    Car(True,  5, 3, 1, "light purple")
    ]

num_cars = len(cars)
B = BoolSort()
bv3 = BitVecSort(3)


state = Function('state', [ bv3 for c in cars] + [B])

def num(i):
    return BitVecVal(i,bv3)

def bound(i):
    return Const(cars[i].color, bv3)

fp = Fixedpoint()
fp.set(generate_explanations=True)
fp.declare_var([bound(i) for i in range(num_cars)])
fp.register_relation(state)

def mk_state(car, value):
    return state([ (num(value) if (cars[i] == car) else bound(i))
		   for i in range(num_cars)])

def mk_transition(row, col, i0, j, car0):
    body = [mk_state(car0, i0)]
    for index in range(num_cars):
	car = cars[index]
	if car0 != car:
	    if car.is_vertical and car.base == col:
		for i in range(dimension):
		    if i <= row and row < i + car.length and i + car.length <= dimension:
			   body += [bound(index) != num(i)]
	    if car.base == row and not car.is_vertical:
		for i in range(dimension):
		    if i <= col and col < i + car.length and i + car.length <= dimension:
			   body += [bound(index) != num(i)]

    s = "%s %d->%d" % (car0.color, i0, j)
			
    fp.rule(mk_state(car0, j), body, s)
    

def move_down(i, car):
    free_row = i + car.length
    if free_row < dimension:
	mk_transition(free_row, car.base, i, i + 1, car)
            

def move_up(i, car):
    free_row = i  - 1
    if 0 <= free_row and i + car.length <= dimension:
	mk_transition(free_row, car.base, i, i - 1, car)

def move_left(i, car):
    free_col = i - 1;
    if 0 <= free_col and i + car.length <= dimension:
	mk_transition(car.base, free_col, i, i - 1, car)
	

def move_right(i, car):
    free_col = car.length + i
    if free_col < dimension:
	mk_transition(car.base, free_col, i, i + 1, car)
	

# Initial state:
fp.fact(state([num(cars[i].start) for i in range(num_cars)]))

# Transitions:
for car in cars:
    for i in range(dimension):
	if car.is_vertical:
	    move_down(i, car)
	    move_up(i, car)
	else:
	    move_left(i, car)
	    move_right(i, car)
    

def get_instructions(ans):
    lastAnd = ans.arg(0).children()[-1]
    trace = lastAnd.children()[1]
    while trace.num_args() > 0:
	print trace.decl()
	trace = trace.children()[-1]

print fp

goal = state([ (num(4) if cars[i] == red_car else bound(i))
	       for i in range(num_cars)])
fp.query(goal)

get_instructions(fp.get_answer())
    

Abstract Domains

The underlying engine uses table operations that are based on relational algebra. Representations are opaque to the underlying engine. Relational algebra operations are well defined for arbitrary relations. They don't depend on whether the relations denote a finite or an infinite set of values. Z3 contains two built-in tables for infinite domains. The first is for intervals of integers and reals. The second is for bound constraints between two integers or reals. A bound constraint is of the form x or x . When used in conjunction, they form an abstract domain that is called the Pentagon abstract domain. Z3 implements reduced products of abstract domains that enables sharing constraints between the interval and bounds domains.

Below we give a simple example that illustrates a loop at location l0. The loop is incremented as long as the loop counter does not exceed an upper bound. Using the combination of bound and interval domains we can collect derived invariants from the loop and we can also establish that the state after the loop does not exceed the bound.

I  = IntSort()
B  = BoolSort()
l0 = Function('l0',I,I,B)
l1 = Function('l1',I,I,B)

s = Fixedpoint()
s.set(engine='datalog',compile_with_widening=True,
      unbound_compressor=False)

s.register_relation(l0,l1)
s.set_predicate_representation(l0, 'interval_relation', 'bound_relation')
s.set_predicate_representation(l1, 'interval_relation', 'bound_relation')

m, x, y = Ints('m x y')

s.declare_var(m, x, y)
s.rule(l0(0,m),   0 < m)
s.rule(l0(x+1,m), [l0(x,m), x < m])
s.rule(l1(x,m),   [l0(x,m), m <= x])

print "At l0 we learn that x, y are non-negative:"
print s.query(l0(x,y))
print s.get_answer()

print "At l1 we learn that x <= y and both x and y are bigger than 0:"
print s.query(l1(x,y))
print s.get_answer()


print "The state where x < y is not reachable"
print s.query(And(l1(x,y), x < y))
The example uses the option
   set_option(dl_compile_with_widening=True)
to instrument Z3 to apply abstract interpretation widening on loop boundaries.

Engine for Property Directed Reachability

A different underlying engine for fixed-points is based on an algorithm for Property Directed Reachability (PDR). The PDR engine is enabled using the instruction
  set_option(dl_engine=1)
The version in Z3 applies to Horn clauses with arithmetic and Boolean domains. When using arithmetic you should enable the main abstraction engine used in Z3 for arithmetic in PDR.
 set_option(dl_pdr_use_farkas=True)
The engine also works with domains using algebraic data-types and bit-vectors, although it is currently not overly tuned for either. The PDR engine is targeted at applications from symbolic model checking of software. The systems may be infinite state. The following examples also serve a purpose of showing how software model checking problems (of safety properties) can be embedded into Horn clauses and solved using PDR.

Procedure Calls

McCarthy's 91 function illustrates a procedure that calls itself recursively twice. The Horn clauses below encode the recursive function:

  mc(x) = if x > 100 then x - 10 else mc(mc(x+11))
The general scheme for encoding recursive procedures is by creating a predicate for each procedure and adding an additional output variable to the predicate. Nested calls to procedures within a body can be encoded as a conjunction of relations.
mc = Function('mc', IntSort(), IntSort(), BoolSort())
n, m, p = Ints('n m p')

fp = Fixedpoint()

fp.declare_var(n,m)
fp.register_relation(mc)

fp.rule(mc(m, m-10), m > 100)
fp.rule(mc(m, n), [m <= 100, mc(m+11,p),mc(p,n)])
    
print fp.query(And(mc(m,n),n < 90))
print fp.get_answer()

print fp.query(And(mc(m,n),n < 91))
print fp.get_answer()

print fp.query(And(mc(m,n),n < 92))
print fp.get_answer()
The first two queries are unsatisfiable. The PDR engine produces the same proof of unsatisfiability. The proof is an inductive invariant for each recursive predicate. The PDR engine introduces a special query predicate for the query.

Bakery

We can also prove invariants of reactive systems. It is convenient to encode reactive systems as guarded transition systems. It is perhaps for some not as convenient to directly encode guarded transitions as recursive Horn clauses. But it is fairly easy to write a translator from guarded transition systems to recursive Horn clauses. We illustrate a translator and Lamport's two process Bakery algorithm in the next example.

set_option(relevancy=0,verbose=1)

def flatten(l):
    return [s for t in l for s in t]


class TransitionSystem():
    def __init__(self, initial, transitions, vars1):
	self.fp = Fixedpoint()        
	self.initial     = initial
	self.transitions = transitions
	self.vars1 = vars1

    def declare_rels(self):
	B = BoolSort()
	var_sorts   = [ v.sort() for v in self.vars1 ]
	state_sorts = var_sorts
	self.state_vals = [ v for v in self.vars1 ]
	self.state_sorts  = state_sorts
	self.var_sorts = var_sorts
	self.state  = Function('state', state_sorts + [ B ])
	self.step   = Function('step',  state_sorts + state_sorts + [ B ])
	self.fp.register_relation(self.state)
	self.fp.register_relation(self.step)

# Set of reachable states are transitive closure of step.

    def state0(self):
	idx = range(len(self.state_sorts))
	return self.state([Var(i,self.state_sorts[i]) for i in idx])
	
    def state1(self):
	n = len(self.state_sorts)
	return self.state([Var(i+n, self.state_sorts[i]) for i in range(n)])

    def rho(self):
	n = len(self.state_sorts)
	args1 = [ Var(i,self.state_sorts[i]) for i in range(n) ]
	args2 = [ Var(i+n,self.state_sorts[i]) for i in range(n) ]
	args = args1 + args2 
	return self.step(args)

    def declare_reachability(self):
	self.fp.rule(self.state1(), [self.state0(), self.rho()])


# Define transition relation

    def abstract(self, e):
	n = len(self.state_sorts)
	sub = [(self.state_vals[i], Var(i,self.state_sorts[i])) for i in range(n)]
	return substitute(e, sub)
	
    def declare_transition(self, tr):
	len_s  = len(self.state_sorts)
	effect = tr["effect"]
	vars1  = [Var(i,self.state_sorts[i]) for i in range(len_s)] + effect
	rho1  = self.abstract(self.step(vars1))
	guard = self.abstract(tr["guard"])
	self.fp.rule(rho1, guard)
	
    def declare_transitions(self):
	for t in self.transitions:
	    self.declare_transition(t)

    def declare_initial(self):
	self.fp.rule(self.state0(),[self.abstract(self.initial)])
	
    def query(self, query):
	self.declare_rels()
	self.declare_initial()
	self.declare_reachability()
	self.declare_transitions()
	query = And(self.state0(), self.abstract(query))
	print self.fp
	print query
	print self.fp.query(query)
	print self.fp.get_answer()
#	print self.fp.statistics()


L = Datatype('L')
L.declare('L0')
L.declare('L1')
L.declare('L2')
L = L.create()
L0 = L.L0
L1 = L.L1
L2 = L.L2


y0 = Int('y0')
y1 = Int('y1')
l  = Const('l', L)
m  = Const('m', L)


t1 = { "guard" : l == L0,
       "effect" : [ L1, y1 + 1, m, y1 ] }
t2 = { "guard" : And(l == L1, Or([y0 <= y1, y1 == 0])),
       "effect" : [ L2, y0,     m, y1 ] }
t3 = { "guard" : l == L2,
       "effect" : [ L0, IntVal(0), m, y1 ]}
s1 = { "guard" : m == L0,
       "effect" : [ l,  y0, L1, y0 + 1 ] }
s2 = { "guard" : And(m == L1, Or([y1 <= y0, y0 == 0])),
       "effect" : [ l,  y0, L2, y1 ] }
s3 = { "guard" : m == L2,
       "effect" : [ l,  y0, L0, IntVal(0) ]}


ptr = TransitionSystem( And(l == L0, y0 == 0, m == L0, y1 == 0),
			[t1, t2, t3, s1, s2, s3],
			[l, y0, m, y1])

ptr.query(And([l == L2, m == L2 ]))
The rather verbose (and in no way minimal) inductive invariants are produced as answers.

Functional Programs

We can also verify some properties of functional programs using Z3's generalized PDR. Let us here consider an example from Predicate Abstraction and CEGAR for Higher-Order Model Checking, Kobayashi et.al. PLDI 2011. We encode functional programs by taking a suitable operational semantics and encoding an evaluator that is specialized to the program being verified (we don't encode a general purpose evaluator, you should partial evaluate it to help verification). We use algebraic data-types to encode the current closure that is being evaluated.
# let max max2 x y z = max2 (max2 x y) z
# let f x y = if x > y then x else y
# assert (f (max f x y z) x) = (max f x y z)


Expr = Datatype('Expr')
Expr.declare('Max')
Expr.declare('f')
Expr.declare('I', ('i', IntSort()))
Expr.declare('App', ('fn',Expr),('arg',Expr))
Expr = Expr.create()
Max  = Expr.Max
I    = Expr.I
App  = Expr.App
f    = Expr.f
Eval = Function('Eval',Expr,Expr,Expr,BoolSort())

x   = Const('x',Expr)
y   = Const('y',Expr)
z   = Const('z',Expr)
r1  = Const('r1',Expr)
r2  = Const('r2',Expr)
max = Const('max',Expr)
xi  = Const('xi',IntSort())
yi  = Const('yi',IntSort())

fp = Fixedpoint()
fp.register_relation(Eval)
fp.declare_var(x,y,z,r1,r2,max,xi,yi)

# Max max x y z = max (max x y) z
fp.rule(Eval(App(App(App(Max,max),x),y), z, r2),
	[Eval(App(max,x),y,r1),
	 Eval(App(max,r1),z,r2)])

# f x y = x if x >= y
# f x y = y if x < y
fp.rule(Eval(App(f,I(xi)),I(yi),I(xi)),xi >= yi)
fp.rule(Eval(App(f,I(xi)),I(yi),I(yi)),xi < yi)

print fp.query(And(Eval(App(App(App(Max,f),x),y),z,r1),
		   Eval(App(f,x),r1,r2),
		   r1 != r2))

print fp.get_answer()
z3-z3-4.8.7/examples/python/tutorial/html/guide-examples.htm000066400000000000000000003134061356505360400240270ustar00rootroot00000000000000 Z3Py Guide

Z3 API in Python

Z3 is a high performance theorem prover developed at Microsoft Research. Z3 is used in many applications such as: software/hardware verification and testing, constraint solving, analysis of hybrid systems, security, biology (in silico analysis), and geometrical problems.

This tutorial demonstrates the main capabilities of Z3Py: the Z3 API in Python. No Python background is needed to read this tutorial. However, it is useful to learn Python (a fun language!) at some point, and there are many excellent free resources for doing so (Python Tutorial).

The Z3 distribution also contains the C, .Net and OCaml APIs. The source code of Z3Py is available in the Z3 distribution, feel free to modify it to meet your needs. The source code also demonstrates how to use new features in Z3 4.0. Other cool front-ends for Z3 include Scala^Z3 and SBV.

Be sure to follow along with the examples by clicking the load in editor link in the corner. See what Z3Py says, try your own scripts, and experiment!

Please send feedback, comments and/or corrections to leonardo@microsoft.com. Your comments are very valuable.

Getting Started

Let us start with the following simple example:

x = Int('x')
y = Int('y')
solve(x > 2, y < 10, x + 2*y == 7)

The function Int('x') creates an integer variable in Z3 named x. The solve function solves a system of constraints. The example above uses two variables x and y, and three constraints. Z3Py like Python uses = for assignment. The operators <, <=, >, >=, == and != for comparison. In the example above, the expression x + 2*y == 7 is a Z3 constraint. Z3 can solve and crunch formulas.

The next examples show how to use the Z3 formula/expression simplifier.

x = Int('x')
y = Int('y')
print simplify(x + y + 2*x + 3)
print simplify(x < y + x + 2)
print simplify(And(x + 1 >= 3, x**2 + x**2 + y**2 + 2 >= 5))

By default, Z3Py (for the web) displays formulas and expressions using mathematical notation. As usual, is the logical and, is the logical or, and so on. The command set_option(html_mode=False) makes all formulas and expressions to be displayed in Z3Py notation. This is also the default mode for the offline version of Z3Py that comes with the Z3 distribution.

x = Int('x')
y = Int('y')
print x**2 + y**2 >= 1
set_option(html_mode=False)
print x**2 + y**2 >= 1

Z3 provides functions for traversing expressions.

x = Int('x')
y = Int('y')
n = x + y >= 3
print "num args: ", n.num_args()
print "children: ", n.children()
print "1st child:", n.arg(0)
print "2nd child:", n.arg(1)
print "operator: ", n.decl()
print "op name:  ", n.decl().name()

Z3 provides all basic mathematical operations. Z3Py uses the same operator precedence of the Python language. Like Python, ** is the power operator. Z3 can solve nonlinear polynomial constraints.

x = Real('x')
y = Real('y')
solve(x**2 + y**2 > 3, x**3 + y < 5)

The procedure Real('x') creates the real variable x. Z3Py can represent arbitrarily large integers, rational numbers (like in the example above), and irrational algebraic numbers. An irrational algebraic number is a root of a polynomial with integer coefficients. Internally, Z3 represents all these numbers precisely. The irrational numbers are displayed in decimal notation for making it easy to read the results.

x = Real('x')
y = Real('y')
solve(x**2 + y**2 == 3, x**3 == 2)

set_option(precision=30)
print "Solving, and displaying result with 30 decimal places"
solve(x**2 + y**2 == 3, x**3 == 2)

The procedure set_option is used to configure the Z3 environment. It is used to set global configuration options such as how the result is displayed. The option set_option(precision=30) sets the number of decimal places used when displaying results. The ? mark in 1.2599210498? indicates the output is truncated.

The following example demonstrates a common mistake. The expression 3/2 is a Python integer and not a Z3 rational number. The example also shows different ways to create rational numbers in Z3Py. The procedure Q(num, den) creates a Z3 rational where num is the numerator and den is the denominator. The RealVal(1) creates a Z3 real number representing the number 1.

print 1/3
print RealVal(1)/3
print Q(1,3)

x = Real('x')
print x + 1/3
print x + Q(1,3)
print x + "1/3"
print x + 0.25

Rational numbers can also be displayed in decimal notation.

x = Real('x')
solve(3*x == 1)

set_option(rational_to_decimal=True)
solve(3*x == 1)

set_option(precision=30)
solve(3*x == 1)

A system of constraints may not have a solution. In this case, we say the system is unsatisfiable.

x = Real('x')
solve(x > 4, x < 0)

Like in Python, comments begin with the hash character # and are terminated by the end of line. Z3Py does not support comments that span more than one line.

# This is a comment
x = Real('x') # comment: creating x
print x**2 + 2*x + 2  # comment: printing polynomial

Boolean Logic

Z3 supports Boolean operators: And, Or, Not, Implies (implication), If (if-then-else). Bi-implications are represented using equality ==. The following example shows how to solve a simple set of Boolean constraints.

p = Bool('p')
q = Bool('q')
r = Bool('r')
solve(Implies(p, q), r == Not(q), Or(Not(p), r))

The Python Boolean constants True and False can be used to build Z3 Boolean expressions.

p = Bool('p')
q = Bool('q')
print And(p, q, True)
print simplify(And(p, q, True))
print simplify(And(p, False))

The following example uses a combination of polynomial and Boolean constraints.

p = Bool('p')
x = Real('x')
solve(Or(x < 5, x > 10), Or(p, x**2 == 2), Not(p))

Solvers

Z3 provides different solvers. The command solve, used in the previous examples, is implemented using the Z3 solver API. The implementation can be found in the file z3.py in the Z3 distribution. The following example demonstrates the basic Solver API.

x = Int('x')
y = Int('y')

s = Solver()
print s

s.add(x > 10, y == x + 2)
print s
print "Solving constraints in the solver s ..."
print s.check()

print "Create a new scope..."
s.push()
s.add(y < 11)
print s
print "Solving updated set of constraints..."
print s.check()

print "Restoring state..."
s.pop()
print s
print "Solving restored set of constraints..."
print s.check()

The command Solver() creates a general purpose solver. Constraints can be added using the method add. We say the constraints have been asserted in the solver. The method check() solves the asserted constraints. The result is sat (satisfiable) if a solution was found. The result is unsat (unsatisfiable) if no solution exists. We may also say the system of asserted constraints is infeasible. Finally, a solver may fail to solve a system of constraints and unknown is returned.

In some applications, we want to explore several similar problems that share several constraints. We can use the commands push and pop for doing that. Each solver maintains a stack of assertions. The command push creates a new scope by saving the current stack size. The command pop removes any assertion performed between it and the matching push. The check method always operates on the content of solver assertion stack.

The following example shows an example that Z3 cannot solve. The solver returns unknown in this case. Recall that Z3 can solve nonlinear polynomial constraints, but 2**x is not a polynomial.

x = Real('x')
s = Solver()
s.add(2**x == 3)
print s.check()

The following example shows how to traverse the constraints asserted into a solver, and how to collect performance statistics for the check method.

x = Real('x')
y = Real('y')
s = Solver()
s.add(x > 1, y > 1, Or(x + y > 3, x - y < 2))
print "asserted constraints..."
for c in s.assertions():
    print c

print s.check()
print "statistics for the last check method..."
print s.statistics()
# Traversing statistics
for k, v in s.statistics():
    print "%s : %s" % (k, v)

The command check returns sat when Z3 finds a solution for the set of asserted constraints. We say Z3 satisfied the set of constraints. We say the solution is a model for the set of asserted constraints. A model is an interpretation that makes each asserted constraint true. The following example shows the basic methods for inspecting models.

x, y, z = Reals('x y z')
s = Solver()
s.add(x > 1, y > 1, x + y > 3, z - x < 10)
print s.check()

m = s.model()
print "x = %s" % m[x]

print "traversing model..."
for d in m.decls():
    print "%s = %s" % (d.name(), m[d])

In the example above, the function Reals('x y z') creates the variables. x, y and z. It is shorthand for:

x = Real('x')
y = Real('y')
z = Real('z')

The expression m[x] returns the interpretation of x in the model m. The expression "%s = %s" % (d.name(), m[d]) returns a string where the first %s is replaced with the name of d (i.e., d.name()), and the second %s with a textual representation of the interpretation of d (i.e., m[d]). Z3Py automatically converts Z3 objects into a textual representation when needed.

Arithmetic

Z3 supports real and integer variables. They can be mixed in a single problem. Like most programming languages, Z3Py will automatically add coercions converting integer expressions to real ones when needed. The following example demonstrates different ways to declare integer and real variables.

x = Real('x')
y = Int('y')
a, b, c = Reals('a b c')
s, r = Ints('s r')
print x + y + 1 + (a + s)
print ToReal(y) + c

The function ToReal casts an integer expression into a real expression.

Z3Py supports all basic arithmetic operations.

a, b, c = Ints('a b c')
d, e = Reals('d e')
solve(a > b + 2,
      a == 2*c + 10,
      c + b <= 1000,
      d >= e)

The command simplify applies simple transformations on Z3 expressions.

x, y = Reals('x y')
# Put expression in sum-of-monomials form
t = simplify((x + y)**3, som=True)
print t
# Use power operator
t = simplify(t, mul_to_power=True)
print t

The command help_simplify() prints all available options. Z3Py allows users to write option in two styles. The Z3 internal option names start with : and words are separated by -. These options can be used in Z3Py. Z3Py also supports Python-like names, where : is suppressed and - is replaced with _. The following example demonstrates how to use both styles.

x, y = Reals('x y')
# Using Z3 native option names
print simplify(x == y + 2, ':arith-lhs', True)
# Using Z3Py option names
print simplify(x == y + 2, arith_lhs=True)

print "\nAll available options:"
help_simplify()

Z3Py supports arbitrarily large numbers. The following example demonstrates how to perform basic arithmetic using larger integer, rational and irrational numbers. Z3Py only supports algebraic irrational numbers. Algebraic irrational numbers are sufficient for presenting the solutions of systems of polynomial constraints. Z3Py will always display irrational numbers in decimal notation since it is more convenient to read. The internal representation can be extracted using the method sexpr(). It displays Z3 internal representation for mathematical formulas and expressions in s-expression (Lisp-like) notation.

x, y = Reals('x y')
solve(x + 10000000000000000000000 == y, y > 20000000000000000)

print Sqrt(2) + Sqrt(3)
print simplify(Sqrt(2) + Sqrt(3))
print simplify(Sqrt(2) + Sqrt(3)).sexpr()
# The sexpr() method is available for any Z3 expression
print (x + Sqrt(y) * 2).sexpr()

Machine Arithmetic

Modern CPUs and main-stream programming languages use arithmetic over fixed-size bit-vectors. Machine arithmetic is available in Z3Py as Bit-Vectors. They implement the precise semantics of unsigned and of signed two-complements arithmetic.

The following example demonstrates how to create bit-vector variables and constants. The function BitVec('x', 16) creates a bit-vector variable in Z3 named x with 16 bits. For convenience, integer constants can be used to create bit-vector expressions in Z3Py. The function BitVecVal(10, 32) creates a bit-vector of size 32 containing the value 10.

x = BitVec('x', 16)
y = BitVec('y', 16)
print x + 2
# Internal representation
print (x + 2).sexpr()

# -1 is equal to 65535 for 16-bit integers 
print simplify(x + y - 1)

# Creating bit-vector constants
a = BitVecVal(-1, 16)
b = BitVecVal(65535, 16)
print simplify(a == b)

a = BitVecVal(-1, 32)
b = BitVecVal(65535, 32)
# -1 is not equal to 65535 for 32-bit integers 
print simplify(a == b)

In contrast to programming languages, such as C, C++, C#, Java, there is no distinction between signed and unsigned bit-vectors as numbers. Instead, Z3 provides special signed versions of arithmetical operations where it makes a difference whether the bit-vector is treated as signed or unsigned. In Z3Py, the operators <, <=, >, >=, /, % and >> correspond to the signed versions. The corresponding unsigned operators are ULT, ULE, UGT, UGE, UDiv, URem and LShR.

# Create to bit-vectors of size 32
x, y = BitVecs('x y', 32)

solve(x + y == 2, x > 0, y > 0)

# Bit-wise operators
# & bit-wise and
# | bit-wise or
# ~ bit-wise not
solve(x & y == ~y)

solve(x < 0)

# using unsigned version of < 
solve(ULT(x, 0))

The operator >> is the arithmetic shift right, and << is the shift left. The logical shift right is the operator LShR.

# Create to bit-vectors of size 32
x, y = BitVecs('x y', 32)

solve(x >> 2 == 3)

solve(x << 2 == 3)

solve(x << 2 == 24)

Functions

Unlike programming languages, where functions have side-effects, can throw exceptions, or never return, functions in Z3 have no side-effects and are total. That is, they are defined on all input values. This includes functions, such as division. Z3 is based on first-order logic.

Given a constraints such as x + y > 3, we have been saying that x and y are variables. In many textbooks, x and y are called uninterpreted constants. That is, they allow any interpretation that is consistent with the constraint x + y > 3.

More precisely, function and constant symbols in pure first-order logic are uninterpreted or free, which means that no a priori interpretation is attached. This is in contrast to functions belonging to the signature of theories, such as arithmetic where the function + has a fixed standard interpretation (it adds two numbers). Uninterpreted functions and constants are maximally flexible; they allow any interpretation that is consistent with the constraints over the function or constant.

To illustrate uninterpreted functions and constants let us the uninterpreted integer constants (aka variables) x, y. Finally let f be an uninterpreted function that takes one argument of type (aka sort) integer and results in an integer value. The example illustrates how one can force an interpretation where f applied twice to x results in x again, but f applied once to x is different from x.

x = Int('x')
y = Int('y')
f = Function('f', IntSort(), IntSort())
solve(f(f(x)) == x, f(x) == y, x != y)

The solution (interpretation) for f should be read as f(0) is 1, f(1) is 0, and f(a) is 1 for all a different from 0 and 1.

In Z3, we can also evaluate expressions in the model for a system of constraints. The following example shows how to use the evaluate method.

x = Int('x')
y = Int('y')
f = Function('f', IntSort(), IntSort())
s = Solver()
s.add(f(f(x)) == x, f(x) == y, x != y)
print s.check()
m = s.model()
print "f(f(x)) =", m.evaluate(f(f(x)))
print "f(x)    =", m.evaluate(f(x))

Satisfiability and Validity

A formula/constraint F is valid if F always evaluates to true for any assignment of appropriate values to its uninterpreted symbols. A formula/constraint F is satisfiable if there is some assignment of appropriate values to its uninterpreted symbols under which F evaluates to true. Validity is about finding a proof of a statement; satisfiability is about finding a solution to a set of constraints. Consider a formula F containing a and b. We can ask whether F is valid, that is whether it is always true for any combination of values for a and b. If F is always true, then Not(F) is always false, and then Not(F) will not have any satisfying assignment (i.e., solution); that is, Not(F) is unsatisfiable. That is, F is valid precisely when Not(F) is not satisfiable (is unsatisfiable). Alternately, F is satisfiable if and only if Not(F) is not valid (is invalid). The following example proves the deMorgan's law.

The following example redefines the Z3Py function prove that receives a formula as a parameter. This function creates a solver, adds/asserts the negation of the formula, and check if the negation is unsatisfiable. The implementation of this function is a simpler version of the Z3Py command prove.

p, q = Bools('p q')
demorgan = And(p, q) == Not(Or(Not(p), Not(q)))
print demorgan

def prove(f):
    s = Solver()
    s.add(Not(f))
    if s.check() == unsat:
        print "proved"
    else:
        print "failed to prove"

print "Proving demorgan..."
prove(demorgan)

List Comprehensions

Python supports list comprehensions. List comprehensions provide a concise way to create lists. They can be used to create Z3 expressions and problems in Z3Py. The following example demonstrates how to use Python list comprehensions in Z3Py.

# Create list [1, ..., 5] 
print [ x + 1 for x in range(5) ]

# Create two lists containing 5 integer variables
X = [ Int('x%s' % i) for i in range(5) ]
Y = [ Int('y%s' % i) for i in range(5) ]
print X

# Create a list containing X[i]+Y[i]
X_plus_Y = [ X[i] + Y[i] for i in range(5) ]
print X_plus_Y

# Create a list containing X[i] > Y[i]
X_gt_Y = [ X[i] > Y[i] for i in range(5) ]
print X_gt_Y

print And(X_gt_Y)

# Create a 3x3 "matrix" (list of lists) of integer variables
X = [ [ Int("x_%s_%s" % (i+1, j+1)) for j in range(3) ]
      for i in range(3) ]
pp(X)

In the example above, the expression "x%s" % i returns a string where %s is replaced with the value of i.

The command pp is similar to print, but it uses Z3Py formatter for lists and tuples instead of Python's formatter.

Z3Py also provides functions for creating vectors of Boolean, Integer and Real variables. These functions are implemented using list comprehensions.

X = IntVector('x', 5)
Y = RealVector('y', 5)
P = BoolVector('p', 5)
print X
print Y
print P
print [ y**2 for y in Y ]
print Sum([ y**2 for y in Y ])

Kinematic Equations

In high school, students learn the kinematic equations. These equations describe the mathematical relationship between displacement (d), time (t), acceleration (a), initial velocity (v_i) and final velocity (v_f). In Z3Py notation, we can write these equations as:

   d == v_i * t + (a*t**2)/2,
   v_f == v_i + a*t

Problem 1

Ima Hurryin is approaching a stoplight moving with a velocity of 30.0 m/s. The light turns yellow, and Ima applies the brakes and skids to a stop. If Ima's acceleration is -8.00 m/s2, then determine the displacement of the car during the skidding process.

d, a, t, v_i, v_f = Reals('d a t v__i v__f')

equations = [
   d == v_i * t + (a*t**2)/2,
   v_f == v_i + a*t,
]
print "Kinematic equations:"
print equations

# Given v_i, v_f and a, find d
problem = [
    v_i == 30,
    v_f == 0,
    a   == -8
]
print "Problem:"
print problem

print "Solution:"
solve(equations + problem)

Problem 2

Ben Rushin is waiting at a stoplight. When it finally turns green, Ben accelerated from rest at a rate of a 6.00 m/s2 for a time of 4.10 seconds. Determine the displacement of Ben's car during this time period.

d, a, t, v_i, v_f = Reals('d a t v__i v__f')

equations = [
   d == v_i * t + (a*t**2)/2,
   v_f == v_i + a*t,
]

# Given v_i, t and a, find d
problem = [
    v_i == 0,
    t   == 4.10,
    a   == 6
]

solve(equations + problem)

# Display rationals in decimal notation
set_option(rational_to_decimal=True)

solve(equations + problem)

Bit Tricks

Some low level hacks are very popular with C programmers. We use some of these hacks in the Z3 implementation.

Power of two

This hack is frequently used in C programs (Z3 included) to test whether a machine integer is a power of two. We can use Z3 to prove it really works. The claim is that x != 0 && !(x & (x - 1)) is true if and only if x is a power of two.

x      = BitVec('x', 32)
powers = [ 2**i for i in range(32) ]
fast   = And(x != 0, x & (x - 1) == 0)
slow   = Or([ x == p for p in powers ])
print fast
prove(fast == slow)

print "trying to prove buggy version..."
fast   = x & (x - 1) == 0
prove(fast == slow)

Opposite signs

The following simple hack can be used to test whether two machine integers have opposite signs.

x      = BitVec('x', 32)
y      = BitVec('y', 32)

# Claim: (x ^ y) < 0 iff x and y have opposite signs
trick  = (x ^ y) < 0

# Naive way to check if x and y have opposite signs
opposite = Or(And(x < 0, y >= 0),
              And(x >= 0, y < 0))

prove(trick == opposite)

Puzzles

Dog, Cat and Mouse

Consider the following puzzle. Spend exactly 100 dollars and buy exactly 100 animals. Dogs cost 15 dollars, cats cost 1 dollar, and mice cost 25 cents each. You have to buy at least one of each. How many of each should you buy?

# Create 3 integer variables
dog, cat, mouse = Ints('dog cat mouse')
solve(dog >= 1,   # at least one dog
      cat >= 1,   # at least one cat
      mouse >= 1, # at least one mouse
      # we want to buy 100 animals
      dog + cat + mouse == 100,
      # We have 100 dollars (10000 cents):
      #   dogs cost 15 dollars (1500 cents), 
      #   cats cost 1 dollar (100 cents), and 
      #   mice cost 25 cents 
      1500 * dog + 100 * cat + 25 * mouse == 10000)

Sudoku

Sudoku is a very popular puzzle. The goal is to insert the numbers in the boxes to satisfy only one condition: each row, column and 3x3 box must contain the digits 1 through 9 exactly once.

The following example encodes the sudoku problem in Z3. Different sudoku instances can be solved by modifying the matrix instance. This example makes heavy use of list comprehensions available in the Python programming language.

# 9x9 matrix of integer variables
X = [ [ Int("x_%s_%s" % (i+1, j+1)) for j in range(9) ]
      for i in range(9) ]

# each cell contains a value in {1, ..., 9}
cells_c  = [ And(1 <= X[i][j], X[i][j] <= 9)
             for i in range(9) for j in range(9) ]

# each row contains a digit at most once
rows_c   = [ Distinct(X[i]) for i in range(9) ]

# each column contains a digit at most once
cols_c   = [ Distinct([ X[i][j] for i in range(9) ])
             for j in range(9) ]

# each 3x3 square contains a digit at most once
sq_c     = [ Distinct([ X[3*i0 + i][3*j0 + j]
                        for i in range(3) for j in range(3) ])
             for i0 in range(3) for j0 in range(3) ]

sudoku_c = cells_c + rows_c + cols_c + sq_c

# sudoku instance, we use '0' for empty cells
instance = ((0,0,0,0,9,4,0,3,0),
            (0,0,0,5,1,0,0,0,7),
            (0,8,9,0,0,0,0,4,0),
            (0,0,0,0,0,0,2,0,8),
            (0,6,0,2,0,1,0,5,0),
            (1,0,2,0,0,0,0,0,0),
            (0,7,0,0,0,0,5,2,0),
            (9,0,0,0,6,5,0,0,0),
            (0,4,0,9,7,0,0,0,0))

instance_c = [ If(instance[i][j] == 0,
                  True,
                  X[i][j] == instance[i][j])
               for i in range(9) for j in range(9) ]

s = Solver()
s.add(sudoku_c + instance_c)
if s.check() == sat:
    m = s.model()
    r = [ [ m.evaluate(X[i][j]) for j in range(9) ]
          for i in range(9) ]
    print_matrix(r)
else:
    print "failed to solve"

Eight Queens

The eight queens puzzle is the problem of placing eight chess queens on an 8x8 chessboard so that no two queens attack each other. Thus, a solution requires that no two queens share the same row, column, or diagonal.

# We know each queen must be in a different row.
# So, we represent each queen by a single integer: the column position
Q = [ Int('Q_%i' % (i + 1)) for i in range(8) ]

# Each queen is in a column {1, ... 8 }
val_c = [ And(1 <= Q[i], Q[i] <= 8) for i in range(8) ]

# At most one queen per column
col_c = [ Distinct(Q) ]

# Diagonal constraint
diag_c = [ If(i == j,
              True,
              And(Q[i] - Q[j] != i - j, Q[i] - Q[j] != j - i))
           for i in range(8) for j in range(i) ]

solve(val_c + col_c + diag_c)

Application: Install Problem

The install problem consists of determining whether a new set of packages can be installed in a system. This application is based on the article OPIUM: Optimal Package Install/Uninstall Manager. Many packages depend on other packages to provide some functionality. Each distribution contains a meta-data file that explicates the requirements of each package of the distribution. The meta-data contains details like the name, version, etc. More importantly, it contains depends and conflicts clauses that stipulate which other packages should be on the system. The depends clauses stipulate which other packages must be present. The conflicts clauses stipulate which other packages must not be present.

The install problem can be easily solved using Z3. The idea is to define a Boolean variable for each package. This variable is true if the package must be in the system. If package a depends on packages b, c and z, we write:

DependsOn(a, [b, c, z])

DependsOn is a simple Python function that creates Z3 constraints that capture the depends clause semantics.

def DependsOn(pack, deps):
   return And([ Implies(pack, dep) for dep in deps ])

Thus, Depends(a, [b, c, z]) generates the constraint

And(Implies(a, b), Implies(a, c), Implies(a, z))

That is, if users install package a, they must also install packages b, c and z.

If package d conflicts with package e, we write Conflict(d, e). Conflict is also a simple Python function.

def Conflict(p1, p2):
    return Or(Not(p1), Not(p2))

Conflict(d, e) generates the constraint Or(Not(d), Not(e)). With these two functions, we can easily encode the example in the Opium article (Section 2) in Z3Py as:

def DependsOn(pack, deps):
    return And([ Implies(pack, dep) for dep in deps ])

def Conflict(p1, p2):
    return Or(Not(p1), Not(p2))

a, b, c, d, e, f, g, z = Bools('a b c d e f g z')

solve(DependsOn(a, [b, c, z]),
      DependsOn(b, [d]),
      DependsOn(c, [Or(d, e), Or(f, g)]),
      Conflict(d, e),
      a, z)

Note that the example contains the constraint

DependsOn(c, [Or(d, e), Or(f, g)]),

The meaning is: to install c, we must install d or e, and f or g

Now, we refine the previous example. First, we modify DependsOn to allow us to write DependsOn(b, d) instead of DependsOn(b, [d]). We also write a function install_check that returns a list of packages that must be installed in the system. The function Conflict is also modified. It can now receive multiple arguments.

def DependsOn(pack, deps):
    if is_expr(deps):
        return Implies(pack, deps)
    else:
        return And([ Implies(pack, dep) for dep in deps ])

def Conflict(*packs):
    return Or([ Not(pack) for pack in packs ])

a, b, c, d, e, f, g, z = Bools('a b c d e f g z')

def install_check(*problem):
    s = Solver()
    s.add(*problem)
    if s.check() == sat:
        m = s.model()
        r = []
        for x in m:
            if is_true(m[x]):
                # x is a Z3 declaration
                # x() returns the Z3 expression
                # x.name() returns a string
                r.append(x())
        print r
    else:
        print "invalid installation profile"

print "Check 1"
install_check(DependsOn(a, [b, c, z]),
              DependsOn(b, d),
              DependsOn(c, [Or(d, e), Or(f, g)]),
              Conflict(d, e),
              Conflict(d, g),
              a, z)

print "Check 2"
install_check(DependsOn(a, [b, c, z]),
              DependsOn(b, d),
              DependsOn(c, [Or(d, e), Or(f, g)]),
              Conflict(d, e),
              Conflict(d, g),
              a, z, g)

Using Z3Py Locally

Z3Py is part of the Z3 distribution. It is located in the python subdirectory. To use it locally, you have to include the following command in your Python script.

from Z3 import *

The Z3 Python frontend directory must be in your PYTHONPATH environment variable. Z3Py will automatically search for the Z3 library (z3.dll (Windows), libz3.so (Linux), or libz3.dylib (OSX)). You may also initialize Z3Py manually using the command:

init("z3.dll")
z3-z3-4.8.7/examples/python/tutorial/html/index.html000066400000000000000000000001211356505360400223640ustar00rootroot00000000000000z3-z3-4.8.7/examples/python/tutorial/html/strategies-examples.htm000066400000000000000000000744001356505360400251020ustar00rootroot00000000000000 Z3Py Strategies

Strategies

High-performance solvers, such as Z3, contain many tightly integrated, handcrafted heuristic combinations of algorithmic proof methods. While these heuristic combinations tend to be highly tuned for known classes of problems, they may easily perform very badly on new classes of problems. This issue is becoming increasingly pressing as solvers begin to gain the attention of practitioners in diverse areas of science and engineering. In many cases, changes to the solver heuristics can make a tremendous difference.

In this tutorial we show how to create custom strategies using the basic building blocks available in Z3. Z3Py and Z3 4.0 implement the ideas proposed in this article.

Please send feedback, comments and/or corrections to leonardo@microsoft.com. Your comments are very valuable.

Introduction

Z3 implements a methodology for orchestrating reasoning engines where "big" symbolic reasoning steps are represented as functions known as tactics, and tactics are composed using combinators known as tacticals. Tactics process sets of formulas called Goals.

When a tactic is applied to some goal G, four different outcomes are possible. The tactic succeeds in showing G to be satisfiable (i.e., feasible); succeeds in showing G to be unsatisfiable (i.e., infeasible); produces a sequence of subgoals; or fails. When reducing a goal G to a sequence of subgoals G1, ..., Gn, we face the problem of model conversion. A model converter construct a model for G using a model for some subgoal Gi.

In the following example, we create a goal g consisting of three formulas, and a tactic t composed of two built-in tactics: simplify and solve-eqs. The tactic simplify apply transformations equivalent to the ones found in the command simplify. The tactic solver-eqs eliminate variables using Gaussian elimination. Actually, solve-eqs is not restricted only to linear arithmetic. It can also eliminate arbitrary variables. Then, combinator Then applies simplify to the input goal and solve-eqs to each subgoal produced by simplify. In this example, only one subgoal is produced.

x, y = Reals('x y')
g  = Goal()
g.add(x > 0, y > 0, x == y + 2)
print g

t1 = Tactic('simplify')
t2 = Tactic('solve-eqs')
t  = Then(t1, t2)
print t(g)

In the example above, variable x is eliminated, and is not present the resultant goal.

In Z3, we say a clause is any constraint of the form Or(f_1, ..., f_n). The tactic split-clause will select a clause Or(f_1, ..., f_n) in the input goal, and split it n subgoals. One for each subformula f_i.

x, y = Reals('x y')
g  = Goal()
g.add(Or(x < 0, x > 0), x == y + 1, y < 0)

t = Tactic('split-clause')
r = t(g)
for g in r: 
    print g

Tactics

Z3 comes equipped with many built-in tactics. The command describe_tactics() provides a short description of all built-in tactics.

describe_tactics()

Z3Py comes equipped with the following tactic combinators (aka tacticals):

  • Then(t, s) applies t to the input goal and s to every subgoal produced by t.
  • OrElse(t, s) first applies t to the given goal, if it fails then returns the result of s applied to the given goal.
  • Repeat(t) Keep applying the given tactic until no subgoal is modified by it.
  • Repeat(t, n) Keep applying the given tactic until no subgoal is modified by it, or the number of iterations is greater than n.
  • TryFor(t, ms) Apply tactic t to the input goal, if it does not return in ms milliseconds, it fails.
  • With(t, params) Apply the given tactic using the given parameters.

The following example demonstrate how to use these combinators.

x, y, z = Reals('x y z')
g = Goal()
g.add(Or(x == 0, x == 1), 
      Or(y == 0, y == 1), 
      Or(z == 0, z == 1),
      x + y + z > 2)

# Split all clauses"
split_all = Repeat(OrElse(Tactic('split-clause'),
                          Tactic('skip')))
print split_all(g)

split_at_most_2 = Repeat(OrElse(Tactic('split-clause'),
                          Tactic('skip')),
                         1)
print split_at_most_2(g)

# Split all clauses and solve equations
split_solve = Then(Repeat(OrElse(Tactic('split-clause'),
                                 Tactic('skip'))),
                   Tactic('solve-eqs'))

print split_solve(g)

In the tactic split_solver, the tactic solve-eqs discharges all but one goal. Note that, this tactic generates one goal: the empty goal which is trivially satisfiable (i.e., feasible)

The list of subgoals can be easily traversed using the Python for statement.

x, y, z = Reals('x y z')
g = Goal()
g.add(Or(x == 0, x == 1), 
      Or(y == 0, y == 1), 
      Or(z == 0, z == 1),
      x + y + z > 2)

# Split all clauses"
split_all = Repeat(OrElse(Tactic('split-clause'),
                          Tactic('skip')))
for s in split_all(g):
    print s

A tactic can be converted into a solver object using the method solver(). If the tactic produces the empty goal, then the associated solver returns sat. If the tactic produces a single goal containing False, then the solver returns unsat. Otherwise, it returns unknown.

bv_solver = Then('simplify', 
                 'solve-eqs', 
                 'bit-blast', 
                 'sat').solver()

x, y = BitVecs('x y', 16)
solve_using(bv_solver, x | y == 13, x > y)

In the example above, the tactic bv_solver implements a basic bit-vector solver using equation solving, bit-blasting, and a propositional SAT solver. Note that, the command Tactic is suppressed. All Z3Py combinators automatically invoke Tactic command if the argument is a string. Finally, the command solve_using is a variant of the solve command where the first argument specifies the solver to be used.

In the following example, we use the solver API directly instead of the command solve_using. We use the combinator With to configure our little solver. We also include the tactic aig which tries to compress Boolean formulas using And-Inverted Graphs.

bv_solver = Then(With('simplify', mul2concat=True),
                 'solve-eqs', 
                 'bit-blast', 
                 'aig',
                 'sat').solver()
x, y = BitVecs('x y', 16)
bv_solver.add(x*32 + y == 13, x & y < 10, y > -100)
print bv_solver.check()
m = bv_solver.model()
print m
print x*32 + y, "==", m.evaluate(x*32 + y)
print x & y, "==", m.evaluate(x & y)

The tactic smt wraps the main solver in Z3 as a tactic.

x, y = Ints('x y')
s = Tactic('smt').solver()
s.add(x > y + 1)
print s.check()
print s.model()

Now, we show how to implement a solver for integer arithmetic using SAT. The solver is complete only for problems where every variable has a lower and upper bound.

s = Then(With('simplify', arith_lhs=True, som=True),
         'normalize-bounds', 'lia2pb', 'pb2bv', 
         'bit-blast', 'sat').solver()
x, y, z = Ints('x y z')
solve_using(s, 
            x > 0, x < 10, 
            y > 0, y < 10, 
            z > 0, z < 10,
            3*y + 2*x == z)
# It fails on the next example (it is unbounded)
s.reset()
solve_using(s, 3*y + 2*x == z)

Tactics can be combined with solvers. For example, we can apply a tactic to a goal, produced a set of subgoals, then select one of the subgoals and solve it using a solver. The next example demonstrates how to do that, and how to use model converters to convert a model for a subgoal into a model for the original goal.

t = Then('simplify', 
         'normalize-bounds', 
         'solve-eqs')

x, y, z = Ints('x y z')
g = Goal()
g.add(x > 10, y == x + 3, z > y)

r = t(g)
# r contains only one subgoal
print r

s = Solver()
s.add(r[0])
print s.check()
# Model for the subgoal
print s.model()
# Model for the original goal
print r.convert_model(s.model())

Probes

Probes (aka formula measures) are evaluated over goals. Boolean expressions over them can be built using relational operators and Boolean connectives. The tactic FailIf(cond) fails if the given goal does not satisfy the condition cond. Many numeric and Boolean measures are available in Z3Py. The command describe_probes() provides the list of all built-in probes.

describe_probes()

In the following example, we build a simple tactic using FailIf. It also shows that a probe can be applied directly to a goal.

x, y, z = Reals('x y z')
g = Goal()
g.add(x + y + z > 0)

p = Probe('num-consts')
print "num-consts:", p(g)

t = FailIf(p > 2)
try:
    t(g)
except Z3Exception:
    print "tactic failed"

print "trying again..."
g = Goal()
g.add(x + y > 0)
print t(g)

Z3Py also provides the combinator (tactical) If(p, t1, t2) which is a shorthand for:

OrElse(Then(FailIf(Not(p)), t1), t2)

The combinator When(p, t) is a shorthand for:

If(p, t, 'skip')

The tactic skip just returns the input goal. The following example demonstrates how to use the If combinator.

x, y, z = Reals('x y z')
g = Goal()
g.add(x**2 - y**2 >= 0)

p = Probe('num-consts')
t = If(p > 2, 'simplify', 'factor')

print t(g)

g = Goal()
g.add(x + x + y + z >= 0, x**2 - y**2 >= 0)

print t(g)
z3-z3-4.8.7/examples/python/tutorial/jupyter/000077500000000000000000000000001356505360400211335ustar00rootroot00000000000000z3-z3-4.8.7/examples/python/tutorial/jupyter/README.md000066400000000000000000000003741356505360400224160ustar00rootroot00000000000000# Jupyter version of the Z3 Python Tutorial. Thanks to Peter Gragert, the Z3 tutorial guide is now available in the Jupyter notebook format. You can try it directly online from: https://z3examples-nbjorner.notebooks.azure.com/j/notebooks/guide.ipynb z3-z3-4.8.7/examples/python/tutorial/jupyter/advanced.ipynb000066400000000000000000001022561356505360400237510ustar00rootroot00000000000000{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Advanced Topics\n", "\n", "More information on Z3 is available from https://github.com/z3prover/z3.git\n", "\n", "\n", "## Expressions, Sorts and Declarations\n", "In Z3, expressions, sorts and declarations are called ASTs. ASTs are directed acyclic graphs. Every expression has a sort (aka type). The method sort() retrieves the sort of an expression." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "!pip install \"z3-solver\"\n", "from z3 import *\n", "\n", "x = Int('x')\n", "y = Real('y')\n", "print ((x + 1).sort())\n", "print ((y + 1).sort())\n", "print ((x >= 2).sort())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The function eq(n1, n2) returns True if n1 and n2 are the same AST. This is a structural test." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x, y = Ints('x y')\n", "print (eq(x + y, x + y))\n", "print (eq(x + y, y + x))\n", "n = x + y\n", "print (eq(n, x + y))\n", "# x2 is eq to x\n", "x2 = Int('x') \n", "print (eq(x, x2))\n", "# the integer variable x is not equal to \n", "# the real variable x\n", "print (eq(Int('x'), Real('x')))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The method hash() returns a hashcode for an AST node. If eq(n1, n2) returns True, then n1.hash() is equal to n2.hash()." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x = Int('x')\n", "print ((x + 1).hash())\n", "print ((1 + x).hash())\n", "print (x.sort().hash())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Z3 expressions can be divided in three basic groups: applications, quantifiers and bound/free variables. Applications are all you need if your problems do not contain universal/existential quantifiers. Although we say Int('x') is an integer \"variable\", it is technically an integer constant, and internally is represented as a function application with 0 arguments. Every application is associated with a declaration and contains 0 or more arguments. The method decl() returns the declaration associated with an application. The method num_args() returns the number of arguments of an application, and arg(i) one of the arguments. The function is_expr(n) returns True if n is an expression. Similarly is_app(n) (is_func_decl(n)) returns True if n is an application (declaration)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x = Int('x')\n", "print (\"is expression: \", is_expr(x))\n", "n = x + 1\n", "print (\"is application:\", is_app(n))\n", "print (\"decl: \", n.decl())\n", "print (\"num args: \", n.num_args())\n", "for i in range(n.num_args()):\n", " print (\"arg(\", i, \") ->\", n.arg(i))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Declarations have names, they are retrieved using the method name(). A (function) declaration has an arity, a domain and range sorts." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x = Int('x')\n", "x_d = x.decl()\n", "print (\"is_expr(x_d): \", is_expr(x_d))\n", "print (\"is_func_decl(x_d):\", is_func_decl(x_d))\n", "print (\"x_d.name(): \", x_d.name())\n", "print (\"x_d.range(): \", x_d.range())\n", "print (\"x_d.arity(): \", x_d.arity())\n", "# x_d() creates an application with 0 arguments using x_d.\n", "print (\"eq(x_d(), x): \", eq(x_d(), x))\n", "print (\"\\n\")\n", "# f is a function from (Int, Real) to Bool\n", "f = Function('f', IntSort(), RealSort(), BoolSort())\n", "print (\"f.name(): \", f.name())\n", "print (\"f.range(): \", f.range())\n", "print (\"f.arity(): \", f.arity())\n", "for i in range(f.arity()):\n", " print (\"domain(\", i, \"): \", f.domain(i))\n", "# f(x, x) creates an application with 2 arguments using f.\n", "print (f(x, x))\n", "print (eq(f(x, x).decl(), f))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The built-in declarations are identified using their kind. The kind is retrieved using the method kind(). The complete list of built-in declarations can be found in the file z3consts.py (z3_api.h) in the Z3 distribution." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x, y = Ints('x y')\n", "print ((x + y).decl().kind() == Z3_OP_ADD)\n", "print ((x + y).decl().kind() == Z3_OP_SUB)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The following example demonstrates how to substitute sub-expressions in Z3 expressions." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x, y = Ints('x y')\n", "f = Function('f', IntSort(), IntSort(), IntSort())\n", "g = Function('g', IntSort(), IntSort())\n", "n = f(f(g(x), g(g(x))), g(g(y)))\n", "print (n)\n", "# substitute g(g(x)) with y and g(y) with x + 1\n", "print (substitute(n, (g(g(x)), y), (g(y), x + 1)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The function Const(name, sort) declares a constant (aka variable) of the given sort. For example, the functions Int(name) and Real(name) are shorthands for Const(name, IntSort()) and Const(name, RealSort())." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x = Const('x', IntSort())\n", "print (eq(x, Int('x')))\n", "\n", "a, b = Consts('a b', BoolSort())\n", "print (And(a, b))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Arrays\n", "\n", "As part of formulating a programme of a mathematical theory of computation McCarthy proposed a basic theory of arrays as characterized by the select-store axioms. The expression Select(a, i) returns the value stored at position i of the array a; and Store(a, i, v) returns a new array identical to a, but on position i it contains the value v. In Z3Py, we can also write Select(a, i) as a[i]." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Use I as an alias for IntSort()\n", "I = IntSort()\n", "# A is an array from integer to integer\n", "A = Array('A', I, I)\n", "x = Int('x')\n", "print (A[x])\n", "print (Select(A, x))\n", "print (Store(A, x, 10))\n", "print (simplify(Select(Store(A, 2, x+1), 2)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "By default, Z3 assumes that arrays are extensional over select. In other words, Z3 also enforces that if two arrays agree on all positions, then the arrays are equal.\n", "\n", "Z3 also contains various extensions for operations on arrays that remain decidable and amenable to efficient saturation procedures (here efficient means, with an NP-complete satisfiability complexity). We describe these extensions in the following using a collection of examples. Additional background on these extensions is available in the paper [Generalized and Efficient Array Decision Procedures](http://research.microsoft.com/en-us/um/people/leonardo/fmcad09.pdf).\n", "\n", "Arrays in Z3 are used to model unbounded or very large arrays. Arrays should not be used to model small finite collections of values. It is usually much more efficient to create different variables using list comprehensions." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# We want an array with 3 elements.\n", "# 1. Bad solution\n", "X = Array('x', IntSort(), IntSort())\n", "# Example using the array\n", "print (X[0] + X[1] + X[2] >=0)\n", "\n", "# 2. More efficient solution\n", "X = IntVector('x', 3)\n", "print (X[0] + X[1] + X[2] >= 0)\n", "print (Sum(X) >= 0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Select and Store\n", "Let us first check a basic property of arrays. Suppose A is an array of integers, then the constraints A[x] == x, Store(A, x, y) == A are satisfiable for an array that contains an index x that maps to x, and when x == y. We can solve these constraints." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "A = Array('A', IntSort(), IntSort())\n", "x, y = Ints('x y')\n", "solve(A[x] == x, Store(A, x, y) == A)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The interpretation/solution for array variables is very similar to the one used for functions.\n", "\n", "The problem becomes unsatisfiable/infeasible if we add the constraint x != y." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "A = Array('A', IntSort(), IntSort())\n", "x, y = Ints('x y')\n", "solve(A[x] == x, Store(A, x, y) == A, x != y)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Constant arrays\n", "The array that maps all indices to some fixed value can be specified in Z3Py using the K(s, v) construct where s is a sort/type and v is an expression. K(s, v) returns a array that maps any value of s into v. The following example defines a constant array containing only ones." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "AllOne = K(IntSort(), 1)\n", "a, i = Ints('a i')\n", "solve(a == AllOne[i])\n", "# The following constraints do not have a solution\n", "solve(a == AllOne[i], a != 1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Datatypes\n", "Algebraic datatypes, known from programming languages such as ML, offer a convenient way for specifying common data structures. Records and tuples are special cases of algebraic datatypes, and so are scalars (enumeration types). But algebraic datatypes are more general. They can be used to specify finite lists, trees and other recursive structures.\n", "\n", "The following example demonstrates how to declare a List in Z3Py. It is more verbose than using the SMT 2.0 front-end, but much simpler than using the Z3 C API. It consists of two phases. First, we have to declare the new datatype, its constructors and accessors. The function Datatype('List') declares a \"placeholder\" that will contain the constructors and accessors declarations. The method declare(cname, (aname, sort)+) declares a constructor named cname with the given accessors. Each accessor has an associated sort or a reference to the datatypes being declared. For example, declare('cons', ('car', IntSort()), ('cdr', List)) declares the constructor named cons that builds a new List using an integer and a List. It also declares the accessors car and cdr. The accessor car extracts the integer of a cons cell, and cdr the list of a cons cell. After all constructors were declared, we use the method create() to create the actual datatype in Z3. Z3Py makes the new Z3 declarations and constants available as slots of the new object." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Declare a List of integers\n", "List = Datatype('List')\n", "# Constructor cons: (Int, List) -> List\n", "List.declare('cons', ('car', IntSort()), ('cdr', List))\n", "# Constructor nil: List\n", "List.declare('nil')\n", "# Create the datatype\n", "List = List.create()\n", "print (is_sort(List))\n", "cons = List.cons\n", "car = List.car\n", "cdr = List.cdr\n", "nil = List.nil\n", "# cons, car and cdr are function declarations, and nil a constant\n", "print (is_func_decl(cons))\n", "print (is_expr(nil))\n", "\n", "l1 = cons(10, cons(20, nil))\n", "print (l1)\n", "print (simplify(cdr(l1)))\n", "print (simplify(car(l1)))\n", "print (simplify(l1 == nil))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The following example demonstrates how to define a Python function that given a sort creates a list of the given sort." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def DeclareList(sort):\n", " List = Datatype('List_of_%s' % sort.name())\n", " List.declare('cons', ('car', sort), ('cdr', List))\n", " List.declare('nil')\n", " return List.create()\n", "\n", "IntList = DeclareList(IntSort())\n", "RealList = DeclareList(RealSort())\n", "IntListList = DeclareList(IntList)\n", "\n", "l1 = IntList.cons(10, IntList.nil)\n", "print (l1)\n", "print (IntListList.cons(l1, IntListList.cons(l1, IntListList.nil)))\n", "print (RealList.cons(\"1/3\", RealList.nil))\n", "\n", "print (l1.sort())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The example above demonstrates that Z3 supports operator overloading. There are several functions named cons, but they are different since they receive and/or return values of different sorts. Note that it is not necessary to use a different sort name for each instance of the sort list. That is, the expression 'List_of_%s' % sort.name() is not necessary, we use it just to provide more meaningful names.\n", "\n", "As described above enumeration types are a special case of algebraic datatypes. The following example declares an enumeration type consisting of three values: red, green and blue." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "Color = Datatype('Color')\n", "Color.declare('red')\n", "Color.declare('green')\n", "Color.declare('blue')\n", "Color = Color.create()\n", "\n", "print (is_expr(Color.green))\n", "print (Color.green == Color.blue)\n", "print (simplify(Color.green == Color.blue))\n", "\n", "# Let c be a constant of sort Color\n", "c = Const('c', Color)\n", "# Then, c must be red, green or blue\n", "prove(Or(c == Color.green, \n", " c == Color.blue,\n", " c == Color.red))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Z3Py also provides the following shorthand for declaring enumeration sorts." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "Color, (red, green, blue) = EnumSort('Color', ('red', 'green', 'blue'))\n", "\n", "print (green == blue)\n", "print (simplify(green == blue))\n", "\n", "c = Const('c', Color)\n", "solve(c != green, c != blue)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Mutually recursive datatypes can also be declared. The only difference is that we use the function CreateDatatypes instead of the method create() to create the mutually recursive datatypes." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "TreeList = Datatype('TreeList')\n", "Tree = Datatype('Tree')\n", "Tree.declare('leaf', ('val', IntSort()))\n", "Tree.declare('node', ('left', TreeList), ('right', TreeList))\n", "TreeList.declare('nil')\n", "TreeList.declare('cons', ('car', Tree), ('cdr', TreeList))\n", "\n", "Tree, TreeList = CreateDatatypes(Tree, TreeList)\n", "\n", "t1 = Tree.leaf(10)\n", "tl1 = TreeList.cons(t1, TreeList.nil)\n", "t2 = Tree.node(tl1, TreeList.nil)\n", "print (t2)\n", "print (simplify(Tree.val(t1)))\n", "\n", "t1, t2, t3 = Consts('t1 t2 t3', TreeList)\n", "\n", "solve(Distinct(t1, t2, t3))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Uninterpreted Sorts\n", "Function and constant symbols in pure first-order logic are uninterpreted or free, which means that no a priori interpretation is attached. This is in contrast to arithmetic operators such as + and - that have a fixed standard interpretation. Uninterpreted functions and constants are maximally flexible; they allow any interpretation that is consistent with the constraints over the function or constant.\n", "\n", "To illustrate uninterpreted functions and constants let us introduce an (uninterpreted) sort A, and the constants x, y ranging over A. Finally let f be an uninterpreted function that takes one argument of sort A and results in a value of sort A. The example illustrates how one can force an interpretation where f applied twice to x results in x again, but f applied once to x is different from x." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "A = DeclareSort('A')\n", "x, y = Consts('x y', A)\n", "f = Function('f', A, A)\n", "\n", "s = Solver()\n", "s.add(f(f(x)) == x, f(x) == y, x != y)\n", "\n", "print (s.check())\n", "m = s.model()\n", "print (m)\n", "print (\"interpretation assigned to A:\")\n", "print (m[A])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The resulting model introduces abstract values for the elements in A, because the sort A is uninterpreted. The interpretation for f in the model toggles between the two values for x and y, which are different. The expression m[A] returns the interpretation (universe) for the uninterpreted sort A in the model m.\n", "\n", "## Quantifiers\n", "Z3 is can solve quantifier-free problems containing arithmetic, bit-vector, Booleans, arrays, functions and datatypes. Z3 also accepts and can work with formulas that use quantifiers. It is no longer a decision procedure for such formulas in general (and for good reasons, as there can be no decision procedure for first-order logic)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "f = Function('f', IntSort(), IntSort(), IntSort())\n", "x, y = Ints('x y')\n", "print (ForAll([x, y], f(x, y) == 0))\n", "print (Exists(x, f(x, x) >= 0))\n", "\n", "a, b = Ints('a b')\n", "solve(ForAll(x, f(x, x) == 0), f(a, b) == 1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Nevertheless, Z3 is often able to handle formulas involving quantifiers. It uses several approaches to handle quantifiers. The most prolific approach is using pattern-based quantifier instantiation. This approach allows instantiating quantified formulas with ground terms that appear in the current search context based on pattern annotations on quantifiers. Z3 also contains a model-based quantifier instantiation component that uses a model construction to find good terms to instantiate quantifiers with; and Z3 also handles many decidable fragments.\n", "\n", "Note that in the previous example the constants x and y were used to create quantified formulas. This is a \"trick\" for simplifying the construction of quantified formulas in Z3Py. Internally, these constants are replaced by bounded variables. The next example demonstrates that. The method body() retrieves the quantified expression. In the resultant formula the bounded variables are free. The function Var(index, sort) creates a bounded/free variable with the given index and sort." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "f = Function('f', IntSort(), IntSort(), IntSort())\n", "x, y = Ints('x y')\n", "f = ForAll([x, y], f(x, y) == 0)\n", "print (f.body())\n", "v1 = f.body().arg(0).arg(0)\n", "print (v1)\n", "print (eq(v1, Var(1, IntSort())))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Modeling with Quantifiers\n", "Suppose we want to model an object oriented type system with single inheritance. We would need a predicate for sub-typing. Sub-typing should be a partial order, and respect single inheritance. For some built-in type constructors, such as for array_of, sub-typing should be monotone." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "Type = DeclareSort('Type')\n", "subtype = Function('subtype', Type, Type, BoolSort())\n", "array_of = Function('array_of', Type, Type)\n", "root = Const('root', Type)\n", "\n", "x, y, z = Consts('x y z', Type)\n", "\n", "axioms = [ ForAll(x, subtype(x, x)),\n", " ForAll([x, y, z], Implies(And(subtype(x, y), subtype(y, z)),\n", " subtype(x, z))),\n", " ForAll([x, y], Implies(And(subtype(x, y), subtype(y, x)),\n", " x == y)),\n", " ForAll([x, y, z], Implies(And(subtype(x, y), subtype(x, z)),\n", " Or(subtype(y, z), subtype(z, y)))),\n", " ForAll([x, y], Implies(subtype(x, y),\n", " subtype(array_of(x), array_of(y)))),\n", " \n", " ForAll(x, subtype(root, x))\n", " ]\n", "s = Solver()\n", "s.add(axioms)\n", "print (s)\n", "print (s.check())\n", "print (\"Interpretation for Type:\")\n", "print (s.model()[Type])\n", "print (\"Model:\")\n", "print (s.model())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Patterns\n", "The Stanford Pascal verifier and the subsequent Simplify theorem prover pioneered the use of pattern-based quantifier instantiation. The basic idea behind pattern-based quantifier instantiation is in a sense straight-forward: Annotate a quantified formula using a pattern that contains all the bound variables. So a pattern is an expression (that does not contain binding operations, such as quantifiers) that contains variables bound by a quantifier. Then instantiate the quantifier whenever a term that matches the pattern is created during search. This is a conceptually easy starting point, but there are several subtleties that are important.\n", "\n", "In the following example, the first two options make sure that Model-based quantifier instantiation engine is disabled. We also annotate the quantified formula with the pattern f(g(x)). Since there is no ground instance of this pattern, the quantifier is not instantiated, and Z3 fails to show that the formula is unsatisfiable." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "f = Function('f', IntSort(), IntSort())\n", "g = Function('g', IntSort(), IntSort())\n", "a, b, c = Ints('a b c')\n", "x = Int('x')\n", "\n", "s = Solver()\n", "s.set(auto_config=False, mbqi=False)\n", "\n", "s.add( ForAll(x, f(g(x)) == x, patterns = [f(g(x))]),\n", " g(a) == c,\n", " g(b) == c,\n", " a != b )\n", "\n", "# Display solver state using internal format\n", "print (s.sexpr())\n", "print (s.check())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When the more permissive pattern g(x) is used. Z3 proves the formula to be unsatisfiable. More restrictive patterns minimize the number of instantiations (and potentially improve performance), but they may also make Z3 \"less complete\"." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "f = Function('f', IntSort(), IntSort())\n", "g = Function('g', IntSort(), IntSort())\n", "a, b, c = Ints('a b c')\n", "x = Int('x')\n", "\n", "s = Solver()\n", "s.set(auto_config=False, mbqi=False)\n", "\n", "s.add( ForAll(x, f(g(x)) == x, patterns = [g(x)]),\n", " g(a) == c,\n", " g(b) == c,\n", " a != b )\n", "\n", "# Display solver state using internal format\n", "print (s.sexpr())\n", "print (s.check())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Some patterns may also create long instantiation chains. Consider the following assertion.\n", "\n", "
\n",
    "ForAll([x, y], Implies(subtype(x, y),\n",
    "                       subtype(array_of(x), array_of(y))),\n",
    "       patterns=[subtype(x, y)])\n",
    "
\n", "The axiom gets instantiated whenever there is some ground term of the form subtype(s, t). The instantiation causes a fresh ground term subtype(array_of(s), array_of(t)), which enables a new instantiation. This undesirable situation is called a matching loop. Z3 uses many heuristics to break matching loops.\n", "\n", "Before elaborating on the subtleties, we should address an important first question. What defines the terms that are created during search? In the context of most SMT solvers, and of the Simplify theorem prover, terms exist as part of the input formula, they are of course also created by instantiating quantifiers, but terms are also implicitly created when equalities are asserted. The last point means that terms are considered up to congruence and pattern matching takes place modulo ground equalities. We call the matching problem E-matching. For example, if we have the following equalities:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "f = Function('f', IntSort(), IntSort())\n", "g = Function('g', IntSort(), IntSort())\n", "a, b, c = Ints('a b c')\n", "x = Int('x')\n", "\n", "s = Solver()\n", "s.set(auto_config=False, mbqi=False)\n", "\n", "s.add( ForAll(x, f(g(x)) == x, patterns = [f(g(x))]),\n", " a == g(b),\n", " b == c,\n", " f(a) != c )\n", "\n", "print (s.check())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The terms f(a) and f(g(b)) are equal modulo the equalities. The pattern f(g(x)) can be matched and x bound to b and the equality f(g(b)) == b is deduced.\n", "\n", "While E-matching is an NP-complete problem, the main sources of overhead in larger verification problems comes from matching thousands of patterns in the context of an evolving set of terms and equalities. Z3 integrates an efficient E-matching engine using term indexing techniques.\n", "\n", "### Multi-patterns\n", "In some cases, there is no pattern that contains all bound variables and does not contain interpreted symbols. In these cases, we use multi-patterns. In the following example, the quantified formula states that f is injective. This quantified formula is annotated with the multi-pattern MultiPattern(f(x), f(y))." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "A = DeclareSort('A')\n", "B = DeclareSort('B')\n", "f = Function('f', A, B)\n", "a1, a2 = Consts('a1 a2', A)\n", "b = Const('b', B)\n", "x, y = Consts('x y', A)\n", "\n", "s = Solver()\n", "s.add(a1 != a2,\n", " f(a1) == b,\n", " f(a2) == b,\n", " ForAll([x, y], Implies(f(x) == f(y), x == y),\n", " patterns=[MultiPattern(f(x), f(y))])\n", " )\n", "print (s.check())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The quantified formula is instantiated for every pair of occurrences of f. A simple trick allows formulating injectivity of f in such a way that only a linear number of instantiations is required. The trick is to realize that f is injective if and only if it has a partial inverse." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "A = DeclareSort('A')\n", "B = DeclareSort('B')\n", "f = Function('f', A, B)\n", "finv = Function('finv', B, A)\n", "a1, a2 = Consts('a1 a2', A)\n", "b = Const('b', B)\n", "x, y = Consts('x y', A)\n", "\n", "s = Solver()\n", "s.add(a1 != a2,\n", " f(a1) == b,\n", " f(a2) == b,\n", " ForAll(x, finv(f(x)) == x)\n", " )\n", "print (s.check())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Other attributes\n", "In Z3Py, the following additional attributes are supported: qid (quantifier identifier for debugging), weight (hint to the quantifier instantiation module: \"more weight equals less instances\"), no_patterns (expressions that should not be used as patterns, skid (identifier prefix used to create skolem constants/functions." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Multiple Solvers\n", "In Z3Py and Z3 4.0 multiple solvers can be simultaneously used. It is also very easy to copy assertions/formulas from one solver to another." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x, y = Ints('x y')\n", "s1 = Solver()\n", "s1.add(x > 10, y > 10)\n", "s2 = Solver()\n", "# solver s2 is empty\n", "print (s2)\n", "# copy assertions from s1 to s2\n", "s2.add(s1.assertions())\n", "print (s2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Unsat Cores and Soft Constraints\n", "Z3Py also supports unsat core extraction. The basic idea is to use assumptions, that is, auxiliary propositional variables that we want to track. Assumptions are also available in the Z3 SMT 2.0 frontend, and in other Z3 front-ends. They are used to extract unsatisfiable cores. They may be also used to \"retract\" constraints. Note that, assumptions are not really soft constraints, but they can be used to implement them." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "p1, p2, p3 = Bools('p1 p2 p3')\n", "x, y = Ints('x y')\n", "# We assert Implies(p, C) to track constraint C using p\n", "s = Solver()\n", "s.add(Implies(p1, x > 10),\n", " Implies(p1, y > x),\n", " Implies(p2, y < 5),\n", " Implies(p3, y > 0))\n", "print (s)\n", "# Check satisfiability assuming p1, p2, p3 are true\n", "print (s.check(p1, p2, p3))\n", "print (s.unsat_core())\n", "\n", "# Try again retracting p2\n", "print (s.check(p1, p3))\n", "print (s.model())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The example above also shows that a Boolean variable (p1) can be used to track more than one constraint. Note that Z3 does not guarantee that the unsat cores are minimal.\n", "\n", "## Formatter\n", "Z3Py uses a formatter (aka pretty printer) for displaying formulas, expressions, solvers, and other Z3 objects. The formatter supports many configuration options. The command set_option(html_mode=False) makes all formulas and expressions to be displayed in Z3Py notation." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x = Int('x')\n", "y = Int('y')\n", "set_option(html_mode=True)\n", "print (x**2 + y**2 >= 1)\n", "set_option(html_mode=False)\n", "print (x**2 + y**2 >= 1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "By default, Z3Py will truncate the output if the object being displayed is too big. Z3Py uses … to denote the output is truncated. The following configuration options can be set to control the behavior of Z3Py's formatter:\n", "\n", "* max_depth Maximal expression depth. Deep expressions are replaced with ….\n", "* max_args Maximal number of arguments to display per node.\n", "* rational_to_decimal Display rationals as decimals if True.\n", "* precision Maximal number of decimal places for numbers being displayed in decimal notation.\n", "* max_lines Maximal number of lines to be displayed.\n", "* max_width Maximal line width (this is a suggestion to Z3Py).\n", "* max_indent Maximal indentation." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x = IntVector('x', 20)\n", "y = IntVector('y', 20)\n", "f = And(Sum(x) >= 0, Sum(y) >= 0)\n", "\n", "set_option(max_args=5)\n", "print (\"\\ntest 1:\", f)\n", "\n", "set_option(max_args=100, max_lines=10)\n", "print (\"\\ntest 2:\", f)\n", "\n", "set_option(max_width=300)\n", "print (\"\\ntest 3:\", f)\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" } }, "nbformat": 4, "nbformat_minor": 2 } z3-z3-4.8.7/examples/python/tutorial/jupyter/guide.ipynb000066400000000000000000002135441356505360400233040ustar00rootroot00000000000000{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Z3 API in Python\n", "\n", "Z3 is a high performance theorem prover developed at Microsoft Research. Z3 is used in many applications such as: software/hardware verification and testing, constraint solving, analysis of hybrid systems, security, biology (in silicon analysis), and geometrical problems.\n", "\n", "This tutorial demonstrates the main capabilities of Z3Py: the Z3 API in Python. No Python background is needed to read this tutorial. However, it is useful to learn Python (a fun language!) at some point, and there are many excellent free resources for doing so (Python Tutorial).\n", "\n", "The Z3 distribution also contains the C, C++, .Net, Java and OCaml APIs. The source code of Z3Py is available in the Z3 distribution on https://github.com/z3prover/z3.git where you are invited to download, use and update the code, add pull requests and file issues on usability, bugs and features.\n", "\n", "\n", "## First things first\n", "Import Z3\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from z3 import *" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Getting Started\n", "\n", "Let us start with the following simple example:\n", "\n", "\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x = Int('x')\n", "y = Int('y')\n", "solve(x > 2, y < 10, x + 2*y == 7)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The function Int('x') creates an integer variable in Z3 named x. \n", "The solve function solves a system of constraints.\n", "The example above uses two variables x and y, and three constraints. \n", "Z3Py like Python uses = for assignment. \n", "The operators <, <=, >, >=, == and != for comparison. \n", "In the example above, the expression x + 2*y == 7 is a Z3 constraint.\n", "Z3 can solve and crunch formulas.\n", "\n", "The next examples show how to use the Z3 formula/expression simplifier.\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x = Int('x')\n", "y = Int('y')\n", "print(simplify(x + y + 2*x + 3))\n", "print(simplify(x < y + x + 2))\n", "print(simplify(And(x + 1 >= 3, x**2 + x**2 + y**2 + 2 >= 5)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "By default, Z3Py (for the web) displays formulas and expressions using mathematical notation. As usual, ∧ is the logical and, ∨ is the logical or, and so on. The command set_option(html_mode=False) makes all formulas and expressions to be displayed in Z3Py notation. This is also the default mode for the offline version of Z3Py that comes with the Z3 distribution." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x = Int('x')\n", "y = Int('y')\n", "print(x**2 + y**2 >= 1)\n", "set_option(html_mode=False)\n", "print(x**2 + y**2 >= 1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### html mode: use copy paste into a markdown-cell\n", "After execution" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "set_option(html_mode=True)\n", "print(x**2 + y**2 >= 1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### copied from the printed result:\n", "x2 + y2 ≥ 1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Z3 provides functions for traversing expressions." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x = Int('x')\n", "y = Int('y')\n", "n = x + y >= 3\n", "print(\"num args: \", n.num_args())\n", "print(\"children: \", n.children())\n", "print(\"1st child:\", n.arg(0))\n", "print(\"2nd child:\", n.arg(1))\n", "print(\"operator: \", n.decl())\n", "print(\"op name: \", n.decl().name())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Z3 provides all basic mathematical operations. \n", "Z3Py uses the same operator precedence of the Python language. \n", "Like Python, ** is the power operator. \n", "Z3 can solve nonlinear polynomial constraints." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x = Real('x')\n", "y = Real('y')\n", "solve(x**2 + 0.12 * y**2 > 3, x**3 + y < 5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The procedure Real('x') creates the real variable x. Z3Py can represent arbitrarily large integers, rational numbers (like in the example above), and irrational algebraic numbers. An irrational algebraic number is a root of a polynomial with integer coefficients. Internally, Z3 represents all these numbers precisely. The irrational numbers are displayed in decimal notation for making it easy to read the results." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x = Real('x')\n", "y = Real('y')\n", "solve(x**2 + y**2 == 3, x**3 == 2)\n", "\n", "set_option(precision=30)\n", "print(\"Solving, and displaying result with 30 decimal places\")\n", "solve(x**2 + y**2 == 3, x**3 == 2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# The procedure set_option is used to configure the Z3 environment. \n", "It is used to set global configuration options such as how the result is displayed. The option set_option(precision=30) sets the number of decimal places used when displaying results. \n", "\n", "The ? mark in 1.259921049894873164767210607278? indicates the output is truncated.\n", "\n", "The following example demonstrates a common mistake. The expression 3/2 is a Python integer and not a Z3 rational number. The example also shows different ways to create rational numbers in Z3Py. The procedure Q(num, den) creates a Z3 rational where num is the numerator and den is the denominator.\n", "The RealVal(1) creates a Z3 real number representing \n", "the number 1." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(1/3)\n", "print(RealVal(1)/3)\n", "\n", "x = Real('x')\n", "print(x + 1/3)\n", "print(x + \"1/3\")\n", "print(x + 0.25)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "Rational numbers can also be displayed in decimal\n", "notation." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x = Real('x')\n", "solve(3*x == 1)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "\n", "set_option(rational_to_decimal=True)\n", "solve(3*x == 1)\n", "\n", "set_option(precision=20)\n", "solve(3*x == 1)\n", "set_option(rational_to_decimal=False)\n", "solve(6*x == 37)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A system of constraints may not have a solution.\n", "In this case, we say the system is unsatisfiable." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x = Real('x')\n", "solve(x > 4, x < 0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "Like in Python, comments begin with the hash character # and are terminated by the end of line. Z3Py does not support comments that span more than one line.\n", "Boolean Logic" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "set_option(html_mode=False)\n", "# This is a comment\n", "x = Real('x') # comment: creating x\n", "print(x**2 + 2*x + 2) # comment: printing polynomial)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Boolean Logic\n", "\n", "Z3 supports Boolean operators: And, Or, Not, Implies (implication), If (if-then-else). \n", "Bi-implications are represented using equality ==. \n", "The following example shows how to solve\n", "a simple set of Boolean constraints.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "p = Bool('p')\n", "q = Bool('q')\n", "r = Bool('r')\n", "solve(Implies(p, q), r == Not(q), Or(Not(p), r))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The Python Boolean constants True and False\n", "can be used to build Z3 Boolean expressions.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "p = Bool('p')\n", "q = Bool('q')\n", "print(And(p, q, True))\n", "print(simplify(And(p, q, True)))\n", "print(simplify(And(p, False)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The following example uses a combination of polynomial and Boolean constraints." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "p = Bool('p')\n", "x = Real('x')\n", "solve(Or(x < 5, x > 10), Or(p, x**2 == 2), Not(p))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Solvers\n", "\n", "\n", "Z3 provides different solvers. The command solve, used in the previous examples, is implemented using the Z3 solver API. The implementation can be found in the file z3.py in the Z3 distribution. The following example demonstrates the basic Solver API.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x = Int('x')\n", "y = Int('y')\n", "\n", "s = Solver()\n", "print(s)\n", "\n", "s.add(x > 10, y == x + 2)\n", "print(s)\n", "print(\"Solving constraints in the solver s ...\")\n", "print(s.check())\n", "\n", "print(\"Create a new scope...\")\n", "s.push()\n", "s.add(y < 11)\n", "print(s)\n", "print(\"Solving updated set of constraints...\")\n", "print(s.check())\n", "\n", "print(\"Restoring state...\")\n", "s.pop()\n", "print(s)\n", "print(\"Solving restored set of constraints...\")\n", "print(s.check())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Info ...\n", "The command Solver() creates a general purpose solver. Constraints can be added using the method add. We say the constraints have been asserted in the solver. The method check() solves the asserted constraints. The result is sat (satisfiable) if a solution was found. The result is unsat (unsatisfiable) if no solution exists. We may also say the system of asserted constraints is infeasible. Finally, a solver may fail to solve a system of constraints and unknown is returned.\n", "\n", "In some applications, we want to explore several similar problems that share several constraints. We can use the commands push and pop for doing that. Each solver maintains a stack of assertions. The command push creates a new scope by saving the current stack size. The command pop removes any assertion performed between it and the matching push. The check method always operates on the content of solver assertion stack.\n", "\n", "The following example shows an example that Z3 cannot solve. The solver returns unknown in this case. Recall that Z3 can solve nonlinear polynomial constraints, but 2**x is not a polynomial.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x = Real('x')\n", "s = Solver()\n", "s.add(2**x == 3)\n", "print(s.check())\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The following example shows how to traverse the constraints asserted into a solver, and how to collect performance statistics for the check method.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x = Real('x')\n", "y = Real('y')\n", "s = Solver()\n", "s.add(x > 1, y > 1, Or(x + y > 3, x - y < 2))\n", "print(\"asserted constraints...\")\n", "for c in s.assertions():\n", " print(c)\n", "\n", "print(s.check())\n", "print(\"statistics for the last check method...\")\n", "print(s.statistics())\n", "# Traversing statistics\n", "for k, v in s.statistics():\n", " print(\"%s : %s\" % (k, v))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The command check returns sat when Z3 finds a solution for the set of asserted constraints. We say Z3 satisfied the set of constraints. We say the solution is a model for the set of asserted constraints. A model is an interpretation that makes each asserted constraint true. \n", "\n", "### The following example shows the basic methods for inspecting models.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x, y, z = Reals('x y z')\n", "s = Solver()\n", "s.add(x > 1, y > 1, x + y > 3, z - x < 10)\n", "print(s.check())\n", "\n", "m = s.model()\n", "print(\"x = %s\" % m[x])\n", "\n", "print(\"traversing model...\")\n", "for d in m.decls():\n", " print(\"%s = %s\" % (d.name(), m[d]))\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In the example above, the function Reals('x y z') creates the variables. x, y and z. It is shorthand for:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x = Real('x')\n", "y = Real('y')\n", "z = Real('z')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The expression m[x] returns the interpretation of x in the model m. The expression \"%s = %s\" % (d.name(), m[d]) returns a string where the first %s is replaced with the name of d (i.e., d.name()), and the second %s with a textual representation of the interpretation of d (i.e., m[d]). Z3Py automatically converts Z3 objects into a textual representation when needed.\n", "\n", "# Arithmetic\n", "\n", "Z3 supports real and integer variables. They can be mixed in a single problem. Like most programming languages, Z3Py will automatically add coercions converting integer expressions to real ones when needed. The following example demonstrates different ways to declare integer and real variables." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x = Real('x')\n", "y = Int('y')\n", "a, b, c = Reals('a b c')\n", "s, r = Ints('s r')\n", "print(x + y + 1 + (a + s))\n", "print(ToReal(y) + c)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The function ToReal casts an integer expression into a real expression.\n", "\n", "\n", "Z3Py supports all basic arithmetic operations.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "a, b, c = Ints('a b c')\n", "d, e = Reals('d e')\n", "solve(a > b + 2,\n", " a == 2*c + 10,\n", " c + b <= 1000,\n", " d >= e)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The command simplify applies simple transformations on Z3 expressions." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x, y = Reals('x y')\n", "# Put expression in sum-of-monomials form\n", "t = simplify((x + y)**3, som=True)\n", "print(t)\n", "# Use power operator\n", "t = simplify(t, mul_to_power=True)\n", "print(t)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The command help_simplify() prints all available options. Z3Py allows users to write option in two styles. The Z3 internal option names start with : and words are separated by -. These options can be used in Z3Py. Z3Py also supports Python-like names, where : is suppressed and - is replaced with _. The following example demonstrates how to use both styles." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x, y = Reals('x y')\n", "# Using Z3 native option names\n", "print(simplify(x == y + 2, ':arith-lhs', True))\n", "# Using Z3Py option names\n", "print(simplify(x == y + 2, arith_lhs=True))\n", "\n", "print(\"\\nAll available options:\")\n", "help_simplify()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# large numbers\n", "Z3Py supports arbitrarily large numbers. The following example demonstrates how to perform basic arithmetic using larger integer, rational and irrational numbers. Z3Py only supports algebraic irrational numbers. Algebraic irrational numbers are sufficient for presenting the solutions of systems of polynomial constraints. Z3Py will always display irrational numbers in decimal notation since it is more convenient to read. The internal representation can be extracted using the method sexpr(). It displays Z3 internal representation for mathematical formulas and expressions in s-expression (Lisp-like) notation.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x, y = Reals('x y')\n", "solve(x + 10000000000000000000000 == y, y > 20000000000000000)\n", "\n", "print(Sqrt(2) + Sqrt(3))\n", "print(simplify(Sqrt(2) + Sqrt(3)))\n", "print(simplify(Sqrt(2) + Sqrt(3)).sexpr())\n", "# The sexpr() method is available for any Z3 expression\n", "print((x + Sqrt(y) * 2).sexpr())\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Machine Arithmetic\n", "\n", "\n", "Modern CPUs and main-stream programming languages use arithmetic over fixed-size bit-vectors. Machine arithmetic is available in Z3Py as Bit-Vectors. They implement the precise semantics of unsigned and of signed two-complements arithmetic.\n", "\n", "The following example demonstrates how to create bit-vector variables and constants. The function BitVec('x', 16) creates a bit-vector variable in Z3 named x with 16 bits. For convenience, integer constants can be used to create bit-vector expressions in Z3Py. The function BitVecVal(10, 32) creates a bit-vector of size 32 containing the value 10.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x = BitVec('x', 16)\n", "y = BitVec('y', 16)\n", "print(x + 2)\n", "# Internal representation\n", "print((x + 2).sexpr())\n", "\n", "# -1 is equal to 65535 for 16-bit integers \n", "print(simplify(x + y - 1))\n", "\n", "# Creating bit-vector constants\n", "a = BitVecVal(-1, 16)\n", "b = BitVecVal(65535, 16)\n", "print(simplify(a == b))\n", "\n", "a = BitVecVal(-1, 32)\n", "b = BitVecVal(65535, 32)\n", "# -1 is not equal to 65535 for 32-bit integers \n", "print(simplify(a == b))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# In contrast to programming languages, such as\n", "C, C++, C#, Java, there is no distinction between signed and unsigned bit-vectors as numbers. Instead, Z3 provides special signed versions of arithmetical operations where it makes a difference whether the bit-vector is treated as signed or unsigned. In Z3Py, the operators <, <=, >, >=, /, % and >> correspond to the signed versions. The corresponding unsigned operators are ULT, ULE, UGT, UGE, UDiv, URem and LShR.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Create to bit-vectors of size 32\n", "x, y = BitVecs('x y', 32)\n", "\n", "solve(x + y == 2, x > 0, y > 0)\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Bit-wise operators\n", "# & bit-wise and\n", "# | bit-wise or\n", "# ~ bit-wise not\n", "solve(x & y == ~y)\n", "\n", "solve(x < 0)\n", "\n", "# using unsigned version of < \n", "solve(ULT(x, 0))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The operator >> is the arithmetic shift right, and << is the shift left. \n", "\n", "The logical shift right is the operator LShR.\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ " # Create to bit-vectors of size 32\n", "x, y = BitVecs('x y', 32)\n", "\n", "solve(x >> 2 == 3)\n", "\n", "solve(x << 2 == 3)\n", "\n", "solve(x << 2 == 24)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Functions" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "Unlike programming languages, where functions have side-effects, can throw exceptions, or never return, functions in Z3 have no side-effects and are total. That is, they are defined on all input values. This includes functions, such as division. Z3 is based on first-order logic.\n", "\n", "Given a constraints such as x + y > 3, we have been saying that x and y are variables. In many textbooks, x and y are called uninterpreted constants. That is, they allow any interpretation that is consistent with the constraint x + y > 3.\n", "\n", "More precisely, function and constant symbols in pure first-order logic are uninterpreted or free, which means that no a priori interpretation is attached. This is in contrast to functions belonging to the signature of theories, such as arithmetic where the function + has a fixed standard interpretation (it adds two numbers). Uninterpreted functions and constants are maximally flexible; they allow any interpretation that is consistent with the constraints over the function or constant.\n", "\n", "To illustrate uninterpreted functions and constants let us the uninterpreted integer constants (aka variables) x, y. Finally let f be an uninterpreted function that takes one argument of type (aka sort) integer and results in an integer value. The example illustrates how one can force an interpretation where f applied twice to x results in x again, but f applied once to x is different from x.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x = Int('x')\n", "y = Int('y')\n", "f = Function('f', IntSort(), IntSort())\n", "solve(f(f(x)) == x, f(x) == y, x != y)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The solution (interpretation) for f should be read as f(0) is 1, f(1) is 0, \n", "\n", "and f(a) is 1 for all a different from 0 and 1.\n", "\n", "In Z3, we can also evaluate expressions in the model for a system of constraints. The following example shows how to use the evaluate method." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x = Int('x')\n", "y = Int('y')\n", "f = Function('f', IntSort(), IntSort())\n", "s = Solver()\n", "s.add(f(f(x)) == x, f(x) == y, x != y)\n", "print(s.check())\n", "m = s.model()\n", "print(\"f(f(x)) =\", m.evaluate(f(f(x))))\n", "print(\"f(x) =\", m.evaluate(f(x)))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ " \n", "#In Z3, we can also evaluate expressions in the model for a system of constraints. The following example shows how to use the evaluate method.\n", "\n", "x = Int('x')\n", "y = Int('y')\n", "f = Function('f', IntSort(), IntSort())\n", "s = Solver()\n", "s.add(f(f(x)) == x, f(x) == y, x != y)\n", "print(s.check())\n", "m = s.model()\n", "print(\"f(f(x)) =\", m.evaluate(f(f(x))))\n", "print(\"f(x) =\", m.evaluate(f(x)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "# Satisfiability and Validity\n", "\n", "A formula/constraint F is valid if F always evaluates to true for any assignment of appropriate values to its uninterpreted symbols. A formula/constraint F is satisfiable if there is some assignment of appropriate values to its uninterpreted symbols under which F evaluates to true. Validity is about finding a proof of a statement; satisfiability is about finding a solution to a set of constraints. Consider a formula F containing a and b. We can ask whether F is valid, that is whether it is always true for any combination of values for a and b. If F is always true, then Not(F) is always false, and then Not(F) will not have any satisfying assignment (i.e., solution); that is, Not(F) is unsatisfiable. That is, F is valid precisely when Not(F) is not satisfiable (is unsatisfiable). Alternately, F is satisfiable if and only if Not(F) is not valid (is invalid). The following example proves the deMorgan's law.\n", "\n", "The following example redefines the Z3Py function prove that receives a formula as a parameter. This function creates a solver, adds/asserts the negation of the formula, and check if the negation is unsatisfiable. The implementation of this function is a simpler version of the Z3Py command prove.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "\n", "p, q = Bools('p q')\n", "demorgan = And(p, q) == Not(Or(Not(p), Not(q)))\n", "print(demorgan)\n", "\n", "def prove(f):\n", " s = Solver()\n", " s.add(Not(f))\n", " if s.check() == unsat:\n", " print(\"proved\")\n", " else:\n", " print(\"failed to prove\")\n", "\n", "print(\"Proving demorgan...\")\n", "prove(demorgan)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# List comprehension\n", "\n", "Python supports list comprehensions. List comprehensions provide a concise way to create lists. They can be used to create Z3 expressions and problems in Z3Py. The following example demonstrates how to use Python list comprehensions in Z3Py.\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "\n", "# Create list [1, ..., 5] \n", "print([ x + 1 for x in range(5) ])\n", "\n", "# Create two lists containing 5 integer variables\n", "X = [ Int('x%s' % i) for i in range(5) ]\n", "Y = [ Int('y%s' % i) for i in range(5) ]\n", "print(X)\n", "\n", "# Create a list containing X[i]+Y[i]\n", "X_plus_Y = [ X[i] + Y[i] for i in range(5) ]\n", "print(X_plus_Y)\n", "\n", "# Create a list containing X[i] > Y[i]\n", "X_gt_Y = [ X[i] > Y[i] for i in range(5) ]\n", "print(X_gt_Y)\n", "\n", "print(And(X_gt_Y))\n", "\n", "# Create a 3x3 \"matrix\" (list of lists) of integer variables\n", "X = [ [ Int(\"x_%s_%s\" % (i+1, j+1)) for j in range(3) ]\n", " for i in range(3) ]\n", "pp(X)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "In the example above, the expression \"x%s\" % i returns a string where %s is replaced with the value of i.\n", "\n", "The command pp is similar to print, but it uses Z3Py formatter for lists and tuples instead of Python's formatter.\n", "\n", "Z3Py also provides functions for creating vectors of Boolean, Integer and Real variables. These functions are implemented using list comprehensions.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "X = IntVector('x', 5)\n", "Y = RealVector('y', 5)\n", "P = BoolVector('p', 5)\n", "print(X)\n", "print(Y)\n", "print(P)\n", "print([ y**2 for y in Y ])\n", "print(Sum([ y**2 for y in Y ]))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Kinematic equations\n", "\n", "In high school, students learn the kinematic equations. These equations describe the mathematical relationship between displacement (d), time (t), acceleration (a), initial velocity (v_i) and final velocity (v_f). In Z3Py notation, we can write these equations as:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "a, t ,v_i, v_f = Reals('a t v__i v__f') #missing in HTML?\n", "d == v_i * t + (a*t**2)/2,\n", "v_f == v_i + a*t\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "v_f" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Problem 1\n", "\n", "Ima Hurryin is approaching a stoplight moving with a velocity of 30.0 m/s. The light turns yellow, and Ima applies the brakes and skids to a stop. If Ima's acceleration is -8.00 m/s2, then determine the displacement of the car during the skidding process." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "\n", "\n", "d, a, t, v_i, v_f = Reals('d a t v__i v__f')\n", "\n", "equations = [\n", " d == v_i * t + (a*t**2)/2,\n", " v_f == v_i + a*t,\n", "]\n", "print(\"Kinematic equations:\")\n", "print(equations)\n", "\n", "# Given v_i, v_f and a, find d\n", "problem = [\n", " v_i == 30,\n", " v_f == 0,\n", " a == -8\n", "]\n", "print(\"Problem:\")\n", "print(problem)\n", "\n", "print(\"Solution:\")\n", "solve(equations + problem)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Problem 2\n", "\n", "Ben Rushin is waiting at a stoplight. When it finally turns green, Ben accelerated from rest at a rate of a 6.00 m/s2 for a time of 4.10 seconds. Determine the displacement of Ben's car during this time period.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "d, a, t, v_i, v_f = Reals('d a t v__i v__f')\n", "\n", "equations = [\n", " d == v_i * t + (a*t**2)/2,\n", " v_f == v_i + a*t,\n", "]\n", "\n", "# Given v_i, t and a, find d\n", "problem = [\n", " v_i == 0,\n", " t == 4.10,\n", " a == 6\n", "]\n", "\n", "solve(equations + problem)\n", "\n", "# Display rationals in decimal notation\n", "set_option(rational_to_decimal=True)\n", "\n", "solve(equations + problem)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Bit Tricks\n", "\n", "Some low level hacks are very popular with C programmers. We use some of these hacks in the Z3 implementation.\n", "\n", "Power of two\n", "\n", "This hack is frequently used in C programs (Z3 included) to test whether a machine integer is a power of two. We can use Z3 to prove it really works. The claim is that x != 0 && !(x & (x - 1)) is true if and only if x is a power of two.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x = BitVec('x', 32)\n", "powers = [ 2**i for i in range(32) ]\n", "fast = And(x != 0, x & (x - 1) == 0)\n", "slow = Or([ x == p for p in powers ])\n", "print(fast)\n", "prove(fast == slow)\n", "\n", "print(\"trying to prove buggy version...\")\n", "fast = x & (x - 1) == 0\n", "prove(fast == slow)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Opposite signs\n", "\n", "The following simple hack can be used to test whether two machine integers have opposite signs.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "\n", "x = BitVec('x', 32)\n", "y = BitVec('y', 32)\n", "\n", "# Claim: (x ^ y) < 0 iff x and y have opposite signs\n", "trick = (x ^ y) < 0\n", "\n", "# Naive way to check if x and y have opposite signs\n", "opposite = Or(And(x < 0, y >= 0),\n", " And(x >= 0, y < 0))\n", "\n", "prove(trick == opposite)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Puzzles\n", "\n", "## Dog, Cat and Mouse\n", "\n", "Consider the following puzzle. Spend exactly 100 dollars and buy exactly 100 animals. Dogs cost 15 dollars, cats cost 1 dollar, and mice cost 25 cents each. You have to buy at least one of each. How many of each should you buy?\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Create 3 integer variables\n", "dog, cat, mouse = Ints('dog cat mouse')\n", "solve(dog >= 1, # at least one dog\n", " cat >= 1, # at least one cat\n", " mouse >= 1, # at least one mouse\n", " # we want to buy 100 animals\n", " dog + cat + mouse == 100,\n", " # We have 100 dollars (10000 cents):\n", " # dogs cost 15 dollars (1500 cents), \n", " # cats cost 1 dollar (100 cents), and \n", " # mice cost 25 cents \n", " 1500 * dog + 100 * cat + 25 * mouse == 10000)" ] }, { "attachments": { "sudoku.png": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAU4AAAFPCAYAAAAiDyfMAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAC/OSURBVHhe7V3tcd04Ety/+0dVV05ApRB0KVwKSsEpbAouZXApKAWnsClsCpeCzi0b8ogP5Hxg+DTAa1aprF0NSKB7pjkDgsQfrzyIABEgAkTAhcAfLmsaEwEiQASIwCuFk05ABIgAEXAiQOF0AkZzIkAEiACFkz5ABIgAEXAicCGcf/311+t//vMf/kyCwf39/euXL1/efvA7uavtu+SrNj9H8QNtbMeFcKLhH3/8wR9iQB+gD9AHhA9AGymcDAoGBX2APuDwAbNw/utf/ypR+j0+Pr7ip0oZWqk/KNFbhYDfK2BUCR/gUak/5Esv1SvxBQ1s8WUWTmnonDtNNf/+/fsrfqoclfojp1bIV99DyNdx5FTCBz2t1J+9+Dqc42QgMhAjN6tKjj9LIEZwzmpDvvaRpHBmedkkd8TE4bpPxUD0B6Ib5MQG5MvPFzPOgANWcjSW6jqB5Iuluu4lfQtmnFHkOu0YiAzEqDvxRqcjN0N8MePUebywmIHYwLDSmlTCh3OcOq3ki6W67iUJFpUcjRmMTij5YoWgewlL9ShG5nYMxHMD8b///e/betS2fg5r6Z6ensJL0rL5+t///vf68PDw1j/vsdqN7p9//nnFq4j//ve/L/h6eXnxwvNmn81XqBO/GnGOcwS9TdsZiE0crvtUUXwgSDIAe6/+yveFrR2L9mfv/F+/fn0XCWsfmt1Kwglc5QLxHl/Ayntk8+W9vrSncI6gR+F0oRd1fCmaCDhkMzj+/vvvDxmoN5OJ9qc3aGTDUiBcwPwwXkU4wU0TTfwLXHDja3xJLvE3z5HJl+e6PVsK5yiCov0MxCYO132qCD5SkFCWHzkxymTPEelP7/wQ8G2G5ekHbFcRTmT+uIEAD+CyPXBzazcYiKjnyOLLc809WwpnBoq/zjEDsYnDdZ8qgo90ULTvHchyWjB6sphIf3rXb1lUE41bnuNsWBzxEM3Ms/hyO26nAYUzA0UKpwnFiONbg6xlfJ65s0h/tgNtYtmyp9ZfEyDCaJWM0zJ+K6dbDDP48vLCjDMLsYPzzEDsFWDYvUQEH2uQtUzHU/5F+iMH18pOiHabx7MIRw+gVYRT8y9ZHXi4wnlH+dL65vk7M04PWortDMQmDtd9qgg+cu6wPRTaXhiiZRVY2TbSn9Ye12x9k1MIFM5jt5ArDzzTKhROd7jtNxhx/MRuvJ+qUn9WyWBkoO0tOZJzi575xRG+Gr7bPlE4LyMLWLX1rQ2fCsvHRjSAGecIepu2I4GY2I23U60inLK0Q4b37du3D8tbpLB6RSvKVxPq3ucVvX1ovK/CV8+Pt+s4IaLAsE1vWH0/ypf1/B47CqcHLZbqQ2hFHX+7RnIbiFimdK1SHWNoy216gU/h3HcR4CWrA8xxesQz6j9DTrvTmMKZiOoMxCYO132qEXyQeSK7lCUfAq/NkzXB8qzl9PZHvlLZW6MIQCicultI8bz2Kgi9dzYLCqcNJ5OVNxBNJw0arVz6bSGBiPX2f9Gg8/LVss3eK4RH/0/rx0pTK5axbqdfLG1g4+XLet6IHYUzgtpOmxmITRyu+1Rn4SPfRvE8dPD2h8KpUw78La++XmtqRe9xzILCGcOt28obiImXvjjVKhknshMNV/mAaK+E7mGtnTfCz62X6hi/tj5TLh/DAz/rcQZf1mtv7SicUeQ67WYgNnG47lNF8JEPhnqiGC3Tzyr9bl04La9cYmVEw4lznO4wGm8QCcTxq+6foVJ/Vsk45UJzZCeyDMTvcoG8XIhu4fkMvm5dOLeiKG92qB7wd/n1pL2XGq5VIVj8pGfDjDOKHDNON3JRodKWI7XPl3k7FO3P0XVuXTgt304FRlj94JlWOatC8PpMs6dwRpGjcLqRGxEqBBnKOplhoizEwwhP1iI7PdKfvcHfunACl7ZeU4pLW/uK/ye/0elxojP48lxf2lI4o8hRON3IVXL8WTIYN8iJDcjXPpgUzkUdbZU5zkR6Lk5VSRjIl870DHxxe2CdRwaiE6NKjs+MUyePfDHj1L0kwaKSozGD0QklX8cYVcJnlhsdM0497phxOjFiIPozGCfEqebky88XhTPggpUcjRmnTiD5Ysape0nfgg+Hosh12lUKxPv7+/e3M/B7haMSPtVKP/Kle2gl/wkJ5+Pj4/v7w20wn/Hv8/PzK34+49q9a1bqz93d3btw4vcKGFXCB3hU6g/5+q76aCW+oIG9L3IdluoUzj7JlYhlIM4ViORrLr5CwtnbMkBPtPMtKqXuLP10fsnXPkYs1efyn1CpTuHsk1xJGPhwaI1A1EdxnkUlf66WmFA4E/2ukqNROHViyRefqutewqfqUYzM7RiIDESzs2wMeaPTkZshvriOU+fxwmIGYgPDSmtSCZ9ZSr808AMnIl/7oLFUDzjUXpNKjsYMRieWfLFC0L2EpXoUI3M7BiID0ewsLNXdUM0QXyzV3bTOsX1pYFhpTSo5Pkt1nVbyxVJd95IEi0qONlKqy10ItT3EPbBl4tO20/Bcf2ub2Z+RfqDtCF+j155h6il6o9t+hV7zZ/zdcnCO04KS0WaVQMQ4LA5mdbIGXyY+zXGN1HTNMvsz0g8Kpw29CF8Uzg62ESBtFMWsKvVnJINp2Vz2iw5Z+MidFGNM/WyV1Z+RPrS2I3xlXL93jkr4nM1Xwx97WFkOZpwWlIw2lRxtJBDhPMgmqwhn2/wL/cLuiDIbNlLDjDMAVCV/PlM4240Ym/9ZDwqnFSmDXSVHGxHOp6enN3Gy3n0N0AxleEdTB9ZrV8+oRvgaweCobSV/Pks4sUNq2znVs10xhTPR6yo52kggtra4E2ceUXyQcba27d/oNrxyPNH+ZGLCUt2O5hl8eUt0jS8uR7Lz+W55BrGBbrw1GRHOJkoYz7Y8RjaKOdDIkYkPhTPCgK9NJl++K/ets/vz8vLyVllh+gc3Z8/BjNODlmKbTexI16LCKZcitRKm94QdAup1tkx8KJwj3mFrm8mX7YrHVpn9ge+2+fJIIkDhzGD01zkyiR3tVlQ4t/OJyDibQGIOSJ4X4uk5MvGhcHqQj9lm8hXrwcdWmf0ZfQBK4cxgdCHhbOULhGnvToynj7Kct0KY6fgUTivqcbtMvuK9+N0yqz9IBFo1hXNGDgpnBLWdNlnEZnQpmnHiKSPGcfSEsa3zhHh5ss5MfCicGV5yvdI4o7dZ/tOyTZTq0YPCGUWu0y6L2IwuRYXTcm2IaxMu3LmtRyY+FE4r6nG7TL7ivcjPOEfmNltvKJwZjC5UqlvhiCxCzwxECqeVqbhdJl/xXuQKZ6uWcMP3PtyUY6BwZjBK4TShmBmIFE4T5ENGmXwNdSQxvtrLHV+/fh3qEoVzCL6PjSs5WrRURzv84CHR3oH5z96e0hqUmfhQODW0x/+eydd4b8a/LSCX2h35t6WvFE4LSkabSo42IpzaouA2uX705L0HWSY+FE6jUw6YZfI10I33pqP9kQ81R8p0dIjCmcFoYimR1Z2ocErnwrIjeWfGQyEpmp6PImBco44vsaFwZnnK/nky+cro7Wh/UJ5nfbyGwpnB6ELCub2b7n2XE47jvWuPOj6FM9FZDafK5MtwOdVktD9t7WbGx2sonCpddoNRYu1X0i2jGWc7MzJPeQ4IKByP76rr2EcsRvmKXFNrU8mfRysWOS8fecVyixWFU/Mex98rORoDUSeOfB1jVAmfUeHUvcFnQeH04XVoXcnRKJw6seSLwql7Sd+CwhlFrtOOgchAjLoTb3Q6cjPEF7/HqfN4YTEDsYFhpTWphM8spV8a+IETka990JhxBhxqr0klR2MGoxNLvlgh6F7CUj2KkbkdA5GBaHaWjSFvdDpyM8QXS3Wdx9Kl+v39/ftrkfi9wlHJ8auV6uRL99BK/hMq1R8fHy82z2qDuua/z8/Pr/i55jWPrlWpP3d3d+/Cid8rYFQJH+BRqT/k67vqo5X4ggb2vtdwmHFSOPskVyKWgThXIJKvufgKCSfS1ApHpdSdpZ/uEeRrHyOW6nP5T6hUp3D2Sa4kDHzYsEYg6qM4z6KSP1dLTCiciX5XydEonDqx5IurIHQv4XKkKEbmdgxEBqLZWbgcyQ3VDPHF5UhuWnO/Nxm4/IcmzDh1BGcIRH0U51lUwoeleiLPJHYfTAqn7miV/Id8rcEXM06dxwsLBiJL9YDbvDWhcOrIzRBfFE6dRwqnE6NKjj9L6eeEONWcfPkruuWFE9s+fPv27cOdvn3hHA4TOUYdbfvV9ZH+MIPRGRzlS7+C3eIW+ELMPTw8vL1xEzlm4Gtp4QQBbf+RvT11IvsuR4mFQ2Hjs72+4P9790m5hUCMBJ9sE+Vr9Lq99rfAV9ssjcJ5hgc5zhlxfOw90kQT/yLrbJuOYRdHKWDevUki/cFw5TXhXOgHDvRVBpRnL+hbCESHq3RNo3yNXvcWhVPunkrhPMODHOeMOD42G2uZHURze8hNna6x/a10KPTtKKhQ5lgPCqeOVMR/9LPGLFbmSyYrI9s6z8DXsqW6LIf3XNxi02sbIVYGzN7cKjLQ1idrFrxyIMak6bJVhK+sa2/PszJfraLCdBOF8ywPcpw34vhSFPf2Bb+mcFqv1aYXrHOvKweiw0UOTSP+k3XtWxHOJpateqNwnuVBjvNGHF8r1TGP2Mi9RqluFc5217b2icKpO1LEf/SzxixW5KvFEm76LUmhcMb8I7VVxPG3T7BxR2ykbp+2ex7GYGCR/sin++2h0BYk9M8qsK3tioGY6jxBvrL7sCpf8Nnm23IKisJ5lgc5zhsRKpwepEIw95Yk4f97RTMqnHKJxt6SIzk3ZH0iSeHUHSnqP/qZ/Rar8dXGs/VpCqffN9JbRB0fwikFq7d+Umai1o5H+iMf/GyXR+FpZK+flv6sFoiWMXttInx5r2G1X4mvdqPvfbeXwmn1iBPtIo7fK9VbiYx/ZXZnnU9sQ4z0B223a9y2Qi7nZZlx5jlUlK+8Hvw+0yrCCUzho3JeU+JF4TzDe5znjDi+FMa9J9TSxvPGTqQ/bcgQbfSnvZIGB4Nwt+VHzeGsazlXCUSnS7jMR/hyXchgvAJf8pVKVEu9g8JpcIazTSKOL4Vpj1xZPluFKjrHacFILsq3bluyQiBasBmxifjPyPWO2q7AV8s2j14d7v3NiukMfN30AngQ6X2KfaZwyiVS1gx4hUC0BlTUboZAjI4to50XHwrnD93YAr9KIFoWwMvlP5irsR5eR8N5kd1q7eQDor0seVW+rNhH7DTcI+eMtlklvrTxs1TXELrC3yOOry2AR7fxDnsj2PqmTjTjlA+GeqIYKdPRl1sJxBE3i/jPyPVWL9Ut2FA4LSidbBNxfFlO9L6OJB8M4e97i9J7Q4v0Ry4U3q4fRYku15rKhcQatBRODaHYCwv6WWMWt8IXhTPmH6mtIkKFDmjLf9pyCmtZ3AZ1Vn8gntaPe7S+3EogjjhUlK+Ra+61vRW+KJxneI/znCOO39Zsbj8gDAeW3+j0dGmkP22xu8ww0TdkwJ6sl8JpZ2yEL/tVbJYUTh2nGfha9uGQTk/cYgZi46Mbb1kJH4ymUn9uRThHvGgGviicAYZnIDYwrLQmlfChcOq0kq99jPZudBRO3a8uLCo5GjMYnUDydYxRJXxmudFROPW4o3A6MWIg+jMYJ8Sp5uTLzxeFM+CClRyNGadOIPlixql7Sd+CpXoUuU47BiIDMepOvNHpyM0QX4cZ55cvX97eTPnsn8fHx1f8fHY/2vUr9efPP/98f/sJv1fAqBI+wKNSf8iXrieV+IIGtjWp8sM7h8Lp/foJ7f/48NEQ4kE86APr+ACF88c3MOnQxIA+QB/w+IBZOFmq98uKSqUES7+5Sj/yNRdfoVLd+jFdfbp3zKLSZPEs68zGEB9rTb78y1vGEB9rTb78fHE5UsDnKjkan9LqBJIvroLQvYTLkaIYmdsxEBmIZmfZGPJGpyM3Q3wx49R5vLCYgdjAsNKaVMKHUys6reSLpbruJQkWlRyNGYxOKPlihaB7CUv1KEbmdgxEBqLZWViqu6GaIb5Yqrtp5fcdNcgqOT5LdY2tWv48C18UTt2vOMfpxIjC6Z8z80CM7VXkFA0WcbddBLC3lfdYiS+MHzs7SHyw2wI2b8Q4vQc/8uFF7MC+kqNxjlMndhW+IArbLWC2b75AJK61h5aOfMwiyhfayS1pem8FeXazRe8pnDEOu62ixCZ24f1UFE4d1VX4klzLParavlpNKK6xa6uOetwiwhduFk00e7vayhuOZ0NECmecR5bqTuwiju+8hMu8Un+iNzoIQxNGiGbvkFteezKrSvhE5zhRijd8UKpvD4kfRNR6UDitSBnsKjlaNBANwwybVMInGojhwSsNo3xJUdybx0TmKbNO6xhW4EuW5Xvjtths21I4rV5ksKvkaNFANAwzbFIJn9WEU/t+REQcVuBLjnvvxhLBhsIZloHLhpUcjcKpE7sCX5iXQ9Z5ND8nM86HhwcdmF8WlfCJ3ui0Uv3l5eU9G2epbnaNXMNKjkbh1Lm9Fb5ueY5zu+IAWLTMc/u0HSJqPZhxWpEy2N1KIBqg6JpUwieawUTHrrU760YnMyo8Vfas51yFL4wZgrm3JAn/3yOa4JLCqXm04++VHO2sQHTAcWFaCZ/VhbOJhXwodKvrOIEFVhMcfdVdZqIWH6dwWlAy2lQSBgqnTtqqfGG+U2ZXmLvzima1G0u0P71SHXO+OLbrXDnHqcfMKRarBmIWWJXwiQZiFhbb82Tc6CCOckE3HgR5FnVv+7QCX5b5XWmztxbWyhffVQ9ESCVHywjEAASHTSrhs5pwQiBlWe4tPXvErcAXbh4Nl72sO7LqgKV6ojpUcjQKp07sKnxJ0UTG2UpRHYFji0r4RG901jWaVruGGIVz1LtE+0qORuHUiV2BL8zhtflMiKbnqbmGUCV8MoRzDxv8f++bVRROzXscf6/kaBROnbgV+ML71y3oM0UzKlQ66nGLCF/aAnj0RmJofZefwhnn8aJlhNjEy384FYVTR3YFvhrPEIjsoxI+USHHGGQ2CZFsN5iRr0dROBO9rZKjUTh1Ylfg62htYu9v2jvtErVK+ESFE+3kHPAeXt7vlVI49fgyW1RyNAqnTtsKfFE4dZ5h0bLL7QefEScyC7WdjW8OWXEy2a0QiKaBBo0q4TOSwQSHf9iMNzod1Ur+w4xT58tsMQOx5sGcYFgJHwqnTjD52seIwqn7j9mikqMxg9FpI1/HGFXCZ5YbHd8c0uPuwqKSo1E4dQLJF4VT95K+BTPOKHKddgxEBmLUnXij05GbIb6Yceo8MuN0YlTJ8Wcp/ZwQp5qTL85xpjrU3skqOdr9/f37wl/8XuGohE814SRfuodW8p9Qqf74+PjaBvGZ/z4/P7/i5zP7IK9dqT93d3fvwonfK2BUCR/gUak/5Ou76qOV+IIGtjW08qWCw1KdwtknuRKxDMS5ApF8zcVXSDg9r23pCXjcolLqztJP55F87WPEUn0u/wmV6hTOPsmVhIFPadcIRH0U51lU8udqiQmFM9HvKjkahVMnlnxx+ZjuJX0LCmcUuU47BiIDMepOvNHpyM0QX1zHqfN4YTEDsYFhpTWphM8spV8a+IETka990JhxBhxqr0klR2MGoxNLvlgh6F7CUj2KkbkdA5GBaHaWjSFvdDpyM8QXS3WdR5bqTowqOT5LdZ088sVSvYsA9h7B/tPyq9D4HZ/ajxyVHC0jgwEO8jx4UwL4RPfsroRPtnC27RkifoM2GXxFr5099SR3jdS+UO/pcyX/udk5TmxO37ZV7ZFr3e1OEj8DsRZHheNvtxjYYuTdoyVbqCzj0Gwy+WqBpF1z7+8rCSdw1QSz/d2DVyZfnuv2bG9SOLH/SBNN/NsyTAgGBLOR+vLy4sJ3BmItA5JOgewSeOEY2RVwZeGU28ta8PUEYvR8Ge2i/tyy7+wXZaL9ycBie46bFE4pjiBjD5SHhwcX5jMQqw0ImXi7cUA0ewf+f7PxZOaV8BkR8jbFAxzgIzK70vC9hYyz+QeF8wfbq5QSyJp6XzWRDo1Ms9lASKxHJWGI8iVFse0/vR2/xBAZu/WohM+IcB6VolYsrBlM9HwZ7aJ8YY93xM/ejTfat2h/otc7andzGacsq44eAjXh9DwomoFYzYms2UIky6qEz4hw4obSxtL+jczZSS6iNzqNz5G/R/lqY0GsZR7R/mT2oZ3r5oSz3Q3h6L0yfQTkGYjVxocbBcTz6IYhM07PdEYlfEaEs4chhfM3Kg0L8L2dzkD8eZIRiXUl/7k54ZQDBikoy7fLkVZYbnNmBnPrc5wUzv3br1yKdLRqBQK6NxW0d3YKp5b2GP8eAVKWmO3pX2/pBDIpz/xmdgZjhGDX7CzhlPO/CAyP80f4GsXhqH1mf5hx/kR6O/8rkxDEk/RLiKfnyOTLc92e7c1lnFuRBHlNINvT0mYD8ZxVGLKFc4sN13F+DCcK50885I11rySXFZ5nuozCOSr3v9pHgJTCubeURi5X8kxwR/qTBMXFaTKFEwEgyy44vjcbr5aRZ/eHwvnTBTH/jTg48g9Z6Xmyzhnia9l31aVw7mWTci0jRMJ6zECsdSywAw4yO0AGHp3YzxYqzzj2bDP5onDaGVlhOdvNleotc9IW586+3GY045RZATCLPjCT4ZQpVPYw3bfM7A+F08fIqvG1bMbZBIXCue/oUjSRcbZXLn2hcWmdKVSjfcnOgCmcPkYonD68Uq0jgdiW0hy98bLCOsVoxonpi5aVQzQ9D8c0ciN8aecc+XtmfyicP5mA3+Hn6DsPcipMS2CqViw3V6p738X2vDaWGYgjgtAcWHu1tHcN+WZVpmhmZ3ij+GT3h8L5WziBxdGKFLkO2DNnPkN8LVuqb0VFfv2Hy5F+f5PA87TTKmKVHJ/CqbMW4Ws7zSMzz+3XtTwPXrP50kd/bHFzGSfgkJ+V6y1+x/+LLLmJONoogXvto6X6Hh57/3/WUis7EJlx/vZE6XtHfuOtaGaIr6UzziaeWK+5/SwYSPeUD7PNwWhCTeHUEOr/ncL5EZfe7gGYO+e76jH/Sm1V6Q6UncGMAhXNOEeve9SefO2jQ750z6vkPzdZqusUxSxmIDY2spxWlfDhjU7nlHz5b3TLl+q62/gtKjkaMxidP/J1jFElfGa50VE49bi7sKjkaBROnUDyReHUvaRvwVI9ilynHQORgRh1J97odORmiC9mnDqPzDidGFVy/FlKPyfEqebki3OcqQ61d7JKjnZ/f/++4Rx+r3BUwqeacJIv3UMr+U+oVH98fLzYrKoN6pr/Pj8/v+Lnmtc8ulal/tzd3b0LJ36vgFElfIBHpf6Qr++qj1biCxrYe6X5sFSncPZJrkQsA3GuQCRfc/EVEk7Pa3Z6Ah63qJS6s/TTeSRf+xixVJ/Lf0KlOoWzT3IlYeBT2jUCUR/FeRaV/LlaYkLhTPS7So5G4dSJJV9cPqZ7Sd+CwhlFrtOOgchAjLoTb3Q6cjPEF9dx6jxeWMxAbGBYaU0q4TNL6ZcGfuBE5GsfNGacAYfaa1LJ0ZjB6MSSL1YIupewVI9iZG7HQGQgmp1lY8gbnY7cDPHFUl3nkaW6E6NKjs9SXSePfBUs1dveJDp9+xarEdv7aja28Ijsac4MRvesSv5Dvtbg6/SMszmKDtf6wom9VyCQR9tWYNsB7NBpPRiIOlIUTk6t6F5SaI5TbkEb7Xi1UmukP1Lk5K6b210BIZ74f5aDwqmjROGkcOpe8onC2bbdhShsN0eLdnxEqEauedQ2Eojefd6xwZzloHDqKEX40s8asyBfOm4z8JVaqmPAe2WoDtfapTpuJg2bve1SkWU2G2SdloOBqKM0QyDqozjPohI+1RKlq6zjhCBsP2s2up1qNSCj/WnCqb3/L288llChcOooVRIG8rUGX6kZZw8SCudPVPAkHeJ5tJe7zDgx1WE5GIg6ShTOY4wq4RNNTHQviFlcJeOkcMbIaa1kOc85zjEsZetKwsAbnc7rDHwx49R5vLA4g9iXl5cP85t786DbzjAQdQLP4Eu/at+CfOnIzcAXhVPn8VThbCsR5EMhruMMkHLQZIZAzB2x72yV8GGp/os7znHuOzHmO/H0vGGExfEe0cSZmcHoIlFJGMjXGnwx49R5TM84IY7yDSI8CDp6aHTURQaiTiCF8xijSvgw42TG2fXW9u4+skxkm5H30+WJKZwUTh0BCmcUIz5VjyLXaRe9Q0vRRMZpfa2SGecYeVG+xq7ab80bnY7qDHyxVNd5TCnV8RCozWdCNK1PzbXuMRA1hF7fX8rQLc+3IF86xhTOHxjx4dBPR5EfPMkSTZyXgbhGIOqjOM+iklBxjpNznB88vQnc09NTagRQOHU4KwkD+VqDL5bqOo8ppfrRNzh7f9PeaW+dYiDqBFI4jzGqhA8zTmacH7yVwqkL3FkWlYSBNzqd5Rn4Oj3j1GHSLSoBOcsdUUf1PAvytY8thVP3u0r+82nLkXSYdItKQFI4yZeOAIVzBKNK8U7hHGFy03YGYhOH6z5VJXx4o9PpI1/+Gx1Ldd2vUh4OBS5jasLST4epkjCQrzX4onDqPFI4nRhVEipmnDp55IsZp+4lCRaVHI0ZjE4o+TrGqBI+s9zoDjPOL1++vL2Z8tk/j4+Pr/j57H6061fqz59//vn+dhZ+r4BRJXyAR6X+kC9dTyrxBQ1sSwnl2upD4fSuPaT9H7u7fBIbYkMfmNsHKJw/PulGJyYG9AH6gMcHzMLJUr1fVlQqJVj6zVX6ka+5+AqV6tb3pfXp+TELTl77n/qNIT7WmnyRrxEPquQ/XAA/wiQXwLvQq+T4szyldQGcbEy+/Dc6ruMMOGElR+NyJJ1A8sXlSLqX9C2YcUaR67RjIDIQo+7EG52O3AzxxYxT5/HCYgZiA8NKa1IJH5bqOq3ki6W67iUJFpUcjRmMTij5YoWgewlL9ShG5nYMRAai2Vk2hrzR6cjNEF/LlurSQa2LXHVKf1rMQKx1LGfYVcJnJb6wyV+2L2fgg22vt/GGnVz/+uuv0G6ulfzn5h4OUTjPkETbOSs5foYw2EZtsxrJOIFrJeGEkEMgj/qELbH//vtvGzi/rCr5z80Jp5WpBgzujtZjBmKtYznDrhI+KwknMjuIVPaLKVG+pKggfv755583d8K/+O8mqBDP9jeLv0X7Yzm314bC2UGs7XWOu6bnmIFYz3iybSvhs5JwNjGqIJzIIpsw7iUdUjy/fv1qdrNK/kPh3NCGOyDuhCB/xVLC7KUnGFZy/JWE8+np6c1fPdWRhd4IX1IUUbL3DsSYzDotfZmFr2UfDmkkRUr0ds6Io2n9if59ZM4sek2tXSV8ZglEDVP8vXGNSinziPBlzX7l/Ke1z5H+WM/ttWPGKRB7eXl5uxM+PDws+9TP6yCZ9pUcfyXhbCIEfCFc8N/2/5CNYg40ckT4wrXQh6NryowTfbUekf5Yz+21o3D+QgxlRXO4azqalzCrPTNOHakZAlEbhVyK1KaYek+zIaB7pfPeNc7Ch3OcGqsn/z2TWGuJcTSkzP6MQkfh1BFcga/tUiS5RhJz9NIPIJ6e4wx8WlUHcYfQe8T8jP548JC2zDh/oAHy2t0a5ESPGYiNji2jXSV8VinVpRDtVUpyTaXHvzP5QoxtlyKt+PD1ph4ONUI98y09Icl0tFGhYsapI7gCX5gvxDiORKit80SW58k6s/DB9eU0AoTcK5qz3OhuSjhH5zZbiGY5mh7yugWFU8foVvj6rOU/EEeZ7SLOos8PKJy6P5stMhy/3Y298y3MOM00vRtm8OW/6n6LSv05+0Z37eU/MstFbEXfT5fszcDXzWScbfGw5w2GvVCcgdhM4fGeqxI+s2QwXoz37K8pnFI0kXF6Xqs8Gm8l/7nph0NyKQcm2UePGYgdHeNI+0r4rCKcCGD8HPmvfA3S81pmhC/5oBWi6XlqrvlWpD/aOaN/v2nhlHfGDIJnIDbqKBntKuGzknBqL23Ip9meOcYIX+07D+hTRkyxVM+IvM05IsTKU6A8z/yqzGh/MiE6e84s0tdK+KwinNuyWGae268RXeOjNc3vPE/vrb5UyX9uOuNsSySyPo4wA7FWJz3DrhI+qwgnxmH5xixsvBlghC/rd0Gb3dlTB2f48RZzOYblHw7JeR9P+TL75PVZjmQ5byQQLeeN2lTqz2iF0PvaOhKDa7+rTuHceOMosVHnnkWoVspgzuCqGj7V+sP40r1uhhvd8hmnTpPfYgZi/aPKa1EJHwqnziv52sfopuc4ddfxWVRyNGYwOnfk6xijSvjMcqNjxqnH3YVFJUejcOoEki8Kp+4lfQtmnFHkOu0YiAzEqDvxRqcjN0N8MePUeWTG6cSokuPPUvo5IU41J1+c40x1qL2TVXK0+/v79+0T8HuFoxI+1YSTfOkeWsl/QqX64+Pj2zcAP/vn+fn5FT+f3Y92/Ur9ubu7exdO/F4Bo0r4AI9K/SFfup5U4gsa2FvEf1iqUzj7JFciloE4VyCSr7n4Cgmn5zUpPQGPW1RK3Vn66TySr32MWKrP5T+hUp3C2Se5kjDwKe0agaiP4jyLSv5cLTGhcCb6XSVHo3DqxJIvLh/TvaRvQeGMItdpx0BkIEbdiTc6HbkZ4ovrOHUeLyxmIDYwrLQmlfCZpfRLAz9wIvK1DxozzoBD7TWp5GjMYHRiyRcrBN1LWKpHMTK3YyAyEM3OsjHkjU5Hbob4Yqmu88hS3YlRJcdnqa6TR76KlOrbr1S3L1SDoMhBYv3ERnDOajPKF7Z+wDYn2DunvbWB36Nf8I/2R+6Oqn3x3IodM04dqShf+pn9FleZ44SjSWfvOVtk359KQDKD0Z1vhC9sddL2iOr5Dzbe8x7R/qCdJpjt79Y+UTh1pKJ86Wf2W1xFOKVowsHbBvUIBtkB797mlYCkcOrOF+UL/tJEE/+2DBM35LZTKYTqWv7TdpbMfBGEwnme/+hn9lucLpxy+9K9LUNbJx4eHlwjiAai6yIO40r9WSkQpTj2pnWu7T9tn3IKpyM4EkxniK+0h0MygPfmMpFRtNLGM19VCUhmnHpkRPiSvrEnVMg0m/+girEekf7g3EgAcL3I9NJe31a60Vnx99pF+fJex2J/esYp54KOOtRKMc9cVSUgKZy6u0X4+vbtm+mmes0bbwsa9C3roHDqSEb8Rz9rzKKMcLZ5UPxrPSoBSeHUWYvw1bI7CGN09cVezyL9wbmaSKM9sk5MMbX/F93HnMJ5jv/oZ41ZnC6c8kloeyi07ep2eYd1KFHHt57fa1epP6sEohwH+EBZvl2OBPGCD3mPCF/SV4+e8kNAPX1ahS8vBx77CF+e83tsTxdOObG/NyfUJtu9SzgqAcmMU3e7CF9yqkc+aNwuB0LW55nfjPK1XYokRXu7SmTvYWgPKQrnOf6jnzVmcbpwysl93KExL9TuxHA0KawUzhiJKwfiViAhRk0g24L4ZgPx9GR5ESGXD6L2HmTKjNg6vUDh1H0/wpd+1pjF6cKJbh1lCnB6OY+F/7YelYCMZjDWsXrtVglEKZx7Dw7lzdfzwCbiP0gE0O4ou7UswdvyuQpfXj/12Ef48pzfY3sV4USH4HBwcDmRLl+Xk1mDdQCVgKRw6qxF+JLCuZdNQsTkK5h6T35aRPpjOfe2yrK0oXDqKJ3Fl37lS4urCedR56TjexYVVwLyzEDMJDZyrqw2Eb7aAxjNL6zL3uRYIv2xYuHtD4VTR/ZMvvSrf7QoIZxy3sizqLgSkBRO3fUifDUHpXDq+GZbRPjK7sO1bnTefp8unG1O6GiSXM5ReZ6Mkth9ulfJYNqKC2See4csjT2v7Ub8B7ji5+i9+EgFtQpfXgHy2Ef48pzfY3u6cMqJ8p4oRpysDbASkMw4dbeL8CX9w7Kc7eyKpQXM0RN8ubzO+goxhfMc/9HPGrM4XTgxoS+/bCPv1PhdLiK2Lt2gcOpkrxSIciwQpfYixWcsR5KJAB5uSn9Gv6Roet6CW4kv3TtjFpEbb+xKeqvThRNd0JYjyU+F6V3+bVEJSGacOnNRvuRn5fa+gwmR8kzzjPAlg2avP7DxrCmlcJ7nP/qZ/RZXEU50qy12lxkmnF1mEN7uRwPRex2rfaX+rBaIveVsEC2M01oOb3kc4Wu7mwH60nY0iPRnNb6sMeOxG+HLcx2L7dWE09IZr00lIEcyGO+4LfYMRB2lSv5DvtbgK+17nDoccYtKjk/h1HkkX/sYUTjn8h9mnDpfZotKwsBA1GkjX8cYVcJnlsSEGacedxcWlRyNwqkTSL4onLqX9C2YcUaR67RjIDIQo+7EG52O3AzxxYxT55EZpxOjSo4/S+nnhDjVnHz556QpnAEXrORo9/f3718Mwu8Vjkr4VBNO8qV7aCX/CZXqj4+P75/kaoP5jH+fn59f8fMZ1+5ds1J/7u7u3oUTv1fAqBI+wKNSf8jXd9VHK/EFDWwvP8gP0BxmnBTOPsmViGUgzhWI5GsuvkLCqX3iS0+6cywqpe4s/XROydc+RizV5/KfUKlO4eyTXEkY+JR2jUDUR3GeRSV/rpaYUDgT/a6So1E4dWLJF5eP6V7St6BwRpHrtGMgMhCj7sQbnY7cDPHF5Ug6jxcWMxAbGFZak0r4zFL6pYEfOBH52geNGWfAofaaVHI0ZjA6seSLFYLuJSzVoxiZ2zEQGYhmZ9kY8kanIzdDfLFU13lkqe7EqJLjs1TXySNfLNV1L/lhga0OsAkX3giIHJUcLSOD2X7lvH3hHOOMHBF8LNtUbLevsPYt0h/rub12GXx5r6nZV8JnlhvdTWaccptizal6f6/kaCOBiBsItjXZ208H/9+zm2TDKoIPhTPiiTltInzlXLl/lkr94cOhXxxtN5SLOMAMxFrGJUUTN5O2qyT2jZIOc7S3+LVvLK1fHkFfhS8LpxGbSvgw44wwuNMmi1gIgtxE7pZLdXkDeXp66iLfRArTGp4ji6/tNb99+/aWHXu2450lED34ZtuexVe0n5X6w4zzB4stw5J7YkfInYFYbVzSIfbmMpGBtjLes6PjGfjIrYOvtT2whmHk7yNTK5HrWdqcwZfluns2lfpz88LZxLJlK00QIgTPQKw2LjmveWTbMnSU8tbjDHwiJXrr7xn9sWKxtaNw6sjNwNdNPBzCHF3bDxsPRHBQOP8wYdCydE95nO34jT9MGTT+9PD7bZHdH8+1KZx+tGbga3nhRKC1rEmWpLcunHKutz0U2ro4sLNmprJtpuPLpWOe6YKz+uOXgY8tmHHqCGb6j361Y4ubLdX3SrxbF065JGvvCbWcC/Y8SMt0/NaHkU8cZvbnrEAcPe9I+0r4YByV+nOTwnkUdLcunPLBD7JPPLFuZTAevkhh9WKV5fh71YJXJLL6471uz54Zp47iDHwtW6oD/O28pqTMKwYrln7bNa3bhfBYpvSZpXq78XmXQ21Dc4ZA1OXkPItK+DDjTOTZS6ycF9tbukLh/EkQMk9kl+0V1LZOss0nNpw84uXla89VWp+ic5vtvFn9yXBpZpw6ijPwtWTG2bLNo1cJe3/TKf1pMQOx1rEc2eGm09vhTzt3Bj4tG8Y0QuRJ+ooVgoZ79O8ZfEWv3WtXqT83NcdJ4cxx47YMyPvOeobjt2kCz/rRvVFn9CcH0dcPr7KOPPDK6k+1RKBaf25KOC1OdeulOkp0TVDkAyLP2zraeTV+5DIo73vys2YwGiZn/n2Ur+y+VeoPhXPD7q0Lp3ww1BPFaJmekTHIvo2W6Rn9yRQGznHqaFI4dYxMFmcAeevCKZf6YB5RZnb4XS6Qly8OWAgb5atlulml7Gh/LGO22lA4daRm4GvJh0M6NXzlEhhpy5EgnpEn2qOO30Tb8+m4I85H+2PxJ6sNhVNHaga+KJw6jxcWMxBrHVZb7C4zTLyXDtHaexVTO/cIPnKKICLanOPU2Ln8+whf/qvpLSr1h3OcOl9mixmINQ/mBMNK+HCOUyeYfO1jROHU/cdsUcnRWPrptJGvY4wq4TPLje5mS3U93PYtKjkahVNnknxROHUv6Vsw44wi12nHQGQgRt2JNzoduRniixmnzuPSD4cCw1ebVHL8WUo/FdQTDcgX5zhPdK/fp67kaMxgdMrJFysE3UsSS3UsUUFgfvbP4+PjK34+ux/t+pX68+XLl/cPceD3ChhVwgd4VOoP+dL1pBJfcpmefCHjsFT3fl2I9r/38SEWxII+sJYPUDh/7M1NpyYG9AH6gMcHDoUTb4xUKPfYB72kAUb39/evKP/wg9+Jmw23z8KJfNXm58gv5CvAF6V6dBKV7YgAESACt4IAhfNWmOY4iQARSEPg/76vmIh2d5FcAAAAAElFTkSuQmCC" } }, "cell_type": "markdown", "metadata": {}, "source": [ "## Sodoku\n", "\n", "

Sudokuis a very popular puzzle. The goal is to insert the numbers in the boxes to satisfy only one condition: each row, column and 3x3 box must contain the digits 1 through 9 exactly once.\n", " \n", "![sudoku.png](attachment:sudoku.png)\n", " \n", "The following example encodes the sudoku problem in Z3. Different sudoku instances can be solved by modifying the matrix instance. This example makes heavy use of list comprehensions available in the Python programming language.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "\n", " # 9x9 matrix of integer variables\n", "X = [ [ Int(\"x_%s_%s\" % (i+1, j+1)) for j in range(9) ]\n", " for i in range(9) ]\n", "\n", "# each cell contains a value in {1, ..., 9}\n", "cells_c = [ And(1 <= X[i][j], X[i][j] <= 9)\n", " for i in range(9) for j in range(9) ]\n", "\n", "# each row contains a digit at most once\n", "rows_c = [ Distinct(X[i]) for i in range(9) ]\n", "\n", "# each column contains a digit at most once\n", "cols_c = [ Distinct([ X[i][j] for i in range(9) ])\n", " for j in range(9) ]\n", "\n", "# each 3x3 square contains a digit at most once\n", "sq_c = [ Distinct([ X[3*i0 + i][3*j0 + j]\n", " for i in range(3) for j in range(3) ])\n", " for i0 in range(3) for j0 in range(3) ]\n", "\n", "sudoku_c = cells_c + rows_c + cols_c + sq_c\n", "\n", "# sudoku instance, we use '0' for empty cells\n", "instance = ((0,0,0,0,9,4,0,3,0),\n", " (0,0,0,5,1,0,0,0,7),\n", " (0,8,9,0,0,0,0,4,0),\n", " (0,0,0,0,0,0,2,0,8),\n", " (0,6,0,2,0,1,0,5,0),\n", " (1,0,2,0,0,0,0,0,0),\n", " (0,7,0,0,0,0,5,2,0),\n", " (9,0,0,0,6,5,0,0,0),\n", " (0,4,0,9,7,0,0,0,0))\n", "\n", "instance_c = [ If(instance[i][j] == 0,\n", " True,\n", " X[i][j] == instance[i][j])\n", " for i in range(9) for j in range(9) ]\n", "\n", "s = Solver()\n", "s.add(sudoku_c + instance_c)\n", "if s.check() == sat:\n", " m = s.model()\n", " r = [ [ m.evaluate(X[i][j]) for j in range(9) ]\n", " for i in range(9) ]\n", " print_matrix(r)\n", "else:\n", " print(\"failed to solve\")\n", " \n", "\n", "# Let us remove 9 from the first row and see if there is more than one solution\n", "\n", "instance = ((0,0,0,0,0,4,0,3,0),\n", " (0,0,0,5,1,0,0,0,7),\n", " (0,8,9,0,0,0,0,4,0),\n", " (0,0,0,0,0,0,2,0,8),\n", " (0,6,0,2,0,1,0,5,0),\n", " (1,0,2,0,0,0,0,0,0),\n", " (0,7,0,0,0,0,5,2,0),\n", " (9,0,0,0,6,5,0,0,0),\n", " (0,4,0,9,7,0,0,0,0))\n", "\n", "instance_c = [ If(instance[i][j] == 0,\n", " True,\n", " X[i][j] == instance[i][j])\n", " for i in range(9) for j in range(9) ] \n", " \n", "def n_solutions(n):\n", " s = Solver()\n", " s.add(sudoku_c + instance_c)\n", " i = 0\n", " while s.check() == sat and i < n:\n", " m = s.model()\n", " print([[ m.evaluate(X[i][j]) for j in range(9)] for i in range(9)])\n", " fml = And([X[i][j] == m.evaluate(X[i][j]) for i in range(9) for j in range(9)])\n", " s.add(Not(fml))\n", " i += 1\n", " \n", "n_solutions(10)" ] }, { "attachments": { "queens.png": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAANUAAADTCAYAAAAWGVaeAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAABxUSURBVHhe7Z17UFXX9ceZ2j/aIigCURR5iDwuikSDaEw1ER8kVVFjokIeRlHUGLWJjwBqh1hpABsLSSSSBAI0dChtpjOEmknaMglJUyRJ08fYv9rpHx3GmdZpZ0pHOm3h+7tr4yWE373uc/Zd9+bAXc7sYd+z11quc/b+7LXPPueuGwb5J1dArgDrFQj7nLVfNwFBKL89n49glGCcC/0fwTgX+j/kfMzGZ9D65yZMAhXDJBK0TmPw1QqYcj5mk74nQAlUDAPV7iDsKl2Jn5aush3hrADBIWP3fEzlOXy1YsPUP7t6AhUDTJ4O9XXxP61ej/33ZqJodQbeP5unIHp5fy4Wpqe4yzxVp2O/qcnHC3uWoPWJZbcEzcoA4pCxO5hM5Tl8tWLD1D+7egJVEKA6t3MRSvbuwfmaGjy2LlMBU5Kfiebm19DU1IgSN3B0rGxbNgo25CN3SRaaDo6A5q1YGUAcMnYHk6k8h69WbJj6Z1dPoAoAVJdKluKBVRloOTQScZ53R5/VK1fg/i0FOFqwSB175/TdmBUdqQrV6VjhapcC7fhTT+JbD2apY689vkzZanDb9HSulQHEIWN3MJnKc/hqxYapf3b1BCpmqH51bi3S583F5cuXkZoYB1r6UafkZ89CWFgYPqka+Uxl1oypqng+//zMPZgy5UvInncb+p5dp3TnJ8QpW+nJ8eitXKtkrQwgDhm7g8lUnsNXKzZM/bOrJ1AxQ0UwuFLiUVNTjYykOHWfRJ3ybNEiBdVb5Z9tTMyOnQ4qnk774OwaJXNu50iUIt30xFmorq6Ca94cfOS2LVCZbacH9ZGHbKmbd9L42dEDx4+eWoF7Fs5UgPz6ZqQiqCIjInDgvpH7pw/PrXFHtARVqE7HSrcuVDokS59Jlz7fvWAm3jh2lyz//JwA7UYcU3mJVH521FiwxnbCT058XQFxbPPC0UhVsmcXkuNvU8u6xoNLUfjAZlWoTrqu5DgUbt82ChXpkg2yNda2laUOh4zpoLKrx+GrFRt2/TKVF6gCBBVFmdgZ05GSOFs9i6Loc/LJIyjavhUvujcuTmzOxIXnnlOF6rTbt2H9aiVDsqRDumTDE+1ko8K/VYUpJHb1BKoAQUUdQfdUba+/jruyEkah6unpwbqcFGxakQaqU6H6xuWpakPCAxXpkC7ZGN+pVmZlDhm7g8lUnsNXKzZM/bOrJ1AFEKoNy9PQ29uLbZs3YEVGrAJmeHgYGSmJmDb1axgYGFBlxrRwzE+Kx9DQkJIhWdIhXbIhUPkXoXQP5+1Co5MXqAII1aH7XGhsbMS1a9fUvdHuR4vU9T59qkx9JqD6+/tVvaz0pGojGfpMOqRLNgQqgcrycxQd8VztVpYGHDLj/fXs+OUuzUFMTAyioqIQHR2NWHed/iYmJiApKXHkWGys++8MJUOySxZnIzw8HDUPZwtUTBMf13jS2ZFIxdRh3p6D/ODIchV1urq6cPXqVQwODmq/r3Pjxg0l29nZqXR/PGYrXTYq/ItYOhi42gWqAEJFnbR7nQv73FvpV65cQXt7Oy5evIiqqipVysvLVfF8pjaS6evrQ/HuR1C8fuSZliz//INJ7qkC8MVFjqWdFRveAPi0Zj0iw7+CvLw8lJWVKagaGhrQ1taGjo4OVahOx6iNZEh2+tSvgnQFKh6g5I0KZrCsAMEh42v5cGZbJo4cPoTu7m60tLSMRqvKykpQoUhVX1+v2kjm8BOP47Rbx5c9Dl+t2OBaDunsWPGFQ0bnB1e7LP8CvPyjaBMXHQGXy4Xi4mJUVFSgrq5ORabW1lZVqE7HqI1kcnJykDDzs3cCZfnHE624oNHZEagCDBW9HLu/eJd2g2K8QEpivM9vBXPM2lZs6AYPV7sVXzhkuPzV2RGoAgzV9lXpqK2txfXr1y2DRbJFhTvwzPaR9wYlUkmkkudUY0Cgd/jWLJmHrPnxSIiLQcLsmUhzf9/q9sxU5N6+QBWq07GE2bcpGZLNW5yM9ypWC1QBnvR0UcekXSLVF9Bp9EVFylXxrhsaKlQf++VFXUdyLIWs2ND5wdVuxRcOGS5/dXYEqi8AKl2n6No5BpgVGzo/uNqt+MIhw+Wvzo5AJVD5XK7rBg9XOwcwVmxw+auz4xUqnRJXu5ULwSHD5a/ODoevVmzo/OBqt+ILhwyXvzo7HL5asuHt6/Q657jaLTnIEEm4/NXZkfMx26XTXVeu9mD1j0Qqxrc3gtVpXINMZ8ep5+P0zL4ClUDl2NehJmpmX4FKoHIsVBM1s69AJVA5BqrJktlXoBKoHAHVZMrsK1AJVI6AajJl9hWoBCpHQEU7kZMls69AJVA5BioCazJk9hWoBCpHQTUZMvsKVAKVo6CiaDXRM/sKVAKV46Ca6Jl9BSqBynFQTfTMvgKVQOU4qCZ6Zl+BSqByHFQTPbOvQCVQOQ4q2qyYyJl9BSqBypFQTeTMvgKVQOVIqChaTdTMvgKVQOVIqCZyZl+BSqByJFQTObOvQCVQORKqiZzZV6ASqBwJ1UTO7CtQCVSOhMpbUpqJktlXoBKoJgxUuuxPuvZgZYcSqAQqgYoht+TngPWWTDNYROtmFq52OR9JckljgGs86exILnXGWUp3sbnaZZJw+CQhker/d9AfO6vwpzerLf/OlmeQc0GjsyNQCVSO/VWJ/338Kk7t24pDhffi7+++oPz82aUTyF7ocpcMVadjw5804s26o/iw+dQtQdPBwNUuUAlUjoWq+exeHCjZh++eP4+ndm1Ufpbt3YLm5mY0NTWhfN8Wdazu6UewtWAj7sy9A++9WurY8+GGjWsS0Nnh9tuXPZ0fXO0hdU/1dv0xFG9biw+ayhQYnbVHseaeVXjg/q2oPLJDHfvLW89hzqwYzJkZo+p07ODOfAXaiePH0HD6MXWsp7FU2XrnpeOjkHF1is7OpBuEjPe1t7o2uuvK1R4yUA188BIWpM/H5cuX4UpNBi39qAN2rM9FWFgY/tP3yigc8W6oqHg6qP/tC/jylClYtigNg70NSjdjfpKylZmWgn/98iUly9UpOjsClSz/HLFcIhgWuVJRU1ODrPR56j6JBmdbZYmC6s9dNaN+znX/oDUVz+D9x3svKpkW93LRc4+1IC0Z1dXVyMpIwb97Xxao/HjeN+kmiVDa/fvtD5/BplWLFSD//WgkUhFUkZEROLN/q/r8z/frkemOaFSoru6pTj6sdEiWPpMufd7otvX7jm/L8s8PoCgqC1SM61/dMoerfWyn/eGNSgXE+ScLR6E6ULIXqclz1bLu3VeexsM7H1SF6qS7yB2NHircMQoV6ZINsjXWNpe/OjuTbhAyjim5p/JzhtMNPk/72AtNUWZmbDTSUpLUsyiKPqUnjuGRoh3oqvsmLhwrwvcuXFCF6rTbV7AhX8mQLOmQLtnwRDuPfav++CsnUMk9lSPuqcYORLoPamt7HevuWjwKVU9PD7asXY6ijXeD6lSoXrhhpdqQ8ECldNy6ZGP84PYXFqv6ApVA5TiodnxjJXp7e7F92xbkr8hSwAwPD2NBRiqipkVgYGBAlZgZ05A+PxlDQ0NKhmRJh3TJhkCVz7LjOekmiVDaqPB03jOP34/GxkZcu3ZN3RsV796lLsOZ06fUZwKqv79f1cvL3A973f9Ihj6TDumSDYFKoPK2ugiZ51RjAWj7zn5ERkRgWe5SxMTEICoqCtHR0YiNjVV/kxITkZyU9LljJEOyOUsWIzw8HO1VBwQqpntiiVSMOzVW7yH8lRvfaX3fP6OiTldXF65evYrBwUHP5OLz740bN5RsZ2en0v1dx1mBSqDyuvwNyUhFkB1/bBMO7CvGlStX0N7ejosXL6KqqkqV8vJyVTyfqY1k+vr6UFK8Gyf3FHi9T/QXfqv6k25mZ5yoZUudaabTDUZvF3ro40ZERU5FXl4eysrKFFQNDQ3unb02dHR0qEJ1OkZtJEOyM6ZHgHS92dT5wdUuUMnun+N2/zyD8tKpR3H0yGF0d3ejpaVlNFpVVlaCCkWq+vp61UYyRw8fxqXyRx17PtywcU0COjvcfvuyp/ODqz1kl38UbebGxcLlcqG4uBgVFRWoq6tTkam1tVUVqtMxaiOZnJwcpCTECVTMKwyBinH9yzUz6Ox46zR6Ofag+/Uku/9Sb76JIcs/nu10efePESgalDoYuNq9AVDy4DrU1tbi+vXrlrki2YeKCtFUsUfuqRijlUQqRrC4oNHZ8dZp9A5fQd4y3JGVjuSE2e4yBy73d6PuyF6IO5cuUYXqdIzaSIZkN63Oxd+6nxeoBCrfqd1C8Y0KXzMjfVGRclX89Rd1qlB97JcXdTOqDm6udp0fXO1c/urscPmrs6Pzg6s9ZDcqdB1g0s7VKTo7Jr6Z6Oj84Go38c1Eh8tfnR2BapIsZ00GmU5HN3i42nV+cLVz+auzI2mf5d5A0j4zTqxqAvB2T6UjkaudawbS2eHyV2dH5wdXu84PrnYuf3V2uPzV2dH5wdYuUMnzFl+DkW2QaSKBDgaudrvnY5qpWCKVLP9CfvnHnalYoBKoQh4q7kzFApVAFXJQBTpTsUAlUIUUVMHIVCxQCVQhBVUwMhULVAJVSEGlXuQOcKZigUqgCjmoCKxAZioWqASqkIQqkJmKBSqBKiShomgVqEzFApVAFbJQBSpTsUAlUIUsVIHKVCxQCVQhC1WgMhULVAJVyEIVqEzFApVAFbJQ0WZFIDIVC1QCVUhDFYhMxQKVQBXSUFG04s5ULFAJVCENVSAyFQtUAlVIQxWITMUClUAV0lAFIlOxQCVQhTRUgchULFAJVCENlbdkMP5mKhaoBCqBSvL+2f8VPa4UVzo7dlNgmcrr/OBqN/XPrh6Xvzo7dv0ylpcfKLAPqa+LretUrnbjzrY5I3P5q7Mz6c5HoBKoZJLgS6hKE8iE+oGCrtKV+GnpKts/FjfpZkKbEcf0/HURhqvd1D+7elz+6uw4EqpPq9dj/72ZKFqdgffP5imIXt6fi4XpKe4yT9Xp2G9q8vHCniVofWLZLUGze/FN5XUXm6vd1D+7elz+6uzY9ctUXucHV7sjoTq3cxFK9u7B+ZoaPLYuUwFTkp+J5ubX0NTUiBI3cHSsbFs2CjbkI3dJFpoOjoDmrZh2gl09rk7R2bHrl6m8zg+udlP/7Opx+auz4wioLpUsxQOrMtByaCTiPO+OPqtXrsD9WwpwtGCROvbO6bsxKzpSFarTscLVLgXa8aeexLcezFLHXnt8mbLV4LbpOXm7F99UXnexudpN/bOrx+Wvzo5dv0zldX5wtX/hUP3q3Fqkz5uLy5cvIzUxDrT0o5PLz56FsLAwfFI18pnKrBlTVfF8/vmZezBlypeQPe829D27TunOT4hTttKT49FbuVbJmnaCXT2uTtHZseuXqbzOD652U//s6nH5q7PzhUNFMLhS4lFTU42MpDh1n0ROP1u0SEH1VvlnGxOzY6eDiuekPji7Rsmc2zkSpUg3PXEWqqur4Jo3Bx+5bQtU5rubusHD1W4XDlN5Ln91dr5wqMjBHz21AvcsnKkA+fXNSEVQRUZE4MB9I/dPH55b445oCapQnY6Vbl2odEiWPpMufb57wUy8cewuWf75uUuoGzxc7aaQ2NXj8ldnxxFQkZM/OfF1BcSxzQtHI1XJnl1Ijr9NLesaDy5F4QObVaE66biS41C4fdsoVKRLNsjW2BO3e/FN5XUXm6vd1D+7elz+6uzY9ctUXucHV7tjoKIoEztjOlISZ6tnURR9Tj55BEXbt+JF98bFic2ZuPDcc6pQnXb7NqxfrWRIlnRIl2x4op3nIpl2gl09rk7R2bHrl6m8zg+udlP/7Opx+auz4xioyFG6p2p7/XXclZUwClVPTw/W5aRg04o0UJ0K1TcuT1UbEh6oSId0ycb4k7Z78U3ldRebq93UP7t6XP7q7Nj1y1Re5wdXu6Og2rA8Db29vdi2eQNWZMQqYIaHh5GRkohpU7+GgYEBVWZMC8f8pHgMDQ0pGZIlHdIlGwKV+ebE2AHLNch0dkwhsaun84Or3VFQHbrPhcbGRly7dk3dG+1+tEj5d/pUmfpMQPX396t6WelJ1UYy9Jl0SJdsCFQClTfguKDR2XEUVJ4dv9ylOYiJiUFUVBSio6MR667T38TEBCQlJY4ci411/52hZEh2yeJshIeHo+bhbIHKz10/z4DUDR6udrsRx1Sey1+dHUdB9YMjy1XU6erqwtWrVzE4ODj6xq+vyo0bN5RsZ2en0v3xmK102ajwL2LpBg9XuykkdvW4/NXZcRRU5OzudS7sc2+lX7lyBe3t7bh48SKqqqpUKS8vV8XzmdpIpq+vD8W7H0Hx+pFnWrL88w8miVT+fRXEcVB9WrMekeFfQV5eHsrKyhRUDQ0NaGtrQ0dHhypUp2PURjIkO33qV0G6AhUPUASWbkbmarcbcUzlufzV2XEcVOTwmW2ZOHL4ELq7u9HS0jIarSorK0GFIlV9fb1qI5nDTzyO024dXydr2gl29XQXm6vdrl+m8lz+6uyY+mdXT+cHV7vjoKJoExcdAZfLheLiYlRUVKCurk5FptbWVlWoTseojWRycnKQMPOzdwJl+ccTrbgGmc6OXThM5XV+cLU7Dip6OXZ/8S7tBsV4gZTEeJ/fCjbtBLt6XJ2is2PXL1N5nR9c7ab+2dXj8ldnx3FQbV+VjtraWly/ft0yWCRbVLgDz2wfeW9QIpVEKnlONQYEeodvzZJ5yJofj4S4GCTMnok09/etbs9MRe7tC1ShOh1LmH2bkiHZvMXJeK9itUDF9IxKNirMdwAdF6m8RRr6oiLlqnjXDQ0Vqo/98qI2HDMOtFstOXR+cLXbXfaYynP5q7Nj6p9dPZ0fXO0TAip/T9buxTeV99dPq/qm/tnVs+qPv3J2/TKV99dPq/oCFWMUs3rR/ZUzHVR29fz106q+Xb9M5a3646+cV6j8NWpV3/Ti2NWz6o+/cnb9MpX310+r+qb+2dWz6o+/cnb9Mpa/SVXY2K02f523qm/stM3oYtUff+XkfMx2Hf297lb1g9U/EqlC4Fc/JLPvyE6eQGUzGsmuXL7K5SGZfX1vhQtUApXPmdXXckcy+9762ZJAJVBpoZLMvvYe0ApUAtUtoZLMvvaAknsqRqAm6+s2ktlXoJJ38hgnCs89lWT2tQeWLP8CMAitPs8wlQtWp431TzL7WgcrWP0jz6km+HMqyewrUEmOc4YIPD6SSmZfa2BJpGIYfJ6LaLqcs6sXrE4b75dk9hWo5Cdu/JwwxkMlmX0FKoGKGSrJ7CtQCVTMUElmX4FKoGKGipaDktlXD1aw7nllS32Cb6l77q8ks69AFdTvt9jdxTOVD9ZM6Ms/yewrb6kH7UtjppDY1fsioZLMvhKpJFL5cV/lDXbJ7CtQCVTMUElmX4FKoGKGSjL7ClQCFTNU3paEktn386AF655XttQnyZa63U0VK/LBGoRWfOGQCdb5CFQClfxYnh+rBq+gekumGSyiOWYfKzbkfCTJZVDTKghUZgPOG6hWAOeQkUnCrM84rr0VGyw/UPDHzir86c1q2w9zrTjIISOD0OGDkHv55cMex1iyYsMWVP/7+FWc2rcVhwrvxd/ffUFB9LNLJ5C90OUuGapOx4Y/acSbdUfxYfOpW4JmxUEOGYFKoHLs8q/57F4cKNmH754/j6d2bVTAlO3dgubmZjQ1NaF83xZ1rO7pR7C1YCPuzL0D771aqk0KyQHOrWwIVAKVY6B6u/4YiretxQdNZQqMztqjWHPPKjxw/1ZUHtmhjv3lrecwZ1YM5syMUXU6dnBnvgLtxPFjaDj9mDrW01iqbL3z0vFRyAINk8e+QCVQOQKqgQ9ewoL0+bh8+TJcqcmgpR85tmN9LsLCwvCfvldG4Yh3Q0XFM3j7376AL0+ZgmWL0jDY26B0M+YnKVuZaSn41y9fUrIClf4tAG/XSCYJh08Svnb/CIZFrlTU1NQgK32euk+izmyrLFFQ/bmrZhSiue4ftKbi6ex/vPeikmlxLxc991gL0pJRXV2NrIwU/Lv3ZYHKj+djAtUEhUpFkh8+g02rFitA/vvRSKQiqCIjI3Bm/1b1+Z/v1yPTHdGoUF3dU518WOmQLH0mXfq80W3r9x3fluWfH0BR5BKoJjBU1Hl/eKNSAXH+ycJRqA6U7EVq8ly1rHv3lafx8M4HVaE66SxyR6OHCneMQkW6ZINsjR0QsvyT5d+knCR0D38pysyMjUZaSpJ6FkXRp/TEMTxStANddd/EhWNF+N6FC6pQnXb7CjbkKxmSJR3SJRueaOcBS6ASqEISKgKA7oPa2l7HursWj0LV09ODLWuXo2jj3aA6FaoXblipNiQ8UCkdty7ZGL9sEagEqpCFasc3VqK3txfbt21B/oosBczw8DAWZKQialoEBgYGVImZMQ3p85MxNDSkZEiWdEiXbAhUZhCNn3zknmqC31NRBz7z+P1obGzEtWvX1L1R8e5datV45vQp9ZmA6u/vV/XyMvfDXvc/kqHPpEO6ZEOgEqhC4hGB7p5K7fh9Zz8iIyKwLHcpYmJiEBUVhejoaMTGxqq/SYmJSE5K+twxkiHZnCWLER4ejvaqAwKVn7t+8jDbLEIF/R7eClR93z+jok5XVxeuXr2KwcFBzzuDPv/euHFDyXZ2dird33WcFagEqtD48T8rUBHpxx/bhAP7inHlyhW0t7fj4sWLqKqqUqW8vFwVz2dqI5m+vj6UFO/GyT0FXp+tyEaF2XJQ7qnMIlbQxptVqIY+bkRU5FTk5eWhrKxMQdXQ0ODe2WtDR0eHKlSnY9RGMiQ7Y3oESNfbQAjaSU62rxbI+Rg9AA/aeLMKFUFx6dSjOHrkMLq7u9HS0jIarSorK0GFIlV9fb1qI5mjhw/jUvmj8pY607JP7qnMIpQj76nIKYo2c+Ni4XK5UFxcjIqKCtTV1anI1NraqgrV6Ri1kUxOTg5SEuIEKoHqli9PT7rlrNVIRS/HHnS/nmT3X+rNNzFk+Wd2/xQSW9CTbTlrFaqSB9ehtrYW169ft8wVyT5UVIimij1yT8UYrSbdzB6qUNE7fAV5y3BHVjqSE2a7yxy43N+NuiN7Ie5cukQVqtMxaiMZkt20Ohd/635eoBKoQicVmtVI5W12pC8qUq6Kv/6iThWqj/3yom5GDdpuzGSbCeV8Js/unw4Su+0Cldl9lt3rbCov/WPYP/5EKtPOCvoWp8zszp7ZJ1v/eINKZijDGWqyDQ45H6PJSHKpywZC6GwgBGmSEKgEKjaoJFPxzTc+ZPlnttQL5YeykqlY87qUQCVQ+bqH9rURJZmKBSpJ2mm4xPVAJZmKbb7IK5FKItWtIpVkKrYJFG2GCFQC1a2gkkzFApW8Y2i41LvVxotkKrYJlkQqiVRWNiokU7ENsAQqgcoKVJKpWKCSnO0My8DxW+qSqdgiWBKpJFJZiVQEmGQqFqjkJ3v8jFbjI5VkKhaoBCpmqCRTsUAlUDFDJZmKBSqBihkqyVQsUAlUAYBKMhVbAEt2/2T3z+run2fjQjIVy1vq8pa6YcTy9tUPyVQskUotASXnhlk09gaVZCoWqAQqwyjl67d4JVOxQCVQMUMlmYoFKoGKGSrJVCxQCVRBgEqSqo4DTbbUzW7iQzmbkr8QjdefbBtJXr9Ob/m3ckRQroBcAZ9XIEyujVwBuQK8V+D/AJyKkPfRDES2AAAAAElFTkSuQmCC" } }, "cell_type": "markdown", "metadata": {}, "source": [ "## Eight queens\n", "\n", "\n", "The eight queens puzzle is the problem of placing eight chess queens on an 8x8 chessboard so that no two queens attack each other. Thus, a solution requires that no two queens share the same row, column, or diagonal.\n", "\n", "![queens.png](attachment:queens.png)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# We know each queen must be in a different row.\n", "# So, we represent each queen by a single integer: the column position\n", "Queens = [ Int('Q_%i' % (i + 1)) for i in range(8) ]\n", "\n", "# Each queen is in a column {1, ... 8 }\n", "val_c = [ And(1 <= Queens[i], Queens[i] <= 8) for i in range(8) ]\n", "\n", "# At most one queen per column\n", "col_c = [ Distinct(Queens) ]\n", "\n", "# Diagonal constraint\n", "diag_c = [ If(i == j,\n", " True,\n", " And(Queens[i] - Queens[j] != i - j, Queens[i] - Queens[j] != j - i))\n", " for i in range(8) for j in range(i) ]\n", "\n", "solve(val_c + col_c + diag_c)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Application: Install Problem\n", "\n", "The install problem consists of determining whether a new set of packages can be installed in a system. This application is based on the article OPIUM: Optimal Package Install/Uninstall Manager. Many packages depend on other packages to provide some functionality. Each distribution contains a meta-data file that explicates the requirements of each package of the distribution. The meta-data contains details like the name, version, etc. More importantly, it contains depends and conflicts clauses that stipulate which other packages should be on the system. The depends clauses stipulate which other packages must be present. The conflicts clauses stipulate which other packages must not be present.\n", "\n", "The install problem can be easily solved using Z3. The idea is to define a Boolean variable for each package. This variable is true if the package must be in the system. If package a depends on packages b, c and z, we write:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "a, b, c, d, e, f, g, z = Bools('a b c d e f g z')\n", "\n", "#DependsOn(a, [b, c, z])\n", "#DependsOn is a simple Python function that creates Z3 constraints that capture the depends clause semantics.\n", "\n", "def DependsOn(pack, deps):\n", " return And([ Implies(pack, dep) for dep in deps ])\n", "\n", "#Thus, DependsOn(a, [b, c, z]) generates the constraint\n", "# And(Implies(a, b), Implies(a, c), Implies(a, z))\n", "\n", "print(DependsOn(a, [b, c, z]))\n", "\n", "#That is, if users install package a, they must also install packages b, c and z.\n", "\n", "#If package d conflicts with package e, we write Conflict(d, e). Conflict is also a simple Python function.\n", "\n", "def Conflict(p1, p2):\n", " return Or(Not(p1), Not(p2))\n", "\n", "# Conflict(d, e) generates the constraint Or(Not(d), Not(e)). \n", "# With these two functions, we can easily encode the example \n", "# in the Opium article (Section 2) in Z3Py as:\n", "\n", "def DependsOn(pack, deps):\n", " return And([ Implies(pack, dep) for dep in deps ])\n", "\n", "def Conflict(p1, p2):\n", " return Or(Not(p1), Not(p2))\n", "\n", "\n", "solve(DependsOn(a, [b, c, z]),\n", " DependsOn(b, [d]),\n", " DependsOn(c, [Or(d, e), Or(f, g)]),\n", " Conflict(d, e),\n", " a, z)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def DependsOn(pack, deps):\n", " return And([ Implies(pack, dep) for dep in deps ])\n", "\n", "def Conflict(p1, p2):\n", " return Or(Not(p1), Not(p2))\n", "\n", "a, b, c, d, e, f, g, z = Bools('a b c d e f g z')\n", "\n", "solve(DependsOn(a, [b, c, z]),\n", " DependsOn(b, [d]),\n", " DependsOn(c, [Or(d, e), Or(f, g)]),\n", " Conflict(d, e),\n", " a, z)\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true }, "outputs": [], "source": [ "def install_check(*problem):\n", " s = Solver()\n", " s.add(*problem)\n", " if s.check() == sat:\n", " m = s.model()\n", " r = []\n", " for x in m:\n", " if is_true(m[x]):\n", " # x is a Z3 declaration\n", " # x() returns the Z3 expression\n", " # x.name() returns a string\n", " r.append(x())\n", " print(r)\n", " else:\n", " print(\"invalid installation profile\")\n", "\n", "print(\"Check 1\")\n", "install_check(DependsOn(a, [b, c, z]),\n", " DependsOn(b, [d]),\n", " DependsOn(c, [Or(d, e), Or(f, g)]),\n", " Conflict(d, e),\n", " Conflict(d, g),\n", " a, z)\n", "\n", "print(\"Check 2\")\n", "install_check(DependsOn(a, [b, c, z]),\n", " #DependsOn(b, d),\n", " DependsOn(b, [d]),\n", " DependsOn(c, [Or(d, e), Or(f, g)]),\n", " Conflict(d, e),\n", " Conflict(d, g),\n", " a, z, g)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Special thanks to Peter Gragert for porting the original web-based guide to the jupyter notebook format." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.1" } }, "nbformat": 4, "nbformat_minor": 2 } z3-z3-4.8.7/examples/python/tutorial/jupyter/strategies.ipynb000066400000000000000000000304451356505360400243560ustar00rootroot00000000000000{ "cells": [ { "metadata": { "collapsed": true }, "cell_type": "markdown", "source": "# Strategies\n\nHigh-performance solvers, such as Z3, contain many tightly integrated, handcrafted heuristic combinations of algorithmic proof methods. While these heuristic combinations tend to be highly tuned for known classes of problems, they may easily perform very badly on new classes of problems. This issue is becoming increasingly pressing as solvers begin to gain the attention of practitioners in diverse areas of science and engineering. In many cases, changes to the solver heuristics can make a tremendous difference.\n\nMore information on Z3 is available from https://github.com/z3prover/z3.git\n\n## Introduction\nZ3 implements a methodology for orchestrating reasoning engines where \"big\" symbolic reasoning steps are represented as functions known as tactics, and tactics are composed using combinators known as tacticals. Tactics process sets of formulas called Goals.\n\nWhen a tactic is applied to some goal G, four different outcomes are possible. The tactic succeeds in showing G to be satisfiable (i.e., feasible); succeeds in showing G to be unsatisfiable (i.e., infeasible); produces a sequence of subgoals; or fails. When reducing a goal G to a sequence of subgoals G1, ..., Gn, we face the problem of model conversion. A model converter construct a model for G using a model for some subgoal Gi.\n\nIn the following example, we create a goal g consisting of three formulas, and a tactic t composed of two built-in tactics: simplify and solve-eqs. The tactic simplify apply transformations equivalent to the ones found in the command simplify. The tactic solver-eqs eliminate variables using Gaussian elimination. Actually, solve-eqs is not restricted only to linear arithmetic. It can also eliminate arbitrary variables. Then, combinator Then applies simplify to the input goal and solve-eqs to each subgoal produced by simplify. In this example, only one subgoal is produced." }, { "metadata": { "trusted": true }, "cell_type": "code", "source": "!pip install \"z3-solver\"\n\nfrom z3 import *\n\nx, y = Reals('x y')\ng = Goal()\ng.add(x > 0, y > 0, x == y + 2)\nprint(g)\n\nt1 = Tactic('simplify')\nt2 = Tactic('solve-eqs')\nt = Then(t1, t2)\nprint(t(g))", "execution_count": null, "outputs": [] }, { "metadata": {}, "cell_type": "markdown", "source": "In the example above, variable x is eliminated, and is not present the resultant goal.\n\nIn Z3, we say a clause is any constraint of the form Or(f_1, ..., f_n). The tactic split-clause will select a clause Or(f_1, ..., f_n) in the input goal, and split it n subgoals. One for each subformula f_i." }, { "metadata": { "trusted": true }, "cell_type": "code", "source": "x, y = Reals('x y')\ng = Goal()\ng.add(Or(x < 0, x > 0), x == y + 1, y < 0)\n\nt = Tactic('split-clause')\nr = t(g)\nfor g in r: \n print(g)", "execution_count": null, "outputs": [] }, { "metadata": {}, "cell_type": "markdown", "source": "Tactics\nZ3 comes equipped with many built-in tactics. The command describe_tactics() provides a short description of all built-in tactics.\n\n describe_tactics()\nZ3Py comes equipped with the following tactic combinators (aka tacticals):\n\n* Then(t, s) applies t to the input goal and s to every subgoal produced by t.\n* OrElse(t, s) first applies t to the given goal, if it fails then returns the result of s applied to the given goal.\n* Repeat(t) Keep applying the given tactic until no subgoal is modified by it.\n* Repeat(t, n) Keep applying the given tactic until no subgoal is modified by it, or the number of iterations is greater than n.\n* TryFor(t, ms) Apply tactic t to the input goal, if it does not return in ms milliseconds, it fails.\n* With(t, params) Apply the given tactic using the given parameters.\n\nThe following example demonstrate how to use these combinators." }, { "metadata": { "trusted": true }, "cell_type": "code", "source": "x, y, z = Reals('x y z')\ng = Goal()\ng.add(Or(x == 0, x == 1), \n Or(y == 0, y == 1), \n Or(z == 0, z == 1),\n x + y + z > 2)\n\n# Split all clauses\"\nsplit_all = Repeat(OrElse(Tactic('split-clause'),\n Tactic('skip')))\nprint(split_all(g))\n\nsplit_at_most_2 = Repeat(OrElse(Tactic('split-clause'),\n Tactic('skip')),\n 1)\nprint(split_at_most_2(g))\n\n# Split all clauses and solve equations\nsplit_solve = Then(Repeat(OrElse(Tactic('split-clause'),\n Tactic('skip'))),\n Tactic('solve-eqs'))\n\nprint(split_solve(g))", "execution_count": null, "outputs": [] }, { "metadata": {}, "cell_type": "markdown", "source": "In the tactic split_solver, the tactic solve-eqs discharges all but one goal. Note that, this tactic generates one goal: the empty goal which is trivially satisfiable (i.e., feasible)\n\nThe list of subgoals can be easily traversed using the Python for statement." }, { "metadata": { "trusted": true }, "cell_type": "code", "source": "x, y, z = Reals('x y z')\ng = Goal()\ng.add(Or(x == 0, x == 1), \n Or(y == 0, y == 1), \n Or(z == 0, z == 1),\n x + y + z > 2)\n\n# Split all clauses\"\nsplit_all = Repeat(OrElse(Tactic('split-clause'),\n Tactic('skip')))\nfor s in split_all(g):\n print(s)", "execution_count": null, "outputs": [] }, { "metadata": {}, "cell_type": "markdown", "source": "A tactic can be converted into a solver object using the method solver(). If the tactic produces the empty goal, then the associated solver returns sat. If the tactic produces a single goal containing False, then the solver returns unsat. Otherwise, it returns unknown." }, { "metadata": { "trusted": true }, "cell_type": "code", "source": "bv_solver = Then('simplify', \n 'solve-eqs', \n 'bit-blast', \n 'sat').solver()\n\nx, y = BitVecs('x y', 16)\nsolve_using(bv_solver, x | y == 13, x > y)", "execution_count": null, "outputs": [] }, { "metadata": {}, "cell_type": "markdown", "source": "In the example above, the tactic bv_solver implements a basic bit-vector solver using equation solving, bit-blasting, and a propositional SAT solver. Note that, the command Tactic is suppressed. All Z3Py combinators automatically invoke Tactic command if the argument is a string. Finally, the command solve_using is a variant of the solve command where the first argument specifies the solver to be used.\n\nIn the following example, we use the solver API directly instead of the command solve_using. We use the combinator With to configure our little solver. We also include the tactic aig which tries to compress Boolean formulas using And-Inverted Graphs." }, { "metadata": { "trusted": true }, "cell_type": "code", "source": "bv_solver = Then(With('simplify', mul2concat=True),\n 'solve-eqs', \n 'bit-blast', \n 'aig',\n 'sat').solver()\nx, y = BitVecs('x y', 16)\nbv_solver.add(x*32 + y == 13, x & y < 10, y > -100)\nprint(bv_solver.check())\nm = bv_solver.model()\nprint(m)\nprint(x*32 + y, \"==\", m.evaluate(x*32 + y))\nprint(x & y, \"==\", m.evaluate(x & y))", "execution_count": null, "outputs": [] }, { "metadata": {}, "cell_type": "markdown", "source": "The tactic smt wraps the main solver in Z3 as a tactic." }, { "metadata": { "trusted": true }, "cell_type": "code", "source": "x, y = Ints('x y')\ns = Tactic('smt').solver()\ns.add(x > y + 1)\nprint(s.check())\nprint(s.model())", "execution_count": null, "outputs": [] }, { "metadata": {}, "cell_type": "markdown", "source": "Now, we show how to implement a solver for integer arithmetic using SAT. The solver is complete only for problems where every variable has a lower and upper bound." }, { "metadata": { "trusted": true }, "cell_type": "code", "source": "s = Then(With('simplify', arith_lhs=True, som=True),\n 'normalize-bounds', 'lia2pb', 'pb2bv', \n 'bit-blast', 'sat').solver()\nx, y, z = Ints('x y z')\nsolve_using(s, \n x > 0, x < 10, \n y > 0, y < 10, \n z > 0, z < 10,\n 3*y + 2*x == z)\n# It fails on the next example (it is unbounded)\ns.reset()\nsolve_using(s, 3*y + 2*x == z)", "execution_count": null, "outputs": [] }, { "metadata": {}, "cell_type": "markdown", "source": "Tactics can be combined with solvers. For example, we can apply a tactic to a goal, produced a set of subgoals, then select one of the subgoals and solve it using a solver. The next example demonstrates how to do that, and how to use model converters to convert a model for a subgoal into a model for the original goal." }, { "metadata": { "trusted": true }, "cell_type": "code", "source": "t = Then('simplify', \n 'normalize-bounds', \n 'solve-eqs')\n\nx, y, z = Ints('x y z')\ng = Goal()\ng.add(x > 10, y == x + 3, z > y)\n\nr = t(g)\n# r contains only one subgoal\nprint(r)\n\ns = Solver()\ns.add(r[0])\nprint(s.check())\n# Model for the subgoal\nprint(s.model())\n# Model for the original goal\nprint(r[0].convert_model(s.model()))", "execution_count": null, "outputs": [] }, { "metadata": {}, "cell_type": "markdown", "source": "## Probes\n\nProbes (aka formula measures) are evaluated over goals. Boolean expressions over them can be built using relational operators and Boolean connectives. The tactic FailIf(cond) fails if the given goal does not satisfy the condition cond. Many numeric and Boolean measures are available in Z3Py. The command describe_probes() provides the list of all built-in probes." }, { "metadata": { "trusted": true }, "cell_type": "code", "source": "describe_probes()", "execution_count": null, "outputs": [] }, { "metadata": {}, "cell_type": "markdown", "source": "In the following example, we build a simple tactic using FailIf. It also shows that a probe can be applied directly to a goal." }, { "metadata": { "trusted": true }, "cell_type": "code", "source": "x, y, z = Reals('x y z')\ng = Goal()\ng.add(x + y + z > 0)\n\np = Probe('num-consts')\nprint(\"num-consts:\", p(g))\n\nt = FailIf(p > 2)\ntry:\n t(g)\nexcept Z3Exception:\n print(\"tactic failed\")\n\nprint(\"trying again...\")\ng = Goal()\ng.add(x + y > 0)\nprint(t(g))", "execution_count": null, "outputs": [] }, { "metadata": {}, "cell_type": "markdown", "source": "Z3Py also provides the combinator (tactical) If(p, t1, t2) which is a shorthand for:\n\nOrElse(Then(FailIf(Not(p)), t1), t2)\n\nThe combinator When(p, t) is a shorthand for:\n\nIf(p, t, 'skip')\nThe tactic skip just returns the input goal. The following example demonstrates how to use the If combinator." }, { "metadata": { "trusted": true }, "cell_type": "code", "source": "x, y, z = Reals('x y z')\ng = Goal()\ng.add(x**2 - y**2 >= 0)\n\np = Probe('num-consts')\nt = If(p > 2, 'simplify', 'factor')\n\nprint(t(g))\n\ng = Goal()\ng.add(x + x + y + z >= 0, x**2 - y**2 >= 0)\n\nprint(t(g))", "execution_count": null, "outputs": [] }, { "metadata": { "trusted": true }, "cell_type": "code", "source": "", "execution_count": null, "outputs": [] } ], "metadata": { "kernelspec": { "name": "python3", "display_name": "Python 3", "language": "python" }, "language_info": { "mimetype": "text/x-python", "nbconvert_exporter": "python", "name": "python", "pygments_lexer": "ipython3", "version": "3.6.6", "file_extension": ".py", "codemirror_mode": { "version": 3, "name": "ipython" } } }, "nbformat": 4, "nbformat_minor": 2 }z3-z3-4.8.7/examples/python/union_sort.py000066400000000000000000000010371356505360400203400ustar00rootroot00000000000000# Copyright Microsoft Corporation 2019 # This example illustrates the use of union types # from the Python API. It is given as an example # as response to #2215. from z3 import * u = Datatype('IntOrString') u.declare('IntV', ('int', IntSort())) u.declare('StringV', ('string', StringSort())) IntOrString = u.create() StringV = IntOrString.StringV IntV = IntOrString.IntV print(IntV(1)) print(StringV(StringVal("abc"))) print(IntV(1).sort()) print(IntV(1) == StringV(StringVal("abc"))) s = String('s') print(simplify(IntV(1) == StringV(s))) z3-z3-4.8.7/examples/python/visitor.py000066400000000000000000000011551356505360400176410ustar00rootroot00000000000000# Copyright (c) Microsoft Corporation 2015 from __future__ import print_function from z3 import * def visitor(e, seen): if e in seen: return seen[e] = True yield e if is_app(e): for ch in e.children(): for e in visitor(ch, seen): yield e return if is_quantifier(e): for e in visitor(e.body(), seen): yield e return x, y = Ints('x y') fml = x + x + y > 2 seen = {} for e in visitor(fml, seen): if is_const(e) and e.decl().kind() == Z3_OP_UNINTERPRETED: print("Variable", e) else: print(e) z3-z3-4.8.7/examples/tptp/000077500000000000000000000000001356505360400152345ustar00rootroot00000000000000z3-z3-4.8.7/examples/tptp/CMakeLists.txt000066400000000000000000000025361356505360400200020ustar00rootroot00000000000000################################################################################ # TPTP example ################################################################################ project(Z3_TPTP5 CXX) cmake_minimum_required(VERSION 3.4) find_package(Z3 REQUIRED CONFIG # `NO_DEFAULT_PATH` is set so that -DZ3_DIR has to be passed to find Z3. # This should prevent us from accidentally picking up an installed # copy of Z3. This is here to benefit Z3's build system when building # this project. When making your own project you probably shouldn't # use this option. NO_DEFAULT_PATH ) message(STATUS "Z3_FOUND: ${Z3_FOUND}") message(STATUS "Found Z3 ${Z3_VERSION_STRING}") message(STATUS "Z3_DIR: ${Z3_DIR}") add_executable(z3_tptp5 tptp5.cpp tptp5.lex.cpp) target_include_directories(z3_tptp5 PRIVATE ${Z3_CXX_INCLUDE_DIRS}) target_link_libraries(z3_tptp5 PRIVATE ${Z3_LIBRARIES}) if ("${CMAKE_SYSTEM_NAME}" MATCHES "[Ww]indows") # On Windows we need to copy the Z3 libraries # into the same directory as the executable # so that they can be found. foreach (z3_lib ${Z3_LIBRARIES}) message(STATUS "Adding copy rule for ${z3_lib}") add_custom_command(TARGET z3_tptp5 POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different $ $ ) endforeach() endif() z3-z3-4.8.7/examples/tptp/README000066400000000000000000000011641356505360400161160ustar00rootroot00000000000000TPTP front-end and utilities as a sample using the C++ bindings. To build the example execute make examples in the build directory. This command will create the executable tptp. On Windows, you can just execute it. On macOS and Linux, you must install z3 first using sudo make install OR update LD_LIBRARY_PATH (Linux) or DYLD_LIBRARY_PATH (macOS) with the build directory. You need that to be able to find the Z3 shared library. The sample illustrates using Z3 from the TPTP language. The TPTP language is documented on http://tptp.org It also exposes utilities for converting between SMT-LIB and TPTP format. z3-z3-4.8.7/examples/tptp/tptp5.cpp000066400000000000000000002375541356505360400170340ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "z3++.h" struct alloc_region { std::list m_alloc; void * allocate(size_t s) { char * res = new char[s]; m_alloc.push_back(res); return res; } ~alloc_region() { std::list::iterator it = m_alloc.begin(), end = m_alloc.end(); for (; it != end; ++it) { delete *it; } } }; template class flet { T & m_ref; T m_old; public: flet(T& x, T const& y): m_ref(x), m_old(x) { x = y; } ~flet() { m_ref = m_old; } }; struct symbol_compare { bool operator()(z3::symbol const& s1, z3::symbol const& s2) const { return s1 < s2; }; }; template struct symbol_table { typedef std::map map; map m_map; void insert(z3::symbol s, T val) { m_map.insert(std::pair(s, val)); } bool find(z3::symbol const& s, T& val) { typename map::iterator it = m_map.find(s); if (it == m_map.end()) { return false; } else { val = it->second; return true; } } }; typedef std::set symbol_set; struct named_formulas { std::vector m_formulas; std::vector m_names; std::vector m_files; bool m_has_conjecture; named_formulas(): m_has_conjecture(false) {} void push_back(z3::expr fml, char const * name, char const* file) { m_formulas.push_back(fml); m_names.push_back(name); m_files.push_back(file); } void set_has_conjecture() { m_has_conjecture = true; } bool has_conjecture() const { return m_has_conjecture; } }; inline void * operator new(size_t s, alloc_region & r) { return r.allocate(s); } inline void * operator new[](size_t s, alloc_region & r) { return r.allocate(s); } inline void operator delete(void *, alloc_region & ) { /* do nothing */ } inline void operator delete[](void *, alloc_region & ) { /* do nothing */ } struct failure_ex { std::string msg; failure_ex(char const* m):msg(m) {} }; extern char* tptp_lval[]; extern int yylex(); static char* strdup(alloc_region& r, char const* s) { size_t l = strlen(s) + 1; char* result = new (r) char[l]; memcpy(result, s, l); return result; } class TreeNode { char const* m_symbol; int m_symbol_index; TreeNode** m_children; public: TreeNode(alloc_region& r, char const* sym, TreeNode* A, TreeNode* B, TreeNode* C, TreeNode* D, TreeNode* E, TreeNode* F, TreeNode* G, TreeNode* H, TreeNode* I, TreeNode* J): m_symbol(strdup(r, sym)), m_symbol_index(-1) { m_children = new (r) TreeNode*[10]; m_children[0] = A; m_children[1] = B; m_children[2] = C; m_children[3] = D; m_children[4] = E; m_children[5] = F; m_children[6] = G; m_children[7] = H; m_children[8] = I; m_children[9] = J; } char const* symbol() const { return m_symbol; } TreeNode *const* children() const { return m_children; } TreeNode* child(unsigned i) const { return m_children[i]; } int index() const { return m_symbol_index; } void set_index(int idx) { m_symbol_index = idx; } }; TreeNode* MkToken(alloc_region& r, char const* token, int symbolIndex) { TreeNode* ss; char* symbol = tptp_lval[symbolIndex]; ss = new (r) TreeNode(r, symbol, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL); ss->set_index(symbolIndex); return ss; } // ------------------------------------------------------ // Build Z3 formulas. class env { z3::context& m_context; z3::expr_vector m_bound; // vector of bound constants. z3::sort m_univ; symbol_table m_decls; symbol_table m_defined_sorts; static std::vector* m_nodes; static alloc_region* m_region; char const* m_filename; enum binary_connective { IFF, IMPLIES, IMPLIED, LESS_TILDE_GREATER, TILDE_VLINE }; void mk_error(TreeNode* f, char const* msg) { std::ostringstream strm; strm << "expected: " << msg << "\n"; strm << "got: " << f->symbol(); throw failure_ex(strm.str().c_str()); } void mk_not_handled(TreeNode* f, char const* msg) { std::ostringstream strm; strm << "Construct " << f->symbol() << " not handled: " << msg; throw failure_ex(strm.str().c_str()); } void mk_input(TreeNode* f, named_formulas& fmls) { if (!strcmp(f->symbol(),"annotated_formula")) { mk_annotated_formula(f->child(0), fmls); } else if (!strcmp(f->symbol(),"include")) { mk_include(f->child(2), f->child(3), fmls); } else { mk_error(f, "annotated formula or include"); } } void mk_annotated_formula(TreeNode* f, named_formulas& fmls) { if (!strcmp(f->symbol(),"fof_annotated")) { fof_annotated(f->child(2), f->child(4), f->child(6), f->child(7), fmls); } else if (!strcmp(f->symbol(),"tff_annotated")) { fof_annotated(f->child(2), f->child(4), f->child(6), f->child(7), fmls); } else if (!strcmp(f->symbol(),"cnf_annotated")) { cnf_annotated(f->child(2), f->child(4), f->child(6), f->child(7), fmls); } else if (!strcmp(f->symbol(),"thf_annotated")) { mk_error(f, "annotated formula (not thf)"); } else { mk_error(f, "annotated formula"); } } void check_arity(unsigned num_args, unsigned arity) { if (num_args != arity) { throw failure_ex("arity mismatch"); } } void mk_include(TreeNode* file_name, TreeNode* formula_selection, named_formulas& fmls) { char const* fn = file_name->child(0)->symbol(); TreeNode* name_list = formula_selection->child(2); if (name_list && !strcmp("null",name_list->symbol())) { name_list = 0; } std::string inc_name; bool f_exists = false; for (unsigned i = 1; !f_exists && i <= 3; ++i) { inc_name.clear(); f_exists = mk_filename(fn, i, inc_name); } if (!f_exists) { inc_name.clear(); f_exists = mk_env_filename(fn, inc_name); } if (!f_exists) { inc_name = fn; } parse(inc_name.c_str(), fmls); while (name_list) { return mk_error(name_list, "name list (not handled)"); //char const* name = name_list->child(0)->symbol(); name_list = name_list->child(2); } } #define CHECK(_node_) if (0 != strcmp(_node_->symbol(),#_node_)) return mk_error(_node_,#_node_); const char* get_name(TreeNode* name) { if (!name->child(0)) { mk_error(name, "node with a child"); } if (!name->child(0)->child(0)) { return name->child(0)->symbol(); } return name->child(0)->child(0)->symbol(); } z3::expr mk_forall(z3::expr_vector& bound, z3::expr body) { return mk_quantifier(true, bound, body); } z3::expr mk_quantifier(bool is_forall, z3::expr_vector& bound, z3::expr body) { Z3_app* vars = new Z3_app[bound.size()]; for (unsigned i = 0; i < bound.size(); ++i) { vars[i] = (Z3_app) bound[i]; } Z3_ast r = Z3_mk_quantifier_const(m_context, is_forall, 1, bound.size(), vars, 0, 0, body); delete[] vars; return z3::expr(m_context, r); } void cnf_annotated(TreeNode* name, TreeNode* formula_role, TreeNode* formula, TreeNode* annotations, named_formulas& fmls) { symbol_set st; get_cnf_variables(formula, st); symbol_set::iterator it = st.begin(), end = st.end(); std::vector names; m_bound.resize(0); for(; it != end; ++it) { names.push_back(*it); m_bound.push_back(m_context.constant(names.back(), m_univ)); } z3::expr r(m_context); cnf_formula(formula, r); if (!m_bound.empty()) { r = mk_forall(m_bound, r); } char const* role = formula_role->child(0)->symbol(); if (!strcmp(role,"conjecture")) { fmls.set_has_conjecture(); r = !r; } fmls.push_back(r, get_name(name), m_filename); m_bound.resize(0); } void cnf_formula(TreeNode* formula, z3::expr& r) { std::vector disj; if (formula->child(1)) { disjunction(formula->child(1), disj); } else { disjunction(formula->child(0), disj); } if (disj.size() > 0) { r = disj[0]; } else { r = m_context.bool_val(false); } for (unsigned i = 1; i < disj.size(); ++i) { r = r || disj[i]; } } void disjunction(TreeNode* d, std::vector& r) { z3::expr lit(m_context); if (d->child(2)) { disjunction(d->child(0), r); literal(d->child(2), lit); r.push_back(lit); } else { literal(d->child(0), lit); r.push_back(lit); } } void literal(TreeNode* l, z3::expr& lit) { if (!strcmp(l->child(0)->symbol(),"~")) { fof_formula(l->child(1), lit); lit = !lit; } else { fof_formula(l->child(0), lit); } } void fof_annotated(TreeNode* name, TreeNode* formula_role, TreeNode* formula, TreeNode* annotations, named_formulas& fmls) { z3::expr fml(m_context); //CHECK(fof_formula); CHECK(formula_role); fof_formula(formula->child(0), fml); char const* role = formula_role->child(0)->symbol(); if (!strcmp(role,"conjecture")) { fmls.set_has_conjecture(); fmls.push_back(!fml, get_name(name), m_filename); } else if (!strcmp(role,"type")) { } else { fmls.push_back(fml, get_name(name), m_filename); } } void fof_formula(TreeNode* f, z3::expr& fml) { z3::expr f1(m_context); char const* name = f->symbol(); if (!strcmp(name,"fof_logic_formula") || !strcmp(name,"fof_binary_assoc") || !strcmp(name,"fof_binary_formula") || !strcmp(name,"tff_logic_formula") || !strcmp(name,"tff_binary_assoc") || !strcmp(name,"tff_binary_formula") || !strcmp(name,"atomic_formula") || !strcmp(name,"defined_atomic_formula")) { fof_formula(f->child(0), fml); } else if (!strcmp(name, "fof_sequent") || !strcmp(name, "tff_sequent")) { fof_formula(f->child(0), f1); fof_formula(f->child(2), fml); fml = implies(f1, fml); } else if (!strcmp(name, "fof_binary_nonassoc") || !strcmp(name, "tff_binary_nonassoc")) { fof_formula(f->child(0), f1); fof_formula(f->child(2), fml); //SASSERT(!strcmp("binary_connective",f->child(1)->symbol())); char const* conn = f->child(1)->child(0)->symbol(); if (!strcmp(conn, "<=>")) { fml = (f1 == fml); } else if (!strcmp(conn, "=>")) { fml = implies(f1, fml); } else if (!strcmp(conn, "<=")) { fml = implies(fml, f1); } else if (!strcmp(conn, "<~>")) { fml = ! (f1 == fml); } else if (!strcmp(conn, "~|")) { fml = !(f1 || fml); } else if (!strcmp(conn, "~&")) { fml = ! (f1 && fml); } else { mk_error(f->child(1)->child(0), "connective"); } } else if (!strcmp(name,"fof_or_formula") || !strcmp(name,"tff_or_formula")) { fof_formula(f->child(0), f1); fof_formula(f->child(2), fml); fml = f1 || fml; } else if (!strcmp(name,"fof_and_formula") || !strcmp(name,"tff_and_formula")) { fof_formula(f->child(0), f1); fof_formula(f->child(2), fml); fml = f1 && fml; } else if (!strcmp(name,"fof_unitary_formula") || !strcmp(name,"tff_unitary_formula")) { if (f->child(1)) { // parenthesis fof_formula(f->child(1), fml); } else { fof_formula(f->child(0), fml); } } else if (!strcmp(name,"fof_quantified_formula") || !strcmp(name,"tff_quantified_formula")) { fof_quantified_formula(f->child(0), f->child(2), f->child(5), fml); } else if (!strcmp(name,"fof_unary_formula") || !strcmp(name,"tff_unary_formula")) { if (!f->child(1)) { fof_formula(f->child(0), fml); } else { fof_formula(f->child(1), fml); char const* conn = f->child(0)->child(0)->symbol(); if (!strcmp(conn,"~")) { fml = !fml; } else { mk_error(f->child(0)->child(0), "fof_unary_formula"); } } } else if (!strcmp(name,"fof_let")) { mk_let(f->child(2), f->child(5), fml); } else if (!strcmp(name,"variable")) { char const* v = f->child(0)->symbol(); if (!find_bound(v, fml)) { mk_error(f->child(0), "variable"); } } else if (!strcmp(name,"fof_conditional")) { z3::expr f2(m_context); fof_formula(f->child(2), f1); fof_formula(f->child(4), f2); fof_formula(f->child(6), fml); fml = ite(f1, f2, fml); } else if (!strcmp(name,"plain_atomic_formula") || !strcmp(name,"defined_plain_formula") || !strcmp(name,"system_atomic_formula")) { z3::sort srt(m_context.bool_sort()); term(f->child(0), srt, fml); } else if (!strcmp(name,"defined_infix_formula") || !strcmp(name,"fol_infix_unary")) { z3::expr t1(m_context), t2(m_context); term(f->child(0), m_univ, t1); term(f->child(2), m_univ, t2); TreeNode* inf = f->child(1); while (inf && strcmp(inf->symbol(),"=") && strcmp(inf->symbol(),"!=")) { inf = inf->child(0); } if (!inf) { mk_error(f->child(1), "defined_infix_formula"); } char const* conn = inf->symbol(); if (!strcmp(conn,"=")) { fml = t1 == t2; } else if (!strcmp(conn,"!=")) { fml = ! (t1 == t2); } else { mk_error(inf, "defined_infix_formula"); } } else if (!strcmp(name, "tff_typed_atom")) { while (!strcmp(f->child(0)->symbol(),"(")) { f = f->child(1); } char const* id = 0; z3::sort s(m_context); z3::sort_vector sorts(m_context); mk_id(f->child(0), id); if (is_ttype(f->child(2))) { s = mk_sort(id); m_defined_sorts.insert(symbol(id), s); } else { mk_mapping_sort(f->child(2), sorts, s); z3::func_decl fd(m_context.function(id, sorts, s)); m_decls.insert(symbol(id), fd); } } else { mk_error(f, "fof_formula"); } } bool is_ttype(TreeNode* t) { char const* name = t->symbol(); if (!strcmp(name,"atomic_defined_word")) { return !strcmp("$tType", t->child(0)->symbol()); } return false; } void fof_quantified_formula(TreeNode* fol_quantifier, TreeNode* vl, TreeNode* formula, z3::expr& fml) { unsigned l = m_bound.size(); mk_variable_list(vl); fof_formula(formula, fml); bool is_forall = !strcmp(fol_quantifier->child(0)->symbol(),"!"); z3::expr_vector bound(m_context); for (unsigned i = l; i < m_bound.size(); ++i) { bound.push_back(m_bound[i]); } fml = mk_quantifier(is_forall, bound, fml); m_bound.resize(l); } void mk_variable_list(TreeNode* variable_list) { while (variable_list) { TreeNode* var = variable_list->child(0); if (!strcmp(var->symbol(),"tff_variable")) { var = var->child(0); } if (!strcmp(var->symbol(),"variable")) { char const* name = var->child(0)->symbol(); m_bound.push_back(m_context.constant(name, m_univ)); } else if (!strcmp(var->symbol(),"tff_typed_variable")) { z3::sort s(m_context); char const* name = var->child(0)->child(0)->symbol(); mk_sort(var->child(2), s); m_bound.push_back(m_context.constant(name, s)); } else { mk_error(var, "variable_list"); } variable_list = variable_list->child(2); } } void mk_sort(TreeNode* t, z3::sort& s) { char const* name = t->symbol(); if (!strcmp(name, "tff_atomic_type") || !strcmp(name, "defined_type")) { mk_sort(t->child(0), s); } else if (!strcmp(name, "atomic_defined_word")) { z3::symbol sname = symbol(t->child(0)->symbol()); z3::sort srt(m_context); if (!strcmp("$tType", t->child(0)->symbol())) { char const* id = 0; s = mk_sort(id); m_defined_sorts.insert(symbol(id), s); } else if (m_defined_sorts.find(sname, srt)) { s = srt; } else { s = mk_sort(sname); if (sname == symbol("$rat")) { throw failure_ex("rational sorts are not handled\n"); } mk_error(t, sname.str().c_str()); } } else if (!strcmp(name,"atomic_word")) { name = t->child(0)->symbol(); z3::symbol symname = symbol(name); s = mk_sort(symname); } else { mk_error(t, "sort"); } } void mk_mapping_sort(TreeNode* t, z3::sort_vector& domain, z3::sort& s) { char const* name = t->symbol(); //char const* id = 0; if (!strcmp(name,"tff_top_level_type")) { mk_mapping_sort(t->child(0), domain, s); } else if (!strcmp(name,"tff_atomic_type")) { mk_sort(t->child(0), s); } else if (!strcmp(name,"tff_mapping_type")) { TreeNode* t1 = t->child(0); if (t1->child(1)) { mk_xprod_sort(t1->child(1), domain); } else { mk_sort(t1->child(0), s); domain.push_back(s); } mk_sort(t->child(2), s); } else { mk_error(t, "mapping sort"); } } void mk_xprod_sort(TreeNode* t, z3::sort_vector& sorts) { char const* name = t->symbol(); z3::sort s1(m_context), s2(m_context); if (!strcmp(name, "tff_atomic_type")) { mk_sort(t->child(0), s1); sorts.push_back(s1); } else if (!strcmp(name, "tff_xprod_type")) { name = t->child(0)->symbol(); if (!strcmp(name, "tff_atomic_type") || !strcmp(name, "tff_xprod_type")) { mk_xprod_sort(t->child(0), sorts); mk_xprod_sort(t->child(2), sorts); } else if (t->child(1)) { mk_xprod_sort(t->child(1), sorts); } else { mk_error(t, "xprod sort"); } } else { mk_error(t, "xprod sort"); } } void term(TreeNode* t, z3::sort const& s, z3::expr& r) { char const* name = t->symbol(); if (!strcmp(name, "defined_plain_term") || !strcmp(name, "system_term") || !strcmp(name, "plain_term")) { if (!t->child(1)) { term(t->child(0), s, r); } else { apply_term(t->child(0), t->child(2), s, r); } } else if (!strcmp(name, "constant") || !strcmp(name, "functor") || !strcmp(name, "defined_plain_formula") || !strcmp(name, "defined_functor") || !strcmp(name, "defined_constant") || !strcmp(name, "system_constant") || !strcmp(name, "defined_atomic_term") || !strcmp(name, "system_functor") || !strcmp(name, "function_term") || !strcmp(name, "term") || !strcmp(name, "defined_term")) { term(t->child(0), s, r); } else if (!strcmp(name, "defined_atom")) { char const* name0 = t->child(0)->symbol(); if (!strcmp(name0,"number")) { name0 = t->child(0)->child(0)->symbol(); char const* per = strchr(name0, '.'); bool is_real = 0 != per; bool is_rat = 0 != strchr(name0, '/'); bool is_int = !is_real && !is_rat; if (is_int) { r = m_context.int_val(name0); } else { r = m_context.real_val(name0); } } else if (!strcmp(name0, "distinct_object")) { throw failure_ex("distinct object not handled"); } else { mk_error(t->child(0), "number or distinct object"); } } else if (!strcmp(name, "atomic_defined_word")) { char const* ch = t->child(0)->symbol(); z3::symbol s = symbol(ch); z3::func_decl fd(m_context); if (!strcmp(ch, "$true")) { r = m_context.bool_val(true); } else if (!strcmp(ch, "$false")) { r = m_context.bool_val(false); } else if (m_decls.find(s, fd)) { r = fd(0,0); } else { mk_error(t->child(0), "atomic_defined_word"); } } else if (!strcmp(name, "atomic_word")) { z3::func_decl f(m_context); z3::symbol sym = symbol(t->child(0)->symbol()); if (m_decls.find(sym, f)) { r = f(0,0); } else { r = m_context.constant(sym, s); } } else if (!strcmp(name, "variable")) { char const* v = t->child(0)->symbol(); if (!find_bound(v, r)) { mk_error(t->child(0), "variable not bound"); } } else { mk_error(t, "term not recognized"); } } void apply_term(TreeNode* f, TreeNode* args, z3::sort const& s, z3::expr& r) { z3::expr_vector terms(m_context); z3::sort_vector sorts(m_context); mk_args(args, terms); for (unsigned i = 0; i < terms.size(); ++i) { sorts.push_back(terms[i].get_sort()); } if (!strcmp(f->symbol(),"functor") || !strcmp(f->symbol(),"system_functor") || !strcmp(f->symbol(),"defined_functor")) { f = f->child(0); } bool atomic_word = !strcmp(f->symbol(),"atomic_word"); if (atomic_word || !strcmp(f->symbol(),"atomic_defined_word") || !strcmp(f->symbol(),"atomic_system_word")) { char const* ch = f->child(0)->symbol(); z3::symbol fn = symbol(ch); z3::func_decl fun(m_context); z3::context& ctx = r.ctx(); if (!strcmp(ch,"$less")) { check_arity(terms.size(), 2); r = terms[0] < terms[1]; } else if (!strcmp(ch,"$lesseq")) { check_arity(terms.size(), 2); r = terms[0] <= terms[1]; } else if (!strcmp(ch,"$greater")) { check_arity(terms.size(), 2); r = terms[0] > terms[1]; } else if (!strcmp(ch,"$greatereq")) { check_arity(terms.size(), 2); r = terms[0] >= terms[1]; } else if (!strcmp(ch,"$uminus")) { check_arity(terms.size(), 1); r = -terms[0]; } else if (!strcmp(ch,"$sum")) { check_arity(terms.size(), 2); r = terms[0] + terms[1]; } else if (!strcmp(ch,"$plus")) { check_arity(terms.size(), 2); r = terms[0] + terms[1]; } else if (!strcmp(ch,"$difference")) { check_arity(terms.size(), 2); r = terms[0] - terms[1]; } else if (!strcmp(ch,"$product")) { check_arity(terms.size(), 2); r = terms[0] * terms[1]; } else if (!strcmp(ch,"$quotient")) { check_arity(terms.size(), 2); r = terms[0] / terms[1]; } else if (!strcmp(ch,"$quotient_e")) { check_arity(terms.size(), 2); r = terms[0] / terms[1]; } else if (!strcmp(ch,"$distinct")) { if (terms.size() == 2) { r = terms[0] != terms[1]; } else { r = distinct(terms); } } else if (!strcmp(ch,"$floor") || !strcmp(ch,"$to_int")) { check_arity(terms.size(), 1); r = to_real(to_int(terms[0])); } else if (!strcmp(ch,"$to_real")) { check_arity(terms.size(), 1); r = terms[0]; if (r.get_sort().is_int()) { r = to_real(terms[0]); } } else if (!strcmp(ch,"$is_int")) { check_arity(terms.size(), 1); r = z3::expr(ctx, Z3_mk_is_int(ctx, terms[0])); } else if (!strcmp(ch,"$true")) { r = ctx.bool_val(true); } else if (!strcmp(ch,"$false")) { r = ctx.bool_val(false); } // ceiling(x) = -floor(-x) else if (!strcmp(ch,"$ceiling")) { check_arity(terms.size(), 1); r = ceiling(terms[0]); } // truncate - The nearest integral value with magnitude not greater than the absolute value of the argument. // if x >= 0 floor(x) else ceiling(x) else if (!strcmp(ch,"$truncate")) { check_arity(terms.size(), 1); r = truncate(terms[0]); } // The nearest integral number to the argument. When the argument // is halfway between two integral numbers, the nearest even integral number to the argument. else if (!strcmp(ch,"$round")) { check_arity(terms.size(), 1); z3::expr t = terms[0]; z3::expr i = to_int(t); z3::expr i2 = i + ctx.real_val(1,2); r = ite(t > i2, i + 1, ite(t == i2, ite(is_even(i), i, i+1), i)); } // $quotient_e(N,D) - the Euclidean quotient, which has a non-negative remainder. // If D is positive then $quotient_e(N,D) is the floor (in the type of N and D) of // the real division N/D, and if D is negative then $quotient_e(N,D) is the ceiling of N/D. // $quotient_t(N,D) - the truncation of the real division N/D. else if (!strcmp(ch,"$quotient_t")) { check_arity(terms.size(), 2); r = truncate(terms[0] / terms[1]); } // $quotient_f(N,D) - the floor of the real division N/D. else if (!strcmp(ch,"$quotient_f")) { check_arity(terms.size(), 2); r = to_real(to_int(terms[0] / terms[1])); } // For t in {$int,$rat, $real}, x in {e, t,f}, $quotient_x and $remainder_x are related by // ! [N:t,D:t] : $sum($product($quotient_x(N,D),D),$remainder_x(N,D)) = N // For zero divisors the result is not specified. else if (!strcmp(ch,"$remainder_t")) { mk_not_handled(f, ch); } else if (!strcmp(ch,"$remainder_e")) { check_arity(terms.size(), 2); r = z3::expr(ctx, Z3_mk_mod(ctx, terms[0], terms[1])); } else if (!strcmp(ch,"$remainder_r")) { mk_not_handled(f, ch); } else if (!strcmp(ch,"$to_rat") || !strcmp(ch,"$is_rat")) { mk_not_handled(f, ch); } else if (m_decls.find(fn, fun)) { r = fun(terms); } else if (true) { z3::func_decl func(m_context); func = m_context.function(fn, sorts, s); r = func(terms); } else { mk_error(f->child(0), "atomic, defined or system word"); } return; } mk_error(f, "function"); } z3::expr to_int(z3::expr e) { return z3::expr(e.ctx(), Z3_mk_real2int(e.ctx(), e)); } z3::expr to_real(z3::expr e) { return z3::expr(e.ctx(), Z3_mk_int2real(e.ctx(), e)); } z3::expr ceiling(z3::expr e) { return -to_real(to_int(-e)); } z3::expr is_even(z3::expr e) { z3::context& ctx = e.ctx(); z3::expr two = ctx.int_val(2); z3::expr m = z3::expr(ctx, Z3_mk_mod(ctx, e, two)); return m == 0; } z3::expr truncate(z3::expr e) { return ite(e >= 0, to_int(e), ceiling(e)); } bool check_app(z3::func_decl& f, unsigned num, z3::expr const* args) { if (f.arity() == num) { for (unsigned i = 0; i < num; ++i) { if (!eq(args[i].get_sort(), f.domain(i))) { return false; } } return true; } else { return true; } } void mk_args(TreeNode* args, z3::expr_vector& result) { z3::expr t(m_context); while (args) { term(args->child(0), m_univ, t); result.push_back(t); args = args->child(2); } } bool find_bound(char const* v, z3::expr& b) { for (unsigned l = m_bound.size(); l > 0; ) { --l; if (v == m_bound[l].decl().name().str()) { b = m_bound[l]; return true; } } return false; } void mk_id(TreeNode* f, char const*& sym) { char const* name = f->symbol(); if (!strcmp(name, "tff_untyped_atom") || !strcmp(name, "functor") || !strcmp(name, "system_functor")) { mk_id(f->child(0), sym); } else if (!strcmp(name, "atomic_word") || !strcmp(name, "atomic_system_word")) { sym = f->child(0)->symbol(); } else { mk_error(f, "atom"); } } void mk_let(TreeNode* let_vars, TreeNode* f, z3::expr& fml) { mk_error(f, "let construct is not handled"); } FILE* open_file(char const* filename) { FILE* fp = 0; #ifdef _WINDOWS if (0 > fopen_s(&fp, filename, "r") || fp == 0) { fp = 0; } #else fp = fopen(filename, "r"); #endif return fp; } bool is_sep(char s) { return s == '/' || s == '\\'; } void add_separator(const char* rel_name, std::string& inc_name) { size_t sz = inc_name.size(); if (sz == 0) return; if (sz > 0 && is_sep(inc_name[sz-1])) return; if (is_sep(rel_name[0])) return; inc_name += "/"; } void append_rel_name(const char * rel_name, std::string& inc_name) { if (rel_name[0] == '\'') { add_separator(rel_name+1, inc_name); inc_name.append(rel_name+1); inc_name.resize(inc_name.size()-1); } else { add_separator(rel_name, inc_name); inc_name.append(rel_name); } } bool mk_filename(const char *rel_name, unsigned num_sep, std::string& inc_name) { unsigned sep1 = 0, sep2 = 0, sep3 = 0; size_t len = strlen(m_filename); for (unsigned i = 0; i < len; ++i) { if (is_sep(m_filename[i])) { sep3 = sep2; sep2 = sep1; sep1 = i; } } if ((num_sep == 3) && sep3 > 0) { inc_name.append(m_filename,sep3+1); } if ((num_sep == 2) && sep2 > 0) { inc_name.append(m_filename,sep2+1); } if ((num_sep == 1) && sep1 > 0) { inc_name.append(m_filename,sep1+1); } append_rel_name(rel_name, inc_name); return file_exists(inc_name.c_str()); } bool file_exists(char const* filename) { FILE* fp = open_file(filename); if (!fp) { return false; } fclose(fp); return true; } bool mk_env_filename(const char* rel_name, std::string& inc_name) { #ifdef _WINDOWS char buffer[1024]; size_t sz; errno_t err = getenv_s( &sz, buffer, "$TPTP"); if (err != 0) { return false; } #else char const* buffer = getenv("$TPTP"); if (!buffer) { return false; } #endif inc_name = buffer; append_rel_name(rel_name, inc_name); return file_exists(inc_name.c_str()); } void get_cnf_variables(TreeNode* t, symbol_set& symbols) { std::vector todo; todo.push_back(t); while (!todo.empty()) { t = todo.back(); todo.pop_back(); if (!t) continue; if (!strcmp(t->symbol(),"variable")) { z3::symbol sym = symbol(t->child(0)->symbol()); symbols.insert(sym); } else { for (unsigned i = 0; i < 10; ++i) { todo.push_back(t->child(i)); } } } } z3::symbol symbol(char const* s) { return m_context.str_symbol(s); } z3::sort mk_sort(char const* s) { return m_context.uninterpreted_sort(s); } z3::sort mk_sort(z3::symbol& s) { return m_context.uninterpreted_sort(s); } public: env(z3::context& ctx): m_context(ctx), m_bound(ctx), m_univ(mk_sort("$i")), m_filename(0) { m_nodes = 0; m_region = new alloc_region(); m_defined_sorts.insert(symbol("$i"), m_univ); m_defined_sorts.insert(symbol("$o"), m_context.bool_sort()); m_defined_sorts.insert(symbol("$real"), m_context.real_sort()); m_defined_sorts.insert(symbol("$int"), m_context.int_sort()); } ~env() { delete m_region; m_region = 0; } void parse(const char* filename, named_formulas& fmls); static void register_node(TreeNode* t) { m_nodes->push_back(t); } static alloc_region& r() { return *m_region; } }; std::vector* env::m_nodes = 0; alloc_region* env::m_region = 0; # define P_USERPROC # define P_ACT(ss) if(verbose)printf("%7d %s\n",yylineno,ss); # define P_BUILD(sym,A,B,C,D,E,F,G,H,I,J) new (env::r()) TreeNode(env::r(), sym,A,B,C,D,E,F,G,H,I,J) # define P_TOKEN(tok,symbolIndex) MkToken(env::r(), tok,symbolIndex) # define P_PRINT(ss) env::register_node(ss) // ------------------------------------------------------ // created by YACC. #include "tptp5.tab.c" extern FILE* yyin; void env::parse(const char* filename, named_formulas& fmls) { std::vector nodes; flet fn(m_filename, filename); flet*> fnds(m_nodes, &nodes); FILE* fp = open_file(filename); if (!fp) { std::stringstream strm; strm << "Could not open file " << filename << "\n"; throw failure_ex(strm.str().c_str()); } yyin = fp; int result = yyparse(); fclose(fp); if (result != 0) { throw failure_ex("could not parse input"); } for (unsigned i = 0; i < nodes.size(); ++i) { TreeNode* cl = nodes[i]; if (cl) { mk_input(cl, fmls); } } } class pp_tptp { z3::context& ctx; std::vector names; std::vector sorts; std::vector funs; std::vector todo; std::set seen_ids; unsigned m_formula_id; unsigned m_node_number; std::map m_proof_ids; std::map > m_proof_hypotheses; std::map m_axiom_ids; named_formulas* m_named_formulas; public: pp_tptp(z3::context& ctx): ctx(ctx), m_formula_id(0) {} void display_func_decl(std::ostream& out, z3::func_decl& f) { std::string name = lower_case_fun(f.name()); out << "tff(" << name << "_type, type, (\n " << name << ": "; unsigned na = f.arity(); switch(na) { case 0: break; case 1: { z3::sort s(f.domain(0)); display_sort(out, s); out << " > "; break; } default: out << "( "; for (unsigned j = 0; j < na; ++j) { z3::sort s(f.domain(j)); display_sort(out, s); if (j + 1 < na) { out << " * "; } } out << " ) > "; } z3::sort srt(f.range()); display_sort(out, srt); out << ")).\n"; } void display_axiom(std::ostream& out, z3::expr e) { out << "tff(formula" << (++m_formula_id) << ", axiom,\n "; display(out, e, true); out << ").\n"; } void display(std::ostream& out, z3::expr e, bool in_paren) { std::string s; if (e.is_numeral(s)) { out << s; } else if (e.is_var()) { unsigned idx = Z3_get_index_value(ctx, e); out << names[names.size()-1-idx]; } else if (e.is_app()) { switch(e.decl().decl_kind()) { case Z3_OP_TRUE: out << "$true"; break; case Z3_OP_FALSE: out << "$false"; break; case Z3_OP_AND: display_infix(out, "&", e, in_paren); break; case Z3_OP_OR: display_infix(out, "|", e, in_paren); break; case Z3_OP_IMPLIES: display_infix(out, "=>", e, in_paren); break; case Z3_OP_NOT: if (!in_paren) out << "("; out << "~"; display(out, e.arg(0), false); if (!in_paren) out << ")"; break; case Z3_OP_EQ: if (e.arg(0).is_bool()) { display_infix(out, "<=>", e, in_paren); } else { display_infix(out, "=", e, in_paren); } break; case Z3_OP_IFF: display_infix(out, "<=>", e, in_paren); break; case Z3_OP_XOR: display_infix(out, "<~>", e, in_paren); break; case Z3_OP_MUL: display_binary(out, "$product", e); break; case Z3_OP_ADD: display_binary(out, "$sum", e); break; case Z3_OP_SUB: display_prefix(out, "$difference", e); break; case Z3_OP_LE: display_prefix(out, "$lesseq", e); break; case Z3_OP_GE: display_prefix(out, "$greatereq", e); break; case Z3_OP_LT: display_prefix(out, "$less", e); break; case Z3_OP_GT: display_prefix(out, "$greater", e); break; case Z3_OP_UMINUS: display_prefix(out, "$uminus", e); break; case Z3_OP_DIV: display_prefix(out, "$quotient", e); break; case Z3_OP_IS_INT: display_prefix(out, "$is_int", e); break; case Z3_OP_TO_REAL: display_prefix(out, "$to_real", e); break; case Z3_OP_TO_INT: display_prefix(out, "$to_int", e); break; case Z3_OP_IDIV: display_prefix(out, "$quotient_e", e); break; case Z3_OP_MOD: display_prefix(out, "$remainder_e", e); break; case Z3_OP_ITE: display_prefix(out, e.is_bool()?"ite_f":"ite_t", e); break; case Z3_OP_DISTINCT: display_prefix(out, "$distinct", e); break; case Z3_OP_REM: throw failure_ex("rem is not handled"); break; case Z3_OP_OEQ: display_prefix(out, "$oeq", e); break; default: display_app(out, e); break; } } else if (e.is_quantifier()) { bool is_forall = Z3_is_quantifier_forall(ctx, e); unsigned nb = Z3_get_quantifier_num_bound(ctx, e); out << (is_forall?"!":"?") << "["; for (unsigned i = 0; i < nb; ++i) { Z3_symbol n = Z3_get_quantifier_bound_name(ctx, e, i); names.push_back(upper_case_var(z3::symbol(ctx, n))); z3::sort srt(ctx, Z3_get_quantifier_bound_sort(ctx, e, i)); out << names.back() << ": "; display_sort(out, srt); if (i + 1 < nb) { out << ", "; } } out << "] : "; display(out, e.body(), false); for (unsigned i = 0; i < nb; ++i) { names.pop_back(); } } } void display_app(std::ostream& out, z3::expr e) { if (e.is_const()) { out << e; return; } out << lower_case_fun(e.decl().name()) << "("; unsigned n = e.num_args(); for(unsigned i = 0; i < n; ++i) { display(out, e.arg(i), n == 1); if (i + 1 < n) { out << ", "; } } out << ")"; } void display_sort(std::ostream& out, z3::sort const& s) { if (s.is_int()) { out << "$int"; } else if (s.is_real()) { out << "$real"; } else if (s.is_bool()) { out << "$o"; } else { out << s; } } void display_infix(std::ostream& out, char const* conn, z3::expr& e, bool in_paren) { if (!in_paren) out << "("; unsigned sz = e.num_args(); for (unsigned i = 0; i < sz; ++i) { display(out, e.arg(i), false); if (i + 1 < sz) { out << " " << conn << " "; } } if (!in_paren) out << ")"; } void display_prefix(std::ostream& out, char const* conn, z3::expr& e) { out << conn << "("; unsigned sz = e.num_args(); for (unsigned i = 0; i < sz; ++i) { display(out, e.arg(i), sz == 1); if (i + 1 < sz) { out << ", "; } } out << ")"; } void display_binary(std::ostream& out, char const* conn, z3::expr& e) { out << conn << "("; unsigned sz = e.num_args(); unsigned np = 1; for (unsigned i = 0; i < sz; ++i) { display(out, e.arg(i), false); if (i + 1 < sz) { out << ", "; } if (i + 2 < sz) { out << conn << "("; ++np; } } for (unsigned i = 0; i < np; ++i) { out << ")"; } } void collect_axiom_ids(named_formulas& axioms) { m_named_formulas = &axioms; m_axiom_ids.clear(); for (unsigned i = 0; i < axioms.m_formulas.size(); ++i) { z3::expr& e = axioms.m_formulas[i]; unsigned id = Z3_get_ast_id(ctx, e); m_axiom_ids.insert(std::make_pair(id, i)); } } void display_proof(std::ostream& out, named_formulas& fmls, z3::solver& solver) { m_node_number = 0; m_proof_ids.clear(); m_proof_hypotheses.clear(); z3::expr proof = solver.proof(); collect_axiom_ids(fmls); collect_decls(proof); collect_hypotheses(proof); display_sort_decls(out); display_func_decls(out); display_proof_rec(out, proof); } /** \brief collect hypotheses for each proof node. */ void collect_hypotheses(z3::expr& proof) { Z3_sort proof_sort = proof.get_sort(); size_t todo_size = todo.size(); todo.push_back(proof); while (todo_size != todo.size()) { z3::expr p = todo.back(); unsigned id = Z3_get_ast_id(ctx, p); if (m_proof_hypotheses.find(id) != m_proof_hypotheses.end()) { todo.pop_back(); continue; } bool all_visited = true; for (unsigned i = 0; i < p.num_args(); ++i) { z3::expr arg = p.arg(i); if (arg.get_sort() == proof_sort) { if (m_proof_hypotheses.find(Z3_get_ast_id(ctx,arg)) == m_proof_hypotheses.end()) { all_visited = false; todo.push_back(arg); } } } if (!all_visited) { continue; } todo.pop_back(); std::set hyps; if (p.decl().decl_kind() == Z3_OP_PR_LEMMA) { // we assume here that all hypotheses get consumed in lemmas. } else { for (unsigned i = 0; i < p.num_args(); ++i) { z3::expr arg = p.arg(i); if (arg.get_sort() == proof_sort) { unsigned arg_id = Z3_get_ast_id(ctx,arg); std::set const& arg_hyps = m_proof_hypotheses.find(arg_id)->second; std::set::iterator it = arg_hyps.begin(), end = arg_hyps.end(); for (; it != end; ++it) { hyps.insert(*it); } } } } m_proof_hypotheses.insert(std::make_pair(id, hyps)); } } unsigned display_proof_rec(std::ostream& out, z3::expr proof) { Z3_sort proof_sort = proof.get_sort(); size_t todo_size = todo.size(); todo.push_back(proof); while (todo_size != todo.size()) { z3::expr p = todo.back(); unsigned id = Z3_get_ast_id(ctx, p); if (m_proof_ids.find(id) != m_proof_ids.end()) { todo.pop_back(); continue; } switch (p.decl().decl_kind()) { case Z3_OP_PR_MODUS_PONENS_OEQ: { unsigned hyp = display_proof_rec(out, p.arg(0)); unsigned num = display_proof_hyp(out, hyp, p.arg(1)); m_proof_ids.insert(std::make_pair(id, num)); todo.pop_back(); continue; } default: break; } bool all_visited = true; for (unsigned i = 0; i < p.num_args(); ++i) { z3::expr arg = p.arg(i); if (arg.get_sort() == proof_sort) { if (m_proof_ids.find(Z3_get_ast_id(ctx,arg)) == m_proof_ids.end()) { all_visited = false; todo.push_back(arg); } } } if (!all_visited) { continue; } todo.pop_back(); unsigned num = ++m_node_number; m_proof_ids.insert(std::make_pair(id, num)); switch (p.decl().decl_kind()) { case Z3_OP_PR_ASSERTED: { std::string formula_name; std::string formula_file; unsigned id = Z3_get_ast_id(ctx, p.arg(0)); std::map::iterator it = m_axiom_ids.find(id); if (it != m_axiom_ids.end()) { formula_name = m_named_formulas->m_names[it->second]; formula_file = m_named_formulas->m_files[it->second]; } else { std::ostringstream str; str << "axiom_" << id; formula_name = str.str(); formula_file = "unknown"; } out << "tff(" << m_node_number << ",axiom,("; display(out, get_proof_formula(p), true); out << "), file('" << formula_file << "','"; out << formula_name << "')).\n"; break; } case Z3_OP_PR_UNDEF: throw failure_ex("undef rule not handled"); case Z3_OP_PR_TRUE: display_inference(out, "true", "thm", p); break; case Z3_OP_PR_GOAL: display_inference(out, "goal", "thm", p); break; case Z3_OP_PR_MODUS_PONENS: display_inference(out, "modus_ponens", "thm", p); break; case Z3_OP_PR_REFLEXIVITY: display_inference(out, "reflexivity", "thm", p); break; case Z3_OP_PR_SYMMETRY: display_inference(out, "symmetry", "thm", p); break; case Z3_OP_PR_TRANSITIVITY: case Z3_OP_PR_TRANSITIVITY_STAR: display_inference(out, "transitivity", "thm", p); break; case Z3_OP_PR_MONOTONICITY: display_inference(out, "monotonicity", "thm", p); break; case Z3_OP_PR_QUANT_INTRO: display_inference(out, "quant_intro", "thm", p); break; case Z3_OP_PR_DISTRIBUTIVITY: display_inference(out, "distributivity", "thm", p); break; case Z3_OP_PR_AND_ELIM: display_inference(out, "and_elim", "thm", p); break; case Z3_OP_PR_NOT_OR_ELIM: display_inference(out, "or_elim", "thm", p); break; case Z3_OP_PR_REWRITE: case Z3_OP_PR_REWRITE_STAR: display_inference(out, "rewrite", "thm", p); break; case Z3_OP_PR_PULL_QUANT: display_inference(out, "pull_quant", "thm", p); break; case Z3_OP_PR_PUSH_QUANT: display_inference(out, "push_quant", "thm", p); break; case Z3_OP_PR_ELIM_UNUSED_VARS: display_inference(out, "elim_unused_vars", "thm", p); break; case Z3_OP_PR_DER: display_inference(out, "destructive_equality_resolution", "thm", p); break; case Z3_OP_PR_QUANT_INST: display_inference(out, "quant_inst", "thm", p); break; case Z3_OP_PR_HYPOTHESIS: out << "tff(" << m_node_number << ",assumption,("; display(out, get_proof_formula(p), true); out << "), introduced(assumption)).\n"; break; case Z3_OP_PR_LEMMA: { out << "tff(" << m_node_number << ",plain,("; display(out, get_proof_formula(p), true); out << "), inference(lemma,lemma(discharge,"; unsigned parent_id = Z3_get_ast_id(ctx, p.arg(0)); std::set const& hyps = m_proof_hypotheses.find(parent_id)->second; print_hypotheses(out, hyps); out << "))).\n"; break; } case Z3_OP_PR_UNIT_RESOLUTION: display_inference(out, "unit_resolution", "thm", p); break; case Z3_OP_PR_IFF_TRUE: display_inference(out, "iff_true", "thm", p); break; case Z3_OP_PR_IFF_FALSE: display_inference(out, "iff_false", "thm", p); break; case Z3_OP_PR_COMMUTATIVITY: display_inference(out, "commutativity", "thm", p); break; case Z3_OP_PR_DEF_AXIOM: display_inference(out, "tautology", "thm", p); break; case Z3_OP_PR_DEF_INTRO: display_inference(out, "def_intro", "sab", p); break; case Z3_OP_PR_APPLY_DEF: display_inference(out, "apply_def", "sab", p); break; case Z3_OP_PR_IFF_OEQ: display_inference(out, "iff_oeq", "sab", p); break; case Z3_OP_PR_NNF_POS: display_inference(out, "nnf_pos", "sab", p); break; case Z3_OP_PR_NNF_NEG: display_inference(out, "nnf_neg", "sab", p); break; case Z3_OP_PR_SKOLEMIZE: display_inference(out, "skolemize", "sab", p); break; case Z3_OP_PR_MODUS_PONENS_OEQ: display_inference(out, "modus_ponens_sab", "sab", p); break; case Z3_OP_PR_TH_LEMMA: display_inference(out, "theory_lemma", "thm", p); break; case Z3_OP_PR_HYPER_RESOLVE: display_inference(out, "hyper_resolve", "thm", p); break; default: out << "TBD: " << m_node_number << "\n" << p << "\n"; throw failure_ex("rule not handled"); } } return m_proof_ids.find(Z3_get_ast_id(ctx, proof))->second; } unsigned display_proof_hyp(std::ostream& out, unsigned hyp, z3::expr p) { z3::expr fml = p.arg(p.num_args()-1); z3::expr conclusion = fml.arg(1); switch (p.decl().decl_kind()) { case Z3_OP_PR_REFLEXIVITY: return display_hyp_inference(out, "reflexivity", "sab", conclusion, hyp); case Z3_OP_PR_IFF_OEQ: { unsigned hyp2 = display_proof_rec(out, p.arg(0)); return display_hyp_inference(out, "modus_ponens", "thm", conclusion, hyp, hyp2); } case Z3_OP_PR_NNF_POS: case Z3_OP_PR_SKOLEMIZE: return display_hyp_inference(out, "skolemize", "sab", conclusion, hyp); case Z3_OP_PR_TRANSITIVITY: case Z3_OP_PR_TRANSITIVITY_STAR: { unsigned na = p.num_args(); for (unsigned i = 0; i + 1 < na; ++i) { if (p.arg(i).num_args() != 2) { // cop-out: Z3 produces transitivity proofs that are not a chain of equivalences/equi-sats. // the generated proof is (most likely) not going to be checkable. continue; } z3::expr conclusion = p.arg(i).arg(1); hyp = display_hyp_inference(out, "transitivity", "sab", conclusion, hyp); } return hyp; } case Z3_OP_PR_MONOTONICITY: throw failure_ex("monotonicity rule is not handled"); default: unsigned hyp2 = 0; if (p.num_args() == 2) { hyp2 = display_proof_rec(out, p.arg(0)); } if (p.num_args() > 2) { std::cout << "unexpected number of arguments: " << p << "\n"; throw failure_ex("unexpected number of arguments"); } return display_hyp_inference(out, p.decl().name().str().c_str(), "sab", conclusion, hyp, hyp2); } return 0; } void display_inference(std::ostream& out, char const* name, char const* status, z3::expr p) { unsigned id = Z3_get_ast_id(ctx, p); std::set const& hyps = m_proof_hypotheses.find(id)->second; out << "tff(" << m_node_number << ",plain,\n ("; display(out, get_proof_formula(p), true); out << "),\n inference(" << name << ",[status(" << status << ")"; if (!hyps.empty()) { out << ", assumptions("; print_hypotheses(out, hyps); out << ")"; } out << "],"; display_hypotheses(out, p); out << ")).\n"; } void print_hypotheses(std::ostream& out, std::set const& hyps) { std::set::iterator it = hyps.begin(), end = hyps.end(); bool first = true; out << "["; for (; it != end; ++it) { if (!first) { out << ", "; } first = false; out << m_proof_ids.find(*it)->second; } out << "]"; } unsigned display_hyp_inference(std::ostream& out, char const* name, char const* status, z3::expr conclusion, unsigned hyp1, unsigned hyp2 = 0) { ++m_node_number; out << "tff(" << m_node_number << ",plain,(\n "; display(out, conclusion, true); out << "),\n inference(" << name << ",[status(" << status << ")],"; out << "[" << hyp1; if (hyp2) { out << ", " << hyp2; } out << "])).\n"; return m_node_number; } void get_free_vars(z3::expr const& e, std::vector& vars) { std::set seen; size_t sz = todo.size(); todo.push_back(e); while (todo.size() != sz) { z3::expr e = todo.back(); todo.pop_back(); unsigned id = Z3_get_ast_id(e.ctx(), e); if (seen.find(id) != seen.end()) { continue; } seen.insert(id); if (e.is_var()) { unsigned idx = Z3_get_index_value(ctx, e); while (idx >= vars.size()) { vars.push_back(e.get_sort()); } vars[idx] = e.get_sort(); } else if (e.is_app()) { unsigned sz = e.num_args(); for (unsigned i = 0; i < sz; ++i) { todo.push_back(e.arg(i)); } } else { // e is a quantifier std::vector fv; get_free_vars(e.body(), fv); unsigned nb = Z3_get_quantifier_num_bound(e.ctx(), e); for (unsigned i = nb; i < fv.size(); ++i) { if (vars.size() <= i - nb) { vars.push_back(fv[i]); } } } } } z3::expr get_proof_formula(z3::expr proof) { // unsigned na = proof.num_args(); z3::expr result = proof.arg(proof.num_args()-1); std::vector vars; get_free_vars(result, vars); if (vars.empty()) { return result; } Z3_sort* sorts = new Z3_sort[vars.size()]; Z3_symbol* names = new Z3_symbol[vars.size()]; for (unsigned i = 0; i < vars.size(); ++i) { std::ostringstream str; str << "X" << (i+1); sorts[vars.size()-i-1] = vars[i]; names[vars.size()-i-1] = Z3_mk_string_symbol(ctx, str.str().c_str()); } result = z3::expr(ctx, Z3_mk_forall(ctx, 1, 0, 0, static_cast(vars.size()), sorts, names, result)); delete[] sorts; delete[] names; return result; } void display_hypotheses(std::ostream& out, z3::expr p) { unsigned na = p.num_args(); out << "["; for (unsigned i = 0; i + 1 < na; ++i) { out << m_proof_ids.find(Z3_get_ast_id(p.ctx(), p.arg(i)))->second; if (i + 2 < na) { out << ", "; } } out << "]"; } void display_sort_decls(std::ostream& out) { for (unsigned i = 0; i < sorts.size(); ++i) { display_sort_decl(out, sorts[i]); } } void display_sort_decl(std::ostream& out, z3::sort& s) { out << "tff(" << s << "_type, type, (" << s << ": $tType)).\n"; } void display_func_decls(std::ostream& out) { for (size_t i = 0; i < funs.size(); ++i) { display_func_decl(out, funs[i]); } } bool contains_id(unsigned id) const { return seen_ids.find(id) != seen_ids.end(); } void collect_decls(z3::expr e) { todo.push_back(e); while (!todo.empty()) { z3::expr e = todo.back(); todo.pop_back(); unsigned id = Z3_get_ast_id(ctx, e); if (contains_id(id)) { continue; } seen_ids.insert(id); if (e.is_app()) { collect_fun(e.decl()); unsigned sz = e.num_args(); for (unsigned i = 0; i < sz; ++i) { todo.push_back(e.arg(i)); } } else if (e.is_quantifier()) { unsigned nb = Z3_get_quantifier_num_bound(e.ctx(), e); for (unsigned i = 0; i < nb; ++i) { z3::sort srt(ctx, Z3_get_quantifier_bound_sort(e.ctx(), e, i)); collect_sort(srt); } todo.push_back(e.body()); } else if (e.is_var()) { collect_sort(e.get_sort()); } } } void collect_sort(z3::sort s) { unsigned id = Z3_get_sort_id(ctx, s); if (s.sort_kind() == Z3_UNINTERPRETED_SORT && contains_id(id)) { seen_ids.insert(id); sorts.push_back(s); } } void collect_fun(z3::func_decl f) { unsigned id = Z3_get_func_decl_id(ctx, f); if (contains_id(id)) { return; } seen_ids.insert(id); if (f.decl_kind() == Z3_OP_UNINTERPRETED) { funs.push_back(f); } for (unsigned i = 0; i < f.arity(); ++i) { collect_sort(f.domain(i)); } collect_sort(f.range()); } std::string upper_case_var(z3::symbol const& sym) { std::string result = sanitize(sym); char ch = result[0]; if ('A' <= ch && ch <= 'Z') { return result; } return "X" + result; } std::string lower_case_fun(z3::symbol const& sym) { std::string result = sanitize(sym); char ch = result[0]; if ('a' <= ch && ch <= 'z') { return result; } else { return "tptp_fun_" + result; } } std::string sanitize(z3::symbol const& sym) { std::ostringstream str; if (sym.kind() == Z3_INT_SYMBOL) { str << sym; return str.str(); } std::string s = sym.str(); size_t sz = s.size(); for (size_t i = 0; i < sz; ++i) { char ch = s[i]; if ('a' <= ch && ch <= 'z') { str << ch; } else if ('A' <= ch && ch <= 'Z') { str << ch; } else if ('0' <= ch && ch <= '9') { str << ch; } else if ('_' == ch) { str << ch; } else { str << "_"; } } return str.str(); } }; static char* g_input_file = 0; static bool g_display_smt2 = false; static bool g_generate_model = false; static bool g_generate_proof = false; static bool g_generate_core = false; static bool g_display_statistics = false; static bool g_first_interrupt = true; static bool g_smt2status = false; static bool g_check_status = false; static int g_timeout = 0; static double g_start_time = 0; static z3::solver* g_solver = 0; static z3::context* g_context = 0; static std::ostream* g_out = &std::cout; static void display_usage() { unsigned major, minor, build_number, revision_number; Z3_get_version(&major, &minor, &build_number, &revision_number); std::cout << "Z3tptp [" << major << "." << minor << "." << build_number << "." << revision_number << "] (c) 2006-20**. Microsoft Corp.\n"; std::cout << "Usage: tptp [options] [-file:]file\n"; std::cout << " -h, -? prints this message.\n"; std::cout << " -smt2 print SMT-LIB2 benchmark.\n"; std::cout << " -m, -model generate model.\n"; std::cout << " -p, -proof generate proof.\n"; std::cout << " -c, -core generate unsat core of named formulas.\n"; std::cout << " -st, -statistics display statistics.\n"; std::cout << " -t:timeout set timeout (in second).\n"; std::cout << " -smt2status display status in smt2 format instead of SZS.\n"; std::cout << " -check_status check the status produced by Z3 against annotation in benchmark.\n"; std::cout << " -: configuration parameter and value.\n"; std::cout << " -o: file to place output in.\n"; } static void display_statistics() { if (g_solver && g_display_statistics) { std::cout.flush(); std::cerr.flush(); double end_time = static_cast(clock()); z3::stats stats = g_solver->statistics(); std::cout << stats << "\n"; std::cout << "time: " << (end_time - g_start_time)/CLOCKS_PER_SEC << " secs\n"; } } static void on_ctrl_c(int) { if (g_context && g_first_interrupt) { Z3_interrupt(*g_context); g_first_interrupt = false; } else { signal (SIGINT, SIG_DFL); display_statistics(); raise(SIGINT); } } bool parse_token(char const*& line, char const* token) { char const* result = line; while (result[0] == ' ') ++result; while (token[0] && result[0] == token[0]) { ++token; ++result; } if (!token[0]) { line = result; return true; } else { return false; } } bool parse_is_sat_line(char const* line, bool& is_sat) { if (!parse_token(line, "%")) return false; if (!parse_token(line, "Status")) return false; if (!parse_token(line, ":")) return false; if (parse_token(line, "Unsatisfiable")) { is_sat = false; return true; } if (parse_token(line, "Theorem")) { is_sat = false; return true; } if (parse_token(line, "Theorem")) { is_sat = false; return true; } if (parse_token(line, "CounterSatisfiable")) { is_sat = true; return true; } if (parse_token(line, "Satisfiable")) { is_sat = true; return true; } return false; } bool parse_is_sat(char const* filename, bool& is_sat) { std::ifstream is(filename); if (is.bad() || is.fail()) { std::stringstream strm; strm << "Could not open file " << filename << "\n"; throw failure_ex(strm.str().c_str()); } for (unsigned i = 0; !is.eof() && i < 200; ++i) { std::string line; std::getline(is, line); if (parse_is_sat_line(line.c_str(), is_sat)) { return true; } } return false; } void parse_cmd_line_args(int argc, char ** argv) { g_input_file = 0; g_display_smt2 = false; int i = 1; while (i < argc) { char* arg = argv[i]; //char * eq = 0; char * opt_arg = 0; if (arg[0] == '-' || arg[0] == '/') { ++arg; while (*arg == '-') { ++arg; } char * colon = strchr(arg, ':'); if (colon) { opt_arg = colon + 1; *colon = 0; } if (!strcmp(arg,"h") || !strcmp(arg,"help") || !strcmp(arg,"?")) { display_usage(); exit(0); } if (!strcmp(arg,"p") || !strcmp(arg,"proof")) { g_generate_proof = true; } else if (!strcmp(arg,"m") || !strcmp(arg,"model")) { g_generate_model = true; } else if (!strcmp(arg,"c") || !strcmp(arg,"core")) { g_generate_core = true; } else if (!strcmp(arg,"st") || !strcmp(arg,"statistics")) { g_display_statistics = true; } else if (!strcmp(arg,"check_status")) { g_check_status = true; } else if (!strcmp(arg,"t") || !strcmp(arg,"timeout")) { if (!opt_arg) { display_usage(); exit(0); } g_timeout = atoi(opt_arg); } else if (!strcmp(arg,"smt2status")) { g_smt2status = true; } else if (!strcmp(arg,"o")) { if (opt_arg) { g_out = new std::ofstream(opt_arg); if (g_out->bad() || g_out->fail()) { std::cout << "Could not open file of output: " << opt_arg << "\n"; exit(0); } } else { display_usage(); exit(0); } } else if (!strcmp(arg,"smt2")) { g_display_smt2 = true; } else if (!strcmp(arg, "file")) { g_input_file = opt_arg; } else if (opt_arg && arg[0] != '"') { Z3_global_param_set(arg, opt_arg); } else { std::cerr << "parameter " << arg << " was not recognized\n"; display_usage(); exit(0); } } else { g_input_file = arg; } ++i; } if (!g_input_file) { display_usage(); exit(0); } } static bool is_smt2_file(char const* filename) { size_t len = strlen(filename); return (len > 4 && !strcmp(filename + len - 5,".smt2")); } static void check_error(z3::context& ctx) { Z3_error_code e = Z3_get_error_code(ctx); if (e != Z3_OK) { std::cout << Z3_get_error_msg(ctx, e) << "\n"; exit(1); } } static void display_tptp(std::ostream& out) { // run SMT2 parser, pretty print TFA format. z3::context ctx; z3::expr_vector fmls = ctx.parse_file(g_input_file); z3::expr fml = z3::mk_and(fmls); pp_tptp pp(ctx); pp.collect_decls(fml); pp.display_sort_decls(out); pp.display_func_decls(out); if (fml.decl().decl_kind() == Z3_OP_AND) { for (unsigned i = 0; i < fml.num_args(); ++i) { pp.display_axiom(out, fml.arg(i)); } } else { pp.display_axiom(out, fml); } } static void display_proof(z3::context& ctx, named_formulas& fmls, z3::solver& solver) { pp_tptp pp(ctx); pp.display_proof(std::cout, fmls, solver); } static void display_model(z3::context& ctx, z3::model model) { unsigned nc = model.num_consts(); unsigned nf = model.num_funcs(); z3::expr_vector fmls(ctx); for (unsigned i = 0; i < nc; ++i) { z3::func_decl f = model.get_const_decl(i); z3::expr e = model.get_const_interp(f); fmls.push_back(f() == e); } for (unsigned i = 0; i < nf; ++i) { z3::func_decl f = model.get_func_decl(i); z3::func_interp fi = model.get_func_interp(f); unsigned arity = f.arity(); z3::expr_vector args(ctx); for (unsigned j = 0; j < arity; ++j) { std::ostringstream str; str << "X" << j; z3::symbol sym(ctx, Z3_mk_string_symbol(ctx, str.str().c_str())); args.push_back(ctx.constant(sym, f.domain(j))); } unsigned ne = fi.num_entries(); Z3_ast* conds = new Z3_ast[arity]; Z3_ast* conds_match = new Z3_ast[ne]; z3::expr_vector conds_matchv(ctx); z3::expr els = fi.else_value(); unsigned num_cases = 0; for (unsigned k = 0; k < ne; ++k) { z3::func_entry e = fi.entry(k); z3::expr_vector condv(ctx), args_e(ctx); if (((Z3_ast)els) && (Z3_get_ast_id(ctx, els) == Z3_get_ast_id(ctx, e.value()))) { continue; } for (unsigned j = 0; j < arity; ++j) { args_e.push_back(e.arg(j)); condv.push_back(e.arg(j) == args[j]); conds[j] = condv.back(); } z3::expr cond(ctx, Z3_mk_and(ctx, arity, conds)); conds_matchv.push_back(cond); conds_match[num_cases] = cond; fmls.push_back(f(args_e) == e.value()); ++num_cases; } if (els) { els = f(args) == els; switch (num_cases) { case 0: els = forall(args, els); break; case 1: els = forall(args, implies(!z3::expr(ctx, conds_match[0]), els)); break; default: els = forall(args, implies(!z3::expr(ctx, Z3_mk_or(ctx, num_cases, conds_match)), els)); break; } fmls.push_back(els); } delete[] conds; delete[] conds_match; } pp_tptp pp(ctx); for (unsigned i = 0; i < fmls.size(); ++i) { pp.collect_decls(fmls[i]); } pp.display_sort_decls(std::cout); pp.display_func_decls(std::cout); for (unsigned i = 0; i < fmls.size(); ++i) { pp.display_axiom(std::cout, fmls[i]); } } static void display_smt2(std::ostream& out) { z3::config config; z3::context ctx(config); named_formulas fmls; env env(ctx); try { env.parse(g_input_file, fmls); } catch (failure_ex& ex) { std::cerr << ex.msg << "\n"; return; } size_t num_assumptions = fmls.m_formulas.size(); Z3_ast* assumptions = new Z3_ast[num_assumptions]; for (size_t i = 0; i < num_assumptions; ++i) { assumptions[i] = fmls.m_formulas[i]; } Z3_string s = Z3_benchmark_to_smtlib_string( ctx, "Benchmark generated from TPTP", // comment 0, // no logic is set "unknown", // no status annotation "", // attributes static_cast(num_assumptions), assumptions, ctx.bool_val(true)); out << s << "\n"; delete[] assumptions; } static void prove_tptp() { z3::config config; if (g_generate_proof) { config.set("proof", true); z3::set_param("proof", true); } z3::context ctx(config); z3::solver solver(ctx); g_solver = &solver; g_context = &ctx; if (g_timeout) { // TBD overflow check z3::set_param("timeout", g_timeout*1000); z3::params params(ctx); params.set("timeout", static_cast(g_timeout*1000)); solver.set(params); } named_formulas fmls; env env(ctx); try { env.parse(g_input_file, fmls); } catch (failure_ex& ex) { std::cerr << ex.msg << "\n"; std::cout << "% SZS status GaveUp\n"; return; } size_t num_assumptions = fmls.m_formulas.size(); z3::check_result result; if (g_generate_core) { z3::expr_vector assumptions(ctx); for (size_t i = 0; i < num_assumptions; ++i) { z3::expr pred = ctx.constant(fmls.m_names[i].c_str(), ctx.bool_sort()); z3::expr def = fmls.m_formulas[i] == pred; solver.add(def); assumptions.push_back(pred); } result = solver.check(assumptions); } else { for (unsigned i = 0; i < num_assumptions; ++i) { solver.add(fmls.m_formulas[i]); } result = solver.check(); } switch(result) { case z3::unsat: if (g_smt2status) { std::cout << result << "\n"; } else if (fmls.has_conjecture()) { std::cout << "% SZS status Theorem\n"; } else { std::cout << "% SZS status Unsatisfiable\n"; } if (g_generate_proof) { try { std::cout << "% SZS output start Proof\n"; display_proof(ctx, fmls, solver); std::cout << "% SZS output end Proof\n"; } catch (failure_ex& ex) { std::cerr << "Proof display could not be completed: " << ex.msg << "\n"; } } if (g_generate_core) { z3::expr_vector core = solver.unsat_core(); std::cout << "% SZS core "; for (unsigned i = 0; i < core.size(); ++i) { std::cout << core[i] << " "; } std::cout << "\n"; } break; case z3::sat: if (g_smt2status) { std::cout << result << "\n"; } else if (fmls.has_conjecture()) { std::cout << "% SZS status CounterSatisfiable\n"; } else { std::cout << "% SZS status Satisfiable\n"; } if (g_generate_model) { std::cout << "% SZS output start Model\n"; display_model(ctx, solver.get_model()); std::cout << "% SZS output end Model\n"; } break; case z3::unknown: if (g_smt2status) { std::cout << result << "\n"; } else if (!g_first_interrupt) { std::cout << "% SZS status Interrupted\n"; } else { std::cout << "% SZS status GaveUp\n"; std::string reason = solver.reason_unknown(); std::cout << "% SZS reason " << reason << "\n"; } break; } bool is_sat = true; if (g_check_status && result != z3::unknown && parse_is_sat(g_input_file, is_sat)) { if (is_sat && result == z3::unsat) { std::cout << "BUG!! expected result is Satisfiable, returned result is Unsat\n"; } if (!is_sat && result == z3::sat) { std::cout << "BUG!! expected result is Unsatisfiable, returned result is Satisfiable\n"; } } display_statistics(); } int main(int argc, char** argv) { g_start_time = static_cast(clock()); signal(SIGINT, on_ctrl_c); parse_cmd_line_args(argc, argv); if (is_smt2_file(g_input_file)) { display_tptp(*g_out); } else if (g_display_smt2) { display_smt2(*g_out); } else { try { prove_tptp(); } catch (z3::exception& ex) { std::cerr << "Exception during proof: " << ex.msg() << "\n"; } } return 0; } z3-z3-4.8.7/examples/tptp/tptp5.h000066400000000000000000000021041356505360400164560ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #ifndef TPTP5_H_ #define TPTP5_H_ class TreeNode; #if 0 class named_formulas { expr_ref_vector m_fmls; svector m_names; bool m_has_conjecture; unsigned m_conjecture_index; public: named_formulas(ast_manager& m) : m_fmls(m), m_has_conjecture(false), m_conjecture_index(0) {} void push_back(expr* fml, char const* name) { m_fmls.push_back(fml); m_names.push_back(symbol(name)); } unsigned size() const { return m_fmls.size(); } expr*const* c_ptr() const { return m_fmls.c_ptr(); } expr* operator[](unsigned i) { return m_fmls[i].get(); } symbol const& name(unsigned i) { return m_names[i]; } void set_has_conjecture() { m_has_conjecture = true; m_conjecture_index = m_fmls.size(); } bool has_conjecture() const { return m_has_conjecture; } unsigned conjecture_index() const { return m_conjecture_index; } }; bool tptp5_parse(ast_manager& m, char const* filename, named_formulas& fmls); #endif #endif z3-z3-4.8.7/examples/tptp/tptp5.lex.cpp000066400000000000000000002050761356505360400176150ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #line 2 "tptp5.lex.cpp" #line 4 "tptp5.lex.cpp" #define YY_INT_ALIGNED short int /* A lexical scanner generated by flex */ #define FLEX_SCANNER #define YY_FLEX_MAJOR_VERSION 2 #define YY_FLEX_MINOR_VERSION 5 #define YY_FLEX_SUBMINOR_VERSION 35 #if YY_FLEX_SUBMINOR_VERSION > 0 #define FLEX_BETA #endif /* First, we deal with platform-specific or compiler-specific issues. */ /* begin standard C headers. */ #include #include #include #include /* end standard C headers. */ /* flex integer type definitions */ #ifndef FLEXINT_H #define FLEXINT_H /* C99 systems have . Non-C99 systems may or may not. */ #if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, * if you want the limit (max/min) macros for int types. */ #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS 1 #endif #include typedef int8_t flex_int8_t; typedef uint8_t flex_uint8_t; typedef int16_t flex_int16_t; typedef uint16_t flex_uint16_t; typedef int32_t flex_int32_t; typedef uint32_t flex_uint32_t; #else typedef signed char flex_int8_t; typedef short int flex_int16_t; typedef int flex_int32_t; typedef unsigned char flex_uint8_t; typedef unsigned short int flex_uint16_t; typedef unsigned int flex_uint32_t; #endif /* ! C99 */ /* Limits of integral types. */ #ifndef INT8_MIN #define INT8_MIN (-128) #endif #ifndef INT16_MIN #define INT16_MIN (-32767-1) #endif #ifndef INT32_MIN #define INT32_MIN (-2147483647-1) #endif #ifndef INT8_MAX #define INT8_MAX (127) #endif #ifndef INT16_MAX #define INT16_MAX (32767) #endif #ifndef INT32_MAX #define INT32_MAX (2147483647) #endif #ifndef UINT8_MAX #define UINT8_MAX (255U) #endif #ifndef UINT16_MAX #define UINT16_MAX (65535U) #endif #ifndef UINT32_MAX #define UINT32_MAX (4294967295U) #endif #endif /* ! FLEXINT_H */ #ifdef __cplusplus /* The "const" storage-class-modifier is valid. */ #define YY_USE_CONST #else /* ! __cplusplus */ /* C99 requires __STDC__ to be defined as 1. */ #if defined (__STDC__) #define YY_USE_CONST #endif /* defined (__STDC__) */ #endif /* ! __cplusplus */ #ifdef YY_USE_CONST #define yyconst const #else #define yyconst #endif /* Returned upon end-of-file. */ #define YY_NULL 0 /* Promotes a possibly negative, possibly signed char to an unsigned * integer for use as an array index. If the signed char is negative, * we want to instead treat it as an 8-bit unsigned char, hence the * double cast. */ #define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) /* Enter a start condition. This macro really ought to take a parameter, * but we do it the disgusting crufty way forced on us by the ()-less * definition of BEGIN. */ #define BEGIN (yy_start) = 1 + 2 * /* Translate the current start state into a value that can be later handed * to BEGIN to return to the state. The YYSTATE alias is for lex * compatibility. */ #define YY_START (((yy_start) - 1) / 2) #define YYSTATE YY_START /* Action number for EOF rule of a given start state. */ #define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) /* Special action meaning "start processing a new file". */ #define YY_NEW_FILE yyrestart(yyin ) #define YY_END_OF_BUFFER_CHAR 0 /* Size of default input buffer. */ #ifndef YY_BUF_SIZE #define YY_BUF_SIZE 16384 #endif /* The state buf must be large enough to hold one state per character in the main buffer. */ #define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) #ifndef YY_TYPEDEF_YY_BUFFER_STATE #define YY_TYPEDEF_YY_BUFFER_STATE typedef struct yy_buffer_state *YY_BUFFER_STATE; #endif extern int yyleng; extern FILE *yyin, *yyout; #define EOB_ACT_CONTINUE_SCAN 0 #define EOB_ACT_END_OF_FILE 1 #define EOB_ACT_LAST_MATCH 2 /* Note: We specifically omit the test for yy_rule_can_match_eol because it requires * access to the local variable yy_act. Since yyless() is a macro, it would break * existing scanners that call yyless() from OUTSIDE yylex. * One obvious solution it to make yy_act a global. I tried that, and saw * a 5% performance hit in a non-yylineno scanner, because yy_act is * normally declared as a register variable-- so it is not worth it. */ #define YY_LESS_LINENO(n) \ do { \ int yyl;\ for ( yyl = n; yyl < yyleng; ++yyl )\ if ( yytext[yyl] == '\n' )\ --yylineno;\ }while(0) /* Return all but the first "n" matched characters back to the input stream. */ #define yyless(n) \ do \ { \ /* Undo effects of setting up yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ *yy_cp = (yy_hold_char); \ YY_RESTORE_YY_MORE_OFFSET \ (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ YY_DO_BEFORE_ACTION; /* set up yytext again */ \ } \ while ( 0 ) #define unput(c) yyunput( c, (yytext_ptr) ) #ifndef YY_TYPEDEF_YY_SIZE_T #define YY_TYPEDEF_YY_SIZE_T typedef size_t yy_size_t; #endif #ifndef YY_STRUCT_YY_BUFFER_STATE #define YY_STRUCT_YY_BUFFER_STATE struct yy_buffer_state { FILE *yy_input_file; char *yy_ch_buf; /* input buffer */ char *yy_buf_pos; /* current position in input buffer */ /* Size of input buffer in bytes, not including room for EOB * characters. */ yy_size_t yy_buf_size; /* Number of characters read into yy_ch_buf, not including EOB * characters. */ int yy_n_chars; /* Whether we "own" the buffer - i.e., we know we created it, * and can realloc() it to grow it, and should free() it to * delete it. */ int yy_is_our_buffer; /* Whether this is an "interactive" input source; if so, and * if we're using stdio for input, then we want to use getc() * instead of fread(), to make sure we stop fetching input after * each newline. */ int yy_is_interactive; /* Whether we're considered to be at the beginning of a line. * If so, '^' rules will be active on the next match, otherwise * not. */ int yy_at_bol; int yy_bs_lineno; /**< The line count. */ int yy_bs_column; /**< The column count. */ /* Whether to try to fill the input buffer when we reach the * end of it. */ int yy_fill_buffer; int yy_buffer_status; #define YY_BUFFER_NEW 0 #define YY_BUFFER_NORMAL 1 /* When an EOF's been seen but there's still some text to process * then we mark the buffer as YY_EOF_PENDING, to indicate that we * shouldn't try reading from the input source any more. We might * still have a bunch of tokens to match, though, because of * possible backing-up. * * When we actually see the EOF, we change the status to "new" * (via yyrestart()), so that the user can continue scanning by * just pointing yyin at a new input file. */ #define YY_BUFFER_EOF_PENDING 2 }; #endif /* !YY_STRUCT_YY_BUFFER_STATE */ /* Stack of input buffers. */ static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */ static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */ static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */ /* We provide macros for accessing buffer states in case in the * future we want to put the buffer states in a more general * "scanner state". * * Returns the top of the stack, or NULL. */ #define YY_CURRENT_BUFFER ( (yy_buffer_stack) \ ? (yy_buffer_stack)[(yy_buffer_stack_top)] \ : NULL) /* Same as previous macro, but useful when we know that the buffer stack is not * NULL or when we need an lvalue. For internal use only. */ #define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)] /* yy_hold_char holds the character lost when yytext is formed. */ static char yy_hold_char; static int yy_n_chars; /* number of characters read into yy_ch_buf */ int yyleng; /* Points to current character in buffer. */ static char *yy_c_buf_p = (char *) 0; static int yy_init = 0; /* whether we need to initialize */ static int yy_start = 0; /* start state number */ /* Flag which is used to allow yywrap()'s to do buffer switches * instead of setting up a fresh yyin. A bit of a hack ... */ static int yy_did_buffer_switch_on_eof; void yyrestart (FILE *input_file ); void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ); YY_BUFFER_STATE yy_create_buffer (FILE *file,int size ); void yy_delete_buffer (YY_BUFFER_STATE b ); void yy_flush_buffer (YY_BUFFER_STATE b ); void yypush_buffer_state (YY_BUFFER_STATE new_buffer ); void yypop_buffer_state (void ); static void yyensure_buffer_stack (void ); static void yy_load_buffer_state (void ); static void yy_init_buffer (YY_BUFFER_STATE b,FILE *file ); #define YY_FLUSH_BUFFER yy_flush_buffer(YY_CURRENT_BUFFER ) YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size ); YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str ); YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,int len ); void *yyalloc (yy_size_t ); void *yyrealloc (void *,yy_size_t ); void yyfree (void * ); #define yy_new_buffer yy_create_buffer #define yy_set_interactive(is_interactive) \ { \ if ( ! YY_CURRENT_BUFFER ){ \ yyensure_buffer_stack (); \ YY_CURRENT_BUFFER_LVALUE = \ yy_create_buffer(yyin,YY_BUF_SIZE ); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ } #define yy_set_bol(at_bol) \ { \ if ( ! YY_CURRENT_BUFFER ){\ yyensure_buffer_stack (); \ YY_CURRENT_BUFFER_LVALUE = \ yy_create_buffer(yyin,YY_BUF_SIZE ); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ } #define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) /* Begin user sect3 */ typedef unsigned char YY_CHAR; FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0; typedef int yy_state_type; #define YY_FLEX_LEX_COMPAT extern int yylineno; int yylineno = 1; extern char yytext[]; static yy_state_type yy_get_previous_state (void ); static yy_state_type yy_try_NUL_trans (yy_state_type current_state ); static int yy_get_next_buffer (void ); static void yy_fatal_error (yyconst char msg[] ); /* Done after the current pattern has been matched and before the * corresponding action - sets up yytext. */ #define YY_DO_BEFORE_ACTION \ (yytext_ptr) = yy_bp; \ yyleng = (int) (yy_cp - yy_bp); \ (yy_hold_char) = *yy_cp; \ *yy_cp = '\0'; \ if ( yyleng + (yy_more_offset) >= YYLMAX ) \ YY_FATAL_ERROR( "token too large, exceeds YYLMAX" ); \ yy_flex_strncpy( &yytext[(yy_more_offset)], (yytext_ptr), yyleng + 1 ); \ yyleng += (yy_more_offset); \ (yy_prev_more_offset) = (yy_more_offset); \ (yy_more_offset) = 0; \ (yy_c_buf_p) = yy_cp; #define YY_NUM_RULES 75 #define YY_END_OF_BUFFER 76 /* This struct is not used in this scanner, but its presence is necessary. */ struct yy_trans_info { flex_int32_t yy_verify; flex_int32_t yy_nxt; }; static yyconst flex_int16_t yy_acclist[228] = { 0, 76, 73, 75, 72, 73, 75, 11, 74, 75, 74, 75, 74, 75, 74, 75, 71, 74, 75, 1, 74, 75, 74, 75, 19, 74, 75, 27, 74, 75, 28, 53, 74, 75, 54, 74, 75, 8, 74, 75, 20, 74, 75, 22, 74, 75, 74, 75, 63, 65, 66, 74, 75, 63, 65, 66, 67, 74, 75, 6, 74, 75, 56, 74, 75, 9, 74, 75, 55, 74, 75, 23, 74, 75, 2, 74, 75, 50, 74, 75, 15, 74, 75, 26, 74, 75, 5, 74, 75, 51, 74, 75, 51, 74, 75, 51, 74, 75, 51, 74, 75, 51, 74, 75, 32, 52, 74, 75, 29, 74, 75, 75, 13, 12, 14, 47, 48, 48, 48, 48, 48, 71, 63, 64, 63, 64, 70, 63, 65, 66, 67, 7, 16, 10, 25, 24, 4, 3, 50, 51, 51, 51, 51, 51, 51, 30, 31, 49, 48, 48, 48, 48, 48, 48, 46, 63, 64, 21, 70, 57, 59, 69, 60, 62, 57, 59, 68, 57, 59, 68, 17, 18, 41, 51, 42, 51, 51, 44, 51, 45, 51, 49, 33, 48, 34, 48, 35, 48, 48, 39, 48, 40, 48, 57, 58, 60, 61, 57, 58, 57, 58, 71, 57, 59, 69, 60, 62, 57, 59, 68, 51, 36, 48, 48, 57, 58, 60, 61, 57, 58, 51, 37, 48, 38, 48, 51, 43, 51 } ; static yyconst flex_int16_t yy_accept[153] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 4, 7, 10, 12, 14, 16, 19, 22, 24, 27, 30, 34, 37, 40, 43, 46, 48, 53, 59, 62, 65, 68, 71, 74, 77, 80, 83, 86, 89, 92, 95, 98, 101, 104, 108, 111, 112, 113, 114, 115, 115, 116, 116, 116, 117, 118, 119, 120, 121, 122, 122, 122, 124, 126, 126, 127, 127, 127, 127, 127, 131, 132, 133, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 154, 155, 155, 155, 155, 155, 157, 158, 159, 159, 159, 162, 164, 167, 170, 171, 172, 174, 176, 177, 179, 181, 182, 184, 186, 188, 189, 191, 193, 195, 197, 199, 201, 201, 201, 202, 205, 207, 210, 211, 213, 214, 216, 218, 220, 221, 223, 225, 226, 228, 228 } ; static yyconst flex_int32_t yy_ec[256] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 7, 23, 24, 25, 26, 27, 28, 28, 28, 28, 29, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 30, 31, 32, 33, 34, 7, 35, 35, 36, 37, 38, 39, 35, 40, 41, 35, 35, 42, 35, 43, 44, 35, 35, 35, 35, 45, 46, 35, 35, 35, 35, 35, 7, 47, 7, 48, 49, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50 } ; static yyconst flex_int32_t yy_meta[51] = { 0, 1, 1, 2, 3, 3, 3, 3, 4, 3, 3, 5, 3, 3, 3, 3, 3, 3, 3, 3, 6, 6, 3, 3, 3, 3, 3, 3, 6, 6, 3, 3, 3, 3, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 3, 3, 1, 1 } ; static yyconst flex_int16_t yy_base[164] = { 0, 0, 0, 15, 16, 23, 30, 31, 38, 45, 46, 53, 60, 61, 68, 302, 303, 303, 90, 47, 303, 80, 0, 303, 270, 303, 303, 303, 90, 303, 103, 97, 286, 108, 110, 275, 84, 273, 303, 108, 48, 0, 303, 303, 303, 0, 254, 252, 252, 96, 303, 93, 303, 303, 303, 303, 127, 303, 132, 0, 0, 222, 213, 209, 102, 0, 62, 133, 131, 133, 228, 135, 231, 145, 223, 147, 154, 303, 218, 217, 303, 303, 303, 303, 303, 0, 0, 202, 201, 202, 197, 196, 303, 303, 0, 0, 180, 131, 171, 157, 143, 146, 303, 148, 160, 157, 164, 168, 303, 170, 147, 179, 174, 179, 303, 181, 303, 303, 0, 0, 105, 0, 0, 0, 0, 0, 0, 165, 0, 0, 187, 193, 303, 197, 131, 201, 303, 201, 203, 206, 97, 0, 166, 208, 211, 213, 75, 0, 0, 42, 0, 303, 244, 248, 255, 260, 262, 264, 51, 266, 271, 278, 280, 287 } ; static yyconst flex_int16_t yy_def[164] = { 0, 151, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 151, 151, 151, 151, 152, 151, 153, 154, 151, 155, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 156, 151, 151, 151, 157, 157, 157, 157, 157, 151, 151, 151, 151, 151, 151, 152, 151, 151, 158, 159, 159, 159, 159, 159, 154, 160, 151, 151, 151, 151, 151, 161, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 156, 157, 157, 157, 157, 157, 157, 151, 151, 162, 159, 159, 159, 159, 159, 159, 160, 151, 151, 151, 151, 151, 151, 151, 151, 161, 163, 151, 151, 151, 151, 151, 151, 157, 157, 157, 157, 157, 162, 159, 159, 159, 159, 159, 159, 151, 151, 151, 151, 161, 163, 151, 151, 151, 151, 157, 159, 159, 151, 151, 151, 157, 159, 159, 157, 157, 0, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151 } ; static yyconst flex_int16_t yy_nxt[354] = { 0, 16, 17, 17, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 41, 42, 20, 43, 44, 20, 45, 46, 45, 45, 47, 45, 48, 45, 45, 45, 49, 45, 50, 51, 16, 52, 45, 45, 57, 45, 45, 45, 45, 94, 45, 45, 45, 45, 83, 45, 84, 45, 45, 45, 45, 45, 45, 45, 102, 45, 45, 45, 45, 58, 45, 150, 45, 45, 45, 45, 45, 45, 45, 59, 45, 45, 45, 45, 103, 45, 53, 45, 45, 45, 45, 45, 45, 45, 92, 45, 45, 45, 45, 78, 45, 68, 69, 149, 45, 54, 55, 61, 71, 71, 62, 70, 63, 81, 68, 69, 64, 73, 74, 73, 74, 76, 76, 79, 57, 82, 90, 91, 75, 56, 75, 93, 99, 100, 146, 66, 111, 75, 140, 75, 104, 105, 104, 105, 107, 107, 109, 109, 102, 58, 101, 106, 111, 106, 56, 66, 112, 112, 114, 115, 106, 125, 106, 73, 74, 76, 76, 126, 103, 131, 101, 130, 130, 129, 75, 132, 133, 104, 105, 107, 107, 109, 109, 75, 135, 137, 137, 128, 106, 136, 138, 138, 139, 139, 75, 141, 147, 106, 143, 143, 127, 142, 148, 75, 144, 144, 135, 106, 145, 145, 124, 136, 137, 137, 138, 138, 106, 139, 139, 143, 143, 75, 144, 144, 145, 145, 122, 121, 106, 120, 75, 119, 118, 117, 116, 113, 111, 106, 56, 56, 56, 56, 56, 60, 108, 98, 60, 65, 97, 65, 65, 65, 65, 65, 66, 66, 96, 66, 66, 85, 85, 86, 86, 95, 95, 101, 101, 101, 101, 101, 110, 110, 110, 110, 110, 110, 110, 123, 123, 134, 134, 134, 134, 134, 134, 134, 89, 88, 87, 80, 77, 72, 67, 151, 15, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151 } ; static yyconst flex_int16_t yy_chk[354] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 4, 19, 3, 4, 3, 4, 158, 5, 3, 4, 5, 40, 5, 40, 6, 7, 5, 6, 7, 6, 7, 66, 8, 6, 7, 8, 19, 8, 149, 9, 10, 8, 9, 10, 9, 10, 21, 11, 9, 10, 11, 66, 11, 18, 12, 13, 11, 12, 13, 12, 13, 51, 14, 12, 13, 14, 36, 14, 28, 28, 146, 14, 18, 18, 21, 31, 31, 21, 30, 21, 39, 30, 30, 21, 33, 33, 34, 34, 34, 34, 36, 56, 39, 49, 49, 33, 58, 34, 51, 64, 64, 140, 67, 134, 33, 120, 34, 68, 68, 69, 69, 69, 69, 71, 71, 101, 56, 103, 68, 110, 69, 58, 67, 73, 73, 75, 75, 68, 97, 69, 76, 76, 76, 76, 97, 101, 105, 103, 104, 104, 100, 76, 106, 106, 107, 107, 107, 107, 109, 109, 76, 111, 112, 112, 99, 107, 111, 113, 113, 115, 115, 112, 127, 142, 107, 130, 130, 98, 127, 142, 112, 131, 131, 135, 130, 133, 133, 96, 135, 137, 137, 138, 138, 130, 139, 139, 143, 143, 137, 144, 144, 145, 145, 91, 90, 143, 89, 137, 88, 87, 79, 78, 74, 72, 143, 152, 152, 152, 152, 152, 153, 70, 63, 153, 154, 62, 154, 154, 154, 154, 154, 155, 155, 61, 155, 155, 156, 156, 157, 157, 159, 159, 160, 160, 160, 160, 160, 161, 161, 161, 161, 161, 161, 161, 162, 162, 163, 163, 163, 163, 163, 163, 163, 48, 47, 46, 37, 35, 32, 24, 15, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151 } ; /* Table of booleans, true if rule could match eol. */ static yyconst flex_int32_t yy_rule_can_match_eol[76] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, }; extern int yy_flex_debug; int yy_flex_debug = 0; static yy_state_type *yy_state_buf=0, *yy_state_ptr=0; static char *yy_full_match; static int yy_lp; #define REJECT \ { \ *yy_cp = (yy_hold_char); /* undo effects of setting up yytext */ \ yy_cp = (yy_full_match); /* restore poss. backed-over text */ \ ++(yy_lp); \ goto find_rule; \ } static int yy_more_offset = 0; static int yy_prev_more_offset = 0; #define yymore() ((yy_more_offset) = yy_flex_strlen( yytext )) #define YY_NEED_STRLEN #define YY_MORE_ADJ 0 #define YY_RESTORE_YY_MORE_OFFSET \ { \ (yy_more_offset) = (yy_prev_more_offset); \ yyleng -= (yy_more_offset); \ } #ifndef YYLMAX #define YYLMAX 8192 #endif char yytext[YYLMAX]; char *yytext_ptr; #line 1 "tptp5.l" #line 3 "tptp5.l" //----------------------------------------------------------------------------- #include #include #include #if _WINDOWS #include #define isatty _isatty #else // Linux #include #define _strdup strdup #endif #include "tptp5.h" #include "tptp5.tab.h" #define YY_NO_UNISTD_H #define YY_SKIP_YYWRAP static int yywrap() { return 1; } //----------------------------------------------------------------------------- //----Compile with -DP_VERBOSE=2 to list tokens as they are seen. #ifndef P_VERBOSE # define P_VERBOSE 0 # endif int verbose2 = P_VERBOSE; //----If tptp_prev_tok == PERIOD, you are outside any sentence. //#ifndef PERIOD //# error "Period not defined" //# define PERIOD 46 //# endif #define TPTP_STORE_SIZE 32768 //----These have to be external as they are references from other code that //----is generated by lex/yacc. int tptp_prev_tok = PERIOD; int tptp_store_size = TPTP_STORE_SIZE; char* tptp_lval[TPTP_STORE_SIZE]; //----------------------------------------------------------------------------- void tptp_print_tok(char* lval) { printf("%3d:%s;\n", tptp_prev_tok, lval); return; } //----------------------------------------------------------------------------- int tptp_update_lval(char* lval) { static int tptp_next_store = 0; int next = tptp_next_store; free(tptp_lval[tptp_next_store]); tptp_lval[tptp_next_store] = _strdup(lval); tptp_next_store = (tptp_next_store+1) % TPTP_STORE_SIZE; if (verbose2 == 2) { tptp_print_tok(lval); } return next; } //----------------------------------------------------------------------------- //----%Start: INITIAL begin sentence, B before formula. No others. #line 710 "tptp5.lex.cpp" #define INITIAL 0 #define B 1 #define FF 2 #define SQ1 3 #define SQ2 4 #define Q1 5 #define Q2 6 #ifndef YY_NO_UNISTD_H /* Special case for "unistd.h", since it is non-ANSI. We include it way * down here because we want the user's section 1 to have been scanned first. * The user has a chance to override it with an option. */ #include #endif #ifndef YY_EXTRA_TYPE #define YY_EXTRA_TYPE void * #endif static int yy_init_globals (void ); /* Accessor methods to globals. These are made visible to non-reentrant scanners for convenience. */ int yylex_destroy (void ); int yyget_debug (void ); void yyset_debug (int debug_flag ); YY_EXTRA_TYPE yyget_extra (void ); void yyset_extra (YY_EXTRA_TYPE user_defined ); FILE *yyget_in (void ); void yyset_in (FILE * in_str ); FILE *yyget_out (void ); void yyset_out (FILE * out_str ); int yyget_leng (void ); char *yyget_text (void ); int yyget_lineno (void ); void yyset_lineno (int line_number ); /* Macros after this point can all be overridden by user definitions in * section 1. */ #ifndef YY_SKIP_YYWRAP #ifdef __cplusplus extern "C" int yywrap (void ); #else extern int yywrap (void ); #endif #endif static void yyunput (int c,char *buf_ptr ); #ifndef yytext_ptr static void yy_flex_strncpy (char *,yyconst char *,int ); #endif #ifdef YY_NEED_STRLEN static int yy_flex_strlen (yyconst char * ); #endif #ifndef YY_NO_INPUT #ifdef __cplusplus static int yyinput (void ); #else static int input (void ); #endif #endif /* Amount of stuff to slurp up with each read. */ #ifndef YY_READ_BUF_SIZE #define YY_READ_BUF_SIZE 8192 #endif /* Copy whatever the last rule matched to the standard output. */ #ifndef ECHO /* This used to be an fputs(), but since the string might contain NUL's, * we now use fwrite(). */ #define ECHO fwrite( yytext, yyleng, 1, yyout ) #endif /* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, * is returned in "result". */ #ifndef YY_INPUT #define YY_INPUT(buf,result,max_size) \ if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ { \ int c = '*'; \ int n; \ for ( n = 0; n < max_size && \ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ buf[n] = (char) c; \ if ( c == '\n' ) \ buf[n++] = (char) c; \ if ( c == EOF && ferror( yyin ) ) \ YY_FATAL_ERROR( "input in flex scanner failed" ); \ result = n; \ } \ else \ { \ errno=0; \ while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \ { \ if( errno != EINTR) \ { \ YY_FATAL_ERROR( "input in flex scanner failed" ); \ break; \ } \ errno=0; \ clearerr(yyin); \ } \ }\ \ #endif /* No semi-colon after return; correct usage is to write "yyterminate();" - * we don't want an extra ';' after the "return" because that will cause * some compilers to complain about unreachable statements. */ #ifndef yyterminate #define yyterminate() return YY_NULL #endif /* Number of entries by which start-condition stack grows. */ #ifndef YY_START_STACK_INCR #define YY_START_STACK_INCR 25 #endif /* Report a fatal error. */ #ifndef YY_FATAL_ERROR #define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) #endif /* end tables serialization structures and prototypes */ /* Default declaration of generated scanner - a define so the user can * easily add parameters. */ #ifndef YY_DECL #define YY_DECL_IS_OURS 1 extern int yylex (void); #define YY_DECL int yylex (void) #endif /* !YY_DECL */ /* Code executed at the beginning of each rule, after yytext and yyleng * have been set up. */ #ifndef YY_USER_ACTION #define YY_USER_ACTION #endif /* Code executed at the end of each rule. */ #ifndef YY_BREAK #define YY_BREAK break; #endif #define YY_RULE_SETUP \ YY_USER_ACTION /** The main scanner function which does all the work. */ YY_DECL { register yy_state_type yy_current_state; register char *yy_cp, *yy_bp; register int yy_act; #line 110 "tptp5.l" #line 901 "tptp5.lex.cpp" if ( !(yy_init) ) { (yy_init) = 1; #ifdef YY_USER_INIT YY_USER_INIT; #endif /* Create the reject buffer large enough to save one state per allowed character. */ if ( ! (yy_state_buf) ) (yy_state_buf) = (yy_state_type *)yyalloc(YY_STATE_BUF_SIZE ); if ( ! (yy_state_buf) ) YY_FATAL_ERROR( "out of dynamic memory in yylex()" ); if ( ! (yy_start) ) (yy_start) = 1; /* first start state */ if ( ! yyin ) yyin = stdin; if ( ! yyout ) yyout = stdout; if ( ! YY_CURRENT_BUFFER ) { yyensure_buffer_stack (); YY_CURRENT_BUFFER_LVALUE = yy_create_buffer(yyin,YY_BUF_SIZE ); } yy_load_buffer_state( ); } while ( 1 ) /* loops until end-of-file is reached */ { yy_cp = (yy_c_buf_p); /* Support of yytext. */ *yy_cp = (yy_hold_char); /* yy_bp points to the position in yy_ch_buf of the start of * the current run. */ yy_bp = yy_cp; yy_current_state = (yy_start); (yy_state_ptr) = (yy_state_buf); *(yy_state_ptr)++ = yy_current_state; yy_match: do { register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 152 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; *(yy_state_ptr)++ = yy_current_state; ++yy_cp; } while ( yy_base[yy_current_state] != 303 ); yy_find_action: yy_current_state = *--(yy_state_ptr); (yy_lp) = yy_accept[yy_current_state]; goto find_rule; find_rule: /* we branch to this label when backing up */ for ( ; ; ) /* until we find what rule we matched */ { if ( (yy_lp) && (yy_lp) < yy_accept[yy_current_state + 1] ) { yy_act = yy_acclist[(yy_lp)]; { (yy_full_match) = yy_cp; break; } } --yy_cp; yy_current_state = *--(yy_state_ptr); (yy_lp) = yy_accept[yy_current_state]; } YY_DO_BEFORE_ACTION; if ( yy_act != YY_END_OF_BUFFER && yy_rule_can_match_eol[yy_act] ) { int yyl; for ( yyl = (yy_prev_more_offset); yyl < yyleng; ++yyl ) if ( yytext[yyl] == '\n' ) yylineno++; ; } do_action: /* This label is used only to access EOF actions. */ switch ( yy_act ) { /* beginning of action switch */ case 1: YY_RULE_SETUP #line 112 "tptp5.l" { tptp_prev_tok=AMPERSAND; yylval.ival = tptp_update_lval(yytext); return(AMPERSAND); } YY_BREAK case 2: YY_RULE_SETUP #line 117 "tptp5.l" { tptp_prev_tok=AT_SIGN; yylval.ival = tptp_update_lval(yytext); return(AT_SIGN); } YY_BREAK case 3: YY_RULE_SETUP #line 122 "tptp5.l" { tptp_prev_tok=AT_SIGN_MINUS; yylval.ival = tptp_update_lval(yytext); return(AT_SIGN_MINUS); } YY_BREAK case 4: YY_RULE_SETUP #line 127 "tptp5.l" { tptp_prev_tok=AT_SIGN_PLUS; yylval.ival = tptp_update_lval(yytext); return(AT_SIGN_PLUS); } YY_BREAK case 5: YY_RULE_SETUP #line 132 "tptp5.l" { tptp_prev_tok=CARET; yylval.ival = tptp_update_lval(yytext); return(CARET); } YY_BREAK case 6: YY_RULE_SETUP #line 137 "tptp5.l" { tptp_prev_tok=COLON; yylval.ival = tptp_update_lval(yytext); return(COLON); } YY_BREAK case 7: YY_RULE_SETUP #line 142 "tptp5.l" { tptp_prev_tok=COLON_EQUALS; yylval.ival = tptp_update_lval(yytext); return(COLON_EQUALS); } YY_BREAK case 8: YY_RULE_SETUP #line 147 "tptp5.l" { tptp_prev_tok=COMMA; yylval.ival = tptp_update_lval(yytext); return(COMMA); } YY_BREAK case 9: YY_RULE_SETUP #line 152 "tptp5.l" { tptp_prev_tok=EQUALS; yylval.ival = tptp_update_lval(yytext); return(EQUALS); } YY_BREAK case 10: YY_RULE_SETUP #line 157 "tptp5.l" { tptp_prev_tok=EQUALS_GREATER; yylval.ival = tptp_update_lval(yytext); return(EQUALS_GREATER); } YY_BREAK case 11: YY_RULE_SETUP #line 162 "tptp5.l" { tptp_prev_tok=EXCLAMATION; yylval.ival = tptp_update_lval(yytext); return(EXCLAMATION); } YY_BREAK case 12: YY_RULE_SETUP #line 167 "tptp5.l" { tptp_prev_tok=EXCLAMATION_EQUALS; yylval.ival = tptp_update_lval(yytext); return(EXCLAMATION_EQUALS); } YY_BREAK case 13: YY_RULE_SETUP #line 172 "tptp5.l" { tptp_prev_tok=EXCLAMATION_EXCLAMATION; yylval.ival = tptp_update_lval(yytext); return(EXCLAMATION_EXCLAMATION); } YY_BREAK case 14: YY_RULE_SETUP #line 177 "tptp5.l" { tptp_prev_tok=EXCLAMATION_GREATER; yylval.ival = tptp_update_lval(yytext); return(EXCLAMATION_GREATER); } YY_BREAK case 15: YY_RULE_SETUP #line 182 "tptp5.l" { tptp_prev_tok=LBRKT; yylval.ival = tptp_update_lval(yytext); return(LBRKT); } YY_BREAK case 16: YY_RULE_SETUP #line 187 "tptp5.l" { tptp_prev_tok=LESS_EQUALS; yylval.ival = tptp_update_lval(yytext); return(LESS_EQUALS); } YY_BREAK case 17: YY_RULE_SETUP #line 192 "tptp5.l" { tptp_prev_tok=LESS_EQUALS_GREATER; yylval.ival = tptp_update_lval(yytext); return(LESS_EQUALS_GREATER); } YY_BREAK case 18: YY_RULE_SETUP #line 197 "tptp5.l" { tptp_prev_tok=LESS_TILDE_GREATER; yylval.ival = tptp_update_lval(yytext); return(LESS_TILDE_GREATER); } YY_BREAK case 19: YY_RULE_SETUP #line 202 "tptp5.l" { tptp_prev_tok=LPAREN; yylval.ival = tptp_update_lval(yytext); return(LPAREN); } YY_BREAK case 20: YY_RULE_SETUP #line 207 "tptp5.l" { tptp_prev_tok=MINUS; yylval.ival = tptp_update_lval(yytext); return(MINUS); } YY_BREAK case 21: YY_RULE_SETUP #line 212 "tptp5.l" { tptp_prev_tok=MINUS_MINUS_GREATER; yylval.ival = tptp_update_lval(yytext); return(MINUS_MINUS_GREATER); } YY_BREAK case 22: YY_RULE_SETUP #line 217 "tptp5.l" { BEGIN INITIAL; tptp_prev_tok=PERIOD; yylval.ival = tptp_update_lval(yytext); return(PERIOD); } YY_BREAK case 23: YY_RULE_SETUP #line 223 "tptp5.l" { tptp_prev_tok=QUESTION; yylval.ival = tptp_update_lval(yytext); return(QUESTION); } YY_BREAK case 24: YY_RULE_SETUP #line 228 "tptp5.l" { tptp_prev_tok=QUESTION_QUESTION; yylval.ival = tptp_update_lval(yytext); return(QUESTION_QUESTION); } YY_BREAK case 25: YY_RULE_SETUP #line 233 "tptp5.l" { tptp_prev_tok=QUESTION_STAR; yylval.ival = tptp_update_lval(yytext); return(QUESTION_STAR); } YY_BREAK case 26: YY_RULE_SETUP #line 238 "tptp5.l" { tptp_prev_tok=RBRKT; yylval.ival = tptp_update_lval(yytext); return(RBRKT); } YY_BREAK case 27: YY_RULE_SETUP #line 243 "tptp5.l" { tptp_prev_tok=RPAREN; yylval.ival = tptp_update_lval(yytext); return(RPAREN); } YY_BREAK case 28: YY_RULE_SETUP #line 248 "tptp5.l" { tptp_prev_tok=STAR; yylval.ival = tptp_update_lval(yytext); return(STAR); } YY_BREAK case 29: YY_RULE_SETUP #line 253 "tptp5.l" { tptp_prev_tok=TILDE; yylval.ival = tptp_update_lval(yytext); return(TILDE); } YY_BREAK case 30: YY_RULE_SETUP #line 258 "tptp5.l" { tptp_prev_tok=TILDE_AMPERSAND; yylval.ival = tptp_update_lval(yytext); return(TILDE_AMPERSAND); } YY_BREAK case 31: YY_RULE_SETUP #line 263 "tptp5.l" { tptp_prev_tok=TILDE_VLINE; yylval.ival = tptp_update_lval(yytext); return(TILDE_VLINE); } YY_BREAK case 32: YY_RULE_SETUP #line 268 "tptp5.l" { tptp_prev_tok=VLINE; yylval.ival = tptp_update_lval(yytext); return(VLINE); } YY_BREAK case 33: YY_RULE_SETUP #line 273 "tptp5.l" { tptp_prev_tok=_DLR_cnf; yylval.ival = tptp_update_lval(yytext); return(_DLR_cnf); } YY_BREAK case 34: YY_RULE_SETUP #line 278 "tptp5.l" { tptp_prev_tok=_DLR_fof; yylval.ival = tptp_update_lval(yytext); return(_DLR_fof); } YY_BREAK case 35: YY_RULE_SETUP #line 283 "tptp5.l" { tptp_prev_tok=_DLR_fot; yylval.ival = tptp_update_lval(yytext); return(_DLR_fot); } YY_BREAK case 36: YY_RULE_SETUP #line 288 "tptp5.l" { tptp_prev_tok=_DLR_itef; yylval.ival = tptp_update_lval(yytext); return(_DLR_itef); } YY_BREAK case 37: YY_RULE_SETUP #line 293 "tptp5.l" { tptp_prev_tok=_DLR_itetf; yylval.ival = tptp_update_lval(yytext); return(_DLR_itetf); } YY_BREAK case 38: YY_RULE_SETUP #line 298 "tptp5.l" { tptp_prev_tok=_DLR_itett; yylval.ival = tptp_update_lval(yytext); return(_DLR_itett); } YY_BREAK case 39: YY_RULE_SETUP #line 303 "tptp5.l" { tptp_prev_tok=_DLR_tff; yylval.ival = tptp_update_lval(yytext); return(_DLR_tff); } YY_BREAK case 40: YY_RULE_SETUP #line 308 "tptp5.l" { tptp_prev_tok=_DLR_thf; yylval.ival = tptp_update_lval(yytext); return(_DLR_thf); } YY_BREAK case 41: YY_RULE_SETUP #line 313 "tptp5.l" { BEGIN B; tptp_prev_tok=_LIT_cnf; yylval.ival = tptp_update_lval(yytext); return(_LIT_cnf); } YY_BREAK case 42: YY_RULE_SETUP #line 319 "tptp5.l" { BEGIN B; tptp_prev_tok=_LIT_fof; yylval.ival = tptp_update_lval(yytext); return(_LIT_fof); } YY_BREAK case 43: YY_RULE_SETUP #line 325 "tptp5.l" { BEGIN B; tptp_prev_tok=_LIT_include; yylval.ival = tptp_update_lval(yytext); return(_LIT_include); } YY_BREAK case 44: YY_RULE_SETUP #line 331 "tptp5.l" { BEGIN B; tptp_prev_tok=_LIT_tff; yylval.ival = tptp_update_lval(yytext); return(_LIT_tff); } YY_BREAK case 45: YY_RULE_SETUP #line 337 "tptp5.l" { BEGIN B; tptp_prev_tok=_LIT_thf; yylval.ival = tptp_update_lval(yytext); return(_LIT_thf); } YY_BREAK case 46: YY_RULE_SETUP #line 344 "tptp5.l" { tptp_prev_tok=single_quoted; yylval.ival = tptp_update_lval(yytext); return(single_quoted); } YY_BREAK case 47: YY_RULE_SETUP #line 349 "tptp5.l" { tptp_prev_tok=distinct_object; yylval.ival = tptp_update_lval(yytext); return(distinct_object); } YY_BREAK case 48: YY_RULE_SETUP #line 354 "tptp5.l" { tptp_prev_tok=dollar_word; yylval.ival = tptp_update_lval(yytext); return(dollar_word); } YY_BREAK case 49: YY_RULE_SETUP #line 359 "tptp5.l" { tptp_prev_tok=dollar_dollar_word; yylval.ival = tptp_update_lval(yytext); return(dollar_dollar_word); } YY_BREAK case 50: YY_RULE_SETUP #line 364 "tptp5.l" { tptp_prev_tok=upper_word; yylval.ival = tptp_update_lval(yytext); return(upper_word); } YY_BREAK case 51: YY_RULE_SETUP #line 369 "tptp5.l" { tptp_prev_tok=lower_word; yylval.ival = tptp_update_lval(yytext); return(lower_word); } YY_BREAK case 52: YY_RULE_SETUP #line 374 "tptp5.l" { tptp_prev_tok=vline; yylval.ival = tptp_update_lval(yytext); return(vline); } YY_BREAK case 53: YY_RULE_SETUP #line 379 "tptp5.l" { tptp_prev_tok=star; yylval.ival = tptp_update_lval(yytext); return(star); } YY_BREAK case 54: YY_RULE_SETUP #line 384 "tptp5.l" { tptp_prev_tok=plus; yylval.ival = tptp_update_lval(yytext); return(plus); } YY_BREAK case 55: YY_RULE_SETUP #line 389 "tptp5.l" { tptp_prev_tok=arrow; yylval.ival = tptp_update_lval(yytext); return(arrow); } YY_BREAK case 56: YY_RULE_SETUP #line 394 "tptp5.l" { tptp_prev_tok=less_sign; yylval.ival = tptp_update_lval(yytext); return(less_sign); } YY_BREAK case 57: YY_RULE_SETUP #line 399 "tptp5.l" { tptp_prev_tok=real; yylval.ival = tptp_update_lval(yytext); return(real); } YY_BREAK case 58: YY_RULE_SETUP #line 404 "tptp5.l" { tptp_prev_tok=signed_real; yylval.ival = tptp_update_lval(yytext); return(signed_real); } YY_BREAK case 59: YY_RULE_SETUP #line 409 "tptp5.l" { tptp_prev_tok=unsigned_real; yylval.ival = tptp_update_lval(yytext); return(unsigned_real); } YY_BREAK case 60: YY_RULE_SETUP #line 414 "tptp5.l" { tptp_prev_tok=rational; yylval.ival = tptp_update_lval(yytext); return(rational); } YY_BREAK case 61: YY_RULE_SETUP #line 419 "tptp5.l" { tptp_prev_tok=signed_rational; yylval.ival = tptp_update_lval(yytext); return(signed_rational); } YY_BREAK case 62: YY_RULE_SETUP #line 424 "tptp5.l" { tptp_prev_tok=unsigned_rational; yylval.ival = tptp_update_lval(yytext); return(unsigned_rational); } YY_BREAK case 63: YY_RULE_SETUP #line 429 "tptp5.l" { tptp_prev_tok=integer; yylval.ival = tptp_update_lval(yytext); return(integer); } YY_BREAK case 64: YY_RULE_SETUP #line 434 "tptp5.l" { tptp_prev_tok=signed_integer; yylval.ival = tptp_update_lval(yytext); return(signed_integer); } YY_BREAK case 65: YY_RULE_SETUP #line 439 "tptp5.l" { tptp_prev_tok=unsigned_integer; yylval.ival = tptp_update_lval(yytext); return(unsigned_integer); } YY_BREAK case 66: YY_RULE_SETUP #line 444 "tptp5.l" { tptp_prev_tok=decimal; yylval.ival = tptp_update_lval(yytext); return(decimal); } YY_BREAK case 67: YY_RULE_SETUP #line 449 "tptp5.l" { tptp_prev_tok=positive_decimal; yylval.ival = tptp_update_lval(yytext); return(positive_decimal); } YY_BREAK case 68: YY_RULE_SETUP #line 454 "tptp5.l" { tptp_prev_tok=decimal_exponent; yylval.ival = tptp_update_lval(yytext); return(decimal_exponent); } YY_BREAK case 69: YY_RULE_SETUP #line 459 "tptp5.l" { tptp_prev_tok=decimal_fraction; yylval.ival = tptp_update_lval(yytext); return(decimal_fraction); } YY_BREAK case 70: YY_RULE_SETUP #line 464 "tptp5.l" { tptp_prev_tok=dot_decimal; yylval.ival = tptp_update_lval(yytext); return(dot_decimal); } YY_BREAK case 71: /* rule 71 can match eol */ YY_RULE_SETUP #line 469 "tptp5.l" tptp_update_lval(yytext); YY_BREAK case 72: /* rule 72 can match eol */ YY_RULE_SETUP #line 470 "tptp5.l" ; YY_BREAK case 73: /* rule 73 can match eol */ YY_RULE_SETUP #line 471 "tptp5.l" ; YY_BREAK case 74: YY_RULE_SETUP #line 472 "tptp5.l" return(unrecognized); YY_BREAK case 75: YY_RULE_SETUP #line 473 "tptp5.l" ECHO; YY_BREAK #line 1667 "tptp5.lex.cpp" case YY_STATE_EOF(INITIAL): case YY_STATE_EOF(B): case YY_STATE_EOF(FF): case YY_STATE_EOF(SQ1): case YY_STATE_EOF(SQ2): case YY_STATE_EOF(Q1): case YY_STATE_EOF(Q2): yyterminate(); case YY_END_OF_BUFFER: { /* Amount of text matched not including the EOB char. */ int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1; /* Undo the effects of YY_DO_BEFORE_ACTION. */ *yy_cp = (yy_hold_char); YY_RESTORE_YY_MORE_OFFSET if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) { /* We're scanning a new file or input source. It's * possible that this happened because the user * just pointed yyin at a new source and called * yylex(). If so, then we have to assure * consistency between YY_CURRENT_BUFFER and our * globals. Here is the right place to do so, because * this is the first action (other than possibly a * back-up) that will match for the new input source. */ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; } /* Note that here we test for yy_c_buf_p "<=" to the position * of the first EOB in the buffer, since yy_c_buf_p will * already have been incremented past the NUL character * (since all states make transitions on EOB to the * end-of-buffer state). Contrast this with the test * in input(). */ if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) { /* This was really a NUL. */ yy_state_type yy_next_state; (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( ); /* Okay, we're now positioned to make the NUL * transition. We couldn't have * yy_get_previous_state() go ahead and do it * for us because it doesn't know how to deal * with the possibility of jamming (and we don't * want to build jamming into it because then it * will run more slowly). */ yy_next_state = yy_try_NUL_trans( yy_current_state ); yy_bp = (yytext_ptr) + YY_MORE_ADJ; if ( yy_next_state ) { /* Consume the NUL. */ yy_cp = ++(yy_c_buf_p); yy_current_state = yy_next_state; goto yy_match; } else { yy_cp = (yy_c_buf_p); goto yy_find_action; } } else switch ( yy_get_next_buffer( ) ) { case EOB_ACT_END_OF_FILE: { (yy_did_buffer_switch_on_eof) = 0; if ( yywrap( ) ) { /* Note: because we've taken care in * yy_get_next_buffer() to have set up * yytext, we can now set up * yy_c_buf_p so that if some total * hoser (like flex itself) wants to * call the scanner after we return the * YY_NULL, it'll still work - another * YY_NULL will get returned. */ (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ; yy_act = YY_STATE_EOF(YY_START); goto do_action; } else { if ( ! (yy_did_buffer_switch_on_eof) ) YY_NEW_FILE; } break; } case EOB_ACT_CONTINUE_SCAN: (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( ); yy_cp = (yy_c_buf_p); yy_bp = (yytext_ptr) + YY_MORE_ADJ; goto yy_match; case EOB_ACT_LAST_MATCH: (yy_c_buf_p) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)]; yy_current_state = yy_get_previous_state( ); yy_cp = (yy_c_buf_p); yy_bp = (yytext_ptr) + YY_MORE_ADJ; goto yy_find_action; } break; } default: YY_FATAL_ERROR( "fatal flex scanner internal error--no action found" ); } /* end of action switch */ } /* end of scanning one token */ } /* end of yylex */ /* yy_get_next_buffer - try to read in a new buffer * * Returns a code representing an action: * EOB_ACT_LAST_MATCH - * EOB_ACT_CONTINUE_SCAN - continue scanning from current position * EOB_ACT_END_OF_FILE - end of file */ static int yy_get_next_buffer (void) { register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; register char *source = (yytext_ptr); register int number_to_move, i; int ret_val; if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] ) YY_FATAL_ERROR( "fatal flex scanner internal error--end of buffer missed" ); if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) { /* Don't try to fill the buffer, so this is an EOF. */ if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 ) { /* We matched a single character, the EOB, so * treat this as a final EOF. */ return EOB_ACT_END_OF_FILE; } else { /* We matched some text prior to the EOB, first * process it. */ return EOB_ACT_LAST_MATCH; } } /* Try to read more data. */ /* First move last chars to start of buffer. */ number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1; for ( i = 0; i < number_to_move; ++i ) *(dest++) = *(source++); if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) /* don't do the read, it's not guaranteed to return an EOF, * just force an EOF */ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0; else { size_t num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; while ( num_to_read <= 0 ) { /* Not enough room in the buffer - grow it. */ YY_FATAL_ERROR( "input buffer overflow, can't enlarge buffer because scanner uses REJECT" ); } if ( num_to_read > YY_READ_BUF_SIZE ) num_to_read = YY_READ_BUF_SIZE; /* Read in more data. */ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), (yy_n_chars), (int)num_to_read ); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); } if ( (yy_n_chars) == 0 ) { if ( number_to_move == YY_MORE_ADJ ) { ret_val = EOB_ACT_END_OF_FILE; yyrestart(yyin ); } else { ret_val = EOB_ACT_LAST_MATCH; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_EOF_PENDING; } } else ret_val = EOB_ACT_CONTINUE_SCAN; if ((yy_size_t) ((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { /* Extend the array by 50%, plus the number we really need. */ yy_size_t new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1); YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ); if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); } (yy_n_chars) += number_to_move; YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR; YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR; (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; return ret_val; } /* yy_get_previous_state - get the state just before the EOB char was reached */ static yy_state_type yy_get_previous_state (void) { register yy_state_type yy_current_state; register char *yy_cp; yy_current_state = (yy_start); (yy_state_ptr) = (yy_state_buf); *(yy_state_ptr)++ = yy_current_state; for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp ) { register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 152 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; *(yy_state_ptr)++ = yy_current_state; } return yy_current_state; } /* yy_try_NUL_trans - try to make a transition on the NUL character * * synopsis * next_state = yy_try_NUL_trans( current_state ); */ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state ) { register int yy_is_jam; register YY_CHAR yy_c = 1; while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 152 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; yy_is_jam = (yy_current_state == 151); if ( ! yy_is_jam ) *(yy_state_ptr)++ = yy_current_state; return yy_is_jam ? 0 : yy_current_state; } static void yyunput (int c, register char * yy_bp ) { register char *yy_cp; yy_cp = (yy_c_buf_p); /* undo effects of setting up yytext */ *yy_cp = (yy_hold_char); if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) { /* need to shift things up to make room */ /* +2 for EOB chars. */ register int number_to_move = (yy_n_chars) + 2; register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[ YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2]; register char *source = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]; while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) *--dest = *--source; yy_cp += (int) (dest - source); yy_bp += (int) (dest - source); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = (int)YY_CURRENT_BUFFER_LVALUE->yy_buf_size; if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) YY_FATAL_ERROR( "flex scanner push-back overflow" ); } *--yy_cp = (char) c; if ( c == '\n' ){ --yylineno; } (yytext_ptr) = yy_bp; (yy_hold_char) = *yy_cp; (yy_c_buf_p) = yy_cp; } #ifndef YY_NO_INPUT #ifdef __cplusplus static int yyinput (void) #else static int input (void) #endif { int c; *(yy_c_buf_p) = (yy_hold_char); if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR ) { /* yy_c_buf_p now points to the character we want to return. * If this occurs *before* the EOB characters, then it's a * valid NUL; if not, then we've hit the end of the buffer. */ if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) /* This was really a NUL. */ *(yy_c_buf_p) = '\0'; else { /* need more input */ size_t offset = (yy_c_buf_p) - (yytext_ptr); ++(yy_c_buf_p); switch ( yy_get_next_buffer( ) ) { case EOB_ACT_LAST_MATCH: /* This happens because yy_g_n_b() * sees that we've accumulated a * token and flags that we need to * try matching the token before * proceeding. But for input(), * there's no matching to consider. * So convert the EOB_ACT_LAST_MATCH * to EOB_ACT_END_OF_FILE. */ /* Reset buffer status. */ yyrestart(yyin ); /*FALLTHROUGH*/ case EOB_ACT_END_OF_FILE: { if ( yywrap( ) ) return EOF; if ( ! (yy_did_buffer_switch_on_eof) ) YY_NEW_FILE; #ifdef __cplusplus return yyinput(); #else return input(); #endif } case EOB_ACT_CONTINUE_SCAN: (yy_c_buf_p) = (yytext_ptr) + offset; break; } } } c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */ *(yy_c_buf_p) = '\0'; /* preserve yytext */ (yy_hold_char) = *++(yy_c_buf_p); if ( c == '\n' ) yylineno++; ; return c; } #endif /* ifndef YY_NO_INPUT */ /** Immediately switch to a different input stream. * @param input_file A readable stream. * * @note This function does not reset the start condition to @c INITIAL . */ void yyrestart (FILE * input_file ) { if ( ! YY_CURRENT_BUFFER ){ yyensure_buffer_stack (); YY_CURRENT_BUFFER_LVALUE = yy_create_buffer(yyin,YY_BUF_SIZE ); } yy_init_buffer(YY_CURRENT_BUFFER,input_file ); yy_load_buffer_state( ); } /** Switch to a different input buffer. * @param new_buffer The new input buffer. * */ void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ) { /* TODO. We should be able to replace this entire function body * with * yypop_buffer_state(); * yypush_buffer_state(new_buffer); */ yyensure_buffer_stack (); if ( YY_CURRENT_BUFFER == new_buffer ) return; if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *(yy_c_buf_p) = (yy_hold_char); YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); } YY_CURRENT_BUFFER_LVALUE = new_buffer; yy_load_buffer_state( ); /* We don't actually know whether we did this switch during * EOF (yywrap()) processing, but the only time this flag * is looked at is after yywrap() is called, so it's safe * to go ahead and always set it. */ (yy_did_buffer_switch_on_eof) = 1; } static void yy_load_buffer_state (void) { (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; (yy_hold_char) = *(yy_c_buf_p); } /** Allocate and initialize an input buffer state. * @param file A readable stream. * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. * * @return the allocated buffer state. */ YY_BUFFER_STATE yy_create_buffer (FILE * file, int size ) { YY_BUFFER_STATE b; b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); b->yy_buf_size = size; /* yy_ch_buf has to be 2 characters longer than the size given because * we need to put in 2 end-of-buffer characters. */ b->yy_ch_buf = (char *) yyalloc(b->yy_buf_size + 2 ); if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); b->yy_is_our_buffer = 1; yy_init_buffer(b,file ); return b; } /** Destroy the buffer. * @param b a buffer created with yy_create_buffer() * */ void yy_delete_buffer (YY_BUFFER_STATE b ) { if ( ! b ) return; if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; if ( b->yy_is_our_buffer ) yyfree((void *) b->yy_ch_buf ); yyfree((void *) b ); } #ifndef __cplusplus extern int isatty (int ); #endif /* __cplusplus */ /* Initializes or reinitializes a buffer. * This function is sometimes called more than once on the same buffer, * such as during a yyrestart() or at EOF. */ static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file ) { int oerrno = errno; yy_flush_buffer(b ); b->yy_input_file = file; b->yy_fill_buffer = 1; /* If b is the current buffer, then yy_init_buffer was _probably_ * called from yyrestart() or through yy_get_next_buffer. * In that case, we don't want to reset the lineno or column. */ if (b != YY_CURRENT_BUFFER){ b->yy_bs_lineno = 1; b->yy_bs_column = 0; } #ifdef _WINDOWS b->yy_is_interactive = file ? (isatty( _fileno(file) ) > 0) : 0; #else b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; #endif errno = oerrno; } /** Discard all buffered characters. On the next scan, YY_INPUT will be called. * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. * */ void yy_flush_buffer (YY_BUFFER_STATE b ) { if ( ! b ) return; b->yy_n_chars = 0; /* We always need two end-of-buffer characters. The first causes * a transition to the end-of-buffer state. The second causes * a jam in that state. */ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; b->yy_buf_pos = &b->yy_ch_buf[0]; b->yy_at_bol = 1; b->yy_buffer_status = YY_BUFFER_NEW; if ( b == YY_CURRENT_BUFFER ) yy_load_buffer_state( ); } /** Pushes the new state onto the stack. The new state becomes * the current state. This function will allocate the stack * if necessary. * @param new_buffer The new state. * */ void yypush_buffer_state (YY_BUFFER_STATE new_buffer ) { if (new_buffer == NULL) return; yyensure_buffer_stack(); /* This block is copied from yy_switch_to_buffer. */ if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *(yy_c_buf_p) = (yy_hold_char); YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); } /* Only push if top exists. Otherwise, replace top. */ if (YY_CURRENT_BUFFER) (yy_buffer_stack_top)++; YY_CURRENT_BUFFER_LVALUE = new_buffer; /* copied from yy_switch_to_buffer. */ yy_load_buffer_state( ); (yy_did_buffer_switch_on_eof) = 1; } /** Removes and deletes the top of the stack, if present. * The next element becomes the new top. * */ void yypop_buffer_state (void) { if (!YY_CURRENT_BUFFER) return; yy_delete_buffer(YY_CURRENT_BUFFER ); YY_CURRENT_BUFFER_LVALUE = NULL; if ((yy_buffer_stack_top) > 0) --(yy_buffer_stack_top); if (YY_CURRENT_BUFFER) { yy_load_buffer_state( ); (yy_did_buffer_switch_on_eof) = 1; } } /* Allocates the stack if it does not exist. * Guarantees space for at least one push. */ static void yyensure_buffer_stack (void) { size_t num_to_alloc; if (!(yy_buffer_stack)) { /* First allocation is just for 2 elements, since we don't know if this * scanner will even need a stack. We use 2 instead of 1 to avoid an * immediate realloc on the next call. */ num_to_alloc = 1; (yy_buffer_stack) = (struct yy_buffer_state**)yyalloc (num_to_alloc * sizeof(struct yy_buffer_state*) ); if ( ! (yy_buffer_stack) ) YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*)); (yy_buffer_stack_max) = num_to_alloc; (yy_buffer_stack_top) = 0; return; } if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){ /* Increase the buffer to prepare for a possible push. */ int grow_size = 8 /* arbitrary grow size */; num_to_alloc = (yy_buffer_stack_max) + grow_size; (yy_buffer_stack) = (struct yy_buffer_state**)yyrealloc ((yy_buffer_stack), num_to_alloc * sizeof(struct yy_buffer_state*) ); if ( ! (yy_buffer_stack) ) YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); /* zero only the new slots.*/ memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*)); (yy_buffer_stack_max) = num_to_alloc; } } /** Setup the input buffer state to scan directly from a user-specified character buffer. * @param base the character buffer * @param size the size in bytes of the character buffer * * @return the newly allocated buffer state object. */ YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size ) { YY_BUFFER_STATE b; if ( size < 2 || base[size-2] != YY_END_OF_BUFFER_CHAR || base[size-1] != YY_END_OF_BUFFER_CHAR ) /* They forgot to leave room for the EOB's. */ return 0; b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ b->yy_buf_pos = b->yy_ch_buf = base; b->yy_is_our_buffer = 0; b->yy_input_file = 0; b->yy_n_chars = (int)b->yy_buf_size; b->yy_is_interactive = 0; b->yy_at_bol = 1; b->yy_fill_buffer = 0; b->yy_buffer_status = YY_BUFFER_NEW; yy_switch_to_buffer(b ); return b; } /** Setup the input buffer state to scan a string. The next call to yylex() will * scan from a @e copy of @a str. * @param yystr a NUL-terminated string to scan * * @return the newly allocated buffer state object. * @note If you want to scan bytes that may contain NUL values, then use * yy_scan_bytes() instead. */ YY_BUFFER_STATE yy_scan_string (yyconst char * yystr ) { return yy_scan_bytes(yystr,(int)strlen(yystr) ); } /** Setup the input buffer state to scan the given bytes. The next call to yylex() will * scan from a @e copy of @a bytes. * @param bytes the byte buffer to scan * @param len the number of bytes in the buffer pointed to by @a bytes. * * @return the newly allocated buffer state object. */ YY_BUFFER_STATE yy_scan_bytes (yyconst char * yybytes, int _yybytes_len ) { YY_BUFFER_STATE b; char *buf; yy_size_t n; int i; /* Get memory for full buffer, including space for trailing EOB's. */ n = _yybytes_len + 2; buf = (char *) yyalloc(n ); if ( ! buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); for ( i = 0; i < _yybytes_len; ++i ) buf[i] = yybytes[i]; buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; b = yy_scan_buffer(buf,n ); if ( ! b ) YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); /* It's okay to grow etc. this buffer, and we should throw it * away when we're done. */ b->yy_is_our_buffer = 1; return b; } #ifndef YY_EXIT_FAILURE #define YY_EXIT_FAILURE 2 #endif static void yy_fatal_error (yyconst char* msg ) { (void) fprintf( stderr, "%s\n", msg ); exit( YY_EXIT_FAILURE ); } /* Redefine yyless() so it works in section 3 code. */ #undef yyless #define yyless(n) \ do \ { \ /* Undo effects of setting up yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ yytext[yyleng] = (yy_hold_char); \ (yy_c_buf_p) = yytext + yyless_macro_arg; \ (yy_hold_char) = *(yy_c_buf_p); \ *(yy_c_buf_p) = '\0'; \ yyleng = yyless_macro_arg; \ } \ while ( 0 ) /* Accessor methods (get/set functions) to struct members. */ /** Get the current line number. * */ int yyget_lineno (void) { return yylineno; } /** Get the input stream. * */ FILE *yyget_in (void) { return yyin; } /** Get the output stream. * */ FILE *yyget_out (void) { return yyout; } /** Get the length of the current token. * */ int yyget_leng (void) { return yyleng; } /** Get the current token. * */ char *yyget_text (void) { return yytext; } /** Set the current line number. * @param line_number * */ void yyset_lineno (int line_number ) { yylineno = line_number; } /** Set the input stream. This does not discard the current * input buffer. * @param in_str A readable stream. * * @see yy_switch_to_buffer */ void yyset_in (FILE * in_str ) { yyin = in_str ; } void yyset_out (FILE * out_str ) { yyout = out_str ; } int yyget_debug (void) { return yy_flex_debug; } void yyset_debug (int bdebug ) { yy_flex_debug = bdebug ; } static int yy_init_globals (void) { /* Initialization is the same as for the non-reentrant scanner. * This function is called from yylex_destroy(), so don't allocate here. */ /* We do not touch yylineno unless the option is enabled. */ yylineno = 1; (yy_buffer_stack) = 0; (yy_buffer_stack_top) = 0; (yy_buffer_stack_max) = 0; (yy_c_buf_p) = (char *) 0; (yy_init) = 0; (yy_start) = 0; (yy_state_buf) = 0; (yy_state_ptr) = 0; (yy_full_match) = 0; (yy_lp) = 0; /* Defined in main.c */ #ifdef YY_STDINIT yyin = stdin; yyout = stdout; #else yyin = (FILE *) 0; yyout = (FILE *) 0; #endif /* For future reference: Set errno on error, since we are called by * yylex_init() */ return 0; } /* yylex_destroy is for both reentrant and non-reentrant scanners. */ int yylex_destroy (void) { /* Pop the buffer stack, destroying each element. */ while(YY_CURRENT_BUFFER){ yy_delete_buffer(YY_CURRENT_BUFFER ); YY_CURRENT_BUFFER_LVALUE = NULL; yypop_buffer_state(); } /* Destroy the stack itself. */ yyfree((yy_buffer_stack) ); (yy_buffer_stack) = NULL; yyfree ( (yy_state_buf) ); (yy_state_buf) = NULL; /* Reset the globals. This is important in a non-reentrant scanner so the next time * yylex() is called, initialization will occur. */ yy_init_globals( ); return 0; } /* * Internal utility routines. */ #ifndef yytext_ptr static void yy_flex_strncpy (char* s1, yyconst char * s2, int n ) { register int i; for ( i = 0; i < n; ++i ) s1[i] = s2[i]; } #endif #ifdef YY_NEED_STRLEN static int yy_flex_strlen (yyconst char * s ) { register int n; for ( n = 0; s[n]; ++n ) ; return n; } #endif void *yyalloc (yy_size_t size ) { return (void *) malloc( size ); } void *yyrealloc (void * ptr, yy_size_t size ) { /* The cast to (char *) in the following accommodates both * implementations that use char* generic pointers, and those * that use void* generic pointers. It works with the latter * because both ANSI C and C++ allow castless assignment from * any pointer type to void*, and deal with argument conversions * as though doing an assignment. */ return (void *) realloc( (char *) ptr, size ); } void yyfree (void * ptr ) { free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ } #define YYTABLES_NAME "yytables" #line 473 "tptp5.l" z3-z3-4.8.7/examples/tptp/tptp5.tab.c000066400000000000000000005035031356505360400172270ustar00rootroot00000000000000/* A Bison parser, made by GNU Bison 2.4.2. */ /* Skeleton implementation for Bison's Yacc-like parsers in C Copyright (C) 1984, 1989-1990, 2000-2006, 2009-2010 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work under terms of your choice, so long as that work isn't itself a parser generator using the skeleton or a modified version thereof as a parser skeleton. Alternatively, if you modify or redistribute the parser skeleton itself, you may (at your option) remove this special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ /* C LALR(1) parser skeleton written by Richard Stallman, by simplifying the original so-called "semantic" parser. */ /* All symbols defined below should begin with yy or YY, to avoid infringing on user name space. This should be done even for local variables, as they might otherwise be expanded by user macros. There are some unavoidable exceptions within include files to define necessary library symbols; they are noted "INFRINGES ON USER NAME SPACE" below. */ /* Identify Bison output. */ #define YYBISON 1 /* Bison version. */ #define YYBISON_VERSION "2.4.2" /* Skeleton name. */ #define YYSKELETON_NAME "yacc.c" /* Pure parsers. */ #define YYPURE 0 /* Push parsers. */ #define YYPUSH 0 /* Pull parsers. */ #define YYPULL 1 /* Using locations. */ #define YYLSP_NEEDED 0 /* Copy the first part of user declarations. */ /* Line 189 of yacc.c */ #line 2 "tptp5.y" //----------------------------------------------------------------------------- #include #include #include //----------------------------------------------------------------------------- //----Compile with -DP_VERBOSE=1 for verbose output. #ifndef P_VERBOSE # define P_VERBOSE 0 #endif int verbose = P_VERBOSE; //----Compile with -DP_USERPROC=1 to #include p_user_proc.c. p_user_proc.c //----should #define P_ACT, P_BUILD, P_TOKEN, P_PRINT to different procedures //----from those below, and supply code. #ifdef P_USERPROC #else # define P_ACT(ss) if(verbose)printf("%7d %s\n",yylineno,ss); # define P_BUILD(sym,A,B,C,D,E,F,G,H,I,J) pBuildTree(sym,A,B,C,D,E,F,G,H,I,J) # define P_TOKEN(tok,symbolIndex) pToken(tok,symbolIndex) # define P_PRINT(ss) if(verbose){printf("\n\n");pPrintTree(ss,0);} #endif extern int yylineno; extern int yychar; extern char yytext[]; extern int tptp_store_size; extern char* tptp_lval[]; #define MAX_CHILDREN 12 typedef struct pTreeNode * pTree; struct pTreeNode { char* symbol; int symbolIndex; pTree children[MAX_CHILDREN+1]; }; //----------------------------------------------------------------------------- int yyerror( char const *s ) { fprintf( stderr, "%s in line %d at item \"%s\".\n", s, yylineno, yytext); return 0; } //----------------------------------------------------------------------------- pTree pBuildTree(char* symbol,pTree A,pTree B,pTree C,pTree D,pTree E,pTree F, pTree G, pTree H, pTree I, pTree J) { pTree ss = (pTree)calloc(1,sizeof(struct pTreeNode)); ss->symbol = symbol; ss->symbolIndex = -1; ss->children[0] = A; ss->children[1] = B; ss->children[2] = C; ss->children[3] = D; ss->children[4] = E; ss->children[5] = F; ss->children[6] = G; ss->children[7] = H; ss->children[8] = I; ss->children[9] = J; ss->children[10] = NULL; return ss; } //----------------------------------------------------------------------------- pTree pToken(char* token, int symbolIndex) { //char pTokenBuf[8240]; pTree ss; //char* symbol = tptp_lval[symbolIndex]; char* safeSym = 0; //strncpy(pTokenBuf, token, 39); //strncat(pTokenBuf, symbol, 8193); //safeSym = strdup(pTokenBuf); ss = pBuildTree(safeSym,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL); ss->symbolIndex = symbolIndex; return ss; } //----------------------------------------------------------------------------- void pPrintComments(int start, int depth) { int d, j; char c1[4] = "%", c2[4] = "/*"; j = start; while (tptp_lval[j] != NULL && (tptp_lval[j][0]==c1[0] || (tptp_lval[j][0]==c2[0] && tptp_lval[j][1]==c2[1]))) { for (d=0; d= 0) { pPrintComments(pPrintIdx, 0); pPrintIdx = -1; } if (ss == NULL) { return; } for (d = 0; d < depth-1; d++) { printf("| "); } printf("%1d ",depth % 10); if (ss->children[0] == NULL) { printf("%s\n", ss->symbol); } else { printf("<%s>\n", ss->symbol); } if (strcmp(ss->symbol, "PERIOD .") == 0) { pPrintIdx = (ss->symbolIndex+1) % tptp_store_size; } if (ss->symbolIndex >= 0) { pPrintComments((ss->symbolIndex+1) % tptp_store_size, depth); } i = 0; while(ss->children[i] != NULL) { pPrintTree(ss->children[i],depth+1); i++; } return; } //----------------------------------------------------------------------------- int yywrap(void) { P_PRINT(NULL); return 1; } //----------------------------------------------------------------------------- /* Line 189 of yacc.c */ #line 219 "tptp5.tab.c" /* Enabling traces. */ #ifndef YYDEBUG # define YYDEBUG 0 #endif /* Enabling verbose error messages. */ #ifdef YYERROR_VERBOSE # undef YYERROR_VERBOSE # define YYERROR_VERBOSE 1 #else # define YYERROR_VERBOSE 0 #endif /* Enabling the token table. */ #ifndef YYTOKEN_TABLE # define YYTOKEN_TABLE 0 #endif /* Tokens. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE /* Put the tokens into the symbol table, so that GDB and other debuggers know about them. */ enum yytokentype { AMPERSAND = 258, AT_SIGN = 259, AT_SIGN_MINUS = 260, AT_SIGN_PLUS = 261, CARET = 262, COLON = 263, COLON_EQUALS = 264, COMMA = 265, EQUALS = 266, EQUALS_GREATER = 267, EXCLAMATION = 268, EXCLAMATION_EQUALS = 269, EXCLAMATION_EXCLAMATION = 270, EXCLAMATION_GREATER = 271, LBRKT = 272, LESS_EQUALS = 273, LESS_EQUALS_GREATER = 274, LESS_TILDE_GREATER = 275, LPAREN = 276, MINUS = 277, MINUS_MINUS_GREATER = 278, PERIOD = 279, QUESTION = 280, QUESTION_QUESTION = 281, QUESTION_STAR = 282, RBRKT = 283, RPAREN = 284, STAR = 285, TILDE = 286, TILDE_AMPERSAND = 287, TILDE_VLINE = 288, VLINE = 289, _DLR_cnf = 290, _DLR_fof = 291, _DLR_fot = 292, _DLR_itef = 293, _DLR_itetf = 294, _DLR_itett = 295, _DLR_tff = 296, _DLR_thf = 297, _LIT_cnf = 298, _LIT_fof = 299, _LIT_include = 300, _LIT_tff = 301, _LIT_thf = 302, arrow = 303, comment = 304, comment_line = 305, decimal = 306, decimal_exponent = 307, decimal_fraction = 308, distinct_object = 309, dollar_dollar_word = 310, dollar_word = 311, dot_decimal = 312, integer = 313, less_sign = 314, lower_word = 315, plus = 316, positive_decimal = 317, rational = 318, real = 319, signed_integer = 320, signed_rational = 321, signed_real = 322, single_quoted = 323, star = 324, unrecognized = 325, unsigned_integer = 326, unsigned_rational = 327, unsigned_real = 328, upper_word = 329, vline = 330 }; #endif #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED typedef union YYSTYPE { /* Line 214 of yacc.c */ #line 148 "tptp5.y" int ival; double dval; char* sval; TreeNode* pval; /* Line 214 of yacc.c */ #line 334 "tptp5.tab.c" } YYSTYPE; # define YYSTYPE_IS_TRIVIAL 1 # define yystype YYSTYPE /* obsolescent; will be withdrawn */ # define YYSTYPE_IS_DECLARED 1 #endif /* Copy the second part of user declarations. */ /* Line 264 of yacc.c */ #line 346 "tptp5.tab.c" #ifdef short # undef short #endif #ifdef YYTYPE_UINT8 typedef YYTYPE_UINT8 yytype_uint8; #else typedef unsigned char yytype_uint8; #endif #ifdef YYTYPE_INT8 typedef YYTYPE_INT8 yytype_int8; #elif (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) typedef signed char yytype_int8; #else typedef short int yytype_int8; #endif #ifdef YYTYPE_UINT16 typedef YYTYPE_UINT16 yytype_uint16; #else typedef unsigned short int yytype_uint16; #endif #ifdef YYTYPE_INT16 typedef YYTYPE_INT16 yytype_int16; #else typedef short int yytype_int16; #endif #ifndef YYSIZE_T # ifdef __SIZE_TYPE__ # define YYSIZE_T __SIZE_TYPE__ # elif defined size_t # define YYSIZE_T size_t # elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) # include /* INFRINGES ON USER NAME SPACE */ # define YYSIZE_T size_t # else # define YYSIZE_T unsigned int # endif #endif #define YYSIZE_MAXIMUM ((YYSIZE_T) -1) #ifndef YY_ # if defined YYENABLE_NLS && YYENABLE_NLS # if ENABLE_NLS # include /* INFRINGES ON USER NAME SPACE */ # define YY_(msgid) dgettext ("bison-runtime", msgid) # endif # endif # ifndef YY_ # define YY_(msgid) msgid # endif #endif /* Suppress unused-variable warnings by "using" E. */ #if ! defined lint || defined __GNUC__ # define YYUSE(e) ((void) (e)) #else # define YYUSE(e) /* empty */ #endif /* Identity function, used to suppress warnings about constant conditions. */ #ifndef lint # define YYID(n) (n) #else #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static int YYID (int yyi) #else static int YYID (yyi) int yyi; #endif { return yyi; } #endif #if ! defined yyoverflow || YYERROR_VERBOSE /* The parser invokes alloca or malloc; define the necessary symbols. */ # ifdef YYSTACK_USE_ALLOCA # if YYSTACK_USE_ALLOCA # ifdef __GNUC__ # define YYSTACK_ALLOC __builtin_alloca # elif defined __BUILTIN_VA_ARG_INCR # include /* INFRINGES ON USER NAME SPACE */ # elif defined _AIX # define YYSTACK_ALLOC __alloca # elif defined _MSC_VER # include /* INFRINGES ON USER NAME SPACE */ # define alloca _alloca # else # define YYSTACK_ALLOC alloca # if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) # include /* INFRINGES ON USER NAME SPACE */ # ifndef _STDLIB_H # define _STDLIB_H 1 # endif # endif # endif # endif # endif # ifdef YYSTACK_ALLOC /* Pacify GCC's `empty if-body' warning. */ # define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0)) # ifndef YYSTACK_ALLOC_MAXIMUM /* The OS might guarantee only one guard page at the bottom of the stack, and a page size can be as small as 4096 bytes. So we cannot safely invoke alloca (N) if N exceeds 4096. Use a slightly smaller number to allow for a few compiler-allocated temporary stack slots. */ # define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ # endif # else # define YYSTACK_ALLOC YYMALLOC # define YYSTACK_FREE YYFREE # ifndef YYSTACK_ALLOC_MAXIMUM # define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM # endif # if (defined __cplusplus && ! defined _STDLIB_H \ && ! ((defined YYMALLOC || defined malloc) \ && (defined YYFREE || defined free))) # include /* INFRINGES ON USER NAME SPACE */ # ifndef _STDLIB_H # define _STDLIB_H 1 # endif # endif # ifndef YYMALLOC # define YYMALLOC malloc # if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ # endif # endif # ifndef YYFREE # define YYFREE free # if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) void free (void *); /* INFRINGES ON USER NAME SPACE */ # endif # endif # endif #endif /* ! defined yyoverflow || YYERROR_VERBOSE */ #if (! defined yyoverflow \ && (! defined __cplusplus \ || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) /* A type that is properly aligned for any stack member. */ union yyalloc { yytype_int16 yyss_alloc; YYSTYPE yyvs_alloc; }; /* The size of the maximum gap between one aligned stack and the next. */ # define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) /* The size of an array large to enough to hold all stacks, each with N elements. */ # define YYSTACK_BYTES(N) \ ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ + YYSTACK_GAP_MAXIMUM) /* Copy COUNT objects from FROM to TO. The source and destination do not overlap. */ # ifndef YYCOPY # if defined __GNUC__ && 1 < __GNUC__ # define YYCOPY(To, From, Count) \ __builtin_memcpy (To, From, (Count) * sizeof (*(From))) # else # define YYCOPY(To, From, Count) \ do \ { \ YYSIZE_T yyi; \ for (yyi = 0; yyi < (Count); yyi++) \ (To)[yyi] = (From)[yyi]; \ } \ while (YYID (0)) # endif # endif /* Relocate STACK from its old location to the new one. The local variables YYSIZE and YYSTACKSIZE give the old and new number of elements in the stack, and YYPTR gives the new location of the stack. Advance YYPTR to a properly aligned location for the next stack. */ # define YYSTACK_RELOCATE(Stack_alloc, Stack) \ do \ { \ YYSIZE_T yynewbytes; \ YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ Stack = &yyptr->Stack_alloc; \ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ yyptr += yynewbytes / sizeof (*yyptr); \ } \ while (YYID (0)) #endif /* YYFINAL -- State number of the termination state. */ #define YYFINAL 3 /* YYLAST -- Last index in YYTABLE. */ #define YYLAST 1612 /* YYNTOKENS -- Number of terminals. */ #define YYNTOKENS 76 /* YYNNTS -- Number of nonterminals. */ #define YYNNTS 141 /* YYNRULES -- Number of rules. */ #define YYNRULES 281 /* YYNRULES -- Number of states. */ #define YYNSTATES 523 /* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ #define YYUNDEFTOK 2 #define YYMAXUTOK 330 #define YYTRANSLATE(YYX) \ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) /* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ static const yytype_uint8 yytranslate[] = { 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75 }; #if YYDEBUG /* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in YYRHS. */ static const yytype_uint16 yyprhs[] = { 0, 0, 3, 5, 8, 10, 12, 14, 16, 18, 20, 31, 42, 53, 64, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 94, 96, 98, 100, 104, 108, 112, 116, 120, 124, 126, 128, 130, 132, 134, 136, 140, 147, 149, 153, 155, 157, 161, 166, 170, 172, 174, 178, 182, 184, 186, 188, 190, 192, 196, 200, 204, 208, 212, 216, 218, 220, 223, 227, 229, 233, 240, 242, 246, 250, 254, 263, 267, 271, 273, 275, 277, 279, 281, 283, 285, 289, 291, 293, 297, 301, 305, 309, 311, 313, 315, 317, 319, 321, 325, 332, 334, 338, 340, 342, 346, 349, 351, 355, 359, 361, 363, 365, 367, 369, 373, 375, 377, 381, 385, 389, 393, 397, 404, 406, 410, 414, 419, 423, 432, 436, 440, 442, 444, 446, 448, 450, 452, 456, 458, 460, 464, 468, 472, 476, 478, 480, 482, 484, 486, 488, 492, 499, 501, 505, 508, 510, 517, 519, 523, 527, 532, 536, 545, 549, 553, 557, 559, 561, 565, 567, 570, 572, 574, 576, 578, 582, 584, 586, 588, 590, 592, 594, 596, 598, 600, 602, 604, 606, 609, 611, 613, 615, 617, 619, 621, 623, 625, 627, 629, 631, 633, 635, 637, 639, 641, 643, 645, 647, 649, 653, 655, 657, 659, 661, 663, 665, 667, 669, 671, 673, 675, 680, 682, 684, 686, 688, 690, 692, 694, 696, 701, 703, 705, 707, 712, 714, 716, 718, 720, 724, 733, 742, 744, 747, 749, 751, 758, 763, 765, 767, 771, 773, 777, 779, 781, 786, 788, 790, 792, 794, 799, 804, 809, 814, 819, 822, 826, 828, 832, 834, 836, 838, 840, 842, 844, 846, 848, 850, 852 }; /* YYRHS -- A `-1'-separated list of the rules' RHS. */ static const yytype_int16 yyrhs[] = { 77, 0, -1, 216, -1, 77, 78, -1, 79, -1, 202, -1, 80, -1, 81, -1, 82, -1, 83, -1, 47, 21, 210, 10, 85, 10, 86, 84, 29, 24, -1, 46, 21, 210, 10, 85, 10, 117, 84, 29, 24, -1, 44, 21, 210, 10, 85, 10, 142, 84, 29, 24, -1, 43, 21, 210, 10, 85, 10, 158, 84, 29, 24, -1, 10, 199, 200, -1, 216, -1, 60, -1, 87, -1, 116, -1, 88, -1, 94, -1, 100, -1, 102, -1, 89, -1, 90, -1, 105, -1, 94, 164, 94, -1, 91, -1, 92, -1, 93, -1, 94, 34, 94, -1, 91, 34, 94, -1, 94, 3, 94, -1, 92, 3, 94, -1, 94, 4, 94, -1, 93, 4, 94, -1, 95, -1, 99, -1, 109, -1, 110, -1, 112, -1, 115, -1, 21, 87, 29, -1, 163, 17, 96, 28, 8, 94, -1, 97, -1, 97, 10, 96, -1, 98, -1, 196, -1, 196, 8, 103, -1, 165, 21, 87, 29, -1, 101, 8, 103, -1, 109, -1, 110, -1, 21, 87, 29, -1, 185, 166, 185, -1, 87, -1, 94, -1, 106, -1, 107, -1, 108, -1, 104, 48, 104, -1, 104, 48, 106, -1, 104, 30, 104, -1, 107, 30, 104, -1, 104, 61, 104, -1, 108, 61, 104, -1, 182, -1, 161, -1, 17, 28, -1, 17, 111, 28, -1, 94, -1, 94, 10, 111, -1, 9, 17, 113, 28, 8, 94, -1, 114, -1, 114, 10, 113, -1, 97, 9, 87, -1, 21, 114, 29, -1, 38, 21, 87, 10, 87, 10, 87, 29, -1, 110, 171, 110, -1, 21, 116, 29, -1, 118, -1, 130, -1, 141, -1, 119, -1, 124, -1, 120, -1, 121, -1, 124, 168, 124, -1, 122, -1, 123, -1, 124, 34, 124, -1, 122, 34, 124, -1, 124, 3, 124, -1, 123, 3, 124, -1, 125, -1, 129, -1, 173, -1, 137, -1, 196, -1, 140, -1, 21, 118, 29, -1, 167, 17, 126, 28, 8, 124, -1, 127, -1, 127, 10, 126, -1, 128, -1, 196, -1, 196, 8, 134, -1, 170, 124, -1, 162, -1, 131, 8, 132, -1, 21, 130, 29, -1, 186, -1, 195, -1, 134, -1, 135, -1, 134, -1, 21, 136, 29, -1, 211, -1, 172, -1, 133, 48, 134, -1, 21, 135, 29, -1, 134, 30, 134, -1, 136, 30, 134, -1, 21, 136, 29, -1, 9, 17, 138, 28, 8, 124, -1, 139, -1, 139, 10, 138, -1, 196, 9, 118, -1, 196, 8, 22, 182, -1, 21, 139, 29, -1, 38, 21, 118, 10, 118, 10, 118, 29, -1, 118, 171, 118, -1, 21, 141, 29, -1, 143, -1, 157, -1, 144, -1, 149, -1, 145, -1, 146, -1, 149, 168, 149, -1, 147, -1, 148, -1, 149, 34, 149, -1, 147, 34, 149, -1, 149, 3, 149, -1, 148, 3, 149, -1, 150, -1, 152, -1, 173, -1, 153, -1, 196, -1, 156, -1, 21, 143, 29, -1, 167, 17, 151, 28, 8, 149, -1, 196, -1, 196, 10, 151, -1, 170, 149, -1, 162, -1, 9, 17, 154, 28, 8, 149, -1, 155, -1, 155, 10, 154, -1, 196, 9, 143, -1, 196, 8, 22, 182, -1, 21, 155, 29, -1, 38, 21, 143, 10, 143, 10, 143, 29, -1, 143, 171, 143, -1, 21, 157, 29, -1, 21, 159, 29, -1, 159, -1, 160, -1, 159, 34, 160, -1, 173, -1, 31, 173, -1, 162, -1, 164, -1, 169, -1, 165, -1, 182, 180, 182, -1, 167, -1, 7, -1, 16, -1, 27, -1, 6, -1, 5, -1, 179, -1, 180, -1, 168, -1, 170, -1, 15, -1, 26, -1, 59, 59, -1, 13, -1, 25, -1, 19, -1, 12, -1, 18, -1, 20, -1, 33, -1, 32, -1, 34, -1, 3, -1, 31, -1, 23, -1, 212, -1, 174, -1, 175, -1, 181, -1, 184, -1, 176, -1, 177, -1, 190, -1, 182, 178, 182, -1, 179, -1, 11, -1, 14, -1, 193, -1, 183, -1, 196, -1, 198, -1, 184, -1, 187, -1, 193, -1, 185, -1, 186, 21, 197, 29, -1, 186, -1, 211, -1, 188, -1, 189, -1, 214, -1, 54, -1, 190, -1, 191, -1, 192, 21, 197, 29, -1, 192, -1, 212, -1, 194, -1, 195, 21, 197, 29, -1, 195, -1, 213, -1, 74, -1, 182, -1, 182, 10, 197, -1, 40, 21, 118, 10, 182, 10, 182, 29, -1, 39, 21, 143, 10, 182, 10, 182, 29, -1, 205, -1, 10, 201, -1, 216, -1, 208, -1, 45, 21, 215, 203, 29, 24, -1, 10, 17, 204, 28, -1, 216, -1, 210, -1, 210, 10, 204, -1, 206, -1, 206, 8, 205, -1, 208, -1, 211, -1, 211, 21, 209, 29, -1, 196, -1, 214, -1, 54, -1, 207, -1, 42, 21, 86, 29, -1, 41, 21, 117, 29, -1, 36, 21, 142, 29, -1, 35, 21, 158, 29, -1, 37, 21, 182, 29, -1, 17, 28, -1, 17, 209, 28, -1, 205, -1, 205, 10, 209, -1, 211, -1, 58, -1, 60, -1, 68, -1, 56, -1, 55, -1, 58, -1, 63, -1, 64, -1, 68, -1, -1 }; /* YYRLINE[YYN] -- source line where rule number YYN was defined. */ static const yytype_uint16 yyrline[] = { 0, 225, 225, 226, 229, 230, 233, 234, 235, 236, 239, 242, 245, 248, 251, 252, 255, 258, 259, 262, 263, 264, 265, 268, 269, 270, 273, 276, 277, 278, 281, 282, 285, 286, 289, 290, 293, 294, 295, 296, 297, 298, 299, 302, 305, 306, 309, 310, 313, 316, 319, 322, 323, 324, 327, 330, 333, 336, 337, 338, 341, 342, 345, 346, 349, 350, 353, 354, 357, 358, 361, 362, 365, 368, 369, 372, 373, 376, 379, 380, 383, 384, 385, 388, 389, 392, 393, 396, 399, 400, 403, 404, 407, 408, 411, 412, 413, 414, 415, 416, 417, 420, 423, 424, 427, 428, 431, 434, 435, 438, 439, 442, 443, 446, 447, 450, 451, 454, 455, 458, 459, 462, 463, 464, 467, 470, 471, 474, 475, 476, 479, 482, 483, 486, 487, 490, 491, 494, 495, 498, 501, 502, 505, 506, 509, 510, 513, 514, 515, 516, 517, 518, 519, 522, 525, 526, 529, 530, 533, 536, 537, 540, 541, 542, 545, 548, 549, 552, 553, 556, 557, 560, 561, 562, 565, 566, 567, 570, 573, 574, 575, 576, 577, 578, 581, 582, 583, 586, 587, 588, 591, 594, 595, 598, 599, 600, 601, 602, 603, 606, 607, 610, 613, 616, 619, 620, 621, 624, 627, 628, 631, 634, 637, 640, 643, 646, 649, 650, 651, 654, 655, 656, 659, 660, 663, 666, 669, 670, 673, 674, 677, 680, 681, 684, 687, 690, 691, 694, 697, 700, 703, 704, 707, 708, 711, 714, 715, 718, 721, 724, 725, 728, 729, 732, 733, 734, 737, 738, 739, 740, 741, 742, 745, 746, 747, 748, 749, 752, 753, 756, 757, 760, 761, 764, 765, 768, 771, 774, 775, 776, 779, 782 }; #endif #if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE /* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. First, the terminals, then, starting at YYNTOKENS, nonterminals. */ static const char *const yytname[] = { "$end", "error", "$undefined", "AMPERSAND", "AT_SIGN", "AT_SIGN_MINUS", "AT_SIGN_PLUS", "CARET", "COLON", "COLON_EQUALS", "COMMA", "EQUALS", "EQUALS_GREATER", "EXCLAMATION", "EXCLAMATION_EQUALS", "EXCLAMATION_EXCLAMATION", "EXCLAMATION_GREATER", "LBRKT", "LESS_EQUALS", "LESS_EQUALS_GREATER", "LESS_TILDE_GREATER", "LPAREN", "MINUS", "MINUS_MINUS_GREATER", "PERIOD", "QUESTION", "QUESTION_QUESTION", "QUESTION_STAR", "RBRKT", "RPAREN", "STAR", "TILDE", "TILDE_AMPERSAND", "TILDE_VLINE", "VLINE", "_DLR_cnf", "_DLR_fof", "_DLR_fot", "_DLR_itef", "_DLR_itetf", "_DLR_itett", "_DLR_tff", "_DLR_thf", "_LIT_cnf", "_LIT_fof", "_LIT_include", "_LIT_tff", "_LIT_thf", "arrow", "comment", "comment_line", "decimal", "decimal_exponent", "decimal_fraction", "distinct_object", "dollar_dollar_word", "dollar_word", "dot_decimal", "integer", "less_sign", "lower_word", "plus", "positive_decimal", "rational", "real", "signed_integer", "signed_rational", "signed_real", "single_quoted", "star", "unrecognized", "unsigned_integer", "unsigned_rational", "unsigned_real", "upper_word", "vline", "$accept", "TPTP_file", "TPTP_input", "annotated_formula", "thf_annotated", "tff_annotated", "fof_annotated", "cnf_annotated", "annotations", "formula_role", "thf_formula", "thf_logic_formula", "thf_binary_formula", "thf_binary_pair", "thf_binary_tuple", "thf_or_formula", "thf_and_formula", "thf_apply_formula", "thf_unitary_formula", "thf_quantified_formula", "thf_variable_list", "thf_variable", "thf_typed_variable", "thf_unary_formula", "thf_type_formula", "thf_typeable_formula", "thf_subtype", "thf_top_level_type", "thf_unitary_type", "thf_binary_type", "thf_mapping_type", "thf_xprod_type", "thf_union_type", "thf_atom", "thf_tuple", "thf_tuple_list", "thf_let", "thf_let_list", "thf_defined_var", "thf_conditional", "thf_sequent", "tff_formula", "tff_logic_formula", "tff_binary_formula", "tff_binary_nonassoc", "tff_binary_assoc", "tff_or_formula", "tff_and_formula", "tff_unitary_formula", "tff_quantified_formula", "tff_variable_list", "tff_variable", "tff_typed_variable", "tff_unary_formula", "tff_typed_atom", "tff_untyped_atom", "tff_top_level_type", "tff_unitary_type", "tff_atomic_type", "tff_mapping_type", "tff_xprod_type", "tff_let", "tff_let_list", "tff_defined_var", "tff_conditional", "tff_sequent", "fof_formula", "fof_logic_formula", "fof_binary_formula", "fof_binary_nonassoc", "fof_binary_assoc", "fof_or_formula", "fof_and_formula", "fof_unitary_formula", "fof_quantified_formula", "fof_variable_list", "fof_unary_formula", "fof_let", "fof_let_list", "fof_defined_var", "fof_conditional", "fof_sequent", "cnf_formula", "disjunction", "literal", "thf_conn_term", "fol_infix_unary", "thf_quantifier", "thf_pair_connective", "thf_unary_connective", "subtype_sign", "fol_quantifier", "binary_connective", "assoc_connective", "unary_connective", "gentzen_arrow", "defined_type", "atomic_formula", "plain_atomic_formula", "defined_atomic_formula", "defined_plain_formula", "defined_infix_formula", "defined_infix_pred", "infix_equality", "infix_inequality", "system_atomic_formula", "term", "function_term", "plain_term", "constant", "functor", "defined_term", "defined_atom", "defined_atomic_term", "defined_plain_term", "defined_constant", "defined_functor", "system_term", "system_constant", "system_functor", "variable", "arguments", "conditional_term", "source", "optional_info", "useful_info", "include", "formula_selection", "name_list", "general_term", "general_data", "formula_data", "general_list", "general_terms", "name", "atomic_word", "atomic_defined_word", "atomic_system_word", "number", "file_name", "null", 0 }; #endif # ifdef YYPRINT /* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to token YYLEX-NUM. */ static const yytype_uint16 yytoknum[] = { 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330 }; # endif /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ static const yytype_uint8 yyr1[] = { 0, 76, 77, 77, 78, 78, 79, 79, 79, 79, 80, 81, 82, 83, 84, 84, 85, 86, 86, 87, 87, 87, 87, 88, 88, 88, 89, 90, 90, 90, 91, 91, 92, 92, 93, 93, 94, 94, 94, 94, 94, 94, 94, 95, 96, 96, 97, 97, 98, 99, 100, 101, 101, 101, 102, 103, 104, 105, 105, 105, 106, 106, 107, 107, 108, 108, 109, 109, 110, 110, 111, 111, 112, 113, 113, 114, 114, 115, 116, 116, 117, 117, 117, 118, 118, 119, 119, 120, 121, 121, 122, 122, 123, 123, 124, 124, 124, 124, 124, 124, 124, 125, 126, 126, 127, 127, 128, 129, 129, 130, 130, 131, 131, 132, 132, 133, 133, 134, 134, 135, 135, 136, 136, 136, 137, 138, 138, 139, 139, 139, 140, 141, 141, 142, 142, 143, 143, 144, 144, 145, 146, 146, 147, 147, 148, 148, 149, 149, 149, 149, 149, 149, 149, 150, 151, 151, 152, 152, 153, 154, 154, 155, 155, 155, 156, 157, 157, 158, 158, 159, 159, 160, 160, 160, 161, 161, 161, 162, 163, 163, 163, 163, 163, 163, 164, 164, 164, 165, 165, 165, 166, 167, 167, 168, 168, 168, 168, 168, 168, 169, 169, 170, 171, 172, 173, 173, 173, 174, 175, 175, 176, 177, 178, 179, 180, 181, 182, 182, 182, 183, 183, 183, 184, 184, 185, 186, 187, 187, 188, 188, 189, 190, 190, 191, 192, 193, 193, 194, 195, 196, 197, 197, 198, 198, 199, 200, 200, 201, 202, 203, 203, 204, 204, 205, 205, 205, 206, 206, 206, 206, 206, 206, 207, 207, 207, 207, 207, 208, 208, 209, 209, 210, 210, 211, 211, 212, 213, 214, 214, 214, 215, 216 }; /* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ static const yytype_uint8 yyr2[] = { 0, 2, 1, 2, 1, 1, 1, 1, 1, 1, 10, 10, 10, 10, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 3, 6, 1, 3, 1, 1, 3, 4, 3, 1, 1, 3, 3, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 1, 1, 2, 3, 1, 3, 6, 1, 3, 3, 3, 8, 3, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 3, 6, 1, 3, 1, 1, 3, 2, 1, 3, 3, 1, 1, 1, 1, 1, 3, 1, 1, 3, 3, 3, 3, 3, 6, 1, 3, 3, 4, 3, 8, 3, 3, 1, 1, 1, 1, 1, 1, 3, 1, 1, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 3, 6, 1, 3, 2, 1, 6, 1, 3, 3, 4, 3, 8, 3, 3, 3, 1, 1, 3, 1, 2, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 1, 1, 1, 1, 1, 1, 1, 1, 4, 1, 1, 1, 4, 1, 1, 1, 1, 3, 8, 8, 1, 2, 1, 1, 6, 4, 1, 1, 3, 1, 3, 1, 1, 4, 1, 1, 1, 1, 4, 4, 4, 4, 4, 2, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 }; /* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state STATE-NUM when YYTABLE doesn't specify something else to do. Zero means the default is an error. */ static const yytype_uint16 yydefact[] = { 281, 0, 2, 1, 0, 0, 0, 0, 0, 3, 4, 6, 7, 8, 9, 5, 0, 0, 0, 0, 0, 272, 273, 274, 0, 271, 0, 280, 281, 0, 0, 0, 0, 0, 0, 250, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 251, 248, 0, 0, 0, 0, 0, 0, 229, 276, 275, 277, 278, 279, 239, 281, 168, 169, 173, 171, 204, 205, 208, 209, 206, 0, 216, 207, 222, 224, 220, 226, 227, 210, 231, 233, 215, 235, 237, 217, 218, 225, 234, 238, 228, 0, 191, 0, 192, 201, 0, 281, 133, 135, 137, 138, 140, 141, 136, 146, 147, 149, 151, 134, 157, 0, 0, 148, 150, 249, 0, 0, 0, 0, 281, 80, 83, 85, 86, 88, 89, 84, 94, 95, 81, 0, 97, 99, 82, 108, 0, 0, 96, 224, 237, 98, 200, 183, 182, 179, 0, 213, 194, 214, 188, 180, 0, 195, 193, 196, 0, 189, 181, 198, 197, 199, 0, 281, 17, 19, 23, 24, 27, 28, 29, 20, 36, 37, 21, 0, 22, 0, 25, 57, 58, 59, 38, 39, 40, 41, 18, 67, 0, 174, 176, 178, 186, 175, 187, 184, 185, 66, 219, 222, 230, 221, 0, 172, 0, 0, 0, 0, 0, 15, 0, 0, 212, 0, 0, 0, 0, 0, 0, 0, 0, 0, 202, 0, 0, 0, 0, 0, 0, 0, 0, 156, 252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 68, 70, 38, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 167, 0, 0, 0, 0, 0, 0, 0, 0, 260, 258, 281, 244, 253, 261, 255, 256, 259, 0, 170, 211, 177, 240, 0, 0, 0, 0, 0, 159, 0, 152, 166, 0, 0, 165, 143, 145, 144, 142, 139, 0, 154, 0, 0, 0, 125, 0, 100, 110, 132, 0, 0, 131, 91, 93, 92, 90, 87, 0, 109, 0, 113, 114, 118, 117, 203, 0, 102, 104, 105, 0, 0, 0, 46, 0, 73, 47, 0, 0, 39, 0, 69, 42, 79, 0, 0, 31, 33, 35, 32, 34, 30, 26, 55, 50, 56, 62, 60, 61, 64, 63, 65, 78, 0, 44, 0, 190, 54, 224, 0, 0, 267, 269, 0, 0, 0, 0, 0, 0, 0, 14, 246, 0, 0, 13, 0, 223, 232, 236, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 11, 0, 115, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 71, 0, 10, 0, 0, 49, 0, 0, 0, 268, 0, 0, 0, 0, 0, 245, 247, 254, 0, 241, 163, 0, 160, 0, 161, 0, 0, 155, 129, 0, 126, 0, 127, 0, 0, 0, 120, 116, 0, 119, 0, 103, 106, 76, 75, 0, 74, 48, 0, 0, 45, 0, 0, 270, 265, 264, 266, 263, 262, 257, 158, 162, 0, 153, 124, 128, 0, 123, 121, 122, 101, 72, 0, 43, 0, 0, 0, 0, 0, 243, 242, 164, 130, 77 }; /* YYDEFGOTO[NTERM-NUM]. */ static const yytype_int16 yydefgoto[] = { -1, 1, 9, 10, 11, 12, 13, 14, 210, 39, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 383, 352, 353, 175, 176, 177, 178, 374, 179, 180, 181, 182, 183, 255, 256, 257, 186, 354, 355, 187, 188, 122, 123, 124, 125, 126, 127, 128, 129, 130, 346, 347, 348, 131, 132, 133, 339, 340, 426, 427, 428, 134, 324, 325, 135, 136, 99, 100, 101, 102, 103, 104, 105, 106, 107, 320, 108, 109, 307, 308, 110, 111, 63, 64, 65, 189, 112, 190, 191, 192, 279, 193, 194, 195, 196, 225, 343, 115, 68, 69, 70, 71, 213, 197, 198, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 303, 88, 291, 400, 454, 15, 34, 47, 392, 293, 294, 295, 393, 48, 89, 90, 91, 92, 28, 211 }; /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing STATE-NUM. */ #define YYPACT_NINF -388 static const yytype_int16 yypact[] = { -388, 110, -388, -388, 12, 14, 26, 41, 46, -388, -388, -388, -388, -388, -388, -388, -12, -12, 10, -12, -12, -388, -388, -388, 72, -388, 74, -388, 84, 89, 91, 52, 52, 104, 101, -388, 52, 52, -388, 132, 141, -12, 145, 155, 164, 750, 32, 148, 174, -388, 790, 1403, 594, 471, 177, 179, -388, -388, -388, -388, -388, -388, -388, 201, 178, -388, -388, -388, -388, -388, -388, -388, -388, 50, -388, 111, -388, 192, -388, -388, -388, 127, -388, 193, 152, -388, 195, -388, -388, -388, -388, -388, -388, 216, -388, 32, -388, -388, 214, 201, 217, -388, -388, -388, 205, 238, 298, -388, -388, -388, -388, -388, -388, 227, 1158, -388, 153, -388, -12, 228, 790, 226, 201, 217, -388, -388, -388, 231, 259, 316, -388, -388, -388, 258, -388, -388, -388, -388, 251, 1241, -388, 19, 22, 153, -388, -388, -388, -388, 254, -388, -388, -388, -388, -388, 1339, -388, -388, -388, 1403, -388, -388, -388, -388, -388, 252, 201, -388, -388, -388, -388, 240, 269, 272, 566, -388, -388, -388, 270, -388, -6, -388, -388, 250, 220, 275, 21, -388, -388, -388, -388, 267, -388, 268, -388, -388, -388, -388, -388, -388, -388, -388, 234, -388, -388, 2, -388, 279, 1158, 1241, 1058, 266, -388, 594, 471, -388, 471, 471, 471, 471, -1, 3, 271, 1158, 274, -388, 1158, 1158, 1158, 1158, 1158, 1158, 222, 1158, -388, -388, 0, 36, 276, 282, 1241, 284, 1241, 1241, 1241, 1241, 1241, 1241, 29, 222, 1241, -388, 1, 1467, -388, 287, -388, -388, 295, 285, 296, 1467, 297, 1531, 1531, 1531, 1531, 1531, 1531, 1531, 1467, 1531, 1531, 1531, 1531, 1531, 281, 222, 1467, 245, 23, -388, 289, 314, 1538, 306, 308, 312, 317, 318, -388, -388, 330, -388, 334, -388, -388, 326, -388, 331, -388, -388, -388, 346, 329, 332, 335, -1, 337, 349, 183, -388, -388, 353, 342, -388, -388, -388, -388, -388, -388, 339, 358, 340, 0, 343, 360, 186, -388, -388, -388, 362, 354, -388, -388, -388, -388, -388, -388, 77, -388, 333, 345, -388, -388, -388, -388, 355, 369, -388, 380, 365, 1, 387, -388, 370, 390, 389, 1467, 372, 396, 1531, -388, 397, -388, 400, 382, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, -388, 359, -388, -388, -388, -388, -388, 383, 402, 385, -388, -388, -388, 471, 471, -388, 408, 391, 750, 32, 471, 790, 1403, 404, -388, -388, 1058, 1058, -388, 471, -388, -388, -388, 393, 416, -1, 405, 1158, 1158, -388, 420, 222, 401, 421, 0, 411, 1241, 1241, -388, 77, 412, 409, 167, -2, 433, 222, -2, 418, 1467, 441, 1, 1467, -388, -388, 1467, -388, 442, 222, -388, 443, 445, 1058, -388, 423, 430, 431, 434, 435, -388, -388, -388, 436, -388, -388, 1158, -388, 471, -388, 452, 1158, -388, -388, 1241, -388, 471, -388, 457, 180, -2, -388, -388, -2, -388, 1241, -388, -388, -388, -388, 1531, -388, -388, 458, 1531, -388, 471, 471, -388, -388, -388, -388, -388, -388, -388, -388, -388, 1158, -388, -388, -388, 1241, 424, -388, -388, -388, -388, 1467, -388, 440, 444, 446, 449, 451, -388, -388, -388, -388, -388 }; /* YYPGOTO[NTERM-NUM]. */ static const yytype_int16 yypgoto[] = { -388, -388, -388, -388, -388, -388, -388, -388, -84, 99, 96, -149, -388, -388, -388, -388, -388, -388, -85, -388, 55, -253, -388, -388, -388, -388, -388, 58, -112, -388, 235, -388, -388, 465, 17, 154, -388, 76, 162, -388, 357, 120, -115, -388, -388, -388, -388, -388, -127, -388, 87, -388, -388, -388, 399, -388, -388, -388, -168, 273, 97, -388, 103, 198, -388, 410, 129, -93, -388, -388, -388, -388, -388, -80, -388, 115, -388, -388, 122, 230, -388, 447, 143, 488, 350, -388, 516, -388, 368, -388, -388, 781, -78, -388, 842, -109, -388, 455, -388, -388, -388, -388, -388, -54, 470, -388, -45, -388, -14, 351, -43, -388, -388, -388, 219, -388, -388, 286, -388, -40, 722, -200, -388, -388, -388, -388, -388, -388, 426, -196, -388, -388, 165, -387, 88, -16, -195, -388, -160, -388, 11 }; /* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If positive, shift that token. If negative, reduce the rule which number is the opposite. If zero, do what YYDEFACT says. If YYTABLE_NINF, syntax error. */ #define YYTABLE_NINF -231 static const yytype_int16 yytable[] = { 25, 25, 220, 25, 25, 236, 199, 141, 206, 258, 142, 2, 250, 292, 241, 223, 457, 304, 305, 214, 306, 323, 351, 384, 270, 25, 224, -111, 230, -52, -112, 280, 310, 16, 233, 17, 212, 200, 240, 35, 216, 93, 271, 218, 224, 94, 21, 18, 22, 297, 338, 246, 345, 95, 58, 272, 23, 96, 22, 224, 492, 149, 19, 97, 151, 327, 23, 20, 185, 254, 98, 54, 55, 62, 62, 62, 275, 141, 27, 341, 142, 261, 31, 22, 32, 58, 56, 57, 58, 22, 59, 23, 22, 282, 33, 60, 61, 23, 425, 36, 23, 37, 25, 358, 24, 26, 62, 29, 30, 199, 3, 364, 38, 199, 281, 333, 334, 335, 336, 337, 373, 41, -219, 297, 330, -219, 332, 241, 385, 312, 42, 40, 314, 58, 350, 43, 44, 22, -230, 322, 200, -230, 45, 345, 200, 23, 315, 316, 317, 318, 319, 46, 214, 4, 5, 6, 7, 8, 376, 377, 379, 380, 381, -221, -217, 50, -221, -217, 300, 49, 301, 302, 302, 302, 51, 185, 117, 366, 367, 368, 369, 370, 371, 372, 118, 375, 375, 375, 375, 375, 384, 412, 413, 296, 421, 422, 476, 477, 207, 200, 208, 200, 200, 200, 200, 458, 456, 199, 258, 506, 477, 209, 212, 216, 217, 199, 218, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 345, 344, 199, 219, 345, 222, 388, 345, 200, 226, 224, 227, 297, 297, 231, 235, 200, 239, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 478, 243, 200, 481, 242, 247, 296, 248, 359, 202, 251, 263, 260, 262, 254, 264, 359, 269, 345, 273, 274, 345, -51, 276, 483, 359, 297, 373, 277, 149, 487, 382, 278, 359, 298, 62, 360, 154, 389, 311, 228, 401, 313, 386, 328, 507, 471, 472, 508, 150, 329, 199, 331, 362, 199, 155, 156, 157, 244, 463, 464, 344, 361, 390, 363, 365, 394, 150, 395, 161, 162, 229, 396, 155, 156, 157, 203, 397, 398, 399, 503, 402, 200, 445, 446, 200, 403, 161, 162, 245, 451, 509, 199, 141, 404, 405, 142, 406, 411, 302, 407, 517, 414, 408, 410, 415, 416, 417, 310, 420, 419, 423, 202, 359, 200, 200, 202, 424, 431, 499, 429, 200, 430, 200, 502, 296, 296, 432, 199, 516, 200, 199, -115, 327, 199, 434, 437, 435, 510, 436, 438, 201, 512, -52, -53, 441, 271, 515, 344, 440, 442, 443, 344, 444, 185, 344, 500, 447, 448, 200, 283, 459, 200, 460, 504, 200, 462, 465, 468, 467, 296, 202, 470, 202, 202, 202, 202, 475, 199, 203, 479, 474, 199, 203, 513, 514, 482, 200, 484, 488, 359, 493, 490, 359, 491, 200, 359, 344, 494, 495, 344, 501, 496, 497, 498, 199, 505, 511, 518, 200, 202, -116, 519, 200, 520, 200, 200, 521, 202, 522, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 453, 486, 202, 200, 489, 203, 67, 203, 203, 203, 203, 140, 378, 67, 205, 201, 54, 55, 485, 433, 439, 259, 184, 452, 480, 237, 342, 418, 473, 469, 450, 56, 57, 58, 359, 59, 238, 22, 466, 461, 60, 61, 409, 449, 203, 23, 204, 268, 221, 215, 234, 62, 203, 0, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 66, 299, 203, 455, 0, 137, 0, 66, 265, 266, 0, 0, 0, 0, 140, 202, 149, 150, 202, 151, 0, 0, 0, 155, 156, 157, 0, 0, 0, 0, 0, 0, 0, 140, 0, -56, 0, 161, 162, 267, 0, 0, 201, 0, 0, 0, 0, 202, 202, 0, 201, 0, 0, -56, 202, 0, 202, 0, 0, 201, 0, 0, 184, 202, 53, 0, -56, 201, 0, 387, 0, 0, 54, 55, 0, 137, 0, 0, 0, 0, 0, 0, 203, 0, 0, 203, 0, 56, 57, 58, 0, 59, 202, 22, 137, 202, 60, 61, 202, 0, 0, 23, 140, 0, 0, 0, 67, 62, 0, 0, 0, 0, 0, 0, 203, 203, 0, 0, 0, 0, 202, 203, 0, 203, 0, 0, 0, 0, 202, 0, 203, 0, 0, 140, 0, 140, 140, 140, 140, 140, 140, 0, 202, 140, 0, 0, 202, 201, 202, 202, 0, 0, 0, 0, 0, 0, 184, 0, 0, 203, 0, 0, 203, 137, 184, 203, 0, 66, 0, 202, 0, 0, 0, 184, 0, 0, 0, 0, 0, 0, 0, 184, 0, 0, 0, 0, 0, 203, 201, 0, 0, 0, 0, 0, 137, 203, 137, 137, 137, 137, 137, 137, 0, 0, 137, 0, 0, 116, 0, 203, 52, 143, 0, 203, 0, 203, 203, 0, 0, 0, 53, 0, 0, 0, 201, 0, 0, 201, 54, 55, 201, 0, 0, 0, 0, 0, 203, 0, 119, 0, 0, 0, 94, 56, 57, 58, 0, 59, 0, 22, 120, 0, 60, 61, 96, 0, 116, 23, 0, 0, 97, 184, 0, 62, 0, 0, 113, 121, 54, 55, 138, 0, 0, 0, 0, 116, 0, 0, 0, 0, 0, 143, 0, 56, 57, 58, 0, 59, 67, 22, 0, 140, 60, 61, 0, 0, 0, 23, 0, 0, 143, 201, 184, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 113, 140, 140, 0, 0, 0, 0, 0, 0, 0, 0, 0, 114, 0, 0, 0, 139, 0, 0, 113, 0, 0, 0, 184, 0, 138, 184, 0, 0, 184, 0, 0, 0, 0, 66, 0, 0, 137, 0, 0, 0, 0, 0, 0, 138, 0, 0, 140, 0, 0, 0, 0, 0, 116, 143, 290, 0, 0, 140, 0, 0, 114, 137, 137, 0, 309, 0, 0, 116, 0, 0, 116, 116, 116, 116, 116, 116, 321, 116, 0, 114, 326, 0, 0, 140, 143, 139, 143, 143, 143, 143, 143, 143, 0, 349, 143, 0, 356, 0, 0, 184, 0, 0, 0, 0, 139, 0, 0, 137, 0, 0, 0, 113, 138, 0, 0, 0, 0, 0, 137, 0, 0, 356, 0, 0, 0, 0, 113, 0, 290, 113, 113, 113, 113, 113, 113, 0, 113, 0, 0, 0, 0, 0, 0, 138, 137, 138, 138, 138, 138, 138, 138, 309, 0, 138, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 326, 0, 0, 0, 114, 139, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 114, 0, 0, 114, 114, 114, 114, 114, 114, 356, 114, 283, 0, 0, 0, 0, 0, 139, 0, 139, 139, 139, 139, 139, 139, 0, 0, 139, 0, 284, 285, 286, 0, 0, 0, 287, 288, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 289, 0, 0, 0, 59, 116, 22, 143, 0, 60, 61, 0, 290, 290, 23, 0, 0, 0, 0, 0, 62, 309, 0, 116, 116, 0, 0, 321, 0, 0, 326, 0, 143, 143, 0, 0, 0, 0, 0, 0, 0, 349, 0, 0, 0, 0, 356, 0, 0, 0, 0, 0, 0, 356, 0, 93, 0, 290, 0, 94, 0, 0, 0, 0, 113, 0, 138, 232, 0, 0, 116, 96, 0, 0, 0, 116, 0, 97, 143, 0, 0, 0, 113, 113, 98, 54, 55, 0, 0, 143, 0, 138, 138, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 0, 59, 0, 22, 0, 0, 60, 61, 116, 0, 0, 23, 143, 0, 0, 0, 0, 62, 0, 0, 0, 0, 114, 0, 139, 0, 113, 0, 0, 0, 0, 113, 0, 0, 138, 119, 0, 0, 0, 94, 114, 114, 0, 0, 0, 138, 0, 249, 0, 139, 139, 96, 0, 0, 0, 0, 0, 97, 0, 0, 0, 0, 0, 0, 121, 54, 55, 113, 0, 0, 0, 138, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 0, 59, 0, 22, 114, 0, 60, 61, 0, 114, 0, 23, 139, 0, 0, 0, 0, 62, 0, 0, 0, 0, 0, 139, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 144, 114, 145, 146, 147, 139, 148, 0, 149, 150, 94, 151, 152, 153, 154, 155, 156, 157, 252, 0, 0, 0, 96, 159, 160, 253, 0, 0, 97, 161, 162, 163, 0, 0, 0, 164, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 0, 59, 0, 22, 0, 0, 60, 61, 0, 0, 144, 23, 145, 146, 147, 0, 148, 62, 149, 150, 94, 151, 152, 153, 154, 155, 156, 157, 158, 0, 0, 0, 96, 159, 160, 0, 0, 0, 97, 161, 162, 163, 0, 0, 0, 164, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 0, 59, 0, 22, 0, 0, 60, 61, 0, 0, 144, 23, 145, 146, 147, 0, 148, 62, 149, 150, 94, 151, 152, 153, 154, 155, 156, 157, 357, 0, 0, 0, 96, 159, 160, 0, 0, 0, 97, 161, 162, 163, 0, 0, 0, 164, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 0, 59, 0, 22, 0, 0, 60, 61, 0, 0, 144, 23, 145, 146, 147, 0, 148, 62, 149, 150, 94, 151, 152, 153, 154, 155, 156, 157, 252, 0, 0, 283, 96, 159, 160, 0, 0, 0, 97, 161, 162, 163, 391, 0, 0, 164, 54, 55, 0, 284, 285, 286, 0, 0, 0, 287, 288, 0, 0, 0, 0, 56, 57, 58, 0, 59, 0, 22, 289, 0, 60, 61, 59, 0, 22, 23, 0, 60, 61, 0, 0, 62, 23, 0, 0, 0, 0, 0, 62 }; static const yytype_int16 yycheck[] = { 16, 17, 95, 19, 20, 120, 51, 50, 53, 158, 50, 0, 139, 209, 123, 99, 403, 217, 218, 73, 21, 21, 21, 276, 30, 41, 23, 8, 106, 8, 8, 29, 29, 21, 114, 21, 34, 51, 122, 28, 21, 9, 48, 21, 23, 13, 58, 21, 60, 209, 21, 129, 247, 21, 56, 61, 68, 25, 60, 23, 447, 11, 21, 31, 14, 29, 68, 21, 51, 154, 38, 39, 40, 74, 74, 74, 185, 120, 68, 247, 120, 165, 10, 60, 10, 56, 54, 55, 56, 60, 58, 68, 60, 208, 10, 63, 64, 68, 21, 10, 68, 10, 118, 252, 16, 17, 74, 19, 20, 154, 0, 260, 60, 158, 207, 242, 243, 244, 245, 246, 269, 17, 11, 283, 239, 14, 241, 236, 277, 222, 29, 32, 225, 56, 249, 36, 37, 60, 11, 232, 154, 14, 10, 338, 158, 68, 226, 227, 228, 229, 230, 10, 206, 43, 44, 45, 46, 47, 270, 271, 272, 273, 274, 11, 11, 10, 14, 14, 213, 24, 215, 216, 217, 218, 10, 158, 28, 262, 263, 264, 265, 266, 267, 268, 10, 270, 271, 272, 273, 274, 443, 8, 9, 209, 8, 9, 29, 30, 21, 213, 21, 215, 216, 217, 218, 405, 402, 252, 357, 29, 30, 10, 34, 21, 21, 260, 21, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 425, 247, 277, 17, 429, 21, 279, 432, 252, 34, 23, 3, 402, 403, 17, 17, 260, 21, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 429, 3, 277, 432, 34, 8, 283, 17, 252, 51, 17, 3, 21, 34, 360, 4, 260, 8, 474, 30, 61, 477, 8, 17, 434, 269, 447, 437, 21, 11, 440, 275, 59, 277, 29, 74, 10, 17, 10, 29, 3, 291, 29, 59, 29, 474, 422, 423, 477, 12, 29, 357, 29, 29, 360, 18, 19, 20, 3, 413, 414, 338, 28, 10, 29, 29, 21, 12, 21, 32, 33, 34, 21, 18, 19, 20, 51, 21, 21, 10, 468, 8, 357, 389, 390, 360, 21, 32, 33, 34, 396, 479, 398, 397, 24, 10, 397, 29, 10, 405, 29, 511, 10, 29, 28, 24, 28, 10, 29, 10, 28, 10, 154, 357, 389, 390, 158, 24, 10, 460, 48, 396, 28, 398, 465, 402, 403, 8, 434, 505, 405, 437, 48, 29, 440, 9, 8, 28, 484, 10, 29, 51, 488, 8, 8, 24, 48, 501, 425, 10, 28, 10, 429, 29, 398, 432, 462, 10, 28, 434, 17, 29, 437, 8, 470, 440, 22, 8, 8, 29, 447, 213, 22, 215, 216, 217, 218, 29, 484, 154, 8, 30, 488, 158, 490, 491, 29, 462, 8, 8, 434, 29, 10, 437, 10, 470, 440, 474, 29, 29, 477, 10, 29, 29, 29, 511, 10, 10, 29, 484, 252, 48, 29, 488, 29, 490, 491, 29, 260, 29, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 398, 437, 277, 511, 443, 213, 45, 215, 216, 217, 218, 50, 271, 52, 53, 158, 39, 40, 436, 351, 360, 158, 51, 397, 431, 120, 247, 323, 425, 420, 395, 54, 55, 56, 511, 58, 120, 60, 417, 411, 63, 64, 306, 394, 252, 68, 52, 173, 95, 73, 118, 74, 260, -1, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 45, 212, 277, 399, -1, 50, -1, 52, 3, 4, -1, -1, -1, -1, 120, 357, 11, 12, 360, 14, -1, -1, -1, 18, 19, 20, -1, -1, -1, -1, -1, -1, -1, 139, -1, 30, -1, 32, 33, 34, -1, -1, 252, -1, -1, -1, -1, 389, 390, -1, 260, -1, -1, 48, 396, -1, 398, -1, -1, 269, -1, -1, 158, 405, 31, -1, 61, 277, -1, 279, -1, -1, 39, 40, -1, 120, -1, -1, -1, -1, -1, -1, 357, -1, -1, 360, -1, 54, 55, 56, -1, 58, 434, 60, 139, 437, 63, 64, 440, -1, -1, 68, 208, -1, -1, -1, 212, 74, -1, -1, -1, -1, -1, -1, 389, 390, -1, -1, -1, -1, 462, 396, -1, 398, -1, -1, -1, -1, 470, -1, 405, -1, -1, 239, -1, 241, 242, 243, 244, 245, 246, -1, 484, 249, -1, -1, 488, 357, 490, 491, -1, -1, -1, -1, -1, -1, 252, -1, -1, 434, -1, -1, 437, 208, 260, 440, -1, 212, -1, 511, -1, -1, -1, 269, -1, -1, -1, -1, -1, -1, -1, 277, -1, -1, -1, -1, -1, 462, 398, -1, -1, -1, -1, -1, 239, 470, 241, 242, 243, 244, 245, 246, -1, -1, 249, -1, -1, 46, -1, 484, 21, 50, -1, 488, -1, 490, 491, -1, -1, -1, 31, -1, -1, -1, 434, -1, -1, 437, 39, 40, 440, -1, -1, -1, -1, -1, 511, -1, 9, -1, -1, -1, 13, 54, 55, 56, -1, 58, -1, 60, 21, -1, 63, 64, 25, -1, 95, 68, -1, -1, 31, 357, -1, 74, -1, -1, 46, 38, 39, 40, 50, -1, -1, -1, -1, 114, -1, -1, -1, -1, -1, 120, -1, 54, 55, 56, -1, 58, 394, 60, -1, 397, 63, 64, -1, -1, -1, 68, -1, -1, 139, 511, 398, 74, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 95, 422, 423, -1, -1, -1, -1, -1, -1, -1, -1, -1, 46, -1, -1, -1, 50, -1, -1, 114, -1, -1, -1, 434, -1, 120, 437, -1, -1, 440, -1, -1, -1, -1, 394, -1, -1, 397, -1, -1, -1, -1, -1, -1, 139, -1, -1, 468, -1, -1, -1, -1, -1, 207, 208, 209, -1, -1, 479, -1, -1, 95, 422, 423, -1, 219, -1, -1, 222, -1, -1, 225, 226, 227, 228, 229, 230, 231, 232, -1, 114, 235, -1, -1, 505, 239, 120, 241, 242, 243, 244, 245, 246, -1, 248, 249, -1, 251, -1, -1, 511, -1, -1, -1, -1, 139, -1, -1, 468, -1, -1, -1, 207, 208, -1, -1, -1, -1, -1, 479, -1, -1, 276, -1, -1, -1, -1, 222, -1, 283, 225, 226, 227, 228, 229, 230, -1, 232, -1, -1, -1, -1, -1, -1, 239, 505, 241, 242, 243, 244, 245, 246, 306, -1, 249, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 323, -1, -1, -1, 207, 208, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 222, -1, -1, 225, 226, 227, 228, 229, 230, 351, 232, 17, -1, -1, -1, -1, -1, 239, -1, 241, 242, 243, 244, 245, 246, -1, -1, 249, -1, 35, 36, 37, -1, -1, -1, 41, 42, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 54, -1, -1, -1, 58, 395, 60, 397, -1, 63, 64, -1, 402, 403, 68, -1, -1, -1, -1, -1, 74, 411, -1, 413, 414, -1, -1, 417, -1, -1, 420, -1, 422, 423, -1, -1, -1, -1, -1, -1, -1, 431, -1, -1, -1, -1, 436, -1, -1, -1, -1, -1, -1, 443, -1, 9, -1, 447, -1, 13, -1, -1, -1, -1, 395, -1, 397, 21, -1, -1, 460, 25, -1, -1, -1, 465, -1, 31, 468, -1, -1, -1, 413, 414, 38, 39, 40, -1, -1, 479, -1, 422, 423, -1, -1, -1, -1, -1, -1, -1, 54, 55, 56, -1, 58, -1, 60, -1, -1, 63, 64, 501, -1, -1, 68, 505, -1, -1, -1, -1, 74, -1, -1, -1, -1, 395, -1, 397, -1, 460, -1, -1, -1, -1, 465, -1, -1, 468, 9, -1, -1, -1, 13, 413, 414, -1, -1, -1, 479, -1, 21, -1, 422, 423, 25, -1, -1, -1, -1, -1, 31, -1, -1, -1, -1, -1, -1, 38, 39, 40, 501, -1, -1, -1, 505, -1, -1, -1, -1, -1, -1, -1, -1, 54, 55, 56, -1, 58, -1, 60, 460, -1, 63, 64, -1, 465, -1, 68, 468, -1, -1, -1, -1, 74, -1, -1, -1, -1, -1, 479, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 3, 501, 5, 6, 7, 505, 9, -1, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, -1, -1, -1, 25, 26, 27, 28, -1, -1, 31, 32, 33, 34, -1, -1, -1, 38, 39, 40, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 54, 55, 56, -1, 58, -1, 60, -1, -1, 63, 64, -1, -1, 3, 68, 5, 6, 7, -1, 9, 74, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, -1, -1, -1, 25, 26, 27, -1, -1, -1, 31, 32, 33, 34, -1, -1, -1, 38, 39, 40, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 54, 55, 56, -1, 58, -1, 60, -1, -1, 63, 64, -1, -1, 3, 68, 5, 6, 7, -1, 9, 74, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, -1, -1, -1, 25, 26, 27, -1, -1, -1, 31, 32, 33, 34, -1, -1, -1, 38, 39, 40, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 54, 55, 56, -1, 58, -1, 60, -1, -1, 63, 64, -1, -1, 3, 68, 5, 6, 7, -1, 9, 74, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, -1, -1, 17, 25, 26, 27, -1, -1, -1, 31, 32, 33, 34, 28, -1, -1, 38, 39, 40, -1, 35, 36, 37, -1, -1, -1, 41, 42, -1, -1, -1, -1, 54, 55, 56, -1, 58, -1, 60, 54, -1, 63, 64, 58, -1, 60, 68, -1, 63, 64, -1, -1, 74, 68, -1, -1, -1, -1, -1, 74 }; /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing symbol of state STATE-NUM. */ static const yytype_uint8 yystos[] = { 0, 77, 216, 0, 43, 44, 45, 46, 47, 78, 79, 80, 81, 82, 83, 202, 21, 21, 21, 21, 21, 58, 60, 68, 210, 211, 210, 68, 215, 210, 210, 10, 10, 10, 203, 216, 10, 10, 60, 85, 85, 17, 29, 85, 85, 10, 10, 204, 210, 24, 10, 10, 21, 31, 39, 40, 54, 55, 56, 58, 63, 64, 74, 158, 159, 160, 162, 173, 174, 175, 176, 177, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 198, 211, 212, 213, 214, 9, 13, 21, 25, 31, 38, 142, 143, 144, 145, 146, 147, 148, 149, 150, 152, 153, 156, 157, 162, 167, 170, 173, 196, 28, 10, 9, 21, 38, 117, 118, 119, 120, 121, 122, 123, 124, 125, 129, 130, 131, 137, 140, 141, 162, 167, 170, 173, 186, 195, 196, 3, 5, 6, 7, 9, 11, 12, 14, 15, 16, 17, 18, 19, 20, 21, 26, 27, 32, 33, 34, 38, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 99, 100, 101, 102, 104, 105, 106, 107, 108, 109, 110, 112, 115, 116, 161, 163, 164, 165, 167, 168, 169, 170, 179, 180, 182, 184, 185, 190, 193, 159, 173, 182, 21, 21, 10, 84, 216, 34, 178, 179, 180, 21, 21, 21, 17, 143, 157, 21, 84, 23, 171, 34, 3, 3, 34, 168, 17, 21, 149, 204, 17, 118, 130, 141, 21, 84, 171, 34, 3, 3, 34, 168, 8, 17, 21, 124, 17, 21, 28, 94, 109, 110, 111, 87, 116, 21, 84, 34, 3, 4, 3, 4, 34, 164, 8, 30, 48, 61, 30, 61, 171, 17, 21, 59, 166, 29, 143, 118, 17, 35, 36, 37, 41, 42, 54, 196, 199, 205, 206, 207, 208, 211, 214, 29, 160, 182, 182, 182, 197, 197, 197, 21, 154, 155, 196, 29, 29, 143, 29, 143, 149, 149, 149, 149, 149, 151, 196, 143, 21, 138, 139, 196, 29, 29, 29, 118, 29, 118, 124, 124, 124, 124, 124, 21, 132, 133, 134, 135, 172, 211, 212, 126, 127, 128, 196, 118, 21, 97, 98, 113, 114, 196, 21, 87, 110, 10, 28, 29, 29, 87, 29, 94, 94, 94, 94, 94, 94, 94, 87, 103, 94, 104, 104, 106, 104, 104, 104, 110, 96, 97, 87, 59, 185, 186, 10, 10, 28, 205, 209, 21, 21, 21, 21, 21, 10, 200, 216, 8, 21, 24, 10, 29, 29, 29, 155, 28, 10, 8, 9, 10, 24, 28, 10, 139, 28, 10, 8, 9, 10, 24, 21, 134, 135, 136, 48, 28, 10, 8, 114, 9, 28, 10, 8, 29, 111, 10, 24, 28, 10, 29, 182, 182, 10, 28, 158, 142, 182, 117, 86, 201, 208, 205, 209, 197, 29, 8, 154, 22, 143, 143, 8, 151, 29, 8, 138, 22, 118, 118, 136, 30, 29, 29, 30, 134, 8, 126, 134, 29, 87, 8, 113, 103, 87, 8, 96, 10, 10, 209, 29, 29, 29, 29, 29, 29, 149, 182, 10, 149, 124, 182, 10, 29, 134, 134, 124, 94, 10, 94, 182, 182, 143, 118, 87, 29, 29, 29, 29, 29 }; #define yyerrok (yyerrstatus = 0) #define yyclearin (yychar = YYEMPTY) #define YYEMPTY (-2) #define YYEOF 0 #define YYACCEPT goto yyacceptlab #define YYABORT goto yyabortlab #define YYERROR goto yyerrorlab /* Like YYERROR except do call yyerror. This remains here temporarily to ease the transition to the new meaning of YYERROR, for GCC. Once GCC version 2 has supplanted version 1, this can go. However, YYFAIL appears to be in use. Nevertheless, it is formally deprecated in Bison 2.4.2's NEWS entry, where a plan to phase it out is discussed. */ #define YYFAIL goto yyerrlab #if defined YYFAIL /* This is here to suppress warnings from the GCC cpp's -Wunused-macros. Normally we don't worry about that warning, but some users do, and we want to make it easy for users to remove YYFAIL uses, which will produce warnings from Bison 2.5. */ #endif #define YYRECOVERING() (!!yyerrstatus) #define YYBACKUP(Token, Value) \ do \ if (yychar == YYEMPTY && yylen == 1) \ { \ yychar = (Token); \ yylval = (Value); \ yytoken = YYTRANSLATE (yychar); \ YYPOPSTACK (1); \ goto yybackup; \ } \ else \ { \ yyerror (YY_("syntax error: cannot back up")); \ YYERROR; \ } \ while (YYID (0)) #define YYTERROR 1 #define YYERRCODE 256 /* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. If N is 0, then set CURRENT to the empty location which ends the previous symbol: RHS[0] (always defined). */ #define YYRHSLOC(Rhs, K) ((Rhs)[K]) #ifndef YYLLOC_DEFAULT # define YYLLOC_DEFAULT(Current, Rhs, N) \ do \ if (YYID (N)) \ { \ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ } \ else \ { \ (Current).first_line = (Current).last_line = \ YYRHSLOC (Rhs, 0).last_line; \ (Current).first_column = (Current).last_column = \ YYRHSLOC (Rhs, 0).last_column; \ } \ while (YYID (0)) #endif /* YY_LOCATION_PRINT -- Print the location on the stream. This macro was not mandated originally: define only if we know we won't break user code: when these are the locations we know. */ #ifndef YY_LOCATION_PRINT # if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL # define YY_LOCATION_PRINT(File, Loc) \ fprintf (File, "%d.%d-%d.%d", \ (Loc).first_line, (Loc).first_column, \ (Loc).last_line, (Loc).last_column) # else # define YY_LOCATION_PRINT(File, Loc) ((void) 0) # endif #endif /* YYLEX -- calling `yylex' with the right arguments. */ #ifdef YYLEX_PARAM # define YYLEX yylex (YYLEX_PARAM) #else # define YYLEX yylex () #endif /* Enable debugging if requested. */ #if YYDEBUG # ifndef YYFPRINTF # include /* INFRINGES ON USER NAME SPACE */ # define YYFPRINTF fprintf # endif # define YYDPRINTF(Args) \ do { \ if (yydebug) \ YYFPRINTF Args; \ } while (YYID (0)) # define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ do { \ if (yydebug) \ { \ YYFPRINTF (stderr, "%s ", Title); \ yy_symbol_print (stderr, \ Type, Value); \ YYFPRINTF (stderr, "\n"); \ } \ } while (YYID (0)) /*--------------------------------. | Print this symbol on YYOUTPUT. | `--------------------------------*/ /*ARGSUSED*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) #else static void yy_symbol_value_print (yyoutput, yytype, yyvaluep) FILE *yyoutput; int yytype; YYSTYPE const * const yyvaluep; #endif { if (!yyvaluep) return; # ifdef YYPRINT if (yytype < YYNTOKENS) YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); # else YYUSE (yyoutput); # endif switch (yytype) { default: break; } } /*--------------------------------. | Print this symbol on YYOUTPUT. | `--------------------------------*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) #else static void yy_symbol_print (yyoutput, yytype, yyvaluep) FILE *yyoutput; int yytype; YYSTYPE const * const yyvaluep; #endif { if (yytype < YYNTOKENS) YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); else YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); yy_symbol_value_print (yyoutput, yytype, yyvaluep); YYFPRINTF (yyoutput, ")"); } /*------------------------------------------------------------------. | yy_stack_print -- Print the state stack from its BOTTOM up to its | | TOP (included). | `------------------------------------------------------------------*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop) #else static void yy_stack_print (yybottom, yytop) yytype_int16 *yybottom; yytype_int16 *yytop; #endif { YYFPRINTF (stderr, "Stack now"); for (; yybottom <= yytop; yybottom++) { int yybot = *yybottom; YYFPRINTF (stderr, " %d", yybot); } YYFPRINTF (stderr, "\n"); } # define YY_STACK_PRINT(Bottom, Top) \ do { \ if (yydebug) \ yy_stack_print ((Bottom), (Top)); \ } while (YYID (0)) /*------------------------------------------------. | Report that the YYRULE is going to be reduced. | `------------------------------------------------*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yy_reduce_print (YYSTYPE *yyvsp, int yyrule) #else static void yy_reduce_print (yyvsp, yyrule) YYSTYPE *yyvsp; int yyrule; #endif { int yynrhs = yyr2[yyrule]; int yyi; unsigned long int yylno = yyrline[yyrule]; YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", yyrule - 1, yylno); /* The symbols being reduced. */ for (yyi = 0; yyi < yynrhs; yyi++) { YYFPRINTF (stderr, " $%d = ", yyi + 1); yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], &(yyvsp[(yyi + 1) - (yynrhs)]) ); YYFPRINTF (stderr, "\n"); } } # define YY_REDUCE_PRINT(Rule) \ do { \ if (yydebug) \ yy_reduce_print (yyvsp, Rule); \ } while (YYID (0)) /* Nonzero means print parse trace. It is left uninitialized so that multiple parsers can coexist. */ int yydebug; #else /* !YYDEBUG */ # define YYDPRINTF(Args) # define YY_SYMBOL_PRINT(Title, Type, Value, Location) # define YY_STACK_PRINT(Bottom, Top) # define YY_REDUCE_PRINT(Rule) #endif /* !YYDEBUG */ /* YYINITDEPTH -- initial size of the parser's stacks. */ #ifndef YYINITDEPTH # define YYINITDEPTH 200 #endif /* YYMAXDEPTH -- maximum size the stacks can grow to (effective only if the built-in stack extension method is used). Do not make this value too large; the results are undefined if YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) evaluated with infinite-precision integer arithmetic. */ #ifndef YYMAXDEPTH # define YYMAXDEPTH 10000 #endif #if YYERROR_VERBOSE # ifndef yystrlen # if defined __GLIBC__ && defined _STRING_H # define yystrlen strlen # else /* Return the length of YYSTR. */ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static YYSIZE_T yystrlen (const char *yystr) #else static YYSIZE_T yystrlen (yystr) const char *yystr; #endif { YYSIZE_T yylen; for (yylen = 0; yystr[yylen]; yylen++) continue; return yylen; } # endif # endif # ifndef yystpcpy # if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE # define yystpcpy stpcpy # else /* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in YYDEST. */ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static char * yystpcpy (char *yydest, const char *yysrc) #else static char * yystpcpy (yydest, yysrc) char *yydest; const char *yysrc; #endif { char *yyd = yydest; const char *yys = yysrc; while ((*yyd++ = *yys++) != '\0') continue; return yyd - 1; } # endif # endif # ifndef yytnamerr /* Copy to YYRES the contents of YYSTR after stripping away unnecessary quotes and backslashes, so that it's suitable for yyerror. The heuristic is that double-quoting is unnecessary unless the string contains an apostrophe, a comma, or backslash (other than backslash-backslash). YYSTR is taken from yytname. If YYRES is null, do not copy; instead, return the length of what the result would have been. */ static YYSIZE_T yytnamerr (char *yyres, const char *yystr) { if (*yystr == '"') { YYSIZE_T yyn = 0; char const *yyp = yystr; for (;;) switch (*++yyp) { case '\'': case ',': goto do_not_strip_quotes; case '\\': if (*++yyp != '\\') goto do_not_strip_quotes; /* Fall through. */ default: if (yyres) yyres[yyn] = *yyp; yyn++; break; case '"': if (yyres) yyres[yyn] = '\0'; return yyn; } do_not_strip_quotes: ; } if (! yyres) return yystrlen (yystr); return yystpcpy (yyres, yystr) - yyres; } # endif /* Copy into YYRESULT an error message about the unexpected token YYCHAR while in state YYSTATE. Return the number of bytes copied, including the terminating null byte. If YYRESULT is null, do not copy anything; just return the number of bytes that would be copied. As a special case, return 0 if an ordinary "syntax error" message will do. Return YYSIZE_MAXIMUM if overflow occurs during size calculation. */ static YYSIZE_T yysyntax_error (char *yyresult, int yystate, int yychar) { int yyn = yypact[yystate]; if (! (YYPACT_NINF < yyn && yyn <= YYLAST)) return 0; else { int yytype = YYTRANSLATE (yychar); YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]); YYSIZE_T yysize = yysize0; YYSIZE_T yysize1; int yysize_overflow = 0; enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; int yyx; # if 0 /* This is so xgettext sees the translatable formats that are constructed on the fly. */ YY_("syntax error, unexpected %s"); YY_("syntax error, unexpected %s, expecting %s"); YY_("syntax error, unexpected %s, expecting %s or %s"); YY_("syntax error, unexpected %s, expecting %s or %s or %s"); YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"); # endif char *yyfmt; char const *yyf; static char const yyunexpected[] = "syntax error, unexpected %s"; static char const yyexpecting[] = ", expecting %s"; static char const yyor[] = " or %s"; char yyformat[sizeof yyunexpected + sizeof yyexpecting - 1 + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2) * (sizeof yyor - 1))]; char const *yyprefix = yyexpecting; /* Start YYX at -YYN if negative to avoid negative indexes in YYCHECK. */ int yyxbegin = yyn < 0 ? -yyn : 0; /* Stay within bounds of both yycheck and yytname. */ int yychecklim = YYLAST - yyn + 1; int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; int yycount = 1; yyarg[0] = yytname[yytype]; yyfmt = yystpcpy (yyformat, yyunexpected); for (yyx = yyxbegin; yyx < yyxend; ++yyx) if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) { if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) { yycount = 1; yysize = yysize0; yyformat[sizeof yyunexpected - 1] = '\0'; break; } yyarg[yycount++] = yytname[yyx]; yysize1 = yysize + yytnamerr (0, yytname[yyx]); yysize_overflow |= (yysize1 < yysize); yysize = yysize1; yyfmt = yystpcpy (yyfmt, yyprefix); yyprefix = yyor; } yyf = YY_(yyformat); yysize1 = yysize + yystrlen (yyf); yysize_overflow |= (yysize1 < yysize); yysize = yysize1; if (yysize_overflow) return YYSIZE_MAXIMUM; if (yyresult) { /* Avoid sprintf, as that infringes on the user's name space. Don't have undefined behavior even if the translation produced a string with the wrong number of "%s"s. */ char *yyp = yyresult; int yyi = 0; while ((*yyp = *yyf) != '\0') { if (*yyp == '%' && yyf[1] == 's' && yyi < yycount) { yyp += yytnamerr (yyp, yyarg[yyi++]); yyf += 2; } else { yyp++; yyf++; } } } return yysize; } } #endif /* YYERROR_VERBOSE */ /*-----------------------------------------------. | Release the memory associated to this symbol. | `-----------------------------------------------*/ /*ARGSUSED*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep) #else static void yydestruct (yymsg, yytype, yyvaluep) const char *yymsg; int yytype; YYSTYPE *yyvaluep; #endif { YYUSE (yyvaluep); if (!yymsg) yymsg = "Deleting"; YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); #if 0 switch (yytype) { default: break; } #endif } /* Prevent warnings from -Wmissing-prototypes. */ #ifdef YYPARSE_PARAM #if defined __STDC__ || defined __cplusplus int yyparse (void *YYPARSE_PARAM); #else int yyparse (); #endif #else /* ! YYPARSE_PARAM */ #if defined __STDC__ || defined __cplusplus int yyparse (void); #else int yyparse (); #endif #endif /* ! YYPARSE_PARAM */ /* The lookahead symbol. */ int yychar; /* The semantic value of the lookahead symbol. */ YYSTYPE yylval; /* Number of syntax errors so far. */ int yynerrs; /*-------------------------. | yyparse or yypush_parse. | `-------------------------*/ #ifdef YYPARSE_PARAM #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) int yyparse (void *YYPARSE_PARAM) #else int yyparse (YYPARSE_PARAM) void *YYPARSE_PARAM; #endif #else /* ! YYPARSE_PARAM */ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) int yyparse (void) #else int yyparse () #endif #endif { int yystate; /* Number of tokens to shift before error messages enabled. */ int yyerrstatus; /* The stacks and their tools: `yyss': related to states. `yyvs': related to semantic values. Refer to the stacks thru separate pointers, to allow yyoverflow to reallocate them elsewhere. */ /* The state stack. */ yytype_int16 yyssa[YYINITDEPTH]; yytype_int16 *yyss; yytype_int16 *yyssp; /* The semantic value stack. */ YYSTYPE yyvsa[YYINITDEPTH]; YYSTYPE *yyvs; YYSTYPE *yyvsp; YYSIZE_T yystacksize; int yyn; int yyresult; /* Lookahead token as an internal (translated) token number. */ int yytoken; /* The variables used to return semantic value and location from the action routines. */ YYSTYPE yyval; #if YYERROR_VERBOSE /* Buffer for error messages, and its allocated size. */ char yymsgbuf[128]; char *yymsg = yymsgbuf; YYSIZE_T yymsg_alloc = sizeof yymsgbuf; #endif #define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) /* The number of symbols on the RHS of the reduced rule. Keep to zero when no symbol should be popped. */ int yylen = 0; yytoken = 0; yyss = yyssa; yyvs = yyvsa; yystacksize = YYINITDEPTH; YYDPRINTF ((stderr, "Starting parse\n")); yystate = 0; yyerrstatus = 0; yynerrs = 0; yychar = YYEMPTY; /* Cause a token to be read. */ /* Initialize stack pointers. Waste one element of value and location stack so that they stay on the same level as the state stack. The wasted elements are never initialized. */ yyssp = yyss; yyvsp = yyvs; goto yysetstate; /*------------------------------------------------------------. | yynewstate -- Push a new state, which is found in yystate. | `------------------------------------------------------------*/ yynewstate: /* In all cases, when you get here, the value and location stacks have just been pushed. So pushing a state here evens the stacks. */ yyssp++; yysetstate: *yyssp = yystate; if (yyss + yystacksize - 1 <= yyssp) { /* Get the current used size of the three stacks, in elements. */ YYSIZE_T yysize = yyssp - yyss + 1; #ifdef yyoverflow { /* Give user a chance to reallocate the stack. Use copies of these so that the &'s don't force the real ones into memory. */ YYSTYPE *yyvs1 = yyvs; yytype_int16 *yyss1 = yyss; /* Each stack pointer address is followed by the size of the data in use in that stack, in bytes. This used to be a conditional around just the two extra args, but that might be undefined if yyoverflow is a macro. */ yyoverflow (YY_("memory exhausted"), &yyss1, yysize * sizeof (*yyssp), &yyvs1, yysize * sizeof (*yyvsp), &yystacksize); yyss = yyss1; yyvs = yyvs1; } #else /* no yyoverflow */ # ifndef YYSTACK_RELOCATE goto yyexhaustedlab; # else /* Extend the stack our own way. */ if (YYMAXDEPTH <= yystacksize) goto yyexhaustedlab; yystacksize *= 2; if (YYMAXDEPTH < yystacksize) yystacksize = YYMAXDEPTH; { yytype_int16 *yyss1 = yyss; union yyalloc *yyptr = (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); if (! yyptr) goto yyexhaustedlab; YYSTACK_RELOCATE (yyss_alloc, yyss); YYSTACK_RELOCATE (yyvs_alloc, yyvs); # undef YYSTACK_RELOCATE if (yyss1 != yyssa) YYSTACK_FREE (yyss1); } # endif #endif /* no yyoverflow */ yyssp = yyss + yysize - 1; yyvsp = yyvs + yysize - 1; YYDPRINTF ((stderr, "Stack size increased to %lu\n", (unsigned long int) yystacksize)); if (yyss + yystacksize - 1 <= yyssp) YYABORT; } YYDPRINTF ((stderr, "Entering state %d\n", yystate)); if (yystate == YYFINAL) YYACCEPT; goto yybackup; /*-----------. | yybackup. | `-----------*/ yybackup: /* Do appropriate processing given the current state. Read a lookahead token if we need one and don't already have one. */ /* First try to decide what to do without reference to lookahead token. */ yyn = yypact[yystate]; if (yyn == YYPACT_NINF) goto yydefault; /* Not known => get a lookahead token if don't already have one. */ /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ if (yychar == YYEMPTY) { YYDPRINTF ((stderr, "Reading a token: ")); yychar = YYLEX; } if (yychar <= YYEOF) { yychar = yytoken = YYEOF; YYDPRINTF ((stderr, "Now at end of input.\n")); } else { yytoken = YYTRANSLATE (yychar); YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); } /* If the proper action on seeing token YYTOKEN is to reduce or to detect an error, take that action. */ yyn += yytoken; if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) goto yydefault; yyn = yytable[yyn]; if (yyn <= 0) { if (yyn == 0 || yyn == YYTABLE_NINF) goto yyerrlab; yyn = -yyn; goto yyreduce; } /* Count tokens shifted since error; after three, turn off error status. */ if (yyerrstatus) yyerrstatus--; /* Shift the lookahead token. */ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); /* Discard the shifted token. */ yychar = YYEMPTY; yystate = yyn; *++yyvsp = yylval; goto yynewstate; /*-----------------------------------------------------------. | yydefault -- do the default action for the current state. | `-----------------------------------------------------------*/ yydefault: yyn = yydefact[yystate]; if (yyn == 0) goto yyerrlab; goto yyreduce; /*-----------------------------. | yyreduce -- Do a reduction. | `-----------------------------*/ yyreduce: /* yyn is the number of a rule to reduce with. */ yylen = yyr2[yyn]; /* If YYLEN is nonzero, implement the default value of the action: `$$ = $1'. Otherwise, the following line sets YYVAL to garbage. This behavior is undocumented and Bison users should not rely upon it. Assigning to YYVAL unconditionally makes the parser a bit smaller, and it avoids a GCC warning that YYVAL may be used uninitialized. */ yyval = yyvsp[1-yylen]; YY_REDUCE_PRINT (yyn); switch (yyn) { case 2: /* Line 1464 of yacc.c */ #line 225 "tptp5.y" {;} break; case 3: /* Line 1464 of yacc.c */ #line 226 "tptp5.y" {;} break; case 4: /* Line 1464 of yacc.c */ #line 229 "tptp5.y" {P_PRINT((yyval.pval));;} break; case 5: /* Line 1464 of yacc.c */ #line 230 "tptp5.y" {P_PRINT((yyval.pval));;} break; case 6: /* Line 1464 of yacc.c */ #line 233 "tptp5.y" {(yyval.pval) = P_BUILD("annotated_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 7: /* Line 1464 of yacc.c */ #line 234 "tptp5.y" {(yyval.pval) = P_BUILD("annotated_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 8: /* Line 1464 of yacc.c */ #line 235 "tptp5.y" {(yyval.pval) = P_BUILD("annotated_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 9: /* Line 1464 of yacc.c */ #line 236 "tptp5.y" {(yyval.pval) = P_BUILD("annotated_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 10: /* Line 1464 of yacc.c */ #line 239 "tptp5.y" {(yyval.pval) = P_BUILD("thf_annotated", P_TOKEN("_LIT_thf ", (yyvsp[(1) - (10)].ival)), P_TOKEN("LPAREN ", (yyvsp[(2) - (10)].ival)), (yyvsp[(3) - (10)].pval), P_TOKEN("COMMA ", (yyvsp[(4) - (10)].ival)), (yyvsp[(5) - (10)].pval), P_TOKEN("COMMA ", (yyvsp[(6) - (10)].ival)), (yyvsp[(7) - (10)].pval), (yyvsp[(8) - (10)].pval), P_TOKEN("RPAREN ", (yyvsp[(9) - (10)].ival)), P_TOKEN("PERIOD ", (yyvsp[(10) - (10)].ival)));;} break; case 11: /* Line 1464 of yacc.c */ #line 242 "tptp5.y" {(yyval.pval) = P_BUILD("tff_annotated", P_TOKEN("_LIT_tff ", (yyvsp[(1) - (10)].ival)), P_TOKEN("LPAREN ", (yyvsp[(2) - (10)].ival)), (yyvsp[(3) - (10)].pval), P_TOKEN("COMMA ", (yyvsp[(4) - (10)].ival)), (yyvsp[(5) - (10)].pval), P_TOKEN("COMMA ", (yyvsp[(6) - (10)].ival)), (yyvsp[(7) - (10)].pval), (yyvsp[(8) - (10)].pval), P_TOKEN("RPAREN ", (yyvsp[(9) - (10)].ival)), P_TOKEN("PERIOD ", (yyvsp[(10) - (10)].ival)));;} break; case 12: /* Line 1464 of yacc.c */ #line 245 "tptp5.y" {(yyval.pval) = P_BUILD("fof_annotated", P_TOKEN("_LIT_fof ", (yyvsp[(1) - (10)].ival)), P_TOKEN("LPAREN ", (yyvsp[(2) - (10)].ival)), (yyvsp[(3) - (10)].pval), P_TOKEN("COMMA ", (yyvsp[(4) - (10)].ival)), (yyvsp[(5) - (10)].pval), P_TOKEN("COMMA ", (yyvsp[(6) - (10)].ival)), (yyvsp[(7) - (10)].pval), (yyvsp[(8) - (10)].pval), P_TOKEN("RPAREN ", (yyvsp[(9) - (10)].ival)), P_TOKEN("PERIOD ", (yyvsp[(10) - (10)].ival)));;} break; case 13: /* Line 1464 of yacc.c */ #line 248 "tptp5.y" {(yyval.pval) = P_BUILD("cnf_annotated", P_TOKEN("_LIT_cnf ", (yyvsp[(1) - (10)].ival)), P_TOKEN("LPAREN ", (yyvsp[(2) - (10)].ival)), (yyvsp[(3) - (10)].pval), P_TOKEN("COMMA ", (yyvsp[(4) - (10)].ival)), (yyvsp[(5) - (10)].pval), P_TOKEN("COMMA ", (yyvsp[(6) - (10)].ival)), (yyvsp[(7) - (10)].pval), (yyvsp[(8) - (10)].pval), P_TOKEN("RPAREN ", (yyvsp[(9) - (10)].ival)), P_TOKEN("PERIOD ", (yyvsp[(10) - (10)].ival)));;} break; case 14: /* Line 1464 of yacc.c */ #line 251 "tptp5.y" {(yyval.pval) = P_BUILD("annotations", P_TOKEN("COMMA ", (yyvsp[(1) - (3)].ival)), (yyvsp[(2) - (3)].pval), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 15: /* Line 1464 of yacc.c */ #line 252 "tptp5.y" {(yyval.pval) = P_BUILD("annotations", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 16: /* Line 1464 of yacc.c */ #line 255 "tptp5.y" {(yyval.pval) = P_BUILD("formula_role", P_TOKEN("lower_word ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 17: /* Line 1464 of yacc.c */ #line 258 "tptp5.y" {(yyval.pval) = P_BUILD("thf_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 18: /* Line 1464 of yacc.c */ #line 259 "tptp5.y" {(yyval.pval) = P_BUILD("thf_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 19: /* Line 1464 of yacc.c */ #line 262 "tptp5.y" {(yyval.pval) = P_BUILD("thf_logic_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 20: /* Line 1464 of yacc.c */ #line 263 "tptp5.y" {(yyval.pval) = P_BUILD("thf_logic_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 21: /* Line 1464 of yacc.c */ #line 264 "tptp5.y" {(yyval.pval) = P_BUILD("thf_logic_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 22: /* Line 1464 of yacc.c */ #line 265 "tptp5.y" {(yyval.pval) = P_BUILD("thf_logic_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 23: /* Line 1464 of yacc.c */ #line 268 "tptp5.y" {(yyval.pval) = P_BUILD("thf_binary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 24: /* Line 1464 of yacc.c */ #line 269 "tptp5.y" {(yyval.pval) = P_BUILD("thf_binary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 25: /* Line 1464 of yacc.c */ #line 270 "tptp5.y" {(yyval.pval) = P_BUILD("thf_binary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 26: /* Line 1464 of yacc.c */ #line 273 "tptp5.y" {(yyval.pval) = P_BUILD("thf_binary_pair", (yyvsp[(1) - (3)].pval), (yyvsp[(2) - (3)].pval), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 27: /* Line 1464 of yacc.c */ #line 276 "tptp5.y" {(yyval.pval) = P_BUILD("thf_binary_tuple", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 28: /* Line 1464 of yacc.c */ #line 277 "tptp5.y" {(yyval.pval) = P_BUILD("thf_binary_tuple", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 29: /* Line 1464 of yacc.c */ #line 278 "tptp5.y" {(yyval.pval) = P_BUILD("thf_binary_tuple", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 30: /* Line 1464 of yacc.c */ #line 281 "tptp5.y" {(yyval.pval) = P_BUILD("thf_or_formula", (yyvsp[(1) - (3)].pval), P_TOKEN("VLINE ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 31: /* Line 1464 of yacc.c */ #line 282 "tptp5.y" {(yyval.pval) = P_BUILD("thf_or_formula", (yyvsp[(1) - (3)].pval), P_TOKEN("VLINE ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 32: /* Line 1464 of yacc.c */ #line 285 "tptp5.y" {(yyval.pval) = P_BUILD("thf_and_formula", (yyvsp[(1) - (3)].pval), P_TOKEN("AMPERSAND ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 33: /* Line 1464 of yacc.c */ #line 286 "tptp5.y" {(yyval.pval) = P_BUILD("thf_and_formula", (yyvsp[(1) - (3)].pval), P_TOKEN("AMPERSAND ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 34: /* Line 1464 of yacc.c */ #line 289 "tptp5.y" {(yyval.pval) = P_BUILD("thf_apply_formula", (yyvsp[(1) - (3)].pval), P_TOKEN("AT_SIGN ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 35: /* Line 1464 of yacc.c */ #line 290 "tptp5.y" {(yyval.pval) = P_BUILD("thf_apply_formula", (yyvsp[(1) - (3)].pval), P_TOKEN("AT_SIGN ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 36: /* Line 1464 of yacc.c */ #line 293 "tptp5.y" {(yyval.pval) = P_BUILD("thf_unitary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 37: /* Line 1464 of yacc.c */ #line 294 "tptp5.y" {(yyval.pval) = P_BUILD("thf_unitary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 38: /* Line 1464 of yacc.c */ #line 295 "tptp5.y" {(yyval.pval) = P_BUILD("thf_unitary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 39: /* Line 1464 of yacc.c */ #line 296 "tptp5.y" {(yyval.pval) = P_BUILD("thf_unitary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 40: /* Line 1464 of yacc.c */ #line 297 "tptp5.y" {(yyval.pval) = P_BUILD("thf_unitary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 41: /* Line 1464 of yacc.c */ #line 298 "tptp5.y" {(yyval.pval) = P_BUILD("thf_unitary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 42: /* Line 1464 of yacc.c */ #line 299 "tptp5.y" {(yyval.pval) = P_BUILD("thf_unitary_formula", P_TOKEN("LPAREN ", (yyvsp[(1) - (3)].ival)), (yyvsp[(2) - (3)].pval), P_TOKEN("RPAREN ", (yyvsp[(3) - (3)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 43: /* Line 1464 of yacc.c */ #line 302 "tptp5.y" {(yyval.pval) = P_BUILD("thf_quantified_formula", (yyvsp[(1) - (6)].pval), P_TOKEN("LBRKT ", (yyvsp[(2) - (6)].ival)), (yyvsp[(3) - (6)].pval), P_TOKEN("RBRKT ", (yyvsp[(4) - (6)].ival)), P_TOKEN("COLON ", (yyvsp[(5) - (6)].ival)), (yyvsp[(6) - (6)].pval),NULL,NULL,NULL,NULL);;} break; case 44: /* Line 1464 of yacc.c */ #line 305 "tptp5.y" {(yyval.pval) = P_BUILD("thf_variable_list", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 45: /* Line 1464 of yacc.c */ #line 306 "tptp5.y" {(yyval.pval) = P_BUILD("thf_variable_list", (yyvsp[(1) - (3)].pval), P_TOKEN("COMMA ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 46: /* Line 1464 of yacc.c */ #line 309 "tptp5.y" {(yyval.pval) = P_BUILD("thf_variable", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 47: /* Line 1464 of yacc.c */ #line 310 "tptp5.y" {(yyval.pval) = P_BUILD("thf_variable", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 48: /* Line 1464 of yacc.c */ #line 313 "tptp5.y" {(yyval.pval) = P_BUILD("thf_typed_variable", (yyvsp[(1) - (3)].pval), P_TOKEN("COLON ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 49: /* Line 1464 of yacc.c */ #line 316 "tptp5.y" {(yyval.pval) = P_BUILD("thf_unary_formula", (yyvsp[(1) - (4)].pval), P_TOKEN("LPAREN ", (yyvsp[(2) - (4)].ival)), (yyvsp[(3) - (4)].pval), P_TOKEN("RPAREN ", (yyvsp[(4) - (4)].ival)),NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 50: /* Line 1464 of yacc.c */ #line 319 "tptp5.y" {(yyval.pval) = P_BUILD("thf_type_formula", (yyvsp[(1) - (3)].pval), P_TOKEN("COLON ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 51: /* Line 1464 of yacc.c */ #line 322 "tptp5.y" {(yyval.pval) = P_BUILD("thf_typeable_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 52: /* Line 1464 of yacc.c */ #line 323 "tptp5.y" {(yyval.pval) = P_BUILD("thf_typeable_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 53: /* Line 1464 of yacc.c */ #line 324 "tptp5.y" {(yyval.pval) = P_BUILD("thf_typeable_formula", P_TOKEN("LPAREN ", (yyvsp[(1) - (3)].ival)), (yyvsp[(2) - (3)].pval), P_TOKEN("RPAREN ", (yyvsp[(3) - (3)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 54: /* Line 1464 of yacc.c */ #line 327 "tptp5.y" {(yyval.pval) = P_BUILD("thf_subtype", (yyvsp[(1) - (3)].pval), (yyvsp[(2) - (3)].pval), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 55: /* Line 1464 of yacc.c */ #line 330 "tptp5.y" {(yyval.pval) = P_BUILD("thf_top_level_type", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 56: /* Line 1464 of yacc.c */ #line 333 "tptp5.y" {(yyval.pval) = P_BUILD("thf_unitary_type", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 57: /* Line 1464 of yacc.c */ #line 336 "tptp5.y" {(yyval.pval) = P_BUILD("thf_binary_type", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 58: /* Line 1464 of yacc.c */ #line 337 "tptp5.y" {(yyval.pval) = P_BUILD("thf_binary_type", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 59: /* Line 1464 of yacc.c */ #line 338 "tptp5.y" {(yyval.pval) = P_BUILD("thf_binary_type", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 60: /* Line 1464 of yacc.c */ #line 341 "tptp5.y" {(yyval.pval) = P_BUILD("thf_mapping_type", (yyvsp[(1) - (3)].pval), P_TOKEN("arrow ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 61: /* Line 1464 of yacc.c */ #line 342 "tptp5.y" {(yyval.pval) = P_BUILD("thf_mapping_type", (yyvsp[(1) - (3)].pval), P_TOKEN("arrow ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 62: /* Line 1464 of yacc.c */ #line 345 "tptp5.y" {(yyval.pval) = P_BUILD("thf_xprod_type", (yyvsp[(1) - (3)].pval), P_TOKEN("STAR ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 63: /* Line 1464 of yacc.c */ #line 346 "tptp5.y" {(yyval.pval) = P_BUILD("thf_xprod_type", (yyvsp[(1) - (3)].pval), P_TOKEN("STAR ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 64: /* Line 1464 of yacc.c */ #line 349 "tptp5.y" {(yyval.pval) = P_BUILD("thf_union_type", (yyvsp[(1) - (3)].pval), P_TOKEN("plus ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 65: /* Line 1464 of yacc.c */ #line 350 "tptp5.y" {(yyval.pval) = P_BUILD("thf_union_type", (yyvsp[(1) - (3)].pval), P_TOKEN("plus ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 66: /* Line 1464 of yacc.c */ #line 353 "tptp5.y" {(yyval.pval) = P_BUILD("thf_atom", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 67: /* Line 1464 of yacc.c */ #line 354 "tptp5.y" {(yyval.pval) = P_BUILD("thf_atom", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 68: /* Line 1464 of yacc.c */ #line 357 "tptp5.y" {(yyval.pval) = P_BUILD("thf_tuple", P_TOKEN("LBRKT ", (yyvsp[(1) - (2)].ival)), P_TOKEN("RBRKT ", (yyvsp[(2) - (2)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 69: /* Line 1464 of yacc.c */ #line 358 "tptp5.y" {(yyval.pval) = P_BUILD("thf_tuple", P_TOKEN("LBRKT ", (yyvsp[(1) - (3)].ival)), (yyvsp[(2) - (3)].pval), P_TOKEN("RBRKT ", (yyvsp[(3) - (3)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 70: /* Line 1464 of yacc.c */ #line 361 "tptp5.y" {(yyval.pval) = P_BUILD("thf_tuple_list", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 71: /* Line 1464 of yacc.c */ #line 362 "tptp5.y" {(yyval.pval) = P_BUILD("thf_tuple_list", (yyvsp[(1) - (3)].pval), P_TOKEN("COMMA ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 72: /* Line 1464 of yacc.c */ #line 365 "tptp5.y" {(yyval.pval) = P_BUILD("thf_let", P_TOKEN("COLON_EQUALS ", (yyvsp[(1) - (6)].ival)), P_TOKEN("LBRKT ", (yyvsp[(2) - (6)].ival)), (yyvsp[(3) - (6)].pval), P_TOKEN("RBRKT ", (yyvsp[(4) - (6)].ival)), P_TOKEN("COLON ", (yyvsp[(5) - (6)].ival)), (yyvsp[(6) - (6)].pval),NULL,NULL,NULL,NULL);;} break; case 73: /* Line 1464 of yacc.c */ #line 368 "tptp5.y" {(yyval.pval) = P_BUILD("thf_let_list", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 74: /* Line 1464 of yacc.c */ #line 369 "tptp5.y" {(yyval.pval) = P_BUILD("thf_let_list", (yyvsp[(1) - (3)].pval), P_TOKEN("COMMA ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 75: /* Line 1464 of yacc.c */ #line 372 "tptp5.y" {(yyval.pval) = P_BUILD("thf_defined_var", (yyvsp[(1) - (3)].pval), P_TOKEN("COLON_EQUALS ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 76: /* Line 1464 of yacc.c */ #line 373 "tptp5.y" {(yyval.pval) = P_BUILD("thf_defined_var", P_TOKEN("LPAREN ", (yyvsp[(1) - (3)].ival)), (yyvsp[(2) - (3)].pval), P_TOKEN("RPAREN ", (yyvsp[(3) - (3)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 77: /* Line 1464 of yacc.c */ #line 376 "tptp5.y" {(yyval.pval) = P_BUILD("thf_conditional", P_TOKEN("_DLR_itef ", (yyvsp[(1) - (8)].ival)), P_TOKEN("LPAREN ", (yyvsp[(2) - (8)].ival)), (yyvsp[(3) - (8)].pval), P_TOKEN("COMMA ", (yyvsp[(4) - (8)].ival)), (yyvsp[(5) - (8)].pval), P_TOKEN("COMMA ", (yyvsp[(6) - (8)].ival)), (yyvsp[(7) - (8)].pval), P_TOKEN("RPAREN ", (yyvsp[(8) - (8)].ival)),NULL,NULL);;} break; case 78: /* Line 1464 of yacc.c */ #line 379 "tptp5.y" {(yyval.pval) = P_BUILD("thf_sequent", (yyvsp[(1) - (3)].pval), (yyvsp[(2) - (3)].pval), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 79: /* Line 1464 of yacc.c */ #line 380 "tptp5.y" {(yyval.pval) = P_BUILD("thf_sequent", P_TOKEN("LPAREN ", (yyvsp[(1) - (3)].ival)), (yyvsp[(2) - (3)].pval), P_TOKEN("RPAREN ", (yyvsp[(3) - (3)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 80: /* Line 1464 of yacc.c */ #line 383 "tptp5.y" {(yyval.pval) = P_BUILD("tff_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 81: /* Line 1464 of yacc.c */ #line 384 "tptp5.y" {(yyval.pval) = P_BUILD("tff_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 82: /* Line 1464 of yacc.c */ #line 385 "tptp5.y" {(yyval.pval) = P_BUILD("tff_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 83: /* Line 1464 of yacc.c */ #line 388 "tptp5.y" {(yyval.pval) = P_BUILD("tff_logic_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 84: /* Line 1464 of yacc.c */ #line 389 "tptp5.y" {(yyval.pval) = P_BUILD("tff_logic_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 85: /* Line 1464 of yacc.c */ #line 392 "tptp5.y" {(yyval.pval) = P_BUILD("tff_binary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 86: /* Line 1464 of yacc.c */ #line 393 "tptp5.y" {(yyval.pval) = P_BUILD("tff_binary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 87: /* Line 1464 of yacc.c */ #line 396 "tptp5.y" {(yyval.pval) = P_BUILD("tff_binary_nonassoc", (yyvsp[(1) - (3)].pval), (yyvsp[(2) - (3)].pval), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 88: /* Line 1464 of yacc.c */ #line 399 "tptp5.y" {(yyval.pval) = P_BUILD("tff_binary_assoc", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 89: /* Line 1464 of yacc.c */ #line 400 "tptp5.y" {(yyval.pval) = P_BUILD("tff_binary_assoc", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 90: /* Line 1464 of yacc.c */ #line 403 "tptp5.y" {(yyval.pval) = P_BUILD("tff_or_formula", (yyvsp[(1) - (3)].pval), P_TOKEN("VLINE ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 91: /* Line 1464 of yacc.c */ #line 404 "tptp5.y" {(yyval.pval) = P_BUILD("tff_or_formula", (yyvsp[(1) - (3)].pval), P_TOKEN("VLINE ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 92: /* Line 1464 of yacc.c */ #line 407 "tptp5.y" {(yyval.pval) = P_BUILD("tff_and_formula", (yyvsp[(1) - (3)].pval), P_TOKEN("AMPERSAND ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 93: /* Line 1464 of yacc.c */ #line 408 "tptp5.y" {(yyval.pval) = P_BUILD("tff_and_formula", (yyvsp[(1) - (3)].pval), P_TOKEN("AMPERSAND ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 94: /* Line 1464 of yacc.c */ #line 411 "tptp5.y" {(yyval.pval) = P_BUILD("tff_unitary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 95: /* Line 1464 of yacc.c */ #line 412 "tptp5.y" {(yyval.pval) = P_BUILD("tff_unitary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 96: /* Line 1464 of yacc.c */ #line 413 "tptp5.y" {(yyval.pval) = P_BUILD("tff_unitary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 97: /* Line 1464 of yacc.c */ #line 414 "tptp5.y" {(yyval.pval) = P_BUILD("tff_unitary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 98: /* Line 1464 of yacc.c */ #line 415 "tptp5.y" {(yyval.pval) = P_BUILD("tff_unitary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 99: /* Line 1464 of yacc.c */ #line 416 "tptp5.y" {(yyval.pval) = P_BUILD("tff_unitary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 100: /* Line 1464 of yacc.c */ #line 417 "tptp5.y" {(yyval.pval) = P_BUILD("tff_unitary_formula", P_TOKEN("LPAREN ", (yyvsp[(1) - (3)].ival)), (yyvsp[(2) - (3)].pval), P_TOKEN("RPAREN ", (yyvsp[(3) - (3)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 101: /* Line 1464 of yacc.c */ #line 420 "tptp5.y" {(yyval.pval) = P_BUILD("tff_quantified_formula", (yyvsp[(1) - (6)].pval), P_TOKEN("LBRKT ", (yyvsp[(2) - (6)].ival)), (yyvsp[(3) - (6)].pval), P_TOKEN("RBRKT ", (yyvsp[(4) - (6)].ival)), P_TOKEN("COLON ", (yyvsp[(5) - (6)].ival)), (yyvsp[(6) - (6)].pval),NULL,NULL,NULL,NULL);;} break; case 102: /* Line 1464 of yacc.c */ #line 423 "tptp5.y" {(yyval.pval) = P_BUILD("tff_variable_list", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 103: /* Line 1464 of yacc.c */ #line 424 "tptp5.y" {(yyval.pval) = P_BUILD("tff_variable_list", (yyvsp[(1) - (3)].pval), P_TOKEN("COMMA ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 104: /* Line 1464 of yacc.c */ #line 427 "tptp5.y" {(yyval.pval) = P_BUILD("tff_variable", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 105: /* Line 1464 of yacc.c */ #line 428 "tptp5.y" {(yyval.pval) = P_BUILD("tff_variable", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 106: /* Line 1464 of yacc.c */ #line 431 "tptp5.y" {(yyval.pval) = P_BUILD("tff_typed_variable", (yyvsp[(1) - (3)].pval), P_TOKEN("COLON ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 107: /* Line 1464 of yacc.c */ #line 434 "tptp5.y" {(yyval.pval) = P_BUILD("tff_unary_formula", (yyvsp[(1) - (2)].pval), (yyvsp[(2) - (2)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 108: /* Line 1464 of yacc.c */ #line 435 "tptp5.y" {(yyval.pval) = P_BUILD("tff_unary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 109: /* Line 1464 of yacc.c */ #line 438 "tptp5.y" {(yyval.pval) = P_BUILD("tff_typed_atom", (yyvsp[(1) - (3)].pval), P_TOKEN("COLON ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 110: /* Line 1464 of yacc.c */ #line 439 "tptp5.y" {(yyval.pval) = P_BUILD("tff_typed_atom", P_TOKEN("LPAREN ", (yyvsp[(1) - (3)].ival)), (yyvsp[(2) - (3)].pval), P_TOKEN("RPAREN ", (yyvsp[(3) - (3)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 111: /* Line 1464 of yacc.c */ #line 442 "tptp5.y" {(yyval.pval) = P_BUILD("tff_untyped_atom", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 112: /* Line 1464 of yacc.c */ #line 443 "tptp5.y" {(yyval.pval) = P_BUILD("tff_untyped_atom", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 113: /* Line 1464 of yacc.c */ #line 446 "tptp5.y" {(yyval.pval) = P_BUILD("tff_top_level_type", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 114: /* Line 1464 of yacc.c */ #line 447 "tptp5.y" {(yyval.pval) = P_BUILD("tff_top_level_type", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 115: /* Line 1464 of yacc.c */ #line 450 "tptp5.y" {(yyval.pval) = P_BUILD("tff_unitary_type", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 116: /* Line 1464 of yacc.c */ #line 451 "tptp5.y" {(yyval.pval) = P_BUILD("tff_unitary_type", P_TOKEN("LPAREN ", (yyvsp[(1) - (3)].ival)), (yyvsp[(2) - (3)].pval), P_TOKEN("RPAREN ", (yyvsp[(3) - (3)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 117: /* Line 1464 of yacc.c */ #line 454 "tptp5.y" {(yyval.pval) = P_BUILD("tff_atomic_type", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 118: /* Line 1464 of yacc.c */ #line 455 "tptp5.y" {(yyval.pval) = P_BUILD("tff_atomic_type", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 119: /* Line 1464 of yacc.c */ #line 458 "tptp5.y" {(yyval.pval) = P_BUILD("tff_mapping_type", (yyvsp[(1) - (3)].pval), P_TOKEN("arrow ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 120: /* Line 1464 of yacc.c */ #line 459 "tptp5.y" {(yyval.pval) = P_BUILD("tff_mapping_type", P_TOKEN("LPAREN ", (yyvsp[(1) - (3)].ival)), (yyvsp[(2) - (3)].pval), P_TOKEN("RPAREN ", (yyvsp[(3) - (3)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 121: /* Line 1464 of yacc.c */ #line 462 "tptp5.y" {(yyval.pval) = P_BUILD("tff_xprod_type", (yyvsp[(1) - (3)].pval), P_TOKEN("STAR ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 122: /* Line 1464 of yacc.c */ #line 463 "tptp5.y" {(yyval.pval) = P_BUILD("tff_xprod_type", (yyvsp[(1) - (3)].pval), P_TOKEN("STAR ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 123: /* Line 1464 of yacc.c */ #line 464 "tptp5.y" {(yyval.pval) = P_BUILD("tff_xprod_type", P_TOKEN("LPAREN ", (yyvsp[(1) - (3)].ival)), (yyvsp[(2) - (3)].pval), P_TOKEN("RPAREN ", (yyvsp[(3) - (3)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 124: /* Line 1464 of yacc.c */ #line 467 "tptp5.y" {(yyval.pval) = P_BUILD("tff_let", P_TOKEN("COLON_EQUALS ", (yyvsp[(1) - (6)].ival)), P_TOKEN("LBRKT ", (yyvsp[(2) - (6)].ival)), (yyvsp[(3) - (6)].pval), P_TOKEN("RBRKT ", (yyvsp[(4) - (6)].ival)), P_TOKEN("COLON ", (yyvsp[(5) - (6)].ival)), (yyvsp[(6) - (6)].pval),NULL,NULL,NULL,NULL);;} break; case 125: /* Line 1464 of yacc.c */ #line 470 "tptp5.y" {(yyval.pval) = P_BUILD("tff_let_list", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 126: /* Line 1464 of yacc.c */ #line 471 "tptp5.y" {(yyval.pval) = P_BUILD("tff_let_list", (yyvsp[(1) - (3)].pval), P_TOKEN("COMMA ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 127: /* Line 1464 of yacc.c */ #line 474 "tptp5.y" {(yyval.pval) = P_BUILD("tff_defined_var", (yyvsp[(1) - (3)].pval), P_TOKEN("COLON_EQUALS ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 128: /* Line 1464 of yacc.c */ #line 475 "tptp5.y" {(yyval.pval) = P_BUILD("tff_defined_var", (yyvsp[(1) - (4)].pval), P_TOKEN("COLON ", (yyvsp[(2) - (4)].ival)), P_TOKEN("MINUS ", (yyvsp[(3) - (4)].ival)), (yyvsp[(4) - (4)].pval),NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 129: /* Line 1464 of yacc.c */ #line 476 "tptp5.y" {(yyval.pval) = P_BUILD("tff_defined_var", P_TOKEN("LPAREN ", (yyvsp[(1) - (3)].ival)), (yyvsp[(2) - (3)].pval), P_TOKEN("RPAREN ", (yyvsp[(3) - (3)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 130: /* Line 1464 of yacc.c */ #line 479 "tptp5.y" {(yyval.pval) = P_BUILD("tff_conditional", P_TOKEN("_DLR_itef ", (yyvsp[(1) - (8)].ival)), P_TOKEN("LPAREN ", (yyvsp[(2) - (8)].ival)), (yyvsp[(3) - (8)].pval), P_TOKEN("COMMA ", (yyvsp[(4) - (8)].ival)), (yyvsp[(5) - (8)].pval), P_TOKEN("COMMA ", (yyvsp[(6) - (8)].ival)), (yyvsp[(7) - (8)].pval), P_TOKEN("RPAREN ", (yyvsp[(8) - (8)].ival)),NULL,NULL);;} break; case 131: /* Line 1464 of yacc.c */ #line 482 "tptp5.y" {(yyval.pval) = P_BUILD("tff_sequent", (yyvsp[(1) - (3)].pval), (yyvsp[(2) - (3)].pval), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 132: /* Line 1464 of yacc.c */ #line 483 "tptp5.y" {(yyval.pval) = P_BUILD("tff_sequent", P_TOKEN("LPAREN ", (yyvsp[(1) - (3)].ival)), (yyvsp[(2) - (3)].pval), P_TOKEN("RPAREN ", (yyvsp[(3) - (3)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 133: /* Line 1464 of yacc.c */ #line 486 "tptp5.y" {(yyval.pval) = P_BUILD("fof_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 134: /* Line 1464 of yacc.c */ #line 487 "tptp5.y" {(yyval.pval) = P_BUILD("fof_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 135: /* Line 1464 of yacc.c */ #line 490 "tptp5.y" {(yyval.pval) = P_BUILD("fof_logic_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 136: /* Line 1464 of yacc.c */ #line 491 "tptp5.y" {(yyval.pval) = P_BUILD("fof_logic_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 137: /* Line 1464 of yacc.c */ #line 494 "tptp5.y" {(yyval.pval) = P_BUILD("fof_binary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 138: /* Line 1464 of yacc.c */ #line 495 "tptp5.y" {(yyval.pval) = P_BUILD("fof_binary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 139: /* Line 1464 of yacc.c */ #line 498 "tptp5.y" {(yyval.pval) = P_BUILD("fof_binary_nonassoc", (yyvsp[(1) - (3)].pval), (yyvsp[(2) - (3)].pval), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 140: /* Line 1464 of yacc.c */ #line 501 "tptp5.y" {(yyval.pval) = P_BUILD("fof_binary_assoc", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 141: /* Line 1464 of yacc.c */ #line 502 "tptp5.y" {(yyval.pval) = P_BUILD("fof_binary_assoc", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 142: /* Line 1464 of yacc.c */ #line 505 "tptp5.y" {(yyval.pval) = P_BUILD("fof_or_formula", (yyvsp[(1) - (3)].pval), P_TOKEN("VLINE ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 143: /* Line 1464 of yacc.c */ #line 506 "tptp5.y" {(yyval.pval) = P_BUILD("fof_or_formula", (yyvsp[(1) - (3)].pval), P_TOKEN("VLINE ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 144: /* Line 1464 of yacc.c */ #line 509 "tptp5.y" {(yyval.pval) = P_BUILD("fof_and_formula", (yyvsp[(1) - (3)].pval), P_TOKEN("AMPERSAND ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 145: /* Line 1464 of yacc.c */ #line 510 "tptp5.y" {(yyval.pval) = P_BUILD("fof_and_formula", (yyvsp[(1) - (3)].pval), P_TOKEN("AMPERSAND ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 146: /* Line 1464 of yacc.c */ #line 513 "tptp5.y" {(yyval.pval) = P_BUILD("fof_unitary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 147: /* Line 1464 of yacc.c */ #line 514 "tptp5.y" {(yyval.pval) = P_BUILD("fof_unitary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 148: /* Line 1464 of yacc.c */ #line 515 "tptp5.y" {(yyval.pval) = P_BUILD("fof_unitary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 149: /* Line 1464 of yacc.c */ #line 516 "tptp5.y" {(yyval.pval) = P_BUILD("fof_unitary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 150: /* Line 1464 of yacc.c */ #line 517 "tptp5.y" {(yyval.pval) = P_BUILD("fof_unitary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 151: /* Line 1464 of yacc.c */ #line 518 "tptp5.y" {(yyval.pval) = P_BUILD("fof_unitary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 152: /* Line 1464 of yacc.c */ #line 519 "tptp5.y" {(yyval.pval) = P_BUILD("fof_unitary_formula", P_TOKEN("LPAREN ", (yyvsp[(1) - (3)].ival)), (yyvsp[(2) - (3)].pval), P_TOKEN("RPAREN ", (yyvsp[(3) - (3)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 153: /* Line 1464 of yacc.c */ #line 522 "tptp5.y" {(yyval.pval) = P_BUILD("fof_quantified_formula", (yyvsp[(1) - (6)].pval), P_TOKEN("LBRKT ", (yyvsp[(2) - (6)].ival)), (yyvsp[(3) - (6)].pval), P_TOKEN("RBRKT ", (yyvsp[(4) - (6)].ival)), P_TOKEN("COLON ", (yyvsp[(5) - (6)].ival)), (yyvsp[(6) - (6)].pval),NULL,NULL,NULL,NULL);;} break; case 154: /* Line 1464 of yacc.c */ #line 525 "tptp5.y" {(yyval.pval) = P_BUILD("fof_variable_list", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 155: /* Line 1464 of yacc.c */ #line 526 "tptp5.y" {(yyval.pval) = P_BUILD("fof_variable_list", (yyvsp[(1) - (3)].pval), P_TOKEN("COMMA ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 156: /* Line 1464 of yacc.c */ #line 529 "tptp5.y" {(yyval.pval) = P_BUILD("fof_unary_formula", (yyvsp[(1) - (2)].pval), (yyvsp[(2) - (2)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 157: /* Line 1464 of yacc.c */ #line 530 "tptp5.y" {(yyval.pval) = P_BUILD("fof_unary_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 158: /* Line 1464 of yacc.c */ #line 533 "tptp5.y" {(yyval.pval) = P_BUILD("fof_let", P_TOKEN("COLON_EQUALS ", (yyvsp[(1) - (6)].ival)), P_TOKEN("LBRKT ", (yyvsp[(2) - (6)].ival)), (yyvsp[(3) - (6)].pval), P_TOKEN("RBRKT ", (yyvsp[(4) - (6)].ival)), P_TOKEN("COLON ", (yyvsp[(5) - (6)].ival)), (yyvsp[(6) - (6)].pval),NULL,NULL,NULL,NULL);;} break; case 159: /* Line 1464 of yacc.c */ #line 536 "tptp5.y" {(yyval.pval) = P_BUILD("fof_let_list", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 160: /* Line 1464 of yacc.c */ #line 537 "tptp5.y" {(yyval.pval) = P_BUILD("fof_let_list", (yyvsp[(1) - (3)].pval), P_TOKEN("COMMA ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 161: /* Line 1464 of yacc.c */ #line 540 "tptp5.y" {(yyval.pval) = P_BUILD("fof_defined_var", (yyvsp[(1) - (3)].pval), P_TOKEN("COLON_EQUALS ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 162: /* Line 1464 of yacc.c */ #line 541 "tptp5.y" {(yyval.pval) = P_BUILD("fof_defined_var", (yyvsp[(1) - (4)].pval), P_TOKEN("COLON ", (yyvsp[(2) - (4)].ival)), P_TOKEN("MINUS ", (yyvsp[(3) - (4)].ival)), (yyvsp[(4) - (4)].pval),NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 163: /* Line 1464 of yacc.c */ #line 542 "tptp5.y" {(yyval.pval) = P_BUILD("fof_defined_var", P_TOKEN("LPAREN ", (yyvsp[(1) - (3)].ival)), (yyvsp[(2) - (3)].pval), P_TOKEN("RPAREN ", (yyvsp[(3) - (3)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 164: /* Line 1464 of yacc.c */ #line 545 "tptp5.y" {(yyval.pval) = P_BUILD("fof_conditional", P_TOKEN("_DLR_itef ", (yyvsp[(1) - (8)].ival)), P_TOKEN("LPAREN ", (yyvsp[(2) - (8)].ival)), (yyvsp[(3) - (8)].pval), P_TOKEN("COMMA ", (yyvsp[(4) - (8)].ival)), (yyvsp[(5) - (8)].pval), P_TOKEN("COMMA ", (yyvsp[(6) - (8)].ival)), (yyvsp[(7) - (8)].pval), P_TOKEN("RPAREN ", (yyvsp[(8) - (8)].ival)),NULL,NULL);;} break; case 165: /* Line 1464 of yacc.c */ #line 548 "tptp5.y" {(yyval.pval) = P_BUILD("fof_sequent", (yyvsp[(1) - (3)].pval), (yyvsp[(2) - (3)].pval), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 166: /* Line 1464 of yacc.c */ #line 549 "tptp5.y" {(yyval.pval) = P_BUILD("fof_sequent", P_TOKEN("LPAREN ", (yyvsp[(1) - (3)].ival)), (yyvsp[(2) - (3)].pval), P_TOKEN("RPAREN ", (yyvsp[(3) - (3)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 167: /* Line 1464 of yacc.c */ #line 552 "tptp5.y" {(yyval.pval) = P_BUILD("cnf_formula", P_TOKEN("LPAREN ", (yyvsp[(1) - (3)].ival)), (yyvsp[(2) - (3)].pval), P_TOKEN("RPAREN ", (yyvsp[(3) - (3)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 168: /* Line 1464 of yacc.c */ #line 553 "tptp5.y" {(yyval.pval) = P_BUILD("cnf_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 169: /* Line 1464 of yacc.c */ #line 556 "tptp5.y" {(yyval.pval) = P_BUILD("disjunction", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 170: /* Line 1464 of yacc.c */ #line 557 "tptp5.y" {(yyval.pval) = P_BUILD("disjunction", (yyvsp[(1) - (3)].pval), P_TOKEN("VLINE ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 171: /* Line 1464 of yacc.c */ #line 560 "tptp5.y" {(yyval.pval) = P_BUILD("literal", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 172: /* Line 1464 of yacc.c */ #line 561 "tptp5.y" {(yyval.pval) = P_BUILD("literal", P_TOKEN("TILDE ", (yyvsp[(1) - (2)].ival)), (yyvsp[(2) - (2)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 173: /* Line 1464 of yacc.c */ #line 562 "tptp5.y" {(yyval.pval) = P_BUILD("literal", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 174: /* Line 1464 of yacc.c */ #line 565 "tptp5.y" {(yyval.pval) = P_BUILD("thf_conn_term", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 175: /* Line 1464 of yacc.c */ #line 566 "tptp5.y" {(yyval.pval) = P_BUILD("thf_conn_term", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 176: /* Line 1464 of yacc.c */ #line 567 "tptp5.y" {(yyval.pval) = P_BUILD("thf_conn_term", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 177: /* Line 1464 of yacc.c */ #line 570 "tptp5.y" {(yyval.pval) = P_BUILD("fol_infix_unary", (yyvsp[(1) - (3)].pval), (yyvsp[(2) - (3)].pval), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 178: /* Line 1464 of yacc.c */ #line 573 "tptp5.y" {(yyval.pval) = P_BUILD("thf_quantifier", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 179: /* Line 1464 of yacc.c */ #line 574 "tptp5.y" {(yyval.pval) = P_BUILD("thf_quantifier", P_TOKEN("CARET ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 180: /* Line 1464 of yacc.c */ #line 575 "tptp5.y" {(yyval.pval) = P_BUILD("thf_quantifier", P_TOKEN("EXCLAMATION_GREATER ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 181: /* Line 1464 of yacc.c */ #line 576 "tptp5.y" {(yyval.pval) = P_BUILD("thf_quantifier", P_TOKEN("QUESTION_STAR ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 182: /* Line 1464 of yacc.c */ #line 577 "tptp5.y" {(yyval.pval) = P_BUILD("thf_quantifier", P_TOKEN("AT_SIGN_PLUS ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 183: /* Line 1464 of yacc.c */ #line 578 "tptp5.y" {(yyval.pval) = P_BUILD("thf_quantifier", P_TOKEN("AT_SIGN_MINUS ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 184: /* Line 1464 of yacc.c */ #line 581 "tptp5.y" {(yyval.pval) = P_BUILD("thf_pair_connective", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 185: /* Line 1464 of yacc.c */ #line 582 "tptp5.y" {(yyval.pval) = P_BUILD("thf_pair_connective", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 186: /* Line 1464 of yacc.c */ #line 583 "tptp5.y" {(yyval.pval) = P_BUILD("thf_pair_connective", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 187: /* Line 1464 of yacc.c */ #line 586 "tptp5.y" {(yyval.pval) = P_BUILD("thf_unary_connective", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 188: /* Line 1464 of yacc.c */ #line 587 "tptp5.y" {(yyval.pval) = P_BUILD("thf_unary_connective", P_TOKEN("EXCLAMATION_EXCLAMATION ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 189: /* Line 1464 of yacc.c */ #line 588 "tptp5.y" {(yyval.pval) = P_BUILD("thf_unary_connective", P_TOKEN("QUESTION_QUESTION ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 190: /* Line 1464 of yacc.c */ #line 591 "tptp5.y" {(yyval.pval) = P_BUILD("subtype_sign", P_TOKEN("less_sign ", (yyvsp[(1) - (2)].ival)), P_TOKEN("less_sign ", (yyvsp[(2) - (2)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 191: /* Line 1464 of yacc.c */ #line 594 "tptp5.y" {(yyval.pval) = P_BUILD("fol_quantifier", P_TOKEN("EXCLAMATION ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 192: /* Line 1464 of yacc.c */ #line 595 "tptp5.y" {(yyval.pval) = P_BUILD("fol_quantifier", P_TOKEN("QUESTION ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 193: /* Line 1464 of yacc.c */ #line 598 "tptp5.y" {(yyval.pval) = P_BUILD("binary_connective", P_TOKEN("LESS_EQUALS_GREATER ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 194: /* Line 1464 of yacc.c */ #line 599 "tptp5.y" {(yyval.pval) = P_BUILD("binary_connective", P_TOKEN("EQUALS_GREATER ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 195: /* Line 1464 of yacc.c */ #line 600 "tptp5.y" {(yyval.pval) = P_BUILD("binary_connective", P_TOKEN("LESS_EQUALS ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 196: /* Line 1464 of yacc.c */ #line 601 "tptp5.y" {(yyval.pval) = P_BUILD("binary_connective", P_TOKEN("LESS_TILDE_GREATER ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 197: /* Line 1464 of yacc.c */ #line 602 "tptp5.y" {(yyval.pval) = P_BUILD("binary_connective", P_TOKEN("TILDE_VLINE ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 198: /* Line 1464 of yacc.c */ #line 603 "tptp5.y" {(yyval.pval) = P_BUILD("binary_connective", P_TOKEN("TILDE_AMPERSAND ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 199: /* Line 1464 of yacc.c */ #line 606 "tptp5.y" {(yyval.pval) = P_BUILD("assoc_connective", P_TOKEN("VLINE ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 200: /* Line 1464 of yacc.c */ #line 607 "tptp5.y" {(yyval.pval) = P_BUILD("assoc_connective", P_TOKEN("AMPERSAND ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 201: /* Line 1464 of yacc.c */ #line 610 "tptp5.y" {(yyval.pval) = P_BUILD("unary_connective", P_TOKEN("TILDE ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 202: /* Line 1464 of yacc.c */ #line 613 "tptp5.y" {(yyval.pval) = P_BUILD("gentzen_arrow", P_TOKEN("MINUS_MINUS_GREATER ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 203: /* Line 1464 of yacc.c */ #line 616 "tptp5.y" {(yyval.pval) = P_BUILD("defined_type", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 204: /* Line 1464 of yacc.c */ #line 619 "tptp5.y" {(yyval.pval) = P_BUILD("atomic_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 205: /* Line 1464 of yacc.c */ #line 620 "tptp5.y" {(yyval.pval) = P_BUILD("atomic_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 206: /* Line 1464 of yacc.c */ #line 621 "tptp5.y" {(yyval.pval) = P_BUILD("atomic_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 207: /* Line 1464 of yacc.c */ #line 624 "tptp5.y" {(yyval.pval) = P_BUILD("plain_atomic_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 208: /* Line 1464 of yacc.c */ #line 627 "tptp5.y" {(yyval.pval) = P_BUILD("defined_atomic_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 209: /* Line 1464 of yacc.c */ #line 628 "tptp5.y" {(yyval.pval) = P_BUILD("defined_atomic_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 210: /* Line 1464 of yacc.c */ #line 631 "tptp5.y" {(yyval.pval) = P_BUILD("defined_plain_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 211: /* Line 1464 of yacc.c */ #line 634 "tptp5.y" {(yyval.pval) = P_BUILD("defined_infix_formula", (yyvsp[(1) - (3)].pval), (yyvsp[(2) - (3)].pval), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 212: /* Line 1464 of yacc.c */ #line 637 "tptp5.y" {(yyval.pval) = P_BUILD("defined_infix_pred", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 213: /* Line 1464 of yacc.c */ #line 640 "tptp5.y" {(yyval.pval) = P_BUILD("infix_equality", P_TOKEN("EQUALS ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 214: /* Line 1464 of yacc.c */ #line 643 "tptp5.y" {(yyval.pval) = P_BUILD("infix_inequality", P_TOKEN("EXCLAMATION_EQUALS ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 215: /* Line 1464 of yacc.c */ #line 646 "tptp5.y" {(yyval.pval) = P_BUILD("system_atomic_formula", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 216: /* Line 1464 of yacc.c */ #line 649 "tptp5.y" {(yyval.pval) = P_BUILD("term", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 217: /* Line 1464 of yacc.c */ #line 650 "tptp5.y" {(yyval.pval) = P_BUILD("term", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 218: /* Line 1464 of yacc.c */ #line 651 "tptp5.y" {(yyval.pval) = P_BUILD("term", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 219: /* Line 1464 of yacc.c */ #line 654 "tptp5.y" {(yyval.pval) = P_BUILD("function_term", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 220: /* Line 1464 of yacc.c */ #line 655 "tptp5.y" {(yyval.pval) = P_BUILD("function_term", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 221: /* Line 1464 of yacc.c */ #line 656 "tptp5.y" {(yyval.pval) = P_BUILD("function_term", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 222: /* Line 1464 of yacc.c */ #line 659 "tptp5.y" {(yyval.pval) = P_BUILD("plain_term", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 223: /* Line 1464 of yacc.c */ #line 660 "tptp5.y" {(yyval.pval) = P_BUILD("plain_term", (yyvsp[(1) - (4)].pval), P_TOKEN("LPAREN ", (yyvsp[(2) - (4)].ival)), (yyvsp[(3) - (4)].pval), P_TOKEN("RPAREN ", (yyvsp[(4) - (4)].ival)),NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 224: /* Line 1464 of yacc.c */ #line 663 "tptp5.y" {(yyval.pval) = P_BUILD("constant", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 225: /* Line 1464 of yacc.c */ #line 666 "tptp5.y" {(yyval.pval) = P_BUILD("functor", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 226: /* Line 1464 of yacc.c */ #line 669 "tptp5.y" {(yyval.pval) = P_BUILD("defined_term", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 227: /* Line 1464 of yacc.c */ #line 670 "tptp5.y" {(yyval.pval) = P_BUILD("defined_term", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 228: /* Line 1464 of yacc.c */ #line 673 "tptp5.y" {(yyval.pval) = P_BUILD("defined_atom", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 229: /* Line 1464 of yacc.c */ #line 674 "tptp5.y" {(yyval.pval) = P_BUILD("defined_atom", P_TOKEN("distinct_object ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 230: /* Line 1464 of yacc.c */ #line 677 "tptp5.y" {(yyval.pval) = P_BUILD("defined_atomic_term", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 231: /* Line 1464 of yacc.c */ #line 680 "tptp5.y" {(yyval.pval) = P_BUILD("defined_plain_term", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 232: /* Line 1464 of yacc.c */ #line 681 "tptp5.y" {(yyval.pval) = P_BUILD("defined_plain_term", (yyvsp[(1) - (4)].pval), P_TOKEN("LPAREN ", (yyvsp[(2) - (4)].ival)), (yyvsp[(3) - (4)].pval), P_TOKEN("RPAREN ", (yyvsp[(4) - (4)].ival)),NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 233: /* Line 1464 of yacc.c */ #line 684 "tptp5.y" {(yyval.pval) = P_BUILD("defined_constant", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 234: /* Line 1464 of yacc.c */ #line 687 "tptp5.y" {(yyval.pval) = P_BUILD("defined_functor", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 235: /* Line 1464 of yacc.c */ #line 690 "tptp5.y" {(yyval.pval) = P_BUILD("system_term", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 236: /* Line 1464 of yacc.c */ #line 691 "tptp5.y" {(yyval.pval) = P_BUILD("system_term", (yyvsp[(1) - (4)].pval), P_TOKEN("LPAREN ", (yyvsp[(2) - (4)].ival)), (yyvsp[(3) - (4)].pval), P_TOKEN("RPAREN ", (yyvsp[(4) - (4)].ival)),NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 237: /* Line 1464 of yacc.c */ #line 694 "tptp5.y" {(yyval.pval) = P_BUILD("system_constant", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 238: /* Line 1464 of yacc.c */ #line 697 "tptp5.y" {(yyval.pval) = P_BUILD("system_functor", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 239: /* Line 1464 of yacc.c */ #line 700 "tptp5.y" {(yyval.pval) = P_BUILD("variable", P_TOKEN("upper_word ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 240: /* Line 1464 of yacc.c */ #line 703 "tptp5.y" {(yyval.pval) = P_BUILD("arguments", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 241: /* Line 1464 of yacc.c */ #line 704 "tptp5.y" {(yyval.pval) = P_BUILD("arguments", (yyvsp[(1) - (3)].pval), P_TOKEN("COMMA ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 242: /* Line 1464 of yacc.c */ #line 707 "tptp5.y" {(yyval.pval) = P_BUILD("conditional_term", P_TOKEN("_DLR_itett ", (yyvsp[(1) - (8)].ival)), P_TOKEN("LPAREN ", (yyvsp[(2) - (8)].ival)), (yyvsp[(3) - (8)].pval), P_TOKEN("COMMA ", (yyvsp[(4) - (8)].ival)), (yyvsp[(5) - (8)].pval), P_TOKEN("COMMA ", (yyvsp[(6) - (8)].ival)), (yyvsp[(7) - (8)].pval), P_TOKEN("RPAREN ", (yyvsp[(8) - (8)].ival)),NULL,NULL);;} break; case 243: /* Line 1464 of yacc.c */ #line 708 "tptp5.y" {(yyval.pval) = P_BUILD("conditional_term", P_TOKEN("_DLR_itetf ", (yyvsp[(1) - (8)].ival)), P_TOKEN("LPAREN ", (yyvsp[(2) - (8)].ival)), (yyvsp[(3) - (8)].pval), P_TOKEN("COMMA ", (yyvsp[(4) - (8)].ival)), (yyvsp[(5) - (8)].pval), P_TOKEN("COMMA ", (yyvsp[(6) - (8)].ival)), (yyvsp[(7) - (8)].pval), P_TOKEN("RPAREN ", (yyvsp[(8) - (8)].ival)),NULL,NULL);;} break; case 244: /* Line 1464 of yacc.c */ #line 711 "tptp5.y" {(yyval.pval) = P_BUILD("source", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 245: /* Line 1464 of yacc.c */ #line 714 "tptp5.y" {(yyval.pval) = P_BUILD("optional_info", P_TOKEN("COMMA ", (yyvsp[(1) - (2)].ival)), (yyvsp[(2) - (2)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 246: /* Line 1464 of yacc.c */ #line 715 "tptp5.y" {(yyval.pval) = P_BUILD("optional_info", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 247: /* Line 1464 of yacc.c */ #line 718 "tptp5.y" {(yyval.pval) = P_BUILD("useful_info", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 248: /* Line 1464 of yacc.c */ #line 721 "tptp5.y" {(yyval.pval) = P_BUILD("include", P_TOKEN("_LIT_include ", (yyvsp[(1) - (6)].ival)), P_TOKEN("LPAREN ", (yyvsp[(2) - (6)].ival)), (yyvsp[(3) - (6)].pval), (yyvsp[(4) - (6)].pval), P_TOKEN("RPAREN ", (yyvsp[(5) - (6)].ival)), P_TOKEN("PERIOD ", (yyvsp[(6) - (6)].ival)),NULL,NULL,NULL,NULL);;} break; case 249: /* Line 1464 of yacc.c */ #line 724 "tptp5.y" {(yyval.pval) = P_BUILD("formula_selection", P_TOKEN("COMMA ", (yyvsp[(1) - (4)].ival)), P_TOKEN("LBRKT ", (yyvsp[(2) - (4)].ival)), (yyvsp[(3) - (4)].pval), P_TOKEN("RBRKT ", (yyvsp[(4) - (4)].ival)),NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 250: /* Line 1464 of yacc.c */ #line 725 "tptp5.y" {(yyval.pval) = P_BUILD("formula_selection", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 251: /* Line 1464 of yacc.c */ #line 728 "tptp5.y" {(yyval.pval) = P_BUILD("name_list", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 252: /* Line 1464 of yacc.c */ #line 729 "tptp5.y" {(yyval.pval) = P_BUILD("name_list", (yyvsp[(1) - (3)].pval), P_TOKEN("COMMA ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 253: /* Line 1464 of yacc.c */ #line 732 "tptp5.y" {(yyval.pval) = P_BUILD("general_term", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 254: /* Line 1464 of yacc.c */ #line 733 "tptp5.y" {(yyval.pval) = P_BUILD("general_term", (yyvsp[(1) - (3)].pval), P_TOKEN("COLON ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 255: /* Line 1464 of yacc.c */ #line 734 "tptp5.y" {(yyval.pval) = P_BUILD("general_term", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 256: /* Line 1464 of yacc.c */ #line 737 "tptp5.y" {(yyval.pval) = P_BUILD("general_data", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 257: /* Line 1464 of yacc.c */ #line 738 "tptp5.y" {(yyval.pval) = P_BUILD("general_data", (yyvsp[(1) - (4)].pval), P_TOKEN("LPAREN ", (yyvsp[(2) - (4)].ival)), (yyvsp[(3) - (4)].pval), P_TOKEN("RPAREN ", (yyvsp[(4) - (4)].ival)),NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 258: /* Line 1464 of yacc.c */ #line 739 "tptp5.y" {(yyval.pval) = P_BUILD("general_data", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 259: /* Line 1464 of yacc.c */ #line 740 "tptp5.y" {(yyval.pval) = P_BUILD("general_data", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 260: /* Line 1464 of yacc.c */ #line 741 "tptp5.y" {(yyval.pval) = P_BUILD("general_data", P_TOKEN("distinct_object ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 261: /* Line 1464 of yacc.c */ #line 742 "tptp5.y" {(yyval.pval) = P_BUILD("general_data", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 262: /* Line 1464 of yacc.c */ #line 745 "tptp5.y" {(yyval.pval) = P_BUILD("formula_data", P_TOKEN("_DLR_thf ", (yyvsp[(1) - (4)].ival)), P_TOKEN("LPAREN ", (yyvsp[(2) - (4)].ival)), (yyvsp[(3) - (4)].pval), P_TOKEN("RPAREN ", (yyvsp[(4) - (4)].ival)),NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 263: /* Line 1464 of yacc.c */ #line 746 "tptp5.y" {(yyval.pval) = P_BUILD("formula_data", P_TOKEN("_DLR_tff ", (yyvsp[(1) - (4)].ival)), P_TOKEN("LPAREN ", (yyvsp[(2) - (4)].ival)), (yyvsp[(3) - (4)].pval), P_TOKEN("RPAREN ", (yyvsp[(4) - (4)].ival)),NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 264: /* Line 1464 of yacc.c */ #line 747 "tptp5.y" {(yyval.pval) = P_BUILD("formula_data", P_TOKEN("_DLR_fof ", (yyvsp[(1) - (4)].ival)), P_TOKEN("LPAREN ", (yyvsp[(2) - (4)].ival)), (yyvsp[(3) - (4)].pval), P_TOKEN("RPAREN ", (yyvsp[(4) - (4)].ival)),NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 265: /* Line 1464 of yacc.c */ #line 748 "tptp5.y" {(yyval.pval) = P_BUILD("formula_data", P_TOKEN("_DLR_cnf ", (yyvsp[(1) - (4)].ival)), P_TOKEN("LPAREN ", (yyvsp[(2) - (4)].ival)), (yyvsp[(3) - (4)].pval), P_TOKEN("RPAREN ", (yyvsp[(4) - (4)].ival)),NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 266: /* Line 1464 of yacc.c */ #line 749 "tptp5.y" {(yyval.pval) = P_BUILD("formula_data", P_TOKEN("_DLR_fot ", (yyvsp[(1) - (4)].ival)), P_TOKEN("LPAREN ", (yyvsp[(2) - (4)].ival)), (yyvsp[(3) - (4)].pval), P_TOKEN("RPAREN ", (yyvsp[(4) - (4)].ival)),NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 267: /* Line 1464 of yacc.c */ #line 752 "tptp5.y" {(yyval.pval) = P_BUILD("general_list", P_TOKEN("LBRKT ", (yyvsp[(1) - (2)].ival)), P_TOKEN("RBRKT ", (yyvsp[(2) - (2)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 268: /* Line 1464 of yacc.c */ #line 753 "tptp5.y" {(yyval.pval) = P_BUILD("general_list", P_TOKEN("LBRKT ", (yyvsp[(1) - (3)].ival)), (yyvsp[(2) - (3)].pval), P_TOKEN("RBRKT ", (yyvsp[(3) - (3)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 269: /* Line 1464 of yacc.c */ #line 756 "tptp5.y" {(yyval.pval) = P_BUILD("general_terms", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 270: /* Line 1464 of yacc.c */ #line 757 "tptp5.y" {(yyval.pval) = P_BUILD("general_terms", (yyvsp[(1) - (3)].pval), P_TOKEN("COMMA ", (yyvsp[(2) - (3)].ival)), (yyvsp[(3) - (3)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 271: /* Line 1464 of yacc.c */ #line 760 "tptp5.y" {(yyval.pval) = P_BUILD("name", (yyvsp[(1) - (1)].pval),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 272: /* Line 1464 of yacc.c */ #line 761 "tptp5.y" {(yyval.pval) = P_BUILD("name", P_TOKEN("integer ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 273: /* Line 1464 of yacc.c */ #line 764 "tptp5.y" {(yyval.pval) = P_BUILD("atomic_word", P_TOKEN("lower_word ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 274: /* Line 1464 of yacc.c */ #line 765 "tptp5.y" {(yyval.pval) = P_BUILD("atomic_word", P_TOKEN("single_quoted ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 275: /* Line 1464 of yacc.c */ #line 768 "tptp5.y" {(yyval.pval) = P_BUILD("atomic_defined_word", P_TOKEN("dollar_word ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 276: /* Line 1464 of yacc.c */ #line 771 "tptp5.y" {(yyval.pval) = P_BUILD("atomic_system_word", P_TOKEN("dollar_dollar_word ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 277: /* Line 1464 of yacc.c */ #line 774 "tptp5.y" {(yyval.pval) = P_BUILD("number", P_TOKEN("integer ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 278: /* Line 1464 of yacc.c */ #line 775 "tptp5.y" {(yyval.pval) = P_BUILD("number", P_TOKEN("rational ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 279: /* Line 1464 of yacc.c */ #line 776 "tptp5.y" {(yyval.pval) = P_BUILD("number", P_TOKEN("real ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 280: /* Line 1464 of yacc.c */ #line 779 "tptp5.y" {(yyval.pval) = P_BUILD("file_name", P_TOKEN("single_quoted ", (yyvsp[(1) - (1)].ival)),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; case 281: /* Line 1464 of yacc.c */ #line 782 "tptp5.y" {(yyval.pval) = P_BUILD("null",NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);;} break; /* Line 1464 of yacc.c */ #line 4264 "tptp5.tab.c" default: break; } YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); YYPOPSTACK (yylen); yylen = 0; YY_STACK_PRINT (yyss, yyssp); *++yyvsp = yyval; /* Now `shift' the result of the reduction. Determine what state that goes to, based on the state we popped back to and the rule number reduced by. */ yyn = yyr1[yyn]; yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) yystate = yytable[yystate]; else yystate = yydefgoto[yyn - YYNTOKENS]; goto yynewstate; /*------------------------------------. | yyerrlab -- here on detecting error | `------------------------------------*/ yyerrlab: /* If not already recovering from an error, report this error. */ if (!yyerrstatus) { ++yynerrs; #if ! YYERROR_VERBOSE yyerror (YY_("syntax error")); #else { YYSIZE_T yysize = yysyntax_error (0, yystate, yychar); if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM) { YYSIZE_T yyalloc = 2 * yysize; if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM)) yyalloc = YYSTACK_ALLOC_MAXIMUM; if (yymsg != yymsgbuf) YYSTACK_FREE (yymsg); yymsg = (char *) YYSTACK_ALLOC (yyalloc); if (yymsg) yymsg_alloc = yyalloc; else { yymsg = yymsgbuf; yymsg_alloc = sizeof yymsgbuf; } } if (0 < yysize && yysize <= yymsg_alloc) { (void) yysyntax_error (yymsg, yystate, yychar); yyerror (yymsg); } else { yyerror (YY_("syntax error")); if (yysize != 0) goto yyexhaustedlab; } } #endif } if (yyerrstatus == 3) { /* If just tried and failed to reuse lookahead token after an error, discard it. */ if (yychar <= YYEOF) { /* Return failure if at end of input. */ if (yychar == YYEOF) YYABORT; } else { yydestruct ("Error: discarding", yytoken, &yylval); yychar = YYEMPTY; } } /* Else will try to reuse lookahead token after shifting the error token. */ goto yyerrlab1; /*---------------------------------------------------. | yyerrorlab -- error raised explicitly by YYERROR. | `---------------------------------------------------*/ yyerrorlab: /* Pacify compilers like GCC when the user code never invokes YYERROR and the label yyerrorlab therefore never appears in user code. */ if (/*CONSTCOND*/ 0) goto yyerrorlab; /* Do not reclaim the symbols of the rule which action triggered this YYERROR. */ YYPOPSTACK (yylen); yylen = 0; YY_STACK_PRINT (yyss, yyssp); yystate = *yyssp; goto yyerrlab1; /*-------------------------------------------------------------. | yyerrlab1 -- common code for both syntax error and YYERROR. | `-------------------------------------------------------------*/ yyerrlab1: yyerrstatus = 3; /* Each real token shifted decrements this. */ for (;;) { yyn = yypact[yystate]; if (yyn != YYPACT_NINF) { yyn += YYTERROR; if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) { yyn = yytable[yyn]; if (0 < yyn) break; } } /* Pop the current state because it cannot handle the error token. */ if (yyssp == yyss) YYABORT; yydestruct ("Error: popping", yystos[yystate], yyvsp); YYPOPSTACK (1); yystate = *yyssp; YY_STACK_PRINT (yyss, yyssp); } *++yyvsp = yylval; /* Shift the error token. */ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); yystate = yyn; goto yynewstate; /*-------------------------------------. | yyacceptlab -- YYACCEPT comes here. | `-------------------------------------*/ yyacceptlab: yyresult = 0; goto yyreturn; /*-----------------------------------. | yyabortlab -- YYABORT comes here. | `-----------------------------------*/ yyabortlab: yyresult = 1; goto yyreturn; #if !defined(yyoverflow) || YYERROR_VERBOSE /*-------------------------------------------------. | yyexhaustedlab -- memory exhaustion comes here. | `-------------------------------------------------*/ yyexhaustedlab: yyerror (YY_("memory exhausted")); yyresult = 2; /* Fall through. */ #endif yyreturn: if (yychar != YYEMPTY) yydestruct ("Cleanup: discarding lookahead", yytoken, &yylval); /* Do not reclaim the symbols of the rule which action triggered this YYABORT or YYACCEPT. */ YYPOPSTACK (yylen); YY_STACK_PRINT (yyss, yyssp); while (yyssp != yyss) { yydestruct ("Cleanup: popping", yystos[*yyssp], yyvsp); YYPOPSTACK (1); } #ifndef yyoverflow if (yyss != yyssa) YYSTACK_FREE (yyss); #endif #if YYERROR_VERBOSE if (yymsg != yymsgbuf) YYSTACK_FREE (yymsg); #endif /* Make sure YYID is used. */ return YYID (yyresult); } z3-z3-4.8.7/examples/tptp/tptp5.tab.h000066400000000000000000000074211356505360400172320ustar00rootroot00000000000000/* A Bison parser, made by GNU Bison 2.4.2. */ /* Skeleton interface for Bison's Yacc-like parsers in C Copyright (C) 1984, 1989-1990, 2000-2006, 2009-2010 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work under terms of your choice, so long as that work isn't itself a parser generator using the skeleton or a modified version thereof as a parser skeleton. Alternatively, if you modify or redistribute the parser skeleton itself, you may (at your option) remove this special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ /* Tokens. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE /* Put the tokens into the symbol table, so that GDB and other debuggers know about them. */ enum yytokentype { AMPERSAND = 258, AT_SIGN = 259, AT_SIGN_MINUS = 260, AT_SIGN_PLUS = 261, CARET = 262, COLON = 263, COLON_EQUALS = 264, COMMA = 265, EQUALS = 266, EQUALS_GREATER = 267, EXCLAMATION = 268, EXCLAMATION_EQUALS = 269, EXCLAMATION_EXCLAMATION = 270, EXCLAMATION_GREATER = 271, LBRKT = 272, LESS_EQUALS = 273, LESS_EQUALS_GREATER = 274, LESS_TILDE_GREATER = 275, LPAREN = 276, MINUS = 277, MINUS_MINUS_GREATER = 278, PERIOD = 279, QUESTION = 280, QUESTION_QUESTION = 281, QUESTION_STAR = 282, RBRKT = 283, RPAREN = 284, STAR = 285, TILDE = 286, TILDE_AMPERSAND = 287, TILDE_VLINE = 288, VLINE = 289, _DLR_cnf = 290, _DLR_fof = 291, _DLR_fot = 292, _DLR_itef = 293, _DLR_itetf = 294, _DLR_itett = 295, _DLR_tff = 296, _DLR_thf = 297, _LIT_cnf = 298, _LIT_fof = 299, _LIT_include = 300, _LIT_tff = 301, _LIT_thf = 302, arrow = 303, comment = 304, comment_line = 305, decimal = 306, decimal_exponent = 307, decimal_fraction = 308, distinct_object = 309, dollar_dollar_word = 310, dollar_word = 311, dot_decimal = 312, integer = 313, less_sign = 314, lower_word = 315, plus = 316, positive_decimal = 317, rational = 318, real = 319, signed_integer = 320, signed_rational = 321, signed_real = 322, single_quoted = 323, star = 324, unrecognized = 325, unsigned_integer = 326, unsigned_rational = 327, unsigned_real = 328, upper_word = 329, vline = 330 }; #endif #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED typedef union YYSTYPE { /* Line 1685 of yacc.c */ #line 148 "tptp5.y" int ival; double dval; char* sval; TreeNode* pval; /* Line 1685 of yacc.c */ #line 130 "tptp5.tab.h" } YYSTYPE; # define YYSTYPE_IS_TRIVIAL 1 # define yystype YYSTYPE /* obsolescent; will be withdrawn */ # define YYSTYPE_IS_DECLARED 1 #endif extern YYSTYPE yylval; z3-z3-4.8.7/noarch/000077500000000000000000000000001356505360400137015ustar00rootroot00000000000000z3-z3-4.8.7/noarch/repodata.json000066400000000000000000000000001356505360400163610ustar00rootroot00000000000000z3-z3-4.8.7/noarch/repodata.json.bz2000066400000000000000000000000161356505360400170640ustar00rootroot00000000000000BZh9rE8Pz3-z3-4.8.7/package/000077500000000000000000000000001356505360400140225ustar00rootroot00000000000000z3-z3-4.8.7/package/Microsoft.Z3.x64.nuspec000066400000000000000000000020651356505360400201240ustar00rootroot00000000000000 Microsoft.Z3.x64 $(releaseVersion) © Microsoft Corporation. All rights reserved. Microsoft https://raw.githubusercontent.com/Z3Prover/z3/$(releaseCommitHash)/package/icon.jpg https://github.com/Z3Prover/z3 https://raw.githubusercontent.com/Z3Prover/z3/$(releaseCommitHash)/LICENSE.txt true Z3 is a satisfiability modulo theories solver from Microsoft Research. smt constraint solver theorem prover en z3-z3-4.8.7/package/Microsoft.Z3.x64.targets000066400000000000000000000006141356505360400202760ustar00rootroot00000000000000 false libz3.dll PreserveNewest z3-z3-4.8.7/package/PackageCreationDirections.md000066400000000000000000000034271356505360400214160ustar00rootroot00000000000000# Z3 NuGet packaging ## Creation 1. After tagging a commit for release, sign Microsoft.Z3.dll and libz3.dll (both x86 and x64 versions) with Microsoft's Authenticode certificate 2. Test the signed DLLs with the `Get-AuthenticodeSignature` PowerShell commandlet 3. Create the following directory structure for the x64 package (for x86, substitute the "x64" strings for "x86" and use x86 DLLs): ``` +-- Microsoft.Z3.x64 | +-- Microsoft.Z3.x64.nuspec | +-- lib | +-- net40 | +-- Microsoft.Z3.dll | +-- build | +-- Microsoft.Z3.x64.targets | +-- libz3.dll ``` 4. Open the nuspec file and fill in the appropriate macro values: * $(releaseVersion) - the Z3 version being released in this package * $(releaseCommitHash) - hash of the release commit (there are several of these) 5. Run `nuget pack Microsoft.Z3.x64\Microsoft.Z3.x64.nuspec` 6. Test the resulting nupkg file (described below) then submit the package for signing before uploading to NuGet.org ## Testing 1. Create a directory on your machine at C:\nuget-test-source 2. Put the Microsoft.Z3.x64.nupkg file in the directory 3. Open Visual Studio 2017, create a new C# project, then right click the project and click "Manage NuGet packages" 4. Add a new package source - your C:\nuget-test-source directory 5. Find the Microsoft.Z3.x64 package, ensuring in preview window that icon is present and all fields correct 6. Install the Microsoft.Z3.x64 package, ensuring you are asked to accept the license 7. Build your project. Check the output directory to ensure both Microsoft.Z3.dll and libz3.dll are present 8. Import Microsoft.Z3 to your project then add a simple line of code like `using (var ctx = new Context()) { }`; build then run your project to ensure the assemblies load properly z3-z3-4.8.7/resources/000077500000000000000000000000001356505360400144415ustar00rootroot00000000000000z3-z3-4.8.7/resources/icon.jpg000066400000000000000000001312431356505360400160770ustar00rootroot00000000000000ÿØÿàJFIFKKÿÛCÿÛCÿÂIIÿÄ  ÿÄÿÚ ßà`!Åã×™hæ[8èrÌΡé[f5V~qd¤#ˆ±Ìc¶³ÈÀrp\1(9J›çlgŽÕ1÷`5=^uU·U!½ têëBÚ¦¡TFĆ…Õ_+g 8û9?ÎØÚ¥rÌ»aøó …×—ÆïÔøyú+ÉuÃŒñ‡ÉÎ\ç–[æý|üü½ï~…!³±©ÐùFÙ®Wõ÷Íýíú"DZŽkÄJñÚjÒZs¡üæí†ùÏ¢Éo–zŸ¥že÷{£­ßcPZóøëúç'¿LxÞ 8Ás0Ž7|sÐìKóW»ú„*ÎÆ¯ƒN~2}§'-P|ÿ˜c¼!ˆµb$!ˆE‚4+ÌZñiü·VÛüèŸWÜ û¥ØÑív§£? ¿aóù=úKșÌss s w®=«vŽ1ã~—eÿýÞýqUG×ÖÄJ%òçõÏ+–lò¡ˆÐÄX"Á¼E¯1«D†cA¼Ç¯1£œDùÇ¥Ù_Å}þó³G©××*©|†}[…þçæ!W)*%åhÎyc=²ôo‡«µUwè´cb=áŽðÇx;Ç#Vøï©Úçïy¾ŒÑïuµõ­=}oÊTýMZ_¹5Ë»‡C["}§ ówù‘«”ÅhÕæb¼Ç†hŽ^íüÿôÏ©ŽNÍÍßыҾӟÍåwÚ)ÆpÆ}(æ6\å× 98bï·ãlõ€q#ŽðÄZÝëSü«ð¯ó—з³á{Û–·_Ùëk냛v ¥mùªw†©Ý8f¤Žf3›[(j­¥zþëó»ãÚæÌÂSæb˜®^6¥Ø3ðÏ£ýCùî–A4ç=&¿ZsEòöèÚå8MËñžç8ZhKå_Øò2#ôÿÎ*n®´zñE†< °só¿ÐöÝâ{ÛZÓ¿,züÈ»q–ç[nõ6}Æ F6®f6!bºVêƒosû‰ÍßcÁõkÌÍs˜®S0Í·ænbOƽÿÒ÷w!šsž“\0EÕŸ“­Ú+Ò?;®ýŒÄq´jÑ`•Õ³> ô «øE¶-+ò¿wJ{©G2SZTn¬« óéî×EëËå uurÛëþ"îlÓë×)šå3^g+ž»¼s)þ{ë7±£eØÜÑÖ€Mן–«kñ?B|öéú¾\ÜëE¯1 éLµùñ/y²žúmµsös'¡¡?Ò¥æV™`¯®âûñÏ£“,]^¶–PúUƲªº¹Î×9ºó;\ñ÷™¹¯ÿŸz½ûq:;»WÙìëd_4XXϺxßí8³±LW˜ÐG¯=ª–ü“ÚfÇÍ}^àt6³ocO×êës`ÁòqdoÙü |Äjó3\ç«—±TýŠå9 Í×d:¥ƒN–+y.öèøûû$«7±Îï³€Ñ|èVÄÏ´xŒ‡÷J´x¦*Ìxf<3‚Ÿ6õyòÿc¸®vÞw_©îuµ{X¸|…[Vu}SÅ]Jç7 ÍÕ9ªå5 ÎU)¸ÎÅèìi/Ìvò›•»º¾næqҼݎ|mȃ††)μ>±ãò›ßy¿Z ¸f<%1 ÌW,'𾎳ùg²Ü÷7o?­×©zÚ­òaŸ‘Íý]ýË]M{&ë”HNr¼ÌÂsµNn¹ÊS<-Ö¿¹ÛwŸ‰ÒúYçNçv5{]€ë“5gªO¥ù\Æú7—÷á™èfb¹ÌW™¸g|ršùw²Ýg/waYª±ëév¿3F~26k¹½m+w¡³9œ^ͪkYÇ?¶hÈ|âv¹ÎW8Д”'ƒ4Y…<~‡ÔŸ:êãµ¥ÌÀ0ÔMÓ¼óÙ¯ôo+RÂ^¥hõÊfš†qƒÎõ1ßæ>Ëw|ý‰Æºÿ±£ßcéNq‹¿rYŒÌááI¶×«>†žÃû‹Ö„§ª²zšªÍãt6Ûµ©9Ò¤ eëKB>ÉžHò•t3ëW™ª¥3 Í×,äoa?ͽ~îx½ Æ7;±¡ßc éδæv«a™J,Çš.ù…ïóþŠ!+Mž…sŸ®sõO 'd3ô¡ª«ûzƒ]ú²ùàô¼­…ý#ÊWÏ»\§+Ìår™¯6kŸ¹­Ÿz½Þq:;וâêóãoÀã pÖ•Ù¶Ù920À-)è§kôÕ¿ u®ô*”ýSž†qok[DûpúUæßt÷´pÏió;Þæì[è>^äESW)Êç7\¦«­~ˆ¼g¡ÍŽFþYSeߨÖ÷vcäáGVñ†S)l_Z݃ʾeŽ›°»ÇZyÊÎZ—o8¨0£òÁ#ªÈY¸ Mél\ºã7˜GÍqn¯V»0×l®úѲèõ4´Å”sìEíB~ŒekªÃéýòî¸[Z)Ýì£b®½¸ºX†lfPcŸv9ÌM{scVì¶«7¾íY‹j‹»×a,|ÏkÏ1ô7öÚ¢¯íêiS^Xî|æulÑPBs•Nv©ÎW)ê§7\âBQ!™ºç9 ÏÕ9êåèW?B©ÏÂÌg ´6~‰´,¸{ú_+» ¢kmloKf<1çÅä±)8ó,TRku^¶æ¿{ñÌ̆ jËC»{œ½ÜêÞМéÓ¤mico±ágÜ£PÓ9ÈJz¹Î×)êå9 ÏU9Úå?Tça9èN~©Î×g¡\§ê–žo…´¢Ï zžÞ¯ËÙØ×;kšÑô-£½Ð‰t"nÇ™c™™!á¯y|ÿb{¢ænìj0¹ý½lhƒZVËÒòsî9÷+œÝrœ„§+œõsœ®SõÎv¹ÏW)øOѪ~…SôkŸ¥Užm,tt¨˜]¿Š£Umkh²S¶lnâšïý»æXæ@RUç #Cã6ò›vÇ¡·œt¯§_ŸrQÜû´µTŒÏf;ã=ÎØãä™çpä3Kæ;fÐÛÜìi©::ØÍEº5ÅØÅ}^Vauñ›È±$l:žn1`g*^¼•ÛŸzwå…e%”\.¦œM˜².û§·â‘3ŽÇ'`Ï,Xãe×ᎌÔ%ùÝÓéÓZT§?f…¢Û}ÛœÊÜKL£ægÙ©0¸PsΕwVÊ«=íxûUó`ÆV›ä¸Žx­ÄsÄsÆ29Î9Î;[„œÛŽrÿÄ4 !0#&A1€"$%2467ÿÚÿmF9J 'Pi˜’_„A<‚j Á¬Ügøeßâ26ÁÕ¥Õ ow; {»¿w—Áîöö=ß^G»ëÈ÷}yï¯#Ýõä{¾¼wבîúò=ß^G»ÛØÏW—Ñîë`ƒõs±“Û'Ø5¢ÇèMá/­W?Sûuñ]îíÇ ›•ö ŠÄ»,=ã‘ã×ã÷x˲É´*«¡ÔŽæŒ±ß¶VŸ¾5ý¸¤Pªñ³€‰n‚÷OHŠ‘=$zXÁ’¤Òi¾«Øsz¾×Z±5ŸÇÆð§Ê«Vÿ⸱íÐæ¹úd7•‰jƶ¡&£énßÞ>^FÈÂy,vÁØ0A‚Á‚ ¡_Á‘é¢ú£Ø†ÊüÔ¾$ý”ºëÆí,æ±Dày$HRט5ÚΡǜÌ^"2 oˆ0N˦Hl(^Àà`£dçŒôüOíÈÿüAÕÄùXS*Vˆ¸u¶[`m‘&q‹½ÄÃÍ.ƒÍî˜ öl‚F¸ÄÎ |±„ÆLa1jkóë½??;;MmçÔ2çáh…-† ËÒô”k¬tüû9ÇO/² ӳюnknknNËQ¶§G×a—zÝ 7G‚ŽÑÚ;D¤£Xv¦õkäΤ¯£,pØo,}¨’ý6)æ#JWÐEžš….[ê–ØÂz™¶Aµ\–KCWÝ“hê‰=jâ‘=ë±E Âc Œ&0—!K§?ú%!Næ¿ ‰fÒ#ÈÌ Tc01VÀñ˜ÑãqÃÆã…Éýfp³Êí;TLChfx(죴v ­âÚ¨i{ìÕ_5¯¶§U~“Ç“¸«ÖX.¢ Dأ䧀R8ÇE<ެ”gIÔ¨Ÿè’á!„ÆH]UMWD4:“Trp×øgçã+Q{{gMíëU~¼.ÜävŽÁ‚Àé [ÊÈÉ]¦5Í ã:…L¨$Š™? >ÁªSòßoëÇa=OPyÅSîÖ °5·©º}­ÞûlÝÖ(XVð±è#ÁpÂC ¡ÈÝvN4ÍS0еf™nÇà–•e »7LÎК«ÕÛ×Úà¹6pA‚Ñ‚Ž“dl¶ö¹=s®Ú×ÛSê¡J‰?¯ um µ­“ä'B¾>± Ö×F‰ÔqeJ&ŒB Yg’£ÀÂ#‚¡œ‹õÝJ>V”½Ùið…Uvhá?7ŽÛ°mÔð}wN¨’¿nOœ&;L±"\íng^ë fHDª\œÍ›¹?.¢eV›ÛQóôx`11 P›"„AQUfÁ+®êŽ`Z‹dØRUz V ‰ßšË$Ý.¡·úóÎéÄ#ŒS,`a1Ø0˜ìÆþæwkjía餩UŒàìÚ&Õ?Ë?¬m_ùýá—ôÊ—ë "0ˆÂ8D±¬ojp3·;ÒÚº»B’R¿Xú¤àkhG¥Æ1ù(¡'RÝBŠ5+éK‚åCL|±„ÆÄ¼ü¬êllš©X;ÅcØ&É/Íñ²Fr™Ë½ØÁ¾~ ­ œ})°0†FD„CÈ¦Ò ìšv» k›ž…Æ©ßÓÔ7õ›DM¢3òÎp\u!¾ñ–£…0\ªb#ÀÂc Œ& Ø×ÂE§«5ʲëUëFxx¸Ô˜£à™ïÄTÔÄÄeû;ŠÖ|8ºlW†c|Øqª%¿-¨7¨×ÿQWص+fJŽQD}!‘ÆÍÓLgØi}™9­­PÒÍåZ~=Cotj,©µs0ŸÌ 7íL|±„RðÞºÓ[ëזǵŠçÕf!&cøMŽql×P/Ζ¯nS'­“Æ3¬Ð6Õ,Ž[Oðr¥½j‹>¶_LìsZ¢’K¼¥n0ÜôÜŽ¦è+²]>]1\IL(_ŽøÞñúú6©W’¸I‘,µíÆHa!„9èÖ¥B¤H_¥ëU©ˆDcÐÆ?%ÆGÊ àp2Pfè˜lŠü{ún¢QFØ#åÂEoú+pVÀ­‡QfҺߦ™%[–oœÇá¼wtVºˆ®BÌìIô[&DÛ³ìÆDa„EêÞÒ›T­ÌìÛ^²Š×``Ž1Çð ÚÔ¨¹ˆ‹ù=ý²á©4ݻǵ ”Jذ+`VãzÔÝ;ã8=]C?‘¼6WÛZŒ æËfÅÚ2Á1„FDaišN¯ `´<´Ë×7 b: ªX’#ÖDOž±+!ÏY0)¦¯ZÌp ÖÎG½“‡ýgɸ&Ý8ÙqdS*†ÇfåQÆÖe´µê•eax^³¡Üˆ¾¦õ”ŽRÞzÅa÷ƒ^d¯wÖ°d׫Øvh5„»î[ f°Ö1¬ WÒ6Â8H`ƒ°uYl4 §˜¬’®–KÀë;?ÙºßÿY`Ä¥!QÀÂCŒ" ˆ”…i2ÊOKÖJbi¨q¦«Cf´ ¦«`º~»€MMPž­…(Ư‡È¾P¢¡éý6»pÙµySªËýGPážð¬D–U¬®­Š|œ—OÕç˜éÒ; ›§›!söàôó-õzR£xªÊêb¬“<¸èuUyFãk×Ð$Ї‡Kä±hgûKY“ûuš?Ó*Œ"0€* ²!Æ#QŒD1!ˆ´AbÑŒD9XäGQOÃQútCþ‘\'ãª}&àÕ«}TbMpbf5ƒÀ385U‘dž²ÈÅ5oXŽD"ɺÇxí˜ýs[Ôw–ÛB7.¤NÂŽ´Çk4¿·¡ý" 0€Â  Üa¸+a†ã Æn ܰ+aÖILŒGNÄÇ AcˆïóÕ³(l6¦ yO°qŒ?3¤.ßßÕÍrÂ2*庬õÊëF-ë±Dkðë4ÙôÝjv¥­4q„°î4aä`ÃØ±‡ñ ¯âF$"$"d"$!Ád!Ád"_ľ‰y ê,u å²±< d a±ÃóÖŒi1§¶­F.&÷PšL‹·PsÎFz");§Q´«{¿S7Kʵ +/6æ¾’ 땤ئ\qñê{]XîÑÿnö\x-+kgµÇƒmà›`xÙ¶Gí¡à›lx&Û ·ƒíÑá;xx^ßÀðíÁâ[‹Å·–¾Ù³#TA9‹Ç ÿÎÑÖñ;.ÁÓxõM©6t)—Æçƒ;÷qTˆß«½ˆÜ{ȺyVñž²nY õ}° âÛ÷s±¹l‰AôôÐíëtHˆVÑtçNÍÚ<¸Çø®Ž#ú*Õûzû|û~û~ð‡€$<àHxCÀû~û~˜û~û~˜û|˜Ž¤¢ÉT“ùdÀsÉÈqPh°u¯Û9Öè=ÕLÜdú‚0gPE jH²†šÕ³S7£(QU (¨4‚d×L¥üøÀàp8Ààp8?.Ú;G`íî ÿÄH !1QAa¡2Rbq‘Ñ "0BS¢Áð#3‚’±áCcrÂ$€ƒÒñ4@²ÿÚ?ÿMR9*§%QÙo ©UOÉ ©ù!U9*®ËÑ2‰kq»®JÒm÷U¤>Žåi¡»ÅZÃè÷µZCèîVú;•¤>Žåi£¹ZCèîVú;•¤>Žåi£¹ZCèîVú¼U¤<Ù¹ZCèû¨Da¸U'ð¦Â´lÁ–É'´´qöáÃt‘¤Á‡:Ï`_YQZ>ú쥨“ûÁ¹?MQ[ëO·ö_^Ñz]ÿ²úþ‹“»××ô\ßû&iª+½iu•KÑñš:Êm2NLHní@°ü$¬ÁäžÃè‹djëò™ÊZuïe8´×7„)TŸjåÆi>Øï\f“íŽõÆ©^Øï\j•íŠãT¯lW¥{b¸Õ+ÛÆ©^Ø®5JöÅqªW¶+R½±\j•íŠãT¯lw®3IöÇzã4Ÿjw­ 4JWŸ¸KZ£à$^v†þ®á¥Ø/-Õ|”Hñ"¹Óy"zÿUwr]¾Eß?ö›$>CË{uJÒàŸ¼/ØU éu$Y›€#ZƒIdV1޾sðÁ3Ãó›{¯Õ³Gïä3”¾ãëðCÓMVZN)r€TžQêg÷p VšŽ`Q+«ù¨|JýMO†|2à’ÑúJ-#fâèfâ œ‚¡R›­ŠÃ6;TÔXaÍ´f?ª<-¸¯¤Gü³?¯Á =šíSíWõ"s½}å=@À*G-߇ãÀÝg%ô’7Ù²ÚÛ‚Ô§Ô§µOhSž±¹]äÈ©)ô~–Z÷@{®<€¨ï›dTf¼Ë Vß.â´ì(‘`1°Ú\kNîÄ4}(ÿ ñ}]Kæ;çµ K>©CDRÎ¥õ=3ækêz_Ì×Õ¬‘Ñt¾näíK &¡»b3i‘¹ÃTú—jžJ|/£—×9M@ÀvªG-ÿƒôà7­i]I§ÒÁ†é07^ ŸFâúñoѸ>³»ú7BÉå‡(<×÷¯ðåšþõèÜ/Ḍ¦©º&“D¾UÙ›u)))ªªJ]j‚jRá;j£:r9…€ŠØ™Kz—G—×.¤hÇþ>«Èwì\fcîýñà'*·c0ëúŽþq;*€­\u§=ÇvÉf9Œñ­:-fšÙÜ´üŒhcb‘Aª®xª·©lT(nu*Z€7ãög«Ñ5I¤C£CtG™5»ÊÒø”èÎ$›&Ÿ0lSòaÉâYÎÉh­Ú$6ÌN+‡œrž¥ •zÔÛ LüìQ"›ðÔ>uù5v /Ä#5ÌüUûÒU&’È,.yë†j“Ô˜®{ŒÄîØƒJ ÍT ª‘Z&ŒAtW‹$¨"C­Gt›W)ö*Ç1èš!¹î5ZÑyÍi=%«|Ø-7m’¸a䱎ˆðÆ ¹×‰Ñm¢±¯xœw áÉšc*¥\Ð\zÔX•Ìõz£ÊuÌëNÓTv=Ì3óLbúî‹Ò_]Ѻ[—×T~’¦b’D&йëQ¢Å¤:³Üz•Rª•U †èÏhÅQà†€Ð<Ö¦HugŸè–ÿ@ÔXŒ‚Âç8¶÷9i}(i²„H€Ó/êR—Š’’k\ãU³$­¢„ˆÑDâ» Š%~²€ºeF‹^áÉïòé« Ûø§€bÄ9¹U*¸\ªlAЬj %&Ã$È šÕG—ʼÜÔ8MhMhÀ\X‚d/qÃ÷Dþ³ôp†ÒId­1¤ÝIqý¸žwZRÉg4ðz–‡ÑUeHŽß8ÞÆŸŠ… ¬‰êØš%yëQ¢Ö›G$c·ÐS¢u"Ù¹Ýj¨’ *ª AŠ®ÅV{‰\ Ã`¹¢íjXuÞ[1±=á‚ëÎIî.5Ž'ÐV i=äêZ_J˜„Ñà½g j¯ ´N‹™ã…Þ«J…BrêMÊ´ÚÜ/¿3—Î>€b©÷À‰ÔšÙª¿2A¨5ªª¡Aˆ4Œ&›+uÌ(t›ä붨Qåq½¥>p®Ó[¶ùz ˜6˹i})s¨ð æç8jUq¿÷Rÿµ$FÅ%UhÍjDh¢L Ô t€À&¶wê <_Q¸£ måG‡l×0Ì›£¡ŒÊH`}Ø)Ô8gÔ’â Ì£A¸àŒ3ƒPj A¥Y¦Í¦ë©‘À¨ðÇÞ4]‹¾VkJé:€Á‚fóÊpÔ¥;ÌÉ:Ê«5U©lU Ñú<Ç}w‰1»Ô!­I£ÖÏ©F‹WÌn:ö"~¿B:±€˜¸õjÍY…f˜N€ ‹·TÂ|9:ì ¨ƒPb¨ƒT]’*+*ßÅÙ„¼ŽMç±i=#d 8fq'$Aq™ÅוVz•Bª*»>{•UB¡:ù›¡ŒJ£Àk@UkS[Ü£E³[‰ä‰ùÏѵå¸+gá^î©oU¶o*{7”#–Ù•oŸ¹Õ…WJöã)'4Ö‚ƒ$ƒb OgšsjŒU#—>‡÷p‹¯+HS„´ý¡Õ’3ˆâçÞJªªªª®ÅTª-Çx¨ÅQà5­h“GÁ5¸¢v*O(Aÿéjü¿EÀì $(¯ªq’12½œ·„NÞµ?A) z¶ŒÓb‰£0 é ƒ\ëlºîþUDi1OZ B©ÜƒFJJª Öæ€ M@75&檌ÐhÍUnÄd¢ÎWóÜ{ €.ùØ©<¿öÿ¹jü¿Láõ:]’ V€QC‹‰$‘,—·¸©—T©K‡D^™›Ýrke . ïl1µ9ÄÌŸ[w X^Ù \¬JÀä¬ °9+dV%X•brܬœ¬œ¬ŸòU”Eg¹T‹òUH¹ïU"ç½YÄ8¨BRTŽ_àø©ÝµBˆaÁÄöH'Q†¢J²ˆ0U" Õg"Ùõ…>Š˜æ©ôUÜÕøUîÁ²’â&À±½)Õ /q—Z‰HJ÷c—z&dŸ"J„^´•¬|Èü'ÁZAÏs¼¤÷iîŸkœ+¿â­`säw‚µƒÎ?•þ Ö~ëü¬ý×ø+X9û®ÿеƒÎ?•Þ Ö<þWx+X<óù]à­`óåw‚µ…ÎÜïi ¸ø+H\íÇÁZAÏs¼´!÷J‰»§«Õ$aqÔ›Ivº›ÿuo0uÝ4 ØùÕUoj0V[ÉYl Ïb³*¨Ü­a7Y8ê>N¤ßæ‰6XúÞz1qsõþ¿ûóSÙ¼ªÛ7•[fò­[ÏŠ´9owН³yñU¶~¨›ÊŸú‚ÿÄU !1AQa"2Rq‘’¡¢ÑÒð BSUbcr“±Á#03Cs£²á$4Dd‚ñTt€ƒ5EÂÓãÿÚ?ÿ¶J£ß^nÑÞŒñ Uú@#q˜z~KýÆaéù/ôW‡§ä¿Ñ\bŸ’ÿEq˜z~KýÆ`éù.ó ði@ì~i¿ïúœ[-Ó…bšPÛÅÔn×ÇMKþiÙhþ'™q FËGñŸe£ø‹ˆÚ?ÌykˆÏþcË\J§òכ鼵Ŀúo-q9¾›½ë‰ÍôÞZâS}7–¸”ÿOå®#?ù-qÿÌykˆÏ²ÑüEÄ--Äó'Xf`¼üû[´—㢕î³<à$®·—Ô;gj‚jÝšì´] ½^×zèöÈy}ä‘Û__!Û­e¹»5¦\èÃYׯjùmƃ&Ú†òÚ¼¨¸ —ßîNÚÓý>ǹjCŒ¾¶Ÿ:ü™å£ð?ªüšem±ø òk•¾ÀþªÑì–!8B$ú¬>uiàfX`©°=ÿU¸úö©²ºÍ„¶KD:ÆvàQ‚XÉ q8жJº ªÏÌÒìëh.Ѧ6ו´ÕÞ~ÄÛEc2S’.ãV&† E7Ñ ê°è÷¶ŸÕ°½Š,¶[^Y’;\-œ\Q®ÄWb½Ïä?“¡ðB÷=¾Mƒ¹{È_&Áܹ̃òl=Ã̽Îd“aîeîo |™pó/syäÈ{‡™{›È&CÜ<ËÜÞ@ù2áæ^æòÉ÷2÷7>L‡¸y—¹¼òdÃ̽Îd“aîeîs ü›‚<ËÜîAù6/sÙí°w/dûK±d ,¶Há”Ê9mU[9ÅXùÌÜù>èý¼‹¦Ê6hÞ/_µ4ü×:´îVŸb²Yà ²ÆÉD"ý¾Y×nî 8㳸#!õ¢ÎZ#)FR6w#-tÓÁe=žÉiþñ$ëhVþ ä[ppxáqøM5–ýŽç€äÇñ–З±Úº•³&I®cØèeŒÓ¬uŠŸ|r;=z·s|‹­Ãy´Â¸lñ(å ‚1ÃAšÏX~òÓú£Ö°ÿþ¹/ý3~÷þ’ªò/Eë8½–­‹C¢AV®{•œÏ¯'òÇíNi 馔ï+€Y=–ì±gæe`éN~Œtšt  ÜÀKØšwT¨ah«4¶7¢—«CÕ Ì=ù!¢§³ošP4· É\àã` ·[/ca;“° ‘q‘p—„WA±Yí$ÔV™ï&¼ã¬§¾˜(lî%4$¸§p‚okŽÏRÆ]/8rQM_Ôé;ëïßÍwRȘZ¬ýe>D]¿Æ‹½j‹Ñ‘gt¨Ì¤Q¡>Ïf~‘G)¬T©ilV›ì@ºà£y‰×$ÀTšÒ´qiÒ0 7×Jê#áa§Ï|ç‚ç`%4:ÒêþÈu½%’rI´¸avé;TMŽÍbŒ€“dFTdFMêÓjÌDdÓEjµIiqsÕìï{¯bqÖ¢…Ì»P Ú“F3uj§^*§¢{‚qymZu0ïª-´H\ÖšvÑfí?ä3ÑY«OÇÿ žŠÍOð¦òi÷¥‘öw‹îsÙ¦¥kÛ\;ºÔv±p¸Óõ”¡p¿p×7`0TÖˆŸkªnþ!Yq‰ªK;_ÏmQ²´ærݺ ,´·› j¸Á‡cVrvÖôwñ$S +€ì\aÚàx\düKüKŒ<èý¤*½í躑{!í'¯YNŸI)òÔ•W+¤ê=˜¡$É®v ï\\üd?h1Êx_†"¤iV·r€Ü¢eçàiËkFçÐýUÅäøÖýŒ?ýküPýÈþg¬ë¶Fõb.•}_EúVq®kÛqÔ ®'fFÅfFÇgFËâК…\‹b-‹r"4û”4 Ú#©Cª0¦‚˜ée7#î;^.¯`V?Õ ê?rtÔF|V}q­q†£ib6±ðF)ö—»N ÒoªåJn´N ›gp4’±×É.¯`Лfi¯ç º/S6áZj©:ÔvzV{I-kàukÓE™Îsèëܲ]ªšÑdcÖÔéßAxÀÜ•õ.'i¢³íúf"_küOþüÎY1ÿÙ[Ô‹÷£ FTeFRŒˆÈ³¥NÕœ;Q—z3#1FmèÈQíL–µU¯œL¾úƒðÚÜEyÁÇAúª$cpä¾q ͈èkPè”l•㸩¤¡¢2â³…^r¼ôÖÈìAhúÏk|N!fäéG_ÞÇé!g”’×W“@nPéé¨BÆ j÷Ú9WiM$ÓQF]7K«umêÒœÖ÷¦ÆÆÒƒ@»Ù‰ü}ä§o‡¦©'ÍðãôÕ$ù¿i¦®¿æý¤~šºÿ›ö‘újëþoÚGéªIó~Ò?MQû¼6zjÝá³ÓTÍðÙéªI»Ãg¤®JóAv§Ëg¤ €€ó›#Ž [}¥¢‡:wûRÄ M+^{zT7¸š§Y)ÍΜæ7X­ $Õ¯õš”4;“C¢¯`ц·jÖ‹žÜˆ‡)(Jv¬ùМí\`ô—;V{çdÞQ—bWónøliî.6Ê÷‡Ö·šàÚ éÇ•{WRm]h-`!Õ% Ô¶š+_\­»O‚ÇI¯½ 4¨Ñ£îY˜º Y˜º Y˜º Y˜º Y˜º Y˜º Y˜º Y˜º Y˜º Y˜º Y˜º Y˜º Y˜º Y˜º Y˜º Y˜º Y˜º Y˜º B(Á¨`bhÃ_¼1°ÐŒ+NÜJÌ´:ó9&éno^ðaßä³ÑFË_‡ä3̸éy ó."Þ—’ß2â,Ûä·Ì¸“z^K|ÈY)¡þK=ÈKH7ëºëGà™l]zÐhÖkÚîÿÄZ  !1A‘ "24QaqÑ#B’“”¡±ÁÒ035CRr¢²á$Sbcs£Ó6@dÂt€ƒ„%EPTe‚¤³´ÃÔÿÚ?ÿvª©IHåQ×TüšO!™d®1´äGüÓ>ü|ë#ç-{Ñ󬇜·×:ÈyË}q½´äOüÓ>ôQ‰©w#O!Ã÷Iø©Éâ*%˜qÌx)'-4ä‰Ùk9©rĤìË( iAKmµ©±|r¥pŒee¼W:ãYmKëŽ)-©}qÅ%µ/ÞŽ)-÷ýèârß{®8¤¾¥{ÑÅ%õ+ÞŽ'/÷½èâ’ú•ïG—Ô¯z8¤¶¥uÇ–ûþôqImKëŒ%%µ/®0•–ñW×^U<ä/®œµC6ŠIK·IºªtÀ)Å'#ÙbʱÛBgfåÃÌ:´ßIÆ‹ÉBéMä5¬2YÃiÚŸ¾˜[N:à U º“>ß ™˜´í}±f¦““¡)]o­[XtÏMÿTGÏVŸN^>z´<öoúñóա糟×å«j«ìÏMÿXÇlÙ–•¨UÈãï<5-ÏlÚš/$w¯´ºSÇTm{-²»e¥Q%É[×Û:UBqè¤6–-†dæàËMoIÐ UH—BР T’’FÕ¬“ÿqð·Û}°âüöý©èâMêÄ›Ô#‰7¨GoPŽ"Þ¡E½QÄ›Ô#‰7¨GoPŽ$Þ¡I½B8‹z£ˆ·¨GoPŽ$Þ¡I½Bb] ¬¬b:b^âˆJÝöĪ•ÅzûG$‚O@Äà ɸ§’¦´%û«§%ä¡0ÐKJÂ$d5ѨGå¸ü£F¡Ý„9àGk!’{äˆnn˜qN6o…]StÇz¬ýpÝŸ²´¬í´4÷l’VËuĦ¹Ó”D­µdM¶ó(âV›íšbœÅ76¹ÿ"ÿà1oŸóóßü§¿À´Žqz¹]+ö¯±>ñɹI…rdÒ°ðźóÕUË^uÄéìÂÒŸ€5|bÔ†ÒÜÒA)RE*a¹7æ\ø1×’Ô֭ܺ­)­/¡'áÉÓ²Ë je´¸ŒrWql¯ü›¿Qn«–nlë™{ü >³¿Š"[ìÆÈm9cúB%T–…hMä”áÑQà‰û]ÞÊÝZ¾Û‹RÕéWc(àú##ª2:£/ˆjÕ•fŠo•1ðlÛ½ÖQa–’NmŒ¢àÓAèìÛŠÿ(çàT[.̸–ÒffqQ¦;{±Æ[ñ£Œ#\|ªUÐcóŒ•Ó0<0@¯< ÞB²#Nî]<´‹;ø‚%þÁì"Ë+á-± *U+è‚™”¾·U‚xT$ÁÚ%×ÍQ—bªÐ)Xª%Oà¬Ç_\c(ªÜ.gåéN†ab]ü®¸hTy·v‹woU³ê…Ê^»¾­Þƒ ã–>ˆÏÑØ´¬rák·eœm.õdQ'Ó›\¸yÇ8¿YWÕ眈=ØùÌù1îÆ6’¼AîÇ_‹ùG_‹ùG_‹ùFËñ(TÜõ®ßFÆöò¼¬%( Ú[P+^9Ë °ž it¹‰…Š€n#ëB–iE7è(7­¢¹D•™x»µÝR•ϤC‘'±gL5j,¥9ÜéPàZ(s5ôÆÖý¨fÑvž !-¿d‰§Ò‘œ5c¥'ìþQA „°:£ˆ ÿÃPk š uC›m”‚âÒ@]8'–´åÜY”uê² rÇ^M‚¿–—M×UõˆÃu6WÁÚÕ_ÊGÉ»ñï}°7ÛOìV2þWçåÇùqÀû‘À$p‰–嬶C -L¶²ŸXNlfL8óahÛH—k­1QKÃëS(D¬ºEê ëî—52°› ”ƒ¤òB$äÐâš.PP¢¼5膥ešÛg\»ÛÒª½¦‡’ó‰ß`rƒL°§`¤ä¯l1û(ÒWÌSHÞ°Úz#‚#O³ùG}鎉q0”mϸ[—kÊ"uÃP’æû«QÕ¨ íFèÈœ4Cóô¨©Z`Š™µíi”KIʶ\qj M ®f)&ã¢Â”˜[vtªI¸xµ`gQ•a(Jl¬êô×täÔÊÂm$Ô˜EŸg¥jd¹uMnݨˆm–šZ)Ol½v§’‡žFûŒ¤R›–SoÚŒI)÷6¶Ò·J‰¥ì–´šRYÙ’‰ÑxuÀ¹nIþñ=q·!å›÷ ¦kdrMŸâê4‡³f‘lL7“ ) ¨ò~÷†‘E%ÄYˆz²òG(ŠœI¦ˆ–—Ný(åùTÎâ‘+bH¿yànÍ´ƒÓ@iÊp†\y¤Ï©/¤é V4†ª3]##ñ6• òX••inºâÍIW²Øí†µ·`²ú™m,•RppÜTza+q!sΤ“Þ×’*w+˜˜Xm¦ÅâU…iÉʳBŒ¶Ùµ¶„Wºhª©¢x·¶ÚS)JœR†ù²{ÑÑ}ôcÄ@BE)žêE»ÄÑÒ”ŽJ·HbfZfà}°âh¥ B¢­Ú&™Ra}qªïœ*/ÍNmªý¥¨ÿª»RaÓ01(I7+^Ï•BEàúºL^V{ŠXquZ¤¡ ¤ÕHQÈ‘ÍÜŸhö‹Omó.¸ ×4„ ž9¡„4ŠKËÝKxw¢lh§«â›šu,°Â Üqdé…ì;b®8«/lHqÆúeTZTÕRx9_0&çRh:/b>J½ïLTîTëª B$žh5“x³¶mToéZh†­;E°í¢øJ›AÚk–™—ÑÈr€”Ša»š²«Üd—H¯×)\±"ÝNõ„Di×q™×ˆÊ2ì©ù×Ûa¤ ÊQZp‡ìý56°R‰ÄäÞˆ­²·Q$\ u÷ª‰­JPU‰["Ë— J0*”ЬŒÊˆÎ°í cÑ»[Ï--4ÒJÜZÍ”¤T’O4?°†¼¾ÔCŠ–›d›î>‡BJDâ“LëDôÀ´çÓ~yÑyX–ëqÓ•w%J!)©G Øv1RñÚÞSU%JÊê)œ5n[mß´¢å¥ÕÄ«£^û–ûèà ThÑ BÝ“ÉÁgó­SÀÚa„ž[H=4ìåØË²ìí,f&rHºHMtš”:ggfC•¬°R’€Šbb~~õ¢Ý/JL›¬•xa¶¤Cr” å¬BIH½Ñ ÚœqA@¼¥(Ð$ $ÃÛ Ø¤Å\w¹ÍÍ0ªÞmÄ©=Íh<*Õ7tgÚµÆÙ0ïtm.o•UcyW±$œIŽA snk“Í °ìuUÕoy¾|.Ši†öCl·yÅoåštTãTf눣b—SLæ„! |DÒÆiaÃ÷LL•ãzk&˜P¦JŒ‡Œ:ã½Ö#(Ëp©y¶PóJÌ)!^¸[JÞÆé@ ôBçl—VåÍòZh¨9†8ÃV6ÉÛrbÍÛÒƒ ‡©¥åTâ,KZ–\ËoKÌ (hHÈÐèÝ’MÄ“l7bÓv› ·;2Ê‚û_|R¦ðïˆÈ >í»yKZ‹­6æn-XÞPŒ0JFÉv2Œ£Û ²,¥…Í8 ]q' Œ lŽÝB¶›÷ÚmÑ‹Š­A¡å†¨Ö×.ÝBR(>&ÐÚþSµ^¸9MLJ!Ï…·ðsý²Äã© ­—.“tÝ9 YE _|–¨þ\¶å È8ÑXõõ@qÓ?0”šÝq· u JìD›ºTÛ¢¿Ë†Ûµ¬¦Ød‘}IN!:N DìÙĉРèx„o´XåÈÆQ”b˜;Á4LMÙì-R§B›N.¨cwÃcv³‹ø5çij¬º­ì¢²ª+ÁCSî¡Ô¥M¤ÓûØôÀrÚšmÆu£S*T€Rj“QÂÜC›)Ù*œt¼ò¦~¥n¸¥_7«½©Ãš„&ãHÞ¥)Á 7J“p.Ðy%$$8˜; ·/öŠ\Û@rµ˜Uk…{Øa–Y J²”!)¢h2ÂF_Ž#H9ri»*S¶ß,†‰>4³%³ýBz£æùo"ž¨ÆÎ–ò)ê‚ ™-îSÕNIxYMÜ|›fÍ[æM._ fõæ@ýe)éŽÔ´;zNšœ\ËFQ—c×Û$³P¦ß´œ¿4S‚R  J…2Æ‘&Úœ*2iC $éBV4¯pí“d¸ÜÞÈfЦƒhX¬ªUy wN(9êû'Ù#Ž8—ÛªïÓœÆ$§ ˜C ¦ã-„$ ¢ã([M-Zo¤¥¦íGòRm[Js´RæÚ¥¹ôôQ;P¯{ÉòRldåî¥ Jh(0ÑÞŠÐtüe¨Š„ Æ~úrøbÐ1²;.‚;EÅb¥ŠôÄÄ› !„¼±te½YukZo|µ”ÍöuD̰Ws37©á†—ÏO»Ù˜’’}¹L!m2ÂRʈ®˜§qÑìŠßqç[Åõ8ñR’wÅ[R/wƒF¸nRU°Ü»@$Še„ 7BÝY žyK7¤*šasóËqRizû ®Öå‰{.Î`"]›©QJixŒÏ†¼è4@èøŸ­ bZUð ÷]A€œH…®Åµd­4·ò†QÔ¹sír|E©"óèrÖµ¥Œ¬¬¢ÝS¶ý*“ÑHÙéPºé¢ˆáÞ5$xIŠÓ8Ê2ìì· ¤”¯dL¯÷ÐØÑÂðÒ‘Ÿ¢´šMéɯÑ%)ÂCÏ¥ dŒaýì÷i×v×ß^Jý„ ‚tPCvužÐjY ½";ì#(Ëq1k8‹× €wôÂ;nÓu]¯·U-hmºòtC6eŸe¡HaÀ‘W•õ«þ‡J•Ëv—,uiºh|–”ÆöÈÂã^Ç ©« õ(…ô4ð]&¼˜GrجÂÿâëTovç†e>ñîb¼èuÁ»\§>ÚÚ½&±8ìü²¤¦¤žÚñ(!ÁË„^ì…éÞžxµdPV…NNQ&ƒlqt 9}[›•aеK¯z¬>¯T[aV­Åo“4ÖùI侯aÕM¯`»$¬…ŽrœN»U¹[À`êГ¤QæÉä±ýâ=è¿ý¤»üT{е«d’k(¸ŠŸ½KìbKá …›Ì!hR4¨ïð4Ëzq‡-Õ¾dÖæ/»T¥¦/„žlÕßD¥eµF™ K®$|©ÒL6ƃ±”rFbd£þ¼J˜<ã*DÌúê•.kx”×8i\¸}ÚÆ~ˆ²-¤Ï­q(„ ·/SOLS±”ev‘žl9.¼ÒEs…Z^&¼ß2ç‹ ç‹ ¿8º¼XÂPø‘„—ÜŽ 'ùGÍÍù8µæÚm¡’R»”)Àå„OíD„™‘Xmj8ÝC¦Ö.²Yb˜W~¼b_neöÑ‚±ïD»2X׿څít…)Éu¢¿«M)ÑH?¹6ýáY“¦a#’)Û©¹ö—ïCfÑš¬½{­Î9ŽpÓ̰üãé¡P˜Äד}\! ÊÊ"J\.2‹€ŽxJ”€WžQL·6~Ã,·%ÔœBæJï }‚qè‹E¤\qL4©„Ó¿ ­a”óÂ; ÿÄ›õ9<èœ4nrìb˜à QÀ£€5GjŽÕª8Tp¨z]@O¢â"mtáMª=ÑýÛüŒ0¿ýCqf?úƑꚦ1lj“N¨àïGLwšã¯ÉJzD © štn¦6·›rÙšn䬰RJÅãvñMkèÂ&öcmßRYySM­ÜŸqD« æh#¶.Q°EÑL„%@±b7§·›WþìYœí&ÑñyvrìlU)$%n*ðå¢Npçñ“ë†zGá~È—´ƒÚÏJ6”/EPò‰‹Ô£Ô!=1ÈÆ_T¢‘‰Q4s˜v^Mæ­M>Ê»NU‡Ûi^`¦´§&ç ÛN?Ú[vÚâÝ¿´4ÉUå6ÅêgŽ:bVñٻ),•8+PÁU„ïhº û6Jt †?ûbżói;Jj Ò4tÂJg/Ö#®8ãQqÇòˆëŽ:Ç”G\qÖ<¢:㎱å×y*ޏãÌyDuÇ—ò¨ëŽ?/åQ×~_Ê·×~_Ê·×~_Ê·×z_Ê·×z_Ê·×v[Ë7ïFÄÐÃÍ:CÎ×kZU…ÕS‚Lo»÷RD1àü1¯Ù´’Þÿ¶ .œªZ†<õ‹*N×´›“r] Bгm©7ê@^mñ”w7™^ãˆW¨˜ÌnÔäý¡-,”Võ÷SQLê+.Z}›^e¾¼«­­@òP.¤Á²ö+1 Ãœ(8ÞK¤xÔå†vg2êj­µrÓ +qëÆõ*ªŸÂ³l‰A-*Ø ªQE*œª•­;ìÉ0)˳ « ÷kºÚIÃí•üB²0Èo¡/,ôaNNSý©}QÇ&üé}QÇ&üé}QÇfüé}QÇf¼é~ìqÙ¿;sÝŽ;7çn{±Ç¦üíΨãÓ~vç»~sÎÜêŽ?9çnuGξvç»8Îùãžì|å=çŽ{±óœÿž9îÇΖ‡ž9îÃiµ]˜ž |—l>·.tTa2 îôFÛL¯C>ÃØUj¸¶Rˉ}—[á%Äå"FzeÐ/]*®z!_¾ól¡GjSS lÝѽƑDÛ6Ò/~¦ijõ¢6‰„MÚt³Œ¸åiËu¥ú„~•±Æœ<èy#R¥°î¼¿ßÿóÆ;cïF7»—ûÿЀ%ö2ÚOì¿Ó*b’Ÿ Ù ¾•Õ¾ŒRj7Ëa òf0ƒÛ6´ûèw„™™µc^]à†—oϾ™Ó¾y¶‰»{29q†¥,Û!•ºÝ.ÎÆÛÓZB5SJ`FˆÌTsE7 lãxaX.]kG®;ÍqÞkŽó\fqš5Çy®3F¸Íã4kŒÑ®3F¸Íã¼×G®>\}¸o*,%Ggº6|}±€çŠíL¯¥!PB—WK)êŽég3ÿ•”d|Ø$:£æ¤ù!ÕYHòCª*͘Íi}‘½’—GC)È>È»NОsXZIæc¥?dSþÀÎ3õF~¨ü‡T~Cª3ô¨ÏÕþðŸÿÄ)!1AQaq‘ð ¡Áѱáñ0€ÿÚ?!ÿÍA£B‡šže:iŸ%ØÁT˜º“þ äÊ[ô|¿”ÿÅŸP66/"õfæ'Á×gTaál(1s›Ât?Ƈùž=«_äý«\ÿăÁ"¸:Ñ1—G¡—ë†üu5»äi"=Fü­ýüPµB,»F^ÑXnê憮{ÆÌôà sÊX˜Šúà0:gÏÀ‡ÆÇªžõëÓ•Úá¿·h5~eÖ~1Ü‚ø;ˆy“* :PšuÊFæ+V+"³Xæºs\f¦n_Òfد´«ç¹Ø&O´~!üO˜~>‚<üùgâ|³ñ>YøŸ,üO–~'ø/¢G ø‡âaæÞ S´²£X(f‹V{<zjè*zµqüflhÞ¬Üw 9^ÐÎûAâ|¥­×²ð%| ?Dò=“©o?Ñ(°\ÔyÁ¹j-Mü‘ÞŒ3â²h*Œ™Lc,48vÒÒ’ÎG£×èµqPržñwÿK&õ+É=Ñx~ä*£ÚS>hð¥(´l‰Œ#œfh Bp»Â¢µí#|$;%úBKq²âR!¿8lÈÚ3›¶Mò»&6`àsÌi;h:º±_Î8|råPûÃçßü.\§ÊY•ЙxH5ðìŸ"ñ ø“71®å:wK½ÑÖlÏ: Xç¤N¯h/(äÉæîè.¢»C-C²Ÿi؈vÆÆ;ê „ÛŠ¼W“¤› ¼²³S°C>>zx ²ëPl'˜º¶zÚ܃ýLìuËDÁøJâ^ŽöHAˆ sªû}J-­-CFj(õÐD0pÁO6¾òPÈó_Ì̵^û§ZħÍÙÂO©¯|hÚ£¸¿Y‰z„ªê½¿øjbý¹An‚BöÍÞ˜¥MulS•‡ þ`Åß8•TLx%ðçBÝÆñ´]i†øÞúB"CÕ#ÆæakKäB?@`+˜ùúœé!nu|07›ŽÁ+íàKÍûÑ_ÚðFýžÜCÚªNܶ;z`C+kbTÈmY.áë ÏBR£Üž¢ZêŽb2fJù³P6(5gXñõ€úêÙ\þ% Ö-BøÛ° Ñ‹²h)²ªíb»µ`zé;¸ mxÔæŠi´ù_‘¶ UTC EUÜo”°À 1 €€QUP+÷õ!€«Ð Æ„»AžTp|!Ð`ÇúxBé.@V4uˆó ½´/AFâ‚!èK4 Ûö‰HÀ…ª‡íBܳ^PI>_Ö€ú3*Q€%{Ééèk%\½¬/jÑ3,’Å«—psTà  ;J¸'x'o­KíP¬`Üô%âó‘o¶ò• $Ó)S¬Iù;³ÀWH4z ®ÜÀ«îýzØ™ ¶t‚Òèók¯Cÿ‚+ ÂßRL*l6;2s ¯|.,<=eßjüœ- 뇶žCT4…oŸ ÓŠä!œJQÒÞŸÔE—ÂJÀãºØÈ h € å5â§€ëUó¬šk '½!PD$ì`õs…È•¤t@ÞA£*ƒú üB pˆá3mÐ¥^å”qáà9 ³nJ—vKáöR”œ-¤¿+­†±Ô3ËNߘse0€]Ì1«ð˜ €9ÁÑ:Cã£Ý Üô—ã:.7yÝ“AöZý_üÎÚ•š'0e³¤€@è(¶Öž 0W±§Fv£ü# 2é‹{1Šr¶ƒnQšX)ƒ‚›ƒ´¨t.êTlÅ'Úu=Ÿó?ªã¤4Ñú—ø?ÿâX%ù´´%iË€Z…€›&0éÞi¨¼&¿Ô䩃_i‹u…fÆô‰h°¶-·òÎnÇØoc»y;ì­êÓì´Ê¯–½‹ÕK‚a¥”:ÔB‰Û”º©zx"ä AoÚ\p*\æ4¦½j &ÐN-„:,68¥qÏü ´«ÀK|d'ƒnÐÝTªlG@ÞU³oü 1ar(˜`çÎ_¸ É›$Õ+/¼ !1ˆ'1Û{@ÇEc ¡¶ÿÅ>ê:ÞHŠªhÕ9—Ç>µXÃlÛ(÷`M["Ûï­ÚÌè Û‰Ûë4í2ÑÞ¦mk&Èš4µS(–™È-òÁT3(_½N3Ç õ}m³ÍÿæeÀ ôÑ:…îZv+þ-f«öðµöÁš~‚ûÁG ½û&rfÚ¤\t¢Î½g¦£‡ ÈòsØnÙ ” Fn-Ck(j²Ë伡꾯•Aã‡d ‰á,Òê–ÎA—ѱ…”ÝUb¦X^>²P°.ÿ)ò9âÜ<éˆVÙøkr6ÖÀÄDøUCõµg Hzã A–úÁ¥r½¡ NcÔªz ?°˜öÁzÞÌ;ðšàì™õ–”Wî}å¿Ḁ̂蟉j Äk0Í" 4,úKªàoôˆ1ÕÅ;ƒ¬ÊKPÀ¾ ù`‹ÝŸ='÷ü ‡ùí7ÏúLp>Öhºˆý1ýJÒ`è¸V“N?Q/'¤½Œa­œÿ3p¶Ž¢=l”/š>dRàpÚ¸#M.|T–.m/“ . Õ㣠èâòÇíŽZÍš·Ì´©|ÉA´ L`ͨȸ"´4”8†éÐS»Î[h ®Œp­¸V°¸"ô•á £›G[˜Ö©>LJžÒ/§ürÎÐÎÄìÍxM>aà?RNLx¿«§ôôëN7´íÚ,ßàQ1ï3‘¾×´]}”ûî._'•›´Ü’P6dÛ_D±²¾Ï 6k¶©ö¨7+:[õ9µaýòàÖ‹«WXìGÐÞs¼&&|‘jÈý Ó‡ˆöœÇ`X4cí^]sõ¹ö'Ãø™ÇP±‰ÙƒÒt ÛÓí ¤Å©®;3³5e; &Ø.Oq-ᕘ¨ü+ ‰r±dKs.ƒè’)Ð|é&4ì«éÎWrއ·Ôå&Ô <åÌì€h"x&ˆQTÞÓ2ÚõáVуÓ0|Ô†ad2XÁœ t¸ ð}œÀw©ydM¥®x{RHpJâÜù7ô]þ/}üMÕàÖê5µí­8Õ@¹'1~×YDïüœ«ˆ ‹ÄQÜ é8¶ú3oX¢+¥ØÞñìÎÝJó0šO$ú”6‡›P7R¥ó`'G3=æàiÜà*ñ|Êm‡4¶7e›8s„»'Ðè¨Ý xÅô ° MÀW, ïÜõ^”¥4Þ¢½GͱCY„N2.¼7|X{¾‡0¡vŸ£SµJq&8åü$ñ¦§ú2¤€«Št©Sä‡Ê˜ÚZÄM´œÔ7J¬][5¨‡ƒ@—ˆ+X舞Á[ab]5îØðjo «¸t)ä0qRÎ:¿³úgòQ_÷i}Ì`º!1\ºÕŠÆX^d¨¦cWkѧ"ÕkµUææcÁeÍYk‰ØTéAˆ϶¥&£€éô £ißí(ø£Áø`¡äóíNüŽ¿Œ÷¾¦9JpÅw}¹Þƒþd_ú&r€˜:ô‡­×ô¹:$ÀH¶mPå%#1gÀEÔq¢Ói\×¾à q’ÏŠšUB´ H…-y"0¸$’¥c~ý€Zã§ ¸¤Ï_Ö€;CHôÜ€Çʯž+3áew²?¿Åà. íºeÄ@ãz]\À…­Óûè sñšÜ>­é€«ð_Йˆ´õƒÐ%¾áe›¦JH¸ô7ÀÈÀ>©ô:˜9nôÀ¿½\„±âYŽ¡ÊóÓô´òTP@¿9î÷j².™øÇ|…@èë@X~‘¦xœÔÈàÐCYb>5S³{¶zÁÉñ€ P È8¾ó€"=ï˹tO_ Dƒ½û½ ^£°u¿–Ûm¶Ûm¶øa¹‘l@ÿÄ)!1AQaq‘¡±ÁÑðáñ 0€ÿÚ?ÿæ¡ n y¡gcBû]+à~Ý« ³‹›NûW øæ­ŒÇÑ3¥~ɯÙ«S(3ÇXikr}ÿâ@,üw©eƒÖW=ÿgw^Ý«y3‹ã¦·í[JèÕË?S¤×'ŸÒ¹<þ•Éçô®O?¥ryý+“Ïé\žJäóúSrøïý>:×5™¿o ´=KïÀÌb$럶.w¤$¸±'À¯8óDB‰x!rA¢¹ÉÈŠPÖLˆ•1{Á¶D•ˆsiÏ@ïF]«2„q†Übñµ-¡±%ßfœzÔ¢4«mÞ}þ©~=™pÑ&!^:íî±¼Q,B¼NojKoŸ×­un„ùCV3ëjj 2XˆåhmµñOª¸X:á"RŠ`¸Ê!q.Â$ë·J°‰fë ‚{å;Í*« ²&’hLè–t’¡Ûþpwöi’BÖÚi¶²ñ¤ü×ú¿jÿWí@ãËö¯÷_ºÿuû¯÷_ºÿuû¯÷_ºÿuû¯÷_ºÿuû¯÷_ºS>_µ«ö¯õþÕ4ä—C_Þ¬mVIævý 1•½Aö·òÿ]•l–/¤½à¦–ŽA„d·}fi.S¤ ëuö«­,òuƧÍIxžeû¤‹Èásçæ|U›ª<'¸DéK.Tå¿dzB±züú¨]¤œôùÛߊVÁ×ñNœXqÞ48ÓWb¤E®:ñ~ ”¤ö/Vú¾¿Æ+L_Æýé%’.m" ›´Urjç<šä)^†Z¶ÚäzÔ² _£èㆦ¹~Ÿ̲¯¤t þáåÏ›õõ¦tΓŽñDÌ­¶Û½#)â_jÛ]ÿM!I 2NÛö´X¤C ÅÀ-µÎ‘ —.ˆGµ´ö§P ”m¿rl—‹j †Å±Ñ‹GöCøH¦oíPıv"vÎOÖ¬ý‰ÿÉÞýûjvù¥2»l'õÝ­@Y͉ïø­Óé{ÏìPC!CtùÇ­1ä—÷;ÐxXâá÷æ­>?ŠÅ!gOG7‰‹d|Z1¤Ô,¼‹Ö$O C´oS{#m¯íBA &Ê2i“]»Fhs{wl|Tk««û·þS8ÕÐQ‰ïE p¬zë5'P%BkogjƒrÆ é¶ïOz™[=_îí]bä^bÎ,3漓¶±Úþ‘Î)·¬¾ÒüzSŠP@R=$Í'ªÅ³Ö„@]–&÷ƒâ”mÙ¶«–ؤ*-›‚\‰¶Œ˜²hSuwÿ  y˧Î)ÈØÔÒ0›G=&²–@™f"ÁΦfº44ffý.ϻڒÂX‘/ïßvÆDr‚'º.Yu·Œæžb(ÄÂdíl}MB±nþ¶Ï­eTLOÔ¡!Y›ùôô i=¿Ê"Æ gÅoE-z=/¿H¼;¨Ÿîî.âÔ0Òí®‚m<¾Ÿøª(;º5aY eÓXÒ¢ "‘˜ÌÿuccÍ´ø»µ37€Ù_OïWœöûþ£¿ñÄO™¤šà …ˆPÁ{ÐõšLµ œ;¡µÚ0´ÀË¢…§×O¸„¬0rÙq›2m¦ô³®r,Œë˜oůÿFi´'` 1°;ùµ7ŒhË þ-ší>LzRfrÁ/ŽºRê5˜Ì1±[æ¤äZG‘×®ý)Zà¨Õ‰Û[Xo4‚ֹͺÿ”ZX Á¦sˆâÀéCIö÷çÓæf0îäïÓÖ–föµµ7}uÞ™’ζuöæÓQŽŠ&ao7ãZ…˜Ú#ú(T"t1Ÿyúÿ´@eý4$™F/6œ‹eÁ±M–„Š[2Ž3éÞaöŒ{¾³jé>óˆõ¡Äooö‰ÈÉ7èÉQçuÞÑâ„@åvÁúhd”JK"ϬŸFDIcgõöé6!D1‚ÝKX5ßF)S!‘$$"ÐͰ1¥,ðhmÿQ bxZø¿€5špÖíâÝgÅ@Z|vÉ8 6a[cSO5Ð v›Clb‚÷ˆá™›nýPvv%¾«fb¤]O_ÞÄ\ˆK­ãÁ¬ÙŒÐ:i‹i{õÖw½¦mV)×LL[4’IC<.NÞ×(•®q‹,¾µ2K¼ñ¥µ½B €fÑ-˜'»ÒRi2ÊYÈI=OOô ÁG!KžŒ]鎉…L&¤öëA€1ø~b¸µŸÜT bèíîó@t?³ßôTˆr³!bûú8¨ 0 x]M§š€–lô0x½B0ixÀ³¯š|BÁBtÔ‰ÃK:A±ïÿ@¨X¦E€Œéy¿z„‹q-ý Þ]hÄ/ž—ø·´´Aoê8¡âbKAº¾Iï¥Z‚÷¯nÿ¬W ¬8-ß3q®=èp²ˆyïø§—xÄ[nKäàÇ`™ÞØi1n0âí“v%´k;#ñÓE×HÙ›‹²Î… ‹ª ÜrÚ1Cÿa8©e4ÿIë›2¨(È–¤èD˜º‡ ©Unê«Ï"…Èo0?4L½›ñæÜzÒ»Z/Ø"c­nÚ4"‚Á*ÆžÄëDRä;è—ú£$ „q]Ȭ!ªÜÚ-·Õö«„„ ¡$ÀÍãajYŽõ_˜8ÿ´3%OE#±8šºY:YvùÉF޶ѽˆŽcS=>ê[¤ã6‰èߘڇCAxŸ¼kÒ‚À Üç_H¢Hn`ÀؘÄsƒ…Š—Lk«y¾ zÔKr2[ÏPªl À¢ÿºgá©*ß‘¦3žiÀÒ"K$†Ï’|Œ£°Rbí´0I’R/Þ/i›_ßúŸù@ºØ¦éE]8(émoõPo$ÝoõÞ…ssuÊ—YÏîû{á~ bƒ&ºFþŽÒ8éBbЛ¶éœïAbѤ^[þ̾”Îé#Žs­±¾”ÅDPY#SŸÍ=Ë\&Æ}—â ºÊiÄóó¤R–;ç}yïÿ€€qué"` 3o|ç\æ¡4#$|jãÇX`1)7ç¿÷D°-È7yŠvŸ6ýû’‰ÀG^-¤s¬SæÒ~ž}Z…\[­üþšXÁ¬Cðê_JDé÷R¬Okéžœ@'±Ú×;sÒ‰%b#f÷Ûbb£®[‡“m·ïþ¼£ Äß{{¢}é¿üÄe`\-bò»qïVoɃt6MÝp즲 ÜQ]_Í/Dcm$ù~9 6ÄÆ³'ãÖ‚ÜèòT6è#¼FmcZónŸé”QÊqc½½ºŒc0þßÖŽ@µ±o0ïÚ„X”K@±=I±}Xݺ«s–ágf]ðY5l&ŒûoW„šôJÀ°.å™éQ082èu·©-cõ³müï[‘Ãi·Í.ÃŒ©6´íC\ Ô“HíçŠ÷;c®b»Z†Ònšæ']ðZˆd·Iµ±:_;ö© ÒN&tè[jFÐÉ¡½­Ïâ‡8ß'Øm¾¯J¯<ú_ïRM‹ÉÆú(âˆD™áôÍù¥³YCÔQ¿ÀÛ‘³d^ÓŸzÔ C}€f ÑÇZC:Ò,݉rÁn¾h@€G 剃KökÚø5“YúÒ€3Ú%ô‘ï½-ŸÖpöæ„/õÛµ–‚L^¯§€< ‰CƒXÏÍêb Ö7ñ­íX cd‚Íç^1-Í*e¼Ý–W)3·ì³ÿ’S6‹ÍÕSyí/rU˜ :.N$æõÏæ£ùüÑ 4L[Icͨ±0â/ÅüÔÜá$¤Zê\\Eà ´Å–;=êÛP÷±8úÆ+/÷noÖ[Z¤V5õêÅó0kz³¿1?#¢ÛÑs¾ôrj¢ÂÁ ¼æÑPI|éëßÌQdµop¼ÿ šðë±h‚"€ÐÚxg}©³)•n·bŸ\Q)‘â'ù×Ú„h9ÚyÍbïÎsD-‘yèwôm¯ióí5¯õÔ맂ɇ(g–òóéB¢€´ÂMã‹Ç®jÅnÉlŠ;[áïJY™As}Ø¿×þ6*bˆ ëÕÓÚA($K¶c}ôzÇþBx]Š&ÂvëÓƒJHéùã3­ [üw;T ­ó0óŽßzP W)kΧÃAaøyÙ¨ˆ7@¾d–] Ú”xCÞ~6Ûš²JÅ”bxBѤT÷yhÁ¸Fëîæá„HmþliI FVpèN£°­Óͧ¥˜ç3jšÁb^çÉRYftóÒ2êÑhÀ1ñÒ|£@µ¦x¨4‰EYÆc‹|[` GÚ(pK]þŸßŠ'°”EæDؤá~šC*«6o8ޝ‚µþxç¥ÊÈ7éooR„’õ;©Ä&U{M¹øÅ±û KAÞìȶ2n½(e6„€MË¡˜ÕœØŒ†P&LI“Fm›Q¦ž”’Vb;ƛŠ,!¨F~¼fðnaÒÓ’c#B *J€qy¿é¡DTaÒG4Ç[ µ,Ã8¨Â¦ÄòñÝêDò‚äºi÷Žlà¢MèF—ð{kZÓˆ~£¾'J‚)m ¯¶Ñ®÷¼´6Á}ískž” &’N:¿F£$dhg}#ni7$èǧÍ]’TÝz&½ªýnEùŸÝ±z»jí µô¨þ4zßU4·n'M`¾ãJe¹­cÇ½Ì ÑïØ¢Bß ºk·yN*'·œ¿¸iÔccýïF:`ç_;g¡B-»#oMÇLNjZm%•‰Öæ£þQ®Âcãñš!ö¿Êœ¶`a?¯(b 3hfÿ=¡WµÈÞÞ?ÜE@ı“ÒqmgµE6¾ùµüþim䬡—M%¼o§¼a–8Íÿw’£$o9·ã{kv¢IÓ¶qi© ÜLÃõš„¿½·í‚B$úóÄK[,ÞÏÍ‹ôƵ8Ãh¾¯7^í ‘&y¹Ò6ý``í7ñ;M-%ÚsãïÕÂbwÒ#'ϧ†Ð`0Î"Öô]æ±8į£7ç}éEEÚ±}bußSb@–$z>ô–P…]o×öò•€’,G{kC¡}ZÌ©e«|`ƒ­EòëpÆäéÞÅíå èÄ^ë/òwš ºqÐÉùÚ 8Ô‡M/ˆïÐ…˜ºoìR†ß¹F‚'²š” BöâxÄE\@6vƒ{>ýèÀ[Íü¸É9¨’,ˆ°‘ßÑAY‚LYxñ·ZT¹Ú7F-¿÷7ÙÇ÷ÝßûÅ‹«ÓÆ8 ´'m¾ ¾³}í™Ï]©¦[|õ‹kAm™ö¼úiõ,Hªû;s»ëJã¯NcÚ…aU´b „óž§H¹[®|WâìoVD3ÆMy;ílÔMˆ3¾÷Äf€DþÃÖG—v’Ì_K³3hÇ:ëVÖÙ<Åéȹ¾J T2³iyS±¬ˆ¢Ö°+i´†C;ÎT‘u’óœÏíeNU–^¬}ñ±7÷‰7·>i3Z ñŽoæ.3BA)6œM±tœé·!H¾‚37}ôJ‹/©zX÷)ÔW-¬qôâô9o!ÌnÝÇÝʈŒb'LÛ3sV¢«+ª¾ñëA˜†Úm¶ÞådÀÌçh>ô6žŽ½ÜsïF#3homïÓKšPÛ.ù¯O4À3%ï?´â”1­Ïõéxµ.¶0dú|XÛ-Sá|ßÚ5ŠÆ-yæcºÐ˜q\3´ìí¦ô“õ8e¯æŒg_LÞÝõך8˜Td}ËõÍ\ŽC›Ñ0mz9ô¡Àu4ÏkÅÄXŸ§çÍFŒŽd–¥ÊEó zpÔl™ÿuçæžs¼B,Na¸Çy¥X,4óÞýê]ß/ýØ«e´ BÈ€4×m3E#—¤[öÞHp˦¿¥JPR†Dè\Ž­Âô«±ÁÇõoâl7}–­ŽS:–×2sb–’oŽì}ñŒLß-û'jâu@·ÿYíR.µý‰æÙ èÇíˆõ  ,Ú8ÓÅK»çæøÇÅl.î='ó4?¥(4­-ó ¦t¶˜nŒ³/;ëÉ*I.Æc]sÚø¨ðKˆ[رx±xŒº1 Ș ZÖŒïÏð’Tå†\¬äŒ÷½.ÊW3›ó·Ö†*&Ç®>~Ži3óûJ›O‡î¶ åJG':ý×â¸OÂh‘DqSßS÷Úµ~^Ñ4hG›ú“P¬ÌáqÎØ£z:Ãâ¹_UnÉ´»=ßB˜71‰g÷Ö%µ!BñŒ“Ez_ÍBZ@1d'Ö?b†h¢ÄJo¡¬^1-´zëÛÒ—&‹dÆÓ{éþäz—ãÓ6˜ÅY8ÎBÒÌDáË /S<Ž<~Ò<#O1Ç_ê–Èë³ûÖ¦l üÿTÚ$X·—¤ŒEðõ¥ÄŒ.ýu¶<Ô!L”èß=ºñQ‰ÛÂÜÇ[óšB˜n€:­³r÷m¡HL-²%µG]'+es<^ñ1ÿìF` LƒWÕ8fœ Ê:ÅÖxiw_7»]D÷™ßãIÖ¹u ˆãêVâu éwå\Ú<Ú<³÷ª£céõ‘£b[“¸¾ŸÅ%ømoÖ¼úúm»5”žU͉cª†¡@̈D7”õ˜ÌBdd˜ŸCã P@ÊVâý#5=‚d ¨á M˜'1¡6 †¢éý}o("<;Þ'S?éW»¦oçN#ZI¢4±>lëÓÅ:÷sÚ¡I³y~º÷«0#d=Y£X/{ZúêzÃQ!d¼_«3ëK‘@†Œ¶-‘mX¦H‘af‰/$Ýg6¦X€R224ÌqÑVg_ú—wËRîùj]ß-K»å©w|µ.ï–¥ÝòÔ»¾Z—wËRîùj]ß-K»å©w|µ.ï–¥ÝòÔ»¾Z—wËRîùj]ß/ü˻楲LÝsæ‚~‰PÛøæ­Ä?П§ðn}Oº¨móö H³™=)Jݾ’ãýÿÄ)!1AQaq‘¡±ÁðÑáñ 0€ÿÚ?ÿæDA‰…&31˜{P‰"&åÏùHV0VËl3Vð[ST¨NÌ&¡\êˆêó{PL*šRªs­öÝÚ’M‚v$Þp € ²XfËÃJc¯þt˜[+wÀý²Js& çT"xŠdfeŸ¼¯­#·ÓæÕ~÷f0Û‹Å7°âºoÚŒK¡ÄÛОý¨ŠÆ³ïô¦[q>óùŠg-÷¹B·z¥üEd®ƒ½¯Jîxšúëý£·ø n$÷‡ªøŠšf&ì &êÚÛÓé{ÁfŬé"üb¤\sÆ‹mÙÅi²o¬OÅlµdYà“ï¹&Tß=ö;4†Ù•õzUÊMäg¸ïP-ñÌzK«¾±ý­yõM÷¿j¹g¿ŸÁR Üï{ã‰â†ù"‚cX‚.åKlP=¥ÊZñ ZÝ«¦€&l€–e`.ôO—Ñóµ Ë‹)8 ýÅ:Þ6‹1¢û}kù/ͬKÁ3á×¥+Mätyþßü’?G’iK1¸ßؤ`»Ë Íó~>7pâ D¶ ôöâú H$ȸ\øh“™ÓH‚o­A†g8~mH–¶ûEóv™>_ºq.9"Ý阄‰ügõWÕ•.ææƒ¡ºÅ½Ý^½óíoÓü>ò†‰ Y¨pò Á+kÿ)cª$¾“&wÝ¢O0õ»l±v’›ÚÀ]Ðö¬œ‘küƵ.PÙT–(F¿ˆ„Ö˜ÒôpK$É@ÄÊÆ^µÀfåâÍÆêxšoEÓ¡êÇh¤5-ÛÙ¿éI¾{/oJUÉö1dñ¸—oÞJá<l®uʰ™–d†!Ûõ}h-<‚!…݇þÜû l“–&LD°5€MJD[×ãJ†þ.þýÖÞ´•±â¹o4ß:_W`m´4jÍŠáö§4ÆÖï¥,¢ úöÌ¥íDAèLLŒ6š’ƒ‘dUÞÚóL'÷£«ˆ^5 °ñ{˧J8_º`¢¼JJï¾1ný©›Lì "Ë:7):L€÷@!×z“ ==ƒß¯Îd˜¶žI¥”’H¤„ÍŒ˜‚ n ±/Aõ&ñùÖ°ÇèiçŒïRO@ŒÄzbûîÖé6lƬêrXõ"¥ˆ)Ýø‘ [»i1òüu¤ˆpZ7¹:³¿ËMøJP.b$‘¶;R…#tÉ,ÎÙaâ¹tli6Û±Öi[7¼ûè4!oç§­©7dø}'Í©&!L˜çLzMqfèͳØéjc0ÇÒÏV*E¸Ä9Åý=º¬àI$!t¸¼c8\È ±PDâˆj{&¬ëá;úÑ=ý_°h"ÀÞþ—§*´ªRè"˜&%ÞÓ Â+G0&ú––‡¤Ñp0I6Ë¥[/P ®DÉéŠF'—röQ>i.wÄvœóYo#¥ß˜«2?Ž–sëC ö¼ß=WÒ <“!‰\­k§”0Ïï§I`—Kâ׌GŠ,ÂEF`Ü&]±AÅÕ™+Ü^fS±G€ÛÜb‚—)KØ‚T£(–C6©)²–²”K{‚$. ¸Ù³Å#ieYÚmšJ’n1oݨlføÅcW15.}£‚"ÚÔ¥ÄHH·¼ÎäoOé€óˆ0ò€ÄFF’I7½£yÔÆ7´ ñŸ˜™×ÒõžX îùsµ »‰Á{nô©%çSN§â s7½Ö÷ÛñÓ)ݲÄ}z<Ë,íó®¿Ê) g.Ñ}mÜÔ¾ÿì* A±à¨6<Ç‚ ØðQ HÞ­¡&Ñ5êL°€±j²ÞÁX5‰3¹,ÒbÎt™é3˜ñ1ìP»/ÁG‰—I´u´4îÚwˆŠ9SPÌ$™€µõ¡âC¤X±{ì©(F ‡)†ý¼°yOScum36±L‰2DV7k}M¶fŒŸì ˜\ C1r±ŽúQP„œŠ™Æ Ziä^c’ÊŽ‘H(­‹©7©ŒÀ Œ„a²¨NÅãi…— B š&(™–4Úvz“ ™›Eã6tóŠl˜Ñ¿ÇNØ©f˾<¿sï¾ûÀxï@:¾™Èv©­c¯rQÖ*Ì–"@^ÏKèô‰m1—oM4÷É $• "BB¾¦¿‰ùá1In®p¹d0´†R‹ÝÍ€˜ZË®VˆØP,¤£[<”Y]mÒs³WWN‘Û7Ö¶´üãÖ…,¯Yôç­a&¹×z_A`寴.˜¼Š €07´sH ÃX½Ð“õåáyˆÆ³Þxù¥,À˜ŠA &SÀ&‚ÕÕêÿ̧]Þ•êØñP YÇ_š¾³s]ç^·¤ÙJù»Dox°Þ³‘‡E¡…Õ'õZÀsÛFñ:Üm4t„‡IcÒ÷Ù¹2ß×SñÈßµJY7ÉÏÜtt§‘5Ï%ïâ‚UfâvÄÏ S3ÓýqE Ӭѩ<¥¦RîgCðšR‰’YÝsŽuë60IÉD†bbR"wffmÿ‚$ƒCv&(AT2·91™V…æ`€E Æ¹¾i–f_bd ûkLe¹1kzgР'N¿hw%·´v(£Fÿ‰ø¦°B°böÖx§Ø Å2äõìíJ͹L“—«Ç™›:¸ô‹Dä·ÅªÈ‚ &DZRE²I2¬3ã¯_úPGd`|ΜqH ¶ÌE†:[䡿]Üh™a­Ï3âöß¼Tkv8ç3ñS™¿W3ßӨŠË-±´ÛδY•ÝÛ§h`†sÁÒm?¥¶ wÖçš1º®†¹ºÒ¬<€lß?b˜”̹-˜Óæ4¦B+6œLéǾh±ržù„.ÈE©ÒCä#T¤¬)ûBàÝ`5P±Wˆ ÀHMÙKµ€XˆrJ$´Ybœ%7ÚbÙÓ'êig%·1lãðSAeþÜýRßuÃóñHŒ±üm€ŒJÄ7VÞ´7%$;ž/­Ô¹nfY+=f4’†U,æ[ÇkkZŒ'”’GYŒ(8,'q˜„ËyŽ“Çý4Ë MZ±JÖ´e'îi ðîsmišÄݾ1¥éŠÙÓ<ô}é¿d•Ès·¡f|Ó¦V™þÆ}š¼åvzx¦¼±ÞøóåJwÄÁ¬ ^!í 0$Ò#}hô¹À…ˆ¾3ûÄÓ%ç—MvéîS(žÜ¤ ÕÕ‹¤`¿Á…B7*ûpàD® @ÆêDÉ9TPˆ7T´é‚¥2 # ‘‘œ:jô±ÅŒgžºÔ¼ZÓö6Ѭª¯wyѦS yüO­Y†Ü o}ö(IqU,foóéY=ˆA=òR ‰Q”feV^y†,Ô$37C3îϽð!c#€HÅ™`J:ÁÌ# ƃe(&úM®û×nk1t¬Ç½_I±™rzkÔæ&ģޓ­-…ÕÈKI~úþi%VKjÞßmVů›išoiëñA9R[Œ¿OÆ*dŒ½õÓѧS îcž½èE”ÖÖ±›‘ؼT ”îbXÍø´ï@•]^/|tÔÚ¡z@¸V+pE$âö¡¾À B*KŠIac6?å8†£ }ƒš„axB«"×UEÛÙ)l$0éꀉ Ø´G›æ®*èe=¯çâr3;k:¦|r½‹ë÷§Íl$–º:õÆÕ{xff-ʳö&(¨…æ\ž'[ߤ‘©è*–gGGlQƒ,îž~Û¶id€ÀF" &ÁªR¬90› k`ècÿ%¨áƒp¿j²\PÝœìöÖ#* “·Ýºº³–›—ÕéÕ"Þägëd÷±ñ÷j+«Íâþך=ŽHœ/×Ö*áäÌóx¼é¾ýÝó®—})õ†<ûy¾´ÉyDÚoi´ï~%éJ& áÔ¿=;Ÿ‘|؉×Iã €‘«ùü^*T¶Yrp°É›A2ɈFÈ–dÎá@UV.ªØ-% pÔ^t7Òø‹3DåèÄ.JÈÄÍúk@t@Àbц›EK˜[¾»Îzžõ’føÞÞ½ßLP wo¿Ž´f·ïnqo)fï9¹Äk˜}¨/…‚½‰,c "fu³|b¥atí:&- ¬¶.ñ›ä ŸJeœµ`XBYóa2ijAЦ[´#ÿ F‡ u Hâ£Ê”T Mò!3kò¹´ÑÚ&|þêó-¢aëÞ>)s$Z—oÌö ‘1¶˜åŸ³¨ÑdHnãŸb(G‹vKkÚc´±d3»éªÛ>”È“ LÄÃ÷=jk:ÜŒ_NǼÕÔ†ÿYü›ÍHYGû§“œUÕ™L¼óø¨Fc7™ÛÒ•l­•”%Ä×-¥AXî…Πr»ÿ¸ÍLÃ3ÎR"Œ6R™ð†‹Q”Ýz¸¢ä)  úÔ¦Öï½óžzÒǦǼzS†f/¡ïžýªË|“¿Ði³3lNh@‰¤¹bä;ì7&™‚UT”3ޏ«Å×VGƽ&§`QXê2ÃaÔ È¤‹b–Tݱøƒ„&å>^> ùX@ E\ybhLBJ¢f†ŒÞ÷®Ç ´„ÞˆP¸›ÂÔr dÊ©œšb¡e¾)2Rè&Œ¨I1½ù·hA0ÍîÙ]q¾û7«“7Ç[ϘÛôÉPŒékfÓ:í[°}åê`ãÃ4Fá<Þ8cǽ:-œ.Çi/¾8¡I³ƒ‹ü4Ò4âq¿Ùú‡$ bI;ô%tn'ðÌù¨X0†K[X×Ö®¦·~sw}4j9¼>lçîé ¾Ó¾ÿ¾)oÇ㯥£H ¥à†osz‹%DšÃt2zZŸ¢ V ø¤U”{yw¡²& §+Öëb¡S ¨@šB÷ƒþÝ0 WB¢ ‹!È&\*LCvg3¥pRÚ@…®ºÀ HH0qz Ž_ù”¶œLZ{Ñ¡lŽÈ$!H'Pp !£]÷óÍ_XNX¾™ß#G4 ³ ú8Ç^î*U$"ÆÞgyßÖ)rÃ×Å‚xÅ+ý=ë¥0$‡a63ÚŒ£×Í6ÚÍÂN…]‹¼â<%¢Ö”R—Y!C‚“›Ï:Ñr0¡ï%ó{Û7³YË7ƒYç¡oK¥\Uà’;ä÷íE;ß\m1¥¯JNIµçòûP§[Ͷ¾gŠˆJ8fÞ›þké.ÈŸ?ŽÔ¡j²LÛíüÒò=á”°Zù˜þR®Q²œ¢Ó#±¤0r¯#u²e¼ÝYºÓqM”rÝ NÞ™{m`Í 5ê³R„ÿ,ïÑß?â23$XwÀ–ñl4¾ð!¼d’£rÌ@À¢@UL Ê&ŠŠØWB€;LCn=qQˆF!Ãç;Mó?2%äܸpŒëÌ °E€ ‰,ZÑBf„Õ!¦Xn¸©®µ Avói9‚¯~¿Jxº€-e[Å­Ô©L¢Éæ÷âodê)u_¡7©‰\Çx´ºñíJÁÉm¦Ñ´çLÒã^üÝ&yh ‡u¦3Õ(Pœ–‚a—i… 1÷¸©èñ9"Á´À°–K»×‡Þ)I! D7ˆÉÇù; è|Õ³Êq›:âs;&ú_˜ÄްÓmÙÚÿnèÓÎã:§kÅY‚Fä~;]ëj@7w™¿˜³ÄÑ¥Òú¬Þñ¾‘ù­ ’f×ði~„輩 8ˆ'îÒMª Êbö·c¶›Ðpî¯Ýºy«˜m¾½¯o­´³Žv×­¦Îu¤4ƒZ½†/1¾_ÞÖ @ [ܛûÚá¿!Ð4¼›ßORÍŽ ½ý\é¨Õ´éÞÝÊfªR #k‰«š…4„€DIÚzLM4¼”’ö38ä¶Ôã8··#¡/NÔ:¢X.úüÉÅD2wÛ{Ü6þI £`"g¼Ôñ!Ìã_‹sÞ‰'OÁl{ÃEN‘Á3cJ à]³hy~á %VwY¾O¦“Íp%c`œ}3P"ŠÃvn½øÜ©|EEt™„„Ûb¡µ8æg€EQd ˜cRXaD4*x/ÀsA „A˜”,«Τƒ¡XóŸš#ßtLó'ÛÿŒÇcÉïÄvéPhdÉ<ëÒõ ™o:˩Β.6¡9;¹ôü4f¸e¯»íO‰aë|s%@I¶ ±Æµš{^Ø÷í½šôqrý1åþA9mhÚöõ½¯R29ŒKÌGŠL\:ÍþzZˆ7^._Êïßʃ‚ú—½¯o¥!‹s|wôX‰¤–õÖï;}»J¸™ïµí臨×Õ¹kG¯tµ|ˆäÌßH馒ÃpÓIë8rS¡4dÐD-¹éBzJ(„ËiÚŒˆ-™OÍNh&zöå¼ih/H«Äãìé¶!ðÅ.¤¶Òy×N™Æ×ì©áõdÒ³]óøgÇj Á„FàÙ&Ñp®ºIX3ú­ ‰H‹6LMänQ”)B"蔑&"g.c `ˆ‹(SÞ ØñþäD¢« ðÊÁ£P&( Ò i7¼¿ÉÆùŽ?Ïù0bŽ^¿où0ðzØçûo kv—ïïšì¾ñéhqlκ½£·\€å`§‘=&øÖ/ Ì©„‚¦ðº´*6ìNªuɽ ì·ŠXek«¹6÷ÉÁÉ›RÀHį Ñ!;Ü‚À"šn [z¨š¨›é•v)}û8c&~ÿWÛÂS_‡KÆ–Q=ÙÂÍõŒ%? ȺJaˆ-jûͲB”˜qàohШQ!¤(Õh²%y([0I¤¢ßÈH›§é5ß_ÔtBÍ’“G`$J_ëx}³øøš£\`4H†9´]ãi$X•g—Í,Ñ'‰ƒïˆðO²˜•µìSӟװÇ}£®ð‚…Õ«ÏŒ9Š/As'œ==`íY()Cl”ñôÂrñ¸ÿrrÂúÚuï”yQ÷ª©ÉA„$2 "pñXÖw²Æ±T;‘V:~r%1 ŒÊå`Ñt‹zF¢ÚG2H8˜×ÿ=@:cK,È’ÍÊÏìb•æ=«ÿrÚè>ÿŒ^æ;¿³„ܼµÏ½FD@^G¿Ë㈯¤ùëÌ`¡’}~›Ã" ²£¿YÇ–öDí=0F„HÑÕ}û³ø?Â(ç 0öIbCšÄMvY* 𱋤ˆ¯zãÛvOt¼kÆGT³ò|NC¡Ú]¸Hõ͉ˆn‹Ý|¿qEÇ´ŒO×"*õ÷g}°·ÁH‰]‘ï• .’Nú}\©1c_ŠË’q­^æ»Iõtbwç eÓfë]Ì ÏÕä"@+ŽbYŽpÄ”!;m™À€tB¸R‹ÚjuüÇžûs6“Á¥ ðT•÷Þ.7ÅžWø.tuA3Ãúɉ¢ÇÔú^:äð¿1“èüËóA®Ä_—x ¹9–¾X\=`B`¥[jã´ázãI‹×Î ÔÃèÏ«Û,k¦ý`ÇX{ÍVñ?ö¾ø”nÿŽ[#L_‰1n ´˜Þ‡zG!A‰8‹þO½e1ñ¿‰R}Ü)vjPŒXâ:€¬·"Áb ² ¥·÷.Á±GNHZˆØ\AžÁ$b%qÄ8ú½gбS-Ì=c‰ D½ÜڸÔŒ¶ž‘²" $ˆ¦DkëÂ@”é]z\yÀbEöÝwü`PºêO%×Òk®"WÐ*㓎聆 bã§~øN;£D_Ï| Î"i¢¦õ3Ž2KTÕö’·¾GýßÁY'0Ô©Y„€N@D#Î!*aŠ„Ú«ª$¤÷ãRóîÈ]kÑxúŽD—‹ÇŸÕé‚ ¹Aê¹¾˜ïéãøZ¨7g^\óFISÆ ÀÊ…i›è'BYÃP nzœnI*veY=¸23«éÄâ1o„룜ÿ?'ÖLh0̯sèUßB±ºm×v8Ÿ†;eÕ’O€h´ù¬E”TRà”A‘@¹F‹S”{ëÒ<™2£Hˆ&ÈŽ{uþ ßÎTôa°¶GíÄ„ u/Ra¿zBJZ²Ìôp[°$‚E6‰í¬…u…¨*Õ‹ÁI2‡…±«Œ;¶$ˆtéÌýñd®JWî¹ì…D„@ Í…lCI™ô#€"Ö©šhŽ}9ŲHÃöåðG¦Mþ¾zvyô‚bGý™½¾’dyCQJ¦ºaΫª˜&P8è¼`e˜Ü•C×ÞÕÏÓóüèECȦ=qieªŒ nFɸ¶Š1ò’fáÌÅþ¹žT¦‡šïˆ]î0¾7dóÝ뢰r'ˆ™O·AþãÐý« ~ãü±. )&ö<ÖæBE‰A ÜB#e³¯œV˜ZwÖ~¼aa€Ø32P8n>‡ÙƘÚ*FvuýuÇ’/ˆ€™˜ØaHÙ/©gd(9®¹K ‰Àšî۱C­DNºå,ôbuÈŒèñ8ø@" Š$õþprC„2ÅO”äØîkVÝæPq‡Ä‘¡²0„PÌèrs˜ «I-Ìuñ<0UESÛœLQ›ˆ MòSŽr"! P‰Ã]qá^™·2'ÛˆÒnÊ Wn1€µE!”HÅ&÷Ó9›\1qŒ{dBÁ£·ùÅ"%vı[¯öûã*†«¯|pѺ:Åd,D5 $ŠžœÓ< )4q´lî°½B…:“N½þŸùXª€¬šÈÁA¶VXë]ž¸àškn ÙH8 "ݭ΀óñÄ5íùq¶‹ÙãÑÀ}:÷ñ×Òq®àŽÿ}ñ0*… $FÍÆòð@ ½A1JöˆÄ)GBGŽäÊTiT’oN0‘ ©P„yÊaÔÈ ŸÐˆÞÞ\S@à‹ÞøÉ‘VÊSÏg’§"X‰êâ&pC$ /ÓŽ† IÍ×1§ÌtƃÀóÖò,vÊDKQº™.z:w./8@ ~ØÎ-6‚Vm‡s£ÐXv³íh@ƒ#˜b`ÒÆ—“H%$BÏY—£J!B!0§ ?µýßxfæ \±P”l†Â€N£¸«Îa °ª¬âÇD·® q›¡–ªÄ5…ˆ©žŒY5¼iÔS¿Þ¸RJ„#u_ºÿ—Z1Å.€l¦Zâ·I‰àTžüwTJ$2ETÁ¢„±Æ­€4J± – Ƨ¹fVÙf⿱ÂW´‚žÀ.sJ¾B&Àã8¦’Àv>ž²Ôe”Nø~IÈ4ôD•\±=œ;×¾¡ôÊ h¢ž,ç^ýð…ÄÑÔýäÈŠÑzíð`³€ž'ÚpXQÇ  ðµB¢ÏÖò…1$-ãÐLS DH 3¢&£ ŠO¤xË-i‰BÓ+ÆÜ "¤DV Àælcû‡ËLĨZ½‰PÇqMô{„ldp(ÁLO]Jb(ÃYLÐ5Ùß%EqvzbèG¯ûË㜅$ÔýãxD6g¸“ßÅsÜÈÒF¸YAɸÍå±7 Ò•%3m‰*ÔäD ¥”Š EÆ‚¨B]ðR2‚1%î Ì2l¢ »÷Ÿî¤¢² ôm휢ܳÒOòrÀ˜h¸5Ñ©r Òлá–,I–ЏVcéš°‘ÑÂôñ€…Â"Þϵb¨2Eò‰>ÞØEÀ®”ð%>¹Xúº£é1ÏÝjX€ˆªúfž¶e@’"b/ ´ŽõAZ /ˆÁDôÄà€­°ÁåŠæ4ˆ¤žHcy*ªÆ{‰^N?°VÙÕW÷€ÄàņB’Á‡f>ñ€I;¢j“‚4€Á€ˆáÃ+Ÿ©vnç ÷цŽÀ}Ly=P€êwqtåI6M‘Åñãß'JiUhÝ{àÛ«L@ª\ˆš¥6(  êàÄ@f`@#«¼‘Õ–$I'}æò<‘nu1phìxÿ¹ÉWxPP¨^Ä…,@&eTg ŽÛÄÁF]58]LTÄlF!"À²”‡ObÐïta…Û{ÀBˆNK ‰àÀ¨½ ‘A ‰ÂTÓ‰ŒJö¾çÒMYÐõÉwW O¯ìq‘ù_: ã´^°ÚRÁ°Aȓ܊4jÐ7¦EÖ—l j¿À@Š×*1@ˆOòtˆÐ­”P µÉ 2qýPU  «ÀdÔ·§h‡(U†WKì²Üy ‘xË2"CcÓ!X!È×ΙõÞã'F²ßZãÌSü7Ë̳ÎðMüLÀp®=þ1}TÐFëŠ8:ç.`P„MŠ(Ä„ã1X›»p,z¢{¡†Z"6 t.úºü7Œ¢DFB8DtÕ ­ŸH¡.…^©‰BJL¶°Ìþž; ÀÔupªëÁŠùHPáKG¿maQ‚@²p“7û.£Ñd@¨ÜÙU1…¦vHtÞ* @0L¬,±H>ÅnÈH±Äºÿ1‘¤n jºýsÄÐÜ‘ßlÛšŸißý¬-?ȪT¸TÓøC¡Õ±X®˜ÉÃ()Í•µÎ`´E¤Øê1¯Ros¶Ê‚Šäu¶#oa)ÈX'¬È L/É€jõ;â_Ù¼-ŠA££«3#ú/Ñ K,»y8H`bJ(,¬ê{ä€@(‚ùIãGþ[ÈÑ‘€„f+]YŠÇÔ`²C6ƒ7Ü‘z(C;œ9EÔ×PŸœH(Ø©X¡aš™(EƒH…ov•ú“l•ö:ÆAIžÇÙ?îRšcª˜ ƒšöÄ‹|ìù¾N4¤5Àާn›½cì!ÅUÖHÈj¯x w.A¤& ŒG.OL™S0í¯‡ëÿ2 sžA6RHB¸Dí¾j(ÒáÊ!‚ÇïŒ!;|Ìãò6澚ð8à­“½ðOšÈ‡Ã¤I퓇0„“Ì_c§=òEU@"Ö€“ưó•¶)³¹0c¶COMõ!=ÅÒ ÀYv3éÐ@aLïkuÅ`€0StCgŸ;ÿÀÃ,„•^%pf« “%*€=Ø­N’¢W… `ÌÒ"ï³áãldêWLtÄ‚*ÉÊ\Ar0Ò$’,òa6V“a¹ÿœíÀ¼pc$Z¤‚F@™:1‰SÇŸÀ£Žc€&…—ž$Ç ºH}#¼ÏÐv™ˆ]Àäº=ù]m¤ Û ’Ö@zœh ˆ. ö  D¶^Ø#2 °ã‹–:Ö Ž=¼ë^™PÓõá™ö IzñÕŽw¬±BÌ‘|ßKÀ?¾*Êïqï{fˆˆì+`q8’‰‚¿\"¶´ Ø,nf¶8]ZÊpl\&v+M¾„ù!b·7ÕÝIài™½ÅhA’L'OÕõ}N9_°¾8`Ÿ_ÇðªÐÈ­¢«0”l‰ÌHa*‡f€-Y¬H’S‚U%e*Õ±’ %1ÊÄ Q!Ã(²• $AƒÑwh«’ –…q¥^IȱÁJÁ´+"’ƒ JèàÍÊÐ’³€A E€ø_œzàNÔõg¶¡QîÕ R!f‡`¤³½ƒHŠbšœ'¡$ÈN³‡ Ø„½™Z÷È+Ú@ÌFW¿vÊ”A(0[ÜZ=u¼e´|MçLÁ]wÄü ôØQBâ^®C'è0A@®îN]&a(ÁÅçëü8Ĉ)lPOHx\’;Zø õ›p„(—C'˜=}&L1§±QûÆB<¢{í92a:­îgŸÆ84P‹n½úúeêÄ­ \ô>}Np"~ Mf*'Á °w)n #QHÔ Y‹ ]ÃÓëÐÍú…Úµ)·Iú8žCvtã¿l›yg.«Ê½±²ª"Öçðq†ý2ÝÃõâ²2†Ô<¯wŒÓk×ï篧8/1Õ¨2ªM^A7ˆYˆïì˜ð‘S*¦Öæ;ÞªIB’€ iÍ\uË+/hIÈ›Åà¡ìB$ ïž7ÝP´Ñ&wËÓ›”Jr+°‰Ã…×_†¢5‰¤³%‘©¹Ÿ|•%R€ì‡Š;ë’D‰JB™¼“ùq„⸪8ÓŠ|‰É$=CcÝ)PQ,¬mb6_VVPÂPu@UižÕqK¡pq(ë°Öx0)1´Û/`¨X˜Mm\쎮5–ªÀ¶pè<'e™^d%›§ 8ÔŸ›Í¦"D$êGoŸÄvçP‚û#^;b$šò·]Ý_dÇ[GK±]>‘òY€ä“è³·“k› ‚]¡h™Ýà€@F‡*0,!Ltr ¢Lx4h'Q½0&˜ ƒ°qýf˜Ñxr&ÃAË diÍ"§f2@X”cÀ((#§9ƤHËŠ †¯¼oB¢ •Ü&ofXÑÀ§Žo‚]`])ôßÃßÎJÇnÃ!ªuxu=56³ƒ«!ßõúzàS)®W2‡¨à!DZ<@GŠÆUäC_NÜ>ו“u.àŸ¬÷èYV¥U'_÷ ËÐê÷ï„höó¸:ãÖzМw}rÅêé’O«®aÏùpôûâD?kE±åÝÁ¥Ç‹s×ýtãQ­†Š5þ=£! JlݽFEX G$>·‘-!=÷¡â0!\S=fu߯â$üWdðƒ«Fòýu¬AHNÝ_µáøÈ©Å\{¦]e4Ì élì|ã90™ÄTPH‚ò_6 ° çÚõœ+v£eöÁŽàäkBâ ’{†\dý€RÂÎ"ã¤ì“^ÕoˆB"€¬(ƒÊI9õ`_ ‚$Œ'õHêÝLLóc@²†HaŒAdI\ûMDÛóP°®À9 +Ù°aªç‘˜B"ÔÊÏ€ÈCjb¨$–yëA’þèÑ"¡U÷×óNÈîò(0 …b ¦pAl3H(QãXªÞ|«Ê'Þ ‰ú<5=ÜàzÀ™ƒŒÓ#+Î'ë둚ùÑ’$8Ôpgt;àþL,RŸ«ç®¹/WùaÅ)ÐýÙ4ƒ{zW„ÏÈw‘5&eó‘ÕÝ#8a“¶H˜4ä¯có×*4™IرDEHîb{F?vL¾Ç¬aK€—s$"EQU‘™œ ½W0P„Z(;Ø/¤51Ô¼,˜S ²É’16ÉÃuKw N®s(ù?8b=÷Û~ë¶w/ówÉd'I.Vú¤Ec^kÛì\B]fð€tZÚÞIÚ&÷„9T…ê ËT©,¡ ƒmÐÌï ßP Pk+•j ¦ÐRzOy‚4€@c`Pÿ@’œìÞVxBk­äÎ9E¥fÄs=~ ²Óˆ{±%kŒÿ‘üàÑýætÏÁÀöÞƒíŸðŸŒÿ€ü`|¾Gãf.Øý§çÛùŸËˆˆ€ê_µgêŸlSð~Ñ‹)WûLæÝó%ã›é—=ÎH©¨N¾¹n”Rf‰K%æ°Q#é5õÅ õ% -ØÂÝöœŒ J $ˆ@)"NB…¹DÜ$$TÀL1Ö,L–ò¿HêÖH·¡!|ÃWy* º%–|9ã%•¥þϧ\piiWçホêFC¸^óïÛ ;áÜmå?8ù°ºÃ¬ ™ßÙ—‘ÏIa¸•&M0ãÀ¢*TMȤ¢æ© 0^ˆ(éDI¡ W{ÛûíýPbANvYÙge–vYÙge–vYÙge–vYÙge–vYÙd<0€¡ýØöëyÂRJ< âÆçó}òh–ý«‡× í9Öç­¿áp_§Aß&hºåE3{'ÎÆ «žW«ß4G>ÿýÿÙz3-z3-4.8.7/resources/z3.snk000066400000000000000000000011241356505360400155100ustar00rootroot00000000000000$RSA2wz8\–€´ÓŒ:ó[¿¥ë[ÀßîÉ[b'}ó`u]¯$zô_PàÂþbQŠ Ê/ŽÅ׺€Œ“ 3Õ¨¨Ø5RÆu8 ÓÕ0€]€<›NîÈä+ð5ü‚AFíº‘Ã×)§ þk[ ? ³*·¸Â[€òPpòæ+2V˺[†END•…dÝv,ŒçªILôÔ&¥ïQ½ÿ¶‡}ybÛF@»Mûï)Ý`L26GÎþ`H!îÞ¢ „»vÐæ}®–ì¼Q¡g:»« üøùdœK§z˜s¸+ž¾û×Eû Jø;åôêçK&¡"y©|ê^ QÅðÃ9-Ïiq­ááQÝŸ“ž…ò’9|Û¤,3¾´ï­áUûm•ÂJ1Êâ½%ÙSÒ,‡ÿs¼j¢w ޏePí4õÚ€l6 E:Ú@rC {|{—n±®ðåLðšS@9}eäð¤ËQ9W³ÛN­$7Ì99ìTëh°Y7£}›a¹n߄׵¢q‹Ño0@å°–©¿!dÌPYv8æ¹ ³Á/Â5[te–éuȉ.*÷¬kå@Ǭ5ÛÖi—>J )!œ$C”v:F±$ò£pþâîÚ‚œª$¡’[^‚JŒ<ù`”»yKFÐ+TÄž(÷VHëC9¾°…2šÆÁU„LûDÜ_«Í­¯¼¡Š(’È…‚ք羓+φ„ù¬?„•¾µãà.U¤‡ÆÑ`RhÇy1Sz3-z3-4.8.7/scripts/000077500000000000000000000000001356505360400141165ustar00rootroot00000000000000z3-z3-4.8.7/scripts/README000066400000000000000000000006071356505360400150010ustar00rootroot00000000000000Instructions for updating external Z3 API ----------------------------------------- The python "macros": def_Type() and def_API() are used to add new types and function definitions to the Z3 API. The .h files provided to `mk_bindings(API_files)` contain these definitions. See src\api\z3_api.h for many examples. The bindings for .Net and Python are generated when mk_make.py is invoked. z3-z3-4.8.7/scripts/authorization.json000066400000000000000000000006411356505360400177120ustar00rootroot00000000000000{ "Version": "1.0.0", "AuthenticationType": "AAD_CERT", "ClientId": "1c614a83-2dbe-4d3c-853b-effaefd4fb20", "AuthCert": { "SubjectName": "1c614a83-2dbe-4d3c-853b-effaefd4fb20.microsoft.com", "StoreLocation": "LocalMachine", "StoreName": "My" }, "RequestSigningCert": { "SubjectName": "1c614a83-2dbe-4d3c-853b-effaefd4fb20", "StoreLocation": "LocalMachine", "StoreName": "My" } } z3-z3-4.8.7/scripts/generate-doc.yml000066400000000000000000000002461356505360400172000ustar00rootroot00000000000000steps: - script: | cd doc sudo apt-get install doxygen sudo apt-get install graphviz python mk_api_doc.py --z3py-package-path=../build/python/z3 cd .. z3-z3-4.8.7/scripts/mk_consts_files.py000077500000000000000000000055441356505360400176650ustar00rootroot00000000000000#!/usr/bin/env python """ Reads a list of Z3 API header files and generate the constant declarations need by one or more Z3 language bindings """ import mk_genfile_common import argparse import logging import os import sys def main(args): logging.basicConfig(level=logging.INFO) parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("api_files", nargs="+") parser.add_argument("--z3py-output-dir", dest="z3py_output_dir", default=None) parser.add_argument("--dotnet-output-dir", dest="dotnet_output_dir", default=None) parser.add_argument("--java-output-dir", dest="java_output_dir", default=None) parser.add_argument("--java-package-name", dest="java_package_name", default=None, help="Name to give the Java package (e.g. ``com.microsoft.z3``).") parser.add_argument("--ml-output-dir", dest="ml_output_dir", default=None) pargs = parser.parse_args(args) if not mk_genfile_common.check_files_exist(pargs.api_files): logging.error('One or more API files do not exist') return 1 count = 0 if pargs.z3py_output_dir: if not mk_genfile_common.check_dir_exists(pargs.z3py_output_dir): return 1 output = mk_genfile_common.mk_z3consts_py_internal(pargs.api_files, pargs.z3py_output_dir) logging.info('Generated "{}"'.format(output)) count += 1 if pargs.dotnet_output_dir: if not mk_genfile_common.check_dir_exists(pargs.dotnet_output_dir): return 1 output = mk_genfile_common.mk_z3consts_dotnet_internal( pargs.api_files, pargs.dotnet_output_dir) logging.info('Generated "{}"'.format(output)) count += 1 if pargs.java_output_dir: if pargs.java_package_name == None: logging.error('Java package name must be specified') return 1 if not mk_genfile_common.check_dir_exists(pargs.java_output_dir): return 1 outputs = mk_genfile_common.mk_z3consts_java_internal( pargs.api_files, pargs.java_package_name, pargs.java_output_dir) for generated_file in outputs: logging.info('Generated "{}"'.format(generated_file)) count += 1 if pargs.ml_output_dir: if not mk_genfile_common.check_dir_exists(pargs.ml_output_dir): return 1 output = mk_genfile_common.mk_z3consts_ml_internal( pargs.api_files, pargs.ml_output_dir) logging.info('Generated "{}"'.format(output)) count += 1 if count == 0: logging.info('No files generated. You need to specific an output directory' ' for the relevant language bindings') # TODO: Add support for other bindings return 0 if __name__ == '__main__': sys.exit(main(sys.argv[1:])) z3-z3-4.8.7/scripts/mk_copyright.py000066400000000000000000000023531356505360400171720ustar00rootroot00000000000000# Copyright (c) 2015 Microsoft Corporation import os import re cr = re.compile("Copyright") aut = re.compile("Automatically generated") cr_notice = """ /*++ Copyright (c) 2015 Microsoft Corporation --*/ """ def has_cr(file): ins = open(file) lines = 0 line = ins.readline() while line and lines < 20: m = cr.search(line) if m: ins.close() return True m = aut.search(line) if m: ins.close() return True line = ins.readline() ins.close() return False def add_cr(file): tmp = "%s.tmp" % file ins = open(file) ous = open(tmp,'w') ous.write(cr_notice) line = ins.readline() while line: ous.write(line) line = ins.readline() ins.close() ous.close() os.system("move %s %s" % (tmp, file)) def add_missing_cr(dir): for root, dirs, files in os.walk(dir): for f in files: if f.endswith('.cpp') or f.endswith('.h') or f.endswith('.c') or f.endswith('.cs'): path = "%s\\%s" % (root, f) if not has_cr(path): print("Missing CR for %s" % path) add_cr(path) add_missing_cr('src') add_missing_cr('examples') z3-z3-4.8.7/scripts/mk_def_file.py000077500000000000000000000017751356505360400167310ustar00rootroot00000000000000#!/usr/bin/env python """ Reads a list of Z3 API header files and generate a ``.def`` file to define the exported symbols of a dll. This file can be passed to the ``/DEF`` to the linker used by MSVC. """ import mk_genfile_common import argparse import logging import os import sys def main(args): logging.basicConfig(level=logging.INFO) parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("output_file", help="output def file path") parser.add_argument("dllname", help="dllname to use in def file") parser.add_argument("api_files", nargs="+") pargs = parser.parse_args(args) if not mk_genfile_common.check_files_exist(pargs.api_files): logging.error('One or more api files do not exist') return 1 mk_genfile_common.mk_def_file_internal( pargs.output_file, pargs.dllname, pargs.api_files) logging.info('Generated "{}"'.format(pargs.output_file)) return 0 if __name__ == '__main__': sys.exit(main(sys.argv[1:])) z3-z3-4.8.7/scripts/mk_exception.py000066400000000000000000000005001356505360400171500ustar00rootroot00000000000000############################################ # Copyright (c) 2012 Microsoft Corporation # # Author: Leonardo de Moura (leonardo) ############################################ class MKException(Exception): def __init__(self, value): self.value = value def __str__(self): return repr(self.value) z3-z3-4.8.7/scripts/mk_genfile_common.py000066400000000000000000001123221356505360400201410ustar00rootroot00000000000000# This file contains code that is common to # both the Python build system and the CMake # build system. # # The code here generally is involved in # generating files needed by Z3 at build time. # # 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 import re import sys # Logger for this module _logger = logging.getLogger(__name__) ############################################################################### # Utility functions ############################################################################### def check_dir_exists(output_dir): """ Returns ``True`` if ``output_dir`` exists, otherwise returns ``False``. """ if not os.path.isdir(output_dir): _logger.error('"{}" is not an existing directory'.format(output_dir)) return False return True def check_files_exist(files): assert isinstance(files, list) for f in files: if not os.path.exists(f): _logger.error('"{}" does not exist'.format(f)) return False return True def sorted_headers_by_component(l): """ Take a list of header files and sort them by the path after ``src/``. E.g. for ``src/ast/fpa/fpa2bv_converter.h`` the sorting key is ``ast/fpa/fpa2bv_converter.h``. The sort is done this way because for the CMake build there are two directories for every component (e.g. ``/src/ast/fpa`` and ``/src/ast/fpa``). We don't want to sort based on different ```` and ```` prefixes so that we can match the Python build system's behaviour. """ assert isinstance(l, list) def get_key(path): _logger.debug("get_key({})".format(path)) path_components = [] stripped_path = path assert 'src' in stripped_path.split(os.path.sep) or 'src' in stripped_path.split('/') # Keep stripping off directory components until we hit ``src`` while os.path.basename(stripped_path) != 'src': path_components.append(os.path.basename(stripped_path)) stripped_path = os.path.dirname(stripped_path) assert len(path_components) > 0 path_components.reverse() # For consistency across platforms use ``/`` rather than ``os.sep``. # This is a sorting key so it doesn't need to a platform suitable # path r = '/'.join(path_components) _logger.debug("return key:'{}'".format(r)) return r sorted_headers = sorted(l, key=get_key) _logger.debug('sorted headers:{}'.format(pprint.pformat(sorted_headers))) return sorted_headers ############################################################################### # Functions for generating constant declarations for language bindings ############################################################################### def mk_z3consts_py_internal(api_files, output_dir): """ Generate ``z3consts.py`` from the list of API header files in ``api_files`` and write the output file into the ``output_dir`` directory Returns the path to the generated file. """ assert os.path.isdir(output_dir) assert isinstance(api_files, list) blank_pat = re.compile("^ *\r?$") comment_pat = re.compile("^ *//.*$") typedef_pat = re.compile("typedef enum *") typedef2_pat = re.compile("typedef enum { *") openbrace_pat = re.compile("{ *") closebrace_pat = re.compile("}.*;") z3consts = open(os.path.join(output_dir, 'z3', 'z3consts.py'), 'w') z3consts_output_path = z3consts.name z3consts.write('# Automatically generated file\n\n') for api_file in api_files: api = open(api_file, 'r') SEARCHING = 0 FOUND_ENUM = 1 IN_ENUM = 2 mode = SEARCHING decls = {} idx = 0 linenum = 1 for line in api: m1 = blank_pat.match(line) m2 = comment_pat.match(line) if m1 or m2: # skip blank lines and comments linenum = linenum + 1 elif mode == SEARCHING: m = typedef_pat.match(line) if m: mode = FOUND_ENUM m = typedef2_pat.match(line) if m: mode = IN_ENUM decls = {} idx = 0 elif mode == FOUND_ENUM: m = openbrace_pat.match(line) if m: mode = IN_ENUM decls = {} idx = 0 else: assert False, "Invalid %s, line: %s" % (api_file, linenum) else: assert mode == IN_ENUM words = re.split('[^\-a-zA-Z0-9_]+', line) m = closebrace_pat.match(line) if m: name = words[1] z3consts.write('# enum %s\n' % name) # Iterate over key-value pairs ordered by value for k, v in sorted(decls.items(), key=lambda pair: pair[1]): z3consts.write('%s = %s\n' % (k, v)) z3consts.write('\n') mode = SEARCHING elif len(words) <= 2: assert False, "Invalid %s, line: %s" % (api_file, linenum) else: if words[2] != '': if len(words[2]) > 1 and words[2][1] == 'x': idx = int(words[2], 16) else: idx = int(words[2]) decls[words[1]] = idx idx = idx + 1 linenum = linenum + 1 api.close() z3consts.close() return z3consts_output_path def mk_z3consts_dotnet_internal(api_files, output_dir): """ Generate ``Enumerations.cs`` from the list of API header files in ``api_files`` and write the output file into the ``output_dir`` directory Returns the path to the generated file. """ assert os.path.isdir(output_dir) assert isinstance(api_files, list) blank_pat = re.compile("^ *\r?$") comment_pat = re.compile("^ *//.*$") typedef_pat = re.compile("typedef enum *") typedef2_pat = re.compile("typedef enum { *") openbrace_pat = re.compile("{ *") closebrace_pat = re.compile("}.*;") DeprecatedEnums = [ 'Z3_search_failure' ] z3consts = open(os.path.join(output_dir, 'Enumerations.cs'), 'w') z3consts_output_path = z3consts.name z3consts.write('// Automatically generated file\n\n') z3consts.write('using System;\n\n' '#pragma warning disable 1591\n\n' 'namespace Microsoft.Z3\n' '{\n') for api_file in api_files: api = open(api_file, 'r') SEARCHING = 0 FOUND_ENUM = 1 IN_ENUM = 2 mode = SEARCHING decls = {} idx = 0 linenum = 1 for line in api: m1 = blank_pat.match(line) m2 = comment_pat.match(line) if m1 or m2: # skip blank lines and comments linenum = linenum + 1 elif mode == SEARCHING: m = typedef_pat.match(line) if m: mode = FOUND_ENUM m = typedef2_pat.match(line) if m: mode = IN_ENUM decls = {} idx = 0 elif mode == FOUND_ENUM: m = openbrace_pat.match(line) if m: mode = IN_ENUM decls = {} idx = 0 else: assert False, "Invalid %s, line: %s" % (api_file, linenum) else: assert mode == IN_ENUM words = re.split('[^\-a-zA-Z0-9_]+', line) m = closebrace_pat.match(line) if m: name = words[1] if name not in DeprecatedEnums: z3consts.write(' ///

%s\n' % name) z3consts.write(' public enum %s {\n' % name) z3consts.write # Iterate over key-value pairs ordered by value for k, v in sorted(decls.items(), key=lambda pair: pair[1]): z3consts.write(' %s = %s,\n' % (k, v)) z3consts.write(' }\n\n') mode = SEARCHING elif len(words) <= 2: assert False, "Invalid %s, line: %s" % (api_file, linenum) else: if words[2] != '': if len(words[2]) > 1 and words[2][1] == 'x': idx = int(words[2], 16) else: idx = int(words[2]) decls[words[1]] = idx idx = idx + 1 linenum = linenum + 1 api.close() z3consts.write('}\n'); z3consts.close() return z3consts_output_path def mk_z3consts_java_internal(api_files, package_name, output_dir): """ Generate "com.microsoft.z3.enumerations" package from the list of API header files in ``api_files`` and write the package directory into the ``output_dir`` directory Returns a list of the generated java source files. """ blank_pat = re.compile("^ *$") comment_pat = re.compile("^ *//.*$") typedef_pat = re.compile("typedef enum *") typedef2_pat = re.compile("typedef enum { *") openbrace_pat = re.compile("{ *") closebrace_pat = re.compile("}.*;") DeprecatedEnums = [ 'Z3_search_failure' ] gendir = os.path.join(output_dir, "enumerations") if not os.path.exists(gendir): os.mkdir(gendir) generated_enumeration_files = [] for api_file in api_files: api = open(api_file, 'r') SEARCHING = 0 FOUND_ENUM = 1 IN_ENUM = 2 mode = SEARCHING decls = {} idx = 0 linenum = 1 for line in api: m1 = blank_pat.match(line) m2 = comment_pat.match(line) if m1 or m2: # skip blank lines and comments linenum = linenum + 1 elif mode == SEARCHING: m = typedef_pat.match(line) if m: mode = FOUND_ENUM m = typedef2_pat.match(line) if m: mode = IN_ENUM decls = {} idx = 0 elif mode == FOUND_ENUM: m = openbrace_pat.match(line) if m: mode = IN_ENUM decls = {} idx = 0 else: assert False, "Invalid %s, line: %s" % (api_file, linenum) else: assert mode == IN_ENUM words = re.split('[^\-a-zA-Z0-9_]+', line) m = closebrace_pat.match(line) if m: name = words[1] if name not in DeprecatedEnums: efile = open('%s.java' % os.path.join(gendir, name), 'w') generated_enumeration_files.append(efile.name) efile.write('/**\n * Automatically generated file\n **/\n\n') efile.write('package %s.enumerations;\n\n' % package_name) efile.write('import java.util.HashMap;\n') efile.write('import java.util.Map;\n') efile.write('\n') efile.write('/**\n') efile.write(' * %s\n' % name) efile.write(' **/\n') efile.write('public enum %s {\n' % name) efile.write first = True # Iterate over key-value pairs ordered by value for k, v in sorted(decls.items(), key=lambda pair: pair[1]): if first: first = False else: efile.write(',\n') efile.write(' %s (%s)' % (k, v)) efile.write(";\n") efile.write('\n private final int intValue;\n\n') efile.write(' %s(int v) {\n' % name) efile.write(' this.intValue = v;\n') efile.write(' }\n\n') efile.write(' // Cannot initialize map in constructor, so need to do it lazily.\n') efile.write(' // Easiest thread-safe way is the initialization-on-demand holder pattern.\n') efile.write(' private static class %s_MappingHolder {\n' % name) efile.write(' private static final Map intMapping = new HashMap<>();\n' % name) efile.write(' static {\n') efile.write(' for (%s k : %s.values())\n' % (name, name)) efile.write(' intMapping.put(k.toInt(), k);\n') efile.write(' }\n') efile.write(' }\n\n') efile.write(' public static final %s fromInt(int v) {\n' % name) efile.write(' %s k = %s_MappingHolder.intMapping.get(v);\n' % (name, name)) efile.write(' if (k != null) return k;\n') efile.write(' throw new IllegalArgumentException("Illegal value " + v + " for %s");\n' % name) efile.write(' }\n\n') efile.write(' public final int toInt() { return this.intValue; }\n') # efile.write(';\n %s(int v) {}\n' % name) efile.write('}\n\n') efile.close() mode = SEARCHING else: if words[2] != '': if len(words[2]) > 1 and words[2][1] == 'x': idx = int(words[2], 16) else: idx = int(words[2]) decls[words[1]] = idx idx = idx + 1 linenum = linenum + 1 api.close() return generated_enumeration_files # Extract enumeration types from z3_api.h, and add ML definitions def mk_z3consts_ml_internal(api_files, output_dir): """ Generate ``z3enums.ml`` from the list of API header files in ``api_files`` and write the output file into the ``output_dir`` directory Returns the path to the generated file. """ assert os.path.isdir(output_dir) assert isinstance(api_files, list) blank_pat = re.compile("^ *$") comment_pat = re.compile("^ *//.*$") typedef_pat = re.compile("typedef enum *") typedef2_pat = re.compile("typedef enum { *") openbrace_pat = re.compile("{ *") closebrace_pat = re.compile("}.*;") DeprecatedEnums = [ 'Z3_search_failure' ] if not os.path.exists(output_dir): os.mkdir(output_dir) efile = open('%s.ml' % os.path.join(output_dir, "z3enums"), 'w') z3consts_output_path = efile.name efile.write('(* Automatically generated file *)\n\n') efile.write('(** The enumeration types of Z3. *)\n\n') for api_file in api_files: api = open(api_file, 'r') SEARCHING = 0 FOUND_ENUM = 1 IN_ENUM = 2 mode = SEARCHING decls = {} idx = 0 linenum = 1 for line in api: m1 = blank_pat.match(line) m2 = comment_pat.match(line) if m1 or m2: # skip blank lines and comments linenum = linenum + 1 elif mode == SEARCHING: m = typedef_pat.match(line) if m: mode = FOUND_ENUM m = typedef2_pat.match(line) if m: mode = IN_ENUM decls = {} idx = 0 elif mode == FOUND_ENUM: m = openbrace_pat.match(line) if m: mode = IN_ENUM decls = {} idx = 0 else: assert False, "Invalid %s, line: %s" % (api_file, linenum) else: assert mode == IN_ENUM words = re.split('[^\-a-zA-Z0-9_]+', line) m = closebrace_pat.match(line) if m: name = words[1] if name not in DeprecatedEnums: sorted_decls = sorted(decls.items(), key=lambda pair: pair[1]) efile.write('(** %s *)\n' % name[3:]) efile.write('type %s =\n' % name[3:]) # strip Z3_ for k, i in sorted_decls: efile.write(' | %s \n' % k[3:]) # strip Z3_ efile.write('\n') efile.write('(** Convert %s to int*)\n' % name[3:]) efile.write('let int_of_%s x : int =\n' % (name[3:])) # strip Z3_ efile.write(' match x with\n') for k, i in sorted_decls: efile.write(' | %s -> %d\n' % (k[3:], i)) efile.write('\n') efile.write('(** Convert int to %s*)\n' % name[3:]) efile.write('let %s_of_int x : %s =\n' % (name[3:],name[3:])) # strip Z3_ efile.write(' match x with\n') for k, i in sorted_decls: efile.write(' | %d -> %s\n' % (i, k[3:])) # use Z3.Exception? efile.write(' | _ -> raise (Failure "undefined enum value")\n\n') mode = SEARCHING else: if words[2] != '': if len(words[2]) > 1 and words[2][1] == 'x': idx = int(words[2], 16) else: idx = int(words[2]) decls[words[1]] = idx idx = idx + 1 linenum = linenum + 1 api.close() efile.close() return z3consts_output_path # efile = open('%s.mli' % os.path.join(gendir, "z3enums"), 'w') # efile.write('(* Automatically generated file *)\n\n') # efile.write('(** The enumeration types of Z3. *)\n\n') # for api_file in api_files: # api_file_c = ml.find_file(api_file, ml.name) # api_file = os.path.join(api_file_c.src_dir, api_file) # api = open(api_file, 'r') # SEARCHING = 0 # FOUND_ENUM = 1 # IN_ENUM = 2 # mode = SEARCHING # decls = {} # idx = 0 # linenum = 1 # for line in api: # m1 = blank_pat.match(line) # m2 = comment_pat.match(line) # if m1 or m2: # # skip blank lines and comments # linenum = linenum + 1 # elif mode == SEARCHING: # m = typedef_pat.match(line) # if m: # mode = FOUND_ENUM # m = typedef2_pat.match(line) # if m: # mode = IN_ENUM # decls = {} # idx = 0 # elif mode == FOUND_ENUM: # m = openbrace_pat.match(line) # if m: # mode = IN_ENUM # decls = {} # idx = 0 # else: # assert False, "Invalid %s, line: %s" % (api_file, linenum) # else: # assert mode == IN_ENUM # words = re.split('[^\-a-zA-Z0-9_]+', line) # m = closebrace_pat.match(line) # if m: # name = words[1] # if name not in DeprecatedEnums: # efile.write('(** %s *)\n' % name[3:]) # efile.write('type %s =\n' % name[3:]) # strip Z3_ # for k, i in sorted(decls.items(), key=lambda pair: pair[1]): # efile.write(' | %s \n' % k[3:]) # strip Z3_ # efile.write('\n') # efile.write('(** Convert %s to int*)\n' % name[3:]) # efile.write('val int_of_%s : %s -> int\n' % (name[3:], name[3:])) # strip Z3_ # efile.write('(** Convert int to %s*)\n' % name[3:]) # efile.write('val %s_of_int : int -> %s\n' % (name[3:],name[3:])) # strip Z3_ # efile.write('\n') # mode = SEARCHING # else: # if words[2] != '': # if len(words[2]) > 1 and words[2][1] == 'x': # idx = int(words[2], 16) # else: # idx = int(words[2]) # decls[words[1]] = idx # idx = idx + 1 # linenum = linenum + 1 # api.close() # efile.close() # if VERBOSE: # print ('Generated "%s/z3enums.mli"' % ('%s' % gendir)) ############################################################################### # Functions for generating a "module definition file" for MSVC ############################################################################### def mk_def_file_internal(defname, dll_name, export_header_files): """ Writes to a module definition file to a file named ``defname``. ``dll_name`` is the name of the dll (without the ``.dll`` suffix). ``export_header_file`` is a list of header files to scan for symbols to include in the module definition file. """ assert isinstance(export_header_files, list) pat1 = re.compile(".*Z3_API.*") fout = open(defname, 'w') fout.write('LIBRARY "%s"\nEXPORTS\n' % dll_name) num = 1 for export_header_file in export_header_files: api = open(export_header_file, 'r') for line in api: m = pat1.match(line) if m: words = re.split('\W+', line) i = 0 for w in words: if w == 'Z3_API': f = words[i+1] fout.write('\t%s @%s\n' % (f, num)) i = i + 1 num = num + 1 api.close() fout.close() ############################################################################### # Functions for generating ``gparams_register_modules.cpp`` ############################################################################### def path_after_src(h_file): h_file = h_file.replace("\\","/") idx = h_file.rfind("src/") if idx == -1: return h_file return h_file[idx + 4:] def mk_gparams_register_modules_internal(h_files_full_path, path): """ Generate a ``gparams_register_modules.cpp`` file in the directory ``path``. Returns the path to the generated file. This file implements the procedure ``` void gparams_register_modules() ``` This procedure is invoked by gparams::init() """ assert isinstance(h_files_full_path, list) assert check_dir_exists(path) cmds = [] mod_cmds = [] mod_descrs = [] fullname = os.path.join(path, 'gparams_register_modules.cpp') fout = open(fullname, 'w') fout.write('// Automatically generated file.\n') fout.write('#include "util/gparams.h"\n') reg_pat = re.compile('[ \t]*REG_PARAMS\(\'([^\']*)\'\)') reg_mod_pat = re.compile('[ \t]*REG_MODULE_PARAMS\(\'([^\']*)\', *\'([^\']*)\'\)') 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 io.open(h_file, encoding='utf-8', mode='r') as fin: for line in fin: m = reg_pat.match(line) if m: if not added_include: added_include = True fout.write('#include "%s"\n' % path_after_src(h_file)) cmds.append((m.group(1))) m = reg_mod_pat.match(line) if m: if not added_include: added_include = True fout.write('#include "%s"\n' % path_after_src(h_file)) mod_cmds.append((m.group(1), m.group(2))) m = reg_mod_descr_pat.match(line) if m: mod_descrs.append((m.group(1), m.group(2))) fout.write('void gparams_register_modules() {\n') for code in cmds: fout.write('{ param_descrs d; %s(d); gparams::register_global(d); }\n' % code) for (mod, code) in mod_cmds: fout.write('{ param_descrs * d = alloc(param_descrs); %s(*d); gparams::register_module("%s", d); }\n' % (code, mod)) for (mod, descr) in mod_descrs: fout.write('gparams::register_module_descr("%s", "%s");\n' % (mod, descr)) fout.write('}\n') fout.close() return fullname ############################################################################### # Functions/data structures for generating ``install_tactics.cpp`` ############################################################################### def mk_install_tactic_cpp_internal(h_files_full_path, path): """ Generate a ``install_tactics.cpp`` file in the directory ``path``. Returns the path the generated file. This file implements the procedure ``` void install_tactics(tactic_manager & ctx) ``` It installs all tactics declared in the given header files ``h_files_full_path`` The procedure looks for ``ADD_TACTIC`` and ``ADD_PROBE``commands in the ``.h`` and ``.hpp`` files of these components. """ ADD_TACTIC_DATA = [] ADD_PROBE_DATA = [] def ADD_TACTIC(name, descr, cmd): ADD_TACTIC_DATA.append((name, descr, cmd)) def ADD_PROBE(name, descr, cmd): ADD_PROBE_DATA.append((name, descr, cmd)) eval_globals = { 'ADD_TACTIC': ADD_TACTIC, 'ADD_PROBE': ADD_PROBE, } assert isinstance(h_files_full_path, list) assert check_dir_exists(path) fullname = os.path.join(path, 'install_tactic.cpp') fout = open(fullname, 'w') fout.write('// Automatically generated file.\n') fout.write('#include "tactic/tactic.h"\n') fout.write('#include "cmd_context/tactic_cmds.h"\n') fout.write('#include "cmd_context/cmd_context.h"\n') tactic_pat = re.compile('[ \t]*ADD_TACTIC\(.*\)') probe_pat = re.compile('[ \t]*ADD_PROBE\(.*\)') for h_file in sorted_headers_by_component(h_files_full_path): added_include = False try: 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: added_include = True fout.write('#include "%s"\n' % path_after_src(h_file)) try: eval(line.strip('\n '), eval_globals, None) except Exception as e: _logger.error("Failed processing ADD_TACTIC command at '{}'\n{}".format( fullname, line)) raise e if probe_pat.match(line): if not added_include: added_include = True fout.write('#include "%s"\n' % path_after_src(h_file)) try: eval(line.strip('\n '), eval_globals, None) except Exception as e: _logger.error("Failed processing ADD_PROBE command at '{}'\n{}".format( fullname, line)) raise e except Exception as e: _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') fout.write('#define ADD_PROBE(NAME, DESCR, PROBE) ctx.insert(alloc(probe_info, symbol(NAME), DESCR, PROBE))\n') fout.write('void install_tactics(tactic_manager & ctx) {\n') for data in ADD_TACTIC_DATA: fout.write(' ADD_TACTIC_CMD("%s", "%s", %s);\n' % data) for data in ADD_PROBE_DATA: fout.write(' ADD_PROBE("%s", "%s", %s);\n' % data) fout.write('}\n') fout.close() return fullname ############################################################################### # Functions for generating ``mem_initializer.cpp`` ############################################################################### def mk_mem_initializer_cpp_internal(h_files_full_path, path): """ Generate a ``mem_initializer.cpp`` file in the directory ``path``. Returns the path to the generated file. This file implements the procedures ``` void mem_initialize() void mem_finalize() ``` These procedures are invoked by the Z3 memory_manager """ assert isinstance(h_files_full_path, list) assert check_dir_exists(path) initializer_cmds = [] finalizer_cmds = [] fullname = os.path.join(path, 'mem_initializer.cpp') fout = open(fullname, 'w') fout.write('// Automatically generated file.\n') initializer_pat = re.compile('[ \t]*ADD_INITIALIZER\(\'([^\']*)\'\)') # ADD_INITIALIZER with priority initializer_prio_pat = re.compile('[ \t]*ADD_INITIALIZER\(\'([^\']*)\',[ \t]*(-?[0-9]*)\)') finalizer_pat = re.compile('[ \t]*ADD_FINALIZER\(\'([^\']*)\'\)') for h_file in sorted_headers_by_component(h_files_full_path): added_include = False with io.open(h_file, encoding='utf-8', mode='r') as fin: for line in fin: m = initializer_pat.match(line) if m: if not added_include: added_include = True fout.write('#include "%s"\n' % path_after_src(h_file)) initializer_cmds.append((m.group(1), 0)) m = initializer_prio_pat.match(line) if m: if not added_include: added_include = True fout.write('#include "%s"\n' % path_after_src(h_file)) initializer_cmds.append((m.group(1), int(m.group(2)))) m = finalizer_pat.match(line) if m: if not added_include: added_include = True fout.write('#include "%s"\n' % path_after_src(h_file)) finalizer_cmds.append(m.group(1)) initializer_cmds.sort(key=lambda tup: tup[1]) fout.write('void mem_initialize() {\n') for (cmd, prio) in initializer_cmds: fout.write(cmd) fout.write('\n') fout.write('}\n') fout.write('void mem_finalize() {\n') for cmd in finalizer_cmds: fout.write(cmd) fout.write('\n') fout.write('}\n') fout.close() return fullname ############################################################################### # Functions for generating ``database.h`` ############################################################################### def mk_pat_db_internal(inputFilePath, outputFilePath): """ Generate ``g_pattern_database[]`` declaration header file. """ with open(inputFilePath, 'r') as fin: with open(outputFilePath, 'w') as fout: fout.write('static char const g_pattern_database[] =\n') for line in fin: fout.write('"%s\\n"\n' % line.strip('\n')) fout.write(';\n') ############################################################################### # Functions and data structures for generating ``*_params.hpp`` files from # ``*.pyg`` files ############################################################################### UINT = 0 BOOL = 1 DOUBLE = 2 STRING = 3 SYMBOL = 4 UINT_MAX = 4294967295 TYPE2CPK = { UINT : 'CPK_UINT', BOOL : 'CPK_BOOL', DOUBLE : 'CPK_DOUBLE', STRING : 'CPK_STRING', SYMBOL : 'CPK_SYMBOL' } TYPE2CTYPE = { UINT : 'unsigned', BOOL : 'bool', DOUBLE : 'double', STRING : 'char const *', SYMBOL : 'symbol' } TYPE2GETTER = { UINT : 'get_uint', BOOL : 'get_bool', DOUBLE : 'get_double', STRING : 'get_str', SYMBOL : 'get_sym' } def pyg_default(p): if p[1] == BOOL: if p[2]: return "true" else: return "false" return p[2] def pyg_default_as_c_literal(p): if p[1] == BOOL: if p[2]: return "true" else: return "false" elif p[1] == STRING: return '"%s"' % p[2] elif p[1] == SYMBOL: return 'symbol("%s")' % p[2] elif p[1] == UINT: return '%su' % p[2] else: return p[2] def to_c_method(s): return s.replace('.', '_') def max_memory_param(): return ('max_memory', UINT, UINT_MAX, 'maximum amount of memory in megabytes') def max_steps_param(): return ('max_steps', UINT, UINT_MAX, 'maximum number of steps') def mk_hpp_from_pyg(pyg_file, output_dir): """ Generates the corresponding header file for the input pyg file at the path ``pyg_file``. The file is emitted into the directory ``output_dir``. Returns the path to the generated file """ CURRENT_PYG_HPP_DEST_DIR = output_dir # Note OUTPUT_HPP_FILE cannot be a string as we need a mutable variable # for the nested function to modify OUTPUT_HPP_FILE = [ ] # The function below has been nested so that it can use closure to capture # the above variables that aren't global but instead local to this # function. This avoids use of global state which makes this function safer. def def_module_params(module_name, export, params, class_name=None, description=None): dirname = CURRENT_PYG_HPP_DEST_DIR assert(os.path.exists(dirname)) if class_name is None: class_name = '%s_params' % module_name hpp = os.path.join(dirname, '%s.hpp' % class_name) out = open(hpp, 'w') out.write('// Automatically generated file\n') out.write('#ifndef __%s_HPP_\n' % class_name.upper()) out.write('#define __%s_HPP_\n' % class_name.upper()) out.write('#include "util/params.h"\n') if export: out.write('#include "util/gparams.h"\n') out.write('struct %s {\n' % class_name) out.write(' params_ref const & p;\n') if export: out.write(' params_ref g;\n') out.write(' %s(params_ref const & _p = params_ref::get_empty()):\n' % class_name) out.write(' p(_p)') if export: out.write(', g(gparams::get_module("%s"))' % module_name) out.write(' {}\n') out.write(' static void collect_param_descrs(param_descrs & d) {\n') for param in params: out.write(' d.insert("%s", %s, "%s", "%s","%s");\n' % (param[0], TYPE2CPK[param[1]], param[3], pyg_default(param), module_name)) out.write(' }\n') if export: out.write(' /*\n') out.write(" REG_MODULE_PARAMS('%s', '%s::collect_param_descrs')\n" % (module_name, class_name)) if description is not None: out.write(" REG_MODULE_DESCRIPTION('%s', '%s')\n" % (module_name, description)) out.write(' */\n') # Generated accessors for param in params: if export: out.write(' %s %s() const { return p.%s("%s", g, %s); }\n' % (TYPE2CTYPE[param[1]], to_c_method(param[0]), TYPE2GETTER[param[1]], param[0], pyg_default_as_c_literal(param))) else: out.write(' %s %s() const { return p.%s("%s", %s); }\n' % (TYPE2CTYPE[param[1]], to_c_method(param[0]), TYPE2GETTER[param[1]], param[0], pyg_default_as_c_literal(param))) out.write('};\n') out.write('#endif\n') out.close() OUTPUT_HPP_FILE.append(hpp) # Globals to use when executing the ``.pyg`` file. pyg_globals = { 'UINT' : UINT, 'BOOL' : BOOL, 'DOUBLE' : DOUBLE, 'STRING' : STRING, 'SYMBOL' : SYMBOL, 'UINT_MAX' : UINT_MAX, 'max_memory_param' : max_memory_param, 'max_steps_param' : max_steps_param, # Note that once this function is enterred that function # executes with respect to the globals of this module and # not the globals defined here 'def_module_params' : def_module_params, } with open(pyg_file, 'r') as fh: eval(fh.read() + "\n", pyg_globals, None) assert len(OUTPUT_HPP_FILE) == 1 return OUTPUT_HPP_FILE[0] z3-z3-4.8.7/scripts/mk_gparams_register_modules_cpp.py000077500000000000000000000023211356505360400231100ustar00rootroot00000000000000#!/usr/bin/env python """ Determines the available global parameters from a list of header files and generates a ``gparams_register_modules.cpp`` file in the destination directory that defines a function ``void gparams_register_modules()``. """ import mk_genfile_common import argparse import logging import os import sys def main(args): logging.basicConfig(level=logging.INFO) parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("destination_dir", help="destination directory") parser.add_argument("header_files", nargs="+", help="One or more header files to parse") pargs = parser.parse_args(args) if not mk_genfile_common.check_dir_exists(pargs.destination_dir): return 1 if not mk_genfile_common.check_files_exist(pargs.header_files): return 1 h_files_full_path = [] for header_file in pargs.header_files: h_files_full_path.append(os.path.abspath(header_file)) output = mk_genfile_common.mk_gparams_register_modules_internal( h_files_full_path, pargs.destination_dir ) logging.info('Generated "{}"'.format(output)) return 0 if __name__ == '__main__': sys.exit(main(sys.argv[1:])) z3-z3-4.8.7/scripts/mk_install_tactic_cpp.py000077500000000000000000000024571356505360400210310ustar00rootroot00000000000000#!/usr/bin/env python """ Determines the available tactics from a list of header files and generates a ``install_tactic.cpp`` file in the destination directory that defines a function ``void install_tactics(tactic_manager& ctx)``. """ import mk_genfile_common import argparse import logging import os import sys def main(args): logging.basicConfig(level=logging.INFO) parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("destination_dir", help="destination directory") parser.add_argument("deps", help="file with header file names to parse") pargs = parser.parse_args(args) if not mk_genfile_common.check_dir_exists(pargs.destination_dir): return 1 if not mk_genfile_common.check_files_exist([pargs.deps]): return 1 with open(pargs.deps, 'r') as f: lines = f.read().split('\n') h_files_full_path = [os.path.abspath(header_file) for header_file in lines if header_file] if not mk_genfile_common.check_files_exist(h_files_full_path): return 1 output = mk_genfile_common.mk_install_tactic_cpp_internal( h_files_full_path, pargs.destination_dir ) logging.info('Generated "{}"'.format(output)) return 0 if __name__ == '__main__': sys.exit(main(sys.argv[1:])) z3-z3-4.8.7/scripts/mk_make.py000066400000000000000000000007471356505360400161040ustar00rootroot00000000000000############################################ # Copyright (c) 2012 Microsoft Corporation # # Scripts for generating Makefiles and Visual # Studio project files. # # Author: Leonardo de Moura (leonardo) ############################################ from mk_util import * from mk_project import * parse_options() check_eol() API_files = init_project_def() update_version() mk_auto_src() mk_bindings(API_files) mk_vs_proj('z3', ['shell']) mk_vs_proj_dll('libz3', ['api_dll']) mk_makefile() z3-z3-4.8.7/scripts/mk_mem_initializer_cpp.py000077500000000000000000000021651356505360400212110ustar00rootroot00000000000000#!/usr/bin/env python """ Scans the listed header files for memory initializers and finalizers and emits and implementation of ``void mem_initialize()`` and ``void mem_finalize()`` into ``mem_initializer.cpp`` in the destination directory. """ import mk_genfile_common import argparse import logging import os import sys def main(args): logging.basicConfig(level=logging.INFO) parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("destination_dir", help="destination directory") parser.add_argument("header_files", nargs="+", help="One or more header files to parse") pargs = parser.parse_args(args) if not mk_genfile_common.check_dir_exists(pargs.destination_dir): return 1 h_files_full_path = [] for header_file in pargs.header_files: h_files_full_path.append(os.path.abspath(header_file)) output = mk_genfile_common.mk_mem_initializer_cpp_internal( h_files_full_path, pargs.destination_dir ) logging.info('Generated "{}"'.format(output)) return 0 if __name__ == '__main__': sys.exit(main(sys.argv[1:])) z3-z3-4.8.7/scripts/mk_nuget_release.py000066400000000000000000000137241356505360400200100ustar00rootroot00000000000000# # Copyright (c) 2018 Microsoft Corporation # # 1. download releases from github # 2. copy over libz3.dll for the different architectures # 3. copy over Microsoft.Z3.dll from suitable distribution # 4. copy nuspec file from packages # 5. call nuget pack # 6. sign package import json import os import urllib.request import zipfile import sys import os.path import shutil import subprocess import mk_util import mk_project release_data = json.loads(urllib.request.urlopen("https://api.github.com/repos/Z3Prover/z3/releases/latest").read().decode()) release_tag_name = release_data['tag_name'] release_tag_ref_data = json.loads(urllib.request.urlopen("https://api.github.com/repos/Z3Prover/z3/git/refs/tags/%s" % release_tag_name).read().decode()) release_tag_sha = release_tag_ref_data['object']['sha'] #release_tag_data = json.loads(urllib.request.urlopen("https://api.github.com/repos/Z3Prover/z3/commits/%s" % release_tag_sha).read().decode()) release_version = release_tag_name[3:] release_commit = release_tag_sha # release_tag_data['object']['sha'] print(release_version) def mk_dir(d): if not os.path.exists(d): os.makedirs(d) def download_installs(): for asset in release_data['assets']: url = asset['browser_download_url'] name = asset['name'] print("Downloading ", url) sys.stdout.flush() urllib.request.urlretrieve(url, "packages/%s" % name) os_info = {"z64-ubuntu-14" : ('so', 'ubuntu.14.04-x64'), 'ubuntu-16' : ('so', 'ubuntu-x64'), 'x64-win' : ('dll', 'win-x64'), # Skip x86 as I can't get dotnet build to produce AnyCPU TargetPlatform # 'x86-win' : ('dll', 'win-x86'), 'osx' : ('dylib', 'macos'), 'debian' : ('so', 'debian.8-x64') } def classify_package(f): for os_name in os_info: if os_name in f: ext, dst = os_info[os_name] return os_name, f[:-4], ext, dst return None def unpack(): shutil.rmtree("out", ignore_errors=True) # unzip files in packages # out # +- runtimes # +- win-x64 # +- win-x86 # +- ubuntu.16.04-x64 # +- ubuntu.14.04-x64 # +- debian.8-x64 # +- macos # + for f in os.listdir("packages"): print(f) if f.endswith(".zip") and classify_package(f): os_name, package_dir, ext, dst = classify_package(f) 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/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") shutil.move("tmp/%s/bin/%s" % (package_dir, b), "out/lib/netstandard1.4/%s" % b) def mk_targets(): mk_dir("out/build") shutil.copy("../src/api/dotnet/Microsoft.Z3.targets.in", "out/build/Microsoft.Z3.targets") def create_nuget_spec(): contents = """ Microsoft.Z3.x64 {0} Microsoft Z3 is a satisfiability modulo theories solver from Microsoft Research. Linux Dependencies: libgomp.so.1 installed © Microsoft Corporation. All rights reserved. smt constraint solver theorem prover https://raw.githubusercontent.com/Z3Prover/z3/{1}/resources/icon.jpg https://github.com/Z3Prover/z3 https://raw.githubusercontent.com/Z3Prover/z3/{1}/LICENSE.txt true en """.format(release_version, release_commit) with open("out/Microsoft.Z3.x64.nuspec", 'w') as f: f.write(contents) def create_nuget_package(): subprocess.call(["nuget", "pack"], cwd="out") nuget_sign_input = """ { "Version": "1.0.0", "SignBatches" : [ { "SourceLocationType": "UNC", "SourceRootDirectory": "%s", "DestinationLocationType": "UNC", "DestinationRootDirectory": "%s", "SignRequestFiles": [ { "CustomerCorrelationId": "42fc9577-af9e-4ac9-995d-1788d8721d17", "SourceLocation": "Microsoft.Z3.x64.%s.nupkg", "DestinationLocation": "Microsoft.Z3.x64.%s.nupkg" } ], "SigningInfo": { "Operations": [ { "KeyCode" : "CP-401405", "OperationCode" : "NuGetSign", "Parameters" : {}, "ToolName" : "sign", "ToolVersion" : "1.0" }, { "KeyCode" : "CP-401405", "OperationCode" : "NuGetVerify", "Parameters" : {}, "ToolName" : "sign", "ToolVersion" : "1.0" } ] } } ] }""" def sign_nuget_package(): package_name = "Microsoft.Z3.x64.%s.nupkg" % release_version input_file = "out/nuget_sign_input.json" output_path = os.path.abspath("out").replace("\\","\\\\") with open(input_file, 'w') as f: f.write(nuget_sign_input % (output_path, output_path, release_version, release_version)) r = subprocess.call(["EsrpClient.exe", "sign", "-a", "authorization.json", "-p", "policy.json", "-i", input_file, "-o", "out\\diagnostics.json"], shell=True, stderr=subprocess.STDOUT) print(r) def main(): mk_dir("packages") download_installs() unpack() mk_targets() create_nuget_spec() create_nuget_package() sign_nuget_package() main() z3-z3-4.8.7/scripts/mk_nuget_task.py000066400000000000000000000115661356505360400173340ustar00rootroot00000000000000# # Copyright (c) 2018 Microsoft Corporation # # 1. copy over dlls # 2. copy over libz3.dll for the different architectures # 3. copy over Microsoft.Z3.dll from suitable distribution # 4. copy nuspec file from packages # 5. call nuget pack # 6. sign package import json import os import zipfile import sys import os.path import shutil import subprocess def mk_dir(d): if not os.path.exists(d): os.makedirs(d) os_info = {"z64-ubuntu-14" : ('so', 'ubuntu.14.04-x64'), 'ubuntu-16' : ('so', 'ubuntu-x64'), 'x64-win' : ('dll', 'win-x64'), # Skip x86 as I can't get dotnet build to produce AnyCPU TargetPlatform # 'x86-win' : ('dll', 'win-x86'), 'osx' : ('dylib', 'macos'), 'debian' : ('so', 'debian.8-x64') } def classify_package(f): for os_name in os_info: if os_name in f: ext, dst = os_info[os_name] return os_name, f[:-4], ext, dst return None def unpack(packages): # unzip files in packages # out # +- runtimes # +- win-x64 # +- win-x86 # +- ubuntu.16.04-x64 # +- ubuntu.14.04-x64 # +- debian.8-x64 # +- macos # + for f in os.listdir(packages): print(f) if f.endswith(".zip") and classify_package(f): os_name, package_dir, ext, dst = classify_package(f) 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/native" % dst) shutil.move("tmp/%s/bin/libz3.%s" % (package_dir, ext), "out/runtimes/%s/native/." % dst) 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") shutil.move("tmp/%s/bin/%s" % (package_dir, b), "out/lib/netstandard1.4/%s" % b) def mk_targets(): mk_dir("out/build") shutil.copy("../src/api/dotnet/Microsoft.Z3.targets.in", "out/build/Microsoft.Z3.targets") def create_nuget_spec(release_version, release_commit): contents = """ Microsoft.Z3.x64 {0} Microsoft Z3 is a satisfiability modulo theories solver from Microsoft Research. Linux Dependencies: libgomp.so.1 installed © Microsoft Corporation. All rights reserved. smt constraint solver theorem prover https://raw.githubusercontent.com/Z3Prover/z3/{1}/resources/icon.jpg https://github.com/Z3Prover/z3 https://raw.githubusercontent.com/Z3Prover/z3/{1}/LICENSE.txt true en """.format(release_version, release_commit) print(contents) with open("out/Microsoft.Z3.x64.nuspec", 'w') as f: f.write(contents) nuget_sign_input = """ { "Version": "1.0.0", "SignBatches" : [ { "SourceLocationType": "UNC", "SourceRootDirectory": "%s", "DestinationLocationType": "UNC", "DestinationRootDirectory": "%s", "SignRequestFiles": [ { "CustomerCorrelationId": "42fc9577-af9e-4ac9-995d-1788d8721d17", "SourceLocation": "Microsoft.Z3.x64.%s.nupkg", "DestinationLocation": "Microsoft.Z3.x64.%s.nupkg" } ], "SigningInfo": { "Operations": [ { "KeyCode" : "CP-401405", "OperationCode" : "NuGetSign", "Parameters" : {}, "ToolName" : "sign", "ToolVersion" : "1.0" }, { "KeyCode" : "CP-401405", "OperationCode" : "NuGetVerify", "Parameters" : {}, "ToolName" : "sign", "ToolVersion" : "1.0" } ] } } ] }""" def create_sign_input(release_version): package_name = "Microsoft.Z3.x64.%s.nupkg" % release_version input_file = "out/nuget_sign_input.json" output_path = os.path.abspath("out").replace("\\","\\\\") with open(input_file, 'w') as f: f.write(nuget_sign_input % (output_path, output_path, release_version, release_version)) def main(): packages = sys.argv[1] release_version = sys.argv[2] release_commit = sys.argv[3] print(packages) mk_dir(packages) unpack(packages) mk_targets() create_nuget_spec(release_version, release_commit) create_sign_input(release_version) # create_nuget_package() # sign_nuget_package(release_version) main() z3-z3-4.8.7/scripts/mk_pat_db.py000077500000000000000000000014471356505360400164210ustar00rootroot00000000000000#!/usr/bin/env python """ Reads a pattern database and generates the corresponding header file. """ import mk_genfile_common import argparse import logging import os import sys def main(args): logging.basicConfig(level=logging.INFO) parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("db_file", help="pattern database file") parser.add_argument("output_file", help="output header file path") pargs = parser.parse_args(args) if not os.path.exists(pargs.db_file): logging.error('"{}" does not exist'.format(pargs.db_file)) return 1 mk_genfile_common.mk_pat_db_internal(pargs.db_file, pargs.output_file) logging.info('Generated "{}"'.format(pargs.output_file)) return 0 if __name__ == '__main__': sys.exit(main(sys.argv[1:])) z3-z3-4.8.7/scripts/mk_project.py000066400000000000000000000147001356505360400166270ustar00rootroot00000000000000############################################ # Copyright (c) 2012 Microsoft Corporation # # Z3 project configuration files # # Author: Leonardo de Moura (leonardo) ############################################ from mk_util import * def init_version(): set_version(4, 8, 7, 0) # Z3 Project definition def init_project_def(): init_version() add_lib('util', [], includes2install = ['z3_version.h']) add_lib('polynomial', ['util'], 'math/polynomial') add_lib('sat', ['util']) add_lib('nlsat', ['polynomial', 'sat']) add_lib('lp', ['util','nlsat'], 'util/lp') add_lib('hilbert', ['util'], 'math/hilbert') add_lib('simplex', ['util'], 'math/simplex') add_lib('automata', ['util'], 'math/automata') add_lib('interval', ['util'], 'math/interval') add_lib('realclosure', ['interval'], 'math/realclosure') add_lib('subpaving', ['interval'], 'math/subpaving') add_lib('ast', ['util', 'polynomial']) add_lib('rewriter', ['ast', 'polynomial', 'automata'], 'ast/rewriter') add_lib('macros', ['rewriter'], 'ast/macros') add_lib('normal_forms', ['rewriter'], 'ast/normal_forms') add_lib('model', ['rewriter']) add_lib('tactic', ['ast', 'model']) add_lib('substitution', ['ast', 'rewriter'], 'ast/substitution') add_lib('parser_util', ['ast'], 'parsers/util') add_lib('grobner', ['ast'], 'math/grobner') add_lib('euclid', ['util'], 'math/euclid') add_lib('proofs', ['rewriter', 'util'], 'ast/proofs') add_lib('solver', ['model', 'tactic', 'proofs']) add_lib('cmd_context', ['solver', 'rewriter']) add_lib('sat_tactic', ['tactic', 'sat', 'solver'], 'sat/tactic') add_lib('smt2parser', ['cmd_context', 'parser_util'], 'parsers/smt2') add_lib('pattern', ['normal_forms', 'smt2parser', 'rewriter'], 'ast/pattern') add_lib('core_tactics', ['tactic', 'macros', 'normal_forms', 'rewriter', 'pattern'], 'tactic/core') add_lib('arith_tactics', ['core_tactics', 'sat'], 'tactic/arith') add_lib('nlsat_tactic', ['nlsat', 'sat_tactic', 'arith_tactics'], 'nlsat/tactic') add_lib('subpaving_tactic', ['core_tactics', 'subpaving'], 'math/subpaving/tactic') add_lib('aig_tactic', ['tactic'], 'tactic/aig') add_lib('ackermannization', ['model', 'rewriter', 'ast', 'solver', 'tactic'], 'ackermannization') add_lib('fpa', ['ast', 'util', 'rewriter', 'model'], 'ast/fpa') add_lib('bit_blaster', ['rewriter', 'rewriter'], 'ast/rewriter/bit_blaster') add_lib('smt_params', ['ast', 'rewriter', 'pattern', 'bit_blaster'], 'smt/params') add_lib('proto_model', ['model', 'rewriter', 'smt_params'], 'smt/proto_model') add_lib('smt', ['bit_blaster', 'macros', 'normal_forms', 'cmd_context', 'proto_model', 'substitution', 'grobner', 'euclid', 'simplex', 'proofs', 'pattern', 'parser_util', 'fpa', 'lp']) add_lib('bv_tactics', ['tactic', 'bit_blaster', 'core_tactics'], 'tactic/bv') add_lib('fuzzing', ['ast'], 'test/fuzzing') add_lib('smt_tactic', ['smt'], 'smt/tactic') add_lib('sls_tactic', ['tactic', 'normal_forms', 'core_tactics', 'bv_tactics'], 'tactic/sls') add_lib('qe', ['smt','sat','nlsat','tactic','nlsat_tactic'], 'qe') add_lib('sat_solver', ['solver', 'core_tactics', 'aig_tactic', 'bv_tactics', 'arith_tactics', 'sat_tactic'], 'sat/sat_solver') add_lib('fd_solver', ['core_tactics', 'arith_tactics', 'sat_solver', 'smt'], 'tactic/fd_solver') add_lib('muz', ['smt', 'sat', 'smt2parser', 'aig_tactic', 'qe'], 'muz/base') add_lib('dataflow', ['muz'], 'muz/dataflow') add_lib('transforms', ['muz', 'hilbert', 'dataflow'], 'muz/transforms') add_lib('rel', ['muz', 'transforms'], 'muz/rel') add_lib('spacer', ['muz', 'transforms', 'arith_tactics', 'smt_tactic'], 'muz/spacer') add_lib('clp', ['muz', 'transforms'], 'muz/clp') add_lib('tab', ['muz', 'transforms'], 'muz/tab') add_lib('ddnf', ['muz', 'transforms', 'rel'], 'muz/ddnf') add_lib('bmc', ['muz', 'transforms', 'fd_solver'], 'muz/bmc') add_lib('fp', ['muz', 'clp', 'tab', 'rel', 'bmc', 'ddnf', 'spacer'], 'muz/fp') add_lib('ufbv_tactic', ['normal_forms', 'core_tactics', 'macros', 'smt_tactic', 'rewriter'], 'tactic/ufbv') add_lib('smtlogic_tactics', ['ackermannization', 'sat_solver', 'arith_tactics', 'bv_tactics', 'nlsat_tactic', 'smt_tactic', 'aig_tactic', 'fp', 'muz','qe'], 'tactic/smtlogics') add_lib('fpa_tactics', ['fpa', 'core_tactics', 'bv_tactics', 'sat_tactic', 'smt_tactic', 'arith_tactics', 'smtlogic_tactics'], 'tactic/fpa') add_lib('portfolio', ['smtlogic_tactics', 'sat_solver', 'ufbv_tactic', 'fpa_tactics', 'aig_tactic', 'fp', 'fd_solver', 'qe','sls_tactic', 'subpaving_tactic'], 'tactic/portfolio') add_lib('opt', ['smt', 'smtlogic_tactics', 'sls_tactic', 'sat_solver'], 'opt') API_files = ['z3_api.h', 'z3_ast_containers.h', 'z3_algebraic.h', 'z3_polynomial.h', 'z3_rcf.h', 'z3_fixedpoint.h', 'z3_optimization.h', 'z3_fpa.h', 'z3_spacer.h'] add_lib('api', ['portfolio', 'realclosure', 'opt'], includes2install=['z3.h', 'z3_v1.h', 'z3_macros.h'] + API_files) add_lib('extra_cmds', ['cmd_context', 'subpaving_tactic', 'qe', 'arith_tactics'], 'cmd_context/extra_cmds') add_exe('shell', ['api', 'sat', 'extra_cmds','opt'], exe_name='z3') add_exe('test', ['api', 'fuzzing', 'simplex'], exe_name='test-z3', install=False) _libz3Component = add_dll('api_dll', ['api', 'sat', 'extra_cmds'], 'api/dll', reexports=['api'], dll_name='libz3', static=build_static_lib(), export_files=API_files, staging_link='python') add_dot_net_core_dll('dotnet', ['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']) set_z3py_dir('api/python') add_python(_libz3Component) add_python_install(_libz3Component) add_js() # Examples add_cpp_example('cpp_example', 'c++') add_cpp_example('z3_tptp', 'tptp') add_c_example('c_example', 'c') add_c_example('maxsat') add_dotnet_example('dotnet_example', 'dotnet') add_java_example('java_example', 'java') add_ml_example('ml_example', 'ml') add_z3py_example('py_example', 'python') return API_files z3-z3-4.8.7/scripts/mk_unix_dist.py000066400000000000000000000170161356505360400171720ustar00rootroot00000000000000############################################ # Copyright (c) 2013 Microsoft Corporation # # Scripts for automatically generating # Linux/OSX/BSD distribution zip files. # # Author: Leonardo de Moura (leonardo) ############################################ import os import glob import re import getopt import sys import shutil import subprocess import zipfile from mk_exception import * from mk_project import * import mk_util BUILD_DIR='build-dist' VERBOSE=True DIST_DIR='dist' FORCE_MK=False DOTNET_CORE_ENABLED=True DOTNET_KEY_FILE=None JAVA_ENABLED=True GIT_HASH=False PYTHON_ENABLED=True MAKEJOBS=getenv("MAKEJOBS", '8') def set_verbose(flag): global VERBOSE VERBOSE = flag def is_verbose(): return VERBOSE def mk_dir(d): if not os.path.exists(d): os.makedirs(d) def set_build_dir(path): global BUILD_DIR BUILD_DIR = mk_util.norm_path(path) mk_dir(BUILD_DIR) def display_help(): print("mk_unix_dist.py: Z3 Linux/OSX/BSD distribution generator\n") print("This script generates the zip files containing executables, shared objects, header files for Linux/OSX/BSD.") print("It must be executed from the Z3 root directory.") print("\nOptions:") print(" -h, --help display this message.") print(" -s, --silent do not print verbose messages.") print(" -b , --build= subdirectory where x86 and x64 Z3 versions will be built (default: build-dist).") print(" -f, --force force script to regenerate Makefiles.") print(" --nodotnet do not include .NET bindings in the binary distribution files.") print(" --dotnet-key= sign the .NET assembly with the private key in .") 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.") exit(0) # Parse configuration option for mk_make script def parse_options(): global FORCE_MK, JAVA_ENABLED, GIT_HASH, DOTNET_CORE_ENABLED, DOTNET_KEY_FILE path = BUILD_DIR options, remainder = getopt.gnu_getopt(sys.argv[1:], 'b:hsf', ['build=', 'help', 'silent', 'force', 'nojava', 'nodotnet', 'dotnet-key=', 'githash', 'nopython' ]) for opt, arg in options: if opt in ('-b', '--build'): if arg == 'src': raise MKException('The src directory should not be used to host the Makefile') path = arg elif opt in ('-s', '--silent'): set_verbose(False) elif opt in ('-h', '--help'): display_help() elif opt in ('-f', '--force'): FORCE_MK = True elif opt == '--nodotnet': DOTNET_CORE_ENABLED = False elif opt == '--nopython': PYTHON_ENABLED = False elif opt == '--dotnet-key': DOTNET_KEY_FILE = arg elif opt == '--nojava': JAVA_ENABLED = False elif opt == '--githash': GIT_HASH = True else: raise MKException("Invalid command line option '%s'" % opt) set_build_dir(path) # Check whether build directory already exists or not def check_build_dir(path): return os.path.exists(path) and os.path.exists(os.path.join(path, 'Makefile')) # Create a build directory using mk_make.py def mk_build_dir(path): if not check_build_dir(path) or FORCE_MK: opts = [sys.executable, os.path.join('scripts', 'mk_make.py'), "-b", path, "--staticlib"] if DOTNET_CORE_ENABLED: opts.append('--dotnet') if not DOTNET_KEY_FILE is None: opts.append('--dotnet-key=' + DOTNET_KEY_FILE) if JAVA_ENABLED: opts.append('--java') if GIT_HASH: opts.append('--githash=%s' % mk_util.git_hash()) opts.append('--git-describe') if PYTHON_ENABLED: opts.append('--python') if subprocess.call(opts) != 0: raise MKException("Failed to generate build directory at '%s'" % path) # Create build directories def mk_build_dirs(): mk_build_dir(BUILD_DIR) class cd: def __init__(self, newPath): self.newPath = newPath def __enter__(self): self.savedPath = os.getcwd() os.chdir(self.newPath) def __exit__(self, etype, value, traceback): os.chdir(self.savedPath) def mk_z3(): with cd(BUILD_DIR): try: return subprocess.call(['make', '-j', MAKEJOBS]) except: return 1 def get_os_name(): import platform basic = os.uname()[0].lower() if basic == 'linux': dist = platform.linux_distribution() if len(dist) == 3 and len(dist[0]) > 0 and len(dist[1]) > 0: return '%s-%s' % (dist[0].lower(), dist[1].lower()) else: return basic elif basic == 'darwin': ver = platform.mac_ver() if len(ver) == 3 and len(ver[0]) > 0: return 'osx-%s' % ver[0] else: return 'osx' elif basic == 'freebsd': ver = platform.version() idx1 = ver.find(' ') idx2 = ver.find('-') if idx1 < 0 or idx2 < 0 or idx1 >= idx2: return basic else: return 'freebsd-%s' % ver[(idx1+1):idx2] else: return basic def get_z3_name(): major, minor, build, revision = get_version() if sys.maxsize >= 2**32: platform = "x64" else: platform = "x86" osname = get_os_name() if GIT_HASH: return 'z3-%s.%s.%s.%s-%s-%s' % (major, minor, build, mk_util.git_hash(), platform, osname) else: return 'z3-%s.%s.%s-%s-%s' % (major, minor, build, platform, osname) def mk_dist_dir(): build_path = BUILD_DIR dist_path = os.path.join(DIST_DIR, get_z3_name()) mk_dir(dist_path) mk_util.DOTNET_CORE_ENABLED = DOTNET_CORE_ENABLED mk_util.DOTNET_ENABLED = False mk_util.DOTNET_KEY_FILE = DOTNET_KEY_FILE mk_util.JAVA_ENABLED = JAVA_ENABLED mk_util.PYTHON_ENABLED = PYTHON_ENABLED mk_unix_dist(build_path, dist_path) if is_verbose(): print("Generated distribution folder at '%s'" % dist_path) def get_dist_path(): return get_z3_name() def mk_zip(): dist_path = get_dist_path() old = os.getcwd() try: os.chdir(DIST_DIR) zfname = '%s.zip' % dist_path zipout = zipfile.ZipFile(zfname, 'w', zipfile.ZIP_DEFLATED) for root, dirs, files in os.walk(dist_path): for f in files: zipout.write(os.path.join(root, f)) if is_verbose(): print("Generated '%s'" % zfname) except: pass os.chdir(old) def cp_license(): shutil.copy("LICENSE.txt", os.path.join(DIST_DIR, get_dist_path())) # Entry point def main(): parse_options() mk_build_dirs() mk_z3() init_project_def() mk_dist_dir() cp_license() mk_zip() main() z3-z3-4.8.7/scripts/mk_util.py000066400000000000000000004215051356505360400161430ustar00rootroot00000000000000############################################ # Copyright (c) 2012 Microsoft Corporation # # Auxiliary scripts for generating Makefiles # and Visual Studio project files. # # Author: Leonardo de Moura (leonardo) ############################################ import io import sys import os import re import getopt import shutil from mk_exception import * import mk_genfile_common from fnmatch import fnmatch import distutils.sysconfig import compileall import subprocess def getenv(name, default): try: return os.environ[name].strip(' "\'') except: return default CXX=getenv("CXX", None) CC=getenv("CC", None) CPPFLAGS=getenv("CPPFLAGS", "") CXXFLAGS=getenv("CXXFLAGS", "") AR=getenv("AR", "ar") EXAMP_DEBUG_FLAG='' LDFLAGS=getenv("LDFLAGS", "") JNI_HOME=getenv("JNI_HOME", None) OCAMLC=getenv("OCAMLC", "ocamlc") OCAMLOPT=getenv("OCAMLOPT", "ocamlopt") OCAML_LIB=getenv("OCAML_LIB", None) OCAMLFIND=getenv("OCAMLFIND", "ocamlfind") CSC=getenv("CSC", None) DOTNET="dotnet" GACUTIL=getenv("GACUTIL", 'gacutil') # Standard install directories relative to PREFIX INSTALL_BIN_DIR=getenv("Z3_INSTALL_BIN_DIR", "bin") INSTALL_LIB_DIR=getenv("Z3_INSTALL_LIB_DIR", "lib") INSTALL_INCLUDE_DIR=getenv("Z3_INSTALL_INCLUDE_DIR", "include") INSTALL_PKGCONFIG_DIR=getenv("Z3_INSTALL_PKGCONFIG_DIR", os.path.join(INSTALL_LIB_DIR, 'pkgconfig')) CXX_COMPILERS=['g++', 'clang++'] C_COMPILERS=['gcc', 'clang'] CSC_COMPILERS=['csc', 'mcs'] JAVAC=None JAR=None PYTHON_PACKAGE_DIR=distutils.sysconfig.get_python_lib(prefix=getenv("PREFIX", None)) BUILD_DIR='build' REV_BUILD_DIR='..' SRC_DIR='src' EXAMPLE_DIR='examples' # Required Components Z3_DLL_COMPONENT='api_dll' PATTERN_COMPONENT='pattern' UTIL_COMPONENT='util' API_COMPONENT='api' DOTNET_COMPONENT='dotnet' DOTNET_CORE_COMPONENT='dotnet' JAVA_COMPONENT='java' ML_COMPONENT='ml' CPP_COMPONENT='cpp' PYTHON_COMPONENT='python' ##################### IS_WINDOWS=False IS_LINUX=False IS_HURD=False IS_OSX=False IS_FREEBSD=False IS_NETBSD=False IS_OPENBSD=False IS_CYGWIN=False IS_CYGWIN_MINGW=False IS_MSYS2=False VERBOSE=True DEBUG_MODE=False SHOW_CPPS = True VS_X64 = False VS_ARM = False LINUX_X64 = True ONLY_MAKEFILES = False Z3PY_SRC_DIR=None Z3JS_SRC_DIR=None VS_PROJ = False TRACE = False PYTHON_ENABLED=False DOTNET_CORE_ENABLED=False ESRP_SIGN=False DOTNET_KEY_FILE=getenv("Z3_DOTNET_KEY_FILE", None) JAVA_ENABLED=False ML_ENABLED=False JS_ENABLED=False PYTHON_INSTALL_ENABLED=False STATIC_LIB=False STATIC_BIN=False VER_MAJOR=None VER_MINOR=None VER_BUILD=None VER_TWEAK=None PREFIX=sys.prefix GMP=False VS_PAR=False VS_PAR_NUM=8 GPROF=False GIT_HASH=False GIT_DESCRIBE=False SLOW_OPTIMIZE=False LOG_SYNC=False SINGLE_THREADED=False GUARD_CF=False ALWAYS_DYNAMIC_BASE=False FPMATH="Default" FPMATH_FLAGS="-mfpmath=sse -msse -msse2" def check_output(cmd): out = subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate()[0] if out != None: enc = sys.stdout.encoding if enc != None: return out.decode(enc).rstrip('\r\n') else: return out.rstrip('\r\n') else: return "" def git_hash(): try: branch = check_output(['git', 'rev-parse', '--abbrev-ref', 'HEAD']) r = check_output(['git', 'show-ref', '--abbrev=12', 'refs/heads/%s' % branch]) except: raise MKException("Failed to retrieve git hash") ls = r.split(' ') if len(ls) != 2: raise MKException("Unexpected git output " + r) return ls[0] def is_windows(): return IS_WINDOWS def is_linux(): return IS_LINUX def is_hurd(): return IS_HURD def is_freebsd(): return IS_FREEBSD def is_netbsd(): return IS_NETBSD def is_openbsd(): return IS_OPENBSD def is_osx(): return IS_OSX def is_cygwin(): return IS_CYGWIN def is_cygwin_mingw(): return IS_CYGWIN_MINGW def is_msys2(): return IS_MSYS2 def norm_path(p): return os.path.expanduser(os.path.normpath(p)) def which(program): import os def is_exe(fpath): return os.path.isfile(fpath) and os.access(fpath, os.X_OK) fpath, fname = os.path.split(program) if fpath: if is_exe(program): return program else: for path in getenv("PATH", "").split(os.pathsep): exe_file = os.path.join(path, program) if is_exe(exe_file): return exe_file return None class TempFile: def __init__(self, name): try: self.name = name self.fname = open(name, 'w') except: raise MKException("Failed to create temporary file '%s'" % self.name) def add(self, s): self.fname.write(s) def commit(self): self.fname.close() def __del__(self): self.fname.close() try: os.remove(self.name) except: pass def exec_cmd(cmd): if isinstance(cmd, str): cmd = cmd.split(' ') new_cmd = [] first = True for e in cmd: if first: first = False new_cmd.append(e) else: if e != "": se = e.split(' ') if len(se) > 1: for e2 in se: if e2 != "": new_cmd.append(e2) else: new_cmd.append(e) cmd = new_cmd null = open(os.devnull, 'wb') try: return subprocess.call(cmd, stdout=null, stderr=null) except: # Failed to create process return 1 finally: null.close() # rm -f fname def rmf(fname): if os.path.exists(fname): os.remove(fname) def exec_compiler_cmd(cmd): r = exec_cmd(cmd) if is_windows() or is_cygwin_mingw() or is_cygwin() or is_msys2(): rmf('a.exe') else: rmf('a.out') return r def test_cxx_compiler(cc): if is_verbose(): print("Testing %s..." % cc) t = TempFile('tst.cpp') t.add('#include\nint main() { return 0; }\n') t.commit() return exec_compiler_cmd([cc, CPPFLAGS, CXXFLAGS, 'tst.cpp', LDFLAGS]) == 0 def test_c_compiler(cc): if is_verbose(): print("Testing %s..." % cc) t = TempFile('tst.c') t.add('#include\nint main() { return 0; }\n') t.commit() return exec_compiler_cmd([cc, CPPFLAGS, 'tst.c', LDFLAGS]) == 0 def test_gmp(cc): if is_verbose(): print("Testing GMP...") t = TempFile('tstgmp.cpp') t.add('#include\nint main() { mpz_t t; mpz_init(t); mpz_clear(t); return 0; }\n') t.commit() return exec_compiler_cmd([cc, CPPFLAGS, 'tstgmp.cpp', LDFLAGS, '-lgmp']) == 0 def test_fpmath(cc): global FPMATH_FLAGS if is_verbose(): print("Testing floating point support...") t = TempFile('tstsse.cpp') t.add('int main() { return 42; }\n') t.commit() # -Werror is needed because some versions of clang warn about unrecognized # -m flags. if exec_compiler_cmd([cc, CPPFLAGS, '-Werror', 'tstsse.cpp', LDFLAGS, '-mfpmath=sse -msse -msse2']) == 0: FPMATH_FLAGS="-mfpmath=sse -msse -msse2" return "SSE2-GCC" elif exec_compiler_cmd([cc, CPPFLAGS, '-Werror', 'tstsse.cpp', LDFLAGS, '-msse -msse2']) == 0: FPMATH_FLAGS="-msse -msse2" return "SSE2-CLANG" elif exec_compiler_cmd([cc, CPPFLAGS, '-Werror', 'tstsse.cpp', LDFLAGS, '-mfpu=vfp -mfloat-abi=hard']) == 0: FPMATH_FLAGS="-mfpu=vfp -mfloat-abi=hard" return "ARM-VFP" else: FPMATH_FLAGS="" return "UNKNOWN" def find_jni_h(path): for root, dirs, files in os.walk(path): for f in files: if f == 'jni.h': return root return False def check_java(): global JNI_HOME global JAVAC global JAR JDK_HOME = getenv('JDK_HOME', None) # we only need to check this locally. if is_verbose(): print("Finding javac ...") if JDK_HOME is not None: if IS_WINDOWS: JAVAC = os.path.join(JDK_HOME, 'bin', 'javac.exe') else: JAVAC = os.path.join(JDK_HOME, 'bin', 'javac') if not os.path.exists(JAVAC): raise MKException("Failed to detect javac at '%s/bin'; the environment variable JDK_HOME is probably set to the wrong path." % os.path.join(JDK_HOME)) else: # Search for javac in the path. ind = 'javac' if IS_WINDOWS: ind = ind + '.exe' paths = os.getenv('PATH', None) if paths: spaths = paths.split(os.pathsep) for i in range(0, len(spaths)): cmb = os.path.join(spaths[i], ind) if os.path.exists(cmb): JAVAC = cmb break if JAVAC is None: raise MKException('No java compiler in the path, please adjust your PATH or set JDK_HOME to the location of the JDK.') if is_verbose(): print("Finding jar ...") if IS_WINDOWS: JAR = os.path.join(os.path.dirname(JAVAC), 'jar.exe') else: JAR = os.path.join(os.path.dirname(JAVAC), 'jar') if not os.path.exists(JAR): raise MKException("Failed to detect jar at '%s'; the environment variable JDK_HOME is probably set to the wrong path." % os.path.join(JDK_HOME)) if is_verbose(): print("Testing %s..." % JAVAC) t = TempFile('Hello.java') t.add('public class Hello { public static void main(String[] args) { System.out.println("Hello, World"); }}\n') t.commit() oo = TempFile('output') eo = TempFile('errout') try: subprocess.call([JAVAC, 'Hello.java', '-verbose'], stdout=oo.fname, stderr=eo.fname) oo.commit() eo.commit() except: raise MKException('Found, but failed to run Java compiler at %s' % (JAVAC)) os.remove('Hello.class') if is_verbose(): print("Finding jni.h...") if JNI_HOME is not None: if not os.path.exists(os.path.join(JNI_HOME, 'jni.h')): raise MKException("Failed to detect jni.h '%s'; the environment variable JNI_HOME is probably set to the wrong path." % os.path.join(JNI_HOME)) else: # Search for jni.h in the library directories... t = open('errout', 'r') open_pat = re.compile("\[search path for class files: (.*)\]") cdirs = [] for line in t: m = open_pat.match(line) if m: libdirs = m.group(1).split(',') for libdir in libdirs: q = os.path.dirname(libdir) if cdirs.count(q) == 0 and len(q) > 0: cdirs.append(q) t.close() # ... plus some heuristic ones. extra_dirs = [] # For the libraries, even the JDK usually uses a JRE that comes with it. To find the # headers we have to go a little bit higher up. for dir in cdirs: extra_dirs.append(os.path.abspath(os.path.join(dir, '..'))) if IS_OSX: # Apparently Apple knows best where to put stuff... extra_dirs.append('/System/Library/Frameworks/JavaVM.framework/Headers/') cdirs[len(cdirs):] = extra_dirs for dir in cdirs: q = find_jni_h(dir) if q is not False: JNI_HOME = q if JNI_HOME is None: raise MKException("Failed to detect jni.h. Possible solution: set JNI_HOME with the path to JDK.") def test_csc_compiler(c): t = TempFile('hello.cs') t.add('public class hello { public static void Main() {} }') t.commit() if is_verbose(): print ('Testing %s...' % c) r = exec_cmd([c, 'hello.cs']) try: rmf('hello.cs') rmf('hello.exe') except: pass return r == 0 def check_dotnet_core(): if not IS_WINDOWS: return r = exec_cmd([DOTNET, '--help']) if r != 0: raise MKException('Failed testing dotnet. Make sure to install and configure dotnet core utilities') def check_ml(): t = TempFile('hello.ml') t.add('print_string "Hello world!\n";;') t.commit() if is_verbose(): print ('Testing %s...' % OCAMLC) r = exec_cmd([OCAMLC, '-o', 'a.out', 'hello.ml']) if r != 0: raise MKException('Failed testing ocamlc compiler. Set environment variable OCAMLC with the path to the Ocaml compiler') if is_verbose(): print ('Testing %s...' % OCAMLOPT) r = exec_cmd([OCAMLOPT, '-o', 'a.out', 'hello.ml']) if r != 0: raise MKException('Failed testing ocamlopt compiler. Set environment variable OCAMLOPT with the path to the Ocaml native compiler. Note that ocamlopt may require flexlink to be in your path.') try: rmf('hello.cmi') rmf('hello.cmo') rmf('hello.cmx') rmf('a.out') rmf('hello.o') except: pass find_ml_lib() find_ocaml_find() def find_ocaml_find(): global OCAMLFIND if is_verbose(): print ("Testing %s..." % OCAMLFIND) r = exec_cmd([OCAMLFIND, 'printconf']) if r != 0: OCAMLFIND = '' def find_ml_lib(): global OCAML_LIB if is_verbose(): print ('Finding OCAML_LIB...') t = TempFile('output') null = open(os.devnull, 'wb') try: subprocess.call([OCAMLC, '-where'], stdout=t.fname, stderr=null) t.commit() except: raise MKException('Failed to find Ocaml library; please set OCAML_LIB') finally: null.close() t = open('output', 'r') for line in t: OCAML_LIB = line[:-1] if is_verbose(): print ('OCAML_LIB=%s' % OCAML_LIB) t.close() rmf('output') return def is64(): global LINUX_X64 return LINUX_X64 and sys.maxsize >= 2**32 def check_ar(): if is_verbose(): print("Testing ar...") if which(AR) is None: raise MKException('%s (archive tool) was not found' % AR) def find_cxx_compiler(): global CXX, CXX_COMPILERS if CXX is not None: if test_cxx_compiler(CXX): return CXX for cxx in CXX_COMPILERS: if test_cxx_compiler(cxx): CXX = cxx return CXX raise MKException('C++ compiler was not found. Try to set the environment variable CXX with the C++ compiler available in your system.') def find_c_compiler(): global CC, C_COMPILERS if CC is not None: if test_c_compiler(CC): return CC for c in C_COMPILERS: if test_c_compiler(c): CC = c return CC raise MKException('C compiler was not found. Try to set the environment variable CC with the C compiler available in your system.') def set_version(major, minor, build, revision): global VER_MAJOR, VER_MINOR, VER_BUILD, VER_TWEAK, GIT_DESCRIBE VER_MAJOR = major VER_MINOR = minor VER_BUILD = build VER_TWEAK = revision if GIT_DESCRIBE: branch = check_output(['git', 'rev-parse', '--abbrev-ref', 'HEAD']) VER_TWEAK = int(check_output(['git', 'rev-list', '--count', 'HEAD'])) def get_version(): return (VER_MAJOR, VER_MINOR, VER_BUILD, VER_TWEAK) def get_version_string(n): if n == 3: return "{}.{}.{}".format(VER_MAJOR,VER_MINOR,VER_BUILD) return "{}.{}.{}.{}".format(VER_MAJOR,VER_MINOR,VER_BUILD,VER_TWEAK) def build_static_lib(): return STATIC_LIB def build_static_bin(): return STATIC_BIN def is_cr_lf(fname): # Check whether text files use cr/lf f = open(fname, 'r') line = f.readline() f.close() sz = len(line) return sz >= 2 and line[sz-2] == '\r' and line[sz-1] == '\n' # dos2unix in python # cr/lf --> lf def dos2unix(fname): if is_cr_lf(fname): fin = open(fname, 'r') fname_new = '%s.new' % fname fout = open(fname_new, 'w') for line in fin: line = line.rstrip('\r\n') fout.write(line) fout.write('\n') fin.close() fout.close() shutil.move(fname_new, fname) if is_verbose(): print("dos2unix '%s'" % fname) def dos2unix_tree(): for root, dirs, files in os.walk('src'): for f in files: dos2unix(os.path.join(root, f)) def check_eol(): if not IS_WINDOWS: # Linux/OSX/BSD check if the end-of-line is cr/lf if is_cr_lf('LICENSE.txt'): if is_verbose(): print("Fixing end of line...") dos2unix_tree() if os.name == 'nt': IS_WINDOWS=True # Visual Studio already displays the files being compiled SHOW_CPPS=False elif os.name == 'posix': if os.uname()[0] == 'Darwin': IS_OSX=True PREFIX="/usr/local" elif os.uname()[0] == 'Linux': IS_LINUX=True elif os.uname()[0] == 'GNU': IS_HURD=True elif os.uname()[0] == 'FreeBSD': IS_FREEBSD=True elif os.uname()[0] == 'NetBSD': IS_NETBSD=True elif os.uname()[0] == 'OpenBSD': IS_OPENBSD=True elif os.uname()[0][:6] == 'CYGWIN': IS_CYGWIN=True if (CC != None and "mingw" in CC): IS_CYGWIN_MINGW=True elif os.uname()[0].startswith('MSYS_NT') or os.uname()[0].startswith('MINGW'): IS_MSYS2=True if os.uname()[4] == 'x86_64': LINUX_X64=True else: LINUX_X64=False def display_help(exit_code): print("mk_make.py: Z3 Makefile generator\n") print("This script generates the Makefile for the Z3 theorem prover.") print("It must be executed from the Z3 root directory.") print("\nOptions:") print(" -h, --help display this message.") print(" -s, --silent do not print verbose messages.") if not IS_WINDOWS: print(" -p , --prefix= installation prefix (default: %s)." % PREFIX) else: print(" --parallel=num use cl option /MP with 'num' parallel processes") print(" --pypkgdir= Force a particular Python package directory (default %s)" % PYTHON_PACKAGE_DIR) print(" -b , --build= subdirectory where Z3 will be built (default: %s)." % BUILD_DIR) print(" --githash=hash include the given hash in the binaries.") print(" --git-describe include the output of 'git describe' in the version information.") print(" -d, --debug compile Z3 in debug mode.") print(" -t, --trace enable tracing in release mode.") if IS_WINDOWS: print(" --guardcf enable Control Flow Guard runtime checks.") print(" -x, --x64 create 64 binary when using Visual Studio.") else: print(" --x86 force 32-bit x86 build on x64 systems.") print(" -m, --makefiles generate only makefiles.") if IS_WINDOWS: print(" -v, --vsproj generate Visual Studio Project Files.") print(" --optimize generate optimized code during linking.") print(" --dotnet generate .NET platform bindings.") print(" --dotnet-key= sign the .NET assembly using the private key in .") print(" --java generate Java bindings.") print(" --ml generate OCaml bindings.") print(" --js generate JScript bindings.") print(" --python generate Python bindings.") print(" --staticlib build Z3 static library.") print(" --staticbin build a statically linked Z3 binary.") if not IS_WINDOWS: print(" -g, --gmp use GMP.") print(" --gprof enable gprof") print(" --log-sync synchronize access to API log files to enable multi-thread API logging.") print(" --single-threaded non-thread-safe build") print("") print("Some influential environment variables:") if not IS_WINDOWS: print(" CXX C++ compiler") print(" CC C compiler") print(" LDFLAGS Linker flags, e.g., -L if you have libraries in a non-standard directory") print(" CPPFLAGS Preprocessor flags, e.g., -I if you have header files in a non-standard directory") print(" CXXFLAGS C++ compiler flags") print(" JDK_HOME JDK installation directory (only relevant if -j or --java option is provided)") print(" JNI_HOME JNI bindings directory (only relevant if -j or --java option is provided)") print(" OCAMLC Ocaml byte-code compiler (only relevant with --ml)") print(" OCAMLFIND Ocaml find tool (only relevant with --ml)") print(" OCAMLOPT Ocaml native compiler (only relevant with --ml)") print(" OCAML_LIB Ocaml library directory (only relevant with --ml)") print(" CSC C# Compiler (only relevant if .NET bindings are enabled)") print(" GACUTIL GAC Utility (only relevant if .NET bindings are enabled)") print(" Z3_INSTALL_BIN_DIR Install directory for binaries relative to install prefix") print(" Z3_INSTALL_LIB_DIR Install directory for libraries relative to install prefix") print(" Z3_INSTALL_INCLUDE_DIR Install directory for header files relative to install prefix") print(" Z3_INSTALL_PKGCONFIG_DIR Install directory for pkgconfig files relative to install prefix") exit(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_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, LOG_SYNC, SINGLE_THREADED 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', 'dotnet-key=', 'esrp', 'staticlib', 'prefix=', 'gmp', 'java', 'parallel=', 'gprof', 'js', 'githash=', 'git-describe', 'x86', 'ml', 'optimize', 'pypkgdir=', 'python', 'staticbin', 'log-sync', 'single-threaded']) except: print("ERROR: Invalid command line option") display_help(1) for opt, arg in options: print('opt = %s, arg = %s' % (opt, arg)) if opt in ('-b', '--build'): if arg == 'src': raise MKException('The src directory should not be used to host the Makefile') set_build_dir(arg) elif opt in ('-s', '--silent'): VERBOSE = False elif opt in ('-d', '--debug'): DEBUG_MODE = True elif opt in ('-x', '--x64'): if not IS_WINDOWS: raise MKException('x64 compilation mode can only be specified when using Visual Studio') VS_X64 = True elif opt in ('--x86'): LINUX_X64=False elif opt in ('-h', '--help'): display_help(0) elif opt in ('-m', '--makefiles'): ONLY_MAKEFILES = True elif opt in ('-c', '--showcpp'): SHOW_CPPS = True elif opt in ('-v', '--vsproj'): VS_PROJ = True elif opt in ('-t', '--trace'): TRACE = True elif opt in ('--dotnet',): 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'): STATIC_BIN = True elif opt in ('--optimize'): SLOW_OPTIMIZE = True elif not IS_WINDOWS and opt in ('-p', '--prefix'): PREFIX = arg elif opt in ('--pypkgdir'): PYTHON_PACKAGE_DIR = arg elif IS_WINDOWS and opt == '--parallel': VS_PAR = True VS_PAR_NUM = int(arg) elif opt in ('-g', '--gmp'): GMP = True elif opt in ('-j', '--java'): JAVA_ENABLED = True elif opt == '--gprof': GPROF = True elif opt == '--githash': GIT_HASH=arg elif opt == '--git-describe': GIT_DESCRIBE = True elif opt in ('', '--ml'): ML_ENABLED = True elif opt == "--js": JS_ENABLED = True elif opt in ('', '--log-sync'): LOG_SYNC = True elif opt == '--single-threaded': SINGLE_THREADED = True elif opt in ('--python'): PYTHON_ENABLED = True PYTHON_INSTALL_ENABLED = True elif opt == '--guardcf': GUARD_CF = True ALWAYS_DYNAMIC_BASE = True # /GUARD:CF requires /DYNAMICBASE else: print("ERROR: Invalid command line option '%s'" % opt) display_help(1) # Return a list containing a file names included using '#include' in # the given C/C++ file named fname. def extract_c_includes(fname): result = [] # We look for well behaved #include directives std_inc_pat = re.compile("[ \t]*#include[ \t]*\"(.*)\"[ \t]*") system_inc_pat = re.compile("[ \t]*#include[ \t]*\<.*\>[ \t]*") # 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 = io.open(fname, encoding='utf-8', mode='r') linenum = 1 for line in f: m1 = std_inc_pat.match(line) if m1: root_file_name = m1.group(1) slash_pos = root_file_name.rfind('/') if slash_pos >= 0 and root_file_name.find("..") < 0 : #it is a hack for lp include files that behave as continued from "src" # print(root_file_name) root_file_name = root_file_name[slash_pos+1:] result.append(root_file_name) elif not system_inc_pat.match(line) and non_std_inc_pat.match(line): raise MKException("Invalid #include directive at '%s':%s" % (fname, line)) linenum = linenum + 1 f.close() return result # Given a path dir1/subdir2/subdir3 returns ../../.. def reverse_path(p): # Filter out empty components (e.g. will have one if path ends in a slash) l = list(filter(lambda x: len(x) > 0, p.split(os.sep))) n = len(l) r = '..' for i in range(1, n): r = os.path.join(r, '..') return r def mk_dir(d): if not os.path.exists(d): os.makedirs(d) def set_build_dir(d): global BUILD_DIR, REV_BUILD_DIR BUILD_DIR = norm_path(d) REV_BUILD_DIR = reverse_path(d) def set_z3js_dir(p): global SRC_DIR, Z3JS_SRC_DIR p = norm_path(p) full = os.path.join(SRC_DIR, p) if not os.path.exists(full): raise MKException("Python bindings directory '%s' does not exist" % full) Z3JS_SRC_DIR = full if VERBOSE: print("Js bindings directory was detected.") def set_z3py_dir(p): global SRC_DIR, Z3PY_SRC_DIR p = norm_path(p) full = os.path.join(SRC_DIR, p) if not os.path.exists(full): raise MKException("Python bindings directory '%s' does not exist" % full) Z3PY_SRC_DIR = full if VERBOSE: print("Python bindings directory was detected.") _UNIQ_ID = 0 def mk_fresh_name(prefix): global _UNIQ_ID r = '%s_%s' % (prefix, _UNIQ_ID) _UNIQ_ID = _UNIQ_ID + 1 return r _Id = 0 _Components = [] _ComponentNames = set() _Name2Component = {} _Processed_Headers = set() # Return the Component object named name def get_component(name): return _Name2Component[name] def get_components(): return _Components # Return the directory where the python bindings are located. def get_z3py_dir(): return Z3PY_SRC_DIR # Return directory where the js bindings are located def get_z3js_dir(): return Z3JS_SRC_DIR # Return true if in verbose mode def is_verbose(): return VERBOSE def is_java_enabled(): return JAVA_ENABLED def is_ml_enabled(): return ML_ENABLED def is_js_enabled(): return JS_ENABLED def is_dotnet_core_enabled(): return DOTNET_CORE_ENABLED def is_python_enabled(): return PYTHON_ENABLED def is_python_install_enabled(): return PYTHON_INSTALL_ENABLED def is_compiler(given, expected): """ Return True if the 'given' compiler is the expected one. >>> is_compiler('g++', 'g++') True >>> is_compiler('/home/g++', 'g++') True >>> is_compiler(os.path.join('home', 'g++'), 'g++') True >>> is_compiler('clang++', 'g++') False >>> is_compiler(os.path.join('home', 'clang++'), 'clang++') True """ if given == expected: return True if len(expected) < len(given): return given[len(given) - len(expected) - 1] == os.sep and given[len(given) - len(expected):] == expected return False def is_CXX_gpp(): return is_compiler(CXX, 'g++') def is_clang_in_gpp_form(cc): str = check_output([cc, '--version']) try: version_string = str.encode('utf-8') except: version_string = str clang = 'clang'.encode('utf-8') return version_string.find(clang) != -1 def is_CXX_clangpp(): if is_compiler(CXX, 'g++'): return is_clang_in_gpp_form(CXX) return is_compiler(CXX, 'clang++') def get_cpp_files(path): return filter(lambda f: f.endswith('.cpp'), os.listdir(path)) def get_c_files(path): return filter(lambda f: f.endswith('.c'), os.listdir(path)) def get_cs_files(path): return filter(lambda f: f.endswith('.cs'), os.listdir(path)) def get_java_files(path): return filter(lambda f: f.endswith('.java'), os.listdir(path)) def get_ml_files(path): return filter(lambda f: f.endswith('.ml'), os.listdir(path)) def find_all_deps(name, deps): new_deps = [] for dep in deps: if dep in _ComponentNames: if not (dep in new_deps): new_deps.append(dep) for dep_dep in get_component(dep).deps: if not (dep_dep in new_deps): new_deps.append(dep_dep) else: raise MKException("Unknown component '%s' at '%s'." % (dep, name)) return new_deps class Component: def __init__(self, name, path, deps): global BUILD_DIR, SRC_DIR, REV_BUILD_DIR if name in _ComponentNames: raise MKException("Component '%s' was already defined." % name) if path is None: path = name self.name = name path = norm_path(path) self.path = path self.deps = find_all_deps(name, deps) self.build_dir = path self.src_dir = os.path.join(SRC_DIR, path) self.to_src_dir = os.path.join(REV_BUILD_DIR, self.src_dir) def get_link_name(self): return os.path.join(self.build_dir, self.name) + '$(LIB_EXT)' # Find fname in the include paths for the given component. # ownerfile is only used for creating error messages. # That is, we were looking for fname when processing ownerfile def find_file(self, fname, ownerfile): full_fname = os.path.join(self.src_dir, fname) if os.path.exists(full_fname): return self for dep in self.deps: c_dep = get_component(dep) full_fname = os.path.join(c_dep.src_dir, fname) if os.path.exists(full_fname): return c_dep raise MKException("Failed to find include file '%s' for '%s' when processing '%s'." % (fname, ownerfile, self.name)) # Display all dependencies of file basename located in the given component directory. # The result is displayed at out def add_cpp_h_deps(self, out, basename): includes = extract_c_includes(os.path.join(self.src_dir, basename)) out.write(os.path.join(self.to_src_dir, basename)) for include in includes: owner = self.find_file(include, basename) out.write(' %s.node' % os.path.join(owner.build_dir, include)) # Add a rule for each #include directive in the file basename located at the current component. def add_rule_for_each_include(self, out, basename): fullname = os.path.join(self.src_dir, basename) includes = extract_c_includes(fullname) for include in includes: owner = self.find_file(include, fullname) owner.add_h_rule(out, include) # Display a Makefile rule for an include file located in the given component directory. # 'include' is something of the form: ast.h, polynomial.h # The rule displayed at out is of the form # ast/ast_pp.h.node : ../src/util/ast_pp.h util/util.h.node ast/ast.h.node # @echo "done" > ast/ast_pp.h.node def add_h_rule(self, out, include): include_src_path = os.path.join(self.to_src_dir, include) if include_src_path in _Processed_Headers: return _Processed_Headers.add(include_src_path) self.add_rule_for_each_include(out, include) include_node = '%s.node' % os.path.join(self.build_dir, include) out.write('%s: ' % include_node) self.add_cpp_h_deps(out, include) out.write('\n') out.write('\t@echo done > %s\n' % include_node) def add_cpp_rules(self, out, include_defs, cppfile): self.add_rule_for_each_include(out, cppfile) objfile = '%s$(OBJ_EXT)' % os.path.join(self.build_dir, os.path.splitext(cppfile)[0]) srcfile = os.path.join(self.to_src_dir, cppfile) out.write('%s: ' % objfile) self.add_cpp_h_deps(out, cppfile) out.write('\n') if SHOW_CPPS: out.write('\t@echo %s\n' % os.path.join(self.src_dir, cppfile)) out.write('\t@$(CXX) $(CXXFLAGS) $(%s) $(CXX_OUT_FLAG)%s %s\n' % (include_defs, objfile, srcfile)) def mk_makefile(self, out): include_defs = mk_fresh_name('includes') out.write('%s =' % include_defs) for dep in self.deps: out.write(' -I%s' % get_component(dep).to_src_dir) out.write(' -I%s' % os.path.join(REV_BUILD_DIR,"src")) out.write('\n') mk_dir(os.path.join(BUILD_DIR, self.build_dir)) if VS_PAR and IS_WINDOWS: cppfiles = list(get_cpp_files(self.src_dir)) dependencies = set() for cppfile in cppfiles: dependencies.add(os.path.join(self.to_src_dir, cppfile)) self.add_rule_for_each_include(out, cppfile) includes = extract_c_includes(os.path.join(self.src_dir, cppfile)) for include in includes: owner = self.find_file(include, cppfile) dependencies.add('%s.node' % os.path.join(owner.build_dir, include)) for cppfile in cppfiles: out.write('%s$(OBJ_EXT) ' % os.path.join(self.build_dir, os.path.splitext(cppfile)[0])) out.write(': ') for dep in dependencies: out.write(dep) out.write(' ') out.write('\n') out.write('\t@$(CXX) $(CXXFLAGS) /MP%s $(%s)' % (VS_PAR_NUM, include_defs)) for cppfile in cppfiles: out.write(' ') out.write(os.path.join(self.to_src_dir, cppfile)) out.write('\n') out.write('\tmove *.obj %s\n' % self.build_dir) else: for cppfile in get_cpp_files(self.src_dir): self.add_cpp_rules(out, include_defs, cppfile) # Return true if the component should be included in the all: rule def main_component(self): return False # Return true if the component contains an AssemblyInfo.cs file that needs to be updated. def has_assembly_info(self): return False # Return true if the component needs builder to generate an install_tactics.cpp file def require_install_tactics(self): return False # Return true if the component needs a def file def require_def_file(self): return False # Return true if the component needs builder to generate a mem_initializer.cpp file with mem_initialize() and mem_finalize() functions. def require_mem_initializer(self): return False def mk_install_deps(self, out): return def mk_install(self, out): return def mk_uninstall(self, out): return def is_example(self): return False # Invoked when creating a (windows) distribution package using components at build_path, and # storing them at dist_path def mk_win_dist(self, build_path, dist_path): return def mk_unix_dist(self, build_path, dist_path): return # Used to print warnings or errors after mk_make.py is done, so that they # are not quite as easy to miss. def final_info(self): pass class LibComponent(Component): def __init__(self, name, path, deps, includes2install): Component.__init__(self, name, path, deps) self.includes2install = includes2install def mk_makefile(self, out): Component.mk_makefile(self, out) # generate rule for lib objs = [] for cppfile in get_cpp_files(self.src_dir): objfile = '%s$(OBJ_EXT)' % os.path.join(self.build_dir, os.path.splitext(cppfile)[0]) objs.append(objfile) libfile = '%s$(LIB_EXT)' % os.path.join(self.build_dir, self.name) out.write('%s:' % libfile) for obj in objs: out.write(' ') out.write(obj) out.write('\n') out.write('\t@$(AR) $(AR_FLAGS) $(AR_OUTFLAG)%s' % libfile) for obj in objs: out.write(' ') out.write(obj) out.write('\n') out.write('%s: %s\n\n' % (self.name, libfile)) def mk_install_deps(self, out): return def mk_install(self, out): for include in self.includes2install: MakeRuleCmd.install_files( out, os.path.join(self.to_src_dir, include), os.path.join(INSTALL_INCLUDE_DIR, include) ) def mk_uninstall(self, out): for include in self.includes2install: MakeRuleCmd.remove_installed_files(out, os.path.join(INSTALL_INCLUDE_DIR, include)) def mk_win_dist(self, build_path, dist_path): mk_dir(os.path.join(dist_path, INSTALL_INCLUDE_DIR)) for include in self.includes2install: shutil.copy(os.path.join(self.src_dir, include), os.path.join(dist_path, INSTALL_INCLUDE_DIR, include)) def mk_unix_dist(self, build_path, dist_path): self.mk_win_dist(build_path, dist_path) # "Library" containing only .h files. This is just a placeholder for includes files to be installed. class HLibComponent(LibComponent): def __init__(self, name, path, includes2install): LibComponent.__init__(self, name, path, [], includes2install) def mk_makefile(self, out): return # Auxiliary function for sort_components def comp_components(c1, c2): id1 = get_component(c1).id id2 = get_component(c2).id return id2 - id1 # Sort components based on (reverse) definition time def sort_components(cnames): return sorted(cnames, key=lambda c: get_component(c).id, reverse=True) class ExeComponent(Component): def __init__(self, name, exe_name, path, deps, install): Component.__init__(self, name, path, deps) if exe_name is None: exe_name = name self.exe_name = exe_name self.install = install def mk_makefile(self, out): Component.mk_makefile(self, out) # generate rule for exe exefile = '%s$(EXE_EXT)' % self.exe_name out.write('%s:' % exefile) deps = sort_components(self.deps) objs = [] for cppfile in get_cpp_files(self.src_dir): objfile = '%s$(OBJ_EXT)' % os.path.join(self.build_dir, os.path.splitext(cppfile)[0]) objs.append(objfile) for obj in objs: out.write(' ') out.write(obj) for dep in deps: c_dep = get_component(dep) out.write(' ' + c_dep.get_link_name()) out.write('\n') extra_opt = '-static' if not IS_WINDOWS and STATIC_BIN else '' out.write('\t$(LINK) %s $(LINK_OUT_FLAG)%s $(LINK_FLAGS)' % (extra_opt, exefile)) for obj in objs: out.write(' ') out.write(obj) for dep in deps: c_dep = get_component(dep) out.write(' ' + c_dep.get_link_name()) out.write(' $(LINK_EXTRA_FLAGS)\n') out.write('%s: %s\n\n' % (self.name, exefile)) def require_install_tactics(self): return ('tactic' in self.deps) and ('cmd_context' in self.deps) def require_mem_initializer(self): return True # All executables (to be installed) are included in the all: rule def main_component(self): return self.install def mk_install_deps(self, out): if self.install: exefile = '%s$(EXE_EXT)' % self.exe_name out.write('%s' % exefile) def mk_install(self, out): if self.install: exefile = '%s$(EXE_EXT)' % self.exe_name MakeRuleCmd.install_files(out, exefile, os.path.join(INSTALL_BIN_DIR, exefile)) def mk_uninstall(self, out): if self.install: exefile = '%s$(EXE_EXT)' % self.exe_name MakeRuleCmd.remove_installed_files(out, os.path.join(INSTALL_BIN_DIR, exefile)) def mk_win_dist(self, build_path, dist_path): if self.install: mk_dir(os.path.join(dist_path, INSTALL_BIN_DIR)) shutil.copy('%s.exe' % os.path.join(build_path, self.exe_name), '%s.exe' % os.path.join(dist_path, INSTALL_BIN_DIR, self.exe_name)) def mk_unix_dist(self, build_path, dist_path): if self.install: mk_dir(os.path.join(dist_path, INSTALL_BIN_DIR)) shutil.copy(os.path.join(build_path, self.exe_name), os.path.join(dist_path, INSTALL_BIN_DIR, self.exe_name)) class ExtraExeComponent(ExeComponent): def __init__(self, name, exe_name, path, deps, install): ExeComponent.__init__(self, name, exe_name, path, deps, install) def main_component(self): return False def require_mem_initializer(self): return False def get_so_ext(): sysname = os.uname()[0] if sysname == 'Darwin': return 'dylib' elif sysname == 'Linux' or sysname == 'GNU' or sysname == 'FreeBSD' or sysname == 'NetBSD' or sysname == 'OpenBSD': return 'so' elif sysname == 'CYGWIN' or sysname.startswith('MSYS_NT') or sysname.startswith('MINGW'): return 'dll' else: assert(False) return 'dll' class DLLComponent(Component): def __init__(self, name, dll_name, path, deps, export_files, reexports, install, static, staging_link=None): Component.__init__(self, name, path, deps) if dll_name is None: dll_name = name self.dll_name = dll_name self.export_files = export_files self.reexports = reexports self.install = install self.static = static self.staging_link = staging_link # link a copy of the shared object into this directory on build def get_link_name(self): if self.static: return os.path.join(self.build_dir, self.name) + '$(LIB_EXT)' else: return self.name + '$(SO_EXT)' def dll_file(self): """ Return file name of component suitable for use in a Makefile """ return '%s$(SO_EXT)' % self.dll_name def install_path(self): """ Return install location of component (relative to prefix) suitable for use in a Makefile """ return os.path.join(INSTALL_LIB_DIR, self.dll_file()) def mk_makefile(self, out): Component.mk_makefile(self, out) # generate rule for (SO_EXT) out.write('%s:' % self.dll_file()) deps = sort_components(self.deps) objs = [] for cppfile in get_cpp_files(self.src_dir): objfile = '%s$(OBJ_EXT)' % os.path.join(self.build_dir, os.path.splitext(cppfile)[0]) objs.append(objfile) # Explicitly include obj files of reexport. This fixes problems with exported symbols on Linux and OSX. for reexport in self.reexports: reexport = get_component(reexport) for cppfile in get_cpp_files(reexport.src_dir): objfile = '%s$(OBJ_EXT)' % os.path.join(reexport.build_dir, os.path.splitext(cppfile)[0]) objs.append(objfile) for obj in objs: out.write(' ') out.write(obj) for dep in deps: if dep not in self.reexports: c_dep = get_component(dep) out.write(' ' + c_dep.get_link_name()) out.write('\n') out.write('\t$(LINK) $(SLINK_OUT_FLAG)%s $(SLINK_FLAGS)' % self.dll_file()) for obj in objs: out.write(' ') out.write(obj) for dep in deps: if dep not in self.reexports: c_dep = get_component(dep) out.write(' ' + c_dep.get_link_name()) out.write(' $(SLINK_EXTRA_FLAGS)') if IS_WINDOWS: out.write(' /DEF:%s.def' % os.path.join(self.to_src_dir, self.name)) if self.staging_link: if IS_WINDOWS: out.write('\n\tcopy %s %s' % (self.dll_file(), self.staging_link)) elif IS_OSX: out.write('\n\tcp %s %s' % (self.dll_file(), self.staging_link)) else: out.write('\n\tln -f -s %s %s' % (os.path.join(reverse_path(self.staging_link), self.dll_file()), self.staging_link)) out.write('\n') if self.static: if IS_WINDOWS: libfile = '%s-static$(LIB_EXT)' % self.dll_name else: libfile = '%s$(LIB_EXT)' % self.dll_name self.mk_static(out, libfile) out.write('%s: %s %s\n\n' % (self.name, self.dll_file(), libfile)) else: out.write('%s: %s\n\n' % (self.name, self.dll_file())) def mk_static(self, out, libfile): # generate rule for lib objs = [] for cppfile in get_cpp_files(self.src_dir): objfile = '%s$(OBJ_EXT)' % os.path.join(self.build_dir, os.path.splitext(cppfile)[0]) objs.append(objfile) # we have to "reexport" all object files for dep in self.deps: dep = get_component(dep) for cppfile in get_cpp_files(dep.src_dir): objfile = '%s$(OBJ_EXT)' % os.path.join(dep.build_dir, os.path.splitext(cppfile)[0]) objs.append(objfile) out.write('%s:' % libfile) for obj in objs: out.write(' ') out.write(obj) out.write('\n') out.write('\t@$(AR) $(AR_FLAGS) $(AR_OUTFLAG)%s' % libfile) for obj in objs: out.write(' ') out.write(obj) out.write('\n') def main_component(self): return self.install def require_install_tactics(self): return ('tactic' in self.deps) and ('cmd_context' in self.deps) def require_mem_initializer(self): return True def require_def_file(self): return IS_WINDOWS and self.export_files def mk_install_deps(self, out): out.write('%s$(SO_EXT)' % self.dll_name) if self.static: out.write(' %s$(LIB_EXT)' % self.dll_name) def mk_install(self, out): if self.install: MakeRuleCmd.install_files(out, self.dll_file(), self.install_path()) if self.static: libfile = '%s$(LIB_EXT)' % self.dll_name MakeRuleCmd.install_files(out, libfile, os.path.join(INSTALL_LIB_DIR, libfile)) def mk_uninstall(self, out): MakeRuleCmd.remove_installed_files(out, self.install_path()) libfile = '%s$(LIB_EXT)' % self.dll_name MakeRuleCmd.remove_installed_files(out, os.path.join(INSTALL_LIB_DIR, libfile)) def mk_win_dist(self, build_path, dist_path): if self.install: mk_dir(os.path.join(dist_path, INSTALL_BIN_DIR)) shutil.copy('%s.dll' % os.path.join(build_path, self.dll_name), '%s.dll' % os.path.join(dist_path, INSTALL_BIN_DIR, self.dll_name)) shutil.copy('%s.pdb' % os.path.join(build_path, self.dll_name), '%s.pdb' % os.path.join(dist_path, INSTALL_BIN_DIR, self.dll_name)) shutil.copy('%s.lib' % os.path.join(build_path, self.dll_name), '%s.lib' % os.path.join(dist_path, INSTALL_BIN_DIR, self.dll_name)) def mk_unix_dist(self, build_path, dist_path): if self.install: mk_dir(os.path.join(dist_path, INSTALL_BIN_DIR)) so = get_so_ext() shutil.copy('%s.%s' % (os.path.join(build_path, self.dll_name), so), '%s.%s' % (os.path.join(dist_path, INSTALL_BIN_DIR, self.dll_name), so)) shutil.copy('%s.a' % os.path.join(build_path, self.dll_name), '%s.a' % os.path.join(dist_path, INSTALL_BIN_DIR, self.dll_name)) class JsComponent(Component): def __init__(self): Component.__init__(self, "js", None, []) def main_component(self): return False def mk_win_dist(self, build_path, dist_path): return def mk_unix_dist(self, build_path, dist_path): return def mk_makefile(self, out): return class PythonComponent(Component): def __init__(self, name, libz3Component): assert isinstance(libz3Component, DLLComponent) global PYTHON_ENABLED Component.__init__(self, name, None, []) self.libz3Component = libz3Component def main_component(self): return False def mk_win_dist(self, build_path, dist_path): if not is_python_enabled(): return src = os.path.join(build_path, 'python', 'z3') dst = os.path.join(dist_path, INSTALL_BIN_DIR, 'python', 'z3') if os.path.exists(dst): shutil.rmtree(dst) shutil.copytree(src, dst) def mk_unix_dist(self, build_path, dist_path): self.mk_win_dist(build_path, dist_path) def mk_makefile(self, out): return class PythonInstallComponent(Component): def __init__(self, name, libz3Component): assert isinstance(libz3Component, DLLComponent) global PYTHON_INSTALL_ENABLED Component.__init__(self, name, None, []) self.pythonPkgDir = None self.in_prefix_install = True self.libz3Component = libz3Component if not PYTHON_INSTALL_ENABLED: return if IS_WINDOWS: # Installing under Windows doesn't make sense as the install prefix is used # but that doesn't make sense under Windows # CMW: It makes perfectly good sense; the prefix is Python's sys.prefix, # i.e., something along the lines of C:\Python\... At the moment we are not # sure whether we would want to install libz3.dll into that directory though. PYTHON_INSTALL_ENABLED = False return else: PYTHON_INSTALL_ENABLED = True if IS_WINDOWS or IS_OSX: # Use full path that is possibly outside of install prefix self.in_prefix_install = PYTHON_PACKAGE_DIR.startswith(PREFIX) self.pythonPkgDir = strip_path_prefix(PYTHON_PACKAGE_DIR, PREFIX) else: # Use path inside the prefix (should be the normal case on Linux) # CMW: Also normal on *BSD? if not PYTHON_PACKAGE_DIR.startswith(PREFIX): raise MKException(('The python package directory ({}) must live ' + 'under the install prefix ({}) to install the python bindings.' + 'Use --pypkgdir and --prefix to set the python package directory ' + 'and install prefix respectively. Note that the python package ' + 'directory does not need to exist and will be created if ' + 'necessary during install.').format( PYTHON_PACKAGE_DIR, PREFIX)) self.pythonPkgDir = strip_path_prefix(PYTHON_PACKAGE_DIR, PREFIX) self.in_prefix_install = True if self.in_prefix_install: assert not os.path.isabs(self.pythonPkgDir) def final_info(self): if not PYTHON_PACKAGE_DIR.startswith(PREFIX) and PYTHON_INSTALL_ENABLED: print("Warning: The detected Python package directory (%s) is not " "in the installation prefix (%s). This can lead to a broken " "Python API installation. Use --pypkgdir= to change the " "Python package directory." % (PYTHON_PACKAGE_DIR, PREFIX)) def main_component(self): return False def mk_install(self, out): if not is_python_install_enabled(): return MakeRuleCmd.make_install_directory(out, os.path.join(self.pythonPkgDir, 'z3'), in_prefix=self.in_prefix_install) MakeRuleCmd.make_install_directory(out, os.path.join(self.pythonPkgDir, 'z3', 'lib'), in_prefix=self.in_prefix_install) # Sym-link or copy libz3 into python package directory if IS_WINDOWS or IS_OSX: MakeRuleCmd.install_files(out, self.libz3Component.dll_file(), os.path.join(self.pythonPkgDir, 'z3', 'lib', self.libz3Component.dll_file()), in_prefix=self.in_prefix_install ) else: # Create symbolic link to save space. # It's important that this symbolic link be relative (rather # than absolute) so that the install is relocatable (needed for # staged installs that use DESTDIR). MakeRuleCmd.create_relative_symbolic_link(out, self.libz3Component.install_path(), os.path.join(self.pythonPkgDir, 'z3', 'lib', self.libz3Component.dll_file() ), ) MakeRuleCmd.install_files(out, os.path.join('python', 'z3', '*.py'), os.path.join(self.pythonPkgDir, 'z3'), in_prefix=self.in_prefix_install) if sys.version >= "3": pythonPycacheDir = os.path.join(self.pythonPkgDir, 'z3', '__pycache__') MakeRuleCmd.make_install_directory(out, pythonPycacheDir, in_prefix=self.in_prefix_install) MakeRuleCmd.install_files(out, os.path.join('python', 'z3', '__pycache__', '*.pyc'), pythonPycacheDir, in_prefix=self.in_prefix_install) else: MakeRuleCmd.install_files(out, os.path.join('python', 'z3', '*.pyc'), os.path.join(self.pythonPkgDir,'z3'), in_prefix=self.in_prefix_install) if PYTHON_PACKAGE_DIR != distutils.sysconfig.get_python_lib(): out.write('\t@echo Z3Py was installed at \'%s\', make sure this directory is in your PYTHONPATH environment variable.' % PYTHON_PACKAGE_DIR) def mk_uninstall(self, out): if not is_python_install_enabled(): return MakeRuleCmd.remove_installed_files(out, os.path.join(self.pythonPkgDir, self.libz3Component.dll_file()), in_prefix=self.in_prefix_install ) MakeRuleCmd.remove_installed_files(out, os.path.join(self.pythonPkgDir, 'z3', '*.py'), in_prefix=self.in_prefix_install) MakeRuleCmd.remove_installed_files(out, os.path.join(self.pythonPkgDir, 'z3', '*.pyc'), in_prefix=self.in_prefix_install) MakeRuleCmd.remove_installed_files(out, os.path.join(self.pythonPkgDir, 'z3', '__pycache__', '*.pyc'), in_prefix=self.in_prefix_install ) MakeRuleCmd.remove_installed_files(out, os.path.join(self.pythonPkgDir, 'z3', 'lib', self.libz3Component.dll_file())) def mk_makefile(self, out): return def set_key_file(self): global DOTNET_KEY_FILE # We need to give the assembly a strong name so that it # can be installed into the GAC with ``make install`` if not DOTNET_KEY_FILE is None: self.key_file = DOTNET_KEY_FILE if not self.key_file is None: if os.path.isfile(self.key_file): self.key_file = os.path.abspath(self.key_file) elif os.path.isfile(os.path.join(self.src_dir, self.key_file)): self.key_file = os.path.abspath(os.path.join(self.src_dir, self.key_file)) else: print("Keyfile '%s' could not be found; %s.dll will be unsigned." % (self.key_file, self.dll_name)) self.key_file = None # build for dotnet core class DotNetDLLComponent(Component): def __init__(self, name, dll_name, path, deps, assembly_info_dir, default_key_file): Component.__init__(self, name, path, deps) if dll_name is None: dll_name = name if assembly_info_dir is None: assembly_info_dir = "." self.dll_name = dll_name self.assembly_info_dir = assembly_info_dir self.key_file = default_key_file def mk_makefile(self, out): if not is_dotnet_core_enabled(): return cs_fp_files = [] for cs_file in get_cs_files(self.src_dir): cs_fp_files.append(os.path.join(self.to_src_dir, cs_file)) if self.assembly_info_dir != '.': for cs_file in get_cs_files(os.path.join(self.src_dir, self.assembly_info_dir)): cs_fp_files.append(os.path.join(self.to_src_dir, self.assembly_info_dir, cs_file)) dllfile = '%s.dll' % self.dll_name out.write('%s: %s$(SO_EXT)' % (dllfile, get_component(Z3_DLL_COMPONENT).dll_name)) for cs_file in cs_fp_files: out.write(' ') out.write(cs_file) out.write('\n') set_key_file(self) key = "" if not self.key_file is None: key = "%s" % self.key_file key += "\ntrue" version = get_version_string(3) core_csproj_str = """ netstandard1.4 $(DefineConstants);DOTNET_CORE portable Microsoft.Z3 Library Microsoft.Z3 1.0.4 %s true Microsoft Microsoft false Z3 is a satisfiability modulo theories solver from Microsoft Research. Copyright Microsoft Corporation. All rights reserved. smt constraint solver theorem prover %s """ % (version, key, self.to_src_dir) mk_dir(os.path.join(BUILD_DIR, 'dotnet')) csproj = os.path.join('dotnet', 'z3.csproj') with open(os.path.join(BUILD_DIR, csproj), 'w') as ous: ous.write(core_csproj_str) dotnetCmdLine = [DOTNET, "build", csproj] dotnetCmdLine.extend(['-c']) if DEBUG_MODE: dotnetCmdLine.extend(['Debug']) else: dotnetCmdLine.extend(['Release']) path = os.path.join(os.path.abspath(BUILD_DIR), ".") dotnetCmdLine.extend(['-o', path]) MakeRuleCmd.write_cmd(out, ' '.join(dotnetCmdLine)) 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 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)) shutil.copy('%s.dll' % os.path.join(build_path, self.dll_name), '%s.dll' % os.path.join(dist_path, INSTALL_BIN_DIR, self.dll_name)) shutil.copy('%s.pdb' % os.path.join(build_path, self.dll_name), '%s.pdb' % os.path.join(dist_path, INSTALL_BIN_DIR, self.dll_name)) shutil.copy('%s.deps.json' % os.path.join(build_path, self.dll_name), '%s.deps.json' % os.path.join(dist_path, INSTALL_BIN_DIR, self.dll_name)) if DEBUG_MODE: shutil.copy('%s.pdb' % os.path.join(build_path, self.dll_name), '%s.pdb' % os.path.join(dist_path, INSTALL_BIN_DIR, self.dll_name)) def mk_unix_dist(self, build_path, dist_path): if is_dotnet_core_enabled(): mk_dir(os.path.join(dist_path, INSTALL_BIN_DIR)) shutil.copy('%s.dll' % os.path.join(build_path, self.dll_name), '%s.dll' % os.path.join(dist_path, INSTALL_BIN_DIR, self.dll_name)) shutil.copy('%s.deps.json' % os.path.join(build_path, self.dll_name), '%s.deps.json' % os.path.join(dist_path, INSTALL_BIN_DIR, self.dll_name)) def mk_install_deps(self, out): pass def mk_install(self, out): pass def mk_uninstall(self, out): pass class JavaDLLComponent(Component): def __init__(self, name, dll_name, package_name, manifest_file, path, deps): Component.__init__(self, name, path, deps) if dll_name is None: dll_name = name self.dll_name = dll_name self.package_name = package_name self.manifest_file = manifest_file self.install = not is_windows() def mk_makefile(self, out): global JAVAC global JAR if is_java_enabled(): mk_dir(os.path.join(BUILD_DIR, 'api', 'java', 'classes')) dllfile = '%s$(SO_EXT)' % self.dll_name out.write('libz3java$(SO_EXT): libz3$(SO_EXT) %s\n' % os.path.join(self.to_src_dir, 'Native.cpp')) t = '\t$(CXX) $(CXXFLAGS) $(CXX_OUT_FLAG)api/java/Native$(OBJ_EXT) -I"%s" -I"%s/PLATFORM" -I%s %s/Native.cpp\n' % (JNI_HOME, JNI_HOME, get_component('api').to_src_dir, self.to_src_dir) if IS_OSX: t = t.replace('PLATFORM', 'darwin') elif is_linux(): t = t.replace('PLATFORM', 'linux') elif is_hurd(): t = t.replace('PLATFORM', 'hurd') elif IS_FREEBSD: t = t.replace('PLATFORM', 'freebsd') elif IS_NETBSD: t = t.replace('PLATFORM', 'netbsd') elif IS_OPENBSD: t = t.replace('PLATFORM', 'openbsd') elif IS_CYGWIN: t = t.replace('PLATFORM', 'cygwin') elif IS_MSYS2: t = t.replace('PLATFORM', 'win32') else: t = t.replace('PLATFORM', 'win32') out.write(t) if IS_WINDOWS: # On Windows, CL creates a .lib file to link against. out.write('\t$(SLINK) $(SLINK_OUT_FLAG)libz3java$(SO_EXT) $(SLINK_FLAGS) %s$(OBJ_EXT) libz3$(LIB_EXT)\n' % os.path.join('api', 'java', 'Native')) else: out.write('\t$(SLINK) $(SLINK_OUT_FLAG)libz3java$(SO_EXT) $(SLINK_FLAGS) %s$(OBJ_EXT) libz3$(SO_EXT)\n' % os.path.join('api', 'java', 'Native')) out.write('%s.jar: libz3java$(SO_EXT) ' % self.package_name) deps = '' for jfile in get_java_files(self.src_dir): deps += ('%s ' % os.path.join(self.to_src_dir, jfile)) for jfile in get_java_files(os.path.join(self.src_dir, "enumerations")): deps += '%s ' % os.path.join(self.to_src_dir, 'enumerations', jfile) out.write(deps) out.write('\n') #if IS_WINDOWS: JAVAC = '"%s"' % JAVAC JAR = '"%s"' % JAR t = ('\t%s %s.java -d %s\n' % (JAVAC, os.path.join(self.to_src_dir, 'enumerations', '*'), os.path.join('api', 'java', 'classes'))) out.write(t) t = ('\t%s -cp %s %s.java -d %s\n' % (JAVAC, os.path.join('api', 'java', 'classes'), os.path.join(self.to_src_dir, '*'), os.path.join('api', 'java', 'classes'))) out.write(t) out.write('\t%s cfm %s.jar %s -C %s .\n' % (JAR, self.package_name, os.path.join(self.to_src_dir, 'manifest'), os.path.join('api', 'java', 'classes'))) out.write('java: %s.jar\n\n' % self.package_name) def main_component(self): return is_java_enabled() def mk_win_dist(self, build_path, dist_path): if JAVA_ENABLED: mk_dir(os.path.join(dist_path, INSTALL_BIN_DIR)) shutil.copy('%s.jar' % os.path.join(build_path, self.package_name), '%s.jar' % os.path.join(dist_path, INSTALL_BIN_DIR, self.package_name)) shutil.copy(os.path.join(build_path, 'libz3java.dll'), os.path.join(dist_path, INSTALL_BIN_DIR, 'libz3java.dll')) shutil.copy(os.path.join(build_path, 'libz3java.lib'), os.path.join(dist_path, INSTALL_BIN_DIR, 'libz3java.lib')) def mk_unix_dist(self, build_path, dist_path): if JAVA_ENABLED: mk_dir(os.path.join(dist_path, INSTALL_BIN_DIR)) shutil.copy('%s.jar' % os.path.join(build_path, self.package_name), '%s.jar' % os.path.join(dist_path, INSTALL_BIN_DIR, self.package_name)) so = get_so_ext() shutil.copy(os.path.join(build_path, 'libz3java.%s' % so), os.path.join(dist_path, INSTALL_BIN_DIR, 'libz3java.%s' % so)) def mk_install(self, out): if is_java_enabled() and self.install: dllfile = '%s$(SO_EXT)' % self.dll_name MakeRuleCmd.install_files(out, dllfile, os.path.join(INSTALL_LIB_DIR, dllfile)) jarfile = '{}.jar'.format(self.package_name) MakeRuleCmd.install_files(out, jarfile, os.path.join(INSTALL_LIB_DIR, jarfile)) def mk_uninstall(self, out): if is_java_enabled() and self.install: dllfile = '%s$(SO_EXT)' % self.dll_name MakeRuleCmd.remove_installed_files(out, os.path.join(INSTALL_LIB_DIR, dllfile)) jarfile = '{}.jar'.format(self.package_name) MakeRuleCmd.remove_installed_files(out, os.path.join(INSTALL_LIB_DIR, jarfile)) class MLComponent(Component): def __init__(self, name, lib_name, path, deps): Component.__init__(self, name, path, deps) if lib_name is None: lib_name = name self.lib_name = lib_name self.modules = ["z3enums", "z3native", "z3"] # dependencies in this order! self.stubs = "z3native_stubs" self.sub_dir = os.path.join('api', 'ml') self.destdir = "" self.ldconf = "" # Calling _init_ocamlfind_paths() is postponed to later because # OCAMLFIND hasn't been checked yet. def _install_bindings(self): # FIXME: Depending on global state is gross. We can't pre-compute this # in the constructor because we haven't tested for ocamlfind yet return OCAMLFIND != '' def _init_ocamlfind_paths(self): """ Initialises self.destdir and self.ldconf Do not call this from the MLComponent constructor because OCAMLFIND has not been checked at that point """ if self.destdir != "" and self.ldconf != "": # Initialisation already done return # Use Ocamlfind to get the default destdir and ldconf path self.destdir = check_output([OCAMLFIND, 'printconf', 'destdir']) if self.destdir == "": raise MKException('Failed to get OCaml destdir') if not os.path.isdir(self.destdir): raise MKException('The destdir reported by {ocamlfind} ({destdir}) does not exist'.format(ocamlfind=OCAMLFIND, destdir=self.destdir)) self.ldconf = check_output([OCAMLFIND, 'printconf', 'ldconf']) if self.ldconf == "": raise MKException('Failed to get OCaml ldconf path') def final_info(self): if not self._install_bindings(): print("WARNING: Could not find ocamlfind utility. OCaml bindings will not be installed") def mk_makefile(self, out): if is_ml_enabled(): CP_CMD = 'cp' if IS_WINDOWS: CP_CMD='copy' OCAML_FLAGS = '' if DEBUG_MODE: OCAML_FLAGS += '-g' if OCAMLFIND: OCAMLCF = OCAMLFIND + ' ' + 'ocamlc -package zarith' + ' ' + OCAML_FLAGS OCAMLOPTF = OCAMLFIND + ' ' + 'ocamlopt -package zarith' + ' ' + OCAML_FLAGS else: OCAMLCF = OCAMLC + ' ' + OCAML_FLAGS OCAMLOPTF = OCAMLOPT + ' ' + OCAML_FLAGS src_dir = self.to_src_dir mk_dir(os.path.join(BUILD_DIR, self.sub_dir)) api_src = get_component(API_COMPONENT).to_src_dir # remove /GL and -std=c++11; the ocaml tools don't like them. if IS_WINDOWS: out.write('CXXFLAGS_OCAML=$(CXXFLAGS:/GL=)\n') else: out.write('CXXFLAGS_OCAML=$(subst -std=c++11,,$(CXXFLAGS))\n') substitutions = { 'VERSION': "{}.{}.{}.{}".format(VER_MAJOR, VER_MINOR, VER_BUILD, VER_TWEAK) } configure_file(os.path.join(self.src_dir, 'META.in'), os.path.join(BUILD_DIR, self.sub_dir, 'META'), substitutions) stubsc = os.path.join(src_dir, self.stubs + '.c') stubso = os.path.join(self.sub_dir, self.stubs) + '$(OBJ_EXT)' base_dll_name = get_component(Z3_DLL_COMPONENT).dll_name if STATIC_LIB: z3link = 'z3-static' z3linkdep = base_dll_name + '-static$(LIB_EXT)' out.write('%s: %s\n' % (z3linkdep, base_dll_name + '$(LIB_EXT)')) out.write('\tcp $< $@\n') else: z3link = 'z3' z3linkdep = base_dll_name + '$(SO_EXT)' out.write('%s: %s %s\n' % (stubso, stubsc, z3linkdep)) out.write('\t%s -ccopt "$(CXXFLAGS_OCAML) -I %s -I %s -I %s $(CXX_OUT_FLAG)%s" -c %s\n' % (OCAMLCF, OCAML_LIB, api_src, src_dir, stubso, stubsc)) cmos = '' for m in self.modules: ml = os.path.join(src_dir, m + '.ml') cmo = os.path.join(self.sub_dir, m + '.cmo') existing_mli = os.path.join(src_dir, m + '.mli') mli = os.path.join(self.sub_dir, m + '.mli') cmi = os.path.join(self.sub_dir, m + '.cmi') out.write('%s: %s %s\n' % (cmo, ml, cmos)) if (os.path.exists(existing_mli[3:])): out.write('\t%s %s %s\n' % (CP_CMD, existing_mli, mli)) else: out.write('\t%s -i -I %s -c %s > %s\n' % (OCAMLCF, self.sub_dir, ml, mli)) out.write('\t%s -I %s -o %s -c %s\n' % (OCAMLCF, self.sub_dir, cmi, mli)) out.write('\t%s -I %s -o %s -c %s\n' % (OCAMLCF, self.sub_dir, cmo, ml)) cmos = cmos + cmo + ' ' cmxs = '' for m in self.modules: ff = os.path.join(src_dir, m + '.ml') ft = os.path.join(self.sub_dir, m + '.cmx') out.write('%s: %s %s %s\n' % (ft, ff, cmos, cmxs)) out.write('\t%s -I %s -o %s -c %s\n' % (OCAMLOPTF, self.sub_dir, ft, ff)) cmxs = cmxs + ' ' + ft OCAMLMKLIB = 'ocamlmklib' LIBZ3 = '-cclib -l' + z3link if is_cygwin() and not(is_cygwin_mingw()): LIBZ3 = z3linkdep LIBZ3 = LIBZ3 + ' ' + ' '.join(map(lambda x: '-cclib ' + x, LDFLAGS.split())) if DEBUG_MODE and not(is_cygwin()): # Some ocamlmklib's don't like -g; observed on cygwin, but may be others as well. OCAMLMKLIB += ' -g' z3mls = os.path.join(self.sub_dir, 'z3ml') out.write('%s.cma: %s %s %s\n' % (z3mls, cmos, stubso, z3linkdep)) out.write('\t%s -o %s -I %s %s %s %s\n' % (OCAMLMKLIB, z3mls, self.sub_dir, stubso, cmos, LIBZ3)) out.write('%s.cmxa: %s %s %s %s.cma\n' % (z3mls, cmxs, stubso, z3linkdep, z3mls)) out.write('\t%s -o %s -I %s %s %s %s\n' % (OCAMLMKLIB, z3mls, self.sub_dir, stubso, cmxs, LIBZ3)) out.write('%s.cmxs: %s.cmxa\n' % (z3mls, z3mls)) out.write('\t%s -linkall -shared -o %s.cmxs -I . -I %s %s.cmxa\n' % (OCAMLOPTF, z3mls, self.sub_dir, z3mls)) out.write('\n') out.write('ml: %s.cma %s.cmxa %s.cmxs\n' % (z3mls, z3mls, z3mls)) out.write('\n') if IS_WINDOWS: out.write('ocamlfind_install: ') self.mk_install_deps(out) out.write('\n') self.mk_install(out) out.write('\n') out.write('ocamlfind_uninstall:\n') self.mk_uninstall(out) out.write('\n') def mk_install_deps(self, out): if is_ml_enabled() and self._install_bindings(): out.write(get_component(Z3_DLL_COMPONENT).dll_name + '$(SO_EXT) ') out.write(os.path.join(self.sub_dir, 'META ')) out.write(os.path.join(self.sub_dir, 'z3ml.cma ')) out.write(os.path.join(self.sub_dir, 'z3ml.cmxa ')) out.write(os.path.join(self.sub_dir, 'z3ml.cmxs ')) def mk_install(self, out): if is_ml_enabled() and self._install_bindings(): self._init_ocamlfind_paths() in_prefix = self.destdir.startswith(PREFIX) maybe_stripped_destdir = strip_path_prefix(self.destdir, PREFIX) # Note that when doing a staged install with DESTDIR that modifying # OCaml's ``ld.conf`` may fail. Therefore packagers will need to # make their packages modify it manually at package install time # as opposed to ``make install`` time. MakeRuleCmd.make_install_directory(out, maybe_stripped_destdir, in_prefix=in_prefix) out.write('\t@{ocamlfind} install -ldconf $(DESTDIR){ldconf} -destdir $(DESTDIR){ocaml_destdir} Z3 {metafile}'.format( ldconf=self.ldconf, ocamlfind=OCAMLFIND, ocaml_destdir=self.destdir, metafile=os.path.join(self.sub_dir, 'META'))) for m in self.modules: mli = os.path.join(self.src_dir, m) + '.mli' if os.path.exists(mli): out.write(' ' + os.path.join(self.to_src_dir, m) + '.mli') else: out.write(' ' + os.path.join(self.sub_dir, m) + '.mli') out.write(' ' + os.path.join(self.sub_dir, m) + '.cmi') out.write(' ' + os.path.join(self.sub_dir, m) + '.cmx') out.write(' %s' % ((os.path.join(self.sub_dir, 'libz3ml$(LIB_EXT)')))) out.write(' %s' % ((os.path.join(self.sub_dir, 'z3ml$(LIB_EXT)')))) out.write(' %s' % ((os.path.join(self.sub_dir, 'z3ml.cma')))) out.write(' %s' % ((os.path.join(self.sub_dir, 'z3ml.cmxa')))) out.write(' %s' % ((os.path.join(self.sub_dir, 'z3ml.cmxs')))) out.write(' %s' % ((os.path.join(self.sub_dir, 'dllz3ml')))) if is_windows() or is_cygwin_mingw(): out.write('.dll') else: out.write('.so') # .so also on OSX! out.write('\n') def mk_uninstall(self, out): if is_ml_enabled() and self._install_bindings(): self._init_ocamlfind_paths() out.write('\t@{ocamlfind} remove -ldconf $(DESTDIR){ldconf} -destdir $(DESTDIR){ocaml_destdir} Z3\n'.format( ldconf=self.ldconf, ocamlfind=OCAMLFIND, ocaml_destdir=self.destdir)) def main_component(self): return is_ml_enabled() class ExampleComponent(Component): def __init__(self, name, path): Component.__init__(self, name, path, []) self.ex_dir = os.path.join(EXAMPLE_DIR, self.path) self.to_ex_dir = os.path.join(REV_BUILD_DIR, self.ex_dir) def is_example(self): return True class CppExampleComponent(ExampleComponent): def __init__(self, name, path): ExampleComponent.__init__(self, name, path) def compiler(self): return "$(CXX)" def src_files(self): return get_cpp_files(self.ex_dir) def mk_makefile(self, out): dll_name = get_component(Z3_DLL_COMPONENT).dll_name dll = '%s$(SO_EXT)' % dll_name objfiles = '' for cppfile in self.src_files(): objfile = '%s$(OBJ_EXT)' % (cppfile[:cppfile.rfind('.')]) objfiles = objfiles + ('%s ' % objfile) out.write('%s: %s\n' % (objfile, os.path.join(self.to_ex_dir, cppfile))); out.write('\t%s $(CXXFLAGS) $(OS_DEFINES) $(EXAMP_DEBUG_FLAG) $(CXX_OUT_FLAG)%s $(LINK_FLAGS)' % (self.compiler(), objfile)) # Add include dir components out.write(' -I%s' % get_component(API_COMPONENT).to_src_dir) out.write(' -I%s' % get_component(CPP_COMPONENT).to_src_dir) out.write(' %s' % os.path.join(self.to_ex_dir, cppfile)) out.write('\n') exefile = '%s$(EXE_EXT)' % self.name out.write('%s: %s %s\n' % (exefile, dll, objfiles)) out.write('\t$(LINK) $(LINK_OUT_FLAG)%s $(LINK_FLAGS) %s ' % (exefile, objfiles)) if IS_WINDOWS: out.write('%s.lib' % dll_name) else: out.write(dll) out.write(' $(LINK_EXTRA_FLAGS)\n') out.write('_ex_%s: %s\n\n' % (self.name, exefile)) class CExampleComponent(CppExampleComponent): def __init__(self, name, path): CppExampleComponent.__init__(self, name, path) def compiler(self): return "$(CC)" def src_files(self): return get_c_files(self.ex_dir) def mk_makefile(self, out): dll_name = get_component(Z3_DLL_COMPONENT).dll_name dll = '%s$(SO_EXT)' % dll_name objfiles = '' for cfile in self.src_files(): objfile = '%s$(OBJ_EXT)' % (cfile[:cfile.rfind('.')]) objfiles = objfiles + ('%s ' % objfile) out.write('%s: %s\n' % (objfile, os.path.join(self.to_ex_dir, cfile))); out.write('\t%s $(CFLAGS) $(OS_DEFINES) $(EXAMP_DEBUG_FLAG) $(C_OUT_FLAG)%s $(LINK_FLAGS)' % (self.compiler(), objfile)) out.write(' -I%s' % get_component(API_COMPONENT).to_src_dir) out.write(' %s' % os.path.join(self.to_ex_dir, cfile)) out.write('\n') exefile = '%s$(EXE_EXT)' % self.name out.write('%s: %s %s\n' % (exefile, dll, objfiles)) out.write('\t$(LINK) $(LINK_OUT_FLAG)%s $(LINK_FLAGS) %s ' % (exefile, objfiles)) if IS_WINDOWS: out.write('%s.lib' % dll_name) else: out.write(dll) out.write(' $(LINK_EXTRA_FLAGS)\n') out.write('_ex_%s: %s\n\n' % (self.name, exefile)) class DotNetExampleComponent(ExampleComponent): def __init__(self, name, path): ExampleComponent.__init__(self, name, path) def is_example(self): return is_dotnet_core_enabled() def mk_makefile(self, out): if is_dotnet_core_enabled(): proj_name = 'dotnet_example.csproj' out.write('_ex_%s:' % self.name) for csfile in get_cs_files(self.ex_dir): out.write(' ') out.write(os.path.join(self.to_ex_dir, csfile)) mk_dir(os.path.join(BUILD_DIR, 'dotnet_example')) csproj = os.path.join('dotnet_example', proj_name) if VS_X64: platform = 'x64' elif VS_ARM: platform = 'ARM' else: platform = 'x86' dotnet_proj_str = """ Exe netcoreapp2.0 %s ..\Microsoft.Z3.dll """ % (platform, self.to_ex_dir) with open(os.path.join(BUILD_DIR, csproj), 'w') as ous: ous.write(dotnet_proj_str) out.write('\n') dotnetCmdLine = [DOTNET, "build", csproj] dotnetCmdLine.extend(['-c']) if DEBUG_MODE: dotnetCmdLine.extend(['Debug']) else: dotnetCmdLine.extend(['Release']) MakeRuleCmd.write_cmd(out, ' '.join(dotnetCmdLine)) out.write('\n') class JavaExampleComponent(ExampleComponent): def __init__(self, name, path): ExampleComponent.__init__(self, name, path) def is_example(self): return JAVA_ENABLED def mk_makefile(self, out): if JAVA_ENABLED: pkg = get_component(JAVA_COMPONENT).package_name + '.jar' out.write('JavaExample.class: %s' % (pkg)) deps = '' for jfile in get_java_files(self.ex_dir): out.write(' %s' % os.path.join(self.to_ex_dir, jfile)) if IS_WINDOWS: deps = deps.replace('/', '\\') out.write('%s\n' % deps) out.write('\t%s -cp %s ' % (JAVAC, pkg)) win_ex_dir = self.to_ex_dir for javafile in get_java_files(self.ex_dir): out.write(' ') out.write(os.path.join(win_ex_dir, javafile)) out.write(' -d .\n') out.write('_ex_%s: JavaExample.class\n\n' % (self.name)) class MLExampleComponent(ExampleComponent): def __init__(self, name, path): ExampleComponent.__init__(self, name, path) def is_example(self): return ML_ENABLED def mk_makefile(self, out): if ML_ENABLED: out.write('ml_example.byte: api/ml/z3ml.cma') for mlfile in get_ml_files(self.ex_dir): out.write(' %s' % os.path.join(self.to_ex_dir, mlfile)) out.write('\n') out.write('\t%s ' % OCAMLC) if DEBUG_MODE: out.write('-g ') out.write('-custom -o ml_example.byte -I -zarith -I api/ml -cclib "-L. -lz3" zarith.cma z3ml.cma') for mlfile in get_ml_files(self.ex_dir): out.write(' %s/%s' % (self.to_ex_dir, mlfile)) out.write('\n') out.write('ml_example$(EXE_EXT): api/ml/z3ml.cmxa') for mlfile in get_ml_files(self.ex_dir): out.write(' %s' % os.path.join(self.to_ex_dir, mlfile)) out.write('\n') out.write('\t%s ' % OCAMLOPT) if DEBUG_MODE: out.write('-g ') out.write('-o ml_example$(EXE_EXT) -I -zarith -I api/ml -cclib "-L. -lz3" zarith.cmxa z3ml.cmxa') for mlfile in get_ml_files(self.ex_dir): out.write(' %s/%s' % (self.to_ex_dir, mlfile)) out.write('\n') out.write('_ex_%s: ml_example.byte ml_example$(EXE_EXT)\n\n' % self.name) class PythonExampleComponent(ExampleComponent): def __init__(self, name, path): ExampleComponent.__init__(self, name, path) # Python examples are just placeholders, we just copy the *.py files when mk_makefile is invoked. # We don't need to include them in the :examples rule def mk_makefile(self, out): full = os.path.join(EXAMPLE_DIR, self.path) for py in filter(lambda f: f.endswith('.py'), os.listdir(full)): shutil.copyfile(os.path.join(full, py), os.path.join(BUILD_DIR, 'python', py)) if is_verbose(): print("Copied Z3Py example '%s' to '%s'" % (py, os.path.join(BUILD_DIR, 'python'))) out.write('_ex_%s: \n\n' % self.name) def mk_win_dist(self, build_path, dist_path): full = os.path.join(EXAMPLE_DIR, self.path) py = 'example.py' shutil.copyfile(os.path.join(full, py), os.path.join(dist_path, INSTALL_BIN_DIR, 'python', py)) def mk_unix_dist(self, build_path, dist_path): self.mk_win_dist(build_path, dist_path) def reg_component(name, c): global _Id, _Components, _ComponentNames, _Name2Component c.id = _Id _Id = _Id + 1 _Components.append(c) _ComponentNames.add(name) _Name2Component[name] = c if VERBOSE: print("New component: '%s'" % name) def add_lib(name, deps=[], path=None, includes2install=[]): c = LibComponent(name, path, deps, includes2install) reg_component(name, c) def add_hlib(name, path=None, includes2install=[]): c = HLibComponent(name, path, includes2install) reg_component(name, c) def add_exe(name, deps=[], path=None, exe_name=None, install=True): c = ExeComponent(name, exe_name, path, deps, install) reg_component(name, c) def add_extra_exe(name, deps=[], path=None, exe_name=None, install=True): c = ExtraExeComponent(name, exe_name, path, deps, install) reg_component(name, c) def add_dll(name, deps=[], path=None, dll_name=None, export_files=[], reexports=[], install=True, static=False, staging_link=None): c = DLLComponent(name, dll_name, path, deps, export_files, reexports, install, static, staging_link) reg_component(name, c) return c def add_dot_net_core_dll(name, deps=[], path=None, dll_name=None, assembly_info_dir=None, default_key_file=None): c = DotNetDLLComponent(name, dll_name, path, deps, assembly_info_dir, default_key_file) reg_component(name, c) def add_java_dll(name, deps=[], path=None, dll_name=None, package_name=None, manifest_file=None): c = JavaDLLComponent(name, dll_name, package_name, manifest_file, path, deps) reg_component(name, c) def add_python(libz3Component): name = 'python' reg_component(name, PythonComponent(name, libz3Component)) def add_js(): reg_component('js', JsComponent()) def add_python_install(libz3Component): name = 'python_install' reg_component(name, PythonInstallComponent(name, libz3Component)) def add_ml_lib(name, deps=[], path=None, lib_name=None): c = MLComponent(name, lib_name, path, deps) reg_component(name, c) def add_cpp_example(name, path=None): c = CppExampleComponent(name, path) reg_component(name, c) def add_c_example(name, path=None): c = CExampleComponent(name, path) reg_component(name, c) def add_dotnet_example(name, path=None): c = DotNetExampleComponent(name, path) reg_component(name, c) def add_java_example(name, path=None): c = JavaExampleComponent(name, path) reg_component(name, c) def add_ml_example(name, path=None): c = MLExampleComponent(name, path) reg_component(name, c) def add_z3py_example(name, path=None): c = PythonExampleComponent(name, path) reg_component(name, c) def mk_config(): if ONLY_MAKEFILES: return config = open(os.path.join(BUILD_DIR, 'config.mk'), 'w') global CXX, CC, GMP, CPPFLAGS, CXXFLAGS, LDFLAGS, EXAMP_DEBUG_FLAG, FPMATH_FLAGS, LOG_SYNC, SINGLE_THREADED if IS_WINDOWS: config.write( 'CC=cl\n' 'CXX=cl\n' 'CXX_OUT_FLAG=/Fo\n' 'C_OUT_FLAG=/Fo\n' 'OBJ_EXT=.obj\n' 'LIB_EXT=.lib\n' 'AR=lib\n' 'AR_OUTFLAG=/OUT:\n' 'EXE_EXT=.exe\n' 'LINK=cl\n' 'LINK_OUT_FLAG=/Fe\n' 'SO_EXT=.dll\n' 'SLINK=cl\n' 'SLINK_OUT_FLAG=/Fe\n' 'OS_DEFINES=/D _WINDOWS\n') extra_opt = '' link_extra_opt = '' if LOG_SYNC: extra_opt = '%s /DZ3_LOG_SYNC' % extra_opt if SINGLE_THREADED: extra_opt = '%s /DSINGLE_THREAD' % extra_opt if GIT_HASH: extra_opt = ' %s /D Z3GITHASH=%s' % (extra_opt, GIT_HASH) if GUARD_CF: extra_opt = ' %s /guard:cf' % extra_opt link_extra_opt = ' %s /GUARD:CF' % link_extra_opt if STATIC_BIN: static_opt = '/MT' else: static_opt = '/MD' maybe_disable_dynamic_base = '/DYNAMICBASE' if ALWAYS_DYNAMIC_BASE else '/DYNAMICBASE:NO' if DEBUG_MODE: static_opt = static_opt + 'd' config.write( 'AR_FLAGS=/nologo\n' 'LINK_FLAGS=/nologo %s\n' 'SLINK_FLAGS=/nologo /LDd\n' % static_opt) if VS_X64: config.write( 'CXXFLAGS=/c /Zi /nologo /W3 /WX- /Od /Oy- /D WIN32 /D _DEBUG /D Z3DEBUG /D _CONSOLE /D _TRACE /D _WINDOWS /Gm- /EHsc /RTC1 /GS /Gd %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)) elif VS_ARM: print("ARM on VS is unsupported") exit(1) else: config.write( 'CXXFLAGS=/c /Zi /nologo /W3 /WX- /Od /Oy- /D WIN32 /D _DEBUG /D Z3DEBUG /D _CONSOLE /D _TRACE /D _WINDOWS /Gm- /EHsc /RTC1 /GS /Gd /arch:SSE2 %s %s\n' % (extra_opt, static_opt)) config.write( 'LINK_EXTRA_FLAGS=/link /DEBUG /MACHINE:X86 /SUBSYSTEM:CONSOLE /INCREMENTAL:NO /STACK:8388608 /OPT:REF /OPT:ICF /TLBID:1 /DYNAMICBASE /NXCOMPAT %s\n' 'SLINK_EXTRA_FLAGS=/link /DEBUG /MACHINE:X86 /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)) else: # Windows Release mode LTCG=' /LTCG' if SLOW_OPTIMIZE else '' GL = ' /GL' if SLOW_OPTIMIZE else '' config.write( 'AR_FLAGS=/nologo %s\n' 'LINK_FLAGS=/nologo %s\n' 'SLINK_FLAGS=/nologo /LD\n' % (LTCG, static_opt)) if TRACE: 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 _UNICODE /D UNICODE /Gm- /EHsc /GS /Gd /GF /Gy /TP %s %s\n' % (GL, extra_opt, static_opt)) config.write( 'LINK_EXTRA_FLAGS=/link%s /profile /MACHINE:X64 /SUBSYSTEM:CONSOLE /STACK:8388608 %s\n' 'SLINK_EXTRA_FLAGS=/link%s /profile /MACHINE:X64 /SUBSYSTEM:WINDOWS /STACK:8388608 %s\n' % (LTCG, link_extra_opt, LTCG, link_extra_opt)) elif VS_ARM: print("ARM on VS is unsupported") exit(1) else: config.write( 'CXXFLAGS=/nologo /c%s /Zi /W3 /WX- /O2 /Oy- /D _EXTERNAL_RELEASE /D WIN32 /D NDEBUG /D _CONSOLE /D _WINDOWS /D ASYNC_COMMANDS /Gm- /EHsc /GS /Gd /arch:SSE2 %s %s\n' % (GL, extra_opt, static_opt)) config.write( 'LINK_EXTRA_FLAGS=/link%s /DEBUG /MACHINE:X86 /SUBSYSTEM:CONSOLE /INCREMENTAL:NO /STACK:8388608 /OPT:REF /OPT:ICF /TLBID:1 /DYNAMICBASE /NXCOMPAT %s\n' 'SLINK_EXTRA_FLAGS=/link%s /DEBUG /MACHINE:X86 /SUBSYSTEM:WINDOWS /INCREMENTAL:NO /STACK:8388608 /OPT:REF /OPT:ICF /TLBID:1 %s %s\n' % (LTCG, link_extra_opt, LTCG, maybe_disable_dynamic_base, link_extra_opt)) config.write('CFLAGS=$(CXXFLAGS)\n') # End of Windows VS config.mk if is_verbose(): print('64-bit: %s' % is64()) if is_java_enabled(): print('JNI Bindings: %s' % JNI_HOME) print('Java Compiler: %s' % JAVAC) if is_ml_enabled(): print('OCaml Compiler: %s' % OCAMLC) print('OCaml Find tool: %s' % OCAMLFIND) print('OCaml Native: %s' % OCAMLOPT) print('OCaml Library: %s' % OCAML_LIB) else: OS_DEFINES = "" ARITH = "internal" check_ar() CXX = find_cxx_compiler() CC = find_c_compiler() SLIBEXTRAFLAGS = '' # SLIBEXTRAFLAGS = '%s -Wl,-soname,libz3.so.0' % LDFLAGS EXE_EXT = '' LIB_EXT = '.a' if GPROF: CXXFLAGS = '%s -pg' % CXXFLAGS LDFLAGS = '%s -pg' % LDFLAGS if GMP: test_gmp(CXX) ARITH = "gmp" CPPFLAGS = '%s -D_MP_GMP' % CPPFLAGS LDFLAGS = '%s -lgmp' % LDFLAGS SLIBEXTRAFLAGS = '%s -lgmp' % SLIBEXTRAFLAGS else: CPPFLAGS = '%s -D_MP_INTERNAL' % CPPFLAGS if GIT_HASH: CPPFLAGS = '%s -DZ3GITHASH=%s' % (CPPFLAGS, GIT_HASH) CXXFLAGS = '%s -std=c++11' % CXXFLAGS CXXFLAGS = '%s -fvisibility=hidden -c' % CXXFLAGS FPMATH = test_fpmath(CXX) CXXFLAGS = '%s %s' % (CXXFLAGS, FPMATH_FLAGS) if LOG_SYNC: CXXFLAGS = '%s -DZ3_LOG_SYNC' % CXXFLAGS if SINGLE_THREADED: CXXFLAGS = '%s -DSINGLE_THREAD' % CXXFLAGS if DEBUG_MODE: CXXFLAGS = '%s -g -Wall' % CXXFLAGS EXAMP_DEBUG_FLAG = '-g' CPPFLAGS = '%s -DZ3DEBUG -D_DEBUG' % CPPFLAGS else: CXXFLAGS = '%s -O3' % CXXFLAGS if GPROF: CXXFLAGS += '-fomit-frame-pointer' CPPFLAGS = '%s -DNDEBUG -D_EXTERNAL_RELEASE' % CPPFLAGS if is_CXX_clangpp(): CXXFLAGS = '%s -Wno-unknown-pragmas -Wno-overloaded-virtual -Wno-unused-value' % CXXFLAGS sysname, _, _, _, machine = os.uname() if sysname == 'Darwin': SO_EXT = '.dylib' SLIBFLAGS = '-dynamiclib' elif sysname == 'Linux': CXXFLAGS = '%s -D_LINUX_' % CXXFLAGS OS_DEFINES = '-D_LINUX_' SO_EXT = '.so' SLIBFLAGS = '-shared' SLIBEXTRAFLAGS = '%s -Wl,-soname,libz3.so' % SLIBEXTRAFLAGS elif sysname == 'GNU': CXXFLAGS = '%s -D_HURD_' % CXXFLAGS OS_DEFINES = '-D_HURD_' SO_EXT = '.so' SLIBFLAGS = '-shared' elif sysname == 'FreeBSD': CXXFLAGS = '%s -D_FREEBSD_' % CXXFLAGS OS_DEFINES = '-D_FREEBSD_' SO_EXT = '.so' SLIBFLAGS = '-shared' elif sysname == 'NetBSD': CXXFLAGS = '%s -D_NETBSD_' % CXXFLAGS OS_DEFINES = '-D_NETBSD_' SO_EXT = '.so' SLIBFLAGS = '-shared' elif sysname == 'OpenBSD': CXXFLAGS = '%s -D_OPENBSD_' % CXXFLAGS OS_DEFINES = '-D_OPENBSD_' SO_EXT = '.so' SLIBFLAGS = '-shared' elif sysname.startswith('CYGWIN'): CXXFLAGS = '%s -D_CYGWIN' % CXXFLAGS OS_DEFINES = '-D_CYGWIN' SO_EXT = '.dll' SLIBFLAGS = '-shared' elif sysname.startswith('MSYS_NT') or sysname.startswith('MINGW'): CXXFLAGS = '%s -D_MINGW' % CXXFLAGS OS_DEFINES = '-D_MINGW' SO_EXT = '.dll' SLIBFLAGS = '-shared' EXE_EXT = '.exe' LIB_EXT = '.lib' else: raise MKException('Unsupported platform: %s' % sysname) if is64(): if not sysname.startswith('CYGWIN') and not sysname.startswith('MSYS') and not sysname.startswith('MINGW'): CXXFLAGS = '%s -fPIC' % CXXFLAGS if sysname == 'Linux': CPPFLAGS = '%s -D_USE_THREAD_LOCAL' % CPPFLAGS elif not LINUX_X64: CXXFLAGS = '%s -m32' % CXXFLAGS LDFLAGS = '%s -m32' % LDFLAGS SLIBFLAGS = '%s -m32' % SLIBFLAGS if TRACE or DEBUG_MODE: CPPFLAGS = '%s -D_TRACE' % CPPFLAGS if is_cygwin_mingw(): # when cross-compiling with MinGW, we need to statically link its standard libraries # and to make it create an import library. SLIBEXTRAFLAGS = '%s -static-libgcc -static-libstdc++ -Wl,--out-implib,libz3.dll.a' % SLIBEXTRAFLAGS LDFLAGS = '%s -static-libgcc -static-libstdc++' % LDFLAGS if sysname == 'Linux' and machine.startswith('armv7') or machine.startswith('armv8'): CXXFLAGS = '%s -fpic' % CXXFLAGS config.write('PREFIX=%s\n' % PREFIX) config.write('CC=%s\n' % CC) config.write('CXX=%s\n' % CXX) config.write('CXXFLAGS=%s %s\n' % (CPPFLAGS, CXXFLAGS)) config.write('CFLAGS=%s %s\n' % (CPPFLAGS, CXXFLAGS.replace('-std=c++11', ''))) config.write('EXAMP_DEBUG_FLAG=%s\n' % EXAMP_DEBUG_FLAG) config.write('CXX_OUT_FLAG=-o \n') config.write('C_OUT_FLAG=-o \n') config.write('OBJ_EXT=.o\n') config.write('LIB_EXT=%s\n' % LIB_EXT) config.write('AR=%s\n' % AR) config.write('AR_FLAGS=rcs\n') config.write('AR_OUTFLAG=\n') config.write('EXE_EXT=%s\n' % EXE_EXT) config.write('LINK=%s\n' % CXX) config.write('LINK_FLAGS=\n') config.write('LINK_OUT_FLAG=-o \n') if is_linux() and (build_static_lib() or build_static_bin()): config.write('LINK_EXTRA_FLAGS=-Wl,--whole-archive -lpthread -Wl,--no-whole-archive %s\n' % LDFLAGS) else: config.write('LINK_EXTRA_FLAGS=-lpthread %s\n' % LDFLAGS) config.write('SO_EXT=%s\n' % SO_EXT) config.write('SLINK=%s\n' % CXX) config.write('SLINK_FLAGS=%s\n' % SLIBFLAGS) config.write('SLINK_EXTRA_FLAGS=-lpthread %s\n' % SLIBEXTRAFLAGS) config.write('SLINK_OUT_FLAG=-o \n') config.write('OS_DEFINES=%s\n' % OS_DEFINES) if is_verbose(): print('Host platform: %s' % sysname) print('C++ Compiler: %s' % CXX) print('C Compiler : %s' % CC) if is_cygwin_mingw(): print('MinGW32 cross: %s' % (is_cygwin_mingw())) print('Archive Tool: %s' % AR) print('Arithmetic: %s' % ARITH) print('Prefix: %s' % PREFIX) print('64-bit: %s' % is64()) print('FP math: %s' % FPMATH) print("Python pkg dir: %s" % PYTHON_PACKAGE_DIR) if GPROF: print('gprof: enabled') print('Python version: %s' % distutils.sysconfig.get_python_version()) if is_java_enabled(): print('JNI Bindings: %s' % JNI_HOME) print('Java Compiler: %s' % JAVAC) if is_ml_enabled(): print('OCaml Compiler: %s' % OCAMLC) print('OCaml Find tool: %s' % OCAMLFIND) print('OCaml Native: %s' % OCAMLOPT) print('OCaml Library: %s' % OCAML_LIB) if is_dotnet_core_enabled(): print('C# Compiler: %s' % DOTNET) config.close() def mk_install(out): out.write('install: ') for c in get_components(): c.mk_install_deps(out) out.write(' ') out.write('\n') MakeRuleCmd.make_install_directory(out, INSTALL_BIN_DIR) MakeRuleCmd.make_install_directory(out, INSTALL_INCLUDE_DIR) MakeRuleCmd.make_install_directory(out, INSTALL_LIB_DIR) for c in get_components(): c.mk_install(out) out.write('\t@echo Z3 was successfully installed.\n') out.write('\n') def mk_uninstall(out): out.write('uninstall:\n') for c in get_components(): c.mk_uninstall(out) out.write('\t@echo Z3 was successfully uninstalled.\n') out.write('\n') # Generate the Z3 makefile def mk_makefile(): mk_dir(BUILD_DIR) mk_config() if VERBOSE: print("Writing %s" % os.path.join(BUILD_DIR, 'Makefile')) out = open(os.path.join(BUILD_DIR, 'Makefile'), 'w') out.write('# Automatically generated file.\n') out.write('include config.mk\n') # Generate :all rule out.write('all:') for c in get_components(): if c.main_component(): out.write(' %s' % c.name) out.write('\n\t@echo Z3 was successfully built.\n') out.write("\t@echo \"Z3Py scripts can already be executed in the \'%s\' directory.\"\n" % os.path.join(BUILD_DIR, 'python')) pathvar = "DYLD_LIBRARY_PATH" if IS_OSX else "PATH" if IS_WINDOWS else "LD_LIBRARY_PATH" out.write("\t@echo \"Z3Py scripts stored in arbitrary directories can be executed if the \'%s\' directory is added to the PYTHONPATH environment variable and the \'%s\' directory is added to the %s environment variable.\"\n" % (os.path.join(BUILD_DIR, 'python'), BUILD_DIR, pathvar)) if not IS_WINDOWS: out.write("\t@echo Use the following command to install Z3 at prefix $(PREFIX).\n") out.write('\t@echo " sudo make install"\n\n') # out.write("\t@echo If you are doing a staged install you can use DESTDIR.\n") # out.write('\t@echo " make DESTDIR=/some/temp/directory install"\n') # Generate :examples rule out.write('examples:') for c in get_components(): if c.is_example(): out.write(' _ex_%s' % c.name) out.write('\n\t@echo Z3 examples were successfully built.\n') # Generate components for c in get_components(): c.mk_makefile(out) # Generate install/uninstall rules if not WINDOWS if not IS_WINDOWS: mk_install(out) mk_uninstall(out) for c in get_components(): c.final_info() out.close() # Finalize if VERBOSE: print("Makefile was successfully generated.") if DEBUG_MODE: print(" compilation mode: Debug") else: print(" compilation mode: Release") if IS_WINDOWS: if VS_X64: print(" platform: x64\n") print("To build Z3, open a [Visual Studio x64 Command Prompt], then") elif VS_ARM: print(" platform: ARM\n") print("To build Z3, open a [Visual Studio ARM Command Prompt], then") else: print(" platform: x86") print("To build Z3, open a [Visual Studio Command Prompt], then") print("type 'cd %s && nmake'\n" % os.path.join(os.getcwd(), BUILD_DIR)) print('Remark: to open a Visual Studio Command Prompt, go to: "Start > All Programs > Visual Studio > Visual Studio Tools"') else: print("Type 'cd %s; make' to build Z3" % BUILD_DIR) # Generate automatically generated source code def mk_auto_src(): if not ONLY_MAKEFILES: exec_pyg_scripts() mk_pat_db() mk_all_install_tactic_cpps() mk_all_mem_initializer_cpps() mk_all_gparams_register_modules() def _execfile(file, globals=globals(), locals=locals()): if sys.version < "2.7": execfile(file, globals, locals) else: with open(file, "r") as fh: exec(fh.read()+"\n", globals, locals) # Execute python auxiliary scripts that generate extra code for Z3. def exec_pyg_scripts(): for root, dirs, files in os.walk('src'): for f in files: if f.endswith('.pyg'): script = os.path.join(root, f) generated_file = mk_genfile_common.mk_hpp_from_pyg(script, root) if is_verbose(): print("Generated '{}'".format(generated_file)) # TODO: delete after src/ast/pattern/expr_pattern_match # database.smt ==> database.h def mk_pat_db(): c = get_component(PATTERN_COMPONENT) fin = os.path.join(c.src_dir, 'database.smt2') fout = os.path.join(c.src_dir, 'database.h') mk_genfile_common.mk_pat_db_internal(fin, fout) if VERBOSE: print("Generated '{}'".format(fout)) # Update version numbers def update_version(): major = VER_MAJOR minor = VER_MINOR build = VER_BUILD revision = VER_TWEAK if major is None or minor is None or build is None or revision is None: raise MKException("set_version(major, minor, build, revision) must be used before invoking update_version()") if not ONLY_MAKEFILES: mk_version_dot_h(major, minor, build, revision) mk_all_assembly_infos(major, minor, build, revision) mk_def_files() def get_full_version_string(major, minor, build, revision): global GIT_HASH, GIT_DESCRIBE res = "Z3 %s.%s.%s.%s" % (major, minor, build, revision) if GIT_HASH: res += " " + GIT_HASH if GIT_DESCRIBE: branch = check_output(['git', 'rev-parse', '--abbrev-ref', 'HEAD']) res += " " + branch + " " + check_output(['git', 'describe']) return '"' + res + '"' # Update files with the version number def mk_version_dot_h(major, minor, build, revision): c = get_component(UTIL_COMPONENT) version_template = os.path.join(c.src_dir, 'z3_version.h.in') version_header_output = os.path.join(c.src_dir, 'z3_version.h') # Note the substitution names are what is used by the CMake # builds system. If you change these you should change them # in the CMake build too configure_file(version_template, version_header_output, { 'Z3_VERSION_MAJOR': str(major), 'Z3_VERSION_MINOR': str(minor), 'Z3_VERSION_PATCH': str(build), 'Z3_VERSION_TWEAK': str(revision), 'Z3_FULL_VERSION': get_full_version_string(major, minor, build, revision) } ) if VERBOSE: print("Generated '%s'" % version_header_output) # Generate AssemblyInfo.cs files with the right version numbers by using ``AssemblyInfo.cs.in`` files as a template def mk_all_assembly_infos(major, minor, build, revision): for c in get_components(): if c.has_assembly_info(): c.make_assembly_info(major, minor, build, revision) def get_header_files_for_components(component_src_dirs): assert isinstance(component_src_dirs, list) h_files_full_path = [] for component_src_dir in sorted(component_src_dirs): h_files = filter(lambda f: f.endswith('.h') or f.endswith('.hpp'), os.listdir(component_src_dir)) h_files = list(map(lambda p: os.path.join(component_src_dir, p), h_files)) h_files_full_path.extend(h_files) return h_files_full_path def mk_install_tactic_cpp(cnames, path): component_src_dirs = [] for cname in cnames: print("Component %s" % cname) c = get_component(cname) component_src_dirs.append(c.src_dir) h_files_full_path = get_header_files_for_components(component_src_dirs) generated_file = mk_genfile_common.mk_install_tactic_cpp_internal(h_files_full_path, path) if VERBOSE: print("Generated '{}'".format(generated_file)) def mk_all_install_tactic_cpps(): if not ONLY_MAKEFILES: for c in get_components(): if c.require_install_tactics(): cnames = [] cnames.extend(c.deps) cnames.append(c.name) mk_install_tactic_cpp(cnames, c.src_dir) def mk_mem_initializer_cpp(cnames, path): component_src_dirs = [] for cname in cnames: c = get_component(cname) component_src_dirs.append(c.src_dir) h_files_full_path = get_header_files_for_components(component_src_dirs) generated_file = mk_genfile_common.mk_mem_initializer_cpp_internal(h_files_full_path, path) if VERBOSE: print("Generated '{}'".format(generated_file)) def mk_all_mem_initializer_cpps(): if not ONLY_MAKEFILES: for c in get_components(): if c.require_mem_initializer(): cnames = [] cnames.extend(c.deps) cnames.append(c.name) mk_mem_initializer_cpp(cnames, c.src_dir) def mk_gparams_register_modules(cnames, path): component_src_dirs = [] for cname in cnames: c = get_component(cname) component_src_dirs.append(c.src_dir) h_files_full_path = get_header_files_for_components(component_src_dirs) generated_file = mk_genfile_common.mk_gparams_register_modules_internal(h_files_full_path, path) if VERBOSE: print("Generated '{}'".format(generated_file)) def mk_all_gparams_register_modules(): if not ONLY_MAKEFILES: for c in get_components(): if c.require_mem_initializer(): cnames = [] cnames.extend(c.deps) cnames.append(c.name) mk_gparams_register_modules(cnames, c.src_dir) # Generate a .def based on the files at c.export_files slot. def mk_def_file(c): defname = '%s.def' % os.path.join(c.src_dir, c.name) dll_name = c.dll_name export_header_files = [] for dot_h in c.export_files: dot_h_c = c.find_file(dot_h, c.name) api = os.path.join(dot_h_c.src_dir, dot_h) export_header_files.append(api) mk_genfile_common.mk_def_file_internal(defname, dll_name, export_header_files) if VERBOSE: print("Generated '%s'" % defname) def mk_def_files(): if not ONLY_MAKEFILES: for c in get_components(): if c.require_def_file(): mk_def_file(c) def cp_z3py_to_build(): mk_dir(BUILD_DIR) mk_dir(os.path.join(BUILD_DIR, 'python')) z3py_dest = os.path.join(BUILD_DIR, 'python', 'z3') z3py_src = os.path.join(Z3PY_SRC_DIR, 'z3') # Erase existing .pyc files for root, dirs, files in os.walk(Z3PY_SRC_DIR): for f in files: if f.endswith('.pyc'): rmf(os.path.join(root, f)) # Compile Z3Py files if compileall.compile_dir(z3py_src, force=1) != 1: raise MKException("failed to compile Z3Py sources") if is_verbose: print("Generated python bytecode") # Copy sources to build mk_dir(z3py_dest) for py in filter(lambda f: f.endswith('.py'), os.listdir(z3py_src)): shutil.copyfile(os.path.join(z3py_src, py), os.path.join(z3py_dest, py)) if is_verbose(): print("Copied '%s'" % py) # Python 2.x support for pyc in filter(lambda f: f.endswith('.pyc'), os.listdir(z3py_src)): shutil.copyfile(os.path.join(z3py_src, pyc), os.path.join(z3py_dest, pyc)) if is_verbose(): print("Copied '%s'" % pyc) # Python 3.x support src_pycache = os.path.join(z3py_src, '__pycache__') target_pycache = os.path.join(z3py_dest, '__pycache__') if os.path.exists(src_pycache): for pyc in filter(lambda f: f.endswith('.pyc'), os.listdir(src_pycache)): mk_dir(target_pycache) shutil.copyfile(os.path.join(src_pycache, pyc), os.path.join(target_pycache, pyc)) if is_verbose(): print("Copied '%s'" % pyc) # Copy z3test.py shutil.copyfile(os.path.join(Z3PY_SRC_DIR, 'z3test.py'), os.path.join(BUILD_DIR, 'python', 'z3test.py')) def mk_bindings(api_files): if not ONLY_MAKEFILES: mk_z3consts_py(api_files) new_api_files = [] api = get_component(API_COMPONENT) for api_file in api_files: api_file_path = api.find_file(api_file, api.name) new_api_files.append(os.path.join(api_file_path.src_dir, api_file)) g = globals() g["API_FILES"] = new_api_files if is_java_enabled(): check_java() mk_z3consts_java(api_files) # Generate some of the bindings and "api" module files import update_api dotnet_output_dir = None if is_dotnet_core_enabled(): dotnet_output_dir = os.path.join(BUILD_DIR, 'dotnet') mk_dir(dotnet_output_dir) java_output_dir = None java_package_name = None if is_java_enabled(): java_output_dir = get_component('java').src_dir java_package_name = get_component('java').package_name ml_output_dir = None if is_ml_enabled(): ml_output_dir = get_component('ml').src_dir if is_js_enabled(): set_z3js_dir("api/js") js_output_dir = get_component('js').src_dir # Get the update_api module to do the work for us update_api.generate_files(api_files=new_api_files, api_output_dir=get_component('api').src_dir, z3py_output_dir=get_z3py_dir(), dotnet_output_dir=dotnet_output_dir, java_output_dir=java_output_dir, java_package_name=java_package_name, js_output_dir=get_z3js_dir(), ml_output_dir=ml_output_dir, ml_src_dir=ml_output_dir ) cp_z3py_to_build() if is_ml_enabled(): check_ml() mk_z3consts_ml(api_files) if is_dotnet_core_enabled(): check_dotnet_core() mk_z3consts_dotnet(api_files, dotnet_output_dir) # Extract enumeration types from API files, and add python definitions. def mk_z3consts_py(api_files): if Z3PY_SRC_DIR is None: raise MKException("You must invoke set_z3py_dir(path):") full_path_api_files = [] api_dll = get_component(Z3_DLL_COMPONENT) for api_file in api_files: api_file_c = api_dll.find_file(api_file, api_dll.name) api_file = os.path.join(api_file_c.src_dir, api_file) full_path_api_files.append(api_file) generated_file = mk_genfile_common.mk_z3consts_py_internal(full_path_api_files, Z3PY_SRC_DIR) if VERBOSE: print("Generated '{}".format(generated_file)) # Extract enumeration types from z3_api.h, and add .Net definitions def mk_z3consts_dotnet(api_files, output_dir): dotnet = get_component(DOTNET_COMPONENT) if not dotnet: dotnet = get_component(DOTNET_CORE_COMPONENT) full_path_api_files = [] for api_file in api_files: api_file_c = dotnet.find_file(api_file, dotnet.name) api_file = os.path.join(api_file_c.src_dir, api_file) full_path_api_files.append(api_file) generated_file = mk_genfile_common.mk_z3consts_dotnet_internal(full_path_api_files, output_dir) if VERBOSE: print("Generated '{}".format(generated_file)) # Extract enumeration types from z3_api.h, and add Java definitions def mk_z3consts_java(api_files): java = get_component(JAVA_COMPONENT) full_path_api_files = [] for api_file in api_files: api_file_c = java.find_file(api_file, java.name) api_file = os.path.join(api_file_c.src_dir, api_file) full_path_api_files.append(api_file) generated_files = mk_genfile_common.mk_z3consts_java_internal( full_path_api_files, java.package_name, java.src_dir) if VERBOSE: for generated_file in generated_files: print("Generated '{}'".format(generated_file)) # Extract enumeration types from z3_api.h, and add ML definitions def mk_z3consts_ml(api_files): ml = get_component(ML_COMPONENT) full_path_api_files = [] for api_file in api_files: api_file_c = ml.find_file(api_file, ml.name) api_file = os.path.join(api_file_c.src_dir, api_file) full_path_api_files.append(api_file) generated_file = mk_genfile_common.mk_z3consts_ml_internal( full_path_api_files, ml.src_dir) if VERBOSE: print ('Generated "%s"' % generated_file) def mk_gui_str(id): return '4D2F40D8-E5F9-473B-B548-%012d' % id def get_platform_toolset_str(): default = 'v110'; vstr = check_output(['msbuild', '/ver']) lines = vstr.split('\n') lline = lines[-1] tokens = lline.split('.') if len(tokens) < 2: return default else: if tokens[0] == "15": # Visual Studio 2017 reports 15.* but the PlatformToolsetVersion is 141 return "v141" else: return 'v' + tokens[0] + tokens[1] def mk_vs_proj_property_groups(f, name, target_ext, type): f.write(' \n') f.write(' \n') f.write(' Debug\n') f.write(' Win32\n') f.write(' \n') f.write(' \n') f.write(' Release\n') f.write(' Win32\n') f.write(' \n') f.write(' \n') f.write(' \n') f.write(' {%s}\n' % mk_gui_str(0)) f.write(' %s\n' % name) f.write(' Win32Proj\n') f.write(' %s\n' % get_platform_toolset_str()) f.write(' \n') f.write(' \n') f.write(' \n') f.write(' %s\n' % type) f.write(' Unicode\n') f.write(' false\n') f.write(' \n') f.write(' \n') f.write(' %s\n' % type) f.write(' Unicode\n') f.write(' false\n') f.write(' \n') f.write(' \n') f.write(' \n') f.write(' \n') f.write(' \n') f.write(' \n') f.write(' \n') f.write(' $(SolutionDir)\$(ProjectName)\$(Configuration)\\n') f.write(' %s\n' % name) f.write(' .%s\n' % target_ext) f.write(' $(SolutionDir)\$(ProjectName)\$(Configuration)\\n') f.write(' %s\n' % name) f.write(' .%s\n' % target_ext) f.write(' \n') f.write(' \n') f.write(' $(ProjectName)\$(Configuration)\\n') f.write(' \n') f.write(' \n') f.write(' $(ProjectName)\$(Configuration)\\n') f.write(' \n') def mk_vs_proj_cl_compile(f, name, components, debug): f.write(' \n') f.write(' Disabled\n') if debug: f.write(' WIN32;_DEBUG;Z3DEBUG;_TRACE;_MP_INTERNAL;_WINDOWS;%(PreprocessorDefinitions)\n') else: f.write(' WIN32;NDEBUG;_MP_INTERNAL;_WINDOWS;%(PreprocessorDefinitions)\n') if VS_PAR: f.write(' false\n') f.write(' true\n') else: f.write(' true\n') f.write(' EnableFastChecks\n') f.write(' Level3\n') if debug: f.write(' MultiThreadedDebugDLL\n') else: f.write(' MultiThreadedDLL\n') f.write(' ProgramDatabase\n') f.write(' ') deps = find_all_deps(name, components) first = True for dep in deps: if first: first = False else: f.write(';') f.write(get_component(dep).to_src_dir) f.write(';%s\n' % os.path.join(REV_BUILD_DIR, SRC_DIR)) f.write('\n') f.write(' \n') def mk_vs_proj_dep_groups(f, name, components): f.write(' \n') deps = find_all_deps(name, components) for dep in deps: dep = get_component(dep) for cpp in filter(lambda f: f.endswith('.cpp'), os.listdir(dep.src_dir)): f.write(' \n' % os.path.join(dep.to_src_dir, cpp)) f.write(' \n') def mk_vs_proj_link_exe(f, name, debug): f.write(' \n') f.write(' $(OutDir)%s.exe\n' % name) f.write(' true\n') f.write(' Console\n') f.write(' 8388608\n') f.write(' false\n') f.write(' \n') f.write(' MachineX86\n') f.write(' %(AdditionalLibraryDirectories)\n') f.write(' psapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)\n') f.write(' \n') def mk_vs_proj(name, components): if not VS_PROJ: return proj_name = '%s.vcxproj' % os.path.join(BUILD_DIR, name) modes=['Debug', 'Release'] PLATFORMS=['Win32'] f = open(proj_name, 'w') f.write('\n') f.write('\n') mk_vs_proj_property_groups(f, name, 'exe', 'Application') f.write(' \n') mk_vs_proj_cl_compile(f, name, components, debug=True) mk_vs_proj_link_exe(f, name, debug=True) f.write(' \n') f.write(' \n') mk_vs_proj_cl_compile(f, name, components, debug=False) mk_vs_proj_link_exe(f, name, debug=False) f.write(' \n') mk_vs_proj_dep_groups(f, name, components) f.write(' \n') f.write(' \n') f.write(' \n') f.write('\n') f.close() if is_verbose(): print("Generated '%s'" % proj_name) def mk_vs_proj_link_dll(f, name, debug): f.write(' \n') f.write(' $(OutDir)%s.dll\n' % name) f.write(' true\n') f.write(' Console\n') f.write(' 8388608\n') f.write(' false\n') f.write(' \n') f.write(' MachineX86\n') f.write(' %(AdditionalLibraryDirectories)\n') f.write(' psapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)\n') f.write(' %s' % os.path.join(get_component('api_dll').to_src_dir, 'api_dll.def')) f.write(' \n') def mk_vs_proj_dll(name, components): if not VS_PROJ: return proj_name = '%s.vcxproj' % os.path.join(BUILD_DIR, name) modes=['Debug', 'Release'] PLATFORMS=['Win32'] f = open(proj_name, 'w') f.write('\n') f.write('\n') mk_vs_proj_property_groups(f, name, 'dll', 'DynamicLibrary') f.write(' \n') mk_vs_proj_cl_compile(f, name, components, debug=True) mk_vs_proj_link_dll(f, name, debug=True) f.write(' \n') f.write(' \n') mk_vs_proj_cl_compile(f, name, components, debug=False) mk_vs_proj_link_dll(f, name, debug=False) f.write(' \n') mk_vs_proj_dep_groups(f, name, components) f.write(' \n') f.write(' \n') f.write(' \n') f.write('\n') f.close() if is_verbose(): print("Generated '%s'" % proj_name) def mk_win_dist(build_path, dist_path): for c in get_components(): c.mk_win_dist(build_path, dist_path) def mk_unix_dist(build_path, dist_path): for c in get_components(): c.mk_unix_dist(build_path, dist_path) # Add Z3Py to bin directory for pyc in filter(lambda f: f.endswith('.pyc') or f.endswith('.py'), os.listdir(build_path)): shutil.copy(os.path.join(build_path, pyc), os.path.join(dist_path, INSTALL_BIN_DIR, pyc)) class MakeRuleCmd(object): """ These class methods provide a convenient way to emit frequently needed commands used in Makefile rules Note that several of the method are meant for use during ``make install`` and ``make uninstall``. These methods correctly use ``$(PREFIX)`` and ``$(DESTDIR)`` and therefore are preferable to writing commands manually which can be error prone. """ @classmethod def install_root(cls): """ Returns a string that will expand to the install location when used in a makefile rule. """ # Note: DESTDIR is to support staged installs return "$(DESTDIR)$(PREFIX)/" @classmethod def _is_str(cls, obj): if sys.version_info.major > 2: # Python 3 or newer. Strings are always unicode and of type str return isinstance(obj, str) else: # Python 2. Has byte-string and unicode representation, allow both return isinstance(obj, str) or isinstance(obj, unicode) @classmethod def _install_root(cls, path, in_prefix, out, is_install=True): if not in_prefix: # The Python bindings on OSX are sometimes not installed inside the prefix. install_root = "$(DESTDIR)" action_string = 'install' if is_install else 'uninstall' cls.write_cmd(out, 'echo "WARNING: {}ing files/directories ({}) that are not in the install prefix ($(PREFIX))."'.format( action_string, path)) #print("WARNING: Generating makefile rule that {}s {} '{}' which is outside the installation prefix '{}'.".format( # action_string, 'to' if is_install else 'from', path, PREFIX)) else: # assert not os.path.isabs(path) install_root = cls.install_root() return install_root @classmethod def install_files(cls, out, src_pattern, dest, in_prefix=True): assert len(dest) > 0 assert cls._is_str(src_pattern) assert not ' ' in src_pattern assert cls._is_str(dest) assert not ' ' in dest assert not os.path.isabs(src_pattern) install_root = cls._install_root(dest, in_prefix, out) cls.write_cmd(out, "cp {src_pattern} {install_root}{dest}".format( src_pattern=src_pattern, install_root=install_root, dest=dest)) @classmethod def remove_installed_files(cls, out, pattern, in_prefix=True): assert len(pattern) > 0 assert cls._is_str(pattern) assert not ' ' in pattern install_root = cls._install_root(pattern, in_prefix, out, is_install=False) cls.write_cmd(out, "rm -f {install_root}{pattern}".format( install_root=install_root, pattern=pattern)) @classmethod def make_install_directory(cls, out, dir, in_prefix=True): assert len(dir) > 0 assert cls._is_str(dir) assert not ' ' in dir install_root = cls._install_root(dir, in_prefix, out) if is_windows(): cls.write_cmd(out, "IF NOT EXIST {dir} (mkdir {dir})".format( install_root=install_root, dir=dir)) else: cls.write_cmd(out, "mkdir -p {install_root}{dir}".format( install_root=install_root, dir=dir)) @classmethod def _is_path_prefix_of(cls, temp_path, target_as_abs): """ Returns True iff ``temp_path`` is a path prefix of ``target_as_abs`` """ assert cls._is_str(temp_path) assert cls._is_str(target_as_abs) assert len(temp_path) > 0 assert len(target_as_abs) > 0 assert os.path.isabs(temp_path) assert os.path.isabs(target_as_abs) # Need to stick extra slash in front otherwise we might think that # ``/lib`` is a prefix of ``/lib64``. Of course if ``temp_path == # '/'`` then we shouldn't else we would check if ``//`` (rather than # ``/``) is a prefix of ``/lib64``, which would fail. if len(temp_path) > 1: temp_path += os.sep return target_as_abs.startswith(temp_path) @classmethod def create_relative_symbolic_link(cls, out, target, link_name): assert cls._is_str(target) assert cls._is_str(link_name) assert len(target) > 0 assert len(link_name) > 0 assert not os.path.isabs(target) assert not os.path.isabs(link_name) # We can't test to see if link_name is a file or directory # because it may not exist yet. Instead follow the convention # that if there is a leading slash target is a directory otherwise # it's a file if link_name[-1] != '/': # link_name is a file temp_path = os.path.dirname(link_name) else: # link_name is a directory temp_path = link_name[:-1] temp_path = '/' + temp_path relative_path = "" targetAsAbs = '/' + target assert os.path.isabs(targetAsAbs) assert os.path.isabs(temp_path) # Keep walking up the directory tree until temp_path # is a prefix of targetAsAbs while not cls._is_path_prefix_of(temp_path, targetAsAbs): assert temp_path != '/' temp_path = os.path.dirname(temp_path) relative_path += '../' # Now get the path from the common prefix directory to the target target_from_prefix = targetAsAbs[len(temp_path):] relative_path += target_from_prefix # Remove any double slashes relative_path = relative_path.replace('//','/') cls.create_symbolic_link(out, relative_path, link_name) @classmethod def create_symbolic_link(cls, out, target, link_name): assert cls._is_str(target) assert cls._is_str(link_name) assert not os.path.isabs(target) cls.write_cmd(out, 'ln -s {target} {install_root}{link_name}'.format( target=target, install_root=cls.install_root(), link_name=link_name)) # TODO: Refactor all of the build system to emit commands using this # helper to simplify code. This will also let us replace ``@`` with # ``$(Verb)`` and have it set to ``@`` or empty at build time depending on # a variable (e.g. ``VERBOSE``) passed to the ``make`` invocation. This # would be very helpful for debugging. @classmethod def write_cmd(cls, out, line): out.write("\t@{}\n".format(line)) def strip_path_prefix(path, prefix): if path.startswith(prefix): stripped_path = path[len(prefix):] stripped_path.replace('//','/') if stripped_path[0] == '/': stripped_path = stripped_path[1:] assert not os.path.isabs(stripped_path) return stripped_path else: return path def configure_file(template_file_path, output_file_path, substitutions): """ Read a template file ``template_file_path``, perform substitutions found in the ``substitutions`` dictionary and write the result to the output file ``output_file_path``. The template file should contain zero or more template strings of the form ``@NAME@``. The substitutions dictionary maps old strings (without the ``@`` symbols) to their replacements. """ assert isinstance(template_file_path, str) assert isinstance(output_file_path, str) assert isinstance(substitutions, dict) assert len(template_file_path) > 0 assert len(output_file_path) > 0 print("Generating {} from {}".format(output_file_path, template_file_path)) if not os.path.exists(template_file_path): raise MKException('Could not find template file "{}"'.format(template_file_path)) # Read whole template file into string template_string = None with open(template_file_path, 'r') as f: template_string = f.read() # Do replacements for (old_string, replacement) in substitutions.items(): template_string = template_string.replace('@{}@'.format(old_string), replacement) # Write the string to the file with open(output_file_path, 'w') as f: f.write(template_string) if __name__ == '__main__': import doctest doctest.testmod() z3-z3-4.8.7/scripts/mk_win_dist.cmd000066400000000000000000000005711356505360400171150ustar00rootroot00000000000000 call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Auxiliary\Build\vcvars64.bat" python scripts\mk_win_dist.py --x64-only --dotnet-key=$(Agent.TempDirectory)\z3.snk call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Auxiliary\Build\vcvars32.bat" python scripts\mk_win_dist.py --x86-only --dotnet-key=$(Agent.TempDirectory)\z3.snk z3-z3-4.8.7/scripts/mk_win_dist.py000066400000000000000000000262111356505360400170010ustar00rootroot00000000000000############################################ # Copyright (c) 2012 Microsoft Corporation # # Scripts for automatically generating # Window distribution zip files. # # Author: Leonardo de Moura (leonardo) ############################################ import os import glob import re import getopt import sys import shutil import subprocess import zipfile from mk_exception import * from mk_project import * import mk_util BUILD_DIR='build-dist' BUILD_X64_DIR=os.path.join('build-dist', 'x64') BUILD_X86_DIR=os.path.join('build-dist', 'x86') VERBOSE=True DIST_DIR='dist' FORCE_MK=False DOTNET_CORE_ENABLED=True ESRP_SIGN=False DOTNET_KEY_FILE=None JAVA_ENABLED=True GIT_HASH=False PYTHON_ENABLED=True X86ONLY=False X64ONLY=False MAKEJOBS=getenv("MAKEJOBS", "24") def set_verbose(flag): global VERBOSE VERBOSE = flag def is_verbose(): return VERBOSE def mk_dir(d): if not os.path.exists(d): os.makedirs(d) def set_build_dir(path): global BUILD_DIR, BUILD_X86_DIR, BUILD_X64_DIR BUILD_DIR = mk_util.norm_path(path) BUILD_X86_DIR = os.path.join(path, 'x86') BUILD_X64_DIR = os.path.join(path, 'x64') mk_dir(BUILD_X86_DIR) mk_dir(BUILD_X64_DIR) def display_help(): print("mk_win_dist.py: Z3 Windows distribution generator\n") print("This script generates the zip files containing executables, dlls, header files for Windows.") print("It must be executed from the Z3 root directory.") print("\nOptions:") print(" -h, --help display this message.") print(" -s, --silent do not print verbose messages.") print(" -b , --build= subdirectory where x86 and x64 Z3 versions will be built (default: build-dist).") print(" -f, --force force script to regenerate Makefiles.") print(" --nodotnet do not include .NET bindings in the binary distribution files.") 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.") print(" --x86-only x86 dist only.") print(" --x64-only x64 dist only.") exit(0) # Parse configuration option for mk_make script def parse_options(): global FORCE_MK, JAVA_ENABLED, GIT_HASH, 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', 'silent', 'force', 'nojava', 'nodotnet', 'dotnet-key=', 'esrp', 'githash', 'nopython', 'x86-only', 'x64-only' ]) print(options) for opt, arg in options: if opt in ('-b', '--build'): if arg == 'src': raise MKException('The src directory should not be used to host the Makefile') path = arg elif opt in ('-s', '--silent'): set_verbose(False) elif opt in ('-h', '--help'): display_help() elif opt in ('-f', '--force'): FORCE_MK = True elif opt == '--nodotnet': DOTNET_CORE_ENABLED = False elif opt == '--nopython': 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': GIT_HASH = True elif opt == '--x86-only' and not X64ONLY: X86ONLY = True elif opt == '--x64-only' and not X86ONLY: X64ONLY = True else: raise MKException("Invalid command line option '%s'" % opt) set_build_dir(path) # Check whether build directory already exists or not def check_build_dir(path): return os.path.exists(path) and os.path.exists(os.path.join(path, 'Makefile')) # Create a build directory using mk_make.py def mk_build_dir(path, x64): if not check_build_dir(path) or FORCE_MK: parallel = '--parallel=' + MAKEJOBS opts = ["python", os.path.join('scripts', 'mk_make.py'), parallel, "-b", path] if DOTNET_CORE_ENABLED: opts.append('--dotnet') if not DOTNET_KEY_FILE is None: opts.append('--dotnet-key=' + DOTNET_KEY_FILE) if JAVA_ENABLED: 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') if PYTHON_ENABLED: opts.append('--python') if subprocess.call(opts) != 0: raise MKException("Failed to generate build directory at '%s'" % path) # Create build directories def mk_build_dirs(): mk_build_dir(BUILD_X86_DIR, False) mk_build_dir(BUILD_X64_DIR, True) # Check if on Visual Studio command prompt def check_vc_cmd_prompt(): try: DEVNULL = open(os.devnull, 'wb') subprocess.call(['cl'], stdout=DEVNULL, stderr=DEVNULL) except: raise MKException("You must execute the mk_win_dist.py script on a Visual Studio Command Prompt") def exec_cmds(cmds): cmd_file = 'z3_tmp.cmd' f = open(cmd_file, 'w') for cmd in cmds: f.write(cmd) f.write('\n') f.close() res = 0 try: res = subprocess.call(cmd_file, shell=True) except: res = 1 try: os.erase(cmd_file) except: pass return res # Compile Z3 (if x64 == True, then it builds it in x64 mode). def mk_z3(x64): cmds = [] if x64: cmds.append('call "%VCINSTALLDIR%vcvarsall.bat" amd64') cmds.append('cd %s' % BUILD_X64_DIR) else: cmds.append('call "%VCINSTALLDIR%vcvarsall.bat" x86') cmds.append('cd %s' % BUILD_X86_DIR) cmds.append('nmake') if exec_cmds(cmds) != 0: raise MKException("Failed to make z3, x64: %s" % x64) def mk_z3s(): mk_z3(False) mk_z3(True) def get_z3_name(x64): major, minor, build, revision = get_version() if x64: platform = "x64" else: platform = "x86" if GIT_HASH: return 'z3-%s.%s.%s.%s-%s-win' % (major, minor, build, mk_util.git_hash(), platform) else: 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 else: platform = "x86" 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 mk_util.DOTNET_CORE_ENABLED = True mk_util.DOTNET_KEY_FILE = DOTNET_KEY_FILE mk_util.JAVA_ENABLED = JAVA_ENABLED mk_util.PYTHON_ENABLED = PYTHON_ENABLED mk_win_dist(build_path, dist_path) if is_verbose(): print("Generated %s distribution folder at '%s'" % (platform, dist_path)) def mk_dist_dirs(): mk_dist_dir(False) mk_dist_dir(True) def get_dist_path(x64): return get_z3_name(x64) def mk_zip(x64): dist_path = get_dist_path(x64) old = os.getcwd() try: os.chdir(DIST_DIR) zfname = '%s.zip' % dist_path zipout = zipfile.ZipFile(zfname, 'w', zipfile.ZIP_DEFLATED) for root, dirs, files in os.walk(dist_path): for f in files: zipout.write(os.path.join(root, f)) if is_verbose(): print("Generated '%s'" % zfname) except: pass os.chdir(old) # Create a zip file for each platform def mk_zips(): mk_zip(False) mk_zip(True) VS_RUNTIME_PATS = [re.compile('vcomp.*\.dll'), re.compile('msvcp.*\.dll'), re.compile('msvcr.*\.dll')] # Copy Visual Studio Runtime libraries def cp_vs_runtime(x64): if x64: platform = "x64" else: platform = "x86" vcdir = os.environ['VCINSTALLDIR'] path = '%sredist' % vcdir vs_runtime_files = [] print("Walking %s" % path) # Everything changes with every release of VS # Prior versions of VS had DLLs under "redist\x64" # There are now several variants of redistributables # The naming convention defies my understanding so # we use a "check_root" filter to find some hopefully suitable # redistributable. def check_root(root): return platform in root and ("CRT" in root or "MP" in root) and "onecore" not in root and "debug" not in root for root, dirs, files in os.walk(path): for filename in files: if fnmatch(filename, '*.dll') and check_root(root): print("Checking %s %s" % (root, filename)) for pat in VS_RUNTIME_PATS: if pat.match(filename): fname = os.path.join(root, filename) if not os.path.isdir(fname): vs_runtime_files.append(fname) if not vs_runtime_files: raise MKException("Did not find any runtime files to include") bin_dist_path = os.path.join(DIST_DIR, get_dist_path(x64), 'bin') for f in vs_runtime_files: shutil.copy(f, bin_dist_path) if is_verbose(): print("Copied '%s' to '%s'" % (f, bin_dist_path)) def cp_vs_runtimes(): cp_vs_runtime(True) cp_vs_runtime(False) def cp_license(x64): shutil.copy("LICENSE.txt", os.path.join(DIST_DIR, get_dist_path(x64))) def cp_licenses(): cp_license(True) cp_license(False) # Entry point def main(): if os.name != 'nt': raise MKException("This script is for Windows only") parse_options() check_vc_cmd_prompt() if X86ONLY: mk_build_dir(BUILD_X86_DIR, False) mk_z3(False) init_project_def() mk_dist_dir(False) cp_license(False) cp_vs_runtime(False) mk_zip(False) elif X64ONLY: mk_build_dir(BUILD_X64_DIR, True) mk_z3(True) init_project_def() mk_dist_dir(True) cp_license(True) cp_vs_runtime(True) mk_zip(True) else: mk_build_dirs() mk_z3s() init_project_def() mk_dist_dirs() cp_licenses() cp_vs_runtimes() mk_zips() main() z3-z3-4.8.7/scripts/nightly.yaml000066400000000000000000000140551356505360400164650ustar00rootroot00000000000000variables: z3Version: '4.8.7' jobs: - job: Mac displayName: "Mac Build" pool: vmImage: "macOS-10.14" steps: - task: DownloadSecureFile@1 inputs: secureFile: 'z3.snk' - script: python scripts/mk_unix_dist.py --dotnet-key=$(Agent.TempDirectory)/z3.snk - script: git clone https://github.com/z3prover/z3test z3test - script: python z3test/scripts/test_benchmarks.py build-dist/z3 z3test/regressions/smt2 - script: cp dist/*.zip $(Build.ArtifactStagingDirectory)/. - task: PublishPipelineArtifact@0 inputs: artifactName: 'Mac' targetPath: $(Build.ArtifactStagingDirectory) - job: Ubuntu displayName: "Ubuntu build" pool: vmImage: "ubuntu-16.04" steps: - task: DownloadSecureFile@1 inputs: secureFile: 'z3.snk' - script: python scripts/mk_unix_dist.py --dotnet-key=$(Agent.TempDirectory)/z3.snk - script: git clone https://github.com/z3prover/z3test z3test - script: python z3test/scripts/test_benchmarks.py build-dist/z3 z3test/regressions/smt2 - script: cp dist/*.zip $(Build.ArtifactStagingDirectory)/. - task: PublishPipelineArtifact@0 inputs: artifactName: 'Ubuntu' targetPath: $(Build.ArtifactStagingDirectory) - job: Manylinux displayName: "Manylinux build" pool: vmImage: "ubuntu-16.04" container: "rhelmot/manylinux1_x86_64:latest" variables: python: "/opt/python/cp35-cp35m/bin/python" steps: - script: $(python) scripts/mk_unix_dist.py --nodotnet --nojava - script: git clone https://github.com/z3prover/z3test z3test - script: $(python) z3test/scripts/test_benchmarks.py build-dist/z3 z3test/regressions/smt2 - script: cp dist/*.zip $(Build.ArtifactStagingDirectory)/ - task: PublishPipelineArtifact@0 inputs: artifactName: 'Manylinux' targetPath: $(Build.ArtifactStagingDirectory) - job: Windows displayName: "Windows build" pool: vmImage: "vs2017-win2016" steps: - task: DownloadSecureFile@1 inputs: secureFile: 'z3.snk' - script: scripts\mk_win_dist.cmd # - script: git clone https://github.com/z3prover/z3test z3test # - script: python z3test/scripts/test_benchmarks.py build-dist\z3.exe z3test/regressions/smt2 - script: xcopy dist\*.zip $(Build.ArtifactStagingDirectory)\* /y - task: PublishPipelineArtifact@0 inputs: artifactName: 'Windows' targetPath: $(Build.ArtifactStagingDirectory) - job: NuGet displayName: "Create Nuget Package" dependsOn: - Mac - Ubuntu - Windows steps: - task: DownloadPipelineArtifact@0 inputs: artifactName: 'Windows' targetPath: tmp - task: DownloadPipelineArtifact@0 inputs: artifactName: 'Mac' targetPath: tmp - task: DownloadPipelineArtifact@0 inputs: artifactName: 'Ubuntu' targetPath: tmp - script: | cd scripts python mk_nuget_task.py ../tmp $(z3Version) $(Build.SourceVersion) cd .. # - task: NuGetCommand@2 # inputs: # command: pack # packagesToPack: scripts/out/*.nuspec # packDestination: $(Build.ArtifactStagingDirectory) # - task: NuGetCommand@2 # inputs: # command: 'pack' # packagesToPack: scripts/out/*.nuspec # includesymbols: true # packDestination: $(Build.ArtifactStagingDirectory) # Not available as a task? # - script: | # cd scripts # EsprClient.exe sign -a authorization.json -p policy.json -i out/nuget_sign_input.json -o out/diagnostics.json # cd .. - job: Python displayName: "Python packaging" dependsOn: - Manylinux - Windows pool: vmImage: "ubuntu-16.04" steps: - task: DownloadPipelineArtifact@0 inputs: artifactName: 'Windows' targetPath: $(Agent.TempDirectory) - task: DownloadPipelineArtifact@0 inputs: artifactName: 'Manylinux' targetPath: $(Agent.TempDirectory) - script: cd $(Agent.TempDirectory); mkdir linux-bin; cd linux-bin; unzip ../*centos*.zip - script: cd $(Agent.TempDirectory); mkdir win32-bin; cd win32-bin; unzip ../*x86-win*.zip - script: cd $(Agent.TempDirectory); mkdir win64-bin; cd win64-bin; unzip ../*x64-win*.zip - script: python -m pip install --user -U setuptools wheel - script: cd src/api/python; python setup.py sdist # take a look at this PREMIUM HACK I came up with to get around the fact that the azure variable syntax overloads the bash syntax for subshells - script: cd src/api/python; echo $(Agent.TempDirectory)/linux-bin/* | xargs printf 'PACKAGE_FROM_RELEASE=%s\n' | xargs -I '{}' env '{}' python setup.py bdist_wheel - script: cd src/api/python; echo $(Agent.TempDirectory)/win32-bin/* | xargs printf 'PACKAGE_FROM_RELEASE=%s\n' | xargs -I '{}' env '{}' python setup.py bdist_wheel - script: cd src/api/python; echo $(Agent.TempDirectory)/win64-bin/* | xargs printf 'PACKAGE_FROM_RELEASE=%s\n' | xargs -I '{}' env '{}' python setup.py bdist_wheel - task: PublishPipelineArtifact@0 inputs: artifactName: 'Python packages' targetPath: src/api/python/dist - job: Deploy displayName: "Deploy into GitHub" dependsOn: - Mac - Ubuntu - Windows steps: - task: DownloadPipelineArtifact@0 inputs: artifactName: 'Windows' targetPath: tmp - task: DownloadPipelineArtifact@0 inputs: artifactName: 'Mac' targetPath: tmp - task: DownloadPipelineArtifact@0 inputs: artifactName: 'Ubuntu' targetPath: tmp # - task: DownloadPipelineArtifact@0 # inputs: # artifactName: 'NuGet' # targetPath: tmp - task: GitHubRelease@0 inputs: gitHubConnection: Z3GitHub repositoryName: 'Z3Prover/z3' action: 'delete' # target: '$(Build.SourceVersion)' tagSource: 'manual' tag: 'Nightly' - task: GitHubRelease@0 inputs: gitHubConnection: Z3GitHub repositoryName: 'Z3Prover/z3' action: 'create' # target: '$(Build.SourceVersion)' tagSource: 'manual' tag: 'Nightly' title: 'Nightly' releaseNotesSource: 'input' releaseNotes: 'nightly build' assets: 'tmp/*' assetUploadMode: 'replace' isDraft: false isPreRelease: true # TBD: run regression tests on generated binaries. z3-z3-4.8.7/scripts/policy.json000066400000000000000000000002541356505360400163110ustar00rootroot00000000000000{ "Version": "1.0.0", "Intent": "ProductRelease", "ContentType": "Binaries", "ContentOrigin": "1stParty", "ProductState": "Next", "Audience": "ExternalBroad" } z3-z3-4.8.7/scripts/pyg2hpp.py000077500000000000000000000021111356505360400160570ustar00rootroot00000000000000#!/usr/bin/env python """ Reads a pyg file and emits the corresponding C++ header file into the specified destination directory. """ import mk_genfile_common import argparse import logging import os import sys def main(args): logging.basicConfig(level=logging.INFO) parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("pyg_file", help="pyg file") parser.add_argument("destination_dir", help="destination directory") pargs = parser.parse_args(args) if not os.path.exists(pargs.pyg_file): logging.error('"{}" does not exist'.format(pargs.pyg_file)) return 1 if not mk_genfile_common.check_dir_exists(pargs.destination_dir): return 1 pyg_full_path = os.path.abspath(pargs.pyg_file) destination_dir_full_path = os.path.abspath(pargs.destination_dir) logging.info('Using {}'.format(pyg_full_path)) output = mk_genfile_common.mk_hpp_from_pyg(pyg_full_path, destination_dir_full_path) logging.info('Generated "{}"'.format(output)) return 0 if __name__ == '__main__': sys.exit(main(sys.argv[1:])) z3-z3-4.8.7/scripts/release.yml000066400000000000000000000120011356505360400162530ustar00rootroot00000000000000jobs: - job: Mac displayName: "Mac Build" pool: vmImage: "macOS-10.14" steps: - task: DownloadSecureFile@1 inputs: secureFile: 'z3.snk' - script: python scripts/mk_unix_dist.py --dotnet-key=$(Agent.TempDirectory)/z3.snk - script: git clone https://github.com/z3prover/z3test z3test - script: python z3test/scripts/test_benchmarks.py build-dist/z3 z3test/regressions/smt2 - script: cp dist/*.zip $(Build.ArtifactStagingDirectory)/. - task: PublishPipelineArtifact@0 inputs: artifactName: 'Mac' targetPath: $(Build.ArtifactStagingDirectory) - job: Ubuntu displayName: "Ubuntu build" pool: vmImage: "ubuntu-16.04" steps: - task: DownloadSecureFile@1 inputs: secureFile: 'z3.snk' - script: python scripts/mk_unix_dist.py --dotnet-key=$(Agent.TempDirectory)/z3.snk - script: git clone https://github.com/z3prover/z3test z3test - script: python z3test/scripts/test_benchmarks.py build-dist/z3 z3test/regressions/smt2 - script: cp dist/*.zip $(Build.ArtifactStagingDirectory)/. - task: PublishPipelineArtifact@0 inputs: artifactName: 'Ubuntu' targetPath: $(Build.ArtifactStagingDirectory) - job: Manylinux displayName: "Manylinux build" pool: vmImage: "ubuntu-16.04" container: "rhelmot/manylinux1_x86_64:latest" variables: python: "/opt/python/cp35-cp35m/bin/python" steps: - script: $(python) scripts/mk_unix_dist.py --nodotnet --nojava - script: git clone https://github.com/z3prover/z3test z3test - script: $(python) z3test/scripts/test_benchmarks.py build-dist/z3 z3test/regressions/smt2 - script: cp dist/*.zip $(Build.ArtifactStagingDirectory)/ - task: PublishPipelineArtifact@0 inputs: artifactName: 'Manylinux' targetPath: $(Build.ArtifactStagingDirectory) - job: Windows displayName: "Windows build" pool: vmImage: "vs2017-win2016" steps: - task: DownloadSecureFile@1 inputs: secureFile: 'z3.snk' - script: scripts\mk_win_dist.cmd - script: xcopy dist\*.zip $(Build.ArtifactStagingDirectory)\* /y - task: PublishPipelineArtifact@0 inputs: artifactName: 'Windows' targetPath: $(Build.ArtifactStagingDirectory) - job: Python displayName: "Python packaging" dependsOn: - Manylinux - Windows pool: vmImage: "ubuntu-16.04" steps: - task: DownloadPipelineArtifact@0 inputs: artifactName: 'Windows' targetPath: $(Agent.TempDirectory) - task: DownloadPipelineArtifact@0 inputs: artifactName: 'Manylinux' targetPath: $(Agent.TempDirectory) - script: cd $(Agent.TempDirectory); mkdir linux-bin; cd linux-bin; unzip ../*centos*.zip - script: cd $(Agent.TempDirectory); mkdir win32-bin; cd win32-bin; unzip ../*x86-win*.zip - script: cd $(Agent.TempDirectory); mkdir win64-bin; cd win64-bin; unzip ../*x64-win*.zip - script: python -m pip install --user -U setuptools wheel - script: cd src/api/python; python setup.py sdist # take a look at this PREMIUM HACK I came up with to get around the fact that the azure variable syntax overloads the bash syntax for subshells - script: cd src/api/python; echo $(Agent.TempDirectory)/linux-bin/* | xargs printf 'PACKAGE_FROM_RELEASE=%s\n' | xargs -I '{}' env '{}' python setup.py bdist_wheel - script: cd src/api/python; echo $(Agent.TempDirectory)/win32-bin/* | xargs printf 'PACKAGE_FROM_RELEASE=%s\n' | xargs -I '{}' env '{}' python setup.py bdist_wheel - script: cd src/api/python; echo $(Agent.TempDirectory)/win64-bin/* | xargs printf 'PACKAGE_FROM_RELEASE=%s\n' | xargs -I '{}' env '{}' python setup.py bdist_wheel - task: PublishPipelineArtifact@0 inputs: artifactName: 'Python packages' targetPath: src/api/python/dist - job: Deploy displayName: "Deploy into GitHub and PyPI" dependsOn: - Mac - Ubuntu - Windows - Python steps: - task: DownloadPipelineArtifact@0 inputs: artifactName: 'Windows' targetPath: tmp - task: DownloadPipelineArtifact@0 inputs: artifactName: 'Mac' targetPath: tmp - task: DownloadPipelineArtifact@0 inputs: artifactName: 'Ubuntu' targetPath: tmp # TBD: build NuGet package # TBD: this script should build a specific pre-specified tag - task: GitHubRelease@0 inputs: gitHubConnection: Z3GitHub repositoryName: 'Z3Prover/z3' action: 'create' target: '$(Build.SourceVersion)' tagSource: 'manual' tag: 'z3-4.8.7' title: 'z3-4.8.7' releaseNotesSource: 'input' releaseNotes: '4.8.7 release' assets: 'tmp/*' isDraft: true isPreRelease: true - task: DownloadPipelineArtifact@0 inputs: artifactName: 'Python packages' targetPath: dist - task: DownloadSecureFile@1 name: pypirc inputs: secureFile: 'pypirc' - script: pip install --upgrade pip - script: python -m pip install --user -U setuptools importlib_metadata wheel twine # Uncomment on release: - script: python -m twine upload --config-file $(pypirc.secureFilePath) -r $(pypiReleaseServer) dist/* # TBD: run regression tests on generated binaries. z3-z3-4.8.7/scripts/test-examples-cmake.yml000066400000000000000000000005451356505360400205160ustar00rootroot00000000000000steps: - script: | set -e cd build ninja c_example ninja cpp_example ninja z3_tptp5 ninja c_maxsat_example examples/c_example_build_dir/c_example examples/cpp_example_build_dir/cpp_example examples/tptp_build_dir/z3_tptp5 -help examples/c_maxsat_example_build_dir/c_maxsat_example ../examples/maxsat/ex.smt cd .. z3-z3-4.8.7/scripts/test-java-cmake.yml000066400000000000000000000004701356505360400176160ustar00rootroot00000000000000steps: - script: | cd build mkdir -p examples/java cp ../examples/java/JavaExample.java examples/java/ javac examples/java/Javaexamplejava -classpath com.microsoft.z3.jar export LD_LIBRARY_PATH=$(pwd):${LD_LIBRARY_PATH} java -cp .:examples/java:com.microsoft.z3.jar JavaExample cd .. z3-z3-4.8.7/scripts/test-jupyter.yml000066400000000000000000000006141356505360400173210ustar00rootroot00000000000000# Need to install jupyter-nbconvert # -steps # - script: | # jupyter-nbconvert --to notebook --execute --output out.txt examples/python/tutorial/jupyter/guide.ipynb # jupyter-nbconvert --to notebook --execute --output out.txt examples/python/tutorial/jupyter/strategies.ipynb # jupyter-nbconvert --to notebook --execute --output out.txt examples/python/tutorial/jupyter/advanced.ipynb z3-z3-4.8.7/scripts/test-regressions.yml000066400000000000000000000002351356505360400201610ustar00rootroot00000000000000steps: - script: git clone https://github.com/z3prover/z3test z3test - script: python z3test/scripts/test_benchmarks.py build/z3 z3test/regressions/smt2 z3-z3-4.8.7/scripts/test-z3.yml000066400000000000000000000000731356505360400161520ustar00rootroot00000000000000steps: - script: | cd build ./test-z3 -a cd .. z3-z3-4.8.7/scripts/trackall.sh000077500000000000000000000005411356505360400162520ustar00rootroot00000000000000#!/bin/bash # Copyright (c) 2015 Microsoft Corporation # Script for "cloning" (and tracking) all branches at codeplex. # On Windows, this script must be executed in the "git Bash" console. for branch in `git branch -a | grep remotes | grep -v HEAD | grep -v master`; do git branch --track ${branch##*/} $branch done git fetch --all git pull --all z3-z3-4.8.7/scripts/update_api.py000077500000000000000000002312521356505360400166130ustar00rootroot00000000000000#!/usr/bin/env python ############################################ # Copyright (c) 2012 Microsoft Corporation # # Scripts for generating Makefiles and Visual # Studio project files. # # Author: Leonardo de Moura (leonardo) ############################################ """ This script generates the ``api_log_macros.h``, ``api_log_macros.cpp`` and ``api_commands.cpp`` files for the "api" module based on parsing several API header files. It can also optionally emit some of the files required for Z3's different language bindings. """ import mk_util import mk_exception import argparse import logging import re import os import sys ########################################################## # TODO: rewrite this file without using global variables. # This file is a big HACK. # It started as small simple script. # Now, it is too big, and is invoked from mk_make.py # ########################################################## IN = 0 OUT = 1 INOUT = 2 IN_ARRAY = 3 OUT_ARRAY = 4 INOUT_ARRAY = 5 OUT_MANAGED_ARRAY = 6 # Primitive Types VOID = 0 VOID_PTR = 1 INT = 2 UINT = 3 INT64 = 4 UINT64 = 5 STRING = 6 STRING_PTR = 7 BOOL = 8 SYMBOL = 9 PRINT_MODE = 10 ERROR_CODE = 11 DOUBLE = 12 FLOAT = 13 CHAR = 14 CHAR_PTR = 15 FIRST_OBJ_ID = 100 def is_obj(ty): return ty >= FIRST_OBJ_ID Type2Str = { VOID : 'void', VOID_PTR : 'void*', INT : 'int', UINT : 'unsigned', INT64 : 'int64_t', UINT64 : 'uint64_t', DOUBLE : 'double', FLOAT : 'float', STRING : 'Z3_string', STRING_PTR : 'Z3_string_ptr', BOOL : 'bool', SYMBOL : 'Z3_symbol', PRINT_MODE : 'Z3_ast_print_mode', ERROR_CODE : 'Z3_error_code', CHAR: 'char', CHAR_PTR: 'Z3_char_ptr' } Type2PyStr = { VOID_PTR : 'ctypes.c_void_p', INT : 'ctypes.c_int', UINT : 'ctypes.c_uint', INT64 : 'ctypes.c_longlong', UINT64 : 'ctypes.c_ulonglong', DOUBLE : 'ctypes.c_double', FLOAT : 'ctypes.c_float', STRING : 'ctypes.c_char_p', STRING_PTR : 'ctypes.POINTER(ctypes.c_char_p)', BOOL : 'ctypes.c_bool', SYMBOL : 'Symbol', PRINT_MODE : 'ctypes.c_uint', ERROR_CODE : 'ctypes.c_uint', CHAR : 'ctypes.c_char', CHAR_PTR: 'ctypes.c_char_p' } # Mapping to .NET types Type2Dotnet = { VOID : 'void', VOID_PTR : 'IntPtr', INT : 'int', UINT : 'uint', INT64 : 'Int64', UINT64 : 'UInt64', DOUBLE : 'double', FLOAT : 'float', STRING : 'string', STRING_PTR : 'byte**', BOOL : 'byte', SYMBOL : 'IntPtr', PRINT_MODE : 'uint', ERROR_CODE : 'uint', CHAR : 'char', CHAR_PTR : 'char*' } # Mapping to Java types Type2Java = { VOID : 'void', VOID_PTR : 'long', INT : 'int', UINT : 'int', INT64 : 'long', UINT64 : 'long', DOUBLE : 'double', FLOAT : 'float', STRING : 'String', STRING_PTR : 'StringPtr', BOOL : 'boolean', SYMBOL : 'long', PRINT_MODE : 'int', ERROR_CODE : 'int', CHAR : 'char', CHAR_PTR : 'long' } Type2JavaW = { VOID : 'void', VOID_PTR : 'jlong', INT : 'jint', UINT : 'jint', INT64 : 'jlong', UINT64 : 'jlong', DOUBLE : 'jdouble', FLOAT : 'jfloat', STRING : 'jstring', STRING_PTR : 'jobject', BOOL : 'jboolean', SYMBOL : 'jlong', PRINT_MODE : 'jint', ERROR_CODE : 'jint', CHAR : 'jchar', CHAR_PTR : 'jlong'} # Mapping to ML types Type2ML = { VOID : 'unit', VOID_PTR : 'VOIDP', INT : 'int', UINT : 'int', INT64 : 'int', UINT64 : 'int', DOUBLE : 'float', FLOAT : 'float', STRING : 'string', STRING_PTR : 'char**', BOOL : 'bool', SYMBOL : 'z3_symbol', PRINT_MODE : 'int', ERROR_CODE : 'int', CHAR : 'char', CHAR_PTR : 'char const*' } next_type_id = FIRST_OBJ_ID def def_Type(var, c_type, py_type): global next_type_id exec('%s = %s' % (var, next_type_id), globals()) Type2Str[next_type_id] = c_type Type2PyStr[next_type_id] = py_type next_type_id = next_type_id + 1 def def_Types(api_files): pat1 = re.compile(" *def_Type\(\'(.*)\',[^\']*\'(.*)\',[^\']*\'(.*)\'\)[ \t]*") for api_file in api_files: api = open(api_file, 'r') for line in api: m = pat1.match(line) if m: def_Type(m.group(1), m.group(2), m.group(3)) for k in Type2Str: v = Type2Str[k] if is_obj(k): Type2Dotnet[k] = v Type2ML[k] = v.lower() def type2str(ty): global Type2Str return Type2Str[ty] def type2pystr(ty): global Type2PyStr return Type2PyStr[ty] def type2dotnet(ty): global Type2Dotnet return Type2Dotnet[ty] def type2java(ty): global Type2Java if (ty >= FIRST_OBJ_ID): return 'long' else: return Type2Java[ty] def type2javaw(ty): global Type2JavaW if (ty >= FIRST_OBJ_ID): return 'jlong' else: return Type2JavaW[ty] def type2ml(ty): global Type2ML q = Type2ML[ty] if q[0:3] == 'z3_': return q[3:] else: return q; def _in(ty): return (IN, ty) def _in_array(sz, ty): return (IN_ARRAY, ty, sz) def _out(ty): return (OUT, ty) def _out_array(sz, ty): return (OUT_ARRAY, ty, sz, sz) # cap contains the position of the argument that stores the capacity of the array # sz contains the position of the output argument that stores the (real) size of the array def _out_array2(cap, sz, ty): return (OUT_ARRAY, ty, cap, sz) def _inout_array(sz, ty): return (INOUT_ARRAY, ty, sz, sz) def _out_managed_array(sz,ty): return (OUT_MANAGED_ARRAY, ty, 0, sz) def param_kind(p): return p[0] def param_type(p): return p[1] def param_array_capacity_pos(p): return p[2] def param_array_size_pos(p): return p[3] def param2str(p): if param_kind(p) == IN_ARRAY: return "%s const *" % type2str(param_type(p)) elif param_kind(p) == OUT_ARRAY or param_kind(p) == IN_ARRAY or param_kind(p) == INOUT_ARRAY: return "%s*" % type2str(param_type(p)) elif param_kind(p) == OUT: return "%s*" % type2str(param_type(p)) else: return type2str(param_type(p)) def param2dotnet(p): k = param_kind(p) if k == OUT: if param_type(p) == STRING: return "out IntPtr" else: return "[In, Out] ref %s" % type2dotnet(param_type(p)) elif k == IN_ARRAY: return "[In] %s[]" % type2dotnet(param_type(p)) elif k == INOUT_ARRAY: return "[In, Out] %s[]" % type2dotnet(param_type(p)) elif k == OUT_ARRAY: return "[Out] %s[]" % type2dotnet(param_type(p)) elif k == OUT_MANAGED_ARRAY: return "[Out] out %s[]" % type2dotnet(param_type(p)) else: return type2dotnet(param_type(p)) def param2java(p): k = param_kind(p) if k == OUT: if param_type(p) == INT or param_type(p) == UINT: return "IntPtr" elif param_type(p) == INT64 or param_type(p) == UINT64 or param_type(p) == VOID_PTR or param_type(p) >= FIRST_OBJ_ID: return "LongPtr" elif param_type(p) == STRING: return "StringPtr" else: print("ERROR: unreachable code") assert(False) exit(1) elif k == IN_ARRAY or k == INOUT_ARRAY or k == OUT_ARRAY: return "%s[]" % type2java(param_type(p)) elif k == OUT_MANAGED_ARRAY: if param_type(p) == UINT: return "UIntArrayPtr" else: return "ObjArrayPtr" else: return type2java(param_type(p)) def param2javaw(p): k = param_kind(p) if k == OUT: return "jobject" elif k == IN_ARRAY or k == INOUT_ARRAY or k == OUT_ARRAY: if param_type(p) == INT or param_type(p) == UINT or param_type(p) == BOOL: return "jintArray" else: return "jlongArray" elif k == OUT_MANAGED_ARRAY: return "jlong" else: return type2javaw(param_type(p)) def param2pystr(p): if param_kind(p) == IN_ARRAY or param_kind(p) == OUT_ARRAY or param_kind(p) == IN_ARRAY or param_kind(p) == INOUT_ARRAY or param_kind(p) == OUT: return "ctypes.POINTER(%s)" % type2pystr(param_type(p)) else: return type2pystr(param_type(p)) def param2ml(p): k = param_kind(p) if k == OUT: if param_type(p) == INT or param_type(p) == UINT or param_type(p) == BOOL or param_type(p) == INT64 or param_type(p) == UINT64: return "int" elif param_type(p) == STRING: return "string" else: return "ptr" elif k == IN_ARRAY or k == INOUT_ARRAY or k == OUT_ARRAY: return "%s list" % type2ml(param_type(p)) elif k == OUT_MANAGED_ARRAY: return "%s list" % type2ml(param_type(p)) else: return type2ml(param_type(p)) # Save name, result, params to generate wrapper _API2PY = [] def mk_py_binding(name, result, params): global core_py global _API2PY _API2PY.append((name, result, params)) if result != VOID: core_py.write("_lib.%s.restype = %s\n" % (name, type2pystr(result))) core_py.write("_lib.%s.argtypes = [" % name) first = True for p in params: if first: first = False else: core_py.write(", ") core_py.write(param2pystr(p)) core_py.write("]\n") def extra_API(name, result, params): mk_py_binding(name, result, params) reg_dotnet(name, result, params) def display_args(num): for i in range(num): if i > 0: core_py.write(", ") core_py.write("a%s" % i) def display_args_to_z3(params): i = 0 for p in params: if i > 0: core_py.write(", ") if param_type(p) == STRING: core_py.write("_to_ascii(a%s)" % i) else: core_py.write("a%s" % i) i = i + 1 NULLWrapped = [ 'Z3_mk_context', 'Z3_mk_context_rc' ] Unwrapped = [ 'Z3_del_context', 'Z3_get_error_code' ] def mk_py_wrappers(): core_py.write(""" class Elementaries: def __init__(self, f): self.f = f self.get_error_code = _lib.Z3_get_error_code self.get_error_message = _lib.Z3_get_error_msg self.OK = Z3_OK self.Exception = Z3Exception def Check(self, ctx): err = self.get_error_code(ctx) if err != self.OK: raise self.Exception(self.get_error_message(ctx, err)) def Z3_set_error_handler(ctx, hndlr, _elems=Elementaries(_lib.Z3_set_error_handler)): ceh = _error_handler_type(hndlr) _elems.f(ctx, ceh) _elems.Check(ctx) return ceh """) for sig in _API2PY: 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 _dotnet_decls = [] def reg_dotnet(name, result, params): global _dotnet_decls _dotnet_decls.append((name, result, params)) def mk_dotnet(dotnet): global Type2Str dotnet.write('// Automatically generated file\n') dotnet.write('using System;\n') dotnet.write('using System.Collections.Generic;\n') dotnet.write('using System.Text;\n') dotnet.write('using System.Runtime.InteropServices;\n\n') dotnet.write('#pragma warning disable 1591\n\n') dotnet.write('namespace Microsoft.Z3\n') dotnet.write('{\n') for k in Type2Str: v = Type2Str[k] if is_obj(k): dotnet.write(' using %s = System.IntPtr;\n' % v) dotnet.write('\n') dotnet.write(' public class Native\n') dotnet.write(' {\n\n') dotnet.write(' [UnmanagedFunctionPointer(CallingConvention.Cdecl)]\n') dotnet.write(' public delegate void Z3_error_handler(Z3_context c, Z3_error_code e);\n\n') dotnet.write(' public class LIB\n') dotnet.write(' {\n') dotnet.write(' const string Z3_DLL_NAME = \"libz3\";\n' ' \n') dotnet.write(' [DllImport(Z3_DLL_NAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]\n') dotnet.write(' public extern static void Z3_set_error_handler(Z3_context a0, Z3_error_handler a1);\n\n') for name, result, params in _dotnet_decls: dotnet.write(' [DllImport(Z3_DLL_NAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]\n') dotnet.write(' ') if result == STRING: dotnet.write('public extern static IntPtr %s(' % (name)) else: dotnet.write('public extern static %s %s(' % (type2dotnet(result), name)) first = True i = 0 for param in params: if first: first = False else: dotnet.write(', ') dotnet.write('%s a%d' % (param2dotnet(param), i)) i = i + 1 dotnet.write(');\n\n') dotnet.write(' }\n') def mk_dotnet_wrappers(dotnet): global Type2Str dotnet.write("\n") dotnet.write(" public static void Z3_set_error_handler(Z3_context a0, Z3_error_handler a1) {\n") dotnet.write(" LIB.Z3_set_error_handler(a0, a1);\n") dotnet.write(" Z3_error_code err = (Z3_error_code)LIB.Z3_get_error_code(a0);\n") dotnet.write(" if (err != Z3_error_code.Z3_OK)\n") dotnet.write(" throw new Z3Exception(Marshal.PtrToStringAnsi(LIB.Z3_get_error_msg(a0, (uint)err)));\n") dotnet.write(" }\n\n") for name, result, params in _dotnet_decls: if result == STRING: dotnet.write(' public static string %s(' % (name)) else: dotnet.write(' public static %s %s(' % (type2dotnet(result), name)) first = True i = 0 for param in params: if first: first = False else: dotnet.write(', ') dotnet.write('%s a%d' % (param2dotnet(param), i)) i = i + 1 dotnet.write(') {\n') dotnet.write(' ') if result == STRING: dotnet.write('IntPtr r = ') elif result != VOID: dotnet.write('%s r = ' % type2dotnet(result)) dotnet.write('LIB.%s(' % (name)) first = True i = 0 for param in params: if first: first = False else: dotnet.write(', ') if param_kind(param) == OUT: if param_type(param) == STRING: dotnet.write('out ') else: dotnet.write('ref ') elif param_kind(param) == OUT_MANAGED_ARRAY: dotnet.write('out ') dotnet.write('a%d' % i) i = i + 1 dotnet.write(');\n') if name not in Unwrapped: if name in NULLWrapped: dotnet.write(" if (r == IntPtr.Zero)\n") dotnet.write(" throw new Z3Exception(\"Object allocation failed.\");\n") else: if len(params) > 0 and param_type(params[0]) == CONTEXT: dotnet.write(" Z3_error_code err = (Z3_error_code)LIB.Z3_get_error_code(a0);\n") dotnet.write(" if (err != Z3_error_code.Z3_OK)\n") dotnet.write(" throw new Z3Exception(Marshal.PtrToStringAnsi(LIB.Z3_get_error_msg(a0, (uint)err)));\n") if result == STRING: dotnet.write(" return Marshal.PtrToStringAnsi(r);\n") elif result != VOID: dotnet.write(" return r;\n") dotnet.write(" }\n\n") dotnet.write(" }\n\n") dotnet.write("}\n\n") def java_method_name(name): result = '' name = name[3:] # Remove Z3_ n = len(name) i = 0 while i < n: if name[i] == '_': i = i + 1 if i < n: result += name[i].upper() else: result += name[i] i = i + 1 return result # Return the type of the java array elements def java_array_element_type(p): if param_type(p) == INT or param_type(p) == UINT or param_type(p) == BOOL: return 'jint' else: return 'jlong' def mk_java(java_dir, package_name): java_nativef = os.path.join(java_dir, 'Native.java') java_wrapperf = os.path.join(java_dir, 'Native.cpp') java_native = open(java_nativef, 'w') java_native.write('// Automatically generated file\n') java_native.write('package %s;\n' % package_name) java_native.write('import %s.enumerations.*;\n' % package_name) java_native.write('public final class Native {\n') java_native.write(' public static class IntPtr { public int value; }\n') java_native.write(' public static class LongPtr { public long value; }\n') java_native.write(' public static class StringPtr { public String value; }\n') java_native.write(' public static class ObjArrayPtr { public long[] value; }\n') java_native.write(' public static class UIntArrayPtr { public int[] value; }\n') java_native.write(' public static native void setInternalErrorHandler(long ctx);\n\n') java_native.write(' static {\n') java_native.write(' try { System.loadLibrary("z3java"); }\n') java_native.write(' catch (UnsatisfiedLinkError ex) { System.loadLibrary("libz3java"); }\n') java_native.write(' }\n') java_native.write('\n') for name, result, params in _dotnet_decls: java_native.write(' protected static native %s INTERNAL%s(' % (type2java(result), java_method_name(name))) first = True i = 0 for param in params: if first: first = False else: java_native.write(', ') java_native.write('%s a%d' % (param2java(param), i)) i = i + 1 java_native.write(');\n') java_native.write('\n\n') # Exception wrappers for name, result, params in _dotnet_decls: java_native.write(' public static %s %s(' % (type2java(result), java_method_name(name))) first = True i = 0 for param in params: if first: first = False else: java_native.write(', ') java_native.write('%s a%d' % (param2java(param), i)) i = i + 1 java_native.write(')') if (len(params) > 0 and param_type(params[0]) == CONTEXT) or name in NULLWrapped: java_native.write(' throws Z3Exception') java_native.write('\n') java_native.write(' {\n') java_native.write(' ') if result != VOID: java_native.write('%s res = ' % type2java(result)) java_native.write('INTERNAL%s(' % (java_method_name(name))) first = True i = 0 for param in params: if first: first = False else: java_native.write(', ') java_native.write('a%d' % i) i = i + 1 java_native.write(');\n') if name not in Unwrapped: if name in NULLWrapped: java_native.write(" if (res == 0)\n") java_native.write(" throw new Z3Exception(\"Object allocation failed.\");\n") else: if len(params) > 0 and param_type(params[0]) == CONTEXT: java_native.write(' Z3_error_code err = Z3_error_code.fromInt(INTERNALgetErrorCode(a0));\n') java_native.write(' if (err != Z3_error_code.Z3_OK)\n') java_native.write(' throw new Z3Exception(INTERNALgetErrorMsg(a0, err.toInt()));\n') if result != VOID: java_native.write(' return res;\n') java_native.write(' }\n\n') java_native.write('}\n') java_wrapper = open(java_wrapperf, 'w') pkg_str = package_name.replace('.', '_') java_wrapper.write('// Automatically generated file\n') java_wrapper.write('#include\n') java_wrapper.write('#include\n') java_wrapper.write('#include"z3.h"\n') java_wrapper.write('#ifdef __cplusplus\n') 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(__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') java_wrapper.write(' if (OLD != 0) jenv->ReleaseLongArrayElements(OLD, (jlong *) NEW, JNI_ABORT); \n\n') java_wrapper.write('#define GETLONGAREGION(T,OLD,Z,SZ,NEW) \\\n') java_wrapper.write(' jenv->GetLongArrayRegion(OLD,Z,(jsize)SZ,(jlong*)NEW); \n') java_wrapper.write('#define SETLONGAREGION(OLD,Z,SZ,NEW) \\\n') java_wrapper.write(' jenv->SetLongArrayRegion(OLD,Z,(jsize)SZ,(jlong*)NEW) \n\n') java_wrapper.write('#else\n\n') java_wrapper.write('#define GETLONGAELEMS(T,OLD,NEW) \\\n') java_wrapper.write(' T * NEW = 0; { \\\n') java_wrapper.write(' jlong * temp = (OLD == 0) ? 0 : jenv->GetLongArrayElements(OLD, NULL); \\\n') java_wrapper.write(' unsigned int size = (OLD == 0) ? 0 :jenv->GetArrayLength(OLD); \\\n') java_wrapper.write(' if (OLD != 0) { \\\n') java_wrapper.write(' NEW = (T*) (new int[size]); \\\n') java_wrapper.write(' for (unsigned i=0; i < size; i++) \\\n') java_wrapper.write(' NEW[i] = reinterpret_cast(temp[i]); \\\n') java_wrapper.write(' jenv->ReleaseLongArrayElements(OLD, temp, JNI_ABORT); \\\n') java_wrapper.write(' } \\\n') java_wrapper.write(' } \n\n') java_wrapper.write('#define RELEASELONGAELEMS(OLD,NEW) \\\n') java_wrapper.write(' delete [] NEW; \n\n') java_wrapper.write('#define GETLONGAREGION(T,OLD,Z,SZ,NEW) \\\n') java_wrapper.write(' { \\\n') java_wrapper.write(' jlong * temp = new jlong[SZ]; \\\n') java_wrapper.write(' jenv->GetLongArrayRegion(OLD,Z,(jsize)SZ,(jlong*)temp); \\\n') java_wrapper.write(' for (int i = 0; i < (SZ); i++) \\\n') java_wrapper.write(' NEW[i] = reinterpret_cast(temp[i]); \\\n') java_wrapper.write(' delete [] temp; \\\n') java_wrapper.write(' }\n\n') java_wrapper.write('#define SETLONGAREGION(OLD,Z,SZ,NEW) \\\n') java_wrapper.write(' { \\\n') java_wrapper.write(' jlong * temp = new jlong[SZ]; \\\n') java_wrapper.write(' for (int i = 0; i < (SZ); i++) \\\n') java_wrapper.write(' temp[i] = reinterpret_cast(NEW[i]); \\\n') java_wrapper.write(' jenv->SetLongArrayRegion(OLD,Z,(jsize)SZ,temp); \\\n') java_wrapper.write(' delete [] temp; \\\n') java_wrapper.write(' }\n\n') java_wrapper.write('#endif\n\n') java_wrapper.write('void Z3JavaErrorHandler(Z3_context c, Z3_error_code e)\n') java_wrapper.write('{\n') java_wrapper.write(' // Internal do-nothing error handler. This is required to avoid that Z3 calls exit()\n') java_wrapper.write(' // upon errors, but the actual error handling is done by throwing exceptions in the\n') java_wrapper.write(' // wrappers below.\n') java_wrapper.write('}\n\n') java_wrapper.write('DLL_VIS JNIEXPORT void JNICALL Java_%s_Native_setInternalErrorHandler(JNIEnv * jenv, jclass cls, jlong a0)\n' % pkg_str) java_wrapper.write('{\n') java_wrapper.write(' Z3_set_error_handler((Z3_context)a0, Z3JavaErrorHandler);\n') java_wrapper.write('}\n\n') java_wrapper.write('') for name, result, params in _dotnet_decls: java_wrapper.write('DLL_VIS JNIEXPORT %s JNICALL Java_%s_Native_INTERNAL%s(JNIEnv * jenv, jclass cls' % (type2javaw(result), pkg_str, java_method_name(name))) i = 0 for param in params: java_wrapper.write(', ') java_wrapper.write('%s a%d' % (param2javaw(param), i)) i = i + 1 java_wrapper.write(') {\n') # preprocess arrays, strings, in/out arguments i = 0 for param in params: k = param_kind(param) if k == OUT or k == INOUT: java_wrapper.write(' %s _a%s;\n' % (type2str(param_type(param)), i)) elif k == IN_ARRAY or k == INOUT_ARRAY: if param_type(param) == INT or param_type(param) == UINT or param_type(param) == BOOL: java_wrapper.write(' %s * _a%s = (%s*) jenv->GetIntArrayElements(a%s, NULL);\n' % (type2str(param_type(param)), i, type2str(param_type(param)), i)) else: java_wrapper.write(' GETLONGAELEMS(%s, a%s, _a%s);\n' % (type2str(param_type(param)), i, i)) elif k == OUT_ARRAY: java_wrapper.write(' %s * _a%s = (%s *) malloc(((unsigned)a%s) * sizeof(%s));\n' % (type2str(param_type(param)), i, type2str(param_type(param)), param_array_capacity_pos(param), type2str(param_type(param)))) if param_type(param) == INT or param_type(param) == UINT or param_type(param) == BOOL: java_wrapper.write(' jenv->GetIntArrayRegion(a%s, 0, (jsize)a%s, (jint*)_a%s);\n' % (i, param_array_capacity_pos(param), i)) else: java_wrapper.write(' GETLONGAREGION(%s, a%s, 0, a%s, _a%s);\n' % (type2str(param_type(param)), i, param_array_capacity_pos(param), i)) elif k == IN and param_type(param) == STRING: java_wrapper.write(' Z3_string _a%s = (Z3_string) jenv->GetStringUTFChars(a%s, NULL);\n' % (i, i)) elif k == OUT_MANAGED_ARRAY: java_wrapper.write(' %s * _a%s = 0;\n' % (type2str(param_type(param)), i)) i = i + 1 # invoke procedure java_wrapper.write(' ') if result != VOID: java_wrapper.write('%s result = ' % type2str(result)) java_wrapper.write('%s(' % name) i = 0 first = True for param in params: if first: first = False else: java_wrapper.write(', ') k = param_kind(param) if k == OUT or k == INOUT: java_wrapper.write('&_a%s' % i) elif k == OUT_ARRAY or k == IN_ARRAY or k == INOUT_ARRAY: java_wrapper.write('_a%s' % i) elif k == OUT_MANAGED_ARRAY: java_wrapper.write('&_a%s' % i) elif k == IN and param_type(param) == STRING: java_wrapper.write('_a%s' % i) else: java_wrapper.write('(%s)a%i' % (param2str(param), i)) i = i + 1 java_wrapper.write(');\n') # cleanup i = 0 for param in params: k = param_kind(param) if k == OUT_ARRAY: if param_type(param) == INT or param_type(param) == UINT or param_type(param) == BOOL: java_wrapper.write(' jenv->SetIntArrayRegion(a%s, 0, (jsize)a%s, (jint*)_a%s);\n' % (i, param_array_capacity_pos(param), i)) else: java_wrapper.write(' SETLONGAREGION(a%s, 0, a%s, _a%s);\n' % (i, param_array_capacity_pos(param), i)) java_wrapper.write(' free(_a%s);\n' % i) elif k == IN_ARRAY or k == OUT_ARRAY: if param_type(param) == INT or param_type(param) == UINT or param_type(param) == BOOL: java_wrapper.write(' jenv->ReleaseIntArrayElements(a%s, (jint*)_a%s, JNI_ABORT);\n' % (i, i)) else: java_wrapper.write(' RELEASELONGAELEMS(a%s, _a%s);\n' % (i, i)) elif k == OUT or k == INOUT: if param_type(param) == INT or param_type(param) == UINT or param_type(param) == BOOL: java_wrapper.write(' {\n') java_wrapper.write(' jclass mc = jenv->GetObjectClass(a%s);\n' % i) java_wrapper.write(' jfieldID fid = jenv->GetFieldID(mc, "value", "I");\n') java_wrapper.write(' jenv->SetIntField(a%s, fid, (jint) _a%s);\n' % (i, i)) java_wrapper.write(' }\n') else: java_wrapper.write(' {\n') java_wrapper.write(' jclass mc = jenv->GetObjectClass(a%s);\n' % i) java_wrapper.write(' jfieldID fid = jenv->GetFieldID(mc, "value", "J");\n') java_wrapper.write(' jenv->SetLongField(a%s, fid, (jlong) _a%s);\n' % (i, i)) java_wrapper.write(' }\n') elif k == OUT_MANAGED_ARRAY: java_wrapper.write(' *(jlong**)a%s = (jlong*)_a%s;\n' % (i, i)) elif k == IN and param_type(param) == STRING: java_wrapper.write(' jenv->ReleaseStringUTFChars(a%s, _a%s);\n' % (i, i)); i = i + 1 # return if result == STRING: java_wrapper.write(' return jenv->NewStringUTF(result);\n') elif result != VOID: java_wrapper.write(' return (%s) result;\n' % type2javaw(result)) java_wrapper.write('}\n') java_wrapper.write('#ifdef __cplusplus\n') java_wrapper.write('}\n') java_wrapper.write('#endif\n') if mk_util.is_verbose(): print("Generated '%s'" % java_nativef) Type2Napi = { VOID : '', VOID_PTR : '', INT : 'number', UINT : 'number', INT64 : 'number', UINT64 : 'number', DOUBLE : 'number', FLOAT : 'number', STRING : 'string', STRING_PTR : 'array', BOOL : 'number', SYMBOL : 'external', PRINT_MODE : 'number', ERROR_CODE : 'number', CHAR : 'number' } def type2napi(t): try: return Type2Napi[t] except: return "external" Type2NapiBuilder = { VOID : '', VOID_PTR : '', INT : 'int32', UINT : 'uint32', INT64 : 'int64', UINT64 : 'uint64', DOUBLE : 'double', FLOAT : 'float', STRING : 'string', STRING_PTR : 'array', BOOL : 'bool', SYMBOL : 'external', PRINT_MODE : 'int32', ERROR_CODE : 'int32', CHAR : 'char' } def type2napibuilder(t): try: return Type2NapiBuilder[t] except: return "external" def mk_js(js_output_dir): with open(os.path.join(js_output_dir, "z3.json"), 'w') as ous: ous.write("{\n") ous.write(" \"api\": [\n") for name, result, params in _dotnet_decls: ous.write(" {\n") ous.write(" \"name\": \"%s\",\n" % name) ous.write(" \"c_type\": \"%s\",\n" % Type2Str[result]) ous.write(" \"napi_type\": \"%s\",\n" % type2napi(result)) ous.write(" \"arg_list\": [") first = True for p in params: if first: first = False ous.write("\n {\n") else: ous.write(",\n {\n") t = param_type(p) k = t ous.write(" \"name\": \"%s\",\n" % "") # TBD ous.write(" \"c_type\": \"%s\",\n" % type2str(t)) ous.write(" \"napi_type\": \"%s\",\n" % type2napi(t)) ous.write(" \"napi_builder\": \"%s\"\n" % type2napibuilder(t)) ous.write( " }") ous.write("],\n") ous.write(" \"napi_builder\": \"%s\"\n" % type2napibuilder(result)) ous.write(" },\n") ous.write(" ]\n") ous.write("}\n") def mk_log_header(file, name, params): file.write("void log_%s(" % name) i = 0 for p in params: if i > 0: file.write(", ") file.write("%s a%s" % (param2str(p), i)) i = i + 1 file.write(")") def log_param(p): kind = param_kind(p) ty = param_type(p) return is_obj(ty) and (kind == OUT or kind == INOUT or kind == OUT_ARRAY or kind == INOUT_ARRAY) def log_result(result, params): for p in params: if log_param(p): return True return False def mk_log_macro(file, name, params): file.write("#define LOG_%s(" % name) i = 0 for p in params: if i > 0: file.write(", ") file.write("_ARG%s" % i) i = i + 1 file.write(") z3_log_ctx _LOG_CTX; ") auxs = set() i = 0 for p in params: if log_param(p): kind = param_kind(p) if kind == OUT_ARRAY or kind == INOUT_ARRAY: cap = param_array_capacity_pos(p) if cap not in auxs: auxs.add(cap) file.write("unsigned _Z3_UNUSED Z3ARG%s = 0; " % cap) sz = param_array_size_pos(p) if sz not in auxs: auxs.add(sz) file.write("unsigned * _Z3_UNUSED Z3ARG%s = 0; " % sz) file.write("%s _Z3_UNUSED Z3ARG%s = 0; " % (param2str(p), i)) i = i + 1 file.write("if (_LOG_CTX.enabled()) { log_%s(" % name) i = 0 for p in params: if (i > 0): file.write(', ') file.write("_ARG%s" %i) i = i + 1 file.write("); ") auxs = set() i = 0 for p in params: if log_param(p): kind = param_kind(p) if kind == OUT_ARRAY or kind == INOUT_ARRAY: cap = param_array_capacity_pos(p) if cap not in auxs: auxs.add(cap) file.write("Z3ARG%s = _ARG%s; " % (cap, cap)) sz = param_array_size_pos(p) if sz not in auxs: auxs.add(sz) file.write("Z3ARG%s = _ARG%s; " % (sz, sz)) file.write("Z3ARG%s = _ARG%s; " % (i, i)) i = i + 1 file.write("}\n") def mk_log_result_macro(file, name, result, params): file.write("#define RETURN_%s" % name) if is_obj(result): file.write("(Z3RES)") file.write(" ") file.write("if (_LOG_CTX.enabled()) { ") if is_obj(result): file.write("SetR(Z3RES); ") i = 0 for p in params: if log_param(p): kind = param_kind(p) if kind == OUT_ARRAY or kind == INOUT_ARRAY: cap = param_array_capacity_pos(p) sz = param_array_size_pos(p) if cap == sz: file.write("for (unsigned i = 0; i < Z3ARG%s; i++) { SetAO(Z3ARG%s[i], %s, i); } " % (sz, i, i)) else: file.write("for (unsigned i = 0; Z3ARG%s && i < *Z3ARG%s; i++) { SetAO(Z3ARG%s[i], %s, i); } " % (sz, sz, i, i)) if kind == OUT or kind == INOUT: file.write("SetO((Z3ARG%s == 0 ? 0 : *Z3ARG%s), %s); " % (i, i, i)) i = i + 1 file.write("} ") if is_obj(result): file.write("return Z3RES\n") else: file.write("return\n") def mk_exec_header(file, name): file.write("void exec_%s(z3_replayer & in)" % name) def error(msg): sys.stderr.write(msg) exit(-1) next_id = 0 API2Id = {} def def_API(name, result, params): global API2Id, next_id global log_h, log_c mk_py_binding(name, result, params) reg_dotnet(name, result, params) API2Id[next_id] = name mk_log_header(log_h, name, params) log_h.write(';\n') mk_log_header(log_c, name, params) log_c.write(' {\n R();\n') mk_exec_header(exe_c, name) exe_c.write(' {\n') # Create Log function & Function call i = 0 exe_c.write(" ") if is_obj(result): exe_c.write("%s result = " % type2str(result)) exe_c.write("%s(\n " % name) for p in params: kind = param_kind(p) ty = param_type(p) if (i > 0): exe_c.write(",\n ") if kind == IN: if is_obj(ty): log_c.write(" P(a%s);\n" % i) exe_c.write("reinterpret_cast<%s>(in.get_obj(%s))" % (param2str(p), i)) elif ty == STRING: log_c.write(" S(a%s);\n" % i) exe_c.write("in.get_str(%s)" % i) elif ty == SYMBOL: log_c.write(" Sy(a%s);\n" % i) exe_c.write("in.get_symbol(%s)" % i) elif ty == UINT: log_c.write(" U(a%s);\n" % i) exe_c.write("in.get_uint(%s)" % i) elif ty == UINT64: log_c.write(" U(a%s);\n" % i) exe_c.write("in.get_uint64(%s)" % i) elif ty == INT: log_c.write(" I(a%s);\n" % i) exe_c.write("in.get_int(%s)" % i) elif ty == INT64: log_c.write(" I(a%s);\n" % i) exe_c.write("in.get_int64(%s)" % i) elif ty == DOUBLE: log_c.write(" D(a%s);\n" % i) exe_c.write("in.get_double(%s)" % i) elif ty == FLOAT: log_c.write(" D(a%s);\n" % i) exe_c.write("in.get_float(%s)" % i) elif ty == BOOL: log_c.write(" I(a%s);\n" % i) exe_c.write("in.get_bool(%s)" % i) elif ty == PRINT_MODE or ty == ERROR_CODE: log_c.write(" U(static_cast(a%s));\n" % i) exe_c.write("static_cast<%s>(in.get_uint(%s))" % (type2str(ty), i)) else: error("unsupported parameter for %s, %s" % (name, p)) elif kind == INOUT: error("unsupported parameter for %s, %s" % (name, p)) elif kind == OUT: if is_obj(ty): log_c.write(" P(0);\n") exe_c.write("reinterpret_cast<%s>(in.get_obj_addr(%s))" % (param2str(p), i)) elif ty == STRING: log_c.write(" S(\"\");\n") exe_c.write("in.get_str_addr(%s)" % i) elif ty == UINT: log_c.write(" U(0);\n") exe_c.write("in.get_uint_addr(%s)" % i) elif ty == UINT64: log_c.write(" U(0);\n") exe_c.write("in.get_uint64_addr(%s)" % i) elif ty == INT: log_c.write(" I(0);\n") exe_c.write("in.get_int_addr(%s)" % i) elif ty == INT64: log_c.write(" I(0);\n") exe_c.write("in.get_int64_addr(%s)" % i) elif ty == VOID_PTR: log_c.write(" P(0);\n") exe_c.write("in.get_obj_addr(%s)" % i) else: error("unsupported parameter for %s, %s" % (name, p)) elif kind == IN_ARRAY or kind == INOUT_ARRAY: sz = param_array_capacity_pos(p) log_c.write(" for (unsigned i = 0; i < a%s; i++) { " % sz) if is_obj(ty): log_c.write("P(a%s[i]);" % i) log_c.write(" }\n") log_c.write(" Ap(a%s);\n" % sz) exe_c.write("reinterpret_cast<%s*>(in.get_obj_array(%s))" % (type2str(ty), i)) elif ty == SYMBOL: log_c.write("Sy(a%s[i]);" % i) log_c.write(" }\n") log_c.write(" Asy(a%s);\n" % sz) exe_c.write("in.get_symbol_array(%s)" % i) elif ty == UINT: log_c.write("U(a%s[i]);" % i) log_c.write(" }\n") log_c.write(" Au(a%s);\n" % sz) exe_c.write("in.get_uint_array(%s)" % i) elif ty == INT: log_c.write("I(a%s[i]);" % i) log_c.write(" }\n") log_c.write(" Ai(a%s);\n" % sz) exe_c.write("in.get_int_array(%s)" % i) elif ty == BOOL: log_c.write("U(a%s[i]);" % i) log_c.write(" }\n") log_c.write(" Au(a%s);\n" % sz) exe_c.write("in.get_bool_array(%s)" % i) else: error ("unsupported parameter for %s, %s, %s" % (ty, name, p)) elif kind == OUT_ARRAY: sz = param_array_capacity_pos(p) sz_p = params[sz] sz_p_k = param_kind(sz_p) tstr = type2str(ty) if sz_p_k == OUT or sz_p_k == INOUT: sz_e = ("(*a%s)" % sz) else: sz_e = ("a%s" % sz) log_c.write(" for (unsigned i = 0; i < %s; i++) { " % sz_e) if is_obj(ty): log_c.write("P(0);") log_c.write(" }\n") log_c.write(" Ap(%s);\n" % sz_e) exe_c.write("reinterpret_cast<%s*>(in.get_obj_array(%s))" % (tstr, i)) elif ty == UINT: log_c.write("U(0);") log_c.write(" }\n") log_c.write(" Au(%s);\n" % sz_e) exe_c.write("in.get_uint_array(%s)" % i) else: error ("unsupported parameter for %s, %s" % (name, p)) elif kind == OUT_MANAGED_ARRAY: sz = param_array_size_pos(p) sz_p = params[sz] sz_p_k = param_kind(sz_p) tstr = type2str(ty) if sz_p_k == OUT or sz_p_k == INOUT: sz_e = ("(*a%s)" % sz) else: sz_e = ("a%s" % sz) log_c.write(" for (unsigned i = 0; i < %s; i++) { " % sz_e) log_c.write("P(0);") log_c.write(" }\n") log_c.write(" Ap(%s);\n" % sz_e) exe_c.write("reinterpret_cast<%s**>(in.get_obj_array(%s))" % (tstr, i)) else: error ("unsupported parameter for %s, %s" % (name, p)) i = i + 1 log_c.write(" C(%s);\n" % next_id) exe_c.write(");\n") if is_obj(result): exe_c.write(" in.store_result(result);\n") if name == 'Z3_mk_context' or name == 'Z3_mk_context_rc': exe_c.write(" Z3_set_error_handler(result, Z3_replayer_error_handler);") log_c.write('}\n') exe_c.write('}\n') mk_log_macro(log_h, name, params) if log_result(result, params): mk_log_result_macro(log_h, name, result, params) next_id = next_id + 1 def mk_bindings(exe_c): exe_c.write("void register_z3_replayer_cmds(z3_replayer & in) {\n") for key, val in API2Id.items(): exe_c.write(" in.register_cmd(%s, exec_%s, \"%s\");\n" % (key, val, val)) exe_c.write("}\n") def ml_method_name(name): return name[3:] # Remove Z3_ def is_out_param(p): if param_kind(p) == OUT or param_kind(p) == INOUT or param_kind(p) == OUT_ARRAY or param_kind(p) == INOUT_ARRAY or param_kind(p) == OUT_MANAGED_ARRAY: return True else: return False def outparams(params): op = [] for param in params: if is_out_param(param): op.append(param) return op def is_in_param(p): if param_kind(p) == IN or param_kind(p) == INOUT or param_kind(p) == IN_ARRAY or param_kind(p) == INOUT_ARRAY: return True else: return False def inparams(params): ip = [] for param in params: if is_in_param(param): ip.append(param) return ip def is_array_param(p): if param_kind(p) == IN_ARRAY or param_kind(p) == INOUT_ARRAY or param_kind(p) == OUT_ARRAY: return True else: return False def arrayparams(params): op = [] for param in params: if is_array_param(param): op.append(param) return op def ml_plus_type(ts): if ts == 'Z3_context': return 'Z3_context_plus' elif ts == 'Z3_ast' or ts == 'Z3_sort' or ts == 'Z3_func_decl' or ts == 'Z3_app' or ts == 'Z3_pattern': return 'Z3_ast_plus' elif ts == 'Z3_symbol': return 'Z3_symbol_plus' elif ts == 'Z3_constructor': return 'Z3_constructor_plus' elif ts == 'Z3_constructor_list': return 'Z3_constructor_list_plus' elif ts == 'Z3_rcf_num': return 'Z3_rcf_num_plus' elif ts == 'Z3_params': return 'Z3_params_plus' elif ts == 'Z3_param_descrs': return 'Z3_param_descrs_plus' elif ts == 'Z3_model': return 'Z3_model_plus' elif ts == 'Z3_func_interp': return 'Z3_func_interp_plus' elif ts == 'Z3_func_entry': return 'Z3_func_entry_plus' elif ts == 'Z3_goal': return 'Z3_goal_plus' elif ts == 'Z3_tactic': return 'Z3_tactic_plus' elif ts == 'Z3_probe': return 'Z3_probe_plus' elif ts == 'Z3_apply_result': return 'Z3_apply_result_plus' elif ts == 'Z3_solver': return 'Z3_solver_plus' elif ts == 'Z3_stats': return 'Z3_stats_plus' elif ts == 'Z3_ast_vector': return 'Z3_ast_vector_plus' elif ts == 'Z3_ast_map': return 'Z3_ast_map_plus' elif ts == 'Z3_fixedpoint': return 'Z3_fixedpoint_plus' elif ts == 'Z3_optimize': return 'Z3_optimize_plus' else: return ts def ml_minus_type(ts): if ts == 'Z3_ast' or ts == 'Z3_sort' or ts == 'Z3_func_decl' or ts == 'Z3_app' or ts == 'Z3_pattern': return 'Z3_ast' if ts == 'Z3_ast_plus' or ts == 'Z3_sort_plus' or ts == 'Z3_func_decl_plus' or ts == 'Z3_app_plus' or ts == 'Z3_pattern_plus': return 'Z3_ast' elif ts == 'Z3_constructor_plus': return 'Z3_constructor' elif ts == 'Z3_constructor_list_plus': return 'Z3_constructor_list' elif ts == 'Z3_rcf_num_plus': return 'Z3_rcf_num' elif ts == 'Z3_params_plus': return 'Z3_params' elif ts == 'Z3_param_descrs_plus': return 'Z3_param_descrs' elif ts == 'Z3_model_plus': return 'Z3_model' elif ts == 'Z3_func_interp_plus': return 'Z3_func_interp' elif ts == 'Z3_func_entry_plus': return 'Z3_func_entry' elif ts == 'Z3_goal_plus': return 'Z3_goal' elif ts == 'Z3_tactic_plus': return 'Z3_tactic' elif ts == 'Z3_probe_plus': return 'Z3_probe' elif ts == 'Z3_apply_result_plus': return 'Z3_apply_result' elif ts == 'Z3_solver_plus': return 'Z3_solver' elif ts == 'Z3_stats_plus': return 'Z3_stats' elif ts == 'Z3_ast_vector_plus': return 'Z3_ast_vector' elif ts == 'Z3_ast_map_plus': return 'Z3_ast_map' elif ts == 'Z3_fixedpoint_plus': return 'Z3_fixedpoint' elif ts == 'Z3_optimize_plus': return 'Z3_optimize' else: return ts def ml_plus_type_raw(ts): if ml_has_plus_type(ts): return ml_plus_type(ts) + '_raw'; else: return ts def ml_plus_ops_type(ts): if ml_has_plus_type(ts): return ml_plus_type(ts) + '_custom_ops' else: return 'default_custom_ops' def ml_has_plus_type(ts): return ts != ml_plus_type(ts) def ml_unwrap(t, ts, s): if t == STRING: return '(' + ts + ') String_val(' + s + ')' elif t == BOOL or (type2str(t) == 'bool'): return '(' + ts + ') Bool_val(' + s + ')' elif t == INT or t == PRINT_MODE or t == ERROR_CODE: return '(' + ts + ') Int_val(' + s + ')' elif t == UINT: return '(' + ts + ') Unsigned_int_val(' + s + ')' elif t == INT64: return '(' + ts + ') Long_val(' + s + ')' elif t == UINT64: return '(' + ts + ') Unsigned_long_val(' + s + ')' elif t == DOUBLE: return '(' + ts + ') Double_val(' + s + ')' elif ml_has_plus_type(ts): pts = ml_plus_type(ts) return '(' + ts + ') ' + ml_plus_type_raw(ts) + '((' + pts + '*) Data_custom_val(' + s + '))' else: return '* ((' + ts + '*) Data_custom_val(' + s + '))' def ml_set_wrap(t, d, n): if t == VOID: return d + ' = Val_unit;' elif t == BOOL or (type2str(t) == 'bool'): return d + ' = Val_bool(' + n + ');' elif t == INT or t == UINT or t == PRINT_MODE or t == ERROR_CODE: return d + ' = Val_int(' + n + ');' elif t == INT64 or t == UINT64: return d + ' = Val_long(' + n + ');' elif t == DOUBLE: return d + '= caml_copy_double(' + n + ');' elif t == STRING: return d + ' = caml_copy_string((const char*) ' + n + ');' else: pts = ml_plus_type(type2str(t)) return '*(' + pts + '*)Data_custom_val(' + d + ') = ' + n + ';' def ml_alloc_and_store(t, lhs, rhs): if t == VOID or t == BOOL or t == INT or t == UINT or t == PRINT_MODE or t == ERROR_CODE or t == INT64 or t == UINT64 or t == DOUBLE or t == STRING or (type2str(t) == 'bool'): return ml_set_wrap(t, lhs, rhs) else: pts = ml_plus_type(type2str(t)) pops = ml_plus_ops_type(type2str(t)) alloc_str = '%s = caml_alloc_custom(&%s, sizeof(%s), 0, 1); ' % (lhs, pops, pts) return alloc_str + ml_set_wrap(t, lhs, rhs) def mk_ml(ml_src_dir, ml_output_dir): global Type2Str ml_nativef = os.path.join(ml_output_dir, 'z3native.ml') ml_native = open(ml_nativef, 'w') ml_native.write('(* Automatically generated file *)\n\n') ml_pref = open(os.path.join(ml_src_dir, 'z3native.ml.pre'), 'r') for s in ml_pref: ml_native.write(s); ml_pref.close() ml_native.write('\n') for name, result, params in _dotnet_decls: ml_native.write('external %s : ' % ml_method_name(name)) ip = inparams(params) op = outparams(params) if len(ip) == 0: ml_native.write(' unit -> ') for p in ip: ml_native.write('%s -> ' % param2ml(p)) if len(op) > 0: ml_native.write('(') first = True if result != VOID or len(op) == 0: ml_native.write('%s' % type2ml(result)) first = False for p in op: if first: first = False else: ml_native.write(' * ') ml_native.write('%s' % param2ml(p)) if len(op) > 0: ml_native.write(')') if len(ip) > 5: ml_native.write(' = "n_%s_bytecode" "n_%s"\n' % (ml_method_name(name), ml_method_name(name))) else: ml_native.write(' = "n_%s"\n' % ml_method_name(name)) ml_native.write('\n') # null pointer helpers for type_id in Type2Str: type_name = Type2Str[type_id] if ml_has_plus_type(type_name) and not type_name in ['Z3_context', 'Z3_sort', 'Z3_func_decl', 'Z3_app', 'Z3_pattern']: ml_name = type2ml(type_id) ml_native.write('external context_of_%s : %s -> context = "n_context_of_%s"\n' % (ml_name, ml_name, ml_name)) ml_native.write('external is_null_%s : %s -> bool = "n_is_null_%s"\n' % (ml_name, ml_name, ml_name)) ml_native.write('external mk_null_%s : context -> %s = "n_mk_null_%s"\n\n' % (ml_name, ml_name, ml_name)) ml_native.write('(**/**)\n') ml_native.close() if mk_util.is_verbose(): print ('Generated "%s"' % ml_nativef) mk_z3native_stubs_c(ml_src_dir, ml_output_dir) z3_long_funs = frozenset([ 'Z3_solver_check', 'Z3_solver_check_assumptions', 'Z3_simplify', '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') ml_wrapper.write('// Automatically generated file\n\n') ml_pref = open(os.path.join(ml_src_dir, 'z3native_stubs.c.pre'), 'r') for s in ml_pref: ml_wrapper.write(s); 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) ret_size = len(op) if result != VOID: ret_size = ret_size + 1 # Setup frame ml_wrapper.write('CAMLprim DLL_PUBLIC value n_%s(' % ml_method_name(name)) first = True i = 0 for p in params: if is_in_param(p): if first: first = False else: ml_wrapper.write(', ') ml_wrapper.write('value a%d' % i) i = i + 1 ml_wrapper.write(') {\n') ml_wrapper.write(' CAMLparam%d(' % len(ip)) i = 0 first = True for p in params: if is_in_param(p): if first: first = False else: ml_wrapper.write(', ') ml_wrapper.write('a%d' % i) i = i + 1 ml_wrapper.write(');\n') i = 0 if len(op) + len(ap) == 0: ml_wrapper.write(' CAMLlocal1(result);\n') else: c = 0 needs_tmp_value = False for p in params: if is_out_param(p) or is_array_param(p): c = c + 1 needs_tmp_value = needs_tmp_value or param_kind(p) == OUT_ARRAY or param_kind(p) == INOUT_ARRAY if needs_tmp_value: c = c + 1 if len(ap) > 0: c = c + 1 ml_wrapper.write(' CAMLlocal%s(result, z3rv_val' % (c+2)) for p in params: if is_out_param(p) or is_array_param(p): ml_wrapper.write(', _a%s_val' % i) i = i + 1 if needs_tmp_value: ml_wrapper.write(', tmp_val') if len(ap) != 0: ml_wrapper.write(', _iter'); ml_wrapper.write(');\n') if len(ap) > 0: ml_wrapper.write(' unsigned _i;\n') # determine if the function has a context as parameter. have_context = (len(params) > 0) and (param_type(params[0]) == CONTEXT) if have_context and name not in Unwrapped: ml_wrapper.write(' Z3_error_code ec;\n') if result != VOID: ts = type2str(result) if ml_has_plus_type(ts): pts = ml_plus_type(ts) ml_wrapper.write(' %s z3rv_m;\n' % ts) ml_wrapper.write(' %s z3rv;\n' % pts) else: ml_wrapper.write(' %s z3rv;\n' % ts) # declare all required local variables # To comply with C89, we need to first declare the variables and initialize them # only afterwards. i = 0 for param in params: if param_type(param) == CONTEXT and i == 0: ml_wrapper.write(' Z3_context_plus ctx_p;\n') ml_wrapper.write(' Z3_context _a0;\n') else: k = param_kind(param) if k == OUT_ARRAY: ml_wrapper.write(' %s * _a%s;\n' % (type2str(param_type(param)), i)) elif k == OUT_MANAGED_ARRAY: ml_wrapper.write(' %s * _a%s;\n' % (type2str(param_type(param)), i)) elif k == IN_ARRAY or k == INOUT_ARRAY: t = param_type(param) ts = type2str(t) ml_wrapper.write(' %s * _a%s;\n' % (ts, i)) elif k == IN: t = param_type(param) ml_wrapper.write(' %s _a%s;\n' % (type2str(t), i)) elif k == OUT or k == INOUT: t = param_type(param) ml_wrapper.write(' %s _a%s;\n' % (type2str(t), i)) ts = type2str(t) if ml_has_plus_type(ts): pts = ml_plus_type(ts) ml_wrapper.write(' %s _a%dp;\n' % (pts, i)) i = i + 1 # End of variable declarations in outermost block: # To comply with C89, no variable declarations may occur in the outermost block # from that point onwards (breaks builds with at least VC 2012 and prior) ml_wrapper.write('\n') # Declare locals, preprocess arrays, strings, in/out arguments i = 0 for param in params: if param_type(param) == CONTEXT and i == 0: ml_wrapper.write(' ctx_p = *(Z3_context_plus*) Data_custom_val(a' + str(i) + ');\n') ml_wrapper.write(' _a0 = ctx_p->ctx;\n') else: k = param_kind(param) if k == OUT_ARRAY: ml_wrapper.write(' _a%s = (%s*) malloc(sizeof(%s) * (_a%s));\n' % ( i, type2str(param_type(param)), type2str(param_type(param)), param_array_capacity_pos(param))) elif k == OUT_MANAGED_ARRAY: ml_wrapper.write(' _a%s = 0;\n' % i) elif k == IN_ARRAY or k == INOUT_ARRAY: t = param_type(param) ts = type2str(t) ml_wrapper.write(' _a%s = (%s*) malloc(sizeof(%s) * _a%s);\n' % (i, ts, ts, param_array_capacity_pos(param))) elif k == IN: t = param_type(param) ml_wrapper.write(' _a%s = %s;\n' % (i, ml_unwrap(t, type2str(t), 'a' + str(i)))) i = i + 1 i = 0 for param in params: k = param_kind(param) if k == IN_ARRAY or k == INOUT_ARRAY: t = param_type(param) ts = type2str(t) ml_wrapper.write(' _iter = a' + str(i) + ';\n') ml_wrapper.write(' for (_i = 0; _i < _a%s; _i++) {\n' % param_array_capacity_pos(param)) ml_wrapper.write(' assert(_iter != Val_emptylist);\n') ml_wrapper.write(' _a%s[_i] = %s;\n' % (i, ml_unwrap(t, ts, 'Field(_iter, 0)'))) ml_wrapper.write(' _iter = Field(_iter, 1);\n') ml_wrapper.write(' }\n') ml_wrapper.write(' assert(_iter == Val_emptylist);\n\n') i = i + 1 release_caml_gc= name in z3_long_funs if release_caml_gc: ml_wrapper.write('\n caml_release_runtime_system();\n') ml_wrapper.write('\n /* invoke Z3 function */\n ') if result != VOID: ts = type2str(result) if ml_has_plus_type(ts): ml_wrapper.write('z3rv_m = ') else: ml_wrapper.write('z3rv = ') # invoke procedure ml_wrapper.write('%s(' % name) i = 0 first = True for param in params: if first: first = False else: ml_wrapper.write(', ') k = param_kind(param) if k == OUT or k == INOUT or k == OUT_MANAGED_ARRAY: ml_wrapper.write('&_a%s' % i) else: ml_wrapper.write('_a%i' % i) 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') if have_context and name not in Unwrapped: ml_wrapper.write(' ec = Z3_get_error_code(ctx_p->ctx);\n') ml_wrapper.write(' if (ec != Z3_OK) {\n') ml_wrapper.write(' const char * msg = Z3_get_error_msg(ctx_p->ctx, ec);\n') ml_wrapper.write(' caml_raise_with_string(*caml_named_value("Z3EXCEPTION"), msg);\n') ml_wrapper.write(' }\n') if result != VOID: ts = type2str(result) if ml_has_plus_type(ts): pts = ml_plus_type(ts) if name in NULLWrapped: ml_wrapper.write(' z3rv = %s_mk(z3rv_m);\n' % pts) else: ml_wrapper.write(' z3rv = %s_mk(ctx_p, (%s) z3rv_m);\n' % (pts, ml_minus_type(ts))) # convert output params if len(op) > 0: # we have output parameters (i.e. call-by-reference arguments to the Z3 native # code function). Hence, the value returned by the OCaml native wrapper is a tuple # which contains the Z3 native function's return value (if it is non-void) in its # first and the output parameters in the following components. ml_wrapper.write('\n /* construct return tuple */\n') ml_wrapper.write(' result = caml_alloc(%s, 0);\n' % ret_size) i = 0 for p in params: pt = param_type(p) ts = type2str(pt) if param_kind(p) == OUT_ARRAY or param_kind(p) == INOUT_ARRAY: # convert a C-array into an OCaml list and return it ml_wrapper.write('\n _a%s_val = Val_emptylist;\n' % i) ml_wrapper.write(' for (_i = _a%s; _i > 0; _i--) {\n' % param_array_capacity_pos(p)) pts = ml_plus_type(ts) pops = ml_plus_ops_type(ts) if ml_has_plus_type(ts): ml_wrapper.write(' %s _a%dp = %s_mk(ctx_p, (%s) _a%d[_i - 1]);\n' % (pts, i, pts, ml_minus_type(ts), i)) ml_wrapper.write(' %s\n' % ml_alloc_and_store(pt, 'tmp_val', '_a%dp' % i)) else: ml_wrapper.write(' %s\n' % ml_alloc_and_store(pt, 'tmp_val', '_a%d[_i - 1]' % i)) ml_wrapper.write(' _iter = caml_alloc(2,0);\n') ml_wrapper.write(' Store_field(_iter, 0, tmp_val);\n') ml_wrapper.write(' Store_field(_iter, 1, _a%s_val);\n' % i) ml_wrapper.write(' _a%s_val = _iter;\n' % i) ml_wrapper.write(' }\n\n') elif param_kind(p) == OUT_MANAGED_ARRAY: wrp = ml_set_wrap(pt, '_a%d_val' % i, '_a%d' % i) wrp = wrp.replace('*)', '**)') wrp = wrp.replace('_plus', '') ml_wrapper.write(' %s\n' % wrp) elif is_out_param(p): if ml_has_plus_type(ts): pts = ml_plus_type(ts) ml_wrapper.write(' _a%dp = %s_mk(ctx_p, (%s) _a%d);\n' % (i, pts, ml_minus_type(ts), i)) ml_wrapper.write(' %s\n' % ml_alloc_and_store(pt, '_a%d_val' % i, '_a%dp' % i)) else: ml_wrapper.write(' %s\n' % ml_alloc_and_store(pt, '_a%d_val' % i, '_a%d' % i)) i = i + 1 # return tuples i = j = 0 if result != VOID: ml_wrapper.write(' %s' % ml_alloc_and_store(result, 'z3rv_val', 'z3rv')) ml_wrapper.write(' Store_field(result, 0, z3rv_val);\n') j = j + 1 for p in params: if is_out_param(p): ml_wrapper.write(' Store_field(result, %s, _a%s_val);\n' % (j, i)) j = j + 1 i = i + 1 else: # As we have no output parameters, we simply return the result ml_wrapper.write('\n /* construct simple return value */\n') ml_wrapper.write(' %s' % ml_alloc_and_store(result, "result", "z3rv")) # local array cleanup ml_wrapper.write('\n /* cleanup and return */\n') i = 0 for p in params: k = param_kind(p) if k == OUT_ARRAY or k == IN_ARRAY or k == INOUT_ARRAY: ml_wrapper.write(' free(_a%s);\n' % i) i = i + 1 # return ml_wrapper.write(' CAMLreturn(result);\n') ml_wrapper.write('}\n\n') if len(ip) > 5: ml_wrapper.write('CAMLprim DLL_PUBLIC value n_%s_bytecode(value * argv, int argn) {\n' % ml_method_name(name)) ml_wrapper.write(' return n_%s(' % ml_method_name(name)) i = 0 while i < len(ip): if i == 0: ml_wrapper.write('argv[0]') else: ml_wrapper.write(', argv[%s]' % i) i = i + 1 ml_wrapper.write(');\n}\n') ml_wrapper.write('\n\n') ml_wrapper.write('#ifdef __cplusplus\n') ml_wrapper.write('}\n') ml_wrapper.write('#endif\n') if mk_util.is_verbose(): print ('Generated "%s"' % ml_wrapperf) # Collect API(...) commands from def def_APIs(api_files): pat1 = re.compile(" *def_API.*") pat2 = re.compile(" *extra_API.*") for api_file in api_files: api = open(api_file, 'r') for line in api: line = line.strip('\r\n\t ') try: m = pat1.match(line) if m: eval(line) m = pat2.match(line) if m: eval(line) except Exception: raise mk_exec_header.MKException("Failed to process API definition: %s" % line) def write_log_h_preamble(log_h): log_h.write('// Automatically generated file\n') log_h.write('#include\"api/z3.h\"\n') log_h.write('#ifdef __GNUC__\n') log_h.write('#define _Z3_UNUSED __attribute__((unused))\n') log_h.write('#else\n') log_h.write('#define _Z3_UNUSED\n') log_h.write('#endif\n') # log_h.write('#include\n') log_h.write('#include\n') log_h.write('extern std::ostream * g_z3_log;\n') log_h.write('extern std::atomic g_z3_log_enabled;\n') log_h.write('class z3_log_ctx { bool m_prev; public: z3_log_ctx() { m_prev = g_z3_log_enabled.exchange(false); } ~z3_log_ctx() { g_z3_log_enabled = m_prev; } bool enabled() const { return m_prev; } };\n') log_h.write('inline void SetR(void * obj) { *g_z3_log << "= " << obj << "\\n"; }\ninline void SetO(void * obj, unsigned pos) { *g_z3_log << "* " << obj << " " << pos << "\\n"; } \ninline void SetAO(void * obj, unsigned pos, unsigned idx) { *g_z3_log << "@ " << obj << " " << pos << " " << idx << "\\n"; }\n') log_h.write('#define RETURN_Z3(Z3RES) if (_LOG_CTX.enabled()) { SetR(Z3RES); } return Z3RES\n') log_h.write('void _Z3_append_log(char const * msg);\n') def write_log_c_preamble(log_c): log_c.write('// Automatically generated file\n') log_c.write('#include\n') log_c.write('#include\"api/z3.h\"\n') log_c.write('#include\"api/api_log_macros.h\"\n') log_c.write('#include\"api/z3_logger.h\"\n') def write_exe_c_preamble(exe_c): exe_c.write('// Automatically generated file\n') exe_c.write('#include\"api/z3.h\"\n') exe_c.write('#include\"api/z3_replayer.h\"\n') # exe_c.write('void Z3_replayer_error_handler(Z3_context ctx, Z3_error_code c) { printf("[REPLAYER ERROR HANDLER]: %s\\n", Z3_get_error_msg(ctx, c)); }\n') def write_core_py_post(core_py): core_py.write(""" # Clean up del _lib del _default_dirs del _all_dirs del _ext """) def write_core_py_preamble(core_py): core_py.write( """ # Automatically generated file import sys, os import ctypes import pkg_resources from .z3types import * from .z3consts import * _ext = 'dll' if sys.platform in ('win32', 'cygwin') else 'dylib' if sys.platform == 'darwin' else 'so' _lib = None _default_dirs = ['.', os.path.dirname(os.path.abspath(__file__)), pkg_resources.resource_filename('z3', 'lib'), os.path.join(sys.prefix, 'lib'), None] _all_dirs = [] if sys.version < '3': import __builtin__ if hasattr(__builtin__, "Z3_LIB_DIRS"): _all_dirs = __builtin__.Z3_LIB_DIRS else: import builtins if hasattr(builtins, "Z3_LIB_DIRS"): _all_dirs = builtins.Z3_LIB_DIRS for v in ('Z3_LIBRARY_PATH', 'PATH', 'PYTHONPATH'): if v in os.environ: lp = os.environ[v]; lds = lp.split(';') if sys.platform in ('win32') else lp.split(':') _all_dirs.extend(lds) _all_dirs.extend(_default_dirs) _failures = [] for d in _all_dirs: try: d = os.path.realpath(d) if os.path.isdir(d): d = os.path.join(d, 'libz3.%s' % _ext) if os.path.isfile(d): _lib = ctypes.CDLL(d) break except Exception as e: _failures += [e] pass if _lib is None: # If all else failed, ask the system to find it. try: _lib = ctypes.CDLL('libz3.%s' % _ext) except Exception as e: _failures += [e] pass if _lib is None: print("Could not find libz3.%s; consider adding the directory containing it to" % _ext) print(" - your system's PATH environment variable,") print(" - the Z3_LIBRARY_PATH environment variable, or ") print(" - to the custom Z3_LIBRARY_DIRS Python-builtin before importing the z3 module, e.g. via") if sys.version < '3': print(" import __builtin__") print(" __builtin__.Z3_LIB_DIRS = [ '/path/to/libz3.%s' ] " % _ext) else: print(" import builtins") print(" builtins.Z3_LIB_DIRS = [ '/path/to/libz3.%s' ] " % _ext) raise Z3Exception("libz3.%s not found." % _ext) def _to_ascii(s): if isinstance(s, str): try: return s.encode('ascii') except: # kick the bucket down the road. :-J return s else: return s if sys.version < '3': def _to_pystr(s): return s else: def _to_pystr(s): if s != None: enc = sys.stdout.encoding if enc != None: return s.decode(enc) else: return s.decode('ascii') else: return "" _error_handler_type = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_uint) _lib.Z3_set_error_handler.restype = None _lib.Z3_set_error_handler.argtypes = [ContextObj, _error_handler_type] """ ) log_h = None log_c = None exe_c = None core_py = None # FIXME: This can only be called once from this module # due to its use of global state! def generate_files(api_files, api_output_dir=None, z3py_output_dir=None, dotnet_output_dir=None, java_output_dir=None, java_package_name=None, js_output_dir=None, ml_output_dir=None, ml_src_dir=None): """ Scan the api files in ``api_files`` and emit the relevant API files into the output directories specified. If an output directory is set to ``None`` then the files for that language binding or module are not emitted. The reason for this function interface is: * The CMake build system needs to control where files are emitted. * The CMake build system needs to be able to choose which API files are emitted. * This function should be as decoupled from the Python build system as much as possible but it must be possible for the Python build system code to use this function. Therefore we: * Do not use the ``mk_util.is_*_enabled()`` functions to determine if certain files should be or should not be emitted. * Do not use the components declared in the Python build system to determine the output directory paths. """ # FIXME: These should not be global global log_h, log_c, exe_c, core_py assert isinstance(api_files, list) # Hack: Avoid emitting files when we don't want them # by writing to temporary files that get deleted when # closed. This allows us to work around the fact that # existing code is designed to always emit these files. def mk_file_or_temp(output_dir, file_name, mode='w'): if output_dir != None: assert os.path.exists(output_dir) and os.path.isdir(output_dir) return open(os.path.join(output_dir, file_name), mode) else: # Return a file that we can write to without caring print("Faking emission of '{}'".format(file_name)) import tempfile return tempfile.TemporaryFile(mode=mode) with mk_file_or_temp(api_output_dir, 'api_log_macros.h') as log_h: with mk_file_or_temp(api_output_dir, 'api_log_macros.cpp') as log_c: with mk_file_or_temp(api_output_dir, 'api_commands.cpp') as exe_c: with mk_file_or_temp(z3py_output_dir, os.path.join('z3', 'z3core.py')) as core_py: # Write preambles write_log_h_preamble(log_h) write_log_c_preamble(log_c) write_exe_c_preamble(exe_c) write_core_py_preamble(core_py) # FIXME: these functions are awful def_Types(api_files) def_APIs(api_files) mk_bindings(exe_c) mk_py_wrappers() write_core_py_post(core_py) if mk_util.is_verbose(): print("Generated '{}'".format(log_h.name)) print("Generated '{}'".format(log_c.name)) print("Generated '{}'".format(exe_c.name)) print("Generated '{}'".format(core_py.name)) if dotnet_output_dir: with open(os.path.join(dotnet_output_dir, 'Native.cs'), 'w') as dotnet_file: mk_dotnet(dotnet_file) mk_dotnet_wrappers(dotnet_file) if mk_util.is_verbose(): print("Generated '{}'".format(dotnet_file.name)) if java_output_dir: mk_java(java_output_dir, java_package_name) if ml_output_dir: assert not ml_src_dir is None mk_ml(ml_src_dir, ml_output_dir) if js_output_dir: mk_js(js_output_dir) def main(args): logging.basicConfig(level=logging.INFO) parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("api_files", nargs="+", help="API header files to generate files from") parser.add_argument("--api_output_dir", default=None, help="Directory to emit files for api module. If not specified no files are emitted.") parser.add_argument("--z3py-output-dir", dest="z3py_output_dir", default=None, help="Directory to emit z3py files. If not specified no files are emitted.") parser.add_argument("--dotnet-output-dir", dest="dotnet_output_dir", default=None, help="Directory to emit dotnet files. If not specified no files are emitted.") parser.add_argument("--java-output-dir", dest="java_output_dir", default=None, help="Directory to emit Java files. If not specified no files are emitted.") parser.add_argument("--java-package-name", dest="java_package_name", default=None, help="Name to give the Java package (e.g. ``com.microsoft.z3``).") parser.add_argument("--ml-src-dir", dest="ml_src_dir", default=None, help="Directory containing OCaml source files. If not specified no files are emitted") parser.add_argument("--ml-output-dir", dest="ml_output_dir", default=None, help="Directory to emit OCaml files. If not specified no files are emitted.") parser.add_argument("--js_output_dir", dest="js_output_dir", default=None, help="Directory to emit js bindings. If not specified no files are emitted.") pargs = parser.parse_args(args) if pargs.java_output_dir: if pargs.java_package_name == None: logging.error('--java-package-name must be specified') return 1 if pargs.ml_output_dir: if pargs.ml_src_dir is None: logging.error('--ml-src-dir must be specified') return 1 for api_file in pargs.api_files: if not os.path.exists(api_file): logging.error('"{}" does not exist'.format(api_file)) return 1 generate_files(api_files=pargs.api_files, api_output_dir=pargs.api_output_dir, z3py_output_dir=pargs.z3py_output_dir, dotnet_output_dir=pargs.dotnet_output_dir, java_output_dir=pargs.java_output_dir, java_package_name=pargs.java_package_name, js_output_dir=pargs.js_output_dir, ml_output_dir=pargs.ml_output_dir, ml_src_dir=pargs.ml_src_dir) return 0 if __name__ == '__main__': sys.exit(main(sys.argv[1:])) z3-z3-4.8.7/scripts/update_header_guards.py000066400000000000000000000034001356505360400206240ustar00rootroot00000000000000# Copyright (c) 2015 Microsoft Corporation import os import re ifndef = re.compile("#ifndef \_(.*)\_H\_") doubleu = re.compile("#(.*) (.*)\_\_H\_") defn = re.compile("#define \_(.*)\_H\_") endif = re.compile("#endif /\* \_(.*)\_H\_") def fix_hdr(file): print(file) tmp = "%s.tmp" % file ins = open(file) ous = open(tmp,'w') line = ins.readline() found = False while line: m = doubleu.search(line) if m: ous.write("#") ous.write(m.group(1)) ous.write(" ") ous.write(m.group(2)) ous.write("_H_\n") line = ins.readline() found = True continue m = ifndef.search(line) if m: print(m.group(1)) ous.write("#ifndef ") ous.write(m.group(1)) ous.write("_H_\n") line = ins.readline() found = True continue m = defn.search(line) if m: ous.write("#define ") ous.write(m.group(1)) ous.write("_H_\n") line = ins.readline() found = True continue m = endif.search(line) if m: ous.write("#endif /* ") ous.write(m.group(1)) ous.write("_H_ */\n") line = ins.readline() found = True continue ous.write(line) line = ins.readline() ins.close() ous.close() if found: os.system("move %s %s" % (tmp, file)) else: os.system("del %s" % tmp) def fixup(dir): for root, dirs, files in os.walk(dir): for f in files: if f.endswith('.h'): path = "%s\\%s" % (root, f) fix_hdr(path) fixup('src') z3-z3-4.8.7/scripts/update_include.py000066400000000000000000000034771356505360400174700ustar00rootroot00000000000000# Copyright (c) 2017 Microsoft Corporation import os import re is_include = re.compile("#include \"(.*)\"") is_include2 = re.compile("#include\"(.*)\"") def fix_include(file, paths): tmp = "%s.tmp" % file ins = open(file) ous = open(tmp,'w') line = ins.readline() found = False while line: m = is_include.search(line) if m and m.group(1) in paths: ous.write("#include \"") ous.write(paths[m.group(1)]) ous.write("\"\n") found = True line = ins.readline() continue m = is_include2.search(line) if m and m.group(1) in paths: ous.write("#include \"") ous.write(paths[m.group(1)]) ous.write("\"\n") found = True line = ins.readline() continue ous.write(line) line = ins.readline() ins.close() ous.close() if found: print(file) os.system("move %s %s" % (tmp, file)) else: os.system("del %s" % tmp) def find_paths(dir): paths = {} for root, dirs, files in os.walk(dir): root1 = root.replace("\\","/")[4:] for f in files: if f.endswith('.h') or f.endswith('.hpp') or f.endswith('.cpp'): path = "%s/%s" % (root1, f) paths[f] = path if f.endswith('.pyg'): f = f.replace("pyg","hpp") path = "%s/%s" % (root1, f) paths[f] = path return paths paths = find_paths('src') def fixup(dir): for root, dirs, files in os.walk(dir): for f in files: if f == "z3.h": continue if f.endswith('.h') or f.endswith('.cpp'): path = "%s\\%s" % (root, f) fix_include(path, paths) fixup('src') z3-z3-4.8.7/scripts/vsts-mac.sh000066400000000000000000000006111356505360400162050ustar00rootroot00000000000000#!/bin/sh cd .. mkdir build CSC=/usr/bin/csc GACUTIL=/usr/bin/gacutil CXX=clang++ CC=clang python scripts/mk_make.py --java --python cd build make make test-z3 make cpp_example make c_example # make java_example # make python_example ./cpp_example ./test_capi git clone https://github.com/z3prover/z3test.git z3test ls python z3test/scripts/test_benchmarks.py ./z3 ./z3test/regressions/smt2z3-z3-4.8.7/scripts/vsts-vs2013.cmd000066400000000000000000000021131356505360400165330ustar00rootroot00000000000000 set echo "Build" md build cd build call "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" amd64 cmake -DBUILD_DOTNET_BINDINGS=True -DBUILD_JAVA_BINDINGS=True -DBUILD_PYTHON_BINDINGS=True -G "NMake Makefiles" ../ nmake if ERRORLEVEL 1 exit 1 rem echo "Test python bindings" rem pushd python rem python z3test.py z3 rem if ERRORLEVEL 1 exit 1 rem python z3test.py z3num rem if ERRORLEVEL 1 exit 1 rem popd echo "Build and run examples" nmake cpp_example examples\cpp_example_build_dir\cpp_example.exe if ERRORLEVEL 1 exit 1 nmake c_example examples\c_example_build_dir\c_example.exe if ERRORLEVEL 1 exit 1 rem nmake java_example rem java_example.exe if ERRORLEVEL 1 exit 1 rem nmake dotnet_example rem dotnet_example.exe if ERRORLEVEL 1 exit 1 echo "Build and run unit tests" nmake test-z3 rem TBD: test error level rem test-z3.exe -a cd .. echo "Run regression tests" git clone https://github.com/z3prover/z3test z3test echo "test-benchmarks" python z3test\scripts\test_benchmarks.py build\z3.exe z3test\regressions\smt2 if ERRORLEVEL 1 exit 1 echo "benchmarks tested" z3-z3-4.8.7/scripts/vsts-vs2017.cmd000066400000000000000000000024661356505360400165520ustar00rootroot00000000000000rem Supply argument x64, x86 or amd64_arm64 echo "Build" md build cd build call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" %1 cmake -DZ3_BUILD_DOTNET_BINDINGS=True -DZ3_BUILD_JAVA_BINDINGS=True -DZ3_BUILD_PYTHON_BINDINGS=True -G "NMake Makefiles" ../ nmake if ERRORLEVEL 1 exit 1 if %1==x86 goto BUILD_EXAMPLES if %1==amd64_arm64 goto BUILD_EXAMPLES echo "Test python bindings" pushd python python z3test.py z3 if ERRORLEVEL 1 exit 1 python z3test.py z3num if ERRORLEVEL 1 exit 1 popd :BUILD_EXAMPLES echo "Build and run examples" nmake cpp_example if %1==amd64_arm64 goto C_EXAMPLE examples\cpp_example_build_dir\cpp_example.exe if ERRORLEVEL 1 exit 1 :C_EXAMPLE nmake c_example if %1==amd64_arm64 goto ALL_DONE examples\c_example_build_dir\c_example.exe if ERRORLEVEL 1 exit 1 rem nmake java_example rem java_example.exe if ERRORLEVEL 1 exit 1 rem nmake dotnet_example rem dotnet_example.exe if ERRORLEVEL 1 exit 1 echo "Build and run unit tests" nmake test-z3 rem TBD: test error level rem test-z3.exe -a cd .. echo "Run regression tests" git clone https://github.com/z3prover/z3test z3test echo "test-benchmarks" python z3test\scripts\test_benchmarks.py build\z3.exe z3test\regressions\smt2 if ERRORLEVEL 1 exit 1 echo "benchmarks tested" :ALL_DONE echo "All done" z3-z3-4.8.7/src/000077500000000000000000000000001356505360400132165ustar00rootroot00000000000000z3-z3-4.8.7/src/CMakeLists.txt000066400000000000000000000227171356505360400157670ustar00rootroot00000000000000################################################################################ # API header files ################################################################################ # This lists the API header files that are scanned by # some of the build rules to generate some files needed # by the build set(Z3_API_HEADER_FILES_TO_SCAN z3_api.h z3_ast_containers.h z3_algebraic.h z3_polynomial.h z3_rcf.h z3_fixedpoint.h z3_optimization.h z3_fpa.h z3_spacer.h ) set(Z3_FULL_PATH_API_HEADER_FILES_TO_SCAN "") foreach (header_file ${Z3_API_HEADER_FILES_TO_SCAN}) set(full_path_api_header_file "${CMAKE_CURRENT_SOURCE_DIR}/api/${header_file}") list(APPEND Z3_FULL_PATH_API_HEADER_FILES_TO_SCAN "${full_path_api_header_file}") if (NOT EXISTS "${full_path_api_header_file}") message(FATAL_ERROR "API header file \"${full_path_api_header_file}\" does not exist") endif() endforeach() ################################################################################ # Traverse directories each adding a Z3 component ################################################################################ # I'm duplicating the order in ``mk_project.py`` for now to help us keep # the build systems in sync. # # The components in these directory explicitly declare their dependencies so # you may be able to re-order some of these directories but an error will be # raised if you try to declare a component is dependent on another component # that has not yet been declared. add_subdirectory(util) add_subdirectory(math/polynomial) add_subdirectory(sat) add_subdirectory(nlsat) add_subdirectory(util/lp) add_subdirectory(math/hilbert) add_subdirectory(math/simplex) add_subdirectory(math/automata) add_subdirectory(math/interval) add_subdirectory(math/realclosure) add_subdirectory(math/subpaving) add_subdirectory(ast) add_subdirectory(ast/rewriter) add_subdirectory(ast/normal_forms) add_subdirectory(model) add_subdirectory(tactic) add_subdirectory(ast/substitution) add_subdirectory(parsers/util) add_subdirectory(math/grobner) add_subdirectory(math/euclid) add_subdirectory(tactic/core) add_subdirectory(math/subpaving/tactic) add_subdirectory(tactic/aig) add_subdirectory(solver) add_subdirectory(sat/tactic) add_subdirectory(tactic/arith) add_subdirectory(nlsat/tactic) add_subdirectory(ackermannization) add_subdirectory(cmd_context) add_subdirectory(cmd_context/extra_cmds) add_subdirectory(parsers/smt2) add_subdirectory(ast/proofs) add_subdirectory(ast/fpa) add_subdirectory(ast/macros) add_subdirectory(ast/pattern) add_subdirectory(ast/rewriter/bit_blaster) add_subdirectory(smt/params) add_subdirectory(smt/proto_model) add_subdirectory(smt) add_subdirectory(tactic/bv) add_subdirectory(smt/tactic) add_subdirectory(tactic/sls) add_subdirectory(qe) add_subdirectory(muz/base) add_subdirectory(muz/dataflow) add_subdirectory(muz/transforms) add_subdirectory(muz/rel) add_subdirectory(muz/clp) add_subdirectory(muz/tab) add_subdirectory(muz/bmc) add_subdirectory(muz/ddnf) add_subdirectory(muz/spacer) add_subdirectory(muz/fp) add_subdirectory(tactic/ufbv) add_subdirectory(sat/sat_solver) add_subdirectory(tactic/smtlogics) add_subdirectory(tactic/fpa) add_subdirectory(tactic/fd_solver) add_subdirectory(tactic/portfolio) add_subdirectory(opt) add_subdirectory(api) add_subdirectory(api/dll) ################################################################################ # libz3 ################################################################################ get_property(Z3_LIBZ3_COMPONENTS_LIST GLOBAL PROPERTY Z3_LIBZ3_COMPONENTS) set (object_files "") foreach (component ${Z3_LIBZ3_COMPONENTS_LIST}) list(APPEND object_files $) endforeach() if (Z3_BUILD_LIBZ3_SHARED) set(lib_type "SHARED") else() set(lib_type "STATIC") endif() add_library(libz3 ${lib_type} ${object_files}) set_target_properties(libz3 PROPERTIES # VERSION determines the version in the filename of the shared library. # SOVERSION determines the value of the DT_SONAME field on ELF platforms. # On ELF platforms the final compiled filename will be libz3.so.W.X.Y.Z # but symlinks will be made to this file from libz3.so and also from # libz3.so.W.X. # This indicates that no breaking API changes will be made within a single # minor version. VERSION ${Z3_VERSION} SOVERSION ${Z3_VERSION_MAJOR}.${Z3_VERSION_MINOR}) if (NOT MSVC) # On UNIX like platforms if we don't change the OUTPUT_NAME # the library gets a name like ``liblibz3.so`` so we change it # here. We don't do a rename with MSVC because we get file naming # conflicts (the z3 executable also has this OUTPUT_NAME) with # ``.ilk``, ``.pdb``, ``.lib`` and ``.exp`` files sharing the same # prefix. set_target_properties(libz3 PROPERTIES OUTPUT_NAME z3) endif() # The `PRIVATE` usage requirement is specified so that when building Z3 as a # shared library the dependent libraries are specified on the link command line # so that if those are also shared libraries they are referenced by `libz3.so`. target_link_libraries(libz3 PRIVATE ${Z3_DEPENDENT_LIBS}) # This is currently only for the OpenMP flags. It needs to be set # via `target_link_libraries()` rather than `z3_append_linker_flag_list_to_target()` # because when building the `libz3` as a static library when the target is exported # the link dependencies need to be exported too. foreach (flag_name ${Z3_DEPENDENT_EXTRA_CXX_LINK_FLAGS}) target_link_libraries(libz3 PRIVATE ${flag_name}) endforeach() # Declare which header file are the public header files of libz3 # these will automatically installed when the libz3 target is installed set (libz3_public_headers z3_algebraic.h z3_api.h z3_ast_containers.h z3_fixedpoint.h z3_fpa.h z3.h c++/z3++.h z3_macros.h z3_optimization.h z3_polynomial.h z3_rcf.h z3_v1.h z3_spacer.h ) foreach (header ${libz3_public_headers}) set_property(TARGET libz3 APPEND PROPERTY PUBLIC_HEADER "${PROJECT_SOURCE_DIR}/src/api/${header}") endforeach() set_property(TARGET libz3 APPEND PROPERTY PUBLIC_HEADER "${CMAKE_CURRENT_BINARY_DIR}/util/z3_version.h") install(TARGETS libz3 EXPORT Z3_EXPORTED_TARGETS LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" # On Windows this installs ``libz3.lib`` which CMake calls the "corresponding import library". Do we want this installed? RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" # For Windows. DLLs are runtime targets for CMake PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" ) if (MSVC) # Handle settings dll exports when using MSVC # FIXME: This seems unnecessarily complicated but I'm doing # this because this is what the python build system does. # CMake has a much more elegant (see ``GenerateExportHeader.cmake``) # way of handling this. set(dll_module_exports_file "${CMAKE_CURRENT_BINARY_DIR}/api_dll.def") add_custom_command(OUTPUT "${dll_module_exports_file}" COMMAND "${PYTHON_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/scripts/mk_def_file.py" "${dll_module_exports_file}" "libz3" ${Z3_FULL_PATH_API_HEADER_FILES_TO_SCAN} DEPENDS "${PROJECT_SOURCE_DIR}/scripts/mk_def_file.py" ${Z3_GENERATED_FILE_EXTRA_DEPENDENCIES} ${Z3_FULL_PATH_API_HEADER_FILES_TO_SCAN} COMMENT "Generating \"${dll_module_exports_file}\"" ${ADD_CUSTOM_COMMAND_USES_TERMINAL_ARG} VERBATIM ) add_custom_target(libz3_extra_depends DEPENDS "${dll_module_exports_file}" ) add_dependencies(libz3 libz3_extra_depends) z3_append_linker_flag_list_to_target(libz3 "/DEF:${dll_module_exports_file}") endif() ################################################################################ # Z3 executable ################################################################################ add_subdirectory(shell) ################################################################################ # z3-test ################################################################################ add_subdirectory(test) ################################################################################ # Z3 API bindings ################################################################################ option(Z3_BUILD_PYTHON_BINDINGS "Build Python bindings for Z3" OFF) if (Z3_BUILD_PYTHON_BINDINGS) if (NOT Z3_BUILD_LIBZ3_SHARED) message(FATAL_ERROR "The python bindings will not work with a static libz3. " "You either need to disable Z3_BUILD_PYTHON_BINDINGS or enable Z3_BUILD_LIBZ3_SHARED") endif() add_subdirectory(api/python) endif() ################################################################################ # .NET bindings ################################################################################ option(Z3_BUILD_DOTNET_BINDINGS "Build .NET bindings for Z3" OFF) if (Z3_BUILD_DOTNET_BINDINGS) if (NOT Z3_BUILD_LIBZ3_SHARED) message(FATAL_ERROR "The .NET bindings will not work with a static libz3. " "You either need to disable Z3_BUILD_DOTNET_BINDINGS or enable Z3_BUILD_LIBZ3_SHARED") endif() add_subdirectory(api/dotnet) endif() ################################################################################ # Java bindings ################################################################################ option(Z3_BUILD_JAVA_BINDINGS "Build Java bindings for Z3" OFF) if (Z3_BUILD_JAVA_BINDINGS) if (NOT Z3_BUILD_LIBZ3_SHARED) message(FATAL_ERROR "The Java bindings will not work with a static libz3. " "You either need to disable Z3_BUILD_JAVA_BINDINGS or enable Z3_BUILD_LIBZ3_SHARED") endif() add_subdirectory(api/java) endif() # TODO: Implement support for other bindigns z3-z3-4.8.7/src/ackermannization/000077500000000000000000000000001356505360400165535ustar00rootroot00000000000000z3-z3-4.8.7/src/ackermannization/CMakeLists.txt000066400000000000000000000007651356505360400213230ustar00rootroot00000000000000z3_add_component(ackermannization SOURCES ackermannize_bv_model_converter.cpp ackermannize_bv_tactic.cpp ackr_bound_probe.cpp ackr_helper.cpp ackr_model_converter.cpp lackr.cpp lackr_model_constructor.cpp lackr_model_converter_lazy.cpp COMPONENT_DEPENDENCIES ast model rewriter solver tactic PYG_FILES ackermannization_params.pyg ackermannize_bv_tactic_params.pyg TACTIC_HEADERS ackermannize_bv_tactic.h ackr_bound_probe.h ) z3-z3-4.8.7/src/ackermannization/ackermannization_params.pyg000066400000000000000000000004341356505360400241750ustar00rootroot00000000000000def_module_params('ackermannization', description='solving UF via ackermannization', export=True, params=( ('eager', BOOL, True, 'eagerly instantiate all congruence rules'), )) z3-z3-4.8.7/src/ackermannization/ackermannize_bv_model_converter.cpp000066400000000000000000000006721356505360400256710ustar00rootroot00000000000000/*++ Copyright (c) 2016 Microsoft Corporation Module Name: ackermannize_bv_model_converter.cpp Abstract: Author: Mikolas Janota (MikolasJanota) Revision History: --*/ #include "ackermannization/ackr_model_converter.h" #include "ackermannization/ackermannize_bv_model_converter.h" model_converter * mk_ackermannize_bv_model_converter(ast_manager & m, const ackr_info_ref& info) { return mk_ackr_model_converter(m, info); } z3-z3-4.8.7/src/ackermannization/ackermannize_bv_model_converter.h000066400000000000000000000007551356505360400253400ustar00rootroot00000000000000 /*++ Copyright (c) 2016 Microsoft Corporation Module Name: ackermannize_bv_model_converter.h Abstract: Author: Mikolas Janota (MikolasJanota) Revision History: --*/ #ifndef ACKERMANNIZE_BV_MODEL_CONVERTER_H_ #define ACKERMANNIZE_BV_MODEL_CONVERTER_H_ #include "tactic/model_converter.h" #include "ackermannization/ackr_info.h" model_converter * mk_ackermannize_bv_model_converter(ast_manager & m, const ackr_info_ref& info); #endif /* ACKERMANNIZE_BV_MODEL_CONVERTER_H_ */ z3-z3-4.8.7/src/ackermannization/ackermannize_bv_tactic.cpp000066400000000000000000000053641356505360400237540ustar00rootroot00000000000000/*++ Copyright (c) 2016 Microsoft Corporation Module Name: ackermannize_bv_tactic.cpp Abstract: Author: Mikolas Janota Revision History: --*/ #include "ackermannization/ackermannize_bv_tactic.h" #include "tactic/tactical.h" #include "ackermannization/lackr.h" #include "model/model_smt2_pp.h" #include "ackermannization/ackermannize_bv_tactic_params.hpp" #include "ackermannization/ackermannize_bv_model_converter.h" class ackermannize_bv_tactic : public tactic { public: ackermannize_bv_tactic(ast_manager& m, params_ref const& p) : m(m), m_p(p) { updt_params(p); } ~ackermannize_bv_tactic() override { } void operator()(goal_ref const & g, goal_ref_buffer & result) override { tactic_report report("ackermannize", *g); fail_if_unsat_core_generation("ackermannize", g); fail_if_proof_generation("ackermannize", g); TRACE("ackermannize", g->display(tout << "in\n");); ptr_vector flas; const unsigned sz = g->size(); for (unsigned i = 0; i < sz; i++) flas.push_back(g->form(i)); lackr lackr(m, m_p, m_st, flas, nullptr); // mk result goal_ref resg(alloc(goal, *g, true)); const bool success = lackr.mk_ackermann(resg, m_lemma_limit); if (!success) { // Just pass on the input unchanged TRACE("ackermannize", tout << "ackermannize not run due to limit" << std::endl;); result.reset(); result.push_back(g.get()); return; } result.push_back(resg.get()); // report model if (g->models_enabled()) { resg->add(mk_ackermannize_bv_model_converter(m, lackr.get_info())); } resg->inc_depth(); TRACE("ackermannize", resg->display(tout << "out\n");); SASSERT(resg->is_well_sorted()); } void updt_params(params_ref const & _p) override { ackermannize_bv_tactic_params p(_p); m_lemma_limit = p.div0_ackermann_limit(); } void collect_param_descrs(param_descrs & r) override { ackermannize_bv_tactic_params::collect_param_descrs(r); } void collect_statistics(statistics & st) const override { st.update("ackr-constraints", m_st.m_ackrs_sz); } void reset_statistics() override { m_st.reset(); } void cleanup() override { } tactic* translate(ast_manager& m) override { return alloc(ackermannize_bv_tactic, m, m_p); } private: ast_manager& m; params_ref m_p; lackr_stats m_st; double m_lemma_limit; }; tactic * mk_ackermannize_bv_tactic(ast_manager & m, params_ref const & p) { return alloc(ackermannize_bv_tactic, m, p); } z3-z3-4.8.7/src/ackermannization/ackermannize_bv_tactic.h000066400000000000000000000007211356505360400234110ustar00rootroot00000000000000/*++ Copyright (c) 2016 Microsoft Corporation Module Name: ackermannize_bv_tactic.h Abstract: Author: Mikolas Janota Revision History: --*/ #ifndef _ACKERMANNIZE_TACTIC_H_ #define _ACKERMANNIZE_TACTIC_H_ #include "tactic/tactical.h" tactic * mk_ackermannize_bv_tactic(ast_manager & m, params_ref const & p); /* ADD_TACTIC("ackermannize_bv", "A tactic for performing full Ackermannization on bv instances.", "mk_ackermannize_bv_tactic(m, p)") */ #endif z3-z3-4.8.7/src/ackermannization/ackermannize_bv_tactic_params.pyg000066400000000000000000000005331356505360400253250ustar00rootroot00000000000000def_module_params(module_name='rewriter', class_name='ackermannize_bv_tactic_params', export=True, params=( ("div0_ackermann_limit", UINT, 1000, "a bound for number of congruence Ackermann lemmas for div0 modelling"), ) ) z3-z3-4.8.7/src/ackermannization/ackr_bound_probe.cpp000066400000000000000000000046461356505360400225670ustar00rootroot00000000000000/*++ Copyright (c) 2016 Microsoft Corporation Module Name: ackr_bound_probe.cpp Abstract: Author: Mikolas Janota Revision History: --*/ #include "ast/array_decl_plugin.h" #include "ast/ast_smt2_pp.h" #include "ackermannization/ackr_helper.h" #include "ackermannization/ackr_bound_probe.h" /* For each function f, calculate the number of its occurrences o_f and compute "o_f choose 2". The probe then sums up these for all functions. This upper bound might be crude because some congruence lemmas trivially simplify to true. */ class ackr_bound_probe : public probe { struct proc { typedef ackr_helper::fun2terms_map fun2terms_map; typedef ackr_helper::sel2terms_map sel2terms_map; typedef ackr_helper::app_set app_set; ast_manager& m; fun2terms_map m_fun2terms; // a map from functions to occurrences sel2terms_map m_sel2terms; // a map from functions to occurrences ackr_helper m_ackr_helper; expr_mark m_non_select; proc(ast_manager & m) : m(m), m_ackr_helper(m) { } ~proc() { for (auto & kv : m_fun2terms) { dealloc(kv.m_value); } for (auto & kv : m_sel2terms) { dealloc(kv.m_value); } } void prune_non_select() { m_ackr_helper.prune_non_select(m_sel2terms, m_non_select); } void operator()(quantifier *) {} void operator()(var *) {} void operator()(app * a) { m_ackr_helper.mark_non_select(a, m_non_select); m_ackr_helper.insert(m_fun2terms, m_sel2terms, a); } }; public: ackr_bound_probe() {} result operator()(goal const & g) override { proc p(g.m()); unsigned sz = g.size(); expr_fast_mark1 visited; for (unsigned i = 0; i < sz; i++) { for_each_expr_core(p, visited, g.form(i)); } p.prune_non_select(); double total = ackr_helper::calculate_lemma_bound(p.m_fun2terms, p.m_sel2terms); TRACE("ackermannize", tout << "total=" << total << std::endl;); return result(total); } inline static unsigned n_choose_2(unsigned n) { return n & 1 ? (n * (n >> 1)) : (n >> 1) * (n - 1); } }; probe * mk_ackr_bound_probe() { return alloc(ackr_bound_probe); } z3-z3-4.8.7/src/ackermannization/ackr_bound_probe.h000066400000000000000000000010521356505360400222200ustar00rootroot00000000000000 /*++ Copyright (c) 2016 Microsoft Corporation Module Name: ackr_bound_probe.h Abstract: A probe to give an upper bound of Ackermann congruence lemmas that a formula might generate. Author: Mikolas Janota Revision History: --*/ #ifndef ACKR_BOUND_PROBE_H_ #define ACKR_BOUND_PROBE_H_ #include "tactic/probe.h" probe * mk_ackr_bound_probe(); /* ADD_PROBE("ackr-bound-probe", "A probe to give an upper bound of Ackermann congruence lemmas that a formula might generate.", "mk_ackr_bound_probe()") */ #endif /* ACKR_BOUND_PROBE_H_ */ z3-z3-4.8.7/src/ackermannization/ackr_helper.cpp000066400000000000000000000013051356505360400215350ustar00rootroot00000000000000/*++ Copyright (c) 2016 Microsoft Corporation Module Name: ackr_helper.cpp Abstract: Author: Mikolas Janota (MikolasJanota) Revision History: --*/ #include "ackermannization/ackr_helper.h" double ackr_helper::calculate_lemma_bound(fun2terms_map const& occs1, sel2terms_map const& occs2) { double total = 0; for (auto const& kv : occs1) { total += n_choose_2_chk(kv.m_value->var_args.size()); total += kv.m_value->const_args.size() * kv.m_value->var_args.size(); } for (auto const& kv : occs2) { total += n_choose_2_chk(kv.m_value->var_args.size()); total += kv.m_value->const_args.size() * kv.m_value->var_args.size(); } return total; } z3-z3-4.8.7/src/ackermannization/ackr_helper.h000066400000000000000000000100121356505360400211750ustar00rootroot00000000000000 /*++ Copyright (c) 2016 Microsoft Corporation Module Name: ackr_helper.h Abstract: Author: Mikolas Janota Revision History: --*/ #ifndef ACKR_HELPER_H_ #define ACKR_HELPER_H_ #include "ast/bv_decl_plugin.h" #include "ast/array_decl_plugin.h" class ackr_helper { public: struct app_occ { obj_hashtable const_args; obj_hashtable var_args; }; typedef app_occ app_set; typedef obj_map fun2terms_map; typedef obj_map sel2terms_map; ackr_helper(ast_manager& m) : m_bvutil(m), m_autil(m) {} /** \brief Determines if a given function should be Ackermannized. This includes all uninterpreted functions but also "special" functions, e.g. OP_BSMOD0, which are not marked as uninterpreted but effectively are. */ inline bool is_uninterp_fn(app const * a) const { if (is_uninterp(a)) return true; else { decl_plugin * p = m_bvutil.get_manager().get_plugin(a->get_family_id()); return p->is_considered_uninterpreted(a->get_decl()); } } /** \brief determines if a term is a candidate select for Ackerman reduction */ inline bool is_select(app* a) { return m_autil.is_select(a) && is_uninterp_const(a->get_arg(0)); } void mark_non_select(app* a, expr_mark& non_select) { if (m_autil.is_select(a)) { bool first = true; for (expr* arg : *a) { if (first) first = false; else non_select.mark(arg, true); } } else { for (expr* arg : *a) { non_select.mark(arg, true); } } } void prune_non_select(obj_map & sels, expr_mark& non_select) { ptr_vector nons; for (auto& kv : sels) { if (non_select.is_marked(kv.m_key)) { nons.push_back(kv.m_key); dealloc(kv.m_value); } } for (app* s : nons) { sels.erase(s); } } inline bv_util& bvutil() { return m_bvutil; } /** \brief Calculates an upper bound for congruence lemmas given a map of function of occurrences. */ static double calculate_lemma_bound(fun2terms_map const& occs1, sel2terms_map const& occs2); /** \brief Calculate n choose 2. **/ inline static unsigned n_choose_2(unsigned n) { return (n & 1) ? (n * (n >> 1)) : (n >> 1) * (n - 1); } /** \brief Calculate n choose 2 guarded for overflow. Returns infinity if unsafe. **/ inline static double n_choose_2_chk(unsigned n) { SASSERT(std::numeric_limits().max() & 32); return n & (1 << 16) ? std::numeric_limits().infinity() : n_choose_2(n); } void insert(fun2terms_map& f2t, sel2terms_map& s2t, app* a) { if (a->get_num_args() == 0) return; ast_manager& m = m_bvutil.get_manager(); app_set* ts = nullptr; bool is_const_args = true; if (is_select(a)) { app* sel = to_app(a->get_arg(0)); if (!s2t.find(sel, ts)) { ts = alloc(app_set); s2t.insert(sel, ts); } } else if (is_uninterp_fn(a)) { func_decl* const fd = a->get_decl(); if (!f2t.find(fd, ts)) { ts = alloc(app_set); f2t.insert(fd, ts); } is_const_args = m.is_value(a->get_arg(0)); } else { return; } for (unsigned i = 1; is_const_args && i < a->get_num_args(); ++i) { is_const_args &= m.is_value(a->get_arg(i)); } if (is_const_args) { ts->const_args.insert(a); } else { ts->var_args.insert(a); } } private: bv_util m_bvutil; array_util m_autil; }; #endif /* ACKR_HELPER_H_ */ z3-z3-4.8.7/src/ackermannization/ackr_info.h000066400000000000000000000057111356505360400206630ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: ackr_info.h Abstract: Author: Mikolas Janota Revision History: --*/ #ifndef ACKR_INFO_H_ #define ACKR_INFO_H_ #include "util/ref.h" #include "util/obj_hashtable.h" #include "ast/ast.h" #include "ast/rewriter/expr_replacer.h" #include "ast/ast_translation.h" /** \brief Information about how a formula is being converted into a formula without uninterpreted function symbols via ackermannization. The intended use is that new terms are added via set_abstr. Once all terms are abstracted, call seal. The function abstract may only be called when sealed. The class enables reference counting. **/ class ackr_info { public: ackr_info(ast_manager& m) : m(m), m_er(mk_default_expr_replacer(m)), m_subst(m), m_ref_count(0), m_sealed(false) {} virtual ~ackr_info() { for (auto & kv : m_t2c) { m.dec_ref(kv.m_key); m.dec_ref(kv.m_value); } } inline void set_abstr(app* term, app* c) { SASSERT(!m_sealed); SASSERT(c && term); m_t2c.insert(term,c); m_c2t.insert(c->get_decl(),term); m_subst.insert(term, c); m.inc_ref(term); m.inc_ref(c); } inline expr_ref abstract(expr * e) { expr_ref res(m); SASSERT(m_sealed); (*m_er)(e, res); return res; } inline app* find_term(func_decl* c) const { app * rv = nullptr; m_c2t.find(c,rv); return rv; } inline app* get_abstr(app* term) const { return m_t2c.find(term); } inline void seal() { m_sealed = true; m_er->set_substitution(&m_subst); } virtual ackr_info * translate(ast_translation & translator) { ackr_info * const retv = alloc(ackr_info, translator.to()); for (auto & kv : m_t2c) { retv->set_abstr(translator(kv.m_key), translator(kv.m_value)); } if (m_sealed) retv->seal(); return retv; } // // Reference counting // void inc_ref() { ++m_ref_count; } void dec_ref() { --m_ref_count; if (m_ref_count == 0) { dealloc(this); } } private: typedef obj_map t2ct; typedef obj_map c2tt; ast_manager& m; t2ct m_t2c; // terms to constants c2tt m_c2t; // constants to terms (inversion of m_t2c) // replacer and substitution used to compute abstractions scoped_ptr m_er; expr_substitution m_subst; unsigned m_ref_count; // reference counting bool m_sealed; // debugging }; typedef ref ackr_info_ref; #endif /* ACKR_INFO_H_ */ z3-z3-4.8.7/src/ackermannization/ackr_model_converter.cpp000066400000000000000000000146441356505360400234570ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: ackr_model_converter.cpp Abstract: Author: Mikolas Janota Revision History: --*/ #include "ast/ast_smt2_pp.h" #include "ast/array_decl_plugin.h" #include "model/model_evaluator.h" #include "ackermannization/ackr_model_converter.h" #include "ackermannization/ackr_info.h" class ackr_model_converter : public model_converter { public: ackr_model_converter(ast_manager & m, const ackr_info_ref& info, model_ref& abstr_model) : m(m), info(info), abstr_model(abstr_model), fixed_model(true) { } ackr_model_converter(ast_manager & m, const ackr_info_ref& info) : m(m), info(info), fixed_model(false) { } ~ackr_model_converter() override { } void get_units(obj_map& units) override { units.reset(); } void operator()(model_ref & md) override { SASSERT(!fixed_model || md.get() == 0 || (!md->get_num_constants() && !md->get_num_functions())); model_ref& old_model = fixed_model ? abstr_model : md; SASSERT(old_model.get()); model * new_model = alloc(model, m); convert(old_model.get(), new_model); md = new_model; } model_converter * translate(ast_translation & translator) override { ackr_info_ref retv_info = info->translate(translator); if (fixed_model) { model_ref retv_mod_ref = abstr_model->translate(translator); return alloc(ackr_model_converter, translator.to(), retv_info, retv_mod_ref); } else { return alloc(ackr_model_converter, translator.to(), retv_info); } } void display(std::ostream & out) override { out << "(ackr-model-converter)\n"; } protected: ast_manager & m; const ackr_info_ref info; model_ref abstr_model; bool fixed_model; void convert(model * source, model * destination); void add_entry(model_evaluator & evaluator, app* term, expr* value, obj_map& interpretations); void add_entry(model_evaluator & evaluator, app* term, expr* value, obj_map& interpretations); void convert_constants(model * source, model * destination); }; void ackr_model_converter::convert(model * source, model * destination) { destination->copy_func_interps(*source); destination->copy_usort_interps(*source); convert_constants(source, destination); } void ackr_model_converter::convert_constants(model * source, model * destination) { TRACE("ackermannize", tout << "converting constants\n";); obj_map interpretations; obj_map array_interpretations; model_evaluator evaluator(*source); evaluator.set_model_completion(true); array_util autil(m); for (unsigned i = 0; i < source->get_num_constants(); i++) { func_decl * const c = source->get_constant(i); app * const term = info->find_term(c); expr * value = source->get_const_interp(c); if (!term) { destination->register_decl(c, value); } else if (autil.is_select(term)) { add_entry(evaluator, term, value, array_interpretations); } else { add_entry(evaluator, term, value, interpretations); } } for (auto & kv : interpretations) { func_decl* const fd = kv.m_key; func_interp* const fi = kv.m_value; fi->set_else(m.get_some_value(fd->get_range())); destination->register_decl(fd, fi); } for (auto & kv : array_interpretations) { destination->register_decl(kv.m_key->get_decl(), kv.m_value); } } void ackr_model_converter::add_entry(model_evaluator & evaluator, app* term, expr* value, obj_map& array_interpretations) { array_util autil(m); app* A = to_app(term->get_arg(0)); expr * e = nullptr, *c = nullptr; if (!array_interpretations.find(A, e)) { e = autil.mk_const_array(m.get_sort(A), value); } else { // avoid storing the same as the default value. c = e; while (autil.is_store(c)) c = to_app(c)->get_arg(0); if (autil.is_const(c, c) && c == value) { return; } expr_ref_vector args(m); unsigned sz = term->get_num_args(); args.push_back(e); for (unsigned i = 1; i < sz; ++i) { expr * arg = term->get_arg(i); args.push_back(evaluator(info->abstract(arg))); } args.push_back(value); e = autil.mk_store(args.size(), args.c_ptr()); } array_interpretations.insert(A, e); } void ackr_model_converter::add_entry(model_evaluator & evaluator, app* term, expr* value, obj_map& interpretations) { TRACE("ackermannize", tout << "add_entry" << mk_ismt2_pp(term, m, 2) << "->" << mk_ismt2_pp(value, m, 2) << "\n";); func_interp * fi = nullptr; func_decl * const declaration = term->get_decl(); const unsigned sz = declaration->get_arity(); SASSERT(sz == term->get_num_args()); if (!interpretations.find(declaration, fi)) { fi = alloc(func_interp, m, sz); interpretations.insert(declaration, fi); } expr_ref_vector args(m); for (expr* arg : *term) { args.push_back(evaluator(info->abstract(arg))); } if (fi->get_entry(args.c_ptr()) == nullptr) { TRACE("ackermannize", tout << mk_ismt2_pp(declaration, m) << " args: " << std::endl; for (expr* arg : args) { tout << mk_ismt2_pp(arg, m) << std::endl; } tout << " -> " << mk_ismt2_pp(value, m) << "\n"; ); fi->insert_new_entry(args.c_ptr(), value); } else { TRACE("ackermannize", tout << "entry already present\n";); } } model_converter * mk_ackr_model_converter(ast_manager & m, const ackr_info_ref& info) { return alloc(ackr_model_converter, m, info); } model_converter * mk_ackr_model_converter(ast_manager & m, const ackr_info_ref& info, model_ref& abstr_model) { return alloc(ackr_model_converter, m, info, abstr_model); } z3-z3-4.8.7/src/ackermannization/ackr_model_converter.h000066400000000000000000000010261356505360400231120ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: ackr_model_converter.h Abstract: Author: Mikolas Janota Revision History: --*/ #ifndef ACKR_MODEL_CONVERTER_H_ #define ACKR_MODEL_CONVERTER_H_ #include "tactic/model_converter.h" #include "ackermannization/ackr_info.h" model_converter * mk_ackr_model_converter(ast_manager & m, const ackr_info_ref & info, model_ref & abstr_model); model_converter * mk_ackr_model_converter(ast_manager & m, const ackr_info_ref & info); #endif /* LACKR_MODEL_CONVERTER_H_ */ z3-z3-4.8.7/src/ackermannization/lackr.cpp000066400000000000000000000211351356505360400203550ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: lackr.cpp Abstract: Author: Mikolas Janota Revision History: --*/ #include "ackermannization/lackr.h" #include "ackermannization/ackermannization_params.hpp" #include "tactic/tactic.h" #include "ackermannization/lackr_model_constructor.h" #include "ackermannization/ackr_info.h" #include "ast/for_each_expr.h" #include "ast/ast_util.h" #include "model/model_smt2_pp.h" lackr::lackr(ast_manager& m, const params_ref& p, lackr_stats& st, const ptr_vector& formulas, solver * uffree_solver) : m(m), m_p(p), m_formulas(formulas), m_autil(m), m_abstr(m), m_solver(uffree_solver), m_ackr_helper(m), m_simp(m), m_ackrs(m), m_st(st), m_is_init(false) { updt_params(p); } void lackr::updt_params(params_ref const & _p) { ackermannization_params p(_p); m_eager = p.eager(); } lackr::~lackr() { for (auto& kv : m_fun2terms) { dealloc(kv.get_value()); } for (auto& kv : m_sel2terms) { dealloc(kv.get_value()); } } lbool lackr::operator()() { SASSERT(m_solver); if (!init()) return l_undef; lbool rv = m_eager ? eager() : lazy(); if (rv == l_true) { m_solver->get_model(m_model); } CTRACE("ackermannize", rv == l_true, model_smt2_pp(tout << "abstr_model(\n", m, *(m_model.get()), 2); tout << ")\n"; ); return rv; } bool lackr::mk_ackermann(/*out*/goal_ref& g, double lemmas_upper_bound) { if (lemmas_upper_bound <= 0) return false; if (!init()) return false; if (lemmas_upper_bound != std::numeric_limits::infinity() && ackr_helper::calculate_lemma_bound(m_fun2terms, m_sel2terms) > lemmas_upper_bound) return false; eager_enc(); for (expr* a : m_abstr) g->assert_expr(a); for (expr* a : m_ackrs) g->assert_expr(a); return true; } bool lackr::init() { if (!m_is_init) { params_ref simp_p(m_p); m_simp.updt_params(simp_p); m_info = alloc(ackr_info, m); if (!collect_terms()) return false; abstract(); m_is_init = true; } return true; } // // Introduce ackermann lemma for the two given terms. // bool lackr::ackr(app * const t1, app * const t2) { TRACE("ackermannize", tout << "ackr " << mk_ismt2_pp(t1, m, 2) << " , " << mk_ismt2_pp(t2, m, 2) << "\n";); const unsigned sz = t1->get_num_args(); SASSERT(t2->get_num_args() == sz); expr_ref_vector eqs(m); for (unsigned i = 0; i < sz; ++i) { expr * const arg1 = t1->get_arg(i); expr * const arg2 = t2->get_arg(i); if (m.are_equal(arg1, arg2)) continue; // quickly skip syntactically equal if (m.are_distinct(arg1, arg2)){ // quickly abort if there are two distinct (e.g. numerals) TRACE("ackermannize", tout << "never eq\n";); return false; } eqs.push_back(m.mk_eq(arg1, arg2)); } app * const a1 = m_info->get_abstr(t1); app * const a2 = m_info->get_abstr(t2); SASSERT(a1 && a2); expr_ref lhs = mk_and(eqs); expr_ref rhs(m.mk_eq(a1, a2),m); expr_ref cg(m.mk_implies(lhs, rhs), m); expr_ref cga = m_info->abstract(cg); // constraint needs abstraction due to nested applications m_simp(cga); TRACE("ackermannize", tout << "abstr1 " << mk_ismt2_pp(a1, m, 2) << "\n"; tout << "abstr2 " << mk_ismt2_pp(a2, m, 2) << "\n"; tout << "ackr constr lhs" << mk_ismt2_pp(lhs, m, 2) << "\n"; tout << "ackr constr rhs" << mk_ismt2_pp(rhs, m, 2) << "\n"; tout << "ackr constr" << mk_ismt2_pp(cg, m, 2) << "\n"; tout << "ackr constr abs:" << mk_ismt2_pp(cga, m, 2) << "\n";); if (m.is_true(cga)) { return false; } m_st.m_ackrs_sz++; m_ackrs.push_back(std::move(cga)); return true; } // // Introduce the ackermann lemma for each pair of terms. // void lackr::eager_enc() { TRACE("ackermannize", tout << "#funs: " << m_fun2terms.size() << "#sels: " << m_sel2terms.size() << std::endl;); for (auto const& kv : m_fun2terms) { checkpoint(); ackr(kv.get_value()); } for (auto const& kv : m_sel2terms) { checkpoint(); ackr(kv.get_value()); } } void lackr::ackr(app_set const* ts) { auto r = ts->var_args.end(); for (auto j = ts->var_args.begin(); j != r; ++j) { app * const t1 = *j; auto k = j; ++k; for (; k != r; ++k) { app * const t2 = *k; if (t1 != t2) { ackr(t1, t2); } } for (app* t2 : ts->const_args) { ackr(t1, t2); } } } void lackr::abstract_fun(fun2terms_map const& apps) { for (auto const& kv : apps) { func_decl* fd = kv.m_key; for (app * t : kv.m_value->var_args) { app * fc = m.mk_fresh_const(fd->get_name(), m.get_sort(t)); SASSERT(t->get_decl() == fd); m_info->set_abstr(t, fc); } for (app * t : kv.m_value->const_args) { app * fc = m.mk_fresh_const(fd->get_name(), m.get_sort(t)); SASSERT(t->get_decl() == fd); m_info->set_abstr(t, fc); } } } void lackr::abstract_sel(sel2terms_map const& apps) { for (auto const& kv : apps) { func_decl * fd = kv.m_key->get_decl(); for (app * t : kv.m_value->const_args) { app * fc = m.mk_fresh_const(fd->get_name(), m.get_sort(t)); m_info->set_abstr(t, fc); } for (app * t : kv.m_value->var_args) { app * fc = m.mk_fresh_const(fd->get_name(), m.get_sort(t)); m_info->set_abstr(t, fc); } } } void lackr::abstract() { abstract_fun(m_fun2terms); abstract_sel(m_sel2terms); m_info->seal(); // perform abstraction of the formulas for (expr * f : m_formulas) { expr_ref a = m_info->abstract(f); m_abstr.push_back(std::move(a)); } } void lackr::add_term(app* a) { m_ackr_helper.insert(m_fun2terms, m_sel2terms, a); } void lackr::push_abstraction() { for (expr* a : m_abstr) { m_solver->assert_expr(a); } } lbool lackr::eager() { SASSERT(m_is_init); push_abstraction(); TRACE("ackermannize", tout << "run sat 0\n"; ); lbool rv0 = m_solver->check_sat(0, nullptr); if (rv0 == l_false) { return l_false; } eager_enc(); expr_ref all = mk_and(m_ackrs); m_simp(all); m_solver->assert_expr(all); TRACE("ackermannize", tout << "run sat all\n"; ); return m_solver->check_sat(0, nullptr); } lbool lackr::lazy() { SASSERT(m_is_init); lackr_model_constructor mc(m, m_info); push_abstraction(); unsigned ackr_head = 0; while (true) { m_st.m_it++; checkpoint(); TRACE("ackermannize", tout << "lazy check: " << m_st.m_it << "\n";); const lbool r = m_solver->check_sat(0, nullptr); if (r == l_undef) return l_undef; // give up if (r == l_false) return l_false; // abstraction unsat // reconstruct model model_ref am; m_solver->get_model(am); const bool mc_res = mc.check(am); if (mc_res) return l_true; // model okay // refine abstraction for (auto const& kv : mc.get_conflicts()) { ackr(kv.first, kv.second); } while (ackr_head < m_ackrs.size()) { m_solver->assert_expr(m_ackrs.get(ackr_head++)); } } } // // Collect all uninterpreted terms, skipping 0-arity. // bool lackr::collect_terms() { ptr_vector stack = m_formulas; expr_mark visited; while (!stack.empty()) { expr * curr = stack.back(); if (visited.is_marked(curr)) { stack.pop_back(); continue; } switch (curr->get_kind()) { case AST_VAR: visited.mark(curr, true); stack.pop_back(); break; 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(); m_ackr_helper.mark_non_select(a, m_non_select); add_term(a); } break; } case AST_QUANTIFIER: return false; // quantifiers not supported default: UNREACHABLE(); return false; } } m_ackr_helper.prune_non_select(m_sel2terms, m_non_select); return true; } z3-z3-4.8.7/src/ackermannization/lackr.h000066400000000000000000000075701356505360400200310ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation Module Name: lackr.h Abstract: Author: Mikolas Janota Revision History: --*/ #ifndef LACKR_H_ #define LACKR_H_ #include "util/lbool.h" #include "util/util.h" #include "ast/rewriter/th_rewriter.h" #include "ast/bv_decl_plugin.h" #include "model/model.h" #include "solver/solver.h" #include "tactic/tactic_exception.h" #include "tactic/goal.h" #include "ackermannization/ackr_info.h" #include "ackermannization/ackr_helper.h" struct lackr_stats { lackr_stats() : m_it(0), m_ackrs_sz(0) {} void reset() { m_it = m_ackrs_sz = 0; } unsigned m_it; // number of lazy iterations unsigned m_ackrs_sz; // number of congruence constraints }; /** \brief A class to encode or directly solve problems with uninterpreted functions via ackermannization. Currently, solving is supported only for QF_UFBV. **/ class lackr { public: lackr(ast_manager& m, const params_ref& p, lackr_stats& st, const ptr_vector& formulas, solver * uffree_solver); ~lackr(); void updt_params(params_ref const & _p); /** \brief * Solve the formula that the class was initialized with. **/ lbool operator() (); /** \brief Converts function occurrences to constants and encodes all congruence ackermann lemmas. This procedure guarantees a equisatisfiability with the input formula and it has a worst-case quadratic blowup. Before ackermannization an upper bound on congruence lemmas is computed and tested against \p lemmas_upper_bound. If this bound is exceeded, the function returns false, it returns true otherwise. **/ bool mk_ackermann(/*out*/goal_ref& g, double lemmas_upper_bound); // // getters // inline ackr_info_ref get_info() { return m_info; } inline model_ref get_model() { return m_model; } // // timeout mechanism // void checkpoint() { if (m.canceled()) { throw tactic_exception(TACTIC_CANCELED_MSG); } } private: typedef ackr_helper::fun2terms_map fun2terms_map; typedef ackr_helper::sel2terms_map sel2terms_map; typedef ackr_helper::app_set app_set; ast_manager& m; params_ref m_p; const ptr_vector& m_formulas; array_util m_autil; expr_ref_vector m_abstr; fun2terms_map m_fun2terms; sel2terms_map m_sel2terms; ackr_info_ref m_info; solver* m_solver; ackr_helper m_ackr_helper; th_rewriter m_simp; expr_ref_vector m_ackrs; model_ref m_model; bool m_eager; expr_mark m_non_select; lackr_stats& m_st; bool m_is_init; bool init(); lbool eager(); lbool lazy(); // // Introduce congruence ackermann lemma for the two given terms. // bool ackr(app * t1, app * t2); void ackr(app_set const* ts); // // Introduce the ackermann lemma for each pair of terms. // void eager_enc(); void abstract(); void push_abstraction(); void add_term(app* a); // // Collect all uninterpreted terms, skipping 0-arity. // bool collect_terms(); void abstract_sel(sel2terms_map const& apps); void abstract_fun(fun2terms_map const& apps); }; #endif /* LACKR_H_ */ z3-z3-4.8.7/src/ackermannization/lackr_model_constructor.cpp000066400000000000000000000306731356505360400242110ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: model_constructor.cpp Abstract: Author: Mikolas Janota Revision History: --*/ #include "ackermannization/lackr_model_constructor.h" #include "model/model_evaluator.h" #include "ast/ast_smt2_pp.h" #include "ackermannization/ackr_info.h" #include "ast/for_each_expr.h" #include "ast/rewriter/bv_rewriter.h" #include "ast/rewriter/bool_rewriter.h" struct lackr_model_constructor::imp { public: imp(ast_manager & m, ackr_info_ref info, model_ref & abstr_model, conflict_list & conflicts) : m(m) , m_info(info) , m_abstr_model(abstr_model) , m_conflicts(conflicts) , m_pinned(m) , m_b_rw(m) , m_bv_rw(m) , m_evaluator(nullptr) , m_empty_model(m) , m_ackr_helper(m) {} ~imp() { } // // Returns true iff model was successfully constructed. // Conflicts are saved as a side effect. // bool check() { bool retv = true; for (unsigned i = 0; i < m_abstr_model->get_num_constants(); i++) { func_decl * const c = m_abstr_model->get_constant(i); app * const _term = m_info->find_term(c); expr * const term = _term ? _term : m.mk_const(c); if (!check_term(term)) retv = false; } return retv; } void make_model(model_ref& destination) { for (unsigned i = 0; i < m_abstr_model->get_num_uninterpreted_sorts(); i++) { sort * const s = m_abstr_model->get_uninterpreted_sort(i); ptr_vector u = m_abstr_model->get_universe(s); destination->register_usort(s, u.size(), u.c_ptr()); } for (unsigned i = 0; i < m_abstr_model->get_num_functions(); i++) { func_decl * const fd = m_abstr_model->get_function(i); func_interp * const fi = m_abstr_model->get_func_interp(fd); destination->register_decl(fd, fi); } for (auto & kv : m_app2val) { app * a = kv.m_key; if (a->get_num_args() == 0) destination->register_decl(a->get_decl(), kv.m_value); } obj_map interpretations; for (auto & kv : m_values2val) { add_entry(kv.m_key, kv.m_value.value, interpretations); } for (auto & kv : interpretations) { func_decl* const fd = kv.m_key; func_interp* const fi = kv.get_value(); fi->set_else(m.get_some_value(fd->get_range())); destination->register_decl(fd, fi); } } void add_entry(app* term, expr* value, obj_map& interpretations) { func_interp* fi = nullptr; func_decl * const declaration = term->get_decl(); const unsigned sz = declaration->get_arity(); SASSERT(sz == term->get_num_args()); if (!interpretations.find(declaration, fi)) { fi = alloc(func_interp, m, sz); interpretations.insert(declaration, fi); } fi->insert_new_entry(term->get_args(), value); } private: ast_manager& m; ackr_info_ref m_info; model_ref& m_abstr_model; conflict_list& m_conflicts; ast_ref_vector m_pinned; bool_rewriter m_b_rw; bv_rewriter m_bv_rw; scoped_ptr m_evaluator; model m_empty_model; private: struct val_info { expr * value; app * source_term; }; typedef obj_map app2val_t; typedef obj_map values2val_t; values2val_t m_values2val; app2val_t m_app2val; ptr_vector m_stack; ackr_helper m_ackr_helper; expr_mark m_visited; static inline val_info mk_val_info(expr* value, app* source_term) { val_info rv; rv.value = value; rv.source_term = source_term; return rv; } // Performs congruence check on a given term. bool check_term(expr * term) { m_stack.push_back(term); const bool rv = _check_stack(); m_stack.reset(); return rv; } // Performs congruence check on terms on the stack. // Stops upon the first failure. // Returns true if and only if all congruence checks succeeded. bool _check_stack() { if (!m_evaluator) { m_evaluator = alloc(model_evaluator, m_empty_model); } expr * curr; while (!m_stack.empty()) { curr = m_stack.back(); if (m_visited.is_marked(curr)) { m_stack.pop_back(); continue; } switch (curr->get_kind()) { case AST_VAR: UNREACHABLE(); return false; case AST_APP: { app * a = to_app(curr); if (for_each_expr_args(m_stack, m_visited, a->get_num_args(), a->get_args())) { m_visited.mark(a, true); m_stack.pop_back(); if (!mk_value(a)) return false; } break; } case AST_QUANTIFIER: UNREACHABLE(); return false; default: UNREACHABLE(); return false; } } return true; } inline bool is_val(expr * e) { return m.is_value(e); } inline bool eval_cached(app * a, expr *& val) { if (is_val(a)) { val = a; return true; } return m_app2val.find(a, val); } bool evaluate(app * const a, expr_ref& result) { SASSERT(!is_val(a)); const unsigned num = a->get_num_args(); if (num == 0) { // handle constants make_value_constant(a, result); return true; } // evaluate arguments expr_ref_vector values(m); values.reserve(num); expr * const * args = a->get_args(); for (unsigned i = 0; i < num; ++i) { expr * val = nullptr; const bool b = eval_cached(to_app(args[i]), val); // TODO: OK conversion to_app? CTRACE("model_constructor", m_conflicts.empty() && !b, tout << "fail arg val(\n" << mk_ismt2_pp(args[i], m, 2) << '\n'; ); if (!b) { // bailing out because args eval failed previously return false; } TRACE("model_constructor", tout << "arg val " << i << "(\n" << mk_ismt2_pp(args[i], m, 2) << " : " << mk_ismt2_pp(val, m, 2) << '\n'; ); SASSERT(b); values[i] = val; } // handle functions if (m_ackr_helper.is_uninterp_fn(a)) { // handle uninterpreted app_ref key(m.mk_app(a->get_decl(), values.c_ptr()), m); if (!make_value_uninterpreted_function(a, key.get(), result)) { return false; } } else if (m_ackr_helper.is_select(a)) { // fail on select terms return false; } else { // handle interpreted make_value_interpreted_function(a, values, result); } return true; } // // Check and record the value for a given term, given that all arguments are already checked. // bool mk_value(app * a) { if (is_val(a)) return true; // skip numerals TRACE("model_constructor", tout << "mk_value(\n" << mk_ismt2_pp(a, m, 2) << ")\n";); SASSERT(!m_app2val.contains(a)); expr_ref result(m); if (!evaluate(a, result)) return false; SASSERT(is_val(result)); TRACE("model_constructor", tout << "map term(\n" << mk_ismt2_pp(a, m, 2) << "\n->" << mk_ismt2_pp(result.get(), m, 2)<< ")\n"; ); CTRACE("model_constructor", !is_val(result.get()), tout << "eval fail\n" << mk_ismt2_pp(a, m, 2) << mk_ismt2_pp(result, m, 2) << "\n"; ); SASSERT(is_val(result.get())); m_app2val.insert(a, result.get()); // memoize m_pinned.push_back(a); m_pinned.push_back(result); return true; } // Constants from the abstract model are directly mapped to the concrete one. void make_value_constant(app * const a, expr_ref& result) { SASSERT(a->get_num_args() == 0); func_decl * const fd = a->get_decl(); expr * val = m_abstr_model->get_const_interp(fd); if (val == nullptr) { // TODO: avoid model completion? sort * s = fd->get_range(); val = m_abstr_model->get_some_value(s); } result = val; } bool make_value_uninterpreted_function(app* a, app* key, expr_ref& result) { // get ackermann constant app * const ac = m_info->get_abstr(a); func_decl * const a_fd = a->get_decl(); SASSERT(ac->get_num_args() == 0); SASSERT(a_fd->get_range() == ac->get_decl()->get_range()); expr_ref value(m); value = m_abstr_model->get_const_interp(ac->get_decl()); // get ackermann constant's interpretation if (value.get() == nullptr) { // TODO: avoid model completion? sort * s = a_fd->get_range(); value = m_abstr_model->get_some_value(s); } // check congruence val_info vi; if(m_values2val.find(key,vi)) { // already is mapped to a value SASSERT(vi.source_term); const bool ok = vi.value == value; if (!ok) { TRACE("model_constructor", tout << "already mapped by(\n" << mk_ismt2_pp(vi.source_term, m, 2) << "\n->" << mk_ismt2_pp(vi.value, m, 2) << ")\n"; ); m_conflicts.push_back(std::make_pair(a, vi.source_term)); } result = vi.value; return ok; } else { // new value result = value; vi.value = value; vi.source_term = a; m_values2val.insert(key,vi); m_pinned.push_back(vi.source_term); m_pinned.push_back(vi.value); m_pinned.push_back(key); return true; } UNREACHABLE(); return true; } void make_value_interpreted_function(app* a, expr_ref_vector& values, expr_ref& result) { const unsigned num = values.size(); func_decl * const fd = a->get_decl(); const family_id fid = fd->get_family_id(); expr_ref term(m); term = m.mk_app(a->get_decl(), num, values.c_ptr()); m_evaluator->operator() (term, result); TRACE("model_constructor", tout << "eval(\n" << mk_ismt2_pp(term.get(), m, 2) << "\n->" << mk_ismt2_pp(result.get(), m, 2) << ")\n"; ); return; if (fid == m_b_rw.get_fid()) { decl_kind k = fd->get_decl_kind(); if (k == OP_EQ) { // theory dispatch for = SASSERT(num == 2); family_id s_fid = m.get_sort(values.get(0))->get_family_id(); if (s_fid == m_bv_rw.get_fid()) m_bv_rw.mk_eq_core(values.get(0), values.get(1), result); } else { m_b_rw.mk_app_core(fd, num, values.c_ptr(), result); } } else { if (fid == m_bv_rw.get_fid()) { m_bv_rw.mk_app_core(fd, num, values.c_ptr(), result); } else { UNREACHABLE(); } } } }; lackr_model_constructor::lackr_model_constructor(ast_manager& m, ackr_info_ref info) : m_imp(nullptr) , m(m) , m_state(UNKNOWN) , m_info(info) , m_ref_count(0) {} lackr_model_constructor::~lackr_model_constructor() { dealloc(m_imp); } bool lackr_model_constructor::check(model_ref& abstr_model) { m_conflicts.reset(); dealloc(m_imp); m_imp = alloc(lackr_model_constructor::imp, m, m_info, abstr_model, m_conflicts); const bool rv = m_imp->check(); m_state = rv ? CHECKED : CONFLICT; return rv; } void lackr_model_constructor::make_model(model_ref& model) { SASSERT(m_state == CHECKED); m_imp->make_model(model); } z3-z3-4.8.7/src/ackermannization/lackr_model_constructor.h000066400000000000000000000030361356505360400236470ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation Module Name: model_constructor.h Abstract: Given a propositional abstraction, attempt to construct a model. Author: Mikolas Janota Revision History: --*/ #ifndef LACKR_MODEL_CONSTRUCTOR_H_ #define LACKR_MODEL_CONSTRUCTOR_H_ #include "ast/ast.h" #include "ackermannization/ackr_info.h" #include "ackermannization/ackr_helper.h" #include "model/model.h" class lackr_model_constructor { public: typedef std::pair app_pair; typedef vector conflict_list; lackr_model_constructor(ast_manager& m, ackr_info_ref info); ~lackr_model_constructor(); bool check(model_ref& abstr_model); const conflict_list& get_conflicts() { SASSERT(m_state == CONFLICT); return m_conflicts; } void make_model(model_ref& model); // // Reference counting // void inc_ref() { ++m_ref_count; } void dec_ref() { --m_ref_count; if (m_ref_count == 0) { dealloc(this); } } private: struct imp; imp * m_imp; ast_manager & m; enum {CHECKED, CONFLICT, UNKNOWN} m_state; conflict_list m_conflicts; const ackr_info_ref m_info; unsigned m_ref_count; // reference counting }; typedef ref lackr_model_constructor_ref; #endif /* MODEL_CONSTRUCTOR_H_ */ z3-z3-4.8.7/src/ackermannization/lackr_model_converter_lazy.cpp000066400000000000000000000030561356505360400246650ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: lackr_model_converter_lazy.cpp Abstract: Author: Mikolas Janota Revision History: --*/ #include "ackermannization/lackr_model_converter_lazy.h" #include "model/model_evaluator.h" #include "ast/ast_smt2_pp.h" #include "ackermannization/ackr_info.h" #include "ackermannization/lackr_model_constructor.h" class lackr_model_converter_lazy : public model_converter { public: lackr_model_converter_lazy(ast_manager & m, const lackr_model_constructor_ref& lmc) : m(m) , model_constructor(lmc) { } ~lackr_model_converter_lazy() override { } void operator()(model_ref & md) override { SASSERT(md.get() == 0 || (!md->get_num_constants() && !md->get_num_functions())); SASSERT(model_constructor.get()); model * new_model = alloc(model, m); md = new_model; model_constructor->make_model(md); } void get_units(obj_map& units) override { units.reset(); } //void display(std::ostream & out); model_converter * translate(ast_translation & translator) override { NOT_IMPLEMENTED_YET(); } void display(std::ostream & out) override { out << "(lackr-model-converter)\n"; } protected: ast_manager& m; const lackr_model_constructor_ref model_constructor; }; model_converter * mk_lackr_model_converter_lazy(ast_manager & m, const lackr_model_constructor_ref& model_constructor) { return alloc(lackr_model_converter_lazy, m, model_constructor); } z3-z3-4.8.7/src/ackermannization/lackr_model_converter_lazy.h000066400000000000000000000007341356505360400243320ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation Module Name: lackr_model_converter_lazy.h Abstract: Author: Mikolas Janota Revision History: --*/ #ifndef LACKR_MODEL_CONVERTER_LAZY_H_ #define LACKR_MODEL_CONVERTER_LAZY_H_ #include "tactic/model_converter.h" #include "ackermannization/ackr_info.h" model_converter * mk_lackr_model_converter_lazy(ast_manager & m, const ackr_info_ref& info, model_ref& abstr_model); #endif /* LACKR_MODEL_CONVERTER_LAZY_H_ */ z3-z3-4.8.7/src/api/000077500000000000000000000000001356505360400137675ustar00rootroot00000000000000z3-z3-4.8.7/src/api/CMakeLists.txt000066400000000000000000000034161356505360400165330ustar00rootroot00000000000000set(generated_files api_commands.cpp api_log_macros.cpp api_log_macros.h ) # Sanity check foreach (gen_file ${generated_files}) if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${gen_file}") message(FATAL_ERROR "\"${CMAKE_CURRENT_SOURCE_DIR}/${gen_file}\"" ${z3_polluted_tree_msg}) endif() endforeach() set(full_path_generated_files "") foreach (gen_file ${generated_files}) list(APPEND full_path_generated_files "${CMAKE_CURRENT_BINARY_DIR}/${gen_file}") endforeach() add_custom_command(OUTPUT ${generated_files} COMMAND "${PYTHON_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/scripts/update_api.py" ${Z3_FULL_PATH_API_HEADER_FILES_TO_SCAN} "--api_output_dir" "${CMAKE_CURRENT_BINARY_DIR}" DEPENDS "${PROJECT_SOURCE_DIR}/scripts/update_api.py" ${Z3_GENERATED_FILE_EXTRA_DEPENDENCIES} ${Z3_FULL_PATH_API_HEADER_FILES_TO_SCAN} # FIXME: When update_api.py no longer uses ``mk_util`` drop this dependency "${PROJECT_SOURCE_DIR}/scripts/mk_util.py" COMMENT "Generating ${generated_files}" ${ADD_CUSTOM_COMMAND_USES_TERMINAL_ARG} VERBATIM ) z3_add_component(api SOURCES api_algebraic.cpp api_arith.cpp api_array.cpp api_ast.cpp api_ast_map.cpp api_ast_vector.cpp api_bv.cpp api_config_params.cpp api_context.cpp api_datalog.cpp api_datatype.cpp api_fpa.cpp api_goal.cpp api_log.cpp api_model.cpp api_numeral.cpp api_opt.cpp api_params.cpp api_parsers.cpp api_pb.cpp api_polynomial.cpp api_qe.cpp api_quant.cpp api_rcf.cpp api_seq.cpp api_solver.cpp api_special_relations.cpp api_stats.cpp api_tactic.cpp z3_replayer.cpp ${full_path_generated_files} COMPONENT_DEPENDENCIES opt portfolio realclosure ) z3-z3-4.8.7/src/api/api_algebraic.cpp000066400000000000000000000373411356505360400172450ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_algebraic.cpp Abstract: Additional APIs for handling Z3 algebraic numbers encoded as Z3_ASTs Author: Leonardo de Moura (leonardo) 2012-12-07 Notes: --*/ #include "api/z3.h" #include "api/api_log_macros.h" #include "api/api_context.h" #include "api/api_ast_vector.h" #include "math/polynomial/algebraic_numbers.h" #include "ast/expr2polynomial.h" #include "util/cancel_eh.h" #include "util/scoped_timer.h" #define CHECK_IS_ALGEBRAIC(ARG, RET) { \ if (!Z3_algebraic_is_value_core(c, ARG)) { \ SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); \ return RET; \ } \ } #define CHECK_IS_ALGEBRAIC_X(ARG, RET) { \ if (!Z3_algebraic_is_value_core(c, ARG)) { \ SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); \ RETURN_Z3(RET); \ } \ } static arith_util & au(Z3_context c) { return mk_c(c)->autil(); } static algebraic_numbers::manager & am(Z3_context c) { return au(c).am(); } static bool is_rational(Z3_context c, Z3_ast a) { return au(c).is_numeral(to_expr(a)); } static bool is_irrational(Z3_context c, Z3_ast a) { return au(c).is_irrational_algebraic_numeral(to_expr(a)); } static rational get_rational(Z3_context c, Z3_ast a) { SASSERT(is_rational(c, a)); rational r; VERIFY(au(c).is_numeral(to_expr(a), r)); return r; } static algebraic_numbers::anum const & get_irrational(Z3_context c, Z3_ast a) { SASSERT(is_irrational(c, a)); return au(c).to_irrational_algebraic_numeral(to_expr(a)); } extern "C" { bool Z3_algebraic_is_value_core(Z3_context c, Z3_ast a) { api::context * _c = mk_c(c); return is_expr(a) && (_c->autil().is_numeral(to_expr(a)) || _c->autil().is_irrational_algebraic_numeral(to_expr(a))); } bool Z3_API Z3_algebraic_is_value(Z3_context c, Z3_ast a) { Z3_TRY; LOG_Z3_algebraic_is_value(c, a); RESET_ERROR_CODE(); return Z3_algebraic_is_value_core(c, a); Z3_CATCH_RETURN(false); } bool Z3_API Z3_algebraic_is_pos(Z3_context c, Z3_ast a) { return Z3_algebraic_sign(c, a) > 0; } bool Z3_API Z3_algebraic_is_neg(Z3_context c, Z3_ast a) { return Z3_algebraic_sign(c, a) < 0; } bool Z3_API Z3_algebraic_is_zero(Z3_context c, Z3_ast a) { return Z3_algebraic_sign(c, a) == 0; } int Z3_API Z3_algebraic_sign(Z3_context c, Z3_ast a) { Z3_TRY; LOG_Z3_algebraic_sign(c, a); RESET_ERROR_CODE(); CHECK_IS_ALGEBRAIC(a, 0); if (is_rational(c, a)) { rational v = get_rational(c, a); if (v.is_pos()) return 1; else if (v.is_neg()) return -1; else return 0; } else { algebraic_numbers::anum const & v = get_irrational(c, a); if (am(c).is_pos(v)) return 1; else if (am(c).is_neg(v)) return -1; else return 0; } Z3_CATCH_RETURN(0); } #define BIN_OP(RAT_OP, IRAT_OP) \ algebraic_numbers::manager & _am = am(c); \ ast * r = 0; \ if (is_rational(c, a)) { \ rational av = get_rational(c, a); \ if (is_rational(c, b)) { \ rational bv = get_rational(c, b); \ r = au(c).mk_numeral(av RAT_OP bv, false); \ } \ else { \ algebraic_numbers::anum const & bv = get_irrational(c, b); \ scoped_anum _av(_am); \ _am.set(_av, av.to_mpq()); \ scoped_anum _r(_am); \ _am.IRAT_OP(_av, bv, _r); \ r = au(c).mk_numeral(_r, false); \ } \ } \ else { \ algebraic_numbers::anum const & av = get_irrational(c, a); \ if (is_rational(c, b)) { \ rational bv = get_rational(c, b); \ scoped_anum _bv(_am); \ _am.set(_bv, bv.to_mpq()); \ scoped_anum _r(_am); \ _am.IRAT_OP(av, _bv, _r); \ r = au(c).mk_numeral(_r, false); \ } \ else { \ algebraic_numbers::anum const & bv = get_irrational(c, b); \ scoped_anum _r(_am); \ _am.IRAT_OP(av, bv, _r); \ r = au(c).mk_numeral(_r, false); \ } \ } \ mk_c(c)->save_ast_trail(r); \ RETURN_Z3(of_ast(r)); Z3_ast Z3_API Z3_algebraic_add(Z3_context c, Z3_ast a, Z3_ast b) { Z3_TRY; LOG_Z3_algebraic_add(c, a, b); RESET_ERROR_CODE(); CHECK_IS_ALGEBRAIC_X(a, nullptr); CHECK_IS_ALGEBRAIC_X(b, nullptr); BIN_OP(+,add); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_algebraic_sub(Z3_context c, Z3_ast a, Z3_ast b) { Z3_TRY; LOG_Z3_algebraic_sub(c, a, b); RESET_ERROR_CODE(); CHECK_IS_ALGEBRAIC_X(a, nullptr); CHECK_IS_ALGEBRAIC_X(b, nullptr); BIN_OP(-,sub); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_algebraic_mul(Z3_context c, Z3_ast a, Z3_ast b) { Z3_TRY; LOG_Z3_algebraic_mul(c, a, b); RESET_ERROR_CODE(); CHECK_IS_ALGEBRAIC_X(a, nullptr); CHECK_IS_ALGEBRAIC_X(b, nullptr); BIN_OP(*,mul); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_algebraic_div(Z3_context c, Z3_ast a, Z3_ast b) { Z3_TRY; LOG_Z3_algebraic_div(c, a, b); RESET_ERROR_CODE(); CHECK_IS_ALGEBRAIC_X(a, nullptr); CHECK_IS_ALGEBRAIC_X(b, nullptr); if ((is_rational(c, b) && get_rational(c, b).is_zero()) || (!is_rational(c, b) && am(c).is_zero(get_irrational(c, b)))) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } BIN_OP(/,div); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_algebraic_root(Z3_context c, Z3_ast a, unsigned k) { Z3_TRY; LOG_Z3_algebraic_root(c, a, k); RESET_ERROR_CODE(); CHECK_IS_ALGEBRAIC_X(a, nullptr); if (k % 2 == 0) { if ((is_rational(c, a) && get_rational(c, a).is_neg()) || (!is_rational(c, a) && am(c).is_neg(get_irrational(c, a)))) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } } algebraic_numbers::manager & _am = am(c); scoped_anum _r(_am); if (is_rational(c, a)) { scoped_anum av(_am); _am.set(av, get_rational(c, a).to_mpq()); _am.root(av, k, _r); } else { algebraic_numbers::anum const & av = get_irrational(c, a); _am.root(av, k, _r); } expr * r = au(c).mk_numeral(_r, false); mk_c(c)->save_ast_trail(r); RETURN_Z3(of_ast(r)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_algebraic_power(Z3_context c, Z3_ast a, unsigned k) { Z3_TRY; LOG_Z3_algebraic_power(c, a, k); RESET_ERROR_CODE(); CHECK_IS_ALGEBRAIC_X(a, nullptr); algebraic_numbers::manager & _am = am(c); scoped_anum _r(_am); if (is_rational(c, a)) { scoped_anum av(_am); _am.set(av, get_rational(c, a).to_mpq()); _am.power(av, k, _r); } else { algebraic_numbers::anum const & av = get_irrational(c, a); _am.power(av, k, _r); } expr * r = au(c).mk_numeral(_r, false); mk_c(c)->save_ast_trail(r); RETURN_Z3(of_ast(r)); Z3_CATCH_RETURN(nullptr); } #define BIN_PRED(RAT_PRED, IRAT_PRED) \ algebraic_numbers::manager & _am = am(c); \ bool r; \ if (is_rational(c, a)) { \ rational av = get_rational(c, a); \ if (is_rational(c, b)) { \ rational bv = get_rational(c, b); \ r = av RAT_PRED bv; \ } \ else { \ algebraic_numbers::anum const & bv = get_irrational(c, b); \ scoped_anum _av(_am); \ _am.set(_av, av.to_mpq()); \ r = _am.IRAT_PRED(_av, bv); \ } \ } \ else { \ algebraic_numbers::anum const & av = get_irrational(c, a); \ if (is_rational(c, b)) { \ rational bv = get_rational(c, b); \ scoped_anum _bv(_am); \ _am.set(_bv, bv.to_mpq()); \ r = _am.IRAT_PRED(av, _bv); \ } \ else { \ algebraic_numbers::anum const & bv = get_irrational(c, b); \ r = _am.IRAT_PRED(av, bv); \ } \ } \ return r; bool Z3_API Z3_algebraic_lt(Z3_context c, Z3_ast a, Z3_ast b) { Z3_TRY; LOG_Z3_algebraic_lt(c, a, b); RESET_ERROR_CODE(); CHECK_IS_ALGEBRAIC(a, 0); CHECK_IS_ALGEBRAIC(b, 0); BIN_PRED(<,lt); Z3_CATCH_RETURN(false); } bool Z3_API Z3_algebraic_gt(Z3_context c, Z3_ast a, Z3_ast b) { return Z3_algebraic_lt(c, b, a); } bool Z3_API Z3_algebraic_le(Z3_context c, Z3_ast a, Z3_ast b) { return !Z3_algebraic_lt(c, b, a); } bool Z3_API Z3_algebraic_ge(Z3_context c, Z3_ast a, Z3_ast b) { return !Z3_algebraic_lt(c, a, b); } bool Z3_API Z3_algebraic_eq(Z3_context c, Z3_ast a, Z3_ast b) { Z3_TRY; LOG_Z3_algebraic_eq(c, a, b); RESET_ERROR_CODE(); CHECK_IS_ALGEBRAIC(a, 0); CHECK_IS_ALGEBRAIC(b, 0); BIN_PRED(==,eq); Z3_CATCH_RETURN(0); } bool Z3_API Z3_algebraic_neq(Z3_context c, Z3_ast a, Z3_ast b) { return !Z3_algebraic_eq(c, a, b); } static bool to_anum_vector(Z3_context c, unsigned n, Z3_ast a[], scoped_anum_vector & as) { algebraic_numbers::manager & _am = am(c); scoped_anum tmp(_am); for (unsigned i = 0; i < n; i++) { if (is_rational(c, a[i])) { _am.set(tmp, get_rational(c, a[i]).to_mpq()); as.push_back(tmp); } else if (is_irrational(c, a[i])) { as.push_back(get_irrational(c, a[i])); } else { return false; } } return true; } class vector_var2anum : public polynomial::var2anum { scoped_anum_vector const & m_as; public: vector_var2anum(scoped_anum_vector & as):m_as(as) {} virtual ~vector_var2anum() {} algebraic_numbers::manager & m() const override { return m_as.m(); } bool contains(polynomial::var x) const override { return static_cast(x) < m_as.size(); } algebraic_numbers::anum const & operator()(polynomial::var x) const override { return m_as.get(x); } }; Z3_ast_vector Z3_API Z3_algebraic_roots(Z3_context c, Z3_ast p, unsigned n, Z3_ast a[]) { Z3_TRY; LOG_Z3_algebraic_roots(c, p, n, a); RESET_ERROR_CODE(); polynomial::manager & pm = mk_c(c)->pm(); polynomial_ref _p(pm); polynomial::scoped_numeral d(pm.m()); expr2polynomial converter(mk_c(c)->m(), pm, nullptr, true); if (!converter.to_polynomial(to_expr(p), _p, d) || static_cast(max_var(_p)) >= n + 1) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return nullptr; } algebraic_numbers::manager & _am = am(c); scoped_anum_vector as(_am); if (!to_anum_vector(c, n, a, as)) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return nullptr; } scoped_anum_vector roots(_am); { cancel_eh eh(mk_c(c)->m().limit()); api::context::set_interruptable si(*(mk_c(c)), eh); scoped_timer timer(mk_c(c)->params().m_timeout, &eh); vector_var2anum v2a(as); _am.isolate_roots(_p, v2a, roots); } Z3_ast_vector_ref* result = alloc(Z3_ast_vector_ref, *mk_c(c), mk_c(c)->m()); mk_c(c)->save_object(result); for (unsigned i = 0; i < roots.size(); i++) { result->m_ast_vector.push_back(au(c).mk_numeral(roots.get(i), false)); } RETURN_Z3(of_ast_vector(result)); Z3_CATCH_RETURN(nullptr); } int Z3_API Z3_algebraic_eval(Z3_context c, Z3_ast p, unsigned n, Z3_ast a[]) { Z3_TRY; LOG_Z3_algebraic_eval(c, p, n, a); RESET_ERROR_CODE(); polynomial::manager & pm = mk_c(c)->pm(); polynomial_ref _p(pm); polynomial::scoped_numeral d(pm.m()); expr2polynomial converter(mk_c(c)->m(), pm, nullptr, true); if (!converter.to_polynomial(to_expr(p), _p, d) || static_cast(max_var(_p)) >= n) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return 0; } algebraic_numbers::manager & _am = am(c); scoped_anum_vector as(_am); if (!to_anum_vector(c, n, a, as)) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return 0; } { cancel_eh eh(mk_c(c)->m().limit()); api::context::set_interruptable si(*(mk_c(c)), eh); scoped_timer timer(mk_c(c)->params().m_timeout, &eh); vector_var2anum v2a(as); int r = _am.eval_sign_at(_p, v2a); if (r > 0) return 1; else if (r < 0) return -1; else return 0; } Z3_CATCH_RETURN(0); } }; z3-z3-4.8.7/src/api/api_arith.cpp000066400000000000000000000147331356505360400164430ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_arith.cpp Abstract: API for arith theory Author: Leonardo de Moura (leonardo) 2012-02-29. Revision History: --*/ #include "api/z3.h" #include "api/api_log_macros.h" #include "api/api_context.h" #include "api/api_util.h" #include "ast/arith_decl_plugin.h" #include "math/polynomial/algebraic_numbers.h" #define MK_ARITH_OP(NAME, OP) MK_NARY(NAME, mk_c(c)->get_arith_fid(), OP, SKIP) #define MK_BINARY_ARITH_OP(NAME, OP) MK_BINARY(NAME, mk_c(c)->get_arith_fid(), OP, SKIP) #define MK_ARITH_PRED(NAME, OP) MK_BINARY(NAME, mk_c(c)->get_arith_fid(), OP, SKIP) extern "C" { Z3_sort Z3_API Z3_mk_int_sort(Z3_context c) { Z3_TRY; LOG_Z3_mk_int_sort(c); RESET_ERROR_CODE(); Z3_sort r = of_sort(mk_c(c)->m().mk_sort(mk_c(c)->get_arith_fid(), INT_SORT)); RETURN_Z3(r); Z3_CATCH_RETURN(nullptr); } Z3_sort Z3_API Z3_mk_real_sort(Z3_context c) { Z3_TRY; LOG_Z3_mk_real_sort(c); RESET_ERROR_CODE(); Z3_sort r = of_sort(mk_c(c)->m().mk_sort(mk_c(c)->get_arith_fid(), REAL_SORT)); RETURN_Z3(r); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_real(Z3_context c, int num, int den) { Z3_TRY; LOG_Z3_mk_real(c, num, den); RESET_ERROR_CODE(); if (den == 0) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } sort* s = mk_c(c)->m().mk_sort(mk_c(c)->get_arith_fid(), REAL_SORT); ast* a = mk_c(c)->mk_numeral_core(rational(num, den), s); RETURN_Z3(of_ast(a)); Z3_CATCH_RETURN(nullptr); } MK_ARITH_OP(Z3_mk_add, OP_ADD); MK_ARITH_OP(Z3_mk_mul, OP_MUL); MK_BINARY_ARITH_OP(Z3_mk_power, OP_POWER); MK_BINARY_ARITH_OP(Z3_mk_mod, OP_MOD); MK_BINARY_ARITH_OP(Z3_mk_rem, OP_REM); Z3_ast Z3_API Z3_mk_div(Z3_context c, Z3_ast n1, Z3_ast n2) { Z3_TRY; LOG_Z3_mk_div(c, n1, n2); RESET_ERROR_CODE(); decl_kind k = OP_IDIV; sort* ty = mk_c(c)->m().get_sort(to_expr(n1)); sort* real_ty = mk_c(c)->m().mk_sort(mk_c(c)->get_arith_fid(), REAL_SORT); if (ty == real_ty) { k = OP_DIV; } expr * args[2] = { to_expr(n1), to_expr(n2) }; ast* a = mk_c(c)->m().mk_app(mk_c(c)->get_arith_fid(), k, 0, nullptr, 2, args); mk_c(c)->save_ast_trail(a); check_sorts(c, a); RETURN_Z3(of_ast(a)); Z3_CATCH_RETURN(nullptr); } MK_ARITH_PRED(Z3_mk_lt, OP_LT); MK_ARITH_PRED(Z3_mk_gt, OP_GT); MK_ARITH_PRED(Z3_mk_le, OP_LE); MK_ARITH_PRED(Z3_mk_ge, OP_GE); MK_ARITH_PRED(Z3_mk_divides, OP_IDIVIDES); MK_UNARY(Z3_mk_int2real, mk_c(c)->get_arith_fid(), OP_TO_REAL, SKIP); MK_UNARY(Z3_mk_real2int, mk_c(c)->get_arith_fid(), OP_TO_INT, SKIP); MK_UNARY(Z3_mk_is_int, mk_c(c)->get_arith_fid(), OP_IS_INT, SKIP); Z3_ast Z3_API Z3_mk_sub(Z3_context c, unsigned num_args, Z3_ast const args[]) { Z3_TRY; LOG_Z3_mk_sub(c, num_args, args); RESET_ERROR_CODE(); if (num_args == 0) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } expr* r = to_expr(args[0]); for (unsigned i = 1; i < num_args; ++i) { expr* args1[2] = { r, to_expr(args[i]) }; r = mk_c(c)->m().mk_app(mk_c(c)->get_arith_fid(), OP_SUB, 0, nullptr, 2, args1); check_sorts(c, r); } mk_c(c)->save_ast_trail(r); RETURN_Z3(of_expr(r)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_unary_minus(Z3_context c, Z3_ast n) { Z3_TRY; LOG_Z3_mk_unary_minus(c, n); RESET_ERROR_CODE(); MK_UNARY_BODY(Z3_mk_unary_minus, mk_c(c)->get_arith_fid(), OP_UMINUS, SKIP); Z3_CATCH_RETURN(nullptr); } bool Z3_API Z3_is_algebraic_number(Z3_context c, Z3_ast a) { LOG_Z3_is_algebraic_number(c, a); return mk_c(c)->autil().is_irrational_algebraic_numeral(to_expr(a)); } Z3_ast Z3_API Z3_get_algebraic_number_lower(Z3_context c, Z3_ast a, unsigned precision) { Z3_TRY; LOG_Z3_get_algebraic_number_lower(c, a, precision); RESET_ERROR_CODE(); if (!Z3_is_algebraic_number(c, a)) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } expr * e = to_expr(a); algebraic_numbers::anum const & val = mk_c(c)->autil().to_irrational_algebraic_numeral(e); rational l; mk_c(c)->autil().am().get_lower(val, l, precision); expr * r = mk_c(c)->autil().mk_numeral(l, false); mk_c(c)->save_ast_trail(r); RETURN_Z3(of_expr(r)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_get_algebraic_number_upper(Z3_context c, Z3_ast a, unsigned precision) { Z3_TRY; LOG_Z3_get_algebraic_number_upper(c, a, precision); RESET_ERROR_CODE(); if (!Z3_is_algebraic_number(c, a)) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } expr * e = to_expr(a); algebraic_numbers::anum const & val = mk_c(c)->autil().to_irrational_algebraic_numeral(e); rational l; mk_c(c)->autil().am().get_upper(val, l, precision); expr * r = mk_c(c)->autil().mk_numeral(l, false); mk_c(c)->save_ast_trail(r); RETURN_Z3(of_expr(r)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_get_numerator(Z3_context c, Z3_ast a) { Z3_TRY; LOG_Z3_get_numerator(c, a); RESET_ERROR_CODE(); rational val; ast * _a = to_ast(a); if (!is_expr(_a) || !mk_c(c)->autil().is_numeral(to_expr(_a), val)) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } expr * r = mk_c(c)->autil().mk_numeral(numerator(val), true); mk_c(c)->save_ast_trail(r); RETURN_Z3(of_expr(r)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_get_denominator(Z3_context c, Z3_ast a) { Z3_TRY; LOG_Z3_get_denominator(c, a); RESET_ERROR_CODE(); rational val; ast * _a = to_ast(a); if (!is_expr(_a) || !mk_c(c)->autil().is_numeral(to_expr(_a), val)) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } expr * r = mk_c(c)->autil().mk_numeral(denominator(val), true); mk_c(c)->save_ast_trail(r); RETURN_Z3(of_expr(r)); Z3_CATCH_RETURN(nullptr); } }; z3-z3-4.8.7/src/api/api_array.cpp000066400000000000000000000270751356505360400164550ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_array.cpp Abstract: API for array theory Author: Leonardo de Moura (leonardo) 2012-02-29. Revision History: --*/ #include "api/z3.h" #include "api/api_log_macros.h" #include "api/api_context.h" #include "api/api_util.h" #include "ast/array_decl_plugin.h" extern "C" { Z3_sort Z3_API Z3_mk_array_sort(Z3_context c, Z3_sort domain, Z3_sort range) { Z3_TRY; LOG_Z3_mk_array_sort(c, domain, range); RESET_ERROR_CODE(); parameter params[2] = { parameter(to_sort(domain)), parameter(to_sort(range)) }; sort * ty = mk_c(c)->m().mk_sort(mk_c(c)->get_array_fid(), ARRAY_SORT, 2, params); mk_c(c)->save_ast_trail(ty); RETURN_Z3(of_sort(ty)); Z3_CATCH_RETURN(nullptr); } Z3_sort Z3_API Z3_mk_array_sort_n(Z3_context c, unsigned n, Z3_sort const* domain, Z3_sort range) { Z3_TRY; LOG_Z3_mk_array_sort_n(c, n, domain, range); RESET_ERROR_CODE(); vector params; for (unsigned i = 0; i < n; ++i) params.push_back(parameter(to_sort(domain[i]))); params.push_back(parameter(to_sort(range))); sort * ty = mk_c(c)->m().mk_sort(mk_c(c)->get_array_fid(), ARRAY_SORT, params.size(), params.c_ptr()); mk_c(c)->save_ast_trail(ty); RETURN_Z3(of_sort(ty)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_select(Z3_context c, Z3_ast a, Z3_ast i) { Z3_TRY; LOG_Z3_mk_select(c, a, i); RESET_ERROR_CODE(); ast_manager & m = mk_c(c)->m(); expr * _a = to_expr(a); expr * _i = to_expr(i); sort * a_ty = m.get_sort(_a); sort * i_ty = m.get_sort(_i); if (a_ty->get_family_id() != mk_c(c)->get_array_fid()) { SET_ERROR_CODE(Z3_SORT_ERROR, nullptr); RETURN_Z3(nullptr); } sort * domain[2] = {a_ty, i_ty}; func_decl * d = m.mk_func_decl(mk_c(c)->get_array_fid(), OP_SELECT, 2, a_ty->get_parameters(), 2, domain); expr * args[2] = {_a, _i}; app * r = m.mk_app(d, 2, args); mk_c(c)->save_ast_trail(r); check_sorts(c, r); RETURN_Z3(of_ast(r)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_select_n(Z3_context c, Z3_ast a, unsigned n, Z3_ast const* idxs) { Z3_TRY; LOG_Z3_mk_select_n(c, a, n, idxs); RESET_ERROR_CODE(); ast_manager & m = mk_c(c)->m(); expr * _a = to_expr(a); // expr * _i = to_expr(i); sort * a_ty = m.get_sort(_a); // sort * i_ty = m.get_sort(_i); if (a_ty->get_family_id() != mk_c(c)->get_array_fid()) { SET_ERROR_CODE(Z3_SORT_ERROR, nullptr); RETURN_Z3(nullptr); } ptr_vector domain; ptr_vector args; args.push_back(_a); domain.push_back(a_ty); for (unsigned i = 0; i < n; ++i) { args.push_back(to_expr(idxs[i])); domain.push_back(m.get_sort(to_expr(idxs[i]))); } func_decl * d = m.mk_func_decl(mk_c(c)->get_array_fid(), OP_SELECT, 2, a_ty->get_parameters(), domain.size(), domain.c_ptr()); app * r = m.mk_app(d, args.size(), args.c_ptr()); mk_c(c)->save_ast_trail(r); check_sorts(c, r); RETURN_Z3(of_ast(r)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_store(Z3_context c, Z3_ast a, Z3_ast i, Z3_ast v) { Z3_TRY; LOG_Z3_mk_store(c, a, i, v); RESET_ERROR_CODE(); ast_manager & m = mk_c(c)->m(); expr * _a = to_expr(a); expr * _i = to_expr(i); expr * _v = to_expr(v); sort * a_ty = m.get_sort(_a); sort * i_ty = m.get_sort(_i); sort * v_ty = m.get_sort(_v); if (a_ty->get_family_id() != mk_c(c)->get_array_fid()) { SET_ERROR_CODE(Z3_SORT_ERROR, nullptr); RETURN_Z3(nullptr); } sort * domain[3] = {a_ty, i_ty, v_ty}; func_decl * d = m.mk_func_decl(mk_c(c)->get_array_fid(), OP_STORE, 2, a_ty->get_parameters(), 3, domain); expr * args[3] = {_a, _i, _v}; app * r = m.mk_app(d, 3, args); mk_c(c)->save_ast_trail(r); check_sorts(c, r); RETURN_Z3(of_ast(r)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_store_n(Z3_context c, Z3_ast a, unsigned n, Z3_ast const* idxs, Z3_ast v) { Z3_TRY; LOG_Z3_mk_store_n(c, a, n, idxs, v); RESET_ERROR_CODE(); ast_manager & m = mk_c(c)->m(); expr * _a = to_expr(a); expr * _v = to_expr(v); sort * a_ty = m.get_sort(_a); sort * v_ty = m.get_sort(_v); if (a_ty->get_family_id() != mk_c(c)->get_array_fid()) { SET_ERROR_CODE(Z3_SORT_ERROR, nullptr); RETURN_Z3(nullptr); } ptr_vector domain; ptr_vector args; args.push_back(_a); domain.push_back(a_ty); for (unsigned i = 0; i < n; ++i) { args.push_back(to_expr(idxs[i])); domain.push_back(m.get_sort(to_expr(idxs[i]))); } args.push_back(_v); domain.push_back(v_ty); func_decl * d = m.mk_func_decl(mk_c(c)->get_array_fid(), OP_STORE, 2, a_ty->get_parameters(), domain.size(), domain.c_ptr()); app * r = m.mk_app(d, args.size(), args.c_ptr()); mk_c(c)->save_ast_trail(r); check_sorts(c, r); RETURN_Z3(of_ast(r)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_map(Z3_context c, Z3_func_decl f, unsigned n, Z3_ast const* args) { Z3_TRY; LOG_Z3_mk_map(c, f, n, args); RESET_ERROR_CODE(); if (n == 0) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } ast_manager & m = mk_c(c)->m(); func_decl* _f = to_func_decl(f); expr* const* _args = to_exprs(n, args); ptr_vector domain; for (unsigned i = 0; i < n; ++i) { domain.push_back(m.get_sort(_args[i])); } parameter param(_f); func_decl * d = m.mk_func_decl(mk_c(c)->get_array_fid(), OP_ARRAY_MAP, 1, ¶m, n, domain.c_ptr()); app* r = m.mk_app(d, n, _args); mk_c(c)->save_ast_trail(r); check_sorts(c, r); RETURN_Z3(of_ast(r)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_const_array(Z3_context c, Z3_sort domain, Z3_ast v) { Z3_TRY; LOG_Z3_mk_const_array(c, domain, v); RESET_ERROR_CODE(); ast_manager & m = mk_c(c)->m(); expr * _v = to_expr(v); sort * _range = m.get_sort(_v); sort * _domain = to_sort(domain); parameter params[2] = { parameter(_domain), parameter(_range) }; sort * a_ty = mk_c(c)->m().mk_sort(mk_c(c)->get_array_fid(), ARRAY_SORT, 2, params); parameter param(a_ty); func_decl* cd = m.mk_func_decl(mk_c(c)->get_array_fid(), OP_CONST_ARRAY, 1, ¶m, 1, &_range); app * r = m.mk_app(cd, 1, &_v); mk_c(c)->save_ast_trail(r); check_sorts(c, r); RETURN_Z3(of_ast(r)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_array_default(Z3_context c, Z3_ast array) { Z3_TRY; LOG_Z3_mk_array_default(c, array); RESET_ERROR_CODE(); ast_manager & m = mk_c(c)->m(); expr * _a = to_expr(array); func_decl * f = m.mk_func_decl(mk_c(c)->get_array_fid(), OP_ARRAY_DEFAULT, 0, nullptr, 1, &_a); app * r = m.mk_app(f, 1, &_a); mk_c(c)->save_ast_trail(r); check_sorts(c, r); RETURN_Z3(of_ast(r)); Z3_CATCH_RETURN(nullptr); } Z3_ast mk_app_array_core(Z3_context c, Z3_sort domain, Z3_ast v) { RESET_ERROR_CODE(); ast_manager & m = mk_c(c)->m(); expr * _v = to_expr(v); sort * _range = m.get_sort(_v); sort * _domain = to_sort(domain); parameter params[2] = { parameter(_domain), parameter(_range) }; sort * a_ty = mk_c(c)->m().mk_sort(mk_c(c)->get_array_fid(), ARRAY_SORT, 2, params); parameter param(a_ty); func_decl * cd = m.mk_func_decl(mk_c(c)->get_array_fid(), OP_CONST_ARRAY, 1, ¶m, 1, &_range); app * r = m.mk_app(cd, 1, &_v); mk_c(c)->save_ast_trail(r); check_sorts(c, r); return of_ast(r); } Z3_sort Z3_API Z3_mk_set_sort(Z3_context c, Z3_sort ty) { Z3_TRY; return Z3_mk_array_sort(c, ty, Z3_mk_bool_sort(c)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_empty_set(Z3_context c, Z3_sort domain) { Z3_TRY; LOG_Z3_mk_empty_set(c, domain); RESET_ERROR_CODE(); Z3_ast r = mk_app_array_core(c, domain, Z3_mk_false(c)); RETURN_Z3(r); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_full_set(Z3_context c, Z3_sort domain) { Z3_TRY; LOG_Z3_mk_full_set(c, domain); RESET_ERROR_CODE(); Z3_ast r = mk_app_array_core(c, domain, Z3_mk_true(c)); RETURN_Z3(r); Z3_CATCH_RETURN(nullptr); } MK_NARY(Z3_mk_set_union, mk_c(c)->get_array_fid(), OP_SET_UNION, SKIP); MK_NARY(Z3_mk_set_intersect, mk_c(c)->get_array_fid(), OP_SET_INTERSECT, SKIP); MK_BINARY(Z3_mk_set_difference, mk_c(c)->get_array_fid(), OP_SET_DIFFERENCE, SKIP); MK_UNARY(Z3_mk_set_complement, mk_c(c)->get_array_fid(), OP_SET_COMPLEMENT, SKIP); MK_BINARY(Z3_mk_set_subset, mk_c(c)->get_array_fid(), OP_SET_SUBSET, SKIP); MK_BINARY(Z3_mk_array_ext, mk_c(c)->get_array_fid(), OP_ARRAY_EXT, SKIP); MK_BINARY(Z3_mk_set_has_size, mk_c(c)->get_array_fid(), OP_SET_HAS_SIZE, SKIP); Z3_ast Z3_API Z3_mk_as_array(Z3_context c, Z3_func_decl f) { Z3_TRY; LOG_Z3_mk_as_array(c, f); RESET_ERROR_CODE(); ast_manager & m = mk_c(c)->m(); array_util a(m); app * r = a.mk_as_array(to_func_decl(f)); mk_c(c)->save_ast_trail(r); return of_ast(r); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_mk_set_member(Z3_context c, Z3_ast elem, Z3_ast set) { return Z3_mk_select(c, set, elem); } Z3_ast Z3_mk_set_add(Z3_context c, Z3_ast set, Z3_ast elem) { return Z3_mk_store(c, set, elem, Z3_mk_true(c)); } Z3_ast Z3_mk_set_del(Z3_context c, Z3_ast set, Z3_ast elem) { return Z3_mk_store(c, set, elem, Z3_mk_false(c)); } Z3_sort Z3_API Z3_get_array_sort_domain(Z3_context c, Z3_sort t) { Z3_TRY; LOG_Z3_get_array_sort_domain(c, t); RESET_ERROR_CODE(); CHECK_VALID_AST(t, nullptr); if (to_sort(t)->get_family_id() == mk_c(c)->get_array_fid() && to_sort(t)->get_decl_kind() == ARRAY_SORT) { Z3_sort r = reinterpret_cast(to_sort(t)->get_parameter(0).get_ast()); RETURN_Z3(r); } SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); Z3_CATCH_RETURN(nullptr); } Z3_sort Z3_API Z3_get_array_sort_range(Z3_context c, Z3_sort t) { Z3_TRY; LOG_Z3_get_array_sort_range(c, t); RESET_ERROR_CODE(); CHECK_VALID_AST(t, nullptr); if (to_sort(t)->get_family_id() == mk_c(c)->get_array_fid() && to_sort(t)->get_decl_kind() == ARRAY_SORT) { unsigned n = to_sort(t)->get_num_parameters(); Z3_sort r = reinterpret_cast(to_sort(t)->get_parameter(n-1).get_ast()); RETURN_Z3(r); } SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); Z3_CATCH_RETURN(nullptr); } }; z3-z3-4.8.7/src/api/api_ast.cpp000066400000000000000000001425221356505360400161210ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_ast.cpp Abstract: Basic API for ASTs Author: Leonardo de Moura (leonardo) 2012-02-29. Revision History: --*/ #include #include "api/api_log_macros.h" #include "api/api_context.h" #include "api/api_util.h" #include "ast/well_sorted.h" #include "ast/arith_decl_plugin.h" #include "ast/bv_decl_plugin.h" #include "ast/datatype_decl_plugin.h" #include "ast/array_decl_plugin.h" #include "ast/pb_decl_plugin.h" #include "ast/ast_translation.h" #include "ast/ast_pp.h" #include "ast/ast_ll_pp.h" #include "ast/ast_smt_pp.h" #include "ast/ast_smt2_pp.h" #include "ast/rewriter/th_rewriter.h" #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" #include "util/scoped_timer.h" #include "ast/pp_params.hpp" #include "ast/expr_abstract.h" extern bool is_numeral_sort(Z3_context c, Z3_sort ty); extern "C" { Z3_symbol Z3_API Z3_mk_int_symbol(Z3_context c, int i) { Z3_TRY; LOG_Z3_mk_int_symbol(c, i); RESET_ERROR_CODE(); if (i < 0 || (size_t)i >= (SIZE_MAX >> PTR_ALIGNMENT)) { SET_ERROR_CODE(Z3_IOB, nullptr); return nullptr; } Z3_symbol result = of_symbol(symbol(i)); return result; Z3_CATCH_RETURN(nullptr); } Z3_symbol Z3_API Z3_mk_string_symbol(Z3_context c, char const * str) { Z3_TRY; LOG_Z3_mk_string_symbol(c, str); RESET_ERROR_CODE(); symbol s; if (str == nullptr || *str == 0) s = symbol::null; else s = symbol(str); Z3_symbol result = of_symbol(s); return result; Z3_CATCH_RETURN(nullptr); } bool Z3_API Z3_is_eq_sort(Z3_context c, Z3_sort s1, Z3_sort s2) { RESET_ERROR_CODE(); return s1 == s2; } Z3_sort Z3_API Z3_mk_uninterpreted_sort(Z3_context c, Z3_symbol name) { Z3_TRY; LOG_Z3_mk_uninterpreted_sort(c, name); RESET_ERROR_CODE(); sort* ty = mk_c(c)->m().mk_uninterpreted_sort(to_symbol(name)); mk_c(c)->save_ast_trail(ty); RETURN_Z3(of_sort(ty)); Z3_CATCH_RETURN(nullptr); } bool Z3_API Z3_is_eq_ast(Z3_context c, Z3_ast s1, Z3_ast s2) { RESET_ERROR_CODE(); return s1 == s2; } bool Z3_API Z3_is_eq_func_decl(Z3_context c, Z3_func_decl s1, Z3_func_decl s2) { RESET_ERROR_CODE(); return s1 == s2; } Z3_func_decl Z3_API Z3_mk_func_decl(Z3_context c, Z3_symbol s, unsigned domain_size, Z3_sort const* domain, Z3_sort range) { Z3_TRY; LOG_Z3_mk_func_decl(c, s, domain_size, domain, range); RESET_ERROR_CODE(); func_decl* d = mk_c(c)->m().mk_func_decl(to_symbol(s), domain_size, to_sorts(domain), to_sort(range)); mk_c(c)->save_ast_trail(d); RETURN_Z3(of_func_decl(d)); Z3_CATCH_RETURN(nullptr); } Z3_func_decl Z3_API Z3_mk_rec_func_decl(Z3_context c, Z3_symbol s, unsigned domain_size, Z3_sort const* domain, Z3_sort range) { Z3_TRY; LOG_Z3_mk_rec_func_decl(c, s, domain_size, domain, range); RESET_ERROR_CODE(); // recfun::promise_def def = mk_c(c)->recfun().get_plugin().mk_def(to_symbol(s), domain_size, to_sorts(domain), to_sort(range)); func_decl* d = def.get_def()->get_decl(); mk_c(c)->save_ast_trail(d); RETURN_Z3(of_func_decl(d)); Z3_CATCH_RETURN(nullptr); } void Z3_API Z3_add_rec_def(Z3_context c, Z3_func_decl f, unsigned n, Z3_ast args[], Z3_ast body) { Z3_TRY; LOG_Z3_add_rec_def(c, f, n, args, body); func_decl* d = to_func_decl(f); ast_manager& m = mk_c(c)->m(); recfun::decl::plugin& p = mk_c(c)->recfun().get_plugin(); expr_ref abs_body(m); expr_ref_vector _args(m); var_ref_vector _vars(m); for (unsigned i = 0; i < n; ++i) { _args.push_back(to_expr(args[i])); _vars.push_back(m.mk_var(n - i - 1, m.get_sort(_args.back()))); if (m.get_sort(_args.back()) != d->get_domain(i)) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return; } } expr_abstract(m, 0, n, _args.c_ptr(), to_expr(body), abs_body); recfun::promise_def pd = p.get_promise_def(d); if (!pd.get_def()) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return; } if (m.get_sort(abs_body) != d->get_range()) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return; } recfun_replace replace(m); p.set_definition(replace, pd, n, _vars.c_ptr(), abs_body); Z3_CATCH; } Z3_ast Z3_API Z3_mk_app(Z3_context c, Z3_func_decl d, unsigned num_args, Z3_ast const * args) { Z3_TRY; LOG_Z3_mk_app(c, d, num_args, args); RESET_ERROR_CODE(); ptr_buffer arg_list; for (unsigned i = 0; i < num_args; ++i) { arg_list.push_back(to_expr(args[i])); } func_decl* _d = reinterpret_cast(d); app* a = mk_c(c)->m().mk_app(_d, num_args, arg_list.c_ptr()); mk_c(c)->save_ast_trail(a); check_sorts(c, a); RETURN_Z3(of_ast(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_const(Z3_context c, Z3_symbol s, Z3_sort ty) { Z3_TRY; LOG_Z3_mk_const(c, s, ty); RESET_ERROR_CODE(); app* a = mk_c(c)->m().mk_const(mk_c(c)->m().mk_const_decl(to_symbol(s), to_sort(ty))); mk_c(c)->save_ast_trail(a); RETURN_Z3(of_ast(a)); Z3_CATCH_RETURN(nullptr); } Z3_func_decl Z3_API Z3_mk_fresh_func_decl(Z3_context c, const char * prefix, unsigned domain_size, Z3_sort const domain[], Z3_sort range) { Z3_TRY; LOG_Z3_mk_fresh_func_decl(c, prefix, domain_size, domain, range); RESET_ERROR_CODE(); if (prefix == nullptr) { prefix = ""; } func_decl* d = mk_c(c)->m().mk_fresh_func_decl(prefix, domain_size, reinterpret_cast(domain), to_sort(range), false); mk_c(c)->save_ast_trail(d); RETURN_Z3(of_func_decl(d)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fresh_const(Z3_context c, const char * prefix, Z3_sort ty) { Z3_TRY; LOG_Z3_mk_fresh_const(c, prefix, ty); RESET_ERROR_CODE(); if (prefix == nullptr) { prefix = ""; } app* a = mk_c(c)->m().mk_fresh_const(prefix, to_sort(ty), false); mk_c(c)->save_ast_trail(a); RETURN_Z3(of_ast(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_true(Z3_context c) { Z3_TRY; LOG_Z3_mk_true(c); RESET_ERROR_CODE(); Z3_ast r = of_ast(mk_c(c)->m().mk_true()); RETURN_Z3(r); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_false(Z3_context c) { Z3_TRY; LOG_Z3_mk_false(c); RESET_ERROR_CODE(); Z3_ast r = of_ast(mk_c(c)->m().mk_false()); RETURN_Z3(r); Z3_CATCH_RETURN(nullptr); } MK_UNARY(Z3_mk_not, mk_c(c)->get_basic_fid(), OP_NOT, SKIP); MK_BINARY(Z3_mk_eq, mk_c(c)->get_basic_fid(), OP_EQ, SKIP); MK_NARY(Z3_mk_distinct, mk_c(c)->get_basic_fid(), OP_DISTINCT, SKIP); MK_BINARY(Z3_mk_iff, mk_c(c)->get_basic_fid(), OP_EQ, SKIP); MK_BINARY(Z3_mk_implies, mk_c(c)->get_basic_fid(), OP_IMPLIES, SKIP); MK_BINARY(Z3_mk_xor, mk_c(c)->get_basic_fid(), OP_XOR, SKIP); MK_NARY(Z3_mk_and, mk_c(c)->get_basic_fid(), OP_AND, SKIP); MK_NARY(Z3_mk_or, mk_c(c)->get_basic_fid(), OP_OR, SKIP); Z3_ast mk_ite_core(Z3_context c, Z3_ast t1, Z3_ast t2, Z3_ast t3) { expr * result = mk_c(c)->m().mk_ite(to_expr(t1), to_expr(t2), to_expr(t3)); mk_c(c)->save_ast_trail(result); check_sorts(c, result); return of_ast(result); } Z3_ast Z3_API Z3_mk_ite(Z3_context c, Z3_ast t1, Z3_ast t2, Z3_ast t3) { Z3_TRY; LOG_Z3_mk_ite(c, t1, t2, t3); RESET_ERROR_CODE(); Z3_ast r = mk_ite_core(c, t1, t2, t3); RETURN_Z3(r); Z3_CATCH_RETURN(nullptr); } Z3_sort Z3_API Z3_mk_bool_sort(Z3_context c) { Z3_TRY; LOG_Z3_mk_bool_sort(c); RESET_ERROR_CODE(); Z3_sort r = of_sort(mk_c(c)->m().mk_sort(mk_c(c)->m().get_basic_family_id(), BOOL_SORT)); RETURN_Z3(r); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_app_to_ast(Z3_context c, Z3_app a) { RESET_ERROR_CODE(); return (Z3_ast)(a); } Z3_ast Z3_API Z3_sort_to_ast(Z3_context c, Z3_sort s) { RESET_ERROR_CODE(); return (Z3_ast)(s); } Z3_ast Z3_API Z3_func_decl_to_ast(Z3_context c, Z3_func_decl f) { RESET_ERROR_CODE(); return (Z3_ast)(f); } // ------------------------ unsigned Z3_API Z3_get_ast_id(Z3_context c, Z3_ast t) { LOG_Z3_get_ast_id(c, t); RESET_ERROR_CODE(); return to_expr(t)->get_id(); } unsigned Z3_API Z3_get_func_decl_id(Z3_context c, Z3_func_decl f) { LOG_Z3_get_func_decl_id(c, f); RESET_ERROR_CODE(); return to_func_decl(f)->get_id(); } unsigned Z3_API Z3_get_sort_id(Z3_context c, Z3_sort s) { LOG_Z3_get_sort_id(c, s); RESET_ERROR_CODE(); return to_sort(s)->get_id(); } bool Z3_API Z3_is_well_sorted(Z3_context c, Z3_ast t) { Z3_TRY; LOG_Z3_is_well_sorted(c, t); RESET_ERROR_CODE(); return is_well_sorted(mk_c(c)->m(), to_expr(t)); Z3_CATCH_RETURN(false); } Z3_symbol_kind Z3_API Z3_get_symbol_kind(Z3_context c, Z3_symbol s) { Z3_TRY; LOG_Z3_get_symbol_kind(c, s); RESET_ERROR_CODE(); symbol _s = to_symbol(s); return _s.is_numerical() ? Z3_INT_SYMBOL : Z3_STRING_SYMBOL; Z3_CATCH_RETURN(Z3_INT_SYMBOL); } int Z3_API Z3_get_symbol_int(Z3_context c, Z3_symbol s) { Z3_TRY; LOG_Z3_get_symbol_int(c, s); RESET_ERROR_CODE(); symbol _s = to_symbol(s); if (_s.is_numerical()) { return _s.get_num(); } SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return -1; Z3_CATCH_RETURN(-1); } Z3_API char const * Z3_get_symbol_string(Z3_context c, Z3_symbol s) { Z3_TRY; LOG_Z3_get_symbol_string(c, s); RESET_ERROR_CODE(); symbol _s = to_symbol(s); if (_s.is_numerical()) { std::ostringstream buffer; buffer << _s.get_num(); return mk_c(c)->mk_external_string(buffer.str()); } else { return mk_c(c)->mk_external_string(_s.bare_str()); } Z3_CATCH_RETURN(""); } Z3_ast_kind Z3_API Z3_get_ast_kind(Z3_context c, Z3_ast a) { Z3_TRY; LOG_Z3_get_ast_kind(c, a); RESET_ERROR_CODE(); CHECK_VALID_AST(a, Z3_UNKNOWN_AST); ast * _a = to_expr(a); switch (_a->get_kind()) { case AST_APP: { expr * e = to_expr(_a); // Real algebraic numbers are not considered Z3_NUMERAL_AST if (is_numeral_sort(c, of_sort(mk_c(c)->m().get_sort(e))) && mk_c(c)->m().is_unique_value(e)) return Z3_NUMERAL_AST; return Z3_APP_AST; } case AST_VAR: return Z3_VAR_AST; case AST_QUANTIFIER: return Z3_QUANTIFIER_AST; case AST_SORT: return Z3_SORT_AST; case AST_FUNC_DECL: return Z3_FUNC_DECL_AST; default: return Z3_UNKNOWN_AST; } Z3_CATCH_RETURN(Z3_UNKNOWN_AST); } unsigned Z3_API Z3_get_ast_hash(Z3_context c, Z3_ast a) { LOG_Z3_get_ast_hash(c, a); RESET_ERROR_CODE(); return to_ast(a)->hash(); } bool Z3_API Z3_is_app(Z3_context c, Z3_ast a) { LOG_Z3_is_app(c, a); RESET_ERROR_CODE(); return a != nullptr && is_app(reinterpret_cast(a)); } Z3_app Z3_API Z3_to_app(Z3_context c, Z3_ast a) { LOG_Z3_to_app(c, a); RESET_ERROR_CODE(); SASSERT(is_app(reinterpret_cast(a))); RETURN_Z3(of_app(reinterpret_cast(a))); } Z3_func_decl Z3_API Z3_to_func_decl(Z3_context c, Z3_ast a) { LOG_Z3_to_func_decl(c, a); RESET_ERROR_CODE(); SASSERT(is_func_decl(reinterpret_cast(a))); RETURN_Z3(of_func_decl(reinterpret_cast(a))); } Z3_func_decl Z3_API Z3_get_app_decl(Z3_context c, Z3_app a) { LOG_Z3_get_app_decl(c, a); RESET_ERROR_CODE(); if (!is_app(reinterpret_cast(a))) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } RETURN_Z3(of_func_decl(to_app(a)->get_decl())); } unsigned Z3_API Z3_get_app_num_args(Z3_context c, Z3_app a) { LOG_Z3_get_app_num_args(c, a); RESET_ERROR_CODE(); return to_app(a)->get_num_args(); } Z3_ast Z3_API Z3_get_app_arg(Z3_context c, Z3_app a, unsigned i) { LOG_Z3_get_app_arg(c, a, i); RESET_ERROR_CODE(); if (!is_app(reinterpret_cast(a))) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } if (i >= to_app(a)->get_num_args()) { SET_ERROR_CODE(Z3_IOB, nullptr); RETURN_Z3(nullptr); } RETURN_Z3(of_ast(to_app(a)->get_arg(i))); } Z3_symbol Z3_API Z3_get_decl_name(Z3_context c, Z3_func_decl d) { LOG_Z3_get_decl_name(c, d); RESET_ERROR_CODE(); CHECK_VALID_AST(d, nullptr); return of_symbol(to_func_decl(d)->get_name()); } unsigned Z3_API Z3_get_decl_num_parameters(Z3_context c, Z3_func_decl d) { LOG_Z3_get_decl_num_parameters(c, d); RESET_ERROR_CODE(); CHECK_VALID_AST(d, 0); return to_func_decl(d)->get_num_parameters(); } Z3_parameter_kind Z3_API Z3_get_decl_parameter_kind(Z3_context c, Z3_func_decl d, unsigned idx) { Z3_TRY; LOG_Z3_get_decl_parameter_kind(c, d, idx); RESET_ERROR_CODE(); CHECK_VALID_AST(d, Z3_PARAMETER_INT); if (idx >= to_func_decl(d)->get_num_parameters()) { SET_ERROR_CODE(Z3_IOB, nullptr); return Z3_PARAMETER_INT; } parameter const& p = to_func_decl(d)->get_parameters()[idx]; if (p.is_int()) { return Z3_PARAMETER_INT; } if (p.is_double()) { return Z3_PARAMETER_DOUBLE; } if (p.is_symbol()) { return Z3_PARAMETER_SYMBOL; } if (p.is_rational()) { return Z3_PARAMETER_RATIONAL; } if (p.is_ast() && is_sort(p.get_ast())) { return Z3_PARAMETER_SORT; } if (p.is_ast() && is_expr(p.get_ast())) { return Z3_PARAMETER_AST; } SASSERT(p.is_ast() && is_func_decl(p.get_ast())); return Z3_PARAMETER_FUNC_DECL; Z3_CATCH_RETURN(Z3_PARAMETER_INT); } int Z3_API Z3_get_decl_int_parameter(Z3_context c, Z3_func_decl d, unsigned idx) { Z3_TRY; LOG_Z3_get_decl_int_parameter(c, d, idx); RESET_ERROR_CODE(); CHECK_VALID_AST(d, 0); if (idx >= to_func_decl(d)->get_num_parameters()) { SET_ERROR_CODE(Z3_IOB, nullptr); return 0; } parameter const& p = to_func_decl(d)->get_parameters()[idx]; if (!p.is_int()) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return 0; } return p.get_int(); Z3_CATCH_RETURN(0); } double Z3_API Z3_get_decl_double_parameter(Z3_context c, Z3_func_decl d, unsigned idx) { Z3_TRY; LOG_Z3_get_decl_double_parameter(c, d, idx); RESET_ERROR_CODE(); CHECK_VALID_AST(d, 0); if (idx >= to_func_decl(d)->get_num_parameters()) { SET_ERROR_CODE(Z3_IOB, nullptr); return 0; } parameter const& p = to_func_decl(d)->get_parameters()[idx]; if (!p.is_double()) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return 0; } return p.get_double(); Z3_CATCH_RETURN(0.0); } Z3_symbol Z3_API Z3_get_decl_symbol_parameter(Z3_context c, Z3_func_decl d, unsigned idx) { Z3_TRY; LOG_Z3_get_decl_symbol_parameter(c, d, idx); RESET_ERROR_CODE(); CHECK_VALID_AST(d, nullptr); if (idx >= to_func_decl(d)->get_num_parameters()) { SET_ERROR_CODE(Z3_IOB, nullptr); return nullptr; } parameter const& p = to_func_decl(d)->get_parameters()[idx]; if (!p.is_symbol()) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return nullptr; } return of_symbol(p.get_symbol()); Z3_CATCH_RETURN(nullptr); } Z3_sort Z3_API Z3_get_decl_sort_parameter(Z3_context c, Z3_func_decl d, unsigned idx) { Z3_TRY; LOG_Z3_get_decl_sort_parameter(c, d, idx); RESET_ERROR_CODE(); CHECK_VALID_AST(d, nullptr); if (idx >= to_func_decl(d)->get_num_parameters()) { SET_ERROR_CODE(Z3_IOB, nullptr); RETURN_Z3(nullptr); } parameter const& p = to_func_decl(d)->get_parameters()[idx]; if (!p.is_ast() || !is_sort(p.get_ast())) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } RETURN_Z3(of_sort(to_sort(p.get_ast()))); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_get_decl_ast_parameter(Z3_context c, Z3_func_decl d, unsigned idx) { Z3_TRY; LOG_Z3_get_decl_ast_parameter(c, d, idx); RESET_ERROR_CODE(); CHECK_VALID_AST(d, nullptr); if (idx >= to_func_decl(d)->get_num_parameters()) { SET_ERROR_CODE(Z3_IOB, nullptr); RETURN_Z3(nullptr); } parameter const& p = to_func_decl(d)->get_parameters()[idx]; if (!p.is_ast()) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } RETURN_Z3(of_ast(p.get_ast())); Z3_CATCH_RETURN(nullptr); } Z3_func_decl Z3_API Z3_get_decl_func_decl_parameter(Z3_context c, Z3_func_decl d, unsigned idx) { Z3_TRY; LOG_Z3_get_decl_func_decl_parameter(c, d, idx); RESET_ERROR_CODE(); CHECK_VALID_AST(d, nullptr); if (idx >= to_func_decl(d)->get_num_parameters()) { SET_ERROR_CODE(Z3_IOB, nullptr); RETURN_Z3(nullptr); } parameter const& p = to_func_decl(d)->get_parameters()[idx]; if (!p.is_ast() || !is_func_decl(p.get_ast())) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } RETURN_Z3(of_func_decl(to_func_decl(p.get_ast()))); Z3_CATCH_RETURN(nullptr); } Z3_string Z3_API Z3_get_decl_rational_parameter(Z3_context c, Z3_func_decl d, unsigned idx) { Z3_TRY; LOG_Z3_get_decl_rational_parameter(c, d, idx); RESET_ERROR_CODE(); CHECK_VALID_AST(d, ""); if (idx >= to_func_decl(d)->get_num_parameters()) { SET_ERROR_CODE(Z3_IOB, nullptr); return ""; } parameter const& p = to_func_decl(d)->get_parameters()[idx]; if (!p.is_rational()) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return ""; } return mk_c(c)->mk_external_string(p.get_rational().to_string()); Z3_CATCH_RETURN(""); } Z3_symbol Z3_API Z3_get_sort_name(Z3_context c, Z3_sort t) { Z3_TRY; LOG_Z3_get_sort_name(c, t); RESET_ERROR_CODE(); CHECK_VALID_AST(t, nullptr); return of_symbol(to_sort(t)->get_name()); Z3_CATCH_RETURN(nullptr); } Z3_sort Z3_API Z3_get_sort(Z3_context c, Z3_ast a) { Z3_TRY; LOG_Z3_get_sort(c, a); RESET_ERROR_CODE(); CHECK_IS_EXPR(a, nullptr); Z3_sort r = of_sort(mk_c(c)->m().get_sort(to_expr(a))); RETURN_Z3(r); Z3_CATCH_RETURN(nullptr); } unsigned Z3_API Z3_get_arity(Z3_context c, Z3_func_decl d) { Z3_TRY; LOG_Z3_get_arity(c, d); RESET_ERROR_CODE(); CHECK_VALID_AST(d, 0); return to_func_decl(d)->get_arity(); Z3_CATCH_RETURN(0); } unsigned Z3_API Z3_get_domain_size(Z3_context c, Z3_func_decl d) { Z3_TRY; LOG_Z3_get_domain_size(c, d); RESET_ERROR_CODE(); CHECK_VALID_AST(d, 0); return to_func_decl(d)->get_arity(); Z3_CATCH_RETURN(0); } Z3_sort Z3_API Z3_get_domain(Z3_context c, Z3_func_decl d, unsigned i) { Z3_TRY; LOG_Z3_get_domain(c, d, i); RESET_ERROR_CODE(); CHECK_VALID_AST(d, nullptr); if (i >= to_func_decl(d)->get_arity()) { SET_ERROR_CODE(Z3_IOB, nullptr); RETURN_Z3(nullptr); } Z3_sort r = of_sort(to_func_decl(d)->get_domain(i)); RETURN_Z3(r); Z3_CATCH_RETURN(nullptr); } Z3_sort Z3_API Z3_get_range(Z3_context c, Z3_func_decl d) { Z3_TRY; LOG_Z3_get_range(c, d); RESET_ERROR_CODE(); CHECK_VALID_AST(d, nullptr); Z3_sort r = of_sort(to_func_decl(d)->get_range()); RETURN_Z3(r); Z3_CATCH_RETURN(nullptr); } Z3_sort_kind Z3_get_sort_kind(Z3_context c, Z3_sort t) { LOG_Z3_get_sort_kind(c, t); RESET_ERROR_CODE(); CHECK_VALID_AST(t, Z3_UNKNOWN_SORT); family_id fid = to_sort(t)->get_family_id(); decl_kind k = to_sort(t)->get_decl_kind(); if (mk_c(c)->m().is_uninterp(to_sort(t))) { return Z3_UNINTERPRETED_SORT; } else if (fid == mk_c(c)->m().get_basic_family_id() && k == BOOL_SORT) { return Z3_BOOL_SORT; } else if (fid == mk_c(c)->get_arith_fid() && k == INT_SORT) { return Z3_INT_SORT; } else if (fid == mk_c(c)->get_arith_fid() && k == REAL_SORT) { return Z3_REAL_SORT; } else if (fid == mk_c(c)->get_bv_fid() && k == BV_SORT) { return Z3_BV_SORT; } else if (fid == mk_c(c)->get_array_fid() && k == ARRAY_SORT) { return Z3_ARRAY_SORT; } else if (fid == mk_c(c)->get_dt_fid() && k == DATATYPE_SORT) { return Z3_DATATYPE_SORT; } else if (fid == mk_c(c)->get_datalog_fid() && k == datalog::DL_RELATION_SORT) { return Z3_RELATION_SORT; } else if (fid == mk_c(c)->get_datalog_fid() && k == datalog::DL_FINITE_SORT) { return Z3_FINITE_DOMAIN_SORT; } else if (fid == mk_c(c)->get_fpa_fid() && k == FLOATING_POINT_SORT) { return Z3_FLOATING_POINT_SORT; } else if (fid == mk_c(c)->get_fpa_fid() && k == ROUNDING_MODE_SORT) { return Z3_ROUNDING_MODE_SORT; } else if (fid == mk_c(c)->get_seq_fid() && k == SEQ_SORT) { return Z3_SEQ_SORT; } else if (fid == mk_c(c)->get_seq_fid() && k == RE_SORT) { return Z3_RE_SORT; } else { return Z3_UNKNOWN_SORT; } } Z3_lbool Z3_API Z3_get_bool_value(Z3_context c, Z3_ast a) { Z3_TRY; LOG_Z3_get_bool_value(c, a); RESET_ERROR_CODE(); CHECK_IS_EXPR(a, Z3_L_UNDEF); ast_manager & m = mk_c(c)->m(); ast * n = to_ast(a); if (m.is_true(to_expr(n))) return Z3_L_TRUE; if (m.is_false(to_expr(n))) return Z3_L_FALSE; return Z3_L_UNDEF; Z3_CATCH_RETURN(Z3_L_UNDEF); } static Z3_ast simplify(Z3_context c, Z3_ast _a, Z3_params _p) { Z3_TRY; RESET_ERROR_CODE(); ast_manager & m = mk_c(c)->m(); expr * a = to_expr(_a); params_ref p = to_param_ref(_p); 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); { scoped_ctrl_c ctrlc(eh, false, use_ctrl_c); scoped_timer timer(timeout, &eh); try { m_rw(a, result); } catch (z3_exception & ex) { mk_c(c)->handle_exception(ex); return nullptr; } } mk_c(c)->save_ast_trail(result); return of_ast(result.get()); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_simplify(Z3_context c, Z3_ast _a) { LOG_Z3_simplify(c, _a); RETURN_Z3(simplify(c, _a, nullptr)); } Z3_ast Z3_API Z3_simplify_ex(Z3_context c, Z3_ast _a, Z3_params p) { LOG_Z3_simplify_ex(c, _a, p); RETURN_Z3(simplify(c, _a, p)); } Z3_string Z3_API Z3_simplify_get_help(Z3_context c) { Z3_TRY; LOG_Z3_simplify_get_help(c); RESET_ERROR_CODE(); std::ostringstream buffer; param_descrs descrs; th_rewriter::get_param_descrs(descrs); descrs.display(buffer); return mk_c(c)->mk_external_string(buffer.str()); Z3_CATCH_RETURN(""); } Z3_param_descrs Z3_API Z3_simplify_get_param_descrs(Z3_context c) { Z3_TRY; LOG_Z3_simplify_get_param_descrs(c); RESET_ERROR_CODE(); Z3_param_descrs_ref * d = alloc(Z3_param_descrs_ref, *mk_c(c)); mk_c(c)->save_object(d); th_rewriter::get_param_descrs(d->m_descrs); Z3_param_descrs r = of_param_descrs(d); RETURN_Z3(r); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_update_term(Z3_context c, Z3_ast _a, unsigned num_args, Z3_ast const _args[]) { Z3_TRY; LOG_Z3_update_term(c, _a, num_args, _args); RESET_ERROR_CODE(); ast_manager& m = mk_c(c)->m(); expr* a = to_expr(_a); expr* const* args = to_exprs(num_args, _args); switch(a->get_kind()) { case AST_APP: { app* e = to_app(a); if (e->get_num_args() != num_args) { SET_ERROR_CODE(Z3_IOB, nullptr); } else { a = m.mk_app(e->get_decl(), num_args, args); } break; } case AST_QUANTIFIER: { if (num_args != 1) { SET_ERROR_CODE(Z3_IOB, nullptr); } else { a = m.update_quantifier(to_quantifier(a), args[0]); } break; } default: break; } mk_c(c)->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_substitute(Z3_context c, Z3_ast _a, unsigned num_exprs, Z3_ast const _from[], Z3_ast const _to[]) { Z3_TRY; LOG_Z3_substitute(c, _a, num_exprs, _from, _to); RESET_ERROR_CODE(); ast_manager & m = mk_c(c)->m(); expr * a = to_expr(_a); expr * const * from = to_exprs(num_exprs, _from); expr * const * to = to_exprs(num_exprs, _to); expr * r = nullptr; for (unsigned i = 0; i < num_exprs; i++) { if (m.get_sort(from[i]) != m.get_sort(to[i])) { SET_ERROR_CODE(Z3_SORT_ERROR, nullptr); RETURN_Z3(of_expr(nullptr)); } SASSERT(from[i]->get_ref_count() > 0); SASSERT(to[i]->get_ref_count() > 0); } expr_safe_replace subst(m); for (unsigned i = 0; i < num_exprs; i++) { subst.insert(from[i], to[i]); } expr_ref new_a(m); subst(a, new_a); mk_c(c)->save_ast_trail(new_a); r = new_a.get(); RETURN_Z3(of_expr(r)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_substitute_vars(Z3_context c, Z3_ast _a, unsigned num_exprs, Z3_ast const _to[]) { Z3_TRY; LOG_Z3_substitute_vars(c, _a, num_exprs, _to); RESET_ERROR_CODE(); ast_manager & m = mk_c(c)->m(); expr * a = to_expr(_a); expr * const * to = to_exprs(num_exprs, _to); var_subst subst(m, false); expr_ref new_a = subst(a, num_exprs, to); mk_c(c)->save_ast_trail(new_a); RETURN_Z3(of_expr(new_a.get())); Z3_CATCH_RETURN(nullptr); } Z3_API char const * Z3_ast_to_string(Z3_context c, Z3_ast a) { Z3_TRY; LOG_Z3_ast_to_string(c, a); RESET_ERROR_CODE(); std::ostringstream buffer; switch (mk_c(c)->get_print_mode()) { case Z3_PRINT_SMTLIB_FULL: { params_ref p; p.set_uint("max_depth", 4294967295u); p.set_uint("min_alias_size", 4294967295u); buffer << mk_pp(to_ast(a), mk_c(c)->m(), p); break; } case Z3_PRINT_LOW_LEVEL: buffer << mk_ll_pp(to_ast(a), mk_c(c)->m()); break; case Z3_PRINT_SMTLIB2_COMPLIANT: buffer << mk_ismt2_pp(to_ast(a), mk_c(c)->m()); break; default: UNREACHABLE(); } return mk_c(c)->mk_external_string(buffer.str()); Z3_CATCH_RETURN(nullptr); } Z3_API char const * Z3_sort_to_string(Z3_context c, Z3_sort s) { return Z3_ast_to_string(c, reinterpret_cast(s)); } Z3_API char const * Z3_func_decl_to_string(Z3_context c, Z3_func_decl f) { return Z3_ast_to_string(c, reinterpret_cast(f)); } Z3_string Z3_API Z3_benchmark_to_smtlib_string(Z3_context c, Z3_string name, Z3_string logic, Z3_string status, Z3_string attributes, unsigned num_assumptions, Z3_ast const assumptions[], Z3_ast formula) { Z3_TRY; LOG_Z3_benchmark_to_smtlib_string(c, name, logic, status, attributes, num_assumptions, assumptions, formula); RESET_ERROR_CODE(); std::ostringstream buffer; ast_smt_pp pp(mk_c(c)->m()); pp.set_benchmark_name(name); pp.set_logic(logic?symbol(logic):symbol::null); pp.set_status(status); pp.add_attributes(attributes); pp_params params; pp.set_simplify_implies(params.simplify_implies()); for (unsigned i = 0; i < num_assumptions; ++i) { pp.add_assumption(to_expr(assumptions[i])); } pp.display_smt2(buffer, to_expr(formula)); return mk_c(c)->mk_external_string(buffer.str()); Z3_CATCH_RETURN(""); } Z3_decl_kind Z3_API Z3_get_decl_kind(Z3_context c, Z3_func_decl d) { Z3_TRY; LOG_Z3_get_decl_kind(c, d); RESET_ERROR_CODE(); func_decl* _d = to_func_decl(d); if (d == nullptr || null_family_id == _d->get_family_id()) { return Z3_OP_UNINTERPRETED; } if (mk_c(c)->get_basic_fid() == _d->get_family_id()) { switch(_d->get_decl_kind()) { case OP_TRUE: return Z3_OP_TRUE; case OP_FALSE: return Z3_OP_FALSE; case OP_EQ: return Z3_OP_EQ; case OP_DISTINCT: return Z3_OP_DISTINCT; case OP_ITE: return Z3_OP_ITE; case OP_AND: return Z3_OP_AND; case OP_OR: return Z3_OP_OR; case OP_XOR: return Z3_OP_XOR; case OP_NOT: return Z3_OP_NOT; case OP_IMPLIES: return Z3_OP_IMPLIES; case OP_OEQ: return Z3_OP_OEQ; case PR_UNDEF: return Z3_OP_PR_UNDEF; case PR_TRUE: return Z3_OP_PR_TRUE; case PR_ASSERTED: return Z3_OP_PR_ASSERTED; case PR_GOAL: return Z3_OP_PR_GOAL; case PR_MODUS_PONENS: return Z3_OP_PR_MODUS_PONENS; case PR_REFLEXIVITY: return Z3_OP_PR_REFLEXIVITY; case PR_SYMMETRY: return Z3_OP_PR_SYMMETRY; case PR_TRANSITIVITY: return Z3_OP_PR_TRANSITIVITY; case PR_TRANSITIVITY_STAR: return Z3_OP_PR_TRANSITIVITY_STAR; case PR_MONOTONICITY: return Z3_OP_PR_MONOTONICITY; case PR_QUANT_INTRO: return Z3_OP_PR_QUANT_INTRO; case PR_BIND: return Z3_OP_PR_BIND; case PR_DISTRIBUTIVITY: return Z3_OP_PR_DISTRIBUTIVITY; case PR_AND_ELIM: return Z3_OP_PR_AND_ELIM; case PR_NOT_OR_ELIM: return Z3_OP_PR_NOT_OR_ELIM; case PR_REWRITE: return Z3_OP_PR_REWRITE; case PR_REWRITE_STAR: return Z3_OP_PR_REWRITE_STAR; case PR_PULL_QUANT: return Z3_OP_PR_PULL_QUANT; case PR_PUSH_QUANT: return Z3_OP_PR_PUSH_QUANT; case PR_ELIM_UNUSED_VARS: return Z3_OP_PR_ELIM_UNUSED_VARS; case PR_DER: return Z3_OP_PR_DER; case PR_QUANT_INST: return Z3_OP_PR_QUANT_INST; case PR_HYPOTHESIS: return Z3_OP_PR_HYPOTHESIS; case PR_LEMMA: return Z3_OP_PR_LEMMA; case PR_UNIT_RESOLUTION: return Z3_OP_PR_UNIT_RESOLUTION; case PR_IFF_TRUE: return Z3_OP_PR_IFF_TRUE; case PR_IFF_FALSE: return Z3_OP_PR_IFF_FALSE; case PR_COMMUTATIVITY: return Z3_OP_PR_COMMUTATIVITY; case PR_DEF_AXIOM: return Z3_OP_PR_DEF_AXIOM; case PR_ASSUMPTION_ADD: return Z3_OP_PR_ASSUMPTION_ADD; case PR_LEMMA_ADD: return Z3_OP_PR_LEMMA_ADD; case PR_REDUNDANT_DEL: return Z3_OP_PR_REDUNDANT_DEL; case PR_CLAUSE_TRAIL: return Z3_OP_PR_CLAUSE_TRAIL; case PR_DEF_INTRO: return Z3_OP_PR_DEF_INTRO; case PR_APPLY_DEF: return Z3_OP_PR_APPLY_DEF; case PR_IFF_OEQ: return Z3_OP_PR_IFF_OEQ; case PR_NNF_POS: return Z3_OP_PR_NNF_POS; case PR_NNF_NEG: return Z3_OP_PR_NNF_NEG; case PR_SKOLEMIZE: return Z3_OP_PR_SKOLEMIZE; case PR_MODUS_PONENS_OEQ: return Z3_OP_PR_MODUS_PONENS_OEQ; case PR_TH_LEMMA: return Z3_OP_PR_TH_LEMMA; case PR_HYPER_RESOLVE: return Z3_OP_PR_HYPER_RESOLVE; default: return Z3_OP_INTERNAL; } } if (mk_c(c)->get_arith_fid() == _d->get_family_id()) { switch(_d->get_decl_kind()) { case OP_NUM: return Z3_OP_ANUM; case OP_IRRATIONAL_ALGEBRAIC_NUM: return Z3_OP_AGNUM; case OP_LE: return Z3_OP_LE; case OP_GE: return Z3_OP_GE; case OP_LT: return Z3_OP_LT; case OP_GT: return Z3_OP_GT; case OP_ADD: return Z3_OP_ADD; case OP_SUB: return Z3_OP_SUB; case OP_UMINUS: return Z3_OP_UMINUS; case OP_MUL: return Z3_OP_MUL; case OP_DIV: return Z3_OP_DIV; case OP_IDIV: return Z3_OP_IDIV; case OP_REM: return Z3_OP_REM; case OP_MOD: return Z3_OP_MOD; case OP_POWER: return Z3_OP_POWER; case OP_TO_REAL: return Z3_OP_TO_REAL; case OP_TO_INT: return Z3_OP_TO_INT; case OP_IS_INT: return Z3_OP_IS_INT; default: return Z3_OP_INTERNAL; } } if (mk_c(c)->get_array_fid() == _d->get_family_id()) { switch(_d->get_decl_kind()) { case OP_STORE: return Z3_OP_STORE; case OP_SELECT: return Z3_OP_SELECT; case OP_CONST_ARRAY: return Z3_OP_CONST_ARRAY; case OP_ARRAY_DEFAULT: return Z3_OP_ARRAY_DEFAULT; case OP_ARRAY_MAP: return Z3_OP_ARRAY_MAP; case OP_SET_UNION: return Z3_OP_SET_UNION; case OP_SET_INTERSECT: return Z3_OP_SET_INTERSECT; case OP_SET_DIFFERENCE: return Z3_OP_SET_DIFFERENCE; case OP_SET_COMPLEMENT: return Z3_OP_SET_COMPLEMENT; case OP_SET_SUBSET: return Z3_OP_SET_SUBSET; case OP_AS_ARRAY: return Z3_OP_AS_ARRAY; case OP_ARRAY_EXT: return Z3_OP_ARRAY_EXT; case OP_SET_CARD: return Z3_OP_SET_CARD; case OP_SET_HAS_SIZE: return Z3_OP_SET_HAS_SIZE; default: return Z3_OP_INTERNAL; } } if (mk_c(c)->get_special_relations_fid() == _d->get_family_id()) { switch(_d->get_decl_kind()) { case OP_SPECIAL_RELATION_LO : return Z3_OP_SPECIAL_RELATION_LO; case OP_SPECIAL_RELATION_PO : return Z3_OP_SPECIAL_RELATION_PO; case OP_SPECIAL_RELATION_PLO: return Z3_OP_SPECIAL_RELATION_PLO; case OP_SPECIAL_RELATION_TO : return Z3_OP_SPECIAL_RELATION_TO; case OP_SPECIAL_RELATION_TC : return Z3_OP_SPECIAL_RELATION_TC; default: UNREACHABLE(); } } if (mk_c(c)->get_bv_fid() == _d->get_family_id()) { switch(_d->get_decl_kind()) { case OP_BV_NUM: return Z3_OP_BNUM; case OP_BIT1: return Z3_OP_BIT1; case OP_BIT0: return Z3_OP_BIT0; case OP_BNEG: return Z3_OP_BNEG; case OP_BADD: return Z3_OP_BADD; case OP_BSUB: return Z3_OP_BSUB; case OP_BMUL: return Z3_OP_BMUL; case OP_BSDIV: return Z3_OP_BSDIV; case OP_BUDIV: return Z3_OP_BUDIV; case OP_BSREM: return Z3_OP_BSREM; case OP_BUREM: return Z3_OP_BUREM; case OP_BSMOD: return Z3_OP_BSMOD; case OP_BSDIV0: return Z3_OP_BSDIV0; case OP_BUDIV0: return Z3_OP_BUDIV0; case OP_BSREM0: return Z3_OP_BUREM0; case OP_BUREM0: return Z3_OP_BUREM0; case OP_BSMOD0: return Z3_OP_BSMOD0; case OP_ULEQ: return Z3_OP_ULEQ; case OP_SLEQ: return Z3_OP_SLEQ; case OP_UGEQ: return Z3_OP_UGEQ; case OP_SGEQ: return Z3_OP_SGEQ; case OP_ULT: return Z3_OP_ULT; case OP_SLT: return Z3_OP_SLT; case OP_UGT: return Z3_OP_UGT; case OP_SGT: return Z3_OP_SGT; case OP_BAND: return Z3_OP_BAND; case OP_BOR: return Z3_OP_BOR; case OP_BNOT: return Z3_OP_BNOT; case OP_BXOR: return Z3_OP_BXOR; case OP_BNAND: return Z3_OP_BNAND; case OP_BNOR: return Z3_OP_BNOR; case OP_BXNOR: return Z3_OP_BXNOR; case OP_CONCAT: return Z3_OP_CONCAT; case OP_SIGN_EXT: return Z3_OP_SIGN_EXT; case OP_ZERO_EXT: return Z3_OP_ZERO_EXT; case OP_EXTRACT: return Z3_OP_EXTRACT; case OP_REPEAT: return Z3_OP_REPEAT; case OP_BREDOR: return Z3_OP_BREDOR; case OP_BREDAND: return Z3_OP_BREDAND; case OP_BCOMP: return Z3_OP_BCOMP; case OP_BSHL: return Z3_OP_BSHL; case OP_BLSHR: return Z3_OP_BLSHR; case OP_BASHR: return Z3_OP_BASHR; case OP_ROTATE_LEFT: return Z3_OP_ROTATE_LEFT; case OP_ROTATE_RIGHT: return Z3_OP_ROTATE_RIGHT; case OP_EXT_ROTATE_LEFT: return Z3_OP_EXT_ROTATE_LEFT; case OP_EXT_ROTATE_RIGHT: return Z3_OP_EXT_ROTATE_RIGHT; case OP_INT2BV: return Z3_OP_INT2BV; case OP_BV2INT: return Z3_OP_BV2INT; case OP_CARRY: return Z3_OP_CARRY; case OP_XOR3: return Z3_OP_XOR3; case OP_BIT2BOOL: return Z3_OP_BIT2BOOL; case OP_BSMUL_NO_OVFL: return Z3_OP_BSMUL_NO_OVFL; case OP_BUMUL_NO_OVFL: return Z3_OP_BUMUL_NO_OVFL; case OP_BSMUL_NO_UDFL: return Z3_OP_BSMUL_NO_UDFL; case OP_BSDIV_I: return Z3_OP_BSDIV_I; case OP_BUDIV_I: return Z3_OP_BUDIV_I; case OP_BSREM_I: return Z3_OP_BSREM_I; case OP_BUREM_I: return Z3_OP_BUREM_I; case OP_BSMOD_I: return Z3_OP_BSMOD_I; default: return Z3_OP_INTERNAL; } } if (mk_c(c)->get_dt_fid() == _d->get_family_id()) { switch(_d->get_decl_kind()) { case OP_DT_CONSTRUCTOR: return Z3_OP_DT_CONSTRUCTOR; case OP_DT_RECOGNISER: return Z3_OP_DT_RECOGNISER; case OP_DT_IS: return Z3_OP_DT_IS; case OP_DT_ACCESSOR: return Z3_OP_DT_ACCESSOR; case OP_DT_UPDATE_FIELD: return Z3_OP_DT_UPDATE_FIELD; default: return Z3_OP_INTERNAL; } } if (mk_c(c)->get_datalog_fid() == _d->get_family_id()) { switch(_d->get_decl_kind()) { case datalog::OP_RA_STORE: return Z3_OP_RA_STORE; case datalog::OP_RA_EMPTY: return Z3_OP_RA_EMPTY; case datalog::OP_RA_IS_EMPTY: return Z3_OP_RA_IS_EMPTY; case datalog::OP_RA_JOIN: return Z3_OP_RA_JOIN; case datalog::OP_RA_UNION: return Z3_OP_RA_UNION; case datalog::OP_RA_WIDEN: return Z3_OP_RA_WIDEN; case datalog::OP_RA_PROJECT: return Z3_OP_RA_PROJECT; case datalog::OP_RA_FILTER: return Z3_OP_RA_FILTER; case datalog::OP_RA_NEGATION_FILTER: return Z3_OP_RA_NEGATION_FILTER; case datalog::OP_RA_RENAME: return Z3_OP_RA_RENAME; case datalog::OP_RA_COMPLEMENT: return Z3_OP_RA_COMPLEMENT; case datalog::OP_RA_SELECT: return Z3_OP_RA_SELECT; case datalog::OP_RA_CLONE: return Z3_OP_RA_CLONE; case datalog::OP_DL_CONSTANT: return Z3_OP_FD_CONSTANT; case datalog::OP_DL_LT: return Z3_OP_FD_LT; default: return Z3_OP_INTERNAL; } } if (mk_c(c)->get_seq_fid() == _d->get_family_id()) { switch (_d->get_decl_kind()) { case OP_SEQ_UNIT: return Z3_OP_SEQ_UNIT; case OP_SEQ_EMPTY: return Z3_OP_SEQ_EMPTY; case OP_SEQ_CONCAT: return Z3_OP_SEQ_CONCAT; case OP_SEQ_PREFIX: return Z3_OP_SEQ_PREFIX; case OP_SEQ_SUFFIX: return Z3_OP_SEQ_SUFFIX; case OP_SEQ_CONTAINS: return Z3_OP_SEQ_CONTAINS; case OP_SEQ_EXTRACT: return Z3_OP_SEQ_EXTRACT; case OP_SEQ_REPLACE: return Z3_OP_SEQ_REPLACE; case OP_SEQ_AT: return Z3_OP_SEQ_AT; case OP_SEQ_NTH: return Z3_OP_SEQ_NTH; case OP_SEQ_LENGTH: return Z3_OP_SEQ_LENGTH; case OP_SEQ_INDEX: return Z3_OP_SEQ_INDEX; case OP_SEQ_TO_RE: return Z3_OP_SEQ_TO_RE; case OP_SEQ_IN_RE: return Z3_OP_SEQ_IN_RE; case _OP_STRING_STRREPL: return Z3_OP_SEQ_REPLACE; case _OP_STRING_CONCAT: return Z3_OP_SEQ_CONCAT; case _OP_STRING_LENGTH: return Z3_OP_SEQ_LENGTH; case _OP_STRING_STRCTN: return Z3_OP_SEQ_CONTAINS; case _OP_STRING_PREFIX: return Z3_OP_SEQ_PREFIX; case _OP_STRING_SUFFIX: return Z3_OP_SEQ_SUFFIX; case _OP_STRING_IN_REGEXP: return Z3_OP_SEQ_IN_RE; case _OP_STRING_TO_REGEXP: return Z3_OP_SEQ_TO_RE; case _OP_STRING_CHARAT: return Z3_OP_SEQ_AT; case _OP_STRING_SUBSTR: return Z3_OP_SEQ_EXTRACT; case _OP_STRING_STRIDOF: return Z3_OP_SEQ_INDEX; case _OP_REGEXP_EMPTY: return Z3_OP_RE_EMPTY_SET; case _OP_REGEXP_FULL_CHAR: return Z3_OP_RE_FULL_SET; case OP_STRING_STOI: return Z3_OP_STR_TO_INT; case OP_STRING_ITOS: return Z3_OP_INT_TO_STR; case OP_RE_PLUS: return Z3_OP_RE_PLUS; case OP_RE_STAR: return Z3_OP_RE_STAR; case OP_RE_OPTION: return Z3_OP_RE_OPTION; case OP_RE_CONCAT: return Z3_OP_RE_CONCAT; case OP_RE_UNION: return Z3_OP_RE_UNION; case OP_RE_INTERSECT: return Z3_OP_RE_INTERSECT; case OP_RE_LOOP: return Z3_OP_RE_LOOP; case OP_RE_FULL_SEQ_SET: return Z3_OP_RE_FULL_SET; //case OP_RE_FULL_CHAR_SET: return Z3_OP_RE_FULL_SET; case OP_RE_EMPTY_SET: return Z3_OP_RE_EMPTY_SET; default: return Z3_OP_INTERNAL; } } if (mk_c(c)->get_fpa_fid() == _d->get_family_id()) { switch (_d->get_decl_kind()) { case OP_FPA_RM_NEAREST_TIES_TO_EVEN: return Z3_OP_FPA_RM_NEAREST_TIES_TO_EVEN; case OP_FPA_RM_NEAREST_TIES_TO_AWAY: return Z3_OP_FPA_RM_NEAREST_TIES_TO_AWAY; case OP_FPA_RM_TOWARD_POSITIVE: return Z3_OP_FPA_RM_TOWARD_POSITIVE; case OP_FPA_RM_TOWARD_NEGATIVE: return Z3_OP_FPA_RM_TOWARD_NEGATIVE; case OP_FPA_RM_TOWARD_ZERO: return Z3_OP_FPA_RM_TOWARD_ZERO; case OP_FPA_NUM: return Z3_OP_FPA_NUM; case OP_FPA_PLUS_INF: return Z3_OP_FPA_PLUS_INF; case OP_FPA_MINUS_INF: return Z3_OP_FPA_MINUS_INF; case OP_FPA_NAN: return Z3_OP_FPA_NAN; case OP_FPA_MINUS_ZERO: return Z3_OP_FPA_MINUS_ZERO; case OP_FPA_PLUS_ZERO: return Z3_OP_FPA_PLUS_ZERO; case OP_FPA_ADD: return Z3_OP_FPA_ADD; case OP_FPA_SUB: return Z3_OP_FPA_SUB; case OP_FPA_NEG: return Z3_OP_FPA_NEG; case OP_FPA_MUL: return Z3_OP_FPA_MUL; case OP_FPA_DIV: return Z3_OP_FPA_DIV; case OP_FPA_REM: return Z3_OP_FPA_REM; case OP_FPA_ABS: return Z3_OP_FPA_ABS; case OP_FPA_MIN: return Z3_OP_FPA_MIN; case OP_FPA_MAX: return Z3_OP_FPA_MAX; case OP_FPA_FMA: return Z3_OP_FPA_FMA; case OP_FPA_SQRT: return Z3_OP_FPA_SQRT; case OP_FPA_EQ: return Z3_OP_FPA_EQ; case OP_FPA_ROUND_TO_INTEGRAL: return Z3_OP_FPA_ROUND_TO_INTEGRAL; case OP_FPA_LT: return Z3_OP_FPA_LT; case OP_FPA_GT: return Z3_OP_FPA_GT; case OP_FPA_LE: return Z3_OP_FPA_LE; case OP_FPA_GE: return Z3_OP_FPA_GE; case OP_FPA_IS_NAN: return Z3_OP_FPA_IS_NAN; case OP_FPA_IS_INF: return Z3_OP_FPA_IS_INF; case OP_FPA_IS_ZERO: return Z3_OP_FPA_IS_ZERO; case OP_FPA_IS_NORMAL: return Z3_OP_FPA_IS_NORMAL; case OP_FPA_IS_SUBNORMAL: return Z3_OP_FPA_IS_SUBNORMAL; case OP_FPA_IS_NEGATIVE: return Z3_OP_FPA_IS_NEGATIVE; case OP_FPA_IS_POSITIVE: return Z3_OP_FPA_IS_POSITIVE; case OP_FPA_FP: return Z3_OP_FPA_FP; case OP_FPA_TO_FP: return Z3_OP_FPA_TO_FP; case OP_FPA_TO_FP_UNSIGNED: return Z3_OP_FPA_TO_FP_UNSIGNED; case OP_FPA_TO_UBV: return Z3_OP_FPA_TO_UBV; case OP_FPA_TO_SBV: return Z3_OP_FPA_TO_SBV; case OP_FPA_TO_REAL: return Z3_OP_FPA_TO_REAL; case OP_FPA_TO_IEEE_BV: return Z3_OP_FPA_TO_IEEE_BV; case OP_FPA_BVWRAP: return Z3_OP_FPA_BVWRAP; case OP_FPA_BV2RM: return Z3_OP_FPA_BV2RM; return Z3_OP_UNINTERPRETED; default: return Z3_OP_INTERNAL; } } if (mk_c(c)->m().get_label_family_id() == _d->get_family_id()) { switch(_d->get_decl_kind()) { case OP_LABEL: return Z3_OP_LABEL; case OP_LABEL_LIT: return Z3_OP_LABEL_LIT; default: return Z3_OP_INTERNAL; } } if (mk_c(c)->get_pb_fid() == _d->get_family_id()) { switch(_d->get_decl_kind()) { case OP_PB_LE: return Z3_OP_PB_LE; case OP_PB_GE: return Z3_OP_PB_GE; case OP_PB_EQ: return Z3_OP_PB_EQ; case OP_AT_MOST_K: return Z3_OP_PB_AT_MOST; case OP_AT_LEAST_K: return Z3_OP_PB_AT_LEAST; default: return Z3_OP_INTERNAL; } } return Z3_OP_UNINTERPRETED; Z3_CATCH_RETURN(Z3_OP_UNINTERPRETED); } unsigned Z3_API Z3_get_index_value(Z3_context c, Z3_ast a) { Z3_TRY; LOG_Z3_get_index_value(c, a); RESET_ERROR_CODE(); ast* _a = reinterpret_cast(a); if (!_a || _a->get_kind() != AST_VAR) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return 0; } var* va = to_var(_a); if (va) { return va->get_idx(); } SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return 0; Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_translate(Z3_context c, Z3_ast a, Z3_context target) { Z3_TRY; LOG_Z3_translate(c, a, target); RESET_ERROR_CODE(); CHECK_VALID_AST(a, nullptr); if (c == target) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } SASSERT(mk_c(c)->m().contains(to_ast(a))); ast_translation translator(mk_c(c)->m(), mk_c(target)->m()); ast * _result = translator(to_ast(a)); mk_c(target)->save_ast_trail(_result); RETURN_Z3(of_ast(_result)); Z3_CATCH_RETURN(nullptr); } }; z3-z3-4.8.7/src/api/api_ast_map.cpp000066400000000000000000000116701356505360400167550ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_ast_map.cpp Abstract: API for creating AST maps Author: Leonardo de Moura (leonardo) 2012-03-09. Revision History: --*/ #include #include "api/z3.h" #include "api/api_log_macros.h" #include "api/api_context.h" #include "api/api_ast_map.h" #include "api/api_ast_vector.h" #include "ast/ast_smt2_pp.h" #include "util/dec_ref_util.h" Z3_ast_map_ref::~Z3_ast_map_ref() { dec_ref_key_values(m, m_map); } extern "C" { Z3_ast_map Z3_API Z3_mk_ast_map(Z3_context c) { Z3_TRY; LOG_Z3_mk_ast_map(c); RESET_ERROR_CODE(); Z3_ast_map_ref * m = alloc(Z3_ast_map_ref, *mk_c(c), mk_c(c)->m()); mk_c(c)->save_object(m); Z3_ast_map r = of_ast_map(m); RETURN_Z3(r); Z3_CATCH_RETURN(nullptr); } void Z3_API Z3_ast_map_inc_ref(Z3_context c, Z3_ast_map m) { Z3_TRY; LOG_Z3_ast_map_inc_ref(c, m); RESET_ERROR_CODE(); to_ast_map(m)->inc_ref(); Z3_CATCH; } void Z3_API Z3_ast_map_dec_ref(Z3_context c, Z3_ast_map m) { Z3_TRY; LOG_Z3_ast_map_dec_ref(c, m); RESET_ERROR_CODE(); to_ast_map(m)->dec_ref(); Z3_CATCH; } bool Z3_API Z3_ast_map_contains(Z3_context c, Z3_ast_map m, Z3_ast k) { Z3_TRY; LOG_Z3_ast_map_contains(c, m, k); RESET_ERROR_CODE(); return to_ast_map_ref(m).contains(to_ast(k)); Z3_CATCH_RETURN(false); } Z3_ast Z3_API Z3_ast_map_find(Z3_context c, Z3_ast_map m, Z3_ast k) { Z3_TRY; LOG_Z3_ast_map_find(c, m, k); RESET_ERROR_CODE(); obj_map::obj_map_entry * entry = to_ast_map_ref(m).find_core(to_ast(k)); if (entry == nullptr) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } else { ast * r = entry->get_data().m_value; RETURN_Z3(of_ast(r)); } Z3_CATCH_RETURN(nullptr); } void Z3_API Z3_ast_map_insert(Z3_context c, Z3_ast_map m, Z3_ast k, Z3_ast v) { Z3_TRY; LOG_Z3_ast_map_insert(c, m, k, v); RESET_ERROR_CODE(); ast_manager & mng = to_ast_map(m)->m; obj_map::obj_map_entry * entry = to_ast_map_ref(m).insert_if_not_there2(to_ast(k), 0); if (entry->get_data().m_value == 0) { // new entry mng.inc_ref(to_ast(k)); mng.inc_ref(to_ast(v)); entry->get_data().m_value = to_ast(v); } else { // replacing entry mng.inc_ref(to_ast(v)); mng.dec_ref(entry->get_data().m_value); entry->get_data().m_value = to_ast(v); } Z3_CATCH; } void Z3_API Z3_ast_map_reset(Z3_context c, Z3_ast_map m) { Z3_TRY; LOG_Z3_ast_map_reset(c, m); RESET_ERROR_CODE(); dec_ref_key_values(to_ast_map(m)->m, to_ast_map_ref(m)); SASSERT(to_ast_map_ref(m).empty()); Z3_CATCH; } void Z3_API Z3_ast_map_erase(Z3_context c, Z3_ast_map m, Z3_ast k) { Z3_TRY; LOG_Z3_ast_map_erase(c, m, k); RESET_ERROR_CODE(); ast * v = nullptr; if (to_ast_map_ref(m).find(to_ast(k), v)) { to_ast_map_ref(m).erase(to_ast(k)); ast_manager & mng = to_ast_map(m)->m; mng.dec_ref(to_ast(k)); mng.dec_ref(v); } Z3_CATCH; } unsigned Z3_API Z3_ast_map_size(Z3_context c, Z3_ast_map m) { Z3_TRY; LOG_Z3_ast_map_size(c, m); RESET_ERROR_CODE(); return to_ast_map_ref(m).size(); Z3_CATCH_RETURN(0); } Z3_ast_vector Z3_API Z3_ast_map_keys(Z3_context c, Z3_ast_map m) { Z3_TRY; LOG_Z3_ast_map_keys(c, m); RESET_ERROR_CODE(); Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, *mk_c(c), to_ast_map(m)->m); mk_c(c)->save_object(v); obj_map::iterator it = to_ast_map_ref(m).begin(); obj_map::iterator end = to_ast_map_ref(m).end(); for (; it != end; ++it) { v->m_ast_vector.push_back(it->m_key); } Z3_ast_vector r = of_ast_vector(v); RETURN_Z3(r); Z3_CATCH_RETURN(nullptr); } Z3_string Z3_API Z3_ast_map_to_string(Z3_context c, Z3_ast_map m) { Z3_TRY; LOG_Z3_ast_map_to_string(c, m); RESET_ERROR_CODE(); std::ostringstream buffer; ast_manager & mng = to_ast_map(m)->m; buffer << "(ast-map"; obj_map::iterator it = to_ast_map_ref(m).begin(); obj_map::iterator end = to_ast_map_ref(m).end(); for (; it != end; ++it) { buffer << "\n (" << mk_ismt2_pp(it->m_key, mng, 3) << "\n " << mk_ismt2_pp(it->m_value, mng, 3) << ")"; } buffer << ")"; return mk_c(c)->mk_external_string(buffer.str()); Z3_CATCH_RETURN(nullptr); } }; z3-z3-4.8.7/src/api/api_ast_map.h000066400000000000000000000014671356505360400164250ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_ast_map.h Abstract: API for creating AST maps Author: Leonardo de Moura (leonardo) 2012-03-09. Revision History: --*/ #ifndef API_AST_MAP_H_ #define API_AST_MAP_H_ #include "api/api_util.h" #include "util/obj_hashtable.h" struct Z3_ast_map_ref : public api::object { ast_manager & m; obj_map m_map; Z3_ast_map_ref(api::context& c, ast_manager & _m): api::object(c), m(_m) {} ~Z3_ast_map_ref() override; }; inline Z3_ast_map_ref * to_ast_map(Z3_ast_map v) { return reinterpret_cast(v); } inline Z3_ast_map of_ast_map(Z3_ast_map_ref * v) { return reinterpret_cast(v); } inline obj_map & to_ast_map_ref(Z3_ast_map v) { return to_ast_map(v)->m_map; } #endif z3-z3-4.8.7/src/api/api_ast_vector.cpp000066400000000000000000000077431356505360400175100ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_ast_vector.cpp Abstract: API for creating AST vectors Author: Leonardo de Moura (leonardo) 2012-03-09. Revision History: --*/ #include #include "api/z3.h" #include "api/api_log_macros.h" #include "api/api_context.h" #include "api/api_ast_vector.h" #include "ast/ast_translation.h" #include "ast/ast_smt2_pp.h" extern "C" { Z3_ast_vector Z3_API Z3_mk_ast_vector(Z3_context c) { Z3_TRY; LOG_Z3_mk_ast_vector(c); RESET_ERROR_CODE(); Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, *mk_c(c), mk_c(c)->m()); mk_c(c)->save_object(v); Z3_ast_vector r = of_ast_vector(v); RETURN_Z3(r); Z3_CATCH_RETURN(nullptr); } void Z3_API Z3_ast_vector_inc_ref(Z3_context c, Z3_ast_vector v) { Z3_TRY; LOG_Z3_ast_vector_inc_ref(c, v); RESET_ERROR_CODE(); to_ast_vector(v)->inc_ref(); Z3_CATCH; } void Z3_API Z3_ast_vector_dec_ref(Z3_context c, Z3_ast_vector v) { Z3_TRY; LOG_Z3_ast_vector_dec_ref(c, v); RESET_ERROR_CODE(); to_ast_vector(v)->dec_ref(); Z3_CATCH; } unsigned Z3_API Z3_ast_vector_size(Z3_context c, Z3_ast_vector v) { Z3_TRY; LOG_Z3_ast_vector_size(c, v); RESET_ERROR_CODE(); return to_ast_vector_ref(v).size(); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_ast_vector_get(Z3_context c, Z3_ast_vector v, unsigned i) { Z3_TRY; LOG_Z3_ast_vector_get(c, v, i); RESET_ERROR_CODE(); if (i >= to_ast_vector_ref(v).size()) { SET_ERROR_CODE(Z3_IOB, nullptr); RETURN_Z3(nullptr); } // Remark: Don't need to invoke save_object. ast * r = to_ast_vector_ref(v).get(i); RETURN_Z3(of_ast(r)); Z3_CATCH_RETURN(nullptr); } void Z3_API Z3_ast_vector_set(Z3_context c, Z3_ast_vector v, unsigned i, Z3_ast a) { Z3_TRY; LOG_Z3_ast_vector_set(c, v, i, a); RESET_ERROR_CODE(); if (i >= to_ast_vector_ref(v).size()) { SET_ERROR_CODE(Z3_IOB, nullptr); return; } to_ast_vector_ref(v).set(i, to_ast(a)); Z3_CATCH; } void Z3_API Z3_ast_vector_resize(Z3_context c, Z3_ast_vector v, unsigned n) { Z3_TRY; LOG_Z3_ast_vector_resize(c, v, n); RESET_ERROR_CODE(); to_ast_vector_ref(v).resize(n); Z3_CATCH; } void Z3_API Z3_ast_vector_push(Z3_context c, Z3_ast_vector v, Z3_ast a) { Z3_TRY; LOG_Z3_ast_vector_push(c, v, a); RESET_ERROR_CODE(); to_ast_vector_ref(v).push_back(to_ast(a)); Z3_CATCH; } Z3_ast_vector Z3_API Z3_ast_vector_translate(Z3_context c, Z3_ast_vector v, Z3_context t) { Z3_TRY; LOG_Z3_ast_vector_translate(c, v, t); RESET_ERROR_CODE(); if (c == t) { RETURN_Z3(v); } ast_translation translator(mk_c(c)->m(), mk_c(t)->m()); Z3_ast_vector_ref * new_v = alloc(Z3_ast_vector_ref, *mk_c(t), mk_c(t)->m()); mk_c(t)->save_object(new_v); unsigned sz = to_ast_vector_ref(v).size(); for (unsigned i = 0; i < sz; i++) { ast * new_ast = translator(to_ast_vector_ref(v).get(i)); new_v->m_ast_vector.push_back(new_ast); } RETURN_Z3(of_ast_vector(new_v)); Z3_CATCH_RETURN(nullptr); } Z3_string Z3_API Z3_ast_vector_to_string(Z3_context c, Z3_ast_vector v) { Z3_TRY; LOG_Z3_ast_vector_to_string(c, v); RESET_ERROR_CODE(); std::ostringstream buffer; buffer << "(ast-vector"; unsigned sz = to_ast_vector_ref(v).size(); for (unsigned i = 0; i < sz; i++) { buffer << "\n " << mk_ismt2_pp(to_ast_vector_ref(v).get(i), mk_c(c)->m(), 2); } buffer << ")"; return mk_c(c)->mk_external_string(buffer.str()); Z3_CATCH_RETURN(nullptr); } }; z3-z3-4.8.7/src/api/api_ast_vector.h000066400000000000000000000015461356505360400171500ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_ast_vector.h Abstract: API for creating AST vectors Author: Leonardo de Moura (leonardo) 2012-03-09. Revision History: --*/ #ifndef API_AST_VECTOR_H_ #define API_AST_VECTOR_H_ #include "api/api_util.h" namespace api { class context; }; struct Z3_ast_vector_ref : public api::object { ast_ref_vector m_ast_vector; Z3_ast_vector_ref(api::context& c, ast_manager & m): api::object(c), m_ast_vector(m) {} ~Z3_ast_vector_ref() override {} }; inline Z3_ast_vector_ref * to_ast_vector(Z3_ast_vector v) { return reinterpret_cast(v); } inline Z3_ast_vector of_ast_vector(Z3_ast_vector_ref * v) { return reinterpret_cast(v); } inline ast_ref_vector & to_ast_vector_ref(Z3_ast_vector v) { return to_ast_vector(v)->m_ast_vector; } #endif z3-z3-4.8.7/src/api/api_bv.cpp000066400000000000000000000327711356505360400157450ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_bv.cpp Abstract: API for bv theory Author: Leonardo de Moura (leonardo) 2012-02-29. Revision History: --*/ #include "api/z3.h" #include "api/api_log_macros.h" #include "api/api_context.h" #include "api/api_util.h" #include "ast/bv_decl_plugin.h" extern "C" { Z3_sort Z3_API Z3_mk_bv_sort(Z3_context c, unsigned sz) { Z3_TRY; LOG_Z3_mk_bv_sort(c, sz); RESET_ERROR_CODE(); parameter p(sz); Z3_sort r = of_sort(mk_c(c)->m().mk_sort(mk_c(c)->get_bv_fid(), BV_SORT, 1, &p)); RETURN_Z3(r); Z3_CATCH_RETURN(nullptr); } #define MK_BV_UNARY(NAME, OP) MK_UNARY(NAME, mk_c(c)->get_bv_fid(), OP, SKIP) #define MK_BV_BINARY(NAME, OP) MK_BINARY(NAME, mk_c(c)->get_bv_fid(), OP, SKIP) MK_BV_UNARY(Z3_mk_bvnot, OP_BNOT); MK_BV_UNARY(Z3_mk_bvredand, OP_BREDAND); MK_BV_UNARY(Z3_mk_bvredor, OP_BREDOR); MK_BV_BINARY(Z3_mk_bvand, OP_BAND); MK_BV_BINARY(Z3_mk_bvor, OP_BOR); MK_BV_BINARY(Z3_mk_bvxor, OP_BXOR); MK_BV_BINARY(Z3_mk_bvnand, OP_BNAND); MK_BV_BINARY(Z3_mk_bvnor, OP_BNOR); MK_BV_BINARY(Z3_mk_bvxnor, OP_BXNOR); MK_BV_BINARY(Z3_mk_bvadd, OP_BADD); MK_BV_BINARY(Z3_mk_bvmul, OP_BMUL); MK_BV_BINARY(Z3_mk_bvudiv, OP_BUDIV); MK_BV_BINARY(Z3_mk_bvsdiv, OP_BSDIV); MK_BV_BINARY(Z3_mk_bvurem, OP_BUREM); MK_BV_BINARY(Z3_mk_bvsrem, OP_BSREM); MK_BV_BINARY(Z3_mk_bvsmod, OP_BSMOD); MK_BV_BINARY(Z3_mk_bvule, OP_ULEQ); MK_BV_BINARY(Z3_mk_bvsle, OP_SLEQ); MK_BV_BINARY(Z3_mk_bvuge, OP_UGEQ); MK_BV_BINARY(Z3_mk_bvsge, OP_SGEQ); MK_BV_BINARY(Z3_mk_bvult, OP_ULT); MK_BV_BINARY(Z3_mk_bvslt, OP_SLT); MK_BV_BINARY(Z3_mk_bvugt, OP_UGT); MK_BV_BINARY(Z3_mk_bvsgt, OP_SGT); MK_BV_BINARY(Z3_mk_concat, OP_CONCAT); MK_BV_BINARY(Z3_mk_bvshl, OP_BSHL); MK_BV_BINARY(Z3_mk_bvlshr, OP_BLSHR); MK_BV_BINARY(Z3_mk_bvashr, OP_BASHR); MK_BV_BINARY(Z3_mk_ext_rotate_left, OP_EXT_ROTATE_LEFT); MK_BV_BINARY(Z3_mk_ext_rotate_right, OP_EXT_ROTATE_RIGHT); static Z3_ast mk_extract_core(Z3_context c, unsigned high, unsigned low, Z3_ast n) { expr * _n = to_expr(n); parameter params[2] = { parameter(high), parameter(low) }; expr * a = mk_c(c)->m().mk_app(mk_c(c)->get_bv_fid(), OP_EXTRACT, 2, params, 1, &_n); mk_c(c)->save_ast_trail(a); check_sorts(c, a); return of_ast(a); } Z3_ast Z3_API Z3_mk_extract(Z3_context c, unsigned high, unsigned low, Z3_ast n) { Z3_TRY; LOG_Z3_mk_extract(c, high, low, n); RESET_ERROR_CODE(); Z3_ast r = mk_extract_core(c, high, low, n); RETURN_Z3(r); Z3_CATCH_RETURN(nullptr); } #define MK_BV_PUNARY(NAME, OP) \ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \ Z3_TRY; \ LOG_ ## NAME(c, i, n); \ RESET_ERROR_CODE(); \ expr * _n = to_expr(n); \ parameter p(i); \ ast* a = mk_c(c)->m().mk_app(mk_c(c)->get_bv_fid(), OP, 1, &p, 1, &_n); \ mk_c(c)->save_ast_trail(a); \ check_sorts(c, a); \ RETURN_Z3(of_ast(a)); \ Z3_CATCH_RETURN(0); \ } MK_BV_PUNARY(Z3_mk_sign_ext, OP_SIGN_EXT); MK_BV_PUNARY(Z3_mk_zero_ext, OP_ZERO_EXT); MK_BV_PUNARY(Z3_mk_repeat, OP_REPEAT); MK_BV_PUNARY(Z3_mk_rotate_left, OP_ROTATE_LEFT); MK_BV_PUNARY(Z3_mk_rotate_right, OP_ROTATE_RIGHT); MK_BV_PUNARY(Z3_mk_int2bv, OP_INT2BV); Z3_ast Z3_API Z3_mk_bv2int(Z3_context c, Z3_ast n, bool is_signed) { Z3_TRY; LOG_Z3_mk_bv2int(c, n, is_signed); RESET_ERROR_CODE(); Z3_sort int_s = Z3_mk_int_sort(c); if (is_signed) { Z3_ast r = Z3_mk_bv2int(c, n, false); Z3_inc_ref(c, r); Z3_sort s = Z3_get_sort(c, n); unsigned sz = Z3_get_bv_sort_size(c, s); rational max_bound = power(rational(2), sz); Z3_ast bound = Z3_mk_numeral(c, max_bound.to_string().c_str(), int_s); Z3_inc_ref(c, bound); Z3_ast zero = Z3_mk_int(c, 0, s); Z3_inc_ref(c, zero); Z3_ast pred = Z3_mk_bvslt(c, n, zero); Z3_inc_ref(c, pred); // if n <_sigend 0 then r - s^sz else r Z3_ast args[2] = { r, bound }; Z3_ast sub = Z3_mk_sub(c, 2, args); Z3_inc_ref(c, sub); Z3_ast res = Z3_mk_ite(c, pred, sub, r); Z3_dec_ref(c, bound); Z3_dec_ref(c, pred); Z3_dec_ref(c, sub); Z3_dec_ref(c, zero); Z3_dec_ref(c, r); RETURN_Z3(res); } else { expr * _n = to_expr(n); parameter p(to_sort(int_s)); ast* a = mk_c(c)->m().mk_app(mk_c(c)->get_bv_fid(), OP_BV2INT, 1, &p, 1, &_n); mk_c(c)->save_ast_trail(a); check_sorts(c, a); RETURN_Z3(of_ast(a)); } Z3_CATCH_RETURN(nullptr); } /** \brief Create a bit-vector of sort \s with 1 in the most significant bit position. The sort \s must be a bit-vector sort. This function is a shorthand for shl(1, N-1) where N are the number of bits of \c s. */ Z3_ast Z3_API Z3_mk_bvmsb(Z3_context c, Z3_sort s) { Z3_TRY; RESET_ERROR_CODE(); // Not logging this one, since it is just syntax sugar. unsigned sz = Z3_get_bv_sort_size(c, s); if (sz == 0) { SET_ERROR_CODE(Z3_INVALID_ARG, "zero length bit-vector supplied"); return nullptr; } Z3_ast x = Z3_mk_int64(c, 1, s); Z3_inc_ref(c, x); Z3_ast y = Z3_mk_int64(c, sz - 1, s); Z3_inc_ref(c, y); Z3_ast result = Z3_mk_bvshl(c, x, y); Z3_dec_ref(c, x); Z3_dec_ref(c, y); return result; Z3_CATCH_RETURN(nullptr); } static Z3_ast Z3_mk_bvsmin(Z3_context c, Z3_sort s) { return Z3_mk_bvmsb(c, s); } Z3_ast Z3_API Z3_mk_bvadd_no_overflow(Z3_context c, Z3_ast t1, Z3_ast t2, bool is_signed) { Z3_TRY; RESET_ERROR_CODE(); if (is_signed) { Z3_ast zero = Z3_mk_int(c, 0, Z3_get_sort(c, t1)); Z3_inc_ref(c, zero); Z3_ast r = Z3_mk_bvadd(c, t1, t2); Z3_inc_ref(c, r); Z3_ast l1 = Z3_mk_bvslt(c, zero, t1); Z3_inc_ref(c, l1); Z3_ast l2 = Z3_mk_bvslt(c, zero, t2); Z3_inc_ref(c, l2); Z3_ast args[2] = { l1, l2 }; Z3_ast args_pos = Z3_mk_and(c, 2, args); Z3_inc_ref(c, args_pos); Z3_ast result = Z3_mk_implies(c, args_pos, Z3_mk_bvslt(c, zero, r)); Z3_dec_ref(c, r); Z3_dec_ref(c, l1); Z3_dec_ref(c, l2); Z3_dec_ref(c, args_pos); Z3_dec_ref(c, zero); return result; } else { unsigned sz = Z3_get_bv_sort_size(c, Z3_get_sort(c, t1)); t1 = Z3_mk_zero_ext(c, 1, t1); Z3_inc_ref(c, t1); t2 = Z3_mk_zero_ext(c, 1, t2); Z3_inc_ref(c, t2); Z3_ast r = Z3_mk_bvadd(c, t1, t2); Z3_inc_ref(c, r); Z3_ast ex = Z3_mk_extract(c, sz, sz, r); Z3_inc_ref(c, ex); Z3_ast result = Z3_mk_eq(c, ex, Z3_mk_int(c, 0, Z3_mk_bv_sort(c, 1))); Z3_dec_ref(c, t1); Z3_dec_ref(c, t2); Z3_dec_ref(c, ex); Z3_dec_ref(c, r); return result; } Z3_CATCH_RETURN(nullptr); } // only for signed machine integers Z3_ast Z3_API Z3_mk_bvadd_no_underflow(Z3_context c, Z3_ast t1, Z3_ast t2) { Z3_TRY; RESET_ERROR_CODE(); Z3_ast zero = Z3_mk_int(c, 0, Z3_get_sort(c, t1)); Z3_inc_ref(c, zero); Z3_ast r = Z3_mk_bvadd(c, t1, t2); Z3_inc_ref(c, r); Z3_ast l1 = Z3_mk_bvslt(c, t1, zero); Z3_inc_ref(c, l1); Z3_ast l2 = Z3_mk_bvslt(c, t2, zero); Z3_inc_ref(c, l2); Z3_ast args[2] = { l1, l2 }; Z3_ast args_neg = Z3_mk_and(c, 2, args); Z3_inc_ref(c, args_neg); Z3_ast lt = Z3_mk_bvslt(c, r, zero); Z3_inc_ref(c, lt); Z3_ast result = Z3_mk_implies(c, args_neg, lt); Z3_dec_ref(c, lt); Z3_dec_ref(c, l1); Z3_dec_ref(c, l2); Z3_dec_ref(c, r); Z3_dec_ref(c, args_neg); Z3_dec_ref(c, zero); return result; Z3_CATCH_RETURN(nullptr); } // only for signed machine integers Z3_ast Z3_API Z3_mk_bvsub_no_overflow(Z3_context c, Z3_ast t1, Z3_ast t2) { Z3_TRY; RESET_ERROR_CODE(); Z3_ast minus_t2 = Z3_mk_bvneg(c, t2); Z3_inc_ref(c, minus_t2); Z3_sort s = Z3_get_sort(c, t2); Z3_ast min = Z3_mk_bvsmin(c, s); Z3_inc_ref(c, min); Z3_ast x = Z3_mk_eq(c, t2, min); Z3_inc_ref(c, x); Z3_ast zero = Z3_mk_int(c, 0, s); Z3_inc_ref(c, zero); Z3_ast y = Z3_mk_bvslt(c, t1, zero); Z3_inc_ref(c, y); Z3_ast z = Z3_mk_bvadd_no_overflow(c, t1, minus_t2, true); Z3_inc_ref(c, z); Z3_ast result = Z3_mk_ite(c, x, y, z); mk_c(c)->save_ast_trail(to_app(result)); Z3_dec_ref(c, minus_t2); Z3_dec_ref(c, min); Z3_dec_ref(c, x); Z3_dec_ref(c, y); Z3_dec_ref(c, z); Z3_dec_ref(c, zero); return result; Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_bvsub_no_underflow(Z3_context c, Z3_ast t1, Z3_ast t2, bool is_signed) { Z3_TRY; RESET_ERROR_CODE(); if (is_signed) { Z3_ast zero = Z3_mk_int(c, 0, Z3_get_sort(c, t1)); Z3_inc_ref(c, zero); Z3_ast minus_t2 = Z3_mk_bvneg(c, t2); Z3_inc_ref(c, minus_t2); Z3_ast x = Z3_mk_bvslt(c, zero, t2); Z3_inc_ref(c, x); Z3_ast y = Z3_mk_bvadd_no_underflow(c, t1, minus_t2); Z3_inc_ref(c, y); Z3_ast result = Z3_mk_implies(c, x, y); Z3_dec_ref(c, zero); Z3_dec_ref(c, minus_t2); Z3_dec_ref(c, x); Z3_dec_ref(c, y); return result; } else { return Z3_mk_bvule(c, t2, t1); } Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_bvmul_no_overflow(Z3_context c, Z3_ast n1, Z3_ast n2, bool is_signed) { LOG_Z3_mk_bvmul_no_overflow(c, n1, n2, is_signed); RESET_ERROR_CODE(); if (is_signed) { MK_BINARY_BODY(Z3_mk_bvmul_no_overflow, mk_c(c)->get_bv_fid(), OP_BSMUL_NO_OVFL, SKIP); } else { MK_BINARY_BODY(Z3_mk_bvmul_no_overflow, mk_c(c)->get_bv_fid(), OP_BUMUL_NO_OVFL, SKIP); } } // only for signed machine integers Z3_ast Z3_API Z3_mk_bvmul_no_underflow(Z3_context c, Z3_ast n1, Z3_ast n2) { LOG_Z3_mk_bvmul_no_underflow(c, n1, n2); MK_BINARY_BODY(Z3_mk_bvmul_no_underflow, mk_c(c)->get_bv_fid(), OP_BSMUL_NO_UDFL, SKIP); } // only for signed machine integers Z3_ast Z3_API Z3_mk_bvneg_no_overflow(Z3_context c, Z3_ast t) { Z3_TRY; RESET_ERROR_CODE(); Z3_ast min = Z3_mk_bvsmin(c, Z3_get_sort(c, t)); if (Z3_get_error_code(c) != Z3_OK) return nullptr; Z3_ast eq = Z3_mk_eq(c, t, min); if (Z3_get_error_code(c) != Z3_OK) return nullptr; return Z3_mk_not(c, eq); Z3_CATCH_RETURN(nullptr); } // only for signed machine integers Z3_ast Z3_API Z3_mk_bvsdiv_no_overflow(Z3_context c, Z3_ast t1, Z3_ast t2) { Z3_TRY; RESET_ERROR_CODE(); Z3_sort s = Z3_get_sort(c, t1); Z3_ast min = Z3_mk_bvmsb(c, s); Z3_inc_ref(c, min); Z3_ast x = Z3_mk_eq(c, t1, min); Z3_inc_ref(c, x); Z3_ast y = Z3_mk_int(c, -1, s); Z3_inc_ref(c, y); Z3_ast z = Z3_mk_eq(c, t2, y); Z3_inc_ref(c, z); Z3_ast args[2] = { x, z }; Z3_ast u = Z3_mk_and(c, 2, args); Z3_inc_ref(c, u); Z3_ast result = Z3_mk_not(c, u); Z3_dec_ref(c, min); Z3_dec_ref(c, x); Z3_dec_ref(c, y); Z3_dec_ref(c, z); Z3_dec_ref(c, u); return result; Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_bvsub(Z3_context c, Z3_ast n1, Z3_ast n2) { Z3_TRY; LOG_Z3_mk_bvsub(c, n1, n2); RESET_ERROR_CODE(); MK_BINARY_BODY(Z3_mk_bvsub, mk_c(c)->get_bv_fid(), OP_BSUB, SKIP); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_bvneg(Z3_context c, Z3_ast n) { Z3_TRY; LOG_Z3_mk_bvneg(c, n); RESET_ERROR_CODE(); MK_UNARY_BODY(Z3_mk_bvneg, mk_c(c)->get_bv_fid(), OP_BNEG, SKIP); Z3_CATCH_RETURN(nullptr); } unsigned Z3_API Z3_get_bv_sort_size(Z3_context c, Z3_sort t) { Z3_TRY; LOG_Z3_get_bv_sort_size(c, t); RESET_ERROR_CODE(); CHECK_VALID_AST(t, 0); if (to_sort(t)->get_family_id() == mk_c(c)->get_bv_fid() && to_sort(t)->get_decl_kind() == BV_SORT) { return to_sort(t)->get_parameter(0).get_int(); } SET_ERROR_CODE(Z3_INVALID_ARG, "sort is not a bit-vector"); return 0; Z3_CATCH_RETURN(0); } }; z3-z3-4.8.7/src/api/api_config_params.cpp000066400000000000000000000062611356505360400201410ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_config_params.cpp Abstract: Configuration parameters Author: Leonardo de Moura (leonardo) 2012-02-29. Revision History: --*/ #include "api/z3.h" #include "api/api_context.h" #include "ast/pp.h" #include "api/api_log_macros.h" #include "api/api_util.h" #include "cmd_context/cmd_context.h" #include "util/symbol.h" #include "util/gparams.h" #include "util/env_params.h" #include "cmd_context/context_params.h" extern "C" { void Z3_API Z3_global_param_set(Z3_string param_id, Z3_string param_value) { memory::initialize(UINT_MAX); LOG_Z3_global_param_set(param_id, param_value); try { gparams::set(param_id, param_value); env_params::updt_params(); } catch (z3_exception & ex) { // The error handler is only available for contexts // Just throw a warning. warning_msg("%s", ex.msg()); } } void Z3_API Z3_global_param_reset_all(void) { memory::initialize(UINT_MAX); LOG_Z3_global_param_reset_all(); gparams::reset(); env_params::updt_params(); } static std::string g_Z3_global_param_get_buffer; Z3_bool_opt Z3_API Z3_global_param_get(Z3_string param_id, Z3_string_ptr param_value) { memory::initialize(UINT_MAX); LOG_Z3_global_param_get(param_id, param_value); *param_value = nullptr; try { g_Z3_global_param_get_buffer = gparams::get_value(param_id); *param_value = g_Z3_global_param_get_buffer.c_str(); return true; } catch (z3_exception & ex) { // The error handler is only available for contexts // Just throw a warning. warning_msg("%s", ex.msg()); return false; } } Z3_config Z3_API Z3_mk_config(void) { 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) { LOG_Z3_del_config(c); dealloc((reinterpret_cast(c))); } void Z3_API Z3_set_param_value(Z3_config c, char const * param_id, char const * param_value) { LOG_Z3_set_param_value(c, param_id, param_value); try { context_params * p = reinterpret_cast(c); p->set(param_id, param_value); } catch (z3_exception & ex) { // The error handler is only available for contexts // Just throw a warning. warning_msg("%s", ex.msg()); } } void Z3_API Z3_update_param_value(Z3_context c, Z3_string param_id, Z3_string param_value) { Z3_TRY; LOG_Z3_update_param_value(c, param_id, param_value); RESET_ERROR_CODE(); mk_c(c)->params().set(param_id, param_value); Z3_CATCH; } }; z3-z3-4.8.7/src/api/api_context.cpp000066400000000000000000000342071356505360400170160ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_context.cpp Abstract: Interface of Z3 with "external world". It was called _Z3_context Author: Leonardo de Moura (leonardo) 2012-02-29. Revision History: --*/ #include #include "api/api_context.h" #include "util/z3_version.h" #include "ast/ast_pp.h" #include "ast/ast_ll_pp.h" #include "api/api_log_macros.h" #include "api/api_util.h" #include "ast/reg_decl_plugins.h" #include "math/realclosure/realclosure.h" // The install_tactics procedure is automatically generated void install_tactics(tactic_manager & ctx); namespace api { object::object(context& c): m_ref_count(0), m_context(c) { this->m_id = m_context.add_object(this); } void object::inc_ref() { m_ref_count++; } void object::dec_ref() { SASSERT(m_ref_count > 0); m_ref_count--; if (m_ref_count == 0) m_context.del_object(this); } unsigned context::add_object(api::object* o) { unsigned id = m_allocated_objects.size(); if (!m_free_object_ids.empty()) { id = m_free_object_ids.back(); m_free_object_ids.pop_back(); } m_allocated_objects.insert(id, o); return id; } void context::del_object(api::object* o) { m_free_object_ids.push_back(o->id()); m_allocated_objects.remove(o->id()); dealloc(o); } static void default_error_handler(Z3_context ctx, Z3_error_code c) { printf("Error: %s\n", Z3_get_error_msg(ctx, c)); exit(1); } context::add_plugins::add_plugins(ast_manager & m) { reg_decl_plugins(m); } // ------------------------ // // Core // // ------------------------ context::context(context_params * p, bool user_ref_count): m_params(p != nullptr ? *p : context_params()), m_user_ref_count(user_ref_count), m_manager(m_params.mk_ast_manager()), m_plugins(m()), m_arith_util(m()), m_bv_util(m()), m_datalog_util(m()), m_fpa_util(m()), m_sutil(m()), m_recfun(m()), m_last_result(m()), m_ast_trail(m()), m_pmanager(m_limit) { m_error_code = Z3_OK; m_print_mode = Z3_PRINT_SMTLIB_FULL; m_searching = false; m_interruptable = nullptr; m_error_handler = &default_error_handler; m_basic_fid = m().get_basic_family_id(); m_arith_fid = m().mk_family_id("arith"); m_bv_fid = m().mk_family_id("bv"); m_pb_fid = m().mk_family_id("pb"); m_array_fid = m().mk_family_id("array"); m_dt_fid = m().mk_family_id("datatype"); m_datalog_fid = m().mk_family_id("datalog_relation"); m_fpa_fid = m().mk_family_id("fpa"); m_seq_fid = m().mk_family_id("seq"); m_special_relations_fid = m().mk_family_id("special_relations"); m_dt_plugin = static_cast(m().get_plugin(m_dt_fid)); install_tactics(*this); } context::~context() { m_last_obj = nullptr; 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); } } context::set_interruptable::set_interruptable(context & ctx, event_handler & i): m_ctx(ctx) { lock_guard lock(ctx.m_mux); SASSERT(m_ctx.m_interruptable == 0); m_ctx.m_interruptable = &i; } context::set_interruptable::~set_interruptable() { lock_guard lock(m_ctx.m_mux); m_ctx.m_interruptable = nullptr; } void context::interrupt() { lock_guard lock(m_mux); if (m_interruptable) (*m_interruptable)(API_INTERRUPT_EH_CALLER); m_limit.cancel(); m().limit().cancel(); } void context::set_error_code(Z3_error_code err, char const* opt_msg) { m_error_code = err; if (err != Z3_OK) { m_exception_msg.clear(); if (opt_msg) m_exception_msg = opt_msg; invoke_error_handler(err); } } void context::reset_error_code() { m_error_code = Z3_OK; } void context::check_searching() { if (m_searching) { set_error_code(Z3_INVALID_USAGE, "cannot use function while searching"); // TBD: error code could be fixed. } } char * context::mk_external_string(char const * str) { m_string_buffer = str?str:""; return const_cast(m_string_buffer.c_str()); } char * context::mk_external_string(char const * str, unsigned n) { m_string_buffer.clear(); m_string_buffer.append(str, n); return const_cast(m_string_buffer.c_str()); } char * context::mk_external_string(std::string && str) { m_string_buffer = std::move(str); return const_cast(m_string_buffer.c_str()); } expr * context::mk_numeral_core(rational const & n, sort * s) { expr* e = nullptr; family_id fid = s->get_family_id(); if (fid == m_arith_fid) { e = m_arith_util.mk_numeral(n, s); } else if (fid == m_bv_fid) { e = m_bv_util.mk_numeral(n, s); } else if (fid == get_datalog_fid() && n.is_uint64()) { uint64_t sz; if (m_datalog_util.try_get_size(s, sz) && sz <= n.get_uint64()) { invoke_error_handler(Z3_INVALID_ARG); } e = m_datalog_util.mk_numeral(n.get_uint64(), s); } else { invoke_error_handler(Z3_INVALID_ARG); } save_ast_trail(e); return e; } expr * context::mk_and(unsigned num_exprs, expr * const * exprs) { switch(num_exprs) { case 0: return m().mk_true(); case 1: save_ast_trail(exprs[0]); return exprs[0]; default: { expr * a = m().mk_and(num_exprs, exprs); save_ast_trail(a); return a; } } } void context::save_ast_trail(ast * n) { SASSERT(m().contains(n)); if (m_user_ref_count) { // Corner case bug: n may be in m_last_result, and this is the only reference to n. // When, we execute reset() it is deleted // To avoid this bug, I bump the reference counter before resetting m_last_result ast_ref node(n, m()); m_last_result.reset(); m_last_result.push_back(std::move(node)); } else { m_ast_trail.push_back(n); } } void context::save_multiple_ast_trail(ast * n) { if (m_user_ref_count) m_last_result.push_back(n); else m_ast_trail.push_back(n); } void context::reset_last_result() { if (m_user_ref_count) m_last_result.reset(); m_last_obj = nullptr; } void context::save_object(object * r) { m_last_obj = r; } void context::handle_exception(z3_exception & ex) { if (ex.has_error_code()) { switch(ex.error_code()) { case ERR_MEMOUT: set_error_code(Z3_MEMOUT_FAIL, nullptr); break; case ERR_PARSER: set_error_code(Z3_PARSER_ERROR, ex.msg()); break; case ERR_INI_FILE: set_error_code(Z3_INVALID_ARG, nullptr); break; case ERR_OPEN_FILE: set_error_code(Z3_FILE_ACCESS_ERROR, nullptr); break; default: set_error_code(Z3_INTERNAL_FATAL, nullptr); break; } } else { set_error_code(Z3_EXCEPTION, ex.msg()); } } void context::invoke_error_handler(Z3_error_code c) { if (m_error_handler) { if (g_z3_log) { // error handler can do crazy stuff such as longjmp g_z3_log_enabled = true; } m_error_handler(reinterpret_cast(this), c); } } void context::check_sorts(ast * n) { if (!m().check_sorts(n)) { switch(n->get_kind()) { case AST_APP: { std::ostringstream buffer; app * a = to_app(n); buffer << mk_pp(a->get_decl(), m()) << " applied to: "; if (a->get_num_args() > 1) buffer << "\n"; for (unsigned i = 0; i < a->get_num_args(); ++i) { buffer << mk_bounded_pp(a->get_arg(i), m(), 3) << " of sort "; buffer << mk_pp(m().get_sort(a->get_arg(i)), m()) << "\n"; } warning_msg("%s",buffer.str().c_str()); break; } case AST_VAR: case AST_QUANTIFIER: case AST_SORT: case AST_FUNC_DECL: break; } set_error_code(Z3_SORT_ERROR, nullptr); } } // ------------------------ // // RCF manager // // ----------------------- realclosure::manager & context::rcfm() { if (m_rcf_manager.get() == nullptr) { m_rcf_manager = alloc(realclosure::manager, m_limit, m_rcf_qm); } return *(m_rcf_manager.get()); } }; // ------------------------ // // Context creation API // // ------------------------ extern "C" { Z3_context Z3_API Z3_mk_context(Z3_config c) { Z3_TRY; LOG_Z3_mk_context(c); memory::initialize(UINT_MAX); Z3_context r = reinterpret_cast(alloc(api::context, reinterpret_cast(c), false)); RETURN_Z3(r); Z3_CATCH_RETURN_NO_HANDLE(nullptr); } Z3_context Z3_API Z3_mk_context_rc(Z3_config c) { Z3_TRY; LOG_Z3_mk_context_rc(c); memory::initialize(UINT_MAX); Z3_context r = reinterpret_cast(alloc(api::context, reinterpret_cast(c), true)); RETURN_Z3(r); Z3_CATCH_RETURN_NO_HANDLE(nullptr); } void Z3_API Z3_del_context(Z3_context c) { Z3_TRY; LOG_Z3_del_context(c); RESET_ERROR_CODE(); dealloc(mk_c(c)); Z3_CATCH; } void Z3_API Z3_interrupt(Z3_context c) { Z3_TRY; LOG_Z3_interrupt(c); mk_c(c)->interrupt(); Z3_CATCH; } void Z3_API Z3_toggle_warning_messages(bool enabled) { LOG_Z3_toggle_warning_messages(enabled); enable_warning_messages(enabled != 0); } void Z3_API Z3_inc_ref(Z3_context c, Z3_ast a) { Z3_TRY; LOG_Z3_inc_ref(c, a); RESET_ERROR_CODE(); mk_c(c)->m().inc_ref(to_ast(a)); Z3_CATCH; } void Z3_API Z3_dec_ref(Z3_context c, Z3_ast a) { Z3_TRY; LOG_Z3_dec_ref(c, a); RESET_ERROR_CODE(); if (a && to_ast(a)->get_ref_count() == 0) { SET_ERROR_CODE(Z3_DEC_REF_ERROR, nullptr); return; } if (a) { mk_c(c)->m().dec_ref(to_ast(a)); } Z3_CATCH; } void Z3_API Z3_get_version(unsigned * major, unsigned * minor, unsigned * build_number, unsigned * revision_number) { LOG_Z3_get_version(major, minor, build_number, revision_number); *major = Z3_MAJOR_VERSION; *minor = Z3_MINOR_VERSION; *build_number = Z3_BUILD_NUMBER; *revision_number = Z3_REVISION_NUMBER; } Z3_string Z3_API Z3_get_full_version(void) { LOG_Z3_get_full_version(); return Z3_FULL_VERSION; } void Z3_API Z3_enable_trace(Z3_string tag) { memory::initialize(UINT_MAX); LOG_Z3_enable_trace(tag); // Tag is a string that was probably not allocated by Z3. Create a copy using symbol. symbol tag_sym(tag); enable_trace(tag_sym.bare_str()); } void Z3_API Z3_disable_trace(Z3_string tag) { LOG_Z3_disable_trace(tag); disable_trace(tag); } void Z3_API Z3_reset_memory(void) { LOG_Z3_reset_memory(); memory::finalize(); memory::initialize(0); } void Z3_API Z3_finalize_memory(void) { LOG_Z3_finalize_memory(); memory::finalize(); } Z3_error_code Z3_API Z3_get_error_code(Z3_context c) { LOG_Z3_get_error_code(c); return mk_c(c)->get_error_code(); } void Z3_API Z3_set_error_handler(Z3_context c, Z3_error_handler h) { RESET_ERROR_CODE(); mk_c(c)->set_error_handler(h); } void Z3_API Z3_set_error(Z3_context c, Z3_error_code e) { SET_ERROR_CODE(e, nullptr); } static char const * _get_error_msg(Z3_context c, Z3_error_code err) { if (c) { char const* msg = mk_c(c)->get_exception_msg(); if (msg && *msg) return msg; } switch(err) { case Z3_OK: return "ok"; case Z3_SORT_ERROR: return "type error"; case Z3_IOB: return "index out of bounds"; case Z3_INVALID_ARG: return "invalid argument"; case Z3_PARSER_ERROR: return "parser error"; case Z3_NO_PARSER: return "parser (data) is not available"; case Z3_INVALID_PATTERN: return "invalid pattern"; case Z3_MEMOUT_FAIL: return "out of memory"; case Z3_FILE_ACCESS_ERROR: return "file access error"; case Z3_INTERNAL_FATAL: return "internal error"; case Z3_INVALID_USAGE: return "invalid usage"; case Z3_DEC_REF_ERROR: return "invalid dec_ref command"; case Z3_EXCEPTION: return "Z3 exception"; default: return "unknown"; } } Z3_API char const * Z3_get_error_msg(Z3_context c, Z3_error_code err) { LOG_Z3_get_error_msg(c, err); return _get_error_msg(c, err); } void Z3_API Z3_set_ast_print_mode(Z3_context c, Z3_ast_print_mode mode) { Z3_TRY; LOG_Z3_set_ast_print_mode(c, mode); RESET_ERROR_CODE(); mk_c(c)->set_print_mode(mode); Z3_CATCH; } }; z3-z3-4.8.7/src/api/api_context.h000066400000000000000000000243721356505360400164650ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_context.h Abstract: Interface of Z3 with "external world". It was called _Z3_context Author: Leonardo de Moura (leonardo) 2012-02-29. Revision History: --*/ #ifndef API_CONTEXT_H_ #define API_CONTEXT_H_ #include "util/hashtable.h" #include "util/mutex.h" #include "util/event_handler.h" #include "ast/ast.h" #include "ast/arith_decl_plugin.h" #include "ast/bv_decl_plugin.h" #include "ast/seq_decl_plugin.h" #include "ast/datatype_decl_plugin.h" #include "ast/dl_decl_plugin.h" #include "ast/fpa_decl_plugin.h" #include "ast/recfun_decl_plugin.h" #include "ast/special_relations_decl_plugin.h" #include "ast/rewriter/seq_rewriter.h" #include "smt/params/smt_params.h" #include "smt/smt_kernel.h" #include "smt/smt_solver.h" #include "cmd_context/tactic_manager.h" #include "cmd_context/context_params.h" #include "cmd_context/cmd_context.h" #include "solver/solver.h" #include "api/z3.h" #include "api/api_util.h" #include "api/api_polynomial.h" namespace smtlib { class parser; }; namespace realclosure { class manager; }; 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); }; context_params m_params; bool m_user_ref_count; //!< if true, the user is responsible for managing reference counters. scoped_ptr m_manager; scoped_ptr m_cmd; add_plugins m_plugins; mutex m_mux; arith_util m_arith_util; bv_util m_bv_util; datalog::dl_decl_util m_datalog_util; fpa_util m_fpa_util; seq_util m_sutil; recfun::util m_recfun; // Support for old solver API smt_params m_fparams; // ------------------------------- ast_ref_vector m_last_result; //!< used when m_user_ref_count == true ast_ref_vector m_ast_trail; //!< used when m_user_ref_count == false ref m_last_obj; //!< reference to the last API object returned by the APIs u_map m_allocated_objects; // !< table containing current set of allocated API objects unsigned_vector m_free_object_ids; // !< free list of identifiers available for allocated objects. family_id m_basic_fid; family_id m_array_fid; family_id m_arith_fid; family_id m_bv_fid; family_id m_dt_fid; family_id m_datalog_fid; family_id m_pb_fid; family_id m_fpa_fid; family_id m_seq_fid; family_id m_special_relations_fid; datatype_decl_plugin * m_dt_plugin; std::string m_string_buffer; // temporary buffer used to cache strings sent to the "external" world. Z3_error_code m_error_code; Z3_error_handler * m_error_handler; std::string m_exception_msg; // catch the message associated with a Z3 exception bool m_searching; Z3_ast_print_mode m_print_mode; event_handler * m_interruptable; // Reference to an object that can be interrupted by Z3_interrupt public: // Scoped obj for setting m_interruptable class set_interruptable { context & m_ctx; public: set_interruptable(context & ctx, event_handler & i); ~set_interruptable(); }; // ------------------------ // // Core // // ------------------------ context(context_params * p, bool user_ref_count = false); ~context(); ast_manager & m() const { return *(m_manager.get()); } context_params & params() { m_params.updt_params(); return m_params; } scoped_ptr& cmd() { return m_cmd; } bool produce_proofs() const { return m().proofs_enabled(); } bool produce_models() const { return m_params.m_model; } bool produce_unsat_cores() const { return m_params.m_unsat_core; } bool use_auto_config() const { return m_params.m_auto_config; } unsigned get_timeout() const { return m_params.m_timeout; } unsigned get_rlimit() const { return m_params.rlimit(); } arith_util & autil() { return m_arith_util; } bv_util & bvutil() { return m_bv_util; } datalog::dl_decl_util & datalog_util() { return m_datalog_util; } fpa_util & fpautil() { return m_fpa_util; } datatype_util& dtutil() { return m_dt_plugin->u(); } seq_util& sutil() { return m_sutil; } recfun::util& recfun() { return m_recfun; } family_id get_basic_fid() const { return m_basic_fid; } family_id get_array_fid() const { return m_array_fid; } family_id get_arith_fid() const { return m_arith_fid; } family_id get_bv_fid() const { return m_bv_fid; } family_id get_dt_fid() const { return m_dt_fid; } family_id get_datalog_fid() const { return m_datalog_fid; } family_id get_pb_fid() const { return m_pb_fid; } family_id get_fpa_fid() const { return m_fpa_fid; } family_id get_seq_fid() const { return m_seq_fid; } datatype_decl_plugin * get_dt_plugin() const { return m_dt_plugin; } family_id get_special_relations_fid() const { return m_special_relations_fid; } Z3_error_code get_error_code() const { return m_error_code; } void reset_error_code(); void set_error_code(Z3_error_code err, char const* opt_msg); void set_error_handler(Z3_error_handler h) { m_error_handler = h; } // Sign an error if solver is searching void check_searching(); unsigned add_object(api::object* o); void del_object(api::object* o); Z3_ast_print_mode get_print_mode() const { return m_print_mode; } void set_print_mode(Z3_ast_print_mode m) { m_print_mode = m; } // Store a copy of str in m_string_buffer, and return a reference to it. // This method is used to communicate local/internal strings with the "external world" char * mk_external_string(char const * str, unsigned n); char * mk_external_string(char const * str); char * mk_external_string(std::string && str); // Create a numeral of the given sort expr * mk_numeral_core(rational const & n, sort * s); // Return a conjunction that will be exposed to the "external" world. expr * mk_and(unsigned num_exprs, expr * const * exprs); // Hack for preventing an AST for being GC when ref-count is not used // void persist_ast(ast * n, unsigned num_scopes); // "Save" an AST that will exposed to the "external" world. void save_ast_trail(ast * n); // Similar to previous method, but it "adds" n to the result. void save_multiple_ast_trail(ast * n); // Reset the cache that stores the ASTs exposed in the previous call. // This is a NOOP if ref-count is disabled. void reset_last_result(); // "Save" a reference to an object that is exposed by the API void save_object(object * r); // Process exception: save message and set error code. void handle_exception(z3_exception & ex); char const * get_exception_msg() const { return m_exception_msg.c_str(); } // Interrupt the current interruptable object void interrupt(); void invoke_error_handler(Z3_error_code c); void check_sorts(ast * n); // ------------------------ // // Polynomial manager & caches // // ----------------------- private: reslimit m_limit; pmanager m_pmanager; public: polynomial::manager & pm() { return m_pmanager.pm(); } reslimit & poly_limit() { return m_limit; } // ------------------------ // // RCF manager // // ----------------------- private: unsynch_mpq_manager m_rcf_qm; scoped_ptr m_rcf_manager; public: realclosure::manager & rcfm(); // ------------------------ // // Solver interface for backward compatibility // // ------------------------ smt_params & fparams() { return m_fparams; } }; }; inline api::context * mk_c(Z3_context c) { return reinterpret_cast(c); } #define RESET_ERROR_CODE() { mk_c(c)->reset_error_code(); } #define SET_ERROR_CODE(ERR, MSG) { mk_c(c)->set_error_code(ERR, MSG); } #define CHECK_NON_NULL(_p_,_ret_) { if (_p_ == 0) { SET_ERROR_CODE(Z3_INVALID_ARG, "ast is null"); return _ret_; } } #define CHECK_VALID_AST(_a_, _ret_) { if (_a_ == 0 || !CHECK_REF_COUNT(_a_)) { SET_ERROR_CODE(Z3_INVALID_ARG, "not a valid ast"); return _ret_; } } #define CHECK_SEARCHING(c) mk_c(c)->check_searching(); inline bool is_expr(Z3_ast a) { return is_expr(to_ast(a)); } #define CHECK_IS_EXPR(_p_, _ret_) { if (_p_ == 0 || !is_expr(_p_)) { SET_ERROR_CODE(Z3_INVALID_ARG, "ast is not an expression"); return _ret_; } } inline bool is_bool_expr(Z3_context c, Z3_ast a) { return is_expr(a) && mk_c(c)->m().is_bool(to_expr(a)); } #define CHECK_FORMULA(_a_, _ret_) { if (_a_ == 0 || !CHECK_REF_COUNT(_a_) || !is_bool_expr(c, _a_)) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return _ret_; } } inline void check_sorts(Z3_context c, ast * n) { mk_c(c)->check_sorts(n); } #endif z3-z3-4.8.7/src/api/api_datalog.cpp000066400000000000000000000616071356505360400167510ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_datalog.cpp Abstract: Datalog API Author: Leonardo de Moura (leonardo) 2012-02-29. Revision History: --*/ #include "api/api_datalog.h" #include "api/api_context.h" #include "api/api_util.h" #include "ast/ast_pp.h" #include "api/api_ast_vector.h" #include "api/api_log_macros.h" #include "api/api_stats.h" #include "muz/fp/datalog_parser.h" #include "util/cancel_eh.h" #include "util/scoped_ctrl_c.h" #include "util/scoped_timer.h" #include "muz/fp/dl_cmds.h" #include "cmd_context/cmd_context.h" #include "parsers/smt2/smt2parser.h" #include "muz/base/dl_context.h" #include "muz/fp/dl_register_engine.h" #include "muz/rel/dl_external_relation.h" #include "ast/dl_decl_plugin.h" #include "muz/rel/rel_context.h" namespace api { class fixedpoint_context : public datalog::external_relation_context { void * m_state; reduce_app_callback_fptr m_reduce_app; reduce_assign_callback_fptr m_reduce_assign; datalog::register_engine m_register_engine; datalog::context m_context; ast_ref_vector m_trail; public: fixedpoint_context(ast_manager& m, smt_params& p): m_state(nullptr), m_reduce_app(nullptr), m_reduce_assign(nullptr), m_context(m, m_register_engine, p), m_trail(m) {} ~fixedpoint_context() override {} family_id get_family_id() const override { return const_cast(m_context).get_decl_util().get_family_id(); } void set_state(void* state) { SASSERT(!m_state); m_state = state; symbol name("datalog_relation"); ast_manager& m = m_context.get_manager(); if (!m.has_plugin(name)) { m.register_plugin(name, alloc(datalog::dl_decl_plugin)); } datalog::rel_context_base* rel = m_context.get_rel_context(); if (rel) { datalog::relation_manager& r = rel->get_rmanager(); r.register_plugin(alloc(datalog::external_relation_plugin, *this, r)); } } void set_reduce_app(reduce_app_callback_fptr f) { m_reduce_app = f; } void set_reduce_assign(reduce_assign_callback_fptr f) { m_reduce_assign = f; } void reduce(func_decl* f, unsigned num_args, expr * const* args, expr_ref& result) override { expr* r = nullptr; if (m_reduce_app) { m_reduce_app(m_state, f, num_args, args, &r); result = r; m_trail.push_back(f); for (unsigned i = 0; i < num_args; ++i) { m_trail.push_back(args[i]); } m_trail.push_back(r); } // allow fallthrough. if (r == nullptr) { ast_manager& m = m_context.get_manager(); result = m.mk_app(f, num_args, args); } } void reduce_assign(func_decl* f, unsigned num_args, expr * const* args, unsigned num_out, expr* const* outs) override { if (m_reduce_assign) { m_trail.push_back(f); for (unsigned i = 0; i < num_args; ++i) { m_trail.push_back(args[i]); } m_reduce_assign(m_state, f, num_args, args, num_out, outs); } } datalog::context& ctx() { return m_context; } void add_rule(expr* rule, symbol const& name) { m_context.add_rule(rule, name); } void update_rule(expr* rule, symbol const& name) { m_context.update_rule(rule, name); } void add_table_fact(func_decl* r, unsigned num_args, unsigned args[]) { m_context.add_table_fact(r, num_args, args); } std::string get_last_status() { datalog::execution_result status = m_context.get_status(); switch(status) { case datalog::INPUT_ERROR: return "input error"; case datalog::OK: return "ok"; case datalog::TIMEOUT: return "timeout"; case datalog::APPROX: return "approximated"; default: UNREACHABLE(); return "unknown"; } } std::string to_string(unsigned num_queries, expr* const* queries) { std::stringstream str; m_context.display_smt2(num_queries, queries, str); return str.str(); } unsigned get_num_levels(func_decl* pred) { return m_context.get_num_levels(pred); } expr_ref get_cover_delta(int level, func_decl* pred) { return m_context.get_cover_delta(level, pred); } void add_cover(int level, func_decl* pred, expr* predicate) { m_context.add_cover(level, pred, predicate); } void collect_param_descrs(param_descrs & p) { m_context.collect_params(p); } void updt_params(params_ref const& p) { m_context.updt_params(p); } }; }; extern "C" { //////////////////////////////////// // Datalog utilities // unsigned Z3_API Z3_get_relation_arity(Z3_context c, Z3_sort s) { Z3_TRY; LOG_Z3_get_relation_arity(c, s); RESET_ERROR_CODE(); sort * r = to_sort(s); if (Z3_get_sort_kind(c, s) != Z3_RELATION_SORT) { SET_ERROR_CODE(Z3_INVALID_ARG, "sort should be a relation"); return 0; } return r->get_num_parameters(); Z3_CATCH_RETURN(0); } Z3_sort Z3_API Z3_get_relation_column(Z3_context c, Z3_sort s, unsigned col) { Z3_TRY; LOG_Z3_get_relation_column(c, s, col); RESET_ERROR_CODE(); sort * r = to_sort(s); if (Z3_get_sort_kind(c, s) != Z3_RELATION_SORT) { SET_ERROR_CODE(Z3_INVALID_ARG, "sort should be a relation"); RETURN_Z3(nullptr); } if (col >= r->get_num_parameters()) { SET_ERROR_CODE(Z3_IOB, nullptr); RETURN_Z3(nullptr); } parameter const& p = r->get_parameter(col); if (!p.is_ast() || !is_sort(p.get_ast())) { UNREACHABLE(); warning_msg("Sort parameter expected at %d", col); SET_ERROR_CODE(Z3_INTERNAL_FATAL, "sort parameter expected"); RETURN_Z3(nullptr); } Z3_sort res = of_sort(to_sort(p.get_ast())); RETURN_Z3(res); Z3_CATCH_RETURN(nullptr); } Z3_sort Z3_API Z3_mk_finite_domain_sort(Z3_context c, Z3_symbol name, uint64_t size) { Z3_TRY; LOG_Z3_mk_finite_domain_sort(c, name, size); RESET_ERROR_CODE(); sort* s = mk_c(c)->datalog_util().mk_sort(to_symbol(name), size); mk_c(c)->save_ast_trail(s); RETURN_Z3(of_sort(s)); Z3_CATCH_RETURN(nullptr); } bool Z3_API Z3_get_finite_domain_sort_size(Z3_context c, Z3_sort s, uint64_t * out) { Z3_TRY; if (out) { *out = 0; } if (Z3_get_sort_kind(c, s) != Z3_FINITE_DOMAIN_SORT) { return false; } if (!out) { return false; } // must start logging here, since function uses Z3_get_sort_kind above LOG_Z3_get_finite_domain_sort_size(c, s, out); RESET_ERROR_CODE(); VERIFY(mk_c(c)->datalog_util().try_get_size(to_sort(s), *out)); return true; Z3_CATCH_RETURN(false); } Z3_fixedpoint Z3_API Z3_mk_fixedpoint(Z3_context c) { Z3_TRY; LOG_Z3_mk_fixedpoint(c); RESET_ERROR_CODE(); Z3_fixedpoint_ref * d = alloc(Z3_fixedpoint_ref, *mk_c(c)); d->m_datalog = alloc(api::fixedpoint_context, mk_c(c)->m(), mk_c(c)->fparams()); mk_c(c)->save_object(d); Z3_fixedpoint r = of_datalog(d); RETURN_Z3(r); Z3_CATCH_RETURN(nullptr); } void Z3_API Z3_fixedpoint_inc_ref(Z3_context c, Z3_fixedpoint s) { Z3_TRY; LOG_Z3_fixedpoint_inc_ref(c, s); RESET_ERROR_CODE(); to_fixedpoint(s)->inc_ref(); Z3_CATCH; } void Z3_API Z3_fixedpoint_dec_ref(Z3_context c, Z3_fixedpoint s) { Z3_TRY; LOG_Z3_fixedpoint_dec_ref(c, s); RESET_ERROR_CODE(); to_fixedpoint(s)->dec_ref(); Z3_CATCH; } void Z3_API Z3_fixedpoint_assert(Z3_context c, Z3_fixedpoint d, Z3_ast a) { Z3_TRY; LOG_Z3_fixedpoint_assert(c, d, a); RESET_ERROR_CODE(); CHECK_FORMULA(a,); to_fixedpoint_ref(d)->ctx().assert_expr(to_expr(a)); Z3_CATCH; } void Z3_API Z3_fixedpoint_add_rule(Z3_context c, Z3_fixedpoint d, Z3_ast a, Z3_symbol name) { Z3_TRY; LOG_Z3_fixedpoint_add_rule(c, d, a, name); RESET_ERROR_CODE(); CHECK_FORMULA(a,); to_fixedpoint_ref(d)->add_rule(to_expr(a), to_symbol(name)); Z3_CATCH; } void Z3_API Z3_fixedpoint_add_fact(Z3_context c, Z3_fixedpoint d, Z3_func_decl r, unsigned num_args, unsigned args[]) { Z3_TRY; LOG_Z3_fixedpoint_add_fact(c, d, r, num_args, args); RESET_ERROR_CODE(); to_fixedpoint_ref(d)->add_table_fact(to_func_decl(r), num_args, args); Z3_CATCH; } Z3_lbool Z3_API Z3_fixedpoint_query(Z3_context c,Z3_fixedpoint d, Z3_ast q) { Z3_TRY; LOG_Z3_fixedpoint_query(c, d, q); RESET_ERROR_CODE(); lbool r = l_undef; unsigned timeout = to_fixedpoint(d)->m_params.get_uint("timeout", mk_c(c)->get_timeout()); unsigned rlimit = to_fixedpoint(d)->m_params.get_uint("rlimit", mk_c(c)->get_rlimit()); bool use_ctrl_c = to_fixedpoint(d)->m_params.get_bool("ctrl_c", true); { scoped_rlimit _rlimit(mk_c(c)->m().limit(), rlimit); cancel_eh eh(mk_c(c)->m().limit()); api::context::set_interruptable si(*(mk_c(c)), eh); scoped_timer timer(timeout, &eh); scoped_ctrl_c ctrlc(eh, false, use_ctrl_c); try { r = to_fixedpoint_ref(d)->ctx().query(to_expr(q)); } catch (z3_exception& ex) { r = l_undef; mk_c(c)->handle_exception(ex); } to_fixedpoint_ref(d)->ctx().cleanup(); } return of_lbool(r); Z3_CATCH_RETURN(Z3_L_UNDEF); } Z3_lbool Z3_API Z3_fixedpoint_query_relations( Z3_context c,Z3_fixedpoint d, unsigned num_relations, Z3_func_decl const relations[]) { Z3_TRY; LOG_Z3_fixedpoint_query_relations(c, d, num_relations, relations); RESET_ERROR_CODE(); lbool r = l_undef; unsigned timeout = to_fixedpoint(d)->m_params.get_uint("timeout", mk_c(c)->get_timeout()); cancel_eh eh(mk_c(c)->m().limit()); api::context::set_interruptable si(*(mk_c(c)), eh); { scoped_timer timer(timeout, &eh); try { r = to_fixedpoint_ref(d)->ctx().rel_query(num_relations, to_func_decls(relations)); } catch (z3_exception& ex) { mk_c(c)->handle_exception(ex); r = l_undef; } to_fixedpoint_ref(d)->ctx().cleanup(); } return of_lbool(r); Z3_CATCH_RETURN(Z3_L_UNDEF); } Z3_ast Z3_API Z3_fixedpoint_get_answer(Z3_context c, Z3_fixedpoint d) { Z3_TRY; LOG_Z3_fixedpoint_get_answer(c, d); RESET_ERROR_CODE(); expr* e = to_fixedpoint_ref(d)->ctx().get_answer_as_formula(); mk_c(c)->save_ast_trail(e); RETURN_Z3(of_expr(e)); Z3_CATCH_RETURN(nullptr); } Z3_string Z3_API Z3_fixedpoint_get_reason_unknown(Z3_context c,Z3_fixedpoint d) { Z3_TRY; LOG_Z3_fixedpoint_get_reason_unknown(c, d); RESET_ERROR_CODE(); return mk_c(c)->mk_external_string(to_fixedpoint_ref(d)->get_last_status()); Z3_CATCH_RETURN(""); } Z3_string Z3_API Z3_fixedpoint_to_string( Z3_context c, Z3_fixedpoint d, unsigned num_queries, Z3_ast _queries[]) { Z3_TRY; expr*const* queries = to_exprs(num_queries, _queries); LOG_Z3_fixedpoint_to_string(c, d, num_queries, _queries); RESET_ERROR_CODE(); return mk_c(c)->mk_external_string(to_fixedpoint_ref(d)->to_string(num_queries, queries)); Z3_CATCH_RETURN(""); } Z3_ast_vector Z3_fixedpoint_from_stream( Z3_context c, Z3_fixedpoint d, std::istream& s) { ast_manager& m = mk_c(c)->m(); dl_collected_cmds coll(m); cmd_context ctx(false, &m); install_dl_collect_cmds(coll, ctx); ctx.set_ignore_check(true); if (!parse_smt2_commands(ctx, s)) { SET_ERROR_CODE(Z3_PARSER_ERROR, nullptr); return nullptr; } Z3_ast_vector_ref* v = alloc(Z3_ast_vector_ref, *mk_c(c), m); mk_c(c)->save_object(v); for (expr * q : coll.m_queries) { v->m_ast_vector.push_back(q); } for (func_decl * f : coll.m_rels) { to_fixedpoint_ref(d)->ctx().register_predicate(f, true); } for (unsigned i = 0; i < coll.m_rules.size(); ++i) { to_fixedpoint_ref(d)->add_rule(coll.m_rules[i].get(), coll.m_names[i]); } for (expr * e : ctx.assertions()) { to_fixedpoint_ref(d)->ctx().assert_expr(e); } return of_ast_vector(v); } Z3_ast_vector Z3_API Z3_fixedpoint_from_string( Z3_context c, Z3_fixedpoint d, Z3_string s) { Z3_TRY; LOG_Z3_fixedpoint_from_string(c, d, s); std::string str(s); std::istringstream is(str); RETURN_Z3(Z3_fixedpoint_from_stream(c, d, is)); Z3_CATCH_RETURN(nullptr); } Z3_ast_vector Z3_API Z3_fixedpoint_from_file( Z3_context c, Z3_fixedpoint d, Z3_string s) { Z3_TRY; LOG_Z3_fixedpoint_from_file(c, d, s); std::ifstream is(s); if (!is) { SET_ERROR_CODE(Z3_PARSER_ERROR, nullptr); RETURN_Z3(nullptr); } RETURN_Z3(Z3_fixedpoint_from_stream(c, d, is)); Z3_CATCH_RETURN(nullptr); } Z3_stats Z3_API Z3_fixedpoint_get_statistics(Z3_context c,Z3_fixedpoint d) { Z3_TRY; LOG_Z3_fixedpoint_get_statistics(c, d); RESET_ERROR_CODE(); Z3_stats_ref * st = alloc(Z3_stats_ref, (*mk_c(c))); to_fixedpoint_ref(d)->ctx().collect_statistics(st->m_stats); mk_c(c)->save_object(st); Z3_stats r = of_stats(st); RETURN_Z3(r); Z3_CATCH_RETURN(nullptr); } void Z3_API Z3_fixedpoint_register_relation(Z3_context c,Z3_fixedpoint d, Z3_func_decl f) { Z3_TRY; LOG_Z3_fixedpoint_register_relation(c, d, f); to_fixedpoint_ref(d)->ctx().register_predicate(to_func_decl(f), true); Z3_CATCH; } void Z3_API Z3_fixedpoint_set_predicate_representation( Z3_context c, Z3_fixedpoint d, Z3_func_decl f, unsigned num_relations, Z3_symbol const relation_kinds[]) { Z3_TRY; LOG_Z3_fixedpoint_set_predicate_representation(c, d, f, num_relations, relation_kinds); svector kinds; for (unsigned i = 0; i < num_relations; ++i) { kinds.push_back(to_symbol(relation_kinds[i])); } to_fixedpoint_ref(d)->ctx().set_predicate_representation(to_func_decl(f), num_relations, kinds.c_ptr()); Z3_CATCH; } Z3_ast_vector Z3_API Z3_fixedpoint_get_rules( Z3_context c, Z3_fixedpoint d) { Z3_TRY; LOG_Z3_fixedpoint_get_rules(c, d); ast_manager& m = mk_c(c)->m(); Z3_ast_vector_ref* v = alloc(Z3_ast_vector_ref, *mk_c(c), m); mk_c(c)->save_object(v); expr_ref_vector rules(m), queries(m); svector names; to_fixedpoint_ref(d)->ctx().get_rules_as_formulas(rules, queries, names); for (expr* r : rules) { v->m_ast_vector.push_back(r); } for (expr* q : queries) { v->m_ast_vector.push_back(m.mk_not(q)); } RETURN_Z3(of_ast_vector(v)); Z3_CATCH_RETURN(nullptr); } Z3_ast_vector Z3_API Z3_fixedpoint_get_assertions( Z3_context c, Z3_fixedpoint d) { Z3_TRY; LOG_Z3_fixedpoint_get_assertions(c, d); ast_manager& m = mk_c(c)->m(); Z3_ast_vector_ref* v = alloc(Z3_ast_vector_ref, *mk_c(c), m); mk_c(c)->save_object(v); unsigned num_asserts = to_fixedpoint_ref(d)->ctx().get_num_assertions(); for (unsigned i = 0; i < num_asserts; ++i) { v->m_ast_vector.push_back(to_fixedpoint_ref(d)->ctx().get_assertion(i)); } RETURN_Z3(of_ast_vector(v)); Z3_CATCH_RETURN(nullptr); } void Z3_API Z3_fixedpoint_set_reduce_assign_callback( Z3_context c, Z3_fixedpoint d, Z3_fixedpoint_reduce_assign_callback_fptr f) { Z3_TRY; // no logging to_fixedpoint_ref(d)->set_reduce_assign((reduce_assign_callback_fptr)f); Z3_CATCH; } void Z3_API Z3_fixedpoint_set_reduce_app_callback( Z3_context c, Z3_fixedpoint d, Z3_fixedpoint_reduce_app_callback_fptr f) { Z3_TRY; // no logging to_fixedpoint_ref(d)->set_reduce_app((reduce_app_callback_fptr)f); Z3_CATCH; } void Z3_API Z3_fixedpoint_init(Z3_context c,Z3_fixedpoint d, void* state) { Z3_TRY; // not logged to_fixedpoint_ref(d)->set_state(state); Z3_CATCH; } void Z3_API Z3_fixedpoint_update_rule(Z3_context c, Z3_fixedpoint d, Z3_ast a, Z3_symbol name) { Z3_TRY; LOG_Z3_fixedpoint_update_rule(c, d, a, name); RESET_ERROR_CODE(); CHECK_FORMULA(a,); to_fixedpoint_ref(d)->update_rule(to_expr(a), to_symbol(name)); Z3_CATCH; } unsigned Z3_API Z3_fixedpoint_get_num_levels(Z3_context c, Z3_fixedpoint d, Z3_func_decl pred) { Z3_TRY; LOG_Z3_fixedpoint_get_num_levels(c, d, pred); RESET_ERROR_CODE(); return to_fixedpoint_ref(d)->get_num_levels(to_func_decl(pred)); Z3_CATCH_RETURN(0) } Z3_ast Z3_API Z3_fixedpoint_get_cover_delta(Z3_context c, Z3_fixedpoint d, int level, Z3_func_decl pred) { Z3_TRY; LOG_Z3_fixedpoint_get_cover_delta(c, d, level, pred); RESET_ERROR_CODE(); expr_ref r = to_fixedpoint_ref(d)->get_cover_delta(level, to_func_decl(pred)); mk_c(c)->save_ast_trail(r); RETURN_Z3(of_expr(r.get())); Z3_CATCH_RETURN(nullptr); } void Z3_API Z3_fixedpoint_add_cover(Z3_context c, Z3_fixedpoint d, int level, Z3_func_decl pred, Z3_ast property) { Z3_TRY; LOG_Z3_fixedpoint_add_cover(c, d, level, pred, property); RESET_ERROR_CODE(); to_fixedpoint_ref(d)->add_cover(level, to_func_decl(pred), to_expr(property)); Z3_CATCH; } Z3_string Z3_API Z3_fixedpoint_get_help(Z3_context c, Z3_fixedpoint d) { Z3_TRY; LOG_Z3_fixedpoint_get_help(c, d); RESET_ERROR_CODE(); std::ostringstream buffer; param_descrs descrs; to_fixedpoint_ref(d)->collect_param_descrs(descrs); descrs.display(buffer); return mk_c(c)->mk_external_string(buffer.str()); Z3_CATCH_RETURN(""); } Z3_param_descrs Z3_API Z3_fixedpoint_get_param_descrs(Z3_context c, Z3_fixedpoint f) { Z3_TRY; LOG_Z3_fixedpoint_get_param_descrs(c, f); RESET_ERROR_CODE(); Z3_param_descrs_ref * d = alloc(Z3_param_descrs_ref, *mk_c(c)); mk_c(c)->save_object(d); to_fixedpoint_ref(f)->collect_param_descrs(d->m_descrs); Z3_param_descrs r = of_param_descrs(d); RETURN_Z3(r); Z3_CATCH_RETURN(nullptr); } void Z3_API Z3_fixedpoint_set_params(Z3_context c, Z3_fixedpoint d, Z3_params p) { Z3_TRY; LOG_Z3_fixedpoint_set_params(c, d, p); RESET_ERROR_CODE(); param_descrs descrs; to_fixedpoint_ref(d)->collect_param_descrs(descrs); to_params(p)->m_params.validate(descrs); to_fixedpoint_ref(d)->updt_params(to_param_ref(p)); to_fixedpoint(d)->m_params = to_param_ref(p); Z3_CATCH; } void Z3_API Z3_fixedpoint_add_callback(Z3_context c, Z3_fixedpoint d, void *state, Z3_fixedpoint_new_lemma_eh new_lemma_eh, Z3_fixedpoint_predecessor_eh predecessor_eh, Z3_fixedpoint_unfold_eh unfold_eh){ Z3_TRY; // not logged to_fixedpoint_ref(d)->ctx().add_callback(state, reinterpret_cast(new_lemma_eh), reinterpret_cast(predecessor_eh), reinterpret_cast(unfold_eh)); Z3_CATCH; } void Z3_API Z3_fixedpoint_add_constraint (Z3_context c, Z3_fixedpoint d, Z3_ast e, unsigned lvl){ to_fixedpoint_ref(d)->ctx().add_constraint(to_expr(e), lvl); } Z3_lbool Z3_API Z3_fixedpoint_query_from_lvl (Z3_context c, Z3_fixedpoint d, Z3_ast q, unsigned lvl) { Z3_TRY; LOG_Z3_fixedpoint_query_from_lvl (c, d, q, lvl); RESET_ERROR_CODE(); lbool r = l_undef; unsigned timeout = to_fixedpoint(d)->m_params.get_uint("timeout", mk_c(c)->get_timeout()); unsigned rlimit = to_fixedpoint(d)->m_params.get_uint("rlimit", mk_c(c)->get_rlimit()); { scoped_rlimit _rlimit(mk_c(c)->m().limit(), rlimit); cancel_eh eh(mk_c(c)->m().limit()); api::context::set_interruptable si(*(mk_c(c)), eh); scoped_timer timer(timeout, &eh); try { r = to_fixedpoint_ref(d)->ctx().query_from_lvl (to_expr(q), lvl); } catch (z3_exception& ex) { mk_c(c)->handle_exception(ex); r = l_undef; } to_fixedpoint_ref(d)->ctx().cleanup(); } return of_lbool(r); Z3_CATCH_RETURN(Z3_L_UNDEF); } Z3_ast Z3_API Z3_fixedpoint_get_ground_sat_answer(Z3_context c, Z3_fixedpoint d) { Z3_TRY; LOG_Z3_fixedpoint_get_ground_sat_answer(c, d); RESET_ERROR_CODE(); expr* e = to_fixedpoint_ref(d)->ctx().get_ground_sat_answer(); mk_c(c)->save_ast_trail(e); RETURN_Z3(of_expr(e)); Z3_CATCH_RETURN(nullptr); } Z3_ast_vector Z3_API Z3_fixedpoint_get_rules_along_trace( Z3_context c, Z3_fixedpoint d) { Z3_TRY; LOG_Z3_fixedpoint_get_rules_along_trace(c, d); ast_manager& m = mk_c(c)->m(); Z3_ast_vector_ref* v = alloc(Z3_ast_vector_ref, *mk_c(c), m); mk_c(c)->save_object(v); expr_ref_vector rules(m); svector names; to_fixedpoint_ref(d)->ctx().get_rules_along_trace_as_formulas(rules, names); for (unsigned i = 0; i < rules.size(); ++i) { v->m_ast_vector.push_back(rules[i].get()); } RETURN_Z3(of_ast_vector(v)); Z3_CATCH_RETURN(nullptr); } Z3_symbol Z3_API Z3_fixedpoint_get_rule_names_along_trace( Z3_context c, Z3_fixedpoint d) { Z3_TRY; LOG_Z3_fixedpoint_get_rule_names_along_trace(c, d); ast_manager& m = mk_c(c)->m(); Z3_ast_vector_ref* v = alloc(Z3_ast_vector_ref, *mk_c(c), m); mk_c(c)->save_object(v); expr_ref_vector rules(m); svector names; std::stringstream ss; to_fixedpoint_ref(d)->ctx().get_rules_along_trace_as_formulas(rules, names); for (unsigned i = 0; i < names.size(); ++i) { ss << ";" << names[i].str(); } RETURN_Z3(of_symbol(symbol(ss.str().substr(1).c_str()))); Z3_CATCH_RETURN(nullptr); } void Z3_API Z3_fixedpoint_add_invariant(Z3_context c, Z3_fixedpoint d, Z3_func_decl pred, Z3_ast property) { Z3_TRY; LOG_Z3_fixedpoint_add_invariant(c, d, pred, property); RESET_ERROR_CODE(); to_fixedpoint_ref(d)->ctx ().add_invariant(to_func_decl(pred), to_expr(property)); Z3_CATCH; } Z3_ast Z3_API Z3_fixedpoint_get_reachable(Z3_context c, Z3_fixedpoint d, Z3_func_decl pred) { Z3_TRY; LOG_Z3_fixedpoint_get_reachable(c, d, pred); RESET_ERROR_CODE(); expr_ref r = to_fixedpoint_ref(d)->ctx().get_reachable(to_func_decl(pred)); mk_c(c)->save_ast_trail(r); RETURN_Z3(of_expr(r.get())); Z3_CATCH_RETURN(nullptr); } }; z3-z3-4.8.7/src/api/api_datalog.h000066400000000000000000000023741356505360400164120ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_datalog.h Abstract: Datalog API old external_relation_context_impl Author: Leonardo de Moura (leonardo) 2012-02-29. Revision History: --*/ #ifndef API_DATALOG_H_ #define API_DATALOG_H_ #include "api/z3.h" #include "ast/ast.h" #include "smt/params/smt_params.h" #include "smt/smt_kernel.h" #include "api/api_util.h" typedef void (*reduce_app_callback_fptr)(void*, func_decl*, unsigned, expr*const*, expr**); typedef void (*reduce_assign_callback_fptr)(void*, func_decl*, unsigned, expr*const*, unsigned, expr*const*); namespace api { class fixedpoint_context; class context; }; struct Z3_fixedpoint_ref : public api::object { api::fixedpoint_context * m_datalog; params_ref m_params; Z3_fixedpoint_ref(api::context& c): api::object(c), m_datalog(nullptr) {} ~Z3_fixedpoint_ref() override { dealloc(m_datalog); } }; inline Z3_fixedpoint_ref * to_fixedpoint(Z3_fixedpoint s) { return reinterpret_cast(s); } inline Z3_fixedpoint of_datalog(Z3_fixedpoint_ref * s) { return reinterpret_cast(s); } inline api::fixedpoint_context * to_fixedpoint_ref(Z3_fixedpoint s) { return to_fixedpoint(s)->m_datalog; } #endif z3-z3-4.8.7/src/api/api_datatype.cpp000066400000000000000000000543701356505360400171500ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_datatype.cpp Abstract: API for datatype theory Author: Leonardo de Moura (leonardo) 2012-02-29. Revision History: --*/ #include "api/z3.h" #include "api/api_log_macros.h" #include "api/api_context.h" #include "api/api_util.h" #include "ast/datatype_decl_plugin.h" extern "C" { Z3_sort Z3_API Z3_mk_tuple_sort(Z3_context c, Z3_symbol name, unsigned num_fields, Z3_symbol const field_names[], Z3_sort const field_sorts[], Z3_func_decl * mk_tuple_decl, Z3_func_decl proj_decls[]) { Z3_TRY; LOG_Z3_mk_tuple_sort(c, name, num_fields, field_names, field_sorts, mk_tuple_decl, proj_decls); RESET_ERROR_CODE(); mk_c(c)->reset_last_result(); ast_manager& m = mk_c(c)->m(); datatype_util& dt_util = mk_c(c)->dtutil(); sort_ref_vector tuples(m); sort* tuple; std::string recognizer_s("is_"); recognizer_s += to_symbol(name).str(); symbol recognizer(recognizer_s.c_str()); ptr_vector acc; for (unsigned i = 0; i < num_fields; ++i) { acc.push_back(mk_accessor_decl(m, to_symbol(field_names[i]), type_ref(to_sort(field_sorts[i])))); } constructor_decl* constrs[1] = { mk_constructor_decl(to_symbol(name), recognizer, acc.size(), acc.c_ptr()) }; { datatype_decl * dt = mk_datatype_decl(dt_util, to_symbol(name), 0, nullptr, 1, constrs); bool is_ok = mk_c(c)->get_dt_plugin()->mk_datatypes(1, &dt, 0, nullptr, tuples); del_datatype_decl(dt); if (!is_ok) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } } // create tuple type SASSERT(tuples.size() == 1); tuple = tuples[0].get(); mk_c(c)->save_multiple_ast_trail(tuple); // create constructor SASSERT(dt_util.is_datatype(tuple)); SASSERT(!dt_util.is_recursive(tuple)); ptr_vector const & decls = *dt_util.get_datatype_constructors(tuple); func_decl* decl = (decls)[0]; mk_c(c)->save_multiple_ast_trail(decl); *mk_tuple_decl = of_func_decl(decl); // Create projections ptr_vector const & _accs = *dt_util.get_constructor_accessors(decl); SASSERT(_accs.size() == num_fields); for (unsigned i = 0; i < _accs.size(); i++) { mk_c(c)->save_multiple_ast_trail(_accs[i]); proj_decls[i] = of_func_decl(_accs[i]); } RETURN_Z3_mk_tuple_sort(of_sort(tuple)); Z3_CATCH_RETURN(nullptr); } Z3_sort Z3_API Z3_mk_enumeration_sort(Z3_context c, Z3_symbol name, unsigned n, Z3_symbol const enum_names[], Z3_func_decl enum_consts[], Z3_func_decl enum_testers[]) { Z3_TRY; LOG_Z3_mk_enumeration_sort(c, name, n, enum_names, enum_consts, enum_testers); RESET_ERROR_CODE(); mk_c(c)->reset_last_result(); ast_manager& m = mk_c(c)->m(); datatype_util& dt_util = mk_c(c)->dtutil(); sort_ref_vector sorts(m); sort* e; ptr_vector constrs; for (unsigned i = 0; i < n; ++i) { symbol e_name(to_symbol(enum_names[i])); std::string recognizer_s("is_"); recognizer_s += e_name.str(); symbol recognizer(recognizer_s.c_str()); constrs.push_back(mk_constructor_decl(e_name, recognizer, 0, nullptr)); } { datatype_decl * dt = mk_datatype_decl(dt_util, to_symbol(name), 0, nullptr, n, constrs.c_ptr()); bool is_ok = mk_c(c)->get_dt_plugin()->mk_datatypes(1, &dt, 0, nullptr, sorts); del_datatype_decl(dt); if (!is_ok) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } } // create enum type. SASSERT(sorts.size() == 1); e = sorts[0].get(); mk_c(c)->save_multiple_ast_trail(e); // create constructor SASSERT(dt_util.is_datatype(e)); SASSERT(!dt_util.is_recursive(e)); ptr_vector const & decls = *dt_util.get_datatype_constructors(e); SASSERT(decls.size() == n); for (unsigned i = 0; i < n; ++i) { func_decl* decl = (decls)[i]; mk_c(c)->save_multiple_ast_trail(decl); enum_consts[i] = of_func_decl(decl); decl = dt_util.get_constructor_is(decl); mk_c(c)->save_multiple_ast_trail(decl); enum_testers[i] = of_func_decl(decl); } RETURN_Z3_mk_enumeration_sort(of_sort(e)); Z3_CATCH_RETURN(nullptr); } Z3_sort Z3_API Z3_mk_list_sort(Z3_context c, Z3_symbol name, Z3_sort elem_sort, Z3_func_decl* nil_decl, Z3_func_decl* is_nil_decl, Z3_func_decl* cons_decl, Z3_func_decl* is_cons_decl, Z3_func_decl* head_decl, Z3_func_decl* tail_decl ) { Z3_TRY; LOG_Z3_mk_list_sort(c, name, elem_sort, nil_decl, is_nil_decl, cons_decl, is_cons_decl, head_decl, tail_decl); RESET_ERROR_CODE(); ast_manager& m = mk_c(c)->m(); func_decl_ref nil(m), is_nil(m), cons(m), is_cons(m), head(m), tail(m); datatype_util& dt_util = mk_c(c)->dtutil(); mk_c(c)->reset_last_result(); sort_ref s = dt_util.mk_list_datatype(to_sort(elem_sort), to_symbol(name), cons, is_cons, head, tail, nil, is_nil); if (!s) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } mk_c(c)->save_multiple_ast_trail(s); if (nil_decl) { mk_c(c)->save_multiple_ast_trail(nil); *nil_decl = of_func_decl(nil); } if (is_nil_decl) { mk_c(c)->save_multiple_ast_trail(is_nil); *is_nil_decl = of_func_decl(is_nil); } if (cons_decl) { mk_c(c)->save_multiple_ast_trail(cons); *cons_decl = of_func_decl(cons); } if (is_cons_decl) { mk_c(c)->save_multiple_ast_trail(is_cons); *is_cons_decl = of_func_decl(is_cons); } if (head_decl) { mk_c(c)->save_multiple_ast_trail(head); *head_decl = of_func_decl(head); } if (tail_decl) { mk_c(c)->save_multiple_ast_trail(tail); *tail_decl = of_func_decl(tail); } RETURN_Z3_mk_list_sort(of_sort(s)); Z3_CATCH_RETURN(nullptr); } struct constructor { symbol m_name; symbol m_tester; svector m_field_names; sort_ref_vector m_sorts; unsigned_vector m_sort_refs; func_decl_ref m_constructor; constructor(ast_manager& m) : m_sorts(m), m_constructor(m) {} }; Z3_constructor Z3_API Z3_mk_constructor(Z3_context c, Z3_symbol name, Z3_symbol tester, unsigned num_fields, Z3_symbol const field_names[], Z3_sort const sorts[], unsigned sort_refs[] ) { Z3_TRY; LOG_Z3_mk_constructor(c, name, tester, num_fields, field_names, sorts, sort_refs); RESET_ERROR_CODE(); ast_manager& m = mk_c(c)->m(); constructor* cnstr = alloc(constructor, m); cnstr->m_name = to_symbol(name); cnstr->m_tester = to_symbol(tester); for (unsigned i = 0; i < num_fields; ++i) { cnstr->m_field_names.push_back(to_symbol(field_names[i])); cnstr->m_sorts.push_back(to_sort(sorts[i])); cnstr->m_sort_refs.push_back(sort_refs[i]); } RETURN_Z3(reinterpret_cast(cnstr)); Z3_CATCH_RETURN(nullptr); } void Z3_API Z3_query_constructor(Z3_context c, Z3_constructor constr, unsigned num_fields, Z3_func_decl* constructor_decl, Z3_func_decl* tester, Z3_func_decl accessors[]) { Z3_TRY; LOG_Z3_query_constructor(c, constr, num_fields, constructor_decl, tester, accessors); RESET_ERROR_CODE(); mk_c(c)->reset_last_result(); if (!constr) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return; } ast_manager& m = mk_c(c)->m(); datatype_util data_util(m); func_decl* f = reinterpret_cast(constr)->m_constructor.get(); if (!f) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return; } if (constructor_decl) { mk_c(c)->save_multiple_ast_trail(f); *constructor_decl = of_func_decl(f); } if (tester) { func_decl* f2 = data_util.get_constructor_is(f); mk_c(c)->save_multiple_ast_trail(f2); *tester = of_func_decl(f2); } ptr_vector const& accs = *data_util.get_constructor_accessors(f); for (unsigned i = 0; i < num_fields; ++i) { func_decl* f2 = (accs)[i]; mk_c(c)->save_multiple_ast_trail(f2); accessors[i] = of_func_decl(f2); } RETURN_Z3_query_constructor; Z3_CATCH; } void Z3_API Z3_del_constructor(Z3_context c, Z3_constructor constr) { Z3_TRY; LOG_Z3_del_constructor(c, constr); RESET_ERROR_CODE(); dealloc(reinterpret_cast(constr)); Z3_CATCH; } static datatype_decl* mk_datatype_decl(Z3_context c, Z3_symbol name, unsigned num_constructors, Z3_constructor constructors[]) { datatype_util& dt_util = mk_c(c)->dtutil(); ast_manager& m = mk_c(c)->m(); ptr_vector constrs; for (unsigned i = 0; i < num_constructors; ++i) { constructor* cn = reinterpret_cast(constructors[i]); ptr_vector acc; for (unsigned j = 0; j < cn->m_sorts.size(); ++j) { if (cn->m_sorts[j].get()) { acc.push_back(mk_accessor_decl(m, cn->m_field_names[j], type_ref(cn->m_sorts[j].get()))); } else { acc.push_back(mk_accessor_decl(m, cn->m_field_names[j], type_ref(cn->m_sort_refs[j]))); } } constrs.push_back(mk_constructor_decl(cn->m_name, cn->m_tester, acc.size(), acc.c_ptr())); } return mk_datatype_decl(dt_util, to_symbol(name), 0, nullptr, num_constructors, constrs.c_ptr()); } Z3_sort Z3_API Z3_mk_datatype(Z3_context c, Z3_symbol name, unsigned num_constructors, Z3_constructor constructors[]) { Z3_TRY; LOG_Z3_mk_datatype(c, name, num_constructors, constructors); RESET_ERROR_CODE(); ast_manager& m = mk_c(c)->m(); datatype_util data_util(m); sort_ref_vector sorts(m); { datatype_decl * data = mk_datatype_decl(c, name, num_constructors, constructors); bool is_ok = mk_c(c)->get_dt_plugin()->mk_datatypes(1, &data, 0, nullptr, sorts); del_datatype_decl(data); if (!is_ok) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } } sort * s = sorts.get(0); mk_c(c)->save_ast_trail(s); ptr_vector const& cnstrs = *data_util.get_datatype_constructors(s); for (unsigned i = 0; i < num_constructors; ++i) { constructor* cn = reinterpret_cast(constructors[i]); cn->m_constructor = cnstrs[i]; } RETURN_Z3_mk_datatype(of_sort(s)); Z3_CATCH_RETURN(nullptr); } typedef ptr_vector constructor_list; Z3_constructor_list Z3_API Z3_mk_constructor_list(Z3_context c, unsigned num_constructors, Z3_constructor const constructors[]) { Z3_TRY; LOG_Z3_mk_constructor_list(c, num_constructors, constructors); RESET_ERROR_CODE(); constructor_list* result = alloc(ptr_vector); for (unsigned i = 0; i < num_constructors; ++i) { result->push_back(reinterpret_cast(constructors[i])); } RETURN_Z3(reinterpret_cast(result)); Z3_CATCH_RETURN(nullptr); } void Z3_API Z3_del_constructor_list(Z3_context c, Z3_constructor_list clist) { Z3_TRY; LOG_Z3_del_constructor_list(c, clist); RESET_ERROR_CODE(); dealloc(reinterpret_cast(clist)); Z3_CATCH; } void Z3_API Z3_mk_datatypes(Z3_context c, unsigned num_sorts, Z3_symbol const sort_names[], Z3_sort sorts[], Z3_constructor_list constructor_lists[]) { Z3_TRY; LOG_Z3_mk_datatypes(c, num_sorts, sort_names, sorts, constructor_lists); RESET_ERROR_CODE(); ast_manager& m = mk_c(c)->m(); mk_c(c)->reset_last_result(); datatype_util data_util(m); ptr_vector datas; for (unsigned i = 0; i < num_sorts; ++i) { constructor_list* cl = reinterpret_cast(constructor_lists[i]); datas.push_back(mk_datatype_decl(c, sort_names[i], cl->size(), reinterpret_cast(cl->c_ptr()))); } sort_ref_vector _sorts(m); bool ok = mk_c(c)->get_dt_plugin()->mk_datatypes(datas.size(), datas.c_ptr(), 0, nullptr, _sorts); del_datatype_decls(datas.size(), datas.c_ptr()); if (!ok) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return; } SASSERT(_sorts.size() == num_sorts); for (unsigned i = 0; i < _sorts.size(); ++i) { sort* s = _sorts[i].get(); mk_c(c)->save_multiple_ast_trail(s); sorts[i] = of_sort(s); constructor_list* cl = reinterpret_cast(constructor_lists[i]); ptr_vector const& cnstrs = *data_util.get_datatype_constructors(s); for (unsigned j = 0; j < cl->size(); ++j) { constructor* cn = (*cl)[j]; cn->m_constructor = cnstrs[j]; } } RETURN_Z3_mk_datatypes; Z3_CATCH; } unsigned Z3_API Z3_get_datatype_sort_num_constructors(Z3_context c, Z3_sort t) { Z3_TRY; LOG_Z3_get_datatype_sort_num_constructors(c, t); RESET_ERROR_CODE(); CHECK_VALID_AST(t, 0); sort * _t = to_sort(t); datatype_util& dt_util = mk_c(c)->dtutil(); if (!dt_util.is_datatype(_t)) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return 0; } return dt_util.get_datatype_constructors(_t)->size(); Z3_CATCH_RETURN(0); } Z3_func_decl get_datatype_sort_constructor_core(Z3_context c, Z3_sort t, unsigned idx) { RESET_ERROR_CODE(); CHECK_VALID_AST(t, nullptr); sort * _t = to_sort(t); datatype_util& dt_util = mk_c(c)->dtutil(); if (!dt_util.is_datatype(_t)) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return nullptr; } ptr_vector const & decls = *dt_util.get_datatype_constructors(_t); if (idx >= decls.size()) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return nullptr; } func_decl* decl = (decls)[idx]; mk_c(c)->save_ast_trail(decl); return of_func_decl(decl); } Z3_func_decl Z3_API Z3_get_datatype_sort_constructor(Z3_context c, Z3_sort t, unsigned idx) { Z3_TRY; LOG_Z3_get_datatype_sort_constructor(c, t, idx); RESET_ERROR_CODE(); Z3_func_decl r = get_datatype_sort_constructor_core(c, t, idx); RETURN_Z3(r); Z3_CATCH_RETURN(nullptr); } Z3_func_decl Z3_API Z3_get_datatype_sort_recognizer(Z3_context c, Z3_sort t, unsigned idx) { Z3_TRY; LOG_Z3_get_datatype_sort_recognizer(c, t, idx); RESET_ERROR_CODE(); sort * _t = to_sort(t); datatype_util& dt_util = mk_c(c)->dtutil(); if (!dt_util.is_datatype(_t)) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } ptr_vector const & decls = *dt_util.get_datatype_constructors(_t); if (idx >= decls.size()) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } func_decl* decl = (decls)[idx]; decl = dt_util.get_constructor_is(decl); mk_c(c)->save_ast_trail(decl); RETURN_Z3(of_func_decl(decl)); Z3_CATCH_RETURN(nullptr); } Z3_func_decl Z3_API Z3_get_datatype_sort_constructor_accessor(Z3_context c, Z3_sort t, unsigned idx_c, unsigned idx_a) { Z3_TRY; LOG_Z3_get_datatype_sort_constructor_accessor(c, t, idx_c, idx_a); RESET_ERROR_CODE(); sort * _t = to_sort(t); datatype_util& dt_util = mk_c(c)->dtutil(); if (!dt_util.is_datatype(_t)) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } ptr_vector const & decls = *dt_util.get_datatype_constructors(_t); if (idx_c >= decls.size()) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return nullptr; } func_decl* decl = (decls)[idx_c]; if (decl->get_arity() <= idx_a) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } ptr_vector const & accs = *dt_util.get_constructor_accessors(decl); SASSERT(accs.size() == decl->get_arity()); if (accs.size() <= idx_a) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } decl = (accs)[idx_a]; mk_c(c)->save_ast_trail(decl); RETURN_Z3(of_func_decl(decl)); Z3_CATCH_RETURN(nullptr); } Z3_func_decl Z3_API Z3_get_tuple_sort_mk_decl(Z3_context c, Z3_sort t) { Z3_TRY; LOG_Z3_get_tuple_sort_mk_decl(c, t); RESET_ERROR_CODE(); sort * tuple = to_sort(t); datatype_util& dt_util = mk_c(c)->dtutil(); if (!dt_util.is_datatype(tuple) || dt_util.is_recursive(tuple) || dt_util.get_datatype_num_constructors(tuple) != 1) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } Z3_func_decl r = get_datatype_sort_constructor_core(c, t, 0); RETURN_Z3(r); Z3_CATCH_RETURN(nullptr); } unsigned Z3_API Z3_get_tuple_sort_num_fields(Z3_context c, Z3_sort t) { Z3_TRY; LOG_Z3_get_tuple_sort_num_fields(c, t); RESET_ERROR_CODE(); sort * tuple = to_sort(t); datatype_util& dt_util = mk_c(c)->dtutil(); if (!dt_util.is_datatype(tuple) || dt_util.is_recursive(tuple) || dt_util.get_datatype_num_constructors(tuple) != 1) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return 0; } ptr_vector const & decls = *dt_util.get_datatype_constructors(tuple); if (decls.size() != 1) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return 0; } ptr_vector const & accs = *dt_util.get_constructor_accessors(decls[0]); return accs.size(); Z3_CATCH_RETURN(0); } Z3_func_decl Z3_API Z3_get_tuple_sort_field_decl(Z3_context c, Z3_sort t, unsigned i) { Z3_TRY; LOG_Z3_get_tuple_sort_field_decl(c, t, i); RESET_ERROR_CODE(); sort * tuple = to_sort(t); datatype_util& dt_util = mk_c(c)->dtutil(); if (!dt_util.is_datatype(tuple) || dt_util.is_recursive(tuple) || dt_util.get_datatype_num_constructors(tuple) != 1) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } ptr_vector const & decls = *dt_util.get_datatype_constructors(tuple); if (decls.size() != 1) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } ptr_vector const & accs = *dt_util.get_constructor_accessors((decls)[0]); if (accs.size() <= i) { SET_ERROR_CODE(Z3_IOB, nullptr); RETURN_Z3(nullptr); } func_decl* acc = (accs)[i]; mk_c(c)->save_ast_trail(acc); RETURN_Z3(of_func_decl(acc)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_datatype_update_field( Z3_context c, Z3_func_decl f, Z3_ast t, Z3_ast v) { Z3_TRY; LOG_Z3_datatype_update_field(c, f, t, v); RESET_ERROR_CODE(); ast_manager & m = mk_c(c)->m(); func_decl* _f = to_func_decl(f); expr* _t = to_expr(t); expr* _v = to_expr(v); expr* args[2] = { _t, _v }; sort* domain[2] = { m.get_sort(_t), m.get_sort(_v) }; parameter param(_f); func_decl * d = m.mk_func_decl(mk_c(c)->get_dt_fid(), OP_DT_UPDATE_FIELD, 1, ¶m, 2, domain); app* r = m.mk_app(d, 2, args); mk_c(c)->save_ast_trail(r); check_sorts(c, r); RETURN_Z3(of_ast(r)); Z3_CATCH_RETURN(nullptr); } }; z3-z3-4.8.7/src/api/api_fpa.cpp000066400000000000000000001340441356505360400161000ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: api_fpa.cpp Abstract: Additional APIs for floating-point arithmetic (FP). Author: Christoph M. Wintersteiger (cwinter) 2013-06-05 Notes: --*/ #include #include "api/z3.h" #include "api/api_log_macros.h" #include "api/api_context.h" #include "ast/fpa_decl_plugin.h" static bool is_fp_sort(Z3_context c, Z3_sort s) { return mk_c(c)->fpautil().is_float(to_sort(s)); } static bool is_fp(Z3_context c, Z3_ast a) { return mk_c(c)->fpautil().is_float(to_expr(a)); } static bool is_rm(Z3_context c, Z3_ast a) { return mk_c(c)->fpautil().is_rm(to_expr(a)); } static bool is_bv(Z3_context c, Z3_ast a) { return mk_c(c)->bvutil().is_bv(to_expr(a)); } extern "C" { Z3_sort Z3_API Z3_mk_fpa_rounding_mode_sort(Z3_context c) { Z3_TRY; LOG_Z3_mk_fpa_rounding_mode_sort(c); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); sort * s = ctx->fpautil().mk_rm_sort(); mk_c(c)->save_ast_trail(s); RETURN_Z3(of_sort(s)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_round_nearest_ties_to_even(Z3_context c) { Z3_TRY; LOG_Z3_mk_fpa_round_nearest_ties_to_even(c); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_round_nearest_ties_to_even(); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_rne(Z3_context c) { Z3_TRY; LOG_Z3_mk_fpa_rne(c); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_round_nearest_ties_to_even(); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_round_nearest_ties_to_away(Z3_context c) { Z3_TRY; LOG_Z3_mk_fpa_round_nearest_ties_to_away(c); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_round_nearest_ties_to_away(); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_rna(Z3_context c) { Z3_TRY; LOG_Z3_mk_fpa_rna(c); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_round_nearest_ties_to_away(); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_round_toward_positive(Z3_context c) { Z3_TRY; LOG_Z3_mk_fpa_round_toward_positive(c); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_round_toward_positive(); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_rtp(Z3_context c) { Z3_TRY; LOG_Z3_mk_fpa_rtp(c); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_round_toward_positive(); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_round_toward_negative(Z3_context c) { Z3_TRY; LOG_Z3_mk_fpa_round_toward_negative(c); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_round_toward_negative(); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_rtn(Z3_context c) { Z3_TRY; LOG_Z3_mk_fpa_rtn(c); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_round_toward_negative(); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_round_toward_zero(Z3_context c) { Z3_TRY; LOG_Z3_mk_fpa_round_toward_zero(c); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_round_toward_zero(); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_rtz(Z3_context c) { Z3_TRY; LOG_Z3_mk_fpa_rtz(c); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_round_toward_zero(); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(nullptr); } Z3_sort Z3_API Z3_mk_fpa_sort(Z3_context c, unsigned ebits, unsigned sbits) { Z3_TRY; LOG_Z3_mk_fpa_sort(c, ebits, sbits); RESET_ERROR_CODE(); if (ebits < 2 || sbits < 3) { SET_ERROR_CODE(Z3_INVALID_ARG, "ebits should be at least 2, sbits at least 3"); } api::context * ctx = mk_c(c); sort * s = ctx->fpautil().mk_float_sort(ebits, sbits); ctx->save_ast_trail(s); RETURN_Z3(of_sort(s)); Z3_CATCH_RETURN(nullptr); } Z3_sort Z3_API Z3_mk_fpa_sort_half(Z3_context c) { return Z3_mk_fpa_sort(c, 5, 11); } Z3_sort Z3_API Z3_mk_fpa_sort_16(Z3_context c) { return Z3_mk_fpa_sort(c, 5, 11); } Z3_sort Z3_API Z3_mk_fpa_sort_single(Z3_context c) { return Z3_mk_fpa_sort(c, 8, 24); } Z3_sort Z3_API Z3_mk_fpa_sort_32(Z3_context c) { return Z3_mk_fpa_sort(c, 8, 24); } Z3_sort Z3_API Z3_mk_fpa_sort_double(Z3_context c) { return Z3_mk_fpa_sort(c, 11, 53); } Z3_sort Z3_API Z3_mk_fpa_sort_64(Z3_context c) { return Z3_mk_fpa_sort(c, 11, 53); } Z3_sort Z3_API Z3_mk_fpa_sort_quadruple(Z3_context c) { return Z3_mk_fpa_sort(c, 15, 113); } Z3_sort Z3_API Z3_mk_fpa_sort_128(Z3_context c) { return Z3_mk_fpa_sort(c, 15, 113); } Z3_ast Z3_API Z3_mk_fpa_nan(Z3_context c, Z3_sort s) { Z3_TRY; LOG_Z3_mk_fpa_nan(c, s); RESET_ERROR_CODE(); CHECK_VALID_AST(s, nullptr); if (!is_fp_sort(c, s)) { SET_ERROR_CODE(Z3_INVALID_ARG, "fp sort expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_nan(to_sort(s)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_inf(Z3_context c, Z3_sort s, bool negative) { Z3_TRY; LOG_Z3_mk_fpa_inf(c, s, negative); RESET_ERROR_CODE(); CHECK_VALID_AST(s, nullptr); if (!is_fp_sort(c, s)) { SET_ERROR_CODE(Z3_INVALID_ARG, "fp sort expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); 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); } Z3_ast Z3_API Z3_mk_fpa_zero(Z3_context c, Z3_sort s, bool negative) { Z3_TRY; LOG_Z3_mk_fpa_inf(c, s, negative); RESET_ERROR_CODE(); CHECK_VALID_AST(s, nullptr); if (!is_fp_sort(c, s)) { SET_ERROR_CODE(Z3_INVALID_ARG, "fp sort expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); 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); } Z3_ast Z3_API Z3_mk_fpa_fp(Z3_context c, Z3_ast sgn, Z3_ast exp, Z3_ast sig) { Z3_TRY; LOG_Z3_mk_fpa_fp(c, sgn, exp, sig); RESET_ERROR_CODE(); if (!is_bv(c, sgn) || !is_bv(c, exp) || !is_bv(c, sig)) { SET_ERROR_CODE(Z3_INVALID_ARG, "bv sorts expected for arguments"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_fp(to_expr(sgn), to_expr(exp), to_expr(sig)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_numeral_float(Z3_context c, float v, Z3_sort ty) { Z3_TRY; LOG_Z3_mk_fpa_numeral_float(c, v, ty); RESET_ERROR_CODE(); if (!is_fp_sort(c, ty)) { SET_ERROR_CODE(Z3_INVALID_ARG,"fp sort expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); scoped_mpf tmp(ctx->fpautil().fm()); ctx->fpautil().fm().set(tmp, ctx->fpautil().get_ebits(to_sort(ty)), ctx->fpautil().get_sbits(to_sort(ty)), v); expr * a = ctx->fpautil().mk_value(tmp); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_numeral_double(Z3_context c, double v, Z3_sort ty) { Z3_TRY; LOG_Z3_mk_fpa_numeral_double(c, v, ty); RESET_ERROR_CODE(); if (!is_fp_sort(c, ty)) { SET_ERROR_CODE(Z3_INVALID_ARG, "fp sort expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); scoped_mpf tmp(ctx->fpautil().fm()); ctx->fpautil().fm().set(tmp, ctx->fpautil().get_ebits(to_sort(ty)), ctx->fpautil().get_sbits(to_sort(ty)), v); expr * a = ctx->fpautil().mk_value(tmp); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_numeral_int(Z3_context c, signed v, Z3_sort ty) { Z3_TRY; LOG_Z3_mk_fpa_numeral_int(c, v, ty); RESET_ERROR_CODE(); if (!is_fp_sort(c, ty)) { SET_ERROR_CODE(Z3_INVALID_ARG, "fp sort expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); scoped_mpf tmp(ctx->fpautil().fm()); ctx->fpautil().fm().set(tmp, ctx->fpautil().get_ebits(to_sort(ty)), ctx->fpautil().get_sbits(to_sort(ty)), v); expr * a = ctx->fpautil().mk_value(tmp); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_numeral_int_uint(Z3_context c, bool sgn, signed exp, unsigned sig, Z3_sort ty) { Z3_TRY; LOG_Z3_mk_fpa_numeral_int64_uint64(c, sgn, exp, sig, ty); RESET_ERROR_CODE(); if (!is_fp_sort(c, ty)) { SET_ERROR_CODE(Z3_INVALID_ARG, "fp sort expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); scoped_mpf tmp(ctx->fpautil().fm()); ctx->fpautil().fm().set(tmp, ctx->fpautil().get_ebits(to_sort(ty)), ctx->fpautil().get_sbits(to_sort(ty)), sgn, exp, sig); expr * a = ctx->fpautil().mk_value(tmp); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_numeral_int64_uint64(Z3_context c, bool sgn, int64_t exp, uint64_t sig, Z3_sort ty) { Z3_TRY; LOG_Z3_mk_fpa_numeral_int64_uint64(c, sgn, exp, sig, ty); RESET_ERROR_CODE(); if (!is_fp_sort(c, ty)) { SET_ERROR_CODE(Z3_INVALID_ARG, "fp sort expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); scoped_mpf tmp(ctx->fpautil().fm()); ctx->fpautil().fm().set(tmp, ctx->fpautil().get_ebits(to_sort(ty)), ctx->fpautil().get_sbits(to_sort(ty)), sgn, exp, sig); expr * a = ctx->fpautil().mk_value(tmp); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_abs(Z3_context c, Z3_ast t) { Z3_TRY; LOG_Z3_mk_fpa_abs(c, t); RESET_ERROR_CODE(); if (!is_fp(c, t)) { SET_ERROR_CODE(Z3_INVALID_ARG, "fp sort expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_abs(to_expr(t)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_neg(Z3_context c, Z3_ast t) { Z3_TRY; LOG_Z3_mk_fpa_neg(c, t); RESET_ERROR_CODE(); if (!is_fp(c, t)) { SET_ERROR_CODE(Z3_INVALID_ARG, "fp sort expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_neg(to_expr(t)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_add(Z3_context c, Z3_ast rm, Z3_ast t1, Z3_ast t2) { Z3_TRY; LOG_Z3_mk_fpa_add(c, rm, t1, t2); RESET_ERROR_CODE(); if (!is_rm(c, rm) || !is_fp(c, t1) || !is_fp(c, t2)) { SET_ERROR_CODE(Z3_INVALID_ARG, "rm and fp sorts expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_add(to_expr(rm), to_expr(t1), to_expr(t2)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_sub(Z3_context c, Z3_ast rm, Z3_ast t1, Z3_ast t2) { Z3_TRY; LOG_Z3_mk_fpa_add(c, rm, t1, t2); RESET_ERROR_CODE(); if (!is_rm(c, rm) || !is_fp(c, t1) || !is_fp(c, t2)) { SET_ERROR_CODE(Z3_INVALID_ARG, "rm and fp sorts expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_sub(to_expr(rm), to_expr(t1), to_expr(t2)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_mul(Z3_context c, Z3_ast rm, Z3_ast t1, Z3_ast t2) { Z3_TRY; LOG_Z3_mk_fpa_add(c, rm, t1, t2); RESET_ERROR_CODE(); if (!is_rm(c, rm) || !is_fp(c, t1) || !is_fp(c, t2)) { SET_ERROR_CODE(Z3_INVALID_ARG, "rm and fp sorts expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_mul(to_expr(rm), to_expr(t1), to_expr(t2)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_div(Z3_context c, Z3_ast rm, Z3_ast t1, Z3_ast t2) { Z3_TRY; LOG_Z3_mk_fpa_add(c, rm, t1, t2); RESET_ERROR_CODE(); if (!is_rm(c, rm) || !is_fp(c, t1) || !is_fp(c, t2)) { SET_ERROR_CODE(Z3_INVALID_ARG, "rm and fp sorts expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_div(to_expr(rm), to_expr(t1), to_expr(t2)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_fma(Z3_context c, Z3_ast rm, Z3_ast t1, Z3_ast t2, Z3_ast t3) { Z3_TRY; LOG_Z3_mk_fpa_fma(c, rm, t1, t2, t3); RESET_ERROR_CODE(); if (!is_rm(c, rm) || !is_fp(c, t1) || !is_fp(c, t2) || !is_fp(c, t3)) { SET_ERROR_CODE(Z3_INVALID_ARG, "rm and fp sorts expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_fma(to_expr(rm), to_expr(t1), to_expr(t2), to_expr(t3)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_sqrt(Z3_context c, Z3_ast rm, Z3_ast t) { Z3_TRY; LOG_Z3_mk_fpa_sqrt(c, rm, t); RESET_ERROR_CODE(); if (!is_rm(c, rm) || !is_fp(c, t)) { SET_ERROR_CODE(Z3_INVALID_ARG, "rm and fp sorts expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_sqrt(to_expr(rm), to_expr(t)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_rem(Z3_context c, Z3_ast t1, Z3_ast t2) { Z3_TRY; LOG_Z3_mk_fpa_rem(c, t1, t2); RESET_ERROR_CODE(); if (!is_fp(c, t1) || !is_fp(c, t2)) { SET_ERROR_CODE(Z3_INVALID_ARG, "fp sorts expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_rem(to_expr(t1), to_expr(t2)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_round_to_integral(Z3_context c, Z3_ast rm, Z3_ast t) { Z3_TRY; LOG_Z3_mk_fpa_round_to_integral(c, rm, t); RESET_ERROR_CODE(); if (!is_rm(c, rm) || !is_fp(c, t)) { SET_ERROR_CODE(Z3_INVALID_ARG, "rm and fp sorts expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_round_to_integral(to_expr(rm), to_expr(t)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_min(Z3_context c, Z3_ast t1, Z3_ast t2) { Z3_TRY; LOG_Z3_mk_fpa_min(c, t1, t2); RESET_ERROR_CODE(); if (!is_fp(c, t1) || !is_fp(c, t2)) { SET_ERROR_CODE(Z3_INVALID_ARG, "fp sorts expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_min(to_expr(t1), to_expr(t2)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_max(Z3_context c, Z3_ast t1, Z3_ast t2) { Z3_TRY; LOG_Z3_mk_fpa_max(c, t1, t2); RESET_ERROR_CODE(); if (!is_fp(c, t1) || !is_fp(c, t2)) { SET_ERROR_CODE(Z3_INVALID_ARG, "fp sorts expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_max(to_expr(t1), to_expr(t2)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_leq(Z3_context c, Z3_ast t1, Z3_ast t2) { Z3_TRY; LOG_Z3_mk_fpa_leq(c, t1, t2); RESET_ERROR_CODE(); if (!is_fp(c, t1) || !is_fp(c, t2)) { SET_ERROR_CODE(Z3_INVALID_ARG, "fp sorts expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_le(to_expr(t1), to_expr(t2)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_lt(Z3_context c, Z3_ast t1, Z3_ast t2) { Z3_TRY; LOG_Z3_mk_fpa_lt(c, t1, t2); RESET_ERROR_CODE(); if (!is_fp(c, t1) || !is_fp(c, t2)) { SET_ERROR_CODE(Z3_INVALID_ARG, "fp sorts expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_lt(to_expr(t1), to_expr(t2)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_geq(Z3_context c, Z3_ast t1, Z3_ast t2) { Z3_TRY; LOG_Z3_mk_fpa_geq(c, t1, t2); RESET_ERROR_CODE(); if (!is_fp(c, t1) || !is_fp(c, t2)) { SET_ERROR_CODE(Z3_INVALID_ARG, "fp sorts expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_ge(to_expr(t1), to_expr(t2)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_gt(Z3_context c, Z3_ast t1, Z3_ast t2) { Z3_TRY; LOG_Z3_mk_fpa_gt(c, t1, t2); RESET_ERROR_CODE(); if (!is_fp(c, t1) || !is_fp(c, t2)) { SET_ERROR_CODE(Z3_INVALID_ARG, "fp sorts expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_gt(to_expr(t1), to_expr(t2)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_eq(Z3_context c, Z3_ast t1, Z3_ast t2) { Z3_TRY; LOG_Z3_mk_fpa_eq(c, t1, t2); RESET_ERROR_CODE(); if (!is_fp(c, t1) || !is_fp(c, t2)) { SET_ERROR_CODE(Z3_INVALID_ARG, "fp sorts expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_float_eq(to_expr(t1), to_expr(t2)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_is_normal(Z3_context c, Z3_ast t) { Z3_TRY; LOG_Z3_mk_fpa_is_normal(c, t); RESET_ERROR_CODE(); if (!is_fp(c, t)) { SET_ERROR_CODE(Z3_INVALID_ARG, "fp sort expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_is_normal(to_expr(t)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_is_subnormal(Z3_context c, Z3_ast t) { Z3_TRY; LOG_Z3_mk_fpa_is_subnormal(c, t); RESET_ERROR_CODE(); if (!is_fp(c, t)) { SET_ERROR_CODE(Z3_INVALID_ARG, "fp sort expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_is_subnormal(to_expr(t)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_is_zero(Z3_context c, Z3_ast t) { Z3_TRY; LOG_Z3_mk_fpa_is_zero(c, t); RESET_ERROR_CODE(); if (!is_fp(c, t)) { SET_ERROR_CODE(Z3_INVALID_ARG, "fp sort expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_is_zero(to_expr(t)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_is_infinite(Z3_context c, Z3_ast t) { Z3_TRY; LOG_Z3_mk_fpa_is_infinite(c, t); RESET_ERROR_CODE(); if (!is_fp(c, t)) { SET_ERROR_CODE(Z3_INVALID_ARG, "fp sort expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_is_inf(to_expr(t)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_is_nan(Z3_context c, Z3_ast t) { Z3_TRY; LOG_Z3_mk_fpa_is_nan(c, t); RESET_ERROR_CODE(); if (!is_fp(c, t)) { SET_ERROR_CODE(Z3_INVALID_ARG, "fp sort expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_is_nan(to_expr(t)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_is_negative(Z3_context c, Z3_ast t) { Z3_TRY; LOG_Z3_mk_fpa_is_negative(c, t); RESET_ERROR_CODE(); if (!is_fp(c, t)) { SET_ERROR_CODE(Z3_INVALID_ARG, "fp sort expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_is_negative(to_expr(t)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_is_positive(Z3_context c, Z3_ast t) { Z3_TRY; LOG_Z3_mk_fpa_is_positive(c, t); RESET_ERROR_CODE(); if (!is_fp(c, t)) { SET_ERROR_CODE(Z3_INVALID_ARG, "fp sort expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_is_positive(to_expr(t)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_to_fp_bv(Z3_context c, Z3_ast bv, Z3_sort s) { Z3_TRY; LOG_Z3_mk_fpa_to_fp_bv(c, bv, s); RESET_ERROR_CODE(); if (!is_bv(c, bv) || !is_fp_sort(c, s)) { SET_ERROR_CODE(Z3_INVALID_ARG, "bv then fp sort expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); fpa_util & fu = ctx->fpautil(); if (!ctx->bvutil().is_bv(to_expr(bv)) || !fu.is_float(to_sort(s))) { SET_ERROR_CODE(Z3_INVALID_ARG, "bv sort the flaot sort expected"); return nullptr; } expr * a = fu.mk_to_fp(to_sort(s), to_expr(bv)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_to_fp_float(Z3_context c, Z3_ast rm, Z3_ast t, Z3_sort s) { Z3_TRY; LOG_Z3_mk_fpa_to_fp_float(c, rm, t, s); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); fpa_util & fu = ctx->fpautil(); if (!fu.is_rm(to_expr(rm)) || !fu.is_float(to_expr(t)) || !fu.is_float(to_sort(s))) { SET_ERROR_CODE(Z3_INVALID_ARG, "rm and float sorts expected"); return nullptr; } expr * a = fu.mk_to_fp(to_sort(s), to_expr(rm), to_expr(t)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_to_fp_real(Z3_context c, Z3_ast rm, Z3_ast t, Z3_sort s) { Z3_TRY; LOG_Z3_mk_fpa_to_fp_real(c, rm, t, s); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); fpa_util & fu = ctx->fpautil(); if (!fu.is_rm(to_expr(rm)) || !ctx->autil().is_real(to_expr(t)) || !fu.is_float(to_sort(s))) { SET_ERROR_CODE(Z3_INVALID_ARG, "rm and float sorts expected"); return nullptr; } expr * a = fu.mk_to_fp(to_sort(s), to_expr(rm), to_expr(t)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_to_fp_signed(Z3_context c, Z3_ast rm, Z3_ast t, Z3_sort s) { Z3_TRY; LOG_Z3_mk_fpa_to_fp_signed(c, rm, t, s); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); fpa_util & fu = ctx->fpautil(); if (!fu.is_rm(to_expr(rm)) || !ctx->bvutil().is_bv(to_expr(t)) || !fu.is_float(to_sort(s))) { SET_ERROR_CODE(Z3_INVALID_ARG, "rm and float sorts expected"); return nullptr; } expr * a = fu.mk_to_fp(to_sort(s), to_expr(rm), to_expr(t)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_to_fp_unsigned(Z3_context c, Z3_ast rm, Z3_ast t, Z3_sort s) { Z3_TRY; LOG_Z3_mk_fpa_to_fp_unsigned(c, rm, t, s); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); fpa_util & fu = ctx->fpautil(); if (!fu.is_rm(to_expr(rm)) || !ctx->bvutil().is_bv(to_expr(t)) || !fu.is_float(to_sort(s))) { SET_ERROR_CODE(Z3_INVALID_ARG, "rm and float sorts expected"); return nullptr; } expr * a = fu.mk_to_fp_unsigned(to_sort(s), to_expr(rm), to_expr(t)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_to_ubv(Z3_context c, Z3_ast rm, Z3_ast t, unsigned sz) { Z3_TRY; LOG_Z3_mk_fpa_to_ubv(c, rm, t, sz); RESET_ERROR_CODE(); if (!is_rm(c, rm) || !is_fp(c, t)) { SET_ERROR_CODE(Z3_INVALID_ARG, "rm and float sorts expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_to_ubv(to_expr(rm), to_expr(t), sz); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_to_sbv(Z3_context c, Z3_ast rm, Z3_ast t, unsigned sz) { Z3_TRY; LOG_Z3_mk_fpa_to_sbv(c, rm, t, sz); RESET_ERROR_CODE(); if (!is_rm(c, rm) || !is_fp(c, t)) { SET_ERROR_CODE(Z3_INVALID_ARG, "rm and float sorts expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_to_sbv(to_expr(rm), to_expr(t), sz); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_to_real(Z3_context c, Z3_ast t) { Z3_TRY; LOG_Z3_mk_fpa_to_real(c, t); RESET_ERROR_CODE(); if (!is_fp(c, t)) { SET_ERROR_CODE(Z3_INVALID_ARG, "fp sort expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_to_real(to_expr(t)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(nullptr); } unsigned Z3_API Z3_fpa_get_ebits(Z3_context c, Z3_sort s) { Z3_TRY; LOG_Z3_fpa_get_ebits(c, s); RESET_ERROR_CODE(); CHECK_NON_NULL(s, 0); CHECK_VALID_AST(s, 0); if (!is_fp_sort(c, s)) { SET_ERROR_CODE(Z3_INVALID_ARG, "fp sort expected"); RETURN_Z3(0); } return mk_c(c)->fpautil().get_ebits(to_sort(s)); Z3_CATCH_RETURN(0); } unsigned Z3_API Z3_fpa_get_sbits(Z3_context c, Z3_sort s) { Z3_TRY; LOG_Z3_fpa_get_sbits(c, s); RESET_ERROR_CODE(); CHECK_NON_NULL(s, 0); CHECK_VALID_AST(s, 0); if (!is_fp_sort(c, s)) { SET_ERROR_CODE(Z3_INVALID_ARG, "fp sort expected"); RETURN_Z3(0); } return mk_c(c)->fpautil().get_sbits(to_sort(s)); Z3_CATCH_RETURN(0); } bool Z3_API Z3_fpa_get_numeral_sign(Z3_context c, Z3_ast t, int * sgn) { Z3_TRY; LOG_Z3_fpa_get_numeral_sign(c, t, sgn); RESET_ERROR_CODE(); CHECK_NON_NULL(t, 0); CHECK_VALID_AST(t, 0); if (sgn == nullptr) { SET_ERROR_CODE(Z3_INVALID_ARG, "sign cannot be a nullpointer"); return false; } ast_manager & m = mk_c(c)->m(); mpf_manager & mpfm = mk_c(c)->fpautil().fm(); family_id fid = mk_c(c)->get_fpa_fid(); fpa_decl_plugin * plugin = (fpa_decl_plugin*)m.get_plugin(fid); expr * e = to_expr(t); if (!is_app(e) || is_app_of(e, fid, OP_FPA_NAN) || !is_fp(c, t)) { SET_ERROR_CODE(Z3_INVALID_ARG, "invalid expression argument, expecting a valid fp, not a NaN"); return false; } scoped_mpf val(mpfm); bool r = plugin->is_numeral(to_expr(t), val); if (!r || mpfm.is_nan(val)) { SET_ERROR_CODE(Z3_INVALID_ARG, "invalid expression argument, expecting a valid fp, not a NaN"); return false; } *sgn = mpfm.sgn(val); return r; Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_fpa_get_numeral_sign_bv(Z3_context c, Z3_ast t) { Z3_TRY; LOG_Z3_fpa_get_numeral_sign_bv(c, t); RESET_ERROR_CODE(); CHECK_NON_NULL(t, nullptr); CHECK_VALID_AST(t, nullptr); ast_manager & m = mk_c(c)->m(); mpf_manager & mpfm = mk_c(c)->fpautil().fm(); family_id fid = mk_c(c)->get_fpa_fid(); fpa_decl_plugin * plugin = (fpa_decl_plugin*)m.get_plugin(fid); api::context * ctx = mk_c(c); expr * e = to_expr(t); if (!is_app(e) || is_app_of(e, fid, OP_FPA_NAN) || !is_fp(c, t)) { SET_ERROR_CODE(Z3_INVALID_ARG, "invalid expression argument, expecting a valid fp, not a NaN"); RETURN_Z3(nullptr); } scoped_mpf val(mpfm); bool r = plugin->is_numeral(to_expr(t), val); if (!r || mpfm.is_nan(val)) { SET_ERROR_CODE(Z3_INVALID_ARG, "invalid expression argument, expecting a valid fp, not a NaN"); return nullptr; } app * a; if (mpfm.is_pos(val)) a = ctx->bvutil().mk_numeral(0, 1); else a = ctx->bvutil().mk_numeral(1, 1); mk_c(c)->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_fpa_get_numeral_significand_bv(Z3_context c, Z3_ast t) { Z3_TRY; LOG_Z3_fpa_get_numeral_significand_bv(c, t); RESET_ERROR_CODE(); CHECK_NON_NULL(t, nullptr); CHECK_VALID_AST(t, nullptr); ast_manager & m = mk_c(c)->m(); mpf_manager & mpfm = mk_c(c)->fpautil().fm(); unsynch_mpq_manager & mpqm = mpfm.mpq_manager(); family_id fid = mk_c(c)->get_fpa_fid(); fpa_decl_plugin * plugin = (fpa_decl_plugin*)m.get_plugin(fid); SASSERT(plugin != 0); expr * e = to_expr(t); if (!is_app(e) || is_app_of(e, fid, OP_FPA_NAN) || !is_fp(c, t)) { SET_ERROR_CODE(Z3_INVALID_ARG, "invalid expression argument, expecting a valid fp, not a NaN"); RETURN_Z3(nullptr); } scoped_mpf val(mpfm); bool r = plugin->is_numeral(e, val); if (!r || !(mpfm.is_normal(val) || mpfm.is_denormal(val) || mpfm.is_zero(val) || mpfm.is_inf(val))) { SET_ERROR_CODE(Z3_INVALID_ARG, "invalid expression argument, expecting a valid fp, not a NaN"); RETURN_Z3(nullptr); } unsigned sbits = val.get().get_sbits(); scoped_mpq q(mpqm); mpqm.set(q, mpfm.sig(val)); if (mpfm.is_inf(val)) mpqm.set(q, 0); app * a = mk_c(c)->bvutil().mk_numeral(q.get(), sbits-1); mk_c(c)->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(nullptr); } Z3_string Z3_API Z3_fpa_get_numeral_significand_string(Z3_context c, Z3_ast t) { Z3_TRY; LOG_Z3_fpa_get_numeral_significand_string(c, t); RESET_ERROR_CODE(); CHECK_NON_NULL(t, nullptr); CHECK_VALID_AST(t, nullptr); ast_manager & m = mk_c(c)->m(); mpf_manager & mpfm = mk_c(c)->fpautil().fm(); unsynch_mpq_manager & mpqm = mpfm.mpq_manager(); family_id fid = mk_c(c)->get_fpa_fid(); fpa_decl_plugin * plugin = (fpa_decl_plugin*)m.get_plugin(fid); SASSERT(plugin != 0); expr * e = to_expr(t); if (!is_app(e) || is_app_of(e, fid, OP_FPA_NAN) || !is_fp(c, t)) { SET_ERROR_CODE(Z3_INVALID_ARG, "invalid expression argument, expecting a valid fp, not a NaN"); return ""; } scoped_mpf val(mpfm); bool r = plugin->is_numeral(e, val); if (!r || !(mpfm.is_normal(val) || mpfm.is_denormal(val) || mpfm.is_zero(val) || mpfm.is_inf(val))) { SET_ERROR_CODE(Z3_INVALID_ARG, "invalid expression argument, expecting a valid fp, not a NaN"); return ""; } unsigned sbits = val.get().get_sbits(); scoped_mpq q(mpqm); mpqm.set(q, mpfm.sig(val)); if (!mpfm.is_denormal(val)) mpqm.add(q, mpfm.m_powers2(sbits - 1), q); mpqm.div(q, mpfm.m_powers2(sbits - 1), q); if (mpfm.is_inf(val)) mpqm.set(q, 0); std::stringstream ss; mpqm.display_decimal(ss, q, sbits); return mk_c(c)->mk_external_string(ss.str()); Z3_CATCH_RETURN(""); } bool Z3_API Z3_fpa_get_numeral_significand_uint64(Z3_context c, Z3_ast t, uint64_t * n) { Z3_TRY; LOG_Z3_fpa_get_numeral_significand_uint64(c, t, n); RESET_ERROR_CODE(); CHECK_NON_NULL(t, 0); CHECK_VALID_AST(t, 0); if (n == nullptr) { SET_ERROR_CODE(Z3_INVALID_ARG, "invalid nullptr argument"); return false; } ast_manager & m = mk_c(c)->m(); mpf_manager & mpfm = mk_c(c)->fpautil().fm(); unsynch_mpz_manager & mpzm = mpfm.mpz_manager(); family_id fid = mk_c(c)->get_fpa_fid(); fpa_decl_plugin * plugin = (fpa_decl_plugin*)m.get_plugin(fid); SASSERT(plugin != 0); expr * e = to_expr(t); if (!is_app(e) || is_app_of(e, fid, OP_FPA_NAN) || !is_fp(c, t)) { SET_ERROR_CODE(Z3_INVALID_ARG, "invalid expression argument, expecting a valid fp, not a NaN"); *n = 0; return false; } scoped_mpf val(mpfm); bool r = plugin->is_numeral(e, val); const mpz & z = mpfm.sig(val); if (!r || !(mpfm.is_normal(val) || mpfm.is_denormal(val) || mpfm.is_zero(val) || mpfm.is_inf(val)) || !mpzm.is_uint64(z)) { SET_ERROR_CODE(Z3_INVALID_ARG, "invalid expression argument, expecting a valid fp, not a NaN"); *n = 0; return false; } *n = mpzm.get_uint64(z); return true; Z3_CATCH_RETURN(0); } Z3_string Z3_API Z3_fpa_get_numeral_exponent_string(Z3_context c, Z3_ast t, bool biased) { Z3_TRY; LOG_Z3_fpa_get_numeral_exponent_string(c, t, biased); RESET_ERROR_CODE(); CHECK_NON_NULL(t, nullptr); CHECK_VALID_AST(t, nullptr); ast_manager & m = mk_c(c)->m(); mpf_manager & mpfm = mk_c(c)->fpautil().fm(); family_id fid = mk_c(c)->get_fpa_fid(); fpa_decl_plugin * plugin = (fpa_decl_plugin*)m.get_plugin(mk_c(c)->get_fpa_fid()); SASSERT(plugin != 0); expr * e = to_expr(t); if (!is_app(e) || is_app_of(e, fid, OP_FPA_NAN) || !is_fp(c, t)) { SET_ERROR_CODE(Z3_INVALID_ARG, "invalid expression argument, expecting a valid fp, not a NaN"); return ""; } scoped_mpf val(mpfm); bool r = plugin->is_numeral(e, val); if (!r || !(mpfm.is_normal(val) || mpfm.is_denormal(val) || mpfm.is_zero(val) || mpfm.is_inf(val))) { SET_ERROR_CODE(Z3_INVALID_ARG, "invalid expression argument, expecting a valid fp, not a NaN"); return ""; } unsigned ebits = val.get().get_ebits(); mpf_exp_t exp; if (biased) { exp = mpfm.is_zero(val) ? 0 : mpfm.is_inf(val) ? mpfm.mk_top_exp(ebits) : mpfm.bias_exp(ebits, mpfm.exp(val)); } else { exp = mpfm.is_zero(val) ? 0 : mpfm.is_inf(val) ? mpfm.mk_top_exp(ebits) : mpfm.is_denormal(val) ? mpfm.mk_min_exp(ebits) : mpfm.exp(val); } std::stringstream ss; ss << exp; return mk_c(c)->mk_external_string(ss.str()); Z3_CATCH_RETURN(""); } bool Z3_API Z3_fpa_get_numeral_exponent_int64(Z3_context c, Z3_ast t, int64_t * n, bool biased) { Z3_TRY; LOG_Z3_fpa_get_numeral_exponent_int64(c, t, n, biased); RESET_ERROR_CODE(); CHECK_NON_NULL(t, 0); CHECK_VALID_AST(t, 0); if (n == nullptr) { SET_ERROR_CODE(Z3_INVALID_ARG, "invalid null argument"); return false; } ast_manager & m = mk_c(c)->m(); mpf_manager & mpfm = mk_c(c)->fpautil().fm(); family_id fid = mk_c(c)->get_fpa_fid(); fpa_decl_plugin * plugin = (fpa_decl_plugin*)m.get_plugin(mk_c(c)->get_fpa_fid()); SASSERT(plugin != 0); expr * e = to_expr(t); if (!is_app(e) || is_app_of(e, fid, OP_FPA_NAN) || !is_fp(c, t)) { SET_ERROR_CODE(Z3_INVALID_ARG, "invalid expression argument, expecting a valid fp, not a NaN"); *n = 0; return false; } scoped_mpf val(mpfm); bool r = plugin->is_numeral(e, val); if (!r || !(mpfm.is_normal(val) || mpfm.is_denormal(val) || mpfm.is_zero(val) || mpfm.is_inf(val))) { SET_ERROR_CODE(Z3_INVALID_ARG, "invalid expression argument, expecting a valid fp, not a NaN"); *n = 0; return false; } unsigned ebits = val.get().get_ebits(); if (biased) { *n = mpfm.is_zero(val) ? 0 : mpfm.is_inf(val) ? mpfm.mk_top_exp(ebits) : mpfm.bias_exp(ebits, mpfm.exp(val)); } else { *n = mpfm.is_zero(val) ? 0 : mpfm.is_inf(val) ? mpfm.mk_top_exp(ebits) : mpfm.is_denormal(val) ? mpfm.mk_min_exp(ebits) : mpfm.exp(val); } return true; Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_fpa_get_numeral_exponent_bv(Z3_context c, Z3_ast t, bool biased) { Z3_TRY; LOG_Z3_fpa_get_numeral_exponent_bv(c, t, biased); RESET_ERROR_CODE(); CHECK_NON_NULL(t, nullptr); CHECK_VALID_AST(t, nullptr); ast_manager & m = mk_c(c)->m(); mpf_manager & mpfm = mk_c(c)->fpautil().fm(); family_id fid = mk_c(c)->get_fpa_fid(); fpa_decl_plugin * plugin = (fpa_decl_plugin*)m.get_plugin(fid); expr * e = to_expr(t); if (!is_app(e) || is_app_of(e, fid, OP_FPA_NAN) || !is_fp(c, t)) { SET_ERROR_CODE(Z3_INVALID_ARG, "invalid expression argument, expecting a valid fp, not a NaN"); RETURN_Z3(nullptr); } scoped_mpf val(mpfm); bool r = plugin->is_numeral(e, val); if (!r || !(mpfm.is_normal(val) || mpfm.is_denormal(val) || mpfm.is_zero(val) || mpfm.is_inf(val))) { SET_ERROR_CODE(Z3_INVALID_ARG, "invalid expression argument, expecting a valid fp, not a NaN"); RETURN_Z3(nullptr); } unsigned ebits = val.get().get_ebits(); mpf_exp_t exp; if (biased) { exp = mpfm.is_zero(val) ? 0 : mpfm.is_inf(val) ? mpfm.mk_top_exp(ebits) : mpfm.bias_exp(ebits, mpfm.exp(val)); } else { exp = mpfm.is_zero(val) ? 0 : mpfm.is_inf(val) ? mpfm.mk_top_exp(ebits) : mpfm.is_denormal(val) ? mpfm.mk_min_exp(ebits) : mpfm.exp(val); } app * a = mk_c(c)->bvutil().mk_numeral(exp, ebits); mk_c(c)->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_to_ieee_bv(Z3_context c, Z3_ast t) { Z3_TRY; LOG_Z3_mk_fpa_to_ieee_bv(c, t); RESET_ERROR_CODE(); CHECK_NON_NULL(t, nullptr); CHECK_VALID_AST(t, nullptr); if (!is_fp(c, t)) { SET_ERROR_CODE(Z3_INVALID_ARG, "fp sort expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); Z3_ast r = of_ast(ctx->fpautil().mk_to_ieee_bv(to_expr(t))); RETURN_Z3(r); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_to_fp_int_real(Z3_context c, Z3_ast rm, Z3_ast exp, Z3_ast sig, Z3_sort s) { Z3_TRY; LOG_Z3_mk_fpa_to_fp_int_real(c, rm, exp, sig, s); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); fpa_util & fu = ctx->fpautil(); if (!fu.is_rm(to_expr(rm)) || !ctx->autil().is_int(to_expr(exp)) || !ctx->autil().is_real(to_expr(sig)) || !fu.is_float(to_sort(s))) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return nullptr; } expr * a = fu.mk_to_fp(to_sort(s), to_expr(rm), to_expr(exp), to_expr(sig)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); Z3_CATCH_RETURN(nullptr); } bool Z3_API Z3_fpa_is_numeral_nan(Z3_context c, Z3_ast t) { Z3_TRY; LOG_Z3_fpa_is_numeral_nan(c, t); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); fpa_util & fu = ctx->fpautil(); if (!is_expr(t) || !fu.is_numeral(to_expr(t))) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return false; } return fu.is_nan(to_expr(t)); Z3_CATCH_RETURN(false); } bool Z3_API Z3_fpa_is_numeral_inf(Z3_context c, Z3_ast t) { Z3_TRY; LOG_Z3_fpa_is_numeral_inf(c, t); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); fpa_util & fu = ctx->fpautil(); if (!is_expr(t) || !fu.is_numeral(to_expr(t))) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return false; } return fu.is_inf(to_expr(t)); Z3_CATCH_RETURN(false); } bool Z3_API Z3_fpa_is_numeral_zero(Z3_context c, Z3_ast t) { Z3_TRY; LOG_Z3_fpa_is_numeral_zero(c, t); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); fpa_util & fu = ctx->fpautil(); if (!is_expr(t) || !fu.is_numeral(to_expr(t))) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return false; } return fu.is_zero(to_expr(t)); Z3_CATCH_RETURN(false); } bool Z3_API Z3_fpa_is_numeral_normal(Z3_context c, Z3_ast t) { Z3_TRY; LOG_Z3_fpa_is_numeral_normal(c, t); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); fpa_util & fu = ctx->fpautil(); if (!is_expr(t) || !fu.is_numeral(to_expr(t))) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return false; } return fu.is_normal(to_expr(t)); Z3_CATCH_RETURN(false); } bool Z3_API Z3_fpa_is_numeral_subnormal(Z3_context c, Z3_ast t) { Z3_TRY; LOG_Z3_fpa_is_numeral_subnormal(c, t); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); fpa_util & fu = ctx->fpautil(); if (!is_expr(t) || !fu.is_numeral(to_expr(t))) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return false; } return fu.is_subnormal(to_expr(t)); Z3_CATCH_RETURN(false); } bool Z3_API Z3_fpa_is_numeral_positive(Z3_context c, Z3_ast t) { Z3_TRY; LOG_Z3_fpa_is_numeral_positive(c, t); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); fpa_util & fu = ctx->fpautil(); if (!is_expr(t) || !fu.is_numeral(to_expr(t))) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return false; } return fu.is_positive(to_expr(t)); Z3_CATCH_RETURN(false); } bool Z3_API Z3_fpa_is_numeral_negative(Z3_context c, Z3_ast t) { Z3_TRY; LOG_Z3_fpa_is_numeral_negative(c, t); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); fpa_util & fu = ctx->fpautil(); if (!is_expr(t) || !fu.is_numeral(to_expr(t))) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return false; } return fu.is_negative(to_expr(t)); Z3_CATCH_RETURN(false); } }; z3-z3-4.8.7/src/api/api_goal.cpp000066400000000000000000000146521356505360400162560ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_goal.cpp Abstract: API for creating goals Author: Leonardo de Moura (leonardo) 2012-03-06. Revision History: --*/ #include #include "api/z3.h" #include "api/api_log_macros.h" #include "api/api_context.h" #include "api/api_goal.h" #include "ast/ast_translation.h" #include "api/api_model.h" extern "C" { Z3_goal Z3_API Z3_mk_goal(Z3_context c, bool models, bool unsat_cores, bool proofs) { Z3_TRY; LOG_Z3_mk_goal(c, models, unsat_cores, proofs); RESET_ERROR_CODE(); if (proofs != 0 && !mk_c(c)->m().proofs_enabled()) { SET_ERROR_CODE(Z3_INVALID_ARG, "proofs are required, but proofs are not enabled on the context"); RETURN_Z3(nullptr); } Z3_goal_ref * g = alloc(Z3_goal_ref, *mk_c(c)); g->m_goal = alloc(goal, mk_c(c)->m(), proofs != 0, models != 0, unsat_cores != 0); mk_c(c)->save_object(g); Z3_goal r = of_goal(g); RETURN_Z3(r); Z3_CATCH_RETURN(nullptr); } void Z3_API Z3_goal_inc_ref(Z3_context c, Z3_goal g) { Z3_TRY; LOG_Z3_goal_inc_ref(c, g); RESET_ERROR_CODE(); to_goal(g)->inc_ref(); Z3_CATCH; } void Z3_API Z3_goal_dec_ref(Z3_context c, Z3_goal g) { Z3_TRY; LOG_Z3_goal_dec_ref(c, g); RESET_ERROR_CODE(); to_goal(g)->dec_ref(); Z3_CATCH; } Z3_goal_prec Z3_API Z3_goal_precision(Z3_context c, Z3_goal g) { Z3_TRY; LOG_Z3_goal_precision(c, g); RESET_ERROR_CODE(); switch (to_goal_ref(g)->prec()) { case goal::PRECISE: return Z3_GOAL_PRECISE; case goal::UNDER: return Z3_GOAL_UNDER; case goal::OVER: return Z3_GOAL_OVER; case goal::UNDER_OVER: return Z3_GOAL_UNDER_OVER; default: UNREACHABLE(); return Z3_GOAL_UNDER_OVER; } Z3_CATCH_RETURN(Z3_GOAL_UNDER_OVER); } void Z3_API Z3_goal_assert(Z3_context c, Z3_goal g, Z3_ast a) { Z3_TRY; LOG_Z3_goal_assert(c, g, a); RESET_ERROR_CODE(); CHECK_FORMULA(a,); to_goal_ref(g)->assert_expr(to_expr(a)); Z3_CATCH; } bool Z3_API Z3_goal_inconsistent(Z3_context c, Z3_goal g) { Z3_TRY; LOG_Z3_goal_inconsistent(c, g); RESET_ERROR_CODE(); return to_goal_ref(g)->inconsistent(); Z3_CATCH_RETURN(false); } unsigned Z3_API Z3_goal_depth(Z3_context c, Z3_goal g) { Z3_TRY; LOG_Z3_goal_depth(c, g); RESET_ERROR_CODE(); return to_goal_ref(g)->depth(); Z3_CATCH_RETURN(0); } void Z3_API Z3_goal_reset(Z3_context c, Z3_goal g) { Z3_TRY; LOG_Z3_goal_reset(c, g); RESET_ERROR_CODE(); to_goal_ref(g)->reset(); Z3_CATCH; } unsigned Z3_API Z3_goal_size(Z3_context c, Z3_goal g) { Z3_TRY; LOG_Z3_goal_size(c, g); RESET_ERROR_CODE(); return to_goal_ref(g)->size(); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_goal_formula(Z3_context c, Z3_goal g, unsigned idx) { Z3_TRY; LOG_Z3_goal_formula(c, g, idx); RESET_ERROR_CODE(); if (idx >= to_goal_ref(g)->size()) { SET_ERROR_CODE(Z3_IOB, nullptr); RETURN_Z3(nullptr); } expr * result = to_goal_ref(g)->form(idx); mk_c(c)->save_ast_trail(result); RETURN_Z3(of_ast(result)); Z3_CATCH_RETURN(nullptr); } unsigned Z3_API Z3_goal_num_exprs(Z3_context c, Z3_goal g) { Z3_TRY; LOG_Z3_goal_num_exprs(c, g); RESET_ERROR_CODE(); return to_goal_ref(g)->num_exprs(); Z3_CATCH_RETURN(0); } bool Z3_API Z3_goal_is_decided_sat(Z3_context c, Z3_goal g) { Z3_TRY; LOG_Z3_goal_is_decided_sat(c, g); RESET_ERROR_CODE(); return to_goal_ref(g)->is_decided_sat(); Z3_CATCH_RETURN(false); } bool Z3_API Z3_goal_is_decided_unsat(Z3_context c, Z3_goal g) { Z3_TRY; LOG_Z3_goal_is_decided_unsat(c, g); RESET_ERROR_CODE(); return to_goal_ref(g)->is_decided_unsat(); Z3_CATCH_RETURN(false); } Z3_model Z3_API Z3_goal_convert_model(Z3_context c, Z3_goal g, Z3_model m) { Z3_TRY; LOG_Z3_goal_convert_model(c, g, m); RESET_ERROR_CODE(); model_ref new_m; Z3_model_ref * m_ref = alloc(Z3_model_ref, *mk_c(c)); mk_c(c)->save_object(m_ref); if (m) m_ref->m_model = to_model_ref(m)->copy(); if (to_goal_ref(g)->mc()) (*to_goal_ref(g)->mc())(m_ref->m_model); RETURN_Z3(of_model(m_ref)); Z3_CATCH_RETURN(nullptr); } Z3_goal Z3_API Z3_goal_translate(Z3_context c, Z3_goal g, Z3_context target) { Z3_TRY; LOG_Z3_goal_translate(c, g, target); RESET_ERROR_CODE(); ast_translation translator(mk_c(c)->m(), mk_c(target)->m()); Z3_goal_ref * _r = alloc(Z3_goal_ref, *mk_c(target)); _r->m_goal = to_goal_ref(g)->translate(translator); mk_c(target)->save_object(_r); Z3_goal r = of_goal(_r); RETURN_Z3(r); Z3_CATCH_RETURN(nullptr); } Z3_string Z3_API Z3_goal_to_string(Z3_context c, Z3_goal g) { Z3_TRY; LOG_Z3_goal_to_string(c, g); RESET_ERROR_CODE(); std::ostringstream buffer; to_goal_ref(g)->display(buffer); // Hack for removing the trailing '\n' std::string result = buffer.str(); SASSERT(result.size() > 0); result.resize(result.size()-1); return mk_c(c)->mk_external_string(std::move(result)); Z3_CATCH_RETURN(""); } Z3_string Z3_API Z3_goal_to_dimacs_string(Z3_context c, Z3_goal g) { Z3_TRY; LOG_Z3_goal_to_dimacs_string(c, g); RESET_ERROR_CODE(); std::ostringstream buffer; if (!to_goal_ref(g)->is_cnf()) { SET_ERROR_CODE(Z3_INVALID_ARG, "If this is not what you want, then preprocess by optional bit-blasting and applying tseitin-cnf"); RETURN_Z3(nullptr); } to_goal_ref(g)->display_dimacs(buffer); // Hack for removing the trailing '\n' std::string result = buffer.str(); SASSERT(result.size() > 0); result.resize(result.size()-1); return mk_c(c)->mk_external_string(std::move(result)); Z3_CATCH_RETURN(""); } }; z3-z3-4.8.7/src/api/api_goal.h000066400000000000000000000013141356505360400157120ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_goal.h Abstract: API for creating goals Author: Leonardo de Moura (leonardo) 2012-03-06. Revision History: --*/ #ifndef API_GOAL_H_ #define API_GOAL_H_ #include "api/api_util.h" #include "tactic/goal.h" struct Z3_goal_ref : public api::object { goal_ref m_goal; Z3_goal_ref(api::context& c) : api::object(c) {} ~Z3_goal_ref() override {} }; inline Z3_goal_ref * to_goal(Z3_goal g) { return reinterpret_cast(g); } inline Z3_goal of_goal(Z3_goal_ref * g) { return reinterpret_cast(g); } inline goal_ref to_goal_ref(Z3_goal g) { return g == nullptr ? goal_ref() : to_goal(g)->m_goal; } #endif z3-z3-4.8.7/src/api/api_log.cpp000066400000000000000000000033741356505360400161140ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_log.cpp Abstract: API for creating logs Author: Leonardo de Moura (leonardo) 2012-02-29. Revision History: --*/ #include #include #include "api/z3.h" #include "api/api_log_macros.h" #include "util/util.h" #include "util/z3_version.h" std::ostream * g_z3_log = nullptr; std::atomic g_z3_log_enabled; #ifdef Z3_LOG_SYNC static std::mutex g_log_mux; #define SCOPED_LOCK() std::lock_guard lock(g_log_mux) #else #define SCOPED_LOCK() {} #endif extern "C" { void Z3_close_log_unsafe(void) { if (g_z3_log != nullptr) { dealloc(g_z3_log); g_z3_log_enabled = false; g_z3_log = nullptr; } } bool Z3_API Z3_open_log(Z3_string filename) { bool res = true; SCOPED_LOCK(); if (g_z3_log != nullptr) Z3_close_log_unsafe(); g_z3_log = alloc(std::ofstream, filename); if (g_z3_log->bad() || g_z3_log->fail()) { dealloc(g_z3_log); g_z3_log = nullptr; res = false; } else { *g_z3_log << "V \"" << Z3_MAJOR_VERSION << "." << Z3_MINOR_VERSION << "." << Z3_BUILD_NUMBER << "." << Z3_REVISION_NUMBER << " " << __DATE__ << "\"\n"; g_z3_log->flush(); g_z3_log_enabled = true; } return res; } void Z3_API Z3_append_log(Z3_string str) { if (g_z3_log == nullptr) return; SCOPED_LOCK(); if (g_z3_log != nullptr) _Z3_append_log(static_cast(str)); } void Z3_API Z3_close_log(void) { if (g_z3_log != nullptr) { SCOPED_LOCK(); Z3_close_log_unsafe(); } } } z3-z3-4.8.7/src/api/api_model.cpp000066400000000000000000000371161356505360400164340ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_quant.cpp Abstract: API for models Author: Leonardo de Moura (leonardo) 2012-02-29. Revision History: --*/ #include "api/z3.h" #include "api/api_log_macros.h" #include "api/api_context.h" #include "api/api_model.h" #include "api/api_ast_vector.h" #include "ast/array_decl_plugin.h" #include "model/model.h" #include "model/model_v2_pp.h" #include "model/model_smt2_pp.h" #include "model/model_params.hpp" #include "model/model_evaluator_params.hpp" extern "C" { Z3_model Z3_API Z3_mk_model(Z3_context c) { Z3_TRY; LOG_Z3_mk_model(c); RESET_ERROR_CODE(); Z3_model_ref * m_ref = alloc(Z3_model_ref, *mk_c(c)); m_ref->m_model = alloc(model, mk_c(c)->m()); mk_c(c)->save_object(m_ref); RETURN_Z3(of_model(m_ref)); Z3_CATCH_RETURN(nullptr); } void Z3_API Z3_model_inc_ref(Z3_context c, Z3_model m) { Z3_TRY; LOG_Z3_model_inc_ref(c, m); RESET_ERROR_CODE(); if (m) { to_model(m)->inc_ref(); } Z3_CATCH; } void Z3_API Z3_model_dec_ref(Z3_context c, Z3_model m) { Z3_TRY; LOG_Z3_model_dec_ref(c, m); RESET_ERROR_CODE(); if (m) { to_model(m)->dec_ref(); } Z3_CATCH; } Z3_ast_opt Z3_API Z3_model_get_const_interp(Z3_context c, Z3_model m, Z3_func_decl a) { Z3_TRY; LOG_Z3_model_get_const_interp(c, m, a); RESET_ERROR_CODE(); CHECK_NON_NULL(m, nullptr); expr * r = to_model_ref(m)->get_const_interp(to_func_decl(a)); if (!r) { RETURN_Z3(nullptr); } mk_c(c)->save_ast_trail(r); RETURN_Z3(of_expr(r)); Z3_CATCH_RETURN(nullptr); } bool Z3_API Z3_model_has_interp(Z3_context c, Z3_model m, Z3_func_decl a) { Z3_TRY; LOG_Z3_model_has_interp(c, m, a); CHECK_NON_NULL(m, 0); return to_model_ref(m)->has_interpretation(to_func_decl(a)); Z3_CATCH_RETURN(false); } Z3_func_interp Z3_API Z3_model_get_func_interp(Z3_context c, Z3_model m, Z3_func_decl f) { Z3_TRY; LOG_Z3_model_get_func_interp(c, m, f); RESET_ERROR_CODE(); CHECK_NON_NULL(m, nullptr); func_interp * _fi = to_model_ref(m)->get_func_interp(to_func_decl(f)); if (!_fi) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } Z3_func_interp_ref * fi = alloc(Z3_func_interp_ref, *mk_c(c), to_model_ref(m)); fi->m_func_interp = _fi; mk_c(c)->save_object(fi); RETURN_Z3(of_func_interp(fi)); Z3_CATCH_RETURN(nullptr); } unsigned Z3_API Z3_model_get_num_consts(Z3_context c, Z3_model m) { Z3_TRY; LOG_Z3_model_get_num_consts(c, m); RESET_ERROR_CODE(); CHECK_NON_NULL(m, 0); return to_model_ref(m)->get_num_constants(); Z3_CATCH_RETURN(0); } Z3_func_decl Z3_API Z3_model_get_const_decl(Z3_context c, Z3_model m, unsigned i) { Z3_TRY; LOG_Z3_model_get_const_decl(c, m, i); RESET_ERROR_CODE(); CHECK_NON_NULL(m, nullptr); model * _m = to_model_ref(m); if (i < _m->get_num_constants()) { RETURN_Z3(of_func_decl(_m->get_constant(i))); } else { SET_ERROR_CODE(Z3_IOB, nullptr); RETURN_Z3(nullptr); } Z3_CATCH_RETURN(nullptr); } unsigned Z3_API Z3_model_get_num_funcs(Z3_context c, Z3_model m) { Z3_TRY; LOG_Z3_model_get_num_funcs(c, m); RESET_ERROR_CODE(); CHECK_NON_NULL(m, 0); return to_model_ref(m)->get_num_functions(); Z3_CATCH_RETURN(0); } Z3_func_decl get_model_func_decl_core(Z3_context c, Z3_model m, unsigned i) { CHECK_NON_NULL(m, nullptr); model * _m = to_model_ref(m); if (i >= _m->get_num_functions()) { SET_ERROR_CODE(Z3_IOB, nullptr); return nullptr; } return of_func_decl(_m->get_function(i)); } Z3_func_decl Z3_API Z3_model_get_func_decl(Z3_context c, Z3_model m, unsigned i) { Z3_TRY; LOG_Z3_model_get_func_decl(c, m, i); RESET_ERROR_CODE(); Z3_func_decl r = get_model_func_decl_core(c, m, i); RETURN_Z3(r); Z3_CATCH_RETURN(nullptr); } bool Z3_API Z3_model_eval(Z3_context c, Z3_model m, Z3_ast t, bool model_completion, Z3_ast * v) { Z3_TRY; LOG_Z3_model_eval(c, m, t, model_completion, v); if (v) *v = nullptr; RESET_ERROR_CODE(); CHECK_NON_NULL(m, false); CHECK_IS_EXPR(t, false); model * _m = to_model_ref(m); params_ref p; ast_manager& mgr = mk_c(c)->m(); if (!_m->has_solver()) { _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()); *v = of_ast(result.get()); RETURN_Z3_model_eval true; Z3_CATCH_RETURN(false); } unsigned Z3_API Z3_model_get_num_sorts(Z3_context c, Z3_model m) { Z3_TRY; LOG_Z3_model_get_num_sorts(c, m); RESET_ERROR_CODE(); return to_model_ref(m)->get_num_uninterpreted_sorts(); Z3_CATCH_RETURN(0); } Z3_sort Z3_API Z3_model_get_sort(Z3_context c, Z3_model m, unsigned i) { Z3_TRY; LOG_Z3_model_get_sort(c, m, i); RESET_ERROR_CODE(); if (i >= to_model_ref(m)->get_num_uninterpreted_sorts()) { SET_ERROR_CODE(Z3_IOB, nullptr); RETURN_Z3(nullptr); } sort * s = to_model_ref(m)->get_uninterpreted_sort(i); RETURN_Z3(of_sort(s)); Z3_CATCH_RETURN(nullptr); } Z3_ast_vector Z3_API Z3_model_get_sort_universe(Z3_context c, Z3_model m, Z3_sort s) { Z3_TRY; LOG_Z3_model_get_sort_universe(c, m, s); RESET_ERROR_CODE(); if (!to_model_ref(m)->has_uninterpreted_sort(to_sort(s))) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } ptr_vector const & universe = to_model_ref(m)->get_universe(to_sort(s)); Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, *mk_c(c), mk_c(c)->m()); mk_c(c)->save_object(v); for (expr * e : universe) { v->m_ast_vector.push_back(e); } RETURN_Z3(of_ast_vector(v)); Z3_CATCH_RETURN(nullptr); } Z3_model Z3_API Z3_model_translate(Z3_context c, Z3_model m, Z3_context target) { Z3_TRY; LOG_Z3_model_translate(c, m, target); RESET_ERROR_CODE(); Z3_model_ref* dst = alloc(Z3_model_ref, *mk_c(target)); ast_translation tr(mk_c(c)->m(), mk_c(target)->m()); dst->m_model = to_model_ref(m)->translate(tr); mk_c(target)->save_object(dst); RETURN_Z3(of_model(dst)); Z3_CATCH_RETURN(nullptr); } bool Z3_API Z3_is_as_array(Z3_context c, Z3_ast a) { Z3_TRY; LOG_Z3_is_as_array(c, a); RESET_ERROR_CODE(); return a && is_expr(to_ast(a)) && is_app_of(to_expr(a), mk_c(c)->get_array_fid(), OP_AS_ARRAY); Z3_CATCH_RETURN(false); } Z3_func_decl Z3_API Z3_get_as_array_func_decl(Z3_context c, Z3_ast a) { Z3_TRY; LOG_Z3_get_as_array_func_decl(c, a); RESET_ERROR_CODE(); if (a && is_expr(to_ast(a)) && is_app_of(to_expr(a), mk_c(c)->get_array_fid(), OP_AS_ARRAY)) { RETURN_Z3(of_func_decl(to_func_decl(to_app(a)->get_decl()->get_parameter(0).get_ast()))); } else { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } Z3_CATCH_RETURN(nullptr); } Z3_func_interp Z3_API Z3_add_func_interp(Z3_context c, Z3_model m, Z3_func_decl f, Z3_ast else_val) { Z3_TRY; LOG_Z3_add_func_interp(c, m, f, else_val); RESET_ERROR_CODE(); CHECK_NON_NULL(f, nullptr); func_decl* d = to_func_decl(f); model* mdl = to_model_ref(m); Z3_func_interp_ref * f_ref = alloc(Z3_func_interp_ref, *mk_c(c), mdl); f_ref->m_func_interp = alloc(func_interp, mk_c(c)->m(), d->get_arity()); mk_c(c)->save_object(f_ref); mdl->register_decl(d, f_ref->m_func_interp); f_ref->m_func_interp->set_else(to_expr(else_val)); RETURN_Z3(of_func_interp(f_ref)); Z3_CATCH_RETURN(nullptr); } void Z3_API Z3_add_const_interp(Z3_context c, Z3_model m, Z3_func_decl f, Z3_ast a) { Z3_TRY; LOG_Z3_add_const_interp(c, m, f, a); RESET_ERROR_CODE(); func_decl* d = to_func_decl(f); if (!d || d->get_arity() != 0) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); } else { model* mdl = to_model_ref(m); mdl->register_decl(d, to_expr(a)); } Z3_CATCH; } void Z3_API Z3_func_interp_inc_ref(Z3_context c, Z3_func_interp f) { Z3_TRY; LOG_Z3_func_interp_inc_ref(c, f); RESET_ERROR_CODE(); if (f) { to_func_interp(f)->inc_ref(); } Z3_CATCH; } void Z3_API Z3_func_interp_dec_ref(Z3_context c, Z3_func_interp f) { Z3_TRY; LOG_Z3_func_interp_dec_ref(c, f); RESET_ERROR_CODE(); if (f) { to_func_interp(f)->dec_ref(); } Z3_CATCH; } unsigned Z3_API Z3_func_interp_get_num_entries(Z3_context c, Z3_func_interp f) { Z3_TRY; LOG_Z3_func_interp_get_num_entries(c, f); RESET_ERROR_CODE(); CHECK_NON_NULL(f, 0); return to_func_interp_ref(f)->num_entries(); Z3_CATCH_RETURN(0); } Z3_func_entry Z3_API Z3_func_interp_get_entry(Z3_context c, Z3_func_interp f, unsigned i) { Z3_TRY; LOG_Z3_func_interp_get_entry(c, f, i); RESET_ERROR_CODE(); CHECK_NON_NULL(f, nullptr); if (i >= to_func_interp_ref(f)->num_entries()) { SET_ERROR_CODE(Z3_IOB, nullptr); RETURN_Z3(nullptr); } Z3_func_entry_ref * e = alloc(Z3_func_entry_ref, *mk_c(c), to_func_interp(f)->m_model.get()); e->m_func_interp = to_func_interp_ref(f); e->m_func_entry = to_func_interp_ref(f)->get_entry(i); mk_c(c)->save_object(e); RETURN_Z3(of_func_entry(e)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_func_interp_get_else(Z3_context c, Z3_func_interp f) { Z3_TRY; LOG_Z3_func_interp_get_else(c, f); RESET_ERROR_CODE(); CHECK_NON_NULL(f, nullptr); expr * e = to_func_interp_ref(f)->get_else(); if (e) { mk_c(c)->save_ast_trail(e); } RETURN_Z3(of_expr(e)); Z3_CATCH_RETURN(nullptr); } void Z3_API Z3_func_interp_set_else(Z3_context c, Z3_func_interp f, Z3_ast else_value) { Z3_TRY; LOG_Z3_func_interp_set_else(c, f, else_value); RESET_ERROR_CODE(); // CHECK_NON_NULL(f, 0); to_func_interp_ref(f)->set_else(to_expr(else_value)); Z3_CATCH; } unsigned Z3_API Z3_func_interp_get_arity(Z3_context c, Z3_func_interp f) { Z3_TRY; LOG_Z3_func_interp_get_arity(c, f); RESET_ERROR_CODE(); CHECK_NON_NULL(f, 0); return to_func_interp_ref(f)->get_arity(); Z3_CATCH_RETURN(0); } void Z3_API Z3_func_interp_add_entry(Z3_context c, Z3_func_interp fi, Z3_ast_vector args, Z3_ast value) { Z3_TRY; LOG_Z3_func_interp_add_entry(c, fi, args, value); //CHECK_NON_NULL(fi, void); //CHECK_NON_NULL(args, void); //CHECK_NON_NULL(value, void); func_interp* _fi = to_func_interp_ref(fi); expr* _value = to_expr(value); if (to_ast_vector_ref(args).size() != _fi->get_arity()) { SET_ERROR_CODE(Z3_IOB, nullptr); return; } // check sorts of value expr* const* _args = (expr* const*) to_ast_vector_ref(args).c_ptr(); _fi->insert_entry(_args, _value); Z3_CATCH; } void Z3_API Z3_func_entry_inc_ref(Z3_context c, Z3_func_entry e) { Z3_TRY; LOG_Z3_func_entry_inc_ref(c, e); RESET_ERROR_CODE(); if (e) { to_func_entry(e)->inc_ref(); } Z3_CATCH; } void Z3_API Z3_func_entry_dec_ref(Z3_context c, Z3_func_entry e) { Z3_TRY; LOG_Z3_func_entry_dec_ref(c, e); RESET_ERROR_CODE(); if (e) { to_func_entry(e)->dec_ref(); } Z3_CATCH; } Z3_ast Z3_API Z3_func_entry_get_value(Z3_context c, Z3_func_entry e) { Z3_TRY; LOG_Z3_func_entry_get_value(c, e); RESET_ERROR_CODE(); expr * v = to_func_entry_ref(e)->get_result(); mk_c(c)->save_ast_trail(v); RETURN_Z3(of_expr(v)); Z3_CATCH_RETURN(nullptr); } unsigned Z3_API Z3_func_entry_get_num_args(Z3_context c, Z3_func_entry e) { Z3_TRY; LOG_Z3_func_entry_get_num_args(c, e); RESET_ERROR_CODE(); return to_func_entry(e)->m_func_interp->get_arity(); Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_func_entry_get_arg(Z3_context c, Z3_func_entry e, unsigned i) { Z3_TRY; LOG_Z3_func_entry_get_arg(c, e, i); RESET_ERROR_CODE(); if (i >= to_func_entry(e)->m_func_interp->get_arity()) { SET_ERROR_CODE(Z3_IOB, nullptr); RETURN_Z3(nullptr); } expr * r = to_func_entry(e)->m_func_entry->get_arg(i); RETURN_Z3(of_expr(r)); Z3_CATCH_RETURN(nullptr); } unsigned get_model_func_num_entries_core(Z3_context c, Z3_model m, unsigned i) { RESET_ERROR_CODE(); CHECK_NON_NULL(m, 0); Z3_func_decl d = get_model_func_decl_core(c, m, i); if (d) { model * _m = to_model_ref(m); func_interp * g = _m->get_func_interp(to_func_decl(d)); if (g) { return g->num_entries(); } SET_ERROR_CODE(Z3_IOB, nullptr); return 0; } return 0; } unsigned get_model_func_entry_num_args_core(Z3_context c, Z3_model m, unsigned i, unsigned j) { RESET_ERROR_CODE(); CHECK_NON_NULL(m, 0); if (j >= get_model_func_num_entries_core(c, m, i)) { SET_ERROR_CODE(Z3_IOB, nullptr); return 0; } Z3_func_decl d = get_model_func_decl_core(c, m, i); if (d) { model * _m = to_model_ref(m); func_interp * g = _m->get_func_interp(to_func_decl(d)); return g->get_arity(); } return 0; } Z3_API char const * Z3_model_to_string(Z3_context c, Z3_model m) { Z3_TRY; LOG_Z3_model_to_string(c, m); RESET_ERROR_CODE(); CHECK_NON_NULL(m, nullptr); std::ostringstream buffer; std::string result; if (mk_c(c)->get_print_mode() == Z3_PRINT_SMTLIB2_COMPLIANT) { model_smt2_pp(buffer, mk_c(c)->m(), *(to_model_ref(m)), 0); // Hack for removing the trailing '\n' result = buffer.str(); if (!result.empty()) result.resize(result.size()-1); } else { model_params p; model_v2_pp(buffer, *(to_model_ref(m)), p.partial()); result = buffer.str(); } return mk_c(c)->mk_external_string(std::move(result)); Z3_CATCH_RETURN(nullptr); } }; z3-z3-4.8.7/src/api/api_model.h000066400000000000000000000040251356505360400160720ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_model.h Abstract: API for models Author: Leonardo de Moura (leonardo) 2012-03-08. Revision History: --*/ #ifndef API_MODEL_H_ #define API_MODEL_H_ #include "api/api_util.h" #include "model/model.h" struct Z3_model_ref : public api::object { model_ref m_model; Z3_model_ref(api::context& c): api::object(c) {} ~Z3_model_ref() override {} }; inline Z3_model_ref * to_model(Z3_model s) { return reinterpret_cast(s); } inline Z3_model of_model(Z3_model_ref * s) { return reinterpret_cast(s); } inline model * to_model_ref(Z3_model s) { return to_model(s)->m_model.get(); } struct Z3_func_interp_ref : public api::object { model_ref m_model; // must have it to prevent reference to m_func_interp to be killed. func_interp * m_func_interp; Z3_func_interp_ref(api::context& c, model * m): api::object(c), m_model(m), m_func_interp(nullptr) {} ~Z3_func_interp_ref() override {} }; inline Z3_func_interp_ref * to_func_interp(Z3_func_interp s) { return reinterpret_cast(s); } inline Z3_func_interp of_func_interp(Z3_func_interp_ref * s) { return reinterpret_cast(s); } inline func_interp * to_func_interp_ref(Z3_func_interp s) { return to_func_interp(s)->m_func_interp; } struct Z3_func_entry_ref : public api::object { model_ref m_model; // must have it to prevent reference to m_func_entry to be killed. func_interp * m_func_interp; func_entry const * m_func_entry; Z3_func_entry_ref(api::context& c, model * m):api::object(c), m_model(m), m_func_interp(nullptr), m_func_entry(nullptr) {} ~Z3_func_entry_ref() override {} }; inline Z3_func_entry_ref * to_func_entry(Z3_func_entry s) { return reinterpret_cast(s); } inline Z3_func_entry of_func_entry(Z3_func_entry_ref * s) { return reinterpret_cast(s); } inline func_entry const * to_func_entry_ref(Z3_func_entry s) { return to_func_entry(s)->m_func_entry; } #endif z3-z3-4.8.7/src/api/api_numeral.cpp000066400000000000000000000346561356505360400170050ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_numeral.cpp Abstract: API for handling numerals in Z3 Author: Leonardo de Moura (leonardo) 2012-02-29. Revision History: --*/ #include #include #include "api/z3.h" #include "api/api_log_macros.h" #include "api/api_context.h" #include "api/api_util.h" #include "ast/arith_decl_plugin.h" #include "ast/bv_decl_plugin.h" #include "math/polynomial/algebraic_numbers.h" #include "ast/fpa_decl_plugin.h" bool is_numeral_sort(Z3_context c, Z3_sort ty) { if (!ty) return false; sort * _ty = to_sort(ty); family_id fid = _ty->get_family_id(); if (fid != mk_c(c)->get_arith_fid() && fid != mk_c(c)->get_bv_fid() && fid != mk_c(c)->get_datalog_fid() && fid != mk_c(c)->get_fpa_fid()) { return false; } return true; } static bool check_numeral_sort(Z3_context c, Z3_sort ty) { bool is_num = is_numeral_sort(c, ty); if (!is_num) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); } return is_num; } extern "C" { Z3_ast Z3_API Z3_mk_numeral(Z3_context c, const char* n, Z3_sort ty) { Z3_TRY; LOG_Z3_mk_numeral(c, n, ty); RESET_ERROR_CODE(); if (!check_numeral_sort(c, ty)) { RETURN_Z3(nullptr); } if (!n) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } sort * _ty = to_sort(ty); bool is_float = mk_c(c)->fpautil().is_float(_ty); char const* m = n; while (*m) { if (!(('0' <= *m && *m <= '9') || ('/' == *m) || ('-' == *m) || (' ' == *m) || ('\n' == *m) || ('.' == *m) || ('e' == *m) || ('E' == *m) || ('+' == *m) || (is_float && (('p' == *m) || ('P' == *m))))) { SET_ERROR_CODE(Z3_PARSER_ERROR, nullptr); RETURN_Z3(nullptr); } ++m; } ast * a = nullptr; if (_ty->get_family_id() == mk_c(c)->get_fpa_fid()) { // avoid expanding floats into huge rationals. fpa_util & fu = mk_c(c)->fpautil(); scoped_mpf t(fu.fm()); fu.fm().set(t, fu.get_ebits(_ty), fu.get_sbits(_ty), MPF_ROUND_TOWARD_ZERO, n); a = fu.mk_value(t); mk_c(c)->save_ast_trail(a); } else a = mk_c(c)->mk_numeral_core(rational(n), _ty); RETURN_Z3(of_ast(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_int(Z3_context c, int value, Z3_sort ty) { Z3_TRY; LOG_Z3_mk_int(c, value, ty); RESET_ERROR_CODE(); if (!check_numeral_sort(c, ty)) { RETURN_Z3(nullptr); } ast * a = mk_c(c)->mk_numeral_core(rational(value), to_sort(ty)); RETURN_Z3(of_ast(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_unsigned_int(Z3_context c, unsigned value, Z3_sort ty) { Z3_TRY; LOG_Z3_mk_unsigned_int(c, value, ty); RESET_ERROR_CODE(); if (!check_numeral_sort(c, ty)) { RETURN_Z3(nullptr); } ast * a = mk_c(c)->mk_numeral_core(rational(value), to_sort(ty)); RETURN_Z3(of_ast(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_int64(Z3_context c, int64_t value, Z3_sort ty) { Z3_TRY; LOG_Z3_mk_int64(c, value, ty); RESET_ERROR_CODE(); if (!check_numeral_sort(c, ty)) { RETURN_Z3(nullptr); } rational n(value, rational::i64()); ast* a = mk_c(c)->mk_numeral_core(n, to_sort(ty)); RETURN_Z3(of_ast(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_unsigned_int64(Z3_context c, uint64_t value, Z3_sort ty) { Z3_TRY; LOG_Z3_mk_unsigned_int64(c, value, ty); RESET_ERROR_CODE(); if (!check_numeral_sort(c, ty)) { RETURN_Z3(nullptr); } rational n(value, rational::ui64()); ast * a = mk_c(c)->mk_numeral_core(n, to_sort(ty)); RETURN_Z3(of_ast(a)); Z3_CATCH_RETURN(nullptr); } bool Z3_API Z3_is_numeral_ast(Z3_context c, Z3_ast a) { Z3_TRY; LOG_Z3_is_numeral_ast(c, a); RESET_ERROR_CODE(); CHECK_IS_EXPR(a, false); expr* e = to_expr(a); return mk_c(c)->autil().is_numeral(e) || mk_c(c)->bvutil().is_numeral(e) || mk_c(c)->fpautil().is_numeral(e) || mk_c(c)->fpautil().is_rm_numeral(e) || mk_c(c)->datalog_util().is_numeral_ext(e); Z3_CATCH_RETURN(false); } bool Z3_API Z3_get_numeral_rational(Z3_context c, Z3_ast a, rational& r) { Z3_TRY; // This function is not part of the public API RESET_ERROR_CODE(); CHECK_IS_EXPR(a, false); expr* e = to_expr(a); if (mk_c(c)->autil().is_numeral(e, r)) { return true; } unsigned bv_size; if (mk_c(c)->bvutil().is_numeral(e, r, bv_size)) { return true; } uint64_t v; if (mk_c(c)->datalog_util().is_numeral(e, v)) { r = rational(v, rational::ui64()); return true; } return false; Z3_CATCH_RETURN(false); } Z3_string Z3_API Z3_get_numeral_string(Z3_context c, Z3_ast a) { Z3_TRY; // This function invokes Z3_get_numeral_rational, but it is still ok to add LOG command here because it does not return a Z3 object. LOG_Z3_get_numeral_string(c, a); RESET_ERROR_CODE(); CHECK_IS_EXPR(a, ""); rational r; bool ok = Z3_get_numeral_rational(c, a, r); if (ok) { return mk_c(c)->mk_external_string(r.to_string()); } else { fpa_util & fu = mk_c(c)->fpautil(); scoped_mpf tmp(fu.fm()); mpf_rounding_mode rm; if (mk_c(c)->fpautil().is_rm_numeral(to_expr(a), rm)) { switch (rm) { case MPF_ROUND_NEAREST_TEVEN: return mk_c(c)->mk_external_string("roundNearestTiesToEven"); break; case MPF_ROUND_NEAREST_TAWAY: return mk_c(c)->mk_external_string("roundNearestTiesToAway"); break; case MPF_ROUND_TOWARD_POSITIVE: return mk_c(c)->mk_external_string("roundTowardPositive"); break; case MPF_ROUND_TOWARD_NEGATIVE: return mk_c(c)->mk_external_string("roundTowardNegative"); break; case MPF_ROUND_TOWARD_ZERO: default: return mk_c(c)->mk_external_string("roundTowardZero"); break; } } else if (mk_c(c)->fpautil().is_numeral(to_expr(a), tmp)) { std::ostringstream buffer; fu.fm().display_smt2(buffer, tmp, false); return mk_c(c)->mk_external_string(buffer.str()); } else { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return ""; } } Z3_CATCH_RETURN(""); } double Z3_API Z3_get_numeral_double(Z3_context c, Z3_ast a) { LOG_Z3_get_numeral_double(c, a); RESET_ERROR_CODE(); if (!is_expr(a)) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return NAN; } expr* e = to_expr(a); fpa_util & fu = mk_c(c)->fpautil(); scoped_mpf tmp(fu.fm()); if (mk_c(c)->fpautil().is_numeral(e, tmp)) { if (tmp.get().get_ebits() > 11 || tmp.get().get_sbits() > 53) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return NAN; } return fu.fm().to_double(tmp); } rational r; arith_util & u = mk_c(c)->autil(); if (u.is_numeral(e, r)) { return r.get_double(); } SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return 0.0; } Z3_string Z3_API Z3_get_numeral_decimal_string(Z3_context c, Z3_ast a, unsigned precision) { Z3_TRY; LOG_Z3_get_numeral_decimal_string(c, a, precision); RESET_ERROR_CODE(); CHECK_IS_EXPR(a, ""); expr* e = to_expr(a); rational r; arith_util & u = mk_c(c)->autil(); fpa_util & fu = mk_c(c)->fpautil(); scoped_mpf ftmp(fu.fm()); mpf_rounding_mode rm; if (u.is_numeral(e, r) && !r.is_int()) { std::ostringstream buffer; r.display_decimal(buffer, precision); return mk_c(c)->mk_external_string(buffer.str()); } if (u.is_irrational_algebraic_numeral(e)) { algebraic_numbers::anum const & n = u.to_irrational_algebraic_numeral(e); algebraic_numbers::manager & am = u.am(); std::ostringstream buffer; am.display_decimal(buffer, n, precision); return mk_c(c)->mk_external_string(buffer.str()); } else if (mk_c(c)->fpautil().is_rm_numeral(to_expr(a), rm)) return Z3_get_numeral_string(c, a); else if (mk_c(c)->fpautil().is_numeral(to_expr(a), ftmp)) { std::ostringstream buffer; fu.fm().display_decimal(buffer, ftmp, 12); return mk_c(c)->mk_external_string(buffer.str()); } else if (Z3_get_numeral_rational(c, a, r)) { return mk_c(c)->mk_external_string(r.to_string()); } else { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return ""; } Z3_CATCH_RETURN(""); } bool Z3_API Z3_get_numeral_small(Z3_context c, Z3_ast a, int64_t* num, int64_t* den) { Z3_TRY; // This function invokes Z3_get_numeral_rational, but it is still ok to add LOG command here because it does not return a Z3 object. LOG_Z3_get_numeral_small(c, a, num, den); RESET_ERROR_CODE(); CHECK_IS_EXPR(a, false); rational r; bool ok = Z3_get_numeral_rational(c, a, r); if (ok) { rational n = numerator(r); rational d = denominator(r); if (n.is_int64() && d.is_int64()) { *num = n.get_int64(); *den = d.get_int64(); return true; } else { return false; } } SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return false; Z3_CATCH_RETURN(false); } bool Z3_API Z3_get_numeral_int(Z3_context c, Z3_ast v, int* i) { Z3_TRY; // This function invokes Z3_get_numeral_int64, but it is still ok to add LOG command here because it does not return a Z3 object. LOG_Z3_get_numeral_int(c, v, i); RESET_ERROR_CODE(); CHECK_IS_EXPR(v, false); if (!i) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return false; } int64_t l; if (Z3_get_numeral_int64(c, v, &l) && l >= INT_MIN && l <= INT_MAX) { *i = static_cast(l); return true; } return false; Z3_CATCH_RETURN(false); } bool Z3_API Z3_get_numeral_uint(Z3_context c, Z3_ast v, unsigned* u) { Z3_TRY; // This function invokes Z3_get_numeral_uint64, but it is still ok to add LOG command here because it does not return a Z3 object. LOG_Z3_get_numeral_uint(c, v, u); RESET_ERROR_CODE(); CHECK_IS_EXPR(v, false); if (!u) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return false; } uint64_t l; if (Z3_get_numeral_uint64(c, v, &l) && (l <= 0xFFFFFFFF)) { *u = static_cast(l); return true; } return false; Z3_CATCH_RETURN(false); } bool Z3_API Z3_get_numeral_uint64(Z3_context c, Z3_ast v, uint64_t* u) { Z3_TRY; // This function invokes Z3_get_numeral_rational, but it is still ok to add LOG command here because it does not return a Z3 object. LOG_Z3_get_numeral_uint64(c, v, u); RESET_ERROR_CODE(); CHECK_IS_EXPR(v, false); if (!u) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return false; } rational r; bool ok = Z3_get_numeral_rational(c, v, r); SASSERT(u); if (ok && r.is_uint64()) { *u = r.get_uint64(); return ok; } return false; Z3_CATCH_RETURN(false); } bool Z3_API Z3_get_numeral_int64(Z3_context c, Z3_ast v, int64_t* i) { Z3_TRY; // This function invokes Z3_get_numeral_rational, but it is still ok to add LOG command here because it does not return a Z3 object. LOG_Z3_get_numeral_int64(c, v, i); RESET_ERROR_CODE(); CHECK_IS_EXPR(v, false); if (!i) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return false; } rational r; bool ok = Z3_get_numeral_rational(c, v, r); if (ok && r.is_int64()) { *i = r.get_int64(); return ok; } return false; Z3_CATCH_RETURN(false); } bool Z3_API Z3_get_numeral_rational_int64(Z3_context c, Z3_ast v, int64_t* num, int64_t* den) { Z3_TRY; // This function invokes Z3_get_numeral_rational, but it is still ok to add LOG command here because it does not return a Z3 object. LOG_Z3_get_numeral_rational_int64(c, v, num, den); RESET_ERROR_CODE(); CHECK_IS_EXPR(v, false); if (!num || !den) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return false; } rational r; bool ok = Z3_get_numeral_rational(c, v, r); if (ok != true) { return ok; } rational n = numerator(r); rational d = denominator(r); if (n.is_int64() && d.is_int64()) { *num = n.get_int64(); *den = d.get_int64(); return ok; } return false; Z3_CATCH_RETURN(false); } Z3_ast Z3_API Z3_mk_bv_numeral(Z3_context c, unsigned sz, bool const* bits) { Z3_TRY; LOG_Z3_mk_bv_numeral(c, sz, bits); RESET_ERROR_CODE(); rational r(0); for (unsigned i = 0; i < sz; ++i) { if (bits[i]) r += rational::power_of_two(i); } ast * a = mk_c(c)->mk_numeral_core(r, mk_c(c)->bvutil().mk_sort(sz)); RETURN_Z3(of_ast(a)); Z3_CATCH_RETURN(nullptr); } }; z3-z3-4.8.7/src/api/api_opt.cpp000066400000000000000000000346571356505360400161450ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: api_opt.cpp Abstract: API for optimization Author: Nikolaj Bjorner (nbjorner) 2013-12-3. Revision History: --*/ #include #include "util/cancel_eh.h" #include "util/scoped_timer.h" #include "util/scoped_ctrl_c.h" #include "util/file_path.h" #include "parsers/smt2/smt2parser.h" #include "model/model_params.hpp" #include "opt/opt_context.h" #include "opt/opt_cmds.h" #include "opt/opt_parse.h" #include "api/z3.h" #include "api/api_log_macros.h" #include "api/api_stats.h" #include "api/api_context.h" #include "api/api_util.h" #include "api/api_model.h" #include "api/api_ast_vector.h" extern "C" { struct Z3_optimize_ref : public api::object { opt::context* m_opt; Z3_optimize_ref(api::context& c): api::object(c), m_opt(nullptr) {} ~Z3_optimize_ref() override { dealloc(m_opt); } }; inline Z3_optimize_ref * to_optimize(Z3_optimize o) { return reinterpret_cast(o); } inline Z3_optimize of_optimize(Z3_optimize_ref * o) { return reinterpret_cast(o); } inline opt::context* to_optimize_ptr(Z3_optimize o) { return to_optimize(o)->m_opt; } Z3_optimize Z3_API Z3_mk_optimize(Z3_context c) { Z3_TRY; LOG_Z3_mk_optimize(c); RESET_ERROR_CODE(); Z3_optimize_ref * o = alloc(Z3_optimize_ref, *mk_c(c)); o->m_opt = alloc(opt::context,mk_c(c)->m()); mk_c(c)->save_object(o); RETURN_Z3(of_optimize(o)); Z3_CATCH_RETURN(nullptr); } void Z3_API Z3_optimize_inc_ref(Z3_context c, Z3_optimize o) { Z3_TRY; LOG_Z3_optimize_inc_ref(c, o); RESET_ERROR_CODE(); to_optimize(o)->inc_ref(); Z3_CATCH; } void Z3_API Z3_optimize_dec_ref(Z3_context c, Z3_optimize o) { Z3_TRY; LOG_Z3_optimize_dec_ref(c, o); RESET_ERROR_CODE(); to_optimize(o)->dec_ref(); Z3_CATCH; } void Z3_API Z3_optimize_assert(Z3_context c, Z3_optimize o, Z3_ast a) { Z3_TRY; LOG_Z3_optimize_assert(c, o, a); RESET_ERROR_CODE(); CHECK_FORMULA(a,); to_optimize_ptr(o)->add_hard_constraint(to_expr(a)); 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); RESET_ERROR_CODE(); CHECK_FORMULA(a,0); rational w(weight); return to_optimize_ptr(o)->add_soft_constraint(to_expr(a), w, to_symbol(id)); Z3_CATCH_RETURN(0); } unsigned Z3_API Z3_optimize_maximize(Z3_context c, Z3_optimize o, Z3_ast t) { Z3_TRY; LOG_Z3_optimize_maximize(c, o, t); RESET_ERROR_CODE(); CHECK_VALID_AST(t,0); return to_optimize_ptr(o)->add_objective(to_app(t), true); Z3_CATCH_RETURN(0); } unsigned Z3_API Z3_optimize_minimize(Z3_context c, Z3_optimize o, Z3_ast t) { Z3_TRY; LOG_Z3_optimize_minimize(c, o, t); RESET_ERROR_CODE(); CHECK_VALID_AST(t,0); return to_optimize_ptr(o)->add_objective(to_app(t), false); Z3_CATCH_RETURN(0); } void Z3_API Z3_optimize_push(Z3_context c,Z3_optimize d) { Z3_TRY; LOG_Z3_optimize_push(c, d); RESET_ERROR_CODE(); to_optimize_ptr(d)->push(); Z3_CATCH; } void Z3_API Z3_optimize_pop(Z3_context c,Z3_optimize d) { Z3_TRY; LOG_Z3_optimize_pop(c, d); RESET_ERROR_CODE(); to_optimize_ptr(d)->pop(1); Z3_CATCH; } Z3_lbool Z3_API Z3_optimize_check(Z3_context c, Z3_optimize o, unsigned num_assumptions, Z3_ast const assumptions[]) { Z3_TRY; LOG_Z3_optimize_check(c, o, num_assumptions, assumptions); RESET_ERROR_CODE(); for (unsigned i = 0; i < num_assumptions; i++) { if (!is_expr(to_ast(assumptions[i]))) { SET_ERROR_CODE(Z3_INVALID_ARG, "assumption is not an expression"); return Z3_L_UNDEF; } } lbool r = l_undef; cancel_eh eh(mk_c(c)->m().limit()); unsigned timeout = to_optimize_ptr(o)->get_params().get_uint("timeout", mk_c(c)->get_timeout()); unsigned rlimit = to_optimize_ptr(o)->get_params().get_uint("rlimit", mk_c(c)->get_rlimit()); bool use_ctrl_c = to_optimize_ptr(o)->get_params().get_bool("ctrl_c", true); api::context::set_interruptable si(*(mk_c(c)), eh); { scoped_ctrl_c ctrlc(eh, false, use_ctrl_c); scoped_timer timer(timeout, &eh); scoped_rlimit _rlimit(mk_c(c)->m().limit(), rlimit); try { expr_ref_vector asms(mk_c(c)->m()); asms.append(num_assumptions, to_exprs(num_assumptions, assumptions)); r = to_optimize_ptr(o)->optimize(asms); } catch (z3_exception& ex) { if (!mk_c(c)->m().canceled()) { mk_c(c)->handle_exception(ex); } r = l_undef; if (ex.msg() == std::string("canceled") && mk_c(c)->m().canceled()) { to_optimize_ptr(o)->set_reason_unknown(ex.msg()); } else { mk_c(c)->handle_exception(ex); } } // to_optimize_ref(d).cleanup(); } return of_lbool(r); Z3_CATCH_RETURN(Z3_L_UNDEF); } Z3_ast_vector Z3_API Z3_optimize_get_unsat_core(Z3_context c, Z3_optimize o) { Z3_TRY; LOG_Z3_optimize_get_unsat_core(c, o); RESET_ERROR_CODE(); expr_ref_vector core(mk_c(c)->m()); to_optimize_ptr(o)->get_unsat_core(core); Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, *mk_c(c), mk_c(c)->m()); mk_c(c)->save_object(v); for (expr* e : core) { v->m_ast_vector.push_back(e); } RETURN_Z3(of_ast_vector(v)); Z3_CATCH_RETURN(nullptr); } Z3_string Z3_API Z3_optimize_get_reason_unknown(Z3_context c, Z3_optimize o) { Z3_TRY; LOG_Z3_optimize_to_string(c, o); RESET_ERROR_CODE(); return mk_c(c)->mk_external_string(to_optimize_ptr(o)->reason_unknown()); Z3_CATCH_RETURN(""); } Z3_model Z3_API Z3_optimize_get_model(Z3_context c, Z3_optimize o) { Z3_TRY; LOG_Z3_optimize_get_model(c, o); RESET_ERROR_CODE(); model_ref _m; to_optimize_ptr(o)->get_model(_m); Z3_model_ref * m_ref = alloc(Z3_model_ref, *mk_c(c)); if (_m) { model_params mp(to_optimize_ptr(o)->get_params()); if (mp.compact()) _m->compress(); m_ref->m_model = _m; } else { m_ref->m_model = alloc(model, mk_c(c)->m()); } mk_c(c)->save_object(m_ref); RETURN_Z3(of_model(m_ref)); Z3_CATCH_RETURN(nullptr); } void Z3_API Z3_optimize_set_params(Z3_context c, Z3_optimize o, Z3_params p) { Z3_TRY; LOG_Z3_optimize_set_params(c, o, p); RESET_ERROR_CODE(); param_descrs descrs; to_optimize_ptr(o)->collect_param_descrs(descrs); to_params(p)->m_params.validate(descrs); params_ref pr = to_param_ref(p); to_optimize_ptr(o)->updt_params(pr); Z3_CATCH; } Z3_param_descrs Z3_API Z3_optimize_get_param_descrs(Z3_context c, Z3_optimize o) { Z3_TRY; LOG_Z3_optimize_get_param_descrs(c, o); RESET_ERROR_CODE(); Z3_param_descrs_ref * d = alloc(Z3_param_descrs_ref, *mk_c(c)); mk_c(c)->save_object(d); to_optimize_ptr(o)->collect_param_descrs(d->m_descrs); Z3_param_descrs r = of_param_descrs(d); RETURN_Z3(r); Z3_CATCH_RETURN(nullptr); } // get lower value or current approximation Z3_ast Z3_API Z3_optimize_get_lower(Z3_context c, Z3_optimize o, unsigned idx) { Z3_TRY; LOG_Z3_optimize_get_lower(c, o, idx); RESET_ERROR_CODE(); expr_ref e = to_optimize_ptr(o)->get_lower(idx); mk_c(c)->save_ast_trail(e); RETURN_Z3(of_expr(e)); Z3_CATCH_RETURN(nullptr); } // get upper or current approximation Z3_ast Z3_API Z3_optimize_get_upper(Z3_context c, Z3_optimize o, unsigned idx) { Z3_TRY; LOG_Z3_optimize_get_upper(c, o, idx); RESET_ERROR_CODE(); expr_ref e = to_optimize_ptr(o)->get_upper(idx); mk_c(c)->save_ast_trail(e); RETURN_Z3(of_expr(e)); Z3_CATCH_RETURN(nullptr); } // get lower value or current approximation Z3_ast_vector Z3_API Z3_optimize_get_lower_as_vector(Z3_context c, Z3_optimize o, unsigned idx) { Z3_TRY; LOG_Z3_optimize_get_lower_as_vector(c, o, idx); RESET_ERROR_CODE(); expr_ref_vector es(mk_c(c)->m()); to_optimize_ptr(o)->get_lower(idx, es); Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, *mk_c(c), mk_c(c)->m()); mk_c(c)->save_object(v); v->m_ast_vector.append(es.size(), (ast*const*)es.c_ptr()); RETURN_Z3(of_ast_vector(v)); Z3_CATCH_RETURN(nullptr); } // get upper or current approximation Z3_ast_vector Z3_API Z3_optimize_get_upper_as_vector(Z3_context c, Z3_optimize o, unsigned idx) { Z3_TRY; LOG_Z3_optimize_get_upper_as_vector(c, o, idx); RESET_ERROR_CODE(); expr_ref_vector es(mk_c(c)->m()); to_optimize_ptr(o)->get_upper(idx, es); Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, *mk_c(c), mk_c(c)->m()); mk_c(c)->save_object(v); v->m_ast_vector.append(es.size(), (ast*const*)es.c_ptr()); RETURN_Z3(of_ast_vector(v)); Z3_CATCH_RETURN(nullptr); } Z3_string Z3_API Z3_optimize_to_string(Z3_context c, Z3_optimize o) { Z3_TRY; LOG_Z3_optimize_to_string(c, o); RESET_ERROR_CODE(); return mk_c(c)->mk_external_string(to_optimize_ptr(o)->to_string()); Z3_CATCH_RETURN(""); } Z3_string Z3_API Z3_optimize_get_help(Z3_context c, Z3_optimize d) { Z3_TRY; LOG_Z3_optimize_get_help(c, d); RESET_ERROR_CODE(); std::ostringstream buffer; param_descrs descrs; to_optimize_ptr(d)->collect_param_descrs(descrs); descrs.display(buffer); return mk_c(c)->mk_external_string(buffer.str()); Z3_CATCH_RETURN(""); } Z3_stats Z3_API Z3_optimize_get_statistics(Z3_context c,Z3_optimize d) { Z3_TRY; LOG_Z3_optimize_get_statistics(c, d); RESET_ERROR_CODE(); Z3_stats_ref * st = alloc(Z3_stats_ref, *mk_c(c)); to_optimize_ptr(d)->collect_statistics(st->m_stats); mk_c(c)->save_object(st); Z3_stats r = of_stats(st); RETURN_Z3(r); Z3_CATCH_RETURN(nullptr); } static void Z3_optimize_from_stream( Z3_context c, Z3_optimize opt, std::istream& s, char const* ext) { ast_manager& m = mk_c(c)->m(); if (ext && std::string("opb") == ext) { unsigned_vector h; parse_opb(*to_optimize_ptr(opt), s, h); return; } if (ext && std::string("wcnf") == ext) { unsigned_vector h; parse_wcnf(*to_optimize_ptr(opt), s, h); return; } if (ext && std::string("lp") == ext) { unsigned_vector h; parse_lp(*to_optimize_ptr(opt), s, h); return; } scoped_ptr ctx = alloc(cmd_context, false, &m); install_opt_cmds(*ctx.get(), to_optimize_ptr(opt)); std::stringstream errstrm; ctx->set_regular_stream(errstrm); ctx->set_ignore_check(true); try { if (!parse_smt2_commands(*ctx.get(), s)) { ctx = nullptr; SET_ERROR_CODE(Z3_PARSER_ERROR, errstrm.str().c_str()); return; } } catch (z3_exception& e) { errstrm << e.msg(); ctx = nullptr; SET_ERROR_CODE(Z3_PARSER_ERROR, errstrm.str().c_str()); return; } for (expr * e : ctx->assertions()) { to_optimize_ptr(opt)->add_hard_constraint(e); } } void Z3_API Z3_optimize_from_string( Z3_context c, Z3_optimize d, Z3_string s) { Z3_TRY; //LOG_Z3_optimize_from_string(c, d, s); std::string str(s); std::istringstream is(str); Z3_optimize_from_stream(c, d, is, nullptr); Z3_CATCH; } void Z3_API Z3_optimize_from_file( Z3_context c, Z3_optimize d, Z3_string s) { Z3_TRY; //LOG_Z3_optimize_from_file(c, d, s); std::ifstream is(s); if (!is) { std::ostringstream strm; strm << "Could not open file " << s; throw default_exception(strm.str()); } Z3_optimize_from_stream(c, d, is, get_extension(s)); Z3_CATCH; } Z3_ast_vector Z3_API Z3_optimize_get_assertions(Z3_context c, Z3_optimize o) { Z3_TRY; LOG_Z3_optimize_get_assertions(c, o); RESET_ERROR_CODE(); 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 hard(mk_c(c)->m()); to_optimize_ptr(o)->get_hard_constraints(hard); for (expr* h : hard) { v->m_ast_vector.push_back(h); } RETURN_Z3(of_ast_vector(v)); Z3_CATCH_RETURN(nullptr); } Z3_ast_vector Z3_API Z3_optimize_get_objectives(Z3_context c, Z3_optimize o) { Z3_TRY; LOG_Z3_optimize_get_objectives(c, o); RESET_ERROR_CODE(); unsigned n = to_optimize_ptr(o)->num_objectives(); Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, *mk_c(c), mk_c(c)->m()); mk_c(c)->save_object(v); for (unsigned i = 0; i < n; i++) { v->m_ast_vector.push_back(to_optimize_ptr(o)->get_objective(i)); } RETURN_Z3(of_ast_vector(v)); Z3_CATCH_RETURN(nullptr); } }; z3-z3-4.8.7/src/api/api_params.cpp000066400000000000000000000146131356505360400166140ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_params.cpp Abstract: API for creating parameter sets. This is essentially a wrapper for params_ref. Author: Leonardo de Moura (leonardo) 2012-03-05. Revision History: --*/ #include #include "api/z3.h" #include "api/api_log_macros.h" #include "api/api_context.h" #include "api/api_util.h" #include "util/params.h" extern "C" { Z3_params Z3_API Z3_mk_params(Z3_context c) { Z3_TRY; LOG_Z3_mk_params(c); RESET_ERROR_CODE(); Z3_params_ref * p = alloc(Z3_params_ref, *mk_c(c)); mk_c(c)->save_object(p); Z3_params r = of_params(p); RETURN_Z3(r); Z3_CATCH_RETURN(nullptr); } /** \brief Increment the reference counter of the given parameter set. */ void Z3_API Z3_params_inc_ref(Z3_context c, Z3_params p) { Z3_TRY; LOG_Z3_params_inc_ref(c, p); RESET_ERROR_CODE(); to_params(p)->inc_ref(); Z3_CATCH; } /** \brief Decrement the reference counter of the given parameter set. */ void Z3_API Z3_params_dec_ref(Z3_context c, Z3_params p) { Z3_TRY; LOG_Z3_params_dec_ref(c, p); RESET_ERROR_CODE(); to_params(p)->dec_ref(); Z3_CATCH; } /** \brief Add a Boolean parameter \c k with value \c v to the parameter set \c p. */ void Z3_API Z3_params_set_bool(Z3_context c, Z3_params p, Z3_symbol k, bool v) { 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); Z3_CATCH; } /** \brief Add a unsigned parameter \c k with value \c v to the parameter set \c p. */ void Z3_API Z3_params_set_uint(Z3_context c, Z3_params p, Z3_symbol k, unsigned v) { Z3_TRY; LOG_Z3_params_set_uint(c, p, k, v); RESET_ERROR_CODE(); to_params(p)->m_params.set_uint(norm_param_name(to_symbol(k)).c_str(), v); Z3_CATCH; } /** \brief Add a double parameter \c k with value \c v to the parameter set \c p. */ void Z3_API Z3_params_set_double(Z3_context c, Z3_params p, Z3_symbol k, double v) { Z3_TRY; LOG_Z3_params_set_double(c, p, k, v); RESET_ERROR_CODE(); to_params(p)->m_params.set_double(norm_param_name(to_symbol(k)).c_str(), v); Z3_CATCH; } /** \brief Add a symbol parameter \c k with value \c v to the parameter set \c p. */ void Z3_API Z3_params_set_symbol(Z3_context c, Z3_params p, Z3_symbol k, Z3_symbol v) { Z3_TRY; LOG_Z3_params_set_symbol(c, p, k, v); RESET_ERROR_CODE(); to_params(p)->m_params.set_sym(norm_param_name(to_symbol(k)).c_str(), to_symbol(v)); Z3_CATCH; } /** \brief Convert a parameter set into a string. This function is mainly used for printing the contents of a parameter set. */ Z3_string Z3_API Z3_params_to_string(Z3_context c, Z3_params p) { Z3_TRY; LOG_Z3_params_to_string(c, p); RESET_ERROR_CODE(); std::ostringstream buffer; to_params(p)->m_params.display(buffer); return mk_c(c)->mk_external_string(buffer.str()); Z3_CATCH_RETURN(""); } void Z3_API Z3_params_validate(Z3_context c, Z3_params p, Z3_param_descrs d) { Z3_TRY; LOG_Z3_params_validate(c, p, d); RESET_ERROR_CODE(); to_params(p)->m_params.validate(*to_param_descrs_ptr(d)); Z3_CATCH; } void Z3_API Z3_param_descrs_inc_ref(Z3_context c, Z3_param_descrs p) { Z3_TRY; LOG_Z3_param_descrs_inc_ref(c, p); RESET_ERROR_CODE(); to_param_descrs(p)->inc_ref(); Z3_CATCH; } void Z3_API Z3_param_descrs_dec_ref(Z3_context c, Z3_param_descrs p) { Z3_TRY; LOG_Z3_param_descrs_dec_ref(c, p); RESET_ERROR_CODE(); to_param_descrs(p)->dec_ref(); Z3_CATCH; } Z3_param_kind Z3_API Z3_param_descrs_get_kind(Z3_context c, Z3_param_descrs p, Z3_symbol n) { Z3_TRY; LOG_Z3_param_descrs_get_kind(c, p, n); RESET_ERROR_CODE(); param_kind k = to_param_descrs_ptr(p)->get_kind(to_symbol(n)); switch (k) { case CPK_UINT: return Z3_PK_UINT; case CPK_BOOL: return Z3_PK_BOOL; case CPK_DOUBLE: return Z3_PK_DOUBLE; case CPK_STRING: return Z3_PK_STRING; case CPK_SYMBOL: return Z3_PK_SYMBOL; case CPK_INVALID: return Z3_PK_INVALID; default: return Z3_PK_OTHER; } Z3_CATCH_RETURN(Z3_PK_INVALID); } unsigned Z3_API Z3_param_descrs_size(Z3_context c, Z3_param_descrs p) { Z3_TRY; LOG_Z3_param_descrs_size(c, p); RESET_ERROR_CODE(); return to_param_descrs_ptr(p)->size(); Z3_CATCH_RETURN(0); } Z3_symbol Z3_API Z3_param_descrs_get_name(Z3_context c, Z3_param_descrs p, unsigned i) { Z3_TRY; LOG_Z3_param_descrs_get_name(c, p, i); RESET_ERROR_CODE(); if (i >= to_param_descrs_ptr(p)->size()) { SET_ERROR_CODE(Z3_IOB, nullptr); RETURN_Z3(nullptr); } Z3_symbol result = of_symbol(to_param_descrs_ptr(p)->get_param_name(i)); return result; Z3_CATCH_RETURN(nullptr); } Z3_string Z3_API Z3_param_descrs_get_documentation(Z3_context c, Z3_param_descrs p, Z3_symbol s) { Z3_TRY; LOG_Z3_param_descrs_get_documentation(c, p, s); RESET_ERROR_CODE(); char const* result = to_param_descrs_ptr(p)->get_descr(to_symbol(s)); if (result == nullptr) { SET_ERROR_CODE(Z3_IOB, nullptr); RETURN_Z3(nullptr); } return mk_c(c)->mk_external_string(result); Z3_CATCH_RETURN(nullptr); } Z3_string Z3_API Z3_param_descrs_to_string(Z3_context c, Z3_param_descrs p) { Z3_TRY; LOG_Z3_param_descrs_to_string(c, p); RESET_ERROR_CODE(); std::ostringstream buffer; buffer << "("; unsigned sz = to_param_descrs_ptr(p)->size(); for (unsigned i = 0; i < sz; i++) { if (i > 0) buffer << ", "; buffer << to_param_descrs_ptr(p)->get_param_name(i); } buffer << ")"; return mk_c(c)->mk_external_string(buffer.str()); Z3_CATCH_RETURN(""); } }; z3-z3-4.8.7/src/api/api_parsers.cpp000066400000000000000000000145121356505360400170060ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_parsers.cpp Abstract: API for parsing different formats Author: Leonardo de Moura (leonardo) 2012-02-29. Revision History: --*/ #include #include "api/z3.h" #include "api/api_log_macros.h" #include "api/api_context.h" #include "api/api_util.h" #include "api/api_ast_vector.h" #include "cmd_context/cmd_context.h" #include "smt/smt_solver.h" #include "parsers/smt2/smt2parser.h" #include "solver/solver_na2as.h" extern "C" { // --------------- // Support for SMTLIB2 Z3_ast_vector parse_smtlib2_stream(bool exec, Z3_context c, std::istream& is, unsigned num_sorts, Z3_symbol const _sort_names[], Z3_sort const _sorts[], unsigned num_decls, Z3_symbol const decl_names[], Z3_func_decl const decls[]) { Z3_TRY; ast_manager& m = mk_c(c)->m(); scoped_ptr ctx = alloc(cmd_context, false, &(m)); ctx->set_ignore_check(true); Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, *mk_c(c), m); vector sort_names; ptr_vector sorts; for (unsigned i = 0; i < num_sorts; ++i) { sorts.push_back(to_sort(_sorts[i])); sort_names.push_back(to_symbol(_sort_names[i])); } mk_c(c)->save_object(v); for (unsigned i = 0; i < num_decls; ++i) { func_decl* d = to_func_decl(decls[i]); ctx->insert(to_symbol(decl_names[i]), d); sort_names.push_back(d->get_range()->get_name()); sorts.push_back(d->get_range()); for (sort* s : *d) { sort_names.push_back(s->get_name()); sorts.push_back(s); } } datatype_util dt(m); for (unsigned i = 0; i < num_sorts; ++i) { sort* srt = sorts[i]; symbol name = sort_names[i]; if (ctx->find_psort_decl(name)) { continue; } psort* ps = ctx->pm().mk_psort_cnst(srt); ctx->insert(ctx->pm().mk_psort_user_decl(0, name, ps)); if (!dt.is_datatype(srt)) { continue; } for (func_decl * c : *dt.get_datatype_constructors(srt)) { ctx->insert(c->get_name(), c); func_decl * r = dt.get_constructor_recognizer(c); ctx->insert(r->get_name(), r); for (func_decl * a : *dt.get_constructor_accessors(c)) { ctx->insert(a->get_name(), a); } } } std::stringstream errstrm; ctx->set_regular_stream(errstrm); try { if (!parse_smt2_commands(*ctx.get(), is)) { ctx = nullptr; SET_ERROR_CODE(Z3_PARSER_ERROR, errstrm.str().c_str()); return of_ast_vector(v); } } catch (z3_exception& e) { errstrm << e.msg(); ctx = nullptr; SET_ERROR_CODE(Z3_PARSER_ERROR, errstrm.str().c_str()); return of_ast_vector(v); } for (expr * e : ctx->assertions()) { v->m_ast_vector.push_back(e); } return of_ast_vector(v); Z3_CATCH_RETURN(nullptr); } Z3_ast_vector Z3_API Z3_parse_smtlib2_string(Z3_context c, Z3_string str, unsigned num_sorts, Z3_symbol const sort_names[], Z3_sort const sorts[], unsigned num_decls, Z3_symbol const decl_names[], Z3_func_decl const decls[]) { Z3_TRY; LOG_Z3_parse_smtlib2_string(c, str, num_sorts, sort_names, sorts, num_decls, decl_names, decls); std::string s(str); std::istringstream is(s); Z3_ast_vector r = parse_smtlib2_stream(false, c, is, num_sorts, sort_names, sorts, num_decls, decl_names, decls); RETURN_Z3(r); Z3_CATCH_RETURN(nullptr); } Z3_ast_vector Z3_API Z3_parse_smtlib2_file(Z3_context c, Z3_string file_name, unsigned num_sorts, Z3_symbol const sort_names[], Z3_sort const sorts[], unsigned num_decls, Z3_symbol const decl_names[], Z3_func_decl const decls[]) { Z3_TRY; LOG_Z3_parse_smtlib2_string(c, file_name, num_sorts, sort_names, sorts, num_decls, decl_names, decls); std::ifstream is(file_name); if (!is) { SET_ERROR_CODE(Z3_FILE_ACCESS_ERROR, nullptr); return nullptr; } Z3_ast_vector r = parse_smtlib2_stream(false, c, is, num_sorts, sort_names, sorts, num_decls, decl_names, decls); RETURN_Z3(r); Z3_CATCH_RETURN(nullptr); } Z3_string Z3_API Z3_eval_smtlib2_string(Z3_context c, Z3_string str) { std::stringstream ous; Z3_TRY; LOG_Z3_eval_smtlib2_string(c, str); if (!mk_c(c)->cmd()) { mk_c(c)->cmd() = alloc(cmd_context, false, &(mk_c(c)->m())); mk_c(c)->cmd()->set_solver_factory(mk_smt_strategic_solver_factory()); } scoped_ptr& ctx = mk_c(c)->cmd(); std::string s(str); std::istringstream is(s); ctx->set_regular_stream(ous); ctx->set_diagnostic_stream(ous); try { if (!parse_smt2_commands(*ctx.get(), is)) { SET_ERROR_CODE(Z3_PARSER_ERROR, ous.str().c_str()); RETURN_Z3(mk_c(c)->mk_external_string(ous.str())); } } catch (z3_exception& e) { if (ous.str().empty()) ous << e.msg(); SET_ERROR_CODE(Z3_PARSER_ERROR, ous.str().c_str()); RETURN_Z3(mk_c(c)->mk_external_string(ous.str())); } RETURN_Z3(mk_c(c)->mk_external_string(ous.str())); Z3_CATCH_RETURN(mk_c(c)->mk_external_string(ous.str())); } }; z3-z3-4.8.7/src/api/api_pb.cpp000066400000000000000000000064571356505360400157410ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: api_pb.cpp Abstract: API for pb theory Author: Nikolaj Bjorner (nbjorner) 2013-11-13. Revision History: --*/ #include "api/z3.h" #include "api/api_log_macros.h" #include "api/api_context.h" #include "api/api_util.h" #include "ast/pb_decl_plugin.h" extern "C" { Z3_ast Z3_API Z3_mk_atmost(Z3_context c, unsigned num_args, Z3_ast const args[], unsigned k) { Z3_TRY; LOG_Z3_mk_atmost(c, num_args, args, k); RESET_ERROR_CODE(); parameter param(k); pb_util util(mk_c(c)->m()); ast* a = util.mk_at_most_k(num_args, to_exprs(num_args, args), k); mk_c(c)->save_ast_trail(a); check_sorts(c, a); RETURN_Z3(of_ast(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_atleast(Z3_context c, unsigned num_args, Z3_ast const args[], unsigned k) { Z3_TRY; LOG_Z3_mk_atmost(c, num_args, args, k); RESET_ERROR_CODE(); parameter param(k); pb_util util(mk_c(c)->m()); ast* a = util.mk_at_least_k(num_args, to_exprs(num_args, args), k); mk_c(c)->save_ast_trail(a); check_sorts(c, a); RETURN_Z3(of_ast(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_pble(Z3_context c, unsigned num_args, Z3_ast const args[], int const _coeffs[], int k) { Z3_TRY; LOG_Z3_mk_pble(c, num_args, args, _coeffs, k); RESET_ERROR_CODE(); pb_util util(mk_c(c)->m()); vector coeffs; for (unsigned i = 0; i < num_args; ++i) { coeffs.push_back(rational(_coeffs[i])); } ast* a = util.mk_le(num_args, coeffs.c_ptr(), to_exprs(num_args, args), rational(k)); mk_c(c)->save_ast_trail(a); check_sorts(c, a); RETURN_Z3(of_ast(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_pbge(Z3_context c, unsigned num_args, Z3_ast const args[], int const _coeffs[], int k) { Z3_TRY; LOG_Z3_mk_pble(c, num_args, args, _coeffs, k); RESET_ERROR_CODE(); pb_util util(mk_c(c)->m()); vector coeffs; for (unsigned i = 0; i < num_args; ++i) { coeffs.push_back(rational(_coeffs[i])); } ast* a = util.mk_ge(num_args, coeffs.c_ptr(), to_exprs(num_args, args), rational(k)); mk_c(c)->save_ast_trail(a); check_sorts(c, a); RETURN_Z3(of_ast(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_pbeq(Z3_context c, unsigned num_args, Z3_ast const args[], int const _coeffs[], int k) { Z3_TRY; LOG_Z3_mk_pble(c, num_args, args, _coeffs, k); RESET_ERROR_CODE(); pb_util util(mk_c(c)->m()); vector coeffs; for (unsigned i = 0; i < num_args; ++i) { coeffs.push_back(rational(_coeffs[i])); } ast* a = util.mk_eq(num_args, coeffs.c_ptr(), to_exprs(num_args, args), rational(k)); mk_c(c)->save_ast_trail(a); check_sorts(c, a); RETURN_Z3(of_ast(a)); Z3_CATCH_RETURN(nullptr); } }; z3-z3-4.8.7/src/api/api_polynomial.cpp000066400000000000000000000040321356505360400175060ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_polynomial.cpp Abstract: Polynomial manager and caches for the external API. Author: Leonardo de Moura (leonardo) 2012-12-08 Notes: --*/ #include "api/z3.h" #include "api/api_log_macros.h" #include "api/api_context.h" #include "api/api_polynomial.h" #include "api/api_ast_vector.h" #include "ast/expr2polynomial.h" #include "util/cancel_eh.h" #include "util/scoped_timer.h" #include "ast/expr2var.h" extern "C" { Z3_ast_vector Z3_API Z3_polynomial_subresultants(Z3_context c, Z3_ast p, Z3_ast q, Z3_ast x) { Z3_TRY; LOG_Z3_polynomial_subresultants(c, p, q, x); RESET_ERROR_CODE(); polynomial::manager & pm = mk_c(c)->pm(); polynomial_ref _p(pm), _q(pm); polynomial::scoped_numeral d(pm.m()); default_expr2polynomial converter(mk_c(c)->m(), pm); if (!converter.to_polynomial(to_expr(p), _p, d) || !converter.to_polynomial(to_expr(q), _q, d)) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return nullptr; } Z3_ast_vector_ref* result = alloc(Z3_ast_vector_ref, *mk_c(c), mk_c(c)->m()); mk_c(c)->save_object(result); if (converter.is_var(to_expr(x))) { expr2var const & mapping = converter.get_mapping(); unsigned v_x = mapping.to_var(to_expr(x)); polynomial_ref_vector rs(pm); polynomial_ref r(pm); expr_ref _r(mk_c(c)->m()); { cancel_eh eh(mk_c(c)->poly_limit()); api::context::set_interruptable si(*(mk_c(c)), eh); scoped_timer timer(mk_c(c)->params().m_timeout, &eh); pm.psc_chain(_p, _q, v_x, rs); } for (unsigned i = 0; i < rs.size(); i++) { r = rs.get(i); converter.to_expr(r, true, _r); result->m_ast_vector.push_back(_r); } } RETURN_Z3(of_ast_vector(result)); Z3_CATCH_RETURN(nullptr); } }; z3-z3-4.8.7/src/api/api_polynomial.h000066400000000000000000000012331356505360400171530ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_polynomial.h Abstract: Polynomial manager and caches for the external API. Author: Leonardo de Moura (leonardo) 2012-12-08 Notes: --*/ #ifndef API_POLYNOMIAL_H_ #define API_POLYNOMIAL_H_ #include "math/polynomial/polynomial.h" namespace api { class pmanager final { unsynch_mpz_manager m_nm; polynomial::manager m_pm; // TODO: add support for caching expressions -> polynomial and back public: pmanager(reslimit& lim) : m_pm(lim, m_nm) {} ~pmanager() {} polynomial::manager & pm() { return m_pm; } }; }; #endif z3-z3-4.8.7/src/api/api_qe.cpp000066400000000000000000000111601356505360400157300ustar00rootroot00000000000000/*++ Copyright (c) Microsoft Corporation, Arie Gurfinkel 2017 Module Name: api_qe.cpp Abstract: Model-based Projection (MBP) and Quantifier Elimination (QE) API Author: Arie Gurfinkel (arie) Notes: --*/ #include #include "api/z3.h" #include "api/api_log_macros.h" #include "api/api_context.h" #include "api/api_util.h" #include "api/api_model.h" #include "api/api_ast_map.h" #include "api/api_ast_vector.h" #include "qe/qe_vartest.h" #include "qe/qe_lite.h" #include "muz/spacer/spacer_util.h" #include "ast/expr_map.h" extern "C" { static bool to_apps(unsigned n, Z3_app const es[], app_ref_vector& result) { for (unsigned i = 0; i < n; ++i) { if (!is_app(to_app(es[i]))) { return false; } result.push_back (to_app (es [i])); } return true; } Z3_ast Z3_API Z3_qe_model_project (Z3_context c, Z3_model m, unsigned num_bounds, Z3_app const bound[], Z3_ast body) { Z3_TRY; LOG_Z3_qe_model_project (c, m, num_bounds, bound, body); RESET_ERROR_CODE(); app_ref_vector vars(mk_c(c)->m ()); if (!to_apps(num_bounds, bound, vars)) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } expr_ref result (mk_c(c)->m ()); result = to_expr (body); model_ref model (to_model_ref (m)); spacer::qe_project (mk_c(c)->m (), vars, result, *model); mk_c(c)->save_ast_trail (result.get ()); return of_expr (result.get ()); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_qe_model_project_skolem (Z3_context c, Z3_model mdl, unsigned num_bounds, Z3_app const bound[], Z3_ast body, Z3_ast_map map) { Z3_TRY; LOG_Z3_qe_model_project_skolem (c, mdl, num_bounds, bound, body, map); RESET_ERROR_CODE(); ast_manager& m = mk_c(c)->m(); app_ref_vector vars(m); if (!to_apps(num_bounds, bound, vars)) { RETURN_Z3(nullptr); } expr_ref result (m); result = to_expr (body); model_ref model (to_model_ref (mdl)); expr_map emap (m); spacer::qe_project(m, vars, result, model, emap); mk_c(c)->save_ast_trail(result); obj_map &map_z3 = to_ast_map_ref(map); for (auto& kv : emap) { m.inc_ref(kv.m_key); m.inc_ref(kv.m_value); map_z3.insert(kv.m_key, kv.m_value); } return of_expr (result); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_model_extrapolate (Z3_context c, Z3_model m, Z3_ast fml) { Z3_TRY; LOG_Z3_model_extrapolate (c, m, fml); RESET_ERROR_CODE(); model_ref model (to_model_ref (m)); expr_ref_vector facts (mk_c(c)->m ()); facts.push_back (to_expr (fml)); flatten_and (facts); expr_ref_vector lits (mk_c(c)->m()); spacer::compute_implicant_literals (*model, facts, lits); expr_ref result (mk_c(c)->m ()); result = mk_and (lits); mk_c(c)->save_ast_trail (result); return of_expr (result); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_qe_lite (Z3_context c, Z3_ast_vector vars, Z3_ast body) { Z3_TRY; LOG_Z3_qe_lite (c, vars, body); RESET_ERROR_CODE(); ast_ref_vector &vVars = to_ast_vector_ref (vars); app_ref_vector vApps (mk_c(c)->m()); for (ast* v : vVars) { app * a = to_app(v); if (a->get_kind () != AST_APP) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } vApps.push_back (a); } expr_ref result (mk_c(c)->m ()); result = to_expr (body); params_ref p; qe_lite qe (mk_c(c)->m (), p); qe (vApps, result); // -- copy back variables that were not eliminated if (vApps.size () < vVars.size ()) { vVars.reset (); for (app* v : vApps) { vVars.push_back (v); } } mk_c(c)->save_ast_trail (result); return of_expr (result); Z3_CATCH_RETURN(nullptr); } } z3-z3-4.8.7/src/api/api_quant.cpp000066400000000000000000000467301356505360400164660ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_quant.cpp Abstract: API for quantifiers Author: Leonardo de Moura (leonardo) 2012-02-29. Revision History: --*/ #include "api/z3.h" #include "api/api_log_macros.h" #include "api/api_context.h" #include "api/api_util.h" #include "parsers/util/pattern_validation.h" #include "ast/expr_abstract.h" extern "C" { Z3_ast Z3_API Z3_mk_quantifier( Z3_context c, bool is_forall, unsigned weight, unsigned num_patterns, Z3_pattern const patterns[], unsigned num_decls, Z3_sort const sorts[], Z3_symbol const decl_names[], Z3_ast body) { return Z3_mk_quantifier_ex( c, is_forall, weight, nullptr, nullptr, num_patterns, patterns, 0, nullptr, num_decls, sorts, decl_names, body ); } Z3_ast mk_quantifier_ex_core( Z3_context c, bool is_forall, unsigned weight, Z3_symbol quantifier_id, Z3_symbol skolem_id, unsigned num_patterns, Z3_pattern const patterns[], unsigned num_no_patterns, Z3_ast const no_patterns[], unsigned num_decls, Z3_sort const sorts[], Z3_symbol const decl_names[], Z3_ast body) { Z3_TRY; RESET_ERROR_CODE(); if (!mk_c(c)->m().is_bool(to_expr(body))) { SET_ERROR_CODE(Z3_SORT_ERROR, nullptr); return nullptr; } if (num_patterns > 0 && num_no_patterns > 0) { SET_ERROR_CODE(Z3_INVALID_USAGE, nullptr); return nullptr; } expr * const* ps = reinterpret_cast(patterns); expr * const* no_ps = reinterpret_cast(no_patterns); symbol qid = to_symbol(quantifier_id); bool is_rec = mk_c(c)->m().rec_fun_qid() == qid; if (!is_rec) { pattern_validator v(mk_c(c)->m()); for (unsigned i = 0; i < num_patterns; i++) { if (!v(num_decls, ps[i], 0, 0)) { SET_ERROR_CODE(Z3_INVALID_PATTERN, nullptr); return nullptr; } } } sort* const* ts = reinterpret_cast(sorts); svector names; for (unsigned i = 0; i < num_decls; ++i) { names.push_back(to_symbol(decl_names[i])); } expr_ref result(mk_c(c)->m()); if (num_decls > 0) { result = mk_c(c)->m().mk_quantifier( is_forall ? forall_k : exists_k, names.size(), ts, names.c_ptr(), to_expr(body), weight, qid, to_symbol(skolem_id), num_patterns, ps, num_no_patterns, no_ps ); } else { result = to_expr(body); } mk_c(c)->save_ast_trail(result.get()); return of_ast(result.get()); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_quantifier_ex( Z3_context c, bool is_forall, unsigned weight, Z3_symbol quantifier_id, Z3_symbol skolem_id, unsigned num_patterns, Z3_pattern const patterns[], unsigned num_no_patterns, Z3_ast const no_patterns[], unsigned num_decls, Z3_sort const sorts[], Z3_symbol const decl_names[], Z3_ast body) { LOG_Z3_mk_quantifier_ex(c, is_forall, weight, quantifier_id, skolem_id, num_patterns, patterns, num_no_patterns, no_patterns, num_decls, sorts, decl_names, body); Z3_ast r = mk_quantifier_ex_core(c, is_forall, weight, quantifier_id, skolem_id, num_patterns, patterns, num_no_patterns, no_patterns, num_decls, sorts, decl_names, body); RETURN_Z3(r); } Z3_ast Z3_API Z3_mk_forall(Z3_context c, unsigned weight, unsigned num_patterns, Z3_pattern const patterns[], unsigned num_decls, Z3_sort const types[], Z3_symbol const decl_names[], Z3_ast body) { return Z3_mk_quantifier(c, true, weight, num_patterns, patterns, num_decls, types, decl_names, body); } Z3_ast Z3_API Z3_mk_exists(Z3_context c, unsigned weight, unsigned num_patterns, Z3_pattern const patterns[], unsigned num_decls, Z3_sort const types[], Z3_symbol const decl_names[], Z3_ast body) { return Z3_mk_quantifier(c, false, weight, num_patterns, patterns, num_decls, types, decl_names, body); } Z3_ast Z3_API Z3_mk_lambda(Z3_context c, unsigned num_decls, Z3_sort const types[], Z3_symbol const decl_names[], Z3_ast body) { Z3_TRY; LOG_Z3_mk_lambda(c, num_decls, types, decl_names, body); RESET_ERROR_CODE(); expr_ref result(mk_c(c)->m()); if (num_decls == 0) { SET_ERROR_CODE(Z3_INVALID_USAGE, nullptr); RETURN_Z3(nullptr); } sort* const* ts = reinterpret_cast(types); svector names; for (unsigned i = 0; i < num_decls; ++i) { names.push_back(to_symbol(decl_names[i])); } result = mk_c(c)->m().mk_lambda(names.size(), ts, names.c_ptr(), to_expr(body)); mk_c(c)->save_ast_trail(result.get()); return of_ast(result.get()); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_lambda_const(Z3_context c, unsigned num_decls, Z3_app const vars[], Z3_ast body) { Z3_TRY; LOG_Z3_mk_lambda_const(c, num_decls, vars, body); RESET_ERROR_CODE(); if (num_decls == 0) { SET_ERROR_CODE(Z3_INVALID_USAGE, nullptr); RETURN_Z3(nullptr); } svector _names; ptr_vector _vars; ptr_vector _args; for (unsigned i = 0; i < num_decls; ++i) { app* a = to_app(vars[i]); _names.push_back(to_app(a)->get_decl()->get_name()); _args.push_back(a); _vars.push_back(mk_c(c)->m().get_sort(a)); } expr_ref result(mk_c(c)->m()); expr_abstract(mk_c(c)->m(), 0, num_decls, _args.c_ptr(), to_expr(body), result); result = mk_c(c)->m().mk_lambda(_vars.size(), _vars.c_ptr(), _names.c_ptr(), result); mk_c(c)->save_ast_trail(result.get()); return of_ast(result.get()); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_quantifier_const_ex(Z3_context c, bool is_forall, unsigned weight, Z3_symbol quantifier_id, Z3_symbol skolem_id, unsigned num_bound, Z3_app const bound[], unsigned num_patterns, Z3_pattern const patterns[], unsigned num_no_patterns, Z3_ast const no_patterns[], Z3_ast body) { Z3_TRY; LOG_Z3_mk_quantifier_const_ex(c, is_forall, weight, quantifier_id, skolem_id, num_bound, bound, num_patterns, patterns, num_no_patterns, no_patterns, body); RESET_ERROR_CODE(); svector names; svector types; ptr_vector bound_asts; if (num_patterns > 0 && num_no_patterns > 0) { SET_ERROR_CODE(Z3_INVALID_USAGE, nullptr); RETURN_Z3(nullptr); } if (num_bound == 0) { SET_ERROR_CODE(Z3_INVALID_USAGE, "number of bound variables is 0"); RETURN_Z3(nullptr); } for (unsigned i = 0; i < num_bound; ++i) { app* a = to_app(bound[i]); if (a->get_kind() != AST_APP) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } symbol s(to_app(a)->get_decl()->get_name()); names.push_back(of_symbol(s)); types.push_back(of_sort(mk_c(c)->m().get_sort(a))); bound_asts.push_back(a); if (a->get_family_id() != null_family_id || a->get_num_args() != 0) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } } // Abstract patterns svector _patterns; expr_ref_vector pinned(mk_c(c)->m()); for (unsigned i = 0; i < num_patterns; ++i) { expr_ref result(mk_c(c)->m()); app* pat = to_pattern(patterns[i]); SASSERT(mk_c(c)->m().is_pattern(pat)); expr_abstract(mk_c(c)->m(), 0, num_bound, bound_asts.c_ptr(), pat, result); SASSERT(result.get()->get_kind() == AST_APP); pinned.push_back(result.get()); SASSERT(mk_c(c)->m().is_pattern(result.get())); _patterns.push_back(of_pattern(result.get())); } svector _no_patterns; for (unsigned i = 0; i < num_no_patterns; ++i) { expr_ref result(mk_c(c)->m()); if (!is_app(to_expr(no_patterns[i]))) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } app* pat = to_app(to_expr(no_patterns[i])); expr_abstract(mk_c(c)->m(), 0, num_bound, bound_asts.c_ptr(), pat, result); SASSERT(result.get()->get_kind() == AST_APP); pinned.push_back(result.get()); _no_patterns.push_back(of_ast(result.get())); } expr_ref abs_body(mk_c(c)->m()); expr_abstract(mk_c(c)->m(), 0, num_bound, bound_asts.c_ptr(), to_expr(body), abs_body); Z3_ast result = mk_quantifier_ex_core(c, is_forall, weight, quantifier_id, skolem_id, num_patterns, _patterns.c_ptr(), num_no_patterns, _no_patterns.c_ptr(), names.size(), types.c_ptr(), names.c_ptr(), of_ast(abs_body.get())); RETURN_Z3(result); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_quantifier_const(Z3_context c, bool is_forall, unsigned weight, unsigned num_bound, Z3_app const bound[], unsigned num_patterns, Z3_pattern const patterns[], Z3_ast body) { return Z3_mk_quantifier_const_ex(c, is_forall, weight, nullptr, nullptr, num_bound, bound, num_patterns, patterns, 0, nullptr, body); } Z3_ast Z3_API Z3_mk_forall_const(Z3_context c, unsigned weight, unsigned num_bound, Z3_app const bound[], unsigned num_patterns, Z3_pattern const patterns[], Z3_ast body) { return Z3_mk_quantifier_const(c, true, weight, num_bound, bound, num_patterns, patterns, body); } Z3_ast Z3_API Z3_mk_exists_const(Z3_context c, unsigned weight, unsigned num_bound, Z3_app const bound[], unsigned num_patterns, Z3_pattern const patterns[], Z3_ast body) { return Z3_mk_quantifier_const(c, false, weight, num_bound, bound, num_patterns, patterns, body); } Z3_pattern Z3_API Z3_mk_pattern(Z3_context c, unsigned num_patterns, Z3_ast const terms[]) { Z3_TRY; LOG_Z3_mk_pattern(c, num_patterns, terms); RESET_ERROR_CODE(); for (unsigned i = 0; i < num_patterns; ++i) { if (!is_app(to_expr(terms[i]))) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } } app* a = mk_c(c)->m().mk_pattern(num_patterns, reinterpret_cast(to_exprs(num_patterns, terms))); mk_c(c)->save_ast_trail(a); RETURN_Z3(of_pattern(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_bound(Z3_context c, unsigned index, Z3_sort ty) { Z3_TRY; LOG_Z3_mk_bound(c, index, ty); RESET_ERROR_CODE(); ast* a = mk_c(c)->m().mk_var(index, to_sort(ty)); mk_c(c)->save_ast_trail(a); RETURN_Z3(of_ast(a)); Z3_CATCH_RETURN(nullptr); } bool Z3_API Z3_is_quantifier_forall(Z3_context c, Z3_ast a) { Z3_TRY; LOG_Z3_is_quantifier_forall(c, a); RESET_ERROR_CODE(); return ::is_forall(to_ast(a)); Z3_CATCH_RETURN(false); } bool Z3_API Z3_is_quantifier_exists(Z3_context c, Z3_ast a) { Z3_TRY; LOG_Z3_is_quantifier_exists(c, a); RESET_ERROR_CODE(); return ::is_exists(to_ast(a)); Z3_CATCH_RETURN(false); } bool Z3_API Z3_is_lambda(Z3_context c, Z3_ast a) { Z3_TRY; LOG_Z3_is_lambda(c, a); RESET_ERROR_CODE(); return ::is_lambda(to_ast(a)); Z3_CATCH_RETURN(false); } unsigned Z3_API Z3_get_quantifier_weight(Z3_context c, Z3_ast a) { Z3_TRY; LOG_Z3_get_quantifier_weight(c, a); RESET_ERROR_CODE(); ast * _a = to_ast(a); if (_a->get_kind() == AST_QUANTIFIER) { return to_quantifier(_a)->get_weight(); } else { SET_ERROR_CODE(Z3_SORT_ERROR, nullptr); return 0; } Z3_CATCH_RETURN(0); } unsigned Z3_API Z3_get_quantifier_num_patterns(Z3_context c, Z3_ast a) { Z3_TRY; LOG_Z3_get_quantifier_num_patterns(c, a); RESET_ERROR_CODE(); ast * _a = to_ast(a); if (_a->get_kind() == AST_QUANTIFIER) { return to_quantifier(_a)->get_num_patterns(); } else { SET_ERROR_CODE(Z3_SORT_ERROR, nullptr); return 0; } Z3_CATCH_RETURN(0); } Z3_pattern Z3_API Z3_get_quantifier_pattern_ast(Z3_context c, Z3_ast a, unsigned i) { Z3_TRY; LOG_Z3_get_quantifier_pattern_ast(c, a, i); RESET_ERROR_CODE(); ast * _a = to_ast(a); if (_a->get_kind() == AST_QUANTIFIER) { Z3_pattern r = of_pattern(to_quantifier(_a)->get_patterns()[i]); RETURN_Z3(r); } else { SET_ERROR_CODE(Z3_SORT_ERROR, nullptr); RETURN_Z3(nullptr); } Z3_CATCH_RETURN(nullptr); } unsigned Z3_API Z3_get_quantifier_num_no_patterns(Z3_context c, Z3_ast a) { Z3_TRY; LOG_Z3_get_quantifier_num_no_patterns(c, a); RESET_ERROR_CODE(); ast * _a = to_ast(a); if (_a->get_kind() == AST_QUANTIFIER) { return to_quantifier(_a)->get_num_no_patterns(); } else { SET_ERROR_CODE(Z3_SORT_ERROR, nullptr); return 0; } Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_get_quantifier_no_pattern_ast(Z3_context c, Z3_ast a, unsigned i) { Z3_TRY; LOG_Z3_get_quantifier_no_pattern_ast(c, a, i); RESET_ERROR_CODE(); ast * _a = to_ast(a); if (_a->get_kind() == AST_QUANTIFIER) { Z3_ast r = of_ast(to_quantifier(_a)->get_no_pattern(i)); RETURN_Z3(r); } else { SET_ERROR_CODE(Z3_SORT_ERROR, nullptr); RETURN_Z3(nullptr); } Z3_CATCH_RETURN(nullptr); } Z3_symbol Z3_API Z3_get_quantifier_bound_name(Z3_context c, Z3_ast a, unsigned i) { Z3_TRY; LOG_Z3_get_quantifier_bound_name(c, a, i); RESET_ERROR_CODE(); ast * _a = to_ast(a); if (_a->get_kind() == AST_QUANTIFIER) { return of_symbol(to_quantifier(_a)->get_decl_names()[i]); } else { SET_ERROR_CODE(Z3_SORT_ERROR, nullptr); return nullptr; } Z3_CATCH_RETURN(nullptr); } Z3_sort Z3_API Z3_get_quantifier_bound_sort(Z3_context c, Z3_ast a, unsigned i) { Z3_TRY; LOG_Z3_get_quantifier_bound_sort(c, a, i); RESET_ERROR_CODE(); ast * _a = to_ast(a); if (_a->get_kind() == AST_QUANTIFIER) { Z3_sort r = of_sort(to_quantifier(_a)->get_decl_sort(i)); RETURN_Z3(r); } else { SET_ERROR_CODE(Z3_SORT_ERROR, nullptr); RETURN_Z3(nullptr); } Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_get_quantifier_body(Z3_context c, Z3_ast a) { Z3_TRY; LOG_Z3_get_quantifier_body(c, a); RESET_ERROR_CODE(); ast * _a = to_ast(a); if (_a->get_kind() == AST_QUANTIFIER) { Z3_ast r = of_ast(to_quantifier(_a)->get_expr()); RETURN_Z3(r); } else { SET_ERROR_CODE(Z3_SORT_ERROR, nullptr); RETURN_Z3(nullptr); } Z3_CATCH_RETURN(nullptr); } unsigned Z3_API Z3_get_quantifier_num_bound(Z3_context c, Z3_ast a) { Z3_TRY; LOG_Z3_get_quantifier_num_bound(c, a); RESET_ERROR_CODE(); ast * _a = to_ast(a); if (_a->get_kind() == AST_QUANTIFIER) { return to_quantifier(_a)->get_num_decls(); } else { SET_ERROR_CODE(Z3_SORT_ERROR, nullptr); return 0; } Z3_CATCH_RETURN(0); } unsigned Z3_API Z3_get_pattern_num_terms(Z3_context c, Z3_pattern p) { Z3_TRY; LOG_Z3_get_pattern_num_terms(c, p); RESET_ERROR_CODE(); app* _p = to_pattern(p); if (mk_c(c)->m().is_pattern(_p)) { return _p->get_num_args(); } else { SET_ERROR_CODE(Z3_SORT_ERROR, nullptr); return 0; } Z3_CATCH_RETURN(0); } Z3_ast Z3_API Z3_get_pattern(Z3_context c, Z3_pattern p, unsigned idx) { Z3_TRY; LOG_Z3_get_pattern(c, p, idx); RESET_ERROR_CODE(); app* _p = to_pattern(p); if (mk_c(c)->m().is_pattern(_p)) { Z3_ast r = of_ast(_p->get_arg(idx)); RETURN_Z3(r); } else { SET_ERROR_CODE(Z3_SORT_ERROR, nullptr); RETURN_Z3(nullptr); } Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_pattern_to_ast(Z3_context c, Z3_pattern p) { RESET_ERROR_CODE(); return (Z3_ast)(p); } Z3_API char const * Z3_pattern_to_string(Z3_context c, Z3_pattern p) { return Z3_ast_to_string(c, reinterpret_cast(p)); } }; z3-z3-4.8.7/src/api/api_rcf.cpp000066400000000000000000000211551356505360400161020ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: api_rcf.cpp Abstract: Additional APIs for handling elements of the Z3 real closed field that contains: - transcendental extensions - infinitesimal extensions - algebraic extensions Author: Leonardo de Moura (leonardo) 2012-01-05 Notes: --*/ #include #include "api/z3.h" #include "api/api_log_macros.h" #include "api/api_context.h" #include "math/realclosure/realclosure.h" static rcmanager & rcfm(Z3_context c) { return mk_c(c)->rcfm(); } static void reset_rcf_cancel(Z3_context c) { // no-op } static Z3_rcf_num from_rcnumeral(rcnumeral a) { return reinterpret_cast(a.c_ptr()); } static rcnumeral to_rcnumeral(Z3_rcf_num a) { return rcnumeral::mk(a); } extern "C" { void Z3_API Z3_rcf_del(Z3_context c, Z3_rcf_num a) { Z3_TRY; LOG_Z3_rcf_del(c, a); RESET_ERROR_CODE(); rcnumeral _a = to_rcnumeral(a); rcfm(c).del(_a); Z3_CATCH; } Z3_rcf_num Z3_API Z3_rcf_mk_rational(Z3_context c, Z3_string val) { Z3_TRY; LOG_Z3_rcf_mk_rational(c, val); RESET_ERROR_CODE(); reset_rcf_cancel(c); scoped_mpq q(rcfm(c).qm()); rcfm(c).qm().set(q, val); rcnumeral r; rcfm(c).set(r, q); RETURN_Z3(from_rcnumeral(r)); Z3_CATCH_RETURN(nullptr); } Z3_rcf_num Z3_API Z3_rcf_mk_small_int(Z3_context c, int val) { Z3_TRY; LOG_Z3_rcf_mk_small_int(c, val); RESET_ERROR_CODE(); reset_rcf_cancel(c); rcnumeral r; rcfm(c).set(r, val); RETURN_Z3(from_rcnumeral(r)); Z3_CATCH_RETURN(nullptr); } Z3_rcf_num Z3_API Z3_rcf_mk_pi(Z3_context c) { Z3_TRY; LOG_Z3_rcf_mk_pi(c); RESET_ERROR_CODE(); reset_rcf_cancel(c); rcnumeral r; rcfm(c).mk_pi(r); RETURN_Z3(from_rcnumeral(r)); Z3_CATCH_RETURN(nullptr); } Z3_rcf_num Z3_API Z3_rcf_mk_e(Z3_context c) { Z3_TRY; LOG_Z3_rcf_mk_e(c); RESET_ERROR_CODE(); reset_rcf_cancel(c); rcnumeral r; rcfm(c).mk_e(r); RETURN_Z3(from_rcnumeral(r)); Z3_CATCH_RETURN(nullptr); } Z3_rcf_num Z3_API Z3_rcf_mk_infinitesimal(Z3_context c) { Z3_TRY; LOG_Z3_rcf_mk_infinitesimal(c); RESET_ERROR_CODE(); reset_rcf_cancel(c); rcnumeral r; rcfm(c).mk_infinitesimal(r); RETURN_Z3(from_rcnumeral(r)); Z3_CATCH_RETURN(nullptr); } unsigned Z3_API Z3_rcf_mk_roots(Z3_context c, unsigned n, Z3_rcf_num const a[], Z3_rcf_num roots[]) { Z3_TRY; LOG_Z3_rcf_mk_roots(c, n, a, roots); RESET_ERROR_CODE(); reset_rcf_cancel(c); rcnumeral_vector av; unsigned rz = 0; for (unsigned i = 0; i < n; i++) { if (!rcfm(c).is_zero(to_rcnumeral(a[i]))) rz = i + 1; av.push_back(to_rcnumeral(a[i])); } if (rz == 0) { // it is the zero polynomial SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return 0; } av.shrink(rz); rcnumeral_vector rs; rcfm(c).isolate_roots(av.size(), av.c_ptr(), rs); unsigned num_roots = rs.size(); for (unsigned i = 0; i < num_roots; i++) { roots[i] = from_rcnumeral(rs[i]); } RETURN_Z3_rcf_mk_roots num_roots; Z3_CATCH_RETURN(0); } Z3_rcf_num Z3_API Z3_rcf_add(Z3_context c, Z3_rcf_num a, Z3_rcf_num b) { Z3_TRY; LOG_Z3_rcf_add(c, a, b); RESET_ERROR_CODE(); reset_rcf_cancel(c); rcnumeral r; rcfm(c).add(to_rcnumeral(a), to_rcnumeral(b), r); RETURN_Z3(from_rcnumeral(r)); Z3_CATCH_RETURN(nullptr); } Z3_rcf_num Z3_API Z3_rcf_sub(Z3_context c, Z3_rcf_num a, Z3_rcf_num b) { Z3_TRY; LOG_Z3_rcf_sub(c, a, b); RESET_ERROR_CODE(); reset_rcf_cancel(c); rcnumeral r; rcfm(c).sub(to_rcnumeral(a), to_rcnumeral(b), r); RETURN_Z3(from_rcnumeral(r)); Z3_CATCH_RETURN(nullptr); } Z3_rcf_num Z3_API Z3_rcf_mul(Z3_context c, Z3_rcf_num a, Z3_rcf_num b) { Z3_TRY; LOG_Z3_rcf_mul(c, a, b); RESET_ERROR_CODE(); reset_rcf_cancel(c); rcnumeral r; rcfm(c).mul(to_rcnumeral(a), to_rcnumeral(b), r); RETURN_Z3(from_rcnumeral(r)); Z3_CATCH_RETURN(nullptr); } Z3_rcf_num Z3_API Z3_rcf_div(Z3_context c, Z3_rcf_num a, Z3_rcf_num b) { Z3_TRY; LOG_Z3_rcf_div(c, a, b); RESET_ERROR_CODE(); reset_rcf_cancel(c); rcnumeral r; rcfm(c).div(to_rcnumeral(a), to_rcnumeral(b), r); RETURN_Z3(from_rcnumeral(r)); Z3_CATCH_RETURN(nullptr); } Z3_rcf_num Z3_API Z3_rcf_neg(Z3_context c, Z3_rcf_num a) { Z3_TRY; LOG_Z3_rcf_neg(c, a); RESET_ERROR_CODE(); reset_rcf_cancel(c); rcnumeral r; rcfm(c).neg(to_rcnumeral(a), r); RETURN_Z3(from_rcnumeral(r)); Z3_CATCH_RETURN(nullptr); } Z3_rcf_num Z3_API Z3_rcf_inv(Z3_context c, Z3_rcf_num a) { Z3_TRY; LOG_Z3_rcf_inv(c, a); RESET_ERROR_CODE(); reset_rcf_cancel(c); rcnumeral r; rcfm(c).inv(to_rcnumeral(a), r); RETURN_Z3(from_rcnumeral(r)); Z3_CATCH_RETURN(nullptr); } Z3_rcf_num Z3_API Z3_rcf_power(Z3_context c, Z3_rcf_num a, unsigned k) { Z3_TRY; LOG_Z3_rcf_power(c, a, k); RESET_ERROR_CODE(); reset_rcf_cancel(c); rcnumeral r; rcfm(c).power(to_rcnumeral(a), k, r); RETURN_Z3(from_rcnumeral(r)); Z3_CATCH_RETURN(nullptr); } bool Z3_API Z3_rcf_lt(Z3_context c, Z3_rcf_num a, Z3_rcf_num b) { Z3_TRY; LOG_Z3_rcf_lt(c, a, b); RESET_ERROR_CODE(); reset_rcf_cancel(c); return rcfm(c).lt(to_rcnumeral(a), to_rcnumeral(b)); Z3_CATCH_RETURN(false); } bool Z3_API Z3_rcf_gt(Z3_context c, Z3_rcf_num a, Z3_rcf_num b) { Z3_TRY; LOG_Z3_rcf_gt(c, a, b); RESET_ERROR_CODE(); reset_rcf_cancel(c); return rcfm(c).gt(to_rcnumeral(a), to_rcnumeral(b)); Z3_CATCH_RETURN(false); } bool Z3_API Z3_rcf_le(Z3_context c, Z3_rcf_num a, Z3_rcf_num b) { Z3_TRY; LOG_Z3_rcf_le(c, a, b); RESET_ERROR_CODE(); reset_rcf_cancel(c); return rcfm(c).le(to_rcnumeral(a), to_rcnumeral(b)); Z3_CATCH_RETURN(false); } bool Z3_API Z3_rcf_ge(Z3_context c, Z3_rcf_num a, Z3_rcf_num b) { Z3_TRY; LOG_Z3_rcf_ge(c, a, b); RESET_ERROR_CODE(); reset_rcf_cancel(c); return rcfm(c).ge(to_rcnumeral(a), to_rcnumeral(b)); Z3_CATCH_RETURN(false); } bool Z3_API Z3_rcf_eq(Z3_context c, Z3_rcf_num a, Z3_rcf_num b) { Z3_TRY; LOG_Z3_rcf_eq(c, a, b); RESET_ERROR_CODE(); reset_rcf_cancel(c); return rcfm(c).eq(to_rcnumeral(a), to_rcnumeral(b)); Z3_CATCH_RETURN(false); } bool Z3_API Z3_rcf_neq(Z3_context c, Z3_rcf_num a, Z3_rcf_num b) { Z3_TRY; LOG_Z3_rcf_neq(c, a, b); RESET_ERROR_CODE(); reset_rcf_cancel(c); return rcfm(c).neq(to_rcnumeral(a), to_rcnumeral(b)); Z3_CATCH_RETURN(false); } Z3_string Z3_API Z3_rcf_num_to_string(Z3_context c, Z3_rcf_num a, bool compact, bool html) { Z3_TRY; LOG_Z3_rcf_num_to_string(c, a, compact, html); RESET_ERROR_CODE(); reset_rcf_cancel(c); std::ostringstream buffer; rcfm(c).display(buffer, to_rcnumeral(a), compact, html); return mk_c(c)->mk_external_string(buffer.str()); Z3_CATCH_RETURN(""); } Z3_string Z3_API Z3_rcf_num_to_decimal_string(Z3_context c, Z3_rcf_num a, unsigned prec) { Z3_TRY; LOG_Z3_rcf_num_to_decimal_string(c, a, prec); RESET_ERROR_CODE(); reset_rcf_cancel(c); std::ostringstream buffer; rcfm(c).display_decimal(buffer, to_rcnumeral(a), prec); return mk_c(c)->mk_external_string(buffer.str()); Z3_CATCH_RETURN(""); } void Z3_API Z3_rcf_get_numerator_denominator(Z3_context c, Z3_rcf_num a, Z3_rcf_num * n, Z3_rcf_num * d) { Z3_TRY; LOG_Z3_rcf_get_numerator_denominator(c, a, n, d); RESET_ERROR_CODE(); reset_rcf_cancel(c); rcnumeral _n, _d; rcfm(c).clean_denominators(to_rcnumeral(a), _n, _d); *n = from_rcnumeral(_n); *d = from_rcnumeral(_d); RETURN_Z3_rcf_get_numerator_denominator; Z3_CATCH; } }; z3-z3-4.8.7/src/api/api_seq.cpp000066400000000000000000000177261356505360400161310ustar00rootroot00000000000000/*++ Copyright (c) 2016 Microsoft Corporation Module Name: api_seq.cpp Abstract: API for sequences and regular expressions. Author: Nikolaj Bjorner (nbjorner) 2016-01-02. Revision History: --*/ #include "api/z3.h" #include "api/api_log_macros.h" #include "api/api_context.h" #include "api/api_util.h" #include "ast/ast_pp.h" extern "C" { Z3_sort Z3_API Z3_mk_seq_sort(Z3_context c, Z3_sort domain) { Z3_TRY; LOG_Z3_mk_seq_sort(c, domain); RESET_ERROR_CODE(); sort * ty = mk_c(c)->sutil().str.mk_seq(to_sort(domain)); mk_c(c)->save_ast_trail(ty); RETURN_Z3(of_sort(ty)); Z3_CATCH_RETURN(nullptr); } Z3_sort Z3_API Z3_mk_re_sort(Z3_context c, Z3_sort domain) { Z3_TRY; LOG_Z3_mk_re_sort(c, domain); RESET_ERROR_CODE(); sort * ty = mk_c(c)->sutil().re.mk_re(to_sort(domain)); mk_c(c)->save_ast_trail(ty); RETURN_Z3(of_sort(ty)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_string(Z3_context c, Z3_string str) { Z3_TRY; LOG_Z3_mk_string(c, str); RESET_ERROR_CODE(); zstring s(str, zstring::ascii); app* a = mk_c(c)->sutil().str.mk_string(s); mk_c(c)->save_ast_trail(a); RETURN_Z3(of_ast(a)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_lstring(Z3_context c, unsigned sz, Z3_string str) { Z3_TRY; LOG_Z3_mk_string(c, str); RESET_ERROR_CODE(); unsigned_vector chs; for (unsigned i = 0; i < sz; ++i) chs.push_back(str[i]); zstring s(sz, chs.c_ptr(), zstring::ascii); app* a = mk_c(c)->sutil().str.mk_string(s); mk_c(c)->save_ast_trail(a); RETURN_Z3(of_ast(a)); Z3_CATCH_RETURN(nullptr); } Z3_sort Z3_API Z3_mk_string_sort(Z3_context c) { Z3_TRY; LOG_Z3_mk_string_sort(c); RESET_ERROR_CODE(); sort* ty = mk_c(c)->sutil().str.mk_string_sort(); mk_c(c)->save_ast_trail(ty); RETURN_Z3(of_sort(ty)); Z3_CATCH_RETURN(nullptr); } bool Z3_API Z3_is_seq_sort(Z3_context c, Z3_sort s) { Z3_TRY; LOG_Z3_is_seq_sort(c, s); RESET_ERROR_CODE(); return mk_c(c)->sutil().is_seq(to_sort(s)); Z3_CATCH_RETURN(false); } bool Z3_API Z3_is_re_sort(Z3_context c, Z3_sort s) { Z3_TRY; LOG_Z3_is_re_sort(c, s); RESET_ERROR_CODE(); return mk_c(c)->sutil().is_re(to_sort(s)); Z3_CATCH_RETURN(false); } Z3_sort Z3_API Z3_get_seq_sort_basis(Z3_context c, Z3_sort s) { Z3_TRY; LOG_Z3_get_seq_sort_basis(c, s); RESET_ERROR_CODE(); sort* r = nullptr; if (!mk_c(c)->sutil().is_seq(to_sort(s), r)) { SET_ERROR_CODE(Z3_INVALID_ARG, "expected sequence sort"); RETURN_Z3(nullptr); } RETURN_Z3(of_sort(r)); Z3_CATCH_RETURN(nullptr); } Z3_sort Z3_API Z3_get_re_sort_basis(Z3_context c, Z3_sort s) { Z3_TRY; LOG_Z3_get_re_sort_basis(c, s); RESET_ERROR_CODE(); sort* r = nullptr; if (!mk_c(c)->sutil().is_re(to_sort(s), r)) { SET_ERROR_CODE(Z3_INVALID_ARG, "expected regex sort"); RETURN_Z3(nullptr); } RETURN_Z3(of_sort(r)); Z3_CATCH_RETURN(nullptr); } bool Z3_API Z3_is_string_sort(Z3_context c, Z3_sort s) { Z3_TRY; LOG_Z3_is_string_sort(c, s); RESET_ERROR_CODE(); return mk_c(c)->sutil().is_string(to_sort(s)); Z3_CATCH_RETURN(false); } bool Z3_API Z3_is_string(Z3_context c, Z3_ast s) { Z3_TRY; LOG_Z3_is_string(c, s); RESET_ERROR_CODE(); return mk_c(c)->sutil().str.is_string(to_expr(s)); Z3_CATCH_RETURN(false); } Z3_string Z3_API Z3_get_string(Z3_context c, Z3_ast s) { Z3_TRY; LOG_Z3_get_string(c, s); RESET_ERROR_CODE(); zstring str; if (!mk_c(c)->sutil().str.is_string(to_expr(s), str)) { SET_ERROR_CODE(Z3_INVALID_ARG, "expression is not a string literal"); return ""; } return mk_c(c)->mk_external_string(str.encode()); Z3_CATCH_RETURN(""); } Z3_string Z3_API Z3_get_lstring(Z3_context c, Z3_ast s, unsigned* length) { Z3_TRY; LOG_Z3_get_lstring(c, s, length); RESET_ERROR_CODE(); zstring str; if (!length) { SET_ERROR_CODE(Z3_INVALID_ARG, "length argument is null"); return ""; } if (!mk_c(c)->sutil().str.is_string(to_expr(s), str)) { SET_ERROR_CODE(Z3_INVALID_ARG, "expression is not a string literal"); return ""; } std::string s = str.as_string(); *length = (unsigned)(s.size()); return mk_c(c)->mk_external_string(s.c_str(), *length); Z3_CATCH_RETURN(""); } #define MK_SORTED(NAME, FN ) \ Z3_ast Z3_API NAME(Z3_context c, Z3_sort s) { \ Z3_TRY; \ LOG_ ## NAME(c, s); \ RESET_ERROR_CODE(); \ app* a = FN(to_sort(s)); \ mk_c(c)->save_ast_trail(a); \ RETURN_Z3(of_ast(a)); \ Z3_CATCH_RETURN(0); \ } MK_SORTED(Z3_mk_seq_empty, mk_c(c)->sutil().str.mk_empty); MK_UNARY(Z3_mk_seq_unit, mk_c(c)->get_seq_fid(), OP_SEQ_UNIT, SKIP); MK_NARY(Z3_mk_seq_concat, mk_c(c)->get_seq_fid(), OP_SEQ_CONCAT, SKIP); MK_BINARY(Z3_mk_seq_prefix, mk_c(c)->get_seq_fid(), OP_SEQ_PREFIX, SKIP); MK_BINARY(Z3_mk_seq_suffix, mk_c(c)->get_seq_fid(), OP_SEQ_SUFFIX, SKIP); MK_BINARY(Z3_mk_seq_contains, mk_c(c)->get_seq_fid(), OP_SEQ_CONTAINS, SKIP); MK_BINARY(Z3_mk_str_lt, mk_c(c)->get_seq_fid(), OP_STRING_LT, SKIP); MK_BINARY(Z3_mk_str_le, mk_c(c)->get_seq_fid(), OP_STRING_LE, SKIP); MK_TERNARY(Z3_mk_seq_extract, mk_c(c)->get_seq_fid(), OP_SEQ_EXTRACT, SKIP); MK_TERNARY(Z3_mk_seq_replace, mk_c(c)->get_seq_fid(), OP_SEQ_REPLACE, SKIP); MK_BINARY(Z3_mk_seq_at, mk_c(c)->get_seq_fid(), OP_SEQ_AT, SKIP); MK_BINARY(Z3_mk_seq_nth, mk_c(c)->get_seq_fid(), OP_SEQ_NTH, SKIP); MK_UNARY(Z3_mk_seq_length, mk_c(c)->get_seq_fid(), OP_SEQ_LENGTH, SKIP); MK_TERNARY(Z3_mk_seq_index, mk_c(c)->get_seq_fid(), OP_SEQ_INDEX, SKIP); MK_BINARY(Z3_mk_seq_last_index, mk_c(c)->get_seq_fid(), OP_SEQ_LAST_INDEX, SKIP); MK_UNARY(Z3_mk_seq_to_re, mk_c(c)->get_seq_fid(), OP_SEQ_TO_RE, SKIP); MK_BINARY(Z3_mk_seq_in_re, mk_c(c)->get_seq_fid(), OP_SEQ_IN_RE, SKIP); MK_UNARY(Z3_mk_int_to_str, mk_c(c)->get_seq_fid(), OP_STRING_ITOS, SKIP); MK_UNARY(Z3_mk_str_to_int, mk_c(c)->get_seq_fid(), OP_STRING_STOI, SKIP); Z3_ast Z3_API Z3_mk_re_loop(Z3_context c, Z3_ast r, unsigned lo, unsigned hi) { Z3_TRY; LOG_Z3_mk_re_loop(c, r, lo, hi); RESET_ERROR_CODE(); app* a = hi == 0 ? mk_c(c)->sutil().re.mk_loop(to_expr(r), lo) : mk_c(c)->sutil().re.mk_loop(to_expr(r), lo, hi); mk_c(c)->save_ast_trail(a); RETURN_Z3(of_ast(a)); Z3_CATCH_RETURN(nullptr); } MK_UNARY(Z3_mk_re_plus, mk_c(c)->get_seq_fid(), OP_RE_PLUS, SKIP); MK_UNARY(Z3_mk_re_star, mk_c(c)->get_seq_fid(), OP_RE_STAR, SKIP); MK_UNARY(Z3_mk_re_option, mk_c(c)->get_seq_fid(), OP_RE_OPTION, SKIP); MK_UNARY(Z3_mk_re_complement, mk_c(c)->get_seq_fid(), OP_RE_COMPLEMENT, SKIP); MK_NARY(Z3_mk_re_union, mk_c(c)->get_seq_fid(), OP_RE_UNION, SKIP); MK_NARY(Z3_mk_re_intersect, mk_c(c)->get_seq_fid(), OP_RE_INTERSECT, SKIP); MK_NARY(Z3_mk_re_concat, mk_c(c)->get_seq_fid(), OP_RE_CONCAT, SKIP); MK_BINARY(Z3_mk_re_range, mk_c(c)->get_seq_fid(), OP_RE_RANGE, SKIP); MK_SORTED(Z3_mk_re_empty, mk_c(c)->sutil().re.mk_empty); MK_SORTED(Z3_mk_re_full, mk_c(c)->sutil().re.mk_full_seq); }; z3-z3-4.8.7/src/api/api_solver.cpp000066400000000000000000000721711356505360400166460ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_solver.cpp Abstract: Solver API Author: Leonardo de Moura (leonardo) 2012-03-07. Revision History: --*/ #include #include "util/scoped_ctrl_c.h" #include "util/cancel_eh.h" #include "util/file_path.h" #include "util/scoped_timer.h" #include "util/file_path.h" #include "ast/ast_pp.h" #include "api/z3.h" #include "api/api_log_macros.h" #include "api/api_context.h" #include "api/api_tactic.h" #include "api/api_solver.h" #include "api/api_model.h" #include "api/api_stats.h" #include "api/api_ast_vector.h" #include "model/model_params.hpp" #include "smt/smt_solver.h" #include "smt/smt_implied_equalities.h" #include "solver/smt_logics.h" #include "solver/tactic2solver.h" #include "solver/solver_params.hpp" #include "cmd_context/cmd_context.h" #include "parsers/smt2/smt2parser.h" #include "sat/dimacs.h" #include "sat/sat_solver.h" #include "sat/tactic/goal2sat.h" extern "C" { void solver2smt2_pp::assert_expr(expr* e) { m_pp_util.collect(e); m_pp_util.display_decls(m_out); m_pp_util.display_assert(m_out, e, true); } void solver2smt2_pp::assert_expr(expr* e, expr* t) { m_pp_util.collect(e); m_pp_util.collect(t); m_pp_util.display_decls(m_out); m_pp_util.display_assert_and_track(m_out, e, t, true); m_tracked.push_back(t); } void solver2smt2_pp::push() { m_out << "(push)\n"; m_pp_util.push(); m_tracked_lim.push_back(m_tracked.size()); } void solver2smt2_pp::pop(unsigned n) { m_out << "(pop " << n << ")\n"; m_pp_util.pop(n); m_tracked.shrink(m_tracked_lim[m_tracked_lim.size() - n]); m_tracked_lim.shrink(m_tracked_lim.size() - n); } void solver2smt2_pp::reset() { m_out << "(reset)\n"; m_pp_util.reset(); } void solver2smt2_pp::check(unsigned n, expr* const* asms) { m_out << "(check-sat"; for (unsigned i = 0; i < n; ++i) { m_pp_util.display_expr(m_out << "\n", asms[i]); } for (expr* e : m_tracked) { m_pp_util.display_expr(m_out << "\n", e); } m_out << ")\n"; m_out.flush(); } void solver2smt2_pp::get_consequences(expr_ref_vector const& assumptions, expr_ref_vector const& variables) { m_out << "(get-consequences ("; for (expr* f : assumptions) { m_out << "\n"; m_pp_util.display_expr(m_out, f); } m_out << ") ("; for (expr* f : variables) { m_out << "\n"; m_pp_util.display_expr(m_out, f); } m_out << ")\n"; m_out.flush(); } solver2smt2_pp::solver2smt2_pp(ast_manager& m, char const* file): m_pp_util(m), m_out(file), m_tracked(m) { if (!m_out) { throw default_exception("could not open " + std::string(file) + " for output"); } } void Z3_solver_ref::set_eh(event_handler* eh) { std::lock_guard lock(m_mux); m_eh = eh; } void Z3_solver_ref::set_cancel() { std::lock_guard lock(m_mux); if (m_eh) (*m_eh)(API_INTERRUPT_EH_CALLER); } void Z3_solver_ref::assert_expr(expr * e) { if (m_pp) m_pp->assert_expr(e); m_solver->assert_expr(e); } void Z3_solver_ref::assert_expr(expr * e, expr* t) { if (m_pp) m_pp->assert_expr(e, t); m_solver->assert_expr(e, t); } static void init_solver_core(Z3_context c, Z3_solver _s) { Z3_solver_ref * s = to_solver(_s); bool proofs_enabled, models_enabled, unsat_core_enabled; params_ref p = s->m_params; mk_c(c)->params().get_solver_params(mk_c(c)->m(), p, proofs_enabled, models_enabled, unsat_core_enabled); s->m_solver = (*(s->m_solver_factory))(mk_c(c)->m(), p, proofs_enabled, models_enabled, unsat_core_enabled, s->m_logic); param_descrs r; s->m_solver->collect_param_descrs(r); context_params::collect_solver_param_descrs(r); p.validate(r); s->m_solver->updt_params(p); } static void init_solver(Z3_context c, Z3_solver s) { if (to_solver(s)->m_solver.get() == nullptr) init_solver_core(c, s); } Z3_solver Z3_API Z3_mk_simple_solver(Z3_context c) { Z3_TRY; LOG_Z3_mk_simple_solver(c); RESET_ERROR_CODE(); Z3_solver_ref * s = alloc(Z3_solver_ref, *mk_c(c), mk_smt_solver_factory()); mk_c(c)->save_object(s); Z3_solver r = of_solver(s); RETURN_Z3(r); Z3_CATCH_RETURN(nullptr); } Z3_solver Z3_API Z3_mk_solver(Z3_context c) { Z3_TRY; LOG_Z3_mk_solver(c); RESET_ERROR_CODE(); Z3_solver_ref * s = alloc(Z3_solver_ref, *mk_c(c), mk_smt_strategic_solver_factory()); mk_c(c)->save_object(s); Z3_solver r = of_solver(s); RETURN_Z3(r); Z3_CATCH_RETURN(nullptr); } Z3_solver Z3_API Z3_mk_solver_for_logic(Z3_context c, Z3_symbol logic) { Z3_TRY; LOG_Z3_mk_solver_for_logic(c, logic); RESET_ERROR_CODE(); if (!smt_logics::supported_logic(to_symbol(logic))) { std::ostringstream strm; strm << "logic '" << to_symbol(logic) << "' is not recognized"; throw default_exception(strm.str()); RETURN_Z3(nullptr); } else { Z3_solver_ref * s = alloc(Z3_solver_ref, *mk_c(c), mk_smt_strategic_solver_factory(to_symbol(logic))); mk_c(c)->save_object(s); Z3_solver r = of_solver(s); RETURN_Z3(r); } Z3_CATCH_RETURN(nullptr); } Z3_solver Z3_API Z3_mk_solver_from_tactic(Z3_context c, Z3_tactic t) { Z3_TRY; LOG_Z3_mk_solver_from_tactic(c, t); RESET_ERROR_CODE(); Z3_solver_ref * s = alloc(Z3_solver_ref, *mk_c(c), mk_tactic2solver_factory(to_tactic_ref(t))); mk_c(c)->save_object(s); Z3_solver r = of_solver(s); RETURN_Z3(r); Z3_CATCH_RETURN(nullptr); } Z3_solver Z3_API Z3_solver_translate(Z3_context c, Z3_solver s, Z3_context target) { Z3_TRY; LOG_Z3_solver_translate(c, s, target); RESET_ERROR_CODE(); params_ref const& p = to_solver(s)->m_params; Z3_solver_ref * sr = alloc(Z3_solver_ref, *mk_c(target), nullptr); init_solver(c, s); sr->m_solver = to_solver(s)->m_solver->translate(mk_c(target)->m(), p); mk_c(target)->save_object(sr); Z3_solver r = of_solver(sr); RETURN_Z3(r); Z3_CATCH_RETURN(nullptr); } void Z3_API Z3_solver_import_model_converter(Z3_context c, Z3_solver src, Z3_solver dst) { Z3_TRY; LOG_Z3_solver_import_model_converter(c, src, dst); model_converter_ref mc = to_solver_ref(src)->get_model_converter(); to_solver_ref(dst)->set_model_converter(mc.get()); Z3_CATCH; } void solver_from_stream(Z3_context c, Z3_solver s, std::istream& is) { scoped_ptr ctx = alloc(cmd_context, false, &(mk_c(c)->m())); ctx->set_ignore_check(true); std::stringstream errstrm; ctx->set_regular_stream(errstrm); if (!parse_smt2_commands(*ctx.get(), is)) { ctx = nullptr; SET_ERROR_CODE(Z3_PARSER_ERROR, errstrm.str().c_str()); return; } bool initialized = to_solver(s)->m_solver.get() != nullptr; if (!initialized) init_solver(c, s); for (expr * e : ctx->assertions()) { to_solver(s)->assert_expr(e); } 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(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); if (is_dimacs_string(c_str)) { solver_from_dimacs_stream(c, s, is); } else { solver_from_stream(c, s, is); } Z3_CATCH; } void Z3_API Z3_solver_from_file(Z3_context c, Z3_solver s, Z3_string file_name) { Z3_TRY; LOG_Z3_solver_from_file(c, s, file_name); char const* ext = get_extension(file_name); std::ifstream is(file_name); init_solver(c, s); if (!is) { SET_ERROR_CODE(Z3_FILE_ACCESS_ERROR, nullptr); } 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); } Z3_CATCH; } Z3_string Z3_API Z3_solver_get_help(Z3_context c, Z3_solver s) { Z3_TRY; LOG_Z3_solver_get_help(c, s); RESET_ERROR_CODE(); std::ostringstream buffer; param_descrs descrs; bool initialized = to_solver(s)->m_solver.get() != nullptr; if (!initialized) init_solver(c, s); to_solver_ref(s)->collect_param_descrs(descrs); context_params::collect_solver_param_descrs(descrs); if (!initialized) to_solver(s)->m_solver = nullptr; descrs.display(buffer); return mk_c(c)->mk_external_string(buffer.str()); Z3_CATCH_RETURN(""); } Z3_param_descrs Z3_API Z3_solver_get_param_descrs(Z3_context c, Z3_solver s) { Z3_TRY; LOG_Z3_solver_get_param_descrs(c, s); RESET_ERROR_CODE(); Z3_param_descrs_ref * d = alloc(Z3_param_descrs_ref, *mk_c(c)); mk_c(c)->save_object(d); bool initialized = to_solver(s)->m_solver.get() != nullptr; if (!initialized) init_solver(c, s); to_solver_ref(s)->collect_param_descrs(d->m_descrs); context_params::collect_solver_param_descrs(d->m_descrs); if (!initialized) to_solver(s)->m_solver = nullptr; Z3_param_descrs r = of_param_descrs(d); RETURN_Z3(r); Z3_CATCH_RETURN(nullptr); } void Z3_API Z3_solver_set_params(Z3_context c, Z3_solver s, Z3_params p) { Z3_TRY; LOG_Z3_solver_set_params(c, s, p); RESET_ERROR_CODE(); symbol logic = to_param_ref(p).get_sym("smt.logic", symbol::null); symbol smt2log = to_param_ref(p).get_sym("solver.smtlib2_log", symbol::null); if (logic != symbol::null) { to_solver(s)->m_logic = logic; } if (smt2log != symbol::null && !to_solver(s)->m_pp) { to_solver(s)->m_pp = alloc(solver2smt2_pp, mk_c(c)->m(), smt2log.str().c_str()); } if (to_solver(s)->m_solver) { bool old_model = to_solver(s)->m_params.get_bool("model", true); bool new_model = to_param_ref(p).get_bool("model", true); if (old_model != new_model) to_solver_ref(s)->set_produce_models(new_model); param_descrs r; to_solver_ref(s)->collect_param_descrs(r); context_params::collect_solver_param_descrs(r); to_param_ref(p).validate(r); to_solver_ref(s)->updt_params(to_param_ref(p)); } to_solver(s)->m_params.append(to_param_ref(p)); Z3_CATCH; } void Z3_API Z3_solver_inc_ref(Z3_context c, Z3_solver s) { Z3_TRY; LOG_Z3_solver_inc_ref(c, s); RESET_ERROR_CODE(); to_solver(s)->inc_ref(); Z3_CATCH; } void Z3_API Z3_solver_dec_ref(Z3_context c, Z3_solver s) { Z3_TRY; LOG_Z3_solver_dec_ref(c, s); RESET_ERROR_CODE(); to_solver(s)->dec_ref(); Z3_CATCH; } void Z3_API Z3_solver_push(Z3_context c, Z3_solver s) { Z3_TRY; LOG_Z3_solver_push(c, s); RESET_ERROR_CODE(); init_solver(c, s); to_solver_ref(s)->push(); if (to_solver(s)->m_pp) to_solver(s)->m_pp->push(); Z3_CATCH; } void Z3_API Z3_solver_interrupt(Z3_context c, Z3_solver s) { to_solver(s)->set_cancel(); } void Z3_API Z3_solver_pop(Z3_context c, Z3_solver s, unsigned n) { Z3_TRY; LOG_Z3_solver_pop(c, s, n); RESET_ERROR_CODE(); init_solver(c, s); if (n > to_solver_ref(s)->get_scope_level()) { SET_ERROR_CODE(Z3_IOB, nullptr); return; } if (n > 0) { to_solver_ref(s)->pop(n); if (to_solver(s)->m_pp) to_solver(s)->m_pp->pop(n); } Z3_CATCH; } void Z3_API Z3_solver_reset(Z3_context c, Z3_solver s) { Z3_TRY; LOG_Z3_solver_reset(c, s); RESET_ERROR_CODE(); to_solver(s)->m_solver = nullptr; if (to_solver(s)->m_pp) to_solver(s)->m_pp->reset(); Z3_CATCH; } unsigned Z3_API Z3_solver_get_num_scopes(Z3_context c, Z3_solver s) { Z3_TRY; LOG_Z3_solver_get_num_scopes(c, s); RESET_ERROR_CODE(); init_solver(c, s); return to_solver_ref(s)->get_scope_level(); Z3_CATCH_RETURN(0); } void Z3_API Z3_solver_assert(Z3_context c, Z3_solver s, Z3_ast a) { Z3_TRY; LOG_Z3_solver_assert(c, s, a); RESET_ERROR_CODE(); init_solver(c, s); CHECK_FORMULA(a,); to_solver(s)->assert_expr(to_expr(a)); Z3_CATCH; } void Z3_API Z3_solver_assert_and_track(Z3_context c, Z3_solver s, Z3_ast a, Z3_ast p) { Z3_TRY; LOG_Z3_solver_assert_and_track(c, s, a, p); RESET_ERROR_CODE(); init_solver(c, s); CHECK_FORMULA(a,); CHECK_FORMULA(p,); to_solver(s)->assert_expr(to_expr(a), to_expr(p)); Z3_CATCH; } Z3_ast_vector Z3_API Z3_solver_get_assertions(Z3_context c, Z3_solver s) { Z3_TRY; LOG_Z3_solver_get_assertions(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); unsigned sz = to_solver_ref(s)->get_num_assertions(); for (unsigned i = 0; i < sz; i++) { v->m_ast_vector.push_back(to_solver_ref(s)->get_assertion(i)); } RETURN_Z3(of_ast_vector(v)); Z3_CATCH_RETURN(nullptr); } Z3_ast_vector Z3_API Z3_solver_get_units(Z3_context c, Z3_solver s) { Z3_TRY; LOG_Z3_solver_get_units(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 fmls = to_solver_ref(s)->get_units(); for (expr* f : fmls) { v->m_ast_vector.push_back(f); } RETURN_Z3(of_ast_vector(v)); Z3_CATCH_RETURN(nullptr); } Z3_ast_vector Z3_API Z3_solver_get_non_units(Z3_context c, Z3_solver s) { Z3_TRY; LOG_Z3_solver_get_non_units(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 fmls = to_solver_ref(s)->get_non_units(); for (expr* f : fmls) { v->m_ast_vector.push_back(f); } RETURN_Z3(of_ast_vector(v)); 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; } 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]))) { SET_ERROR_CODE(Z3_INVALID_ARG, "assumption is not an expression"); return Z3_L_UNDEF; } } expr * const * _assumptions = to_exprs(num_assumptions, assumptions); solver_params sp(to_solver(s)->m_params); unsigned timeout = mk_c(c)->get_timeout(); timeout = to_solver(s)->m_params.get_uint("timeout", timeout); timeout = sp.timeout() != UINT_MAX ? sp.timeout() : timeout; unsigned rlimit = to_solver(s)->m_params.get_uint("rlimit", mk_c(c)->get_rlimit()); bool use_ctrl_c = to_solver(s)->m_params.get_bool("ctrl_c", true); cancel_eh eh(mk_c(c)->m().limit()); to_solver(s)->set_eh(&eh); api::context::set_interruptable si(*(mk_c(c)), eh); lbool result = l_undef; { scoped_ctrl_c ctrlc(eh, false, use_ctrl_c); scoped_timer timer(timeout, &eh); scoped_rlimit _rlimit(mk_c(c)->m().limit(), rlimit); try { if (to_solver(s)->m_pp) to_solver(s)->m_pp->check(num_assumptions, _assumptions); result = to_solver_ref(s)->check_sat(num_assumptions, _assumptions); } catch (z3_exception & ex) { to_solver_ref(s)->set_reason_unknown(eh); to_solver(s)->set_eh(nullptr); if (!mk_c(c)->m().canceled()) { mk_c(c)->handle_exception(ex); } return Z3_L_UNDEF; } catch (...) { to_solver_ref(s)->set_reason_unknown(eh); to_solver(s)->set_eh(nullptr); return Z3_L_UNDEF; } } to_solver(s)->set_eh(nullptr); if (result == l_undef) { to_solver_ref(s)->set_reason_unknown(eh); } return static_cast(result); } Z3_lbool Z3_API Z3_solver_check(Z3_context c, Z3_solver s) { Z3_TRY; LOG_Z3_solver_check(c, s); RESET_ERROR_CODE(); init_solver(c, s); return _solver_check(c, s, 0, nullptr); Z3_CATCH_RETURN(Z3_L_UNDEF); } Z3_lbool Z3_API Z3_solver_check_assumptions(Z3_context c, Z3_solver s, unsigned num_assumptions, Z3_ast const assumptions[]) { Z3_TRY; LOG_Z3_solver_check_assumptions(c, s, num_assumptions, assumptions); RESET_ERROR_CODE(); init_solver(c, s); return _solver_check(c, s, num_assumptions, assumptions); Z3_CATCH_RETURN(Z3_L_UNDEF); } Z3_model Z3_API Z3_solver_get_model(Z3_context c, Z3_solver s) { Z3_TRY; LOG_Z3_solver_get_model(c, s); RESET_ERROR_CODE(); init_solver(c, s); model_ref _m; to_solver_ref(s)->get_model(_m); if (!_m) { SET_ERROR_CODE(Z3_INVALID_USAGE, "there is no current model"); RETURN_Z3(nullptr); } if (_m) { model_params mp(to_solver_ref(s)->get_params()); if (mp.compact()) _m->compress(); } Z3_model_ref * m_ref = alloc(Z3_model_ref, *mk_c(c)); m_ref->m_model = _m; mk_c(c)->save_object(m_ref); RETURN_Z3(of_model(m_ref)); Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_solver_get_proof(Z3_context c, Z3_solver s) { Z3_TRY; LOG_Z3_solver_get_proof(c, s); RESET_ERROR_CODE(); init_solver(c, s); proof * p = to_solver_ref(s)->get_proof(); if (!p) { SET_ERROR_CODE(Z3_INVALID_USAGE, "there is no current proof"); RETURN_Z3(nullptr); } mk_c(c)->save_ast_trail(p); RETURN_Z3(of_ast(p)); Z3_CATCH_RETURN(nullptr); } Z3_ast_vector Z3_API Z3_solver_get_unsat_core(Z3_context c, Z3_solver s) { Z3_TRY; LOG_Z3_solver_get_unsat_core(c, s); RESET_ERROR_CODE(); init_solver(c, s); expr_ref_vector core(mk_c(c)->m()); to_solver_ref(s)->get_unsat_core(core); Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, *mk_c(c), mk_c(c)->m()); mk_c(c)->save_object(v); for (expr* e : core) { v->m_ast_vector.push_back(e); } RETURN_Z3(of_ast_vector(v)); Z3_CATCH_RETURN(nullptr); } Z3_string Z3_API Z3_solver_get_reason_unknown(Z3_context c, Z3_solver s) { Z3_TRY; LOG_Z3_solver_get_reason_unknown(c, s); RESET_ERROR_CODE(); init_solver(c, s); return mk_c(c)->mk_external_string(to_solver_ref(s)->reason_unknown()); Z3_CATCH_RETURN(""); } Z3_stats Z3_API Z3_solver_get_statistics(Z3_context c, Z3_solver s) { Z3_TRY; LOG_Z3_solver_get_statistics(c, s); RESET_ERROR_CODE(); init_solver(c, s); Z3_stats_ref * st = alloc(Z3_stats_ref, *mk_c(c)); to_solver_ref(s)->collect_statistics(st->m_stats); get_memory_statistics(st->m_stats); get_rlimit_statistics(mk_c(c)->m().limit(), st->m_stats); mk_c(c)->save_object(st); Z3_stats r = of_stats(st); RETURN_Z3(r); Z3_CATCH_RETURN(nullptr); } Z3_string Z3_API Z3_solver_to_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(buffer); return mk_c(c)->mk_external_string(buffer.str()); 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, unsigned num_terms, Z3_ast const terms[], unsigned class_ids[]) { Z3_TRY; LOG_Z3_get_implied_equalities(c, s, num_terms, terms, class_ids); ast_manager& m = mk_c(c)->m(); RESET_ERROR_CODE(); CHECK_SEARCHING(c); init_solver(c, s); lbool result = smt::implied_equalities(m, *to_solver_ref(s), num_terms, to_exprs(num_terms, terms), class_ids); return static_cast(result); Z3_CATCH_RETURN(Z3_L_UNDEF); } Z3_lbool Z3_API Z3_solver_get_consequences(Z3_context c, Z3_solver s, Z3_ast_vector assumptions, Z3_ast_vector variables, Z3_ast_vector consequences) { Z3_TRY; LOG_Z3_solver_get_consequences(c, s, assumptions, variables, consequences); ast_manager& m = mk_c(c)->m(); RESET_ERROR_CODE(); CHECK_SEARCHING(c); init_solver(c, s); expr_ref_vector _assumptions(m), _consequences(m), _variables(m); ast_ref_vector const& __assumptions = to_ast_vector_ref(assumptions); for (ast* e : __assumptions) { if (!is_expr(e)) { _assumptions.finalize(); _consequences.finalize(); _variables.finalize(); SET_ERROR_CODE(Z3_INVALID_USAGE, "assumption is not an expression"); return Z3_L_UNDEF; } _assumptions.push_back(to_expr(e)); } ast_ref_vector const& __variables = to_ast_vector_ref(variables); for (ast* a : __variables) { if (!is_expr(a)) { _assumptions.finalize(); _consequences.finalize(); _variables.finalize(); SET_ERROR_CODE(Z3_INVALID_USAGE, "variable is not an expression"); return Z3_L_UNDEF; } _variables.push_back(to_expr(a)); } lbool result = l_undef; unsigned timeout = to_solver(s)->m_params.get_uint("timeout", mk_c(c)->get_timeout()); unsigned rlimit = to_solver(s)->m_params.get_uint("rlimit", mk_c(c)->get_rlimit()); bool use_ctrl_c = to_solver(s)->m_params.get_bool("ctrl_c", true); cancel_eh eh(mk_c(c)->m().limit()); to_solver(s)->set_eh(&eh); api::context::set_interruptable si(*(mk_c(c)), eh); { scoped_ctrl_c ctrlc(eh, false, use_ctrl_c); scoped_timer timer(timeout, &eh); scoped_rlimit _rlimit(mk_c(c)->m().limit(), rlimit); try { if (to_solver(s)->m_pp) to_solver(s)->m_pp->get_consequences(_assumptions, _variables); result = to_solver_ref(s)->get_consequences(_assumptions, _variables, _consequences); } catch (z3_exception & ex) { to_solver(s)->set_eh(nullptr); to_solver_ref(s)->set_reason_unknown(eh); _assumptions.finalize(); _consequences.finalize(); _variables.finalize(); mk_c(c)->handle_exception(ex); return Z3_L_UNDEF; } catch (...) { } } to_solver(s)->set_eh(nullptr); if (result == l_undef) { to_solver_ref(s)->set_reason_unknown(eh); } for (expr* e : _consequences) { to_ast_vector_ref(consequences).push_back(e); } return static_cast(result); Z3_CATCH_RETURN(Z3_L_UNDEF); } Z3_ast_vector Z3_API Z3_solver_cube(Z3_context c, Z3_solver s, Z3_ast_vector vs, unsigned cutoff) { Z3_TRY; LOG_Z3_solver_cube(c, s, vs, cutoff); ast_manager& m = mk_c(c)->m(); expr_ref_vector result(m), vars(m); for (ast* a : to_ast_vector_ref(vs)) { if (!is_expr(a)) { SET_ERROR_CODE(Z3_INVALID_USAGE, "cube contains a non-expression"); } else { vars.push_back(to_expr(a)); } } unsigned timeout = to_solver(s)->m_params.get_uint("timeout", mk_c(c)->get_timeout()); unsigned rlimit = to_solver(s)->m_params.get_uint("rlimit", mk_c(c)->get_rlimit()); bool use_ctrl_c = to_solver(s)->m_params.get_bool("ctrl_c", true); cancel_eh eh(mk_c(c)->m().limit()); to_solver(s)->set_eh(&eh); api::context::set_interruptable si(*(mk_c(c)), eh); { scoped_ctrl_c ctrlc(eh, false, use_ctrl_c); scoped_timer timer(timeout, &eh); scoped_rlimit _rlimit(mk_c(c)->m().limit(), rlimit); try { result.append(to_solver_ref(s)->cube(vars, cutoff)); } catch (z3_exception & ex) { to_solver(s)->set_eh(nullptr); mk_c(c)->handle_exception(ex); return nullptr; } catch (...) { } } to_solver(s)->set_eh(nullptr); Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, *mk_c(c), mk_c(c)->m()); mk_c(c)->save_object(v); for (expr* e : result) { v->m_ast_vector.push_back(e); } to_ast_vector_ref(vs).reset(); for (expr* a : vars) { to_ast_vector_ref(vs).push_back(a); } RETURN_Z3(of_ast_vector(v)); Z3_CATCH_RETURN(nullptr); } }; z3-z3-4.8.7/src/api/api_solver.h000066400000000000000000000032511356505360400163040ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_solver.h Abstract: New solver API Author: Leonardo de Moura (leonardo) 2012-03-07. Revision History: --*/ #ifndef API_SOLVER_H_ #define API_SOLVER_H_ #include "api/api_util.h" #include "solver/solver.h" struct solver2smt2_pp { ast_pp_util m_pp_util; std::ofstream m_out; expr_ref_vector m_tracked; unsigned_vector m_tracked_lim; solver2smt2_pp(ast_manager& m, char const* file); void assert_expr(expr* e); void assert_expr(expr* e, expr* t); void push(); void pop(unsigned n); void reset(); void check(unsigned n, expr* const* asms); void get_consequences(expr_ref_vector const& assumptions, expr_ref_vector const& variables); }; struct Z3_solver_ref : public api::object { scoped_ptr m_solver_factory; ref m_solver; params_ref m_params; symbol m_logic; scoped_ptr m_pp; std::mutex m_mux; event_handler* m_eh; Z3_solver_ref(api::context& c, solver_factory * f): api::object(c), m_solver_factory(f), m_solver(nullptr), m_logic(symbol::null), m_eh(nullptr) {} ~Z3_solver_ref() override {} void assert_expr(expr* e); void assert_expr(expr* e, expr* t); void set_eh(event_handler* eh); void set_cancel(); }; inline Z3_solver_ref * to_solver(Z3_solver s) { return reinterpret_cast(s); } inline Z3_solver of_solver(Z3_solver_ref * s) { return reinterpret_cast(s); } inline solver * to_solver_ref(Z3_solver s) { return to_solver(s)->m_solver.get(); } #endif z3-z3-4.8.7/src/api/api_special_relations.cpp000066400000000000000000000047421356505360400210330ustar00rootroot00000000000000/*++ Copyright (c) 2019 Microsoft Corporation Module Name: api_special_relations.cpp Abstract: Basic API for Special relations Author: Nikolaj Bjorner (nbjorner) 2019-03-25 Ashutosh Gupta 2016 Revision History: --*/ #include #include "api/z3.h" #include "api/api_log_macros.h" #include "api/api_context.h" #include "api/api_util.h" #include "ast/ast_pp.h" #include "ast/special_relations_decl_plugin.h" extern "C" { #define MK_SPECIAL_R(NAME, FID) \ Z3_func_decl Z3_API NAME(Z3_context c, Z3_sort s, unsigned index) { \ LOG_ ##NAME(c, s, index); \ Z3_TRY; \ parameter p(index); \ sort* domain[2] = { to_sort(s), to_sort(s) }; \ func_decl* f = mk_c(c)->m().mk_func_decl(mk_c(c)->get_special_relations_fid(), FID, 1, &p, 2, domain, mk_c(c)->m().mk_bool_sort()); \ mk_c(c)->save_ast_trail(f); \ RETURN_Z3(of_func_decl(f)); \ Z3_CATCH_RETURN(nullptr); \ } MK_SPECIAL_R(Z3_mk_linear_order, OP_SPECIAL_RELATION_LO); MK_SPECIAL_R(Z3_mk_partial_order, OP_SPECIAL_RELATION_PO); MK_SPECIAL_R(Z3_mk_piecewise_linear_order, OP_SPECIAL_RELATION_PLO); MK_SPECIAL_R(Z3_mk_tree_order, OP_SPECIAL_RELATION_TO); #define MK_DECL(NAME, FID) \ Z3_func_decl Z3_API NAME(Z3_context c,Z3_func_decl f) { \ Z3_TRY; \ LOG_ ##NAME(c, f); \ RESET_ERROR_CODE(); \ ast_manager & m = mk_c(c)->m(); \ func_decl* _f = to_func_decl(f); \ parameter param(_f); \ sort* domain[2] = { _f->get_domain(0), _f->get_domain(1) }; \ func_decl * d = m.mk_func_decl(mk_c(c)->get_special_relations_fid(), FID, 1, ¶m, 2, domain); \ mk_c(c)->save_ast_trail(d); \ RETURN_Z3(of_func_decl(d)); \ Z3_CATCH_RETURN(nullptr); \ } MK_DECL(Z3_mk_transitive_closure, OP_SPECIAL_RELATION_TC); }; z3-z3-4.8.7/src/api/api_stats.cpp000066400000000000000000000072511356505360400164670ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_stats.cpp Abstract: API for browsing statistics Author: Leonardo de Moura (leonardo) 2012-03-07. Revision History: --*/ #include #include "api/z3.h" #include "api/api_log_macros.h" #include "api/api_context.h" #include "api/api_stats.h" extern "C" { Z3_string Z3_API Z3_stats_to_string(Z3_context c, Z3_stats s) { Z3_TRY; LOG_Z3_stats_to_string(c, s); RESET_ERROR_CODE(); std::ostringstream buffer; to_stats_ref(s).display_smt2(buffer); std::string result = buffer.str(); // Hack for removing the trailing '\n' result = buffer.str(); SASSERT(result.size() > 0); result.resize(result.size()-1); return mk_c(c)->mk_external_string(std::move(result)); Z3_CATCH_RETURN(""); } void Z3_API Z3_stats_inc_ref(Z3_context c, Z3_stats s) { Z3_TRY; LOG_Z3_stats_inc_ref(c, s); RESET_ERROR_CODE(); to_stats(s)->inc_ref(); Z3_CATCH; } void Z3_API Z3_stats_dec_ref(Z3_context c, Z3_stats s) { Z3_TRY; LOG_Z3_stats_dec_ref(c, s); RESET_ERROR_CODE(); to_stats(s)->dec_ref(); Z3_CATCH; } unsigned Z3_API Z3_stats_size(Z3_context c, Z3_stats s) { Z3_TRY; LOG_Z3_stats_size(c, s); RESET_ERROR_CODE(); return to_stats_ref(s).size(); Z3_CATCH_RETURN(0); } Z3_string Z3_API Z3_stats_get_key(Z3_context c, Z3_stats s, unsigned idx) { Z3_TRY; LOG_Z3_stats_get_key(c, s, idx); RESET_ERROR_CODE(); if (idx >= to_stats_ref(s).size()) { SET_ERROR_CODE(Z3_IOB, nullptr); return ""; } return to_stats_ref(s).get_key(idx); Z3_CATCH_RETURN(""); } bool Z3_API Z3_stats_is_uint(Z3_context c, Z3_stats s, unsigned idx) { Z3_TRY; LOG_Z3_stats_is_uint(c, s, idx); RESET_ERROR_CODE(); if (idx >= to_stats_ref(s).size()) { SET_ERROR_CODE(Z3_IOB, nullptr); return false; } return to_stats_ref(s).is_uint(idx); Z3_CATCH_RETURN(0); } bool Z3_API Z3_stats_is_double(Z3_context c, Z3_stats s, unsigned idx) { Z3_TRY; LOG_Z3_stats_is_double(c, s, idx); RESET_ERROR_CODE(); if (idx >= to_stats_ref(s).size()) { SET_ERROR_CODE(Z3_IOB, nullptr); return false; } return !to_stats_ref(s).is_uint(idx); Z3_CATCH_RETURN(false); } unsigned Z3_API Z3_stats_get_uint_value(Z3_context c, Z3_stats s, unsigned idx) { Z3_TRY; LOG_Z3_stats_get_uint_value(c, s, idx); RESET_ERROR_CODE(); if (idx >= to_stats_ref(s).size()) { SET_ERROR_CODE(Z3_IOB, nullptr); return 0; } if (!to_stats_ref(s).is_uint(idx)) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return 0; } return to_stats_ref(s).get_uint_value(idx); Z3_CATCH_RETURN(0); } double Z3_API Z3_stats_get_double_value(Z3_context c, Z3_stats s, unsigned idx) { Z3_TRY; LOG_Z3_stats_get_double_value(c, s, idx); RESET_ERROR_CODE(); if (idx >= to_stats_ref(s).size()) { SET_ERROR_CODE(Z3_IOB, nullptr); return 0.0; } if (to_stats_ref(s).is_uint(idx)) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return 0.0; } return to_stats_ref(s).get_double_value(idx); Z3_CATCH_RETURN(0.0); } uint64_t Z3_API Z3_get_estimated_alloc_size(void) { return memory::get_allocation_size(); } }; z3-z3-4.8.7/src/api/api_stats.h000066400000000000000000000013131356505360400161250ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_stats.h Abstract: API for Z3 statistics Author: Leonardo de Moura (leonardo) 2012-03-07. Revision History: --*/ #ifndef API_STATS_H_ #define API_STATS_H_ #include "api/api_util.h" #include "util/statistics.h" struct Z3_stats_ref : public api::object { statistics m_stats; Z3_stats_ref(api::context& c): api::object(c) {} ~Z3_stats_ref() override {} }; inline Z3_stats_ref * to_stats(Z3_stats s) { return reinterpret_cast(s); } inline Z3_stats of_stats(Z3_stats_ref * s) { return reinterpret_cast(s); } inline statistics & to_stats_ref(Z3_stats s) { return to_stats(s)->m_stats; } #endif z3-z3-4.8.7/src/api/api_tactic.cpp000066400000000000000000000402401356505360400165730ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_tactic.cpp Abstract: API for creating tactics and probes Author: Leonardo de Moura (leonardo) 2012-03-06. Revision History: --*/ #include #include "api/z3.h" #include "api/api_log_macros.h" #include "api/api_context.h" #include "api/api_tactic.h" #include "api/api_model.h" #include "util/scoped_ctrl_c.h" #include "util/cancel_eh.h" #include "util/scoped_timer.h" Z3_apply_result_ref::Z3_apply_result_ref(api::context& c, ast_manager & m): api::object(c) { } extern "C" { #define RETURN_TACTIC(_t_) { \ Z3_tactic_ref * _ref_ = alloc(Z3_tactic_ref, *mk_c(c)); \ _ref_->m_tactic = _t_; \ mk_c(c)->save_object(_ref_); \ Z3_tactic _result_ = of_tactic(_ref_); \ RETURN_Z3(_result_); \ } #define RETURN_PROBE(_t_) { \ Z3_probe_ref * _ref_ = alloc(Z3_probe_ref, *mk_c(c)); \ _ref_->m_probe = _t_; \ mk_c(c)->save_object(_ref_); \ Z3_probe _result_ = of_probe(_ref_); \ RETURN_Z3(_result_); \ } Z3_tactic Z3_API Z3_mk_tactic(Z3_context c, Z3_string name) { Z3_TRY; LOG_Z3_mk_tactic(c, name); RESET_ERROR_CODE(); tactic_cmd * t = mk_c(c)->find_tactic_cmd(symbol(name)); if (t == nullptr) { std::stringstream err; err << "unknown tactic " << name; SET_ERROR_CODE(Z3_INVALID_ARG, err.str().c_str()); RETURN_Z3(nullptr); } tactic * new_t = t->mk(mk_c(c)->m()); RETURN_TACTIC(new_t); Z3_CATCH_RETURN(nullptr); } void Z3_API Z3_tactic_inc_ref(Z3_context c, Z3_tactic t) { Z3_TRY; LOG_Z3_tactic_inc_ref(c, t); RESET_ERROR_CODE(); to_tactic(t)->inc_ref(); Z3_CATCH; } void Z3_API Z3_tactic_dec_ref(Z3_context c, Z3_tactic t) { Z3_TRY; LOG_Z3_tactic_dec_ref(c, t); RESET_ERROR_CODE(); to_tactic(t)->dec_ref(); Z3_CATCH; } Z3_probe Z3_API Z3_mk_probe(Z3_context c, Z3_string name) { Z3_TRY; LOG_Z3_mk_probe(c, name); RESET_ERROR_CODE(); probe_info * p = mk_c(c)->find_probe(symbol(name)); if (p == nullptr) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } probe * new_p = p->get(); RETURN_PROBE(new_p); Z3_CATCH_RETURN(nullptr); } void Z3_API Z3_probe_inc_ref(Z3_context c, Z3_probe p) { Z3_TRY; LOG_Z3_probe_inc_ref(c, p); RESET_ERROR_CODE(); to_probe(p)->inc_ref(); Z3_CATCH; } void Z3_API Z3_probe_dec_ref(Z3_context c, Z3_probe p) { Z3_TRY; LOG_Z3_probe_dec_ref(c, p); RESET_ERROR_CODE(); to_probe(p)->dec_ref(); Z3_CATCH; } Z3_tactic Z3_API Z3_tactic_and_then(Z3_context c, Z3_tactic t1, Z3_tactic t2) { Z3_TRY; LOG_Z3_tactic_and_then(c, t1, t2); RESET_ERROR_CODE(); tactic * new_t = and_then(to_tactic_ref(t1), to_tactic_ref(t2)); RETURN_TACTIC(new_t); Z3_CATCH_RETURN(nullptr); } Z3_tactic Z3_API Z3_tactic_or_else(Z3_context c, Z3_tactic t1, Z3_tactic t2) { Z3_TRY; LOG_Z3_tactic_or_else(c, t1, t2); RESET_ERROR_CODE(); tactic * new_t = or_else(to_tactic_ref(t1), to_tactic_ref(t2)); RETURN_TACTIC(new_t); Z3_CATCH_RETURN(nullptr); } Z3_tactic Z3_API Z3_tactic_par_or(Z3_context c, unsigned num, Z3_tactic const ts[]) { Z3_TRY; LOG_Z3_tactic_par_or(c, num, ts); RESET_ERROR_CODE(); ptr_buffer _ts; for (unsigned i = 0; i < num; i++) { _ts.push_back(to_tactic_ref(ts[i])); } tactic * new_t = par(num, _ts.c_ptr()); RETURN_TACTIC(new_t); Z3_CATCH_RETURN(nullptr); } Z3_tactic Z3_API Z3_tactic_par_and_then(Z3_context c, Z3_tactic t1, Z3_tactic t2) { Z3_TRY; LOG_Z3_tactic_par_and_then(c, t1, t2); RESET_ERROR_CODE(); tactic * new_t = par_and_then(to_tactic_ref(t1), to_tactic_ref(t2)); RETURN_TACTIC(new_t); Z3_CATCH_RETURN(nullptr); } Z3_tactic Z3_API Z3_tactic_try_for(Z3_context c, Z3_tactic t, unsigned ms) { Z3_TRY; LOG_Z3_tactic_try_for(c, t, ms); RESET_ERROR_CODE(); tactic * new_t = try_for(to_tactic_ref(t), ms); RETURN_TACTIC(new_t); Z3_CATCH_RETURN(nullptr); } Z3_tactic Z3_API Z3_tactic_when(Z3_context c, Z3_probe p, Z3_tactic t) { Z3_TRY; LOG_Z3_tactic_when(c, p, t); RESET_ERROR_CODE(); tactic * new_t = when(to_probe_ref(p), to_tactic_ref(t)); RETURN_TACTIC(new_t); Z3_CATCH_RETURN(nullptr); } Z3_tactic Z3_API Z3_tactic_cond(Z3_context c, Z3_probe p, Z3_tactic t1, Z3_tactic t2) { Z3_TRY; LOG_Z3_tactic_cond(c, p, t1, t2); RESET_ERROR_CODE(); tactic * new_t = cond(to_probe_ref(p), to_tactic_ref(t1), to_tactic_ref(t2)); RETURN_TACTIC(new_t); Z3_CATCH_RETURN(nullptr); } Z3_tactic Z3_API Z3_tactic_repeat(Z3_context c, Z3_tactic t, unsigned max) { Z3_TRY; LOG_Z3_tactic_repeat(c, t, max); RESET_ERROR_CODE(); tactic * new_t = repeat(to_tactic_ref(t), max); RETURN_TACTIC(new_t); Z3_CATCH_RETURN(nullptr); } Z3_tactic Z3_API Z3_tactic_skip(Z3_context c) { Z3_TRY; LOG_Z3_tactic_skip(c); RESET_ERROR_CODE(); tactic * new_t = mk_skip_tactic(); RETURN_TACTIC(new_t); Z3_CATCH_RETURN(nullptr); } Z3_tactic Z3_API Z3_tactic_fail(Z3_context c) { Z3_TRY; LOG_Z3_tactic_fail(c); RESET_ERROR_CODE(); tactic * new_t = mk_fail_tactic(); RETURN_TACTIC(new_t); Z3_CATCH_RETURN(nullptr); } Z3_tactic Z3_API Z3_tactic_fail_if(Z3_context c, Z3_probe p) { Z3_TRY; LOG_Z3_tactic_fail_if(c, p); RESET_ERROR_CODE(); tactic * new_t = fail_if(to_probe_ref(p)); RETURN_TACTIC(new_t); Z3_CATCH_RETURN(nullptr); } Z3_tactic Z3_API Z3_tactic_fail_if_not_decided(Z3_context c) { Z3_TRY; LOG_Z3_tactic_fail_if_not_decided(c); RESET_ERROR_CODE(); tactic * new_t = mk_fail_if_undecided_tactic(); RETURN_TACTIC(new_t); Z3_CATCH_RETURN(nullptr); } Z3_tactic Z3_API Z3_tactic_using_params(Z3_context c, Z3_tactic t, Z3_params p) { Z3_TRY; LOG_Z3_tactic_using_params(c, t, p); RESET_ERROR_CODE(); param_descrs r; to_tactic_ref(t)->collect_param_descrs(r); to_param_ref(p).validate(r); tactic * new_t = using_params(to_tactic_ref(t), to_param_ref(p)); RETURN_TACTIC(new_t); Z3_CATCH_RETURN(nullptr); } Z3_probe Z3_API Z3_probe_const(Z3_context c, double val) { Z3_TRY; LOG_Z3_probe_const(c, val); RESET_ERROR_CODE(); probe * new_p = mk_const_probe(val); RETURN_PROBE(new_p); Z3_CATCH_RETURN(nullptr); } Z3_probe Z3_API Z3_probe_lt(Z3_context c, Z3_probe p1, Z3_probe p2) { Z3_TRY; LOG_Z3_probe_lt(c, p1, p2); RESET_ERROR_CODE(); probe * new_p = mk_lt(to_probe_ref(p1), to_probe_ref(p2)); RETURN_PROBE(new_p); Z3_CATCH_RETURN(nullptr); } Z3_probe Z3_API Z3_probe_gt(Z3_context c, Z3_probe p1, Z3_probe p2) { Z3_TRY; LOG_Z3_probe_gt(c, p1, p2); RESET_ERROR_CODE(); probe * new_p = mk_gt(to_probe_ref(p1), to_probe_ref(p2)); RETURN_PROBE(new_p); Z3_CATCH_RETURN(nullptr); } Z3_probe Z3_API Z3_probe_le(Z3_context c, Z3_probe p1, Z3_probe p2) { Z3_TRY; LOG_Z3_probe_le(c, p1, p2); RESET_ERROR_CODE(); probe * new_p = mk_le(to_probe_ref(p1), to_probe_ref(p2)); RETURN_PROBE(new_p); Z3_CATCH_RETURN(nullptr); } Z3_probe Z3_API Z3_probe_ge(Z3_context c, Z3_probe p1, Z3_probe p2) { Z3_TRY; LOG_Z3_probe_ge(c, p1, p2); RESET_ERROR_CODE(); probe * new_p = mk_ge(to_probe_ref(p1), to_probe_ref(p2)); RETURN_PROBE(new_p); Z3_CATCH_RETURN(nullptr); } Z3_probe Z3_API Z3_probe_eq(Z3_context c, Z3_probe p1, Z3_probe p2) { Z3_TRY; LOG_Z3_probe_eq(c, p1, p2); RESET_ERROR_CODE(); probe * new_p = mk_eq(to_probe_ref(p1), to_probe_ref(p2)); RETURN_PROBE(new_p); Z3_CATCH_RETURN(nullptr); } Z3_probe Z3_API Z3_probe_and(Z3_context c, Z3_probe p1, Z3_probe p2) { Z3_TRY; LOG_Z3_probe_and(c, p1, p2); RESET_ERROR_CODE(); probe * new_p = mk_and(to_probe_ref(p1), to_probe_ref(p2)); RETURN_PROBE(new_p); Z3_CATCH_RETURN(nullptr); } Z3_probe Z3_API Z3_probe_or(Z3_context c, Z3_probe p1, Z3_probe p2) { Z3_TRY; LOG_Z3_probe_or(c, p1, p2); RESET_ERROR_CODE(); probe * new_p = mk_or(to_probe_ref(p1), to_probe_ref(p2)); RETURN_PROBE(new_p); Z3_CATCH_RETURN(nullptr); } Z3_probe Z3_API Z3_probe_not(Z3_context c, Z3_probe p) { Z3_TRY; LOG_Z3_probe_not(c, p); RESET_ERROR_CODE(); probe * new_p = mk_not(to_probe_ref(p)); RETURN_PROBE(new_p); Z3_CATCH_RETURN(nullptr); } unsigned Z3_API Z3_get_num_tactics(Z3_context c) { Z3_TRY; LOG_Z3_get_num_tactics(c); RESET_ERROR_CODE(); return mk_c(c)->num_tactics(); Z3_CATCH_RETURN(0); } Z3_string Z3_API Z3_get_tactic_name(Z3_context c, unsigned idx) { Z3_TRY; LOG_Z3_get_tactic_name(c, idx); RESET_ERROR_CODE(); if (idx >= mk_c(c)->num_tactics()) { SET_ERROR_CODE(Z3_IOB, nullptr); return ""; } return mk_c(c)->get_tactic(idx)->get_name().bare_str(); Z3_CATCH_RETURN(""); } unsigned Z3_API Z3_get_num_probes(Z3_context c) { Z3_TRY; LOG_Z3_get_num_probes(c); RESET_ERROR_CODE(); return mk_c(c)->num_probes(); Z3_CATCH_RETURN(0); } Z3_string Z3_API Z3_get_probe_name(Z3_context c, unsigned idx) { Z3_TRY; LOG_Z3_get_probe_name(c, idx); RESET_ERROR_CODE(); if (idx >= mk_c(c)->num_probes()) { SET_ERROR_CODE(Z3_IOB, nullptr); return ""; } return mk_c(c)->get_probe(idx)->get_name().bare_str(); Z3_CATCH_RETURN(""); } Z3_string Z3_API Z3_tactic_get_help(Z3_context c, Z3_tactic t) { Z3_TRY; LOG_Z3_tactic_get_help(c, t); RESET_ERROR_CODE(); std::ostringstream buffer; param_descrs descrs; to_tactic_ref(t)->collect_param_descrs(descrs); descrs.display(buffer); return mk_c(c)->mk_external_string(buffer.str()); Z3_CATCH_RETURN(""); } Z3_param_descrs Z3_API Z3_tactic_get_param_descrs(Z3_context c, Z3_tactic t) { Z3_TRY; LOG_Z3_tactic_get_param_descrs(c, t); RESET_ERROR_CODE(); Z3_param_descrs_ref * d = alloc(Z3_param_descrs_ref, *mk_c(c)); mk_c(c)->save_object(d); to_tactic_ref(t)->collect_param_descrs(d->m_descrs); Z3_param_descrs r = of_param_descrs(d); RETURN_Z3(r); Z3_CATCH_RETURN(nullptr); } Z3_string Z3_API Z3_tactic_get_descr(Z3_context c, Z3_string name) { Z3_TRY; LOG_Z3_tactic_get_descr(c, name); RESET_ERROR_CODE(); tactic_cmd * t = mk_c(c)->find_tactic_cmd(symbol(name)); if (t == nullptr) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return ""; } return t->get_descr(); Z3_CATCH_RETURN(""); } Z3_string Z3_API Z3_probe_get_descr(Z3_context c, Z3_string name) { Z3_TRY; LOG_Z3_probe_get_descr(c, name); RESET_ERROR_CODE(); probe_info * p = mk_c(c)->find_probe(symbol(name)); if (p == nullptr) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return ""; } return p->get_descr(); Z3_CATCH_RETURN(""); } static Z3_apply_result _tactic_apply(Z3_context c, Z3_tactic t, Z3_goal g, params_ref p) { goal_ref new_goal; new_goal = alloc(goal, *to_goal_ref(g)); Z3_apply_result_ref * ref = alloc(Z3_apply_result_ref, (*mk_c(c)), mk_c(c)->m()); mk_c(c)->save_object(ref); unsigned timeout = p.get_uint("timeout", UINT_MAX); bool use_ctrl_c = p.get_bool("ctrl_c", false); cancel_eh eh(mk_c(c)->m().limit()); to_tactic_ref(t)->updt_params(p); api::context::set_interruptable si(*(mk_c(c)), eh); { scoped_ctrl_c ctrlc(eh, false, use_ctrl_c); scoped_timer timer(timeout, &eh); try { exec(*to_tactic_ref(t), new_goal, ref->m_subgoals); ref->m_pc = new_goal->pc(); ref->m_mc = new_goal->mc(); return of_apply_result(ref); } catch (z3_exception & ex) { mk_c(c)->handle_exception(ex); return nullptr; } } } double Z3_API Z3_probe_apply(Z3_context c, Z3_probe p, Z3_goal g) { Z3_TRY; LOG_Z3_probe_apply(c, p, g); RESET_ERROR_CODE(); return to_probe_ref(p)->operator()(*to_goal_ref(g)).get_value(); Z3_CATCH_RETURN(0); } Z3_apply_result Z3_API Z3_tactic_apply(Z3_context c, Z3_tactic t, Z3_goal g) { Z3_TRY; LOG_Z3_tactic_apply(c, t, g); RESET_ERROR_CODE(); params_ref p; Z3_apply_result r = _tactic_apply(c, t, g, p); RETURN_Z3(r); Z3_CATCH_RETURN(nullptr); } Z3_apply_result Z3_API Z3_tactic_apply_ex(Z3_context c, Z3_tactic t, Z3_goal g, Z3_params p) { Z3_TRY; LOG_Z3_tactic_apply_ex(c, t, g, p); RESET_ERROR_CODE(); param_descrs pd; to_tactic_ref(t)->collect_param_descrs(pd); to_param_ref(p).validate(pd); Z3_apply_result r = _tactic_apply(c, t, g, to_param_ref(p)); RETURN_Z3(r); Z3_CATCH_RETURN(nullptr); } void Z3_API Z3_apply_result_inc_ref(Z3_context c, Z3_apply_result r) { Z3_TRY; LOG_Z3_apply_result_inc_ref(c, r); RESET_ERROR_CODE(); to_apply_result(r)->inc_ref(); Z3_CATCH; } void Z3_API Z3_apply_result_dec_ref(Z3_context c, Z3_apply_result r) { Z3_TRY; LOG_Z3_apply_result_dec_ref(c, r); RESET_ERROR_CODE(); to_apply_result(r)->dec_ref(); Z3_CATCH; } Z3_string Z3_API Z3_apply_result_to_string(Z3_context c, Z3_apply_result r) { Z3_TRY; LOG_Z3_apply_result_to_string(c, r); RESET_ERROR_CODE(); std::ostringstream buffer; buffer << "(goals\n"; unsigned sz = to_apply_result(r)->m_subgoals.size(); for (unsigned i = 0; i < sz; i++) { to_apply_result(r)->m_subgoals[i]->display(buffer); } buffer << ")"; return mk_c(c)->mk_external_string(buffer.str()); Z3_CATCH_RETURN(""); } unsigned Z3_API Z3_apply_result_get_num_subgoals(Z3_context c, Z3_apply_result r) { Z3_TRY; LOG_Z3_apply_result_get_num_subgoals(c, r); RESET_ERROR_CODE(); return to_apply_result(r)->m_subgoals.size(); Z3_CATCH_RETURN(0); } Z3_goal Z3_API Z3_apply_result_get_subgoal(Z3_context c, Z3_apply_result r, unsigned i) { Z3_TRY; LOG_Z3_apply_result_get_subgoal(c, r, i); RESET_ERROR_CODE(); if (i > to_apply_result(r)->m_subgoals.size()) { SET_ERROR_CODE(Z3_IOB, nullptr); RETURN_Z3(nullptr); } Z3_goal_ref * g = alloc(Z3_goal_ref, *mk_c(c)); g->m_goal = to_apply_result(r)->m_subgoals[i]; mk_c(c)->save_object(g); Z3_goal result = of_goal(g); RETURN_Z3(result); Z3_CATCH_RETURN(nullptr); } }; z3-z3-4.8.7/src/api/api_tactic.h000066400000000000000000000033121356505360400162370ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_tactic.h Abstract: API for creating tactics and goals. Author: Leonardo de Moura (leonardo) 2012-03-06. Revision History: --*/ #ifndef API_TACTIC_H_ #define API_TACTIC_H_ #include "api/api_goal.h" #include "tactic/tactical.h" namespace api { class context; } struct Z3_tactic_ref : public api::object { tactic_ref m_tactic; Z3_tactic_ref(api::context& c): api::object(c) {} ~Z3_tactic_ref() override {} }; struct Z3_probe_ref : public api::object { probe_ref m_probe; Z3_probe_ref(api::context& c):api::object(c) {} ~Z3_probe_ref() override {} }; inline Z3_tactic_ref * to_tactic(Z3_tactic g) { return reinterpret_cast(g); } inline Z3_tactic of_tactic(Z3_tactic_ref * g) { return reinterpret_cast(g); } inline tactic * to_tactic_ref(Z3_tactic g) { return g == nullptr ? nullptr : to_tactic(g)->m_tactic.get(); } inline Z3_probe_ref * to_probe(Z3_probe g) { return reinterpret_cast(g); } inline Z3_probe of_probe(Z3_probe_ref * g) { return reinterpret_cast(g); } inline probe * to_probe_ref(Z3_probe g) { return g == nullptr ? nullptr : to_probe(g)->m_probe.get(); } struct Z3_apply_result_ref : public api::object { goal_ref_buffer m_subgoals; model_converter_ref m_mc; proof_converter_ref m_pc; Z3_apply_result_ref(api::context& c, ast_manager & m); ~Z3_apply_result_ref() override {} }; inline Z3_apply_result_ref * to_apply_result(Z3_apply_result g) { return reinterpret_cast(g); } inline Z3_apply_result of_apply_result(Z3_apply_result_ref * g) { return reinterpret_cast(g); } #endif z3-z3-4.8.7/src/api/api_util.h000066400000000000000000000167521356505360400157610ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: api_util.h Abstract: Goodies used to build the Z3 external API. Author: Leonardo de Moura (leonardo) 2012-02-29. Revision History: --*/ #ifndef API_UTIL_H_ #define API_UTIL_H_ #include "util/params.h" #include "util/lbool.h" #include "ast/ast.h" #define Z3_TRY try { #define Z3_CATCH_CORE(CODE) } catch (z3_exception & ex) { mk_c(c)->handle_exception(ex); CODE } #define Z3_CATCH Z3_CATCH_CORE(return;) #define Z3_CATCH_RETURN(VAL) Z3_CATCH_CORE(return VAL;) #define Z3_CATCH_RETURN_NO_HANDLE(VAL) } catch (z3_exception &) { return VAL; } #define CHECK_REF_COUNT(a) (reinterpret_cast(a)->get_ref_count() > 0) namespace api { class context; // Generic wrapper for ref-count objects exposed by the API class object { unsigned m_ref_count; unsigned m_id; context& m_context; public: object(context& c); virtual ~object() {} unsigned ref_count() const { return m_ref_count; } unsigned id() const { return m_id; } void inc_ref(); void dec_ref(); }; }; inline ast * to_ast(Z3_ast a) { return reinterpret_cast(a); } inline Z3_ast of_ast(ast* a) { return reinterpret_cast(a); } inline expr * to_expr(Z3_ast a) { return reinterpret_cast(a); } inline Z3_ast of_expr(expr* e) { return reinterpret_cast(e); } inline expr * const * to_exprs(unsigned n, Z3_ast const* a) { return reinterpret_cast(a); } inline Z3_ast * const * of_exprs(expr* const* e) { return reinterpret_cast(e); } inline app * to_app(Z3_app a) { return reinterpret_cast(a); } inline app * to_app(Z3_ast a) { return reinterpret_cast(a); } inline Z3_app of_app(app* a) { return reinterpret_cast(a); } inline app * const* to_apps(Z3_ast const* a) { return reinterpret_cast(a); } inline ast * const * to_asts(Z3_ast const* a) { return reinterpret_cast(a); } inline sort * to_sort(Z3_sort a) { return reinterpret_cast(a); } inline Z3_sort of_sort(sort* s) { return reinterpret_cast(s); } inline sort * const * to_sorts(Z3_sort const* a) { return reinterpret_cast(a); } inline Z3_sort const * of_sorts(sort* const* s) { return reinterpret_cast(s); } inline func_decl * to_func_decl(Z3_func_decl a) { return reinterpret_cast(a); } inline Z3_func_decl of_func_decl(func_decl* f) { return reinterpret_cast(f); } inline func_decl * const * to_func_decls(Z3_func_decl const* f) { return reinterpret_cast(f); } inline symbol to_symbol(Z3_symbol s) { return symbol::mk_symbol_from_c_ptr(reinterpret_cast(s)); } inline Z3_symbol of_symbol(symbol s) { return reinterpret_cast(const_cast(s.c_ptr())); } inline Z3_pattern of_pattern(ast* a) { return reinterpret_cast(a); } inline app* to_pattern(Z3_pattern p) { return reinterpret_cast(p); } inline Z3_lbool of_lbool(lbool b) { return static_cast(b); } inline lbool to_lbool(Z3_lbool b) { return static_cast(b); } struct Z3_params_ref : public api::object { params_ref m_params; Z3_params_ref(api::context& c): api::object(c) {} ~Z3_params_ref() override {} }; inline Z3_params_ref * to_params(Z3_params p) { return reinterpret_cast(p); } inline Z3_params of_params(Z3_params_ref * p) { return reinterpret_cast(p); } inline params_ref to_param_ref(Z3_params p) { return p == nullptr ? params_ref() : to_params(p)->m_params; } struct Z3_param_descrs_ref : public api::object { param_descrs m_descrs; Z3_param_descrs_ref(api::context& c): api::object(c) {} ~Z3_param_descrs_ref() override {} }; inline Z3_param_descrs_ref * to_param_descrs(Z3_param_descrs p) { return reinterpret_cast(p); } inline Z3_param_descrs of_param_descrs(Z3_param_descrs_ref * p) { return reinterpret_cast(p); } inline param_descrs * to_param_descrs_ptr(Z3_param_descrs p) { return p == nullptr ? nullptr : &(to_param_descrs(p)->m_descrs); } #define SKIP ((void) 0) #define MK_UNARY_BODY(NAME, FID, OP, EXTRA_CODE) \ Z3_TRY; \ RESET_ERROR_CODE(); \ EXTRA_CODE; \ expr * _n = to_expr(n); \ ast* a = mk_c(c)->m().mk_app(FID, OP, 0, 0, 1, &_n); \ mk_c(c)->save_ast_trail(a); \ check_sorts(c, a); \ RETURN_Z3(of_ast(a)); \ Z3_CATCH_RETURN(0); #define MK_UNARY(NAME, FID, OP, EXTRA_CODE) \ Z3_ast Z3_API NAME(Z3_context c, Z3_ast n) { \ LOG_ ## NAME(c, n); \ MK_UNARY_BODY(NAME, FID, OP, EXTRA_CODE); \ } #define MK_BINARY_BODY(NAME, FID, OP, EXTRA_CODE) \ Z3_TRY; \ RESET_ERROR_CODE(); \ EXTRA_CODE; \ expr * args[2] = { to_expr(n1), to_expr(n2) }; \ ast* a = mk_c(c)->m().mk_app(FID, OP, 0, 0, 2, args); \ mk_c(c)->save_ast_trail(a); \ check_sorts(c, a); \ RETURN_Z3(of_ast(a)); \ Z3_CATCH_RETURN(0); #define MK_BINARY(NAME, FID, OP, EXTRA_CODE) \ Z3_ast Z3_API NAME(Z3_context c, Z3_ast n1, Z3_ast n2) { \ LOG_ ## NAME(c, n1, n2); \ MK_BINARY_BODY(NAME, FID, OP, EXTRA_CODE); \ } #define MK_TERNARY_BODY(NAME, FID, OP, EXTRA_CODE) \ Z3_TRY; \ RESET_ERROR_CODE(); \ EXTRA_CODE; \ expr * args[3] = { to_expr(n1), to_expr(n2), to_expr(n3) }; \ ast* a = mk_c(c)->m().mk_app(FID, OP, 0, 0, 3, args); \ mk_c(c)->save_ast_trail(a); \ check_sorts(c, a); \ RETURN_Z3(of_ast(a)); \ Z3_CATCH_RETURN(0); #define MK_TERNARY(NAME, FID, OP, EXTRA_CODE) \ Z3_ast Z3_API NAME(Z3_context c, Z3_ast n1, Z3_ast n2, Z3_ast n3) { \ LOG_ ## NAME(c, n1, n2, n3); \ MK_TERNARY_BODY(NAME, FID, OP, EXTRA_CODE); \ } #define MK_NARY(NAME, FID, OP, EXTRA_CODE) \ Z3_ast Z3_API NAME(Z3_context c, unsigned num_args, Z3_ast const* args) { \ Z3_TRY; \ LOG_ ## NAME(c, num_args, args); \ RESET_ERROR_CODE(); \ EXTRA_CODE; \ ast* a = mk_c(c)->m().mk_app(FID, OP, 0, 0, num_args, to_exprs(num_args, args)); \ mk_c(c)->save_ast_trail(a); \ check_sorts(c, a); \ RETURN_Z3(of_ast(a)); \ Z3_CATCH_RETURN(0); \ } #endif z3-z3-4.8.7/src/api/c++/000077500000000000000000000000001356505360400143375ustar00rootroot00000000000000z3-z3-4.8.7/src/api/c++/z3++.h000066400000000000000000004540351356505360400152050ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Thin C++ layer on top of the Z3 C API. Main features: - Smart pointers for all Z3 objects. - Object-Oriented interface. - Operator overloading. - Exceptions for signaling Z3 errors The C API can be used simultaneously with the C++ layer. However, if you use the C API directly, you will have to check the error conditions manually. Of course, you can invoke the method check_error() of the context object. Author: Leonardo (leonardo) 2012-03-28 Notes: --*/ #ifndef Z3PP_H_ #define Z3PP_H_ #include #include #include #include #include #include #undef min #undef max /** \defgroup cppapi C++ API */ /*@{*/ /** @name C++ API classes and functions */ /*@{*/ /** \brief Z3 C++ namespace */ namespace z3 { class exception; class config; class context; class symbol; class params; class param_descrs; class ast; class sort; class func_decl; class expr; class solver; class goal; class tactic; class probe; class model; class func_interp; class func_entry; class statistics; class apply_result; template class ast_vector_tpl; typedef ast_vector_tpl ast_vector; typedef ast_vector_tpl expr_vector; typedef ast_vector_tpl sort_vector; typedef ast_vector_tpl func_decl_vector; inline void set_param(char const * param, char const * value) { Z3_global_param_set(param, value); } inline void set_param(char const * param, bool value) { Z3_global_param_set(param, value ? "true" : "false"); } inline void set_param(char const * param, int value) { std::ostringstream oss; oss << value; Z3_global_param_set(param, oss.str().c_str()); } inline void reset_params() { Z3_global_param_reset_all(); } /** \brief Exception used to sign API usage errors. */ class exception { std::string m_msg; public: exception(char const * msg):m_msg(msg) {} char const * msg() const { return m_msg.c_str(); } friend std::ostream & operator<<(std::ostream & out, exception const & e); }; inline std::ostream & operator<<(std::ostream & out, exception const & e) { out << e.msg(); return out; } #if !defined(Z3_THROW) #if __cpp_exceptions || _CPPUNWIND || __EXCEPTIONS #define Z3_THROW(x) throw x #else #define Z3_THROW(x) {} #endif #endif // !defined(Z3_THROW) /** \brief Z3 global configuration object. */ class config { Z3_config m_cfg; config(config const & s); config & operator=(config const & s); public: config() { m_cfg = Z3_mk_config(); } ~config() { Z3_del_config(m_cfg); } operator Z3_config() const { return m_cfg; } /** \brief Set global parameter \c param with string \c value. */ void set(char const * param, char const * value) { Z3_set_param_value(m_cfg, param, value); } /** \brief Set global parameter \c param with Boolean \c value. */ void set(char const * param, bool value) { Z3_set_param_value(m_cfg, param, value ? "true" : "false"); } /** \brief Set global parameter \c param with integer \c value. */ void set(char const * param, int value) { std::ostringstream oss; oss << value; Z3_set_param_value(m_cfg, param, oss.str().c_str()); } }; enum check_result { unsat, sat, unknown }; enum rounding_mode { RNA, RNE, RTP, RTN, RTZ }; inline check_result to_check_result(Z3_lbool l) { if (l == Z3_L_TRUE) return sat; else if (l == Z3_L_FALSE) return unsat; return unknown; } /** \brief A Context manages all other Z3 objects, global configuration options, etc. */ class context { private: bool m_enable_exceptions; rounding_mode m_rounding_mode; Z3_context m_ctx; void init(config & c) { m_ctx = Z3_mk_context_rc(c); m_enable_exceptions = true; m_rounding_mode = RNA; Z3_set_error_handler(m_ctx, 0); Z3_set_ast_print_mode(m_ctx, Z3_PRINT_SMTLIB2_COMPLIANT); } context(context const & s); context & operator=(context const & s); public: context() { config c; init(c); } context(config & c) { init(c); } ~context() { Z3_del_context(m_ctx); } operator Z3_context() const { return m_ctx; } /** \brief Auxiliary method used to check for API usage errors. */ Z3_error_code check_error() const { Z3_error_code e = Z3_get_error_code(m_ctx); if (e != Z3_OK && enable_exceptions()) Z3_THROW(exception(Z3_get_error_msg(m_ctx, e))); return e; } void check_parser_error() const { check_error(); } /** \brief The C++ API uses by defaults exceptions on errors. For applications that don't work well with exceptions (there should be only few) you have the ability to turn off exceptions. The tradeoffs are that applications have to be very careful about using check_error() after calls that may result in an erroneous state. */ void set_enable_exceptions(bool f) { m_enable_exceptions = f; } bool enable_exceptions() const { return m_enable_exceptions; } /** \brief Update global parameter \c param with string \c value. */ void set(char const * param, char const * value) { Z3_update_param_value(m_ctx, param, value); } /** \brief Update global parameter \c param with Boolean \c value. */ void set(char const * param, bool value) { Z3_update_param_value(m_ctx, param, value ? "true" : "false"); } /** \brief Update global parameter \c param with Integer \c value. */ void set(char const * param, int value) { std::ostringstream oss; oss << value; Z3_update_param_value(m_ctx, param, oss.str().c_str()); } /** \brief Interrupt the current procedure being executed by any object managed by this context. This is a soft interruption: there is no guarantee the object will actually stop. */ void interrupt() { Z3_interrupt(m_ctx); } /** \brief Create a Z3 symbol based on the given string. */ symbol str_symbol(char const * s); /** \brief Create a Z3 symbol based on the given integer. */ symbol int_symbol(int n); /** \brief Return the Boolean sort. */ sort bool_sort(); /** \brief Return the integer sort. */ sort int_sort(); /** \brief Return the Real sort. */ sort real_sort(); /** \brief Return the Bit-vector sort of size \c sz. That is, the sort for bit-vectors of size \c sz. */ sort bv_sort(unsigned sz); /** \brief Return the sort for ASCII strings. */ sort string_sort(); /** \brief Return a sequence sort over base sort \c s. */ sort seq_sort(sort& s); /** \brief Return a regular expression sort over sequences \c seq_sort. */ sort re_sort(sort& seq_sort); /** \brief Return an array sort for arrays from \c d to \c r. Example: Given a context \c c, c.array_sort(c.int_sort(), c.bool_sort()) is an array sort from integer to Boolean. */ sort array_sort(sort d, sort r); sort array_sort(sort_vector const& d, sort r); /** \brief Return a floating point sort. \c ebits is a number of exponent bits, \c sbits is a number of significand bits, \pre where ebits must be larger than 1 and sbits must be larger than 2. */ sort fpa_sort(unsigned ebits, unsigned sbits); /** \brief Return a FloatingPoint sort with given precision bitwidth (16, 32, 64 or 128). */ template sort fpa_sort(); /** \brief Return a RoundingMode sort. */ sort fpa_rounding_mode(); /** \brief Sets RoundingMode of FloatingPoints. */ void set_rounding_mode(rounding_mode rm); /** \brief Return an enumeration sort: enum_names[0], ..., enum_names[n-1]. \c cs and \c ts are output parameters. The method stores in \c cs the constants corresponding to the enumerated elements, and in \c ts the predicates for testing if terms of the enumeration sort correspond to an enumeration. */ sort enumeration_sort(char const * name, unsigned n, char const * const * enum_names, func_decl_vector & cs, func_decl_vector & ts); /** \brief Return a tuple constructor. \c name is the name of the returned constructor, \c n are the number of arguments, \c names and \c sorts are their projected sorts. \c projs is an output parameter. It contains the set of projection functions. */ func_decl tuple_sort(char const * name, unsigned n, char const * const * names, sort const* sorts, func_decl_vector & projs); /** \brief create an uninterpreted sort with the name given by the string or symbol. */ sort uninterpreted_sort(char const* name); sort uninterpreted_sort(symbol const& name); func_decl function(symbol const & name, unsigned arity, sort const * domain, sort const & range); func_decl function(char const * name, unsigned arity, sort const * domain, sort const & range); func_decl function(symbol const& name, sort_vector const& domain, sort const& range); func_decl function(char const * name, sort_vector const& domain, sort const& range); func_decl function(char const * name, sort const & domain, sort const & range); func_decl function(char const * name, sort const & d1, sort const & d2, sort const & range); func_decl function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & range); func_decl function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & d4, sort const & range); func_decl function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & d4, sort const & d5, sort const & range); func_decl recfun(symbol const & name, unsigned arity, sort const * domain, sort const & range); func_decl recfun(char const * name, unsigned arity, sort const * domain, sort const & range); func_decl recfun(char const * name, sort const & domain, sort const & range); func_decl recfun(char const * name, sort const & d1, sort const & d2, sort const & range); void recdef(func_decl, expr_vector const& args, expr const& body); expr constant(symbol const & name, sort const & s); expr constant(char const * name, sort const & s); expr bool_const(char const * name); expr int_const(char const * name); expr real_const(char const * name); expr bv_const(char const * name, unsigned sz); expr fpa_const(char const * name, unsigned ebits, unsigned sbits); template expr fpa_const(char const * name); expr bool_val(bool b); expr int_val(int n); expr int_val(unsigned n); expr int_val(int64_t n); expr int_val(uint64_t n); expr int_val(char const * n); expr real_val(int n, int d); expr real_val(int n); expr real_val(unsigned n); expr real_val(int64_t n); expr real_val(uint64_t n); expr real_val(char const * n); expr bv_val(int n, unsigned sz); expr bv_val(unsigned n, unsigned sz); expr bv_val(int64_t n, unsigned sz); expr bv_val(uint64_t n, unsigned sz); expr bv_val(char const * n, unsigned sz); expr bv_val(unsigned n, bool const* bits); expr fpa_val(double n); expr fpa_val(float n); expr string_val(char const* s); expr string_val(char const* s, unsigned n); expr string_val(std::string const& s); expr num_val(int n, sort const & s); /** \brief parsing */ expr_vector parse_string(char const* s); expr_vector parse_file(char const* file); expr_vector parse_string(char const* s, sort_vector const& sorts, func_decl_vector const& decls); expr_vector parse_file(char const* s, sort_vector const& sorts, func_decl_vector const& decls); }; template class array { T * m_array; unsigned m_size; array(array const & s); array & operator=(array const & s); public: array(unsigned sz):m_size(sz) { m_array = new T[sz]; } 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]; } T const * ptr() const { return m_array; } T * ptr() { return m_array; } }; class object { protected: context * m_ctx; public: object(context & c):m_ctx(&c) {} object(object const & s):m_ctx(s.m_ctx) {} context & ctx() const { return *m_ctx; } Z3_error_code check_error() const { return m_ctx->check_error(); } friend void check_context(object const & a, object const & b); }; inline void check_context(object const & a, object const & b) { (void)a; (void)b; assert(a.m_ctx == b.m_ctx); } class symbol : public object { Z3_symbol m_sym; public: symbol(context & c, Z3_symbol s):object(c), m_sym(s) {} symbol(symbol const & s):object(s), m_sym(s.m_sym) {} symbol & operator=(symbol const & s) { m_ctx = s.m_ctx; m_sym = s.m_sym; return *this; } operator Z3_symbol() const { return m_sym; } Z3_symbol_kind kind() const { return Z3_get_symbol_kind(ctx(), m_sym); } std::string str() const { assert(kind() == Z3_STRING_SYMBOL); return Z3_get_symbol_string(ctx(), m_sym); } int to_int() const { assert(kind() == Z3_INT_SYMBOL); return Z3_get_symbol_int(ctx(), m_sym); } friend std::ostream & operator<<(std::ostream & out, symbol const & s); }; inline std::ostream & operator<<(std::ostream & out, symbol const & s) { if (s.kind() == Z3_INT_SYMBOL) out << "k!" << s.to_int(); else out << s.str().c_str(); return out; } class param_descrs : public object { Z3_param_descrs m_descrs; public: param_descrs(context& c, Z3_param_descrs d): object(c), m_descrs(d) { Z3_param_descrs_inc_ref(c, d); } param_descrs(param_descrs const& o): object(o.ctx()), m_descrs(o.m_descrs) { Z3_param_descrs_inc_ref(ctx(), m_descrs); } param_descrs& operator=(param_descrs const& o) { Z3_param_descrs_inc_ref(o.ctx(), o.m_descrs); Z3_param_descrs_dec_ref(ctx(), m_descrs); m_descrs = o.m_descrs; m_ctx = o.m_ctx; return *this; } ~param_descrs() { Z3_param_descrs_dec_ref(ctx(), m_descrs); } static param_descrs simplify_param_descrs(context& c) { return param_descrs(c, Z3_simplify_get_param_descrs(c)); } unsigned size() { return Z3_param_descrs_size(ctx(), m_descrs); } symbol name(unsigned i) { return symbol(ctx(), Z3_param_descrs_get_name(ctx(), m_descrs, i)); } Z3_param_kind kind(symbol const& s) { return Z3_param_descrs_get_kind(ctx(), m_descrs, s); } std::string documentation(symbol const& s) { char const* r = Z3_param_descrs_get_documentation(ctx(), m_descrs, s); check_error(); return r; } std::string to_string() const { return Z3_param_descrs_to_string(ctx(), m_descrs); } }; inline std::ostream& operator<<(std::ostream & out, param_descrs const & d) { return out << d.to_string(); } class params : public object { Z3_params m_params; public: params(context & c):object(c) { m_params = Z3_mk_params(c); Z3_params_inc_ref(ctx(), m_params); } params(params const & s):object(s), m_params(s.m_params) { Z3_params_inc_ref(ctx(), m_params); } ~params() { Z3_params_dec_ref(ctx(), m_params); } operator Z3_params() const { return m_params; } params & operator=(params const & s) { Z3_params_inc_ref(s.ctx(), s.m_params); Z3_params_dec_ref(ctx(), m_params); m_ctx = s.m_ctx; m_params = s.m_params; return *this; } void set(char const * k, bool b) { Z3_params_set_bool(ctx(), m_params, ctx().str_symbol(k), b); } void set(char const * k, unsigned n) { Z3_params_set_uint(ctx(), m_params, ctx().str_symbol(k), n); } void set(char const * k, double n) { Z3_params_set_double(ctx(), m_params, ctx().str_symbol(k), n); } void set(char const * k, symbol const & s) { Z3_params_set_symbol(ctx(), m_params, ctx().str_symbol(k), s); } void set(char const * k, char const* s) { Z3_params_set_symbol(ctx(), m_params, ctx().str_symbol(k), ctx().str_symbol(s)); } friend std::ostream & operator<<(std::ostream & out, params const & p); }; inline std::ostream & operator<<(std::ostream & out, params const & p) { out << Z3_params_to_string(p.ctx(), p); return out; } class ast : public object { protected: Z3_ast m_ast; public: ast(context & c):object(c), m_ast(0) {} ast(context & c, Z3_ast n):object(c), m_ast(n) { Z3_inc_ref(ctx(), m_ast); } ast(ast const & s):object(s), m_ast(s.m_ast) { Z3_inc_ref(ctx(), m_ast); } ~ast() { if (m_ast) Z3_dec_ref(*m_ctx, m_ast); } operator Z3_ast() const { return m_ast; } operator bool() const { return m_ast != 0; } ast & operator=(ast const & s) { Z3_inc_ref(s.ctx(), s.m_ast); if (m_ast) Z3_dec_ref(ctx(), m_ast); m_ctx = s.m_ctx; m_ast = s.m_ast; return *this; } Z3_ast_kind kind() const { Z3_ast_kind r = Z3_get_ast_kind(ctx(), m_ast); check_error(); return r; } unsigned hash() const { unsigned r = Z3_get_ast_hash(ctx(), m_ast); check_error(); return r; } friend std::ostream & operator<<(std::ostream & out, ast const & n); std::string to_string() const { return std::string(Z3_ast_to_string(ctx(), m_ast)); } /** \brief Return true if the ASTs are structurally identical. */ friend bool eq(ast const & a, ast const & b); }; inline std::ostream & operator<<(std::ostream & out, ast const & n) { 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); } /** \brief A Z3 sort (aka type). Every expression (i.e., formula or term) in Z3 has a sort. */ class sort : public ast { public: sort(context & c):ast(c) {} sort(context & c, Z3_sort s):ast(c, reinterpret_cast(s)) {} 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. */ sort & operator=(sort const & s) { return static_cast(ast::operator=(s)); } /** \brief Return the internal sort kind. */ Z3_sort_kind sort_kind() const { return Z3_get_sort_kind(*m_ctx, *this); } /** \brief Return name of sort. */ symbol name() const { Z3_symbol s = Z3_get_sort_name(ctx(), *this); check_error(); return symbol(ctx(), s); } /** \brief Return true if this sort is the Boolean sort. */ bool is_bool() const { return sort_kind() == Z3_BOOL_SORT; } /** \brief Return true if this sort is the Integer sort. */ bool is_int() const { return sort_kind() == Z3_INT_SORT; } /** \brief Return true if this sort is the Real sort. */ bool is_real() const { return sort_kind() == Z3_REAL_SORT; } /** \brief Return true if this sort is the Integer or Real sort. */ bool is_arith() const { return is_int() || is_real(); } /** \brief Return true if this sort is a Bit-vector sort. */ bool is_bv() const { return sort_kind() == Z3_BV_SORT; } /** \brief Return true if this sort is a Array sort. */ bool is_array() const { return sort_kind() == Z3_ARRAY_SORT; } /** \brief Return true if this sort is a Datatype sort. */ bool is_datatype() const { return sort_kind() == Z3_DATATYPE_SORT; } /** \brief Return true if this sort is a Relation sort. */ bool is_relation() const { return sort_kind() == Z3_RELATION_SORT; } /** \brief Return true if this sort is a Sequence sort. */ bool is_seq() const { return sort_kind() == Z3_SEQ_SORT; } /** \brief Return true if this sort is a regular expression sort. */ bool is_re() const { return sort_kind() == Z3_RE_SORT; } /** \brief Return true if this sort is a Finite domain sort. */ bool is_finite_domain() const { return sort_kind() == Z3_FINITE_DOMAIN_SORT; } /** \brief Return true if this sort is a Floating point sort. */ bool is_fpa() const { return sort_kind() == Z3_FLOATING_POINT_SORT; } /** \brief Return the size of this Bit-vector sort. \pre is_bv() */ unsigned bv_size() const { assert(is_bv()); unsigned r = Z3_get_bv_sort_size(ctx(), *this); check_error(); return r; } unsigned fpa_ebits() const { assert(is_fpa()); unsigned r = Z3_fpa_get_ebits(ctx(), *this); check_error(); return r; } unsigned fpa_sbits() const { assert(is_fpa()); unsigned r = Z3_fpa_get_sbits(ctx(), *this); check_error(); return r; } /** \brief Return the domain of this Array sort. \pre is_array() */ sort array_domain() const { assert(is_array()); Z3_sort s = Z3_get_array_sort_domain(ctx(), *this); check_error(); return sort(ctx(), s); } /** \brief Return the range of this Array sort. \pre is_array() */ sort array_range() const { assert(is_array()); Z3_sort s = Z3_get_array_sort_range(ctx(), *this); check_error(); return sort(ctx(), s); } }; /** \brief Function declaration (aka function definition). It is the signature of interpreted and uninterpreted functions in Z3. The basic building block in Z3 is the function application. */ class func_decl : public ast { public: func_decl(context & c):ast(c) {} func_decl(context & c, Z3_func_decl n):ast(c, reinterpret_cast(n)) {} func_decl(func_decl const & s):ast(s) {} 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); } symbol name() const { Z3_symbol s = Z3_get_decl_name(ctx(), *this); check_error(); return symbol(ctx(), s); } Z3_decl_kind decl_kind() const { return Z3_get_decl_kind(ctx(), *this); } func_decl transitive_closure(func_decl const&) { Z3_func_decl tc = Z3_mk_transitive_closure(ctx(), *this); check_error(); return func_decl(ctx(), tc); } bool is_const() const { return arity() == 0; } expr operator()() const; expr operator()(unsigned n, expr const * args) const; expr operator()(expr_vector const& v) const; expr operator()(expr const & a) const; expr operator()(int a) const; expr operator()(expr const & a1, expr const & a2) const; expr operator()(expr const & a1, int a2) const; expr operator()(int a1, expr const & a2) const; expr operator()(expr const & a1, expr const & a2, expr const & a3) const; expr operator()(expr const & a1, expr const & a2, expr const & a3, expr const & a4) const; expr operator()(expr const & a1, expr const & a2, expr const & a3, expr const & a4, expr const & a5) const; }; /** \brief forward declarations */ expr select(expr const & a, expr const& i); expr select(expr const & a, expr_vector const & i); /** \brief A Z3 expression is used to represent formulas and terms. For Z3, a formula is any expression of sort Boolean. Every expression has a sort. */ class expr : public ast { public: expr(context & c):ast(c) {} expr(context & c, Z3_ast n):ast(c, reinterpret_cast(n)) {} expr(expr const & n):ast(n) {} expr & operator=(expr const & n) { return static_cast(ast::operator=(n)); } /** \brief Return the sort of this expression. */ sort get_sort() const { Z3_sort s = Z3_get_sort(*m_ctx, m_ast); check_error(); return sort(*m_ctx, s); } /** \brief Return true if this is a Boolean expression. */ bool is_bool() const { return get_sort().is_bool(); } /** \brief Return true if this is an integer expression. */ bool is_int() const { return get_sort().is_int(); } /** \brief Return true if this is a real expression. */ bool is_real() const { return get_sort().is_real(); } /** \brief Return true if this is an integer or real expression. */ bool is_arith() const { return get_sort().is_arith(); } /** \brief Return true if this is a Bit-vector expression. */ bool is_bv() const { return get_sort().is_bv(); } /** \brief Return true if this is a Array expression. */ bool is_array() const { return get_sort().is_array(); } /** \brief Return true if this is a Datatype expression. */ bool is_datatype() const { return get_sort().is_datatype(); } /** \brief Return true if this is a Relation expression. */ bool is_relation() const { return get_sort().is_relation(); } /** \brief Return true if this is a sequence expression. */ bool is_seq() const { return get_sort().is_seq(); } /** \brief Return true if this is a regular expression. */ bool is_re() const { return get_sort().is_re(); } /** \brief Return true if this is a Finite-domain expression. \remark Finite-domain is special kind of interpreted sort: is_bool(), is_bv() and is_finite_domain() are mutually exclusive. */ bool is_finite_domain() const { return get_sort().is_finite_domain(); } /** \brief Return true if this is a FloatingPoint expression. . */ bool is_fpa() const { return get_sort().is_fpa(); } /** \brief Return true if this expression is a numeral. Specialized functions also return representations for the numerals as 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 = 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; } /** \brief Return true if this expression is an application. */ bool is_app() const { return kind() == Z3_APP_AST || kind() == Z3_NUMERAL_AST; } /** \brief Return true if this expression is a constant (i.e., an application with 0 arguments). */ bool is_const() const { return is_app() && num_args() == 0; } /** \brief Return true if this expression is a quantifier. */ bool is_quantifier() const { return kind() == Z3_QUANTIFIER_AST; } /** \brief Return true if this expression is a universal quantifier. */ 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 Z3_is_quantifier_exists(ctx(), m_ast); } /** \brief Return true if this expression is a lambda expression. */ bool is_lambda() const { return Z3_is_lambda(ctx(), m_ast); } /** \brief Return true if this expression is a variable. */ bool is_var() const { return kind() == Z3_VAR_AST; } /** \brief Return true if expression is an algebraic number. */ 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); check_error(); return r; } /** \brief Return string representation of numeral or algebraic number This method assumes the expression is numeral or algebraic \pre is_numeral() || is_algebraic() */ std::string get_decimal_string(int precision) const { assert(is_numeral() || is_algebraic()); 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 It only makes sense to use this function if the caller can ensure that the result is an integer or if exceptions are enabled. If exceptions are disabled, then use the is_numeral_i function. \pre is_numeral() */ int get_numeral_int() const { int result = 0; if (!is_numeral_i(result)) { assert(ctx().enable_exceptions()); if (!ctx().enable_exceptions()) return 0; Z3_THROW(exception("numeral does not fit in machine int")); } return result; } /** \brief Return uint value of numeral, throw if result cannot fit in machine uint It only makes sense to use this function if the caller can ensure that the result is an integer or if exceptions are enabled. If exceptions are disabled, then use the is_numeral_u function. \pre is_numeral() */ unsigned get_numeral_uint() const { assert(is_numeral()); unsigned result = 0; if (!is_numeral_u(result)) { assert(ctx().enable_exceptions()); if (!ctx().enable_exceptions()) return 0; Z3_THROW(exception("numeral does not fit in machine uint")); } return result; } /** \brief Return \c int64_t value of numeral, throw if result cannot fit in \c int64_t. \pre is_numeral() */ int64_t get_numeral_int64() const { assert(is_numeral()); int64_t result = 0; if (!is_numeral_i64(result)) { assert(ctx().enable_exceptions()); if (!ctx().enable_exceptions()) return 0; Z3_THROW(exception("numeral does not fit in machine int64_t")); } return result; } /** \brief Return \c uint64_t value of numeral, throw if result cannot fit in \c uint64_t. \pre is_numeral() */ uint64_t get_numeral_uint64() const { assert(is_numeral()); uint64_t result = 0; if (!is_numeral_u64(result)) { assert(ctx().enable_exceptions()); if (!ctx().enable_exceptions()) return 0; Z3_THROW(exception("numeral does not fit in machine uint64_t")); } return result; } Z3_lbool bool_value() const { return Z3_get_bool_value(ctx(), m_ast); } expr numerator() const { assert(is_numeral()); Z3_ast r = Z3_get_numerator(ctx(), m_ast); check_error(); return expr(ctx(),r); } expr denominator() const { assert(is_numeral()); Z3_ast r = Z3_get_denominator(ctx(), m_ast); check_error(); return expr(ctx(),r); } /** \brief for a string value expression return an escaped or unescaped string value. \pre expression is for a string value. */ std::string get_escaped_string() const { char const* s = Z3_get_string(ctx(), m_ast); check_error(); return std::string(s); } std::string get_string() const { unsigned n; char const* s = Z3_get_lstring(ctx(), m_ast, &n); check_error(); return std::string(s, n); } operator Z3_app() const { assert(is_app()); return reinterpret_cast(m_ast); } /** \brief Return a RoundingMode sort. */ sort fpa_rounding_mode() { assert(is_fpa()); Z3_sort s = ctx().fpa_rounding_mode(); check_error(); return sort(ctx(), s); } /** \brief Return the declaration associated with this application. This method assumes the expression is an application. \pre is_app() */ func_decl decl() const { Z3_func_decl f = Z3_get_app_decl(ctx(), *this); check_error(); return func_decl(ctx(), f); } /** \brief Return the number of arguments in this application. This method assumes the expression is an application. \pre is_app() */ unsigned num_args() const { unsigned r = Z3_get_app_num_args(ctx(), *this); check_error(); return r; } /** \brief Return the i-th argument of this application. This method assumes the expression is an application. \pre is_app() \pre i < num_args() */ expr arg(unsigned i) const { Z3_ast r = Z3_get_app_arg(ctx(), *this, i); check_error(); return expr(ctx(), r); } /** \brief Return the 'body' of this quantifier. \pre is_quantifier() */ expr body() const { assert(is_quantifier()); Z3_ast r = Z3_get_quantifier_body(ctx(), *this); check_error(); return expr(ctx(), r); } /** \brief Return an expression representing not(a). \pre a.is_bool() */ friend expr operator!(expr const & a); /** \brief Return an expression representing a and b. \pre a.is_bool() \pre b.is_bool() */ friend expr operator&&(expr const & a, expr const & b); /** \brief Return an expression representing a and b. The C++ Boolean value \c b is automatically converted into a Z3 Boolean constant. \pre a.is_bool() */ friend expr operator&&(expr const & a, bool b); /** \brief Return an expression representing a and b. The C++ Boolean value \c a is automatically converted into a Z3 Boolean constant. \pre b.is_bool() */ friend expr operator&&(bool a, expr const & b); /** \brief Return an expression representing a or b. \pre a.is_bool() \pre b.is_bool() */ friend expr operator||(expr const & a, expr const & b); /** \brief Return an expression representing a or b. The C++ Boolean value \c b is automatically converted into a Z3 Boolean constant. \pre a.is_bool() */ friend expr operator||(expr const & a, bool b); /** \brief Return an expression representing a or b. The C++ Boolean value \c a is automatically converted into a Z3 Boolean constant. \pre b.is_bool() */ friend expr operator||(bool a, expr const & b); friend expr implies(expr const & a, expr const & b); friend expr implies(expr const & a, bool b); friend expr implies(bool a, expr const & b); friend expr mk_or(expr_vector const& args); friend expr mk_and(expr_vector const& args); friend expr ite(expr const & c, expr const & t, expr const & e); bool is_true() const { return is_app() && Z3_OP_TRUE == decl().decl_kind(); } bool is_false() const { return is_app() && Z3_OP_FALSE == decl().decl_kind(); } bool is_not() const { return is_app() && Z3_OP_NOT == decl().decl_kind(); } bool is_and() const { return is_app() && Z3_OP_AND == decl().decl_kind(); } bool is_or() const { return is_app() && Z3_OP_OR == decl().decl_kind(); } bool is_xor() const { return is_app() && Z3_OP_XOR == decl().decl_kind(); } bool is_implies() const { return is_app() && Z3_OP_IMPLIES == decl().decl_kind(); } bool is_eq() const { return is_app() && Z3_OP_EQ == decl().decl_kind(); } bool is_ite() const { return is_app() && Z3_OP_ITE == decl().decl_kind(); } bool is_distinct() const { return is_app() && Z3_OP_DISTINCT == decl().decl_kind(); } friend expr distinct(expr_vector const& args); friend expr concat(expr const& a, expr const& b); friend expr concat(expr_vector const& args); friend expr operator==(expr const & a, expr const & b); friend expr operator==(expr const & a, int b); friend expr operator==(int a, expr const & b); friend expr operator!=(expr const & a, expr const & b); friend expr operator!=(expr const & a, int b); friend expr operator!=(int a, expr const & b); friend expr operator+(expr const & a, expr const & b); friend expr operator+(expr const & a, int b); friend expr operator+(int a, expr const & b); friend expr sum(expr_vector const& args); friend expr operator*(expr const & a, expr const & b); friend expr operator*(expr const & a, int b); friend expr operator*(int a, expr const & b); /* \brief Power operator */ friend expr pw(expr const & a, expr const & b); friend expr pw(expr const & a, int b); friend expr pw(int a, expr const & b); /* \brief mod operator */ friend expr mod(expr const& a, expr const& b); friend expr mod(expr const& a, int b); friend expr mod(int a, expr const& b); /* \brief rem operator */ friend expr rem(expr const& a, expr const& b); friend expr rem(expr const& a, int b); friend expr rem(int a, expr const& b); friend expr is_int(expr const& e); friend expr operator/(expr const & a, expr const & b); friend expr operator/(expr const & a, int b); friend expr operator/(int a, expr const & b); friend expr operator-(expr const & a); friend expr operator-(expr const & a, expr const & b); friend expr operator-(expr const & a, int b); friend expr operator-(int a, expr const & b); friend expr operator<=(expr const & a, expr const & b); friend expr operator<=(expr const & a, int b); friend expr operator<=(int a, expr const & b); friend expr operator>=(expr const & a, expr const & b); friend expr operator>=(expr const & a, int b); friend expr operator>=(int a, expr const & b); friend expr operator<(expr const & a, expr const & b); friend expr operator<(expr const & a, int b); friend expr operator<(int a, expr const & b); friend expr operator>(expr const & a, expr const & b); friend expr operator>(expr const & a, int b); friend expr operator>(int a, expr const & b); friend expr pble(expr_vector const& es, int const * coeffs, int bound); friend expr pbge(expr_vector const& es, int const * coeffs, int bound); friend expr pbeq(expr_vector const& es, int const * coeffs, int bound); friend expr atmost(expr_vector const& es, unsigned bound); friend expr atleast(expr_vector const& es, unsigned bound); friend expr operator&(expr const & a, expr const & b); friend expr operator&(expr const & a, int b); friend expr operator&(int a, expr const & b); friend expr operator^(expr const & a, expr const & b); friend expr operator^(expr const & a, int b); friend expr operator^(int a, expr const & b); friend expr operator|(expr const & a, expr const & b); friend expr operator|(expr const & a, int b); friend expr operator|(int a, expr const & b); friend expr nand(expr const& a, expr const& b); friend expr nor(expr const& a, expr const& b); friend expr xnor(expr const& a, expr const& b); friend expr min(expr const& a, expr const& b); friend expr max(expr const& a, expr const& b); friend expr bv2int(expr const& a, bool is_signed); friend expr int2bv(unsigned n, expr const& a); friend expr bvadd_no_overflow(expr const& a, expr const& b, bool is_signed); friend expr bvadd_no_underflow(expr const& a, expr const& b); friend expr bvsub_no_overflow(expr const& a, expr const& b); friend expr bvsub_no_underflow(expr const& a, expr const& b, bool is_signed); friend expr bvsdiv_no_overflow(expr const& a, expr const& b); friend expr bvneg_no_overflow(expr const& a); friend expr bvmul_no_overflow(expr const& a, expr const& b, bool is_signed); friend expr bvmul_no_underflow(expr const& a, expr const& b); expr rotate_left(unsigned i) { Z3_ast r = Z3_mk_rotate_left(ctx(), i, *this); ctx().check_error(); return expr(ctx(), r); } expr rotate_right(unsigned i) { Z3_ast r = Z3_mk_rotate_right(ctx(), i, *this); ctx().check_error(); return expr(ctx(), r); } expr repeat(unsigned i) { Z3_ast r = Z3_mk_repeat(ctx(), i, *this); ctx().check_error(); return expr(ctx(), r); } friend expr abs(expr const & a); friend expr sqrt(expr const & a, expr const & rm); friend expr operator~(expr const & a); expr extract(unsigned hi, unsigned lo) const { Z3_ast r = Z3_mk_extract(ctx(), hi, lo, *this); ctx().check_error(); return expr(ctx(), r); } unsigned lo() const { assert (is_app() && Z3_get_decl_num_parameters(ctx(), decl()) == 2); return static_cast(Z3_get_decl_int_parameter(ctx(), decl(), 1)); } unsigned hi() const { assert (is_app() && Z3_get_decl_num_parameters(ctx(), decl()) == 2); return static_cast(Z3_get_decl_int_parameter(ctx(), decl(), 0)); } /** \brief FloatingPoint fused multiply-add. */ friend expr fma(expr const& a, expr const& b, expr const& c); /** \brief sequence and regular expression operations. + is overloaded as sequence concatenation and regular expression union. concat is overloaded to handle sequences and regular expressions */ expr extract(expr const& offset, expr const& length) const { check_context(*this, offset); check_context(offset, length); Z3_ast r = Z3_mk_seq_extract(ctx(), *this, offset, length); check_error(); return expr(ctx(), r); } expr replace(expr const& src, expr const& dst) const { check_context(*this, src); check_context(src, dst); Z3_ast r = Z3_mk_seq_replace(ctx(), *this, src, dst); check_error(); return expr(ctx(), r); } expr unit() const { Z3_ast r = Z3_mk_seq_unit(ctx(), *this); check_error(); return expr(ctx(), r); } expr contains(expr const& s) { check_context(*this, s); Z3_ast r = Z3_mk_seq_contains(ctx(), *this, s); check_error(); return expr(ctx(), r); } expr at(expr const& index) const { check_context(*this, index); Z3_ast r = Z3_mk_seq_at(ctx(), *this, index); check_error(); return expr(ctx(), r); } expr nth(expr const& index) const { check_context(*this, index); Z3_ast r = Z3_mk_seq_nth(ctx(), *this, index); check_error(); return expr(ctx(), r); } expr length() const { Z3_ast r = Z3_mk_seq_length(ctx(), *this); check_error(); return expr(ctx(), r); } expr stoi() const { Z3_ast r = Z3_mk_str_to_int(ctx(), *this); check_error(); return expr(ctx(), r); } expr itos() const { Z3_ast r = Z3_mk_int_to_str(ctx(), *this); check_error(); return expr(ctx(), r); } friend expr range(expr const& lo, expr const& hi); /** \brief create a looping regular expression. */ expr loop(unsigned lo) { Z3_ast r = Z3_mk_re_loop(ctx(), m_ast, lo, 0); check_error(); return expr(ctx(), r); } expr loop(unsigned lo, unsigned hi) { Z3_ast r = Z3_mk_re_loop(ctx(), m_ast, lo, hi); check_error(); return expr(ctx(), r); } /** * index operator defined on arrays and sequences. */ expr operator[](expr const& index) const { assert(is_array() || is_seq()); if (is_array()) { return select(*this, index); } return nth(index); } expr operator[](expr_vector const& index) const { return select(*this, index); } /** \brief Return a simplified version of this expression. */ expr simplify() const { Z3_ast r = Z3_simplify(ctx(), m_ast); check_error(); return expr(ctx(), r); } /** \brief Return a simplified version of this expression. The parameter \c p is a set of parameters for the Z3 simplifier. */ expr simplify(params const & p) const { Z3_ast r = Z3_simplify_ex(ctx(), m_ast, p); check_error(); return expr(ctx(), r); } /** \brief Apply substitution. Replace src expressions by dst. */ expr substitute(expr_vector const& src, expr_vector const& dst); /** \brief Apply substitution. Replace bound variables by expressions. */ expr substitute(expr_vector const& dst); }; #define _Z3_MK_BIN_(a, b, binop) \ check_context(a, b); \ Z3_ast r = binop(a.ctx(), a, b); \ a.check_error(); \ return expr(a.ctx(), r); \ inline expr implies(expr const & a, expr const & b) { assert(a.is_bool() && b.is_bool()); _Z3_MK_BIN_(a, b, Z3_mk_implies); } inline expr implies(expr const & a, bool b) { return implies(a, a.ctx().bool_val(b)); } inline expr implies(bool a, expr const & b) { return implies(b.ctx().bool_val(a), b); } inline expr pw(expr const & a, expr const & b) { _Z3_MK_BIN_(a, b, Z3_mk_power); } inline expr pw(expr const & a, int b) { return pw(a, a.ctx().num_val(b, a.get_sort())); } inline expr pw(int a, expr const & b) { return pw(b.ctx().num_val(a, b.get_sort()), b); } inline expr mod(expr const& a, expr const& b) { if (a.is_bv()) { _Z3_MK_BIN_(a, b, Z3_mk_bvsmod); } else { _Z3_MK_BIN_(a, b, Z3_mk_mod); } } inline expr mod(expr const & a, int b) { return mod(a, a.ctx().num_val(b, a.get_sort())); } inline expr mod(int a, expr const & b) { return mod(b.ctx().num_val(a, b.get_sort()), b); } inline expr operator%(expr const& a, expr const& b) { return mod(a, b); } inline expr operator%(expr const& a, int b) { return mod(a, b); } inline expr operator%(int a, expr const& b) { return mod(a, b); } inline expr rem(expr const& a, expr const& b) { if (a.is_fpa() && b.is_fpa()) { _Z3_MK_BIN_(a, b, Z3_mk_fpa_rem); } else { _Z3_MK_BIN_(a, b, Z3_mk_rem); } } inline expr rem(expr const & a, int b) { return rem(a, a.ctx().num_val(b, a.get_sort())); } inline expr rem(int a, expr const & b) { return rem(b.ctx().num_val(a, b.get_sort()), b); } #undef _Z3_MK_BIN_ #define _Z3_MK_UN_(a, mkun) \ Z3_ast r = mkun(a.ctx(), a); \ a.check_error(); \ return expr(a.ctx(), r); \ inline expr operator!(expr const & a) { assert(a.is_bool()); _Z3_MK_UN_(a, Z3_mk_not); } inline expr is_int(expr const& e) { _Z3_MK_UN_(e, Z3_mk_is_int); } #undef _Z3_MK_UN_ inline expr operator&&(expr const & a, expr const & b) { check_context(a, b); assert(a.is_bool() && b.is_bool()); Z3_ast args[2] = { a, b }; Z3_ast r = Z3_mk_and(a.ctx(), 2, args); a.check_error(); return expr(a.ctx(), r); } inline expr operator&&(expr const & a, bool b) { return a && a.ctx().bool_val(b); } inline expr operator&&(bool a, expr const & b) { return b.ctx().bool_val(a) && b; } inline expr operator||(expr const & a, expr const & b) { check_context(a, b); assert(a.is_bool() && b.is_bool()); Z3_ast args[2] = { a, b }; Z3_ast r = Z3_mk_or(a.ctx(), 2, args); a.check_error(); return expr(a.ctx(), r); } inline expr operator||(expr const & a, bool b) { return a || a.ctx().bool_val(b); } inline expr operator||(bool a, expr const & b) { return b.ctx().bool_val(a) || b; } inline expr operator==(expr const & a, expr const & b) { check_context(a, b); Z3_ast r = Z3_mk_eq(a.ctx(), a, b); a.check_error(); return expr(a.ctx(), r); } inline expr operator==(expr const & a, int b) { assert(a.is_arith() || a.is_bv() || a.is_fpa()); return a == a.ctx().num_val(b, a.get_sort()); } inline expr operator==(int a, expr const & b) { assert(b.is_arith() || b.is_bv() || b.is_fpa()); return b.ctx().num_val(a, b.get_sort()) == b; } inline expr operator!=(expr const & a, expr const & b) { check_context(a, b); Z3_ast args[2] = { a, b }; Z3_ast r = Z3_mk_distinct(a.ctx(), 2, args); a.check_error(); return expr(a.ctx(), r); } inline expr operator!=(expr const & a, int b) { assert(a.is_arith() || a.is_bv() || a.is_fpa()); return a != a.ctx().num_val(b, a.get_sort()); } inline expr operator!=(int a, expr const & b) { assert(b.is_arith() || b.is_bv() || b.is_fpa()); return b.ctx().num_val(a, b.get_sort()) != b; } inline expr operator+(expr const & a, expr const & b) { check_context(a, b); Z3_ast r = 0; if (a.is_arith() && b.is_arith()) { Z3_ast args[2] = { a, b }; r = Z3_mk_add(a.ctx(), 2, args); } else if (a.is_bv() && b.is_bv()) { r = Z3_mk_bvadd(a.ctx(), a, b); } else if (a.is_seq() && b.is_seq()) { return concat(a, b); } else if (a.is_re() && b.is_re()) { Z3_ast _args[2] = { a, b }; r = Z3_mk_re_union(a.ctx(), 2, _args); } else if (a.is_fpa() && b.is_fpa()) { r = Z3_mk_fpa_add(a.ctx(), a.ctx().fpa_rounding_mode(), a, b); } else { // operator is not supported by given arguments. assert(false); } a.check_error(); return expr(a.ctx(), r); } inline expr operator+(expr const & a, int b) { return a + a.ctx().num_val(b, a.get_sort()); } inline expr operator+(int a, expr const & b) { return b.ctx().num_val(a, b.get_sort()) + b; } inline expr operator*(expr const & a, expr const & b) { check_context(a, b); Z3_ast r = 0; if (a.is_arith() && b.is_arith()) { Z3_ast args[2] = { a, b }; r = Z3_mk_mul(a.ctx(), 2, args); } else if (a.is_bv() && b.is_bv()) { r = Z3_mk_bvmul(a.ctx(), a, b); } else if (a.is_fpa() && b.is_fpa()) { r = Z3_mk_fpa_mul(a.ctx(), a.ctx().fpa_rounding_mode(), a, b); } else { // operator is not supported by given arguments. assert(false); } a.check_error(); return expr(a.ctx(), r); } inline expr operator*(expr const & a, int b) { return a * a.ctx().num_val(b, a.get_sort()); } inline expr operator*(int a, expr const & b) { return b.ctx().num_val(a, b.get_sort()) * b; } inline expr operator>=(expr const & a, expr const & b) { check_context(a, b); Z3_ast r = 0; if (a.is_arith() && b.is_arith()) { r = Z3_mk_ge(a.ctx(), a, b); } else if (a.is_bv() && b.is_bv()) { r = Z3_mk_bvsge(a.ctx(), a, b); } else { // operator is not supported by given arguments. assert(false); } a.check_error(); return expr(a.ctx(), r); } inline expr operator/(expr const & a, expr const & b) { check_context(a, b); Z3_ast r = 0; if (a.is_arith() && b.is_arith()) { r = Z3_mk_div(a.ctx(), a, b); } else if (a.is_bv() && b.is_bv()) { r = Z3_mk_bvsdiv(a.ctx(), a, b); } else if (a.is_fpa() && b.is_fpa()) { r = Z3_mk_fpa_div(a.ctx(), a.ctx().fpa_rounding_mode(), a, b); } else { // operator is not supported by given arguments. assert(false); } a.check_error(); return expr(a.ctx(), r); } inline expr operator/(expr const & a, int b) { return a / a.ctx().num_val(b, a.get_sort()); } inline expr operator/(int a, expr const & b) { return b.ctx().num_val(a, b.get_sort()) / b; } inline expr operator-(expr const & a) { Z3_ast r = 0; if (a.is_arith()) { r = Z3_mk_unary_minus(a.ctx(), a); } else if (a.is_bv()) { r = Z3_mk_bvneg(a.ctx(), a); } else if (a.is_fpa()) { r = Z3_mk_fpa_neg(a.ctx(), a); } else { // operator is not supported by given arguments. assert(false); } a.check_error(); return expr(a.ctx(), r); } inline expr operator-(expr const & a, expr const & b) { check_context(a, b); Z3_ast r = 0; if (a.is_arith() && b.is_arith()) { Z3_ast args[2] = { a, b }; r = Z3_mk_sub(a.ctx(), 2, args); } else if (a.is_bv() && b.is_bv()) { r = Z3_mk_bvsub(a.ctx(), a, b); } else if (a.is_fpa() && b.is_fpa()) { r = Z3_mk_fpa_sub(a.ctx(), a.ctx().fpa_rounding_mode(), a, b); } else { // operator is not supported by given arguments. assert(false); } a.check_error(); return expr(a.ctx(), r); } inline expr operator-(expr const & a, int b) { return a - a.ctx().num_val(b, a.get_sort()); } inline expr operator-(int a, expr const & b) { return b.ctx().num_val(a, b.get_sort()) - b; } inline expr operator<=(expr const & a, expr const & b) { check_context(a, b); Z3_ast r = 0; if (a.is_arith() && b.is_arith()) { r = Z3_mk_le(a.ctx(), a, b); } else if (a.is_bv() && b.is_bv()) { r = Z3_mk_bvsle(a.ctx(), a, b); } else if (a.is_fpa() && b.is_fpa()) { r = Z3_mk_fpa_leq(a.ctx(), a, b); } else { // operator is not supported by given arguments. assert(false); } a.check_error(); return expr(a.ctx(), r); } inline expr operator<=(expr const & a, int b) { return a <= a.ctx().num_val(b, a.get_sort()); } inline expr operator<=(int a, expr const & b) { return b.ctx().num_val(a, b.get_sort()) <= b; } inline expr operator>=(expr const & a, int b) { return a >= a.ctx().num_val(b, a.get_sort()); } inline expr operator>=(int a, expr const & b) { return b.ctx().num_val(a, b.get_sort()) >= b; } inline expr operator<(expr const & a, expr const & b) { check_context(a, b); Z3_ast r = 0; if (a.is_arith() && b.is_arith()) { r = Z3_mk_lt(a.ctx(), a, b); } else if (a.is_bv() && b.is_bv()) { r = Z3_mk_bvslt(a.ctx(), a, b); } else if (a.is_fpa() && b.is_fpa()) { r = Z3_mk_fpa_lt(a.ctx(), a, b); } else { // operator is not supported by given arguments. assert(false); } a.check_error(); return expr(a.ctx(), r); } inline expr operator<(expr const & a, int b) { return a < a.ctx().num_val(b, a.get_sort()); } inline expr operator<(int a, expr const & b) { return b.ctx().num_val(a, b.get_sort()) < b; } inline expr operator>(expr const & a, expr const & b) { check_context(a, b); Z3_ast r = 0; if (a.is_arith() && b.is_arith()) { r = Z3_mk_gt(a.ctx(), a, b); } else if (a.is_bv() && b.is_bv()) { r = Z3_mk_bvsgt(a.ctx(), a, b); } else if (a.is_fpa() && b.is_fpa()) { r = Z3_mk_fpa_gt(a.ctx(), a, b); } else { // operator is not supported by given arguments. assert(false); } a.check_error(); return expr(a.ctx(), r); } inline expr operator>(expr const & a, int b) { return a > a.ctx().num_val(b, a.get_sort()); } inline expr operator>(int a, expr const & b) { return b.ctx().num_val(a, b.get_sort()) > b; } inline expr operator&(expr const & a, expr const & b) { check_context(a, b); Z3_ast r = Z3_mk_bvand(a.ctx(), a, b); return expr(a.ctx(), r); } inline expr operator&(expr const & a, int b) { return a & a.ctx().num_val(b, a.get_sort()); } inline expr operator&(int a, expr const & b) { return b.ctx().num_val(a, b.get_sort()) & b; } inline expr operator^(expr const & a, expr const & b) { check_context(a, b); Z3_ast r = Z3_mk_bvxor(a.ctx(), a, b); return expr(a.ctx(), r); } inline expr operator^(expr const & a, int b) { return a ^ a.ctx().num_val(b, a.get_sort()); } inline expr operator^(int a, expr const & b) { return b.ctx().num_val(a, b.get_sort()) ^ b; } inline expr operator|(expr const & a, expr const & b) { check_context(a, b); Z3_ast r = Z3_mk_bvor(a.ctx(), a, b); return expr(a.ctx(), r); } inline expr operator|(expr const & a, int b) { return a | a.ctx().num_val(b, a.get_sort()); } inline expr operator|(int a, expr const & b) { return b.ctx().num_val(a, b.get_sort()) | b; } inline expr nand(expr const& a, expr const& b) { check_context(a, b); Z3_ast r = Z3_mk_bvnand(a.ctx(), a, b); return expr(a.ctx(), r); } inline expr nor(expr const& a, expr const& b) { check_context(a, b); Z3_ast r = Z3_mk_bvnor(a.ctx(), a, b); return expr(a.ctx(), r); } inline expr xnor(expr const& a, expr const& b) { check_context(a, b); Z3_ast r = Z3_mk_bvxnor(a.ctx(), a, b); return expr(a.ctx(), r); } inline expr min(expr const& a, expr const& b) { check_context(a, b); Z3_ast r; if (a.is_arith()) { r = Z3_mk_ite(a.ctx(), Z3_mk_ge(a.ctx(), a, b), b, a); } else if (a.is_bv()) { r = Z3_mk_ite(a.ctx(), Z3_mk_bvuge(a.ctx(), a, b), b, a); } else { assert(a.is_fpa()); r = Z3_mk_fpa_min(a.ctx(), a, b); } return expr(a.ctx(), r); } inline expr max(expr const& a, expr const& b) { check_context(a, b); Z3_ast r; if (a.is_arith()) { r = Z3_mk_ite(a.ctx(), Z3_mk_ge(a.ctx(), a, b), a, b); } else if (a.is_bv()) { r = Z3_mk_ite(a.ctx(), Z3_mk_bvuge(a.ctx(), a, b), a, b); } else { assert(a.is_fpa()); r = Z3_mk_fpa_max(a.ctx(), a, b); } return expr(a.ctx(), r); } inline expr abs(expr const & a) { Z3_ast r; if (a.is_int()) { expr zero = a.ctx().int_val(0); r = Z3_mk_ite(a.ctx(), Z3_mk_ge(a.ctx(), a, zero), a, -a); } else if (a.is_real()) { expr zero = a.ctx().real_val(0); r = Z3_mk_ite(a.ctx(), Z3_mk_ge(a.ctx(), a, zero), a, -a); } else { r = Z3_mk_fpa_abs(a.ctx(), a); } a.check_error(); return expr(a.ctx(), r); } inline expr sqrt(expr const & a, expr const& rm) { check_context(a, rm); assert(a.is_fpa()); Z3_ast r = Z3_mk_fpa_sqrt(a.ctx(), rm, a); a.check_error(); return expr(a.ctx(), r); } inline expr operator~(expr const & a) { Z3_ast r = Z3_mk_bvnot(a.ctx(), a); return expr(a.ctx(), r); } inline expr fma(expr const& a, expr const& b, expr const& c, expr const& rm) { check_context(a, b); check_context(a, c); check_context(a, rm); assert(a.is_fpa() && b.is_fpa() && c.is_fpa()); Z3_ast r = Z3_mk_fpa_fma(a.ctx(), rm, a, b, c); a.check_error(); return expr(a.ctx(), r); } /** \brief Create the if-then-else expression ite(c, t, e) \pre c.is_bool() */ inline expr ite(expr const & c, expr const & t, expr const & e) { check_context(c, t); check_context(c, e); assert(c.is_bool()); Z3_ast r = Z3_mk_ite(c.ctx(), c, t, e); c.check_error(); return expr(c.ctx(), r); } /** \brief Wraps a Z3_ast as an expr object. It also checks for errors. This function allows the user to use the whole C API with the C++ layer defined in this file. */ inline expr to_expr(context & c, Z3_ast a) { c.check_error(); assert(Z3_get_ast_kind(c, a) == Z3_APP_AST || Z3_get_ast_kind(c, a) == Z3_NUMERAL_AST || Z3_get_ast_kind(c, a) == Z3_VAR_AST || Z3_get_ast_kind(c, a) == Z3_QUANTIFIER_AST); return expr(c, a); } inline sort to_sort(context & c, Z3_sort s) { c.check_error(); return sort(c, s); } inline func_decl to_func_decl(context & c, Z3_func_decl f) { c.check_error(); return func_decl(c, f); } /** \brief signed less than or equal to operator for bitvectors. */ inline expr sle(expr const & a, expr const & b) { return to_expr(a.ctx(), Z3_mk_bvsle(a.ctx(), a, b)); } inline expr sle(expr const & a, int b) { return sle(a, a.ctx().num_val(b, a.get_sort())); } inline expr sle(int a, expr const & b) { return sle(b.ctx().num_val(a, b.get_sort()), b); } /** \brief signed less than operator for bitvectors. */ inline expr slt(expr const & a, expr const & b) { return to_expr(a.ctx(), Z3_mk_bvslt(a.ctx(), a, b)); } inline expr slt(expr const & a, int b) { return slt(a, a.ctx().num_val(b, a.get_sort())); } inline expr slt(int a, expr const & b) { return slt(b.ctx().num_val(a, b.get_sort()), b); } /** \brief unsigned less than or equal to operator for bitvectors. */ inline expr ule(expr const & a, expr const & b) { return to_expr(a.ctx(), Z3_mk_bvule(a.ctx(), a, b)); } inline expr ule(expr const & a, int b) { return ule(a, a.ctx().num_val(b, a.get_sort())); } inline expr ule(int a, expr const & b) { return ule(b.ctx().num_val(a, b.get_sort()), b); } /** \brief unsigned less than operator for bitvectors. */ inline expr ult(expr const & a, expr const & b) { return to_expr(a.ctx(), Z3_mk_bvult(a.ctx(), a, b)); } inline expr ult(expr const & a, int b) { return ult(a, a.ctx().num_val(b, a.get_sort())); } inline expr ult(int a, expr const & b) { return ult(b.ctx().num_val(a, b.get_sort()), b); } /** \brief unsigned greater than or equal to operator for bitvectors. */ inline expr uge(expr const & a, expr const & b) { return to_expr(a.ctx(), Z3_mk_bvuge(a.ctx(), a, b)); } inline expr uge(expr const & a, int b) { return uge(a, a.ctx().num_val(b, a.get_sort())); } inline expr uge(int a, expr const & b) { return uge(b.ctx().num_val(a, b.get_sort()), b); } /** \brief unsigned greater than operator for bitvectors. */ inline expr ugt(expr const & a, expr const & b) { return to_expr(a.ctx(), Z3_mk_bvugt(a.ctx(), a, b)); } inline expr ugt(expr const & a, int b) { return ugt(a, a.ctx().num_val(b, a.get_sort())); } inline expr ugt(int a, expr const & b) { return ugt(b.ctx().num_val(a, b.get_sort()), b); } /** \brief unsigned division operator for bitvectors. */ inline expr udiv(expr const & a, expr const & b) { return to_expr(a.ctx(), Z3_mk_bvudiv(a.ctx(), a, b)); } inline expr udiv(expr const & a, int b) { return udiv(a, a.ctx().num_val(b, a.get_sort())); } inline expr udiv(int a, expr const & b) { return udiv(b.ctx().num_val(a, b.get_sort()), b); } /** \brief signed remainder operator for bitvectors */ inline expr srem(expr const & a, expr const & b) { return to_expr(a.ctx(), Z3_mk_bvsrem(a.ctx(), a, b)); } inline expr srem(expr const & a, int b) { return srem(a, a.ctx().num_val(b, a.get_sort())); } inline expr srem(int a, expr const & b) { return srem(b.ctx().num_val(a, b.get_sort()), b); } /** \brief signed modulus operator for bitvectors */ inline expr smod(expr const & a, expr const & b) { return to_expr(a.ctx(), Z3_mk_bvsmod(a.ctx(), a, b)); } inline expr smod(expr const & a, int b) { return smod(a, a.ctx().num_val(b, a.get_sort())); } inline expr smod(int a, expr const & b) { return smod(b.ctx().num_val(a, b.get_sort()), b); } /** \brief unsigned reminder operator for bitvectors */ inline expr urem(expr const & a, expr const & b) { return to_expr(a.ctx(), Z3_mk_bvurem(a.ctx(), a, b)); } inline expr urem(expr const & a, int b) { return urem(a, a.ctx().num_val(b, a.get_sort())); } inline expr urem(int a, expr const & b) { return urem(b.ctx().num_val(a, b.get_sort()), b); } /** \brief shift left operator for bitvectors */ inline expr shl(expr const & a, expr const & b) { return to_expr(a.ctx(), Z3_mk_bvshl(a.ctx(), a, b)); } inline expr shl(expr const & a, int b) { return shl(a, a.ctx().num_val(b, a.get_sort())); } inline expr shl(int a, expr const & b) { return shl(b.ctx().num_val(a, b.get_sort()), b); } /** \brief logic shift right operator for bitvectors */ inline expr lshr(expr const & a, expr const & b) { return to_expr(a.ctx(), Z3_mk_bvlshr(a.ctx(), a, b)); } inline expr lshr(expr const & a, int b) { return lshr(a, a.ctx().num_val(b, a.get_sort())); } inline expr lshr(int a, expr const & b) { return lshr(b.ctx().num_val(a, b.get_sort()), b); } /** \brief arithmetic shift right operator for bitvectors */ inline expr ashr(expr const & a, expr const & b) { return to_expr(a.ctx(), Z3_mk_bvashr(a.ctx(), a, b)); } inline expr ashr(expr const & a, int b) { return ashr(a, a.ctx().num_val(b, a.get_sort())); } inline expr ashr(int a, expr const & b) { return ashr(b.ctx().num_val(a, b.get_sort()), b); } /** \brief Extend the given bit-vector with zeros to the (unsigned) equivalent bitvector of size m+i, where m is the size of the given bit-vector. */ inline expr zext(expr const & a, unsigned i) { return to_expr(a.ctx(), Z3_mk_zero_ext(a.ctx(), i, a)); } /** \brief bit-vector and integer conversions. */ inline expr bv2int(expr const& a, bool is_signed) { Z3_ast r = Z3_mk_bv2int(a.ctx(), a, is_signed); a.check_error(); return expr(a.ctx(), r); } inline expr int2bv(unsigned n, expr const& a) { Z3_ast r = Z3_mk_int2bv(a.ctx(), n, a); a.check_error(); return expr(a.ctx(), r); } /** \brief bit-vector overflow/underflow checks */ inline expr bvadd_no_overflow(expr const& a, expr const& b, bool is_signed) { check_context(a, b); Z3_ast r = Z3_mk_bvadd_no_overflow(a.ctx(), a, b, is_signed); a.check_error(); return expr(a.ctx(), r); } inline expr bvadd_no_underflow(expr const& a, expr const& b) { check_context(a, b); Z3_ast r = Z3_mk_bvadd_no_underflow(a.ctx(), a, b); a.check_error(); return expr(a.ctx(), r); } inline expr bvsub_no_overflow(expr const& a, expr const& b) { check_context(a, b); Z3_ast r = Z3_mk_bvsub_no_overflow(a.ctx(), a, b); a.check_error(); return expr(a.ctx(), r); } inline expr bvsub_no_underflow(expr const& a, expr const& b, bool is_signed) { check_context(a, b); Z3_ast r = Z3_mk_bvsub_no_underflow(a.ctx(), a, b, is_signed); a.check_error(); return expr(a.ctx(), r); } inline expr bvsdiv_no_overflow(expr const& a, expr const& b) { check_context(a, b); Z3_ast r = Z3_mk_bvsdiv_no_overflow(a.ctx(), a, b); a.check_error(); return expr(a.ctx(), r); } inline expr bvneg_no_overflow(expr const& a) { Z3_ast r = Z3_mk_bvneg_no_overflow(a.ctx(), a); a.check_error(); return expr(a.ctx(), r); } inline expr bvmul_no_overflow(expr const& a, expr const& b, bool is_signed) { check_context(a, b); Z3_ast r = Z3_mk_bvmul_no_overflow(a.ctx(), a, b, is_signed); a.check_error(); return expr(a.ctx(), r); } inline expr bvmul_no_underflow(expr const& a, expr const& b) { check_context(a, b); Z3_ast r = Z3_mk_bvmul_no_underflow(a.ctx(), a, b); a.check_error(); return expr(a.ctx(), r); } /** \brief Sign-extend of the given bit-vector to the (signed) equivalent bitvector of size m+i, where m is the size of the given bit-vector. */ inline expr sext(expr const & a, unsigned i) { return to_expr(a.ctx(), Z3_mk_sign_ext(a.ctx(), i, a)); } inline func_decl linear_order(sort const& a, unsigned index) { return to_func_decl(a.ctx(), Z3_mk_linear_order(a.ctx(), a, index)); } inline func_decl partial_order(sort const& a, unsigned index) { return to_func_decl(a.ctx(), Z3_mk_partial_order(a.ctx(), a, index)); } inline func_decl piecewise_linear_order(sort const& a, unsigned index) { return to_func_decl(a.ctx(), Z3_mk_piecewise_linear_order(a.ctx(), a, index)); } inline func_decl tree_order(sort const& a, unsigned index) { return to_func_decl(a.ctx(), Z3_mk_tree_order(a.ctx(), a, index)); } template class cast_ast; template<> class cast_ast { public: ast operator()(context & c, Z3_ast a) { return ast(c, a); } }; template<> class cast_ast { public: expr operator()(context & c, Z3_ast a) { assert(Z3_get_ast_kind(c, a) == Z3_NUMERAL_AST || Z3_get_ast_kind(c, a) == Z3_APP_AST || Z3_get_ast_kind(c, a) == Z3_QUANTIFIER_AST || Z3_get_ast_kind(c, a) == Z3_VAR_AST); return expr(c, a); } }; template<> class cast_ast { public: sort operator()(context & c, Z3_ast a) { assert(Z3_get_ast_kind(c, a) == Z3_SORT_AST); return sort(c, reinterpret_cast(a)); } }; template<> class cast_ast { public: func_decl operator()(context & c, Z3_ast a) { assert(Z3_get_ast_kind(c, a) == Z3_FUNC_DECL_AST); return func_decl(c, reinterpret_cast(a)); } }; template class ast_vector_tpl : public object { Z3_ast_vector m_vector; void init(Z3_ast_vector v) { Z3_ast_vector_inc_ref(ctx(), v); m_vector = v; } public: ast_vector_tpl(context & c):object(c) { init(Z3_mk_ast_vector(c)); } ast_vector_tpl(context & c, Z3_ast_vector v):object(c) { init(v); } ast_vector_tpl(ast_vector_tpl const & s):object(s), m_vector(s.m_vector) { Z3_ast_vector_inc_ref(ctx(), m_vector); } ~ast_vector_tpl() { Z3_ast_vector_dec_ref(ctx(), m_vector); } operator Z3_ast_vector() const { return m_vector; } unsigned size() const { return Z3_ast_vector_size(ctx(), m_vector); } T operator[](int i) const { assert(0 <= i); Z3_ast r = Z3_ast_vector_get(ctx(), m_vector, i); check_error(); return cast_ast()(ctx(), r); } void push_back(T const & e) { Z3_ast_vector_push(ctx(), m_vector, e); check_error(); } void resize(unsigned sz) { Z3_ast_vector_resize(ctx(), m_vector, sz); check_error(); } T back() const { return operator[](size() - 1); } void pop_back() { assert(size() > 0); resize(size() - 1); } bool empty() const { return size() == 0; } ast_vector_tpl & operator=(ast_vector_tpl const & s) { Z3_ast_vector_inc_ref(s.ctx(), s.m_vector); Z3_ast_vector_dec_ref(ctx(), m_vector); m_ctx = s.m_ctx; 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 { for (T y : *this) if (eq(x, y)) return true; return false; } */ class iterator { ast_vector_tpl const* m_vector; unsigned m_index; public: iterator(ast_vector_tpl const* v, unsigned i): m_vector(v), m_index(i) {} iterator(iterator const& other): m_vector(other.m_vector), m_index(other.m_index) {} iterator operator=(iterator const& other) { m_vector = other.m_vector; m_index = other.m_index; return *this; } bool operator==(iterator const& other) const { return other.m_index == m_index; }; bool operator!=(iterator const& other) const { return other.m_index != m_index; }; iterator& operator++() { ++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]; } }; iterator begin() const { return iterator(this, 0); } iterator end() const { return iterator(this, size()); } friend std::ostream & operator<<(std::ostream & out, ast_vector_tpl const & v) { out << Z3_ast_vector_to_string(v.ctx(), v); return out; } }; template template array::array(ast_vector_tpl const & v) { m_array = new T[v.size()]; m_size = v.size(); for (unsigned i = 0; i < m_size; i++) { m_array[i] = v[i]; } } // Basic functions for creating quantified formulas. // The C API should be used for creating quantifiers with patterns, weights, many variables, etc. inline expr forall(expr const & x, expr const & b) { check_context(x, b); Z3_app vars[] = {(Z3_app) x}; Z3_ast r = Z3_mk_forall_const(b.ctx(), 0, 1, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); } inline expr forall(expr const & x1, expr const & x2, expr const & b) { check_context(x1, b); check_context(x2, b); Z3_app vars[] = {(Z3_app) x1, (Z3_app) x2}; Z3_ast r = Z3_mk_forall_const(b.ctx(), 0, 2, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); } inline expr forall(expr const & x1, expr const & x2, expr const & x3, expr const & b) { check_context(x1, b); check_context(x2, b); check_context(x3, b); Z3_app vars[] = {(Z3_app) x1, (Z3_app) x2, (Z3_app) x3 }; Z3_ast r = Z3_mk_forall_const(b.ctx(), 0, 3, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); } inline expr forall(expr const & x1, expr const & x2, expr const & x3, expr const & x4, expr const & b) { check_context(x1, b); check_context(x2, b); check_context(x3, b); check_context(x4, b); Z3_app vars[] = {(Z3_app) x1, (Z3_app) x2, (Z3_app) x3, (Z3_app) x4 }; Z3_ast r = Z3_mk_forall_const(b.ctx(), 0, 4, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); } inline expr forall(expr_vector const & xs, expr const & b) { array vars(xs); Z3_ast r = Z3_mk_forall_const(b.ctx(), 0, vars.size(), vars.ptr(), 0, 0, b); b.check_error(); return expr(b.ctx(), r); } inline expr exists(expr const & x, expr const & b) { check_context(x, b); Z3_app vars[] = {(Z3_app) x}; Z3_ast r = Z3_mk_exists_const(b.ctx(), 0, 1, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); } inline expr exists(expr const & x1, expr const & x2, expr const & b) { check_context(x1, b); check_context(x2, b); Z3_app vars[] = {(Z3_app) x1, (Z3_app) x2}; Z3_ast r = Z3_mk_exists_const(b.ctx(), 0, 2, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); } inline expr exists(expr const & x1, expr const & x2, expr const & x3, expr const & b) { check_context(x1, b); check_context(x2, b); check_context(x3, b); Z3_app vars[] = {(Z3_app) x1, (Z3_app) x2, (Z3_app) x3 }; Z3_ast r = Z3_mk_exists_const(b.ctx(), 0, 3, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); } inline expr exists(expr const & x1, expr const & x2, expr const & x3, expr const & x4, expr const & b) { check_context(x1, b); check_context(x2, b); check_context(x3, b); check_context(x4, b); Z3_app vars[] = {(Z3_app) x1, (Z3_app) x2, (Z3_app) x3, (Z3_app) x4 }; Z3_ast r = Z3_mk_exists_const(b.ctx(), 0, 4, vars, 0, 0, b); b.check_error(); return expr(b.ctx(), r); } inline expr exists(expr_vector const & xs, expr const & b) { array vars(xs); Z3_ast r = Z3_mk_exists_const(b.ctx(), 0, vars.size(), vars.ptr(), 0, 0, b); b.check_error(); return expr(b.ctx(), r); } inline expr lambda(expr const & x, expr const & b) { check_context(x, b); Z3_app vars[] = {(Z3_app) x}; Z3_ast r = Z3_mk_lambda_const(b.ctx(), 1, vars, b); b.check_error(); return expr(b.ctx(), r); } inline expr lambda(expr const & x1, expr const & x2, expr const & b) { check_context(x1, b); check_context(x2, b); Z3_app vars[] = {(Z3_app) x1, (Z3_app) x2}; Z3_ast r = Z3_mk_lambda_const(b.ctx(), 2, vars, b); b.check_error(); return expr(b.ctx(), r); } inline expr lambda(expr const & x1, expr const & x2, expr const & x3, expr const & b) { check_context(x1, b); check_context(x2, b); check_context(x3, b); Z3_app vars[] = {(Z3_app) x1, (Z3_app) x2, (Z3_app) x3 }; Z3_ast r = Z3_mk_lambda_const(b.ctx(), 3, vars, b); b.check_error(); return expr(b.ctx(), r); } inline expr lambda(expr const & x1, expr const & x2, expr const & x3, expr const & x4, expr const & b) { check_context(x1, b); check_context(x2, b); check_context(x3, b); check_context(x4, b); Z3_app vars[] = {(Z3_app) x1, (Z3_app) x2, (Z3_app) x3, (Z3_app) x4 }; Z3_ast r = Z3_mk_lambda_const(b.ctx(), 4, vars, b); b.check_error(); return expr(b.ctx(), r); } inline expr lambda(expr_vector const & xs, expr const & b) { array vars(xs); Z3_ast r = Z3_mk_lambda_const(b.ctx(), vars.size(), vars.ptr(), b); b.check_error(); return expr(b.ctx(), r); } inline expr pble(expr_vector const& es, int const* coeffs, int bound) { assert(es.size() > 0); context& ctx = es[0].ctx(); array _es(es); Z3_ast r = Z3_mk_pble(ctx, _es.size(), _es.ptr(), coeffs, bound); ctx.check_error(); return expr(ctx, r); } inline expr pbge(expr_vector const& es, int const* coeffs, int bound) { assert(es.size() > 0); context& ctx = es[0].ctx(); array _es(es); Z3_ast r = Z3_mk_pbge(ctx, _es.size(), _es.ptr(), coeffs, bound); ctx.check_error(); return expr(ctx, r); } inline expr pbeq(expr_vector const& es, int const* coeffs, int bound) { assert(es.size() > 0); context& ctx = es[0].ctx(); array _es(es); Z3_ast r = Z3_mk_pbeq(ctx, _es.size(), _es.ptr(), coeffs, bound); ctx.check_error(); return expr(ctx, r); } inline expr atmost(expr_vector const& es, unsigned bound) { assert(es.size() > 0); context& ctx = es[0].ctx(); array _es(es); Z3_ast r = Z3_mk_atmost(ctx, _es.size(), _es.ptr(), bound); ctx.check_error(); return expr(ctx, r); } inline expr atleast(expr_vector const& es, unsigned bound) { assert(es.size() > 0); context& ctx = es[0].ctx(); array _es(es); Z3_ast r = Z3_mk_atleast(ctx, _es.size(), _es.ptr(), bound); ctx.check_error(); return expr(ctx, r); } inline expr sum(expr_vector const& args) { assert(args.size() > 0); context& ctx = args[0].ctx(); array _args(args); Z3_ast r = Z3_mk_add(ctx, _args.size(), _args.ptr()); ctx.check_error(); return expr(ctx, r); } inline expr distinct(expr_vector const& args) { assert(args.size() > 0); context& ctx = args[0].ctx(); array _args(args); Z3_ast r = Z3_mk_distinct(ctx, _args.size(), _args.ptr()); ctx.check_error(); return expr(ctx, r); } inline expr concat(expr const& a, expr const& b) { check_context(a, b); Z3_ast r; if (Z3_is_seq_sort(a.ctx(), a.get_sort())) { Z3_ast _args[2] = { a, b }; r = Z3_mk_seq_concat(a.ctx(), 2, _args); } else if (Z3_is_re_sort(a.ctx(), a.get_sort())) { Z3_ast _args[2] = { a, b }; r = Z3_mk_re_concat(a.ctx(), 2, _args); } else { r = Z3_mk_concat(a.ctx(), a, b); } a.ctx().check_error(); return expr(a.ctx(), r); } inline expr concat(expr_vector const& args) { Z3_ast r; assert(args.size() > 0); if (args.size() == 1) { return args[0]; } context& ctx = args[0].ctx(); array _args(args); if (Z3_is_seq_sort(ctx, args[0].get_sort())) { r = Z3_mk_seq_concat(ctx, _args.size(), _args.ptr()); } else if (Z3_is_re_sort(ctx, args[0].get_sort())) { r = Z3_mk_re_concat(ctx, _args.size(), _args.ptr()); } else { r = _args[args.size()-1]; for (unsigned i = args.size()-1; i > 0; ) { --i; r = Z3_mk_concat(ctx, _args[i], r); ctx.check_error(); } } ctx.check_error(); return expr(ctx, r); } inline expr mk_or(expr_vector const& args) { array _args(args); Z3_ast r = Z3_mk_or(args.ctx(), _args.size(), _args.ptr()); args.check_error(); return expr(args.ctx(), r); } inline expr mk_and(expr_vector const& args) { array _args(args); Z3_ast r = Z3_mk_and(args.ctx(), _args.size(), _args.ptr()); args.check_error(); return expr(args.ctx(), r); } class func_entry : public object { Z3_func_entry m_entry; void init(Z3_func_entry e) { m_entry = e; Z3_func_entry_inc_ref(ctx(), m_entry); } public: func_entry(context & c, Z3_func_entry e):object(c) { init(e); } func_entry(func_entry const & s):object(s) { init(s.m_entry); } ~func_entry() { Z3_func_entry_dec_ref(ctx(), m_entry); } operator Z3_func_entry() const { return m_entry; } func_entry & operator=(func_entry const & s) { Z3_func_entry_inc_ref(s.ctx(), s.m_entry); Z3_func_entry_dec_ref(ctx(), m_entry); m_ctx = s.m_ctx; m_entry = s.m_entry; return *this; } expr value() const { Z3_ast r = Z3_func_entry_get_value(ctx(), m_entry); check_error(); return expr(ctx(), r); } unsigned num_args() const { unsigned r = Z3_func_entry_get_num_args(ctx(), m_entry); check_error(); return r; } expr arg(unsigned i) const { Z3_ast r = Z3_func_entry_get_arg(ctx(), m_entry, i); check_error(); return expr(ctx(), r); } }; class func_interp : public object { Z3_func_interp m_interp; void init(Z3_func_interp e) { m_interp = e; Z3_func_interp_inc_ref(ctx(), m_interp); } public: func_interp(context & c, Z3_func_interp e):object(c) { init(e); } func_interp(func_interp const & s):object(s) { init(s.m_interp); } ~func_interp() { Z3_func_interp_dec_ref(ctx(), m_interp); } operator Z3_func_interp() const { return m_interp; } func_interp & operator=(func_interp const & s) { Z3_func_interp_inc_ref(s.ctx(), s.m_interp); Z3_func_interp_dec_ref(ctx(), m_interp); m_ctx = s.m_ctx; m_interp = s.m_interp; return *this; } expr else_value() const { Z3_ast r = Z3_func_interp_get_else(ctx(), m_interp); check_error(); return expr(ctx(), r); } unsigned num_entries() const { unsigned r = Z3_func_interp_get_num_entries(ctx(), m_interp); check_error(); return r; } func_entry entry(unsigned i) const { Z3_func_entry e = Z3_func_interp_get_entry(ctx(), m_interp, i); check_error(); return func_entry(ctx(), e); } void add_entry(expr_vector const& args, expr& value) { Z3_func_interp_add_entry(ctx(), m_interp, args, value); check_error(); } void set_else(expr& value) { Z3_func_interp_set_else(ctx(), m_interp, value); check_error(); } }; class model : public object { Z3_model m_model; void init(Z3_model m) { m_model = m; Z3_model_inc_ref(ctx(), m); } public: struct translate {}; model(context & c):object(c) { init(Z3_mk_model(c)); } model(context & c, Z3_model m):object(c) { init(m); } model(model const & s):object(s) { init(s.m_model); } model(model& src, context& dst, translate) : object(dst) { init(Z3_model_translate(src.ctx(), src, dst)); } ~model() { Z3_model_dec_ref(ctx(), m_model); } operator Z3_model() const { return m_model; } model & operator=(model const & s) { Z3_model_inc_ref(s.ctx(), s.m_model); Z3_model_dec_ref(ctx(), m_model); m_ctx = s.m_ctx; m_model = s.m_model; return *this; } expr eval(expr const & n, bool model_completion=false) const { check_context(*this, n); Z3_ast r = 0; bool status = Z3_model_eval(ctx(), m_model, n, model_completion, &r); check_error(); if (status == false && ctx().enable_exceptions()) Z3_THROW(exception("failed to evaluate expression")); return expr(ctx(), r); } unsigned num_consts() const { return Z3_model_get_num_consts(ctx(), m_model); } unsigned num_funcs() const { return Z3_model_get_num_funcs(ctx(), m_model); } func_decl get_const_decl(unsigned i) const { Z3_func_decl r = Z3_model_get_const_decl(ctx(), m_model, i); check_error(); return func_decl(ctx(), r); } func_decl get_func_decl(unsigned i) const { Z3_func_decl r = Z3_model_get_func_decl(ctx(), m_model, i); check_error(); return func_decl(ctx(), r); } unsigned size() const { return num_consts() + num_funcs(); } func_decl operator[](int i) const { assert(0 <= i); return static_cast(i) < num_consts() ? get_const_decl(i) : get_func_decl(i - num_consts()); } // returns interpretation of constant declaration c. // If c is not assigned any value in the model it returns // an expression with a null ast reference. expr get_const_interp(func_decl c) const { check_context(*this, c); Z3_ast r = Z3_model_get_const_interp(ctx(), m_model, c); check_error(); return expr(ctx(), r); } func_interp get_func_interp(func_decl f) const { check_context(*this, f); Z3_func_interp r = Z3_model_get_func_interp(ctx(), m_model, f); check_error(); return func_interp(ctx(), r); } // returns true iff the model contains an interpretation // for function f. bool has_interp(func_decl f) const { check_context(*this, f); return Z3_model_has_interp(ctx(), m_model, f); } func_interp add_func_interp(func_decl& f, expr& else_val) { Z3_func_interp r = Z3_add_func_interp(ctx(), m_model, f, else_val); check_error(); return func_interp(ctx(), r); } void add_const_interp(func_decl& f, expr& value) { Z3_add_const_interp(ctx(), m_model, f, value); check_error(); } friend std::ostream & operator<<(std::ostream & out, model const & m); }; inline std::ostream & operator<<(std::ostream & out, model const & m) { out << Z3_model_to_string(m.ctx(), m); return out; } class stats : public object { Z3_stats m_stats; void init(Z3_stats e) { m_stats = e; Z3_stats_inc_ref(ctx(), m_stats); } public: stats(context & c):object(c), m_stats(0) {} stats(context & c, Z3_stats e):object(c) { init(e); } stats(stats const & s):object(s) { init(s.m_stats); } ~stats() { if (m_stats) Z3_stats_dec_ref(ctx(), m_stats); } operator Z3_stats() const { return m_stats; } stats & operator=(stats const & s) { Z3_stats_inc_ref(s.ctx(), s.m_stats); if (m_stats) Z3_stats_dec_ref(ctx(), m_stats); m_ctx = s.m_ctx; m_stats = s.m_stats; return *this; } 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; } 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); }; inline std::ostream & operator<<(std::ostream & out, stats const & s) { out << Z3_stats_to_string(s.ctx(), s); return out; } inline std::ostream & operator<<(std::ostream & out, check_result r) { if (r == unsat) out << "unsat"; else if (r == sat) out << "sat"; else out << "unknown"; return out; } class solver : public object { Z3_solver m_solver; void init(Z3_solver s) { m_solver = s; Z3_solver_inc_ref(ctx(), s); } public: struct simple {}; struct translate {}; solver(context & c):object(c) { init(Z3_mk_solver(c)); } solver(context & c, simple):object(c) { init(Z3_mk_simple_solver(c)); } solver(context & c, Z3_solver s):object(c) { init(s); } solver(context & c, char const * logic):object(c) { init(Z3_mk_solver_for_logic(c, c.str_symbol(logic))); } solver(context & c, solver const& src, translate): object(c) { init(Z3_solver_translate(src.ctx(), src, c)); } solver(solver const & s):object(s) { init(s.m_solver); } ~solver() { Z3_solver_dec_ref(ctx(), m_solver); } operator Z3_solver() const { return m_solver; } solver & operator=(solver const & s) { Z3_solver_inc_ref(s.ctx(), s.m_solver); Z3_solver_dec_ref(ctx(), m_solver); m_ctx = s.m_ctx; m_solver = s.m_solver; return *this; } void set(params const & p) { Z3_solver_set_params(ctx(), m_solver, p); check_error(); } void set(char const * k, bool v) { params p(ctx()); p.set(k, v); set(p); } void set(char const * k, unsigned v) { params p(ctx()); p.set(k, v); set(p); } void set(char const * k, double v) { params p(ctx()); p.set(k, v); set(p); } void set(char const * k, symbol const & v) { params p(ctx()); p.set(k, v); set(p); } void set(char const * k, char const* v) { params p(ctx()); p.set(k, v); set(p); } void push() { Z3_solver_push(ctx(), m_solver); check_error(); } void pop(unsigned n = 1) { Z3_solver_pop(ctx(), m_solver, n); check_error(); } void reset() { Z3_solver_reset(ctx(), m_solver); check_error(); } void add(expr const & e) { assert(e.is_bool()); Z3_solver_assert(ctx(), m_solver, e); check_error(); } void add(expr const & e, expr const & p) { assert(e.is_bool()); assert(p.is_bool()); assert(p.is_const()); Z3_solver_assert_and_track(ctx(), m_solver, e, p); check_error(); } void add(expr const & e, char const * p) { add(e, ctx().bool_const(p)); } // fails for some compilers: // void add(expr_vector const& v) { check_context(*this, v); for (expr e : v) add(e); } void from_file(char const* file) { Z3_solver_from_file(ctx(), m_solver, file); ctx().check_parser_error(); } void from_string(char const* s) { Z3_solver_from_string(ctx(), m_solver, s); ctx().check_parser_error(); } check_result check() { Z3_lbool r = Z3_solver_check(ctx(), m_solver); check_error(); return to_check_result(r); } check_result check(unsigned n, expr * const assumptions) { array _assumptions(n); for (unsigned i = 0; i < n; i++) { check_context(*this, assumptions[i]); _assumptions[i] = assumptions[i]; } Z3_lbool r = Z3_solver_check_assumptions(ctx(), m_solver, n, _assumptions.ptr()); check_error(); return to_check_result(r); } check_result check(expr_vector const& assumptions) { unsigned n = assumptions.size(); array _assumptions(n); for (unsigned i = 0; i < n; i++) { check_context(*this, assumptions[i]); _assumptions[i] = assumptions[i]; } Z3_lbool r = Z3_solver_check_assumptions(ctx(), m_solver, n, _assumptions.ptr()); check_error(); return to_check_result(r); } model get_model() const { Z3_model m = Z3_solver_get_model(ctx(), m_solver); check_error(); return model(ctx(), m); } check_result consequences(expr_vector& assumptions, expr_vector& vars, expr_vector& conseq) { Z3_lbool r = Z3_solver_get_consequences(ctx(), m_solver, assumptions, vars, conseq); check_error(); return to_check_result(r); } std::string reason_unknown() const { Z3_string r = Z3_solver_get_reason_unknown(ctx(), m_solver); check_error(); return r; } stats statistics() const { Z3_stats r = Z3_solver_get_statistics(ctx(), m_solver); check_error(); return stats(ctx(), r); } expr_vector unsat_core() const { Z3_ast_vector r = Z3_solver_get_unsat_core(ctx(), m_solver); check_error(); return expr_vector(ctx(), r); } 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; } 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); std::string to_smt2(char const* status = "unknown") { array es(assertions()); Z3_ast const* fmls = es.ptr(); Z3_ast fml = 0; unsigned sz = es.size(); if (sz > 0) { --sz; fml = fmls[sz]; } else { fml = ctx().bool_val(true); } return std::string(Z3_benchmark_to_smtlib_string( ctx(), "", "", status, "", sz, fmls, 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)); } expr_vector cube(expr_vector& vars, unsigned cutoff) { Z3_ast_vector r = Z3_solver_cube(ctx(), m_solver, vars, cutoff); check_error(); return expr_vector(ctx(), r); } class cube_iterator { solver& m_solver; unsigned& m_cutoff; expr_vector& m_vars; expr_vector m_cube; bool m_end; bool m_empty; void inc() { assert(!m_end && !m_empty); m_cube = m_solver.cube(m_vars, m_cutoff); m_cutoff = 0xFFFFFFFF; if (m_cube.size() == 1 && m_cube[0].is_false()) { m_cube = z3::expr_vector(m_solver.ctx()); m_end = true; } else if (m_cube.empty()) { m_empty = true; } } public: cube_iterator(solver& s, expr_vector& vars, unsigned& cutoff, bool end): m_solver(s), m_cutoff(cutoff), m_vars(vars), m_cube(s.ctx()), m_end(end), m_empty(false) { if (!m_end) { inc(); } } cube_iterator& operator++() { assert(!m_end); if (m_empty) { m_end = true; } else { inc(); } return *this; } cube_iterator operator++(int) { assert(false); return *this; } expr_vector const * operator->() const { return &(operator*()); } expr_vector const& operator*() const { return m_cube; } bool operator==(cube_iterator const& other) { return other.m_end == m_end; }; bool operator!=(cube_iterator const& other) { return other.m_end != m_end; }; }; class cube_generator { solver& m_solver; unsigned m_cutoff; expr_vector m_default_vars; expr_vector& m_vars; public: cube_generator(solver& s): m_solver(s), m_cutoff(0xFFFFFFFF), m_default_vars(s.ctx()), m_vars(m_default_vars) {} cube_generator(solver& s, expr_vector& vars): m_solver(s), m_cutoff(0xFFFFFFFF), m_default_vars(s.ctx()), m_vars(vars) {} cube_iterator begin() { return cube_iterator(m_solver, m_vars, m_cutoff, false); } cube_iterator end() { return cube_iterator(m_solver, m_vars, m_cutoff, true); } void set_cutoff(unsigned c) { m_cutoff = c; } }; cube_generator cubes() { return cube_generator(*this); } cube_generator cubes(expr_vector& vars) { return cube_generator(*this, vars); } }; inline std::ostream & operator<<(std::ostream & out, solver const & s) { out << Z3_solver_to_string(s.ctx(), s); return out; } class goal : public object { Z3_goal m_goal; void init(Z3_goal s) { m_goal = s; Z3_goal_inc_ref(ctx(), s); } public: goal(context & c, bool models=true, bool unsat_cores=false, bool proofs=false):object(c) { init(Z3_mk_goal(c, models, unsat_cores, proofs)); } goal(context & c, Z3_goal s):object(c) { init(s); } goal(goal const & s):object(s) { init(s.m_goal); } ~goal() { Z3_goal_dec_ref(ctx(), m_goal); } operator Z3_goal() const { return m_goal; } goal & operator=(goal const & s) { Z3_goal_inc_ref(s.ctx(), s.m_goal); Z3_goal_dec_ref(ctx(), m_goal); m_ctx = s.m_ctx; m_goal = s.m_goal; return *this; } void add(expr const & f) { check_context(*this, f); Z3_goal_assert(ctx(), m_goal, f); check_error(); } void add(expr_vector const& v) { check_context(*this, v); for (unsigned i = 0; i < v.size(); ++i) add(v[i]); } 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); } 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); } 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); check_error(); return model(ctx(), new_m); } model get_model() const { Z3_model new_m = Z3_goal_convert_model(ctx(), m_goal, 0); check_error(); return model(ctx(), new_m); } expr as_expr() const { unsigned n = size(); if (n == 0) return ctx().bool_val(true); else if (n == 1) return operator[](0); else { array args(n); for (unsigned i = 0; i < n; i++) args[i] = operator[](i); return expr(ctx(), Z3_mk_and(ctx(), n, args.ptr())); } } std::string dimacs() const { return std::string(Z3_goal_to_dimacs_string(ctx(), m_goal)); } friend std::ostream & operator<<(std::ostream & out, goal const & g); }; inline std::ostream & operator<<(std::ostream & out, goal const & g) { out << Z3_goal_to_string(g.ctx(), g); return out; } class apply_result : public object { Z3_apply_result m_apply_result; void init(Z3_apply_result s) { m_apply_result = s; Z3_apply_result_inc_ref(ctx(), s); } public: apply_result(context & c, Z3_apply_result s):object(c) { init(s); } apply_result(apply_result const & s):object(s) { init(s.m_apply_result); } ~apply_result() { Z3_apply_result_dec_ref(ctx(), m_apply_result); } operator Z3_apply_result() const { return m_apply_result; } apply_result & operator=(apply_result const & s) { Z3_apply_result_inc_ref(s.ctx(), s.m_apply_result); Z3_apply_result_dec_ref(ctx(), m_apply_result); m_ctx = s.m_ctx; m_apply_result = s.m_apply_result; return *this; } unsigned size() const { return Z3_apply_result_get_num_subgoals(ctx(), m_apply_result); } goal operator[](int i) const { assert(0 <= i); Z3_goal r = Z3_apply_result_get_subgoal(ctx(), m_apply_result, i); check_error(); return goal(ctx(), r); } friend std::ostream & operator<<(std::ostream & out, apply_result const & r); }; inline std::ostream & operator<<(std::ostream & out, apply_result const & r) { out << Z3_apply_result_to_string(r.ctx(), r); return out; } class tactic : public object { Z3_tactic m_tactic; void init(Z3_tactic s) { m_tactic = s; Z3_tactic_inc_ref(ctx(), s); } public: tactic(context & c, char const * name):object(c) { Z3_tactic r = Z3_mk_tactic(c, name); check_error(); init(r); } tactic(context & c, Z3_tactic s):object(c) { init(s); } tactic(tactic const & s):object(s) { init(s.m_tactic); } ~tactic() { Z3_tactic_dec_ref(ctx(), m_tactic); } operator Z3_tactic() const { return m_tactic; } tactic & operator=(tactic const & s) { Z3_tactic_inc_ref(s.ctx(), s.m_tactic); Z3_tactic_dec_ref(ctx(), m_tactic); m_ctx = s.m_ctx; m_tactic = s.m_tactic; return *this; } solver mk_solver() const { Z3_solver r = Z3_mk_solver_from_tactic(ctx(), m_tactic); check_error(); return solver(ctx(), r); } apply_result apply(goal const & g) const { check_context(*this, g); Z3_apply_result r = Z3_tactic_apply(ctx(), m_tactic, g); check_error(); return apply_result(ctx(), r); } apply_result operator()(goal const & g) const { return apply(g); } std::string help() const { char const * r = Z3_tactic_get_help(ctx(), m_tactic); check_error(); return r; } friend tactic operator&(tactic const & t1, tactic const & t2); friend tactic operator|(tactic const & t1, tactic const & t2); friend tactic repeat(tactic const & t, unsigned max); friend tactic with(tactic const & t, params const & p); friend tactic try_for(tactic const & t, unsigned ms); friend tactic par_or(unsigned n, tactic const* tactics); friend tactic par_and_then(tactic const& t1, tactic const& t2); param_descrs get_param_descrs() { return param_descrs(ctx(), Z3_tactic_get_param_descrs(ctx(), m_tactic)); } }; inline tactic operator&(tactic const & t1, tactic const & t2) { check_context(t1, t2); Z3_tactic r = Z3_tactic_and_then(t1.ctx(), t1, t2); t1.check_error(); return tactic(t1.ctx(), r); } inline tactic operator|(tactic const & t1, tactic const & t2) { check_context(t1, t2); Z3_tactic r = Z3_tactic_or_else(t1.ctx(), t1, t2); t1.check_error(); return tactic(t1.ctx(), r); } inline tactic repeat(tactic const & t, unsigned max=UINT_MAX) { Z3_tactic r = Z3_tactic_repeat(t.ctx(), t, max); t.check_error(); return tactic(t.ctx(), r); } inline tactic with(tactic const & t, params const & p) { Z3_tactic r = Z3_tactic_using_params(t.ctx(), t, p); t.check_error(); return tactic(t.ctx(), r); } inline tactic try_for(tactic const & t, unsigned ms) { Z3_tactic r = Z3_tactic_try_for(t.ctx(), t, ms); t.check_error(); return tactic(t.ctx(), r); } inline tactic par_or(unsigned n, tactic const* tactics) { if (n == 0) { Z3_THROW(exception("a non-zero number of tactics need to be passed to par_or")); } array buffer(n); for (unsigned i = 0; i < n; ++i) buffer[i] = tactics[i]; return tactic(tactics[0].ctx(), Z3_tactic_par_or(tactics[0].ctx(), n, buffer.ptr())); } inline tactic par_and_then(tactic const & t1, tactic const & t2) { check_context(t1, t2); Z3_tactic r = Z3_tactic_par_and_then(t1.ctx(), t1, t2); t1.check_error(); return tactic(t1.ctx(), r); } class probe : public object { Z3_probe m_probe; void init(Z3_probe s) { m_probe = s; Z3_probe_inc_ref(ctx(), s); } public: probe(context & c, char const * name):object(c) { Z3_probe r = Z3_mk_probe(c, name); check_error(); init(r); } probe(context & c, double val):object(c) { Z3_probe r = Z3_probe_const(c, val); check_error(); init(r); } probe(context & c, Z3_probe s):object(c) { init(s); } probe(probe const & s):object(s) { init(s.m_probe); } ~probe() { Z3_probe_dec_ref(ctx(), m_probe); } operator Z3_probe() const { return m_probe; } probe & operator=(probe const & s) { Z3_probe_inc_ref(s.ctx(), s.m_probe); Z3_probe_dec_ref(ctx(), m_probe); m_ctx = s.m_ctx; m_probe = s.m_probe; return *this; } double apply(goal const & g) const { double r = Z3_probe_apply(ctx(), m_probe, g); check_error(); return r; } double operator()(goal const & g) const { return apply(g); } friend probe operator<=(probe const & p1, probe const & p2); friend probe operator<=(probe const & p1, double p2); friend probe operator<=(double p1, probe const & p2); friend probe operator>=(probe const & p1, probe const & p2); friend probe operator>=(probe const & p1, double p2); friend probe operator>=(double p1, probe const & p2); friend probe operator<(probe const & p1, probe const & p2); friend probe operator<(probe const & p1, double p2); friend probe operator<(double p1, probe const & p2); friend probe operator>(probe const & p1, probe const & p2); friend probe operator>(probe const & p1, double p2); friend probe operator>(double p1, probe const & p2); friend probe operator==(probe const & p1, probe const & p2); friend probe operator==(probe const & p1, double p2); friend probe operator==(double p1, probe const & p2); friend probe operator&&(probe const & p1, probe const & p2); friend probe operator||(probe const & p1, probe const & p2); friend probe operator!(probe const & p); }; inline probe operator<=(probe const & p1, probe const & p2) { check_context(p1, p2); Z3_probe r = Z3_probe_le(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); } inline probe operator<=(probe const & p1, double p2) { return p1 <= probe(p1.ctx(), p2); } inline probe operator<=(double p1, probe const & p2) { return probe(p2.ctx(), p1) <= p2; } inline probe operator>=(probe const & p1, probe const & p2) { check_context(p1, p2); Z3_probe r = Z3_probe_ge(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); } inline probe operator>=(probe const & p1, double p2) { return p1 >= probe(p1.ctx(), p2); } inline probe operator>=(double p1, probe const & p2) { return probe(p2.ctx(), p1) >= p2; } inline probe operator<(probe const & p1, probe const & p2) { check_context(p1, p2); Z3_probe r = Z3_probe_lt(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); } inline probe operator<(probe const & p1, double p2) { return p1 < probe(p1.ctx(), p2); } inline probe operator<(double p1, probe const & p2) { return probe(p2.ctx(), p1) < p2; } inline probe operator>(probe const & p1, probe const & p2) { check_context(p1, p2); Z3_probe r = Z3_probe_gt(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); } inline probe operator>(probe const & p1, double p2) { return p1 > probe(p1.ctx(), p2); } inline probe operator>(double p1, probe const & p2) { return probe(p2.ctx(), p1) > p2; } inline probe operator==(probe const & p1, probe const & p2) { check_context(p1, p2); Z3_probe r = Z3_probe_eq(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); } inline probe operator==(probe const & p1, double p2) { return p1 == probe(p1.ctx(), p2); } inline probe operator==(double p1, probe const & p2) { return probe(p2.ctx(), p1) == p2; } inline probe operator&&(probe const & p1, probe const & p2) { check_context(p1, p2); Z3_probe r = Z3_probe_and(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); } inline probe operator||(probe const & p1, probe const & p2) { check_context(p1, p2); Z3_probe r = Z3_probe_or(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); } inline probe operator!(probe const & p) { Z3_probe r = Z3_probe_not(p.ctx(), p); p.check_error(); return probe(p.ctx(), r); } class optimize : public object { Z3_optimize m_opt; public: class handle { unsigned m_h; public: handle(unsigned h): m_h(h) {} unsigned h() const { return m_h; } }; optimize(context& c):object(c) { m_opt = Z3_mk_optimize(c); Z3_optimize_inc_ref(c, m_opt); } optimize(optimize& o):object(o) { Z3_optimize_inc_ref(o.ctx(), o.m_opt); m_opt = o.m_opt; } optimize& operator=(optimize const& o) { Z3_optimize_inc_ref(o.ctx(), o.m_opt); Z3_optimize_dec_ref(ctx(), m_opt); m_opt = o.m_opt; m_ctx = o.m_ctx; return *this; } ~optimize() { Z3_optimize_dec_ref(ctx(), m_opt); } operator Z3_optimize() const { return m_opt; } void add(expr const& e) { assert(e.is_bool()); Z3_optimize_assert(ctx(), m_opt, e); } handle add(expr const& e, unsigned weight) { assert(e.is_bool()); std::stringstream strm; 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)); } handle maximize(expr const& e) { return handle(Z3_optimize_maximize(ctx(), m_opt, e)); } handle minimize(expr const& e) { return handle(Z3_optimize_minimize(ctx(), m_opt, e)); } void push() { Z3_optimize_push(ctx(), m_opt); } void pop() { Z3_optimize_pop(ctx(), m_opt); } check_result check() { Z3_lbool r = Z3_optimize_check(ctx(), m_opt, 0, 0); check_error(); return to_check_result(r); } check_result check(expr_vector const& asms) { unsigned n = asms.size(); array _asms(n); for (unsigned i = 0; i < n; i++) { check_context(*this, asms[i]); _asms[i] = asms[i]; } Z3_lbool r = Z3_optimize_check(ctx(), m_opt, n, _asms.ptr()); check_error(); return to_check_result(r); } model get_model() const { Z3_model m = Z3_optimize_get_model(ctx(), m_opt); check_error(); return model(ctx(), m); } expr_vector unsat_core() const { Z3_ast_vector r = Z3_optimize_get_unsat_core(ctx(), m_opt); check_error(); return expr_vector(ctx(), r); } void set(params const & p) { Z3_optimize_set_params(ctx(), m_opt, p); check_error(); } expr lower(handle const& h) { Z3_ast r = Z3_optimize_get_lower(ctx(), m_opt, h.h()); check_error(); return expr(ctx(), r); } expr upper(handle const& h) { Z3_ast r = Z3_optimize_get_upper(ctx(), m_opt, h.h()); check_error(); return expr(ctx(), r); } expr_vector assertions() const { Z3_ast_vector r = Z3_optimize_get_assertions(ctx(), m_opt); check_error(); return expr_vector(ctx(), r); } expr_vector objectives() const { Z3_ast_vector r = Z3_optimize_get_objectives(ctx(), m_opt); check_error(); return expr_vector(ctx(), r); } stats statistics() const { Z3_stats r = Z3_optimize_get_statistics(ctx(), m_opt); check_error(); return stats(ctx(), r); } friend std::ostream & operator<<(std::ostream & out, optimize const & s); void from_file(char const* filename) { Z3_optimize_from_file(ctx(), m_opt, filename); check_error(); } void from_string(char const* constraints) { Z3_optimize_from_string(ctx(), m_opt, constraints); check_error(); } std::string help() const { char const * r = Z3_optimize_get_help(ctx(), m_opt); check_error(); return r; } }; inline std::ostream & operator<<(std::ostream & out, optimize const & s) { out << Z3_optimize_to_string(s.ctx(), s.m_opt); return out; } class fixedpoint : public object { Z3_fixedpoint m_fp; public: fixedpoint(context& c):object(c) { m_fp = Z3_mk_fixedpoint(c); Z3_fixedpoint_inc_ref(c, m_fp); } ~fixedpoint() { Z3_fixedpoint_dec_ref(ctx(), m_fp); } operator Z3_fixedpoint() const { return m_fp; } void from_string(char const* s) { Z3_fixedpoint_from_string(ctx(), m_fp, s); check_error(); } void from_file(char const* s) { Z3_fixedpoint_from_file(ctx(), m_fp, s); check_error(); } void add_rule(expr& rule, symbol const& name) { Z3_fixedpoint_add_rule(ctx(), m_fp, rule, name); check_error(); } void add_fact(func_decl& f, unsigned * args) { Z3_fixedpoint_add_fact(ctx(), m_fp, f, f.arity(), args); check_error(); } check_result query(expr& q) { Z3_lbool r = Z3_fixedpoint_query(ctx(), m_fp, q); check_error(); return to_check_result(r); } check_result query(func_decl_vector& relations) { array rs(relations); Z3_lbool r = Z3_fixedpoint_query_relations(ctx(), m_fp, rs.size(), rs.ptr()); check_error(); return to_check_result(r); } expr get_answer() { Z3_ast r = Z3_fixedpoint_get_answer(ctx(), m_fp); check_error(); return expr(ctx(), r); } std::string reason_unknown() { return Z3_fixedpoint_get_reason_unknown(ctx(), m_fp); } void update_rule(expr& rule, symbol const& name) { Z3_fixedpoint_update_rule(ctx(), m_fp, rule, name); check_error(); } unsigned get_num_levels(func_decl& p) { unsigned r = Z3_fixedpoint_get_num_levels(ctx(), m_fp, p); check_error(); return r; } expr get_cover_delta(int level, func_decl& p) { Z3_ast r = Z3_fixedpoint_get_cover_delta(ctx(), m_fp, level, p); check_error(); return expr(ctx(), r); } void add_cover(int level, func_decl& p, expr& property) { Z3_fixedpoint_add_cover(ctx(), m_fp, level, p, property); check_error(); } stats statistics() const { Z3_stats r = Z3_fixedpoint_get_statistics(ctx(), m_fp); check_error(); return stats(ctx(), r); } void register_relation(func_decl& p) { Z3_fixedpoint_register_relation(ctx(), m_fp, p); } expr_vector assertions() const { Z3_ast_vector r = Z3_fixedpoint_get_assertions(ctx(), m_fp); check_error(); return expr_vector(ctx(), r); } expr_vector rules() const { Z3_ast_vector r = Z3_fixedpoint_get_rules(ctx(), m_fp); check_error(); return expr_vector(ctx(), r); } void set(params const & p) { Z3_fixedpoint_set_params(ctx(), m_fp, p); check_error(); } std::string help() const { return Z3_fixedpoint_get_help(ctx(), m_fp); } param_descrs get_param_descrs() { return param_descrs(ctx(), Z3_fixedpoint_get_param_descrs(ctx(), m_fp)); } std::string to_string() { return Z3_fixedpoint_to_string(ctx(), m_fp, 0, 0); } std::string to_string(expr_vector const& queries) { array qs(queries); return Z3_fixedpoint_to_string(ctx(), m_fp, qs.size(), qs.ptr()); } }; inline std::ostream & operator<<(std::ostream & out, fixedpoint const & f) { return out << Z3_fixedpoint_to_string(f.ctx(), f, 0, 0); } inline tactic fail_if(probe const & p) { Z3_tactic r = Z3_tactic_fail_if(p.ctx(), p); p.check_error(); return tactic(p.ctx(), r); } inline tactic when(probe const & p, tactic const & t) { check_context(p, t); Z3_tactic r = Z3_tactic_when(t.ctx(), p, t); t.check_error(); return tactic(t.ctx(), r); } inline tactic cond(probe const & p, tactic const & t1, tactic const & t2) { check_context(p, t1); check_context(p, t2); Z3_tactic r = Z3_tactic_cond(t1.ctx(), p, t1, t2); t1.check_error(); return tactic(t1.ctx(), r); } inline symbol context::str_symbol(char const * s) { Z3_symbol r = Z3_mk_string_symbol(m_ctx, s); check_error(); return symbol(*this, r); } inline symbol context::int_symbol(int n) { Z3_symbol r = Z3_mk_int_symbol(m_ctx, n); check_error(); return symbol(*this, r); } inline sort context::bool_sort() { Z3_sort s = Z3_mk_bool_sort(m_ctx); check_error(); return sort(*this, s); } inline sort context::int_sort() { Z3_sort s = Z3_mk_int_sort(m_ctx); check_error(); return sort(*this, s); } inline sort context::real_sort() { Z3_sort s = Z3_mk_real_sort(m_ctx); check_error(); return sort(*this, s); } inline sort context::bv_sort(unsigned sz) { Z3_sort s = Z3_mk_bv_sort(m_ctx, sz); check_error(); return sort(*this, s); } inline sort context::string_sort() { Z3_sort s = Z3_mk_string_sort(m_ctx); check_error(); return sort(*this, s); } inline sort context::seq_sort(sort& s) { Z3_sort r = Z3_mk_seq_sort(m_ctx, s); check_error(); return sort(*this, r); } inline sort context::re_sort(sort& s) { Z3_sort r = Z3_mk_re_sort(m_ctx, s); check_error(); return sort(*this, r); } inline sort context::fpa_sort(unsigned ebits, unsigned sbits) { Z3_sort s = Z3_mk_fpa_sort(m_ctx, ebits, sbits); check_error(); return sort(*this, s); } template<> inline sort context::fpa_sort<16>() { return fpa_sort(5, 11); } template<> inline sort context::fpa_sort<32>() { return fpa_sort(8, 24); } template<> inline sort context::fpa_sort<64>() { return fpa_sort(11, 53); } template<> inline sort context::fpa_sort<128>() { return fpa_sort(15, 113); } inline sort context::fpa_rounding_mode() { switch (m_rounding_mode) { case RNA: return sort(*this, Z3_mk_fpa_rna(m_ctx)); case RNE: return sort(*this, Z3_mk_fpa_rne(m_ctx)); case RTP: return sort(*this, Z3_mk_fpa_rtp(m_ctx)); case RTN: return sort(*this, Z3_mk_fpa_rtn(m_ctx)); case RTZ: return sort(*this, Z3_mk_fpa_rtz(m_ctx)); default: return sort(*this); } } inline void context::set_rounding_mode(rounding_mode rm) { m_rounding_mode = rm; } inline sort context::array_sort(sort d, sort r) { Z3_sort s = Z3_mk_array_sort(m_ctx, d, r); check_error(); return sort(*this, s); } inline sort context::array_sort(sort_vector const& d, sort r) { array dom(d); Z3_sort s = Z3_mk_array_sort_n(m_ctx, dom.size(), dom.ptr(), r); check_error(); return sort(*this, s); } inline sort context::enumeration_sort(char const * name, unsigned n, char const * const * enum_names, func_decl_vector & cs, func_decl_vector & ts) { array _enum_names(n); for (unsigned i = 0; i < n; i++) { _enum_names[i] = Z3_mk_string_symbol(*this, enum_names[i]); } array _cs(n); array _ts(n); Z3_symbol _name = Z3_mk_string_symbol(*this, name); sort s = to_sort(*this, Z3_mk_enumeration_sort(*this, _name, n, _enum_names.ptr(), _cs.ptr(), _ts.ptr())); check_error(); for (unsigned i = 0; i < n; i++) { cs.push_back(func_decl(*this, _cs[i])); ts.push_back(func_decl(*this, _ts[i])); } return s; } inline func_decl context::tuple_sort(char const * name, unsigned n, char const * const * names, sort const* sorts, func_decl_vector & projs) { array _names(n); array _sorts(n); for (unsigned i = 0; i < n; i++) { _names[i] = Z3_mk_string_symbol(*this, names[i]); _sorts[i] = sorts[i]; } array _projs(n); Z3_symbol _name = Z3_mk_string_symbol(*this, name); Z3_func_decl tuple; sort _ignore_s = to_sort(*this, Z3_mk_tuple_sort(*this, _name, n, _names.ptr(), _sorts.ptr(), &tuple, _projs.ptr())); check_error(); for (unsigned i = 0; i < n; i++) { projs.push_back(func_decl(*this, _projs[i])); } return func_decl(*this, tuple); } inline sort context::uninterpreted_sort(char const* name) { Z3_symbol _name = Z3_mk_string_symbol(*this, name); return to_sort(*this, Z3_mk_uninterpreted_sort(*this, _name)); } inline sort context::uninterpreted_sort(symbol const& name) { return to_sort(*this, Z3_mk_uninterpreted_sort(*this, name)); } inline func_decl context::function(symbol const & name, unsigned arity, sort const * domain, sort const & range) { array args(arity); for (unsigned i = 0; i < arity; i++) { check_context(domain[i], range); args[i] = domain[i]; } Z3_func_decl f = Z3_mk_func_decl(m_ctx, name, arity, args.ptr(), range); check_error(); return func_decl(*this, f); } inline func_decl context::function(char const * name, unsigned arity, sort const * domain, sort const & range) { return function(range.ctx().str_symbol(name), arity, domain, range); } inline func_decl context::function(symbol const& name, sort_vector const& domain, sort const& range) { array args(domain.size()); for (unsigned i = 0; i < domain.size(); i++) { check_context(domain[i], range); args[i] = domain[i]; } Z3_func_decl f = Z3_mk_func_decl(m_ctx, name, domain.size(), args.ptr(), range); check_error(); return func_decl(*this, f); } inline func_decl context::function(char const * name, sort_vector const& domain, sort const& range) { return function(range.ctx().str_symbol(name), domain, range); } inline func_decl context::function(char const * name, sort const & domain, sort const & range) { check_context(domain, range); Z3_sort args[1] = { domain }; Z3_func_decl f = Z3_mk_func_decl(m_ctx, str_symbol(name), 1, args, range); check_error(); return func_decl(*this, f); } inline func_decl context::function(char const * name, sort const & d1, sort const & d2, sort const & range) { check_context(d1, range); check_context(d2, range); Z3_sort args[2] = { d1, d2 }; Z3_func_decl f = Z3_mk_func_decl(m_ctx, str_symbol(name), 2, args, range); check_error(); return func_decl(*this, f); } inline func_decl context::function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & range) { check_context(d1, range); check_context(d2, range); check_context(d3, range); Z3_sort args[3] = { d1, d2, d3 }; Z3_func_decl f = Z3_mk_func_decl(m_ctx, str_symbol(name), 3, args, range); check_error(); return func_decl(*this, f); } inline func_decl context::function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & d4, sort const & range) { check_context(d1, range); check_context(d2, range); check_context(d3, range); check_context(d4, range); Z3_sort args[4] = { d1, d2, d3, d4 }; Z3_func_decl f = Z3_mk_func_decl(m_ctx, str_symbol(name), 4, args, range); check_error(); return func_decl(*this, f); } inline func_decl context::function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & d4, sort const & d5, sort const & range) { check_context(d1, range); check_context(d2, range); check_context(d3, range); check_context(d4, range); check_context(d5, range); Z3_sort args[5] = { d1, d2, d3, d4, d5 }; Z3_func_decl f = Z3_mk_func_decl(m_ctx, str_symbol(name), 5, args, range); check_error(); return func_decl(*this, f); } inline func_decl context::recfun(symbol const & name, unsigned arity, sort const * domain, sort const & range) { array args(arity); for (unsigned i = 0; i < arity; i++) { check_context(domain[i], range); args[i] = domain[i]; } Z3_func_decl f = Z3_mk_rec_func_decl(m_ctx, name, arity, args.ptr(), range); check_error(); return func_decl(*this, f); } inline func_decl context::recfun(char const * name, unsigned arity, sort const * domain, sort const & range) { return recfun(str_symbol(name), arity, domain, range); } inline func_decl context::recfun(char const * name, sort const& d1, sort const & range) { return recfun(str_symbol(name), 1, &d1, range); } inline func_decl context::recfun(char const * name, sort const& d1, sort const& d2, sort const & range) { sort dom[2] = { d1, d2 }; return recfun(str_symbol(name), 2, dom, range); } inline void context::recdef(func_decl f, expr_vector const& args, expr const& body) { check_context(f, args); check_context(f, body); array vars(args); Z3_add_rec_def(f.ctx(), f, vars.size(), vars.ptr(), body); } inline expr context::constant(symbol const & name, sort const & s) { Z3_ast r = Z3_mk_const(m_ctx, name, s); check_error(); return expr(*this, r); } inline expr context::constant(char const * name, sort const & s) { return constant(str_symbol(name), s); } inline expr context::bool_const(char const * name) { return constant(name, bool_sort()); } inline expr context::int_const(char const * name) { return constant(name, int_sort()); } inline expr context::real_const(char const * name) { return constant(name, real_sort()); } inline expr context::bv_const(char const * name, unsigned sz) { return constant(name, bv_sort(sz)); } inline expr context::fpa_const(char const * name, unsigned ebits, unsigned sbits) { return constant(name, fpa_sort(ebits, sbits)); } template inline expr context::fpa_const(char const * name) { return constant(name, fpa_sort()); } inline expr context::bool_val(bool b) { return b ? expr(*this, Z3_mk_true(m_ctx)) : expr(*this, Z3_mk_false(m_ctx)); } inline expr context::int_val(int n) { Z3_ast r = Z3_mk_int(m_ctx, n, int_sort()); check_error(); return expr(*this, r); } inline expr context::int_val(unsigned n) { Z3_ast r = Z3_mk_unsigned_int(m_ctx, n, int_sort()); check_error(); return expr(*this, r); } inline expr context::int_val(int64_t n) { Z3_ast r = Z3_mk_int64(m_ctx, n, int_sort()); check_error(); return expr(*this, r); } inline expr context::int_val(uint64_t n) { Z3_ast r = Z3_mk_unsigned_int64(m_ctx, n, int_sort()); check_error(); return expr(*this, r); } inline expr context::int_val(char const * n) { Z3_ast r = Z3_mk_numeral(m_ctx, n, int_sort()); check_error(); return expr(*this, r); } inline expr context::real_val(int n, int d) { Z3_ast r = Z3_mk_real(m_ctx, n, d); check_error(); return expr(*this, r); } inline expr context::real_val(int n) { Z3_ast r = Z3_mk_int(m_ctx, n, real_sort()); check_error(); return expr(*this, r); } inline expr context::real_val(unsigned n) { Z3_ast r = Z3_mk_unsigned_int(m_ctx, n, real_sort()); check_error(); return expr(*this, r); } inline expr context::real_val(int64_t n) { Z3_ast r = Z3_mk_int64(m_ctx, n, real_sort()); check_error(); return expr(*this, r); } inline expr context::real_val(uint64_t n) { Z3_ast r = Z3_mk_unsigned_int64(m_ctx, n, real_sort()); check_error(); return expr(*this, r); } inline expr context::real_val(char const * n) { Z3_ast r = Z3_mk_numeral(m_ctx, n, real_sort()); check_error(); return expr(*this, r); } inline expr context::bv_val(int n, unsigned sz) { sort s = bv_sort(sz); Z3_ast r = Z3_mk_int(m_ctx, n, s); check_error(); return expr(*this, r); } inline expr context::bv_val(unsigned n, unsigned sz) { sort s = bv_sort(sz); Z3_ast r = Z3_mk_unsigned_int(m_ctx, n, s); check_error(); return expr(*this, r); } inline expr context::bv_val(int64_t n, unsigned sz) { sort s = bv_sort(sz); Z3_ast r = Z3_mk_int64(m_ctx, n, s); check_error(); return expr(*this, r); } inline expr context::bv_val(uint64_t n, unsigned sz) { sort s = bv_sort(sz); Z3_ast r = Z3_mk_unsigned_int64(m_ctx, n, s); check_error(); return expr(*this, r); } inline expr context::bv_val(char const * n, unsigned sz) { sort s = bv_sort(sz); Z3_ast r = Z3_mk_numeral(m_ctx, n, s); check_error(); return expr(*this, r); } inline expr context::bv_val(unsigned n, bool const* bits) { array _bits(n); for (unsigned i = 0; i < n; ++i) _bits[i] = bits[i] ? 1 : 0; Z3_ast r = Z3_mk_bv_numeral(m_ctx, n, _bits.ptr()); check_error(); return expr(*this, r); } inline expr context::fpa_val(double n) { sort s = fpa_sort<64>(); Z3_ast r = Z3_mk_fpa_numeral_double(m_ctx, n, s); check_error(); return expr(*this, r); } inline expr context::fpa_val(float n) { sort s = fpa_sort<32>(); Z3_ast r = Z3_mk_fpa_numeral_float(m_ctx, n, s); check_error(); return expr(*this, r); } inline expr context::string_val(char const* s, unsigned n) { Z3_ast r = Z3_mk_lstring(m_ctx, n, s); check_error(); return expr(*this, r); } inline expr context::string_val(char const* s) { Z3_ast r = Z3_mk_string(m_ctx, s); check_error(); return expr(*this, r); } inline expr context::string_val(std::string const& s) { Z3_ast r = Z3_mk_string(m_ctx, s.c_str()); check_error(); return expr(*this, r); } inline expr context::num_val(int n, sort const & s) { Z3_ast r = Z3_mk_int(m_ctx, n, s); check_error(); return expr(*this, r); } inline expr func_decl::operator()(unsigned n, expr const * args) const { array _args(n); for (unsigned i = 0; i < n; i++) { check_context(*this, args[i]); _args[i] = args[i]; } Z3_ast r = Z3_mk_app(ctx(), *this, n, _args.ptr()); check_error(); return expr(ctx(), r); } inline expr func_decl::operator()(expr_vector const& args) const { array _args(args.size()); for (unsigned i = 0; i < args.size(); i++) { check_context(*this, args[i]); _args[i] = args[i]; } Z3_ast r = Z3_mk_app(ctx(), *this, args.size(), _args.ptr()); check_error(); return expr(ctx(), r); } inline expr func_decl::operator()() const { Z3_ast r = Z3_mk_app(ctx(), *this, 0, 0); ctx().check_error(); return expr(ctx(), r); } inline expr func_decl::operator()(expr const & a) const { check_context(*this, a); Z3_ast args[1] = { a }; Z3_ast r = Z3_mk_app(ctx(), *this, 1, args); ctx().check_error(); return expr(ctx(), r); } inline expr func_decl::operator()(int a) const { Z3_ast args[1] = { ctx().num_val(a, domain(0)) }; Z3_ast r = Z3_mk_app(ctx(), *this, 1, args); ctx().check_error(); return expr(ctx(), r); } inline expr func_decl::operator()(expr const & a1, expr const & a2) const { check_context(*this, a1); check_context(*this, a2); Z3_ast args[2] = { a1, a2 }; Z3_ast r = Z3_mk_app(ctx(), *this, 2, args); ctx().check_error(); return expr(ctx(), r); } inline expr func_decl::operator()(expr const & a1, int a2) const { check_context(*this, a1); Z3_ast args[2] = { a1, ctx().num_val(a2, domain(1)) }; Z3_ast r = Z3_mk_app(ctx(), *this, 2, args); ctx().check_error(); return expr(ctx(), r); } inline expr func_decl::operator()(int a1, expr const & a2) const { check_context(*this, a2); Z3_ast args[2] = { ctx().num_val(a1, domain(0)), a2 }; Z3_ast r = Z3_mk_app(ctx(), *this, 2, args); ctx().check_error(); return expr(ctx(), r); } inline expr func_decl::operator()(expr const & a1, expr const & a2, expr const & a3) const { check_context(*this, a1); check_context(*this, a2); check_context(*this, a3); Z3_ast args[3] = { a1, a2, a3 }; Z3_ast r = Z3_mk_app(ctx(), *this, 3, args); ctx().check_error(); return expr(ctx(), r); } inline expr func_decl::operator()(expr const & a1, expr const & a2, expr const & a3, expr const & a4) const { check_context(*this, a1); check_context(*this, a2); check_context(*this, a3); check_context(*this, a4); Z3_ast args[4] = { a1, a2, a3, a4 }; Z3_ast r = Z3_mk_app(ctx(), *this, 4, args); ctx().check_error(); return expr(ctx(), r); } inline expr func_decl::operator()(expr const & a1, expr const & a2, expr const & a3, expr const & a4, expr const & a5) const { check_context(*this, a1); check_context(*this, a2); check_context(*this, a3); check_context(*this, a4); check_context(*this, a5); Z3_ast args[5] = { a1, a2, a3, a4, a5 }; Z3_ast r = Z3_mk_app(ctx(), *this, 5, args); ctx().check_error(); return expr(ctx(), r); } inline expr to_real(expr const & a) { Z3_ast r = Z3_mk_int2real(a.ctx(), a); a.check_error(); return expr(a.ctx(), r); } inline func_decl function(symbol const & name, unsigned arity, sort const * domain, sort const & range) { return range.ctx().function(name, arity, domain, range); } inline func_decl function(char const * name, unsigned arity, sort const * domain, sort const & range) { return range.ctx().function(name, arity, domain, range); } inline func_decl function(char const * name, sort const & domain, sort const & range) { return range.ctx().function(name, domain, range); } inline func_decl function(char const * name, sort const & d1, sort const & d2, sort const & range) { return range.ctx().function(name, d1, d2, range); } inline func_decl function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & range) { return range.ctx().function(name, d1, d2, d3, range); } inline func_decl function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & d4, sort const & range) { return range.ctx().function(name, d1, d2, d3, d4, range); } inline func_decl function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & d4, sort const & d5, sort const & range) { return range.ctx().function(name, d1, d2, d3, d4, d5, range); } inline func_decl function(char const* name, sort_vector const& domain, sort const& range) { return range.ctx().function(name, domain, range); } inline func_decl function(std::string const& name, sort_vector const& domain, sort const& range) { return range.ctx().function(name.c_str(), domain, range); } inline func_decl recfun(symbol const & name, unsigned arity, sort const * domain, sort const & range) { return range.ctx().recfun(name, arity, domain, range); } inline func_decl recfun(char const * name, unsigned arity, sort const * domain, sort const & range) { return range.ctx().recfun(name, arity, domain, range); } inline func_decl recfun(char const * name, sort const& d1, sort const & range) { return range.ctx().recfun(name, d1, range); } inline func_decl recfun(char const * name, sort const& d1, sort const& d2, sort const & range) { return range.ctx().recfun(name, d1, d2, range); } inline expr select(expr const & a, expr const & i) { check_context(a, i); Z3_ast r = Z3_mk_select(a.ctx(), a, i); a.check_error(); return expr(a.ctx(), r); } inline expr select(expr const & a, int i) { return select(a, a.ctx().num_val(i, a.get_sort().array_domain())); } inline expr select(expr const & a, expr_vector const & i) { check_context(a, i); array idxs(i); Z3_ast r = Z3_mk_select_n(a.ctx(), a, idxs.size(), idxs.ptr()); a.check_error(); return expr(a.ctx(), r); } inline expr store(expr const & a, expr const & i, expr const & v) { check_context(a, i); check_context(a, v); Z3_ast r = Z3_mk_store(a.ctx(), a, i, v); a.check_error(); return expr(a.ctx(), r); } inline expr store(expr const & a, int i, expr const & v) { return store(a, a.ctx().num_val(i, a.get_sort().array_domain()), v); } inline expr store(expr const & a, expr i, int v) { return store(a, i, a.ctx().num_val(v, a.get_sort().array_range())); } inline expr store(expr const & a, int i, int v) { return store(a, a.ctx().num_val(i, a.get_sort().array_domain()), a.ctx().num_val(v, a.get_sort().array_range())); } inline expr store(expr const & a, expr_vector const & i, expr const & v) { check_context(a, i); check_context(a, v); array idxs(i); Z3_ast r = Z3_mk_store_n(a.ctx(), a, idxs.size(), idxs.ptr(), v); a.check_error(); return expr(a.ctx(), r); } inline expr as_array(func_decl & f) { Z3_ast r = Z3_mk_as_array(f.ctx(), f); f.check_error(); return expr(f.ctx(), r); } #define MK_EXPR1(_fn, _arg) \ Z3_ast r = _fn(_arg.ctx(), _arg); \ _arg.check_error(); \ return expr(_arg.ctx(), r); #define MK_EXPR2(_fn, _arg1, _arg2) \ check_context(_arg1, _arg2); \ Z3_ast r = _fn(_arg1.ctx(), _arg1, _arg2); \ _arg1.check_error(); \ return expr(_arg1.ctx(), r); inline expr const_array(sort const & d, expr const & v) { MK_EXPR2(Z3_mk_const_array, d, v); } inline expr empty_set(sort const& s) { MK_EXPR1(Z3_mk_empty_set, s); } inline expr full_set(sort const& s) { MK_EXPR1(Z3_mk_full_set, s); } inline expr set_add(expr const& s, expr const& e) { MK_EXPR2(Z3_mk_set_add, s, e); } inline expr set_del(expr const& s, expr const& e) { MK_EXPR2(Z3_mk_set_del, s, e); } inline expr set_union(expr const& a, expr const& b) { check_context(a, b); Z3_ast es[2] = { a, b }; Z3_ast r = Z3_mk_set_union(a.ctx(), 2, es); a.check_error(); return expr(a.ctx(), r); } inline expr set_intersect(expr const& a, expr const& b) { check_context(a, b); Z3_ast es[2] = { a, b }; Z3_ast r = Z3_mk_set_intersect(a.ctx(), 2, es); a.check_error(); return expr(a.ctx(), r); } inline expr set_difference(expr const& a, expr const& b) { MK_EXPR2(Z3_mk_set_difference, a, b); } inline expr set_complement(expr const& a) { MK_EXPR1(Z3_mk_set_complement, a); } inline expr set_member(expr const& s, expr const& e) { MK_EXPR2(Z3_mk_set_member, s, e); } inline expr set_subset(expr const& a, expr const& b) { MK_EXPR2(Z3_mk_set_subset, a, b); } // sequence and regular expression operations. // union is + // concat is overloaded to handle sequences and regular expressions inline expr empty(sort const& s) { Z3_ast r = Z3_mk_seq_empty(s.ctx(), s); s.check_error(); return expr(s.ctx(), r); } inline expr suffixof(expr const& a, expr const& b) { check_context(a, b); Z3_ast r = Z3_mk_seq_suffix(a.ctx(), a, b); a.check_error(); return expr(a.ctx(), r); } inline expr prefixof(expr const& a, expr const& b) { check_context(a, b); Z3_ast r = Z3_mk_seq_prefix(a.ctx(), a, b); a.check_error(); return expr(a.ctx(), r); } inline expr indexof(expr const& s, expr const& substr, expr const& offset) { check_context(s, substr); check_context(s, offset); Z3_ast r = Z3_mk_seq_index(s.ctx(), s, substr, offset); s.check_error(); return expr(s.ctx(), r); } inline expr last_indexof(expr const& s, expr const& substr) { check_context(s, substr); Z3_ast r = Z3_mk_seq_last_index(s.ctx(), s, substr); s.check_error(); return expr(s.ctx(), r); } inline expr to_re(expr const& s) { MK_EXPR1(Z3_mk_seq_to_re, s); } inline expr in_re(expr const& s, expr const& re) { MK_EXPR2(Z3_mk_seq_in_re, s, re); } inline expr plus(expr const& re) { MK_EXPR1(Z3_mk_re_plus, re); } inline expr option(expr const& re) { MK_EXPR1(Z3_mk_re_option, re); } inline expr star(expr const& re) { MK_EXPR1(Z3_mk_re_star, re); } inline expr re_empty(sort const& s) { Z3_ast r = Z3_mk_re_empty(s.ctx(), s); s.check_error(); return expr(s.ctx(), r); } inline expr re_full(sort const& s) { Z3_ast r = Z3_mk_re_full(s.ctx(), s); s.check_error(); return expr(s.ctx(), r); } inline expr re_intersect(expr_vector const& args) { assert(args.size() > 0); context& ctx = args[0].ctx(); array _args(args); Z3_ast r = Z3_mk_re_intersect(ctx, _args.size(), _args.ptr()); ctx.check_error(); return expr(ctx, r); } inline expr re_complement(expr const& a) { MK_EXPR1(Z3_mk_re_complement, a); } inline expr range(expr const& lo, expr const& hi) { check_context(lo, hi); Z3_ast r = Z3_mk_re_range(lo.ctx(), lo, hi); lo.check_error(); return expr(lo.ctx(), r); } inline expr_vector context::parse_string(char const* s) { Z3_ast_vector r = Z3_parse_smtlib2_string(*this, s, 0, 0, 0, 0, 0, 0); check_error(); return expr_vector(*this, r); } inline expr_vector context::parse_file(char const* s) { Z3_ast_vector r = Z3_parse_smtlib2_file(*this, s, 0, 0, 0, 0, 0, 0); check_error(); return expr_vector(*this, r); } inline expr_vector context::parse_string(char const* s, sort_vector const& sorts, func_decl_vector const& decls) { array sort_names(sorts.size()); array decl_names(decls.size()); array sorts1(sorts); array decls1(decls); for (unsigned i = 0; i < sorts.size(); ++i) { sort_names[i] = sorts[i].name(); } for (unsigned i = 0; i < decls.size(); ++i) { decl_names[i] = decls[i].name(); } Z3_ast_vector r = Z3_parse_smtlib2_string(*this, s, sorts.size(), sort_names.ptr(), sorts1.ptr(), decls.size(), decl_names.ptr(), decls1.ptr()); check_error(); return expr_vector(*this, r); } inline expr_vector context::parse_file(char const* s, sort_vector const& sorts, func_decl_vector const& decls) { array sort_names(sorts.size()); array decl_names(decls.size()); array sorts1(sorts); array decls1(decls); for (unsigned i = 0; i < sorts.size(); ++i) { sort_names[i] = sorts[i].name(); } for (unsigned i = 0; i < decls.size(); ++i) { decl_names[i] = decls[i].name(); } Z3_ast_vector r = Z3_parse_smtlib2_file(*this, s, sorts.size(), sort_names.ptr(), sorts1.ptr(), decls.size(), decl_names.ptr(), decls1.ptr()); check_error(); return expr_vector(*this, r); } inline expr expr::substitute(expr_vector const& src, expr_vector const& dst) { assert(src.size() == dst.size()); array _src(src.size()); array _dst(dst.size()); for (unsigned i = 0; i < src.size(); ++i) { _src[i] = src[i]; _dst[i] = dst[i]; } Z3_ast r = Z3_substitute(ctx(), m_ast, src.size(), _src.ptr(), _dst.ptr()); check_error(); return expr(ctx(), r); } inline expr expr::substitute(expr_vector const& dst) { array _dst(dst.size()); for (unsigned i = 0; i < dst.size(); ++i) { _dst[i] = dst[i]; } Z3_ast r = Z3_substitute_vars(ctx(), m_ast, dst.size(), _dst.ptr()); check_error(); return expr(ctx(), r); } } /*@}*/ /*@}*/ #undef Z3_THROW #endif z3-z3-4.8.7/src/api/dll/000077500000000000000000000000001356505360400145425ustar00rootroot00000000000000z3-z3-4.8.7/src/api/dll/CMakeLists.txt000066400000000000000000000006771356505360400173140ustar00rootroot00000000000000set(api_dll_deps api extra_cmds sat) z3_add_component(api_dll SOURCES dll.cpp "${CMAKE_CURRENT_BINARY_DIR}/gparams_register_modules.cpp" "${CMAKE_CURRENT_BINARY_DIR}/install_tactic.cpp" "${CMAKE_CURRENT_BINARY_DIR}/mem_initializer.cpp" COMPONENT_DEPENDENCIES ${api_dll_deps} ) z3_add_install_tactic_rule(${api_dll_deps}) z3_add_memory_initializer_rule(${api_dll_deps}) z3_add_gparams_register_modules_rule(${api_dll_deps}) z3-z3-4.8.7/src/api/dll/dll.cpp000066400000000000000000000010401356505360400160140ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #ifdef _WINDOWS #include #ifdef _MANAGED #pragma managed(push, off) #endif BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; } #ifdef _MANAGED #pragma managed(pop) #endif #endif z3-z3-4.8.7/src/api/dotnet/000077500000000000000000000000001356505360400152645ustar00rootroot00000000000000z3-z3-4.8.7/src/api/dotnet/AST.cs000066400000000000000000000173301356505360400162460ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: AST.cs Abstract: Z3 Managed API: ASTs Author: Christoph Wintersteiger (cwinter) 2012-03-16 Notes: --*/ using System.Diagnostics; using System; using System.Collections; using System.Collections.Generic; namespace Microsoft.Z3 { /// /// The abstract syntax tree (AST) class. /// public class AST : Z3Object, IComparable { /// /// Comparison operator. /// /// An AST /// An AST /// True if and are from the same context /// and represent the same sort; false otherwise. public static bool operator ==(AST a, AST b) { return Object.ReferenceEquals(a, b) || (!Object.ReferenceEquals(a, null) && !Object.ReferenceEquals(b, null) && a.Context.nCtx == b.Context.nCtx && 0 != Native.Z3_is_eq_ast(a.Context.nCtx, a.NativeObject, b.NativeObject)); } /// /// Comparison operator. /// /// An AST /// An AST /// True if and are not from the same context /// or represent different sorts; false otherwise. public static bool operator !=(AST a, AST b) { return !(a == b); } /// /// Object comparison. /// public override bool Equals(object o) { AST casted = o as AST; if (casted == null) return false; return this == casted; } /// /// Object Comparison. /// /// Another AST /// Negative if the object should be sorted before , positive if after else zero. public virtual int CompareTo(object other) { if (other == null) return 1; AST oAST = other as AST; if (oAST == null) return 1; else { if (Id < oAST.Id) return -1; else if (Id > oAST.Id) return +1; else return 0; } } /// /// The AST's hash code. /// /// A hash code public override int GetHashCode() { return (int)Native.Z3_get_ast_hash(Context.nCtx, NativeObject); } /// /// A unique identifier for the AST (unique among all ASTs). /// public uint Id { get { return Native.Z3_get_ast_id(Context.nCtx, NativeObject); } } /// /// Translates (copies) the AST to the Context . /// /// A context /// A copy of the AST which is associated with public AST Translate(Context ctx) { Debug.Assert(ctx != null); if (ReferenceEquals(Context, ctx)) return this; else return Create(ctx, Native.Z3_translate(Context.nCtx, NativeObject, ctx.nCtx)); } /// /// The kind of the AST. /// public Z3_ast_kind ASTKind { get { return (Z3_ast_kind)Native.Z3_get_ast_kind(Context.nCtx, NativeObject); } } /// /// Indicates whether the AST is an Expr /// public bool IsExpr { get { switch (ASTKind) { case Z3_ast_kind.Z3_APP_AST: case Z3_ast_kind.Z3_NUMERAL_AST: case Z3_ast_kind.Z3_QUANTIFIER_AST: case Z3_ast_kind.Z3_VAR_AST: return true; default: return false; } } } /// /// Indicates whether the AST is an application /// public bool IsApp { get { return this.ASTKind == Z3_ast_kind.Z3_APP_AST; } } /// /// Indicates whether the AST is a BoundVariable /// public bool IsVar { get { return this.ASTKind == Z3_ast_kind.Z3_VAR_AST; } } /// /// Indicates whether the AST is a Quantifier /// public bool IsQuantifier { get { return this.ASTKind == Z3_ast_kind.Z3_QUANTIFIER_AST; } } /// /// Indicates whether the AST is a Sort /// public bool IsSort { get { return this.ASTKind == Z3_ast_kind.Z3_SORT_AST; } } /// /// Indicates whether the AST is a FunctionDeclaration /// public bool IsFuncDecl { get { return this.ASTKind == Z3_ast_kind.Z3_FUNC_DECL_AST; } } /// /// A string representation of the AST. /// public override string ToString() { return Native.Z3_ast_to_string(Context.nCtx, NativeObject); } /// /// A string representation of the AST in s-expression notation. /// public string SExpr() { return Native.Z3_ast_to_string(Context.nCtx, NativeObject); } #region Internal internal AST(Context ctx) : base(ctx) { Debug.Assert(ctx != null); } internal AST(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } internal class DecRefQueue : IDecRefQueue { public DecRefQueue() : base() { } public DecRefQueue(uint move_limit) : base(move_limit) { } internal override void IncRef(Context ctx, IntPtr obj) { Native.Z3_inc_ref(ctx.nCtx, obj); } internal override void DecRef(Context ctx, IntPtr obj) { Native.Z3_dec_ref(ctx.nCtx, obj); } }; internal override void IncRef(IntPtr o) { // Console.WriteLine("AST IncRef()"); if (Context == null || o == IntPtr.Zero) return; Context.AST_DRQ.IncAndClear(Context, o); base.IncRef(o); } internal override void DecRef(IntPtr o) { // Console.WriteLine("AST DecRef()"); if (Context == null || o == IntPtr.Zero) return; Context.AST_DRQ.Add(o); base.DecRef(o); } internal static AST Create(Context ctx, IntPtr obj) { Debug.Assert(ctx != null); switch ((Z3_ast_kind)Native.Z3_get_ast_kind(ctx.nCtx, obj)) { case Z3_ast_kind.Z3_FUNC_DECL_AST: return new FuncDecl(ctx, obj); case Z3_ast_kind.Z3_QUANTIFIER_AST: return new Quantifier(ctx, obj); case Z3_ast_kind.Z3_SORT_AST: return Sort.Create(ctx, obj); case Z3_ast_kind.Z3_APP_AST: case Z3_ast_kind.Z3_NUMERAL_AST: case Z3_ast_kind.Z3_VAR_AST: return Expr.Create(ctx, obj); default: throw new Z3Exception("Unknown AST kind"); } } #endregion } } z3-z3-4.8.7/src/api/dotnet/ASTMap.cs000066400000000000000000000101301356505360400166730ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: ASTMap.cs Abstract: Z3 Managed API: AST Maps Author: Christoph Wintersteiger (cwinter) 2012-03-21 Notes: --*/ using System.Diagnostics; using System; namespace Microsoft.Z3 { /// /// Map from AST to AST /// internal class ASTMap : Z3Object { /// /// Checks whether the map contains the key . /// /// An AST /// True if is a key in the map, false otherwise. public bool Contains(AST k) { Debug.Assert(k != null); return 0 != Native.Z3_ast_map_contains(Context.nCtx, NativeObject, k.NativeObject); } /// /// Finds the value associated with the key . /// /// /// This function signs an error when is not a key in the map. /// /// An AST public AST Find(AST k) { Debug.Assert(k != null); return new AST(Context, Native.Z3_ast_map_find(Context.nCtx, NativeObject, k.NativeObject)); } /// /// Stores or replaces a new key/value pair in the map. /// /// The key AST /// The value AST public void Insert(AST k, AST v) { Debug.Assert(k != null); Debug.Assert(v != null); Native.Z3_ast_map_insert(Context.nCtx, NativeObject, k.NativeObject, v.NativeObject); } /// /// Erases the key from the map. /// /// An AST public void Erase(AST k) { Debug.Assert(k != null); Native.Z3_ast_map_erase(Context.nCtx, NativeObject, k.NativeObject); } /// /// Removes all keys from the map. /// public void Reset() { Native.Z3_ast_map_reset(Context.nCtx, NativeObject); } /// /// The size of the map /// public uint Size { get { return Native.Z3_ast_map_size(Context.nCtx, NativeObject); } } /// /// The keys stored in the map. /// public AST[] Keys { get { ASTVector res = new ASTVector(Context, Native.Z3_ast_map_keys(Context.nCtx, NativeObject)); return res.ToArray(); } } /// /// Retrieves a string representation of the map. /// public override string ToString() { return Native.Z3_ast_map_to_string(Context.nCtx, NativeObject); } #region Internal internal ASTMap(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } internal ASTMap(Context ctx) : base(ctx, Native.Z3_mk_ast_map(ctx.nCtx)) { Debug.Assert(ctx != null); } internal class DecRefQueue : IDecRefQueue { public DecRefQueue() : base() { } public DecRefQueue(uint move_limit) : base(move_limit) { } internal override void IncRef(Context ctx, IntPtr obj) { Native.Z3_ast_map_inc_ref(ctx.nCtx, obj); } internal override void DecRef(Context ctx, IntPtr obj) { Native.Z3_ast_map_dec_ref(ctx.nCtx, obj); } }; internal override void IncRef(IntPtr o) { Context.ASTMap_DRQ.IncAndClear(Context, o); base.IncRef(o); } internal override void DecRef(IntPtr o) { Context.ASTMap_DRQ.Add(o); base.DecRef(o); } #endregion } } z3-z3-4.8.7/src/api/dotnet/ASTVector.cs000066400000000000000000000174211356505360400174320ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: ASTVector.cs Abstract: Z3 Managed API: AST Vectors Author: Christoph Wintersteiger (cwinter) 2012-03-21 Notes: --*/ using System.Diagnostics; using System; namespace Microsoft.Z3 { /// /// Vectors of ASTs. /// public class ASTVector : Z3Object { /// /// The size of the vector /// public uint Size { get { return Native.Z3_ast_vector_size(Context.nCtx, NativeObject); } } /// /// Retrieves the i-th object in the vector. /// /// May throw an IndexOutOfBoundsException when is out of range. /// Index /// An AST public AST this[uint i] { get { return new AST(Context, Native.Z3_ast_vector_get(Context.nCtx, NativeObject, i)); } set { Debug.Assert(value != null); Native.Z3_ast_vector_set(Context.nCtx, NativeObject, i, value.NativeObject); } } /// /// Resize the vector to . /// /// The new size of the vector. public void Resize(uint newSize) { Native.Z3_ast_vector_resize(Context.nCtx, NativeObject, newSize); } /// /// Add the AST to the back of the vector. The size /// is increased by 1. /// /// An AST public void Push(AST a) { Debug.Assert(a != null); Native.Z3_ast_vector_push(Context.nCtx, NativeObject, a.NativeObject); } /// /// Translates all ASTs in the vector to . /// /// A context /// A new ASTVector public ASTVector Translate(Context ctx) { Debug.Assert(ctx != null); return new ASTVector(Context, Native.Z3_ast_vector_translate(Context.nCtx, NativeObject, ctx.nCtx)); } /// /// Retrieves a string representation of the vector. /// public override string ToString() { return Native.Z3_ast_vector_to_string(Context.nCtx, NativeObject); } /// /// Translates an AST vector into an AST[] /// public AST[] ToArray() { uint n = Size; AST[] res = new AST[n]; for (uint i = 0; i < n; i++) res[i] = AST.Create(this.Context, this[i].NativeObject); return res; } /// /// Translates an ASTVector into an Expr[] /// public Expr[] ToExprArray() { uint n = Size; Expr[] res = new Expr[n]; for (uint i = 0; i < n; i++) res[i] = Expr.Create(this.Context, this[i].NativeObject); return res; } /// /// Translates an ASTVector into a BoolExpr[] /// public BoolExpr[] ToBoolExprArray() { uint n = Size; BoolExpr[] res = new BoolExpr[n]; for (uint i = 0; i < n; i++) res[i] = (BoolExpr) Expr.Create(this.Context, this[i].NativeObject); return res; } /// /// Translates an ASTVector into a BitVecExpr[] /// public BitVecExpr[] ToBitVecExprArray() { uint n = Size; BitVecExpr[] res = new BitVecExpr[n]; for (uint i = 0; i < n; i++) res[i] = (BitVecExpr)Expr.Create(this.Context, this[i].NativeObject); return res; } /// /// Translates an ASTVector into a ArithExpr[] /// public ArithExpr[] ToArithExprArray() { uint n = Size; ArithExpr[] res = new ArithExpr[n]; for (uint i = 0; i < n; i++) res[i] = (ArithExpr)Expr.Create(this.Context, this[i].NativeObject); return res; } /// /// Translates an ASTVector into a ArrayExpr[] /// public ArrayExpr[] ToArrayExprArray() { uint n = Size; ArrayExpr[] res = new ArrayExpr[n]; for (uint i = 0; i < n; i++) res[i] = (ArrayExpr)Expr.Create(this.Context, this[i].NativeObject); return res; } /// /// Translates an ASTVector into a DatatypeExpr[] /// public DatatypeExpr[] ToDatatypeExprArray() { uint n = Size; DatatypeExpr[] res = new DatatypeExpr[n]; for (uint i = 0; i < n; i++) res[i] = (DatatypeExpr)Expr.Create(this.Context, this[i].NativeObject); return res; } /// /// Translates an ASTVector into a FPExpr[] /// public FPExpr[] ToFPExprArray() { uint n = Size; FPExpr[] res = new FPExpr[n]; for (uint i = 0; i < n; i++) res[i] = (FPExpr)Expr.Create(this.Context, this[i].NativeObject); return res; } /// /// Translates an ASTVector into a FPRMExpr[] /// public FPRMExpr[] ToFPRMExprArray() { uint n = Size; FPRMExpr[] res = new FPRMExpr[n]; for (uint i = 0; i < n; i++) res[i] = (FPRMExpr)Expr.Create(this.Context, this[i].NativeObject); return res; } /// /// Translates an ASTVector into a IntExpr[] /// public IntExpr[] ToIntExprArray() { uint n = Size; IntExpr[] res = new IntExpr[n]; for (uint i = 0; i < n; i++) res[i] = (IntExpr)Expr.Create(this.Context, this[i].NativeObject); return res; } /// /// Translates an ASTVector into a RealExpr[] /// public RealExpr[] ToRealExprArray() { uint n = Size; RealExpr[] res = new RealExpr[n]; for (uint i = 0; i < n; i++) res[i] = (RealExpr)Expr.Create(this.Context, this[i].NativeObject); return res; } #region Internal internal ASTVector(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } internal ASTVector(Context ctx) : base(ctx, Native.Z3_mk_ast_vector(ctx.nCtx)) { Debug.Assert(ctx != null); } internal class DecRefQueue : IDecRefQueue { public DecRefQueue() : base() { } public DecRefQueue(uint move_limit) : base(move_limit) { } internal override void IncRef(Context ctx, IntPtr obj) { Native.Z3_ast_vector_inc_ref(ctx.nCtx, obj); } internal override void DecRef(Context ctx, IntPtr obj) { Native.Z3_ast_vector_dec_ref(ctx.nCtx, obj); } }; internal override void IncRef(IntPtr o) { Context.ASTVector_DRQ.IncAndClear(Context, o); base.IncRef(o); } internal override void DecRef(IntPtr o) { Context.ASTVector_DRQ.Add(o); base.DecRef(o); } #endregion } } z3-z3-4.8.7/src/api/dotnet/AlgebraicNum.cs000066400000000000000000000041601356505360400201450ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: AlgebraicNum.cs Abstract: Z3 Managed API: Algebraic Numerals Author: Christoph Wintersteiger (cwinter) 2012-03-20 Notes: --*/ using System.Diagnostics; using System; #if !FRAMEWORK_LT_4 using System.Numerics; #endif namespace Microsoft.Z3 { /// /// Algebraic numbers /// public class AlgebraicNum : ArithExpr { /// /// Return a upper bound for a given real algebraic number. /// The interval isolating the number is smaller than 1/10^. /// /// /// the precision of the result /// A numeral Expr of sort Real public RatNum ToUpper(uint precision) { return new RatNum(Context, Native.Z3_get_algebraic_number_upper(Context.nCtx, NativeObject, precision)); } /// /// Return a lower bound for the given real algebraic number. /// The interval isolating the number is smaller than 1/10^. /// /// /// /// A numeral Expr of sort Real public RatNum ToLower(uint precision) { return new RatNum(Context, Native.Z3_get_algebraic_number_lower(Context.nCtx, NativeObject, precision)); } /// /// Returns a string representation in decimal notation. /// /// The result has at most decimal places. public string ToDecimal(uint precision) { return Native.Z3_get_numeral_decimal_string(Context.nCtx, NativeObject, precision); } #region Internal internal AlgebraicNum(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } #endregion } } z3-z3-4.8.7/src/api/dotnet/ApplyResult.cs000066400000000000000000000046411356505360400201040ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: ApplyResult.cs Abstract: Z3 Managed API: Result object for tactic applications Author: Christoph Wintersteiger (cwinter) 2012-03-21 Notes: --*/ using System.Diagnostics; using System; namespace Microsoft.Z3 { /// /// ApplyResult objects represent the result of an application of a /// tactic to a goal. It contains the subgoals that were produced. /// public class ApplyResult : Z3Object { /// /// The number of Subgoals. /// public uint NumSubgoals { get { return Native.Z3_apply_result_get_num_subgoals(Context.nCtx, NativeObject); } } /// /// Retrieves the subgoals from the ApplyResult. /// public Goal[] Subgoals { get { uint n = NumSubgoals; Goal[] res = new Goal[n]; for (uint i = 0; i < n; i++) res[i] = new Goal(Context, Native.Z3_apply_result_get_subgoal(Context.nCtx, NativeObject, i)); return res; } } /// /// A string representation of the ApplyResult. /// public override string ToString() { return Native.Z3_apply_result_to_string(Context.nCtx, NativeObject); } #region Internal internal ApplyResult(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } internal class DecRefQueue : IDecRefQueue { public DecRefQueue() : base() { } public DecRefQueue(uint move_limit) : base(move_limit) { } internal override void IncRef(Context ctx, IntPtr obj) { Native.Z3_apply_result_inc_ref(ctx.nCtx, obj); } internal override void DecRef(Context ctx, IntPtr obj) { Native.Z3_apply_result_dec_ref(ctx.nCtx, obj); } }; internal override void IncRef(IntPtr o) { Context.ApplyResult_DRQ.IncAndClear(Context, o); base.IncRef(o); } internal override void DecRef(IntPtr o) { Context.ApplyResult_DRQ.Add(o); base.DecRef(o); } #endregion } } z3-z3-4.8.7/src/api/dotnet/ArithExpr.cs000066400000000000000000000201701356505360400175210ustar00rootroot00000000000000/*++ Copyright () 2012 Microsoft Corporation Module Name: ArithExpr.cs Abstract: Z3 Managed API: Arith Expressions Author: Christoph Wintersteiger (cwinter) 2012-11-23 Notes: --*/ using System.Diagnostics; using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Microsoft.Z3 { /// /// Arithmetic expressions (int/real) /// public class ArithExpr : Expr { #region Internal /// Constructor for ArithExpr internal ArithExpr(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } #endregion #region Operators private static ArithExpr MkNum(ArithExpr e, int i) { return (ArithExpr)e.Context.MkNumeral(i, e.Context.MkIntSort()); } private static ArithExpr MkNum(ArithExpr e, double d) { return (ArithExpr)e.Context.MkNumeral(d.ToString(), e.Context.MkRealSort()); } /// Operator overloading for arithmetical division operator (over reals) public static ArithExpr operator /(ArithExpr a, ArithExpr b) { return a.Context.MkDiv(a, b); } /// Operator overloading for arithmetical operator public static ArithExpr operator /(ArithExpr a, int b) { return a / MkNum(a, b); } /// Operator overloading for arithmetical operator public static ArithExpr operator /(ArithExpr a, double b) { return a / MkNum(a, b); } /// Operator overloading for arithmetical operator public static ArithExpr operator /(int a, ArithExpr b) { return MkNum(b, a) / b; } /// Operator overloading for arithmetical operator public static ArithExpr operator /(double a, ArithExpr b) { return MkNum(b, a) / b; } /// Operator overloading for arithmetical operator public static ArithExpr operator -(ArithExpr a) { return a.Context.MkUnaryMinus(a); } /// Operator overloading for arithmetical operator public static ArithExpr operator -(ArithExpr a, ArithExpr b) { return a.Context.MkSub(a, b); } /// Operator overloading for arithmetical operator public static ArithExpr operator -(ArithExpr a, int b) { return a - MkNum(a, b); } /// Operator overloading for arithmetical operator public static ArithExpr operator -(ArithExpr a, double b) { return a - MkNum(a, b); } /// Operator overloading for arithmetical operator public static ArithExpr operator -(int a, ArithExpr b) { return MkNum(b, a) - b; } /// Operator overloading for arithmetical operator public static ArithExpr operator -(double a, ArithExpr b) { return MkNum(b, a) - b; } /// Operator overloading for arithmetical operator public static ArithExpr operator +(ArithExpr a, ArithExpr b) { return a.Context.MkAdd(a, b); } /// Operator overloading for arithmetical operator public static ArithExpr operator +(ArithExpr a, int b) { return a + MkNum(a, b); } /// Operator overloading for arithmetical operator public static ArithExpr operator +(ArithExpr a, double b) { return a + MkNum(a, b); } /// Operator overloading for arithmetical operator public static ArithExpr operator +(int a, ArithExpr b) { return MkNum(b, a) + b; } /// Operator overloading for arithmetical operator public static ArithExpr operator +(double a, ArithExpr b) { return MkNum(b, a) + b; } /// Operator overloading for arithmetical operator public static ArithExpr operator *(ArithExpr a, ArithExpr b) { return a.Context.MkMul(a, b); } /// Operator overloading for arithmetical operator public static ArithExpr operator *(ArithExpr a, int b) { return a * MkNum(a, b); } /// Operator overloading for arithmetical operator public static ArithExpr operator *(ArithExpr a, double b) { return a * MkNum(a, b); } /// Operator overloading for arithmetical operator public static ArithExpr operator *(int a, ArithExpr b) { return MkNum(b, a) * b; } /// Operator overloading for arithmetical operator public static ArithExpr operator *(double a, ArithExpr b) { return MkNum(b, a) * b; } /// Operator overloading for arithmetical operator public static BoolExpr operator <=(ArithExpr a, ArithExpr b) { return a.Context.MkLe(a, b); } /// Operator overloading for arithmetical operator public static BoolExpr operator <=(ArithExpr a, int b) { return a <= MkNum(a, b); } /// Operator overloading for arithmetical operator public static BoolExpr operator <=(ArithExpr a, double b) { return a <= MkNum(a, b); } /// Operator overloading for arithmetical operator public static BoolExpr operator <=(int a, ArithExpr b) { return MkNum(b, a) <= b; } /// Operator overloading for arithmetical operator public static BoolExpr operator <=(double a, ArithExpr b) { return MkNum(b, a) <= b; } /// Operator overloading for arithmetical operator public static BoolExpr operator <(ArithExpr a, ArithExpr b) { return a.Context.MkLt(a, b); } /// Operator overloading for arithmetical operator public static BoolExpr operator <(ArithExpr a, int b) { return a < MkNum(a, b); } /// Operator overloading for arithmetical operator public static BoolExpr operator <(ArithExpr a, double b) { return a < MkNum(a, b); } /// Operator overloading for arithmetical operator public static BoolExpr operator <(int a, ArithExpr b) { return MkNum(b, a) < b; } /// Operator overloading for arithmetical operator public static BoolExpr operator <(double a, ArithExpr b) { return MkNum(b, a) < b; } /// Operator overloading for arithmetical operator public static BoolExpr operator >(ArithExpr a, ArithExpr b) { return a.Context.MkGt(a, b); } /// Operator overloading for arithmetical operator public static BoolExpr operator >(ArithExpr a, int b) { return a > MkNum(a, b); } /// Operator overloading for arithmetical operator public static BoolExpr operator >(ArithExpr a, double b) { return a > MkNum(a, b); } /// Operator overloading for arithmetical operator public static BoolExpr operator >(int a, ArithExpr b) { return MkNum(b, a) > b; } /// Operator overloading for arithmetical operator public static BoolExpr operator >(double a, ArithExpr b) { return MkNum(b, a) > b; } /// Operator overloading for arithmetical operator public static BoolExpr operator >=(ArithExpr a, ArithExpr b) { return a.Context.MkGe(a, b); } /// Operator overloading for arithmetical operator public static BoolExpr operator >=(ArithExpr a, int b) { return a >= MkNum(a, b); } /// Operator overloading for arithmetical operator public static BoolExpr operator >=(ArithExpr a, double b) { return a >= MkNum(a, b); } /// Operator overloading for arithmetical operator public static BoolExpr operator >=(int a, ArithExpr b) { return MkNum(b, a) >= b; } /// Operator overloading for arithmetical operator public static BoolExpr operator >=(double a, ArithExpr b) { return MkNum(b, a) >= b; } #endregion } } z3-z3-4.8.7/src/api/dotnet/ArithSort.cs000066400000000000000000000010421356505360400175270ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: ArithSort.cs Abstract: Z3 Managed API: Arith Sorts Author: Christoph Wintersteiger (cwinter) 2012-11-23 Notes: --*/ using System.Diagnostics; using System; namespace Microsoft.Z3 { /// /// An arithmetic sort, i.e., Int or Real. /// public class ArithSort : Sort { #region Internal internal ArithSort(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } #endregion }; } z3-z3-4.8.7/src/api/dotnet/ArrayExpr.cs000066400000000000000000000020611356505360400175270ustar00rootroot00000000000000/*++ Copyright () 2012 Microsoft Corporation Module Name: ArrayExpr.cs Abstract: Z3 Managed API: Array Expressions Author: Christoph Wintersteiger (cwinter) 2012-11-23 Notes: --*/ using System.Diagnostics; using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Microsoft.Z3 { /// /// Array expressions /// public class ArrayExpr : Expr { #region Internal /// Constructor for ArrayExpr internal ArrayExpr(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } #endregion /// Single argument select public Expr this[Expr index] { get { return Context.MkSelect(this, index); } } /// Multi argument select public Expr this[IEnumerable index] { get { return Context.MkSelect(this, index.ToArray()); } } } } z3-z3-4.8.7/src/api/dotnet/ArraySort.cs000066400000000000000000000033071356505360400175440ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: ArraySort.cs Abstract: Z3 Managed API: Array Sorts Author: Christoph Wintersteiger (cwinter) 2012-11-23 Notes: --*/ using System.Diagnostics; using System; namespace Microsoft.Z3 { /// /// Array sorts. /// public class ArraySort : Sort { /// /// The domain of the array sort. /// public Sort Domain { get { return Sort.Create(Context, Native.Z3_get_array_sort_domain(Context.nCtx, NativeObject)); } } /// /// The range of the array sort. /// public Sort Range { get { return Sort.Create(Context, Native.Z3_get_array_sort_range(Context.nCtx, NativeObject)); } } #region Internal internal ArraySort(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } internal ArraySort(Context ctx, Sort domain, Sort range) : base(ctx, Native.Z3_mk_array_sort(ctx.nCtx, domain.NativeObject, range.NativeObject)) { Debug.Assert(ctx != null); Debug.Assert(domain != null); Debug.Assert(range != null); } internal ArraySort(Context ctx, Sort[] domain, Sort range) : base(ctx, Native.Z3_mk_array_sort_n(ctx.nCtx, (uint)domain.Length, AST.ArrayToNative(domain), range.NativeObject)) { Debug.Assert(ctx != null); Debug.Assert(domain != null); Debug.Assert(range != null); } #endregion }; } z3-z3-4.8.7/src/api/dotnet/BitVecExpr.cs000066400000000000000000000015541356505360400176330ustar00rootroot00000000000000/*++ Copyright () 2012 Microsoft Corporation Module Name: BitVecExpr.cs Abstract: Z3 Managed API: BitVec Expressions Author: Christoph Wintersteiger (cwinter) 2012-11-23 Notes: --*/ using System.Diagnostics; using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Microsoft.Z3 { /// /// Bit-vector expressions /// public class BitVecExpr : Expr { /// /// The size of the sort of a bit-vector term. /// public uint SortSize { get { return ((BitVecSort)Sort).Size; } } #region Internal /// Constructor for BitVecExpr internal BitVecExpr(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } #endregion } } z3-z3-4.8.7/src/api/dotnet/BitVecNum.cs000066400000000000000000000052261356505360400174540ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: BitVecNum.cs Abstract: Z3 Managed API: BitVec Numerals Author: Christoph Wintersteiger (cwinter) 2012-03-20 Notes: --*/ using System.Diagnostics; using System; #if !FRAMEWORK_LT_4 using System.Numerics; #endif namespace Microsoft.Z3 { /// /// Bit-vector numerals /// public class BitVecNum : BitVecExpr { /// /// Retrieve the 64-bit unsigned integer value. /// public UInt64 UInt64 { get { UInt64 res = 0; if (Native.Z3_get_numeral_uint64(Context.nCtx, NativeObject, ref res) == 0) throw new Z3Exception("Numeral is not a 64 bit unsigned"); return res; } } /// /// Retrieve the int value. /// public int Int { get { int res = 0; if (Native.Z3_get_numeral_int(Context.nCtx, NativeObject, ref res) == 0) throw new Z3Exception("Numeral is not an int"); return res; } } /// /// Retrieve the 64-bit int value. /// public Int64 Int64 { get { Int64 res = 0; if (Native.Z3_get_numeral_int64(Context.nCtx, NativeObject, ref res) == 0) throw new Z3Exception("Numeral is not an int64"); return res; } } /// /// Retrieve the int value. /// public uint UInt { get { uint res = 0; if (Native.Z3_get_numeral_uint(Context.nCtx, NativeObject, ref res) == 0) throw new Z3Exception("Numeral is not a uint"); return res; } } #if !FRAMEWORK_LT_4 /// /// Retrieve the BigInteger value. /// public BigInteger BigInteger { get { return BigInteger.Parse(this.ToString()); } } #endif /// /// Returns a string representation of the numeral. /// public override string ToString() { return Native.Z3_get_numeral_string(Context.nCtx, NativeObject); } #region Internal internal BitVecNum(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } #endregion } } z3-z3-4.8.7/src/api/dotnet/BitVecSort.cs000066400000000000000000000013541356505360400176420ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: BitVecSort.cs Abstract: Z3 Managed API: BitVec Sorts Author: Christoph Wintersteiger (cwinter) 2012-11-23 Notes: --*/ using System.Diagnostics; using System; namespace Microsoft.Z3 { /// /// Bit-vector sorts. /// public class BitVecSort : Sort { /// /// The size of the bit-vector sort. /// public uint Size { get { return Native.Z3_get_bv_sort_size(Context.nCtx, NativeObject); } } #region Internal internal BitVecSort(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } #endregion }; } z3-z3-4.8.7/src/api/dotnet/BoolExpr.cs000066400000000000000000000025041356505360400173460ustar00rootroot00000000000000/*++ Copyright () 2012 Microsoft Corporation Module Name: BoolExpr.cs Abstract: Z3 Managed API: Boolean Expressions Author: Christoph Wintersteiger (cwinter) 2012-11-23 Notes: --*/ using System.Diagnostics; using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Microsoft.Z3 { /// /// Boolean expressions /// public class BoolExpr : Expr { #region Internal /// Constructor for BoolExpr internal BoolExpr(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } #endregion #region Operators /// Disjunction of Boolean expressions public static BoolExpr operator|(BoolExpr a, BoolExpr b) { return a.Context.MkOr(a, b); } /// Conjunction of Boolean expressions public static BoolExpr operator &(BoolExpr a, BoolExpr b) { return a.Context.MkAnd(a, b); } /// Xor of Boolean expressions public static BoolExpr operator ^(BoolExpr a, BoolExpr b) { return a.Context.MkXor(a, b); } /// Negation public static BoolExpr operator !(BoolExpr a) { return a.Context.MkNot(a); } #endregion } } z3-z3-4.8.7/src/api/dotnet/BoolSort.cs000066400000000000000000000011731356505360400173600ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: BoolSort.cs Abstract: Z3 Managed API: Bool Sorts Author: Christoph Wintersteiger (cwinter) 2012-11-23 Notes: --*/ using System.Diagnostics; using System; namespace Microsoft.Z3 { /// /// A Boolean sort. /// public class BoolSort : Sort { #region Internal internal BoolSort(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } internal BoolSort(Context ctx) : base(ctx, Native.Z3_mk_bool_sort(ctx.nCtx)) { Debug.Assert(ctx != null); } #endregion }; } z3-z3-4.8.7/src/api/dotnet/CMakeLists.txt000066400000000000000000000134141356505360400200270ustar00rootroot00000000000000find_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_TWEAK "${Z3_VERSION_TWEAK}") # Generate Native.cs set(Z3_DOTNET_NATIVE_FILE "${CMAKE_CURRENT_BINARY_DIR}/Native.cs") add_custom_command(OUTPUT "${Z3_DOTNET_NATIVE_FILE}" COMMAND "${PYTHON_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/scripts/update_api.py" ${Z3_FULL_PATH_API_HEADER_FILES_TO_SCAN} "--dotnet-output-dir" "${CMAKE_CURRENT_BINARY_DIR}" DEPENDS ${Z3_FULL_PATH_API_HEADER_FILES_TO_SCAN} "${PROJECT_SOURCE_DIR}/scripts/update_api.py" ${Z3_GENERATED_FILE_EXTRA_DEPENDENCIES} # FIXME: When update_api.py no longer uses ``mk_util`` drop this dependency "${PROJECT_SOURCE_DIR}/scripts/mk_util.py" COMMENT "Generating ${Z3_DOTNET_NATIVE_FILE}" ${ADD_CUSTOM_COMMAND_USES_TERMINAL_ARG} ) # Generate Enumerations.cs set(Z3_DOTNET_CONST_FILE "${CMAKE_CURRENT_BINARY_DIR}/Enumerations.cs") add_custom_command(OUTPUT "${Z3_DOTNET_CONST_FILE}" COMMAND "${PYTHON_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/scripts/mk_consts_files.py" ${Z3_FULL_PATH_API_HEADER_FILES_TO_SCAN} "--dotnet-output-dir" "${CMAKE_CURRENT_BINARY_DIR}" DEPENDS ${Z3_FULL_PATH_API_HEADER_FILES_TO_SCAN} "${PROJECT_SOURCE_DIR}/scripts/mk_consts_files.py" ${Z3_GENERATED_FILE_EXTRA_DEPENDENCIES} COMMENT "Generating ${Z3_DOTNET_CONST_FILE}" ${ADD_CUSTOM_COMMAND_USES_TERMINAL_ARG} ) set(Z3_DOTNET_ASSEMBLY_SOURCES_IN_SRC_TREE 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 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 IntExpr.cs IntNum.cs IntSort.cs IntSymbol.cs Lambda.cs ListSort.cs Log.cs Model.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 ) set(Z3_DOTNET_ASSEMBLY_SOURCES "") # Make paths to source files absolute foreach (csfile ${Z3_DOTNET_ASSEMBLY_SOURCES_IN_SRC_TREE}) list(APPEND Z3_DOTNET_ASSEMBLY_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/${csfile}") endforeach() # Add generated files list(APPEND Z3_DOTNET_ASSEMBLY_SOURCES "${Z3_DOTNET_CONST_FILE}" "${Z3_DOTNET_NATIVE_FILE}" ) # Generate items set(Z3_DOTNET_COMPILE_ITEMS "") foreach(csfile ${Z3_DOTNET_ASSEMBLY_SOURCES}) set(Z3_DOTNET_COMPILE_ITEMS "${Z3_DOTNET_COMPILE_ITEMS}\n ") endforeach() # 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() set(Z3_DOTNET_PLATFORM "AnyCPU") endif() # TODO conditional for signing. we can then enable the ``Release_delaysign`` configuration 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 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} ${PROJECT_BINARY_DIR}) ############################################################################### # 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(Z3_INSTALL_DOTNET_BINDINGS "Install .NET bindings when invoking install target" ON) if(Z3_INSTALL_DOTNET_BINDINGS) install(FILES "${PROJECT_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 "${PROJECT_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() z3-z3-4.8.7/src/api/dotnet/Constructor.cs000066400000000000000000000073031356505360400201430ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: Constructor.cs Abstract: Z3 Managed API: Constructors Author: Christoph Wintersteiger (cwinter) 2012-03-22 Notes: --*/ using System.Diagnostics; using System; namespace Microsoft.Z3 { /// /// Constructors are used for datatype sorts. /// public class Constructor : Z3Object { /// /// The number of fields of the constructor. /// public uint NumFields { get { return n; } } /// /// The function declaration of the constructor. /// public FuncDecl ConstructorDecl { get { IntPtr constructor = IntPtr.Zero; IntPtr tester = IntPtr.Zero; IntPtr[] accessors = new IntPtr[n]; Native.Z3_query_constructor(Context.nCtx, NativeObject, n, ref constructor, ref tester, accessors); return new FuncDecl(Context, constructor); } } /// /// The function declaration of the tester. /// public FuncDecl TesterDecl { get { IntPtr constructor = IntPtr.Zero; IntPtr tester = IntPtr.Zero; IntPtr[] accessors = new IntPtr[n]; Native.Z3_query_constructor(Context.nCtx, NativeObject, n, ref constructor, ref tester, accessors); return new FuncDecl(Context, tester); } } /// /// The function declarations of the accessors /// public FuncDecl[] AccessorDecls { get { IntPtr constructor = IntPtr.Zero; IntPtr tester = IntPtr.Zero; IntPtr[] accessors = new IntPtr[n]; Native.Z3_query_constructor(Context.nCtx, NativeObject, n, ref constructor, ref tester, accessors); FuncDecl[] t = new FuncDecl[n]; for (uint i = 0; i < n; i++) t[i] = new FuncDecl(Context, accessors[i]); return t; } } /// /// Destructor. /// ~Constructor() { Native.Z3_del_constructor(Context.nCtx, NativeObject); } #region Internal private uint n = 0; internal Constructor(Context ctx, Symbol name, Symbol recognizer, Symbol[] fieldNames, Sort[] sorts, uint[] sortRefs) : base(ctx) { Debug.Assert(ctx != null); Debug.Assert(name != null); Debug.Assert(recognizer != null); n = AST.ArrayLength(fieldNames); if (n != AST.ArrayLength(sorts)) throw new Z3Exception("Number of field names does not match number of sorts"); if (sortRefs != null && sortRefs.Length != n) throw new Z3Exception("Number of field names does not match number of sort refs"); if (sortRefs == null) sortRefs = new uint[n]; NativeObject = Native.Z3_mk_constructor(ctx.nCtx, name.NativeObject, recognizer.NativeObject, n, Symbol.ArrayToNative(fieldNames), Sort.ArrayToNative(sorts), sortRefs); } #endregion } } z3-z3-4.8.7/src/api/dotnet/ConstructorList.cs000066400000000000000000000022651356505360400210010ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: ConstructorList.cs Abstract: Z3 Managed API: Constructor Lists Author: Christoph Wintersteiger (cwinter) 2012-11-23 Notes: --*/ using System.Diagnostics; using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Microsoft.Z3 { /// /// Lists of constructors /// public class ConstructorList : Z3Object { /// /// Destructor. /// ~ConstructorList() { Native.Z3_del_constructor_list(Context.nCtx, NativeObject); } #region Internal internal ConstructorList(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } internal ConstructorList(Context ctx, Constructor[] constructors) : base(ctx) { Debug.Assert(ctx != null); Debug.Assert(constructors != null); NativeObject = Native.Z3_mk_constructor_list(Context.nCtx, (uint)constructors.Length, Constructor.ArrayToNative(constructors)); } #endregion } } z3-z3-4.8.7/src/api/dotnet/Context.cs000066400000000000000000005334001356505360400172440ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: Context.cs Abstract: Z3 Managed API: Context Author: Christoph Wintersteiger (cwinter) 2012-03-15 Notes: --*/ using System; using System.Diagnostics; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Linq; namespace Microsoft.Z3 { /// /// The main interaction with Z3 happens via the Context. /// public class Context : IDisposable { #region Constructors /// /// Constructor. /// public Context() : base() { lock (creation_lock) { m_ctx = Native.Z3_mk_context_rc(IntPtr.Zero); InitContext(); } } /// /// Constructor. /// /// /// The following parameters can be set: /// - proof (Boolean) Enable proof generation /// - debug_ref_count (Boolean) Enable debug support for Z3_ast reference counting /// - trace (Boolean) Tracing support for VCC /// - trace_file_name (String) Trace out file for VCC traces /// - timeout (unsigned) default timeout (in milliseconds) used for solvers /// - well_sorted_check type checker /// - auto_config use heuristics to automatically select solver and configure it /// - model model generation for solvers, this parameter can be overwritten when creating a solver /// - model_validate validate models produced by solvers /// - unsat_core unsat-core generation for solvers, this parameter can be overwritten when creating a solver /// Note that in previous versions of Z3, this constructor was also used to set global and module parameters. /// For this purpose we should now use /// public Context(Dictionary settings) : base() { Debug.Assert(settings != null); lock (creation_lock) { IntPtr cfg = Native.Z3_mk_config(); foreach (KeyValuePair kv in settings) Native.Z3_set_param_value(cfg, kv.Key, kv.Value); m_ctx = Native.Z3_mk_context_rc(cfg); Native.Z3_del_config(cfg); InitContext(); } } #endregion #region Symbols /// /// Creates a new symbol using an integer. /// /// /// Not all integers can be passed to this function. /// The legal range of unsigned integers is 0 to 2^30-1. /// public IntSymbol MkSymbol(int i) { return new IntSymbol(this, i); } /// /// Create a symbol using a string. /// public StringSymbol MkSymbol(string name) { return new StringSymbol(this, name); } /// /// Create an array of symbols. /// internal Symbol[] MkSymbols(string[] names) { if (names == null) return null; Symbol[] result = new Symbol[names.Length]; for (int i = 0; i < names.Length; ++i) result[i] = MkSymbol(names[i]); return result; } #endregion #region Sorts private BoolSort m_boolSort = null; private IntSort m_intSort = null; private RealSort m_realSort = null; private SeqSort m_stringSort = null; /// /// Retrieves the Boolean sort of the context. /// public BoolSort BoolSort { get { if (m_boolSort == null) m_boolSort = new BoolSort(this); return m_boolSort; } } /// /// Retrieves the Integer sort of the context. /// public IntSort IntSort { get { if (m_intSort == null) m_intSort = new IntSort(this); return m_intSort; } } /// /// Retrieves the Real sort of the context. /// public RealSort RealSort { get { if (m_realSort == null) m_realSort = new RealSort(this); return m_realSort; } } /// /// Retrieves the String sort of the context. /// public SeqSort StringSort { get { if (m_stringSort == null) m_stringSort = new SeqSort(this, Native.Z3_mk_string_sort(nCtx)); return m_stringSort; } } /// /// Create a new Boolean sort. /// public BoolSort MkBoolSort() { return new BoolSort(this); } /// /// Create a new uninterpreted sort. /// public UninterpretedSort MkUninterpretedSort(Symbol s) { Debug.Assert(s != null); CheckContextMatch(s); return new UninterpretedSort(this, s); } /// /// Create a new uninterpreted sort. /// public UninterpretedSort MkUninterpretedSort(string str) { return MkUninterpretedSort(MkSymbol(str)); } /// /// Create a new integer sort. /// public IntSort MkIntSort() { return new IntSort(this); } /// /// Create a real sort. /// public RealSort MkRealSort() { return new RealSort(this); } /// /// Create a new bit-vector sort. /// public BitVecSort MkBitVecSort(uint size) { return new BitVecSort(this, Native.Z3_mk_bv_sort(nCtx, size)); } /// /// Create a new sequence sort. /// public SeqSort MkSeqSort(Sort s) { Debug.Assert(s != null); return new SeqSort(this, Native.Z3_mk_seq_sort(nCtx, s.NativeObject)); } /// /// Create a new regular expression sort. /// public ReSort MkReSort(SeqSort s) { Debug.Assert(s != null); return new ReSort(this, Native.Z3_mk_re_sort(nCtx, s.NativeObject)); } /// /// Create a new array sort. /// public ArraySort MkArraySort(Sort domain, Sort range) { Debug.Assert(domain != null); Debug.Assert(range != null); CheckContextMatch(domain); CheckContextMatch(range); return new ArraySort(this, domain, range); } /// /// Create a new n-ary array sort. /// public ArraySort MkArraySort(Sort[] domain, Sort range) { Debug.Assert(domain != null); Debug.Assert(range != null); CheckContextMatch(domain); CheckContextMatch(range); return new ArraySort(this, domain, range); } /// /// Create a new tuple sort. /// public TupleSort MkTupleSort(Symbol name, Symbol[] fieldNames, Sort[] fieldSorts) { Debug.Assert(name != null); Debug.Assert(fieldNames != null); Debug.Assert(fieldNames.All(fn => fn != null)); Debug.Assert(fieldSorts == null || fieldSorts.All(fs => fs != null)); CheckContextMatch(name); CheckContextMatch(fieldNames); CheckContextMatch(fieldSorts); return new TupleSort(this, name, (uint)fieldNames.Length, fieldNames, fieldSorts); } /// /// Create a new enumeration sort. /// public EnumSort MkEnumSort(Symbol name, params Symbol[] enumNames) { Debug.Assert(name != null); Debug.Assert(enumNames != null); Debug.Assert(enumNames.All(f => f != null)); CheckContextMatch(name); CheckContextMatch(enumNames); return new EnumSort(this, name, enumNames); } /// /// Create a new enumeration sort. /// public EnumSort MkEnumSort(string name, params string[] enumNames) { Debug.Assert(enumNames != null); return new EnumSort(this, MkSymbol(name), MkSymbols(enumNames)); } /// /// Create a new list sort. /// public ListSort MkListSort(Symbol name, Sort elemSort) { Debug.Assert(name != null); Debug.Assert(elemSort != null); CheckContextMatch(name); CheckContextMatch(elemSort); return new ListSort(this, name, elemSort); } /// /// Create a new list sort. /// public ListSort MkListSort(string name, Sort elemSort) { Debug.Assert(elemSort != null); CheckContextMatch(elemSort); return new ListSort(this, MkSymbol(name), elemSort); } /// /// Create a new finite domain sort. /// The result is a sort /// /// The name used to identify the sort /// The size of the sort public FiniteDomainSort MkFiniteDomainSort(Symbol name, ulong size) { Debug.Assert(name != null); CheckContextMatch(name); return new FiniteDomainSort(this, name, size); } /// /// Create a new finite domain sort. /// The result is a sort /// Elements of the sort are created using , /// and the elements range from 0 to size-1. /// /// The name used to identify the sort /// The size of the sort public FiniteDomainSort MkFiniteDomainSort(string name, ulong size) { return new FiniteDomainSort(this, MkSymbol(name), size); } #region Datatypes /// /// Create a datatype constructor. /// /// constructor name /// name of recognizer function. /// names of the constructor fields. /// field sorts, 0 if the field sort refers to a recursive sort. /// reference to datatype sort that is an argument to the constructor; /// if the corresponding sort reference is 0, then the value in sort_refs should be an index /// referring to one of the recursive datatypes that is declared. public Constructor MkConstructor(Symbol name, Symbol recognizer, Symbol[] fieldNames = null, Sort[] sorts = null, uint[] sortRefs = null) { Debug.Assert(name != null); Debug.Assert(recognizer != null); return new Constructor(this, name, recognizer, fieldNames, sorts, sortRefs); } /// /// Create a datatype constructor. /// /// /// /// /// /// /// public Constructor MkConstructor(string name, string recognizer, string[] fieldNames = null, Sort[] sorts = null, uint[] sortRefs = null) { return new Constructor(this, MkSymbol(name), MkSymbol(recognizer), MkSymbols(fieldNames), sorts, sortRefs); } /// /// Create a new datatype sort. /// public DatatypeSort MkDatatypeSort(Symbol name, Constructor[] constructors) { Debug.Assert(name != null); Debug.Assert(constructors != null); Debug.Assert(constructors.All(c => c != null)); CheckContextMatch(name); CheckContextMatch(constructors); return new DatatypeSort(this, name, constructors); } /// /// Create a new datatype sort. /// public DatatypeSort MkDatatypeSort(string name, Constructor[] constructors) { Debug.Assert(constructors != null); Debug.Assert(constructors.All(c => c != null)); CheckContextMatch(constructors); return new DatatypeSort(this, MkSymbol(name), constructors); } /// /// Create mutually recursive datatypes. /// /// names of datatype sorts /// list of constructors, one list per sort. public DatatypeSort[] MkDatatypeSorts(Symbol[] names, Constructor[][] c) { Debug.Assert(names != null); Debug.Assert(c != null); Debug.Assert(names.Length == c.Length); //Debug.Assert(Contract.ForAll(0, c.Length, j => c[j] != null)); Debug.Assert(names.All(name => name != null)); CheckContextMatch(names); uint n = (uint)names.Length; ConstructorList[] cla = new ConstructorList[n]; IntPtr[] n_constr = new IntPtr[n]; for (uint i = 0; i < n; i++) { Constructor[] constructor = c[i]; CheckContextMatch(constructor); cla[i] = new ConstructorList(this, constructor); n_constr[i] = cla[i].NativeObject; } IntPtr[] n_res = new IntPtr[n]; Native.Z3_mk_datatypes(nCtx, n, Symbol.ArrayToNative(names), n_res, n_constr); DatatypeSort[] res = new DatatypeSort[n]; for (uint i = 0; i < n; i++) res[i] = new DatatypeSort(this, n_res[i]); return res; } /// /// Create mutually recursive data-types. /// /// /// /// public DatatypeSort[] MkDatatypeSorts(string[] names, Constructor[][] c) { Debug.Assert(names != null); Debug.Assert(c != null); Debug.Assert(names.Length == c.Length); //Debug.Assert(Contract.ForAll(0, c.Length, j => c[j] != null)); //Debug.Assert(names.All(name => name != null)); return MkDatatypeSorts(MkSymbols(names), c); } /// /// 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 remaining fields of t are unchanged. /// public Expr MkUpdateField(FuncDecl field, Expr t, Expr v) { return Expr.Create(this, Native.Z3_datatype_update_field( nCtx, field.NativeObject, t.NativeObject, v.NativeObject)); } #endregion #endregion #region Function Declarations /// /// Creates a new function declaration. /// public FuncDecl MkFuncDecl(Symbol name, Sort[] domain, Sort range) { Debug.Assert(name != null); Debug.Assert(range != null); Debug.Assert(domain.All(d => d != null)); CheckContextMatch(name); CheckContextMatch(domain); CheckContextMatch(range); return new FuncDecl(this, name, domain, range); } /// /// Creates a new function declaration. /// public FuncDecl MkFuncDecl(Symbol name, Sort domain, Sort range) { Debug.Assert(name != null); Debug.Assert(domain != null); Debug.Assert(range != null); CheckContextMatch(name); CheckContextMatch(domain); CheckContextMatch(range); Sort[] q = new Sort[] { domain }; return new FuncDecl(this, name, q, range); } /// /// Creates a new function declaration. /// public FuncDecl MkFuncDecl(string name, Sort[] domain, Sort range) { Debug.Assert(range != null); Debug.Assert(domain.All(d => d != null)); CheckContextMatch(domain); CheckContextMatch(range); return new FuncDecl(this, MkSymbol(name), domain, range); } /// /// Creates a new recursive function declaration. /// public FuncDecl MkRecFuncDecl(string name, Sort[] domain, Sort range) { Debug.Assert(range != null); Debug.Assert(domain.All(d => d != null)); CheckContextMatch(domain); CheckContextMatch(range); return new FuncDecl(this, MkSymbol(name), domain, range, true); } /// /// Bind a definition to a recursive function declaration. /// The function must have previously been created using /// MkRecFuncDecl. The body may contain recursive uses of the function or /// other mutually recursive functions. /// public void AddRecDef(FuncDecl f, Expr[] args, Expr body) { CheckContextMatch(f); CheckContextMatch(args); CheckContextMatch(body); IntPtr[] argsNative = AST.ArrayToNative(args); Native.Z3_add_rec_def(nCtx, f.NativeObject, (uint)args.Length, argsNative, body.NativeObject); } /// /// Creates a new function declaration. /// public FuncDecl MkFuncDecl(string name, Sort domain, Sort range) { Debug.Assert(range != null); Debug.Assert(domain != null); CheckContextMatch(domain); CheckContextMatch(range); Sort[] q = new Sort[] { domain }; return new FuncDecl(this, MkSymbol(name), q, range); } /// /// Creates a fresh function declaration with a name prefixed with . /// /// /// public FuncDecl MkFreshFuncDecl(string prefix, Sort[] domain, Sort range) { Debug.Assert(range != null); Debug.Assert(domain.All(d => d != null)); CheckContextMatch(domain); CheckContextMatch(range); return new FuncDecl(this, prefix, domain, range); } /// /// Creates a new constant function declaration. /// public FuncDecl MkConstDecl(Symbol name, Sort range) { Debug.Assert(name != null); Debug.Assert(range != null); CheckContextMatch(name); CheckContextMatch(range); return new FuncDecl(this, name, null, range); } /// /// Creates a new constant function declaration. /// public FuncDecl MkConstDecl(string name, Sort range) { Debug.Assert(range != null); CheckContextMatch(range); return new FuncDecl(this, MkSymbol(name), null, range); } /// /// Creates a fresh constant function declaration with a name prefixed with . /// /// /// public FuncDecl MkFreshConstDecl(string prefix, Sort range) { Debug.Assert(range != null); CheckContextMatch(range); return new FuncDecl(this, prefix, null, range); } #endregion #region Bound Variables /// /// Creates a new bound variable. /// /// The de-Bruijn index of the variable /// The sort of the variable public Expr MkBound(uint index, Sort ty) { Debug.Assert(ty != null); return Expr.Create(this, Native.Z3_mk_bound(nCtx, index, ty.NativeObject)); } #endregion #region Quantifier Patterns /// /// Create a quantifier pattern. /// public Pattern MkPattern(params Expr[] terms) { Debug.Assert(terms != null); if (terms.Length == 0) throw new Z3Exception("Cannot create a pattern from zero terms"); IntPtr[] termsNative = AST.ArrayToNative(terms); return new Pattern(this, Native.Z3_mk_pattern(nCtx, (uint)terms.Length, termsNative)); } #endregion #region Constants /// /// Creates a new Constant of sort and named . /// public Expr MkConst(Symbol name, Sort range) { Debug.Assert(name != null); Debug.Assert(range != null); CheckContextMatch(name); CheckContextMatch(range); return Expr.Create(this, Native.Z3_mk_const(nCtx, name.NativeObject, range.NativeObject)); } /// /// Creates a new Constant of sort and named . /// public Expr MkConst(string name, Sort range) { Debug.Assert(range != null); return MkConst(MkSymbol(name), range); } /// /// Creates a fresh Constant of sort and a /// name prefixed with . /// public Expr MkFreshConst(string prefix, Sort range) { Debug.Assert(range != null); CheckContextMatch(range); return Expr.Create(this, Native.Z3_mk_fresh_const(nCtx, prefix, range.NativeObject)); } /// /// Creates a fresh constant from the FuncDecl . /// /// A decl of a 0-arity function public Expr MkConst(FuncDecl f) { Debug.Assert(f != null); return MkApp(f); } /// /// Create a Boolean constant. /// public BoolExpr MkBoolConst(Symbol name) { Debug.Assert(name != null); return (BoolExpr)MkConst(name, BoolSort); } /// /// Create a Boolean constant. /// public BoolExpr MkBoolConst(string name) { return (BoolExpr)MkConst(MkSymbol(name), BoolSort); } /// /// Creates an integer constant. /// public IntExpr MkIntConst(Symbol name) { Debug.Assert(name != null); return (IntExpr)MkConst(name, IntSort); } /// /// Creates an integer constant. /// public IntExpr MkIntConst(string name) { Debug.Assert(name != null); return (IntExpr)MkConst(name, IntSort); } /// /// Creates a real constant. /// public RealExpr MkRealConst(Symbol name) { Debug.Assert(name != null); return (RealExpr)MkConst(name, RealSort); } /// /// Creates a real constant. /// public RealExpr MkRealConst(string name) { return (RealExpr)MkConst(name, RealSort); } /// /// Creates a bit-vector constant. /// public BitVecExpr MkBVConst(Symbol name, uint size) { Debug.Assert(name != null); return (BitVecExpr)MkConst(name, MkBitVecSort(size)); } /// /// Creates a bit-vector constant. /// public BitVecExpr MkBVConst(string name, uint size) { return (BitVecExpr)MkConst(name, MkBitVecSort(size)); } #endregion #region Terms /// /// Create a new function application. /// public Expr MkApp(FuncDecl f, params Expr[] args) { Debug.Assert(f != null); Debug.Assert(args == null || args.All(a => a != null)); CheckContextMatch(f); CheckContextMatch(args); return Expr.Create(this, f, args); } /// /// Create a new function application. /// public Expr MkApp(FuncDecl f, IEnumerable args) { Debug.Assert(f != null); Debug.Assert(args == null || args.All( a => a != null)); CheckContextMatch(f); CheckContextMatch(args); return Expr.Create(this, f, args.ToArray()); } #region Propositional /// /// The true Term. /// public BoolExpr MkTrue() { return new BoolExpr(this, Native.Z3_mk_true(nCtx)); } /// /// The false Term. /// public BoolExpr MkFalse() { return new BoolExpr(this, Native.Z3_mk_false(nCtx)); } /// /// Creates a Boolean value. /// public BoolExpr MkBool(bool value) { return value ? MkTrue() : MkFalse(); } /// /// Creates the equality = . /// public BoolExpr MkEq(Expr x, Expr y) { Debug.Assert(x != null); Debug.Assert(y != null); CheckContextMatch(x); CheckContextMatch(y); return new BoolExpr(this, Native.Z3_mk_eq(nCtx, x.NativeObject, y.NativeObject)); } /// /// Creates a distinct term. /// public BoolExpr MkDistinct(params Expr[] args) { Debug.Assert(args != null); Debug.Assert(args.All(a => a != null)); CheckContextMatch(args); return new BoolExpr(this, Native.Z3_mk_distinct(nCtx, (uint)args.Length, AST.ArrayToNative(args))); } /// /// Mk an expression representing not(a). /// public BoolExpr MkNot(BoolExpr a) { Debug.Assert(a != null); CheckContextMatch(a); return new BoolExpr(this, Native.Z3_mk_not(nCtx, a.NativeObject)); } /// /// Create an expression representing an if-then-else: ite(t1, t2, t3). /// /// An expression with Boolean sort /// An expression /// An expression with the same sort as public Expr MkITE(BoolExpr t1, Expr t2, Expr t3) { Debug.Assert(t1 != null); Debug.Assert(t2 != null); Debug.Assert(t3 != null); CheckContextMatch(t1); CheckContextMatch(t2); CheckContextMatch(t3); return Expr.Create(this, Native.Z3_mk_ite(nCtx, t1.NativeObject, t2.NativeObject, t3.NativeObject)); } /// /// Create an expression representing t1 iff t2. /// public BoolExpr MkIff(BoolExpr t1, BoolExpr t2) { Debug.Assert(t1 != null); Debug.Assert(t2 != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BoolExpr(this, Native.Z3_mk_iff(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Create an expression representing t1 -> t2. /// public BoolExpr MkImplies(BoolExpr t1, BoolExpr t2) { Debug.Assert(t1 != null); Debug.Assert(t2 != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BoolExpr(this, Native.Z3_mk_implies(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Create an expression representing t1 xor t2. /// public BoolExpr MkXor(BoolExpr t1, BoolExpr t2) { Debug.Assert(t1 != null); Debug.Assert(t2 != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BoolExpr(this, Native.Z3_mk_xor(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Create an expression representing t1 xor t2 xor t3 ... . /// public BoolExpr MkXor(IEnumerable ts) { Debug.Assert(ts != null); Debug.Assert(ts.All(a => a != null)); CheckContextMatch(ts); BoolExpr r = null; foreach (var t in ts) { if (r == null) r = t; else r = MkXor(r, t); } if (r == null) r = MkTrue(); return r; } /// /// Create an expression representing t[0] and t[1] and .... /// public BoolExpr MkAnd(params BoolExpr[] t) { Debug.Assert(t != null); Debug.Assert(t.All(a => a != null)); CheckContextMatch(t); return new BoolExpr(this, Native.Z3_mk_and(nCtx, (uint)t.Length, AST.ArrayToNative(t))); } /// /// Create an expression representing t[0] and t[1] and .... /// public BoolExpr MkAnd(IEnumerable t) { Debug.Assert(t != null); Debug.Assert(t.All(a => a != null)); CheckContextMatch(t); return new BoolExpr(this, Native.Z3_mk_and(nCtx, (uint)t.Count(), AST.EnumToNative(t))); } /// /// Create an expression representing t[0] or t[1] or .... /// public BoolExpr MkOr(params BoolExpr[] t) { Debug.Assert(t != null); Debug.Assert(t.All(a => a != null)); CheckContextMatch(t); return new BoolExpr(this, Native.Z3_mk_or(nCtx, (uint)t.Length, AST.ArrayToNative(t))); } /// /// Create an expression representing t[0] or t[1] or .... /// public BoolExpr MkOr(IEnumerable t) { Debug.Assert(t != null); Debug.Assert(t.All(a => a != null)); CheckContextMatch(t); return new BoolExpr(this, Native.Z3_mk_or(nCtx, (uint)t.Count(), AST.EnumToNative(t))); } #endregion #region Arithmetic /// /// Create an expression representing t[0] + t[1] + .... /// public ArithExpr MkAdd(params ArithExpr[] t) { Debug.Assert(t != null); Debug.Assert(t.All(a => a != null)); CheckContextMatch(t); return (ArithExpr)Expr.Create(this, Native.Z3_mk_add(nCtx, (uint)t.Length, AST.ArrayToNative(t))); } /// /// Create an expression representing t[0] + t[1] + .... /// public ArithExpr MkAdd(IEnumerable t) { Debug.Assert(t != null); Debug.Assert(t.All(a => a != null)); CheckContextMatch(t); return (ArithExpr)Expr.Create(this, Native.Z3_mk_add(nCtx, (uint)t.Count(), AST.EnumToNative(t))); } /// /// Create an expression representing t[0] * t[1] * .... /// public ArithExpr MkMul(params ArithExpr[] t) { Debug.Assert(t != null); Debug.Assert(t.All(a => a != null)); CheckContextMatch(t); return (ArithExpr)Expr.Create(this, Native.Z3_mk_mul(nCtx, (uint)t.Length, AST.ArrayToNative(t))); } /// /// Create an expression representing t[0] * t[1] * .... /// public ArithExpr MkMul(IEnumerable t) { Debug.Assert(t != null); Debug.Assert(t.All(a => a != null)); CheckContextMatch(t); return (ArithExpr)Expr.Create(this, Native.Z3_mk_mul(nCtx, (uint)t.Count(), AST.EnumToNative(t))); } /// /// Create an expression representing t[0] - t[1] - .... /// public ArithExpr MkSub(params ArithExpr[] t) { Debug.Assert(t != null); Debug.Assert(t.All(a => a != null)); CheckContextMatch(t); return (ArithExpr)Expr.Create(this, Native.Z3_mk_sub(nCtx, (uint)t.Length, AST.ArrayToNative(t))); } /// /// Create an expression representing -t. /// public ArithExpr MkUnaryMinus(ArithExpr t) { Debug.Assert(t != null); CheckContextMatch(t); return (ArithExpr)Expr.Create(this, Native.Z3_mk_unary_minus(nCtx, t.NativeObject)); } /// /// Create an expression representing t1 / t2. /// public ArithExpr MkDiv(ArithExpr t1, ArithExpr t2) { Debug.Assert(t1 != null); Debug.Assert(t2 != null); CheckContextMatch(t1); CheckContextMatch(t2); return (ArithExpr)Expr.Create(this, Native.Z3_mk_div(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Create an expression representing t1 mod t2. /// /// The arguments must have int type. public IntExpr MkMod(IntExpr t1, IntExpr t2) { Debug.Assert(t1 != null); Debug.Assert(t2 != null); CheckContextMatch(t1); CheckContextMatch(t2); return new IntExpr(this, Native.Z3_mk_mod(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Create an expression representing t1 rem t2. /// /// The arguments must have int type. public IntExpr MkRem(IntExpr t1, IntExpr t2) { Debug.Assert(t1 != null); Debug.Assert(t2 != null); CheckContextMatch(t1); CheckContextMatch(t2); return new IntExpr(this, Native.Z3_mk_rem(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Create an expression representing t1 ^ t2. /// public ArithExpr MkPower(ArithExpr t1, ArithExpr t2) { Debug.Assert(t1 != null); Debug.Assert(t2 != null); CheckContextMatch(t1); CheckContextMatch(t2); return (ArithExpr)Expr.Create(this, Native.Z3_mk_power(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Create an expression representing t1 < t2 /// public BoolExpr MkLt(ArithExpr t1, ArithExpr t2) { Debug.Assert(t1 != null); Debug.Assert(t2 != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BoolExpr(this, Native.Z3_mk_lt(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Create an expression representing t1 <= t2 /// public BoolExpr MkLe(ArithExpr t1, ArithExpr t2) { Debug.Assert(t1 != null); Debug.Assert(t2 != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BoolExpr(this, Native.Z3_mk_le(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Create an expression representing t1 > t2 /// public BoolExpr MkGt(ArithExpr t1, ArithExpr t2) { Debug.Assert(t1 != null); Debug.Assert(t2 != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BoolExpr(this, Native.Z3_mk_gt(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Create an expression representing t1 >= t2 /// public BoolExpr MkGe(ArithExpr t1, ArithExpr t2) { Debug.Assert(t1 != null); Debug.Assert(t2 != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BoolExpr(this, Native.Z3_mk_ge(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Coerce an integer to a real. /// /// /// There is also a converse operation exposed. It follows the semantics prescribed by the SMT-LIB standard. /// /// You can take the floor of a real by creating an auxiliary integer Term k and /// and asserting MakeInt2Real(k) <= t1 < MkInt2Real(k)+1. /// The argument must be of integer sort. /// public RealExpr MkInt2Real(IntExpr t) { Debug.Assert(t != null); CheckContextMatch(t); return new RealExpr(this, Native.Z3_mk_int2real(nCtx, t.NativeObject)); } /// /// Coerce a real to an integer. /// /// /// The semantics of this function follows the SMT-LIB standard for the function to_int. /// The argument must be of real sort. /// public IntExpr MkReal2Int(RealExpr t) { Debug.Assert(t != null); CheckContextMatch(t); return new IntExpr(this, Native.Z3_mk_real2int(nCtx, t.NativeObject)); } /// /// Creates an expression that checks whether a real number is an integer. /// public BoolExpr MkIsInteger(RealExpr t) { Debug.Assert(t != null); CheckContextMatch(t); return new BoolExpr(this, Native.Z3_mk_is_int(nCtx, t.NativeObject)); } #endregion #region Bit-vectors /// /// Bitwise negation. /// /// The argument must have a bit-vector sort. public BitVecExpr MkBVNot(BitVecExpr t) { Debug.Assert(t != null); CheckContextMatch(t); return new BitVecExpr(this, Native.Z3_mk_bvnot(nCtx, t.NativeObject)); } /// /// Take conjunction of bits in a vector, return vector of length 1. /// /// The argument must have a bit-vector sort. public BitVecExpr MkBVRedAND(BitVecExpr t) { Debug.Assert(t != null); CheckContextMatch(t); return new BitVecExpr(this, Native.Z3_mk_bvredand(nCtx, t.NativeObject)); } /// /// Take disjunction of bits in a vector, return vector of length 1. /// /// The argument must have a bit-vector sort. public BitVecExpr MkBVRedOR(BitVecExpr t) { Debug.Assert(t != null); CheckContextMatch(t); return new BitVecExpr(this, Native.Z3_mk_bvredor(nCtx, t.NativeObject)); } /// /// Bitwise conjunction. /// /// The arguments must have a bit-vector sort. public BitVecExpr MkBVAND(BitVecExpr t1, BitVecExpr t2) { Debug.Assert(t1 != null); Debug.Assert(t2 != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BitVecExpr(this, Native.Z3_mk_bvand(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Bitwise disjunction. /// /// The arguments must have a bit-vector sort. public BitVecExpr MkBVOR(BitVecExpr t1, BitVecExpr t2) { Debug.Assert(t1 != null); Debug.Assert(t2 != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BitVecExpr(this, Native.Z3_mk_bvor(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Bitwise XOR. /// /// The arguments must have a bit-vector sort. public BitVecExpr MkBVXOR(BitVecExpr t1, BitVecExpr t2) { Debug.Assert(t1 != null); Debug.Assert(t2 != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BitVecExpr(this, Native.Z3_mk_bvxor(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Bitwise NAND. /// /// The arguments must have a bit-vector sort. public BitVecExpr MkBVNAND(BitVecExpr t1, BitVecExpr t2) { Debug.Assert(t1 != null); Debug.Assert(t2 != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BitVecExpr(this, Native.Z3_mk_bvnand(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Bitwise NOR. /// /// The arguments must have a bit-vector sort. public BitVecExpr MkBVNOR(BitVecExpr t1, BitVecExpr t2) { Debug.Assert(t1 != null); Debug.Assert(t2 != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BitVecExpr(this, Native.Z3_mk_bvnor(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Bitwise XNOR. /// /// The arguments must have a bit-vector sort. public BitVecExpr MkBVXNOR(BitVecExpr t1, BitVecExpr t2) { Debug.Assert(t1 != null); Debug.Assert(t2 != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BitVecExpr(this, Native.Z3_mk_bvxnor(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Standard two's complement unary minus. /// /// The arguments must have a bit-vector sort. public BitVecExpr MkBVNeg(BitVecExpr t) { Debug.Assert(t != null); CheckContextMatch(t); return new BitVecExpr(this, Native.Z3_mk_bvneg(nCtx, t.NativeObject)); } /// /// Two's complement addition. /// /// The arguments must have the same bit-vector sort. public BitVecExpr MkBVAdd(BitVecExpr t1, BitVecExpr t2) { Debug.Assert(t1 != null); Debug.Assert(t2 != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BitVecExpr(this, Native.Z3_mk_bvadd(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Two's complement subtraction. /// /// The arguments must have the same bit-vector sort. public BitVecExpr MkBVSub(BitVecExpr t1, BitVecExpr t2) { Debug.Assert(t1 != null); Debug.Assert(t2 != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BitVecExpr(this, Native.Z3_mk_bvsub(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Two's complement multiplication. /// /// The arguments must have the same bit-vector sort. public BitVecExpr MkBVMul(BitVecExpr t1, BitVecExpr t2) { Debug.Assert(t1 != null); Debug.Assert(t2 != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BitVecExpr(this, Native.Z3_mk_bvmul(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Unsigned division. /// /// /// It is defined as the floor of t1/t2 if \c t2 is /// different from zero. If t2 is zero, then the result /// is undefined. /// The arguments must have the same bit-vector sort. /// public BitVecExpr MkBVUDiv(BitVecExpr t1, BitVecExpr t2) { Debug.Assert(t1 != null); Debug.Assert(t2 != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BitVecExpr(this, Native.Z3_mk_bvudiv(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Signed division. /// /// /// It is defined in the following way: /// /// - The \c floor of t1/t2 if \c t2 is different from zero, and t1*t2 >= 0. /// /// - The \c ceiling of t1/t2 if \c t2 is different from zero, and t1*t2 < 0. /// /// If t2 is zero, then the result is undefined. /// The arguments must have the same bit-vector sort. /// public BitVecExpr MkBVSDiv(BitVecExpr t1, BitVecExpr t2) { Debug.Assert(t1 != null); Debug.Assert(t2 != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BitVecExpr(this, Native.Z3_mk_bvsdiv(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Unsigned remainder. /// /// /// It is defined as t1 - (t1 /u t2) * t2, where /u represents unsigned division. /// If t2 is zero, then the result is undefined. /// The arguments must have the same bit-vector sort. /// public BitVecExpr MkBVURem(BitVecExpr t1, BitVecExpr t2) { Debug.Assert(t1 != null); Debug.Assert(t2 != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BitVecExpr(this, Native.Z3_mk_bvurem(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Signed remainder. /// /// /// It is defined as t1 - (t1 /s t2) * t2, where /s represents signed division. /// The most significant bit (sign) of the result is equal to the most significant bit of \c t1. /// /// If t2 is zero, then the result is undefined. /// The arguments must have the same bit-vector sort. /// public BitVecExpr MkBVSRem(BitVecExpr t1, BitVecExpr t2) { Debug.Assert(t1 != null); Debug.Assert(t2 != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BitVecExpr(this, Native.Z3_mk_bvsrem(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Two's complement signed remainder (sign follows divisor). /// /// /// If t2 is zero, then the result is undefined. /// The arguments must have the same bit-vector sort. /// public BitVecExpr MkBVSMod(BitVecExpr t1, BitVecExpr t2) { Debug.Assert(t1 != null); Debug.Assert(t2 != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BitVecExpr(this, Native.Z3_mk_bvsmod(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Unsigned less-than /// /// /// The arguments must have the same bit-vector sort. /// public BoolExpr MkBVULT(BitVecExpr t1, BitVecExpr t2) { Debug.Assert(t1 != null); Debug.Assert(t2 != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BoolExpr(this, Native.Z3_mk_bvult(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Two's complement signed less-than /// /// /// The arguments must have the same bit-vector sort. /// public BoolExpr MkBVSLT(BitVecExpr t1, BitVecExpr t2) { Debug.Assert(t1 != null); Debug.Assert(t2 != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BoolExpr(this, Native.Z3_mk_bvslt(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Unsigned less-than or equal to. /// /// /// The arguments must have the same bit-vector sort. /// public BoolExpr MkBVULE(BitVecExpr t1, BitVecExpr t2) { Debug.Assert(t1 != null); Debug.Assert(t2 != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BoolExpr(this, Native.Z3_mk_bvule(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Two's complement signed less-than or equal to. /// /// /// The arguments must have the same bit-vector sort. /// public BoolExpr MkBVSLE(BitVecExpr t1, BitVecExpr t2) { Debug.Assert(t1 != null); Debug.Assert(t2 != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BoolExpr(this, Native.Z3_mk_bvsle(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Unsigned greater than or equal to. /// /// /// The arguments must have the same bit-vector sort. /// public BoolExpr MkBVUGE(BitVecExpr t1, BitVecExpr t2) { Debug.Assert(t1 != null); Debug.Assert(t2 != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BoolExpr(this, Native.Z3_mk_bvuge(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Two's complement signed greater than or equal to. /// /// /// The arguments must have the same bit-vector sort. /// public BoolExpr MkBVSGE(BitVecExpr t1, BitVecExpr t2) { Debug.Assert(t1 != null); Debug.Assert(t2 != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BoolExpr(this, Native.Z3_mk_bvsge(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Unsigned greater-than. /// /// /// The arguments must have the same bit-vector sort. /// public BoolExpr MkBVUGT(BitVecExpr t1, BitVecExpr t2) { Debug.Assert(t1 != null); Debug.Assert(t2 != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BoolExpr(this, Native.Z3_mk_bvugt(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Two's complement signed greater-than. /// /// /// The arguments must have the same bit-vector sort. /// public BoolExpr MkBVSGT(BitVecExpr t1, BitVecExpr t2) { Debug.Assert(t1 != null); Debug.Assert(t2 != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BoolExpr(this, Native.Z3_mk_bvsgt(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Bit-vector concatenation. /// /// /// The arguments must have a bit-vector sort. /// /// /// The result is a bit-vector of size n1+n2, where n1 (n2) /// is the size of t1 (t2). /// public BitVecExpr MkConcat(BitVecExpr t1, BitVecExpr t2) { Debug.Assert(t1 != null); Debug.Assert(t2 != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BitVecExpr(this, Native.Z3_mk_concat(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Bit-vector extraction. /// /// /// Extract the bits down to from a bitvector of /// size m to yield a new bitvector of size n, where /// n = high - low + 1. /// The argument must have a bit-vector sort. /// public BitVecExpr MkExtract(uint high, uint low, BitVecExpr t) { Debug.Assert(t != null); CheckContextMatch(t); return new BitVecExpr(this, Native.Z3_mk_extract(nCtx, high, low, t.NativeObject)); } /// /// Bit-vector sign extension. /// /// /// Sign-extends the given bit-vector to the (signed) equivalent bitvector of /// size m+i, where \c m is the size of the given bit-vector. /// The argument must have a bit-vector sort. /// public BitVecExpr MkSignExt(uint i, BitVecExpr t) { Debug.Assert(t != null); CheckContextMatch(t); return new BitVecExpr(this, Native.Z3_mk_sign_ext(nCtx, i, t.NativeObject)); } /// /// Bit-vector zero extension. /// /// /// Extend the given bit-vector with zeros to the (unsigned) equivalent /// bitvector of size m+i, where \c m is the size of the /// given bit-vector. /// The argument must have a bit-vector sort. /// public BitVecExpr MkZeroExt(uint i, BitVecExpr t) { Debug.Assert(t != null); CheckContextMatch(t); return new BitVecExpr(this, Native.Z3_mk_zero_ext(nCtx, i, t.NativeObject)); } /// /// Bit-vector repetition. /// /// /// The argument must have a bit-vector sort. /// public BitVecExpr MkRepeat(uint i, BitVecExpr t) { Debug.Assert(t != null); CheckContextMatch(t); return new BitVecExpr(this, Native.Z3_mk_repeat(nCtx, i, t.NativeObject)); } /// /// Shift left. /// /// /// It is equivalent to multiplication by 2^x where \c x is the value of . /// /// NB. The semantics of shift operations varies between environments. This /// definition does not necessarily capture directly the semantics of the /// programming language or assembly architecture you are modeling. /// /// The arguments must have a bit-vector sort. /// public BitVecExpr MkBVSHL(BitVecExpr t1, BitVecExpr t2) { Debug.Assert(t1 != null); Debug.Assert(t2 != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BitVecExpr(this, Native.Z3_mk_bvshl(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Logical shift right /// /// /// It is equivalent to unsigned division by 2^x where \c x is the value of . /// /// NB. The semantics of shift operations varies between environments. This /// definition does not necessarily capture directly the semantics of the /// programming language or assembly architecture you are modeling. /// /// The arguments must have a bit-vector sort. /// public BitVecExpr MkBVLSHR(BitVecExpr t1, BitVecExpr t2) { Debug.Assert(t1 != null); Debug.Assert(t2 != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BitVecExpr(this, Native.Z3_mk_bvlshr(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Arithmetic shift right /// /// /// It is like logical shift right except that the most significant /// bits of the result always copy the most significant bit of the /// second argument. /// /// NB. The semantics of shift operations varies between environments. This /// definition does not necessarily capture directly the semantics of the /// programming language or assembly architecture you are modeling. /// /// The arguments must have a bit-vector sort. /// public BitVecExpr MkBVASHR(BitVecExpr t1, BitVecExpr t2) { Debug.Assert(t1 != null); Debug.Assert(t2 != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BitVecExpr(this, Native.Z3_mk_bvashr(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Rotate Left. /// /// /// Rotate bits of \c t to the left \c i times. /// The argument must have a bit-vector sort. /// public BitVecExpr MkBVRotateLeft(uint i, BitVecExpr t) { Debug.Assert(t != null); CheckContextMatch(t); return new BitVecExpr(this, Native.Z3_mk_rotate_left(nCtx, i, t.NativeObject)); } /// /// Rotate Right. /// /// /// Rotate bits of \c t to the right \c i times. /// The argument must have a bit-vector sort. /// public BitVecExpr MkBVRotateRight(uint i, BitVecExpr t) { Debug.Assert(t != null); CheckContextMatch(t); return new BitVecExpr(this, Native.Z3_mk_rotate_right(nCtx, i, t.NativeObject)); } /// /// Rotate Left. /// /// /// Rotate bits of to the left times. /// The arguments must have the same bit-vector sort. /// public BitVecExpr MkBVRotateLeft(BitVecExpr t1, BitVecExpr t2) { Debug.Assert(t1 != null); Debug.Assert(t2 != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BitVecExpr(this, Native.Z3_mk_ext_rotate_left(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Rotate Right. /// /// /// Rotate bits of to the right times. /// The arguments must have the same bit-vector sort. /// public BitVecExpr MkBVRotateRight(BitVecExpr t1, BitVecExpr t2) { Debug.Assert(t1 != null); Debug.Assert(t2 != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BitVecExpr(this, Native.Z3_mk_ext_rotate_right(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Create an bit bit-vector from the integer argument . /// /// /// NB. This function is essentially treated as uninterpreted. /// So you cannot expect Z3 to precisely reflect the semantics of this function /// when solving constraints with this function. /// /// The argument must be of integer sort. /// public BitVecExpr MkInt2BV(uint n, IntExpr t) { Debug.Assert(t != null); CheckContextMatch(t); return new BitVecExpr(this, Native.Z3_mk_int2bv(nCtx, n, t.NativeObject)); } /// /// Create an integer from the bit-vector argument . /// /// /// If \c is_signed is false, then the bit-vector \c t1 is treated as unsigned. /// So the result is non-negative and in the range [0..2^N-1], where /// N are the number of bits in . /// If \c is_signed is true, \c t1 is treated as a signed bit-vector. /// /// NB. This function is essentially treated as uninterpreted. /// So you cannot expect Z3 to precisely reflect the semantics of this function /// when solving constraints with this function. /// /// The argument must be of bit-vector sort. /// public IntExpr MkBV2Int(BitVecExpr t, bool signed) { Debug.Assert(t != null); CheckContextMatch(t); return new IntExpr(this, Native.Z3_mk_bv2int(nCtx, t.NativeObject, (byte)(signed ? 1 : 0))); } /// /// Create a predicate that checks that the bit-wise addition does not overflow. /// /// /// The arguments must be of bit-vector sort. /// public BoolExpr MkBVAddNoOverflow(BitVecExpr t1, BitVecExpr t2, bool isSigned) { Debug.Assert(t1 != null); Debug.Assert(t2 != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BoolExpr(this, Native.Z3_mk_bvadd_no_overflow(nCtx, t1.NativeObject, t2.NativeObject, (byte)(isSigned ? 1 : 0))); } /// /// Create a predicate that checks that the bit-wise addition does not underflow. /// /// /// The arguments must be of bit-vector sort. /// public BoolExpr MkBVAddNoUnderflow(BitVecExpr t1, BitVecExpr t2) { Debug.Assert(t1 != null); Debug.Assert(t2 != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BoolExpr(this, Native.Z3_mk_bvadd_no_underflow(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Create a predicate that checks that the bit-wise subtraction does not overflow. /// /// /// The arguments must be of bit-vector sort. /// public BoolExpr MkBVSubNoOverflow(BitVecExpr t1, BitVecExpr t2) { Debug.Assert(t1 != null); Debug.Assert(t2 != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BoolExpr(this, Native.Z3_mk_bvsub_no_overflow(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Create a predicate that checks that the bit-wise subtraction does not underflow. /// /// /// The arguments must be of bit-vector sort. /// public BoolExpr MkBVSubNoUnderflow(BitVecExpr t1, BitVecExpr t2, bool isSigned) { Debug.Assert(t1 != null); Debug.Assert(t2 != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BoolExpr(this, Native.Z3_mk_bvsub_no_underflow(nCtx, t1.NativeObject, t2.NativeObject, (byte)(isSigned ? 1 : 0))); } /// /// Create a predicate that checks that the bit-wise signed division does not overflow. /// /// /// The arguments must be of bit-vector sort. /// public BoolExpr MkBVSDivNoOverflow(BitVecExpr t1, BitVecExpr t2) { Debug.Assert(t1 != null); Debug.Assert(t2 != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BoolExpr(this, Native.Z3_mk_bvsdiv_no_overflow(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Create a predicate that checks that the bit-wise negation does not overflow. /// /// /// The arguments must be of bit-vector sort. /// public BoolExpr MkBVNegNoOverflow(BitVecExpr t) { Debug.Assert(t != null); CheckContextMatch(t); return new BoolExpr(this, Native.Z3_mk_bvneg_no_overflow(nCtx, t.NativeObject)); } /// /// Create a predicate that checks that the bit-wise multiplication does not overflow. /// /// /// The arguments must be of bit-vector sort. /// public BoolExpr MkBVMulNoOverflow(BitVecExpr t1, BitVecExpr t2, bool isSigned) { Debug.Assert(t1 != null); Debug.Assert(t2 != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BoolExpr(this, Native.Z3_mk_bvmul_no_overflow(nCtx, t1.NativeObject, t2.NativeObject, (byte)(isSigned ? 1 : 0))); } /// /// Create a predicate that checks that the bit-wise multiplication does not underflow. /// /// /// The arguments must be of bit-vector sort. /// public BoolExpr MkBVMulNoUnderflow(BitVecExpr t1, BitVecExpr t2) { Debug.Assert(t1 != null); Debug.Assert(t2 != null); CheckContextMatch(t1); CheckContextMatch(t2); return new BoolExpr(this, Native.Z3_mk_bvmul_no_underflow(nCtx, t1.NativeObject, t2.NativeObject)); } #endregion #region Arrays /// /// Create an array constant. /// public ArrayExpr MkArrayConst(Symbol name, Sort domain, Sort range) { Debug.Assert(name != null); Debug.Assert(domain != null); Debug.Assert(range != null); return (ArrayExpr)MkConst(name, MkArraySort(domain, range)); } /// /// Create an array constant. /// public ArrayExpr MkArrayConst(string name, Sort domain, Sort range) { Debug.Assert(domain != null); Debug.Assert(range != null); return (ArrayExpr)MkConst(MkSymbol(name), MkArraySort(domain, range)); } /// /// Array read. /// /// /// The argument a is the array and i is the index /// of the array that gets read. /// /// The node a must have an array sort [domain -> range], /// and i must have the sort domain. /// The sort of the result is range. /// /// /// public Expr MkSelect(ArrayExpr a, Expr i) { Debug.Assert(a != null); Debug.Assert(i != null); CheckContextMatch(a); CheckContextMatch(i); return Expr.Create(this, Native.Z3_mk_select(nCtx, a.NativeObject, i.NativeObject)); } /// /// Array read. /// /// /// The argument a is the array and args are the indices /// of the array that gets read. /// /// The node a must have an array sort [domain1,..,domaink -> range], /// and args must have the sort domain1,..,domaink. /// The sort of the result is range. /// /// /// public Expr MkSelect(ArrayExpr a, params Expr[] args) { Debug.Assert(a != null); Debug.Assert(args != null && args.All(n => n != null)); CheckContextMatch(a); CheckContextMatch(args); return Expr.Create(this, Native.Z3_mk_select_n(nCtx, a.NativeObject, AST.ArrayLength(args), AST.ArrayToNative(args))); } /// /// Array update. /// /// /// The node a must have an array sort [domain -> range], /// i must have sort domain, /// v must have sort range. The sort of the result is [domain -> range]. /// The semantics of this function is given by the theory of arrays described in the SMT-LIB /// standard. See http://smtlib.org for more details. /// The result of this function is an array that is equal to a /// (with respect to select) /// on all indices except for i, where it maps to v /// (and the select of a with /// respect to i may be a different value). /// /// /// /// public ArrayExpr MkStore(ArrayExpr a, Expr i, Expr v) { Debug.Assert(a != null); Debug.Assert(i != null); Debug.Assert(v != null); CheckContextMatch(a); CheckContextMatch(i); CheckContextMatch(v); return new ArrayExpr(this, Native.Z3_mk_store(nCtx, a.NativeObject, i.NativeObject, v.NativeObject)); } /// /// Array update. /// /// /// The node a must have an array sort [domain1,..,domaink -> range], /// args must have sort domain1,..,domaink, /// v must have sort range. The sort of the result is [domain -> range]. /// The semantics of this function is given by the theory of arrays described in the SMT-LIB /// standard. See http://smtlib.org for more details. /// The result of this function is an array that is equal to a /// (with respect to select) /// on all indices except for args, where it maps to v /// (and the select of a with /// respect to args may be a different value). /// /// /// /// public ArrayExpr MkStore(ArrayExpr a, Expr[] args, Expr v) { Debug.Assert(a != null); Debug.Assert(args != null); Debug.Assert(v != null); CheckContextMatch(args); CheckContextMatch(a); CheckContextMatch(v); return new ArrayExpr(this, Native.Z3_mk_store_n(nCtx, a.NativeObject, AST.ArrayLength(args), AST.ArrayToNative(args), v.NativeObject)); } /// /// Create a constant array. /// /// /// The resulting term is an array, such that a selecton an arbitrary index /// produces the value v. /// /// /// public ArrayExpr MkConstArray(Sort domain, Expr v) { Debug.Assert(domain != null); Debug.Assert(v != null); CheckContextMatch(domain); CheckContextMatch(v); return new ArrayExpr(this, Native.Z3_mk_const_array(nCtx, domain.NativeObject, v.NativeObject)); } /// /// Maps f on the argument arrays. /// /// /// Each element of args must be of an array sort [domain_i -> range_i]. /// The function declaration f must have type range_1 .. range_n -> range. /// v must have sort range. The sort of the result is [domain_i -> range]. /// /// /// /// public ArrayExpr MkMap(FuncDecl f, params ArrayExpr[] args) { Debug.Assert(f != null); Debug.Assert(args == null || args.All(a => a != null)); CheckContextMatch(f); CheckContextMatch(args); return (ArrayExpr)Expr.Create(this, Native.Z3_mk_map(nCtx, f.NativeObject, AST.ArrayLength(args), AST.ArrayToNative(args))); } /// /// Access the array default value. /// /// /// Produces the default range value, for arrays that can be represented as /// finite maps with a default range value. /// public Expr MkTermArray(ArrayExpr array) { Debug.Assert(array != null); CheckContextMatch(array); return Expr.Create(this, Native.Z3_mk_array_default(nCtx, array.NativeObject)); } /// /// Create Extentionality index. Two arrays are equal if and only if they are equal on the index returned by MkArrayExt. /// public Expr MkArrayExt(ArrayExpr arg1, ArrayExpr arg2) { Debug.Assert(arg1 != null); Debug.Assert(arg2 != null); CheckContextMatch(arg1); CheckContextMatch(arg2); return Expr.Create(this, Native.Z3_mk_array_ext(nCtx, arg1.NativeObject, arg2.NativeObject)); } #endregion #region Sets /// /// Create a set type. /// public SetSort MkSetSort(Sort ty) { Debug.Assert(ty != null); CheckContextMatch(ty); return new SetSort(this, ty); } /// /// Create an empty set. /// public ArrayExpr MkEmptySet(Sort domain) { Debug.Assert(domain != null); CheckContextMatch(domain); return (ArrayExpr)Expr.Create(this, Native.Z3_mk_empty_set(nCtx, domain.NativeObject)); } /// /// Create the full set. /// public ArrayExpr MkFullSet(Sort domain) { Debug.Assert(domain != null); CheckContextMatch(domain); return (ArrayExpr)Expr.Create(this, Native.Z3_mk_full_set(nCtx, domain.NativeObject)); } /// /// Add an element to the set. /// public ArrayExpr MkSetAdd(ArrayExpr set, Expr element) { Debug.Assert(set != null); Debug.Assert(element != null); CheckContextMatch(set); CheckContextMatch(element); return (ArrayExpr)Expr.Create(this, Native.Z3_mk_set_add(nCtx, set.NativeObject, element.NativeObject)); } /// /// Remove an element from a set. /// public ArrayExpr MkSetDel(ArrayExpr set, Expr element) { Debug.Assert(set != null); Debug.Assert(element != null); CheckContextMatch(set); CheckContextMatch(element); return (ArrayExpr)Expr.Create(this, Native.Z3_mk_set_del(nCtx, set.NativeObject, element.NativeObject)); } /// /// Take the union of a list of sets. /// public ArrayExpr MkSetUnion(params ArrayExpr[] args) { Debug.Assert(args != null); Debug.Assert(args.All(a => a != null)); CheckContextMatch(args); return (ArrayExpr)Expr.Create(this, Native.Z3_mk_set_union(nCtx, (uint)args.Length, AST.ArrayToNative(args))); } /// /// Take the intersection of a list of sets. /// public ArrayExpr MkSetIntersection(params ArrayExpr[] args) { Debug.Assert(args != null); Debug.Assert(args.All(a => a != null)); CheckContextMatch(args); return (ArrayExpr)Expr.Create(this, Native.Z3_mk_set_intersect(nCtx, (uint)args.Length, AST.ArrayToNative(args))); } /// /// Take the difference between two sets. /// public ArrayExpr MkSetDifference(ArrayExpr arg1, ArrayExpr arg2) { Debug.Assert(arg1 != null); Debug.Assert(arg2 != null); CheckContextMatch(arg1); CheckContextMatch(arg2); return (ArrayExpr)Expr.Create(this, Native.Z3_mk_set_difference(nCtx, arg1.NativeObject, arg2.NativeObject)); } /// /// Take the complement of a set. /// public ArrayExpr MkSetComplement(ArrayExpr arg) { Debug.Assert(arg != null); CheckContextMatch(arg); return (ArrayExpr)Expr.Create(this, Native.Z3_mk_set_complement(nCtx, arg.NativeObject)); } /// /// Check for set membership. /// public BoolExpr MkSetMembership(Expr elem, ArrayExpr set) { Debug.Assert(elem != null); Debug.Assert(set != null); CheckContextMatch(elem); CheckContextMatch(set); return (BoolExpr) Expr.Create(this, Native.Z3_mk_set_member(nCtx, elem.NativeObject, set.NativeObject)); } /// /// Check for subsetness of sets. /// public BoolExpr MkSetSubset(ArrayExpr arg1, ArrayExpr arg2) { Debug.Assert(arg1 != null); Debug.Assert(arg2 != null); CheckContextMatch(arg1); CheckContextMatch(arg2); return (BoolExpr) Expr.Create(this, Native.Z3_mk_set_subset(nCtx, arg1.NativeObject, arg2.NativeObject)); } #endregion #region Sequence, string and regular expressions /// /// Create the empty sequence. /// public SeqExpr MkEmptySeq(Sort s) { Debug.Assert(s != null); return new SeqExpr(this, Native.Z3_mk_seq_empty(nCtx, s.NativeObject)); } /// /// Create the singleton sequence. /// public SeqExpr MkUnit(Expr elem) { Debug.Assert(elem != null); return new SeqExpr(this, Native.Z3_mk_seq_unit(nCtx, elem.NativeObject)); } /// /// Create a string constant. /// public SeqExpr MkString(string s) { Debug.Assert(s != null); return new SeqExpr(this, Native.Z3_mk_string(nCtx, s)); } /// /// Convert an integer expression to a string. /// public SeqExpr IntToString(Expr e) { Debug.Assert(e != null); Debug.Assert(e is ArithExpr); return new SeqExpr(this, Native.Z3_mk_int_to_str(nCtx, e.NativeObject)); } /// /// Convert an integer expression to a string. /// public IntExpr StringToInt(Expr e) { Debug.Assert(e != null); Debug.Assert(e is SeqExpr); return new IntExpr(this, Native.Z3_mk_str_to_int(nCtx, e.NativeObject)); } /// /// Concatenate sequences. /// public SeqExpr MkConcat(params SeqExpr[] t) { Debug.Assert(t != null); Debug.Assert(t.All(a => a != null)); CheckContextMatch(t); return new SeqExpr(this, Native.Z3_mk_seq_concat(nCtx, (uint)t.Length, AST.ArrayToNative(t))); } /// /// Retrieve the length of a given sequence. /// public IntExpr MkLength(SeqExpr s) { Debug.Assert(s != null); return (IntExpr) Expr.Create(this, Native.Z3_mk_seq_length(nCtx, s.NativeObject)); } /// /// Check for sequence prefix. /// public BoolExpr MkPrefixOf(SeqExpr s1, SeqExpr s2) { Debug.Assert(s1 != null); Debug.Assert(s2 != null); CheckContextMatch(s1, s2); return new BoolExpr(this, Native.Z3_mk_seq_prefix(nCtx, s1.NativeObject, s2.NativeObject)); } /// /// Check for sequence suffix. /// public BoolExpr MkSuffixOf(SeqExpr s1, SeqExpr s2) { Debug.Assert(s1 != null); Debug.Assert(s2 != null); CheckContextMatch(s1, s2); return new BoolExpr(this, Native.Z3_mk_seq_suffix(nCtx, s1.NativeObject, s2.NativeObject)); } /// /// Check for sequence containment of s2 in s1. /// public BoolExpr MkContains(SeqExpr s1, SeqExpr s2) { Debug.Assert(s1 != null); Debug.Assert(s2 != null); CheckContextMatch(s1, s2); return new BoolExpr(this, Native.Z3_mk_seq_contains(nCtx, s1.NativeObject, s2.NativeObject)); } /// /// Check if the string s1 is lexicographically strictly less than s2. /// public BoolExpr MkStringLt(SeqExpr s1, SeqExpr s2) { Debug.Assert(s1 != null); Debug.Assert(s2 != null); CheckContextMatch(s1, s2); return new BoolExpr(this, Native.Z3_mk_str_lt(nCtx, s1.NativeObject, s2.NativeObject)); } /// /// Check if the string s1 is lexicographically strictly less than s2. /// public BoolExpr MkStringLe(SeqExpr s1, SeqExpr s2) { Debug.Assert(s1 != null); Debug.Assert(s2 != null); CheckContextMatch(s1, s2); return new BoolExpr(this, Native.Z3_mk_str_le(nCtx, s1.NativeObject, s2.NativeObject)); } /// /// Retrieve sequence of length one at index. /// public SeqExpr MkAt(SeqExpr s, Expr index) { Debug.Assert(s != null); Debug.Assert(index != null); CheckContextMatch(s, index); return new SeqExpr(this, Native.Z3_mk_seq_at(nCtx, s.NativeObject, index.NativeObject)); } /// /// Retrieve element at index. /// public SeqExpr MkNth(SeqExpr s, Expr index) { Debug.Assert(s != null); Debug.Assert(index != null); CheckContextMatch(s, index); return new SeqExpr(this, Native.Z3_mk_seq_nth(nCtx, s.NativeObject, index.NativeObject)); } /// /// Extract subsequence. /// public SeqExpr MkExtract(SeqExpr s, IntExpr offset, IntExpr length) { Debug.Assert(s != null); Debug.Assert(offset != null); Debug.Assert(length != null); CheckContextMatch(s, offset, length); return new SeqExpr(this, Native.Z3_mk_seq_extract(nCtx, s.NativeObject, offset.NativeObject, length.NativeObject)); } /// /// Extract index of sub-string starting at offset. /// public IntExpr MkIndexOf(SeqExpr s, SeqExpr substr, ArithExpr offset) { Debug.Assert(s != null); Debug.Assert(offset != null); Debug.Assert(substr != null); CheckContextMatch(s, substr, offset); return new IntExpr(this, Native.Z3_mk_seq_index(nCtx, s.NativeObject, substr.NativeObject, offset.NativeObject)); } /// /// Replace the first occurrence of src by dst in s. /// public SeqExpr MkReplace(SeqExpr s, SeqExpr src, SeqExpr dst) { Debug.Assert(s != null); Debug.Assert(src != null); Debug.Assert(dst != null); CheckContextMatch(s, src, dst); return new SeqExpr(this, Native.Z3_mk_seq_replace(nCtx, s.NativeObject, src.NativeObject, dst.NativeObject)); } /// /// Convert a regular expression that accepts sequence s. /// public ReExpr MkToRe(SeqExpr s) { Debug.Assert(s != null); return new ReExpr(this, Native.Z3_mk_seq_to_re(nCtx, s.NativeObject)); } /// /// Check for regular expression membership. /// public BoolExpr MkInRe(SeqExpr s, ReExpr re) { Debug.Assert(s != null); Debug.Assert(re != null); CheckContextMatch(s, re); return new BoolExpr(this, Native.Z3_mk_seq_in_re(nCtx, s.NativeObject, re.NativeObject)); } /// /// Take the Kleene star of a regular expression. /// public ReExpr MkStar(ReExpr re) { Debug.Assert(re != null); return new ReExpr(this, Native.Z3_mk_re_star(nCtx, re.NativeObject)); } /// /// Take the bounded Kleene star of a regular expression. /// public ReExpr MkLoop(ReExpr re, uint lo, uint hi = 0) { Debug.Assert(re != null); return new ReExpr(this, Native.Z3_mk_re_loop(nCtx, re.NativeObject, lo, hi)); } /// /// Take the Kleene plus of a regular expression. /// public ReExpr MkPlus(ReExpr re) { Debug.Assert(re != null); return new ReExpr(this, Native.Z3_mk_re_plus(nCtx, re.NativeObject)); } /// /// Create the optional regular expression. /// public ReExpr MkOption(ReExpr re) { Debug.Assert(re != null); return new ReExpr(this, Native.Z3_mk_re_option(nCtx, re.NativeObject)); } /// /// Create the complement regular expression. /// public ReExpr MkComplement(ReExpr re) { Debug.Assert(re != null); return new ReExpr(this, Native.Z3_mk_re_complement(nCtx, re.NativeObject)); } /// /// Create the concatenation of regular languages. /// public ReExpr MkConcat(params ReExpr[] t) { Debug.Assert(t != null); Debug.Assert(t.All(a => a != null)); CheckContextMatch(t); return new ReExpr(this, Native.Z3_mk_re_concat(nCtx, (uint)t.Length, AST.ArrayToNative(t))); } /// /// Create the union of regular languages. /// public ReExpr MkUnion(params ReExpr[] t) { Debug.Assert(t != null); Debug.Assert(t.All(a => a != null)); CheckContextMatch(t); return new ReExpr(this, Native.Z3_mk_re_union(nCtx, (uint)t.Length, AST.ArrayToNative(t))); } /// /// Create the intersection of regular languages. /// public ReExpr MkIntersect(params ReExpr[] t) { Debug.Assert(t != null); Debug.Assert(t.All(a => a != null)); CheckContextMatch(t); return new ReExpr(this, Native.Z3_mk_re_intersect(nCtx, (uint)t.Length, AST.ArrayToNative(t))); } /// /// Create the empty regular expression. /// The sort s should be a regular expression. /// public ReExpr MkEmptyRe(Sort s) { Debug.Assert(s != null); return new ReExpr(this, Native.Z3_mk_re_empty(nCtx, s.NativeObject)); } /// /// Create the full regular expression. /// The sort s should be a regular expression. /// public ReExpr MkFullRe(Sort s) { Debug.Assert(s != null); return new ReExpr(this, Native.Z3_mk_re_full(nCtx, s.NativeObject)); } /// /// Create a range expression. /// public ReExpr MkRange(SeqExpr lo, SeqExpr hi) { Debug.Assert(lo != null); Debug.Assert(hi != null); CheckContextMatch(lo, hi); return new ReExpr(this, Native.Z3_mk_re_range(nCtx, lo.NativeObject, hi.NativeObject)); } #endregion #region Pseudo-Boolean constraints /// /// Create an at-most-k constraint. /// public BoolExpr MkAtMost(IEnumerable args, uint k) { Debug.Assert(args != null); CheckContextMatch(args); return new BoolExpr(this, Native.Z3_mk_atmost(nCtx, (uint) args.Count(), AST.EnumToNative(args), k)); } /// /// Create an at-least-k constraint. /// public BoolExpr MkAtLeast(IEnumerable args, uint k) { Debug.Assert(args != null); CheckContextMatch(args); return new BoolExpr(this, Native.Z3_mk_atleast(nCtx, (uint) args.Count(), AST.EnumToNative(args), k)); } /// /// Create a pseudo-Boolean less-or-equal constraint. /// public BoolExpr MkPBLe(int[] coeffs, BoolExpr[] args, int k) { Debug.Assert(args != null); Debug.Assert(coeffs != null); Debug.Assert(args.Length == coeffs.Length); CheckContextMatch(args); return new BoolExpr(this, Native.Z3_mk_pble(nCtx, (uint) args.Length, AST.ArrayToNative(args), coeffs, k)); } /// /// Create a pseudo-Boolean greater-or-equal constraint. /// public BoolExpr MkPBGe(int[] coeffs, BoolExpr[] args, int k) { Debug.Assert(args != null); Debug.Assert(coeffs != null); Debug.Assert(args.Length == coeffs.Length); CheckContextMatch(args); return new BoolExpr(this, Native.Z3_mk_pbge(nCtx, (uint) args.Length, AST.ArrayToNative(args), coeffs, k)); } /// /// Create a pseudo-Boolean equal constraint. /// public BoolExpr MkPBEq(int[] coeffs, BoolExpr[] args, int k) { Debug.Assert(args != null); Debug.Assert(coeffs != null); Debug.Assert(args.Length == coeffs.Length); CheckContextMatch(args); return new BoolExpr(this, Native.Z3_mk_pbeq(nCtx, (uint) args.Length, AST.ArrayToNative(args), coeffs, k)); } #endregion #region Numerals #region General Numerals /// /// Create a Term of a given sort. /// /// A string representing the Term value in decimal notation. If the given sort is a real, then the Term can be a rational, that is, a string of the form [num]* / [num]*. /// The sort of the numeral. In the current implementation, the given sort can be an int, real, or bit-vectors of arbitrary size. /// A Term with value and sort public Expr MkNumeral(string v, Sort ty) { Debug.Assert(ty != null); CheckContextMatch(ty); return Expr.Create(this, Native.Z3_mk_numeral(nCtx, v, ty.NativeObject)); } /// /// Create a Term of a given sort. This function can be used to create numerals that fit in a machine integer. /// It is slightly faster than MakeNumeral since it is not necessary to parse a string. /// /// Value of the numeral /// Sort of the numeral /// A Term with value and type public Expr MkNumeral(int v, Sort ty) { Debug.Assert(ty != null); CheckContextMatch(ty); return Expr.Create(this, Native.Z3_mk_int(nCtx, v, ty.NativeObject)); } /// /// Create a Term of a given sort. This function can be used to create numerals that fit in a machine integer. /// It is slightly faster than MakeNumeral since it is not necessary to parse a string. /// /// Value of the numeral /// Sort of the numeral /// A Term with value and type public Expr MkNumeral(uint v, Sort ty) { Debug.Assert(ty != null); CheckContextMatch(ty); return Expr.Create(this, Native.Z3_mk_unsigned_int(nCtx, v, ty.NativeObject)); } /// /// Create a Term of a given sort. This function can be used to create numerals that fit in a machine integer. /// It is slightly faster than MakeNumeral since it is not necessary to parse a string. /// /// Value of the numeral /// Sort of the numeral /// A Term with value and type public Expr MkNumeral(long v, Sort ty) { Debug.Assert(ty != null); CheckContextMatch(ty); return Expr.Create(this, Native.Z3_mk_int64(nCtx, v, ty.NativeObject)); } /// /// Create a Term of a given sort. This function can be used to create numerals that fit in a machine integer. /// It is slightly faster than MakeNumeral since it is not necessary to parse a string. /// /// Value of the numeral /// Sort of the numeral /// A Term with value and type public Expr MkNumeral(ulong v, Sort ty) { Debug.Assert(ty != null); CheckContextMatch(ty); return Expr.Create(this, Native.Z3_mk_unsigned_int64(nCtx, v, ty.NativeObject)); } #endregion #region Reals /// /// Create a real from a fraction. /// /// numerator of rational. /// denominator of rational. /// A Term with value / and sort Real /// public RatNum MkReal(int num, int den) { if (den == 0) throw new Z3Exception("Denominator is zero"); return new RatNum(this, Native.Z3_mk_real(nCtx, num, den)); } /// /// Create a real numeral. /// /// A string representing the Term value in decimal notation. /// A Term with value and sort Real public RatNum MkReal(string v) { return new RatNum(this, Native.Z3_mk_numeral(nCtx, v, RealSort.NativeObject)); } /// /// Create a real numeral. /// /// value of the numeral. /// A Term with value and sort Real public RatNum MkReal(int v) { return new RatNum(this, Native.Z3_mk_int(nCtx, v, RealSort.NativeObject)); } /// /// Create a real numeral. /// /// value of the numeral. /// A Term with value and sort Real public RatNum MkReal(uint v) { return new RatNum(this, Native.Z3_mk_unsigned_int(nCtx, v, RealSort.NativeObject)); } /// /// Create a real numeral. /// /// value of the numeral. /// A Term with value and sort Real public RatNum MkReal(long v) { return new RatNum(this, Native.Z3_mk_int64(nCtx, v, RealSort.NativeObject)); } /// /// Create a real numeral. /// /// value of the numeral. /// A Term with value and sort Real public RatNum MkReal(ulong v) { return new RatNum(this, Native.Z3_mk_unsigned_int64(nCtx, v, RealSort.NativeObject)); } #endregion #region Integers /// /// Create an integer numeral. /// /// A string representing the Term value in decimal notation. public IntNum MkInt(string v) { return new IntNum(this, Native.Z3_mk_numeral(nCtx, v, IntSort.NativeObject)); } /// /// Create an integer numeral. /// /// value of the numeral. /// A Term with value and sort Integer public IntNum MkInt(int v) { return new IntNum(this, Native.Z3_mk_int(nCtx, v, IntSort.NativeObject)); } /// /// Create an integer numeral. /// /// value of the numeral. /// A Term with value and sort Integer public IntNum MkInt(uint v) { return new IntNum(this, Native.Z3_mk_unsigned_int(nCtx, v, IntSort.NativeObject)); } /// /// Create an integer numeral. /// /// value of the numeral. /// A Term with value and sort Integer public IntNum MkInt(long v) { return new IntNum(this, Native.Z3_mk_int64(nCtx, v, IntSort.NativeObject)); } /// /// Create an integer numeral. /// /// value of the numeral. /// A Term with value and sort Integer public IntNum MkInt(ulong v) { return new IntNum(this, Native.Z3_mk_unsigned_int64(nCtx, v, IntSort.NativeObject)); } #endregion #region Bit-vectors /// /// Create a bit-vector numeral. /// /// A string representing the value in decimal notation. /// the size of the bit-vector public BitVecNum MkBV(string v, uint size) { return (BitVecNum)MkNumeral(v, MkBitVecSort(size)); } /// /// Create a bit-vector numeral. /// /// value of the numeral. /// the size of the bit-vector public BitVecNum MkBV(int v, uint size) { return (BitVecNum)MkNumeral(v, MkBitVecSort(size)); } /// /// Create a bit-vector numeral. /// /// value of the numeral. /// the size of the bit-vector public BitVecNum MkBV(uint v, uint size) { return (BitVecNum)MkNumeral(v, MkBitVecSort(size)); } /// /// Create a bit-vector numeral. /// /// value of the numeral. /// the size of the bit-vector public BitVecNum MkBV(long v, uint size) { return (BitVecNum)MkNumeral(v, MkBitVecSort(size)); } /// /// Create a bit-vector numeral. /// /// value of the numeral. /// the size of the bit-vector public BitVecNum MkBV(ulong v, uint size) { return (BitVecNum)MkNumeral(v, MkBitVecSort(size)); } /// /// Create a bit-vector numeral. /// /// An array of bits representing the bit-vector. Least significant bit is at position 0. public BitVecNum MkBV(bool[] bits) { byte[] _bits = new byte[bits.Length]; for (int i = 0; i < bits.Length; ++i) _bits[i] = (byte)(bits[i] ? 1 : 0); return (BitVecNum)Expr.Create(this, Native.Z3_mk_bv_numeral(nCtx, (uint)bits.Length, _bits)); } #endregion #endregion // Numerals #region Quantifiers /// /// Create a universal Quantifier. /// /// /// Creates a forall formula, where is the weight, /// is an array of patterns, is an array /// with the sorts of the bound variables, is an array with the /// 'names' of the bound variables, and is the body of the /// quantifier. Quantifiers are associated with weights indicating the importance of /// using the quantifier during instantiation. /// Note that the bound variables are de-Bruijn indices created using . /// Z3 applies the convention that the last element in and /// refers to the variable with index 0, the second to last element /// of and refers to the variable /// with index 1, etc. /// /// the sorts of the bound variables. /// names of the bound variables /// the body of the quantifier. /// quantifiers are associated with weights indicating the importance of using the quantifier during instantiation. By default, pass the weight 0. /// array containing the patterns created using MkPattern. /// array containing the anti-patterns created using MkPattern. /// optional symbol to track quantifier. /// optional symbol to track skolem constants. public Quantifier MkForall(Sort[] sorts, Symbol[] names, Expr body, uint weight = 1, Pattern[] patterns = null, Expr[] noPatterns = null, Symbol quantifierID = null, Symbol skolemID = null) { Debug.Assert(sorts != null); Debug.Assert(names != null); Debug.Assert(body != null); Debug.Assert(sorts.Length == names.Length); Debug.Assert(sorts.All(s => s != null)); Debug.Assert(names.All(n => n != null)); Debug.Assert(patterns == null || patterns.All(p => p != null)); Debug.Assert(noPatterns == null || noPatterns.All(np => np != null)); return new Quantifier(this, true, sorts, names, body, weight, patterns, noPatterns, quantifierID, skolemID); } /// /// Create a universal Quantifier. /// /// /// Creates a universal quantifier using a list of constants that will /// form the set of bound variables. /// /// public Quantifier MkForall(Expr[] boundConstants, Expr body, uint weight = 1, Pattern[] patterns = null, Expr[] noPatterns = null, Symbol quantifierID = null, Symbol skolemID = null) { Debug.Assert(body != null); Debug.Assert(boundConstants == null || boundConstants.All(b => b != null)); Debug.Assert(patterns == null || patterns.All(p => p != null)); Debug.Assert(noPatterns == null || noPatterns.All(np => np != null)); return new Quantifier(this, true, boundConstants, body, weight, patterns, noPatterns, quantifierID, skolemID); } /// /// Create an existential Quantifier. /// /// /// Creates an existential quantifier using de-Bruijn indexed variables. /// (). /// public Quantifier MkExists(Sort[] sorts, Symbol[] names, Expr body, uint weight = 1, Pattern[] patterns = null, Expr[] noPatterns = null, Symbol quantifierID = null, Symbol skolemID = null) { Debug.Assert(sorts != null); Debug.Assert(names != null); Debug.Assert(body != null); Debug.Assert(sorts.Length == names.Length); Debug.Assert(sorts.All(s => s != null)); Debug.Assert(names.All(n => n != null)); Debug.Assert(patterns == null || patterns.All(p => p != null)); Debug.Assert(noPatterns == null || noPatterns.All(np => np != null)); return new Quantifier(this, false, sorts, names, body, weight, patterns, noPatterns, quantifierID, skolemID); } /// /// Create an existential Quantifier. /// /// /// Creates an existential quantifier using a list of constants that will /// form the set of bound variables. /// /// public Quantifier MkExists(Expr[] boundConstants, Expr body, uint weight = 1, Pattern[] patterns = null, Expr[] noPatterns = null, Symbol quantifierID = null, Symbol skolemID = null) { Debug.Assert(body != null); Debug.Assert(boundConstants == null || boundConstants.All(n => n != null)); Debug.Assert(patterns == null || patterns.All(p => p != null)); Debug.Assert(noPatterns == null || noPatterns.All(np => np != null)); return new Quantifier(this, false, boundConstants, body, weight, patterns, noPatterns, quantifierID, skolemID); } /// /// Create a Quantifier. /// /// public Quantifier MkQuantifier(bool universal, Sort[] sorts, Symbol[] names, Expr body, uint weight = 1, Pattern[] patterns = null, Expr[] noPatterns = null, Symbol quantifierID = null, Symbol skolemID = null) { Debug.Assert(body != null); Debug.Assert(names != null); Debug.Assert(sorts != null); Debug.Assert(sorts.Length == names.Length); Debug.Assert(sorts.All(s => s != null)); Debug.Assert(names.All(n => n != null)); Debug.Assert(patterns == null || patterns.All(p => p != null)); Debug.Assert(noPatterns == null || noPatterns.All(np => np != null)); if (universal) return MkForall(sorts, names, body, weight, patterns, noPatterns, quantifierID, skolemID); else return MkExists(sorts, names, body, weight, patterns, noPatterns, quantifierID, skolemID); } /// /// Create a Quantifier. /// /// public Quantifier MkQuantifier(bool universal, Expr[] boundConstants, Expr body, uint weight = 1, Pattern[] patterns = null, Expr[] noPatterns = null, Symbol quantifierID = null, Symbol skolemID = null) { Debug.Assert(body != null); Debug.Assert(boundConstants == null || boundConstants.All(n => n != null)); Debug.Assert(patterns == null || patterns.All(p => p != null)); Debug.Assert(noPatterns == null || noPatterns.All(np => np != null)); if (universal) return MkForall(boundConstants, body, weight, patterns, noPatterns, quantifierID, skolemID); else return MkExists(boundConstants, body, weight, patterns, noPatterns, quantifierID, skolemID); } /// /// Create a lambda expression. /// /// /// Creates a lambda expression. /// is an array /// with the sorts of the bound variables, is an array with the /// 'names' of the bound variables, and is the body of the /// lambda. /// Note that the bound variables are de-Bruijn indices created using . /// Z3 applies the convention that the last element in and /// refers to the variable with index 0, the second to last element /// of and refers to the variable /// with index 1, etc. /// /// the sorts of the bound variables. /// names of the bound variables /// the body of the quantifier. public Lambda MkLambda(Sort[] sorts, Symbol[] names, Expr body) { Debug.Assert(sorts != null); Debug.Assert(names != null); Debug.Assert(body != null); Debug.Assert(sorts.Length == names.Length); Debug.Assert(sorts.All(s => s != null)); Debug.Assert(names.All(n => n != null)); return new Lambda(this, sorts, names, body); } /// /// Create a lambda expression. /// /// /// Creates a lambda expression using a list of constants that will /// form the set of bound variables. /// /// public Lambda MkLambda(Expr[] boundConstants, Expr body) { Debug.Assert(body != null); Debug.Assert(boundConstants != null && boundConstants.All(b => b != null)); return new Lambda(this, boundConstants, body); } #endregion #endregion // Expr #region Options /// /// Selects the format used for pretty-printing expressions. /// /// /// The default mode for pretty printing expressions is to produce /// SMT-LIB style output where common subexpressions are printed /// at each occurrence. The mode is called Z3_PRINT_SMTLIB_FULL. /// To print shared common subexpressions only once, /// use the Z3_PRINT_LOW_LEVEL mode. /// To print in way that conforms to SMT-LIB standards and uses let /// expressions to share common sub-expressions use Z3_PRINT_SMTLIB_COMPLIANT. /// /// /// /// /// public Z3_ast_print_mode PrintMode { set { Native.Z3_set_ast_print_mode(nCtx, (uint)value); } } #endregion #region SMT Files & Strings /// /// Parse the given string using the SMT-LIB2 parser. /// /// A conjunction of assertions in the scope (up to push/pop) at the end of the string. public BoolExpr[] ParseSMTLIB2String(string str, Symbol[] sortNames = null, Sort[] sorts = null, Symbol[] declNames = null, FuncDecl[] decls = null) { uint csn = Symbol.ArrayLength(sortNames); uint cs = Sort.ArrayLength(sorts); uint cdn = Symbol.ArrayLength(declNames); uint cd = AST.ArrayLength(decls); if (csn != cs || cdn != cd) throw new Z3Exception("Argument size mismatch"); ASTVector assertions = new ASTVector(this, Native.Z3_parse_smtlib2_string(nCtx, str, AST.ArrayLength(sorts), Symbol.ArrayToNative(sortNames), AST.ArrayToNative(sorts), AST.ArrayLength(decls), Symbol.ArrayToNative(declNames), AST.ArrayToNative(decls))); return assertions.ToBoolExprArray(); } /// /// Parse the given file using the SMT-LIB2 parser. /// /// public BoolExpr[] ParseSMTLIB2File(string fileName, Symbol[] sortNames = null, Sort[] sorts = null, Symbol[] declNames = null, FuncDecl[] decls = null) { uint csn = Symbol.ArrayLength(sortNames); uint cs = Sort.ArrayLength(sorts); uint cdn = Symbol.ArrayLength(declNames); uint cd = AST.ArrayLength(decls); if (csn != cs || cdn != cd) throw new Z3Exception("Argument size mismatch"); ASTVector assertions = new ASTVector(this, Native.Z3_parse_smtlib2_file(nCtx, fileName, AST.ArrayLength(sorts), Symbol.ArrayToNative(sortNames), AST.ArrayToNative(sorts), AST.ArrayLength(decls), Symbol.ArrayToNative(declNames), AST.ArrayToNative(decls))); return assertions.ToBoolExprArray(); } #endregion #region Goals /// /// Creates a new Goal. /// /// /// Note that the Context must have been created with proof generation support if /// is set to true here. /// /// Indicates whether model generation should be enabled. /// Indicates whether unsat core generation should be enabled. /// Indicates whether proof generation should be enabled. public Goal MkGoal(bool models = true, bool unsatCores = false, bool proofs = false) { return new Goal(this, models, unsatCores, proofs); } #endregion #region ParameterSets /// /// Creates a new ParameterSet. /// public Params MkParams() { return new Params(this); } #endregion #region Tactics /// /// The number of supported tactics. /// public uint NumTactics { get { return Native.Z3_get_num_tactics(nCtx); } } /// /// The names of all supported tactics. /// public string[] TacticNames { get { uint n = NumTactics; string[] res = new string[n]; for (uint i = 0; i < n; i++) res[i] = Native.Z3_get_tactic_name(nCtx, i); return res; } } /// /// Returns a string containing a description of the tactic with the given name. /// public string TacticDescription(string name) { return Native.Z3_tactic_get_descr(nCtx, name); } /// /// Creates a new Tactic. /// public Tactic MkTactic(string name) { return new Tactic(this, name); } /// /// Create a tactic that applies to a Goal and /// then to every subgoal produced by . /// public Tactic AndThen(Tactic t1, Tactic t2, params Tactic[] ts) { Debug.Assert(t1 != null); Debug.Assert(t2 != null); // Debug.Assert(ts == null || Contract.ForAll(0, ts.Length, j => ts[j] != null)); CheckContextMatch(t1); CheckContextMatch(t2); CheckContextMatch(ts); IntPtr last = IntPtr.Zero; if (ts != null && ts.Length > 0) { last = ts[ts.Length - 1].NativeObject; for (int i = ts.Length - 2; i >= 0; i--) last = Native.Z3_tactic_and_then(nCtx, ts[i].NativeObject, last); } if (last != IntPtr.Zero) { last = Native.Z3_tactic_and_then(nCtx, t2.NativeObject, last); return new Tactic(this, Native.Z3_tactic_and_then(nCtx, t1.NativeObject, last)); } else return new Tactic(this, Native.Z3_tactic_and_then(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Create a tactic that applies to a Goal and /// then to every subgoal produced by . /// /// /// Shorthand for AndThen. /// public Tactic Then(Tactic t1, Tactic t2, params Tactic[] ts) { Debug.Assert(t1 != null); Debug.Assert(t2 != null); // Debug.Assert(ts == null || Contract.ForAll(0, ts.Length, j => ts[j] != null)); return AndThen(t1, t2, ts); } /// /// Create a tactic that first applies to a Goal and /// if it fails then returns the result of applied to the Goal. /// public Tactic OrElse(Tactic t1, Tactic t2) { Debug.Assert(t1 != null); Debug.Assert(t2 != null); CheckContextMatch(t1); CheckContextMatch(t2); return new Tactic(this, Native.Z3_tactic_or_else(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Create a tactic that applies to a goal for milliseconds. /// /// /// If does not terminate within milliseconds, then it fails. /// public Tactic TryFor(Tactic t, uint ms) { Debug.Assert(t != null); CheckContextMatch(t); return new Tactic(this, Native.Z3_tactic_try_for(nCtx, t.NativeObject, ms)); } /// /// Create a tactic that applies to a given goal if the probe /// evaluates to true. /// /// /// If evaluates to false, then the new tactic behaves like the skip tactic. /// public Tactic When(Probe p, Tactic t) { Debug.Assert(p != null); Debug.Assert(t != null); CheckContextMatch(t); CheckContextMatch(p); return new Tactic(this, Native.Z3_tactic_when(nCtx, p.NativeObject, t.NativeObject)); } /// /// Create a tactic that applies to a given goal if the probe /// evaluates to true and otherwise. /// public Tactic Cond(Probe p, Tactic t1, Tactic t2) { Debug.Assert(p != null); Debug.Assert(t1 != null); Debug.Assert(t2 != null); CheckContextMatch(p); CheckContextMatch(t1); CheckContextMatch(t2); return new Tactic(this, Native.Z3_tactic_cond(nCtx, p.NativeObject, t1.NativeObject, t2.NativeObject)); } /// /// Create a tactic that keeps applying until the goal is not /// modified anymore or the maximum number of iterations is reached. /// public Tactic Repeat(Tactic t, uint max = uint.MaxValue) { Debug.Assert(t != null); CheckContextMatch(t); return new Tactic(this, Native.Z3_tactic_repeat(nCtx, t.NativeObject, max)); } /// /// Create a tactic that just returns the given goal. /// public Tactic Skip() { return new Tactic(this, Native.Z3_tactic_skip(nCtx)); } /// /// Create a tactic always fails. /// public Tactic Fail() { return new Tactic(this, Native.Z3_tactic_fail(nCtx)); } /// /// Create a tactic that fails if the probe evaluates to false. /// public Tactic FailIf(Probe p) { Debug.Assert(p != null); CheckContextMatch(p); return new Tactic(this, Native.Z3_tactic_fail_if(nCtx, p.NativeObject)); } /// /// Create a tactic that fails if the goal is not trivially satisfiable (i.e., empty) /// or trivially unsatisfiable (i.e., contains `false'). /// public Tactic FailIfNotDecided() { return new Tactic(this, Native.Z3_tactic_fail_if_not_decided(nCtx)); } /// /// Create a tactic that applies using the given set of parameters . /// public Tactic UsingParams(Tactic t, Params p) { Debug.Assert(t != null); Debug.Assert(p != null); CheckContextMatch(t); CheckContextMatch(p); return new Tactic(this, Native.Z3_tactic_using_params(nCtx, t.NativeObject, p.NativeObject)); } /// /// Create a tactic that applies using the given set of parameters . /// /// Alias for UsingParams public Tactic With(Tactic t, Params p) { Debug.Assert(t != null); Debug.Assert(p != null); return UsingParams(t, p); } /// /// Create a tactic that applies the given tactics in parallel until one of them succeeds (i.e., the first that doesn't fail). /// public Tactic ParOr(params Tactic[] t) { Debug.Assert(t == null || t.All(tactic => tactic != null)); CheckContextMatch(t); return new Tactic(this, Native.Z3_tactic_par_or(nCtx, Tactic.ArrayLength(t), Tactic.ArrayToNative(t))); } /// /// Create a tactic that applies to a given goal and then /// to every subgoal produced by . The subgoals are processed in parallel. /// public Tactic ParAndThen(Tactic t1, Tactic t2) { Debug.Assert(t1 != null); Debug.Assert(t2 != null); CheckContextMatch(t1); CheckContextMatch(t2); return new Tactic(this, Native.Z3_tactic_par_and_then(nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Interrupt the execution of a Z3 procedure. /// /// This procedure can be used to interrupt: solvers, simplifiers and tactics. public void Interrupt() { Native.Z3_interrupt(nCtx); } #endregion #region Probes /// /// The number of supported Probes. /// public uint NumProbes { get { return Native.Z3_get_num_probes(nCtx); } } /// /// The names of all supported Probes. /// public string[] ProbeNames { get { uint n = NumProbes; string[] res = new string[n]; for (uint i = 0; i < n; i++) res[i] = Native.Z3_get_probe_name(nCtx, i); return res; } } /// /// Returns a string containing a description of the probe with the given name. /// public string ProbeDescription(string name) { return Native.Z3_probe_get_descr(nCtx, name); } /// /// Creates a new Probe. /// public Probe MkProbe(string name) { return new Probe(this, name); } /// /// Create a probe that always evaluates to . /// public Probe ConstProbe(double val) { return new Probe(this, Native.Z3_probe_const(nCtx, val)); } /// /// Create a probe that evaluates to "true" when the value returned by /// is less than the value returned by /// public Probe Lt(Probe p1, Probe p2) { Debug.Assert(p1 != null); Debug.Assert(p2 != null); CheckContextMatch(p1); CheckContextMatch(p2); return new Probe(this, Native.Z3_probe_lt(nCtx, p1.NativeObject, p2.NativeObject)); } /// /// Create a probe that evaluates to "true" when the value returned by /// is greater than the value returned by /// public Probe Gt(Probe p1, Probe p2) { Debug.Assert(p1 != null); Debug.Assert(p2 != null); CheckContextMatch(p1); CheckContextMatch(p2); return new Probe(this, Native.Z3_probe_gt(nCtx, p1.NativeObject, p2.NativeObject)); } /// /// Create a probe that evaluates to "true" when the value returned by /// is less than or equal the value returned by /// public Probe Le(Probe p1, Probe p2) { Debug.Assert(p1 != null); Debug.Assert(p2 != null); CheckContextMatch(p1); CheckContextMatch(p2); return new Probe(this, Native.Z3_probe_le(nCtx, p1.NativeObject, p2.NativeObject)); } /// /// Create a probe that evaluates to "true" when the value returned by /// is greater than or equal the value returned by /// public Probe Ge(Probe p1, Probe p2) { Debug.Assert(p1 != null); Debug.Assert(p2 != null); CheckContextMatch(p1); CheckContextMatch(p2); return new Probe(this, Native.Z3_probe_ge(nCtx, p1.NativeObject, p2.NativeObject)); } /// /// Create a probe that evaluates to "true" when the value returned by /// is equal to the value returned by /// public Probe Eq(Probe p1, Probe p2) { Debug.Assert(p1 != null); Debug.Assert(p2 != null); CheckContextMatch(p1); CheckContextMatch(p2); return new Probe(this, Native.Z3_probe_eq(nCtx, p1.NativeObject, p2.NativeObject)); } /// /// Create a probe that evaluates to "true" when the value /// and evaluate to "true". /// public Probe And(Probe p1, Probe p2) { Debug.Assert(p1 != null); Debug.Assert(p2 != null); CheckContextMatch(p1); CheckContextMatch(p2); return new Probe(this, Native.Z3_probe_and(nCtx, p1.NativeObject, p2.NativeObject)); } /// /// Create a probe that evaluates to "true" when the value /// or evaluate to "true". /// public Probe Or(Probe p1, Probe p2) { Debug.Assert(p1 != null); Debug.Assert(p2 != null); CheckContextMatch(p1); CheckContextMatch(p2); return new Probe(this, Native.Z3_probe_or(nCtx, p1.NativeObject, p2.NativeObject)); } /// /// Create a probe that evaluates to "true" when the value /// does not evaluate to "true". /// public Probe Not(Probe p) { Debug.Assert(p != null); CheckContextMatch(p); return new Probe(this, Native.Z3_probe_not(nCtx, p.NativeObject)); } #endregion #region Solvers /// /// Creates a new (incremental) solver. /// /// /// This solver also uses a set of builtin tactics for handling the first /// check-sat command, and check-sat commands that take more than a given /// number of milliseconds to be solved. /// public Solver MkSolver(Symbol logic = null) { if (logic == null) return new Solver(this, Native.Z3_mk_solver(nCtx)); else return new Solver(this, Native.Z3_mk_solver_for_logic(nCtx, logic.NativeObject)); } /// /// Creates a new (incremental) solver. /// /// public Solver MkSolver(string logic) { return MkSolver(MkSymbol(logic)); } /// /// Creates a new (incremental) solver. /// public Solver MkSimpleSolver() { return new Solver(this, Native.Z3_mk_simple_solver(nCtx)); } /// /// Creates a solver that is implemented using the given tactic. /// /// /// The solver supports the commands Push and Pop, but it /// will always solve each check from scratch. /// public Solver MkSolver(Tactic t) { Debug.Assert(t != null); return new Solver(this, Native.Z3_mk_solver_from_tactic(nCtx, t.NativeObject)); } #endregion #region Fixedpoints /// /// Create a Fixedpoint context. /// public Fixedpoint MkFixedpoint() { return new Fixedpoint(this); } #endregion #region Optimization /// /// Create an Optimization context. /// public Optimize MkOptimize() { return new Optimize(this); } #endregion #region Floating-Point Arithmetic #region Rounding Modes #region RoundingMode Sort /// /// Create the floating-point RoundingMode sort. /// public FPRMSort MkFPRoundingModeSort() { return new FPRMSort(this); } #endregion #region Numerals /// /// Create a numeral of RoundingMode sort which represents the NearestTiesToEven rounding mode. /// public FPRMExpr MkFPRoundNearestTiesToEven() { return new FPRMExpr(this, Native.Z3_mk_fpa_round_nearest_ties_to_even(nCtx)); } /// /// Create a numeral of RoundingMode sort which represents the NearestTiesToEven rounding mode. /// public FPRMNum MkFPRNE() { return new FPRMNum(this, Native.Z3_mk_fpa_rne(nCtx)); } /// /// Create a numeral of RoundingMode sort which represents the NearestTiesToAway rounding mode. /// public FPRMNum MkFPRoundNearestTiesToAway() { return new FPRMNum(this, Native.Z3_mk_fpa_round_nearest_ties_to_away(nCtx)); } /// /// Create a numeral of RoundingMode sort which represents the NearestTiesToAway rounding mode. /// public FPRMNum MkFPRNA() { return new FPRMNum(this, Native.Z3_mk_fpa_rna(nCtx)); } /// /// Create a numeral of RoundingMode sort which represents the RoundTowardPositive rounding mode. /// public FPRMNum MkFPRoundTowardPositive() { return new FPRMNum(this, Native.Z3_mk_fpa_round_toward_positive(nCtx)); } /// /// Create a numeral of RoundingMode sort which represents the RoundTowardPositive rounding mode. /// public FPRMNum MkFPRTP() { return new FPRMNum(this, Native.Z3_mk_fpa_rtp(nCtx)); } /// /// Create a numeral of RoundingMode sort which represents the RoundTowardNegative rounding mode. /// public FPRMNum MkFPRoundTowardNegative() { return new FPRMNum(this, Native.Z3_mk_fpa_round_toward_negative(nCtx)); } /// /// Create a numeral of RoundingMode sort which represents the RoundTowardNegative rounding mode. /// public FPRMNum MkFPRTN() { return new FPRMNum(this, Native.Z3_mk_fpa_rtn(nCtx)); } /// /// Create a numeral of RoundingMode sort which represents the RoundTowardZero rounding mode. /// public FPRMNum MkFPRoundTowardZero() { return new FPRMNum(this, Native.Z3_mk_fpa_round_toward_zero(nCtx)); } /// /// Create a numeral of RoundingMode sort which represents the RoundTowardZero rounding mode. /// public FPRMNum MkFPRTZ() { return new FPRMNum(this, Native.Z3_mk_fpa_rtz(nCtx)); } #endregion #endregion #region FloatingPoint Sorts /// /// Create a FloatingPoint sort. /// /// exponent bits in the FloatingPoint sort. /// significand bits in the FloatingPoint sort. public FPSort MkFPSort(uint ebits, uint sbits) { return new FPSort(this, ebits, sbits); } /// /// Create the half-precision (16-bit) FloatingPoint sort. /// public FPSort MkFPSortHalf() { return new FPSort(this, Native.Z3_mk_fpa_sort_half(nCtx)); } /// /// Create the half-precision (16-bit) FloatingPoint sort. /// public FPSort MkFPSort16() { return new FPSort(this, Native.Z3_mk_fpa_sort_16(nCtx)); } /// /// Create the single-precision (32-bit) FloatingPoint sort. /// public FPSort MkFPSortSingle() { return new FPSort(this, Native.Z3_mk_fpa_sort_single(nCtx)); } /// /// Create the single-precision (32-bit) FloatingPoint sort. /// public FPSort MkFPSort32() { return new FPSort(this, Native.Z3_mk_fpa_sort_32(nCtx)); } /// /// Create the double-precision (64-bit) FloatingPoint sort. /// public FPSort MkFPSortDouble() { return new FPSort(this, Native.Z3_mk_fpa_sort_double(nCtx)); } /// /// Create the double-precision (64-bit) FloatingPoint sort. /// public FPSort MkFPSort64() { return new FPSort(this, Native.Z3_mk_fpa_sort_64(nCtx)); } /// /// Create the quadruple-precision (128-bit) FloatingPoint sort. /// public FPSort MkFPSortQuadruple() { return new FPSort(this, Native.Z3_mk_fpa_sort_quadruple(nCtx)); } /// /// Create the quadruple-precision (128-bit) FloatingPoint sort. /// public FPSort MkFPSort128() { return new FPSort(this, Native.Z3_mk_fpa_sort_128(nCtx)); } #endregion #region Numerals /// /// Create a NaN of sort s. /// /// FloatingPoint sort. public FPNum MkFPNaN(FPSort s) { return new FPNum(this, Native.Z3_mk_fpa_nan(nCtx, s.NativeObject)); } /// /// Create a floating-point infinity of sort s. /// /// FloatingPoint sort. /// indicates whether the result should be negative. public FPNum MkFPInf(FPSort s, bool negative) { return new FPNum(this, Native.Z3_mk_fpa_inf(nCtx, s.NativeObject, (byte)(negative ? 1 : 0))); } /// /// Create a floating-point zero of sort s. /// /// FloatingPoint sort. /// indicates whether the result should be negative. public FPNum MkFPZero(FPSort s, bool negative) { return new FPNum(this, Native.Z3_mk_fpa_zero(nCtx, s.NativeObject, (byte)(negative ? 1 : 0))); } /// /// Create a numeral of FloatingPoint sort from a float. /// /// numeral value. /// FloatingPoint sort. public FPNum MkFPNumeral(float v, FPSort s) { return new FPNum(this, Native.Z3_mk_fpa_numeral_float(nCtx, v, s.NativeObject)); } /// /// Create a numeral of FloatingPoint sort from a float. /// /// numeral value. /// FloatingPoint sort. public FPNum MkFPNumeral(double v, FPSort s) { return new FPNum(this, Native.Z3_mk_fpa_numeral_double(nCtx, v, s.NativeObject)); } /// /// Create a numeral of FloatingPoint sort from an int. /// /// numeral value. /// FloatingPoint sort. public FPNum MkFPNumeral(int v, FPSort s) { return new FPNum(this, Native.Z3_mk_fpa_numeral_int(nCtx, v, s.NativeObject)); } /// /// Create a numeral of FloatingPoint sort from a sign bit and two integers. /// /// the sign. /// the significand. /// the exponent. /// FloatingPoint sort. public FPNum MkFPNumeral(bool sgn, uint sig, int exp, FPSort s) { return new FPNum(this, Native.Z3_mk_fpa_numeral_int_uint(nCtx, (byte)(sgn ? 1 : 0), exp, sig, s.NativeObject)); } /// /// Create a numeral of FloatingPoint sort from a sign bit and two 64-bit integers. /// /// the sign. /// the significand. /// the exponent. /// FloatingPoint sort. public FPNum MkFPNumeral(bool sgn, Int64 exp, UInt64 sig, FPSort s) { return new FPNum(this, Native.Z3_mk_fpa_numeral_int64_uint64(nCtx, (byte)(sgn ? 1 : 0), exp, sig, s.NativeObject)); } /// /// Create a numeral of FloatingPoint sort from a float. /// /// numeral value. /// FloatingPoint sort. public FPNum MkFP(float v, FPSort s) { return MkFPNumeral(v, s); } /// /// Create a numeral of FloatingPoint sort from a float. /// /// numeral value. /// FloatingPoint sort. public FPNum MkFP(double v, FPSort s) { return MkFPNumeral(v, s); } /// /// Create a numeral of FloatingPoint sort from an int. /// /// numeral value. /// FloatingPoint sort. public FPNum MkFP(int v, FPSort s) { return MkFPNumeral(v, s); } /// /// Create a numeral of FloatingPoint sort from a sign bit and two integers. /// /// the sign. /// the exponent. /// the significand. /// FloatingPoint sort. public FPNum MkFP(bool sgn, int exp, uint sig, FPSort s) { return MkFPNumeral(sgn, exp, sig, s); } /// /// Create a numeral of FloatingPoint sort from a sign bit and two 64-bit integers. /// /// the sign. /// the exponent. /// the significand. /// FloatingPoint sort. public FPNum MkFP(bool sgn, Int64 exp, UInt64 sig, FPSort s) { return MkFPNumeral(sgn, exp, sig, s); } #endregion #region Operators /// /// Floating-point absolute value /// /// floating-point term public FPExpr MkFPAbs(FPExpr t) { return new FPExpr(this, Native.Z3_mk_fpa_abs(this.nCtx, t.NativeObject)); } /// /// Floating-point negation /// /// floating-point term public FPExpr MkFPNeg(FPExpr t) { return new FPExpr(this, Native.Z3_mk_fpa_neg(this.nCtx, t.NativeObject)); } /// /// Floating-point addition /// /// rounding mode term /// floating-point term /// floating-point term public FPExpr MkFPAdd(FPRMExpr rm, FPExpr t1, FPExpr t2) { return new FPExpr(this, Native.Z3_mk_fpa_add(this.nCtx, rm.NativeObject, t1.NativeObject, t2.NativeObject)); } /// /// Floating-point subtraction /// /// rounding mode term /// floating-point term /// floating-point term public FPExpr MkFPSub(FPRMExpr rm, FPExpr t1, FPExpr t2) { return new FPExpr(this, Native.Z3_mk_fpa_sub(this.nCtx, rm.NativeObject, t1.NativeObject, t2.NativeObject)); } /// /// Floating-point multiplication /// /// rounding mode term /// floating-point term /// floating-point term public FPExpr MkFPMul(FPRMExpr rm, FPExpr t1, FPExpr t2) { return new FPExpr(this, Native.Z3_mk_fpa_mul(this.nCtx, rm.NativeObject, t1.NativeObject, t2.NativeObject)); } /// /// Floating-point division /// /// rounding mode term /// floating-point term /// floating-point term public FPExpr MkFPDiv(FPRMExpr rm, FPExpr t1, FPExpr t2) { return new FPExpr(this, Native.Z3_mk_fpa_div(this.nCtx, rm.NativeObject, t1.NativeObject, t2.NativeObject)); } /// /// Floating-point fused multiply-add /// /// /// The result is round((t1 * t2) + t3) /// /// rounding mode term /// floating-point term /// floating-point term /// floating-point term public FPExpr MkFPFMA(FPRMExpr rm, FPExpr t1, FPExpr t2, FPExpr t3) { return new FPExpr(this, Native.Z3_mk_fpa_fma(this.nCtx, rm.NativeObject, t1.NativeObject, t2.NativeObject, t3.NativeObject)); } /// /// Floating-point square root /// /// rounding mode term /// floating-point term public FPExpr MkFPSqrt(FPRMExpr rm, FPExpr t) { return new FPExpr(this, Native.Z3_mk_fpa_sqrt(this.nCtx, rm.NativeObject, t.NativeObject)); } /// /// Floating-point remainder /// /// floating-point term /// floating-point term public FPExpr MkFPRem(FPExpr t1, FPExpr t2) { return new FPExpr(this, Native.Z3_mk_fpa_rem(this.nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Floating-point roundToIntegral. Rounds a floating-point number to /// the closest integer, again represented as a floating-point number. /// /// term of RoundingMode sort /// floating-point term public FPExpr MkFPRoundToIntegral(FPRMExpr rm, FPExpr t) { return new FPExpr(this, Native.Z3_mk_fpa_round_to_integral(this.nCtx, rm.NativeObject, t.NativeObject)); } /// /// Minimum of floating-point numbers. /// /// floating-point term /// floating-point term public FPExpr MkFPMin(FPExpr t1, FPExpr t2) { return new FPExpr(this, Native.Z3_mk_fpa_min(this.nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Maximum of floating-point numbers. /// /// floating-point term /// floating-point term public FPExpr MkFPMax(FPExpr t1, FPExpr t2) { return new FPExpr(this, Native.Z3_mk_fpa_max(this.nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Floating-point less than or equal. /// /// floating-point term /// floating-point term public BoolExpr MkFPLEq(FPExpr t1, FPExpr t2) { return new BoolExpr(this, Native.Z3_mk_fpa_leq(this.nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Floating-point less than. /// /// floating-point term /// floating-point term public BoolExpr MkFPLt(FPExpr t1, FPExpr t2) { return new BoolExpr(this, Native.Z3_mk_fpa_lt(this.nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Floating-point greater than or equal. /// /// floating-point term /// floating-point term public BoolExpr MkFPGEq(FPExpr t1, FPExpr t2) { return new BoolExpr(this, Native.Z3_mk_fpa_geq(this.nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Floating-point greater than. /// /// floating-point term /// floating-point term public BoolExpr MkFPGt(FPExpr t1, FPExpr t2) { return new BoolExpr(this, Native.Z3_mk_fpa_gt(this.nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Floating-point equality. /// /// /// Note that this is IEEE 754 equality (as opposed to standard =). /// /// floating-point term /// floating-point term public BoolExpr MkFPEq(FPExpr t1, FPExpr t2) { return new BoolExpr(this, Native.Z3_mk_fpa_eq(this.nCtx, t1.NativeObject, t2.NativeObject)); } /// /// Predicate indicating whether t is a normal floating-point number. /// /// floating-point term public BoolExpr MkFPIsNormal(FPExpr t) { return new BoolExpr(this, Native.Z3_mk_fpa_is_normal(this.nCtx, t.NativeObject)); } /// /// Predicate indicating whether t is a subnormal floating-point number. /// /// floating-point term public BoolExpr MkFPIsSubnormal(FPExpr t) { return new BoolExpr(this, Native.Z3_mk_fpa_is_subnormal(this.nCtx, t.NativeObject)); } /// /// Predicate indicating whether t is a floating-point number with zero value, i.e., +0 or -0. /// /// floating-point term public BoolExpr MkFPIsZero(FPExpr t) { return new BoolExpr(this, Native.Z3_mk_fpa_is_zero(this.nCtx, t.NativeObject)); } /// /// Predicate indicating whether t is a floating-point number representing +oo or -oo. /// /// floating-point term public BoolExpr MkFPIsInfinite(FPExpr t) { return new BoolExpr(this, Native.Z3_mk_fpa_is_infinite(this.nCtx, t.NativeObject)); } /// /// Predicate indicating whether t is a NaN. /// /// floating-point term public BoolExpr MkFPIsNaN(FPExpr t) { return new BoolExpr(this, Native.Z3_mk_fpa_is_nan(this.nCtx, t.NativeObject)); } /// /// Predicate indicating whether t is a negative floating-point number. /// /// floating-point term public BoolExpr MkFPIsNegative(FPExpr t) { return new BoolExpr(this, Native.Z3_mk_fpa_is_negative(this.nCtx, t.NativeObject)); } /// /// Predicate indicating whether t is a positive floating-point number. /// /// floating-point term public BoolExpr MkFPIsPositive(FPExpr t) { return new BoolExpr(this, Native.Z3_mk_fpa_is_positive(this.nCtx, t.NativeObject)); } #endregion #region Conversions to FloatingPoint terms /// /// 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 sgn is required to be a bit-vector of size 1. Significand and exponent /// are required to be greater than 1 and 2 respectively. The FloatingPoint sort /// of the resulting expression is automatically determined from the bit-vector sizes /// of the arguments. /// /// bit-vector term (of size 1) representing the sign. /// bit-vector term representing the significand. /// bit-vector term representing the exponent. public FPExpr MkFP(BitVecExpr sgn, BitVecExpr sig, BitVecExpr exp) { return new FPExpr(this, Native.Z3_mk_fpa_fp(this.nCtx, sgn.NativeObject, sig.NativeObject, exp.NativeObject)); } /// /// 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. The bit-vector size of bv (m) must be equal /// to ebits+sbits of s. The format of the bit-vector is as defined by the /// IEEE 754-2008 interchange format. /// /// bit-vector value (of size m). /// FloatingPoint sort (ebits+sbits == m) public FPExpr MkFPToFP(BitVecExpr bv, FPSort s) { return new FPExpr(this, Native.Z3_mk_fpa_to_fp_bv(this.nCtx, bv.NativeObject, s.NativeObject)); } /// /// 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. /// /// RoundingMode term. /// FloatingPoint term. /// FloatingPoint sort. public FPExpr MkFPToFP(FPRMExpr rm, FPExpr t, FPSort s) { return new FPExpr(this, Native.Z3_mk_fpa_to_fp_float(this.nCtx, rm.NativeObject, t.NativeObject, s.NativeObject)); } /// /// 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. /// /// RoundingMode term. /// term of Real sort. /// FloatingPoint sort. public FPExpr MkFPToFP(FPRMExpr rm, RealExpr t, FPSort s) { return new FPExpr(this, Native.Z3_mk_fpa_to_fp_real(this.nCtx, rm.NativeObject, t.NativeObject, s.NativeObject)); } /// /// 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 /// 2's complement format (when signed==true, otherwise unsigned). If necessary, the /// result will be rounded according to rounding mode rm. /// /// RoundingMode term. /// term of bit-vector sort. /// FloatingPoint sort. /// flag indicating whether t is interpreted as signed or unsigned bit-vector. public FPExpr MkFPToFP(FPRMExpr rm, BitVecExpr t, FPSort s, bool signed) { if (signed) return new FPExpr(this, Native.Z3_mk_fpa_to_fp_signed(this.nCtx, rm.NativeObject, t.NativeObject, s.NativeObject)); else return new FPExpr(this, Native.Z3_mk_fpa_to_fp_unsigned(this.nCtx, rm.NativeObject, t.NativeObject, s.NativeObject)); } /// /// Conversion of a floating-point number to another FloatingPoint sort s. /// /// /// Produces a term that represents the conversion of a floating-point term t to a different /// FloatingPoint sort s. If necessary, rounding according to rm is applied. /// /// FloatingPoint sort /// floating-point rounding mode term /// floating-point term public FPExpr MkFPToFP(FPSort s, FPRMExpr rm, FPExpr t) { return new FPExpr(this, Native.Z3_mk_fpa_to_fp_float(this.nCtx, s.NativeObject, rm.NativeObject, t.NativeObject)); } #endregion #region Conversions from FloatingPoint terms /// /// Conversion of a floating-point term into a bit-vector. /// /// /// Produces a term that represents the conversion of the floating-point term t into a /// bit-vector term of size sz in 2's complement format (signed when signed==true). If necessary, /// the result will be rounded according to rounding mode rm. /// /// RoundingMode term. /// FloatingPoint term /// Size of the resulting bit-vector. /// Indicates whether the result is a signed or unsigned bit-vector. public BitVecExpr MkFPToBV(FPRMExpr rm, FPExpr t, uint sz, bool signed) { if (signed) return new BitVecExpr(this, Native.Z3_mk_fpa_to_sbv(this.nCtx, rm.NativeObject, t.NativeObject, sz)); else return new BitVecExpr(this, Native.Z3_mk_fpa_to_ubv(this.nCtx, rm.NativeObject, t.NativeObject, sz)); } /// /// 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 /// real number. Note that this type of conversion will often result in non-linear /// constraints over real terms. /// /// FloatingPoint term public RealExpr MkFPToReal(FPExpr t) { return new RealExpr(this, Native.Z3_mk_fpa_to_real(this.nCtx, t.NativeObject)); } #endregion #region Z3-specific extensions /// /// Conversion of a floating-point term into a bit-vector term in IEEE 754-2008 format. /// /// /// The size of the resulting bit-vector is automatically determined. Note that /// IEEE 754-2008 allows multiple different representations of NaN. This conversion /// knows only one NaN and it will always produce the same bit-vector representation of /// that NaN. /// /// FloatingPoint term. public BitVecExpr MkFPToIEEEBV(FPExpr t) { return new BitVecExpr(this, Native.Z3_mk_fpa_to_ieee_bv(this.nCtx, t.NativeObject)); } /// /// 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. /// /// RoundingMode term. /// Exponent term of Int sort. /// Significand term of Real sort. /// FloatingPoint sort. public BitVecExpr MkFPToFP(FPRMExpr rm, IntExpr exp, RealExpr sig, FPSort s) { return new BitVecExpr(this, Native.Z3_mk_fpa_to_fp_int_real(this.nCtx, rm.NativeObject, exp.NativeObject, sig.NativeObject, s.NativeObject)); } #endregion #endregion // Floating-point Arithmetic #region Miscellaneous /// /// Wraps an AST. /// /// This function is used for transitions between native and /// managed objects. Note that must be a /// native object obtained from Z3 (e.g., through ) /// and that it must have a correct reference count (see e.g., /// . /// /// The native pointer to wrap. public AST WrapAST(IntPtr nativeObject) { return AST.Create(this, nativeObject); } /// /// Unwraps an AST. /// /// This function is used for transitions between native and /// managed objects. It returns the native pointer to the AST. Note that /// AST objects are reference counted and unwrapping an AST disables automatic /// reference counting, i.e., all references to the IntPtr that is returned /// must be handled externally and through native calls (see e.g., /// ). /// /// The AST to unwrap. public IntPtr UnwrapAST(AST a) { return a.NativeObject; } /// /// Return a string describing all available parameters to Expr.Simplify. /// public string SimplifyHelp() { return Native.Z3_simplify_get_help(nCtx); } /// /// Retrieves parameter descriptions for simplifier. /// public ParamDescrs SimplifyParameterDescriptions { get { return new ParamDescrs(this, Native.Z3_simplify_get_param_descrs(nCtx)); } } #endregion #region Error Handling ///// ///// A delegate which is executed when an error is raised. ///// ///// ///// Note that it is possible for memory leaks to occur if error handlers ///// throw exceptions. ///// //public delegate void ErrorHandler(Context ctx, Z3_error_code errorCode, string errorString); ///// ///// The OnError event. ///// //public event ErrorHandler OnError = null; #endregion #region Parameters /// /// Update a mutable configuration parameter. /// /// /// The list of all configuration parameters can be obtained using the Z3 executable: /// z3.exe -p /// Only a few configuration parameters are mutable once the context is created. /// An exception is thrown when trying to modify an immutable parameter. /// public void UpdateParamValue(string id, string value) { Native.Z3_update_param_value(nCtx, id, value); } #endregion #region Internal internal IntPtr m_ctx = IntPtr.Zero; internal Native.Z3_error_handler m_n_err_handler = null; internal static Object creation_lock = new Object(); internal IntPtr nCtx { get { return m_ctx; } } internal void NativeErrorHandler(IntPtr ctx, Z3_error_code errorCode) { // Do-nothing error handler. The wrappers in Z3.Native will throw exceptions upon errors. } internal void InitContext() { PrintMode = Z3_ast_print_mode.Z3_PRINT_SMTLIB2_COMPLIANT; m_n_err_handler = new Native.Z3_error_handler(NativeErrorHandler); // keep reference so it doesn't get collected. Native.Z3_set_error_handler(m_ctx, m_n_err_handler); GC.SuppressFinalize(this); } internal void CheckContextMatch(Z3Object other) { Debug.Assert(other != null); if (!ReferenceEquals(this, other.Context)) throw new Z3Exception("Context mismatch"); } internal void CheckContextMatch(Z3Object other1, Z3Object other2) { Debug.Assert(other1 != null); Debug.Assert(other2 != null); CheckContextMatch(other1); CheckContextMatch(other2); } internal void CheckContextMatch(Z3Object other1, Z3Object other2, Z3Object other3) { Debug.Assert(other1 != null); Debug.Assert(other2 != null); Debug.Assert(other3 != null); CheckContextMatch(other1); CheckContextMatch(other2); CheckContextMatch(other3); } internal void CheckContextMatch(Z3Object[] arr) { Debug.Assert(arr == null || arr.All(a => a != null)); if (arr != null) { foreach (Z3Object a in arr) { Debug.Assert(a != null); // It was an assume, now we added the precondition, and we made it into an assert CheckContextMatch(a); } } } internal void CheckContextMatch(IEnumerable arr) where T : Z3Object { Debug.Assert(arr == null || arr.All(a => a != null)); if (arr != null) { foreach (Z3Object a in arr) { Debug.Assert(a != null); // It was an assume, now we added the precondition, and we made it into an assert CheckContextMatch(a); } } } private void ObjectInvariant() { Debug.Assert(m_AST_DRQ != null); Debug.Assert(m_ASTMap_DRQ != null); Debug.Assert(m_ASTVector_DRQ != null); Debug.Assert(m_ApplyResult_DRQ != null); Debug.Assert(m_FuncEntry_DRQ != null); Debug.Assert(m_FuncInterp_DRQ != null); Debug.Assert(m_Goal_DRQ != null); Debug.Assert(m_Model_DRQ != null); Debug.Assert(m_Params_DRQ != null); Debug.Assert(m_ParamDescrs_DRQ != null); Debug.Assert(m_Probe_DRQ != null); Debug.Assert(m_Solver_DRQ != null); Debug.Assert(m_Statistics_DRQ != null); Debug.Assert(m_Tactic_DRQ != null); Debug.Assert(m_Fixedpoint_DRQ != null); Debug.Assert(m_Optimize_DRQ != null); } readonly private AST.DecRefQueue m_AST_DRQ = new AST.DecRefQueue(); readonly private ASTMap.DecRefQueue m_ASTMap_DRQ = new ASTMap.DecRefQueue(10); readonly private ASTVector.DecRefQueue m_ASTVector_DRQ = new ASTVector.DecRefQueue(10); readonly private ApplyResult.DecRefQueue m_ApplyResult_DRQ = new ApplyResult.DecRefQueue(10); readonly private FuncInterp.Entry.DecRefQueue m_FuncEntry_DRQ = new FuncInterp.Entry.DecRefQueue(10); readonly private FuncInterp.DecRefQueue m_FuncInterp_DRQ = new FuncInterp.DecRefQueue(10); readonly private Goal.DecRefQueue m_Goal_DRQ = new Goal.DecRefQueue(10); readonly private Model.DecRefQueue m_Model_DRQ = new Model.DecRefQueue(10); readonly private Params.DecRefQueue m_Params_DRQ = new Params.DecRefQueue(10); readonly private ParamDescrs.DecRefQueue m_ParamDescrs_DRQ = new ParamDescrs.DecRefQueue(10); readonly private Probe.DecRefQueue m_Probe_DRQ = new Probe.DecRefQueue(10); readonly private Solver.DecRefQueue m_Solver_DRQ = new Solver.DecRefQueue(10); readonly private Statistics.DecRefQueue m_Statistics_DRQ = new Statistics.DecRefQueue(10); readonly private Tactic.DecRefQueue m_Tactic_DRQ = new Tactic.DecRefQueue(10); readonly private Fixedpoint.DecRefQueue m_Fixedpoint_DRQ = new Fixedpoint.DecRefQueue(10); readonly private Optimize.DecRefQueue m_Optimize_DRQ = new Optimize.DecRefQueue(10); /// /// AST DRQ /// public IDecRefQueue AST_DRQ { get { return m_AST_DRQ; } } /// /// ASTMap DRQ /// public IDecRefQueue ASTMap_DRQ { get { return m_ASTMap_DRQ; } } /// /// ASTVector DRQ /// public IDecRefQueue ASTVector_DRQ { get { return m_ASTVector_DRQ; } } /// /// ApplyResult DRQ /// public IDecRefQueue ApplyResult_DRQ { get { return m_ApplyResult_DRQ; } } /// /// FuncEntry DRQ /// public IDecRefQueue FuncEntry_DRQ { get { return m_FuncEntry_DRQ; } } /// /// FuncInterp DRQ /// public IDecRefQueue FuncInterp_DRQ { get { return m_FuncInterp_DRQ; } } /// /// Goal DRQ /// public IDecRefQueue Goal_DRQ { get { return m_Goal_DRQ; } } /// /// Model DRQ /// public IDecRefQueue Model_DRQ { get { return m_Model_DRQ; } } /// /// Params DRQ /// public IDecRefQueue Params_DRQ { get { return m_Params_DRQ; } } /// /// ParamDescrs DRQ /// public IDecRefQueue ParamDescrs_DRQ { get { return m_ParamDescrs_DRQ; } } /// /// Probe DRQ /// public IDecRefQueue Probe_DRQ { get { return m_Probe_DRQ; } } /// /// Solver DRQ /// public IDecRefQueue Solver_DRQ { get { return m_Solver_DRQ; } } /// /// Statistics DRQ /// public IDecRefQueue Statistics_DRQ { get { return m_Statistics_DRQ; } } /// /// Tactic DRQ /// public IDecRefQueue Tactic_DRQ { get { return m_Tactic_DRQ; } } /// /// FixedPoint DRQ /// public IDecRefQueue Fixedpoint_DRQ { get { return m_Fixedpoint_DRQ; } } /// /// Optimize DRQ /// public IDecRefQueue Optimize_DRQ { get { return m_Fixedpoint_DRQ; } } internal long refCount = 0; /// /// Finalizer. /// ~Context() { // Console.WriteLine("Context Finalizer from " + System.Threading.Thread.CurrentThread.ManagedThreadId); Dispose(); if (refCount == 0 && m_ctx != IntPtr.Zero) { m_n_err_handler = null; IntPtr ctx = m_ctx; m_ctx = IntPtr.Zero; Native.Z3_del_context(ctx); } else GC.ReRegisterForFinalize(this); } /// /// Disposes of the context. /// public void Dispose() { // Console.WriteLine("Context Dispose from " + System.Threading.Thread.CurrentThread.ManagedThreadId); AST_DRQ.Clear(this); ASTMap_DRQ.Clear(this); ASTVector_DRQ.Clear(this); ApplyResult_DRQ.Clear(this); FuncEntry_DRQ.Clear(this); FuncInterp_DRQ.Clear(this); Goal_DRQ.Clear(this); Model_DRQ.Clear(this); Params_DRQ.Clear(this); ParamDescrs_DRQ.Clear(this); Probe_DRQ.Clear(this); Solver_DRQ.Clear(this); Statistics_DRQ.Clear(this); Tactic_DRQ.Clear(this); Fixedpoint_DRQ.Clear(this); Optimize_DRQ.Clear(this); m_boolSort = null; m_intSort = null; m_realSort = null; m_stringSort = null; } #endregion } } z3-z3-4.8.7/src/api/dotnet/DatatypeExpr.cs000066400000000000000000000013211356505360400202220ustar00rootroot00000000000000/*++ Copyright () 2012 Microsoft Corporation Module Name: DatatypeExpr.cs Abstract: Z3 Managed API: Datatype Expressions Author: Christoph Wintersteiger (cwinter) 2012-11-23 Notes: --*/ using System.Diagnostics; using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Microsoft.Z3 { /// /// Datatype expressions /// public class DatatypeExpr : Expr { #region Internal /// Constructor for DatatypeExpr internal DatatypeExpr(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } #endregion } } z3-z3-4.8.7/src/api/dotnet/DatatypeSort.cs000066400000000000000000000056001356505360400202370ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: DatatypeSort.cs Abstract: Z3 Managed API: Datatype Sorts Author: Christoph Wintersteiger (cwinter) 2012-11-23 Notes: --*/ using System.Diagnostics; using System; namespace Microsoft.Z3 { /// /// Datatype sorts. /// public class DatatypeSort : Sort { /// /// The number of constructors of the datatype sort. /// public uint NumConstructors { get { return Native.Z3_get_datatype_sort_num_constructors(Context.nCtx, NativeObject); } } /// /// The constructors. /// public FuncDecl[] Constructors { get { uint n = NumConstructors; FuncDecl[] res = new FuncDecl[n]; for (uint i = 0; i < n; i++) res[i] = new FuncDecl(Context, Native.Z3_get_datatype_sort_constructor(Context.nCtx, NativeObject, i)); return res; } } /// /// The recognizers. /// public FuncDecl[] Recognizers { get { uint n = NumConstructors; FuncDecl[] res = new FuncDecl[n]; for (uint i = 0; i < n; i++) res[i] = new FuncDecl(Context, Native.Z3_get_datatype_sort_recognizer(Context.nCtx, NativeObject, i)); return res; } } /// /// The constructor accessors. /// public FuncDecl[][] Accessors { get { uint n = NumConstructors; FuncDecl[][] res = new FuncDecl[n][]; for (uint i = 0; i < n; i++) { FuncDecl fd = new FuncDecl(Context, Native.Z3_get_datatype_sort_constructor(Context.nCtx, NativeObject, i)); uint ds = fd.DomainSize; FuncDecl[] tmp = new FuncDecl[ds]; for (uint j = 0; j < ds; j++) tmp[j] = new FuncDecl(Context, Native.Z3_get_datatype_sort_constructor_accessor(Context.nCtx, NativeObject, i, j)); res[i] = tmp; } return res; } } #region Internal internal DatatypeSort(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } internal DatatypeSort(Context ctx, Symbol name, Constructor[] constructors) : base(ctx, Native.Z3_mk_datatype(ctx.nCtx, name.NativeObject, (uint)constructors.Length, ArrayToNative(constructors))) { Debug.Assert(ctx != null); Debug.Assert(name != null); Debug.Assert(constructors != null); } #endregion }; } z3-z3-4.8.7/src/api/dotnet/Deprecated.cs000066400000000000000000000010511356505360400176500ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: Deprecated.cs Abstract: Expose deprecated features for use from the managed API those who use them for experiments. Author: Christoph Wintersteiger (cwinter) 2012-03-15 Notes: --*/ using System.Diagnostics; using System; using System.Collections.Generic; using System.Runtime.InteropServices; namespace Microsoft.Z3 { /// /// The main interaction with Z3 happens via the Context. /// public class Deprecated { } }z3-z3-4.8.7/src/api/dotnet/EnumSort.cs000066400000000000000000000070411356505360400173710ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: EnumSort.cs Abstract: Z3 Managed API: Enum Sorts Author: Christoph Wintersteiger (cwinter) 2012-11-23 Notes: --*/ using System.Diagnostics; using System; namespace Microsoft.Z3 { /// /// Enumeration sorts. /// public class EnumSort : Sort { /// /// The function declarations of the constants in the enumeration. /// public FuncDecl[] ConstDecls { get { uint n = Native.Z3_get_datatype_sort_num_constructors(Context.nCtx, NativeObject); FuncDecl[] t = new FuncDecl[n]; for (uint i = 0; i < n; i++) t[i] = new FuncDecl(Context, Native.Z3_get_datatype_sort_constructor(Context.nCtx, NativeObject, i)); return t; } } /// /// Retrieves the inx'th constant declaration in the enumeration. /// /// /// public FuncDecl ConstDecl(uint inx) { return new FuncDecl(Context, Native.Z3_get_datatype_sort_constructor(Context.nCtx, NativeObject, inx)); } /// /// The constants in the enumeration. /// public Expr[] Consts { get { FuncDecl[] cds = ConstDecls; Expr[] t = new Expr[cds.Length]; for (uint i = 0; i < t.Length; i++) t[i] = Context.MkApp(cds[i]); return t; } } /// /// Retrieves the inx'th constant in the enumeration. /// /// /// public Expr Const(uint inx) { return Context.MkApp(ConstDecl(inx)); } /// /// The test predicates (recognizers) for the constants in the enumeration. /// public FuncDecl[] TesterDecls { get { uint n = Native.Z3_get_datatype_sort_num_constructors(Context.nCtx, NativeObject); FuncDecl[] t = new FuncDecl[n]; for (uint i = 0; i < n; i++) t[i] = new FuncDecl(Context, Native.Z3_get_datatype_sort_recognizer(Context.nCtx, NativeObject, i)); return t; } } /// /// Retrieves the inx'th tester/recognizer declaration in the enumeration. /// /// /// public FuncDecl TesterDecl(uint inx) { return new FuncDecl(Context, Native.Z3_get_datatype_sort_recognizer(Context.nCtx, NativeObject, inx)); } #region Internal internal EnumSort(Context ctx, Symbol name, Symbol[] enumNames) : base(ctx, IntPtr.Zero) { Debug.Assert(ctx != null); Debug.Assert(name != null); Debug.Assert(enumNames != null); int n = enumNames.Length; IntPtr[] n_constdecls = new IntPtr[n]; IntPtr[] n_testers = new IntPtr[n]; NativeObject = Native.Z3_mk_enumeration_sort(ctx.nCtx, name.NativeObject, (uint)n, Symbol.ArrayToNative(enumNames), n_constdecls, n_testers); } #endregion }; } z3-z3-4.8.7/src/api/dotnet/Expr.cs000066400000000000000000002366341356505360400165470ustar00rootroot00000000000000/*++ Copyright () 2012 Microsoft Corporation Module Name: Expr.cs Abstract: Z3 Managed API: Expressions Author: Christoph Wintersteiger (cwinter) 2012-03-20 Notes: --*/ using System.Diagnostics; using System; using System.Linq; namespace Microsoft.Z3 { /// /// Expressions are terms. /// public class Expr : AST { /// /// Returns a simplified version of the expression. /// /// A set of parameters to configure the simplifier /// public Expr Simplify(Params p = null) { if (p == null) return Expr.Create(Context, Native.Z3_simplify(Context.nCtx, NativeObject)); else return Expr.Create(Context, Native.Z3_simplify_ex(Context.nCtx, NativeObject, p.NativeObject)); } /// /// The function declaration of the function that is applied in this expression. /// public FuncDecl FuncDecl { get { return new FuncDecl(Context, Native.Z3_get_app_decl(Context.nCtx, NativeObject)); } } /// /// Indicates whether the expression is the true or false expression /// or something else (Z3_L_UNDEF). /// public Z3_lbool BoolValue { get { return (Z3_lbool)Native.Z3_get_bool_value(Context.nCtx, NativeObject); } } /// /// The number of arguments of the expression. /// public uint NumArgs { get { return Native.Z3_get_app_num_args(Context.nCtx, NativeObject); } } /// /// The arguments of the expression. /// public Expr[] Args { get { uint n = NumArgs; Expr[] res = new Expr[n]; for (uint i = 0; i < n; i++) res[i] = Expr.Create(Context, Native.Z3_get_app_arg(Context.nCtx, NativeObject, i)); return res; } } /// /// The i'th argument of the expression. /// public Expr Arg(uint i) { return Expr.Create(Context, Native.Z3_get_app_arg(Context.nCtx, NativeObject, i)); } /// /// Update the arguments of the expression using the arguments /// The number of new arguments should coincide with the current number of arguments. /// public void Update(Expr[] args) { Debug.Assert(args != null); Debug.Assert(args.All(a => a != null)); Context.CheckContextMatch(args); if (IsApp && args.Length != NumArgs) throw new Z3Exception("Number of arguments does not match"); NativeObject = Native.Z3_update_term(Context.nCtx, NativeObject, (uint)args.Length, Expr.ArrayToNative(args)); } /// /// Substitute every occurrence of from[i] in the expression with to[i], for i smaller than num_exprs. /// /// /// The result is the new expression. The arrays from and to must have size num_exprs. /// For every i smaller than num_exprs, we must have that /// sort of from[i] must be equal to sort of to[i]. /// public Expr Substitute(Expr[] from, Expr[] to) { Debug.Assert(from != null); Debug.Assert(to != null); Debug.Assert(from.All(f => f != null)); Debug.Assert(to.All(t => t != null)); Context.CheckContextMatch(from); Context.CheckContextMatch(to); if (from.Length != to.Length) throw new Z3Exception("Argument sizes do not match"); return Expr.Create(Context, Native.Z3_substitute(Context.nCtx, NativeObject, (uint)from.Length, Expr.ArrayToNative(from), Expr.ArrayToNative(to))); } /// /// Substitute every occurrence of from in the expression with to. /// /// public Expr Substitute(Expr from, Expr to) { Debug.Assert(from != null); Debug.Assert(to != null); return Substitute(new Expr[] { from }, new Expr[] { to }); } /// /// Substitute the free variables in the expression with the expressions in /// /// /// For every i smaller than num_exprs, the variable with de-Bruijn index i is replaced with term to[i]. /// public Expr SubstituteVars(Expr[] to) { Debug.Assert(to != null); Debug.Assert(to.All(t => t != null)); Context.CheckContextMatch(to); return Expr.Create(Context, Native.Z3_substitute_vars(Context.nCtx, NativeObject, (uint)to.Length, Expr.ArrayToNative(to))); } /// /// Translates (copies) the term to the Context . /// /// A context /// A copy of the term which is associated with new public Expr Translate(Context ctx) { return (Expr)base.Translate(ctx); } /// /// Returns a string representation of the expression. /// public override string ToString() { return base.ToString(); } /// /// Indicates whether the term is a numeral /// public bool IsNumeral { get { return Native.Z3_is_numeral_ast(Context.nCtx, NativeObject) != 0; } } /// /// Indicates whether the term is well-sorted. /// /// True if the term is well-sorted, false otherwise. public bool IsWellSorted { get { return Native.Z3_is_well_sorted(Context.nCtx, NativeObject) != 0; } } /// /// The Sort of the term. /// public Sort Sort { get { return Sort.Create(Context, Native.Z3_get_sort(Context.nCtx, NativeObject)); } } #region Constants /// /// Indicates whether the term represents a constant. /// public bool IsConst { get { return IsApp && NumArgs == 0 && FuncDecl.DomainSize == 0; } } #endregion #region Integer Numerals /// /// Indicates whether the term is an integer numeral. /// public bool IsIntNum { get { return IsNumeral && IsInt; } } #endregion #region Real Numerals /// /// Indicates whether the term is a real numeral. /// public bool IsRatNum { get { return IsNumeral && IsReal; } } #endregion #region Algebraic Numbers /// /// Indicates whether the term is an algebraic number /// public bool IsAlgebraicNumber { get { return 0 != Native.Z3_is_algebraic_number(Context.nCtx, NativeObject); } } #endregion #region Term Kind Tests #region Boolean Terms /// /// Indicates whether the term has Boolean sort. /// public bool IsBool { get { return (IsExpr && Native.Z3_is_eq_sort(Context.nCtx, Native.Z3_mk_bool_sort(Context.nCtx), Native.Z3_get_sort(Context.nCtx, NativeObject)) != 0); } } /// /// Indicates whether the term is the constant true. /// public bool IsTrue { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_TRUE; } } /// /// Indicates whether the term is the constant false. /// public bool IsFalse { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FALSE; } } /// /// Indicates whether the term is an equality predicate. /// public bool IsEq { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_EQ; } } /// /// Indicates whether the term is an n-ary distinct predicate (every argument is mutually distinct). /// public bool IsDistinct { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_DISTINCT; } } /// /// Indicates whether the term is a ternary if-then-else term /// public bool IsITE { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ITE; } } /// /// Indicates whether the term is an n-ary conjunction /// public bool IsAnd { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_AND; } } /// /// Indicates whether the term is an n-ary disjunction /// public bool IsOr { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_OR; } } /// /// Indicates whether the term is an if-and-only-if (Boolean equivalence, binary) /// public bool IsIff { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_IFF; } } /// /// Indicates whether the term is an exclusive or /// public bool IsXor { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_XOR; } } /// /// Indicates whether the term is a negation /// public bool IsNot { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_NOT; } } /// /// Indicates whether the term is an implication /// public bool IsImplies { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_IMPLIES; } } /// /// Indicates whether the term is at-most /// public bool IsAtMost { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PB_AT_MOST; } } /// /// Retrieve bound of at-most /// public uint AtMostBound { get { Debug.Assert(IsAtMost); return (uint)FuncDecl.Parameters[0].Int; } } /// /// Indicates whether the term is at-least /// public bool IsAtLeast { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PB_AT_LEAST; } } /// /// Retrieve bound of at-least /// public uint AtLeastBound { get { Debug.Assert(IsAtLeast); return (uint)FuncDecl.Parameters[0].Int; } } /// /// Indicates whether the term is pbeq /// public bool IsPbEq { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PB_EQ; } } /// /// Indicates whether the term is pble /// public bool IsPbLe { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PB_LE; } } /// /// Indicates whether the term is pbge /// public bool IsPbGe { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PB_GE; } } #endregion #region Arithmetic Terms /// /// Indicates whether the term is of integer sort. /// public bool IsInt { get { return Native.Z3_get_sort_kind(Context.nCtx, Native.Z3_get_sort(Context.nCtx, NativeObject)) == (uint)Z3_sort_kind.Z3_INT_SORT; } } /// /// Indicates whether the term is of sort real. /// public bool IsReal { get { return Native.Z3_get_sort_kind(Context.nCtx, Native.Z3_get_sort(Context.nCtx, NativeObject)) == (uint)Z3_sort_kind.Z3_REAL_SORT; } } /// /// Indicates whether the term is an arithmetic numeral. /// public bool IsArithmeticNumeral { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ANUM; } } /// /// Indicates whether the term is a less-than-or-equal /// public bool IsLE { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_LE; } } /// /// Indicates whether the term is a greater-than-or-equal /// public bool IsGE { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_GE; } } /// /// Indicates whether the term is a less-than /// public bool IsLT { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_LT; } } /// /// Indicates whether the term is a greater-than /// public bool IsGT { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_GT; } } /// /// Indicates whether the term is addition (binary) /// public bool IsAdd { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ADD; } } /// /// Indicates whether the term is subtraction (binary) /// public bool IsSub { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SUB; } } /// /// Indicates whether the term is a unary minus /// public bool IsUMinus { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_UMINUS; } } /// /// Indicates whether the term is multiplication (binary) /// public bool IsMul { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_MUL; } } /// /// Indicates whether the term is division (binary) /// public bool IsDiv { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_DIV; } } /// /// Indicates whether the term is integer division (binary) /// public bool IsIDiv { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_IDIV; } } /// /// Indicates whether the term is remainder (binary) /// public bool IsRemainder { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_REM; } } /// /// Indicates whether the term is modulus (binary) /// public bool IsModulus { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_MOD; } } /// /// Indicates whether the term is a coercion of integer to real (unary) /// public bool IsIntToReal { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_TO_REAL; } } /// /// Indicates whether the term is a coercion of real to integer (unary) /// public bool IsRealToInt { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_TO_INT; } } /// /// Indicates whether the term is a check that tests whether a real is integral (unary) /// public bool IsRealIsInt { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_IS_INT; } } #endregion #region Array Terms /// /// Indicates whether the term is of an array sort. /// public bool IsArray { get { return (Native.Z3_is_app(Context.nCtx, NativeObject) != 0 && (Z3_sort_kind)Native.Z3_get_sort_kind(Context.nCtx, Native.Z3_get_sort(Context.nCtx, NativeObject)) == Z3_sort_kind.Z3_ARRAY_SORT); } } /// /// Indicates whether the term is an array store. /// /// It satisfies select(store(a,i,v),j) = if i = j then v else select(a,j). /// Array store takes at least 3 arguments. public bool IsStore { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_STORE; } } /// /// Indicates whether the term is an array select. /// public bool IsSelect { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SELECT; } } /// /// Indicates whether the term is a constant array. /// /// For example, select(const(v),i) = v holds for every v and i. The function is unary. public bool IsConstantArray { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_CONST_ARRAY; } } /// /// Indicates whether the term is a default array. /// /// For example default(const(v)) = v. The function is unary. public bool IsDefaultArray { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ARRAY_DEFAULT; } } /// /// Indicates whether the term is an array map. /// /// It satisfies map[f](a1,..,a_n)[i] = f(a1[i],...,a_n[i]) for every i. public bool IsArrayMap { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ARRAY_MAP; } } /// /// Indicates whether the term is an as-array term. /// /// An as-array term is n array value that behaves as the function graph of the /// function passed as parameter. public bool IsAsArray { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_AS_ARRAY; } } #endregion #region Set Terms /// /// Indicates whether the term is set union /// public bool IsSetUnion { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SET_UNION; } } /// /// Indicates whether the term is set intersection /// public bool IsSetIntersect { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SET_INTERSECT; } } /// /// Indicates whether the term is set difference /// public bool IsSetDifference { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SET_DIFFERENCE; } } /// /// Indicates whether the term is set complement /// public bool IsSetComplement { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SET_COMPLEMENT; } } /// /// Indicates whether the term is set subset /// public bool IsSetSubset { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SET_SUBSET; } } #endregion #region Bit-vector terms /// /// Indicates whether the terms is of bit-vector sort. /// public bool IsBV { get { return Native.Z3_get_sort_kind(Context.nCtx, Native.Z3_get_sort(Context.nCtx, NativeObject)) == (uint)Z3_sort_kind.Z3_BV_SORT; } } /// /// Indicates whether the term is a bit-vector numeral /// public bool IsBVNumeral { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BNUM; } } /// /// Indicates whether the term is a one-bit bit-vector with value one /// public bool IsBVBitOne { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BIT1; } } /// /// Indicates whether the term is a one-bit bit-vector with value zero /// public bool IsBVBitZero { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BIT0; } } /// /// Indicates whether the term is a bit-vector unary minus /// public bool IsBVUMinus { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BNEG; } } /// /// Indicates whether the term is a bit-vector addition (binary) /// public bool IsBVAdd { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BADD; } } /// /// Indicates whether the term is a bit-vector subtraction (binary) /// public bool IsBVSub { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BSUB; } } /// /// Indicates whether the term is a bit-vector multiplication (binary) /// public bool IsBVMul { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BMUL; } } /// /// Indicates whether the term is a bit-vector signed division (binary) /// public bool IsBVSDiv { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BSDIV; } } /// /// Indicates whether the term is a bit-vector unsigned division (binary) /// public bool IsBVUDiv { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BUDIV; } } /// /// Indicates whether the term is a bit-vector signed remainder (binary) /// public bool IsBVSRem { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BSREM; } } /// /// Indicates whether the term is a bit-vector unsigned remainder (binary) /// public bool IsBVURem { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BUREM; } } /// /// Indicates whether the term is a bit-vector signed modulus /// public bool IsBVSMod { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BSMOD; } } /// /// Indicates whether the term is a bit-vector signed division by zero /// internal bool IsBVSDiv0 { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BSDIV0; } } /// /// Indicates whether the term is a bit-vector unsigned division by zero /// internal bool IsBVUDiv0 { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BUDIV0; } } /// /// Indicates whether the term is a bit-vector signed remainder by zero /// internal bool IsBVSRem0 { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BSREM0; } } /// /// Indicates whether the term is a bit-vector unsigned remainder by zero /// internal bool IsBVURem0 { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BUREM0; } } /// /// Indicates whether the term is a bit-vector signed modulus by zero /// internal bool IsBVSMod0 { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BSMOD0; } } /// /// Indicates whether the term is an unsigned bit-vector less-than-or-equal /// public bool IsBVULE { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ULEQ; } } /// /// Indicates whether the term is a signed bit-vector less-than-or-equal /// public bool IsBVSLE { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SLEQ; } } /// /// Indicates whether the term is an unsigned bit-vector greater-than-or-equal /// public bool IsBVUGE { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_UGEQ; } } /// /// Indicates whether the term is a signed bit-vector greater-than-or-equal /// public bool IsBVSGE { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SGEQ; } } /// /// Indicates whether the term is an unsigned bit-vector less-than /// public bool IsBVULT { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ULT; } } /// /// Indicates whether the term is a signed bit-vector less-than /// public bool IsBVSLT { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SLT; } } /// /// Indicates whether the term is an unsigned bit-vector greater-than /// public bool IsBVUGT { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_UGT; } } /// /// Indicates whether the term is a signed bit-vector greater-than /// public bool IsBVSGT { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SGT; } } /// /// Indicates whether the term is a bit-wise AND /// public bool IsBVAND { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BAND; } } /// /// Indicates whether the term is a bit-wise OR /// public bool IsBVOR { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BOR; } } /// /// Indicates whether the term is a bit-wise NOT /// public bool IsBVNOT { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BNOT; } } /// /// Indicates whether the term is a bit-wise XOR /// public bool IsBVXOR { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BXOR; } } /// /// Indicates whether the term is a bit-wise NAND /// public bool IsBVNAND { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BNAND; } } /// /// Indicates whether the term is a bit-wise NOR /// public bool IsBVNOR { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BNOR; } } /// /// Indicates whether the term is a bit-wise XNOR /// public bool IsBVXNOR { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BXNOR; } } /// /// Indicates whether the term is a bit-vector concatenation (binary) /// public bool IsBVConcat { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_CONCAT; } } /// /// Indicates whether the term is a bit-vector sign extension /// public bool IsBVSignExtension { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SIGN_EXT; } } /// /// Indicates whether the term is a bit-vector zero extension /// public bool IsBVZeroExtension { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ZERO_EXT; } } /// /// Indicates whether the term is a bit-vector extraction /// public bool IsBVExtract { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_EXTRACT; } } /// /// Indicates whether the term is a bit-vector repetition /// public bool IsBVRepeat { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_REPEAT; } } /// /// Indicates whether the term is a bit-vector reduce OR /// public bool IsBVReduceOR { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BREDOR; } } /// /// Indicates whether the term is a bit-vector reduce AND /// public bool IsBVReduceAND { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BREDAND; } } /// /// Indicates whether the term is a bit-vector comparison /// public bool IsBVComp { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BCOMP; } } /// /// Indicates whether the term is a bit-vector shift left /// public bool IsBVShiftLeft { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BSHL; } } /// /// Indicates whether the term is a bit-vector logical shift right /// public bool IsBVShiftRightLogical { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BLSHR; } } /// /// Indicates whether the term is a bit-vector arithmetic shift left /// public bool IsBVShiftRightArithmetic { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BASHR; } } /// /// Indicates whether the term is a bit-vector rotate left /// public bool IsBVRotateLeft { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ROTATE_LEFT; } } /// /// Indicates whether the term is a bit-vector rotate right /// public bool IsBVRotateRight { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_ROTATE_RIGHT; } } /// /// Indicates whether the term is a bit-vector rotate left (extended) /// /// Similar to Z3_OP_ROTATE_LEFT, but it is a binary operator instead of a parametric one. public bool IsBVRotateLeftExtended { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_EXT_ROTATE_LEFT; } } /// /// Indicates whether the term is a bit-vector rotate right (extended) /// /// Similar to Z3_OP_ROTATE_RIGHT, but it is a binary operator instead of a parametric one. public bool IsBVRotateRightExtended { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_EXT_ROTATE_RIGHT; } } /// /// Indicates whether the term is a coercion from integer to bit-vector /// /// This function is not supported by the decision procedures. Only the most /// rudimentary simplification rules are applied to this function. public bool IsIntToBV { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_INT2BV; } } /// /// Indicates whether the term is a coercion from bit-vector to integer /// /// This function is not supported by the decision procedures. Only the most /// rudimentary simplification rules are applied to this function. public bool IsBVToInt { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_BV2INT; } } /// /// Indicates whether the term is a bit-vector carry /// /// Compute the carry bit in a full-adder. The meaning is given by the /// equivalence (carry l1 l2 l3) <=> (or (and l1 l2) (and l1 l3) (and l2 l3))) public bool IsBVCarry { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_CARRY; } } /// /// Indicates whether the term is a bit-vector ternary XOR /// /// The meaning is given by the equivalence (xor3 l1 l2 l3) <=> (xor (xor l1 l2) l3) public bool IsBVXOR3 { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_XOR3; } } #endregion #region Labels /// /// Indicates whether the term is a label (used by the Boogie Verification condition generator). /// /// The label has two parameters, a string and a Boolean polarity. It takes one argument, a formula. public bool IsLabel { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_LABEL; } } /// /// Indicates whether the term is a label literal (used by the Boogie Verification condition generator). /// /// A label literal has a set of string parameters. It takes no arguments. public bool IsLabelLit { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_LABEL_LIT; } } #endregion #region Sequences and Strings /// /// Check whether expression is a string constant. /// /// a Boolean public bool IsString { get { return IsApp && Native.Z3_is_string(Context.nCtx, NativeObject) != 0; } } /// /// Retrieve string corresponding to string constant. /// /// the expression should be a string constant, (IsString should be true). public string String { get { return Native.Z3_get_string(Context.nCtx, NativeObject); } } /// /// Check whether expression is a concatenation. /// /// a Boolean public bool IsConcat { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SEQ_CONCAT; } } /// /// Check whether expression is a prefix. /// /// a Boolean public bool IsPrefix { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SEQ_PREFIX; } } /// /// Check whether expression is a suffix. /// /// a Boolean public bool IsSuffix { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SEQ_SUFFIX; } } /// /// Check whether expression is a contains. /// /// a Boolean public bool IsContains { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SEQ_CONTAINS; } } /// /// Check whether expression is an extract. /// /// a Boolean public bool IsExtract { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SEQ_EXTRACT; } } /// /// Check whether expression is a replace. /// /// a Boolean public bool IsReplace { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SEQ_REPLACE; } } /// /// Check whether expression is an at. /// /// a Boolean public bool IsAt { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SEQ_AT; } } /// /// Check whether expression is a sequence length. /// /// a Boolean public bool IsLength { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SEQ_LENGTH; } } /// /// Check whether expression is a sequence index. /// /// a Boolean public bool IsIndex { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_SEQ_INDEX; } } #endregion #region Proof Terms /// /// Indicates whether the term is a binary equivalence modulo namings. /// /// This binary predicate is used in proof terms. /// It captures equisatisfiability and equivalence modulo renamings. public bool IsOEQ { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_OEQ; } } /// /// Indicates whether the term is a Proof for the expression 'true'. /// public bool IsProofTrue { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_TRUE; } } /// /// Indicates whether the term is a proof for a fact asserted by the user. /// public bool IsProofAsserted { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_ASSERTED; } } /// /// Indicates whether the term is a proof for a fact (tagged as goal) asserted by the user. /// public bool IsProofGoal { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_GOAL; } } /// /// Indicates whether the term is proof via modus ponens /// /// /// Given a proof for p and a proof for (implies p q), produces a proof for q. /// T1: p /// T2: (implies p q) /// [mp T1 T2]: q /// The second antecedents may also be a proof for (iff p q). public bool IsProofModusPonens { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_MODUS_PONENS; } } /// /// Indicates whether the term is a proof for (R t t), where R is a reflexive relation. /// /// This proof object has no antecedents. /// The only reflexive relations that are used are /// equivalence modulo namings, equality and equivalence. /// That is, R is either '~', '=' or 'iff'. public bool IsProofReflexivity { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_REFLEXIVITY; } } /// /// Indicates whether the term is proof by symmetricity of a relation /// /// /// Given an symmetric relation R and a proof for (R t s), produces a proof for (R s t). /// T1: (R t s) /// [symmetry T1]: (R s t) /// T1 is the antecedent of this proof object. /// public bool IsProofSymmetry { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_SYMMETRY; } } /// /// Indicates whether the term is a proof by transitivity of a relation /// /// /// Given a transitive relation R, and proofs for (R t s) and (R s u), produces a proof /// for (R t u). /// T1: (R t s) /// T2: (R s u) /// [trans T1 T2]: (R t u) /// public bool IsProofTransitivity { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_TRANSITIVITY; } } /// /// Indicates whether the term is a proof by condensed transitivity of a relation /// /// /// Condensed transitivity proof. /// It combines several symmetry and transitivity proofs. /// Example: /// T1: (R a b) /// T2: (R c b) /// T3: (R c d) /// [trans* T1 T2 T3]: (R a d) /// R must be a symmetric and transitive relation. /// /// Assuming that this proof object is a proof for (R s t), then /// a proof checker must check if it is possible to prove (R s t) /// using the antecedents, symmetry and transitivity. That is, /// if there is a path from s to t, if we view every /// antecedent (R a b) as an edge between a and b. /// public bool IsProofTransitivityStar { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_TRANSITIVITY_STAR; } } /// /// Indicates whether the term is a monotonicity proof object. /// /// /// T1: (R t_1 s_1) /// ... /// Tn: (R t_n s_n) /// [monotonicity T1 ... Tn]: (R (f t_1 ... t_n) (f s_1 ... s_n)) /// Remark: if t_i == s_i, then the antecedent Ti is suppressed. /// That is, reflexivity proofs are suppressed to save space. /// public bool IsProofMonotonicity { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_MONOTONICITY; } } /// /// Indicates whether the term is a quant-intro proof /// /// /// Given a proof for (~ p q), produces a proof for (~ (forall (x) p) (forall (x) q)). /// T1: (~ p q) /// [quant-intro T1]: (~ (forall (x) p) (forall (x) q)) /// public bool IsProofQuantIntro { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_QUANT_INTRO; } } /// /// Indicates whether the term is a distributivity proof object. /// /// /// Given that f (= or) distributes over g (= and), produces a proof for /// (= (f a (g c d)) /// (g (f a c) (f a d))) /// If f and g are associative, this proof also justifies the following equality: /// (= (f (g a b) (g c d)) /// (g (f a c) (f a d) (f b c) (f b d))) /// where each f and g can have arbitrary number of arguments. /// /// This proof object has no antecedents. /// Remark. This rule is used by the CNF conversion pass and /// instantiated by f = or, and g = and. /// public bool IsProofDistributivity { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_DISTRIBUTIVITY; } } /// /// Indicates whether the term is a proof by elimination of AND /// /// /// Given a proof for (and l_1 ... l_n), produces a proof for l_i /// T1: (and l_1 ... l_n) /// [and-elim T1]: l_i /// public bool IsProofAndElimination { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_AND_ELIM; } } /// /// Indicates whether the term is a proof by elimination of not-or /// /// /// Given a proof for (not (or l_1 ... l_n)), produces a proof for (not l_i). /// T1: (not (or l_1 ... l_n)) /// [not-or-elim T1]: (not l_i) /// public bool IsProofOrElimination { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_NOT_OR_ELIM; } } /// /// Indicates whether the term is a proof by rewriting /// /// /// A proof for a local rewriting step (= t s). /// The head function symbol of t is interpreted. /// /// This proof object has no antecedents. /// The conclusion of a rewrite rule is either an equality (= t s), /// an equivalence (iff t s), or equi-satisfiability (~ t s). /// Remark: if f is bool, then = is iff. /// /// Examples: /// (= (+ x 0) x) /// (= (+ x 1 2) (+ 3 x)) /// (iff (or x false) x) /// public bool IsProofRewrite { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_REWRITE; } } /// /// Indicates whether the term is a proof by rewriting /// /// /// A proof for rewriting an expression t into an expression s. /// This proof object can have n antecedents. /// The antecedents are proofs for equalities used as substitution rules. /// The object is used in a few cases: /// - When applying contextual simplification (CONTEXT_SIMPLIFIER=true) /// - When converting bit-vectors to Booleans (BIT2BOOL=true) /// public bool IsProofRewriteStar { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_REWRITE_STAR; } } /// /// Indicates whether the term is a proof for pulling quantifiers out. /// /// /// A proof for (iff (f (forall (x) q(x)) r) (forall (x) (f (q x) r))). This proof object has no antecedents. /// public bool IsProofPullQuant { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_PULL_QUANT; } } /// /// Indicates whether the term is a proof for pushing quantifiers in. /// /// /// A proof for: /// (iff (forall (x_1 ... x_m) (and p_1[x_1 ... x_m] ... p_n[x_1 ... x_m])) /// (and (forall (x_1 ... x_m) p_1[x_1 ... x_m]) /// ... /// (forall (x_1 ... x_m) p_n[x_1 ... x_m]))) /// This proof object has no antecedents /// public bool IsProofPushQuant { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_PUSH_QUANT; } } /// /// Indicates whether the term is a proof for elimination of unused variables. /// /// /// A proof for (iff (forall (x_1 ... x_n y_1 ... y_m) p[x_1 ... x_n]) /// (forall (x_1 ... x_n) p[x_1 ... x_n])) /// /// It is used to justify the elimination of unused variables. /// This proof object has no antecedents. /// public bool IsProofElimUnusedVars { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_ELIM_UNUSED_VARS; } } /// /// Indicates whether the term is a proof for destructive equality resolution /// /// /// A proof for destructive equality resolution: /// (iff (forall (x) (or (not (= x t)) P[x])) P[t]) /// if x does not occur in t. /// /// This proof object has no antecedents. /// /// Several variables can be eliminated simultaneously. /// public bool IsProofDER { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_DER; } } /// /// Indicates whether the term is a proof for quantifier instantiation /// /// /// A proof of (or (not (forall (x) (P x))) (P a)) /// public bool IsProofQuantInst { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_QUANT_INST; } } /// /// Indicates whether the term is a hypothesis marker. /// /// Mark a hypothesis in a natural deduction style proof. public bool IsProofHypothesis { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_HYPOTHESIS; } } /// /// Indicates whether the term is a proof by lemma /// /// /// T1: false /// [lemma T1]: (or (not l_1) ... (not l_n)) /// /// This proof object has one antecedent: a hypothetical proof for false. /// It converts the proof in a proof for (or (not l_1) ... (not l_n)), /// when T1 contains the hypotheses: l_1, ..., l_n. /// public bool IsProofLemma { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_LEMMA; } } /// /// Indicates whether the term is a proof by unit resolution /// /// /// T1: (or l_1 ... l_n l_1' ... l_m') /// T2: (not l_1) /// ... /// T(n+1): (not l_n) /// [unit-resolution T1 ... T(n+1)]: (or l_1' ... l_m') /// public bool IsProofUnitResolution { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_UNIT_RESOLUTION; } } /// /// Indicates whether the term is a proof by iff-true /// /// /// T1: p /// [iff-true T1]: (iff p true) /// public bool IsProofIFFTrue { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_IFF_TRUE; } } /// /// Indicates whether the term is a proof by iff-false /// /// /// T1: (not p) /// [iff-false T1]: (iff p false) /// public bool IsProofIFFFalse { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_IFF_FALSE; } } /// /// Indicates whether the term is a proof by commutativity /// /// /// [comm]: (= (f a b) (f b a)) /// /// f is a commutative operator. /// /// This proof object has no antecedents. /// Remark: if f is bool, then = is iff. /// public bool IsProofCommutativity { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_COMMUTATIVITY; } } /// /// Indicates whether the term is a proof for Tseitin-like axioms /// /// /// Proof object used to justify Tseitin's like axioms: /// /// (or (not (and p q)) p) /// (or (not (and p q)) q) /// (or (not (and p q r)) p) /// (or (not (and p q r)) q) /// (or (not (and p q r)) r) /// ... /// (or (and p q) (not p) (not q)) /// (or (not (or p q)) p q) /// (or (or p q) (not p)) /// (or (or p q) (not q)) /// (or (not (iff p q)) (not p) q) /// (or (not (iff p q)) p (not q)) /// (or (iff p q) (not p) (not q)) /// (or (iff p q) p q) /// (or (not (ite a b c)) (not a) b) /// (or (not (ite a b c)) a c) /// (or (ite a b c) (not a) (not b)) /// (or (ite a b c) a (not c)) /// (or (not (not a)) (not a)) /// (or (not a) a) /// /// This proof object has no antecedents. /// Note: all axioms are propositional tautologies. /// Note also that 'and' and 'or' can take multiple arguments. /// You can recover the propositional tautologies by /// unfolding the Boolean connectives in the axioms a small /// bounded number of steps (=3). /// public bool IsProofDefAxiom { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_DEF_AXIOM; } } /// /// Indicates whether the term is a proof for introduction of a name /// /// /// Introduces a name for a formula/term. /// Suppose e is an expression with free variables x, and def-intro /// introduces the name n(x). The possible cases are: /// /// When e is of Boolean type: /// [def-intro]: (and (or n (not e)) (or (not n) e)) /// /// or: /// [def-intro]: (or (not n) e) /// when e only occurs positively. /// /// When e is of the form (ite cond th el): /// [def-intro]: (and (or (not cond) (= n th)) (or cond (= n el))) /// /// Otherwise: /// [def-intro]: (= n e) /// public bool IsProofDefIntro { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_DEF_INTRO; } } /// /// Indicates whether the term is a proof for application of a definition /// /// /// [apply-def T1]: F ~ n /// F is 'equivalent' to n, given that T1 is a proof that /// n is a name for F. /// public bool IsProofApplyDef { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_APPLY_DEF; } } /// /// Indicates whether the term is a proof iff-oeq /// /// /// T1: (iff p q) /// [iff~ T1]: (~ p q) /// public bool IsProofIFFOEQ { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_IFF_OEQ; } } /// /// Indicates whether the term is a proof for a positive NNF step /// /// /// Proof for a (positive) NNF step. Example: /// /// T1: (not s_1) ~ r_1 /// T2: (not s_2) ~ r_2 /// T3: s_1 ~ r_1' /// T4: s_2 ~ r_2' /// [nnf-pos T1 T2 T3 T4]: (~ (iff s_1 s_2) /// (and (or r_1 r_2') (or r_1' r_2))) /// /// The negation normal form steps NNF_POS and NNF_NEG are used in the following cases: /// (a) When creating the NNF of a positive force quantifier. /// The quantifier is retained (unless the bound variables are eliminated). /// Example /// T1: q ~ q_new /// [nnf-pos T1]: (~ (forall (x T) q) (forall (x T) q_new)) /// /// (b) When recursively creating NNF over Boolean formulas, where the top-level /// connective is changed during NNF conversion. The relevant Boolean connectives /// for NNF_POS are 'implies', 'iff', 'xor', 'ite'. /// NNF_NEG furthermore handles the case where negation is pushed /// over Boolean connectives 'and' and 'or'. /// public bool IsProofNNFPos { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_NNF_POS; } } /// /// Indicates whether the term is a proof for a negative NNF step /// /// /// Proof for a (negative) NNF step. Examples: /// /// T1: (not s_1) ~ r_1 /// ... /// Tn: (not s_n) ~ r_n /// [nnf-neg T1 ... Tn]: (not (and s_1 ... s_n)) ~ (or r_1 ... r_n) /// and /// T1: (not s_1) ~ r_1 /// ... /// Tn: (not s_n) ~ r_n /// [nnf-neg T1 ... Tn]: (not (or s_1 ... s_n)) ~ (and r_1 ... r_n) /// and /// T1: (not s_1) ~ r_1 /// T2: (not s_2) ~ r_2 /// T3: s_1 ~ r_1' /// T4: s_2 ~ r_2' /// [nnf-neg T1 T2 T3 T4]: (~ (not (iff s_1 s_2)) /// (and (or r_1 r_2) (or r_1' r_2'))) /// public bool IsProofNNFNeg { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_NNF_NEG; } } /// /// Indicates whether the term is a proof for a Skolemization step /// /// /// Proof for: /// /// [sk]: (~ (not (forall x (p x y))) (not (p (sk y) y))) /// [sk]: (~ (exists x (p x y)) (p (sk y) y)) /// /// This proof object has no antecedents. /// public bool IsProofSkolemize { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_SKOLEMIZE; } } /// /// Indicates whether the term is a proof by modus ponens for equi-satisfiability. /// /// /// Modus ponens style rule for equi-satisfiability. /// T1: p /// T2: (~ p q) /// [mp~ T1 T2]: q /// public bool IsProofModusPonensOEQ { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_MODUS_PONENS_OEQ; } } /// /// Indicates whether the term is a proof for theory lemma /// /// /// Generic proof for theory lemmas. /// /// The theory lemma function comes with one or more parameters. /// The first parameter indicates the name of the theory. /// For the theory of arithmetic, additional parameters provide hints for /// checking the theory lemma. /// The hints for arithmetic are: /// - farkas - followed by rational coefficients. Multiply the coefficients to the /// inequalities in the lemma, add the (negated) inequalities and obtain a contradiction. /// - triangle-eq - Indicates a lemma related to the equivalence: /// (iff (= t1 t2) (and (<= t1 t2) (<= t2 t1))) /// - gcd-test - Indicates an integer linear arithmetic lemma that uses a gcd test. /// public bool IsProofTheoryLemma { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_TH_LEMMA; } } #endregion #region Relational Terms /// /// Indicates whether the term is of relation sort. /// public bool IsRelation { get { return (Native.Z3_is_app(Context.nCtx, NativeObject) != 0 && Native.Z3_get_sort_kind(Context.nCtx, Native.Z3_get_sort(Context.nCtx, NativeObject)) == (uint)Z3_sort_kind.Z3_RELATION_SORT); } } /// /// Indicates whether the term is an relation store /// /// /// Insert a record into a relation. /// The function takes n+1 arguments, where the first argument is the relation and the remaining n elements /// correspond to the n columns of the relation. /// public bool IsRelationStore { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_STORE; } } /// /// Indicates whether the term is an empty relation /// public bool IsEmptyRelation { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_EMPTY; } } /// /// Indicates whether the term is a test for the emptiness of a relation /// public bool IsIsEmptyRelation { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_IS_EMPTY; } } /// /// Indicates whether the term is a relational join /// public bool IsRelationalJoin { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_JOIN; } } /// /// Indicates whether the term is the union or convex hull of two relations. /// /// The function takes two arguments. public bool IsRelationUnion { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_UNION; } } /// /// Indicates whether the term is the widening of two relations /// /// The function takes two arguments. public bool IsRelationWiden { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_WIDEN; } } /// /// Indicates whether the term is a projection of columns (provided as numbers in the parameters). /// /// The function takes one argument. public bool IsRelationProject { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_PROJECT; } } /// /// Indicates whether the term is a relation filter /// /// /// Filter (restrict) a relation with respect to a predicate. /// The first argument is a relation. /// The second argument is a predicate with free de-Bruijn indices /// corresponding to the columns of the relation. /// So the first column in the relation has index 0. /// public bool IsRelationFilter { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_FILTER; } } /// /// Indicates whether the term is an intersection of a relation with the negation of another. /// /// /// Intersect the first relation with respect to negation /// of the second relation (the function takes two arguments). /// Logically, the specification can be described by a function /// /// target = filter_by_negation(pos, neg, columns) /// /// where columns are pairs c1, d1, .., cN, dN of columns from pos and neg, such that /// target are elements in x in pos, such that there is no y in neg that agrees with /// x on the columns c1, d1, .., cN, dN. /// public bool IsRelationNegationFilter { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_NEGATION_FILTER; } } /// /// Indicates whether the term is the renaming of a column in a relation /// /// /// The function takes one argument. /// The parameters contain the renaming as a cycle. /// public bool IsRelationRename { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_RENAME; } } /// /// Indicates whether the term is the complement of a relation /// public bool IsRelationComplement { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_COMPLEMENT; } } /// /// Indicates whether the term is a relational select /// /// /// Check if a record is an element of the relation. /// The function takes n+1 arguments, where the first argument is a relation, /// and the remaining n arguments correspond to a record. /// public bool IsRelationSelect { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_SELECT; } } /// /// Indicates whether the term is a relational clone (copy) /// /// /// Create a fresh copy (clone) of a relation. /// The function is logically the identity, but /// in the context of a register machine allows /// for terms of kind /// to perform destructive updates to the first argument. /// public bool IsRelationClone { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_RA_CLONE; } } #endregion #region Finite domain terms /// /// Indicates whether the term is of an array sort. /// public bool IsFiniteDomain { get { return (Native.Z3_is_app(Context.nCtx, NativeObject) != 0 && Native.Z3_get_sort_kind(Context.nCtx, Native.Z3_get_sort(Context.nCtx, NativeObject)) == (uint)Z3_sort_kind.Z3_FINITE_DOMAIN_SORT); } } /// /// Indicates whether the term is a less than predicate over a finite domain. /// public bool IsFiniteDomainLT { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FD_LT; } } #endregion #region Floating-point terms /// /// Indicates whether the terms is of floating-point sort. /// public bool IsFP { get { return Native.Z3_get_sort_kind(Context.nCtx, Native.Z3_get_sort(Context.nCtx, NativeObject)) == (uint)Z3_sort_kind.Z3_FLOATING_POINT_SORT; } } /// /// Indicates whether the terms is of floating-point rounding mode sort. /// public bool IsFPRM { get { return Native.Z3_get_sort_kind(Context.nCtx, Native.Z3_get_sort(Context.nCtx, NativeObject)) == (uint)Z3_sort_kind.Z3_ROUNDING_MODE_SORT; } } /// /// Indicates whether the term is a floating-point numeral /// public bool IsFPNumeral { get { return IsFP && IsNumeral; } } /// /// Indicates whether the term is a floating-point rounding mode numeral /// public bool IsFPRMNumeral { get { return IsFPRM && IsNumeral; } } /// /// Indicates whether the term is the floating-point rounding numeral roundNearestTiesToEven /// public bool IsFPRMRoundNearestTiesToEven{ get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_RM_NEAREST_TIES_TO_EVEN; } } /// /// Indicates whether the term is the floating-point rounding numeral roundNearestTiesToAway /// public bool IsFPRMRoundNearestTiesToAway{ get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_RM_NEAREST_TIES_TO_AWAY; } } /// /// Indicates whether the term is the floating-point rounding numeral roundTowardNegative /// public bool IsFPRMRoundTowardNegative{ get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_RM_TOWARD_NEGATIVE; } } /// /// Indicates whether the term is the floating-point rounding numeral roundTowardPositive /// public bool IsFPRMRoundTowardPositive{ get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_RM_TOWARD_POSITIVE; } } /// /// Indicates whether the term is the floating-point rounding numeral roundTowardZero /// public bool IsFPRMRoundTowardZero{ get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_RM_TOWARD_ZERO; } } /// /// Indicates whether the term is the floating-point rounding numeral roundNearestTiesToEven /// public bool IsFPRMExprRNE{ get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_RM_NEAREST_TIES_TO_EVEN; } } /// /// Indicates whether the term is the floating-point rounding numeral roundNearestTiesToAway /// public bool IsFPRMExprRNA { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_RM_NEAREST_TIES_TO_AWAY; } } /// /// Indicates whether the term is the floating-point rounding numeral roundTowardNegative /// public bool IsFPRMExprRTN { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_RM_TOWARD_NEGATIVE; } } /// /// Indicates whether the term is the floating-point rounding numeral roundTowardPositive /// public bool IsFPRMExprRTP { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_RM_TOWARD_POSITIVE; } } /// /// Indicates whether the term is the floating-point rounding numeral roundTowardZero /// public bool IsFPRMExprRTZ { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_RM_TOWARD_ZERO; } } /// /// Indicates whether the term is a floating-point rounding mode numeral /// public bool IsFPRMExpr { get { return IsApp && (FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_RM_NEAREST_TIES_TO_AWAY|| FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_RM_NEAREST_TIES_TO_EVEN || FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_RM_TOWARD_POSITIVE || FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_RM_TOWARD_NEGATIVE || FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_RM_TOWARD_ZERO); } } /// /// Indicates whether the term is a floating-point +oo /// public bool IsFPPlusInfinity{ get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_PLUS_INF; } } /// /// Indicates whether the term is a floating-point -oo /// public bool IsFPMinusInfinity{ get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_MINUS_INF; } } /// /// Indicates whether the term is a floating-point NaN /// public bool IsFPNaN { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_NAN; } } /// /// Indicates whether the term is a floating-point +zero /// public bool IsFPPlusZero { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_PLUS_ZERO; } } /// /// Indicates whether the term is a floating-point -zero /// public bool IsFPMinusZero { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_MINUS_ZERO; } } /// /// Indicates whether the term is a floating-point addition term /// public bool IsFPAdd { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_ADD; } } /// /// Indicates whether the term is a floating-point subtraction term /// public bool IsFPSub { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_SUB; } } /// /// Indicates whether the term is a floating-point negation term /// public bool IsFPNeg { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_NEG; } } /// /// Indicates whether the term is a floating-point multiplication term /// public bool IsFPMul { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_MUL; } } /// /// Indicates whether the term is a floating-point division term /// public bool IsFPDiv { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_DIV; } } /// /// Indicates whether the term is a floating-point remainder term /// public bool IsFPRem { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_REM; } } /// /// Indicates whether the term is a floating-point term absolute value term /// public bool IsFPAbs { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_ABS; } } /// /// Indicates whether the term is a floating-point minimum term /// public bool IsFPMin { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_MIN; } } /// /// Indicates whether the term is a floating-point maximum term /// public bool IsFPMax { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_MAX; } } /// /// Indicates whether the term is a floating-point fused multiply-add term /// public bool IsFPFMA { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_FMA; } } /// /// Indicates whether the term is a floating-point square root term /// public bool IsFPSqrt { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_SQRT; } } /// /// Indicates whether the term is a floating-point roundToIntegral term /// public bool IsFPRoundToIntegral { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_ROUND_TO_INTEGRAL; } } /// /// Indicates whether the term is a floating-point equality term /// public bool IsFPEq { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_EQ; } } /// /// Indicates whether the term is a floating-point less-than term /// public bool IsFPLt { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_LT; } } /// /// Indicates whether the term is a floating-point greater-than term /// public bool IsFPGt { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_GT; } } /// /// Indicates whether the term is a floating-point less-than or equal term /// public bool IsFPLe { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_LE; } } /// /// Indicates whether the term is a floating-point greater-than or equal term /// public bool IsFPGe { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_GE; } } /// /// Indicates whether the term is a floating-point isNaN predicate term /// public bool IsFPisNaN { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_IS_NAN; } } /// /// Indicates whether the term is a floating-point isInf predicate term /// public bool IsFPisInf { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_IS_INF; } } /// /// Indicates whether the term is a floating-point isZero predicate term /// public bool IsFPisZero { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_IS_ZERO; } } /// /// Indicates whether the term is a floating-point isNormal term /// public bool IsFPisNormal { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_IS_NORMAL; } } /// /// Indicates whether the term is a floating-point isSubnormal predicate term /// public bool IsFPisSubnormal { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_IS_SUBNORMAL; } } /// /// Indicates whether the term is a floating-point isNegative predicate term /// public bool IsFPisNegative { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_IS_NEGATIVE; } } /// /// Indicates whether the term is a floating-point isPositive predicate term /// public bool IsFPisPositive { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_IS_POSITIVE; } } /// /// Indicates whether the term is a floating-point constructor term /// public bool IsFPFP { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_FP; } } /// /// Indicates whether the term is a floating-point conversion term /// public bool IsFPToFp { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_TO_FP; } } /// /// Indicates whether the term is a floating-point conversion from unsigned bit-vector term /// public bool IsFPToFpUnsigned { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_TO_FP_UNSIGNED; } } /// /// Indicates whether the term is a floating-point conversion to unsigned bit-vector term /// public bool IsFPToUBV { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_TO_UBV; } } /// /// Indicates whether the term is a floating-point conversion to signed bit-vector term /// public bool IsFPToSBV { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_TO_SBV; } } /// /// Indicates whether the term is a floating-point conversion to real term /// public bool IsFPToReal { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_TO_REAL; } } /// /// Indicates whether the term is a floating-point conversion to IEEE-754 bit-vector term /// public bool IsFPToIEEEBV { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_TO_IEEE_BV; } } #endregion #endregion #region Bound Variables /// /// The de-Bruijn index of a bound variable. /// /// /// Bound variables are indexed by de-Bruijn indices. It is perhaps easiest to explain /// the meaning of de-Bruijn indices by indicating the compilation process from /// non-de-Bruijn formulas to de-Bruijn format. /// /// abs(forall (x1) phi) = forall (x1) abs1(phi, x1, 0) /// abs(forall (x1, x2) phi) = abs(forall (x1) abs(forall (x2) phi)) /// abs1(x, x, n) = b_n /// abs1(y, x, n) = y /// abs1(f(t1,...,tn), x, n) = f(abs1(t1,x,n), ..., abs1(tn,x,n)) /// abs1(forall (x1) phi, x, n) = forall (x1) (abs1(phi, x, n+1)) /// /// The last line is significant: the index of a bound variable is different depending /// on the scope in which it appears. The deeper x appears, the higher is its /// index. /// public uint Index { get { if (!IsVar) throw new Z3Exception("Term is not a bound variable."); return Native.Z3_get_index_value(Context.nCtx, NativeObject); } } #endregion #region Internal /// /// Constructor for Expr /// internal protected Expr(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } #if DEBUG internal override void CheckNativeObject(IntPtr obj) { if (Native.Z3_is_app(Context.nCtx, obj) == 0 && Native.Z3_get_ast_kind(Context.nCtx, obj) != (uint)Z3_ast_kind.Z3_VAR_AST && Native.Z3_get_ast_kind(Context.nCtx, obj) != (uint)Z3_ast_kind.Z3_QUANTIFIER_AST) throw new Z3Exception("Underlying object is not a term"); base.CheckNativeObject(obj); } #endif internal static Expr Create(Context ctx, FuncDecl f, params Expr[] arguments) { Debug.Assert(ctx != null); Debug.Assert(f != null); IntPtr obj = Native.Z3_mk_app(ctx.nCtx, f.NativeObject, AST.ArrayLength(arguments), AST.ArrayToNative(arguments)); return Create(ctx, obj); } new internal static Expr Create(Context ctx, IntPtr obj) { Debug.Assert(ctx != null); Z3_ast_kind k = (Z3_ast_kind)Native.Z3_get_ast_kind(ctx.nCtx, obj); if (k == Z3_ast_kind.Z3_QUANTIFIER_AST) return new Quantifier(ctx, obj); IntPtr s = Native.Z3_get_sort(ctx.nCtx, obj); Z3_sort_kind sk = (Z3_sort_kind)Native.Z3_get_sort_kind(ctx.nCtx, s); if (0 != Native.Z3_is_algebraic_number(ctx.nCtx, obj)) // is this a numeral ast? return new AlgebraicNum(ctx, obj); if (Native.Z3_is_numeral_ast(ctx.nCtx, obj) != 0) { switch (sk) { case Z3_sort_kind.Z3_INT_SORT: return new IntNum(ctx, obj); case Z3_sort_kind.Z3_REAL_SORT: return new RatNum(ctx, obj); case Z3_sort_kind.Z3_BV_SORT: return new BitVecNum(ctx, obj); case Z3_sort_kind.Z3_FLOATING_POINT_SORT: return new FPNum(ctx, obj); case Z3_sort_kind.Z3_ROUNDING_MODE_SORT: return new FPRMNum(ctx, obj); case Z3_sort_kind.Z3_FINITE_DOMAIN_SORT: return new FiniteDomainNum(ctx, obj); } } switch (sk) { case Z3_sort_kind.Z3_BOOL_SORT: return new BoolExpr(ctx, obj); case Z3_sort_kind.Z3_INT_SORT: return new IntExpr(ctx, obj); case Z3_sort_kind.Z3_REAL_SORT: return new RealExpr(ctx, obj); case Z3_sort_kind.Z3_BV_SORT: return new BitVecExpr(ctx, obj); case Z3_sort_kind.Z3_ARRAY_SORT: return new ArrayExpr(ctx, obj); case Z3_sort_kind.Z3_DATATYPE_SORT: return new DatatypeExpr(ctx, obj); case Z3_sort_kind.Z3_FLOATING_POINT_SORT: return new FPExpr(ctx, obj); case Z3_sort_kind.Z3_ROUNDING_MODE_SORT: return new FPRMExpr(ctx, obj); case Z3_sort_kind.Z3_FINITE_DOMAIN_SORT: return new FiniteDomainExpr(ctx, obj); case Z3_sort_kind.Z3_RE_SORT: return new ReExpr(ctx, obj); case Z3_sort_kind.Z3_SEQ_SORT: return new SeqExpr(ctx, obj); } return new Expr(ctx, obj); } #endregion } } z3-z3-4.8.7/src/api/dotnet/FPExpr.cs000066400000000000000000000017751356505360400167710ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: FPExpr.cs Abstract: Z3 Managed API: Floating Point Expressions Author: Christoph Wintersteiger (cwinter) 2013-06-10 Notes: --*/ using System.Diagnostics; using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Microsoft.Z3 { /// /// FloatingPoint Expressions /// public class FPExpr : Expr { /// /// The number of exponent bits. /// public uint EBits { get { return ((FPSort)Sort).EBits; } } /// /// The number of significand bits. /// public uint SBits { get { return ((FPSort)Sort).EBits; } } #region Internal /// Constructor for FPExpr internal FPExpr(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } #endregion } } z3-z3-4.8.7/src/api/dotnet/FPNum.cs000066400000000000000000000142451356505360400166060ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: FPNum.cs Abstract: Z3 Managed API: Floating Point Numerals Author: Christoph Wintersteiger (cwinter) 2013-06-10 Notes: --*/ using System.Diagnostics; using System; namespace Microsoft.Z3 { /// /// FloatiungPoint Numerals /// public class FPNum : FPExpr { /// /// The sign of a floating-point numeral as a bit-vector expression /// /// /// NaN's do not have a bit-vector sign, so they are invalid arguments. /// public BitVecExpr SignBV { get { return new BitVecExpr(Context, Native.Z3_fpa_get_numeral_sign_bv(Context.nCtx, NativeObject)); } } /// /// Retrieves the sign of a floating-point literal /// /// /// Remarks: returns true if the numeral is negative /// public bool Sign { get { int res = 0; if (Native.Z3_fpa_get_numeral_sign(Context.nCtx, NativeObject, ref res) == 0) throw new Z3Exception("Sign is not a Boolean value"); return res != 0; } } /// /// The significand value of a floating-point numeral as a string /// /// /// The significand s is always 0 < s < 2.0; the resulting string is long /// enough to represent the real significand precisely. /// public string Significand { get { return Native.Z3_fpa_get_numeral_significand_string(Context.nCtx, NativeObject); } } /// /// The significand value of a floating-point numeral as a UInt64 /// /// /// This function extracts the significand bits, without the /// hidden bit or normalization. Throws an exception if the /// significand does not fit into a UInt64. /// public UInt64 SignificandUInt64 { get { UInt64 result = 0; if (Native.Z3_fpa_get_numeral_significand_uint64(Context.nCtx, NativeObject, ref result) == 0) throw new Z3Exception("Significand is not a 64 bit unsigned integer"); return result; } } /// /// The significand of a floating-point numeral as a bit-vector expression /// /// /// +oo, -oo and NaN's do not have a bit-vector significand, so they are invalid arguments. /// public BitVecExpr SignificandBV { get { return new BitVecExpr(Context, Native.Z3_fpa_get_numeral_significand_bv(Context.nCtx, NativeObject)); } } /// /// Return the (biased) exponent value of a floating-point numeral as a string /// public string Exponent(bool biased = true) { return Native.Z3_fpa_get_numeral_exponent_string(Context.nCtx, NativeObject, (byte)(biased ? 1 : 0)); } /// /// Return the exponent value of a floating-point numeral as a signed 64-bit integer /// public Int64 ExponentInt64(bool biased = true) { Int64 result = 0; if (Native.Z3_fpa_get_numeral_exponent_int64(Context.nCtx, NativeObject, ref result, (byte)(biased? 1 : 0)) == 0) throw new Z3Exception("Exponent is not a 64 bit integer"); return result; } /// /// The exponent of a floating-point numeral as a bit-vector expression /// /// /// +oo, -oo and NaN's do not have a bit-vector exponent, so they are invalid arguments. /// public BitVecExpr ExponentBV(bool biased = true) { return new BitVecExpr(Context, Native.Z3_fpa_get_numeral_exponent_bv(Context.nCtx, NativeObject, (byte)(biased ? 1 : 0))); } /// /// Indicates whether the numeral is a NaN. /// public bool IsNaN { get { return Native.Z3_fpa_is_numeral_nan(Context.nCtx, NativeObject) != 0; } } /// /// Indicates whether the numeral is a +oo or -oo. /// public bool IsInf { get { return Native.Z3_fpa_is_numeral_inf(Context.nCtx, NativeObject) != 0; } } /// /// Indicates whether the numeral is +zero or -zero. /// public bool IsZero{ get { return Native.Z3_fpa_is_numeral_zero(Context.nCtx, NativeObject) != 0; } } /// /// Indicates whether the numeral is normal. /// public bool IsNormal { get { return Native.Z3_fpa_is_numeral_normal(Context.nCtx, NativeObject) != 0; } } /// /// Indicates whether the numeral is subnormal. /// public bool IsSubnormal { get { return Native.Z3_fpa_is_numeral_subnormal(Context.nCtx, NativeObject) != 0; } } /// /// Indicates whether the numeral is positive. /// public bool IsPositive { get { return Native.Z3_fpa_is_numeral_positive(Context.nCtx, NativeObject) != 0; } } /// /// Indicates whether the numeral is negative. /// public bool IsNegative { get { return Native.Z3_fpa_is_numeral_negative(Context.nCtx, NativeObject) != 0; } } #region Internal internal FPNum(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } #endregion /// /// Returns a string representation of the numeral. /// public override string ToString() { return Native.Z3_get_numeral_string(Context.nCtx, NativeObject); } } } z3-z3-4.8.7/src/api/dotnet/FPRMExpr.cs000066400000000000000000000013531356505360400172200ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: FPRMExpr.cs Abstract: Z3 Managed API: Floating Point Expressions over Rounding Modes Author: Christoph Wintersteiger (cwinter) 2013-06-10 Notes: --*/ using System.Diagnostics; using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Microsoft.Z3 { /// /// FloatingPoint RoundingMode Expressions /// public class FPRMExpr : Expr { #region Internal /// Constructor for FPRMExpr internal FPRMExpr(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } #endregion } } z3-z3-4.8.7/src/api/dotnet/FPRMNum.cs000066400000000000000000000071751356505360400170510ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: FPRMExpr.cs Abstract: Z3 Managed API: Floating Point Rounding Mode Numerals Author: Christoph Wintersteiger (cwinter) 2013-06-10 Notes: --*/ using System.Diagnostics; using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Microsoft.Z3 { /// /// Floating-point rounding mode numerals /// public class FPRMNum : FPRMExpr { /// /// Indicates whether the term is the floating-point rounding numeral roundNearestTiesToEven /// public bool isRoundNearestTiesToEven { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_RM_NEAREST_TIES_TO_EVEN; } } /// /// Indicates whether the term is the floating-point rounding numeral roundNearestTiesToEven /// public bool isRNE { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_RM_NEAREST_TIES_TO_EVEN; } } /// /// Indicates whether the term is the floating-point rounding numeral roundNearestTiesToAway /// public bool isRoundNearestTiesToAway { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_RM_NEAREST_TIES_TO_AWAY; } } /// /// Indicates whether the term is the floating-point rounding numeral roundNearestTiesToAway /// public bool isRNA { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_RM_NEAREST_TIES_TO_AWAY; } } /// /// Indicates whether the term is the floating-point rounding numeral roundTowardPositive /// public bool isRoundTowardPositive { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_RM_TOWARD_POSITIVE; } } /// /// Indicates whether the term is the floating-point rounding numeral roundTowardPositive /// public bool isRTP { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_RM_TOWARD_POSITIVE; } } /// /// Indicates whether the term is the floating-point rounding numeral roundTowardNegative /// public bool isRoundTowardNegative { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_RM_TOWARD_NEGATIVE; } } /// /// Indicates whether the term is the floating-point rounding numeral roundTowardNegative /// public bool isRTN { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_RM_TOWARD_NEGATIVE; } } /// /// Indicates whether the term is the floating-point rounding numeral roundTowardZero /// public bool isRoundTowardZero { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_RM_TOWARD_ZERO; } } /// /// Indicates whether the term is the floating-point rounding numeral roundTowardZero /// public bool isRTZ { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_RM_TOWARD_ZERO; } } /// /// Returns a string representation of the numeral. /// public override string ToString() { return Native.Z3_get_numeral_string(Context.nCtx, NativeObject); } #region Internal /// Constructor for FPRMNum internal FPRMNum(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } #endregion } } z3-z3-4.8.7/src/api/dotnet/FPRMSort.cs000066400000000000000000000013621356505360400172310ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: FPRMSort.cs Abstract: Z3 Managed API: Rounding Mode Sort Author: Christoph Wintersteiger (cwinter) 2013-06-10 Notes: --*/ using System.Diagnostics; using System; namespace Microsoft.Z3 { /// /// The FloatingPoint RoundingMode sort /// public class FPRMSort : Sort { #region Internal internal FPRMSort(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } internal FPRMSort(Context ctx) : base(ctx, Native.Z3_mk_fpa_rounding_mode_sort(ctx.nCtx)) { Debug.Assert(ctx != null); } #endregion } }z3-z3-4.8.7/src/api/dotnet/FPSort.cs000066400000000000000000000021541356505360400167720ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: FPSort.cs Abstract: Z3 Managed API: Floating Point Sorts Author: Christoph Wintersteiger (cwinter) 2013-06-10 Notes: --*/ using System.Diagnostics; using System; namespace Microsoft.Z3 { /// /// FloatingPoint sort /// public class FPSort : Sort { /// /// The number of exponent bits. /// public uint EBits { get { return Native.Z3_fpa_get_ebits(Context.nCtx, NativeObject); } } /// /// The number of significand bits. /// public uint SBits { get { return Native.Z3_fpa_get_sbits(Context.nCtx, NativeObject); } } #region Internal internal FPSort(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } internal FPSort(Context ctx, uint ebits, uint sbits) : base(ctx, Native.Z3_mk_fpa_sort(ctx.nCtx, ebits, sbits)) { Debug.Assert(ctx != null); } #endregion } } z3-z3-4.8.7/src/api/dotnet/FiniteDomainExpr.cs000066400000000000000000000012321356505360400210160ustar00rootroot00000000000000/*++ Copyright () 2012 Microsoft Corporation Module Name: FiniteDomainExpr.cs Abstract: Z3 Managed API: Finite-domain Expressions Author: Christoph Wintersteiger (cwinter) 2015-12-02 Notes: --*/ using System.Diagnostics; using System; namespace Microsoft.Z3 { /// /// Finite-domain expressions /// public class FiniteDomainExpr : Expr { #region Internal /// Constructor for DatatypeExpr internal FiniteDomainExpr(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } #endregion } } z3-z3-4.8.7/src/api/dotnet/FiniteDomainNum.cs000066400000000000000000000052561356505360400206510ustar00rootroot00000000000000/*++ Copyright () 2012 Microsoft Corporation Module Name: FiniteDomainNum.cs Abstract: Z3 Managed API: Finite-domain Numerals Author: Christoph Wintersteiger (cwinter) 2015-12-02 Notes: --*/ using System.Diagnostics; using System; #if !FRAMEWORK_LT_4 using System.Numerics; #endif namespace Microsoft.Z3 { /// /// Finite-domain numerals /// public class FiniteDomainNum : FiniteDomainExpr { /// /// Retrieve the 64-bit unsigned integer value. /// public UInt64 UInt64 { get { UInt64 res = 0; if (Native.Z3_get_numeral_uint64(Context.nCtx, NativeObject, ref res) == 0) throw new Z3Exception("Numeral is not a 64 bit unsigned"); return res; } } /// /// Retrieve the int value. /// public int Int { get { int res = 0; if (Native.Z3_get_numeral_int(Context.nCtx, NativeObject, ref res) == 0) throw new Z3Exception("Numeral is not an int"); return res; } } /// /// Retrieve the 64-bit int value. /// public Int64 Int64 { get { Int64 res = 0; if (Native.Z3_get_numeral_int64(Context.nCtx, NativeObject, ref res) == 0) throw new Z3Exception("Numeral is not an int64"); return res; } } /// /// Retrieve the int value. /// public uint UInt { get { uint res = 0; if (Native.Z3_get_numeral_uint(Context.nCtx, NativeObject, ref res) == 0) throw new Z3Exception("Numeral is not a uint"); return res; } } #if !FRAMEWORK_LT_4 /// /// Retrieve the BigInteger value. /// public BigInteger BigInteger { get { return BigInteger.Parse(this.ToString()); } } #endif /// /// Returns a string representation of the numeral. /// public override string ToString() { return Native.Z3_get_numeral_string(Context.nCtx, NativeObject); } #region Internal internal FiniteDomainNum(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } #endregion } } z3-z3-4.8.7/src/api/dotnet/FiniteDomainSort.cs000066400000000000000000000022531356505360400210330ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: FiniteDomainSort.cs Abstract: Z3 Managed API: Finite Domain Sorts Author: Christoph Wintersteiger (cwinter) 2012-11-23 Notes: --*/ using System.Diagnostics; using System; namespace Microsoft.Z3 { /// /// Finite domain sorts. /// public class FiniteDomainSort : Sort { /// /// The size of the finite domain sort. /// public ulong Size { get { ulong res = 0; Native.Z3_get_finite_domain_sort_size(Context.nCtx, NativeObject, ref res); return res; } } #region Internal internal FiniteDomainSort(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } internal FiniteDomainSort(Context ctx, Symbol name, ulong size) : base(ctx, Native.Z3_mk_finite_domain_sort(ctx.nCtx, name.NativeObject, size)) { Debug.Assert(ctx != null); Debug.Assert(name != null); } #endregion } } z3-z3-4.8.7/src/api/dotnet/Fixedpoint.cs000066400000000000000000000266211356505360400177330ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: Fixedpoints.cs Abstract: Z3 Managed API: Fixedpoints Author: Christoph Wintersteiger (cwinter) 2012-03-21 Notes: --*/ using System; using System.Diagnostics; using System.Linq; namespace Microsoft.Z3 { /// /// Object for managing fixedpoints /// public class Fixedpoint : Z3Object { /// /// A string that describes all available fixedpoint solver parameters. /// public string Help { get { return Native.Z3_fixedpoint_get_help(Context.nCtx, NativeObject); } } /// /// Sets the fixedpoint solver parameters. /// public Params Parameters { set { Debug.Assert(value != null); Context.CheckContextMatch(value); Native.Z3_fixedpoint_set_params(Context.nCtx, NativeObject, value.NativeObject); } } /// /// Retrieves parameter descriptions for Fixedpoint solver. /// public ParamDescrs ParameterDescriptions { get { return new ParamDescrs(Context, Native.Z3_fixedpoint_get_param_descrs(Context.nCtx, NativeObject)); } } /// /// Assert a constraint (or multiple) into the fixedpoint solver. /// public void Assert(params BoolExpr[] constraints) { Debug.Assert(constraints != null); Debug.Assert(constraints.All(c => c != null)); Context.CheckContextMatch(constraints); foreach (BoolExpr a in constraints) { Native.Z3_fixedpoint_assert(Context.nCtx, NativeObject, a.NativeObject); } } /// /// Alias for Assert. /// public void Add(params BoolExpr[] constraints) { Assert(constraints); } /// /// Register predicate as recursive relation. /// public void RegisterRelation(FuncDecl f) { Debug.Assert(f != null); Context.CheckContextMatch(f); Native.Z3_fixedpoint_register_relation(Context.nCtx, NativeObject, f.NativeObject); } /// /// Add rule into the fixedpoint solver. /// public void AddRule(BoolExpr rule, Symbol name = null) { Debug.Assert(rule != null); Context.CheckContextMatch(rule); Native.Z3_fixedpoint_add_rule(Context.nCtx, NativeObject, rule.NativeObject, AST.GetNativeObject(name)); } /// /// Add table fact to the fixedpoint solver. /// public void AddFact(FuncDecl pred, params uint[] args) { Debug.Assert(pred != null); Debug.Assert(args != null); Context.CheckContextMatch(pred); Native.Z3_fixedpoint_add_fact(Context.nCtx, NativeObject, pred.NativeObject, (uint)args.Length, args); } /// /// Query the fixedpoint solver. /// A query is a conjunction of constraints. The constraints may include the recursively defined relations. /// The query is satisfiable if there is an instance of the query variables and a derivation for it. /// The query is unsatisfiable if there are no derivations satisfying the query variables. /// public Status Query(BoolExpr query) { Debug.Assert(query != null); Context.CheckContextMatch(query); Z3_lbool r = (Z3_lbool)Native.Z3_fixedpoint_query(Context.nCtx, NativeObject, query.NativeObject); switch (r) { case Z3_lbool.Z3_L_TRUE: return Status.SATISFIABLE; case Z3_lbool.Z3_L_FALSE: return Status.UNSATISFIABLE; default: return Status.UNKNOWN; } } /// /// Query the fixedpoint solver. /// A query is an array of relations. /// The query is satisfiable if there is an instance of some relation that is non-empty. /// The query is unsatisfiable if there are no derivations satisfying any of the relations. /// public Status Query(params FuncDecl[] relations) { Debug.Assert(relations != null); Debug.Assert(relations.All(rel => rel != null)); Context.CheckContextMatch(relations); Z3_lbool r = (Z3_lbool)Native.Z3_fixedpoint_query_relations(Context.nCtx, NativeObject, AST.ArrayLength(relations), AST.ArrayToNative(relations)); switch (r) { case Z3_lbool.Z3_L_TRUE: return Status.SATISFIABLE; case Z3_lbool.Z3_L_FALSE: return Status.UNSATISFIABLE; default: return Status.UNKNOWN; } } /// /// Update named rule into in the fixedpoint solver. /// public void UpdateRule(BoolExpr rule, Symbol name) { Debug.Assert(rule != null); Context.CheckContextMatch(rule); Native.Z3_fixedpoint_update_rule(Context.nCtx, NativeObject, rule.NativeObject, AST.GetNativeObject(name)); } /// /// Retrieve satisfying instance or instances of solver, /// or definitions for the recursive predicates that show unsatisfiability. /// public Expr GetAnswer() { IntPtr ans = Native.Z3_fixedpoint_get_answer(Context.nCtx, NativeObject); return (ans == IntPtr.Zero) ? null : Expr.Create(Context, ans); } /// /// Retrieve explanation why fixedpoint engine returned status Unknown. /// public string GetReasonUnknown() { return Native.Z3_fixedpoint_get_reason_unknown(Context.nCtx, NativeObject); } /// /// Retrieve the number of levels explored for a given predicate. /// public uint GetNumLevels(FuncDecl predicate) { return Native.Z3_fixedpoint_get_num_levels(Context.nCtx, NativeObject, predicate.NativeObject); } /// /// Retrieve the cover of a predicate. /// public Expr GetCoverDelta(int level, FuncDecl predicate) { IntPtr res = Native.Z3_fixedpoint_get_cover_delta(Context.nCtx, NativeObject, level, predicate.NativeObject); return (res == IntPtr.Zero) ? null : Expr.Create(Context, res); } /// /// Add property about the predicate. /// The property is added at level. /// public void AddCover(int level, FuncDecl predicate, Expr property) { Native.Z3_fixedpoint_add_cover(Context.nCtx, NativeObject, level, predicate.NativeObject, property.NativeObject); } /// /// Retrieve internal string representation of fixedpoint object. /// public override string ToString() { return Native.Z3_fixedpoint_to_string(Context.nCtx, NativeObject, 0, null); } /// /// Instrument the Datalog engine on which table representation to use for recursive predicate. /// public void SetPredicateRepresentation(FuncDecl f, Symbol[] kinds) { Debug.Assert(f != null); Native.Z3_fixedpoint_set_predicate_representation(Context.nCtx, NativeObject, f.NativeObject, AST.ArrayLength(kinds), Symbol.ArrayToNative(kinds)); } /// /// Convert benchmark given as set of axioms, rules and queries to a string. /// public string ToString(params BoolExpr[] queries) { return Native.Z3_fixedpoint_to_string(Context.nCtx, NativeObject, AST.ArrayLength(queries), AST.ArrayToNative(queries)); } /// /// Retrieve set of rules added to fixedpoint context. /// public BoolExpr[] Rules { get { ASTVector av = new ASTVector(Context, Native.Z3_fixedpoint_get_rules(Context.nCtx, NativeObject)); return av.ToBoolExprArray(); } } /// /// Retrieve set of assertions added to fixedpoint context. /// public BoolExpr[] Assertions { get { ASTVector av = new ASTVector(Context, Native.Z3_fixedpoint_get_assertions(Context.nCtx, NativeObject)); return av.ToBoolExprArray(); } } /// /// Fixedpoint statistics. /// public Statistics Statistics { get { return new Statistics(Context, Native.Z3_fixedpoint_get_statistics(Context.nCtx, NativeObject)); } } /// /// Parse an SMT-LIB2 file with fixedpoint rules. /// Add the rules to the current fixedpoint context. /// Return the set of queries in the file. /// public BoolExpr[] ParseFile(string file) { ASTVector av = new ASTVector(Context, Native.Z3_fixedpoint_from_file(Context.nCtx, NativeObject, file)); return av.ToBoolExprArray(); } /// /// Similar to ParseFile. Instead it takes as argument a string. /// public BoolExpr[] ParseString(string s) { ASTVector av = new ASTVector(Context, Native.Z3_fixedpoint_from_string(Context.nCtx, NativeObject, s)); return av.ToBoolExprArray(); } #region Internal internal Fixedpoint(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } internal Fixedpoint(Context ctx) : base(ctx, Native.Z3_mk_fixedpoint(ctx.nCtx)) { Debug.Assert(ctx != null); } internal class DecRefQueue : IDecRefQueue { public DecRefQueue() : base() { } public DecRefQueue(uint move_limit) : base(move_limit) { } internal override void IncRef(Context ctx, IntPtr obj) { Native.Z3_fixedpoint_inc_ref(ctx.nCtx, obj); } internal override void DecRef(Context ctx, IntPtr obj) { Native.Z3_fixedpoint_dec_ref(ctx.nCtx, obj); } }; internal override void IncRef(IntPtr o) { Context.Fixedpoint_DRQ.IncAndClear(Context, o); base.IncRef(o); } internal override void DecRef(IntPtr o) { Context.Fixedpoint_DRQ.Add(o); base.DecRef(o); } #endregion } } z3-z3-4.8.7/src/api/dotnet/FuncDecl.cs000066400000000000000000000313721356505360400173040ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: FuncDecl.cs Abstract: Z3 Managed API: Function Declarations Author: Christoph Wintersteiger (cwinter) 2012-03-16 Notes: --*/ using System; using System.Diagnostics; using System.Linq; namespace Microsoft.Z3 { /// /// Function declarations. /// public class FuncDecl : AST { /// /// Comparison operator. /// /// True if and share the same context and are equal, false otherwise. public static bool operator ==(FuncDecl a, FuncDecl b) { return Object.ReferenceEquals(a, b) || (!Object.ReferenceEquals(a, null) && !Object.ReferenceEquals(b, null) && a.Context.nCtx == b.Context.nCtx && Native.Z3_is_eq_func_decl(a.Context.nCtx, a.NativeObject, b.NativeObject) != 0); } /// /// Comparison operator. /// /// True if and do not share the same context or are not equal, false otherwise. public static bool operator !=(FuncDecl a, FuncDecl b) { return !(a == b); } /// /// Object comparison. /// public override bool Equals(object o) { FuncDecl casted = o as FuncDecl; if (casted == null) return false; return this == casted; } /// /// A hash code. /// public override int GetHashCode() { return base.GetHashCode(); } /// /// A string representations of the function declaration. /// public override string ToString() { return Native.Z3_func_decl_to_string(Context.nCtx, NativeObject); } /// /// Returns a unique identifier for the function declaration. /// new public uint Id { get { return Native.Z3_get_func_decl_id(Context.nCtx, NativeObject); } } /// /// The arity of the function declaration /// public uint Arity { get { return Native.Z3_get_arity(Context.nCtx, NativeObject); } } /// /// The size of the domain of the function declaration /// /// public uint DomainSize { get { return Native.Z3_get_domain_size(Context.nCtx, NativeObject); } } /// /// The domain of the function declaration /// public Sort[] Domain { get { uint n = DomainSize; Sort[] res = new Sort[n]; for (uint i = 0; i < n; i++) res[i] = Sort.Create(Context, Native.Z3_get_domain(Context.nCtx, NativeObject, i)); return res; } } /// /// The range of the function declaration /// public Sort Range { get { return Sort.Create(Context, Native.Z3_get_range(Context.nCtx, NativeObject)); } } /// /// The kind of the function declaration. /// public Z3_decl_kind DeclKind { get { return (Z3_decl_kind)Native.Z3_get_decl_kind(Context.nCtx, NativeObject); } } /// /// The name of the function declaration /// public Symbol Name { get { return Symbol.Create(Context, Native.Z3_get_decl_name(Context.nCtx, NativeObject)); } } /// /// The number of parameters of the function declaration /// public uint NumParameters { get { return Native.Z3_get_decl_num_parameters(Context.nCtx, NativeObject); } } /// /// The parameters of the function declaration /// public Parameter[] Parameters { get { uint num = NumParameters; Parameter[] res = new Parameter[num]; for (uint i = 0; i < num; i++) { Z3_parameter_kind k = (Z3_parameter_kind)Native.Z3_get_decl_parameter_kind(Context.nCtx, NativeObject, i); switch (k) { case Z3_parameter_kind.Z3_PARAMETER_INT: res[i] = new Parameter(k, Native.Z3_get_decl_int_parameter(Context.nCtx, NativeObject, i)); break; case Z3_parameter_kind.Z3_PARAMETER_DOUBLE: res[i] = new Parameter(k, Native.Z3_get_decl_double_parameter(Context.nCtx, NativeObject, i)); break; case Z3_parameter_kind.Z3_PARAMETER_SYMBOL: res[i] = new Parameter(k, Symbol.Create(Context, Native.Z3_get_decl_symbol_parameter(Context.nCtx, NativeObject, i))); break; case Z3_parameter_kind.Z3_PARAMETER_SORT: res[i] = new Parameter(k, Sort.Create(Context, Native.Z3_get_decl_sort_parameter(Context.nCtx, NativeObject, i))); break; case Z3_parameter_kind.Z3_PARAMETER_AST: res[i] = new Parameter(k, new AST(Context, Native.Z3_get_decl_ast_parameter(Context.nCtx, NativeObject, i))); break; case Z3_parameter_kind.Z3_PARAMETER_FUNC_DECL: res[i] = new Parameter(k, new FuncDecl(Context, Native.Z3_get_decl_func_decl_parameter(Context.nCtx, NativeObject, i))); break; case Z3_parameter_kind.Z3_PARAMETER_RATIONAL: res[i] = new Parameter(k, Native.Z3_get_decl_rational_parameter(Context.nCtx, NativeObject, i)); break; default: throw new Z3Exception("Unknown function declaration parameter kind encountered"); } } return res; } } /// /// Function declarations can have Parameters associated with them. /// public class Parameter { readonly private Z3_parameter_kind kind; readonly private int i; readonly private double d; readonly private Symbol sym; readonly private Sort srt; readonly private AST ast; readonly private FuncDecl fd; readonly private string r; /// The int value of the parameter. public int Int { get { if (ParameterKind != Z3_parameter_kind.Z3_PARAMETER_INT) throw new Z3Exception("parameter is not an int"); return i; } } /// The double value of the parameter. public double Double { get { if (ParameterKind != Z3_parameter_kind.Z3_PARAMETER_DOUBLE) throw new Z3Exception("parameter is not a double "); return d; } } /// The Symbol value of the parameter. public Symbol Symbol { get { if (ParameterKind != Z3_parameter_kind.Z3_PARAMETER_SYMBOL) throw new Z3Exception("parameter is not a Symbol"); return sym; } } /// The Sort value of the parameter. public Sort Sort { get { if (ParameterKind != Z3_parameter_kind.Z3_PARAMETER_SORT) throw new Z3Exception("parameter is not a Sort"); return srt; } } /// The AST value of the parameter. public AST AST { get { if (ParameterKind != Z3_parameter_kind.Z3_PARAMETER_AST) throw new Z3Exception("parameter is not an AST"); return ast; } } /// The FunctionDeclaration value of the parameter. public FuncDecl FuncDecl { get { if (ParameterKind != Z3_parameter_kind.Z3_PARAMETER_FUNC_DECL) throw new Z3Exception("parameter is not a function declaration"); return fd; } } /// The rational string value of the parameter. public string Rational { get { if (ParameterKind != Z3_parameter_kind.Z3_PARAMETER_RATIONAL) throw new Z3Exception("parameter is not a rational string"); return r; } } /// /// The kind of the parameter. /// public Z3_parameter_kind ParameterKind { get { return kind; } } #region Internal internal Parameter(Z3_parameter_kind k, int i) { this.kind = k; this.i = i; } internal Parameter(Z3_parameter_kind k, double d) { this.kind = k; this.d = d; } internal Parameter(Z3_parameter_kind k, Symbol s) { this.kind = k; this.sym = s; } internal Parameter(Z3_parameter_kind k, Sort s) { this.kind = k; this.srt = s; } internal Parameter(Z3_parameter_kind k, AST a) { this.kind = k; this.ast = a; } internal Parameter(Z3_parameter_kind k, FuncDecl fd) { this.kind = k; this.fd = fd; } internal Parameter(Z3_parameter_kind k, string r) { this.kind = k; this.r = r; } #endregion } #region Internal internal FuncDecl(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } internal FuncDecl(Context ctx, Symbol name, Sort[] domain, Sort range) : base(ctx, Native.Z3_mk_func_decl(ctx.nCtx, name.NativeObject, AST.ArrayLength(domain), AST.ArrayToNative(domain), range.NativeObject)) { Debug.Assert(ctx != null); Debug.Assert(name != null); Debug.Assert(range != null); } internal FuncDecl(Context ctx, string prefix, Sort[] domain, Sort range) : base(ctx, Native.Z3_mk_fresh_func_decl(ctx.nCtx, prefix, AST.ArrayLength(domain), AST.ArrayToNative(domain), range.NativeObject)) { Debug.Assert(ctx != null); Debug.Assert(range != null); } internal FuncDecl(Context ctx, Symbol name, Sort[] domain, Sort range, bool is_rec) : base(ctx, Native.Z3_mk_rec_func_decl(ctx.nCtx, name.NativeObject, AST.ArrayLength(domain), AST.ArrayToNative(domain), range.NativeObject)) { Debug.Assert(ctx != null); Debug.Assert(name != null); Debug.Assert(range != null); } #if DEBUG internal override void CheckNativeObject(IntPtr obj) { if (Native.Z3_get_ast_kind(Context.nCtx, obj) != (uint)Z3_ast_kind.Z3_FUNC_DECL_AST) throw new Z3Exception("Underlying object is not a function declaration"); base.CheckNativeObject(obj); } #endif #endregion /// /// Translates (copies) the function declaration to the Context . /// /// A context /// A copy of the function declaration which is associated with new public FuncDecl Translate(Context ctx) { return (FuncDecl) base.Translate(ctx); } /// /// Create expression that applies function to arguments. /// /// /// public Expr this[params Expr[] args] { get { Debug.Assert(args == null || args.All(a => a != null)); return Apply(args); } } /// /// Create expression that applies function to arguments. /// /// /// public Expr Apply(params Expr[] args) { Debug.Assert(args == null || args.All(a => a != null)); Context.CheckContextMatch(args); return Expr.Create(Context, this, args); } } } z3-z3-4.8.7/src/api/dotnet/FuncInterp.cs000066400000000000000000000143271356505360400176770ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: FuncInterp.cs Abstract: Z3 Managed API: Function Interpretations Author: Christoph Wintersteiger (cwinter) 2012-03-21 Notes: --*/ using System.Diagnostics; using System; namespace Microsoft.Z3 { /// /// A function interpretation is represented as a finite map and an 'else' value. /// Each entry in the finite map represents the value of a function given a set of arguments. /// public class FuncInterp : Z3Object { /// /// An Entry object represents an element in the finite map used to encode /// a function interpretation. /// public class Entry : Z3Object { /// /// Return the (symbolic) value of this entry. /// public Expr Value { get { return Expr.Create(Context, Native.Z3_func_entry_get_value(Context.nCtx, NativeObject)); } } /// /// The number of arguments of the entry. /// public uint NumArgs { get { return Native.Z3_func_entry_get_num_args(Context.nCtx, NativeObject); } } /// /// The arguments of the function entry. /// public Expr[] Args { get { uint n = NumArgs; Expr[] res = new Expr[n]; for (uint i = 0; i < n; i++) res[i] = Expr.Create(Context, Native.Z3_func_entry_get_arg(Context.nCtx, NativeObject, i)); return res; } } /// /// A string representation of the function entry. /// public override string ToString() { uint n = NumArgs; string res = "["; Expr[] args = Args; for (uint i = 0; i < n; i++) res += args[i] + ", "; return res + Value + "]"; } #region Internal internal Entry(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } internal class DecRefQueue : IDecRefQueue { public DecRefQueue() : base() { } public DecRefQueue(uint move_limit) : base(move_limit) { } internal override void IncRef(Context ctx, IntPtr obj) { Native.Z3_func_entry_inc_ref(ctx.nCtx, obj); } internal override void DecRef(Context ctx, IntPtr obj) { Native.Z3_func_entry_dec_ref(ctx.nCtx, obj); } }; internal override void IncRef(IntPtr o) { Context.FuncEntry_DRQ.IncAndClear(Context, o); base.IncRef(o); } internal override void DecRef(IntPtr o) { Context.FuncEntry_DRQ.Add(o); base.DecRef(o); } #endregion }; /// /// The number of entries in the function interpretation. /// public uint NumEntries { get { return Native.Z3_func_interp_get_num_entries(Context.nCtx, NativeObject); } } /// /// The entries in the function interpretation /// public Entry[] Entries { get { uint n = NumEntries; Entry[] res = new Entry[n]; for (uint i = 0; i < n; i++) res[i] = new Entry(Context, Native.Z3_func_interp_get_entry(Context.nCtx, NativeObject, i)); return res; } } /// /// The (symbolic) `else' value of the function interpretation. /// public Expr Else { get { return Expr.Create(Context, Native.Z3_func_interp_get_else(Context.nCtx, NativeObject)); } } /// /// The arity of the function interpretation /// public uint Arity { get { return Native.Z3_func_interp_get_arity(Context.nCtx, NativeObject); } } /// /// A string representation of the function interpretation. /// public override string ToString() { string res = ""; res += "["; foreach (Entry e in Entries) { uint n = e.NumArgs; if (n > 1) res += "["; Expr[] args = e.Args; for (uint i = 0; i < n; i++) { if (i != 0) res += ", "; res += args[i]; } if (n > 1) res += "]"; res += " -> " + e.Value + ", "; } res += "else -> " + Else; res += "]"; return res; } #region Internal internal FuncInterp(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } internal class DecRefQueue : IDecRefQueue { public DecRefQueue() : base() { } public DecRefQueue(uint move_limit) : base(move_limit) { } internal override void IncRef(Context ctx, IntPtr obj) { Native.Z3_func_interp_inc_ref(ctx.nCtx, obj); } internal override void DecRef(Context ctx, IntPtr obj) { Native.Z3_func_interp_dec_ref(ctx.nCtx, obj); } }; internal override void IncRef(IntPtr o) { Context.FuncInterp_DRQ.IncAndClear(Context, o); base.IncRef(o); } internal override void DecRef(IntPtr o) { Context.FuncInterp_DRQ.Add(o); base.DecRef(o); } #endregion } } z3-z3-4.8.7/src/api/dotnet/Global.cs000066400000000000000000000100031356505360400170050ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: Global.cs Abstract: Z3 Managed API: Global Functions Author: Christoph Wintersteiger (cwinter) 2013-01-15 Notes: --*/ using System.Diagnostics; using System; using System.Runtime.InteropServices; namespace Microsoft.Z3 { /// /// Global functions for Z3. /// /// /// This (static) class contains functions that effect the behaviour of Z3 /// globally across contexts, etc. /// public static class Global { /// /// Set a global (or module) parameter, which is shared by all Z3 contexts. /// /// /// When a Z3 module is initialized it will use the value of these parameters /// when Z3_params objects are not provided. /// The name of parameter can be composed of characters [a-z][A-Z], digits [0-9], '-' and '_'. /// The character '.' is a delimiter (more later). /// The parameter names are case-insensitive. The character '-' should be viewed as an "alias" for '_'. /// Thus, the following parameter names are considered equivalent: "pp.decimal-precision" and "PP.DECIMAL_PRECISION". /// This function can be used to set parameters for a specific Z3 module. /// This can be done by using [module-name].[parameter-name]. /// For example: /// Z3_global_param_set('pp.decimal', 'true') /// will set the parameter "decimal" in the module "pp" to true. /// public static void SetParameter(string id, string value) { Native.Z3_global_param_set(id, value); } /// /// Get a global (or module) parameter. /// /// /// Returns null if the parameter does not exist. /// The caller must invoke #Z3_global_param_del_value to delete the value returned at \c param_value. /// This function cannot be invoked simultaneously from different threads without synchronization. /// The result string stored in param_value is stored in a shared location. /// public static string GetParameter(string id) { IntPtr t; if (Native.Z3_global_param_get(id, out t) == 0) return null; else return Marshal.PtrToStringAnsi(t); } /// /// Restore the value of all global (and module) parameters. /// /// /// This command will not affect already created objects (such as tactics and solvers) /// /// public static void ResetParameters() { Native.Z3_global_param_reset_all(); } /// /// Enable/disable printing of warning messages to the console. /// /// Note that this function is static and effects the behaviour of /// all contexts globally. public static void ToggleWarningMessages(bool enabled) { Native.Z3_toggle_warning_messages((byte)(enabled ? 1 : 0)); } /// /// Enable tracing messages tagged as `tag' when Z3 is compiled in debug mode. /// /// /// It is a NOOP otherwise. /// /// trace tag public static void EnableTrace(string tag) { Native.Z3_enable_trace(tag); } /// /// Disable tracing messages tagged as `tag' when Z3 is compiled in debug mode. /// /// /// It is a NOOP otherwise. /// /// trace tag public static void DisableTrace(string tag) { Native.Z3_disable_trace(tag); } } } z3-z3-4.8.7/src/api/dotnet/Goal.cs000066400000000000000000000211031356505360400164720ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: Goal.cs Abstract: Z3 Managed API: Goal Author: Christoph Wintersteiger (cwinter) 2012-03-21 Notes: --*/ using System; using System.Diagnostics; using System.Linq; namespace Microsoft.Z3 { /// /// A goal (aka problem). A goal is essentially a set /// of formulas, that can be solved and/or transformed using /// tactics and solvers. /// public class Goal : Z3Object { /// /// The precision of the goal. /// /// /// Goals can be transformed using over and under approximations. /// An under approximation is applied when the objective is to find a model for a given goal. /// An over approximation is applied when the objective is to find a proof for a given goal. /// public Z3_goal_prec Precision { get { return (Z3_goal_prec)Native.Z3_goal_precision(Context.nCtx, NativeObject); } } /// /// Indicates whether the goal is precise. /// public bool IsPrecise { get { return Precision == Z3_goal_prec.Z3_GOAL_PRECISE; } } /// /// Indicates whether the goal is an under-approximation. /// public bool IsUnderApproximation { get { return Precision == Z3_goal_prec.Z3_GOAL_UNDER; } } /// /// Indicates whether the goal is an over-approximation. /// public bool IsOverApproximation { get { return Precision == Z3_goal_prec.Z3_GOAL_OVER; } } /// /// Indicates whether the goal is garbage (i.e., the product of over- and under-approximations). /// public bool IsGarbage { get { return Precision == Z3_goal_prec.Z3_GOAL_UNDER_OVER; } } /// /// Adds the to the given goal. /// public void Assert(params BoolExpr[] constraints) { Debug.Assert(constraints != null); Debug.Assert(constraints.All(c => c != null)); Context.CheckContextMatch(constraints); foreach (BoolExpr c in constraints) { Debug.Assert(c != null); // It was an assume, now made an assert just to be sure we do not regress Native.Z3_goal_assert(Context.nCtx, NativeObject, c.NativeObject); } } /// /// Alias for Assert. /// public void Add(params BoolExpr[] constraints) { Assert(constraints); } /// /// Indicates whether the goal contains `false'. /// public bool Inconsistent { get { return Native.Z3_goal_inconsistent(Context.nCtx, NativeObject) != 0; } } /// /// The depth of the goal. /// /// /// This tracks how many transformations were applied to it. /// public uint Depth { get { return Native.Z3_goal_depth(Context.nCtx, NativeObject); } } /// /// Erases all formulas from the given goal. /// public void Reset() { Native.Z3_goal_reset(Context.nCtx, NativeObject); } /// /// The number of formulas in the goal. /// public uint Size { get { return Native.Z3_goal_size(Context.nCtx, NativeObject); } } /// /// The formulas in the goal. /// public BoolExpr[] Formulas { get { uint n = Size; BoolExpr[] res = new BoolExpr[n]; for (uint i = 0; i < n; i++) res[i] = (BoolExpr)Expr.Create(Context, Native.Z3_goal_formula(Context.nCtx, NativeObject, i)); return res; } } /// /// The number of formulas, subformulas and terms in the goal. /// public uint NumExprs { get { return Native.Z3_goal_num_exprs(Context.nCtx, NativeObject); } } /// /// Indicates whether the goal is empty, and it is precise or the product of an under approximation. /// public bool IsDecidedSat { get { return Native.Z3_goal_is_decided_sat(Context.nCtx, NativeObject) != 0; } } /// /// Indicates whether the goal contains `false', and it is precise or the product of an over approximation. /// public bool IsDecidedUnsat { get { return Native.Z3_goal_is_decided_unsat(Context.nCtx, NativeObject) != 0; } } /// /// Convert a model for the goal into a model of the /// original goal from which this goal was derived. /// /// A model for g public Model ConvertModel(Model m) { if (m != null) return new Model(Context, Native.Z3_goal_convert_model(Context.nCtx, NativeObject, m.NativeObject)); else return new Model(Context, Native.Z3_goal_convert_model(Context.nCtx, NativeObject, IntPtr.Zero)); } /// /// Translates (copies) the Goal to the target Context . /// public Goal Translate(Context ctx) { Debug.Assert(ctx != null); return new Goal(ctx, Native.Z3_goal_translate(Context.nCtx, NativeObject, ctx.nCtx)); } /// /// Simplifies the goal. /// /// Essentially invokes the `simplify' tactic on the goal. public Goal Simplify(Params p = null) { Tactic t = Context.MkTactic("simplify"); ApplyResult res = t.Apply(this, p); if (res.NumSubgoals == 0) throw new Z3Exception("No subgoals"); else return res.Subgoals[0]; } /// /// Goal to string conversion. /// /// A string representation of the Goal. public override string ToString() { return Native.Z3_goal_to_string(Context.nCtx, NativeObject); } /// /// Goal to DIMACS formatted string conversion. /// /// A string representation of the Goal. public string ToDimacs() { return Native.Z3_goal_to_dimacs_string(Context.nCtx, NativeObject); } /// /// Goal to BoolExpr conversion. /// /// A string representation of the Goal. public BoolExpr AsBoolExpr() { uint n = Size; if (n == 0) return Context.MkTrue(); else if (n == 1) return Formulas[0]; else { return Context.MkAnd(Formulas); } } #region Internal internal Goal(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } internal Goal(Context ctx, bool models, bool unsatCores, bool proofs) : base(ctx, Native.Z3_mk_goal(ctx.nCtx, (byte)(models ? 1 : 0), (byte)(unsatCores ? 1 : 0), (byte)(proofs ? 1 : 0))) { Debug.Assert(ctx != null); } internal class DecRefQueue : IDecRefQueue { public DecRefQueue() : base() { } public DecRefQueue(uint move_limit) : base(move_limit) { } internal override void IncRef(Context ctx, IntPtr obj) { Native.Z3_goal_inc_ref(ctx.nCtx, obj); } internal override void DecRef(Context ctx, IntPtr obj) { Native.Z3_goal_dec_ref(ctx.nCtx, obj); } }; internal override void IncRef(IntPtr o) { Context.Goal_DRQ.IncAndClear(Context, o); base.IncRef(o); } internal override void DecRef(IntPtr o) { Context.Goal_DRQ.Add(o); base.DecRef(o); } #endregion } } z3-z3-4.8.7/src/api/dotnet/IDecRefQueue.cs000066400000000000000000000043031356505360400200610ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: DecRefQueue.cs Abstract: Z3 Managed API: DecRef Queues Author: Christoph Wintersteiger (cwinter) 2012-03-16 Notes: --*/ using System.Diagnostics; using System; using System.Collections; using System.Collections.Generic; using System.Threading; namespace Microsoft.Z3 { /// /// DecRefQueue interface /// public abstract class IDecRefQueue { #region Object invariant private void ObjectInvariant() { Debug.Assert(this.m_queue != null); } #endregion readonly private Object m_lock = new Object(); readonly private List m_queue = new List(); private uint m_move_limit; internal IDecRefQueue(uint move_limit = 1024) { m_move_limit = move_limit; } /// /// Sets the limit on numbers of objects that are kept back at GC collection. /// /// public void SetLimit(uint l) { m_move_limit = l; } internal abstract void IncRef(Context ctx, IntPtr obj); internal abstract void DecRef(Context ctx, IntPtr obj); internal void IncAndClear(Context ctx, IntPtr o) { Debug.Assert(ctx != null); IncRef(ctx, o); if (m_queue.Count >= m_move_limit) Clear(ctx); } internal void Add(IntPtr o) { if (o == IntPtr.Zero) return; lock (m_lock) { m_queue.Add(o); } } internal void Clear(Context ctx) { Debug.Assert(ctx != null); lock (m_lock) { foreach (IntPtr o in m_queue) DecRef(ctx, o); m_queue.Clear(); } } } abstract class DecRefQueueContracts : IDecRefQueue { internal override void IncRef(Context ctx, IntPtr obj) { Debug.Assert(ctx != null); } internal override void DecRef(Context ctx, IntPtr obj) { Debug.Assert(ctx != null); } } } z3-z3-4.8.7/src/api/dotnet/IntExpr.cs000066400000000000000000000012661356505360400172110ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: IntExpr.cs Abstract: Z3 Managed API: Int Expressions Author: Christoph Wintersteiger (cwinter) 2012-11-23 Notes: --*/ using System.Diagnostics; using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Microsoft.Z3 { /// /// Int expressions /// public class IntExpr : ArithExpr { #region Internal /// Constructor for IntExpr internal IntExpr(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } #endregion } } z3-z3-4.8.7/src/api/dotnet/IntNum.cs000066400000000000000000000052561356505360400170350ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: IntNum.cs Abstract: Z3 Managed API: Int Numerals Author: Christoph Wintersteiger (cwinter) 2012-03-20 Notes: --*/ using System.Diagnostics; using System; #if !FRAMEWORK_LT_4 using System.Numerics; #endif namespace Microsoft.Z3 { /// /// Integer Numerals /// public class IntNum : IntExpr { #region Internal internal IntNum(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } #endregion /// /// Retrieve the 64-bit unsigned integer value. /// public UInt64 UInt64 { get { UInt64 res = 0; if (Native.Z3_get_numeral_uint64(Context.nCtx, NativeObject, ref res) == 0) throw new Z3Exception("Numeral is not a 64 bit unsigned"); return res; } } /// /// Retrieve the int value. /// public int Int { get { int res = 0; if (Native.Z3_get_numeral_int(Context.nCtx, NativeObject, ref res) == 0) throw new Z3Exception("Numeral is not an int"); return res; } } /// /// Retrieve the 64-bit int value. /// public Int64 Int64 { get { Int64 res = 0; if (Native.Z3_get_numeral_int64(Context.nCtx, NativeObject, ref res) == 0) throw new Z3Exception("Numeral is not an int64"); return res; } } /// /// Retrieve the int value. /// public uint UInt { get { uint res = 0; if (Native.Z3_get_numeral_uint(Context.nCtx, NativeObject, ref res) == 0) throw new Z3Exception("Numeral is not a uint"); return res; } } #if !FRAMEWORK_LT_4 /// /// Retrieve the BigInteger value. /// public BigInteger BigInteger { get { return BigInteger.Parse(this.ToString()); } } #endif /// /// Returns a string representation of the numeral. /// public override string ToString() { return Native.Z3_get_numeral_string(Context.nCtx, NativeObject); } } } z3-z3-4.8.7/src/api/dotnet/IntSort.cs000066400000000000000000000013121356505360400172120ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: IntSort.cs Abstract: Z3 Managed API: Int Sorts Author: Christoph Wintersteiger (cwinter) 2012-11-23 Notes: --*/ using System.Diagnostics; using System; namespace Microsoft.Z3 { /// /// An Integer sort /// public class IntSort : ArithSort { #region Internal internal IntSort(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } internal IntSort(Context ctx) : base(ctx, Native.Z3_mk_int_sort(ctx.nCtx)) { Debug.Assert(ctx != null); } #endregion } } z3-z3-4.8.7/src/api/dotnet/IntSymbol.cs000066400000000000000000000030121356505360400175270ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: IntSymbol.cs Abstract: Z3 Managed API: Int Symbols Author: Christoph Wintersteiger (cwinter) 2012-11-23 Notes: --*/ using System; using System.Diagnostics; using System.Runtime.InteropServices; namespace Microsoft.Z3 { /// /// Numbered symbols /// public class IntSymbol : Symbol { /// /// The int value of the symbol. /// /// Throws an exception if the symbol is not of int kind. public int Int { get { if (!IsIntSymbol()) throw new Z3Exception("Int requested from non-Int symbol"); return Native.Z3_get_symbol_int(Context.nCtx, NativeObject); } } #region Internal internal IntSymbol(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } internal IntSymbol(Context ctx, int i) : base(ctx, Native.Z3_mk_int_symbol(ctx.nCtx, i)) { Debug.Assert(ctx != null); } #if DEBUG internal override void CheckNativeObject(IntPtr obj) { if ((Z3_symbol_kind)Native.Z3_get_symbol_kind(Context.nCtx, obj) != Z3_symbol_kind.Z3_INT_SYMBOL) throw new Z3Exception("Symbol is not of integer kind"); base.CheckNativeObject(obj); } #endif #endregion } } z3-z3-4.8.7/src/api/dotnet/Lambda.cs000066400000000000000000000103651356505360400170000ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Lambda.cs Abstract: Z3 Managed API: Lambda Author: Christoph Wintersteiger (cwinter) 2012-03-19 Notes: --*/ using System; using System.Diagnostics; using System.Linq; namespace Microsoft.Z3 { /// /// Lambda expressions. /// public class Lambda : ArrayExpr { /// /// The number of bound variables. /// public uint NumBound { get { return Native.Z3_get_quantifier_num_bound(Context.nCtx, NativeObject); } } /// /// The symbols for the bound variables. /// public Symbol[] BoundVariableNames { get { uint n = NumBound; Symbol[] res = new Symbol[n]; for (uint i = 0; i < n; i++) res[i] = Symbol.Create(Context, Native.Z3_get_quantifier_bound_name(Context.nCtx, NativeObject, i)); return res; } } /// /// The sorts of the bound variables. /// public Sort[] BoundVariableSorts { get { uint n = NumBound; Sort[] res = new Sort[n]; for (uint i = 0; i < n; i++) res[i] = Sort.Create(Context, Native.Z3_get_quantifier_bound_sort(Context.nCtx, NativeObject, i)); return res; } } /// /// The body of the lambda. /// public Expr Body { get { return Expr.Create(Context, Native.Z3_get_quantifier_body(Context.nCtx, NativeObject)); } } /// /// Translates (copies) the lambda to the Context . /// /// A context /// A copy of the lambda which is associated with new public Lambda Translate(Context ctx) { return (Lambda)base.Translate(ctx); } #region Internal internal Lambda(Context ctx, Sort[] sorts, Symbol[] names, Expr body) : base(ctx, IntPtr.Zero) { Debug.Assert(ctx != null); Debug.Assert(sorts != null); Debug.Assert(names != null); Debug.Assert(body != null); Debug.Assert(sorts.Length == names.Length); Debug.Assert(sorts.All(s => s != null)); Debug.Assert(names.All(n => n != null)); Context.CheckContextMatch(sorts); Context.CheckContextMatch(names); Context.CheckContextMatch(body); if (sorts.Length != names.Length) throw new Z3Exception("Number of sorts does not match number of names"); NativeObject = Native.Z3_mk_lambda(ctx.nCtx, AST.ArrayLength(sorts), AST.ArrayToNative(sorts), Symbol.ArrayToNative(names), body.NativeObject); } internal Lambda(Context ctx, Expr[] bound, Expr body) : base(ctx, IntPtr.Zero) { Debug.Assert(ctx != null); Debug.Assert(body != null); Debug.Assert(bound != null && bound.Length > 0 && bound.All(n => n != null)); Context.CheckContextMatch(bound); Context.CheckContextMatch(body); NativeObject = Native.Z3_mk_lambda_const(ctx.nCtx, AST.ArrayLength(bound), AST.ArrayToNative(bound), body.NativeObject); } internal Lambda(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } #if DEBUG internal override void CheckNativeObject(IntPtr obj) { if ((Z3_ast_kind)Native.Z3_get_ast_kind(Context.nCtx, obj) != Z3_ast_kind.Z3_QUANTIFIER_AST) throw new Z3Exception("Underlying object is not a quantifier"); base.CheckNativeObject(obj); } #endif #endregion } } z3-z3-4.8.7/src/api/dotnet/ListSort.cs000066400000000000000000000064121356505360400174010ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: ListSort.cs Abstract: Z3 Managed API: List Sorts Author: Christoph Wintersteiger (cwinter) 2012-11-23 Notes: --*/ using System.Diagnostics; using System; namespace Microsoft.Z3 { /// /// List sorts. /// public class ListSort : Sort { /// /// The declaration of the nil function of this list sort. /// public FuncDecl NilDecl { get { return new FuncDecl(Context, Native.Z3_get_datatype_sort_constructor(Context.nCtx, NativeObject, 0)); } } /// /// The empty list. /// public Expr Nil { get { return Context.MkApp(NilDecl); } } /// /// The declaration of the isNil function of this list sort. /// public FuncDecl IsNilDecl { get { return new FuncDecl(Context, Native.Z3_get_datatype_sort_recognizer(Context.nCtx, NativeObject, 0)); } } /// /// The declaration of the cons function of this list sort. /// public FuncDecl ConsDecl { get { return new FuncDecl(Context, Native.Z3_get_datatype_sort_constructor(Context.nCtx, NativeObject, 1)); } } /// /// The declaration of the isCons function of this list sort. /// /// public FuncDecl IsConsDecl { get { return new FuncDecl(Context, Native.Z3_get_datatype_sort_recognizer(Context.nCtx, NativeObject, 1)); } } /// /// The declaration of the head function of this list sort. /// public FuncDecl HeadDecl { get { return new FuncDecl(Context, Native.Z3_get_datatype_sort_constructor_accessor(Context.nCtx, NativeObject, 1, 0)); } } /// /// The declaration of the tail function of this list sort. /// public FuncDecl TailDecl { get { return new FuncDecl(Context, Native.Z3_get_datatype_sort_constructor_accessor(Context.nCtx, NativeObject, 1, 1)); } } #region Internal internal ListSort(Context ctx, Symbol name, Sort elemSort) : base(ctx, IntPtr.Zero) { Debug.Assert(ctx != null); Debug.Assert(name != null); Debug.Assert(elemSort != null); IntPtr inil = IntPtr.Zero, iisnil = IntPtr.Zero, icons = IntPtr.Zero, iiscons = IntPtr.Zero, ihead = IntPtr.Zero, itail = IntPtr.Zero; NativeObject = Native.Z3_mk_list_sort(ctx.nCtx, name.NativeObject, elemSort.NativeObject, ref inil, ref iisnil, ref icons, ref iiscons, ref ihead, ref itail); } #endregion }; } z3-z3-4.8.7/src/api/dotnet/Log.cs000066400000000000000000000034711356505360400163410ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: Log.cs Abstract: Z3 Managed API: Log Author: Christoph Wintersteiger (cwinter) 2012-03-15 Notes: --*/ using System.Diagnostics; using System; namespace Microsoft.Z3 { /// /// Interaction logging for Z3. /// /// /// Note that this is a global, static log and if multiple Context /// objects are created, it logs the interaction with all of them. /// public static class Log { private static bool m_is_open = false; /// /// Open an interaction log file. /// /// the name of the file to open /// True if opening the log file succeeds, false otherwise. public static bool Open(string filename) { m_is_open = true; return Native.Z3_open_log(filename) == 1; } /// /// Closes the interaction log. /// public static void Close() { m_is_open = false; Native.Z3_close_log(); } /// /// Appends the user-provided string to the interaction log. /// public static void Append(string s) { Debug.Assert(isOpen()); if (!m_is_open) throw new Z3Exception("Log cannot be closed."); Native.Z3_append_log(s); } /// /// Checks whether the interaction log is opened. /// /// True if the interaction log is open, false otherwise. public static bool isOpen() { return m_is_open; } } } z3-z3-4.8.7/src/api/dotnet/Microsoft.Z3.Sharp.pc.in000066400000000000000000000003211356505360400215250ustar00rootroot00000000000000prefix=@PREFIX@ assemblies_dir=${prefix}/lib/mono/@GAC_PKG_NAME@ Name: @GAC_PKG_NAME@ Description: .NET bindings for The Microsoft Z3 SMT solver Version: @VERSION@ Libs: -r:${assemblies_dir}/Microsoft.Z3.dll z3-z3-4.8.7/src/api/dotnet/Microsoft.Z3.csproj.in000066400000000000000000000072061356505360400213600ustar00rootroot00000000000000 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_TWEAK@ @VER_MAJOR@.@VER_MINOR@.@VER_BUILD@.@VER_TWEAK@ @VER_MAJOR@.@VER_MINOR@.@VER_BUILD@.@VER_TWEAK@ @VER_MAJOR@.@VER_MINOR@.@VER_BUILD@.@VER_TWEAK@ ${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 z3-z3-4.8.7/src/api/dotnet/Microsoft.Z3.props000066400000000000000000000030071356505360400206110ustar00rootroot00000000000000 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 z3-z3-4.8.7/src/api/dotnet/Microsoft.Z3.snk000066400000000000000000000011241356505360400202370ustar00rootroot00000000000000$RSA2w Ô‡³“‘So5߯ç‚ÔÌ6˜¿Q7)Öet?§çh ñ¢H*|KlÄÄgÉð’÷UèÅ_éd ..âaNàI¢«5\2‰}A sÚ\:û½ýo6ÃÕÙ9:Þÿï¹¥ÉV¤¡Î}ú%›;g@£ÆýX¥/³®2¡Þ[ñ_ˆ%IÜ%ü’>Š| ºŸN;÷jêVW»¸t… :D‡.ü*o2ÿŠTzÜ0{Ñ[K õÂæD´21náXßU**’kk»¨ªnM4}†Ñw–³6}››yØõ$ËÈ3IR¯Sç/šï˜ÂDL&µe펊®9tÝæß‘‰šò÷ÃzÚîe÷º„æÎïߊ Žåc˜/ÁjóbìUsT" ?>bP¯ ,”U‹9eõñ¿b=y¨oMi‘÷hla|7¨‡lOô_²ö_]XŠSÕ'Ë‹Â8gâºEa‡=‚KeV8Û™BM7ó^8µÍûocu³oøÕêÂ'~¼Àï–ÆŽWéÙVEMp¶>jå,]lq¸?ý§ „¥Z|]!ÿ¼Ú‹M¨##àá^½–«2¾_8µîvûâÙàÐvëöQ­Hs;òЪ!¶ŒóG·Ü!0z3-z3-4.8.7/src/api/dotnet/Microsoft.Z3.targets000066400000000000000000000007351356505360400211240ustar00rootroot00000000000000 %(RecursiveDir)%(FileName)%(Extension) PreserveNewest z3-z3-4.8.7/src/api/dotnet/Microsoft.Z3.targets.in000066400000000000000000000006471356505360400215330ustar00rootroot00000000000000 false libz3.dll PreserveNewest z3-z3-4.8.7/src/api/dotnet/Model.cs000066400000000000000000000270071356505360400166610ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: Model.cs Abstract: Z3 Managed API: Models Author: Christoph Wintersteiger (cwinter) 2012-03-21 Notes: --*/ using System; using System.Diagnostics; using System.Collections.Generic; namespace Microsoft.Z3 { /// /// A Model contains interpretations (assignments) of constants and functions. /// public class Model : Z3Object { /// /// Retrieves the interpretation (the assignment) of in the model. /// /// A Constant /// An expression if the constant has an interpretation in the model, null otherwise. public Expr ConstInterp(Expr a) { Debug.Assert(a != null); Context.CheckContextMatch(a); return ConstInterp(a.FuncDecl); } /// /// Retrieves the interpretation (the assignment) of in the model. /// /// A function declaration of zero arity /// An expression if the function has an interpretation in the model, null otherwise. public Expr ConstInterp(FuncDecl f) { Debug.Assert(f != null); Context.CheckContextMatch(f); if (f.Arity != 0) throw new Z3Exception("Non-zero arity functions have FunctionInterpretations as a model. Use FuncInterp."); IntPtr n = Native.Z3_model_get_const_interp(Context.nCtx, NativeObject, f.NativeObject); if (n == IntPtr.Zero) return null; else return Expr.Create(Context, n); } /// /// Retrieves the interpretation (the assignment) of a non-constant in the model. /// /// A function declaration of non-zero arity /// A FunctionInterpretation if the function has an interpretation in the model, null otherwise. public FuncInterp FuncInterp(FuncDecl f) { Debug.Assert(f != null); Context.CheckContextMatch(f); Z3_sort_kind sk = (Z3_sort_kind)Native.Z3_get_sort_kind(Context.nCtx, Native.Z3_get_range(Context.nCtx, f.NativeObject)); if (f.Arity == 0) { IntPtr n = Native.Z3_model_get_const_interp(Context.nCtx, NativeObject, f.NativeObject); if (sk == Z3_sort_kind.Z3_ARRAY_SORT) { if (n == IntPtr.Zero) return null; else { if (Native.Z3_is_as_array(Context.nCtx, n) == 0) throw new Z3Exception("Argument was not an array constant"); IntPtr fd = Native.Z3_get_as_array_func_decl(Context.nCtx, n); return FuncInterp(new FuncDecl(Context, fd)); } } else { throw new Z3Exception("Constant functions do not have a function interpretation; use ConstInterp"); } } else { IntPtr n = Native.Z3_model_get_func_interp(Context.nCtx, NativeObject, f.NativeObject); if (n == IntPtr.Zero) return null; else return new FuncInterp(Context, n); } } /// /// The number of constants that have an interpretation in the model. /// public uint NumConsts { get { return Native.Z3_model_get_num_consts(Context.nCtx, NativeObject); } } /// /// The function declarations of the constants in the model. /// public FuncDecl[] ConstDecls { get { uint n = NumConsts; FuncDecl[] res = new FuncDecl[n]; for (uint i = 0; i < n; i++) res[i] = new FuncDecl(Context, Native.Z3_model_get_const_decl(Context.nCtx, NativeObject, i)); return res; } } /// /// Enumerate constants in model. /// public IEnumerable> Consts { get { uint nc = NumConsts; for (uint i = 0; i < nc; ++i) { var f = new FuncDecl(Context, Native.Z3_model_get_const_decl(Context.nCtx, NativeObject, i)); IntPtr n = Native.Z3_model_get_const_interp(Context.nCtx, NativeObject, f.NativeObject); if (n == IntPtr.Zero) continue; yield return new KeyValuePair(f, Expr.Create(Context, n)); } } } /// /// The number of function interpretations in the model. /// public uint NumFuncs { get { return Native.Z3_model_get_num_funcs(Context.nCtx, NativeObject); } } /// /// The function declarations of the function interpretations in the model. /// public FuncDecl[] FuncDecls { get { uint n = NumFuncs; FuncDecl[] res = new FuncDecl[n]; for (uint i = 0; i < n; i++) res[i] = new FuncDecl(Context, Native.Z3_model_get_func_decl(Context.nCtx, NativeObject, i)); return res; } } /// /// All symbols that have an interpretation in the model. /// public FuncDecl[] Decls { get { uint nFuncs = NumFuncs; uint nConsts = NumConsts; uint n = nFuncs + nConsts; FuncDecl[] res = new FuncDecl[n]; for (uint i = 0; i < nConsts; i++) res[i] = new FuncDecl(Context, Native.Z3_model_get_const_decl(Context.nCtx, NativeObject, i)); for (uint i = 0; i < nFuncs; i++) res[nConsts + i] = new FuncDecl(Context, Native.Z3_model_get_func_decl(Context.nCtx, NativeObject, i)); return res; } } /// /// A ModelEvaluationFailedException is thrown when an expression cannot be evaluated by the model. /// public class ModelEvaluationFailedException : Z3Exception { /// /// An exception that is thrown when model evaluation fails. /// public ModelEvaluationFailedException() : base() { } } /// /// Evaluates the expression in the current model. /// /// /// This function may fail if contains quantifiers, /// is partial (MODEL_PARTIAL enabled), or if is not well-sorted. /// In this case a ModelEvaluationFailedException is thrown. /// /// An expression /// /// When this flag is enabled, a model value will be assigned to any constant /// or function that does not have an interpretation in the model. /// /// The evaluation of in the model. public Expr Eval(Expr t, bool completion = false) { Debug.Assert(t != null); IntPtr v = IntPtr.Zero; if (Native.Z3_model_eval(Context.nCtx, NativeObject, t.NativeObject, (byte)(completion ? 1 : 0), ref v) == (byte)0) throw new ModelEvaluationFailedException(); else return Expr.Create(Context, v); } /// /// Alias for Eval. /// public Expr Evaluate(Expr t, bool completion = false) { Debug.Assert(t != null); return Eval(t, completion); } /// /// Evaluate expression to a double, assuming it is a numeral already. /// public double Double(Expr t) { var r = Eval(t, true); return Native.Z3_get_numeral_double(Context.nCtx, r.NativeObject); } /// /// The number of uninterpreted sorts that the model has an interpretation for. /// public uint NumSorts { get { return Native.Z3_model_get_num_sorts(Context.nCtx, NativeObject); } } /// /// The uninterpreted sorts that the model has an interpretation for. /// /// /// Z3 also provides an interpretation for uninterpreted sorts used in a formula. /// The interpretation for a sort is a finite set of distinct values. We say this finite set is /// the "universe" of the sort. /// /// /// public Sort[] Sorts { get { uint n = NumSorts; Sort[] res = new Sort[n]; for (uint i = 0; i < n; i++) res[i] = Sort.Create(Context, Native.Z3_model_get_sort(Context.nCtx, NativeObject, i)); return res; } } /// /// The finite set of distinct values that represent the interpretation for sort . /// /// /// An uninterpreted sort /// An array of expressions, where each is an element of the universe of public Expr[] SortUniverse(Sort s) { Debug.Assert(s != null); ASTVector av = new ASTVector(Context, Native.Z3_model_get_sort_universe(Context.nCtx, NativeObject, s.NativeObject)); return av.ToExprArray(); } /// /// Conversion of models to strings. /// /// A string representation of the model. public override string ToString() { return Native.Z3_model_to_string(Context.nCtx, NativeObject); } #region Internal internal Model(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } internal class DecRefQueue : IDecRefQueue { public DecRefQueue() : base() { } public DecRefQueue(uint move_limit) : base(move_limit) { } internal override void IncRef(Context ctx, IntPtr obj) { Native.Z3_model_inc_ref(ctx.nCtx, obj); } internal override void DecRef(Context ctx, IntPtr obj) { Native.Z3_model_dec_ref(ctx.nCtx, obj); } }; internal override void IncRef(IntPtr o) { Context.Model_DRQ.IncAndClear(Context, o); base.IncRef(o); } internal override void DecRef(IntPtr o) { Context.Model_DRQ.Add(o); base.DecRef(o); } #endregion } } z3-z3-4.8.7/src/api/dotnet/Optimize.cs000066400000000000000000000350261356505360400174210ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: Optimize.cs Abstract: Z3 Managed API: Optimizes Author: Nikolaj Bjorner (nbjorner) 2013-12-03 Notes: --*/ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; namespace Microsoft.Z3 { /// /// Object for managing optimization context /// public class Optimize : Z3Object { /// /// A string that describes all available optimize solver parameters. /// public string Help { get { return Native.Z3_optimize_get_help(Context.nCtx, NativeObject); } } /// /// Sets the optimize solver parameters. /// public Params Parameters { set { Debug.Assert(value != null); Context.CheckContextMatch(value); Native.Z3_optimize_set_params(Context.nCtx, NativeObject, value.NativeObject); } } /// /// Sets parameter on the optimize solver /// public void Set(string name, bool value) { Parameters = Context.MkParams().Add(name, value); } /// /// Sets parameter on the optimize solver /// public void Set(string name, uint value) { Parameters = Context.MkParams().Add(name, value); } /// /// Sets parameter on the optimize solver /// public void Set(string name, double value) { Parameters = Context.MkParams().Add(name, value); } /// /// Sets parameter on the optimize solver /// public void Set(string name, string value) { Parameters = Context.MkParams().Add(name, value); } /// /// Sets parameter on the optimize solver /// public void Set(string name, Symbol value) { Parameters = Context.MkParams().Add(name, value); } /// /// Sets parameter on the optimize solver /// public void Set(Symbol name, bool value) { Parameters = Context.MkParams().Add(name, value); } /// /// Sets parameter on the optimize solver /// public void Set(Symbol name, uint value) { Parameters = Context.MkParams().Add(name, value); } /// /// Sets parameter on the optimize solver /// public void Set(Symbol name, double value) { Parameters = Context.MkParams().Add(name, value); } /// /// Sets parameter on the optimize solver /// public void Set(Symbol name, string value) { Parameters = Context.MkParams().Add(name, value); } /// /// Sets parameter on the optimize solver /// public void Set(Symbol name, Symbol value) { Parameters = Context.MkParams().Add(name, value); } /// /// Retrieves parameter descriptions for Optimize solver. /// public ParamDescrs ParameterDescriptions { get { return new ParamDescrs(Context, Native.Z3_optimize_get_param_descrs(Context.nCtx, NativeObject)); } } /// /// Assert a constraint (or multiple) into the optimize solver. /// public void Assert(params BoolExpr[] constraints) { AddConstraints(constraints); } /// /// Assert a constraint (or multiple) into the optimize solver. /// public void Assert(IEnumerable constraints) { AddConstraints(constraints); } /// /// Alias for Assert. /// public void Add(params BoolExpr[] constraints) { AddConstraints(constraints); } /// /// Alias for Assert. /// public void Add(IEnumerable constraints) { AddConstraints(constraints); } /// /// Assert a constraint (or multiple) into the optimize solver. /// private void AddConstraints(IEnumerable constraints) { Debug.Assert(constraints != null); Debug.Assert(constraints.All(c => c != null)); Context.CheckContextMatch(constraints); foreach (BoolExpr a in constraints) { Native.Z3_optimize_assert(Context.nCtx, NativeObject, a.NativeObject); } } /// /// Handle to objectives returned by objective functions. /// public class Handle { Optimize opt; uint handle; internal Handle(Optimize opt, uint h) { this.opt = opt; this.handle = h; } /// /// Retrieve a lower bound for the objective handle. /// public Expr Lower { get { return opt.GetLower(handle); } } /// /// Retrieve an upper bound for the objective handle. /// public Expr Upper { get { return opt.GetUpper(handle); } } /// /// Retrieve the value of an objective. /// public Expr Value { get { return Lower; } } /// /// Retrieve a lower bound for the objective handle. /// public Expr[] LowerAsVector { get { return opt.GetLowerAsVector(handle); } } /// /// Retrieve an upper bound for the objective handle. /// public Expr[] UpperAsVector { get { return opt.GetUpperAsVector(handle); } } } /// /// Assert soft constraint /// /// /// Return an objective which associates with the group of constraints. /// public Handle AssertSoft(BoolExpr constraint, uint weight, string group) { Context.CheckContextMatch(constraint); Symbol s = Context.MkSymbol(group); return new Handle(this, Native.Z3_optimize_assert_soft(Context.nCtx, NativeObject, constraint.NativeObject, weight.ToString(), s.NativeObject)); } /// /// Check satisfiability of asserted constraints. /// Produce a model that (when the objectives are bounded and /// don't use strict inequalities) meets the objectives. /// /// public Status Check(params Expr[] assumptions) { Z3_lbool r = (Z3_lbool)Native.Z3_optimize_check(Context.nCtx, NativeObject, (uint)assumptions.Length, AST.ArrayToNative(assumptions)); switch (r) { case Z3_lbool.Z3_L_TRUE: return Status.SATISFIABLE; case Z3_lbool.Z3_L_FALSE: return Status.UNSATISFIABLE; default: return Status.UNKNOWN; } } /// /// Creates a backtracking point. /// /// public void Push() { Native.Z3_optimize_push(Context.nCtx, NativeObject); } /// /// Backtrack one backtracking point. /// /// Note that an exception is thrown if Pop is called without a corresponding Push /// public void Pop() { Native.Z3_optimize_pop(Context.nCtx, NativeObject); } /// /// The model of the last Check. /// /// /// The result is null if Check was not invoked before, /// if its results was not SATISFIABLE, or if model production is not enabled. /// public Model Model { get { IntPtr x = Native.Z3_optimize_get_model(Context.nCtx, NativeObject); if (x == IntPtr.Zero) return null; else return new Model(Context, x); } } /// /// The unsat core of the last Check. /// /// /// The unsat core is a subset of assumptions /// The result is empty if Check was not invoked before, /// if its results was not UNSATISFIABLE, or if core production is disabled. /// public BoolExpr[] UnsatCore { get { ASTVector core = new ASTVector(Context, Native.Z3_optimize_get_unsat_core(Context.nCtx, NativeObject)); return core.ToBoolExprArray(); } } /// /// Declare an arithmetical maximization objective. /// Return a handle to the objective. The handle is used as /// to retrieve the values of objectives after calling Check. /// The expression can be either an arithmetical expression or bit-vector. /// public Handle MkMaximize(Expr e) { return new Handle(this, Native.Z3_optimize_maximize(Context.nCtx, NativeObject, e.NativeObject)); } /// /// Declare an arithmetical minimization objective. /// Similar to MkMaximize. /// public Handle MkMinimize(Expr e) { return new Handle(this, Native.Z3_optimize_minimize(Context.nCtx, NativeObject, e.NativeObject)); } /// /// Retrieve a lower bound for the objective handle. /// private Expr GetLower(uint index) { return Expr.Create(Context, Native.Z3_optimize_get_lower(Context.nCtx, NativeObject, index)); } /// /// Retrieve an upper bound for the objective handle. /// private Expr GetUpper(uint index) { return Expr.Create(Context, Native.Z3_optimize_get_upper(Context.nCtx, NativeObject, index)); } /// /// Retrieve a lower bound for the objective handle. /// private Expr[] GetLowerAsVector(uint index) { ASTVector v = new ASTVector(Context, Native.Z3_optimize_get_lower_as_vector(Context.nCtx, NativeObject, index)); return v.ToExprArray(); } /// /// Retrieve an upper bound for the objective handle. /// private Expr[] GetUpperAsVector(uint index) { ASTVector v = new ASTVector(Context, Native.Z3_optimize_get_upper_as_vector(Context.nCtx, NativeObject, index)); return v.ToExprArray(); } /// /// Return a string the describes why the last to check returned unknown /// public String ReasonUnknown { get { return Native.Z3_optimize_get_reason_unknown(Context.nCtx, NativeObject); } } /// /// Print the context to a string (SMT-LIB parseable benchmark). /// public override string ToString() { return Native.Z3_optimize_to_string(Context.nCtx, NativeObject); } /// /// Parse an SMT-LIB2 file with optimization objectives and constraints. /// The parsed constraints and objectives are added to the optimization context. /// public void FromFile(string file) { Native.Z3_optimize_from_file(Context.nCtx, NativeObject, file); } /// /// Similar to FromFile. Instead it takes as argument a string. /// public void FromString(string s) { Native.Z3_optimize_from_string(Context.nCtx, NativeObject, s); } /// /// The set of asserted formulas. /// public BoolExpr[] Assertions { get { ASTVector assertions = new ASTVector(Context, Native.Z3_optimize_get_assertions(Context.nCtx, NativeObject)); return assertions.ToBoolExprArray(); } } /// /// The set of asserted formulas. /// public Expr[] Objectives { get { ASTVector objectives = new ASTVector(Context, Native.Z3_optimize_get_objectives(Context.nCtx, NativeObject)); return objectives.ToExprArray(); } } /// /// Optimize statistics. /// public Statistics Statistics { get { return new Statistics(Context, Native.Z3_optimize_get_statistics(Context.nCtx, NativeObject)); } } #region Internal internal Optimize(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } internal Optimize(Context ctx) : base(ctx, Native.Z3_mk_optimize(ctx.nCtx)) { Debug.Assert(ctx != null); } internal class DecRefQueue : IDecRefQueue { public DecRefQueue() : base() { } public DecRefQueue(uint move_limit) : base(move_limit) { } internal override void IncRef(Context ctx, IntPtr obj) { Native.Z3_optimize_inc_ref(ctx.nCtx, obj); } internal override void DecRef(Context ctx, IntPtr obj) { Native.Z3_optimize_dec_ref(ctx.nCtx, obj); } }; internal override void IncRef(IntPtr o) { Context.Optimize_DRQ.IncAndClear(Context, o); base.IncRef(o); } internal override void DecRef(IntPtr o) { Context.Optimize_DRQ.Add(o); base.DecRef(o); } #endregion } } z3-z3-4.8.7/src/api/dotnet/ParamDescrs.cs000066400000000000000000000064131356505360400200230ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: Parameter.cs Abstract: Z3 Managed API: Parameter Descriptions Author: Christoph Wintersteiger (cwinter) 2012-03-20 Notes: --*/ using System.Diagnostics; using System; namespace Microsoft.Z3 { /// /// A ParamDescrs describes a set of parameters. /// public class ParamDescrs : Z3Object { /// /// validate a set of parameters. /// public void Validate(Params p) { Debug.Assert(p != null); Native.Z3_params_validate(Context.nCtx, p.NativeObject, NativeObject); } /// /// Retrieve kind of parameter. /// public Z3_param_kind GetKind(Symbol name) { Debug.Assert(name != null); return (Z3_param_kind)Native.Z3_param_descrs_get_kind(Context.nCtx, NativeObject, name.NativeObject); } /// /// Retrieve documentation of parameter. /// public string GetDocumentation(Symbol name) { Debug.Assert(name != null); return Native.Z3_param_descrs_get_documentation(Context.nCtx, NativeObject, name.NativeObject); } /// /// Retrieve all names of parameters. /// public Symbol[] Names { get { uint sz = Native.Z3_param_descrs_size(Context.nCtx, NativeObject); Symbol[] names = new Symbol[sz]; for (uint i = 0; i < sz; ++i) { names[i] = Symbol.Create(Context, Native.Z3_param_descrs_get_name(Context.nCtx, NativeObject, i)); } return names; } } /// /// The size of the ParamDescrs. /// public uint Size { get { return Native.Z3_param_descrs_size(Context.nCtx, NativeObject); } } /// /// Retrieves a string representation of the ParamDescrs. /// public override string ToString() { return Native.Z3_param_descrs_to_string(Context.nCtx, NativeObject); } #region Internal internal ParamDescrs(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } internal class DecRefQueue : IDecRefQueue { public DecRefQueue() : base() { } public DecRefQueue(uint move_limit) : base(move_limit) { } internal override void IncRef(Context ctx, IntPtr obj) { Native.Z3_param_descrs_inc_ref(ctx.nCtx, obj); } internal override void DecRef(Context ctx, IntPtr obj) { Native.Z3_param_descrs_dec_ref(ctx.nCtx, obj); } }; internal override void IncRef(IntPtr o) { Context.ParamDescrs_DRQ.IncAndClear(Context, o); base.IncRef(o); } internal override void DecRef(IntPtr o) { Context.ParamDescrs_DRQ.Add(o); base.DecRef(o); } #endregion } } z3-z3-4.8.7/src/api/dotnet/Params.cs000066400000000000000000000116121356505360400170370ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: Parameter.cs Abstract: Z3 Managed API: Parameters Author: Christoph Wintersteiger (cwinter) 2012-03-20 Notes: --*/ using System.Diagnostics; using System; namespace Microsoft.Z3 { /// /// A Params objects represents a configuration in the form of Symbol/value pairs. /// public class Params : Z3Object { /// /// Adds a parameter setting. /// public Params Add(Symbol name, bool value) { Debug.Assert(name != null); Native.Z3_params_set_bool(Context.nCtx, NativeObject, name.NativeObject, (byte)(value ? 1 : 0)); return this; } /// /// Adds a parameter setting. /// public Params Add(Symbol name, uint value) { Debug.Assert(name != null); Native.Z3_params_set_uint(Context.nCtx, NativeObject, name.NativeObject, value); return this; } /// /// Adds a parameter setting. /// public Params Add(Symbol name, double value) { Debug.Assert(name != null); Native.Z3_params_set_double(Context.nCtx, NativeObject, name.NativeObject, value); return this; } /// /// Adds a parameter setting. /// public Params Add(Symbol name, string value) { Debug.Assert(value != null); Native.Z3_params_set_symbol(Context.nCtx, NativeObject, name.NativeObject, Context.MkSymbol(value).NativeObject); return this; } /// /// Adds a parameter setting. /// public Params Add(Symbol name, Symbol value) { Debug.Assert(name != null); Debug.Assert(value != null); Native.Z3_params_set_symbol(Context.nCtx, NativeObject, name.NativeObject, value.NativeObject); return this; } /// /// Adds a parameter setting. /// public Params Add(string name, bool value) { Native.Z3_params_set_bool(Context.nCtx, NativeObject, Context.MkSymbol(name).NativeObject, (byte)(value ? 1 : 0)); return this; } /// /// Adds a parameter setting. /// public Params Add(string name, uint value) { Native.Z3_params_set_uint(Context.nCtx, NativeObject, Context.MkSymbol(name).NativeObject, value); return this; } /// /// Adds a parameter setting. /// public Params Add(string name, double value) { Native.Z3_params_set_double(Context.nCtx, NativeObject, Context.MkSymbol(name).NativeObject, value); return this; } /// /// Adds a parameter setting. /// public Params Add(string name, Symbol value) { Debug.Assert(value != null); Native.Z3_params_set_symbol(Context.nCtx, NativeObject, Context.MkSymbol(name).NativeObject, value.NativeObject); return this; } /// /// Adds a parameter setting. /// public Params Add(string name, string value) { Debug.Assert(name != null); Debug.Assert(value != null); Native.Z3_params_set_symbol(Context.nCtx, NativeObject, Context.MkSymbol(name).NativeObject, Context.MkSymbol(value).NativeObject); return this; } /// /// A string representation of the parameter set. /// public override string ToString() { return Native.Z3_params_to_string(Context.nCtx, NativeObject); } #region Internal internal Params(Context ctx) : base(ctx, Native.Z3_mk_params(ctx.nCtx)) { Debug.Assert(ctx != null); } internal class DecRefQueue : IDecRefQueue { public DecRefQueue() : base() { } public DecRefQueue(uint move_limit) : base(move_limit) { } internal override void IncRef(Context ctx, IntPtr obj) { Native.Z3_params_inc_ref(ctx.nCtx, obj); } internal override void DecRef(Context ctx, IntPtr obj) { Native.Z3_params_dec_ref(ctx.nCtx, obj); } }; internal override void IncRef(IntPtr o) { Context.Params_DRQ.IncAndClear(Context, o); base.IncRef(o); } internal override void DecRef(IntPtr o) { Context.Params_DRQ.Add(o); base.DecRef(o); } #endregion } } z3-z3-4.8.7/src/api/dotnet/Pattern.cs000066400000000000000000000031431356505360400172310ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: Pattern.cs Abstract: Z3 Managed API: Patterns Author: Christoph Wintersteiger (cwinter) 2012-03-16 Notes: --*/ using System.Diagnostics; using System; using System.Runtime.InteropServices; namespace Microsoft.Z3 { /// /// Patterns comprise a list of terms. The list should be /// non-empty. If the list comprises of more than one term, it is /// also called a multi-pattern. /// public class Pattern : AST { /// /// The number of terms in the pattern. /// public uint NumTerms { get { return Native.Z3_get_pattern_num_terms(Context.nCtx, NativeObject); } } /// /// The terms in the pattern. /// public Expr[] Terms { get { uint n = NumTerms; Expr[] res = new Expr[n]; for (uint i = 0; i < n; i++) res[i] = Expr.Create(Context, Native.Z3_get_pattern(Context.nCtx, NativeObject, i)); return res; } } /// /// A string representation of the pattern. /// public override string ToString() { return Native.Z3_pattern_to_string(Context.nCtx, NativeObject); } #region Internal internal Pattern(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } #endregion } } z3-z3-4.8.7/src/api/dotnet/Probe.cs000066400000000000000000000051311356505360400166620ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: Probe.cs Abstract: Z3 Managed API: Probes Author: Christoph Wintersteiger (cwinter) 2012-03-21 Notes: --*/ using System.Diagnostics; using System; using System.Runtime.InteropServices; namespace Microsoft.Z3 { /// /// Probes are used to inspect a goal (aka problem) and collect information that may be used to decide /// which solver and/or preprocessing step will be used. /// The complete list of probes may be obtained using the procedures Context.NumProbes /// and Context.ProbeNames. /// It may also be obtained using the command (help-tactic) in the SMT 2.0 front-end. /// public class Probe : Z3Object { /// /// Execute the probe over the goal. /// /// A probe always produce a double value. /// "Boolean" probes return 0.0 for false, and a value different from 0.0 for true. public double Apply(Goal g) { Debug.Assert(g != null); Context.CheckContextMatch(g); return Native.Z3_probe_apply(Context.nCtx, NativeObject, g.NativeObject); } /// /// Apply the probe to a goal. /// public double this[Goal g] { get { Debug.Assert(g != null); return Apply(g); } } #region Internal internal Probe(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } internal Probe(Context ctx, string name) : base(ctx, Native.Z3_mk_probe(ctx.nCtx, name)) { Debug.Assert(ctx != null); } internal class DecRefQueue : IDecRefQueue { public DecRefQueue() : base() { } public DecRefQueue(uint move_limit) : base(move_limit) { } internal override void IncRef(Context ctx, IntPtr obj) { Native.Z3_probe_inc_ref(ctx.nCtx, obj); } internal override void DecRef(Context ctx, IntPtr obj) { Native.Z3_probe_dec_ref(ctx.nCtx, obj); } }; internal override void IncRef(IntPtr o) { Context.Probe_DRQ.IncAndClear(Context, o); base.IncRef(o); } internal override void DecRef(IntPtr o) { Context.Probe_DRQ.Add(o); base.DecRef(o); } #endregion } } z3-z3-4.8.7/src/api/dotnet/Properties/000077500000000000000000000000001356505360400174205ustar00rootroot00000000000000z3-z3-4.8.7/src/api/dotnet/Properties/AssemblyInfo.cs.in000066400000000000000000000031051356505360400227460ustar00rootroot00000000000000using 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_TWEAK@")] [assembly: AssemblyFileVersion("@VER_MAJOR@.@VER_MINOR@.@VER_BUILD@.@VER_TWEAK@")] z3-z3-4.8.7/src/api/dotnet/Quantifier.cs000066400000000000000000000215201356505360400177220ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: Quantifier.cs Abstract: Z3 Managed API: Quantifiers Author: Christoph Wintersteiger (cwinter) 2012-03-19 Notes: --*/ using System; using System.Diagnostics; using System.Linq; namespace Microsoft.Z3 { /// /// Quantifier expressions. /// public class Quantifier : BoolExpr { /// /// Indicates whether the quantifier is universal. /// public bool IsUniversal { get { return 0 != Native.Z3_is_quantifier_forall(Context.nCtx, NativeObject); } } /// /// Indicates whether the quantifier is existential. /// public bool IsExistential { get { return 0 != Native.Z3_is_quantifier_exists(Context.nCtx, NativeObject); } } /// /// The weight of the quantifier. /// public uint Weight { get { return Native.Z3_get_quantifier_weight(Context.nCtx, NativeObject); } } /// /// The number of patterns. /// public uint NumPatterns { get { return Native.Z3_get_quantifier_num_patterns(Context.nCtx, NativeObject); } } /// /// The patterns. /// public Pattern[] Patterns { get { uint n = NumPatterns; Pattern[] res = new Pattern[n]; for (uint i = 0; i < n; i++) res[i] = new Pattern(Context, Native.Z3_get_quantifier_pattern_ast(Context.nCtx, NativeObject, i)); return res; } } /// /// The number of no-patterns. /// public uint NumNoPatterns { get { return Native.Z3_get_quantifier_num_no_patterns(Context.nCtx, NativeObject); } } /// /// The no-patterns. /// public Pattern[] NoPatterns { get { uint n = NumNoPatterns; Pattern[] res = new Pattern[n]; for (uint i = 0; i < n; i++) res[i] = new Pattern(Context, Native.Z3_get_quantifier_no_pattern_ast(Context.nCtx, NativeObject, i)); return res; } } /// /// The number of bound variables. /// public uint NumBound { get { return Native.Z3_get_quantifier_num_bound(Context.nCtx, NativeObject); } } /// /// The symbols for the bound variables. /// public Symbol[] BoundVariableNames { get { uint n = NumBound; Symbol[] res = new Symbol[n]; for (uint i = 0; i < n; i++) res[i] = Symbol.Create(Context, Native.Z3_get_quantifier_bound_name(Context.nCtx, NativeObject, i)); return res; } } /// /// The sorts of the bound variables. /// public Sort[] BoundVariableSorts { get { uint n = NumBound; Sort[] res = new Sort[n]; for (uint i = 0; i < n; i++) res[i] = Sort.Create(Context, Native.Z3_get_quantifier_bound_sort(Context.nCtx, NativeObject, i)); return res; } } /// /// The body of the quantifier. /// public Expr Body { get { return Expr.Create(Context, Native.Z3_get_quantifier_body(Context.nCtx, NativeObject)); } } /// /// Translates (copies) the quantifier to the Context . /// /// A context /// A copy of the quantifier which is associated with new public Quantifier Translate(Context ctx) { return (Quantifier)base.Translate(ctx); } #region Internal internal Quantifier(Context ctx, bool isForall, Sort[] sorts, Symbol[] names, Expr body, uint weight = 1, Pattern[] patterns = null, Expr[] noPatterns = null, Symbol quantifierID = null, Symbol skolemID = null) : base(ctx, IntPtr.Zero) { Debug.Assert(ctx != null); Debug.Assert(sorts != null); Debug.Assert(names != null); Debug.Assert(body != null); Debug.Assert(sorts.Length == names.Length); Debug.Assert(sorts.All(s => s != null)); Debug.Assert(names.All(n => n != null)); Debug.Assert(patterns == null || patterns.All(p => p != null)); Debug.Assert(noPatterns == null || noPatterns.All(np => np != null)); Context.CheckContextMatch(patterns); Context.CheckContextMatch(noPatterns); Context.CheckContextMatch(sorts); Context.CheckContextMatch(names); Context.CheckContextMatch(body); if (sorts.Length != names.Length) throw new Z3Exception("Number of sorts does not match number of names"); if (noPatterns == null && quantifierID == null && skolemID == null) { NativeObject = Native.Z3_mk_quantifier(ctx.nCtx, (byte)(isForall ? 1 : 0) , weight, AST.ArrayLength(patterns), AST.ArrayToNative(patterns), AST.ArrayLength(sorts), AST.ArrayToNative(sorts), Symbol.ArrayToNative(names), body.NativeObject); } else { NativeObject = Native.Z3_mk_quantifier_ex(ctx.nCtx, (byte)(isForall ? 1 : 0), weight, AST.GetNativeObject(quantifierID), AST.GetNativeObject(skolemID), AST.ArrayLength(patterns), AST.ArrayToNative(patterns), AST.ArrayLength(noPatterns), AST.ArrayToNative(noPatterns), AST.ArrayLength(sorts), AST.ArrayToNative(sorts), Symbol.ArrayToNative(names), body.NativeObject); } } internal Quantifier(Context ctx, bool isForall, Expr[] bound, Expr body, uint weight = 1, Pattern[] patterns = null, Expr[] noPatterns = null, Symbol quantifierID = null, Symbol skolemID = null) : base(ctx, IntPtr.Zero) { Debug.Assert(ctx != null); Debug.Assert(body != null); Debug.Assert(patterns == null || patterns.All(p => p != null)); Debug.Assert(noPatterns == null || noPatterns.All(np => np != null)); Debug.Assert(bound == null || bound.All(n => n != null)); Context.CheckContextMatch(noPatterns); Context.CheckContextMatch(patterns); //Context.CheckContextMatch(bound); Context.CheckContextMatch(body); if (noPatterns == null && quantifierID == null && skolemID == null) { NativeObject = Native.Z3_mk_quantifier_const(ctx.nCtx, (byte)(isForall ? 1 : 0) , weight, AST.ArrayLength(bound), AST.ArrayToNative(bound), AST.ArrayLength(patterns), AST.ArrayToNative(patterns), body.NativeObject); } else { NativeObject = Native.Z3_mk_quantifier_const_ex(ctx.nCtx, (byte)(isForall ? 1 : 0), weight, AST.GetNativeObject(quantifierID), AST.GetNativeObject(skolemID), AST.ArrayLength(bound), AST.ArrayToNative(bound), AST.ArrayLength(patterns), AST.ArrayToNative(patterns), AST.ArrayLength(noPatterns), AST.ArrayToNative(noPatterns), body.NativeObject); } } internal Quantifier(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } #if DEBUG internal override void CheckNativeObject(IntPtr obj) { if ((Z3_ast_kind)Native.Z3_get_ast_kind(Context.nCtx, obj) != Z3_ast_kind.Z3_QUANTIFIER_AST) throw new Z3Exception("Underlying object is not a quantifier"); base.CheckNativeObject(obj); } #endif #endregion } } z3-z3-4.8.7/src/api/dotnet/RatNum.cs000066400000000000000000000052411356505360400170230ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: IntNum.cs Abstract: Z3 Managed API: Int Numerals Author: Christoph Wintersteiger (cwinter) 2012-03-20 Notes: --*/ using System.Diagnostics; using System; #if !FRAMEWORK_LT_4 using System.Numerics; #endif namespace Microsoft.Z3 { /// /// Rational Numerals /// public class RatNum : RealExpr { /// /// The numerator of a rational numeral. /// public IntNum Numerator { get { return new IntNum(Context, Native.Z3_get_numerator(Context.nCtx, NativeObject)); } } /// /// The denominator of a rational numeral. /// public IntNum Denominator { get { return new IntNum(Context, Native.Z3_get_denominator(Context.nCtx, NativeObject)); } } #if !FRAMEWORK_LT_4 /// /// Converts the numerator of the rational to a BigInteger /// public BigInteger BigIntNumerator { get { IntNum n = Numerator; return BigInteger.Parse(n.ToString()); } } /// /// Converts the denominator of the rational to a BigInteger /// public BigInteger BigIntDenominator { get { IntNum n = Denominator; return BigInteger.Parse(n.ToString()); } } #endif /// /// Returns a string representation in decimal notation. /// /// The result has at most decimal places. public string ToDecimalString(uint precision) { return Native.Z3_get_numeral_decimal_string(Context.nCtx, NativeObject, precision); } /// /// Returns a double representing the value. /// public double Double { get { return Native.Z3_get_numeral_double(Context.nCtx, NativeObject); } } /// /// Returns a string representation of the numeral. /// public override string ToString() { return Native.Z3_get_numeral_string(Context.nCtx, NativeObject); } #region Internal internal RatNum(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } #endregion } } z3-z3-4.8.7/src/api/dotnet/ReExpr.cs000066400000000000000000000013021356505360400170140ustar00rootroot00000000000000/*++ Copyright () 2016 Microsoft Corporation Module Name: ReExpr.cs Abstract: Z3 Managed API: Regular Expressions Author: Christoph Wintersteiger (cwinter) 2012-11-23 Notes: --*/ using System.Diagnostics; using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Microsoft.Z3 { /// /// Regular expression expressions /// public class ReExpr : Expr { #region Internal /// Constructor for ReExpr internal ReExpr(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } #endregion } } z3-z3-4.8.7/src/api/dotnet/ReSort.cs000066400000000000000000000013321356505360400170300ustar00rootroot00000000000000/*++ Copyright (c) 2016 Microsoft Corporation Module Name: ReSort.cs Abstract: Z3 Managed API: Regular expression Sorts Author: Christoph Wintersteiger (cwinter) 2012-11-23 Notes: --*/ using System.Diagnostics; using System; namespace Microsoft.Z3 { /// /// A regular expression sort /// public class ReSort : Sort { #region Internal internal ReSort(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } internal ReSort(Context ctx) : base(ctx, Native.Z3_mk_int_sort(ctx.nCtx)) { Debug.Assert(ctx != null); } #endregion } } z3-z3-4.8.7/src/api/dotnet/RealExpr.cs000066400000000000000000000012761356505360400173430ustar00rootroot00000000000000/*++ Copyright () 2012 Microsoft Corporation Module Name: RealExpr.cs Abstract: Z3 Managed API: Real Expressions Author: Christoph Wintersteiger (cwinter) 2012-11-23 Notes: --*/ using System.Diagnostics; using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Microsoft.Z3 { /// /// Real expressions /// public class RealExpr : ArithExpr { #region Internal /// Constructor for RealExpr internal RealExpr(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } #endregion } } z3-z3-4.8.7/src/api/dotnet/RealSort.cs000066400000000000000000000013131356505360400173440ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: RealSort.cs Abstract: Z3 Managed API: Real Sorts Author: Christoph Wintersteiger (cwinter) 2012-11-23 Notes: --*/ using System.Diagnostics; using System; namespace Microsoft.Z3 { /// /// A real sort /// public class RealSort : ArithSort { #region Internal internal RealSort(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } internal RealSort(Context ctx) : base(ctx, Native.Z3_mk_real_sort(ctx.nCtx)) { Debug.Assert(ctx != null); } #endregion } } z3-z3-4.8.7/src/api/dotnet/RelationSort.cs000066400000000000000000000025571356505360400202510ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: RelationSort.cs Abstract: Z3 Managed API: Relation Sorts Author: Christoph Wintersteiger (cwinter) 2012-11-23 Notes: --*/ using System.Diagnostics; using System; namespace Microsoft.Z3 { /// /// Relation sorts. /// public class RelationSort : Sort { /// /// The arity of the relation sort. /// public uint Arity { get { return Native.Z3_get_relation_arity(Context.nCtx, NativeObject); } } /// /// The sorts of the columns of the relation sort. /// public Sort[] ColumnSorts { get { if (m_columnSorts != null) return m_columnSorts; uint n = Arity; Sort[] res = new Sort[n]; for (uint i = 0; i < n; i++) res[i] = Sort.Create(Context, Native.Z3_get_relation_column(Context.nCtx, NativeObject, i)); return res; } } #region Internal private Sort[] m_columnSorts = null; internal RelationSort(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } #endregion } } z3-z3-4.8.7/src/api/dotnet/SeqExpr.cs000066400000000000000000000015651356505360400172110ustar00rootroot00000000000000/*++ Copyright () 2016 Microsoft Corporation Module Name: SeqExpr.cs Abstract: Z3 Managed API: Sequence Expressions Author: Christoph Wintersteiger (cwinter) 2012-11-23 Notes: --*/ using System.Diagnostics; using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Microsoft.Z3 { /// /// Sequence expressions /// public class SeqExpr : Expr { #region Internal /// Constructor for SeqExpr internal SeqExpr(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } #endregion /// Access the nth element of a sequence public Expr this[Expr index] { get { return Context.MkNth(this, index); } } } } z3-z3-4.8.7/src/api/dotnet/SeqSort.cs000066400000000000000000000013121356505360400172100ustar00rootroot00000000000000/*++ Copyright (c) 2016 Microsoft Corporation Module Name: SeqSort.cs Abstract: Z3 Managed API: Sequence Sorts Author: Christoph Wintersteiger (cwinter) 2012-11-23 Notes: --*/ using System.Diagnostics; using System; namespace Microsoft.Z3 { /// /// A Sequence sort /// public class SeqSort : Sort { #region Internal internal SeqSort(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } internal SeqSort(Context ctx) : base(ctx, Native.Z3_mk_int_sort(ctx.nCtx)) { Debug.Assert(ctx != null); } #endregion } } z3-z3-4.8.7/src/api/dotnet/SetSort.cs000066400000000000000000000013771356505360400172260ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: SetSort.cs Abstract: Z3 Managed API: Set Sorts Author: Christoph Wintersteiger (cwinter) 2012-11-23 Notes: --*/ using System.Diagnostics; using System; namespace Microsoft.Z3 { /// /// Set sorts. /// public class SetSort : Sort { #region Internal internal SetSort(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } internal SetSort(Context ctx, Sort ty) : base(ctx, Native.Z3_mk_set_sort(ctx.nCtx, ty.NativeObject)) { Debug.Assert(ctx != null); Debug.Assert(ty != null); } #endregion } } z3-z3-4.8.7/src/api/dotnet/Solver.cs000066400000000000000000000423761356505360400171010ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: Solver.cs Abstract: Z3 Managed API: Solvers Author: Christoph Wintersteiger (cwinter) 2012-03-22 Notes: --*/ using System; using System.Diagnostics; using System.Linq; using System.Collections.Generic; namespace Microsoft.Z3 { /// /// Solvers. /// public class Solver : Z3Object { /// /// A string that describes all available solver parameters. /// public string Help { get { return Native.Z3_solver_get_help(Context.nCtx, NativeObject); } } /// /// Sets the solver parameters. /// public Params Parameters { set { Debug.Assert(value != null); Context.CheckContextMatch(value); Native.Z3_solver_set_params(Context.nCtx, NativeObject, value.NativeObject); } } /// /// Sets parameter on the solver /// public void Set(string name, bool value) { Parameters = Context.MkParams().Add(name, value); } /// /// Sets parameter on the solver /// public void Set(string name, uint value) { Parameters = Context.MkParams().Add(name, value); } /// /// Sets parameter on the solver /// public void Set(string name, double value) { Parameters = Context.MkParams().Add(name, value); } /// /// Sets parameter on the solver /// public void Set(string name, string value) { Parameters = Context.MkParams().Add(name, value); } /// /// Sets parameter on the solver /// public void Set(string name, Symbol value) { Parameters = Context.MkParams().Add(name, value); } /// /// Sets parameter on the solver /// public void Set(Symbol name, bool value) { Parameters = Context.MkParams().Add(name, value); } /// /// Sets parameter on the solver /// public void Set(Symbol name, uint value) { Parameters = Context.MkParams().Add(name, value); } /// /// Sets parameter on the solver /// public void Set(Symbol name, double value) { Parameters = Context.MkParams().Add(name, value); } /// /// Sets parameter on the solver /// public void Set(Symbol name, string value) { Parameters = Context.MkParams().Add(name, value); } /// /// Sets parameter on the solver /// public void Set(Symbol name, Symbol value) { Parameters = Context.MkParams().Add(name, value); } /// /// Retrieves parameter descriptions for solver. /// public ParamDescrs ParameterDescriptions { get { return new ParamDescrs(Context, Native.Z3_solver_get_param_descrs(Context.nCtx, NativeObject)); } } /// /// The current number of backtracking points (scopes). /// /// /// public uint NumScopes { get { return Native.Z3_solver_get_num_scopes(Context.nCtx, NativeObject); } } /// /// Creates a backtracking point. /// /// public void Push() { Native.Z3_solver_push(Context.nCtx, NativeObject); } /// /// Backtracks backtracking points. /// /// Note that an exception is thrown if is not smaller than NumScopes /// public void Pop(uint n = 1) { Native.Z3_solver_pop(Context.nCtx, NativeObject, n); } /// /// Resets the Solver. /// /// This removes all assertions from the solver. public void Reset() { Native.Z3_solver_reset(Context.nCtx, NativeObject); } /// /// Assert a constraint (or multiple) into the solver. /// public void Assert(params BoolExpr[] constraints) { Debug.Assert(constraints != null); Debug.Assert(constraints.All(c => c != null)); Context.CheckContextMatch(constraints); foreach (BoolExpr a in constraints) { Native.Z3_solver_assert(Context.nCtx, NativeObject, a.NativeObject); } } /// /// Alias for Assert. /// public void Add(params BoolExpr[] constraints) { Assert(constraints); } /// /// Alias for Assert. /// public void Add(IEnumerable constraints) { Assert(constraints.ToArray()); } /// /// Assert multiple constraints into the solver, and track them (in the unsat) core /// using the Boolean constants in ps. /// /// /// This API is an alternative to with assumptions for extracting unsat cores. /// Both APIs can be used in the same solver. The unsat core will contain a combination /// of the Boolean variables provided using /// and the Boolean literals /// provided using with assumptions. /// public void AssertAndTrack(BoolExpr[] constraints, BoolExpr[] ps) { Debug.Assert(constraints != null); Debug.Assert(constraints.All(c => c != null)); Debug.Assert(ps.All(c => c != null)); Context.CheckContextMatch(constraints); Context.CheckContextMatch(ps); if (constraints.Length != ps.Length) throw new Z3Exception("Argument size mismatch"); for (int i = 0 ; i < constraints.Length; i++) Native.Z3_solver_assert_and_track(Context.nCtx, NativeObject, constraints[i].NativeObject, ps[i].NativeObject); } /// /// Assert a constraint into the solver, and track it (in the unsat) core /// using the Boolean constant p. /// /// /// This API is an alternative to with assumptions for extracting unsat cores. /// Both APIs can be used in the same solver. The unsat core will contain a combination /// of the Boolean variables provided using /// and the Boolean literals /// provided using with assumptions. /// public void AssertAndTrack(BoolExpr constraint, BoolExpr p) { Debug.Assert(constraint != null); Debug.Assert(p != null); Context.CheckContextMatch(constraint); Context.CheckContextMatch(p); Native.Z3_solver_assert_and_track(Context.nCtx, NativeObject, constraint.NativeObject, p.NativeObject); } /// /// Load solver assertions from a file. /// public void FromFile(string file) { Native.Z3_solver_from_file(Context.nCtx, NativeObject, file); } /// /// Load solver assertions from a string. /// public void FromString(string str) { Native.Z3_solver_from_string(Context.nCtx, NativeObject, str); } /// /// The number of assertions in the solver. /// public uint NumAssertions { get { ASTVector assertions = new ASTVector(Context, Native.Z3_solver_get_assertions(Context.nCtx, NativeObject)); return assertions.Size; } } /// /// The set of asserted formulas. /// public BoolExpr[] Assertions { get { ASTVector assertions = new ASTVector(Context, Native.Z3_solver_get_assertions(Context.nCtx, NativeObject)); return assertions.ToBoolExprArray(); } } /// /// Currently inferred units. /// public BoolExpr[] Units { get { ASTVector assertions = new ASTVector(Context, Native.Z3_solver_get_units(Context.nCtx, NativeObject)); return assertions.ToBoolExprArray(); } } /// /// Checks whether the assertions in the solver are consistent or not. /// /// /// /// /// /// public Status Check(params Expr[] assumptions) { Z3_lbool r; if (assumptions == null || assumptions.Length == 0) r = (Z3_lbool)Native.Z3_solver_check(Context.nCtx, NativeObject); else r = (Z3_lbool)Native.Z3_solver_check_assumptions(Context.nCtx, NativeObject, (uint)assumptions.Length, AST.ArrayToNative(assumptions)); return lboolToStatus(r); } /// /// Checks whether the assertions in the solver are consistent or not. /// /// /// /// /// /// public Status Check(IEnumerable assumptions) { Z3_lbool r; BoolExpr[] asms = assumptions.ToArray(); if (asms.Length == 0) r = (Z3_lbool)Native.Z3_solver_check(Context.nCtx, NativeObject); else r = (Z3_lbool)Native.Z3_solver_check_assumptions(Context.nCtx, NativeObject, (uint)asms.Length, AST.ArrayToNative(asms)); return lboolToStatus(r); } /// /// Retrieve fixed assignments to the set of variables in the form of consequences. /// Each consequence is an implication of the form /// /// relevant-assumptions Implies variable = value /// /// where the relevant assumptions is a subset of the assumptions that are passed in /// and the equality on the right side of the implication indicates how a variable /// is fixed. /// /// /// /// /// /// public Status Consequences(IEnumerable assumptions, IEnumerable variables, out BoolExpr[] consequences) { ASTVector result = new ASTVector(Context); ASTVector asms = new ASTVector(Context); ASTVector vars = new ASTVector(Context); foreach (var asm in assumptions) asms.Push(asm); foreach (var v in variables) vars.Push(v); Z3_lbool r = (Z3_lbool)Native.Z3_solver_get_consequences(Context.nCtx, NativeObject, asms.NativeObject, vars.NativeObject, result.NativeObject); consequences = result.ToBoolExprArray(); return lboolToStatus(r); } /// /// The model of the last Check(params Expr[] assumptions). /// /// /// The result is null if Check(params Expr[] assumptions) was not invoked before, /// if its results was not SATISFIABLE, or if model production is not enabled. /// public Model Model { get { IntPtr x = Native.Z3_solver_get_model(Context.nCtx, NativeObject); if (x == IntPtr.Zero) return null; else return new Model(Context, x); } } /// /// The proof of the last Check(params Expr[] assumptions). /// /// /// The result is null if Check(params Expr[] assumptions) was not invoked before, /// if its results was not UNSATISFIABLE, or if proof production is disabled. /// public Expr Proof { get { IntPtr x = Native.Z3_solver_get_proof(Context.nCtx, NativeObject); if (x == IntPtr.Zero) return null; else return Expr.Create(Context, x); } } /// /// The unsat core of the last Check. /// /// /// The unsat core is a subset of Assertions /// The result is empty if Check was not invoked before, /// if its results was not UNSATISFIABLE, or if core production is disabled. /// public BoolExpr[] UnsatCore { get { ASTVector core = new ASTVector(Context, Native.Z3_solver_get_unsat_core(Context.nCtx, NativeObject)); return core.ToBoolExprArray(); } } /// /// A brief justification of why the last call to Check returned UNKNOWN. /// public string ReasonUnknown { get { return Native.Z3_solver_get_reason_unknown(Context.nCtx, NativeObject); } } /// /// Backtrack level that can be adjusted by conquer process /// public uint BacktrackLevel { get; set; } /// /// Variables available and returned by the cuber. /// public BoolExpr[] CubeVariables { get; set; } /// /// Return a set of cubes. /// public IEnumerable Cube() { ASTVector cv = new ASTVector(Context); if (CubeVariables != null) foreach (var b in CubeVariables) cv.Push(b); while (true) { var lvl = BacktrackLevel; BacktrackLevel = uint.MaxValue; ASTVector r = new ASTVector(Context, Native.Z3_solver_cube(Context.nCtx, NativeObject, cv.NativeObject, lvl)); var v = r.ToBoolExprArray(); CubeVariables = cv.ToBoolExprArray(); if (v.Length == 1 && v[0].IsFalse) { break; } yield return v; if (v.Length == 0) { break; } } } /// /// Create a clone of the current solver with respect to ctx. /// public Solver Translate(Context ctx) { Debug.Assert(ctx != null); return new Solver(ctx, Native.Z3_solver_translate(Context.nCtx, NativeObject, ctx.nCtx)); } /// /// Import model converter from other solver. /// public void ImportModelConverter(Solver src) { Native.Z3_solver_import_model_converter(Context.nCtx, src.NativeObject, NativeObject); } /// /// Solver statistics. /// public Statistics Statistics { get { return new Statistics(Context, Native.Z3_solver_get_statistics(Context.nCtx, NativeObject)); } } /// /// A string representation of the solver. /// public override string ToString() { return Native.Z3_solver_to_string(Context.nCtx, NativeObject); } #region Internal internal Solver(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); this.BacktrackLevel = uint.MaxValue; } internal class DecRefQueue : IDecRefQueue { public DecRefQueue() : base() { } public DecRefQueue(uint move_limit) : base(move_limit) { } internal override void IncRef(Context ctx, IntPtr obj) { Native.Z3_solver_inc_ref(ctx.nCtx, obj); } internal override void DecRef(Context ctx, IntPtr obj) { Native.Z3_solver_dec_ref(ctx.nCtx, obj); } }; internal override void IncRef(IntPtr o) { Context.Solver_DRQ.IncAndClear(Context, o); base.IncRef(o); } internal override void DecRef(IntPtr o) { Context.Solver_DRQ.Add(o); base.DecRef(o); } private Status lboolToStatus(Z3_lbool r) { switch (r) { case Z3_lbool.Z3_L_TRUE: return Status.SATISFIABLE; case Z3_lbool.Z3_L_FALSE: return Status.UNSATISFIABLE; default: return Status.UNKNOWN; } } #endregion } } z3-z3-4.8.7/src/api/dotnet/Sort.cs000066400000000000000000000124271356505360400165500ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: Sort.cs Abstract: Z3 Managed API: Sorts Author: Christoph Wintersteiger (cwinter) 2012-03-15 Notes: --*/ using System.Diagnostics; using System; namespace Microsoft.Z3 { /// /// The Sort class implements type information for ASTs. /// public class Sort : AST { /// /// Comparison operator. /// /// A Sort /// A Sort /// True if and are from the same context /// and represent the same sort; false otherwise. public static bool operator ==(Sort a, Sort b) { return Object.ReferenceEquals(a, b) || (!Object.ReferenceEquals(a, null) && !Object.ReferenceEquals(b, null) && a.Context == b.Context && 0 != Native.Z3_is_eq_sort(a.Context.nCtx, a.NativeObject, b.NativeObject)); } /// /// Comparison operator. /// /// A Sort /// A Sort /// True if and are not from the same context /// or represent different sorts; false otherwise. public static bool operator !=(Sort a, Sort b) { return !(a == b); } /// /// Equality operator for objects of type Sort. /// /// /// public override bool Equals(object o) { Sort casted = o as Sort; if (casted == null) return false; return this == casted; } /// /// Hash code generation for Sorts /// /// A hash code public override int GetHashCode() { return base.GetHashCode(); } /// /// Returns a unique identifier for the sort. /// new public uint Id { get { return Native.Z3_get_sort_id(Context.nCtx, NativeObject); } } /// /// The kind of the sort. /// public Z3_sort_kind SortKind { get { return (Z3_sort_kind)Native.Z3_get_sort_kind(Context.nCtx, NativeObject); } } /// /// The name of the sort /// public Symbol Name { get { return Symbol.Create(Context, Native.Z3_get_sort_name(Context.nCtx, NativeObject)); } } /// /// A string representation of the sort. /// public override string ToString() { return Native.Z3_sort_to_string(Context.nCtx, NativeObject); } /// /// Translates (copies) the sort to the Context . /// /// A context /// A copy of the sort which is associated with new public Sort Translate(Context ctx) { return (Sort)base.Translate(ctx); } #region Internal /// /// Sort constructor /// internal Sort(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } #if DEBUG internal override void CheckNativeObject(IntPtr obj) { if (Native.Z3_get_ast_kind(Context.nCtx, obj) != (uint)Z3_ast_kind.Z3_SORT_AST) throw new Z3Exception("Underlying object is not a sort"); base.CheckNativeObject(obj); } #endif new internal static Sort Create(Context ctx, IntPtr obj) { Debug.Assert(ctx != null); switch ((Z3_sort_kind)Native.Z3_get_sort_kind(ctx.nCtx, obj)) { case Z3_sort_kind.Z3_ARRAY_SORT: return new ArraySort(ctx, obj); case Z3_sort_kind.Z3_BOOL_SORT: return new BoolSort(ctx, obj); case Z3_sort_kind.Z3_BV_SORT: return new BitVecSort(ctx, obj); case Z3_sort_kind.Z3_DATATYPE_SORT: return new DatatypeSort(ctx, obj); case Z3_sort_kind.Z3_INT_SORT: return new IntSort(ctx, obj); case Z3_sort_kind.Z3_REAL_SORT: return new RealSort(ctx, obj); case Z3_sort_kind.Z3_UNINTERPRETED_SORT: return new UninterpretedSort(ctx, obj); case Z3_sort_kind.Z3_FINITE_DOMAIN_SORT: return new FiniteDomainSort(ctx, obj); case Z3_sort_kind.Z3_RELATION_SORT: return new RelationSort(ctx, obj); case Z3_sort_kind.Z3_FLOATING_POINT_SORT: return new FPSort(ctx, obj); case Z3_sort_kind.Z3_ROUNDING_MODE_SORT: return new FPRMSort(ctx, obj); case Z3_sort_kind.Z3_SEQ_SORT: return new SeqSort(ctx, obj); case Z3_sort_kind.Z3_RE_SORT: return new ReSort(ctx, obj); default: throw new Z3Exception("Unknown sort kind"); } } #endregion } } z3-z3-4.8.7/src/api/dotnet/Statistics.cs000066400000000000000000000137771356505360400177640ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: Statistics.cs Abstract: Z3 Managed API: Statistics Author: Christoph Wintersteiger (cwinter) 2012-03-22 Notes: --*/ using System; using System.Diagnostics; namespace Microsoft.Z3 { /// /// Objects of this class track statistical information about solvers. /// public class Statistics : Z3Object { /// /// Statistical data is organized into pairs of [Key, Entry], where every /// Entry is either a DoubleEntry or a UIntEntry /// public class Entry { /// /// The key of the entry. /// readonly public string Key; /// /// The uint-value of the entry. /// public uint UIntValue { get { return m_uint; } } /// /// The double-value of the entry. /// public double DoubleValue { get { return m_double; } } /// /// True if the entry is uint-valued. /// public bool IsUInt { get { return m_is_uint; } } /// /// True if the entry is double-valued. /// public bool IsDouble { get { return m_is_double; } } /// /// The string representation of the entry's value. /// public string Value { get { if (IsUInt) return m_uint.ToString(); else if (IsDouble) return m_double.ToString(); else throw new Z3Exception("Unknown statistical entry type"); } } /// /// The string representation of the Entry. /// public override string ToString() { return Key + ": " + Value; } #region Internal readonly private bool m_is_uint = false; readonly private bool m_is_double = false; readonly private uint m_uint = 0; readonly private double m_double = 0.0; internal Entry(string k, uint v) { Key = k; m_is_uint = true; m_uint = v; } internal Entry(string k, double v) { Key = k; m_is_double = true; m_double = v; } #endregion } /// /// A string representation of the statistical data. /// public override string ToString() { return Native.Z3_stats_to_string(Context.nCtx, NativeObject); } /// /// The number of statistical data. /// public uint Size { get { return Native.Z3_stats_size(Context.nCtx, NativeObject); } } /// /// The data entries. /// public Entry[] Entries { get { uint n = Size; Entry[] res = new Entry[n]; for (uint i = 0; i < n; i++) { Entry e; string k = Native.Z3_stats_get_key(Context.nCtx, NativeObject, i); if (Native.Z3_stats_is_uint(Context.nCtx, NativeObject, i) != 0) e = new Entry(k, Native.Z3_stats_get_uint_value(Context.nCtx, NativeObject, i)); else if (Native.Z3_stats_is_double(Context.nCtx, NativeObject, i) != 0) e = new Entry(k, Native.Z3_stats_get_double_value(Context.nCtx, NativeObject, i)); else throw new Z3Exception("Unknown data entry value"); res[i] = e; } return res; } } /// /// The statistical counters. /// public string[] Keys { get { uint n = Size; string[] res = new string[n]; for (uint i = 0; i < n; i++) res[i] = Native.Z3_stats_get_key(Context.nCtx, NativeObject, i); return res; } } /// /// The value of a particular statistical counter. /// /// Returns null if the key is unknown. public Entry this[string key] { get { uint n = Size; Entry[] es = Entries; for (uint i = 0; i < n; i++) if (es[i].Key == key) return es[i]; return null; } } #region Internal internal Statistics(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } internal class DecRefQueue : IDecRefQueue { public DecRefQueue() : base() { } public DecRefQueue(uint move_limit) : base(move_limit) { } internal override void IncRef(Context ctx, IntPtr obj) { Native.Z3_stats_inc_ref(ctx.nCtx, obj); } internal override void DecRef(Context ctx, IntPtr obj) { Native.Z3_stats_dec_ref(ctx.nCtx, obj); } }; internal override void IncRef(IntPtr o) { Context.Statistics_DRQ.IncAndClear(Context, o); base.IncRef(o); } internal override void DecRef(IntPtr o) { Context.Statistics_DRQ.Add(o); base.DecRef(o); } #endregion } } z3-z3-4.8.7/src/api/dotnet/Status.cs000066400000000000000000000012271356505360400171000ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: Status.cs Abstract: Z3 Managed API: Status Author: Christoph Wintersteiger (cwinter) 2012-03-15 Notes: --*/ using System.Diagnostics; using System; namespace Microsoft.Z3 { /// /// Status values. /// public enum Status { /// /// Used to signify an unsatisfiable status. /// UNSATISFIABLE = -1, /// /// Used to signify an unknown status. /// UNKNOWN = 0, /// /// Used to signify a satisfiable status. /// SATISFIABLE = 1 } } z3-z3-4.8.7/src/api/dotnet/StringSymbol.cs000066400000000000000000000030711356505360400202500ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: StringSymbol.cs Abstract: Z3 Managed API: String Symbols Author: Christoph Wintersteiger (cwinter) 2012-11-23 Notes: --*/ using System; using System.Diagnostics; using System.Runtime.InteropServices; namespace Microsoft.Z3 { /// /// Named symbols /// public class StringSymbol : Symbol { /// /// The string value of the symbol. /// /// Throws an exception if the symbol is not of string kind. public string String { get { if (!IsStringSymbol()) throw new Z3Exception("String requested from non-String symbol"); return Native.Z3_get_symbol_string(Context.nCtx, NativeObject); } } #region Internal internal StringSymbol(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } internal StringSymbol(Context ctx, string s) : base(ctx, Native.Z3_mk_string_symbol(ctx.nCtx, s)) { Debug.Assert(ctx != null); } #if DEBUG internal override void CheckNativeObject(IntPtr obj) { if ((Z3_symbol_kind)Native.Z3_get_symbol_kind(Context.nCtx, obj) != Z3_symbol_kind.Z3_STRING_SYMBOL) throw new Z3Exception("Symbol is not of string kind"); base.CheckNativeObject(obj); } #endif #endregion } } z3-z3-4.8.7/src/api/dotnet/Symbol.cs000066400000000000000000000065361356505360400170720ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: Symbol.cs Abstract: Z3 Managed API: Symbols Author: Christoph Wintersteiger (cwinter) 2012-03-16 Notes: --*/ using System; using System.Diagnostics; using System.Runtime.InteropServices; namespace Microsoft.Z3 { /// /// Symbols are used to name several term and type constructors. /// public class Symbol : Z3Object { /// /// The kind of the symbol (int or string) /// protected Z3_symbol_kind Kind { get { return (Z3_symbol_kind)Native.Z3_get_symbol_kind(Context.nCtx, NativeObject); } } /// /// Indicates whether the symbol is of Int kind /// public bool IsIntSymbol() { return Kind == Z3_symbol_kind.Z3_INT_SYMBOL; } /// /// Indicates whether the symbol is of string kind. /// public bool IsStringSymbol() { return Kind == Z3_symbol_kind.Z3_STRING_SYMBOL; } /// /// A string representation of the symbol. /// public override string ToString() { if (IsIntSymbol()) return ((IntSymbol)this).Int.ToString(); else if (IsStringSymbol()) return ((StringSymbol)this).String; else throw new Z3Exception("Unknown symbol kind encountered"); } /// /// Equality overloading. /// public static bool operator ==(Symbol s1, Symbol s2) { return Object.ReferenceEquals(s1, s2) || (!Object.ReferenceEquals(s1, null) && !Object.ReferenceEquals(s2, null) && s1.NativeObject == s2.NativeObject); } /// /// Equality overloading. /// public static bool operator !=(Symbol s1, Symbol s2) { return !(s1 == s2); } /// /// Object comparison. /// public override bool Equals(object o) { Symbol casted = o as Symbol; if (casted == null) return false; return this == casted; } /// /// The Symbols's hash code. /// /// A hash code public override int GetHashCode() { return (int)NativeObject; } #region Internal /// /// Symbol constructor /// internal protected Symbol(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } internal static Symbol Create(Context ctx, IntPtr obj) { Debug.Assert(ctx != null); switch ((Z3_symbol_kind)Native.Z3_get_symbol_kind(ctx.nCtx, obj)) { case Z3_symbol_kind.Z3_INT_SYMBOL: return new IntSymbol(ctx, obj); case Z3_symbol_kind.Z3_STRING_SYMBOL: return new StringSymbol(ctx, obj); default: throw new Z3Exception("Unknown symbol kind encountered"); } } #endregion } } z3-z3-4.8.7/src/api/dotnet/Tactic.cs000066400000000000000000000071261356505360400170300ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: Tactic.cs Abstract: Z3 Managed API: Tactics Author: Christoph Wintersteiger (cwinter) 2012-03-21 Notes: --*/ using System; using System.Diagnostics; namespace Microsoft.Z3 { /// /// Tactics are the basic building block for creating custom solvers for specific problem domains. /// The complete list of tactics may be obtained using Context.NumTactics /// and Context.TacticNames. /// It may also be obtained using the command (help-tactic) in the SMT 2.0 front-end. /// public class Tactic : Z3Object { /// /// A string containing a description of parameters accepted by the tactic. /// public string Help { get { return Native.Z3_tactic_get_help(Context.nCtx, NativeObject); } } /// /// Retrieves parameter descriptions for Tactics. /// public ParamDescrs ParameterDescriptions { get { return new ParamDescrs(Context, Native.Z3_tactic_get_param_descrs(Context.nCtx, NativeObject)); } } /// /// Execute the tactic over the goal. /// public ApplyResult Apply(Goal g, Params p = null) { Debug.Assert(g != null); Context.CheckContextMatch(g); if (p == null) return new ApplyResult(Context, Native.Z3_tactic_apply(Context.nCtx, NativeObject, g.NativeObject)); else { Context.CheckContextMatch(p); return new ApplyResult(Context, Native.Z3_tactic_apply_ex(Context.nCtx, NativeObject, g.NativeObject, p.NativeObject)); } } /// /// Apply the tactic to a goal. /// public ApplyResult this[Goal g] { get { Debug.Assert(g != null); return Apply(g); } } /// /// Creates a solver that is implemented using the given tactic. /// /// public Solver Solver { get { return Context.MkSolver(this); } } #region Internal internal Tactic(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } internal Tactic(Context ctx, string name) : base(ctx, Native.Z3_mk_tactic(ctx.nCtx, name)) { Debug.Assert(ctx != null); } /// /// DecRefQueue /// internal class DecRefQueue : IDecRefQueue { public DecRefQueue() : base() { } public DecRefQueue(uint move_limit) : base(move_limit) { } internal override void IncRef(Context ctx, IntPtr obj) { Native.Z3_tactic_inc_ref(ctx.nCtx, obj); } internal override void DecRef(Context ctx, IntPtr obj) { Native.Z3_tactic_dec_ref(ctx.nCtx, obj); } }; internal override void IncRef(IntPtr o) { Context.Tactic_DRQ.IncAndClear(Context, o); base.IncRef(o); } internal override void DecRef(IntPtr o) { Context.Tactic_DRQ.Add(o); base.DecRef(o); } #endregion } } z3-z3-4.8.7/src/api/dotnet/TupleSort.cs000066400000000000000000000037421356505360400175620ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: TupleSort.cs Abstract: Z3 Managed API: Tuple Sorts Author: Christoph Wintersteiger (cwinter) 2012-11-23 Notes: --*/ using System; using System.Diagnostics; namespace Microsoft.Z3 { /// /// Tuple sorts. /// public class TupleSort : Sort { /// /// The constructor function of the tuple. /// public FuncDecl MkDecl { get { return new FuncDecl(Context, Native.Z3_get_tuple_sort_mk_decl(Context.nCtx, NativeObject)); } } /// /// The number of fields in the tuple. /// public uint NumFields { get { return Native.Z3_get_tuple_sort_num_fields(Context.nCtx, NativeObject); } } /// /// The field declarations. /// public FuncDecl[] FieldDecls { get { uint n = NumFields; FuncDecl[] res = new FuncDecl[n]; for (uint i = 0; i < n; i++) res[i] = new FuncDecl(Context, Native.Z3_get_tuple_sort_field_decl(Context.nCtx, NativeObject, i)); return res; } } #region Internal internal TupleSort(Context ctx, Symbol name, uint numFields, Symbol[] fieldNames, Sort[] fieldSorts) : base(ctx, IntPtr.Zero) { Debug.Assert(ctx != null); Debug.Assert(name != null); IntPtr t = IntPtr.Zero; IntPtr[] f = new IntPtr[numFields]; NativeObject = Native.Z3_mk_tuple_sort(ctx.nCtx, name.NativeObject, numFields, Symbol.ArrayToNative(fieldNames), AST.ArrayToNative(fieldSorts), ref t, f); } #endregion }; } z3-z3-4.8.7/src/api/dotnet/UninterpretedSort.cs000066400000000000000000000015031356505360400213120ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: UninterpretedSort.cs Abstract: Z3 Managed API: Uninterpreted Sorts Author: Christoph Wintersteiger (cwinter) 2012-11-23 Notes: --*/ using System; using System.Diagnostics; namespace Microsoft.Z3 { /// /// Uninterpreted Sorts /// public class UninterpretedSort : Sort { #region Internal internal UninterpretedSort(Context ctx, IntPtr obj) : base(ctx, obj) { Debug.Assert(ctx != null); } internal UninterpretedSort(Context ctx, Symbol s) : base(ctx, Native.Z3_mk_uninterpreted_sort(ctx.nCtx, s.NativeObject)) { Debug.Assert(ctx != null); Debug.Assert(s != null); } #endregion } } z3-z3-4.8.7/src/api/dotnet/Version.cs000066400000000000000000000050221356505360400172370ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: Version.cs Abstract: Z3 Managed API: Version information Author: Christoph Wintersteiger (cwinter) 2012-03-16 Notes: --*/ using System.Diagnostics; using System; namespace Microsoft.Z3 { /// /// Version information. /// /// Note that this class is static. public static class Version { static Version() { } /// /// The major version /// public static uint Major { get { uint major = 0, minor = 0, build = 0, revision = 0; Native.Z3_get_version(ref major, ref minor, ref build, ref revision); return major; } } /// /// The minor version /// public static uint Minor { get { uint major = 0, minor = 0, build = 0, revision = 0; Native.Z3_get_version(ref major, ref minor, ref build, ref revision); return minor; } } /// /// The build version /// public static uint Build { get { uint major = 0, minor = 0, build = 0, revision = 0; Native.Z3_get_version(ref major, ref minor, ref build, ref revision); return build; } } /// /// The revision /// public static uint Revision { get { uint major = 0, minor = 0, build = 0, revision = 0; Native.Z3_get_version(ref major, ref minor, ref build, ref revision); return revision; } } /// /// A full version string /// public static string FullVersion { get { return Native.Z3_get_full_version(); } } /// /// A string representation of the version information. /// new public static string ToString() { uint major = 0, minor = 0, build = 0, revision = 0; Native.Z3_get_version(ref major, ref minor, ref build, ref revision); return major.ToString() + "." + minor.ToString() + "." + build.ToString() + "." + revision.ToString(); } } } z3-z3-4.8.7/src/api/dotnet/Z3Exception.cs000066400000000000000000000014611356505360400177700ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: Exception.cs Abstract: Z3 Managed API: Exceptions Author: Christoph Wintersteiger (cwinter) 2012-03-15 Notes: --*/ using System.Diagnostics; using System; namespace Microsoft.Z3 { /// /// The exception base class for error reporting from Z3 /// #if !DOTNET_CORE [Serializable] #endif public class Z3Exception : Exception { /// /// Constructor. /// public Z3Exception() : base() { } /// /// Constructor. /// public Z3Exception(string message) : base(message) { } /// /// Constructor. /// public Z3Exception(string message, System.Exception inner) : base(message, inner) { } } } z3-z3-4.8.7/src/api/dotnet/Z3Object.cs000066400000000000000000000070021356505360400172350ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: Z3Object.cs Abstract: Z3 Managed API: Internal Z3 Objects Author: Christoph Wintersteiger (cwinter) 2012-03-21 Notes: --*/ using System.Diagnostics; using System; using System.Threading; using System.Collections.Generic; using System.Linq; namespace Microsoft.Z3 { /// /// Internal base class for interfacing with native Z3 objects. /// Should not be used externally. /// public class Z3Object : IDisposable { /// /// Finalizer. /// ~Z3Object() { Dispose(); } /// /// Disposes of the underlying native Z3 object. /// public void Dispose() { if (m_n_obj != IntPtr.Zero) { DecRef(m_n_obj); m_n_obj = IntPtr.Zero; } if (m_ctx != null) { if (Interlocked.Decrement(ref m_ctx.refCount) == 0) GC.ReRegisterForFinalize(m_ctx); m_ctx = null; } GC.SuppressFinalize(this); } #region Object Invariant private void ObjectInvariant() { Debug.Assert(this.m_ctx != null); } #endregion #region Internal private Context m_ctx = null; private IntPtr m_n_obj = IntPtr.Zero; internal Z3Object(Context ctx) { Debug.Assert(ctx != null); Interlocked.Increment(ref ctx.refCount); m_ctx = ctx; } internal Z3Object(Context ctx, IntPtr obj) { Debug.Assert(ctx != null); Interlocked.Increment(ref ctx.refCount); m_ctx = ctx; IncRef(obj); m_n_obj = obj; } internal virtual void IncRef(IntPtr o) { } internal virtual void DecRef(IntPtr o) { } internal virtual void CheckNativeObject(IntPtr obj) { } internal virtual IntPtr NativeObject { get { return m_n_obj; } set { if (value != IntPtr.Zero) { CheckNativeObject(value); IncRef(value); } if (m_n_obj != IntPtr.Zero) { DecRef(m_n_obj); } m_n_obj = value; } } internal static IntPtr GetNativeObject(Z3Object s) { if (s == null) return new IntPtr(); return s.NativeObject; } internal Context Context { get { return m_ctx; } } internal static IntPtr[] ArrayToNative(Z3Object[] a) { if (a == null) return null; IntPtr[] an = new IntPtr[a.Length]; for (uint i = 0; i < a.Length; i++) if (a[i] != null) an[i] = a[i].NativeObject; return an; } internal static IntPtr[] EnumToNative(IEnumerable a) where T : Z3Object { if (a == null) return null; IntPtr[] an = new IntPtr[a.Count()]; int i = 0; foreach (var ai in a) { if (ai != null) an[i] = ai.NativeObject; ++i; } return an; } internal static uint ArrayLength(Z3Object[] a) { return (a == null)?0:(uint)a.Length; } #endregion } } z3-z3-4.8.7/src/api/dotnet/cmake_install_gac.cmake.in000066400000000000000000000010271356505360400223130ustar00rootroot00000000000000# Install assembly to the GAC set(GAC_ROOT "$ENV{DESTDIR}@CMAKE_INSTALL_FULL_LIBDIR@") execute_process(COMMAND "@Z3_DOTNET_GACUTIL_EXECUTABLE@" "-i" "@Z3_DOTNET_ASSEMBLY_DLL@" "-f" "-package" "@GAC_PKG_NAME@" "-root" "${GAC_ROOT}" WORKING_DIRECTORY "@CMAKE_CURRENT_BINARY_DIR@" RESULT_VARIABLE gacutil_exit_code ) if ("${gacutil_exit_code}" EQUAL 0) message(STATUS "Installed \"@Z3_DOTNET_ASSEMBLY_DLL@\" to the GAC") else() message(FATAL_ERROR "Failed to install \"@Z3_DOTNET_ASSEMBLY_DLL@\" to the GAC") endif() z3-z3-4.8.7/src/api/dotnet/cmake_uninstall_gac.cmake.in000066400000000000000000000012151356505360400226550ustar00rootroot00000000000000# Uninstall assembly from the GAC set(GAC_ROOT "$ENV{DESTDIR}@CMAKE_INSTALL_FULL_LIBDIR@") execute_process(COMMAND "@Z3_DOTNET_GACUTIL_EXECUTABLE@" # Note ``-us`` takes assembly file name rather than # ``-u`` which takes an assembly display name "-us" "@Z3_DOTNET_ASSEMBLY_NAME@" "-f" "-package" "@GAC_PKG_NAME@" "-root" "${GAC_ROOT}" WORKING_DIRECTORY "@CMAKE_CURRENT_BINARY_DIR@" RESULT_VARIABLE gacutil_exit_code ) if ("${gacutil_exit_code}" EQUAL 0) message(STATUS "Uninstalled \"@Z3_DOTNET_ASSEMBLY_NAME@\" from the GAC") else() message(FATAL_ERROR "Failed to uninstall \"@Z3_DOTNET_ASSEMBLY_NAME@\" from the GAC") endif() z3-z3-4.8.7/src/api/java/000077500000000000000000000000001356505360400147105ustar00rootroot00000000000000z3-z3-4.8.7/src/api/java/AST.java000066400000000000000000000116751356505360400162140ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: AST.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; import com.microsoft.z3.enumerations.Z3_ast_kind; /** * The abstract syntax tree (AST) class. **/ public class AST extends Z3Object implements Comparable { /** * Object comparison. * * @param o another AST **/ @Override public boolean equals(Object o) { if (o == this) return true; if (!(o instanceof AST)) return false; AST casted = (AST) o; return (getContext().nCtx() == casted.getContext().nCtx()) && (Native.isEqAst(getContext().nCtx(), getNativeObject(), casted.getNativeObject())); } /** * Object Comparison. * @param other Another AST * * @return Negative if the object should be sorted before {@code other}, * positive if after else zero. * @throws Z3Exception on error **/ @Override public int compareTo(AST other) { if (other == null) { return 1; } return Integer.compare(getId(), other.getId()); } /** * The AST's hash code. * * @return A hash code **/ @Override public int hashCode() { return Native.getAstHash(getContext().nCtx(), getNativeObject()); } /** * A unique identifier for the AST (unique among all ASTs). * @throws Z3Exception on error **/ public int getId() { return Native.getAstId(getContext().nCtx(), getNativeObject()); } /** * Translates (copies) the AST to the Context {@code ctx}. * @param ctx A context * * @return A copy of the AST which is associated with {@code ctx} * @throws Z3Exception on error **/ public AST translate(Context ctx) { if (getContext() == ctx) { return this; } else { return create(ctx, Native.translate(getContext().nCtx(), getNativeObject(), ctx.nCtx())); } } /** * The kind of the AST. * @throws Z3Exception on error **/ public Z3_ast_kind getASTKind() { return Z3_ast_kind.fromInt(Native.getAstKind(getContext().nCtx(), getNativeObject())); } /** * Indicates whether the AST is an Expr * @throws Z3Exception on error * @throws Z3Exception on error **/ public boolean isExpr() { switch (getASTKind()) { case Z3_APP_AST: case Z3_NUMERAL_AST: case Z3_QUANTIFIER_AST: case Z3_VAR_AST: return true; default: return false; } } /** * Indicates whether the AST is an application * @return a boolean * @throws Z3Exception on error **/ public boolean isApp() { return this.getASTKind() == Z3_ast_kind.Z3_APP_AST; } /** * Indicates whether the AST is a BoundVariable. * @return a boolean * @throws Z3Exception on error **/ public boolean isVar() { return this.getASTKind() == Z3_ast_kind.Z3_VAR_AST; } /** * Indicates whether the AST is a Quantifier * @return a boolean * @throws Z3Exception on error **/ public boolean isQuantifier() { return this.getASTKind() == Z3_ast_kind.Z3_QUANTIFIER_AST; } /** * Indicates whether the AST is a Sort **/ public boolean isSort() { return this.getASTKind() == Z3_ast_kind.Z3_SORT_AST; } /** * Indicates whether the AST is a FunctionDeclaration **/ public boolean isFuncDecl() { return this.getASTKind() == Z3_ast_kind.Z3_FUNC_DECL_AST; } /** * A string representation of the AST. **/ @Override public String toString() { return Native.astToString(getContext().nCtx(), getNativeObject()); } /** * A string representation of the AST in s-expression notation. **/ public String getSExpr() { return Native.astToString(getContext().nCtx(), getNativeObject()); } AST(Context ctx, long obj) { super(ctx, obj); } @Override void incRef() { Native.incRef(getContext().nCtx(), getNativeObject()); } @Override void addToReferenceQueue() { getContext().getASTDRQ().storeReference(getContext(), this); } static AST create(Context ctx, long obj) { switch (Z3_ast_kind.fromInt(Native.getAstKind(ctx.nCtx(), obj))) { case Z3_FUNC_DECL_AST: return new FuncDecl(ctx, obj); case Z3_QUANTIFIER_AST: return new Quantifier(ctx, obj); case Z3_SORT_AST: return Sort.create(ctx, obj); case Z3_APP_AST: case Z3_NUMERAL_AST: case Z3_VAR_AST: return Expr.create(ctx, obj); default: throw new Z3Exception("Unknown AST kind"); } } } z3-z3-4.8.7/src/api/java/ASTDecRefQueue.java000066400000000000000000000006651356505360400202670ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: ASTDecRefQueue.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; class ASTDecRefQueue extends IDecRefQueue { public ASTDecRefQueue() { super(); } @Override protected void decRef(Context ctx, long obj) { Native.decRef(ctx.nCtx(), obj); } }; z3-z3-4.8.7/src/api/java/ASTMap.java000066400000000000000000000052631356505360400166460ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: ASTMap.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * Map from AST to AST **/ class ASTMap extends Z3Object { /** * Checks whether the map contains the key {@code k}. * @param k An AST * * @return True if {@code k} is a key in the map, false * otherwise. **/ public boolean contains(AST k) { return Native.astMapContains(getContext().nCtx(), getNativeObject(), k.getNativeObject()); } /** * Finds the value associated with the key {@code k}. * Remarks: This function signs an error when {@code k} is not a key in * the map. * @param k An AST * * @throws Z3Exception **/ public AST find(AST k) { return new AST(getContext(), Native.astMapFind(getContext().nCtx(), getNativeObject(), k.getNativeObject())); } /** * Stores or replaces a new key/value pair in the map. * @param k The key AST * @param v The value AST **/ public void insert(AST k, AST v) { Native.astMapInsert(getContext().nCtx(), getNativeObject(), k.getNativeObject(), v.getNativeObject()); } /** * Erases the key {@code k} from the map. * @param k An AST **/ public void erase(AST k) { Native.astMapErase(getContext().nCtx(), getNativeObject(), k.getNativeObject()); } /** * Removes all keys from the map. **/ public void reset() { Native.astMapReset(getContext().nCtx(), getNativeObject()); } /** * The size of the map **/ public int size() { return Native.astMapSize(getContext().nCtx(), getNativeObject()); } /** * The keys stored in the map. * * @throws Z3Exception **/ public AST[] getKeys() { ASTVector av = new ASTVector(getContext(), Native.astMapKeys(getContext().nCtx(), getNativeObject())); return av.ToArray(); } /** * Retrieves a string representation of the map. **/ @Override public String toString() { return Native.astMapToString(getContext().nCtx(), getNativeObject()); } ASTMap(Context ctx, long obj) { super(ctx, obj); } ASTMap(Context ctx) { super(ctx, Native.mkAstMap(ctx.nCtx())); } @Override void incRef() { Native.astMapIncRef(getContext().nCtx(), getNativeObject()); } @Override void addToReferenceQueue() { getContext().getASTMapDRQ().storeReference(getContext(), this); } } z3-z3-4.8.7/src/api/java/ASTVector.java000066400000000000000000000137321356505360400173730ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: ASTVector.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * Vectors of ASTs. **/ public class ASTVector extends Z3Object { /** * The size of the vector **/ public int size() { return Native.astVectorSize(getContext().nCtx(), getNativeObject()); } /** * Retrieves the i-th object in the vector. * Remarks: May throw an {@code IndexOutOfBoundsException} when * {@code i} is out of range. * @param i Index * * @return An AST * @throws Z3Exception **/ public AST get(int i) { return new AST(getContext(), Native.astVectorGet(getContext().nCtx(), getNativeObject(), i)); } public void set(int i, AST value) { Native.astVectorSet(getContext().nCtx(), getNativeObject(), i, value.getNativeObject()); } /** * Resize the vector to {@code newSize}. * @param newSize The new size of the vector. **/ public void resize(int newSize) { Native.astVectorResize(getContext().nCtx(), getNativeObject(), newSize); } /** * Add the AST {@code a} to the back of the vector. The size is * increased by 1. * @param a An AST **/ public void push(AST a) { Native.astVectorPush(getContext().nCtx(), getNativeObject(), a.getNativeObject()); } /** * Translates all ASTs in the vector to {@code ctx}. * @param ctx A context * * @return A new ASTVector * @throws Z3Exception **/ public ASTVector translate(Context ctx) { return new ASTVector(getContext(), Native.astVectorTranslate(getContext() .nCtx(), getNativeObject(), ctx.nCtx())); } /** * Retrieves a string representation of the vector. **/ @Override public String toString() { return Native.astVectorToString(getContext().nCtx(), getNativeObject()); } ASTVector(Context ctx, long obj) { super(ctx, obj); } ASTVector(Context ctx) { super(ctx, Native.mkAstVector(ctx.nCtx())); } @Override void incRef() { Native.astVectorIncRef(getContext().nCtx(), getNativeObject()); } @Override void addToReferenceQueue() { getContext().getASTVectorDRQ().storeReference(getContext(), this); } /** * Translates the AST vector into an AST[] * */ public AST[] ToArray() { int n = size(); AST[] res = new AST[n]; for (int i = 0; i < n; i++) res[i] = AST.create(getContext(), get(i).getNativeObject()); return res; } /** * Translates the AST vector into an Expr[] * */ public Expr[] ToExprArray() { int n = size(); Expr[] res = new Expr[n]; for (int i = 0; i < n; i++) res[i] = Expr.create(getContext(), get(i).getNativeObject()); return res; } /** * Translates the AST vector into an BoolExpr[] * */ public BoolExpr[] ToBoolExprArray() { int n = size(); BoolExpr[] res = new BoolExpr[n]; for (int i = 0; i < n; i++) res[i] = (BoolExpr) Expr.create(getContext(), get(i).getNativeObject()); return res; } /** * Translates the AST vector into an BitVecExpr[] * */ public BitVecExpr[] ToBitVecExprArray() { int n = size(); BitVecExpr[] res = new BitVecExpr[n]; for (int i = 0; i < n; i++) res[i] = (BitVecExpr)Expr.create(getContext(), get(i).getNativeObject()); return res; } /** * Translates the AST vector into an ArithExpr[] * */ public ArithExpr[] ToArithExprExprArray() { int n = size(); ArithExpr[] res = new ArithExpr[n]; for (int i = 0; i < n; i++) res[i] = (ArithExpr)Expr.create(getContext(), get(i).getNativeObject()); return res; } /** * Translates the AST vector into an ArrayExpr[] * */ public ArrayExpr[] ToArrayExprArray() { int n = size(); ArrayExpr[] res = new ArrayExpr[n]; for (int i = 0; i < n; i++) res[i] = (ArrayExpr)Expr.create(getContext(), get(i).getNativeObject()); return res; } /** * Translates the AST vector into an DatatypeExpr[] * */ public DatatypeExpr[] ToDatatypeExprArray() { int n = size(); DatatypeExpr[] res = new DatatypeExpr[n]; for (int i = 0; i < n; i++) res[i] = (DatatypeExpr)Expr.create(getContext(), get(i).getNativeObject()); return res; } /** * Translates the AST vector into an FPExpr[] * */ public FPExpr[] ToFPExprArray() { int n = size(); FPExpr[] res = new FPExpr[n]; for (int i = 0; i < n; i++) res[i] = (FPExpr)Expr.create(getContext(), get(i).getNativeObject()); return res; } /** * Translates the AST vector into an FPRMExpr[] * */ public FPRMExpr[] ToFPRMExprArray() { int n = size(); FPRMExpr[] res = new FPRMExpr[n]; for (int i = 0; i < n; i++) res[i] = (FPRMExpr)Expr.create(getContext(), get(i).getNativeObject()); return res; } /** * Translates the AST vector into an IntExpr[] * */ public IntExpr[] ToIntExprArray() { int n = size(); IntExpr[] res = new IntExpr[n]; for (int i = 0; i < n; i++) res[i] = (IntExpr)Expr.create(getContext(), get(i).getNativeObject()); return res; } /** * Translates the AST vector into an RealExpr[] * */ public RealExpr[] ToRealExprArray() { int n = size(); RealExpr[] res = new RealExpr[n]; for (int i = 0; i < n; i++) res[i] = (RealExpr)Expr.create(getContext(), get(i).getNativeObject()); return res; } }z3-z3-4.8.7/src/api/java/AlgebraicNum.java000066400000000000000000000034641356505360400201130ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: AlgebraicNum.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * Algebraic numbers **/ public class AlgebraicNum extends ArithExpr { /** * Return a upper bound for a given real algebraic number. The interval * isolating the number is smaller than 1/10^{@code precision}. * * @see Expr#isAlgebraicNumber * @param precision the precision of the result * * @return A numeral Expr of sort Real * @throws Z3Exception on error **/ public RatNum toUpper(int precision) { return new RatNum(getContext(), Native.getAlgebraicNumberUpper(getContext() .nCtx(), getNativeObject(), precision)); } /** * Return a lower bound for the given real algebraic number. The interval * isolating the number is smaller than 1/10^{@code precision}. * * @see Expr#isAlgebraicNumber * @param precision precision * * @return A numeral Expr of sort Real * @throws Z3Exception on error **/ public RatNum toLower(int precision) { return new RatNum(getContext(), Native.getAlgebraicNumberLower(getContext() .nCtx(), getNativeObject(), precision)); } /** * Returns a string representation in decimal notation. * Remarks: The result has at most {@code precision} decimal places. * @param precision precision * @return String * @throws Z3Exception on error **/ public String toDecimal(int precision) { return Native.getNumeralDecimalString(getContext().nCtx(), getNativeObject(), precision); } AlgebraicNum(Context ctx, long obj) { super(ctx, obj); } } z3-z3-4.8.7/src/api/java/ApplyResult.java000066400000000000000000000027731356505360400200500ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: ApplyResult.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * ApplyResult objects represent the result of an application of a tactic to a * goal. It contains the subgoals that were produced. **/ public class ApplyResult extends Z3Object { /** * The number of Subgoals. **/ public int getNumSubgoals() { return Native.applyResultGetNumSubgoals(getContext().nCtx(), getNativeObject()); } /** * Retrieves the subgoals from the ApplyResult. * * @throws Z3Exception **/ public Goal[] getSubgoals() { int n = getNumSubgoals(); Goal[] res = new Goal[n]; for (int i = 0; i < n; i++) res[i] = new Goal(getContext(), Native.applyResultGetSubgoal(getContext().nCtx(), getNativeObject(), i)); return res; } /** * A string representation of the ApplyResult. **/ @Override public String toString() { return Native.applyResultToString(getContext().nCtx(), getNativeObject()); } ApplyResult(Context ctx, long obj) { super(ctx, obj); } @Override void incRef() { Native.applyResultIncRef(getContext().nCtx(), getNativeObject()); } @Override void addToReferenceQueue() { getContext().getApplyResultDRQ().storeReference(getContext(), this); } } z3-z3-4.8.7/src/api/java/ApplyResultDecRefQueue.java000066400000000000000000000007401356505360400221160ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: ApplyResultDecRefQueue.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; class ApplyResultDecRefQueue extends IDecRefQueue { public ApplyResultDecRefQueue() { super(); } @Override protected void decRef(Context ctx, long obj) { Native.applyResultDecRef(ctx.nCtx(), obj); } }; z3-z3-4.8.7/src/api/java/ArithExpr.java000066400000000000000000000006441356505360400174650ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: ArithExpr.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * Arithmetic expressions (int/real) **/ public class ArithExpr extends Expr { /** * Constructor for ArithExpr **/ ArithExpr(Context ctx, long obj) { super(ctx, obj); } } z3-z3-4.8.7/src/api/java/ArithSort.java000066400000000000000000000005701356505360400174740ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: ArithSort.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * An arithmetic sort, i.e., Int or Real. **/ public class ArithSort extends Sort { ArithSort(Context ctx, long obj) { super(ctx, obj); } }; z3-z3-4.8.7/src/api/java/ArrayExpr.java000066400000000000000000000006251356505360400174730ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: ArrayExpr.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * Array expressions **/ public class ArrayExpr extends Expr { /** * Constructor for ArrayExpr **/ ArrayExpr(Context ctx, long obj) { super(ctx, obj); } } z3-z3-4.8.7/src/api/java/ArraySort.java000066400000000000000000000025051356505360400175030ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: ArraySort.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * Array sorts. **/ public class ArraySort extends Sort { /** * The domain of the array sort. * @throws Z3Exception * @throws Z3Exception on error * @return a sort **/ public Sort getDomain() { return Sort.create(getContext(), Native.getArraySortDomain(getContext().nCtx(), getNativeObject())); } /** * The range of the array sort. * @throws Z3Exception * @throws Z3Exception on error * @return a sort **/ public Sort getRange() { return Sort.create(getContext(), Native.getArraySortRange(getContext().nCtx(), getNativeObject())); } ArraySort(Context ctx, long obj) { super(ctx, obj); } ArraySort(Context ctx, Sort domain, Sort range) { super(ctx, Native.mkArraySort(ctx.nCtx(), domain.getNativeObject(), range.getNativeObject())); } ArraySort(Context ctx, Sort[] domains, Sort range) { super(ctx, Native.mkArraySortN(ctx.nCtx(), domains.length, AST.arrayToNative(domains), range.getNativeObject())); } }; z3-z3-4.8.7/src/api/java/AstMapDecRefQueue.java000066400000000000000000000007061356505360400210210ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: AstMapDecRefQueue.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; class ASTMapDecRefQueue extends IDecRefQueue { public ASTMapDecRefQueue() { super(); } @Override protected void decRef(Context ctx, long obj) { Native.astMapDecRef(ctx.nCtx(), obj); } } z3-z3-4.8.7/src/api/java/AstVectorDecRefQueue.java000066400000000000000000000007251356505360400215470ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: AstVectorDecRefQueue.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; class ASTVectorDecRefQueue extends IDecRefQueue { public ASTVectorDecRefQueue() { super(); } @Override protected void decRef(Context ctx, long obj) { Native.astVectorDecRef(ctx.nCtx(), obj); } } z3-z3-4.8.7/src/api/java/BitVecExpr.java000066400000000000000000000012241356505360400175650ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: BitVecExpr.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * Bit-vector expressions **/ public class BitVecExpr extends Expr { /** * The size of the sort of a bit-vector term. * @throws Z3Exception * @throws Z3Exception on error * @return an int **/ public int getSortSize() { return ((BitVecSort) getSort()).getSize(); } /** * Constructor for BitVecExpr **/ BitVecExpr(Context ctx, long obj) { super(ctx, obj); } } z3-z3-4.8.7/src/api/java/BitVecNum.java000066400000000000000000000026651356505360400174200ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: BitVecNum.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; import java.math.BigInteger; /** * Bit-vector numerals **/ public class BitVecNum extends BitVecExpr { /** * Retrieve the int value. * * @throws Z3Exception **/ public int getInt() { Native.IntPtr res = new Native.IntPtr(); if (!Native.getNumeralInt(getContext().nCtx(), getNativeObject(), res)) { throw new Z3Exception("Numeral is not an int"); } return res.value; } /** * Retrieve the 64-bit int value. * * @throws Z3Exception **/ public long getLong() { Native.LongPtr res = new Native.LongPtr(); if (!Native.getNumeralInt64(getContext().nCtx(), getNativeObject(), res)) { throw new Z3Exception("Numeral is not a long"); } return res.value; } /** * Retrieve the BigInteger value. **/ public BigInteger getBigInteger() { return new BigInteger(this.toString()); } /** * Returns a string representation of the numeral. **/ @Override public String toString() { return Native.getNumeralString(getContext().nCtx(), getNativeObject()); } BitVecNum(Context ctx, long obj) { super(ctx, obj); } } z3-z3-4.8.7/src/api/java/BitVecSort.java000066400000000000000000000011141356505360400175740ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: BitVecSort.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * Bit-vector sorts. **/ public class BitVecSort extends Sort { /** * The size of the bit-vector sort. * @throws Z3Exception on error * @return an int **/ public int getSize() { return Native.getBvSortSize(getContext().nCtx(), getNativeObject()); } BitVecSort(Context ctx, long obj) { super(ctx, obj); } }; z3-z3-4.8.7/src/api/java/BoolExpr.java000066400000000000000000000007231356505360400173070ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: BoolExpr.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * Boolean expressions **/ public class BoolExpr extends Expr { /** * Constructor for BoolExpr * @throws Z3Exception * @throws Z3Exception on error **/ BoolExpr(Context ctx, long obj) { super(ctx, obj); } } z3-z3-4.8.7/src/api/java/BoolSort.java000066400000000000000000000006371356505360400173240ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: BoolSort.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * A Boolean sort. **/ public class BoolSort extends Sort { BoolSort(Context ctx, long obj) { super(ctx, obj); { }} BoolSort(Context ctx) { super(ctx, Native.mkBoolSort(ctx.nCtx())); { }} }; z3-z3-4.8.7/src/api/java/CMakeLists.txt000066400000000000000000000154341356505360400174570ustar00rootroot00000000000000find_package(Java REQUIRED) find_package(JNI REQUIRED) include(UseJava) # Sanity check for dirty source tree foreach (file_name "enumerations" "Native.cpp" "Native.java") if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${file_name}") message(FATAL_ERROR "\"${CMAKE_CURRENT_SOURCE_DIR}/${file_name}\"" ${z3_polluted_tree_msg}) endif() endforeach() set(Z3_JAVA_PACKAGE_NAME "com.microsoft.z3") # Rule to generate ``Native.java`` and ``Native.cpp`` set(Z3_JAVA_NATIVE_JAVA "${CMAKE_CURRENT_BINARY_DIR}/Native.java") set(Z3_JAVA_NATIVE_CPP "${CMAKE_CURRENT_BINARY_DIR}/Native.cpp") add_custom_command(OUTPUT "${Z3_JAVA_NATIVE_JAVA}" "${Z3_JAVA_NATIVE_CPP}" COMMAND "${PYTHON_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/scripts/update_api.py" ${Z3_FULL_PATH_API_HEADER_FILES_TO_SCAN} "--java-output-dir" "${CMAKE_CURRENT_BINARY_DIR}" "--java-package-name" ${Z3_JAVA_PACKAGE_NAME} DEPENDS ${Z3_FULL_PATH_API_HEADER_FILES_TO_SCAN} "${PROJECT_SOURCE_DIR}/scripts/update_api.py" ${Z3_GENERATED_FILE_EXTRA_DEPENDENCIES} # FIXME: When update_api.py no longer uses ``mk_util`` drop this dependency "${PROJECT_SOURCE_DIR}/scripts/mk_util.py" COMMENT "Generating \"${Z3_JAVA_NATIVE_JAVA}\" and \"${Z3_JAVA_NATIVE_CPP}\"" ${ADD_CUSTOM_COMMAND_USES_TERMINAL_ARG} ) # Add rule to build native code that provides a bridge between # ``Native.java`` and libz3's interfac3. 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. 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}) target_include_directories(z3java PRIVATE "${PROJECT_SOURCE_DIR}/src/api" "${PROJECT_BINARY_DIR}/src/api" ${JNI_INCLUDE_DIRS} ) # FIXME: Should this library have SONAME and VERSION set? # This prevents CMake from automatically defining ``z3java_EXPORTS`` set_property(TARGET z3java PROPERTY DEFINE_SYMBOL "") # Rule to generate the ``com.microsoft.z3.enumerations`` package # FIXME: This list of files is fragile set(Z3_JAVA_ENUMERATION_PACKAGE_FILES Z3_ast_kind.java Z3_ast_print_mode.java Z3_decl_kind.java Z3_error_code.java Z3_goal_prec.java Z3_lbool.java Z3_param_kind.java Z3_parameter_kind.java Z3_sort_kind.java Z3_symbol_kind.java ) set(Z3_JAVA_ENUMERATION_PACKAGE_FILES_FULL_PATH "") foreach (enum_file ${Z3_JAVA_ENUMERATION_PACKAGE_FILES}) list(APPEND Z3_JAVA_ENUMERATION_PACKAGE_FILES_FULL_PATH "${CMAKE_CURRENT_BINARY_DIR}/enumerations/${enum_file}" ) endforeach() add_custom_command(OUTPUT ${Z3_JAVA_ENUMERATION_PACKAGE_FILES_FULL_PATH} COMMAND "${PYTHON_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/scripts/mk_consts_files.py" ${Z3_FULL_PATH_API_HEADER_FILES_TO_SCAN} "--java-output-dir" "${CMAKE_CURRENT_BINARY_DIR}" "--java-package-name" ${Z3_JAVA_PACKAGE_NAME} DEPENDS ${Z3_FULL_PATH_API_HEADER_FILES_TO_SCAN} "${PROJECT_SOURCE_DIR}/scripts/mk_consts_files.py" ${Z3_GENERATED_FILE_EXTRA_DEPENDENCIES} COMMENT "Generating ${Z3_JAVA_PACKAGE_NAME}.enumerations package" ${ADD_CUSTOM_COMMAND_USES_TERMINAL_ARG} ) set(Z3_JAVA_JAR_SOURCE_FILES AlgebraicNum.java ApplyResultDecRefQueue.java ApplyResult.java ArithExpr.java ArithSort.java ArrayExpr.java ArraySort.java ASTDecRefQueue.java AST.java AstMapDecRefQueue.java ASTMap.java AstVectorDecRefQueue.java ASTVector.java BitVecExpr.java BitVecNum.java BitVecSort.java BoolExpr.java BoolSort.java ConstructorDecRefQueue.java Constructor.java ConstructorListDecRefQueue.java ConstructorList.java Context.java DatatypeExpr.java DatatypeSort.java EnumSort.java Expr.java FiniteDomainExpr.java FiniteDomainNum.java FiniteDomainSort.java FixedpointDecRefQueue.java Fixedpoint.java FPExpr.java FPNum.java FPRMExpr.java FPRMNum.java FPRMSort.java FPSort.java FuncDecl.java FuncInterpDecRefQueue.java FuncInterpEntryDecRefQueue.java FuncInterp.java Global.java GoalDecRefQueue.java Goal.java IDecRefQueue.java IntExpr.java IntNum.java IntSort.java IntSymbol.java Lambda.java ListSort.java Log.java ModelDecRefQueue.java Model.java OptimizeDecRefQueue.java Optimize.java ParamDescrsDecRefQueue.java ParamDescrs.java ParamsDecRefQueue.java Params.java Pattern.java ProbeDecRefQueue.java Probe.java Quantifier.java RatNum.java RealExpr.java RealSort.java ReExpr.java RelationSort.java ReSort.java SeqExpr.java SeqSort.java SetSort.java SolverDecRefQueue.java Solver.java Sort.java StatisticsDecRefQueue.java Statistics.java Status.java StringSymbol.java Symbol.java TacticDecRefQueue.java Tactic.java TupleSort.java UninterpretedSort.java Version.java Z3Exception.java Z3Object.java ) set(Z3_JAVA_JAR_SOURCE_FILES_FULL_PATH "") foreach (java_src_file ${Z3_JAVA_JAR_SOURCE_FILES}) list(APPEND Z3_JAVA_JAR_SOURCE_FILES_FULL_PATH "${CMAKE_CURRENT_SOURCE_DIR}/${java_src_file}") endforeach() # Add generated files to list list(APPEND Z3_JAVA_JAR_SOURCE_FILES_FULL_PATH ${Z3_JAVA_NATIVE_JAVA} ${Z3_JAVA_ENUMERATION_PACKAGE_FILES_FULL_PATH} ) # Convenient top-level target add_custom_target(build_z3_java_bindings ALL DEPENDS z3java z3JavaJar ) # Rule to build ``com.microsoft.z3.jar`` # TODO: Should we set ``CMAKE_JNI_TARGET`` to ``TRUE``? add_jar(z3JavaJar SOURCES ${Z3_JAVA_JAR_SOURCE_FILES_FULL_PATH} OUTPUT_NAME ${Z3_JAVA_PACKAGE_NAME} OUTPUT_DIR "${PROJECT_BINARY_DIR}" VERSION "${Z3_VERSION}" ) ############################################################################### # Install ############################################################################### option(Z3_INSTALL_JAVA_BINDINGS "Install Java bindings when invoking install target" ON) if (Z3_INSTALL_JAVA_BINDINGS) # Provide cache variables for the install locations that the user can change. # This defaults to ``/usr/local/java`` which seems to be the location for ``.jar`` # files on Linux distributions set(Z3_JAVA_JAR_INSTALLDIR "${CMAKE_INSTALL_DATAROOTDIR}/java" CACHE PATH "Directory to install Z3 Java jar file relative to install prefix" ) # FIXME: I don't think this the right installation location set(Z3_JAVA_JNI_LIB_INSTALLDIR "${CMAKE_INSTALL_LIBDIR}" CACHE PATH "Directory to install Z3 Java JNI bridge library relative to install prefix" ) install(TARGETS z3java DESTINATION "${Z3_JAVA_JNI_LIB_INSTALLDIR}") # Note: Don't use ``DESTINATION`` here as the version of ``UseJava.cmake`` shipped # with CMake 2.8.12.2 handles that incorrectly. install_jar(z3JavaJar "${Z3_JAVA_JAR_INSTALLDIR}") endif() z3-z3-4.8.7/src/api/java/Constructor.java000066400000000000000000000064401356505360400201040ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: Constructor.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * Constructors are used for datatype sorts. **/ public class Constructor extends Z3Object { private final int n; Constructor(Context ctx, int n, long nativeObj) { super(ctx, nativeObj); this.n = n; } /** * The number of fields of the constructor. * @throws Z3Exception * @throws Z3Exception on error * @return an int **/ public int getNumFields() { return n; } /** * The function declaration of the constructor. * @throws Z3Exception * @throws Z3Exception on error **/ public FuncDecl ConstructorDecl() { Native.LongPtr constructor = new Native.LongPtr(); Native.LongPtr tester = new Native.LongPtr(); long[] accessors = new long[n]; Native.queryConstructor(getContext().nCtx(), getNativeObject(), n, constructor, tester, accessors); return new FuncDecl(getContext(), constructor.value); } /** * The function declaration of the tester. * @throws Z3Exception * @throws Z3Exception on error **/ public FuncDecl getTesterDecl() { Native.LongPtr constructor = new Native.LongPtr(); Native.LongPtr tester = new Native.LongPtr(); long[] accessors = new long[n]; Native.queryConstructor(getContext().nCtx(), getNativeObject(), n, constructor, tester, accessors); return new FuncDecl(getContext(), tester.value); } /** * The function declarations of the accessors * @throws Z3Exception * @throws Z3Exception on error **/ public FuncDecl[] getAccessorDecls() { Native.LongPtr constructor = new Native.LongPtr(); Native.LongPtr tester = new Native.LongPtr(); long[] accessors = new long[n]; Native.queryConstructor(getContext().nCtx(), getNativeObject(), n, constructor, tester, accessors); FuncDecl[] t = new FuncDecl[n]; for (int i = 0; i < n; i++) t[i] = new FuncDecl(getContext(), accessors[i]); return t; } @Override void incRef() { // Datatype constructors are not reference counted. } @Override void addToReferenceQueue() { getContext().getConstructorDRQ().storeReference(getContext(), this); } static Constructor of(Context ctx, Symbol name, Symbol recognizer, Symbol[] fieldNames, Sort[] sorts, int[] sortRefs) { int n = AST.arrayLength(fieldNames); if (n != AST.arrayLength(sorts)) throw new Z3Exception( "Number of field names does not match number of sorts"); if (sortRefs != null && sortRefs.length != n) throw new Z3Exception( "Number of field names does not match number of sort refs"); if (sortRefs == null) sortRefs = new int[n]; long nativeObj = Native.mkConstructor(ctx.nCtx(), name.getNativeObject(), recognizer.getNativeObject(), n, Symbol.arrayToNative(fieldNames), Sort.arrayToNative(sorts), sortRefs); return new Constructor(ctx, n, nativeObj); } } z3-z3-4.8.7/src/api/java/ConstructorDecRefQueue.java000066400000000000000000000004321356505360400221550ustar00rootroot00000000000000package com.microsoft.z3; public class ConstructorDecRefQueue extends IDecRefQueue { public ConstructorDecRefQueue() { super(); } @Override protected void decRef(Context ctx, long obj) { Native.delConstructor(ctx.nCtx(), obj); } } z3-z3-4.8.7/src/api/java/ConstructorList.java000066400000000000000000000015071356505360400207370ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: ConstructorList.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * Lists of constructors **/ public class ConstructorList extends Z3Object { ConstructorList(Context ctx, long obj) { super(ctx, obj); } @Override void incRef() { // Constructor lists are not reference counted. } @Override void addToReferenceQueue() { getContext().getConstructorListDRQ().storeReference(getContext(), this); } ConstructorList(Context ctx, Constructor[] constructors) { super(ctx, Native.mkConstructorList(ctx.nCtx(), constructors.length, Constructor.arrayToNative(constructors))); } } z3-z3-4.8.7/src/api/java/ConstructorListDecRefQueue.java000066400000000000000000000004521356505360400230130ustar00rootroot00000000000000package com.microsoft.z3; public class ConstructorListDecRefQueue extends IDecRefQueue { public ConstructorListDecRefQueue() { super(); } @Override protected void decRef(Context ctx, long obj) { Native.delConstructorList(ctx.nCtx(), obj); } } z3-z3-4.8.7/src/api/java/Context.java000066400000000000000000003672531356505360400172170ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: Context.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; import static com.microsoft.z3.Constructor.of; import com.microsoft.z3.enumerations.Z3_ast_print_mode; import java.util.Map; /** * The main interaction with Z3 happens via the Context. * For applications that spawn an unbounded number of contexts, * the proper use is within a try-with-resources * scope so that the Context object gets garbage collected in * a predictable way. Contexts maintain all data-structures * related to terms and formulas that are created relative * to them. **/ public class Context implements AutoCloseable { private long m_ctx; static final Object creation_lock = new Object(); public Context () { synchronized (creation_lock) { m_ctx = Native.mkContextRc(0); init(); } } protected Context (long m_ctx) { synchronized (creation_lock) { this.m_ctx = m_ctx; init(); } } /** * Constructor. * Remarks: * The following parameters can be set: * - proof (Boolean) Enable proof generation * - debug_ref_count (Boolean) Enable debug support for Z3_ast reference counting * - trace (Boolean) Tracing support for VCC * - trace_file_name (String) Trace out file for VCC traces * - timeout (unsigned) default timeout (in milliseconds) used for solvers * - well_sorted_check type checker * - auto_config use heuristics to automatically select solver and configure it * - model model generation for solvers, this parameter can be overwritten when creating a solver * - model_validate validate models produced by solvers * - unsat_core unsat-core generation for solvers, this parameter can be overwritten when creating a solver * Note that in previous versions of Z3, this constructor was also used to set global and * module parameters. For this purpose we should now use {@code Global.setParameter} **/ public Context(Map settings) { synchronized (creation_lock) { long cfg = Native.mkConfig(); for (Map.Entry kv : settings.entrySet()) { Native.setParamValue(cfg, kv.getKey(), kv.getValue()); } m_ctx = Native.mkContextRc(cfg); Native.delConfig(cfg); init(); } } private void init() { setPrintMode(Z3_ast_print_mode.Z3_PRINT_SMTLIB2_COMPLIANT); Native.setInternalErrorHandler(m_ctx); } /** * Creates a new symbol using an integer. * Remarks: Not all integers can be passed to this function. * The legal range of unsigned integers is 0 to 2^30-1. **/ public IntSymbol mkSymbol(int i) { return new IntSymbol(this, i); } /** * Create a symbol using a string. **/ public StringSymbol mkSymbol(String name) { return new StringSymbol(this, name); } /** * Create an array of symbols. **/ Symbol[] mkSymbols(String[] names) { if (names == null) return null; Symbol[] result = new Symbol[names.length]; for (int i = 0; i < names.length; ++i) result[i] = mkSymbol(names[i]); return result; } private BoolSort m_boolSort = null; private IntSort m_intSort = null; private RealSort m_realSort = null; private SeqSort m_stringSort = null; /** * Retrieves the Boolean sort of the context. **/ public BoolSort getBoolSort() { if (m_boolSort == null) { m_boolSort = new BoolSort(this); } return m_boolSort; } /** * Retrieves the Integer sort of the context. **/ public IntSort getIntSort() { if (m_intSort == null) { m_intSort = new IntSort(this); } return m_intSort; } /** * Retrieves the Real sort of the context. **/ public RealSort getRealSort() { if (m_realSort == null) { m_realSort = new RealSort(this); } return m_realSort; } /** * Create a new Boolean sort. **/ public BoolSort mkBoolSort() { return new BoolSort(this); } /** * Retrieves the Integer sort of the context. **/ public SeqSort getStringSort() { if (m_stringSort == null) { m_stringSort = mkStringSort(); } return m_stringSort; } /** * Create a new uninterpreted sort. **/ public UninterpretedSort mkUninterpretedSort(Symbol s) { checkContextMatch(s); return new UninterpretedSort(this, s); } /** * Create a new uninterpreted sort. **/ public UninterpretedSort mkUninterpretedSort(String str) { return mkUninterpretedSort(mkSymbol(str)); } /** * Create a new integer sort. **/ public IntSort mkIntSort() { return new IntSort(this); } /** * Create a real sort. **/ public RealSort mkRealSort() { return new RealSort(this); } /** * Create a new bit-vector sort. **/ public BitVecSort mkBitVecSort(int size) { return new BitVecSort(this, Native.mkBvSort(nCtx(), size)); } /** * Create a new array sort. **/ public ArraySort mkArraySort(Sort domain, Sort range) { checkContextMatch(domain); checkContextMatch(range); return new ArraySort(this, domain, range); } /** * Create a new array sort. **/ public ArraySort mkArraySort(Sort[] domains, Sort range) { checkContextMatch(domains); checkContextMatch(range); return new ArraySort(this, domains, range); } /** * Create a new string sort **/ public SeqSort mkStringSort() { return new SeqSort(this, Native.mkStringSort(nCtx())); } /** * Create a new sequence sort **/ public SeqSort mkSeqSort(Sort s) { return new SeqSort(this, Native.mkSeqSort(nCtx(), s.getNativeObject())); } /** * Create a new regular expression sort **/ public ReSort mkReSort(Sort s) { return new ReSort(this, Native.mkReSort(nCtx(), s.getNativeObject())); } /** * Create a new tuple sort. **/ public TupleSort mkTupleSort(Symbol name, Symbol[] fieldNames, Sort[] fieldSorts) { checkContextMatch(name); checkContextMatch(fieldNames); checkContextMatch(fieldSorts); return new TupleSort(this, name, fieldNames.length, fieldNames, fieldSorts); } /** * Create a new enumeration sort. **/ public EnumSort mkEnumSort(Symbol name, Symbol... enumNames) { checkContextMatch(name); checkContextMatch(enumNames); return new EnumSort(this, name, enumNames); } /** * Create a new enumeration sort. **/ public EnumSort mkEnumSort(String name, String... enumNames) { return new EnumSort(this, mkSymbol(name), mkSymbols(enumNames)); } /** * Create a new list sort. **/ public ListSort mkListSort(Symbol name, Sort elemSort) { checkContextMatch(name); checkContextMatch(elemSort); return new ListSort(this, name, elemSort); } /** * Create a new list sort. **/ public ListSort mkListSort(String name, Sort elemSort) { checkContextMatch(elemSort); return new ListSort(this, mkSymbol(name), elemSort); } /** * Create a new finite domain sort. **/ public FiniteDomainSort mkFiniteDomainSort(Symbol name, long size) { checkContextMatch(name); return new FiniteDomainSort(this, name, size); } /** * Create a new finite domain sort. **/ public FiniteDomainSort mkFiniteDomainSort(String name, long size) { return new FiniteDomainSort(this, mkSymbol(name), size); } /** * Create a datatype constructor. * @param name constructor name * @param recognizer name of recognizer function. * @param fieldNames names of the constructor fields. * @param sorts field sorts, 0 if the field sort refers to a recursive sort. * @param sortRefs reference to datatype sort that is an argument to the * constructor; if the corresponding sort reference is 0, then the value in sort_refs should be * an index referring to one of the recursive datatypes that is * declared. **/ public Constructor mkConstructor(Symbol name, Symbol recognizer, Symbol[] fieldNames, Sort[] sorts, int[] sortRefs) { return of(this, name, recognizer, fieldNames, sorts, sortRefs); } /** * Create a datatype constructor. **/ public Constructor mkConstructor(String name, String recognizer, String[] fieldNames, Sort[] sorts, int[] sortRefs) { return of(this, mkSymbol(name), mkSymbol(recognizer), mkSymbols(fieldNames), sorts, sortRefs); } /** * Create a new datatype sort. **/ public DatatypeSort mkDatatypeSort(Symbol name, Constructor[] constructors) { checkContextMatch(name); checkContextMatch(constructors); return new DatatypeSort(this, name, constructors); } /** * Create a new datatype sort. **/ public DatatypeSort mkDatatypeSort(String name, Constructor[] constructors) { checkContextMatch(constructors); return new DatatypeSort(this, mkSymbol(name), constructors); } /** * Create mutually recursive datatypes. * @param names names of datatype sorts * @param c list of constructors, one list per sort. **/ public DatatypeSort[] mkDatatypeSorts(Symbol[] names, Constructor[][] c) { checkContextMatch(names); int n = names.length; ConstructorList[] cla = new ConstructorList[n]; long[] n_constr = new long[n]; for (int i = 0; i < n; i++) { Constructor[] constructor = c[i]; checkContextMatch(constructor); cla[i] = new ConstructorList(this, constructor); n_constr[i] = cla[i].getNativeObject(); } long[] n_res = new long[n]; Native.mkDatatypes(nCtx(), n, Symbol.arrayToNative(names), n_res, n_constr); DatatypeSort[] res = new DatatypeSort[n]; for (int i = 0; i < n; i++) res[i] = new DatatypeSort(this, n_res[i]); return res; } /** * Create mutually recursive data-types. **/ public DatatypeSort[] mkDatatypeSorts(String[] names, Constructor[][] c) { return mkDatatypeSorts(mkSymbols(names), c); } /** * 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 remaining fields of t are unchanged. **/ public Expr mkUpdateField(FuncDecl field, Expr t, Expr v) throws Z3Exception { return Expr.create (this, Native.datatypeUpdateField (nCtx(), field.getNativeObject(), t.getNativeObject(), v.getNativeObject())); } /** * Creates a new function declaration. **/ public FuncDecl mkFuncDecl(Symbol name, Sort[] domain, Sort range) { checkContextMatch(name); checkContextMatch(domain); checkContextMatch(range); return new FuncDecl(this, name, domain, range); } /** * Creates a new function declaration. **/ public FuncDecl mkFuncDecl(Symbol name, Sort domain, Sort range) { checkContextMatch(name); checkContextMatch(domain); checkContextMatch(range); Sort[] q = new Sort[] { domain }; return new FuncDecl(this, name, q, range); } /** * Creates a new function declaration. **/ public FuncDecl mkFuncDecl(String name, Sort[] domain, Sort range) { checkContextMatch(domain); checkContextMatch(range); return new FuncDecl(this, mkSymbol(name), domain, range); } /** * Creates a new function declaration. **/ public FuncDecl mkFuncDecl(String name, Sort domain, Sort range) { checkContextMatch(domain); checkContextMatch(range); Sort[] q = new Sort[] { domain }; return new FuncDecl(this, mkSymbol(name), q, range); } /** * Creates a fresh function declaration with a name prefixed with * {@code prefix}. * @see #mkFuncDecl(String,Sort,Sort) * @see #mkFuncDecl(String,Sort[],Sort) **/ public FuncDecl mkFreshFuncDecl(String prefix, Sort[] domain, Sort range) { checkContextMatch(domain); checkContextMatch(range); return new FuncDecl(this, prefix, domain, range); } /** * Creates a new constant function declaration. **/ public FuncDecl mkConstDecl(Symbol name, Sort range) { checkContextMatch(name); checkContextMatch(range); return new FuncDecl(this, name, null, range); } /** * Creates a new constant function declaration. **/ public FuncDecl mkConstDecl(String name, Sort range) { checkContextMatch(range); return new FuncDecl(this, mkSymbol(name), null, range); } /** * Creates a fresh constant function declaration with a name prefixed with * {@code prefix}. * @see #mkFuncDecl(String,Sort,Sort) * @see #mkFuncDecl(String,Sort[],Sort) **/ public FuncDecl mkFreshConstDecl(String prefix, Sort range) { checkContextMatch(range); return new FuncDecl(this, prefix, null, range); } /** * Creates a new bound variable. * @param index The de-Bruijn index of the variable * @param ty The sort of the variable **/ public Expr mkBound(int index, Sort ty) { return Expr.create(this, Native.mkBound(nCtx(), index, ty.getNativeObject())); } /** * Create a quantifier pattern. **/ public Pattern mkPattern(Expr... terms) { if (terms.length == 0) throw new Z3Exception("Cannot create a pattern from zero terms"); long[] termsNative = AST.arrayToNative(terms); return new Pattern(this, Native.mkPattern(nCtx(), terms.length, termsNative)); } /** * Creates a new Constant of sort {@code range} and named * {@code name}. **/ public Expr mkConst(Symbol name, Sort range) { checkContextMatch(name); checkContextMatch(range); return Expr.create( this, Native.mkConst(nCtx(), name.getNativeObject(), range.getNativeObject())); } /** * Creates a new Constant of sort {@code range} and named * {@code name}. **/ public Expr mkConst(String name, Sort range) { return mkConst(mkSymbol(name), range); } /** * Creates a fresh Constant of sort {@code range} and a name * prefixed with {@code prefix}. **/ public Expr mkFreshConst(String prefix, Sort range) { checkContextMatch(range); return Expr.create(this, Native.mkFreshConst(nCtx(), prefix, range.getNativeObject())); } /** * Creates a fresh constant from the FuncDecl {@code f}. * @param f A decl of a 0-arity function **/ public Expr mkConst(FuncDecl f) { return mkApp(f, (Expr[]) null); } /** * Create a Boolean constant. **/ public BoolExpr mkBoolConst(Symbol name) { return (BoolExpr) mkConst(name, getBoolSort()); } /** * Create a Boolean constant. **/ public BoolExpr mkBoolConst(String name) { return (BoolExpr) mkConst(mkSymbol(name), getBoolSort()); } /** * Creates an integer constant. **/ public IntExpr mkIntConst(Symbol name) { return (IntExpr) mkConst(name, getIntSort()); } /** * Creates an integer constant. **/ public IntExpr mkIntConst(String name) { return (IntExpr) mkConst(name, getIntSort()); } /** * Creates a real constant. **/ public RealExpr mkRealConst(Symbol name) { return (RealExpr) mkConst(name, getRealSort()); } /** * Creates a real constant. **/ public RealExpr mkRealConst(String name) { return (RealExpr) mkConst(name, getRealSort()); } /** * Creates a bit-vector constant. **/ public BitVecExpr mkBVConst(Symbol name, int size) { return (BitVecExpr) mkConst(name, mkBitVecSort(size)); } /** * Creates a bit-vector constant. **/ public BitVecExpr mkBVConst(String name, int size) { return (BitVecExpr) mkConst(name, mkBitVecSort(size)); } /** * Create a new function application. **/ public Expr mkApp(FuncDecl f, Expr... args) { checkContextMatch(f); checkContextMatch(args); return Expr.create(this, f, args); } /** * The true Term. **/ public BoolExpr mkTrue() { return new BoolExpr(this, Native.mkTrue(nCtx())); } /** * The false Term. **/ public BoolExpr mkFalse() { return new BoolExpr(this, Native.mkFalse(nCtx())); } /** * Creates a Boolean value. **/ public BoolExpr mkBool(boolean value) { return value ? mkTrue() : mkFalse(); } /** * Creates the equality {@code x = y} **/ public BoolExpr mkEq(Expr x, Expr y) { checkContextMatch(x); checkContextMatch(y); return new BoolExpr(this, Native.mkEq(nCtx(), x.getNativeObject(), y.getNativeObject())); } /** * Creates a {@code distinct} term. **/ public BoolExpr mkDistinct(Expr... args) { checkContextMatch(args); return new BoolExpr(this, Native.mkDistinct(nCtx(), args.length, AST.arrayToNative(args))); } /** * Create an expression representing {@code not(a)}. **/ public BoolExpr mkNot(BoolExpr a) { checkContextMatch(a); return new BoolExpr(this, Native.mkNot(nCtx(), a.getNativeObject())); } /** * Create an expression representing an if-then-else: * {@code ite(t1, t2, t3)}. * @param t1 An expression with Boolean sort * @param t2 An expression * @param t3 An expression with the same sort as {@code t2} **/ public Expr mkITE(BoolExpr t1, Expr t2, Expr t3) { checkContextMatch(t1); checkContextMatch(t2); checkContextMatch(t3); return Expr.create(this, Native.mkIte(nCtx(), t1.getNativeObject(), t2.getNativeObject(), t3.getNativeObject())); } /** * Create an expression representing {@code t1 iff t2}. **/ public BoolExpr mkIff(BoolExpr t1, BoolExpr t2) { checkContextMatch(t1); checkContextMatch(t2); return new BoolExpr(this, Native.mkIff(nCtx(), t1.getNativeObject(), t2.getNativeObject())); } /** * Create an expression representing {@code t1 -> t2}. **/ public BoolExpr mkImplies(BoolExpr t1, BoolExpr t2) { checkContextMatch(t1); checkContextMatch(t2); return new BoolExpr(this, Native.mkImplies(nCtx(), t1.getNativeObject(), t2.getNativeObject())); } /** * Create an expression representing {@code t1 xor t2}. **/ public BoolExpr mkXor(BoolExpr t1, BoolExpr t2) { checkContextMatch(t1); checkContextMatch(t2); return new BoolExpr(this, Native.mkXor(nCtx(), t1.getNativeObject(), t2.getNativeObject())); } /** * Create an expression representing {@code t[0] and t[1] and ...}. **/ public BoolExpr mkAnd(BoolExpr... t) { checkContextMatch(t); return new BoolExpr(this, Native.mkAnd(nCtx(), t.length, AST.arrayToNative(t))); } /** * Create an expression representing {@code t[0] or t[1] or ...}. **/ public BoolExpr mkOr(BoolExpr... t) { checkContextMatch(t); return new BoolExpr(this, Native.mkOr(nCtx(), t.length, AST.arrayToNative(t))); } /** * Create an expression representing {@code t[0] + t[1] + ...}. **/ public ArithExpr mkAdd(ArithExpr... t) { checkContextMatch(t); return (ArithExpr) Expr.create(this, Native.mkAdd(nCtx(), t.length, AST.arrayToNative(t))); } /** * Create an expression representing {@code t[0] * t[1] * ...}. **/ public ArithExpr mkMul(ArithExpr... t) { checkContextMatch(t); return (ArithExpr) Expr.create(this, Native.mkMul(nCtx(), t.length, AST.arrayToNative(t))); } /** * Create an expression representing {@code t[0] - t[1] - ...}. **/ public ArithExpr mkSub(ArithExpr... t) { checkContextMatch(t); return (ArithExpr) Expr.create(this, Native.mkSub(nCtx(), t.length, AST.arrayToNative(t))); } /** * Create an expression representing {@code -t}. **/ public ArithExpr mkUnaryMinus(ArithExpr t) { checkContextMatch(t); return (ArithExpr) Expr.create(this, Native.mkUnaryMinus(nCtx(), t.getNativeObject())); } /** * Create an expression representing {@code t1 / t2}. **/ public ArithExpr mkDiv(ArithExpr t1, ArithExpr t2) { checkContextMatch(t1); checkContextMatch(t2); return (ArithExpr) Expr.create(this, Native.mkDiv(nCtx(), t1.getNativeObject(), t2.getNativeObject())); } /** * Create an expression representing {@code t1 mod t2}. * Remarks: The * arguments must have int type. **/ public IntExpr mkMod(IntExpr t1, IntExpr t2) { checkContextMatch(t1); checkContextMatch(t2); return new IntExpr(this, Native.mkMod(nCtx(), t1.getNativeObject(), t2.getNativeObject())); } /** * Create an expression representing {@code t1 rem t2}. * Remarks: The * arguments must have int type. **/ public IntExpr mkRem(IntExpr t1, IntExpr t2) { checkContextMatch(t1); checkContextMatch(t2); return new IntExpr(this, Native.mkRem(nCtx(), t1.getNativeObject(), t2.getNativeObject())); } /** * Create an expression representing {@code t1 ^ t2}. **/ public ArithExpr mkPower(ArithExpr t1, ArithExpr t2) { checkContextMatch(t1); checkContextMatch(t2); return (ArithExpr) Expr.create( this, Native.mkPower(nCtx(), t1.getNativeObject(), t2.getNativeObject())); } /** * Create an expression representing {@code t1 < t2} **/ public BoolExpr mkLt(ArithExpr t1, ArithExpr t2) { checkContextMatch(t1); checkContextMatch(t2); return new BoolExpr(this, Native.mkLt(nCtx(), t1.getNativeObject(), t2.getNativeObject())); } /** * Create an expression representing {@code t1 <= t2} **/ public BoolExpr mkLe(ArithExpr t1, ArithExpr t2) { checkContextMatch(t1); checkContextMatch(t2); return new BoolExpr(this, Native.mkLe(nCtx(), t1.getNativeObject(), t2.getNativeObject())); } /** * Create an expression representing {@code t1 > t2} **/ public BoolExpr mkGt(ArithExpr t1, ArithExpr t2) { checkContextMatch(t1); checkContextMatch(t2); return new BoolExpr(this, Native.mkGt(nCtx(), t1.getNativeObject(), t2.getNativeObject())); } /** * Create an expression representing {@code t1 >= t2} **/ public BoolExpr mkGe(ArithExpr t1, ArithExpr t2) { checkContextMatch(t1); checkContextMatch(t2); return new BoolExpr(this, Native.mkGe(nCtx(), t1.getNativeObject(), t2.getNativeObject())); } /** * Coerce an integer to a real. * Remarks: There is also a converse operation * exposed. It follows the semantics prescribed by the SMT-LIB standard. * * You can take the floor of a real by creating an auxiliary integer Term * {@code k} and asserting * {@code MakeInt2Real(k) <= t1 < MkInt2Real(k)+1}. The argument * must be of integer sort. **/ public RealExpr mkInt2Real(IntExpr t) { checkContextMatch(t); return new RealExpr(this, Native.mkInt2real(nCtx(), t.getNativeObject())); } /** * Coerce a real to an integer. * Remarks: The semantics of this function * follows the SMT-LIB standard for the function to_int. The argument must * be of real sort. **/ public IntExpr mkReal2Int(RealExpr t) { checkContextMatch(t); return new IntExpr(this, Native.mkReal2int(nCtx(), t.getNativeObject())); } /** * Creates an expression that checks whether a real number is an integer. **/ public BoolExpr mkIsInteger(RealExpr t) { checkContextMatch(t); return new BoolExpr(this, Native.mkIsInt(nCtx(), t.getNativeObject())); } /** * Bitwise negation. * Remarks: The argument must have a bit-vector * sort. **/ public BitVecExpr mkBVNot(BitVecExpr t) { checkContextMatch(t); return new BitVecExpr(this, Native.mkBvnot(nCtx(), t.getNativeObject())); } /** * Take conjunction of bits in a vector, return vector of length 1. * * Remarks: The argument must have a bit-vector sort. **/ public BitVecExpr mkBVRedAND(BitVecExpr t) { checkContextMatch(t); return new BitVecExpr(this, Native.mkBvredand(nCtx(), t.getNativeObject())); } /** * Take disjunction of bits in a vector, return vector of length 1. * * Remarks: The argument must have a bit-vector sort. **/ public BitVecExpr mkBVRedOR(BitVecExpr t) { checkContextMatch(t); return new BitVecExpr(this, Native.mkBvredor(nCtx(), t.getNativeObject())); } /** * Bitwise conjunction. * Remarks: The arguments must have a bit-vector * sort. **/ public BitVecExpr mkBVAND(BitVecExpr t1, BitVecExpr t2) { checkContextMatch(t1); checkContextMatch(t2); return new BitVecExpr(this, Native.mkBvand(nCtx(), t1.getNativeObject(), t2.getNativeObject())); } /** * Bitwise disjunction. * Remarks: The arguments must have a bit-vector * sort. **/ public BitVecExpr mkBVOR(BitVecExpr t1, BitVecExpr t2) { checkContextMatch(t1); checkContextMatch(t2); return new BitVecExpr(this, Native.mkBvor(nCtx(), t1.getNativeObject(), t2.getNativeObject())); } /** * Bitwise XOR. * Remarks: The arguments must have a bit-vector * sort. **/ public BitVecExpr mkBVXOR(BitVecExpr t1, BitVecExpr t2) { checkContextMatch(t1); checkContextMatch(t2); return new BitVecExpr(this, Native.mkBvxor(nCtx(), t1.getNativeObject(), t2.getNativeObject())); } /** * Bitwise NAND. * Remarks: The arguments must have a bit-vector * sort. **/ public BitVecExpr mkBVNAND(BitVecExpr t1, BitVecExpr t2) { checkContextMatch(t1); checkContextMatch(t2); return new BitVecExpr(this, Native.mkBvnand(nCtx(), t1.getNativeObject(), t2.getNativeObject())); } /** * Bitwise NOR. * Remarks: The arguments must have a bit-vector * sort. **/ public BitVecExpr mkBVNOR(BitVecExpr t1, BitVecExpr t2) { checkContextMatch(t1); checkContextMatch(t2); return new BitVecExpr(this, Native.mkBvnor(nCtx(), t1.getNativeObject(), t2.getNativeObject())); } /** * Bitwise XNOR. * Remarks: The arguments must have a bit-vector * sort. **/ public BitVecExpr mkBVXNOR(BitVecExpr t1, BitVecExpr t2) { checkContextMatch(t1); checkContextMatch(t2); return new BitVecExpr(this, Native.mkBvxnor(nCtx(), t1.getNativeObject(), t2.getNativeObject())); } /** * Standard two's complement unary minus. * Remarks: The arguments must have a * bit-vector sort. **/ public BitVecExpr mkBVNeg(BitVecExpr t) { checkContextMatch(t); return new BitVecExpr(this, Native.mkBvneg(nCtx(), t.getNativeObject())); } /** * Two's complement addition. * Remarks: The arguments must have the same * bit-vector sort. **/ public BitVecExpr mkBVAdd(BitVecExpr t1, BitVecExpr t2) { checkContextMatch(t1); checkContextMatch(t2); return new BitVecExpr(this, Native.mkBvadd(nCtx(), t1.getNativeObject(), t2.getNativeObject())); } /** * Two's complement subtraction. * Remarks: The arguments must have the same * bit-vector sort. **/ public BitVecExpr mkBVSub(BitVecExpr t1, BitVecExpr t2) { checkContextMatch(t1); checkContextMatch(t2); return new BitVecExpr(this, Native.mkBvsub(nCtx(), t1.getNativeObject(), t2.getNativeObject())); } /** * Two's complement multiplication. * Remarks: The arguments must have the * same bit-vector sort. **/ public BitVecExpr mkBVMul(BitVecExpr t1, BitVecExpr t2) { checkContextMatch(t1); checkContextMatch(t2); return new BitVecExpr(this, Native.mkBvmul(nCtx(), t1.getNativeObject(), t2.getNativeObject())); } /** * Unsigned division. * Remarks: It is defined as the floor of * {@code t1/t2} if \c t2 is different from zero. If {@code t2} is * zero, then the result is undefined. The arguments must have the same * bit-vector sort. **/ public BitVecExpr mkBVUDiv(BitVecExpr t1, BitVecExpr t2) { checkContextMatch(t1); checkContextMatch(t2); return new BitVecExpr(this, Native.mkBvudiv(nCtx(), t1.getNativeObject(), t2.getNativeObject())); } /** * Signed division. * Remarks: It is defined in the following way: * * - The \c floor of {@code t1/t2} if \c t2 is different from zero, and * {@code t1*t2 >= 0}. * * - The \c ceiling of {@code t1/t2} if \c t2 is different from zero, * and {@code t1*t2 < 0}. * * If {@code t2} is zero, then the result is undefined. The arguments * must have the same bit-vector sort. **/ public BitVecExpr mkBVSDiv(BitVecExpr t1, BitVecExpr t2) { checkContextMatch(t1); checkContextMatch(t2); return new BitVecExpr(this, Native.mkBvsdiv(nCtx(), t1.getNativeObject(), t2.getNativeObject())); } /** * Unsigned remainder. * Remarks: It is defined as * {@code t1 - (t1 /u t2) * t2}, where {@code /u} represents * unsigned division. If {@code t2} is zero, then the result is * undefined. The arguments must have the same bit-vector sort. **/ public BitVecExpr mkBVURem(BitVecExpr t1, BitVecExpr t2) { checkContextMatch(t1); checkContextMatch(t2); return new BitVecExpr(this, Native.mkBvurem(nCtx(), t1.getNativeObject(), t2.getNativeObject())); } /** * Signed remainder. * Remarks: It is defined as * {@code t1 - (t1 /s t2) * t2}, where {@code /s} represents * signed division. The most significant bit (sign) of the result is equal * to the most significant bit of \c t1. * * If {@code t2} is zero, then the result is undefined. The arguments * must have the same bit-vector sort. **/ public BitVecExpr mkBVSRem(BitVecExpr t1, BitVecExpr t2) { checkContextMatch(t1); checkContextMatch(t2); return new BitVecExpr(this, Native.mkBvsrem(nCtx(), t1.getNativeObject(), t2.getNativeObject())); } /** * Two's complement signed remainder (sign follows divisor). * Remarks: If * {@code t2} is zero, then the result is undefined. The arguments must * have the same bit-vector sort. **/ public BitVecExpr mkBVSMod(BitVecExpr t1, BitVecExpr t2) { checkContextMatch(t1); checkContextMatch(t2); return new BitVecExpr(this, Native.mkBvsmod(nCtx(), t1.getNativeObject(), t2.getNativeObject())); } /** * Unsigned less-than * Remarks: The arguments must have the same bit-vector * sort. **/ public BoolExpr mkBVULT(BitVecExpr t1, BitVecExpr t2) { checkContextMatch(t1); checkContextMatch(t2); return new BoolExpr(this, Native.mkBvult(nCtx(), t1.getNativeObject(), t2.getNativeObject())); } /** * Two's complement signed less-than * Remarks: The arguments must have the * same bit-vector sort. **/ public BoolExpr mkBVSLT(BitVecExpr t1, BitVecExpr t2) { checkContextMatch(t1); checkContextMatch(t2); return new BoolExpr(this, Native.mkBvslt(nCtx(), t1.getNativeObject(), t2.getNativeObject())); } /** * Unsigned less-than or equal to. * Remarks: The arguments must have the * same bit-vector sort. **/ public BoolExpr mkBVULE(BitVecExpr t1, BitVecExpr t2) { checkContextMatch(t1); checkContextMatch(t2); return new BoolExpr(this, Native.mkBvule(nCtx(), t1.getNativeObject(), t2.getNativeObject())); } /** * Two's complement signed less-than or equal to. * Remarks: The arguments * must have the same bit-vector sort. **/ public BoolExpr mkBVSLE(BitVecExpr t1, BitVecExpr t2) { checkContextMatch(t1); checkContextMatch(t2); return new BoolExpr(this, Native.mkBvsle(nCtx(), t1.getNativeObject(), t2.getNativeObject())); } /** * Unsigned greater than or equal to. * Remarks: The arguments must have the * same bit-vector sort. **/ public BoolExpr mkBVUGE(BitVecExpr t1, BitVecExpr t2) { checkContextMatch(t1); checkContextMatch(t2); return new BoolExpr(this, Native.mkBvuge(nCtx(), t1.getNativeObject(), t2.getNativeObject())); } /** * Two's complement signed greater than or equal to. * Remarks: The arguments * must have the same bit-vector sort. **/ public BoolExpr mkBVSGE(BitVecExpr t1, BitVecExpr t2) { checkContextMatch(t1); checkContextMatch(t2); return new BoolExpr(this, Native.mkBvsge(nCtx(), t1.getNativeObject(), t2.getNativeObject())); } /** * Unsigned greater-than. * Remarks: The arguments must have the same * bit-vector sort. **/ public BoolExpr mkBVUGT(BitVecExpr t1, BitVecExpr t2) { checkContextMatch(t1); checkContextMatch(t2); return new BoolExpr(this, Native.mkBvugt(nCtx(), t1.getNativeObject(), t2.getNativeObject())); } /** * Two's complement signed greater-than. * Remarks: The arguments must have * the same bit-vector sort. **/ public BoolExpr mkBVSGT(BitVecExpr t1, BitVecExpr t2) { checkContextMatch(t1); checkContextMatch(t2); return new BoolExpr(this, Native.mkBvsgt(nCtx(), t1.getNativeObject(), t2.getNativeObject())); } /** * Bit-vector concatenation. * Remarks: The arguments must have a bit-vector * sort. * * @return The result is a bit-vector of size {@code n1+n2}, where * {@code n1} ({@code n2}) is the size of {@code t1} * ({@code t2}). * **/ public BitVecExpr mkConcat(BitVecExpr t1, BitVecExpr t2) { checkContextMatch(t1); checkContextMatch(t2); return new BitVecExpr(this, Native.mkConcat(nCtx(), t1.getNativeObject(), t2.getNativeObject())); } /** * Bit-vector extraction. * Remarks: Extract the bits {@code high} * down to {@code low} from a bitvector of size {@code m} to * yield a new bitvector of size {@code n}, where * {@code n = high - low + 1}. The argument {@code t} must * have a bit-vector sort. **/ public BitVecExpr mkExtract(int high, int low, BitVecExpr t) { checkContextMatch(t); return new BitVecExpr(this, Native.mkExtract(nCtx(), high, low, t.getNativeObject())); } /** * Bit-vector sign extension. * Remarks: Sign-extends the given bit-vector to * the (signed) equivalent bitvector of size {@code m+i}, where \c m is * the size of the given bit-vector. The argument {@code t} must * have a bit-vector sort. **/ public BitVecExpr mkSignExt(int i, BitVecExpr t) { checkContextMatch(t); return new BitVecExpr(this, Native.mkSignExt(nCtx(), i, t.getNativeObject())); } /** * Bit-vector zero extension. * Remarks: Extend the given bit-vector with * zeros to the (unsigned) equivalent bitvector of size {@code m+i}, * where \c m is the size of the given bit-vector. The argument {@code t} * must have a bit-vector sort. **/ public BitVecExpr mkZeroExt(int i, BitVecExpr t) { checkContextMatch(t); return new BitVecExpr(this, Native.mkZeroExt(nCtx(), i, t.getNativeObject())); } /** * Bit-vector repetition. * Remarks: The argument {@code t} must * have a bit-vector sort. **/ public BitVecExpr mkRepeat(int i, BitVecExpr t) { checkContextMatch(t); return new BitVecExpr(this, Native.mkRepeat(nCtx(), i, t.getNativeObject())); } /** * Shift left. * Remarks: It is equivalent to multiplication by * {@code 2^x} where \c x is the value of {@code t2}. * * NB. The semantics of shift operations varies between environments. This * definition does not necessarily capture directly the semantics of the * programming language or assembly architecture you are modeling. * * The arguments must have a bit-vector sort. **/ public BitVecExpr mkBVSHL(BitVecExpr t1, BitVecExpr t2) { checkContextMatch(t1); checkContextMatch(t2); return new BitVecExpr(this, Native.mkBvshl(nCtx(), t1.getNativeObject(), t2.getNativeObject())); } /** * Logical shift right * Remarks: It is equivalent to unsigned division by * {@code 2^x} where \c x is the value of {@code t2}. * * NB. The semantics of shift operations varies between environments. This * definition does not necessarily capture directly the semantics of the * programming language or assembly architecture you are modeling. * * The arguments must have a bit-vector sort. **/ public BitVecExpr mkBVLSHR(BitVecExpr t1, BitVecExpr t2) { checkContextMatch(t1); checkContextMatch(t2); return new BitVecExpr(this, Native.mkBvlshr(nCtx(), t1.getNativeObject(), t2.getNativeObject())); } /** * Arithmetic shift right * Remarks: It is like logical shift right except * that the most significant bits of the result always copy the most * significant bit of the second argument. * * NB. The semantics of shift operations varies between environments. This * definition does not necessarily capture directly the semantics of the * programming language or assembly architecture you are modeling. * * The arguments must have a bit-vector sort. **/ public BitVecExpr mkBVASHR(BitVecExpr t1, BitVecExpr t2) { checkContextMatch(t1); checkContextMatch(t2); return new BitVecExpr(this, Native.mkBvashr(nCtx(), t1.getNativeObject(), t2.getNativeObject())); } /** * Rotate Left. * Remarks: Rotate bits of \c t to the left \c i times. The * argument {@code t} must have a bit-vector sort. **/ public BitVecExpr mkBVRotateLeft(int i, BitVecExpr t) { checkContextMatch(t); return new BitVecExpr(this, Native.mkRotateLeft(nCtx(), i, t.getNativeObject())); } /** * Rotate Right. * Remarks: Rotate bits of \c t to the right \c i times. The * argument {@code t} must have a bit-vector sort. **/ public BitVecExpr mkBVRotateRight(int i, BitVecExpr t) { checkContextMatch(t); return new BitVecExpr(this, Native.mkRotateRight(nCtx(), i, t.getNativeObject())); } /** * Rotate Left. * Remarks: Rotate bits of {@code t1} to the left * {@code t2} times. The arguments must have the same bit-vector * sort. **/ public BitVecExpr mkBVRotateLeft(BitVecExpr t1, BitVecExpr t2) { checkContextMatch(t1); checkContextMatch(t2); return new BitVecExpr(this, Native.mkExtRotateLeft(nCtx(), t1.getNativeObject(), t2.getNativeObject())); } /** * Rotate Right. * Remarks: Rotate bits of {@code t1} to the * right{@code t2} times. The arguments must have the same * bit-vector sort. **/ public BitVecExpr mkBVRotateRight(BitVecExpr t1, BitVecExpr t2) { checkContextMatch(t1); checkContextMatch(t2); return new BitVecExpr(this, Native.mkExtRotateRight(nCtx(), t1.getNativeObject(), t2.getNativeObject())); } /** * Create an {@code n} bit bit-vector from the integer argument * {@code t}. * Remarks: NB. This function is essentially treated * as uninterpreted. So you cannot expect Z3 to precisely reflect the * semantics of this function when solving constraints with this function. * * The argument must be of integer sort. **/ public BitVecExpr mkInt2BV(int n, IntExpr t) { checkContextMatch(t); return new BitVecExpr(this, Native.mkInt2bv(nCtx(), n, t.getNativeObject())); } /** * Create an integer from the bit-vector argument {@code t}. * Remarks: If \c is_signed is false, then the bit-vector \c t1 is treated * as unsigned. So the result is non-negative and in the range * {@code [0..2^N-1]}, where N are the number of bits in {@code t}. * If \c is_signed is true, \c t1 is treated as a signed * bit-vector. * * NB. This function is essentially treated as uninterpreted. So you cannot * expect Z3 to precisely reflect the semantics of this function when * solving constraints with this function. * * The argument must be of bit-vector sort. **/ public IntExpr mkBV2Int(BitVecExpr t, boolean signed) { checkContextMatch(t); return new IntExpr(this, Native.mkBv2int(nCtx(), t.getNativeObject(), (signed))); } /** * Create a predicate that checks that the bit-wise addition does not * overflow. * Remarks: The arguments must be of bit-vector sort. **/ public BoolExpr mkBVAddNoOverflow(BitVecExpr t1, BitVecExpr t2, boolean isSigned) { checkContextMatch(t1); checkContextMatch(t2); return new BoolExpr(this, Native.mkBvaddNoOverflow(nCtx(), t1 .getNativeObject(), t2.getNativeObject(), (isSigned))); } /** * Create a predicate that checks that the bit-wise addition does not * underflow. * Remarks: The arguments must be of bit-vector sort. **/ public BoolExpr mkBVAddNoUnderflow(BitVecExpr t1, BitVecExpr t2) { checkContextMatch(t1); checkContextMatch(t2); return new BoolExpr(this, Native.mkBvaddNoUnderflow(nCtx(), t1.getNativeObject(), t2.getNativeObject())); } /** * Create a predicate that checks that the bit-wise subtraction does not * overflow. * Remarks: The arguments must be of bit-vector sort. **/ public BoolExpr mkBVSubNoOverflow(BitVecExpr t1, BitVecExpr t2) { checkContextMatch(t1); checkContextMatch(t2); return new BoolExpr(this, Native.mkBvsubNoOverflow(nCtx(), t1.getNativeObject(), t2.getNativeObject())); } /** * Create a predicate that checks that the bit-wise subtraction does not * underflow. * Remarks: The arguments must be of bit-vector sort. **/ public BoolExpr mkBVSubNoUnderflow(BitVecExpr t1, BitVecExpr t2, boolean isSigned) { checkContextMatch(t1); checkContextMatch(t2); return new BoolExpr(this, Native.mkBvsubNoUnderflow(nCtx(), t1 .getNativeObject(), t2.getNativeObject(), (isSigned))); } /** * Create a predicate that checks that the bit-wise signed division does not * overflow. * Remarks: The arguments must be of bit-vector sort. **/ public BoolExpr mkBVSDivNoOverflow(BitVecExpr t1, BitVecExpr t2) { checkContextMatch(t1); checkContextMatch(t2); return new BoolExpr(this, Native.mkBvsdivNoOverflow(nCtx(), t1.getNativeObject(), t2.getNativeObject())); } /** * Create a predicate that checks that the bit-wise negation does not * overflow. * Remarks: The arguments must be of bit-vector sort. **/ public BoolExpr mkBVNegNoOverflow(BitVecExpr t) { checkContextMatch(t); return new BoolExpr(this, Native.mkBvnegNoOverflow(nCtx(), t.getNativeObject())); } /** * Create a predicate that checks that the bit-wise multiplication does not * overflow. * Remarks: The arguments must be of bit-vector sort. **/ public BoolExpr mkBVMulNoOverflow(BitVecExpr t1, BitVecExpr t2, boolean isSigned) { checkContextMatch(t1); checkContextMatch(t2); return new BoolExpr(this, Native.mkBvmulNoOverflow(nCtx(), t1 .getNativeObject(), t2.getNativeObject(), (isSigned))); } /** * Create a predicate that checks that the bit-wise multiplication does not * underflow. * Remarks: The arguments must be of bit-vector sort. **/ public BoolExpr mkBVMulNoUnderflow(BitVecExpr t1, BitVecExpr t2) { checkContextMatch(t1); checkContextMatch(t2); return new BoolExpr(this, Native.mkBvmulNoUnderflow(nCtx(), t1.getNativeObject(), t2.getNativeObject())); } /** * Create an array constant. **/ public ArrayExpr mkArrayConst(Symbol name, Sort domain, Sort range) { return (ArrayExpr) mkConst(name, mkArraySort(domain, range)); } /** * Create an array constant. **/ public ArrayExpr mkArrayConst(String name, Sort domain, Sort range) { return (ArrayExpr) mkConst(mkSymbol(name), mkArraySort(domain, range)); } /** * Array read. * Remarks: The argument {@code a} is the array and * {@code i} is the index of the array that gets read. * * The node {@code a} must have an array sort * {@code [domain -> range]}, and {@code i} must have the sort * {@code domain}. The sort of the result is {@code range}. * * @see #mkArraySort * @see #mkStore **/ public Expr mkSelect(ArrayExpr a, Expr i) { checkContextMatch(a); checkContextMatch(i); return Expr.create( this, Native.mkSelect(nCtx(), a.getNativeObject(), i.getNativeObject())); } /** * Array read. * Remarks: The argument {@code a} is the array and * {@code args} are the indices of the array that gets read. * * The node {@code a} must have an array sort * {@code [domains -> range]}, and {@code args} must have the sorts * {@code domains}. The sort of the result is {@code range}. * * @see #mkArraySort * @see #mkStore **/ public Expr mkSelect(ArrayExpr a, Expr[] args) { checkContextMatch(a); checkContextMatch(args); return Expr.create( this, Native.mkSelectN(nCtx(), a.getNativeObject(), args.length, AST.arrayToNative(args))); } /** * Array update. * Remarks: The node {@code a} must have an array sort * {@code [domain -> range]}, {@code i} must have sort * {@code domain}, {@code v} must have sort range. The sort of the * result is {@code [domain -> range]}. The semantics of this function * is given by the theory of arrays described in the SMT-LIB standard. See * http://smtlib.org for more details. The result of this function is an * array that is equal to {@code a} (with respect to * {@code select}) on all indices except for {@code i}, where it * maps to {@code v} (and the {@code select} of {@code a} * with respect to {@code i} may be a different value). * @see #mkArraySort * @see #mkSelect **/ public ArrayExpr mkStore(ArrayExpr a, Expr i, Expr v) { checkContextMatch(a); checkContextMatch(i); checkContextMatch(v); return new ArrayExpr(this, Native.mkStore(nCtx(), a.getNativeObject(), i.getNativeObject(), v.getNativeObject())); } /** * Array update. * Remarks: The node {@code a} must have an array sort * {@code [domains -> range]}, {@code i} must have sort * {@code domain}, {@code v} must have sort range. The sort of the * result is {@code [domains -> range]}. The semantics of this function * is given by the theory of arrays described in the SMT-LIB standard. See * http://smtlib.org for more details. The result of this function is an * array that is equal to {@code a} (with respect to * {@code select}) on all indices except for {@code args}, where it * maps to {@code v} (and the {@code select} of {@code a} * with respect to {@code args} may be a different value). * @see #mkArraySort * @see #mkSelect **/ public ArrayExpr mkStore(ArrayExpr a, Expr[] args, Expr v) { checkContextMatch(a); checkContextMatch(args); checkContextMatch(v); return new ArrayExpr(this, Native.mkStoreN(nCtx(), a.getNativeObject(), args.length, AST.arrayToNative(args), v.getNativeObject())); } /** * Create a constant array. * Remarks: The resulting term is an array, such * that a {@code select} on an arbitrary index produces the value * {@code v}. * @see #mkArraySort * @see #mkSelect * **/ public ArrayExpr mkConstArray(Sort domain, Expr v) { checkContextMatch(domain); checkContextMatch(v); return new ArrayExpr(this, Native.mkConstArray(nCtx(), domain.getNativeObject(), v.getNativeObject())); } /** * Maps f on the argument arrays. * Remarks: Each element of * {@code args} must be of an array sort * {@code [domain_i -> range_i]}. The function declaration * {@code f} must have type {@code range_1 .. range_n -> range}. * {@code v} must have sort range. The sort of the result is * {@code [domain_i -> range]}. * @see #mkArraySort * @see #mkSelect * @see #mkStore **/ public ArrayExpr mkMap(FuncDecl f, ArrayExpr... args) { checkContextMatch(f); checkContextMatch(args); return (ArrayExpr) Expr.create(this, Native.mkMap(nCtx(), f.getNativeObject(), AST.arrayLength(args), AST.arrayToNative(args))); } /** * Access the array default value. * Remarks: Produces the default range * value, for arrays that can be represented as finite maps with a default * range value. **/ public Expr mkTermArray(ArrayExpr array) { checkContextMatch(array); return Expr.create(this, Native.mkArrayDefault(nCtx(), array.getNativeObject())); } /** * Create Extentionality index. Two arrays are equal if and only if they are equal on the index returned by MkArrayExt. **/ public Expr mkArrayExt(ArrayExpr arg1, ArrayExpr arg2) { checkContextMatch(arg1); checkContextMatch(arg2); return Expr.create(this, Native.mkArrayExt(nCtx(), arg1.getNativeObject(), arg2.getNativeObject())); } /** * Create a set type. **/ public SetSort mkSetSort(Sort ty) { checkContextMatch(ty); return new SetSort(this, ty); } /** * Create an empty set. **/ public ArrayExpr mkEmptySet(Sort domain) { checkContextMatch(domain); return (ArrayExpr)Expr.create(this, Native.mkEmptySet(nCtx(), domain.getNativeObject())); } /** * Create the full set. **/ public ArrayExpr mkFullSet(Sort domain) { checkContextMatch(domain); return (ArrayExpr)Expr.create(this, Native.mkFullSet(nCtx(), domain.getNativeObject())); } /** * Add an element to the set. **/ public ArrayExpr mkSetAdd(ArrayExpr set, Expr element) { checkContextMatch(set); checkContextMatch(element); return (ArrayExpr)Expr.create(this, Native.mkSetAdd(nCtx(), set.getNativeObject(), element.getNativeObject())); } /** * Remove an element from a set. **/ public ArrayExpr mkSetDel(ArrayExpr set, Expr element) { checkContextMatch(set); checkContextMatch(element); return (ArrayExpr)Expr.create(this, Native.mkSetDel(nCtx(), set.getNativeObject(), element.getNativeObject())); } /** * Take the union of a list of sets. **/ public ArrayExpr mkSetUnion(ArrayExpr... args) { checkContextMatch(args); return (ArrayExpr)Expr.create(this, Native.mkSetUnion(nCtx(), args.length, AST.arrayToNative(args))); } /** * Take the intersection of a list of sets. **/ public ArrayExpr mkSetIntersection(ArrayExpr... args) { checkContextMatch(args); return (ArrayExpr)Expr.create(this, Native.mkSetIntersect(nCtx(), args.length, AST.arrayToNative(args))); } /** * Take the difference between two sets. **/ public ArrayExpr mkSetDifference(ArrayExpr arg1, ArrayExpr arg2) { checkContextMatch(arg1); checkContextMatch(arg2); return (ArrayExpr)Expr.create(this, Native.mkSetDifference(nCtx(), arg1.getNativeObject(), arg2.getNativeObject())); } /** * Take the complement of a set. **/ public ArrayExpr mkSetComplement(ArrayExpr arg) { checkContextMatch(arg); return (ArrayExpr)Expr.create(this, Native.mkSetComplement(nCtx(), arg.getNativeObject())); } /** * Check for set membership. **/ public BoolExpr mkSetMembership(Expr elem, ArrayExpr set) { checkContextMatch(elem); checkContextMatch(set); return (BoolExpr) Expr.create(this, Native.mkSetMember(nCtx(), elem.getNativeObject(), set.getNativeObject())); } /** * Check for subsetness of sets. **/ public BoolExpr mkSetSubset(ArrayExpr arg1, ArrayExpr arg2) { checkContextMatch(arg1); checkContextMatch(arg2); return (BoolExpr) Expr.create(this, Native.mkSetSubset(nCtx(), arg1.getNativeObject(), arg2.getNativeObject())); } /** * Sequences, Strings and regular expressions. */ /** * Create the empty sequence. */ public SeqExpr mkEmptySeq(Sort s) { checkContextMatch(s); return (SeqExpr) Expr.create(this, Native.mkSeqEmpty(nCtx(), s.getNativeObject())); } /** * Create the singleton sequence. */ public SeqExpr mkUnit(Expr elem) { checkContextMatch(elem); return (SeqExpr) Expr.create(this, Native.mkSeqUnit(nCtx(), elem.getNativeObject())); } /** * Create a string constant. */ public SeqExpr mkString(String s) { return (SeqExpr) Expr.create(this, Native.mkString(nCtx(), s)); } /** * Convert an integer expression to a string. */ public SeqExpr intToString(Expr e) { return (SeqExpr) Expr.create(this, Native.mkIntToStr(nCtx(), e.getNativeObject())); } /** * Convert an integer expression to a string. */ public IntExpr stringToInt(Expr e) { return (IntExpr) Expr.create(this, Native.mkStrToInt(nCtx(), e.getNativeObject())); } /** * Concatenate sequences. */ public SeqExpr mkConcat(SeqExpr... t) { checkContextMatch(t); return (SeqExpr) Expr.create(this, Native.mkSeqConcat(nCtx(), t.length, AST.arrayToNative(t))); } /** * Retrieve the length of a given sequence. */ public IntExpr mkLength(SeqExpr s) { checkContextMatch(s); return (IntExpr) Expr.create(this, Native.mkSeqLength(nCtx(), s.getNativeObject())); } /** * Check for sequence prefix. */ public BoolExpr mkPrefixOf(SeqExpr s1, SeqExpr s2) { checkContextMatch(s1, s2); return (BoolExpr) Expr.create(this, Native.mkSeqPrefix(nCtx(), s1.getNativeObject(), s2.getNativeObject())); } /** * Check for sequence suffix. */ public BoolExpr mkSuffixOf(SeqExpr s1, SeqExpr s2) { checkContextMatch(s1, s2); return (BoolExpr)Expr.create(this, Native.mkSeqSuffix(nCtx(), s1.getNativeObject(), s2.getNativeObject())); } /** * Check for sequence containment of s2 in s1. */ public BoolExpr mkContains(SeqExpr s1, SeqExpr s2) { checkContextMatch(s1, s2); return (BoolExpr) Expr.create(this, Native.mkSeqContains(nCtx(), s1.getNativeObject(), s2.getNativeObject())); } /** * Retrieve sequence of length one at index. */ public SeqExpr mkAt(SeqExpr s, IntExpr index) { checkContextMatch(s, index); return (SeqExpr) Expr.create(this, Native.mkSeqAt(nCtx(), s.getNativeObject(), index.getNativeObject())); } /** * Extract subsequence. */ public SeqExpr mkExtract(SeqExpr s, IntExpr offset, IntExpr length) { checkContextMatch(s, offset, length); return (SeqExpr) Expr.create(this, Native.mkSeqExtract(nCtx(), s.getNativeObject(), offset.getNativeObject(), length.getNativeObject())); } /** * Extract index of sub-string starting at offset. */ public IntExpr mkIndexOf(SeqExpr s, SeqExpr substr, ArithExpr offset) { checkContextMatch(s, substr, offset); return (IntExpr)Expr.create(this, Native.mkSeqIndex(nCtx(), s.getNativeObject(), substr.getNativeObject(), offset.getNativeObject())); } /** * Replace the first occurrence of src by dst in s. */ public SeqExpr mkReplace(SeqExpr s, SeqExpr src, SeqExpr dst) { checkContextMatch(s, src, dst); return (SeqExpr) Expr.create(this, Native.mkSeqReplace(nCtx(), s.getNativeObject(), src.getNativeObject(), dst.getNativeObject())); } /** * Convert a regular expression that accepts sequence s. */ public ReExpr mkToRe(SeqExpr s) { checkContextMatch(s); return (ReExpr) Expr.create(this, Native.mkSeqToRe(nCtx(), s.getNativeObject())); } /** * Check for regular expression membership. */ public BoolExpr mkInRe(SeqExpr s, ReExpr re) { checkContextMatch(s, re); return (BoolExpr) Expr.create(this, Native.mkSeqInRe(nCtx(), s.getNativeObject(), re.getNativeObject())); } /** * Take the Kleene star of a regular expression. */ public ReExpr mkStar(ReExpr re) { checkContextMatch(re); return (ReExpr) Expr.create(this, Native.mkReStar(nCtx(), re.getNativeObject())); } /** * Take the lower and upper-bounded Kleene star of a regular expression. */ public ReExpr mkLoop(ReExpr re, int lo, int hi) { return (ReExpr) Expr.create(this, Native.mkReLoop(nCtx(), re.getNativeObject(), lo, hi)); } /** * Take the lower-bounded Kleene star of a regular expression. */ public ReExpr mkLoop(ReExpr re, int lo) { return (ReExpr) Expr.create(this, Native.mkReLoop(nCtx(), re.getNativeObject(), lo, 0)); } /** * Take the Kleene plus of a regular expression. */ public ReExpr mkPlus(ReExpr re) { checkContextMatch(re); return (ReExpr) Expr.create(this, Native.mkRePlus(nCtx(), re.getNativeObject())); } /** * Create the optional regular expression. */ public ReExpr mkOption(ReExpr re) { checkContextMatch(re); return (ReExpr) Expr.create(this, Native.mkReOption(nCtx(), re.getNativeObject())); } /** * Create the complement regular expression. */ public ReExpr mkComplement(ReExpr re) { checkContextMatch(re); return (ReExpr) Expr.create(this, Native.mkReComplement(nCtx(), re.getNativeObject())); } /** * Create the concatenation of regular languages. */ public ReExpr mkConcat(ReExpr... t) { checkContextMatch(t); return (ReExpr) Expr.create(this, Native.mkReConcat(nCtx(), t.length, AST.arrayToNative(t))); } /** * Create the union of regular languages. */ public ReExpr mkUnion(ReExpr... t) { checkContextMatch(t); return (ReExpr) Expr.create(this, Native.mkReUnion(nCtx(), t.length, AST.arrayToNative(t))); } /** * Create the intersection of regular languages. */ public ReExpr mkIntersect(ReExpr... t) { checkContextMatch(t); return (ReExpr) Expr.create(this, Native.mkReIntersect(nCtx(), t.length, AST.arrayToNative(t))); } /** * Create the empty regular expression. */ public ReExpr mkEmptyRe(Sort s) { return (ReExpr) Expr.create(this, Native.mkReEmpty(nCtx(), s.getNativeObject())); } /** * Create the full regular expression. */ public ReExpr mkFullRe(Sort s) { return (ReExpr) Expr.create(this, Native.mkReFull(nCtx(), s.getNativeObject())); } /** * Create a range expression. */ public ReExpr mkRange(SeqExpr lo, SeqExpr hi) { checkContextMatch(lo, hi); return (ReExpr) Expr.create(this, Native.mkReRange(nCtx(), lo.getNativeObject(), hi.getNativeObject())); } /** * Create an at-most-k constraint. */ public BoolExpr mkAtMost(BoolExpr[] args, int k) { checkContextMatch(args); return (BoolExpr) Expr.create(this, Native.mkAtmost(nCtx(), args.length, AST.arrayToNative(args), k)); } /** * Create an at-least-k constraint. */ public BoolExpr mkAtLeast(BoolExpr[] args, int k) { checkContextMatch(args); return (BoolExpr) Expr.create(this, Native.mkAtleast(nCtx(), args.length, AST.arrayToNative(args), k)); } /** * Create a pseudo-Boolean less-or-equal constraint. */ public BoolExpr mkPBLe(int[] coeffs, BoolExpr[] args, int k) { checkContextMatch(args); return (BoolExpr) Expr.create(this, Native.mkPble(nCtx(), args.length, AST.arrayToNative(args), coeffs, k)); } /** * Create a pseudo-Boolean greater-or-equal constraint. */ public BoolExpr mkPBGe(int[] coeffs, BoolExpr[] args, int k) { checkContextMatch(args); return (BoolExpr) Expr.create(this, Native.mkPbge(nCtx(), args.length, AST.arrayToNative(args), coeffs, k)); } /** * Create a pseudo-Boolean equal constraint. */ public BoolExpr mkPBEq(int[] coeffs, BoolExpr[] args, int k) { checkContextMatch(args); return (BoolExpr) Expr.create(this, Native.mkPbeq(nCtx(), args.length, AST.arrayToNative(args), coeffs, k)); } /** * Create a Term of a given sort. * @param v A string representing the term value in decimal notation. If the given sort is a real, then the * Term can be a rational, that is, a string of the form * {@code [num]* / [num]*}. * @param ty The sort of the * numeral. In the current implementation, the given sort can be an int, * real, or bit-vectors of arbitrary size. * * @return A Term with value {@code v} and sort {@code ty} **/ public Expr mkNumeral(String v, Sort ty) { checkContextMatch(ty); return Expr.create(this, Native.mkNumeral(nCtx(), v, ty.getNativeObject())); } /** * Create a Term of a given sort. This function can be used to create * numerals that fit in a machine integer. It is slightly faster than * {@code MakeNumeral} since it is not necessary to parse a string. * * @param v Value of the numeral * @param ty Sort of the numeral * * @return A Term with value {@code v} and type {@code ty} **/ public Expr mkNumeral(int v, Sort ty) { checkContextMatch(ty); return Expr.create(this, Native.mkInt(nCtx(), v, ty.getNativeObject())); } /** * Create a Term of a given sort. This function can be used to create * numerals that fit in a machine integer. It is slightly faster than * {@code MakeNumeral} since it is not necessary to parse a string. * * @param v Value of the numeral * @param ty Sort of the numeral * * @return A Term with value {@code v} and type {@code ty} **/ public Expr mkNumeral(long v, Sort ty) { checkContextMatch(ty); return Expr.create(this, Native.mkInt64(nCtx(), v, ty.getNativeObject())); } /** * Create a real from a fraction. * @param num numerator of rational. * @param den denominator of rational. * * @return A Term with value {@code num}/{@code den} * and sort Real * @see #mkNumeral(String,Sort) **/ public RatNum mkReal(int num, int den) { if (den == 0) { throw new Z3Exception("Denominator is zero"); } return new RatNum(this, Native.mkReal(nCtx(), num, den)); } /** * Create a real numeral. * @param v A string representing the Term value in decimal notation. * * @return A Term with value {@code v} and sort Real **/ public RatNum mkReal(String v) { return new RatNum(this, Native.mkNumeral(nCtx(), v, getRealSort() .getNativeObject())); } /** * Create a real numeral. * @param v value of the numeral. * * @return A Term with value {@code v} and sort Real **/ public RatNum mkReal(int v) { return new RatNum(this, Native.mkInt(nCtx(), v, getRealSort() .getNativeObject())); } /** * Create a real numeral. * @param v value of the numeral. * * @return A Term with value {@code v} and sort Real **/ public RatNum mkReal(long v) { return new RatNum(this, Native.mkInt64(nCtx(), v, getRealSort() .getNativeObject())); } /** * Create an integer numeral. * @param v A string representing the Term value in decimal notation. **/ public IntNum mkInt(String v) { return new IntNum(this, Native.mkNumeral(nCtx(), v, getIntSort() .getNativeObject())); } /** * Create an integer numeral. * @param v value of the numeral. * * @return A Term with value {@code v} and sort Integer **/ public IntNum mkInt(int v) { return new IntNum(this, Native.mkInt(nCtx(), v, getIntSort() .getNativeObject())); } /** * Create an integer numeral. * @param v value of the numeral. * * @return A Term with value {@code v} and sort Integer **/ public IntNum mkInt(long v) { return new IntNum(this, Native.mkInt64(nCtx(), v, getIntSort() .getNativeObject())); } /** * Create a bit-vector numeral. * @param v A string representing the value in decimal notation. * @param size the size of the bit-vector **/ public BitVecNum mkBV(String v, int size) { return (BitVecNum) mkNumeral(v, mkBitVecSort(size)); } /** * Create a bit-vector numeral. * @param v value of the numeral. * @param size the size of the bit-vector **/ public BitVecNum mkBV(int v, int size) { return (BitVecNum) mkNumeral(v, mkBitVecSort(size)); } /** * Create a bit-vector numeral. * @param v value of the numeral. * * @param size the size of the bit-vector **/ public BitVecNum mkBV(long v, int size) { return (BitVecNum) mkNumeral(v, mkBitVecSort(size)); } /** * Create a universal Quantifier. * * @param sorts the sorts of the bound variables. * @param names names of the bound variables * @param body the body of the quantifier. * @param weight quantifiers are associated with weights indicating the importance of using the quantifier during instantiation. By default, pass the weight 0. * @param patterns array containing the patterns created using {@code MkPattern}. * @param noPatterns array containing the anti-patterns created using {@code MkPattern}. * @param quantifierID optional symbol to track quantifier. * @param skolemID optional symbol to track skolem constants. * * @return Creates a forall formula, where * {@code weight} is the weight, {@code patterns} is * an array of patterns, {@code sorts} is an array with the sorts * of the bound variables, {@code names} is an array with the * 'names' of the bound variables, and {@code body} is the body * of the quantifier. Quantifiers are associated with weights indicating the * importance of using the quantifier during instantiation. * Note that the bound variables are de-Bruijn indices created using {@link #mkBound}. * Z3 applies the convention that the last element in {@code names} and * {@code sorts} refers to the variable with index 0, the second to last element * of {@code names} and {@code sorts} refers to the variable * with index 1, etc. **/ public Quantifier mkForall(Sort[] sorts, Symbol[] names, Expr body, int weight, Pattern[] patterns, Expr[] noPatterns, Symbol quantifierID, Symbol skolemID) { return Quantifier.of(this, true, sorts, names, body, weight, patterns, noPatterns, quantifierID, skolemID); } /** * Creates a universal quantifier using a list of constants that will form the set of bound variables. * @see #mkForall(Sort[],Symbol[],Expr,int,Pattern[],Expr[],Symbol,Symbol) **/ public Quantifier mkForall(Expr[] boundConstants, Expr body, int weight, Pattern[] patterns, Expr[] noPatterns, Symbol quantifierID, Symbol skolemID) { return Quantifier.of(this, true, boundConstants, body, weight, patterns, noPatterns, quantifierID, skolemID); } /** * Creates an existential quantifier using de-Bruijn indexed variables. * @see #mkForall(Sort[],Symbol[],Expr,int,Pattern[],Expr[],Symbol,Symbol) **/ public Quantifier mkExists(Sort[] sorts, Symbol[] names, Expr body, int weight, Pattern[] patterns, Expr[] noPatterns, Symbol quantifierID, Symbol skolemID) { return Quantifier.of(this, false, sorts, names, body, weight, patterns, noPatterns, quantifierID, skolemID); } /** * Creates an existential quantifier using a list of constants that will form the set of bound variables. * @see #mkForall(Sort[],Symbol[],Expr,int,Pattern[],Expr[],Symbol,Symbol) **/ public Quantifier mkExists(Expr[] boundConstants, Expr body, int weight, Pattern[] patterns, Expr[] noPatterns, Symbol quantifierID, Symbol skolemID) { return Quantifier.of(this, false, boundConstants, body, weight, patterns, noPatterns, quantifierID, skolemID); } /** * Create a Quantifier. * @see #mkForall(Sort[],Symbol[],Expr,int,Pattern[],Expr[],Symbol,Symbol) **/ public Quantifier mkQuantifier(boolean universal, Sort[] sorts, Symbol[] names, Expr body, int weight, Pattern[] patterns, Expr[] noPatterns, Symbol quantifierID, Symbol skolemID) { if (universal) return mkForall(sorts, names, body, weight, patterns, noPatterns, quantifierID, skolemID); else return mkExists(sorts, names, body, weight, patterns, noPatterns, quantifierID, skolemID); } /** * Create a Quantifier * @see #mkForall(Sort[],Symbol[],Expr,int,Pattern[],Expr[],Symbol,Symbol) **/ public Quantifier mkQuantifier(boolean universal, Expr[] boundConstants, Expr body, int weight, Pattern[] patterns, Expr[] noPatterns, Symbol quantifierID, Symbol skolemID) { if (universal) return mkForall(boundConstants, body, weight, patterns, noPatterns, quantifierID, skolemID); else return mkExists(boundConstants, body, weight, patterns, noPatterns, quantifierID, skolemID); } /** * Create a lambda expression. * * {@code sorts} is an array * with the sorts of the bound variables, {@code names} is an array with the * 'names' of the bound variables, and {@code body} is the body of the * lambda. * Note that the bound variables are de-Bruijn indices created using {@link #mkBound} * Z3 applies the convention that the last element in {@code names} and * {@code sorts} refers to the variable with index 0, the second to last element * of {@code names} and {@code sorts} refers to the variable * with index 1, etc. * * @param sorts the sorts of the bound variables. * @param names names of the bound variables. * @param body the body of the quantifier. **/ public Lambda mkLambda(Sort[] sorts, Symbol[] names, Expr body) { return Lambda.of(this, sorts, names, body); } /** * Create a lambda expression. * * Creates a lambda expression using a list of constants that will * form the set of bound variables. **/ public Lambda mkLambda(Expr[] boundConstants, Expr body) { return Lambda.of(this, boundConstants, body); } /** * Selects the format used for pretty-printing expressions. * Remarks: The * default mode for pretty printing expressions is to produce SMT-LIB style * output where common subexpressions are printed at each occurrence. The * mode is called Z3_PRINT_SMTLIB_FULL. To print shared common * subexpressions only once, use the Z3_PRINT_LOW_LEVEL mode. To print in * way that conforms to SMT-LIB standards and uses let expressions to share * common sub-expressions use Z3_PRINT_SMTLIB_COMPLIANT. * @see AST#toString * @see Pattern#toString * @see FuncDecl#toString * @see Sort#toString **/ public void setPrintMode(Z3_ast_print_mode value) { Native.setAstPrintMode(nCtx(), value.toInt()); } /** * Convert a benchmark into an SMT-LIB formatted string. * @param name Name of the benchmark. The argument is optional. * * @param logic The benchmark logic. * @param status The status string (sat, unsat, or unknown) * @param attributes Other attributes, such as source, difficulty or * category. * @param assumptions Auxiliary assumptions. * @param formula Formula to be checked for consistency in conjunction with assumptions. * * @return A string representation of the benchmark. **/ public String benchmarkToSMTString(String name, String logic, String status, String attributes, BoolExpr[] assumptions, BoolExpr formula) { return Native.benchmarkToSmtlibString(nCtx(), name, logic, status, attributes, assumptions.length, AST.arrayToNative(assumptions), formula.getNativeObject()); } /** * Parse the given string using the SMT-LIB2 parser. * * @return A conjunction of assertions. * * If the string contains push/pop commands, the * set of assertions returned are the ones in the * last scope level. **/ public BoolExpr[] parseSMTLIB2String(String str, Symbol[] sortNames, Sort[] sorts, Symbol[] declNames, FuncDecl[] decls) { int csn = Symbol.arrayLength(sortNames); int cs = Sort.arrayLength(sorts); int cdn = Symbol.arrayLength(declNames); int cd = AST.arrayLength(decls); if (csn != cs || cdn != cd) { throw new Z3Exception("Argument size mismatch"); } ASTVector v = new ASTVector(this, Native.parseSmtlib2String(nCtx(), str, AST.arrayLength(sorts), Symbol.arrayToNative(sortNames), AST.arrayToNative(sorts), AST.arrayLength(decls), Symbol.arrayToNative(declNames), AST.arrayToNative(decls))); return v.ToBoolExprArray(); } /** * Parse the given file using the SMT-LIB2 parser. * @see #parseSMTLIB2String **/ public BoolExpr[] parseSMTLIB2File(String fileName, Symbol[] sortNames, Sort[] sorts, Symbol[] declNames, FuncDecl[] decls) { int csn = Symbol.arrayLength(sortNames); int cs = Sort.arrayLength(sorts); int cdn = Symbol.arrayLength(declNames); int cd = AST.arrayLength(decls); if (csn != cs || cdn != cd) throw new Z3Exception("Argument size mismatch"); ASTVector v = new ASTVector(this, Native.parseSmtlib2File(nCtx(), fileName, AST.arrayLength(sorts), Symbol.arrayToNative(sortNames), AST.arrayToNative(sorts), AST.arrayLength(decls), Symbol.arrayToNative(declNames), AST.arrayToNative(decls))); return v.ToBoolExprArray(); } /** * Creates a new Goal. * Remarks: Note that the Context must have been * created with proof generation support if {@code proofs} is set * to true here. * @param models Indicates whether model generation should be enabled. * @param unsatCores Indicates whether unsat core generation should be enabled. * @param proofs Indicates whether proof generation should be * enabled. **/ public Goal mkGoal(boolean models, boolean unsatCores, boolean proofs) { return new Goal(this, models, unsatCores, proofs); } /** * Creates a new ParameterSet. **/ public Params mkParams() { return new Params(this); } /** * The number of supported tactics. **/ public int getNumTactics() { return Native.getNumTactics(nCtx()); } /** * The names of all supported tactics. **/ public String[] getTacticNames() { int n = getNumTactics(); String[] res = new String[n]; for (int i = 0; i < n; i++) res[i] = Native.getTacticName(nCtx(), i); return res; } /** * Returns a string containing a description of the tactic with the given * name. **/ public String getTacticDescription(String name) { return Native.tacticGetDescr(nCtx(), name); } /** * Creates a new Tactic. **/ public Tactic mkTactic(String name) { return new Tactic(this, name); } /** * Create a tactic that applies {@code t1} to a Goal and then * {@code t2} to every subgoal produced by {@code t1} **/ public Tactic andThen(Tactic t1, Tactic t2, Tactic... ts) { checkContextMatch(t1); checkContextMatch(t2); checkContextMatch(ts); long last = 0; if (ts != null && ts.length > 0) { last = ts[ts.length - 1].getNativeObject(); for (int i = ts.length - 2; i >= 0; i--) { last = Native.tacticAndThen(nCtx(), ts[i].getNativeObject(), last); } } if (last != 0) { last = Native.tacticAndThen(nCtx(), t2.getNativeObject(), last); return new Tactic(this, Native.tacticAndThen(nCtx(), t1.getNativeObject(), last)); } else return new Tactic(this, Native.tacticAndThen(nCtx(), t1.getNativeObject(), t2.getNativeObject())); } /** * Create a tactic that applies {@code t1} to a Goal and then * {@code t2} to every subgoal produced by {@code t1} * * Remarks: Shorthand for {@code AndThen}. **/ public Tactic then(Tactic t1, Tactic t2, Tactic... ts) { return andThen(t1, t2, ts); } /** * Create a tactic that first applies {@code t1} to a Goal and if * it fails then returns the result of {@code t2} applied to the * Goal. **/ public Tactic orElse(Tactic t1, Tactic t2) { checkContextMatch(t1); checkContextMatch(t2); return new Tactic(this, Native.tacticOrElse(nCtx(), t1.getNativeObject(), t2.getNativeObject())); } /** * Create a tactic that applies {@code t} to a goal for {@code ms} milliseconds. * Remarks: If {@code t} does not * terminate within {@code ms} milliseconds, then it fails. * **/ public Tactic tryFor(Tactic t, int ms) { checkContextMatch(t); return new Tactic(this, Native.tacticTryFor(nCtx(), t.getNativeObject(), ms)); } /** * Create a tactic that applies {@code t} to a given goal if the * probe {@code p} evaluates to true. * Remarks: If {@code p} evaluates to false, then the new tactic behaves like the * {@code skip} tactic. **/ public Tactic when(Probe p, Tactic t) { checkContextMatch(t); checkContextMatch(p); return new Tactic(this, Native.tacticWhen(nCtx(), p.getNativeObject(), t.getNativeObject())); } /** * Create a tactic that applies {@code t1} to a given goal if the * probe {@code p} evaluates to true and {@code t2} * otherwise. **/ public Tactic cond(Probe p, Tactic t1, Tactic t2) { checkContextMatch(p); checkContextMatch(t1); checkContextMatch(t2); return new Tactic(this, Native.tacticCond(nCtx(), p.getNativeObject(), t1.getNativeObject(), t2.getNativeObject())); } /** * Create a tactic that keeps applying {@code t} until the goal * is not modified anymore or the maximum number of iterations {@code max} is reached. **/ public Tactic repeat(Tactic t, int max) { checkContextMatch(t); return new Tactic(this, Native.tacticRepeat(nCtx(), t.getNativeObject(), max)); } /** * Create a tactic that just returns the given goal. **/ public Tactic skip() { return new Tactic(this, Native.tacticSkip(nCtx())); } /** * Create a tactic always fails. **/ public Tactic fail() { return new Tactic(this, Native.tacticFail(nCtx())); } /** * Create a tactic that fails if the probe {@code p} evaluates to * false. **/ public Tactic failIf(Probe p) { checkContextMatch(p); return new Tactic(this, Native.tacticFailIf(nCtx(), p.getNativeObject())); } /** * Create a tactic that fails if the goal is not trivially satisfiable (i.e., * empty) or trivially unsatisfiable (i.e., contains `false'). **/ public Tactic failIfNotDecided() { return new Tactic(this, Native.tacticFailIfNotDecided(nCtx())); } /** * Create a tactic that applies {@code t} using the given set of * parameters {@code p}. **/ public Tactic usingParams(Tactic t, Params p) { checkContextMatch(t); checkContextMatch(p); return new Tactic(this, Native.tacticUsingParams(nCtx(), t.getNativeObject(), p.getNativeObject())); } /** * Create a tactic that applies {@code t} using the given set of * parameters {@code p}. * Remarks: Alias for * {@code UsingParams} **/ public Tactic with(Tactic t, Params p) { return usingParams(t, p); } /** * Create a tactic that applies the given tactics in parallel until one of them succeeds (i.e., the first that doesn't fail). **/ public Tactic parOr(Tactic... t) { checkContextMatch(t); return new Tactic(this, Native.tacticParOr(nCtx(), Tactic.arrayLength(t), Tactic.arrayToNative(t))); } /** * Create a tactic that applies {@code t1} to a given goal and * then {@code t2} to every subgoal produced by {@code t1}. The subgoals are processed in parallel. **/ public Tactic parAndThen(Tactic t1, Tactic t2) { checkContextMatch(t1); checkContextMatch(t2); return new Tactic(this, Native.tacticParAndThen(nCtx(), t1.getNativeObject(), t2.getNativeObject())); } /** * Interrupt the execution of a Z3 procedure. * Remarks: This procedure can be * used to interrupt: solvers, simplifiers and tactics. **/ public void interrupt() { Native.interrupt(nCtx()); } /** * The number of supported Probes. **/ public int getNumProbes() { return Native.getNumProbes(nCtx()); } /** * The names of all supported Probes. **/ public String[] getProbeNames() { int n = getNumProbes(); String[] res = new String[n]; for (int i = 0; i < n; i++) res[i] = Native.getProbeName(nCtx(), i); return res; } /** * Returns a string containing a description of the probe with the given * name. **/ public String getProbeDescription(String name) { return Native.probeGetDescr(nCtx(), name); } /** * Creates a new Probe. **/ public Probe mkProbe(String name) { return new Probe(this, name); } /** * Create a probe that always evaluates to {@code val}. **/ public Probe constProbe(double val) { return new Probe(this, Native.probeConst(nCtx(), val)); } /** * Create a probe that evaluates to {@code true} when the value returned by * {@code p1} is less than the value returned by {@code p2} **/ public Probe lt(Probe p1, Probe p2) { checkContextMatch(p1); checkContextMatch(p2); return new Probe(this, Native.probeLt(nCtx(), p1.getNativeObject(), p2.getNativeObject())); } /** * Create a probe that evaluates to {@code true} when the value returned by * {@code p1} is greater than the value returned by {@code p2} **/ public Probe gt(Probe p1, Probe p2) { checkContextMatch(p1); checkContextMatch(p2); return new Probe(this, Native.probeGt(nCtx(), p1.getNativeObject(), p2.getNativeObject())); } /** * Create a probe that evaluates to {@code true} when the value returned by * {@code p1} is less than or equal the value returned by * {@code p2} **/ public Probe le(Probe p1, Probe p2) { checkContextMatch(p1); checkContextMatch(p2); return new Probe(this, Native.probeLe(nCtx(), p1.getNativeObject(), p2.getNativeObject())); } /** * Create a probe that evaluates to {@code true} when the value returned by * {@code p1} is greater than or equal the value returned by * {@code p2} **/ public Probe ge(Probe p1, Probe p2) { checkContextMatch(p1); checkContextMatch(p2); return new Probe(this, Native.probeGe(nCtx(), p1.getNativeObject(), p2.getNativeObject())); } /** * Create a probe that evaluates to {@code true} when the value returned by * {@code p1} is equal to the value returned by {@code p2} **/ public Probe eq(Probe p1, Probe p2) { checkContextMatch(p1); checkContextMatch(p2); return new Probe(this, Native.probeEq(nCtx(), p1.getNativeObject(), p2.getNativeObject())); } /** * Create a probe that evaluates to {@code true} when the value {@code p1} and {@code p2} evaluate to {@code true}. **/ public Probe and(Probe p1, Probe p2) { checkContextMatch(p1); checkContextMatch(p2); return new Probe(this, Native.probeAnd(nCtx(), p1.getNativeObject(), p2.getNativeObject())); } /** * Create a probe that evaluates to {@code true} when the value {@code p1} or {@code p2} evaluate to {@code true}. **/ public Probe or(Probe p1, Probe p2) { checkContextMatch(p1); checkContextMatch(p2); return new Probe(this, Native.probeOr(nCtx(), p1.getNativeObject(), p2.getNativeObject())); } /** * Create a probe that evaluates to {@code true} when the value {@code p} does not evaluate to {@code true}. **/ public Probe not(Probe p) { checkContextMatch(p); return new Probe(this, Native.probeNot(nCtx(), p.getNativeObject())); } /** * Creates a new (incremental) solver. * Remarks: This solver also uses a set * of builtin tactics for handling the first check-sat command, and * check-sat commands that take more than a given number of milliseconds to * be solved. **/ public Solver mkSolver() { return mkSolver((Symbol) null); } /** * Creates a new (incremental) solver. * Remarks: This solver also uses a set * of builtin tactics for handling the first check-sat command, and * check-sat commands that take more than a given number of milliseconds to * be solved. **/ public Solver mkSolver(Symbol logic) { if (logic == null) return new Solver(this, Native.mkSolver(nCtx())); else return new Solver(this, Native.mkSolverForLogic(nCtx(), logic.getNativeObject())); } /** * Creates a new (incremental) solver. * @see #mkSolver(Symbol) **/ public Solver mkSolver(String logic) { return mkSolver(mkSymbol(logic)); } /** * Creates a new (incremental) solver. **/ public Solver mkSimpleSolver() { return new Solver(this, Native.mkSimpleSolver(nCtx())); } /** * Creates a solver that is implemented using the given tactic. * Remarks: * The solver supports the commands {@code Push} and {@code Pop}, * but it will always solve each check from scratch. **/ public Solver mkSolver(Tactic t) { return new Solver(this, Native.mkSolverFromTactic(nCtx(), t.getNativeObject())); } /** * Create a Fixedpoint context. **/ public Fixedpoint mkFixedpoint() { return new Fixedpoint(this); } /** * Create a Optimize context. **/ public Optimize mkOptimize() { return new Optimize(this); } /** * Create the floating-point RoundingMode sort. * @throws Z3Exception **/ public FPRMSort mkFPRoundingModeSort() { return new FPRMSort(this); } /** * Create a numeral of RoundingMode sort which represents the NearestTiesToEven rounding mode. * @throws Z3Exception **/ public FPRMExpr mkFPRoundNearestTiesToEven() { return new FPRMExpr(this, Native.mkFpaRoundNearestTiesToEven(nCtx())); } /** * Create a numeral of RoundingMode sort which represents the NearestTiesToEven rounding mode. * @throws Z3Exception **/ public FPRMNum mkFPRNE() { return new FPRMNum(this, Native.mkFpaRne(nCtx())); } /** * Create a numeral of RoundingMode sort which represents the NearestTiesToAway rounding mode. * @throws Z3Exception **/ public FPRMNum mkFPRoundNearestTiesToAway() { return new FPRMNum(this, Native.mkFpaRoundNearestTiesToAway(nCtx())); } /** * Create a numeral of RoundingMode sort which represents the NearestTiesToAway rounding mode. * @throws Z3Exception **/ public FPRMNum mkFPRNA() { return new FPRMNum(this, Native.mkFpaRna(nCtx())); } /** * Create a numeral of RoundingMode sort which represents the RoundTowardPositive rounding mode. * @throws Z3Exception **/ public FPRMNum mkFPRoundTowardPositive() { return new FPRMNum(this, Native.mkFpaRoundTowardPositive(nCtx())); } /** * Create a numeral of RoundingMode sort which represents the RoundTowardPositive rounding mode. * @throws Z3Exception **/ public FPRMNum mkFPRTP() { return new FPRMNum(this, Native.mkFpaRtp(nCtx())); } /** * Create a numeral of RoundingMode sort which represents the RoundTowardNegative rounding mode. * @throws Z3Exception **/ public FPRMNum mkFPRoundTowardNegative() { return new FPRMNum(this, Native.mkFpaRoundTowardNegative(nCtx())); } /** * Create a numeral of RoundingMode sort which represents the RoundTowardNegative rounding mode. * @throws Z3Exception **/ public FPRMNum mkFPRTN() { return new FPRMNum(this, Native.mkFpaRtn(nCtx())); } /** * Create a numeral of RoundingMode sort which represents the RoundTowardZero rounding mode. * @throws Z3Exception **/ public FPRMNum mkFPRoundTowardZero() { return new FPRMNum(this, Native.mkFpaRoundTowardZero(nCtx())); } /** * Create a numeral of RoundingMode sort which represents the RoundTowardZero rounding mode. * @throws Z3Exception **/ public FPRMNum mkFPRTZ() { return new FPRMNum(this, Native.mkFpaRtz(nCtx())); } /** * Create a FloatingPoint sort. * @param ebits exponent bits in the FloatingPoint sort. * @param sbits significand bits in the FloatingPoint sort. * @throws Z3Exception **/ public FPSort mkFPSort(int ebits, int sbits) { return new FPSort(this, ebits, sbits); } /** * Create the half-precision (16-bit) FloatingPoint sort. * @throws Z3Exception **/ public FPSort mkFPSortHalf() { return new FPSort(this, Native.mkFpaSortHalf(nCtx())); } /** * Create the half-precision (16-bit) FloatingPoint sort. * @throws Z3Exception **/ public FPSort mkFPSort16() { return new FPSort(this, Native.mkFpaSort16(nCtx())); } /** * Create the single-precision (32-bit) FloatingPoint sort. * @throws Z3Exception **/ public FPSort mkFPSortSingle() { return new FPSort(this, Native.mkFpaSortSingle(nCtx())); } /** * Create the single-precision (32-bit) FloatingPoint sort. * @throws Z3Exception **/ public FPSort mkFPSort32() { return new FPSort(this, Native.mkFpaSort32(nCtx())); } /** * Create the double-precision (64-bit) FloatingPoint sort. * @throws Z3Exception **/ public FPSort mkFPSortDouble() { return new FPSort(this, Native.mkFpaSortDouble(nCtx())); } /** * Create the double-precision (64-bit) FloatingPoint sort. * @throws Z3Exception **/ public FPSort mkFPSort64() { return new FPSort(this, Native.mkFpaSort64(nCtx())); } /** * Create the quadruple-precision (128-bit) FloatingPoint sort. * @throws Z3Exception **/ public FPSort mkFPSortQuadruple() { return new FPSort(this, Native.mkFpaSortQuadruple(nCtx())); } /** * Create the quadruple-precision (128-bit) FloatingPoint sort. * @throws Z3Exception **/ public FPSort mkFPSort128() { return new FPSort(this, Native.mkFpaSort128(nCtx())); } /** * Create a NaN of sort s. * @param s FloatingPoint sort. * @throws Z3Exception **/ public FPNum mkFPNaN(FPSort s) { return new FPNum(this, Native.mkFpaNan(nCtx(), s.getNativeObject())); } /** * Create a floating-point infinity of sort s. * @param s FloatingPoint sort. * @param negative indicates whether the result should be negative. * @throws Z3Exception **/ public FPNum mkFPInf(FPSort s, boolean negative) { return new FPNum(this, Native.mkFpaInf(nCtx(), s.getNativeObject(), negative)); } /** * Create a floating-point zero of sort s. * @param s FloatingPoint sort. * @param negative indicates whether the result should be negative. * @throws Z3Exception **/ public FPNum mkFPZero(FPSort s, boolean negative) { return new FPNum(this, Native.mkFpaZero(nCtx(), s.getNativeObject(), negative)); } /** * Create a numeral of FloatingPoint sort from a float. * @param v numeral value. * @param s FloatingPoint sort. * @throws Z3Exception **/ public FPNum mkFPNumeral(float v, FPSort s) { return new FPNum(this, Native.mkFpaNumeralFloat(nCtx(), v, s.getNativeObject())); } /** * Create a numeral of FloatingPoint sort from a double. * @param v numeral value. * @param s FloatingPoint sort. * @throws Z3Exception **/ public FPNum mkFPNumeral(double v, FPSort s) { return new FPNum(this, Native.mkFpaNumeralDouble(nCtx(), v, s.getNativeObject())); } /** * Create a numeral of FloatingPoint sort from an int. * @param v numeral value. * @param s FloatingPoint sort. * @throws Z3Exception **/ public FPNum mkFPNumeral(int v, FPSort s) { return new FPNum(this, Native.mkFpaNumeralInt(nCtx(), v, s.getNativeObject())); } /** * Create a numeral of FloatingPoint sort from a sign bit and two integers. * @param sgn the sign. * @param exp the exponent. * @param sig the significand. * @param s FloatingPoint sort. * @throws Z3Exception **/ public FPNum mkFPNumeral(boolean sgn, int exp, int sig, FPSort s) { return new FPNum(this, Native.mkFpaNumeralIntUint(nCtx(), sgn, exp, sig, s.getNativeObject())); } /** * Create a numeral of FloatingPoint sort from a sign bit and two 64-bit integers. * @param sgn the sign. * @param exp the exponent. * @param sig the significand. * @param s FloatingPoint sort. * @throws Z3Exception **/ public FPNum mkFPNumeral(boolean sgn, long exp, long sig, FPSort s) { return new FPNum(this, Native.mkFpaNumeralInt64Uint64(nCtx(), sgn, exp, sig, s.getNativeObject())); } /** * Create a numeral of FloatingPoint sort from a float. * @param v numeral value. * @param s FloatingPoint sort. * @throws Z3Exception **/ public FPNum mkFP(float v, FPSort s) { return mkFPNumeral(v, s); } /** * Create a numeral of FloatingPoint sort from a double. * @param v numeral value. * @param s FloatingPoint sort. * @throws Z3Exception **/ public FPNum mkFP(double v, FPSort s) { return mkFPNumeral(v, s); } /** * Create a numeral of FloatingPoint sort from an int. * @param v numeral value. * @param s FloatingPoint sort. * @throws Z3Exception **/ public FPNum mkFP(int v, FPSort s) { return mkFPNumeral(v, s); } /** * Create a numeral of FloatingPoint sort from a sign bit and two integers. * @param sgn the sign. * @param exp the exponent. * @param sig the significand. * @param s FloatingPoint sort. * @throws Z3Exception **/ public FPNum mkFP(boolean sgn, int exp, int sig, FPSort s) { return mkFPNumeral(sgn, exp, sig, s); } /** * Create a numeral of FloatingPoint sort from a sign bit and two 64-bit integers. * @param sgn the sign. * @param exp the exponent. * @param sig the significand. * @param s FloatingPoint sort. * @throws Z3Exception **/ public FPNum mkFP(boolean sgn, long exp, long sig, FPSort s) { return mkFPNumeral(sgn, exp, sig, s); } /** * Floating-point absolute value * @param t floating-point term * @throws Z3Exception **/ public FPExpr mkFPAbs(FPExpr t) { return new FPExpr(this, Native.mkFpaAbs(nCtx(), t.getNativeObject())); } /** * Floating-point negation * @param t floating-point term * @throws Z3Exception **/ public FPExpr mkFPNeg(FPExpr t) { return new FPExpr(this, Native.mkFpaNeg(nCtx(), t.getNativeObject())); } /** * Floating-point addition * @param rm rounding mode term * @param t1 floating-point term * @param t2 floating-point term * @throws Z3Exception **/ public FPExpr mkFPAdd(FPRMExpr rm, FPExpr t1, FPExpr t2) { return new FPExpr(this, Native.mkFpaAdd(nCtx(), rm.getNativeObject(), t1.getNativeObject(), t2.getNativeObject())); } /** * Floating-point subtraction * @param rm rounding mode term * @param t1 floating-point term * @param t2 floating-point term * @throws Z3Exception **/ public FPExpr mkFPSub(FPRMExpr rm, FPExpr t1, FPExpr t2) { return new FPExpr(this, Native.mkFpaSub(nCtx(), rm.getNativeObject(), t1.getNativeObject(), t2.getNativeObject())); } /** * Floating-point multiplication * @param rm rounding mode term * @param t1 floating-point term * @param t2 floating-point term * @throws Z3Exception **/ public FPExpr mkFPMul(FPRMExpr rm, FPExpr t1, FPExpr t2) { return new FPExpr(this, Native.mkFpaMul(nCtx(), rm.getNativeObject(), t1.getNativeObject(), t2.getNativeObject())); } /** * Floating-point division * @param rm rounding mode term * @param t1 floating-point term * @param t2 floating-point term * @throws Z3Exception **/ public FPExpr mkFPDiv(FPRMExpr rm, FPExpr t1, FPExpr t2) { return new FPExpr(this, Native.mkFpaDiv(nCtx(), rm.getNativeObject(), t1.getNativeObject(), t2.getNativeObject())); } /** * Floating-point fused multiply-add * @param rm rounding mode term * @param t1 floating-point term * @param t2 floating-point term * @param t3 floating-point term * Remarks: * The result is round((t1 * t2) + t3) * @throws Z3Exception **/ public FPExpr mkFPFMA(FPRMExpr rm, FPExpr t1, FPExpr t2, FPExpr t3) { return new FPExpr(this, Native.mkFpaFma(nCtx(), rm.getNativeObject(), t1.getNativeObject(), t2.getNativeObject(), t3.getNativeObject())); } /** * Floating-point square root * @param rm rounding mode term * @param t floating-point term * @throws Z3Exception **/ public FPExpr mkFPSqrt(FPRMExpr rm, FPExpr t) { return new FPExpr(this, Native.mkFpaSqrt(nCtx(), rm.getNativeObject(), t.getNativeObject())); } /** * Floating-point remainder * @param t1 floating-point term * @param t2 floating-point term * @throws Z3Exception **/ public FPExpr mkFPRem(FPExpr t1, FPExpr t2) { return new FPExpr(this, Native.mkFpaRem(nCtx(), t1.getNativeObject(), t2.getNativeObject())); } /** * Floating-point roundToIntegral. Rounds a floating-point number to * the closest integer, again represented as a floating-point number. * @param rm term of RoundingMode sort * @param t floating-point term * @throws Z3Exception **/ public FPExpr mkFPRoundToIntegral(FPRMExpr rm, FPExpr t) { return new FPExpr(this, Native.mkFpaRoundToIntegral(nCtx(), rm.getNativeObject(), t.getNativeObject())); } /** * Minimum of floating-point numbers. * @param t1 floating-point term * @param t2 floating-point term * @throws Z3Exception **/ public FPExpr mkFPMin(FPExpr t1, FPExpr t2) { return new FPExpr(this, Native.mkFpaMin(nCtx(), t1.getNativeObject(), t2.getNativeObject())); } /** * Maximum of floating-point numbers. * @param t1 floating-point term * @param t2 floating-point term * @throws Z3Exception **/ public FPExpr mkFPMax(FPExpr t1, FPExpr t2) { return new FPExpr(this, Native.mkFpaMax(nCtx(), t1.getNativeObject(), t2.getNativeObject())); } /** * Floating-point less than or equal. * @param t1 floating-point term * @param t2 floating-point term * @throws Z3Exception **/ public BoolExpr mkFPLEq(FPExpr t1, FPExpr t2) { return new BoolExpr(this, Native.mkFpaLeq(nCtx(), t1.getNativeObject(), t2.getNativeObject())); } /** * Floating-point less than. * @param t1 floating-point term * @param t2 floating-point term * @throws Z3Exception **/ public BoolExpr mkFPLt(FPExpr t1, FPExpr t2) { return new BoolExpr(this, Native.mkFpaLt(nCtx(), t1.getNativeObject(), t2.getNativeObject())); } /** * Floating-point greater than or equal. * @param t1 floating-point term * @param t2 floating-point term * @throws Z3Exception **/ public BoolExpr mkFPGEq(FPExpr t1, FPExpr t2) { return new BoolExpr(this, Native.mkFpaGeq(nCtx(), t1.getNativeObject(), t2.getNativeObject())); } /** * Floating-point greater than. * @param t1 floating-point term * @param t2 floating-point term * @throws Z3Exception **/ public BoolExpr mkFPGt(FPExpr t1, FPExpr t2) { return new BoolExpr(this, Native.mkFpaGt(nCtx(), t1.getNativeObject(), t2.getNativeObject())); } /** * Floating-point equality. * @param t1 floating-point term * @param t2 floating-point term * Remarks: * Note that this is IEEE 754 equality (as opposed to standard =). * @throws Z3Exception **/ public BoolExpr mkFPEq(FPExpr t1, FPExpr t2) { return new BoolExpr(this, Native.mkFpaEq(nCtx(), t1.getNativeObject(), t2.getNativeObject())); } /** * Predicate indicating whether t is a normal floating-point number.\ * @param t floating-point term * @throws Z3Exception **/ public BoolExpr mkFPIsNormal(FPExpr t) { return new BoolExpr(this, Native.mkFpaIsNormal(nCtx(), t.getNativeObject())); } /** * Predicate indicating whether t is a subnormal floating-point number.\ * @param t floating-point term * @throws Z3Exception **/ public BoolExpr mkFPIsSubnormal(FPExpr t) { return new BoolExpr(this, Native.mkFpaIsSubnormal(nCtx(), t.getNativeObject())); } /** * Predicate indicating whether t is a floating-point number with zero value, i.e., +0 or -0. * @param t floating-point term * @throws Z3Exception **/ public BoolExpr mkFPIsZero(FPExpr t) { return new BoolExpr(this, Native.mkFpaIsZero(nCtx(), t.getNativeObject())); } /** * Predicate indicating whether t is a floating-point number representing +oo or -oo. * @param t floating-point term * @throws Z3Exception **/ public BoolExpr mkFPIsInfinite(FPExpr t) { return new BoolExpr(this, Native.mkFpaIsInfinite(nCtx(), t.getNativeObject())); } /** * Predicate indicating whether t is a NaN. * @param t floating-point term * @throws Z3Exception **/ public BoolExpr mkFPIsNaN(FPExpr t) { return new BoolExpr(this, Native.mkFpaIsNan(nCtx(), t.getNativeObject())); } /** * Predicate indicating whether t is a negative floating-point number. * @param t floating-point term * @throws Z3Exception **/ public BoolExpr mkFPIsNegative(FPExpr t) { return new BoolExpr(this, Native.mkFpaIsNegative(nCtx(), t.getNativeObject())); } /** * Predicate indicating whether t is a positive floating-point number. * @param t floating-point term * @throws Z3Exception **/ public BoolExpr mkFPIsPositive(FPExpr t) { return new BoolExpr(this, Native.mkFpaIsPositive(nCtx(), t.getNativeObject())); } /** * Create an expression of FloatingPoint sort from three bit-vector expressions. * @param sgn bit-vector term (of size 1) representing the sign. * @param sig bit-vector term representing the significand. * @param exp bit-vector term representing the exponent. * Remarks: * This is the operator named `fp' in the SMT FP theory definition. * Note that sgn is required to be a bit-vector of size 1. Significand and exponent * are required to be greater than 1 and 2 respectively. The FloatingPoint sort * of the resulting expression is automatically determined from the bit-vector sizes * of the arguments. * @throws Z3Exception **/ public FPExpr mkFP(BitVecExpr sgn, BitVecExpr sig, BitVecExpr exp) { return new FPExpr(this, Native.mkFpaFp(nCtx(), sgn.getNativeObject(), sig.getNativeObject(), exp.getNativeObject())); } /** * Conversion of a single IEEE 754-2008 bit-vector into a floating-point number. * @param bv bit-vector value (of size m). * @param s FloatingPoint sort (ebits+sbits == m) * Remarks: * Produces a term that represents the conversion of a bit-vector term bv to a * floating-point term of sort s. The bit-vector size of bv (m) must be equal * to ebits+sbits of s. The format of the bit-vector is as defined by the * IEEE 754-2008 interchange format. * @throws Z3Exception **/ public FPExpr mkFPToFP(BitVecExpr bv, FPSort s) { return new FPExpr(this, Native.mkFpaToFpBv(nCtx(), bv.getNativeObject(), s.getNativeObject())); } /** * Conversion of a FloatingPoint term into another term of different FloatingPoint sort. * @param rm RoundingMode term. * @param t FloatingPoint term. * @param s FloatingPoint sort. * Remarks: * 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. * @throws Z3Exception **/ public FPExpr mkFPToFP(FPRMExpr rm, FPExpr t, FPSort s) { return new FPExpr(this, Native.mkFpaToFpFloat(nCtx(), rm.getNativeObject(), t.getNativeObject(), s.getNativeObject())); } /** * Conversion of a term of real sort into a term of FloatingPoint sort. * @param rm RoundingMode term. * @param t term of Real sort. * @param s FloatingPoint sort. * Remarks: * 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. * @throws Z3Exception **/ public FPExpr mkFPToFP(FPRMExpr rm, RealExpr t, FPSort s) { return new FPExpr(this, Native.mkFpaToFpReal(nCtx(), rm.getNativeObject(), t.getNativeObject(), s.getNativeObject())); } /** * Conversion of a 2's complement signed bit-vector term into a term of FloatingPoint sort. * @param rm RoundingMode term. * @param t term of bit-vector sort. * @param s FloatingPoint sort. * @param signed flag indicating whether t is interpreted as signed or unsigned bit-vector. * Remarks: * 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 * 2's complement format (when signed==true, otherwise unsigned). If necessary, the * result will be rounded according to rounding mode rm. * @throws Z3Exception **/ public FPExpr mkFPToFP(FPRMExpr rm, BitVecExpr t, FPSort s, boolean signed) { if (signed) return new FPExpr(this, Native.mkFpaToFpSigned(nCtx(), rm.getNativeObject(), t.getNativeObject(), s.getNativeObject())); else return new FPExpr(this, Native.mkFpaToFpUnsigned(nCtx(), rm.getNativeObject(), t.getNativeObject(), s.getNativeObject())); } /** * Conversion of a floating-point number to another FloatingPoint sort s. * @param s FloatingPoint sort * @param rm floating-point rounding mode term * @param t floating-point term * Remarks: * Produces a term that represents the conversion of a floating-point term t to a different * FloatingPoint sort s. If necessary, rounding according to rm is applied. * @throws Z3Exception **/ public FPExpr mkFPToFP(FPSort s, FPRMExpr rm, FPExpr t) { return new FPExpr(this, Native.mkFpaToFpFloat(nCtx(), s.getNativeObject(), rm.getNativeObject(), t.getNativeObject())); } /** * Conversion of a floating-point term into a bit-vector. * @param rm RoundingMode term. * @param t FloatingPoint term * @param sz Size of the resulting bit-vector. * @param signed Indicates whether the result is a signed or unsigned bit-vector. * Remarks: * Produces a term that represents the conversion of the floating-point term t into a * bit-vector term of size sz in 2's complement format (signed when signed==true). If necessary, * the result will be rounded according to rounding mode rm. * @throws Z3Exception **/ public BitVecExpr mkFPToBV(FPRMExpr rm, FPExpr t, int sz, boolean signed) { if (signed) return new BitVecExpr(this, Native.mkFpaToSbv(nCtx(), rm.getNativeObject(), t.getNativeObject(), sz)); else return new BitVecExpr(this, Native.mkFpaToUbv(nCtx(), rm.getNativeObject(), t.getNativeObject(), sz)); } /** * Conversion of a floating-point term into a real-numbered term. * @param t FloatingPoint term * Remarks: * Produces a term that represents the conversion of the floating-point term t into a * real number. Note that this type of conversion will often result in non-linear * constraints over real terms. * @throws Z3Exception **/ public RealExpr mkFPToReal(FPExpr t) { return new RealExpr(this, Native.mkFpaToReal(nCtx(), t.getNativeObject())); } /** * Conversion of a floating-point term into a bit-vector term in IEEE 754-2008 format. * @param t FloatingPoint term. * Remarks: * The size of the resulting bit-vector is automatically determined. Note that * IEEE 754-2008 allows multiple different representations of NaN. This conversion * knows only one NaN and it will always produce the same bit-vector representation of * that NaN. * @throws Z3Exception **/ public BitVecExpr mkFPToIEEEBV(FPExpr t) { return new BitVecExpr(this, Native.mkFpaToIeeeBv(nCtx(), t.getNativeObject())); } /** * Conversion of a real-sorted significand and an integer-sorted exponent into a term of FloatingPoint sort. * @param rm RoundingMode term. * @param exp Exponent term of Int sort. * @param sig Significand term of Real sort. * @param s FloatingPoint sort. * Remarks: * 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. * @throws Z3Exception **/ public BitVecExpr mkFPToFP(FPRMExpr rm, IntExpr exp, RealExpr sig, FPSort s) { return new BitVecExpr(this, Native.mkFpaToFpIntReal(nCtx(), rm.getNativeObject(), exp.getNativeObject(), sig.getNativeObject(), s.getNativeObject())); } /** * Wraps an AST. * Remarks: This function is used for transitions between * native and managed objects. Note that {@code nativeObject} * must be a native object obtained from Z3 (e.g., through * {@code UnwrapAST}) and that it must have a correct reference count. * @see Native#incRef * @see #unwrapAST * @param nativeObject The native pointer to wrap. **/ public AST wrapAST(long nativeObject) { return AST.create(this, nativeObject); } /** * Unwraps an AST. * Remarks: This function is used for transitions between * native and managed objects. It returns the native pointer to the AST. * Note that AST objects are reference counted and unwrapping an AST * disables automatic reference counting, i.e., all references to the IntPtr * that is returned must be handled externally and through native calls (see * e.g., * @see Native#incRef * @see #wrapAST * @param a The AST to unwrap. **/ public long unwrapAST(AST a) { return a.getNativeObject(); } /** * Return a string describing all available parameters to * {@code Expr.Simplify}. **/ public String SimplifyHelp() { return Native.simplifyGetHelp(nCtx()); } /** * Retrieves parameter descriptions for simplifier. **/ public ParamDescrs getSimplifyParameterDescriptions() { return new ParamDescrs(this, Native.simplifyGetParamDescrs(nCtx())); } /** * Update a mutable configuration parameter. * Remarks: The list of all * configuration parameters can be obtained using the Z3 executable: * {@code z3.exe -ini?} Only a few configuration parameters are mutable * once the context is created. An exception is thrown when trying to modify * an immutable parameter. **/ public void updateParamValue(String id, String value) { Native.updateParamValue(nCtx(), id, value); } long nCtx() { if (m_ctx == 0) throw new Z3Exception("Context closed"); return m_ctx; } void checkContextMatch(Z3Object other) { if (this != other.getContext()) throw new Z3Exception("Context mismatch"); } void checkContextMatch(Z3Object other1, Z3Object other2) { checkContextMatch(other1); checkContextMatch(other2); } void checkContextMatch(Z3Object other1, Z3Object other2, Z3Object other3) { checkContextMatch(other1); checkContextMatch(other2); checkContextMatch(other3); } void checkContextMatch(Z3Object[] arr) { if (arr != null) for (Z3Object a : arr) checkContextMatch(a); } private ASTDecRefQueue m_AST_DRQ = new ASTDecRefQueue(); private ASTMapDecRefQueue m_ASTMap_DRQ = new ASTMapDecRefQueue(); private ASTVectorDecRefQueue m_ASTVector_DRQ = new ASTVectorDecRefQueue(); private ApplyResultDecRefQueue m_ApplyResult_DRQ = new ApplyResultDecRefQueue(); private FuncInterpEntryDecRefQueue m_FuncEntry_DRQ = new FuncInterpEntryDecRefQueue(); private FuncInterpDecRefQueue m_FuncInterp_DRQ = new FuncInterpDecRefQueue(); private GoalDecRefQueue m_Goal_DRQ = new GoalDecRefQueue(); private ModelDecRefQueue m_Model_DRQ = new ModelDecRefQueue(); private ParamsDecRefQueue m_Params_DRQ = new ParamsDecRefQueue(); private ParamDescrsDecRefQueue m_ParamDescrs_DRQ = new ParamDescrsDecRefQueue(); private ProbeDecRefQueue m_Probe_DRQ = new ProbeDecRefQueue(); private SolverDecRefQueue m_Solver_DRQ = new SolverDecRefQueue(); private StatisticsDecRefQueue m_Statistics_DRQ = new StatisticsDecRefQueue(); private TacticDecRefQueue m_Tactic_DRQ = new TacticDecRefQueue(); private FixedpointDecRefQueue m_Fixedpoint_DRQ = new FixedpointDecRefQueue(); private OptimizeDecRefQueue m_Optimize_DRQ = new OptimizeDecRefQueue(); private ConstructorDecRefQueue m_Constructor_DRQ = new ConstructorDecRefQueue(); private ConstructorListDecRefQueue m_ConstructorList_DRQ = new ConstructorListDecRefQueue(); public IDecRefQueue getConstructorDRQ() { return m_Constructor_DRQ; } public IDecRefQueue getConstructorListDRQ() { return m_ConstructorList_DRQ; } public IDecRefQueue getASTDRQ() { return m_AST_DRQ; } public IDecRefQueue getASTMapDRQ() { return m_ASTMap_DRQ; } public IDecRefQueue getASTVectorDRQ() { return m_ASTVector_DRQ; } public IDecRefQueue getApplyResultDRQ() { return m_ApplyResult_DRQ; } public IDecRefQueue getFuncEntryDRQ() { return m_FuncEntry_DRQ; } public IDecRefQueue getFuncInterpDRQ() { return m_FuncInterp_DRQ; } public IDecRefQueue getGoalDRQ() { return m_Goal_DRQ; } public IDecRefQueue getModelDRQ() { return m_Model_DRQ; } public IDecRefQueue getParamsDRQ() { return m_Params_DRQ; } public IDecRefQueue getParamDescrsDRQ() { return m_ParamDescrs_DRQ; } public IDecRefQueue getProbeDRQ() { return m_Probe_DRQ; } public IDecRefQueue getSolverDRQ() { return m_Solver_DRQ; } public IDecRefQueue getStatisticsDRQ() { return m_Statistics_DRQ; } public IDecRefQueue getTacticDRQ() { return m_Tactic_DRQ; } public IDecRefQueue getFixedpointDRQ() { return m_Fixedpoint_DRQ; } public IDecRefQueue getOptimizeDRQ() { return m_Optimize_DRQ; } /** * Disposes of the context. **/ @Override public void close() { m_AST_DRQ.forceClear(this); m_ASTMap_DRQ.forceClear(this); m_ASTVector_DRQ.forceClear(this); m_ApplyResult_DRQ.forceClear(this); m_FuncEntry_DRQ.forceClear(this); m_FuncInterp_DRQ.forceClear(this); m_Goal_DRQ.forceClear(this); m_Model_DRQ.forceClear(this); m_Params_DRQ.forceClear(this); m_Probe_DRQ.forceClear(this); m_Solver_DRQ.forceClear(this); m_Optimize_DRQ.forceClear(this); m_Statistics_DRQ.forceClear(this); m_Tactic_DRQ.forceClear(this); m_Fixedpoint_DRQ.forceClear(this); m_boolSort = null; m_intSort = null; m_realSort = null; m_stringSort = null; synchronized (creation_lock) { Native.delContext(m_ctx); } m_ctx = 0; } } z3-z3-4.8.7/src/api/java/DatatypeExpr.java000066400000000000000000000006431356505360400201700ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: DatatypeExpr.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * Datatype expressions **/ public class DatatypeExpr extends Expr { /** * Constructor for DatatypeExpr **/ DatatypeExpr(Context ctx, long obj) { super(ctx, obj); } } z3-z3-4.8.7/src/api/java/DatatypeSort.java000066400000000000000000000051401356505360400201760ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: DatatypeSort.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * Datatype sorts. **/ public class DatatypeSort extends Sort { /** * The number of constructors of the datatype sort. * @throws Z3Exception on error * @return an int **/ public int getNumConstructors() { return Native.getDatatypeSortNumConstructors(getContext().nCtx(), getNativeObject()); } /** * The constructors. * * @throws Z3Exception * @throws Z3Exception on error **/ public FuncDecl[] getConstructors() { int n = getNumConstructors(); FuncDecl[] res = new FuncDecl[n]; for (int i = 0; i < n; i++) res[i] = new FuncDecl(getContext(), Native.getDatatypeSortConstructor( getContext().nCtx(), getNativeObject(), i)); return res; } /** * The recognizers. * * @throws Z3Exception * @throws Z3Exception on error **/ public FuncDecl[] getRecognizers() { int n = getNumConstructors(); FuncDecl[] res = new FuncDecl[n]; for (int i = 0; i < n; i++) res[i] = new FuncDecl(getContext(), Native.getDatatypeSortRecognizer( getContext().nCtx(), getNativeObject(), i)); return res; } /** * The constructor accessors. * * @throws Z3Exception * @throws Z3Exception on error **/ public FuncDecl[][] getAccessors() { int n = getNumConstructors(); FuncDecl[][] res = new FuncDecl[n][]; for (int i = 0; i < n; i++) { FuncDecl fd = new FuncDecl(getContext(), Native.getDatatypeSortConstructor(getContext().nCtx(), getNativeObject(), i)); int ds = fd.getDomainSize(); FuncDecl[] tmp = new FuncDecl[ds]; for (int j = 0; j < ds; j++) tmp[j] = new FuncDecl(getContext(), Native.getDatatypeSortConstructorAccessor(getContext() .nCtx(), getNativeObject(), i, j)); res[i] = tmp; } return res; } DatatypeSort(Context ctx, long obj) { super(ctx, obj); } DatatypeSort(Context ctx, Symbol name, Constructor[] constructors) { super(ctx, Native.mkDatatype(ctx.nCtx(), name.getNativeObject(), constructors.length, arrayToNative(constructors))); } }; z3-z3-4.8.7/src/api/java/EnumSort.java000066400000000000000000000053371356505360400173370ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: EnumSort.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * Enumeration sorts. **/ public class EnumSort extends Sort { /** * The function declarations of the constants in the enumeration. * @throws Z3Exception on error **/ public FuncDecl[] getConstDecls() { int n = Native.getDatatypeSortNumConstructors(getContext().nCtx(), getNativeObject()); FuncDecl[] t = new FuncDecl[n]; for (int i = 0; i < n; i++) t[i] = new FuncDecl(getContext(), Native.getDatatypeSortConstructor(getContext().nCtx(), getNativeObject(), i)); return t; } /** * Retrieves the inx'th constant declaration in the enumeration. * @throws Z3Exception on error **/ public FuncDecl getConstDecl(int inx) { return new FuncDecl(getContext(), Native.getDatatypeSortConstructor(getContext().nCtx(), getNativeObject(), inx)); } /** * The constants in the enumeration. * @throws Z3Exception on error * @return an Expr[] **/ public Expr[] getConsts() { FuncDecl[] cds = getConstDecls(); Expr[] t = new Expr[cds.length]; for (int i = 0; i < t.length; i++) t[i] = getContext().mkApp(cds[i]); return t; } /** * Retrieves the inx'th constant in the enumeration. * @throws Z3Exception on error * @return an Expr **/ public Expr getConst(int inx) { return getContext().mkApp(getConstDecl(inx)); } /** * The test predicates for the constants in the enumeration. * @throws Z3Exception on error **/ public FuncDecl[] getTesterDecls() { int n = Native.getDatatypeSortNumConstructors(getContext().nCtx(), getNativeObject()); FuncDecl[] t = new FuncDecl[n]; for (int i = 0; i < n; i++) t[i] = new FuncDecl(getContext(), Native.getDatatypeSortRecognizer(getContext().nCtx(), getNativeObject(), i)); return t; } /** * Retrieves the inx'th tester/recognizer declaration in the enumeration. * @throws Z3Exception on error **/ public FuncDecl getTesterDecl(int inx) { return new FuncDecl(getContext(), Native.getDatatypeSortRecognizer(getContext().nCtx(), getNativeObject(), inx)); } EnumSort(Context ctx, Symbol name, Symbol[] enumNames) { super(ctx, Native.mkEnumerationSort(ctx.nCtx(), name.getNativeObject(), enumNames.length, Symbol.arrayToNative(enumNames), new long[enumNames.length], new long[enumNames.length])); } }; z3-z3-4.8.7/src/api/java/Expr.java000066400000000000000000002041151356505360400164740ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: Expr.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; import com.microsoft.z3.enumerations.Z3_ast_kind; import com.microsoft.z3.enumerations.Z3_decl_kind; import com.microsoft.z3.enumerations.Z3_lbool; import com.microsoft.z3.enumerations.Z3_sort_kind; /* using System; */ /** * Expressions are terms. **/ public class Expr extends AST { /** * Returns a simplified version of the expression * @return Expr * @throws Z3Exception on error **/ public Expr simplify() { return simplify(null); } /** * Returns a simplified version of the expression * A set of * parameters * @param p a Params object to configure the simplifier * @see Context#SimplifyHelp * @return an Expr * @throws Z3Exception on error **/ public Expr simplify(Params p) { if (p == null) { return Expr.create(getContext(), Native.simplify(getContext().nCtx(), getNativeObject())); } else { return Expr.create( getContext(), Native.simplifyEx(getContext().nCtx(), getNativeObject(), p.getNativeObject())); } } /** * The function declaration of the function that is applied in this * expression. * @return a FuncDecl * @throws Z3Exception on error **/ public FuncDecl getFuncDecl() { return new FuncDecl(getContext(), Native.getAppDecl(getContext().nCtx(), getNativeObject())); } /** * Indicates whether the expression is the true or false expression or * something else (Z3_L_UNDEF). * @throws Z3Exception on error * @return a Z3_lbool **/ public Z3_lbool getBoolValue() { return Z3_lbool.fromInt(Native.getBoolValue(getContext().nCtx(), getNativeObject())); } /** * The number of arguments of the expression. * @throws Z3Exception on error * @return an int **/ public int getNumArgs() { return Native.getAppNumArgs(getContext().nCtx(), getNativeObject()); } /** * The arguments of the expression. * @throws Z3Exception on error * @return an Expr[] **/ public Expr[] getArgs() { int n = getNumArgs(); Expr[] res = new Expr[n]; for (int i = 0; i < n; i++) { res[i] = Expr.create(getContext(), Native.getAppArg(getContext().nCtx(), getNativeObject(), i)); } return res; } /** * Update the arguments of the expression using the arguments {@code args} * The number of new arguments should coincide with the * current number of arguments. * @param args arguments * @throws Z3Exception on error **/ public Expr update(Expr[] args) { getContext().checkContextMatch(args); if (isApp() && args.length != getNumArgs()) { throw new Z3Exception("Number of arguments does not match"); } return Expr.create(getContext(), Native.updateTerm(getContext().nCtx(), getNativeObject(), args.length, Expr.arrayToNative(args))); } /** * Substitute every occurrence of {@code from[i]} in the expression * with {@code to[i]}, for {@code i} smaller than * {@code num_exprs}. * Remarks: The result is the new expression. The * arrays {@code from} and {@code to} must have size * {@code num_exprs}. For every {@code i} smaller than * {@code num_exprs}, we must have that sort of {@code from[i]} * must be equal to sort of {@code to[i]}. * @throws Z3Exception on error * @return an Expr **/ public Expr substitute(Expr[] from, Expr[] to) { getContext().checkContextMatch(from); getContext().checkContextMatch(to); if (from.length != to.length) { throw new Z3Exception("Argument sizes do not match"); } return Expr.create(getContext(), Native.substitute(getContext().nCtx(), getNativeObject(), from.length, Expr.arrayToNative(from), Expr.arrayToNative(to))); } /** * Substitute every occurrence of {@code from} in the expression with * {@code to}. * @see Expr#substitute(Expr[],Expr[]) * @throws Z3Exception on error * @return an Expr **/ public Expr substitute(Expr from, Expr to) { return substitute(new Expr[] { from }, new Expr[] { to }); } /** * Substitute the free variables in the expression with the expressions in * {@code to} * Remarks: For every {@code i} smaller than * {@code num_exprs}, the * variable with de-Bruijn index {@code i} * is replaced with term * {@code to[i]}. * @throws Z3Exception on error * @throws Z3Exception on error * @return an Expr **/ public Expr substituteVars(Expr[] to) { getContext().checkContextMatch(to); return Expr.create(getContext(), Native.substituteVars(getContext().nCtx(), getNativeObject(), to.length, Expr.arrayToNative(to))); } /** * Translates (copies) the term to the Context {@code ctx}. * * @param ctx A context * * @return A copy of the term which is associated with {@code ctx} * @throws Z3Exception on error **/ public Expr translate(Context ctx) { return (Expr) super.translate(ctx); } /** * Returns a string representation of the expression. **/ @Override public String toString() { return super.toString(); } /** * Indicates whether the term is a numeral * @throws Z3Exception on error * @return a boolean **/ public boolean isNumeral() { return Native.isNumeralAst(getContext().nCtx(), getNativeObject()); } /** * Indicates whether the term is well-sorted. * * @throws Z3Exception on error * @return True if the term is well-sorted, false otherwise. **/ public boolean isWellSorted() { return Native.isWellSorted(getContext().nCtx(), getNativeObject()); } /** * The Sort of the term. * @throws Z3Exception on error * @return a sort **/ public Sort getSort() { return Sort.create(getContext(), Native.getSort(getContext().nCtx(), getNativeObject())); } /** * Indicates whether the term represents a constant. * @throws Z3Exception on error * @return a boolean **/ public boolean isConst() { return isApp() && getNumArgs() == 0 && getFuncDecl().getDomainSize() == 0; } /** * Indicates whether the term is an integer numeral. * @throws Z3Exception on error * @return a boolean **/ public boolean isIntNum() { return isNumeral() && isInt(); } /** * Indicates whether the term is a real numeral. * @throws Z3Exception on error * @return a boolean **/ public boolean isRatNum() { return isNumeral() && isReal(); } /** * Indicates whether the term is an algebraic number * @throws Z3Exception on error * @return a boolean **/ public boolean isAlgebraicNumber() { return Native.isAlgebraicNumber(getContext().nCtx(), getNativeObject()); } /** * Indicates whether the term has Boolean sort. * @throws Z3Exception on error * @return a boolean **/ public boolean isBool() { return (isExpr() && Native.isEqSort(getContext().nCtx(), Native.mkBoolSort(getContext().nCtx()), Native.getSort(getContext().nCtx(), getNativeObject()))); } /** * Indicates whether the term is the constant true. * @throws Z3Exception on error * @return a boolean **/ public boolean isTrue() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_TRUE; } /** * Indicates whether the term is the constant false. * @throws Z3Exception on error * @return a boolean **/ public boolean isFalse() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_FALSE; } /** * Indicates whether the term is an equality predicate. * @throws Z3Exception on error * @return a boolean **/ public boolean isEq() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_EQ; } /** * Indicates whether the term is an n-ary distinct predicate (every argument * is mutually distinct). * @throws Z3Exception on error * @return a boolean **/ public boolean isDistinct() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_DISTINCT; } /** * Indicates whether the term is a ternary if-then-else term * @throws Z3Exception on error * @return a boolean **/ public boolean isITE() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ITE; } /** * Indicates whether the term is an n-ary conjunction * @throws Z3Exception on error * @return a boolean **/ public boolean isAnd() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_AND; } /** * Indicates whether the term is an n-ary disjunction * @throws Z3Exception on error * @return a boolean **/ public boolean isOr() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_OR; } /** * Indicates whether the term is an if-and-only-if (Boolean equivalence, * binary) * @throws Z3Exception on error * @return a boolean **/ public boolean isIff() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_IFF; } /** * Indicates whether the term is an exclusive or * @throws Z3Exception on error * @return a boolean **/ public boolean isXor() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_XOR; } /** * Indicates whether the term is a negation * @throws Z3Exception on error * @return a boolean **/ public boolean isNot() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_NOT; } /** * Indicates whether the term is an implication * @throws Z3Exception on error * @return a boolean **/ public boolean isImplies() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_IMPLIES; } /** * Indicates whether the term is of integer sort. * @throws Z3Exception on error * @return a boolean **/ public boolean isInt() { return Native.getSortKind(getContext().nCtx(), Native.getSort(getContext().nCtx(), getNativeObject())) == Z3_sort_kind.Z3_INT_SORT.toInt(); } /** * Indicates whether the term is of sort real. * @throws Z3Exception on error * @return a boolean **/ public boolean isReal() { return Native.getSortKind(getContext().nCtx(), Native.getSort(getContext().nCtx(), getNativeObject())) == Z3_sort_kind.Z3_REAL_SORT.toInt(); } /** * Indicates whether the term is an arithmetic numeral. * @throws Z3Exception on error * @return a boolean **/ public boolean isArithmeticNumeral() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ANUM; } /** * Indicates whether the term is a less-than-or-equal * @throws Z3Exception on error * @return a boolean **/ public boolean isLE() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_LE; } /** * Indicates whether the term is a greater-than-or-equal * @throws Z3Exception on error * @return a boolean **/ public boolean isGE() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_GE; } /** * Indicates whether the term is a less-than * @throws Z3Exception on error * @return a boolean **/ public boolean isLT() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_LT; } /** * Indicates whether the term is a greater-than * @throws Z3Exception on error * @return a boolean **/ public boolean isGT() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_GT; } /** * Indicates whether the term is addition (binary) * @throws Z3Exception on error * @return a boolean **/ public boolean isAdd() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ADD; } /** * Indicates whether the term is subtraction (binary) * @throws Z3Exception on error * @return a boolean **/ public boolean isSub() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SUB; } /** * Indicates whether the term is a unary minus * @throws Z3Exception on error * @return a boolean **/ public boolean isUMinus() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_UMINUS; } /** * Indicates whether the term is multiplication (binary) * @throws Z3Exception on error * @return a boolean **/ public boolean isMul() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_MUL; } /** * Indicates whether the term is division (binary) * @throws Z3Exception on error * @return a boolean **/ public boolean isDiv() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_DIV; } /** * Indicates whether the term is integer division (binary) * @throws Z3Exception on error * @return a boolean **/ public boolean isIDiv() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_IDIV; } /** * Indicates whether the term is remainder (binary) * @throws Z3Exception on error * @return a boolean **/ public boolean isRemainder() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_REM; } /** * Indicates whether the term is modulus (binary) * @throws Z3Exception on error * @return a boolean **/ public boolean isModulus() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_MOD; } /** * Indicates whether the term is a coercion of integer to real (unary) * @throws Z3Exception on error * @return a boolean **/ public boolean isIntToReal() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_TO_REAL; } /** * Indicates whether the term is a coercion of real to integer (unary) * @throws Z3Exception on error * @return a boolean **/ public boolean isRealToInt() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_TO_INT; } /** * Indicates whether the term is a check that tests whether a real is * integral (unary) * @throws Z3Exception on error * @return a boolean **/ public boolean isRealIsInt() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_IS_INT; } /** * Indicates whether the term is of an array sort. * @throws Z3Exception on error * @return a boolean **/ public boolean isArray() { return (Native.isApp(getContext().nCtx(), getNativeObject()) && Z3_sort_kind .fromInt(Native.getSortKind(getContext().nCtx(), Native.getSort(getContext().nCtx(), getNativeObject()))) == Z3_sort_kind.Z3_ARRAY_SORT); } /** * Indicates whether the term is an array store. * Remarks: It satisfies * select(store(a,i,v),j) = if i = j then v else select(a,j). Array store * takes at least 3 arguments. * @throws Z3Exception on error * @return a boolean **/ public boolean isStore() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_STORE; } /** * Indicates whether the term is an array select. * @throws Z3Exception on error * @return a boolean **/ public boolean isSelect() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SELECT; } /** * Indicates whether the term is a constant array. * Remarks: For example, * select(const(v),i) = v holds for every v and i. The function is * unary. * @throws Z3Exception on error * @return a boolean **/ public boolean isConstantArray() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_CONST_ARRAY; } /** * Indicates whether the term is a default array. * Remarks: For example default(const(v)) = v. The function is unary. * @throws Z3Exception on error * @return a boolean **/ public boolean isDefaultArray() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ARRAY_DEFAULT; } /** * Indicates whether the term is an array map. * Remarks: It satisfies * map[f](a1,..,a_n)[i] = f(a1[i],...,a_n[i]) for every i. * @throws Z3Exception on error * @return a boolean **/ public boolean isArrayMap() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ARRAY_MAP; } /** * Indicates whether the term is an as-array term. * Remarks: An as-array term * is n array value that behaves as the function graph of the function * passed as parameter. * @throws Z3Exception on error * @return a boolean **/ public boolean isAsArray() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_AS_ARRAY; } /** * Indicates whether the term is set union * @throws Z3Exception on error * @return a boolean **/ public boolean isSetUnion() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SET_UNION; } /** * Indicates whether the term is set intersection * @throws Z3Exception on error * @return a boolean **/ public boolean isSetIntersect() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SET_INTERSECT; } /** * Indicates whether the term is set difference * @throws Z3Exception on error * @return a boolean **/ public boolean isSetDifference() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SET_DIFFERENCE; } /** * Indicates whether the term is set complement * @throws Z3Exception on error * @return a boolean **/ public boolean isSetComplement() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SET_COMPLEMENT; } /** * Indicates whether the term is set subset * @throws Z3Exception on error * @return a boolean **/ public boolean isSetSubset() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SET_SUBSET; } /** * Indicates whether the terms is of bit-vector sort. * @throws Z3Exception on error * @return a boolean **/ public boolean isBV() { return Native.getSortKind(getContext().nCtx(), Native.getSort(getContext().nCtx(), getNativeObject())) == Z3_sort_kind.Z3_BV_SORT .toInt(); } /** * Indicates whether the term is a bit-vector numeral * @throws Z3Exception on error * @return a boolean **/ public boolean isBVNumeral() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BNUM; } /** * Indicates whether the term is a one-bit bit-vector with value one * @throws Z3Exception on error * @return a boolean **/ public boolean isBVBitOne() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BIT1; } /** * Indicates whether the term is a one-bit bit-vector with value zero * @throws Z3Exception on error * @return a boolean **/ public boolean isBVBitZero() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BIT0; } /** * Indicates whether the term is a bit-vector unary minus * @throws Z3Exception on error * @return a boolean **/ public boolean isBVUMinus() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BNEG; } /** * Indicates whether the term is a bit-vector addition (binary) * @throws Z3Exception on error * @return a boolean **/ public boolean isBVAdd() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BADD; } /** * Indicates whether the term is a bit-vector subtraction (binary) * @throws Z3Exception on error * @return a boolean **/ public boolean isBVSub() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BSUB; } /** * Indicates whether the term is a bit-vector multiplication (binary) * @throws Z3Exception on error * @return a boolean **/ public boolean isBVMul() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BMUL; } /** * Indicates whether the term is a bit-vector signed division (binary) * @throws Z3Exception on error * @return a boolean **/ public boolean isBVSDiv() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BSDIV; } /** * Indicates whether the term is a bit-vector unsigned division (binary) * @throws Z3Exception on error * @return a boolean **/ public boolean isBVUDiv() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BUDIV; } /** * Indicates whether the term is a bit-vector signed remainder (binary) * @throws Z3Exception on error * @return a boolean **/ public boolean isBVSRem() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BSREM; } /** * Indicates whether the term is a bit-vector unsigned remainder (binary) * @throws Z3Exception on error * @return a boolean **/ public boolean isBVURem() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BUREM; } /** * Indicates whether the term is a bit-vector signed modulus * @throws Z3Exception on error * @return a boolean **/ public boolean isBVSMod() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BSMOD; } /** * Indicates whether the term is a bit-vector signed division by zero * @return a boolean * @throws Z3Exception on error **/ boolean isBVSDiv0() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BSDIV0; } /** * Indicates whether the term is a bit-vector unsigned division by zero * @return a boolean * @throws Z3Exception on error **/ boolean isBVUDiv0() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BUDIV0; } /** * Indicates whether the term is a bit-vector signed remainder by zero * @return a boolean * @throws Z3Exception on error **/ boolean isBVSRem0() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BSREM0; } /** * Indicates whether the term is a bit-vector unsigned remainder by zero * @return a boolean * @throws Z3Exception on error **/ boolean isBVURem0() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BUREM0; } /** * Indicates whether the term is a bit-vector signed modulus by zero * @return a boolean * @throws Z3Exception on error **/ boolean isBVSMod0() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BSMOD0; } /** * Indicates whether the term is an unsigned bit-vector less-than-or-equal * @throws Z3Exception on error * @return a boolean **/ public boolean isBVULE() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ULEQ; } /** * Indicates whether the term is a signed bit-vector less-than-or-equal * @throws Z3Exception on error * @return a boolean **/ public boolean isBVSLE() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SLEQ; } /** * Indicates whether the term is an unsigned bit-vector * greater-than-or-equal * @throws Z3Exception on error * @return a boolean **/ public boolean isBVUGE() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_UGEQ; } /** * Indicates whether the term is a signed bit-vector greater-than-or-equal * @throws Z3Exception on error * @return a boolean **/ public boolean isBVSGE() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SGEQ; } /** * Indicates whether the term is an unsigned bit-vector less-than * @throws Z3Exception on error * @return a boolean **/ public boolean isBVULT() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ULT; } /** * Indicates whether the term is a signed bit-vector less-than * @throws Z3Exception on error * @return a boolean **/ public boolean isBVSLT() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SLT; } /** * Indicates whether the term is an unsigned bit-vector greater-than * @throws Z3Exception on error * @return a boolean **/ public boolean isBVUGT() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_UGT; } /** * Indicates whether the term is a signed bit-vector greater-than * @throws Z3Exception on error * @return a boolean **/ public boolean isBVSGT() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SGT; } /** * Indicates whether the term is a bit-wise AND * @throws Z3Exception on error * @return a boolean **/ public boolean isBVAND() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BAND; } /** * Indicates whether the term is a bit-wise OR * @throws Z3Exception on error * @return a boolean **/ public boolean isBVOR() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BOR; } /** * Indicates whether the term is a bit-wise NOT * @throws Z3Exception on error * @return a boolean **/ public boolean isBVNOT() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BNOT; } /** * Indicates whether the term is a bit-wise XOR * @throws Z3Exception on error * @return a boolean **/ public boolean isBVXOR() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BXOR; } /** * Indicates whether the term is a bit-wise NAND * @throws Z3Exception on error * @return a boolean **/ public boolean isBVNAND() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BNAND; } /** * Indicates whether the term is a bit-wise NOR * @throws Z3Exception on error * @return a boolean **/ public boolean isBVNOR() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BNOR; } /** * Indicates whether the term is a bit-wise XNOR * @throws Z3Exception on error * @return a boolean **/ public boolean isBVXNOR() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BXNOR; } /** * Indicates whether the term is a bit-vector concatenation (binary) * @throws Z3Exception on error * @return a boolean **/ public boolean isBVConcat() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_CONCAT; } /** * Indicates whether the term is a bit-vector sign extension * @throws Z3Exception on error * @return a boolean **/ public boolean isBVSignExtension() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SIGN_EXT; } /** * Indicates whether the term is a bit-vector zero extension * @throws Z3Exception on error * @return a boolean **/ public boolean isBVZeroExtension() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ZERO_EXT; } /** * Indicates whether the term is a bit-vector extraction * @throws Z3Exception on error * @return a boolean **/ public boolean isBVExtract() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_EXTRACT; } /** * Indicates whether the term is a bit-vector repetition * @throws Z3Exception on error * @return a boolean **/ public boolean isBVRepeat() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_REPEAT; } /** * Indicates whether the term is a bit-vector reduce OR * @throws Z3Exception on error * @return a boolean **/ public boolean isBVReduceOR() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BREDOR; } /** * Indicates whether the term is a bit-vector reduce AND * @throws Z3Exception on error * @return a boolean **/ public boolean isBVReduceAND() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BREDAND; } /** * Indicates whether the term is a bit-vector comparison * @throws Z3Exception on error * @return a boolean **/ public boolean isBVComp() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BCOMP; } /** * Indicates whether the term is a bit-vector shift left * @throws Z3Exception on error * @return a boolean **/ public boolean isBVShiftLeft() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BSHL; } /** * Indicates whether the term is a bit-vector logical shift right * @throws Z3Exception on error * @return a boolean **/ public boolean isBVShiftRightLogical() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BLSHR; } /** * Indicates whether the term is a bit-vector arithmetic shift left * @throws Z3Exception on error * @return a boolean **/ public boolean isBVShiftRightArithmetic() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BASHR; } /** * Indicates whether the term is a bit-vector rotate left * @throws Z3Exception on error * @return a boolean **/ public boolean isBVRotateLeft() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ROTATE_LEFT; } /** * Indicates whether the term is a bit-vector rotate right * @throws Z3Exception on error * @return a boolean **/ public boolean isBVRotateRight() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_ROTATE_RIGHT; } /** * Indicates whether the term is a bit-vector rotate left (extended) * Remarks: Similar to Z3_OP_ROTATE_LEFT, but it is a binary operator * instead of a parametric one. * @throws Z3Exception on error * @return a boolean **/ public boolean isBVRotateLeftExtended() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_EXT_ROTATE_LEFT; } /** * Indicates whether the term is a bit-vector rotate right (extended) * Remarks: Similar to Z3_OP_ROTATE_RIGHT, but it is a binary operator * instead of a parametric one. * @throws Z3Exception on error * @return a boolean **/ public boolean isBVRotateRightExtended() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_EXT_ROTATE_RIGHT; } /** * Indicates whether the term is a coercion from integer to bit-vector * * Remarks: This function is not supported by the decision procedures. Only * the most rudimentary simplification rules are applied to this * function. * @throws Z3Exception on error * @return a boolean **/ public boolean isIntToBV() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_INT2BV; } /** * Indicates whether the term is a coercion from bit-vector to integer * * Remarks: This function is not supported by the decision procedures. Only * the most rudimentary simplification rules are applied to this * function. * @throws Z3Exception on error * @return a boolean **/ public boolean isBVToInt() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_BV2INT; } /** * Indicates whether the term is a bit-vector carry * Remarks: Compute the * carry bit in a full-adder. The meaning is given by the equivalence (carry * l1 l2 l3) <=> (or (and l1 l2) (and l1 l3) (and l2 l3))) * @throws Z3Exception on error * @return a boolean **/ public boolean isBVCarry() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_CARRY; } /** * Indicates whether the term is a bit-vector ternary XOR * Remarks: The * meaning is given by the equivalence (xor3 l1 l2 l3) <=> (xor (xor * l1 l2) l3) * @throws Z3Exception on error * @return a boolean **/ public boolean isBVXOR3() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_XOR3; } /** * Indicates whether the term is a label (used by the Boogie Verification * condition generator). * Remarks: The label has two parameters, a string and * a Boolean polarity. It takes one argument, a formula. * @throws Z3Exception on error * @return a boolean **/ public boolean isLabel() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_LABEL; } /** * Indicates whether the term is a label literal (used by the Boogie * Verification condition generator). * Remarks: A label literal has a set of * string parameters. It takes no arguments. * @throws Z3Exception on error * @return a boolean **/ public boolean isLabelLit() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_LABEL_LIT; } /** * Check whether expression is a string constant. * @return a boolean */ public boolean isString() { return isApp() && Native.isString(getContext().nCtx(), getNativeObject()); } /** * Retrieve string corresponding to string constant. * Remark: the expression should be a string constant, (isString() should return true). * @throws Z3Exception on error * @return a string */ public String getString() { return Native.getString(getContext().nCtx(), getNativeObject()); } /** * TBD: sketch for #2522, 'Pointer' seems deprecated and instead * approach seems to be around Set/Get CharArrayRegion and updating Native.cpp * code generation to produce the char[]. * public char[] getNativeString() { Native.IntPtr len = new Native.IntPtr(); long s = Native.getLstring(getContext().nCtx(), getNativeObject(), len); char[] result = new char[len.value]; Pointer ptr = Pointer.createConstant(s); for (int i = 0; i < len.value; ++i) result[i] = ptr.getChar(i); return result; } */ /** * Check whether expression is a concatenation * @return a boolean */ public boolean isConcat() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_SEQ_CONCAT; } /** * Indicates whether the term is a binary equivalence modulo namings. * Remarks: This binary predicate is used in proof terms. It captures * equisatisfiability and equivalence modulo renamings. * @throws Z3Exception on error * @return a boolean **/ public boolean isOEQ() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_OEQ; } /** * Indicates whether the term is a Proof for the expression 'true'. * @throws Z3Exception on error * @return a boolean **/ public boolean isProofTrue() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_TRUE; } /** * Indicates whether the term is a proof for a fact asserted by the user. * @throws Z3Exception on error * @return a boolean **/ public boolean isProofAsserted() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_ASSERTED; } /** * Indicates whether the term is a proof for a fact (tagged as goal) * asserted by the user. * @throws Z3Exception on error * @return a boolean **/ public boolean isProofGoal() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_GOAL; } /** * Indicates whether the term is proof via modus ponens * Remarks: Given a * proof for p and a proof for (implies p q), produces a proof for q. T1: p * T2: (implies p q) [mp T1 T2]: q The second antecedents may also be a * proof for (iff p q). * @throws Z3Exception on error * @return a boolean **/ public boolean isProofModusPonens() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_MODUS_PONENS; } /** * Indicates whether the term is a proof for (R t t), where R is a reflexive * relation. * Remarks: This proof object has no antecedents. The only * reflexive relations that are used are equivalence modulo namings, * equality and equivalence. That is, R is either '~', '=' or * 'iff'. * @throws Z3Exception on error * @return a boolean **/ public boolean isProofReflexivity() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_REFLEXIVITY; } /** * Indicates whether the term is proof by symmetricity of a relation * * Remarks: Given an symmetric relation R and a proof for (R t s), produces * a proof for (R s t). T1: (R t s) [symmetry T1]: (R s t) T1 is the * antecedent of this proof object. * @throws Z3Exception on error * @return a boolean **/ public boolean isProofSymmetry() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_SYMMETRY; } /** * Indicates whether the term is a proof by transitivity of a relation * * Remarks: Given a transitive relation R, and proofs for (R t s) and (R s * u), produces a proof for (R t u). T1: (R t s) T2: (R s u) [trans T1 T2]: * (R t u) * @throws Z3Exception on error * @return a boolean **/ public boolean isProofTransitivity() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_TRANSITIVITY; } /** * Indicates whether the term is a proof by condensed transitivity of a * relation * Remarks: Condensed transitivity proof. It combines several symmetry * and transitivity proofs. Example: T1: (R a b) T2: (R c b) T3: (R c d) * [trans* T1 T2 T3]: (R a d) R must be a symmetric and transitive relation. * * Assuming that this proof object is a proof for (R s t), then a proof * checker must check if it is possible to prove (R s t) using the * antecedents, symmetry and transitivity. That is, if there is a path from * s to t, if we view every antecedent (R a b) as an edge between a and b. * * @throws Z3Exception on error * @return a boolean **/ public boolean isProofTransitivityStar() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_TRANSITIVITY_STAR; } /** * Indicates whether the term is a monotonicity proof object. * Remarks: T1: * (R t_1 s_1) ... Tn: (R t_n s_n) [monotonicity T1 ... Tn]: (R (f t_1 ... * t_n) (f s_1 ... s_n)) Remark: if t_i == s_i, then the antecedent Ti is * suppressed. That is, reflexivity proofs are suppressed to save space. * * @throws Z3Exception on error * @return a boolean **/ public boolean isProofMonotonicity() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_MONOTONICITY; } /** * Indicates whether the term is a quant-intro proof * Remarks: Given a proof * for (~ p q), produces a proof for (~ (forall (x) p) (forall (x) q)). T1: * (~ p q) [quant-intro T1]: (~ (forall (x) p) (forall (x) q)) * @throws Z3Exception on error * @return a boolean **/ public boolean isProofQuantIntro() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_QUANT_INTRO; } /** * Indicates whether the term is a distributivity proof object. * Remarks: * Given that f (= or) distributes over g (= and), produces a proof for (= * (f a (g c d)) (g (f a c) (f a d))) If f and g are associative, this proof * also justifies the following equality: (= (f (g a b) (g c d)) (g (f a c) * (f a d) (f b c) (f b d))) where each f and g can have arbitrary number of * arguments. * * This proof object has no antecedents. Remark. This rule is used by the * CNF conversion pass and instantiated by f = or, and g = and. * @throws Z3Exception on error * @return a boolean **/ public boolean isProofDistributivity() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_DISTRIBUTIVITY; } /** * Indicates whether the term is a proof by elimination of AND * Remarks: * Given a proof for (and l_1 ... l_n), produces a proof for l_i T1: (and * l_1 ... l_n) [and-elim T1]: l_i * @throws Z3Exception on error * @return a boolean **/ public boolean isProofAndElimination() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_AND_ELIM; } /** * Indicates whether the term is a proof by elimination of not-or * Remarks: * Given a proof for (not (or l_1 ... l_n)), produces a proof for (not l_i). * T1: (not (or l_1 ... l_n)) [not-or-elim T1]: (not l_i) * @throws Z3Exception on error * @return a boolean **/ public boolean isProofOrElimination() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_NOT_OR_ELIM; } /** * Indicates whether the term is a proof by rewriting * Remarks: A proof for * a local rewriting step (= t s). The head function symbol of t is * interpreted. * * This proof object has no antecedents. The conclusion of a rewrite rule is * either an equality (= t s), an equivalence (iff t s), or * equi-satisfiability (~ t s). Remark: if f is bool, then = is iff. * * Examples: (= (+ x 0) x) (= (+ x 1 2) (+ 3 x)) (iff (or x false) x) * * @throws Z3Exception on error * @return a boolean **/ public boolean isProofRewrite() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_REWRITE; } /** * Indicates whether the term is a proof by rewriting * Remarks: A proof for * rewriting an expression t into an expression s. This proof object can have n * antecedents. The antecedents are proofs for equalities used as * substitution rules. The object is used in a few cases . The cases are: - When applying contextual * simplification (CONTEXT_SIMPLIFIER=true) - When converting bit-vectors to * Booleans (BIT2BOOL=true) * @throws Z3Exception on error * @return a boolean **/ public boolean isProofRewriteStar() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_REWRITE_STAR; } /** * Indicates whether the term is a proof for pulling quantifiers out. * Remarks: A proof for (iff (f (forall (x) q(x)) r) (forall (x) (f (q x) * r))). This proof object has no antecedents. * @throws Z3Exception on error * @return a boolean **/ public boolean isProofPullQuant() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_PULL_QUANT; } /** * Indicates whether the term is a proof for pushing quantifiers in. * Remarks: A proof for: (iff (forall (x_1 ... x_m) (and p_1[x_1 ... x_m] * ... p_n[x_1 ... x_m])) (and (forall (x_1 ... x_m) p_1[x_1 ... x_m]) ... * (forall (x_1 ... x_m) p_n[x_1 ... x_m]))) This proof object has no * antecedents * @throws Z3Exception on error * @return a boolean **/ public boolean isProofPushQuant() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_PUSH_QUANT; } /** * Indicates whether the term is a proof for elimination of unused * variables. * Remarks: A proof for (iff (forall (x_1 ... x_n y_1 ... y_m) * p[x_1 ... x_n]) (forall (x_1 ... x_n) p[x_1 ... x_n])) * * It is used to justify the elimination of unused variables. This proof * object has no antecedents. * @throws Z3Exception on error * @return a boolean **/ public boolean isProofElimUnusedVars() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_ELIM_UNUSED_VARS; } /** * Indicates whether the term is a proof for destructive equality resolution * Remarks: A proof for destructive equality resolution: (iff (forall (x) * (or (not (= x t)) P[x])) P[t]) if x does not occur in t. * * This proof object has no antecedents. * * Several variables can be eliminated simultaneously. * @throws Z3Exception on error * @return a boolean **/ public boolean isProofDER() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_DER; } /** * Indicates whether the term is a proof for quantifier instantiation * * Remarks: A proof of (or (not (forall (x) (P x))) (P a)) * @throws Z3Exception on error * @return a boolean **/ public boolean isProofQuantInst() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_QUANT_INST; } /** * Indicates whether the term is a hypothesis marker. * Remarks: Mark a * hypothesis in a natural deduction style proof. * @throws Z3Exception on error * @return a boolean **/ public boolean isProofHypothesis() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_HYPOTHESIS; } /** * Indicates whether the term is a proof by lemma * Remarks: T1: false [lemma * T1]: (or (not l_1) ... (not l_n)) * * This proof object has one antecedent: a hypothetical proof for false. It * converts the proof in a proof for (or (not l_1) ... (not l_n)), when T1 * contains the hypotheses: l_1, ..., l_n. * @throws Z3Exception on error * @return a boolean **/ public boolean isProofLemma() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_LEMMA; } /** * Indicates whether the term is a proof by unit resolution * Remarks: T1: * (or l_1 ... l_n l_1' ... l_m') T2: (not l_1) ... T(n+1): (not l_n) * [unit-resolution T1 ... T(n+1)]: (or l_1' ... l_m') * @throws Z3Exception on error * @return a boolean **/ public boolean isProofUnitResolution() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_UNIT_RESOLUTION; } /** * Indicates whether the term is a proof by iff-true * Remarks: T1: p * [iff-true T1]: (iff p true) * @throws Z3Exception on error * @return a boolean **/ public boolean isProofIFFTrue() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_IFF_TRUE; } /** * Indicates whether the term is a proof by iff-false * Remarks: T1: (not p) * [iff-false T1]: (iff p false) * @throws Z3Exception on error * @return a boolean **/ public boolean isProofIFFFalse() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_IFF_FALSE; } /** * Indicates whether the term is a proof by commutativity * Remarks: [comm]: * (= (f a b) (f b a)) * * f is a commutative operator. * * This proof object has no antecedents. Remark: if f is bool, then = is * iff. * @throws Z3Exception on error * @return a boolean **/ public boolean isProofCommutativity() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_COMMUTATIVITY; } /** * Indicates whether the term is a proof for Tseitin-like axioms * Remarks: * Proof object used to justify Tseitin's like axioms: * * (or (not (and p q)) p) (or (not (and p q)) q) (or (not (and p q r)) p) * (or (not (and p q r)) q) (or (not (and p q r)) r) ... (or (and p q) (not * p) (not q)) (or (not (or p q)) p q) (or (or p q) (not p)) (or (or p q) * (not q)) (or (not (iff p q)) (not p) q) (or (not (iff p q)) p (not q)) * (or (iff p q) (not p) (not q)) (or (iff p q) p q) (or (not (ite a b c)) * (not a) b) (or (not (ite a b c)) a c) (or (ite a b c) (not a) (not b)) * (or (ite a b c) a (not c)) (or (not (not a)) (not a)) (or (not a) a) * * This proof object has no antecedents. Note: all axioms are propositional * tautologies. Note also that 'and' and 'or' can take multiple arguments. * You can recover the propositional tautologies by unfolding the Boolean * connectives in the axioms a small bounded number of steps (=3). * * @throws Z3Exception on error * @return a boolean **/ public boolean isProofDefAxiom() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_DEF_AXIOM; } /** * Indicates whether the term is a proof for introduction of a name * Remarks: Introduces a name for a formula/term. Suppose e is an * expression with free variables x, and def-intro introduces the name n(x). * The possible cases are: * * When e is of Boolean type: [def-intro]: (and (or n (not e)) (or (not n) * e)) * * or: [def-intro]: (or (not n) e) when e only occurs positively. * * When e is of the form (ite cond th el): [def-intro]: (and (or (not cond) * (= n th)) (or cond (= n el))) * * Otherwise: [def-intro]: (= n e) * @throws Z3Exception on error * @return a boolean **/ public boolean isProofDefIntro() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_DEF_INTRO; } /** * Indicates whether the term is a proof for application of a definition * Remarks: [apply-def T1]: F ~ n F is 'equivalent' to n, given that T1 is * a proof that n is a name for F. * @throws Z3Exception on error * @return a boolean **/ public boolean isProofApplyDef() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_APPLY_DEF; } /** * Indicates whether the term is a proof iff-oeq * Remarks: T1: (iff p q) * [iff~ T1]: (~ p q) * @throws Z3Exception on error * @return a boolean **/ public boolean isProofIFFOEQ() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_IFF_OEQ; } /** * Indicates whether the term is a proof for a positive NNF step * Remarks: * Proof for a (positive) NNF step. Example: * * T1: (not s_1) ~ r_1 T2: (not s_2) ~ r_2 T3: s_1 ~ r_1' T4: s_2 ~ r_2' * [nnf-pos T1 T2 T3 T4]: (~ (iff s_1 s_2) (and (or r_1 r_2') (or r_1' * r_2))) * * The negation normal form steps NNF_POS and NNF_NEG are used in the * following cases: (a) When creating the NNF of a positive force * quantifier. The quantifier is retained (unless the bound variables are * eliminated). Example T1: q ~ q_new [nnf-pos T1]: (~ (forall (x T) q) * (forall (x T) q_new)) * * (b) When recursively creating NNF over Boolean formulas, where the * top-level connective is changed during NNF conversion. The relevant * Boolean connectives for NNF_POS are 'implies', 'iff', 'xor', 'ite'. * NNF_NEG furthermore handles the case where negation is pushed over * Boolean connectives 'and' and 'or'. * @throws Z3Exception on error * @return a boolean **/ public boolean isProofNNFPos() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_NNF_POS; } /** * Indicates whether the term is a proof for a negative NNF step * Remarks: * Proof for a (negative) NNF step. Examples: * * T1: (not s_1) ~ r_1 ... Tn: (not s_n) ~ r_n [nnf-neg T1 ... Tn]: (not * (and s_1 ... s_n)) ~ (or r_1 ... r_n) and T1: (not s_1) ~ r_1 ... Tn: * (not s_n) ~ r_n [nnf-neg T1 ... Tn]: (not (or s_1 ... s_n)) ~ (and r_1 * ... r_n) and T1: (not s_1) ~ r_1 T2: (not s_2) ~ r_2 T3: s_1 ~ r_1' T4: * s_2 ~ r_2' [nnf-neg T1 T2 T3 T4]: (~ (not (iff s_1 s_2)) (and (or r_1 * r_2) (or r_1' r_2'))) * @throws Z3Exception on error * @return a boolean **/ public boolean isProofNNFNeg() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_NNF_NEG; } /** * Indicates whether the term is a proof for a Skolemization step * Remarks: * Proof for: * * [sk]: (~ (not (forall x (p x y))) (not (p (sk y) y))) [sk]: (~ (exists x * (p x y)) (p (sk y) y)) * * This proof object has no antecedents. * @throws Z3Exception on error * @return a boolean **/ public boolean isProofSkolemize() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_SKOLEMIZE; } /** * Indicates whether the term is a proof by modus ponens for * equi-satisfiability. * Remarks: Modus ponens style rule for * equi-satisfiability. T1: p T2: (~ p q) [mp~ T1 T2]: q * @throws Z3Exception on error * @return a boolean **/ public boolean isProofModusPonensOEQ() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_MODUS_PONENS_OEQ; } /** * Indicates whether the term is a proof for theory lemma * Remarks: Generic * proof for theory lemmas. * * The theory lemma function comes with one or more parameters. The first * parameter indicates the name of the theory. For the theory of arithmetic, * additional parameters provide hints for checking the theory lemma. The * hints for arithmetic are: - farkas - followed by rational coefficients. * Multiply the coefficients to the inequalities in the lemma, add the * (negated) inequalities and obtain a contradiction. - triangle-eq - * Indicates a lemma related to the equivalence: (iff (= t1 t2) (and (<= * t1 t2) (<= t2 t1))) - gcd-test - Indicates an integer linear * arithmetic lemma that uses a gcd test. * @throws Z3Exception on error * @return a boolean **/ public boolean isProofTheoryLemma() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_TH_LEMMA; } /** * Indicates whether the term is of an array sort. * @throws Z3Exception on error * @return a boolean **/ public boolean isRelation() { return (Native.isApp(getContext().nCtx(), getNativeObject()) && Native .getSortKind(getContext().nCtx(), Native.getSort(getContext().nCtx(), getNativeObject())) == Z3_sort_kind.Z3_RELATION_SORT .toInt()); } /** * Indicates whether the term is an relation store * Remarks: Insert a record * into a relation. The function takes {@code n+1} arguments, where the * first argument is the relation and the remaining {@code n} elements * correspond to the {@code n} columns of the relation. * @throws Z3Exception on error * @return a boolean **/ public boolean isRelationStore() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_STORE; } /** * Indicates whether the term is an empty relation * @throws Z3Exception on error * @return a boolean **/ public boolean isEmptyRelation() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_EMPTY; } /** * Indicates whether the term is a test for the emptiness of a relation * @throws Z3Exception on error * @return a boolean **/ public boolean isIsEmptyRelation() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_IS_EMPTY; } /** * Indicates whether the term is a relational join * @throws Z3Exception on error * @return a boolean **/ public boolean isRelationalJoin() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_JOIN; } /** * Indicates whether the term is the union or convex hull of two relations. * * Remarks: The function takes two arguments. * @throws Z3Exception on error * @return a boolean **/ public boolean isRelationUnion() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_UNION; } /** * Indicates whether the term is the widening of two relations * Remarks: The * function takes two arguments. * @throws Z3Exception on error * @return a boolean **/ public boolean isRelationWiden() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_WIDEN; } /** * Indicates whether the term is a projection of columns (provided as * numbers in the parameters). * Remarks: The function takes one * argument. * @throws Z3Exception on error * @return a boolean **/ public boolean isRelationProject() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_PROJECT; } /** * Indicates whether the term is a relation filter * Remarks: Filter * (restrict) a relation with respect to a predicate. The first argument is * a relation. The second argument is a predicate with free de-Bruijn * indices corresponding to the columns of the relation. So the first column * in the relation has index 0. * @throws Z3Exception on error * @return a boolean **/ public boolean isRelationFilter() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_FILTER; } /** * Indicates whether the term is an intersection of a relation with the * negation of another. * Remarks: Intersect the first relation with respect * to negation of the second relation (the function takes two arguments). * Logically, the specification can be described by a function * * target = filter_by_negation(pos, neg, columns) * * where columns are pairs c1, d1, .., cN, dN of columns from pos and neg, * such that target are elements in x in pos, such that there is no y in neg * that agrees with x on the columns c1, d1, .., cN, dN. * @throws Z3Exception on error * @return a boolean **/ public boolean isRelationNegationFilter() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_NEGATION_FILTER; } /** * Indicates whether the term is the renaming of a column in a relation * Remarks: The function takes one argument. The parameters contain the * renaming as a cycle. * @throws Z3Exception on error * @return a boolean **/ public boolean isRelationRename() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_RENAME; } /** * Indicates whether the term is the complement of a relation * @throws Z3Exception on error * @return a boolean **/ public boolean isRelationComplement() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_COMPLEMENT; } /** * Indicates whether the term is a relational select * Remarks: Check if a * record is an element of the relation. The function takes {@code n+1} * arguments, where the first argument is a relation, and the remaining * {@code n} arguments correspond to a record. * @throws Z3Exception on error * @return a boolean **/ public boolean isRelationSelect() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_SELECT; } /** * Indicates whether the term is a relational clone (copy) * Remarks: Create * a fresh copy (clone) of a relation. The function is logically the * identity, but in the context of a register machine allows for terms of * kind {@code isRelationUnion} to perform destructive updates to * the first argument. * @see #isRelationUnion * @throws Z3Exception on error * @return a boolean **/ public boolean isRelationClone() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_RA_CLONE; } /** * Indicates whether the term is of an array sort. * @throws Z3Exception on error * @return a boolean **/ public boolean isFiniteDomain() { return (Native.isApp(getContext().nCtx(), getNativeObject()) && Native .getSortKind(getContext().nCtx(), Native.getSort(getContext().nCtx(), getNativeObject())) == Z3_sort_kind.Z3_FINITE_DOMAIN_SORT .toInt()); } /** * Indicates whether the term is a less than predicate over a finite domain. * @throws Z3Exception on error * @return a boolean **/ public boolean isFiniteDomainLT() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_FD_LT; } /** * The de-Bruijn index of a bound variable. * Remarks: Bound variables are * indexed by de-Bruijn indices. It is perhaps easiest to explain the * meaning of de-Bruijn indices by indicating the compilation process from * non-de-Bruijn formulas to de-Bruijn format. {@code * abs(forall (x1) phi) = forall (x1) abs1(phi, x1, 0) * abs(forall (x1, x2) phi) = abs(forall (x1) abs(forall (x2) phi)) * abs1(x, x, n) = b_n * abs1(y, x, n) = y * abs1(f(t1,...,tn), x, n) = f(abs1(t1,x,n), ..., abs1(tn,x,n)) * abs1(forall (x1) phi, x, n) = forall (x1) (abs1(phi, x, n+1)) * } The last line is significant: the index of a bound variable is * different depending on the scope in which it appears. The deeper x * appears, the higher is its index. * @throws Z3Exception on error * @return an int **/ public int getIndex() { if (!isVar()) { throw new Z3Exception("Term is not a bound variable."); } return Native.getIndexValue(getContext().nCtx(), getNativeObject()); } /** * Constructor for Expr * @throws Z3Exception on error **/ protected Expr(Context ctx, long obj) { super(ctx, obj); } @Override void checkNativeObject(long obj) { if (!Native.isApp(getContext().nCtx(), obj) && Native.getAstKind(getContext().nCtx(), obj) != Z3_ast_kind.Z3_VAR_AST.toInt() && Native.getAstKind(getContext().nCtx(), obj) != Z3_ast_kind.Z3_QUANTIFIER_AST.toInt()) { throw new Z3Exception("Underlying object is not a term"); } super.checkNativeObject(obj); } static Expr create(Context ctx, FuncDecl f, Expr ... arguments) { long obj = Native.mkApp(ctx.nCtx(), f.getNativeObject(), AST.arrayLength(arguments), AST.arrayToNative(arguments)); return create(ctx, obj); } static Expr create(Context ctx, long obj) { Z3_ast_kind k = Z3_ast_kind.fromInt(Native.getAstKind(ctx.nCtx(), obj)); if (k == Z3_ast_kind.Z3_QUANTIFIER_AST) return new Quantifier(ctx, obj); long s = Native.getSort(ctx.nCtx(), obj); Z3_sort_kind sk = Z3_sort_kind .fromInt(Native.getSortKind(ctx.nCtx(), s)); if (Native.isAlgebraicNumber(ctx.nCtx(), obj)) // is this a numeral ast? return new AlgebraicNum(ctx, obj); if (Native.isNumeralAst(ctx.nCtx(), obj)) { switch (sk) { case Z3_INT_SORT: return new IntNum(ctx, obj); case Z3_REAL_SORT: return new RatNum(ctx, obj); case Z3_BV_SORT: return new BitVecNum(ctx, obj); case Z3_FLOATING_POINT_SORT: return new FPNum(ctx, obj); case Z3_ROUNDING_MODE_SORT: return new FPRMNum(ctx, obj); case Z3_FINITE_DOMAIN_SORT: return new FiniteDomainNum(ctx, obj); default: ; } } switch (sk) { case Z3_BOOL_SORT: return new BoolExpr(ctx, obj); case Z3_INT_SORT: return new IntExpr(ctx, obj); case Z3_REAL_SORT: return new RealExpr(ctx, obj); case Z3_BV_SORT: return new BitVecExpr(ctx, obj); case Z3_ARRAY_SORT: return new ArrayExpr(ctx, obj); case Z3_DATATYPE_SORT: return new DatatypeExpr(ctx, obj); case Z3_FLOATING_POINT_SORT: return new FPExpr(ctx, obj); case Z3_ROUNDING_MODE_SORT: return new FPRMExpr(ctx, obj); case Z3_FINITE_DOMAIN_SORT: return new FiniteDomainExpr(ctx, obj); case Z3_SEQ_SORT: return new SeqExpr(ctx, obj); case Z3_RE_SORT: return new ReExpr(ctx, obj); default: ; } return new Expr(ctx, obj); } } z3-z3-4.8.7/src/api/java/FPExpr.java000066400000000000000000000012201356505360400167120ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: FPExpr.java Abstract: Author: Christoph Wintersteiger (cwinter) 2013-06-10 Notes: --*/ package com.microsoft.z3; /** * FloatingPoint Expressions */ public class FPExpr extends Expr { /** * The number of exponent bits. * @throws Z3Exception */ public int getEBits() { return ((FPSort)getSort()).getEBits(); } /** * The number of significand bits. * @throws Z3Exception */ public int getSBits() { return ((FPSort)getSort()).getSBits(); } public FPExpr(Context ctx, long obj) { super(ctx, obj); } } z3-z3-4.8.7/src/api/java/FPNum.java000066400000000000000000000130011356505360400165330ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: FPNum.java Abstract: Author: Christoph Wintersteiger (cwinter) 2013-06-10 Notes: --*/ package com.microsoft.z3; /** * FloatingPoint Numerals */ public class FPNum extends FPExpr { /** * Retrieves the sign of a floating-point literal * Remarks: returns true if the numeral is negative * @throws Z3Exception */ public boolean getSign() { Native.IntPtr res = new Native.IntPtr(); if (!Native.fpaGetNumeralSign(getContext().nCtx(), getNativeObject(), res)) throw new Z3Exception("Sign is not a Boolean value"); return res.value != 0; } /** * The sign of a floating-point numeral as a bit-vector expression * Remarks: NaN's do not have a bit-vector sign, so they are invalid arguments. * @throws Z3Exception */ public BitVecExpr getSignBV() { return new BitVecExpr(getContext(), Native.fpaGetNumeralSignBv(getContext().nCtx(), getNativeObject())); } /** * The significand value of a floating-point numeral as a string * Remarks: The significand s is always 0 < s < 2.0; the resulting string is long * enough to represent the real significand precisely. * @throws Z3Exception **/ public String getSignificand() { return Native.fpaGetNumeralSignificandString(getContext().nCtx(), getNativeObject()); } /** * The significand value of a floating-point numeral as a UInt64 * Remarks: This function extracts the significand bits, without the * hidden bit or normalization. Throws an exception if the * significand does not fit into a UInt64. * @throws Z3Exception **/ public long getSignificandUInt64() { Native.LongPtr res = new Native.LongPtr(); if (!Native.fpaGetNumeralSignificandUint64(getContext().nCtx(), getNativeObject(), res)) throw new Z3Exception("Significand is not a 64 bit unsigned integer"); return res.value; } /** * The significand of a floating-point numeral as a bit-vector expression * Remarks: NaN is an invalid argument. * @throws Z3Exception */ public BitVecExpr getSignificandBV() { return new BitVecExpr(getContext(), Native.fpaGetNumeralSignificandBv(getContext().nCtx(), getNativeObject())); } /** * Return the exponent value of a floating-point numeral as a string * Remarks: NaN is an invalid argument. * @throws Z3Exception */ public String getExponent(boolean biased) { return Native.fpaGetNumeralExponentString(getContext().nCtx(), getNativeObject(), biased); } /** * Return the exponent value of a floating-point numeral as a signed 64-bit integer * Remarks: NaN is an invalid argument. * @throws Z3Exception */ public long getExponentInt64(boolean biased) { Native.LongPtr res = new Native.LongPtr(); if (!Native.fpaGetNumeralExponentInt64(getContext().nCtx(), getNativeObject(), res, biased)) throw new Z3Exception("Exponent is not a 64 bit integer"); return res.value; } /** * The exponent of a floating-point numeral as a bit-vector expression * Remarks: NaN is an invalid argument. * @throws Z3Exception */ public BitVecExpr getExponentBV(boolean biased) { return new BitVecExpr(getContext(), Native.fpaGetNumeralExponentBv(getContext().nCtx(), getNativeObject(), biased)); } /** * Indicates whether the numeral is a NaN. * @throws Z3Exception on error * @return a boolean **/ public boolean isNaN() { return Native.fpaIsNumeralNan(getContext().nCtx(), getNativeObject()); } /** * Indicates whether the numeral is a +oo or -oo. * @throws Z3Exception on error * @return a boolean **/ public boolean isInf() { return Native.fpaIsNumeralInf(getContext().nCtx(), getNativeObject()); } /** * Indicates whether the numeral is +zero or -zero. * @throws Z3Exception on error * @return a boolean **/ public boolean isZero() { return Native.fpaIsNumeralZero(getContext().nCtx(), getNativeObject()); } /** * Indicates whether the numeral is normal. * @throws Z3Exception on error * @return a boolean **/ public boolean isNormal() { return Native.fpaIsNumeralNormal(getContext().nCtx(), getNativeObject()); } /** * Indicates whether the numeral is subnormal. * @throws Z3Exception on error * @return a boolean **/ public boolean isSubnormal() { return Native.fpaIsNumeralSubnormal(getContext().nCtx(), getNativeObject()); } /** * Indicates whether the numeral is positive. * @throws Z3Exception on error * @return a boolean **/ public boolean isPositive() { return Native.fpaIsNumeralPositive(getContext().nCtx(), getNativeObject()); } /** * Indicates whether the numeral is negative. * @throws Z3Exception on error * @return a boolean **/ public boolean isNegative() { return Native.fpaIsNumeralNegative(getContext().nCtx(), getNativeObject()); } public FPNum(Context ctx, long obj) { super(ctx, obj); } /** * Returns a string representation of the numeral. */ public String toString() { return Native.getNumeralString(getContext().nCtx(), getNativeObject()); } } z3-z3-4.8.7/src/api/java/FPRMExpr.java000066400000000000000000000005631356505360400171620ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: FPRMExpr.java Abstract: Author: Christoph Wintersteiger (cwinter) 2013-06-10 Notes: --*/ package com.microsoft.z3; /** * FloatingPoint RoundingMode Expressions */ public class FPRMExpr extends Expr { public FPRMExpr(Context ctx, long obj) { super(ctx, obj); } } z3-z3-4.8.7/src/api/java/FPRMNum.java000066400000000000000000000060671356505360400170100ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: FPRMNum.java Abstract: Author: Christoph Wintersteiger (cwinter) 2013-06-10 Notes: --*/ package com.microsoft.z3; import com.microsoft.z3.enumerations.Z3_decl_kind; /** * FloatingPoint RoundingMode Numerals */ public class FPRMNum extends FPRMExpr { /** * Indicates whether the term is the floating-point rounding numeral roundNearestTiesToEven * @throws Z3Exception * **/ public boolean isRoundNearestTiesToEven() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_FPA_RM_NEAREST_TIES_TO_EVEN; } /** * Indicates whether the term is the floating-point rounding numeral roundNearestTiesToEven * @throws Z3Exception */ public boolean isRNE() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_FPA_RM_NEAREST_TIES_TO_EVEN; } /** * Indicates whether the term is the floating-point rounding numeral roundNearestTiesToAway * @throws Z3Exception */ public boolean isRoundNearestTiesToAway() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_FPA_RM_NEAREST_TIES_TO_AWAY; } /** * Indicates whether the term is the floating-point rounding numeral roundNearestTiesToAway * @throws Z3Exception */ public boolean isRNA() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_FPA_RM_NEAREST_TIES_TO_AWAY; } /** * Indicates whether the term is the floating-point rounding numeral roundTowardPositive * @throws Z3Exception */ public boolean isRoundTowardPositive() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_FPA_RM_TOWARD_POSITIVE; } /** * Indicates whether the term is the floating-point rounding numeral roundTowardPositive * @throws Z3Exception */ public boolean isRTP() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_FPA_RM_TOWARD_POSITIVE; } /** * Indicates whether the term is the floating-point rounding numeral roundTowardNegative * @throws Z3Exception */ public boolean isRoundTowardNegative() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_FPA_RM_TOWARD_NEGATIVE; } /** * Indicates whether the term is the floating-point rounding numeral roundTowardNegative * @throws Z3Exception */ public boolean isRTN() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_FPA_RM_TOWARD_NEGATIVE; } /** * Indicates whether the term is the floating-point rounding numeral roundTowardZero * @throws Z3Exception */ public boolean isRoundTowardZero() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_FPA_RM_TOWARD_ZERO; } /** * Indicates whether the term is the floating-point rounding numeral roundTowardZero * @throws Z3Exception */ public boolean isRTZ() { return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_FPA_RM_TOWARD_ZERO; } public FPRMNum(Context ctx, long obj) { super(ctx, obj); } } z3-z3-4.8.7/src/api/java/FPRMSort.java000066400000000000000000000007371356505360400171760ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: FPRMExpr.java Abstract: Author: Christoph Wintersteiger (cwinter) 2013-06-10 Notes: --*/ package com.microsoft.z3; /** * The FloatingPoint RoundingMode sort **/ public class FPRMSort extends Sort { public FPRMSort(Context ctx) { super(ctx, Native.mkFpaRoundingModeSort(ctx.nCtx())); } public FPRMSort(Context ctx, long obj) { super(ctx, obj); } }z3-z3-4.8.7/src/api/java/FPSort.java000066400000000000000000000015171356505360400167340ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: FPSort.java Abstract: Author: Christoph Wintersteiger (cwinter) 2013-06-10 Notes: --*/ package com.microsoft.z3; /** * A FloatingPoint sort **/ public class FPSort extends Sort { public FPSort(Context ctx, long obj) { super(ctx, obj); } public FPSort(Context ctx, int ebits, int sbits) { super(ctx, Native.mkFpaSort(ctx.nCtx(), ebits, sbits)); } /** * The number of exponent bits. */ public int getEBits() { return Native.fpaGetEbits(getContext().nCtx(), getNativeObject()); } /** * The number of significand bits. */ public int getSBits() { return Native.fpaGetSbits(getContext().nCtx(), getNativeObject()); } } z3-z3-4.8.7/src/api/java/FiniteDomainExpr.java000066400000000000000000000007351356505360400207650ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: FiniteDomainExpr.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2015-12-02 Notes: **/ package com.microsoft.z3; /** * Finite-domain expressions **/ public class FiniteDomainExpr extends Expr { /** * Constructor for FiniteDomainExpr * @throws Z3Exception on error **/ FiniteDomainExpr(Context ctx, long obj) { super(ctx, obj); } } z3-z3-4.8.7/src/api/java/FiniteDomainNum.java000066400000000000000000000026171356505360400206070ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: FiniteDomainNum.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2015-12-02 Notes: **/ package com.microsoft.z3; import java.math.BigInteger; /** * Finite-domain Numerals **/ public class FiniteDomainNum extends FiniteDomainExpr { FiniteDomainNum(Context ctx, long obj) { super(ctx, obj); } /** * Retrieve the int value. **/ public int getInt() { Native.IntPtr res = new Native.IntPtr(); if (!Native.getNumeralInt(getContext().nCtx(), getNativeObject(), res)) { throw new Z3Exception("Numeral is not an int"); } return res.value; } /** * Retrieve the 64-bit int value. **/ public long getInt64() { Native.LongPtr res = new Native.LongPtr(); if (!Native.getNumeralInt64(getContext().nCtx(), getNativeObject(), res)) { throw new Z3Exception("Numeral is not an int64"); } return res.value; } /** * Retrieve the BigInteger value. **/ public BigInteger getBigInteger() { return new BigInteger(this.toString()); } /** * Returns a string representation of the numeral. **/ @Override public String toString() { return Native.getNumeralString(getContext().nCtx(), getNativeObject()); } } z3-z3-4.8.7/src/api/java/FiniteDomainSort.java000066400000000000000000000015231356505360400207720ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: FiniteDomainSort.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * Finite domain sorts. **/ public class FiniteDomainSort extends Sort { /** * The size of the finite domain sort. * @throws Z3Exception on error **/ public long getSize() { Native.LongPtr res = new Native.LongPtr(); Native.getFiniteDomainSortSize(getContext().nCtx(), getNativeObject(), res); return res.value; } FiniteDomainSort(Context ctx, long obj) { super(ctx, obj); } FiniteDomainSort(Context ctx, Symbol name, long size) { super(ctx, Native.mkFiniteDomainSort(ctx.nCtx(), name.getNativeObject(), size)); } } z3-z3-4.8.7/src/api/java/Fixedpoint.java000066400000000000000000000227701356505360400176740ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: Fixedpoint.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; import com.microsoft.z3.enumerations.Z3_lbool; /** * Object for managing fixedpoints **/ public class Fixedpoint extends Z3Object { /** * A string that describes all available fixedpoint solver parameters. **/ public String getHelp() { return Native.fixedpointGetHelp(getContext().nCtx(), getNativeObject()); } /** * Sets the fixedpoint solver parameters. * * @throws Z3Exception **/ public void setParameters(Params value) { getContext().checkContextMatch(value); Native.fixedpointSetParams(getContext().nCtx(), getNativeObject(), value.getNativeObject()); } /** * Retrieves parameter descriptions for Fixedpoint solver. * * @throws Z3Exception **/ public ParamDescrs getParameterDescriptions() { return new ParamDescrs(getContext(), Native.fixedpointGetParamDescrs( getContext().nCtx(), getNativeObject())); } /** * Assert a constraint (or multiple) into the fixedpoint solver. * * @throws Z3Exception **/ public void add(BoolExpr ... constraints) { getContext().checkContextMatch(constraints); for (BoolExpr a : constraints) { Native.fixedpointAssert(getContext().nCtx(), getNativeObject(), a.getNativeObject()); } } /** * Register predicate as recursive relation. * * @throws Z3Exception **/ public void registerRelation(FuncDecl f) { getContext().checkContextMatch(f); Native.fixedpointRegisterRelation(getContext().nCtx(), getNativeObject(), f.getNativeObject()); } /** * Add rule into the fixedpoint solver. * * @param rule implication (Horn clause) representing rule * @param name Nullable rule name. * @throws Z3Exception **/ public void addRule(BoolExpr rule, Symbol name) { getContext().checkContextMatch(rule); Native.fixedpointAddRule(getContext().nCtx(), getNativeObject(), rule.getNativeObject(), AST.getNativeObject(name)); } /** * Add table fact to the fixedpoint solver. * * @throws Z3Exception **/ public void addFact(FuncDecl pred, int ... args) { getContext().checkContextMatch(pred); Native.fixedpointAddFact(getContext().nCtx(), getNativeObject(), pred.getNativeObject(), args.length, args); } /** * Query the fixedpoint solver. A query is a conjunction of constraints. The * constraints may include the recursively defined relations. The query is * satisfiable if there is an instance of the query variables and a * derivation for it. The query is unsatisfiable if there are no derivations * satisfying the query variables. * * @throws Z3Exception **/ public Status query(BoolExpr query) { getContext().checkContextMatch(query); Z3_lbool r = Z3_lbool.fromInt(Native.fixedpointQuery(getContext().nCtx(), getNativeObject(), query.getNativeObject())); switch (r) { case Z3_L_TRUE: return Status.SATISFIABLE; case Z3_L_FALSE: return Status.UNSATISFIABLE; default: return Status.UNKNOWN; } } /** * Query the fixedpoint solver. A query is an array of relations. The query * is satisfiable if there is an instance of some relation that is * non-empty. The query is unsatisfiable if there are no derivations * satisfying any of the relations. * * @throws Z3Exception **/ public Status query(FuncDecl[] relations) { getContext().checkContextMatch(relations); Z3_lbool r = Z3_lbool.fromInt(Native.fixedpointQueryRelations(getContext() .nCtx(), getNativeObject(), AST.arrayLength(relations), AST .arrayToNative(relations))); switch (r) { case Z3_L_TRUE: return Status.SATISFIABLE; case Z3_L_FALSE: return Status.UNSATISFIABLE; default: return Status.UNKNOWN; } } /** * Update named rule into in the fixedpoint solver. * * @param rule implication (Horn clause) representing rule * @param name Nullable rule name. * @throws Z3Exception **/ public void updateRule(BoolExpr rule, Symbol name) { getContext().checkContextMatch(rule); Native.fixedpointUpdateRule(getContext().nCtx(), getNativeObject(), rule.getNativeObject(), AST.getNativeObject(name)); } /** * Retrieve satisfying instance or instances of solver, or definitions for * the recursive predicates that show unsatisfiability. * * @throws Z3Exception **/ public Expr getAnswer() { long ans = Native.fixedpointGetAnswer(getContext().nCtx(), getNativeObject()); return (ans == 0) ? null : Expr.create(getContext(), ans); } /** * Retrieve explanation why fixedpoint engine returned status Unknown. **/ public String getReasonUnknown() { return Native.fixedpointGetReasonUnknown(getContext().nCtx(), getNativeObject()); } /** * Retrieve the number of levels explored for a given predicate. **/ public int getNumLevels(FuncDecl predicate) { return Native.fixedpointGetNumLevels(getContext().nCtx(), getNativeObject(), predicate.getNativeObject()); } /** * Retrieve the cover of a predicate. * * @throws Z3Exception **/ public Expr getCoverDelta(int level, FuncDecl predicate) { long res = Native.fixedpointGetCoverDelta(getContext().nCtx(), getNativeObject(), level, predicate.getNativeObject()); return (res == 0) ? null : Expr.create(getContext(), res); } /** * Add property about the predicate. The property is added * at level. **/ public void addCover(int level, FuncDecl predicate, Expr property) { Native.fixedpointAddCover(getContext().nCtx(), getNativeObject(), level, predicate.getNativeObject(), property.getNativeObject()); } /** * Retrieve internal string representation of fixedpoint object. **/ @Override public String toString() { return Native.fixedpointToString(getContext().nCtx(), getNativeObject(), 0, null); } /** * Instrument the Datalog engine on which table representation to use for * recursive predicate. **/ public void setPredicateRepresentation(FuncDecl f, Symbol[] kinds) { Native.fixedpointSetPredicateRepresentation(getContext().nCtx(), getNativeObject(), f.getNativeObject(), AST.arrayLength(kinds), Symbol.arrayToNative(kinds)); } /** * Convert benchmark given as set of axioms, rules and queries to a string. **/ public String toString(BoolExpr[] queries) { return Native.fixedpointToString(getContext().nCtx(), getNativeObject(), AST.arrayLength(queries), AST.arrayToNative(queries)); } /** * Retrieve set of rules added to fixedpoint context. * * @throws Z3Exception **/ public BoolExpr[] getRules() { ASTVector v = new ASTVector(getContext(), Native.fixedpointGetRules(getContext().nCtx(), getNativeObject())); return v.ToBoolExprArray(); } /** * Retrieve set of assertions added to fixedpoint context. * * @throws Z3Exception **/ public BoolExpr[] getAssertions() { ASTVector v = new ASTVector(getContext(), Native.fixedpointGetAssertions(getContext().nCtx(), getNativeObject())); return v.ToBoolExprArray(); } /** * Fixedpoint statistics. * * @throws Z3Exception **/ public Statistics getStatistics() { return new Statistics(getContext(), Native.fixedpointGetStatistics( getContext().nCtx(), getNativeObject())); } /** * Parse an SMT-LIB2 file with fixedpoint rules. * Add the rules to the current fixedpoint context. * Return the set of queries in the file. **/ public BoolExpr[] ParseFile(String file) { ASTVector av = new ASTVector(getContext(), Native.fixedpointFromFile(getContext().nCtx(), getNativeObject(), file)); return av.ToBoolExprArray(); } /** * Parse an SMT-LIB2 string with fixedpoint rules. * Add the rules to the current fixedpoint context. * Return the set of queries in the file. **/ public BoolExpr[] ParseString(String s) { ASTVector av = new ASTVector(getContext(), Native.fixedpointFromString(getContext().nCtx(), getNativeObject(), s)); return av.ToBoolExprArray(); } Fixedpoint(Context ctx, long obj) throws Z3Exception { super(ctx, obj); } Fixedpoint(Context ctx) { super(ctx, Native.mkFixedpoint(ctx.nCtx())); } @Override void incRef() { Native.fixedpointIncRef(getContext().nCtx(), getNativeObject()); } @Override void addToReferenceQueue() { getContext().getFixedpointDRQ().storeReference(getContext(), this); } @Override void checkNativeObject(long obj) { } } z3-z3-4.8.7/src/api/java/FixedpointDecRefQueue.java000066400000000000000000000007401356505360400217430ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: FixedpointDecRefQueue.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; class FixedpointDecRefQueue extends IDecRefQueue { public FixedpointDecRefQueue() { super(); } @Override protected void decRef(Context ctx, long obj) { Native.fixedpointDecRef(ctx.nCtx(), obj); } }; z3-z3-4.8.7/src/api/java/FuncDecl.java000066400000000000000000000235341356505360400172450ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: FuncDecl.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; import com.microsoft.z3.enumerations.Z3_ast_kind; import com.microsoft.z3.enumerations.Z3_decl_kind; import com.microsoft.z3.enumerations.Z3_parameter_kind; /** * Function declarations. **/ public class FuncDecl extends AST { /** * Object comparison. **/ @Override public boolean equals(Object o) { if (o == this) return true; if (!(o instanceof FuncDecl)) return false; FuncDecl other = (FuncDecl) o; return (getContext().nCtx() == other.getContext().nCtx()) && (Native.isEqFuncDecl( getContext().nCtx(), getNativeObject(), other.getNativeObject())); } @Override public String toString() { return Native.funcDeclToString(getContext().nCtx(), getNativeObject()); } /** * Returns a unique identifier for the function declaration. **/ @Override public int getId() { return Native.getFuncDeclId(getContext().nCtx(), getNativeObject()); } /** * Translates (copies) the function declaration to the Context {@code ctx}. * @param ctx A context * * @return A copy of the function declaration which is associated with {@code ctx} * @throws Z3Exception on error **/ public FuncDecl translate(Context ctx) { return (FuncDecl) super.translate(ctx); } /** * The arity of the function declaration **/ public int getArity() { return Native.getArity(getContext().nCtx(), getNativeObject()); } /** * The size of the domain of the function declaration * @see #getArity **/ public int getDomainSize() { return Native.getDomainSize(getContext().nCtx(), getNativeObject()); } /** * The domain of the function declaration **/ public Sort[] getDomain() { int n = getDomainSize(); Sort[] res = new Sort[n]; for (int i = 0; i < n; i++) res[i] = Sort.create(getContext(), Native.getDomain(getContext().nCtx(), getNativeObject(), i)); return res; } /** * The range of the function declaration **/ public Sort getRange() { return Sort.create(getContext(), Native.getRange(getContext().nCtx(), getNativeObject())); } /** * The kind of the function declaration. **/ public Z3_decl_kind getDeclKind() { return Z3_decl_kind.fromInt(Native.getDeclKind(getContext().nCtx(), getNativeObject())); } /** * The name of the function declaration **/ public Symbol getName() { return Symbol.create(getContext(), Native.getDeclName(getContext().nCtx(), getNativeObject())); } /** * The number of parameters of the function declaration **/ public int getNumParameters() { return Native.getDeclNumParameters(getContext().nCtx(), getNativeObject()); } /** * The parameters of the function declaration **/ public Parameter[] getParameters() { int num = getNumParameters(); Parameter[] res = new Parameter[num]; for (int i = 0; i < num; i++) { Z3_parameter_kind k = Z3_parameter_kind.fromInt(Native .getDeclParameterKind(getContext().nCtx(), getNativeObject(), i)); switch (k) { case Z3_PARAMETER_INT: res[i] = new Parameter(k, Native.getDeclIntParameter(getContext() .nCtx(), getNativeObject(), i)); break; case Z3_PARAMETER_DOUBLE: res[i] = new Parameter(k, Native.getDeclDoubleParameter( getContext().nCtx(), getNativeObject(), i)); break; case Z3_PARAMETER_SYMBOL: res[i] = new Parameter(k, Symbol.create(getContext(), Native .getDeclSymbolParameter(getContext().nCtx(), getNativeObject(), i))); break; case Z3_PARAMETER_SORT: res[i] = new Parameter(k, Sort.create(getContext(), Native .getDeclSortParameter(getContext().nCtx(), getNativeObject(), i))); break; case Z3_PARAMETER_AST: res[i] = new Parameter(k, new AST(getContext(), Native.getDeclAstParameter(getContext().nCtx(), getNativeObject(), i))); break; case Z3_PARAMETER_FUNC_DECL: res[i] = new Parameter(k, new FuncDecl(getContext(), Native.getDeclFuncDeclParameter(getContext().nCtx(), getNativeObject(), i))); break; case Z3_PARAMETER_RATIONAL: res[i] = new Parameter(k, Native.getDeclRationalParameter( getContext().nCtx(), getNativeObject(), i)); break; default: throw new Z3Exception( "Unknown function declaration parameter kind encountered"); } } return res; } /** * Function declarations can have Parameters associated with them. **/ public class Parameter { private Z3_parameter_kind kind; private int i; private double d; private Symbol sym; private Sort srt; private AST ast; private FuncDecl fd; private String r; /** * The int value of the parameter. **/ public int getInt() { if (getParameterKind() != Z3_parameter_kind.Z3_PARAMETER_INT) throw new Z3Exception("parameter is not an int"); return i; } /** * The double value of the parameter. **/ public double getDouble() { if (getParameterKind() != Z3_parameter_kind.Z3_PARAMETER_DOUBLE) throw new Z3Exception("parameter is not a double "); return d; } /** * The Symbol value of the parameter. **/ public Symbol getSymbol() { if (getParameterKind() != Z3_parameter_kind.Z3_PARAMETER_SYMBOL) throw new Z3Exception("parameter is not a Symbol"); return sym; } /** * The Sort value of the parameter. **/ public Sort getSort() { if (getParameterKind() != Z3_parameter_kind.Z3_PARAMETER_SORT) throw new Z3Exception("parameter is not a Sort"); return srt; } /** * The AST value of the parameter. **/ public AST getAST() { if (getParameterKind() != Z3_parameter_kind.Z3_PARAMETER_AST) throw new Z3Exception("parameter is not an AST"); return ast; } /** * The FunctionDeclaration value of the parameter. **/ public FuncDecl getFuncDecl() { if (getParameterKind() != Z3_parameter_kind.Z3_PARAMETER_FUNC_DECL) throw new Z3Exception("parameter is not a function declaration"); return fd; } /** * The rational string value of the parameter. **/ public String getRational() { if (getParameterKind() != Z3_parameter_kind.Z3_PARAMETER_RATIONAL) throw new Z3Exception("parameter is not a rational String"); return r; } /** * The kind of the parameter. **/ public Z3_parameter_kind getParameterKind() { return kind; } Parameter(Z3_parameter_kind k, int i) { this.kind = k; this.i = i; } Parameter(Z3_parameter_kind k, double d) { this.kind = k; this.d = d; } Parameter(Z3_parameter_kind k, Symbol s) { this.kind = k; this.sym = s; } Parameter(Z3_parameter_kind k, Sort s) { this.kind = k; this.srt = s; } Parameter(Z3_parameter_kind k, AST a) { this.kind = k; this.ast = a; } Parameter(Z3_parameter_kind k, FuncDecl fd) { this.kind = k; this.fd = fd; } Parameter(Z3_parameter_kind k, String r) { this.kind = k; this.r = r; } } FuncDecl(Context ctx, long obj) { super(ctx, obj); } FuncDecl(Context ctx, Symbol name, Sort[] domain, Sort range) { super(ctx, Native.mkFuncDecl(ctx.nCtx(), name.getNativeObject(), AST.arrayLength(domain), AST.arrayToNative(domain), range.getNativeObject())); } FuncDecl(Context ctx, String prefix, Sort[] domain, Sort range) { super(ctx, Native.mkFreshFuncDecl(ctx.nCtx(), prefix, AST.arrayLength(domain), AST.arrayToNative(domain), range.getNativeObject())); } void checkNativeObject(long obj) { if (Native.getAstKind(getContext().nCtx(), obj) != Z3_ast_kind.Z3_FUNC_DECL_AST .toInt()) throw new Z3Exception( "Underlying object is not a function declaration"); super.checkNativeObject(obj); } /** * Create expression that applies function to arguments. **/ public Expr apply(Expr ... args) { getContext().checkContextMatch(args); return Expr.create(getContext(), this, args); } } z3-z3-4.8.7/src/api/java/FuncInterp.java000066400000000000000000000112211356505360400176250ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: FuncInterp.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * A function interpretation is represented as a finite map and an 'else' value. * Each entry in the finite map represents the value of a function given a set * of arguments. **/ public class FuncInterp extends Z3Object { /** * An Entry object represents an element in the finite map used to encode a * function interpretation. **/ public static class Entry extends Z3Object { /** * Return the (symbolic) value of this entry. * * @throws Z3Exception * @throws Z3Exception on error **/ public Expr getValue() { return Expr.create(getContext(), Native.funcEntryGetValue(getContext().nCtx(), getNativeObject())); } /** * The number of arguments of the entry. * @throws Z3Exception on error **/ public int getNumArgs() { return Native.funcEntryGetNumArgs(getContext().nCtx(), getNativeObject()); } /** * The arguments of the function entry. * * @throws Z3Exception * @throws Z3Exception on error **/ public Expr[] getArgs() { int n = getNumArgs(); Expr[] res = new Expr[n]; for (int i = 0; i < n; i++) res[i] = Expr.create(getContext(), Native.funcEntryGetArg( getContext().nCtx(), getNativeObject(), i)); return res; } /** * A string representation of the function entry. **/ @Override public String toString() { int n = getNumArgs(); String res = "["; Expr[] args = getArgs(); for (int i = 0; i < n; i++) res += args[i] + ", "; return res + getValue() + "]"; } Entry(Context ctx, long obj) { super(ctx, obj); } @Override void incRef() { Native.funcEntryIncRef(getContext().nCtx(), getNativeObject()); } @Override void addToReferenceQueue() { getContext().getFuncEntryDRQ().storeReference(getContext(), this); } } /** * The number of entries in the function interpretation. * @throws Z3Exception on error * @return an int **/ public int getNumEntries() { return Native.funcInterpGetNumEntries(getContext().nCtx(), getNativeObject()); } /** * The entries in the function interpretation * * @throws Z3Exception * @throws Z3Exception on error **/ public Entry[] getEntries() { int n = getNumEntries(); Entry[] res = new Entry[n]; for (int i = 0; i < n; i++) res[i] = new Entry(getContext(), Native.funcInterpGetEntry(getContext() .nCtx(), getNativeObject(), i)); return res; } /** * The (symbolic) `else' value of the function interpretation. * * @throws Z3Exception * @throws Z3Exception on error * @return an Expr **/ public Expr getElse() { return Expr.create(getContext(), Native.funcInterpGetElse(getContext().nCtx(), getNativeObject())); } /** * The arity of the function interpretation * @throws Z3Exception on error * @return an int **/ public int getArity() { return Native.funcInterpGetArity(getContext().nCtx(), getNativeObject()); } /** * A string representation of the function interpretation. **/ public String toString() { String res = ""; res += "["; for (Entry e : getEntries()) { int n = e.getNumArgs(); if (n > 1) res += "["; Expr[] args = e.getArgs(); for (int i = 0; i < n; i++) { if (i != 0) res += ", "; res += args[i]; } if (n > 1) res += "]"; res += " -> " + e.getValue() + ", "; } res += "else -> " + getElse(); res += "]"; return res; } FuncInterp(Context ctx, long obj) { super(ctx, obj); } @Override void incRef() { Native.funcInterpIncRef(getContext().nCtx(), getNativeObject()); } @Override void addToReferenceQueue() { getContext().getFuncInterpDRQ().storeReference(getContext(), this); } } z3-z3-4.8.7/src/api/java/FuncInterpDecRefQueue.java000066400000000000000000000007341356505360400217120ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: FuncInterpDecRefQueue.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; class FuncInterpDecRefQueue extends IDecRefQueue { public FuncInterpDecRefQueue() { super(); } @Override protected void decRef(Context ctx, long obj) { Native.funcInterpDecRef(ctx.nCtx(), obj); } }; z3-z3-4.8.7/src/api/java/FuncInterpEntryDecRefQueue.java000066400000000000000000000007561356505360400227400ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: FuncInterpEntryDecRefQueue.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; class FuncInterpEntryDecRefQueue extends IDecRefQueue { public FuncInterpEntryDecRefQueue() { super(); } @Override protected void decRef(Context ctx, long obj) { Native.funcEntryDecRef(ctx.nCtx(), obj); } } z3-z3-4.8.7/src/api/java/Global.java000066400000000000000000000061541356505360400167610ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: Global.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * Global functions for Z3. * Remarks: * This (static) class contains functions that effect the behaviour of Z3 * globally across contexts, etc. * **/ public final class Global { /** * Set a global (or module) parameter, which is shared by all Z3 contexts. * Remarks: * When a Z3 module is initialized it will use the value of these parameters * when Z3_params objects are not provided. * The name of parameter can be composed of characters [a-z][A-Z], digits [0-9], '-' and '_'. * The character '.' is a delimiter (more later). * The parameter names are case-insensitive. The character '-' should be viewed as an "alias" for '_'. * Thus, the following parameter names are considered equivalent: "pp.decimal-precision" and "PP.DECIMAL_PRECISION". * This function can be used to set parameters for a specific Z3 module. * This can be done by using <module-name>.<parameter-name>. * For example: * Z3_global_param_set('pp.decimal', 'true') * will set the parameter "decimal" in the module "pp" to true. * **/ public static void setParameter(String id, String value) { Native.globalParamSet(id, value); } /** * Get a global (or module) parameter. * Remarks: * This function cannot be invoked simultaneously from different threads without synchronization. * The result string stored in param_value is stored in a shared location. * @return null if the parameter {@code id} does not exist. **/ public static String getParameter(String id) { Native.StringPtr res = new Native.StringPtr(); if (!Native.globalParamGet(id, res)) { return null; } else { return res.value; } } /** * Restore the value of all global (and module) parameters. * Remarks: * This command will not affect already created objects (such as tactics and solvers) * @see #setParameter **/ public static void resetParameters() { Native.globalParamResetAll(); } /** * Enable/disable printing of warning messages to the console. * Remarks: Note * that this function is static and effects the behaviour of all contexts * globally. **/ public static void ToggleWarningMessages(boolean enabled) { Native.toggleWarningMessages((enabled)); } /** * Enable tracing messages tagged as `tag' when Z3 is compiled in debug mode. * * Remarks: It is a NOOP otherwise. **/ public static void enableTrace(String tag) { Native.enableTrace(tag); } /** * Disable tracing messages tagged as `tag' when Z3 is compiled in debug mode. * * Remarks: It is a NOOP otherwise. **/ public static void disableTrace(String tag) { Native.disableTrace(tag); } } z3-z3-4.8.7/src/api/java/Goal.java000066400000000000000000000147321356505360400164440ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: Goal.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; import com.microsoft.z3.enumerations.Z3_goal_prec; /** * A goal (aka problem). A goal is essentially a set of formulas, that can be * solved and/or transformed using tactics and solvers. **/ public class Goal extends Z3Object { /** * The precision of the goal. * Remarks: Goals can be transformed using over * and under approximations. An under approximation is applied when the * objective is to find a model for a given goal. An over approximation is * applied when the objective is to find a proof for a given goal. * **/ public Z3_goal_prec getPrecision() { return Z3_goal_prec.fromInt(Native.goalPrecision(getContext().nCtx(), getNativeObject())); } /** * Indicates whether the goal is precise. **/ public boolean isPrecise() { return getPrecision() == Z3_goal_prec.Z3_GOAL_PRECISE; } /** * Indicates whether the goal is an under-approximation. **/ public boolean isUnderApproximation() { return getPrecision() == Z3_goal_prec.Z3_GOAL_UNDER; } /** * Indicates whether the goal is an over-approximation. **/ public boolean isOverApproximation() { return getPrecision() == Z3_goal_prec.Z3_GOAL_OVER; } /** * Indicates whether the goal is garbage (i.e., the product of over- and * under-approximations). **/ public boolean isGarbage() { return getPrecision() == Z3_goal_prec.Z3_GOAL_UNDER_OVER; } /** * Adds the {@code constraints} to the given goal. * * @throws Z3Exception **/ public void add(BoolExpr ... constraints) { getContext().checkContextMatch(constraints); for (BoolExpr c : constraints) { Native.goalAssert(getContext().nCtx(), getNativeObject(), c.getNativeObject()); } } /** * Indicates whether the goal contains `false'. **/ public boolean inconsistent() { return Native.goalInconsistent(getContext().nCtx(), getNativeObject()); } /** * The depth of the goal. * Remarks: This tracks how many transformations * were applied to it. **/ public int getDepth() { return Native.goalDepth(getContext().nCtx(), getNativeObject()); } /** * Erases all formulas from the given goal. **/ public void reset() { Native.goalReset(getContext().nCtx(), getNativeObject()); } /** * The number of formulas in the goal. **/ public int size() { return Native.goalSize(getContext().nCtx(), getNativeObject()); } /** * The formulas in the goal. * * @throws Z3Exception **/ public BoolExpr[] getFormulas() { int n = size(); BoolExpr[] res = new BoolExpr[n]; for (int i = 0; i < n; i++) res[i] = (BoolExpr) Expr.create(getContext(), Native.goalFormula(getContext().nCtx(), getNativeObject(), i)); return res; } /** * The number of formulas, subformulas and terms in the goal. **/ public int getNumExprs() { return Native.goalNumExprs(getContext().nCtx(), getNativeObject()); } /** * Indicates whether the goal is empty, and it is precise or the product of * an under approximation. **/ public boolean isDecidedSat() { return Native.goalIsDecidedSat(getContext().nCtx(), getNativeObject()); } /** * Indicates whether the goal contains `false', and it is precise or the * product of an over approximation. **/ public boolean isDecidedUnsat() { return Native .goalIsDecidedUnsat(getContext().nCtx(), getNativeObject()); } /** * Translates (copies) the Goal to the target Context {@code ctx}. * * @throws Z3Exception **/ public Goal translate(Context ctx) { return new Goal(ctx, Native.goalTranslate(getContext().nCtx(), getNativeObject(), ctx.nCtx())); } /** * Simplifies the goal. * Remarks: Essentially invokes the `simplify' tactic * on the goal. **/ public Goal simplify() { Tactic t = getContext().mkTactic("simplify"); ApplyResult res = t.apply(this); if (res.getNumSubgoals() == 0) throw new Z3Exception("No subgoals"); else return res.getSubgoals()[0]; } /** * Simplifies the goal. * Remarks: Essentially invokes the `simplify' tactic * on the goal. **/ public Goal simplify(Params p) { Tactic t = getContext().mkTactic("simplify"); ApplyResult res = t.apply(this, p); if (res.getNumSubgoals() == 0) throw new Z3Exception("No subgoals"); else return res.getSubgoals()[0]; } /** * Goal to string conversion. * * @return A string representation of the Goal. **/ public String toString() { return Native.goalToString(getContext().nCtx(), getNativeObject()); } /** * Goal to BoolExpr conversion. * * Returns a string representation of the Goal. **/ public BoolExpr AsBoolExpr() { int n = size(); if (n == 0) { return getContext().mkTrue(); } else if (n == 1) { return getFormulas()[0]; } else { return getContext().mkAnd(getFormulas()); } } Goal(Context ctx, long obj) { super(ctx, obj); } Goal(Context ctx, boolean models, boolean unsatCores, boolean proofs) { super(ctx, Native.mkGoal(ctx.nCtx(), (models), (unsatCores), (proofs))); } /** * Convert a model for the goal into a model of the * original goal from which this goal was derived. * * @return A model for {@code g} * @throws Z3Exception **/ public Model convertModel(Model m) { return new Model(getContext(), Native.goalConvertModel(getContext().nCtx(), getNativeObject(), m.getNativeObject())); } @Override void incRef() { Native.goalIncRef(getContext().nCtx(), getNativeObject()); } @Override void addToReferenceQueue() { getContext().getGoalDRQ().storeReference(getContext(), this); } } z3-z3-4.8.7/src/api/java/GoalDecRefQueue.java000066400000000000000000000007011356505360400205110ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: GoalDecRefQueue.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; class GoalDecRefQueue extends IDecRefQueue { public GoalDecRefQueue() { super(); } @Override protected void decRef(Context ctx, long obj) { Native.goalDecRef(ctx.nCtx(), obj); } } z3-z3-4.8.7/src/api/java/IDecRefQueue.java000066400000000000000000000044761356505360400200340ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: IDecRefQueue.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; import java.lang.ref.PhantomReference; import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; import java.util.IdentityHashMap; import java.util.Map; /** * A queue to handle management of native memory. * *

Mechanics: once an object is created, a metadata is stored for it in * {@code referenceMap}, and a {@link PhantomReference} is created with a * reference to {@code referenceQueue}. * Once the object becomes strongly unreachable, the phantom reference gets * added by JVM to the {@code referenceQueue}. * After each object creation, we iterate through the available objects in * {@code referenceQueue} and decrement references for them. * * @param Type of object stored in queue. */ public abstract class IDecRefQueue { private final ReferenceQueue referenceQueue = new ReferenceQueue<>(); private final Map, Long> referenceMap = new IdentityHashMap<>(); protected IDecRefQueue() {} /** * An implementation of this method should decrement the reference on a * given native object. * This function should always be called on the {@code ctx} thread. * * @param ctx Z3 context. * @param obj Pointer to a Z3 object. */ protected abstract void decRef(Context ctx, long obj); public void storeReference(Context ctx, T obj) { PhantomReference ref = new PhantomReference<>(obj, referenceQueue); referenceMap.put(ref, obj.getNativeObject()); clear(ctx); } /** * Clean all references currently in {@code referenceQueue}. */ protected void clear(Context ctx) { Reference ref; while ((ref = referenceQueue.poll()) != null) { long z3ast = referenceMap.remove(ref); decRef(ctx, z3ast); } } /** * Clean all references stored in {@code referenceMap}, * regardless of whether they are in {@code referenceMap} or not. */ public void forceClear(Context ctx) { for (long ref : referenceMap.values()) { decRef(ctx, ref); } } } z3-z3-4.8.7/src/api/java/IntExpr.java000066400000000000000000000006641356505360400171520ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: IntExpr.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * Int expressions **/ public class IntExpr extends ArithExpr { /** * Constructor for IntExpr * @throws Z3Exception on error **/ IntExpr(Context ctx, long obj) { super(ctx, obj); } } z3-z3-4.8.7/src/api/java/IntNum.java000066400000000000000000000024731356505360400167730ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: IntNum.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; import java.math.BigInteger; /** * Integer Numerals **/ public class IntNum extends IntExpr { IntNum(Context ctx, long obj) { super(ctx, obj); } /** * Retrieve the int value. **/ public int getInt() { Native.IntPtr res = new Native.IntPtr(); if (!Native.getNumeralInt(getContext().nCtx(), getNativeObject(), res)) throw new Z3Exception("Numeral is not an int"); return res.value; } /** * Retrieve the 64-bit int value. **/ public long getInt64() { Native.LongPtr res = new Native.LongPtr(); if (!Native.getNumeralInt64(getContext().nCtx(), getNativeObject(), res)) throw new Z3Exception("Numeral is not an int64"); return res.value; } /** * Retrieve the BigInteger value. **/ public BigInteger getBigInteger() { return new BigInteger(this.toString()); } /** * Returns a string representation of the numeral. **/ public String toString() { return Native.getNumeralString(getContext().nCtx(), getNativeObject()); } } z3-z3-4.8.7/src/api/java/IntSort.java000066400000000000000000000006701356505360400171600ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: IntSort.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * An Integer sort **/ public class IntSort extends ArithSort { IntSort(Context ctx, long obj) { super(ctx, obj); } IntSort(Context ctx) { super(ctx, Native.mkIntSort(ctx.nCtx())); } } z3-z3-4.8.7/src/api/java/IntSymbol.java000066400000000000000000000021671356505360400175010ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: IntSymbol.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; import com.microsoft.z3.enumerations.Z3_symbol_kind; /** * Numbered symbols **/ public class IntSymbol extends Symbol { /** * The int value of the symbol. * Remarks: Throws an exception if the symbol * is not of int kind. **/ public int getInt() { if (!isIntSymbol()) throw new Z3Exception("Int requested from non-Int symbol"); return Native.getSymbolInt(getContext().nCtx(), getNativeObject()); } IntSymbol(Context ctx, long obj) { super(ctx, obj); } IntSymbol(Context ctx, int i) { super(ctx, Native.mkIntSymbol(ctx.nCtx(), i)); } @Override void checkNativeObject(long obj) { if (Native.getSymbolKind(getContext().nCtx(), obj) != Z3_symbol_kind.Z3_INT_SYMBOL .toInt()) throw new Z3Exception("Symbol is not of integer kind"); super.checkNativeObject(obj); } } z3-z3-4.8.7/src/api/java/Lambda.java000066400000000000000000000060241356505360400167350ustar00rootroot00000000000000/** Copyright (c) 2017 Microsoft Corporation Module Name: Lambda.java Abstract: Z3 Java API: Lambda Author: Christoph Wintersteiger (cwinter) 2012-03-19 Notes: **/ package com.microsoft.z3; import com.microsoft.z3.enumerations.Z3_ast_kind; /** * Lambda expressions. */public class Lambda extends ArrayExpr { /** * The number of bound variables. **/ public int getNumBound() { return Native.getQuantifierNumBound(getContext().nCtx(), getNativeObject()); } /** * The symbols for the bound variables. * * @throws Z3Exception **/ public Symbol[] getBoundVariableNames() { int n = getNumBound(); Symbol[] res = new Symbol[n]; for (int i = 0; i < n; i++) res[i] = Symbol.create(getContext(), Native.getQuantifierBoundName( getContext().nCtx(), getNativeObject(), i)); return res; } /** * The sorts of the bound variables. * * @throws Z3Exception **/ public Sort[] getBoundVariableSorts() { int n = getNumBound(); Sort[] res = new Sort[n]; for (int i = 0; i < n; i++) res[i] = Sort.create(getContext(), Native.getQuantifierBoundSort( getContext().nCtx(), getNativeObject(), i)); return res; } /** * The body of the quantifier. * * @throws Z3Exception **/ public Expr getBody() { return Expr.create(getContext(), Native.getQuantifierBody(getContext() .nCtx(), getNativeObject())); } /** * Translates (copies) the quantifier to the Context {@code ctx}. * * @param ctx A context * * @return A copy of the quantifier which is associated with {@code ctx} * @throws Z3Exception on error **/ public Lambda translate(Context ctx) { return (Lambda) super.translate(ctx); } public static Lambda of(Context ctx, Sort[] sorts, Symbol[] names, Expr body) { ctx.checkContextMatch(sorts); ctx.checkContextMatch(names); ctx.checkContextMatch(body); if (sorts.length != names.length) throw new Z3Exception("Number of sorts does not match number of names"); long nativeObject = Native.mkLambda(ctx.nCtx(), AST.arrayLength(sorts), AST.arrayToNative(sorts), Symbol.arrayToNative(names), body.getNativeObject()); return new Lambda(ctx, nativeObject); } /** * @param ctx Context to create the lambda on. * @param bound Bound variables. * @param body Body of the lambda expression. */ public static Lambda of(Context ctx, Expr[] bound, Expr body) { ctx.checkContextMatch(body); long nativeObject = Native.mkLambdaConst(ctx.nCtx(), AST.arrayLength(bound), AST.arrayToNative(bound), body.getNativeObject()); return new Lambda(ctx, nativeObject); } private Lambda(Context ctx, long obj) { super(ctx, obj); } } z3-z3-4.8.7/src/api/java/ListSort.java000066400000000000000000000046501356505360400173430ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: ListSort.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; import com.microsoft.z3.Native.LongPtr; /** * List sorts. **/ public class ListSort extends Sort { /** * The declaration of the nil function of this list sort. * @throws Z3Exception **/ public FuncDecl getNilDecl() { return new FuncDecl(getContext(), Native.getDatatypeSortConstructor(getContext().nCtx(), getNativeObject(), 0)); } /** * The empty list. * @throws Z3Exception **/ public Expr getNil() { return getContext().mkApp(getNilDecl()); } /** * The declaration of the isNil function of this list sort. * @throws Z3Exception **/ public FuncDecl getIsNilDecl() { return new FuncDecl(getContext(), Native.getDatatypeSortRecognizer(getContext().nCtx(), getNativeObject(), 0)); } /** * The declaration of the cons function of this list sort. * @throws Z3Exception **/ public FuncDecl getConsDecl() { return new FuncDecl(getContext(), Native.getDatatypeSortConstructor(getContext().nCtx(), getNativeObject(), 1)); } /** * The declaration of the isCons function of this list sort. * @throws Z3Exception * **/ public FuncDecl getIsConsDecl() { return new FuncDecl(getContext(), Native.getDatatypeSortRecognizer(getContext().nCtx(), getNativeObject(), 1)); } /** * The declaration of the head function of this list sort. * @throws Z3Exception **/ public FuncDecl getHeadDecl() { return new FuncDecl(getContext(), Native.getDatatypeSortConstructorAccessor(getContext().nCtx(), getNativeObject(), 1, 0)); } /** * The declaration of the tail function of this list sort. * @throws Z3Exception **/ public FuncDecl getTailDecl() { return new FuncDecl(getContext(), Native.getDatatypeSortConstructorAccessor(getContext().nCtx(), getNativeObject(), 1, 1)); } ListSort(Context ctx, Symbol name, Sort elemSort) { super(ctx, Native.mkListSort(ctx.nCtx(), name.getNativeObject(), elemSort.getNativeObject(), new LongPtr(), new Native.LongPtr(), new LongPtr(), new LongPtr(), new LongPtr(), new LongPtr())); } }; z3-z3-4.8.7/src/api/java/Log.java000066400000000000000000000026521356505360400163010ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: Log.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * Interaction logging for Z3. * Remarks: Note that this is a global, static log * and if multiple Context objects are created, it logs the interaction with all * of them. **/ public final class Log { private static boolean m_is_open = false; /** * Open an interaction log file. * @param filename the name of the file to open * * @return True if opening the log file succeeds, false otherwise. **/ public static boolean open(String filename) { m_is_open = true; return Native.openLog(filename) == 1; } /** * Closes the interaction log. **/ public static void close() { m_is_open = false; Native.closeLog(); } /** * Appends the user-provided string {@code s} to the interaction * log. * @throws Z3Exception **/ public static void append(String s) { if (!m_is_open) throw new Z3Exception("Log cannot be closed."); Native.appendLog(s); } /** * Checks whether the interaction log is opened. * * @return True if the interaction log is open, false otherwise. **/ public static boolean isOpen() { return m_is_open; } } z3-z3-4.8.7/src/api/java/Model.java000066400000000000000000000214151356505360400166160ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: Model.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; import com.microsoft.z3.enumerations.Z3_sort_kind; /** * A Model contains interpretations (assignments) of constants and functions. **/ public class Model extends Z3Object { /** * Retrieves the interpretation (the assignment) of {@code a} in * the model. * @param a A Constant * * @return An expression if the constant has an interpretation in the model, * null otherwise. * @throws Z3Exception **/ public Expr getConstInterp(Expr a) { getContext().checkContextMatch(a); return getConstInterp(a.getFuncDecl()); } /** * Retrieves the interpretation (the assignment) of {@code f} in * the model. * @param f A function declaration of zero arity * * @return An expression if the function has an interpretation in the model, * null otherwise. * @throws Z3Exception **/ public Expr getConstInterp(FuncDecl f) { getContext().checkContextMatch(f); if (f.getArity() != 0) throw new Z3Exception( "Non-zero arity functions have FunctionInterpretations as a model. Use getFuncInterp."); long n = Native.modelGetConstInterp(getContext().nCtx(), getNativeObject(), f.getNativeObject()); if (n == 0) return null; else return Expr.create(getContext(), n); } /** * Retrieves the interpretation (the assignment) of a non-constant {@code f} in the model. * @param f A function declaration of non-zero arity * * @return A FunctionInterpretation if the function has an interpretation in * the model, null otherwise. * @throws Z3Exception **/ public FuncInterp getFuncInterp(FuncDecl f) { getContext().checkContextMatch(f); Z3_sort_kind sk = Z3_sort_kind.fromInt(Native.getSortKind(getContext() .nCtx(), Native.getRange(getContext().nCtx(), f.getNativeObject()))); if (f.getArity() == 0) { long n = Native.modelGetConstInterp(getContext().nCtx(), getNativeObject(), f.getNativeObject()); if (sk == Z3_sort_kind.Z3_ARRAY_SORT) { if (n == 0) return null; else { if (Native.isAsArray(getContext().nCtx(), n)) { long fd = Native.getAsArrayFuncDecl(getContext().nCtx(), n); return getFuncInterp(new FuncDecl(getContext(), fd)); } return null; } } else { throw new Z3Exception( "Constant functions do not have a function interpretation; use getConstInterp"); } } else { long n = Native.modelGetFuncInterp(getContext().nCtx(), getNativeObject(), f.getNativeObject()); if (n == 0) return null; else return new FuncInterp(getContext(), n); } } /** * The number of constants that have an interpretation in the model. **/ public int getNumConsts() { return Native.modelGetNumConsts(getContext().nCtx(), getNativeObject()); } /** * The function declarations of the constants in the model. * * @throws Z3Exception **/ public FuncDecl[] getConstDecls() { int n = getNumConsts(); FuncDecl[] res = new FuncDecl[n]; for (int i = 0; i < n; i++) res[i] = new FuncDecl(getContext(), Native.modelGetConstDecl(getContext() .nCtx(), getNativeObject(), i)); return res; } /** * The number of function interpretations in the model. **/ public int getNumFuncs() { return Native.modelGetNumFuncs(getContext().nCtx(), getNativeObject()); } /** * The function declarations of the function interpretations in the model. * * @throws Z3Exception **/ public FuncDecl[] getFuncDecls() { int n = getNumFuncs(); FuncDecl[] res = new FuncDecl[n]; for (int i = 0; i < n; i++) res[i] = new FuncDecl(getContext(), Native.modelGetFuncDecl(getContext() .nCtx(), getNativeObject(), i)); return res; } /** * All symbols that have an interpretation in the model. * * @throws Z3Exception **/ public FuncDecl[] getDecls() { int nFuncs = getNumFuncs(); int nConsts = getNumConsts(); int n = nFuncs + nConsts; FuncDecl[] res = new FuncDecl[n]; for (int i = 0; i < nConsts; i++) res[i] = new FuncDecl(getContext(), Native.modelGetConstDecl(getContext() .nCtx(), getNativeObject(), i)); for (int i = 0; i < nFuncs; i++) res[nConsts + i] = new FuncDecl(getContext(), Native.modelGetFuncDecl( getContext().nCtx(), getNativeObject(), i)); return res; } /** * A ModelEvaluationFailedException is thrown when an expression cannot be * evaluated by the model. **/ @SuppressWarnings("serial") public class ModelEvaluationFailedException extends Z3Exception { /** * An exception that is thrown when model evaluation fails. **/ public ModelEvaluationFailedException() { super(); } } /** * Evaluates the expression {@code t} in the current model. * Remarks: This function may fail if {@code t} contains * quantifiers, is partial (MODEL_PARTIAL enabled), or if {@code t} is not well-sorted. In this case a * {@code ModelEvaluationFailedException} is thrown. * @param t the expression to evaluate * @param completion An expression {@code completion} When this flag * is enabled, a model value will be assigned to any constant or function * that does not have an interpretation in the model. * @return The evaluation of {@code t} in the model. * @throws Z3Exception **/ public Expr eval(Expr t, boolean completion) { Native.LongPtr v = new Native.LongPtr(); if (!Native.modelEval(getContext().nCtx(), getNativeObject(), t.getNativeObject(), (completion), v)) throw new ModelEvaluationFailedException(); else return Expr.create(getContext(), v.value); } /** * Alias for {@code Eval}. * * @throws Z3Exception **/ public Expr evaluate(Expr t, boolean completion) { return eval(t, completion); } /** * The number of uninterpreted sorts that the model has an interpretation * for. **/ public int getNumSorts() { return Native.modelGetNumSorts(getContext().nCtx(), getNativeObject()); } /** * The uninterpreted sorts that the model has an interpretation for. * Remarks: Z3 also provides an interpretation for uninterpreted sorts used * in a formula. The interpretation for a sort is a finite set of distinct * values. We say this finite set is the "universe" of the sort. * * @see #getNumSorts * @see #getSortUniverse * * @throws Z3Exception **/ public Sort[] getSorts() { int n = getNumSorts(); Sort[] res = new Sort[n]; for (int i = 0; i < n; i++) res[i] = Sort.create(getContext(), Native.modelGetSort(getContext().nCtx(), getNativeObject(), i)); return res; } /** * The finite set of distinct values that represent the interpretation for * sort {@code s}. * @param s An uninterpreted sort * * @return An array of expressions, where each is an element of the universe * of {@code s} * @throws Z3Exception **/ public Expr[] getSortUniverse(Sort s) { ASTVector nUniv = new ASTVector(getContext(), Native.modelGetSortUniverse( getContext().nCtx(), getNativeObject(), s.getNativeObject())); return nUniv.ToExprArray(); } /** * Conversion of models to strings. * * @return A string representation of the model. **/ @Override public String toString() { return Native.modelToString(getContext().nCtx(), getNativeObject()); } Model(Context ctx, long obj) { super(ctx, obj); } @Override void incRef() { Native.modelIncRef(getContext().nCtx(), getNativeObject()); } @Override void addToReferenceQueue() { getContext().getModelDRQ().storeReference(getContext(), this); } } z3-z3-4.8.7/src/api/java/ModelDecRefQueue.java000066400000000000000000000007021356505360400206700ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: ModelDecRefQueue.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; class ModelDecRefQueue extends IDecRefQueue { public ModelDecRefQueue() { super(); } @Override protected void decRef(Context ctx, long obj) { Native.modelDecRef(ctx.nCtx(), obj); } } z3-z3-4.8.7/src/api/java/Optimize.java000066400000000000000000000246411356505360400173620ustar00rootroot00000000000000/** Copyright (c) 2015 Microsoft Corporation Module Name: Optimize.java Abstract: Z3 Java API: Optimizes Author: Nikolaj Bjorner (nbjorner) 2015-07-16 Notes: **/ package com.microsoft.z3; import com.microsoft.z3.enumerations.Z3_lbool; /** * Object for managing optimization context **/ public class Optimize extends Z3Object { /** * A string that describes all available optimize solver parameters. **/ public String getHelp() { return Native.optimizeGetHelp(getContext().nCtx(), getNativeObject()); } /** * Sets the optimize solver parameters. * * @throws Z3Exception **/ public void setParameters(Params value) { Native.optimizeSetParams(getContext().nCtx(), getNativeObject(), value.getNativeObject()); } /** * Retrieves parameter descriptions for Optimize solver. **/ public ParamDescrs getParameterDescriptions() { return new ParamDescrs(getContext(), Native.optimizeGetParamDescrs(getContext().nCtx(), getNativeObject())); } /** * Assert a constraint (or multiple) into the optimize solver. **/ public void Assert(BoolExpr ... constraints) { getContext().checkContextMatch(constraints); for (BoolExpr a : constraints) { Native.optimizeAssert(getContext().nCtx(), getNativeObject(), a.getNativeObject()); } } /** * Alias for Assert. **/ public void Add(BoolExpr ... constraints) { Assert(constraints); } /** * Handle to objectives returned by objective functions. **/ public static class Handle { private final Optimize opt; private final int handle; Handle(Optimize opt, int h) { this.opt = opt; this.handle = h; } /** * Retrieve a lower bound for the objective handle. **/ public Expr getLower() { return opt.GetLower(handle); } /** * Retrieve an upper bound for the objective handle. **/ public Expr getUpper() { return opt.GetUpper(handle); } /** * @return a triple representing the upper bound of the objective handle. * * The triple contains values {@code inf, value, eps}, * where the objective value is unbounded iff {@code inf} is non-zero, * and otherwise is represented by the expression {@code value + eps * EPSILON}, * where {@code EPSILON} is an arbitrarily small real number. */ public Expr[] getUpperAsVector() { return opt.GetUpperAsVector(handle); } /** * @return a triple representing the upper bound of the objective handle. * *

See {@link #getUpperAsVector()} for triple semantics. */ public Expr[] getLowerAsVector() { return opt.GetLowerAsVector(handle); } /** * Retrieve the value of an objective. **/ public Expr getValue() { return getLower(); } /** * Print a string representation of the handle. **/ @Override public String toString() { return getValue().toString(); } } /** * Assert soft constraint * * Return an objective which associates with the group of constraints. * **/ public Handle AssertSoft(BoolExpr constraint, int weight, String group) { getContext().checkContextMatch(constraint); Symbol s = getContext().mkSymbol(group); return new Handle(this, Native.optimizeAssertSoft(getContext().nCtx(), getNativeObject(), constraint.getNativeObject(), Integer.toString(weight), s.getNativeObject())); } /** * Check satisfiability of asserted constraints. * Produce a model that (when the objectives are bounded and * don't use strict inequalities) meets the objectives. **/ public Status Check(Expr... assumptions) { Z3_lbool r; if (assumptions == null) { r = Z3_lbool.fromInt( Native.optimizeCheck( getContext().nCtx(), getNativeObject(), 0, null)); } else { r = Z3_lbool.fromInt( Native.optimizeCheck( getContext().nCtx(), getNativeObject(), assumptions.length, AST.arrayToNative(assumptions))); } switch (r) { case Z3_L_TRUE: return Status.SATISFIABLE; case Z3_L_FALSE: return Status.UNSATISFIABLE; default: return Status.UNKNOWN; } } /** * Creates a backtracking point. **/ public void Push() { Native.optimizePush(getContext().nCtx(), getNativeObject()); } /** * Backtrack one backtracking point. * * Note that an exception is thrown if Pop is called without a corresponding Push. **/ public void Pop() { Native.optimizePop(getContext().nCtx(), getNativeObject()); } /** * The model of the last Check. * * The result is null if Check was not invoked before, * if its results was not SATISFIABLE, or if model production is not enabled. **/ public Model getModel() { long x = Native.optimizeGetModel(getContext().nCtx(), getNativeObject()); if (x == 0) { return null; } else { return new Model(getContext(), x); } } /** * The unsat core of the last {@code Check}. * Remarks: The unsat core * is a subset of {@code Assumptions} The result is empty if * {@code Check} was not invoked before, if its results was not * {@code UNSATISFIABLE}, or if core production is disabled. * * @throws Z3Exception **/ public BoolExpr[] getUnsatCore() { ASTVector core = new ASTVector(getContext(), Native.optimizeGetUnsatCore(getContext().nCtx(), getNativeObject())); return core.ToBoolExprArray(); } /** * Declare an arithmetical maximization objective. * Return a handle to the objective. The handle is used as * to retrieve the values of objectives after calling Check. **/ public Handle MkMaximize(Expr e) { return new Handle(this, Native.optimizeMaximize(getContext().nCtx(), getNativeObject(), e.getNativeObject())); } /** * Declare an arithmetical minimization objective. * Similar to MkMaximize. **/ public Handle MkMinimize(Expr e) { return new Handle(this, Native.optimizeMinimize(getContext().nCtx(), getNativeObject(), e.getNativeObject())); } /** * Retrieve a lower bound for the objective handle. **/ private Expr GetLower(int index) { return Expr.create(getContext(), Native.optimizeGetLower(getContext().nCtx(), getNativeObject(), index)); } /** * Retrieve an upper bound for the objective handle. **/ private Expr GetUpper(int index) { return Expr.create(getContext(), Native.optimizeGetUpper(getContext().nCtx(), getNativeObject(), index)); } /** * @return Triple representing the upper bound for the objective handle. * *

See {@link Handle#getUpperAsVector}. */ private Expr[] GetUpperAsVector(int index) { return unpackObjectiveValueVector( Native.optimizeGetUpperAsVector( getContext().nCtx(), getNativeObject(), index ) ); } /** * @return Triple representing the upper bound for the objective handle. * *

See {@link Handle#getLowerAsVector}. */ private Expr[] GetLowerAsVector(int index) { return unpackObjectiveValueVector( Native.optimizeGetLowerAsVector( getContext().nCtx(), getNativeObject(), index ) ); } private Expr[] unpackObjectiveValueVector(long nativeVec) { ASTVector vec = new ASTVector( getContext(), nativeVec ); return new Expr[] { (Expr) vec.get(0), (Expr) vec.get(1), (Expr) vec.get(2) }; } /** * Return a string the describes why the last to check returned unknown **/ public String getReasonUnknown() { return Native.optimizeGetReasonUnknown(getContext().nCtx(), getNativeObject()); } /** * Print the context to a String (SMT-LIB parseable benchmark). **/ @Override public String toString() { return Native.optimizeToString(getContext().nCtx(), getNativeObject()); } /** * Parse an SMT-LIB2 file with optimization objectives and constraints. * The parsed constraints and objectives are added to the optimization context. */ public void fromFile(String file) { Native.optimizeFromFile(getContext().nCtx(), getNativeObject(), file); } /** * Similar to FromFile. Instead it takes as argument a string. */ public void fromString(String s) { Native.optimizeFromString(getContext().nCtx(), getNativeObject(), s); } /** * The set of asserted formulas. */ public BoolExpr[] getAssertions() { ASTVector assertions = new ASTVector(getContext(), Native.optimizeGetAssertions(getContext().nCtx(), getNativeObject())); return assertions.ToBoolExprArray(); } /** * The set of asserted formulas. */ public Expr[] getObjectives() { ASTVector objectives = new ASTVector(getContext(), Native.optimizeGetObjectives(getContext().nCtx(), getNativeObject())); return objectives.ToExprArray(); } /** * Optimize statistics. **/ public Statistics getStatistics() { return new Statistics(getContext(), Native.optimizeGetStatistics(getContext().nCtx(), getNativeObject())); } Optimize(Context ctx, long obj) throws Z3Exception { super(ctx, obj); } Optimize(Context ctx) throws Z3Exception { super(ctx, Native.mkOptimize(ctx.nCtx())); } @Override void incRef() { Native.optimizeIncRef(getContext().nCtx(), getNativeObject()); } @Override void addToReferenceQueue() { getContext().getOptimizeDRQ().storeReference(getContext(), this); } } z3-z3-4.8.7/src/api/java/OptimizeDecRefQueue.java000066400000000000000000000007221356505360400214320ustar00rootroot00000000000000/** Copyright (c) 2012-2015 Microsoft Corporation Module Name: OptimizeDecRefQueue.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; class OptimizeDecRefQueue extends IDecRefQueue { public OptimizeDecRefQueue() { super(); } @Override protected void decRef(Context ctx, long obj) { Native.optimizeDecRef(ctx.nCtx(), obj); } }; z3-z3-4.8.7/src/api/java/ParamDescrs.java000066400000000000000000000043271356505360400177650ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: ParamDescrs.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; import com.microsoft.z3.enumerations.Z3_param_kind; /** * A ParamDescrs describes a set of parameters. **/ public class ParamDescrs extends Z3Object { /** * validate a set of parameters. **/ public void validate(Params p) { Native.paramsValidate(getContext().nCtx(), p.getNativeObject(), getNativeObject()); } /** * Retrieve kind of parameter. **/ public Z3_param_kind getKind(Symbol name) { return Z3_param_kind.fromInt(Native.paramDescrsGetKind( getContext().nCtx(), getNativeObject(), name.getNativeObject())); } /** * Retrieve documentation of parameter. **/ public String getDocumentation(Symbol name) { return Native.paramDescrsGetDocumentation(getContext().nCtx(), getNativeObject(), name.getNativeObject()); } /** * Retrieve all names of parameters. * * @throws Z3Exception **/ public Symbol[] getNames() { int sz = Native.paramDescrsSize(getContext().nCtx(), getNativeObject()); Symbol[] names = new Symbol[sz]; for (int i = 0; i < sz; ++i) { names[i] = Symbol.create(getContext(), Native.paramDescrsGetName( getContext().nCtx(), getNativeObject(), i)); } return names; } /** * The size of the ParamDescrs. **/ public int size() { return Native.paramDescrsSize(getContext().nCtx(), getNativeObject()); } /** * Retrieves a string representation of the ParamDescrs. **/ @Override public String toString() { return Native.paramDescrsToString(getContext().nCtx(), getNativeObject()); } ParamDescrs(Context ctx, long obj) { super(ctx, obj); } @Override void incRef() { Native.paramDescrsIncRef(getContext().nCtx(), getNativeObject()); } @Override void addToReferenceQueue() { getContext().getParamDescrsDRQ().storeReference(getContext(), this); } } z3-z3-4.8.7/src/api/java/ParamDescrsDecRefQueue.java000066400000000000000000000007441356505360400220420ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: ParamDescrsDecRefQueue.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; class ParamDescrsDecRefQueue extends IDecRefQueue { public ParamDescrsDecRefQueue() { super(); } @Override protected void decRef(Context ctx, long obj) { Native.paramDescrsDecRef(ctx.nCtx(), obj); } } z3-z3-4.8.7/src/api/java/Params.java000066400000000000000000000062021356505360400167760ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: Params.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * A ParameterSet represents a configuration in the form of Symbol/value pairs. **/ public class Params extends Z3Object { /** * Adds a parameter setting. **/ public void add(Symbol name, boolean value) { Native.paramsSetBool(getContext().nCtx(), getNativeObject(), name.getNativeObject(), (value)); } /** * Adds a parameter setting. **/ public void add(Symbol name, double value) { Native.paramsSetDouble(getContext().nCtx(), getNativeObject(), name.getNativeObject(), value); } /** * Adds a parameter setting. **/ public void add(Symbol name, String value) { Native.paramsSetSymbol(getContext().nCtx(), getNativeObject(), name.getNativeObject(), getContext().mkSymbol(value).getNativeObject()); } /** * Adds a parameter setting. **/ public void add(Symbol name, Symbol value) { Native.paramsSetSymbol(getContext().nCtx(), getNativeObject(), name.getNativeObject(), value.getNativeObject()); } /** * Adds a parameter setting. **/ public void add(String name, boolean value) { Native.paramsSetBool(getContext().nCtx(), getNativeObject(), getContext().mkSymbol(name).getNativeObject(), value); } /** * Adds a parameter setting. **/ public void add(String name, int value) { Native.paramsSetUint(getContext().nCtx(), getNativeObject(), getContext() .mkSymbol(name).getNativeObject(), value); } /** * Adds a parameter setting. **/ public void add(String name, double value) { Native.paramsSetDouble(getContext().nCtx(), getNativeObject(), getContext() .mkSymbol(name).getNativeObject(), value); } /** * Adds a parameter setting. **/ public void add(String name, Symbol value) { Native.paramsSetSymbol(getContext().nCtx(), getNativeObject(), getContext() .mkSymbol(name).getNativeObject(), value.getNativeObject()); } /** * Adds a parameter setting. **/ public void add(String name, String value) { Native.paramsSetSymbol(getContext().nCtx(), getNativeObject(), getContext().mkSymbol(name).getNativeObject(), getContext().mkSymbol(value).getNativeObject()); } /** * A string representation of the parameter set. **/ @Override public String toString() { return Native.paramsToString(getContext().nCtx(), getNativeObject()); } Params(Context ctx) { super(ctx, Native.mkParams(ctx.nCtx())); } @Override void incRef() { Native.paramsIncRef(getContext().nCtx(), getNativeObject()); } @Override void addToReferenceQueue() { getContext().getParamsDRQ().storeReference(getContext(), this); } } z3-z3-4.8.7/src/api/java/ParamsDecRefQueue.java000066400000000000000000000007061356505360400210570ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: ParamDecRefQueue.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; class ParamsDecRefQueue extends IDecRefQueue { public ParamsDecRefQueue() { super(); } @Override protected void decRef(Context ctx, long obj) { Native.paramsDecRef(ctx.nCtx(), obj); } } z3-z3-4.8.7/src/api/java/Pattern.java000066400000000000000000000023301356505360400171660ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: Pattern.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * Patterns comprise a list of terms. The list should be non-empty. If the list * comprises of more than one term, it is also called a multi-pattern. **/ public class Pattern extends AST { /** * The number of terms in the pattern. **/ public int getNumTerms() { return Native.getPatternNumTerms(getContext().nCtx(), getNativeObject()); } /** * The terms in the pattern. * * @throws Z3Exception **/ public Expr[] getTerms() { int n = getNumTerms(); Expr[] res = new Expr[n]; for (int i = 0; i < n; i++) res[i] = Expr.create(getContext(), Native.getPattern(getContext().nCtx(), getNativeObject(), i)); return res; } /** * A string representation of the pattern. **/ @Override public String toString() { return Native.patternToString(getContext().nCtx(), getNativeObject()); } Pattern(Context ctx, long obj) { super(ctx, obj); } } z3-z3-4.8.7/src/api/java/Probe.java000066400000000000000000000027131356505360400166250ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: Probe.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * Probes are used to inspect a goal (aka problem) and collect information that * may be used to decide which solver and/or preprocessing step will be used. * The complete list of probes may be obtained using the procedures * {@code Context.NumProbes} and {@code Context.ProbeNames}. It may * also be obtained using the command {@code (help-tactic)} in the SMT 2.0 * front-end. **/ public class Probe extends Z3Object { /** * Execute the probe over the goal. * * @return A probe always produce a double value. "Boolean" probes return * 0.0 for false, and a value different from 0.0 for true. * @throws Z3Exception **/ public double apply(Goal g) { getContext().checkContextMatch(g); return Native.probeApply(getContext().nCtx(), getNativeObject(), g.getNativeObject()); } Probe(Context ctx, long obj) { super(ctx, obj); } Probe(Context ctx, String name) { super(ctx, Native.mkProbe(ctx.nCtx(), name)); } @Override void incRef() { Native.probeIncRef(getContext().nCtx(), getNativeObject()); } @Override void addToReferenceQueue() { getContext().getProbeDRQ().storeReference(getContext(), this); } } z3-z3-4.8.7/src/api/java/ProbeDecRefQueue.java000066400000000000000000000007071356505360400207040ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: ProbeDecRefQueue.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; class ProbeDecRefQueue extends IDecRefQueue { public ProbeDecRefQueue() { super(); } @Override protected void decRef(Context ctx, long obj) { Native.probeDecRef(ctx.nCtx(), obj); } }; z3-z3-4.8.7/src/api/java/Quantifier.java000066400000000000000000000166601356505360400176730ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: Quantifier.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; import com.microsoft.z3.enumerations.Z3_ast_kind; /** * Quantifier expressions. **/ public class Quantifier extends BoolExpr { /** * Indicates whether the quantifier is universal. **/ public boolean isUniversal() { return Native.isQuantifierForall(getContext().nCtx(), getNativeObject()); } /** * Indicates whether the quantifier is existential. **/ public boolean isExistential() { return Native.isQuantifierExists(getContext().nCtx(), getNativeObject()); } /** * The weight of the quantifier. **/ public int getWeight() { return Native.getQuantifierWeight(getContext().nCtx(), getNativeObject()); } /** * The number of patterns. **/ public int getNumPatterns() { return Native .getQuantifierNumPatterns(getContext().nCtx(), getNativeObject()); } /** * The patterns. * * @throws Z3Exception **/ public Pattern[] getPatterns() { int n = getNumPatterns(); Pattern[] res = new Pattern[n]; for (int i = 0; i < n; i++) res[i] = new Pattern(getContext(), Native.getQuantifierPatternAst( getContext().nCtx(), getNativeObject(), i)); return res; } /** * The number of no-patterns. **/ public int getNumNoPatterns() { return Native.getQuantifierNumNoPatterns(getContext().nCtx(), getNativeObject()); } /** * The no-patterns. * * @throws Z3Exception **/ public Pattern[] getNoPatterns() { int n = getNumNoPatterns(); Pattern[] res = new Pattern[n]; for (int i = 0; i < n; i++) res[i] = new Pattern(getContext(), Native.getQuantifierNoPatternAst( getContext().nCtx(), getNativeObject(), i)); return res; } /** * The number of bound variables. **/ public int getNumBound() { return Native.getQuantifierNumBound(getContext().nCtx(), getNativeObject()); } /** * The symbols for the bound variables. * * @throws Z3Exception **/ public Symbol[] getBoundVariableNames() { int n = getNumBound(); Symbol[] res = new Symbol[n]; for (int i = 0; i < n; i++) res[i] = Symbol.create(getContext(), Native.getQuantifierBoundName( getContext().nCtx(), getNativeObject(), i)); return res; } /** * The sorts of the bound variables. * * @throws Z3Exception **/ public Sort[] getBoundVariableSorts() { int n = getNumBound(); Sort[] res = new Sort[n]; for (int i = 0; i < n; i++) res[i] = Sort.create(getContext(), Native.getQuantifierBoundSort( getContext().nCtx(), getNativeObject(), i)); return res; } /** * The body of the quantifier. * * @throws Z3Exception **/ public Expr getBody() { return Expr.create(getContext(), Native.getQuantifierBody(getContext() .nCtx(), getNativeObject())); } /** * Translates (copies) the quantifier to the Context {@code ctx}. * * @param ctx A context * * @return A copy of the quantifier which is associated with {@code ctx} * @throws Z3Exception on error **/ public Quantifier translate(Context ctx) { return (Quantifier) super.translate(ctx); } /** * Create a quantified expression. * * @param patterns Nullable patterns * @param noPatterns Nullable noPatterns * @param quantifierID Nullable quantifierID * @param skolemID Nullable skolemID */ public static Quantifier of( Context ctx, boolean isForall, Sort[] sorts, Symbol[] names, Expr body, int weight, Pattern[] patterns, Expr[] noPatterns, Symbol quantifierID, Symbol skolemID) { ctx.checkContextMatch(patterns); ctx.checkContextMatch(noPatterns); ctx.checkContextMatch(sorts); ctx.checkContextMatch(names); ctx.checkContextMatch(body); if (sorts.length != names.length) { throw new Z3Exception( "Number of sorts does not match number of names"); } long nativeObj; if (noPatterns == null && quantifierID == null && skolemID == null) { nativeObj = Native.mkQuantifier(ctx.nCtx(), (isForall), weight, AST.arrayLength(patterns), AST .arrayToNative(patterns), AST.arrayLength(sorts), AST .arrayToNative(sorts), Symbol.arrayToNative(names), body .getNativeObject()); } else { nativeObj = Native.mkQuantifierEx(ctx.nCtx(), (isForall), weight, AST.getNativeObject(quantifierID), AST.getNativeObject(skolemID), AST.arrayLength(patterns), AST.arrayToNative(patterns), AST.arrayLength(noPatterns), AST.arrayToNative(noPatterns), AST.arrayLength(sorts), AST.arrayToNative(sorts), Symbol.arrayToNative(names), body.getNativeObject()); } return new Quantifier(ctx, nativeObj); } /** * @param ctx Context to create the quantifier on. * @param isForall Quantifier type. * @param bound Bound variables. * @param body Body of the quantifier. * @param weight Weight. * @param patterns Nullable array of patterns. * @param noPatterns Nullable array of noPatterns. * @param quantifierID Nullable quantifier identifier. * @param skolemID Nullable skolem identifier. */ public static Quantifier of(Context ctx, boolean isForall, Expr[] bound, Expr body, int weight, Pattern[] patterns, Expr[] noPatterns, Symbol quantifierID, Symbol skolemID) { ctx.checkContextMatch(noPatterns); ctx.checkContextMatch(patterns); ctx.checkContextMatch(body); long nativeObj; if (noPatterns == null && quantifierID == null && skolemID == null) { nativeObj = Native.mkQuantifierConst(ctx.nCtx(), isForall, weight, AST.arrayLength(bound), AST.arrayToNative(bound), AST.arrayLength(patterns), AST.arrayToNative(patterns), body.getNativeObject()); } else { nativeObj = Native.mkQuantifierConstEx(ctx.nCtx(), isForall, weight, AST.getNativeObject(quantifierID), AST.getNativeObject(skolemID), AST.arrayLength(bound), AST.arrayToNative(bound), AST.arrayLength(patterns), AST.arrayToNative(patterns), AST.arrayLength(noPatterns), AST.arrayToNative(noPatterns), body.getNativeObject()); } return new Quantifier(ctx, nativeObj); } Quantifier(Context ctx, long obj) { super(ctx, obj); } @Override void checkNativeObject(long obj) { if (Native.getAstKind(getContext().nCtx(), obj) != Z3_ast_kind.Z3_QUANTIFIER_AST .toInt()) { throw new Z3Exception("Underlying object is not a quantifier"); } super.checkNativeObject(obj); } } z3-z3-4.8.7/src/api/java/README000066400000000000000000000003521356505360400155700ustar00rootroot00000000000000Java bindings ------------- The Java bindings will be included in the Z3 build if it is configured with the option --java to python scripts/mk_make.py. This will produce the com.microsoft.z3.jar package in the build directory. z3-z3-4.8.7/src/api/java/RatNum.java000066400000000000000000000034211356505360400167610ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: RatNum.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; import java.math.BigInteger; /** * Rational Numerals **/ public class RatNum extends RealExpr { /** * The numerator of a rational numeral. **/ public IntNum getNumerator() { return new IntNum(getContext(), Native.getNumerator(getContext().nCtx(), getNativeObject())); } /** * The denominator of a rational numeral. **/ public IntNum getDenominator() { return new IntNum(getContext(), Native.getDenominator(getContext().nCtx(), getNativeObject())); } /** * Converts the numerator of the rational to a BigInteger **/ public BigInteger getBigIntNumerator() { IntNum n = getNumerator(); return new BigInteger(n.toString()); } /** * Converts the denominator of the rational to a BigInteger **/ public BigInteger getBigIntDenominator() { IntNum n = getDenominator(); return new BigInteger(n.toString()); } /** * Returns a string representation in decimal notation. * Remarks: The result * has at most {@code precision} decimal places. **/ public String toDecimalString(int precision) { return Native.getNumeralDecimalString(getContext().nCtx(), getNativeObject(), precision); } /** * Returns a string representation of the numeral. **/ @Override public String toString() { return Native.getNumeralString(getContext().nCtx(), getNativeObject()); } RatNum(Context ctx, long obj) { super(ctx, obj); } } z3-z3-4.8.7/src/api/java/ReExpr.java000066400000000000000000000006521356505360400167630ustar00rootroot00000000000000/** Copyright (c) 2012-2016 Microsoft Corporation Module Name: ReExpr.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * Re expressions **/ public class ReExpr extends Expr { /** * Constructor for ReExpr * @throws Z3Exception on error **/ ReExpr(Context ctx, long obj) { super(ctx, obj); } } z3-z3-4.8.7/src/api/java/ReSort.java000066400000000000000000000005421356505360400167720ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: ReSort.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * A Regular expression sort **/ public class ReSort extends Sort { ReSort(Context ctx, long obj) { super(ctx, obj); } } z3-z3-4.8.7/src/api/java/RealExpr.java000066400000000000000000000006251356505360400173000ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: RealExpr.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * Real expressions **/ public class RealExpr extends ArithExpr { /** * Constructor for RealExpr **/ RealExpr(Context ctx, long obj) { super(ctx, obj); } } z3-z3-4.8.7/src/api/java/RealSort.java000066400000000000000000000006711356505360400173120ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: RealSort.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * A real sort **/ public class RealSort extends ArithSort { RealSort(Context ctx, long obj) { super(ctx, obj); } RealSort(Context ctx) { super(ctx, Native.mkRealSort(ctx.nCtx())); } } z3-z3-4.8.7/src/api/java/RelationSort.java000066400000000000000000000020351356505360400202000ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: RelationSort.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * Relation sorts. **/ public class RelationSort extends Sort { /** * The arity of the relation sort. **/ public int getArity() { return Native.getRelationArity(getContext().nCtx(), getNativeObject()); } /** * The sorts of the columns of the relation sort. * @throws Z3Exception **/ public Sort[] getColumnSorts() { if (m_columnSorts != null) return m_columnSorts; int n = getArity(); Sort[] res = new Sort[n]; for (int i = 0; i < n; i++) res[i] = Sort.create(getContext(), Native.getRelationColumn(getContext() .nCtx(), getNativeObject(), i)); return res; } private Sort[] m_columnSorts = null; RelationSort(Context ctx, long obj) { super(ctx, obj); } } z3-z3-4.8.7/src/api/java/SeqExpr.java000066400000000000000000000006571356505360400171520ustar00rootroot00000000000000/** Copyright (c) 2012-2016 Microsoft Corporation Module Name: SeqExpr.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * Seq expressions **/ public class SeqExpr extends Expr { /** * Constructor for SeqExpr * @throws Z3Exception on error **/ SeqExpr(Context ctx, long obj) { super(ctx, obj); } } z3-z3-4.8.7/src/api/java/SeqSort.java000066400000000000000000000005331356505360400171540ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: SeqSort.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * A Sequence sort **/ public class SeqSort extends Sort { SeqSort(Context ctx, long obj) { super(ctx, obj); } } z3-z3-4.8.7/src/api/java/SetSort.java000066400000000000000000000007151356505360400171610ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: SetSort.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * Set sorts. **/ public class SetSort extends Sort { SetSort(Context ctx, long obj) { super(ctx, obj); } SetSort(Context ctx, Sort ty) { super(ctx, Native.mkSetSort(ctx.nCtx(), ty.getNativeObject())); } } z3-z3-4.8.7/src/api/java/Solver.java000066400000000000000000000224631356505360400170340ustar00rootroot00000000000000 /** Copyright (c) 2012-2014 Microsoft Corporation Module Name: Solver.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; import com.microsoft.z3.enumerations.Z3_lbool; /** * Solvers. **/ public class Solver extends Z3Object { /** * A string that describes all available solver parameters. **/ public String getHelp() { return Native.solverGetHelp(getContext().nCtx(), getNativeObject()); } /** * Sets the solver parameters. * * @throws Z3Exception **/ public void setParameters(Params value) { getContext().checkContextMatch(value); Native.solverSetParams(getContext().nCtx(), getNativeObject(), value.getNativeObject()); } /** * Retrieves parameter descriptions for solver. * * @throws Z3Exception **/ public ParamDescrs getParameterDescriptions() { return new ParamDescrs(getContext(), Native.solverGetParamDescrs( getContext().nCtx(), getNativeObject())); } /** * The current number of backtracking points (scopes). * @see #pop * @see #push **/ public int getNumScopes() { return Native .solverGetNumScopes(getContext().nCtx(), getNativeObject()); } /** * Creates a backtracking point. * @see #pop **/ public void push() { Native.solverPush(getContext().nCtx(), getNativeObject()); } /** * Backtracks one backtracking point. * Remarks: . **/ public void pop() { pop(1); } /** * Backtracks {@code n} backtracking points. * Remarks: Note that * an exception is thrown if {@code n} is not smaller than * {@code NumScopes} * @see #push **/ public void pop(int n) { Native.solverPop(getContext().nCtx(), getNativeObject(), n); } /** * Resets the Solver. * Remarks: This removes all assertions from the * solver. **/ public void reset() { Native.solverReset(getContext().nCtx(), getNativeObject()); } /** * Assert a multiple constraints into the solver. * * @throws Z3Exception **/ public void add(BoolExpr... constraints) { getContext().checkContextMatch(constraints); for (BoolExpr a : constraints) { Native.solverAssert(getContext().nCtx(), getNativeObject(), a.getNativeObject()); } } /** * Assert multiple constraints into the solver, and track them (in the * unsat) core * using the Boolean constants in ps. * * Remarks: * This API is an alternative to {@link #check()} with assumptions for * extracting unsat cores. * Both APIs can be used in the same solver. The unsat core will contain a * combination * of the Boolean variables provided using {@code #assertAndTrack} * and the Boolean literals * provided using {@link #check()} with assumptions. **/ public void assertAndTrack(BoolExpr[] constraints, BoolExpr[] ps) { getContext().checkContextMatch(constraints); getContext().checkContextMatch(ps); if (constraints.length != ps.length) { throw new Z3Exception("Argument size mismatch"); } for (int i = 0; i < constraints.length; i++) { Native.solverAssertAndTrack(getContext().nCtx(), getNativeObject(), constraints[i].getNativeObject(), ps[i].getNativeObject()); } } /** * Assert a constraint into the solver, and track it (in the unsat) core * using the Boolean constant p. * * Remarks: * This API is an alternative to {@link #check} with assumptions for * extracting unsat cores. * Both APIs can be used in the same solver. The unsat core will contain a * combination * of the Boolean variables provided using {@link #assertAndTrack} * and the Boolean literals * provided using {@link #check} with assumptions. */ public void assertAndTrack(BoolExpr constraint, BoolExpr p) { getContext().checkContextMatch(constraint); getContext().checkContextMatch(p); Native.solverAssertAndTrack(getContext().nCtx(), getNativeObject(), constraint.getNativeObject(), p.getNativeObject()); } ///

/// Load solver assertions from a file. /// public void fromFile(String file) { Native.solverFromFile(getContext().nCtx(), getNativeObject(), file); } /// /// Load solver assertions from a string. /// public void fromString(String str) { Native.solverFromString(getContext().nCtx(), getNativeObject(), str); } /** * The number of assertions in the solver. * * @throws Z3Exception **/ public int getNumAssertions() { ASTVector assrts = new ASTVector(getContext(), Native.solverGetAssertions(getContext().nCtx(), getNativeObject())); return assrts.size(); } /** * The set of asserted formulas. * * @throws Z3Exception **/ public BoolExpr[] getAssertions() { ASTVector assrts = new ASTVector(getContext(), Native.solverGetAssertions(getContext().nCtx(), getNativeObject())); return assrts.ToBoolExprArray(); } /** * Checks whether the assertions in the solver are consistent or not. * Remarks: * @see #getModel * @see #getUnsatCore * @see #getProof **/ public Status check(Expr... assumptions) { Z3_lbool r; if (assumptions == null) { r = Z3_lbool.fromInt(Native.solverCheck(getContext().nCtx(), getNativeObject())); } else { r = Z3_lbool.fromInt(Native.solverCheckAssumptions(getContext() .nCtx(), getNativeObject(), assumptions.length, AST .arrayToNative(assumptions))); } switch (r) { case Z3_L_TRUE: return Status.SATISFIABLE; case Z3_L_FALSE: return Status.UNSATISFIABLE; default: return Status.UNKNOWN; } } /** * Checks whether the assertions in the solver are consistent or not. * Remarks: * @see #getModel * @see #getUnsatCore * @see #getProof **/ public Status check() { return check((Expr[]) null); } /** * The model of the last {@code Check}. * Remarks: The result is * {@code null} if {@code Check} was not invoked before, if its * results was not {@code SATISFIABLE}, or if model production is not * enabled. * * @throws Z3Exception **/ public Model getModel() { long x = Native.solverGetModel(getContext().nCtx(), getNativeObject()); if (x == 0) { return null; } else { return new Model(getContext(), x); } } /** * The proof of the last {@code Check}. * Remarks: The result is * {@code null} if {@code Check} was not invoked before, if its * results was not {@code UNSATISFIABLE}, or if proof production is * disabled. * * @throws Z3Exception **/ public Expr getProof() { long x = Native.solverGetProof(getContext().nCtx(), getNativeObject()); if (x == 0) { return null; } else { return Expr.create(getContext(), x); } } /** * The unsat core of the last {@code Check}. * Remarks: The unsat core * is a subset of {@code Assertions} The result is empty if * {@code Check} was not invoked before, if its results was not * {@code UNSATISFIABLE}, or if core production is disabled. * * @throws Z3Exception **/ public BoolExpr[] getUnsatCore() { ASTVector core = new ASTVector(getContext(), Native.solverGetUnsatCore(getContext().nCtx(), getNativeObject())); return core.ToBoolExprArray(); } /** * A brief justification of why the last call to {@code Check} returned * {@code UNKNOWN}. **/ public String getReasonUnknown() { return Native.solverGetReasonUnknown(getContext().nCtx(), getNativeObject()); } /** * Create a clone of the current solver with respect to{@code ctx}. */ public Solver translate(Context ctx) { return new Solver(ctx, Native.solverTranslate(getContext().nCtx(), getNativeObject(), ctx.nCtx())); } /** * Solver statistics. * * @throws Z3Exception **/ public Statistics getStatistics() { return new Statistics(getContext(), Native.solverGetStatistics( getContext().nCtx(), getNativeObject())); } /** * A string representation of the solver. **/ @Override public String toString() { return Native .solverToString(getContext().nCtx(), getNativeObject()); } Solver(Context ctx, long obj) { super(ctx, obj); } @Override void incRef() { Native.solverIncRef(getContext().nCtx(), getNativeObject()); } @Override void addToReferenceQueue() { getContext().getSolverDRQ().storeReference(getContext(), this); } } z3-z3-4.8.7/src/api/java/SolverDecRefQueue.java000066400000000000000000000006721356505360400211100ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: SolverDecRefQueue.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; class SolverDecRefQueue extends IDecRefQueue { public SolverDecRefQueue() { super(); } @Override protected void decRef(Context ctx, long obj) { Native.solverDecRef(ctx.nCtx(), obj); } } z3-z3-4.8.7/src/api/java/Sort.java000066400000000000000000000071761356505360400165150ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: Sort.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; import com.microsoft.z3.enumerations.Z3_ast_kind; import com.microsoft.z3.enumerations.Z3_sort_kind; /** * The Sort class implements type information for ASTs. **/ public class Sort extends AST { /** * Equality operator for objects of type Sort. **/ @Override public boolean equals(Object o) { if (o == this) return true; if (!(o instanceof Sort)) return false; Sort other = (Sort) o; return (getContext().nCtx() == other.getContext().nCtx()) && (Native.isEqSort(getContext().nCtx(), getNativeObject(), other.getNativeObject())); } /** * Hash code generation for Sorts * * @return A hash code **/ public int hashCode() { return super.hashCode(); } /** * Returns a unique identifier for the sort. **/ public int getId() { return Native.getSortId(getContext().nCtx(), getNativeObject()); } /** * The kind of the sort. **/ public Z3_sort_kind getSortKind() { return Z3_sort_kind.fromInt(Native.getSortKind(getContext().nCtx(), getNativeObject())); } /** * The name of the sort **/ public Symbol getName() { return Symbol.create(getContext(), Native.getSortName(getContext().nCtx(), getNativeObject())); } /** * A string representation of the sort. **/ @Override public String toString() { return Native.sortToString(getContext().nCtx(), getNativeObject()); } /** * Translates (copies) the sort to the Context {@code ctx}. * * @param ctx A context * * @return A copy of the sort which is associated with {@code ctx} * @throws Z3Exception on error **/ public Sort translate(Context ctx) { return (Sort) super.translate(ctx); } /** * Sort constructor **/ Sort(Context ctx, long obj) { super(ctx, obj); } @Override void checkNativeObject(long obj) { if (Native.getAstKind(getContext().nCtx(), obj) != Z3_ast_kind.Z3_SORT_AST .toInt()) throw new Z3Exception("Underlying object is not a sort"); super.checkNativeObject(obj); } static Sort create(Context ctx, long obj) { Z3_sort_kind sk = Z3_sort_kind.fromInt(Native.getSortKind(ctx.nCtx(), obj)); switch (sk) { case Z3_ARRAY_SORT: return new ArraySort(ctx, obj); case Z3_BOOL_SORT: return new BoolSort(ctx, obj); case Z3_BV_SORT: return new BitVecSort(ctx, obj); case Z3_DATATYPE_SORT: return new DatatypeSort(ctx, obj); case Z3_INT_SORT: return new IntSort(ctx, obj); case Z3_REAL_SORT: return new RealSort(ctx, obj); case Z3_UNINTERPRETED_SORT: return new UninterpretedSort(ctx, obj); case Z3_FINITE_DOMAIN_SORT: return new FiniteDomainSort(ctx, obj); case Z3_RELATION_SORT: return new RelationSort(ctx, obj); case Z3_FLOATING_POINT_SORT: return new FPSort(ctx, obj); case Z3_ROUNDING_MODE_SORT: return new FPRMSort(ctx, obj); case Z3_SEQ_SORT: return new SeqSort(ctx, obj); case Z3_RE_SORT: return new ReSort(ctx, obj); default: throw new Z3Exception("Unknown sort kind"); } } } z3-z3-4.8.7/src/api/java/Statistics.java000066400000000000000000000110031356505360400177000ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: Statistics.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * Objects of this class track statistical information about solvers. **/ public class Statistics extends Z3Object { /** * Statistical data is organized into pairs of [Key, Entry], where every * Entry is either a {@code DoubleEntry} or a {@code UIntEntry} **/ public class Entry { /** * The key of the entry. **/ public String Key; /** * The uint-value of the entry. **/ public int getUIntValue() { return m_int; } /** * The double-value of the entry. **/ public double getDoubleValue() { return m_double; } /** * True if the entry is uint-valued. **/ public boolean isUInt() { return m_is_int; } /** * True if the entry is double-valued. **/ public boolean isDouble() { return m_is_double; } /** * The string representation of the entry's value. * * @throws Z3Exception **/ public String getValueString() { if (isUInt()) { return Integer.toString(m_int); } else if (isDouble()) { return Double.toString(m_double); } else { throw new Z3Exception("Unknown statistical entry type"); } } /** * The string representation of the Entry. **/ @Override public String toString() { return Key + ": " + getValueString(); } private boolean m_is_int = false; private boolean m_is_double = false; private int m_int = 0; private double m_double = 0.0; Entry(String k, int v) { Key = k; m_is_int = true; m_int = v; } Entry(String k, double v) { Key = k; m_is_double = true; m_double = v; } } /** * A string representation of the statistical data. **/ @Override public String toString() { return Native.statsToString(getContext().nCtx(), getNativeObject()); } /** * The number of statistical data. **/ public int size() { return Native.statsSize(getContext().nCtx(), getNativeObject()); } /** * The data entries. * * @throws Z3Exception **/ public Entry[] getEntries() { int n = size(); Entry[] res = new Entry[n]; for (int i = 0; i < n; i++) { Entry e; String k = Native.statsGetKey(getContext().nCtx(), getNativeObject(), i); if (Native.statsIsUint(getContext().nCtx(), getNativeObject(), i)) { e = new Entry(k, Native.statsGetUintValue(getContext().nCtx(), getNativeObject(), i)); } else if (Native.statsIsDouble(getContext().nCtx(), getNativeObject(), i)) { e = new Entry(k, Native.statsGetDoubleValue(getContext().nCtx(), getNativeObject(), i)); } else { throw new Z3Exception("Unknown data entry value"); } res[i] = e; } return res; } /** * The statistical counters. **/ public String[] getKeys() { int n = size(); String[] res = new String[n]; for (int i = 0; i < n; i++) res[i] = Native.statsGetKey(getContext().nCtx(), getNativeObject(), i); return res; } /** * The value of a particular statistical counter. * Remarks: Returns null if * the key is unknown. * * @throws Z3Exception **/ public Entry get(String key) { int n = size(); Entry[] es = getEntries(); for (int i = 0; i < n; i++) { if (es[i].Key.equals(key)) { return es[i]; } } return null; } Statistics(Context ctx, long obj) { super(ctx, obj); } @Override void incRef() { getContext().getStatisticsDRQ().storeReference(getContext(), this); } @Override void addToReferenceQueue() { Native.statsIncRef(getContext().nCtx(), getNativeObject()); } } z3-z3-4.8.7/src/api/java/StatisticsDecRefQueue.java000066400000000000000000000007261356505360400217700ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: StatisticsDecRefQueue.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; class StatisticsDecRefQueue extends IDecRefQueue { public StatisticsDecRefQueue() { super(); } @Override protected void decRef(Context ctx, long obj) { Native.statsDecRef(ctx.nCtx(), obj); } } z3-z3-4.8.7/src/api/java/Status.java000066400000000000000000000014351356505360400170410ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: Status.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * Status values. **/ public enum Status { // / Used to signify an unsatisfiable status. UNSATISFIABLE(-1), // / Used to signify an unknown status. UNKNOWN(0), // / Used to signify a satisfiable status. SATISFIABLE(1); private final int intValue; Status(int v) { this.intValue = v; } public static Status fromInt(int v) { for (Status k : values()) if (k.intValue == v) return k; return values()[0]; } public final int toInt() { return this.intValue; } } z3-z3-4.8.7/src/api/java/StringSymbol.java000066400000000000000000000020761356505360400202140ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: StringSymbol.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; import com.microsoft.z3.enumerations.Z3_symbol_kind; /** * Named symbols **/ public class StringSymbol extends Symbol { /** * The string value of the symbol. * Remarks: Throws an exception if the * symbol is not of string kind. **/ public String getString() { return Native.getSymbolString(getContext().nCtx(), getNativeObject()); } StringSymbol(Context ctx, long obj) { super(ctx, obj); } StringSymbol(Context ctx, String s) { super(ctx, Native.mkStringSymbol(ctx.nCtx(), s)); } @Override void checkNativeObject(long obj) { if (Native.getSymbolKind(getContext().nCtx(), obj) != Z3_symbol_kind.Z3_STRING_SYMBOL .toInt()) { throw new Z3Exception("Symbol is not of String kind"); } super.checkNativeObject(obj); } } z3-z3-4.8.7/src/api/java/Symbol.java000066400000000000000000000044151356505360400170240ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: Symbol.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; import com.microsoft.z3.enumerations.Z3_symbol_kind; /** * Symbols are used to name several term and type constructors. **/ public class Symbol extends Z3Object { /** * The kind of the symbol (int or string) **/ protected Z3_symbol_kind getKind() { return Z3_symbol_kind.fromInt(Native.getSymbolKind(getContext().nCtx(), getNativeObject())); } /** * Indicates whether the symbol is of Int kind **/ public boolean isIntSymbol() { return getKind() == Z3_symbol_kind.Z3_INT_SYMBOL; } /** * Indicates whether the symbol is of string kind. **/ public boolean isStringSymbol() { return getKind() == Z3_symbol_kind.Z3_STRING_SYMBOL; } @Override public boolean equals(Object o) { if (o == this) return true; if (!(o instanceof Symbol)) return false; Symbol other = (Symbol) o; return this.getNativeObject() == other.getNativeObject(); } /** * A string representation of the symbol. **/ @Override public String toString() { if (isIntSymbol()) { return Integer.toString(((IntSymbol) this).getInt()); } else if (isStringSymbol()) { return ((StringSymbol) this).getString(); } else { return "Z3Exception: Unknown symbol kind encountered."; } } /** * Symbol constructor **/ protected Symbol(Context ctx, long obj) { super(ctx, obj); } @Override void incRef() { // Symbol does not require tracking. } @Override void addToReferenceQueue() { // Symbol does not require tracking. } static Symbol create(Context ctx, long obj) { switch (Z3_symbol_kind.fromInt(Native.getSymbolKind(ctx.nCtx(), obj))) { case Z3_INT_SYMBOL: return new IntSymbol(ctx, obj); case Z3_STRING_SYMBOL: return new StringSymbol(ctx, obj); default: throw new Z3Exception("Unknown symbol kind encountered"); } } } z3-z3-4.8.7/src/api/java/Tactic.java000066400000000000000000000047571356505360400167770ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: Tactic.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * Tactics are the basic building block for creating custom solvers for specific * problem domains. The complete list of tactics may be obtained using * {@code Context.NumTactics} and {@code Context.TacticNames}. It may * also be obtained using the command {@code (help-tactic)} in the SMT 2.0 * front-end. **/ public class Tactic extends Z3Object { /** * A string containing a description of parameters accepted by the tactic. **/ public String getHelp() { return Native.tacticGetHelp(getContext().nCtx(), getNativeObject()); } /** * Retrieves parameter descriptions for Tactics. * @throws Z3Exception **/ public ParamDescrs getParameterDescriptions() { return new ParamDescrs(getContext(), Native.tacticGetParamDescrs(getContext() .nCtx(), getNativeObject())); } /** * Execute the tactic over the goal. * @throws Z3Exception **/ public ApplyResult apply(Goal g) { return apply(g, null); } /** * Execute the tactic over the goal. * @throws Z3Exception **/ public ApplyResult apply(Goal g, Params p) { getContext().checkContextMatch(g); if (p == null) return new ApplyResult(getContext(), Native.tacticApply(getContext() .nCtx(), getNativeObject(), g.getNativeObject())); else { getContext().checkContextMatch(p); return new ApplyResult(getContext(), Native.tacticApplyEx(getContext().nCtx(), getNativeObject(), g.getNativeObject(), p.getNativeObject())); } } /** * Creates a solver that is implemented using the given tactic. * @see Context#mkSolver(Tactic) * @throws Z3Exception **/ public Solver getSolver() { return getContext().mkSolver(this); } Tactic(Context ctx, long obj) { super(ctx, obj); } Tactic(Context ctx, String name) { super(ctx, Native.mkTactic(ctx.nCtx(), name)); } @Override void incRef() { Native.tacticIncRef(getContext().nCtx(), getNativeObject()); } @Override void addToReferenceQueue() { getContext().getTacticDRQ().storeReference(getContext(), this); } } z3-z3-4.8.7/src/api/java/TacticDecRefQueue.java000066400000000000000000000007131356505360400210410ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: TacticDecRefQueue.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; class TacticDecRefQueue extends IDecRefQueue { public TacticDecRefQueue() { super(); } @Override protected void decRef(Context ctx, long obj) { Native.tacticDecRef(ctx.nCtx(), obj); } } z3-z3-4.8.7/src/api/java/TupleSort.java000066400000000000000000000027201356505360400175150ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: TupleSort.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * Tuple sorts. **/ public class TupleSort extends Sort { /** * The constructor function of the tuple. * @throws Z3Exception **/ public FuncDecl mkDecl() { return new FuncDecl(getContext(), Native.getTupleSortMkDecl(getContext() .nCtx(), getNativeObject())); } /** * The number of fields in the tuple. **/ public int getNumFields() { return Native.getTupleSortNumFields(getContext().nCtx(), getNativeObject()); } /** * The field declarations. * @throws Z3Exception **/ public FuncDecl[] getFieldDecls() { int n = getNumFields(); FuncDecl[] res = new FuncDecl[n]; for (int i = 0; i < n; i++) res[i] = new FuncDecl(getContext(), Native.getTupleSortFieldDecl( getContext().nCtx(), getNativeObject(), i)); return res; } TupleSort(Context ctx, Symbol name, int numFields, Symbol[] fieldNames, Sort[] fieldSorts) { super(ctx, Native.mkTupleSort(ctx.nCtx(), name.getNativeObject(), numFields, Symbol.arrayToNative(fieldNames), AST.arrayToNative(fieldSorts), new Native.LongPtr(), new long[numFields])); } }; z3-z3-4.8.7/src/api/java/UninterpretedSort.java000066400000000000000000000010101356505360400212430ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: UninterpretedSort.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * Uninterpreted Sorts **/ public class UninterpretedSort extends Sort { UninterpretedSort(Context ctx, long obj) { super(ctx, obj); } UninterpretedSort(Context ctx, Symbol s) { super(ctx, Native.mkUninterpretedSort(ctx.nCtx(), s.getNativeObject())); } } z3-z3-4.8.7/src/api/java/Version.java000066400000000000000000000042551356505360400172060ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: Version.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * Version information. * Remarks: Note that this class is static. **/ public class Version { /** * The major version **/ public static int getMajor() { Native.IntPtr major = new Native.IntPtr(), minor = new Native.IntPtr(), build = new Native.IntPtr(), revision = new Native.IntPtr(); Native.getVersion(major, minor, build, revision); return major.value; } /** * The minor version **/ public static int getMinor() { Native.IntPtr major = new Native.IntPtr(), minor = new Native.IntPtr(), build = new Native.IntPtr(), revision = new Native.IntPtr(); Native.getVersion(major, minor, build, revision); return minor.value; } /** * The build version **/ public static int getBuild() { Native.IntPtr major = new Native.IntPtr(), minor = new Native.IntPtr(), build = new Native.IntPtr(), revision = new Native.IntPtr(); Native.getVersion(major, minor, build, revision); return build.value; } /** * The revision **/ public static int getRevision() { Native.IntPtr major = new Native.IntPtr(), minor = new Native.IntPtr(), build = new Native.IntPtr(), revision = new Native.IntPtr(); Native.getVersion(major, minor, build, revision); return revision.value; } /** * A full version string **/ public static String getFullVersion() { return Native.getFullVersion(); } /** * A string representation of the version information. **/ public static String getString() { Native.IntPtr major = new Native.IntPtr(), minor = new Native.IntPtr(), build = new Native.IntPtr(), revision = new Native.IntPtr(); Native.getVersion(major, minor, build, revision); return Integer.toString(major.value) + "." + Integer.toString(minor.value) + "." + Integer.toString(build.value) + "." + Integer.toString(revision.value); } } z3-z3-4.8.7/src/api/java/Z3Exception.java000066400000000000000000000012761356505360400177340ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: Z3Exception.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * The exception base class for error reporting from Z3 **/ @SuppressWarnings("serial") public class Z3Exception extends RuntimeException { /** * Constructor. **/ public Z3Exception() { super(); } /** * Constructor. **/ public Z3Exception(String message) { super(message); } /** * Constructor. **/ public Z3Exception(String message, Exception inner) { super(message, inner); } } z3-z3-4.8.7/src/api/java/Z3Object.java000066400000000000000000000033201356505360400171740ustar00rootroot00000000000000/** Copyright (c) 2012-2014 Microsoft Corporation Module Name: Z3Object.java Abstract: Author: @author Christoph Wintersteiger (cwinter) 2012-03-15 Notes: **/ package com.microsoft.z3; /** * Internal base class for interfacing with native Z3 objects. Should not be * used externally. **/ public abstract class Z3Object { private final Context m_ctx; private final long m_n_obj; Z3Object(Context ctx, long obj) { m_ctx = ctx; checkNativeObject(obj); m_n_obj = obj; incRef(); addToReferenceQueue(); } /** * Add to ReferenceQueue for tracking reachability on the object and * decreasing the reference count when the object is no longer reachable. */ abstract void addToReferenceQueue(); /** * Increment reference count on {@code this}. */ abstract void incRef(); /** * This function is provided for overriding, and a child class * can insert consistency checks on {@code obj}. * * @param obj Z3 native object. */ void checkNativeObject(long obj) {} long getNativeObject() { return m_n_obj; } static long getNativeObject(Z3Object s) { if (s == null) return 0; return s.getNativeObject(); } Context getContext() { return m_ctx; } static long[] arrayToNative(Z3Object[] a) { if (a == null) return null; long[] an = new long[a.length]; for (int i = 0; i < a.length; i++) an[i] = (a[i] == null) ? 0 : a[i].getNativeObject(); return an; } static int arrayLength(Z3Object[] a) { return (a == null) ? 0 : a.length; } } z3-z3-4.8.7/src/api/java/manifest000066400000000000000000000001021356505360400164320ustar00rootroot00000000000000Manifest-Version: 1.0 Created-By: 4.3.2 (Microsoft Research LTD.) z3-z3-4.8.7/src/api/ml/000077500000000000000000000000001356505360400143775ustar00rootroot00000000000000z3-z3-4.8.7/src/api/ml/META.in000066400000000000000000000005501356505360400154550ustar00rootroot00000000000000# META file for the "z3" package: version = "@VERSION@" description = "Z3 Theorem Prover (OCaml API)" requires = "zarith threads" archive(byte) = "z3ml.cma" archive(native) = "z3ml.cmxa" archive(byte,plugin) = "z3ml.cma" archive(native,plugin) = "z3ml.cmxs" archive(byte,toploop) = "z3ml.cma" archive(native,toploop) = "z3ml.cmxa" linkopts = "-cclib -lstdc++" z3-z3-4.8.7/src/api/ml/README000066400000000000000000000013011356505360400152520ustar00rootroot00000000000000This is the new ML API introduced with Z3 4.4. For the legacy bindings, please refer to previous releases of Z3. On Windows, there are no less than four different ports of OCaml. The Z3 build system assumes that either the win32 or the win64 port is installed. This means that OCaml will use `cl' as the underlying C compiler and not the cygwin or mingw compilers. OCamlfind: When ocamlfind is found, the `install' target will install the Z3 OCaml bindings into the ocamlfind site-lib directory. The installed package is linked against the (dynamic) libz3 and it adds $(PREFIX)/lib to the library include paths. On Windows, there is no $(PREFIX), so the build directory is used instead (see META.in). z3-z3-4.8.7/src/api/ml/z3.ml000066400000000000000000002506751356505360400153040ustar00rootroot00000000000000(** The Z3 ML/OCaml Interface. Copyright (C) 2012 Microsoft Corporation @author CM Wintersteiger (cwinter) 2012-12-17 *) open Z3enums exception Error of string let _ = Callback.register_exception "Z3EXCEPTION" (Error "") type context = Z3native.context module Log = struct let open_ filename = lbool_of_int (Z3native.open_log filename) = L_TRUE let close = Z3native.close_log let append = Z3native.append_log end module Version = struct let (major, minor, build, revision) = Z3native.get_version () let full_version : string = Z3native.get_full_version() let to_string = string_of_int major ^ "." ^ string_of_int minor ^ "." ^ string_of_int build ^ "." ^ string_of_int revision end let mk_list f n = let rec mk_list' i accu = if i >= n then List.rev accu else mk_list' (i + 1) ((f i)::accu) in mk_list' 0 [] let check_int32 v = v = Int32.to_int (Int32.of_int v) let mk_int_expr ctx v ty = if not (check_int32 v) then Z3native.mk_numeral ctx (string_of_int v) ty else Z3native.mk_int ctx v ty let mk_context (settings:(string * string) list) = let cfg = Z3native.mk_config () in let f e = Z3native.set_param_value cfg (fst e) (snd e) in List.iter f settings; let res = Z3native.mk_context_rc cfg in Z3native.del_config cfg; Z3native.set_ast_print_mode res (Z3enums.int_of_ast_print_mode PRINT_SMTLIB2_COMPLIANT); Z3native.set_internal_error_handler res; res module Symbol = struct type symbol = Z3native.symbol let gc = Z3native.context_of_symbol let kind o = symbol_kind_of_int (Z3native.get_symbol_kind (gc o) o) let is_int_symbol o = kind o = INT_SYMBOL let is_string_symbol o = kind o = STRING_SYMBOL let get_int o = Z3native.get_symbol_int (gc o) o let get_string o = Z3native.get_symbol_string (gc o) o let to_string o = match kind o with | INT_SYMBOL -> string_of_int (Z3native.get_symbol_int (gc o) o) | STRING_SYMBOL -> Z3native.get_symbol_string (gc o) o let mk_int = Z3native.mk_int_symbol let mk_string = Z3native.mk_string_symbol let mk_ints ctx names = List.map (mk_int ctx) names let mk_strings ctx names = List.map (mk_string ctx) names end module rec AST : sig type ast = Z3native.ast val gc : ast -> context module ASTVector : sig type ast_vector = Z3native.ast_vector val mk_ast_vector : context -> ast_vector val get_size : ast_vector -> int val get : ast_vector -> int -> ast val set : ast_vector -> int -> ast -> unit val resize : ast_vector -> int -> unit val push : ast_vector -> ast -> unit val translate : ast_vector -> context -> ast_vector val to_list : ast_vector -> ast list val to_expr_list : ast_vector -> Expr.expr list val to_string : ast_vector -> string end module ASTMap : sig type ast_map = Z3native.ast_map val mk_ast_map : context -> ast_map val contains : ast_map -> ast -> bool val find : ast_map -> ast -> ast val insert : ast_map -> ast -> ast -> unit val erase : ast_map -> ast -> unit val reset : ast_map -> unit val get_size : ast_map -> int val get_keys : ast_map -> ast list val to_string : ast_map -> string end val hash : ast -> int val get_id : ast -> int val get_ast_kind : ast -> Z3enums.ast_kind val is_expr : ast -> bool val is_app : ast -> bool val is_var : ast -> bool val is_quantifier : ast -> bool val is_sort : ast -> bool val is_func_decl : ast -> bool val to_string : ast -> string val to_sexpr : ast -> string val equal : ast -> ast -> bool val compare : ast -> ast -> int val translate : ast -> context -> ast end = struct type ast = Z3native.ast let gc = Z3native.context_of_ast module ASTVector = struct type ast_vector = Z3native.ast_vector let gc = Z3native.context_of_ast_vector let mk_ast_vector = Z3native.mk_ast_vector let get_size (x:ast_vector) = Z3native.ast_vector_size (gc x) x let get (x:ast_vector) (i:int) = Z3native.ast_vector_get (gc x) x i let set (x:ast_vector) (i:int) (value:ast) = Z3native.ast_vector_set (gc x) x i value let resize (x:ast_vector) (new_size:int) = Z3native.ast_vector_resize (gc x) x new_size let push (x:ast_vector) (a:ast) = Z3native.ast_vector_push (gc x) x a let translate (x:ast_vector) (to_ctx:context) = Z3native.ast_vector_translate (gc x) x to_ctx let to_list (x:ast_vector) = let xs = get_size x in let f i = get x i in mk_list f xs let to_expr_list (x:ast_vector) = let xs = get_size x in let f i = get x i in mk_list f xs let to_string x = Z3native.ast_vector_to_string (gc x) x end module ASTMap = struct type ast_map = Z3native.ast_map let gc = Z3native.context_of_ast_map let mk_ast_map = Z3native.mk_ast_map let contains (x:ast_map) (key:ast) = Z3native.ast_map_contains (gc x) x key let find (x:ast_map) (key:ast) = Z3native.ast_map_find (gc x) x key let insert (x:ast_map) (key:ast) (value:ast) = Z3native.ast_map_insert (gc x) x key value let erase (x:ast_map) (key:ast) = Z3native.ast_map_erase (gc x) x key let reset (x:ast_map) = Z3native.ast_map_reset (gc x) x let get_size (x:ast_map) = Z3native.ast_map_size (gc x) x let get_keys (x:ast_map) = let av = Z3native.ast_map_keys (gc x) x in ASTVector.to_list av let to_string (x:ast_map) = Z3native.ast_map_to_string (gc x) x end let hash (x:ast) = Z3native.get_ast_hash (gc x) x let get_id (x:ast) = Z3native.get_ast_id (gc x) x let get_ast_kind (x:ast) = ast_kind_of_int (Z3native.get_ast_kind (gc x) x) let is_expr (x:ast) = match get_ast_kind x with | APP_AST | NUMERAL_AST | QUANTIFIER_AST | VAR_AST -> true | _ -> false let is_app (x:ast) = get_ast_kind x = APP_AST let is_var (x:ast) = get_ast_kind x = VAR_AST let is_quantifier (x:ast) = get_ast_kind x = QUANTIFIER_AST let is_sort (x:ast) = get_ast_kind x = SORT_AST let is_func_decl (x:ast) = get_ast_kind x = FUNC_DECL_AST let to_string (x:ast) = Z3native.ast_to_string (gc x) x let to_sexpr (x:ast) = Z3native.ast_to_string (gc x) x (* The built-in equality uses the custom operations of the C layer *) let equal = (=) (* The standard comparison uses the custom operations of the C layer *) let compare = Pervasives.compare let translate (x:ast) (to_ctx:context) = if gc x = to_ctx then x else Z3native.translate (gc x) x to_ctx end and Sort : sig type sort = AST.ast val gc : sort -> context val equal : sort -> sort -> bool val get_id : sort -> int val get_sort_kind : sort -> Z3enums.sort_kind val get_name : sort -> Symbol.symbol val to_string : sort -> string val mk_uninterpreted : context -> Symbol.symbol -> sort val mk_uninterpreted_s : context -> string -> sort end = struct type sort = Z3native.sort let gc = Z3native.context_of_ast let equal a b = (a = b) || (gc a = gc b && Z3native.is_eq_sort (gc a) a b) let get_id (x:sort) = Z3native.get_sort_id (gc x) x let get_sort_kind (x:sort) = sort_kind_of_int (Z3native.get_sort_kind (gc x) x) let get_name (x:sort) = Z3native.get_sort_name (gc x) x let to_string (x:sort) = Z3native.sort_to_string (gc x) x let mk_uninterpreted = Z3native.mk_uninterpreted_sort let mk_uninterpreted_s (ctx:context) (s:string) = mk_uninterpreted ctx (Symbol.mk_string ctx s) end and FuncDecl : sig type func_decl = Z3native.func_decl val gc : func_decl -> context module Parameter : sig type parameter = P_Int of int | P_Dbl of float | P_Sym of Symbol.symbol | P_Srt of Sort.sort | P_Ast of AST.ast | P_Fdl of func_decl | P_Rat of string val get_kind : parameter -> Z3enums.parameter_kind val get_int : parameter -> int val get_float : parameter -> float val get_symbol : parameter -> Symbol.symbol val get_sort : parameter -> Sort.sort val get_ast : parameter -> AST.ast val get_func_decl : parameter -> func_decl val get_rational : parameter -> string end val mk_func_decl : context -> Symbol.symbol -> Sort.sort list -> Sort.sort -> func_decl val mk_func_decl_s : context -> string -> Sort.sort list -> Sort.sort -> func_decl val mk_fresh_func_decl : context -> string -> Sort.sort list -> Sort.sort -> func_decl val mk_const_decl : context -> Symbol.symbol -> Sort.sort -> func_decl val mk_const_decl_s : context -> string -> Sort.sort -> func_decl val mk_fresh_const_decl : context -> string -> Sort.sort -> func_decl val equal : func_decl -> func_decl -> bool val to_string : func_decl -> string val get_id : func_decl -> int val get_arity : func_decl -> int val get_domain_size : func_decl -> int val get_domain : func_decl -> Sort.sort list val get_range : func_decl -> Sort.sort val get_decl_kind : func_decl -> Z3enums.decl_kind val get_name : func_decl -> Symbol.symbol val get_num_parameters : func_decl -> int val get_parameters : func_decl -> Parameter.parameter list val apply : func_decl -> Expr.expr list -> Expr.expr end = struct type func_decl = AST.ast let gc = Z3native.context_of_ast module Parameter = struct type parameter = | P_Int of int | P_Dbl of float | P_Sym of Symbol.symbol | P_Srt of Sort.sort | P_Ast of AST.ast | P_Fdl of func_decl | P_Rat of string let get_kind = function | P_Int _ -> PARAMETER_INT | P_Dbl _ -> PARAMETER_DOUBLE | P_Sym _ -> PARAMETER_SYMBOL | P_Srt _ -> PARAMETER_SORT | P_Ast _ -> PARAMETER_AST | P_Fdl _ -> PARAMETER_FUNC_DECL | P_Rat _ -> PARAMETER_RATIONAL let get_int = function | P_Int x -> x | _ -> raise (Error "parameter is not an int") let get_float = function | P_Dbl x -> x | _ -> raise (Error "parameter is not a float") let get_symbol = function | P_Sym x -> x | _ -> raise (Error "parameter is not a symbol") let get_sort = function | P_Srt x -> x | _ -> raise (Error "parameter is not a sort") let get_ast = function | P_Ast x -> x | _ -> raise (Error "parameter is not an ast") let get_func_decl = function | P_Fdl x -> x | _ -> raise (Error "parameter is not a func_decl") let get_rational = function | P_Rat x -> x | _ -> raise (Error "parameter is not a rational string") end let mk_func_decl (ctx:context) (name:Symbol.symbol) (domain:Sort.sort list) (range:Sort.sort) = Z3native.mk_func_decl ctx name (List.length domain) domain range let mk_func_decl_s (ctx:context) (name:string) (domain:Sort.sort list) (range:Sort.sort) = mk_func_decl ctx (Symbol.mk_string ctx name) domain range let mk_fresh_func_decl (ctx:context) (prefix:string) (domain:Sort.sort list) (range:Sort.sort) = Z3native.mk_fresh_func_decl ctx prefix (List.length domain) domain range let mk_const_decl (ctx:context) (name:Symbol.symbol) (range:Sort.sort) = Z3native.mk_func_decl ctx name 0 [] range let mk_const_decl_s (ctx:context) (name:string) (range:Sort.sort) = Z3native.mk_func_decl ctx (Symbol.mk_string ctx name) 0 [] range let mk_fresh_const_decl (ctx:context) (prefix:string) (range:Sort.sort) = Z3native.mk_fresh_func_decl ctx prefix 0 [] range let equal a b = (a = b) || (gc a = gc b && Z3native.is_eq_func_decl (gc a) a b) let to_string (x:func_decl) = Z3native.func_decl_to_string (gc x) x let get_id (x:func_decl) = Z3native.get_func_decl_id (gc x) x let get_arity (x:func_decl) = Z3native.get_arity (gc x) x let get_domain_size (x:func_decl) = Z3native.get_domain_size (gc x) x let get_domain (x:func_decl) = let n = get_domain_size x in let f i = Z3native.get_domain (gc x) x i in mk_list f n let get_range (x:func_decl) = Z3native.get_range (gc x) x let get_decl_kind (x:func_decl) = decl_kind_of_int (Z3native.get_decl_kind (gc x) x) let get_name (x:func_decl) = Z3native.get_decl_name (gc x) x let get_num_parameters (x:func_decl) = Z3native.get_decl_num_parameters (gc x) x let get_parameters (x:func_decl) = let n = get_num_parameters x in let f i = match parameter_kind_of_int (Z3native.get_decl_parameter_kind (gc x) x i) with | PARAMETER_INT -> Parameter.P_Int (Z3native.get_decl_int_parameter (gc x) x i) | PARAMETER_DOUBLE -> Parameter.P_Dbl (Z3native.get_decl_double_parameter (gc x) x i) | PARAMETER_SYMBOL-> Parameter.P_Sym (Z3native.get_decl_symbol_parameter (gc x) x i) | PARAMETER_SORT -> Parameter.P_Srt (Z3native.get_decl_sort_parameter (gc x) x i) | PARAMETER_AST -> Parameter.P_Ast (Z3native.get_decl_ast_parameter (gc x) x i) | PARAMETER_FUNC_DECL -> Parameter.P_Fdl (Z3native.get_decl_func_decl_parameter (gc x) x i) | PARAMETER_RATIONAL -> Parameter.P_Rat (Z3native.get_decl_rational_parameter (gc x) x i) in mk_list f n let apply (x:func_decl) (args:Expr.expr list) = Expr.expr_of_func_app (gc x) x args end and Params: sig type params = Z3native.params module ParamDescrs : sig type param_descrs = Z3native.param_descrs val validate : param_descrs -> params -> unit val get_kind : param_descrs -> Symbol.symbol -> Z3enums.param_kind val get_names : param_descrs -> Symbol.symbol list val get_size : param_descrs -> int val to_string : param_descrs -> string end val add_bool : params -> Symbol.symbol -> bool -> unit val add_int : params -> Symbol.symbol -> int -> unit val add_float : params -> Symbol.symbol -> float -> unit val add_symbol : params -> Symbol.symbol -> Symbol.symbol -> unit val mk_params : context -> params val to_string : params -> string val update_param_value : context -> string -> string -> unit val set_print_mode : context -> Z3enums.ast_print_mode -> unit end = struct type params = Z3native.params let gc = Z3native.context_of_params module ParamDescrs = struct type param_descrs = Z3native.param_descrs let gc = Z3native.context_of_param_descrs let validate (x:param_descrs) (p:params) = Z3native.params_validate (gc x) p x let get_kind (x:param_descrs) (name:Symbol.symbol) = param_kind_of_int (Z3native.param_descrs_get_kind (gc x) x name) let get_names (x:param_descrs) = let n = Z3native.param_descrs_size (gc x) x in let f i = Z3native.param_descrs_get_name (gc x) x i in mk_list f n let get_size (x:param_descrs) = Z3native.param_descrs_size (gc x) x let to_string (x:param_descrs) = Z3native.param_descrs_to_string (gc x) x end let add_bool (x:params) (name:Symbol.symbol) (value:bool) = Z3native.params_set_bool (gc x) x name value let add_int (x:params) (name:Symbol.symbol) (value:int) = Z3native.params_set_uint (gc x) x name value let add_float (x:params) (name:Symbol.symbol) (value:float) = Z3native.params_set_double (gc x) x name value let add_symbol (x:params) (name:Symbol.symbol) (value:Symbol.symbol) = Z3native.params_set_symbol (gc x) x name value let mk_params (ctx:context) = Z3native.mk_params ctx let to_string (x:params) = Z3native.params_to_string (gc x) x let update_param_value (ctx:context) (id:string) (value:string) = Z3native.update_param_value ctx id value let set_print_mode (ctx:context) (value:ast_print_mode) = Z3native.set_ast_print_mode ctx (int_of_ast_print_mode value) end (** General expressions (terms) *) and Expr : sig type expr = AST.ast val gc : expr -> context val ast_of_expr : expr -> AST.ast val expr_of_ast : AST.ast -> expr val expr_of_func_app : context -> FuncDecl.func_decl -> expr list -> expr val simplify : expr -> Params.params option -> expr val get_simplify_help : context -> string val get_simplify_parameter_descrs : context -> Params.ParamDescrs.param_descrs val get_func_decl : expr -> FuncDecl.func_decl val get_num_args : expr -> int val get_args : expr -> expr list val update : expr -> expr list -> expr val substitute : expr -> expr list -> expr list -> expr val substitute_one : expr -> expr -> expr -> expr val substitute_vars : expr -> expr list -> expr val translate : expr -> context -> expr val to_string : expr -> string val is_numeral : expr -> bool val is_well_sorted : expr -> bool val get_sort : expr -> Sort.sort val is_const : expr -> bool val mk_const : context -> Symbol.symbol -> Sort.sort -> expr val mk_const_s : context -> string -> Sort.sort -> expr val mk_const_f : context -> FuncDecl.func_decl -> expr val mk_fresh_const : context -> string -> Sort.sort -> expr val mk_app : context -> FuncDecl.func_decl -> expr list -> expr val mk_numeral_string : context -> string -> Sort.sort -> expr val mk_numeral_int : context -> int -> Sort.sort -> expr val equal : expr -> expr -> bool val compare : expr -> expr -> int end = struct type expr = AST.ast let gc = Z3native.context_of_ast let expr_of_ast a = let q = Z3enums.ast_kind_of_int (Z3native.get_ast_kind (gc a) a) in if q <> Z3enums.APP_AST && q <> VAR_AST && q <> QUANTIFIER_AST && q <> NUMERAL_AST then raise (Error "Invalid coercion") else a let ast_of_expr e = e let expr_of_func_app ctx f args = Z3native.mk_app ctx f (List.length args) args let simplify (x:expr) (p:Params.params option) = match p with | None -> Z3native.simplify (gc x) x | Some pp -> Z3native.simplify_ex (gc x) x pp let get_simplify_help = Z3native.simplify_get_help let get_simplify_parameter_descrs = Z3native.simplify_get_param_descrs let get_func_decl (x:expr) = Z3native.get_app_decl (gc x) x let get_num_args (x:expr) = Z3native.get_app_num_args (gc x) x let get_args (x:expr) = let n = get_num_args x in let f i = Z3native.get_app_arg (gc x) x i in mk_list f n let update (x:expr) (args:expr list) = if AST.is_app x && List.length args <> get_num_args x then raise (Error "Number of arguments does not match") else Z3native.update_term (gc x) x (List.length args) args let substitute (x:expr) (from:expr list) (to_:expr list) = let len = List.length from in if List.length to_ <> len then raise (Error "Argument sizes do not match") else Z3native.substitute (gc x) x len from to_ let substitute_one x from to_ = substitute x [ from ] [ to_ ] let substitute_vars x to_ = Z3native.substitute_vars (gc x) x (List.length to_) to_ let translate (x:expr) to_ctx = if gc x = to_ctx then x else Z3native.translate (gc x) x to_ctx let to_string (x:expr) = Z3native.ast_to_string (gc x) x let is_numeral (x:expr) = Z3native.is_numeral_ast (gc x) x let is_well_sorted (x:expr) = Z3native.is_well_sorted (gc x) x let get_sort (x:expr) = Z3native.get_sort (gc x) x let is_const (x:expr) = AST.is_app x && get_num_args x = 0 && FuncDecl.get_domain_size (get_func_decl x) = 0 let mk_const (ctx:context) (name:Symbol.symbol) (range:Sort.sort) = Z3native.mk_const ctx name range let mk_const_s (ctx:context) (name:string) (range:Sort.sort) = mk_const ctx (Symbol.mk_string ctx name) range let mk_const_f (ctx:context) (f:FuncDecl.func_decl) = expr_of_func_app ctx f [] let mk_fresh_const (ctx:context) (prefix:string) (range:Sort.sort) = Z3native.mk_fresh_const ctx prefix range let mk_app (ctx:context) (f:FuncDecl.func_decl) (args:expr list) = expr_of_func_app ctx f args let mk_numeral_string (ctx:context) (v:string) (ty:Sort.sort) = Z3native.mk_numeral ctx v ty let mk_numeral_int (ctx:context) (v:int) (ty:Sort.sort) = mk_int_expr ctx v ty let equal (a:expr) (b:expr) = AST.equal a b let compare (a:expr) (b:expr) = AST.compare a b end open FuncDecl open Expr module Boolean = struct let mk_sort = Z3native.mk_bool_sort let mk_const (ctx:context) (name:Symbol.symbol) = Expr.mk_const ctx name (mk_sort ctx) let mk_const_s (ctx:context) (name:string) = mk_const ctx (Symbol.mk_string ctx name) let mk_true = Z3native.mk_true let mk_false = Z3native.mk_false let mk_val (ctx:context) (value:bool) = if value then mk_true ctx else mk_false ctx let mk_not = Z3native.mk_not let mk_ite = Z3native.mk_ite let mk_iff = Z3native.mk_iff let mk_implies = Z3native.mk_implies let mk_xor = Z3native.mk_xor let mk_and ctx args = Z3native.mk_and ctx (List.length args) args let mk_or ctx args = Z3native.mk_or ctx (List.length args) args let mk_eq = Z3native.mk_eq let mk_distinct ctx args = Z3native.mk_distinct ctx (List.length args) args let get_bool_value x = lbool_of_int (Z3native.get_bool_value (gc x) x) let is_bool x = AST.is_expr x && Z3native.is_eq_sort (gc x) (Z3native.mk_bool_sort (gc x)) (Z3native.get_sort (gc x) x) let is_true x = AST.is_app x && FuncDecl.get_decl_kind (get_func_decl x) = OP_TRUE let is_false x = AST.is_app x && FuncDecl.get_decl_kind (get_func_decl x) = OP_FALSE let is_eq x = AST.is_app x && FuncDecl.get_decl_kind (get_func_decl x) = OP_EQ let is_distinct x = AST.is_app x && FuncDecl.get_decl_kind (get_func_decl x) = OP_DISTINCT let is_ite x = AST.is_app x && FuncDecl.get_decl_kind (get_func_decl x) = OP_ITE let is_and x = AST.is_app x && FuncDecl.get_decl_kind (get_func_decl x) = OP_AND let is_or x = AST.is_app x && FuncDecl.get_decl_kind (get_func_decl x) = OP_OR let is_iff x = AST.is_app x && FuncDecl.get_decl_kind (get_func_decl x) = OP_IFF let is_xor x = AST.is_app x && FuncDecl.get_decl_kind (get_func_decl x) = OP_XOR let is_not x = AST.is_app x && FuncDecl.get_decl_kind (get_func_decl x) = OP_NOT let is_implies x = AST.is_app x && FuncDecl.get_decl_kind (get_func_decl x) = OP_IMPLIES end module Quantifier = struct type quantifier = AST.ast let gc = Z3native.context_of_ast let expr_of_quantifier q = q let quantifier_of_expr e = let q = Z3enums.ast_kind_of_int (Z3native.get_ast_kind (gc e) e) in if q <> Z3enums.QUANTIFIER_AST then raise (Error "Invalid coercion") else e module Pattern = struct type pattern = Z3native.pattern let gc = Z3native.context_of_ast let get_num_terms x = Z3native.get_pattern_num_terms (gc x) x let get_terms x = let n = get_num_terms x in let f i = Z3native.get_pattern (gc x) x i in mk_list f n let to_string x = Z3native.pattern_to_string (gc x) x end let get_index (x:expr) = if not (AST.is_var x) then raise (Error "Term is not a bound variable.") else Z3native.get_index_value (gc x) x let is_universal x = Z3native.is_quantifier_forall (gc x) x let is_existential x = not (is_universal x) let get_weight x = Z3native.get_quantifier_weight (gc x) x let get_num_patterns x = Z3native.get_quantifier_num_patterns (gc x) x let get_patterns x = let n = get_num_patterns x in let f i = Z3native.get_quantifier_pattern_ast (gc x) x i in mk_list f n let get_num_no_patterns x = Z3native.get_quantifier_num_no_patterns (gc x) x let get_no_patterns x = let n = get_num_patterns x in let f i = Z3native.get_quantifier_no_pattern_ast (gc x) x i in mk_list f n let get_num_bound x = Z3native.get_quantifier_num_bound (gc x) x let get_bound_variable_names x = let n = get_num_bound x in let f i = Z3native.get_quantifier_bound_name (gc x) x i in mk_list f n let get_bound_variable_sorts x = let n = get_num_bound x in let f i = Z3native.get_quantifier_bound_sort (gc x) x i in mk_list f n let get_body x = Z3native.get_quantifier_body (gc x) x let mk_bound = Z3native.mk_bound let mk_pattern ctx terms = let len = List.length terms in if len = 0 then raise (Error "Cannot create a pattern from zero terms") else Z3native.mk_pattern ctx len terms let _internal_mk_quantifier ~universal ctx sorts names body weight patterns nopatterns quantifier_id skolem_id = let len = List.length sorts in if List.length names <> len then raise (Error "Number of sorts does not match number of names") else match nopatterns, quantifier_id, skolem_id with | [], None, None -> Z3native.mk_quantifier ctx universal (match weight with | None -> 1 | Some x -> x) (List.length patterns) patterns len sorts names body | _ -> Z3native.mk_quantifier_ex ctx universal (match weight with | None -> 1 | Some x -> x) (match quantifier_id with | None -> Z3native.mk_null_symbol ctx | Some x -> x) (match skolem_id with | None -> Z3native.mk_null_symbol ctx | Some x -> x) (List.length patterns) patterns (List.length nopatterns) nopatterns len sorts names body let _internal_mk_quantifier_const ~universal ctx bound_constants body weight patterns nopatterns quantifier_id skolem_id = match nopatterns, quantifier_id, skolem_id with | [], None, None -> Z3native.mk_quantifier_const ctx universal (match weight with | None -> 1 | Some x -> x) (List.length bound_constants) bound_constants (List.length patterns) patterns body | _ -> Z3native.mk_quantifier_const_ex ctx universal (match weight with | None -> 1 | Some x -> x) (match quantifier_id with | None -> Z3native.mk_null_symbol ctx | Some x -> x) (match skolem_id with | None -> Z3native.mk_null_symbol ctx | Some x -> x) (List.length bound_constants) bound_constants (List.length patterns) patterns (List.length nopatterns) nopatterns body let mk_forall = _internal_mk_quantifier ~universal:true let mk_forall_const = _internal_mk_quantifier_const ~universal:true let mk_exists = _internal_mk_quantifier ~universal:false let mk_exists_const = _internal_mk_quantifier_const ~universal:false let mk_lambda_const ctx bound body = Z3native.mk_lambda_const ctx (List.length bound) bound body let mk_lambda ctx bound body = let names = List.map (fun (x,_) -> x) bound in let sorts = List.map (fun (_,y) -> y) bound in Z3native.mk_lambda ctx (List.length bound) sorts names body let mk_quantifier (ctx:context) (universal:bool) (sorts:Sort.sort list) (names:Symbol.symbol list) (body:expr) (weight:int option) (patterns:Pattern.pattern list) (nopatterns:expr list) (quantifier_id:Symbol.symbol option) (skolem_id:Symbol.symbol option) = if universal then mk_forall ctx sorts names body weight patterns nopatterns quantifier_id skolem_id else mk_exists ctx sorts names body weight patterns nopatterns quantifier_id skolem_id let mk_quantifier_const (ctx:context) (universal:bool) (bound_constants:expr list) (body:expr) (weight:int option) (patterns:Pattern.pattern list) (nopatterns:expr list) (quantifier_id:Symbol.symbol option) (skolem_id:Symbol.symbol option) = if universal then mk_forall_const ctx bound_constants body weight patterns nopatterns quantifier_id skolem_id else mk_exists_const ctx bound_constants body weight patterns nopatterns quantifier_id skolem_id let to_string x = Expr.to_string x end module Z3Array = struct let mk_sort = Z3native.mk_array_sort let is_store x = AST.is_app x && FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_STORE let is_select x = AST.is_app x && FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_SELECT let is_constant_array x = AST.is_app x && FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_CONST_ARRAY let is_default_array x = AST.is_app x && FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_ARRAY_DEFAULT let is_array_map x = AST.is_app x && FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_ARRAY_MAP let is_as_array x = AST.is_app x && FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_AS_ARRAY let is_array x = Z3native.is_app (Expr.gc x) x && sort_kind_of_int (Z3native.get_sort_kind (Expr.gc x) (Z3native.get_sort (Expr.gc x) x)) = ARRAY_SORT let get_domain x = Z3native.get_array_sort_domain (Sort.gc x) x let get_range x = Z3native.get_array_sort_range (Sort.gc x) x let mk_const ctx name domain range = Expr.mk_const ctx name (mk_sort ctx domain range) let mk_const_s ctx name domain range = mk_const ctx (Symbol.mk_string ctx name) domain range let mk_select = Z3native.mk_select let mk_store = Z3native.mk_store let mk_const_array = Z3native.mk_const_array let mk_map ctx f args = Z3native.mk_map ctx f (List.length args) args let mk_term_array = Z3native.mk_array_default let mk_array_ext = Z3native.mk_array_ext end module Set = struct let mk_sort = Z3native.mk_set_sort let is_union (x:expr) = AST.is_app x && FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_SET_UNION let is_intersect (x:expr) = AST.is_app x && FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_SET_INTERSECT let is_difference (x:expr) = AST.is_app x && FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_SET_DIFFERENCE let is_complement (x:expr) = AST.is_app x && FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_SET_COMPLEMENT let is_subset (x:expr) = AST.is_app x && FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_SET_SUBSET let mk_empty = Z3native.mk_empty_set let mk_full = Z3native.mk_full_set let mk_set_add = Z3native.mk_set_add let mk_del = Z3native.mk_set_del let mk_union ctx args = Z3native.mk_set_union ctx (List.length args) args let mk_intersection ctx args = Z3native.mk_set_intersect ctx (List.length args) args let mk_difference = Z3native.mk_set_difference let mk_complement = Z3native.mk_set_complement let mk_membership = Z3native.mk_set_member let mk_subset = Z3native.mk_set_subset end module FiniteDomain = struct let mk_sort = Z3native.mk_finite_domain_sort let mk_sort_s ctx name size = mk_sort ctx (Symbol.mk_string ctx name) size let is_finite_domain (x:expr) = let nc = Expr.gc x in Z3native.is_app nc x && sort_kind_of_int (Z3native.get_sort_kind nc (Z3native.get_sort nc x)) = FINITE_DOMAIN_SORT let is_lt (x:expr) = AST.is_app x && FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_FD_LT let get_size x = match Z3native.get_finite_domain_sort_size (Sort.gc x) x with | true, v -> v | false, _ -> raise (Error "Conversion failed.") end module Relation = struct let is_relation x = let nc = Expr.gc x in Z3native.is_app nc x && sort_kind_of_int (Z3native.get_sort_kind nc (Z3native.get_sort nc x)) = RELATION_SORT let is_store (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_RA_STORE) let is_empty (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_RA_EMPTY) let is_is_empty (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_RA_IS_EMPTY) let is_join (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_RA_JOIN) let is_union (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_RA_UNION) let is_widen (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_RA_WIDEN) let is_project (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_RA_PROJECT) let is_filter (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_RA_FILTER) let is_negation_filter (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_RA_NEGATION_FILTER) let is_rename (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_RA_RENAME) let is_complement (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_RA_COMPLEMENT) let is_select (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_RA_SELECT) let is_clone (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_RA_CLONE) let get_arity (x:Sort.sort) = Z3native.get_relation_arity (Sort.gc x) x let get_column_sorts (x:Sort.sort) = let n = get_arity x in let f i = Z3native.get_relation_column (Sort.gc x) x i in mk_list f n end module Datatype = struct module Constructor = struct type constructor = Z3native.constructor module FieldNumTable = Hashtbl.Make(struct type t = AST.ast let equal x y = AST.compare x y = 0 let hash = AST.hash end) let _field_nums = FieldNumTable.create 0 let create (ctx:context) (name:Symbol.symbol) (recognizer:Symbol.symbol) (field_names:Symbol.symbol list) (sorts:Sort.sort option list) (sort_refs:int list) = let n = List.length field_names in if n <> List.length sorts then raise (Error "Number of field names does not match number of sorts") else if n <> List.length sort_refs then raise (Error "Number of field names does not match number of sort refs") else let no = Z3native.mk_constructor ctx name recognizer n field_names (let f x = match x with None -> Z3native.mk_null_ast ctx | Some s -> s in List.map f sorts) sort_refs in FieldNumTable.add _field_nums no n; no let get_num_fields (x:constructor) = FieldNumTable.find _field_nums x let get_constructor_decl (x:constructor) = let (a, _, _) = (Z3native.query_constructor (gc x) x (get_num_fields x)) in a let get_tester_decl (x:constructor) = let (_, b, _) = (Z3native.query_constructor (gc x) x (get_num_fields x)) in b let get_accessor_decls (x:constructor) = let (_, _, c) = (Z3native.query_constructor (gc x) x (get_num_fields x)) in c end module ConstructorList = struct type constructor_list = Z3native.constructor_list let create (ctx:context) (c:Constructor.constructor list) = Z3native.mk_constructor_list ctx (List.length c) c end let mk_constructor (ctx:context) (name:Symbol.symbol) (recognizer:Symbol.symbol) (field_names:Symbol.symbol list) (sorts:Sort.sort option list) (sort_refs:int list) = Constructor.create ctx name recognizer field_names sorts sort_refs let mk_constructor_s (ctx:context) (name:string) (recognizer:Symbol.symbol) (field_names:Symbol.symbol list) (sorts:Sort.sort option list) (sort_refs:int list) = mk_constructor ctx (Symbol.mk_string ctx name) recognizer field_names sorts sort_refs let mk_sort (ctx:context) (name:Symbol.symbol) (constructors:Constructor.constructor list) = let (x,_) = Z3native.mk_datatype ctx name (List.length constructors) constructors in x let mk_sort_s (ctx:context) (name:string) (constructors:Constructor.constructor list) = mk_sort ctx (Symbol.mk_string ctx name) constructors let mk_sorts (ctx:context) (names:Symbol.symbol list) (c:Constructor.constructor list list) = let n = List.length names in let f e = ConstructorList.create ctx e in let cla = List.map f c in let (r, _) = Z3native.mk_datatypes ctx n names cla in r let mk_sorts_s (ctx:context) (names:string list) (c:Constructor.constructor list list) = mk_sorts ctx (List.map (fun x -> Symbol.mk_string ctx x) names) c let get_num_constructors (x:Sort.sort) = Z3native.get_datatype_sort_num_constructors (Sort.gc x) x let get_constructors (x:Sort.sort) = let n = get_num_constructors x in let f i = Z3native.get_datatype_sort_constructor (Sort.gc x) x i in mk_list f n let get_recognizers (x:Sort.sort) = let n = (get_num_constructors x) in let f i = Z3native.get_datatype_sort_recognizer (Sort.gc x) x i in mk_list f n let get_accessors (x:Sort.sort) = let n = (get_num_constructors x) in let f i = ( let fd = Z3native.get_datatype_sort_constructor (Sort.gc x) x i in let ds = Z3native.get_domain_size (FuncDecl.gc fd) fd in let g j = Z3native.get_datatype_sort_constructor_accessor (Sort.gc x) x i j in mk_list g ds) in mk_list f n end module Enumeration = struct let mk_sort (ctx:context) (name:Symbol.symbol) (enum_names:Symbol.symbol list) = let (a, _, _) = Z3native.mk_enumeration_sort ctx name (List.length enum_names) enum_names in a let mk_sort_s (ctx:context) (name:string) (enum_names:string list) = mk_sort ctx (Symbol.mk_string ctx name) (Symbol.mk_strings ctx enum_names) let get_const_decls (x:Sort.sort) = let n = Z3native.get_datatype_sort_num_constructors (Sort.gc x) x in let f i = Z3native.get_datatype_sort_constructor (Sort.gc x) x i in mk_list f n let get_const_decl (x:Sort.sort) (inx:int) = Z3native.get_datatype_sort_constructor (Sort.gc x) x inx let get_consts (x:Sort.sort) = let n = Z3native.get_datatype_sort_num_constructors (Sort.gc x) x in let f i = Expr.mk_const_f (Sort.gc x) (get_const_decl x i) in mk_list f n let get_const (x:Sort.sort) (inx:int) = Expr.mk_const_f (Sort.gc x) (get_const_decl x inx) let get_tester_decls (x:Sort.sort) = let n = Z3native.get_datatype_sort_num_constructors (Sort.gc x) x in let f i = Z3native.get_datatype_sort_recognizer (Sort.gc x) x i in mk_list f n let get_tester_decl (x:Sort.sort) (inx:int) = Z3native.get_datatype_sort_recognizer (Sort.gc x) x inx end module Z3List = struct let mk_sort (ctx:context) (name:Symbol.symbol) (elem_sort:Sort.sort) = let (r, _, _, _, _, _, _) = Z3native.mk_list_sort ctx name elem_sort in r let mk_list_s (ctx:context) (name:string) elem_sort = mk_sort ctx (Symbol.mk_string ctx name) elem_sort let get_nil_decl (x:Sort.sort) = Z3native.get_datatype_sort_constructor (Sort.gc x) x 0 let get_is_nil_decl (x:Sort.sort) = Z3native.get_datatype_sort_recognizer (Sort.gc x) x 0 let get_cons_decl (x:Sort.sort) = Z3native.get_datatype_sort_constructor (Sort.gc x) x 1 let get_is_cons_decl (x:Sort.sort) =Z3native.get_datatype_sort_recognizer (Sort.gc x) x 1 let get_head_decl (x:Sort.sort) = Z3native.get_datatype_sort_constructor_accessor (Sort.gc x) x 1 0 let get_tail_decl (x:Sort.sort) = Z3native.get_datatype_sort_constructor_accessor (Sort.gc x) x 1 1 let nil (x:Sort.sort) = expr_of_func_app (Sort.gc x) (get_nil_decl x) [] end module Tuple = struct let mk_sort (ctx:context) (name:Symbol.symbol) (field_names:Symbol.symbol list) (field_sorts:Sort.sort list) = let (r, _, _) = Z3native.mk_tuple_sort ctx name (List.length field_names) field_names field_sorts in r let get_mk_decl (x:Sort.sort) = Z3native.get_tuple_sort_mk_decl (Sort.gc x) x let get_num_fields (x:Sort.sort) = Z3native.get_tuple_sort_num_fields (Sort.gc x) x let get_field_decls (x:Sort.sort) = let n = get_num_fields x in let f i =Z3native.get_tuple_sort_field_decl (Sort.gc x) x i in mk_list f n end module Arithmetic = struct let is_int (x:expr) = ((sort_kind_of_int (Z3native.get_sort_kind (Expr.gc x) (Z3native.get_sort (Expr.gc x) x))) = INT_SORT) let is_arithmetic_numeral (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_ANUM) let is_le (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_LE) let is_ge (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_GE) let is_lt (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_LT) let is_gt (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_GT) let is_add (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_ADD) let is_sub (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_SUB) let is_uminus (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_UMINUS) let is_mul (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_MUL) let is_div (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_DIV) let is_idiv (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_IDIV) let is_remainder (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_REM) let is_modulus (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_MOD) let is_int2real (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_TO_REAL) let is_real2int (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_TO_INT) let is_real_is_int (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_IS_INT) let is_real (x:expr) = ((sort_kind_of_int (Z3native.get_sort_kind (Expr.gc x) (Z3native.get_sort (Expr.gc x) x))) = REAL_SORT) let is_int_numeral (x:expr) = (Expr.is_numeral x) && (is_int x) let is_rat_numeral (x:expr) = (Expr.is_numeral x) && (is_real x) let is_algebraic_number (x:expr) = Z3native.is_algebraic_number (Expr.gc x) x module Integer = struct let mk_sort = Z3native.mk_int_sort let get_int x = match Z3native.get_numeral_int (Expr.gc x) x with | true, v -> v | false, _ -> raise (Error "Conversion failed.") let get_big_int (x:expr) = if is_int_numeral x then let s = (Z3native.get_numeral_string (Expr.gc x) x) in Z.of_string s else raise (Error "Conversion failed.") let numeral_to_string (x:expr) = Z3native.get_numeral_string (Expr.gc x) x let mk_const (ctx:context) (name:Symbol.symbol) = Expr.mk_const ctx name (mk_sort ctx) let mk_const_s (ctx:context) (name:string) = mk_const ctx (Symbol.mk_string ctx name) let mk_mod = Z3native.mk_mod let mk_rem = Z3native.mk_rem let mk_numeral_s (ctx:context) (v:string) = Z3native.mk_numeral ctx v (mk_sort ctx) let mk_numeral_i (ctx:context) (v:int) = mk_int_expr ctx v (mk_sort ctx) let mk_int2real = Z3native.mk_int2real let mk_int2bv = Z3native.mk_int2bv end module Real = struct let mk_sort = Z3native.mk_real_sort let get_numerator x = Z3native.get_numerator (Expr.gc x) x let get_denominator x = Z3native.get_denominator (Expr.gc x) x let get_ratio x = if is_rat_numeral x then let s = Z3native.get_numeral_string (Expr.gc x) x in Q.of_string s else raise (Error "Conversion failed.") let to_decimal_string (x:expr) (precision:int) = Z3native.get_numeral_decimal_string (Expr.gc x) x precision let numeral_to_string (x:expr) = Z3native.get_numeral_string (Expr.gc x) x let mk_const (ctx:context) (name:Symbol.symbol) = Expr.mk_const ctx name (mk_sort ctx) let mk_const_s (ctx:context) (name:string) = mk_const ctx (Symbol.mk_string ctx name) let mk_numeral_nd (ctx:context) (num:int) (den:int) = if den = 0 then raise (Error "Denominator is zero") else if not (check_int32 num) || not (check_int32 den) then raise (Error "numerals don't fit in 32 bits") else Z3native.mk_real ctx num den let mk_numeral_s (ctx:context) (v:string) = Z3native.mk_numeral ctx v (mk_sort ctx) let mk_numeral_i (ctx:context) (v:int) = mk_int_expr ctx v (mk_sort ctx) let mk_is_integer = Z3native.mk_is_int let mk_real2int = Z3native.mk_real2int module AlgebraicNumber = struct let to_upper (x:expr) (precision:int) = Z3native.get_algebraic_number_upper (Expr.gc x) x precision let to_lower (x:expr) (precision:int) = Z3native.get_algebraic_number_lower (Expr.gc x) x precision let to_decimal_string (x:expr) (precision:int) = Z3native.get_numeral_decimal_string (Expr.gc x) x precision let numeral_to_string (x:expr) = Z3native.get_numeral_string (Expr.gc x) x end end let mk_add (ctx:context) (t:expr list) = Z3native.mk_add ctx (List.length t) t let mk_mul (ctx:context) (t:expr list) = Z3native.mk_mul ctx (List.length t) t let mk_sub (ctx:context) (t:expr list) = Z3native.mk_sub ctx (List.length t) t let mk_unary_minus = Z3native.mk_unary_minus let mk_div = Z3native.mk_div let mk_power = Z3native.mk_power let mk_lt = Z3native.mk_lt let mk_le = Z3native.mk_le let mk_gt = Z3native.mk_gt let mk_ge = Z3native.mk_ge end module BitVector = struct let mk_sort (ctx:context) size = Z3native.mk_bv_sort ctx size let is_bv (x:expr) = ((sort_kind_of_int (Z3native.get_sort_kind (Expr.gc x) (Z3native.get_sort (Expr.gc x) x))) = BV_SORT) let is_bv_numeral (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_BNUM) let is_bv_bit1 (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_BIT1) let is_bv_bit0 (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_BIT0) let is_bv_uminus (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_BNEG) let is_bv_add (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_BADD) let is_bv_sub (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_BSUB) let is_bv_mul (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_BMUL) let is_bv_sdiv (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_BSDIV) let is_bv_udiv (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_BUDIV) let is_bv_SRem (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_BSREM) let is_bv_urem (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_BUREM) let is_bv_smod (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_BSMOD) let is_bv_sdiv0 (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_BSDIV0) let is_bv_udiv0 (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_BUDIV0) let is_bv_srem0 (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_BSREM0) let is_bv_urem0 (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_BUREM0) let is_bv_smod0 (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_BSMOD0) let is_bv_ule (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_ULEQ) let is_bv_sle (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_SLEQ) let is_bv_uge (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_UGEQ) let is_bv_sge (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_SGEQ) let is_bv_ult (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_ULT) let is_bv_slt (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_SLT) let is_bv_ugt (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_UGT) let is_bv_sgt (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_SGT) let is_bv_and (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_BAND) let is_bv_or (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_BOR) let is_bv_not (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_BNOT) let is_bv_xor (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_BXOR) let is_bv_nand (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_BNAND) let is_bv_nor (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_BNOR) let is_bv_xnor (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_BXNOR) let is_bv_concat (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_CONCAT) let is_bv_signextension (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_SIGN_EXT) let is_bv_zeroextension (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_ZERO_EXT) let is_bv_extract (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_EXTRACT) let is_bv_repeat (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_REPEAT) let is_bv_reduceor (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_BREDOR) let is_bv_reduceand (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_BREDAND) let is_bv_comp (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_BCOMP) let is_bv_shiftleft (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_BSHL) let is_bv_shiftrightlogical (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_BLSHR) let is_bv_shiftrightarithmetic (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_BASHR) let is_bv_rotateleft (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_ROTATE_LEFT) let is_bv_rotateright (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_ROTATE_RIGHT) let is_bv_rotateleftextended (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_EXT_ROTATE_LEFT) let is_bv_rotaterightextended (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_EXT_ROTATE_RIGHT) let is_int2bv (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_INT2BV) let is_bv2int (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_BV2INT) let is_bv_carry (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_CARRY) let is_bv_xor3 (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_XOR3) let get_size (x:Sort.sort) = Z3native.get_bv_sort_size (Sort.gc x) x let numeral_to_string (x:expr) = Z3native.get_numeral_string (Expr.gc x) x let mk_const (ctx:context) (name:Symbol.symbol) (size:int) = Expr.mk_const ctx name (mk_sort ctx size) let mk_const_s (ctx:context) (name:string) (size:int) = mk_const ctx (Symbol.mk_string ctx name) size let mk_not = Z3native.mk_bvnot let mk_redand = Z3native.mk_bvredand let mk_redor = Z3native.mk_bvredor let mk_and = Z3native.mk_bvand let mk_or = Z3native.mk_bvor let mk_xor = Z3native.mk_bvxor let mk_nand = Z3native.mk_bvnand let mk_nor = Z3native.mk_bvnor let mk_xnor = Z3native.mk_bvxnor let mk_neg = Z3native.mk_bvneg let mk_add = Z3native.mk_bvadd let mk_sub = Z3native.mk_bvsub let mk_mul = Z3native.mk_bvmul let mk_udiv = Z3native.mk_bvudiv let mk_sdiv = Z3native.mk_bvsdiv let mk_urem = Z3native.mk_bvurem let mk_srem = Z3native.mk_bvsrem let mk_smod = Z3native.mk_bvsmod let mk_ult = Z3native.mk_bvult let mk_slt = Z3native.mk_bvslt let mk_ule = Z3native.mk_bvule let mk_sle = Z3native.mk_bvsle let mk_uge = Z3native.mk_bvuge let mk_sge = Z3native.mk_bvsge let mk_ugt = Z3native.mk_bvugt let mk_sgt = Z3native.mk_bvsgt let mk_concat = Z3native.mk_concat let mk_extract = Z3native.mk_extract let mk_sign_ext = Z3native.mk_sign_ext let mk_zero_ext = Z3native.mk_zero_ext let mk_repeat = Z3native.mk_repeat let mk_shl = Z3native.mk_bvshl let mk_lshr = Z3native.mk_bvlshr let mk_ashr = Z3native.mk_bvashr let mk_rotate_left = Z3native.mk_rotate_left let mk_rotate_right = Z3native.mk_rotate_right let mk_ext_rotate_left = Z3native.mk_ext_rotate_left let mk_ext_rotate_right = Z3native.mk_ext_rotate_right let mk_bv2int = Z3native.mk_bv2int let mk_add_no_overflow = Z3native.mk_bvadd_no_overflow let mk_add_no_underflow = Z3native.mk_bvadd_no_underflow let mk_sub_no_overflow = Z3native.mk_bvsub_no_overflow let mk_sub_no_underflow = Z3native.mk_bvsub_no_underflow let mk_sdiv_no_overflow = Z3native.mk_bvsdiv_no_overflow let mk_neg_no_overflow = Z3native.mk_bvneg_no_overflow let mk_mul_no_overflow = Z3native.mk_bvmul_no_overflow let mk_mul_no_underflow = Z3native.mk_bvmul_no_underflow let mk_numeral ctx v size = Z3native.mk_numeral ctx v (mk_sort ctx size) end module Seq = struct let mk_seq_sort = Z3native.mk_seq_sort let is_seq_sort = Z3native.is_seq_sort let mk_re_sort = Z3native.mk_re_sort let is_re_sort = Z3native.is_re_sort let mk_string_sort = Z3native.mk_string_sort let is_string_sort = Z3native.is_string_sort let mk_string = Z3native.mk_string let is_string = Z3native.is_string let get_string = Z3native.get_string let mk_seq_empty = Z3native.mk_seq_empty let mk_seq_unit = Z3native.mk_seq_unit let mk_seq_concat ctx args = Z3native.mk_seq_concat ctx (List.length args) args let mk_seq_prefix = Z3native.mk_seq_prefix let mk_seq_suffix = Z3native.mk_seq_suffix let mk_seq_contains = Z3native.mk_seq_contains let mk_seq_extract = Z3native.mk_seq_extract let mk_seq_replace = Z3native.mk_seq_replace let mk_seq_at = Z3native.mk_seq_at let mk_seq_length = Z3native.mk_seq_length let mk_seq_index = Z3native.mk_seq_index let mk_str_to_int = Z3native.mk_str_to_int let mk_int_to_str = Z3native.mk_int_to_str let mk_seq_to_re = Z3native.mk_seq_to_re let mk_seq_in_re = Z3native.mk_seq_in_re let mk_re_plus = Z3native.mk_re_plus let mk_re_star = Z3native.mk_re_star let mk_re_option = Z3native.mk_re_option let mk_re_union ctx args = Z3native.mk_re_union ctx (List.length args) args let mk_re_concat ctx args = Z3native.mk_re_concat ctx (List.length args) args let mk_re_range = Z3native.mk_re_range let mk_re_loop = Z3native.mk_re_loop let mk_re_intersect = Z3native.mk_re_intersect let mk_re_complement = Z3native.mk_re_complement let mk_re_empty = Z3native.mk_re_empty let mk_re_full = Z3native.mk_re_full end module FloatingPoint = struct module RoundingMode = struct let mk_sort = Z3native.mk_fpa_rounding_mode_sort let is_fprm x = Sort.get_sort_kind (Expr.get_sort x) = ROUNDING_MODE_SORT let mk_round_nearest_ties_to_even = Z3native.mk_fpa_round_nearest_ties_to_even let mk_rne = Z3native.mk_fpa_rne let mk_round_nearest_ties_to_away = Z3native.mk_fpa_round_nearest_ties_to_away let mk_rna = Z3native.mk_fpa_rna let mk_round_toward_positive = Z3native.mk_fpa_round_toward_positive let mk_rtp = Z3native.mk_fpa_rtp let mk_round_toward_negative = Z3native.mk_fpa_round_toward_negative let mk_rtn = Z3native.mk_fpa_rtn let mk_round_toward_zero = Z3native.mk_fpa_round_toward_zero let mk_rtz = Z3native.mk_fpa_rtz end let mk_sort = Z3native.mk_fpa_sort let mk_sort_half = Z3native.mk_fpa_sort_half let mk_sort_16 = Z3native.mk_fpa_sort_16 let mk_sort_single = Z3native.mk_fpa_sort_single let mk_sort_32 = Z3native.mk_fpa_sort_32 let mk_sort_double = Z3native.mk_fpa_sort_double let mk_sort_64 = Z3native.mk_fpa_sort_64 let mk_sort_quadruple = Z3native.mk_fpa_sort_quadruple let mk_sort_128 = Z3native.mk_fpa_sort_128 let mk_nan = Z3native.mk_fpa_nan let mk_inf = Z3native.mk_fpa_inf let mk_zero = Z3native.mk_fpa_zero let mk_fp = Z3native.mk_fpa_fp let mk_numeral_f = Z3native.mk_fpa_numeral_double let mk_numeral_i = Z3native.mk_fpa_numeral_int let mk_numeral_i_u = Z3native.mk_fpa_numeral_int64_uint64 let mk_numeral_s = Z3native.mk_numeral let is_fp (x:expr) = (Sort.get_sort_kind (Expr.get_sort x)) = FLOATING_POINT_SORT let is_abs (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_FPA_ABS) let is_neg (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_FPA_NEG) let is_add (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_FPA_ADD) let is_sub (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_FPA_SUB) let is_mul (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_FPA_MUL) let is_div (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_FPA_DIV) let is_fma (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_FPA_FMA) let is_sqrt (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_FPA_SQRT) let is_rem (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_FPA_REM) let is_round_to_integral (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_FPA_ROUND_TO_INTEGRAL) let is_min (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_FPA_MIN) let is_max (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_FPA_MAX) let is_leq (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_FPA_LE) let is_lt (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_FPA_LT) let is_geq (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_FPA_GE) let is_gt (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_FPA_GT) let is_eq (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_FPA_EQ) let is_is_normal (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_FPA_IS_NORMAL) let is_is_subnormal (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_FPA_IS_SUBNORMAL) let is_is_zero (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_FPA_IS_ZERO) let is_is_infinite (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_FPA_IS_INF) let is_is_nan (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_FPA_IS_NAN) let is_is_negative (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_FPA_IS_NEGATIVE) let is_is_positive (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_FPA_IS_POSITIVE) let is_to_fp (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_FPA_TO_FP) let is_to_fp_unsigned (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_FPA_TO_FP_UNSIGNED) let is_to_ubv (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_FPA_TO_UBV) let is_to_sbv (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_FPA_TO_SBV) let is_to_real (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_FPA_TO_REAL) let is_to_ieee_bv (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_FPA_TO_IEEE_BV) let numeral_to_string (x:expr) = Z3native.get_numeral_string (Expr.gc x) x let mk_const = Expr.mk_const let mk_const_s = Expr.mk_const_s let mk_abs = Z3native.mk_fpa_abs let mk_neg = Z3native.mk_fpa_neg let mk_add = Z3native.mk_fpa_add let mk_sub = Z3native.mk_fpa_sub let mk_mul = Z3native.mk_fpa_mul let mk_div = Z3native.mk_fpa_div let mk_fma = Z3native.mk_fpa_fma let mk_sqrt = Z3native.mk_fpa_sqrt let mk_rem = Z3native.mk_fpa_rem let mk_round_to_integral = Z3native.mk_fpa_round_to_integral let mk_min = Z3native.mk_fpa_min let mk_max = Z3native.mk_fpa_max let mk_leq = Z3native.mk_fpa_leq let mk_lt = Z3native.mk_fpa_lt let mk_geq = Z3native.mk_fpa_geq let mk_gt = Z3native.mk_fpa_gt let mk_eq = Z3native.mk_fpa_eq let mk_is_normal = Z3native.mk_fpa_is_normal let mk_is_subnormal = Z3native.mk_fpa_is_subnormal let mk_is_zero = Z3native.mk_fpa_is_zero let mk_is_infinite = Z3native.mk_fpa_is_infinite let mk_is_nan = Z3native.mk_fpa_is_nan let mk_is_negative = Z3native.mk_fpa_is_negative let mk_is_positive = Z3native.mk_fpa_is_positive let mk_to_fp_bv = Z3native.mk_fpa_to_fp_bv let mk_to_fp_float = Z3native.mk_fpa_to_fp_float let mk_to_fp_real = Z3native.mk_fpa_to_fp_real let mk_to_fp_signed = Z3native.mk_fpa_to_fp_signed let mk_to_fp_unsigned = Z3native.mk_fpa_to_fp_unsigned let mk_to_ubv = Z3native.mk_fpa_to_ubv let mk_to_sbv = Z3native.mk_fpa_to_sbv let mk_to_real = Z3native.mk_fpa_to_real let get_ebits = Z3native.fpa_get_ebits let get_sbits = Z3native.fpa_get_sbits let get_numeral_sign = Z3native.fpa_get_numeral_sign let get_numeral_sign_bv = Z3native.fpa_get_numeral_sign_bv let get_numeral_exponent_string = Z3native.fpa_get_numeral_exponent_string let get_numeral_exponent_int = Z3native.fpa_get_numeral_exponent_int64 let get_numeral_exponent_bv = Z3native.fpa_get_numeral_exponent_bv let get_numeral_significand_string = Z3native.fpa_get_numeral_significand_string let get_numeral_significand_uint = Z3native.fpa_get_numeral_significand_uint64 let get_numeral_significand_bv = Z3native.fpa_get_numeral_significand_bv let is_numeral_nan = Z3native.fpa_is_numeral_nan let is_numeral_inf = Z3native.fpa_is_numeral_inf let is_numeral_zero = Z3native.fpa_is_numeral_zero let is_numeral_normal = Z3native.fpa_is_numeral_normal let is_numeral_subnormal = Z3native.fpa_is_numeral_subnormal let is_numeral_positive = Z3native.fpa_is_numeral_positive let is_numeral_negative = Z3native.fpa_is_numeral_negative let mk_to_ieee_bv = Z3native.mk_fpa_to_ieee_bv let mk_to_fp_int_real = Z3native.mk_fpa_to_fp_int_real let numeral_to_string x = Z3native.get_numeral_string (Expr.gc x) x end module Proof = struct let is_true (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_PR_TRUE) let is_asserted (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_PR_ASSERTED) let is_goal (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_PR_GOAL) let is_oeq (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_OEQ) let is_modus_ponens (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_PR_MODUS_PONENS) let is_reflexivity (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_PR_REFLEXIVITY) let is_symmetry (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_PR_SYMMETRY) let is_transitivity (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_PR_TRANSITIVITY) let is_Transitivity_star (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_PR_TRANSITIVITY_STAR) let is_monotonicity (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_PR_MONOTONICITY) let is_quant_intro (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_PR_QUANT_INTRO) let is_distributivity (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_PR_DISTRIBUTIVITY) let is_and_elimination (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_PR_AND_ELIM) let is_or_elimination (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_PR_NOT_OR_ELIM) let is_rewrite (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_PR_REWRITE) let is_rewrite_star (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_PR_REWRITE_STAR) let is_pull_quant (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_PR_PULL_QUANT) let is_push_quant (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_PR_PUSH_QUANT) let is_elim_unused_vars (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_PR_ELIM_UNUSED_VARS) let is_der (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_PR_DER) let is_quant_inst (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_PR_QUANT_INST) let is_hypothesis (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_PR_HYPOTHESIS) let is_lemma (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_PR_LEMMA) let is_unit_resolution (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_PR_UNIT_RESOLUTION) let is_iff_true (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_PR_IFF_TRUE) let is_iff_false (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_PR_IFF_FALSE) let is_commutativity (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_PR_COMMUTATIVITY) (* *) let is_def_axiom (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_PR_DEF_AXIOM) let is_def_intro (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_PR_DEF_INTRO) let is_apply_def (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_PR_APPLY_DEF) let is_iff_oeq (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_PR_IFF_OEQ) let is_nnf_pos (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_PR_NNF_POS) let is_nnf_neg (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_PR_NNF_NEG) let is_skolemize (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_PR_SKOLEMIZE) let is_modus_ponens_oeq (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_PR_MODUS_PONENS_OEQ) let is_theory_lemma (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_PR_TH_LEMMA) end module Goal = struct type goal = Z3native.goal let gc = Z3native.context_of_goal let get_precision (x:goal) = goal_prec_of_int (Z3native.goal_precision (gc x) x) let is_precise (x:goal) = (get_precision x) = GOAL_PRECISE let is_underapproximation (x:goal) = (get_precision x) = GOAL_UNDER let is_overapproximation (x:goal) = (get_precision x) = GOAL_OVER let is_garbage (x:goal) = (get_precision x) = GOAL_UNDER_OVER let add x constraints = List.iter (Z3native.goal_assert (gc x) x) constraints let is_inconsistent (x:goal) = Z3native.goal_inconsistent (gc x) x let get_depth (x:goal) = Z3native.goal_depth (gc x) x let reset (x:goal) = Z3native.goal_reset (gc x) x let get_size (x:goal) = Z3native.goal_size (gc x) x let get_formulas (x:goal) = let n = get_size x in let f i = Z3native.goal_formula (gc x) x i in mk_list f n let get_num_exprs (x:goal) = Z3native.goal_num_exprs (gc x) x let is_decided_sat (x:goal) = Z3native.goal_is_decided_sat (gc x) x let is_decided_unsat (x:goal) = Z3native.goal_is_decided_unsat (gc x) x let translate (x:goal) (to_ctx:context) = Z3native.goal_translate (gc x) x to_ctx let simplify (x:goal) (p:Params.params option) = let tn = Z3native.mk_tactic (gc x) "simplify" in Z3native.tactic_inc_ref (gc x) tn; let arn = match p with | None -> Z3native.tactic_apply (gc x) tn x | Some pn -> Z3native.tactic_apply_ex (gc x) tn x pn in Z3native.apply_result_inc_ref (gc x) arn; let sg = Z3native.apply_result_get_num_subgoals (gc x) arn in let res = if sg = 0 then raise (Error "No subgoals") else Z3native.apply_result_get_subgoal (gc x) arn 0 in Z3native.apply_result_dec_ref (gc x) arn; Z3native.tactic_dec_ref (gc x) tn; res let mk_goal = Z3native.mk_goal let to_string (x:goal) = Z3native.goal_to_string (gc x) x let as_expr (x:goal) = match get_size x with | 0 -> Boolean.mk_true (gc x) | 1 -> List.hd (get_formulas x) | _ -> Boolean.mk_and (gc x) (get_formulas x) end module Model = struct type model = Z3native.model let gc = Z3native.context_of_model module FuncInterp = struct type func_interp = Z3native.func_interp let gc = Z3native.context_of_func_interp module FuncEntry = struct type func_entry = Z3native.func_entry let gc = Z3native.context_of_func_entry let get_value (x:func_entry) = Z3native.func_entry_get_value (gc x) x let get_num_args (x:func_entry) = Z3native.func_entry_get_num_args (gc x) x let get_args (x:func_entry) = let n = get_num_args x in let f i = Z3native.func_entry_get_arg (gc x) x i in mk_list f n let to_string (x:func_entry) = let a = get_args x in let f c p = (p ^ (Expr.to_string c) ^ ", ") in "[" ^ List.fold_right f a ((Expr.to_string (get_value x)) ^ "]") end let get_num_entries (x:func_interp) = Z3native.func_interp_get_num_entries (gc x) x let get_entries (x:func_interp) = let n = get_num_entries x in let f i = Z3native.func_interp_get_entry (gc x) x i in mk_list f n let get_else (x:func_interp) = Z3native.func_interp_get_else (gc x) x let get_arity (x:func_interp) = Z3native.func_interp_get_arity (gc x) x let to_string (x:func_interp) = let f c p = ( let n = FuncEntry.get_num_args c in p ^ let g c p = (p ^ (Expr.to_string c) ^ ", ") in (if n > 1 then "[" else "") ^ (List.fold_right g (FuncEntry.get_args c) ((if n > 1 then "]" else "") ^ " -> " ^ (Expr.to_string (FuncEntry.get_value c)) ^ ", "))) in List.fold_right f (get_entries x) ("else -> " ^ (Expr.to_string (get_else x)) ^ "]") end let get_const_interp (x:model) (f:func_decl) = if FuncDecl.get_arity f <> 0 then raise (Error "Non-zero arity functions have FunctionInterpretations as a model. Use FuncInterp.") else let np = Z3native.model_get_const_interp (gc x) x f in if Z3native.is_null_ast np then None else Some np let get_const_interp_e (x:model) (a:expr) = get_const_interp x (Expr.get_func_decl a) let rec get_func_interp (x:model) (f:func_decl) = let sk = sort_kind_of_int (Z3native.get_sort_kind (gc x) (Z3native.get_range (FuncDecl.gc f) f)) in if FuncDecl.get_arity f = 0 then let n = Z3native.model_get_const_interp (gc x) x f in if Z3native.is_null_ast n then None else match sk with | ARRAY_SORT -> if not (Z3native.is_as_array (gc x) n) then raise (Error "Argument was not an array constant") else let fd = Z3native.get_as_array_func_decl (gc x) n in get_func_interp x fd | _ -> raise (Error "Constant functions do not have a function interpretation; use ConstInterp"); else let n = Z3native.model_get_func_interp (gc x) x f in if Z3native.is_null_func_interp n then None else Some n (** The number of constants that have an interpretation in the model. *) let get_num_consts (x:model) = Z3native.model_get_num_consts (gc x) x let get_const_decls (x:model) = let n = (get_num_consts x) in let f i = Z3native.model_get_const_decl (gc x) x i in mk_list f n let get_num_funcs (x:model) = Z3native.model_get_num_funcs (gc x) x let get_func_decls (x:model) = let n = (get_num_funcs x) in let f i = Z3native.model_get_func_decl (gc x) x i in mk_list f n let get_decls (x:model) = let n_funcs = get_num_funcs x in let n_consts = get_num_consts x in let f i = Z3native.model_get_func_decl (gc x) x i in let g i = Z3native.model_get_const_decl (gc x) x i in (mk_list f n_funcs) @ (mk_list g n_consts) let eval (x:model) (t:expr) (completion:bool) = match Z3native.model_eval (gc x) x t completion with | (false, _) -> None | (true, v) -> Some v let evaluate = eval let get_num_sorts (x:model) = Z3native.model_get_num_sorts (gc x) x let get_sorts (x:model) = let n = get_num_sorts x in let f i = Z3native.model_get_sort (gc x) x i in mk_list f n let sort_universe (x:model) (s:Sort.sort) = let av = Z3native.model_get_sort_universe (gc x) x s in AST.ASTVector.to_expr_list av let to_string (x:model) = Z3native.model_to_string (gc x) x end module Probe = struct type probe = Z3native.probe let apply (x:probe) (g:Goal.goal) = Z3native.probe_apply (gc x) x g let get_num_probes = Z3native.get_num_probes let get_probe_names (ctx:context) = let n = get_num_probes ctx in let f i = Z3native.get_probe_name ctx i in mk_list f n let get_probe_description = Z3native.probe_get_descr let mk_probe = Z3native.mk_probe let const = Z3native.probe_const let lt = Z3native.probe_lt let gt = Z3native.probe_gt let le = Z3native.probe_le let ge = Z3native.probe_ge let eq = Z3native.probe_eq let and_ = Z3native.probe_and let or_ = Z3native.probe_or let not_ = Z3native.probe_not end module Tactic = struct type tactic = Z3native.tactic let gc = Z3native.context_of_tactic module ApplyResult = struct type apply_result = Z3native.apply_result let gc = Z3native.context_of_apply_result let get_num_subgoals (x:apply_result) = Z3native.apply_result_get_num_subgoals (gc x) x let get_subgoals (x:apply_result) = let n = get_num_subgoals x in let f i = Z3native.apply_result_get_subgoal (gc x) x i in mk_list f n let get_subgoal (x:apply_result) (i:int) = Z3native.apply_result_get_subgoal (gc x) x i let to_string (x:apply_result) = Z3native.apply_result_to_string (gc x) x end let get_help (x:tactic) = Z3native.tactic_get_help (gc x) x let get_param_descrs (x:tactic) = Z3native.tactic_get_param_descrs (gc x) x let apply (x:tactic) (g:Goal.goal) (p:Params.params option) = match p with | None -> Z3native.tactic_apply (gc x) x g | Some pn -> Z3native.tactic_apply_ex (gc x) x g pn let get_num_tactics = Z3native.get_num_tactics let get_tactic_names (ctx:context) = let n = get_num_tactics ctx in let f i = Z3native.get_tactic_name ctx i in mk_list f n let get_tactic_description = Z3native.tactic_get_descr let mk_tactic = Z3native.mk_tactic let and_then (ctx:context) (t1:tactic) (t2:tactic) (ts:tactic list) = let f p c = (match p with | None -> Some c | Some(x) -> Some (Z3native.tactic_and_then ctx c x)) in match (List.fold_left f None ts) with | None -> Z3native.tactic_and_then ctx t1 t2 | Some(x) -> let o = Z3native.tactic_and_then ctx t2 x in Z3native.tactic_and_then ctx t1 o let or_else = Z3native.tactic_or_else let try_for = Z3native.tactic_try_for let when_ = Z3native.tactic_when let cond = Z3native.tactic_cond let repeat = Z3native.tactic_repeat let skip = Z3native.tactic_skip let fail = Z3native.tactic_fail let fail_if = Z3native.tactic_fail_if let fail_if_not_decided = Z3native.tactic_fail_if_not_decided let using_params = Z3native.tactic_using_params let with_ = using_params let par_or (ctx:context) (t:tactic list) = Z3native.tactic_par_or ctx (List.length t) t let par_and_then = Z3native.tactic_par_and_then let interrupt = Z3native.interrupt end module Statistics = struct type statistics = Z3native.stats let gc = Z3native.context_of_stats module Entry = struct type statistics_entry = { m_key:string; m_is_int:bool; m_is_float:bool; m_int:int; m_float:float } let create_si k v = { m_key = k; m_is_int = true; m_is_float = false; m_int = v; m_float = 0.0 } let create_sd k v = { m_key = k; m_is_int = false; m_is_float = true; m_int = 0; m_float = v } let get_key (x:statistics_entry) = x.m_key let get_int (x:statistics_entry) = x.m_int let get_float (x:statistics_entry) = x.m_float let is_int (x:statistics_entry) = x.m_is_int let is_float (x:statistics_entry) = x.m_is_float let to_string_value (x:statistics_entry) = if is_int x then string_of_int (get_int x) else if is_float x then string_of_float (get_float x) else raise (Error "Unknown statistical entry type") let to_string (x:statistics_entry) = (get_key x) ^ ": " ^ (to_string_value x) end let to_string (x:statistics) = Z3native.stats_to_string (gc x) x let get_size (x:statistics) = Z3native.stats_size (gc x) x let get_entries (x:statistics) = let n = get_size x in let f i = let k = Z3native.stats_get_key (gc x) x i in if Z3native.stats_is_uint (gc x) x i then Entry.create_si k (Z3native.stats_get_uint_value (gc x) x i) else Entry.create_sd k (Z3native.stats_get_double_value (gc x) x i) in mk_list f n let get_keys (x:statistics) = let n = get_size x in let f i = Z3native.stats_get_key (gc x) x i in mk_list f n let get (x:statistics) (key:string) = try Some(List.find (fun c -> Entry.get_key c = key) (get_entries x)) with | Not_found -> None end module Solver = struct type solver = Z3native.solver type status = UNSATISFIABLE | UNKNOWN | SATISFIABLE let gc = Z3native.context_of_solver let string_of_status (s:status) = match s with | UNSATISFIABLE -> "unsatisfiable" | SATISFIABLE -> "satisfiable" | _ -> "unknown" let get_help (x:solver) = Z3native.solver_get_help (gc x) x let set_parameters (x:solver) (p:Params.params) = Z3native.solver_set_params (gc x) x p let get_param_descrs (x:solver) = Z3native.solver_get_param_descrs (gc x) x let get_num_scopes (x:solver) = Z3native.solver_get_num_scopes (gc x) x let push (x:solver) = Z3native.solver_push (gc x) x let pop (x:solver) (n:int) = Z3native.solver_pop (gc x) x n let reset (x:solver) = Z3native.solver_reset (gc x) x let add x constraints = List.iter (Z3native.solver_assert (gc x) x) constraints let assert_and_track_l x cs ps = try List.iter2 (Z3native.solver_assert_and_track (gc x) x) cs ps with | Invalid_argument _ -> raise (Error "Argument size mismatch") let assert_and_track x = Z3native.solver_assert_and_track (gc x) x let get_num_assertions x = let a = Z3native.solver_get_assertions (gc x) x in AST.ASTVector.get_size a let get_assertions x = let av = Z3native.solver_get_assertions (gc x) x in AST.ASTVector.to_expr_list av let check (x:solver) (assumptions:expr list) = let result = match assumptions with | [] -> Z3native.solver_check (gc x) x | _::_ -> Z3native.solver_check_assumptions (gc x) x (List.length assumptions) assumptions in match lbool_of_int result with | L_TRUE -> SATISFIABLE | L_FALSE -> UNSATISFIABLE | _ -> UNKNOWN let get_model x = try let q = Z3native.solver_get_model (gc x) x in if Z3native.is_null_model q then None else Some q with | _ -> None let get_proof x = let q = Z3native.solver_get_proof (gc x) x in if Z3native.is_null_ast q then None else Some q let get_unsat_core x = let av = Z3native.solver_get_unsat_core (gc x) x in AST.ASTVector.to_expr_list av let get_reason_unknown x = Z3native.solver_get_reason_unknown (gc x) x let get_statistics x = Z3native.solver_get_statistics (gc x) x let mk_solver ctx logic = match logic with | None -> Z3native.mk_solver ctx | Some x -> Z3native.mk_solver_for_logic ctx x let mk_solver_s ctx logic = mk_solver ctx (Some (Symbol.mk_string ctx logic)) let mk_simple_solver = Z3native.mk_simple_solver let mk_solver_t = Z3native.mk_solver_from_tactic let translate x = Z3native.solver_translate (gc x) x let to_string x = Z3native.solver_to_string (gc x) x end module Fixedpoint = struct type fixedpoint = Z3native.fixedpoint let gc = Z3native.context_of_fixedpoint let get_help x = Z3native.fixedpoint_get_help (gc x) x let set_parameters x = Z3native.fixedpoint_set_params (gc x) x let get_param_descrs x = Z3native.fixedpoint_get_param_descrs (gc x) x let add x constraints = List.iter (Z3native.fixedpoint_assert (gc x) x) constraints let register_relation x = Z3native.fixedpoint_register_relation (gc x) x let add_rule (x:fixedpoint) (rule:expr) (name:Symbol.symbol option) = match name with | None -> Z3native.fixedpoint_add_rule (gc x) x rule (Z3native.mk_null_symbol (gc x)) | Some y -> Z3native.fixedpoint_add_rule (gc x) x rule y let add_fact (x:fixedpoint) (pred:func_decl) (args:int list) = Z3native.fixedpoint_add_fact (gc x) x pred (List.length args) args let query (x:fixedpoint) (query:expr) = match lbool_of_int (Z3native.fixedpoint_query (gc x) x query) with | L_TRUE -> Solver.SATISFIABLE | L_FALSE -> Solver.UNSATISFIABLE | _ -> Solver.UNKNOWN let query_r (x:fixedpoint) (relations:func_decl list) = match lbool_of_int (Z3native.fixedpoint_query_relations (gc x) x (List.length relations) relations) with | L_TRUE -> Solver.SATISFIABLE | L_FALSE -> Solver.UNSATISFIABLE | _ -> Solver.UNKNOWN let update_rule x = Z3native.fixedpoint_update_rule (gc x) x let get_answer x = let q = Z3native.fixedpoint_get_answer (gc x) x in if Z3native.is_null_ast q then None else Some q let get_reason_unknown x = Z3native.fixedpoint_get_reason_unknown (gc x) x let get_num_levels x = Z3native.fixedpoint_get_num_levels (gc x) x let get_cover_delta (x:fixedpoint) (level:int) (predicate:func_decl) = let q = Z3native.fixedpoint_get_cover_delta (gc x) x level predicate in if Z3native.is_null_ast q then None else Some q let add_cover (x:fixedpoint) (level:int) (predicate:func_decl) (property:expr) = Z3native.fixedpoint_add_cover (gc x) x level predicate property let to_string (x:fixedpoint) = Z3native.fixedpoint_to_string (gc x) x 0 [] let set_predicate_representation (x:fixedpoint) (f:func_decl) (kinds:Symbol.symbol list) = Z3native.fixedpoint_set_predicate_representation (gc x) x f (List.length kinds) kinds let to_string_q (x:fixedpoint) (queries:expr list) = Z3native.fixedpoint_to_string (gc x) x (List.length queries) queries let get_rules (x:fixedpoint) = let av = Z3native.fixedpoint_get_rules (gc x) x in AST.ASTVector.to_expr_list av let get_assertions (x:fixedpoint) = let av = Z3native.fixedpoint_get_assertions (gc x) x in (AST.ASTVector.to_expr_list av) let mk_fixedpoint (ctx:context) = Z3native.mk_fixedpoint ctx let get_statistics (x:fixedpoint) = Z3native.fixedpoint_get_statistics (gc x) x let parse_string (x:fixedpoint) (s:string) = let av = Z3native.fixedpoint_from_string (gc x) x s in AST.ASTVector.to_expr_list av let parse_file (x:fixedpoint) (filename:string) = let av = Z3native.fixedpoint_from_file (gc x) x filename in AST.ASTVector.to_expr_list av end module Optimize = struct type optimize = Z3native.optimize type handle = { opt:optimize; h:int } let mk_handle opt h = { opt; h } let mk_opt (ctx:context) = Z3native.mk_optimize ctx let get_help (x:optimize) = Z3native.optimize_get_help (gc x) x let set_parameters (x:optimize) (p:Params.params) = Z3native.optimize_set_params (gc x) x p let get_param_descrs (x:optimize) = Z3native.optimize_get_param_descrs (gc x) x let add x constraints = List.iter (Z3native.optimize_assert (gc x) x) constraints let add_soft (x:optimize) (e:Expr.expr) (w:string) (s:Symbol.symbol) = mk_handle x (Z3native.optimize_assert_soft (gc x) x e w s) let maximize (x:optimize) (e:Expr.expr) = mk_handle x (Z3native.optimize_maximize (gc x) x e) let minimize (x:optimize) (e:Expr.expr) = mk_handle x (Z3native.optimize_minimize (gc x) x e) let check (x:optimize) = let r = lbool_of_int (Z3native.optimize_check (gc x) x 0 []) in match r with | L_TRUE -> Solver.SATISFIABLE | L_FALSE -> Solver.UNSATISFIABLE | _ -> Solver.UNKNOWN let get_model (x:optimize) = try let q = Z3native.optimize_get_model (gc x) x in if Z3native.is_null_model q then None else Some q with | _ -> None let get_lower (x:handle) = Z3native.optimize_get_lower (gc x.opt) x.opt x.h let get_upper (x:handle) = Z3native.optimize_get_upper (gc x.opt) x.opt x.h let push (x:optimize) = Z3native.optimize_push (gc x) x let pop (x:optimize) = Z3native.optimize_pop (gc x) x let get_reason_unknown (x:optimize) = Z3native.optimize_get_reason_unknown (gc x) x let to_string (x:optimize) = Z3native.optimize_to_string (gc x) x let get_statistics (x:optimize) = Z3native.optimize_get_statistics (gc x) x let from_file (x:optimize) (s:string) = Z3native.optimize_from_file (gc x) x s let from_string (x:optimize) (s:string) = Z3native.optimize_from_string (gc x) x s let get_assertions (x:optimize) = AST.ASTVector.to_expr_list (Z3native.optimize_get_assertions (gc x) x) let get_objectives (x:optimize) = AST.ASTVector.to_expr_list (Z3native.optimize_get_objectives (gc x) x) end module SMT = struct let benchmark_to_smtstring (ctx:context) (name:string) (logic:string) (status:string) (attributes:string) (assumptions:expr list) (formula:expr) = Z3native.benchmark_to_smtlib_string ctx name logic status attributes (List.length assumptions) assumptions formula let parse_smtlib2_string (ctx:context) (str:string) (sort_names:Symbol.symbol list) (sorts:Sort.sort list) (decl_names:Symbol.symbol list) (decls:func_decl list) = let csn = List.length sort_names in let cs = List.length sorts in let cdn = List.length decl_names in let cd = List.length decls in if csn <> cs || cdn <> cd then raise (Error "Argument size mismatch") else Z3native.parse_smtlib2_string ctx str cs sort_names sorts cd decl_names decls let parse_smtlib2_file (ctx:context) (file_name:string) (sort_names:Symbol.symbol list) (sorts:Sort.sort list) (decl_names:Symbol.symbol list) (decls:func_decl list) = let csn = List.length sort_names in let cs = List.length sorts in let cdn = List.length decl_names in let cd = List.length decls in if csn <> cs || cdn <> cd then raise (Error "Argument size mismatch") else Z3native.parse_smtlib2_file ctx file_name cs sort_names sorts cd decl_names decls end let set_global_param = Z3native.global_param_set let get_global_param id = match Z3native.global_param_get id with | (false, _) -> None | (true, v) -> Some v let global_param_reset_all = Z3native.global_param_reset_all 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 z3-z3-4.8.7/src/api/ml/z3.mli000066400000000000000000003710071356505360400154460ustar00rootroot00000000000000(** The Z3 ML/OCaml Interface. Copyright (C) 2012 Microsoft Corporation @author CM Wintersteiger (cwinter) 2012-12-17 *) (** General Z3 exceptions Many functions in this API may throw an exception; if they do, it is this one.*) exception Error of string (** Context objects. Most interactions with Z3 are interpreted in some context; many users will only require one such object, but power users may require more than one. To start using Z3, do let ctx = (mk_context []) in (...) where a list of pairs of strings may be passed to set options on the context, e.g., like so: let cfg = [("model", "true"); ("...", "...")] in let ctx = (mk_context cfg) in (...) *) type context (** Create a context object The following parameters can be set: - proof (Boolean) Enable proof generation - debug_ref_count (Boolean) Enable debug support for Z3_ast reference counting - trace (Boolean) Tracing support for VCC - trace_file_name (String) Trace out file for VCC traces - timeout (unsigned) default timeout (in milliseconds) used for solvers - well_sorted_check type checker - auto_config use heuristics to automatically select solver and configure it - model model generation for solvers, this parameter can be overwritten when creating a solver - model_validate validate models produced by solvers - unsat_core unsat-core generation for solvers, this parameter can be overwritten when creating a solver *) val mk_context : (string * string) list -> context (** Interaction logging for Z3 Note that this is a global, static log and if multiple Context objects are created, it logs the interaction with all of them. *) module Log : sig (** Open an interaction log file. @return True if opening the log file succeeds, false otherwise. *) (* CMW: "open" is a reserved keyword. *) val open_ : string -> bool (** Closes the interaction log. *) val close : unit -> unit (** Appends a user-provided string to the interaction log. *) val append : string -> unit end (** Version information *) module Version : sig (** The major version. *) val major : int (** The minor version. *) val minor : int (** The build version. *) val build : int (** The revision. *) val revision : int (** A full version string. *) val full_version : string (** A string representation of the version information. *) val to_string : string end (** Symbols are used to name several term and type constructors *) module Symbol : sig type symbol (** The kind of the symbol (int or string) *) val kind : symbol -> Z3enums.symbol_kind (** Indicates whether the symbol is of Int kind *) val is_int_symbol : symbol -> bool (** Indicates whether the symbol is of string kind. *) val is_string_symbol : symbol -> bool (** The int value of the symbol. *) val get_int : symbol -> int (** The string value of the symbol. *) val get_string : symbol -> string (** A string representation of the symbol. *) val to_string : symbol -> string (** Creates a new symbol using an integer. Not all integers can be passed to this function. The legal range of unsigned integers is 0 to 2^30-1. *) val mk_int : context -> int -> symbol (** Creates a new symbol using a string. *) val mk_string : context -> string -> symbol (** Create a list of symbols. *) val mk_ints : context -> int list -> symbol list (** Create a list of symbols. *) val mk_strings : context -> string list -> symbol list end (** The abstract syntax tree (AST) module *) module rec AST : sig type ast (** Vectors of ASTs *) module ASTVector : sig type ast_vector (** Create an empty AST vector *) val mk_ast_vector : context -> ast_vector (** The size of the vector *) val get_size : ast_vector -> int (** Retrieves the i-th object in the vector. @return An AST *) val get : ast_vector -> int -> ast (** Sets the i-th object in the vector. *) val set : ast_vector -> int -> ast -> unit (** Resize the vector to a new size. *) val resize : ast_vector -> int -> unit (** Add an ast to the back of the vector. The size is increased by 1. *) val push : ast_vector -> ast -> unit (** Translates all ASTs in the vector to another context. @return A new ASTVector *) val translate : ast_vector -> context -> ast_vector (** Translates the ASTVector into an (Ast.ast list) *) val to_list : ast_vector -> ast list (** Translates the ASTVector into an (Expr.expr list) *) val to_expr_list : ast_vector -> Expr.expr list (** Retrieves a string representation of the vector. *) val to_string : ast_vector -> string end (** Map from AST to AST *) module ASTMap : sig type ast_map (** Create an empty mapping from AST to AST *) val mk_ast_map : context -> ast_map (** Checks whether the map contains a key. @return True if the key in the map, false otherwise. *) val contains : ast_map -> ast -> bool (** Finds the value associated with the key. This function signs an error when the key is not a key in the map. *) val find : ast_map -> ast -> ast (** Stores or replaces a new key/value pair in the map. *) val insert : ast_map -> ast -> ast -> unit (** Erases the key from the map.*) val erase : ast_map -> ast -> unit (** Removes all keys from the map. *) val reset : ast_map -> unit (** The size of the map *) val get_size : ast_map -> int (** The keys stored in the map. *) val get_keys : ast_map -> ast list (** Retrieves a string representation of the map.*) val to_string : ast_map -> string end (** The AST's hash code. @return A hash code *) val hash : ast -> int (** A unique identifier for the AST (unique among all ASTs). *) val get_id : ast -> int (** The kind of the AST. *) val get_ast_kind : ast -> Z3enums.ast_kind (** Indicates whether the AST is an Expr *) val is_expr : ast -> bool (** Indicates whether the AST is a bound variable*) val is_var : ast -> bool (** Indicates whether the AST is a Quantifier *) val is_quantifier : ast -> bool (** Indicates whether the AST is a Sort *) val is_sort : ast -> bool (** Indicates whether the AST is a func_decl *) val is_func_decl : ast -> bool (** A string representation of the AST. *) val to_string : ast -> string (** A string representation of the AST in s-expression notation. *) val to_sexpr : ast -> string (** Comparison operator. @return True if the two ast's are from the same context and represent the same sort; false otherwise. *) val equal : ast -> ast -> bool (** Object Comparison. @return Negative if the first ast should be sorted before the second, positive if after else zero. *) val compare : ast -> ast -> int (** Translates (copies) the AST to another context. @return A copy of the AST which is associated with the other context. *) val translate : ast -> context -> ast end (** The Sort module implements type information for ASTs *) and Sort : sig type sort (** Comparison operator. @return True if the two sorts are from the same context and represent the same sort; false otherwise. *) val equal : sort -> sort -> bool (** Returns a unique identifier for the sort. *) val get_id : sort -> int (** The kind of the sort. *) val get_sort_kind : sort -> Z3enums.sort_kind (** The name of the sort *) val get_name : sort -> Symbol.symbol (** A string representation of the sort. *) val to_string : sort -> string (** Create a new uninterpreted sort. *) val mk_uninterpreted : context -> Symbol.symbol -> sort (** Create a new uninterpreted sort. *) val mk_uninterpreted_s : context -> string -> sort end (** Function declarations *) and FuncDecl : sig type func_decl (** Parameters of Func_Decls *) module Parameter : sig (** Parameters of func_decls *) type parameter = P_Int of int | P_Dbl of float | P_Sym of Symbol.symbol | P_Srt of Sort.sort | P_Ast of AST.ast | P_Fdl of func_decl | P_Rat of string (** The kind of the parameter. *) val get_kind : parameter -> Z3enums.parameter_kind (** The int value of the parameter.*) val get_int : parameter -> int (** The float value of the parameter.*) val get_float : parameter -> float (** The Symbol.Symbol value of the parameter.*) val get_symbol : parameter -> Symbol.symbol (** The Sort value of the parameter.*) val get_sort : parameter -> Sort.sort (** The AST value of the parameter.*) val get_ast : parameter -> AST.ast (** The FunctionDeclaration value of the parameter.*) val get_func_decl : parameter -> func_decl (** The rational string value of the parameter.*) val get_rational : parameter -> string end (** Creates a new function declaration. *) val mk_func_decl : context -> Symbol.symbol -> Sort.sort list -> Sort.sort -> func_decl (** Creates a new function declaration. *) val mk_func_decl_s : context -> string -> Sort.sort list -> Sort.sort -> func_decl (** Creates a fresh function declaration with a name prefixed with a prefix string. *) val mk_fresh_func_decl : context -> string -> Sort.sort list -> Sort.sort -> func_decl (** Creates a new constant function declaration. *) val mk_const_decl : context -> Symbol.symbol -> Sort.sort -> func_decl (** Creates a new constant function declaration. *) val mk_const_decl_s : context -> string -> Sort.sort -> func_decl (** Creates a fresh constant function declaration with a name prefixed with a prefix string. {!mk_func_decl} {!mk_func_decl} *) val mk_fresh_const_decl : context -> string -> Sort.sort -> func_decl (** Comparison operator. @return True if a and b are from the same context and represent the same func_decl; false otherwise. *) val equal : func_decl -> func_decl -> bool (** A string representations of the function declaration. *) val to_string : func_decl -> string (** Returns a unique identifier for the function declaration. *) val get_id : func_decl -> int (** The arity of the function declaration *) val get_arity : func_decl -> int (** The size of the domain of the function declaration {!get_arity} *) val get_domain_size : func_decl -> int (** The domain of the function declaration *) val get_domain : func_decl -> Sort.sort list (** The range of the function declaration *) val get_range : func_decl -> Sort.sort (** The kind of the function declaration. *) val get_decl_kind : func_decl -> Z3enums.decl_kind (** The name of the function declaration*) val get_name : func_decl -> Symbol.symbol (** The number of parameters of the function declaration *) val get_num_parameters : func_decl -> int (** The parameters of the function declaration *) val get_parameters : func_decl -> Parameter.parameter list (** Create expression that applies function to arguments. *) val apply : func_decl -> Expr.expr list -> Expr.expr end (** Parameter sets (of Solvers, Tactics, ...) A Params objects represents a configuration in the form of Symbol.symbol/value pairs. *) and Params : sig type params (** ParamDescrs describe sets of parameters (of Solvers, Tactics, ...) *) module ParamDescrs : sig type param_descrs (** Validate a set of parameters. *) val validate : param_descrs -> params -> unit (** Retrieve kind of parameter. *) val get_kind : param_descrs -> Symbol.symbol -> Z3enums.param_kind (** Retrieve all names of parameters. *) val get_names : param_descrs -> Symbol.symbol list (** The size of the ParamDescrs. *) val get_size : param_descrs -> int (** Retrieves a string representation of the ParamDescrs. *) val to_string : param_descrs -> string end (** Adds a parameter setting. *) val add_bool : params -> Symbol.symbol -> bool -> unit (** Adds a parameter setting. *) val add_int : params -> Symbol.symbol -> int -> unit (** Adds a parameter setting. *) val add_float : params -> Symbol.symbol -> float -> unit (** Adds a parameter setting. *) val add_symbol : params -> Symbol.symbol -> Symbol.symbol -> unit (** Creates a new parameter set *) val mk_params : context -> params (** A string representation of the parameter set. *) val to_string : params -> string (** Update a mutable configuration parameter. The list of all configuration parameters can be obtained using the Z3 executable: [z3.exe -p] Only a few configuration parameters are mutable once the context is created. An exception is thrown when trying to modify an immutable parameter. *) val update_param_value : context -> string -> string -> unit (** Selects the format used for pretty-printing expressions. The default mode for pretty printing expressions is to produce SMT-LIB style output where common subexpressions are printed at each occurrence. The mode is called PRINT_SMTLIB_FULL. To print shared common subexpressions only once, use the PRINT_LOW_LEVEL mode. To print in way that conforms to SMT-LIB standards and uses let expressions to share common sub-expressions use PRINT_SMTLIB_COMPLIANT. {!AST.to_string} {!Quantifier.Pattern.to_string} {!FuncDecl.to_string} {!Sort.to_string} *) val set_print_mode : context -> Z3enums.ast_print_mode -> unit end (** General Expressions (terms) *) and Expr : sig type expr val ast_of_expr : Expr.expr -> AST.ast val expr_of_ast : AST.ast -> Expr.expr (** Returns a simplified version of the expression. {!get_simplify_help} *) val simplify : Expr.expr -> Params.params option -> expr (** A string describing all available parameters to [Expr.Simplify]. *) val get_simplify_help : context -> string (** Retrieves parameter descriptions for simplifier. *) val get_simplify_parameter_descrs : context -> Params.ParamDescrs.param_descrs (** The function declaration of the function that is applied in this expression. *) val get_func_decl : Expr.expr -> FuncDecl.func_decl (** The number of arguments of the expression. *) val get_num_args : Expr.expr -> int (** The arguments of the expression. *) val get_args : Expr.expr -> Expr.expr list (** Update the arguments of the expression using an array of expressions. The number of new arguments should coincide with the current number of arguments. *) val update : Expr.expr -> Expr.expr list -> expr (** Substitute every occurrence of [from[i]] in the expression with [to[i]], for [i] smaller than [num_exprs]. The result is the new expression. The arrays [from] and [to] must have size [num_exprs]. For every [i] smaller than [num_exprs], we must have that sort of [from[i]] must be equal to sort of [to[i]]. *) val substitute : Expr.expr -> Expr.expr list -> Expr.expr list -> expr (** Substitute every occurrence of [from] in the expression with [to]. {!substitute} *) val substitute_one : Expr.expr -> Expr.expr -> Expr.expr -> expr (** Substitute the free variables in the expression with the expressions in the expr array For every [i] smaller than [num_exprs], the variable with de-Bruijn index [i] is replaced with term [to[i]]. *) val substitute_vars : Expr.expr -> Expr.expr list -> expr (** Translates (copies) the term to another context. @return A copy of the term which is associated with the other context *) val translate : Expr.expr -> context -> expr (** Returns a string representation of the expression. *) val to_string : Expr.expr -> string (** Indicates whether the term is a numeral *) val is_numeral : Expr.expr -> bool (** Indicates whether the term is well-sorted. @return True if the term is well-sorted, false otherwise. *) val is_well_sorted : Expr.expr -> bool (** The Sort of the term. *) val get_sort : Expr.expr -> Sort.sort (** Indicates whether the term represents a constant. *) val is_const : Expr.expr -> bool (** Creates a new constant. *) val mk_const : context -> Symbol.symbol -> Sort.sort -> expr (** Creates a new constant. *) val mk_const_s : context -> string -> Sort.sort -> expr (** Creates a constant from the func_decl. *) val mk_const_f : context -> FuncDecl.func_decl -> expr (** Creates a fresh constant with a name prefixed with a string. *) val mk_fresh_const : context -> string -> Sort.sort -> expr (** Create a new function application. *) val mk_app : context -> FuncDecl.func_decl -> Expr.expr list -> expr (** Create a numeral of a given sort. @return A Term with the given value and sort *) val mk_numeral_string : context -> string -> Sort.sort -> expr (** Create a numeral of a given sort. This function can be used to create numerals that fit in a machine integer. It is slightly faster than [MakeNumeral] since it is not necessary to parse a string. @return A Term with the given value and sort *) val mk_numeral_int : context -> int -> Sort.sort -> expr (** Comparison operator. @return True if the two expr's are equal; false otherwise. *) val equal : expr -> expr -> bool (** Object Comparison. @return Negative if the first expr should be sorted before the second, positive if after, else zero. *) val compare : expr -> expr -> int end (** Boolean expressions; Propositional logic and equality *) module Boolean : sig (** Create a Boolean sort *) val mk_sort : context -> Sort.sort (** Create a Boolean constant. *) val mk_const : context -> Symbol.symbol -> Expr.expr (** Create a Boolean constant. *) val mk_const_s : context -> string -> Expr.expr (** The true Term. *) val mk_true : context -> Expr.expr (** The false Term. *) val mk_false : context -> Expr.expr (** Creates a Boolean value. *) val mk_val : context -> bool -> Expr.expr (** Mk an expression representing [not(a)]. *) val mk_not : context -> Expr.expr -> Expr.expr (** Create an expression representing an if-then-else: [ite(t1, t2, t3)]. *) val mk_ite : context -> Expr.expr -> Expr.expr -> Expr.expr -> Expr.expr (** Create an expression representing [t1 iff t2]. *) val mk_iff : context -> Expr.expr -> Expr.expr -> Expr.expr (** Create an expression representing [t1 -> t2]. *) val mk_implies : context -> Expr.expr -> Expr.expr -> Expr.expr (** Create an expression representing [t1 xor t2]. *) val mk_xor : context -> Expr.expr -> Expr.expr -> Expr.expr (** Create an expression representing the AND of args *) val mk_and : context -> Expr.expr list -> Expr.expr (** Create an expression representing the OR of args *) val mk_or : context -> Expr.expr list -> Expr.expr (** Creates the equality between two expr's. *) val mk_eq : context -> Expr.expr -> Expr.expr -> Expr.expr (** Creates a [distinct] term. *) val mk_distinct : context -> Expr.expr list -> Expr.expr (** Indicates whether the expression is the true or false expression or something else (L_UNDEF). *) val get_bool_value : Expr.expr -> Z3enums.lbool (** Indicates whether the term has Boolean sort. *) val is_bool : Expr.expr -> bool (** Indicates whether the term is the constant true. *) val is_true : Expr.expr -> bool (** Indicates whether the term is the constant false. *) val is_false : Expr.expr -> bool (** Indicates whether the term is an equality predicate. *) val is_eq : Expr.expr -> bool (** Indicates whether the term is an n-ary distinct predicate (every argument is mutually distinct). *) val is_distinct : Expr.expr -> bool (** Indicates whether the term is a ternary if-then-else term *) val is_ite : Expr.expr -> bool (** Indicates whether the term is an n-ary conjunction *) val is_and : Expr.expr -> bool (** Indicates whether the term is an n-ary disjunction *) val is_or : Expr.expr -> bool (** Indicates whether the term is an if-and-only-if (Boolean equivalence, binary) *) val is_iff : Expr.expr -> bool (** Indicates whether the term is an exclusive or *) val is_xor : Expr.expr -> bool (** Indicates whether the term is a negation *) val is_not : Expr.expr -> bool (** Indicates whether the term is an implication *) val is_implies : Expr.expr -> bool end (** Quantifier expressions *) module Quantifier : sig type quantifier val expr_of_quantifier : quantifier -> Expr.expr val quantifier_of_expr : Expr.expr -> quantifier (** Quantifier patterns Patterns comprise a list of terms. The list should be non-empty. If the list comprises of more than one term, it is also called a multi-pattern. *) module Pattern : sig type pattern (** The number of terms in the pattern. *) val get_num_terms : pattern -> int (** The terms in the pattern. *) val get_terms : pattern -> Expr.expr list (** A string representation of the pattern. *) val to_string : pattern -> string end (** The de-Bruijn index of a bound variable. Bound variables are indexed by de-Bruijn indices. It is perhaps easiest to explain the meaning of de-Bruijn indices by indicating the compilation process from non-de-Bruijn formulas to de-Bruijn format. abs(forall (x1) phi) = forall (x1) abs1(phi, x1, 0) abs(forall (x1, x2) phi) = abs(forall (x1) abs(forall (x2) phi)) abs1(x, x, n) = b_n abs1(y, x, n) = y abs1(f(t1,...,tn), x, n) = f(abs1(t1,x,n), ..., abs1(tn,x,n)) abs1(forall (x1) phi, x, n) = forall (x1) (abs1(phi, x, n+1)) The last line is significant: the index of a bound variable is different depending on the scope in which it appears. The deeper ( x : expr ) appears, the higher is its index. *) val get_index : Expr.expr -> int (** Indicates whether the quantifier is universal. *) val is_universal : quantifier -> bool (** Indicates whether the quantifier is existential. *) val is_existential : quantifier -> bool (** The weight of the quantifier. *) val get_weight : quantifier -> int (** The number of patterns. *) val get_num_patterns : quantifier -> int (** The patterns. *) val get_patterns : quantifier -> Pattern.pattern list (** The number of no-patterns. *) val get_num_no_patterns : quantifier -> int (** The no-patterns. *) val get_no_patterns : quantifier -> Pattern.pattern list (** The number of bound variables. *) val get_num_bound : quantifier -> int (** The symbols for the bound variables. *) val get_bound_variable_names : quantifier -> Symbol.symbol list (** The sorts of the bound variables. *) val get_bound_variable_sorts : quantifier -> Sort.sort list (** The body of the quantifier. *) val get_body : quantifier -> Expr.expr (** Creates a new bound variable. *) val mk_bound : context -> int -> Sort.sort -> Expr.expr (** Create a quantifier pattern. *) val mk_pattern : context -> Expr.expr list -> Pattern.pattern (** Create a universal Quantifier. *) val mk_forall : context -> Sort.sort list -> Symbol.symbol list -> Expr.expr -> int option -> Pattern.pattern list -> Expr.expr list -> Symbol.symbol option -> Symbol.symbol option -> quantifier (** Create a universal Quantifier. *) val mk_forall_const : context -> Expr.expr list -> Expr.expr -> int option -> Pattern.pattern list -> Expr.expr list -> Symbol.symbol option -> Symbol.symbol option -> quantifier (** Create an existential Quantifier. *) val mk_exists : context -> Sort.sort list -> Symbol.symbol list -> Expr.expr -> int option -> Pattern.pattern list -> Expr.expr list -> Symbol.symbol option -> Symbol.symbol option -> quantifier (** Create an existential Quantifier. *) val mk_exists_const : context -> Expr.expr list -> Expr.expr -> int option -> Pattern.pattern list -> Expr.expr list -> Symbol.symbol option -> Symbol.symbol option -> quantifier (** Create a lambda binding. *) val mk_lambda_const : context -> Expr.expr list -> Expr.expr -> quantifier (** Create a lambda binding where bound variables are given by symbols and sorts *) val mk_lambda : context -> (Symbol.symbol * Sort.sort) list -> Expr.expr -> quantifier (** Create a Quantifier. *) val mk_quantifier : context -> bool -> Sort.sort list -> Symbol.symbol list -> Expr.expr -> int option -> Pattern.pattern list -> Expr.expr list -> Symbol.symbol option -> Symbol.symbol option -> quantifier (** Create a Quantifier. *) val mk_quantifier_const : context -> bool -> Expr.expr list -> Expr.expr -> int option -> Pattern.pattern list -> Expr.expr list -> Symbol.symbol option -> Symbol.symbol option -> quantifier (** A string representation of the quantifier. *) val to_string : quantifier -> string end (** Functions to manipulate Array expressions *) module Z3Array : sig (** Create a new array sort. *) val mk_sort : context -> Sort.sort -> Sort.sort -> Sort.sort (** Indicates whether the term is an array store. It satisfies select(store(a,i,v),j) = if i = j then v else select(a,j). Array store takes at least 3 arguments. *) val is_store : Expr.expr -> bool (** Indicates whether the term is an array select. *) val is_select : Expr.expr -> bool (** Indicates whether the term is a constant array. For example, select(const(v),i) = v holds for every v and i. The function is unary. *) val is_constant_array : Expr.expr -> bool (** Indicates whether the term is a default array. For example default(const(v)) = v. The function is unary. *) val is_default_array : Expr.expr -> bool (** Indicates whether the term is an array map. It satisfies map[f](a1,..,a_n)[i] = f(a1[i],...,a_n[i]) for every i. *) val is_array_map : Expr.expr -> bool (** Indicates whether the term is an as-array term. An as-array term is n array value that behaves as the function graph of the function passed as parameter. *) val is_as_array : Expr.expr -> bool (** Indicates whether the term is of an array sort. *) val is_array : Expr.expr -> bool (** The domain of the array sort. *) val get_domain : Sort.sort -> Sort.sort (** The range of the array sort. *) val get_range : Sort.sort -> Sort.sort (** Create an array constant. *) val mk_const : context -> Symbol.symbol -> Sort.sort -> Sort.sort -> Expr.expr (** Create an array constant. *) val mk_const_s : context -> string -> Sort.sort -> Sort.sort -> Expr.expr (** Array read. The argument [a] is the array and [i] is the index of the array that gets read. The node [a] must have an array sort [[domain -> range]], and [i] must have the sort [domain]. The sort of the result is [range]. {!Z3Array.mk_sort} {!mk_store} *) val mk_select : context -> Expr.expr -> Expr.expr -> Expr.expr (** Array update. The node [a] must have an array sort [[domain -> range]], [i] must have sort [domain], [v] must have sort range. The sort of the result is [[domain -> range]]. The semantics of this function is given by the theory of arrays described in the SMT-LIB standard. See http://smtlib.org for more details. The result of this function is an array that is equal to [a] (with respect to [select]) on all indices except for [i], where it maps to [v] (and the [select] of [a] with respect to [i] may be a different value). {!Z3Array.mk_sort} {!mk_select} *) val mk_store : context -> Expr.expr -> Expr.expr -> Expr.expr -> Expr.expr (** Create a constant array. The resulting term is an array, such that a [select]on an arbitrary index produces the value [v]. {!Z3Array.mk_sort} {!mk_select} *) val mk_const_array : context -> Sort.sort -> Expr.expr -> Expr.expr (** Maps f on the argument arrays. Each element of [args] must be of an array sort [[domain_i -> range_i]]. The function declaration [f] must have type [ range_1 .. range_n -> range]. [v] must have sort range. The sort of the result is [[domain_i -> range]]. {!Z3Array.mk_sort} {!mk_select} {!mk_store} *) val mk_map : context -> FuncDecl.func_decl -> Expr.expr list -> Expr.expr (** Access the array default value. Produces the default range value, for arrays that can be represented as finite maps with a default range value. *) val mk_term_array : context -> Expr.expr -> Expr.expr (** Create array extensionality index given two arrays with the same sort. The meaning is given by the axiom: (=> (= (select A (array-ext A B)) (select B (array-ext A B))) (= A B)) *) val mk_array_ext : context -> Expr.expr -> Expr.expr -> Expr.expr end (** Functions to manipulate Set expressions *) module Set : sig (** Create a set type. *) val mk_sort : context -> Sort.sort -> Sort.sort (** Indicates whether the term is set union *) val is_union : Expr.expr -> bool (** Indicates whether the term is set intersection *) val is_intersect : Expr.expr -> bool (** Indicates whether the term is set difference *) val is_difference : Expr.expr -> bool (** Indicates whether the term is set complement *) val is_complement : Expr.expr -> bool (** Indicates whether the term is set subset *) val is_subset : Expr.expr -> bool (** Create an empty set. *) val mk_empty : context -> Sort.sort -> Expr.expr (** Create the full set. *) val mk_full : context -> Sort.sort -> Expr.expr (** Add an element to the set. *) val mk_set_add : context -> Expr.expr -> Expr.expr -> Expr.expr (** Remove an element from a set. *) val mk_del : context -> Expr.expr -> Expr.expr -> Expr.expr (** Take the union of a list of sets. *) val mk_union : context -> Expr.expr list -> Expr.expr (** Take the intersection of a list of sets. *) val mk_intersection : context -> Expr.expr list -> Expr.expr (** Take the difference between two sets. *) val mk_difference : context -> Expr.expr -> Expr.expr -> Expr.expr (** Take the complement of a set. *) val mk_complement : context -> Expr.expr -> Expr.expr (** Check for set membership. *) val mk_membership : context -> Expr.expr -> Expr.expr -> Expr.expr (** Check for subsetness of sets. *) val mk_subset : context -> Expr.expr -> Expr.expr -> Expr.expr end (** Functions to manipulate Finite Domain expressions *) module FiniteDomain : sig (** Create a new finite domain sort. *) val mk_sort : context -> Symbol.symbol -> int -> Sort.sort (** Create a new finite domain sort. *) val mk_sort_s : context -> string -> int -> Sort.sort (** Indicates whether the term is of an array sort. *) val is_finite_domain : Expr.expr -> bool (** Indicates whether the term is a less than predicate over a finite domain. *) val is_lt : Expr.expr -> bool (** The size of the finite domain sort. *) val get_size : Sort.sort -> int end (** Functions to manipulate Relation expressions *) module Relation : sig (** Indicates whether the term is of a relation sort. *) val is_relation : Expr.expr -> bool (** Indicates whether the term is an relation store Insert a record into a relation. The function takes [n+1] arguments, where the first argument is the relation and the remaining [n] elements correspond to the [n] columns of the relation. *) val is_store : Expr.expr -> bool (** Indicates whether the term is an empty relation *) val is_empty : Expr.expr -> bool (** Indicates whether the term is a test for the emptiness of a relation *) val is_is_empty : Expr.expr -> bool (** Indicates whether the term is a relational join *) val is_join : Expr.expr -> bool (** Indicates whether the term is the union or convex hull of two relations. The function takes two arguments. *) val is_union : Expr.expr -> bool (** Indicates whether the term is the widening of two relations The function takes two arguments. *) val is_widen : Expr.expr -> bool (** Indicates whether the term is a projection of columns (provided as numbers in the parameters). The function takes one argument. *) val is_project : Expr.expr -> bool (** Indicates whether the term is a relation filter Filter (restrict) a relation with respect to a predicate. The first argument is a relation. The second argument is a predicate with free de-Bruijn indices corresponding to the columns of the relation. So the first column in the relation has index 0. *) val is_filter : Expr.expr -> bool (** Indicates whether the term is an intersection of a relation with the negation of another. Intersect the first relation with respect to negation of the second relation (the function takes two arguments). Logically, the specification can be described by a function target = filter_by_negation(pos, neg, columns) where columns are pairs c1, d1, .., cN, dN of columns from pos and neg, such that target are elements in ( x : expr ) in pos, such that there is no y in neg that agrees with ( x : expr ) on the columns c1, d1, .., cN, dN. *) val is_negation_filter : Expr.expr -> bool (** Indicates whether the term is the renaming of a column in a relation The function takes one argument. The parameters contain the renaming as a cycle. *) val is_rename : Expr.expr -> bool (** Indicates whether the term is the complement of a relation *) val is_complement : Expr.expr -> bool (** Indicates whether the term is a relational select Check if a record is an element of the relation. The function takes [n+1] arguments, where the first argument is a relation, and the remaining [n] arguments correspond to a record. *) val is_select : Expr.expr -> bool (** Indicates whether the term is a relational clone (copy) Create a fresh copy (clone) of a relation. The function is logically the identity, but in the context of a register machine allows for terms of kind {!is_union} to perform destructive updates to the first argument. *) val is_clone : Expr.expr -> bool (** The arity of the relation sort. *) val get_arity : Sort.sort -> int (** The sorts of the columns of the relation sort. *) val get_column_sorts : Sort.sort -> Sort.sort list end (** Functions to manipulate Datatype expressions *) module Datatype : sig (** Datatype Constructors *) module Constructor : sig type constructor (** The number of fields of the constructor. *) val get_num_fields : constructor -> int (** The function declaration of the constructor. *) val get_constructor_decl : constructor -> FuncDecl.func_decl (** The function declaration of the tester. *) val get_tester_decl : constructor -> FuncDecl.func_decl (** The function declarations of the accessors *) val get_accessor_decls : constructor -> FuncDecl.func_decl list end (** Create a datatype constructor. if the corresponding sort reference is 0, then the value in sort_refs should be an index referring to one of the recursive datatypes that is declared. *) val mk_constructor : context -> Symbol.symbol -> Symbol.symbol -> Symbol.symbol list -> Sort.sort option list -> int list -> Constructor.constructor (** Create a datatype constructor. if the corresponding sort reference is 0, then the value in sort_refs should be an index referring to one of the recursive datatypes that is declared. *) val mk_constructor_s : context -> string -> Symbol.symbol -> Symbol.symbol list -> Sort.sort option list -> int list -> Constructor.constructor (** Create a new datatype sort. *) val mk_sort : context -> Symbol.symbol -> Constructor.constructor list -> Sort.sort (** Create a new datatype sort. *) val mk_sort_s : context -> string -> Constructor.constructor list -> Sort.sort (** Create mutually recursive datatypes. *) val mk_sorts : context -> Symbol.symbol list -> Constructor.constructor list list -> Sort.sort list (** Create mutually recursive data-types. *) val mk_sorts_s : context -> string list -> Constructor.constructor list list -> Sort.sort list (** The number of constructors of the datatype sort. *) val get_num_constructors : Sort.sort -> int (** The constructors. *) val get_constructors : Sort.sort -> FuncDecl.func_decl list (** The recognizers. *) val get_recognizers : Sort.sort -> FuncDecl.func_decl list (** The constructor accessors. *) val get_accessors : Sort.sort -> FuncDecl.func_decl list list end (** Functions to manipulate Enumeration expressions *) module Enumeration : sig (** Create a new enumeration sort. *) val mk_sort : context -> Symbol.symbol -> Symbol.symbol list -> Sort.sort (** Create a new enumeration sort. *) val mk_sort_s : context -> string -> string list -> Sort.sort (** The function declarations of the constants in the enumeration. *) val get_const_decls : Sort.sort -> FuncDecl.func_decl list (** Retrieves the inx'th constant declaration in the enumeration. *) val get_const_decl : Sort.sort -> int -> FuncDecl.func_decl (** The constants in the enumeration. *) val get_consts : Sort.sort -> Expr.expr list (** Retrieves the inx'th constant in the enumeration. *) val get_const : Sort.sort -> int -> Expr.expr (** The test predicates for the constants in the enumeration. *) val get_tester_decls : Sort.sort -> FuncDecl.func_decl list (** Retrieves the inx'th tester/recognizer declaration in the enumeration. *) val get_tester_decl : Sort.sort -> int -> FuncDecl.func_decl end (** Functions to manipulate List expressions *) module Z3List : sig (** Create a new list sort. *) val mk_sort : context -> Symbol.symbol -> Sort.sort -> Sort.sort (** Create a new list sort. *) val mk_list_s : context -> string -> Sort.sort -> Sort.sort (** The declaration of the nil function of this list sort. *) val get_nil_decl : Sort.sort -> FuncDecl.func_decl (** The declaration of the isNil function of this list sort. *) val get_is_nil_decl : Sort.sort -> FuncDecl.func_decl (** The declaration of the cons function of this list sort. *) val get_cons_decl : Sort.sort -> FuncDecl.func_decl (** The declaration of the isCons function of this list sort. *) val get_is_cons_decl : Sort.sort -> FuncDecl.func_decl (** The declaration of the head function of this list sort. *) val get_head_decl : Sort.sort -> FuncDecl.func_decl (** The declaration of the tail function of this list sort. *) val get_tail_decl : Sort.sort -> FuncDecl.func_decl (** The empty list. *) val nil : Sort.sort -> Expr.expr end (** Functions to manipulate Tuple expressions *) module Tuple : sig (** Create a new tuple sort. *) val mk_sort : context -> Symbol.symbol -> Symbol.symbol list -> Sort.sort list -> Sort.sort (** The constructor function of the tuple. *) val get_mk_decl : Sort.sort -> FuncDecl.func_decl (** The number of fields in the tuple. *) val get_num_fields : Sort.sort -> int (** The field declarations. *) val get_field_decls : Sort.sort -> FuncDecl.func_decl list end (** Functions to manipulate arithmetic expressions *) module Arithmetic : sig (** Integer Arithmetic *) module Integer : sig (** Create a new integer sort. *) val mk_sort : context -> Sort.sort (** Get a big_int from an integer numeral *) val get_big_int : Expr.expr -> Z.t (** Returns a string representation of a numeral. *) val numeral_to_string : Expr.expr -> string (** Creates an integer constant. *) val mk_const : context -> Symbol.symbol -> Expr.expr (** Creates an integer constant. *) val mk_const_s : context -> string -> Expr.expr (** Create an expression representing [t1 mod t2]. The arguments must have int type. *) val mk_mod : context -> Expr.expr -> Expr.expr -> Expr.expr (** Create an expression representing [t1 rem t2]. The arguments must have int type. *) val mk_rem : context -> Expr.expr -> Expr.expr -> Expr.expr (** Create an integer numeral. *) val mk_numeral_s : context -> string -> Expr.expr (** Create an integer numeral. @return A Term with the given value and sort Integer *) val mk_numeral_i : context -> int -> Expr.expr (** Coerce an integer to a real. There is also a converse operation exposed. It follows the semantics prescribed by the SMT-LIB standard. You can take the floor of a real by creating an auxiliary integer Term [k] and and asserting [MakeInt2Real(k) <= t1 < MkInt2Real(k)+1]. The argument must be of integer sort. *) val mk_int2real : context -> Expr.expr -> Expr.expr (** Create an n-bit bit-vector from an integer argument. NB. This function is essentially treated as uninterpreted. So you cannot expect Z3 to precisely reflect the semantics of this function when solving constraints with this function. The argument must be of integer sort. *) val mk_int2bv : context -> int -> Expr.expr -> Expr.expr end (** Real Arithmetic *) module Real : sig (** Create a real sort. *) val mk_sort : context -> Sort.sort (** The numerator of a rational numeral. *) val get_numerator : Expr.expr -> Expr.expr (** The denominator of a rational numeral. *) val get_denominator : Expr.expr -> Expr.expr (** Get a ratio from a real numeral *) val get_ratio : Expr.expr -> Q.t (** Returns a string representation in decimal notation. The result has at most as many decimal places as indicated by the int argument.*) val to_decimal_string : Expr.expr-> int -> string (** Returns a string representation of a numeral. *) val numeral_to_string : Expr.expr-> string (** Creates a real constant. *) val mk_const : context -> Symbol.symbol -> Expr.expr (** Creates a real constant. *) val mk_const_s : context -> string -> Expr.expr (** Create a real numeral from a fraction. @return A Term with rational value and sort Real {!mk_numeral_s} *) val mk_numeral_nd : context -> int -> int -> Expr.expr (** Create a real numeral. @return A Term with the given value and sort Real *) val mk_numeral_s : context -> string -> Expr.expr (** Create a real numeral. @return A Term with the given value and sort Real *) val mk_numeral_i : context -> int -> Expr.expr (** Creates an expression that checks whether a real number is an integer. *) val mk_is_integer : context -> Expr.expr -> Expr.expr (** Coerce a real to an integer. The semantics of this function follows the SMT-LIB standard for the function to_int. The argument must be of real sort. *) val mk_real2int : context -> Expr.expr -> Expr.expr (** Algebraic Numbers *) module AlgebraicNumber : sig (** Return a upper bound for a given real algebraic number. The interval isolating the number is smaller than 1/10^precision. {!is_algebraic_number} @return A numeral Expr of sort Real *) val to_upper : Expr.expr -> int -> Expr.expr (** Return a lower bound for the given real algebraic number. The interval isolating the number is smaller than 1/10^precision. {!is_algebraic_number} @return A numeral Expr of sort Real *) val to_lower : Expr.expr -> int -> Expr.expr (** Returns a string representation in decimal notation. The result has at most as many decimal places as the int argument provided.*) val to_decimal_string : Expr.expr -> int -> string (** Returns a string representation of a numeral. *) val numeral_to_string : Expr.expr -> string end end (** Indicates whether the term is of integer sort. *) val is_int : Expr.expr -> bool (** Indicates whether the term is an arithmetic numeral. *) val is_arithmetic_numeral : Expr.expr -> bool (** Indicates whether the term is a less-than-or-equal *) val is_le : Expr.expr -> bool (** Indicates whether the term is a greater-than-or-equal *) val is_ge : Expr.expr -> bool (** Indicates whether the term is a less-than *) val is_lt : Expr.expr -> bool (** Indicates whether the term is a greater-than *) val is_gt : Expr.expr -> bool (** Indicates whether the term is addition (binary) *) val is_add : Expr.expr -> bool (** Indicates whether the term is subtraction (binary) *) val is_sub : Expr.expr -> bool (** Indicates whether the term is a unary minus *) val is_uminus : Expr.expr -> bool (** Indicates whether the term is multiplication (binary) *) val is_mul : Expr.expr -> bool (** Indicates whether the term is division (binary) *) val is_div : Expr.expr -> bool (** Indicates whether the term is integer division (binary) *) val is_idiv : Expr.expr -> bool (** Indicates whether the term is remainder (binary) *) val is_remainder : Expr.expr -> bool (** Indicates whether the term is modulus (binary) *) val is_modulus : Expr.expr -> bool (** Indicates whether the term is a coercion of integer to real (unary) *) val is_int2real : Expr.expr -> bool (** Indicates whether the term is a coercion of real to integer (unary) *) val is_real2int : Expr.expr -> bool (** Indicates whether the term is a check that tests whether a real is integral (unary) *) val is_real_is_int : Expr.expr -> bool (** Indicates whether the term is of sort real. *) val is_real : Expr.expr -> bool (** Indicates whether the term is an integer numeral. *) val is_int_numeral : Expr.expr -> bool (** Indicates whether the term is a real numeral. *) val is_rat_numeral : Expr.expr -> bool (** Indicates whether the term is an algebraic number *) val is_algebraic_number : Expr.expr -> bool (** Create an expression representing [t[0] + t[1] + ...]. *) val mk_add : context -> Expr.expr list -> Expr.expr (** Create an expression representing [t[0] * t[1] * ...]. *) val mk_mul : context -> Expr.expr list -> Expr.expr (** Create an expression representing [t[0] - t[1] - ...]. *) val mk_sub : context -> Expr.expr list -> Expr.expr (** Create an expression representing [-t]. *) val mk_unary_minus : context -> Expr.expr -> Expr.expr (** Create an expression representing [t1 / t2]. *) val mk_div : context -> Expr.expr -> Expr.expr -> Expr.expr (** Create an expression representing [t1 ^ t2]. *) val mk_power : context -> Expr.expr -> Expr.expr -> Expr.expr (** Create an expression representing [t1 < t2] *) val mk_lt : context -> Expr.expr -> Expr.expr -> Expr.expr (** Create an expression representing [t1 <= t2] *) val mk_le : context -> Expr.expr -> Expr.expr -> Expr.expr (** Create an expression representing [t1 > t2] *) val mk_gt : context -> Expr.expr -> Expr.expr -> Expr.expr (** Create an expression representing [t1 >= t2] *) val mk_ge : context -> Expr.expr -> Expr.expr -> Expr.expr end (** Functions to manipulate bit-vector expressions *) module BitVector : sig (** Create a new bit-vector sort. *) val mk_sort : context -> int -> Sort.sort (** Indicates whether the terms is of bit-vector sort. *) val is_bv : Expr.expr -> bool (** Indicates whether the term is a bit-vector numeral *) val is_bv_numeral : Expr.expr -> bool (** Indicates whether the term is a one-bit bit-vector with value one *) val is_bv_bit1 : Expr.expr -> bool (** Indicates whether the term is a one-bit bit-vector with value zero *) val is_bv_bit0 : Expr.expr -> bool (** Indicates whether the term is a bit-vector unary minus *) val is_bv_uminus : Expr.expr -> bool (** Indicates whether the term is a bit-vector addition (binary) *) val is_bv_add : Expr.expr -> bool (** Indicates whether the term is a bit-vector subtraction (binary) *) val is_bv_sub : Expr.expr -> bool (** Indicates whether the term is a bit-vector multiplication (binary) *) val is_bv_mul : Expr.expr -> bool (** Indicates whether the term is a bit-vector signed division (binary) *) val is_bv_sdiv : Expr.expr -> bool (** Indicates whether the term is a bit-vector unsigned division (binary) *) val is_bv_udiv : Expr.expr -> bool (** Indicates whether the term is a bit-vector signed remainder (binary) *) val is_bv_SRem : Expr.expr -> bool (** Indicates whether the term is a bit-vector unsigned remainder (binary) *) val is_bv_urem : Expr.expr -> bool (** Indicates whether the term is a bit-vector signed modulus *) val is_bv_smod : Expr.expr -> bool (** Indicates whether the term is a bit-vector signed division by zero *) val is_bv_sdiv0 : Expr.expr -> bool (** Indicates whether the term is a bit-vector unsigned division by zero *) val is_bv_udiv0 : Expr.expr -> bool (** Indicates whether the term is a bit-vector signed remainder by zero *) val is_bv_srem0 : Expr.expr -> bool (** Indicates whether the term is a bit-vector unsigned remainder by zero *) val is_bv_urem0 : Expr.expr -> bool (** Indicates whether the term is a bit-vector signed modulus by zero *) val is_bv_smod0 : Expr.expr -> bool (** Indicates whether the term is an unsigned bit-vector less-than-or-equal *) val is_bv_ule : Expr.expr -> bool (** Indicates whether the term is a signed bit-vector less-than-or-equal *) val is_bv_sle : Expr.expr -> bool (** Indicates whether the term is an unsigned bit-vector greater-than-or-equal *) val is_bv_uge : Expr.expr -> bool (** Indicates whether the term is a signed bit-vector greater-than-or-equal *) val is_bv_sge : Expr.expr -> bool (** Indicates whether the term is an unsigned bit-vector less-than *) val is_bv_ult : Expr.expr -> bool (** Indicates whether the term is a signed bit-vector less-than *) val is_bv_slt : Expr.expr -> bool (** Indicates whether the term is an unsigned bit-vector greater-than *) val is_bv_ugt : Expr.expr -> bool (** Indicates whether the term is a signed bit-vector greater-than *) val is_bv_sgt : Expr.expr -> bool (** Indicates whether the term is a bit-wise AND *) val is_bv_and : Expr.expr -> bool (** Indicates whether the term is a bit-wise OR *) val is_bv_or : Expr.expr -> bool (** Indicates whether the term is a bit-wise NOT *) val is_bv_not : Expr.expr -> bool (** Indicates whether the term is a bit-wise XOR *) val is_bv_xor : Expr.expr -> bool (** Indicates whether the term is a bit-wise NAND *) val is_bv_nand : Expr.expr -> bool (** Indicates whether the term is a bit-wise NOR *) val is_bv_nor : Expr.expr -> bool (** Indicates whether the term is a bit-wise XNOR *) val is_bv_xnor : Expr.expr -> bool (** Indicates whether the term is a bit-vector concatenation (binary) *) val is_bv_concat : Expr.expr -> bool (** Indicates whether the term is a bit-vector sign extension *) val is_bv_signextension : Expr.expr -> bool (** Indicates whether the term is a bit-vector zero extension *) val is_bv_zeroextension : Expr.expr -> bool (** Indicates whether the term is a bit-vector extraction *) val is_bv_extract : Expr.expr -> bool (** Indicates whether the term is a bit-vector repetition *) val is_bv_repeat : Expr.expr -> bool (** Indicates whether the term is a bit-vector reduce OR *) val is_bv_reduceor : Expr.expr -> bool (** Indicates whether the term is a bit-vector reduce AND *) val is_bv_reduceand : Expr.expr -> bool (** Indicates whether the term is a bit-vector comparison *) val is_bv_comp : Expr.expr -> bool (** Indicates whether the term is a bit-vector shift left *) val is_bv_shiftleft : Expr.expr -> bool (** Indicates whether the term is a bit-vector logical shift right *) val is_bv_shiftrightlogical : Expr.expr -> bool (** Indicates whether the term is a bit-vector arithmetic shift left *) val is_bv_shiftrightarithmetic : Expr.expr -> bool (** Indicates whether the term is a bit-vector rotate left *) val is_bv_rotateleft : Expr.expr -> bool (** Indicates whether the term is a bit-vector rotate right *) val is_bv_rotateright : Expr.expr -> bool (** Indicates whether the term is a bit-vector rotate left (extended) Similar to Z3_OP_ROTATE_LEFT, but it is a binary operator instead of a parametric one. *) val is_bv_rotateleftextended : Expr.expr -> bool (** Indicates whether the term is a bit-vector rotate right (extended) Similar to Z3_OP_ROTATE_RIGHT, but it is a binary operator instead of a parametric one. *) val is_bv_rotaterightextended : Expr.expr -> bool (** Indicates whether the term is a coercion from bit-vector to integer This function is not supported by the decision procedures. Only the most rudimentary simplification rules are applied to this function. *) val is_int2bv : Expr.expr -> bool (** Indicates whether the term is a coercion from integer to bit-vector This function is not supported by the decision procedures. Only the most rudimentary simplification rules are applied to this function. *) val is_bv2int : Expr.expr -> bool (** Indicates whether the term is a bit-vector carry Compute the carry bit in a full-adder. The meaning is given by the equivalence (carry l1 l2 l3) <=> (or (and l1 l2) (and l1 l3) (and l2 l3))) *) val is_bv_carry : Expr.expr -> bool (** Indicates whether the term is a bit-vector ternary XOR The meaning is given by the equivalence (xor3 l1 l2 l3) <=> (xor (xor l1 l2) l3) *) val is_bv_xor3 : Expr.expr -> bool (** The size of a bit-vector sort. *) val get_size : Sort.sort -> int (** Returns a string representation of a numeral. *) val numeral_to_string : Expr.expr -> string (** Creates a bit-vector constant. *) val mk_const : context -> Symbol.symbol -> int -> Expr.expr (** Creates a bit-vector constant. *) val mk_const_s : context -> string -> int -> Expr.expr (** Bitwise negation. The argument must have a bit-vector sort. *) val mk_not : context -> Expr.expr -> Expr.expr (** Take conjunction of bits in a vector,vector of length 1. The argument must have a bit-vector sort. *) val mk_redand : context -> Expr.expr -> Expr.expr (** Take disjunction of bits in a vector,vector of length 1. The argument must have a bit-vector sort. *) val mk_redor : context -> Expr.expr -> Expr.expr (** Bitwise conjunction. The arguments must have a bit-vector sort. *) val mk_and : context -> Expr.expr -> Expr.expr -> Expr.expr (** Bitwise disjunction. The arguments must have a bit-vector sort. *) val mk_or : context -> Expr.expr -> Expr.expr -> Expr.expr (** Bitwise XOR. The arguments must have a bit-vector sort. *) val mk_xor : context -> Expr.expr -> Expr.expr -> Expr.expr (** Bitwise NAND. The arguments must have a bit-vector sort. *) val mk_nand : context -> Expr.expr -> Expr.expr -> Expr.expr (** Bitwise NOR. The arguments must have a bit-vector sort. *) val mk_nor : context -> Expr.expr -> Expr.expr -> Expr.expr (** Bitwise XNOR. The arguments must have a bit-vector sort. *) val mk_xnor : context -> Expr.expr -> Expr.expr -> Expr.expr (** Standard two's complement unary minus. The arguments must have a bit-vector sort. *) val mk_neg : context -> Expr.expr -> Expr.expr (** Two's complement addition. The arguments must have the same bit-vector sort. *) val mk_add : context -> Expr.expr -> Expr.expr -> Expr.expr (** Two's complement subtraction. The arguments must have the same bit-vector sort. *) val mk_sub : context -> Expr.expr -> Expr.expr -> Expr.expr (** Two's complement multiplication. The arguments must have the same bit-vector sort. *) val mk_mul : context -> Expr.expr -> Expr.expr -> Expr.expr (** Unsigned division. It is defined as the floor of [t1/t2] if \c t2 is different from zero. If [t2] is zero, then the result is undefined. The arguments must have the same bit-vector sort. *) val mk_udiv : context -> Expr.expr -> Expr.expr -> Expr.expr (** Signed division. It is defined in the following way: - The \c floor of [t1/t2] if \c t2 is different from zero, and [t1*t2 >= 0]. - The \c ceiling of [t1/t2] if \c t2 is different from zero, and [t1*t2 < 0]. If [t2] is zero, then the result is undefined. The arguments must have the same bit-vector sort. *) val mk_sdiv : context -> Expr.expr -> Expr.expr -> Expr.expr (** Unsigned remainder. It is defined as [t1 - (t1 /u t2) * t2], where [/u] represents unsigned division. If [t2] is zero, then the result is undefined. The arguments must have the same bit-vector sort. *) val mk_urem : context -> Expr.expr -> Expr.expr -> Expr.expr (** Signed remainder. It is defined as [t1 - (t1 /s t2) * t2], where [/s] represents signed division. The most significant bit (sign) of the result is equal to the most significant bit of \c t1. If [t2] is zero, then the result is undefined. The arguments must have the same bit-vector sort. *) val mk_srem : context -> Expr.expr -> Expr.expr -> Expr.expr (** Two's complement signed remainder (sign follows divisor). If [t2] is zero, then the result is undefined. The arguments must have the same bit-vector sort. *) val mk_smod : context -> Expr.expr -> Expr.expr -> Expr.expr (** Unsigned less-than The arguments must have the same bit-vector sort. *) val mk_ult : context -> Expr.expr -> Expr.expr -> Expr.expr (** Two's complement signed less-than The arguments must have the same bit-vector sort. *) val mk_slt : context -> Expr.expr -> Expr.expr -> Expr.expr (** Unsigned less-than or equal to. The arguments must have the same bit-vector sort. *) val mk_ule : context -> Expr.expr -> Expr.expr -> Expr.expr (** Two's complement signed less-than or equal to. The arguments must have the same bit-vector sort. *) val mk_sle : context -> Expr.expr -> Expr.expr -> Expr.expr (** Unsigned greater than or equal to. The arguments must have the same bit-vector sort. *) val mk_uge : context -> Expr.expr -> Expr.expr -> Expr.expr (** Two's complement signed greater than or equal to. The arguments must have the same bit-vector sort. *) val mk_sge : context -> Expr.expr -> Expr.expr -> Expr.expr (** Unsigned greater-than. The arguments must have the same bit-vector sort. *) val mk_ugt : context -> Expr.expr -> Expr.expr -> Expr.expr (** Two's complement signed greater-than. The arguments must have the same bit-vector sort. *) val mk_sgt : context -> Expr.expr -> Expr.expr -> Expr.expr (** Bit-vector concatenation. The arguments must have a bit-vector sort. @return The result is a bit-vector of size [n1+n2], where [n1] ([n2]) is the size of [t1] ([t2]). *) val mk_concat : context -> Expr.expr -> Expr.expr -> Expr.expr (** Bit-vector extraction. Extract the bits between two limits from a bitvector of size [m] to yield a new bitvector of size [n], where [n = high - low + 1]. *) val mk_extract : context -> int -> int -> Expr.expr -> Expr.expr (** Bit-vector sign extension. Sign-extends the given bit-vector to the (signed) equivalent bitvector of size [m+i], where \c m is the size of the given bit-vector. *) val mk_sign_ext : context -> int -> Expr.expr -> Expr.expr (** Bit-vector zero extension. Extend the given bit-vector with zeros to the (unsigned) equivalent bitvector of size [m+i], where \c m is the size of the given bit-vector. *) val mk_zero_ext : context -> int -> Expr.expr -> Expr.expr (** Bit-vector repetition. *) val mk_repeat : context -> int -> Expr.expr -> Expr.expr (** Shift left. It is equivalent to multiplication by [2^x] where \c x is the value of third argument. NB. The semantics of shift operations varies between environments. This definition does not necessarily capture directly the semantics of the programming language or assembly architecture you are modeling.*) val mk_shl : context -> Expr.expr -> Expr.expr -> Expr.expr (** Logical shift right It is equivalent to unsigned division by [2^x] where \c x is the value of the third argument. NB. The semantics of shift operations varies between environments. This definition does not necessarily capture directly the semantics of the programming language or assembly architecture you are modeling. The arguments must have a bit-vector sort. *) val mk_lshr : context -> Expr.expr -> Expr.expr -> Expr.expr (** Arithmetic shift right It is like logical shift right except that the most significant bits of the result always copy the most significant bit of the second argument. NB. The semantics of shift operations varies between environments. This definition does not necessarily capture directly the semantics of the programming language or assembly architecture you are modeling. The arguments must have a bit-vector sort. *) val mk_ashr : context -> Expr.expr -> Expr.expr -> Expr.expr (** Rotate Left. Rotate bits of \c t to the left \c i times. *) val mk_rotate_left : context -> int -> Expr.expr -> Expr.expr (** Rotate Right. Rotate bits of \c t to the right \c i times.*) val mk_rotate_right : context -> int -> Expr.expr -> Expr.expr (** Rotate Left. Rotate bits of the second argument to the left.*) val mk_ext_rotate_left : context -> Expr.expr -> Expr.expr -> Expr.expr (** Rotate Right. Rotate bits of the second argument to the right. *) val mk_ext_rotate_right : context -> Expr.expr -> Expr.expr -> Expr.expr (** Create an integer from the bit-vector argument If \c is_signed is false, then the bit-vector \c t1 is treated as unsigned. So the result is non-negative and in the range [[0..2^N-1]], where N are the number of bits in the argument. If \c is_signed is true, \c t1 is treated as a signed bit-vector. NB. This function is essentially treated as uninterpreted. So you cannot expect Z3 to precisely reflect the semantics of this function when solving constraints with this function.*) val mk_bv2int : context -> Expr.expr -> bool -> Expr.expr (** Create a predicate that checks that the bit-wise addition does not overflow. The arguments must be of bit-vector sort. *) val mk_add_no_overflow : context -> Expr.expr -> Expr.expr -> bool -> Expr.expr (** Create a predicate that checks that the bit-wise addition does not underflow. The arguments must be of bit-vector sort. *) val mk_add_no_underflow : context -> Expr.expr -> Expr.expr -> Expr.expr (** Create a predicate that checks that the bit-wise subtraction does not overflow. The arguments must be of bit-vector sort. *) val mk_sub_no_overflow : context -> Expr.expr -> Expr.expr -> Expr.expr (** Create a predicate that checks that the bit-wise subtraction does not underflow. The arguments must be of bit-vector sort. *) val mk_sub_no_underflow : context -> Expr.expr -> Expr.expr -> bool -> Expr.expr (** Create a predicate that checks that the bit-wise signed division does not overflow. The arguments must be of bit-vector sort. *) val mk_sdiv_no_overflow : context -> Expr.expr -> Expr.expr -> Expr.expr (** Create a predicate that checks that the bit-wise negation does not overflow. The arguments must be of bit-vector sort. *) val mk_neg_no_overflow : context -> Expr.expr -> Expr.expr (** Create a predicate that checks that the bit-wise multiplication does not overflow. The arguments must be of bit-vector sort. *) val mk_mul_no_overflow : context -> Expr.expr -> Expr.expr -> bool -> Expr.expr (** Create a predicate that checks that the bit-wise multiplication does not underflow. The arguments must be of bit-vector sort. *) val mk_mul_no_underflow : context -> Expr.expr -> Expr.expr -> Expr.expr (** Create a bit-vector numeral. *) val mk_numeral : context -> string -> int -> Expr.expr end (** Sequences, Strings and Regular Expressions **) module Seq : sig (* create a sequence sort *) val mk_seq_sort : context -> Sort.sort -> Sort.sort (* test if sort is a sequence sort *) val is_seq_sort : context -> Sort.sort -> bool (* create regular expression sorts over sequences of the argument sort *) val mk_re_sort : context -> Sort.sort -> Sort.sort (* test if sort is a regular expression sort *) val is_re_sort : context -> Sort.sort -> bool (* create string sort *) val mk_string_sort : context -> Sort.sort (* test if sort is a string sort (a sequence of 8-bit bit-vectors) *) val is_string_sort : context -> Sort.sort -> bool (* create a string literal *) val mk_string : context -> string -> Expr.expr (* test if expression is a string *) val is_string : context -> Expr.expr -> bool (* retrieve string from string Expr.expr *) val get_string : context -> Expr.expr -> string (* the empty sequence over base sort *) val mk_seq_empty : context -> Sort.sort -> Expr.expr (* a unit sequence *) val mk_seq_unit : context -> Expr.expr -> Expr.expr (* sequence concatenation *) val mk_seq_concat : context -> Expr.expr list -> Expr.expr (* predicate if the first argument is a prefix of the second *) val mk_seq_prefix : context -> Expr.expr -> Expr.expr -> Expr.expr (* predicate if the first argument is a suffix of the second *) val mk_seq_suffix : context -> Expr.expr -> Expr.expr -> Expr.expr (* predicate if the first argument contains the second *) val mk_seq_contains : context -> Expr.expr -> Expr.expr -> Expr.expr (* extract sub-sequence starting at index given by second argument and of length provided by third argument *) val mk_seq_extract : context -> Expr.expr -> Expr.expr -> Expr.expr -> Expr.expr (* replace first occurrence of second argument by third *) val mk_seq_replace : context -> Expr.expr -> Expr.expr -> Expr.expr -> Expr.expr (* a unit sequence at index provided by second argument *) val mk_seq_at : context -> Expr.expr -> Expr.expr -> Expr.expr (* length of a sequence *) val mk_seq_length : context -> Expr.expr -> Expr.expr (* index of the first occurrence of the second argument in the first *) val mk_seq_index : context -> Expr.expr -> Expr.expr -> Expr.expr -> Expr.expr (* retrieve integer expression encoded in string *) val mk_str_to_int : context -> Expr.expr -> Expr.expr (* convert an integer expression to a string *) val mk_int_to_str : context -> Expr.expr -> Expr.expr (* create regular expression that accepts the argument sequence *) val mk_seq_to_re : context -> Expr.expr -> Expr.expr (* regular expression membership predicate *) val mk_seq_in_re : context -> Expr.expr -> Expr.expr -> Expr.expr (* regular expression plus *) val mk_re_plus : context -> Expr.expr -> Expr.expr (* regular expression star *) val mk_re_star : context -> Expr.expr -> Expr.expr (* optional regular expression *) val mk_re_option : context -> Expr.expr -> Expr.expr (* union of regular expressions *) val mk_re_union : context -> Expr.expr list -> Expr.expr (* concatenation of regular expressions *) val mk_re_concat : context -> Expr.expr list -> Expr.expr (* regular expression for the range between two characters *) val mk_re_range : context -> Expr.expr -> Expr.expr -> Expr.expr (* bounded loop regular expression *) val mk_re_loop : context -> Expr.expr -> int -> int -> Expr.expr (* intersection of regular expressions *) val mk_re_intersect : context -> int -> Expr.expr list -> Expr.expr (* the regular expression complement *) val mk_re_complement : context -> Expr.expr -> Expr.expr (* the regular expression that accepts no sequences *) val mk_re_empty : context -> Sort.sort -> Expr.expr (* the regular expression that accepts all sequences *) val mk_re_full : context -> Sort.sort -> Expr.expr end (** Floating-Point Arithmetic *) module FloatingPoint : sig (** Rounding Modes *) module RoundingMode : sig (** Create the RoundingMode sort. *) val mk_sort : context -> Sort.sort (** Indicates whether the terms is of floating-point rounding mode sort. *) val is_fprm : Expr.expr -> bool (** Create a numeral of RoundingMode sort which represents the NearestTiesToEven rounding mode. *) val mk_round_nearest_ties_to_even : context -> Expr.expr (** Create a numeral of RoundingMode sort which represents the NearestTiesToEven rounding mode. *) val mk_rne : context -> Expr.expr (** Create a numeral of RoundingMode sort which represents the NearestTiesToAway rounding mode. *) val mk_round_nearest_ties_to_away : context -> Expr.expr (** Create a numeral of RoundingMode sort which represents the NearestTiesToAway rounding mode. *) val mk_rna : context -> Expr.expr (** Create a numeral of RoundingMode sort which represents the TowardPositive rounding mode. *) val mk_round_toward_positive : context -> Expr.expr (** Create a numeral of RoundingMode sort which represents the TowardPositive rounding mode. *) val mk_rtp : context -> Expr.expr (** Create a numeral of RoundingMode sort which represents the TowardNegative rounding mode. *) val mk_round_toward_negative : context -> Expr.expr (** Create a numeral of RoundingMode sort which represents the TowardNegative rounding mode. *) val mk_rtn : context -> Expr.expr (** Create a numeral of RoundingMode sort which represents the TowardZero rounding mode. *) val mk_round_toward_zero : context -> Expr.expr (** Create a numeral of RoundingMode sort which represents the TowardZero rounding mode. *) val mk_rtz : context -> Expr.expr end (** Create a FloatingPoint sort. *) val mk_sort : context -> int -> int -> Sort.sort (** Create the half-precision (16-bit) FloatingPoint sort.*) val mk_sort_half : context -> Sort.sort (** Create the half-precision (16-bit) FloatingPoint sort. *) val mk_sort_16 : context -> Sort.sort (** Create the single-precision (32-bit) FloatingPoint sort.*) val mk_sort_single : context -> Sort.sort (** Create the single-precision (32-bit) FloatingPoint sort. *) val mk_sort_32 : context -> Sort.sort (** Create the double-precision (64-bit) FloatingPoint sort. *) val mk_sort_double : context -> Sort.sort (** Create the double-precision (64-bit) FloatingPoint sort. *) val mk_sort_64 : context -> Sort.sort (** Create the quadruple-precision (128-bit) FloatingPoint sort. *) val mk_sort_quadruple : context -> Sort.sort (** Create the quadruple-precision (128-bit) FloatingPoint sort. *) val mk_sort_128 : context -> Sort.sort (** Create a floating-point NaN of a given FloatingPoint sort. *) val mk_nan : context -> Sort.sort -> Expr.expr (** Create a floating-point infinity of a given FloatingPoint sort. *) val mk_inf : context -> Sort.sort -> bool -> Expr.expr (** Create a floating-point zero of a given FloatingPoint sort. *) val mk_zero : context -> Sort.sort -> bool -> Expr.expr (** 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 are required to be greater than 1 and 2 respectively. The FloatingPoint sort of the resulting expression is automatically determined from the bit-vector sizes of the arguments. *) val mk_fp : context -> Expr.expr -> Expr.expr -> Expr.expr -> Expr.expr (** Create a numeral of FloatingPoint sort from a float. This function is used to create numerals that fit in a float value. It is slightly faster than #Z3_mk_numeral since it is not necessary to parse a string. *) val mk_numeral_f : context -> float -> Sort.sort -> Expr.expr (** Create a numeral of FloatingPoint sort from a signed integer. *) val mk_numeral_i : context -> int -> Sort.sort -> Expr.expr (** Create a numeral of FloatingPoint sort from a sign bit and two integers. *) val mk_numeral_i_u : context -> bool -> int -> int -> Sort.sort -> Expr.expr (** Create a numeral of FloatingPoint sort from a string *) val mk_numeral_s : context -> string -> Sort.sort -> Expr.expr (** Indicates whether the terms is of floating-point sort. *) val is_fp : Expr.expr -> bool (** Indicates whether an expression is a floating-point abs expression *) val is_abs : Expr.expr -> bool (** Indicates whether an expression is a floating-point neg expression *) val is_neg : Expr.expr -> bool (** Indicates whether an expression is a floating-point add expression *) val is_add : Expr.expr -> bool (** Indicates whether an expression is a floating-point sub expression *) val is_sub : Expr.expr -> bool (** Indicates whether an expression is a floating-point mul expression *) val is_mul : Expr.expr -> bool (** Indicates whether an expression is a floating-point div expression *) val is_div : Expr.expr -> bool (** Indicates whether an expression is a floating-point fma expression *) val is_fma : Expr.expr -> bool (** Indicates whether an expression is a floating-point sqrt expression *) val is_sqrt : Expr.expr -> bool (** Indicates whether an expression is a floating-point rem expression *) val is_rem : Expr.expr -> bool (** Indicates whether an expression is a floating-point round_to_integral expression *) val is_round_to_integral : Expr.expr -> bool (** Indicates whether an expression is a floating-point min expression *) val is_min : Expr.expr -> bool (** Indicates whether an expression is a floating-point max expression *) val is_max : Expr.expr -> bool (** Indicates whether an expression is a floating-point leq expression *) val is_leq : Expr.expr -> bool (** Indicates whether an expression is a floating-point lt expression *) val is_lt : Expr.expr -> bool (** Indicates whether an expression is a floating-point geq expression *) val is_geq : Expr.expr -> bool (** Indicates whether an expression is a floating-point gt expression *) val is_gt : Expr.expr -> bool (** Indicates whether an expression is a floating-point eq expression *) val is_eq : Expr.expr -> bool (** Indicates whether an expression is a floating-point is_normal expression *) val is_is_normal : Expr.expr -> bool (** Indicates whether an expression is a floating-point is_subnormal expression *) val is_is_subnormal : Expr.expr -> bool (** Indicates whether an expression is a floating-point is_zero expression *) val is_is_zero : Expr.expr -> bool (** Indicates whether an expression is a floating-point is_infinite expression *) val is_is_infinite : Expr.expr -> bool (** Indicates whether an expression is a floating-point is_nan expression *) val is_is_nan : Expr.expr -> bool (** Indicates whether an expression is a floating-point is_negative expression *) val is_is_negative : Expr.expr -> bool (** Indicates whether an expression is a floating-point is_positive expression *) val is_is_positive : Expr.expr -> bool (** Indicates whether an expression is a floating-point to_fp expression *) val is_to_fp : Expr.expr -> bool (** Indicates whether an expression is a floating-point to_fp_unsigned expression *) val is_to_fp_unsigned : Expr.expr -> bool (** Indicates whether an expression is a floating-point to_ubv expression *) val is_to_ubv : Expr.expr -> bool (** Indicates whether an expression is a floating-point to_sbv expression *) val is_to_sbv : Expr.expr -> bool (** Indicates whether an expression is a floating-point to_real expression *) val is_to_real : Expr.expr -> bool (** Indicates whether an expression is a floating-point to_ieee_bv expression *) val is_to_ieee_bv : Expr.expr -> bool (** Returns a string representation of a numeral. *) val numeral_to_string : Expr.expr -> string (** Creates a floating-point constant. *) val mk_const : context -> Symbol.symbol -> Sort.sort -> Expr.expr (** Creates a floating-point constant. *) val mk_const_s : context -> string -> Sort.sort -> Expr.expr (** Floating-point absolute value *) val mk_abs : context -> Expr.expr -> Expr.expr (** Floating-point negation *) val mk_neg : context -> Expr.expr -> Expr.expr (** Floating-point addition *) val mk_add : context -> Expr.expr -> Expr.expr -> Expr.expr -> Expr.expr (** Floating-point subtraction *) val mk_sub : context -> Expr.expr -> Expr.expr -> Expr.expr -> Expr.expr (** Floating-point multiplication *) val mk_mul : context -> Expr.expr -> Expr.expr -> Expr.expr -> Expr.expr (** Floating-point division *) val mk_div : context -> Expr.expr -> Expr.expr -> Expr.expr -> Expr.expr (** Floating-point fused multiply-add. *) val mk_fma : context -> Expr.expr -> Expr.expr -> Expr.expr -> Expr.expr -> Expr.expr (** Floating-point square root *) val mk_sqrt : context -> Expr.expr -> Expr.expr -> Expr.expr (** Floating-point remainder *) val mk_rem : context -> Expr.expr -> Expr.expr -> Expr.expr (** Floating-point roundToIntegral. Rounds a floating-point number to the closest integer, again represented as a floating-point number. *) val mk_round_to_integral : context -> Expr.expr -> Expr.expr -> Expr.expr (** Minimum of floating-point numbers. *) val mk_min : context -> Expr.expr -> Expr.expr -> Expr.expr (** Maximum of floating-point numbers. *) val mk_max : context -> Expr.expr -> Expr.expr -> Expr.expr (** Floating-point less than or equal. *) val mk_leq : context -> Expr.expr -> Expr.expr -> Expr.expr (** Floating-point less than. *) val mk_lt : context -> Expr.expr -> Expr.expr -> Expr.expr (** Floating-point greater than or equal. *) val mk_geq : context -> Expr.expr -> Expr.expr -> Expr.expr (** Floating-point greater than. *) val mk_gt : context -> Expr.expr -> Expr.expr -> Expr.expr (** Floating-point equality. *) val mk_eq : context -> Expr.expr -> Expr.expr -> Expr.expr (** Predicate indicating whether t is a normal floating-point number. *) val mk_is_normal : context -> Expr.expr -> Expr.expr (** Predicate indicating whether t is a subnormal floating-point number. *) val mk_is_subnormal : context -> Expr.expr -> Expr.expr (** Predicate indicating whether t is a floating-point number with zero value, i.e., +zero or -zero. *) val mk_is_zero : context -> Expr.expr -> Expr.expr (** Predicate indicating whether t is a floating-point number representing +oo or -oo. *) val mk_is_infinite : context -> Expr.expr -> Expr.expr (** Predicate indicating whether t is a NaN. *) val mk_is_nan : context -> Expr.expr -> Expr.expr (** Predicate indicating whether t is a negative floating-point number. *) val mk_is_negative : context -> Expr.expr -> Expr.expr (** Predicate indicating whether t is a positive floating-point number. *) val mk_is_positive : context -> Expr.expr -> Expr.expr (** Conversion of a single IEEE 754-2008 bit-vector into a floating-point number. *) val mk_to_fp_bv : context -> Expr.expr -> Sort.sort -> Expr.expr (** Conversion of a FloatingPoint term into another term of different FloatingPoint sort. *) val mk_to_fp_float : context -> Expr.expr -> Expr.expr -> Sort.sort -> Expr.expr (** Conversion of a term of real sort into a term of FloatingPoint sort. *) val mk_to_fp_real : context -> Expr.expr -> Expr.expr -> Sort.sort -> Expr.expr (** Conversion of a 2's complement signed bit-vector term into a term of FloatingPoint sort. *) val mk_to_fp_signed : context -> Expr.expr -> Expr.expr -> Sort.sort -> Expr.expr (** Conversion of a 2's complement unsigned bit-vector term into a term of FloatingPoint sort. *) val mk_to_fp_unsigned : context -> Expr.expr -> Expr.expr -> Sort.sort -> Expr.expr (** Conversion of a floating-point term into an unsigned bit-vector. *) val mk_to_ubv : context -> Expr.expr -> Expr.expr -> int -> Expr.expr (** Conversion of a floating-point term into a signed bit-vector. *) val mk_to_sbv : context -> Expr.expr -> Expr.expr -> int -> Expr.expr (** Conversion of a floating-point term into a real-numbered term. *) val mk_to_real : context -> Expr.expr -> Expr.expr (** Retrieves the number of bits reserved for the exponent in a FloatingPoint sort. *) val get_ebits : context -> Sort.sort -> int (** Retrieves the number of bits reserved for the significand in a FloatingPoint sort. *) val get_sbits : context -> Sort.sort -> int (** Retrieves the sign of a floating-point literal. *) val get_numeral_sign : context -> Expr.expr -> bool * int (** Return the sign of a floating-point numeral as a bit-vector expression. Remark: NaN's do not have a bit-vector sign, so they are invalid arguments. *) val get_numeral_sign_bv : context -> Expr.expr -> Expr.expr (** Return the exponent value of a floating-point numeral as a string *) val get_numeral_exponent_string : context -> Expr.expr -> bool -> string (** Return the exponent value of a floating-point numeral as a signed integer *) val get_numeral_exponent_int : context -> Expr.expr -> bool -> bool * int (** Return the exponent of a floating-point numeral as a bit-vector expression. Remark: NaN's do not have a bit-vector exponent, so they are invalid arguments. *) val get_numeral_exponent_bv : context -> Expr.expr -> bool -> Expr.expr (** Return the significand value of a floating-point numeral as a bit-vector expression. Remark: NaN's do not have a bit-vector significand, so they are invalid arguments. *) val get_numeral_significand_bv : context -> Expr.expr -> Expr.expr (** Return the significand value of a floating-point numeral as a string. *) val get_numeral_significand_string : context -> Expr.expr -> string (** Return the significand value of a floating-point numeral as a uint64. Remark: This function extracts the significand bits, without the hidden bit or normalization. Throws an exception if the significand does not fit into an int. *) val get_numeral_significand_uint : context -> Expr.expr -> bool * int (** Indicates whether a floating-point numeral is a NaN. *) val is_numeral_nan : context -> Expr.expr -> bool (** Indicates whether a floating-point numeral is +oo or -oo. *) val is_numeral_inf : context -> Expr.expr -> bool (** Indicates whether a floating-point numeral is +zero or -zero. *) val is_numeral_zero : context -> Expr.expr -> bool (** Indicates whether a floating-point numeral is normal. *) val is_numeral_normal : context -> Expr.expr -> bool (** Indicates whether a floating-point numeral is subnormal. *) val is_numeral_subnormal : context -> Expr.expr -> bool (** Indicates whether a floating-point numeral is positive. *) val is_numeral_positive : context -> Expr.expr -> bool (** Indicates whether a floating-point numeral is negative. *) val is_numeral_negative : context -> Expr.expr -> bool (** Conversion of a floating-point term into a bit-vector term in IEEE 754-2008 format. *) val mk_to_ieee_bv : context -> Expr.expr -> Expr.expr (** Conversion of a real-sorted significand and an integer-sorted exponent into a term of FloatingPoint sort. *) val mk_to_fp_int_real : context -> Expr.expr -> Expr.expr -> Expr.expr -> Sort.sort -> Expr.expr (** The string representation of a numeral *) val numeral_to_string : Expr.expr -> string end (** Functions to manipulate proof expressions *) module Proof : sig (** Indicates whether the term is a Proof for the expression 'true'. *) val is_true : Expr.expr -> bool (** Indicates whether the term is a proof for a fact asserted by the user. *) val is_asserted : Expr.expr -> bool (** Indicates whether the term is a proof for a fact (tagged as goal) asserted by the user. *) val is_goal : Expr.expr -> bool (** Indicates whether the term is a binary equivalence modulo namings. This binary predicate is used in proof terms. It captures equisatisfiability and equivalence modulo renamings. *) val is_oeq : Expr.expr -> bool (** Indicates whether the term is proof via modus ponens Given a proof for p and a proof for (implies p q), produces a proof for q. T1: p T2: (implies p q) [mp T1 T2]: q The second antecedents may also be a proof for (iff p q). *) val is_modus_ponens : Expr.expr -> bool (** Indicates whether the term is a proof for (R t t), where R is a reflexive relation. This proof object has no antecedents. The only reflexive relations that are used are equivalence modulo namings, equality and equivalence. That is, R is either '~', '=' or 'iff'. *) val is_reflexivity : Expr.expr -> bool (** Indicates whether the term is proof by symmetricity of a relation Given an symmetric relation R and a proof for (R t s), produces a proof for (R s t). T1: (R t s) [symmetry T1]: (R s t) T1 is the antecedent of this proof object. *) val is_symmetry : Expr.expr -> bool (** Indicates whether the term is a proof by transitivity of a relation Given a transitive relation R, and proofs for (R t s) and (R s u), produces a proof for (R t u). T1: (R t s) T2: (R s u) [trans T1 T2]: (R t u) *) val is_transitivity : Expr.expr -> bool (** Indicates whether the term is a proof by condensed transitivity of a relation Condensed transitivity proof. It combines several symmetry and transitivity proofs. Example: T1: (R a b) T2: (R c b) T3: (R c d) [trans* T1 T2 T3]: (R a d) R must be a symmetric and transitive relation. Assuming that this proof object is a proof for (R s t), then a proof checker must check if it is possible to prove (R s t) using the antecedents, symmetry and transitivity. That is, if there is a path from s to t, if we view every antecedent (R a b) as an edge between a and b. *) val is_Transitivity_star : Expr.expr -> bool (** Indicates whether the term is a monotonicity proof object. T1: (R t_1 s_1) ... Tn: (R t_n s_n) [monotonicity T1 ... Tn]: (R (f t_1 ... t_n) (f s_1 ... s_n)) Remark: if t_i == s_i, then the antecedent Ti is suppressed. That is, reflexivity proofs are suppressed to save space. *) val is_monotonicity : Expr.expr -> bool (** Indicates whether the term is a quant-intro proof Given a proof for (~ p q), produces a proof for (~ (forall (x) p) (forall (x) q)). T1: (~ p q) [quant-intro T1]: (~ (forall (x) p) (forall (x) q)) *) val is_quant_intro : Expr.expr -> bool (** Indicates whether the term is a distributivity proof object. Given that f (= or) distributes over g (= and), produces a proof for (= (f a (g c d)) (g (f a c) (f a d))) If f and g are associative, this proof also justifies the following equality: (= (f (g a b) (g c d)) (g (f a c) (f a d) (f b c) (f b d))) where each f and g can have arbitrary number of arguments. This proof object has no antecedents. Remark. This rule is used by the CNF conversion pass and instantiated by f = or, and g = and. *) val is_distributivity : Expr.expr -> bool (** Indicates whether the term is a proof by elimination of AND Given a proof for (and l_1 ... l_n), produces a proof for l_i T1: (and l_1 ... l_n) [and-elim T1]: l_i *) val is_and_elimination : Expr.expr -> bool (** Indicates whether the term is a proof by elimination of not-or Given a proof for (not (or l_1 ... l_n)), produces a proof for (not l_i). T1: (not (or l_1 ... l_n)) [not-or-elim T1]: (not l_i) *) val is_or_elimination : Expr.expr -> bool (** Indicates whether the term is a proof by rewriting A proof for a local rewriting step (= t s). The head function symbol of t is interpreted. This proof object has no antecedents. The conclusion of a rewrite rule is either an equality (= t s), an equivalence (iff t s), or equi-satisfiability (~ t s). Remark: if f is bool, then = is iff. Examples: (= (+ ( x : expr ) 0) x) (= (+ ( x : expr ) 1 2) (+ 3 x)) (iff (or ( x : expr ) false) x) *) val is_rewrite : Expr.expr -> bool (** Indicates whether the term is a proof by rewriting A proof for rewriting an expression t into an expression s. This proof object can have n antecedents. The antecedents are proofs for equalities used as substitution rules. The object is also used in a few cases. The cases are: - When applying contextual simplification (CONTEXT_SIMPLIFIER=true) - When converting bit-vectors to Booleans (BIT2BOOL=true) *) val is_rewrite_star : Expr.expr -> bool (** Indicates whether the term is a proof for pulling quantifiers out. A proof for (iff (f (forall (x) q(x)) r) (forall (x) (f (q x) r))). This proof object has no antecedents. *) val is_pull_quant : Expr.expr -> bool (** Indicates whether the term is a proof for pushing quantifiers in. A proof for: (iff (forall (x_1 ... x_m) (and p_1[x_1 ... x_m] ... p_n[x_1 ... x_m])) (and (forall (x_1 ... x_m) p_1[x_1 ... x_m]) ... (forall (x_1 ... x_m) p_n[x_1 ... x_m]))) This proof object has no antecedents *) val is_push_quant : Expr.expr -> bool (** Indicates whether the term is a proof for elimination of unused variables. A proof for (iff (forall (x_1 ... x_n y_1 ... y_m) p[x_1 ... x_n]) (forall (x_1 ... x_n) p[x_1 ... x_n])) It is used to justify the elimination of unused variables. This proof object has no antecedents. *) val is_elim_unused_vars : Expr.expr -> bool (** Indicates whether the term is a proof for destructive equality resolution A proof for destructive equality resolution: (iff (forall (x) (or (not (= ( x : expr ) t)) P[x])) P[t]) if ( x : expr ) does not occur in t. This proof object has no antecedents. Several variables can be eliminated simultaneously. *) val is_der : Expr.expr -> bool (** Indicates whether the term is a proof for quantifier instantiation A proof of (or (not (forall (x) (P x))) (P a)) *) val is_quant_inst : Expr.expr -> bool (** Indicates whether the term is a hypothesis marker. Mark a hypothesis in a natural deduction style proof. *) val is_hypothesis : Expr.expr -> bool (** Indicates whether the term is a proof by lemma T1: false [lemma T1]: (or (not l_1) ... (not l_n)) This proof object has one antecedent: a hypothetical proof for false. It converts the proof in a proof for (or (not l_1) ... (not l_n)), when T1 contains the hypotheses: l_1, ..., l_n. *) val is_lemma : Expr.expr -> bool (** Indicates whether the term is a proof by unit resolution T1: (or l_1 ... l_n l_1' ... l_m') T2: (not l_1) ... T(n+1): (not l_n) [unit-resolution T1 ... T(n+1)]: (or l_1' ... l_m') *) val is_unit_resolution : Expr.expr -> bool (** Indicates whether the term is a proof by iff-true T1: p [iff-true T1]: (iff p true) *) val is_iff_true : Expr.expr -> bool (** Indicates whether the term is a proof by iff-false T1: (not p) [iff-false T1]: (iff p false) *) val is_iff_false : Expr.expr -> bool (** Indicates whether the term is a proof by commutativity [comm]: (= (f a b) (f b a)) f is a commutative operator. This proof object has no antecedents. Remark: if f is bool, then = is iff. *) val is_commutativity : Expr.expr -> bool (** Indicates whether the term is a proof for Tseitin-like axioms Proof object used to justify Tseitin's like axioms: (or (not (and p q)) p) (or (not (and p q)) q) (or (not (and p q r)) p) (or (not (and p q r)) q) (or (not (and p q r)) r) ... (or (and p q) (not p) (not q)) (or (not (or p q)) p q) (or (or p q) (not p)) (or (or p q) (not q)) (or (not (iff p q)) (not p) q) (or (not (iff p q)) p (not q)) (or (iff p q) (not p) (not q)) (or (iff p q) p q) (or (not (ite a b c)) (not a) b) (or (not (ite a b c)) a c) (or (ite a b c) (not a) (not b)) (or (ite a b c) a (not c)) (or (not (not a)) (not a)) (or (not a) a) This proof object has no antecedents. Note: all axioms are propositional tautologies. Note also that 'and' and 'or' can take multiple arguments. You can recover the propositional tautologies by unfolding the Boolean connectives in the axioms a small bounded number of steps (=3). *) val is_def_axiom : Expr.expr -> bool (** Indicates whether the term is a proof for introduction of a name Introduces a name for a formula/term. Suppose e is an expression with free variables x, and def-intro introduces the name n(x). The possible cases are: When e is of Boolean type: [def-intro]: (and (or n (not e)) (or (not n) e)) or: [def-intro]: (or (not n) e) when e only occurs positively. When e is of the form (ite cond th el): [def-intro]: (and (or (not cond) (= n th)) (or cond (= n el))) Otherwise: [def-intro]: (= n e) *) val is_def_intro : Expr.expr -> bool (** Indicates whether the term is a proof for application of a definition [apply-def T1]: F ~ n F is 'equivalent' to n, given that T1 is a proof that n is a name for F. *) val is_apply_def : Expr.expr -> bool (** Indicates whether the term is a proof iff-oeq T1: (iff p q) [iff~ T1]: (~ p q) *) val is_iff_oeq : Expr.expr -> bool (** Indicates whether the term is a proof for a positive NNF step Proof for a (positive) NNF step. Example: T1: (not s_1) ~ r_1 T2: (not s_2) ~ r_2 T3: s_1 ~ r_1' T4: s_2 ~ r_2' [nnf-pos T1 T2 T3 T4]: (~ (iff s_1 s_2) (and (or r_1 r_2') (or r_1' r_2))) The negation normal form steps NNF_POS and NNF_NEG are used in the following cases: (a) When creating the NNF of a positive force quantifier. The quantifier is retained (unless the bound variables are eliminated). Example T1: q ~ q_new [nnf-pos T1]: (~ (forall (x T) q) (forall (x T) q_new)) (b) When recursively creating NNF over Boolean formulas, where the top-level connective is changed during NNF conversion. The relevant Boolean connectives for NNF_POS are 'implies', 'iff', 'xor', 'ite'. NNF_NEG furthermore handles the case where negation is pushed over Boolean connectives 'and' and 'or'. *) val is_nnf_pos : Expr.expr -> bool (** Indicates whether the term is a proof for a negative NNF step Proof for a (negative) NNF step. Examples: T1: (not s_1) ~ r_1 ... Tn: (not s_n) ~ r_n [nnf-neg T1 ... Tn]: (not (and s_1 ... s_n)) ~ (or r_1 ... r_n) and T1: (not s_1) ~ r_1 ... Tn: (not s_n) ~ r_n [nnf-neg T1 ... Tn]: (not (or s_1 ... s_n)) ~ (and r_1 ... r_n) and T1: (not s_1) ~ r_1 T2: (not s_2) ~ r_2 T3: s_1 ~ r_1' T4: s_2 ~ r_2' [nnf-neg T1 T2 T3 T4]: (~ (not (iff s_1 s_2)) (and (or r_1 r_2) (or r_1' r_2'))) *) val is_nnf_neg : Expr.expr -> bool (** Indicates whether the term is a proof for a Skolemization step Proof for: [sk]: (~ (not (forall ( x : expr ) (p ( x : expr ) y))) (not (p (sk y) y))) [sk]: (~ (exists ( x : expr ) (p ( x : expr ) y)) (p (sk y) y)) This proof object has no antecedents. *) val is_skolemize : Expr.expr -> bool (** Indicates whether the term is a proof by modus ponens for equi-satisfiability. Modus ponens style rule for equi-satisfiability. T1: p T2: (~ p q) [mp~ T1 T2]: q *) val is_modus_ponens_oeq : Expr.expr -> bool (** Indicates whether the term is a proof for theory lemma Generic proof for theory lemmas. The theory lemma function comes with one or more parameters. The first parameter indicates the name of the theory. For the theory of arithmetic, additional parameters provide hints for checking the theory lemma. The hints for arithmetic are: - farkas - followed by rational coefficients. Multiply the coefficients to the inequalities in the lemma, add the (negated) inequalities and obtain a contradiction. - triangle-eq - Indicates a lemma related to the equivalence: (iff (= t1 t2) (and (<= t1 t2) (<= t2 t1))) - gcd-test - Indicates an integer linear arithmetic lemma that uses a gcd test. *) val is_theory_lemma : Expr.expr -> bool end (** Goals A goal (aka problem). A goal is essentially a of formulas, that can be solved and/or transformed using tactics and solvers. *) module Goal : sig type goal (** The precision of the goal. Goals can be transformed using over and under approximations. An under approximation is applied when the objective is to find a model for a given goal. An over approximation is applied when the objective is to find a proof for a given goal. *) val get_precision : goal -> Z3enums.goal_prec (** Indicates whether the goal is precise. *) val is_precise : goal -> bool (** Indicates whether the goal is an under-approximation. *) val is_underapproximation : goal -> bool (** Indicates whether the goal is an over-approximation. *) val is_overapproximation : goal -> bool (** Indicates whether the goal is garbage (i.e., the product of over- and under-approximations). *) val is_garbage : goal -> bool (** Adds the constraints to the given goal. *) val add : goal -> Expr.expr list -> unit (** Indicates whether the goal contains `false'. *) val is_inconsistent : goal -> bool (** The depth of the goal. This tracks how many transformations were applied to it. *) val get_depth : goal -> int (** Erases all formulas from the given goal. *) val reset : goal -> unit (** The number of formulas in the goal. *) val get_size : goal -> int (** The formulas in the goal. *) val get_formulas : goal -> Expr.expr list (** The number of formulas, subformulas and terms in the goal. *) val get_num_exprs : goal -> int (** Indicates whether the goal is empty, and it is precise or the product of an under approximation. *) val is_decided_sat : goal -> bool (** Indicates whether the goal contains `false', and it is precise or the product of an over approximation. *) val is_decided_unsat : goal -> bool (** Translates (copies) the Goal to another context.. *) val translate : goal -> context -> goal (** Simplifies the goal. Essentially invokes the `simplify' tactic on the goal. *) val simplify : goal -> Params.params option -> goal (** Creates a new Goal. Note that the Context must have been created with proof generation support if the fourth argument is set to true here. *) val mk_goal : context -> bool -> bool -> bool -> goal (** A string representation of the Goal. *) val to_string : goal -> string (** Goal to BoolExpr conversion. *) val as_expr : goal -> Expr.expr end (** Models A Model contains interpretations (assignments) of constants and functions. *) module Model : sig type model (** Function interpretations A function interpretation is represented as a finite map and an 'else'. Each entry in the finite map represents the value of a function given a set of arguments. *) module FuncInterp : sig type func_interp (** Function interpretations entries An Entry object represents an element in the finite map used to a function interpretation. *) module FuncEntry : sig type func_entry (** Return the (symbolic) value of this entry. *) val get_value : func_entry -> Expr.expr (** The number of arguments of the entry. *) val get_num_args : func_entry -> int (** The arguments of the function entry. *) val get_args : func_entry -> Expr.expr list (** A string representation of the function entry. *) val to_string : func_entry -> string end (** The number of entries in the function interpretation. *) val get_num_entries : func_interp -> int (** The entries in the function interpretation *) val get_entries : func_interp -> FuncEntry.func_entry list (** The (symbolic) `else' value of the function interpretation. *) val get_else : func_interp -> Expr.expr (** The arity of the function interpretation *) val get_arity : func_interp -> int (** A string representation of the function interpretation. *) val to_string : func_interp -> string end (** Retrieves the interpretation (the assignment) of a func_decl in the model. @return An expression if the function has an interpretation in the model, null otherwise. *) val get_const_interp : model -> FuncDecl.func_decl -> Expr.expr option (** Retrieves the interpretation (the assignment) of an expression in the model. @return An expression if the constant has an interpretation in the model, null otherwise. *) val get_const_interp_e : model -> Expr.expr -> Expr.expr option (** Retrieves the interpretation (the assignment) of a non-constant func_decl in the model. @return A FunctionInterpretation if the function has an interpretation in the model, null otherwise. *) val get_func_interp : model -> FuncDecl.func_decl -> FuncInterp.func_interp option (** The number of constant interpretations in the model. *) val get_num_consts : model -> int (** The function declarations of the constants in the model. *) val get_const_decls : model -> FuncDecl.func_decl list (** The number of function interpretations in the model. *) val get_num_funcs : model -> int (** The function declarations of the function interpretations in the model. *) val get_func_decls : model -> FuncDecl.func_decl list (** All symbols that have an interpretation in the model. *) val get_decls : model -> FuncDecl.func_decl list (** Evaluates an expression in the current model. This function may fail if the argument contains quantifiers, is partial (MODEL_PARTIAL enabled), or if it is not well-sorted. In this case a [ModelEvaluationFailedException] is thrown. *) val eval : model -> Expr.expr -> bool -> Expr.expr option (** Alias for [eval]. *) val evaluate : model -> Expr.expr -> bool -> Expr.expr option (** The number of uninterpreted sorts that the model has an interpretation for. *) val get_num_sorts : model -> int (** The uninterpreted sorts that the model has an interpretation for. Z3 also provides an interpretation for uninterpreted sorts used in a formula. The interpretation for a sort is a finite set of distinct values. We say this finite set is the "universe" of the sort. {!get_num_sorts} {!sort_universe} *) val get_sorts : model -> Sort.sort list (** The finite set of distinct values that represent the interpretation of a sort. {!get_sorts} @return A list of expressions, where each is an element of the universe of the sort *) val sort_universe : model -> Sort.sort -> Expr.expr list (** Conversion of models to strings. @return A string representation of the model. *) val to_string : model -> string end (** Probes Probes are used to inspect a goal (aka problem) and collect information that may be used to decide which solver and/or preprocessing step will be used. The complete list of probes may be obtained using the procedures [Context.NumProbes] and [Context.ProbeNames]. It may also be obtained using the command [(help-tactic)] in the SMT 2.0 front-end. *) module Probe : sig type probe (** Execute the probe over the goal. @return A probe always produce a float value. "Boolean" probes return 0.0 for false, and a value different from 0.0 for true. *) val apply : probe -> Goal.goal -> float (** The number of supported Probes. *) val get_num_probes : context -> int (** The names of all supported Probes. *) val get_probe_names : context -> string list (** Returns a string containing a description of the probe with the given name. *) val get_probe_description : context -> string -> string (** Creates a new Probe. *) val mk_probe : context -> string -> probe (** Create a probe that always evaluates to a float value. *) val const : context -> float -> probe (** Create a probe that evaluates to "true" when the value returned by the first argument is less than the value returned by second argument *) val lt : context -> probe -> probe -> probe (** Create a probe that evaluates to "true" when the value returned by the first argument is greater than the value returned by second argument *) val gt : context -> probe -> probe -> probe (** Create a probe that evaluates to "true" when the value returned by the first argument is less than or equal the value returned by second argument *) val le : context -> probe -> probe -> probe (** Create a probe that evaluates to "true" when the value returned by the first argument is greater than or equal the value returned by second argument *) val ge : context -> probe -> probe -> probe (** Create a probe that evaluates to "true" when the value returned by the first argument is equal the value returned by second argument *) val eq : context -> probe -> probe -> probe (** Create a probe that evaluates to "true" when both of two probes evaluate to "true". *) val and_ : context -> probe -> probe -> probe (** Create a probe that evaluates to "true" when either of two probes evaluates to "true". *) val or_ : context -> probe -> probe -> probe (** Create a probe that evaluates to "true" when another probe does not evaluate to "true". *) val not_ : context -> probe -> probe end (** Tactics Tactics are the basic building block for creating custom solvers for specific problem domains. The complete list of tactics may be obtained using [Context.get_num_tactics] and [Context.get_tactic_names]. It may also be obtained using the command [(help-tactic)] in the SMT 2.0 front-end. *) module Tactic : sig type tactic (** Tactic application results ApplyResult objects represent the result of an application of a tactic to a goal. It contains the subgoals that were produced. *) module ApplyResult : sig type apply_result (** The number of Subgoals. *) val get_num_subgoals : apply_result -> int (** Retrieves the subgoals from the apply_result. *) val get_subgoals : apply_result -> Goal.goal list (** Retrieves a subgoal from the apply_result. *) val get_subgoal : apply_result -> int -> Goal.goal (** A string representation of the ApplyResult. *) val to_string : apply_result -> string end (** A string containing a description of parameters accepted by the tactic. *) val get_help : tactic -> string (** Retrieves parameter descriptions for Tactics. *) val get_param_descrs : tactic -> Params.ParamDescrs.param_descrs (** Apply the tactic to the goal. *) val apply : tactic -> Goal.goal -> Params.params option -> ApplyResult.apply_result (** The number of supported tactics. *) val get_num_tactics : context -> int (** The names of all supported tactics. *) val get_tactic_names : context -> string list (** Returns a string containing a description of the tactic with the given name. *) val get_tactic_description : context -> string -> string (** Creates a new Tactic. *) val mk_tactic : context -> string -> tactic (** Create a tactic that applies one tactic to a Goal and then another one to every subgoal produced by the first one. *) val and_then : context -> tactic -> tactic -> tactic list -> tactic (** Create a tactic that first applies one tactic to a Goal and if it fails then returns the result of another tactic applied to the Goal. *) val or_else : context -> tactic -> tactic -> tactic (** Create a tactic that applies one tactic to a goal for some time (in milliseconds). If the tactic does not terminate within the timeout, then it fails. *) val try_for : context -> tactic -> int -> tactic (** Create a tactic that applies one tactic to a given goal if the probe evaluates to true. If the probe evaluates to false, then the new tactic behaves like the [skip] tactic. *) val when_ : context -> Probe.probe -> tactic -> tactic (** Create a tactic that applies a tactic to a given goal if the probe evaluates to true and another tactic otherwise. *) val cond : context -> Probe.probe -> tactic -> tactic -> tactic (** Create a tactic that keeps applying one tactic until the goal is not modified anymore or the maximum number of iterations is reached. *) val repeat : context -> tactic -> int -> tactic (** Create a tactic that just returns the given goal. *) val skip : context -> tactic (** Create a tactic always fails. *) val fail : context -> tactic (** Create a tactic that fails if the probe evaluates to false. *) val fail_if : context -> Probe.probe -> tactic (** Create a tactic that fails if the goal is not trivially satisfiable (i.e., empty) or trivially unsatisfiable (i.e., contains `false'). *) val fail_if_not_decided : context -> tactic (** Create a tactic that applies a tactic using the given set of parameters. *) val using_params : context -> tactic -> Params.params -> tactic (** Create a tactic that applies a tactic using the given set of parameters. Alias for [UsingParams]*) val with_ : context -> tactic -> Params.params -> tactic (** Create a tactic that applies the given tactics in parallel until one of them succeeds (i.e., the first that doesn't fail). *) val par_or : context -> tactic list -> tactic (** Create a tactic that applies a tactic to a given goal and then another tactic to every subgoal produced by the first one. The subgoals are processed in parallel. *) val par_and_then : context -> tactic -> tactic -> tactic (** Interrupt the execution of a Z3 procedure. This procedure can be used to interrupt: solvers, simplifiers and tactics. *) val interrupt : context -> unit end (** Objects that track statistical information. *) module Statistics : sig type statistics (** Statistical data is organized into pairs of \[Key, Entry\], where every Entry is either a floating point or integer value. *) module Entry : sig type statistics_entry (** The key of the entry. *) val get_key : statistics_entry -> string (** The int-value of the entry. *) val get_int : statistics_entry -> int (** The float-value of the entry. *) val get_float : statistics_entry -> float (** True if the entry is uint-valued. *) val is_int : statistics_entry -> bool (** True if the entry is float-valued. *) val is_float : statistics_entry -> bool (** The string representation of the entry's value. *) val to_string_value : statistics_entry -> string (** The string representation of the entry (key and value) *) val to_string : statistics_entry -> string end (** A string representation of the statistical data. *) val to_string : statistics -> string (** The number of statistical data. *) val get_size : statistics -> int (** The data entries. *) val get_entries : statistics -> Entry.statistics_entry list (** The statistical counters. *) val get_keys : statistics -> string list (** The value of a particular statistical counter. *) val get : statistics -> string -> Entry.statistics_entry option end (** Solvers *) module Solver : sig type solver type status = UNSATISFIABLE | UNKNOWN | SATISFIABLE val string_of_status : status -> string (** A string that describes all available solver parameters. *) val get_help : solver -> string (** Sets the solver parameters. *) val set_parameters : solver -> Params.params -> unit (** Retrieves parameter descriptions for solver. *) val get_param_descrs : solver -> Params.ParamDescrs.param_descrs (** The current number of backtracking points (scopes). {!pop} {!push} *) val get_num_scopes : solver -> int (** Creates a backtracking point. {!pop} *) val push : solver -> unit (** Backtracks a number of backtracking points. Note that an exception is thrown if the integer is not smaller than {!get_num_scopes} {!push} *) val pop : solver -> int -> unit (** Resets the Solver. This removes all assertions from the solver. *) val reset : solver -> unit (** Assert a constraint (or multiple) into the solver. *) val add : solver -> Expr.expr list -> unit (** Assert multiple constraints (cs) into the solver, and track them (in the unsat) core using the Boolean constants in ps. This API is an alternative to {!check} with assumptions for extracting unsat cores. Both APIs can be used in the same solver. The unsat core will contain a combination of the Boolean variables provided using {!assert_and_track} and the Boolean literals provided using {!check} with assumptions. *) val assert_and_track_l : solver -> Expr.expr list -> Expr.expr list -> unit (** Assert a constraint (c) into the solver, and track it (in the unsat) core using the Boolean constant p. This API is an alternative to {!check} with assumptions for extracting unsat cores. Both APIs can be used in the same solver. The unsat core will contain a combination of the Boolean variables provided using {!assert_and_track} and the Boolean literals provided using {!check} with assumptions. *) val assert_and_track : solver -> Expr.expr -> Expr.expr -> unit (** The number of assertions in the solver. *) val get_num_assertions : solver -> int (** The set of asserted formulas. *) val get_assertions : solver -> Expr.expr list (** Checks whether the assertions in the solver are consistent or not. {!Model} {!get_unsat_core} {!Proof} *) val check : solver -> Expr.expr list -> status (** The model of the last [Check]. The result is [None] if [Check] was not invoked before, if its results was not [SATISFIABLE], or if model production is not enabled. *) val get_model : solver -> Model.model option (** The proof of the last [Check]. The result is [null] if [Check] was not invoked before, if its results was not [UNSATISFIABLE], or if proof production is disabled. *) val get_proof : solver -> Expr.expr option (** The unsat core of the last [Check]. The unsat core is a subset of [Assertions] The result is empty if [Check] was not invoked before, if its results was not [UNSATISFIABLE], or if core production is disabled. *) val get_unsat_core : solver -> Expr.expr list (** A brief justification of why the last call to [Check] returned [UNKNOWN]. *) val get_reason_unknown : solver -> string (** Solver statistics. *) val get_statistics : solver -> Statistics.statistics (** Creates a new (incremental) solver. This solver also uses a set of builtin tactics for handling the first check-sat command, and check-sat commands that take more than a given number of milliseconds to be solved. *) val mk_solver : context -> Symbol.symbol option -> solver (** Creates a new (incremental) solver. {!mk_solver} *) val mk_solver_s : context -> string -> solver (** Creates a new (incremental) solver. *) val mk_simple_solver : context -> solver (** Creates a solver that is implemented using the given tactic. The solver supports the commands [Push] and [Pop], but it will always solve each check from scratch. *) val mk_solver_t : context -> Tactic.tactic -> solver (** Create a clone of the current solver with respect to a context. *) val translate : solver -> context -> solver (** A string representation of the solver. *) val to_string : solver -> string end (** Fixedpoint solving *) module Fixedpoint : sig type fixedpoint (** A string that describes all available fixedpoint solver parameters. *) val get_help : fixedpoint -> string (** Sets the fixedpoint solver parameters. *) val set_parameters : fixedpoint -> Params.params -> unit (** Retrieves parameter descriptions for Fixedpoint solver. *) val get_param_descrs : fixedpoint -> Params.ParamDescrs.param_descrs (** Assert a constraints into the fixedpoint solver. *) val add : fixedpoint -> Expr.expr list -> unit (** Register predicate as recursive relation. *) val register_relation : fixedpoint -> FuncDecl.func_decl -> unit (** Add rule into the fixedpoint solver. *) val add_rule : fixedpoint -> Expr.expr -> Symbol.symbol option -> unit (** Add table fact to the fixedpoint solver. *) val add_fact : fixedpoint -> FuncDecl.func_decl -> int list -> unit (** Query the fixedpoint solver. A query is a conjunction of constraints. The constraints may include the recursively defined relations. The query is satisfiable if there is an instance of the query variables and a derivation for it. The query is unsatisfiable if there are no derivations satisfying the query variables. *) val query : fixedpoint -> Expr.expr -> Solver.status (** Query the fixedpoint solver. A query is an array of relations. The query is satisfiable if there is an instance of some relation that is non-empty. The query is unsatisfiable if there are no derivations satisfying any of the relations. *) val query_r : fixedpoint -> FuncDecl.func_decl list -> Solver.status (** Update named rule into in the fixedpoint solver. *) val update_rule : fixedpoint -> Expr.expr -> Symbol.symbol -> unit (** Retrieve satisfying instance or instances of solver, or definitions for the recursive predicates that show unsatisfiability. *) val get_answer : fixedpoint -> Expr.expr option (** Retrieve explanation why fixedpoint engine returned status Unknown. *) val get_reason_unknown : fixedpoint -> string (** Retrieve the number of levels explored for a given predicate. *) val get_num_levels : fixedpoint -> FuncDecl.func_decl -> int (** Retrieve the cover of a predicate. *) val get_cover_delta : fixedpoint -> int -> FuncDecl.func_decl -> Expr.expr option (** Add property about the predicate. The property is added at level. *) val add_cover : fixedpoint -> int -> FuncDecl.func_decl -> Expr.expr -> unit (** Retrieve internal string representation of fixedpoint object. *) val to_string : fixedpoint -> string (** Instrument the Datalog engine on which table representation to use for recursive predicate. *) val set_predicate_representation : fixedpoint -> FuncDecl.func_decl -> Symbol.symbol list -> unit (** Convert benchmark given as set of axioms, rules and queries to a string. *) val to_string_q : fixedpoint -> Expr.expr list -> string (** Retrieve set of rules added to fixedpoint context. *) val get_rules : fixedpoint -> Expr.expr list (** Retrieve set of assertions added to fixedpoint context. *) val get_assertions : fixedpoint -> Expr.expr list (** Create a Fixedpoint context. *) val mk_fixedpoint : context -> fixedpoint (** Retrieve statistics information from the last call to #Z3_fixedpoint_query. *) val get_statistics : fixedpoint -> Statistics.statistics (** Parse an SMT-LIB2 string with fixedpoint rules. Add the rules to the current fixedpoint context. Return the set of queries in the string. *) val parse_string : fixedpoint -> string -> Expr.expr list (** Parse an SMT-LIB2 file with fixedpoint rules. Add the rules to the current fixedpoint context. Return the set of queries in the file. *) val parse_file : fixedpoint -> string -> Expr.expr list end (** Optimization *) module Optimize : sig type optimize type handle (** Create a Optimize context. *) val mk_opt : context -> optimize (** A string that describes all available optimize solver parameters. *) val get_help : optimize -> string (** Sets the optimize solver parameters. *) val set_parameters : optimize -> Params.params -> unit (** Retrieves parameter descriptions for Optimize solver. *) val get_param_descrs : optimize -> Params.ParamDescrs.param_descrs (** Assert a constraints into the optimize solver. *) val add : optimize -> Expr.expr list -> unit (** Assert a soft constraint. Supply integer weight and string that identifies a group of soft constraints. *) val add_soft : optimize -> Expr.expr -> string -> Symbol.symbol -> handle (** Add maximization objective. *) val maximize : optimize -> Expr.expr -> handle (** Add minimization objective. *) val minimize : optimize -> Expr.expr -> handle (** Checks whether the assertions in the context are satisfiable and solves objectives. *) val check : optimize -> Solver.status (** Retrieve model from satisfiable context *) val get_model : optimize -> Model.model option (** Retrieve lower bound in current model for handle *) val get_lower : handle -> Expr.expr (** Retrieve upper bound in current model for handle *) val get_upper : handle -> Expr.expr (** Creates a backtracking point. {!pop} *) val push : optimize -> unit (** Backtrack one backtracking point. Note that an exception is thrown if Pop is called without a corresponding [Push] {!push} *) val pop : optimize -> unit (** Retrieve explanation why optimize engine returned status Unknown. *) val get_reason_unknown : optimize -> string (** Retrieve SMT-LIB string representation of optimize object. *) val to_string : optimize -> string (** Retrieve statistics information from the last call to check *) val get_statistics : optimize -> Statistics.statistics (** Parse an SMT-LIB2 file with assertions, soft constraints and optimization objectives. Add the parsed constraints and objectives to the optimization context. *) val from_file : optimize -> string -> unit (** Parse an SMT-LIB2 string with assertions, soft constraints and optimization objectives. Add the parsed constraints and objectives to the optimization context. *) val from_string : optimize -> string -> unit (** Return the set of asserted formulas on the optimization context. *) val get_assertions : optimize -> Expr.expr list (** 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) ...). 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. *) val get_objectives : optimize -> Expr.expr list end (** Functions for handling SMT and SMT2 expressions and files *) module SMT : sig (** Convert a benchmark into an SMT-LIB formatted string. @return A string representation of the benchmark. *) val benchmark_to_smtstring : context -> string -> string -> string -> string -> Expr.expr list -> Expr.expr -> string (** Parse the given string using the SMT-LIB2 parser. @return A conjunction of assertions in the scope (up to push/pop) at the end of the string. *) val parse_smtlib2_string : context -> string -> Symbol.symbol list -> Sort.sort list -> Symbol.symbol list -> FuncDecl.func_decl list -> AST.ASTVector.ast_vector (** Parse the given file using the SMT-LIB2 parser. *) val parse_smtlib2_file : context -> string -> Symbol.symbol list -> Sort.sort list -> Symbol.symbol list -> FuncDecl.func_decl list -> AST.ASTVector.ast_vector end (** Set a global (or module) parameter, which is shared by all Z3 contexts. When a Z3 module is initialized it will use the value of these parameters when Z3_params objects are not provided. The name of parameter can be composed of characters [a-z][A-Z], digits [0-9], '-' and '_'. The character '.' is a delimiter (more later). The parameter names are case-insensitive. The character '-' should be viewed as an "alias" for '_'. Thus, the following parameter names are considered equivalent: "pp.decimal-precision" and "PP.DECIMAL_PRECISION". This function can be used to set parameters for a specific Z3 module. This can be done by using .. For example: (set_global_param "pp.decimal" "true") will set the parameter "decimal" in the module "pp" to true. *) val set_global_param : string -> string -> unit (** Get a global (or module) parameter. Returns None if the parameter does not exist. The caller must invoke #Z3_global_param_del_value to delete the value returned at param_value. This function cannot be invoked simultaneously from different threads without synchronization. The result string stored in param_value is stored in a shared location. *) val get_global_param : string -> string option (** Restore the value of all global (and module) parameters. This command will not affect already created objects (such as tactics and solvers) {!set_global_param} *) val global_param_reset_all : unit -> unit (** Enable/disable printing of warning messages to the console. Note that this function is static and effects the behaviour of all contexts globally. *) val toggle_warning_messages : bool -> unit (** Enable tracing messages tagged as `tag' when Z3 is compiled in debug mode. Remarks: It is a NOOP otherwise. *) val enable_trace : string -> unit (** Disable tracing messages tagged as `tag' when Z3 is compiled in debug mode. 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 z3-z3-4.8.7/src/api/ml/z3native.ml.pre000066400000000000000000000012501356505360400172570ustar00rootroot00000000000000(** The native (raw) interface to the dynamic Z3 library. *) open Z3enums (**/**) type ptr and symbol = ptr and config = ptr and context = ptr and ast = ptr and app = ast and sort = ast and func_decl = ast and pattern = ast and model = ptr and literals = ptr and constructor = ptr and constructor_list = ptr and solver = ptr and goal = ptr and tactic = ptr and params = ptr and probe = ptr and stats = ptr and ast_vector = ptr and ast_map = ptr and apply_result = ptr and func_interp = ptr and func_entry = ptr and fixedpoint = ptr and optimize = ptr and param_descrs = ptr and rcf_num = ptr external set_internal_error_handler : ptr -> unit = "n_set_internal_error_handler" z3-z3-4.8.7/src/api/ml/z3native_stubs.c.pre000066400000000000000000000463301356505360400203210ustar00rootroot00000000000000#include #include #include #ifdef __cplusplus extern "C" { #endif #include #include #include #include #include #include #ifdef Custom_tag #include #include #endif #ifdef __cplusplus } #endif #include #include #define CAMLlocal6(X1,X2,X3,X4,X5,X6) \ CAMLlocal5(X1,X2,X3,X4,X5); \ CAMLlocal1(X6) #define CAMLlocal7(X1,X2,X3,X4,X5,X6,X7) \ CAMLlocal5(X1,X2,X3,X4,X5); \ CAMLlocal2(X6,X7) #define CAMLlocal8(X1,X2,X3,X4,X5,X6,X7,X8) \ CAMLlocal5(X1,X2,X3,X4,X5); \ CAMLlocal3(X6,X7,X8) #define CAMLparam6(X1,X2,X3,X4,X5,X6) \ CAMLparam5(X1,X2,X3,X4,X5); \ CAMLxparam1(X6) #define CAMLparam7(X1,X2,X3,X4,X5,X6,X7) \ CAMLparam5(X1,X2,X3,X4,X5); \ CAMLxparam2(X6,X7) #define CAMLparam8(X1,X2,X3,X4,X5,X6,X7,X8) \ CAMLparam5(X1,X2,X3,X4,X5); \ CAMLxparam3(X6,X7,X8) #define CAMLparam9(X1,X2,X3,X4,X5,X6,X7,X8,X9) \ CAMLparam5(X1,X2,X3,X4,X5); \ CAMLxparam4(X6,X7,X8,X9) #define CAMLparam12(X1,X2,X3,X4,X5,X6,X7,X8,X9,X10,X11,X12) \ CAMLparam5(X1,X2,X3,X4,X5); \ CAMLxparam5(X6,X7,X8,X9,X10); \ CAMLxparam2(X11,X12) #define CAMLparam13(X1,X2,X3,X4,X5,X6,X7,X8,X9,X10,X11,X12,X13) \ CAMLparam5(X1,X2,X3,X4,X5); \ CAMLxparam5(X6,X7,X8,X9,X10); \ CAMLxparam3(X11,X12,X13) static struct custom_operations default_custom_ops = { (char*) "default handling", custom_finalize_default, custom_compare_default, custom_hash_default, custom_serialize_default, custom_deserialize_default, custom_compare_ext_default, }; int compare_pointers(void* pt1, void* pt2) { if (pt1 == pt2) return 0; else if ((intnat)pt1 < (intnat)pt2) return -1; else return +1; } #define MK_CTX_OF(X) \ CAMLprim DLL_PUBLIC value n_context_of_ ## X(value v) { \ CAMLparam1(v); \ CAMLlocal1(result); \ Z3_context_plus cp; \ Z3_ ## X ## _plus * p = (Z3_ ## X ## _plus *) Data_custom_val(v); \ cp = p->cp; \ result = caml_alloc_custom(&Z3_context_plus_custom_ops, sizeof(Z3_context_plus), 0, 1); \ *(Z3_context_plus *)Data_custom_val(result) = cp; \ /* We increment the usage counter of the context, as we just \ created a second custom block holding that context */ \ cp->obj_count++; \ CAMLreturn(result); \ } \ \ CAMLprim value DLL_PUBLIC n_is_null_ ## X (value v) { \ CAMLparam1(v); \ Z3_ ## X ## _plus* pp = (Z3_ ## X ## _plus*)Data_custom_val(v); \ CAMLreturn(Val_bool(pp->p == NULL)); \ } \ \ CAMLprim value DLL_PUBLIC n_mk_null_ ## X (value v) { \ CAMLparam1(v); \ CAMLlocal1(result); \ Z3_context_plus cp = *(Z3_context_plus*)(Data_custom_val(v)); \ Z3_ ## X ## _plus a = Z3_ ## X ## _plus_mk(cp, NULL); \ result = caml_alloc_custom(&Z3_ ## X ## _plus_custom_ops, sizeof(Z3_ ## X ## _plus), 0, 1); \ *(Z3_ ## X ## _plus*)(Data_custom_val(result)) = a; \ CAMLreturn(result); \ } /* Context objects */ /* The Z3context_plus_data exists exactly once for each context, no matter how many custom blocks for that context exist. Each custom block only stores a pointer to the corresponding Z3_context_plus_data. This ensures that the reference counting is performed at exactly one place and not within the custom blocks that get copied. */ typedef struct { Z3_context ctx; unsigned long obj_count; } Z3_context_plus_data; /* A context is wrapped to an OCaml value by storing a pointer to its associated Z3_context_plus_data instance. This instance gets created in mk_context and is deleted together with the Z3 context instance in try_to_delete_context whenever the obj_count field is zero. */ typedef Z3_context_plus_data* Z3_context_plus; Z3_context_plus Z3_context_plus_mk(Z3_context c) { Z3_context_plus r = (Z3_context_plus)malloc(sizeof(Z3_context_plus_data)); r->ctx = c; /* The context created here will be wrapped into a custom block. We regard custom blocks that point to a Z3_context_plus structure as a usage of this structure. Hence, we assign it a counter of one. */ r->obj_count = 1; return r; } Z3_context Z3_context_plus_raw(Z3_context_plus * cp) { return (*cp)->ctx; } void try_to_delete_context(Z3_context_plus cp) { if (cp->obj_count == 0) { /* printf("try_to_delete_context: Deleting context %p(%p) with cnt=0.\n", cp, cp->ctx); */ Z3_del_context(cp->ctx); free(cp); } /* else if (cp->obj_count > 0) printf("try_to_delete_context: Not deleting context %p(%p) with cnt=%lu.\n", cp, cp->ctx, cp->obj_count); else if (cp->obj_count < 0) printf("try_to_delete_context: ERROR, found context %p(%p) with negative cnt=%lu.\n", cp, cp->ctx, cp->obj_count); */ } void Z3_context_finalize(value v) { Z3_context_plus cp = *(Z3_context_plus*)Data_custom_val(v); cp->obj_count--; try_to_delete_context(cp); } int Z3_context_compare(value v1, value v2) { /* As each context created within the OCaml bindings has a unique Z3_context_plus_data allocated to store the handle and the ref counters, we can just compare pointers here. This suffices to test for (in)equality and induces an arbitrary (but fixed) ordering. */ Z3_context_plus cp1 = *(Z3_context_plus*)Data_custom_val(v1); Z3_context_plus cp2 = *(Z3_context_plus*)Data_custom_val(v2); return compare_pointers(cp1, cp2); } int Z3_context_compare_ext(value v1, value v2) { Z3_context_plus cp = *(Z3_context_plus*)Data_custom_val(v1); return compare_pointers(cp, (void*)Val_int(v2)); } /* We use the pointer to the Z3_context_plus_data structure as a hash value; it is unique, at least. */ intnat Z3_context_hash(value v) { /* We use the address of the context's Z3_context_plus_data structure as a hash value */ Z3_context_plus cp = *(Z3_context_plus*)Data_custom_val(v); return (intnat)cp; } static struct custom_operations Z3_context_plus_custom_ops = { (char*) "Z3_context ops", Z3_context_finalize, Z3_context_compare, Z3_context_hash, custom_serialize_default, custom_deserialize_default, Z3_context_compare_ext }; /* AST objects */ typedef struct { Z3_context_plus cp; Z3_ast p; } Z3_ast_plus; Z3_ast_plus Z3_ast_plus_mk(Z3_context_plus cp, Z3_ast p) { Z3_ast_plus r; r.cp = cp; r.p = p; /* printf("++\n"); */ cp->obj_count++; if (p != NULL) Z3_inc_ref(cp->ctx, p); return r; } Z3_ast Z3_ast_plus_raw(Z3_ast_plus * ap) { return ap->p; } void Z3_ast_finalize(value v) { /* printf("--\n"); */ Z3_ast_plus * ap = (Z3_ast_plus*)(Data_custom_val(v)); if (ap->p != NULL) Z3_dec_ref(ap->cp->ctx, ap->p); ap->cp->obj_count--; try_to_delete_context(ap->cp); } int Z3_ast_compare(value v1, value v2) { Z3_ast_plus * a1 = (Z3_ast_plus*)Data_custom_val(v1); Z3_ast_plus * a2 = (Z3_ast_plus*)Data_custom_val(v2); unsigned id1, id2; /* if the two ASTs belong to different contexts, we take their contexts' addresses to order them (arbitrarily, but fixed) */ if (a1->cp->ctx != a2->cp->ctx) return compare_pointers(a1->cp->ctx, a2->cp->ctx); /* handling of NULL pointers */ if (a1->p == NULL && a2->p == NULL) return 0; if (a1->p == NULL) return -1; if (a2->p == NULL) return +1; /* Comparison according to AST ids. */ id1 = Z3_get_ast_id(a1->cp->ctx, a1->p); id2 = Z3_get_ast_id(a2->cp->ctx, a2->p); if (id1 == id2) return 0; else if (id1 < id2) return -1; else return +1; } int Z3_ast_compare_ext(value v1, value v2) { Z3_ast_plus * a1 = (Z3_ast_plus*)Data_custom_val(v1); unsigned id1; unsigned id2 = (unsigned)Val_int(v2); if (a1->p == NULL && id2 == 0) return 0; if (a1->p == NULL) return -1; if (id2 == 0) return +1; id1 = Z3_get_ast_id(a1->cp->ctx, a1->p); if (id1 == id2) return 0; else if (id1 < id2) return -1; else return +1; } intnat Z3_ast_hash(value v) { Z3_ast_plus * ap = (Z3_ast_plus*)Data_custom_val(v); if (ap->p == NULL) return 0; else return Z3_get_ast_hash(ap->cp->ctx, ap->p); } static struct custom_operations Z3_ast_plus_custom_ops = { (char*) "Z3_ast ops", Z3_ast_finalize, Z3_ast_compare, Z3_ast_hash, custom_serialize_default, custom_deserialize_default, Z3_ast_compare_ext }; MK_CTX_OF(ast) #define MK_PLUS_OBJ_NO_REF(X) \ typedef struct { \ Z3_context_plus cp; \ Z3_ ## X p; \ } Z3_ ## X ## _plus; \ \ Z3_ ## X ## _plus Z3_ ## X ## _plus_mk(Z3_context_plus cp, Z3_ ## X p) { \ Z3_ ## X ## _plus r; \ r.cp = cp; \ r.p = p; \ r.cp->obj_count++; \ return r; \ } \ \ Z3_ ## X Z3_ ## X ## _plus_raw(Z3_ ## X ## _plus * pp) { \ return pp->p; \ } \ \ void Z3_ ## X ## _finalize(value v) { \ Z3_ ## X ## _plus * pp = (Z3_ ## X ## _plus*)Data_custom_val(v); \ pp->cp->obj_count--; \ try_to_delete_context(pp->cp); \ } \ \ int Z3_ ## X ## _compare(value v1, value v2) { \ Z3_ ## X ## _plus * pp1 = (Z3_ ## X ## _plus*)Data_custom_val(v1); \ Z3_ ## X ## _plus * pp2 = (Z3_ ## X ## _plus*)Data_custom_val(v2); \ if (pp1->cp != pp2->cp) \ return compare_pointers(pp1->cp, pp2->cp); \ else \ return compare_pointers(pp1->p, pp2->p); \ } \ \ intnat Z3_ ## X ## _hash(value v) { \ Z3_ ## X ## _plus * pp = (Z3_ ## X ## _plus*)Data_custom_val(v); \ return (intnat)pp->p; \ } \ \ int Z3_ ## X ## _compare_ext(value v1, value v2) { \ Z3_ ## X ## _plus * pp = (Z3_ ## X ## _plus*)Data_custom_val(v1); \ return compare_pointers(pp->p, (void*)Val_int(v2)); \ } \ \ static struct custom_operations Z3_ ## X ## _plus_custom_ops = { \ (char*) "Z3_" #X " ops", \ Z3_ ## X ## _finalize, \ Z3_ ## X ## _compare, \ Z3_ ## X ## _hash, \ custom_serialize_default, \ custom_deserialize_default, \ Z3_ ## X ## _compare_ext \ }; \ \ MK_CTX_OF(X) #define MK_PLUS_OBJ(X) \ typedef struct { \ Z3_context_plus cp; \ Z3_ ## X p; \ } Z3_ ## X ## _plus; \ \ Z3_ ## X ## _plus Z3_ ## X ## _plus_mk(Z3_context_plus cp, Z3_ ## X p) { \ Z3_ ## X ## _plus r; \ r.cp = cp; \ r.p = p; \ r.cp->obj_count++; \ if (p != NULL) \ Z3_ ## X ## _inc_ref(cp->ctx, p); \ return r; \ } \ \ Z3_ ## X Z3_ ## X ## _plus_raw(Z3_ ## X ## _plus * pp) { \ return pp->p; \ } \ \ void Z3_ ## X ## _finalize(value v) { \ Z3_ ## X ## _plus * pp = (Z3_ ## X ## _plus*)Data_custom_val(v); \ if (pp->p != NULL) \ Z3_ ## X ## _dec_ref(pp->cp->ctx, pp->p); \ pp->cp->obj_count--; \ try_to_delete_context(pp->cp); \ } \ \ int Z3_ ## X ## _compare(value v1, value v2) { \ Z3_ ## X ## _plus * pp1 = (Z3_ ## X ## _plus*)Data_custom_val(v1); \ Z3_ ## X ## _plus * pp2 = (Z3_ ## X ## _plus*)Data_custom_val(v2); \ if (pp1->cp != pp2->cp) \ return compare_pointers(pp1->cp, pp2->cp); \ else \ return compare_pointers(pp1->p, pp2->p); \ } \ \ intnat Z3_ ## X ## _hash(value v) { \ Z3_ ## X ## _plus * pp = (Z3_ ## X ## _plus*)Data_custom_val(v); \ return (intnat)pp->p; \ } \ \ int Z3_ ## X ## _compare_ext(value v1, value v2) { \ Z3_ ## X ## _plus * pp = (Z3_ ## X ## _plus*)Data_custom_val(v1); \ return compare_pointers(pp->p, (void*)Val_int(v2)); \ } \ \ static struct custom_operations Z3_ ## X ## _plus_custom_ops = { \ (char*) "Z3_" #X " ops", \ Z3_ ## X ## _finalize, \ Z3_ ## X ## _compare, \ Z3_ ## X ## _hash, \ custom_serialize_default, \ custom_deserialize_default, \ Z3_ ## X ## _compare_ext \ }; \ \ MK_CTX_OF(X) MK_PLUS_OBJ_NO_REF(symbol) MK_PLUS_OBJ_NO_REF(constructor) MK_PLUS_OBJ_NO_REF(constructor_list) MK_PLUS_OBJ_NO_REF(rcf_num) MK_PLUS_OBJ(params) MK_PLUS_OBJ(param_descrs) MK_PLUS_OBJ(model) MK_PLUS_OBJ(func_interp) MK_PLUS_OBJ(func_entry) MK_PLUS_OBJ(goal) MK_PLUS_OBJ(tactic) MK_PLUS_OBJ(probe) MK_PLUS_OBJ(apply_result) MK_PLUS_OBJ(solver) MK_PLUS_OBJ(stats) MK_PLUS_OBJ(ast_map) MK_PLUS_OBJ(ast_vector) MK_PLUS_OBJ(fixedpoint) MK_PLUS_OBJ(optimize) #ifdef __cplusplus extern "C" { #endif void MLErrorHandler(Z3_context c, Z3_error_code e) { /* Internal do-nothing error handler. This is required to avoid that Z3 calls exit() upon errors, but the actual error handling is done by throwing exceptions in the n_* wrapper functions. */ } CAMLprim value DLL_PUBLIC n_set_internal_error_handler(value ctx_v) { CAMLparam1(ctx_v); Z3_context_plus ctx_p = *(Z3_context_plus*) Data_custom_val(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); } z3-z3-4.8.7/src/api/ml/z3native_stubs.h000066400000000000000000000010601356505360400175300ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: z3native_stubs.h Abstract: DLL/SO/DYLIB export macros. Author: Christoph (cwinter) 2015-12-12 Notes: --*/ #ifndef Z3NATIVE_STUBS_H_ #define Z3NATIVE_STUBS_H_ #if defined _WIN32 || defined __CYGWIN__ #ifdef __GNUC__ #define DLL_PUBLIC __attribute__ ((dllexport)) #else #define DLL_PUBLIC __declspec(dllexport) #endif #else #if __GNUC__ >= 4 #define DLL_PUBLIC __attribute__ ((visibility ("default"))) #else #define DLL_PUBLIC #endif #endif #endif z3-z3-4.8.7/src/api/python/000077500000000000000000000000001356505360400153105ustar00rootroot00000000000000z3-z3-4.8.7/src/api/python/.gitignore000066400000000000000000000000721356505360400172770ustar00rootroot00000000000000MANIFEST dist core build *.egg-info bin z3/lib z3/include z3-z3-4.8.7/src/api/python/CMakeLists.txt000066400000000000000000000133751356505360400200610ustar00rootroot00000000000000message(STATUS "Emitting rules to build Z3 python bindings") ############################################################################### # Add target to build python bindings for the build directory ############################################################################### # This allows the python bindings to be used directly from the build directory set(z3py_files z3/__init__.py z3/z3.py z3/z3num.py z3/z3poly.py z3/z3printer.py z3/z3rcf.py z3test.py z3/z3types.py z3/z3util.py ) set(z3py_bindings_build_dest "${PROJECT_BINARY_DIR}/python") file(MAKE_DIRECTORY "${z3py_bindings_build_dest}") file(MAKE_DIRECTORY "${z3py_bindings_build_dest}/z3") set(build_z3_python_bindings_target_depends "") foreach (z3py_file ${z3py_files}) add_custom_command(OUTPUT "${z3py_bindings_build_dest}/${z3py_file}" COMMAND "${CMAKE_COMMAND}" "-E" "copy" "${CMAKE_CURRENT_SOURCE_DIR}/${z3py_file}" "${z3py_bindings_build_dest}/${z3py_file}" DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${z3py_file}" COMMENT "Copying \"${z3py_file}\" to ${z3py_bindings_build_dest}/${z3py_file}" ) list(APPEND build_z3_python_bindings_target_depends "${z3py_bindings_build_dest}/${z3py_file}") endforeach() # Generate z3core.py add_custom_command(OUTPUT "${z3py_bindings_build_dest}/z3/z3core.py" COMMAND "${PYTHON_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/scripts/update_api.py" ${Z3_FULL_PATH_API_HEADER_FILES_TO_SCAN} "--z3py-output-dir" "${z3py_bindings_build_dest}" DEPENDS ${Z3_FULL_PATH_API_HEADER_FILES_TO_SCAN} "${PROJECT_SOURCE_DIR}/scripts/update_api.py" ${Z3_GENERATED_FILE_EXTRA_DEPENDENCIES} # FIXME: When update_api.py no longer uses ``mk_util`` drop this dependency "${PROJECT_SOURCE_DIR}/scripts/mk_util.py" COMMENT "Generating z3core.py" ${ADD_CUSTOM_COMMAND_USES_TERMINAL_ARG} ) list(APPEND build_z3_python_bindings_target_depends "${z3py_bindings_build_dest}/z3/z3core.py") # Generate z3consts.py add_custom_command(OUTPUT "${z3py_bindings_build_dest}/z3/z3consts.py" COMMAND "${PYTHON_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/scripts/mk_consts_files.py" ${Z3_FULL_PATH_API_HEADER_FILES_TO_SCAN} "--z3py-output-dir" "${z3py_bindings_build_dest}" DEPENDS ${Z3_FULL_PATH_API_HEADER_FILES_TO_SCAN} "${PROJECT_SOURCE_DIR}/scripts/mk_consts_files.py" ${Z3_GENERATED_FILE_EXTRA_DEPENDENCIES} COMMENT "Generating z3consts.py" ${ADD_CUSTOM_COMMAND_USES_TERMINAL_ARG} ) list(APPEND build_z3_python_bindings_target_depends "${z3py_bindings_build_dest}/z3/z3consts.py") if (UNIX) set(LINK_COMMAND "create_symlink") else() set(LINK_COMMAND "copy") endif() # Link libz3 into the python directory so bindings work out of the box add_custom_command(OUTPUT "${z3py_bindings_build_dest}/libz3${CMAKE_SHARED_MODULE_SUFFIX}" COMMAND "${CMAKE_COMMAND}" "-E" "${LINK_COMMAND}" "${PROJECT_BINARY_DIR}/libz3${CMAKE_SHARED_MODULE_SUFFIX}" "${z3py_bindings_build_dest}/libz3${CMAKE_SHARED_MODULE_SUFFIX}" DEPENDS libz3 COMMENT "Linking libz3 into python directory" ) # Convenient top-level target add_custom_target(build_z3_python_bindings ALL DEPENDS ${build_z3_python_bindings_target_depends} "${z3py_bindings_build_dest}/libz3${CMAKE_SHARED_MODULE_SUFFIX}" ) ############################################################################### # Install ############################################################################### option(Z3_INSTALL_PYTHON_BINDINGS "Install Python bindings when invoking install target" ON) if (Z3_INSTALL_PYTHON_BINDINGS) message(STATUS "Emitting rules to install Z3 python bindings") # Try to guess the installation path for the bindings if (NOT DEFINED CMAKE_INSTALL_PYTHON_PKG_DIR) message(STATUS "CMAKE_INSTALL_PYTHON_PKG_DIR not set. Trying to guess") execute_process( COMMAND "${PYTHON_EXECUTABLE}" "-c" "import distutils.sysconfig; print(distutils.sysconfig.get_python_lib())" RESULT_VARIABLE exit_code OUTPUT_VARIABLE CMAKE_INSTALL_PYTHON_PKG_DIR OUTPUT_STRIP_TRAILING_WHITESPACE ) if (NOT ("${exit_code}" EQUAL 0)) message(FATAL_ERROR "Failed to determine your Python package directory") endif() message(STATUS "Detected Python package directory: \"${CMAKE_INSTALL_PYTHON_PKG_DIR}\"") # Set a cache variable that the user can modify if needed set(CMAKE_INSTALL_PYTHON_PKG_DIR "${CMAKE_INSTALL_PYTHON_PKG_DIR}" CACHE PATH "Path to install python bindings. This can be relative or absolute.") mark_as_advanced(CMAKE_INSTALL_PYTHON_PKG_DIR) else() message(STATUS "CMAKE_INSTALL_PYTHON_PKG_DIR already set (\"${CMAKE_INSTALL_PYTHON_PKG_DIR}\")" ". Not trying to guess.") endif() # Check if path exists under the install prefix if it is absolute. If the # path is relative it will be installed under the install prefix so there # if nothing to check if (IS_ABSOLUTE "${CMAKE_INSTALL_PYTHON_PKG_DIR}") string(FIND "${CMAKE_INSTALL_PYTHON_PKG_DIR}" "${CMAKE_INSTALL_PREFIX}" position) if (NOT ("${position}" EQUAL 0)) message(WARNING "The directory to install the python bindings \"${CMAKE_INSTALL_PYTHON_PKG_DIR}\" " "is not under the install prefix \"${CMAKE_INSTALL_PREFIX}\"." " Running the install target may lead to a broken installation. " "To change the install directory modify the CMAKE_INSTALL_PYTHON_PKG_DIR cache variable." ) endif() endif() # Using DESTDIR still seems to work even if we use an absolute path message(STATUS "Python bindings will be installed to \"${CMAKE_INSTALL_PYTHON_PKG_DIR}\"") install(FILES ${build_z3_python_bindings_target_depends} DESTINATION "${CMAKE_INSTALL_PYTHON_PKG_DIR}/z3" ) else() message(STATUS "Not emitting rules to install Z3 python bindings") endif() z3-z3-4.8.7/src/api/python/MANIFEST.in000066400000000000000000000001711356505360400170450ustar00rootroot00000000000000include core/LICENSE.txt recursive-include core/src * recursive-include core/scripts * recursive-include core/examples * z3-z3-4.8.7/src/api/python/README.txt000066400000000000000000000007751356505360400170170ustar00rootroot00000000000000On 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 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 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 z3-z3-4.8.7/src/api/python/setup.py000066400000000000000000000260201356505360400170220ustar00rootroot00000000000000import os import sys import shutil import platform import subprocess import multiprocessing import re import glob from setuptools import setup 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'] = build_env.get('CXXFLAGS', '') + " -std=c++11" # determine where we're building and where sources are ROOT_DIR = os.path.abspath(os.path.dirname(__file__)) SRC_DIR_LOCAL = os.path.join(ROOT_DIR, 'core') SRC_DIR_REPO = os.path.join(ROOT_DIR, '..', '..', '..') SRC_DIR = SRC_DIR_LOCAL if os.path.exists(SRC_DIR_LOCAL) else SRC_DIR_REPO # determine where binaries are RELEASE_DIR = os.environ.get('PACKAGE_FROM_RELEASE', None) if RELEASE_DIR is None: BUILD_DIR = os.path.join(SRC_DIR, 'build') # implicit in configure script HEADER_DIRS = [os.path.join(SRC_DIR, 'src', 'api'), os.path.join(SRC_DIR, 'src', 'api', 'c++')] RELEASE_METADATA = None BUILD_PLATFORM = sys.platform else: if not os.path.isdir(RELEASE_DIR): raise Exception("RELEASE_DIR (%s) is not a directory!" % RELEASE_DIR) BUILD_DIR = os.path.join(RELEASE_DIR, 'bin') HEADER_DIRS = [os.path.join(RELEASE_DIR, 'include')] RELEASE_METADATA = os.path.basename(RELEASE_DIR).split('-') if RELEASE_METADATA[0] != 'z3' or len(RELEASE_METADATA) not in (4, 5): raise Exception("RELEASE_DIR (%s) must be in the format z3-version-arch-os[-osversion] so we can extract metadata from it. Sorry!" % RELEASE_DIR) RELEASE_METADATA.pop(0) BUILD_PLATFORM = RELEASE_METADATA[2] # determine where destinations are LIBS_DIR = os.path.join(ROOT_DIR, 'z3', 'lib') HEADERS_DIR = os.path.join(ROOT_DIR, 'z3', 'include') BINS_DIR = os.path.join(ROOT_DIR, 'bin') # determine platform-specific filenames if BUILD_PLATFORM in ('darwin', 'osx'): LIBRARY_FILE = "libz3.dylib" EXECUTABLE_FILE = "z3" elif BUILD_PLATFORM in ('win32', 'cygwin', 'win'): LIBRARY_FILE = "libz3.dll" EXECUTABLE_FILE = "z3.exe" 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 """ 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', '') if RELEASE_DIR is None: fn = os.path.join(SRC_DIR, 'scripts', 'mk_project.py') if os.path.exists(fn): with open(fn) as f: for line in f: n = re.match(r".*set_version\((.*), (.*), (.*), (.*)\).*", line) if not n is None: return n.group(1) + '.' + n.group(2) + '.' + n.group(3) + '.' + n.group(4) + post return "?.?.?.?" else: version = RELEASE_METADATA[0] if version.count('.') == 2: version += '.0' return version + post def _configure_z3(): # bail out early if we don't need to do this - it forces a rebuild every time otherwise if os.path.exists(BUILD_DIR): return args = [sys.executable, os.path.join(SRC_DIR, 'scripts', 'mk_make.py')] if sys.platform == 'win32' and platform.architecture()[0] == '64bit': args += ['-x'] if subprocess.call(args, env=build_env, cwd=SRC_DIR) != 0: raise LibError("Unable to configure Z3.") def _build_z3(): if sys.platform == 'win32': if subprocess.call(['nmake'], env=build_env, cwd=BUILD_DIR) != 0: raise LibError("Unable to build Z3.") else: # linux and macOS if subprocess.call(['make', '-j', str(multiprocessing.cpu_count())], env=build_env, cwd=BUILD_DIR) != 0: raise LibError("Unable to build Z3.") def _copy_bins(): """ Copy the library and header files into their final destinations """ # STEP 1: If we're performing a build from a copied source tree, # copy the generated python files into the package _clean_bins() python_dir = None if RELEASE_DIR is not None: python_dir = os.path.join(RELEASE_DIR, 'bin', 'python') elif SRC_DIR == SRC_DIR_LOCAL: python_dir = os.path.join(SRC_DIR, 'src', 'api', 'python') if python_dir is not None: shutil.copy(os.path.join(python_dir, 'z3', 'z3core.py'), os.path.join(ROOT_DIR, 'z3')) shutil.copy(os.path.join(python_dir, 'z3', 'z3consts.py'), os.path.join(ROOT_DIR, 'z3')) # STEP 2: Copy the shared library, the executable and the headers os.mkdir(LIBS_DIR) os.mkdir(BINS_DIR) os.mkdir(HEADERS_DIR) shutil.copy(os.path.join(BUILD_DIR, LIBRARY_FILE), LIBS_DIR) shutil.copy(os.path.join(BUILD_DIR, EXECUTABLE_FILE), BINS_DIR) for filepath in glob.glob(os.path.join(BUILD_DIR, "msvcp*")) + glob.glob(os.path.join(BUILD_DIR, "vcomp*")): shutil.copy(filepath, LIBS_DIR) for header_dir in HEADER_DIRS: for fname in os.listdir(header_dir): if not fname.endswith('.h'): continue shutil.copy(os.path.join(header_dir, fname), os.path.join(HEADERS_DIR, fname)) def _copy_sources(): """ Prepare for a source distribution by assembling a minimal set of source files needed for building """ shutil.rmtree(SRC_DIR_LOCAL, ignore_errors=True) os.mkdir(SRC_DIR_LOCAL) shutil.copy(os.path.join(SRC_DIR_REPO, 'LICENSE.txt'), SRC_DIR_LOCAL) shutil.copytree(os.path.join(SRC_DIR_REPO, 'scripts'), os.path.join(SRC_DIR_LOCAL, 'scripts')) shutil.copytree(os.path.join(SRC_DIR_REPO, 'examples'), os.path.join(SRC_DIR_LOCAL, 'examples')) shutil.copytree(os.path.join(SRC_DIR_REPO, 'src'), os.path.join(SRC_DIR_LOCAL, 'src'), ignore=lambda src, names: ['python'] if 'api' in src else []) # stub python dir to make build happy os.mkdir(os.path.join(SRC_DIR_LOCAL, 'src', 'api', 'python')) os.mkdir(os.path.join(SRC_DIR_LOCAL, 'src', 'api', 'python', 'z3')) open(os.path.join(SRC_DIR_LOCAL, 'src', 'api', 'python', 'z3', '.placeholder'), 'w').close() open(os.path.join(SRC_DIR_LOCAL, 'src', 'api', 'python', 'z3test.py'), 'w').close() class build(_build): def run(self): if RELEASE_DIR is None: self.execute(_configure_z3, (), msg="Configuring Z3") self.execute(_build_z3, (), msg="Building Z3") self.execute(_copy_bins, (), msg="Copying binaries") _build.run(self) class develop(_develop): def run(self): self.execute(_configure_z3, (), msg="Configuring Z3") self.execute(_build_z3, (), msg="Building Z3") self.execute(_copy_bins, (), msg="Copying binaries") _develop.run(self) class bdist_egg(_bdist_egg): def run(self): self.run_command('build') _bdist_egg.run(self) class sdist(_sdist): def run(self): 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 if 'bdist_wheel' in sys.argv and '--plat-name' not in sys.argv: if RELEASE_DIR is None: name = get_platform() if 'linux' in name: # linux_* platform tags are disallowed because the python ecosystem is fubar # linux builds should be built in the centos 5 vm for maximum compatibility # see https://github.com/pypa/manylinux # see also https://github.com/angr/angr-dev/blob/master/admin/bdist.py plat_name = 'manylinux1_' + platform.machine() elif 'mingw' in name: if platform.architecture()[0] == '64bit': plat_name = 'win_amd64' else: plat_name ='win32' else: # https://www.python.org/dev/peps/pep-0425/ plat_name = name.replace('.', '_').replace('-', '_') else: # extract the architecture of the release from the directory name arch = RELEASE_METADATA[1] distos = RELEASE_METADATA[2] if distos in ('debian', 'ubuntu') or 'linux' in distos: raise Exception("Linux binary distributions must be built on centos to conform to PEP 513") elif distos == 'centos': if arch == 'x64': plat_name = 'manylinux1_x86_64' else: plat_name = 'manylinux1_i686' elif distos == 'win': if arch == 'x64': plat_name = 'win_amd64' else: plat_name = 'win32' elif distos == 'osx': osver = RELEASE_METADATA[3] if osver.count('.') > 1: osver = '.'.join(osver.split('.')[:2]) if arch == 'x64': plat_name ='macosx_%s_x86_64' % osver.replace('.', '_') else: raise Exception('idk how os x works. what goes here?') else: raise Exception("idk how to translate between this z3 release os and the python naming scheme") idx = sys.argv.index('bdist_wheel') + 1 sys.argv.insert(idx, '--plat-name') sys.argv.insert(idx + 1, plat_name) sys.argv.insert(idx + 2, '--universal') # supports py2+py3. if --plat-name is not specified this will also mean that the package can be installed on any machine regardless of architecture, so watch out! 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, 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", url='https://github.com/Z3Prover/z3', license='MIT License', keywords=['z3', 'smt', 'sat', 'prover', 'theorem'], packages=['z3'], include_package_data=True, package_data={ '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, 'clean': clean}, ) z3-z3-4.8.7/src/api/python/z3/000077500000000000000000000000001356505360400156445ustar00rootroot00000000000000z3-z3-4.8.7/src/api/python/z3/__init__.py000066400000000000000000000003221356505360400177520ustar00rootroot00000000000000from .z3 import * from . import z3num from . import z3poly from . import z3printer from . import z3rcf from . import z3types from . import z3util # generated files from . import z3core from . import z3consts z3-z3-4.8.7/src/api/python/z3/z3.py000066400000000000000000011361461356505360400165660ustar00rootroot00000000000000############################################ # Copyright (c) 2012 Microsoft Corporation # # Z3 Python interface # # Author: Leonardo de Moura (leonardo) ############################################ """Z3 is a high performance theorem prover developed at Microsoft Research. Z3 is used in many applications such as: software/hardware verification and testing, constraint solving, analysis of hybrid systems, security, biology (in silico analysis), and geometrical problems. Several online tutorials for Z3Py are available at: http://rise4fun.com/Z3Py/tutorial/guide Please send feedback, comments and/or corrections on the Issue tracker for https://github.com/Z3prover/z3.git. Your comments are very valuable. Small example: >>> x = Int('x') >>> y = Int('y') >>> s = Solver() >>> s.add(x > 0) >>> s.add(x < 2) >>> s.add(y == x + 1) >>> s.check() sat >>> m = s.model() >>> m[x] 1 >>> m[y] 2 Z3 exceptions: >>> try: ... x = BitVec('x', 32) ... y = Bool('y') ... # the expression x + y is type incorrect ... n = x + y ... except Z3Exception as ex: ... print("failed: %s" % ex) failed: sort mismatch """ from . import z3core from .z3core import * from .z3types import * from .z3consts import * from .z3printer import * from fractions import Fraction import sys import io import math import copy Z3_DEBUG = __debug__ def z3_debug(): global Z3_DEBUG return Z3_DEBUG if sys.version < '3': def _is_int(v): return isinstance(v, (int, long)) else: def _is_int(v): return isinstance(v, int) def enable_trace(msg): Z3_enable_trace(msg) def disable_trace(msg): Z3_disable_trace(msg) def get_version_string(): major = ctypes.c_uint(0) minor = ctypes.c_uint(0) build = ctypes.c_uint(0) rev = ctypes.c_uint(0) Z3_get_version(major, minor, build, rev) return "%s.%s.%s" % (major.value, minor.value, build.value) def get_version(): major = ctypes.c_uint(0) minor = ctypes.c_uint(0) build = ctypes.c_uint(0) rev = ctypes.c_uint(0) Z3_get_version(major, minor, build, rev) return (major.value, minor.value, build.value, rev.value) def get_full_version(): return Z3_get_full_version() # We use _z3_assert instead of the assert command because we want to # produce nice error messages in Z3Py at rise4fun.com def _z3_assert(cond, msg): if not cond: raise Z3Exception(msg) def _z3_check_cint_overflow(n, name): _z3_assert(ctypes.c_int(n).value == n, name + " is too large") def open_log(fname): """Log interaction to a file. This function must be invoked immediately after init(). """ Z3_open_log(fname) def append_log(s): """Append user-defined string to interaction log. """ Z3_append_log(s) def to_symbol(s, ctx=None): """Convert an integer or string into a Z3 symbol.""" if _is_int(s): return Z3_mk_int_symbol(_get_ctx(ctx).ref(), s) else: return Z3_mk_string_symbol(_get_ctx(ctx).ref(), s) def _symbol2py(ctx, s): """Convert a Z3 symbol back into a Python object. """ if Z3_get_symbol_kind(ctx.ref(), s) == Z3_INT_SYMBOL: return "k!%s" % Z3_get_symbol_int(ctx.ref(), s) else: return Z3_get_symbol_string(ctx.ref(), s) # Hack for having nary functions that can receive one argument that is the # list of arguments. # Use this when function takes a single list of arguments def _get_args(args): try: if len(args) == 1 and (isinstance(args[0], tuple) or isinstance(args[0], list)): return args[0] elif len(args) == 1 and (isinstance(args[0], set) or isinstance(args[0], AstVector)): return [arg for arg in args[0]] else: return args except: # len is not necessarily defined when args is not a sequence (use reflection?) return args # Use this when function takes multiple arguments def _get_args_ast_list(args): try: if isinstance(args, set) or isinstance(args, AstVector) or isinstance(args, tuple): return [arg for arg in args] else: return args except: return args def _to_param_value(val): if isinstance(val, bool): if val == True: return "true" else: return "false" else: return str(val) def z3_error_handler(c, e): # Do nothing error handler, just avoid exit(0) # The wrappers in z3core.py will raise a Z3Exception if an error is detected return class Context: """A Context manages all other Z3 objects, global configuration options, etc. Z3Py uses a default global context. For most applications this is sufficient. An application may use multiple Z3 contexts. Objects created in one context cannot be used in another one. However, several objects may be "translated" from one context to another. It is not safe to access Z3 objects from multiple threads. The only exception is the method `interrupt()` that can be used to interrupt() a long computation. The initialization method receives global configuration options for the new context. """ def __init__(self, *args, **kws): if z3_debug(): _z3_assert(len(args) % 2 == 0, "Argument list must have an even number of elements.") conf = Z3_mk_config() for key in kws: value = kws[key] Z3_set_param_value(conf, str(key).upper(), _to_param_value(value)) prev = None for a in args: if prev is None: prev = a else: Z3_set_param_value(conf, str(prev), _to_param_value(a)) prev = None self.ctx = Z3_mk_context_rc(conf) self.eh = Z3_set_error_handler(self.ctx, z3_error_handler) Z3_set_ast_print_mode(self.ctx, Z3_PRINT_SMTLIB2_COMPLIANT) Z3_del_config(conf) def __del__(self): Z3_del_context(self.ctx) self.ctx = None self.eh = None def ref(self): """Return a reference to the actual C pointer to the Z3 context.""" return self.ctx def interrupt(self): """Interrupt a solver performing a satisfiability test, a tactic processing a goal, or simplify functions. This method can be invoked from a thread different from the one executing the interruptible procedure. """ Z3_interrupt(self.ref()) # Global Z3 context _main_ctx = None def main_ctx(): """Return a reference to the global Z3 context. >>> x = Real('x') >>> x.ctx == main_ctx() True >>> c = Context() >>> c == main_ctx() False >>> x2 = Real('x', c) >>> x2.ctx == c True >>> eq(x, x2) False """ global _main_ctx if _main_ctx is None: _main_ctx = Context() return _main_ctx def _get_ctx(ctx): if ctx is None: return main_ctx() else: return ctx def get_ctx(ctx): return _get_ctx(ctx) def set_param(*args, **kws): """Set Z3 global (or module) parameters. >>> set_param(precision=10) """ if z3_debug(): _z3_assert(len(args) % 2 == 0, "Argument list must have an even number of elements.") new_kws = {} for k in kws: v = kws[k] if not set_pp_option(k, v): new_kws[k] = v for key in new_kws: value = new_kws[key] Z3_global_param_set(str(key).upper(), _to_param_value(value)) prev = None for a in args: if prev is None: prev = a else: Z3_global_param_set(str(prev), _to_param_value(a)) prev = None def reset_params(): """Reset all global (or module) parameters. """ Z3_global_param_reset_all() def set_option(*args, **kws): """Alias for 'set_param' for backward compatibility. """ return set_param(*args, **kws) def get_param(name): """Return the value of a Z3 global (or module) parameter >>> get_param('nlsat.reorder') 'true' """ ptr = (ctypes.c_char_p * 1)() if Z3_global_param_get(str(name), ptr): r = z3core._to_pystr(ptr[0]) return r raise Z3Exception("failed to retrieve value for '%s'" % name) ######################################### # # ASTs base class # ######################################### # Mark objects that use pretty printer class Z3PPObject: """Superclass for all Z3 objects that have support for pretty printing.""" def use_pp(self): return True def _repr_html_(self): in_html = in_html_mode() set_html_mode(True) res = repr(self) set_html_mode(in_html) return res class AstRef(Z3PPObject): """AST are Direct Acyclic Graphs (DAGs) used to represent sorts, declarations and expressions.""" def __init__(self, ast, ctx=None): self.ast = ast self.ctx = _get_ctx(ctx) Z3_inc_ref(self.ctx.ref(), self.as_ast()) def __del__(self): if self.ctx.ref() is not None and self.ast is not None: Z3_dec_ref(self.ctx.ref(), self.as_ast()) self.ast = None def __deepcopy__(self, memo={}): return _to_ast_ref(self.ast, self.ctx) def __str__(self): return obj_to_string(self) def __repr__(self): return obj_to_string(self) def __eq__(self, other): return self.eq(other) def __hash__(self): return self.hash() def __nonzero__(self): return self.__bool__() def __bool__(self): if is_true(self): return True elif is_false(self): return False elif is_eq(self) and self.num_args() == 2: return self.arg(0).eq(self.arg(1)) else: raise Z3Exception("Symbolic expressions cannot be cast to concrete Boolean values.") def sexpr(self): """Return a string representing the AST node in s-expression notation. >>> x = Int('x') >>> ((x + 1)*x).sexpr() '(* (+ x 1) x)' """ return Z3_ast_to_string(self.ctx_ref(), self.as_ast()) def as_ast(self): """Return a pointer to the corresponding C Z3_ast object.""" return self.ast def get_id(self): """Return unique identifier for object. It can be used for hash-tables and maps.""" return Z3_get_ast_id(self.ctx_ref(), self.as_ast()) def ctx_ref(self): """Return a reference to the C context where this AST node is stored.""" return self.ctx.ref() def eq(self, other): """Return `True` if `self` and `other` are structurally identical. >>> x = Int('x') >>> n1 = x + 1 >>> n2 = 1 + x >>> n1.eq(n2) False >>> n1 = simplify(n1) >>> n2 = simplify(n2) >>> n1.eq(n2) True """ if z3_debug(): _z3_assert(is_ast(other), "Z3 AST expected") return Z3_is_eq_ast(self.ctx_ref(), self.as_ast(), other.as_ast()) def translate(self, target): """Translate `self` to the context `target`. That is, return a copy of `self` in the context `target`. >>> c1 = Context() >>> c2 = Context() >>> x = Int('x', c1) >>> y = Int('y', c2) >>> # Nodes in different contexts can't be mixed. >>> # However, we can translate nodes from one context to another. >>> x.translate(c2) + y x + y """ if z3_debug(): _z3_assert(isinstance(target, Context), "argument must be a Z3 context") return _to_ast_ref(Z3_translate(self.ctx.ref(), self.as_ast(), target.ref()), target) def __copy__(self): return self.translate(self.ctx) def hash(self): """Return a hashcode for the `self`. >>> n1 = simplify(Int('x') + 1) >>> n2 = simplify(2 + Int('x') - 1) >>> n1.hash() == n2.hash() True """ return Z3_get_ast_hash(self.ctx_ref(), self.as_ast()) def is_ast(a): """Return `True` if `a` is an AST node. >>> is_ast(10) False >>> is_ast(IntVal(10)) True >>> is_ast(Int('x')) True >>> is_ast(BoolSort()) True >>> is_ast(Function('f', IntSort(), IntSort())) True >>> is_ast("x") False >>> is_ast(Solver()) False """ return isinstance(a, AstRef) def eq(a, b): """Return `True` if `a` and `b` are structurally identical AST nodes. >>> x = Int('x') >>> y = Int('y') >>> eq(x, y) False >>> eq(x + 1, x + 1) True >>> eq(x + 1, 1 + x) False >>> eq(simplify(x + 1), simplify(1 + x)) True """ if z3_debug(): _z3_assert(is_ast(a) and is_ast(b), "Z3 ASTs expected") return a.eq(b) def _ast_kind(ctx, a): if is_ast(a): a = a.as_ast() return Z3_get_ast_kind(ctx.ref(), a) def _ctx_from_ast_arg_list(args, default_ctx=None): ctx = None for a in args: if is_ast(a) or is_probe(a): if ctx is None: ctx = a.ctx else: if z3_debug(): _z3_assert(ctx == a.ctx, "Context mismatch") if ctx is None: ctx = default_ctx return ctx def _ctx_from_ast_args(*args): return _ctx_from_ast_arg_list(args) def _to_func_decl_array(args): sz = len(args) _args = (FuncDecl * sz)() for i in range(sz): _args[i] = args[i].as_func_decl() return _args, sz def _to_ast_array(args): sz = len(args) _args = (Ast * sz)() for i in range(sz): _args[i] = args[i].as_ast() return _args, sz def _to_ref_array(ref, args): sz = len(args) _args = (ref * sz)() for i in range(sz): _args[i] = args[i].as_ast() return _args, sz def _to_ast_ref(a, ctx): k = _ast_kind(ctx, a) if k == Z3_SORT_AST: return _to_sort_ref(a, ctx) elif k == Z3_FUNC_DECL_AST: return _to_func_decl_ref(a, ctx) else: return _to_expr_ref(a, ctx) ######################################### # # Sorts # ######################################### def _sort_kind(ctx, s): return Z3_get_sort_kind(ctx.ref(), s) class SortRef(AstRef): """A Sort is essentially a type. Every Z3 expression has a sort. A sort is an AST node.""" def as_ast(self): return Z3_sort_to_ast(self.ctx_ref(), self.ast) def get_id(self): return Z3_get_ast_id(self.ctx_ref(), self.as_ast()) def kind(self): """Return the Z3 internal kind of a sort. This method can be used to test if `self` is one of the Z3 builtin sorts. >>> b = BoolSort() >>> b.kind() == Z3_BOOL_SORT True >>> b.kind() == Z3_INT_SORT False >>> A = ArraySort(IntSort(), IntSort()) >>> A.kind() == Z3_ARRAY_SORT True >>> A.kind() == Z3_INT_SORT False """ return _sort_kind(self.ctx, self.ast) def subsort(self, other): """Return `True` if `self` is a subsort of `other`. >>> IntSort().subsort(RealSort()) True """ return False def cast(self, val): """Try to cast `val` as an element of sort `self`. This method is used in Z3Py to convert Python objects such as integers, floats, longs and strings into Z3 expressions. >>> x = Int('x') >>> RealSort().cast(x) ToReal(x) """ if z3_debug(): _z3_assert(is_expr(val), "Z3 expression expected") _z3_assert(self.eq(val.sort()), "Sort mismatch") return val def name(self): """Return the name (string) of sort `self`. >>> BoolSort().name() 'Bool' >>> ArraySort(IntSort(), IntSort()).name() 'Array' """ return _symbol2py(self.ctx, Z3_get_sort_name(self.ctx_ref(), self.ast)) def __eq__(self, other): """Return `True` if `self` and `other` are the same Z3 sort. >>> p = Bool('p') >>> p.sort() == BoolSort() True >>> p.sort() == IntSort() False """ if other is None: return False return Z3_is_eq_sort(self.ctx_ref(), self.ast, other.ast) def __ne__(self, other): """Return `True` if `self` and `other` are not the same Z3 sort. >>> p = Bool('p') >>> p.sort() != BoolSort() False >>> p.sort() != IntSort() True """ return not Z3_is_eq_sort(self.ctx_ref(), self.ast, other.ast) def __hash__(self): """ Hash code. """ return AstRef.__hash__(self) def is_sort(s): """Return `True` if `s` is a Z3 sort. >>> is_sort(IntSort()) True >>> is_sort(Int('x')) False >>> is_expr(Int('x')) True """ return isinstance(s, SortRef) def _to_sort_ref(s, ctx): if z3_debug(): _z3_assert(isinstance(s, Sort), "Z3 Sort expected") k = _sort_kind(ctx, s) if k == Z3_BOOL_SORT: return BoolSortRef(s, ctx) elif k == Z3_INT_SORT or k == Z3_REAL_SORT: return ArithSortRef(s, ctx) elif k == Z3_BV_SORT: return BitVecSortRef(s, ctx) elif k == Z3_ARRAY_SORT: return ArraySortRef(s, ctx) elif k == Z3_DATATYPE_SORT: return DatatypeSortRef(s, ctx) elif k == Z3_FINITE_DOMAIN_SORT: return FiniteDomainSortRef(s, ctx) elif k == Z3_FLOATING_POINT_SORT: return FPSortRef(s, ctx) elif k == Z3_ROUNDING_MODE_SORT: return FPRMSortRef(s, ctx) elif k == Z3_RE_SORT: return ReSortRef(s, ctx) elif k == Z3_SEQ_SORT: return SeqSortRef(s, ctx) return SortRef(s, ctx) def _sort(ctx, a): return _to_sort_ref(Z3_get_sort(ctx.ref(), a), ctx) def DeclareSort(name, ctx=None): """Create a new uninterpreted sort named `name`. If `ctx=None`, then the new sort is declared in the global Z3Py context. >>> A = DeclareSort('A') >>> a = Const('a', A) >>> b = Const('b', A) >>> a.sort() == A True >>> b.sort() == A True >>> a == b a == b """ ctx = _get_ctx(ctx) return SortRef(Z3_mk_uninterpreted_sort(ctx.ref(), to_symbol(name, ctx)), ctx) ######################################### # # Function Declarations # ######################################### class FuncDeclRef(AstRef): """Function declaration. Every constant and function have an associated declaration. The declaration assigns a name, a sort (i.e., type), and for function the sort (i.e., type) of each of its arguments. Note that, in Z3, a constant is a function with 0 arguments. """ def as_ast(self): return Z3_func_decl_to_ast(self.ctx_ref(), self.ast) def get_id(self): return Z3_get_ast_id(self.ctx_ref(), self.as_ast()) def as_func_decl(self): return self.ast def name(self): """Return the name of the function declaration `self`. >>> f = Function('f', IntSort(), IntSort()) >>> f.name() 'f' >>> isinstance(f.name(), str) True """ return _symbol2py(self.ctx, Z3_get_decl_name(self.ctx_ref(), self.ast)) def arity(self): """Return the number of arguments of a function declaration. If `self` is a constant, then `self.arity()` is 0. >>> f = Function('f', IntSort(), RealSort(), BoolSort()) >>> f.arity() 2 """ return int(Z3_get_arity(self.ctx_ref(), self.ast)) def domain(self, i): """Return the sort of the argument `i` of a function declaration. This method assumes that `0 <= i < self.arity()`. >>> f = Function('f', IntSort(), RealSort(), BoolSort()) >>> f.domain(0) Int >>> f.domain(1) Real """ if z3_debug(): _z3_assert(i < self.arity(), "Index out of bounds") return _to_sort_ref(Z3_get_domain(self.ctx_ref(), self.ast, i), self.ctx) def range(self): """Return the sort of the range of a function declaration. For constants, this is the sort of the constant. >>> f = Function('f', IntSort(), RealSort(), BoolSort()) >>> f.range() Bool """ return _to_sort_ref(Z3_get_range(self.ctx_ref(), self.ast), self.ctx) def kind(self): """Return the internal kind of a function declaration. It can be used to identify Z3 built-in functions such as addition, multiplication, etc. >>> x = Int('x') >>> d = (x + 1).decl() >>> d.kind() == Z3_OP_ADD True >>> d.kind() == Z3_OP_MUL False """ return Z3_get_decl_kind(self.ctx_ref(), self.ast) def params(self): ctx = self.ctx n = Z3_get_decl_num_parameters(self.ctx_ref(), self.ast) result = [ None for i in range(n) ] for i in range(n): k = Z3_get_decl_parameter_kind(self.ctx_ref(), self.ast, i) if k == Z3_PARAMETER_INT: result[i] = Z3_get_decl_int_parameter(self.ctx_ref(), self.ast, i) elif k == Z3_PARAMETER_DOUBLE: result[i] = Z3_get_decl_double_parameter(self.ctx_ref(), self.ast, i) elif k == Z3_PARAMETER_RATIONAL: result[i] = Z3_get_decl_rational_parameter(self.ctx_ref(), self.ast, i) elif k == Z3_PARAMETER_SYMBOL: result[i] = Z3_get_decl_symbol_parameter(self.ctx_ref(), self.ast, i) elif k == Z3_PARAMETER_SORT: result[i] = SortRef(Z3_get_decl_sort_parameter(self.ctx_ref(), self.ast, i), ctx) elif k == Z3_PARAMETER_AST: result[i] = ExprRef(Z3_get_decl_ast_parameter(self.ctx_ref(), self.ast, i), ctx) elif k == Z3_PARAMETER_FUNC_DECL: result[i] = FuncDeclRef(Z3_get_decl_func_decl_parameter(self.ctx_ref(), self.ast, i), ctx) else: assert(False) return result def __call__(self, *args): """Create a Z3 application expression using the function `self`, and the given arguments. The arguments must be Z3 expressions. This method assumes that the sorts of the elements in `args` match the sorts of the domain. Limited coercion is supported. For example, if args[0] is a Python integer, and the function expects a Z3 integer, then the argument is automatically converted into a Z3 integer. >>> f = Function('f', IntSort(), RealSort(), BoolSort()) >>> x = Int('x') >>> y = Real('y') >>> f(x, y) f(x, y) >>> f(x, x) f(x, ToReal(x)) """ args = _get_args(args) num = len(args) if z3_debug(): _z3_assert(num == self.arity(), "Incorrect number of arguments to %s" % self) _args = (Ast * num)() saved = [] for i in range(num): # self.domain(i).cast(args[i]) may create a new Z3 expression, # then we must save in 'saved' to prevent it from being garbage collected. tmp = self.domain(i).cast(args[i]) saved.append(tmp) _args[i] = tmp.as_ast() return _to_expr_ref(Z3_mk_app(self.ctx_ref(), self.ast, len(args), _args), self.ctx) def is_func_decl(a): """Return `True` if `a` is a Z3 function declaration. >>> f = Function('f', IntSort(), IntSort()) >>> is_func_decl(f) True >>> x = Real('x') >>> is_func_decl(x) False """ return isinstance(a, FuncDeclRef) def Function(name, *sig): """Create a new Z3 uninterpreted function with the given sorts. >>> f = Function('f', IntSort(), IntSort()) >>> f(f(0)) f(f(0)) """ sig = _get_args(sig) if z3_debug(): _z3_assert(len(sig) > 0, "At least two arguments expected") arity = len(sig) - 1 rng = sig[arity] if z3_debug(): _z3_assert(is_sort(rng), "Z3 sort expected") dom = (Sort * arity)() for i in range(arity): if z3_debug(): _z3_assert(is_sort(sig[i]), "Z3 sort expected") dom[i] = sig[i].ast ctx = rng.ctx return FuncDeclRef(Z3_mk_func_decl(ctx.ref(), to_symbol(name, ctx), arity, dom, rng.ast), ctx) def _to_func_decl_ref(a, ctx): return FuncDeclRef(a, ctx) def RecFunction(name, *sig): """Create a new Z3 recursive with the given sorts.""" sig = _get_args(sig) if z3_debug(): _z3_assert(len(sig) > 0, "At least two arguments expected") arity = len(sig) - 1 rng = sig[arity] if z3_debug(): _z3_assert(is_sort(rng), "Z3 sort expected") dom = (Sort * arity)() for i in range(arity): if z3_debug(): _z3_assert(is_sort(sig[i]), "Z3 sort expected") dom[i] = sig[i].ast ctx = rng.ctx return FuncDeclRef(Z3_mk_rec_func_decl(ctx.ref(), to_symbol(name, ctx), arity, dom, rng.ast), ctx) def RecAddDefinition(f, args, body): """Set the body of a recursive function. Recursive definitions are only unfolded during search. >>> ctx = Context() >>> fac = RecFunction('fac', IntSort(ctx), IntSort(ctx)) >>> n = Int('n', ctx) >>> RecAddDefinition(fac, n, If(n == 0, 1, n*fac(n-1))) >>> simplify(fac(5)) fac(5) >>> s = Solver(ctx=ctx) >>> s.add(fac(n) < 3) >>> s.check() sat >>> s.model().eval(fac(5)) 120 """ if is_app(args): args = [args] ctx = body.ctx args = _get_args(args) n = len(args) _args = (Ast * n)() for i in range(n): _args[i] = args[i].ast Z3_add_rec_def(ctx.ref(), f.ast, n, _args, body.ast) ######################################### # # Expressions # ######################################### class ExprRef(AstRef): """Constraints, formulas and terms are expressions in Z3. Expressions are ASTs. Every expression has a sort. There are three main kinds of expressions: function applications, quantifiers and bounded variables. A constant is a function application with 0 arguments. For quantifier free problems, all expressions are function applications. """ def as_ast(self): return self.ast def get_id(self): return Z3_get_ast_id(self.ctx_ref(), self.as_ast()) def sort(self): """Return the sort of expression `self`. >>> x = Int('x') >>> (x + 1).sort() Int >>> y = Real('y') >>> (x + y).sort() Real """ return _sort(self.ctx, self.as_ast()) def sort_kind(self): """Shorthand for `self.sort().kind()`. >>> a = Array('a', IntSort(), IntSort()) >>> a.sort_kind() == Z3_ARRAY_SORT True >>> a.sort_kind() == Z3_INT_SORT False """ return self.sort().kind() def __eq__(self, other): """Return a Z3 expression that represents the constraint `self == other`. If `other` is `None`, then this method simply returns `False`. >>> a = Int('a') >>> b = Int('b') >>> a == b a == b >>> a is None False """ if other is None: return False a, b = _coerce_exprs(self, other) return BoolRef(Z3_mk_eq(self.ctx_ref(), a.as_ast(), b.as_ast()), self.ctx) def __hash__(self): """ Hash code. """ return AstRef.__hash__(self) def __ne__(self, other): """Return a Z3 expression that represents the constraint `self != other`. If `other` is `None`, then this method simply returns `True`. >>> a = Int('a') >>> b = Int('b') >>> a != b a != b >>> a is not None True """ if other is None: return True a, b = _coerce_exprs(self, other) _args, sz = _to_ast_array((a, b)) return BoolRef(Z3_mk_distinct(self.ctx_ref(), 2, _args), self.ctx) def params(self): return self.decl().params() def decl(self): """Return the Z3 function declaration associated with a Z3 application. >>> f = Function('f', IntSort(), IntSort()) >>> a = Int('a') >>> t = f(a) >>> eq(t.decl(), f) True >>> (a + 1).decl() + """ if z3_debug(): _z3_assert(is_app(self), "Z3 application expected") return FuncDeclRef(Z3_get_app_decl(self.ctx_ref(), self.as_ast()), self.ctx) def num_args(self): """Return the number of arguments of a Z3 application. >>> a = Int('a') >>> b = Int('b') >>> (a + b).num_args() 2 >>> f = Function('f', IntSort(), IntSort(), IntSort(), IntSort()) >>> t = f(a, b, 0) >>> t.num_args() 3 """ if z3_debug(): _z3_assert(is_app(self), "Z3 application expected") return int(Z3_get_app_num_args(self.ctx_ref(), self.as_ast())) def arg(self, idx): """Return argument `idx` of the application `self`. This method assumes that `self` is a function application with at least `idx+1` arguments. >>> a = Int('a') >>> b = Int('b') >>> f = Function('f', IntSort(), IntSort(), IntSort(), IntSort()) >>> t = f(a, b, 0) >>> t.arg(0) a >>> t.arg(1) b >>> t.arg(2) 0 """ if z3_debug(): _z3_assert(is_app(self), "Z3 application expected") _z3_assert(idx < self.num_args(), "Invalid argument index") return _to_expr_ref(Z3_get_app_arg(self.ctx_ref(), self.as_ast(), idx), self.ctx) def children(self): """Return a list containing the children of the given expression >>> a = Int('a') >>> b = Int('b') >>> f = Function('f', IntSort(), IntSort(), IntSort(), IntSort()) >>> t = f(a, b, 0) >>> t.children() [a, b, 0] """ if is_app(self): return [self.arg(i) for i in range(self.num_args())] else: return [] def _to_expr_ref(a, ctx): if isinstance(a, Pattern): return PatternRef(a, ctx) ctx_ref = ctx.ref() k = Z3_get_ast_kind(ctx_ref, a) if k == Z3_QUANTIFIER_AST: return QuantifierRef(a, ctx) sk = Z3_get_sort_kind(ctx_ref, Z3_get_sort(ctx_ref, a)) if sk == Z3_BOOL_SORT: return BoolRef(a, ctx) if sk == Z3_INT_SORT: if k == Z3_NUMERAL_AST: return IntNumRef(a, ctx) return ArithRef(a, ctx) if sk == Z3_REAL_SORT: if k == Z3_NUMERAL_AST: return RatNumRef(a, ctx) if _is_algebraic(ctx, a): return AlgebraicNumRef(a, ctx) return ArithRef(a, ctx) if sk == Z3_BV_SORT: if k == Z3_NUMERAL_AST: return BitVecNumRef(a, ctx) else: return BitVecRef(a, ctx) if sk == Z3_ARRAY_SORT: return ArrayRef(a, ctx) if sk == Z3_DATATYPE_SORT: return DatatypeRef(a, ctx) if sk == Z3_FLOATING_POINT_SORT: if k == Z3_APP_AST and _is_numeral(ctx, a): return FPNumRef(a, ctx) else: return FPRef(a, ctx) if sk == Z3_FINITE_DOMAIN_SORT: if k == Z3_NUMERAL_AST: return FiniteDomainNumRef(a, ctx) else: return FiniteDomainRef(a, ctx) if sk == Z3_ROUNDING_MODE_SORT: return FPRMRef(a, ctx) if sk == Z3_SEQ_SORT: return SeqRef(a, ctx) if sk == Z3_RE_SORT: return ReRef(a, ctx) return ExprRef(a, ctx) def _coerce_expr_merge(s, a): if is_expr(a): s1 = a.sort() if s is None: return s1 if s1.eq(s): return s elif s.subsort(s1): return s1 elif s1.subsort(s): return s else: if z3_debug(): _z3_assert(s1.ctx == s.ctx, "context mismatch") _z3_assert(False, "sort mismatch") else: return s def _coerce_exprs(a, b, ctx=None): if not is_expr(a) and not is_expr(b): a = _py2expr(a, ctx) b = _py2expr(b, ctx) s = None s = _coerce_expr_merge(s, a) s = _coerce_expr_merge(s, b) a = s.cast(a) b = s.cast(b) return (a, b) def _reduce(f, l, a): r = a for e in l: r = f(r, e) return r def _coerce_expr_list(alist, ctx=None): has_expr = False for a in alist: if is_expr(a): has_expr = True break if not has_expr: alist = [ _py2expr(a, ctx) for a in alist ] s = _reduce(_coerce_expr_merge, alist, None) return [ s.cast(a) for a in alist ] def is_expr(a): """Return `True` if `a` is a Z3 expression. >>> a = Int('a') >>> is_expr(a) True >>> is_expr(a + 1) True >>> is_expr(IntSort()) False >>> is_expr(1) False >>> is_expr(IntVal(1)) True >>> x = Int('x') >>> is_expr(ForAll(x, x >= 0)) True >>> is_expr(FPVal(1.0)) True """ return isinstance(a, ExprRef) def is_app(a): """Return `True` if `a` is a Z3 function application. Note that, constants are function applications with 0 arguments. >>> a = Int('a') >>> is_app(a) True >>> is_app(a + 1) True >>> is_app(IntSort()) False >>> is_app(1) False >>> is_app(IntVal(1)) True >>> x = Int('x') >>> is_app(ForAll(x, x >= 0)) False """ if not isinstance(a, ExprRef): return False k = _ast_kind(a.ctx, a) return k == Z3_NUMERAL_AST or k == Z3_APP_AST def is_const(a): """Return `True` if `a` is Z3 constant/variable expression. >>> a = Int('a') >>> is_const(a) True >>> is_const(a + 1) False >>> is_const(1) False >>> is_const(IntVal(1)) True >>> x = Int('x') >>> is_const(ForAll(x, x >= 0)) False """ return is_app(a) and a.num_args() == 0 def is_var(a): """Return `True` if `a` is variable. Z3 uses de-Bruijn indices for representing bound variables in quantifiers. >>> x = Int('x') >>> is_var(x) False >>> is_const(x) True >>> f = Function('f', IntSort(), IntSort()) >>> # Z3 replaces x with bound variables when ForAll is executed. >>> q = ForAll(x, f(x) == x) >>> b = q.body() >>> b f(Var(0)) == Var(0) >>> b.arg(1) Var(0) >>> is_var(b.arg(1)) True """ return is_expr(a) and _ast_kind(a.ctx, a) == Z3_VAR_AST def get_var_index(a): """Return the de-Bruijn index of the Z3 bounded variable `a`. >>> x = Int('x') >>> y = Int('y') >>> is_var(x) False >>> is_const(x) True >>> f = Function('f', IntSort(), IntSort(), IntSort()) >>> # Z3 replaces x and y with bound variables when ForAll is executed. >>> q = ForAll([x, y], f(x, y) == x + y) >>> q.body() f(Var(1), Var(0)) == Var(1) + Var(0) >>> b = q.body() >>> b.arg(0) f(Var(1), Var(0)) >>> v1 = b.arg(0).arg(0) >>> v2 = b.arg(0).arg(1) >>> v1 Var(1) >>> v2 Var(0) >>> get_var_index(v1) 1 >>> get_var_index(v2) 0 """ if z3_debug(): _z3_assert(is_var(a), "Z3 bound variable expected") return int(Z3_get_index_value(a.ctx.ref(), a.as_ast())) def is_app_of(a, k): """Return `True` if `a` is an application of the given kind `k`. >>> x = Int('x') >>> n = x + 1 >>> is_app_of(n, Z3_OP_ADD) True >>> is_app_of(n, Z3_OP_MUL) False """ return is_app(a) and a.decl().kind() == k def If(a, b, c, ctx=None): """Create a Z3 if-then-else expression. >>> x = Int('x') >>> y = Int('y') >>> max = If(x > y, x, y) >>> max If(x > y, x, y) >>> simplify(max) If(x <= y, y, x) """ if isinstance(a, Probe) or isinstance(b, Tactic) or isinstance(c, Tactic): return Cond(a, b, c, ctx) else: ctx = _get_ctx(_ctx_from_ast_arg_list([a, b, c], ctx)) s = BoolSort(ctx) a = s.cast(a) b, c = _coerce_exprs(b, c, ctx) if z3_debug(): _z3_assert(a.ctx == b.ctx, "Context mismatch") return _to_expr_ref(Z3_mk_ite(ctx.ref(), a.as_ast(), b.as_ast(), c.as_ast()), ctx) def Distinct(*args): """Create a Z3 distinct expression. >>> x = Int('x') >>> y = Int('y') >>> Distinct(x, y) x != y >>> z = Int('z') >>> Distinct(x, y, z) Distinct(x, y, z) >>> simplify(Distinct(x, y, z)) Distinct(x, y, z) >>> simplify(Distinct(x, y, z), blast_distinct=True) And(Not(x == y), Not(x == z), Not(y == z)) """ args = _get_args(args) ctx = _ctx_from_ast_arg_list(args) if z3_debug(): _z3_assert(ctx is not None, "At least one of the arguments must be a Z3 expression") args = _coerce_expr_list(args, ctx) _args, sz = _to_ast_array(args) return BoolRef(Z3_mk_distinct(ctx.ref(), sz, _args), ctx) def _mk_bin(f, a, b): args = (Ast * 2)() if z3_debug(): _z3_assert(a.ctx == b.ctx, "Context mismatch") args[0] = a.as_ast() args[1] = b.as_ast() return f(a.ctx.ref(), 2, args) def Const(name, sort): """Create a constant of the given sort. >>> Const('x', IntSort()) x """ if z3_debug(): _z3_assert(isinstance(sort, SortRef), "Z3 sort expected") ctx = sort.ctx return _to_expr_ref(Z3_mk_const(ctx.ref(), to_symbol(name, ctx), sort.ast), ctx) def Consts(names, sort): """Create several constants of the given sort. `names` is a string containing the names of all constants to be created. Blank spaces separate the names of different constants. >>> x, y, z = Consts('x y z', IntSort()) >>> x + y + z x + y + z """ if isinstance(names, str): names = names.split(" ") return [Const(name, sort) for name in names] def FreshConst(sort, prefix='c'): """Create a fresh constant of a specified sort""" ctx = _get_ctx(sort.ctx) return _to_expr_ref(Z3_mk_fresh_const(ctx.ref(), prefix, sort.ast), ctx) def Var(idx, s): """Create a Z3 free variable. Free variables are used to create quantified formulas. >>> Var(0, IntSort()) Var(0) >>> eq(Var(0, IntSort()), Var(0, BoolSort())) False """ if z3_debug(): _z3_assert(is_sort(s), "Z3 sort expected") return _to_expr_ref(Z3_mk_bound(s.ctx_ref(), idx, s.ast), s.ctx) def RealVar(idx, ctx=None): """ Create a real free variable. Free variables are used to create quantified formulas. They are also used to create polynomials. >>> RealVar(0) Var(0) """ return Var(idx, RealSort(ctx)) def RealVarVector(n, ctx=None): """ Create a list of Real free variables. The variables have ids: 0, 1, ..., n-1 >>> x0, x1, x2, x3 = RealVarVector(4) >>> x2 Var(2) """ return [ RealVar(i, ctx) for i in range(n) ] ######################################### # # Booleans # ######################################### class BoolSortRef(SortRef): """Boolean sort.""" def cast(self, val): """Try to cast `val` as a Boolean. >>> x = BoolSort().cast(True) >>> x True >>> is_expr(x) True >>> is_expr(True) False >>> x.sort() Bool """ if isinstance(val, bool): return BoolVal(val, self.ctx) if z3_debug(): if not is_expr(val): _z3_assert(is_expr(val), "True, False or Z3 Boolean expression expected. Received %s" % val) if not self.eq(val.sort()): _z3_assert(self.eq(val.sort()), "Value cannot be converted into a Z3 Boolean value") return val def subsort(self, other): return isinstance(other, ArithSortRef) def is_int(self): return True def is_bool(self): return True class BoolRef(ExprRef): """All Boolean expressions are instances of this class.""" def sort(self): return BoolSortRef(Z3_get_sort(self.ctx_ref(), self.as_ast()), self.ctx) def __rmul__(self, other): return self * other def __mul__(self, other): """Create the Z3 expression `self * other`. """ if other == 1: return self if other == 0: return 0 return If(self, other, 0) def is_bool(a): """Return `True` if `a` is a Z3 Boolean expression. >>> p = Bool('p') >>> is_bool(p) True >>> q = Bool('q') >>> is_bool(And(p, q)) True >>> x = Real('x') >>> is_bool(x) False >>> is_bool(x == 0) True """ return isinstance(a, BoolRef) def is_true(a): """Return `True` if `a` is the Z3 true expression. >>> p = Bool('p') >>> is_true(p) False >>> is_true(simplify(p == p)) True >>> x = Real('x') >>> is_true(x == 0) False >>> # True is a Python Boolean expression >>> is_true(True) False """ return is_app_of(a, Z3_OP_TRUE) def is_false(a): """Return `True` if `a` is the Z3 false expression. >>> p = Bool('p') >>> is_false(p) False >>> is_false(False) False >>> is_false(BoolVal(False)) True """ return is_app_of(a, Z3_OP_FALSE) def is_and(a): """Return `True` if `a` is a Z3 and expression. >>> p, q = Bools('p q') >>> is_and(And(p, q)) True >>> is_and(Or(p, q)) False """ return is_app_of(a, Z3_OP_AND) def is_or(a): """Return `True` if `a` is a Z3 or expression. >>> p, q = Bools('p q') >>> is_or(Or(p, q)) True >>> is_or(And(p, q)) False """ return is_app_of(a, Z3_OP_OR) def is_implies(a): """Return `True` if `a` is a Z3 implication expression. >>> p, q = Bools('p q') >>> is_implies(Implies(p, q)) True >>> is_implies(And(p, q)) False """ return is_app_of(a, Z3_OP_IMPLIES) def is_not(a): """Return `True` if `a` is a Z3 not expression. >>> p = Bool('p') >>> is_not(p) False >>> is_not(Not(p)) True """ return is_app_of(a, Z3_OP_NOT) def is_eq(a): """Return `True` if `a` is a Z3 equality expression. >>> x, y = Ints('x y') >>> is_eq(x == y) True """ return is_app_of(a, Z3_OP_EQ) def is_distinct(a): """Return `True` if `a` is a Z3 distinct expression. >>> x, y, z = Ints('x y z') >>> is_distinct(x == y) False >>> is_distinct(Distinct(x, y, z)) True """ return is_app_of(a, Z3_OP_DISTINCT) def BoolSort(ctx=None): """Return the Boolean Z3 sort. If `ctx=None`, then the global context is used. >>> BoolSort() Bool >>> p = Const('p', BoolSort()) >>> is_bool(p) True >>> r = Function('r', IntSort(), IntSort(), BoolSort()) >>> r(0, 1) r(0, 1) >>> is_bool(r(0, 1)) True """ ctx = _get_ctx(ctx) return BoolSortRef(Z3_mk_bool_sort(ctx.ref()), ctx) def BoolVal(val, ctx=None): """Return the Boolean value `True` or `False`. If `ctx=None`, then the global context is used. >>> BoolVal(True) True >>> is_true(BoolVal(True)) True >>> is_true(True) False >>> is_false(BoolVal(False)) True """ ctx = _get_ctx(ctx) if val == False: return BoolRef(Z3_mk_false(ctx.ref()), ctx) else: return BoolRef(Z3_mk_true(ctx.ref()), ctx) def Bool(name, ctx=None): """Return a Boolean constant named `name`. If `ctx=None`, then the global context is used. >>> p = Bool('p') >>> q = Bool('q') >>> And(p, q) And(p, q) """ ctx = _get_ctx(ctx) return BoolRef(Z3_mk_const(ctx.ref(), to_symbol(name, ctx), BoolSort(ctx).ast), ctx) def Bools(names, ctx=None): """Return a tuple of Boolean constants. `names` is a single string containing all names separated by blank spaces. If `ctx=None`, then the global context is used. >>> p, q, r = Bools('p q r') >>> And(p, Or(q, r)) And(p, Or(q, r)) """ ctx = _get_ctx(ctx) if isinstance(names, str): names = names.split(" ") return [Bool(name, ctx) for name in names] def BoolVector(prefix, sz, ctx=None): """Return a list of Boolean constants of size `sz`. The constants are named using the given prefix. If `ctx=None`, then the global context is used. >>> P = BoolVector('p', 3) >>> P [p__0, p__1, p__2] >>> And(P) And(p__0, p__1, p__2) """ return [ Bool('%s__%s' % (prefix, i)) for i in range(sz) ] def FreshBool(prefix='b', ctx=None): """Return a fresh Boolean constant in the given context using the given prefix. If `ctx=None`, then the global context is used. >>> b1 = FreshBool() >>> b2 = FreshBool() >>> eq(b1, b2) False """ ctx = _get_ctx(ctx) return BoolRef(Z3_mk_fresh_const(ctx.ref(), prefix, BoolSort(ctx).ast), ctx) def Implies(a, b, ctx=None): """Create a Z3 implies expression. >>> p, q = Bools('p q') >>> Implies(p, q) Implies(p, q) """ ctx = _get_ctx(_ctx_from_ast_arg_list([a, b], ctx)) s = BoolSort(ctx) a = s.cast(a) b = s.cast(b) return BoolRef(Z3_mk_implies(ctx.ref(), a.as_ast(), b.as_ast()), ctx) def Xor(a, b, ctx=None): """Create a Z3 Xor expression. >>> p, q = Bools('p q') >>> Xor(p, q) Xor(p, q) >>> simplify(Xor(p, q)) Not(p) == q """ ctx = _get_ctx(_ctx_from_ast_arg_list([a, b], ctx)) s = BoolSort(ctx) a = s.cast(a) b = s.cast(b) return BoolRef(Z3_mk_xor(ctx.ref(), a.as_ast(), b.as_ast()), ctx) def Not(a, ctx=None): """Create a Z3 not expression or probe. >>> p = Bool('p') >>> Not(Not(p)) Not(Not(p)) >>> simplify(Not(Not(p))) p """ ctx = _get_ctx(_ctx_from_ast_arg_list([a], ctx)) if is_probe(a): # Not is also used to build probes return Probe(Z3_probe_not(ctx.ref(), a.probe), ctx) else: s = BoolSort(ctx) a = s.cast(a) return BoolRef(Z3_mk_not(ctx.ref(), a.as_ast()), ctx) def mk_not(a): if is_not(a): return a.arg(0) else: return Not(a) def _has_probe(args): """Return `True` if one of the elements of the given collection is a Z3 probe.""" for arg in args: if is_probe(arg): return True return False def And(*args): """Create a Z3 and-expression or and-probe. >>> p, q, r = Bools('p q r') >>> And(p, q, r) And(p, q, r) >>> P = BoolVector('p', 5) >>> And(P) And(p__0, p__1, p__2, p__3, p__4) """ last_arg = None if len(args) > 0: last_arg = args[len(args)-1] if isinstance(last_arg, Context): ctx = args[len(args)-1] args = args[:len(args)-1] elif len(args) == 1 and isinstance(args[0], AstVector): ctx = args[0].ctx args = [a for a in args[0]] else: ctx = main_ctx() args = _get_args(args) ctx_args = _ctx_from_ast_arg_list(args, ctx) if z3_debug(): _z3_assert(ctx_args is None or ctx_args == ctx, "context mismatch") _z3_assert(ctx is not None, "At least one of the arguments must be a Z3 expression or probe") if _has_probe(args): return _probe_and(args, ctx) else: args = _coerce_expr_list(args, ctx) _args, sz = _to_ast_array(args) return BoolRef(Z3_mk_and(ctx.ref(), sz, _args), ctx) def Or(*args): """Create a Z3 or-expression or or-probe. >>> p, q, r = Bools('p q r') >>> Or(p, q, r) Or(p, q, r) >>> P = BoolVector('p', 5) >>> Or(P) Or(p__0, p__1, p__2, p__3, p__4) """ last_arg = None if len(args) > 0: last_arg = args[len(args)-1] if isinstance(last_arg, Context): ctx = args[len(args)-1] args = args[:len(args)-1] else: ctx = main_ctx() args = _get_args(args) ctx_args = _ctx_from_ast_arg_list(args, ctx) if z3_debug(): _z3_assert(ctx_args is None or ctx_args == ctx, "context mismatch") _z3_assert(ctx is not None, "At least one of the arguments must be a Z3 expression or probe") if _has_probe(args): return _probe_or(args, ctx) else: args = _coerce_expr_list(args, ctx) _args, sz = _to_ast_array(args) return BoolRef(Z3_mk_or(ctx.ref(), sz, _args), ctx) ######################################### # # Patterns # ######################################### class PatternRef(ExprRef): """Patterns are hints for quantifier instantiation. """ def as_ast(self): return Z3_pattern_to_ast(self.ctx_ref(), self.ast) def get_id(self): return Z3_get_ast_id(self.ctx_ref(), self.as_ast()) def is_pattern(a): """Return `True` if `a` is a Z3 pattern (hint for quantifier instantiation. >>> f = Function('f', IntSort(), IntSort()) >>> x = Int('x') >>> q = ForAll(x, f(x) == 0, patterns = [ f(x) ]) >>> q ForAll(x, f(x) == 0) >>> q.num_patterns() 1 >>> is_pattern(q.pattern(0)) True >>> q.pattern(0) f(Var(0)) """ return isinstance(a, PatternRef) def MultiPattern(*args): """Create a Z3 multi-pattern using the given expressions `*args` >>> f = Function('f', IntSort(), IntSort()) >>> g = Function('g', IntSort(), IntSort()) >>> x = Int('x') >>> q = ForAll(x, f(x) != g(x), patterns = [ MultiPattern(f(x), g(x)) ]) >>> q ForAll(x, f(x) != g(x)) >>> q.num_patterns() 1 >>> is_pattern(q.pattern(0)) True >>> q.pattern(0) MultiPattern(f(Var(0)), g(Var(0))) """ if z3_debug(): _z3_assert(len(args) > 0, "At least one argument expected") _z3_assert(all([ is_expr(a) for a in args ]), "Z3 expressions expected") ctx = args[0].ctx args, sz = _to_ast_array(args) return PatternRef(Z3_mk_pattern(ctx.ref(), sz, args), ctx) def _to_pattern(arg): if is_pattern(arg): return arg else: return MultiPattern(arg) ######################################### # # Quantifiers # ######################################### class QuantifierRef(BoolRef): """Universally and Existentially quantified formulas.""" def as_ast(self): return self.ast def get_id(self): return Z3_get_ast_id(self.ctx_ref(), self.as_ast()) def sort(self): """Return the Boolean sort or sort of Lambda.""" if self.is_lambda(): return _sort(self.ctx, self.as_ast()) return BoolSort(self.ctx) def is_forall(self): """Return `True` if `self` is a universal quantifier. >>> f = Function('f', IntSort(), IntSort()) >>> x = Int('x') >>> q = ForAll(x, f(x) == 0) >>> q.is_forall() True >>> q = Exists(x, f(x) != 0) >>> q.is_forall() False """ return Z3_is_quantifier_forall(self.ctx_ref(), self.ast) def is_exists(self): """Return `True` if `self` is an existential quantifier. >>> f = Function('f', IntSort(), IntSort()) >>> x = Int('x') >>> q = ForAll(x, f(x) == 0) >>> q.is_exists() False >>> q = Exists(x, f(x) != 0) >>> q.is_exists() True """ return Z3_is_quantifier_exists(self.ctx_ref(), self.ast) def is_lambda(self): """Return `True` if `self` is a lambda expression. >>> f = Function('f', IntSort(), IntSort()) >>> x = Int('x') >>> q = Lambda(x, f(x)) >>> q.is_lambda() True >>> q = Exists(x, f(x) != 0) >>> q.is_lambda() False """ return Z3_is_lambda(self.ctx_ref(), self.ast) def __getitem__(self, arg): """Return the Z3 expression `self[arg]`. """ if z3_debug(): _z3_assert(self.is_lambda(), "quantifier should be a lambda expression") arg = self.sort().domain().cast(arg) return _to_expr_ref(Z3_mk_select(self.ctx_ref(), self.as_ast(), arg.as_ast()), self.ctx) def weight(self): """Return the weight annotation of `self`. >>> f = Function('f', IntSort(), IntSort()) >>> x = Int('x') >>> q = ForAll(x, f(x) == 0) >>> q.weight() 1 >>> q = ForAll(x, f(x) == 0, weight=10) >>> q.weight() 10 """ return int(Z3_get_quantifier_weight(self.ctx_ref(), self.ast)) def num_patterns(self): """Return the number of patterns (i.e., quantifier instantiation hints) in `self`. >>> f = Function('f', IntSort(), IntSort()) >>> g = Function('g', IntSort(), IntSort()) >>> x = Int('x') >>> q = ForAll(x, f(x) != g(x), patterns = [ f(x), g(x) ]) >>> q.num_patterns() 2 """ return int(Z3_get_quantifier_num_patterns(self.ctx_ref(), self.ast)) def pattern(self, idx): """Return a pattern (i.e., quantifier instantiation hints) in `self`. >>> f = Function('f', IntSort(), IntSort()) >>> g = Function('g', IntSort(), IntSort()) >>> x = Int('x') >>> q = ForAll(x, f(x) != g(x), patterns = [ f(x), g(x) ]) >>> q.num_patterns() 2 >>> q.pattern(0) f(Var(0)) >>> q.pattern(1) g(Var(0)) """ if z3_debug(): _z3_assert(idx < self.num_patterns(), "Invalid pattern idx") return PatternRef(Z3_get_quantifier_pattern_ast(self.ctx_ref(), self.ast, idx), self.ctx) def num_no_patterns(self): """Return the number of no-patterns.""" return Z3_get_quantifier_num_no_patterns(self.ctx_ref(), self.ast) def no_pattern(self, idx): """Return a no-pattern.""" if z3_debug(): _z3_assert(idx < self.num_no_patterns(), "Invalid no-pattern idx") return _to_expr_ref(Z3_get_quantifier_no_pattern_ast(self.ctx_ref(), self.ast, idx), self.ctx) def body(self): """Return the expression being quantified. >>> f = Function('f', IntSort(), IntSort()) >>> x = Int('x') >>> q = ForAll(x, f(x) == 0) >>> q.body() f(Var(0)) == 0 """ return _to_expr_ref(Z3_get_quantifier_body(self.ctx_ref(), self.ast), self.ctx) def num_vars(self): """Return the number of variables bounded by this quantifier. >>> f = Function('f', IntSort(), IntSort(), IntSort()) >>> x = Int('x') >>> y = Int('y') >>> q = ForAll([x, y], f(x, y) >= x) >>> q.num_vars() 2 """ return int(Z3_get_quantifier_num_bound(self.ctx_ref(), self.ast)) def var_name(self, idx): """Return a string representing a name used when displaying the quantifier. >>> f = Function('f', IntSort(), IntSort(), IntSort()) >>> x = Int('x') >>> y = Int('y') >>> q = ForAll([x, y], f(x, y) >= x) >>> q.var_name(0) 'x' >>> q.var_name(1) 'y' """ if z3_debug(): _z3_assert(idx < self.num_vars(), "Invalid variable idx") return _symbol2py(self.ctx, Z3_get_quantifier_bound_name(self.ctx_ref(), self.ast, idx)) def var_sort(self, idx): """Return the sort of a bound variable. >>> f = Function('f', IntSort(), RealSort(), IntSort()) >>> x = Int('x') >>> y = Real('y') >>> q = ForAll([x, y], f(x, y) >= x) >>> q.var_sort(0) Int >>> q.var_sort(1) Real """ if z3_debug(): _z3_assert(idx < self.num_vars(), "Invalid variable idx") return _to_sort_ref(Z3_get_quantifier_bound_sort(self.ctx_ref(), self.ast, idx), self.ctx) def children(self): """Return a list containing a single element self.body() >>> f = Function('f', IntSort(), IntSort()) >>> x = Int('x') >>> q = ForAll(x, f(x) == 0) >>> q.children() [f(Var(0)) == 0] """ return [ self.body() ] def is_quantifier(a): """Return `True` if `a` is a Z3 quantifier. >>> f = Function('f', IntSort(), IntSort()) >>> x = Int('x') >>> q = ForAll(x, f(x) == 0) >>> is_quantifier(q) True >>> is_quantifier(f(x)) False """ return isinstance(a, QuantifierRef) def _mk_quantifier(is_forall, vs, body, weight=1, qid="", skid="", patterns=[], no_patterns=[]): if z3_debug(): _z3_assert(is_bool(body) or is_app(vs) or (len(vs) > 0 and is_app(vs[0])), "Z3 expression expected") _z3_assert(is_const(vs) or (len(vs) > 0 and all([ is_const(v) for v in vs])), "Invalid bounded variable(s)") _z3_assert(all([is_pattern(a) or is_expr(a) for a in patterns]), "Z3 patterns expected") _z3_assert(all([is_expr(p) for p in no_patterns]), "no patterns are Z3 expressions") if is_app(vs): ctx = vs.ctx vs = [vs] else: ctx = vs[0].ctx if not is_expr(body): body = BoolVal(body, ctx) num_vars = len(vs) if num_vars == 0: return body _vs = (Ast * num_vars)() for i in range(num_vars): ## TODO: Check if is constant _vs[i] = vs[i].as_ast() patterns = [ _to_pattern(p) for p in patterns ] num_pats = len(patterns) _pats = (Pattern * num_pats)() for i in range(num_pats): _pats[i] = patterns[i].ast _no_pats, num_no_pats = _to_ast_array(no_patterns) qid = to_symbol(qid, ctx) skid = to_symbol(skid, ctx) return QuantifierRef(Z3_mk_quantifier_const_ex(ctx.ref(), is_forall, weight, qid, skid, num_vars, _vs, num_pats, _pats, num_no_pats, _no_pats, body.as_ast()), ctx) def ForAll(vs, body, weight=1, qid="", skid="", patterns=[], no_patterns=[]): """Create a Z3 forall formula. The parameters `weight`, `qid`, `skid`, `patterns` and `no_patterns` are optional annotations. >>> f = Function('f', IntSort(), IntSort(), IntSort()) >>> x = Int('x') >>> y = Int('y') >>> ForAll([x, y], f(x, y) >= x) ForAll([x, y], f(x, y) >= x) >>> ForAll([x, y], f(x, y) >= x, patterns=[ f(x, y) ]) ForAll([x, y], f(x, y) >= x) >>> ForAll([x, y], f(x, y) >= x, weight=10) ForAll([x, y], f(x, y) >= x) """ return _mk_quantifier(True, vs, body, weight, qid, skid, patterns, no_patterns) def Exists(vs, body, weight=1, qid="", skid="", patterns=[], no_patterns=[]): """Create a Z3 exists formula. The parameters `weight`, `qif`, `skid`, `patterns` and `no_patterns` are optional annotations. >>> f = Function('f', IntSort(), IntSort(), IntSort()) >>> x = Int('x') >>> y = Int('y') >>> q = Exists([x, y], f(x, y) >= x, skid="foo") >>> q Exists([x, y], f(x, y) >= x) >>> is_quantifier(q) True >>> r = Tactic('nnf')(q).as_expr() >>> is_quantifier(r) False """ return _mk_quantifier(False, vs, body, weight, qid, skid, patterns, no_patterns) def Lambda(vs, body): """Create a Z3 lambda expression. >>> f = Function('f', IntSort(), IntSort(), IntSort()) >>> mem0 = Array('mem0', IntSort(), IntSort()) >>> lo, hi, e, i = Ints('lo hi e i') >>> mem1 = Lambda([i], If(And(lo <= i, i <= hi), e, mem0[i])) >>> mem1 Lambda(i, If(And(lo <= i, i <= hi), e, mem0[i])) """ ctx = body.ctx if is_app(vs): vs = [vs] num_vars = len(vs) _vs = (Ast * num_vars)() for i in range(num_vars): ## TODO: Check if is constant _vs[i] = vs[i].as_ast() return QuantifierRef(Z3_mk_lambda_const(ctx.ref(), num_vars, _vs, body.as_ast()), ctx) ######################################### # # Arithmetic # ######################################### class ArithSortRef(SortRef): """Real and Integer sorts.""" def is_real(self): """Return `True` if `self` is of the sort Real. >>> x = Real('x') >>> x.is_real() True >>> (x + 1).is_real() True >>> x = Int('x') >>> x.is_real() False """ return self.kind() == Z3_REAL_SORT def is_int(self): """Return `True` if `self` is of the sort Integer. >>> x = Int('x') >>> x.is_int() True >>> (x + 1).is_int() True >>> x = Real('x') >>> x.is_int() False """ return self.kind() == Z3_INT_SORT def subsort(self, other): """Return `True` if `self` is a subsort of `other`.""" return self.is_int() and is_arith_sort(other) and other.is_real() def cast(self, val): """Try to cast `val` as an Integer or Real. >>> IntSort().cast(10) 10 >>> is_int(IntSort().cast(10)) True >>> is_int(10) False >>> RealSort().cast(10) 10 >>> is_real(RealSort().cast(10)) True """ if is_expr(val): if z3_debug(): _z3_assert(self.ctx == val.ctx, "Context mismatch") val_s = val.sort() if self.eq(val_s): return val if val_s.is_int() and self.is_real(): return ToReal(val) if val_s.is_bool() and self.is_int(): return If(val, 1, 0) if val_s.is_bool() and self.is_real(): return ToReal(If(val, 1, 0)) if z3_debug(): _z3_assert(False, "Z3 Integer/Real expression expected" ) else: if self.is_int(): return IntVal(val, self.ctx) if self.is_real(): return RealVal(val, self.ctx) if z3_debug(): _z3_assert(False, "int, long, float, string (numeral), or Z3 Integer/Real expression expected. Got %s" % self) def is_arith_sort(s): """Return `True` if s is an arithmetical sort (type). >>> is_arith_sort(IntSort()) True >>> is_arith_sort(RealSort()) True >>> is_arith_sort(BoolSort()) False >>> n = Int('x') + 1 >>> is_arith_sort(n.sort()) True """ return isinstance(s, ArithSortRef) class ArithRef(ExprRef): """Integer and Real expressions.""" def sort(self): """Return the sort (type) of the arithmetical expression `self`. >>> Int('x').sort() Int >>> (Real('x') + 1).sort() Real """ return ArithSortRef(Z3_get_sort(self.ctx_ref(), self.as_ast()), self.ctx) def is_int(self): """Return `True` if `self` is an integer expression. >>> x = Int('x') >>> x.is_int() True >>> (x + 1).is_int() True >>> y = Real('y') >>> (x + y).is_int() False """ return self.sort().is_int() def is_real(self): """Return `True` if `self` is an real expression. >>> x = Real('x') >>> x.is_real() True >>> (x + 1).is_real() True """ return self.sort().is_real() def __add__(self, other): """Create the Z3 expression `self + other`. >>> x = Int('x') >>> y = Int('y') >>> x + y x + y >>> (x + y).sort() Int """ a, b = _coerce_exprs(self, other) return ArithRef(_mk_bin(Z3_mk_add, a, b), self.ctx) def __radd__(self, other): """Create the Z3 expression `other + self`. >>> x = Int('x') >>> 10 + x 10 + x """ a, b = _coerce_exprs(self, other) return ArithRef(_mk_bin(Z3_mk_add, b, a), self.ctx) def __mul__(self, other): """Create the Z3 expression `self * other`. >>> x = Real('x') >>> y = Real('y') >>> x * y x*y >>> (x * y).sort() Real """ if isinstance(other, BoolRef): return If(other, self, 0) a, b = _coerce_exprs(self, other) return ArithRef(_mk_bin(Z3_mk_mul, a, b), self.ctx) def __rmul__(self, other): """Create the Z3 expression `other * self`. >>> x = Real('x') >>> 10 * x 10*x """ a, b = _coerce_exprs(self, other) return ArithRef(_mk_bin(Z3_mk_mul, b, a), self.ctx) def __sub__(self, other): """Create the Z3 expression `self - other`. >>> x = Int('x') >>> y = Int('y') >>> x - y x - y >>> (x - y).sort() Int """ a, b = _coerce_exprs(self, other) return ArithRef(_mk_bin(Z3_mk_sub, a, b), self.ctx) def __rsub__(self, other): """Create the Z3 expression `other - self`. >>> x = Int('x') >>> 10 - x 10 - x """ a, b = _coerce_exprs(self, other) return ArithRef(_mk_bin(Z3_mk_sub, b, a), self.ctx) def __pow__(self, other): """Create the Z3 expression `self**other` (** is the power operator). >>> x = Real('x') >>> x**3 x**3 >>> (x**3).sort() Real >>> simplify(IntVal(2)**8) 256 """ a, b = _coerce_exprs(self, other) return ArithRef(Z3_mk_power(self.ctx_ref(), a.as_ast(), b.as_ast()), self.ctx) def __rpow__(self, other): """Create the Z3 expression `other**self` (** is the power operator). >>> x = Real('x') >>> 2**x 2**x >>> (2**x).sort() Real >>> simplify(2**IntVal(8)) 256 """ a, b = _coerce_exprs(self, other) return ArithRef(Z3_mk_power(self.ctx_ref(), b.as_ast(), a.as_ast()), self.ctx) def __div__(self, other): """Create the Z3 expression `other/self`. >>> x = Int('x') >>> y = Int('y') >>> x/y x/y >>> (x/y).sort() Int >>> (x/y).sexpr() '(div x y)' >>> x = Real('x') >>> y = Real('y') >>> x/y x/y >>> (x/y).sort() Real >>> (x/y).sexpr() '(/ x y)' """ a, b = _coerce_exprs(self, other) return ArithRef(Z3_mk_div(self.ctx_ref(), a.as_ast(), b.as_ast()), self.ctx) def __truediv__(self, other): """Create the Z3 expression `other/self`.""" return self.__div__(other) def __rdiv__(self, other): """Create the Z3 expression `other/self`. >>> x = Int('x') >>> 10/x 10/x >>> (10/x).sexpr() '(div 10 x)' >>> x = Real('x') >>> 10/x 10/x >>> (10/x).sexpr() '(/ 10.0 x)' """ a, b = _coerce_exprs(self, other) return ArithRef(Z3_mk_div(self.ctx_ref(), b.as_ast(), a.as_ast()), self.ctx) def __rtruediv__(self, other): """Create the Z3 expression `other/self`.""" return self.__rdiv__(other) def __mod__(self, other): """Create the Z3 expression `other%self`. >>> x = Int('x') >>> y = Int('y') >>> x % y x%y >>> simplify(IntVal(10) % IntVal(3)) 1 """ a, b = _coerce_exprs(self, other) if z3_debug(): _z3_assert(a.is_int(), "Z3 integer expression expected") return ArithRef(Z3_mk_mod(self.ctx_ref(), a.as_ast(), b.as_ast()), self.ctx) def __rmod__(self, other): """Create the Z3 expression `other%self`. >>> x = Int('x') >>> 10 % x 10%x """ a, b = _coerce_exprs(self, other) if z3_debug(): _z3_assert(a.is_int(), "Z3 integer expression expected") return ArithRef(Z3_mk_mod(self.ctx_ref(), b.as_ast(), a.as_ast()), self.ctx) def __neg__(self): """Return an expression representing `-self`. >>> x = Int('x') >>> -x -x >>> simplify(-(-x)) x """ return ArithRef(Z3_mk_unary_minus(self.ctx_ref(), self.as_ast()), self.ctx) def __pos__(self): """Return `self`. >>> x = Int('x') >>> +x x """ return self def __le__(self, other): """Create the Z3 expression `other <= self`. >>> x, y = Ints('x y') >>> x <= y x <= y >>> y = Real('y') >>> x <= y ToReal(x) <= y """ a, b = _coerce_exprs(self, other) return BoolRef(Z3_mk_le(self.ctx_ref(), a.as_ast(), b.as_ast()), self.ctx) def __lt__(self, other): """Create the Z3 expression `other < self`. >>> x, y = Ints('x y') >>> x < y x < y >>> y = Real('y') >>> x < y ToReal(x) < y """ a, b = _coerce_exprs(self, other) return BoolRef(Z3_mk_lt(self.ctx_ref(), a.as_ast(), b.as_ast()), self.ctx) def __gt__(self, other): """Create the Z3 expression `other > self`. >>> x, y = Ints('x y') >>> x > y x > y >>> y = Real('y') >>> x > y ToReal(x) > y """ a, b = _coerce_exprs(self, other) return BoolRef(Z3_mk_gt(self.ctx_ref(), a.as_ast(), b.as_ast()), self.ctx) def __ge__(self, other): """Create the Z3 expression `other >= self`. >>> x, y = Ints('x y') >>> x >= y x >= y >>> y = Real('y') >>> x >= y ToReal(x) >= y """ a, b = _coerce_exprs(self, other) return BoolRef(Z3_mk_ge(self.ctx_ref(), a.as_ast(), b.as_ast()), self.ctx) def is_arith(a): """Return `True` if `a` is an arithmetical expression. >>> x = Int('x') >>> is_arith(x) True >>> is_arith(x + 1) True >>> is_arith(1) False >>> is_arith(IntVal(1)) True >>> y = Real('y') >>> is_arith(y) True >>> is_arith(y + 1) True """ return isinstance(a, ArithRef) def is_int(a): """Return `True` if `a` is an integer expression. >>> x = Int('x') >>> is_int(x + 1) True >>> is_int(1) False >>> is_int(IntVal(1)) True >>> y = Real('y') >>> is_int(y) False >>> is_int(y + 1) False """ return is_arith(a) and a.is_int() def is_real(a): """Return `True` if `a` is a real expression. >>> x = Int('x') >>> is_real(x + 1) False >>> y = Real('y') >>> is_real(y) True >>> is_real(y + 1) True >>> is_real(1) False >>> is_real(RealVal(1)) True """ return is_arith(a) and a.is_real() def _is_numeral(ctx, a): return Z3_is_numeral_ast(ctx.ref(), a) def _is_algebraic(ctx, a): return Z3_is_algebraic_number(ctx.ref(), a) def is_int_value(a): """Return `True` if `a` is an integer value of sort Int. >>> is_int_value(IntVal(1)) True >>> is_int_value(1) False >>> is_int_value(Int('x')) False >>> n = Int('x') + 1 >>> n x + 1 >>> n.arg(1) 1 >>> is_int_value(n.arg(1)) True >>> is_int_value(RealVal("1/3")) False >>> is_int_value(RealVal(1)) False """ return is_arith(a) and a.is_int() and _is_numeral(a.ctx, a.as_ast()) def is_rational_value(a): """Return `True` if `a` is rational value of sort Real. >>> is_rational_value(RealVal(1)) True >>> is_rational_value(RealVal("3/5")) True >>> is_rational_value(IntVal(1)) False >>> is_rational_value(1) False >>> n = Real('x') + 1 >>> n.arg(1) 1 >>> is_rational_value(n.arg(1)) True >>> is_rational_value(Real('x')) False """ return is_arith(a) and a.is_real() and _is_numeral(a.ctx, a.as_ast()) def is_algebraic_value(a): """Return `True` if `a` is an algebraic value of sort Real. >>> is_algebraic_value(RealVal("3/5")) False >>> n = simplify(Sqrt(2)) >>> n 1.4142135623? >>> is_algebraic_value(n) True """ return is_arith(a) and a.is_real() and _is_algebraic(a.ctx, a.as_ast()) def is_add(a): """Return `True` if `a` is an expression of the form b + c. >>> x, y = Ints('x y') >>> is_add(x + y) True >>> is_add(x - y) False """ return is_app_of(a, Z3_OP_ADD) def is_mul(a): """Return `True` if `a` is an expression of the form b * c. >>> x, y = Ints('x y') >>> is_mul(x * y) True >>> is_mul(x - y) False """ return is_app_of(a, Z3_OP_MUL) def is_sub(a): """Return `True` if `a` is an expression of the form b - c. >>> x, y = Ints('x y') >>> is_sub(x - y) True >>> is_sub(x + y) False """ return is_app_of(a, Z3_OP_SUB) def is_div(a): """Return `True` if `a` is an expression of the form b / c. >>> x, y = Reals('x y') >>> is_div(x / y) True >>> is_div(x + y) False >>> x, y = Ints('x y') >>> is_div(x / y) False >>> is_idiv(x / y) True """ return is_app_of(a, Z3_OP_DIV) def is_idiv(a): """Return `True` if `a` is an expression of the form b div c. >>> x, y = Ints('x y') >>> is_idiv(x / y) True >>> is_idiv(x + y) False """ return is_app_of(a, Z3_OP_IDIV) def is_mod(a): """Return `True` if `a` is an expression of the form b % c. >>> x, y = Ints('x y') >>> is_mod(x % y) True >>> is_mod(x + y) False """ return is_app_of(a, Z3_OP_MOD) def is_le(a): """Return `True` if `a` is an expression of the form b <= c. >>> x, y = Ints('x y') >>> is_le(x <= y) True >>> is_le(x < y) False """ return is_app_of(a, Z3_OP_LE) def is_lt(a): """Return `True` if `a` is an expression of the form b < c. >>> x, y = Ints('x y') >>> is_lt(x < y) True >>> is_lt(x == y) False """ return is_app_of(a, Z3_OP_LT) def is_ge(a): """Return `True` if `a` is an expression of the form b >= c. >>> x, y = Ints('x y') >>> is_ge(x >= y) True >>> is_ge(x == y) False """ return is_app_of(a, Z3_OP_GE) def is_gt(a): """Return `True` if `a` is an expression of the form b > c. >>> x, y = Ints('x y') >>> is_gt(x > y) True >>> is_gt(x == y) False """ return is_app_of(a, Z3_OP_GT) def is_is_int(a): """Return `True` if `a` is an expression of the form IsInt(b). >>> x = Real('x') >>> is_is_int(IsInt(x)) True >>> is_is_int(x) False """ return is_app_of(a, Z3_OP_IS_INT) def is_to_real(a): """Return `True` if `a` is an expression of the form ToReal(b). >>> x = Int('x') >>> n = ToReal(x) >>> n ToReal(x) >>> is_to_real(n) True >>> is_to_real(x) False """ return is_app_of(a, Z3_OP_TO_REAL) def is_to_int(a): """Return `True` if `a` is an expression of the form ToInt(b). >>> x = Real('x') >>> n = ToInt(x) >>> n ToInt(x) >>> is_to_int(n) True >>> is_to_int(x) False """ return is_app_of(a, Z3_OP_TO_INT) class IntNumRef(ArithRef): """Integer values.""" def as_long(self): """Return a Z3 integer numeral as a Python long (bignum) numeral. >>> v = IntVal(1) >>> v + 1 1 + 1 >>> v.as_long() + 1 2 """ if z3_debug(): _z3_assert(self.is_int(), "Integer value expected") return int(self.as_string()) def as_string(self): """Return a Z3 integer numeral as a Python string. >>> v = IntVal(100) >>> v.as_string() '100' """ return Z3_get_numeral_string(self.ctx_ref(), self.as_ast()) class RatNumRef(ArithRef): """Rational values.""" def numerator(self): """ Return the numerator of a Z3 rational numeral. >>> is_rational_value(RealVal("3/5")) True >>> n = RealVal("3/5") >>> n.numerator() 3 >>> is_rational_value(Q(3,5)) True >>> Q(3,5).numerator() 3 """ return IntNumRef(Z3_get_numerator(self.ctx_ref(), self.as_ast()), self.ctx) def denominator(self): """ Return the denominator of a Z3 rational numeral. >>> is_rational_value(Q(3,5)) True >>> n = Q(3,5) >>> n.denominator() 5 """ return IntNumRef(Z3_get_denominator(self.ctx_ref(), self.as_ast()), self.ctx) def numerator_as_long(self): """ Return the numerator as a Python long. >>> v = RealVal(10000000000) >>> v 10000000000 >>> v + 1 10000000000 + 1 >>> v.numerator_as_long() + 1 == 10000000001 True """ return self.numerator().as_long() def denominator_as_long(self): """ Return the denominator as a Python long. >>> v = RealVal("1/3") >>> v 1/3 >>> v.denominator_as_long() 3 """ return self.denominator().as_long() def is_int(self): return False def is_real(self): return True def is_int_value(self): return self.denominator().is_int() and self.denominator_as_long() == 1 def as_long(self): _z3_assert(self.is_int_value(), "Expected integer fraction") return self.numerator_as_long() def as_decimal(self, prec): """ Return a Z3 rational value as a string in decimal notation using at most `prec` decimal places. >>> v = RealVal("1/5") >>> v.as_decimal(3) '0.2' >>> v = RealVal("1/3") >>> v.as_decimal(3) '0.333?' """ return Z3_get_numeral_decimal_string(self.ctx_ref(), self.as_ast(), prec) def as_string(self): """Return a Z3 rational numeral as a Python string. >>> v = Q(3,6) >>> v.as_string() '1/2' """ return Z3_get_numeral_string(self.ctx_ref(), self.as_ast()) def as_fraction(self): """Return a Z3 rational as a Python Fraction object. >>> v = RealVal("1/5") >>> v.as_fraction() Fraction(1, 5) """ return Fraction(self.numerator_as_long(), self.denominator_as_long()) class AlgebraicNumRef(ArithRef): """Algebraic irrational values.""" def approx(self, precision=10): """Return a Z3 rational number that approximates the algebraic number `self`. The result `r` is such that |r - self| <= 1/10^precision >>> x = simplify(Sqrt(2)) >>> x.approx(20) 6838717160008073720548335/4835703278458516698824704 >>> x.approx(5) 2965821/2097152 """ return RatNumRef(Z3_get_algebraic_number_upper(self.ctx_ref(), self.as_ast(), precision), self.ctx) def as_decimal(self, prec): """Return a string representation of the algebraic number `self` in decimal notation using `prec` decimal places >>> x = simplify(Sqrt(2)) >>> x.as_decimal(10) '1.4142135623?' >>> x.as_decimal(20) '1.41421356237309504880?' """ return Z3_get_numeral_decimal_string(self.ctx_ref(), self.as_ast(), prec) def _py2expr(a, ctx=None): if isinstance(a, bool): return BoolVal(a, ctx) if _is_int(a): return IntVal(a, ctx) if isinstance(a, float): return RealVal(a, ctx) if is_expr(a): return a if z3_debug(): _z3_assert(False, "Python bool, int, long or float expected") def IntSort(ctx=None): """Return the integer sort in the given context. If `ctx=None`, then the global context is used. >>> IntSort() Int >>> x = Const('x', IntSort()) >>> is_int(x) True >>> x.sort() == IntSort() True >>> x.sort() == BoolSort() False """ ctx = _get_ctx(ctx) return ArithSortRef(Z3_mk_int_sort(ctx.ref()), ctx) def RealSort(ctx=None): """Return the real sort in the given context. If `ctx=None`, then the global context is used. >>> RealSort() Real >>> x = Const('x', RealSort()) >>> is_real(x) True >>> is_int(x) False >>> x.sort() == RealSort() True """ ctx = _get_ctx(ctx) return ArithSortRef(Z3_mk_real_sort(ctx.ref()), ctx) def _to_int_str(val): if isinstance(val, float): return str(int(val)) elif isinstance(val, bool): if val: return "1" else: return "0" elif _is_int(val): return str(val) elif isinstance(val, str): return val if z3_debug(): _z3_assert(False, "Python value cannot be used as a Z3 integer") def IntVal(val, ctx=None): """Return a Z3 integer value. If `ctx=None`, then the global context is used. >>> IntVal(1) 1 >>> IntVal("100") 100 """ ctx = _get_ctx(ctx) return IntNumRef(Z3_mk_numeral(ctx.ref(), _to_int_str(val), IntSort(ctx).ast), ctx) def RealVal(val, ctx=None): """Return a Z3 real value. `val` may be a Python int, long, float or string representing a number in decimal or rational notation. If `ctx=None`, then the global context is used. >>> RealVal(1) 1 >>> RealVal(1).sort() Real >>> RealVal("3/5") 3/5 >>> RealVal("1.5") 3/2 """ ctx = _get_ctx(ctx) return RatNumRef(Z3_mk_numeral(ctx.ref(), str(val), RealSort(ctx).ast), ctx) def RatVal(a, b, ctx=None): """Return a Z3 rational a/b. If `ctx=None`, then the global context is used. >>> RatVal(3,5) 3/5 >>> RatVal(3,5).sort() Real """ if z3_debug(): _z3_assert(_is_int(a) or isinstance(a, str), "First argument cannot be converted into an integer") _z3_assert(_is_int(b) or isinstance(b, str), "Second argument cannot be converted into an integer") return simplify(RealVal(a, ctx)/RealVal(b, ctx)) def Q(a, b, ctx=None): """Return a Z3 rational a/b. If `ctx=None`, then the global context is used. >>> Q(3,5) 3/5 >>> Q(3,5).sort() Real """ return simplify(RatVal(a, b)) def Int(name, ctx=None): """Return an integer constant named `name`. If `ctx=None`, then the global context is used. >>> x = Int('x') >>> is_int(x) True >>> is_int(x + 1) True """ ctx = _get_ctx(ctx) return ArithRef(Z3_mk_const(ctx.ref(), to_symbol(name, ctx), IntSort(ctx).ast), ctx) def Ints(names, ctx=None): """Return a tuple of Integer constants. >>> x, y, z = Ints('x y z') >>> Sum(x, y, z) x + y + z """ ctx = _get_ctx(ctx) if isinstance(names, str): names = names.split(" ") return [Int(name, ctx) for name in names] def IntVector(prefix, sz, ctx=None): """Return a list of integer constants of size `sz`. >>> X = IntVector('x', 3) >>> X [x__0, x__1, x__2] >>> Sum(X) x__0 + x__1 + x__2 """ return [ Int('%s__%s' % (prefix, i)) for i in range(sz) ] def FreshInt(prefix='x', ctx=None): """Return a fresh integer constant in the given context using the given prefix. >>> x = FreshInt() >>> y = FreshInt() >>> eq(x, y) False >>> x.sort() Int """ ctx = _get_ctx(ctx) return ArithRef(Z3_mk_fresh_const(ctx.ref(), prefix, IntSort(ctx).ast), ctx) def Real(name, ctx=None): """Return a real constant named `name`. If `ctx=None`, then the global context is used. >>> x = Real('x') >>> is_real(x) True >>> is_real(x + 1) True """ ctx = _get_ctx(ctx) return ArithRef(Z3_mk_const(ctx.ref(), to_symbol(name, ctx), RealSort(ctx).ast), ctx) def Reals(names, ctx=None): """Return a tuple of real constants. >>> x, y, z = Reals('x y z') >>> Sum(x, y, z) x + y + z >>> Sum(x, y, z).sort() Real """ ctx = _get_ctx(ctx) if isinstance(names, str): names = names.split(" ") return [Real(name, ctx) for name in names] def RealVector(prefix, sz, ctx=None): """Return a list of real constants of size `sz`. >>> X = RealVector('x', 3) >>> X [x__0, x__1, x__2] >>> Sum(X) x__0 + x__1 + x__2 >>> Sum(X).sort() Real """ return [ Real('%s__%s' % (prefix, i)) for i in range(sz) ] def FreshReal(prefix='b', ctx=None): """Return a fresh real constant in the given context using the given prefix. >>> x = FreshReal() >>> y = FreshReal() >>> eq(x, y) False >>> x.sort() Real """ ctx = _get_ctx(ctx) return ArithRef(Z3_mk_fresh_const(ctx.ref(), prefix, RealSort(ctx).ast), ctx) def ToReal(a): """ Return the Z3 expression ToReal(a). >>> x = Int('x') >>> x.sort() Int >>> n = ToReal(x) >>> n ToReal(x) >>> n.sort() Real """ if z3_debug(): _z3_assert(a.is_int(), "Z3 integer expression expected.") ctx = a.ctx return ArithRef(Z3_mk_int2real(ctx.ref(), a.as_ast()), ctx) def ToInt(a): """ Return the Z3 expression ToInt(a). >>> x = Real('x') >>> x.sort() Real >>> n = ToInt(x) >>> n ToInt(x) >>> n.sort() Int """ if z3_debug(): _z3_assert(a.is_real(), "Z3 real expression expected.") ctx = a.ctx return ArithRef(Z3_mk_real2int(ctx.ref(), a.as_ast()), ctx) def IsInt(a): """ Return the Z3 predicate IsInt(a). >>> x = Real('x') >>> IsInt(x + "1/2") IsInt(x + 1/2) >>> solve(IsInt(x + "1/2"), x > 0, x < 1) [x = 1/2] >>> solve(IsInt(x + "1/2"), x > 0, x < 1, x != "1/2") no solution """ if z3_debug(): _z3_assert(a.is_real(), "Z3 real expression expected.") ctx = a.ctx return BoolRef(Z3_mk_is_int(ctx.ref(), a.as_ast()), ctx) def Sqrt(a, ctx=None): """ Return a Z3 expression which represents the square root of a. >>> x = Real('x') >>> Sqrt(x) x**(1/2) """ if not is_expr(a): ctx = _get_ctx(ctx) a = RealVal(a, ctx) return a ** "1/2" def Cbrt(a, ctx=None): """ Return a Z3 expression which represents the cubic root of a. >>> x = Real('x') >>> Cbrt(x) x**(1/3) """ if not is_expr(a): ctx = _get_ctx(ctx) a = RealVal(a, ctx) return a ** "1/3" ######################################### # # Bit-Vectors # ######################################### class BitVecSortRef(SortRef): """Bit-vector sort.""" def size(self): """Return the size (number of bits) of the bit-vector sort `self`. >>> b = BitVecSort(32) >>> b.size() 32 """ return int(Z3_get_bv_sort_size(self.ctx_ref(), self.ast)) def subsort(self, other): return is_bv_sort(other) and self.size() < other.size() def cast(self, val): """Try to cast `val` as a Bit-Vector. >>> b = BitVecSort(32) >>> b.cast(10) 10 >>> b.cast(10).sexpr() '#x0000000a' """ if is_expr(val): if z3_debug(): _z3_assert(self.ctx == val.ctx, "Context mismatch") # Idea: use sign_extend if sort of val is a bitvector of smaller size return val else: return BitVecVal(val, self) def is_bv_sort(s): """Return True if `s` is a Z3 bit-vector sort. >>> is_bv_sort(BitVecSort(32)) True >>> is_bv_sort(IntSort()) False """ return isinstance(s, BitVecSortRef) class BitVecRef(ExprRef): """Bit-vector expressions.""" def sort(self): """Return the sort of the bit-vector expression `self`. >>> x = BitVec('x', 32) >>> x.sort() BitVec(32) >>> x.sort() == BitVecSort(32) True """ return BitVecSortRef(Z3_get_sort(self.ctx_ref(), self.as_ast()), self.ctx) def size(self): """Return the number of bits of the bit-vector expression `self`. >>> x = BitVec('x', 32) >>> (x + 1).size() 32 >>> Concat(x, x).size() 64 """ return self.sort().size() def __add__(self, other): """Create the Z3 expression `self + other`. >>> x = BitVec('x', 32) >>> y = BitVec('y', 32) >>> x + y x + y >>> (x + y).sort() BitVec(32) """ a, b = _coerce_exprs(self, other) return BitVecRef(Z3_mk_bvadd(self.ctx_ref(), a.as_ast(), b.as_ast()), self.ctx) def __radd__(self, other): """Create the Z3 expression `other + self`. >>> x = BitVec('x', 32) >>> 10 + x 10 + x """ a, b = _coerce_exprs(self, other) return BitVecRef(Z3_mk_bvadd(self.ctx_ref(), b.as_ast(), a.as_ast()), self.ctx) def __mul__(self, other): """Create the Z3 expression `self * other`. >>> x = BitVec('x', 32) >>> y = BitVec('y', 32) >>> x * y x*y >>> (x * y).sort() BitVec(32) """ a, b = _coerce_exprs(self, other) return BitVecRef(Z3_mk_bvmul(self.ctx_ref(), a.as_ast(), b.as_ast()), self.ctx) def __rmul__(self, other): """Create the Z3 expression `other * self`. >>> x = BitVec('x', 32) >>> 10 * x 10*x """ a, b = _coerce_exprs(self, other) return BitVecRef(Z3_mk_bvmul(self.ctx_ref(), b.as_ast(), a.as_ast()), self.ctx) def __sub__(self, other): """Create the Z3 expression `self - other`. >>> x = BitVec('x', 32) >>> y = BitVec('y', 32) >>> x - y x - y >>> (x - y).sort() BitVec(32) """ a, b = _coerce_exprs(self, other) return BitVecRef(Z3_mk_bvsub(self.ctx_ref(), a.as_ast(), b.as_ast()), self.ctx) def __rsub__(self, other): """Create the Z3 expression `other - self`. >>> x = BitVec('x', 32) >>> 10 - x 10 - x """ a, b = _coerce_exprs(self, other) return BitVecRef(Z3_mk_bvsub(self.ctx_ref(), b.as_ast(), a.as_ast()), self.ctx) def __or__(self, other): """Create the Z3 expression bitwise-or `self | other`. >>> x = BitVec('x', 32) >>> y = BitVec('y', 32) >>> x | y x | y >>> (x | y).sort() BitVec(32) """ a, b = _coerce_exprs(self, other) return BitVecRef(Z3_mk_bvor(self.ctx_ref(), a.as_ast(), b.as_ast()), self.ctx) def __ror__(self, other): """Create the Z3 expression bitwise-or `other | self`. >>> x = BitVec('x', 32) >>> 10 | x 10 | x """ a, b = _coerce_exprs(self, other) return BitVecRef(Z3_mk_bvor(self.ctx_ref(), b.as_ast(), a.as_ast()), self.ctx) def __and__(self, other): """Create the Z3 expression bitwise-and `self & other`. >>> x = BitVec('x', 32) >>> y = BitVec('y', 32) >>> x & y x & y >>> (x & y).sort() BitVec(32) """ a, b = _coerce_exprs(self, other) return BitVecRef(Z3_mk_bvand(self.ctx_ref(), a.as_ast(), b.as_ast()), self.ctx) def __rand__(self, other): """Create the Z3 expression bitwise-or `other & self`. >>> x = BitVec('x', 32) >>> 10 & x 10 & x """ a, b = _coerce_exprs(self, other) return BitVecRef(Z3_mk_bvand(self.ctx_ref(), b.as_ast(), a.as_ast()), self.ctx) def __xor__(self, other): """Create the Z3 expression bitwise-xor `self ^ other`. >>> x = BitVec('x', 32) >>> y = BitVec('y', 32) >>> x ^ y x ^ y >>> (x ^ y).sort() BitVec(32) """ a, b = _coerce_exprs(self, other) return BitVecRef(Z3_mk_bvxor(self.ctx_ref(), a.as_ast(), b.as_ast()), self.ctx) def __rxor__(self, other): """Create the Z3 expression bitwise-xor `other ^ self`. >>> x = BitVec('x', 32) >>> 10 ^ x 10 ^ x """ a, b = _coerce_exprs(self, other) return BitVecRef(Z3_mk_bvxor(self.ctx_ref(), b.as_ast(), a.as_ast()), self.ctx) def __pos__(self): """Return `self`. >>> x = BitVec('x', 32) >>> +x x """ return self def __neg__(self): """Return an expression representing `-self`. >>> x = BitVec('x', 32) >>> -x -x >>> simplify(-(-x)) x """ return BitVecRef(Z3_mk_bvneg(self.ctx_ref(), self.as_ast()), self.ctx) def __invert__(self): """Create the Z3 expression bitwise-not `~self`. >>> x = BitVec('x', 32) >>> ~x ~x >>> simplify(~(~x)) x """ return BitVecRef(Z3_mk_bvnot(self.ctx_ref(), self.as_ast()), self.ctx) def __div__(self, other): """Create the Z3 expression (signed) division `self / other`. Use the function UDiv() for unsigned division. >>> x = BitVec('x', 32) >>> y = BitVec('y', 32) >>> x / y x/y >>> (x / y).sort() BitVec(32) >>> (x / y).sexpr() '(bvsdiv x y)' >>> UDiv(x, y).sexpr() '(bvudiv x y)' """ a, b = _coerce_exprs(self, other) return BitVecRef(Z3_mk_bvsdiv(self.ctx_ref(), a.as_ast(), b.as_ast()), self.ctx) def __truediv__(self, other): """Create the Z3 expression (signed) division `self / other`.""" return self.__div__(other) def __rdiv__(self, other): """Create the Z3 expression (signed) division `other / self`. Use the function UDiv() for unsigned division. >>> x = BitVec('x', 32) >>> 10 / x 10/x >>> (10 / x).sexpr() '(bvsdiv #x0000000a x)' >>> UDiv(10, x).sexpr() '(bvudiv #x0000000a x)' """ a, b = _coerce_exprs(self, other) return BitVecRef(Z3_mk_bvsdiv(self.ctx_ref(), b.as_ast(), a.as_ast()), self.ctx) def __rtruediv__(self, other): """Create the Z3 expression (signed) division `other / self`.""" return self.__rdiv__(other) def __mod__(self, other): """Create the Z3 expression (signed) mod `self % other`. Use the function URem() for unsigned remainder, and SRem() for signed remainder. >>> x = BitVec('x', 32) >>> y = BitVec('y', 32) >>> x % y x%y >>> (x % y).sort() BitVec(32) >>> (x % y).sexpr() '(bvsmod x y)' >>> URem(x, y).sexpr() '(bvurem x y)' >>> SRem(x, y).sexpr() '(bvsrem x y)' """ a, b = _coerce_exprs(self, other) return BitVecRef(Z3_mk_bvsmod(self.ctx_ref(), a.as_ast(), b.as_ast()), self.ctx) def __rmod__(self, other): """Create the Z3 expression (signed) mod `other % self`. Use the function URem() for unsigned remainder, and SRem() for signed remainder. >>> x = BitVec('x', 32) >>> 10 % x 10%x >>> (10 % x).sexpr() '(bvsmod #x0000000a x)' >>> URem(10, x).sexpr() '(bvurem #x0000000a x)' >>> SRem(10, x).sexpr() '(bvsrem #x0000000a x)' """ a, b = _coerce_exprs(self, other) return BitVecRef(Z3_mk_bvsmod(self.ctx_ref(), b.as_ast(), a.as_ast()), self.ctx) def __le__(self, other): """Create the Z3 expression (signed) `other <= self`. Use the function ULE() for unsigned less than or equal to. >>> x, y = BitVecs('x y', 32) >>> x <= y x <= y >>> (x <= y).sexpr() '(bvsle x y)' >>> ULE(x, y).sexpr() '(bvule x y)' """ a, b = _coerce_exprs(self, other) return BoolRef(Z3_mk_bvsle(self.ctx_ref(), a.as_ast(), b.as_ast()), self.ctx) def __lt__(self, other): """Create the Z3 expression (signed) `other < self`. Use the function ULT() for unsigned less than. >>> x, y = BitVecs('x y', 32) >>> x < y x < y >>> (x < y).sexpr() '(bvslt x y)' >>> ULT(x, y).sexpr() '(bvult x y)' """ a, b = _coerce_exprs(self, other) return BoolRef(Z3_mk_bvslt(self.ctx_ref(), a.as_ast(), b.as_ast()), self.ctx) def __gt__(self, other): """Create the Z3 expression (signed) `other > self`. Use the function UGT() for unsigned greater than. >>> x, y = BitVecs('x y', 32) >>> x > y x > y >>> (x > y).sexpr() '(bvsgt x y)' >>> UGT(x, y).sexpr() '(bvugt x y)' """ a, b = _coerce_exprs(self, other) return BoolRef(Z3_mk_bvsgt(self.ctx_ref(), a.as_ast(), b.as_ast()), self.ctx) def __ge__(self, other): """Create the Z3 expression (signed) `other >= self`. Use the function UGE() for unsigned greater than or equal to. >>> x, y = BitVecs('x y', 32) >>> x >= y x >= y >>> (x >= y).sexpr() '(bvsge x y)' >>> UGE(x, y).sexpr() '(bvuge x y)' """ a, b = _coerce_exprs(self, other) return BoolRef(Z3_mk_bvsge(self.ctx_ref(), a.as_ast(), b.as_ast()), self.ctx) def __rshift__(self, other): """Create the Z3 expression (arithmetical) right shift `self >> other` Use the function LShR() for the right logical shift >>> x, y = BitVecs('x y', 32) >>> x >> y x >> y >>> (x >> y).sexpr() '(bvashr x y)' >>> LShR(x, y).sexpr() '(bvlshr x y)' >>> BitVecVal(4, 3) 4 >>> BitVecVal(4, 3).as_signed_long() -4 >>> simplify(BitVecVal(4, 3) >> 1).as_signed_long() -2 >>> simplify(BitVecVal(4, 3) >> 1) 6 >>> simplify(LShR(BitVecVal(4, 3), 1)) 2 >>> simplify(BitVecVal(2, 3) >> 1) 1 >>> simplify(LShR(BitVecVal(2, 3), 1)) 1 """ a, b = _coerce_exprs(self, other) return BitVecRef(Z3_mk_bvashr(self.ctx_ref(), a.as_ast(), b.as_ast()), self.ctx) def __lshift__(self, other): """Create the Z3 expression left shift `self << other` >>> x, y = BitVecs('x y', 32) >>> x << y x << y >>> (x << y).sexpr() '(bvshl x y)' >>> simplify(BitVecVal(2, 3) << 1) 4 """ a, b = _coerce_exprs(self, other) return BitVecRef(Z3_mk_bvshl(self.ctx_ref(), a.as_ast(), b.as_ast()), self.ctx) def __rrshift__(self, other): """Create the Z3 expression (arithmetical) right shift `other` >> `self`. Use the function LShR() for the right logical shift >>> x = BitVec('x', 32) >>> 10 >> x 10 >> x >>> (10 >> x).sexpr() '(bvashr #x0000000a x)' """ a, b = _coerce_exprs(self, other) return BitVecRef(Z3_mk_bvashr(self.ctx_ref(), b.as_ast(), a.as_ast()), self.ctx) def __rlshift__(self, other): """Create the Z3 expression left shift `other << self`. Use the function LShR() for the right logical shift >>> x = BitVec('x', 32) >>> 10 << x 10 << x >>> (10 << x).sexpr() '(bvshl #x0000000a x)' """ a, b = _coerce_exprs(self, other) return BitVecRef(Z3_mk_bvshl(self.ctx_ref(), b.as_ast(), a.as_ast()), self.ctx) class BitVecNumRef(BitVecRef): """Bit-vector values.""" def as_long(self): """Return a Z3 bit-vector numeral as a Python long (bignum) numeral. >>> v = BitVecVal(0xbadc0de, 32) >>> v 195936478 >>> print("0x%.8x" % v.as_long()) 0x0badc0de """ return int(self.as_string()) def as_signed_long(self): """Return a Z3 bit-vector numeral as a Python long (bignum) numeral. The most significant bit is assumed to be the sign. >>> BitVecVal(4, 3).as_signed_long() -4 >>> BitVecVal(7, 3).as_signed_long() -1 >>> BitVecVal(3, 3).as_signed_long() 3 >>> BitVecVal(2**32 - 1, 32).as_signed_long() -1 >>> BitVecVal(2**64 - 1, 64).as_signed_long() -1 """ sz = self.size() val = self.as_long() if val >= 2**(sz - 1): val = val - 2**sz if val < -2**(sz - 1): val = val + 2**sz return int(val) def as_string(self): return Z3_get_numeral_string(self.ctx_ref(), self.as_ast()) def is_bv(a): """Return `True` if `a` is a Z3 bit-vector expression. >>> b = BitVec('b', 32) >>> is_bv(b) True >>> is_bv(b + 10) True >>> is_bv(Int('x')) False """ return isinstance(a, BitVecRef) def is_bv_value(a): """Return `True` if `a` is a Z3 bit-vector numeral value. >>> b = BitVec('b', 32) >>> is_bv_value(b) False >>> b = BitVecVal(10, 32) >>> b 10 >>> is_bv_value(b) True """ return is_bv(a) and _is_numeral(a.ctx, a.as_ast()) def BV2Int(a, is_signed=False): """Return the Z3 expression BV2Int(a). >>> b = BitVec('b', 3) >>> BV2Int(b).sort() Int >>> x = Int('x') >>> x > BV2Int(b) x > BV2Int(b) >>> x > BV2Int(b, is_signed=False) x > BV2Int(b) >>> x > BV2Int(b, is_signed=True) x > If(b < 0, BV2Int(b) - 8, BV2Int(b)) >>> solve(x > BV2Int(b), b == 1, x < 3) [x = 2, b = 1] """ if z3_debug(): _z3_assert(is_bv(a), "Z3 bit-vector expression expected") ctx = a.ctx ## investigate problem with bv2int return ArithRef(Z3_mk_bv2int(ctx.ref(), a.as_ast(), is_signed), ctx) def Int2BV(a, num_bits): """Return the z3 expression Int2BV(a, num_bits). It is a bit-vector of width num_bits and represents the modulo of a by 2^num_bits """ ctx = a.ctx return BitVecRef(Z3_mk_int2bv(ctx.ref(), num_bits, a.as_ast()), ctx) def BitVecSort(sz, ctx=None): """Return a Z3 bit-vector sort of the given size. If `ctx=None`, then the global context is used. >>> Byte = BitVecSort(8) >>> Word = BitVecSort(16) >>> Byte BitVec(8) >>> x = Const('x', Byte) >>> eq(x, BitVec('x', 8)) True """ ctx = _get_ctx(ctx) return BitVecSortRef(Z3_mk_bv_sort(ctx.ref(), sz), ctx) def BitVecVal(val, bv, ctx=None): """Return a bit-vector value with the given number of bits. If `ctx=None`, then the global context is used. >>> v = BitVecVal(10, 32) >>> v 10 >>> print("0x%.8x" % v.as_long()) 0x0000000a """ if is_bv_sort(bv): ctx = bv.ctx return BitVecNumRef(Z3_mk_numeral(ctx.ref(), _to_int_str(val), bv.ast), ctx) else: ctx = _get_ctx(ctx) return BitVecNumRef(Z3_mk_numeral(ctx.ref(), _to_int_str(val), BitVecSort(bv, ctx).ast), ctx) def BitVec(name, bv, ctx=None): """Return a bit-vector constant named `name`. `bv` may be the number of bits of a bit-vector sort. If `ctx=None`, then the global context is used. >>> x = BitVec('x', 16) >>> is_bv(x) True >>> x.size() 16 >>> x.sort() BitVec(16) >>> word = BitVecSort(16) >>> x2 = BitVec('x', word) >>> eq(x, x2) True """ if isinstance(bv, BitVecSortRef): ctx = bv.ctx else: ctx = _get_ctx(ctx) bv = BitVecSort(bv, ctx) return BitVecRef(Z3_mk_const(ctx.ref(), to_symbol(name, ctx), bv.ast), ctx) def BitVecs(names, bv, ctx=None): """Return a tuple of bit-vector constants of size bv. >>> x, y, z = BitVecs('x y z', 16) >>> x.size() 16 >>> x.sort() BitVec(16) >>> Sum(x, y, z) 0 + x + y + z >>> Product(x, y, z) 1*x*y*z >>> simplify(Product(x, y, z)) x*y*z """ ctx = _get_ctx(ctx) if isinstance(names, str): names = names.split(" ") return [BitVec(name, bv, ctx) for name in names] def Concat(*args): """Create a Z3 bit-vector concatenation expression. >>> v = BitVecVal(1, 4) >>> Concat(v, v+1, v) Concat(Concat(1, 1 + 1), 1) >>> simplify(Concat(v, v+1, v)) 289 >>> print("%.3x" % simplify(Concat(v, v+1, v)).as_long()) 121 """ args = _get_args(args) sz = len(args) if z3_debug(): _z3_assert(sz >= 2, "At least two arguments expected.") ctx = None for a in args: if is_expr(a): ctx = a.ctx break if is_seq(args[0]) or isinstance(args[0], str): args = [_coerce_seq(s, ctx) for s in args] if z3_debug(): _z3_assert(all([is_seq(a) for a in args]), "All arguments must be sequence expressions.") v = (Ast * sz)() for i in range(sz): v[i] = args[i].as_ast() return SeqRef(Z3_mk_seq_concat(ctx.ref(), sz, v), ctx) if is_re(args[0]): if z3_debug(): _z3_assert(all([is_re(a) for a in args]), "All arguments must be regular expressions.") v = (Ast * sz)() for i in range(sz): v[i] = args[i].as_ast() return ReRef(Z3_mk_re_concat(ctx.ref(), sz, v), ctx) if z3_debug(): _z3_assert(all([is_bv(a) for a in args]), "All arguments must be Z3 bit-vector expressions.") r = args[0] for i in range(sz - 1): r = BitVecRef(Z3_mk_concat(ctx.ref(), r.as_ast(), args[i+1].as_ast()), ctx) return r def Extract(high, low, a): """Create a Z3 bit-vector extraction expression, or create a string extraction expression. >>> x = BitVec('x', 8) >>> Extract(6, 2, x) Extract(6, 2, x) >>> Extract(6, 2, x).sort() BitVec(5) >>> simplify(Extract(StringVal("abcd"),2,1)) "c" """ if isinstance(high, str): high = StringVal(high) if is_seq(high): s = high offset, length = _coerce_exprs(low, a, s.ctx) return SeqRef(Z3_mk_seq_extract(s.ctx_ref(), s.as_ast(), offset.as_ast(), length.as_ast()), s.ctx) if z3_debug(): _z3_assert(low <= high, "First argument must be greater than or equal to second argument") _z3_assert(_is_int(high) and high >= 0 and _is_int(low) and low >= 0, "First and second arguments must be non negative integers") _z3_assert(is_bv(a), "Third argument must be a Z3 Bitvector expression") return BitVecRef(Z3_mk_extract(a.ctx_ref(), high, low, a.as_ast()), a.ctx) def _check_bv_args(a, b): if z3_debug(): _z3_assert(is_bv(a) or is_bv(b), "At least one of the arguments must be a Z3 bit-vector expression") def ULE(a, b): """Create the Z3 expression (unsigned) `other <= self`. Use the operator <= for signed less than or equal to. >>> x, y = BitVecs('x y', 32) >>> ULE(x, y) ULE(x, y) >>> (x <= y).sexpr() '(bvsle x y)' >>> ULE(x, y).sexpr() '(bvule x y)' """ _check_bv_args(a, b) a, b = _coerce_exprs(a, b) return BoolRef(Z3_mk_bvule(a.ctx_ref(), a.as_ast(), b.as_ast()), a.ctx) def ULT(a, b): """Create the Z3 expression (unsigned) `other < self`. Use the operator < for signed less than. >>> x, y = BitVecs('x y', 32) >>> ULT(x, y) ULT(x, y) >>> (x < y).sexpr() '(bvslt x y)' >>> ULT(x, y).sexpr() '(bvult x y)' """ _check_bv_args(a, b) a, b = _coerce_exprs(a, b) return BoolRef(Z3_mk_bvult(a.ctx_ref(), a.as_ast(), b.as_ast()), a.ctx) def UGE(a, b): """Create the Z3 expression (unsigned) `other >= self`. Use the operator >= for signed greater than or equal to. >>> x, y = BitVecs('x y', 32) >>> UGE(x, y) UGE(x, y) >>> (x >= y).sexpr() '(bvsge x y)' >>> UGE(x, y).sexpr() '(bvuge x y)' """ _check_bv_args(a, b) a, b = _coerce_exprs(a, b) return BoolRef(Z3_mk_bvuge(a.ctx_ref(), a.as_ast(), b.as_ast()), a.ctx) def UGT(a, b): """Create the Z3 expression (unsigned) `other > self`. Use the operator > for signed greater than. >>> x, y = BitVecs('x y', 32) >>> UGT(x, y) UGT(x, y) >>> (x > y).sexpr() '(bvsgt x y)' >>> UGT(x, y).sexpr() '(bvugt x y)' """ _check_bv_args(a, b) a, b = _coerce_exprs(a, b) return BoolRef(Z3_mk_bvugt(a.ctx_ref(), a.as_ast(), b.as_ast()), a.ctx) def UDiv(a, b): """Create the Z3 expression (unsigned) division `self / other`. Use the operator / for signed division. >>> x = BitVec('x', 32) >>> y = BitVec('y', 32) >>> UDiv(x, y) UDiv(x, y) >>> UDiv(x, y).sort() BitVec(32) >>> (x / y).sexpr() '(bvsdiv x y)' >>> UDiv(x, y).sexpr() '(bvudiv x y)' """ _check_bv_args(a, b) a, b = _coerce_exprs(a, b) return BitVecRef(Z3_mk_bvudiv(a.ctx_ref(), a.as_ast(), b.as_ast()), a.ctx) def URem(a, b): """Create the Z3 expression (unsigned) remainder `self % other`. Use the operator % for signed modulus, and SRem() for signed remainder. >>> x = BitVec('x', 32) >>> y = BitVec('y', 32) >>> URem(x, y) URem(x, y) >>> URem(x, y).sort() BitVec(32) >>> (x % y).sexpr() '(bvsmod x y)' >>> URem(x, y).sexpr() '(bvurem x y)' """ _check_bv_args(a, b) a, b = _coerce_exprs(a, b) return BitVecRef(Z3_mk_bvurem(a.ctx_ref(), a.as_ast(), b.as_ast()), a.ctx) def SRem(a, b): """Create the Z3 expression signed remainder. Use the operator % for signed modulus, and URem() for unsigned remainder. >>> x = BitVec('x', 32) >>> y = BitVec('y', 32) >>> SRem(x, y) SRem(x, y) >>> SRem(x, y).sort() BitVec(32) >>> (x % y).sexpr() '(bvsmod x y)' >>> SRem(x, y).sexpr() '(bvsrem x y)' """ _check_bv_args(a, b) a, b = _coerce_exprs(a, b) return BitVecRef(Z3_mk_bvsrem(a.ctx_ref(), a.as_ast(), b.as_ast()), a.ctx) def LShR(a, b): """Create the Z3 expression logical right shift. Use the operator >> for the arithmetical right shift. >>> x, y = BitVecs('x y', 32) >>> LShR(x, y) LShR(x, y) >>> (x >> y).sexpr() '(bvashr x y)' >>> LShR(x, y).sexpr() '(bvlshr x y)' >>> BitVecVal(4, 3) 4 >>> BitVecVal(4, 3).as_signed_long() -4 >>> simplify(BitVecVal(4, 3) >> 1).as_signed_long() -2 >>> simplify(BitVecVal(4, 3) >> 1) 6 >>> simplify(LShR(BitVecVal(4, 3), 1)) 2 >>> simplify(BitVecVal(2, 3) >> 1) 1 >>> simplify(LShR(BitVecVal(2, 3), 1)) 1 """ _check_bv_args(a, b) a, b = _coerce_exprs(a, b) return BitVecRef(Z3_mk_bvlshr(a.ctx_ref(), a.as_ast(), b.as_ast()), a.ctx) def RotateLeft(a, b): """Return an expression representing `a` rotated to the left `b` times. >>> a, b = BitVecs('a b', 16) >>> RotateLeft(a, b) RotateLeft(a, b) >>> simplify(RotateLeft(a, 0)) a >>> simplify(RotateLeft(a, 16)) a """ _check_bv_args(a, b) a, b = _coerce_exprs(a, b) return BitVecRef(Z3_mk_ext_rotate_left(a.ctx_ref(), a.as_ast(), b.as_ast()), a.ctx) def RotateRight(a, b): """Return an expression representing `a` rotated to the right `b` times. >>> a, b = BitVecs('a b', 16) >>> RotateRight(a, b) RotateRight(a, b) >>> simplify(RotateRight(a, 0)) a >>> simplify(RotateRight(a, 16)) a """ _check_bv_args(a, b) a, b = _coerce_exprs(a, b) return BitVecRef(Z3_mk_ext_rotate_right(a.ctx_ref(), a.as_ast(), b.as_ast()), a.ctx) def SignExt(n, a): """Return a bit-vector expression with `n` extra sign-bits. >>> x = BitVec('x', 16) >>> n = SignExt(8, x) >>> n.size() 24 >>> n SignExt(8, x) >>> n.sort() BitVec(24) >>> v0 = BitVecVal(2, 2) >>> v0 2 >>> v0.size() 2 >>> v = simplify(SignExt(6, v0)) >>> v 254 >>> v.size() 8 >>> print("%.x" % v.as_long()) fe """ if z3_debug(): _z3_assert(_is_int(n), "First argument must be an integer") _z3_assert(is_bv(a), "Second argument must be a Z3 Bitvector expression") return BitVecRef(Z3_mk_sign_ext(a.ctx_ref(), n, a.as_ast()), a.ctx) def ZeroExt(n, a): """Return a bit-vector expression with `n` extra zero-bits. >>> x = BitVec('x', 16) >>> n = ZeroExt(8, x) >>> n.size() 24 >>> n ZeroExt(8, x) >>> n.sort() BitVec(24) >>> v0 = BitVecVal(2, 2) >>> v0 2 >>> v0.size() 2 >>> v = simplify(ZeroExt(6, v0)) >>> v 2 >>> v.size() 8 """ if z3_debug(): _z3_assert(_is_int(n), "First argument must be an integer") _z3_assert(is_bv(a), "Second argument must be a Z3 Bitvector expression") return BitVecRef(Z3_mk_zero_ext(a.ctx_ref(), n, a.as_ast()), a.ctx) def RepeatBitVec(n, a): """Return an expression representing `n` copies of `a`. >>> x = BitVec('x', 8) >>> n = RepeatBitVec(4, x) >>> n RepeatBitVec(4, x) >>> n.size() 32 >>> v0 = BitVecVal(10, 4) >>> print("%.x" % v0.as_long()) a >>> v = simplify(RepeatBitVec(4, v0)) >>> v.size() 16 >>> print("%.x" % v.as_long()) aaaa """ if z3_debug(): _z3_assert(_is_int(n), "First argument must be an integer") _z3_assert(is_bv(a), "Second argument must be a Z3 Bitvector expression") return BitVecRef(Z3_mk_repeat(a.ctx_ref(), n, a.as_ast()), a.ctx) def BVRedAnd(a): """Return the reduction-and expression of `a`.""" if z3_debug(): _z3_assert(is_bv(a), "First argument must be a Z3 Bitvector expression") return BitVecRef(Z3_mk_bvredand(a.ctx_ref(), a.as_ast()), a.ctx) def BVRedOr(a): """Return the reduction-or expression of `a`.""" if z3_debug(): _z3_assert(is_bv(a), "First argument must be a Z3 Bitvector expression") return BitVecRef(Z3_mk_bvredor(a.ctx_ref(), a.as_ast()), a.ctx) def BVAddNoOverflow(a, b, signed): """A predicate the determines that bit-vector addition does not overflow""" _check_bv_args(a, b) a, b = _coerce_exprs(a, b) return BoolRef(Z3_mk_bvadd_no_overflow(a.ctx_ref(), a.as_ast(), b.as_ast(), signed), a.ctx) def BVAddNoUnderflow(a, b): """A predicate the determines that signed bit-vector addition does not underflow""" _check_bv_args(a, b) a, b = _coerce_exprs(a, b) return BoolRef(Z3_mk_bvadd_no_underflow(a.ctx_ref(), a.as_ast(), b.as_ast()), a.ctx) def BVSubNoOverflow(a, b): """A predicate the determines that bit-vector subtraction does not overflow""" _check_bv_args(a, b) a, b = _coerce_exprs(a, b) return BoolRef(Z3_mk_bvsub_no_overflow(a.ctx_ref(), a.as_ast(), b.as_ast()), a.ctx) def BVSubNoUnderflow(a, b, signed): """A predicate the determines that bit-vector subtraction does not underflow""" _check_bv_args(a, b) a, b = _coerce_exprs(a, b) return BoolRef(Z3_mk_bvsub_no_underflow(a.ctx_ref(), a.as_ast(), b.as_ast(), signed), a.ctx) def BVSDivNoOverflow(a, b): """A predicate the determines that bit-vector signed division does not overflow""" _check_bv_args(a, b) a, b = _coerce_exprs(a, b) return BoolRef(Z3_mk_bvsdiv_no_overflow(a.ctx_ref(), a.as_ast(), b.as_ast()), a.ctx) def BVSNegNoOverflow(a): """A predicate the determines that bit-vector unary negation does not overflow""" if z3_debug(): _z3_assert(is_bv(a), "Argument should be a bit-vector") return BoolRef(Z3_mk_bvneg_no_overflow(a.ctx_ref(), a.as_ast()), a.ctx) def BVMulNoOverflow(a, b, signed): """A predicate the determines that bit-vector multiplication does not overflow""" _check_bv_args(a, b) a, b = _coerce_exprs(a, b) return BoolRef(Z3_mk_bvmul_no_overflow(a.ctx_ref(), a.as_ast(), b.as_ast(), signed), a.ctx) def BVMulNoUnderflow(a, b): """A predicate the determines that bit-vector signed multiplication does not underflow""" _check_bv_args(a, b) a, b = _coerce_exprs(a, b) return BoolRef(Z3_mk_bvmul_no_underflow(a.ctx_ref(), a.as_ast(), b.as_ast()), a.ctx) ######################################### # # Arrays # ######################################### class ArraySortRef(SortRef): """Array sorts.""" def domain(self): """Return the domain of the array sort `self`. >>> A = ArraySort(IntSort(), BoolSort()) >>> A.domain() Int """ return _to_sort_ref(Z3_get_array_sort_domain(self.ctx_ref(), self.ast), self.ctx) def range(self): """Return the range of the array sort `self`. >>> A = ArraySort(IntSort(), BoolSort()) >>> A.range() Bool """ return _to_sort_ref(Z3_get_array_sort_range(self.ctx_ref(), self.ast), self.ctx) class ArrayRef(ExprRef): """Array expressions. """ def sort(self): """Return the array sort of the array expression `self`. >>> a = Array('a', IntSort(), BoolSort()) >>> a.sort() Array(Int, Bool) """ return ArraySortRef(Z3_get_sort(self.ctx_ref(), self.as_ast()), self.ctx) def domain(self): """Shorthand for `self.sort().domain()`. >>> a = Array('a', IntSort(), BoolSort()) >>> a.domain() Int """ return self.sort().domain() def range(self): """Shorthand for `self.sort().range()`. >>> a = Array('a', IntSort(), BoolSort()) >>> a.range() Bool """ return self.sort().range() def __getitem__(self, arg): """Return the Z3 expression `self[arg]`. >>> a = Array('a', IntSort(), BoolSort()) >>> i = Int('i') >>> a[i] a[i] >>> a[i].sexpr() '(select a i)' """ arg = self.domain().cast(arg) return _to_expr_ref(Z3_mk_select(self.ctx_ref(), self.as_ast(), arg.as_ast()), self.ctx) def default(self): return _to_expr_ref(Z3_mk_array_default(self.ctx_ref(), self.as_ast()), self.ctx) def is_array_sort(a): return Z3_get_sort_kind(a.ctx.ref(), Z3_get_sort(a.ctx.ref(), a.ast)) == Z3_ARRAY_SORT def is_array(a): """Return `True` if `a` is a Z3 array expression. >>> a = Array('a', IntSort(), IntSort()) >>> is_array(a) True >>> is_array(Store(a, 0, 1)) True >>> is_array(a[0]) False """ return isinstance(a, ArrayRef) def is_const_array(a): """Return `True` if `a` is a Z3 constant array. >>> a = K(IntSort(), 10) >>> is_const_array(a) True >>> a = Array('a', IntSort(), IntSort()) >>> is_const_array(a) False """ return is_app_of(a, Z3_OP_CONST_ARRAY) def is_K(a): """Return `True` if `a` is a Z3 constant array. >>> a = K(IntSort(), 10) >>> is_K(a) True >>> a = Array('a', IntSort(), IntSort()) >>> is_K(a) False """ return is_app_of(a, Z3_OP_CONST_ARRAY) def is_map(a): """Return `True` if `a` is a Z3 map array expression. >>> f = Function('f', IntSort(), IntSort()) >>> b = Array('b', IntSort(), IntSort()) >>> a = Map(f, b) >>> a Map(f, b) >>> is_map(a) True >>> is_map(b) False """ return is_app_of(a, Z3_OP_ARRAY_MAP) def is_default(a): """Return `True` if `a` is a Z3 default array expression. >>> d = Default(K(IntSort(), 10)) >>> is_default(d) True """ return is_app_of(a, Z3_OP_ARRAY_DEFAULT) def get_map_func(a): """Return the function declaration associated with a Z3 map array expression. >>> f = Function('f', IntSort(), IntSort()) >>> b = Array('b', IntSort(), IntSort()) >>> a = Map(f, b) >>> eq(f, get_map_func(a)) True >>> get_map_func(a) f >>> get_map_func(a)(0) f(0) """ if z3_debug(): _z3_assert(is_map(a), "Z3 array map expression expected.") return FuncDeclRef(Z3_to_func_decl(a.ctx_ref(), Z3_get_decl_ast_parameter(a.ctx_ref(), a.decl().ast, 0)), a.ctx) def ArraySort(*sig): """Return the Z3 array sort with the given domain and range sorts. >>> A = ArraySort(IntSort(), BoolSort()) >>> A Array(Int, Bool) >>> A.domain() Int >>> A.range() Bool >>> AA = ArraySort(IntSort(), A) >>> AA Array(Int, Array(Int, Bool)) """ sig = _get_args(sig) if z3_debug(): _z3_assert(len(sig) > 1, "At least two arguments expected") arity = len(sig) - 1 r = sig[arity] d = sig[0] if z3_debug(): for s in sig: _z3_assert(is_sort(s), "Z3 sort expected") _z3_assert(s.ctx == r.ctx, "Context mismatch") ctx = d.ctx if len(sig) == 2: return ArraySortRef(Z3_mk_array_sort(ctx.ref(), d.ast, r.ast), ctx) dom = (Sort * arity)() for i in range(arity): dom[i] = sig[i].ast return ArraySortRef(Z3_mk_array_sort_n(ctx.ref(), arity, dom, r.ast), ctx) def Array(name, dom, rng): """Return an array constant named `name` with the given domain and range sorts. >>> a = Array('a', IntSort(), IntSort()) >>> a.sort() Array(Int, Int) >>> a[0] a[0] """ s = ArraySort(dom, rng) ctx = s.ctx return ArrayRef(Z3_mk_const(ctx.ref(), to_symbol(name, ctx), s.ast), ctx) def Update(a, i, v): """Return a Z3 store array expression. >>> a = Array('a', IntSort(), IntSort()) >>> i, v = Ints('i v') >>> s = Update(a, i, v) >>> s.sort() Array(Int, Int) >>> prove(s[i] == v) proved >>> j = Int('j') >>> prove(Implies(i != j, s[j] == a[j])) proved """ if z3_debug(): _z3_assert(is_array_sort(a), "First argument must be a Z3 array expression") i = a.domain().cast(i) v = a.range().cast(v) ctx = a.ctx return _to_expr_ref(Z3_mk_store(ctx.ref(), a.as_ast(), i.as_ast(), v.as_ast()), ctx) def Default(a): """ Return a default value for array expression. >>> b = K(IntSort(), 1) >>> prove(Default(b) == 1) proved """ if z3_debug(): _z3_assert(is_array_sort(a), "First argument must be a Z3 array expression") return a.default() def Store(a, i, v): """Return a Z3 store array expression. >>> a = Array('a', IntSort(), IntSort()) >>> i, v = Ints('i v') >>> s = Store(a, i, v) >>> s.sort() Array(Int, Int) >>> prove(s[i] == v) proved >>> j = Int('j') >>> prove(Implies(i != j, s[j] == a[j])) proved """ return Update(a, i, v) def Select(a, i): """Return a Z3 select array expression. >>> a = Array('a', IntSort(), IntSort()) >>> i = Int('i') >>> Select(a, i) a[i] >>> eq(Select(a, i), a[i]) True """ if z3_debug(): _z3_assert(is_array_sort(a), "First argument must be a Z3 array expression") return a[i] def Map(f, *args): """Return a Z3 map array expression. >>> f = Function('f', IntSort(), IntSort(), IntSort()) >>> a1 = Array('a1', IntSort(), IntSort()) >>> a2 = Array('a2', IntSort(), IntSort()) >>> b = Map(f, a1, a2) >>> b Map(f, a1, a2) >>> prove(b[0] == f(a1[0], a2[0])) proved """ args = _get_args(args) if z3_debug(): _z3_assert(len(args) > 0, "At least one Z3 array expression expected") _z3_assert(is_func_decl(f), "First argument must be a Z3 function declaration") _z3_assert(all([is_array(a) for a in args]), "Z3 array expected expected") _z3_assert(len(args) == f.arity(), "Number of arguments mismatch") _args, sz = _to_ast_array(args) ctx = f.ctx return ArrayRef(Z3_mk_map(ctx.ref(), f.ast, sz, _args), ctx) def K(dom, v): """Return a Z3 constant array expression. >>> a = K(IntSort(), 10) >>> a K(Int, 10) >>> a.sort() Array(Int, Int) >>> i = Int('i') >>> a[i] K(Int, 10)[i] >>> simplify(a[i]) 10 """ if z3_debug(): _z3_assert(is_sort(dom), "Z3 sort expected") ctx = dom.ctx if not is_expr(v): v = _py2expr(v, ctx) return ArrayRef(Z3_mk_const_array(ctx.ref(), dom.ast, v.as_ast()), ctx) def Ext(a, b): """Return extensionality index for one-dimensional arrays. >> a, b = Consts('a b', SetSort(IntSort())) >> Ext(a, b) Ext(a, b) """ ctx = a.ctx if z3_debug(): _z3_assert(is_array_sort(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 SetHasSize(a, k): ctx = a.ctx k = _py2expr(k, ctx) return _to_expr_ref(Z3_mk_set_has_size(ctx.ref(), a.as_ast(), k.as_ast()), ctx) def is_select(a): """Return `True` if `a` is a Z3 array select application. >>> a = Array('a', IntSort(), IntSort()) >>> is_select(a) False >>> i = Int('i') >>> is_select(a[i]) True """ return is_app_of(a, Z3_OP_SELECT) def is_store(a): """Return `True` if `a` is a Z3 array store application. >>> a = Array('a', IntSort(), IntSort()) >>> is_store(a) False >>> is_store(Store(a, 0, 1)) True """ return is_app_of(a, Z3_OP_STORE) ######################################### # # Sets # ######################################### def SetSort(s): """ Create a set sort over element sort s""" return ArraySort(s, BoolSort()) def EmptySet(s): """Create the empty set >>> EmptySet(IntSort()) K(Int, False) """ ctx = s.ctx return ArrayRef(Z3_mk_empty_set(ctx.ref(), s.ast), ctx) def FullSet(s): """Create the full set >>> FullSet(IntSort()) K(Int, True) """ ctx = s.ctx return ArrayRef(Z3_mk_full_set(ctx.ref(), s.ast), ctx) def SetUnion(*args): """ Take the union of sets >>> a = Const('a', SetSort(IntSort())) >>> b = Const('b', SetSort(IntSort())) >>> SetUnion(a, b) union(a, b) """ args = _get_args(args) ctx = _ctx_from_ast_arg_list(args) _args, sz = _to_ast_array(args) return ArrayRef(Z3_mk_set_union(ctx.ref(), sz, _args), ctx) def SetIntersect(*args): """ Take the union of sets >>> a = Const('a', SetSort(IntSort())) >>> b = Const('b', SetSort(IntSort())) >>> SetIntersect(a, b) intersection(a, b) """ args = _get_args(args) ctx = _ctx_from_ast_arg_list(args) _args, sz = _to_ast_array(args) return ArrayRef(Z3_mk_set_intersect(ctx.ref(), sz, _args), ctx) def SetAdd(s, e): """ Add element e to set s >>> a = Const('a', SetSort(IntSort())) >>> SetAdd(a, 1) Store(a, 1, True) """ ctx = _ctx_from_ast_arg_list([s,e]) e = _py2expr(e, ctx) return ArrayRef(Z3_mk_set_add(ctx.ref(), s.as_ast(), e.as_ast()), ctx) def SetDel(s, e): """ Remove element e to set s >>> a = Const('a', SetSort(IntSort())) >>> SetDel(a, 1) Store(a, 1, False) """ ctx = _ctx_from_ast_arg_list([s,e]) e = _py2expr(e, ctx) return ArrayRef(Z3_mk_set_del(ctx.ref(), s.as_ast(), e.as_ast()), ctx) def SetComplement(s): """ The complement of set s >>> a = Const('a', SetSort(IntSort())) >>> SetComplement(a) complement(a) """ ctx = s.ctx return ArrayRef(Z3_mk_set_complement(ctx.ref(), s.as_ast()), ctx) def SetDifference(a, b): """ The set difference of a and b >>> a = Const('a', SetSort(IntSort())) >>> b = Const('b', SetSort(IntSort())) >>> SetDifference(a, b) setminus(a, b) """ ctx = _ctx_from_ast_arg_list([a, b]) return ArrayRef(Z3_mk_set_difference(ctx.ref(), a.as_ast(), b.as_ast()), ctx) def IsMember(e, s): """ Check if e is a member of set s >>> a = Const('a', SetSort(IntSort())) >>> IsMember(1, a) a[1] """ ctx = _ctx_from_ast_arg_list([s,e]) e = _py2expr(e, ctx) return BoolRef(Z3_mk_set_member(ctx.ref(), e.as_ast(), s.as_ast()), ctx) def IsSubset(a, b): """ Check if a is a subset of b >>> a = Const('a', SetSort(IntSort())) >>> b = Const('b', SetSort(IntSort())) >>> IsSubset(a, b) subset(a, b) """ ctx = _ctx_from_ast_arg_list([a, b]) return BoolRef(Z3_mk_set_subset(ctx.ref(), a.as_ast(), b.as_ast()), ctx) ######################################### # # Datatypes # ######################################### def _valid_accessor(acc): """Return `True` if acc is pair of the form (String, Datatype or Sort). """ return isinstance(acc, tuple) and len(acc) == 2 and isinstance(acc[0], str) and (isinstance(acc[1], Datatype) or is_sort(acc[1])) class Datatype: """Helper class for declaring Z3 datatypes. >>> List = Datatype('List') >>> List.declare('cons', ('car', IntSort()), ('cdr', List)) >>> List.declare('nil') >>> List = List.create() >>> # List is now a Z3 declaration >>> List.nil nil >>> List.cons(10, List.nil) cons(10, nil) >>> List.cons(10, List.nil).sort() List >>> cons = List.cons >>> nil = List.nil >>> car = List.car >>> cdr = List.cdr >>> n = cons(1, cons(0, nil)) >>> n cons(1, cons(0, nil)) >>> simplify(cdr(n)) cons(0, nil) >>> simplify(car(n)) 1 """ def __init__(self, name, ctx=None): self.ctx = _get_ctx(ctx) self.name = name self.constructors = [] def __deepcopy__(self, memo={}): r = Datatype(self.name, self.ctx) r.constructors = copy.deepcopy(self.constructors) return r def declare_core(self, name, rec_name, *args): if z3_debug(): _z3_assert(isinstance(name, str), "String expected") _z3_assert(isinstance(rec_name, str), "String expected") _z3_assert(all([_valid_accessor(a) for a in args]), "Valid list of accessors expected. An accessor is a pair of the form (String, Datatype|Sort)") self.constructors.append((name, rec_name, args)) def declare(self, name, *args): """Declare constructor named `name` with the given accessors `args`. Each accessor is a pair `(name, sort)`, where `name` is a string and `sort` a Z3 sort or a reference to the datatypes being declared. In the following example `List.declare('cons', ('car', IntSort()), ('cdr', List))` declares the constructor named `cons` that builds a new List using an integer and a List. It also declares the accessors `car` and `cdr`. The accessor `car` extracts the integer of a `cons` cell, and `cdr` the list of a `cons` cell. After all constructors were declared, we use the method create() to create the actual datatype in Z3. >>> List = Datatype('List') >>> List.declare('cons', ('car', IntSort()), ('cdr', List)) >>> List.declare('nil') >>> List = List.create() """ if z3_debug(): _z3_assert(isinstance(name, str), "String expected") _z3_assert(name != "", "Constructor name cannot be empty") return self.declare_core(name, "is-" + name, *args) def __repr__(self): return "Datatype(%s, %s)" % (self.name, self.constructors) def create(self): """Create a Z3 datatype based on the constructors declared using the method `declare()`. The function `CreateDatatypes()` must be used to define mutually recursive datatypes. >>> List = Datatype('List') >>> List.declare('cons', ('car', IntSort()), ('cdr', List)) >>> List.declare('nil') >>> List = List.create() >>> List.nil nil >>> List.cons(10, List.nil) cons(10, nil) """ return CreateDatatypes([self])[0] class ScopedConstructor: """Auxiliary object used to create Z3 datatypes.""" def __init__(self, c, ctx): self.c = c self.ctx = ctx def __del__(self): if self.ctx.ref() is not None: Z3_del_constructor(self.ctx.ref(), self.c) class ScopedConstructorList: """Auxiliary object used to create Z3 datatypes.""" def __init__(self, c, ctx): self.c = c self.ctx = ctx def __del__(self): if self.ctx.ref() is not None: Z3_del_constructor_list(self.ctx.ref(), self.c) def CreateDatatypes(*ds): """Create mutually recursive Z3 datatypes using 1 or more Datatype helper objects. In the following example we define a Tree-List using two mutually recursive datatypes. >>> TreeList = Datatype('TreeList') >>> Tree = Datatype('Tree') >>> # Tree has two constructors: leaf and node >>> Tree.declare('leaf', ('val', IntSort())) >>> # a node contains a list of trees >>> Tree.declare('node', ('children', TreeList)) >>> TreeList.declare('nil') >>> TreeList.declare('cons', ('car', Tree), ('cdr', TreeList)) >>> Tree, TreeList = CreateDatatypes(Tree, TreeList) >>> Tree.val(Tree.leaf(10)) val(leaf(10)) >>> simplify(Tree.val(Tree.leaf(10))) 10 >>> n1 = Tree.node(TreeList.cons(Tree.leaf(10), TreeList.cons(Tree.leaf(20), TreeList.nil))) >>> n1 node(cons(leaf(10), cons(leaf(20), nil))) >>> n2 = Tree.node(TreeList.cons(n1, TreeList.nil)) >>> simplify(n2 == n1) False >>> simplify(TreeList.car(Tree.children(n2)) == n1) True """ ds = _get_args(ds) if z3_debug(): _z3_assert(len(ds) > 0, "At least one Datatype must be specified") _z3_assert(all([isinstance(d, Datatype) for d in ds]), "Arguments must be Datatypes") _z3_assert(all([d.ctx == ds[0].ctx for d in ds]), "Context mismatch") _z3_assert(all([d.constructors != [] for d in ds]), "Non-empty Datatypes expected") ctx = ds[0].ctx num = len(ds) names = (Symbol * num)() out = (Sort * num)() clists = (ConstructorList * num)() to_delete = [] for i in range(num): d = ds[i] names[i] = to_symbol(d.name, ctx) num_cs = len(d.constructors) cs = (Constructor * num_cs)() for j in range(num_cs): c = d.constructors[j] cname = to_symbol(c[0], ctx) rname = to_symbol(c[1], ctx) fs = c[2] num_fs = len(fs) fnames = (Symbol * num_fs)() sorts = (Sort * num_fs)() refs = (ctypes.c_uint * num_fs)() for k in range(num_fs): fname = fs[k][0] ftype = fs[k][1] fnames[k] = to_symbol(fname, ctx) if isinstance(ftype, Datatype): if z3_debug(): _z3_assert(ds.count(ftype) == 1, "One and only one occurrence of each datatype is expected") sorts[k] = None refs[k] = ds.index(ftype) else: if z3_debug(): _z3_assert(is_sort(ftype), "Z3 sort expected") sorts[k] = ftype.ast refs[k] = 0 cs[j] = Z3_mk_constructor(ctx.ref(), cname, rname, num_fs, fnames, sorts, refs) to_delete.append(ScopedConstructor(cs[j], ctx)) clists[i] = Z3_mk_constructor_list(ctx.ref(), num_cs, cs) to_delete.append(ScopedConstructorList(clists[i], ctx)) Z3_mk_datatypes(ctx.ref(), num, names, out, clists) result = [] ## Create a field for every constructor, recognizer and accessor for i in range(num): dref = DatatypeSortRef(out[i], ctx) num_cs = dref.num_constructors() for j in range(num_cs): cref = dref.constructor(j) cref_name = cref.name() cref_arity = cref.arity() if cref.arity() == 0: cref = cref() setattr(dref, cref_name, cref) rref = dref.recognizer(j) setattr(dref, "is_" + cref_name, rref) for k in range(cref_arity): aref = dref.accessor(j, k) setattr(dref, aref.name(), aref) result.append(dref) return tuple(result) class DatatypeSortRef(SortRef): """Datatype sorts.""" def num_constructors(self): """Return the number of constructors in the given Z3 datatype. >>> List = Datatype('List') >>> List.declare('cons', ('car', IntSort()), ('cdr', List)) >>> List.declare('nil') >>> List = List.create() >>> # List is now a Z3 declaration >>> List.num_constructors() 2 """ return int(Z3_get_datatype_sort_num_constructors(self.ctx_ref(), self.ast)) def constructor(self, idx): """Return a constructor of the datatype `self`. >>> List = Datatype('List') >>> List.declare('cons', ('car', IntSort()), ('cdr', List)) >>> List.declare('nil') >>> List = List.create() >>> # List is now a Z3 declaration >>> List.num_constructors() 2 >>> List.constructor(0) cons >>> List.constructor(1) nil """ if z3_debug(): _z3_assert(idx < self.num_constructors(), "Invalid constructor index") return FuncDeclRef(Z3_get_datatype_sort_constructor(self.ctx_ref(), self.ast, idx), self.ctx) def recognizer(self, idx): """In Z3, each constructor has an associated recognizer predicate. If the constructor is named `name`, then the recognizer `is_name`. >>> List = Datatype('List') >>> List.declare('cons', ('car', IntSort()), ('cdr', List)) >>> List.declare('nil') >>> List = List.create() >>> # List is now a Z3 declaration >>> List.num_constructors() 2 >>> List.recognizer(0) is(cons) >>> List.recognizer(1) is(nil) >>> simplify(List.is_nil(List.cons(10, List.nil))) False >>> simplify(List.is_cons(List.cons(10, List.nil))) True >>> l = Const('l', List) >>> simplify(List.is_cons(l)) is(cons, l) """ if z3_debug(): _z3_assert(idx < self.num_constructors(), "Invalid recognizer index") return FuncDeclRef(Z3_get_datatype_sort_recognizer(self.ctx_ref(), self.ast, idx), self.ctx) def accessor(self, i, j): """In Z3, each constructor has 0 or more accessor. The number of accessors is equal to the arity of the constructor. >>> List = Datatype('List') >>> List.declare('cons', ('car', IntSort()), ('cdr', List)) >>> List.declare('nil') >>> List = List.create() >>> List.num_constructors() 2 >>> List.constructor(0) cons >>> num_accs = List.constructor(0).arity() >>> num_accs 2 >>> List.accessor(0, 0) car >>> List.accessor(0, 1) cdr >>> List.constructor(1) nil >>> num_accs = List.constructor(1).arity() >>> num_accs 0 """ if z3_debug(): _z3_assert(i < self.num_constructors(), "Invalid constructor index") _z3_assert(j < self.constructor(i).arity(), "Invalid accessor index") return FuncDeclRef(Z3_get_datatype_sort_constructor_accessor(self.ctx_ref(), self.ast, i, j), self.ctx) class DatatypeRef(ExprRef): """Datatype expressions.""" def sort(self): """Return the datatype sort of the datatype expression `self`.""" return DatatypeSortRef(Z3_get_sort(self.ctx_ref(), self.as_ast()), self.ctx) def TupleSort(name, sorts, ctx = None): """Create a named tuple sort base on a set of underlying sorts Example: >>> pair, mk_pair, (first, second) = TupleSort("pair", [IntSort(), StringSort()]) """ tuple = Datatype(name, ctx) projects = [ ('project%d' % i, sorts[i]) for i in range(len(sorts)) ] tuple.declare(name, *projects) tuple = tuple.create() return tuple, tuple.constructor(0), [tuple.accessor(0, i) for i in range(len(sorts))] def DisjointSum(name, sorts, ctx=None): """Create a named tagged union sort base on a set of underlying sorts Example: >>> sum, ((inject0, extract0), (inject1, extract1)) = DisjointSum("+", [IntSort(), StringSort()]) """ sum = Datatype(name, ctx) for i in range(len(sorts)): sum.declare("inject%d" % i, ("project%d" % i, sorts[i])) sum = sum.create() return sum, [(sum.constructor(i), sum.accessor(i, 0)) for i in range(len(sorts))] def EnumSort(name, values, ctx=None): """Return a new enumeration sort named `name` containing the given values. The result is a pair (sort, list of constants). Example: >>> Color, (red, green, blue) = EnumSort('Color', ['red', 'green', 'blue']) """ if z3_debug(): _z3_assert(isinstance(name, str), "Name must be a string") _z3_assert(all([isinstance(v, str) for v in values]), "Eumeration sort values must be strings") _z3_assert(len(values) > 0, "At least one value expected") ctx = _get_ctx(ctx) num = len(values) _val_names = (Symbol * num)() for i in range(num): _val_names[i] = to_symbol(values[i]) _values = (FuncDecl * num)() _testers = (FuncDecl * num)() name = to_symbol(name) S = DatatypeSortRef(Z3_mk_enumeration_sort(ctx.ref(), name, num, _val_names, _values, _testers), ctx) V = [] for i in range(num): V.append(FuncDeclRef(_values[i], ctx)) V = [a() for a in V] return S, V ######################################### # # Parameter Sets # ######################################### class ParamsRef: """Set of parameters used to configure Solvers, Tactics and Simplifiers in Z3. Consider using the function `args2params` to create instances of this object. """ def __init__(self, ctx=None, params=None): self.ctx = _get_ctx(ctx) if params is None: self.params = Z3_mk_params(self.ctx.ref()) else: self.params = params Z3_params_inc_ref(self.ctx.ref(), self.params) def __deepcopy__(self, memo={}): return ParamsRef(self.ctx, self.params) def __del__(self): if self.ctx.ref() is not None: Z3_params_dec_ref(self.ctx.ref(), self.params) def set(self, name, val): """Set parameter name with value val.""" if z3_debug(): _z3_assert(isinstance(name, str), "parameter name must be a string") name_sym = to_symbol(name, self.ctx) if isinstance(val, bool): Z3_params_set_bool(self.ctx.ref(), self.params, name_sym, val) elif _is_int(val): Z3_params_set_uint(self.ctx.ref(), self.params, name_sym, val) elif isinstance(val, float): Z3_params_set_double(self.ctx.ref(), self.params, name_sym, val) elif isinstance(val, str): Z3_params_set_symbol(self.ctx.ref(), self.params, name_sym, to_symbol(val, self.ctx)) else: if z3_debug(): _z3_assert(False, "invalid parameter value") def __repr__(self): return Z3_params_to_string(self.ctx.ref(), self.params) def validate(self, ds): _z3_assert(isinstance(ds, ParamDescrsRef), "parameter description set expected") Z3_params_validate(self.ctx.ref(), self.params, ds.descr) def args2params(arguments, keywords, ctx=None): """Convert python arguments into a Z3_params object. A ':' is added to the keywords, and '_' is replaced with '-' >>> args2params(['model', True, 'relevancy', 2], {'elim_and' : True}) (params model true relevancy 2 elim_and true) """ if z3_debug(): _z3_assert(len(arguments) % 2 == 0, "Argument list must have an even number of elements.") prev = None r = ParamsRef(ctx) for a in arguments: if prev is None: prev = a else: r.set(prev, a) prev = None for k in keywords: v = keywords[k] r.set(k, v) return r class ParamDescrsRef: """Set of parameter descriptions for Solvers, Tactics and Simplifiers in Z3. """ def __init__(self, descr, ctx=None): _z3_assert(isinstance(descr, ParamDescrs), "parameter description object expected") self.ctx = _get_ctx(ctx) self.descr = descr Z3_param_descrs_inc_ref(self.ctx.ref(), self.descr) def __deepcopy__(self, memo={}): return ParamsDescrsRef(self.descr, self.ctx) def __del__(self): if self.ctx.ref() is not None: Z3_param_descrs_dec_ref(self.ctx.ref(), self.descr) def size(self): """Return the size of in the parameter description `self`. """ return int(Z3_param_descrs_size(self.ctx.ref(), self.descr)) def __len__(self): """Return the size of in the parameter description `self`. """ return self.size() def get_name(self, i): """Return the i-th parameter name in the parameter description `self`. """ return _symbol2py(self.ctx, Z3_param_descrs_get_name(self.ctx.ref(), self.descr, i)) def get_kind(self, n): """Return the kind of the parameter named `n`. """ return Z3_param_descrs_get_kind(self.ctx.ref(), self.descr, to_symbol(n, self.ctx)) def get_documentation(self, n): """Return the documentation string of the parameter named `n`. """ return Z3_param_descrs_get_documentation(self.ctx.ref(), self.descr, to_symbol(n, self.ctx)) def __getitem__(self, arg): if _is_int(arg): return self.get_name(arg) else: return self.get_kind(arg) def __repr__(self): return Z3_param_descrs_to_string(self.ctx.ref(), self.descr) ######################################### # # Goals # ######################################### class Goal(Z3PPObject): """Goal is a collection of constraints we want to find a solution or show to be unsatisfiable (infeasible). Goals are processed using Tactics. A Tactic transforms a goal into a set of subgoals. A goal has a solution if one of its subgoals has a solution. A goal is unsatisfiable if all subgoals are unsatisfiable. """ def __init__(self, models=True, unsat_cores=False, proofs=False, ctx=None, goal=None): if z3_debug(): _z3_assert(goal is None or ctx is not None, "If goal is different from None, then ctx must be also different from None") self.ctx = _get_ctx(ctx) self.goal = goal if self.goal is None: self.goal = Z3_mk_goal(self.ctx.ref(), models, unsat_cores, proofs) Z3_goal_inc_ref(self.ctx.ref(), self.goal) def __deepcopy__(self, memo={}): return Goal(False, False, False, self.ctx, self.goal) def __del__(self): if self.goal is not None and self.ctx.ref() is not None: Z3_goal_dec_ref(self.ctx.ref(), self.goal) def depth(self): """Return the depth of the goal `self`. The depth corresponds to the number of tactics applied to `self`. >>> x, y = Ints('x y') >>> g = Goal() >>> g.add(x == 0, y >= x + 1) >>> g.depth() 0 >>> r = Then('simplify', 'solve-eqs')(g) >>> # r has 1 subgoal >>> len(r) 1 >>> r[0].depth() 2 """ return int(Z3_goal_depth(self.ctx.ref(), self.goal)) def inconsistent(self): """Return `True` if `self` contains the `False` constraints. >>> x, y = Ints('x y') >>> g = Goal() >>> g.inconsistent() False >>> g.add(x == 0, x == 1) >>> g [x == 0, x == 1] >>> g.inconsistent() False >>> g2 = Tactic('propagate-values')(g)[0] >>> g2.inconsistent() True """ return Z3_goal_inconsistent(self.ctx.ref(), self.goal) def prec(self): """Return the precision (under-approximation, over-approximation, or precise) of the goal `self`. >>> g = Goal() >>> g.prec() == Z3_GOAL_PRECISE True >>> x, y = Ints('x y') >>> g.add(x == y + 1) >>> g.prec() == Z3_GOAL_PRECISE True >>> t = With(Tactic('add-bounds'), add_bound_lower=0, add_bound_upper=10) >>> g2 = t(g)[0] >>> g2 [x == y + 1, x <= 10, x >= 0, y <= 10, y >= 0] >>> g2.prec() == Z3_GOAL_PRECISE False >>> g2.prec() == Z3_GOAL_UNDER True """ return Z3_goal_precision(self.ctx.ref(), self.goal) def precision(self): """Alias for `prec()`. >>> g = Goal() >>> g.precision() == Z3_GOAL_PRECISE True """ return self.prec() def size(self): """Return the number of constraints in the goal `self`. >>> g = Goal() >>> g.size() 0 >>> x, y = Ints('x y') >>> g.add(x == 0, y > x) >>> g.size() 2 """ return int(Z3_goal_size(self.ctx.ref(), self.goal)) def __len__(self): """Return the number of constraints in the goal `self`. >>> g = Goal() >>> len(g) 0 >>> x, y = Ints('x y') >>> g.add(x == 0, y > x) >>> len(g) 2 """ return self.size() def get(self, i): """Return a constraint in the goal `self`. >>> g = Goal() >>> x, y = Ints('x y') >>> g.add(x == 0, y > x) >>> g.get(0) x == 0 >>> g.get(1) y > x """ return _to_expr_ref(Z3_goal_formula(self.ctx.ref(), self.goal, i), self.ctx) def __getitem__(self, arg): """Return a constraint in the goal `self`. >>> g = Goal() >>> x, y = Ints('x y') >>> g.add(x == 0, y > x) >>> g[0] x == 0 >>> g[1] y > x """ if arg >= len(self): raise IndexError return self.get(arg) def assert_exprs(self, *args): """Assert constraints into the goal. >>> x = Int('x') >>> g = Goal() >>> g.assert_exprs(x > 0, x < 2) >>> g [x > 0, x < 2] """ args = _get_args(args) s = BoolSort(self.ctx) for arg in args: arg = s.cast(arg) Z3_goal_assert(self.ctx.ref(), self.goal, arg.as_ast()) def append(self, *args): """Add constraints. >>> x = Int('x') >>> g = Goal() >>> g.append(x > 0, x < 2) >>> g [x > 0, x < 2] """ self.assert_exprs(*args) def insert(self, *args): """Add constraints. >>> x = Int('x') >>> g = Goal() >>> g.insert(x > 0, x < 2) >>> g [x > 0, x < 2] """ self.assert_exprs(*args) def add(self, *args): """Add constraints. >>> x = Int('x') >>> g = Goal() >>> g.add(x > 0, x < 2) >>> g [x > 0, x < 2] """ self.assert_exprs(*args) def convert_model(self, model): """Retrieve model from a satisfiable goal >>> a, b = Ints('a b') >>> g = Goal() >>> g.add(Or(a == 0, a == 1), Or(b == 0, b == 1), a > b) >>> t = Then(Tactic('split-clause'), Tactic('solve-eqs')) >>> r = t(g) >>> r[0] [Or(b == 0, b == 1), Not(0 <= b)] >>> r[1] [Or(b == 0, b == 1), Not(1 <= b)] >>> # Remark: the subgoal r[0] is unsatisfiable >>> # Creating a solver for solving the second subgoal >>> s = Solver() >>> s.add(r[1]) >>> s.check() sat >>> s.model() [b = 0] >>> # Model s.model() does not assign a value to `a` >>> # It is a model for subgoal `r[1]`, but not for goal `g` >>> # The method convert_model creates a model for `g` from a model for `r[1]`. >>> r[1].convert_model(s.model()) [b = 0, a = 1] """ if z3_debug(): _z3_assert(isinstance(model, ModelRef), "Z3 Model expected") return ModelRef(Z3_goal_convert_model(self.ctx.ref(), self.goal, model.model), self.ctx) def __repr__(self): return obj_to_string(self) def sexpr(self): """Return a textual representation of the s-expression representing the goal.""" return Z3_goal_to_string(self.ctx.ref(), self.goal) def dimacs(self): """Return a textual representation of the goal in DIMACS format.""" return Z3_goal_to_dimacs_string(self.ctx.ref(), self.goal) def translate(self, target): """Copy goal `self` to context `target`. >>> x = Int('x') >>> g = Goal() >>> g.add(x > 10) >>> g [x > 10] >>> c2 = Context() >>> g2 = g.translate(c2) >>> g2 [x > 10] >>> g.ctx == main_ctx() True >>> g2.ctx == c2 True >>> g2.ctx == main_ctx() False """ if z3_debug(): _z3_assert(isinstance(target, Context), "target must be a context") return Goal(goal=Z3_goal_translate(self.ctx.ref(), self.goal, target.ref()), ctx=target) def __copy__(self): return self.translate(self.ctx) def __deepcopy__(self, memo={}): return self.translate(self.ctx) def simplify(self, *arguments, **keywords): """Return a new simplified goal. This method is essentially invoking the simplify tactic. >>> g = Goal() >>> x = Int('x') >>> g.add(x + 1 >= 2) >>> g [x + 1 >= 2] >>> g2 = g.simplify() >>> g2 [x >= 1] >>> # g was not modified >>> g [x + 1 >= 2] """ t = Tactic('simplify') return t.apply(self, *arguments, **keywords)[0] def as_expr(self): """Return goal `self` as a single Z3 expression. >>> x = Int('x') >>> g = Goal() >>> g.as_expr() True >>> g.add(x > 1) >>> g.as_expr() x > 1 >>> g.add(x < 10) >>> g.as_expr() And(x > 1, x < 10) """ sz = len(self) if sz == 0: return BoolVal(True, self.ctx) elif sz == 1: return self.get(0) else: return And([ self.get(i) for i in range(len(self)) ], self.ctx) ######################################### # # AST Vector # ######################################### class AstVector(Z3PPObject): """A collection (vector) of ASTs.""" def __init__(self, v=None, ctx=None): self.vector = None if v is None: self.ctx = _get_ctx(ctx) self.vector = Z3_mk_ast_vector(self.ctx.ref()) else: self.vector = v assert ctx is not None self.ctx = ctx Z3_ast_vector_inc_ref(self.ctx.ref(), self.vector) def __deepcopy__(self, memo={}): return AstVector(self.vector, self.ctx) def __del__(self): if self.vector is not None and self.ctx.ref() is not None: Z3_ast_vector_dec_ref(self.ctx.ref(), self.vector) def __len__(self): """Return the size of the vector `self`. >>> A = AstVector() >>> len(A) 0 >>> A.push(Int('x')) >>> A.push(Int('x')) >>> len(A) 2 """ return int(Z3_ast_vector_size(self.ctx.ref(), self.vector)) def __getitem__(self, i): """Return the AST at position `i`. >>> A = AstVector() >>> A.push(Int('x') + 1) >>> A.push(Int('y')) >>> A[0] x + 1 >>> A[1] y """ if isinstance(i, int): if i < 0: i += self.__len__() if i >= self.__len__(): raise IndexError return _to_ast_ref(Z3_ast_vector_get(self.ctx.ref(), self.vector, i), self.ctx) elif isinstance(i, slice): return [_to_ast_ref(Z3_ast_vector_get(self.ctx.ref(), self.vector, ii), self.ctx) for ii in range(*i.indices(self.__len__()))] def __setitem__(self, i, v): """Update AST at position `i`. >>> A = AstVector() >>> A.push(Int('x') + 1) >>> A.push(Int('y')) >>> A[0] x + 1 >>> A[0] = Int('x') >>> A[0] x """ if i >= self.__len__(): raise IndexError Z3_ast_vector_set(self.ctx.ref(), self.vector, i, v.as_ast()) def push(self, v): """Add `v` in the end of the vector. >>> A = AstVector() >>> len(A) 0 >>> A.push(Int('x')) >>> len(A) 1 """ Z3_ast_vector_push(self.ctx.ref(), self.vector, v.as_ast()) def resize(self, sz): """Resize the vector to `sz` elements. >>> A = AstVector() >>> A.resize(10) >>> len(A) 10 >>> for i in range(10): A[i] = Int('x') >>> A[5] x """ Z3_ast_vector_resize(self.ctx.ref(), self.vector, sz) def __contains__(self, item): """Return `True` if the vector contains `item`. >>> x = Int('x') >>> A = AstVector() >>> x in A False >>> A.push(x) >>> x in A True >>> (x+1) in A False >>> A.push(x+1) >>> (x+1) in A True >>> A [x, x + 1] """ for elem in self: if elem.eq(item): return True return False def translate(self, other_ctx): """Copy vector `self` to context `other_ctx`. >>> x = Int('x') >>> A = AstVector() >>> A.push(x) >>> c2 = Context() >>> B = A.translate(c2) >>> B [x] """ return AstVector(Z3_ast_vector_translate(self.ctx.ref(), self.vector, other_ctx.ref()), other_ctx) def __copy__(self): return self.translate(self.ctx) def __deepcopy__(self, memo={}): return self.translate(self.ctx) def __repr__(self): return obj_to_string(self) def sexpr(self): """Return a textual representation of the s-expression representing the vector.""" return Z3_ast_vector_to_string(self.ctx.ref(), self.vector) ######################################### # # AST Map # ######################################### class AstMap: """A mapping from ASTs to ASTs.""" def __init__(self, m=None, ctx=None): self.map = None if m is None: self.ctx = _get_ctx(ctx) self.map = Z3_mk_ast_map(self.ctx.ref()) else: self.map = m assert ctx is not None self.ctx = ctx Z3_ast_map_inc_ref(self.ctx.ref(), self.map) def __deepcopy__(self, memo={}): return AstMap(self.map, self.ctx) def __del__(self): if self.map is not None and self.ctx.ref() is not None: Z3_ast_map_dec_ref(self.ctx.ref(), self.map) def __len__(self): """Return the size of the map. >>> M = AstMap() >>> len(M) 0 >>> x = Int('x') >>> M[x] = IntVal(1) >>> len(M) 1 """ return int(Z3_ast_map_size(self.ctx.ref(), self.map)) def __contains__(self, key): """Return `True` if the map contains key `key`. >>> M = AstMap() >>> x = Int('x') >>> M[x] = x + 1 >>> x in M True >>> x+1 in M False """ return Z3_ast_map_contains(self.ctx.ref(), self.map, key.as_ast()) def __getitem__(self, key): """Retrieve the value associated with key `key`. >>> M = AstMap() >>> x = Int('x') >>> M[x] = x + 1 >>> M[x] x + 1 """ return _to_ast_ref(Z3_ast_map_find(self.ctx.ref(), self.map, key.as_ast()), self.ctx) def __setitem__(self, k, v): """Add/Update key `k` with value `v`. >>> M = AstMap() >>> x = Int('x') >>> M[x] = x + 1 >>> len(M) 1 >>> M[x] x + 1 >>> M[x] = IntVal(1) >>> M[x] 1 """ Z3_ast_map_insert(self.ctx.ref(), self.map, k.as_ast(), v.as_ast()) def __repr__(self): return Z3_ast_map_to_string(self.ctx.ref(), self.map) def erase(self, k): """Remove the entry associated with key `k`. >>> M = AstMap() >>> x = Int('x') >>> M[x] = x + 1 >>> len(M) 1 >>> M.erase(x) >>> len(M) 0 """ Z3_ast_map_erase(self.ctx.ref(), self.map, k.as_ast()) def reset(self): """Remove all entries from the map. >>> M = AstMap() >>> x = Int('x') >>> M[x] = x + 1 >>> M[x+x] = IntVal(1) >>> len(M) 2 >>> M.reset() >>> len(M) 0 """ Z3_ast_map_reset(self.ctx.ref(), self.map) def keys(self): """Return an AstVector containing all keys in the map. >>> M = AstMap() >>> x = Int('x') >>> M[x] = x + 1 >>> M[x+x] = IntVal(1) >>> M.keys() [x, x + x] """ return AstVector(Z3_ast_map_keys(self.ctx.ref(), self.map), self.ctx) ######################################### # # Model # ######################################### class FuncEntry: """Store the value of the interpretation of a function in a particular point.""" def __init__(self, entry, ctx): self.entry = entry self.ctx = ctx Z3_func_entry_inc_ref(self.ctx.ref(), self.entry) def __deepcopy__(self, memo={}): return FuncEntry(self.entry, self.ctx) def __del__(self): if self.ctx.ref() is not None: Z3_func_entry_dec_ref(self.ctx.ref(), self.entry) def num_args(self): """Return the number of arguments in the given entry. >>> f = Function('f', IntSort(), IntSort(), IntSort()) >>> s = Solver() >>> s.add(f(0, 1) == 10, f(1, 2) == 20, f(1, 0) == 10) >>> s.check() sat >>> m = s.model() >>> f_i = m[f] >>> f_i.num_entries() 1 >>> e = f_i.entry(0) >>> e.num_args() 2 """ return int(Z3_func_entry_get_num_args(self.ctx.ref(), self.entry)) def arg_value(self, idx): """Return the value of argument `idx`. >>> f = Function('f', IntSort(), IntSort(), IntSort()) >>> s = Solver() >>> s.add(f(0, 1) == 10, f(1, 2) == 20, f(1, 0) == 10) >>> s.check() sat >>> m = s.model() >>> f_i = m[f] >>> f_i.num_entries() 1 >>> e = f_i.entry(0) >>> e [1, 2, 20] >>> e.num_args() 2 >>> e.arg_value(0) 1 >>> e.arg_value(1) 2 >>> try: ... e.arg_value(2) ... except IndexError: ... print("index error") index error """ if idx >= self.num_args(): raise IndexError return _to_expr_ref(Z3_func_entry_get_arg(self.ctx.ref(), self.entry, idx), self.ctx) def value(self): """Return the value of the function at point `self`. >>> f = Function('f', IntSort(), IntSort(), IntSort()) >>> s = Solver() >>> s.add(f(0, 1) == 10, f(1, 2) == 20, f(1, 0) == 10) >>> s.check() sat >>> m = s.model() >>> f_i = m[f] >>> f_i.num_entries() 1 >>> e = f_i.entry(0) >>> e [1, 2, 20] >>> e.num_args() 2 >>> e.value() 20 """ return _to_expr_ref(Z3_func_entry_get_value(self.ctx.ref(), self.entry), self.ctx) def as_list(self): """Return entry `self` as a Python list. >>> f = Function('f', IntSort(), IntSort(), IntSort()) >>> s = Solver() >>> s.add(f(0, 1) == 10, f(1, 2) == 20, f(1, 0) == 10) >>> s.check() sat >>> m = s.model() >>> f_i = m[f] >>> f_i.num_entries() 1 >>> e = f_i.entry(0) >>> e.as_list() [1, 2, 20] """ args = [ self.arg_value(i) for i in range(self.num_args())] args.append(self.value()) return args def __repr__(self): return repr(self.as_list()) class FuncInterp(Z3PPObject): """Stores the interpretation of a function in a Z3 model.""" def __init__(self, f, ctx): self.f = f self.ctx = ctx if self.f is not None: Z3_func_interp_inc_ref(self.ctx.ref(), self.f) def __deepcopy__(self, memo={}): return FuncInterp(self.f, self.ctx) def __del__(self): if self.f is not None and self.ctx.ref() is not None: Z3_func_interp_dec_ref(self.ctx.ref(), self.f) def else_value(self): """ Return the `else` value for a function interpretation. Return None if Z3 did not specify the `else` value for this object. >>> f = Function('f', IntSort(), IntSort()) >>> s = Solver() >>> s.add(f(0) == 1, f(1) == 1, f(2) == 0) >>> s.check() sat >>> m = s.model() >>> m[f] [2 -> 0, else -> 1] >>> m[f].else_value() 1 """ r = Z3_func_interp_get_else(self.ctx.ref(), self.f) if r: return _to_expr_ref(r, self.ctx) else: return None def num_entries(self): """Return the number of entries/points in the function interpretation `self`. >>> f = Function('f', IntSort(), IntSort()) >>> s = Solver() >>> s.add(f(0) == 1, f(1) == 1, f(2) == 0) >>> s.check() sat >>> m = s.model() >>> m[f] [2 -> 0, else -> 1] >>> m[f].num_entries() 1 """ return int(Z3_func_interp_get_num_entries(self.ctx.ref(), self.f)) def arity(self): """Return the number of arguments for each entry in the function interpretation `self`. >>> f = Function('f', IntSort(), IntSort()) >>> s = Solver() >>> s.add(f(0) == 1, f(1) == 1, f(2) == 0) >>> s.check() sat >>> m = s.model() >>> m[f].arity() 1 """ return int(Z3_func_interp_get_arity(self.ctx.ref(), self.f)) def entry(self, idx): """Return an entry at position `idx < self.num_entries()` in the function interpretation `self`. >>> f = Function('f', IntSort(), IntSort()) >>> s = Solver() >>> s.add(f(0) == 1, f(1) == 1, f(2) == 0) >>> s.check() sat >>> m = s.model() >>> m[f] [2 -> 0, else -> 1] >>> m[f].num_entries() 1 >>> m[f].entry(0) [2, 0] """ if idx >= self.num_entries(): raise IndexError return FuncEntry(Z3_func_interp_get_entry(self.ctx.ref(), self.f, idx), self.ctx) def translate(self, other_ctx): """Copy model 'self' to context 'other_ctx'. """ return ModelRef(Z3_model_translate(self.ctx.ref(), self.model, other_ctx.ref()), other_ctx) def __copy__(self): return self.translate(self.ctx) def __deepcopy__(self, memo={}): return self.translate(self.ctx) def as_list(self): """Return the function interpretation as a Python list. >>> f = Function('f', IntSort(), IntSort()) >>> s = Solver() >>> s.add(f(0) == 1, f(1) == 1, f(2) == 0) >>> s.check() sat >>> m = s.model() >>> m[f] [2 -> 0, else -> 1] >>> m[f].as_list() [[2, 0], 1] """ r = [ self.entry(i).as_list() for i in range(self.num_entries())] r.append(self.else_value()) return r def __repr__(self): return obj_to_string(self) class ModelRef(Z3PPObject): """Model/Solution of a satisfiability problem (aka system of constraints).""" def __init__(self, m, ctx): assert ctx is not None self.model = m self.ctx = ctx Z3_model_inc_ref(self.ctx.ref(), self.model) def __del__(self): if self.ctx.ref() is not None: Z3_model_dec_ref(self.ctx.ref(), self.model) def __repr__(self): return obj_to_string(self) def sexpr(self): """Return a textual representation of the s-expression representing the model.""" return Z3_model_to_string(self.ctx.ref(), self.model) def eval(self, t, model_completion=False): """Evaluate the expression `t` in the model `self`. If `model_completion` is enabled, then a default interpretation is automatically added for symbols that do not have an interpretation in the model `self`. >>> x = Int('x') >>> s = Solver() >>> s.add(x > 0, x < 2) >>> s.check() sat >>> m = s.model() >>> m.eval(x + 1) 2 >>> m.eval(x == 1) True >>> y = Int('y') >>> m.eval(y + x) 1 + y >>> m.eval(y) y >>> m.eval(y, model_completion=True) 0 >>> # Now, m contains an interpretation for y >>> m.eval(y + x) 1 """ r = (Ast * 1)() if Z3_model_eval(self.ctx.ref(), self.model, t.as_ast(), model_completion, r): return _to_expr_ref(r[0], self.ctx) raise Z3Exception("failed to evaluate expression in the model") def evaluate(self, t, model_completion=False): """Alias for `eval`. >>> x = Int('x') >>> s = Solver() >>> s.add(x > 0, x < 2) >>> s.check() sat >>> m = s.model() >>> m.evaluate(x + 1) 2 >>> m.evaluate(x == 1) True >>> y = Int('y') >>> m.evaluate(y + x) 1 + y >>> m.evaluate(y) y >>> m.evaluate(y, model_completion=True) 0 >>> # Now, m contains an interpretation for y >>> m.evaluate(y + x) 1 """ return self.eval(t, model_completion) def __len__(self): """Return the number of constant and function declarations in the model `self`. >>> f = Function('f', IntSort(), IntSort()) >>> x = Int('x') >>> s = Solver() >>> s.add(x > 0, f(x) != x) >>> s.check() sat >>> m = s.model() >>> len(m) 2 """ return int(Z3_model_get_num_consts(self.ctx.ref(), self.model)) + int(Z3_model_get_num_funcs(self.ctx.ref(), self.model)) def get_interp(self, decl): """Return the interpretation for a given declaration or constant. >>> f = Function('f', IntSort(), IntSort()) >>> x = Int('x') >>> s = Solver() >>> s.add(x > 0, x < 2, f(x) == 0) >>> s.check() sat >>> m = s.model() >>> m[x] 1 >>> m[f] [else -> 0] """ if z3_debug(): _z3_assert(isinstance(decl, FuncDeclRef) or is_const(decl), "Z3 declaration expected") if is_const(decl): decl = decl.decl() try: if decl.arity() == 0: _r = Z3_model_get_const_interp(self.ctx.ref(), self.model, decl.ast) if _r.value is None: return None r = _to_expr_ref(_r, self.ctx) if is_as_array(r): return self.get_interp(get_as_array_func(r)) else: return r else: return FuncInterp(Z3_model_get_func_interp(self.ctx.ref(), self.model, decl.ast), self.ctx) except Z3Exception: return None def num_sorts(self): """Return the number of uninterpreted sorts that contain an interpretation in the model `self`. >>> A = DeclareSort('A') >>> a, b = Consts('a b', A) >>> s = Solver() >>> s.add(a != b) >>> s.check() sat >>> m = s.model() >>> m.num_sorts() 1 """ return int(Z3_model_get_num_sorts(self.ctx.ref(), self.model)) def get_sort(self, idx): """Return the uninterpreted sort at position `idx` < self.num_sorts(). >>> A = DeclareSort('A') >>> B = DeclareSort('B') >>> a1, a2 = Consts('a1 a2', A) >>> b1, b2 = Consts('b1 b2', B) >>> s = Solver() >>> s.add(a1 != a2, b1 != b2) >>> s.check() sat >>> m = s.model() >>> m.num_sorts() 2 >>> m.get_sort(0) A >>> m.get_sort(1) B """ if idx >= self.num_sorts(): raise IndexError return _to_sort_ref(Z3_model_get_sort(self.ctx.ref(), self.model, idx), self.ctx) def sorts(self): """Return all uninterpreted sorts that have an interpretation in the model `self`. >>> A = DeclareSort('A') >>> B = DeclareSort('B') >>> a1, a2 = Consts('a1 a2', A) >>> b1, b2 = Consts('b1 b2', B) >>> s = Solver() >>> s.add(a1 != a2, b1 != b2) >>> s.check() sat >>> m = s.model() >>> m.sorts() [A, B] """ return [ self.get_sort(i) for i in range(self.num_sorts()) ] def get_universe(self, s): """Return the interpretation for the uninterpreted sort `s` in the model `self`. >>> A = DeclareSort('A') >>> a, b = Consts('a b', A) >>> s = Solver() >>> s.add(a != b) >>> s.check() sat >>> m = s.model() >>> m.get_universe(A) [A!val!0, A!val!1] """ if z3_debug(): _z3_assert(isinstance(s, SortRef), "Z3 sort expected") try: return AstVector(Z3_model_get_sort_universe(self.ctx.ref(), self.model, s.ast), self.ctx) except Z3Exception: return None def __getitem__(self, idx): """If `idx` is an integer, then the declaration at position `idx` in the model `self` is returned. If `idx` is a declaration, then the actual interpretation is returned. The elements can be retrieved using position or the actual declaration. >>> f = Function('f', IntSort(), IntSort()) >>> x = Int('x') >>> s = Solver() >>> s.add(x > 0, x < 2, f(x) == 0) >>> s.check() sat >>> m = s.model() >>> len(m) 2 >>> m[0] x >>> m[1] f >>> m[x] 1 >>> m[f] [else -> 0] >>> for d in m: print("%s -> %s" % (d, m[d])) x -> 1 f -> [else -> 0] """ if _is_int(idx): if idx >= len(self): raise IndexError num_consts = Z3_model_get_num_consts(self.ctx.ref(), self.model) if (idx < num_consts): return FuncDeclRef(Z3_model_get_const_decl(self.ctx.ref(), self.model, idx), self.ctx) else: return FuncDeclRef(Z3_model_get_func_decl(self.ctx.ref(), self.model, idx - num_consts), self.ctx) if isinstance(idx, FuncDeclRef): return self.get_interp(idx) if is_const(idx): return self.get_interp(idx.decl()) if isinstance(idx, SortRef): return self.get_universe(idx) if z3_debug(): _z3_assert(False, "Integer, Z3 declaration, or Z3 constant expected") return None def decls(self): """Return a list with all symbols that have an interpretation in the model `self`. >>> f = Function('f', IntSort(), IntSort()) >>> x = Int('x') >>> s = Solver() >>> s.add(x > 0, x < 2, f(x) == 0) >>> s.check() sat >>> m = s.model() >>> m.decls() [x, f] """ r = [] for i in range(Z3_model_get_num_consts(self.ctx.ref(), self.model)): r.append(FuncDeclRef(Z3_model_get_const_decl(self.ctx.ref(), self.model, i), self.ctx)) for i in range(Z3_model_get_num_funcs(self.ctx.ref(), self.model)): r.append(FuncDeclRef(Z3_model_get_func_decl(self.ctx.ref(), self.model, i), self.ctx)) return r def translate(self, target): """Translate `self` to the context `target`. That is, return a copy of `self` in the context `target`. """ if z3_debug(): _z3_assert(isinstance(target, Context), "argument must be a Z3 context") model = Z3_model_translate(self.ctx.ref(), self.model, target.ref()) return Model(model, target) def __copy__(self): return self.translate(self.ctx) def __deepcopy__(self, memo={}): return self.translate(self.ctx) def Model(ctx = None): ctx = _get_ctx(ctx) return ModelRef(Z3_mk_model(ctx.ref()), ctx) def is_as_array(n): """Return true if n is a Z3 expression of the form (_ as-array f).""" return isinstance(n, ExprRef) and Z3_is_as_array(n.ctx.ref(), n.as_ast()) def get_as_array_func(n): """Return the function declaration f associated with a Z3 expression of the form (_ as-array f).""" if z3_debug(): _z3_assert(is_as_array(n), "as-array Z3 expression expected.") return FuncDeclRef(Z3_get_as_array_func_decl(n.ctx.ref(), n.as_ast()), n.ctx) ######################################### # # Statistics # ######################################### class Statistics: """Statistics for `Solver.check()`.""" def __init__(self, stats, ctx): self.stats = stats self.ctx = ctx Z3_stats_inc_ref(self.ctx.ref(), self.stats) def __deepcopy__(self, memo={}): return Statistics(self.stats, self.ctx) def __del__(self): if self.ctx.ref() is not None: Z3_stats_dec_ref(self.ctx.ref(), self.stats) def __repr__(self): if in_html_mode(): out = io.StringIO() even = True out.write(u('')) for k, v in self: if even: out.write(u('')) even = False else: out.write(u('')) even = True out.write(u('' % (k, v))) out.write(u('
%s%s
')) return out.getvalue() else: return Z3_stats_to_string(self.ctx.ref(), self.stats) def __len__(self): """Return the number of statistical counters. >>> x = Int('x') >>> s = Then('simplify', 'nlsat').solver() >>> s.add(x > 0) >>> s.check() sat >>> st = s.statistics() >>> len(st) 6 """ return int(Z3_stats_size(self.ctx.ref(), self.stats)) def __getitem__(self, idx): """Return the value of statistical counter at position `idx`. The result is a pair (key, value). >>> x = Int('x') >>> s = Then('simplify', 'nlsat').solver() >>> s.add(x > 0) >>> s.check() sat >>> st = s.statistics() >>> len(st) 6 >>> st[0] ('nlsat propagations', 2) >>> st[1] ('nlsat stages', 2) """ if idx >= len(self): raise IndexError if Z3_stats_is_uint(self.ctx.ref(), self.stats, idx): val = int(Z3_stats_get_uint_value(self.ctx.ref(), self.stats, idx)) else: val = Z3_stats_get_double_value(self.ctx.ref(), self.stats, idx) return (Z3_stats_get_key(self.ctx.ref(), self.stats, idx), val) def keys(self): """Return the list of statistical counters. >>> x = Int('x') >>> s = Then('simplify', 'nlsat').solver() >>> s.add(x > 0) >>> s.check() sat >>> st = s.statistics() """ return [Z3_stats_get_key(self.ctx.ref(), self.stats, idx) for idx in range(len(self))] def get_key_value(self, key): """Return the value of a particular statistical counter. >>> x = Int('x') >>> s = Then('simplify', 'nlsat').solver() >>> s.add(x > 0) >>> s.check() sat >>> st = s.statistics() >>> st.get_key_value('nlsat propagations') 2 """ for idx in range(len(self)): if key == Z3_stats_get_key(self.ctx.ref(), self.stats, idx): if Z3_stats_is_uint(self.ctx.ref(), self.stats, idx): return int(Z3_stats_get_uint_value(self.ctx.ref(), self.stats, idx)) else: return Z3_stats_get_double_value(self.ctx.ref(), self.stats, idx) raise Z3Exception("unknown key") def __getattr__(self, name): """Access the value of statistical using attributes. Remark: to access a counter containing blank spaces (e.g., 'nlsat propagations'), we should use '_' (e.g., 'nlsat_propagations'). >>> x = Int('x') >>> s = Then('simplify', 'nlsat').solver() >>> s.add(x > 0) >>> s.check() sat >>> st = s.statistics() >>> st.nlsat_propagations 2 >>> st.nlsat_stages 2 """ key = name.replace('_', ' ') try: return self.get_key_value(key) except Z3Exception: raise AttributeError ######################################### # # Solver # ######################################### class CheckSatResult: """Represents the result of a satisfiability check: sat, unsat, unknown. >>> s = Solver() >>> s.check() sat >>> r = s.check() >>> isinstance(r, CheckSatResult) True """ def __init__(self, r): self.r = r def __deepcopy__(self, memo={}): return CheckSatResult(self.r) def __eq__(self, other): return isinstance(other, CheckSatResult) and self.r == other.r def __ne__(self, other): return not self.__eq__(other) def __repr__(self): if in_html_mode(): if self.r == Z3_L_TRUE: return "sat" elif self.r == Z3_L_FALSE: return "unsat" else: return "unknown" else: if self.r == Z3_L_TRUE: return "sat" elif self.r == Z3_L_FALSE: return "unsat" else: return "unknown" def _repr_html_(self): in_html = in_html_mode() set_html_mode(True) res = repr(self) set_html_mode(in_html) return res sat = CheckSatResult(Z3_L_TRUE) unsat = CheckSatResult(Z3_L_FALSE) unknown = CheckSatResult(Z3_L_UNDEF) class Solver(Z3PPObject): """Solver API provides methods for implementing the main SMT 2.0 commands: push, pop, check, get-model, etc.""" def __init__(self, solver=None, ctx=None, logFile=None): assert solver is None or ctx is not None self.ctx = _get_ctx(ctx) self.backtrack_level = 4000000000 self.solver = None if solver is None: self.solver = Z3_mk_solver(self.ctx.ref()) else: self.solver = solver Z3_solver_inc_ref(self.ctx.ref(), self.solver) if logFile is not None: self.set("solver.smtlib2_log", logFile) def __del__(self): if self.solver is not None and self.ctx.ref() is not None: Z3_solver_dec_ref(self.ctx.ref(), self.solver) def set(self, *args, **keys): """Set a configuration option. The method `help()` return a string containing all available options. >>> s = Solver() >>> # The option MBQI can be set using three different approaches. >>> s.set(mbqi=True) >>> s.set('MBQI', True) >>> s.set(':mbqi', True) """ p = args2params(args, keys, self.ctx) Z3_solver_set_params(self.ctx.ref(), self.solver, p.params) def push(self): """Create a backtracking point. >>> x = Int('x') >>> s = Solver() >>> s.add(x > 0) >>> s [x > 0] >>> s.push() >>> s.add(x < 1) >>> s [x > 0, x < 1] >>> s.check() unsat >>> s.pop() >>> s.check() sat >>> s [x > 0] """ Z3_solver_push(self.ctx.ref(), self.solver) def pop(self, num=1): """Backtrack \c num backtracking points. >>> x = Int('x') >>> s = Solver() >>> s.add(x > 0) >>> s [x > 0] >>> s.push() >>> s.add(x < 1) >>> s [x > 0, x < 1] >>> s.check() unsat >>> s.pop() >>> s.check() sat >>> s [x > 0] """ Z3_solver_pop(self.ctx.ref(), self.solver, num) def num_scopes(self): """Return the current number of backtracking points. >>> s = Solver() >>> s.num_scopes() 0L >>> s.push() >>> s.num_scopes() 1L >>> s.push() >>> s.num_scopes() 2L >>> s.pop() >>> s.num_scopes() 1L """ return Z3_solver_get_num_scopes(self.ctx.ref(), self.solver) def reset(self): """Remove all asserted constraints and backtracking points created using `push()`. >>> x = Int('x') >>> s = Solver() >>> s.add(x > 0) >>> s [x > 0] >>> s.reset() >>> s [] """ Z3_solver_reset(self.ctx.ref(), self.solver) def assert_exprs(self, *args): """Assert constraints into the solver. >>> x = Int('x') >>> s = Solver() >>> s.assert_exprs(x > 0, x < 2) >>> s [x > 0, x < 2] """ args = _get_args(args) s = BoolSort(self.ctx) for arg in args: if isinstance(arg, Goal) or isinstance(arg, AstVector): for f in arg: Z3_solver_assert(self.ctx.ref(), self.solver, f.as_ast()) else: arg = s.cast(arg) Z3_solver_assert(self.ctx.ref(), self.solver, arg.as_ast()) def add(self, *args): """Assert constraints into the solver. >>> x = Int('x') >>> s = Solver() >>> s.add(x > 0, x < 2) >>> s [x > 0, x < 2] """ self.assert_exprs(*args) def __iadd__(self, fml): self.add(fml) return self def append(self, *args): """Assert constraints into the solver. >>> x = Int('x') >>> s = Solver() >>> s.append(x > 0, x < 2) >>> s [x > 0, x < 2] """ self.assert_exprs(*args) def insert(self, *args): """Assert constraints into the solver. >>> x = Int('x') >>> s = Solver() >>> s.insert(x > 0, x < 2) >>> s [x > 0, x < 2] """ self.assert_exprs(*args) 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 = Solver() >>> s.set(unsat_core=True) >>> 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_solver_assert_and_track(self.ctx.ref(), self.solver, a.as_ast(), p.as_ast()) def check(self, *assumptions): """Check whether the assertions in the given solver plus the optional assumptions are consistent or not. >>> x = Int('x') >>> s = Solver() >>> s.check() sat >>> s.add(x > 0, x < 2) >>> s.check() sat >>> s.model().eval(x) 1 >>> s.add(x < 1) >>> s.check() unsat >>> s.reset() >>> s.add(2**x == 4) >>> s.check() unknown """ assumptions = _get_args(assumptions) num = len(assumptions) _assumptions = (Ast * num)() for i in range(num): _assumptions[i] = assumptions[i].as_ast() r = Z3_solver_check_assumptions(self.ctx.ref(), self.solver, num, _assumptions) return CheckSatResult(r) def model(self): """Return a model for the last `check()`. This function raises an exception if a model is not available (e.g., last `check()` returned unsat). >>> s = Solver() >>> a = Int('a') >>> s.add(a + 2 == 0) >>> s.check() sat >>> s.model() [a = -2] """ try: return ModelRef(Z3_solver_get_model(self.ctx.ref(), self.solver), self.ctx) except Z3Exception: raise Z3Exception("model is not available") def import_model_converter(self, other): """Import model converter from other into the current solver""" Z3_solver_import_model_converter(self.ctx.ref(), other.solver, self.solver) def unsat_core(self): """Return a subset (as an AST vector) of the assumptions provided to the last check(). These are the assumptions Z3 used in the unsatisfiability proof. Assumptions are available in Z3. They are used to extract unsatisfiable cores. They may be also used to "retract" assumptions. Note that, assumptions are not really "soft constraints", but they can be used to implement them. >>> p1, p2, p3 = Bools('p1 p2 p3') >>> x, y = Ints('x y') >>> s = Solver() >>> s.add(Implies(p1, x > 0)) >>> s.add(Implies(p2, y > x)) >>> s.add(Implies(p2, y < 1)) >>> s.add(Implies(p3, y > -3)) >>> s.check(p1, p2, p3) unsat >>> core = s.unsat_core() >>> len(core) 2 >>> p1 in core True >>> p2 in core True >>> p3 in core False >>> # "Retracting" p2 >>> s.check(p1, p3) sat """ return AstVector(Z3_solver_get_unsat_core(self.ctx.ref(), self.solver), self.ctx) def consequences(self, assumptions, variables): """Determine fixed values for the variables based on the solver state and assumptions. >>> s = Solver() >>> a, b, c, d = Bools('a b c d') >>> s.add(Implies(a,b), Implies(b, c)) >>> s.consequences([a],[b,c,d]) (sat, [Implies(a, b), Implies(a, c)]) >>> s.consequences([Not(c),d],[a,b,c,d]) (sat, [Implies(d, d), Implies(Not(c), Not(c)), Implies(Not(c), Not(b)), Implies(Not(c), Not(a))]) """ if isinstance(assumptions, list): _asms = AstVector(None, self.ctx) for a in assumptions: _asms.push(a) assumptions = _asms if isinstance(variables, list): _vars = AstVector(None, self.ctx) for a in variables: _vars.push(a) variables = _vars _z3_assert(isinstance(assumptions, AstVector), "ast vector expected") _z3_assert(isinstance(variables, AstVector), "ast vector expected") consequences = AstVector(None, self.ctx) r = Z3_solver_get_consequences(self.ctx.ref(), self.solver, assumptions.vector, variables.vector, consequences.vector) sz = len(consequences) consequences = [ consequences[i] for i in range(sz) ] return CheckSatResult(r), consequences def from_file(self, filename): """Parse assertions from a file""" Z3_solver_from_file(self.ctx.ref(), self.solver, filename) def from_string(self, s): """Parse assertions from a string""" Z3_solver_from_string(self.ctx.ref(), self.solver, s) def cube(self, vars = None): """Get set of cubes The method takes an optional set of variables that restrict which variables may be used as a starting point for cubing. If vars is not None, then the first case split is based on a variable in this set. """ self.cube_vs = AstVector(None, self.ctx) if vars is not None: for v in vars: self.cube_vs.push(v) while True: lvl = self.backtrack_level self.backtrack_level = 4000000000 r = AstVector(Z3_solver_cube(self.ctx.ref(), self.solver, self.cube_vs.vector, lvl), self.ctx) if (len(r) == 1 and is_false(r[0])): return yield r if (len(r) == 0): return def cube_vars(self): """Access the set of variables that were touched by the most recently generated cube. This set of variables can be used as a starting point for additional cubes. The idea is that variables that appear in clauses that are reduced by the most recent cube are likely more useful to cube on.""" return self.cube_vs def proof(self): """Return a proof for the last `check()`. Proof construction must be enabled.""" return _to_expr_ref(Z3_solver_get_proof(self.ctx.ref(), self.solver), self.ctx) def assertions(self): """Return an AST vector containing all added constraints. >>> s = Solver() >>> s.assertions() [] >>> a = Int('a') >>> s.add(a > 0) >>> s.add(a < 10) >>> s.assertions() [a > 0, a < 10] """ return AstVector(Z3_solver_get_assertions(self.ctx.ref(), self.solver), self.ctx) def units(self): """Return an AST vector containing all currently inferred units. """ return AstVector(Z3_solver_get_units(self.ctx.ref(), self.solver), self.ctx) def non_units(self): """Return an AST vector containing all atomic formulas in solver state that are not units. """ 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 statistics(self): """Return statistics for the last `check()`. >>> s = SimpleSolver() >>> x = Int('x') >>> s.add(x > 0) >>> s.check() sat >>> st = s.statistics() >>> st.get_key_value('final checks') 1 >>> len(st) > 0 True >>> st[0] != 0 True """ return Statistics(Z3_solver_get_statistics(self.ctx.ref(), self.solver), self.ctx) def reason_unknown(self): """Return a string describing why the last `check()` returned `unknown`. >>> x = Int('x') >>> s = SimpleSolver() >>> s.add(2**x == 4) >>> s.check() unknown >>> s.reason_unknown() '(incomplete (theory arithmetic))' """ return Z3_solver_get_reason_unknown(self.ctx.ref(), self.solver) def help(self): """Display a string describing all available options.""" print(Z3_solver_get_help(self.ctx.ref(), self.solver)) def param_descrs(self): """Return the parameter description set.""" return ParamDescrsRef(Z3_solver_get_param_descrs(self.ctx.ref(), self.solver), self.ctx) def __repr__(self): """Return a formatted string with all added constraints.""" return obj_to_string(self) def translate(self, target): """Translate `self` to the context `target`. That is, return a copy of `self` in the context `target`. >>> c1 = Context() >>> c2 = Context() >>> s1 = Solver(ctx=c1) >>> s2 = s1.translate(c2) """ if z3_debug(): _z3_assert(isinstance(target, Context), "argument must be a Z3 context") solver = Z3_solver_translate(self.ctx.ref(), self.solver, target.ref()) return Solver(solver, target) def __copy__(self): return self.translate(self.ctx) def __deepcopy__(self, memo={}): return self.translate(self.ctx) def sexpr(self): """Return a formatted string (in Lisp-like format) with all added constraints. We say the string is in s-expression format. >>> x = Int('x') >>> s = Solver() >>> s.add(x > 0) >>> s.add(x < 2) >>> r = s.sexpr() """ 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() sz = len(es) sz1 = sz if sz1 > 0: sz1 -= 1 v = (Ast * sz1)() for i in range(sz1): v[i] = es[i].as_ast() if sz > 0: e = es[sz1].as_ast() else: e = BoolVal(True, self.ctx).as_ast() return Z3_benchmark_to_smtlib_string(self.ctx.ref(), "benchmark generated from python API", "", "unknown", "", sz1, v, e) def SolverFor(logic, ctx=None, logFile=None): """Create a solver customized for the given logic. The parameter `logic` is a string. It should be contains the name of a SMT-LIB logic. See http://www.smtlib.org/ for the name of all available logics. >>> s = SolverFor("QF_LIA") >>> x = Int('x') >>> s.add(x > 0) >>> s.add(x < 2) >>> s.check() sat >>> s.model() [x = 1] """ ctx = _get_ctx(ctx) logic = to_symbol(logic) return Solver(Z3_mk_solver_for_logic(ctx.ref(), logic), ctx, logFile) def SimpleSolver(ctx=None, logFile=None): """Return a simple general purpose solver with limited amount of preprocessing. >>> s = SimpleSolver() >>> x = Int('x') >>> s.add(x > 0) >>> s.check() sat """ ctx = _get_ctx(ctx) return Solver(Z3_mk_simple_solver(ctx.ref()), ctx, logFile) ######################################### # # Fixedpoint # ######################################### class Fixedpoint(Z3PPObject): """Fixedpoint API provides methods for solving with recursive predicates""" def __init__(self, fixedpoint=None, ctx=None): assert fixedpoint is None or ctx is not None self.ctx = _get_ctx(ctx) self.fixedpoint = None if fixedpoint is None: self.fixedpoint = Z3_mk_fixedpoint(self.ctx.ref()) else: self.fixedpoint = fixedpoint Z3_fixedpoint_inc_ref(self.ctx.ref(), self.fixedpoint) self.vars = [] def __deepcopy__(self, memo={}): return FixedPoint(self.fixedpoint, self.ctx) def __del__(self): if self.fixedpoint is not None and self.ctx.ref() is not None: Z3_fixedpoint_dec_ref(self.ctx.ref(), self.fixedpoint) def set(self, *args, **keys): """Set a configuration option. The method `help()` return a string containing all available options. """ p = args2params(args, keys, self.ctx) Z3_fixedpoint_set_params(self.ctx.ref(), self.fixedpoint, p.params) def help(self): """Display a string describing all available options.""" print(Z3_fixedpoint_get_help(self.ctx.ref(), self.fixedpoint)) def param_descrs(self): """Return the parameter description set.""" return ParamDescrsRef(Z3_fixedpoint_get_param_descrs(self.ctx.ref(), self.fixedpoint), self.ctx) def assert_exprs(self, *args): """Assert constraints as background axioms for the fixedpoint solver.""" args = _get_args(args) s = BoolSort(self.ctx) for arg in args: if isinstance(arg, Goal) or isinstance(arg, AstVector): for f in arg: f = self.abstract(f) Z3_fixedpoint_assert(self.ctx.ref(), self.fixedpoint, f.as_ast()) else: arg = s.cast(arg) arg = self.abstract(arg) Z3_fixedpoint_assert(self.ctx.ref(), self.fixedpoint, arg.as_ast()) def add(self, *args): """Assert constraints as background axioms for the fixedpoint solver. Alias for assert_expr.""" self.assert_exprs(*args) def __iadd__(self, fml): self.add(fml) return self def append(self, *args): """Assert constraints as background axioms for the fixedpoint solver. Alias for assert_expr.""" self.assert_exprs(*args) def insert(self, *args): """Assert constraints as background axioms for the fixedpoint solver. Alias for assert_expr.""" self.assert_exprs(*args) def add_rule(self, head, body = None, name = None): """Assert rules defining recursive predicates to the fixedpoint solver. >>> a = Bool('a') >>> b = Bool('b') >>> s = Fixedpoint() >>> s.register_relation(a.decl()) >>> s.register_relation(b.decl()) >>> s.fact(a) >>> s.rule(b, a) >>> s.query(b) sat """ if name is None: name = "" name = to_symbol(name, self.ctx) if body is None: head = self.abstract(head) Z3_fixedpoint_add_rule(self.ctx.ref(), self.fixedpoint, head.as_ast(), name) else: body = _get_args(body) f = self.abstract(Implies(And(body, self.ctx),head)) Z3_fixedpoint_add_rule(self.ctx.ref(), self.fixedpoint, f.as_ast(), name) def rule(self, head, body = None, name = None): """Assert rules defining recursive predicates to the fixedpoint solver. Alias for add_rule.""" self.add_rule(head, body, name) def fact(self, head, name = None): """Assert facts defining recursive predicates to the fixedpoint solver. Alias for add_rule.""" self.add_rule(head, None, name) def query(self, *query): """Query the fixedpoint engine whether formula is derivable. You can also pass an tuple or list of recursive predicates. """ query = _get_args(query) sz = len(query) if sz >= 1 and isinstance(query[0], FuncDeclRef): _decls = (FuncDecl * sz)() i = 0 for q in query: _decls[i] = q.ast i = i + 1 r = Z3_fixedpoint_query_relations(self.ctx.ref(), self.fixedpoint, sz, _decls) else: if sz == 1: query = query[0] else: query = And(query, self.ctx) query = self.abstract(query, False) r = Z3_fixedpoint_query(self.ctx.ref(), self.fixedpoint, query.as_ast()) return CheckSatResult(r) def query_from_lvl (self, lvl, *query): """Query the fixedpoint engine whether formula is derivable starting at the given query level. """ query = _get_args(query) sz = len(query) if sz >= 1 and isinstance(query[0], FuncDecl): _z3_assert (False, "unsupported") else: if sz == 1: query = query[0] else: query = And(query) query = self.abstract(query, False) r = Z3_fixedpoint_query_from_lvl (self.ctx.ref(), self.fixedpoint, query.as_ast(), lvl) return CheckSatResult(r) def update_rule(self, head, body, name): """update rule""" if name is None: name = "" name = to_symbol(name, self.ctx) body = _get_args(body) f = self.abstract(Implies(And(body, self.ctx),head)) Z3_fixedpoint_update_rule(self.ctx.ref(), self.fixedpoint, f.as_ast(), name) def get_answer(self): """Retrieve answer from last query call.""" r = Z3_fixedpoint_get_answer(self.ctx.ref(), self.fixedpoint) return _to_expr_ref(r, self.ctx) def get_ground_sat_answer(self): """Retrieve a ground cex from last query call.""" r = Z3_fixedpoint_get_ground_sat_answer(self.ctx.ref(), self.fixedpoint) return _to_expr_ref(r, self.ctx) def get_rules_along_trace(self): """retrieve rules along the counterexample trace""" return AstVector(Z3_fixedpoint_get_rules_along_trace(self.ctx.ref(), self.fixedpoint), self.ctx) def get_rule_names_along_trace(self): """retrieve rule names along the counterexample trace""" # this is a hack as I don't know how to return a list of symbols from C++; # obtain names as a single string separated by semicolons names = _symbol2py (self.ctx, Z3_fixedpoint_get_rule_names_along_trace(self.ctx.ref(), self.fixedpoint)) # split into individual names return names.split (';') def get_num_levels(self, predicate): """Retrieve number of levels used for predicate in PDR engine""" return Z3_fixedpoint_get_num_levels(self.ctx.ref(), self.fixedpoint, predicate.ast) def get_cover_delta(self, level, predicate): """Retrieve properties known about predicate for the level'th unfolding. -1 is treated as the limit (infinity)""" r = Z3_fixedpoint_get_cover_delta(self.ctx.ref(), self.fixedpoint, level, predicate.ast) return _to_expr_ref(r, self.ctx) def add_cover(self, level, predicate, property): """Add property to predicate for the level'th unfolding. -1 is treated as infinity (infinity)""" Z3_fixedpoint_add_cover(self.ctx.ref(), self.fixedpoint, level, predicate.ast, property.ast) def register_relation(self, *relations): """Register relation as recursive""" relations = _get_args(relations) for f in relations: Z3_fixedpoint_register_relation(self.ctx.ref(), self.fixedpoint, f.ast) def set_predicate_representation(self, f, *representations): """Control how relation is represented""" representations = _get_args(representations) representations = [to_symbol(s) for s in representations] sz = len(representations) args = (Symbol * sz)() for i in range(sz): args[i] = representations[i] Z3_fixedpoint_set_predicate_representation(self.ctx.ref(), self.fixedpoint, f.ast, sz, args) def parse_string(self, s): """Parse rules and queries from a string""" 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""" 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""" return AstVector(Z3_fixedpoint_get_rules(self.ctx.ref(), self.fixedpoint), self.ctx) def get_assertions(self): """retrieve assertions that have been added to fixedpoint context""" return AstVector(Z3_fixedpoint_get_assertions(self.ctx.ref(), self.fixedpoint), self.ctx) def __repr__(self): """Return a formatted string with all added rules and constraints.""" return self.sexpr() def sexpr(self): """Return a formatted string (in Lisp-like format) with all added constraints. We say the string is in s-expression format. """ return Z3_fixedpoint_to_string(self.ctx.ref(), self.fixedpoint, 0, (Ast * 0)()) def to_string(self, queries): """Return a formatted string (in Lisp-like format) with all added constraints. We say the string is in s-expression format. Include also queries. """ args, len = _to_ast_array(queries) return Z3_fixedpoint_to_string(self.ctx.ref(), self.fixedpoint, len, args) def statistics(self): """Return statistics for the last `query()`. """ return Statistics(Z3_fixedpoint_get_statistics(self.ctx.ref(), self.fixedpoint), self.ctx) def reason_unknown(self): """Return a string describing why the last `query()` returned `unknown`. """ return Z3_fixedpoint_get_reason_unknown(self.ctx.ref(), self.fixedpoint) def declare_var(self, *vars): """Add variable or several variables. The added variable or variables will be bound in the rules and queries """ vars = _get_args(vars) for v in vars: self.vars += [v] def abstract(self, fml, is_forall=True): if self.vars == []: return fml if is_forall: return ForAll(self.vars, fml) else: return Exists(self.vars, fml) ######################################### # # Finite domains # ######################################### class FiniteDomainSortRef(SortRef): """Finite domain sort.""" def size(self): """Return the size of the finite domain sort""" r = (ctypes.c_ulonglong * 1)() if Z3_get_finite_domain_sort_size(self.ctx_ref(), self.ast, r): return r[0] else: raise Z3Exception("Failed to retrieve finite domain sort size") def FiniteDomainSort(name, sz, ctx=None): """Create a named finite domain sort of a given size sz""" if not isinstance(name, Symbol): name = to_symbol(name) ctx = _get_ctx(ctx) return FiniteDomainSortRef(Z3_mk_finite_domain_sort(ctx.ref(), name, sz), ctx) def is_finite_domain_sort(s): """Return True if `s` is a Z3 finite-domain sort. >>> is_finite_domain_sort(FiniteDomainSort('S', 100)) True >>> is_finite_domain_sort(IntSort()) False """ return isinstance(s, FiniteDomainSortRef) class FiniteDomainRef(ExprRef): """Finite-domain expressions.""" def sort(self): """Return the sort of the finite-domain expression `self`.""" return FiniteDomainSortRef(Z3_get_sort(self.ctx_ref(), self.as_ast()), self.ctx) def as_string(self): """Return a Z3 floating point expression as a Python string.""" return Z3_ast_to_string(self.ctx_ref(), self.as_ast()) def is_finite_domain(a): """Return `True` if `a` is a Z3 finite-domain expression. >>> s = FiniteDomainSort('S', 100) >>> b = Const('b', s) >>> is_finite_domain(b) True >>> is_finite_domain(Int('x')) False """ return isinstance(a, FiniteDomainRef) class FiniteDomainNumRef(FiniteDomainRef): """Integer values.""" def as_long(self): """Return a Z3 finite-domain numeral as a Python long (bignum) numeral. >>> s = FiniteDomainSort('S', 100) >>> v = FiniteDomainVal(3, s) >>> v 3 >>> v.as_long() + 1 4 """ return int(self.as_string()) def as_string(self): """Return a Z3 finite-domain numeral as a Python string. >>> s = FiniteDomainSort('S', 100) >>> v = FiniteDomainVal(42, s) >>> v.as_string() '42' """ return Z3_get_numeral_string(self.ctx_ref(), self.as_ast()) def FiniteDomainVal(val, sort, ctx=None): """Return a Z3 finite-domain value. If `ctx=None`, then the global context is used. >>> s = FiniteDomainSort('S', 256) >>> FiniteDomainVal(255, s) 255 >>> FiniteDomainVal('100', s) 100 """ if z3_debug(): _z3_assert(is_finite_domain_sort(sort), "Expected finite-domain sort" ) ctx = sort.ctx return FiniteDomainNumRef(Z3_mk_numeral(ctx.ref(), _to_int_str(val), sort.ast), ctx) def is_finite_domain_value(a): """Return `True` if `a` is a Z3 finite-domain value. >>> s = FiniteDomainSort('S', 100) >>> b = Const('b', s) >>> is_finite_domain_value(b) False >>> b = FiniteDomainVal(10, s) >>> b 10 >>> is_finite_domain_value(b) True """ return is_finite_domain(a) and _is_numeral(a.ctx, a.as_ast()) ######################################### # # Optimize # ######################################### class OptimizeObjective: def __init__(self, opt, value, is_max): self._opt = opt self._value = value self._is_max = is_max def lower(self): opt = self._opt return _to_expr_ref(Z3_optimize_get_lower(opt.ctx.ref(), opt.optimize, self._value), opt.ctx) def upper(self): opt = self._opt return _to_expr_ref(Z3_optimize_get_upper(opt.ctx.ref(), opt.optimize, self._value), opt.ctx) def lower_values(self): opt = self._opt return AstVector(Z3_optimize_get_lower_as_vector(opt.ctx.ref(), opt.optimize, self._value), opt.ctx) def upper_values(self): opt = self._opt return AstVector(Z3_optimize_get_upper_as_vector(opt.ctx.ref(), opt.optimize, self._value), opt.ctx) def value(self): if self._is_max: return self.upper() else: return self.lower() def __str__(self): return "%s:%s" % (self._value, self._is_max) class Optimize(Z3PPObject): """Optimize API provides methods for solving using objective functions and weighted soft constraints""" def __init__(self, ctx=None): self.ctx = _get_ctx(ctx) self.optimize = Z3_mk_optimize(self.ctx.ref()) Z3_optimize_inc_ref(self.ctx.ref(), self.optimize) def __deepcopy__(self, memo={}): return Optimize(self.optimize, self.ctx) def __del__(self): if self.optimize is not None and self.ctx.ref() is not None: Z3_optimize_dec_ref(self.ctx.ref(), self.optimize) def set(self, *args, **keys): """Set a configuration option. The method `help()` return a string containing all available options. """ p = args2params(args, keys, self.ctx) Z3_optimize_set_params(self.ctx.ref(), self.optimize, p.params) def help(self): """Display a string describing all available options.""" print(Z3_optimize_get_help(self.ctx.ref(), self.optimize)) def param_descrs(self): """Return the parameter description set.""" return ParamDescrsRef(Z3_optimize_get_param_descrs(self.ctx.ref(), self.optimize), self.ctx) def assert_exprs(self, *args): """Assert constraints as background axioms for the optimize solver.""" args = _get_args(args) s = BoolSort(self.ctx) for arg in args: if isinstance(arg, Goal) or isinstance(arg, AstVector): for f in arg: Z3_optimize_assert(self.ctx.ref(), self.optimize, f.as_ast()) else: arg = s.cast(arg) Z3_optimize_assert(self.ctx.ref(), self.optimize, arg.as_ast()) def add(self, *args): """Assert constraints as background axioms for the optimize solver. Alias for assert_expr.""" self.assert_exprs(*args) def __iadd__(self, fml): 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 is 1. Soft constraints are grouped by identifiers. Soft constraints that are added without identifiers are grouped by default. """ if _is_int(weight): weight = "%d" % weight elif isinstance(weight, float): weight = "%f" % weight if not isinstance(weight, str): raise Z3Exception("weight should be a string or an integer") if id is None: id = "" id = to_symbol(id, self.ctx) v = Z3_optimize_assert_soft(self.ctx.ref(), self.optimize, arg.as_ast(), weight, id) return OptimizeObjective(self, v, False) def maximize(self, arg): """Add objective function to maximize.""" return OptimizeObjective(self, Z3_optimize_maximize(self.ctx.ref(), self.optimize, arg.as_ast()), True) def minimize(self, arg): """Add objective function to minimize.""" return OptimizeObjective(self, Z3_optimize_minimize(self.ctx.ref(), self.optimize, arg.as_ast()), False) def push(self): """create a backtracking point for added rules, facts and assertions""" Z3_optimize_push(self.ctx.ref(), self.optimize) def pop(self): """restore to previously created backtracking point""" Z3_optimize_pop(self.ctx.ref(), self.optimize) def check(self, *assumptions): """Check satisfiability while optimizing objective functions.""" assumptions = _get_args(assumptions) num = len(assumptions) _assumptions = (Ast * num)() for i in range(num): _assumptions[i] = assumptions[i].as_ast() return CheckSatResult(Z3_optimize_check(self.ctx.ref(), self.optimize, num, _assumptions)) def reason_unknown(self): """Return a string that describes why the last `check()` returned `unknown`.""" return Z3_optimize_get_reason_unknown(self.ctx.ref(), self.optimize) def model(self): """Return a model for the last check().""" try: return ModelRef(Z3_optimize_get_model(self.ctx.ref(), self.optimize), self.ctx) except Z3Exception: raise Z3Exception("model is not available") def unsat_core(self): return AstVector(Z3_optimize_get_unsat_core(self.ctx.ref(), self.optimize), self.ctx) def lower(self, obj): if not isinstance(obj, OptimizeObjective): raise Z3Exception("Expecting objective handle returned by maximize/minimize") return obj.lower() def upper(self, obj): if not isinstance(obj, OptimizeObjective): raise Z3Exception("Expecting objective handle returned by maximize/minimize") return obj.upper() def lower_values(self, obj): if not isinstance(obj, OptimizeObjective): raise Z3Exception("Expecting objective handle returned by maximize/minimize") return obj.lower_values() def upper_values(self, obj): if not isinstance(obj, OptimizeObjective): raise Z3Exception("Expecting objective handle returned by maximize/minimize") return obj.upper_values() def from_file(self, filename): """Parse assertions and objectives from a file""" Z3_optimize_from_file(self.ctx.ref(), self.optimize, filename) def from_string(self, s): """Parse assertions and objectives from a string""" Z3_optimize_from_string(self.ctx.ref(), self.optimize, s) def assertions(self): """Return an AST vector containing all added constraints.""" return AstVector(Z3_optimize_get_assertions(self.ctx.ref(), self.optimize), self.ctx) def objectives(self): """returns set of objective functions""" return AstVector(Z3_optimize_get_objectives(self.ctx.ref(), self.optimize), self.ctx) def __repr__(self): """Return a formatted string with all added rules and constraints.""" return self.sexpr() def sexpr(self): """Return a formatted string (in Lisp-like format) with all added constraints. We say the string is in s-expression format. """ return Z3_optimize_to_string(self.ctx.ref(), self.optimize) def statistics(self): """Return statistics for the last check`. """ return Statistics(Z3_optimize_get_statistics(self.ctx.ref(), self.optimize), self.ctx) ######################################### # # ApplyResult # ######################################### class ApplyResult(Z3PPObject): """An ApplyResult object contains the subgoals produced by a tactic when applied to a goal. It also contains model and proof converters.""" def __init__(self, result, ctx): self.result = result self.ctx = ctx Z3_apply_result_inc_ref(self.ctx.ref(), self.result) def __deepcopy__(self, memo={}): return ApplyResult(self.result, self.ctx) def __del__(self): if self.ctx.ref() is not None: Z3_apply_result_dec_ref(self.ctx.ref(), self.result) def __len__(self): """Return the number of subgoals in `self`. >>> a, b = Ints('a b') >>> g = Goal() >>> g.add(Or(a == 0, a == 1), Or(b == 0, b == 1), a > b) >>> t = Tactic('split-clause') >>> r = t(g) >>> len(r) 2 >>> t = Then(Tactic('split-clause'), Tactic('split-clause')) >>> len(t(g)) 4 >>> t = Then(Tactic('split-clause'), Tactic('split-clause'), Tactic('propagate-values')) >>> len(t(g)) 1 """ return int(Z3_apply_result_get_num_subgoals(self.ctx.ref(), self.result)) def __getitem__(self, idx): """Return one of the subgoals stored in ApplyResult object `self`. >>> a, b = Ints('a b') >>> g = Goal() >>> g.add(Or(a == 0, a == 1), Or(b == 0, b == 1), a > b) >>> t = Tactic('split-clause') >>> r = t(g) >>> r[0] [a == 0, Or(b == 0, b == 1), a > b] >>> r[1] [a == 1, Or(b == 0, b == 1), a > b] """ if idx >= len(self): raise IndexError return Goal(goal=Z3_apply_result_get_subgoal(self.ctx.ref(), self.result, idx), ctx=self.ctx) def __repr__(self): return obj_to_string(self) def sexpr(self): """Return a textual representation of the s-expression representing the set of subgoals in `self`.""" return Z3_apply_result_to_string(self.ctx.ref(), self.result) def as_expr(self): """Return a Z3 expression consisting of all subgoals. >>> x = Int('x') >>> g = Goal() >>> g.add(x > 1) >>> g.add(Or(x == 2, x == 3)) >>> r = Tactic('simplify')(g) >>> r [[Not(x <= 1), Or(x == 2, x == 3)]] >>> r.as_expr() And(Not(x <= 1), Or(x == 2, x == 3)) >>> r = Tactic('split-clause')(g) >>> r [[x > 1, x == 2], [x > 1, x == 3]] >>> r.as_expr() Or(And(x > 1, x == 2), And(x > 1, x == 3)) """ sz = len(self) if sz == 0: return BoolVal(False, self.ctx) elif sz == 1: return self[0].as_expr() else: return Or([ self[i].as_expr() for i in range(len(self)) ]) ######################################### # # Tactics # ######################################### class Tactic: """Tactics transform, solver and/or simplify sets of constraints (Goal). A Tactic can be converted into a Solver using the method solver(). Several combinators are available for creating new tactics using the built-in ones: Then(), OrElse(), FailIf(), Repeat(), When(), Cond(). """ def __init__(self, tactic, ctx=None): self.ctx = _get_ctx(ctx) self.tactic = None if isinstance(tactic, TacticObj): self.tactic = tactic else: if z3_debug(): _z3_assert(isinstance(tactic, str), "tactic name expected") try: self.tactic = Z3_mk_tactic(self.ctx.ref(), str(tactic)) except Z3Exception: raise Z3Exception("unknown tactic '%s'" % tactic) Z3_tactic_inc_ref(self.ctx.ref(), self.tactic) def __deepcopy__(self, memo={}): return Tactic(self.tactic, self.ctx) def __del__(self): if self.tactic is not None and self.ctx.ref() is not None: Z3_tactic_dec_ref(self.ctx.ref(), self.tactic) def solver(self, logFile=None): """Create a solver using the tactic `self`. The solver supports the methods `push()` and `pop()`, but it will always solve each `check()` from scratch. >>> t = Then('simplify', 'nlsat') >>> s = t.solver() >>> x = Real('x') >>> s.add(x**2 == 2, x > 0) >>> s.check() sat >>> s.model() [x = 1.4142135623?] """ return Solver(Z3_mk_solver_from_tactic(self.ctx.ref(), self.tactic), self.ctx, logFile) def apply(self, goal, *arguments, **keywords): """Apply tactic `self` to the given goal or Z3 Boolean expression using the given options. >>> x, y = Ints('x y') >>> t = Tactic('solve-eqs') >>> t.apply(And(x == 0, y >= x + 1)) [[y >= 1]] """ if z3_debug(): _z3_assert(isinstance(goal, Goal) or isinstance(goal, BoolRef), "Z3 Goal or Boolean expressions expected") goal = _to_goal(goal) if len(arguments) > 0 or len(keywords) > 0: p = args2params(arguments, keywords, self.ctx) return ApplyResult(Z3_tactic_apply_ex(self.ctx.ref(), self.tactic, goal.goal, p.params), self.ctx) else: return ApplyResult(Z3_tactic_apply(self.ctx.ref(), self.tactic, goal.goal), self.ctx) def __call__(self, goal, *arguments, **keywords): """Apply tactic `self` to the given goal or Z3 Boolean expression using the given options. >>> x, y = Ints('x y') >>> t = Tactic('solve-eqs') >>> t(And(x == 0, y >= x + 1)) [[y >= 1]] """ return self.apply(goal, *arguments, **keywords) def help(self): """Display a string containing a description of the available options for the `self` tactic.""" print(Z3_tactic_get_help(self.ctx.ref(), self.tactic)) def param_descrs(self): """Return the parameter description set.""" return ParamDescrsRef(Z3_tactic_get_param_descrs(self.ctx.ref(), self.tactic), self.ctx) def _to_goal(a): if isinstance(a, BoolRef): goal = Goal(ctx = a.ctx) goal.add(a) return goal else: return a def _to_tactic(t, ctx=None): if isinstance(t, Tactic): return t else: return Tactic(t, ctx) def _and_then(t1, t2, ctx=None): t1 = _to_tactic(t1, ctx) t2 = _to_tactic(t2, ctx) if z3_debug(): _z3_assert(t1.ctx == t2.ctx, "Context mismatch") return Tactic(Z3_tactic_and_then(t1.ctx.ref(), t1.tactic, t2.tactic), t1.ctx) def _or_else(t1, t2, ctx=None): t1 = _to_tactic(t1, ctx) t2 = _to_tactic(t2, ctx) if z3_debug(): _z3_assert(t1.ctx == t2.ctx, "Context mismatch") return Tactic(Z3_tactic_or_else(t1.ctx.ref(), t1.tactic, t2.tactic), t1.ctx) def AndThen(*ts, **ks): """Return a tactic that applies the tactics in `*ts` in sequence. >>> x, y = Ints('x y') >>> t = AndThen(Tactic('simplify'), Tactic('solve-eqs')) >>> t(And(x == 0, y > x + 1)) [[Not(y <= 1)]] >>> t(And(x == 0, y > x + 1)).as_expr() Not(y <= 1) """ if z3_debug(): _z3_assert(len(ts) >= 2, "At least two arguments expected") ctx = ks.get('ctx', None) num = len(ts) r = ts[0] for i in range(num - 1): r = _and_then(r, ts[i+1], ctx) return r def Then(*ts, **ks): """Return a tactic that applies the tactics in `*ts` in sequence. Shorthand for AndThen(*ts, **ks). >>> x, y = Ints('x y') >>> t = Then(Tactic('simplify'), Tactic('solve-eqs')) >>> t(And(x == 0, y > x + 1)) [[Not(y <= 1)]] >>> t(And(x == 0, y > x + 1)).as_expr() Not(y <= 1) """ return AndThen(*ts, **ks) def OrElse(*ts, **ks): """Return a tactic that applies the tactics in `*ts` until one of them succeeds (it doesn't fail). >>> x = Int('x') >>> t = OrElse(Tactic('split-clause'), Tactic('skip')) >>> # Tactic split-clause fails if there is no clause in the given goal. >>> t(x == 0) [[x == 0]] >>> t(Or(x == 0, x == 1)) [[x == 0], [x == 1]] """ if z3_debug(): _z3_assert(len(ts) >= 2, "At least two arguments expected") ctx = ks.get('ctx', None) num = len(ts) r = ts[0] for i in range(num - 1): r = _or_else(r, ts[i+1], ctx) return r def ParOr(*ts, **ks): """Return a tactic that applies the tactics in `*ts` in parallel until one of them succeeds (it doesn't fail). >>> x = Int('x') >>> t = ParOr(Tactic('simplify'), Tactic('fail')) >>> t(x + 1 == 2) [[x == 1]] """ if z3_debug(): _z3_assert(len(ts) >= 2, "At least two arguments expected") ctx = _get_ctx(ks.get('ctx', None)) ts = [ _to_tactic(t, ctx) for t in ts ] sz = len(ts) _args = (TacticObj * sz)() for i in range(sz): _args[i] = ts[i].tactic return Tactic(Z3_tactic_par_or(ctx.ref(), sz, _args), ctx) def ParThen(t1, t2, ctx=None): """Return a tactic that applies t1 and then t2 to every subgoal produced by t1. The subgoals are processed in parallel. >>> x, y = Ints('x y') >>> t = ParThen(Tactic('split-clause'), Tactic('propagate-values')) >>> t(And(Or(x == 1, x == 2), y == x + 1)) [[x == 1, y == 2], [x == 2, y == 3]] """ t1 = _to_tactic(t1, ctx) t2 = _to_tactic(t2, ctx) if z3_debug(): _z3_assert(t1.ctx == t2.ctx, "Context mismatch") return Tactic(Z3_tactic_par_and_then(t1.ctx.ref(), t1.tactic, t2.tactic), t1.ctx) def ParAndThen(t1, t2, ctx=None): """Alias for ParThen(t1, t2, ctx).""" return ParThen(t1, t2, ctx) def With(t, *args, **keys): """Return a tactic that applies tactic `t` using the given configuration options. >>> x, y = Ints('x y') >>> t = With(Tactic('simplify'), som=True) >>> t((x + 1)*(y + 2) == 0) [[2*x + y + x*y == -2]] """ ctx = keys.pop('ctx', None) t = _to_tactic(t, ctx) p = args2params(args, keys, t.ctx) return Tactic(Z3_tactic_using_params(t.ctx.ref(), t.tactic, p.params), t.ctx) def WithParams(t, p): """Return a tactic that applies tactic `t` using the given configuration options. >>> x, y = Ints('x y') >>> p = ParamsRef() >>> p.set("som", True) >>> t = WithParams(Tactic('simplify'), p) >>> t((x + 1)*(y + 2) == 0) [[2*x + y + x*y == -2]] """ t = _to_tactic(t, None) return Tactic(Z3_tactic_using_params(t.ctx.ref(), t.tactic, p.params), t.ctx) def Repeat(t, max=4294967295, ctx=None): """Return a tactic that keeps applying `t` until the goal is not modified anymore or the maximum number of iterations `max` is reached. >>> x, y = Ints('x y') >>> c = And(Or(x == 0, x == 1), Or(y == 0, y == 1), x > y) >>> t = Repeat(OrElse(Tactic('split-clause'), Tactic('skip'))) >>> r = t(c) >>> for subgoal in r: print(subgoal) [x == 0, y == 0, x > y] [x == 0, y == 1, x > y] [x == 1, y == 0, x > y] [x == 1, y == 1, x > y] >>> t = Then(t, Tactic('propagate-values')) >>> t(c) [[x == 1, y == 0]] """ t = _to_tactic(t, ctx) return Tactic(Z3_tactic_repeat(t.ctx.ref(), t.tactic, max), t.ctx) def TryFor(t, ms, ctx=None): """Return a tactic that applies `t` to a given goal for `ms` milliseconds. If `t` does not terminate in `ms` milliseconds, then it fails. """ t = _to_tactic(t, ctx) return Tactic(Z3_tactic_try_for(t.ctx.ref(), t.tactic, ms), t.ctx) def tactics(ctx=None): """Return a list of all available tactics in Z3. >>> l = tactics() >>> l.count('simplify') == 1 True """ ctx = _get_ctx(ctx) return [ Z3_get_tactic_name(ctx.ref(), i) for i in range(Z3_get_num_tactics(ctx.ref())) ] def tactic_description(name, ctx=None): """Return a short description for the tactic named `name`. >>> d = tactic_description('simplify') """ ctx = _get_ctx(ctx) return Z3_tactic_get_descr(ctx.ref(), name) def describe_tactics(): """Display a (tabular) description of all available tactics in Z3.""" if in_html_mode(): even = True print('') for t in tactics(): if even: print('') even = False else: print('') even = True print('' % (t, insert_line_breaks(tactic_description(t), 40))) print('
%s%s
') else: for t in tactics(): print('%s : %s' % (t, tactic_description(t))) class Probe: """Probes are used to inspect a goal (aka problem) and collect information that may be used to decide which solver and/or preprocessing step will be used.""" def __init__(self, probe, ctx=None): self.ctx = _get_ctx(ctx) self.probe = None if isinstance(probe, ProbeObj): self.probe = probe elif isinstance(probe, float): self.probe = Z3_probe_const(self.ctx.ref(), probe) elif _is_int(probe): self.probe = Z3_probe_const(self.ctx.ref(), float(probe)) elif isinstance(probe, bool): if probe: self.probe = Z3_probe_const(self.ctx.ref(), 1.0) else: self.probe = Z3_probe_const(self.ctx.ref(), 0.0) else: if z3_debug(): _z3_assert(isinstance(probe, str), "probe name expected") try: self.probe = Z3_mk_probe(self.ctx.ref(), probe) except Z3Exception: raise Z3Exception("unknown probe '%s'" % probe) Z3_probe_inc_ref(self.ctx.ref(), self.probe) def __deepcopy__(self, memo={}): return Probe(self.probe, self.ctx) def __del__(self): if self.probe is not None and self.ctx.ref() is not None: Z3_probe_dec_ref(self.ctx.ref(), self.probe) def __lt__(self, other): """Return a probe that evaluates to "true" when the value returned by `self` is less than the value returned by `other`. >>> p = Probe('size') < 10 >>> x = Int('x') >>> g = Goal() >>> g.add(x > 0) >>> g.add(x < 10) >>> p(g) 1.0 """ return Probe(Z3_probe_lt(self.ctx.ref(), self.probe, _to_probe(other, self.ctx).probe), self.ctx) def __gt__(self, other): """Return a probe that evaluates to "true" when the value returned by `self` is greater than the value returned by `other`. >>> p = Probe('size') > 10 >>> x = Int('x') >>> g = Goal() >>> g.add(x > 0) >>> g.add(x < 10) >>> p(g) 0.0 """ return Probe(Z3_probe_gt(self.ctx.ref(), self.probe, _to_probe(other, self.ctx).probe), self.ctx) def __le__(self, other): """Return a probe that evaluates to "true" when the value returned by `self` is less than or equal to the value returned by `other`. >>> p = Probe('size') <= 2 >>> x = Int('x') >>> g = Goal() >>> g.add(x > 0) >>> g.add(x < 10) >>> p(g) 1.0 """ return Probe(Z3_probe_le(self.ctx.ref(), self.probe, _to_probe(other, self.ctx).probe), self.ctx) def __ge__(self, other): """Return a probe that evaluates to "true" when the value returned by `self` is greater than or equal to the value returned by `other`. >>> p = Probe('size') >= 2 >>> x = Int('x') >>> g = Goal() >>> g.add(x > 0) >>> g.add(x < 10) >>> p(g) 1.0 """ return Probe(Z3_probe_ge(self.ctx.ref(), self.probe, _to_probe(other, self.ctx).probe), self.ctx) def __eq__(self, other): """Return a probe that evaluates to "true" when the value returned by `self` is equal to the value returned by `other`. >>> p = Probe('size') == 2 >>> x = Int('x') >>> g = Goal() >>> g.add(x > 0) >>> g.add(x < 10) >>> p(g) 1.0 """ return Probe(Z3_probe_eq(self.ctx.ref(), self.probe, _to_probe(other, self.ctx).probe), self.ctx) def __ne__(self, other): """Return a probe that evaluates to "true" when the value returned by `self` is not equal to the value returned by `other`. >>> p = Probe('size') != 2 >>> x = Int('x') >>> g = Goal() >>> g.add(x > 0) >>> g.add(x < 10) >>> p(g) 0.0 """ p = self.__eq__(other) return Probe(Z3_probe_not(self.ctx.ref(), p.probe), self.ctx) def __call__(self, goal): """Evaluate the probe `self` in the given goal. >>> p = Probe('size') >>> x = Int('x') >>> g = Goal() >>> g.add(x > 0) >>> g.add(x < 10) >>> p(g) 2.0 >>> g.add(x < 20) >>> p(g) 3.0 >>> p = Probe('num-consts') >>> p(g) 1.0 >>> p = Probe('is-propositional') >>> p(g) 0.0 >>> p = Probe('is-qflia') >>> p(g) 1.0 """ if z3_debug(): _z3_assert(isinstance(goal, Goal) or isinstance(goal, BoolRef), "Z3 Goal or Boolean expression expected") goal = _to_goal(goal) return Z3_probe_apply(self.ctx.ref(), self.probe, goal.goal) def is_probe(p): """Return `True` if `p` is a Z3 probe. >>> is_probe(Int('x')) False >>> is_probe(Probe('memory')) True """ return isinstance(p, Probe) def _to_probe(p, ctx=None): if is_probe(p): return p else: return Probe(p, ctx) def probes(ctx=None): """Return a list of all available probes in Z3. >>> l = probes() >>> l.count('memory') == 1 True """ ctx = _get_ctx(ctx) return [ Z3_get_probe_name(ctx.ref(), i) for i in range(Z3_get_num_probes(ctx.ref())) ] def probe_description(name, ctx=None): """Return a short description for the probe named `name`. >>> d = probe_description('memory') """ ctx = _get_ctx(ctx) return Z3_probe_get_descr(ctx.ref(), name) def describe_probes(): """Display a (tabular) description of all available probes in Z3.""" if in_html_mode(): even = True print('') for p in probes(): if even: print('') even = False else: print('') even = True print('' % (p, insert_line_breaks(probe_description(p), 40))) print('
%s%s
') else: for p in probes(): print('%s : %s' % (p, probe_description(p))) def _probe_nary(f, args, ctx): if z3_debug(): _z3_assert(len(args) > 0, "At least one argument expected") num = len(args) r = _to_probe(args[0], ctx) for i in range(num - 1): r = Probe(f(ctx.ref(), r.probe, _to_probe(args[i+1], ctx).probe), ctx) return r def _probe_and(args, ctx): return _probe_nary(Z3_probe_and, args, ctx) def _probe_or(args, ctx): return _probe_nary(Z3_probe_or, args, ctx) def FailIf(p, ctx=None): """Return a tactic that fails if the probe `p` evaluates to true. Otherwise, it returns the input goal unmodified. In the following example, the tactic applies 'simplify' if and only if there are more than 2 constraints in the goal. >>> t = OrElse(FailIf(Probe('size') > 2), Tactic('simplify')) >>> x, y = Ints('x y') >>> g = Goal() >>> g.add(x > 0) >>> g.add(y > 0) >>> t(g) [[x > 0, y > 0]] >>> g.add(x == y + 1) >>> t(g) [[Not(x <= 0), Not(y <= 0), x == 1 + y]] """ p = _to_probe(p, ctx) return Tactic(Z3_tactic_fail_if(p.ctx.ref(), p.probe), p.ctx) def When(p, t, ctx=None): """Return a tactic that applies tactic `t` only if probe `p` evaluates to true. Otherwise, it returns the input goal unmodified. >>> t = When(Probe('size') > 2, Tactic('simplify')) >>> x, y = Ints('x y') >>> g = Goal() >>> g.add(x > 0) >>> g.add(y > 0) >>> t(g) [[x > 0, y > 0]] >>> g.add(x == y + 1) >>> t(g) [[Not(x <= 0), Not(y <= 0), x == 1 + y]] """ p = _to_probe(p, ctx) t = _to_tactic(t, ctx) return Tactic(Z3_tactic_when(t.ctx.ref(), p.probe, t.tactic), t.ctx) def Cond(p, t1, t2, ctx=None): """Return a tactic that applies tactic `t1` to a goal if probe `p` evaluates to true, and `t2` otherwise. >>> t = Cond(Probe('is-qfnra'), Tactic('qfnra'), Tactic('smt')) """ p = _to_probe(p, ctx) t1 = _to_tactic(t1, ctx) t2 = _to_tactic(t2, ctx) return Tactic(Z3_tactic_cond(t1.ctx.ref(), p.probe, t1.tactic, t2.tactic), t1.ctx) ######################################### # # Utils # ######################################### def simplify(a, *arguments, **keywords): """Simplify the expression `a` using the given options. This function has many options. Use `help_simplify` to obtain the complete list. >>> x = Int('x') >>> y = Int('y') >>> simplify(x + 1 + y + x + 1) 2 + 2*x + y >>> simplify((x + 1)*(y + 1), som=True) 1 + x + y + x*y >>> simplify(Distinct(x, y, 1), blast_distinct=True) And(Not(x == y), Not(x == 1), Not(y == 1)) >>> simplify(And(x == 0, y == 1), elim_and=True) Not(Or(Not(x == 0), Not(y == 1))) """ if z3_debug(): _z3_assert(is_expr(a), "Z3 expression expected") if len(arguments) > 0 or len(keywords) > 0: p = args2params(arguments, keywords, a.ctx) return _to_expr_ref(Z3_simplify_ex(a.ctx_ref(), a.as_ast(), p.params), a.ctx) else: return _to_expr_ref(Z3_simplify(a.ctx_ref(), a.as_ast()), a.ctx) def help_simplify(): """Return a string describing all options available for Z3 `simplify` procedure.""" print(Z3_simplify_get_help(main_ctx().ref())) def simplify_param_descrs(): """Return the set of parameter descriptions for Z3 `simplify` procedure.""" return ParamDescrsRef(Z3_simplify_get_param_descrs(main_ctx().ref()), main_ctx()) def substitute(t, *m): """Apply substitution m on t, m is a list of pairs of the form (from, to). Every occurrence in t of from is replaced with to. >>> x = Int('x') >>> y = Int('y') >>> substitute(x + 1, (x, y + 1)) y + 1 + 1 >>> f = Function('f', IntSort(), IntSort()) >>> substitute(f(x) + f(y), (f(x), IntVal(1)), (f(y), IntVal(1))) 1 + 1 """ if isinstance(m, tuple): m1 = _get_args(m) if isinstance(m1, list) and all(isinstance(p, tuple) for p in m1): m = m1 if z3_debug(): _z3_assert(is_expr(t), "Z3 expression expected") _z3_assert(all([isinstance(p, tuple) and is_expr(p[0]) and is_expr(p[1]) and p[0].sort().eq(p[1].sort()) for p in m]), "Z3 invalid substitution, expression pairs expected.") num = len(m) _from = (Ast * num)() _to = (Ast * num)() for i in range(num): _from[i] = m[i][0].as_ast() _to[i] = m[i][1].as_ast() return _to_expr_ref(Z3_substitute(t.ctx.ref(), t.as_ast(), num, _from, _to), t.ctx) def substitute_vars(t, *m): """Substitute the free variables in t with the expression in m. >>> v0 = Var(0, IntSort()) >>> v1 = Var(1, IntSort()) >>> x = Int('x') >>> f = Function('f', IntSort(), IntSort(), IntSort()) >>> # replace v0 with x+1 and v1 with x >>> substitute_vars(f(v0, v1), x + 1, x) f(x + 1, x) """ if z3_debug(): _z3_assert(is_expr(t), "Z3 expression expected") _z3_assert(all([is_expr(n) for n in m]), "Z3 invalid substitution, list of expressions expected.") num = len(m) _to = (Ast * num)() for i in range(num): _to[i] = m[i].as_ast() return _to_expr_ref(Z3_substitute_vars(t.ctx.ref(), t.as_ast(), num, _to), t.ctx) def Sum(*args): """Create the sum of the Z3 expressions. >>> a, b, c = Ints('a b c') >>> Sum(a, b, c) a + b + c >>> Sum([a, b, c]) a + b + c >>> A = IntVector('a', 5) >>> Sum(A) a__0 + a__1 + a__2 + a__3 + a__4 """ args = _get_args(args) if len(args) == 0: return 0 ctx = _ctx_from_ast_arg_list(args) if ctx is None: return _reduce(lambda a, b: a + b, args, 0) args = _coerce_expr_list(args, ctx) if is_bv(args[0]): return _reduce(lambda a, b: a + b, args, 0) else: _args, sz = _to_ast_array(args) return ArithRef(Z3_mk_add(ctx.ref(), sz, _args), ctx) def Product(*args): """Create the product of the Z3 expressions. >>> a, b, c = Ints('a b c') >>> Product(a, b, c) a*b*c >>> Product([a, b, c]) a*b*c >>> A = IntVector('a', 5) >>> Product(A) a__0*a__1*a__2*a__3*a__4 """ args = _get_args(args) if len(args) == 0: return 1 ctx = _ctx_from_ast_arg_list(args) if ctx is None: return _reduce(lambda a, b: a * b, args, 1) args = _coerce_expr_list(args, ctx) if is_bv(args[0]): return _reduce(lambda a, b: a * b, args, 1) else: _args, sz = _to_ast_array(args) return ArithRef(Z3_mk_mul(ctx.ref(), sz, _args), ctx) def AtMost(*args): """Create an at-most Pseudo-Boolean k constraint. >>> a, b, c = Bools('a b c') >>> f = AtMost(a, b, c, 2) """ args = _get_args(args) if z3_debug(): _z3_assert(len(args) > 1, "Non empty list of arguments expected") ctx = _ctx_from_ast_arg_list(args) if z3_debug(): _z3_assert(ctx is not None, "At least one of the arguments must be a Z3 expression") args1 = _coerce_expr_list(args[:-1], ctx) k = args[-1] _args, sz = _to_ast_array(args1) return BoolRef(Z3_mk_atmost(ctx.ref(), sz, _args, k), ctx) def AtLeast(*args): """Create an at-most Pseudo-Boolean k constraint. >>> a, b, c = Bools('a b c') >>> f = AtLeast(a, b, c, 2) """ args = _get_args(args) if z3_debug(): _z3_assert(len(args) > 1, "Non empty list of arguments expected") ctx = _ctx_from_ast_arg_list(args) if z3_debug(): _z3_assert(ctx is not None, "At least one of the arguments must be a Z3 expression") args1 = _coerce_expr_list(args[:-1], ctx) k = args[-1] _args, sz = _to_ast_array(args1) return BoolRef(Z3_mk_atleast(ctx.ref(), sz, _args, k), ctx) def _reorder_pb_arg(arg): a, b = arg if not _is_int(b) and _is_int(a): return b, a return arg def _pb_args_coeffs(args, default_ctx = None): args = _get_args_ast_list(args) if len(args) == 0: return _get_ctx(default_ctx), 0, (Ast * 0)(), (ctypes.c_int * 0)() args = [_reorder_pb_arg(arg) for arg in args] args, coeffs = zip(*args) if z3_debug(): _z3_assert(len(args) > 0, "Non empty list of arguments expected") ctx = _ctx_from_ast_arg_list(args) if z3_debug(): _z3_assert(ctx is not None, "At least one of the arguments must be a Z3 expression") args = _coerce_expr_list(args, ctx) _args, sz = _to_ast_array(args) _coeffs = (ctypes.c_int * len(coeffs))() for i in range(len(coeffs)): _z3_check_cint_overflow(coeffs[i], "coefficient") _coeffs[i] = coeffs[i] return ctx, sz, _args, _coeffs def PbLe(args, k): """Create a Pseudo-Boolean inequality k constraint. >>> a, b, c = Bools('a b c') >>> f = PbLe(((a,1),(b,3),(c,2)), 3) """ _z3_check_cint_overflow(k, "k") ctx, sz, _args, _coeffs = _pb_args_coeffs(args) return BoolRef(Z3_mk_pble(ctx.ref(), sz, _args, _coeffs, k), ctx) def PbGe(args, k): """Create a Pseudo-Boolean inequality k constraint. >>> a, b, c = Bools('a b c') >>> f = PbGe(((a,1),(b,3),(c,2)), 3) """ _z3_check_cint_overflow(k, "k") ctx, sz, _args, _coeffs = _pb_args_coeffs(args) return BoolRef(Z3_mk_pbge(ctx.ref(), sz, _args, _coeffs, k), ctx) def PbEq(args, k, ctx = None): """Create a Pseudo-Boolean inequality k constraint. >>> a, b, c = Bools('a b c') >>> f = PbEq(((a,1),(b,3),(c,2)), 3) """ _z3_check_cint_overflow(k, "k") ctx, sz, _args, _coeffs = _pb_args_coeffs(args) return BoolRef(Z3_mk_pbeq(ctx.ref(), sz, _args, _coeffs, k), ctx) def solve(*args, **keywords): """Solve the constraints `*args`. This is a simple function for creating demonstrations. It creates a solver, configure it using the options in `keywords`, adds the constraints in `args`, and invokes check. >>> a = Int('a') >>> solve(a > 0, a < 2) [a = 1] """ s = Solver() s.set(**keywords) s.add(*args) if keywords.get('show', False): print(s) r = s.check() if r == unsat: print("no solution") elif r == unknown: print("failed to solve") try: print(s.model()) except Z3Exception: return else: print(s.model()) def solve_using(s, *args, **keywords): """Solve the constraints `*args` using solver `s`. This is a simple function for creating demonstrations. It is similar to `solve`, but it uses the given solver `s`. It configures solver `s` using the options in `keywords`, adds the constraints in `args`, and invokes check. """ if z3_debug(): _z3_assert(isinstance(s, Solver), "Solver object expected") s.set(**keywords) s.add(*args) if keywords.get('show', False): print("Problem:") print(s) r = s.check() if r == unsat: print("no solution") elif r == unknown: print("failed to solve") try: print(s.model()) except Z3Exception: return else: if keywords.get('show', False): print("Solution:") print(s.model()) def prove(claim, **keywords): """Try to prove the given claim. This is a simple function for creating demonstrations. It tries to prove `claim` by showing the negation is unsatisfiable. >>> p, q = Bools('p q') >>> prove(Not(And(p, q)) == Or(Not(p), Not(q))) proved """ if z3_debug(): _z3_assert(is_bool(claim), "Z3 Boolean expression expected") s = Solver() s.set(**keywords) s.add(Not(claim)) if keywords.get('show', False): print(s) r = s.check() if r == unsat: print("proved") elif r == unknown: print("failed to prove") print(s.model()) else: print("counterexample") print(s.model()) def _solve_html(*args, **keywords): """Version of function `solve` used in RiSE4Fun.""" s = Solver() s.set(**keywords) s.add(*args) if keywords.get('show', False): print("Problem:") print(s) r = s.check() if r == unsat: print("no solution") elif r == unknown: print("failed to solve") try: print(s.model()) except Z3Exception: return else: if keywords.get('show', False): print("Solution:") print(s.model()) def _solve_using_html(s, *args, **keywords): """Version of function `solve_using` used in RiSE4Fun.""" if z3_debug(): _z3_assert(isinstance(s, Solver), "Solver object expected") s.set(**keywords) s.add(*args) if keywords.get('show', False): print("Problem:") print(s) r = s.check() if r == unsat: print("no solution") elif r == unknown: print("failed to solve") try: print(s.model()) except Z3Exception: return else: if keywords.get('show', False): print("Solution:") print(s.model()) def _prove_html(claim, **keywords): """Version of function `prove` used in RiSE4Fun.""" if z3_debug(): _z3_assert(is_bool(claim), "Z3 Boolean expression expected") s = Solver() s.set(**keywords) s.add(Not(claim)) if keywords.get('show', False): print(s) r = s.check() if r == unsat: print("proved") elif r == unknown: print("failed to prove") print(s.model()) else: print("counterexample") print(s.model()) def _dict2sarray(sorts, ctx): sz = len(sorts) _names = (Symbol * sz)() _sorts = (Sort * sz) () i = 0 for k in sorts: v = sorts[k] if z3_debug(): _z3_assert(isinstance(k, str), "String expected") _z3_assert(is_sort(v), "Z3 sort expected") _names[i] = to_symbol(k, ctx) _sorts[i] = v.ast i = i + 1 return sz, _names, _sorts def _dict2darray(decls, ctx): sz = len(decls) _names = (Symbol * sz)() _decls = (FuncDecl * sz) () i = 0 for k in decls: v = decls[k] if z3_debug(): _z3_assert(isinstance(k, str), "String expected") _z3_assert(is_func_decl(v) or is_const(v), "Z3 declaration or constant expected") _names[i] = to_symbol(k, ctx) if is_const(v): _decls[i] = v.decl().ast else: _decls[i] = v.ast i = i + 1 return sz, _names, _decls def parse_smt2_string(s, sorts={}, decls={}, ctx=None): """Parse a string in SMT 2.0 format using the given sorts and decls. The arguments sorts and decls are Python dictionaries used to initialize the symbol table used for the SMT 2.0 parser. >>> parse_smt2_string('(declare-const x Int) (assert (> x 0)) (assert (< x 10))') [x > 0, x < 10] >>> x, y = Ints('x y') >>> f = Function('f', IntSort(), IntSort()) >>> parse_smt2_string('(assert (> (+ foo (g bar)) 0))', decls={ 'foo' : x, 'bar' : y, 'g' : f}) [x + f(y) > 0] >>> parse_smt2_string('(declare-const a U) (assert (> a 0))', sorts={ 'U' : IntSort() }) [a > 0] """ ctx = _get_ctx(ctx) ssz, snames, ssorts = _dict2sarray(sorts, ctx) dsz, dnames, ddecls = _dict2darray(decls, ctx) return AstVector(Z3_parse_smtlib2_string(ctx.ref(), s, ssz, snames, ssorts, dsz, dnames, ddecls), ctx) def parse_smt2_file(f, sorts={}, decls={}, ctx=None): """Parse a file in SMT 2.0 format using the given sorts and decls. This function is similar to parse_smt2_string(). """ ctx = _get_ctx(ctx) ssz, snames, ssorts = _dict2sarray(sorts, ctx) dsz, dnames, ddecls = _dict2darray(decls, ctx) return AstVector(Z3_parse_smtlib2_file(ctx.ref(), f, ssz, snames, ssorts, dsz, dnames, ddecls), ctx) ######################################### # # Floating-Point Arithmetic # ######################################### # Global default rounding mode _dflt_rounding_mode = Z3_OP_FPA_RM_TOWARD_ZERO _dflt_fpsort_ebits = 11 _dflt_fpsort_sbits = 53 def get_default_rounding_mode(ctx=None): """Retrieves the global default rounding mode.""" global _dflt_rounding_mode if _dflt_rounding_mode == Z3_OP_FPA_RM_TOWARD_ZERO: return RTZ(ctx) elif _dflt_rounding_mode == Z3_OP_FPA_RM_TOWARD_NEGATIVE: return RTN(ctx) elif _dflt_rounding_mode == Z3_OP_FPA_RM_TOWARD_POSITIVE: return RTP(ctx) elif _dflt_rounding_mode == Z3_OP_FPA_RM_NEAREST_TIES_TO_EVEN: return RNE(ctx) elif _dflt_rounding_mode == Z3_OP_FPA_RM_NEAREST_TIES_TO_AWAY: return RNA(ctx) def set_default_rounding_mode(rm, ctx=None): global _dflt_rounding_mode if is_fprm_value(rm): _dflt_rounding_mode = rm.decl().kind() else: _z3_assert(_dflt_rounding_mode == Z3_OP_FPA_RM_TOWARD_ZERO or _dflt_rounding_mode == Z3_OP_FPA_RM_TOWARD_NEGATIVE or _dflt_rounding_mode == Z3_OP_FPA_RM_TOWARD_POSITIVE or _dflt_rounding_mode == Z3_OP_FPA_RM_NEAREST_TIES_TO_EVEN or _dflt_rounding_mode == Z3_OP_FPA_RM_NEAREST_TIES_TO_AWAY, "illegal rounding mode") _dflt_rounding_mode = rm def get_default_fp_sort(ctx=None): return FPSort(_dflt_fpsort_ebits, _dflt_fpsort_sbits, ctx) def set_default_fp_sort(ebits, sbits, ctx=None): global _dflt_fpsort_ebits global _dflt_fpsort_sbits _dflt_fpsort_ebits = ebits _dflt_fpsort_sbits = sbits def _dflt_rm(ctx=None): return get_default_rounding_mode(ctx) def _dflt_fps(ctx=None): return get_default_fp_sort(ctx) def _coerce_fp_expr_list(alist, ctx): first_fp_sort = None for a in alist: if is_fp(a): if first_fp_sort is None: first_fp_sort = a.sort() elif first_fp_sort == a.sort(): pass # OK, same as before else: # we saw at least 2 different float sorts; something will # throw a sort mismatch later, for now assume None. first_fp_sort = None break r = [] for i in range(len(alist)): a = alist[i] if (isinstance(a, str) and a.contains('2**(') and a.endswith(')')) or _is_int(a) or isinstance(a, float) or isinstance(a, bool): r.append(FPVal(a, None, first_fp_sort, ctx)) else: r.append(a) return _coerce_expr_list(r, ctx) ### FP Sorts class FPSortRef(SortRef): """Floating-point sort.""" def ebits(self): """Retrieves the number of bits reserved for the exponent in the FloatingPoint sort `self`. >>> b = FPSort(8, 24) >>> b.ebits() 8 """ return int(Z3_fpa_get_ebits(self.ctx_ref(), self.ast)) def sbits(self): """Retrieves the number of bits reserved for the significand in the FloatingPoint sort `self`. >>> b = FPSort(8, 24) >>> b.sbits() 24 """ return int(Z3_fpa_get_sbits(self.ctx_ref(), self.ast)) def cast(self, val): """Try to cast `val` as a floating-point expression. >>> b = FPSort(8, 24) >>> b.cast(1.0) 1 >>> b.cast(1.0).sexpr() '(fp #b0 #x7f #b00000000000000000000000)' """ if is_expr(val): if z3_debug(): _z3_assert(self.ctx == val.ctx, "Context mismatch") return val else: return FPVal(val, None, self, self.ctx) def Float16(ctx=None): """Floating-point 16-bit (half) sort.""" ctx = _get_ctx(ctx) return FPSortRef(Z3_mk_fpa_sort_16(ctx.ref()), ctx) def FloatHalf(ctx=None): """Floating-point 16-bit (half) sort.""" ctx = _get_ctx(ctx) return FPSortRef(Z3_mk_fpa_sort_half(ctx.ref()), ctx) def Float32(ctx=None): """Floating-point 32-bit (single) sort.""" ctx = _get_ctx(ctx) return FPSortRef(Z3_mk_fpa_sort_32(ctx.ref()), ctx) def FloatSingle(ctx=None): """Floating-point 32-bit (single) sort.""" ctx = _get_ctx(ctx) return FPSortRef(Z3_mk_fpa_sort_single(ctx.ref()), ctx) def Float64(ctx=None): """Floating-point 64-bit (double) sort.""" ctx = _get_ctx(ctx) return FPSortRef(Z3_mk_fpa_sort_64(ctx.ref()), ctx) def FloatDouble(ctx=None): """Floating-point 64-bit (double) sort.""" ctx = _get_ctx(ctx) return FPSortRef(Z3_mk_fpa_sort_double(ctx.ref()), ctx) def Float128(ctx=None): """Floating-point 128-bit (quadruple) sort.""" ctx = _get_ctx(ctx) return FPSortRef(Z3_mk_fpa_sort_128(ctx.ref()), ctx) def FloatQuadruple(ctx=None): """Floating-point 128-bit (quadruple) sort.""" ctx = _get_ctx(ctx) return FPSortRef(Z3_mk_fpa_sort_quadruple(ctx.ref()), ctx) class FPRMSortRef(SortRef): """"Floating-point rounding mode sort.""" def is_fp_sort(s): """Return True if `s` is a Z3 floating-point sort. >>> is_fp_sort(FPSort(8, 24)) True >>> is_fp_sort(IntSort()) False """ return isinstance(s, FPSortRef) def is_fprm_sort(s): """Return True if `s` is a Z3 floating-point rounding mode sort. >>> is_fprm_sort(FPSort(8, 24)) False >>> is_fprm_sort(RNE().sort()) True """ return isinstance(s, FPRMSortRef) ### FP Expressions class FPRef(ExprRef): """Floating-point expressions.""" def sort(self): """Return the sort of the floating-point expression `self`. >>> x = FP('1.0', FPSort(8, 24)) >>> x.sort() FPSort(8, 24) >>> x.sort() == FPSort(8, 24) True """ return FPSortRef(Z3_get_sort(self.ctx_ref(), self.as_ast()), self.ctx) def ebits(self): """Retrieves the number of bits reserved for the exponent in the FloatingPoint expression `self`. >>> b = FPSort(8, 24) >>> b.ebits() 8 """ return self.sort().ebits(); def sbits(self): """Retrieves the number of bits reserved for the exponent in the FloatingPoint expression `self`. >>> b = FPSort(8, 24) >>> b.sbits() 24 """ return self.sort().sbits(); def as_string(self): """Return a Z3 floating point expression as a Python string.""" return Z3_ast_to_string(self.ctx_ref(), self.as_ast()) def __le__(self, other): return fpLEQ(self, other, self.ctx) def __lt__(self, other): return fpLT(self, other, self.ctx) def __ge__(self, other): return fpGEQ(self, other, self.ctx) def __gt__(self, other): return fpGT(self, other, self.ctx) def __add__(self, other): """Create the Z3 expression `self + other`. >>> x = FP('x', FPSort(8, 24)) >>> y = FP('y', FPSort(8, 24)) >>> x + y x + y >>> (x + y).sort() FPSort(8, 24) """ [a, b] = _coerce_fp_expr_list([self, other], self.ctx) return fpAdd(_dflt_rm(), a, b, self.ctx) def __radd__(self, other): """Create the Z3 expression `other + self`. >>> x = FP('x', FPSort(8, 24)) >>> 10 + x 1.25*(2**3) + x """ [a, b] = _coerce_fp_expr_list([other, self], self.ctx) return fpAdd(_dflt_rm(), a, b, self.ctx) def __sub__(self, other): """Create the Z3 expression `self - other`. >>> x = FP('x', FPSort(8, 24)) >>> y = FP('y', FPSort(8, 24)) >>> x - y x - y >>> (x - y).sort() FPSort(8, 24) """ [a, b] = _coerce_fp_expr_list([self, other], self.ctx) return fpSub(_dflt_rm(), a, b, self.ctx) def __rsub__(self, other): """Create the Z3 expression `other - self`. >>> x = FP('x', FPSort(8, 24)) >>> 10 - x 1.25*(2**3) - x """ [a, b] = _coerce_fp_expr_list([other, self], self.ctx) return fpSub(_dflt_rm(), a, b, self.ctx) def __mul__(self, other): """Create the Z3 expression `self * other`. >>> x = FP('x', FPSort(8, 24)) >>> y = FP('y', FPSort(8, 24)) >>> x * y x * y >>> (x * y).sort() FPSort(8, 24) >>> 10 * y 1.25*(2**3) * y """ [a, b] = _coerce_fp_expr_list([self, other], self.ctx) return fpMul(_dflt_rm(), a, b, self.ctx) def __rmul__(self, other): """Create the Z3 expression `other * self`. >>> x = FP('x', FPSort(8, 24)) >>> y = FP('y', FPSort(8, 24)) >>> x * y x * y >>> x * 10 x * 1.25*(2**3) """ [a, b] = _coerce_fp_expr_list([other, self], self.ctx) return fpMul(_dflt_rm(), a, b, self.ctx) def __pos__(self): """Create the Z3 expression `+self`.""" return self def __neg__(self): """Create the Z3 expression `-self`. >>> x = FP('x', Float32()) >>> -x -x """ return fpNeg(self) def __div__(self, other): """Create the Z3 expression `self / other`. >>> x = FP('x', FPSort(8, 24)) >>> y = FP('y', FPSort(8, 24)) >>> x / y x / y >>> (x / y).sort() FPSort(8, 24) >>> 10 / y 1.25*(2**3) / y """ [a, b] = _coerce_fp_expr_list([self, other], self.ctx) return fpDiv(_dflt_rm(), a, b, self.ctx) def __rdiv__(self, other): """Create the Z3 expression `other / self`. >>> x = FP('x', FPSort(8, 24)) >>> y = FP('y', FPSort(8, 24)) >>> x / y x / y >>> x / 10 x / 1.25*(2**3) """ [a, b] = _coerce_fp_expr_list([other, self], self.ctx) return fpDiv(_dflt_rm(), a, b, self.ctx) def __truediv__(self, other): """Create the Z3 expression division `self / other`.""" return self.__div__(other) def __rtruediv__(self, other): """Create the Z3 expression division `other / self`.""" return self.__rdiv__(other) def __mod__(self, other): """Create the Z3 expression mod `self % other`.""" return fpRem(self, other) def __rmod__(self, other): """Create the Z3 expression mod `other % self`.""" return fpRem(other, self) class FPRMRef(ExprRef): """Floating-point rounding mode expressions""" def as_string(self): """Return a Z3 floating point expression as a Python string.""" return Z3_ast_to_string(self.ctx_ref(), self.as_ast()) def RoundNearestTiesToEven(ctx=None): ctx = _get_ctx(ctx) return FPRMRef(Z3_mk_fpa_round_nearest_ties_to_even(ctx.ref()), ctx) def RNE (ctx=None): ctx = _get_ctx(ctx) return FPRMRef(Z3_mk_fpa_round_nearest_ties_to_even(ctx.ref()), ctx) def RoundNearestTiesToAway(ctx=None): ctx = _get_ctx(ctx) return FPRMRef(Z3_mk_fpa_round_nearest_ties_to_away(ctx.ref()), ctx) def RNA (ctx=None): ctx = _get_ctx(ctx) return FPRMRef(Z3_mk_fpa_round_nearest_ties_to_away(ctx.ref()), ctx) def RoundTowardPositive(ctx=None): ctx = _get_ctx(ctx) return FPRMRef(Z3_mk_fpa_round_toward_positive(ctx.ref()), ctx) def RTP(ctx=None): ctx = _get_ctx(ctx) return FPRMRef(Z3_mk_fpa_round_toward_positive(ctx.ref()), ctx) def RoundTowardNegative(ctx=None): ctx = _get_ctx(ctx) return FPRMRef(Z3_mk_fpa_round_toward_negative(ctx.ref()), ctx) def RTN(ctx=None): ctx = _get_ctx(ctx) return FPRMRef(Z3_mk_fpa_round_toward_negative(ctx.ref()), ctx) def RoundTowardZero(ctx=None): ctx = _get_ctx(ctx) return FPRMRef(Z3_mk_fpa_round_toward_zero(ctx.ref()), ctx) def RTZ(ctx=None): ctx = _get_ctx(ctx) return FPRMRef(Z3_mk_fpa_round_toward_zero(ctx.ref()), ctx) def is_fprm(a): """Return `True` if `a` is a Z3 floating-point rounding mode expression. >>> rm = RNE() >>> is_fprm(rm) True >>> rm = 1.0 >>> is_fprm(rm) False """ return isinstance(a, FPRMRef) def is_fprm_value(a): """Return `True` if `a` is a Z3 floating-point rounding mode numeral value.""" return is_fprm(a) and _is_numeral(a.ctx, a.ast) ### FP Numerals class FPNumRef(FPRef): """The sign of the numeral. >>> x = FPVal(+1.0, FPSort(8, 24)) >>> x.sign() False >>> x = FPVal(-1.0, FPSort(8, 24)) >>> x.sign() True """ def sign(self): l = (ctypes.c_int)() if Z3_fpa_get_numeral_sign(self.ctx.ref(), self.as_ast(), byref(l)) == False: raise Z3Exception("error retrieving the sign of a numeral.") return l.value != 0 """The sign of a floating-point numeral as a bit-vector expression. Remark: NaN's are invalid arguments. """ def sign_as_bv(self): return BitVecNumRef(Z3_fpa_get_numeral_sign_bv(self.ctx.ref(), self.as_ast()), self.ctx) """The significand of the numeral. >>> x = FPVal(2.5, FPSort(8, 24)) >>> x.significand() 1.25 """ def significand(self): return Z3_fpa_get_numeral_significand_string(self.ctx.ref(), self.as_ast()) """The significand of the numeral as a long. >>> x = FPVal(2.5, FPSort(8, 24)) >>> x.significand_as_long() 1.25 """ def significand_as_long(self): ptr = (ctypes.c_ulonglong * 1)() if not Z3_fpa_get_numeral_significand_uint64(self.ctx.ref(), self.as_ast(), ptr): raise Z3Exception("error retrieving the significand of a numeral.") return ptr[0] """The significand of the numeral as a bit-vector expression. Remark: NaN are invalid arguments. """ def significand_as_bv(self): return BitVecNumRef(Z3_fpa_get_numeral_significand_bv(self.ctx.ref(), self.as_ast()), self.ctx) """The exponent of the numeral. >>> x = FPVal(2.5, FPSort(8, 24)) >>> x.exponent() 1 """ def exponent(self, biased=True): return Z3_fpa_get_numeral_exponent_string(self.ctx.ref(), self.as_ast(), biased) """The exponent of the numeral as a long. >>> x = FPVal(2.5, FPSort(8, 24)) >>> x.exponent_as_long() 1 """ def exponent_as_long(self, biased=True): ptr = (ctypes.c_longlong * 1)() if not Z3_fpa_get_numeral_exponent_int64(self.ctx.ref(), self.as_ast(), ptr, biased): raise Z3Exception("error retrieving the exponent of a numeral.") return ptr[0] """The exponent of the numeral as a bit-vector expression. Remark: NaNs are invalid arguments. """ def exponent_as_bv(self, biased=True): return BitVecNumRef(Z3_fpa_get_numeral_exponent_bv(self.ctx.ref(), self.as_ast(), biased), self.ctx) """Indicates whether the numeral is a NaN.""" def isNaN(self): return Z3_fpa_is_numeral_nan(self.ctx.ref(), self.as_ast()) """Indicates whether the numeral is +oo or -oo.""" def isInf(self): return Z3_fpa_is_numeral_inf(self.ctx.ref(), self.as_ast()) """Indicates whether the numeral is +zero or -zero.""" def isZero(self): return Z3_fpa_is_numeral_zero(self.ctx.ref(), self.as_ast()) """Indicates whether the numeral is normal.""" def isNormal(self): return Z3_fpa_is_numeral_normal(self.ctx.ref(), self.as_ast()) """Indicates whether the numeral is subnormal.""" def isSubnormal(self): return Z3_fpa_is_numeral_subnormal(self.ctx.ref(), self.as_ast()) """Indicates whether the numeral is positive.""" def isPositive(self): return Z3_fpa_is_numeral_positive(self.ctx.ref(), self.as_ast()) """Indicates whether the numeral is negative.""" def isNegative(self): return Z3_fpa_is_numeral_negative(self.ctx.ref(), self.as_ast()) """ The string representation of the numeral. >>> x = FPVal(20, FPSort(8, 24)) >>> x.as_string() 1.25*(2**4) """ def as_string(self): s = Z3_get_numeral_string(self.ctx.ref(), self.as_ast()) return ("FPVal(%s, %s)" % (s, self.sort())) def is_fp(a): """Return `True` if `a` is a Z3 floating-point expression. >>> b = FP('b', FPSort(8, 24)) >>> is_fp(b) True >>> is_fp(b + 1.0) True >>> is_fp(Int('x')) False """ return isinstance(a, FPRef) def is_fp_value(a): """Return `True` if `a` is a Z3 floating-point numeral value. >>> b = FP('b', FPSort(8, 24)) >>> is_fp_value(b) False >>> b = FPVal(1.0, FPSort(8, 24)) >>> b 1 >>> is_fp_value(b) True """ return is_fp(a) and _is_numeral(a.ctx, a.ast) def FPSort(ebits, sbits, ctx=None): """Return a Z3 floating-point sort of the given sizes. If `ctx=None`, then the global context is used. >>> Single = FPSort(8, 24) >>> Double = FPSort(11, 53) >>> Single FPSort(8, 24) >>> x = Const('x', Single) >>> eq(x, FP('x', FPSort(8, 24))) True """ ctx = _get_ctx(ctx) return FPSortRef(Z3_mk_fpa_sort(ctx.ref(), ebits, sbits), ctx) def _to_float_str(val, exp=0): if isinstance(val, float): if math.isnan(val): res = "NaN" elif val == 0.0: sone = math.copysign(1.0, val) if sone < 0.0: return "-0.0" else: return "+0.0" elif val == float("+inf"): res = "+oo" elif val == float("-inf"): res = "-oo" else: v = val.as_integer_ratio() num = v[0] den = v[1] rvs = str(num) + '/' + str(den) res = rvs + 'p' + _to_int_str(exp) elif isinstance(val, bool): if val: res = "1.0" else: res = "0.0" elif _is_int(val): res = str(val) elif isinstance(val, str): inx = val.find('*(2**') if inx == -1: res = val elif val[-1] == ')': res = val[0:inx] exp = str(int(val[inx+5:-1]) + int(exp)) else: _z3_assert(False, "String does not have floating-point numeral form.") elif z3_debug(): _z3_assert(False, "Python value cannot be used to create floating-point numerals.") if exp == 0: return res else: return res + 'p' + exp def fpNaN(s): """Create a Z3 floating-point NaN term. >>> s = FPSort(8, 24) >>> set_fpa_pretty(True) >>> fpNaN(s) NaN >>> pb = get_fpa_pretty() >>> set_fpa_pretty(False) >>> fpNaN(s) fpNaN(FPSort(8, 24)) >>> set_fpa_pretty(pb) """ _z3_assert(isinstance(s, FPSortRef), "sort mismatch") return FPNumRef(Z3_mk_fpa_nan(s.ctx_ref(), s.ast), s.ctx) def fpPlusInfinity(s): """Create a Z3 floating-point +oo term. >>> s = FPSort(8, 24) >>> pb = get_fpa_pretty() >>> set_fpa_pretty(True) >>> fpPlusInfinity(s) +oo >>> set_fpa_pretty(False) >>> fpPlusInfinity(s) fpPlusInfinity(FPSort(8, 24)) >>> set_fpa_pretty(pb) """ _z3_assert(isinstance(s, FPSortRef), "sort mismatch") return FPNumRef(Z3_mk_fpa_inf(s.ctx_ref(), s.ast, False), s.ctx) def fpMinusInfinity(s): """Create a Z3 floating-point -oo term.""" _z3_assert(isinstance(s, FPSortRef), "sort mismatch") return FPNumRef(Z3_mk_fpa_inf(s.ctx_ref(), s.ast, True), s.ctx) def fpInfinity(s, negative): """Create a Z3 floating-point +oo or -oo term.""" _z3_assert(isinstance(s, FPSortRef), "sort mismatch") _z3_assert(isinstance(negative, bool), "expected Boolean flag") return FPNumRef(Z3_mk_fpa_inf(s.ctx_ref(), s.ast, negative), s.ctx) def fpPlusZero(s): """Create a Z3 floating-point +0.0 term.""" _z3_assert(isinstance(s, FPSortRef), "sort mismatch") return FPNumRef(Z3_mk_fpa_zero(s.ctx_ref(), s.ast, False), s.ctx) def fpMinusZero(s): """Create a Z3 floating-point -0.0 term.""" _z3_assert(isinstance(s, FPSortRef), "sort mismatch") return FPNumRef(Z3_mk_fpa_zero(s.ctx_ref(), s.ast, True), s.ctx) def fpZero(s, negative): """Create a Z3 floating-point +0.0 or -0.0 term.""" _z3_assert(isinstance(s, FPSortRef), "sort mismatch") _z3_assert(isinstance(negative, bool), "expected Boolean flag") return FPNumRef(Z3_mk_fpa_zero(s.ctx_ref(), s.ast, negative), s.ctx) def FPVal(sig, exp=None, fps=None, ctx=None): """Return a floating-point value of value `val` and sort `fps`. If `ctx=None`, then the global context is used. >>> v = FPVal(20.0, FPSort(8, 24)) >>> v 1.25*(2**4) >>> print("0x%.8x" % v.exponent_as_long(False)) 0x00000004 >>> v = FPVal(2.25, FPSort(8, 24)) >>> v 1.125*(2**1) >>> v = FPVal(-2.25, FPSort(8, 24)) >>> v -1.125*(2**1) >>> FPVal(-0.0, FPSort(8, 24)) -0.0 >>> FPVal(0.0, FPSort(8, 24)) +0.0 >>> FPVal(+0.0, FPSort(8, 24)) +0.0 """ ctx = _get_ctx(ctx) if is_fp_sort(exp): fps = exp exp = None elif fps is None: fps = _dflt_fps(ctx) _z3_assert(is_fp_sort(fps), "sort mismatch") if exp is None: exp = 0 val = _to_float_str(sig) if val == "NaN" or val == "nan": return fpNaN(fps) elif val == "-0.0": return fpMinusZero(fps) elif val == "0.0" or val == "+0.0": return fpPlusZero(fps) elif val == "+oo" or val == "+inf" or val == "+Inf": return fpPlusInfinity(fps) elif val == "-oo" or val == "-inf" or val == "-Inf": return fpMinusInfinity(fps) else: return FPNumRef(Z3_mk_numeral(ctx.ref(), val, fps.ast), ctx) def FP(name, fpsort, ctx=None): """Return a floating-point constant named `name`. `fpsort` is the floating-point sort. If `ctx=None`, then the global context is used. >>> x = FP('x', FPSort(8, 24)) >>> is_fp(x) True >>> x.ebits() 8 >>> x.sort() FPSort(8, 24) >>> word = FPSort(8, 24) >>> x2 = FP('x', word) >>> eq(x, x2) True """ if isinstance(fpsort, FPSortRef) and ctx is None: ctx = fpsort.ctx else: ctx = _get_ctx(ctx) return FPRef(Z3_mk_const(ctx.ref(), to_symbol(name, ctx), fpsort.ast), ctx) def FPs(names, fpsort, ctx=None): """Return an array of floating-point constants. >>> x, y, z = FPs('x y z', FPSort(8, 24)) >>> x.sort() FPSort(8, 24) >>> x.sbits() 24 >>> x.ebits() 8 >>> fpMul(RNE(), fpAdd(RNE(), x, y), z) fpMul(RNE(), fpAdd(RNE(), x, y), z) """ ctx = _get_ctx(ctx) if isinstance(names, str): names = names.split(" ") return [FP(name, fpsort, ctx) for name in names] def fpAbs(a, ctx=None): """Create a Z3 floating-point absolute value expression. >>> s = FPSort(8, 24) >>> rm = RNE() >>> x = FPVal(1.0, s) >>> fpAbs(x) fpAbs(1) >>> y = FPVal(-20.0, s) >>> y -1.25*(2**4) >>> fpAbs(y) fpAbs(-1.25*(2**4)) >>> fpAbs(-1.25*(2**4)) fpAbs(-1.25*(2**4)) >>> fpAbs(x).sort() FPSort(8, 24) """ ctx = _get_ctx(ctx) [a] = _coerce_fp_expr_list([a], ctx) return FPRef(Z3_mk_fpa_abs(ctx.ref(), a.as_ast()), ctx) def fpNeg(a, ctx=None): """Create a Z3 floating-point addition expression. >>> s = FPSort(8, 24) >>> rm = RNE() >>> x = FP('x', s) >>> fpNeg(x) -x >>> fpNeg(x).sort() FPSort(8, 24) """ ctx = _get_ctx(ctx) [a] = _coerce_fp_expr_list([a], ctx) return FPRef(Z3_mk_fpa_neg(ctx.ref(), a.as_ast()), ctx) def _mk_fp_unary(f, rm, a, ctx): ctx = _get_ctx(ctx) [a] = _coerce_fp_expr_list([a], ctx) if z3_debug(): _z3_assert(is_fprm(rm), "First argument must be a Z3 floating-point rounding mode expression") _z3_assert(is_fp(a), "Second argument must be a Z3 floating-point expression") return FPRef(f(ctx.ref(), rm.as_ast(), a.as_ast()), ctx) def _mk_fp_unary_pred(f, a, ctx): ctx = _get_ctx(ctx) [a] = _coerce_fp_expr_list([a], ctx) if z3_debug(): _z3_assert(is_fp(a), "First argument must be a Z3 floating-point expression") return BoolRef(f(ctx.ref(), a.as_ast()), ctx) def _mk_fp_bin(f, rm, a, b, ctx): ctx = _get_ctx(ctx) [a, b] = _coerce_fp_expr_list([a, b], ctx) if z3_debug(): _z3_assert(is_fprm(rm), "First argument must be a Z3 floating-point rounding mode expression") _z3_assert(is_fp(a) or is_fp(b), "Second or third argument must be a Z3 floating-point expression") return FPRef(f(ctx.ref(), rm.as_ast(), a.as_ast(), b.as_ast()), ctx) def _mk_fp_bin_norm(f, a, b, ctx): ctx = _get_ctx(ctx) [a, b] = _coerce_fp_expr_list([a, b], ctx) if z3_debug(): _z3_assert(is_fp(a) or is_fp(b), "First or second argument must be a Z3 floating-point expression") return FPRef(f(ctx.ref(), a.as_ast(), b.as_ast()), ctx) def _mk_fp_bin_pred(f, a, b, ctx): ctx = _get_ctx(ctx) [a, b] = _coerce_fp_expr_list([a, b], ctx) if z3_debug(): _z3_assert(is_fp(a) or is_fp(b), "Second or third argument must be a Z3 floating-point expression") return BoolRef(f(ctx.ref(), a.as_ast(), b.as_ast()), ctx) def _mk_fp_tern(f, rm, a, b, c, ctx): ctx = _get_ctx(ctx) [a, b, c] = _coerce_fp_expr_list([a, b, c], ctx) if z3_debug(): _z3_assert(is_fprm(rm), "First argument must be a Z3 floating-point rounding mode expression") _z3_assert(is_fp(a) or is_fp(b) or is_fp(c), "At least one of the arguments must be a Z3 floating-point expression") return FPRef(f(ctx.ref(), rm.as_ast(), a.as_ast(), b.as_ast(), c.as_ast()), ctx) def fpAdd(rm, a, b, ctx=None): """Create a Z3 floating-point addition expression. >>> s = FPSort(8, 24) >>> rm = RNE() >>> x = FP('x', s) >>> y = FP('y', s) >>> fpAdd(rm, x, y) fpAdd(RNE(), x, y) >>> fpAdd(RTZ(), x, y) # default rounding mode is RTZ x + y >>> fpAdd(rm, x, y).sort() FPSort(8, 24) """ return _mk_fp_bin(Z3_mk_fpa_add, rm, a, b, ctx) def fpSub(rm, a, b, ctx=None): """Create a Z3 floating-point subtraction expression. >>> s = FPSort(8, 24) >>> rm = RNE() >>> x = FP('x', s) >>> y = FP('y', s) >>> fpSub(rm, x, y) fpSub(RNE(), x, y) >>> fpSub(rm, x, y).sort() FPSort(8, 24) """ return _mk_fp_bin(Z3_mk_fpa_sub, rm, a, b, ctx) def fpMul(rm, a, b, ctx=None): """Create a Z3 floating-point multiplication expression. >>> s = FPSort(8, 24) >>> rm = RNE() >>> x = FP('x', s) >>> y = FP('y', s) >>> fpMul(rm, x, y) fpMul(RNE(), x, y) >>> fpMul(rm, x, y).sort() FPSort(8, 24) """ return _mk_fp_bin(Z3_mk_fpa_mul, rm, a, b, ctx) def fpDiv(rm, a, b, ctx=None): """Create a Z3 floating-point division expression. >>> s = FPSort(8, 24) >>> rm = RNE() >>> x = FP('x', s) >>> y = FP('y', s) >>> fpDiv(rm, x, y) fpDiv(RNE(), x, y) >>> fpDiv(rm, x, y).sort() FPSort(8, 24) """ return _mk_fp_bin(Z3_mk_fpa_div, rm, a, b, ctx) def fpRem(a, b, ctx=None): """Create a Z3 floating-point remainder expression. >>> s = FPSort(8, 24) >>> x = FP('x', s) >>> y = FP('y', s) >>> fpRem(x, y) fpRem(x, y) >>> fpRem(x, y).sort() FPSort(8, 24) """ return _mk_fp_bin_norm(Z3_mk_fpa_rem, a, b, ctx) def fpMin(a, b, ctx=None): """Create a Z3 floating-point minimum expression. >>> s = FPSort(8, 24) >>> rm = RNE() >>> x = FP('x', s) >>> y = FP('y', s) >>> fpMin(x, y) fpMin(x, y) >>> fpMin(x, y).sort() FPSort(8, 24) """ return _mk_fp_bin_norm(Z3_mk_fpa_min, a, b, ctx) def fpMax(a, b, ctx=None): """Create a Z3 floating-point maximum expression. >>> s = FPSort(8, 24) >>> rm = RNE() >>> x = FP('x', s) >>> y = FP('y', s) >>> fpMax(x, y) fpMax(x, y) >>> fpMax(x, y).sort() FPSort(8, 24) """ return _mk_fp_bin_norm(Z3_mk_fpa_max, a, b, ctx) def fpFMA(rm, a, b, c, ctx=None): """Create a Z3 floating-point fused multiply-add expression. """ return _mk_fp_tern(Z3_mk_fpa_fma, rm, a, b, c, ctx) def fpSqrt(rm, a, ctx=None): """Create a Z3 floating-point square root expression. """ return _mk_fp_unary(Z3_mk_fpa_sqrt, rm, a, ctx) def fpRoundToIntegral(rm, a, ctx=None): """Create a Z3 floating-point roundToIntegral expression. """ return _mk_fp_unary(Z3_mk_fpa_round_to_integral, rm, a, ctx) def fpIsNaN(a, ctx=None): """Create a Z3 floating-point isNaN expression. >>> s = FPSort(8, 24) >>> x = FP('x', s) >>> y = FP('y', s) >>> fpIsNaN(x) fpIsNaN(x) """ return _mk_fp_unary_pred(Z3_mk_fpa_is_nan, a, ctx) def fpIsInf(a, ctx=None): """Create a Z3 floating-point isInfinite expression. >>> s = FPSort(8, 24) >>> x = FP('x', s) >>> fpIsInf(x) fpIsInf(x) """ return _mk_fp_unary_pred(Z3_mk_fpa_is_infinite, a, ctx) def fpIsZero(a, ctx=None): """Create a Z3 floating-point isZero expression. """ return _mk_fp_unary_pred(Z3_mk_fpa_is_zero, a, ctx) def fpIsNormal(a, ctx=None): """Create a Z3 floating-point isNormal expression. """ return _mk_fp_unary_pred(Z3_mk_fpa_is_normal, a, ctx) def fpIsSubnormal(a, ctx=None): """Create a Z3 floating-point isSubnormal expression. """ return _mk_fp_unary_pred(Z3_mk_fpa_is_subnormal, a, ctx) def fpIsNegative(a, ctx=None): """Create a Z3 floating-point isNegative expression. """ return _mk_fp_unary_pred(Z3_mk_fpa_is_negative, a, ctx) def fpIsPositive(a, ctx=None): """Create a Z3 floating-point isPositive expression. """ return _mk_fp_unary_pred(Z3_mk_fpa_is_positive, a, ctx) def _check_fp_args(a, b): if z3_debug(): _z3_assert(is_fp(a) or is_fp(b), "At least one of the arguments must be a Z3 floating-point expression") def fpLT(a, b, ctx=None): """Create the Z3 floating-point expression `other < self`. >>> x, y = FPs('x y', FPSort(8, 24)) >>> fpLT(x, y) x < y >>> (x < y).sexpr() '(fp.lt x y)' """ return _mk_fp_bin_pred(Z3_mk_fpa_lt, a, b, ctx) def fpLEQ(a, b, ctx=None): """Create the Z3 floating-point expression `other <= self`. >>> x, y = FPs('x y', FPSort(8, 24)) >>> fpLEQ(x, y) x <= y >>> (x <= y).sexpr() '(fp.leq x y)' """ return _mk_fp_bin_pred(Z3_mk_fpa_leq, a, b, ctx) def fpGT(a, b, ctx=None): """Create the Z3 floating-point expression `other > self`. >>> x, y = FPs('x y', FPSort(8, 24)) >>> fpGT(x, y) x > y >>> (x > y).sexpr() '(fp.gt x y)' """ return _mk_fp_bin_pred(Z3_mk_fpa_gt, a, b, ctx) def fpGEQ(a, b, ctx=None): """Create the Z3 floating-point expression `other >= self`. >>> x, y = FPs('x y', FPSort(8, 24)) >>> fpGEQ(x, y) x >= y >>> (x >= y).sexpr() '(fp.geq x y)' """ return _mk_fp_bin_pred(Z3_mk_fpa_geq, a, b, ctx) def fpEQ(a, b, ctx=None): """Create the Z3 floating-point expression `fpEQ(other, self)`. >>> x, y = FPs('x y', FPSort(8, 24)) >>> fpEQ(x, y) fpEQ(x, y) >>> fpEQ(x, y).sexpr() '(fp.eq x y)' """ return _mk_fp_bin_pred(Z3_mk_fpa_eq, a, b, ctx) def fpNEQ(a, b, ctx=None): """Create the Z3 floating-point expression `Not(fpEQ(other, self))`. >>> x, y = FPs('x y', FPSort(8, 24)) >>> fpNEQ(x, y) Not(fpEQ(x, y)) >>> (x != y).sexpr() '(distinct x y)' """ return Not(fpEQ(a, b, ctx)) def fpFP(sgn, exp, sig, ctx=None): """Create the Z3 floating-point value `fpFP(sgn, sig, exp)` from the three bit-vectors sgn, sig, and exp. >>> s = FPSort(8, 24) >>> x = fpFP(BitVecVal(1, 1), BitVecVal(2**7-1, 8), BitVecVal(2**22, 23)) >>> print(x) fpFP(1, 127, 4194304) >>> xv = FPVal(-1.5, s) >>> print(xv) -1.5 >>> slvr = Solver() >>> slvr.add(fpEQ(x, xv)) >>> slvr.check() sat >>> xv = FPVal(+1.5, s) >>> print(xv) 1.5 >>> slvr = Solver() >>> slvr.add(fpEQ(x, xv)) >>> slvr.check() unsat """ _z3_assert(is_bv(sgn) and is_bv(exp) and is_bv(sig), "sort mismatch") _z3_assert(sgn.sort().size() == 1, "sort mismatch") ctx = _get_ctx(ctx) _z3_assert(ctx == sgn.ctx == exp.ctx == sig.ctx, "context mismatch") return FPRef(Z3_mk_fpa_fp(ctx.ref(), sgn.ast, exp.ast, sig.ast), ctx) def fpToFP(a1, a2=None, a3=None, ctx=None): """Create a Z3 floating-point conversion expression from other term sorts to floating-point. From a bit-vector term in IEEE 754-2008 format: >>> x = FPVal(1.0, Float32()) >>> x_bv = fpToIEEEBV(x) >>> simplify(fpToFP(x_bv, Float32())) 1 From a floating-point term with different precision: >>> x = FPVal(1.0, Float32()) >>> x_db = fpToFP(RNE(), x, Float64()) >>> x_db.sort() FPSort(11, 53) From a real term: >>> x_r = RealVal(1.5) >>> simplify(fpToFP(RNE(), x_r, Float32())) 1.5 From a signed bit-vector term: >>> x_signed = BitVecVal(-5, BitVecSort(32)) >>> simplify(fpToFP(RNE(), x_signed, Float32())) -1.25*(2**2) """ ctx = _get_ctx(ctx) if is_bv(a1) and is_fp_sort(a2): return FPRef(Z3_mk_fpa_to_fp_bv(ctx.ref(), a1.ast, a2.ast), ctx) elif is_fprm(a1) and is_fp(a2) and is_fp_sort(a3): return FPRef(Z3_mk_fpa_to_fp_float(ctx.ref(), a1.ast, a2.ast, a3.ast), ctx) elif is_fprm(a1) and is_real(a2) and is_fp_sort(a3): return FPRef(Z3_mk_fpa_to_fp_real(ctx.ref(), a1.ast, a2.ast, a3.ast), ctx) elif is_fprm(a1) and is_bv(a2) and is_fp_sort(a3): return FPRef(Z3_mk_fpa_to_fp_signed(ctx.ref(), a1.ast, a2.ast, a3.ast), ctx) else: raise Z3Exception("Unsupported combination of arguments for conversion to floating-point term.") def fpBVToFP(v, sort, ctx=None): """Create a Z3 floating-point conversion expression that represents the conversion from a bit-vector term to a floating-point term. >>> x_bv = BitVecVal(0x3F800000, 32) >>> x_fp = fpBVToFP(x_bv, Float32()) >>> x_fp fpToFP(1065353216) >>> simplify(x_fp) 1 """ _z3_assert(is_bv(v), "First argument must be a Z3 floating-point rounding mode expression.") _z3_assert(is_fp_sort(sort), "Second argument must be a Z3 floating-point sort.") ctx = _get_ctx(ctx) return FPRef(Z3_mk_fpa_to_fp_bv(ctx.ref(), v.ast, sort.ast), ctx) def fpFPToFP(rm, v, sort, ctx=None): """Create a Z3 floating-point conversion expression that represents the conversion from a floating-point term to a floating-point term of different precision. >>> x_sgl = FPVal(1.0, Float32()) >>> x_dbl = fpFPToFP(RNE(), x_sgl, Float64()) >>> x_dbl fpToFP(RNE(), 1) >>> simplify(x_dbl) 1 >>> x_dbl.sort() FPSort(11, 53) """ _z3_assert(is_fprm(rm), "First argument must be a Z3 floating-point rounding mode expression.") _z3_assert(is_fp(v), "Second argument must be a Z3 floating-point expression.") _z3_assert(is_fp_sort(sort), "Third argument must be a Z3 floating-point sort.") ctx = _get_ctx(ctx) return FPRef(Z3_mk_fpa_to_fp_float(ctx.ref(), rm.ast, v.ast, sort.ast), ctx) def fpRealToFP(rm, v, sort, ctx=None): """Create a Z3 floating-point conversion expression that represents the conversion from a real term to a floating-point term. >>> x_r = RealVal(1.5) >>> x_fp = fpRealToFP(RNE(), x_r, Float32()) >>> x_fp fpToFP(RNE(), 3/2) >>> simplify(x_fp) 1.5 """ _z3_assert(is_fprm(rm), "First argument must be a Z3 floating-point rounding mode expression.") _z3_assert(is_real(v), "Second argument must be a Z3 expression or real sort.") _z3_assert(is_fp_sort(sort), "Third argument must be a Z3 floating-point sort.") ctx = _get_ctx(ctx) return FPRef(Z3_mk_fpa_to_fp_real(ctx.ref(), rm.ast, v.ast, sort.ast), ctx) def fpSignedToFP(rm, v, sort, ctx=None): """Create a Z3 floating-point conversion expression that represents the conversion from a signed bit-vector term (encoding an integer) to a floating-point term. >>> x_signed = BitVecVal(-5, BitVecSort(32)) >>> x_fp = fpSignedToFP(RNE(), x_signed, Float32()) >>> x_fp fpToFP(RNE(), 4294967291) >>> simplify(x_fp) -1.25*(2**2) """ _z3_assert(is_fprm(rm), "First argument must be a Z3 floating-point rounding mode expression.") _z3_assert(is_bv(v), "Second argument must be a Z3 expression or real sort.") _z3_assert(is_fp_sort(sort), "Third argument must be a Z3 floating-point sort.") ctx = _get_ctx(ctx) return FPRef(Z3_mk_fpa_to_fp_signed(ctx.ref(), rm.ast, v.ast, sort.ast), ctx) def fpUnsignedToFP(rm, v, sort, ctx=None): """Create a Z3 floating-point conversion expression that represents the conversion from an unsigned bit-vector term (encoding an integer) to a floating-point term. >>> x_signed = BitVecVal(-5, BitVecSort(32)) >>> x_fp = fpUnsignedToFP(RNE(), x_signed, Float32()) >>> x_fp fpToFPUnsigned(RNE(), 4294967291) >>> simplify(x_fp) 1*(2**32) """ _z3_assert(is_fprm(rm), "First argument must be a Z3 floating-point rounding mode expression.") _z3_assert(is_bv(v), "Second argument must be a Z3 expression or real sort.") _z3_assert(is_fp_sort(sort), "Third argument must be a Z3 floating-point sort.") ctx = _get_ctx(ctx) return FPRef(Z3_mk_fpa_to_fp_unsigned(ctx.ref(), rm.ast, v.ast, sort.ast), ctx) def fpToFPUnsigned(rm, x, s, ctx=None): """Create a Z3 floating-point conversion expression, from unsigned bit-vector to floating-point expression.""" if z3_debug(): _z3_assert(is_fprm(rm), "First argument must be a Z3 floating-point rounding mode expression") _z3_assert(is_bv(x), "Second argument must be a Z3 bit-vector expression") _z3_assert(is_fp_sort(s), "Third argument must be Z3 floating-point sort") ctx = _get_ctx(ctx) return FPRef(Z3_mk_fpa_to_fp_unsigned(ctx.ref(), rm.ast, x.ast, s.ast), ctx) def fpToSBV(rm, x, s, ctx=None): """Create a Z3 floating-point conversion expression, from floating-point expression to signed bit-vector. >>> x = FP('x', FPSort(8, 24)) >>> y = fpToSBV(RTZ(), x, BitVecSort(32)) >>> print(is_fp(x)) True >>> print(is_bv(y)) True >>> print(is_fp(y)) False >>> print(is_bv(x)) False """ if z3_debug(): _z3_assert(is_fprm(rm), "First argument must be a Z3 floating-point rounding mode expression") _z3_assert(is_fp(x), "Second argument must be a Z3 floating-point expression") _z3_assert(is_bv_sort(s), "Third argument must be Z3 bit-vector sort") ctx = _get_ctx(ctx) return BitVecRef(Z3_mk_fpa_to_sbv(ctx.ref(), rm.ast, x.ast, s.size()), ctx) def fpToUBV(rm, x, s, ctx=None): """Create a Z3 floating-point conversion expression, from floating-point expression to unsigned bit-vector. >>> x = FP('x', FPSort(8, 24)) >>> y = fpToUBV(RTZ(), x, BitVecSort(32)) >>> print(is_fp(x)) True >>> print(is_bv(y)) True >>> print(is_fp(y)) False >>> print(is_bv(x)) False """ if z3_debug(): _z3_assert(is_fprm(rm), "First argument must be a Z3 floating-point rounding mode expression") _z3_assert(is_fp(x), "Second argument must be a Z3 floating-point expression") _z3_assert(is_bv_sort(s), "Third argument must be Z3 bit-vector sort") ctx = _get_ctx(ctx) return BitVecRef(Z3_mk_fpa_to_ubv(ctx.ref(), rm.ast, x.ast, s.size()), ctx) def fpToReal(x, ctx=None): """Create a Z3 floating-point conversion expression, from floating-point expression to real. >>> x = FP('x', FPSort(8, 24)) >>> y = fpToReal(x) >>> print(is_fp(x)) True >>> print(is_real(y)) True >>> print(is_fp(y)) False >>> print(is_real(x)) False """ if z3_debug(): _z3_assert(is_fp(x), "First argument must be a Z3 floating-point expression") ctx = _get_ctx(ctx) return ArithRef(Z3_mk_fpa_to_real(ctx.ref(), x.ast), ctx) def fpToIEEEBV(x, ctx=None): """\brief Conversion of a floating-point term into a bit-vector term in IEEE 754-2008 format. The size of the resulting bit-vector is automatically determined. Note that IEEE 754-2008 allows multiple different representations of NaN. This conversion knows only one NaN and it will always produce the same bit-vector representation of that NaN. >>> x = FP('x', FPSort(8, 24)) >>> y = fpToIEEEBV(x) >>> print(is_fp(x)) True >>> print(is_bv(y)) True >>> print(is_fp(y)) False >>> print(is_bv(x)) False """ if z3_debug(): _z3_assert(is_fp(x), "First argument must be a Z3 floating-point expression") ctx = _get_ctx(ctx) return BitVecRef(Z3_mk_fpa_to_ieee_bv(ctx.ref(), x.ast), ctx) ######################################### # # Strings, Sequences and Regular expressions # ######################################### class SeqSortRef(SortRef): """Sequence sort.""" def is_string(self): """Determine if sort is a string >>> s = StringSort() >>> s.is_string() True >>> s = SeqSort(IntSort()) >>> s.is_string() False """ return Z3_is_string_sort(self.ctx_ref(), self.ast) def basis(self): return _to_sort_ref(Z3_get_seq_sort_basis(self.ctx_ref(), self.ast), self.ctx) def StringSort(ctx=None): """Create a string sort >>> s = StringSort() >>> print(s) String """ ctx = _get_ctx(ctx) return SeqSortRef(Z3_mk_string_sort(ctx.ref()), ctx) def SeqSort(s): """Create a sequence sort over elements provided in the argument >>> s = SeqSort(IntSort()) >>> s == Unit(IntVal(1)).sort() True """ return SeqSortRef(Z3_mk_seq_sort(s.ctx_ref(), s.ast), s.ctx) class SeqRef(ExprRef): """Sequence expression.""" def sort(self): return SeqSortRef(Z3_get_sort(self.ctx_ref(), self.as_ast()), self.ctx) def __add__(self, other): return Concat(self, other) def __radd__(self, other): return Concat(other, self) def __getitem__(self, i): if _is_int(i): i = IntVal(i, self.ctx) return _to_expr_ref(Z3_mk_seq_nth(self.ctx_ref(), self.as_ast(), i.as_ast()), self.ctx) def at(self, i): if _is_int(i): i = IntVal(i, self.ctx) return SeqRef(Z3_mk_seq_at(self.ctx_ref(), self.as_ast(), i.as_ast()), self.ctx) def is_string(self): return Z3_is_string_sort(self.ctx_ref(), Z3_get_sort(self.ctx_ref(), self.as_ast())) def is_string_value(self): return Z3_is_string(self.ctx_ref(), self.as_ast()) 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()) def __le__(self, other): return SeqRef(Z3_mk_str_le(self.ctx_ref(), self.as_ast(), other.as_ast()), self.ctx) def __lt__(self, other): return SeqRef(Z3_mk_str_lt(self.ctx_ref(), self.as_ast(), other.as_ast()), self.ctx) def __ge__(self, other): return SeqRef(Z3_mk_str_le(self.ctx_ref(), other.as_ast(), self.as_ast()), self.ctx) def __gt__(self, other): return SeqRef(Z3_mk_str_lt(self.ctx_ref(), other.as_ast(), self.as_ast()), self.ctx) def _coerce_seq(s, ctx=None): if isinstance(s, str): ctx = _get_ctx(ctx) s = StringVal(s, ctx) if not is_expr(s): raise Z3Exception("Non-expression passed as a sequence") if not is_seq(s): raise Z3Exception("Non-sequence passed as a sequence") return s def _get_ctx2(a, b, ctx=None): if is_expr(a): return a.ctx if is_expr(b): return b.ctx if ctx is None: ctx = main_ctx() return ctx def is_seq(a): """Return `True` if `a` is a Z3 sequence expression. >>> print (is_seq(Unit(IntVal(0)))) True >>> print (is_seq(StringVal("abc"))) True """ return isinstance(a, SeqRef) def is_string(a): """Return `True` if `a` is a Z3 string expression. >>> print (is_string(StringVal("ab"))) True """ return isinstance(a, SeqRef) and a.is_string() def is_string_value(a): """return 'True' if 'a' is a Z3 string constant expression. >>> print (is_string_value(StringVal("a"))) True >>> print (is_string_value(StringVal("a") + StringVal("b"))) False """ return isinstance(a, SeqRef) and a.is_string_value() def StringVal(s, ctx=None): """create a string expression""" ctx = _get_ctx(ctx) return SeqRef(Z3_mk_lstring(ctx.ref(), len(s), s), ctx) def String(name, ctx=None): """Return a string constant named `name`. If `ctx=None`, then the global context is used. >>> x = String('x') """ ctx = _get_ctx(ctx) return SeqRef(Z3_mk_const(ctx.ref(), to_symbol(name, ctx), StringSort(ctx).ast), ctx) def Strings(names, ctx=None): """Return string constants""" ctx = _get_ctx(ctx) if isinstance(names, str): names = names.split(" ") return [String(name, ctx) for name in names] def SubString(s, offset, length): """Extract substring or subsequence starting at offset""" return Extract(s, offset, length) def SubSeq(s, offset, length): """Extract substring or subsequence starting at offset""" return Extract(s, offset, length) def Strings(names, ctx=None): """Return a tuple of String constants. """ ctx = _get_ctx(ctx) if isinstance(names, str): names = names.split(" ") return [String(name, ctx) for name in names] def Empty(s): """Create the empty sequence of the given sort >>> e = Empty(StringSort()) >>> e2 = StringVal("") >>> print(e.eq(e2)) True >>> e3 = Empty(SeqSort(IntSort())) >>> print(e3) Empty(Seq(Int)) >>> e4 = Empty(ReSort(SeqSort(IntSort()))) >>> print(e4) Empty(ReSort(Seq(Int))) """ if isinstance(s, SeqSortRef): return SeqRef(Z3_mk_seq_empty(s.ctx_ref(), s.ast), s.ctx) if isinstance(s, ReSortRef): return ReRef(Z3_mk_re_empty(s.ctx_ref(), s.ast), s.ctx) raise Z3Exception("Non-sequence, non-regular expression sort passed to Empty") def Full(s): """Create the regular expression that accepts the universal language >>> e = Full(ReSort(SeqSort(IntSort()))) >>> print(e) Full(ReSort(Seq(Int))) >>> e1 = Full(ReSort(StringSort())) >>> print(e1) Full(ReSort(String)) """ if isinstance(s, ReSortRef): return ReRef(Z3_mk_re_full(s.ctx_ref(), s.ast), s.ctx) raise Z3Exception("Non-sequence, non-regular expression sort passed to Full") def Unit(a): """Create a singleton sequence""" return SeqRef(Z3_mk_seq_unit(a.ctx_ref(), a.as_ast()), a.ctx) def PrefixOf(a, b): """Check if 'a' is a prefix of 'b' >>> s1 = PrefixOf("ab", "abc") >>> simplify(s1) True >>> s2 = PrefixOf("bc", "abc") >>> simplify(s2) False """ ctx = _get_ctx2(a, b) a = _coerce_seq(a, ctx) b = _coerce_seq(b, ctx) return BoolRef(Z3_mk_seq_prefix(a.ctx_ref(), a.as_ast(), b.as_ast()), a.ctx) def SuffixOf(a, b): """Check if 'a' is a suffix of 'b' >>> s1 = SuffixOf("ab", "abc") >>> simplify(s1) False >>> s2 = SuffixOf("bc", "abc") >>> simplify(s2) True """ ctx = _get_ctx2(a, b) a = _coerce_seq(a, ctx) b = _coerce_seq(b, ctx) return BoolRef(Z3_mk_seq_suffix(a.ctx_ref(), a.as_ast(), b.as_ast()), a.ctx) def Contains(a, b): """Check if 'a' contains 'b' >>> s1 = Contains("abc", "ab") >>> simplify(s1) True >>> s2 = Contains("abc", "bc") >>> simplify(s2) True >>> x, y, z = Strings('x y z') >>> s3 = Contains(Concat(x,y,z), y) >>> simplify(s3) True """ ctx = _get_ctx2(a, b) a = _coerce_seq(a, ctx) b = _coerce_seq(b, ctx) return BoolRef(Z3_mk_seq_contains(a.ctx_ref(), a.as_ast(), b.as_ast()), a.ctx) def Replace(s, src, dst): """Replace the first occurrence of 'src' by 'dst' in 's' >>> r = Replace("aaa", "a", "b") >>> simplify(r) "baa" """ ctx = _get_ctx2(dst, s) if ctx is None and is_expr(src): ctx = src.ctx src = _coerce_seq(src, ctx) dst = _coerce_seq(dst, ctx) s = _coerce_seq(s, ctx) return SeqRef(Z3_mk_seq_replace(src.ctx_ref(), s.as_ast(), src.as_ast(), dst.as_ast()), s.ctx) def IndexOf(s, substr): return IndexOf(s, substr, IntVal(0)) def IndexOf(s, substr, offset): """Retrieve the index of substring within a string starting at a specified offset. >>> simplify(IndexOf("abcabc", "bc", 0)) 1 >>> simplify(IndexOf("abcabc", "bc", 2)) 4 """ ctx = None if is_expr(offset): ctx = offset.ctx ctx = _get_ctx2(s, substr, ctx) s = _coerce_seq(s, ctx) substr = _coerce_seq(substr, ctx) if _is_int(offset): offset = IntVal(offset, ctx) return ArithRef(Z3_mk_seq_index(s.ctx_ref(), s.as_ast(), substr.as_ast(), offset.as_ast()), s.ctx) def LastIndexOf(s, substr): """Retrieve the last index of substring within a string""" ctx = None ctx = _get_ctx2(s, substr, ctx) s = _coerce_seq(s, ctx) substr = _coerce_seq(substr, ctx) return ArithRef(Z3_mk_seq_last_index(s.ctx_ref(), s.as_ast(), substr.as_ast()), s.ctx) def Length(s): """Obtain the length of a sequence 's' >>> l = Length(StringVal("abc")) >>> simplify(l) 3 """ s = _coerce_seq(s) return ArithRef(Z3_mk_seq_length(s.ctx_ref(), s.as_ast()), s.ctx) def StrToInt(s): """Convert string expression to integer >>> a = StrToInt("1") >>> simplify(1 == a) True >>> b = StrToInt("2") >>> simplify(1 == b) False >>> c = StrToInt(IntToStr(2)) >>> simplify(1 == c) False """ s = _coerce_seq(s) return ArithRef(Z3_mk_str_to_int(s.ctx_ref(), s.as_ast()), s.ctx) def IntToStr(s): """Convert integer expression to string""" if not is_expr(s): s = _py2expr(s) return SeqRef(Z3_mk_int_to_str(s.ctx_ref(), s.as_ast()), s.ctx) def Re(s, ctx=None): """The regular expression that accepts sequence 's' >>> s1 = Re("ab") >>> s2 = Re(StringVal("ab")) >>> s3 = Re(Unit(BoolVal(True))) """ s = _coerce_seq(s, ctx) return ReRef(Z3_mk_seq_to_re(s.ctx_ref(), s.as_ast()), s.ctx) ## Regular expressions class ReSortRef(SortRef): """Regular expression sort.""" def basis(self): return _to_sort_ref(Z3_get_re_sort_basis(self.ctx_ref(), self.ast), self.ctx) def ReSort(s): if is_ast(s): return ReSortRef(Z3_mk_re_sort(s.ctx.ref(), s.ast), s.ctx) if s is None or isinstance(s, Context): ctx = _get_ctx(s) return ReSortRef(Z3_mk_re_sort(ctx.ref(), Z3_mk_string_sort(ctx.ref())), s.ctx) raise Z3Exception("Regular expression sort constructor expects either a string or a context or no argument") class ReRef(ExprRef): """Regular expressions.""" def __add__(self, other): return Union(self, other) def is_re(s): return isinstance(s, ReRef) def InRe(s, re): """Create regular expression membership test >>> re = Union(Re("a"),Re("b")) >>> print (simplify(InRe("a", re))) True >>> print (simplify(InRe("b", re))) True >>> print (simplify(InRe("c", re))) False """ s = _coerce_seq(s, re.ctx) return BoolRef(Z3_mk_seq_in_re(s.ctx_ref(), s.as_ast(), re.as_ast()), s.ctx) def Union(*args): """Create union of regular expressions. >>> re = Union(Re("a"), Re("b"), Re("c")) >>> print (simplify(InRe("d", re))) False """ args = _get_args(args) sz = len(args) if z3_debug(): _z3_assert(sz > 0, "At least one argument expected.") _z3_assert(all([is_re(a) for a in args]), "All arguments must be regular expressions.") if sz == 1: return args[0] ctx = args[0].ctx v = (Ast * sz)() for i in range(sz): v[i] = args[i].as_ast() return ReRef(Z3_mk_re_union(ctx.ref(), sz, v), ctx) def Intersect(*args): """Create intersection of regular expressions. >>> re = Intersect(Re("a"), Re("b"), Re("c")) """ args = _get_args(args) sz = len(args) if z3_debug(): _z3_assert(sz > 0, "At least one argument expected.") _z3_assert(all([is_re(a) for a in args]), "All arguments must be regular expressions.") if sz == 1: return args[0] ctx = args[0].ctx v = (Ast * sz)() for i in range(sz): v[i] = args[i].as_ast() return ReRef(Z3_mk_re_intersect(ctx.ref(), sz, v), ctx) def Plus(re): """Create the regular expression accepting one or more repetitions of argument. >>> re = Plus(Re("a")) >>> print(simplify(InRe("aa", re))) True >>> print(simplify(InRe("ab", re))) False >>> print(simplify(InRe("", re))) False """ return ReRef(Z3_mk_re_plus(re.ctx_ref(), re.as_ast()), re.ctx) def Option(re): """Create the regular expression that optionally accepts the argument. >>> re = Option(Re("a")) >>> print(simplify(InRe("a", re))) True >>> print(simplify(InRe("", re))) True >>> print(simplify(InRe("aa", re))) False """ return ReRef(Z3_mk_re_option(re.ctx_ref(), re.as_ast()), re.ctx) def Complement(re): """Create the complement regular expression.""" return ReRef(Z3_mk_re_complement(re.ctx_ref(), re.as_ast()), re.ctx) def Star(re): """Create the regular expression accepting zero or more repetitions of argument. >>> re = Star(Re("a")) >>> print(simplify(InRe("aa", re))) True >>> print(simplify(InRe("ab", re))) False >>> print(simplify(InRe("", re))) True """ return ReRef(Z3_mk_re_star(re.ctx_ref(), re.as_ast()), re.ctx) def Loop(re, lo, hi=0): """Create the regular expression accepting between a lower and upper bound repetitions >>> re = Loop(Re("a"), 1, 3) >>> print(simplify(InRe("aa", re))) True >>> print(simplify(InRe("aaaa", re))) False >>> print(simplify(InRe("", re))) False """ return ReRef(Z3_mk_re_loop(re.ctx_ref(), re.as_ast(), lo, hi), re.ctx) def Range(lo, hi, ctx = None): """Create the range regular expression over two sequences of length 1 >>> range = Range("a","z") >>> print(simplify(InRe("b", range))) True >>> print(simplify(InRe("bb", range))) False """ lo = _coerce_seq(lo, ctx) hi = _coerce_seq(hi, ctx) return ReRef(Z3_mk_re_range(lo.ctx_ref(), lo.ast, hi.ast), lo.ctx) # Special Relations def PartialOrder(a, index): return FuncDeclRef(Z3_mk_partial_order(a.ctx_ref(), a.ast, index), a.ctx); def LinearOrder(a, index): return FuncDeclRef(Z3_mk_linear_order(a.ctx_ref(), a.ast, index), a.ctx); def TreeOrder(a, index): return FuncDeclRef(Z3_mk_tree_order(a.ctx_ref(), a.ast, index), a.ctx); def PiecewiseLinearOrder(a, index): return FuncDeclRef(Z3_mk_piecewise_linear_order(a.ctx_ref(), a.ast, index), a.ctx); def TransitiveClosure(f): """Given a binary relation R, such that the two arguments have the same sort create the transitive closure relation R+. The transitive closure R+ is a new relation. """ return FuncDeclRef(Z3_mk_transitive_closure(f.ctx_ref(), f.ast), f.ctx) z3-z3-4.8.7/src/api/python/z3/z3num.py000066400000000000000000000375711356505360400173070ustar00rootroot00000000000000############################################ # Copyright (c) 2012 Microsoft Corporation # # Z3 Python interface for Z3 numerals # # Author: Leonardo de Moura (leonardo) ############################################ from .z3 import * from .z3core import * from .z3printer import * from fractions import Fraction from .z3 import _get_ctx def _to_numeral(num, ctx=None): if isinstance(num, Numeral): return num else: return Numeral(num, ctx) class Numeral: """ A Z3 numeral can be used to perform computations over arbitrary precision integers, rationals and real algebraic numbers. It also automatically converts python numeric values. >>> Numeral(2) 2 >>> Numeral("3/2") + 1 5/2 >>> Numeral(Sqrt(2)) 1.4142135623? >>> Numeral(Sqrt(2)) + 2 3.4142135623? >>> Numeral(Sqrt(2)) + Numeral(Sqrt(3)) 3.1462643699? Z3 numerals can be used to perform computations with values in a Z3 model. >>> s = Solver() >>> x = Real('x') >>> s.add(x*x == 2) >>> s.add(x > 0) >>> s.check() sat >>> m = s.model() >>> m[x] 1.4142135623? >>> m[x] + 1 1.4142135623? + 1 The previous result is a Z3 expression. >>> (m[x] + 1).sexpr() '(+ (root-obj (+ (^ x 2) (- 2)) 2) 1.0)' >>> Numeral(m[x]) + 1 2.4142135623? >>> Numeral(m[x]).is_pos() True >>> Numeral(m[x])**2 2 We can also isolate the roots of polynomials. >>> x0, x1, x2 = RealVarVector(3) >>> r0 = isolate_roots(x0**5 - x0 - 1) >>> r0 [1.1673039782?] In the following example, we are isolating the roots of a univariate polynomial (on x1) obtained after substituting x0 -> r0[0] >>> r1 = isolate_roots(x1**2 - x0 + 1, [ r0[0] ]) >>> r1 [-0.4090280898?, 0.4090280898?] Similarly, in the next example we isolate the roots of a univariate polynomial (on x2) obtained after substituting x0 -> r0[0] and x1 -> r1[0] >>> isolate_roots(x1*x2 + x0, [ r0[0], r1[0] ]) [2.8538479564?] """ def __init__(self, num, ctx=None): if isinstance(num, Ast): self.ast = num self.ctx = _get_ctx(ctx) elif isinstance(num, RatNumRef) or isinstance(num, AlgebraicNumRef): self.ast = num.ast self.ctx = num.ctx elif isinstance(num, ArithRef): r = simplify(num) self.ast = r.ast self.ctx = r.ctx else: v = RealVal(num, ctx) self.ast = v.ast self.ctx = v.ctx Z3_inc_ref(self.ctx_ref(), self.as_ast()) assert Z3_algebraic_is_value(self.ctx_ref(), self.ast) def __del__(self): Z3_dec_ref(self.ctx_ref(), self.as_ast()) def is_integer(self): """ Return True if the numeral is integer. >>> Numeral(2).is_integer() True >>> (Numeral(Sqrt(2)) * Numeral(Sqrt(2))).is_integer() True >>> Numeral(Sqrt(2)).is_integer() False >>> Numeral("2/3").is_integer() False """ return self.is_rational() and self.denominator() == 1 def is_rational(self): """ Return True if the numeral is rational. >>> Numeral(2).is_rational() True >>> Numeral("2/3").is_rational() True >>> Numeral(Sqrt(2)).is_rational() False """ return Z3_get_ast_kind(self.ctx_ref(), self.as_ast()) == Z3_NUMERAL_AST def denominator(self): """ Return the denominator if `self` is rational. >>> Numeral("2/3").denominator() 3 """ assert(self.is_rational()) return Numeral(Z3_get_denominator(self.ctx_ref(), self.as_ast()), self.ctx) def numerator(self): """ Return the numerator if `self` is rational. >>> Numeral("2/3").numerator() 2 """ assert(self.is_rational()) return Numeral(Z3_get_numerator(self.ctx_ref(), self.as_ast()), self.ctx) def is_irrational(self): """ Return True if the numeral is irrational. >>> Numeral(2).is_irrational() False >>> Numeral("2/3").is_irrational() False >>> Numeral(Sqrt(2)).is_irrational() True """ return not self.is_rational() def as_long(self): """ Return a numeral (that is an integer) as a Python long. """ assert(self.is_integer()) if sys.version_info[0] >= 3: return int(Z3_get_numeral_string(self.ctx_ref(), self.as_ast())) else: return long(Z3_get_numeral_string(self.ctx_ref(), self.as_ast())) def as_fraction(self): """ Return a numeral (that is a rational) as a Python Fraction. >>> Numeral("1/5").as_fraction() Fraction(1, 5) """ assert(self.is_rational()) return Fraction(self.numerator().as_long(), self.denominator().as_long()) def approx(self, precision=10): """Return a numeral that approximates the numeral `self`. The result `r` is such that |r - self| <= 1/10^precision If `self` is rational, then the result is `self`. >>> x = Numeral(2).root(2) >>> x.approx(20) 6838717160008073720548335/4835703278458516698824704 >>> x.approx(5) 2965821/2097152 >>> Numeral(2).approx(10) 2 """ return self.upper(precision) def upper(self, precision=10): """Return a upper bound that approximates the numeral `self`. The result `r` is such that r - self <= 1/10^precision If `self` is rational, then the result is `self`. >>> x = Numeral(2).root(2) >>> x.upper(20) 6838717160008073720548335/4835703278458516698824704 >>> x.upper(5) 2965821/2097152 >>> Numeral(2).upper(10) 2 """ if self.is_rational(): return self else: return Numeral(Z3_get_algebraic_number_upper(self.ctx_ref(), self.as_ast(), precision), self.ctx) def lower(self, precision=10): """Return a lower bound that approximates the numeral `self`. The result `r` is such that self - r <= 1/10^precision If `self` is rational, then the result is `self`. >>> x = Numeral(2).root(2) >>> x.lower(20) 1709679290002018430137083/1208925819614629174706176 >>> Numeral("2/3").lower(10) 2/3 """ if self.is_rational(): return self else: return Numeral(Z3_get_algebraic_number_lower(self.ctx_ref(), self.as_ast(), precision), self.ctx) def sign(self): """ Return the sign of the numeral. >>> Numeral(2).sign() 1 >>> Numeral(-3).sign() -1 >>> Numeral(0).sign() 0 """ return Z3_algebraic_sign(self.ctx_ref(), self.ast) def is_pos(self): """ Return True if the numeral is positive. >>> Numeral(2).is_pos() True >>> Numeral(-3).is_pos() False >>> Numeral(0).is_pos() False """ return Z3_algebraic_is_pos(self.ctx_ref(), self.ast) def is_neg(self): """ Return True if the numeral is negative. >>> Numeral(2).is_neg() False >>> Numeral(-3).is_neg() True >>> Numeral(0).is_neg() False """ return Z3_algebraic_is_neg(self.ctx_ref(), self.ast) def is_zero(self): """ Return True if the numeral is zero. >>> Numeral(2).is_zero() False >>> Numeral(-3).is_zero() False >>> Numeral(0).is_zero() True >>> sqrt2 = Numeral(2).root(2) >>> sqrt2.is_zero() False >>> (sqrt2 - sqrt2).is_zero() True """ return Z3_algebraic_is_zero(self.ctx_ref(), self.ast) def __add__(self, other): """ Return the numeral `self + other`. >>> Numeral(2) + 3 5 >>> Numeral(2) + Numeral(4) 6 >>> Numeral("2/3") + 1 5/3 """ return Numeral(Z3_algebraic_add(self.ctx_ref(), self.ast, _to_numeral(other, self.ctx).ast), self.ctx) def __radd__(self, other): """ Return the numeral `other + self`. >>> 3 + Numeral(2) 5 """ return Numeral(Z3_algebraic_add(self.ctx_ref(), self.ast, _to_numeral(other, self.ctx).ast), self.ctx) def __sub__(self, other): """ Return the numeral `self - other`. >>> Numeral(2) - 3 -1 """ return Numeral(Z3_algebraic_sub(self.ctx_ref(), self.ast, _to_numeral(other, self.ctx).ast), self.ctx) def __rsub__(self, other): """ Return the numeral `other - self`. >>> 3 - Numeral(2) 1 """ return Numeral(Z3_algebraic_sub(self.ctx_ref(), _to_numeral(other, self.ctx).ast, self.ast), self.ctx) def __mul__(self, other): """ Return the numeral `self * other`. >>> Numeral(2) * 3 6 """ return Numeral(Z3_algebraic_mul(self.ctx_ref(), self.ast, _to_numeral(other, self.ctx).ast), self.ctx) def __rmul__(self, other): """ Return the numeral `other * mul`. >>> 3 * Numeral(2) 6 """ return Numeral(Z3_algebraic_mul(self.ctx_ref(), self.ast, _to_numeral(other, self.ctx).ast), self.ctx) def __div__(self, other): """ Return the numeral `self / other`. >>> Numeral(2) / 3 2/3 >>> Numeral(2).root(2) / 3 0.4714045207? >>> Numeral(Sqrt(2)) / Numeral(Sqrt(3)) 0.8164965809? """ return Numeral(Z3_algebraic_div(self.ctx_ref(), self.ast, _to_numeral(other, self.ctx).ast), self.ctx) def __truediv__(self, other): return self.__div__(other) def __rdiv__(self, other): """ Return the numeral `other / self`. >>> 3 / Numeral(2) 3/2 >>> 3 / Numeral(2).root(2) 2.1213203435? """ return Numeral(Z3_algebraic_div(self.ctx_ref(), _to_numeral(other, self.ctx).ast, self.ast), self.ctx) def __rtruediv__(self, other): return self.__rdiv__(other) def root(self, k): """ Return the numeral `self^(1/k)`. >>> sqrt2 = Numeral(2).root(2) >>> sqrt2 1.4142135623? >>> sqrt2 * sqrt2 2 >>> sqrt2 * 2 + 1 3.8284271247? >>> (sqrt2 * 2 + 1).sexpr() '(root-obj (+ (^ x 2) (* (- 2) x) (- 7)) 2)' """ return Numeral(Z3_algebraic_root(self.ctx_ref(), self.ast, k), self.ctx) def power(self, k): """ Return the numeral `self^k`. >>> sqrt3 = Numeral(3).root(2) >>> sqrt3 1.7320508075? >>> sqrt3.power(2) 3 """ return Numeral(Z3_algebraic_power(self.ctx_ref(), self.ast, k), self.ctx) def __pow__(self, k): """ Return the numeral `self^k`. >>> sqrt3 = Numeral(3).root(2) >>> sqrt3 1.7320508075? >>> sqrt3**2 3 """ return self.power(k) def __lt__(self, other): """ Return True if `self < other`. >>> Numeral(Sqrt(2)) < 2 True >>> Numeral(Sqrt(3)) < Numeral(Sqrt(2)) False >>> Numeral(Sqrt(2)) < Numeral(Sqrt(2)) False """ return Z3_algebraic_lt(self.ctx_ref(), self.ast, _to_numeral(other, self.ctx).ast) def __rlt__(self, other): """ Return True if `other < self`. >>> 2 < Numeral(Sqrt(2)) False """ return self > other def __gt__(self, other): """ Return True if `self > other`. >>> Numeral(Sqrt(2)) > 2 False >>> Numeral(Sqrt(3)) > Numeral(Sqrt(2)) True >>> Numeral(Sqrt(2)) > Numeral(Sqrt(2)) False """ return Z3_algebraic_gt(self.ctx_ref(), self.ast, _to_numeral(other, self.ctx).ast) def __rgt__(self, other): """ Return True if `other > self`. >>> 2 > Numeral(Sqrt(2)) True """ return self < other def __le__(self, other): """ Return True if `self <= other`. >>> Numeral(Sqrt(2)) <= 2 True >>> Numeral(Sqrt(3)) <= Numeral(Sqrt(2)) False >>> Numeral(Sqrt(2)) <= Numeral(Sqrt(2)) True """ return Z3_algebraic_le(self.ctx_ref(), self.ast, _to_numeral(other, self.ctx).ast) def __rle__(self, other): """ Return True if `other <= self`. >>> 2 <= Numeral(Sqrt(2)) False """ return self >= other def __ge__(self, other): """ Return True if `self >= other`. >>> Numeral(Sqrt(2)) >= 2 False >>> Numeral(Sqrt(3)) >= Numeral(Sqrt(2)) True >>> Numeral(Sqrt(2)) >= Numeral(Sqrt(2)) True """ return Z3_algebraic_ge(self.ctx_ref(), self.ast, _to_numeral(other, self.ctx).ast) def __rge__(self, other): """ Return True if `other >= self`. >>> 2 >= Numeral(Sqrt(2)) True """ return self <= other def __eq__(self, other): """ Return True if `self == other`. >>> Numeral(Sqrt(2)) == 2 False >>> Numeral(Sqrt(3)) == Numeral(Sqrt(2)) False >>> Numeral(Sqrt(2)) == Numeral(Sqrt(2)) True """ return Z3_algebraic_eq(self.ctx_ref(), self.ast, _to_numeral(other, self.ctx).ast) def __ne__(self, other): """ Return True if `self != other`. >>> Numeral(Sqrt(2)) != 2 True >>> Numeral(Sqrt(3)) != Numeral(Sqrt(2)) True >>> Numeral(Sqrt(2)) != Numeral(Sqrt(2)) False """ return Z3_algebraic_neq(self.ctx_ref(), self.ast, _to_numeral(other, self.ctx).ast) def __str__(self): if Z3_is_numeral_ast(self.ctx_ref(), self.ast): return str(RatNumRef(self.ast, self.ctx)) else: return str(AlgebraicNumRef(self.ast, self.ctx)) def __repr__(self): return self.__str__() def sexpr(self): return Z3_ast_to_string(self.ctx_ref(), self.as_ast()) def as_ast(self): return self.ast def ctx_ref(self): return self.ctx.ref() def eval_sign_at(p, vs): """ Evaluate the sign of the polynomial `p` at `vs`. `p` is a Z3 Expression containing arithmetic operators: +, -, *, ^k where k is an integer; and free variables x that is_var(x) is True. Moreover, all variables must be real. The result is 1 if the polynomial is positive at the given point, -1 if negative, and 0 if zero. >>> x0, x1, x2 = RealVarVector(3) >>> eval_sign_at(x0**2 + x1*x2 + 1, (Numeral(0), Numeral(1), Numeral(2))) 1 >>> eval_sign_at(x0**2 - 2, [ Numeral(Sqrt(2)) ]) 0 >>> eval_sign_at((x0 + x1)*(x0 + x2), (Numeral(0), Numeral(Sqrt(2)), Numeral(Sqrt(3)))) 1 """ num = len(vs) _vs = (Ast * num)() for i in range(num): _vs[i] = vs[i].ast return Z3_algebraic_eval(p.ctx_ref(), p.as_ast(), num, _vs) def isolate_roots(p, vs=[]): """ Given a multivariate polynomial p(x_0, ..., x_{n-1}, x_n), returns the roots of the univariate polynomial p(vs[0], ..., vs[len(vs)-1], x_n). Remarks: * p is a Z3 expression that contains only arithmetic terms and free variables. * forall i in [0, n) vs is a numeral. The result is a list of numerals >>> x0 = RealVar(0) >>> isolate_roots(x0**5 - x0 - 1) [1.1673039782?] >>> x1 = RealVar(1) >>> isolate_roots(x0**2 - x1**4 - 1, [ Numeral(Sqrt(3)) ]) [-1.1892071150?, 1.1892071150?] >>> x2 = RealVar(2) >>> isolate_roots(x2**2 + x0 - x1, [ Numeral(Sqrt(3)), Numeral(Sqrt(2)) ]) [] """ num = len(vs) _vs = (Ast * num)() for i in range(num): _vs[i] = vs[i].ast _roots = AstVector(Z3_algebraic_roots(p.ctx_ref(), p.as_ast(), num, _vs), p.ctx) return [ Numeral(r) for r in _roots ] z3-z3-4.8.7/src/api/python/z3/z3poly.py000066400000000000000000000021441356505360400174570ustar00rootroot00000000000000############################################ # Copyright (c) 2012 Microsoft Corporation # # Z3 Python interface for Z3 polynomials # # Author: Leonardo de Moura (leonardo) ############################################ from .z3 import * def subresultants(p, q, x): """ Return the non-constant subresultants of 'p' and 'q' with respect to the "variable" 'x'. 'p', 'q' and 'x' are Z3 expressions where 'p' and '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 b in the polynomial f(a)*f(a) + 2*f(a) + 1 >>> x, y = Reals('x y') >>> subresultants(2*x + y, 3*x - 2*y + 2, x) [-7*y + 4] >>> r = subresultants(3*y*x**2 + y**3 + 1, 2*x**3 + y + 3, x) >>> r[0] 4*y**9 + 12*y**6 + 27*y**5 + 162*y**4 + 255*y**3 + 4 >>> r[1] -6*y**4 + -6*y """ return AstVector(Z3_polynomial_subresultants(p.ctx_ref(), p.as_ast(), q.as_ast(), x.as_ast()), p.ctx) if __name__ == "__main__": import doctest if doctest.testmod().failed: exit(1) z3-z3-4.8.7/src/api/python/z3/z3printer.py000066400000000000000000001272141356505360400201650ustar00rootroot00000000000000############################################ # Copyright (c) 2012 Microsoft Corporation # # Z3 Python interface # # Author: Leonardo de Moura (leonardo) ############################################ import sys, io, z3 from .z3consts import * from .z3core import * from ctypes import * def _z3_assert(cond, msg): if not cond: raise Z3Exception(msg) ############################## # # Configuration # ############################## # Z3 operator names to Z3Py _z3_op_to_str = { Z3_OP_TRUE : 'True', Z3_OP_FALSE : 'False', Z3_OP_EQ : '==', Z3_OP_DISTINCT : 'Distinct', Z3_OP_ITE : 'If', Z3_OP_AND : 'And', Z3_OP_OR : 'Or', Z3_OP_IFF : '==', Z3_OP_XOR : 'Xor', Z3_OP_NOT : 'Not', Z3_OP_IMPLIES : 'Implies', Z3_OP_IDIV : '/', Z3_OP_MOD : '%', Z3_OP_TO_REAL : 'ToReal', Z3_OP_TO_INT : 'ToInt', Z3_OP_POWER : '**', Z3_OP_IS_INT : 'IsInt', Z3_OP_BADD : '+', Z3_OP_BSUB : '-', Z3_OP_BMUL : '*', Z3_OP_BOR : '|', Z3_OP_BAND : '&', Z3_OP_BNOT : '~', Z3_OP_BXOR : '^', Z3_OP_BNEG : '-', Z3_OP_BUDIV : 'UDiv', Z3_OP_BSDIV : '/', Z3_OP_BSMOD : '%', Z3_OP_BSREM : 'SRem', Z3_OP_BUREM : 'URem', Z3_OP_EXT_ROTATE_LEFT : 'RotateLeft', Z3_OP_EXT_ROTATE_RIGHT : 'RotateRight', Z3_OP_SLEQ : '<=', Z3_OP_SLT : '<', Z3_OP_SGEQ : '>=', Z3_OP_SGT : '>', Z3_OP_ULEQ : 'ULE', Z3_OP_ULT : 'ULT', Z3_OP_UGEQ : 'UGE', Z3_OP_UGT : 'UGT', Z3_OP_SIGN_EXT : 'SignExt', Z3_OP_ZERO_EXT : 'ZeroExt', Z3_OP_REPEAT : 'RepeatBitVec', Z3_OP_BASHR : '>>', Z3_OP_BSHL : '<<', Z3_OP_BLSHR : 'LShR', Z3_OP_CONCAT : 'Concat', Z3_OP_EXTRACT : 'Extract', Z3_OP_BV2INT : 'BV2Int', Z3_OP_ARRAY_MAP : 'Map', Z3_OP_SELECT : 'Select', Z3_OP_STORE : 'Store', Z3_OP_CONST_ARRAY : 'K', Z3_OP_ARRAY_EXT : 'Ext', Z3_OP_PB_AT_MOST : 'AtMost', Z3_OP_PB_LE : 'PbLe', Z3_OP_PB_GE : 'PbGe', Z3_OP_PB_EQ : 'PbEq', Z3_OP_SEQ_CONCAT : 'Concat', Z3_OP_SEQ_PREFIX : 'PrefixOf', Z3_OP_SEQ_SUFFIX : 'SuffixOf', Z3_OP_SEQ_UNIT : 'Unit', Z3_OP_SEQ_CONTAINS : 'Contains' , Z3_OP_SEQ_REPLACE : 'Replace', Z3_OP_SEQ_AT : 'At', Z3_OP_SEQ_NTH : 'Nth', Z3_OP_SEQ_INDEX : 'IndexOf', Z3_OP_SEQ_LAST_INDEX : 'LastIndexOf', Z3_OP_SEQ_LENGTH : 'Length', Z3_OP_STR_TO_INT : 'StrToInt', Z3_OP_INT_TO_STR : 'IntToStr', Z3_OP_SEQ_IN_RE : 'InRe', Z3_OP_SEQ_TO_RE : 'Re', Z3_OP_RE_PLUS : 'Plus', Z3_OP_RE_STAR : 'Star', Z3_OP_RE_OPTION : 'Option', Z3_OP_RE_UNION : 'Union', Z3_OP_RE_RANGE : 'Range', Z3_OP_RE_INTERSECT : 'Intersect', Z3_OP_RE_COMPLEMENT : 'Complement', Z3_OP_FPA_IS_NAN : 'fpIsNaN', Z3_OP_FPA_IS_INF : 'fpIsInf', Z3_OP_FPA_IS_ZERO : 'fpIsZero', Z3_OP_FPA_IS_NORMAL : 'fpIsNormal', Z3_OP_FPA_IS_SUBNORMAL : 'fpIsSubnormal', Z3_OP_FPA_IS_NEGATIVE : 'fpIsNegative', Z3_OP_FPA_IS_POSITIVE : 'fpIsPositive', } # List of infix operators _z3_infix = [ Z3_OP_EQ, Z3_OP_IFF, Z3_OP_ADD, Z3_OP_SUB, Z3_OP_MUL, Z3_OP_DIV, Z3_OP_IDIV, Z3_OP_MOD, Z3_OP_POWER, Z3_OP_LE, Z3_OP_LT, Z3_OP_GE, Z3_OP_GT, Z3_OP_BADD, Z3_OP_BSUB, Z3_OP_BMUL, Z3_OP_BSDIV, Z3_OP_BSMOD, Z3_OP_BOR, Z3_OP_BAND, Z3_OP_BXOR, Z3_OP_BSDIV, Z3_OP_SLEQ, Z3_OP_SLT, Z3_OP_SGEQ, Z3_OP_SGT, Z3_OP_BASHR, Z3_OP_BSHL ] _z3_unary = [ Z3_OP_UMINUS, Z3_OP_BNOT, Z3_OP_BNEG ] # Precedence _z3_precedence = { Z3_OP_POWER : 0, Z3_OP_UMINUS : 1, Z3_OP_BNEG : 1, Z3_OP_BNOT : 1, Z3_OP_MUL : 2, Z3_OP_DIV : 2, Z3_OP_IDIV : 2, Z3_OP_MOD : 2, Z3_OP_BMUL : 2, Z3_OP_BSDIV : 2, Z3_OP_BSMOD : 2, Z3_OP_ADD : 3, Z3_OP_SUB : 3, Z3_OP_BADD : 3, Z3_OP_BSUB : 3, Z3_OP_BASHR : 4, Z3_OP_BSHL : 4, Z3_OP_BAND : 5, Z3_OP_BXOR : 6, Z3_OP_BOR : 7, Z3_OP_LE : 8, Z3_OP_LT : 8, Z3_OP_GE : 8, Z3_OP_GT : 8, Z3_OP_EQ : 8, Z3_OP_SLEQ : 8, Z3_OP_SLT : 8, Z3_OP_SGEQ : 8, Z3_OP_SGT : 8, Z3_OP_IFF : 8, Z3_OP_FPA_NEG : 1, Z3_OP_FPA_MUL : 2, Z3_OP_FPA_DIV : 2, Z3_OP_FPA_REM : 2, Z3_OP_FPA_FMA : 2, Z3_OP_FPA_ADD: 3, Z3_OP_FPA_SUB : 3, Z3_OP_FPA_LE : 8, Z3_OP_FPA_LT : 8, Z3_OP_FPA_GE : 8, Z3_OP_FPA_GT : 8, Z3_OP_FPA_EQ : 8 } # FPA operators _z3_op_to_fpa_normal_str = { Z3_OP_FPA_RM_NEAREST_TIES_TO_EVEN : 'RoundNearestTiesToEven()', Z3_OP_FPA_RM_NEAREST_TIES_TO_AWAY : 'RoundNearestTiesToAway()', Z3_OP_FPA_RM_TOWARD_POSITIVE : 'RoundTowardPositive()', Z3_OP_FPA_RM_TOWARD_NEGATIVE : 'RoundTowardNegative()', Z3_OP_FPA_RM_TOWARD_ZERO : 'RoundTowardZero()', Z3_OP_FPA_PLUS_INF : 'fpPlusInfinity', Z3_OP_FPA_MINUS_INF : 'fpMinusInfinity', Z3_OP_FPA_NAN : 'fpNaN', Z3_OP_FPA_PLUS_ZERO : 'fpPZero', Z3_OP_FPA_MINUS_ZERO : 'fpNZero', Z3_OP_FPA_ADD : 'fpAdd', Z3_OP_FPA_SUB : 'fpSub', Z3_OP_FPA_NEG : 'fpNeg', Z3_OP_FPA_MUL : 'fpMul', Z3_OP_FPA_DIV : 'fpDiv', Z3_OP_FPA_REM : 'fpRem', Z3_OP_FPA_ABS : 'fpAbs', Z3_OP_FPA_MIN : 'fpMin', Z3_OP_FPA_MAX : 'fpMax', Z3_OP_FPA_FMA : 'fpFMA', Z3_OP_FPA_SQRT : 'fpSqrt', Z3_OP_FPA_ROUND_TO_INTEGRAL : 'fpRoundToIntegral', Z3_OP_FPA_EQ : 'fpEQ', Z3_OP_FPA_LT : 'fpLT', Z3_OP_FPA_GT : 'fpGT', Z3_OP_FPA_LE : 'fpLEQ', Z3_OP_FPA_GE : 'fpGEQ', Z3_OP_FPA_FP : 'fpFP', Z3_OP_FPA_TO_FP : 'fpToFP', Z3_OP_FPA_TO_FP_UNSIGNED: 'fpToFPUnsigned', Z3_OP_FPA_TO_UBV : 'fpToUBV', Z3_OP_FPA_TO_SBV : 'fpToSBV', Z3_OP_FPA_TO_REAL: 'fpToReal', Z3_OP_FPA_TO_IEEE_BV : 'fpToIEEEBV' } _z3_op_to_fpa_pretty_str = { Z3_OP_FPA_RM_NEAREST_TIES_TO_EVEN : 'RNE()', Z3_OP_FPA_RM_NEAREST_TIES_TO_AWAY : 'RNA()', Z3_OP_FPA_RM_TOWARD_POSITIVE : 'RTP()', Z3_OP_FPA_RM_TOWARD_NEGATIVE : 'RTN()', Z3_OP_FPA_RM_TOWARD_ZERO : 'RTZ()', Z3_OP_FPA_PLUS_INF : '+oo', Z3_OP_FPA_MINUS_INF : '-oo', Z3_OP_FPA_NAN : 'NaN', Z3_OP_FPA_PLUS_ZERO : '+0.0', Z3_OP_FPA_MINUS_ZERO : '-0.0', Z3_OP_FPA_ADD : '+', Z3_OP_FPA_SUB : '-', Z3_OP_FPA_MUL : '*', Z3_OP_FPA_DIV : '/', Z3_OP_FPA_REM : '%', Z3_OP_FPA_NEG : '-', Z3_OP_FPA_EQ : 'fpEQ', Z3_OP_FPA_LT : '<', Z3_OP_FPA_GT : '>', Z3_OP_FPA_LE : '<=', Z3_OP_FPA_GE : '>=' } _z3_fpa_infix = [ Z3_OP_FPA_ADD, Z3_OP_FPA_SUB, Z3_OP_FPA_MUL, Z3_OP_FPA_DIV, Z3_OP_FPA_REM, Z3_OP_FPA_LT, Z3_OP_FPA_GT, Z3_OP_FPA_LE, Z3_OP_FPA_GE ] def _is_assoc(k): return k == Z3_OP_BOR or k == Z3_OP_BXOR or k == Z3_OP_BAND or k == Z3_OP_ADD or k == Z3_OP_BADD or k == Z3_OP_MUL or k == Z3_OP_BMUL def _is_left_assoc(k): return _is_assoc(k) or k == Z3_OP_SUB or k == Z3_OP_BSUB def _is_html_assoc(k): return k == Z3_OP_AND or k == Z3_OP_OR or k == Z3_OP_IFF or _is_assoc(k) def _is_html_left_assoc(k): return _is_html_assoc(k) or k == Z3_OP_SUB or k == Z3_OP_BSUB def _is_add(k): return k == Z3_OP_ADD or k == Z3_OP_BADD def _is_sub(k): return k == Z3_OP_SUB or k == Z3_OP_BSUB import sys if sys.version < '3': import codecs def u(x): return codecs.unicode_escape_decode(x)[0] else: def u(x): return x _z3_infix_compact = [ Z3_OP_MUL, Z3_OP_BMUL, Z3_OP_POWER, Z3_OP_DIV, Z3_OP_IDIV, Z3_OP_MOD, Z3_OP_BSDIV, Z3_OP_BSMOD ] _ellipses = '...' _html_ellipses = '…' # Overwrite some of the operators for HTML _z3_pre_html_op_to_str = { Z3_OP_EQ : '=', Z3_OP_IFF : '=', Z3_OP_NOT : '¬', Z3_OP_AND : '∧', Z3_OP_OR : '∨', Z3_OP_IMPLIES : '⇒', Z3_OP_LT : '<', Z3_OP_GT : '>', Z3_OP_LE : '≤', Z3_OP_GE : '≥', Z3_OP_MUL : '·', Z3_OP_SLEQ : '≤', Z3_OP_SLT : '<', Z3_OP_SGEQ : '≥', Z3_OP_SGT : '>', Z3_OP_ULEQ : '≤u', Z3_OP_ULT : '<u', Z3_OP_UGEQ : '≥u', Z3_OP_UGT : '>u', Z3_OP_BMUL : '·', Z3_OP_BUDIV : '/u', Z3_OP_BUREM : '%u', Z3_OP_BASHR : '>>', Z3_OP_BSHL : '<<', Z3_OP_BLSHR : '>>u' } # Extra operators that are infix/unary for HTML _z3_html_infix = [ Z3_OP_AND, Z3_OP_OR, Z3_OP_IMPLIES, Z3_OP_ULEQ, Z3_OP_ULT, Z3_OP_UGEQ, Z3_OP_UGT, Z3_OP_BUDIV, Z3_OP_BUREM, Z3_OP_BLSHR ] _z3_html_unary = [ Z3_OP_NOT ] # Extra Precedence for HTML _z3_pre_html_precedence = { Z3_OP_BUDIV : 2, Z3_OP_BUREM : 2, Z3_OP_BLSHR : 4, Z3_OP_ULEQ : 8, Z3_OP_ULT : 8, Z3_OP_UGEQ : 8, Z3_OP_UGT : 8, Z3_OP_ULEQ : 8, Z3_OP_ULT : 8, Z3_OP_UGEQ : 8, Z3_OP_UGT : 8, Z3_OP_NOT : 1, Z3_OP_AND : 10, Z3_OP_OR : 11, Z3_OP_IMPLIES : 12 } ############################## # # End of Configuration # ############################## def _support_pp(a): return isinstance(a, z3.Z3PPObject) or isinstance(a, list) or isinstance(a, tuple) _infix_map = {} _unary_map = {} _infix_compact_map = {} for _k in _z3_infix: _infix_map[_k] = True for _k in _z3_unary: _unary_map[_k] = True for _k in _z3_infix_compact: _infix_compact_map[_k] = True def _is_infix(k): global _infix_map return _infix_map.get(k, False) def _is_infix_compact(k): global _infix_compact_map return _infix_compact_map.get(k, False) def _is_unary(k): global _unary_map return _unary_map.get(k, False) def _op_name(a): if isinstance(a, z3.FuncDeclRef): f = a else: f = a.decl() k = f.kind() n = _z3_op_to_str.get(k, None) if n == None: return f.name() else: return n def _get_precedence(k): global _z3_precedence return _z3_precedence.get(k, 100000) _z3_html_op_to_str = {} for _k in _z3_op_to_str: _v = _z3_op_to_str[_k] _z3_html_op_to_str[_k] = _v for _k in _z3_pre_html_op_to_str: _v = _z3_pre_html_op_to_str[_k] _z3_html_op_to_str[_k] = _v _z3_html_precedence = {} for _k in _z3_precedence: _v = _z3_precedence[_k] _z3_html_precedence[_k] = _v for _k in _z3_pre_html_precedence: _v = _z3_pre_html_precedence[_k] _z3_html_precedence[_k] = _v _html_infix_map = {} _html_unary_map = {} for _k in _z3_infix: _html_infix_map[_k] = True for _k in _z3_html_infix: _html_infix_map[_k] = True for _k in _z3_unary: _html_unary_map[_k] = True for _k in _z3_html_unary: _html_unary_map[_k] = True def _is_html_infix(k): global _html_infix_map return _html_infix_map.get(k, False) def _is_html_unary(k): global _html_unary_map return _html_unary_map.get(k, False) def _html_op_name(a): global _z3_html_op_to_str if isinstance(a, z3.FuncDeclRef): f = a else: f = a.decl() k = f.kind() n = _z3_html_op_to_str.get(k, None) if n == None: sym = Z3_get_decl_name(f.ctx_ref(), f.ast) if Z3_get_symbol_kind(f.ctx_ref(), sym) == Z3_INT_SYMBOL: return "ζ%s" % Z3_get_symbol_int(f.ctx_ref(), sym) else: # Sanitize the string return f.name() else: return n def _get_html_precedence(k): global _z3_html_predence return _z3_html_precedence.get(k, 100000) class FormatObject: def is_compose(self): return False def is_choice(self): return False def is_indent(self): return False def is_string(self): return False def is_linebreak(self): return False def is_nil(self): return True def children(self): return [] def as_tuple(self): return None def space_upto_nl(self): return (0, False) def flat(self): return self class NAryFormatObject(FormatObject): def __init__(self, fs): assert all([isinstance(a, FormatObject) for a in fs]) self.children = fs def children(self): return self.children class ComposeFormatObject(NAryFormatObject): def is_compose(sef): return True def as_tuple(self): return ('compose', [ a.as_tuple() for a in self.children ]) def space_upto_nl(self): r = 0 for child in self.children: s, nl = child.space_upto_nl() r = r + s if nl: return (r, True) return (r, False) def flat(self): return compose([a.flat() for a in self.children ]) class ChoiceFormatObject(NAryFormatObject): def is_choice(sef): return True def as_tuple(self): return ('choice', [ a.as_tuple() for a in self.children ]) def space_upto_nl(self): return self.children[0].space_upto_nl() def flat(self): return self.children[0].flat() class IndentFormatObject(FormatObject): def __init__(self, indent, child): assert isinstance(child, FormatObject) self.indent = indent self.child = child def children(self): return [self.child] def as_tuple(self): return ('indent', self.indent, self.child.as_tuple()) def space_upto_nl(self): return self.child.space_upto_nl() def flat(self): return indent(self.indent, self.child.flat()) def is_indent(self): return True class LineBreakFormatObject(FormatObject): def __init__(self): self.space = ' ' def is_linebreak(self): return True def as_tuple(self): return '' def space_upto_nl(self): return (0, True) def flat(self): return to_format(self.space) class StringFormatObject(FormatObject): def __init__(self, string): assert isinstance(string, str) self.string = string def is_string(self): return True def as_tuple(self): return self.string def space_upto_nl(self): return (getattr(self, 'size', len(self.string)), False) def fits(f, space_left): s, nl = f.space_upto_nl() return s <= space_left def to_format(arg, size=None): if isinstance(arg, FormatObject): return arg else: r = StringFormatObject(str(arg)) if size != None: r.size = size return r def compose(*args): if len(args) == 1 and (isinstance(args[0], list) or isinstance(args[0], tuple)): args = args[0] return ComposeFormatObject(args) def indent(i, arg): return IndentFormatObject(i, arg) def group(arg): return ChoiceFormatObject([arg.flat(), arg]) def line_break(): return LineBreakFormatObject() def _len(a): if isinstance(a, StringFormatObject): return getattr(a, 'size', len(a.string)) else: return len(a) def seq(args, sep=',', space=True): nl = line_break() if not space: nl.space = '' r = [] r.append(args[0]) num = len(args) for i in range(num - 1): r.append(to_format(sep)) r.append(nl) r.append(args[i+1]) return compose(r) def seq1(header, args, lp='(', rp=')'): return group(compose(to_format(header), to_format(lp), indent(len(lp) + _len(header), seq(args)), to_format(rp))) def seq2(header, args, i=4, lp='(', rp=')'): if len(args) == 0: return compose(to_format(header), to_format(lp), to_format(rp)) else: return group(compose(indent(len(lp), compose(to_format(lp), to_format(header))), indent(i, compose(seq(args), to_format(rp))))) def seq3(args, lp='(', rp=')'): if len(args) == 0: return compose(to_format(lp), to_format(rp)) else: return group(indent(len(lp), compose(to_format(lp), seq(args), to_format(rp)))) class StopPPException(Exception): def __str__(self): return 'pp-interrupted' class PP: def __init__(self): self.max_lines = 200 self.max_width = 60 self.bounded = False self.max_indent = 40 def pp_string(self, f, indent): if not self.bounded or self.pos <= self.max_width: sz = _len(f) if self.bounded and self.pos + sz > self.max_width: self.out.write(u(_ellipses)) else: self.pos = self.pos + sz self.ribbon_pos = self.ribbon_pos + sz self.out.write(u(f.string)) def pp_compose(self, f, indent): for c in f.children: self.pp(c, indent) def pp_choice(self, f, indent): space_left = self.max_width - self.pos if space_left > 0 and fits(f.children[0], space_left): self.pp(f.children[0], indent) else: self.pp(f.children[1], indent) def pp_line_break(self, f, indent): self.pos = indent self.ribbon_pos = 0 self.line = self.line + 1 if self.line < self.max_lines: self.out.write(u('\n')) for i in range(indent): self.out.write(u(' ')) else: self.out.write(u('\n...')) raise StopPPException() def pp(self, f, indent): if isinstance(f, str): self.pp_string(f, indent) elif f.is_string(): self.pp_string(f, indent) elif f.is_indent(): self.pp(f.child, min(indent + f.indent, self.max_indent)) elif f.is_compose(): self.pp_compose(f, indent) elif f.is_choice(): self.pp_choice(f, indent) elif f.is_linebreak(): self.pp_line_break(f, indent) else: return def __call__(self, out, f): try: self.pos = 0 self.ribbon_pos = 0 self.line = 0 self.out = out self.pp(f, 0) except StopPPException: return class Formatter: def __init__(self): global _ellipses self.max_depth = 20 self.max_args = 128 self.rational_to_decimal = False self.precision = 10 self.ellipses = to_format(_ellipses) self.max_visited = 10000 self.fpa_pretty = True def pp_ellipses(self): return self.ellipses def pp_arrow(self): return ' ->' def pp_unknown(self): return '' def pp_name(self, a): return to_format(_op_name(a)) def is_infix(self, a): return _is_infix(a) def is_unary(self, a): return _is_unary(a) def get_precedence(self, a): return _get_precedence(a) def is_infix_compact(self, a): return _is_infix_compact(a) def is_infix_unary(self, a): return self.is_infix(a) or self.is_unary(a) def add_paren(self, a): return compose(to_format('('), indent(1, a), to_format(')')) def pp_sort(self, s): if isinstance(s, z3.ArraySortRef): return seq1('Array', (self.pp_sort(s.domain()), self.pp_sort(s.range()))) elif isinstance(s, z3.BitVecSortRef): return seq1('BitVec', (to_format(s.size()), )) elif isinstance(s, z3.FPSortRef): return seq1('FPSort', (to_format(s.ebits()), to_format(s.sbits()))) elif isinstance(s, z3.ReSortRef): return seq1('ReSort', (self.pp_sort(s.basis()), )) elif isinstance(s, z3.SeqSortRef): if s.is_string(): return to_format("String") return seq1('Seq', (self.pp_sort(s.basis()), )) else: return to_format(s.name()) def pp_const(self, a): k = a.decl().kind() if k == Z3_OP_RE_EMPTY_SET: return self.pp_set("Empty", a) elif k == Z3_OP_SEQ_EMPTY: return self.pp_set("Empty", a) elif k == Z3_OP_RE_FULL_SET: return self.pp_set("Full", a) return self.pp_name(a) def pp_int(self, a): return to_format(a.as_string()) def pp_rational(self, a): if not self.rational_to_decimal: return to_format(a.as_string()) else: return to_format(a.as_decimal(self.precision)) def pp_algebraic(self, a): return to_format(a.as_decimal(self.precision)) def pp_string(self, a): return to_format("\"" + a.as_string() + "\"") def pp_bv(self, a): return to_format(a.as_string()) def pp_fd(self, a): return to_format(a.as_string()) def pp_fprm_value(self, a): _z3_assert(z3.is_fprm_value(a), 'expected FPRMNumRef') if self.fpa_pretty and (a.decl().kind() in _z3_op_to_fpa_pretty_str): return to_format(_z3_op_to_fpa_pretty_str.get(a.decl().kind())) else: return to_format(_z3_op_to_fpa_normal_str.get(a.decl().kind())) def pp_fp_value(self, a): _z3_assert(isinstance(a, z3.FPNumRef), 'type mismatch') if not self.fpa_pretty: r = [] if (a.isNaN()): r.append(to_format(_z3_op_to_fpa_normal_str[Z3_OP_FPA_NAN])) r.append(to_format('(')) r.append(to_format(a.sort())) r.append(to_format(')')) return compose(r) elif (a.isInf()): if (a.isNegative()): r.append(to_format(_z3_op_to_fpa_normal_str[Z3_OP_FPA_MINUS_INF])) else: r.append(to_format(_z3_op_to_fpa_normal_str[Z3_OP_FPA_PLUS_INF])) r.append(to_format('(')) r.append(to_format(a.sort())) r.append(to_format(')')) return compose(r) elif (a.isZero()): if (a.isNegative()): return to_format('-zero') else: return to_format('+zero') else: _z3_assert(z3.is_fp_value(a), 'expecting FP num ast') r = [] sgn = c_int(0) sgnb = Z3_fpa_get_numeral_sign(a.ctx_ref(), a.ast, byref(sgn)) exp = Z3_fpa_get_numeral_exponent_string(a.ctx_ref(), a.ast, False) sig = Z3_fpa_get_numeral_significand_string(a.ctx_ref(), a.ast) r.append(to_format('FPVal(')) if sgnb and sgn.value != 0: r.append(to_format('-')) r.append(to_format(sig)) r.append(to_format('*(2**')) r.append(to_format(exp)) r.append(to_format(', ')) r.append(to_format(a.sort())) r.append(to_format('))')) return compose(r) else: if (a.isNaN()): return to_format(_z3_op_to_fpa_pretty_str[Z3_OP_FPA_NAN]) elif (a.isInf()): if (a.isNegative()): return to_format(_z3_op_to_fpa_pretty_str[Z3_OP_FPA_MINUS_INF]) else: return to_format(_z3_op_to_fpa_pretty_str[Z3_OP_FPA_PLUS_INF]) elif (a.isZero()): if (a.isNegative()): return to_format(_z3_op_to_fpa_pretty_str[Z3_OP_FPA_MINUS_ZERO]) else: return to_format(_z3_op_to_fpa_pretty_str[Z3_OP_FPA_PLUS_ZERO]) else: _z3_assert(z3.is_fp_value(a), 'expecting FP num ast') r = [] sgn = (ctypes.c_int)(0) sgnb = Z3_fpa_get_numeral_sign(a.ctx_ref(), a.ast, byref(sgn)) exp = Z3_fpa_get_numeral_exponent_string(a.ctx_ref(), a.ast, False) sig = Z3_fpa_get_numeral_significand_string(a.ctx_ref(), a.ast) if sgnb and sgn.value != 0: r.append(to_format('-')) r.append(to_format(sig)) if (exp != '0'): r.append(to_format('*(2**')) r.append(to_format(exp)) r.append(to_format(')')) return compose(r) def pp_fp(self, a, d, xs): _z3_assert(isinstance(a, z3.FPRef), "type mismatch") k = a.decl().kind() op = '?' if (self.fpa_pretty and k in _z3_op_to_fpa_pretty_str): op = _z3_op_to_fpa_pretty_str[k] elif k in _z3_op_to_fpa_normal_str: op = _z3_op_to_fpa_normal_str[k] elif k in _z3_op_to_str: op = _z3_op_to_str[k] n = a.num_args() if self.fpa_pretty: if self.is_infix(k) and n >= 3: rm = a.arg(0) if z3.is_fprm_value(rm) and z3.get_default_rounding_mode(a.ctx).eq(rm): arg1 = to_format(self.pp_expr(a.arg(1), d+1, xs)) arg2 = to_format(self.pp_expr(a.arg(2), d+1, xs)) r = [] r.append(arg1) r.append(to_format(' ')) r.append(to_format(op)) r.append(to_format(' ')) r.append(arg2) return compose(r) elif k == Z3_OP_FPA_NEG: return compose([to_format('-') , to_format(self.pp_expr(a.arg(0), d+1, xs))]) if k in _z3_op_to_fpa_normal_str: op = _z3_op_to_fpa_normal_str[k] r = [] r.append(to_format(op)) if not z3.is_const(a): r.append(to_format('(')) first = True for c in a.children(): if first: first = False else: r.append(to_format(', ')) r.append(self.pp_expr(c, d+1, xs)) r.append(to_format(')')) return compose(r) else: return to_format(a.as_string()) def pp_prefix(self, a, d, xs): r = [] sz = 0 for child in a.children(): r.append(self.pp_expr(child, d+1, xs)) sz = sz + 1 if sz > self.max_args: r.append(self.pp_ellipses()) break return seq1(self.pp_name(a), r) def is_assoc(self, k): return _is_assoc(k) def is_left_assoc(self, k): return _is_left_assoc(k) def infix_args_core(self, a, d, xs, r): sz = len(r) k = a.decl().kind() p = self.get_precedence(k) first = True for child in a.children(): child_pp = self.pp_expr(child, d+1, xs) child_k = None if z3.is_app(child): child_k = child.decl().kind() if k == child_k and (self.is_assoc(k) or (first and self.is_left_assoc(k))): self.infix_args_core(child, d, xs, r) sz = len(r) if sz > self.max_args: return elif self.is_infix_unary(child_k): child_p = self.get_precedence(child_k) if p > child_p or (_is_add(k) and _is_sub(child_k)) or (_is_sub(k) and first and _is_add(child_k)): r.append(child_pp) else: r.append(self.add_paren(child_pp)) sz = sz + 1 elif z3.is_quantifier(child): r.append(self.add_paren(child_pp)) else: r.append(child_pp) sz = sz + 1 if sz > self.max_args: r.append(self.pp_ellipses()) return first = False def infix_args(self, a, d, xs): r = [] self.infix_args_core(a, d, xs, r) return r def pp_infix(self, a, d, xs): k = a.decl().kind() if self.is_infix_compact(k): op = self.pp_name(a) return group(seq(self.infix_args(a, d, xs), op, False)) else: op = self.pp_name(a) sz = _len(op) op.string = ' ' + op.string op.size = sz + 1 return group(seq(self.infix_args(a, d, xs), op)) def pp_unary(self, a, d, xs): k = a.decl().kind() p = self.get_precedence(k) child = a.children()[0] child_k = None if z3.is_app(child): child_k = child.decl().kind() child_pp = self.pp_expr(child, d+1, xs) if k != child_k and self.is_infix_unary(child_k): child_p = self.get_precedence(child_k) if p <= child_p: child_pp = self.add_paren(child_pp) if z3.is_quantifier(child): child_pp = self.add_paren(child_pp) name = self.pp_name(a) return compose(to_format(name), indent(_len(name), child_pp)) def pp_power_arg(self, arg, d, xs): r = self.pp_expr(arg, d+1, xs) k = None if z3.is_app(arg): k = arg.decl().kind() if self.is_infix_unary(k) or (z3.is_rational_value(arg) and arg.denominator_as_long() != 1): return self.add_paren(r) else: return r def pp_power(self, a, d, xs): arg1_pp = self.pp_power_arg(a.arg(0), d+1, xs) arg2_pp = self.pp_power_arg(a.arg(1), d+1, xs) return group(seq((arg1_pp, arg2_pp), '**', False)) def pp_neq(self): return to_format("!=") def pp_distinct(self, a, d, xs): if a.num_args() == 2: op = self.pp_neq() sz = _len(op) op.string = ' ' + op.string op.size = sz + 1 return group(seq(self.infix_args(a, d, xs), op)) else: return self.pp_prefix(a, d, xs) def pp_select(self, a, d, xs): if a.num_args() != 2: return self.pp_prefix(a, d, xs) else: arg1_pp = self.pp_expr(a.arg(0), d+1, xs) arg2_pp = self.pp_expr(a.arg(1), d+1, xs) return compose(arg1_pp, indent(2, compose(to_format('['), arg2_pp, to_format(']')))) def pp_unary_param(self, a, d, xs): p = Z3_get_decl_int_parameter(a.ctx_ref(), a.decl().ast, 0) arg = self.pp_expr(a.arg(0), d+1, xs) return seq1(self.pp_name(a), [ to_format(p), arg ]) def pp_extract(self, a, d, xs): h = Z3_get_decl_int_parameter(a.ctx_ref(), a.decl().ast, 0) l = Z3_get_decl_int_parameter(a.ctx_ref(), a.decl().ast, 1) arg = self.pp_expr(a.arg(0), d+1, xs) return seq1(self.pp_name(a), [ to_format(h), to_format(l), arg ]) def pp_loop(self, a, d, xs): l = Z3_get_decl_int_parameter(a.ctx_ref(), a.decl().ast, 0) arg = self.pp_expr(a.arg(0), d+1, xs) if Z3_get_decl_num_parameters(a.ctx_ref(), a.decl().ast) > 1: h = Z3_get_decl_int_parameter(a.ctx_ref(), a.decl().ast, 1) return seq1("Loop", [ arg, to_format(l), to_format(h) ]) return seq1("Loop", [ arg, to_format(l) ]) def pp_set(self, id, a): return seq1(id, [self.pp_sort(a.sort())]) def pp_pattern(self, a, d, xs): if a.num_args() == 1: return self.pp_expr(a.arg(0), d, xs) else: return seq1('MultiPattern', [ self.pp_expr(arg, d+1, xs) for arg in a.children() ]) def pp_is(self, a, d, xs): f = a.params()[0] return self.pp_fdecl(f, a, d, xs) def pp_map(self, a, d, xs): f = z3.get_map_func(a) return self.pp_fdecl(f, a, d, xs) def pp_fdecl(self, f, a, d, xs): r = [] sz = 0 r.append(to_format(f.name())) for child in a.children(): r.append(self.pp_expr(child, d+1, xs)) sz = sz + 1 if sz > self.max_args: r.append(self.pp_ellipses()) break return seq1(self.pp_name(a), r) def pp_K(self, a, d, xs): return seq1(self.pp_name(a), [ self.pp_sort(a.domain()), self.pp_expr(a.arg(0), d+1, xs) ]) def pp_atmost(self, a, d, f, xs): k = Z3_get_decl_int_parameter(a.ctx_ref(), a.decl().ast, 0) return seq1(self.pp_name(a), [seq3([ self.pp_expr(ch, d+1, xs) for ch in a.children()]), to_format(k)]) def pp_pbcmp(self, a, d, f, xs): chs = a.children() rchs = range(len(chs)) k = Z3_get_decl_int_parameter(a.ctx_ref(), a.decl().ast, 0) ks = [Z3_get_decl_int_parameter(a.ctx_ref(), a.decl().ast, i+1) for i in rchs] ls = [ seq3([self.pp_expr(chs[i], d+1,xs), to_format(ks[i])]) for i in rchs] return seq1(self.pp_name(a), [seq3(ls), to_format(k)]) def pp_app(self, a, d, xs): if z3.is_int_value(a): return self.pp_int(a) elif z3.is_rational_value(a): return self.pp_rational(a) elif z3.is_algebraic_value(a): return self.pp_algebraic(a) elif z3.is_bv_value(a): return self.pp_bv(a) elif z3.is_finite_domain_value(a): return self.pp_fd(a) elif z3.is_fprm_value(a): return self.pp_fprm_value(a) elif z3.is_fp_value(a): return self.pp_fp_value(a) elif z3.is_fp(a): return self.pp_fp(a, d, xs) elif z3.is_string_value(a): return self.pp_string(a) elif z3.is_const(a): return self.pp_const(a) else: f = a.decl() k = f.kind() if k == Z3_OP_POWER: return self.pp_power(a, d, xs) elif k == Z3_OP_DISTINCT: return self.pp_distinct(a, d, xs) elif k == Z3_OP_SELECT: return self.pp_select(a, d, xs) elif k == Z3_OP_SIGN_EXT or k == Z3_OP_ZERO_EXT or k == Z3_OP_REPEAT: return self.pp_unary_param(a, d, xs) elif k == Z3_OP_EXTRACT: return self.pp_extract(a, d, xs) elif k == Z3_OP_RE_LOOP: return self.pp_loop(a, d, xs) elif k == Z3_OP_DT_IS: return self.pp_is(a, d, xs) elif k == Z3_OP_ARRAY_MAP: return self.pp_map(a, d, xs) elif k == Z3_OP_CONST_ARRAY: return self.pp_K(a, d, xs) elif k == Z3_OP_PB_AT_MOST: return self.pp_atmost(a, d, f, xs) elif k == Z3_OP_PB_LE: return self.pp_pbcmp(a, d, f, xs) elif k == Z3_OP_PB_GE: return self.pp_pbcmp(a, d, f, xs) elif k == Z3_OP_PB_EQ: return self.pp_pbcmp(a, d, f, xs) elif z3.is_pattern(a): return self.pp_pattern(a, d, xs) elif self.is_infix(k): return self.pp_infix(a, d, xs) elif self.is_unary(k): return self.pp_unary(a, d, xs) else: return self.pp_prefix(a, d, xs) def pp_var(self, a, d, xs): idx = z3.get_var_index(a) sz = len(xs) if idx >= sz: return seq1('Var', (to_format(idx),)) else: return to_format(xs[sz - idx - 1]) def pp_quantifier(self, a, d, xs): ys = [ to_format(a.var_name(i)) for i in range(a.num_vars()) ] new_xs = xs + ys body_pp = self.pp_expr(a.body(), d+1, new_xs) if len(ys) == 1: ys_pp = ys[0] else: ys_pp = seq3(ys, '[', ']') if a.is_forall(): header = 'ForAll' elif a.is_exists(): header = 'Exists' else: header = 'Lambda' return seq1(header, (ys_pp, body_pp)) def pp_expr(self, a, d, xs): self.visited = self.visited + 1 if d > self.max_depth or self.visited > self.max_visited: return self.pp_ellipses() if z3.is_app(a): return self.pp_app(a, d, xs) elif z3.is_quantifier(a): return self.pp_quantifier(a, d, xs) elif z3.is_var(a): return self.pp_var(a, d, xs) else: return to_format(self.pp_unknown()) def pp_decl(self, f): k = f.kind() if k == Z3_OP_DT_IS or k == Z3_OP_ARRAY_MAP: g = f.params()[0] r = [ to_format(g.name()) ] return seq1(self.pp_name(f), r) return self.pp_name(f) def pp_seq_core(self, f, a, d, xs): self.visited = self.visited + 1 if d > self.max_depth or self.visited > self.max_visited: return self.pp_ellipses() r = [] sz = 0 for elem in a: r.append(f(elem, d+1, xs)) sz = sz + 1 if sz > self.max_args: r.append(self.pp_ellipses()) break return seq3(r, '[', ']') def pp_seq(self, a, d, xs): return self.pp_seq_core(self.pp_expr, a, d, xs) def pp_seq_seq(self, a, d, xs): return self.pp_seq_core(self.pp_seq, a, d, xs) def pp_model(self, m): r = [] sz = 0 for d in m: i = m[d] if isinstance(i, z3.FuncInterp): i_pp = self.pp_func_interp(i) else: i_pp = self.pp_expr(i, 0, []) name = self.pp_name(d) r.append(compose(name, to_format(' = '), indent(_len(name) + 3, i_pp))) sz = sz + 1 if sz > self.max_args: r.append(self.pp_ellipses()) break return seq3(r, '[', ']') def pp_func_entry(self, e): num = e.num_args() if num > 1: args = [] for i in range(num): args.append(self.pp_expr(e.arg_value(i), 0, [])) args_pp = group(seq3(args)) else: args_pp = self.pp_expr(e.arg_value(0), 0, []) value_pp = self.pp_expr(e.value(), 0, []) return group(seq((args_pp, value_pp), self.pp_arrow())) def pp_func_interp(self, f): r = [] sz = 0 num = f.num_entries() for i in range(num): r.append(self.pp_func_entry(f.entry(i))) sz = sz + 1 if sz > self.max_args: r.append(self.pp_ellipses()) break if sz <= self.max_args: else_val = f.else_value() if else_val == None: else_pp = to_format('#unspecified') else: else_pp = self.pp_expr(else_val, 0, []) r.append(group(seq((to_format('else'), else_pp), self.pp_arrow()))) return seq3(r, '[', ']') def pp_list(self, a): r = [] sz = 0 for elem in a: if _support_pp(elem): r.append(self.main(elem)) else: r.append(to_format(str(elem))) sz = sz + 1 if sz > self.max_args: r.append(self.pp_ellipses()) break if isinstance(a, tuple): return seq3(r) else: return seq3(r, '[', ']') def main(self, a): if z3.is_expr(a): return self.pp_expr(a, 0, []) elif z3.is_sort(a): return self.pp_sort(a) elif z3.is_func_decl(a): return self.pp_decl(a) elif isinstance(a, z3.Goal) or isinstance(a, z3.AstVector): return self.pp_seq(a, 0, []) elif isinstance(a, z3.Solver): return self.pp_seq(a.assertions(), 0, []) elif isinstance(a, z3.Fixedpoint): return a.sexpr() elif isinstance(a, z3.Optimize): return a.sexpr() elif isinstance(a, z3.ApplyResult): return self.pp_seq_seq(a, 0, []) elif isinstance(a, z3.ModelRef): return self.pp_model(a) elif isinstance(a, z3.FuncInterp): return self.pp_func_interp(a) elif isinstance(a, list) or isinstance(a, tuple): return self.pp_list(a) else: return to_format(self.pp_unknown()) def __call__(self, a): self.visited = 0 return self.main(a) class HTMLFormatter(Formatter): def __init__(self): Formatter.__init__(self) global _html_ellipses self.ellipses = to_format(_html_ellipses) def pp_arrow(self): return to_format(' →', 1) def pp_unknown(self): return 'unknown' def pp_name(self, a): r = _html_op_name(a) if r[0] == '&' or r[0] == '/' or r[0] == '%': return to_format(r, 1) else: pos = r.find('__') if pos == -1 or pos == 0: return to_format(r) else: sz = len(r) if pos + 2 == sz: return to_format(r) else: return to_format('%s%s' % (r[0:pos], r[pos+2:sz]), sz - 2) def is_assoc(self, k): return _is_html_assoc(k) def is_left_assoc(self, k): return _is_html_left_assoc(k) def is_infix(self, a): return _is_html_infix(a) def is_unary(self, a): return _is_html_unary(a) def get_precedence(self, a): return _get_html_precedence(a) def pp_neq(self): return to_format("≠") def pp_power(self, a, d, xs): arg1_pp = self.pp_power_arg(a.arg(0), d+1, xs) arg2_pp = self.pp_expr(a.arg(1), d+1, xs) return compose(arg1_pp, to_format('', 1), arg2_pp, to_format('', 1)) def pp_var(self, a, d, xs): idx = z3.get_var_index(a) sz = len(xs) if idx >= sz: # 957 is the greek letter nu return to_format('ν%s' % idx, 1) else: return to_format(xs[sz - idx - 1]) def pp_quantifier(self, a, d, xs): ys = [ to_format(a.var_name(i)) for i in range(a.num_vars()) ] new_xs = xs + ys body_pp = self.pp_expr(a.body(), d+1, new_xs) ys_pp = group(seq(ys)) if a.is_forall(): header = '∀' else: header = '∃' return group(compose(to_format(header, 1), indent(1, compose(ys_pp, to_format(' :'), line_break(), body_pp)))) _PP = PP() _Formatter = Formatter() def set_pp_option(k, v): if k == 'html_mode': if v: set_html_mode(True) else: set_html_mode(False) return True if k == 'fpa_pretty': if v: set_fpa_pretty(True) else: set_fpa_pretty(False) return True val = getattr(_PP, k, None) if val != None: _z3_assert(type(v) == type(val), "Invalid pretty print option value") setattr(_PP, k, v) return True val = getattr(_Formatter, k, None) if val != None: _z3_assert(type(v) == type(val), "Invalid pretty print option value") setattr(_Formatter, k, v) return True return False def obj_to_string(a): out = io.StringIO() _PP(out, _Formatter(a)) return out.getvalue() _html_out = None def set_html_mode(flag=True): global _Formatter if flag: _Formatter = HTMLFormatter() else: _Formatter = Formatter() def set_fpa_pretty(flag=True): global _Formatter global _z3_op_to_str _Formatter.fpa_pretty = flag if flag: for (_k,_v) in _z3_op_to_fpa_pretty_str.items(): _z3_op_to_str[_k] = _v for _k in _z3_fpa_infix: _infix_map[_k] = True else: for (_k,_v) in _z3_op_to_fpa_normal_str.items(): _z3_op_to_str[_k] = _v for _k in _z3_fpa_infix: _infix_map[_k] = False set_fpa_pretty(True) def get_fpa_pretty(): global Formatter return _Formatter.fpa_pretty def in_html_mode(): return isinstance(_Formatter, HTMLFormatter) def pp(a): if _support_pp(a): print(obj_to_string(a)) else: print(a) def print_matrix(m): _z3_assert(isinstance(m, list) or isinstance(m, tuple), "matrix expected") if not in_html_mode(): print(obj_to_string(m)) else: print('') for r in m: _z3_assert(isinstance(r, list) or isinstance(r, tuple), "matrix expected") print('') for c in r: print('' % c) print('') print('
%s
') def insert_line_breaks(s, width): """Break s in lines of size width (approx)""" sz = len(s) if sz <= width: return s new_str = io.StringIO() w = 0 for i in range(sz): if w > width and s[i] == ' ': new_str.write(u('
')) w = 0 else: new_str.write(u(s[i])) w = w + 1 return new_str.getvalue() z3-z3-4.8.7/src/api/python/z3/z3rcf.py000066400000000000000000000117451356505360400172550ustar00rootroot00000000000000############################################ # Copyright (c) 2013 Microsoft Corporation # # Z3 Python interface for Z3 Real Closed Fields # that may contain # - computable transcendentals # - infinitesimals # - algebraic extensions # # Author: Leonardo de Moura (leonardo) ############################################ from .z3 import * from .z3core import * from .z3printer import * from fractions import Fraction def _to_rcfnum(num, ctx=None): if isinstance(num, RCFNum): return num else: return RCFNum(num, ctx) def Pi(ctx=None): ctx = z3.get_ctx(ctx) return RCFNum(Z3_rcf_mk_pi(ctx.ref()), ctx) def E(ctx=None): ctx = z3.get_ctx(ctx) return RCFNum(Z3_rcf_mk_e(ctx.ref()), ctx) def MkInfinitesimal(name="eps", ctx=None): # Todo: remove parameter name. # For now, we keep it for backward compatibility. ctx = z3.get_ctx(ctx) return RCFNum(Z3_rcf_mk_infinitesimal(ctx.ref()), ctx) def MkRoots(p, ctx=None): ctx = z3.get_ctx(ctx) num = len(p) _tmp = [] _as = (RCFNumObj * num)() _rs = (RCFNumObj * num)() for i in range(num): _a = _to_rcfnum(p[i], ctx) _tmp.append(_a) # prevent GC _as[i] = _a.num nr = Z3_rcf_mk_roots(ctx.ref(), num, _as, _rs) r = [] for i in range(nr): r.append(RCFNum(_rs[i], ctx)) return r class RCFNum: def __init__(self, num, ctx=None): # TODO: add support for converting AST numeral values into RCFNum if isinstance(num, RCFNumObj): self.num = num self.ctx = z3.get_ctx(ctx) else: self.ctx = z3.get_ctx(ctx) self.num = Z3_rcf_mk_rational(self.ctx_ref(), str(num)) def __del__(self): Z3_rcf_del(self.ctx_ref(), self.num) def ctx_ref(self): return self.ctx.ref() def __repr__(self): return Z3_rcf_num_to_string(self.ctx_ref(), self.num, False, in_html_mode()) def compact_str(self): return Z3_rcf_num_to_string(self.ctx_ref(), self.num, True, in_html_mode()) def __add__(self, other): v = _to_rcfnum(other, self.ctx) return RCFNum(Z3_rcf_add(self.ctx_ref(), self.num, v.num), self.ctx) def __radd__(self, other): v = _to_rcfnum(other, self.ctx) return RCFNum(Z3_rcf_add(self.ctx_ref(), v.num, self.num), self.ctx) def __mul__(self, other): v = _to_rcfnum(other, self.ctx) return RCFNum(Z3_rcf_mul(self.ctx_ref(), self.num, v.num), self.ctx) def __rmul__(self, other): v = _to_rcfnum(other, self.ctx) return RCFNum(Z3_rcf_mul(self.ctx_ref(), v.num, self.num), self.ctx) def __sub__(self, other): v = _to_rcfnum(other, self.ctx) return RCFNum(Z3_rcf_sub(self.ctx_ref(), self.num, v.num), self.ctx) def __rsub__(self, other): v = _to_rcfnum(other, self.ctx) return RCFNum(Z3_rcf_sub(self.ctx_ref(), v.num, self.num), self.ctx) def __div__(self, other): v = _to_rcfnum(other, self.ctx) return RCFNum(Z3_rcf_div(self.ctx_ref(), self.num, v.num), self.ctx) def __rdiv__(self, other): v = _to_rcfnum(other, self.ctx) return RCFNum(Z3_rcf_div(self.ctx_ref(), v.num, self.num), self.ctx) def __neg__(self): return self.__rsub__(0) def power(self, k): return RCFNum(Z3_rcf_power(self.ctx_ref(), self.num, k), self.ctx) def __pow__(self, k): return self.power(k) def decimal(self, prec=5): return Z3_rcf_num_to_decimal_string(self.ctx_ref(), self.num, prec) def __lt__(self, other): v = _to_rcfnum(other, self.ctx) return Z3_rcf_lt(self.ctx_ref(), self.num, v.num) def __rlt__(self, other): v = _to_rcfnum(other, self.ctx) return Z3_rcf_lt(self.ctx_ref(), v.num, self.num) def __gt__(self, other): v = _to_rcfnum(other, self.ctx) return Z3_rcf_gt(self.ctx_ref(), self.num, v.num) def __rgt__(self, other): v = _to_rcfnum(other, self.ctx) return Z3_rcf_gt(self.ctx_ref(), v.num, self.num) def __le__(self, other): v = _to_rcfnum(other, self.ctx) return Z3_rcf_le(self.ctx_ref(), self.num, v.num) def __rle__(self, other): v = _to_rcfnum(other, self.ctx) return Z3_rcf_le(self.ctx_ref(), v.num, self.num) def __ge__(self, other): v = _to_rcfnum(other, self.ctx) return Z3_rcf_ge(self.ctx_ref(), self.num, v.num) def __rge__(self, other): v = _to_rcfnum(other, self.ctx) return Z3_rcf_ge(self.ctx_ref(), v.num, self.num) def __eq__(self, other): v = _to_rcfnum(other, self.ctx) return Z3_rcf_eq(self.ctx_ref(), self.num, v.num) def __ne__(self, other): v = _to_rcfnum(other, self.ctx) return Z3_rcf_neq(self.ctx_ref(), self.num, v.num) def split(self): n = (RCFNumObj * 1)() d = (RCFNumObj * 1)() Z3_rcf_get_numerator_denominator(self.ctx_ref(), self.num, n, d) return (RCFNum(n[0], self.ctx), RCFNum(d[0], self.ctx)) z3-z3-4.8.7/src/api/python/z3/z3types.py000066400000000000000000000073561356505360400176520ustar00rootroot00000000000000############################################ # Copyright (c) 2012 Microsoft Corporation # # Z3 Python interface # # Author: Leonardo de Moura (leonardo) ############################################ import ctypes class Z3Exception(Exception): def __init__(self, value): self.value = value def __str__(self): return str(self.value) class ContextObj(ctypes.c_void_p): def __init__(self, context): self._as_parameter_ = context def from_param(obj): return obj class Config(ctypes.c_void_p): def __init__(self, config): self._as_parameter_ = config def from_param(obj): return obj class Symbol(ctypes.c_void_p): def __init__(self, symbol): self._as_parameter_ = symbol def from_param(obj): return obj class Sort(ctypes.c_void_p): def __init__(self, sort): self._as_parameter_ = sort def from_param(obj): return obj class FuncDecl(ctypes.c_void_p): def __init__(self, decl): self._as_parameter_ = decl def from_param(obj): return obj class Ast(ctypes.c_void_p): def __init__(self, ast): self._as_parameter_ = ast def from_param(obj): return obj class Pattern(ctypes.c_void_p): def __init__(self, pattern): self._as_parameter_ = pattern def from_param(obj): return obj class Model(ctypes.c_void_p): def __init__(self, model): self._as_parameter_ = model def from_param(obj): return obj class Literals(ctypes.c_void_p): def __init__(self, literals): self._as_parameter_ = literals def from_param(obj): return obj class Constructor(ctypes.c_void_p): def __init__(self, constructor): self._as_parameter_ = constructor def from_param(obj): return obj class ConstructorList(ctypes.c_void_p): def __init__(self, constructor_list): self._as_parameter_ = constructor_list def from_param(obj): return obj class GoalObj(ctypes.c_void_p): def __init__(self, goal): self._as_parameter_ = goal def from_param(obj): return obj class TacticObj(ctypes.c_void_p): def __init__(self, tactic): self._as_parameter_ = tactic def from_param(obj): return obj class ProbeObj(ctypes.c_void_p): def __init__(self, probe): self._as_parameter_ = probe def from_param(obj): return obj class ApplyResultObj(ctypes.c_void_p): def __init__(self, obj): self._as_parameter_ = obj def from_param(obj): return obj class StatsObj(ctypes.c_void_p): def __init__(self, statistics): self._as_parameter_ = statistics def from_param(obj): return obj class SolverObj(ctypes.c_void_p): def __init__(self, solver): self._as_parameter_ = solver def from_param(obj): return obj class FixedpointObj(ctypes.c_void_p): def __init__(self, fixedpoint): self._as_parameter_ = fixedpoint def from_param(obj): return obj class OptimizeObj(ctypes.c_void_p): def __init__(self, optimize): self._as_parameter_ = optimize def from_param(obj): return obj class ModelObj(ctypes.c_void_p): def __init__(self, model): self._as_parameter_ = model def from_param(obj): return obj class AstVectorObj(ctypes.c_void_p): def __init__(self, vector): self._as_parameter_ = vector def from_param(obj): return obj class AstMapObj(ctypes.c_void_p): def __init__(self, ast_map): self._as_parameter_ = ast_map def from_param(obj): return obj class Params(ctypes.c_void_p): def __init__(self, params): self._as_parameter_ = params def from_param(obj): return obj class ParamDescrs(ctypes.c_void_p): def __init__(self, paramdescrs): self._as_parameter_ = paramdescrs def from_param(obj): return obj class FuncInterpObj(ctypes.c_void_p): def __init__(self, f): self._as_parameter_ = f def from_param(obj): return obj class FuncEntryObj(ctypes.c_void_p): def __init__(self, e): self._as_parameter_ = e def from_param(obj): return obj class RCFNumObj(ctypes.c_void_p): def __init__(self, e): self._as_parameter_ = e def from_param(obj): return obj z3-z3-4.8.7/src/api/python/z3/z3util.py000066400000000000000000000262551356505360400174620ustar00rootroot00000000000000############################################ # Copyright (c) 2012 Microsoft Corporation # # Z3 Python interface # # Authors: Leonardo de Moura (leonardo) # ThanhVu (Vu) Nguyen ############################################ """ Usage: import common_z3 as CM_Z3 """ from .z3 import * def vset(seq, idfun=None, as_list=True): # This functions preserves the order of arguments while removing duplicates. # This function is from https://code.google.com/p/common-python-vu/source/browse/vu_common.py # (Thanhu's personal code). It has been copied here to avoid a dependency on vu_common.py. """ order preserving >>> vset([[11,2],1, [10,['9',1]],2, 1, [11,2],[3,3],[10,99],1,[10,['9',1]]],idfun=repr) [[11, 2], 1, [10, ['9', 1]], 2, [3, 3], [10, 99]] """ def _uniq_normal(seq): d_ = {} for s in seq: if s not in d_: d_[s] = None yield s def _uniq_idfun(seq,idfun): d_ = {} for s in seq: h_ = idfun(s) if h_ not in d_: d_[h_] = None yield s if idfun is None: res = _uniq_normal(seq) else: res = _uniq_idfun(seq,idfun) return list(res) if as_list else res def get_z3_version(as_str=False): major = ctypes.c_uint(0) minor = ctypes.c_uint(0) build = ctypes.c_uint(0) rev = ctypes.c_uint(0) Z3_get_version(major,minor,build,rev) rs = map(int,(major.value,minor.value,build.value,rev.value)) if as_str: return "{}.{}.{}.{}".format(*rs) else: return rs def ehash(v): """ Returns a 'stronger' hash value than the default hash() method. The result from hash() is not enough to distinguish between 2 z3 expressions in some cases. Note: the following doctests will fail with Python 2.x as the default formatting doesn't match that of 3.x. >>> x1 = Bool('x'); x2 = Bool('x'); x3 = Int('x') >>> print(x1.hash(),x2.hash(),x3.hash()) #BAD: all same hash values 783810685 783810685 783810685 >>> print(ehash(x1), ehash(x2), ehash(x3)) x_783810685_1 x_783810685_1 x_783810685_2 """ if z3_debug(): assert is_expr(v) return "{}_{}_{}".format(str(v),v.hash(),v.sort_kind()) """ In Z3, variables are called *uninterpreted* consts and variables are *interpreted* consts. """ def is_expr_var(v): """ EXAMPLES: >>> is_expr_var(Int('7')) True >>> is_expr_var(IntVal('7')) False >>> is_expr_var(Bool('y')) True >>> is_expr_var(Int('x') + 7 == Int('y')) False >>> LOnOff, (On,Off) = EnumSort("LOnOff",['On','Off']) >>> Block,Reset,SafetyInjection=Consts("Block Reset SafetyInjection",LOnOff) >>> is_expr_var(LOnOff) False >>> is_expr_var(On) False >>> is_expr_var(Block) True >>> is_expr_var(SafetyInjection) True """ return is_const(v) and v.decl().kind()==Z3_OP_UNINTERPRETED def is_expr_val(v): """ EXAMPLES: >>> is_expr_val(Int('7')) False >>> is_expr_val(IntVal('7')) True >>> is_expr_val(Bool('y')) False >>> is_expr_val(Int('x') + 7 == Int('y')) False >>> LOnOff, (On,Off) = EnumSort("LOnOff",['On','Off']) >>> Block,Reset,SafetyInjection=Consts("Block Reset SafetyInjection",LOnOff) >>> is_expr_val(LOnOff) False >>> is_expr_val(On) True >>> is_expr_val(Block) False >>> is_expr_val(SafetyInjection) False """ return is_const(v) and v.decl().kind()!=Z3_OP_UNINTERPRETED def get_vars(f,rs=[]): """ >>> x,y = Ints('x y') >>> a,b = Bools('a b') >>> get_vars(Implies(And(x+y==0,x*2==10),Or(a,Implies(a,b==False)))) [x, y, a, b] """ if z3_debug(): assert is_expr(f) if is_const(f): if is_expr_val(f): return rs else: #variable return vset(rs + [f],str) else: for f_ in f.children(): rs = get_vars(f_,rs) return vset(rs,str) def mk_var(name,vsort): if vsort.kind() == Z3_INT_SORT: v = Int(name) elif vsort.kind() == Z3_REAL_SORT: v = Real(name) elif vsort.kind() == Z3_BOOL_SORT: v = Bool(name) elif vsort.kind() == Z3_DATATYPE_SORT: v = Const(name,vsort) else: assert False, 'Cannot handle this sort (s: %sid: %d)'\ %(vsort,vsort.kind()) return v def prove(claim,assume=None,verbose=0): """ >>> r,m = prove(BoolVal(True),verbose=0); r,model_str(m,as_str=False) (True, None) #infinite counter example when proving contradiction >>> r,m = prove(BoolVal(False)); r,model_str(m,as_str=False) (False, []) >>> x,y,z=Bools('x y z') >>> r,m = prove(And(x,Not(x))); r,model_str(m,as_str=True) (False, '[]') >>> r,m = prove(True,assume=And(x,Not(x)),verbose=0) Traceback (most recent call last): ... AssertionError: Assumption is always False! >>> r,m = prove(Implies(x,x),assume=y,verbose=2); r,model_str(m,as_str=False) assume: y claim: Implies(x, x) to_prove: Implies(y, Implies(x, x)) (True, None) >>> r,m = prove(And(x,True),assume=y,verbose=0); r,model_str(m,as_str=False) (False, [(x, False), (y, True)]) >>> r,m = prove(And(x,y),assume=y,verbose=0) >>> print(r) False >>> print(model_str(m,as_str=True)) x = False y = True >>> a,b = Ints('a b') >>> r,m = prove(a**b == b**a,assume=None,verbose=0) E: cannot solve ! >>> r is None and m is None True """ if z3_debug(): assert not assume or is_expr(assume) to_prove = claim if assume: if z3_debug(): is_proved,_ = prove(Not(assume)) def _f(): emsg = "Assumption is always False!" if verbose >= 2: emsg = "{}\n{}".format(assume,emsg) return emsg assert is_proved==False, _f() to_prove = Implies(assume,to_prove) if verbose >= 2: print('assume: ') print(assume) print('claim: ') print(claim) print('to_prove: ') print(to_prove) f = Not(to_prove) models = get_models(f,k=1) if models is None: #unknown print('E: cannot solve !') return None, None elif models == False: #unsat return True,None else: #sat if z3_debug(): assert isinstance(models,list) if models: return False, models[0] #the first counterexample else: return False, [] #infinite counterexample,models def get_models(f,k): """ Returns the first k models satisfiying f. If f is not satisfiable, returns False. If f cannot be solved, returns None If f is satisfiable, returns the first k models Note that if f is a tautology, e.g.\ True, then the result is [] Based on http://stackoverflow.com/questions/11867611/z3py-checking-all-solutions-for-equation EXAMPLES: >>> x, y = Ints('x y') >>> len(get_models(And(0<=x,x <= 4),k=11)) 5 >>> get_models(And(0<=x**y,x <= 1),k=2) is None True >>> get_models(And(0<=x,x <= -1),k=2) False >>> len(get_models(x+y==7,5)) 5 >>> len(get_models(And(x<=5,x>=1),7)) 5 >>> get_models(And(x<=0,x>=5),7) False >>> x = Bool('x') >>> get_models(And(x,Not(x)),k=1) False >>> get_models(Implies(x,x),k=1) [] >>> get_models(BoolVal(True),k=1) [] """ if z3_debug(): assert is_expr(f) assert k>=1 s = Solver() s.add(f) models = [] i = 0 while s.check() == sat and i < k: i = i + 1 m = s.model() if not m: #if m == [] break models.append(m) #create new constraint to block the current model block = Not(And([v() == m[v] for v in m])) s.add(block) if s.check() == unknown: return None elif s.check() == unsat and i==0: return False else: return models def is_tautology(claim,verbose=0): """ >>> is_tautology(Implies(Bool('x'),Bool('x'))) True >>> is_tautology(Implies(Bool('x'),Bool('y'))) False >>> is_tautology(BoolVal(True)) True >>> is_tautology(BoolVal(False)) False """ return prove(claim=claim,assume=None,verbose=verbose)[0] def is_contradiction(claim,verbose=0): """ >>> x,y=Bools('x y') >>> is_contradiction(BoolVal(False)) True >>> is_contradiction(BoolVal(True)) False >>> is_contradiction(x) False >>> is_contradiction(Implies(x,y)) False >>> is_contradiction(Implies(x,x)) False >>> is_contradiction(And(x,Not(x))) True """ return prove(claim=Not(claim),assume=None,verbose=verbose)[0] def exact_one_model(f): """ return True if f has exactly 1 model, False otherwise. EXAMPLES: >>> x, y = Ints('x y') >>> exact_one_model(And(0<=x**y,x <= 0)) False >>> exact_one_model(And(0<=x,x <= 0)) True >>> exact_one_model(And(0<=x,x <= 1)) False >>> exact_one_model(And(0<=x,x <= -1)) False """ models = get_models(f,k=2) if isinstance(models,list): return len(models)==1 else: return False def myBinOp(op,*L): """ >>> myAnd(*[Bool('x'),Bool('y')]) And(x, y) >>> myAnd(*[Bool('x'),None]) x >>> myAnd(*[Bool('x')]) x >>> myAnd(*[]) >>> myAnd(Bool('x'),Bool('y')) And(x, y) >>> myAnd(*[Bool('x'),Bool('y')]) And(x, y) >>> myAnd([Bool('x'),Bool('y')]) And(x, y) >>> myAnd((Bool('x'),Bool('y'))) And(x, y) >>> myAnd(*[Bool('x'),Bool('y'),True]) Traceback (most recent call last): ... AssertionError """ if z3_debug(): assert op == Z3_OP_OR or op == Z3_OP_AND or op == Z3_OP_IMPLIES if len(L)==1 and (isinstance(L[0],list) or isinstance(L[0],tuple)): L = L[0] if z3_debug(): assert all(not isinstance(l,bool) for l in L) L = [l for l in L if is_expr(l)] if L: if len(L)==1: return L[0] else: if op == Z3_OP_OR: return Or(L) elif op == Z3_OP_AND: return And(L) else: #IMPLIES return Implies(L[0],L[1]) else: return None def myAnd(*L): return myBinOp(Z3_OP_AND,*L) def myOr(*L): return myBinOp(Z3_OP_OR,*L) def myImplies(a,b):return myBinOp(Z3_OP_IMPLIES,[a,b]) Iff = lambda f: And(Implies(f[0],f[1]),Implies(f[1],f[0])) def model_str(m,as_str=True): """ Returned a 'sorted' model (so that it's easier to see) The model is sorted by its key, e.g. if the model is y = 3 , x = 10, then the result is x = 10, y = 3 EXAMPLES: see doctest exampels from function prove() """ if z3_debug(): assert m is None or m == [] or isinstance(m,ModelRef) if m : vs = [(v,m[v]) for v in m] vs = sorted(vs,key=lambda a,_: str(a)) if as_str: return '\n'.join(['{} = {}'.format(k,v) for (k,v) in vs]) else: return vs else: return str(m) if as_str else m z3-z3-4.8.7/src/api/python/z3test.py000066400000000000000000000007201356505360400171150ustar00rootroot00000000000000############################################ # Copyright (c) 2012 Microsoft Corporation # # Z3 Python interface # # Author: Leonardo de Moura (leonardo) ############################################ import z3, doctest, sys if len(sys.argv) < 2 or sys.argv[1] == 'z3': r = doctest.testmod(z3.z3) elif sys.argv[1] == 'z3num': r = doctest.testmod(z3.z3num) else: print('Usage: z3test.py (z3 | z3num)') sys.exit(1) if r.failed != 0: sys.exit(1) z3-z3-4.8.7/src/api/z3.h000066400000000000000000000010301356505360400144660ustar00rootroot00000000000000/*++ Copyright (c) 2007 Microsoft Corporation Module Name: z3.h Abstract: Z3 API. Author: Nikolaj Bjorner (nbjorner) Leonardo de Moura (leonardo) 2007-06-8 Notes: --*/ #ifndef Z3_H_ #define Z3_H_ #include #include #include #include "z3_macros.h" #include "z3_api.h" #include "z3_ast_containers.h" #include "z3_algebraic.h" #include "z3_polynomial.h" #include "z3_rcf.h" #include "z3_fixedpoint.h" #include "z3_optimization.h" #include "z3_fpa.h" #include "z3_spacer.h" #endif z3-z3-4.8.7/src/api/z3_algebraic.h000066400000000000000000000150331356505360400164670ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: z3_algebraic.h Abstract: Additional APIs for handling Z3 algebraic numbers encoded as Z3_ASTs Author: Leonardo de Moura (leonardo) 2012-12-07 Notes: --*/ #ifndef Z3_ALGEBRAIC_H_ #define Z3_ALGEBRAIC_H_ #ifdef __cplusplus extern "C" { #endif // __cplusplus /** \defgroup capi C API */ /*@{*/ /** @name Algebraic Numbers */ /*@{*/ /** \brief Return \c true if \c a can be used as value in the Z3 real algebraic number package. def_API('Z3_algebraic_is_value', BOOL, (_in(CONTEXT), _in(AST))) */ bool Z3_API Z3_algebraic_is_value(Z3_context c, Z3_ast a); /** \brief Return \c true if \c a is positive, and \c false otherwise. \pre Z3_algebraic_is_value(c, a) def_API('Z3_algebraic_is_pos', BOOL, (_in(CONTEXT), _in(AST))) */ bool Z3_API Z3_algebraic_is_pos(Z3_context c, Z3_ast a); /** \brief Return \c true if \c a is negative, and \c false otherwise. \pre Z3_algebraic_is_value(c, a) def_API('Z3_algebraic_is_neg', BOOL, (_in(CONTEXT), _in(AST))) */ bool Z3_API Z3_algebraic_is_neg(Z3_context c, Z3_ast a); /** \brief Return \c true if \c a is zero, and \c false otherwise. \pre Z3_algebraic_is_value(c, a) def_API('Z3_algebraic_is_zero', BOOL, (_in(CONTEXT), _in(AST))) */ bool Z3_API Z3_algebraic_is_zero(Z3_context c, Z3_ast a); /** \brief Return 1 if \c a is positive, 0 if \c a is zero, and -1 if \c a is negative. \pre Z3_algebraic_is_value(c, a) def_API('Z3_algebraic_sign', INT, (_in(CONTEXT), _in(AST))) */ int Z3_API Z3_algebraic_sign(Z3_context c, Z3_ast a); /** \brief Return the value a + b. \pre Z3_algebraic_is_value(c, a) \pre Z3_algebraic_is_value(c, b) \post Z3_algebraic_is_value(c, result) def_API('Z3_algebraic_add', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_algebraic_add(Z3_context c, Z3_ast a, Z3_ast b); /** \brief Return the value a - b. \pre Z3_algebraic_is_value(c, a) \pre Z3_algebraic_is_value(c, b) \post Z3_algebraic_is_value(c, result) def_API('Z3_algebraic_sub', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_algebraic_sub(Z3_context c, Z3_ast a, Z3_ast b); /** \brief Return the value a * b. \pre Z3_algebraic_is_value(c, a) \pre Z3_algebraic_is_value(c, b) \post Z3_algebraic_is_value(c, result) def_API('Z3_algebraic_mul', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_algebraic_mul(Z3_context c, Z3_ast a, Z3_ast b); /** \brief Return the value a / b. \pre Z3_algebraic_is_value(c, a) \pre Z3_algebraic_is_value(c, b) \pre !Z3_algebraic_is_zero(c, b) \post Z3_algebraic_is_value(c, result) def_API('Z3_algebraic_div', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_algebraic_div(Z3_context c, Z3_ast a, Z3_ast b); /** \brief Return the a^(1/k) \pre Z3_algebraic_is_value(c, a) \pre k is even => !Z3_algebraic_is_neg(c, a) \post Z3_algebraic_is_value(c, result) def_API('Z3_algebraic_root', AST, (_in(CONTEXT), _in(AST), _in(UINT))) */ Z3_ast Z3_API Z3_algebraic_root(Z3_context c, Z3_ast a, unsigned k); /** \brief Return the a^k \pre Z3_algebraic_is_value(c, a) \post Z3_algebraic_is_value(c, result) def_API('Z3_algebraic_power', AST, (_in(CONTEXT), _in(AST), _in(UINT))) */ Z3_ast Z3_API Z3_algebraic_power(Z3_context c, Z3_ast a, unsigned k); /** \brief Return \c true if a < b, and \c false otherwise. \pre Z3_algebraic_is_value(c, a) \pre Z3_algebraic_is_value(c, b) def_API('Z3_algebraic_lt', BOOL, (_in(CONTEXT), _in(AST), _in(AST))) */ bool Z3_API Z3_algebraic_lt(Z3_context c, Z3_ast a, Z3_ast b); /** \brief Return \c true if a > b, and \c false otherwise. \pre Z3_algebraic_is_value(c, a) \pre Z3_algebraic_is_value(c, b) def_API('Z3_algebraic_gt', BOOL, (_in(CONTEXT), _in(AST), _in(AST))) */ bool Z3_API Z3_algebraic_gt(Z3_context c, Z3_ast a, Z3_ast b); /** \brief Return \c true if a <= b, and \c false otherwise. \pre Z3_algebraic_is_value(c, a) \pre Z3_algebraic_is_value(c, b) def_API('Z3_algebraic_le', BOOL, (_in(CONTEXT), _in(AST), _in(AST))) */ bool Z3_API Z3_algebraic_le(Z3_context c, Z3_ast a, Z3_ast b); /** \brief Return \c true if a >= b, and \c false otherwise. \pre Z3_algebraic_is_value(c, a) \pre Z3_algebraic_is_value(c, b) def_API('Z3_algebraic_ge', BOOL, (_in(CONTEXT), _in(AST), _in(AST))) */ bool Z3_API Z3_algebraic_ge(Z3_context c, Z3_ast a, Z3_ast b); /** \brief Return \c true if a == b, and \c false otherwise. \pre Z3_algebraic_is_value(c, a) \pre Z3_algebraic_is_value(c, b) def_API('Z3_algebraic_eq', BOOL, (_in(CONTEXT), _in(AST), _in(AST))) */ bool Z3_API Z3_algebraic_eq(Z3_context c, Z3_ast a, Z3_ast b); /** \brief Return \c true if a != b, and \c false otherwise. \pre Z3_algebraic_is_value(c, a) \pre Z3_algebraic_is_value(c, b) def_API('Z3_algebraic_neq', BOOL, (_in(CONTEXT), _in(AST), _in(AST))) */ bool Z3_API Z3_algebraic_neq(Z3_context c, Z3_ast a, Z3_ast b); /** \brief Given a multivariate polynomial p(x_0, ..., x_{n-1}, x_n), returns the roots of the univariate polynomial p(a[0], ..., a[n-1], x_n). \pre p is a Z3 expression that contains only arithmetic terms and free variables. \pre forall i in [0, n) Z3_algebraic_is_value(c, a[i]) \post forall r in result Z3_algebraic_is_value(c, result) def_API('Z3_algebraic_roots', AST_VECTOR, (_in(CONTEXT), _in(AST), _in(UINT), _in_array(2, AST))) */ Z3_ast_vector Z3_API Z3_algebraic_roots(Z3_context c, Z3_ast p, unsigned n, Z3_ast a[]); /** \brief Given a multivariate polynomial p(x_0, ..., x_{n-1}), return the sign of p(a[0], ..., a[n-1]). \pre p is a Z3 expression that contains only arithmetic terms and free variables. \pre forall i in [0, n) Z3_algebraic_is_value(c, a[i]) def_API('Z3_algebraic_eval', INT, (_in(CONTEXT), _in(AST), _in(UINT), _in_array(2, AST))) */ int Z3_API Z3_algebraic_eval(Z3_context c, Z3_ast p, unsigned n, Z3_ast a[]); /*@}*/ /*@}*/ #ifdef __cplusplus } #endif // __cplusplus #endif z3-z3-4.8.7/src/api/z3_api.h000066400000000000000000007171001356505360400153330ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation --*/ #ifndef Z3_API_H_ #define Z3_API_H_ DEFINE_TYPE(Z3_symbol); DEFINE_TYPE(Z3_literals); DEFINE_TYPE(Z3_config); DEFINE_TYPE(Z3_context); DEFINE_TYPE(Z3_sort); #define Z3_sort_opt Z3_sort DEFINE_TYPE(Z3_func_decl); DEFINE_TYPE(Z3_ast); #define Z3_ast_opt Z3_ast DEFINE_TYPE(Z3_app); DEFINE_TYPE(Z3_pattern); DEFINE_TYPE(Z3_model); DEFINE_TYPE(Z3_constructor); DEFINE_TYPE(Z3_constructor_list); DEFINE_TYPE(Z3_params); DEFINE_TYPE(Z3_param_descrs); DEFINE_TYPE(Z3_goal); DEFINE_TYPE(Z3_tactic); DEFINE_TYPE(Z3_probe); DEFINE_TYPE(Z3_stats); DEFINE_TYPE(Z3_solver); DEFINE_TYPE(Z3_ast_vector); DEFINE_TYPE(Z3_ast_map); DEFINE_TYPE(Z3_apply_result); DEFINE_TYPE(Z3_func_interp); #define Z3_func_interp_opt Z3_func_interp DEFINE_TYPE(Z3_func_entry); DEFINE_TYPE(Z3_fixedpoint); DEFINE_TYPE(Z3_optimize); DEFINE_TYPE(Z3_rcf_num); /** \defgroup capi C API */ /*@{*/ /** @name Types @{ Most of the types in the C API are opaque pointers. - \c Z3_config: configuration object used to initialize logical contexts. - \c Z3_context: manager of all other Z3 objects, global configuration options, etc. - \c Z3_symbol: Lisp-like symbol used to name types, constants, and functions. A symbol can be created using string or integers. - \c Z3_ast: abstract syntax tree node. That is, the data-structure used in Z3 to represent terms, formulas and types. - \c Z3_sort: kind of AST used to represent types. - \c Z3_func_decl: kind of AST used to represent function symbols. - \c Z3_app: kind of AST used to represent function applications. - \c Z3_pattern: kind of AST used to represent pattern and multi-patterns used to guide quantifier instantiation. - \c Z3_constructor: type constructor for a (recursive) datatype. - \c Z3_constructor_list: list of constructors for a (recursive) datatype. - \c Z3_params: parameter set used to configure many components such as: simplifiers, tactics, solvers, etc. - \c Z3_param_descrs: provides a collection of parameter names, their types, default values and documentation strings. Solvers, tactics, and other objects accept different collection of parameters. - \c Z3_model: model for the constraints asserted into the logical context. - \c Z3_func_interp: interpretation of a function in a model. - \c Z3_func_entry: representation of the value of a \c Z3_func_interp at a particular point. - \c Z3_fixedpoint: context for the recursive predicate solver. - \c Z3_optimize: context for solving optimization queries. - \c Z3_ast_vector: vector of \c Z3_ast objects. - \c Z3_ast_map: mapping from \c Z3_ast to \c Z3_ast objects. - \c Z3_goal: set of formulas that can be solved and/or transformed using tactics and solvers. - \c Z3_tactic: basic building block for creating custom solvers for specific problem domains. - \c Z3_probe: function/predicate used to inspect a goal and collect information that may be used to decide which solver and/or preprocessing step will be used. - \c Z3_apply_result: collection of subgoals resulting from applying of a tactic to a goal. - \c Z3_solver: (incremental) solver, possibly specialized by a particular tactic or logic. - \c Z3_stats: statistical data for a solver. */ /** \brief Z3 Boolean type. It is just an alias for \c bool. */ typedef bool Z3_bool; /** \brief Z3 string type. It is just an alias for \ccode{const char *}. */ typedef const char * Z3_string; typedef char const* Z3_char_ptr; typedef Z3_string * Z3_string_ptr; /** \brief True value. It is just an alias for \c true. */ #define Z3_TRUE true /** \brief False value. It is just an alias for \c false. */ #define Z3_FALSE false /** \brief Lifted Boolean type: \c false, \c undefined, \c true. */ typedef enum { Z3_L_FALSE = -1, Z3_L_UNDEF, Z3_L_TRUE } Z3_lbool; /** \brief The different kinds of symbol. In Z3, a symbol can be represented using integers and strings (See #Z3_get_symbol_kind). \sa Z3_mk_int_symbol \sa Z3_mk_string_symbol */ typedef enum { Z3_INT_SYMBOL, Z3_STRING_SYMBOL } Z3_symbol_kind; /** \brief The different kinds of parameters that can be associated with function symbols. \sa Z3_get_decl_num_parameters \sa Z3_get_decl_parameter_kind - Z3_PARAMETER_INT is used for integer parameters. - Z3_PARAMETER_DOUBLE is used for double parameters. - Z3_PARAMETER_RATIONAL is used for parameters that are rational numbers. - Z3_PARAMETER_SYMBOL is used for parameters that are symbols. - Z3_PARAMETER_SORT is used for sort parameters. - Z3_PARAMETER_AST is used for expression parameters. - Z3_PARAMETER_FUNC_DECL is used for function declaration parameters. */ typedef enum { Z3_PARAMETER_INT, Z3_PARAMETER_DOUBLE, Z3_PARAMETER_RATIONAL, Z3_PARAMETER_SYMBOL, Z3_PARAMETER_SORT, Z3_PARAMETER_AST, Z3_PARAMETER_FUNC_DECL } Z3_parameter_kind; /** \brief The different kinds of Z3 types (See #Z3_get_sort_kind). */ typedef enum { Z3_UNINTERPRETED_SORT, Z3_BOOL_SORT, Z3_INT_SORT, Z3_REAL_SORT, Z3_BV_SORT, Z3_ARRAY_SORT, Z3_DATATYPE_SORT, Z3_RELATION_SORT, Z3_FINITE_DOMAIN_SORT, Z3_FLOATING_POINT_SORT, Z3_ROUNDING_MODE_SORT, Z3_SEQ_SORT, Z3_RE_SORT, Z3_UNKNOWN_SORT = 1000 } Z3_sort_kind; /** \brief The different kinds of Z3 AST (abstract syntax trees). That is, terms, formulas and types. - Z3_APP_AST: constant and applications - Z3_NUMERAL_AST: numeral constants - Z3_VAR_AST: bound variables - Z3_QUANTIFIER_AST: quantifiers - Z3_SORT_AST: sort - Z3_FUNC_DECL_AST: function declaration - Z3_UNKNOWN_AST: internal */ typedef enum { Z3_NUMERAL_AST, Z3_APP_AST, Z3_VAR_AST, Z3_QUANTIFIER_AST, Z3_SORT_AST, Z3_FUNC_DECL_AST, Z3_UNKNOWN_AST = 1000 } Z3_ast_kind; /** \brief The different kinds of interpreted function kinds. - Z3_OP_TRUE The constant true. - Z3_OP_FALSE The constant false. - Z3_OP_EQ The equality predicate. - Z3_OP_DISTINCT The n-ary distinct predicate (every argument is mutually distinct). - Z3_OP_ITE The ternary if-then-else term. - Z3_OP_AND n-ary conjunction. - Z3_OP_OR n-ary disjunction. - Z3_OP_IFF equivalence (binary). - Z3_OP_XOR Exclusive or. - Z3_OP_NOT Negation. - Z3_OP_IMPLIES Implication. - Z3_OP_OEQ Binary equivalence modulo namings. This binary predicate is used in proof terms. It captures equisatisfiability and equivalence modulo renamings. - Z3_OP_ANUM Arithmetic numeral. - Z3_OP_AGNUM Arithmetic algebraic numeral. Algebraic numbers are used to represent irrational numbers in Z3. - Z3_OP_LE <=. - Z3_OP_GE >=. - Z3_OP_LT <. - Z3_OP_GT >. - Z3_OP_ADD Addition - Binary. - Z3_OP_SUB Binary subtraction. - Z3_OP_UMINUS Unary minus. - Z3_OP_MUL Multiplication - Binary. - Z3_OP_DIV Division - Binary. - Z3_OP_IDIV Integer division - Binary. - Z3_OP_REM Remainder - Binary. - Z3_OP_MOD Modulus - Binary. - Z3_OP_TO_REAL Coercion of integer to real - Unary. - Z3_OP_TO_INT Coercion of real to integer - Unary. - Z3_OP_IS_INT Check if real is also an integer - Unary. - Z3_OP_POWER Power operator x^y. - Z3_OP_STORE Array store. It satisfies select(store(a,i,v),j) = if i = j then v else select(a,j). Array store takes at least 3 arguments. - Z3_OP_SELECT Array select. - Z3_OP_CONST_ARRAY The constant array. For example, select(const(v),i) = v holds for every v and i. The function is unary. - Z3_OP_ARRAY_DEFAULT Default value of arrays. For example default(const(v)) = v. The function is unary. - Z3_OP_ARRAY_MAP Array map operator. It satisfies map[f](a1,..,a_n)[i] = f(a1[i],...,a_n[i]) for every i. - Z3_OP_SET_UNION Set union between two Boolean arrays (two arrays whose range type is Boolean). The function is binary. - Z3_OP_SET_INTERSECT Set intersection between two Boolean arrays. The function is binary. - Z3_OP_SET_DIFFERENCE Set difference between two Boolean arrays. The function is binary. - Z3_OP_SET_COMPLEMENT Set complement of a Boolean array. The function is unary. - Z3_OP_SET_SUBSET Subset predicate between two Boolean arrays. The relation is binary. - Z3_OP_AS_ARRAY An array value that behaves as the function graph of the function passed as parameter. - Z3_OP_ARRAY_EXT Array extensionality function. It takes two arrays as arguments and produces an index, such that the arrays are different if they are different on the index. - Z3_OP_BNUM Bit-vector numeral. - Z3_OP_BIT1 One bit bit-vector. - Z3_OP_BIT0 Zero bit bit-vector. - Z3_OP_BNEG Unary minus. - Z3_OP_BADD Binary addition. - Z3_OP_BSUB Binary subtraction. - Z3_OP_BMUL Binary multiplication. - Z3_OP_BSDIV Binary signed division. - Z3_OP_BUDIV Binary unsigned division. - Z3_OP_BSREM Binary signed remainder. - Z3_OP_BUREM Binary unsigned remainder. - Z3_OP_BSMOD Binary signed modulus. - Z3_OP_BSDIV0 Unary function. bsdiv(x,0) is congruent to bsdiv0(x). - Z3_OP_BUDIV0 Unary function. budiv(x,0) is congruent to budiv0(x). - Z3_OP_BSREM0 Unary function. bsrem(x,0) is congruent to bsrem0(x). - Z3_OP_BUREM0 Unary function. burem(x,0) is congruent to burem0(x). - Z3_OP_BSMOD0 Unary function. bsmod(x,0) is congruent to bsmod0(x). - Z3_OP_ULEQ Unsigned bit-vector <= - Binary relation. - Z3_OP_SLEQ Signed bit-vector <= - Binary relation. - Z3_OP_UGEQ Unsigned bit-vector >= - Binary relation. - Z3_OP_SGEQ Signed bit-vector >= - Binary relation. - Z3_OP_ULT Unsigned bit-vector < - Binary relation. - Z3_OP_SLT Signed bit-vector < - Binary relation. - Z3_OP_UGT Unsigned bit-vector > - Binary relation. - Z3_OP_SGT Signed bit-vector > - Binary relation. - Z3_OP_BAND Bit-wise and - Binary. - Z3_OP_BOR Bit-wise or - Binary. - Z3_OP_BNOT Bit-wise not - Unary. - Z3_OP_BXOR Bit-wise xor - Binary. - Z3_OP_BNAND Bit-wise nand - Binary. - Z3_OP_BNOR Bit-wise nor - Binary. - Z3_OP_BXNOR Bit-wise xnor - Binary. - Z3_OP_CONCAT Bit-vector concatenation - Binary. - Z3_OP_SIGN_EXT Bit-vector sign extension. - Z3_OP_ZERO_EXT Bit-vector zero extension. - Z3_OP_EXTRACT Bit-vector extraction. - Z3_OP_REPEAT Repeat bit-vector n times. - Z3_OP_BREDOR Bit-vector reduce or - Unary. - Z3_OP_BREDAND Bit-vector reduce and - Unary. - Z3_OP_BCOMP . - Z3_OP_BSHL Shift left. - Z3_OP_BLSHR Logical shift right. - Z3_OP_BASHR Arithmetical shift right. - Z3_OP_ROTATE_LEFT Left rotation. - Z3_OP_ROTATE_RIGHT Right rotation. - Z3_OP_EXT_ROTATE_LEFT (extended) Left rotation. Similar to Z3_OP_ROTATE_LEFT, but it is a binary operator instead of a parametric one. - Z3_OP_EXT_ROTATE_RIGHT (extended) Right rotation. Similar to Z3_OP_ROTATE_RIGHT, but it is a binary operator instead of a parametric one. - Z3_OP_INT2BV Coerce integer to bit-vector. NB. This function is not supported by the decision procedures. Only the most rudimentary simplification rules are applied to this function. - Z3_OP_BV2INT Coerce bit-vector to integer. NB. This function is not supported by the decision procedures. Only the most rudimentary simplification rules are applied to this function. - Z3_OP_CARRY Compute the carry bit in a full-adder. The meaning is given by the equivalence (carry l1 l2 l3) <=> (or (and l1 l2) (and l1 l3) (and l2 l3))) - Z3_OP_XOR3 Compute ternary XOR. The meaning is given by the equivalence (xor3 l1 l2 l3) <=> (xor (xor l1 l2) l3) - Z3_OP_BSMUL_NO_OVFL: a predicate to check that bit-wise signed multiplication does not overflow. Signed multiplication overflows if the operands have the same sign and the result of multiplication does not fit within the available bits. \sa Z3_mk_bvmul_no_overflow. - Z3_OP_BUMUL_NO_OVFL: check that bit-wise unsigned multiplication does not overflow. Unsigned multiplication overflows if the result does not fit within the available bits. \sa Z3_mk_bvmul_no_overflow. - Z3_OP_BSMUL_NO_UDFL: check that bit-wise signed multiplication does not underflow. Signed multiplication underflows if the operands have opposite signs and the result of multiplication does not fit within the available bits. Z3_mk_bvmul_no_underflow. - Z3_OP_BSDIV_I: Binary signed division. It has the same semantics as Z3_OP_BSDIV, but created in a context where the second operand can be assumed to be non-zero. - Z3_OP_BUDIV_I: Binary unsigned division. It has the same semantics as Z3_OP_BUDIV, but created in a context where the second operand can be assumed to be non-zero. - Z3_OP_BSREM_I: Binary signed remainder. It has the same semantics as Z3_OP_BSREM, but created in a context where the second operand can be assumed to be non-zero. - Z3_OP_BUREM_I: Binary unsigned remainder. It has the same semantics as Z3_OP_BUREM, but created in a context where the second operand can be assumed to be non-zero. - Z3_OP_BSMOD_I: Binary signed modulus. It has the same semantics as Z3_OP_BSMOD, but created in a context where the second operand can be assumed to be non-zero. - Z3_OP_PR_UNDEF: Undef/Null proof object. - Z3_OP_PR_TRUE: Proof for the expression 'true'. - Z3_OP_PR_ASSERTED: Proof for a fact asserted by the user. - Z3_OP_PR_GOAL: Proof for a fact (tagged as goal) asserted by the user. - Z3_OP_PR_MODUS_PONENS: Given a proof for p and a proof for (implies p q), produces a proof for q. \nicebox{ T1: p T2: (implies p q) [mp T1 T2]: q } The second antecedents may also be a proof for (iff p q). - Z3_OP_PR_REFLEXIVITY: A proof for (R t t), where R is a reflexive relation. This proof object has no antecedents. The only reflexive relations that are used are equivalence modulo namings, equality and equivalence. That is, R is either '~', '=' or 'iff'. - Z3_OP_PR_SYMMETRY: Given an symmetric relation R and a proof for (R t s), produces a proof for (R s t). \nicebox{ T1: (R t s) [symmetry T1]: (R s t) } T1 is the antecedent of this proof object. - Z3_OP_PR_TRANSITIVITY: Given a transitive relation R, and proofs for (R t s) and (R s u), produces a proof for (R t u). \nicebox{ T1: (R t s) T2: (R s u) [trans T1 T2]: (R t u) } - Z3_OP_PR_TRANSITIVITY_STAR: Condensed transitivity proof. It combines several symmetry and transitivity proofs. Example: \nicebox{ T1: (R a b) T2: (R c b) T3: (R c d) [trans* T1 T2 T3]: (R a d) } R must be a symmetric and transitive relation. Assuming that this proof object is a proof for (R s t), then a proof checker must check if it is possible to prove (R s t) using the antecedents, symmetry and transitivity. That is, if there is a path from s to t, if we view every antecedent (R a b) as an edge between a and b. - Z3_OP_PR_MONOTONICITY: Monotonicity proof object. \nicebox{ T1: (R t_1 s_1) ... Tn: (R t_n s_n) [monotonicity T1 ... Tn]: (R (f t_1 ... t_n) (f s_1 ... s_n)) } Remark: if t_i == s_i, then the antecedent Ti is suppressed. That is, reflexivity proofs are suppressed to save space. - Z3_OP_PR_QUANT_INTRO: Given a proof for (~ p q), produces a proof for (~ (forall (x) p) (forall (x) q)). T1: (~ p q) [quant-intro T1]: (~ (forall (x) p) (forall (x) q)) - Z3_OP_PR_BIND: Given a proof p, produces a proof of lambda x . p, where x are free variables in p. T1: f [proof-bind T1] forall (x) f - Z3_OP_PR_DISTRIBUTIVITY: Distributivity proof object. Given that f (= or) distributes over g (= and), produces a proof for (= (f a (g c d)) (g (f a c) (f a d))) If f and g are associative, this proof also justifies the following equality: (= (f (g a b) (g c d)) (g (f a c) (f a d) (f b c) (f b d))) where each f and g can have arbitrary number of arguments. This proof object has no antecedents. Remark. This rule is used by the CNF conversion pass and instantiated by f = or, and g = and. - Z3_OP_PR_AND_ELIM: Given a proof for (and l_1 ... l_n), produces a proof for l_i \nicebox{ T1: (and l_1 ... l_n) [and-elim T1]: l_i } - Z3_OP_PR_NOT_OR_ELIM: Given a proof for (not (or l_1 ... l_n)), produces a proof for (not l_i). \nicebox{ T1: (not (or l_1 ... l_n)) [not-or-elim T1]: (not l_i) } - Z3_OP_PR_REWRITE: A proof for a local rewriting step (= t s). The head function symbol of t is interpreted. This proof object has no antecedents. The conclusion of a rewrite rule is either an equality (= t s), an equivalence (iff t s), or equi-satisfiability (~ t s). Remark: if f is bool, then = is iff. Examples: \nicebox{ (= (+ x 0) x) (= (+ x 1 2) (+ 3 x)) (iff (or x false) x) } - Z3_OP_PR_REWRITE_STAR: A proof for rewriting an expression t into an expression s. This proof object can have n antecedents. The antecedents are proofs for equalities used as substitution rules. The proof rule is used in a few cases. The cases are: - When applying contextual simplification (CONTEXT_SIMPLIFIER=true) - When converting bit-vectors to Booleans (BIT2BOOL=true) - Z3_OP_PR_PULL_QUANT: A proof for (iff (f (forall (x) q(x)) r) (forall (x) (f (q x) r))). This proof object has no antecedents. - Z3_OP_PR_PUSH_QUANT: A proof for: \nicebox{ (iff (forall (x_1 ... x_m) (and p_1[x_1 ... x_m] ... p_n[x_1 ... x_m])) (and (forall (x_1 ... x_m) p_1[x_1 ... x_m]) ... (forall (x_1 ... x_m) p_n[x_1 ... x_m]))) } This proof object has no antecedents. - Z3_OP_PR_ELIM_UNUSED_VARS: A proof for (iff (forall (x_1 ... x_n y_1 ... y_m) p[x_1 ... x_n]) (forall (x_1 ... x_n) p[x_1 ... x_n])) It is used to justify the elimination of unused variables. This proof object has no antecedents. - Z3_OP_PR_DER: A proof for destructive equality resolution: (iff (forall (x) (or (not (= x t)) P[x])) P[t]) if x does not occur in t. This proof object has no antecedents. Several variables can be eliminated simultaneously. - Z3_OP_PR_QUANT_INST: A proof of (or (not (forall (x) (P x))) (P a)) - Z3_OP_PR_HYPOTHESIS: Mark a hypothesis in a natural deduction style proof. - Z3_OP_PR_LEMMA: \nicebox{ T1: false [lemma T1]: (or (not l_1) ... (not l_n)) } This proof object has one antecedent: a hypothetical proof for false. It converts the proof in a proof for (or (not l_1) ... (not l_n)), when T1 contains the open hypotheses: l_1, ..., l_n. The hypotheses are closed after an application of a lemma. Furthermore, there are no other open hypotheses in the subtree covered by the lemma. - Z3_OP_PR_UNIT_RESOLUTION: \nicebox{ T1: (or l_1 ... l_n l_1' ... l_m') T2: (not l_1) ... T(n+1): (not l_n) [unit-resolution T1 ... T(n+1)]: (or l_1' ... l_m') } - Z3_OP_PR_IFF_TRUE: \nicebox{ T1: p [iff-true T1]: (iff p true) } - Z3_OP_PR_IFF_FALSE: \nicebox{ T1: (not p) [iff-false T1]: (iff p false) } - Z3_OP_PR_COMMUTATIVITY: [comm]: (= (f a b) (f b a)) f is a commutative operator. This proof object has no antecedents. Remark: if f is bool, then = is iff. - Z3_OP_PR_DEF_AXIOM: Proof object used to justify Tseitin's like axioms: \nicebox{ (or (not (and p q)) p) (or (not (and p q)) q) (or (not (and p q r)) p) (or (not (and p q r)) q) (or (not (and p q r)) r) ... (or (and p q) (not p) (not q)) (or (not (or p q)) p q) (or (or p q) (not p)) (or (or p q) (not q)) (or (not (iff p q)) (not p) q) (or (not (iff p q)) p (not q)) (or (iff p q) (not p) (not q)) (or (iff p q) p q) (or (not (ite a b c)) (not a) b) (or (not (ite a b c)) a c) (or (ite a b c) (not a) (not b)) (or (ite a b c) a (not c)) (or (not (not a)) (not a)) (or (not a) a) } This proof object has no antecedents. Note: all axioms are propositional tautologies. Note also that 'and' and 'or' can take multiple arguments. You can recover the propositional tautologies by unfolding the Boolean connectives in the axioms a small bounded number of steps (=3). - Z3_OP_PR_ASSUMPTION_ADD Clausal proof adding axiom - Z3_OP_PR_LEMMA_ADD Clausal proof lemma addition - Z3_OP_PR_REDUNDANT_DEL Clausal proof lemma deletion - Z3_OP_PR_CLAUSE_TRAIL, Clausal proof trail of additions and deletions - Z3_OP_PR_DEF_INTRO: Introduces a name for a formula/term. Suppose e is an expression with free variables x, and def-intro introduces the name n(x). The possible cases are: When e is of Boolean type: [def-intro]: (and (or n (not e)) (or (not n) e)) or: [def-intro]: (or (not n) e) when e only occurs positively. When e is of the form (ite cond th el): [def-intro]: (and (or (not cond) (= n th)) (or cond (= n el))) Otherwise: [def-intro]: (= n e) - Z3_OP_PR_APPLY_DEF: [apply-def T1]: F ~ n F is 'equivalent' to n, given that T1 is a proof that n is a name for F. - Z3_OP_PR_IFF_OEQ: T1: (iff p q) [iff~ T1]: (~ p q) - Z3_OP_PR_NNF_POS: Proof for a (positive) NNF step. Example: \nicebox{ T1: (not s_1) ~ r_1 T2: (not s_2) ~ r_2 T3: s_1 ~ r_1' T4: s_2 ~ r_2' [nnf-pos T1 T2 T3 T4]: (~ (iff s_1 s_2) (and (or r_1 r_2') (or r_1' r_2))) } The negation normal form steps NNF_POS and NNF_NEG are used in the following cases: (a) When creating the NNF of a positive force quantifier. The quantifier is retained (unless the bound variables are eliminated). Example \nicebox{ T1: q ~ q_new [nnf-pos T1]: (~ (forall (x T) q) (forall (x T) q_new)) } (b) When recursively creating NNF over Boolean formulas, where the top-level connective is changed during NNF conversion. The relevant Boolean connectives for NNF_POS are 'implies', 'iff', 'xor', 'ite'. NNF_NEG furthermore handles the case where negation is pushed over Boolean connectives 'and' and 'or'. - Z3_OP_PR_NNF_NEG: Proof for a (negative) NNF step. Examples: \nicebox{ T1: (not s_1) ~ r_1 ... Tn: (not s_n) ~ r_n [nnf-neg T1 ... Tn]: (not (and s_1 ... s_n)) ~ (or r_1 ... r_n) and T1: (not s_1) ~ r_1 ... Tn: (not s_n) ~ r_n [nnf-neg T1 ... Tn]: (not (or s_1 ... s_n)) ~ (and r_1 ... r_n) and T1: (not s_1) ~ r_1 T2: (not s_2) ~ r_2 T3: s_1 ~ r_1' T4: s_2 ~ r_2' [nnf-neg T1 T2 T3 T4]: (~ (not (iff s_1 s_2)) (and (or r_1 r_2) (or r_1' r_2'))) } - Z3_OP_PR_SKOLEMIZE: Proof for: \nicebox{ [sk]: (~ (not (forall x (p x y))) (not (p (sk y) y))) [sk]: (~ (exists x (p x y)) (p (sk y) y)) } This proof object has no antecedents. - Z3_OP_PR_MODUS_PONENS_OEQ: Modus ponens style rule for equi-satisfiability. \nicebox{ T1: p T2: (~ p q) [mp~ T1 T2]: q } - Z3_OP_PR_TH_LEMMA: Generic proof for theory lemmas. The theory lemma function comes with one or more parameters. The first parameter indicates the name of the theory. For the theory of arithmetic, additional parameters provide hints for checking the theory lemma. The hints for arithmetic are: - farkas - followed by rational coefficients. Multiply the coefficients to the inequalities in the lemma, add the (negated) inequalities and obtain a contradiction. - triangle-eq - Indicates a lemma related to the equivalence: \nicebox{ (iff (= t1 t2) (and (<= t1 t2) (<= t2 t1))) } - gcd-test - Indicates an integer linear arithmetic lemma that uses a gcd test. - Z3_OP_PR_HYPER_RESOLVE: Hyper-resolution rule. The premises of the rules is a sequence of clauses. The first clause argument is the main clause of the rule. with a literal from the first (main) clause. Premises of the rules are of the form \nicebox{ (or l0 l1 l2 .. ln) } or \nicebox{ (=> (and l1 l2 .. ln) l0) } or in the most general (ground) form: \nicebox{ (=> (and ln+1 ln+2 .. ln+m) (or l0 l1 .. ln)) } In other words we use the following (Prolog style) convention for Horn implications: The head of a Horn implication is position 0, the first conjunct in the body of an implication is position 1 the second conjunct in the body of an implication is position 2 For general implications where the head is a disjunction, the first n positions correspond to the n disjuncts in the head. The next m positions correspond to the m conjuncts in the body. The premises can be universally quantified so that the most general non-ground form is: \nicebox{ (forall (vars) (=> (and ln+1 ln+2 .. ln+m) (or l0 l1 .. ln))) } The hyper-resolution rule takes a sequence of parameters. The parameters are substitutions of bound variables separated by pairs of literal positions from the main clause and side clause. - Z3_OP_RA_STORE: Insert a record into a relation. The function takes \c n+1 arguments, where the first argument is the relation and the remaining \c n elements correspond to the \c n columns of the relation. - Z3_OP_RA_EMPTY: Creates the empty relation. - Z3_OP_RA_IS_EMPTY: Tests if the relation is empty. - Z3_OP_RA_JOIN: Create the relational join. - Z3_OP_RA_UNION: Create the union or convex hull of two relations. The function takes two arguments. - Z3_OP_RA_WIDEN: Widen two relations. The function takes two arguments. - Z3_OP_RA_PROJECT: Project the columns (provided as numbers in the parameters). The function takes one argument. - Z3_OP_RA_FILTER: Filter (restrict) a relation with respect to a predicate. The first argument is a relation. The second argument is a predicate with free de-Bruijn indices corresponding to the columns of the relation. So the first column in the relation has index 0. - Z3_OP_RA_NEGATION_FILTER: Intersect the first relation with respect to negation of the second relation (the function takes two arguments). Logically, the specification can be described by a function target = filter_by_negation(pos, neg, columns) where columns are pairs c1, d1, .., cN, dN of columns from pos and neg, such that target are elements in x in pos, such that there is no y in neg that agrees with x on the columns c1, d1, .., cN, dN. - Z3_OP_RA_RENAME: rename columns in the relation. The function takes one argument. The parameters contain the renaming as a cycle. - Z3_OP_RA_COMPLEMENT: Complement the relation. - Z3_OP_RA_SELECT: Check if a record is an element of the relation. The function takes \c n+1 arguments, where the first argument is a relation, and the remaining \c n arguments correspond to a record. - Z3_OP_RA_CLONE: Create a fresh copy (clone) of a relation. The function is logically the identity, but in the context of a register machine allows for #Z3_OP_RA_UNION to perform destructive updates to the first argument. - Z3_OP_FD_LT: A less than predicate over the finite domain Z3_FINITE_DOMAIN_SORT. - Z3_OP_LABEL: A label (used by the Boogie Verification condition generator). The label has two parameters, a string and a Boolean polarity. It takes one argument, a formula. - Z3_OP_LABEL_LIT: A label literal (used by the Boogie Verification condition generator). A label literal has a set of string parameters. It takes no arguments. - Z3_OP_DT_CONSTRUCTOR: datatype constructor. - Z3_OP_DT_RECOGNISER: datatype recognizer. - Z3_OP_DT_IS: datatype recognizer. - Z3_OP_DT_ACCESSOR: datatype accessor. - Z3_OP_DT_UPDATE_FIELD: datatype field update. - Z3_OP_PB_AT_MOST: Cardinality constraint. E.g., x + y + z <= 2 - Z3_OP_PB_AT_LEAST: Cardinality constraint. E.g., x + y + z >= 2 - Z3_OP_PB_LE: Generalized Pseudo-Boolean cardinality constraint. Example 2*x + 3*y <= 4 - Z3_OP_PB_GE: Generalized Pseudo-Boolean cardinality constraint. Example 2*x + 3*y + 2*z >= 4 - Z3_OP_PB_EQ: Generalized Pseudo-Boolean equality constraint. Example 2*x + 1*y + 2*z + 1*u = 4 - Z3_OP_SPECIAL_RELATION_LO: A relation that is a total linear order - Z3_OP_SPECIAL_RELATION_PO: A relation that is a partial order - Z3_OP_SPECIAL_RELATION_PLO: A relation that is a piecewise linear order - Z3_OP_SPECIAL_RELATION_TO: A relation that is a tree order - Z3_OP_SPECIAL_RELATION_TC: Transitive closure of a relation - Z3_OP_SPECIAL_RELATION_TRC: Transitive reflexive closure of a relation - Z3_OP_FPA_RM_NEAREST_TIES_TO_EVEN: Floating-point rounding mode RNE - Z3_OP_FPA_RM_NEAREST_TIES_TO_AWAY: Floating-point rounding mode RNA - Z3_OP_FPA_RM_TOWARD_POSITIVE: Floating-point rounding mode RTP - Z3_OP_FPA_RM_TOWARD_NEGATIVE: Floating-point rounding mode RTN - Z3_OP_FPA_RM_TOWARD_ZERO: Floating-point rounding mode RTZ - Z3_OP_FPA_NUM: Floating-point value - Z3_OP_FPA_PLUS_INF: Floating-point +oo - Z3_OP_FPA_MINUS_INF: Floating-point -oo - Z3_OP_FPA_NAN: Floating-point NaN - Z3_OP_FPA_PLUS_ZERO: Floating-point +zero - Z3_OP_FPA_MINUS_ZERO: Floating-point -zero - Z3_OP_FPA_ADD: Floating-point addition - Z3_OP_FPA_SUB: Floating-point subtraction - Z3_OP_FPA_NEG: Floating-point negation - Z3_OP_FPA_MUL: Floating-point multiplication - Z3_OP_FPA_DIV: Floating-point division - Z3_OP_FPA_REM: Floating-point remainder - Z3_OP_FPA_ABS: Floating-point absolute value - Z3_OP_FPA_MIN: Floating-point minimum - Z3_OP_FPA_MAX: Floating-point maximum - Z3_OP_FPA_FMA: Floating-point fused multiply-add - Z3_OP_FPA_SQRT: Floating-point square root - Z3_OP_FPA_ROUND_TO_INTEGRAL: Floating-point round to integral - Z3_OP_FPA_EQ: Floating-point equality - Z3_OP_FPA_LT: Floating-point less than - Z3_OP_FPA_GT: Floating-point greater than - Z3_OP_FPA_LE: Floating-point less than or equal - Z3_OP_FPA_GE: Floating-point greater than or equal - Z3_OP_FPA_IS_NAN: Floating-point isNaN - Z3_OP_FPA_IS_INF: Floating-point isInfinite - Z3_OP_FPA_IS_ZERO: Floating-point isZero - Z3_OP_FPA_IS_NORMAL: Floating-point isNormal - Z3_OP_FPA_IS_SUBNORMAL: Floating-point isSubnormal - Z3_OP_FPA_IS_NEGATIVE: Floating-point isNegative - Z3_OP_FPA_IS_POSITIVE: Floating-point isPositive - Z3_OP_FPA_FP: Floating-point constructor from 3 bit-vectors - Z3_OP_FPA_TO_FP: Floating-point conversion (various) - Z3_OP_FPA_TO_FP_UNSIGNED: Floating-point conversion from unsigned bit-vector - Z3_OP_FPA_TO_UBV: Floating-point conversion to unsigned bit-vector - Z3_OP_FPA_TO_SBV: Floating-point conversion to signed bit-vector - Z3_OP_FPA_TO_REAL: Floating-point conversion to real number - Z3_OP_FPA_TO_IEEE_BV: Floating-point conversion to IEEE-754 bit-vector - Z3_OP_FPA_BVWRAP: (Implicitly) represents the internal bitvector- representation of a floating-point term (used for the lazy encoding of non-relevant terms in theory_fpa) - Z3_OP_FPA_BV2RM: Conversion of a 3-bit bit-vector term to a floating-point rounding-mode term The conversion uses the following values: 0 = 000 = Z3_OP_FPA_RM_NEAREST_TIES_TO_EVEN, 1 = 001 = Z3_OP_FPA_RM_NEAREST_TIES_TO_AWAY, 2 = 010 = Z3_OP_FPA_RM_TOWARD_POSITIVE, 3 = 011 = Z3_OP_FPA_RM_TOWARD_NEGATIVE, 4 = 100 = Z3_OP_FPA_RM_TOWARD_ZERO. - Z3_OP_INTERNAL: internal (often interpreted) symbol, but no additional information is exposed. Tools may use the string representation of the function declaration to obtain more information. - Z3_OP_UNINTERPRETED: kind used for uninterpreted symbols. */ typedef enum { // Basic Z3_OP_TRUE = 0x100, Z3_OP_FALSE, Z3_OP_EQ, Z3_OP_DISTINCT, Z3_OP_ITE, Z3_OP_AND, Z3_OP_OR, Z3_OP_IFF, Z3_OP_XOR, Z3_OP_NOT, Z3_OP_IMPLIES, Z3_OP_OEQ, // Arithmetic Z3_OP_ANUM = 0x200, Z3_OP_AGNUM, Z3_OP_LE, Z3_OP_GE, Z3_OP_LT, Z3_OP_GT, Z3_OP_ADD, Z3_OP_SUB, Z3_OP_UMINUS, Z3_OP_MUL, Z3_OP_DIV, Z3_OP_IDIV, Z3_OP_REM, Z3_OP_MOD, Z3_OP_TO_REAL, Z3_OP_TO_INT, Z3_OP_IS_INT, Z3_OP_POWER, // Arrays & Sets Z3_OP_STORE = 0x300, Z3_OP_SELECT, Z3_OP_CONST_ARRAY, Z3_OP_ARRAY_MAP, Z3_OP_ARRAY_DEFAULT, Z3_OP_SET_UNION, Z3_OP_SET_INTERSECT, Z3_OP_SET_DIFFERENCE, Z3_OP_SET_COMPLEMENT, Z3_OP_SET_SUBSET, Z3_OP_AS_ARRAY, Z3_OP_ARRAY_EXT, Z3_OP_SET_HAS_SIZE, Z3_OP_SET_CARD, // Bit-vectors Z3_OP_BNUM = 0x400, Z3_OP_BIT1, Z3_OP_BIT0, Z3_OP_BNEG, Z3_OP_BADD, Z3_OP_BSUB, Z3_OP_BMUL, Z3_OP_BSDIV, Z3_OP_BUDIV, Z3_OP_BSREM, Z3_OP_BUREM, Z3_OP_BSMOD, // special functions to record the division by 0 cases // these are internal functions Z3_OP_BSDIV0, Z3_OP_BUDIV0, Z3_OP_BSREM0, Z3_OP_BUREM0, Z3_OP_BSMOD0, Z3_OP_ULEQ, Z3_OP_SLEQ, Z3_OP_UGEQ, Z3_OP_SGEQ, Z3_OP_ULT, Z3_OP_SLT, Z3_OP_UGT, Z3_OP_SGT, Z3_OP_BAND, Z3_OP_BOR, Z3_OP_BNOT, Z3_OP_BXOR, Z3_OP_BNAND, Z3_OP_BNOR, Z3_OP_BXNOR, Z3_OP_CONCAT, Z3_OP_SIGN_EXT, Z3_OP_ZERO_EXT, Z3_OP_EXTRACT, Z3_OP_REPEAT, Z3_OP_BREDOR, Z3_OP_BREDAND, Z3_OP_BCOMP, Z3_OP_BSHL, Z3_OP_BLSHR, Z3_OP_BASHR, Z3_OP_ROTATE_LEFT, Z3_OP_ROTATE_RIGHT, Z3_OP_EXT_ROTATE_LEFT, Z3_OP_EXT_ROTATE_RIGHT, Z3_OP_BIT2BOOL, Z3_OP_INT2BV, Z3_OP_BV2INT, Z3_OP_CARRY, Z3_OP_XOR3, Z3_OP_BSMUL_NO_OVFL, Z3_OP_BUMUL_NO_OVFL, Z3_OP_BSMUL_NO_UDFL, Z3_OP_BSDIV_I, Z3_OP_BUDIV_I, Z3_OP_BSREM_I, Z3_OP_BUREM_I, Z3_OP_BSMOD_I, // Proofs Z3_OP_PR_UNDEF = 0x500, Z3_OP_PR_TRUE, Z3_OP_PR_ASSERTED, Z3_OP_PR_GOAL, Z3_OP_PR_MODUS_PONENS, Z3_OP_PR_REFLEXIVITY, Z3_OP_PR_SYMMETRY, Z3_OP_PR_TRANSITIVITY, Z3_OP_PR_TRANSITIVITY_STAR, Z3_OP_PR_MONOTONICITY, Z3_OP_PR_QUANT_INTRO, Z3_OP_PR_BIND, Z3_OP_PR_DISTRIBUTIVITY, Z3_OP_PR_AND_ELIM, Z3_OP_PR_NOT_OR_ELIM, Z3_OP_PR_REWRITE, Z3_OP_PR_REWRITE_STAR, Z3_OP_PR_PULL_QUANT, Z3_OP_PR_PUSH_QUANT, Z3_OP_PR_ELIM_UNUSED_VARS, Z3_OP_PR_DER, Z3_OP_PR_QUANT_INST, Z3_OP_PR_HYPOTHESIS, Z3_OP_PR_LEMMA, Z3_OP_PR_UNIT_RESOLUTION, Z3_OP_PR_IFF_TRUE, Z3_OP_PR_IFF_FALSE, Z3_OP_PR_COMMUTATIVITY, Z3_OP_PR_DEF_AXIOM, Z3_OP_PR_ASSUMPTION_ADD, Z3_OP_PR_LEMMA_ADD, Z3_OP_PR_REDUNDANT_DEL, Z3_OP_PR_CLAUSE_TRAIL, Z3_OP_PR_DEF_INTRO, Z3_OP_PR_APPLY_DEF, Z3_OP_PR_IFF_OEQ, Z3_OP_PR_NNF_POS, Z3_OP_PR_NNF_NEG, Z3_OP_PR_SKOLEMIZE, Z3_OP_PR_MODUS_PONENS_OEQ, Z3_OP_PR_TH_LEMMA, Z3_OP_PR_HYPER_RESOLVE, // Relational algebra Z3_OP_RA_STORE = 0x600, Z3_OP_RA_EMPTY, Z3_OP_RA_IS_EMPTY, Z3_OP_RA_JOIN, Z3_OP_RA_UNION, Z3_OP_RA_WIDEN, Z3_OP_RA_PROJECT, Z3_OP_RA_FILTER, Z3_OP_RA_NEGATION_FILTER, Z3_OP_RA_RENAME, Z3_OP_RA_COMPLEMENT, Z3_OP_RA_SELECT, Z3_OP_RA_CLONE, Z3_OP_FD_CONSTANT, Z3_OP_FD_LT, // Sequences Z3_OP_SEQ_UNIT, Z3_OP_SEQ_EMPTY, Z3_OP_SEQ_CONCAT, Z3_OP_SEQ_PREFIX, Z3_OP_SEQ_SUFFIX, Z3_OP_SEQ_CONTAINS, Z3_OP_SEQ_EXTRACT, Z3_OP_SEQ_REPLACE, Z3_OP_SEQ_AT, Z3_OP_SEQ_NTH, Z3_OP_SEQ_LENGTH, Z3_OP_SEQ_INDEX, Z3_OP_SEQ_LAST_INDEX, Z3_OP_SEQ_TO_RE, Z3_OP_SEQ_IN_RE, // strings Z3_OP_STR_TO_INT, Z3_OP_INT_TO_STR, // regular expressions Z3_OP_RE_PLUS, Z3_OP_RE_STAR, Z3_OP_RE_OPTION, Z3_OP_RE_CONCAT, Z3_OP_RE_UNION, Z3_OP_RE_RANGE, Z3_OP_RE_LOOP, Z3_OP_RE_INTERSECT, Z3_OP_RE_EMPTY_SET, Z3_OP_RE_FULL_SET, Z3_OP_RE_COMPLEMENT, // Auxiliary Z3_OP_LABEL = 0x700, Z3_OP_LABEL_LIT, // Datatypes Z3_OP_DT_CONSTRUCTOR=0x800, Z3_OP_DT_RECOGNISER, Z3_OP_DT_IS, Z3_OP_DT_ACCESSOR, Z3_OP_DT_UPDATE_FIELD, // Pseudo Booleans Z3_OP_PB_AT_MOST=0x900, Z3_OP_PB_AT_LEAST, Z3_OP_PB_LE, Z3_OP_PB_GE, Z3_OP_PB_EQ, // Special relations Z3_OP_SPECIAL_RELATION_LO = 0xa000, Z3_OP_SPECIAL_RELATION_PO, Z3_OP_SPECIAL_RELATION_PLO, Z3_OP_SPECIAL_RELATION_TO, Z3_OP_SPECIAL_RELATION_TC, Z3_OP_SPECIAL_RELATION_TRC, // Floating-Point Arithmetic Z3_OP_FPA_RM_NEAREST_TIES_TO_EVEN = 0xb000, Z3_OP_FPA_RM_NEAREST_TIES_TO_AWAY, Z3_OP_FPA_RM_TOWARD_POSITIVE, Z3_OP_FPA_RM_TOWARD_NEGATIVE, Z3_OP_FPA_RM_TOWARD_ZERO, Z3_OP_FPA_NUM, Z3_OP_FPA_PLUS_INF, Z3_OP_FPA_MINUS_INF, Z3_OP_FPA_NAN, Z3_OP_FPA_PLUS_ZERO, Z3_OP_FPA_MINUS_ZERO, Z3_OP_FPA_ADD, Z3_OP_FPA_SUB, Z3_OP_FPA_NEG, Z3_OP_FPA_MUL, Z3_OP_FPA_DIV, Z3_OP_FPA_REM, Z3_OP_FPA_ABS, Z3_OP_FPA_MIN, Z3_OP_FPA_MAX, Z3_OP_FPA_FMA, Z3_OP_FPA_SQRT, Z3_OP_FPA_ROUND_TO_INTEGRAL, Z3_OP_FPA_EQ, Z3_OP_FPA_LT, Z3_OP_FPA_GT, Z3_OP_FPA_LE, Z3_OP_FPA_GE, Z3_OP_FPA_IS_NAN, Z3_OP_FPA_IS_INF, Z3_OP_FPA_IS_ZERO, Z3_OP_FPA_IS_NORMAL, Z3_OP_FPA_IS_SUBNORMAL, Z3_OP_FPA_IS_NEGATIVE, Z3_OP_FPA_IS_POSITIVE, Z3_OP_FPA_FP, Z3_OP_FPA_TO_FP, Z3_OP_FPA_TO_FP_UNSIGNED, Z3_OP_FPA_TO_UBV, Z3_OP_FPA_TO_SBV, Z3_OP_FPA_TO_REAL, Z3_OP_FPA_TO_IEEE_BV, Z3_OP_FPA_BVWRAP, Z3_OP_FPA_BV2RM, Z3_OP_INTERNAL, Z3_OP_UNINTERPRETED } Z3_decl_kind; /** \brief The different kinds of parameters that can be associated with parameter sets. (see #Z3_mk_params). - Z3_PK_UINT integer parameters. - Z3_PK_BOOL boolean parameters. - Z3_PK_DOUBLE double parameters. - Z3_PK_SYMBOL symbol parameters. - Z3_PK_STRING string parameters. - Z3_PK_OTHER all internal parameter kinds which are not exposed in the API. - Z3_PK_INVALID invalid parameter. */ typedef enum { Z3_PK_UINT, Z3_PK_BOOL, Z3_PK_DOUBLE, Z3_PK_SYMBOL, Z3_PK_STRING, Z3_PK_OTHER, Z3_PK_INVALID } Z3_param_kind; /** \brief Z3 pretty printing modes (See #Z3_set_ast_print_mode). - Z3_PRINT_SMTLIB_FULL: Print AST nodes in SMTLIB verbose format. - Z3_PRINT_LOW_LEVEL: Print AST nodes using a low-level format. - Z3_PRINT_SMTLIB2_COMPLIANT: Print AST nodes in SMTLIB 2.x compliant format. */ typedef enum { Z3_PRINT_SMTLIB_FULL, Z3_PRINT_LOW_LEVEL, Z3_PRINT_SMTLIB2_COMPLIANT } Z3_ast_print_mode; /** \brief Z3 error codes (See #Z3_get_error_code). - Z3_OK: No error. - Z3_SORT_ERROR: User tried to build an invalid (type incorrect) AST. - Z3_IOB: Index out of bounds. - Z3_INVALID_ARG: Invalid argument was provided. - Z3_PARSER_ERROR: An error occurred when parsing a string or file. - Z3_NO_PARSER: Parser output is not available, that is, user didn't invoke #Z3_parse_smtlib2_string or #Z3_parse_smtlib2_file. - Z3_INVALID_PATTERN: Invalid pattern was used to build a quantifier. - Z3_MEMOUT_FAIL: A memory allocation failure was encountered. - Z3_FILE_ACCESS_ERRROR: A file could not be accessed. - Z3_INVALID_USAGE: API call is invalid in the current state. - Z3_INTERNAL_FATAL: An error internal to Z3 occurred. - Z3_DEC_REF_ERROR: Trying to decrement the reference counter of an AST that was deleted or the reference counter was not initialized with #Z3_inc_ref. - Z3_EXCEPTION: Internal Z3 exception. Additional details can be retrieved using #Z3_get_error_msg. */ typedef enum { Z3_OK, Z3_SORT_ERROR, Z3_IOB, Z3_INVALID_ARG, Z3_PARSER_ERROR, Z3_NO_PARSER, Z3_INVALID_PATTERN, Z3_MEMOUT_FAIL, Z3_FILE_ACCESS_ERROR, Z3_INTERNAL_FATAL, Z3_INVALID_USAGE, Z3_DEC_REF_ERROR, Z3_EXCEPTION } Z3_error_code; /** Definitions for update_api.py def_Type('CONFIG', 'Z3_config', 'Config') def_Type('CONTEXT', 'Z3_context', 'ContextObj') def_Type('AST', 'Z3_ast', 'Ast') def_Type('APP', 'Z3_app', 'Ast') def_Type('SORT', 'Z3_sort', 'Sort') def_Type('FUNC_DECL', 'Z3_func_decl', 'FuncDecl') def_Type('PATTERN', 'Z3_pattern', 'Pattern') def_Type('MODEL', 'Z3_model', 'Model') def_Type('LITERALS', 'Z3_literals', 'Literals') def_Type('CONSTRUCTOR', 'Z3_constructor', 'Constructor') def_Type('CONSTRUCTOR_LIST', 'Z3_constructor_list', 'ConstructorList') def_Type('SOLVER', 'Z3_solver', 'SolverObj') def_Type('GOAL', 'Z3_goal', 'GoalObj') def_Type('TACTIC', 'Z3_tactic', 'TacticObj') def_Type('PARAMS', 'Z3_params', 'Params') def_Type('PROBE', 'Z3_probe', 'ProbeObj') def_Type('STATS', 'Z3_stats', 'StatsObj') def_Type('AST_VECTOR', 'Z3_ast_vector', 'AstVectorObj') def_Type('AST_MAP', 'Z3_ast_map', 'AstMapObj') def_Type('APPLY_RESULT', 'Z3_apply_result', 'ApplyResultObj') def_Type('FUNC_INTERP', 'Z3_func_interp', 'FuncInterpObj') def_Type('FUNC_ENTRY', 'Z3_func_entry', 'FuncEntryObj') def_Type('FIXEDPOINT', 'Z3_fixedpoint', 'FixedpointObj') def_Type('OPTIMIZE', 'Z3_optimize', 'OptimizeObj') def_Type('PARAM_DESCRS', 'Z3_param_descrs', 'ParamDescrs') def_Type('RCF_NUM', 'Z3_rcf_num', 'RCFNumObj') */ /** \brief Z3 custom error handler (See #Z3_set_error_handler). */ typedef void Z3_error_handler(Z3_context c, Z3_error_code e); /** \brief A Goal is essentially a set of formulas. Z3 provide APIs for building strategies/tactics for solving and transforming Goals. Some of these transformations apply under/over approximations. - Z3_GOAL_PRECISE: Approximations/Relaxations were not applied on the goal (sat and unsat answers were preserved). - Z3_GOAL_UNDER: Goal is the product of a under-approximation (sat answers are preserved). - Z3_GOAL_OVER: Goal is the product of an over-approximation (unsat answers are preserved). - Z3_GOAL_UNDER_OVER: Goal is garbage (it is the product of over- and under-approximations, sat and unsat answers are not preserved). */ typedef enum { Z3_GOAL_PRECISE, Z3_GOAL_UNDER, Z3_GOAL_OVER, Z3_GOAL_UNDER_OVER } Z3_goal_prec; /*@}*/ #ifdef __cplusplus extern "C" { #endif // __cplusplus /** @name Global Parameters */ /*@{*/ /** \brief Set a global (or module) parameter. This setting is shared by all Z3 contexts. When a Z3 module is initialized it will use the value of these parameters when Z3_params objects are not provided. The name of parameter can be composed of characters [a-z][A-Z], digits [0-9], '-' and '_'. The character '.' is a delimiter (more later). The parameter names are case-insensitive. The character '-' should be viewed as an "alias" for '_'. Thus, the following parameter names are considered equivalent: "pp.decimal-precision" and "PP.DECIMAL_PRECISION". This function can be used to set parameters for a specific Z3 module. This can be done by using .. For example: Z3_global_param_set('pp.decimal', 'true') will set the parameter "decimal" in the module "pp" to true. \sa Z3_global_param_get \sa Z3_global_param_reset_all def_API('Z3_global_param_set', VOID, (_in(STRING), _in(STRING))) */ void Z3_API Z3_global_param_set(Z3_string param_id, Z3_string param_value); /** \brief Restore the value of all global (and module) parameters. This command will not affect already created objects (such as tactics and solvers). \sa Z3_global_param_get \sa Z3_global_param_set def_API('Z3_global_param_reset_all', VOID, ()) */ void Z3_API Z3_global_param_reset_all(void); /** \brief Get a global (or module) parameter. Returns \c false if the parameter value does not exist. \sa Z3_global_param_reset_all \sa Z3_global_param_set \remark This function cannot be invoked simultaneously from different threads without synchronization. The result string stored in param_value is stored in shared location. def_API('Z3_global_param_get', BOOL, (_in(STRING), _out(STRING))) */ Z3_bool_opt Z3_API Z3_global_param_get(Z3_string param_id, Z3_string_ptr param_value); /*@}*/ /** @name Create configuration */ /*@{*/ /** \brief Create a configuration object for the Z3 context object. Configurations are created in order to assign parameters prior to creating contexts for Z3 interaction. For example, if the users wishes to use proof generation, then call: \ccode{Z3_set_param_value(cfg\, "proof"\, "true")} \remark In previous versions of Z3, the \c Z3_config was used to store global and module configurations. Now, we should use \c Z3_global_param_set. The following parameters can be set: - proof (Boolean) Enable proof generation - debug_ref_count (Boolean) Enable debug support for Z3_ast reference counting - trace (Boolean) Tracing support for VCC - trace_file_name (String) Trace out file for VCC traces - timeout (unsigned) default timeout (in milliseconds) used for solvers - well_sorted_check type checker - auto_config use heuristics to automatically select solver and configure it - model model generation for solvers, this parameter can be overwritten when creating a solver - model_validate validate models produced by solvers - unsat_core unsat-core generation for solvers, this parameter can be overwritten when creating a solver \sa Z3_set_param_value \sa Z3_del_config def_API('Z3_mk_config', CONFIG, ()) */ Z3_config Z3_API Z3_mk_config(void); /** \brief Delete the given configuration object. \sa Z3_mk_config def_API('Z3_del_config', VOID, (_in(CONFIG),)) */ void Z3_API Z3_del_config(Z3_config c); /** \brief Set a configuration parameter. The following parameters can be set for \sa Z3_mk_config def_API('Z3_set_param_value', VOID, (_in(CONFIG), _in(STRING), _in(STRING))) */ void Z3_API Z3_set_param_value(Z3_config c, Z3_string param_id, Z3_string param_value); /*@}*/ /** @name Context and AST Reference Counting */ /*@{*/ /** \brief Create a context using the given configuration. After a context is created, the configuration cannot be changed, 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 \c Z3_ast objects are determined by the scope level of #Z3_solver_push and #Z3_solver_pop. 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 \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: - \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 def_API('Z3_mk_context', CONTEXT, (_in(CONFIG),)) */ Z3_context Z3_API Z3_mk_context(Z3_config 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 \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 \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: - \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 \c Z3_ast is created twice, Z3 will return the same pointer twice. def_API('Z3_mk_context_rc', CONTEXT, (_in(CONFIG),)) */ Z3_context Z3_API Z3_mk_context_rc(Z3_config c); /** \brief Delete the given logical context. \sa Z3_mk_context def_API('Z3_del_context', VOID, (_in(CONTEXT),)) */ void Z3_API Z3_del_context(Z3_context c); /** \brief Increment the reference counter of the given AST. The context \c c should have been created using #Z3_mk_context_rc. This function is a NOOP if \c c was created using #Z3_mk_context. def_API('Z3_inc_ref', VOID, (_in(CONTEXT), _in(AST))) */ void Z3_API Z3_inc_ref(Z3_context c, Z3_ast a); /** \brief Decrement the reference counter of the given AST. The context \c c should have been created using #Z3_mk_context_rc. This function is a NOOP if \c c was created using #Z3_mk_context. def_API('Z3_dec_ref', VOID, (_in(CONTEXT), _in(AST))) */ void Z3_API Z3_dec_ref(Z3_context c, Z3_ast a); /** \brief Set a value of a context parameter. \sa Z3_global_param_set def_API('Z3_update_param_value', VOID, (_in(CONTEXT), _in(STRING), _in(STRING))) */ void Z3_API Z3_update_param_value(Z3_context c, Z3_string param_id, Z3_string param_value); /** \brief Interrupt the execution of a Z3 procedure. This procedure can be used to interrupt: solvers, simplifiers and tactics. def_API('Z3_interrupt', VOID, (_in(CONTEXT),)) */ void Z3_API Z3_interrupt(Z3_context c); /*@}*/ /** @name Parameters */ /*@{*/ /** \brief Create a Z3 (empty) parameter set. 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 \c Z3_context was created using #Z3_mk_context instead of #Z3_mk_context_rc. def_API('Z3_mk_params', PARAMS, (_in(CONTEXT),)) */ Z3_params Z3_API Z3_mk_params(Z3_context c); /** \brief Increment the reference counter of the given parameter set. def_API('Z3_params_inc_ref', VOID, (_in(CONTEXT), _in(PARAMS))) */ void Z3_API Z3_params_inc_ref(Z3_context c, Z3_params p); /** \brief Decrement the reference counter of the given parameter set. def_API('Z3_params_dec_ref', VOID, (_in(CONTEXT), _in(PARAMS))) */ void Z3_API Z3_params_dec_ref(Z3_context c, Z3_params p); /** \brief Add a Boolean parameter \c k with value \c v to the parameter set \c p. def_API('Z3_params_set_bool', VOID, (_in(CONTEXT), _in(PARAMS), _in(SYMBOL), _in(BOOL))) */ void Z3_API Z3_params_set_bool(Z3_context c, Z3_params p, Z3_symbol k, bool v); /** \brief Add a unsigned parameter \c k with value \c v to the parameter set \c p. def_API('Z3_params_set_uint', VOID, (_in(CONTEXT), _in(PARAMS), _in(SYMBOL), _in(UINT))) */ void Z3_API Z3_params_set_uint(Z3_context c, Z3_params p, Z3_symbol k, unsigned v); /** \brief Add a double parameter \c k with value \c v to the parameter set \c p. def_API('Z3_params_set_double', VOID, (_in(CONTEXT), _in(PARAMS), _in(SYMBOL), _in(DOUBLE))) */ void Z3_API Z3_params_set_double(Z3_context c, Z3_params p, Z3_symbol k, double v); /** \brief Add a symbol parameter \c k with value \c v to the parameter set \c p. def_API('Z3_params_set_symbol', VOID, (_in(CONTEXT), _in(PARAMS), _in(SYMBOL), _in(SYMBOL))) */ void Z3_API Z3_params_set_symbol(Z3_context c, Z3_params p, Z3_symbol k, Z3_symbol v); /** \brief Convert a parameter set into a string. This function is mainly used for printing the contents of a parameter set. def_API('Z3_params_to_string', STRING, (_in(CONTEXT), _in(PARAMS))) */ Z3_string Z3_API Z3_params_to_string(Z3_context c, Z3_params p); /** \brief Validate the parameter set \c p against the parameter description set \c d. The procedure invokes the error handler if \c p is invalid. def_API('Z3_params_validate', VOID, (_in(CONTEXT), _in(PARAMS), _in(PARAM_DESCRS))) */ void Z3_API Z3_params_validate(Z3_context c, Z3_params p, Z3_param_descrs d); /*@}*/ /** @name Parameter Descriptions */ /*@{*/ /** \brief Increment the reference counter of the given parameter description set. def_API('Z3_param_descrs_inc_ref', VOID, (_in(CONTEXT), _in(PARAM_DESCRS))) */ void Z3_API Z3_param_descrs_inc_ref(Z3_context c, Z3_param_descrs p); /** \brief Decrement the reference counter of the given parameter description set. def_API('Z3_param_descrs_dec_ref', VOID, (_in(CONTEXT), _in(PARAM_DESCRS))) */ void Z3_API Z3_param_descrs_dec_ref(Z3_context c, Z3_param_descrs p); /** \brief Return the kind associated with the given parameter name \c n. def_API('Z3_param_descrs_get_kind', UINT, (_in(CONTEXT), _in(PARAM_DESCRS), _in(SYMBOL))) */ Z3_param_kind Z3_API Z3_param_descrs_get_kind(Z3_context c, Z3_param_descrs p, Z3_symbol n); /** \brief Return the number of parameters in the given parameter description set. def_API('Z3_param_descrs_size', UINT, (_in(CONTEXT), _in(PARAM_DESCRS))) */ unsigned Z3_API Z3_param_descrs_size(Z3_context c, Z3_param_descrs p); /** \brief Return the name of the parameter at given index \c i. \pre i < Z3_param_descrs_size(c, p) def_API('Z3_param_descrs_get_name', SYMBOL, (_in(CONTEXT), _in(PARAM_DESCRS), _in(UINT))) */ Z3_symbol Z3_API Z3_param_descrs_get_name(Z3_context c, Z3_param_descrs p, unsigned i); /** \brief Retrieve documentation string corresponding to parameter name \c s. def_API('Z3_param_descrs_get_documentation', STRING, (_in(CONTEXT), _in(PARAM_DESCRS), _in(SYMBOL))) */ Z3_string Z3_API Z3_param_descrs_get_documentation(Z3_context c, Z3_param_descrs p, Z3_symbol s); /** \brief Convert a parameter description set into a string. This function is mainly used for printing the contents of a parameter description set. def_API('Z3_param_descrs_to_string', STRING, (_in(CONTEXT), _in(PARAM_DESCRS))) */ Z3_string Z3_API Z3_param_descrs_to_string(Z3_context c, Z3_param_descrs p); /*@}*/ /** @name Symbols */ /*@{*/ /** \brief Create a Z3 symbol using an integer. Symbols are used to name several term and type constructors. NB. Not all integers can be passed to this function. The legal range of unsigned integers is 0 to 2^30-1. \sa Z3_get_symbol_int \sa Z3_mk_string_symbol def_API('Z3_mk_int_symbol', SYMBOL, (_in(CONTEXT), _in(INT))) */ Z3_symbol Z3_API Z3_mk_int_symbol(Z3_context c, int i); /** \brief Create a Z3 symbol using a C string. Symbols are used to name several term and type constructors. \sa Z3_get_symbol_string \sa Z3_mk_int_symbol def_API('Z3_mk_string_symbol', SYMBOL, (_in(CONTEXT), _in(STRING))) */ Z3_symbol Z3_API Z3_mk_string_symbol(Z3_context c, Z3_string s); /*@}*/ /** @name Sorts */ /*@{*/ /** \brief Create a free (uninterpreted) type using the given name (symbol). Two free types are considered the same iff the have the same name. def_API('Z3_mk_uninterpreted_sort', SORT, (_in(CONTEXT), _in(SYMBOL))) */ Z3_sort Z3_API Z3_mk_uninterpreted_sort(Z3_context c, Z3_symbol s); /** \brief Create the Boolean type. This type is used to create propositional variables and predicates. def_API('Z3_mk_bool_sort', SORT, (_in(CONTEXT), )) */ Z3_sort Z3_API Z3_mk_bool_sort(Z3_context c); /** \brief Create the integer type. This type is not the int type found in programming languages. A machine integer can be represented using bit-vectors. The function #Z3_mk_bv_sort creates a bit-vector type. \sa Z3_mk_bv_sort def_API('Z3_mk_int_sort', SORT, (_in(CONTEXT), )) */ Z3_sort Z3_API Z3_mk_int_sort(Z3_context c); /** \brief Create the real type. Note that this type is not a floating point number. def_API('Z3_mk_real_sort', SORT, (_in(CONTEXT), )) */ Z3_sort Z3_API Z3_mk_real_sort(Z3_context c); /** \brief Create a bit-vector type of the given size. This type can also be seen as a machine integer. \remark The size of the bit-vector type must be greater than zero. def_API('Z3_mk_bv_sort', SORT, (_in(CONTEXT), _in(UINT))) */ Z3_sort Z3_API Z3_mk_bv_sort(Z3_context c, unsigned sz); /** \brief Create a named finite domain sort. To create constants that belong to the finite domain, use the APIs for creating numerals and pass a numeric constant together with the sort returned by this call. The numeric constant should be between 0 and the less than the size of the domain. \sa Z3_get_finite_domain_sort_size def_API('Z3_mk_finite_domain_sort', SORT, (_in(CONTEXT), _in(SYMBOL), _in(UINT64))) */ Z3_sort Z3_API Z3_mk_finite_domain_sort(Z3_context c, Z3_symbol name, uint64_t size); /** \brief Create an array type. We usually represent the array type as: \ccode{[domain -> range]}. Arrays are usually used to model the heap/memory in software verification. \sa Z3_mk_select \sa Z3_mk_store def_API('Z3_mk_array_sort', SORT, (_in(CONTEXT), _in(SORT), _in(SORT))) */ Z3_sort Z3_API Z3_mk_array_sort(Z3_context c, Z3_sort domain, Z3_sort range); /** \brief Create an array type with N arguments \sa Z3_mk_select_n \sa Z3_mk_store_n def_API('Z3_mk_array_sort_n', SORT, (_in(CONTEXT), _in(UINT), _in_array(1, SORT), _in(SORT))) */ Z3_sort Z3_API Z3_mk_array_sort_n(Z3_context c, unsigned n, Z3_sort const * domain, Z3_sort range); /** \brief Create a tuple type. A tuple with \c n fields has a constructor and \c n projections. This function will also declare the constructor and projection functions. \param c logical context \param mk_tuple_name name of the constructor function associated with the tuple type. \param num_fields number of fields in the tuple type. \param field_names name of the projection functions. \param field_sorts type of the tuple fields. \param mk_tuple_decl output parameter that will contain the constructor declaration. \param proj_decl output parameter that will contain the projection function declarations. This field must be a buffer of size \c num_fields allocated by the user. def_API('Z3_mk_tuple_sort', SORT, (_in(CONTEXT), _in(SYMBOL), _in(UINT), _in_array(2, SYMBOL), _in_array(2, SORT), _out(FUNC_DECL), _out_array(2, FUNC_DECL))) */ Z3_sort Z3_API Z3_mk_tuple_sort(Z3_context c, Z3_symbol mk_tuple_name, unsigned num_fields, Z3_symbol const field_names[], Z3_sort const field_sorts[], Z3_func_decl * mk_tuple_decl, Z3_func_decl proj_decl[]); /** \brief Create a enumeration sort. An enumeration sort with \c n elements. This function will also declare the functions corresponding to the enumerations. \param c logical context \param name name of the enumeration sort. \param n number of elements in enumeration sort. \param enum_names names of the enumerated elements. \param enum_consts constants corresponding to the enumerated elements. \param enum_testers predicates testing if terms of the enumeration sort correspond to an enumeration. For example, if this function is called with three symbols A, B, C and the name S, then \c s is a sort whose name is S, and the function returns three terms corresponding to A, B, C in \c enum_consts. The array \c enum_testers has three predicates of type \ccode{(s -> Bool)}. The first predicate (corresponding to A) is true when applied to A, and false otherwise. Similarly for the other predicates. def_API('Z3_mk_enumeration_sort', SORT, (_in(CONTEXT), _in(SYMBOL), _in(UINT), _in_array(2, SYMBOL), _out_array(2, FUNC_DECL), _out_array(2, FUNC_DECL))) */ Z3_sort Z3_API Z3_mk_enumeration_sort(Z3_context c, Z3_symbol name, unsigned n, Z3_symbol const enum_names[], Z3_func_decl enum_consts[], Z3_func_decl enum_testers[]); /** \brief Create a list sort A list sort over \c elem_sort This function declares the corresponding constructors and testers for lists. \param c logical context \param name name of the list sort. \param elem_sort sort of list elements. \param nil_decl declaration for the empty list. \param is_nil_decl test for the empty list. \param cons_decl declaration for a cons cell. \param is_cons_decl cons cell test. \param head_decl list head. \param tail_decl list tail. def_API('Z3_mk_list_sort', SORT, (_in(CONTEXT), _in(SYMBOL), _in(SORT), _out(FUNC_DECL), _out(FUNC_DECL), _out(FUNC_DECL), _out(FUNC_DECL), _out(FUNC_DECL), _out(FUNC_DECL))) */ Z3_sort Z3_API Z3_mk_list_sort(Z3_context c, Z3_symbol name, Z3_sort elem_sort, Z3_func_decl* nil_decl, Z3_func_decl* is_nil_decl, Z3_func_decl* cons_decl, Z3_func_decl* is_cons_decl, Z3_func_decl* head_decl, Z3_func_decl* tail_decl ); /** \brief Create a constructor. \param c logical context. \param name constructor name. \param recognizer name of recognizer function. \param num_fields number of fields in constructor. \param field_names names of the constructor fields. \param sorts field sorts, 0 if the field sort refers to a recursive sort. \param sort_refs reference to datatype sort that is an argument to the constructor; if the corresponding sort reference is 0, then the value in sort_refs should be an index referring to one of the recursive datatypes that is declared. \sa Z3_del_constructor \sa Z3_mk_constructor_list \sa Z3_query_constructor def_API('Z3_mk_constructor', CONSTRUCTOR, (_in(CONTEXT), _in(SYMBOL), _in(SYMBOL), _in(UINT), _in_array(3, SYMBOL), _in_array(3, SORT), _in_array(3, UINT))) */ Z3_constructor Z3_API Z3_mk_constructor(Z3_context c, Z3_symbol name, Z3_symbol recognizer, unsigned num_fields, Z3_symbol const field_names[], Z3_sort_opt const sorts[], unsigned sort_refs[] ); /** \brief Reclaim memory allocated to constructor. \param c logical context. \param constr constructor. \sa Z3_mk_constructor def_API('Z3_del_constructor', VOID, (_in(CONTEXT), _in(CONSTRUCTOR))) */ void Z3_API Z3_del_constructor(Z3_context c, Z3_constructor constr); /** \brief Create datatype, such as lists, trees, records, enumerations or unions of records. The datatype may be recursive. Return the datatype sort. \param c logical context. \param name name of datatype. \param num_constructors number of constructors passed in. \param constructors array of constructor containers. \sa Z3_mk_constructor \sa Z3_mk_constructor_list \sa Z3_mk_datatypes def_API('Z3_mk_datatype', SORT, (_in(CONTEXT), _in(SYMBOL), _in(UINT), _inout_array(2, CONSTRUCTOR))) */ Z3_sort Z3_API Z3_mk_datatype(Z3_context c, Z3_symbol name, unsigned num_constructors, Z3_constructor constructors[]); /** \brief Create list of constructors. \param c logical context. \param num_constructors number of constructors in list. \param constructors list of constructors. \sa Z3_del_constructor_list \sa Z3_mk_constructor def_API('Z3_mk_constructor_list', CONSTRUCTOR_LIST, (_in(CONTEXT), _in(UINT), _in_array(1, CONSTRUCTOR))) */ Z3_constructor_list Z3_API Z3_mk_constructor_list(Z3_context c, unsigned num_constructors, Z3_constructor const constructors[]); /** \brief Reclaim memory allocated for constructor list. Each constructor inside the constructor list must be independently reclaimed using #Z3_del_constructor. \param c logical context. \param clist constructor list container. \sa Z3_mk_constructor_list def_API('Z3_del_constructor_list', VOID, (_in(CONTEXT), _in(CONSTRUCTOR_LIST))) */ void Z3_API Z3_del_constructor_list(Z3_context c, Z3_constructor_list clist); /** \brief Create mutually recursive datatypes. \param c logical context. \param num_sorts number of datatype sorts. \param sort_names names of datatype sorts. \param sorts array of datatype sorts. \param constructor_lists list of constructors, one list per sort. \sa Z3_mk_constructor \sa Z3_mk_constructor_list \sa Z3_mk_datatype def_API('Z3_mk_datatypes', VOID, (_in(CONTEXT), _in(UINT), _in_array(1, SYMBOL), _out_array(1, SORT), _inout_array(1, CONSTRUCTOR_LIST))) */ void Z3_API Z3_mk_datatypes(Z3_context c, unsigned num_sorts, Z3_symbol const sort_names[], Z3_sort sorts[], Z3_constructor_list constructor_lists[]); /** \brief Query constructor for declared functions. \param c logical context. \param constr constructor container. The container must have been passed in to a #Z3_mk_datatype call. \param num_fields number of accessor fields in the constructor. \param constructor constructor function declaration, allocated by user. \param tester constructor test function declaration, allocated by user. \param accessors array of accessor function declarations allocated by user. The array must contain num_fields elements. \sa Z3_mk_constructor def_API('Z3_query_constructor', VOID, (_in(CONTEXT), _in(CONSTRUCTOR), _in(UINT), _out(FUNC_DECL), _out(FUNC_DECL), _out_array(2, FUNC_DECL))) */ void Z3_API Z3_query_constructor(Z3_context c, Z3_constructor constr, unsigned num_fields, Z3_func_decl* constructor, Z3_func_decl* tester, Z3_func_decl accessors[]); /*@}*/ /** @name Constants and Applications */ /*@{*/ /** \brief Declare a constant or function. \param c logical context. \param s name of the constant or function. \param domain_size number of arguments. It is 0 when declaring a constant. \param domain array containing the sort of each argument. The array must contain domain_size elements. It is 0 when declaring a constant. \param range sort of the constant or the return sort of the function. After declaring a constant or function, the function #Z3_mk_app can be used to create a constant or function application. \sa Z3_mk_app \sa Z3_mk_fresh_func_decl \sa Z3_mk_rec_func_decl def_API('Z3_mk_func_decl', FUNC_DECL, (_in(CONTEXT), _in(SYMBOL), _in(UINT), _in_array(2, SORT), _in(SORT))) */ Z3_func_decl Z3_API Z3_mk_func_decl(Z3_context c, Z3_symbol s, unsigned domain_size, Z3_sort const domain[], Z3_sort range); /** \brief Create a constant or function application. \sa Z3_mk_fresh_func_decl \sa Z3_mk_func_decl \sa Z3_mk_rec_func_decl def_API('Z3_mk_app', AST, (_in(CONTEXT), _in(FUNC_DECL), _in(UINT), _in_array(2, AST))) */ Z3_ast Z3_API Z3_mk_app( Z3_context c, Z3_func_decl d, unsigned num_args, Z3_ast const args[]); /** \brief Declare and create a constant. This function is a shorthand for: \code Z3_func_decl d = Z3_mk_func_decl(c, s, 0, 0, ty); Z3_ast n = Z3_mk_app(c, d, 0, 0); \endcode \sa Z3_mk_app \sa Z3_mk_fresh_const \sa Z3_mk_func_decl def_API('Z3_mk_const', AST, (_in(CONTEXT), _in(SYMBOL), _in(SORT))) */ Z3_ast Z3_API Z3_mk_const(Z3_context c, Z3_symbol s, Z3_sort ty); /** \brief Declare a fresh constant or function. Z3 will generate an unique name for this function declaration. If prefix is different from \c NULL, then the name generate by Z3 will start with \c prefix. \remark If \c prefix is \c NULL, then it is assumed to be the empty string. \sa Z3_mk_func_decl def_API('Z3_mk_fresh_func_decl', FUNC_DECL, (_in(CONTEXT), _in(STRING), _in(UINT), _in_array(2, SORT), _in(SORT))) */ Z3_func_decl Z3_API Z3_mk_fresh_func_decl(Z3_context c, Z3_string prefix, unsigned domain_size, Z3_sort const domain[], Z3_sort range); /** \brief Declare and create a fresh constant. This function is a shorthand for: \code Z3_func_decl d = Z3_mk_fresh_func_decl(c, prefix, 0, 0, ty); Z3_ast n = Z3_mk_app(c, d, 0, 0); \endcode \remark If \c prefix is \c NULL, then it is assumed to be the empty string. \sa Z3_mk_app \sa Z3_mk_const \sa Z3_mk_fresh_func_decl \sa Z3_mk_func_decl def_API('Z3_mk_fresh_const', AST, (_in(CONTEXT), _in(STRING), _in(SORT))) */ Z3_ast Z3_API Z3_mk_fresh_const(Z3_context c, Z3_string prefix, Z3_sort ty); /** \brief Declare a recursive function \param c logical context. \param s name of the function. \param domain_size number of arguments. It should be greater than 0. \param domain array containing the sort of each argument. The array must contain domain_size elements. \param range sort of the constant or the return sort of the function. After declaring recursive function, it should be associated with a recursive definition #Z3_add_rec_def. The function #Z3_mk_app can be used to create a constant or function application. \sa Z3_add_rec_def \sa Z3_mk_app \sa Z3_mk_func_decl def_API('Z3_mk_rec_func_decl', FUNC_DECL, (_in(CONTEXT), _in(SYMBOL), _in(UINT), _in_array(2, SORT), _in(SORT))) */ Z3_func_decl Z3_API Z3_mk_rec_func_decl(Z3_context c, Z3_symbol s, unsigned domain_size, Z3_sort const domain[], Z3_sort range); /** \brief Define the body of a recursive function. \param c logical context. \param f function declaration. \param n number of arguments to the function \param args constants that are used as arguments to the recursive function in the definition. \param body body of the recursive function After declaring a recursive function or a collection of mutually recursive functions, use this function to provide the definition for the recursive function. \sa Z3_mk_rec_func_decl def_API('Z3_add_rec_def', VOID, (_in(CONTEXT), _in(FUNC_DECL), _in(UINT), _in_array(2, AST), _in(AST))) */ void Z3_API Z3_add_rec_def(Z3_context c, Z3_func_decl f, unsigned n, Z3_ast args[], Z3_ast body); /*@}*/ /** @name Propositional Logic and Equality */ /*@{*/ /** \brief Create an AST node representing \c true. def_API('Z3_mk_true', AST, (_in(CONTEXT), )) */ Z3_ast Z3_API Z3_mk_true(Z3_context c); /** \brief Create an AST node representing \c false. def_API('Z3_mk_false', AST, (_in(CONTEXT), )) */ Z3_ast Z3_API Z3_mk_false(Z3_context c); /** \brief Create an AST node representing \ccode{l = r}. The nodes \c l and \c r must have the same type. def_API('Z3_mk_eq', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_eq(Z3_context c, Z3_ast l, Z3_ast r); /** \brief Create an AST node representing \ccode{distinct(args[0], ..., args[num_args-1])}. The \c distinct construct is used for declaring the arguments pairwise distinct. That is, \ccode{Forall 0 <= i < j < num_args. not args[i] = args[j]}. All arguments must have the same sort. \remark The number of arguments of a distinct construct must be greater than one. def_API('Z3_mk_distinct', AST, (_in(CONTEXT), _in(UINT), _in_array(1, AST))) */ Z3_ast Z3_API Z3_mk_distinct(Z3_context c, unsigned num_args, Z3_ast const args[]); /** \brief Create an AST node representing \ccode{not(a)}. The node \c a must have Boolean sort. def_API('Z3_mk_not', AST, (_in(CONTEXT), _in(AST))) */ Z3_ast Z3_API Z3_mk_not(Z3_context c, Z3_ast a); /** \brief Create an AST node representing an if-then-else: \ccode{ite(t1, t2, t3)}. The node \c t1 must have Boolean sort, \c t2 and \c t3 must have the same sort. The sort of the new node is equal to the sort of \c t2 and \c t3. def_API('Z3_mk_ite', AST, (_in(CONTEXT), _in(AST), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_ite(Z3_context c, Z3_ast t1, Z3_ast t2, Z3_ast t3); /** \brief Create an AST node representing \ccode{t1 iff t2}. The nodes \c t1 and \c t2 must have Boolean sort. def_API('Z3_mk_iff', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_iff(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Create an AST node representing \ccode{t1 implies t2}. The nodes \c t1 and \c t2 must have Boolean sort. def_API('Z3_mk_implies', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_implies(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Create an AST node representing \ccode{t1 xor t2}. The nodes \c t1 and \c t2 must have Boolean sort. def_API('Z3_mk_xor', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_xor(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Create an AST node representing \ccode{args[0] and ... and args[num_args-1]}. The array \c args must have \c num_args elements. All arguments must have Boolean sort. \remark The number of arguments must be greater than zero. def_API('Z3_mk_and', AST, (_in(CONTEXT), _in(UINT), _in_array(1, AST))) */ Z3_ast Z3_API Z3_mk_and(Z3_context c, unsigned num_args, Z3_ast const args[]); /** \brief Create an AST node representing \ccode{args[0] or ... or args[num_args-1]}. The array \c args must have \c num_args elements. All arguments must have Boolean sort. \remark The number of arguments must be greater than zero. def_API('Z3_mk_or', AST, (_in(CONTEXT), _in(UINT), _in_array(1, AST))) */ Z3_ast Z3_API Z3_mk_or(Z3_context c, unsigned num_args, Z3_ast const args[]); /*@}*/ /** @name Integers and Reals */ /*@{*/ /** \brief Create an AST node representing \ccode{args[0] + ... + args[num_args-1]}. The array \c args must have \c num_args elements. All arguments must have int or real sort. \remark The number of arguments must be greater than zero. def_API('Z3_mk_add', AST, (_in(CONTEXT), _in(UINT), _in_array(1, AST))) */ Z3_ast Z3_API Z3_mk_add(Z3_context c, unsigned num_args, Z3_ast const args[]); /** \brief Create an AST node representing \ccode{args[0] * ... * args[num_args-1]}. The array \c args must have \c num_args elements. All arguments must have int or real sort. \remark Z3 has limited support for non-linear arithmetic. \remark The number of arguments must be greater than zero. def_API('Z3_mk_mul', AST, (_in(CONTEXT), _in(UINT), _in_array(1, AST))) */ Z3_ast Z3_API Z3_mk_mul(Z3_context c, unsigned num_args, Z3_ast const args[]); /** \brief Create an AST node representing \ccode{args[0] - ... - args[num_args - 1]}. The array \c args must have \c num_args elements. All arguments must have int or real sort. \remark The number of arguments must be greater than zero. def_API('Z3_mk_sub', AST, (_in(CONTEXT), _in(UINT), _in_array(1, AST))) */ Z3_ast Z3_API Z3_mk_sub(Z3_context c, unsigned num_args, Z3_ast const args[]); /** \brief Create an AST node representing \ccode{- arg}. The arguments must have int or real type. def_API('Z3_mk_unary_minus', AST, (_in(CONTEXT), _in(AST))) */ Z3_ast Z3_API Z3_mk_unary_minus(Z3_context c, Z3_ast arg); /** \brief Create an AST node representing \ccode{arg1 div arg2}. The arguments must either both have int type or both have real type. If the arguments have int type, then the result type is an int type, otherwise the the result type is real. def_API('Z3_mk_div', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_div(Z3_context c, Z3_ast arg1, Z3_ast arg2); /** \brief Create an AST node representing \ccode{arg1 mod arg2}. The arguments must have int type. def_API('Z3_mk_mod', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_mod(Z3_context c, Z3_ast arg1, Z3_ast arg2); /** \brief Create an AST node representing \ccode{arg1 rem arg2}. The arguments must have int type. def_API('Z3_mk_rem', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_rem(Z3_context c, Z3_ast arg1, Z3_ast arg2); /** \brief Create an AST node representing \ccode{arg1 ^ arg2}. The arguments must have int or real type. def_API('Z3_mk_power', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_power(Z3_context c, Z3_ast arg1, Z3_ast arg2); /** \brief Create less than. The nodes \c t1 and \c t2 must have the same sort, and must be int or real. def_API('Z3_mk_lt', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_lt(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Create less than or equal to. The nodes \c t1 and \c t2 must have the same sort, and must be int or real. def_API('Z3_mk_le', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_le(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Create greater than. The nodes \c t1 and \c t2 must have the same sort, and must be int or real. def_API('Z3_mk_gt', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_gt(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Create greater than or equal to. The nodes \c t1 and \c t2 must have the same sort, and must be int or real. def_API('Z3_mk_ge', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_ge(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Create division predicate. The nodes \c t1 and \c t2 must be of integer sort. The predicate is true when \c t1 divides \c t2. For the predicate to be part of linear integer arithmetic, the first argument \c t1 must be a non-zero integer. def_API('Z3_mk_divides', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_divides(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Coerce an integer to a real. There is also a converse operation exposed. It follows the semantics prescribed by the SMT-LIB standard. You can take the floor of a real by creating an auxiliary integer constant \c k and and asserting \ccode{mk_int2real(k) <= t1 < mk_int2real(k)+1}. The node \c t1 must have sort integer. \sa Z3_mk_real2int \sa Z3_mk_is_int def_API('Z3_mk_int2real', AST, (_in(CONTEXT), _in(AST))) */ Z3_ast Z3_API Z3_mk_int2real(Z3_context c, Z3_ast t1); /** \brief Coerce a real to an integer. The semantics of this function follows the SMT-LIB standard for the function to_int \sa Z3_mk_int2real \sa Z3_mk_is_int def_API('Z3_mk_real2int', AST, (_in(CONTEXT), _in(AST))) */ Z3_ast Z3_API Z3_mk_real2int(Z3_context c, Z3_ast t1); /** \brief Check if a real number is an integer. \sa Z3_mk_int2real \sa Z3_mk_real2int def_API('Z3_mk_is_int', AST, (_in(CONTEXT), _in(AST))) */ Z3_ast Z3_API Z3_mk_is_int(Z3_context c, Z3_ast t1); /*@}*/ /** @name Bit-vectors */ /*@{*/ /** \brief Bitwise negation. The node \c t1 must have a bit-vector sort. def_API('Z3_mk_bvnot', AST, (_in(CONTEXT), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvnot(Z3_context c, Z3_ast t1); /** \brief Take conjunction of bits in vector, return vector of length 1. The node \c t1 must have a bit-vector sort. def_API('Z3_mk_bvredand', AST, (_in(CONTEXT), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvredand(Z3_context c, Z3_ast t1); /** \brief Take disjunction of bits in vector, return vector of length 1. The node \c t1 must have a bit-vector sort. def_API('Z3_mk_bvredor', AST, (_in(CONTEXT), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvredor(Z3_context c, Z3_ast t1); /** \brief Bitwise and. The nodes \c t1 and \c t2 must have the same bit-vector sort. def_API('Z3_mk_bvand', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvand(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Bitwise or. The nodes \c t1 and \c t2 must have the same bit-vector sort. def_API('Z3_mk_bvor', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvor(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Bitwise exclusive-or. The nodes \c t1 and \c t2 must have the same bit-vector sort. def_API('Z3_mk_bvxor', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvxor(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Bitwise nand. The nodes \c t1 and \c t2 must have the same bit-vector sort. def_API('Z3_mk_bvnand', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvnand(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Bitwise nor. The nodes \c t1 and \c t2 must have the same bit-vector sort. def_API('Z3_mk_bvnor', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvnor(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Bitwise xnor. The nodes \c t1 and \c t2 must have the same bit-vector sort. def_API('Z3_mk_bvxnor', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvxnor(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Standard two's complement unary minus. The node \c t1 must have bit-vector sort. def_API('Z3_mk_bvneg', AST, (_in(CONTEXT), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvneg(Z3_context c, Z3_ast t1); /** \brief Standard two's complement addition. The nodes \c t1 and \c t2 must have the same bit-vector sort. def_API('Z3_mk_bvadd', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvadd(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Standard two's complement subtraction. The nodes \c t1 and \c t2 must have the same bit-vector sort. def_API('Z3_mk_bvsub', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvsub(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Standard two's complement multiplication. The nodes \c t1 and \c t2 must have the same bit-vector sort. def_API('Z3_mk_bvmul', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvmul(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Unsigned division. It is defined as the \c floor of \ccode{t1/t2} if \c t2 is different from zero. If \ccode{t2} is zero, then the result is undefined. The nodes \c t1 and \c t2 must have the same bit-vector sort. def_API('Z3_mk_bvudiv', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvudiv(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Two's complement signed division. It is defined in the following way: - The \c floor of \ccode{t1/t2} if \c t2 is different from zero, and \ccode{t1*t2 >= 0}. - The \c ceiling of \ccode{t1/t2} if \c t2 is different from zero, and \ccode{t1*t2 < 0}. If \ccode{t2} is zero, then the result is undefined. The nodes \c t1 and \c t2 must have the same bit-vector sort. def_API('Z3_mk_bvsdiv', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvsdiv(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Unsigned remainder. It is defined as \ccode{t1 - (t1 /u t2) * t2}, where \ccode{/u} represents unsigned division. If \ccode{t2} is zero, then the result is undefined. The nodes \c t1 and \c t2 must have the same bit-vector sort. def_API('Z3_mk_bvurem', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvurem(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Two's complement signed remainder (sign follows dividend). It is defined as \ccode{t1 - (t1 /s t2) * t2}, where \ccode{/s} represents signed division. The most significant bit (sign) of the result is equal to the most significant bit of \c t1. If \ccode{t2} is zero, then the result is undefined. The nodes \c t1 and \c t2 must have the same bit-vector sort. \sa Z3_mk_bvsmod def_API('Z3_mk_bvsrem', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvsrem(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Two's complement signed remainder (sign follows divisor). If \ccode{t2} is zero, then the result is undefined. The nodes \c t1 and \c t2 must have the same bit-vector sort. \sa Z3_mk_bvsrem def_API('Z3_mk_bvsmod', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvsmod(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Unsigned less than. The nodes \c t1 and \c t2 must have the same bit-vector sort. def_API('Z3_mk_bvult', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvult(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Two's complement signed less than. It abbreviates: \code (or (and (= (extract[|m-1|:|m-1|] t1) bit1) (= (extract[|m-1|:|m-1|] t2) bit0)) (and (= (extract[|m-1|:|m-1|] t1) (extract[|m-1|:|m-1|] t2)) (bvult t1 t2))) \endcode The nodes \c t1 and \c t2 must have the same bit-vector sort. def_API('Z3_mk_bvslt', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvslt(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Unsigned less than or equal to. The nodes \c t1 and \c t2 must have the same bit-vector sort. def_API('Z3_mk_bvule', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvule(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Two's complement signed less than or equal to. The nodes \c t1 and \c t2 must have the same bit-vector sort. def_API('Z3_mk_bvsle', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvsle(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Unsigned greater than or equal to. The nodes \c t1 and \c t2 must have the same bit-vector sort. def_API('Z3_mk_bvuge', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvuge(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Two's complement signed greater than or equal to. The nodes \c t1 and \c t2 must have the same bit-vector sort. def_API('Z3_mk_bvsge', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvsge(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Unsigned greater than. The nodes \c t1 and \c t2 must have the same bit-vector sort. def_API('Z3_mk_bvugt', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvugt(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Two's complement signed greater than. The nodes \c t1 and \c t2 must have the same bit-vector sort. def_API('Z3_mk_bvsgt', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvsgt(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Concatenate the given bit-vectors. The nodes \c t1 and \c t2 must have (possibly different) bit-vector sorts The result is a bit-vector of size \ccode{n1+n2}, where \c n1 (\c n2) is the size of \c t1 (\c t2). def_API('Z3_mk_concat', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_concat(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Extract the bits \c high down to \c low from a bit-vector of size \c m to yield a new bit-vector of size \c n, where \ccode{n = high - low + 1}. The node \c t1 must have a bit-vector sort. def_API('Z3_mk_extract', AST, (_in(CONTEXT), _in(UINT), _in(UINT), _in(AST))) */ Z3_ast Z3_API Z3_mk_extract(Z3_context c, unsigned high, unsigned low, Z3_ast t1); /** \brief Sign-extend of the given bit-vector to the (signed) equivalent bit-vector of size \ccode{m+i}, where \c m is the size of the given bit-vector. The node \c t1 must have a bit-vector sort. def_API('Z3_mk_sign_ext', AST, (_in(CONTEXT), _in(UINT), _in(AST))) */ Z3_ast Z3_API Z3_mk_sign_ext(Z3_context c, unsigned i, Z3_ast t1); /** \brief Extend the given bit-vector with zeros to the (unsigned) equivalent bit-vector of size \ccode{m+i}, where \c m is the size of the given bit-vector. The node \c t1 must have a bit-vector sort. def_API('Z3_mk_zero_ext', AST, (_in(CONTEXT), _in(UINT), _in(AST))) */ Z3_ast Z3_API Z3_mk_zero_ext(Z3_context c, unsigned i, Z3_ast t1); /** \brief Repeat the given bit-vector up length \ccode{i}. The node \c t1 must have a bit-vector sort. def_API('Z3_mk_repeat', AST, (_in(CONTEXT), _in(UINT), _in(AST))) */ Z3_ast Z3_API Z3_mk_repeat(Z3_context c, unsigned i, Z3_ast t1); /** \brief Shift left. It is equivalent to multiplication by \ccode{2^x} where \c x is the value of the third argument. NB. The semantics of shift operations varies between environments. This definition does not necessarily capture directly the semantics of the programming language or assembly architecture you are modeling. The nodes \c t1 and \c t2 must have the same bit-vector sort. def_API('Z3_mk_bvshl', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvshl(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Logical shift right. It is equivalent to unsigned division by \ccode{2^x} where \c x is the value of the third argument. NB. The semantics of shift operations varies between environments. This definition does not necessarily capture directly the semantics of the programming language or assembly architecture you are modeling. The nodes \c t1 and \c t2 must have the same bit-vector sort. def_API('Z3_mk_bvlshr', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvlshr(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Arithmetic shift right. It is like logical shift right except that the most significant bits of the result always copy the most significant bit of the second argument. The semantics of shift operations varies between environments. This definition does not necessarily capture directly the semantics of the programming language or assembly architecture you are modeling. The nodes \c t1 and \c t2 must have the same bit-vector sort. def_API('Z3_mk_bvashr', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvashr(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Rotate bits of \c t1 to the left \c i times. The node \c t1 must have a bit-vector sort. def_API('Z3_mk_rotate_left', AST, (_in(CONTEXT), _in(UINT), _in(AST))) */ Z3_ast Z3_API Z3_mk_rotate_left(Z3_context c, unsigned i, Z3_ast t1); /** \brief Rotate bits of \c t1 to the right \c i times. The node \c t1 must have a bit-vector sort. def_API('Z3_mk_rotate_right', AST, (_in(CONTEXT), _in(UINT), _in(AST))) */ Z3_ast Z3_API Z3_mk_rotate_right(Z3_context c, unsigned i, Z3_ast t1); /** \brief Rotate bits of \c t1 to the left \c t2 times. The nodes \c t1 and \c t2 must have the same bit-vector sort. def_API('Z3_mk_ext_rotate_left', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_ext_rotate_left(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Rotate bits of \c t1 to the right \c t2 times. The nodes \c t1 and \c t2 must have the same bit-vector sort. def_API('Z3_mk_ext_rotate_right', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_ext_rotate_right(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Create an \c n bit bit-vector from the integer argument \c t1. The resulting bit-vector has \c n bits, where the i'th bit (counting from 0 to \c n-1) is 1 if \c (t1 div 2^i) mod 2 is 1. The node \c t1 must have integer sort. def_API('Z3_mk_int2bv', AST, (_in(CONTEXT), _in(UINT), _in(AST))) */ Z3_ast Z3_API Z3_mk_int2bv(Z3_context c, unsigned n, Z3_ast t1); /** \brief Create an integer from the bit-vector argument \c t1. If \c is_signed is false, then the bit-vector \c t1 is treated as unsigned. So the result is non-negative and in the range \ccode{[0..2^N-1]}, where N are the number of bits in \c t1. If \c is_signed is true, \c t1 is treated as a signed bit-vector. The node \c t1 must have a bit-vector sort. def_API('Z3_mk_bv2int', AST, (_in(CONTEXT), _in(AST), _in(BOOL))) */ Z3_ast Z3_API Z3_mk_bv2int(Z3_context c,Z3_ast t1, bool is_signed); /** \brief Create a predicate that checks that the bit-wise addition of \c t1 and \c t2 does not overflow. The nodes \c t1 and \c t2 must have the same bit-vector sort. The returned node is of sort Bool. def_API('Z3_mk_bvadd_no_overflow', AST, (_in(CONTEXT), _in(AST), _in(AST), _in(BOOL))) */ Z3_ast Z3_API Z3_mk_bvadd_no_overflow(Z3_context c, Z3_ast t1, Z3_ast t2, bool is_signed); /** \brief Create a predicate that checks that the bit-wise signed addition of \c t1 and \c t2 does not underflow. The nodes \c t1 and \c t2 must have the same bit-vector sort. The returned node is of sort Bool. def_API('Z3_mk_bvadd_no_underflow', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvadd_no_underflow(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Create a predicate that checks that the bit-wise signed subtraction of \c t1 and \c t2 does not overflow. The nodes \c t1 and \c t2 must have the same bit-vector sort. The returned node is of sort Bool. def_API('Z3_mk_bvsub_no_overflow', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvsub_no_overflow(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Create a predicate that checks that the bit-wise subtraction of \c t1 and \c t2 does not underflow. The nodes \c t1 and \c t2 must have the same bit-vector sort. The returned node is of sort Bool. def_API('Z3_mk_bvsub_no_underflow', AST, (_in(CONTEXT), _in(AST), _in(AST), _in(BOOL))) */ Z3_ast Z3_API Z3_mk_bvsub_no_underflow(Z3_context c, Z3_ast t1, Z3_ast t2, bool is_signed); /** \brief Create a predicate that checks that the bit-wise signed division of \c t1 and \c t2 does not overflow. The nodes \c t1 and \c t2 must have the same bit-vector sort. The returned node is of sort Bool. def_API('Z3_mk_bvsdiv_no_overflow', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvsdiv_no_overflow(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Check that bit-wise negation does not overflow when \c t1 is interpreted as a signed bit-vector. The node \c t1 must have bit-vector sort. The returned node is of sort Bool. def_API('Z3_mk_bvneg_no_overflow', AST, (_in(CONTEXT), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvneg_no_overflow(Z3_context c, Z3_ast t1); /** \brief Create a predicate that checks that the bit-wise multiplication of \c t1 and \c t2 does not overflow. The nodes \c t1 and \c t2 must have the same bit-vector sort. The returned node is of sort Bool. def_API('Z3_mk_bvmul_no_overflow', AST, (_in(CONTEXT), _in(AST), _in(AST), _in(BOOL))) */ Z3_ast Z3_API Z3_mk_bvmul_no_overflow(Z3_context c, Z3_ast t1, Z3_ast t2, bool is_signed); /** \brief Create a predicate that checks that the bit-wise signed multiplication of \c t1 and \c t2 does not underflow. The nodes \c t1 and \c t2 must have the same bit-vector sort. The returned node is of sort Bool. def_API('Z3_mk_bvmul_no_underflow', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvmul_no_underflow(Z3_context c, Z3_ast t1, Z3_ast t2); /*@}*/ /** @name Arrays */ /*@{*/ /** \brief Array read. The argument \c a is the array and \c i is the index of the array that gets read. The node \c a must have an array sort \ccode{[domain -> range]}, and \c i must have the sort \c domain. The sort of the result is \c range. \sa Z3_mk_array_sort \sa Z3_mk_store def_API('Z3_mk_select', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_select(Z3_context c, Z3_ast a, Z3_ast i); /** \brief n-ary Array read. The argument \c a is the array and \c idxs are the indices of the array that gets read. def_API('Z3_mk_select_n', AST, (_in(CONTEXT), _in(AST), _in(UINT), _in_array(2, AST))) */ Z3_ast Z3_API Z3_mk_select_n(Z3_context c, Z3_ast a, unsigned n, Z3_ast const* idxs); /** \brief Array update. The node \c a must have an array sort \ccode{[domain -> range]}, \c i must have sort \c domain, \c v must have sort range. The sort of the result is \ccode{[domain -> range]}. The semantics of this function is given by the theory of arrays described in the SMT-LIB standard. See http://smtlib.org for more details. The result of this function is an array that is equal to \c a (with respect to \c select) on all indices except for \c i, where it maps to \c v (and the \c select of \c a with respect to \c i may be a different value). \sa Z3_mk_array_sort \sa Z3_mk_select def_API('Z3_mk_store', AST, (_in(CONTEXT), _in(AST), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_store(Z3_context c, Z3_ast a, Z3_ast i, Z3_ast v); /** \brief n-ary Array update. def_API('Z3_mk_store_n', AST, (_in(CONTEXT), _in(AST), _in(UINT), _in_array(2, AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_store_n(Z3_context c, Z3_ast a, unsigned n, Z3_ast const* idxs, Z3_ast v); /** \brief Create the constant array. The resulting term is an array, such that a \c select on an arbitrary index produces the value \c v. \param c logical context. \param domain domain sort for the array. \param v value that the array maps to. def_API('Z3_mk_const_array', AST, (_in(CONTEXT), _in(SORT), _in(AST))) */ Z3_ast Z3_API Z3_mk_const_array(Z3_context c, Z3_sort domain, Z3_ast v); /** \brief Map f on the argument arrays. The \c n nodes \c args must be of array sorts \ccode{[domain_i -> range_i]}. The function declaration \c f must have type \ccode{ range_1 .. range_n -> range}. \c v must have sort range. The sort of the result is \ccode{[domain_i -> range]}. \sa Z3_mk_array_sort \sa Z3_mk_store \sa Z3_mk_select def_API('Z3_mk_map', AST, (_in(CONTEXT), _in(FUNC_DECL), _in(UINT), _in_array(2, AST))) */ Z3_ast Z3_API Z3_mk_map(Z3_context c, Z3_func_decl f, unsigned n, Z3_ast const* args); /** \brief Access the array default value. Produces the default range value, for arrays that can be represented as finite maps with a default range value. \param c logical context. \param array array value whose default range value is accessed. def_API('Z3_mk_array_default', AST, (_in(CONTEXT), _in(AST))) */ Z3_ast Z3_API Z3_mk_array_default(Z3_context c, Z3_ast array); /** \brief Create array with the same interpretation as a function. The array satisfies the property (f x) = (select (_ as-array f) x) for every argument x. def_API('Z3_mk_as_array', AST, (_in(CONTEXT), _in(FUNC_DECL))) */ Z3_ast Z3_API Z3_mk_as_array(Z3_context c, Z3_func_decl f); /** \brief Create predicate that holds if Boolean array \c set has \c k elements set to true. def_API('Z3_mk_set_has_size', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_set_has_size(Z3_context c, Z3_ast set, Z3_ast k); /*@}*/ /** @name Sets */ /*@{*/ /** \brief Create Set type. def_API('Z3_mk_set_sort', SORT, (_in(CONTEXT), _in(SORT))) */ Z3_sort Z3_API Z3_mk_set_sort(Z3_context c, Z3_sort ty); /** \brief Create the empty set. def_API('Z3_mk_empty_set', AST, (_in(CONTEXT), _in(SORT))) */ Z3_ast Z3_API Z3_mk_empty_set(Z3_context c, Z3_sort domain); /** \brief Create the full set. def_API('Z3_mk_full_set', AST, (_in(CONTEXT), _in(SORT))) */ Z3_ast Z3_API Z3_mk_full_set(Z3_context c, Z3_sort domain); /** \brief Add an element to a set. The first argument must be a set, the second an element. def_API('Z3_mk_set_add', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_set_add(Z3_context c, Z3_ast set, Z3_ast elem); /** \brief Remove an element to a set. The first argument must be a set, the second an element. def_API('Z3_mk_set_del', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_set_del(Z3_context c, Z3_ast set, Z3_ast elem); /** \brief Take the union of a list of sets. def_API('Z3_mk_set_union', AST, (_in(CONTEXT), _in(UINT), _in_array(1, AST))) */ Z3_ast Z3_API Z3_mk_set_union(Z3_context c, unsigned num_args, Z3_ast const args[]); /** \brief Take the intersection of a list of sets. def_API('Z3_mk_set_intersect', AST, (_in(CONTEXT), _in(UINT), _in_array(1, AST))) */ Z3_ast Z3_API Z3_mk_set_intersect(Z3_context c, unsigned num_args, Z3_ast const args[]); /** \brief Take the set difference between two sets. def_API('Z3_mk_set_difference', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_set_difference(Z3_context c, Z3_ast arg1, Z3_ast arg2); /** \brief Take the complement of a set. def_API('Z3_mk_set_complement', AST, (_in(CONTEXT), _in(AST))) */ Z3_ast Z3_API Z3_mk_set_complement(Z3_context c, Z3_ast arg); /** \brief Check for set membership. The first argument should be an element type of the set. def_API('Z3_mk_set_member', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_set_member(Z3_context c, Z3_ast elem, Z3_ast set); /** \brief Check for subsetness of sets. def_API('Z3_mk_set_subset', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_set_subset(Z3_context c, Z3_ast arg1, Z3_ast arg2); /** \brief Create array extensionality index given two arrays with the same sort. The meaning is given by the axiom: (=> (= (select A (array-ext A B)) (select B (array-ext A B))) (= A B)) def_API('Z3_mk_array_ext', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_array_ext(Z3_context c, Z3_ast arg1, Z3_ast arg2); /*@}*/ /** @name Numerals */ /*@{*/ /** \brief Create a numeral of a given sort. \param c logical context. \param numeral A string representing the numeral value in decimal notation. The string may be of the form `[num]*[.[num]*][E[+|-][num]+]`. If the given sort is a real, then the numeral can be a rational, that is, a string of the form `[num]* / [num]*` . \param ty The sort of the numeral. In the current implementation, the given sort can be an int, real, finite-domain, or bit-vectors of arbitrary size. \sa Z3_mk_int \sa Z3_mk_unsigned_int def_API('Z3_mk_numeral', AST, (_in(CONTEXT), _in(STRING), _in(SORT))) */ Z3_ast Z3_API Z3_mk_numeral(Z3_context c, Z3_string numeral, Z3_sort ty); /** \brief Create a real from a fraction. \param c logical context. \param num numerator of rational. \param den denominator of rational. \pre den != 0 \sa Z3_mk_numeral \sa Z3_mk_int \sa Z3_mk_unsigned_int def_API('Z3_mk_real', AST, (_in(CONTEXT), _in(INT), _in(INT))) */ Z3_ast Z3_API Z3_mk_real(Z3_context c, int num, int den); /** \brief Create a numeral of an int, bit-vector, or finite-domain sort. This function can be used to create numerals that fit in a machine integer. It is slightly faster than #Z3_mk_numeral since it is not necessary to parse a string. \sa Z3_mk_numeral def_API('Z3_mk_int', AST, (_in(CONTEXT), _in(INT), _in(SORT))) */ Z3_ast Z3_API Z3_mk_int(Z3_context c, int v, Z3_sort ty); /** \brief Create a numeral of a int, bit-vector, or finite-domain sort. This function can be used to create numerals that fit in a machine unsigned integer. It is slightly faster than #Z3_mk_numeral since it is not necessary to parse a string. \sa Z3_mk_numeral def_API('Z3_mk_unsigned_int', AST, (_in(CONTEXT), _in(UINT), _in(SORT))) */ Z3_ast Z3_API Z3_mk_unsigned_int(Z3_context c, unsigned v, Z3_sort ty); /** \brief Create a numeral of a int, bit-vector, or finite-domain sort. This function can be used to create numerals that fit in a machine \c int64_t integer. It is slightly faster than #Z3_mk_numeral since it is not necessary to parse a string. \sa Z3_mk_numeral def_API('Z3_mk_int64', AST, (_in(CONTEXT), _in(INT64), _in(SORT))) */ Z3_ast Z3_API Z3_mk_int64(Z3_context c, int64_t v, Z3_sort ty); /** \brief Create a numeral of a int, bit-vector, or finite-domain sort. This function can be used to create numerals that fit in a machine \c uint64_t integer. It is slightly faster than #Z3_mk_numeral since it is not necessary to parse a string. \sa Z3_mk_numeral def_API('Z3_mk_unsigned_int64', AST, (_in(CONTEXT), _in(UINT64), _in(SORT))) */ Z3_ast Z3_API Z3_mk_unsigned_int64(Z3_context c, uint64_t v, Z3_sort ty); /** \brief create a bit-vector numeral from a vector of Booleans. \sa Z3_mk_numeral def_API('Z3_mk_bv_numeral', AST, (_in(CONTEXT), _in(UINT), _in_array(1, BOOL))) */ Z3_ast Z3_API Z3_mk_bv_numeral(Z3_context c, unsigned sz, bool const* bits); /*@}*/ /** @name Sequences and regular expressions */ /*@{*/ /** \brief Create a sequence sort out of the sort for the elements. def_API('Z3_mk_seq_sort', SORT, (_in(CONTEXT), _in(SORT))) */ Z3_sort Z3_API Z3_mk_seq_sort(Z3_context c, Z3_sort s); /** \brief Check if \c s is a sequence sort. def_API('Z3_is_seq_sort', BOOL, (_in(CONTEXT), _in(SORT))) */ bool Z3_API Z3_is_seq_sort(Z3_context c, Z3_sort s); /** \brief Retrieve basis sort for sequence sort. def_API('Z3_get_seq_sort_basis', SORT, (_in(CONTEXT), _in(SORT))) */ Z3_sort Z3_API Z3_get_seq_sort_basis(Z3_context c, Z3_sort s); /** \brief Create a regular expression sort out of a sequence sort. def_API('Z3_mk_re_sort', SORT, (_in(CONTEXT), _in(SORT))) */ Z3_sort Z3_API Z3_mk_re_sort(Z3_context c, Z3_sort seq); /** \brief Check if \c s is a regular expression sort. def_API('Z3_is_re_sort', BOOL, (_in(CONTEXT), _in(SORT))) */ bool Z3_API Z3_is_re_sort(Z3_context c, Z3_sort s); /** \brief Retrieve basis sort for regex sort. def_API('Z3_get_re_sort_basis', SORT, (_in(CONTEXT), _in(SORT))) */ Z3_sort Z3_API Z3_get_re_sort_basis(Z3_context c, Z3_sort s); /** \brief Create a sort for 8 bit strings. This function creates a sort for ASCII strings. Each character is 8 bits. def_API('Z3_mk_string_sort', SORT ,(_in(CONTEXT), )) */ Z3_sort Z3_API Z3_mk_string_sort(Z3_context c); /** \brief Check if \c s is a string sort. def_API('Z3_is_string_sort', BOOL, (_in(CONTEXT), _in(SORT))) */ bool Z3_API Z3_is_string_sort(Z3_context c, Z3_sort s); /** \brief Create a string constant out of the string that is passed in def_API('Z3_mk_string' ,AST ,(_in(CONTEXT), _in(STRING))) */ Z3_ast Z3_API Z3_mk_string(Z3_context c, Z3_string s); /** \brief Create a string constant out of the string that is passed in It takes the length of the string as well to take into account 0 characters. The string is unescaped. def_API('Z3_mk_lstring' ,AST ,(_in(CONTEXT), _in(UINT), _in(STRING))) */ Z3_ast Z3_API Z3_mk_lstring(Z3_context c, unsigned len, Z3_string s); /** \brief Determine if \c s is a string constant. def_API('Z3_is_string', BOOL, (_in(CONTEXT), _in(AST))) */ bool Z3_API Z3_is_string(Z3_context c, Z3_ast s); /** \brief Retrieve the string constant stored in \c s. \pre Z3_is_string(c, s) def_API('Z3_get_string' ,STRING ,(_in(CONTEXT), _in(AST))) */ Z3_string Z3_API Z3_get_string(Z3_context c, Z3_ast s); /** \brief Retrieve the unescaped string constant stored in \c s. \pre Z3_is_string(c, s) def_API('Z3_get_lstring' ,STRING ,(_in(CONTEXT), _in(AST), _out(UINT))) */ Z3_string Z3_API Z3_get_lstring(Z3_context c, Z3_ast s, unsigned* length); /** \brief Create an empty sequence of the sequence sort \c seq. \pre s is a sequence sort. def_API('Z3_mk_seq_empty' ,AST ,(_in(CONTEXT), _in(SORT))) */ Z3_ast Z3_API Z3_mk_seq_empty(Z3_context c, Z3_sort seq); /** \brief Create a unit sequence of \c a. def_API('Z3_mk_seq_unit' ,AST ,(_in(CONTEXT), _in(AST))) */ Z3_ast Z3_API Z3_mk_seq_unit(Z3_context c, Z3_ast a); /** \brief Concatenate sequences. \pre n > 0 def_API('Z3_mk_seq_concat' ,AST ,(_in(CONTEXT), _in(UINT), _in_array(1, AST))) */ Z3_ast Z3_API Z3_mk_seq_concat(Z3_context c, unsigned n, Z3_ast const args[]); /** \brief Check if \c prefix is a prefix of \c s. \pre prefix and s are the same sequence sorts. def_API('Z3_mk_seq_prefix' ,AST ,(_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_seq_prefix(Z3_context c, Z3_ast prefix, Z3_ast s); /** \brief Check if \c suffix is a suffix of \c s. \pre \c suffix and \c s are the same sequence sorts. def_API('Z3_mk_seq_suffix' ,AST ,(_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_seq_suffix(Z3_context c, Z3_ast suffix, Z3_ast s); /** \brief Check if \c container contains \c containee. \pre \c container and \c containee are the same sequence sorts. def_API('Z3_mk_seq_contains' ,AST ,(_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_seq_contains(Z3_context c, Z3_ast container, Z3_ast containee); /** \brief Check if \c s1 is lexicographically strictly less than \c s2. \pre \c s1 and \c s2 are strings def_API('Z3_mk_str_lt' ,AST ,(_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_str_lt(Z3_context c, Z3_ast prefix, Z3_ast s); /** \brief Check if \c s1 is equal or lexicographically strictly less than \c s2. \pre \c s1 and \c s2 are strings def_API('Z3_mk_str_le' ,AST ,(_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_str_le(Z3_context c, Z3_ast prefix, Z3_ast s); /** \brief Extract subsequence starting at \c offset of \c length. def_API('Z3_mk_seq_extract' ,AST ,(_in(CONTEXT), _in(AST), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_seq_extract(Z3_context c, Z3_ast s, Z3_ast offset, Z3_ast length); /** \brief Replace the first occurrence of \c src with \c dst in \c s. def_API('Z3_mk_seq_replace' ,AST ,(_in(CONTEXT), _in(AST), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_seq_replace(Z3_context c, Z3_ast s, Z3_ast src, Z3_ast dst); /** \brief Retrieve from \c s the unit sequence positioned at position \c index. The sequence is empty if the index is out of bounds. def_API('Z3_mk_seq_at' ,AST ,(_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_seq_at(Z3_context c, Z3_ast s, Z3_ast index); /** \brief Retrieve from \c s the element positioned at position \c index. The function is under-specified if the index is out of bounds. def_API('Z3_mk_seq_nth' ,AST ,(_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_seq_nth(Z3_context c, Z3_ast s, Z3_ast index); /** \brief Return the length of the sequence \c s. def_API('Z3_mk_seq_length' ,AST ,(_in(CONTEXT), _in(AST))) */ Z3_ast Z3_API Z3_mk_seq_length(Z3_context c, Z3_ast s); /** \brief Return index of first occurrence of \c substr in \c s starting from offset \c offset. If \c s does not contain \c substr, then the value is -1, if \c offset is the length of \c s, then the value is -1 as well. The value is -1 if \c offset is negative or larger than the length of \c s. def_API('Z3_mk_seq_index' ,AST ,(_in(CONTEXT), _in(AST), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_seq_index(Z3_context c, Z3_ast s, Z3_ast substr, Z3_ast offset); /** \brief Return the last occurrence of \c substr in \c s. If \c s does not contain \c substr, then the value is -1, def_API('Z3_mk_seq_last_index', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_seq_last_index(Z3_context c, Z3_ast, Z3_ast substr); /** \brief Convert string to integer. def_API('Z3_mk_str_to_int' ,AST ,(_in(CONTEXT), _in(AST))) */ Z3_ast Z3_API Z3_mk_str_to_int(Z3_context c, Z3_ast s); /** \brief Integer to string conversion. def_API('Z3_mk_int_to_str' ,AST ,(_in(CONTEXT), _in(AST))) */ Z3_ast Z3_API Z3_mk_int_to_str(Z3_context c, Z3_ast s); /** \brief Create a regular expression that accepts the sequence \c seq. def_API('Z3_mk_seq_to_re' ,AST ,(_in(CONTEXT), _in(AST))) */ Z3_ast Z3_API Z3_mk_seq_to_re(Z3_context c, Z3_ast seq); /** \brief Check if \c seq is in the language generated by the regular expression \c re. def_API('Z3_mk_seq_in_re' ,AST ,(_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_seq_in_re(Z3_context c, Z3_ast seq, Z3_ast re); /** \brief Create the regular language \c re+. def_API('Z3_mk_re_plus' ,AST ,(_in(CONTEXT), _in(AST))) */ Z3_ast Z3_API Z3_mk_re_plus(Z3_context c, Z3_ast re); /** \brief Create the regular language \c re*. def_API('Z3_mk_re_star' ,AST ,(_in(CONTEXT), _in(AST))) */ Z3_ast Z3_API Z3_mk_re_star(Z3_context c, Z3_ast re); /** \brief Create the regular language \c [re]. def_API('Z3_mk_re_option' ,AST ,(_in(CONTEXT), _in(AST))) */ Z3_ast Z3_API Z3_mk_re_option(Z3_context c, Z3_ast re); /** \brief Create the union of the regular languages. \pre n > 0 def_API('Z3_mk_re_union' ,AST ,(_in(CONTEXT), _in(UINT), _in_array(1, AST))) */ Z3_ast Z3_API Z3_mk_re_union(Z3_context c, unsigned n, Z3_ast const args[]); /** \brief Create the concatenation of the regular languages. \pre n > 0 def_API('Z3_mk_re_concat' ,AST ,(_in(CONTEXT), _in(UINT), _in_array(1, AST))) */ Z3_ast Z3_API Z3_mk_re_concat(Z3_context c, unsigned n, Z3_ast const args[]); /** \brief Create the range regular expression over two sequences of length 1. def_API('Z3_mk_re_range' ,AST ,(_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_re_range(Z3_context c, Z3_ast lo, Z3_ast hi); /** \brief Create a regular expression loop. The supplied regular expression \c r is repeated between \c lo and \c hi times. The \c lo should be below \c hi with one exception: when supplying the value \c hi as 0, the meaning is to repeat the argument \c r at least \c lo number of times, and with an unbounded upper bound. def_API('Z3_mk_re_loop', AST, (_in(CONTEXT), _in(AST), _in(UINT), _in(UINT))) */ Z3_ast Z3_API Z3_mk_re_loop(Z3_context c, Z3_ast r, unsigned lo, unsigned hi); /** \brief Create the intersection of the regular languages. \pre n > 0 def_API('Z3_mk_re_intersect' ,AST ,(_in(CONTEXT), _in(UINT), _in_array(1, AST))) */ Z3_ast Z3_API Z3_mk_re_intersect(Z3_context c, unsigned n, Z3_ast const args[]); /** \brief Create the complement of the regular language \c re. def_API('Z3_mk_re_complement' ,AST ,(_in(CONTEXT), _in(AST))) */ Z3_ast Z3_API Z3_mk_re_complement(Z3_context c, Z3_ast re); /** \brief Create an empty regular expression of sort \c re. \pre re is a regular expression sort. def_API('Z3_mk_re_empty' ,AST ,(_in(CONTEXT), _in(SORT))) */ Z3_ast Z3_API Z3_mk_re_empty(Z3_context c, Z3_sort re); /** \brief Create an universal regular expression of sort \c re. \pre re is a regular expression sort. def_API('Z3_mk_re_full' ,AST ,(_in(CONTEXT), _in(SORT))) */ Z3_ast Z3_API Z3_mk_re_full(Z3_context c, Z3_sort re); /*@}*/ /** @name Special relations */ /*@{*/ /** \brief create a linear ordering relation over signature \c a. The relation is identified by the index \c id. def_API('Z3_mk_linear_order', FUNC_DECL ,(_in(CONTEXT), _in(SORT), _in(UINT))) */ Z3_func_decl Z3_API Z3_mk_linear_order(Z3_context c, Z3_sort a, unsigned id); /** \brief create a partial ordering relation over signature \c a and index \c id. def_API('Z3_mk_partial_order', FUNC_DECL ,(_in(CONTEXT), _in(SORT), _in(UINT))) */ Z3_func_decl Z3_API Z3_mk_partial_order(Z3_context c, Z3_sort a, unsigned id); /** \brief create a piecewise linear ordering relation over signature \c a and index \c id. def_API('Z3_mk_piecewise_linear_order', FUNC_DECL ,(_in(CONTEXT), _in(SORT), _in(UINT))) */ Z3_func_decl Z3_API Z3_mk_piecewise_linear_order(Z3_context c, Z3_sort a, unsigned id); /** \brief create a tree ordering relation over signature \c a identified using index \c id. def_API('Z3_mk_tree_order', FUNC_DECL, (_in(CONTEXT), _in(SORT), _in(UINT))) */ Z3_func_decl Z3_API Z3_mk_tree_order(Z3_context c, Z3_sort a, unsigned id); /** \brief create transitive closure of binary relation. \pre f is a binary relation, such that the two arguments have the same sorts. The resulting relation f+ represents the transitive closure of f. def_API('Z3_mk_transitive_closure', FUNC_DECL ,(_in(CONTEXT), _in(FUNC_DECL))) */ Z3_func_decl Z3_API Z3_mk_transitive_closure(Z3_context c, Z3_func_decl f); /*@}*/ /** @name Quantifiers */ /*@{*/ /** \brief Create a pattern for quantifier instantiation. Z3 uses pattern matching to instantiate quantifiers. If a pattern is not provided for a quantifier, then Z3 will automatically compute a set of patterns for it. However, for optimal performance, the user should provide the patterns. Patterns comprise a list of terms. The list should be non-empty. If the list comprises of more than one term, it is a called a multi-pattern. In general, one can pass in a list of (multi-)patterns in the quantifier constructor. \sa Z3_mk_forall \sa Z3_mk_exists def_API('Z3_mk_pattern', PATTERN, (_in(CONTEXT), _in(UINT), _in_array(1, AST))) */ Z3_pattern Z3_API Z3_mk_pattern(Z3_context c, unsigned num_patterns, Z3_ast const terms[]); /** \brief Create a bound variable. Bound variables are indexed by de-Bruijn indices. It is perhaps easiest to explain the meaning of de-Bruijn indices by indicating the compilation process from non-de-Bruijn formulas to de-Bruijn format. \verbatim abs(forall (x1) phi) = forall (x1) abs1(phi, x1, 0) abs(forall (x1, x2) phi) = abs(forall (x1) abs(forall (x2) phi)) abs1(x, x, n) = b_n abs1(y, x, n) = y abs1(f(t1,...,tn), x, n) = f(abs1(t1,x,n), ..., abs1(tn,x,n)) abs1(forall (x1) phi, x, n) = forall (x1) (abs1(phi, x, n+1)) \endverbatim The last line is significant: the index of a bound variable is different depending on the scope in which it appears. The deeper x appears, the higher is its index. \param c logical context \param index de-Bruijn index \param ty sort of the bound variable \sa Z3_mk_forall \sa Z3_mk_exists def_API('Z3_mk_bound', AST, (_in(CONTEXT), _in(UINT), _in(SORT))) */ Z3_ast Z3_API Z3_mk_bound(Z3_context c, unsigned index, Z3_sort ty); /** \brief Create a forall formula. It takes an expression \c body that contains bound variables of the same sorts as the sorts listed in the array \c sorts. The bound variables are de-Bruijn indices created using #Z3_mk_bound. The array \c decl_names contains the names that the quantified formula uses for the bound variables. Z3 applies the convention that the last element in the \c decl_names and \c sorts array refers to the variable with index 0, the second to last element of \c decl_names and \c sorts refers to the variable with index 1, etc. \param c logical context. \param weight quantifiers are associated with weights indicating the importance of using the quantifier during instantiation. By default, pass the weight 0. \param num_patterns number of patterns. \param patterns array containing the patterns created using #Z3_mk_pattern. \param num_decls number of variables to be bound. \param sorts the sorts of the bound variables. \param decl_names names of the bound variables \param body the body of the quantifier. \sa Z3_mk_pattern \sa Z3_mk_bound \sa Z3_mk_exists def_API('Z3_mk_forall', AST, (_in(CONTEXT), _in(UINT), _in(UINT), _in_array(2, PATTERN), _in(UINT), _in_array(4, SORT), _in_array(4, SYMBOL), _in(AST))) */ Z3_ast Z3_API Z3_mk_forall(Z3_context c, unsigned weight, unsigned num_patterns, Z3_pattern const patterns[], unsigned num_decls, Z3_sort const sorts[], Z3_symbol const decl_names[], Z3_ast body); /** \brief Create an exists formula. Similar to #Z3_mk_forall. \sa Z3_mk_pattern \sa Z3_mk_bound \sa Z3_mk_forall \sa Z3_mk_quantifier def_API('Z3_mk_exists', AST, (_in(CONTEXT), _in(UINT), _in(UINT), _in_array(2, PATTERN), _in(UINT), _in_array(4, SORT), _in_array(4, SYMBOL), _in(AST))) */ Z3_ast Z3_API Z3_mk_exists(Z3_context c, unsigned weight, unsigned num_patterns, Z3_pattern const patterns[], unsigned num_decls, Z3_sort const sorts[], Z3_symbol const decl_names[], Z3_ast body); /** \brief Create a quantifier - universal or existential, with pattern hints. See the documentation for #Z3_mk_forall for an explanation of the parameters. \param c logical context. \param is_forall flag to indicate if this is a universal or existential quantifier. \param weight quantifiers are associated with weights indicating the importance of using the quantifier during instantiation. By default, pass the weight 0. \param num_patterns number of patterns. \param patterns array containing the patterns created using #Z3_mk_pattern. \param num_decls number of variables to be bound. \param sorts array of sorts of the bound variables. \param decl_names names of the bound variables. \param body the body of the quantifier. \sa Z3_mk_pattern \sa Z3_mk_bound \sa Z3_mk_forall \sa Z3_mk_exists def_API('Z3_mk_quantifier', AST, (_in(CONTEXT), _in(BOOL), _in(UINT), _in(UINT), _in_array(3, PATTERN), _in(UINT), _in_array(5, SORT), _in_array(5, SYMBOL), _in(AST))) */ Z3_ast Z3_API Z3_mk_quantifier( Z3_context c, bool is_forall, unsigned weight, unsigned num_patterns, Z3_pattern const patterns[], unsigned num_decls, Z3_sort const sorts[], Z3_symbol const decl_names[], Z3_ast body); /** \brief Create a quantifier - universal or existential, with pattern hints, no patterns, and attributes \param c logical context. \param is_forall flag to indicate if this is a universal or existential quantifier. \param quantifier_id identifier to identify quantifier \param skolem_id identifier to identify skolem constants introduced by quantifier. \param weight quantifiers are associated with weights indicating the importance of using the quantifier during instantiation. By default, pass the weight 0. \param num_patterns number of patterns. \param patterns array containing the patterns created using #Z3_mk_pattern. \param num_no_patterns number of no_patterns. \param no_patterns array containing subexpressions to be excluded from inferred patterns. \param num_decls number of variables to be bound. \param sorts array of sorts of the bound variables. \param decl_names names of the bound variables. \param body the body of the quantifier. \sa Z3_mk_pattern \sa Z3_mk_bound \sa Z3_mk_forall \sa Z3_mk_exists def_API('Z3_mk_quantifier_ex', AST, (_in(CONTEXT), _in(BOOL), _in(UINT), _in(SYMBOL), _in(SYMBOL), _in(UINT), _in_array(5, PATTERN), _in(UINT), _in_array(7, AST), _in(UINT), _in_array(9, SORT), _in_array(9, SYMBOL), _in(AST))) */ Z3_ast Z3_API Z3_mk_quantifier_ex( Z3_context c, bool is_forall, unsigned weight, Z3_symbol quantifier_id, Z3_symbol skolem_id, unsigned num_patterns, Z3_pattern const patterns[], unsigned num_no_patterns, Z3_ast const no_patterns[], unsigned num_decls, Z3_sort const sorts[], Z3_symbol const decl_names[], Z3_ast body); /** \brief Create a universal quantifier using a list of constants that will form the set of bound variables. \param c logical context. \param weight quantifiers are associated with weights indicating the importance of using the quantifier during instantiation. By default, pass the weight 0. \param num_bound number of constants to be abstracted into bound variables. \param bound array of constants to be abstracted into bound variables. \param num_patterns number of patterns. \param patterns array containing the patterns created using #Z3_mk_pattern. \param body the body of the quantifier. \sa Z3_mk_pattern \sa Z3_mk_exists_const def_API('Z3_mk_forall_const', AST, (_in(CONTEXT), _in(UINT), _in(UINT), _in_array(2, APP), _in(UINT), _in_array(4, PATTERN), _in(AST))) */ Z3_ast Z3_API Z3_mk_forall_const( Z3_context c, unsigned weight, unsigned num_bound, Z3_app const bound[], unsigned num_patterns, Z3_pattern const patterns[], Z3_ast body ); /** \brief Similar to #Z3_mk_forall_const. \brief Create an existential quantifier using a list of constants that will form the set of bound variables. \param c logical context. \param weight quantifiers are associated with weights indicating the importance of using the quantifier during instantiation. By default, pass the weight 0. \param num_bound number of constants to be abstracted into bound variables. \param bound array of constants to be abstracted into bound variables. \param num_patterns number of patterns. \param patterns array containing the patterns created using #Z3_mk_pattern. \param body the body of the quantifier. \sa Z3_mk_pattern \sa Z3_mk_forall_const def_API('Z3_mk_exists_const', AST, (_in(CONTEXT), _in(UINT), _in(UINT), _in_array(2, APP), _in(UINT), _in_array(4, PATTERN), _in(AST))) */ Z3_ast Z3_API Z3_mk_exists_const( Z3_context c, unsigned weight, unsigned num_bound, Z3_app const bound[], unsigned num_patterns, Z3_pattern const patterns[], Z3_ast body ); /** \brief Create a universal or existential quantifier using a list of constants that will form the set of bound variables. def_API('Z3_mk_quantifier_const', AST, (_in(CONTEXT), _in(BOOL), _in(UINT), _in(UINT), _in_array(3, APP), _in(UINT), _in_array(5, PATTERN), _in(AST))) */ Z3_ast Z3_API Z3_mk_quantifier_const( Z3_context c, bool is_forall, unsigned weight, unsigned num_bound, Z3_app const bound[], unsigned num_patterns, Z3_pattern const patterns[], Z3_ast body ); /** \brief Create a universal or existential quantifier using a list of constants that will form the set of bound variables. def_API('Z3_mk_quantifier_const_ex', AST, (_in(CONTEXT), _in(BOOL), _in(UINT), _in(SYMBOL), _in(SYMBOL), _in(UINT), _in_array(5, APP), _in(UINT), _in_array(7, PATTERN), _in(UINT), _in_array(9, AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_quantifier_const_ex( Z3_context c, bool is_forall, unsigned weight, Z3_symbol quantifier_id, Z3_symbol skolem_id, unsigned num_bound, Z3_app const bound[], unsigned num_patterns, Z3_pattern const patterns[], unsigned num_no_patterns, Z3_ast const no_patterns[], Z3_ast body ); /** \brief Create a lambda expression. It takes an expression \c body that contains bound variables of the same sorts as the sorts listed in the array \c sorts. The bound variables are de-Bruijn indices created using #Z3_mk_bound. The array \c decl_names contains the names that the quantified formula uses for the bound variables. Z3 applies the convention that the last element in the \c decl_names and \c sorts array refers to the variable with index 0, the second to last element of \c decl_names and \c sorts refers to the variable with index 1, etc. The sort of the resulting expression is \c (Array sorts range) where \c range is the sort of \c body. For example, if the lambda binds two variables of sort \c Int and \c Bool, and the \c body has sort \c Real, the sort of the expression is \c (Array Int Bool Real). \param c logical context \param num_decls number of variables to be bound. \param sorts the sorts of the bound variables. \param decl_names names of the bound variables \param body the body of the lambda expression. \sa Z3_mk_bound \sa Z3_mk_forall \sa Z3_mk_lambda_const def_API('Z3_mk_lambda', AST, (_in(CONTEXT), _in(UINT), _in_array(1, SORT), _in_array(1, SYMBOL), _in(AST))) */ Z3_ast Z3_API Z3_mk_lambda(Z3_context c, unsigned num_decls, Z3_sort const sorts[], Z3_symbol const decl_names[], Z3_ast body); /** \brief Create a lambda expression using a list of constants that form the set of bound variables \param c logical context. \param num_bound number of constants to be abstracted into bound variables. \param bound array of constants to be abstracted into bound variables. \param body the body of the lambda expression. \sa Z3_mk_bound \sa Z3_mk_forall \sa Z3_mk_lambda def_API('Z3_mk_lambda_const', AST, (_in(CONTEXT), _in(UINT), _in_array(1, APP), _in(AST))) */ Z3_ast Z3_API Z3_mk_lambda_const(Z3_context c, unsigned num_bound, Z3_app const bound[], Z3_ast body); /*@}*/ /** @name Accessors */ /*@{*/ /** \brief Return \c Z3_INT_SYMBOL if the symbol was constructed using #Z3_mk_int_symbol, and \c Z3_STRING_SYMBOL if the symbol was constructed using #Z3_mk_string_symbol. def_API('Z3_get_symbol_kind', UINT, (_in(CONTEXT), _in(SYMBOL))) */ Z3_symbol_kind Z3_API Z3_get_symbol_kind(Z3_context c, Z3_symbol s); /** \brief Return the symbol int value. \pre Z3_get_symbol_kind(s) == Z3_INT_SYMBOL \sa Z3_mk_int_symbol def_API('Z3_get_symbol_int', INT, (_in(CONTEXT), _in(SYMBOL))) */ int Z3_API Z3_get_symbol_int(Z3_context c, Z3_symbol s); /** \brief Return the symbol name. \pre Z3_get_symbol_kind(s) == Z3_STRING_SYMBOL \warning The returned buffer is statically allocated by Z3. It will be automatically deallocated when #Z3_del_context is invoked. So, the buffer is invalidated in the next call to \c Z3_get_symbol_string. \sa Z3_mk_string_symbol def_API('Z3_get_symbol_string', STRING, (_in(CONTEXT), _in(SYMBOL))) */ Z3_string Z3_API Z3_get_symbol_string(Z3_context c, Z3_symbol s); /** \brief Return the sort name as a symbol. def_API('Z3_get_sort_name', SYMBOL, (_in(CONTEXT), _in(SORT))) */ Z3_symbol Z3_API Z3_get_sort_name(Z3_context c, Z3_sort d); /** \brief Return a unique identifier for \c s. def_API('Z3_get_sort_id', UINT, (_in(CONTEXT), _in(SORT))) */ unsigned Z3_API Z3_get_sort_id(Z3_context c, Z3_sort s); /** \brief Convert a \c Z3_sort into \c Z3_ast. This is just type casting. def_API('Z3_sort_to_ast', AST, (_in(CONTEXT), _in(SORT))) */ Z3_ast Z3_API Z3_sort_to_ast(Z3_context c, Z3_sort s); /** \brief compare sorts. def_API('Z3_is_eq_sort', BOOL, (_in(CONTEXT), _in(SORT), _in(SORT))) */ bool Z3_API Z3_is_eq_sort(Z3_context c, Z3_sort s1, Z3_sort s2); /** \brief Return the sort kind (e.g., array, tuple, int, bool, etc). \sa Z3_sort_kind def_API('Z3_get_sort_kind', UINT, (_in(CONTEXT), _in(SORT))) */ Z3_sort_kind Z3_API Z3_get_sort_kind(Z3_context c, Z3_sort t); /** \brief Return the size of the given bit-vector sort. \pre Z3_get_sort_kind(c, t) == Z3_BV_SORT \sa Z3_mk_bv_sort \sa Z3_get_sort_kind def_API('Z3_get_bv_sort_size', UINT, (_in(CONTEXT), _in(SORT))) */ unsigned Z3_API Z3_get_bv_sort_size(Z3_context c, Z3_sort t); /** \brief Store the size of the sort in \c r. Return \c false if the call failed. That is, Z3_get_sort_kind(s) == Z3_FINITE_DOMAIN_SORT def_API('Z3_get_finite_domain_sort_size', BOOL, (_in(CONTEXT), _in(SORT), _out(UINT64))) */ Z3_bool_opt Z3_API Z3_get_finite_domain_sort_size(Z3_context c, Z3_sort s, uint64_t* r); /** \brief Return the domain of the given array sort. In the case of a multi-dimensional array, this function returns the sort of the first dimension. \pre Z3_get_sort_kind(c, t) == Z3_ARRAY_SORT \sa Z3_mk_array_sort \sa Z3_get_sort_kind def_API('Z3_get_array_sort_domain', SORT, (_in(CONTEXT), _in(SORT))) */ Z3_sort Z3_API Z3_get_array_sort_domain(Z3_context c, Z3_sort t); /** \brief Return the range of the given array sort. \pre Z3_get_sort_kind(c, t) == Z3_ARRAY_SORT \sa Z3_mk_array_sort \sa Z3_get_sort_kind def_API('Z3_get_array_sort_range', SORT, (_in(CONTEXT), _in(SORT))) */ Z3_sort Z3_API Z3_get_array_sort_range(Z3_context c, Z3_sort t); /** \brief Return the constructor declaration of the given tuple sort. \pre Z3_get_sort_kind(c, t) == Z3_DATATYPE_SORT \sa Z3_mk_tuple_sort \sa Z3_get_sort_kind def_API('Z3_get_tuple_sort_mk_decl', FUNC_DECL, (_in(CONTEXT), _in(SORT))) */ Z3_func_decl Z3_API Z3_get_tuple_sort_mk_decl(Z3_context c, Z3_sort t); /** \brief Return the number of fields of the given tuple sort. \pre Z3_get_sort_kind(c, t) == Z3_DATATYPE_SORT \sa Z3_mk_tuple_sort \sa Z3_get_sort_kind def_API('Z3_get_tuple_sort_num_fields', UINT, (_in(CONTEXT), _in(SORT))) */ unsigned Z3_API Z3_get_tuple_sort_num_fields(Z3_context c, Z3_sort t); /** \brief Return the i-th field declaration (i.e., projection function declaration) of the given tuple sort. \pre Z3_get_sort_kind(t) == Z3_DATATYPE_SORT \pre i < Z3_get_tuple_sort_num_fields(c, t) \sa Z3_mk_tuple_sort \sa Z3_get_sort_kind def_API('Z3_get_tuple_sort_field_decl', FUNC_DECL, (_in(CONTEXT), _in(SORT), _in(UINT))) */ Z3_func_decl Z3_API Z3_get_tuple_sort_field_decl(Z3_context c, Z3_sort t, unsigned i); /** \brief Return number of constructors for datatype. \pre Z3_get_sort_kind(t) == Z3_DATATYPE_SORT \sa Z3_get_datatype_sort_constructor \sa Z3_get_datatype_sort_recognizer \sa Z3_get_datatype_sort_constructor_accessor def_API('Z3_get_datatype_sort_num_constructors', UINT, (_in(CONTEXT), _in(SORT))) */ unsigned Z3_API Z3_get_datatype_sort_num_constructors( Z3_context c, Z3_sort t); /** \brief Return idx'th constructor. \pre Z3_get_sort_kind(t) == Z3_DATATYPE_SORT \pre idx < Z3_get_datatype_sort_num_constructors(c, t) \sa Z3_get_datatype_sort_num_constructors \sa Z3_get_datatype_sort_recognizer \sa Z3_get_datatype_sort_constructor_accessor def_API('Z3_get_datatype_sort_constructor', FUNC_DECL, (_in(CONTEXT), _in(SORT), _in(UINT))) */ Z3_func_decl Z3_API Z3_get_datatype_sort_constructor( Z3_context c, Z3_sort t, unsigned idx); /** \brief Return idx'th recognizer. \pre Z3_get_sort_kind(t) == Z3_DATATYPE_SORT \pre idx < Z3_get_datatype_sort_num_constructors(c, t) \sa Z3_get_datatype_sort_num_constructors \sa Z3_get_datatype_sort_constructor \sa Z3_get_datatype_sort_constructor_accessor def_API('Z3_get_datatype_sort_recognizer', FUNC_DECL, (_in(CONTEXT), _in(SORT), _in(UINT))) */ Z3_func_decl Z3_API Z3_get_datatype_sort_recognizer( Z3_context c, Z3_sort t, unsigned idx); /** \brief Return idx_a'th accessor for the idx_c'th constructor. \pre Z3_get_sort_kind(t) == Z3_DATATYPE_SORT \pre idx_c < Z3_get_datatype_sort_num_constructors(c, t) \pre idx_a < Z3_get_domain_size(c, Z3_get_datatype_sort_constructor(c, idx_c)) \sa Z3_get_datatype_sort_num_constructors \sa Z3_get_datatype_sort_constructor \sa Z3_get_datatype_sort_recognizer def_API('Z3_get_datatype_sort_constructor_accessor', FUNC_DECL, (_in(CONTEXT), _in(SORT), _in(UINT), _in(UINT))) */ Z3_func_decl Z3_API Z3_get_datatype_sort_constructor_accessor(Z3_context c, Z3_sort t, unsigned idx_c, unsigned idx_a); /** \brief Update record field with a value. This corresponds to the 'with' construct in OCaml. It has the effect of updating a record field with a given value. 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 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). \pre Z3_get_sort_kind(Z3_get_sort(c, t)) == Z3_get_domain(c, field_access, 1) == Z3_DATATYPE_SORT \pre Z3_get_sort(c, value) == Z3_get_range(c, field_access) def_API('Z3_datatype_update_field', AST, (_in(CONTEXT), _in(FUNC_DECL), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_datatype_update_field(Z3_context c, Z3_func_decl field_access, Z3_ast t, Z3_ast value); /** \brief Return arity of relation. \pre Z3_get_sort_kind(s) == Z3_RELATION_SORT \sa Z3_get_relation_column def_API('Z3_get_relation_arity', UINT, (_in(CONTEXT), _in(SORT))) */ unsigned Z3_API Z3_get_relation_arity(Z3_context c, Z3_sort s); /** \brief Return sort at i'th column of relation sort. \pre Z3_get_sort_kind(c, s) == Z3_RELATION_SORT \pre col < Z3_get_relation_arity(c, s) \sa Z3_get_relation_arity def_API('Z3_get_relation_column', SORT, (_in(CONTEXT), _in(SORT), _in(UINT))) */ Z3_sort Z3_API Z3_get_relation_column(Z3_context c, Z3_sort s, unsigned col); /** \brief Pseudo-Boolean relations. Encode p1 + p2 + ... + pn <= k def_API('Z3_mk_atmost', AST, (_in(CONTEXT), _in(UINT), _in_array(1,AST), _in(UINT))) */ Z3_ast Z3_API Z3_mk_atmost(Z3_context c, unsigned num_args, Z3_ast const args[], unsigned k); /** \brief Pseudo-Boolean relations. Encode p1 + p2 + ... + pn >= k def_API('Z3_mk_atleast', AST, (_in(CONTEXT), _in(UINT), _in_array(1,AST), _in(UINT))) */ Z3_ast Z3_API Z3_mk_atleast(Z3_context c, unsigned num_args, Z3_ast const args[], unsigned k); /** \brief Pseudo-Boolean relations. Encode k1*p1 + k2*p2 + ... + kn*pn <= k def_API('Z3_mk_pble', AST, (_in(CONTEXT), _in(UINT), _in_array(1,AST), _in_array(1,INT), _in(INT))) */ Z3_ast Z3_API Z3_mk_pble(Z3_context c, unsigned num_args, Z3_ast const args[], int const coeffs[], int k); /** \brief Pseudo-Boolean relations. Encode k1*p1 + k2*p2 + ... + kn*pn >= k def_API('Z3_mk_pbge', AST, (_in(CONTEXT), _in(UINT), _in_array(1,AST), _in_array(1,INT), _in(INT))) */ Z3_ast Z3_API Z3_mk_pbge(Z3_context c, unsigned num_args, Z3_ast const args[], int const coeffs[], int k); /** \brief Pseudo-Boolean relations. Encode k1*p1 + k2*p2 + ... + kn*pn = k def_API('Z3_mk_pbeq', AST, (_in(CONTEXT), _in(UINT), _in_array(1,AST), _in_array(1,INT), _in(INT))) */ Z3_ast Z3_API Z3_mk_pbeq(Z3_context c, unsigned num_args, Z3_ast const args[], int const coeffs[], int k); /** \brief Convert a \c Z3_func_decl into \c Z3_ast. This is just type casting. def_API('Z3_func_decl_to_ast', AST, (_in(CONTEXT), _in(FUNC_DECL))) */ Z3_ast Z3_API Z3_func_decl_to_ast(Z3_context c, Z3_func_decl f); /** \brief Compare terms. def_API('Z3_is_eq_func_decl', BOOL, (_in(CONTEXT), _in(FUNC_DECL), _in(FUNC_DECL))) */ bool Z3_API Z3_is_eq_func_decl(Z3_context c, Z3_func_decl f1, Z3_func_decl f2); /** \brief Return a unique identifier for \c f. def_API('Z3_get_func_decl_id', UINT, (_in(CONTEXT), _in(FUNC_DECL))) */ unsigned Z3_API Z3_get_func_decl_id(Z3_context c, Z3_func_decl f); /** \brief Return the constant declaration name as a symbol. def_API('Z3_get_decl_name', SYMBOL, (_in(CONTEXT), _in(FUNC_DECL))) */ Z3_symbol Z3_API Z3_get_decl_name(Z3_context c, Z3_func_decl d); /** \brief Return declaration kind corresponding to declaration. def_API('Z3_get_decl_kind', UINT, (_in(CONTEXT), _in(FUNC_DECL))) */ Z3_decl_kind Z3_API Z3_get_decl_kind(Z3_context c, Z3_func_decl d); /** \brief Return the number of parameters of the given declaration. \sa Z3_get_arity def_API('Z3_get_domain_size', UINT, (_in(CONTEXT), _in(FUNC_DECL))) */ unsigned Z3_API Z3_get_domain_size(Z3_context c, Z3_func_decl d); /** \brief Alias for \c Z3_get_domain_size. \sa Z3_get_domain_size def_API('Z3_get_arity', UINT, (_in(CONTEXT), _in(FUNC_DECL))) */ unsigned Z3_API Z3_get_arity(Z3_context c, Z3_func_decl d); /** \brief Return the sort of the i-th parameter of the given function declaration. \pre i < Z3_get_domain_size(d) \sa Z3_get_domain_size def_API('Z3_get_domain', SORT, (_in(CONTEXT), _in(FUNC_DECL), _in(UINT))) */ Z3_sort Z3_API Z3_get_domain(Z3_context c, Z3_func_decl d, unsigned i); /** \brief Return the range of the given declaration. If \c d is a constant (i.e., has zero arguments), then this function returns the sort of the constant. def_API('Z3_get_range', SORT, (_in(CONTEXT), _in(FUNC_DECL))) */ Z3_sort Z3_API Z3_get_range(Z3_context c, Z3_func_decl d); /** \brief Return the number of parameters associated with a declaration. def_API('Z3_get_decl_num_parameters', UINT, (_in(CONTEXT), _in(FUNC_DECL))) */ unsigned Z3_API Z3_get_decl_num_parameters(Z3_context c, Z3_func_decl d); /** \brief Return the parameter type associated with a declaration. \param c the context \param d the function declaration \param idx is the index of the named parameter it should be between 0 and the number of parameters. def_API('Z3_get_decl_parameter_kind', UINT, (_in(CONTEXT), _in(FUNC_DECL), _in(UINT))) */ Z3_parameter_kind Z3_API Z3_get_decl_parameter_kind(Z3_context c, Z3_func_decl d, unsigned idx); /** \brief Return the integer value associated with an integer parameter. \pre Z3_get_decl_parameter_kind(c, d, idx) == Z3_PARAMETER_INT def_API('Z3_get_decl_int_parameter', INT, (_in(CONTEXT), _in(FUNC_DECL), _in(UINT))) */ int Z3_API Z3_get_decl_int_parameter(Z3_context c, Z3_func_decl d, unsigned idx); /** \brief Return the double value associated with an double parameter. \pre Z3_get_decl_parameter_kind(c, d, idx) == Z3_PARAMETER_DOUBLE def_API('Z3_get_decl_double_parameter', DOUBLE, (_in(CONTEXT), _in(FUNC_DECL), _in(UINT))) */ double Z3_API Z3_get_decl_double_parameter(Z3_context c, Z3_func_decl d, unsigned idx); /** \brief Return the double value associated with an double parameter. \pre Z3_get_decl_parameter_kind(c, d, idx) == Z3_PARAMETER_SYMBOL def_API('Z3_get_decl_symbol_parameter', SYMBOL, (_in(CONTEXT), _in(FUNC_DECL), _in(UINT))) */ Z3_symbol Z3_API Z3_get_decl_symbol_parameter(Z3_context c, Z3_func_decl d, unsigned idx); /** \brief Return the sort value associated with a sort parameter. \pre Z3_get_decl_parameter_kind(c, d, idx) == Z3_PARAMETER_SORT def_API('Z3_get_decl_sort_parameter', SORT, (_in(CONTEXT), _in(FUNC_DECL), _in(UINT))) */ Z3_sort Z3_API Z3_get_decl_sort_parameter(Z3_context c, Z3_func_decl d, unsigned idx); /** \brief Return the expression value associated with an expression parameter. \pre Z3_get_decl_parameter_kind(c, d, idx) == Z3_PARAMETER_AST def_API('Z3_get_decl_ast_parameter', AST, (_in(CONTEXT), _in(FUNC_DECL), _in(UINT))) */ Z3_ast Z3_API Z3_get_decl_ast_parameter(Z3_context c, Z3_func_decl d, unsigned idx); /** \brief Return the expression value associated with an expression parameter. \pre Z3_get_decl_parameter_kind(c, d, idx) == Z3_PARAMETER_FUNC_DECL def_API('Z3_get_decl_func_decl_parameter', FUNC_DECL, (_in(CONTEXT), _in(FUNC_DECL), _in(UINT))) */ Z3_func_decl Z3_API Z3_get_decl_func_decl_parameter(Z3_context c, Z3_func_decl d, unsigned idx); /** \brief Return the rational value, as a string, associated with a rational parameter. \pre Z3_get_decl_parameter_kind(c, d, idx) == Z3_PARAMETER_RATIONAL def_API('Z3_get_decl_rational_parameter', STRING, (_in(CONTEXT), _in(FUNC_DECL), _in(UINT))) */ Z3_string Z3_API Z3_get_decl_rational_parameter(Z3_context c, Z3_func_decl d, unsigned idx); /** \brief Convert a \c Z3_app into \c Z3_ast. This is just type casting. def_API('Z3_app_to_ast', AST, (_in(CONTEXT), _in(APP))) */ Z3_ast Z3_API Z3_app_to_ast(Z3_context c, Z3_app a); /** \brief Return the declaration of a constant or function application. def_API('Z3_get_app_decl', FUNC_DECL, (_in(CONTEXT), _in(APP))) */ Z3_func_decl Z3_API Z3_get_app_decl(Z3_context c, Z3_app a); /** \brief Return the number of argument of an application. If \c t is an constant, then the number of arguments is 0. def_API('Z3_get_app_num_args', UINT, (_in(CONTEXT), _in(APP))) */ unsigned Z3_API Z3_get_app_num_args(Z3_context c, Z3_app a); /** \brief Return the i-th argument of the given application. \pre i < Z3_get_app_num_args(c, a) def_API('Z3_get_app_arg', AST, (_in(CONTEXT), _in(APP), _in(UINT))) */ Z3_ast Z3_API Z3_get_app_arg(Z3_context c, Z3_app a, unsigned i); /** \brief Compare terms. def_API('Z3_is_eq_ast', BOOL, (_in(CONTEXT), _in(AST), _in(AST))) */ bool Z3_API Z3_is_eq_ast(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Return a unique identifier for \c t. The identifier is unique up to structural equality. Thus, two ast nodes created by the same context and having the same children and same function symbols have the same identifiers. Ast nodes created in the same context, but having different children or different functions have different identifiers. Variables and quantifiers are also assigned different identifiers according to their structure. def_API('Z3_get_ast_id', UINT, (_in(CONTEXT), _in(AST))) */ unsigned Z3_API Z3_get_ast_id(Z3_context c, Z3_ast t); /** \brief Return a hash code for the given AST. The hash code is structural. You can use Z3_get_ast_id interchangeably with this function. def_API('Z3_get_ast_hash', UINT, (_in(CONTEXT), _in(AST))) */ unsigned Z3_API Z3_get_ast_hash(Z3_context c, Z3_ast a); /** \brief Return the sort of an AST node. The AST node must be a constant, application, numeral, bound variable, or quantifier. def_API('Z3_get_sort', SORT, (_in(CONTEXT), _in(AST))) */ Z3_sort Z3_API Z3_get_sort(Z3_context c, Z3_ast a); /** \brief Return \c true if the given expression \c t is well sorted. def_API('Z3_is_well_sorted', BOOL, (_in(CONTEXT), _in(AST))) */ bool Z3_API Z3_is_well_sorted(Z3_context c, Z3_ast t); /** \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))) */ Z3_lbool Z3_API Z3_get_bool_value(Z3_context c, Z3_ast a); /** \brief Return the kind of the given AST. def_API('Z3_get_ast_kind', UINT, (_in(CONTEXT), _in(AST))) */ Z3_ast_kind Z3_API Z3_get_ast_kind(Z3_context c, Z3_ast a); /** def_API('Z3_is_app', BOOL, (_in(CONTEXT), _in(AST))) */ bool Z3_API Z3_is_app(Z3_context c, Z3_ast a); /** def_API('Z3_is_numeral_ast', BOOL, (_in(CONTEXT), _in(AST))) */ bool Z3_API Z3_is_numeral_ast(Z3_context c, Z3_ast a); /** \brief Return \c true if the given AST is a real algebraic number. def_API('Z3_is_algebraic_number', BOOL, (_in(CONTEXT), _in(AST))) */ bool Z3_API Z3_is_algebraic_number(Z3_context c, Z3_ast a); /** \brief Convert an \c ast into an \c APP_AST. This is just type casting. \pre \code Z3_get_ast_kind(c, a) == \c Z3_APP_AST \endcode def_API('Z3_to_app', APP, (_in(CONTEXT), _in(AST))) */ Z3_app Z3_API Z3_to_app(Z3_context c, Z3_ast a); /** \brief Convert an AST into a FUNC_DECL_AST. This is just type casting. \pre \code Z3_get_ast_kind(c, a) == Z3_FUNC_DECL_AST \endcode def_API('Z3_to_func_decl', FUNC_DECL, (_in(CONTEXT), _in(AST))) */ Z3_func_decl Z3_API Z3_to_func_decl(Z3_context c, Z3_ast a); /** \brief Return numeral value, as a string of a numeric constant term \pre Z3_get_ast_kind(c, a) == Z3_NUMERAL_AST def_API('Z3_get_numeral_string', STRING, (_in(CONTEXT), _in(AST))) */ Z3_string Z3_API Z3_get_numeral_string(Z3_context c, Z3_ast a); /** \brief Return numeral as a string in decimal notation. The result has at most \c precision decimal places. \pre Z3_get_ast_kind(c, a) == Z3_NUMERAL_AST || Z3_is_algebraic_number(c, a) def_API('Z3_get_numeral_decimal_string', STRING, (_in(CONTEXT), _in(AST), _in(UINT))) */ Z3_string Z3_API Z3_get_numeral_decimal_string(Z3_context c, Z3_ast a, unsigned precision); /** \brief Return numeral as a double. \pre Z3_get_ast_kind(c, a) == Z3_NUMERAL_AST || Z3_is_algebraic_number(c, a) def_API('Z3_get_numeral_double', DOUBLE, (_in(CONTEXT), _in(AST))) */ double Z3_API Z3_get_numeral_double(Z3_context c, Z3_ast a); /** \brief Return the numerator (as a numeral AST) of a numeral AST of sort Real. \pre Z3_get_ast_kind(c, a) == Z3_NUMERAL_AST def_API('Z3_get_numerator', AST, (_in(CONTEXT), _in(AST))) */ Z3_ast Z3_API Z3_get_numerator(Z3_context c, Z3_ast a); /** \brief Return the denominator (as a numeral AST) of a numeral AST of sort Real. \pre Z3_get_ast_kind(c, a) == Z3_NUMERAL_AST def_API('Z3_get_denominator', AST, (_in(CONTEXT), _in(AST))) */ Z3_ast Z3_API Z3_get_denominator(Z3_context c, Z3_ast a); /** \brief Return numeral value, as a pair of 64 bit numbers if the representation fits. \param c logical context. \param a term. \param num numerator. \param den denominator. Return \c true if the numeral value fits in 64 bit numerals, \c false otherwise. \pre Z3_get_ast_kind(a) == Z3_NUMERAL_AST def_API('Z3_get_numeral_small', BOOL, (_in(CONTEXT), _in(AST), _out(INT64), _out(INT64))) */ bool Z3_API Z3_get_numeral_small(Z3_context c, Z3_ast a, int64_t* num, int64_t* den); /** \brief Similar to #Z3_get_numeral_string, but only succeeds if the value can fit in a machine int. Return \c true if the call succeeded. \pre Z3_get_ast_kind(c, v) == Z3_NUMERAL_AST \sa Z3_get_numeral_string def_API('Z3_get_numeral_int', BOOL, (_in(CONTEXT), _in(AST), _out(INT))) */ bool Z3_API Z3_get_numeral_int(Z3_context c, Z3_ast v, int* i); /** \brief Similar to #Z3_get_numeral_string, but only succeeds if the value can fit in a machine unsigned int. Return \c true if the call succeeded. \pre Z3_get_ast_kind(c, v) == Z3_NUMERAL_AST \sa Z3_get_numeral_string def_API('Z3_get_numeral_uint', BOOL, (_in(CONTEXT), _in(AST), _out(UINT))) */ bool Z3_API Z3_get_numeral_uint(Z3_context c, Z3_ast v, unsigned* u); /** \brief Similar to #Z3_get_numeral_string, but only succeeds if the value can fit in a machine \c uint64_t int. Return \c true if the call succeeded. \pre Z3_get_ast_kind(c, v) == Z3_NUMERAL_AST \sa Z3_get_numeral_string def_API('Z3_get_numeral_uint64', BOOL, (_in(CONTEXT), _in(AST), _out(UINT64))) */ bool Z3_API Z3_get_numeral_uint64(Z3_context c, Z3_ast v, uint64_t* u); /** \brief Similar to #Z3_get_numeral_string, but only succeeds if the value can fit in a machine \c int64_t int. Return \c true if the call succeeded. \pre Z3_get_ast_kind(c, v) == Z3_NUMERAL_AST \sa Z3_get_numeral_string def_API('Z3_get_numeral_int64', BOOL, (_in(CONTEXT), _in(AST), _out(INT64))) */ bool Z3_API Z3_get_numeral_int64(Z3_context c, Z3_ast v, int64_t* i); /** \brief Similar to #Z3_get_numeral_string, but only succeeds if the value can fit as a rational number as machine \c int64_t int. Return \c true if the call succeeded. \pre Z3_get_ast_kind(c, v) == Z3_NUMERAL_AST \sa Z3_get_numeral_string def_API('Z3_get_numeral_rational_int64', BOOL, (_in(CONTEXT), _in(AST), _out(INT64), _out(INT64))) */ bool Z3_API Z3_get_numeral_rational_int64(Z3_context c, Z3_ast v, int64_t* num, int64_t* den); /** \brief Return a lower bound for the given real algebraic number. The interval isolating the number is smaller than 1/10^precision. The result is a numeral AST of sort Real. \pre Z3_is_algebraic_number(c, a) def_API('Z3_get_algebraic_number_lower', AST, (_in(CONTEXT), _in(AST), _in(UINT))) */ Z3_ast Z3_API Z3_get_algebraic_number_lower(Z3_context c, Z3_ast a, unsigned precision); /** \brief Return a upper bound for the given real algebraic number. The interval isolating the number is smaller than 1/10^precision. The result is a numeral AST of sort Real. \pre Z3_is_algebraic_number(c, a) def_API('Z3_get_algebraic_number_upper', AST, (_in(CONTEXT), _in(AST), _in(UINT))) */ Z3_ast Z3_API Z3_get_algebraic_number_upper(Z3_context c, Z3_ast a, unsigned precision); /** \brief Convert a Z3_pattern into Z3_ast. This is just type casting. def_API('Z3_pattern_to_ast', AST, (_in(CONTEXT), _in(PATTERN))) */ Z3_ast Z3_API Z3_pattern_to_ast(Z3_context c, Z3_pattern p); /** \brief Return number of terms in pattern. def_API('Z3_get_pattern_num_terms', UINT, (_in(CONTEXT), _in(PATTERN))) */ unsigned Z3_API Z3_get_pattern_num_terms(Z3_context c, Z3_pattern p); /** \brief Return i'th ast in pattern. def_API('Z3_get_pattern', AST, (_in(CONTEXT), _in(PATTERN), _in(UINT))) */ Z3_ast Z3_API Z3_get_pattern(Z3_context c, Z3_pattern p, unsigned idx); /** \brief Return index of de-Bruijn bound variable. \pre Z3_get_ast_kind(a) == Z3_VAR_AST def_API('Z3_get_index_value', UINT, (_in(CONTEXT), _in(AST))) */ unsigned Z3_API Z3_get_index_value(Z3_context c, Z3_ast a); /** \brief Determine if an ast is a universal quantifier. def_API('Z3_is_quantifier_forall', BOOL, (_in(CONTEXT), _in(AST))) */ bool Z3_API Z3_is_quantifier_forall(Z3_context c, Z3_ast a); /** \brief Determine if ast is an existential quantifier. def_API('Z3_is_quantifier_exists', BOOL, (_in(CONTEXT), _in(AST))) */ bool Z3_API Z3_is_quantifier_exists(Z3_context c, Z3_ast a); /** \brief Determine if ast is a lambda expression. \pre Z3_get_ast_kind(a) == Z3_QUANTIFIER_AST def_API('Z3_is_lambda', BOOL, (_in(CONTEXT), _in(AST))) */ bool Z3_API Z3_is_lambda(Z3_context c, Z3_ast a); /** \brief Obtain weight of quantifier. \pre Z3_get_ast_kind(a) == Z3_QUANTIFIER_AST def_API('Z3_get_quantifier_weight', UINT, (_in(CONTEXT), _in(AST))) */ unsigned Z3_API Z3_get_quantifier_weight(Z3_context c, Z3_ast a); /** \brief Return number of patterns used in quantifier. \pre Z3_get_ast_kind(a) == Z3_QUANTIFIER_AST def_API('Z3_get_quantifier_num_patterns', UINT, (_in(CONTEXT), _in(AST))) */ unsigned Z3_API Z3_get_quantifier_num_patterns(Z3_context c, Z3_ast a); /** \brief Return i'th pattern. \pre Z3_get_ast_kind(a) == Z3_QUANTIFIER_AST def_API('Z3_get_quantifier_pattern_ast', PATTERN, (_in(CONTEXT), _in(AST), _in(UINT))) */ Z3_pattern Z3_API Z3_get_quantifier_pattern_ast(Z3_context c, Z3_ast a, unsigned i); /** \brief Return number of no_patterns used in quantifier. \pre Z3_get_ast_kind(a) == Z3_QUANTIFIER_AST def_API('Z3_get_quantifier_num_no_patterns', UINT, (_in(CONTEXT), _in(AST))) */ unsigned Z3_API Z3_get_quantifier_num_no_patterns(Z3_context c, Z3_ast a); /** \brief Return i'th no_pattern. \pre Z3_get_ast_kind(a) == Z3_QUANTIFIER_AST def_API('Z3_get_quantifier_no_pattern_ast', AST, (_in(CONTEXT), _in(AST), _in(UINT))) */ Z3_ast Z3_API Z3_get_quantifier_no_pattern_ast(Z3_context c, Z3_ast a, unsigned i); /** \brief Return number of bound variables of quantifier. \pre Z3_get_ast_kind(a) == Z3_QUANTIFIER_AST def_API('Z3_get_quantifier_num_bound', UINT, (_in(CONTEXT), _in(AST))) */ unsigned Z3_API Z3_get_quantifier_num_bound(Z3_context c, Z3_ast a); /** \brief Return symbol of the i'th bound variable. \pre Z3_get_ast_kind(a) == Z3_QUANTIFIER_AST def_API('Z3_get_quantifier_bound_name', SYMBOL, (_in(CONTEXT), _in(AST), _in(UINT))) */ Z3_symbol Z3_API Z3_get_quantifier_bound_name(Z3_context c, Z3_ast a, unsigned i); /** \brief Return sort of the i'th bound variable. \pre Z3_get_ast_kind(a) == Z3_QUANTIFIER_AST def_API('Z3_get_quantifier_bound_sort', SORT, (_in(CONTEXT), _in(AST), _in(UINT))) */ Z3_sort Z3_API Z3_get_quantifier_bound_sort(Z3_context c, Z3_ast a, unsigned i); /** \brief Return body of quantifier. \pre Z3_get_ast_kind(a) == Z3_QUANTIFIER_AST def_API('Z3_get_quantifier_body', AST, (_in(CONTEXT), _in(AST))) */ Z3_ast Z3_API Z3_get_quantifier_body(Z3_context c, Z3_ast a); /** \brief Interface to simplifier. Provides an interface to the AST simplifier used by Z3. It returns an AST object which is equal to the argument. The returned AST is simplified using algebraic simplification rules, such as constant propagation (propagating true/false over logical connectives). \sa Z3_simplify_ex def_API('Z3_simplify', AST, (_in(CONTEXT), _in(AST))) */ Z3_ast Z3_API Z3_simplify(Z3_context c, Z3_ast a); /** \brief Interface to simplifier. Provides an interface to the AST simplifier used by Z3. This procedure is similar to #Z3_simplify, but the behavior of the simplifier can be configured using the given parameter set. \sa Z3_simplify \sa Z3_simplify_get_help \sa Z3_simplify_get_param_descrs def_API('Z3_simplify_ex', AST, (_in(CONTEXT), _in(AST), _in(PARAMS))) */ Z3_ast Z3_API Z3_simplify_ex(Z3_context c, Z3_ast a, Z3_params p); /** \brief Return a string describing all available parameters. \sa Z3_simplify_ex \sa Z3_simplify_get_param_descrs def_API('Z3_simplify_get_help', STRING, (_in(CONTEXT),)) */ Z3_string Z3_API Z3_simplify_get_help(Z3_context c); /** \brief Return the parameter description set for the simplify procedure. \sa Z3_simplify_ex \sa Z3_simplify_get_help def_API('Z3_simplify_get_param_descrs', PARAM_DESCRS, (_in(CONTEXT),)) */ Z3_param_descrs Z3_API Z3_simplify_get_param_descrs(Z3_context c); /*@}*/ /** @name Modifiers */ /*@{*/ /** \brief Update the arguments of term \c a using the arguments \c args. The number of arguments \c num_args should coincide with the number of arguments to \c a. If \c a is a quantifier, then num_args has to be 1. def_API('Z3_update_term', AST, (_in(CONTEXT), _in(AST), _in(UINT), _in_array(2, AST))) */ Z3_ast Z3_API Z3_update_term(Z3_context c, Z3_ast a, unsigned num_args, Z3_ast const args[]); /** \brief Substitute every occurrence of \ccode{from[i]} in \c a with \ccode{to[i]}, for \c i smaller than \c num_exprs. The result is the new AST. The arrays \c from and \c to must have size \c num_exprs. For every \c i smaller than \c num_exprs, we must have that sort of \ccode{from[i]} must be equal to sort of \ccode{to[i]}. def_API('Z3_substitute', AST, (_in(CONTEXT), _in(AST), _in(UINT), _in_array(2, AST), _in_array(2, AST))) */ Z3_ast Z3_API Z3_substitute(Z3_context c, Z3_ast a, unsigned num_exprs, Z3_ast const from[], Z3_ast const to[]); /** \brief Substitute the free variables in \c a with the expressions in \c to. For every \c i smaller than \c num_exprs, the variable with de-Bruijn index \c i is replaced with term \ccode{to[i]}. def_API('Z3_substitute_vars', AST, (_in(CONTEXT), _in(AST), _in(UINT), _in_array(2, AST))) */ Z3_ast Z3_API Z3_substitute_vars(Z3_context c, Z3_ast a, unsigned num_exprs, Z3_ast const to[]); /** \brief Translate/Copy the AST \c a from context \c source to context \c target. AST \c a must have been created using context \c source. \pre source != target def_API('Z3_translate', AST, (_in(CONTEXT), _in(AST), _in(CONTEXT))) */ Z3_ast Z3_API Z3_translate(Z3_context source, Z3_ast a, Z3_context target); /*@}*/ /** @name Models */ /*@{*/ /** \brief Create a fresh model object. It has reference count 0. def_API('Z3_mk_model', MODEL, (_in(CONTEXT),)) */ Z3_model Z3_API Z3_mk_model(Z3_context c); /** \brief Increment the reference counter of the given model. def_API('Z3_model_inc_ref', VOID, (_in(CONTEXT), _in(MODEL))) */ void Z3_API Z3_model_inc_ref(Z3_context c, Z3_model m); /** \brief Decrement the reference counter of the given model. def_API('Z3_model_dec_ref', VOID, (_in(CONTEXT), _in(MODEL))) */ void Z3_API Z3_model_dec_ref(Z3_context c, Z3_model m); /** \brief Evaluate the AST node \c t in the given model. Return \c true if succeeded, and store the result in \c v. If \c model_completion is \c true, then Z3 will assign an interpretation for any constant or function that does not have an interpretation in \c m. These constants and functions were essentially don't cares. If \c model_completion is \c false, then Z3 will not assign interpretations to constants for functions that do not have interpretations in \c m. Evaluation behaves as the identify function in this case. The evaluation may fail for the following reasons: - \c t contains a quantifier. - the model \c m is partial, that is, it doesn't have a complete interpretation for uninterpreted functions. That is, the option \ccode{MODEL_PARTIAL=true} was used. - \c t is type incorrect. - \c Z3_interrupt was invoked during evaluation. def_API('Z3_model_eval', BOOL, (_in(CONTEXT), _in(MODEL), _in(AST), _in(BOOL), _out(AST))) */ Z3_bool_opt Z3_API Z3_model_eval(Z3_context c, Z3_model m, Z3_ast t, bool model_completion, Z3_ast * v); /** \brief Return the interpretation (i.e., assignment) of constant \c a in the model \c m. Return \c NULL, if the model does not assign an interpretation for \c a. That should be interpreted as: the value of \c a does not matter. \pre Z3_get_arity(c, a) == 0 def_API('Z3_model_get_const_interp', AST, (_in(CONTEXT), _in(MODEL), _in(FUNC_DECL))) */ Z3_ast_opt Z3_API Z3_model_get_const_interp(Z3_context c, Z3_model m, Z3_func_decl a); /** \brief Test if there exists an interpretation (i.e., assignment) for \c a in the model \c m. def_API('Z3_model_has_interp', BOOL, (_in(CONTEXT), _in(MODEL), _in(FUNC_DECL))) */ bool Z3_API Z3_model_has_interp(Z3_context c, Z3_model m, Z3_func_decl a); /** \brief Return the interpretation of the function \c f in the model \c m. Return \c NULL, if the model does not assign an interpretation for \c f. That should be interpreted as: the \c f does not matter. \pre Z3_get_arity(c, f) > 0 \remark Reference counting must be used to manage Z3_func_interp objects, even when the Z3_context was created using #Z3_mk_context instead of #Z3_mk_context_rc. def_API('Z3_model_get_func_interp', FUNC_INTERP, (_in(CONTEXT), _in(MODEL), _in(FUNC_DECL))) */ Z3_func_interp_opt Z3_API Z3_model_get_func_interp(Z3_context c, Z3_model m, Z3_func_decl f); /** \brief Return the number of constants assigned by the given model. \sa Z3_model_get_const_decl def_API('Z3_model_get_num_consts', UINT, (_in(CONTEXT), _in(MODEL))) */ unsigned Z3_API Z3_model_get_num_consts(Z3_context c, Z3_model m); /** \brief Return the i-th constant in the given model. \pre i < Z3_model_get_num_consts(c, m) \sa Z3_model_eval def_API('Z3_model_get_const_decl', FUNC_DECL, (_in(CONTEXT), _in(MODEL), _in(UINT))) */ Z3_func_decl Z3_API Z3_model_get_const_decl(Z3_context c, Z3_model m, unsigned i); /** \brief Return the number of function interpretations in the given model. A function interpretation is represented as a finite map and an 'else' value. Each entry in the finite map represents the value of a function given a set of arguments. def_API('Z3_model_get_num_funcs', UINT, (_in(CONTEXT), _in(MODEL))) */ unsigned Z3_API Z3_model_get_num_funcs(Z3_context c, Z3_model m); /** \brief Return the declaration of the i-th function in the given model. \pre i < Z3_model_get_num_funcs(c, m) \sa Z3_model_get_num_funcs def_API('Z3_model_get_func_decl', FUNC_DECL, (_in(CONTEXT), _in(MODEL), _in(UINT))) */ Z3_func_decl Z3_API Z3_model_get_func_decl(Z3_context c, Z3_model m, unsigned i); /** \brief Return the number of uninterpreted sorts that \c m assigns an interpretation to. Z3 also provides an interpretation for uninterpreted sorts used in a formula. The interpretation for a sort \c s is a finite set of distinct values. We say this finite set is the "universe" of \c s. \sa Z3_model_get_sort \sa Z3_model_get_sort_universe def_API('Z3_model_get_num_sorts', UINT, (_in(CONTEXT), _in(MODEL))) */ unsigned Z3_API Z3_model_get_num_sorts(Z3_context c, Z3_model m); /** \brief Return a uninterpreted sort that \c m assigns an interpretation. \pre i < Z3_model_get_num_sorts(c, m) \sa Z3_model_get_num_sorts \sa Z3_model_get_sort_universe def_API('Z3_model_get_sort', SORT, (_in(CONTEXT), _in(MODEL), _in(UINT))) */ Z3_sort Z3_API Z3_model_get_sort(Z3_context c, Z3_model m, unsigned i); /** \brief Return the finite set of distinct values that represent the interpretation for sort \c s. \sa Z3_model_get_num_sorts \sa Z3_model_get_sort def_API('Z3_model_get_sort_universe', AST_VECTOR, (_in(CONTEXT), _in(MODEL), _in(SORT))) */ 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 c to context \c dst. def_API('Z3_model_translate', MODEL, (_in(CONTEXT), _in(MODEL), _in(CONTEXT))) */ Z3_model Z3_API Z3_model_translate(Z3_context c, Z3_model m, Z3_context dst); /** \brief The \ccode{(_ as-array f)} AST node is a construct for assigning interpretations for arrays in Z3. It is the array such that forall indices \c i we have that \ccode{(select (_ as-array f) i)} is equal to \ccode{(f i)}. This procedure returns \c true if the \c a is an \c as-array AST node. Z3 current solvers have minimal support for \c as_array nodes. \sa Z3_get_as_array_func_decl def_API('Z3_is_as_array', BOOL, (_in(CONTEXT), _in(AST))) */ bool Z3_API Z3_is_as_array(Z3_context c, Z3_ast a); /** \brief Return the function declaration \c f associated with a \ccode{(_ as_array f)} node. \sa Z3_is_as_array def_API('Z3_get_as_array_func_decl', FUNC_DECL, (_in(CONTEXT), _in(AST))) */ Z3_func_decl Z3_API Z3_get_as_array_func_decl(Z3_context c, Z3_ast a); /** \brief Create a fresh func_interp object, add it to a model for a specified function. It has reference count 0. \param c context \param m model \param f function declaration \param default_value default value for function interpretation def_API('Z3_add_func_interp', FUNC_INTERP, (_in(CONTEXT), _in(MODEL), _in(FUNC_DECL), _in(AST))) */ Z3_func_interp Z3_API Z3_add_func_interp(Z3_context c, Z3_model m, Z3_func_decl f, Z3_ast default_value); /** \brief Add a constant interpretation. def_API('Z3_add_const_interp', VOID, (_in(CONTEXT), _in(MODEL), _in(FUNC_DECL), _in(AST))) */ void Z3_API Z3_add_const_interp(Z3_context c, Z3_model m, Z3_func_decl f, Z3_ast a); /** \brief Increment the reference counter of the given Z3_func_interp object. def_API('Z3_func_interp_inc_ref', VOID, (_in(CONTEXT), _in(FUNC_INTERP))) */ void Z3_API Z3_func_interp_inc_ref(Z3_context c, Z3_func_interp f); /** \brief Decrement the reference counter of the given Z3_func_interp object. def_API('Z3_func_interp_dec_ref', VOID, (_in(CONTEXT), _in(FUNC_INTERP))) */ void Z3_API Z3_func_interp_dec_ref(Z3_context c, Z3_func_interp f); /** \brief Return the number of entries in the given function interpretation. A function interpretation is represented as a finite map and an 'else' value. Each entry in the finite map represents the value of a function given a set of arguments. This procedure return the number of element in the finite map of \c f. def_API('Z3_func_interp_get_num_entries', UINT, (_in(CONTEXT), _in(FUNC_INTERP))) */ unsigned Z3_API Z3_func_interp_get_num_entries(Z3_context c, Z3_func_interp f); /** \brief Return a "point" of the given function interpretation. It represents the value of \c f in a particular point. \pre i < Z3_func_interp_get_num_entries(c, f) \sa Z3_func_interp_get_num_entries def_API('Z3_func_interp_get_entry', FUNC_ENTRY, (_in(CONTEXT), _in(FUNC_INTERP), _in(UINT))) */ Z3_func_entry Z3_API Z3_func_interp_get_entry(Z3_context c, Z3_func_interp f, unsigned i); /** \brief Return the 'else' value of the given function interpretation. A function interpretation is represented as a finite map and an 'else' value. This procedure returns the 'else' value. def_API('Z3_func_interp_get_else', AST, (_in(CONTEXT), _in(FUNC_INTERP))) */ Z3_ast Z3_API Z3_func_interp_get_else(Z3_context c, Z3_func_interp f); /** \brief Return the 'else' value of the given function interpretation. A function interpretation is represented as a finite map and an 'else' value. This procedure can be used to update the 'else' value. def_API('Z3_func_interp_set_else', VOID, (_in(CONTEXT), _in(FUNC_INTERP), _in(AST))) */ void Z3_API Z3_func_interp_set_else(Z3_context c, Z3_func_interp f, Z3_ast else_value); /** \brief Return the arity (number of arguments) of the given function interpretation. def_API('Z3_func_interp_get_arity', UINT, (_in(CONTEXT), _in(FUNC_INTERP))) */ unsigned Z3_API Z3_func_interp_get_arity(Z3_context c, Z3_func_interp f); /** \brief add a function entry to a function interpretation. \param c logical context \param fi a function interpretation to be updated. \param args list of arguments. They should be constant values (such as integers) and be of the same types as the domain of the function. \param value value of the function when the parameters match args. It is assumed that entries added to a function cover disjoint arguments. If an two entries are added with the same arguments, only the second insertion survives and the first inserted entry is removed. def_API('Z3_func_interp_add_entry', VOID, (_in(CONTEXT), _in(FUNC_INTERP), _in(AST_VECTOR), _in(AST))) */ void Z3_API Z3_func_interp_add_entry(Z3_context c, Z3_func_interp fi, Z3_ast_vector args, Z3_ast value); /** \brief Increment the reference counter of the given \c Z3_func_entry object. def_API('Z3_func_entry_inc_ref', VOID, (_in(CONTEXT), _in(FUNC_ENTRY))) */ void Z3_API Z3_func_entry_inc_ref(Z3_context c, Z3_func_entry e); /** \brief Decrement the reference counter of the given \c Z3_func_entry object. def_API('Z3_func_entry_dec_ref', VOID, (_in(CONTEXT), _in(FUNC_ENTRY))) */ void Z3_API Z3_func_entry_dec_ref(Z3_context c, Z3_func_entry e); /** \brief Return the value of this point. A \c Z3_func_entry object represents an element in the finite map used to encode a function interpretation. \sa Z3_func_interp_get_entry def_API('Z3_func_entry_get_value', AST, (_in(CONTEXT), _in(FUNC_ENTRY))) */ Z3_ast Z3_API Z3_func_entry_get_value(Z3_context c, Z3_func_entry e); /** \brief Return the number of arguments in a \c Z3_func_entry object. \sa Z3_func_interp_get_entry def_API('Z3_func_entry_get_num_args', UINT, (_in(CONTEXT), _in(FUNC_ENTRY))) */ unsigned Z3_API Z3_func_entry_get_num_args(Z3_context c, Z3_func_entry e); /** \brief Return an argument of a \c Z3_func_entry object. \pre i < Z3_func_entry_get_num_args(c, e) \sa Z3_func_interp_get_entry def_API('Z3_func_entry_get_arg', AST, (_in(CONTEXT), _in(FUNC_ENTRY), _in(UINT))) */ Z3_ast Z3_API Z3_func_entry_get_arg(Z3_context c, Z3_func_entry e, unsigned i); /*@}*/ /** @name Interaction logging */ /*@{*/ /** \brief Log interaction to a file. extra_API('Z3_open_log', INT, (_in(STRING),)) */ bool Z3_API Z3_open_log(Z3_string filename); /** \brief Append user-defined string to interaction log. The interaction log is opened using Z3_open_log. It contains the formulas that are checked using Z3. You can use this command to append comments, for instance. extra_API('Z3_append_log', VOID, (_in(STRING),)) */ void Z3_API Z3_append_log(Z3_string string); /** \brief Close interaction log. extra_API('Z3_close_log', VOID, ()) */ void Z3_API Z3_close_log(void); /** \brief Enable/disable printing warning messages to the console. Warnings are printed after passing \c true, warning messages are suppressed after calling this method with \c false. def_API('Z3_toggle_warning_messages', VOID, (_in(BOOL),)) */ void Z3_API Z3_toggle_warning_messages(bool enabled); /*@}*/ /** @name String conversion */ /*@{*/ /** \brief Select mode for the format used for pretty-printing AST nodes. The default mode for pretty printing AST nodes is to produce SMT-LIB style output where common subexpressions are printed at each occurrence. The mode is called \c Z3_PRINT_SMTLIB_FULL. To print shared common subexpressions only once, use the \c Z3_PRINT_LOW_LEVEL mode. To print in way that conforms to SMT-LIB standards and uses let expressions to share common sub-expressions use \c Z3_PRINT_SMTLIB2_COMPLIANT. \sa Z3_ast_to_string \sa Z3_pattern_to_string \sa Z3_func_decl_to_string def_API('Z3_set_ast_print_mode', VOID, (_in(CONTEXT), _in(PRINT_MODE))) */ void Z3_API Z3_set_ast_print_mode(Z3_context c, Z3_ast_print_mode mode); /** \brief Convert the given AST node into a string. \warning The result buffer is statically allocated by Z3. It will be automatically deallocated when #Z3_del_context is invoked. So, the buffer is invalidated in the next call to \c Z3_ast_to_string. \sa Z3_pattern_to_string \sa Z3_sort_to_string def_API('Z3_ast_to_string', STRING, (_in(CONTEXT), _in(AST))) */ Z3_string Z3_API Z3_ast_to_string(Z3_context c, Z3_ast a); /** def_API('Z3_pattern_to_string', STRING, (_in(CONTEXT), _in(PATTERN))) */ Z3_string Z3_API Z3_pattern_to_string(Z3_context c, Z3_pattern p); /** def_API('Z3_sort_to_string', STRING, (_in(CONTEXT), _in(SORT))) */ Z3_string Z3_API Z3_sort_to_string(Z3_context c, Z3_sort s); /** def_API('Z3_func_decl_to_string', STRING, (_in(CONTEXT), _in(FUNC_DECL))) */ Z3_string Z3_API Z3_func_decl_to_string(Z3_context c, Z3_func_decl d); /** \brief Convert the given model into a string. \warning The result buffer is statically allocated by Z3. It will be automatically deallocated when #Z3_del_context is invoked. So, the buffer is invalidated in the next call to \c Z3_model_to_string. def_API('Z3_model_to_string', STRING, (_in(CONTEXT), _in(MODEL))) */ Z3_string Z3_API Z3_model_to_string(Z3_context c, Z3_model m); /** \brief Convert the given benchmark into SMT-LIB formatted string. \warning The result buffer is statically allocated by Z3. It will be automatically deallocated when #Z3_del_context is invoked. So, the buffer is invalidated in the next call to \c Z3_benchmark_to_smtlib_string. \param c - context. \param name - name of benchmark. The argument is optional. \param logic - the benchmark logic. \param status - the status string (sat, unsat, or unknown) \param attributes - other attributes, such as source, difficulty or category. \param num_assumptions - number of assumptions. \param assumptions - auxiliary assumptions. \param formula - formula to be checked for consistency in conjunction with assumptions. def_API('Z3_benchmark_to_smtlib_string', STRING, (_in(CONTEXT), _in(STRING), _in(STRING), _in(STRING), _in(STRING), _in(UINT), _in_array(5, AST), _in(AST))) */ Z3_string Z3_API Z3_benchmark_to_smtlib_string(Z3_context c, Z3_string name, Z3_string logic, Z3_string status, Z3_string attributes, unsigned num_assumptions, Z3_ast const assumptions[], Z3_ast formula); /*@}*/ /** @name Parser interface */ /*@{*/ /** \brief Parse the given string using the SMT-LIB2 parser. It returns a formula comprising of the conjunction of assertions in the scope (up to push/pop) at the end of the string. def_API('Z3_parse_smtlib2_string', AST_VECTOR, (_in(CONTEXT), _in(STRING), _in(UINT), _in_array(2, SYMBOL), _in_array(2, SORT), _in(UINT), _in_array(5, SYMBOL), _in_array(5, FUNC_DECL))) */ Z3_ast_vector Z3_API Z3_parse_smtlib2_string(Z3_context c, Z3_string str, unsigned num_sorts, Z3_symbol const sort_names[], Z3_sort const sorts[], unsigned num_decls, Z3_symbol const decl_names[], Z3_func_decl const decls[]); /** \brief Similar to #Z3_parse_smtlib2_string, but reads the benchmark from a file. def_API('Z3_parse_smtlib2_file', AST_VECTOR, (_in(CONTEXT), _in(STRING), _in(UINT), _in_array(2, SYMBOL), _in_array(2, SORT), _in(UINT), _in_array(5, SYMBOL), _in_array(5, FUNC_DECL))) */ Z3_ast_vector Z3_API Z3_parse_smtlib2_file(Z3_context c, Z3_string file_name, unsigned num_sorts, Z3_symbol const sort_names[], Z3_sort const sorts[], unsigned num_decls, Z3_symbol const decl_names[], Z3_func_decl const decls[]); /** \brief Parse and evaluate and SMT-LIB2 command sequence. The state from a previous call is saved so the next evaluation builds on top of the previous call. \returns output generated from processing commands. def_API('Z3_eval_smtlib2_string', STRING, (_in(CONTEXT), _in(STRING),)) */ Z3_string Z3_API Z3_eval_smtlib2_string(Z3_context, Z3_string str); /*@}*/ /** @name Error Handling */ /*@{*/ #ifndef SAFE_ERRORS /** \brief Return the error code for the last API call. A call to a Z3 function may return a non Z3_OK error code, when it is not used correctly. \sa Z3_set_error_handler def_API('Z3_get_error_code', UINT, (_in(CONTEXT), )) */ Z3_error_code Z3_API Z3_get_error_code(Z3_context c); /** \brief Register a Z3 error handler. A call to a Z3 function may return a non \c Z3_OK error code, when it is not used correctly. An error handler can be registered and will be called in this case. To disable the use of the error handler, simply register with \c h=NULL. \warning Log files, created using #Z3_open_log, may be potentially incomplete/incorrect if error handlers are used. \sa Z3_get_error_code */ void Z3_API Z3_set_error_handler(Z3_context c, Z3_error_handler h); #endif /** \brief Set an error. def_API('Z3_set_error', VOID, (_in(CONTEXT), _in(ERROR_CODE))) */ void Z3_API Z3_set_error(Z3_context c, Z3_error_code e); /** \brief Return a string describing the given error code. def_API('Z3_get_error_msg', STRING, (_in(CONTEXT), _in(ERROR_CODE))) */ Z3_string Z3_API Z3_get_error_msg(Z3_context c, Z3_error_code err); /*@}*/ /** @name Miscellaneous */ /*@{*/ /** \brief Return Z3 version number information. \sa Z3_get_full_version def_API('Z3_get_version', VOID, (_out(UINT), _out(UINT), _out(UINT), _out(UINT))) */ void Z3_API Z3_get_version(unsigned * major, unsigned * minor, unsigned * build_number, unsigned * revision_number); /** \brief Return a string that fully describes the version of Z3 in use. \sa Z3_get_version def_API('Z3_get_full_version', STRING, ()) */ Z3_string Z3_API Z3_get_full_version(void); /** \brief Enable tracing messages tagged as \c tag when Z3 is compiled in debug mode. It is a NOOP otherwise \sa Z3_disable_trace def_API('Z3_enable_trace', VOID, (_in(STRING),)) */ void Z3_API Z3_enable_trace(Z3_string tag); /** \brief Disable tracing messages tagged as \c tag when Z3 is compiled in debug mode. It is a NOOP otherwise \sa Z3_enable_trace def_API('Z3_disable_trace', VOID, (_in(STRING),)) */ void Z3_API Z3_disable_trace(Z3_string tag); /** \brief Reset all allocated resources. Use this facility on out-of memory errors. It allows discharging the previous state and resuming afresh. Any pointers previously returned by the API become invalid. def_API('Z3_reset_memory', VOID, ()) */ void Z3_API Z3_reset_memory(void); /** \brief Destroy all allocated resources. Any pointers previously returned by the API become invalid. Can be used for memory leak detection. def_API('Z3_finalize_memory', VOID, ()) */ void Z3_API Z3_finalize_memory(void); /*@}*/ /** @name Goals */ /*@{*/ /** \brief Create a goal (aka problem). A goal is essentially a set of formulas, that can be solved and/or transformed using tactics and solvers. If \c models is \c true, then model generation is enabled for the new goal. If \c unsat_cores is \c true, then unsat core generation is enabled for the new goal. If \c proofs is \c true, then proof generation is enabled for the new goal. Remark, the Z3 context \c c must have been created with proof generation support. \remark Reference counting must be used to manage goals, even when the \c Z3_context was created using #Z3_mk_context instead of #Z3_mk_context_rc. def_API('Z3_mk_goal', GOAL, (_in(CONTEXT), _in(BOOL), _in(BOOL), _in(BOOL))) */ Z3_goal Z3_API Z3_mk_goal(Z3_context c, bool models, bool unsat_cores, bool proofs); /** \brief Increment the reference counter of the given goal. def_API('Z3_goal_inc_ref', VOID, (_in(CONTEXT), _in(GOAL))) */ void Z3_API Z3_goal_inc_ref(Z3_context c, Z3_goal g); /** \brief Decrement the reference counter of the given goal. def_API('Z3_goal_dec_ref', VOID, (_in(CONTEXT), _in(GOAL))) */ void Z3_API Z3_goal_dec_ref(Z3_context c, Z3_goal g); /** \brief Return the "precision" of the given goal. Goals can be transformed using over and under approximations. A under approximation is applied when the objective is to find a model for a given goal. An over approximation is applied when the objective is to find a proof for a given goal. def_API('Z3_goal_precision', UINT, (_in(CONTEXT), _in(GOAL))) */ Z3_goal_prec Z3_API Z3_goal_precision(Z3_context c, Z3_goal g); /** \brief Add a new formula \c a to the given goal. The formula is split according to the following procedure that is applied until a fixed-point: Conjunctions are split into separate formulas. Negations are distributed over disjunctions, resulting in separate formulas. If the goal is \c false, adding new formulas is a no-op. If the formula \c a is \c true, then nothing is added. If the formula \c a is \c false, then the entire goal is replaced by the formula \c false. def_API('Z3_goal_assert', VOID, (_in(CONTEXT), _in(GOAL), _in(AST))) */ void Z3_API Z3_goal_assert(Z3_context c, Z3_goal g, Z3_ast a); /** \brief Return \c true if the given goal contains the formula \c false. def_API('Z3_goal_inconsistent', BOOL, (_in(CONTEXT), _in(GOAL))) */ bool Z3_API Z3_goal_inconsistent(Z3_context c, Z3_goal g); /** \brief Return the depth of the given goal. It tracks how many transformations were applied to it. def_API('Z3_goal_depth', UINT, (_in(CONTEXT), _in(GOAL))) */ unsigned Z3_API Z3_goal_depth(Z3_context c, Z3_goal g); /** \brief Erase all formulas from the given goal. def_API('Z3_goal_reset', VOID, (_in(CONTEXT), _in(GOAL))) */ void Z3_API Z3_goal_reset(Z3_context c, Z3_goal g); /** \brief Return the number of formulas in the given goal. def_API('Z3_goal_size', UINT, (_in(CONTEXT), _in(GOAL))) */ unsigned Z3_API Z3_goal_size(Z3_context c, Z3_goal g); /** \brief Return a formula from the given goal. \pre idx < Z3_goal_size(c, g) def_API('Z3_goal_formula', AST, (_in(CONTEXT), _in(GOAL), _in(UINT))) */ Z3_ast Z3_API Z3_goal_formula(Z3_context c, Z3_goal g, unsigned idx); /** \brief Return the number of formulas, subformulas and terms in the given goal. def_API('Z3_goal_num_exprs', UINT, (_in(CONTEXT), _in(GOAL))) */ unsigned Z3_API Z3_goal_num_exprs(Z3_context c, Z3_goal g); /** \brief Return \c true if the goal is empty, and it is precise or the product of a under approximation. def_API('Z3_goal_is_decided_sat', BOOL, (_in(CONTEXT), _in(GOAL))) */ bool Z3_API Z3_goal_is_decided_sat(Z3_context c, Z3_goal g); /** \brief Return \c true if the goal contains false, and it is precise or the product of an over approximation. def_API('Z3_goal_is_decided_unsat', BOOL, (_in(CONTEXT), _in(GOAL))) */ bool Z3_API Z3_goal_is_decided_unsat(Z3_context c, Z3_goal g); /** \brief Copy a goal \c g from the context \c source to the context \c target. def_API('Z3_goal_translate', GOAL, (_in(CONTEXT), _in(GOAL), _in(CONTEXT))) */ Z3_goal Z3_API Z3_goal_translate(Z3_context source, Z3_goal g, Z3_context target); /** \brief Convert a model of the formulas of a goal to a model of an original goal. The model may be null, in which case the returned model is valid if the goal was established satisfiable. def_API('Z3_goal_convert_model', MODEL, (_in(CONTEXT), _in(GOAL), _in(MODEL))) */ Z3_model Z3_API Z3_goal_convert_model(Z3_context c, Z3_goal g, Z3_model m); /** \brief Convert a goal into a string. def_API('Z3_goal_to_string', STRING, (_in(CONTEXT), _in(GOAL))) */ Z3_string Z3_API Z3_goal_to_string(Z3_context c, Z3_goal g); /** \brief Convert a goal into a DIMACS formatted string. The goal must be in CNF. You can convert a goal to CNF by applying the tseitin-cnf tactic. Bit-vectors are not automatically converted to Booleans either, so if the caller intends to preserve satisfiability, it should apply bit-blasting tactics. Quantifiers and theory atoms will not be encoded. def_API('Z3_goal_to_dimacs_string', STRING, (_in(CONTEXT), _in(GOAL))) */ Z3_string Z3_API Z3_goal_to_dimacs_string(Z3_context c, Z3_goal g); /*@}*/ /** @name Tactics and Probes */ /*@{*/ /** \brief Return a tactic associated with the given name. The complete list of tactics may be obtained using the procedures #Z3_get_num_tactics and #Z3_get_tactic_name. It may also be obtained using the command \ccode{(help-tactic)} in the SMT 2.0 front-end. Tactics are the basic building block for creating custom solvers for specific problem domains. def_API('Z3_mk_tactic', TACTIC, (_in(CONTEXT), _in(STRING))) */ Z3_tactic Z3_API Z3_mk_tactic(Z3_context c, Z3_string name); /** \brief Increment the reference counter of the given tactic. def_API('Z3_tactic_inc_ref', VOID, (_in(CONTEXT), _in(TACTIC))) */ void Z3_API Z3_tactic_inc_ref(Z3_context c, Z3_tactic t); /** \brief Decrement the reference counter of the given tactic. def_API('Z3_tactic_dec_ref', VOID, (_in(CONTEXT), _in(TACTIC))) */ void Z3_API Z3_tactic_dec_ref(Z3_context c, Z3_tactic g); /** \brief Return a probe associated with the given name. The complete list of probes may be obtained using the procedures #Z3_get_num_probes and #Z3_get_probe_name. It may also be obtained using the command \ccode{(help-tactic)} in the SMT 2.0 front-end. Probes are used to inspect a goal (aka problem) and collect information that may be used to decide which solver and/or preprocessing step will be used. def_API('Z3_mk_probe', PROBE, (_in(CONTEXT), _in(STRING))) */ Z3_probe Z3_API Z3_mk_probe(Z3_context c, Z3_string name); /** \brief Increment the reference counter of the given probe. def_API('Z3_probe_inc_ref', VOID, (_in(CONTEXT), _in(PROBE))) */ void Z3_API Z3_probe_inc_ref(Z3_context c, Z3_probe p); /** \brief Decrement the reference counter of the given probe. def_API('Z3_probe_dec_ref', VOID, (_in(CONTEXT), _in(PROBE))) */ void Z3_API Z3_probe_dec_ref(Z3_context c, Z3_probe p); /** \brief Return a tactic that applies \c t1 to a given goal and \c t2 to every subgoal produced by \c t1. def_API('Z3_tactic_and_then', TACTIC, (_in(CONTEXT), _in(TACTIC), _in(TACTIC))) */ Z3_tactic Z3_API Z3_tactic_and_then(Z3_context c, Z3_tactic t1, Z3_tactic t2); /** \brief Return a tactic that first applies \c t1 to a given goal, if it fails then returns the result of \c t2 applied to the given goal. def_API('Z3_tactic_or_else', TACTIC, (_in(CONTEXT), _in(TACTIC), _in(TACTIC))) */ Z3_tactic Z3_API Z3_tactic_or_else(Z3_context c, Z3_tactic t1, Z3_tactic t2); /** \brief Return a tactic that applies the given tactics in parallel. def_API('Z3_tactic_par_or', TACTIC, (_in(CONTEXT), _in(UINT), _in_array(1, TACTIC))) */ Z3_tactic Z3_API Z3_tactic_par_or(Z3_context c, unsigned num, Z3_tactic const ts[]); /** \brief Return a tactic that applies \c t1 to a given goal and then \c t2 to every subgoal produced by \c t1. The subgoals are processed in parallel. def_API('Z3_tactic_par_and_then', TACTIC, (_in(CONTEXT), _in(TACTIC), _in(TACTIC))) */ Z3_tactic Z3_API Z3_tactic_par_and_then(Z3_context c, Z3_tactic t1, Z3_tactic t2); /** \brief Return a tactic that applies \c t to a given goal for \c ms milliseconds. If \c t does not terminate in \c ms milliseconds, then it fails. def_API('Z3_tactic_try_for', TACTIC, (_in(CONTEXT), _in(TACTIC), _in(UINT))) */ Z3_tactic Z3_API Z3_tactic_try_for(Z3_context c, Z3_tactic t, unsigned ms); /** \brief Return a tactic that applies \c t to a given goal is the probe \c p evaluates to true. If \c p evaluates to false, then the new tactic behaves like the skip tactic. def_API('Z3_tactic_when', TACTIC, (_in(CONTEXT), _in(PROBE), _in(TACTIC))) */ Z3_tactic Z3_API Z3_tactic_when(Z3_context c, Z3_probe p, Z3_tactic t); /** \brief Return a tactic that applies \c t1 to a given goal if the probe \c p evaluates to true, and \c t2 if \c p evaluates to false. def_API('Z3_tactic_cond', TACTIC, (_in(CONTEXT), _in(PROBE), _in(TACTIC), _in(TACTIC))) */ Z3_tactic Z3_API Z3_tactic_cond(Z3_context c, Z3_probe p, Z3_tactic t1, Z3_tactic t2); /** \brief Return a tactic that keeps applying \c t until the goal is not modified anymore or the maximum number of iterations \c max is reached. def_API('Z3_tactic_repeat', TACTIC, (_in(CONTEXT), _in(TACTIC), _in(UINT))) */ Z3_tactic Z3_API Z3_tactic_repeat(Z3_context c, Z3_tactic t, unsigned max); /** \brief Return a tactic that just return the given goal. def_API('Z3_tactic_skip', TACTIC, (_in(CONTEXT),)) */ Z3_tactic Z3_API Z3_tactic_skip(Z3_context c); /** \brief Return a tactic that always fails. def_API('Z3_tactic_fail', TACTIC, (_in(CONTEXT),)) */ Z3_tactic Z3_API Z3_tactic_fail(Z3_context c); /** \brief Return a tactic that fails if the probe \c p evaluates to false. def_API('Z3_tactic_fail_if', TACTIC, (_in(CONTEXT), _in(PROBE))) */ Z3_tactic Z3_API Z3_tactic_fail_if(Z3_context c, Z3_probe p); /** \brief Return a tactic that fails if the goal is not trivially satisfiable (i.e., empty) or trivially unsatisfiable (i.e., contains false). def_API('Z3_tactic_fail_if_not_decided', TACTIC, (_in(CONTEXT),)) */ Z3_tactic Z3_API Z3_tactic_fail_if_not_decided(Z3_context c); /** \brief Return a tactic that applies \c t using the given set of parameters. def_API('Z3_tactic_using_params', TACTIC, (_in(CONTEXT), _in(TACTIC), _in(PARAMS))) */ Z3_tactic Z3_API Z3_tactic_using_params(Z3_context c, Z3_tactic t, Z3_params p); /** \brief Return a probe that always evaluates to val. def_API('Z3_probe_const', PROBE, (_in(CONTEXT), _in(DOUBLE))) */ Z3_probe Z3_API Z3_probe_const(Z3_context x, double val); /** \brief Return a probe that evaluates to "true" when the value returned by \c p1 is less than the value returned by \c p2. \remark For probes, "true" is any value different from 0.0. def_API('Z3_probe_lt', PROBE, (_in(CONTEXT), _in(PROBE), _in(PROBE))) */ Z3_probe Z3_API Z3_probe_lt(Z3_context x, Z3_probe p1, Z3_probe p2); /** \brief Return a probe that evaluates to "true" when the value returned by \c p1 is greater than the value returned by \c p2. \remark For probes, "true" is any value different from 0.0. def_API('Z3_probe_gt', PROBE, (_in(CONTEXT), _in(PROBE), _in(PROBE))) */ Z3_probe Z3_API Z3_probe_gt(Z3_context x, Z3_probe p1, Z3_probe p2); /** \brief Return a probe that evaluates to "true" when the value returned by \c p1 is less than or equal to the value returned by \c p2. \remark For probes, "true" is any value different from 0.0. def_API('Z3_probe_le', PROBE, (_in(CONTEXT), _in(PROBE), _in(PROBE))) */ Z3_probe Z3_API Z3_probe_le(Z3_context x, Z3_probe p1, Z3_probe p2); /** \brief Return a probe that evaluates to "true" when the value returned by \c p1 is greater than or equal to the value returned by \c p2. \remark For probes, "true" is any value different from 0.0. def_API('Z3_probe_ge', PROBE, (_in(CONTEXT), _in(PROBE), _in(PROBE))) */ Z3_probe Z3_API Z3_probe_ge(Z3_context x, Z3_probe p1, Z3_probe p2); /** \brief Return a probe that evaluates to "true" when the value returned by \c p1 is equal to the value returned by \c p2. \remark For probes, "true" is any value different from 0.0. def_API('Z3_probe_eq', PROBE, (_in(CONTEXT), _in(PROBE), _in(PROBE))) */ Z3_probe Z3_API Z3_probe_eq(Z3_context x, Z3_probe p1, Z3_probe p2); /** \brief Return a probe that evaluates to "true" when \c p1 and \c p2 evaluates to true. \remark For probes, "true" is any value different from 0.0. def_API('Z3_probe_and', PROBE, (_in(CONTEXT), _in(PROBE), _in(PROBE))) */ Z3_probe Z3_API Z3_probe_and(Z3_context x, Z3_probe p1, Z3_probe p2); /** \brief Return a probe that evaluates to "true" when \c p1 or \c p2 evaluates to true. \remark For probes, "true" is any value different from 0.0. def_API('Z3_probe_or', PROBE, (_in(CONTEXT), _in(PROBE), _in(PROBE))) */ Z3_probe Z3_API Z3_probe_or(Z3_context x, Z3_probe p1, Z3_probe p2); /** \brief Return a probe that evaluates to "true" when \c p does not evaluate to true. \remark For probes, "true" is any value different from 0.0. def_API('Z3_probe_not', PROBE, (_in(CONTEXT), _in(PROBE))) */ Z3_probe Z3_API Z3_probe_not(Z3_context x, Z3_probe p); /** \brief Return the number of builtin tactics available in Z3. def_API('Z3_get_num_tactics', UINT, (_in(CONTEXT),)) */ unsigned Z3_API Z3_get_num_tactics(Z3_context c); /** \brief Return the name of the idx tactic. \pre i < Z3_get_num_tactics(c) def_API('Z3_get_tactic_name', STRING, (_in(CONTEXT), _in(UINT))) */ Z3_string Z3_API Z3_get_tactic_name(Z3_context c, unsigned i); /** \brief Return the number of builtin probes available in Z3. def_API('Z3_get_num_probes', UINT, (_in(CONTEXT),)) */ unsigned Z3_API Z3_get_num_probes(Z3_context c); /** \brief Return the name of the \c i probe. \pre i < Z3_get_num_probes(c) def_API('Z3_get_probe_name', STRING, (_in(CONTEXT), _in(UINT))) */ Z3_string Z3_API Z3_get_probe_name(Z3_context c, unsigned i); /** \brief Return a string containing a description of parameters accepted by the given tactic. def_API('Z3_tactic_get_help', STRING, (_in(CONTEXT), _in(TACTIC))) */ Z3_string Z3_API Z3_tactic_get_help(Z3_context c, Z3_tactic t); /** \brief Return the parameter description set for the given tactic object. def_API('Z3_tactic_get_param_descrs', PARAM_DESCRS, (_in(CONTEXT), _in(TACTIC))) */ Z3_param_descrs Z3_API Z3_tactic_get_param_descrs(Z3_context c, Z3_tactic t); /** \brief Return a string containing a description of the tactic with the given name. def_API('Z3_tactic_get_descr', STRING, (_in(CONTEXT), _in(STRING))) */ Z3_string Z3_API Z3_tactic_get_descr(Z3_context c, Z3_string name); /** \brief Return a string containing a description of the probe with the given name. def_API('Z3_probe_get_descr', STRING, (_in(CONTEXT), _in(STRING))) */ Z3_string Z3_API Z3_probe_get_descr(Z3_context c, Z3_string name); /** \brief Execute the probe over the goal. The probe always produce a double value. "Boolean" probes return 0.0 for false, and a value different from 0.0 for true. def_API('Z3_probe_apply', DOUBLE, (_in(CONTEXT), _in(PROBE), _in(GOAL))) */ double Z3_API Z3_probe_apply(Z3_context c, Z3_probe p, Z3_goal g); /** \brief Apply tactic \c t to the goal \c g. def_API('Z3_tactic_apply', APPLY_RESULT, (_in(CONTEXT), _in(TACTIC), _in(GOAL))) */ Z3_apply_result Z3_API Z3_tactic_apply(Z3_context c, Z3_tactic t, Z3_goal g); /** \brief Apply tactic \c t to the goal \c g using the parameter set \c p. def_API('Z3_tactic_apply_ex', APPLY_RESULT, (_in(CONTEXT), _in(TACTIC), _in(GOAL), _in(PARAMS))) */ Z3_apply_result Z3_API Z3_tactic_apply_ex(Z3_context c, Z3_tactic t, Z3_goal g, Z3_params p); /** \brief Increment the reference counter of the given \c Z3_apply_result object. def_API('Z3_apply_result_inc_ref', VOID, (_in(CONTEXT), _in(APPLY_RESULT))) */ void Z3_API Z3_apply_result_inc_ref(Z3_context c, Z3_apply_result r); /** \brief Decrement the reference counter of the given \c Z3_apply_result object. def_API('Z3_apply_result_dec_ref', VOID, (_in(CONTEXT), _in(APPLY_RESULT))) */ void Z3_API Z3_apply_result_dec_ref(Z3_context c, Z3_apply_result r); /** \brief Convert the \c Z3_apply_result object returned by #Z3_tactic_apply into a string. def_API('Z3_apply_result_to_string', STRING, (_in(CONTEXT), _in(APPLY_RESULT))) */ Z3_string Z3_API Z3_apply_result_to_string(Z3_context c, Z3_apply_result r); /** \brief Return the number of subgoals in the \c Z3_apply_result object returned by #Z3_tactic_apply. def_API('Z3_apply_result_get_num_subgoals', UINT, (_in(CONTEXT), _in(APPLY_RESULT))) */ unsigned Z3_API Z3_apply_result_get_num_subgoals(Z3_context c, Z3_apply_result r); /** \brief Return one of the subgoals in the \c Z3_apply_result object returned by #Z3_tactic_apply. \pre i < Z3_apply_result_get_num_subgoals(c, r) def_API('Z3_apply_result_get_subgoal', GOAL, (_in(CONTEXT), _in(APPLY_RESULT), _in(UINT))) */ Z3_goal Z3_API Z3_apply_result_get_subgoal(Z3_context c, Z3_apply_result r, unsigned i); /*@}*/ /** @name Solvers*/ /*@{*/ /** \brief Create a new solver. This solver is a "combined solver" (see combined_solver module) that internally uses a non-incremental (solver1) and an incremental solver (solver2). This combined solver changes its behaviour based on how it is used and how its parameters are set. If the solver is used in a non incremental way (i.e. no calls to #Z3_solver_push() or #Z3_solver_pop(), and no calls to #Z3_solver_assert() or #Z3_solver_assert_and_track() after checking satisfiability without an intervening #Z3_solver_reset()) then solver1 will be used. This solver will apply Z3's "default" tactic. The "default" tactic will attempt to probe the logic used by the assertions and will apply a specialized tactic if one is supported. Otherwise the general `(and-then simplify smt)` tactic will be used. If the solver is used in an incremental way then the combined solver will switch to using solver2 (which behaves similarly to the general "smt" tactic). Note however it is possible to set the `solver2_timeout`, `solver2_unknown`, and `ignore_solver1` parameters of the combined solver to change its behaviour. 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. The function #Z3_solver_get_model can also be used even if the result is \c Z3_L_UNDEF, but the returned model is not guaranteed to satisfy quantified assertions. \remark User must use #Z3_solver_inc_ref and #Z3_solver_dec_ref to manage solver objects. Even if the context was created using #Z3_mk_context instead of #Z3_mk_context_rc. def_API('Z3_mk_solver', SOLVER, (_in(CONTEXT),)) */ Z3_solver Z3_API Z3_mk_solver(Z3_context c); /** \brief Create a new incremental solver. This is equivalent to applying the "smt" tactic. Unlike #Z3_mk_solver() this solver - Does not attempt to apply any logic specific tactics. - Does not change its behaviour based on whether it used incrementally/non-incrementally. Note that these differences can result in very different performance compared to #Z3_mk_solver(). 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. The function #Z3_solver_get_model can also be used even if the result is \c Z3_L_UNDEF, but the returned model is not guaranteed to satisfy quantified assertions. \remark User must use #Z3_solver_inc_ref and #Z3_solver_dec_ref to manage solver objects. Even if the context was created using #Z3_mk_context instead of #Z3_mk_context_rc. def_API('Z3_mk_simple_solver', SOLVER, (_in(CONTEXT),)) */ Z3_solver Z3_API Z3_mk_simple_solver(Z3_context c); /** \brief Create a new solver customized for the given logic. It behaves like #Z3_mk_solver if the logic is unknown or unsupported. \remark User must use #Z3_solver_inc_ref and #Z3_solver_dec_ref to manage solver objects. Even if the context was created using #Z3_mk_context instead of #Z3_mk_context_rc. def_API('Z3_mk_solver_for_logic', SOLVER, (_in(CONTEXT), _in(SYMBOL))) */ Z3_solver Z3_API Z3_mk_solver_for_logic(Z3_context c, Z3_symbol logic); /** \brief Create a new solver that is implemented using the given tactic. The solver supports the commands #Z3_solver_push and #Z3_solver_pop, but it will always solve each #Z3_solver_check from scratch. \remark User must use #Z3_solver_inc_ref and #Z3_solver_dec_ref to manage solver objects. Even if the context was created using #Z3_mk_context instead of #Z3_mk_context_rc. def_API('Z3_mk_solver_from_tactic', SOLVER, (_in(CONTEXT), _in(TACTIC))) */ Z3_solver Z3_API Z3_mk_solver_from_tactic(Z3_context c, Z3_tactic t); /** \brief Copy a solver \c s from the context \c source to the context \c target. def_API('Z3_solver_translate', SOLVER, (_in(CONTEXT), _in(SOLVER), _in(CONTEXT))) */ Z3_solver Z3_API Z3_solver_translate(Z3_context source, Z3_solver s, Z3_context target); /** \brief Ad-hoc method for importing model conversion from solver. This method is used for scenarios where \c src has been used to solve a set of formulas and was interrupted. The \c dst solver may be a strengthening of \c src obtained from cubing (assigning a subset of literals or adding constraints over the assertions available in \c src). If \c dst ends up being satisfiable, the model for \c dst may not correspond to a model of the original formula due to inprocessing in \c src. This method is used to take the side-effect of inprocessing into account when returning a model for \c dst. def_API('Z3_solver_import_model_converter', VOID, (_in(CONTEXT), _in(SOLVER), _in(SOLVER))) */ void Z3_API Z3_solver_import_model_converter(Z3_context ctx, Z3_solver src, Z3_solver dst); /** \brief Return a string describing all solver available parameters. \sa Z3_solver_get_param_descrs \sa Z3_solver_set_params def_API('Z3_solver_get_help', STRING, (_in(CONTEXT), _in(SOLVER))) */ Z3_string Z3_API Z3_solver_get_help(Z3_context c, Z3_solver s); /** \brief Return the parameter description set for the given solver object. \sa Z3_solver_get_help \sa Z3_solver_set_params def_API('Z3_solver_get_param_descrs', PARAM_DESCRS, (_in(CONTEXT), _in(SOLVER))) */ Z3_param_descrs Z3_API Z3_solver_get_param_descrs(Z3_context c, Z3_solver s); /** \brief Set the given solver using the given parameters. \sa Z3_solver_get_help \sa Z3_solver_get_param_descrs def_API('Z3_solver_set_params', VOID, (_in(CONTEXT), _in(SOLVER), _in(PARAMS))) */ void Z3_API Z3_solver_set_params(Z3_context c, Z3_solver s, Z3_params p); /** \brief Increment the reference counter of the given solver. def_API('Z3_solver_inc_ref', VOID, (_in(CONTEXT), _in(SOLVER))) */ void Z3_API Z3_solver_inc_ref(Z3_context c, Z3_solver s); /** \brief Decrement the reference counter of the given solver. def_API('Z3_solver_dec_ref', VOID, (_in(CONTEXT), _in(SOLVER))) */ void Z3_API Z3_solver_dec_ref(Z3_context c, Z3_solver s); /** \brief Solver local interrupt. Normally you should use Z3_interrupt to cancel solvers because only one solver is enabled concurrently per context. However, per GitHub issue #1006, there are use cases where it is more convenient to cancel a specific solver. Solvers that are not selected for interrupts are left alone. def_API('Z3_solver_interrupt', VOID, (_in(CONTEXT), _in(SOLVER))) */ void Z3_API Z3_solver_interrupt(Z3_context c, Z3_solver s); /** \brief Create a backtracking point. The solver contains a stack of assertions. \sa Z3_solver_get_num_scopes \sa Z3_solver_pop def_API('Z3_solver_push', VOID, (_in(CONTEXT), _in(SOLVER))) */ void Z3_API Z3_solver_push(Z3_context c, Z3_solver s); /** \brief Backtrack \c n backtracking points. \sa Z3_solver_get_num_scopes \sa Z3_solver_push \pre n <= Z3_solver_get_num_scopes(c, s) def_API('Z3_solver_pop', VOID, (_in(CONTEXT), _in(SOLVER), _in(UINT))) */ void Z3_API Z3_solver_pop(Z3_context c, Z3_solver s, unsigned n); /** \brief Remove all assertions from the solver. \sa Z3_solver_assert \sa Z3_solver_assert_and_track def_API('Z3_solver_reset', VOID, (_in(CONTEXT), _in(SOLVER))) */ void Z3_API Z3_solver_reset(Z3_context c, Z3_solver s); /** \brief Return the number of backtracking points. \sa Z3_solver_push \sa Z3_solver_pop def_API('Z3_solver_get_num_scopes', UINT, (_in(CONTEXT), _in(SOLVER))) */ unsigned Z3_API Z3_solver_get_num_scopes(Z3_context c, Z3_solver s); /** \brief Assert a constraint into the solver. The functions #Z3_solver_check and #Z3_solver_check_assumptions should be used to check whether the logical context is consistent or not. \sa Z3_solver_assert_and_track \sa Z3_solver_reset def_API('Z3_solver_assert', VOID, (_in(CONTEXT), _in(SOLVER), _in(AST))) */ void Z3_API Z3_solver_assert(Z3_context c, Z3_solver s, Z3_ast a); /** \brief Assert a constraint \c a into the solver, and track it (in the unsat) core using the Boolean constant \c p. This API is an alternative to #Z3_solver_check_assumptions for extracting unsat cores. Both APIs can be used in the same solver. The unsat core will contain a combination of the Boolean variables provided using Z3_solver_assert_and_track and the Boolean literals provided using #Z3_solver_check_assumptions. \pre \c a must be a Boolean expression \pre \c p must be a Boolean constant (aka variable). \sa Z3_solver_assert \sa Z3_solver_reset def_API('Z3_solver_assert_and_track', VOID, (_in(CONTEXT), _in(SOLVER), _in(AST), _in(AST))) */ void Z3_API Z3_solver_assert_and_track(Z3_context c, Z3_solver s, Z3_ast a, Z3_ast p); /** \brief load solver assertions from a file. \sa Z3_solver_from_string \sa Z3_solver_to_string def_API('Z3_solver_from_file', VOID, (_in(CONTEXT), _in(SOLVER), _in(STRING))) */ void Z3_API Z3_solver_from_file(Z3_context c, Z3_solver s, Z3_string file_name); /** \brief load solver assertions from a string. \sa Z3_solver_from_file \sa Z3_solver_to_string def_API('Z3_solver_from_string', VOID, (_in(CONTEXT), _in(SOLVER), _in(STRING))) */ void Z3_API Z3_solver_from_string(Z3_context c, Z3_solver s, Z3_string file_name); /** \brief Return the set of asserted formulas on the solver. def_API('Z3_solver_get_assertions', AST_VECTOR, (_in(CONTEXT), _in(SOLVER))) */ Z3_ast_vector Z3_API Z3_solver_get_assertions(Z3_context c, Z3_solver s); /** \brief Return the set of units modulo model conversion. def_API('Z3_solver_get_units', AST_VECTOR, (_in(CONTEXT), _in(SOLVER))) */ 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. def_API('Z3_solver_get_non_units', AST_VECTOR, (_in(CONTEXT), _in(SOLVER))) */ 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 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 \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. The function #Z3_solver_get_proof retrieves a proof if proof generation was enabled when the context was created, and the assertions are unsatisfiable (i.e., the result is \c Z3_L_FALSE). \sa Z3_solver_check_assumptions def_API('Z3_solver_check', INT, (_in(CONTEXT), _in(SOLVER))) */ Z3_lbool Z3_API Z3_solver_check(Z3_context c, Z3_solver s); /** \brief Check whether the assertions in the given solver and optional assumptions are consistent or not. The function #Z3_solver_get_unsat_core retrieves the subset of the assumptions used in the unsatisfiability proof produced by Z3. \sa Z3_solver_check def_API('Z3_solver_check_assumptions', INT, (_in(CONTEXT), _in(SOLVER), _in(UINT), _in_array(2, AST))) */ Z3_lbool Z3_API Z3_solver_check_assumptions(Z3_context c, Z3_solver s, unsigned num_assumptions, Z3_ast const assumptions[]); /** \brief Retrieve congruence class representatives for terms. The function can be used for relying on Z3 to identify equal terms under the current set of assumptions. The array of terms and array of class identifiers should have the same length. The class identifiers are numerals that are assigned to the same value for their corresponding terms if the current context forces the terms to be equal. You cannot deduce that terms corresponding to different numerals must be all different, (especially when using non-convex theories). All implied equalities are returned by this call. This means that two terms map to the same class identifier if and only if 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 \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))) */ Z3_lbool Z3_API Z3_get_implied_equalities(Z3_context c, Z3_solver s, unsigned num_terms, Z3_ast const terms[], unsigned class_ids[]); /** \brief retrieve consequences from solver that determine values of the supplied function symbols. def_API('Z3_solver_get_consequences', INT, (_in(CONTEXT), _in(SOLVER), _in(AST_VECTOR), _in(AST_VECTOR), _in(AST_VECTOR))) */ Z3_lbool Z3_API Z3_solver_get_consequences(Z3_context c, Z3_solver s, Z3_ast_vector assumptions, Z3_ast_vector variables, Z3_ast_vector consequences); /** \brief extract a next cube for a solver. The last cube is the constant \c true or \c false. The number of (non-constant) cubes is by default 1. For the sat solver cubing is controlled using parameters sat.lookahead.cube.cutoff and sat.lookahead.cube.fraction. The third argument is a vector of variables that may be used for cubing. The contents of the vector is only used in the first call. The initial list of variables is used in subsequent calls until it returns the unsatisfiable cube. The vector is modified to contain a set of Autarky variables that occur in clauses that are affected by the (last literal in the) cube. These variables could be used by a different cuber (on a different solver object) for further recursive cubing. The last argument is a backtracking level. It instructs the cube process to backtrack below the indicated level for the next cube. def_API('Z3_solver_cube', AST_VECTOR, (_in(CONTEXT), _in(SOLVER), _in(AST_VECTOR), _in(UINT))) */ Z3_ast_vector Z3_API Z3_solver_cube(Z3_context c, Z3_solver s, Z3_ast_vector vars, unsigned backtrack_level); /** \brief Retrieve the model for the last #Z3_solver_check or #Z3_solver_check_assumptions The error handler is invoked if a model is not available because the commands above were not invoked for the given solver, or if the result was \c Z3_L_FALSE. def_API('Z3_solver_get_model', MODEL, (_in(CONTEXT), _in(SOLVER))) */ Z3_model Z3_API Z3_solver_get_model(Z3_context c, Z3_solver s); /** \brief Retrieve the proof for the last #Z3_solver_check or #Z3_solver_check_assumptions The error handler is invoked if proof generation is not enabled, or if the commands above were not invoked for the given solver, or if the result was different from \c Z3_L_FALSE. def_API('Z3_solver_get_proof', AST, (_in(CONTEXT), _in(SOLVER))) */ Z3_ast Z3_API Z3_solver_get_proof(Z3_context c, Z3_solver s); /** \brief Retrieve the unsat core for the last #Z3_solver_check_assumptions The unsat core is a subset of the assumptions \c a. By default, the unsat core will not be minimized. Generation of a minimized unsat core can be enabled via the `"sat.core.minimize"` and `"smt.core.minimize"` settings for SAT and SMT cores respectively. Generation of minimized unsat cores will be more expensive. def_API('Z3_solver_get_unsat_core', AST_VECTOR, (_in(CONTEXT), _in(SOLVER))) */ 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., \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))) */ Z3_string Z3_API Z3_solver_get_reason_unknown(Z3_context c, Z3_solver s); /** \brief Return statistics for the given solver. \remark User must use #Z3_stats_inc_ref and #Z3_stats_dec_ref to manage Z3_stats objects. def_API('Z3_solver_get_statistics', STATS, (_in(CONTEXT), _in(SOLVER))) */ Z3_stats Z3_API Z3_solver_get_statistics(Z3_context c, Z3_solver s); /** \brief Convert a solver into a string. \sa Z3_solver_from_file \sa Z3_solver_from_string def_API('Z3_solver_to_string', STRING, (_in(CONTEXT), _in(SOLVER))) */ 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 */ /*@{*/ /** \brief Convert a statistics into a string. def_API('Z3_stats_to_string', STRING, (_in(CONTEXT), _in(STATS))) */ Z3_string Z3_API Z3_stats_to_string(Z3_context c, Z3_stats s); /** \brief Increment the reference counter of the given statistics object. def_API('Z3_stats_inc_ref', VOID, (_in(CONTEXT), _in(STATS))) */ void Z3_API Z3_stats_inc_ref(Z3_context c, Z3_stats s); /** \brief Decrement the reference counter of the given statistics object. def_API('Z3_stats_dec_ref', VOID, (_in(CONTEXT), _in(STATS))) */ void Z3_API Z3_stats_dec_ref(Z3_context c, Z3_stats s); /** \brief Return the number of statistical data in \c s. def_API('Z3_stats_size', UINT, (_in(CONTEXT), _in(STATS))) */ unsigned Z3_API Z3_stats_size(Z3_context c, Z3_stats s); /** \brief Return the key (a string) for a particular statistical data. \pre idx < Z3_stats_size(c, s) def_API('Z3_stats_get_key', STRING, (_in(CONTEXT), _in(STATS), _in(UINT))) */ Z3_string Z3_API Z3_stats_get_key(Z3_context c, Z3_stats s, unsigned idx); /** \brief Return \c true if the given statistical data is a unsigned integer. \pre idx < Z3_stats_size(c, s) def_API('Z3_stats_is_uint', BOOL, (_in(CONTEXT), _in(STATS), _in(UINT))) */ bool Z3_API Z3_stats_is_uint(Z3_context c, Z3_stats s, unsigned idx); /** \brief Return \c true if the given statistical data is a double. \pre idx < Z3_stats_size(c, s) def_API('Z3_stats_is_double', BOOL, (_in(CONTEXT), _in(STATS), _in(UINT))) */ bool Z3_API Z3_stats_is_double(Z3_context c, Z3_stats s, unsigned idx); /** \brief Return the unsigned value of the given statistical data. \pre idx < Z3_stats_size(c, s) && Z3_stats_is_uint(c, s) def_API('Z3_stats_get_uint_value', UINT, (_in(CONTEXT), _in(STATS), _in(UINT))) */ unsigned Z3_API Z3_stats_get_uint_value(Z3_context c, Z3_stats s, unsigned idx); /** \brief Return the double value of the given statistical data. \pre idx < Z3_stats_size(c, s) && Z3_stats_is_double(c, s) def_API('Z3_stats_get_double_value', DOUBLE, (_in(CONTEXT), _in(STATS), _in(UINT))) */ double Z3_API Z3_stats_get_double_value(Z3_context c, Z3_stats s, unsigned idx); /** \brief Return the estimated allocated memory in bytes. def_API('Z3_get_estimated_alloc_size', UINT64, ()) */ uint64_t Z3_API Z3_get_estimated_alloc_size(void); /*@}*/ #ifdef __cplusplus } #endif // __cplusplus /*@}*/ #endif z3-z3-4.8.7/src/api/z3_ast_containers.h000066400000000000000000000132251356505360400175730ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: z3_ast_containers.h Abstract: AST Containers Author: Christoph M. Wintersteiger (cwinter) 2015-12-03 Notes: --*/ #ifndef Z3_AST_CONTAINERS_H_ #define Z3_AST_CONTAINERS_H_ #ifdef __cplusplus extern "C" { #endif // __cplusplus /** \defgroup capi C API */ /*@{*/ /** @name AST vectors */ /*@{*/ /** \brief Return an empty AST vector. \remark Reference counting must be used to manage AST vectors, even when the Z3_context was created using #Z3_mk_context instead of #Z3_mk_context_rc. def_API('Z3_mk_ast_vector', AST_VECTOR, (_in(CONTEXT),)) */ Z3_ast_vector Z3_API Z3_mk_ast_vector(Z3_context c); /** \brief Increment the reference counter of the given AST vector. def_API('Z3_ast_vector_inc_ref', VOID, (_in(CONTEXT), _in(AST_VECTOR))) */ void Z3_API Z3_ast_vector_inc_ref(Z3_context c, Z3_ast_vector v); /** \brief Decrement the reference counter of the given AST vector. def_API('Z3_ast_vector_dec_ref', VOID, (_in(CONTEXT), _in(AST_VECTOR))) */ void Z3_API Z3_ast_vector_dec_ref(Z3_context c, Z3_ast_vector v); /** \brief Return the size of the given AST vector. def_API('Z3_ast_vector_size', UINT, (_in(CONTEXT), _in(AST_VECTOR))) */ unsigned Z3_API Z3_ast_vector_size(Z3_context c, Z3_ast_vector v); /** \brief Return the AST at position \c i in the AST vector \c v. \pre i < Z3_ast_vector_size(c, v) def_API('Z3_ast_vector_get', AST, (_in(CONTEXT), _in(AST_VECTOR), _in(UINT))) */ Z3_ast Z3_API Z3_ast_vector_get(Z3_context c, Z3_ast_vector v, unsigned i); /** \brief Update position \c i of the AST vector \c v with the AST \c a. \pre i < Z3_ast_vector_size(c, v) def_API('Z3_ast_vector_set', VOID, (_in(CONTEXT), _in(AST_VECTOR), _in(UINT), _in(AST))) */ void Z3_API Z3_ast_vector_set(Z3_context c, Z3_ast_vector v, unsigned i, Z3_ast a); /** \brief Resize the AST vector \c v. def_API('Z3_ast_vector_resize', VOID, (_in(CONTEXT), _in(AST_VECTOR), _in(UINT))) */ void Z3_API Z3_ast_vector_resize(Z3_context c, Z3_ast_vector v, unsigned n); /** \brief Add the AST \c a in the end of the AST vector \c v. The size of \c v is increased by one. def_API('Z3_ast_vector_push', VOID, (_in(CONTEXT), _in(AST_VECTOR), _in(AST))) */ void Z3_API Z3_ast_vector_push(Z3_context c, Z3_ast_vector v, Z3_ast a); /** \brief Translate the AST vector \c v from context \c s into an AST vector in context \c t. def_API('Z3_ast_vector_translate', AST_VECTOR, (_in(CONTEXT), _in(AST_VECTOR), _in(CONTEXT))) */ Z3_ast_vector Z3_API Z3_ast_vector_translate(Z3_context s, Z3_ast_vector v, Z3_context t); /** \brief Convert AST vector into a string. def_API('Z3_ast_vector_to_string', STRING, (_in(CONTEXT), _in(AST_VECTOR))) */ Z3_string Z3_API Z3_ast_vector_to_string(Z3_context c, Z3_ast_vector v); /*@}*/ /** @name AST maps */ /*@{*/ /** \brief Return an empty mapping from AST to AST \remark Reference counting must be used to manage AST maps, even when the Z3_context was created using #Z3_mk_context instead of #Z3_mk_context_rc. def_API('Z3_mk_ast_map', AST_MAP, (_in(CONTEXT),) ) */ Z3_ast_map Z3_API Z3_mk_ast_map(Z3_context c); /** \brief Increment the reference counter of the given AST map. def_API('Z3_ast_map_inc_ref', VOID, (_in(CONTEXT), _in(AST_MAP))) */ void Z3_API Z3_ast_map_inc_ref(Z3_context c, Z3_ast_map m); /** \brief Decrement the reference counter of the given AST map. def_API('Z3_ast_map_dec_ref', VOID, (_in(CONTEXT), _in(AST_MAP))) */ void Z3_API Z3_ast_map_dec_ref(Z3_context c, Z3_ast_map m); /** \brief Return true if the map \c m contains the AST key \c k. def_API('Z3_ast_map_contains', BOOL, (_in(CONTEXT), _in(AST_MAP), _in(AST))) */ bool Z3_API Z3_ast_map_contains(Z3_context c, Z3_ast_map m, Z3_ast k); /** \brief Return the value associated with the key \c k. The procedure invokes the error handler if \c k is not in the map. def_API('Z3_ast_map_find', AST, (_in(CONTEXT), _in(AST_MAP), _in(AST))) */ Z3_ast Z3_API Z3_ast_map_find(Z3_context c, Z3_ast_map m, Z3_ast k); /** \brief Store/Replace a new key, value pair in the given map. def_API('Z3_ast_map_insert', VOID, (_in(CONTEXT), _in(AST_MAP), _in(AST), _in(AST))) */ void Z3_API Z3_ast_map_insert(Z3_context c, Z3_ast_map m, Z3_ast k, Z3_ast v); /** \brief Erase a key from the map. def_API('Z3_ast_map_erase', VOID, (_in(CONTEXT), _in(AST_MAP), _in(AST))) */ void Z3_API Z3_ast_map_erase(Z3_context c, Z3_ast_map m, Z3_ast k); /** \brief Remove all keys from the given map. def_API('Z3_ast_map_reset', VOID, (_in(CONTEXT), _in(AST_MAP))) */ void Z3_API Z3_ast_map_reset(Z3_context c, Z3_ast_map m); /** \brief Return the size of the given map. def_API('Z3_ast_map_size', UINT, (_in(CONTEXT), _in(AST_MAP))) */ unsigned Z3_API Z3_ast_map_size(Z3_context c, Z3_ast_map m); /** \brief Return the keys stored in the given map. def_API('Z3_ast_map_keys', AST_VECTOR, (_in(CONTEXT), _in(AST_MAP))) */ Z3_ast_vector Z3_API Z3_ast_map_keys(Z3_context c, Z3_ast_map m); /** \brief Convert the given map into a string. def_API('Z3_ast_map_to_string', STRING, (_in(CONTEXT), _in(AST_MAP))) */ Z3_string Z3_API Z3_ast_map_to_string(Z3_context c, Z3_ast_map m); /*@}*/ /*@}*/ #ifdef __cplusplus } #endif // __cplusplus #endif z3-z3-4.8.7/src/api/z3_fixedpoint.h000066400000000000000000000335171356505360400167360ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: z3_fixedpoint.h Abstract: Fixedpoint API Author: Christoph M. Wintersteiger (cwinter) 2015-12-03 Notes: --*/ #ifndef Z3_FIXEDPOINT_H_ #define Z3_FIXEDPOINT_H_ #ifdef __cplusplus extern "C" { #endif // __cplusplus /** \defgroup capi C API */ /*@{*/ /** @name Fixedpoint facilities */ /*@{*/ /** \brief Create a new fixedpoint context. \remark User must use #Z3_fixedpoint_inc_ref and #Z3_fixedpoint_dec_ref to manage fixedpoint objects. Even if the context was created using #Z3_mk_context instead of #Z3_mk_context_rc. def_API('Z3_mk_fixedpoint', FIXEDPOINT, (_in(CONTEXT), )) */ Z3_fixedpoint Z3_API Z3_mk_fixedpoint(Z3_context c); /** \brief Increment the reference counter of the given fixedpoint context def_API('Z3_fixedpoint_inc_ref', VOID, (_in(CONTEXT), _in(FIXEDPOINT))) */ void Z3_API Z3_fixedpoint_inc_ref(Z3_context c, Z3_fixedpoint d); /** \brief Decrement the reference counter of the given fixedpoint context. def_API('Z3_fixedpoint_dec_ref', VOID, (_in(CONTEXT), _in(FIXEDPOINT))) */ void Z3_API Z3_fixedpoint_dec_ref(Z3_context c, Z3_fixedpoint d); /** \brief Add a universal Horn clause as a named rule. The \c horn_rule should be of the form: \code horn_rule ::= (forall (bound-vars) horn_rule) | (=> atoms horn_rule) | atom \endcode def_API('Z3_fixedpoint_add_rule', VOID, (_in(CONTEXT), _in(FIXEDPOINT), _in(AST), _in(SYMBOL))) */ void Z3_API Z3_fixedpoint_add_rule(Z3_context c, Z3_fixedpoint d, Z3_ast rule, Z3_symbol name); /** \brief Add a Database fact. \param c - context \param d - fixed point context \param r - relation signature for the row. \param num_args - number of columns for the given row. \param args - array of the row elements. The number of arguments \c num_args should be equal to the number of sorts in the domain of \c r. Each sort in the domain should be an integral (bit-vector, Boolean or or finite domain sort). The call has the same effect as adding a rule where \c r is applied to the arguments. def_API('Z3_fixedpoint_add_fact', VOID, (_in(CONTEXT), _in(FIXEDPOINT), _in(FUNC_DECL), _in(UINT), _in_array(3, UINT))) */ void Z3_API Z3_fixedpoint_add_fact(Z3_context c, Z3_fixedpoint d, Z3_func_decl r, unsigned num_args, unsigned args[]); /** \brief Assert a constraint to the fixedpoint context. The constraints are used as background axioms when the fixedpoint engine uses the PDR mode. They are ignored for standard Datalog mode. def_API('Z3_fixedpoint_assert', VOID, (_in(CONTEXT), _in(FIXEDPOINT), _in(AST))) */ void Z3_API Z3_fixedpoint_assert(Z3_context c, Z3_fixedpoint d, Z3_ast axiom); /** \brief Pose a query against the asserted rules. \code query ::= (exists (bound-vars) query) | literals \endcode query returns - \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))) */ Z3_lbool Z3_API Z3_fixedpoint_query(Z3_context c, Z3_fixedpoint d, Z3_ast query); /** \brief Pose multiple queries against the asserted rules. The queries are encoded as relations (function declarations). query returns - \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))) */ Z3_lbool Z3_API Z3_fixedpoint_query_relations( Z3_context c, Z3_fixedpoint d, unsigned num_relations, Z3_func_decl const relations[]); /** \brief Retrieve a formula that encodes satisfying answers to the query. When used in Datalog mode, the returned answer is a disjunction of conjuncts. 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 \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))) */ Z3_ast Z3_API Z3_fixedpoint_get_answer(Z3_context c, Z3_fixedpoint d); /** \brief Retrieve a string that describes the last status returned by #Z3_fixedpoint_query. Use this method when #Z3_fixedpoint_query returns \c Z3_L_UNDEF. def_API('Z3_fixedpoint_get_reason_unknown', STRING, (_in(CONTEXT), _in(FIXEDPOINT) )) */ Z3_string Z3_API Z3_fixedpoint_get_reason_unknown(Z3_context c, Z3_fixedpoint d); /** \brief Update a named rule. A rule with the same name must have been previously created. def_API('Z3_fixedpoint_update_rule', VOID, (_in(CONTEXT), _in(FIXEDPOINT), _in(AST), _in(SYMBOL))) */ void Z3_API Z3_fixedpoint_update_rule(Z3_context c, Z3_fixedpoint d, Z3_ast a, Z3_symbol name); /** \brief Query the PDR engine for the maximal levels properties are known about predicate. This call retrieves the maximal number of relevant unfoldings of \c pred with respect to the current exploration state. Note: this functionality is PDR specific. def_API('Z3_fixedpoint_get_num_levels', UINT, (_in(CONTEXT), _in(FIXEDPOINT), _in(FUNC_DECL))) */ unsigned Z3_API Z3_fixedpoint_get_num_levels(Z3_context c, Z3_fixedpoint d, Z3_func_decl pred); /** Retrieve the current cover of \c pred up to \c level unfoldings. Return just the delta that is known at \c level. To obtain the full set of properties of \c pred one should query at \c level+1 , \c level+2 etc, and include \c level=-1. Note: this functionality is PDR specific. def_API('Z3_fixedpoint_get_cover_delta', AST, (_in(CONTEXT), _in(FIXEDPOINT), _in(INT), _in(FUNC_DECL))) */ Z3_ast Z3_API Z3_fixedpoint_get_cover_delta(Z3_context c, Z3_fixedpoint d, int level, Z3_func_decl pred); /** \brief Add property about the predicate \c pred. Add a property of predicate \c pred at \c level. It gets pushed forward when possible. Note: level = -1 is treated as the fixedpoint. So passing -1 for the \c level means that the property is true of the fixed-point unfolding with respect to \c pred. Note: this functionality is PDR specific. def_API('Z3_fixedpoint_add_cover', VOID, (_in(CONTEXT), _in(FIXEDPOINT), _in(INT), _in(FUNC_DECL), _in(AST))) */ void Z3_API Z3_fixedpoint_add_cover(Z3_context c, Z3_fixedpoint d, int level, Z3_func_decl pred, Z3_ast property); /** \brief Retrieve statistics information from the last call to #Z3_fixedpoint_query. def_API('Z3_fixedpoint_get_statistics', STATS, (_in(CONTEXT), _in(FIXEDPOINT))) */ Z3_stats Z3_API Z3_fixedpoint_get_statistics(Z3_context c, Z3_fixedpoint d); /** \brief Register relation as Fixedpoint defined. Fixedpoint defined relations have least-fixedpoint semantics. For example, the relation is empty if it does not occur in a head or a fact. def_API('Z3_fixedpoint_register_relation', VOID, (_in(CONTEXT), _in(FIXEDPOINT), _in(FUNC_DECL))) */ void Z3_API Z3_fixedpoint_register_relation(Z3_context c, Z3_fixedpoint d, Z3_func_decl f); /** \brief Configure the predicate representation. It sets the predicate to use a set of domains given by the list of symbols. The domains given by the list of symbols must belong to a set of built-in domains. def_API('Z3_fixedpoint_set_predicate_representation', VOID, (_in(CONTEXT), _in(FIXEDPOINT), _in(FUNC_DECL), _in(UINT), _in_array(3, SYMBOL))) */ void Z3_API Z3_fixedpoint_set_predicate_representation( Z3_context c, Z3_fixedpoint d, Z3_func_decl f, unsigned num_relations, Z3_symbol const relation_kinds[]); /** \brief Retrieve set of rules from fixedpoint context. def_API('Z3_fixedpoint_get_rules', AST_VECTOR, (_in(CONTEXT),_in(FIXEDPOINT))) */ Z3_ast_vector Z3_API Z3_fixedpoint_get_rules( Z3_context c, Z3_fixedpoint f); /** \brief Retrieve set of background assertions from fixedpoint context. def_API('Z3_fixedpoint_get_assertions', AST_VECTOR, (_in(CONTEXT),_in(FIXEDPOINT))) */ Z3_ast_vector Z3_API Z3_fixedpoint_get_assertions( Z3_context c, Z3_fixedpoint f); /** \brief Set parameters on fixedpoint context. \sa Z3_fixedpoint_get_help \sa Z3_fixedpoint_get_param_descrs def_API('Z3_fixedpoint_set_params', VOID, (_in(CONTEXT), _in(FIXEDPOINT), _in(PARAMS))) */ void Z3_API Z3_fixedpoint_set_params(Z3_context c, Z3_fixedpoint f, Z3_params p); /** \brief Return a string describing all fixedpoint available parameters. \sa Z3_fixedpoint_get_param_descrs \sa Z3_fixedpoint_set_params def_API('Z3_fixedpoint_get_help', STRING, (_in(CONTEXT), _in(FIXEDPOINT))) */ Z3_string Z3_API Z3_fixedpoint_get_help(Z3_context c, Z3_fixedpoint f); /** \brief Return the parameter description set for the given fixedpoint object. \sa Z3_fixedpoint_get_help \sa Z3_fixedpoint_set_params def_API('Z3_fixedpoint_get_param_descrs', PARAM_DESCRS, (_in(CONTEXT), _in(FIXEDPOINT))) */ Z3_param_descrs Z3_API Z3_fixedpoint_get_param_descrs(Z3_context c, Z3_fixedpoint f); /** \brief Print the current rules and background axioms as a string. \param c - context. \param f - fixedpoint context. \param num_queries - number of additional queries to print. \param queries - additional queries. \sa Z3_fixedpoint_from_file \sa Z3_fixedpoint_from_string def_API('Z3_fixedpoint_to_string', STRING, (_in(CONTEXT), _in(FIXEDPOINT), _in(UINT), _in_array(2, AST))) */ Z3_string Z3_API Z3_fixedpoint_to_string( Z3_context c, Z3_fixedpoint f, unsigned num_queries, Z3_ast queries[]); /** \brief Parse an SMT-LIB2 string with fixedpoint rules. Add the rules to the current fixedpoint context. Return the set of queries in the string. \param c - context. \param f - fixedpoint context. \param s - string containing SMT2 specification. \sa Z3_fixedpoint_from_file \sa Z3_fixedpoint_to_string def_API('Z3_fixedpoint_from_string', AST_VECTOR, (_in(CONTEXT), _in(FIXEDPOINT), _in(STRING))) */ Z3_ast_vector Z3_API Z3_fixedpoint_from_string(Z3_context c, Z3_fixedpoint f, Z3_string s); /** \brief Parse an SMT-LIB2 file with fixedpoint rules. Add the rules to the current fixedpoint context. Return the set of queries in the file. \param c - context. \param f - fixedpoint context. \param s - path to file containing SMT2 specification. \sa Z3_fixedpoint_from_string \sa Z3_fixedpoint_to_string def_API('Z3_fixedpoint_from_file', AST_VECTOR, (_in(CONTEXT), _in(FIXEDPOINT), _in(STRING))) */ Z3_ast_vector Z3_API Z3_fixedpoint_from_file(Z3_context c, Z3_fixedpoint f, Z3_string s); /** \brief The following utilities allows adding user-defined domains. */ typedef void Z3_fixedpoint_reduce_assign_callback_fptr( void*, Z3_func_decl, unsigned, Z3_ast const [], unsigned, Z3_ast const []); typedef void Z3_fixedpoint_reduce_app_callback_fptr( void*, Z3_func_decl, unsigned, Z3_ast const [], Z3_ast*); /** \brief Initialize the context with a user-defined state. */ void Z3_API Z3_fixedpoint_init(Z3_context c, Z3_fixedpoint d, void* state); /** \brief Register a callback to destructive updates. Registers are identified with terms encoded as fresh constants, */ void Z3_API Z3_fixedpoint_set_reduce_assign_callback( Z3_context c ,Z3_fixedpoint d, Z3_fixedpoint_reduce_assign_callback_fptr cb); /** \brief Register a callback for building terms based on the relational operators. */ void Z3_API Z3_fixedpoint_set_reduce_app_callback( Z3_context c, Z3_fixedpoint d, Z3_fixedpoint_reduce_app_callback_fptr cb); typedef void (*Z3_fixedpoint_new_lemma_eh)(void *state, Z3_ast lemma, unsigned level); typedef void (*Z3_fixedpoint_predecessor_eh)(void *state); typedef void (*Z3_fixedpoint_unfold_eh)(void *state); /** \brief set export callback for lemmas */ void Z3_API Z3_fixedpoint_add_callback(Z3_context ctx, Z3_fixedpoint f, void *state, Z3_fixedpoint_new_lemma_eh new_lemma_eh, Z3_fixedpoint_predecessor_eh predecessor_eh, Z3_fixedpoint_unfold_eh unfold_eh); void Z3_API Z3_fixedpoint_add_constraint (Z3_context c, Z3_fixedpoint d, Z3_ast e, unsigned lvl); /*@}*/ /*@}*/ #ifdef __cplusplus } #endif // __cplusplus #endif z3-z3-4.8.7/src/api/z3_fpa.h000066400000000000000000001062761356505360400153360ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: z3_fpa.h Abstract: Additional APIs for floating-point arithmetic (FP). Author: Christoph M. Wintersteiger (cwinter) 2013-06-05 Notes: --*/ #ifndef Z3_FPA_H_ #define Z3_FPA_H_ #ifdef __cplusplus extern "C" { #endif // __cplusplus /** \defgroup capi C API */ /*@{*/ /** @name Floating-Point Arithmetic */ /*@{*/ /** \brief Create the RoundingMode sort. \param c logical context def_API('Z3_mk_fpa_rounding_mode_sort', SORT, (_in(CONTEXT),)) */ Z3_sort Z3_API Z3_mk_fpa_rounding_mode_sort(Z3_context c); /** \brief Create a numeral of RoundingMode sort which represents the NearestTiesToEven rounding mode. \param c logical context def_API('Z3_mk_fpa_round_nearest_ties_to_even', AST, (_in(CONTEXT),)) */ Z3_ast Z3_API Z3_mk_fpa_round_nearest_ties_to_even(Z3_context c); /** \brief Create a numeral of RoundingMode sort which represents the NearestTiesToEven rounding mode. \param c logical context def_API('Z3_mk_fpa_rne', AST, (_in(CONTEXT),)) */ Z3_ast Z3_API Z3_mk_fpa_rne(Z3_context c); /** \brief Create a numeral of RoundingMode sort which represents the NearestTiesToAway rounding mode. \param c logical context def_API('Z3_mk_fpa_round_nearest_ties_to_away', AST, (_in(CONTEXT),)) */ Z3_ast Z3_API Z3_mk_fpa_round_nearest_ties_to_away(Z3_context c); /** \brief Create a numeral of RoundingMode sort which represents the NearestTiesToAway rounding mode. \param c logical context def_API('Z3_mk_fpa_rna', AST, (_in(CONTEXT),)) */ Z3_ast Z3_API Z3_mk_fpa_rna(Z3_context c); /** \brief Create a numeral of RoundingMode sort which represents the TowardPositive rounding mode. \param c logical context def_API('Z3_mk_fpa_round_toward_positive', AST, (_in(CONTEXT),)) */ Z3_ast Z3_API Z3_mk_fpa_round_toward_positive(Z3_context c); /** \brief Create a numeral of RoundingMode sort which represents the TowardPositive rounding mode. \param c logical context def_API('Z3_mk_fpa_rtp', AST, (_in(CONTEXT),)) */ Z3_ast Z3_API Z3_mk_fpa_rtp(Z3_context c); /** \brief Create a numeral of RoundingMode sort which represents the TowardNegative rounding mode. \param c logical context def_API('Z3_mk_fpa_round_toward_negative', AST, (_in(CONTEXT),)) */ Z3_ast Z3_API Z3_mk_fpa_round_toward_negative(Z3_context c); /** \brief Create a numeral of RoundingMode sort which represents the TowardNegative rounding mode. \param c logical context def_API('Z3_mk_fpa_rtn', AST, (_in(CONTEXT),)) */ Z3_ast Z3_API Z3_mk_fpa_rtn(Z3_context c); /** \brief Create a numeral of RoundingMode sort which represents the TowardZero rounding mode. \param c logical context def_API('Z3_mk_fpa_round_toward_zero', AST, (_in(CONTEXT),)) */ Z3_ast Z3_API Z3_mk_fpa_round_toward_zero(Z3_context c); /** \brief Create a numeral of RoundingMode sort which represents the TowardZero rounding mode. \param c logical context def_API('Z3_mk_fpa_rtz', AST, (_in(CONTEXT),)) */ Z3_ast Z3_API Z3_mk_fpa_rtz(Z3_context c); /** \brief Create a FloatingPoint sort. \param c logical context \param ebits number of exponent bits \param sbits number of significand bits \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))) */ Z3_sort Z3_API Z3_mk_fpa_sort(Z3_context c, unsigned ebits, unsigned sbits); /** \brief Create the half-precision (16-bit) FloatingPoint sort. \param c logical context def_API('Z3_mk_fpa_sort_half', SORT, (_in(CONTEXT),)) */ Z3_sort Z3_API Z3_mk_fpa_sort_half(Z3_context c); /** \brief Create the half-precision (16-bit) FloatingPoint sort. \param c logical context def_API('Z3_mk_fpa_sort_16', SORT, (_in(CONTEXT),)) */ Z3_sort Z3_API Z3_mk_fpa_sort_16(Z3_context c); /** \brief Create the single-precision (32-bit) FloatingPoint sort. \param c logical context. def_API('Z3_mk_fpa_sort_single', SORT, (_in(CONTEXT),)) */ Z3_sort Z3_API Z3_mk_fpa_sort_single(Z3_context c); /** \brief Create the single-precision (32-bit) FloatingPoint sort. \param c logical context def_API('Z3_mk_fpa_sort_32', SORT, (_in(CONTEXT),)) */ Z3_sort Z3_API Z3_mk_fpa_sort_32(Z3_context c); /** \brief Create the double-precision (64-bit) FloatingPoint sort. \param c logical context def_API('Z3_mk_fpa_sort_double', SORT, (_in(CONTEXT),)) */ Z3_sort Z3_API Z3_mk_fpa_sort_double(Z3_context c); /** \brief Create the double-precision (64-bit) FloatingPoint sort. \param c logical context def_API('Z3_mk_fpa_sort_64', SORT, (_in(CONTEXT),)) */ Z3_sort Z3_API Z3_mk_fpa_sort_64(Z3_context c); /** \brief Create the quadruple-precision (128-bit) FloatingPoint sort. \param c logical context def_API('Z3_mk_fpa_sort_quadruple', SORT, (_in(CONTEXT),)) */ Z3_sort Z3_API Z3_mk_fpa_sort_quadruple(Z3_context c); /** \brief Create the quadruple-precision (128-bit) FloatingPoint sort. \param c logical context def_API('Z3_mk_fpa_sort_128', SORT, (_in(CONTEXT),)) */ Z3_sort Z3_API Z3_mk_fpa_sort_128(Z3_context c); /** \brief Create a floating-point NaN of sort \c s. \param c logical context \param s target sort \sa Z3_mk_fpa_inf \sa Z3_mk_fpa_zero def_API('Z3_mk_fpa_nan', AST, (_in(CONTEXT),_in(SORT))) */ Z3_ast Z3_API Z3_mk_fpa_nan(Z3_context c, Z3_sort s); /** \brief Create a floating-point infinity of sort \c s. \param c logical context \param s target sort \param negative indicates whether the result should be negative When \c negative is \c true, -oo will be generated instead of +oo. \sa Z3_mk_fpa_nan \sa Z3_mk_fpa_zero def_API('Z3_mk_fpa_inf', AST, (_in(CONTEXT),_in(SORT),_in(BOOL))) */ Z3_ast Z3_API Z3_mk_fpa_inf(Z3_context c, Z3_sort s, bool negative); /** \brief Create a floating-point zero of sort \c s. \param c logical context \param s target sort \param negative indicates whether the result should be negative When \c negative is \c true, -zero will be generated instead of +zero. \sa Z3_mk_fpa_inf \sa Z3_mk_fpa_nan def_API('Z3_mk_fpa_zero', AST, (_in(CONTEXT),_in(SORT),_in(BOOL))) */ Z3_ast Z3_API Z3_mk_fpa_zero(Z3_context c, Z3_sort s, bool negative); /** \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 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. \param c logical context \param sgn sign \param exp exponent \param sig significand \sa Z3_mk_fpa_numeral_double \sa Z3_mk_fpa_numeral_float \sa Z3_mk_fpa_numeral_int \sa Z3_mk_fpa_numeral_int_uint \sa Z3_mk_fpa_numeral_int64_uint64 \sa Z3_mk_numeral def_API('Z3_mk_fpa_fp', AST, (_in(CONTEXT), _in(AST), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_fpa_fp(Z3_context c, Z3_ast sgn, Z3_ast exp, Z3_ast sig); /** \brief Create a numeral of FloatingPoint sort from a float. This function is used to create numerals that fit in a float value. It is slightly faster than #Z3_mk_numeral since it is not necessary to parse a string. \param c logical context \param v value \param ty sort \c ty must be a FloatingPoint sort \sa Z3_mk_fpa_fp \sa Z3_mk_fpa_numeral_double \sa Z3_mk_fpa_numeral_int \sa Z3_mk_fpa_numeral_int_uint \sa Z3_mk_fpa_numeral_int64_uint64 \sa Z3_mk_numeral def_API('Z3_mk_fpa_numeral_float', AST, (_in(CONTEXT), _in(FLOAT), _in(SORT))) */ Z3_ast Z3_API Z3_mk_fpa_numeral_float(Z3_context c, float v, Z3_sort ty); /** \brief Create a numeral of FloatingPoint sort from a double. This function is used to create numerals that fit in a double value. It is slightly faster than #Z3_mk_numeral since it is not necessary to parse a string. \param c logical context \param v value \param ty sort \c ty must be a FloatingPoint sort \sa Z3_mk_fpa_fp \sa Z3_mk_fpa_numeral_float \sa Z3_mk_fpa_numeral_int \sa Z3_mk_fpa_numeral_int_uint \sa Z3_mk_fpa_numeral_int64_uint64 \sa Z3_mk_numeral def_API('Z3_mk_fpa_numeral_double', AST, (_in(CONTEXT), _in(DOUBLE), _in(SORT))) */ Z3_ast Z3_API Z3_mk_fpa_numeral_double(Z3_context c, double v, Z3_sort ty); /** \brief Create a numeral of FloatingPoint sort from a signed integer. \param c logical context \param v value \param ty result sort \c ty must be a FloatingPoint sort \sa Z3_mk_fpa_fp \sa Z3_mk_fpa_numeral_double \sa Z3_mk_fpa_numeral_float \sa Z3_mk_fpa_numeral_int_uint \sa Z3_mk_fpa_numeral_int64_uint64 \sa Z3_mk_numeral def_API('Z3_mk_fpa_numeral_int', AST, (_in(CONTEXT), _in(INT), _in(SORT))) */ Z3_ast Z3_API Z3_mk_fpa_numeral_int(Z3_context c, signed v, Z3_sort ty); /** \brief Create a numeral of FloatingPoint sort from a sign bit and two integers. \param c logical context \param sgn sign bit (true == negative) \param sig significand \param exp exponent \param ty result sort \c ty must be a FloatingPoint sort \sa Z3_mk_fpa_fp \sa Z3_mk_fpa_numeral_double \sa Z3_mk_fpa_numeral_float \sa Z3_mk_fpa_numeral_int \sa Z3_mk_fpa_numeral_int64_uint64 \sa Z3_mk_numeral def_API('Z3_mk_fpa_numeral_int_uint', AST, (_in(CONTEXT), _in(BOOL), _in(INT), _in(UINT), _in(SORT))) */ Z3_ast Z3_API Z3_mk_fpa_numeral_int_uint(Z3_context c, bool sgn, signed exp, unsigned sig, Z3_sort ty); /** \brief Create a numeral of FloatingPoint sort from a sign bit and two 64-bit integers. \param c logical context \param sgn sign bit (true == negative) \param sig significand \param exp exponent \param ty result sort \c ty must be a FloatingPoint sort \sa Z3_mk_fpa_fp \sa Z3_mk_fpa_numeral_double \sa Z3_mk_fpa_numeral_float \sa Z3_mk_fpa_numeral_int \sa Z3_mk_fpa_numeral_int_uint \sa Z3_mk_numeral def_API('Z3_mk_fpa_numeral_int64_uint64', AST, (_in(CONTEXT), _in(BOOL), _in(INT64), _in(UINT64), _in(SORT))) */ Z3_ast Z3_API Z3_mk_fpa_numeral_int64_uint64(Z3_context c, bool sgn, int64_t exp, uint64_t sig, Z3_sort ty); /** \brief Floating-point absolute value \param c logical context \param t term of FloatingPoint sort def_API('Z3_mk_fpa_abs', AST, (_in(CONTEXT),_in(AST))) */ Z3_ast Z3_API Z3_mk_fpa_abs(Z3_context c, Z3_ast t); /** \brief Floating-point negation \param c logical context \param t term of FloatingPoint sort def_API('Z3_mk_fpa_neg', AST, (_in(CONTEXT),_in(AST))) */ Z3_ast Z3_API Z3_mk_fpa_neg(Z3_context c, Z3_ast t); /** \brief Floating-point addition \param c logical context \param rm term of RoundingMode sort \param t1 term of FloatingPoint sort \param t2 term of 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))) */ Z3_ast Z3_API Z3_mk_fpa_add(Z3_context c, Z3_ast rm, Z3_ast t1, Z3_ast t2); /** \brief Floating-point subtraction \param c logical context \param rm term of RoundingMode sort \param t1 term of FloatingPoint sort \param t2 term of 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))) */ Z3_ast Z3_API Z3_mk_fpa_sub(Z3_context c, Z3_ast rm, Z3_ast t1, Z3_ast t2); /** \brief Floating-point multiplication \param c logical context \param rm term of RoundingMode sort \param t1 term of FloatingPoint sort \param t2 term of 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))) */ Z3_ast Z3_API Z3_mk_fpa_mul(Z3_context c, Z3_ast rm, Z3_ast t1, Z3_ast t2); /** \brief Floating-point division \param c logical context \param rm term of RoundingMode sort \param t1 term of FloatingPoint sort. \param t2 term of 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))) */ Z3_ast Z3_API Z3_mk_fpa_div(Z3_context c, Z3_ast rm, Z3_ast t1, Z3_ast t2); /** \brief Floating-point fused multiply-add. \param c logical context \param rm term of RoundingMode sort \param t1 term of FloatingPoint sort \param t2 term of FloatingPoint sort \param t3 term of FloatingPoint sort The result is \ccode{round((t1 * t2) + t3)}. \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))) */ Z3_ast Z3_API Z3_mk_fpa_fma(Z3_context c, Z3_ast rm, Z3_ast t1, Z3_ast t2, Z3_ast t3); /** \brief Floating-point square root \param c logical context \param rm term of RoundingMode sort \param t term of 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))) */ Z3_ast Z3_API Z3_mk_fpa_sqrt(Z3_context c, Z3_ast rm, Z3_ast t); /** \brief Floating-point remainder \param c logical context \param t1 term of FloatingPoint sort \param t2 term of 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))) */ Z3_ast Z3_API Z3_mk_fpa_rem(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Floating-point roundToIntegral. Rounds a floating-point number to the closest integer, again represented as a floating-point number. \param c logical context \param rm term of RoundingMode sort \param t term 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))) */ Z3_ast Z3_API Z3_mk_fpa_round_to_integral(Z3_context c, Z3_ast rm, Z3_ast t); /** \brief Minimum of floating-point numbers. \param c logical context \param t1 term of FloatingPoint sort \param t2 term of 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))) */ Z3_ast Z3_API Z3_mk_fpa_min(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Maximum of floating-point numbers. \param c logical context \param t1 term of FloatingPoint sort \param t2 term of 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))) */ Z3_ast Z3_API Z3_mk_fpa_max(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Floating-point less than or equal. \param c logical context \param t1 term of FloatingPoint sort \param t2 term of 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))) */ Z3_ast Z3_API Z3_mk_fpa_leq(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Floating-point less than. \param c logical context \param t1 term of FloatingPoint sort \param t2 term of 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))) */ Z3_ast Z3_API Z3_mk_fpa_lt(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Floating-point greater than or equal. \param c logical context \param t1 term of FloatingPoint sort \param t2 term of 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))) */ Z3_ast Z3_API Z3_mk_fpa_geq(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Floating-point greater than. \param c logical context \param t1 term of FloatingPoint sort \param t2 term of 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))) */ Z3_ast Z3_API Z3_mk_fpa_gt(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Floating-point equality. \param c logical context \param t1 term of FloatingPoint sort \param t2 term of FloatingPoint sort Note that this is IEEE 754 equality (as opposed to SMT-LIB \ccode{=}). \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 \c t is a normal floating-point number. \param c logical context \param t term of 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 \c t is a subnormal floating-point number. \param c logical context \param t term of 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 \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 \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 \c t is a floating-point number representing +oo or -oo. \param c logical context \param t term of 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 \c t is a NaN. \param c logical context \param t term of 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 \c t is a negative floating-point number. \param c logical context \param t term of 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 \c t is a positive floating-point number. \param c logical context \param t term of FloatingPoint sort \c t must have FloatingPoint sort. def_API('Z3_mk_fpa_is_positive', AST, (_in(CONTEXT),_in(AST))) */ Z3_ast Z3_API Z3_mk_fpa_is_positive(Z3_context c, Z3_ast t); /** \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 \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 \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))) */ Z3_ast Z3_API Z3_mk_fpa_to_fp_bv(Z3_context c, Z3_ast bv, Z3_sort s); /** \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 \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 \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))) */ Z3_ast Z3_API Z3_mk_fpa_to_fp_float(Z3_context c, Z3_ast rm, Z3_ast t, Z3_sort s); /** \brief Conversion of a term of real sort into a term of FloatingPoint sort. 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 \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))) */ Z3_ast Z3_API Z3_mk_fpa_to_fp_real(Z3_context c, Z3_ast rm, Z3_ast t, Z3_sort s); /** \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 \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 \c rm. \param c logical context \param rm term of RoundingMode sort \param t term of bit-vector sort \param s floating-point 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))) */ Z3_ast Z3_API Z3_mk_fpa_to_fp_signed(Z3_context c, Z3_ast rm, Z3_ast t, Z3_sort s); /** \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 \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 \c rm. \param c logical context \param rm term of RoundingMode sort \param t term of bit-vector sort \param s floating-point 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))) */ Z3_ast Z3_API Z3_mk_fpa_to_fp_unsigned(Z3_context c, Z3_ast rm, Z3_ast t, Z3_sort s); /** \brief Conversion of a floating-point term into an unsigned bit-vector. 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 \param t term of FloatingPoint sort \param sz size of the resulting bit-vector def_API('Z3_mk_fpa_to_ubv', AST, (_in(CONTEXT),_in(AST),_in(AST),_in(UINT))) */ Z3_ast Z3_API Z3_mk_fpa_to_ubv(Z3_context c, Z3_ast rm, Z3_ast t, unsigned sz); /** \brief Conversion of a floating-point term into a signed bit-vector. 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 \param t term of FloatingPoint sort \param sz size of the resulting bit-vector def_API('Z3_mk_fpa_to_sbv', AST, (_in(CONTEXT),_in(AST),_in(AST),_in(UINT))) */ Z3_ast Z3_API Z3_mk_fpa_to_sbv(Z3_context c, Z3_ast rm, Z3_ast t, unsigned sz); /** \brief Conversion of a floating-point term into a real-numbered term. 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. \param c logical context \param t term of FloatingPoint sort def_API('Z3_mk_fpa_to_real', AST, (_in(CONTEXT),_in(AST))) */ Z3_ast Z3_API Z3_mk_fpa_to_real(Z3_context c, Z3_ast t); /** @name Z3-specific floating-point extensions */ /*@{*/ /** \brief Retrieves the number of bits reserved for the exponent in a FloatingPoint sort. \param c logical context \param s FloatingPoint sort def_API('Z3_fpa_get_ebits', UINT, (_in(CONTEXT),_in(SORT))) */ unsigned Z3_API Z3_fpa_get_ebits(Z3_context c, Z3_sort s); /** \brief Retrieves the number of bits reserved for the significand in a FloatingPoint sort. \param c logical context \param s FloatingPoint sort def_API('Z3_fpa_get_sbits', UINT, (_in(CONTEXT),_in(SORT))) */ unsigned Z3_API Z3_fpa_get_sbits(Z3_context c, Z3_sort s); /** \brief Checks whether a given floating-point numeral is a NaN. \param c logical context \param t a floating-point numeral def_API('Z3_fpa_is_numeral_nan', BOOL, (_in(CONTEXT), _in(AST))) */ bool Z3_API Z3_fpa_is_numeral_nan(Z3_context c, Z3_ast t); /** \brief Checks whether a given floating-point numeral is a +oo or -oo. \param c logical context \param t a floating-point numeral def_API('Z3_fpa_is_numeral_inf', BOOL, (_in(CONTEXT), _in(AST))) */ bool Z3_API Z3_fpa_is_numeral_inf(Z3_context c, Z3_ast t); /** \brief Checks whether a given floating-point numeral is +zero or -zero. \param c logical context \param t a floating-point numeral def_API('Z3_fpa_is_numeral_zero', BOOL, (_in(CONTEXT), _in(AST))) */ bool Z3_API Z3_fpa_is_numeral_zero(Z3_context c, Z3_ast t); /** \brief Checks whether a given floating-point numeral is normal. \param c logical context \param t a floating-point numeral def_API('Z3_fpa_is_numeral_normal', BOOL, (_in(CONTEXT), _in(AST))) */ bool Z3_API Z3_fpa_is_numeral_normal(Z3_context c, Z3_ast t); /** \brief Checks whether a given floating-point numeral is subnormal. \param c logical context \param t a floating-point numeral def_API('Z3_fpa_is_numeral_subnormal', BOOL, (_in(CONTEXT), _in(AST))) */ bool Z3_API Z3_fpa_is_numeral_subnormal(Z3_context c, Z3_ast t); /** \brief Checks whether a given floating-point numeral is positive. \param c logical context \param t a floating-point numeral def_API('Z3_fpa_is_numeral_positive', BOOL, (_in(CONTEXT), _in(AST))) */ bool Z3_API Z3_fpa_is_numeral_positive(Z3_context c, Z3_ast t); /** \brief Checks whether a given floating-point numeral is negative. \param c logical context \param t a floating-point numeral def_API('Z3_fpa_is_numeral_negative', BOOL, (_in(CONTEXT), _in(AST))) */ bool Z3_API Z3_fpa_is_numeral_negative(Z3_context c, Z3_ast t); /** \brief Retrieves the sign of a floating-point literal as a bit-vector expression. \param c logical context \param t a floating-point numeral Remarks: NaN is an invalid argument. def_API('Z3_fpa_get_numeral_sign_bv', AST, (_in(CONTEXT), _in(AST))) */ Z3_ast Z3_API Z3_fpa_get_numeral_sign_bv(Z3_context c, Z3_ast t); /** \brief Retrieves the significand of a floating-point literal as a bit-vector expression. \param c logical context \param t a floating-point numeral Remarks: NaN is an invalid argument. def_API('Z3_fpa_get_numeral_significand_bv', AST, (_in(CONTEXT), _in(AST))) */ Z3_ast Z3_API Z3_fpa_get_numeral_significand_bv(Z3_context c, Z3_ast t); /** \brief Retrieves the sign of a floating-point literal. \param c logical context \param t a floating-point numeral \param sgn sign Remarks: sets \c sgn to 0 if `t' is positive and to 1 otherwise, except for NaN, which is an invalid argument. def_API('Z3_fpa_get_numeral_sign', BOOL, (_in(CONTEXT), _in(AST), _out(INT))) */ bool Z3_API Z3_fpa_get_numeral_sign(Z3_context c, Z3_ast t, int * sgn); /** \brief Return the significand value of a floating-point numeral as a string. \param c logical context \param t a floating-point numeral 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))) */ Z3_string Z3_API Z3_fpa_get_numeral_significand_string(Z3_context c, Z3_ast t); /** \brief Return the significand value of a floating-point numeral as a uint64. \param c logical context \param t a floating-point numeral \param n pointer to output uint64 Remarks: This function extracts the significand bits in `t`, without the 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))) */ bool Z3_API Z3_fpa_get_numeral_significand_uint64(Z3_context c, Z3_ast t, uint64_t * n); /** \brief Return the exponent value of a floating-point numeral as a string. \param c logical context \param t a floating-point numeral \param biased flag to indicate whether the result is in biased representation Remarks: This function extracts the exponent in `t`, without normalization. NaN is an invalid argument. def_API('Z3_fpa_get_numeral_exponent_string', STRING, (_in(CONTEXT), _in(AST), _in(BOOL))) */ Z3_string Z3_API Z3_fpa_get_numeral_exponent_string(Z3_context c, Z3_ast t, bool biased); /** \brief Return the exponent value of a floating-point numeral as a signed 64-bit integer \param c logical context \param t a floating-point numeral \param n exponent \param biased flag to indicate whether the result is in biased representation Remarks: This function extracts the exponent in `t`, without normalization. NaN is an invalid argument. def_API('Z3_fpa_get_numeral_exponent_int64', BOOL, (_in(CONTEXT), _in(AST), _out(INT64), _in(BOOL))) */ bool Z3_API Z3_fpa_get_numeral_exponent_int64(Z3_context c, Z3_ast t, int64_t * n, bool biased); /** \brief Retrieves the exponent of a floating-point literal as a bit-vector expression. \param c logical context \param t a floating-point numeral \param biased flag to indicate whether the result is in biased representation Remarks: This function extracts the exponent in `t`, without normalization. NaN is an invalid arguments. def_API('Z3_fpa_get_numeral_exponent_bv', AST, (_in(CONTEXT), _in(AST), _in(BOOL))) */ Z3_ast Z3_API Z3_fpa_get_numeral_exponent_bv(Z3_context c, Z3_ast t, bool biased); /** \brief Conversion of a floating-point term into a bit-vector term in IEEE 754-2008 format. \param c logical context \param t term of FloatingPoint sort \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 knows only one NaN and it will always produce the same bit-vector representation of that NaN. def_API('Z3_mk_fpa_to_ieee_bv', AST, (_in(CONTEXT),_in(AST))) */ Z3_ast Z3_API Z3_mk_fpa_to_ieee_bv(Z3_context c, Z3_ast t); /** \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 \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 \param exp exponent term of Int sort \param sig significand term of Real sort \param s FloatingPoint 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))) */ Z3_ast Z3_API Z3_mk_fpa_to_fp_int_real(Z3_context c, Z3_ast rm, Z3_ast exp, Z3_ast sig, Z3_sort s); /*@}*/ /*@}*/ /*@}*/ #ifdef __cplusplus } #endif // __cplusplus #endif z3-z3-4.8.7/src/api/z3_logger.h000066400000000000000000000055111356505360400160350ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: z3_logger.h Abstract: Goodies for log generation Author: Leonardo de Moura (leonardo) 2011-09-22 Notes: --*/ #include #include "util/symbol.h" struct ll_escaped { char const * m_str; ll_escaped(char const * str):m_str(str) {} }; static std::ostream & operator<<(std::ostream & out, ll_escaped const & d); static void __declspec(noinline) R() { *g_z3_log << "R\n"; g_z3_log->flush(); } static void __declspec(noinline) P(void * obj) { *g_z3_log << "P " << obj << "\n"; g_z3_log->flush(); } static void __declspec(noinline) I(int64_t i) { *g_z3_log << "I " << i << "\n"; g_z3_log->flush(); } static void __declspec(noinline) U(uint64_t u) { *g_z3_log << "U " << u << "\n"; g_z3_log->flush(); } static void __declspec(noinline) D(double d) { *g_z3_log << "D " << d << "\n"; g_z3_log->flush(); } static void __declspec(noinline) S(Z3_string str) { *g_z3_log << "S \"" << ll_escaped(str) << "\"\n"; g_z3_log->flush(); } static void __declspec(noinline) Sy(Z3_symbol sym) { symbol s = symbol::mk_symbol_from_c_ptr(reinterpret_cast(sym)); if (s == symbol::null) { *g_z3_log << "N\n"; } else if (s.is_numerical()) { *g_z3_log << "# " << s.get_num() << "\n"; } else { *g_z3_log << "$ |" << ll_escaped(s.bare_str()) << "|\n"; } g_z3_log->flush(); } static void __declspec(noinline) Ap(unsigned sz) { *g_z3_log << "p " << sz << "\n"; g_z3_log->flush(); } static void __declspec(noinline) Au(unsigned sz) { *g_z3_log << "u " << sz << "\n"; g_z3_log->flush(); } static void __declspec(noinline) Ai(unsigned sz) { *g_z3_log << "i " << sz << "\n"; g_z3_log->flush(); } static void __declspec(noinline) Asy(unsigned sz) { *g_z3_log << "s " << sz << "\n"; g_z3_log->flush(); } static void __declspec(noinline) C(unsigned id) { *g_z3_log << "C " << id << "\n"; g_z3_log->flush(); } void __declspec(noinline) _Z3_append_log(char const * msg) { *g_z3_log << "M \"" << ll_escaped(msg) << "\"\n"; g_z3_log->flush(); } static std::ostream & operator<<(std::ostream & out, ll_escaped const & d) { char const * s = d.m_str; while (*s) { unsigned char c = *s; if (('0' <= c && c <= '9') || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '~' || c == '!' || c == '@' || c == '#' || c == '$' || c == '%' || c == '^' || c == '&' || c == '*' || c == '-' || c == '_' || c == '+' || c == '.' || c == '?' || c == '/' || c == ' ' || c == '<' || c == '>') { out << c; } else { unsigned char str[4] = {'0', '0', '0', 0}; str[2] = '0' + (c % 10); c /= 10; str[1] = '0' + (c % 10); c /= 10; str[0] = '0' + c; out << '\\' << str; } s++; } return out; } z3-z3-4.8.7/src/api/z3_macros.h000066400000000000000000000004731356505360400160440ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #ifndef Z3_bool_opt #define Z3_bool_opt Z3_bool #endif #ifndef Z3_API # ifdef __GNUC__ # define Z3_API __attribute__ ((visibility ("default"))) # else # define Z3_API # endif #endif #ifndef DEFINE_TYPE #define DEFINE_TYPE(T) typedef struct _ ## T *T #endif z3-z3-4.8.7/src/api/z3_optimization.h000066400000000000000000000273221356505360400173100ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: z3_optimization.h Abstract: Optimization facilities Author: Christoph M. Wintersteiger (cwinter) 2015-12-03 Notes: --*/ #ifndef Z3_OPTIMIZATION_H_ #define Z3_OPTIMIZATION_H_ #ifdef __cplusplus extern "C" { #endif // __cplusplus /** \defgroup capi C API */ /*@{*/ /** @name Optimization facilities */ /*@{*/ /** \brief Create a new optimize context. \remark User must use #Z3_optimize_inc_ref and #Z3_optimize_dec_ref to manage optimize objects. Even if the context was created using #Z3_mk_context instead of #Z3_mk_context_rc. def_API('Z3_mk_optimize', OPTIMIZE, (_in(CONTEXT), )) */ Z3_optimize Z3_API Z3_mk_optimize(Z3_context c); /** \brief Increment the reference counter of the given optimize context def_API('Z3_optimize_inc_ref', VOID, (_in(CONTEXT), _in(OPTIMIZE))) */ void Z3_API Z3_optimize_inc_ref(Z3_context c, Z3_optimize d); /** \brief Decrement the reference counter of the given optimize context. def_API('Z3_optimize_dec_ref', VOID, (_in(CONTEXT), _in(OPTIMIZE))) */ void Z3_API Z3_optimize_dec_ref(Z3_context c, Z3_optimize d); /** \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 \param o - optimization context \param a - formula \param weight - a positive weight, penalty for violating soft constraint \param id - optional identifier to group soft constraints \sa Z3_optimize_assert \sa Z3_optimize_assert_and_track def_API('Z3_optimize_assert_soft', UINT, (_in(CONTEXT), _in(OPTIMIZE), _in(AST), _in(STRING), _in(SYMBOL))) */ unsigned Z3_API Z3_optimize_assert_soft(Z3_context c, Z3_optimize o, Z3_ast a, Z3_string weight, Z3_symbol id); /** \brief Add a maximization constraint. \param c - context \param o - optimization context \param t - arithmetical term \sa Z3_optimize_minimize def_API('Z3_optimize_maximize', UINT, (_in(CONTEXT), _in(OPTIMIZE), _in(AST))) */ unsigned Z3_API Z3_optimize_maximize(Z3_context c, Z3_optimize o, Z3_ast t); /** \brief Add a minimization constraint. \param c - context \param o - optimization context \param t - arithmetical term \sa Z3_optimize_maximize def_API('Z3_optimize_minimize', UINT, (_in(CONTEXT), _in(OPTIMIZE), _in(AST))) */ unsigned Z3_API Z3_optimize_minimize(Z3_context c, Z3_optimize o, Z3_ast t); /** \brief Create a backtracking point. The optimize solver contains a set of rules, added facts and assertions. The set of rules, facts and assertions are restored upon calling #Z3_optimize_pop. \sa Z3_optimize_pop def_API('Z3_optimize_push', VOID, (_in(CONTEXT), _in(OPTIMIZE))) */ void Z3_API Z3_optimize_push(Z3_context c, Z3_optimize d); /** \brief Backtrack one level. \sa Z3_optimize_push \pre The number of calls to pop cannot exceed calls to push. def_API('Z3_optimize_pop', VOID, (_in(CONTEXT), _in(OPTIMIZE))) */ void Z3_API Z3_optimize_pop(Z3_context c, Z3_optimize d); /** \brief Check consistency and produce optimal values. \param c - context \param o - optimization context \param num_assumptions - number of additional assumptions \param assumptions - the additional assumptions \sa Z3_optimize_get_reason_unknown \sa Z3_optimize_get_model \sa Z3_optimize_get_statistics \sa Z3_optimize_get_unsat_core def_API('Z3_optimize_check', INT, (_in(CONTEXT), _in(OPTIMIZE), _in(UINT), _in_array(2, AST))) */ Z3_lbool Z3_API Z3_optimize_check(Z3_context c, Z3_optimize o, unsigned num_assumptions, Z3_ast const assumptions[]); /** \brief Retrieve a string that describes the last status returned by #Z3_optimize_check. Use this method when #Z3_optimize_check returns \c Z3_L_UNDEF. def_API('Z3_optimize_get_reason_unknown', STRING, (_in(CONTEXT), _in(OPTIMIZE) )) */ Z3_string Z3_API Z3_optimize_get_reason_unknown(Z3_context c, Z3_optimize d); /** \brief Retrieve the model for the last #Z3_optimize_check The error handler is invoked if a model is not available because the commands above were not invoked for the given optimization solver, or if the result was \c Z3_L_FALSE. def_API('Z3_optimize_get_model', MODEL, (_in(CONTEXT), _in(OPTIMIZE))) */ Z3_model Z3_API Z3_optimize_get_model(Z3_context c, Z3_optimize o); /** \brief Retrieve the unsat core for the last #Z3_optimize_check The unsat core is a subset of the assumptions \c a. def_API('Z3_optimize_get_unsat_core', AST_VECTOR, (_in(CONTEXT), _in(OPTIMIZE))) */ Z3_ast_vector Z3_API Z3_optimize_get_unsat_core(Z3_context c, Z3_optimize o); /** \brief Set parameters on optimization context. \param c - context \param o - optimization context \param p - parameters \sa Z3_optimize_get_help \sa Z3_optimize_get_param_descrs def_API('Z3_optimize_set_params', VOID, (_in(CONTEXT), _in(OPTIMIZE), _in(PARAMS))) */ void Z3_API Z3_optimize_set_params(Z3_context c, Z3_optimize o, Z3_params p); /** \brief Return the parameter description set for the given optimize object. \param c - context \param o - optimization context \sa Z3_optimize_get_help \sa Z3_optimize_set_params def_API('Z3_optimize_get_param_descrs', PARAM_DESCRS, (_in(CONTEXT), _in(OPTIMIZE))) */ Z3_param_descrs Z3_API Z3_optimize_get_param_descrs(Z3_context c, Z3_optimize o); /** \brief Retrieve lower bound value or approximation for the i'th optimization objective. \param c - context \param o - optimization context \param idx - index of optimization objective \sa Z3_optimize_get_upper \sa Z3_optimize_get_lower_as_vector \sa Z3_optimize_get_upper_as_vector def_API('Z3_optimize_get_lower', AST, (_in(CONTEXT), _in(OPTIMIZE), _in(UINT))) */ Z3_ast Z3_API Z3_optimize_get_lower(Z3_context c, Z3_optimize o, unsigned idx); /** \brief Retrieve upper bound value or approximation for the i'th optimization objective. \param c - context \param o - optimization context \param idx - index of optimization objective \sa Z3_optimize_get_lower \sa Z3_optimize_get_lower_as_vector \sa Z3_optimize_get_upper_as_vector def_API('Z3_optimize_get_upper', AST, (_in(CONTEXT), _in(OPTIMIZE), _in(UINT))) */ Z3_ast Z3_API Z3_optimize_get_upper(Z3_context c, Z3_optimize o, unsigned idx); /** \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 \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 \param idx - index of optimization objective \sa Z3_optimize_get_lower \sa Z3_optimize_get_upper \sa Z3_optimize_get_upper_as_vector def_API('Z3_optimize_get_lower_as_vector', AST_VECTOR, (_in(CONTEXT), _in(OPTIMIZE), _in(UINT))) */ Z3_ast_vector Z3_API Z3_optimize_get_lower_as_vector(Z3_context c, Z3_optimize o, unsigned idx); /** \brief Retrieve upper bound value or approximation for the i'th optimization objective. \param c - context \param o - optimization context \param idx - index of optimization objective \sa Z3_optimize_get_lower \sa Z3_optimize_get_upper \sa Z3_optimize_get_lower_as_vector def_API('Z3_optimize_get_upper_as_vector', AST_VECTOR, (_in(CONTEXT), _in(OPTIMIZE), _in(UINT))) */ Z3_ast_vector Z3_API Z3_optimize_get_upper_as_vector(Z3_context c, Z3_optimize o, unsigned idx); /** \brief Print the current context as a string. \param c - context. \param o - optimization context. \sa Z3_optimize_from_file \sa Z3_optimize_from_string def_API('Z3_optimize_to_string', STRING, (_in(CONTEXT), _in(OPTIMIZE))) */ Z3_string Z3_API Z3_optimize_to_string(Z3_context c, Z3_optimize o); /** \brief Parse an SMT-LIB2 string with assertions, soft constraints and optimization objectives. Add the parsed constraints and objectives to the optimization context. \param c - context. \param o - optimize context. \param s - string containing SMT2 specification. \sa Z3_optimize_from_file \sa Z3_optimize_to_string def_API('Z3_optimize_from_string', VOID, (_in(CONTEXT), _in(OPTIMIZE), _in(STRING))) */ void Z3_API Z3_optimize_from_string(Z3_context c, Z3_optimize o, Z3_string s); /** \brief Parse an SMT-LIB2 file with assertions, soft constraints and optimization objectives. Add the parsed constraints and objectives to the optimization context. \param c - context. \param o - optimize context. \param s - path to file containing SMT2 specification. \sa Z3_optimize_from_string \sa Z3_optimize_to_string def_API('Z3_optimize_from_file', VOID, (_in(CONTEXT), _in(OPTIMIZE), _in(STRING))) */ void Z3_API Z3_optimize_from_file(Z3_context c, Z3_optimize o, Z3_string s); /** \brief Return a string containing a description of parameters accepted by optimize. \sa Z3_optimize_get_param_descrs \sa Z3_optimize_set_params def_API('Z3_optimize_get_help', STRING, (_in(CONTEXT), _in(OPTIMIZE))) */ Z3_string Z3_API Z3_optimize_get_help(Z3_context c, Z3_optimize t); /** \brief Retrieve statistics information from the last call to #Z3_optimize_check def_API('Z3_optimize_get_statistics', STATS, (_in(CONTEXT), _in(OPTIMIZE))) */ Z3_stats Z3_API Z3_optimize_get_statistics(Z3_context c, Z3_optimize d); /** \brief Return the set of asserted formulas on the optimization context. def_API('Z3_optimize_get_assertions', AST_VECTOR, (_in(CONTEXT), _in(OPTIMIZE))) */ Z3_ast_vector Z3_API Z3_optimize_get_assertions(Z3_context c, Z3_optimize o); /** \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 \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. def_API('Z3_optimize_get_objectives', AST_VECTOR, (_in(CONTEXT), _in(OPTIMIZE))) */ Z3_ast_vector Z3_API Z3_optimize_get_objectives(Z3_context c, Z3_optimize o); /*@}*/ /*@}*/ #ifdef __cplusplus } #endif // __cplusplus #endif z3-z3-4.8.7/src/api/z3_polynomial.h000066400000000000000000000021101356505360400167310ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: z3_polynomial.h Abstract: Additional APIs for polynomials. Author: Leonardo de Moura (leonardo) 2012-12-09 Notes: --*/ #ifndef Z3_POLYNOMIAL_H_ #define Z3_POLYNOMIAL_H_ #ifdef __cplusplus extern "C" { #endif // __cplusplus /** \defgroup capi C API */ /*@{*/ /** @name Polynomials */ /*@{*/ /** \brief Return the nonzero subresultants of \c p and \c q with respect to the "variable" \c x. \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: \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))) */ Z3_ast_vector Z3_API Z3_polynomial_subresultants(Z3_context c, Z3_ast p, Z3_ast q, Z3_ast x); /*@}*/ /*@}*/ #ifdef __cplusplus } #endif // __cplusplus #endif z3-z3-4.8.7/src/api/z3_private.h000066400000000000000000000007731356505360400162350ustar00rootroot00000000000000/*++ Copyright (c) 2007 Microsoft Corporation Module Name: z3_private.h Abstract: Z3 API. Author: Nikolaj Bjorner (nbjorner) Leonardo de Moura (leonardo) 2007-06-8 Notes: --*/ #include #include "util/rational.h" #include "api/z3_macros.h" #ifndef Z3_PRIVATE_H_ #define Z3_PRIVATE_H_ #ifdef __cplusplus extern "C" { #endif // __cplusplus bool Z3_API Z3_get_numeral_rational(Z3_context c, Z3_ast a, rational& r); #ifdef __cplusplus }; #endif // __cplusplus #endif z3-z3-4.8.7/src/api/z3_rcf.h000066400000000000000000000135621356505360400153350ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: z3_rcf.h Abstract: Additional APIs for handling elements of the Z3 real closed field that contains: - transcendental extensions - infinitesimal extensions - algebraic extensions Author: Leonardo de Moura (leonardo) 2012-01-05 Notes: --*/ #ifndef Z3_RCF_H_ #define Z3_RCF_H_ #ifdef __cplusplus extern "C" { #endif // __cplusplus /** \defgroup capi C API */ /*@{*/ /** @name Real Closed Fields */ /*@{*/ /** \brief Delete a RCF numeral created using the RCF API. def_API('Z3_rcf_del', VOID, (_in(CONTEXT), _in(RCF_NUM))) */ void Z3_API Z3_rcf_del(Z3_context c, Z3_rcf_num a); /** \brief Return a RCF rational using the given string. def_API('Z3_rcf_mk_rational', RCF_NUM, (_in(CONTEXT), _in(STRING))) */ Z3_rcf_num Z3_API Z3_rcf_mk_rational(Z3_context c, Z3_string val); /** \brief Return a RCF small integer. def_API('Z3_rcf_mk_small_int', RCF_NUM, (_in(CONTEXT), _in(INT))) */ Z3_rcf_num Z3_API Z3_rcf_mk_small_int(Z3_context c, int val); /** \brief Return Pi def_API('Z3_rcf_mk_pi', RCF_NUM, (_in(CONTEXT),)) */ Z3_rcf_num Z3_API Z3_rcf_mk_pi(Z3_context c); /** \brief Return e (Euler's constant) def_API('Z3_rcf_mk_e', RCF_NUM, (_in(CONTEXT),)) */ Z3_rcf_num Z3_API Z3_rcf_mk_e(Z3_context c); /** \brief Return a new infinitesimal that is smaller than all elements in the Z3 field. def_API('Z3_rcf_mk_infinitesimal', RCF_NUM, (_in(CONTEXT),)) */ Z3_rcf_num Z3_API Z3_rcf_mk_infinitesimal(Z3_context c); /** \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. \pre The input polynomial is not the zero polynomial. def_API('Z3_rcf_mk_roots', UINT, (_in(CONTEXT), _in(UINT), _in_array(1, RCF_NUM), _out_array(1, RCF_NUM))) */ 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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \ccode{a != b}. def_API('Z3_rcf_neq', BOOL, (_in(CONTEXT), _in(RCF_NUM), _in(RCF_NUM))) */ bool Z3_API Z3_rcf_neq(Z3_context c, Z3_rcf_num a, Z3_rcf_num b); /** \brief Convert the RCF numeral into a string. def_API('Z3_rcf_num_to_string', STRING, (_in(CONTEXT), _in(RCF_NUM), _in(BOOL), _in(BOOL))) */ Z3_string Z3_API Z3_rcf_num_to_string(Z3_context c, Z3_rcf_num a, bool compact, bool html); /** \brief Convert the RCF numeral into a string in decimal notation. def_API('Z3_rcf_num_to_decimal_string', STRING, (_in(CONTEXT), _in(RCF_NUM), _in(UINT))) */ Z3_string Z3_API Z3_rcf_num_to_decimal_string(Z3_context c, Z3_rcf_num a, unsigned prec); /** \brief Extract the "numerator" and "denominator" of the given RCF numeral. 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))) */ void Z3_API Z3_rcf_get_numerator_denominator(Z3_context c, Z3_rcf_num a, Z3_rcf_num * n, Z3_rcf_num * d); /*@}*/ /*@}*/ #ifdef __cplusplus } #endif // __cplusplus #endif z3-z3-4.8.7/src/api/z3_replayer.cpp000066400000000000000000000634641356505360400167470ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: z3_replayer.cpp Abstract: Interpreter for Z3 logs Author: Leonardo de Moura (leonardo) 2011-09-22 Notes: --*/ #include "util/vector.h" #include "util/map.h" #include "api/z3_replayer.h" #include "util/stream_buffer.h" #include "util/symbol.h" #include "util/trace.h" #include #include void register_z3_replayer_cmds(z3_replayer & in); void throw_invalid_reference() { TRACE("z3_replayer", tout << "invalid argument reference\n";); throw z3_replayer_exception("invalid argument reference"); } struct z3_replayer::imp { z3_replayer & m_owner; std::istream & m_stream; int m_curr; // current char; int m_line; // line svector m_string; symbol m_id; int64_t m_int64; uint64_t m_uint64; double m_double; float m_float; size_t m_ptr; size_t_map m_heap; svector m_cmds; std::vector m_cmds_names; enum value_kind { INT64, UINT64, DOUBLE, STRING, SYMBOL, OBJECT, UINT_ARRAY, INT_ARRAY, SYMBOL_ARRAY, OBJECT_ARRAY, FLOAT }; char const* kind2string(value_kind k) const { switch (k) { case INT64: return "int64"; case UINT64: return "uint64"; case DOUBLE: return "double"; case STRING: return "string"; case SYMBOL: return "symbol"; case OBJECT: return "object"; case UINT_ARRAY: return "uint_array"; case INT_ARRAY: return "int_array"; case SYMBOL_ARRAY: return "symbol_array"; case OBJECT_ARRAY: return "object_array"; case FLOAT: return "float"; default: UNREACHABLE(); return "unknown"; } } void check_arg(unsigned pos, value_kind k) const { if (pos >= m_args.size()) { TRACE("z3_replayer", tout << "too few arguments " << m_args.size() << " expecting " << kind2string(k) << "\n";); throw z3_replayer_exception("invalid argument reference"); } if (m_args[pos].m_kind != k) { std::stringstream strm; strm << "expecting " << kind2string(k) << " at position " << pos << " but got " << kind2string(m_args[pos].m_kind); TRACE("z3_replayer", tout << strm.str() << "\n";); throw z3_replayer_exception(strm.str()); } } struct value { value_kind m_kind; union { int64_t m_int; uint64_t m_uint; double m_double; char const * m_str; void * m_obj; float m_float; }; value():m_kind(OBJECT), m_int(0) {} value(void * obj):m_kind(OBJECT), m_obj(obj) {} value(value_kind k, char const * str):m_kind(k), m_str(str) {} value(value_kind k, uint64_t u):m_kind(k), m_uint(u) {} value(value_kind k, int64_t i):m_kind(k), m_int(i) {} value(value_kind k, double d):m_kind(k), m_double(d) {} value(value_kind k, float f):m_kind(k), m_float(f) {} }; svector m_args; void * m_result; vector > m_obj_arrays; vector > m_sym_arrays; vector m_unsigned_arrays; vector > m_int_arrays; imp(z3_replayer & o, std::istream & in): m_owner(o), m_stream(in), m_curr(0), m_line(1) { next(); } void display_arg(std::ostream & out, value const & v) const { switch (v.m_kind) { case INT64: out << v.m_int; break; case UINT64: out << v.m_uint; break; case FLOAT: out << v.m_float; break; case DOUBLE: out << v.m_double; break; case STRING: out << v.m_str; break; case SYMBOL: out << symbol::mk_symbol_from_c_ptr(v.m_str); break; case OBJECT: out << v.m_obj; break; case UINT_ARRAY: case OBJECT_ARRAY: case SYMBOL_ARRAY: out << ""; break; default: out << ""; break; } } void display_args(std::ostream & out) const { for (unsigned i = 0; i < m_args.size(); i++) { if (i > 0) out << " "; display_arg(out, m_args[i]); } } int curr() const { return m_curr; } void new_line() { m_line++; } void next() { m_curr = m_stream.get(); } void read_string_core(char delimiter) { if (curr() != delimiter) throw z3_replayer_exception("invalid string/symbol"); m_string.reset(); next(); while (true) { int c = curr(); if (c == EOF) { throw z3_replayer_exception("unexpected end of file"); } else if (c == '\n') { throw z3_replayer_exception("unexpected end of line"); } else if (c == '\\') { next(); unsigned val = 0; unsigned sz = 0; while (sz < 3) { c = curr(); if ('0' <= c && c <= '9') { val *= 10; val += c - '0'; sz++; } else { throw z3_replayer_exception("invalid escaped character"); } if (val > 255) throw z3_replayer_exception("invalid escaped character"); next(); } TRACE("z3_replayer_escape", tout << "val: " << val << "\n";); m_string.push_back(static_cast(val)); } else if (c == delimiter) { next(); m_string.push_back(0); return; } else { m_string.push_back(c); next(); } } } void read_string() { read_string_core('"'); } void read_quoted_symbol() { read_string_core('|'); m_id = m_string.begin(); } void read_int64() { if (!(curr() == '-' || ('0' <= curr() && curr() <= '9'))) throw z3_replayer_exception("invalid integer"); bool sign = false; if (curr() == '-') { sign = true; next(); if (!('0' <= curr() && curr() <= '9')) throw z3_replayer_exception("invalid integer"); } m_int64 = 0; while (true) { int c = curr(); if ('0' <= c && c <= '9') { m_int64 = 10*m_int64 + (c - '0'); next(); } else { break; } } if (sign) m_int64 = -m_int64; } void read_uint64() { if (!('0' <= curr() && curr() <= '9')) throw z3_replayer_exception("invalid unsigned"); m_uint64 = 0; while (true) { int c = curr(); if ('0' <= c && c <= '9') { m_uint64 = 10*m_uint64 + (c - '0'); next(); } else { break; } } } bool is_double_char() const { return curr() == '-' || curr() == '.' || ('0' <= curr() && curr() <= '9') || curr() == 'e' || curr() == 'E'; } #if (!defined(strtof)) float strtof(const char * str, char ** end_ptr) { // Note: This may introduce a double-rounding problem. return (float)strtod(str, end_ptr); } #endif void read_float() { m_string.reset(); while (is_double_char()) { m_string.push_back(curr()); next(); } if (m_string.empty()) throw z3_replayer_exception("invalid float"); m_string.push_back(0); char * ptr; m_float = strtof(m_string.begin(), &ptr); } void read_double() { m_string.reset(); while (is_double_char()) { m_string.push_back(curr()); next(); } if (m_string.empty()) throw z3_replayer_exception("invalid double"); m_string.push_back(0); char * ptr; m_double = strtod(m_string.begin(), &ptr); } void read_ptr() { if (!(('0' <= curr() && curr() <= '9') || ('A' <= curr() && curr() <= 'F') || ('a' <= curr() && curr() <= 'f'))) { TRACE("invalid_ptr", tout << "curr: " << curr() << "\n";); throw z3_replayer_exception("invalid ptr"); } unsigned pos = 0; m_ptr = 0; while (true) { int c = curr(); if ('0' <= c && c <= '9') { m_ptr = m_ptr * 16 + (c - '0'); } else if ('a' <= c && c <= 'f') { m_ptr = m_ptr * 16 + 10 + (c - 'a'); } else if ('A' <= c && c <= 'F') { m_ptr = m_ptr * 16 + 10 + (c - 'A'); } else if (pos == 1 && (c == 'x' || c == 'X')) { // support for 0x.... notation } else { return; } next(); pos++; } } void skip_blank() { while (true) { int c = curr(); if (c == '\n') { new_line(); next(); } else if (c == ' ' || c == '\t') { next(); } else { break; } } } void push_array(unsigned sz, value_kind k) { unsigned asz = m_args.size(); if (sz > asz) throw z3_replayer_exception("invalid array size"); uint64_t aidx; value_kind nk; for (unsigned i = asz - sz; i < asz; i++) { if (m_args[i].m_kind != k) throw z3_replayer_exception("invalid array: mixed value types"); } if (k == UINT64) { aidx = m_unsigned_arrays.size(); nk = UINT_ARRAY; m_unsigned_arrays.push_back(unsigned_vector()); unsigned_vector & v = m_unsigned_arrays.back(); for (unsigned i = asz - sz; i < asz; i++) { v.push_back(static_cast(m_args[i].m_uint)); } } else if (k == INT64) { aidx = m_int_arrays.size(); nk = INT_ARRAY; m_int_arrays.push_back(svector()); svector & v = m_int_arrays.back(); for (unsigned i = asz - sz; i < asz; i++) { v.push_back(static_cast(m_args[i].m_int)); } } else if (k == SYMBOL) { aidx = m_sym_arrays.size(); nk = SYMBOL_ARRAY; m_sym_arrays.push_back(svector()); svector & v = m_sym_arrays.back(); for (unsigned i = asz - sz; i < asz; i++) { v.push_back(reinterpret_cast(const_cast(m_args[i].m_str))); } } else if (k == OBJECT) { TRACE("z3_replayer_bug", tout << "args: "; display_args(tout); tout << "\n"; tout << "push_back, sz: " << sz << ", m_obj_arrays.size(): " << m_obj_arrays.size() << "\n"; for (unsigned i = asz - sz; i < asz; i++) { tout << "pushing: " << m_args[i].m_obj << "\n"; }); aidx = m_obj_arrays.size(); nk = OBJECT_ARRAY; m_obj_arrays.push_back(ptr_vector()); ptr_vector & v = m_obj_arrays.back(); for (unsigned i = asz - sz; i < asz; i++) { v.push_back(m_args[i].m_obj); } } else { throw z3_replayer_exception("unsupported array type"); } m_args.shrink(asz - sz); m_args.push_back(value(nk, aidx)); } #define TICK_FREQUENCY 100000 void parse() { memory::exit_when_out_of_memory(false, nullptr); uint64_t counter = 0; unsigned tick = 0; while (true) { IF_VERBOSE(1, { counter++; tick++; if (tick >= TICK_FREQUENCY) { std::cout << "[replayer] " << counter << " operations executed" << std::endl; tick = 0; } }); skip_blank(); int c = curr(); if (c == EOF) return; switch (c) { case 'V': // version next(); skip_blank(); read_string(); break; case 'R': // reset next(); TRACE("z3_replayer", tout << "[" << m_line << "] " << "R\n";); reset(); break; case 'P': { // push pointer next(); skip_blank(); read_ptr(); TRACE("z3_replayer", tout << "[" << m_line << "] " << "P " << m_ptr << "\n";); if (m_ptr == 0) { m_args.push_back(nullptr); } else { void * obj = nullptr; if (!m_heap.find(m_ptr, obj)) throw z3_replayer_exception("invalid pointer"); m_args.push_back(value(obj)); TRACE("z3_replayer_bug", tout << "args after 'P':\n"; display_args(tout); tout << "\n";); } break; } case 'S': { // push string next(); skip_blank(); read_string(); TRACE("z3_replayer", tout << "[" << m_line << "] " << "S " << m_string.begin() << "\n";); symbol sym(m_string.begin()); // save string m_args.push_back(value(STRING, sym.bare_str())); break; } case 'N': // push null symbol next(); TRACE("z3_replayer", tout << "[" << m_line << "] " << "N\n";); m_args.push_back(value(SYMBOL, static_cast(nullptr))); break; case '$': { // push symbol next(); skip_blank(); read_quoted_symbol(); TRACE("z3_replayer", tout << "[" << m_line << "] " << "$ " << m_id << "\n";); m_args.push_back(value(SYMBOL, m_id.bare_str())); break; } case '#': { // push numeral symbol next(); skip_blank(); read_uint64(); TRACE("z3_replayer", tout << "[" << m_line << "] " << "# " << m_uint64 << "\n";); symbol sym(static_cast(m_uint64)); m_args.push_back(value(SYMBOL, static_cast(sym.c_ptr()))); break; } case 'I': // push integer; next(); skip_blank(); read_int64(); TRACE("z3_replayer", tout << "[" << m_line << "] " << "I " << m_int64 << "\n";); m_args.push_back(value(INT64, m_int64)); break; case 'U': // push unsigned; next(); skip_blank(); read_uint64(); TRACE("z3_replayer", tout << "[" << m_line << "] " << "U " << m_uint64 << "\n";); m_args.push_back(value(UINT64, m_uint64)); break; case 'F': // push float next(); skip_blank(); read_float(); TRACE("z3_replayer", tout << "[" << m_line << "] " << "F " << m_float << "\n";); m_args.push_back(value(FLOAT, m_float)); break; case 'D': // push double next(); skip_blank(); read_double(); TRACE("z3_replayer", tout << "[" << m_line << "] " << "D " << m_double << "\n";); m_args.push_back(value(DOUBLE, m_double)); break; case 'p': case 's': case 'u': case 'i': // push array next(); skip_blank(); read_uint64(); TRACE("z3_replayer", tout << "[" << m_line << "] " << "A " << m_uint64 << "\n";); if (c == 'p') push_array(static_cast(m_uint64), OBJECT); else if (c == 's') push_array(static_cast(m_uint64), SYMBOL); else if (c == 'i') push_array(static_cast(m_uint64), INT64); else push_array(static_cast(m_uint64), UINT64); break; case 'C': { // call procedure next(); skip_blank(); read_uint64(); TRACE("z3_replayer", tout << "[" << m_line << "] " << "C " << m_uint64 << "\n";); unsigned idx = static_cast(m_uint64); if (idx >= m_cmds.size()) throw z3_replayer_exception("invalid command"); try { TRACE("z3_replayer_cmd", tout << idx << ":" << m_cmds_names[idx] << "\n";); m_cmds[idx](m_owner); } catch (z3_error & ex) { throw ex; } catch (z3_exception & ex) { std::cout << "[z3 exception]: " << ex.msg() << std::endl; } break; } case '=': // save result // = obj_id next(); skip_blank(); read_ptr(); TRACE("z3_replayer", tout << "[" << m_line << "] " << "= " << m_ptr << "\n";); m_heap.insert(m_ptr, m_result); break; case '*': { // save out // @ obj_id pos next(); skip_blank(); read_ptr(); skip_blank(); read_uint64(); unsigned pos = static_cast(m_uint64); TRACE("z3_replayer", tout << "[" << m_line << "] " << "* " << m_ptr << " " << pos << "\n";); check_arg(pos, OBJECT); m_heap.insert(m_ptr, m_args[pos].m_obj); break; } case '@': { // save array out // @ obj_id array_pos idx next(); skip_blank(); read_ptr(); skip_blank(); read_uint64(); unsigned pos = static_cast(m_uint64); check_arg(pos, OBJECT_ARRAY); unsigned aidx = static_cast(m_args[pos].m_uint); ptr_vector & v = m_obj_arrays[aidx]; skip_blank(); read_uint64(); unsigned idx = static_cast(m_uint64); TRACE("z3_replayer", tout << "[" << m_line << "] " << "@ " << m_ptr << " " << pos << " " << idx << "\n";); TRACE("z3_replayer_bug", tout << "v[idx]: " << v[idx] << "\n";); m_heap.insert(m_ptr, v[idx]); break; } case 'M': // user message next(); skip_blank(); read_string(); TRACE("z3_replayer", tout << "[" << m_line << "] " << "M " << m_string.begin() << "\n";); std::cout << m_string.begin() << "\n"; std::cout.flush(); break; default: TRACE("z3_replayer", tout << "unknown command " << c << "\n";); throw z3_replayer_exception("unknown log command"); break; } } } int get_int(unsigned pos) const { check_arg(pos, INT64); return static_cast(m_args[pos].m_int); } int64_t get_int64(unsigned pos) const { check_arg(pos, INT64); return m_args[pos].m_int; } unsigned get_uint(unsigned pos) const { check_arg(pos, UINT64); return static_cast(m_args[pos].m_uint); } uint64_t get_uint64(unsigned pos) const { check_arg(pos, UINT64); return m_args[pos].m_uint; } float get_float(unsigned pos) const { if (pos >= m_args.size() || m_args[pos].m_kind != FLOAT) throw_invalid_reference(); return m_args[pos].m_float; } double get_double(unsigned pos) const { check_arg(pos, DOUBLE); return m_args[pos].m_double; } Z3_string get_str(unsigned pos) const { check_arg(pos, STRING); return m_args[pos].m_str; } Z3_symbol get_symbol(unsigned pos) const { check_arg(pos, SYMBOL); return reinterpret_cast(const_cast(m_args[pos].m_str)); } void * get_obj(unsigned pos) const { check_arg(pos, OBJECT); return m_args[pos].m_obj; } unsigned * get_uint_array(unsigned pos) const { check_arg(pos, UINT_ARRAY); unsigned idx = static_cast(m_args[pos].m_uint); return m_unsigned_arrays[idx].c_ptr(); } int * get_int_array(unsigned pos) const { check_arg(pos, INT_ARRAY); unsigned idx = static_cast(m_args[pos].m_uint); return m_int_arrays[idx].c_ptr(); } bool * get_bool_array(unsigned pos) const { check_arg(pos, UINT_ARRAY); unsigned idx = static_cast(m_args[pos].m_uint); return reinterpret_cast(m_unsigned_arrays[idx].c_ptr()); } Z3_symbol * get_symbol_array(unsigned pos) const { check_arg(pos, SYMBOL_ARRAY); unsigned idx = static_cast(m_args[pos].m_uint); return m_sym_arrays[idx].c_ptr(); } void ** get_obj_array(unsigned pos) const { check_arg(pos, OBJECT_ARRAY); unsigned idx = static_cast(m_args[pos].m_uint); ptr_vector const & v = m_obj_arrays[idx]; TRACE("z3_replayer_bug", tout << "pos: " << pos << ", idx: " << idx << " size(): " << v.size() << "\n"; for (unsigned i = 0; i < v.size(); i++) tout << v[i] << " "; tout << "\n";); return v.c_ptr(); } int * get_int_addr(unsigned pos) { check_arg(pos, INT64); return reinterpret_cast(&(m_args[pos].m_int)); } int64_t * get_int64_addr(unsigned pos) { check_arg(pos, INT64); return &(m_args[pos].m_int); } unsigned * get_uint_addr(unsigned pos) { check_arg(pos, UINT64); return reinterpret_cast(&(m_args[pos].m_uint)); } uint64_t * get_uint64_addr(unsigned pos) { check_arg(pos, UINT64); return &(m_args[pos].m_uint); } Z3_string * get_str_addr(unsigned pos) { check_arg(pos, STRING); return &(m_args[pos].m_str); } void ** get_obj_addr(unsigned pos) { check_arg(pos, OBJECT); return &(m_args[pos].m_obj); } void store_result(void * obj) { m_result = obj; } void register_cmd(unsigned id, z3_replayer_cmd cmd, char const* name) { m_cmds.reserve(id+1, 0); while (static_cast(m_cmds_names.size()) <= id+1) { m_cmds_names.push_back(""); } m_cmds[id] = cmd; m_cmds_names[id] = name; } void reset() { m_result = nullptr; m_args.reset(); m_obj_arrays.reset(); m_sym_arrays.reset(); m_unsigned_arrays.reset(); m_int_arrays.reset(); } }; z3_replayer::z3_replayer(std::istream & in) { m_imp = alloc(imp, *this, in); register_z3_replayer_cmds(*this); } z3_replayer::~z3_replayer() { dealloc(m_imp); } unsigned z3_replayer::get_line() const { return m_imp->m_line; } bool z3_replayer::get_bool(unsigned pos) const { return get_int(pos) != 0; } int z3_replayer::get_int(unsigned pos) const { return m_imp->get_int(pos); } unsigned z3_replayer::get_uint(unsigned pos) const { return m_imp->get_uint(pos); } int64_t z3_replayer::get_int64(unsigned pos) const { return m_imp->get_int64(pos); } uint64_t z3_replayer::get_uint64(unsigned pos) const { return m_imp->get_uint64(pos); } float z3_replayer::get_float(unsigned pos) const { return m_imp->get_float(pos); } double z3_replayer::get_double(unsigned pos) const { return m_imp->get_double(pos); } Z3_string z3_replayer::get_str(unsigned pos) const { return m_imp->get_str(pos); } Z3_symbol z3_replayer::get_symbol(unsigned pos) const { return m_imp->get_symbol(pos); } void * z3_replayer::get_obj(unsigned pos) const { return m_imp->get_obj(pos); } unsigned * z3_replayer::get_uint_array(unsigned pos) const { return m_imp->get_uint_array(pos); } int * z3_replayer::get_int_array(unsigned pos) const { return m_imp->get_int_array(pos); } bool * z3_replayer::get_bool_array(unsigned pos) const { return m_imp->get_bool_array(pos); } Z3_symbol * z3_replayer::get_symbol_array(unsigned pos) const { return m_imp->get_symbol_array(pos); } void ** z3_replayer::get_obj_array(unsigned pos) const { return m_imp->get_obj_array(pos); } int * z3_replayer::get_int_addr(unsigned pos) { return m_imp->get_int_addr(pos); } int64_t * z3_replayer::get_int64_addr(unsigned pos) { return m_imp->get_int64_addr(pos); } unsigned * z3_replayer::get_uint_addr(unsigned pos) { return m_imp->get_uint_addr(pos); } uint64_t * z3_replayer::get_uint64_addr(unsigned pos) { return m_imp->get_uint64_addr(pos); } Z3_string * z3_replayer::get_str_addr(unsigned pos) { return m_imp->get_str_addr(pos); } void ** z3_replayer::get_obj_addr(unsigned pos) { return m_imp->get_obj_addr(pos); } void z3_replayer::store_result(void * obj) { return m_imp->store_result(obj); } void z3_replayer::register_cmd(unsigned id, z3_replayer_cmd cmd, char const* name) { return m_imp->register_cmd(id, cmd, name); } void z3_replayer::parse() { return m_imp->parse(); } z3-z3-4.8.7/src/api/z3_replayer.h000066400000000000000000000031501356505360400163760ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: z3_replayer.h Abstract: Interpreter for Z3 logs Author: Leonardo de Moura (leonardo) 2011-09-22 Notes: --*/ #ifndef Z3_REPLAYER_H_ #define Z3_REPLAYER_H_ #include #include "api/z3.h" #include "util/z3_exception.h" class z3_replayer; typedef void (*z3_replayer_cmd)(z3_replayer &); typedef default_exception z3_replayer_exception; class z3_replayer { struct imp; imp * m_imp; public: z3_replayer(std::istream & in); ~z3_replayer(); void parse(); unsigned get_line() const; int get_int(unsigned pos) const; unsigned get_uint(unsigned pos) const; int64_t get_int64(unsigned pos) const; uint64_t get_uint64(unsigned pos) const; float get_float(unsigned pos) const; double get_double(unsigned pos) const; bool get_bool(unsigned pos) const; Z3_string get_str(unsigned pos) const; Z3_symbol get_symbol(unsigned pos) const; void * get_obj(unsigned pos) const; unsigned * get_uint_array(unsigned pos) const; int * get_int_array(unsigned pos) const; bool * get_bool_array(unsigned pos) const; Z3_symbol * get_symbol_array(unsigned pos) const; void ** get_obj_array(unsigned pos) const; int * get_int_addr(unsigned pos); int64_t * get_int64_addr(unsigned pos); unsigned * get_uint_addr(unsigned pos); uint64_t * get_uint64_addr(unsigned pos); Z3_string * get_str_addr(unsigned pos); void ** get_obj_addr(unsigned pos); void store_result(void * obj); void register_cmd(unsigned id, z3_replayer_cmd cmd, char const* name); }; #endif z3-z3-4.8.7/src/api/z3_spacer.h000066400000000000000000000075331356505360400160410ustar00rootroot00000000000000/*++ Copyright (c) 2017 Arie Gurfinkel Module Name: z3_spacer.h Abstract: Spacer API Author: Arie Gurfinkel (arie) Notes: --*/ #ifndef Z3_SPACER_H_ #define Z3_SPACER_H_ #ifdef __cplusplus extern "C" { #endif // __cplusplus /** \defgroup capi C API */ /*@{*/ /** @name Spacer facilities */ /*@{*/ /** \brief Pose a query against the asserted rules at the given level. \code query ::= (exists (bound-vars) query) | literals \endcode query returns - \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))) */ Z3_lbool Z3_API Z3_fixedpoint_query_from_lvl (Z3_context c,Z3_fixedpoint d, Z3_ast query, unsigned lvl); /** \brief Retrieve a bottom-up (from query) sequence of ground facts 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))) */ Z3_ast Z3_API Z3_fixedpoint_get_ground_sat_answer(Z3_context c,Z3_fixedpoint d); /** \brief Obtain the list of rules along the counterexample trace. def_API('Z3_fixedpoint_get_rules_along_trace', AST_VECTOR, (_in(CONTEXT), _in(FIXEDPOINT))) */ Z3_ast_vector Z3_API Z3_fixedpoint_get_rules_along_trace(Z3_context c,Z3_fixedpoint d); /** \brief Obtain the list of rules along the counterexample trace. def_API('Z3_fixedpoint_get_rule_names_along_trace', SYMBOL, (_in(CONTEXT), _in(FIXEDPOINT))) */ Z3_symbol Z3_API Z3_fixedpoint_get_rule_names_along_trace(Z3_context c,Z3_fixedpoint d); /** \brief Add an invariant for the predicate \c pred. Add an assumed invariant of predicate \c pred. Note: this functionality is Spacer specific. def_API('Z3_fixedpoint_add_invariant', VOID, (_in(CONTEXT), _in(FIXEDPOINT), _in(FUNC_DECL), _in(AST))) */ void Z3_API Z3_fixedpoint_add_invariant(Z3_context c, Z3_fixedpoint d, Z3_func_decl pred, Z3_ast property); /** Retrieve reachable states of a predicate. Note: this functionality is Spacer specific. def_API('Z3_fixedpoint_get_reachable', AST, (_in(CONTEXT), _in(FIXEDPOINT), _in(FUNC_DECL))) */ Z3_ast Z3_API Z3_fixedpoint_get_reachable(Z3_context c, Z3_fixedpoint d, Z3_func_decl pred); /** \brief Project variables given a model def_API('Z3_qe_model_project', AST, (_in(CONTEXT), _in(MODEL), _in(UINT), _in_array(2, APP), _in(AST))) */ Z3_ast Z3_API Z3_qe_model_project (Z3_context c, Z3_model m, unsigned num_bounds, Z3_app const bound[], Z3_ast body); /** \brief Project variables given a model def_API('Z3_qe_model_project_skolem', AST, (_in(CONTEXT), _in(MODEL), _in(UINT), _in_array(2, APP), _in(AST), _in(AST_MAP))) */ Z3_ast Z3_API Z3_qe_model_project_skolem (Z3_context c, Z3_model m, unsigned num_bounds, Z3_app const bound[], Z3_ast body, Z3_ast_map map); /** \brief Extrapolates a model of a formula def_API('Z3_model_extrapolate', AST, (_in(CONTEXT), _in(MODEL), _in(AST))) */ Z3_ast Z3_API Z3_model_extrapolate (Z3_context c, Z3_model m, Z3_ast fml); /** \brief Best-effort quantifier elimination def_API ('Z3_qe_lite', AST, (_in(CONTEXT), _in(AST_VECTOR), _in(AST))) */ Z3_ast Z3_API Z3_qe_lite (Z3_context c, Z3_ast_vector vars, Z3_ast body); /*@}*/ /*@}*/ #ifdef __cplusplus } #endif // __cplusplus #endif z3-z3-4.8.7/src/api/z3_v1.h000066400000000000000000000043101356505360400151000ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: z3_v1.h Abstract: Z3 1.x backwards compatibility macros. These macros are used to simulate the Z3 API using in the 1.x versions. This file should only be used by users still using the Z3 1.x API. Author: Leonardo de Moura (leonardo) 2011-09-22 Notes: --*/ #ifndef Z3_V1_H_ #define Z3_V1_H_ #include "api/z3.h" // Backwards compatibility #define Z3_type_ast Z3_sort #define Z3_const_decl_ast Z3_func_decl #define Z3_const Z3_app #define Z3_pattern_ast Z3_pattern #define Z3_UNINTERPRETED_TYPE Z3_UNINTERPRETED_SORT #define Z3_BOOL_TYPE Z3_BOOL_SORT #define Z3_INT_TYPE Z3_INT_SORT #define Z3_REAL_TYPE Z3_REAL_SORT #define Z3_BV_TYPE Z3_BV_SORT #define Z3_ARRAY_TYPE Z3_ARRAY_SORT #define Z3_TUPLE_TYPE Z3_DATATYPE_SORT #define Z3_UNKNOWN_TYPE Z3_UNKNOWN_SORT #define Z3_CONST_DECL_AST Z3_FUNC_DECL_AST #define Z3_TYPE_AST Z3_SORT_AST #define Z3_SORT_ERROR Z3_TYPE_ERROR #define Z3_mk_uninterpreted_type Z3_mk_uninterpreted_sort #define Z3_mk_bool_type Z3_mk_bool_sort #define Z3_mk_int_type Z3_mk_int_sort #define Z3_mk_real_type Z3_mk_real_sort #define Z3_mk_bv_type Z3_mk_bv_sort #define Z3_mk_array_type Z3_mk_array_sort #define Z3_mk_tuple_type Z3_mk_tuple_sort #define Z3_get_type Z3_get_sort #define Z3_get_pattern_ast Z3_get_pattern #define Z3_get_type_kind Z3_get_sort_kind #define Z3_get_type_name Z3_get_sort_name #define Z3_get_bv_type_size Z3_get_bv_sort_size #define Z3_get_array_type_domain Z3_get_array_sort_domain #define Z3_get_array_type_range Z3_get_array_sort_range #define Z3_get_tuple_type_num_fields Z3_get_tuple_sort_num_fields #define Z3_get_tuple_type_field_decl Z3_get_tuple_sort_field_decl #define Z3_get_tuple_type_mk_decl Z3_get_tuple_sort_mk_decl #define Z3_to_const_ast Z3_to_app #define Z3_get_numeral_value_string Z3_get_numeral_string #define Z3_get_const_ast_decl Z3_get_app_decl #define Z3_get_value Z3_eval_func_decl #endif z3-z3-4.8.7/src/ast/000077500000000000000000000000001356505360400140055ustar00rootroot00000000000000z3-z3-4.8.7/src/ast/CMakeLists.txt000066400000000000000000000021551356505360400165500ustar00rootroot00000000000000z3_add_component(ast SOURCES act_cache.cpp arith_decl_plugin.cpp array_decl_plugin.cpp ast.cpp ast_ll_pp.cpp ast_lt.cpp ast_pp_util.cpp ast_printer.cpp ast_smt2_pp.cpp ast_smt_pp.cpp ast_pp_dot.cpp ast_translation.cpp ast_util.cpp bv_decl_plugin.cpp csp_decl_plugin.cpp datatype_decl_plugin.cpp decl_collector.cpp display_dimacs.cpp dl_decl_plugin.cpp expr2polynomial.cpp expr2var.cpp expr_abstract.cpp expr_functors.cpp expr_map.cpp expr_stat.cpp expr_substitution.cpp for_each_ast.cpp for_each_expr.cpp format.cpp fpa_decl_plugin.cpp func_decl_dependencies.cpp has_free_vars.cpp macro_substitution.cpp num_occurs.cpp occurs.cpp pb_decl_plugin.cpp pp.cpp recfun_decl_plugin.cpp reg_decl_plugins.cpp seq_decl_plugin.cpp shared_occs.cpp special_relations_decl_plugin.cpp static_features.cpp used_vars.cpp well_sorted.cpp COMPONENT_DEPENDENCIES polynomial util # Unnecessary? polynomial already depends on util PYG_FILES pp_params.pyg ) z3-z3-4.8.7/src/ast/act_cache.cpp000066400000000000000000000135151356505360400164100ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: act_cache.cpp Abstract: expr -> expr activity cache It maintains at most N unused entries Author: Leonardo (leonardo) 2011-04-12 Notes: --*/ #include "ast/act_cache.h" #define MIN_MAX_UNUSED 1024 #define INITIAL_CAPACITY 128 /* This cache is a mapping from expr -> tagged expressions A tagged expression is essentially a pair (expr, flag) Thus, an entry t -> (s, 0) maps the key t to value s, and says that key t was never accessed. That is, client code never executed find(t) Similarly, an entry t -> (s, 1) also maps the key t to value s, but signs that key t was already accessed by client code. When a new key/value pair is inserted the flag is 0. The flag is set to 1 after the key is accessed. The number of unused entries (m_unused) is equal to the number of entries of the form t -> (s, 0) That is, it is the number of keys that were never accessed by client code. The cache maintains at most m_max_unused entries. When the maximum number of unused entries exceeds m_max_unused, then the cache will delete the oldest unused entry. */ /** m_queue stores the recently added keys. The queue is implemented as pair: m_queue (vector), m_qhead (unsigned). The "active" part of m_queue is the range [m_qhead, m_queue.size()) The "inactive" part [0, m_qhead) contains keys that were already used by client code. This procedure, deletes the inactive part, and makes m_qhead == 0. */ void act_cache::compress_queue() { SASSERT(m_qhead > 0); unsigned sz = m_queue.size(); unsigned j = 0; for (unsigned i = m_qhead; i < sz; i++, j++) { m_queue[j] = m_queue[i]; } m_queue.shrink(j); m_qhead = 0; } void act_cache::init() { if (m_max_unused < MIN_MAX_UNUSED) m_max_unused = MIN_MAX_UNUSED; m_unused = 0; m_qhead = 0; } void act_cache::dec_refs() { for (auto & kv : m_table) { m_manager.dec_ref(kv.m_key.first); m_manager.dec_ref(UNTAG(expr*, kv.m_value)); } } act_cache::act_cache(ast_manager & m): m_manager(m), m_max_unused(m.get_num_asts()) { init(); } act_cache::act_cache(ast_manager & m, unsigned max_unused): m_manager(m), m_max_unused(max_unused) { init(); } act_cache::~act_cache() { dec_refs(); } /** \brief Search m_queue from [m_qhead, m_queue.size()) until it finds an unused key. That is a key associated with an entry key -> (value, 0) */ void act_cache::del_unused() { unsigned sz = m_queue.size(); while (m_qhead < sz) { entry_t const& e = m_queue[m_qhead]; m_qhead++; 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(e); m_manager.dec_ref(e.first); m_manager.dec_ref(v); break; } } if (m_qhead == sz) { // The "active" part of the queue is empty. // So, we perform a "cheap" compress. m_queue.reset(); m_qhead = 0; } else if (m_qhead > m_max_unused) { compress_queue(); } } /** \brief Insert a new entry k -> v into the cache. */ 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(e, dummy); #if 0 unsigned static counter = 0; counter++; if (counter % 100000 == 0) verbose_stream() << "[act-cache] counter: " << counter << " used_slots: " << m_table.used_slots() << " capacity: " << m_table.capacity() << " size: " << m_table.size() << " collisions: " << m_table.collisions() << "\n"; #endif #ifdef Z3DEBUG unsigned expected_tag; #endif if (entry.m_value == dummy) { // new entry; m_manager.inc_ref(k); m_manager.inc_ref(v); entry.m_value = v; m_queue.push_back(e); m_unused++; DEBUG_CODE(expected_tag = 0;); // new entry } else if (UNTAG(expr*, entry.m_value) == v) { // already there DEBUG_CODE(expected_tag = GET_TAG(entry.m_value);); } else { // replacing old entry m_manager.inc_ref(v); m_manager.dec_ref(UNTAG(expr*, entry.m_value)); entry.m_value = v; SASSERT(GET_TAG(entry.m_value) == 0); // replaced old entry, and reset the tag. DEBUG_CODE(expected_tag = 0;); } DEBUG_CODE({ expr * v2; SASSERT(m_table.find(e, v2)); SASSERT(v == UNTAG(expr*, v2)); SASSERT(expected_tag == GET_TAG(v2)); }); } /** \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, 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) { entry->m_value = TAG(expr*, entry->m_value, 1); SASSERT(GET_TAG(entry->m_value) == 1); SASSERT(m_unused > 0); m_unused--; DEBUG_CODE({ expr * v; SASSERT(m_table.find(e, v)); SASSERT(GET_TAG(v) == 1); }); } return UNTAG(expr*, entry->m_value); } void act_cache::reset() { dec_refs(); m_table.reset(); m_queue.reset(); m_unused = 0; m_qhead = 0; } void act_cache::cleanup() { dec_refs(); m_table.finalize(); m_queue.finalize(); m_unused = 0; m_qhead = 0; } bool act_cache::check_invariant() const { return true; } z3-z3-4.8.7/src/ast/act_cache.h000066400000000000000000000027641356505360400160610ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: act_cache.h Abstract: expr -> expr activity cache It maintains at most N unused entries Author: Leonardo (leonardo) 2011-04-12 Notes: --*/ #ifndef ACT_CACHE_H_ #define ACT_CACHE_H_ #include "ast/ast.h" #include "util/obj_hashtable.h" #include "util/chashtable.h" class act_cache { ast_manager & m_manager; 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; svector m_queue; // recently created queue unsigned m_qhead; unsigned m_unused; unsigned m_max_unused; void compress_queue(); void init(); void dec_refs(); void del_unused(); public: act_cache(ast_manager & m); act_cache(ast_manager & m, unsigned max_unused); ~act_cache(); 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(); } unsigned capacity() const { return m_table.capacity(); } bool empty() const { return m_table.empty(); } bool check_invariant() const; }; #endif z3-z3-4.8.7/src/ast/arith_decl_plugin.cpp000066400000000000000000000726411356505360400201770ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: arith_decl_plugin.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-01-09 Revision History: --*/ #include "ast/arith_decl_plugin.h" #include "util/warning.h" #include "math/polynomial/algebraic_numbers.h" #include "util/id_gen.h" #include "ast/ast_smt2_pp.h" #include "util/gparams.h" struct arith_decl_plugin::algebraic_numbers_wrapper { unsynch_mpq_manager m_qmanager; algebraic_numbers::manager m_amanager; id_gen m_id_gen; scoped_anum_vector m_nums; algebraic_numbers_wrapper(reslimit& lim): m_amanager(lim, m_qmanager), m_nums(m_amanager) { } ~algebraic_numbers_wrapper() { } unsigned mk_id(algebraic_numbers::anum const & val) { SASSERT(!m_amanager.is_rational(val)); unsigned new_id = m_id_gen.mk(); m_nums.reserve(new_id+1); m_amanager.set(m_nums[new_id], val); TRACE("algebraic2expr", tout << "mk_id -> " << new_id << "\n"; m_amanager.display(tout, val); tout << "\n";); return new_id; } void recycle_id(unsigned idx) { SASSERT(idx < m_nums.size()); SASSERT(!m_amanager.is_zero(m_nums[idx])); TRACE("algebraic2expr", tout << "recycling: " << idx << "\n";); m_id_gen.recycle(idx); m_amanager.del(m_nums[idx]); } algebraic_numbers::anum const & idx2anum(unsigned idx) { return m_nums[idx]; } algebraic_numbers::anum const & to_anum(func_decl * f) { SASSERT(f->get_decl_kind() == OP_IRRATIONAL_ALGEBRAIC_NUM); return idx2anum(f->get_parameter(0).get_ext_id()); } }; arith_decl_plugin::algebraic_numbers_wrapper & arith_decl_plugin::aw() const { if (m_aw == nullptr) const_cast(this)->m_aw = alloc(algebraic_numbers_wrapper, m_manager->limit()); return *m_aw; } algebraic_numbers::manager & arith_decl_plugin::am() const { return aw().m_amanager; } app * arith_decl_plugin::mk_numeral(algebraic_numbers::anum const & val, bool is_int) { if (am().is_rational(val)) { rational rval; am().to_rational(val, rval); return mk_numeral(rval, is_int); } else { if (is_int) { m_manager->raise_exception("invalid irrational value passed as an integer"); } unsigned idx = aw().mk_id(val); parameter p(idx, true); SASSERT(p.is_external()); func_decl * decl = m_manager->mk_const_decl(m_rootv_sym, m_real_decl, func_decl_info(m_family_id, OP_IRRATIONAL_ALGEBRAIC_NUM, 1, &p)); app * r = m_manager->mk_const(decl); if (log_constant_meaning_prelude(r)) { am().display_root_smt2(m_manager->trace_stream(), val); m_manager->trace_stream() << "\n"; } return r; } } app * arith_decl_plugin::mk_numeral(sexpr const * p, unsigned i) { scoped_anum r(am()); am().mk_root(p, i, r); return mk_numeral(r, false); } void arith_decl_plugin::del(parameter const & p) { SASSERT(p.is_external()); if (m_aw != nullptr) { aw().recycle_id(p.get_ext_id()); } } parameter arith_decl_plugin::translate(parameter const & p, decl_plugin & target) { SASSERT(p.is_external()); arith_decl_plugin & _target = static_cast(target); return parameter(_target.aw().mk_id(aw().idx2anum(p.get_ext_id())), true); } void arith_decl_plugin::set_manager(ast_manager * m, family_id id) { decl_plugin::set_manager(m, id); m_real_decl = m->mk_sort(symbol("Real"), sort_info(id, REAL_SORT)); m->inc_ref(m_real_decl); sort * r = m_real_decl; m_int_decl = m->mk_sort(symbol("Int"), sort_info(id, INT_SORT)); m->inc_ref(m_int_decl); sort * i = m_int_decl; sort * b = m->mk_bool_sort(); #define MK_PRED(FIELD, NAME, KIND, SORT) { \ func_decl_info info(id, KIND); \ info.set_chainable(true); \ FIELD = m->mk_func_decl(symbol(NAME), SORT, SORT, b, info); \ m->inc_ref(FIELD); \ } MK_PRED(m_r_le_decl, "<=", OP_LE, r); MK_PRED(m_r_ge_decl, ">=", OP_GE, r); MK_PRED(m_r_lt_decl, "<", OP_LT, r); MK_PRED(m_r_gt_decl, ">", OP_GT, r); MK_PRED(m_i_le_decl, "<=", OP_LE, i); MK_PRED(m_i_ge_decl, ">=", OP_GE, i); MK_PRED(m_i_lt_decl, "<", OP_LT, i); MK_PRED(m_i_gt_decl, ">", OP_GT, i); #define MK_AC_OP(FIELD, NAME, KIND, SORT) { \ func_decl_info info(id, KIND); \ info.set_associative(); \ info.set_flat_associative(); \ info.set_commutative(); \ FIELD = m->mk_func_decl(symbol(NAME), SORT, SORT, SORT, info); \ m->inc_ref(FIELD); \ } #define MK_LEFT_ASSOC_OP(FIELD, NAME, KIND, SORT) { \ func_decl_info info(id, KIND); \ info.set_left_associative(); \ FIELD = m->mk_func_decl(symbol(NAME), SORT, SORT, SORT, info); \ m->inc_ref(FIELD); \ } #define MK_OP(FIELD, NAME, KIND, SORT) \ FIELD = m->mk_func_decl(symbol(NAME), SORT, SORT, SORT, func_decl_info(id, KIND)); \ m->inc_ref(FIELD) #define MK_UNARY(FIELD, NAME, KIND, SORT) \ FIELD = m->mk_func_decl(symbol(NAME), SORT, SORT, func_decl_info(id, KIND)); \ m->inc_ref(FIELD) MK_AC_OP(m_r_add_decl, "+", OP_ADD, r); MK_LEFT_ASSOC_OP(m_r_sub_decl, "-", OP_SUB, r); MK_AC_OP(m_r_mul_decl, "*", OP_MUL, r); MK_LEFT_ASSOC_OP(m_r_div_decl, "/", OP_DIV, r); MK_UNARY(m_r_uminus_decl, "-", OP_UMINUS, r); MK_AC_OP(m_i_add_decl, "+", OP_ADD, i); MK_LEFT_ASSOC_OP(m_i_sub_decl, "-", OP_SUB, i); MK_AC_OP(m_i_mul_decl, "*", OP_MUL, i); MK_LEFT_ASSOC_OP(m_i_div_decl, "div", OP_IDIV, i); MK_OP(m_i_rem_decl, "rem", OP_REM, i); MK_OP(m_i_mod_decl, "mod", OP_MOD, i); MK_UNARY(m_i_uminus_decl, "-", OP_UMINUS, i); m_to_real_decl = m->mk_func_decl(symbol("to_real"), i, r, func_decl_info(id, OP_TO_REAL)); m->inc_ref(m_to_real_decl); m_to_int_decl = m->mk_func_decl(symbol("to_int"), r, i, func_decl_info(id, OP_TO_INT)); m->inc_ref(m_to_int_decl); m_is_int_decl = m->mk_func_decl(symbol("is_int"), r, m->mk_bool_sort(), func_decl_info(id, OP_IS_INT)); m->inc_ref(m_is_int_decl); MK_OP(m_r_power_decl, "^", OP_POWER, r); MK_OP(m_i_power_decl, "^", OP_POWER, i); MK_UNARY(m_i_abs_decl, "abs", OP_ABS, i); MK_UNARY(m_r_abs_decl, "abs", OP_ABS, r); MK_UNARY(m_sin_decl, "sin", OP_SIN, r); MK_UNARY(m_cos_decl, "cos", OP_COS, r); MK_UNARY(m_tan_decl, "tan", OP_TAN, r); MK_UNARY(m_asin_decl, "asin", OP_ASIN, r); MK_UNARY(m_acos_decl, "acos", OP_ACOS, r); MK_UNARY(m_atan_decl, "atan", OP_ATAN, r); MK_UNARY(m_sinh_decl, "sinh", OP_SINH, r); MK_UNARY(m_cosh_decl, "cosh", OP_COSH, r); MK_UNARY(m_tanh_decl, "tanh", OP_TANH, r); MK_UNARY(m_asinh_decl, "asinh", OP_ASINH, r); MK_UNARY(m_acosh_decl, "acosh", OP_ACOSH, r); MK_UNARY(m_atanh_decl, "atanh", OP_ATANH, r); func_decl * pi_decl = m->mk_const_decl(symbol("pi"), r, func_decl_info(id, OP_PI)); m_pi = m->mk_const(pi_decl); m->inc_ref(m_pi); func_decl * e_decl = m->mk_const_decl(symbol("euler"), r, func_decl_info(id, OP_E)); m_e = m->mk_const(e_decl); m->inc_ref(m_e); MK_OP(m_neg_root_decl, "neg-root", OP_NEG_ROOT, r); MK_UNARY(m_u_asin_decl, "asin-u", OP_U_ASIN, r); MK_UNARY(m_u_acos_decl, "acos-u", OP_U_ACOS, r); } arith_decl_plugin::arith_decl_plugin(): m_aw(nullptr), m_intv_sym("Int"), m_realv_sym("Real"), m_rootv_sym("RootObject"), m_real_decl(nullptr), m_int_decl(nullptr), m_r_le_decl(nullptr), m_r_ge_decl(nullptr), m_r_lt_decl(nullptr), m_r_gt_decl(nullptr), m_r_add_decl(nullptr), m_r_sub_decl(nullptr), m_r_uminus_decl(nullptr), m_r_mul_decl(nullptr), m_r_div_decl(nullptr), m_i_le_decl(nullptr), m_i_ge_decl(nullptr), m_i_lt_decl(nullptr), m_i_gt_decl(nullptr), m_i_add_decl(nullptr), m_i_sub_decl(nullptr), m_i_uminus_decl(nullptr), m_i_mul_decl(nullptr), m_i_div_decl(nullptr), m_i_mod_decl(nullptr), m_i_rem_decl(nullptr), m_to_real_decl(nullptr), m_to_int_decl(nullptr), m_is_int_decl(nullptr), m_r_power_decl(nullptr), m_i_power_decl(nullptr), m_r_abs_decl(nullptr), m_i_abs_decl(nullptr), m_sin_decl(nullptr), m_cos_decl(nullptr), m_tan_decl(nullptr), m_asin_decl(nullptr), m_acos_decl(nullptr), m_atan_decl(nullptr), m_sinh_decl(nullptr), m_cosh_decl(nullptr), m_tanh_decl(nullptr), m_asinh_decl(nullptr), m_acosh_decl(nullptr), m_atanh_decl(nullptr), m_pi(nullptr), m_e(nullptr), m_neg_root_decl(nullptr), m_u_asin_decl(nullptr), m_u_acos_decl(nullptr), m_convert_int_numerals_to_real(false) { } arith_decl_plugin::~arith_decl_plugin() { dealloc(m_aw); } void arith_decl_plugin::finalize() { #define DEC_REF(decl) if (decl) { m_manager->dec_ref(decl); } ((void) 0) DEC_REF(m_real_decl); DEC_REF(m_int_decl); DEC_REF(m_r_le_decl); DEC_REF(m_r_ge_decl); DEC_REF(m_r_lt_decl); DEC_REF(m_r_gt_decl); DEC_REF(m_r_add_decl); DEC_REF(m_r_sub_decl); DEC_REF(m_r_uminus_decl); DEC_REF(m_r_mul_decl); DEC_REF(m_r_div_decl); DEC_REF(m_i_le_decl); DEC_REF(m_i_ge_decl); DEC_REF(m_i_lt_decl); DEC_REF(m_i_gt_decl); DEC_REF(m_i_add_decl); DEC_REF(m_i_sub_decl); DEC_REF(m_i_uminus_decl); DEC_REF(m_i_mul_decl); DEC_REF(m_i_div_decl); DEC_REF(m_i_mod_decl); DEC_REF(m_i_rem_decl); DEC_REF(m_to_real_decl); DEC_REF(m_to_int_decl); DEC_REF(m_is_int_decl); DEC_REF(m_i_power_decl); DEC_REF(m_r_power_decl); DEC_REF(m_i_abs_decl); DEC_REF(m_r_abs_decl); DEC_REF(m_sin_decl); DEC_REF(m_cos_decl); DEC_REF(m_tan_decl); DEC_REF(m_asin_decl); DEC_REF(m_acos_decl); DEC_REF(m_atan_decl); DEC_REF(m_sinh_decl); DEC_REF(m_cosh_decl); DEC_REF(m_tanh_decl); DEC_REF(m_asinh_decl); DEC_REF(m_acosh_decl); DEC_REF(m_atanh_decl); DEC_REF(m_pi); DEC_REF(m_e); DEC_REF(m_neg_root_decl); DEC_REF(m_u_asin_decl); DEC_REF(m_u_acos_decl); m_manager->dec_array_ref(m_small_ints.size(), m_small_ints.c_ptr()); m_manager->dec_array_ref(m_small_reals.size(), m_small_reals.c_ptr()); } sort * arith_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) { switch (k) { case REAL_SORT: return m_real_decl; case INT_SORT: return m_int_decl; default: return nullptr; } } inline func_decl * arith_decl_plugin::mk_func_decl(decl_kind k, bool is_real) { switch (k) { case OP_LE: return is_real ? m_r_le_decl : m_i_le_decl; case OP_GE: return is_real ? m_r_ge_decl : m_i_ge_decl; case OP_LT: return is_real ? m_r_lt_decl : m_i_lt_decl; case OP_GT: return is_real ? m_r_gt_decl : m_i_gt_decl; case OP_ADD: return is_real ? m_r_add_decl : m_i_add_decl; case OP_SUB: return is_real ? m_r_sub_decl : m_i_sub_decl; case OP_UMINUS: return is_real ? m_r_uminus_decl : m_i_uminus_decl; case OP_MUL: return is_real ? m_r_mul_decl : m_i_mul_decl; case OP_DIV: return m_r_div_decl; case OP_IDIV: return m_i_div_decl; case OP_IDIVIDES: UNREACHABLE(); case OP_REM: return m_i_rem_decl; case OP_MOD: return m_i_mod_decl; case OP_DIV0: return m_manager->mk_func_decl(symbol("/0"), m_real_decl, m_real_decl, m_real_decl, func_decl_info(m_family_id, OP_DIV0)); case OP_IDIV0: return m_manager->mk_func_decl(symbol("div0"), m_int_decl, m_int_decl, m_int_decl, func_decl_info(m_family_id, OP_IDIV0)); case OP_REM0: return m_manager->mk_func_decl(symbol("rem0"), m_int_decl, m_int_decl, m_int_decl, func_decl_info(m_family_id, OP_REM0)); case OP_MOD0: return m_manager->mk_func_decl(symbol("mod0"), m_int_decl, m_int_decl, m_int_decl, func_decl_info(m_family_id, OP_MOD0)); case OP_POWER0: if (is_real) { return m_manager->mk_func_decl(symbol("^0"), m_real_decl, m_real_decl, m_real_decl, func_decl_info(m_family_id, OP_POWER0)); } return m_manager->mk_func_decl(symbol("^0"), m_int_decl, m_int_decl, m_int_decl, func_decl_info(m_family_id, OP_POWER0)); case OP_TO_REAL: return m_to_real_decl; case OP_TO_INT: return m_to_int_decl; case OP_IS_INT: return m_is_int_decl; case OP_POWER: return is_real ? m_r_power_decl : m_i_power_decl; case OP_ABS: return is_real ? m_r_abs_decl : m_i_abs_decl; case OP_SIN: return m_sin_decl; case OP_COS: return m_cos_decl; case OP_TAN: return m_tan_decl; case OP_ASIN: return m_asin_decl; case OP_ACOS: return m_acos_decl; case OP_ATAN: return m_atan_decl; case OP_SINH: return m_sinh_decl; case OP_COSH: return m_cosh_decl; case OP_TANH: return m_tanh_decl; case OP_ASINH: return m_asinh_decl; case OP_ACOSH: return m_acosh_decl; case OP_ATANH: return m_atanh_decl; case OP_PI: return m_pi->get_decl(); case OP_E: return m_e->get_decl(); //case OP_0_PW_0_INT: return m_0_pw_0_int->get_decl(); //case OP_0_PW_0_REAL: return m_0_pw_0_real->get_decl(); case OP_NEG_ROOT: return m_neg_root_decl; //case OP_DIV_0: return m_div_0_decl; //case OP_IDIV_0: return m_idiv_0_decl; //case OP_MOD_0: return m_mod_0_decl; case OP_U_ASIN: return m_u_asin_decl; case OP_U_ACOS: return m_u_acos_decl; default: return nullptr; } } void arith_decl_plugin::check_arity(unsigned arity, unsigned expected_arity) { if (arity != expected_arity) { m_manager->raise_exception("invalid number of arguments passed to function"); } } inline decl_kind arith_decl_plugin::fix_kind(decl_kind k, unsigned arity) { if (k == OP_SUB && arity == 1) { return OP_UMINUS; } return k; } #define MAX_SMALL_NUM_TO_CACHE 16 app * arith_decl_plugin::mk_numeral(rational const & val, bool is_int) { if (is_int && !val.is_int()) { m_manager->raise_exception("invalid rational value passed as an integer"); } if (val.is_unsigned()) { unsigned u_val = val.get_unsigned(); if (u_val < MAX_SMALL_NUM_TO_CACHE) { if (is_int && !m_convert_int_numerals_to_real) { app * r = m_small_ints.get(u_val, 0); if (r == nullptr) { parameter p[2] = { parameter(val), parameter(1) }; r = m_manager->mk_const(m_manager->mk_const_decl(m_intv_sym, m_int_decl, func_decl_info(m_family_id, OP_NUM, 2, p))); m_manager->inc_ref(r); m_small_ints.setx(u_val, r, 0); if (log_constant_meaning_prelude(r)) { m_manager->trace_stream() << u_val << "\n"; } } return r; } else { app * r = m_small_reals.get(u_val, 0); if (r == nullptr) { parameter p[2] = { parameter(val), parameter(0) }; r = m_manager->mk_const(m_manager->mk_const_decl(m_realv_sym, m_real_decl, func_decl_info(m_family_id, OP_NUM, 2, p))); m_manager->inc_ref(r); m_small_reals.setx(u_val, r, 0); if (log_constant_meaning_prelude(r)) { m_manager->trace_stream() << u_val << "\n"; } } return r; } } } parameter p[2] = { parameter(val), parameter(static_cast(is_int)) }; func_decl * decl; if (is_int && !m_convert_int_numerals_to_real) decl = m_manager->mk_const_decl(m_intv_sym, m_int_decl, func_decl_info(m_family_id, OP_NUM, 2, p)); else decl = m_manager->mk_const_decl(m_realv_sym, m_real_decl, func_decl_info(m_family_id, OP_NUM, 2, p)); app * r = m_manager->mk_const(decl); if (log_constant_meaning_prelude(r)) { val.display_smt2(m_manager->trace_stream()); m_manager->trace_stream() << "\n"; } return r; } func_decl * arith_decl_plugin::mk_num_decl(unsigned num_parameters, parameter const * parameters, unsigned arity) { if (!(num_parameters == 2 && arity == 0 && parameters[0].is_rational() && parameters[1].is_int())) { m_manager->raise_exception("invalid numeral declaration"); return nullptr; } if (parameters[1].get_int() != 0) return m_manager->mk_const_decl(m_intv_sym, m_int_decl, func_decl_info(m_family_id, OP_NUM, num_parameters, parameters)); else return m_manager->mk_const_decl(m_realv_sym, m_real_decl, func_decl_info(m_family_id, OP_NUM, num_parameters, parameters)); } static bool use_coercion(decl_kind k) { return k == OP_ADD || k == OP_SUB || k == OP_MUL || k == OP_POWER || k == OP_LE || k == OP_GE || k == OP_LT || k == OP_GT || k == OP_UMINUS; } static bool has_real_arg(unsigned arity, sort * const * domain, sort * real_sort) { for (unsigned i = 0; i < arity; i++) if (domain[i] == real_sort) return true; return false; } static bool has_real_arg(ast_manager * m, unsigned num_args, expr * const * args, sort * real_sort) { for (unsigned i = 0; i < num_args; i++) if (m->get_sort(args[i]) == real_sort) return true; return false; } static bool is_const_op(decl_kind k) { return k == OP_PI || k == OP_E; //k == OP_0_PW_0_INT || //k == OP_0_PW_0_REAL; } func_decl * arith_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { if (k == OP_NUM) return mk_num_decl(num_parameters, parameters, arity); if (arity == 0 && !is_const_op(k)) { m_manager->raise_exception("no arguments supplied to arithmetical operator"); return nullptr; } if (k == OP_IDIVIDES) { if (arity != 1 || domain[0] != m_int_decl || num_parameters != 1 || !parameters[0].is_int()) { m_manager->raise_exception("invalid divides application. Expects integer parameter and one argument of sort integer"); } return m_manager->mk_func_decl(symbol("divisible"), 1, &m_int_decl, m_manager->mk_bool_sort(), func_decl_info(m_family_id, k, num_parameters, parameters)); } if (m_manager->int_real_coercions() && use_coercion(k)) { return mk_func_decl(fix_kind(k, arity), has_real_arg(arity, domain, m_real_decl)); } else { bool is_real = arity > 0 && domain[0] == m_real_decl; return mk_func_decl(fix_kind(k, arity), is_real); } } func_decl * arith_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned num_args, expr * const * args, sort * range) { if (k == OP_NUM) return mk_num_decl(num_parameters, parameters, num_args); if (num_args == 0 && !is_const_op(k)) { m_manager->raise_exception("no arguments supplied to arithmetical operator"); return nullptr; } if (k == OP_IDIVIDES) { if (num_args != 1 || m_manager->get_sort(args[0]) != m_int_decl || num_parameters != 1 || !parameters[0].is_int()) { m_manager->raise_exception("invalid divides application. Expects integer parameter and one argument of sort integer"); } return m_manager->mk_func_decl(symbol("divisible"), 1, &m_int_decl, m_manager->mk_bool_sort(), func_decl_info(m_family_id, k, num_parameters, parameters)); } if (m_manager->int_real_coercions() && use_coercion(k)) { return mk_func_decl(fix_kind(k, num_args), has_real_arg(m_manager, num_args, args, m_real_decl)); } else { bool is_real = num_args > 0 && m_manager->get_sort(args[0]) == m_real_decl; return mk_func_decl(fix_kind(k, num_args), is_real); } } void arith_decl_plugin::get_sort_names(svector& sort_names, symbol const & logic) { if (logic == "NRA" || logic == "QF_NRA" || logic == "QF_UFNRA") { m_convert_int_numerals_to_real = true; sort_names.push_back(builtin_name("Real", REAL_SORT)); } else { // TODO: only define Int and Real in the right logics sort_names.push_back(builtin_name("Int", INT_SORT)); sort_names.push_back(builtin_name("Real", REAL_SORT)); } } void arith_decl_plugin::get_op_names(svector& op_names, symbol const & logic) { op_names.push_back(builtin_name("<=",OP_LE)); op_names.push_back(builtin_name(">=",OP_GE)); op_names.push_back(builtin_name("<",OP_LT)); op_names.push_back(builtin_name(">",OP_GT)); op_names.push_back(builtin_name("+",OP_ADD)); op_names.push_back(builtin_name("-",OP_SUB)); op_names.push_back(builtin_name("~",OP_UMINUS)); op_names.push_back(builtin_name("*",OP_MUL)); op_names.push_back(builtin_name("/",OP_DIV)); op_names.push_back(builtin_name("div",OP_IDIV)); if (gparams::get_value("smtlib2_compliant") == "true") { op_names.push_back(builtin_name("divisible",OP_IDIVIDES)); } op_names.push_back(builtin_name("rem",OP_REM)); op_names.push_back(builtin_name("mod",OP_MOD)); op_names.push_back(builtin_name("to_real",OP_TO_REAL)); op_names.push_back(builtin_name("to_int",OP_TO_INT)); op_names.push_back(builtin_name("is_int",OP_IS_INT)); op_names.push_back(builtin_name("abs", OP_ABS)); if (logic == symbol::null || logic == symbol("ALL")) { op_names.push_back(builtin_name("^", OP_POWER)); op_names.push_back(builtin_name("sin", OP_SIN)); op_names.push_back(builtin_name("cos", OP_COS)); op_names.push_back(builtin_name("tan", OP_TAN)); op_names.push_back(builtin_name("asin", OP_ASIN)); op_names.push_back(builtin_name("acos", OP_ACOS)); op_names.push_back(builtin_name("atan", OP_ATAN)); op_names.push_back(builtin_name("sinh", OP_SINH)); op_names.push_back(builtin_name("cosh", OP_COSH)); op_names.push_back(builtin_name("tanh", OP_TANH)); op_names.push_back(builtin_name("asinh", OP_ASINH)); op_names.push_back(builtin_name("acosh", OP_ACOSH)); op_names.push_back(builtin_name("atanh", OP_ATANH)); op_names.push_back(builtin_name("pi", OP_PI)); op_names.push_back(builtin_name("euler", OP_E)); } } bool arith_decl_plugin::is_value(app * e) const { return is_app_of(e, m_family_id, OP_NUM) || is_app_of(e, m_family_id, OP_IRRATIONAL_ALGEBRAIC_NUM) || is_app_of(e, m_family_id, OP_PI) || is_app_of(e, m_family_id, OP_E); } bool arith_decl_plugin::is_unique_value(app * e) const { return is_app_of(e, m_family_id, OP_NUM) || is_app_of(e, m_family_id, OP_PI) || is_app_of(e, m_family_id, OP_E); } bool arith_decl_plugin::are_equal(app * a, app * b) const { if (decl_plugin::are_equal(a, b)) { return true; } if (is_app_of(a, m_family_id, OP_IRRATIONAL_ALGEBRAIC_NUM) && is_app_of(b, m_family_id, OP_IRRATIONAL_ALGEBRAIC_NUM)) { return am().eq(aw().to_anum(a->get_decl()), aw().to_anum(b->get_decl())); } return false; } bool arith_decl_plugin::are_distinct(app * a, app * b) const { TRACE("are_distinct_bug", tout << mk_ismt2_pp(a, *m_manager) << "\n" << mk_ismt2_pp(b, *m_manager) << "\n";); if (decl_plugin::are_distinct(a,b)) { return true; } if (is_app_of(a, m_family_id, OP_IRRATIONAL_ALGEBRAIC_NUM) && is_app_of(b, m_family_id, OP_IRRATIONAL_ALGEBRAIC_NUM)) { return am().neq(aw().to_anum(a->get_decl()), aw().to_anum(b->get_decl())); } #define is_non_zero(e) is_app_of(e,m_family_id, OP_NUM) && !to_app(e)->get_decl()->get_parameter(0).get_rational().is_zero() if (is_app_of(a, m_family_id, OP_ADD) && a->get_num_args() == 2 && to_app(a)->get_arg(0) == b && is_non_zero(to_app(a)->get_arg(1))) { return true; } if (is_app_of(a, m_family_id, OP_ADD) && a->get_num_args() == 2 && to_app(a)->get_arg(1) == b && is_non_zero(to_app(a)->get_arg(0))) { return true; } if (is_app_of(b, m_family_id, OP_ADD) && b->get_num_args() == 2 && to_app(b)->get_arg(1) == a && is_non_zero(to_app(b)->get_arg(0))) { return true; } if (is_app_of(b, m_family_id, OP_ADD) && b->get_num_args() == 2 && to_app(b)->get_arg(0) == a && is_non_zero(to_app(b)->get_arg(1))) { return true; } return false; } expr * arith_decl_plugin::get_some_value(sort * s) { SASSERT(s == m_int_decl || s == m_real_decl); return mk_numeral(rational(0), s == m_int_decl); } bool arith_recognizers::is_numeral(expr const * n, rational & val, bool & is_int) const { if (!is_app_of(n, m_afid, OP_NUM)) return false; func_decl * decl = to_app(n)->get_decl(); val = decl->get_parameter(0).get_rational(); is_int = decl->get_parameter(1).get_int() != 0; return true; } bool arith_recognizers::is_irrational_algebraic_numeral(expr const * n) const { return is_app(n) && to_app(n)->is_app_of(m_afid, OP_IRRATIONAL_ALGEBRAIC_NUM); } #define IS_INT_EXPR_DEPTH_LIMIT 100 bool arith_recognizers::is_int_expr(expr const *e) const { if (is_int(e)) return true; if (is_uninterp(e)) return false; ptr_buffer todo; todo.push_back(e); rational r; unsigned i = 0; while (!todo.empty()) { ++i; if (i > IS_INT_EXPR_DEPTH_LIMIT) {return false;} e = todo.back(); todo.pop_back(); if (is_to_real(e)) { // pass } else if (is_numeral(e, r) && r.is_int()) { // pass } else if (is_add(e) || is_mul(e)) { todo.append(to_app(e)->get_num_args(), to_app(e)->get_args()); } else { return false; } } return true; } arith_util::arith_util(ast_manager & m): arith_recognizers(m.mk_family_id("arith")), m_manager(m), m_plugin(nullptr) { } void arith_util::init_plugin() { SASSERT(m_plugin == 0); m_plugin = static_cast(m_manager.get_plugin(m_afid)); } bool arith_util::is_irrational_algebraic_numeral2(expr const * n, algebraic_numbers::anum & val) { if (!is_app_of(n, m_afid, OP_IRRATIONAL_ALGEBRAIC_NUM)) return false; am().set(val, to_irrational_algebraic_numeral(n)); return true; } algebraic_numbers::anum const & arith_util::to_irrational_algebraic_numeral(expr const * n) { SASSERT(is_irrational_algebraic_numeral(n)); return plugin().aw().to_anum(to_app(n)->get_decl()); } expr_ref arith_util::mk_mul_simplify(expr_ref_vector const& args) { return mk_mul_simplify(args.size(), args.c_ptr()); } expr_ref arith_util::mk_mul_simplify(unsigned sz, expr* const* args) { expr_ref result(m_manager); switch (sz) { case 0: result = mk_numeral(rational(1), true); break; case 1: result = args[0]; break; default: result = mk_mul(sz, args); break; } return result; } expr_ref arith_util::mk_add_simplify(expr_ref_vector const& args) { return mk_add_simplify(args.size(), args.c_ptr()); } expr_ref arith_util::mk_add_simplify(unsigned sz, expr* const* args) { expr_ref result(m_manager); switch (sz) { case 0: result = mk_numeral(rational(0), true); break; case 1: result = args[0]; break; default: result = mk_add(sz, args); break; } return result; } bool arith_util::is_considered_uninterpreted(func_decl* f, unsigned n, expr* const* args, func_decl_ref& f_out) { rational r; if (is_decl_of(f, m_afid, OP_DIV) && is_numeral(args[1], r) && r.is_zero()) { f_out = mk_div0(); return true; } if (is_decl_of(f, m_afid, OP_IDIV) && is_numeral(args[1], r) && r.is_zero()) { sort* rs[2] = { mk_real(), mk_real() }; f_out = m_manager.mk_func_decl(m_afid, OP_IDIV0, 0, nullptr, 2, rs, mk_real()); return true; } if (is_decl_of(f, m_afid, OP_MOD) && is_numeral(args[1], r) && r.is_zero()) { sort* rs[2] = { mk_real(), mk_real() }; f_out = m_manager.mk_func_decl(m_afid, OP_MOD0, 0, nullptr, 2, rs, mk_real()); return true; } if (is_decl_of(f, m_afid, OP_REM) && is_numeral(args[1], r) && r.is_zero()) { sort* rs[2] = { mk_real(), mk_real() }; f_out = m_manager.mk_func_decl(m_afid, OP_REM0, 0, nullptr, 2, rs, mk_real()); return true; } if (is_decl_of(f, m_afid, OP_POWER) && is_numeral(args[1], r) && r.is_zero() && is_numeral(args[0], r) && r.is_zero()) { sort* rs[2] = { mk_real(), mk_real() }; f_out = m_manager.mk_func_decl(m_afid, OP_POWER0, 0, nullptr, 2, rs, mk_real()); return true; } return plugin().is_considered_uninterpreted(f); } func_decl* arith_util::mk_div0() { sort* rs[2] = { mk_real(), mk_real() }; return m_manager.mk_func_decl(m_afid, OP_DIV0, 0, nullptr, 2, rs, mk_real()); } z3-z3-4.8.7/src/ast/arith_decl_plugin.h000066400000000000000000000507201356505360400176360ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: arith_decl_plugin.h Abstract: Author: Leonardo de Moura (leonardo) 2008-01-09 Revision History: --*/ #ifndef ARITH_DECL_PLUGIN_H_ #define ARITH_DECL_PLUGIN_H_ #include "ast/ast.h" class sexpr; namespace algebraic_numbers { class anum; class manager; }; enum arith_sort_kind { REAL_SORT, INT_SORT }; enum arith_op_kind { OP_NUM, // rational & integers OP_IRRATIONAL_ALGEBRAIC_NUM, // irrationals that are roots of polynomials with integer coefficients // OP_LE, OP_GE, OP_LT, OP_GT, OP_ADD, OP_SUB, OP_UMINUS, OP_MUL, OP_DIV, OP_IDIV, OP_DIV0, OP_IDIV0, OP_IDIVIDES, OP_REM, OP_MOD, OP_REM0, OP_MOD0, OP_TO_REAL, OP_TO_INT, OP_IS_INT, OP_ABS, OP_POWER, OP_POWER0, // hyperbolic and trigonometric functions OP_SIN, OP_COS, OP_TAN, OP_ASIN, OP_ACOS, OP_ATAN, OP_SINH, OP_COSH, OP_TANH, OP_ASINH, OP_ACOSH, OP_ATANH, // constants OP_PI, OP_E, // under-specified symbols OP_NEG_ROOT, // x^n when n is even and x is negative OP_U_ASIN, // asin(x) for x < -1 or x > 1 OP_U_ACOS, // acos(x) for x < -1 or x > 1 LAST_ARITH_OP }; class arith_util; class arith_decl_plugin : public decl_plugin { protected: struct algebraic_numbers_wrapper; algebraic_numbers_wrapper * m_aw; symbol m_intv_sym; symbol m_realv_sym; symbol m_rootv_sym; sort * m_real_decl; sort * m_int_decl; func_decl * m_r_le_decl; func_decl * m_r_ge_decl; func_decl * m_r_lt_decl; func_decl * m_r_gt_decl; func_decl * m_r_add_decl; func_decl * m_r_sub_decl; func_decl * m_r_uminus_decl; func_decl * m_r_mul_decl; func_decl * m_r_div_decl; func_decl * m_i_le_decl; func_decl * m_i_ge_decl; func_decl * m_i_lt_decl; func_decl * m_i_gt_decl; func_decl * m_i_add_decl; func_decl * m_i_sub_decl; func_decl * m_i_uminus_decl; func_decl * m_i_mul_decl; func_decl * m_i_div_decl; func_decl * m_i_mod_decl; func_decl * m_i_rem_decl; func_decl * m_to_real_decl; func_decl * m_to_int_decl; func_decl * m_is_int_decl; func_decl * m_r_power_decl; func_decl * m_i_power_decl; func_decl * m_r_abs_decl; func_decl * m_i_abs_decl; func_decl * m_sin_decl; func_decl * m_cos_decl; func_decl * m_tan_decl; func_decl * m_asin_decl; func_decl * m_acos_decl; func_decl * m_atan_decl; func_decl * m_sinh_decl; func_decl * m_cosh_decl; func_decl * m_tanh_decl; func_decl * m_asinh_decl; func_decl * m_acosh_decl; func_decl * m_atanh_decl; app * m_pi; app * m_e; func_decl * m_neg_root_decl; func_decl * m_u_asin_decl; func_decl * m_u_acos_decl; ptr_vector m_small_ints; ptr_vector m_small_reals; bool m_convert_int_numerals_to_real; func_decl * mk_func_decl(decl_kind k, bool is_real); void set_manager(ast_manager * m, family_id id) override; decl_kind fix_kind(decl_kind k, unsigned arity); void check_arity(unsigned arity, unsigned expected_arity); func_decl * mk_num_decl(unsigned num_parameters, parameter const * parameters, unsigned arity); public: arith_decl_plugin(); ~arith_decl_plugin() override; void finalize() override; algebraic_numbers::manager & am() const; algebraic_numbers_wrapper & aw() const; void del(parameter const & p) override; parameter translate(parameter const & p, decl_plugin & target) override; decl_plugin * mk_fresh() override { return alloc(arith_decl_plugin); } sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) override; func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) override; func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned num_args, expr * const * args, sort * range) override; bool is_value(app * e) const override; bool is_unique_value(app * e) const override; bool are_equal(app * a, app * b) const override; bool are_distinct(app * a, app * b) const override; void get_op_names(svector & op_names, symbol const & logic) override; void get_sort_names(svector & sort_names, symbol const & logic) override; app * mk_numeral(rational const & n, bool is_int); app * mk_numeral(algebraic_numbers::anum const & val, bool is_int); // Create a (real) numeral that is the i-th root of the polynomial encoded using the given sexpr. app * mk_numeral(sexpr const * p, unsigned i); app * mk_pi() const { return m_pi; } app * mk_e() const { return m_e; } expr * get_some_value(sort * s) override; bool is_considered_uninterpreted(func_decl * f) override { if (f->get_family_id() != get_family_id()) return false; switch (f->get_decl_kind()) { case OP_NEG_ROOT: case OP_U_ASIN: case OP_U_ACOS: case OP_DIV0: case OP_IDIV0: case OP_REM0: case OP_MOD0: case OP_POWER0: return true; default: return false; } return false; } }; /** \brief Procedures for recognizing arithmetic expressions. We don't need access to ast_manager, and operations can be simultaneously executed in different threads. */ class arith_recognizers { protected: family_id m_afid; public: arith_recognizers(family_id id):m_afid(id) {} family_id get_family_id() const { return m_afid; } bool is_arith_expr(expr const * n) const { return is_app(n) && to_app(n)->get_family_id() == m_afid; } bool is_irrational_algebraic_numeral(expr const * n) const; bool is_unsigned(expr const * n, unsigned& u) const { rational val; bool is_int = true; return is_numeral(n, val, is_int) && is_int && val.is_unsigned(), u = val.get_unsigned(), true; } bool is_numeral(expr const * n, rational & val, bool & is_int) const; bool is_numeral(expr const * n, rational & val) const { bool is_int; return is_numeral(n, val, is_int); } bool is_numeral(expr const * n) const { return is_app_of(n, m_afid, OP_NUM); } bool is_zero(expr const * n) const { rational val; return is_numeral(n, val) && val.is_zero(); } bool is_minus_one(expr * n) const { rational tmp; return is_numeral(n, tmp) && tmp.is_minus_one(); } // return true if \c n is a term of the form (* -1 r) bool is_times_minus_one(expr * n, expr * & r) const { if (is_mul(n) && to_app(n)->get_num_args() == 2 && is_minus_one(to_app(n)->get_arg(0))) { r = to_app(n)->get_arg(1); return true; } return false; } bool is_int_expr(expr const * e) const; bool is_le(expr const * n) const { return is_app_of(n, m_afid, OP_LE); } bool is_ge(expr const * n) const { return is_app_of(n, m_afid, OP_GE); } bool is_lt(expr const * n) const { return is_app_of(n, m_afid, OP_LT); } bool is_gt(expr const * n) const { return is_app_of(n, m_afid, OP_GT); } bool is_le(func_decl const * n) const { return is_decl_of(n, m_afid, OP_LE); } bool is_ge(func_decl const * n) const { return is_decl_of(n, m_afid, OP_GE); } bool is_lt(func_decl const * n) const { return is_decl_of(n, m_afid, OP_LT); } bool is_gt(func_decl const * n) const { return is_decl_of(n, m_afid, OP_GT); } bool is_div0(func_decl const * n) const { return is_decl_of(n, m_afid, OP_DIV0); } bool is_idiv0(func_decl const * n) const { return is_decl_of(n, m_afid, OP_IDIV0); } bool is_rem0(func_decl const * n) const { return is_decl_of(n, m_afid, OP_REM0); } bool is_mod0(func_decl const * n) const { return is_decl_of(n, m_afid, OP_MOD0); } bool is_power0(func_decl const * n) const { return is_decl_of(n, m_afid, OP_POWER0); } bool is_add(expr const * n) const { return is_app_of(n, m_afid, OP_ADD); } bool is_sub(expr const * n) const { return is_app_of(n, m_afid, OP_SUB); } bool is_uminus(expr const * n) const { return is_app_of(n, m_afid, OP_UMINUS); } bool is_mul(expr const * n) const { return is_app_of(n, m_afid, OP_MUL); } bool is_div(expr const * n) const { return is_app_of(n, m_afid, OP_DIV); } bool is_div0(expr const * n) const { return is_app_of(n, m_afid, OP_DIV0); } bool is_idiv(expr const * n) const { return is_app_of(n, m_afid, OP_IDIV); } bool is_idiv0(expr const * n) const { return is_app_of(n, m_afid, OP_IDIV0); } bool is_mod(expr const * n) const { return is_app_of(n, m_afid, OP_MOD); } bool is_rem(expr const * n) const { return is_app_of(n, m_afid, OP_REM); } bool is_to_real(expr const * n) const { return is_app_of(n, m_afid, OP_TO_REAL); } bool is_to_int(expr const * n) const { return is_app_of(n, m_afid, OP_TO_INT); } bool is_is_int(expr const * n) const { return is_app_of(n, m_afid, OP_IS_INT); } bool is_power(expr const * n) const { return is_app_of(n, m_afid, OP_POWER); } bool is_int(sort const * s) const { return is_sort_of(s, m_afid, INT_SORT); } bool is_int(expr const * n) const { return is_int(get_sort(n)); } bool is_real(sort const * s) const { return is_sort_of(s, m_afid, REAL_SORT); } bool is_real(expr const * n) const { return is_real(get_sort(n)); } bool is_int_real(sort const * s) const { return s->get_family_id() == m_afid; } bool is_int_real(expr const * n) const { return is_int_real(get_sort(n)); } bool is_sin(expr const* n) const { return is_app_of(n, m_afid, OP_SIN); } bool is_cos(expr const* n) const { return is_app_of(n, m_afid, OP_COS); } bool is_tan(expr const* n) const { return is_app_of(n, m_afid, OP_TAN); } bool is_asin(expr const* n) const { return is_app_of(n, m_afid, OP_ASIN); } bool is_acos(expr const* n) const { return is_app_of(n, m_afid, OP_ACOS); } bool is_atan(expr const* n) const { return is_app_of(n, m_afid, OP_ATAN); } bool is_asinh(expr const* n) const { return is_app_of(n, m_afid, OP_ASINH); } bool is_acosh(expr const* n) const { return is_app_of(n, m_afid, OP_ACOSH); } bool is_atanh(expr const* n) const { return is_app_of(n, m_afid, OP_ATANH); } bool is_pi(expr * arg) { return is_app_of(arg, m_afid, OP_PI); } bool is_e(expr * arg) { return is_app_of(arg, m_afid, OP_E); } MATCH_UNARY(is_uminus); MATCH_UNARY(is_to_real); MATCH_UNARY(is_to_int); MATCH_UNARY(is_is_int); MATCH_BINARY(is_sub); MATCH_BINARY(is_add); MATCH_BINARY(is_mul); MATCH_BINARY(is_le); MATCH_BINARY(is_ge); MATCH_BINARY(is_lt); MATCH_BINARY(is_gt); MATCH_BINARY(is_mod); MATCH_BINARY(is_rem); MATCH_BINARY(is_div); MATCH_BINARY(is_idiv); MATCH_BINARY(is_power); MATCH_UNARY(is_sin); MATCH_UNARY(is_asin); MATCH_UNARY(is_asinh); MATCH_UNARY(is_cos); MATCH_UNARY(is_acos); MATCH_UNARY(is_acosh); MATCH_UNARY(is_tan); MATCH_UNARY(is_atan); MATCH_UNARY(is_atanh); }; class arith_util : public arith_recognizers { ast_manager & m_manager; arith_decl_plugin * m_plugin; void init_plugin(); arith_decl_plugin & plugin() const { if (!m_plugin) const_cast(this)->init_plugin(); SASSERT(m_plugin != 0); return *m_plugin; } public: arith_util(ast_manager & m); ast_manager & get_manager() const { return m_manager; } algebraic_numbers::manager & am() { return plugin().am(); } bool is_irrational_algebraic_numeral2(expr const * n, algebraic_numbers::anum & val); algebraic_numbers::anum const & to_irrational_algebraic_numeral(expr const * n); sort * mk_int() { return m_manager.mk_sort(m_afid, INT_SORT); } sort * mk_real() { return m_manager.mk_sort(m_afid, REAL_SORT); } func_decl* mk_div0(); app * mk_numeral(rational const & val, bool is_int) const { return plugin().mk_numeral(val, is_int); } app * mk_numeral(rational const & val, sort const * s) const { SASSERT(is_int(s) || is_real(s)); return mk_numeral(val, is_int(s)); } app * mk_numeral(algebraic_numbers::anum const & val, bool is_int) { return plugin().mk_numeral(val, is_int); } app * mk_numeral(sexpr const * p, unsigned i) { return plugin().mk_numeral(p, i); } app * mk_int(int i) { return mk_numeral(rational(i), true); } app * mk_int(rational const& r) { return mk_numeral(r, true); } app * mk_real(int i) { return mk_numeral(rational(i), false); } app * mk_real(rational const& r) { return mk_numeral(r, false); } app * mk_le(expr * arg1, expr * arg2) const { return m_manager.mk_app(m_afid, OP_LE, arg1, arg2); } app * mk_ge(expr * arg1, expr * arg2) const { return m_manager.mk_app(m_afid, OP_GE, arg1, arg2); } app * mk_lt(expr * arg1, expr * arg2) const { return m_manager.mk_app(m_afid, OP_LT, arg1, arg2); } app * mk_gt(expr * arg1, expr * arg2) const { return m_manager.mk_app(m_afid, OP_GT, arg1, arg2); } app * mk_divides(expr* arg1, expr* arg2) { return m_manager.mk_app(m_afid, OP_IDIVIDES, arg1, arg2); } app * mk_add(unsigned num_args, expr * const * args) const { return num_args == 1 && is_app(args[0]) ? to_app(args[0]) : m_manager.mk_app(m_afid, OP_ADD, num_args, args); } app * mk_add(expr * arg1, expr * arg2) const { return m_manager.mk_app(m_afid, OP_ADD, arg1, arg2); } app * mk_add(expr * arg1, expr * arg2, expr* arg3) const { return m_manager.mk_app(m_afid, OP_ADD, arg1, arg2, arg3); } app * mk_sub(expr * arg1, expr * arg2) const { return m_manager.mk_app(m_afid, OP_SUB, arg1, arg2); } app * mk_sub(unsigned num_args, expr * const * args) const { return m_manager.mk_app(m_afid, OP_SUB, num_args, args); } app * mk_mul(expr * arg1, expr * arg2) const { return m_manager.mk_app(m_afid, OP_MUL, arg1, arg2); } app * mk_mul(expr * arg1, expr * arg2, expr* arg3) const { return m_manager.mk_app(m_afid, OP_MUL, arg1, arg2, arg3); } app * mk_mul(unsigned num_args, expr * const * args) const { return num_args == 1 && is_app(args[0]) ? to_app(args[0]) : m_manager.mk_app(m_afid, OP_MUL, num_args, args); } app * mk_uminus(expr * arg) const { return m_manager.mk_app(m_afid, OP_UMINUS, arg); } app * mk_div(expr * arg1, expr * arg2) { return m_manager.mk_app(m_afid, OP_DIV, arg1, arg2); } app * mk_idiv(expr * arg1, expr * arg2) { return m_manager.mk_app(m_afid, OP_IDIV, arg1, arg2); } app * mk_rem(expr * arg1, expr * arg2) { return m_manager.mk_app(m_afid, OP_REM, arg1, arg2); } app * mk_mod(expr * arg1, expr * arg2) { return m_manager.mk_app(m_afid, OP_MOD, arg1, arg2); } app * mk_div0(expr * arg1, expr * arg2) { return m_manager.mk_app(m_afid, OP_DIV0, arg1, arg2); } app * mk_idiv0(expr * arg1, expr * arg2) { return m_manager.mk_app(m_afid, OP_IDIV0, arg1, arg2); } app * mk_rem0(expr * arg1, expr * arg2) { return m_manager.mk_app(m_afid, OP_REM0, arg1, arg2); } app * mk_mod0(expr * arg1, expr * arg2) { return m_manager.mk_app(m_afid, OP_MOD0, arg1, arg2); } app * mk_to_real(expr * arg1) { return m_manager.mk_app(m_afid, OP_TO_REAL, arg1); } app * mk_to_int(expr * arg1) { return m_manager.mk_app(m_afid, OP_TO_INT, arg1); } app * mk_is_int(expr * arg1) { return m_manager.mk_app(m_afid, OP_IS_INT, arg1); } app * mk_power(expr* arg1, expr* arg2) { return m_manager.mk_app(m_afid, OP_POWER, arg1, arg2); } app * mk_power0(expr* arg1, expr* arg2) { return m_manager.mk_app(m_afid, OP_POWER0, arg1, arg2); } app * mk_sin(expr * arg) { return m_manager.mk_app(m_afid, OP_SIN, arg); } app * mk_cos(expr * arg) { return m_manager.mk_app(m_afid, OP_COS, arg); } app * mk_tan(expr * arg) { return m_manager.mk_app(m_afid, OP_TAN, arg); } app * mk_asin(expr * arg) { return m_manager.mk_app(m_afid, OP_ASIN, arg); } app * mk_acos(expr * arg) { return m_manager.mk_app(m_afid, OP_ACOS, arg); } app * mk_atan(expr * arg) { return m_manager.mk_app(m_afid, OP_ATAN, arg); } app * mk_sinh(expr * arg) { return m_manager.mk_app(m_afid, OP_SINH, arg); } app * mk_cosh(expr * arg) { return m_manager.mk_app(m_afid, OP_COSH, arg); } app * mk_tanh(expr * arg) { return m_manager.mk_app(m_afid, OP_TANH, arg); } app * mk_asinh(expr * arg) { return m_manager.mk_app(m_afid, OP_ASINH, arg); } app * mk_acosh(expr * arg) { return m_manager.mk_app(m_afid, OP_ACOSH, arg); } app * mk_atanh(expr * arg) { return m_manager.mk_app(m_afid, OP_ATANH, arg); } app * mk_pi() { return plugin().mk_pi(); } app * mk_e() { return plugin().mk_e(); } app * mk_neg_root(expr * arg1, expr * arg2) { return m_manager.mk_app(m_afid, OP_NEG_ROOT, arg1, arg2); } app * mk_u_asin(expr * arg) { return m_manager.mk_app(m_afid, OP_U_ASIN, arg); } app * mk_u_acos(expr * arg) { return m_manager.mk_app(m_afid, OP_U_ACOS, arg); } /** \brief Return the equality (= lhs rhs), but it makes sure that if one of the arguments is a numeral, then it will be in the right-hand-side; if none of them are numerals, then the left-hand-side has a smaller id than the right hand side. */ app * mk_eq(expr * lhs, expr * rhs) { if (is_numeral(lhs) || (!is_numeral(rhs) && lhs->get_id() > rhs->get_id())) std::swap(lhs, rhs); if (lhs == rhs) return m_manager.mk_true(); if (is_numeral(lhs) && is_numeral(rhs)) { SASSERT(lhs != rhs); return m_manager.mk_false(); } return m_manager.mk_eq(lhs, rhs); } expr_ref mk_mul_simplify(expr_ref_vector const& args); expr_ref mk_mul_simplify(unsigned sz, expr* const* args); expr_ref mk_add_simplify(expr_ref_vector const& args); expr_ref mk_add_simplify(unsigned sz, expr* const* args); bool is_considered_uninterpreted(func_decl* f, unsigned n, expr* const* args, func_decl_ref& f_out); }; inline app_ref mk_numeral(rational const& r, app_ref const& x) { arith_util a(x.get_manager()); return app_ref(a.mk_numeral(r, r.is_int() && a.is_int(x)), x.get_manager()); } inline app_ref operator+(app_ref const& x, app_ref const& y) { arith_util a(x.get_manager()); return app_ref(a.mk_add(x, y), x.get_manager()); } inline app_ref operator+(app_ref const& x, rational const& y) { return x + mk_numeral(y, x); } inline app_ref operator+(app_ref const& x, int y) { return x + rational(y); } inline app_ref operator+(rational const& x, app_ref const& y) { return mk_numeral(x, y) + y; } inline app_ref operator+(int x, app_ref const& y) { return rational(x) + y; } inline app_ref operator-(app_ref const& x, app_ref const& y) { arith_util a(x.get_manager()); return app_ref(a.mk_sub(x, y), x.get_manager()); } inline app_ref operator-(app_ref const& x, rational const& y) { return x - mk_numeral(y, x); } inline app_ref operator-(app_ref const& x, int y) { return x - rational(y); } inline app_ref operator-(rational const& x, app_ref const& y) { return mk_numeral(x, y) - y; } inline app_ref operator-(int x, app_ref const& y) { return rational(x) - y; } inline app_ref operator*(app_ref const& x, app_ref const& y) { arith_util a(x.get_manager()); return app_ref(a.mk_mul(x, y), x.get_manager()); } inline app_ref operator*(app_ref const& x, rational const& y) { return x * mk_numeral(y, x); } inline app_ref operator*(rational const& x, app_ref const& y) { return mk_numeral(x, y) * y; } inline app_ref operator*(app_ref const& x, int y) { return x * rational(y); } inline app_ref operator*(int x, app_ref const& y) { return rational(x) * y; } inline app_ref operator<=(app_ref const& x, app_ref const& y) { arith_util a(x.get_manager()); return app_ref(a.mk_le(x, y), x.get_manager()); } inline app_ref operator<=(app_ref const& x, rational const& y) { return x <= mk_numeral(y, x); } inline app_ref operator<=(app_ref const& x, int y) { return x <= rational(y); } inline app_ref operator>=(app_ref const& x, app_ref const& y) { arith_util a(x.get_manager()); return app_ref(a.mk_ge(x, y), x.get_manager()); } inline app_ref operator<(app_ref const& x, app_ref const& y) { arith_util a(x.get_manager()); return app_ref(a.mk_lt(x, y), x.get_manager()); } inline app_ref operator>(app_ref const& x, app_ref const& y) { arith_util a(x.get_manager()); return app_ref(a.mk_gt(x, y), x.get_manager()); } #endif /* ARITH_DECL_PLUGIN_H_ */ z3-z3-4.8.7/src/ast/array_decl_plugin.cpp000066400000000000000000000646371356505360400202140ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: array_decl_plugin.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-01-09 Revision History: --*/ #include #include "ast/array_decl_plugin.h" #include "util/warning.h" #include "ast/ast_pp.h" #include "ast/ast_ll_pp.h" #include "ast/arith_decl_plugin.h" array_decl_plugin::array_decl_plugin(): m_store_sym("store"), m_select_sym("select"), m_const_sym("const"), m_default_sym("default"), m_map_sym("map"), m_set_union_sym("union"), m_set_intersect_sym("intersection"), m_set_difference_sym("setminus"), m_set_complement_sym("complement"), m_set_subset_sym("subset"), m_array_ext_sym("array-ext"), m_as_array_sym("as-array"), m_set_has_size_sym("set-has-size"), m_set_card_sym("card") { } #define ARRAY_SORT_STR "Array" sort * array_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) { if (k == _SET_SORT) { if (num_parameters != 1) { m_manager->raise_exception("invalid array sort definition, invalid number of parameters"); return nullptr; } parameter params[2] = { parameters[0], parameter(m_manager->mk_bool_sort()) }; return mk_sort(ARRAY_SORT, 2, params); } SASSERT(k == ARRAY_SORT); if (num_parameters < 2) { m_manager->raise_exception("invalid array sort definition, invalid number of parameters"); return nullptr; } for (unsigned i = 0; i < num_parameters; i++) { if (!parameters[i].is_ast() || !is_sort(parameters[i].get_ast())) { m_manager->raise_exception("invalid array sort definition, parameter is not a sort"); return nullptr; } } sort * range = to_sort(parameters[num_parameters - 1].get_ast()); TRACE("array_decl_plugin_bug", tout << mk_pp(range, *m_manager) << "\n";); if (!range->is_infinite() && !range->is_very_big() && (1 == range->get_num_elements().size())) { return m_manager->mk_sort(symbol(ARRAY_SORT_STR), sort_info(m_family_id, ARRAY_SORT, 1, num_parameters, parameters)); } bool is_infinite = false; bool is_very_big = false; for (unsigned i = 0; i < num_parameters; i++) { sort * s = to_sort(parameters[i].get_ast()); if (s->is_infinite()) { is_infinite = true; } if (s->is_very_big()) { is_very_big = true; } } if (is_infinite) { return m_manager->mk_sort(symbol(ARRAY_SORT_STR), sort_info(m_family_id, ARRAY_SORT, num_parameters, parameters)); } else if (is_very_big) { return m_manager->mk_sort(symbol(ARRAY_SORT_STR), sort_info(m_family_id, ARRAY_SORT, sort_size::mk_very_big(), num_parameters, parameters)); } else { rational domain_sz(1); rational num_elements; for (unsigned i = 0; i < num_parameters - 1; i++) { domain_sz *= rational(to_sort(parameters[i].get_ast())->get_num_elements().size(),rational::ui64()); } if (domain_sz <= rational(128)) { num_elements = rational(range->get_num_elements().size(),rational::ui64()); num_elements = power(num_elements, static_cast(domain_sz.get_int64())); } if (domain_sz > rational(128) || !num_elements.is_uint64()) { // too many elements... return m_manager->mk_sort(symbol(ARRAY_SORT_STR), sort_info(m_family_id, ARRAY_SORT, sort_size::mk_very_big(), num_parameters, parameters)); } else { return m_manager->mk_sort(symbol(ARRAY_SORT_STR), sort_info(m_family_id, ARRAY_SORT, num_elements.get_uint64(), num_parameters, parameters)); } } } bool array_decl_plugin::is_array_sort(sort* s) const { return m_family_id == s->get_family_id() && s->get_decl_kind() == ARRAY_SORT; } func_decl * array_decl_plugin::mk_const(sort * s, unsigned arity, sort * const * domain) { if (arity != 1) { m_manager->raise_exception("invalid const array definition, invalid domain size"); return nullptr; } if (!is_array_sort(s)) { m_manager->raise_exception("invalid const array definition, parameter is not an array sort"); return nullptr; } if (!m_manager->compatible_sorts(get_array_range(s), domain[0])) { m_manager->raise_exception("invalid const array definition, sort mismatch between array range and argument"); return nullptr; } parameter param(s); func_decl_info info(m_family_id, OP_CONST_ARRAY, 1, ¶m); info.m_private_parameters = true; return m_manager->mk_func_decl(m_const_sym, arity, domain, s, info); } func_decl * array_decl_plugin::mk_map(func_decl* f, unsigned arity, sort* const* domain) { if (arity != f->get_arity()) { std::ostringstream buffer; buffer << "map expects to take as many arguments as the function being mapped, " << "it was given " << arity << " but expects " << f->get_arity(); m_manager->raise_exception(buffer.str()); return nullptr; } if (arity == 0) { m_manager->raise_exception("don't use map on constants"); return nullptr; } // // check that each domain[i] is an array sort // with the same domains and same ranges. // and that the ranges coincide with the domain of f. // unsigned dom_arity = get_array_arity(domain[0]); for (unsigned i = 0; i < arity; ++i) { if (!is_array_sort(domain[i])) { std::ostringstream buffer; buffer << "map expects an array sort as argument at position " << i; m_manager->raise_exception(buffer.str()); return nullptr; } if (get_array_arity(domain[i]) != dom_arity) { std::ostringstream buffer; buffer << "map expects all arguments to have the same array domain, " << "this is not the case for argument " << i; m_manager->raise_exception(buffer.str()); return nullptr; } for (unsigned j = 0; j < dom_arity; ++j) { if (get_array_domain(domain[i],j) != get_array_domain(domain[0],j)) { std::ostringstream buffer; buffer << "map expects all arguments to have the same array domain, " << "this is not the case for argument " << i; m_manager->raise_exception(buffer.str()); return nullptr; } } if (get_array_range(domain[i]) != f->get_domain(i)) { std::ostringstream buffer; buffer << "map expects the argument at position " << i << " to have the array range the same as the function"; m_manager->raise_exception(buffer.str()); return nullptr; } } vector parameters; for (unsigned i = 0; i < dom_arity; ++i) { parameters.push_back(domain[0]->get_parameter(i)); } parameters.push_back(parameter(f->get_range())); sort* range = mk_sort(ARRAY_SORT, parameters.size(), parameters.c_ptr()); parameter param(f); func_decl_info info(m_family_id, OP_ARRAY_MAP, 1, ¶m); // // left_associative, right_associative, commutative are inherited. // m_injective is inherited, since: // forall x . g(f(x)) = x implies forall X . map(g)(map(f)(X)) = X // since map(g)(map(f)(X))[i] = g(f(X[i])) = X[i] // info.set_right_associative(f->is_right_associative()); info.set_left_associative(f->is_left_associative()); info.set_commutative(f->is_commutative()); info.set_injective(f->is_injective()); return m_manager->mk_func_decl(m_map_sym, arity, domain, range, info); } func_decl * array_decl_plugin::mk_default(unsigned domain_size, sort * const * domain) { if (domain_size != 1) { m_manager->raise_exception("invalid default array definition, invalid domain size"); return nullptr; } // check that domain[0] is an array sort. unsigned num_parameters = domain[0]->get_num_parameters(); if (num_parameters <= 1) { m_manager->raise_exception("parameter mismatch. There should be more than one parameter to defaults"); return nullptr; } parameter param(domain[0]->get_parameter(num_parameters-1)); if (!param.is_ast() || !is_sort(param.get_ast())) { m_manager->raise_exception("last parameter should be a sort"); return nullptr; } sort * s = to_sort(param.get_ast()); return m_manager->mk_func_decl(m_default_sym, domain_size, domain, s, func_decl_info(m_family_id, OP_ARRAY_DEFAULT)); } func_decl* array_decl_plugin::mk_select(unsigned arity, sort * const * domain) { if (arity <= 1) { m_manager->raise_exception("select takes at least two arguments"); return nullptr; } sort * s = domain[0]; unsigned num_parameters = s->get_num_parameters(); parameter const* parameters = s->get_parameters(); if (num_parameters != arity) { std::stringstream strm; strm << "select requires " << num_parameters << " arguments, but was provided with " << arity << " arguments"; m_manager->raise_exception(strm.str()); return nullptr; } ptr_buffer new_domain; // we need this because of coercions. new_domain.push_back(s); for (unsigned i = 0; i + 1 < num_parameters; ++i) { if (!parameters[i].is_ast() || !is_sort(parameters[i].get_ast()) || !m_manager->compatible_sorts(domain[i+1], to_sort(parameters[i].get_ast()))) { std::stringstream strm; strm << "domain sort " << sort_ref(domain[i+1], *m_manager) << " and parameter "; strm << parameter_pp(parameters[i], *m_manager) << " do not match"; m_manager->raise_exception(strm.str()); return nullptr; } new_domain.push_back(to_sort(parameters[i].get_ast())); } SASSERT(new_domain.size() == arity); return m_manager->mk_func_decl(m_select_sym, arity, new_domain.c_ptr(), get_array_range(domain[0]), func_decl_info(m_family_id, OP_SELECT)); } func_decl * array_decl_plugin::mk_store(unsigned arity, sort * const * domain) { if (arity < 3) { m_manager->raise_exception("store takes at least 3 arguments"); return nullptr; } sort * s = domain[0]; unsigned num_parameters = s->get_num_parameters(); parameter const * parameters = s->get_parameters(); if (!is_array_sort(s)) { m_manager->raise_exception("store expects the first argument sort to be an array"); UNREACHABLE(); return nullptr; } if (arity != num_parameters+1) { std::ostringstream buffer; buffer << "store expects the first argument to be an array taking " << num_parameters+1 << ", instead it was passed " << (arity - 1) << "arguments"; m_manager->raise_exception(buffer.str()); UNREACHABLE(); return nullptr; } ptr_buffer new_domain; // we need this because of coercions. new_domain.push_back(s); for (unsigned i = 0; i < num_parameters; ++i) { if (!parameters[i].is_ast() || !is_sort(parameters[i].get_ast())) { m_manager->raise_exception("expecting sort parameter"); return nullptr; } sort* srt1 = to_sort(parameters[i].get_ast()); sort* srt2 = domain[i+1]; if (!m_manager->compatible_sorts(srt1, srt2)) { std::stringstream strm; strm << "domain sort " << sort_ref(srt2, *m_manager) << " and parameter sort " << sort_ref(srt1, *m_manager) << " do not match"; m_manager->raise_exception(strm.str()); UNREACHABLE(); return nullptr; } new_domain.push_back(to_sort(parameters[i].get_ast())); } SASSERT(new_domain.size() == arity); return m_manager->mk_func_decl(m_store_sym, arity, new_domain.c_ptr(), domain[0], func_decl_info(m_family_id, OP_STORE)); } func_decl * array_decl_plugin::mk_array_ext(unsigned arity, sort * const * domain, unsigned i) { if (arity != 2 || domain[0] != domain[1]) { UNREACHABLE(); return nullptr; } sort * s = domain[0]; unsigned num_parameters = s->get_num_parameters(); if (num_parameters == 0 || i >= num_parameters - 1) { UNREACHABLE(); return nullptr; } sort * r = to_sort(s->get_parameter(i).get_ast()); parameter param(i); return m_manager->mk_func_decl(m_array_ext_sym, arity, domain, r, func_decl_info(m_family_id, OP_ARRAY_EXT, 1, ¶m)); } bool array_decl_plugin::check_set_arguments(unsigned arity, sort * const * domain) { for (unsigned i = 0; i < arity; ++i) { if (domain[i] != domain[0]) { std::ostringstream buffer; buffer << "arguments " << 1 << " and " << (i+1) << " have different sorts"; m_manager->raise_exception(buffer.str()); return false; } if (domain[i]->get_family_id() != m_family_id) { std::ostringstream buffer; buffer << "argument " << (i+1) << " is not of array sort"; m_manager->raise_exception(buffer.str()); return false; } } if (arity > 0) { unsigned num_params = domain[0]->get_num_parameters(); parameter const* params = domain[0]->get_parameters(); if (1 >= num_params) { m_manager->raise_exception("expecting 2 or more parameters"); UNREACHABLE(); return false; } if (!params[num_params-1].is_ast()) { m_manager->raise_exception("expecting term parameters"); UNREACHABLE(); return false; } if (!is_sort(params[num_params-1].get_ast()) || !m_manager->is_bool(to_sort(params[num_params-1].get_ast()))) { m_manager->raise_exception("expecting boolean range"); UNREACHABLE(); return false; } } return true; } func_decl * array_decl_plugin::mk_set_union(unsigned arity, sort * const * domain) { if (arity == 0) { m_manager->raise_exception("union takes at least one argument"); return nullptr; } sort * s = domain[0]; if (!check_set_arguments(arity, domain)) { return nullptr; } parameter param(s); func_decl_info info(m_family_id, OP_SET_UNION, 1, ¶m); info.set_associative(); info.set_commutative(); info.set_idempotent(); sort* domain2[2] = { domain[0], domain[0] }; return m_manager->mk_func_decl(m_set_union_sym, 2, domain2, domain[0], info); } func_decl * array_decl_plugin::mk_set_intersect(unsigned arity, sort * const * domain) { if (arity == 0) { m_manager->raise_exception("intersection takes at least one argument"); return nullptr; } if (!check_set_arguments(arity, domain)) { return nullptr; } func_decl_info info(m_family_id, OP_SET_INTERSECT); info.set_associative(); info.set_commutative(); info.set_idempotent(); sort* domain2[2] = { domain[0], domain[0] }; return m_manager->mk_func_decl(m_set_intersect_sym, 2, domain2, domain[0], info); } func_decl * array_decl_plugin::mk_set_difference(unsigned arity, sort * const * domain) { if (arity != 2) { m_manager->raise_exception("set difference takes precisely two arguments"); return nullptr; } if (!check_set_arguments(arity, domain)) { return nullptr; } return m_manager->mk_func_decl(m_set_difference_sym, arity, domain, domain[0], func_decl_info(m_family_id, OP_SET_DIFFERENCE)); } func_decl * array_decl_plugin::mk_set_complement(unsigned arity, sort * const * domain) { if (arity != 1) { m_manager->raise_exception("set complement takes one argument"); return nullptr; } if (!check_set_arguments(arity, domain)) { return nullptr; } return m_manager->mk_func_decl(m_set_complement_sym, arity, domain, domain[0], func_decl_info(m_family_id, OP_SET_COMPLEMENT)); } func_decl * array_decl_plugin::mk_set_subset(unsigned arity, sort * const * domain) { if (arity != 2) { m_manager->raise_exception("subset takes two arguments"); return nullptr; } if (!check_set_arguments(arity, domain)) { return nullptr; } sort * bool_sort = m_manager->mk_bool_sort(); return m_manager->mk_func_decl(m_set_subset_sym, arity, domain, bool_sort, func_decl_info(m_family_id, OP_SET_SUBSET)); } func_decl * array_decl_plugin::mk_set_card(unsigned arity, sort * const* domain) { if (arity != 1) { m_manager->raise_exception("card takes only one argument"); return nullptr; } arith_util arith(*m_manager); if (!is_array_sort(domain[0]) || !m_manager->is_bool(get_array_range(domain[0]))) { m_manager->raise_exception("card expects an array of Booleans"); } sort * int_sort = arith.mk_int(); return m_manager->mk_func_decl(m_set_card_sym, arity, domain, int_sort, func_decl_info(m_family_id, OP_SET_CARD)); } func_decl * array_decl_plugin::mk_set_has_size(unsigned arity, sort * const* domain) { if (arity != 2) { m_manager->raise_exception("set-has-size takes two arguments"); return nullptr; } // domain[0] is a Boolean array, // domain[1] is Int arith_util arith(*m_manager); if (!arith.is_int(domain[1])) { m_manager->raise_exception("set-has-size expects second argument to be an integer"); } if (!is_array_sort(domain[0]) || !m_manager->is_bool(get_array_range(domain[0]))) { m_manager->raise_exception("set-has-size expects first argument to be an array of Booleans"); } sort * bool_sort = m_manager->mk_bool_sort(); return m_manager->mk_func_decl(m_set_has_size_sym, arity, domain, bool_sort, func_decl_info(m_family_id, OP_SET_HAS_SIZE)); } func_decl * array_decl_plugin::mk_as_array(func_decl * f) { vector parameters; for (unsigned i = 0; i < f->get_arity(); i++) { parameters.push_back(parameter(f->get_domain(i))); } parameters.push_back(parameter(f->get_range())); sort * s = mk_sort(ARRAY_SORT, parameters.size(), parameters.c_ptr()); parameter param(f); func_decl_info info(m_family_id, OP_AS_ARRAY, 1, ¶m); return m_manager->mk_const_decl(m_as_array_sym, s, info); } func_decl * array_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { switch (k) { case OP_SELECT: return mk_select(arity, domain); case OP_STORE: return mk_store(arity, domain); case OP_CONST_ARRAY: { if (num_parameters == 1 && parameters[0].is_ast() && is_sort(parameters[0].get_ast())) { sort * s = to_sort(parameters[0].get_ast()); return mk_const(s, arity, domain); } else if (range != nullptr) { return mk_const(range, arity, domain); } else { m_manager->raise_exception("array operation requires one sort parameter (the array sort)"); UNREACHABLE(); return nullptr; } } case OP_ARRAY_MAP: { if (num_parameters != 1 || !parameters[0].is_ast() || !is_func_decl(parameters[0].get_ast())) { m_manager->raise_exception("array operation requires one function declaration parameter (the function to be mapped)"); UNREACHABLE(); return nullptr; } func_decl * f = to_func_decl(parameters[0].get_ast()); return mk_map(f, arity, domain); } case OP_ARRAY_EXT: if (num_parameters == 0) { return mk_array_ext(arity, domain, 0); } if (num_parameters != 1 || !parameters[0].is_int()) { UNREACHABLE(); return nullptr; } return mk_array_ext(arity, domain, parameters[0].get_int()); case OP_ARRAY_DEFAULT: return mk_default(arity, domain); case OP_SET_UNION: return mk_set_union(arity, domain); case OP_SET_INTERSECT: return mk_set_intersect(arity, domain); case OP_SET_DIFFERENCE: return mk_set_difference(arity, domain); case OP_SET_COMPLEMENT: return mk_set_complement(arity, domain); case OP_SET_SUBSET: return mk_set_subset(arity, domain); case OP_SET_HAS_SIZE: return mk_set_has_size(arity, domain); case OP_SET_CARD: return mk_set_card(arity, domain); case OP_AS_ARRAY: { if (num_parameters != 1 || !parameters[0].is_ast() || !is_func_decl(parameters[0].get_ast()) || to_func_decl(parameters[0].get_ast())->get_arity() == 0) { TRACE("array_bug", tout << "num_parameters: " << num_parameters << std::endl; tout << "parameter.kind: " << parameters[0].is_int() << " " << parameters[0].is_ast() << " " << parameters[0].is_symbol() << "\n"; tout << "as-array-bug: " << to_func_decl(parameters[0].get_ast())->get_name() << " " << to_func_decl(parameters[0].get_ast())->get_arity() << std::endl;); m_manager->raise_exception("as-array takes one parameter, a function declaration with arity greater than zero"); UNREACHABLE(); return nullptr; } func_decl * f = to_func_decl(parameters[0].get_ast()); return mk_as_array(f); } default: return nullptr; } } void array_decl_plugin::get_sort_names(svector& sort_names, symbol const & logic) { sort_names.push_back(builtin_name(ARRAY_SORT_STR, ARRAY_SORT)); sort_names.push_back(builtin_name("=>", ARRAY_SORT)); if (logic == symbol::null || logic == symbol("HORN") || logic == symbol("ALL")) { // this could easily break users even though it is already used in CVC4: sort_names.push_back(builtin_name("Set", _SET_SORT)); } } void array_decl_plugin::get_op_names(svector& op_names, symbol const & logic) { op_names.push_back(builtin_name("store",OP_STORE)); op_names.push_back(builtin_name("select",OP_SELECT)); if (logic == symbol::null || logic == symbol("HORN") || logic == symbol("ALL")) { // none of the SMT2 logics support these extensions op_names.push_back(builtin_name("const",OP_CONST_ARRAY)); op_names.push_back(builtin_name("map",OP_ARRAY_MAP)); op_names.push_back(builtin_name("default",OP_ARRAY_DEFAULT)); op_names.push_back(builtin_name("union",OP_SET_UNION)); op_names.push_back(builtin_name("intersection",OP_SET_INTERSECT)); op_names.push_back(builtin_name("setminus",OP_SET_DIFFERENCE)); op_names.push_back(builtin_name("complement",OP_SET_COMPLEMENT)); op_names.push_back(builtin_name("subset",OP_SET_SUBSET)); op_names.push_back(builtin_name("as-array", OP_AS_ARRAY)); op_names.push_back(builtin_name("array-ext", OP_ARRAY_EXT)); op_names.push_back(builtin_name("set-has-size", OP_SET_HAS_SIZE)); op_names.push_back(builtin_name("card", OP_SET_CARD)); } } expr * array_decl_plugin::get_some_value(sort * s) { SASSERT(s->is_sort_of(m_family_id, ARRAY_SORT)); sort * r = to_sort(s->get_parameter(s->get_num_parameters() - 1).get_ast()); expr * v = m_manager->get_some_value(r); parameter p(s); return m_manager->mk_app(m_family_id, OP_CONST_ARRAY, 1, &p, 1, &v); } bool array_decl_plugin::is_fully_interp(sort * s) const { SASSERT(s->is_sort_of(m_family_id, ARRAY_SORT)); unsigned sz = get_array_arity(s); for (unsigned i = 0; i < sz; i++) { if (!m_manager->is_fully_interp(get_array_domain(s, i))) return false; } return m_manager->is_fully_interp(get_array_range(s)); } func_decl * array_recognizers::get_as_array_func_decl(expr * n) const { SASSERT(is_as_array(n)); return to_func_decl(to_app(n)->get_decl()->get_parameter(0).get_ast()); } func_decl * array_recognizers::get_map_func_decl(func_decl* f) const { SASSERT(f->get_num_parameters() == 1); SASSERT(f->get_parameter(0).is_ast()); SASSERT(is_func_decl(f->get_parameter(0).get_ast())); return to_func_decl(f->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()); } bool array_recognizers::is_const(expr* e, expr*& v) const { return is_const(e) && (v = to_app(e)->get_arg(0), true); } bool array_recognizers::is_store_ext(expr* _e, expr_ref& a, expr_ref_vector& args, expr_ref& value) { if (is_store(_e)) { app* e = to_app(_e); a = e->get_arg(0); unsigned sz = e->get_num_args(); args.reset(); for (unsigned i = 1; i < sz-1; ++i) { args.push_back(e->get_arg(i)); } value = e->get_arg(sz-1); return true; } return false; } array_util::array_util(ast_manager& m): array_recognizers(m.mk_family_id("array")), m_manager(m) { } bool array_util::is_as_array_tree(expr * n) { ptr_buffer todo; todo.push_back(n); while (!todo.empty()) { expr * curr = todo.back(); todo.pop_back(); if (is_as_array(curr)) continue; if (m_manager.is_ite(curr)) { todo.push_back(to_app(curr)->get_arg(1)); todo.push_back(to_app(curr)->get_arg(2)); continue; } return false; } return true; } sort * array_util::mk_array_sort(unsigned arity, sort* const* domain, sort* range) { vector params; for (unsigned i = 0; i < arity; ++i) { params.push_back(parameter(domain[i])); } params.push_back(parameter(range)); return m_manager.mk_sort(m_fid, ARRAY_SORT, params.size(), params.c_ptr()); } func_decl* array_util::mk_array_ext(sort *domain, unsigned i) { sort * domains[2] = { domain, domain }; parameter p(i); return m_manager.mk_func_decl(m_fid, OP_ARRAY_EXT, 1, &p, 2, domains); } z3-z3-4.8.7/src/ast/array_decl_plugin.h000066400000000000000000000211261356505360400176430ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: array_decl_plugin.h Abstract: Author: Leonardo de Moura (leonardo) 2008-01-09. Revision History: --*/ #ifndef ARRAY_DECL_PLUGIN_H_ #define ARRAY_DECL_PLUGIN_H_ #include "ast/ast.h" inline sort* get_array_range(sort const * s) { return to_sort(s->get_parameter(s->get_num_parameters() - 1).get_ast()); } inline unsigned get_array_arity(sort const * s) { return s->get_num_parameters() -1; } inline sort* get_array_domain(sort const * s, unsigned idx) { return to_sort(s->get_parameter(idx).get_ast()); } enum array_sort_kind { ARRAY_SORT, _SET_SORT }; enum array_op_kind { OP_STORE, OP_SELECT, OP_CONST_ARRAY, OP_ARRAY_EXT, OP_ARRAY_DEFAULT, OP_ARRAY_MAP, OP_SET_UNION, OP_SET_INTERSECT, OP_SET_DIFFERENCE, OP_SET_COMPLEMENT, OP_SET_SUBSET, OP_SET_HAS_SIZE, OP_SET_CARD, OP_AS_ARRAY, // used for model construction LAST_ARRAY_OP }; class array_decl_plugin : public decl_plugin { symbol m_store_sym; symbol m_select_sym; symbol m_const_sym; symbol m_default_sym; symbol m_map_sym; symbol m_set_union_sym; symbol m_set_intersect_sym; symbol m_set_difference_sym; symbol m_set_complement_sym; symbol m_set_subset_sym; symbol m_array_ext_sym; symbol m_as_array_sym; symbol m_set_has_size_sym; symbol m_set_card_sym; bool check_set_arguments(unsigned arity, sort * const * domain); func_decl * mk_const(sort* ty, unsigned arity, sort * const * domain); func_decl * mk_map(func_decl* f, unsigned arity, sort* const* domain); func_decl * mk_default(unsigned arity, sort* const* domain); func_decl * mk_select(unsigned arity, sort * const * domain); func_decl * mk_store(unsigned arity, sort * const * domain); func_decl * mk_array_ext(unsigned arity, sort * const * domain, unsigned i); func_decl * mk_set_union(unsigned arity, sort * const * domain); func_decl * mk_set_intersect(unsigned arity, sort * const * domain); func_decl * mk_set_difference(unsigned arity, sort * const * domain); func_decl * mk_set_complement(unsigned arity, sort * const * domain); func_decl * mk_set_subset(unsigned arity, sort * const * domain); func_decl * mk_as_array(func_decl * f); func_decl* mk_set_has_size(unsigned arity, sort * const* domain); func_decl* mk_set_card(unsigned arity, sort * const* domain); bool is_array_sort(sort* s) const; public: array_decl_plugin(); ~array_decl_plugin() override {} decl_plugin * mk_fresh() override { return alloc(array_decl_plugin); } // // Contract for sort: // parameters[0] - 1st dimension // ... // parameters[n-1] - nth dimension // parameters[n] - range // sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) override; // // Contract for func_decl: // parameters[0] - array sort // Contract for others: // no parameters func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) override; void get_op_names(svector & op_names, symbol const & logic) override; void get_sort_names(svector & sort_names, symbol const & logic) override; expr * get_some_value(sort * s) override; bool is_fully_interp(sort * s) const override; }; class array_recognizers { protected: family_id m_fid; public: array_recognizers(family_id fid):m_fid(fid) {} family_id get_family_id() const { return m_fid; } bool is_array(sort* s) const { return is_sort_of(s, m_fid, ARRAY_SORT);} bool is_array(expr* n) const { return is_array(get_sort(n)); } bool is_select(expr* n) const { return is_app_of(n, m_fid, OP_SELECT); } bool is_store(expr* n) const { return is_app_of(n, m_fid, OP_STORE); } bool is_const(expr* n) const { return is_app_of(n, m_fid, OP_CONST_ARRAY); } bool is_ext(expr* n) const { return is_app_of(n, m_fid, OP_ARRAY_EXT); } bool is_map(expr* n) const { return is_app_of(n, m_fid, OP_ARRAY_MAP); } bool is_as_array(expr * n) const { return is_app_of(n, m_fid, OP_AS_ARRAY); } bool is_as_array(expr * n, func_decl*& f) const { return is_as_array(n) && (f = get_as_array_func_decl(n), true); } bool is_set_has_size(expr* e) const { return is_app_of(e, m_fid, OP_SET_HAS_SIZE); } bool is_set_card(expr* e) const { return is_app_of(e, m_fid, OP_SET_CARD); } bool is_select(func_decl* f) const { return is_decl_of(f, m_fid, OP_SELECT); } bool is_store(func_decl* f) const { return is_decl_of(f, m_fid, OP_STORE); } bool is_const(func_decl* f) const { return is_decl_of(f, m_fid, OP_CONST_ARRAY); } 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_set_has_size(func_decl* f) const { return is_decl_of(f, m_fid, OP_SET_HAS_SIZE); } bool is_set_card(func_decl* f) const { return is_decl_of(f, m_fid, OP_SET_CARD); } 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; func_decl * get_map_func_decl(func_decl* f) const; func_decl * get_map_func_decl(expr* e) const { return get_map_func_decl(to_app(e)->get_decl()); } bool is_const(expr* e, expr*& v) const; bool is_store_ext(expr* e, expr_ref& a, expr_ref_vector& args, expr_ref& value); }; class array_util : public array_recognizers { ast_manager & m_manager; public: array_util(ast_manager& m); ast_manager & get_manager() const { return m_manager; } bool is_as_array_tree(expr * n); app * mk_store(unsigned num_args, expr * const * args) { return m_manager.mk_app(m_fid, OP_STORE, 0, nullptr, num_args, args); } app * mk_store(expr_ref_vector const& args) { return mk_store(args.size(), args.c_ptr()); } app * mk_store(ptr_vector const& args) { return mk_store(args.size(), args.c_ptr()); } app * mk_select(unsigned num_args, expr * const * args) { return m_manager.mk_app(m_fid, OP_SELECT, 0, nullptr, num_args, args); } app * mk_select(ptr_vector const& args) { return mk_select(args.size(), args.c_ptr()); } app * mk_select(expr_ref_vector const& args) { return mk_select(args.size(), args.c_ptr()); } app * mk_map(func_decl * f, unsigned num_args, expr * const * args) { parameter p(f); return m_manager.mk_app(m_fid, OP_ARRAY_MAP, 1, &p, num_args, args); } expr * mk_map_assoc(func_decl * f, unsigned num_args, expr * const * args) { expr* r = args[0]; for (unsigned i = 1; i < num_args; ++i) { expr* es[2] = { r, args[i] }; r = mk_map(f, 2, es); } return r; } app * mk_default(expr * a) { return m_manager.mk_app(m_fid, OP_ARRAY_DEFAULT, 0, nullptr, 1, &a); } app * mk_const_array(sort * s, expr * v) { parameter param(s); return m_manager.mk_app(m_fid, OP_CONST_ARRAY, 1, ¶m, 1, &v); } app * mk_empty_set(sort * s) { return mk_const_array(s, m_manager.mk_false()); } app * mk_full_set(sort * s) { return mk_const_array(s, m_manager.mk_true()); } app * mk_setminus(expr* s1, expr* s2) { return m_manager.mk_app(m_fid, OP_SET_DIFFERENCE, s1, s2); } app * mk_intersection(expr* s1, expr* s2) { return m_manager.mk_app(m_fid, OP_SET_INTERSECT, s1, s2); } app * mk_union(expr* s1, expr* s2) { return m_manager.mk_app(m_fid, OP_SET_UNION, s1, s2); } app* mk_has_size(expr* set, expr* n) { return m_manager.mk_app(m_fid, OP_SET_HAS_SIZE, set, n); } app* mk_card(expr* set) { return m_manager.mk_app(m_fid, OP_SET_CARD, set); } func_decl * mk_array_ext(sort* domain, unsigned i); sort * mk_array_sort(sort* dom, sort* range) { return mk_array_sort(1, &dom, range); } sort * mk_array_sort(unsigned arity, sort* const* domain, sort* range); app * mk_as_array(func_decl * f) { parameter param(f); return m_manager.mk_app(m_fid, OP_AS_ARRAY, 1, ¶m, 0, nullptr, nullptr); } sort* get_array_range_rec(sort* s) { while (is_array(s)) { s = get_array_range(s); } return s; } }; #endif /* ARRAY_DECL_PLUGIN_H_ */ z3-z3-4.8.7/src/ast/ast.cpp000066400000000000000000003655011356505360400153120ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: ast.cpp Abstract: Expression DAG Author: Leonardo de Moura (leonardo) 2006-09-28. Revision History: --*/ #include #include #include "ast/ast.h" #include "ast/ast_pp.h" #include "ast/ast_ll_pp.h" #include "util/buffer.h" #include "util/warning.h" #include "util/string_buffer.h" #include "ast/ast_util.h" #include "ast/ast_smt2_pp.h" #include "ast/array_decl_plugin.h" #include "ast/ast_translation.h" #include "util/z3_version.h" // ----------------------------------- // // parameter // // ----------------------------------- parameter::~parameter() { if (m_kind == PARAM_RATIONAL) { dealloc(m_rational); } } parameter::parameter(parameter const& other) { m_kind = PARAM_INT; m_int = 0; *this = other; } parameter& parameter::operator=(parameter const& other) { if (this == &other) { return *this; } if (m_kind == PARAM_RATIONAL) { dealloc(m_rational); } m_kind = other.m_kind; switch(other.m_kind) { case PARAM_INT: m_int = other.get_int(); break; case PARAM_AST: m_ast = other.get_ast(); break; case PARAM_SYMBOL: m_symbol = other.m_symbol; break; case PARAM_RATIONAL: m_rational = alloc(rational, other.get_rational()); break; case PARAM_DOUBLE: m_dval = other.m_dval; break; case PARAM_EXTERNAL: m_ext_id = other.m_ext_id; break; default: UNREACHABLE(); break; } return *this; } void parameter::init_eh(ast_manager & m) { if (is_ast()) { m.inc_ref(get_ast()); } } void parameter::del_eh(ast_manager & m, family_id fid) { if (is_ast()) { m.dec_ref(get_ast()); } else if (is_external()) { SASSERT(fid != null_family_id); decl_plugin * plugin = m.get_plugin(fid); if (plugin) { plugin->del(*this); } } } bool parameter::operator==(parameter const & p) const { if (m_kind != p.m_kind) return false; switch(m_kind) { case PARAM_INT: return m_int == p.m_int; case PARAM_AST: return m_ast == p.m_ast; case PARAM_SYMBOL: return get_symbol() == p.get_symbol(); case PARAM_RATIONAL: return get_rational() == p.get_rational(); case PARAM_DOUBLE: return m_dval == p.m_dval; case PARAM_EXTERNAL: return m_ext_id == p.m_ext_id; default: UNREACHABLE(); return false; } } unsigned parameter::hash() const { unsigned b = 0; switch(m_kind) { case PARAM_INT: b = m_int; break; case PARAM_AST: b = m_ast->hash(); break; case PARAM_SYMBOL: b = get_symbol().hash(); break; case PARAM_RATIONAL: b = get_rational().hash(); break; case PARAM_DOUBLE: b = static_cast(m_dval); break; case PARAM_EXTERNAL: b = m_ext_id; break; } return (b << 2) | m_kind; } std::ostream& parameter::display(std::ostream& out) const { switch(m_kind) { case PARAM_INT: return out << get_int(); case PARAM_SYMBOL: return out << get_symbol(); case PARAM_RATIONAL: return out << get_rational(); case PARAM_AST: return out << "#" << get_ast()->get_id(); case PARAM_DOUBLE: return out << m_dval; case PARAM_EXTERNAL: return out << "@" << m_ext_id; default: UNREACHABLE(); return out << "[invalid parameter]"; } } void display_parameters(std::ostream & out, unsigned n, parameter const * p) { if (n > 0) { out << "["; for (unsigned i = 0; i < n; i ++) out << p[i] << (i < n-1 ? ":" : ""); out << "]"; } } // ----------------------------------- // // family_manager // // ----------------------------------- family_id family_manager::mk_family_id(symbol const & s) { family_id r; if (m_families.find(s, r)) { return r; } r = m_next_id; m_next_id++; m_families.insert(s, r); m_names.push_back(s); return r; } family_id family_manager::get_family_id(symbol const & s) const { family_id r; if (m_families.find(s, r)) return r; else return null_family_id; } bool family_manager::has_family(symbol const & s) const { return m_families.contains(s); } // ----------------------------------- // // decl_info // // ----------------------------------- decl_info::decl_info(family_id family_id, decl_kind k, unsigned num_parameters, parameter const * parameters, bool private_params): m_family_id(family_id), m_kind(k), m_parameters(num_parameters, const_cast(parameters)), m_private_parameters(private_params) { } decl_info::decl_info(decl_info const& other) : m_family_id(other.m_family_id), m_kind(other.m_kind), m_parameters(other.m_parameters.size(), other.m_parameters.c_ptr()), m_private_parameters(other.m_private_parameters) { } void decl_info::init_eh(ast_manager & m) { for (parameter & p : m_parameters) { p.init_eh(m); } } void decl_info::del_eh(ast_manager & m) { for (parameter & p : m_parameters) { p.del_eh(m, m_family_id); } } struct decl_info_child_hash_proc { unsigned operator()(decl_info const * info, unsigned idx) const { return info->get_parameter(idx).hash(); } }; unsigned decl_info::hash() const { unsigned a = m_family_id; unsigned b = m_kind; unsigned c = get_num_parameters() == 0 ? 0 : get_composite_hash, decl_info_child_hash_proc>(this, get_num_parameters()); mix(a, b, c); return c; } bool decl_info::operator==(decl_info const & info) const { return m_family_id == info.m_family_id && m_kind == info.m_kind && compare_arrays(m_parameters.begin(), info.m_parameters.begin(), m_parameters.size()); } std::ostream & operator<<(std::ostream & out, decl_info const & info) { out << ":fid " << info.get_family_id() << " :decl-kind " << info.get_decl_kind() << " :parameters ("; for (unsigned i = 0; i < info.get_num_parameters(); i++) { if (i > 0) out << " "; out << info.get_parameter(i); } out << ")"; return out; } // ----------------------------------- // // sort_size // // ----------------------------------- std::ostream& operator<<(std::ostream& out, sort_size const & ss) { if (ss.is_infinite()) { return out << "infinite"; } else if (ss.is_very_big()) { return out << "very-big"; } else { SASSERT(ss.is_finite()); return out << ss.size(); } } // ----------------------------------- // // sort_info // // ----------------------------------- std::ostream & operator<<(std::ostream & out, sort_info const & info) { operator<<(out, static_cast(info)); out << " :size " << info.get_num_elements(); return out; } // ----------------------------------- // // func_decl_info // // ----------------------------------- func_decl_info::func_decl_info(family_id family_id, decl_kind k, unsigned num_parameters, parameter const * parameters): decl_info(family_id, k, num_parameters, parameters), m_left_assoc(false), m_right_assoc(false), m_flat_associative(false), m_commutative(false), m_chainable(false), m_pairwise(false), m_injective(false), m_idempotent(false), m_skolem(false), m_lambda(false) { } bool func_decl_info::operator==(func_decl_info const & info) const { return decl_info::operator==(info) && m_left_assoc == info.m_left_assoc && m_right_assoc == info.m_right_assoc && m_flat_associative == info.m_flat_associative && m_commutative == info.m_commutative && m_chainable == info.m_chainable && m_pairwise == info.m_pairwise && m_injective == info.m_injective && m_skolem == info.m_skolem && m_lambda == info.m_lambda; } std::ostream & operator<<(std::ostream & out, func_decl_info const & info) { operator<<(out, static_cast(info)); if (info.is_left_associative()) out << " :left-assoc "; if (info.is_right_associative()) out << " :right-assoc "; if (info.is_flat_associative()) out << " :flat-associative "; if (info.is_commutative()) out << " :commutative "; if (info.is_chainable()) out << " :chainable "; if (info.is_pairwise()) out << " :pairwise "; if (info.is_injective()) out << " :injective "; if (info.is_idempotent()) out << " :idempotent "; if (info.is_skolem()) out << " :skolem "; if (info.is_lambda()) out << " :lambda "; return out; } // ----------------------------------- // // ast // // ----------------------------------- static char const * g_ast_kind_names[] = {"application", "variable", "quantifier", "sort", "function declaration" }; char const * get_ast_kind_name(ast_kind k) { return g_ast_kind_names[k]; } // ----------------------------------- // // func_decl // // ----------------------------------- func_decl::func_decl(symbol const & name, unsigned arity, sort * const * domain, sort * range, func_decl_info * info): decl(AST_FUNC_DECL, name, info), m_arity(arity), m_range(range) { if (arity != 0) memcpy(const_cast(get_domain()), domain, sizeof(sort *) * arity); } // ----------------------------------- // // application // // ----------------------------------- static app_flags mk_const_flags() { app_flags r; r.m_depth = 1; r.m_ground = true; r.m_has_quantifiers = false; r.m_has_labels = false; return r; } static app_flags mk_default_app_flags() { app_flags r; r.m_depth = 1; r.m_ground = true; r.m_has_quantifiers = false; r.m_has_labels = false; return r; } app_flags app::g_constant_flags = mk_const_flags(); app::app(func_decl * decl, unsigned num_args, expr * const * args): expr(AST_APP), m_decl(decl), m_num_args(num_args) { for (unsigned i = 0; i < num_args; i++) m_args[i] = args[i]; } // ----------------------------------- // // quantifier // // ----------------------------------- quantifier::quantifier(quantifier_kind k, unsigned num_decls, sort * const * decl_sorts, symbol const * decl_names, expr * body, sort* s, int weight, symbol const & qid, symbol const & skid, unsigned num_patterns, expr * const * patterns, unsigned num_no_patterns, expr * const * no_patterns): expr(AST_QUANTIFIER), m_kind(k), m_num_decls(num_decls), m_expr(body), m_sort(s), m_depth(::get_depth(body) + 1), m_weight(weight), m_has_unused_vars(true), m_has_labels(::has_labels(body)), m_qid(qid), m_skid(skid), m_num_patterns(num_patterns), m_num_no_patterns(num_no_patterns) { SASSERT(m_num_patterns == 0 || m_num_no_patterns == 0); memcpy(const_cast(get_decl_sorts()), decl_sorts, sizeof(sort *) * num_decls); memcpy(const_cast(get_decl_names()), decl_names, sizeof(symbol) * num_decls); if (num_patterns != 0) memcpy(const_cast(get_patterns()), patterns, sizeof(expr *) * num_patterns); if (num_no_patterns != 0) memcpy(const_cast(get_no_patterns()), no_patterns, sizeof(expr *) * num_no_patterns); } quantifier::quantifier(unsigned num_decls, sort * const * decl_sorts, symbol const * decl_names, expr * body, sort* s): expr(AST_QUANTIFIER), m_kind(lambda_k), m_num_decls(num_decls), m_expr(body), m_sort(s), m_depth(::get_depth(body) + 1), m_weight(1), m_has_unused_vars(true), m_has_labels(::has_labels(body)), m_qid(symbol()), m_skid(symbol()), m_num_patterns(0), m_num_no_patterns(0) { memcpy(const_cast(get_decl_sorts()), decl_sorts, sizeof(sort *) * num_decls); memcpy(const_cast(get_decl_names()), decl_names, sizeof(symbol) * num_decls); } // ----------------------------------- // // Auxiliary functions // // ----------------------------------- sort * get_sort(expr const * n) { switch(n->get_kind()) { case AST_APP: return to_app(n)->get_decl()->get_range(); case AST_VAR: return to_var(n)->get_sort(); case AST_QUANTIFIER: return to_quantifier(n)->get_sort(); default: UNREACHABLE(); return nullptr; } } // ----------------------------------- // // AST hash-consing // // ----------------------------------- unsigned get_node_size(ast const * n) { switch(n->get_kind()) { case AST_SORT: return to_sort(n)->get_size(); case AST_FUNC_DECL: return to_func_decl(n)->get_size(); case AST_APP: return to_app(n)->get_size(); case AST_VAR: return to_var(n)->get_size(); case AST_QUANTIFIER: return to_quantifier(n)->get_size(); default: UNREACHABLE(); } return 0; } bool compare_nodes(ast const * n1, ast const * n2) { if (n1->get_kind() != n2->get_kind()) { return false; } switch (n1->get_kind()) { case AST_SORT: if ((to_sort(n1)->get_info() == nullptr) != (to_sort(n2)->get_info() == nullptr)) { return false; } if (to_sort(n1)->get_info() != nullptr && !(*to_sort(n1)->get_info() == *to_sort(n2)->get_info())) { return false; } return to_sort(n1)->get_name() == to_sort(n2)->get_name(); case AST_FUNC_DECL: if ((to_func_decl(n1)->get_info() == nullptr) != (to_func_decl(n2)->get_info() == nullptr)) { return false; } if (to_func_decl(n1)->get_info() != nullptr && !(*to_func_decl(n1)->get_info() == *to_func_decl(n2)->get_info())) { return false; } return to_func_decl(n1)->get_name() == to_func_decl(n2)->get_name() && to_func_decl(n1)->get_arity() == to_func_decl(n2)->get_arity() && to_func_decl(n1)->get_range() == to_func_decl(n2)->get_range() && compare_arrays(to_func_decl(n1)->get_domain(), to_func_decl(n2)->get_domain(), to_func_decl(n1)->get_arity()); case AST_APP: return to_app(n1)->get_decl() == to_app(n2)->get_decl() && to_app(n1)->get_num_args() == to_app(n2)->get_num_args() && compare_arrays(to_app(n1)->get_args(), to_app(n2)->get_args(), to_app(n1)->get_num_args()); case AST_VAR: return to_var(n1)->get_idx() == to_var(n2)->get_idx() && to_var(n1)->get_sort() == to_var(n2)->get_sort(); case AST_QUANTIFIER: return to_quantifier(n1)->get_kind() == to_quantifier(n2)->get_kind() && to_quantifier(n1)->get_num_decls() == to_quantifier(n2)->get_num_decls() && compare_arrays(to_quantifier(n1)->get_decl_sorts(), to_quantifier(n2)->get_decl_sorts(), to_quantifier(n1)->get_num_decls()) && compare_arrays(to_quantifier(n1)->get_decl_names(), to_quantifier(n2)->get_decl_names(), to_quantifier(n1)->get_num_decls()) && to_quantifier(n1)->get_expr() == to_quantifier(n2)->get_expr() && to_quantifier(n1)->get_weight() == to_quantifier(n2)->get_weight() && to_quantifier(n1)->get_num_patterns() == to_quantifier(n2)->get_num_patterns() && compare_arrays(to_quantifier(n1)->get_patterns(), to_quantifier(n2)->get_patterns(), to_quantifier(n1)->get_num_patterns()) && to_quantifier(n1)->get_num_no_patterns() == to_quantifier(n2)->get_num_no_patterns() && compare_arrays(to_quantifier(n1)->get_no_patterns(), to_quantifier(n2)->get_no_patterns(), to_quantifier(n1)->get_num_no_patterns()); default: UNREACHABLE(); } return false; } template inline unsigned ast_array_hash(T * const * array, unsigned size, unsigned init_value) { if (size == 0) return init_value; switch (size) { case 1: return combine_hash(array[0]->hash(), init_value); case 2: return combine_hash(combine_hash(array[0]->hash(), array[1]->hash()), init_value); case 3: return combine_hash(combine_hash(array[0]->hash(), array[1]->hash()), combine_hash(array[2]->hash(), init_value)); default: { unsigned a, b, c; a = b = 0x9e3779b9; c = init_value; while (size >= 3) { size--; a += array[size]->hash(); size--; b += array[size]->hash(); size--; c += array[size]->hash(); mix(a, b, c); } switch (size) { case 2: b += array[1]->hash(); Z3_fallthrough; case 1: c += array[0]->hash(); } mix(a, b, c); return c; } } } unsigned get_asts_hash(unsigned sz, ast * const* ns, unsigned init) { return ast_array_hash(ns, sz, init); } unsigned get_apps_hash(unsigned sz, app * const* ns, unsigned init) { return ast_array_hash(ns, sz, init); } unsigned get_exprs_hash(unsigned sz, expr * const* ns, unsigned init) { return ast_array_hash(ns, sz, init); } unsigned get_sorts_hash(unsigned sz, sort * const* ns, unsigned init) { return ast_array_hash(ns, sz, init); } unsigned get_decl_hash(unsigned sz, func_decl* const* ns, unsigned init) { return ast_array_hash(ns, sz, init); } unsigned get_node_hash(ast const * n) { unsigned a, b, c; switch (n->get_kind()) { case AST_SORT: if (to_sort(n)->get_info() == nullptr) return to_sort(n)->get_name().hash(); else return combine_hash(to_sort(n)->get_name().hash(), to_sort(n)->get_info()->hash()); case AST_FUNC_DECL: return ast_array_hash(to_func_decl(n)->get_domain(), to_func_decl(n)->get_arity(), to_func_decl(n)->get_info() == nullptr ? to_func_decl(n)->get_name().hash() : combine_hash(to_func_decl(n)->get_name().hash(), to_func_decl(n)->get_info()->hash())); case AST_APP: return ast_array_hash(to_app(n)->get_args(), to_app(n)->get_num_args(), to_app(n)->get_decl()->hash()); case AST_VAR: return combine_hash(to_var(n)->get_idx(), to_var(n)->get_sort()->hash()); case AST_QUANTIFIER: a = ast_array_hash(to_quantifier(n)->get_decl_sorts(), to_quantifier(n)->get_num_decls(), to_quantifier(n)->get_kind() == forall_k ? 31 : 19); b = to_quantifier(n)->get_num_patterns(); c = to_quantifier(n)->get_expr()->hash(); mix(a,b,c); return c; default: UNREACHABLE(); } return 0; } void ast_table::push_erase(ast * n) { // It uses two important properties: // 1. n is known to be in the table. // 2. operator== can be used instead of compare_nodes (big savings) unsigned mask = m_slots - 1; unsigned h = n->hash(); unsigned idx = h & mask; cell * c = m_table + idx; cell * prev = nullptr; while (true) { SASSERT(!c->is_free()); cell * next = c->m_next; if (c->m_data == n) { m_size--; if (prev == nullptr) { if (next == nullptr) { m_used_slots--; push_recycle_cell(c); c->mark_free(); SASSERT(c->is_free()); } else { *c = *next; next->m_data = n; push_recycle_cell(next); } } else { prev->m_next = next; push_recycle_cell(c); } return; } CHS_CODE(m_collisions++;); prev = c; c = next; SASSERT(c); } } ast* ast_table::pop_erase() { cell* c = m_tofree_cell; if (c == nullptr) { return nullptr; } if (c->is_free()) { // cell was marked free, should not be recycled. c->unmark_free(); m_tofree_cell = c->m_next; c->mark_free(); } else { // cell should be recycled with m_free_cell m_tofree_cell = c->m_next; recycle_cell(c); } return c->m_data; } // ----------------------------------- // // decl_plugin // // ----------------------------------- /** \brief Checks wether a log is being generated and, if necessary, adds the beginning of an "[attach-meaning]" line to that log. The theory solver should add some description of the meaning of the term in terms of the theory's internal reasoning to the end of the line and insert a line break. \param a the term that should be described. \return true if a log is being generated, false otherwise. */ bool decl_plugin::log_constant_meaning_prelude(app * a) { if (m_manager->has_trace_stream()) { m_manager->trace_stream() << "[attach-meaning] #" << a->get_id() << " " << m_manager->get_family_name(m_family_id).str() << " "; return true; } return false; } func_decl * decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned num_args, expr * const * args, sort * range) { ptr_buffer sorts; for (unsigned i = 0; i < num_args; i++) { sorts.push_back(m_manager->get_sort(args[i])); } return mk_func_decl(k, num_parameters, parameters, num_args, sorts.c_ptr(), range); } // ----------------------------------- // // basic_decl_plugin (i.e., builtin plugin) // // ----------------------------------- basic_decl_plugin::basic_decl_plugin(): m_bool_sort(nullptr), m_true_decl(nullptr), m_false_decl(nullptr), m_and_decl(nullptr), m_or_decl(nullptr), m_xor_decl(nullptr), m_not_decl(nullptr), m_implies_decl(nullptr), m_proof_sort(nullptr), m_undef_decl(nullptr), m_true_pr_decl(nullptr), m_asserted_decl(nullptr), m_goal_decl(nullptr), m_modus_ponens_decl(nullptr), m_reflexivity_decl(nullptr), m_symmetry_decl(nullptr), m_transitivity_decl(nullptr), m_quant_intro_decl(nullptr), m_and_elim_decl(nullptr), m_not_or_elim_decl(nullptr), m_rewrite_decl(nullptr), m_pull_quant_decl(nullptr), m_push_quant_decl(nullptr), m_elim_unused_vars_decl(nullptr), m_der_decl(nullptr), m_quant_inst_decl(nullptr), m_hypothesis_decl(nullptr), m_iff_true_decl(nullptr), m_iff_false_decl(nullptr), m_commutativity_decl(nullptr), m_def_axiom_decl(nullptr), m_lemma_decl(nullptr), m_def_intro_decl(nullptr), m_iff_oeq_decl(nullptr), m_skolemize_decl(nullptr), m_mp_oeq_decl(nullptr), m_assumption_add_decl(nullptr), m_lemma_add_decl(nullptr), m_th_assumption_add_decl(nullptr), m_th_lemma_add_decl(nullptr), m_redundant_del_decl(nullptr), m_clause_trail_decl(nullptr), m_hyper_res_decl0(nullptr) { } bool basic_decl_plugin::check_proof_sorts(basic_op_kind k, unsigned arity, sort * const * domain) const { if (k == PR_UNDEF) return arity == 0; if (arity == 0) return false; else { for (unsigned i = 0; i < arity - 1; i++) if (domain[i] != m_proof_sort) return false; #define is_array(_x_) true return domain[arity-1] == m_bool_sort || domain[arity-1] == m_proof_sort || is_array(domain[arity-1]); } } bool basic_decl_plugin::check_proof_args(basic_op_kind k, unsigned num_args, expr * const * args) const { if (k == PR_UNDEF) return num_args == 0; if (num_args == 0) return false; else { for (unsigned i = 0; i < num_args - 1; i++) if (m_manager->get_sort(args[i]) != m_proof_sort) return false; return m_manager->get_sort(args[num_args - 1]) == m_bool_sort || m_manager->get_sort(args[num_args - 1]) == m_proof_sort || is_lambda(args[num_args-1]); } } func_decl * basic_decl_plugin::mk_bool_op_decl(char const * name, basic_op_kind k, unsigned num_args, bool assoc, bool comm, bool idempotent, bool flat_associative, bool chainable) { ptr_buffer domain; for (unsigned i = 0; i < num_args; i++) domain.push_back(m_bool_sort); func_decl_info info(m_family_id, k); info.set_associative(assoc); info.set_flat_associative(flat_associative); info.set_commutative(comm); info.set_idempotent(idempotent); info.set_chainable(chainable); func_decl * d = m_manager->mk_func_decl(symbol(name), num_args, domain.c_ptr(), m_bool_sort, info); m_manager->inc_ref(d); return d; } func_decl * basic_decl_plugin::mk_implies_decl() { sort * domain[2] = { m_bool_sort, m_bool_sort }; func_decl_info info(m_family_id, OP_IMPLIES); info.set_right_associative(); func_decl * d = m_manager->mk_func_decl(symbol("=>"), 2, domain, m_bool_sort, info); m_manager->inc_ref(d); return d; } func_decl * basic_decl_plugin::mk_proof_decl( char const * name, basic_op_kind k, unsigned num_parameters, parameter const* params, unsigned num_parents) { ptr_buffer domain; for (unsigned i = 0; i < num_parents; i++) domain.push_back(m_proof_sort); domain.push_back(m_bool_sort); func_decl_info info(m_family_id, k, num_parameters, params); return m_manager->mk_func_decl(symbol(name), num_parents+1, domain.c_ptr(), m_proof_sort, info); } func_decl * basic_decl_plugin::mk_proof_decl(char const * name, basic_op_kind k, unsigned num_parents) { ptr_buffer domain; for (unsigned i = 0; i < num_parents; i++) domain.push_back(m_proof_sort); domain.push_back(m_bool_sort); func_decl * d = m_manager->mk_func_decl(symbol(name), num_parents+1, domain.c_ptr(), m_proof_sort, func_decl_info(m_family_id, k)); m_manager->inc_ref(d); return d; } func_decl * basic_decl_plugin::mk_compressed_proof_decl(char const * name, basic_op_kind k, unsigned num_parents) { ptr_buffer domain; for (unsigned i = 0; i < num_parents; i++) domain.push_back(m_proof_sort); func_decl * d = m_manager->mk_func_decl(symbol(name), num_parents, domain.c_ptr(), m_proof_sort, func_decl_info(m_family_id, k)); m_manager->inc_ref(d); return d; } func_decl * basic_decl_plugin::mk_proof_decl(char const * name, basic_op_kind k, unsigned num_parents, ptr_vector & cache) { if (num_parents >= cache.size()) { cache.resize(num_parents+1); } if (cache[num_parents] == 0) { cache[num_parents] = mk_proof_decl(name, k, num_parents); } return cache[num_parents]; } func_decl * basic_decl_plugin::mk_proof_decl(basic_op_kind k, unsigned num_parameters, parameter const* params, unsigned num_parents) { switch(k) { case PR_TH_LEMMA: { return mk_proof_decl("th-lemma", k, num_parameters, params, num_parents); } case PR_QUANT_INST: { SASSERT(num_parents == 0); return mk_proof_decl("quant-inst", k, num_parameters, params, num_parents); } case PR_HYPER_RESOLVE: { return mk_proof_decl("hyper-res", k, num_parameters, params, num_parents); } default: UNREACHABLE(); return nullptr; } } #define MK_DECL(_decl_,_mk_decl_) if (!_decl_) _decl_ = _mk_decl_; return _decl_; func_decl * basic_decl_plugin::mk_proof_decl(char const* name, basic_op_kind k, unsigned num_parents, func_decl*& fn) { if (!fn) { fn = mk_proof_decl(name, k, num_parents); } return fn; } func_decl * basic_decl_plugin::mk_proof_decl(basic_op_kind k, unsigned num_parents) { switch (static_cast(k)) { // // A description of the semantics of the proof // declarations is provided in z3_api.h // case PR_UNDEF: return m_undef_decl; case PR_TRUE: return mk_proof_decl("true-axiom", k, 0, m_true_pr_decl); case PR_ASSERTED: return mk_proof_decl("asserted", k, 0, m_asserted_decl); case PR_GOAL: return mk_proof_decl("goal", k, 2, m_goal_decl); case PR_MODUS_PONENS: return mk_proof_decl("mp", k, 2, m_modus_ponens_decl); case PR_REFLEXIVITY: return mk_proof_decl("refl", k, 0, m_reflexivity_decl); case PR_SYMMETRY: return mk_proof_decl("symm", k, 1, m_symmetry_decl); case PR_TRANSITIVITY: return mk_proof_decl("trans", k, 2, m_transitivity_decl); case PR_TRANSITIVITY_STAR: return mk_proof_decl("trans*", k, num_parents, m_transitivity_star_decls); case PR_MONOTONICITY: return mk_proof_decl("monotonicity", k, num_parents, m_monotonicity_decls); case PR_QUANT_INTRO: return mk_proof_decl("quant-intro", k, 1, m_quant_intro_decl); case PR_BIND: UNREACHABLE(); case PR_DISTRIBUTIVITY: return mk_proof_decl("distributivity", k, num_parents, m_distributivity_decls); case PR_AND_ELIM: return mk_proof_decl("and-elim", k, 1, m_and_elim_decl); case PR_NOT_OR_ELIM: return mk_proof_decl("not-or-elim", k, 1, m_not_or_elim_decl); case PR_REWRITE: return mk_proof_decl("rewrite", k, 0, m_rewrite_decl); case PR_REWRITE_STAR: return mk_proof_decl("rewrite*", k, num_parents, m_rewrite_star_decls); case PR_PULL_QUANT: return mk_proof_decl("pull-quant", k, 0, m_pull_quant_decl); case PR_PUSH_QUANT: return mk_proof_decl("push-quant", k, 0, m_push_quant_decl); case PR_ELIM_UNUSED_VARS: return mk_proof_decl("elim-unused", k, 0, m_elim_unused_vars_decl); case PR_DER: return mk_proof_decl("der", k, 0, m_der_decl); case PR_QUANT_INST: return mk_proof_decl("quant-inst", k, 0, m_quant_inst_decl); case PR_HYPOTHESIS: return mk_proof_decl("hypothesis", k, 0, m_hypothesis_decl); case PR_LEMMA: return mk_proof_decl("lemma", k, 1, m_lemma_decl); case PR_UNIT_RESOLUTION: return mk_proof_decl("unit-resolution", k, num_parents, m_unit_resolution_decls); case PR_IFF_TRUE: return mk_proof_decl("iff-true", k, 1, m_iff_true_decl); case PR_IFF_FALSE: return mk_proof_decl("iff-false", k, 1, m_iff_false_decl); case PR_COMMUTATIVITY: return mk_proof_decl("commutativity", k, 0, m_commutativity_decl); case PR_DEF_AXIOM: return mk_proof_decl("def-axiom", k, 0, m_def_axiom_decl); case PR_DEF_INTRO: return mk_proof_decl("intro-def", k, 0, m_def_intro_decl); case PR_APPLY_DEF: return mk_proof_decl("apply-def", k, num_parents, m_apply_def_decls); case PR_IFF_OEQ: return mk_proof_decl("iff~", k, 1, m_iff_oeq_decl); case PR_NNF_POS: return mk_proof_decl("nnf-pos", k, num_parents, m_nnf_pos_decls); case PR_NNF_NEG: return mk_proof_decl("nnf-neg", k, num_parents, m_nnf_neg_decls); case PR_SKOLEMIZE: return mk_proof_decl("sk", k, 0, m_skolemize_decl); case PR_MODUS_PONENS_OEQ: return mk_proof_decl("mp~", k, 2, m_mp_oeq_decl); case PR_TH_LEMMA: return mk_proof_decl("th-lemma", k, num_parents, m_th_lemma_decls); case PR_HYPER_RESOLVE: return mk_proof_decl("hyper-res", k, num_parents, m_hyper_res_decl0); case PR_ASSUMPTION_ADD: return mk_proof_decl("add-assume", k, num_parents, m_assumption_add_decl); case PR_LEMMA_ADD: return mk_proof_decl("add-lemma", k, num_parents, m_lemma_add_decl); case PR_TH_ASSUMPTION_ADD: return mk_proof_decl("add-th-assume", k, num_parents, m_th_assumption_add_decl); case PR_TH_LEMMA_ADD: return mk_proof_decl("add-th-lemma", k, num_parents, m_th_lemma_add_decl); case PR_REDUNDANT_DEL: return mk_proof_decl("del-redundant", k, num_parents, m_redundant_del_decl); case PR_CLAUSE_TRAIL: return mk_proof_decl("proof-trail", k, num_parents, m_clause_trail_decl); default: UNREACHABLE(); return nullptr; } } void basic_decl_plugin::set_manager(ast_manager * m, family_id id) { decl_plugin::set_manager(m, id); m_bool_sort = m->mk_sort(symbol("Bool"), sort_info(id, BOOL_SORT, sort_size(2))); m->inc_ref(m_bool_sort); m_true_decl = mk_bool_op_decl("true", OP_TRUE); m_false_decl = mk_bool_op_decl("false", OP_FALSE); m_and_decl = mk_bool_op_decl("and", OP_AND, 2, true, true, true, true); m_or_decl = mk_bool_op_decl("or", OP_OR, 2, true, true, true, true); m_xor_decl = mk_bool_op_decl("xor", OP_XOR, 2, true, true); m_not_decl = mk_bool_op_decl("not", OP_NOT, 1); m_implies_decl = mk_implies_decl(); m_proof_sort = m->mk_sort(symbol("Proof"), sort_info(id, PROOF_SORT)); m->inc_ref(m_proof_sort); m_undef_decl = mk_compressed_proof_decl("undef", PR_UNDEF, 0); } void basic_decl_plugin::get_sort_names(svector & sort_names, symbol const & logic) { if (logic == symbol::null) sort_names.push_back(builtin_name("bool", BOOL_SORT)); sort_names.push_back(builtin_name("Bool", BOOL_SORT)); } void basic_decl_plugin::get_op_names(svector & op_names, symbol const & logic) { op_names.push_back(builtin_name("true", OP_TRUE)); op_names.push_back(builtin_name("false", OP_FALSE)); op_names.push_back(builtin_name("=", OP_EQ)); op_names.push_back(builtin_name("distinct", OP_DISTINCT)); op_names.push_back(builtin_name("ite", OP_ITE)); op_names.push_back(builtin_name("and", OP_AND)); op_names.push_back(builtin_name("or", OP_OR)); op_names.push_back(builtin_name("xor", OP_XOR)); op_names.push_back(builtin_name("not", OP_NOT)); op_names.push_back(builtin_name("=>", OP_IMPLIES)); if (logic == symbol::null) { // user friendly aliases op_names.push_back(builtin_name("implies", OP_IMPLIES)); op_names.push_back(builtin_name("iff", OP_EQ)); op_names.push_back(builtin_name("if_then_else", OP_ITE)); op_names.push_back(builtin_name("if", OP_ITE)); op_names.push_back(builtin_name("&&", OP_AND)); op_names.push_back(builtin_name("||", OP_OR)); op_names.push_back(builtin_name("equals", OP_EQ)); op_names.push_back(builtin_name("equiv", OP_EQ)); } } bool basic_decl_plugin::is_value(app* a) const { return a->get_decl() == m_true_decl || a->get_decl() == m_false_decl; } bool basic_decl_plugin::is_unique_value(app* a) const { return is_value(a); } void basic_decl_plugin::finalize() { #define DEC_REF(FIELD) if (FIELD) { m_manager->dec_ref(FIELD); } #define DEC_ARRAY_REF(FIELD) m_manager->dec_array_ref(FIELD.size(), FIELD.begin()) DEC_REF(m_bool_sort); DEC_REF(m_true_decl); DEC_REF(m_false_decl); DEC_REF(m_and_decl); DEC_REF(m_or_decl); DEC_REF(m_not_decl); DEC_REF(m_xor_decl); DEC_REF(m_implies_decl); DEC_ARRAY_REF(m_eq_decls); DEC_ARRAY_REF(m_ite_decls); DEC_ARRAY_REF(m_oeq_decls); DEC_REF(m_proof_sort); DEC_REF(m_undef_decl); DEC_REF(m_true_pr_decl); DEC_REF(m_asserted_decl); DEC_REF(m_goal_decl); DEC_REF(m_modus_ponens_decl); DEC_REF(m_reflexivity_decl); DEC_REF(m_symmetry_decl); DEC_REF(m_transitivity_decl); DEC_REF(m_quant_intro_decl); DEC_REF(m_and_elim_decl); DEC_REF(m_not_or_elim_decl); DEC_REF(m_rewrite_decl); DEC_REF(m_pull_quant_decl); DEC_REF(m_push_quant_decl); DEC_REF(m_elim_unused_vars_decl); DEC_REF(m_der_decl); DEC_REF(m_quant_inst_decl); DEC_ARRAY_REF(m_monotonicity_decls); DEC_ARRAY_REF(m_transitivity_star_decls); DEC_ARRAY_REF(m_distributivity_decls); DEC_ARRAY_REF(m_assoc_flat_decls); DEC_ARRAY_REF(m_rewrite_star_decls); DEC_REF(m_hypothesis_decl); DEC_REF(m_iff_true_decl); DEC_REF(m_iff_false_decl); DEC_REF(m_commutativity_decl); DEC_REF(m_def_axiom_decl); DEC_REF(m_lemma_decl); DEC_ARRAY_REF(m_unit_resolution_decls); DEC_REF(m_def_intro_decl); DEC_REF(m_iff_oeq_decl); DEC_REF(m_skolemize_decl); DEC_REF(m_mp_oeq_decl); DEC_REF(m_assumption_add_decl); DEC_REF(m_lemma_add_decl); DEC_REF(m_th_assumption_add_decl); DEC_REF(m_th_lemma_add_decl); DEC_REF(m_redundant_del_decl); DEC_REF(m_clause_trail_decl); DEC_ARRAY_REF(m_apply_def_decls); DEC_ARRAY_REF(m_nnf_pos_decls); DEC_ARRAY_REF(m_nnf_neg_decls); DEC_ARRAY_REF(m_th_lemma_decls); DEC_REF(m_hyper_res_decl0); } sort * basic_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) { if (k == BOOL_SORT) return m_bool_sort; SASSERT(k == PROOF_SORT); return m_proof_sort; } func_decl * basic_decl_plugin::mk_eq_decl_core(char const * name, decl_kind k, sort * s, ptr_vector & cache) { unsigned id = s->get_decl_id(); force_ptr_array_size(cache, id + 1); if (cache[id] == 0) { sort * domain[2] = { s, s}; func_decl_info info(m_family_id, k); info.set_commutative(); info.set_chainable(); func_decl * decl = m_manager->mk_func_decl(symbol(name), 2, domain, m_bool_sort, info); SASSERT(decl->is_chainable()); cache[id] = decl; m_manager->inc_ref(decl); } return cache[id]; } func_decl * basic_decl_plugin::mk_ite_decl(sort * s) { unsigned id = s->get_decl_id(); force_ptr_array_size(m_ite_decls, id + 1); if (m_ite_decls[id] == 0) { sort * domain[3] = { m_bool_sort, s, s}; func_decl * decl = m_manager->mk_func_decl(symbol("if"), 3, domain, s, func_decl_info(m_family_id, OP_ITE)); m_ite_decls[id] = decl; m_manager->inc_ref(decl); } return m_ite_decls[id]; } sort* basic_decl_plugin::join(unsigned n, sort* const* srts) { SASSERT(n > 0); sort* s = srts[0]; while (n > 1) { ++srts; --n; s = join(s, *srts); } return s; } sort* basic_decl_plugin::join(unsigned n, expr* const* es) { SASSERT(n > 0); sort* s = m_manager->get_sort(*es); while (n > 1) { ++es; --n; s = join(s, m_manager->get_sort(*es)); } return s; } sort* basic_decl_plugin::join(sort* s1, sort* s2) { if (s1 == s2) return s1; if (s1->get_family_id() == m_manager->m_arith_family_id && s2->get_family_id() == m_manager->m_arith_family_id) { if (s1->get_decl_kind() == REAL_SORT) { return s1; } return s2; } std::ostringstream buffer; buffer << "Sorts " << mk_pp(s1, *m_manager) << " and " << mk_pp(s2, *m_manager) << " are incompatible"; throw ast_exception(buffer.str()); } func_decl * basic_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { switch (static_cast(k)) { case OP_TRUE: return m_true_decl; case OP_FALSE: return m_false_decl; case OP_AND: return m_and_decl; case OP_OR: return m_or_decl; case OP_NOT: return m_not_decl; case OP_IMPLIES: return m_implies_decl; case OP_XOR: return m_xor_decl; case OP_ITE: return arity == 3 ? mk_ite_decl(join(domain[1], domain[2])) : nullptr; // eq and oeq must have at least two arguments, they can have more since they are chainable case OP_EQ: return arity >= 2 ? mk_eq_decl_core("=", OP_EQ, join(arity, domain), m_eq_decls) : nullptr; case OP_OEQ: return arity >= 2 ? mk_eq_decl_core("~", OP_OEQ, join(arity, domain), m_oeq_decls) : nullptr; case PR_BIND: { func_decl_info info(m_family_id, PR_BIND); return m_manager->mk_func_decl(symbol("proof-bind"), arity, domain, m_proof_sort, info); } case OP_DISTINCT: { func_decl_info info(m_family_id, OP_DISTINCT); info.set_pairwise(); for (unsigned i = 1; i < arity; i++) { if (domain[i] != domain[0]) { std::ostringstream buffer; buffer << "Sort mismatch between first argument and argument " << (i+1); throw ast_exception(buffer.str()); } } return m_manager->mk_func_decl(symbol("distinct"), arity, domain, m_bool_sort, info); } default: break; } SASSERT(is_proof(k)); if (!check_proof_sorts(static_cast(k), arity, domain)) m_manager->raise_exception("Invalid proof object."); if (num_parameters == 0) { return mk_proof_decl(static_cast(k), arity - 1); } return mk_proof_decl(static_cast(k), num_parameters, parameters, arity - 1); } func_decl * basic_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned num_args, expr * const * args, sort * range) { switch (static_cast(k)) { case OP_TRUE: return m_true_decl; case OP_FALSE: return m_false_decl; case OP_AND: return m_and_decl; case OP_OR: return m_or_decl; case OP_NOT: return m_not_decl; case OP_IMPLIES: return m_implies_decl; case OP_XOR: return m_xor_decl; case OP_ITE: return num_args == 3 ? mk_ite_decl(join(m_manager->get_sort(args[1]), m_manager->get_sort(args[2]))): nullptr; // eq and oeq must have at least two arguments, they can have more since they are chainable case OP_EQ: return num_args >= 2 ? mk_eq_decl_core("=", OP_EQ, join(num_args, args), m_eq_decls) : nullptr; case OP_OEQ: return num_args >= 2 ? mk_eq_decl_core("~", OP_OEQ, join(num_args, args), m_oeq_decls) : nullptr; case OP_DISTINCT: return decl_plugin::mk_func_decl(k, num_parameters, parameters, num_args, args, range); case PR_BIND: { ptr_buffer sorts; for (unsigned i = 0; i < num_args; ++i) sorts.push_back(m_manager->get_sort(args[i])); return mk_func_decl(k, num_parameters, parameters, num_args, sorts.c_ptr(), range); } default: break; } SASSERT(is_proof(k)); if (!check_proof_args(static_cast(k), num_args, args)) m_manager->raise_exception("Invalid proof object."); if (num_parameters == 0) { return mk_proof_decl(static_cast(k), num_args - 1); } return mk_proof_decl(static_cast(k), num_parameters, parameters, num_args - 1); } expr * basic_decl_plugin::get_some_value(sort * s) { if (s == m_bool_sort) return m_manager->mk_false(); return nullptr; } bool basic_recognizers::is_ite(expr const * n, expr * & t1, expr * & t2, expr * & t3) const { if (is_ite(n)) { t1 = to_app(n)->get_arg(0); t2 = to_app(n)->get_arg(1); t3 = to_app(n)->get_arg(2); return true; } return false; } // ----------------------------------- // // label_decl_plugin // // ----------------------------------- label_decl_plugin::label_decl_plugin(): m_lblpos("lblpos"), m_lblneg("lblneg"), m_lbllit("lbl-lit") { } label_decl_plugin::~label_decl_plugin() { } void label_decl_plugin::set_manager(ast_manager * m, family_id id) { decl_plugin::set_manager(m, id); } sort * label_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) { UNREACHABLE(); return nullptr; } func_decl * label_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { if (k == OP_LABEL) { if (arity != 1 || num_parameters < 2 || !parameters[0].is_int() || !parameters[1].is_symbol() || !m_manager->is_bool(domain[0])) { m_manager->raise_exception("invalid label declaration"); return nullptr; } for (unsigned i = 2; i < num_parameters; i++) { if (!parameters[i].is_symbol()) { m_manager->raise_exception("invalid label declaration"); return nullptr; } } return m_manager->mk_func_decl(parameters[0].get_int() ? m_lblpos : m_lblneg, arity, domain, domain[0], func_decl_info(m_family_id, OP_LABEL, num_parameters, parameters)); } else { SASSERT(k == OP_LABEL_LIT); if (arity != 0) { m_manager->raise_exception("invalid label literal declaration"); return nullptr; } for (unsigned i = 0; i < num_parameters; i++) { if (!parameters[i].is_symbol()) { m_manager->raise_exception("invalid label literal declaration"); return nullptr; } } return m_manager->mk_func_decl(m_lbllit, 0, static_cast(nullptr), m_manager->mk_bool_sort(), func_decl_info(m_family_id, OP_LABEL_LIT, num_parameters, parameters)); } } // ----------------------------------- // // pattern_decl_plugin // // ----------------------------------- sort * pattern_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) { UNREACHABLE(); return nullptr; } func_decl * pattern_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { return m_manager->mk_func_decl(symbol("pattern"), arity, domain, m_manager->mk_bool_sort(), // the range can be an arbitrary sort. func_decl_info(m_family_id, OP_PATTERN)); } // ----------------------------------- // // model_value_decl_plugin // // ----------------------------------- sort * model_value_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) { UNREACHABLE(); return nullptr; } func_decl * model_value_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { SASSERT(k == OP_MODEL_VALUE); if (arity != 0 || num_parameters != 2 || !parameters[0].is_int() || !parameters[1].is_ast() || !is_sort(parameters[1].get_ast())) { m_manager->raise_exception("invalid model value"); return nullptr; } int idx = parameters[0].get_int(); sort * s = to_sort(parameters[1].get_ast()); string_buffer<64> buffer; buffer << s->get_name().bare_str() << "!val!" << idx; func_decl_info info(m_family_id, k, num_parameters, parameters); info.m_private_parameters = true; return m_manager->mk_func_decl(symbol(buffer.c_str()), 0, static_cast(nullptr), s, info); } bool model_value_decl_plugin::is_value(app* n) const { return is_app_of(n, m_family_id, OP_MODEL_VALUE); } bool model_value_decl_plugin::is_unique_value(app* n) const { return is_value(n); } // ----------------------------------- // // user_sort_plugin // // ----------------------------------- sort * user_sort_plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) { SASSERT(m_family_id != null_family_id); SASSERT(k < static_cast(m_sort_names.size())); sort_info si(m_family_id, k, num_parameters, parameters); return m_manager->mk_sort(m_sort_names[k], si); } func_decl * user_sort_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { UNREACHABLE(); return nullptr; } decl_kind user_sort_plugin::register_name(symbol s) { decl_kind k; if (m_name2decl_kind.find(s, k)) return k; k = m_sort_names.size(); m_sort_names.push_back(s); m_name2decl_kind.insert(s, k); return k; } decl_plugin * user_sort_plugin::mk_fresh() { user_sort_plugin * p = alloc(user_sort_plugin); for (symbol const& s : m_sort_names) p->register_name(s); return p; } // ----------------------------------- // // ast_manager // // ----------------------------------- ast_manager::ast_manager(proof_gen_mode m, char const * trace_file, bool is_format_manager): m_alloc("ast_manager"), m_expr_array_manager(*this, m_alloc), m_expr_dependency_manager(*this, m_alloc), m_expr_dependency_array_manager(*this, m_alloc), m_proof_mode(m), m_trace_stream(nullptr), m_trace_stream_owner(false), m_rec_fun(":rec-fun"), m_lambda_def(":lambda-def") { if (trace_file) { m_trace_stream = alloc(std::fstream, trace_file, std::ios_base::out); m_trace_stream_owner = true; *m_trace_stream << "[tool-version] Z3 " << Z3_MAJOR_VERSION << "." << Z3_MINOR_VERSION << "." << Z3_BUILD_NUMBER << "\n"; } if (!is_format_manager) m_format_manager = alloc(ast_manager, PGM_DISABLED, m_trace_stream, true); else m_format_manager = nullptr; init(); } ast_manager::ast_manager(proof_gen_mode m, std::fstream * trace_stream, bool is_format_manager): m_alloc("ast_manager"), m_expr_array_manager(*this, m_alloc), m_expr_dependency_manager(*this, m_alloc), m_expr_dependency_array_manager(*this, m_alloc), m_proof_mode(m), m_trace_stream(trace_stream), m_trace_stream_owner(false), m_rec_fun(":rec-fun"), m_lambda_def(":lambda-def") { if (!is_format_manager) m_format_manager = alloc(ast_manager, PGM_DISABLED, trace_stream, true); else m_format_manager = nullptr; init(); } ast_manager::ast_manager(ast_manager const & src, bool disable_proofs): m_alloc("ast_manager"), m_expr_array_manager(*this, m_alloc), m_expr_dependency_manager(*this, m_alloc), m_expr_dependency_array_manager(*this, m_alloc), m_proof_mode(disable_proofs ? PGM_DISABLED : src.m_proof_mode), m_trace_stream(src.m_trace_stream), m_trace_stream_owner(false), m_rec_fun(":rec-fun"), m_lambda_def(":lambda-def") { SASSERT(!src.is_format_manager()); 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) { m_fresh_id = std::max(m_fresh_id, m.m_fresh_id); } void ast_manager::init() { m_int_real_coercions = true; m_debug_ref_count = false; m_fresh_id = 0; m_expr_id_gen.reset(0); m_decl_id_gen.reset(c_first_decl_id); m_some_value_proc = nullptr; m_basic_family_id = mk_family_id("basic"); m_label_family_id = mk_family_id("label"); m_pattern_family_id = mk_family_id("pattern"); m_model_value_family_id = mk_family_id("model-value"); m_user_sort_family_id = mk_family_id("user-sort"); m_arith_family_id = mk_family_id("arith"); basic_decl_plugin * plugin = alloc(basic_decl_plugin); register_plugin(m_basic_family_id, plugin); m_bool_sort = plugin->mk_bool_sort(); inc_ref(m_bool_sort); m_proof_sort = plugin->mk_proof_sort(); inc_ref(m_proof_sort); m_undef_proof = mk_const(m_basic_family_id, PR_UNDEF); inc_ref(m_undef_proof); register_plugin(m_label_family_id, alloc(label_decl_plugin)); register_plugin(m_pattern_family_id, alloc(pattern_decl_plugin)); register_plugin(m_model_value_family_id, alloc(model_value_decl_plugin)); register_plugin(m_user_sort_family_id, alloc(user_sort_plugin)); m_true = mk_const(m_basic_family_id, OP_TRUE); inc_ref(m_true); m_false = mk_const(m_basic_family_id, OP_FALSE); inc_ref(m_false); } template static void mark_array_ref(ast_mark& mark, unsigned sz, T * const * a) { for(unsigned i = 0; i < sz; i++) { mark.mark(a[i], true); } } static void mark_array_ref(ast_mark& mark, unsigned sz, parameter const * a) { for(unsigned i = 0; i < sz; i++) { if (a[i].is_ast()) { mark.mark(a[i].get_ast(), true); } } } ast_manager::~ast_manager() { SASSERT(is_format_manager() || !m_family_manager.has_family(symbol("format"))); dec_ref(m_bool_sort); dec_ref(m_proof_sort); dec_ref(m_true); dec_ref(m_false); dec_ref(m_undef_proof); for (decl_plugin* p : m_plugins) { if (p) p->finalize(); } for (decl_plugin* p : m_plugins) { if (p) dealloc(p); } m_plugins.reset(); while (!m_ast_table.empty()) { DEBUG_CODE(std::cout << "ast_manager LEAKED: " << m_ast_table.size() << std::endl;); ptr_vector roots; ast_mark mark; for (ast * n : m_ast_table) { switch (n->get_kind()) { case AST_SORT: { sort_info* info = to_sort(n)->get_info(); if (info != nullptr) { mark_array_ref(mark, info->get_num_parameters(), info->get_parameters()); } break; } case AST_FUNC_DECL: { func_decl_info* info = to_func_decl(n)->get_info(); if (info != nullptr) { mark_array_ref(mark, info->get_num_parameters(), info->get_parameters()); } mark_array_ref(mark, to_func_decl(n)->get_arity(), to_func_decl(n)->get_domain()); mark.mark(to_func_decl(n)->get_range(), true); break; } case AST_APP: mark.mark(to_app(n)->get_decl(), true); mark_array_ref(mark, to_app(n)->get_num_args(), to_app(n)->get_args()); break; case AST_VAR: mark.mark(to_var(n)->get_sort(), true); break; case AST_QUANTIFIER: mark_array_ref(mark, to_quantifier(n)->get_num_decls(), to_quantifier(n)->get_decl_sorts()); mark.mark(to_quantifier(n)->get_expr(), true); mark_array_ref(mark, to_quantifier(n)->get_num_patterns(), to_quantifier(n)->get_patterns()); mark_array_ref(mark, to_quantifier(n)->get_num_no_patterns(), to_quantifier(n)->get_no_patterns()); break; } } for (ast * n : m_ast_table) { if (!mark.is_marked(n)) { roots.push_back(n); } } SASSERT(!roots.empty()); for (unsigned i = 0; i < roots.size(); ++i) { ast* a = roots[i]; DEBUG_CODE( std::cout << "Leaked: "; if (is_sort(a)) { std::cout << to_sort(a)->get_name() << "\n"; } else { std::cout << mk_ll_pp(a, *this, false) << "id: " << a->get_id() << "\n"; }); a->m_ref_count = 0; delete_node(a); } } if (m_format_manager != nullptr) dealloc(m_format_manager); if (m_trace_stream_owner) { std::fstream & tmp = * m_trace_stream; tmp << "[eof]\n"; tmp.close(); dealloc(m_trace_stream); m_trace_stream = nullptr; } } void ast_manager::compact_memory() { m_alloc.consolidate(); unsigned capacity = m_ast_table.capacity(); if (capacity > 4*m_ast_table.size()) { ast_table new_ast_table; for (ast* curr : m_ast_table) new_ast_table.insert(curr); m_ast_table.swap(new_ast_table); IF_VERBOSE(10, verbose_stream() << "(ast-table :prev-capacity " << capacity << " :capacity " << m_ast_table.capacity() << " :size " << m_ast_table.size() << ")\n";); } else { IF_VERBOSE(10, verbose_stream() << "(ast-table :capacity " << m_ast_table.capacity() << " :size " << m_ast_table.size() << ")\n";); } } void ast_manager::compress_ids() { ptr_vector asts; m_expr_id_gen.cleanup(); m_decl_id_gen.cleanup(c_first_decl_id); for (ast * n : m_ast_table) { if (is_decl(n)) n->m_id = m_decl_id_gen.mk(); else n->m_id = m_expr_id_gen.mk(); asts.push_back(n); } m_ast_table.finalize(); for (ast* a : asts) m_ast_table.insert(a); } void ast_manager::raise_exception(char const * msg) { throw ast_exception(msg); } void ast_manager::raise_exception(std::string && msg) { throw ast_exception(std::move(msg)); } std::ostream& ast_manager::display(std::ostream& out, parameter const& p) { switch (p.get_kind()) { case parameter::PARAM_AST: return out << mk_pp(p.get_ast(), *this); default: return p.display(out); } return out; } void ast_manager::copy_families_plugins(ast_manager const & from) { TRACE("copy_families_plugins", tout << "target:\n"; for (family_id fid = 0; m_family_manager.has_family(fid); fid++) { tout << "fid: " << fid << " fidname: " << get_family_name(fid) << "\n"; }); ast_translation trans(const_cast(from), *this, false); // Inheriting plugins can create new family ids. Since new family ids are // assigned in the order that they are created, this can result in differing // family ids. To avoid this, we first assign all family ids and only then inherit plugins. for (family_id fid = 0; from.m_family_manager.has_family(fid); fid++) { symbol fid_name = from.get_family_name(fid); if (!m_family_manager.has_family(fid)) { family_id new_fid = mk_family_id(fid_name); (void)new_fid; TRACE("copy_families_plugins", tout << "new target fid created: " << new_fid << " fid_name: " << fid_name << "\n";); } } for (family_id fid = 0; from.m_family_manager.has_family(fid); fid++) { SASSERT(from.is_builtin_family_id(fid) == is_builtin_family_id(fid)); SASSERT(!from.is_builtin_family_id(fid) || m_family_manager.has_family(fid)); symbol fid_name = from.get_family_name(fid); (void)fid_name; TRACE("copy_families_plugins", tout << "copying: " << fid_name << ", src fid: " << fid << ", target has_family: " << m_family_manager.has_family(fid) << "\n"; if (m_family_manager.has_family(fid)) tout << get_family_id(fid_name) << "\n";); TRACE("copy_families_plugins", tout << "target fid: " << get_family_id(fid_name) << "\n";); SASSERT(fid == get_family_id(fid_name)); if (from.has_plugin(fid) && !has_plugin(fid)) { decl_plugin * new_p = from.get_plugin(fid)->mk_fresh(); register_plugin(fid, new_p); SASSERT(new_p->get_family_id() == fid); SASSERT(has_plugin(fid)); } if (from.has_plugin(fid)) { get_plugin(fid)->inherit(from.get_plugin(fid), trans); } SASSERT(from.m_family_manager.has_family(fid) == m_family_manager.has_family(fid)); SASSERT(from.get_family_id(fid_name) == get_family_id(fid_name)); SASSERT(!from.has_plugin(fid) || has_plugin(fid)); } } void ast_manager::set_next_expr_id(unsigned id) { try_again: id = m_expr_id_gen.set_next_id(id); for (ast * curr : m_ast_table) { if (curr->get_id() == id) { // id is in use, move to the next one. ++id; goto try_again; } } } unsigned ast_manager::get_node_size(ast const * n) { return ::get_node_size(n); } void ast_manager::register_plugin(symbol const & s, decl_plugin * plugin) { family_id id = m_family_manager.mk_family_id(s); SASSERT(is_format_manager() || s != symbol("format")); register_plugin(id, plugin); } decl_plugin * ast_manager::get_plugin(family_id fid) const { return m_plugins.get(fid, 0); } bool ast_manager::is_value(expr* e) const { decl_plugin const * p = nullptr; if (is_app(e)) { p = get_plugin(to_app(e)->get_family_id()); return p && p->is_value(to_app(e)); } return false; } bool ast_manager::is_unique_value(expr* e) const { decl_plugin const * p = nullptr; if (is_app(e)) { p = get_plugin(to_app(e)->get_family_id()); return p && p->is_unique_value(to_app(e)); } return false; } bool ast_manager::are_equal(expr * a, expr * b) const { if (a == b) { return true; } if (is_app(a) && is_app(b)) { app* ap = to_app(a), *bp = to_app(b); decl_plugin const * p = get_plugin(ap->get_family_id()); if (!p) { p = get_plugin(bp->get_family_id()); } return p && p->are_equal(ap, bp); } return false; } bool ast_manager::are_distinct(expr* a, expr* b) const { if (is_app(a) && is_app(b)) { app* ap = to_app(a), *bp = to_app(b); decl_plugin const * p = get_plugin(ap->get_family_id()); if (!p) { p = get_plugin(bp->get_family_id()); } return p && p->are_distinct(ap, bp); } return false; } void ast_manager::add_lambda_def(func_decl* f, quantifier* q) { m_lambda_defs.insert(f, q); f->get_info()->set_lambda(true); inc_ref(q); } quantifier* ast_manager::is_lambda_def(func_decl* f) { if (f->get_info() && f->get_info()->is_lambda()) { return m_lambda_defs[f]; } return nullptr; } func_decl* ast_manager::get_rec_fun_decl(quantifier* q) const { SASSERT(is_rec_fun_def(q)); return to_app(to_app(q->get_pattern(0))->get_arg(0))->get_decl(); } void ast_manager::register_plugin(family_id id, decl_plugin * plugin) { SASSERT(m_plugins.get(id, 0) == 0); m_plugins.setx(id, plugin, 0); plugin->set_manager(this, id); } bool ast_manager::is_bool(expr const * n) const { return get_sort(n) == m_bool_sort; } #ifdef Z3DEBUG bool ast_manager::slow_not_contains(ast const * n) { unsigned num = 0; for (ast * curr : m_ast_table) { if (compare_nodes(curr, n)) { TRACE("nondet_bug", tout << "id1: " << curr->get_id() << ", id2: " << n->get_id() << "\n"; tout << "hash1: " << get_node_hash(curr) << ", hash2: " << get_node_hash(n) << "\n";); return false; } SASSERT(!(is_app(n) && is_app(curr) && to_app(n)->get_decl() == to_app(curr)->get_decl() && to_app(n)->get_num_args() == 0 && to_app(curr)->get_num_args() == 0)); num++; } SASSERT(num == m_ast_table.size()); return true; } #endif ast * ast_manager::register_node_core(ast * n) { unsigned h = get_node_hash(n); n->m_hash = h; #ifdef Z3DEBUG bool contains = m_ast_table.contains(n); CASSERT("nondet_bug", contains || slow_not_contains(n)); #endif ast* r = m_ast_table.insert_if_not_there(n); SASSERT(r->m_hash == h); if (r != n) { SASSERT(contains); SASSERT(m_ast_table.contains(n)); if (is_func_decl(r) && to_func_decl(r)->get_range() != to_func_decl(n)->get_range()) { std::ostringstream buffer; buffer << "Recycling of declaration for the same name '" << to_func_decl(r)->get_name().str() << "' and domain, but different range type is not permitted"; throw ast_exception(buffer.str()); } deallocate_node(n, ::get_node_size(n)); return r; } else { SASSERT(!contains); SASSERT(m_ast_table.contains(n)); } n->m_id = is_decl(n) ? m_decl_id_gen.mk() : m_expr_id_gen.mk(); TRACE("ast", tout << "Object " << n->m_id << " was created.\n";); TRACE("mk_var_bug", tout << "mk_ast: " << n->m_id << "\n";); // increment reference counters switch (n->get_kind()) { case AST_SORT: if (to_sort(n)->m_info != nullptr) { to_sort(n)->m_info = alloc(sort_info, *(to_sort(n)->get_info())); to_sort(n)->m_info->init_eh(*this); } break; case AST_FUNC_DECL: if (to_func_decl(n)->m_info != nullptr) { to_func_decl(n)->m_info = alloc(func_decl_info, *(to_func_decl(n)->get_info())); to_func_decl(n)->m_info->init_eh(*this); } inc_array_ref(to_func_decl(n)->get_arity(), to_func_decl(n)->get_domain()); inc_ref(to_func_decl(n)->get_range()); break; case AST_APP: { app * t = to_app(n); inc_ref(t->get_decl()); unsigned num_args = t->get_num_args(); if (num_args > 0) { app_flags * f = t->flags(); *f = mk_default_app_flags(); SASSERT(t->is_ground()); SASSERT(!t->has_quantifiers()); SASSERT(!t->has_labels()); if (is_label(t)) f->m_has_labels = true; unsigned depth = 0; for (unsigned i = 0; i < num_args; i++) { expr * arg = t->get_arg(i); inc_ref(arg); unsigned arg_depth = 0; switch (arg->get_kind()) { case AST_APP: { app_flags * arg_flags = to_app(arg)->flags(); arg_depth = arg_flags->m_depth; if (arg_flags->m_has_quantifiers) f->m_has_quantifiers = true; if (arg_flags->m_has_labels) f->m_has_labels = true; if (!arg_flags->m_ground) f->m_ground = false; break; } case AST_QUANTIFIER: f->m_has_quantifiers = true; f->m_ground = false; arg_depth = to_quantifier(arg)->get_depth(); break; case AST_VAR: f->m_ground = false; arg_depth = 1; break; default: UNREACHABLE(); } if (arg_depth > depth) depth = arg_depth; } depth++; if (depth > c_max_depth) depth = c_max_depth; f->m_depth = depth; SASSERT(t->get_depth() == depth); } break; } case AST_VAR: inc_ref(to_var(n)->get_sort()); break; case AST_QUANTIFIER: inc_array_ref(to_quantifier(n)->get_num_decls(), to_quantifier(n)->get_decl_sorts()); inc_ref(to_quantifier(n)->get_expr()); inc_ref(to_quantifier(n)->get_sort()); inc_array_ref(to_quantifier(n)->get_num_patterns(), to_quantifier(n)->get_patterns()); inc_array_ref(to_quantifier(n)->get_num_no_patterns(), to_quantifier(n)->get_no_patterns()); break; default: break; } return n; } void ast_manager::delete_node(ast * n) { TRACE("delete_node_bug", tout << mk_ll_pp(n, *this) << "\n";); SASSERT(m_ast_table.contains(n)); m_ast_table.push_erase(n); while ((n = m_ast_table.pop_erase())) { CTRACE("del_quantifier", is_quantifier(n), tout << "deleting quantifier " << n->m_id << " " << n << "\n";); TRACE("mk_var_bug", tout << "del_ast: " << " " << n->m_ref_count << "\n";); TRACE("ast_delete_node", tout << mk_bounded_pp(n, *this) << "\n";); SASSERT(!m_debug_ref_count || !m_debug_free_indices.contains(n->m_id)); #ifdef RECYCLE_FREE_AST_INDICES if (!m_debug_ref_count) { if (is_decl(n)) m_decl_id_gen.recycle(n->m_id); else m_expr_id_gen.recycle(n->m_id); } #endif switch (n->get_kind()) { case AST_SORT: if (to_sort(n)->m_info != nullptr && !m_debug_ref_count) { sort_info * info = to_sort(n)->get_info(); info->del_eh(*this); dealloc(info); } break; case AST_FUNC_DECL: { func_decl* f = to_func_decl(n); if (f->m_info != nullptr && !m_debug_ref_count) { func_decl_info * info = f->get_info(); if (info->is_lambda()) { push_dec_ref(m_lambda_defs[f]); m_lambda_defs.remove(f); } info->del_eh(*this); dealloc(info); } push_dec_array_ref(f->get_arity(), f->get_domain()); push_dec_ref(f->get_range()); break; } case AST_APP: { app* a = to_app(n); push_dec_ref(a->get_decl()); push_dec_array_ref(a->get_num_args(), a->get_args()); break; } case AST_VAR: push_dec_ref(to_var(n)->get_sort()); break; case AST_QUANTIFIER: { quantifier* q = to_quantifier(n); push_dec_array_ref(q->get_num_decls(), q->get_decl_sorts()); push_dec_ref(q->get_expr()); push_dec_ref(q->get_sort()); push_dec_array_ref(q->get_num_patterns(), q->get_patterns()); push_dec_array_ref(q->get_num_no_patterns(), q->get_no_patterns()); break; } default: break; } if (m_debug_ref_count) { m_debug_free_indices.insert(n->m_id,0); } deallocate_node(n, ::get_node_size(n)); } } sort * ast_manager::mk_sort(family_id fid, decl_kind k, unsigned num_parameters, parameter const * parameters) { decl_plugin * p = get_plugin(fid); if (p) return p->mk_sort(k, num_parameters, parameters); return nullptr; } func_decl * ast_manager::mk_func_decl(family_id fid, decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { decl_plugin * p = get_plugin(fid); if (p) return p->mk_func_decl(k, num_parameters, parameters, arity, domain, range); return nullptr; } func_decl * ast_manager::mk_func_decl(family_id fid, decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned num_args, expr * const * args, sort * range) { decl_plugin * p = get_plugin(fid); if (p) return p->mk_func_decl(k, num_parameters, parameters, num_args, args, range); return nullptr; } app * ast_manager::mk_app(family_id fid, decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned num_args, expr * const * args, sort * range) { func_decl * decl = mk_func_decl(fid, k, num_parameters, parameters, num_args, args, range); if (decl != nullptr) return mk_app(decl, num_args, args); return nullptr; } app * ast_manager::mk_app(family_id fid, decl_kind k, unsigned num_args, expr * const * args) { return mk_app(fid, k, 0, nullptr, num_args, args); } app * ast_manager::mk_app(family_id fid, decl_kind k, expr * arg) { return mk_app(fid, k, 0, nullptr, 1, &arg); } app * ast_manager::mk_app(family_id fid, decl_kind k, expr * arg1, expr * arg2) { expr * args[2] = { arg1, arg2 }; return mk_app(fid, k, 0, nullptr, 2, args); } app * ast_manager::mk_app(family_id fid, decl_kind k, expr * arg1, expr * arg2, expr * arg3) { expr * args[3] = { arg1, arg2, arg3 }; return mk_app(fid, k, 0, nullptr, 3, args); } sort * ast_manager::mk_sort(symbol const & name, sort_info * info) { unsigned sz = sort::get_obj_size(); void * mem = allocate_node(sz); sort * new_node = new (mem) sort(name, info); return register_node(new_node); } sort * ast_manager::substitute(sort* s, unsigned n, sort * const * src, sort * const * dst) { for (unsigned i = 0; i < n; ++i) { if (s == src[i]) return dst[i]; } vector ps; bool change = false; sort_ref_vector sorts(*this); for (parameter const& p : s->parameters()) { if (p.is_ast()) { SASSERT(is_sort(p.get_ast())); change = true; sorts.push_back(substitute(to_sort(p.get_ast()), n, src, dst)); ps.push_back(parameter(sorts.back())); } else { ps.push_back(p); } } if (!change) { return s; } decl_info dinfo(s->get_family_id(), s->get_decl_kind(), ps.size(), ps.c_ptr(), s->private_parameters()); sort_info sinfo(dinfo, s->get_num_elements()); return mk_sort(s->get_name(), &sinfo); } sort * ast_manager::mk_uninterpreted_sort(symbol const & name, unsigned num_parameters, parameter const * parameters) { user_sort_plugin * plugin = get_user_sort_plugin(); decl_kind kind = plugin->register_name(name); return plugin->mk_sort(kind, num_parameters, parameters); } func_decl * ast_manager::mk_func_decl(symbol const & name, unsigned arity, sort * const * domain, sort * range, bool assoc, bool comm, bool inj) { func_decl_info info(null_family_id, null_decl_kind); info.set_associative(assoc); info.set_commutative(comm); info.set_injective(inj); return mk_func_decl(name, arity, domain, range, info); } func_decl * ast_manager::mk_func_decl(symbol const & name, unsigned arity, sort * const * domain, sort * range, func_decl_info * info) { SASSERT(arity == 1 || info == 0 || !info->is_injective()); SASSERT(arity == 2 || info == 0 || !info->is_associative()); SASSERT(arity == 2 || info == 0 || !info->is_commutative()); unsigned sz = func_decl::get_obj_size(arity); void * mem = allocate_node(sz); func_decl * new_node = new (mem) func_decl(name, arity, domain, range, info); return register_node(new_node); } void ast_manager::check_sort(func_decl const * decl, unsigned num_args, expr * const * args) const { ast_manager& m = const_cast(*this); if (decl->is_associative()) { sort * expected = decl->get_domain(0); for (unsigned i = 0; i < num_args; i++) { sort * given = get_sort(args[i]); if (!compatible_sorts(expected, given)) { std::ostringstream buff; buff << "invalid function application for " << decl->get_name() << ", "; buff << "sort mismatch on argument at position " << (i+1) << ", "; buff << "expected " << mk_pp(expected, m) << " but given " << mk_pp(given, m); throw ast_exception(buff.str()); } } } else { if (decl->get_arity() != num_args) { throw ast_exception("invalid function application, wrong number of arguments"); } for (unsigned i = 0; i < num_args; i++) { sort * expected = decl->get_domain(i); sort * given = get_sort(args[i]); if (!compatible_sorts(expected, given)) { std::ostringstream buff; buff << "invalid function application for " << decl->get_name() << ", "; buff << "sort mismatch on argument at position " << (i+1) << ", "; buff << "expected " << mk_pp(expected, m) << " but given " << mk_pp(given, m); throw ast_exception(buff.str()); } } } } /** \brief Shallow sort checker. Return true if success. If n == 0, then fail. If n is an application, checks whether the arguments of n match the expected types. */ void ast_manager::check_sorts_core(ast const * n) const { if (!n) { throw ast_exception("expression is null"); } if (n->get_kind() != AST_APP) return; // nothing else to check... app const * a = to_app(n); func_decl* d = a->get_decl(); check_sort(d, a->get_num_args(), a->get_args()); if (a->get_num_args() == 2 && !d->is_flat_associative() && d->is_right_associative()) { check_sorts_core(a->get_arg(1)); } if (a->get_num_args() == 2 && !d->is_flat_associative() && d->is_left_associative()) { check_sorts_core(a->get_arg(0)); } } bool ast_manager::check_sorts(ast const * n) const { try { check_sorts_core(n); return true; } catch (ast_exception & ex) { warning_msg("%s", ex.msg()); return false; } } bool ast_manager::compatible_sorts(sort * s1, sort * s2) const { if (s1 == s2) return true; if (m_int_real_coercions) return s1->get_family_id() == m_arith_family_id && s2->get_family_id() == m_arith_family_id; return false; } bool ast_manager::coercion_needed(func_decl * decl, unsigned num_args, expr * const * args) { SASSERT(m_int_real_coercions); if (decl->is_associative()) { sort * d = decl->get_domain(0); if (d->get_family_id() == m_arith_family_id) { for (unsigned i = 0; i < num_args; i++) { if (d != get_sort(args[i])) return true; } } } else { if (decl->get_arity() != num_args) { // Invalid input: unexpected number of arguments for non-associative operator. // So, there is no point in coercing the input arguments. return false; } for (unsigned i = 0; i < num_args; i++) { sort * d = decl->get_domain(i); if (d->get_family_id() == m_arith_family_id && d != get_sort(args[i])) return true; } } return false; } app * ast_manager::mk_app_core(func_decl * decl, unsigned num_args, expr * const * args) { app * r = nullptr; 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); if (decl->is_associative()) { sort * d = decl->get_domain(0); for (unsigned i = 0; i < num_args; i++) { sort * s = get_sort(args[i]); if (d != s && d->get_family_id() == m_arith_family_id && s->get_family_id() == m_arith_family_id) { if (d->get_decl_kind() == REAL_SORT) new_args.push_back(mk_app(m_arith_family_id, OP_TO_REAL, args[i])); else new_args.push_back(mk_app(m_arith_family_id, OP_TO_INT, args[i])); } else { new_args.push_back(args[i]); } } } else { for (unsigned i = 0; i < num_args; i++) { sort * d = decl->get_domain(i); sort * s = get_sort(args[i]); if (d != s && d->get_family_id() == m_arith_family_id && s->get_family_id() == m_arith_family_id) { if (d->get_decl_kind() == REAL_SORT) new_args.push_back(mk_app(m_arith_family_id, OP_TO_REAL, args[i])); else new_args.push_back(mk_app(m_arith_family_id, OP_TO_INT, args[i])); } else { new_args.push_back(args[i]); } } } check_args(decl, num_args, new_args.c_ptr()); SASSERT(new_args.size() == num_args); new_node = new (mem)app(decl, num_args, new_args.c_ptr()); r = register_node(new_node); } else { check_args(decl, num_args, args); new_node = new (mem)app(decl, num_args, args); r = register_node(new_node); } if (m_trace_stream && r == new_node) { if (is_proof(r)) { if (decl == mk_func_decl(m_basic_family_id, PR_UNDEF, 0, nullptr, 0, static_cast(nullptr))) return r; *m_trace_stream << "[mk-proof] #"; } else { *m_trace_stream << "[mk-app] #"; } *m_trace_stream << r->get_id() << " "; if (r->get_num_args() == 0 && r->get_decl()->get_name() == "int") { ast_ll_pp(*m_trace_stream, *this, r); } else if (is_label_lit(r)) { ast_ll_pp(*m_trace_stream, *this, r); } else { *m_trace_stream << r->get_decl()->get_name(); for (unsigned i = 0; i < r->get_num_args(); i++) *m_trace_stream << " #" << r->get_arg(i)->get_id(); *m_trace_stream << "\n"; } } } catch (...) { deallocate_node(static_cast(mem), sz); throw; } return r; } void ast_manager::check_args(func_decl* f, unsigned n, expr* const* es) { for (unsigned i = 0; i < n; i++) { sort * actual_sort = get_sort(es[i]); sort * expected_sort = f->is_associative() ? f->get_domain(0) : f->get_domain(i); if (expected_sort != actual_sort) { std::ostringstream buffer; buffer << "Sort mismatch at argument #" << (i+1) << " for function " << mk_pp(f,*this) << " supplied sort is " << mk_pp(actual_sort, *this); throw ast_exception(buffer.str()); } } } inline app * ast_manager::mk_app_core(func_decl * decl, expr * arg1, expr * arg2) { expr * args[2] = { arg1, arg2 }; return mk_app_core(decl, 2, args); } app * ast_manager::mk_app(func_decl * decl, unsigned num_args, expr * const * args) { bool type_error = decl->get_arity() != num_args && !decl->is_right_associative() && !decl->is_left_associative() && !decl->is_chainable(); type_error |= (decl->get_arity() != num_args && num_args < 2 && decl->get_family_id() == m_basic_family_id && !decl->is_associative()); if (type_error) { std::ostringstream buffer; buffer << "Wrong number of arguments (" << num_args << ") passed to function " << mk_pp(decl, *this); throw ast_exception(buffer.str()); } app * r = nullptr; if (num_args == 1 && decl->is_chainable() && decl->get_arity() == 2) { r = mk_true(); } else if (num_args > 2 && !decl->is_flat_associative()) { if (decl->is_right_associative()) { unsigned j = num_args - 1; r = mk_app_core(decl, args[j-1], args[j]); -- j; while (j > 0) { --j; r = mk_app_core(decl, args[j], r); } } else if (decl->is_left_associative()) { r = mk_app_core(decl, args[0], args[1]); for (unsigned i = 2; i < num_args; i++) { r = mk_app_core(decl, r, args[i]); } } else if (decl->is_chainable()) { TRACE("chainable", tout << "chainable...\n";); ptr_buffer new_args; for (unsigned i = 1; i < num_args; i++) { new_args.push_back(mk_app_core(decl, args[i-1], args[i])); } r = mk_and(new_args.size(), new_args.c_ptr()); } } if (r == nullptr) { r = mk_app_core(decl, num_args, args); } SASSERT(r != 0); TRACE("app_ground", tout << "ground: " << r->is_ground() << " id: " << r->get_id() << "\n" << mk_ll_pp(r, *this) << "\n";); return r; } func_decl * ast_manager::mk_fresh_func_decl(symbol const & prefix, symbol const & suffix, unsigned arity, sort * const * domain, sort * range, bool skolem) { func_decl_info info(null_family_id, null_decl_kind); info.m_skolem = skolem; SASSERT(skolem == info.is_skolem()); func_decl * d; if (prefix == symbol::null && suffix == symbol::null) { d = mk_func_decl(symbol(m_fresh_id), arity, domain, range, &info); } else { string_buffer<64> buffer; if (prefix == symbol::null) buffer << "sk"; else buffer << prefix; buffer << "!"; if (suffix != symbol::null) buffer << suffix << "!"; buffer << m_fresh_id; d = mk_func_decl(symbol(buffer.c_str()), arity, domain, range, &info); } m_fresh_id++; SASSERT(d->get_info()); SASSERT(skolem == d->is_skolem()); return d; } sort * ast_manager::mk_fresh_sort(char const * prefix) { string_buffer<32> buffer; buffer << prefix << "!" << m_fresh_id; m_fresh_id++; return mk_uninterpreted_sort(symbol(buffer.c_str())); } symbol ast_manager::mk_fresh_var_name(char const * prefix) { string_buffer<32> buffer; buffer << (prefix ? prefix : "var") << "!" << m_fresh_id; m_fresh_id++; return symbol(buffer.c_str()); } var * ast_manager::mk_var(unsigned idx, sort * s) { unsigned sz = var::get_obj_size(); void * mem = allocate_node(sz); var * new_node = new (mem) var(idx, s); var * r = register_node(new_node); if (m_trace_stream && r == new_node) { *m_trace_stream << "[mk-var] #" << r->get_id() << " " << idx << "\n"; } return r; } app * ast_manager::mk_label(bool pos, unsigned num_names, symbol const * names, expr * n) { SASSERT(num_names > 0); SASSERT(get_sort(n) == m_bool_sort); buffer p; p.push_back(parameter(static_cast(pos))); for (unsigned i = 0; i < num_names; i++) p.push_back(parameter(names[i])); return mk_app(m_label_family_id, OP_LABEL, p.size(), p.c_ptr(), 1, &n); } app * ast_manager::mk_label(bool pos, symbol const & name, expr * n) { return mk_label(pos, 1, &name, n); } bool ast_manager::is_label(expr const * n, bool & pos, buffer & names) const { if (!is_app_of(n, m_label_family_id, OP_LABEL)) { return false; } func_decl const * decl = to_app(n)->get_decl(); pos = decl->get_parameter(0).get_int() != 0; for (unsigned i = 1; i < decl->get_num_parameters(); i++) names.push_back(decl->get_parameter(i).get_symbol()); return true; } app * ast_manager::mk_label_lit(unsigned num_names, symbol const * names) { SASSERT(num_names > 0); buffer p; for (unsigned i = 0; i < num_names; i++) p.push_back(parameter(names[i])); return mk_app(m_label_family_id, OP_LABEL_LIT, p.size(), p.c_ptr(), 0, nullptr); } app * ast_manager::mk_label_lit(symbol const & name) { return mk_label_lit(1, &name); } bool ast_manager::is_label_lit(expr const * n, buffer & names) const { if (!is_app_of(n, m_label_family_id, OP_LABEL_LIT)) { return false; } func_decl const * decl = to_app(n)->get_decl(); for (parameter const& p : decl->parameters()) names.push_back(p.get_symbol()); return true; } app * ast_manager::mk_pattern(unsigned num_exprs, app * const * exprs) { DEBUG_CODE({ for (unsigned i = 0; i < num_exprs; ++i) { SASSERT(is_app(exprs[i])); }}); return mk_app(m_pattern_family_id, OP_PATTERN, 0, nullptr, num_exprs, (expr*const*)exprs); } bool ast_manager::is_pattern(expr const * n) const { if (!is_app_of(n, m_pattern_family_id, OP_PATTERN)) { return false; } for (unsigned i = 0; i < to_app(n)->get_num_args(); ++i) { if (!is_app(to_app(n)->get_arg(i))) { return false; } } return true; } bool ast_manager::is_pattern(expr const * n, ptr_vector &args) { if (!is_app_of(n, m_pattern_family_id, OP_PATTERN)) { return false; } for (unsigned i = 0; i < to_app(n)->get_num_args(); ++i) { expr *arg = to_app(n)->get_arg(i); if (!is_app(arg)) { return false; } args.push_back(arg); } return true; } static void trace_quant(std::ostream& strm, quantifier* q) { strm << (is_lambda(q) ? "[mk-lambda]" : "[mk-quant]") << " #" << q->get_id() << " " << q->get_qid() << " " << q->get_num_decls(); for (unsigned i = 0; i < q->get_num_patterns(); ++i) { strm << " #" << q->get_pattern(i)->get_id(); } strm << " #" << q->get_expr()->get_id() << "\n"; } quantifier * ast_manager::mk_quantifier(quantifier_kind k, unsigned num_decls, sort * const * decl_sorts, symbol const * decl_names, expr * body, int weight , symbol const & qid, symbol const & skid, unsigned num_patterns, expr * const * patterns, unsigned num_no_patterns, expr * const * no_patterns) { SASSERT(body); SASSERT(num_decls > 0); if (num_patterns != 0 && num_no_patterns != 0) throw ast_exception("simultaneous patterns and no-patterns not supported"); DEBUG_CODE({ for (unsigned i = 0; i < num_patterns; ++i) { TRACE("ast", tout << i << " " << mk_pp(patterns[i], *this) << "\n";); SASSERT(is_pattern(patterns[i])); }}); unsigned sz = quantifier::get_obj_size(num_decls, num_patterns, num_no_patterns); void * mem = allocate_node(sz); sort* s = nullptr; if (k == lambda_k) { array_util autil(*this); s = autil.mk_array_sort(num_decls, decl_sorts, ::get_sort(body)); } else { s = mk_bool_sort(); } quantifier * new_node = new (mem) quantifier(k, num_decls, decl_sorts, decl_names, body, s, weight, qid, skid, num_patterns, patterns, num_no_patterns, no_patterns); quantifier * r = register_node(new_node); if (m_trace_stream && r == new_node) { trace_quant(*m_trace_stream, r); *m_trace_stream << "[attach-var-names] #" << r->get_id(); for (unsigned i = 0; i < num_decls; ++i) { *m_trace_stream << " (|" << decl_names[num_decls - i - 1].str() << "| ; |" << decl_sorts[num_decls - i -1]->get_name().str() << "|)"; } *m_trace_stream << "\n"; } return r; } quantifier * ast_manager::mk_lambda(unsigned num_decls, sort * const * decl_sorts, symbol const * decl_names, expr * body) { SASSERT(body); unsigned sz = quantifier::get_obj_size(num_decls, 0, 0); void * mem = allocate_node(sz); array_util autil(*this); sort* s = autil.mk_array_sort(num_decls, decl_sorts, ::get_sort(body)); quantifier * new_node = new (mem) quantifier(num_decls, decl_sorts, decl_names, body, s); quantifier * r = register_node(new_node); if (m_trace_stream && r == new_node) { trace_quant(*m_trace_stream, r); } return r; } // Return true if the patterns of q are the given ones. static bool same_patterns(quantifier * q, unsigned num_patterns, expr * const * patterns) { if (num_patterns != q->get_num_patterns()) return false; for (unsigned i = 0; i < num_patterns; i++) if (q->get_pattern(i) != patterns[i]) return false; return true; } // Return true if the no patterns of q are the given ones. static bool same_no_patterns(quantifier * q, unsigned num_no_patterns, expr * const * no_patterns) { if (num_no_patterns != q->get_num_no_patterns()) return false; for (unsigned i = 0; i < num_no_patterns; i++) if (q->get_no_pattern(i) != no_patterns[i]) return false; return true; } quantifier * ast_manager::update_quantifier(quantifier * q, unsigned num_patterns, expr * const * patterns, expr * body) { if (q->get_expr() == body && same_patterns(q, num_patterns, patterns)) return q; return mk_quantifier(q->get_kind(), q->get_num_decls(), q->get_decl_sorts(), q->get_decl_names(), body, q->get_weight(), q->get_qid(), q->get_skid(), num_patterns, patterns, num_patterns == 0 ? q->get_num_no_patterns() : 0, num_patterns == 0 ? q->get_no_patterns() : nullptr); } quantifier * ast_manager::update_quantifier(quantifier * q, unsigned num_patterns, expr * const * patterns, unsigned num_no_patterns, expr * const * no_patterns, expr * body) { if (q->get_expr() == body && same_patterns(q, num_patterns, patterns) && same_no_patterns(q, num_no_patterns, no_patterns)) return q; return mk_quantifier(q->get_kind(), q->get_num_decls(), q->get_decl_sorts(), q->get_decl_names(), body, q->get_weight(), q->get_qid(), q->get_skid(), num_patterns, patterns, num_no_patterns, no_patterns); } quantifier * ast_manager::update_quantifier(quantifier * q, expr * body) { if (q->get_expr() == body) return q; return mk_quantifier(q->get_kind(), q->get_num_decls(), q->get_decl_sorts(), q->get_decl_names(), body, q->get_weight(), q->get_qid(), q->get_skid(), q->get_num_patterns(), q->get_patterns(), q->get_num_no_patterns(), q->get_no_patterns()); } quantifier * ast_manager::update_quantifier_weight(quantifier * q, int w) { if (q->get_weight() == w) return q; TRACE("update_quantifier_weight", tout << "#" << q->get_id() << " " << q->get_weight() << " -> " << w << "\n";); return mk_quantifier(q->get_kind(), q->get_num_decls(), q->get_decl_sorts(), q->get_decl_names(), q->get_expr(), w, q->get_qid(), q->get_skid(), q->get_num_patterns(), q->get_patterns(), q->get_num_no_patterns(), q->get_no_patterns()); } quantifier * ast_manager::update_quantifier(quantifier * q, quantifier_kind k, expr * body) { if (q->get_expr() == body && q->get_kind() == k) return q; return mk_quantifier(k, q->get_num_decls(), q->get_decl_sorts(), q->get_decl_names(), body, q->get_weight(), q->get_qid(), q->get_skid(), q->get_num_patterns(), q->get_patterns(), q->get_num_no_patterns(), q->get_no_patterns()); } quantifier * ast_manager::update_quantifier(quantifier * q, quantifier_kind k, unsigned num_patterns, expr * const * patterns, expr * body) { if (q->get_expr() == body && q->get_kind() == k && same_patterns(q, num_patterns, patterns)) return q; return mk_quantifier(k, q->get_num_decls(), q->get_decl_sorts(), q->get_decl_names(), body, q->get_weight(), q->get_qid(), q->get_skid(), num_patterns, patterns, num_patterns == 0 ? q->get_num_no_patterns() : 0, num_patterns == 0 ? q->get_no_patterns() : nullptr); } app * ast_manager::mk_distinct(unsigned num_args, expr * const * args) { return mk_app(m_basic_family_id, OP_DISTINCT, num_args, args); } app * ast_manager::mk_distinct_expanded(unsigned num_args, expr * const * args) { if (num_args < 2) return mk_true(); if (num_args == 2) return mk_not(mk_eq(args[0], args[1])); ptr_buffer new_args; for (unsigned i = 0; i < num_args - 1; i++) { expr * a1 = args[i]; for (unsigned j = i + 1; j < num_args; j++) { expr * a2 = args[j]; new_args.push_back(mk_not(mk_eq(a1, a2))); } } app * r = mk_and(new_args.size(), new_args.c_ptr()); TRACE("distinct", tout << "expanded distinct:\n" << mk_pp(r, *this) << "\n";); return r; } // ----------------------------------- // // expr_dependency // // ----------------------------------- expr_dependency * ast_manager::mk_leaf(expr * t) { if (t == nullptr) return nullptr; else return m_expr_dependency_manager.mk_leaf(t); } expr_dependency * ast_manager::mk_join(unsigned n, expr * const * ts) { expr_dependency * d = nullptr; for (unsigned i = 0; i < n; i++) d = mk_join(d, mk_leaf(ts[i])); return d; } void ast_manager::linearize(expr_dependency * d, ptr_vector & ts) { m_expr_dependency_manager.linearize(d, ts); remove_duplicates(ts); } // ----------------------------------- // // Values // // ----------------------------------- app * ast_manager::mk_model_value(unsigned idx, sort * s) { parameter p[2] = { parameter(idx), parameter(s) }; return mk_app(m_model_value_family_id, OP_MODEL_VALUE, 2, p, 0, static_cast(nullptr)); } expr * ast_manager::get_some_value(sort * s, some_value_proc * p) { flet l(m_some_value_proc, p); return get_some_value(s); } expr * ast_manager::get_some_value(sort * s) { expr * v = nullptr; if (m_some_value_proc) v = (*m_some_value_proc)(s); if (v != nullptr) return v; family_id fid = s->get_family_id(); if (fid != null_family_id) { decl_plugin * p = get_plugin(fid); if (p != nullptr) { v = p->get_some_value(s); if (v != nullptr) return v; } } return mk_model_value(0, s); } bool ast_manager::is_fully_interp(sort * s) const { if (is_uninterp(s)) return false; family_id fid = s->get_family_id(); SASSERT(fid != null_family_id); decl_plugin * p = get_plugin(fid); if (p != nullptr) return p->is_fully_interp(s); return false; } // ----------------------------------- // // Proof generation // // ----------------------------------- proof * ast_manager::mk_proof(family_id fid, decl_kind k, unsigned num_args, expr * const * args) { if (proofs_disabled()) return nullptr; return mk_app(fid, k, num_args, args); } proof * ast_manager::mk_proof(family_id fid, decl_kind k, expr * arg) { return mk_proof(fid, k, 1, &arg); } proof * ast_manager::mk_proof(family_id fid, decl_kind k, expr * arg1, expr * arg2) { expr * args[2] = { arg1, arg2 }; return mk_proof(fid, k, 2, args); } proof * ast_manager::mk_proof(family_id fid, decl_kind k, expr * arg1, expr * arg2, expr * arg3) { expr * args[3] = { arg1, arg2, arg3 }; return mk_proof(fid, k, 2, args); } proof * ast_manager::mk_true_proof() { expr * f = mk_true(); return mk_proof(m_basic_family_id, PR_TRUE, f); } proof * ast_manager::mk_asserted(expr * f) { CTRACE("mk_asserted_bug", !is_bool(f), tout << mk_ismt2_pp(f, *this) << "\nsort: " << mk_ismt2_pp(get_sort(f), *this) << "\n";); SASSERT(is_bool(f)); return mk_proof(m_basic_family_id, PR_ASSERTED, f); } proof * ast_manager::mk_goal(expr * f) { SASSERT(is_bool(f)); return mk_proof(m_basic_family_id, PR_GOAL, f); } proof * ast_manager::mk_modus_ponens(proof * p1, proof * p2) { if (!p1 || !p2) return nullptr; SASSERT(has_fact(p1)); SASSERT(has_fact(p2)); CTRACE("mk_modus_ponens", !(is_implies(get_fact(p2)) || is_eq(get_fact(p2)) || is_oeq(get_fact(p2))), tout << mk_ll_pp(p1, *this) << "\n"; tout << mk_ll_pp(p2, *this) << "\n";); SASSERT(is_implies(get_fact(p2)) || is_eq(get_fact(p2)) || is_oeq(get_fact(p2))); CTRACE("mk_modus_ponens", to_app(get_fact(p2))->get_arg(0) != get_fact(p1), tout << mk_pp(get_fact(p1), *this) << "\n" << mk_pp(get_fact(p2), *this) << "\n";); SASSERT(to_app(get_fact(p2))->get_arg(0) == get_fact(p1)); CTRACE("mk_modus_ponens", !is_ground(p2) && !has_quantifiers(p2), tout << "Non-ground: " << mk_pp(p2, *this) << "\n";); CTRACE("mk_modus_ponens", !is_ground(p1) && !has_quantifiers(p1), tout << "Non-ground: " << mk_pp(p1, *this) << "\n";); if (is_reflexivity(p2)) return p1; expr * f = to_app(get_fact(p2))->get_arg(1); if (is_oeq(get_fact(p2))) return mk_app(m_basic_family_id, PR_MODUS_PONENS_OEQ, p1, p2, f); return mk_app(m_basic_family_id, PR_MODUS_PONENS, p1, p2, f); } proof * ast_manager::mk_reflexivity(expr * e) { return mk_app(m_basic_family_id, PR_REFLEXIVITY, mk_eq(e, e)); } proof * ast_manager::mk_oeq_reflexivity(expr * e) { return mk_app(m_basic_family_id, PR_REFLEXIVITY, mk_oeq(e, e)); } proof * ast_manager::mk_commutativity(app * f) { SASSERT(f->get_num_args() == 2); app * f_prime = mk_app(f->get_decl(), f->get_arg(1), f->get_arg(0)); return mk_app(m_basic_family_id, PR_COMMUTATIVITY, mk_eq(f, f_prime)); } /** \brief Given a proof of p, return a proof of (p <=> true) */ proof * ast_manager::mk_iff_true(proof * pr) { if (!pr) return pr; SASSERT(has_fact(pr)); SASSERT(is_bool(get_fact(pr))); return mk_app(m_basic_family_id, PR_IFF_TRUE, pr, mk_iff(get_fact(pr), mk_true())); } /** \brief Given a proof of (not p), return a proof of (p <=> false) */ proof * ast_manager::mk_iff_false(proof * pr) { if (!pr) return pr; SASSERT(has_fact(pr)); SASSERT(is_not(get_fact(pr))); expr * p = to_app(get_fact(pr))->get_arg(0); return mk_app(m_basic_family_id, PR_IFF_FALSE, pr, mk_iff(p, mk_false())); } proof * ast_manager::mk_symmetry(proof * p) { if (!p) return p; if (is_reflexivity(p)) return p; if (is_symmetry(p)) return get_parent(p, 0); SASSERT(has_fact(p)); SASSERT(is_app(get_fact(p))); SASSERT(to_app(get_fact(p))->get_num_args() == 2); return mk_app(m_basic_family_id, PR_SYMMETRY, p, mk_app(to_app(get_fact(p))->get_decl(), to_app(get_fact(p))->get_arg(1), to_app(get_fact(p))->get_arg(0))); } proof * ast_manager::mk_transitivity(proof * p1, proof * p2) { if (!p1) return p2; if (!p2) return p1; SASSERT(has_fact(p1)); SASSERT(has_fact(p2)); SASSERT(is_app(get_fact(p1))); SASSERT(is_app(get_fact(p2))); SASSERT(to_app(get_fact(p1))->get_num_args() == 2); SASSERT(to_app(get_fact(p2))->get_num_args() == 2); CTRACE("mk_transitivity", to_app(get_fact(p1))->get_decl() != to_app(get_fact(p2))->get_decl(), tout << mk_pp(get_fact(p1), *this) << "\n\n" << mk_pp(get_fact(p2), *this) << "\n"; tout << mk_pp(to_app(get_fact(p1))->get_decl(), *this) << "\n"; tout << mk_pp(to_app(get_fact(p2))->get_decl(), *this) << "\n";); SASSERT(to_app(get_fact(p1))->get_decl() == to_app(get_fact(p2))->get_decl() || ( (is_eq(get_fact(p1)) || is_oeq(get_fact(p1))) && (is_eq(get_fact(p2)) || is_oeq(get_fact(p2))))); CTRACE("mk_transitivity", to_app(get_fact(p1))->get_arg(1) != to_app(get_fact(p2))->get_arg(0), tout << mk_pp(get_fact(p1), *this) << "\n\n" << mk_pp(get_fact(p2), *this) << "\n"; tout << mk_bounded_pp(p1, *this, 5) << "\n\n"; tout << mk_bounded_pp(p2, *this, 5) << "\n\n"; ); SASSERT(to_app(get_fact(p1))->get_arg(1) == to_app(get_fact(p2))->get_arg(0)); if (is_reflexivity(p1)) return p2; if (is_reflexivity(p2)) return p1; // OEQ is compatible with EQ for transitivity. func_decl* f = to_app(get_fact(p1))->get_decl(); if (is_oeq(get_fact(p2))) f = to_app(get_fact(p2))->get_decl(); return mk_app(m_basic_family_id, PR_TRANSITIVITY, p1, p2, mk_app(f, to_app(get_fact(p1))->get_arg(0), to_app(get_fact(p2))->get_arg(1))); } proof * ast_manager::mk_transitivity(proof * p1, proof * p2, proof * p3) { return mk_transitivity(mk_transitivity(p1,p2), p3); } proof * ast_manager::mk_transitivity(proof * p1, proof * p2, proof * p3, proof * p4) { return mk_transitivity(mk_transitivity(mk_transitivity(p1,p2), p3), p4); } proof * ast_manager::mk_transitivity(unsigned num_proofs, proof * const * proofs) { SASSERT(num_proofs > 0); proof * r = proofs[0]; for (unsigned i = 1; i < num_proofs; i++) r = mk_transitivity(r, proofs[i]); return r; } proof * ast_manager::mk_transitivity(unsigned num_proofs, proof * const * proofs, expr * n1, expr * n2) { if (num_proofs == 0) return nullptr; if (num_proofs == 1) return proofs[0]; DEBUG_CODE({ for (unsigned i = 0; i < num_proofs; i++) { SASSERT(proofs[i]); SASSERT(!is_reflexivity(proofs[i])); } }); ptr_buffer args; args.append(num_proofs, (expr**) proofs); args.push_back(mk_eq(n1,n2)); return mk_app(m_basic_family_id, PR_TRANSITIVITY_STAR, args.size(), args.c_ptr()); } proof * ast_manager::mk_monotonicity(func_decl * R, app * f1, app * f2, unsigned num_proofs, proof * const * proofs) { SASSERT(f1->get_num_args() == f2->get_num_args()); SASSERT(f1->get_decl() == f2->get_decl()); ptr_buffer args; args.append(num_proofs, (expr**) proofs); args.push_back(mk_app(R, f1, f2)); return mk_app(m_basic_family_id, PR_MONOTONICITY, args.size(), args.c_ptr()); } proof * ast_manager::mk_congruence(app * f1, app * f2, unsigned num_proofs, proof * const * proofs) { SASSERT(get_sort(f1) == get_sort(f2)); sort * s = get_sort(f1); sort * d[2] = { s, s }; return mk_monotonicity(mk_func_decl(m_basic_family_id, get_eq_op(f1), 0, nullptr, 2, d), f1, f2, num_proofs, proofs); } proof * ast_manager::mk_oeq_congruence(app * f1, app * f2, unsigned num_proofs, proof * const * proofs) { SASSERT(get_sort(f1) == get_sort(f2)); sort * s = get_sort(f1); sort * d[2] = { s, s }; return mk_monotonicity(mk_func_decl(m_basic_family_id, OP_OEQ, 0, nullptr, 2, d), f1, f2, num_proofs, proofs); } proof * ast_manager::mk_bind_proof(quantifier * q, proof * p) { expr* b = mk_lambda(q->get_num_decls(), q->get_decl_sorts(), q->get_decl_names(), p); return mk_app(m_basic_family_id, PR_BIND, b); } proof * ast_manager::mk_quant_intro(quantifier * q1, quantifier * q2, proof * p) { if (!p) return nullptr; SASSERT(q1->get_num_decls() == q2->get_num_decls()); SASSERT(has_fact(p)); SASSERT(is_eq(get_fact(p)) || is_lambda(get_fact(p))); return mk_app(m_basic_family_id, PR_QUANT_INTRO, p, mk_iff(q1, q2)); } proof * ast_manager::mk_oeq_quant_intro(quantifier * q1, quantifier * q2, proof * p) { if (!p) return nullptr; SASSERT(q1->get_num_decls() == q2->get_num_decls()); SASSERT(has_fact(p)); SASSERT(is_oeq(get_fact(p)) || is_lambda(get_fact(p))); return mk_app(m_basic_family_id, PR_QUANT_INTRO, p, mk_oeq(q1, q2)); } proof * ast_manager::mk_distributivity(expr * s, expr * r) { return mk_app(m_basic_family_id, PR_DISTRIBUTIVITY, mk_eq(s, r)); } proof * ast_manager::mk_rewrite(expr * s, expr * t) { if (proofs_disabled()) return nullptr; return mk_app(m_basic_family_id, PR_REWRITE, mk_eq(s, t)); } proof * ast_manager::mk_oeq_rewrite(expr * s, expr * t) { if (proofs_disabled()) return nullptr; return mk_app(m_basic_family_id, PR_REWRITE, mk_oeq(s, t)); } proof * ast_manager::mk_rewrite_star(expr * s, expr * t, unsigned num_proofs, proof * const * proofs) { if (proofs_disabled()) return nullptr; ptr_buffer args; args.append(num_proofs, (expr**) proofs); args.push_back(mk_eq(s, t)); return mk_app(m_basic_family_id, PR_REWRITE_STAR, args.size(), args.c_ptr()); } proof * ast_manager::mk_pull_quant(expr * e, quantifier * q) { if (proofs_disabled()) return nullptr; return mk_app(m_basic_family_id, PR_PULL_QUANT, mk_iff(e, q)); } proof * ast_manager::mk_push_quant(quantifier * q, expr * e) { if (proofs_disabled()) return nullptr; return mk_app(m_basic_family_id, PR_PUSH_QUANT, mk_iff(q, e)); } proof * ast_manager::mk_elim_unused_vars(quantifier * q, expr * e) { if (proofs_disabled()) return nullptr; return mk_app(m_basic_family_id, PR_ELIM_UNUSED_VARS, mk_iff(q, e)); } proof * ast_manager::mk_der(quantifier * q, expr * e) { if (proofs_disabled()) return nullptr; return mk_app(m_basic_family_id, PR_DER, mk_iff(q, e)); } proof * ast_manager::mk_quant_inst(expr * not_q_or_i, unsigned num_bind, expr* const* binding) { if (proofs_disabled()) return nullptr; vector params; for (unsigned i = 0; i < num_bind; ++i) { params.push_back(parameter(binding[i])); SASSERT(params.back().is_ast()); } return mk_app(m_basic_family_id, PR_QUANT_INST, num_bind, params.c_ptr(), 1, & not_q_or_i); } bool ast_manager::is_quant_inst(expr const* e, expr*& not_q_or_i, ptr_vector& binding) const { if (is_quant_inst(e)) { not_q_or_i = to_app(e)->get_arg(0); func_decl* d = to_app(e)->get_decl(); SASSERT(binding.empty()); for (parameter const& p : d->parameters()) { binding.push_back(to_expr(p.get_ast())); } return true; } return false; } bool ast_manager::is_rewrite(expr const* e, expr*& r1, expr*& r2) const { if (is_rewrite(e)) { VERIFY (is_eq(to_app(e)->get_arg(0), r1, r2)); return true; } else { return false; } } proof * ast_manager::mk_def_axiom(expr * ax) { if (proofs_disabled()) return nullptr; return mk_app(m_basic_family_id, PR_DEF_AXIOM, ax); } proof * ast_manager::mk_unit_resolution(unsigned num_proofs, proof * const * proofs) { SASSERT(num_proofs >= 2); for (unsigned i = 0; i < num_proofs; i++) { SASSERT(has_fact(proofs[i])); } ptr_buffer args; args.append(num_proofs, (expr**) proofs); expr * fact; expr * f1 = get_fact(proofs[0]); expr * f2 = get_fact(proofs[1]); if (num_proofs == 2 && is_complement(f1, f2)) { fact = mk_false(); } else { CTRACE("mk_unit_resolution_bug", !is_or(f1), tout << mk_ll_pp(f1, *this) << "\n" << mk_ll_pp(f2, *this) << "\n";); SASSERT(is_or(f1)); ptr_buffer new_lits; app const * cls = to_app(f1); unsigned num_args = cls->get_num_args(); #ifdef Z3DEBUG svector found; #endif for (unsigned i = 0; i < num_args; i++) { bool found_complement = false; expr * lit = cls->get_arg(i); for (unsigned j = 1; j < num_proofs; j++) { expr const * _fact = get_fact(proofs[j]); if (is_complement(lit, _fact)) { found_complement = true; DEBUG_CODE(found.setx(j, true, false); continue;); break; } } if (!found_complement) new_lits.push_back(lit); } DEBUG_CODE({ for (unsigned i = 1; proofs_enabled() && i < num_proofs; i++) { CTRACE("mk_unit_resolution_bug", !found.get(i, false), for (unsigned j = 0; j < num_proofs; j++) { if (j == i) tout << "Index " << i << " was not found:\n"; tout << mk_ll_pp(get_fact(proofs[j]), *this); }); SASSERT(found.get(i, false)); } }); switch (new_lits.size()) { case 0: fact = mk_false(); break; case 1: fact = new_lits[0]; break; default: fact = mk_or(new_lits.size(), new_lits.c_ptr()); break; } } args.push_back(fact); proof * pr = mk_app(m_basic_family_id, PR_UNIT_RESOLUTION, args.size(), args.c_ptr()); TRACE("unit_resolution", tout << "unit_resolution generating fact\n" << mk_ll_pp(pr, *this);); return pr; } proof * ast_manager::mk_unit_resolution(unsigned num_proofs, proof * const * proofs, expr * new_fact) { TRACE("unit_bug", for (unsigned i = 0; i < num_proofs; i++) tout << mk_pp(get_fact(proofs[i]), *this) << "\n"; tout << "===>\n"; tout << mk_pp(new_fact, *this) << "\n";); ptr_buffer args; args.append(num_proofs, (expr**) proofs); args.push_back(new_fact); #ifdef Z3DEBUG expr * f1 = get_fact(proofs[0]); expr const * f2 = get_fact(proofs[1]); if (num_proofs == 2 && is_complement(f1, f2)) { SASSERT(is_false(new_fact)); } else { SASSERT(is_or(f1)); app * cls = to_app(f1); unsigned cls_sz = cls->get_num_args(); CTRACE("cunit_bug", !(num_proofs == cls_sz || (num_proofs == cls_sz + 1 && is_false(new_fact))), for (unsigned i = 0; i < num_proofs; i++) tout << mk_pp(get_fact(proofs[i]), *this) << "\n"; tout << "===>\n"; tout << mk_pp(new_fact, *this) << "\n";); // // typically: num_proofs == cls_sz || (num_proofs == cls_sz + 1 && is_false(new_fact)) // but formula could have repeated literals that are merged in the clausal representation. // unsigned num_matches = 0; for (unsigned i = 0; i < cls_sz; i++) { expr * lit = cls->get_arg(i); unsigned j = 1; for (; j < num_proofs; j++) { if (is_complement(lit, get_fact(proofs[j]))) { num_matches++; break; } } if (j == num_proofs) { CTRACE("unit_bug1", new_fact != lit, tout << mk_ll_pp(new_fact, *this) << "\n" << mk_ll_pp(lit, *this) << "\n";); SASSERT(new_fact == lit); } } SASSERT(num_matches == cls_sz || num_matches == cls_sz - 1); SASSERT(num_matches != cls_sz || is_false(new_fact)); } #endif proof * pr = mk_app(m_basic_family_id, PR_UNIT_RESOLUTION, args.size(), args.c_ptr()); TRACE("unit_resolution", tout << "unit_resolution using fact\n" << mk_ll_pp(pr, *this);); return pr; } proof * ast_manager::mk_hypothesis(expr * h) { return mk_app(m_basic_family_id, PR_HYPOTHESIS, h); } proof * ast_manager::mk_lemma(proof * p, expr * lemma) { if (!p) return p; SASSERT(has_fact(p)); CTRACE("mk_lemma", !is_false(get_fact(p)), tout << mk_ll_pp(p, *this) << "\n";); SASSERT(is_false(get_fact(p))); return mk_app(m_basic_family_id, PR_LEMMA, p, lemma); } proof * ast_manager::mk_def_intro(expr * new_def) { SASSERT(is_bool(new_def)); return mk_proof(m_basic_family_id, PR_DEF_INTRO, new_def); } proof * ast_manager::mk_apply_defs(expr * n, expr * def, unsigned num_proofs, proof * const * proofs) { if (proofs_disabled()) return nullptr; ptr_buffer args; args.append(num_proofs, (expr**) proofs); args.push_back(mk_oeq(n, def)); return mk_app(m_basic_family_id, PR_APPLY_DEF, args.size(), args.c_ptr()); } proof * ast_manager::mk_iff_oeq(proof * p) { if (!p) return p; SASSERT(has_fact(p)); SASSERT(is_eq(get_fact(p)) || is_oeq(get_fact(p))); if (is_oeq(get_fact(p))) return p; app * iff = to_app(get_fact(p)); expr * lhs = iff->get_arg(0); expr * rhs = iff->get_arg(1); return mk_app(m_basic_family_id, PR_IFF_OEQ, p, mk_oeq(lhs, rhs)); } bool ast_manager::check_nnf_proof_parents(unsigned num_proofs, proof * const * proofs) const { for (unsigned i = 0; i < num_proofs; i++) { if (!has_fact(proofs[i])) return false; if (!is_oeq(get_fact(proofs[i]))) return false; } return true; } proof * ast_manager::mk_nnf_pos(expr * s, expr * t, unsigned num_proofs, proof * const * proofs) { if (proofs_disabled()) return nullptr; check_nnf_proof_parents(num_proofs, proofs); ptr_buffer args; args.append(num_proofs, (expr**) proofs); args.push_back(mk_oeq(s, t)); return mk_app(m_basic_family_id, PR_NNF_POS, args.size(), args.c_ptr()); } proof * ast_manager::mk_nnf_neg(expr * s, expr * t, unsigned num_proofs, proof * const * proofs) { if (proofs_disabled()) return nullptr; check_nnf_proof_parents(num_proofs, proofs); ptr_buffer args; args.append(num_proofs, (expr**) proofs); args.push_back(mk_oeq(mk_not(s), t)); return mk_app(m_basic_family_id, PR_NNF_NEG, args.size(), args.c_ptr()); } proof * ast_manager::mk_skolemization(expr * q, expr * e) { if (proofs_disabled()) return nullptr; SASSERT(is_bool(q)); SASSERT(is_bool(e)); return mk_app(m_basic_family_id, PR_SKOLEMIZE, mk_oeq(q, e)); } proof * ast_manager::mk_and_elim(proof * p, unsigned i) { if (proofs_disabled()) return nullptr; SASSERT(has_fact(p)); SASSERT(is_and(get_fact(p))); CTRACE("mk_and_elim", i >= to_app(get_fact(p))->get_num_args(), tout << "i: " << i << "\n" << mk_pp(get_fact(p), *this) << "\n";); SASSERT(i < to_app(get_fact(p))->get_num_args()); expr * f = to_app(get_fact(p))->get_arg(i); return mk_app(m_basic_family_id, PR_AND_ELIM, p, f); } proof * ast_manager::mk_not_or_elim(proof * p, unsigned i) { if (proofs_disabled()) return nullptr; SASSERT(has_fact(p)); SASSERT(is_not(get_fact(p))); SASSERT(is_or(to_app(get_fact(p))->get_arg(0))); app * or_app = to_app(to_app(get_fact(p))->get_arg(0)); SASSERT(i < or_app->get_num_args()); expr * c = or_app->get_arg(i); expr * f; if (is_not(c)) f = to_app(c)->get_arg(0); else f = mk_not(c); return mk_app(m_basic_family_id, PR_NOT_OR_ELIM, p, f); } proof* ast_manager::mk_clause_trail_elem(proof *pr, expr* e, decl_kind k) { ptr_buffer args; if (pr) args.push_back(pr); args.push_back(e); return mk_app(m_basic_family_id, k, 0, nullptr, args.size(), args.c_ptr()); } proof * ast_manager::mk_assumption_add(proof* pr, expr* e) { return mk_clause_trail_elem(pr, e, PR_ASSUMPTION_ADD); } proof * ast_manager::mk_lemma_add(proof* pr, expr* e) { return mk_clause_trail_elem(pr, e, PR_LEMMA_ADD); } proof * ast_manager::mk_th_assumption_add(proof* pr, expr* e) { return mk_clause_trail_elem(pr, e, PR_TH_ASSUMPTION_ADD); } proof * ast_manager::mk_th_lemma_add(proof* pr, expr* e) { return mk_clause_trail_elem(pr, e, PR_TH_LEMMA_ADD); } proof * ast_manager::mk_redundant_del(expr* e) { return mk_clause_trail_elem(nullptr, e, PR_REDUNDANT_DEL); } proof * ast_manager::mk_clause_trail(unsigned n, proof* const* ps) { ptr_buffer args; args.append(n, (expr**) ps); args.push_back(mk_false()); return mk_app(m_basic_family_id, PR_CLAUSE_TRAIL, 0, nullptr, args.size(), args.c_ptr()); } proof * ast_manager::mk_th_lemma( family_id tid, expr * fact, unsigned num_proofs, proof * const * proofs, unsigned num_params, parameter const* params ) { if (proofs_disabled()) return nullptr; ptr_buffer args; vector parameters; parameters.push_back(parameter(get_family_name(tid))); for (unsigned i = 0; i < num_params; ++i) { parameters.push_back(params[i]); } args.append(num_proofs, (expr**) proofs); args.push_back(fact); return mk_app(m_basic_family_id, PR_TH_LEMMA, num_params+1, parameters.c_ptr(), args.size(), args.c_ptr()); } proof* ast_manager::mk_hyper_resolve(unsigned num_premises, proof* const* premises, expr* concl, svector > const& positions, vector const& substs) { ptr_vector fmls; SASSERT(positions.size() + 1 == substs.size()); for (unsigned i = 0; i < num_premises; ++i) { TRACE("hyper_res", tout << mk_pp(premises[i], *this) << "\n";); fmls.push_back(get_fact(premises[i])); } SASSERT(is_bool(concl)); vector params; for (unsigned i = 0; i < substs.size(); ++i) { expr_ref_vector const& vec = substs[i]; for (unsigned j = 0; j < vec.size(); ++j) { params.push_back(parameter(vec[j])); } if (i + 1 < substs.size()) { params.push_back(parameter(positions[i].first)); params.push_back(parameter(positions[i].second)); } } TRACE("hyper_res", for (unsigned i = 0; i < params.size(); ++i) { params[i].display(tout); tout << "\n"; }); ptr_vector sorts; ptr_vector args; for (unsigned i = 0; i < num_premises; ++i) { sorts.push_back(mk_proof_sort()); args.push_back(premises[i]); } sorts.push_back(mk_bool_sort()); args.push_back(concl); app* result = mk_app(m_basic_family_id, PR_HYPER_RESOLVE, params.size(), params.c_ptr(), args.size(), args.c_ptr()); SASSERT(result->get_family_id() == m_basic_family_id); SASSERT(result->get_decl_kind() == PR_HYPER_RESOLVE); return result; } bool ast_manager::is_hyper_resolve( proof* p, proof_ref_vector& premises, expr_ref& conclusion, svector > & positions, vector & substs) { if (!is_hyper_resolve(p)) { return false; } unsigned sz = p->get_num_args(); SASSERT(sz > 0); for (unsigned i = 0; i + 1 < sz; ++i) { premises.push_back(to_app(p->get_arg(i))); } conclusion = p->get_arg(sz-1); func_decl* d = p->get_decl(); unsigned num_p = d->get_num_parameters(); parameter const* params = d->get_parameters(); substs.push_back(expr_ref_vector(*this)); for (unsigned i = 0; i < num_p; ++i) { if (params[i].is_int()) { SASSERT(i + 1 < num_p); SASSERT(params[i+1].is_int()); unsigned x = static_cast(params[i].get_int()); unsigned y = static_cast(params[i+1].get_int()); positions.push_back(std::make_pair(x, y)); substs.push_back(expr_ref_vector(*this)); ++i; } else { SASSERT(params[i].is_ast()); ast* a = params[i].get_ast(); SASSERT(is_expr(a)); substs.back().push_back(to_expr(a)); } } return true; } // ----------------------------------- // // ast_mark // // ----------------------------------- bool ast_mark::is_marked(ast * n) const { if (is_decl(n)) return m_decl_marks.is_marked(to_decl(n)); else return m_expr_marks.is_marked(to_expr(n)); } void ast_mark::mark(ast * n, bool flag) { if (is_decl(n)) return m_decl_marks.mark(to_decl(n), flag); else return m_expr_marks.mark(to_expr(n), flag); } void ast_mark::reset() { m_decl_marks.reset(); m_expr_marks.reset(); } // ----------------------------------- // // scoped_mark // // ----------------------------------- void scoped_mark::mark(ast * n, bool flag) { SASSERT(flag); mark(n); } void scoped_mark::mark(ast * n) { if (!ast_mark::is_marked(n)) { m_stack.push_back(n); ast_mark::mark(n, true); } } void scoped_mark::reset() { ast_mark::reset(); m_stack.reset(); m_lim.reset(); } void scoped_mark::push_scope() { m_lim.push_back(m_stack.size()); } void scoped_mark::pop_scope() { unsigned new_size = m_stack.size(); unsigned old_size = m_lim.back(); for (unsigned i = old_size; i < new_size; ++i) { ast_mark::mark(m_stack[i].get(), false); } m_lim.pop_back(); m_stack.resize(old_size); } void scoped_mark::pop_scope(unsigned num_scopes) { for (unsigned i = 0; i < num_scopes; ++i) { pop_scope(); } } // Added by KLM for use in GDB // show an expr_ref on stdout void prexpr(expr_ref &e){ std::cout << mk_pp(e.get(), e.get_manager()) << std::endl; } void ast_manager::show_id_gen(){ std::cout << "id_gen: " << m_expr_id_gen.show_hash() << " " << m_decl_id_gen.show_hash() << "\n"; } z3-z3-4.8.7/src/ast/ast.h000066400000000000000000003164321356505360400147560ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: ast.h Abstract: Expression DAG Author: Leonardo de Moura (leonardo) 2006-09-18. Revision History: --*/ #ifndef AST_H_ #define AST_H_ #include "util/vector.h" #include "util/hashtable.h" #include "util/buffer.h" #include "util/symbol.h" #include "util/rational.h" #include "util/hash.h" #include "util/optional.h" #include "util/trace.h" #include "util/bit_vector.h" #include "util/symbol_table.h" #include "util/tptr.h" #include "util/memory_manager.h" #include "util/small_object_allocator.h" #include "util/obj_ref.h" #include "util/ref_vector.h" #include "util/ref_buffer.h" #include "util/obj_mark.h" #include "util/obj_hashtable.h" #include "util/id_gen.h" #include "util/map.h" #include "util/parray.h" #include "util/dictionary.h" #include "util/chashtable.h" #include "util/z3_exception.h" #include "util/dependency.h" #include "util/rlimit.h" #define RECYCLE_FREE_AST_INDICES #ifdef _MSC_VER #pragma warning(disable : 4200) #pragma warning(disable : 4355) #endif #ifdef _MSC_VER # define Z3_NORETURN __declspec(noreturn) #else # define Z3_NORETURN [[noreturn]] #endif class ast; class ast_manager; /** \brief Generic exception for AST related errors. We used to use fatal_error_msg to report errors inside plugins. */ class ast_exception : public default_exception { public: ast_exception(std::string && msg) : default_exception(std::move(msg)) {} }; typedef int family_id; const family_id null_family_id = -1; // ----------------------------------- // // parameter // // ----------------------------------- /** \brief Interpreted function declarations and sorts may have parameters that are used to encode extra information associated with them. */ class parameter { public: enum kind_t { PARAM_INT, PARAM_AST, PARAM_SYMBOL, PARAM_RATIONAL, PARAM_DOUBLE, // PARAM_EXTERNAL is used for handling decl_plugin specific parameters. // For example, it is used for handling mpf numbers in float_decl_plugin, // and irrational algebraic numbers in arith_decl_plugin. // PARAM_EXTERNAL is not supported by z3 low level input format. This format is legacy, so // this is not a big problem. // Remark: PARAM_EXTERNAL can only be used to decorate theory decls. PARAM_EXTERNAL }; private: kind_t m_kind; // It is not possible to use tag pointers, since symbols are already tagged. union { int m_int; // for PARAM_INT ast* m_ast; // for PARAM_AST void const* m_symbol; // for PARAM_SYMBOL rational* m_rational; // for PARAM_RATIONAL double m_dval; // for PARAM_DOUBLE (remark: this is not used in float_decl_plugin) unsigned m_ext_id; // for PARAM_EXTERNAL }; public: parameter(): m_kind(PARAM_INT), m_int(0) {} explicit parameter(int val): m_kind(PARAM_INT), m_int(val) {} explicit parameter(unsigned val): m_kind(PARAM_INT), m_int(val) {} explicit parameter(ast * p): m_kind(PARAM_AST), m_ast(p) {} explicit parameter(symbol const & s): m_kind(PARAM_SYMBOL), m_symbol(s.c_ptr()) {} explicit parameter(rational const & r): m_kind(PARAM_RATIONAL), m_rational(alloc(rational, r)) {} explicit parameter(rational && r) : m_kind(PARAM_RATIONAL), m_rational(alloc(rational, std::move(r))) {} explicit parameter(double d):m_kind(PARAM_DOUBLE), m_dval(d) {} explicit parameter(const char *s):m_kind(PARAM_SYMBOL), m_symbol(symbol(s).c_ptr()) {} explicit parameter(unsigned ext_id, bool):m_kind(PARAM_EXTERNAL), m_ext_id(ext_id) {} parameter(parameter const&); parameter(parameter && other) : m_kind(other.m_kind) { switch (other.m_kind) { case PARAM_INT: m_int = other.get_int(); break; case PARAM_AST: m_ast = other.get_ast(); break; case PARAM_SYMBOL: m_symbol = other.m_symbol; break; case PARAM_RATIONAL: m_rational = nullptr; std::swap(m_rational, other.m_rational); break; case PARAM_DOUBLE: m_dval = other.m_dval; break; case PARAM_EXTERNAL: m_ext_id = other.m_ext_id; break; default: UNREACHABLE(); break; } } ~parameter(); parameter& operator=(parameter const& other); kind_t get_kind() const { return m_kind; } bool is_int() const { return m_kind == PARAM_INT; } bool is_ast() const { return m_kind == PARAM_AST; } bool is_symbol() const { return m_kind == PARAM_SYMBOL; } bool is_rational() const { return m_kind == PARAM_RATIONAL; } bool is_double() const { return m_kind == PARAM_DOUBLE; } bool is_external() const { return m_kind == PARAM_EXTERNAL; } bool is_int(int & i) const { return is_int() && (i = get_int(), true); } bool is_ast(ast * & a) const { return is_ast() && (a = get_ast(), true); } bool is_symbol(symbol & s) const { return is_symbol() && (s = get_symbol(), true); } bool is_rational(rational & r) const { return is_rational() && (r = get_rational(), true); } bool is_double(double & d) const { return is_double() && (d = get_double(), true); } bool is_external(unsigned & id) const { return is_external() && (id = get_ext_id(), true); } /** \brief This method is invoked when the parameter is attached to a function declaration or sort. */ void init_eh(ast_manager & m); /** \brief This method is invoked before the function declaration or sort associated with the parameter is deleted. */ void del_eh(ast_manager & m, family_id fid); int get_int() const { SASSERT(is_int()); return m_int; } ast * get_ast() const { SASSERT(is_ast()); return m_ast; } symbol get_symbol() const { SASSERT(is_symbol()); return symbol::mk_symbol_from_c_ptr(m_symbol); } rational const & get_rational() const { SASSERT(is_rational()); return *m_rational; } double get_double() const { SASSERT(is_double()); return m_dval; } unsigned get_ext_id() const { SASSERT(is_external()); return m_ext_id; } bool operator==(parameter const & p) const; bool operator!=(parameter const & p) const { return !operator==(p); } unsigned hash() const; std::ostream& display(std::ostream& out) const; }; inline std::ostream& operator<<(std::ostream& out, parameter const & p) { return p.display(out); } void display_parameters(std::ostream & out, unsigned n, parameter const * p); // ----------------------------------- // // family_manager // // ----------------------------------- /** \brief Interpreted functions and sorts are grouped in families. Each family has an unique ID. This class models the mapping between symbols (family names) and the unique IDs. */ class family_manager { family_id m_next_id; symbol_table m_families; svector m_names; public: family_manager():m_next_id(0) {} /** \brief Return the family_id for s, a new id is created if !has_family(s) If has_family(s), then this method is equivalent to get_family_id(s) */ family_id mk_family_id(symbol const & s); /** \brief Return the family_id for s, return null_family_id if s was not registered in the manager. */ family_id get_family_id(symbol const & s) const; bool has_family(symbol const & s) const; void get_dom(svector& dom) const { m_families.get_dom(dom); } void get_range(svector & range) const { m_families.get_range(range); } symbol const & get_name(family_id fid) const { return fid >= 0 && fid < static_cast(m_names.size()) ? m_names[fid] : symbol::null; } bool has_family(family_id fid) const { return fid >= 0 && fid < static_cast(m_names.size()); } }; // ----------------------------------- // // decl_info // // ----------------------------------- /** \brief Each interpreted function declaration or sort has a kind. Kinds are used to identify interpreted functions and sorts in a family. */ typedef int decl_kind; const decl_kind null_decl_kind = -1; /** \brief Interpreted function declarations and sorts are associated with a family id, kind, and parameters. */ class decl_info { family_id m_family_id; decl_kind m_kind; vector m_parameters; public: bool m_private_parameters; decl_info(family_id family_id = null_family_id, decl_kind k = null_decl_kind, unsigned num_parameters = 0, parameter const * parameters = nullptr, bool private_params = false); decl_info(decl_info const& other); ~decl_info() {} void init_eh(ast_manager & m); void del_eh(ast_manager & m); family_id get_family_id() const { return m_family_id; } decl_kind get_decl_kind() const { return m_kind; } unsigned get_num_parameters() const { return m_parameters.size(); } parameter const & get_parameter(unsigned idx) const { return m_parameters[idx]; } parameter const * get_parameters() const { return m_parameters.begin(); } bool private_parameters() const { return m_private_parameters; } struct iterator { decl_info const& d; iterator(decl_info const& d) : d(d) {} parameter const* begin() const { return d.get_parameters(); } parameter const* end() const { return begin() + d.get_num_parameters(); } }; iterator parameters() const { return iterator(*this); } unsigned hash() const; bool operator==(decl_info const & info) const; }; std::ostream & operator<<(std::ostream & out, decl_info const & info); // ----------------------------------- // // sort_size // // ----------------------------------- /** \brief Models the number of elements of a sort. */ class sort_size { enum kind_t { SS_FINITE, // For some sorts it may be too expensive to compute the // number of elements precisely (e.g., arrays). In this // cases, we mark the sort as too big. That is, the number // of elements is at least bigger than 2^64. SS_FINITE_VERY_BIG, SS_INFINITE } m_kind; uint64_t m_size; // It is only meaningful if m_kind == SS_FINITE sort_size(kind_t k, uint64_t r):m_kind(k), m_size(r) {} public: sort_size():m_kind(SS_INFINITE) {} sort_size(uint64_t const & sz):m_kind(SS_FINITE), m_size(sz) {} sort_size(sort_size const& other): m_kind(other.m_kind), m_size(other.m_size) {} explicit sort_size(rational const& r) { if (r.is_uint64()) { m_kind = SS_FINITE; m_size = r.get_uint64(); } else { m_kind = SS_FINITE_VERY_BIG; m_size = 0; } } static sort_size mk_infinite() { return sort_size(SS_INFINITE, 0); } static sort_size mk_very_big() { return sort_size(SS_FINITE_VERY_BIG, 0); } static sort_size mk_finite(uint64_t r) { return sort_size(SS_FINITE, r); } bool is_infinite() const { return m_kind == SS_INFINITE; } bool is_very_big() const { return m_kind == SS_FINITE_VERY_BIG; } bool is_finite() const { return m_kind == SS_FINITE; } static bool is_very_big_base2(unsigned power) { return power >= 64; } uint64_t size() const { SASSERT(is_finite()); return m_size; } }; std::ostream& operator<<(std::ostream& out, sort_size const & ss); // ----------------------------------- // // sort_info // // ----------------------------------- /** \brief Extra information that may be attached to interpreted sorts. */ class sort_info : public decl_info { sort_size m_num_elements; public: sort_info(family_id family_id = null_family_id, decl_kind k = null_decl_kind, unsigned num_parameters = 0, parameter const * parameters = nullptr, bool private_parameters = false): decl_info(family_id, k, num_parameters, parameters, private_parameters) { } sort_info(family_id family_id, decl_kind k, uint64_t num_elements, unsigned num_parameters = 0, parameter const * parameters = nullptr, bool private_parameters = false): decl_info(family_id, k, num_parameters, parameters, private_parameters), m_num_elements(num_elements) { } sort_info(family_id family_id, decl_kind k, sort_size const& num_elements, unsigned num_parameters = 0, parameter const * parameters = nullptr, bool private_parameters = false): decl_info(family_id, k, num_parameters, parameters, private_parameters), m_num_elements(num_elements) { } sort_info(sort_info const& other) : decl_info(other), m_num_elements(other.m_num_elements) { } sort_info(decl_info const& di, sort_size const& num_elements) : decl_info(di), m_num_elements(num_elements) {} ~sort_info() {} bool is_infinite() const { return m_num_elements.is_infinite(); } bool is_very_big() const { return m_num_elements.is_very_big(); } sort_size const & get_num_elements() const { return m_num_elements; } void set_num_elements(sort_size const& s) { m_num_elements = s; } }; std::ostream & operator<<(std::ostream & out, sort_info const & info); // ----------------------------------- // // func_decl_info // // ----------------------------------- /** \brief Extra information that may be attached to interpreted function decls. */ struct func_decl_info : public decl_info { bool m_left_assoc:1; bool m_right_assoc:1; bool m_flat_associative:1; bool m_commutative:1; bool m_chainable:1; bool m_pairwise:1; bool m_injective:1; bool m_idempotent:1; bool m_skolem:1; bool m_lambda:1; func_decl_info(family_id family_id = null_family_id, decl_kind k = null_decl_kind, unsigned num_parameters = 0, parameter const * parameters = nullptr); ~func_decl_info() {} bool is_associative() const { return m_left_assoc && m_right_assoc; } bool is_left_associative() const { return m_left_assoc; } bool is_right_associative() const { return m_right_assoc; } bool is_flat_associative() const { return m_flat_associative; } bool is_commutative() const { return m_commutative; } bool is_chainable() const { return m_chainable; } bool is_pairwise() const { return m_pairwise; } bool is_injective() const { return m_injective; } bool is_idempotent() const { return m_idempotent; } bool is_skolem() const { return m_skolem; } bool is_lambda() const { return m_lambda; } void set_associative(bool flag = true) { m_left_assoc = flag; m_right_assoc = flag; } void set_left_associative(bool flag = true) { m_left_assoc = flag; } void set_right_associative(bool flag = true) { m_right_assoc = flag; } void set_flat_associative(bool flag = true) { m_flat_associative = flag; } void set_commutative(bool flag = true) { m_commutative = flag; } void set_chainable(bool flag = true) { m_chainable = flag; } void set_pairwise(bool flag = true) { m_pairwise = flag; } void set_injective(bool flag = true) { m_injective = flag; } void set_idempotent(bool flag = true) { m_idempotent = flag; } void set_skolem(bool flag = true) { m_skolem = flag; } void set_lambda(bool flag = true) { m_lambda = flag; } bool operator==(func_decl_info const & info) const; // Return true if the func_decl_info is equivalent to the null one (i.e., it does not contain any useful info). bool is_null() const { return get_family_id() == null_family_id && !is_left_associative() && !is_right_associative() && !is_commutative() && !is_chainable() && !is_pairwise() && !is_injective() && !is_idempotent() && !is_skolem(); } }; std::ostream & operator<<(std::ostream & out, func_decl_info const & info); // ----------------------------------- // // ast // // ----------------------------------- typedef enum { AST_APP, AST_VAR, AST_QUANTIFIER, AST_SORT, AST_FUNC_DECL } ast_kind; char const * get_ast_kind_name(ast_kind k); class shared_occs_mark; class ast { protected: friend class ast_manager; unsigned m_id; unsigned m_kind:16; // Warning: the marks should be used carefully, since they are shared. unsigned m_mark1:1; unsigned m_mark2:1; // Private mark used by shared_occs functor // Motivation for this field: // - A mark cannot be used by more than one owner. // So, it is only safe to use mark by "self-contained" code. // They should be viewed as temporary information. // - The functor shared_occs is used by some AST pretty printers. // - So, a code that uses marks could not use the pretty printer if // shared_occs used one of the public marks. // - This was a constant source of assertion violations. unsigned m_mark_shared_occs:1; friend class shared_occs_mark; void mark_so(bool flag) { m_mark_shared_occs = flag; } void reset_mark_so() { m_mark_shared_occs = false; } bool is_marked_so() const { return m_mark_shared_occs; } unsigned m_ref_count; unsigned m_hash; #ifdef Z3DEBUG // In debug mode, we store who is the owner of the mark. void * m_mark1_owner; void * m_mark2_owner; #endif void inc_ref() { SASSERT(m_ref_count < UINT_MAX); m_ref_count ++; } void dec_ref() { SASSERT(m_ref_count > 0); m_ref_count --; } ast(ast_kind k):m_id(UINT_MAX), m_kind(k), m_mark1(false), m_mark2(false), m_mark_shared_occs(false), m_ref_count(0) { DEBUG_CODE({ m_mark1_owner = 0; m_mark2_owner = 0; }); } public: unsigned get_id() const { return m_id; } unsigned get_ref_count() const { return m_ref_count; } ast_kind get_kind() const { return static_cast(m_kind); } unsigned hash() const { return m_hash; } #ifdef Z3DEBUG void mark1(bool flag, void * owner) { SASSERT(m_mark1_owner == 0 || m_mark1_owner == owner); m_mark1 = flag; m_mark1_owner = owner; } void mark2(bool flag, void * owner) { SASSERT(m_mark2_owner == 0 || m_mark2_owner == owner); m_mark2 = flag; m_mark2_owner = owner; } void reset_mark1(void * owner) { SASSERT(m_mark1_owner == 0 || m_mark1_owner == owner); m_mark1 = false; m_mark1_owner = 0; } void reset_mark2(void * owner) { SASSERT(m_mark2_owner == 0 || m_mark2_owner == owner); m_mark2 = false; m_mark2_owner = 0; } bool is_marked1(void * owner) const { SASSERT(m_mark1_owner == 0 || m_mark1_owner == owner); return m_mark1; } bool is_marked2(void * owner) const { SASSERT(m_mark2_owner == 0 || m_mark2_owner == owner); return m_mark2; } #define AST_MARK1(A,F,O) A->mark1(F, O) #define AST_MARK2(A,F,O) A->mark2(F, O) #define AST_RESET_MARK1(A,O) A->reset_mark1(O) #define AST_RESET_MARK2(A,O) A->reset_mark2(O) #define AST_IS_MARKED1(A,O) A->is_marked1(O) #define AST_IS_MARKED2(A,O) A->is_marked2(O) #else void mark1(bool flag) { m_mark1 = flag; } void mark2(bool flag) { m_mark2 = flag; } void reset_mark1() { m_mark1 = false; } void reset_mark2() { m_mark2 = false; } bool is_marked1() const { return m_mark1; } bool is_marked2() const { return m_mark2; } #define AST_MARK1(A,F,O) A->mark1(F) #define AST_MARK2(A,F,O) A->mark2(F) #define AST_RESET_MARK1(A,O) A->reset_mark1() #define AST_RESET_MARK2(A,O) A->reset_mark2() #define AST_IS_MARKED1(A,O) A->is_marked1() #define AST_IS_MARKED2(A,O) A->is_marked2() #endif }; #define MATCH_TERNARY(_MATCHER_) \ bool _MATCHER_(expr const* n, expr*& a1, expr*& a2, expr *& a3) const { \ if (_MATCHER_(n) && to_app(n)->get_num_args() == 3) { \ a1 = to_app(n)->get_arg(0); a2 = to_app(n)->get_arg(1); a3 = to_app(n)->get_arg(2); return true; } \ return false; \ } #define MATCH_BINARY(_MATCHER_) \ bool _MATCHER_(expr const* n, expr*& s, expr*& t) const { \ if (_MATCHER_(n) && to_app(n)->get_num_args() == 2) { s = to_app(n)->get_arg(0); t = to_app(n)->get_arg(1); return true; } \ return false; \ } #define MATCH_UNARY(_MATCHER_) \ bool _MATCHER_(expr const* n, expr*& s) const { \ if (_MATCHER_(n) && to_app(n)->get_num_args() == 1) { s = to_app(n)->get_arg(0); return true; } \ return false; \ } // ----------------------------------- // // decl // // ----------------------------------- /** The ids of expressions and declarations are in different ranges. */ const unsigned c_first_decl_id = (1u << 31u); /** \brief Superclass for function declarations and sorts. */ class decl : public ast { protected: friend class ast_manager; symbol m_name; decl_info * m_info; decl(ast_kind k, symbol const & name, decl_info * info):ast(k), m_name(name), m_info(info) {} public: unsigned get_decl_id() const { SASSERT(get_id() >= c_first_decl_id); return get_id() - c_first_decl_id; } symbol const & get_name() const { return m_name; } decl_info * get_info() const { return m_info; } family_id get_family_id() const { return m_info == nullptr ? null_family_id : m_info->get_family_id(); } decl_kind get_decl_kind() const { return m_info == nullptr ? null_decl_kind : m_info->get_decl_kind(); } unsigned get_num_parameters() const { return m_info == nullptr ? 0 : m_info->get_num_parameters(); } parameter const & get_parameter(unsigned idx) const { return m_info->get_parameter(idx); } parameter const * get_parameters() const { return m_info == nullptr ? nullptr : m_info->get_parameters(); } bool private_parameters() const { return m_info != nullptr && m_info->private_parameters(); } struct iterator { decl const& d; iterator(decl const& d) : d(d) {} parameter const* begin() const { return d.get_parameters(); } parameter const* end() const { return begin() + d.get_num_parameters(); } }; iterator parameters() const { return iterator(*this); } }; // ----------------------------------- // // sort // // ----------------------------------- class sort : public decl { friend class ast_manager; static unsigned get_obj_size() { return sizeof(sort); } sort(symbol const & name, sort_info * info):decl(AST_SORT, name, info) {} public: sort_info * get_info() const { return static_cast(m_info); } bool is_infinite() const { return get_info() == nullptr || get_info()->is_infinite(); } bool is_very_big() const { return get_info() == nullptr || get_info()->is_very_big(); } bool is_sort_of(family_id fid, decl_kind k) const { return get_family_id() == fid && get_decl_kind() == k; } sort_size const & get_num_elements() const { return get_info()->get_num_elements(); } void set_num_elements(sort_size const& s) { get_info()->set_num_elements(s); } unsigned get_size() const { return get_obj_size(); } }; // ----------------------------------- // // func_decl // // ----------------------------------- class func_decl : public decl { friend class ast_manager; unsigned m_arity; sort * m_range; sort * m_domain[0]; static unsigned get_obj_size(unsigned arity) { return sizeof(func_decl) + arity * sizeof(sort *); } func_decl(symbol const & name, unsigned arity, sort * const * domain, sort * range, func_decl_info * info); public: func_decl_info * get_info() const { return static_cast(m_info); } bool is_associative() const { return get_info() != nullptr && get_info()->is_associative(); } bool is_left_associative() const { return get_info() != nullptr && get_info()->is_left_associative(); } bool is_right_associative() const { return get_info() != nullptr && get_info()->is_right_associative(); } bool is_flat_associative() const { return get_info() != nullptr && get_info()->is_flat_associative(); } bool is_commutative() const { return get_info() != nullptr && get_info()->is_commutative(); } bool is_chainable() const { return get_info() != nullptr && get_info()->is_chainable(); } bool is_pairwise() const { return get_info() != nullptr && get_info()->is_pairwise(); } bool is_injective() const { return get_info() != nullptr && get_info()->is_injective(); } bool is_skolem() const { return get_info() != nullptr && get_info()->is_skolem(); } bool is_lambda() const { return get_info() != nullptr && get_info()->is_lambda(); } bool is_idempotent() const { return get_info() != nullptr && get_info()->is_idempotent(); } unsigned get_arity() const { return m_arity; } sort * get_domain(unsigned idx) const { SASSERT(idx < get_arity()); return m_domain[idx]; } sort * const * get_domain() const { return m_domain; } sort * get_range() const { return m_range; } unsigned get_size() const { return get_obj_size(m_arity); } sort * const * begin() const { return get_domain(); } sort * const * end() const { return get_domain() + get_arity(); } }; // ----------------------------------- // // expression // // ----------------------------------- /** \brief Superclass for applications, variables and quantifiers. */ class expr : public ast { protected: friend class ast_manager; expr(ast_kind k):ast(k) {} public: }; // ----------------------------------- // // application // // ----------------------------------- #define APP_DEPTH_NUM_BITS 16 const unsigned c_max_depth = ((1 << APP_DEPTH_NUM_BITS) - 1); struct app_flags { unsigned m_depth:APP_DEPTH_NUM_BITS; // if app is to deep, it doesn't matter. unsigned m_ground:1; // application does not have free variables or nested quantifiers. unsigned m_has_quantifiers:1; // application has nested quantifiers. unsigned m_has_labels:1; // application has nested labels. }; class app : public expr { friend class ast_manager; func_decl * m_decl; unsigned m_num_args; expr * m_args[0]; static app_flags g_constant_flags; // remark: store term depth in the end of the app. the depth is only stored if the num_args > 0 static unsigned get_obj_size(unsigned num_args) { return num_args == 0 ? sizeof(app) : sizeof(app) + num_args * sizeof(expr *) + sizeof(app_flags); } friend class tmp_app; app_flags * flags() const { return m_num_args == 0 ? &g_constant_flags : reinterpret_cast(const_cast(m_args + m_num_args)); } app(func_decl * decl, unsigned num_args, expr * const * args); public: func_decl * get_decl() const { return m_decl; } family_id get_family_id() const { return get_decl()->get_family_id(); } decl_kind get_decl_kind() const { return get_decl()->get_decl_kind(); } symbol const& get_name() const { return get_decl()->get_name(); } unsigned get_num_parameters() const { return get_decl()->get_num_parameters(); } parameter const& get_parameter(unsigned idx) const { return get_decl()->get_parameter(idx); } parameter const* get_parameters() const { return get_decl()->get_parameters(); } bool is_app_of(family_id fid, decl_kind k) const { return get_family_id() == fid && get_decl_kind() == k; } unsigned get_num_args() const { return m_num_args; } expr * get_arg(unsigned idx) const { SASSERT(idx < m_num_args); return m_args[idx]; } expr * const * get_args() const { return m_args; } unsigned get_size() const { return get_obj_size(get_num_args()); } expr * const * begin() const { return m_args; } expr * const * end() const { return m_args + m_num_args; } unsigned get_depth() const { return flags()->m_depth; } bool is_ground() const { return flags()->m_ground; } bool has_quantifiers() const { return flags()->m_has_quantifiers; } bool has_labels() const { return flags()->m_has_labels; } }; // ----------------------------------- // // temporary application: little hack to avoid // the creation of temporary expressions to just // check the presence of the expression in // some container/index. // // ----------------------------------- class tmp_app { unsigned m_num_args; char * m_data; public: tmp_app(unsigned num_args): m_num_args(num_args) { unsigned sz = app::get_obj_size(num_args); m_data = alloc_svect(char, sz); memset(m_data, 0, sz); get_app()->m_num_args = m_num_args; } ~tmp_app() { dealloc_svect(m_data); } app * get_app() { return reinterpret_cast(m_data); } expr ** get_args() { return get_app()->m_args; } void set_decl(func_decl * d) { get_app()->m_decl = d; } void set_num_args(unsigned num_args) { get_app()->m_num_args = num_args; } void set_arg(unsigned idx, expr * arg) { get_args()[idx] = arg; SASSERT(get_app()->get_arg(idx) == arg); } void copy(app * source) { SASSERT(source->get_num_args() <= m_num_args); new (m_data) app(source->get_decl(), source->get_num_args(), source->get_args()); SASSERT(get_app()->get_decl() == source->get_decl()); SASSERT(get_app()->get_arg(0) == source->get_arg(0)); SASSERT(get_app()->get_arg(1) == source->get_arg(1)); } void copy_swapping_args(app * source) { SASSERT(source->get_num_args() == 2 && m_num_args >= 2); expr * args[2] = { source->get_arg(1), source->get_arg(0) }; new (m_data) app(source->get_decl(), 2, args); SASSERT(get_app()->get_decl() == source->get_decl()); SASSERT(get_app()->get_arg(0) == source->get_arg(1)); SASSERT(get_app()->get_arg(1) == source->get_arg(0)); } }; // ----------------------------------- // // variables // // ----------------------------------- class var : public expr { friend class ast_manager; unsigned m_idx; sort * m_sort; static unsigned get_obj_size() { return sizeof(var); } var(unsigned idx, sort * s):expr(AST_VAR), m_idx(idx), m_sort(s) {} public: unsigned get_idx() const { return m_idx; } sort * get_sort() const { return m_sort; } unsigned get_size() const { return get_obj_size(); } }; // ----------------------------------- // // quantifier // // ----------------------------------- enum quantifier_kind { forall_k, exists_k, lambda_k }; class quantifier : public expr { friend class ast_manager; quantifier_kind m_kind; unsigned m_num_decls; expr * m_expr; sort * m_sort; unsigned m_depth; // extra fields int m_weight; bool m_has_unused_vars; bool m_has_labels; symbol m_qid; symbol m_skid; unsigned m_num_patterns; unsigned m_num_no_patterns; char m_patterns_decls[0]; static unsigned get_obj_size(unsigned num_decls, unsigned num_patterns, unsigned num_no_patterns) { return sizeof(quantifier) + num_decls * (sizeof(sort *) + sizeof(symbol)) + (num_patterns + num_no_patterns) * sizeof(expr*); } quantifier(quantifier_kind k, unsigned num_decls, sort * const * decl_sorts, symbol const * decl_names, expr * body, sort* s, int weight, symbol const & qid, symbol const & skid, unsigned num_patterns, expr * const * patterns, unsigned num_no_patterns, expr * const * no_patterns); quantifier(unsigned num_decls, sort * const * decl_sorts, symbol const * decl_names, expr * body, sort* sort); public: quantifier_kind get_kind() const { return m_kind; } // bool is_forall() const { return m_kind == forall_k; } // bool is_exists() const { return m_kind == exists_k; } // bool is_lambda() const { return m_kind == lambda_k; } unsigned get_num_decls() const { return m_num_decls; } sort * const * get_decl_sorts() const { return reinterpret_cast(m_patterns_decls); } symbol const * get_decl_names() const { return reinterpret_cast(get_decl_sorts() + m_num_decls); } sort * get_decl_sort(unsigned idx) const { return get_decl_sorts()[idx]; } symbol const & get_decl_name(unsigned idx) const { return get_decl_names()[idx]; } expr * get_expr() const { return m_expr; } sort * get_sort() const { return m_sort; } unsigned get_depth() const { return m_depth; } int get_weight() const { return m_weight; } symbol const & get_qid() const { return m_qid; } symbol const & get_skid() const { return m_skid; } unsigned get_num_patterns() const { return m_num_patterns; } expr * const * get_patterns() const { return reinterpret_cast(get_decl_names() + m_num_decls); } expr * get_pattern(unsigned idx) const { return get_patterns()[idx]; } unsigned get_num_no_patterns() const { return m_num_no_patterns; } expr * const * get_no_patterns() const { return reinterpret_cast(get_decl_names() + m_num_decls); } expr * get_no_pattern(unsigned idx) const { return get_no_patterns()[idx]; } bool has_patterns() const { return m_num_patterns > 0 || m_num_no_patterns > 0; } unsigned get_size() const { return get_obj_size(m_num_decls, m_num_patterns, m_num_no_patterns); } bool may_have_unused_vars() const { return m_has_unused_vars; } void set_no_unused_vars() { m_has_unused_vars = false; } bool has_labels() const { return m_has_labels; } unsigned get_num_children() const { return 1 + get_num_patterns() + get_num_no_patterns(); } expr * get_child(unsigned idx) const { SASSERT(idx < get_num_children()); if (idx == 0) return get_expr(); else if (idx <= get_num_patterns()) return get_pattern(idx - 1); else return get_no_pattern(idx - get_num_patterns() - 1); } }; // ----------------------------------- // // AST recognisers // // ----------------------------------- inline bool is_decl(ast const * n) { ast_kind k = n->get_kind(); return k == AST_FUNC_DECL || k == AST_SORT; } inline bool is_sort(ast const * n) { return n->get_kind() == AST_SORT; } inline bool is_func_decl(ast const * n) { return n->get_kind() == AST_FUNC_DECL; } inline bool is_expr(ast const * n) { return !is_decl(n); } inline bool is_app(ast const * n) { return n->get_kind() == AST_APP; } inline bool is_var(ast const * n) { return n->get_kind() == AST_VAR; } inline bool is_var(ast const * n, unsigned& idx) { return is_var(n) && (idx = static_cast(n)->get_idx(), true); } inline bool is_quantifier(ast const * n) { return n->get_kind() == AST_QUANTIFIER; } inline bool is_forall(ast const * n) { return is_quantifier(n) && static_cast(n)->get_kind() == forall_k; } inline bool is_exists(ast const * n) { return is_quantifier(n) && static_cast(n)->get_kind() == exists_k; } inline bool is_lambda(ast const * n) { return is_quantifier(n) && static_cast(n)->get_kind() == lambda_k; } // ----------------------------------- // // AST coercions // // ----------------------------------- inline decl * to_decl(ast * n) { SASSERT(is_decl(n)); return static_cast(n); } inline sort * to_sort(ast * n) { SASSERT(is_sort(n)); return static_cast(n); } inline func_decl * to_func_decl(ast * n) { SASSERT(is_func_decl(n)); return static_cast(n); } inline expr * to_expr(ast * n) { SASSERT(is_expr(n)); return static_cast(n); } inline app * to_app(ast * n) { SASSERT(is_app(n)); return static_cast(n); } inline var * to_var(ast * n) { SASSERT(is_var(n)); return static_cast(n); } inline quantifier * to_quantifier(ast * n) { SASSERT(is_quantifier(n)); return static_cast(n); } inline decl const * to_decl(ast const * n) { SASSERT(is_decl(n)); return static_cast(n); } inline sort const * to_sort(ast const * n) { SASSERT(is_sort(n)); return static_cast(n); } inline func_decl const * to_func_decl(ast const * n) { SASSERT(is_func_decl(n)); return static_cast(n); } inline expr const * to_expr(ast const * n) { SASSERT(is_expr(n)); return static_cast(n); } inline app const * to_app(ast const * n) { SASSERT(is_app(n)); return static_cast(n); } inline var const * to_var(ast const * n) { SASSERT(is_var(n)); return static_cast(n); } inline quantifier const * to_quantifier(ast const * n) { SASSERT(is_quantifier(n)); return static_cast(n); } // ----------------------------------- // // AST hash-consing // // ----------------------------------- unsigned get_node_hash(ast const * n); bool compare_nodes(ast const * n1, ast const * n2); unsigned get_node_size(ast const * n); unsigned get_asts_hash(unsigned sz, ast * const* ns, unsigned init); unsigned get_apps_hash(unsigned sz, app * const* ns, unsigned init); unsigned get_exprs_hash(unsigned sz, expr * const* ns, unsigned init); unsigned get_sorts_hash(unsigned sz, sort * const* ns, unsigned init); unsigned get_decl_hash(unsigned sz, func_decl* const* ns, unsigned init); // This is the internal comparison functor for hash-consing AST nodes. struct ast_eq_proc { bool operator()(ast const * n1, ast const * n2) const { return n1->hash() == n2->hash() && compare_nodes(n1, n2); } }; class ast_translation; class ast_table : public chashtable, ast_eq_proc> { public: void push_erase(ast * n); ast* pop_erase(); }; // ----------------------------------- // // decl_plugin // // ----------------------------------- /** \brief Auxiliary data-structure used to initialize the parser symbol tables. */ struct builtin_name { decl_kind m_kind; symbol m_name; builtin_name(char const * name, decl_kind k) : m_kind(k), m_name(name) {} }; /** \brief Each family of interpreted function declarations and sorts must provide a plugin to build sorts and decls of the family. */ class decl_plugin { protected: ast_manager * m_manager; family_id m_family_id; virtual void set_manager(ast_manager * m, family_id id) { SASSERT(m_manager == nullptr); m_manager = m; m_family_id = id; } virtual void inherit(decl_plugin* other_p, ast_translation& ) { } /** \brief Checks wether a log is being generated and, if necessary, adds the beginning of an "[attach-meaning]" line to that log. The theory solver should add some description of the meaning of the term in terms of the theory's internal reasoning to the end of the line and insert a line break. \param a the term that should be described. \return true if a log is being generated, false otherwise. */ bool log_constant_meaning_prelude(app * a); friend class ast_manager; public: decl_plugin():m_manager(nullptr), m_family_id(null_family_id) {} virtual ~decl_plugin() {} virtual void finalize() {} virtual decl_plugin * mk_fresh() = 0; family_id get_family_id() const { return m_family_id; } virtual sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) = 0; virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) = 0; virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const* parameters, unsigned num_args, expr * const * args, sort * range); /** \brief Return true if the plugin can decide whether two interpreted constants are equal or not. For all a, b: If is_value(a) and is_value(b) Then, are_equal(a, b) != are_distinct(a, b) The may be much more expensive than checking a pointer. We need this because some plugin values are too expensive too canonize. */ virtual bool is_value(app * a) const { return false; } /** \brief Return true if \c a is a unique plugin value. The following property should hold for unique theory values: For all a, b: If is_unique_value(a) and is_unique_value(b) Then, a == b (pointer equality) IFF the interpretations of these theory terms are equal. \remark This is a stronger version of is_value. */ virtual bool is_unique_value(app * a) const { return false; } virtual bool are_equal(app * a, app * b) const { return a == b; } virtual bool are_distinct(app * a, app * b) const { return a != b && is_unique_value(a) && is_unique_value(b); } virtual void get_op_names(svector & op_names, symbol const & logic = symbol()) {} virtual void get_sort_names(svector & sort_names, symbol const & logic = symbol()) {} virtual expr * get_some_value(sort * s) { return nullptr; } // Return true if the interpreted sort s does not depend on uninterpreted sorts. // This may be the case, for example, for array and datatype sorts. virtual bool is_fully_interp(sort * s) const { return true; } // Event handlers for deleting/translating PARAM_EXTERNAL virtual void del(parameter const & p) {} virtual parameter translate(parameter const & p, decl_plugin & target) { UNREACHABLE(); return p; } virtual bool is_considered_uninterpreted(func_decl * f) { return false; } }; // ----------------------------------- // // basic_decl_plugin (i.e., builtin plugin) // // ----------------------------------- enum basic_sort_kind { BOOL_SORT, PROOF_SORT }; enum basic_op_kind { OP_TRUE, OP_FALSE, OP_EQ, OP_DISTINCT, OP_ITE, OP_AND, OP_OR, OP_XOR, OP_NOT, OP_IMPLIES, OP_OEQ, LAST_BASIC_OP, PR_UNDEF, PR_TRUE, PR_ASSERTED, PR_GOAL, PR_MODUS_PONENS, PR_REFLEXIVITY, PR_SYMMETRY, PR_TRANSITIVITY, PR_TRANSITIVITY_STAR, PR_MONOTONICITY, PR_QUANT_INTRO, PR_BIND, PR_DISTRIBUTIVITY, PR_AND_ELIM, PR_NOT_OR_ELIM, PR_REWRITE, PR_REWRITE_STAR, PR_PULL_QUANT, PR_PUSH_QUANT, PR_ELIM_UNUSED_VARS, PR_DER, PR_QUANT_INST, PR_HYPOTHESIS, PR_LEMMA, PR_UNIT_RESOLUTION, PR_IFF_TRUE, PR_IFF_FALSE, PR_COMMUTATIVITY, PR_DEF_AXIOM, PR_ASSUMPTION_ADD, PR_TH_ASSUMPTION_ADD, PR_LEMMA_ADD, PR_TH_LEMMA_ADD, PR_REDUNDANT_DEL, PR_CLAUSE_TRAIL, PR_DEF_INTRO, PR_APPLY_DEF, PR_IFF_OEQ, PR_NNF_POS, PR_NNF_NEG, PR_SKOLEMIZE, PR_MODUS_PONENS_OEQ, PR_TH_LEMMA, PR_HYPER_RESOLVE, LAST_BASIC_PR }; class basic_decl_plugin : public decl_plugin { protected: sort * m_bool_sort; func_decl * m_true_decl; func_decl * m_false_decl; func_decl * m_and_decl; func_decl * m_or_decl; func_decl * m_xor_decl; func_decl * m_not_decl; func_decl * m_implies_decl; ptr_vector m_eq_decls; // cached eqs ptr_vector m_ite_decls; // cached ites ptr_vector m_oeq_decls; // cached observational eqs sort * m_proof_sort; func_decl * m_undef_decl; func_decl * m_true_pr_decl; func_decl * m_asserted_decl; func_decl * m_goal_decl; func_decl * m_modus_ponens_decl; func_decl * m_reflexivity_decl; func_decl * m_symmetry_decl; func_decl * m_transitivity_decl; func_decl * m_quant_intro_decl; func_decl * m_and_elim_decl; func_decl * m_not_or_elim_decl; func_decl * m_rewrite_decl; func_decl * m_pull_quant_decl; func_decl * m_push_quant_decl; func_decl * m_elim_unused_vars_decl; func_decl * m_der_decl; func_decl * m_quant_inst_decl; ptr_vector m_monotonicity_decls; ptr_vector m_transitivity_star_decls; ptr_vector m_distributivity_decls; ptr_vector m_assoc_flat_decls; ptr_vector m_rewrite_star_decls; func_decl * m_hypothesis_decl; func_decl * m_iff_true_decl; func_decl * m_iff_false_decl; func_decl * m_commutativity_decl; func_decl * m_def_axiom_decl; func_decl * m_lemma_decl; ptr_vector m_unit_resolution_decls; func_decl * m_def_intro_decl; func_decl * m_iff_oeq_decl; func_decl * m_skolemize_decl; func_decl * m_mp_oeq_decl; func_decl * m_assumption_add_decl; func_decl * m_lemma_add_decl; func_decl * m_th_assumption_add_decl; func_decl * m_th_lemma_add_decl; func_decl * m_redundant_del_decl; func_decl * m_clause_trail_decl; ptr_vector m_apply_def_decls; ptr_vector m_nnf_pos_decls; ptr_vector m_nnf_neg_decls; ptr_vector m_th_lemma_decls; func_decl * m_hyper_res_decl0; static bool is_proof(decl_kind k) { return k > LAST_BASIC_OP; } bool check_proof_sorts(basic_op_kind k, unsigned arity, sort * const * domain) const; bool check_proof_args(basic_op_kind k, unsigned num_args, expr * const * args) const; func_decl * mk_bool_op_decl(char const * name, basic_op_kind k, unsigned num_args = 0, bool asooc = false, bool comm = false, bool idempotent = false, bool flat_associative = false, bool chainable = false); func_decl * mk_implies_decl(); func_decl * mk_proof_decl(char const * name, basic_op_kind k, unsigned num_parents); func_decl * mk_proof_decl(char const * name, basic_op_kind k, unsigned num_parents, func_decl*& fn); func_decl * mk_proof_decl(char const * name, basic_op_kind k, unsigned num_parents, ptr_vector & cache); func_decl * mk_compressed_proof_decl(char const * name, basic_op_kind k, unsigned num_parents); func_decl * mk_proof_decl(basic_op_kind k, unsigned num_parents); func_decl * mk_proof_decl(basic_op_kind k, unsigned num_parameters, parameter const* params, unsigned num_parents); func_decl * mk_proof_decl( char const * name, basic_op_kind k, unsigned num_parameters, parameter const* params, unsigned num_parents); void set_manager(ast_manager * m, family_id id) override; func_decl * mk_eq_decl_core(char const * name, decl_kind k, sort * s, ptr_vector & cache); func_decl * mk_ite_decl(sort * s); sort* join(sort* s1, sort* s2); sort* join(unsigned n, sort*const* srts); sort* join(unsigned n, expr*const* es); public: basic_decl_plugin(); ~basic_decl_plugin() override {} void finalize() override; decl_plugin * mk_fresh() override { return alloc(basic_decl_plugin); } sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const* parameters) override; func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) override; func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned num_args, expr * const * args, sort * range) override; void get_op_names(svector & op_names, symbol const & logic) override; void get_sort_names(svector & sort_names, symbol const & logic) override; bool is_value(app* a) const override; bool is_unique_value(app* a) const override; sort * mk_bool_sort() const { return m_bool_sort; } sort * mk_proof_sort() const { return m_proof_sort; } expr * get_some_value(sort * s) override; }; typedef app proof; /* a proof is just an application */ // ----------------------------------- // // label_decl_plugin // // ----------------------------------- enum label_op_kind { OP_LABEL, OP_LABEL_LIT }; /** \brief Labels are identity functions used to mark sub-expressions. */ class label_decl_plugin : public decl_plugin { symbol m_lblpos; symbol m_lblneg; symbol m_lbllit; void set_manager(ast_manager * m, family_id id) override; public: label_decl_plugin(); ~label_decl_plugin() override; decl_plugin * mk_fresh() override { return alloc(label_decl_plugin); } sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) override; /** contract: when label parameter[0] (int): 0 - if the label is negative, 1 - if positive. parameter[1] (symbol): label's tag. ... parameter[n-1] (symbol): label's tag. contract: when label literal (they are always positive) parameter[0] (symbol): label's tag ... parameter[n-1] (symbol): label's tag. */ func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) override; }; // ----------------------------------- // // pattern_decl_plugin // // ----------------------------------- enum pattern_op_kind { OP_PATTERN }; /** \brief Patterns are used to group expressions. These expressions are using during E-matching for heuristic quantifier instantiation. */ class pattern_decl_plugin : public decl_plugin { public: decl_plugin * mk_fresh() override { return alloc(pattern_decl_plugin); } sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) override; func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) override; }; // ----------------------------------- // // model_value_plugin // // ----------------------------------- enum model_value_op_kind { OP_MODEL_VALUE }; /** \brief Values are used during model construction. All values are assumed to be different. Users should not use them, since they may introduce unsoundness if the sort of a value is finite. Moreover, values should never be internalized in a logical context. However, values can be used during evaluation (i.e., simplification). \remark Model values can be viewed as the partition ids in Z3 1.x. */ class model_value_decl_plugin : public decl_plugin { public: model_value_decl_plugin() {} decl_plugin * mk_fresh() override { return alloc(model_value_decl_plugin); } sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) override; /** contract: parameter[0]: (integer) value idx parameter[1]: (ast) sort of the value. */ func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) override; bool is_value(app* n) const override; bool is_unique_value(app* a) const override; }; // ----------------------------------- // // user_sort_plugin for supporting user declared sorts in SMT2 // // ----------------------------------- class user_sort_plugin : public decl_plugin { svector m_sort_names; dictionary m_name2decl_kind; public: user_sort_plugin() {} sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) override; func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) override; decl_kind register_name(symbol s); decl_plugin * mk_fresh() override; }; // ----------------------------------- // // Auxiliary functions // // ----------------------------------- // Return true if n is an application of d. inline bool is_app_of(expr const * n, func_decl const * d) { return n->get_kind() == AST_APP && to_app(n)->get_decl() == d; } inline bool is_app_of(expr const * n, family_id fid, decl_kind k) { return n->get_kind() == AST_APP && to_app(n)->is_app_of(fid, k); } inline bool is_sort_of(sort const * s, family_id fid, decl_kind k) { return s->is_sort_of(fid, k); } inline bool is_uninterp_const(expr const * n) { return n->get_kind() == AST_APP && to_app(n)->get_num_args() == 0 && to_app(n)->get_family_id() == null_family_id; } inline bool is_uninterp(expr const * n) { return n->get_kind() == AST_APP && to_app(n)->get_family_id() == null_family_id; } inline bool is_decl_of(func_decl const * d, family_id fid, decl_kind k) { return d->get_family_id() == fid && d->get_decl_kind() == k; } inline bool is_ground(expr const * n) { return is_app(n) && to_app(n)->is_ground(); } inline bool is_non_ground(expr const * n) { return ( ! is_ground(n)); } inline unsigned get_depth(expr const * n) { if (is_app(n)) return to_app(n)->get_depth(); else if (is_quantifier(n)) return to_quantifier(n)->get_depth(); else return 1; } inline bool has_quantifiers(expr const * n) { return is_app(n) ? to_app(n)->has_quantifiers() : is_quantifier(n); } inline bool has_labels(expr const * n) { if (is_app(n)) return to_app(n)->has_labels(); else if (is_quantifier(n)) return to_quantifier(n)->has_labels(); else return false; } sort * get_sort(expr const * n); class basic_recognizers { family_id m_fid; public: basic_recognizers(family_id fid):m_fid(fid) {} bool is_bool(sort const * s) const { return is_sort_of(s, m_fid, BOOL_SORT); } bool is_bool(expr const * n) const { return is_bool(get_sort(n)); } bool is_or(expr const * n) const { return is_app_of(n, m_fid, OP_OR); } bool is_implies(expr const * n) const { return is_app_of(n, m_fid, OP_IMPLIES); } bool is_and(expr const * n) const { return is_app_of(n, m_fid, OP_AND); } bool is_not(expr const * n) const { return is_app_of(n, m_fid, OP_NOT); } bool is_eq(expr const * n) const { return is_app_of(n, m_fid, OP_EQ); } bool is_iff(expr const* n) const { return is_eq(n) && is_bool(to_app(n)->get_arg(0)); } bool is_oeq(expr const * n) const { return is_app_of(n, m_fid, OP_OEQ); } bool is_distinct(expr const * n) const { return is_app_of(n, m_fid, OP_DISTINCT); } bool is_xor(expr const * n) const { return is_app_of(n, m_fid, OP_XOR); } bool is_ite(expr const * n) const { return is_app_of(n, m_fid, OP_ITE); } bool is_term_ite(expr const * n) const { return is_ite(n) && !is_bool(n); } bool is_true(expr const * n) const { return is_app_of(n, m_fid, OP_TRUE); } bool is_false(expr const * n) const { return is_app_of(n, m_fid, OP_FALSE); } bool is_complement_core(expr const * n1, expr const * n2) const { return (is_true(n1) && is_false(n2)) || (is_not(n1) && to_app(n1)->get_arg(0) == n2); } bool is_complement(expr const * n1, expr const * n2) const { return is_complement_core(n1, n2) || is_complement_core(n2, n1); } bool is_or(func_decl const * d) const { return is_decl_of(d, m_fid, OP_OR); } bool is_implies(func_decl const * d) const { return is_decl_of(d, m_fid, OP_IMPLIES); } bool is_and(func_decl const * d) const { return is_decl_of(d, m_fid, OP_AND); } bool is_not(func_decl const * d) const { return is_decl_of(d, m_fid, OP_NOT); } bool is_eq(func_decl const * d) const { return is_decl_of(d, m_fid, OP_EQ); } bool is_xor(func_decl const * d) const { return is_decl_of(d, m_fid, OP_XOR); } bool is_ite(func_decl const * d) const { return is_decl_of(d, m_fid, OP_ITE); } bool is_term_ite(func_decl const * d) const { return is_ite(d) && !is_bool(d->get_range()); } bool is_distinct(func_decl const * d) const { return is_decl_of(d, m_fid, OP_DISTINCT); } MATCH_UNARY(is_not); MATCH_BINARY(is_eq); MATCH_BINARY(is_implies); MATCH_BINARY(is_and); MATCH_BINARY(is_or); MATCH_BINARY(is_xor); MATCH_TERNARY(is_and); MATCH_TERNARY(is_or); bool is_iff(expr const* n, expr*& lhs, expr*& rhs) const { return is_eq(n, lhs, rhs) && is_bool(lhs); } bool is_ite(expr const * n, expr * & t1, expr * & t2, expr * & t3) const; }; // ----------------------------------- // // Get Some Value functor // // Functor for returning some value // of the given sort. // // ----------------------------------- class some_value_proc { public: virtual expr * operator()(sort * s) = 0; }; // ----------------------------------- // // Proof generation mode // // ----------------------------------- enum proof_gen_mode { PGM_DISABLED, PGM_ENABLED }; // ----------------------------------- // // ast_manager // // ----------------------------------- class ast_manager { friend class basic_decl_plugin; protected: struct config { typedef ast_manager value_manager; typedef small_object_allocator allocator; static const bool ref_count = true; }; struct array_config : public config { static const bool preserve_roots = true; static const unsigned max_trail_sz = 16; static const unsigned factor = 2; }; struct expr_array_config : public array_config { typedef expr * value; }; typedef parray_manager expr_array_manager; struct expr_dependency_config : public config { typedef expr * value; }; typedef dependency_manager expr_dependency_manager; public: typedef expr_array_manager::ref expr_array; typedef expr_dependency_manager::dependency expr_dependency; protected: struct expr_dependency_array_config : public array_config { typedef expr_dependency * value; }; typedef parray_manager expr_dependency_array_manager; public: typedef expr_dependency_array_manager::ref expr_dependency_array; void show_id_gen(); void update_fresh_id(ast_manager const& other); protected: reslimit m_limit; small_object_allocator m_alloc; family_manager m_family_manager; expr_array_manager m_expr_array_manager; expr_dependency_manager m_expr_dependency_manager; expr_dependency_array_manager m_expr_dependency_array_manager; ptr_vector m_plugins; proof_gen_mode m_proof_mode; bool m_int_real_coercions; // If true, use hack that automatically introduces to_int/to_real when needed. family_id m_basic_family_id; family_id m_label_family_id; family_id m_pattern_family_id; family_id m_model_value_family_id; family_id m_user_sort_family_id; family_id m_arith_family_id; ast_table m_ast_table; obj_map m_lambda_defs; id_gen m_expr_id_gen; id_gen m_decl_id_gen; sort * m_bool_sort; sort * m_proof_sort; app * m_true; app * m_false; proof * m_undef_proof; unsigned m_fresh_id; bool m_debug_ref_count; u_map m_debug_free_indices; std::fstream* m_trace_stream; bool m_trace_stream_owner; #ifdef Z3DEBUG bool slow_not_contains(ast const * n); #endif ast_manager * m_format_manager; // hack for isolating format objects in a different manager. symbol m_rec_fun; symbol m_lambda_def; void init(); bool coercion_needed(func_decl * decl, unsigned num_args, expr * const * args); void check_args(func_decl* f, unsigned n, expr* const* es); public: ast_manager(proof_gen_mode = PGM_DISABLED, char const * trace_file = nullptr, bool is_format_manager = false); ast_manager(proof_gen_mode, std::fstream * trace_stream, bool is_format_manager = false); ast_manager(ast_manager const & src, bool disable_proofs = false); ~ast_manager(); // propagate cancellation signal to decl_plugins bool has_trace_stream() const { return m_trace_stream != nullptr; } std::ostream & trace_stream() { SASSERT(has_trace_stream()); return *m_trace_stream; } void enable_int_real_coercions(bool f) { m_int_real_coercions = f; } bool int_real_coercions() const { return m_int_real_coercions; } // Return true if s1 and s2 are equal, or coercions are enabled, and s1 and s2 are compatible. bool compatible_sorts(sort * s1, sort * s2) const; // For debugging purposes void display_free_ids(std::ostream & out) { m_expr_id_gen.display_free_ids(out); out << "\n"; m_decl_id_gen.display_free_ids(out); } void compact_memory(); void compress_ids(); // Equivalent to throw ast_exception(msg) Z3_NORETURN void raise_exception(char const * msg); Z3_NORETURN void raise_exception(std::string && s); std::ostream& display(std::ostream& out, parameter const& p); bool is_format_manager() const { return m_format_manager == nullptr; } ast_manager & get_format_manager() { return is_format_manager() ? *this : *m_format_manager; } void copy_families_plugins(ast_manager const & from); small_object_allocator & get_allocator() { return m_alloc; } family_id mk_family_id(symbol const & s) { return m_family_manager.mk_family_id(s); } family_id mk_family_id(char const * s) { return mk_family_id(symbol(s)); } family_id get_family_id(symbol const & s) const { return m_family_manager.get_family_id(s); } family_id get_family_id(char const * s) const { return get_family_id(symbol(s)); } symbol const & get_family_name(family_id fid) const { return m_family_manager.get_name(fid); } bool is_builtin_family_id(family_id fid) const { return fid == null_family_id || fid == m_basic_family_id || fid == m_label_family_id || fid == m_pattern_family_id || fid == m_model_value_family_id || fid == m_user_sort_family_id; } reslimit& limit() { return m_limit; } bool canceled() { return !limit().inc(); } void register_plugin(symbol const & s, decl_plugin * plugin); void register_plugin(family_id id, decl_plugin * plugin); decl_plugin * get_plugin(family_id fid) const; bool has_plugin(family_id fid) const { return get_plugin(fid) != nullptr; } bool has_plugin(symbol const & s) const { return m_family_manager.has_family(s) && has_plugin(m_family_manager.get_family_id(s)); } void get_dom(svector & dom) const { m_family_manager.get_dom(dom); } void get_range(svector & range) const { m_family_manager.get_range(range); } family_id get_basic_family_id() const { return m_basic_family_id; } basic_decl_plugin * get_basic_decl_plugin() const { return static_cast(get_plugin(m_basic_family_id)); } family_id get_user_sort_family_id() const { return m_user_sort_family_id; } user_sort_plugin * get_user_sort_plugin() const { return static_cast(get_plugin(m_user_sort_family_id)); } /** \brief Debugging support method: set the next expression identifier to be the least value id' s.t. - id' >= id - id' is not used by any AST in m_table - id' is not in the expression m_free_ids This method should be only used to create small repros that exposes bugs in Z3. */ void set_next_expr_id(unsigned id); bool is_value(expr * e) const; bool is_unique_value(expr * e) const; bool are_equal(expr * a, expr * b) const; bool are_distinct(expr * a, expr * b) const; bool contains(ast * a) const { return m_ast_table.contains(a); } bool is_rec_fun_def(quantifier* q) const { return q->get_qid() == m_rec_fun; } bool is_lambda_def(quantifier* q) const { return q->get_qid() == m_lambda_def; } void add_lambda_def(func_decl* f, quantifier* q); quantifier* is_lambda_def(func_decl* f); func_decl* get_rec_fun_decl(quantifier* q) const; symbol const& rec_fun_qid() const { return m_rec_fun; } symbol const& lambda_def_qid() const { return m_lambda_def; } unsigned get_num_asts() const { return m_ast_table.size(); } void debug_ref_count() { m_debug_ref_count = true; } void inc_ref(ast * n) { if (n) { n->inc_ref(); } } void dec_ref(ast* n) { if (n) { n->dec_ref(); if (n->get_ref_count() == 0) delete_node(n); } } template void inc_array_ref(unsigned sz, T * const * a) { for(unsigned i = 0; i < sz; i++) { inc_ref(a[i]); } } template void dec_array_ref(unsigned sz, T * const * a) { for(unsigned i = 0; i < sz; i++) { dec_ref(a[i]); } } static unsigned get_node_size(ast const * n); size_t get_allocation_size() const { return m_alloc.get_allocation_size(); } protected: ast * register_node_core(ast * n); template T * register_node(T * n) { return static_cast(register_node_core(n)); } void delete_node(ast * n); void * allocate_node(unsigned size) { return m_alloc.allocate(size); } void deallocate_node(ast * n, unsigned sz) { m_alloc.deallocate(sz, n); } public: sort * get_sort(expr const * n) const { return ::get_sort(n); } void check_sort(func_decl const * decl, unsigned num_args, expr * const * args) const; void check_sorts_core(ast const * n) const; bool check_sorts(ast const * n) const; bool is_bool(expr const * n) const; bool is_bool(sort const * s) const { return s == m_bool_sort; } decl_kind get_eq_op(expr const * n) const { return OP_EQ; } private: sort * mk_sort(symbol const & name, sort_info * info); public: sort * mk_uninterpreted_sort(symbol const & name, unsigned num_parameters, parameter const * parameters); sort * mk_uninterpreted_sort(symbol const & name) { return mk_uninterpreted_sort(name, 0, nullptr); } sort * mk_sort(symbol const & name, sort_info const & info) { if (info.get_family_id() == null_family_id) { return mk_uninterpreted_sort(name); } else { return mk_sort(name, &const_cast(info)); } } sort * mk_sort(family_id fid, decl_kind k, unsigned num_parameters = 0, parameter const * parameters = nullptr); sort * substitute(sort* s, unsigned n, sort * const * src, sort * const * dst); sort * mk_bool_sort() const { return m_bool_sort; } sort * mk_proof_sort() const { return m_proof_sort; } sort * mk_fresh_sort(char const * prefix = ""); bool is_uninterp(sort const * s) const { return s->get_family_id() == null_family_id || s->get_family_id() == m_user_sort_family_id; } /** \brief A sort is "fully" interpreted if it is interpreted, and doesn't depend on other uninterpreted sorts. */ bool is_fully_interp(sort * s) const; func_decl * mk_func_decl(family_id fid, decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range = nullptr); func_decl * mk_func_decl(family_id fid, decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned num_args, expr * const * args, sort * range = nullptr); app * mk_app(family_id fid, decl_kind k, unsigned num_parameters = 0, parameter const * parameters = nullptr, unsigned num_args = 0, expr * const * args = nullptr, sort * range = nullptr); app * mk_app(family_id fid, decl_kind k, unsigned num_args, expr * const * args); app * mk_app(family_id fid, decl_kind k, expr * arg); app * mk_app(family_id fid, decl_kind k, expr * arg1, expr * arg2); app * mk_app(family_id fid, decl_kind k, expr * arg1, expr * arg2, expr * arg3); app * mk_const(family_id fid, decl_kind k) { return mk_app(fid, k, 0, static_cast(nullptr)); } private: func_decl * mk_func_decl(symbol const & name, unsigned arity, sort * const * domain, sort * range, func_decl_info * info); app * mk_app_core(func_decl * decl, expr * arg1, expr * arg2); app * mk_app_core(func_decl * decl, unsigned num_args, expr * const * args); public: func_decl * mk_func_decl(symbol const & name, unsigned arity, sort * const * domain, sort * range) { return mk_func_decl(name, arity, domain, range, static_cast(nullptr)); } func_decl * mk_func_decl(symbol const & name, unsigned arity, sort * const * domain, sort * range, func_decl_info const & info) { if (info.is_null()) { return mk_func_decl(name, arity, domain, range, static_cast(nullptr)); } else { return mk_func_decl(name, arity, domain, range, & const_cast(info)); } } func_decl * mk_func_decl(unsigned arity, sort * const * domain, func_decl_info const & info) { return mk_func_decl(info.get_family_id(), info.get_decl_kind(), info.get_num_parameters(), info.get_parameters(), arity, domain); } func_decl * mk_skolem_const_decl(symbol const& name, sort* s) { func_decl_info info; info.set_skolem(true); return mk_func_decl(name, static_cast(0), nullptr, s, info); } func_decl * mk_const_decl(const char* name, sort * s) { return mk_func_decl(symbol(name), static_cast(0), nullptr, s); } func_decl * mk_const_decl(std::string const& name, sort * s) { return mk_func_decl(symbol(name.c_str()), static_cast(0), nullptr, s); } func_decl * mk_const_decl(symbol const & name, sort * s) { return mk_func_decl(name, static_cast(0), nullptr, s); } func_decl * mk_const_decl(symbol const & name, sort * s, func_decl_info const & info) { return mk_func_decl(name, static_cast(0), nullptr, s, info); } func_decl * mk_func_decl(symbol const & name, sort * domain, sort * range, func_decl_info const & info) { return mk_func_decl(name, 1, &domain, range, info); } func_decl * mk_func_decl(symbol const & name, sort * domain, sort * range) { return mk_func_decl(name, 1, &domain, range); } func_decl * mk_func_decl(symbol const & name, sort * domain1, sort * domain2, sort * range, func_decl_info const & info) { sort * d[2] = { domain1, domain2 }; return mk_func_decl(name, 2, d, range, info); } func_decl * mk_func_decl(symbol const & name, sort * domain1, sort * domain2, sort * range) { sort * d[2] = { domain1, domain2 }; return mk_func_decl(name, 2, d, range); } func_decl * mk_func_decl(symbol const & name, unsigned arity, sort * const * domain, sort * range, bool assoc, bool comm = false, bool inj = false); func_decl * mk_func_decl(symbol const & name, sort * domain1, sort * domain2, sort * range, bool assoc, bool comm = false) { sort * d[2] = { domain1, domain2 }; return mk_func_decl(name, 2, d, range, assoc, comm, false); } bool is_considered_uninterpreted(func_decl* f) { if (f->get_family_id() == null_family_id) { return true; } decl_plugin* p = get_plugin(f->get_family_id()); return !p || p->is_considered_uninterpreted(f); } app * mk_app(func_decl * decl, unsigned num_args, expr * const * args); app * mk_app(func_decl * decl, expr * const * args) { return mk_app(decl, decl->get_arity(), args); } app * mk_app(func_decl * decl, expr * arg) { SASSERT(decl->get_arity() == 1); return mk_app(decl, 1, &arg); } app * mk_app(func_decl * decl, expr * arg1, expr * arg2) { SASSERT(decl->get_arity() == 2); expr * args[2] = { arg1, arg2 }; return mk_app(decl, 2, args); } app * mk_app(func_decl * decl, expr * arg1, expr * arg2, expr * arg3) { SASSERT(decl->get_arity() == 3); expr * args[3] = { arg1, arg2, arg3 }; return mk_app(decl, 3, args); } app * mk_const(func_decl * decl) { SASSERT(decl->get_arity() == 0); return mk_app(decl, static_cast(0), static_cast(nullptr)); } app * mk_skolem_const(symbol const & name, sort * s) { return mk_const(mk_skolem_const_decl(name, s)); } app * mk_const(symbol const & name, sort * s) { return mk_const(mk_const_decl(name, s)); } app * mk_const(std::string const & name, sort * s) { return mk_const(mk_const_decl(name, s)); } app * mk_const(char const* name, sort * s) { return mk_const(mk_const_decl(name, s)); } func_decl * mk_fresh_func_decl(symbol const & prefix, symbol const & suffix, unsigned arity, sort * const * domain, sort * range, bool skolem = true); func_decl * mk_fresh_func_decl(unsigned arity, sort * const * domain, sort * range, bool skolem = true) { return mk_fresh_func_decl(symbol::null, symbol::null, arity, domain, range, skolem); } func_decl * mk_fresh_func_decl(char const * prefix, char const * suffix, unsigned arity, sort * const * domain, sort * range, bool skolem = true) { return mk_fresh_func_decl(symbol(prefix), symbol(suffix), arity, domain, range, skolem); } func_decl * mk_fresh_func_decl(char const * prefix, unsigned arity, sort * const * domain, sort * range, bool skolem = true) { return mk_fresh_func_decl(symbol(prefix), symbol::null, arity, domain, range, skolem); } app * mk_fresh_const(char const * prefix, sort * s, bool skolem = true) { return mk_const(mk_fresh_func_decl(prefix, 0, nullptr, s, skolem)); } app * mk_fresh_const(std::string const& prefix, sort * s, bool skolem = true) { return mk_fresh_const(prefix.c_str(), s, skolem); } app * mk_fresh_const(symbol const& prefix, sort * s, bool skolem = true) { return mk_fresh_const(prefix.str().c_str(), s, skolem); } symbol mk_fresh_var_name(char const * prefix = nullptr); var * mk_var(unsigned idx, sort * ty); app * mk_label(bool pos, unsigned num_names, symbol const * names, expr * n); app * mk_label(bool pos, symbol const & name, expr * n); bool is_label(expr const * n, bool & pos, buffer & names) const; bool is_label(expr const * n, bool & pos, buffer & names, expr*& l) const { return is_label(n, pos, names)?(l = to_app(n)->get_arg(0), true):false; } bool is_label(expr const * n) const { return is_app_of(n, m_label_family_id, OP_LABEL); } bool is_label(expr const * n, expr*& l) const { return is_label(n)?(l = to_app(n)->get_arg(0), true):false; } bool is_label(expr const * n, bool& pos) const { if (is_app_of(n, m_label_family_id, OP_LABEL)) { pos = to_app(n)->get_decl()->get_parameter(0).get_int() != 0; return true; } else { return false; } } app * mk_label_lit(unsigned num_names, symbol const * names); app * mk_label_lit(symbol const & name); bool is_label_lit(expr const * n, buffer & names) const; bool is_label_lit(expr const * n) const { return is_app_of(n, m_label_family_id, OP_LABEL_LIT); } family_id get_label_family_id() const { return m_label_family_id; } app * mk_pattern(unsigned num_exprs, app * const * exprs); app * mk_pattern(app * expr) { return mk_pattern(1, &expr); } bool is_pattern(expr const * n) const; bool is_pattern(expr const *n, ptr_vector &args); public: quantifier * mk_quantifier(quantifier_kind k, unsigned num_decls, sort * const * decl_sorts, symbol const * decl_names, expr * body, int weight = 0, symbol const & qid = symbol::null, symbol const & skid = symbol::null, unsigned num_patterns = 0, expr * const * patterns = nullptr, unsigned num_no_patterns = 0, expr * const * no_patterns = nullptr); quantifier * mk_forall(unsigned num_decls, sort * const * decl_sorts, symbol const * decl_names, expr * body, int weight = 0, symbol const & qid = symbol::null, symbol const & skid = symbol::null, unsigned num_patterns = 0, expr * const * patterns = nullptr, unsigned num_no_patterns = 0, expr * const * no_patterns = nullptr) { return mk_quantifier(forall_k, num_decls, decl_sorts, decl_names, body, weight, qid, skid, num_patterns, patterns, num_no_patterns, no_patterns); } quantifier * mk_exists(unsigned num_decls, sort * const * decl_sorts, symbol const * decl_names, expr * body, int weight = 0, symbol const & qid = symbol::null, symbol const & skid = symbol::null, unsigned num_patterns = 0, expr * const * patterns = nullptr, unsigned num_no_patterns = 0, expr * const * no_patterns = nullptr) { return mk_quantifier(exists_k, num_decls, decl_sorts, decl_names, body, weight, qid, skid, num_patterns, patterns, num_no_patterns, no_patterns); } quantifier * mk_lambda(unsigned num_decls, sort * const * decl_sorts, symbol const * decl_names, expr * body); quantifier * update_quantifier(quantifier * q, unsigned new_num_patterns, expr * const * new_patterns, expr * new_body); quantifier * update_quantifier(quantifier * q, unsigned new_num_patterns, expr * const * new_patterns, unsigned new_num_no_patterns, expr * const * new_no_patterns, expr * new_body); quantifier * update_quantifier(quantifier * q, expr * new_body); quantifier * update_quantifier_weight(quantifier * q, int new_weight); quantifier * update_quantifier(quantifier * q, quantifier_kind new_kind, expr * new_body); quantifier * update_quantifier(quantifier * q, quantifier_kind new_kind, unsigned new_num_patterns, expr * const * new_patterns, expr * new_body); // ----------------------------------- // // expr_array // // ----------------------------------- public: void mk(expr_array & r) { m_expr_array_manager.mk(r); } void del(expr_array & r) { m_expr_array_manager.del(r); } void copy(expr_array const & s, expr_array & r) { m_expr_array_manager.copy(s, r); } unsigned size(expr_array const & r) const { return m_expr_array_manager.size(r); } bool empty(expr_array const & r) const { return m_expr_array_manager.empty(r); } expr * get(expr_array const & r, unsigned i) const { return m_expr_array_manager.get(r, i); } void set(expr_array & r, unsigned i, expr * v) { m_expr_array_manager.set(r, i, v); } void set(expr_array const & s, unsigned i, expr * v, expr_array & r) { m_expr_array_manager.set(s, i, v, r); } void push_back(expr_array & r, expr * v) { m_expr_array_manager.push_back(r, v); } void push_back(expr_array const & s, expr * v, expr_array & r) { m_expr_array_manager.push_back(s, v, r); } void pop_back(expr_array & r) { m_expr_array_manager.pop_back(r); } void pop_back(expr_array const & s, expr_array & r) { m_expr_array_manager.pop_back(s, r); } void unshare(expr_array & r) { m_expr_array_manager.unshare(r); } void unfold(expr_array & r) { m_expr_array_manager.unfold(r); } void reroot(expr_array & r) { m_expr_array_manager.reroot(r); } // ----------------------------------- // // expr_dependency // // ----------------------------------- public: expr_dependency * mk_empty_dependencies() { return m_expr_dependency_manager.mk_empty(); } expr_dependency * mk_leaf(expr * t); expr_dependency * mk_join(unsigned n, expr * const * ts); expr_dependency * mk_join(expr_dependency * d1, expr_dependency * d2) { return m_expr_dependency_manager.mk_join(d1, d2); } void inc_ref(expr_dependency * d) { if (d) m_expr_dependency_manager.inc_ref(d); } void dec_ref(expr_dependency * d) { if (d) m_expr_dependency_manager.dec_ref(d); } void linearize(expr_dependency * d, ptr_vector & ts); bool contains(expr_dependency * d, expr * t) { return m_expr_dependency_manager.contains(d, t); } // ----------------------------------- // // expr_dependency_array // // ----------------------------------- public: void mk(expr_dependency_array & r) { m_expr_dependency_array_manager.mk(r); } void del(expr_dependency_array & r) { m_expr_dependency_array_manager.del(r); } void copy(expr_dependency_array const & s, expr_dependency_array & r) { m_expr_dependency_array_manager.copy(s, r); } unsigned size(expr_dependency_array const & r) const { return m_expr_dependency_array_manager.size(r); } bool empty(expr_dependency_array const & r) const { return m_expr_dependency_array_manager.empty(r); } expr_dependency * get(expr_dependency_array const & r, unsigned i) const { return m_expr_dependency_array_manager.get(r, i); } void set(expr_dependency_array & r, unsigned i, expr_dependency * v) { m_expr_dependency_array_manager.set(r, i, v); } void set(expr_dependency_array const & s, unsigned i, expr_dependency * v, expr_dependency_array & r) { m_expr_dependency_array_manager.set(s, i, v, r); } void push_back(expr_dependency_array & r, expr_dependency * v) { m_expr_dependency_array_manager.push_back(r, v); } void push_back(expr_dependency_array const & s, expr_dependency * v, expr_dependency_array & r) { m_expr_dependency_array_manager.push_back(s, v, r); } void pop_back(expr_dependency_array & r) { m_expr_dependency_array_manager.pop_back(r); } void pop_back(expr_dependency_array const & s, expr_dependency_array & r) { m_expr_dependency_array_manager.pop_back(s, r); } void unshare(expr_dependency_array & r) { m_expr_dependency_array_manager.unshare(r); } void unfold(expr_dependency_array & r) { m_expr_dependency_array_manager.unfold(r); } void reroot(expr_dependency_array & r) { m_expr_dependency_array_manager.reroot(r); } // ----------------------------------- // // Builtin operators // // ----------------------------------- public: bool is_or(expr const * n) const { return is_app_of(n, m_basic_family_id, OP_OR); } bool is_implies(expr const * n) const { return is_app_of(n, m_basic_family_id, OP_IMPLIES); } bool is_and(expr const * n) const { return is_app_of(n, m_basic_family_id, OP_AND); } bool is_not(expr const * n) const { return is_app_of(n, m_basic_family_id, OP_NOT); } bool is_eq(expr const * n) const { return is_app_of(n, m_basic_family_id, OP_EQ); } bool is_iff(expr const * n) const { return is_eq(n) && is_bool(to_app(n)->get_arg(0)); } bool is_oeq(expr const * n) const { return is_app_of(n, m_basic_family_id, OP_OEQ); } bool is_distinct(expr const * n) const { return is_app_of(n, m_basic_family_id, OP_DISTINCT); } bool is_xor(expr const * n) const { return is_app_of(n, m_basic_family_id, OP_XOR); } bool is_ite(expr const * n) const { return is_app_of(n, m_basic_family_id, OP_ITE); } bool is_term_ite(expr const * n) const { return is_ite(n) && !is_bool(n); } bool is_true(expr const * n) const { return n == m_true; } bool is_false(expr const * n) const { return n == m_false; } bool is_complement_core(expr const * n1, expr const * n2) const { return (is_true(n1) && is_false(n2)) || (is_not(n1) && to_app(n1)->get_arg(0) == n2); } bool is_complement(expr const * n1, expr const * n2) const { return is_complement_core(n1, n2) || is_complement_core(n2, n1); } bool is_or(func_decl const * d) const { return is_decl_of(d, m_basic_family_id, OP_OR); } bool is_implies(func_decl const * d) const { return is_decl_of(d, m_basic_family_id, OP_IMPLIES); } bool is_and(func_decl const * d) const { return is_decl_of(d, m_basic_family_id, OP_AND); } bool is_not(func_decl const * d) const { return is_decl_of(d, m_basic_family_id, OP_NOT); } bool is_eq(func_decl const * d) const { return is_decl_of(d, m_basic_family_id, OP_EQ); } bool is_iff(func_decl const * d) const { return is_decl_of(d, m_basic_family_id, OP_EQ) && is_bool(d->get_range()); } bool is_xor(func_decl const * d) const { return is_decl_of(d, m_basic_family_id, OP_XOR); } bool is_ite(func_decl const * d) const { return is_decl_of(d, m_basic_family_id, OP_ITE); } bool is_term_ite(func_decl const * d) const { return is_ite(d) && !is_bool(d->get_range()); } bool is_distinct(func_decl const * d) const { return is_decl_of(d, m_basic_family_id, OP_DISTINCT); } public: MATCH_UNARY(is_not); MATCH_BINARY(is_eq); MATCH_BINARY(is_implies); MATCH_BINARY(is_and); MATCH_BINARY(is_or); MATCH_BINARY(is_xor); MATCH_TERNARY(is_and); MATCH_TERNARY(is_or); bool is_iff(expr const* n, expr*& lhs, expr*& rhs) const { return is_eq(n, lhs, rhs) && is_bool(lhs); } bool is_ite(expr const* n, expr*& t1, expr*& t2, expr*& t3) const { if (is_ite(n)) { t1 = to_app(n)->get_arg(0); t2 = to_app(n)->get_arg(1); t3 = to_app(n)->get_arg(2); return true; } return false; } public: app * mk_eq(expr * lhs, expr * rhs) { return mk_app(m_basic_family_id, get_eq_op(lhs), lhs, rhs); } app * mk_iff(expr * lhs, expr * rhs) { return mk_app(m_basic_family_id, OP_EQ, lhs, rhs); } app * mk_oeq(expr * lhs, expr * rhs) { return mk_app(m_basic_family_id, OP_OEQ, lhs, rhs); } app * mk_xor(expr * lhs, expr * rhs) { return mk_app(m_basic_family_id, OP_XOR, lhs, rhs); } app * mk_ite(expr * c, expr * t, expr * e) { return mk_app(m_basic_family_id, OP_ITE, c, t, e); } app * mk_xor(unsigned num_args, expr * const * args) { return mk_app(m_basic_family_id, OP_XOR, num_args, args); } app * mk_or(unsigned num_args, expr * const * args) { return mk_app(m_basic_family_id, OP_OR, num_args, args); } app * mk_and(unsigned num_args, expr * const * args) { return mk_app(m_basic_family_id, OP_AND, num_args, args); } app * mk_or(expr * arg1, expr * arg2) { return mk_app(m_basic_family_id, OP_OR, arg1, arg2); } app * mk_and(expr * arg1, expr * arg2) { return mk_app(m_basic_family_id, OP_AND, arg1, arg2); } app * mk_or(expr * arg1, expr * arg2, expr * arg3) { return mk_app(m_basic_family_id, OP_OR, arg1, arg2, arg3); } app * mk_or(expr* a, expr* b, expr* c, expr* d) { expr* args[4] = { a, b, c, d }; return mk_app(m_basic_family_id, OP_OR, 4, args); } app * mk_and(expr * arg1, expr * arg2, expr * arg3) { return mk_app(m_basic_family_id, OP_AND, arg1, arg2, arg3); } app * mk_implies(expr * arg1, expr * arg2) { return mk_app(m_basic_family_id, OP_IMPLIES, arg1, arg2); } app * mk_not(expr * n) { return mk_app(m_basic_family_id, OP_NOT, n); } app * mk_distinct(unsigned num_args, expr * const * args); app * mk_distinct_expanded(unsigned num_args, expr * const * args); app * mk_true() const { return m_true; } app * mk_false() const { return m_false; } app * mk_bool_val(bool b) { return b?m_true:m_false; } func_decl* mk_and_decl() { sort* domain[2] = { m_bool_sort, m_bool_sort }; return mk_func_decl(m_basic_family_id, OP_AND, 0, nullptr, 2, domain); } func_decl* mk_not_decl() { return mk_func_decl(m_basic_family_id, OP_NOT, 0, nullptr, 1, &m_bool_sort); } func_decl* mk_or_decl() { sort* domain[2] = { m_bool_sort, m_bool_sort }; return mk_func_decl(m_basic_family_id, OP_OR, 0, nullptr, 2, domain); } // ----------------------------------- // // Values // // ----------------------------------- protected: some_value_proc * m_some_value_proc; public: app * mk_model_value(unsigned idx, sort * s); bool is_model_value(expr const * n) const { return is_app_of(n, m_model_value_family_id, OP_MODEL_VALUE); } bool is_model_value(func_decl const * d) const { return is_decl_of(d, m_model_value_family_id, OP_MODEL_VALUE); } expr * get_some_value(sort * s, some_value_proc * p); expr * get_some_value(sort * s); // ----------------------------------- // // Proof generation // // ----------------------------------- protected: proof * mk_proof(family_id fid, decl_kind k, unsigned num_args, expr * const * args); proof * mk_proof(family_id fid, decl_kind k, expr * arg); proof * mk_proof(family_id fid, decl_kind k, expr * arg1, expr * arg2); proof * mk_proof(family_id fid, decl_kind k, expr * arg1, expr * arg2, expr * arg3); proof * mk_undef_proof() const { return m_undef_proof; } public: bool proofs_enabled() const { return m_proof_mode != PGM_DISABLED; } bool proofs_disabled() const { return m_proof_mode == PGM_DISABLED; } proof_gen_mode proof_mode() const { return m_proof_mode; } void toggle_proof_mode(proof_gen_mode m) { m_proof_mode = m; } // APIs for creating proof objects return [undef] bool is_proof(expr const * n) const { return is_app(n) && to_app(n)->get_decl()->get_range() == m_proof_sort; } proof* mk_hyper_resolve(unsigned num_premises, proof* const* premises, expr* concl, svector > const& positions, vector > const& substs); bool is_undef_proof(expr const * e) const { return e == m_undef_proof; } bool is_asserted(expr const * e) const { return is_app_of(e, m_basic_family_id, PR_ASSERTED); } bool is_hypothesis (expr const *e) const {return is_app_of (e, m_basic_family_id, PR_HYPOTHESIS);} bool is_goal(expr const * e) const { return is_app_of(e, m_basic_family_id, PR_GOAL); } bool is_modus_ponens(expr const * e) const { return is_app_of(e, m_basic_family_id, PR_MODUS_PONENS); } bool is_reflexivity(expr const * e) const { return is_app_of(e, m_basic_family_id, PR_REFLEXIVITY); } bool is_symmetry(expr const * e) const { return is_app_of(e, m_basic_family_id, PR_SYMMETRY); } bool is_transitivity(expr const * e) const { return is_app_of(e, m_basic_family_id, PR_TRANSITIVITY); } bool is_monotonicity(expr const * e) const { return is_app_of(e, m_basic_family_id, PR_MONOTONICITY); } bool is_quant_intro(expr const * e) const { return is_app_of(e, m_basic_family_id, PR_QUANT_INTRO); } bool is_quant_inst(expr const * e) const { return is_app_of(e, m_basic_family_id, PR_QUANT_INST); } bool is_distributivity(expr const * e) const { return is_app_of(e, m_basic_family_id, PR_DISTRIBUTIVITY); } bool is_and_elim(expr const * e) const { return is_app_of(e, m_basic_family_id, PR_AND_ELIM); } bool is_not_or_elim(expr const * e) const { return is_app_of(e, m_basic_family_id, PR_NOT_OR_ELIM); } bool is_rewrite(expr const * e) const { return is_app_of(e, m_basic_family_id, PR_REWRITE); } bool is_rewrite_star(expr const * e) const { return is_app_of(e, m_basic_family_id, PR_REWRITE_STAR); } bool is_unit_resolution(expr const * e) const { return is_app_of(e, m_basic_family_id, PR_UNIT_RESOLUTION); } bool is_lemma(expr const * e) const { return is_app_of(e, m_basic_family_id, PR_LEMMA); } bool is_quant_inst(expr const* e, expr*& not_q_or_i, ptr_vector& binding) const; bool is_rewrite(expr const* e, expr*& r1, expr*& r2) const; bool is_hyper_resolve(proof* p) const { return is_app_of(p, m_basic_family_id, PR_HYPER_RESOLVE); } bool is_hyper_resolve(proof* p, ref_vector& premises, obj_ref& conclusion, svector > & positions, vector >& substs); bool is_def_intro(expr const * e) const { return is_app_of(e, m_basic_family_id, PR_DEF_INTRO); } bool is_apply_def(expr const * e) const { return is_app_of(e, m_basic_family_id, PR_APPLY_DEF); } bool is_skolemize(expr const * e) const { return is_app_of(e, m_basic_family_id, PR_SKOLEMIZE); } MATCH_UNARY(is_asserted); MATCH_UNARY(is_hypothesis); MATCH_UNARY(is_lemma); bool has_fact(proof const * p) const { SASSERT(is_proof(p)); unsigned n = p->get_num_args(); return n > 0 && get_sort(p->get_arg(n - 1)) != m_proof_sort; } expr * get_fact(proof const * p) const { SASSERT(is_proof(p)); SASSERT(has_fact(p)); return p->get_arg(p->get_num_args() - 1); } class proof_parents { ast_manager& m; proof * m_proof; public: proof_parents(ast_manager& m, proof * p): m(m), m_proof(p) {} proof * const * begin() const { return (proof* const*)(m_proof->begin()); } proof * const * end() const { unsigned n = m_proof->get_num_args(); return (proof* const*)(begin() + (m.has_fact(m_proof) ? n - 1 : n)); } }; proof_parents get_parents(proof* p) { return proof_parents(*this, p); } unsigned get_num_parents(proof const * p) const { SASSERT(is_proof(p)); unsigned n = p->get_num_args(); return !has_fact(p) ? n : n - 1; } proof * get_parent(proof const * p, unsigned idx) const { SASSERT(is_proof(p)); return to_app(p->get_arg(idx)); } proof * mk_true_proof(); proof * mk_asserted(expr * f); proof * mk_goal(expr * f); proof * mk_modus_ponens(proof * p1, proof * p2); proof * mk_reflexivity(expr * e); proof * mk_oeq_reflexivity(expr * e); proof * mk_symmetry(proof * p); proof * mk_transitivity(proof * p1, proof * p2); proof * mk_transitivity(proof * p1, proof * p2, proof * p3); proof * mk_transitivity(proof * p1, proof * p2, proof * p3, proof * p4); proof * mk_transitivity(unsigned num_proofs, proof * const * proofs); proof * mk_transitivity(unsigned num_proofs, proof * const * proofs, expr * n1, expr * n2); proof * mk_monotonicity(func_decl * R, app * f1, app * f2, unsigned num_proofs, proof * const * proofs); proof * mk_congruence(app * f1, app * f2, unsigned num_proofs, proof * const * proofs); proof * mk_oeq_congruence(app * f1, app * f2, unsigned num_proofs, proof * const * proofs); proof * mk_commutativity(app * f); proof * mk_iff_true(proof * pr); proof * mk_iff_false(proof * pr); proof * mk_quant_intro(quantifier * q1, quantifier * q2, proof * p); proof * mk_oeq_quant_intro(quantifier * q1, quantifier * q2, proof * p); proof * mk_distributivity(expr * s, expr * r); proof * mk_rewrite(expr * s, expr * t); proof * mk_oeq_rewrite(expr * s, expr * t); proof * mk_rewrite_star(expr * s, expr * t, unsigned num_proofs, proof * const * proofs); proof * mk_bind_proof(quantifier * q, proof * p); proof * mk_pull_quant(expr * e, quantifier * q); proof * mk_push_quant(quantifier * q, expr * e); proof * mk_elim_unused_vars(quantifier * q, expr * r); proof * mk_der(quantifier * q, expr * r); proof * mk_quant_inst(expr * not_q_or_i, unsigned num_bind, expr* const* binding); proof * mk_clause_trail_elem(proof* p, expr* e, decl_kind k); proof * mk_assumption_add(proof* pr, expr* e); proof * mk_lemma_add(proof* pr, expr* e); proof * mk_th_assumption_add(proof* pr, expr* e); proof * mk_th_lemma_add(proof* pr, expr* e); proof * mk_redundant_del(expr* e); proof * mk_clause_trail(unsigned n, proof* const* ps); proof * mk_def_axiom(expr * ax); proof * mk_unit_resolution(unsigned num_proofs, proof * const * proofs); proof * mk_unit_resolution(unsigned num_proofs, proof * const * proofs, expr * new_fact); proof * mk_hypothesis(expr * h); proof * mk_lemma(proof * p, expr * lemma); proof * mk_def_intro(expr * new_def); proof * mk_apply_defs(expr * n, expr * def, unsigned num_proofs, proof * const * proofs); proof * mk_apply_def(expr * n, expr * def, proof * p) { return mk_apply_defs(n, def, 1, &p); } proof * mk_iff_oeq(proof * parent); proof * mk_nnf_pos(expr * s, expr * t, unsigned num_proofs, proof * const * proofs); proof * mk_nnf_neg(expr * s, expr * t, unsigned num_proofs, proof * const * proofs); proof * mk_skolemization(expr * q, expr * e); proof * mk_and_elim(proof * p, unsigned i); proof * mk_not_or_elim(proof * p, unsigned i); proof * mk_th_lemma(family_id tid, expr * fact, unsigned num_proofs, proof * const * proofs, unsigned num_params = 0, parameter const* params = nullptr); protected: bool check_nnf_proof_parents(unsigned num_proofs, proof * const * proofs) const; private: void push_dec_ref(ast * n) { n->dec_ref(); if (n->get_ref_count() == 0) { m_ast_table.push_erase(n); } } template void push_dec_array_ref(unsigned sz, T * const * a) { for(unsigned i = 0; i < sz; i++) { push_dec_ref(a[i]); } } }; typedef ast_manager::expr_array expr_array; typedef ast_manager::expr_dependency expr_dependency; typedef ast_manager::expr_dependency_array expr_dependency_array; typedef obj_ref expr_dependency_ref; typedef ref_vector expr_dependency_ref_vector; typedef ref_buffer expr_dependency_ref_buffer; // ----------------------------------- // // More Auxiliary Functions // // ----------------------------------- inline bool is_predicate(ast_manager const & m, func_decl const * d) { return m.is_bool(d->get_range()); } struct ast_lt_proc { bool operator()(ast const * n1, ast const * n2) const { return n1->get_id() < n2->get_id(); } }; // ----------------------------------- // // ast_ref (smart pointer) // // ----------------------------------- typedef obj_ref ast_ref; typedef obj_ref expr_ref; typedef obj_ref sort_ref; typedef obj_ref func_decl_ref; typedef obj_ref quantifier_ref; typedef obj_ref app_ref; typedef obj_ref var_ref; typedef app_ref proof_ref; // ----------------------------------- // // ast_vector (smart pointer vector) // // ----------------------------------- typedef ref_vector ast_ref_vector; typedef ref_vector decl_ref_vector; typedef ref_vector sort_ref_vector; typedef ref_vector func_decl_ref_vector; typedef ref_vector expr_ref_vector; typedef ref_vector app_ref_vector; typedef ref_vector var_ref_vector; typedef ref_vector quantifier_ref_vector; typedef app_ref_vector proof_ref_vector; // ----------------------------------- // // ast_buffer // // ----------------------------------- typedef ref_buffer ast_ref_buffer; typedef ref_buffer expr_ref_buffer; typedef ref_buffer sort_ref_buffer; typedef ref_buffer app_ref_buffer; typedef app_ref_buffer proof_ref_buffer; // ----------------------------------- // // expr_mark // // ----------------------------------- typedef obj_mark expr_mark; class expr_sparse_mark { obj_hashtable m_marked; public: expr_sparse_mark() {} bool is_marked(expr * n) const { return m_marked.contains(n); } void mark(expr * n) { m_marked.insert(n); } void mark(expr * n, bool flag) { if (flag) m_marked.insert(n); else m_marked.erase(n); } void reset() { m_marked.reset(); } }; template class ast_fast_mark { ptr_buffer m_to_unmark; public: ast_fast_mark() {} ~ast_fast_mark() { reset(); } bool is_marked(ast * n) { return IDX == 1 ? AST_IS_MARKED1(n, this) : AST_IS_MARKED2(n, this); } void reset_mark(ast * n) { if (IDX == 1) { AST_RESET_MARK1(n, this); } else { AST_RESET_MARK2(n, this); } } void mark(ast * n) { if (IDX == 1) { if (AST_IS_MARKED1(n, this)) return; AST_MARK1(n, true, this); } else { if (AST_IS_MARKED2(n, this)) return; AST_MARK2(n, true, this); } m_to_unmark.push_back(n); } void reset() { for (ast* a : m_to_unmark) reset_mark(a); m_to_unmark.reset(); } void mark(ast * n, bool flag) { if (flag) mark(n); else reset_mark(n); } unsigned get_level() { return m_to_unmark.size(); } void set_level(unsigned new_size) { SASSERT(new_size <= m_to_unmark.size()); while (new_size < m_to_unmark.size()) { reset_mark(m_to_unmark.back()); m_to_unmark.pop_back(); } } }; typedef ast_fast_mark<1> ast_fast_mark1; typedef ast_fast_mark<2> ast_fast_mark2; typedef ast_fast_mark1 expr_fast_mark1; typedef ast_fast_mark2 expr_fast_mark2; /** Similar to ast_fast_mark, but increases reference counter. */ template class ast_ref_fast_mark { ast_ref_buffer m_to_unmark; public: ast_ref_fast_mark(ast_manager & m):m_to_unmark(m) {} ~ast_ref_fast_mark() { reset(); } bool is_marked(ast * n) { return IDX == 1 ? AST_IS_MARKED1(n, this) : AST_IS_MARKED2(n, this); } // It will not decrease the reference counter void reset_mark(ast * n) { if (IDX == 1) { AST_RESET_MARK1(n, this); } else { AST_RESET_MARK2(n, this); } } void mark(ast * n) { if (IDX == 1) { if (AST_IS_MARKED1(n, this)) return; AST_MARK1(n, true, this); } else { if (AST_IS_MARKED2(n, this)) return; AST_MARK2(n, true, this); } m_to_unmark.push_back(n); } void reset() { ast * const * it = m_to_unmark.c_ptr(); ast * const * end = it + m_to_unmark.size(); for (; it != end; ++it) { reset_mark(*it); } m_to_unmark.reset(); } void mark(ast * n, bool flag) { if (flag) mark(n); else reset_mark(n); } }; typedef ast_ref_fast_mark<1> ast_ref_fast_mark1; typedef ast_ref_fast_mark<2> ast_ref_fast_mark2; typedef ast_ref_fast_mark1 expr_ref_fast_mark1; typedef ast_ref_fast_mark2 expr_ref_fast_mark2; // ----------------------------------- // // ast_mark // // ----------------------------------- /** \brief A mapping from AST to Boolean \warning This map does not cleanup the entry associated with a node N, when N is deleted. */ class ast_mark { struct decl2uint { unsigned operator()(decl const & d) const { return d.get_decl_id(); } }; obj_mark m_expr_marks; obj_mark m_decl_marks; public: virtual ~ast_mark() {} bool is_marked(ast * n) const; virtual void mark(ast * n, bool flag); virtual void reset(); }; // ----------------------------------- // // scoped_mark // // ----------------------------------- /** \brief Class for scoped-based marking of asts. This class is safe with respect to life-times of asts. */ class scoped_mark : public ast_mark { ast_ref_vector m_stack; unsigned_vector m_lim; public: scoped_mark(ast_manager& m): m_stack(m) {} ~scoped_mark() override {} void mark(ast * n, bool flag) override; void reset() override; void mark(ast * n); void push_scope(); void pop_scope(); void pop_scope(unsigned num_scopes); }; // ------------------------------------- // // inc_ref & dec_ref functors // // ------------------------------------- template class dec_ref_proc { ast_manager & m_manager; public: dec_ref_proc(ast_manager & m):m_manager(m) {} void operator()(AST * n) { m_manager.dec_ref(n); } }; template class inc_ref_proc { ast_manager & m_manager; public: inc_ref_proc(ast_manager & m):m_manager(m) {} void operator()(AST * n) { m_manager.inc_ref(n); } }; struct parameter_pp { parameter const& p; ast_manager& m; parameter_pp(parameter const& p, ast_manager& m): p(p), m(m) {} }; inline std::ostream& operator<<(std::ostream& out, parameter_pp const& pp) { return pp.m.display(out, pp.p); } #endif /* AST_H_ */ z3-z3-4.8.7/src/ast/ast_ll_pp.cpp000066400000000000000000000217531356505360400164760ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: ast_ll_pp.cpp Abstract: AST low level pretty printer. Author: Leonardo de Moura (leonardo) 2006-10-19. Revision History: --*/ #include #include "ast/for_each_ast.h" #include "ast/arith_decl_plugin.h" // #define AST_LL_PP_SHOW_FAMILY_NAME class ll_printer { std::ostream & m_out; ast_manager & m_manager; ast * m_root; bool m_only_exprs; bool m_compact; arith_util m_autil; void display_def_header(ast * n) { if (n != m_root) { m_out << "#" << n->get_id() << " := "; } } void display_child_ref(ast * n) { m_out << "#" << n->get_id(); } void display_name(func_decl * decl) { symbol n = decl->get_name(); if (decl->is_skolem() && n.is_numerical()) m_out << "z3.sk." << n.get_num(); else m_out << n; } bool process_numeral(expr * n) { rational val; bool is_int; if (m_autil.is_numeral(n, val, is_int)) { m_out << val << "::" << (is_int ? "Int" : "Real"); return true; } return false; } void display_sort(sort * s) { m_out << s->get_name(); display_params(s); } void display_child(ast * n) { switch (n->get_kind()) { case AST_SORT: display_sort(to_sort(n)); break; case AST_APP: if (process_numeral(to_expr(n))) { // skip } else if (to_app(n)->get_num_args() == 0) { display_name(to_app(n)->get_decl()); display_params(to_app(n)->get_decl()); } else { display_child_ref(n); } break; default: display_child_ref(n); } } template void display_children(unsigned num_children, T * const * children) { for (unsigned i = 0; i < num_children; i++) { if (i > 0) { m_out << " "; } display_child(children[i]); } } void display_params(decl * d) { unsigned n = d->get_num_parameters(); parameter const* p = d->get_parameters(); if (n > 0 && !d->private_parameters()) { m_out << "["; for (unsigned i = 0; i < n; i ++) { if (p[i].is_ast()) { display_child(p[i].get_ast()); } else { m_out << p[i]; } m_out << (i < n-1 ? ":" : ""); } m_out << "]"; } } public: ll_printer(std::ostream & out, ast_manager & m, ast * n, bool only_exprs, bool compact): m_out(out), m_manager(m), m_root(n), m_only_exprs(only_exprs), m_compact(compact), m_autil(m) { } void pp(ast* n) { ast_mark visited; pp(n, visited); } void pp(ast* n, ast_mark& visited) { if (is_sort(n)) { display_sort(to_sort(n)); } else { for_each_ast(*this, visited, n, true); } } void operator()(sort* n) { } void operator()(func_decl * n) { if (m_only_exprs) { return; } if (n->get_family_id() != null_family_id) { return; } m_out << "decl "; display_name(n); m_out << " :: "; if (n->get_arity() == 0) { display_child(n->get_range()); } else { m_out << "(-> "; display_children(n->get_arity(), n->get_domain()); m_out << " "; display_child(n->get_range()); m_out << ")"; display_params(n); if (n->is_associative()) { m_out << " :assoc"; } if (n->is_commutative()) { m_out << " :comm"; } if (n->is_injective()) { m_out << " :inj"; } } m_out << "\n"; } void operator()(var * n) { display_def_header(n); m_out << "(:var " << to_var(n)->get_idx() << " "; display_sort(n->get_sort()); m_out << ")\n"; } void operator()(app * n) { if (m_autil.is_numeral(n)) { if (!m_compact) display_def_header(n); if (n == m_root || !m_compact) { process_numeral(n); m_out << "\n"; } } else if (m_manager.is_proof(n)) { display_def_header(n); m_out << "[" << n->get_decl()->get_name(); unsigned num_params = n->get_decl()->get_num_parameters(); for (unsigned i = 0; i < num_params; ++i) { m_out << " "; m_out << n->get_decl()->get_parameter(i); } unsigned num_parents = m_manager.get_num_parents(n); for (unsigned i = 0; i < num_parents; i++) { m_out << " "; display_child(m_manager.get_parent(n, i)); } m_out << "]: "; if (m_manager.has_fact(n)) { // display(m_manager.get_fact(n), 6); display_child(m_manager.get_fact(n)); } else m_out << "*"; m_out << "\n"; } else if (m_compact && n->get_num_args() == 0) { if (n == m_root) { display_child(n); m_out << "\n"; } } else { display_def_header(n); if (n->get_num_args() > 0) m_out << "("; display_name(n->get_decl()); display_params(n->get_decl()); if (n->get_num_args() > 0) { m_out << " "; display_children(n->get_num_args(), n->get_args()); m_out << ")"; } #ifdef AST_LL_PP_SHOW_FAMILY_NAME if (to_app(n)->get_family_id() != null_family_id) { m_out << " family: " << m_manager.get_family_name(to_app(n)->get_family_id()); } #endif m_out << "\n"; } } void operator()(quantifier * n) { display_def_header(n); m_out << "(" << (n->get_kind() == forall_k ? "forall" : (n->get_kind() == exists_k ? "exists" : "lambda")) << " "; unsigned num_decls = n->get_num_decls(); m_out << "(vars "; for (unsigned i = 0; i < num_decls; i++) { if (i > 0) { m_out << " "; } m_out << "(" << n->get_decl_name(i) << " "; display_sort(n->get_decl_sort(i)); m_out << ")"; } m_out << ") "; if (n->get_num_patterns() > 0) { m_out << "(:pat "; display_children(n->get_num_patterns(), n->get_patterns()); m_out << ") "; } if (n->get_num_no_patterns() > 0) { m_out << "(:nopat "; display_children(n->get_num_no_patterns(), n->get_no_patterns()); m_out << ") "; } display_child(n->get_expr()); m_out << ")\n"; } void display(expr * n, unsigned depth) { if (is_var(n)) { m_out << "(:var " << to_var(n)->get_idx() << ")"; return; } if (!is_app(n) || depth == 0 || to_app(n)->get_num_args() == 0) { display_child(n); return; } if (to_app(n)->get_num_args() > depth && to_app(n)->get_num_args() > 16) { display_child(n); return; } unsigned num_args = to_app(n)->get_num_args(); if (num_args > 0) m_out << "("; display_name(to_app(n)->get_decl()); display_params(to_app(n)->get_decl()); for (unsigned i = 0; i < num_args; i++) { m_out << " "; display(to_app(n)->get_arg(i), depth-1); } if (num_args > 0) m_out << ")"; } void display_bounded(ast * n, unsigned depth) { if (is_app(n)) { display(to_expr(n), depth); } else if (is_var(n)) { m_out << "(:var " << to_var(n)->get_idx() << ")"; } else { m_out << "#" << n->get_id(); } } }; void ast_ll_pp(std::ostream & out, ast_manager & m, ast * n, bool only_exprs, bool compact) { ll_printer p(out, m, n, only_exprs, compact); p.pp(n); } void ast_ll_pp(std::ostream & out, ast_manager & m, ast * n, ast_mark & visited, bool only_exprs, bool compact) { ll_printer p(out, m, n, only_exprs, compact); p.pp(n, visited); } void ast_def_ll_pp(std::ostream & out, ast_manager & m, ast * n, ast_mark & visited, bool only_exprs, bool compact) { ll_printer p(out, m, nullptr, only_exprs, compact); p.pp(n, visited); } void ast_ll_bounded_pp(std::ostream & out, ast_manager & m, ast * n, unsigned depth) { ll_printer p(out, m, nullptr, false, true); p.display_bounded(n, depth); } z3-z3-4.8.7/src/ast/ast_ll_pp.h000066400000000000000000000030571356505360400161400ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: ast_ll_pp.h Abstract: AST low level pretty printer. Author: Leonardo de Moura (leonardo) 2006-10-19. Revision History: --*/ #ifndef AST_LL_PP_H_ #define AST_LL_PP_H_ #include "ast/ast.h" #include void ast_ll_pp(std::ostream & out, ast_manager & m, ast * n, bool only_exprs=true, bool compact=true); void ast_ll_pp(std::ostream & out, ast_manager & m, ast * n, ast_mark & visited, bool only_exprs=true, bool compact=true); void ast_def_ll_pp(std::ostream & out, ast_manager & m, ast * n, ast_mark & visited, bool only_exprs=true, bool compact=true); void ast_ll_bounded_pp(std::ostream & out, ast_manager & m, ast * n, unsigned depth); struct mk_ll_pp { ast * m_ast; ast_manager & m_manager; bool m_only_exprs; bool m_compact; mk_ll_pp(ast * a, ast_manager & m, bool only_exprs=true, bool compact=true): m_ast(a), m_manager(m), m_only_exprs(only_exprs), m_compact(compact) {} }; inline std::ostream & operator<<(std::ostream & out, mk_ll_pp const & p) { ast_ll_pp(out, p.m_manager, p.m_ast, p.m_only_exprs, p.m_compact); return out; } struct mk_bounded_pp { ast * m_ast; ast_manager & m_manager; unsigned m_depth; mk_bounded_pp(ast * a, ast_manager & m, unsigned depth=3): m_ast(a), m_manager(m), m_depth(depth) {} }; inline std::ostream & operator<<(std::ostream & out, mk_bounded_pp const & p) { ast_ll_bounded_pp(out, p.m_manager, p.m_ast, p.m_depth); return out; } #endif /* AST_LL_PP_H_ */ z3-z3-4.8.7/src/ast/ast_lt.cpp000066400000000000000000000143121356505360400160000ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: ast_lt.cpp Abstract: Total order on ASTs that does not depend on the internal ids. Author: Leonardo de Moura (leonardo) 2011-04-08 Revision History: --*/ #include "ast/ast.h" #define check_symbol(S1,S2) if (S1 != S2) return lt(S1,S2) #define check_value(V1,V2) if (V1 != V2) return V1 < V2 #define check_bool(B1,B2) if (B1 != B2) return !B1 && B2 #define check_ptr(P1,P2) if (!P1 && P2) return true; if (P1 && !P2) return false #define check_ast(T1,T2) if (T1 != T2) { n1 = T1; n2 = T2; goto start; } #define check_parameter(p1, p2) { \ check_value(p1.get_kind(), p2.get_kind()); \ switch (p1.get_kind()) { \ case parameter::PARAM_INT: \ check_value(p1.get_int(), p2.get_int()); \ break; \ case parameter::PARAM_AST: \ check_ast(p1.get_ast(), p2.get_ast()); \ break; \ case parameter::PARAM_SYMBOL: \ check_symbol(p1.get_symbol(), p2.get_symbol()); \ break; \ case parameter::PARAM_RATIONAL: \ check_value(p1.get_rational(), p2.get_rational()); \ break; \ case parameter::PARAM_DOUBLE: \ check_value(p1.get_double(), p2.get_double()); \ break; \ case parameter::PARAM_EXTERNAL: \ check_value(p1.get_ext_id(), p2.get_ext_id()); \ break; \ default: \ UNREACHABLE(); \ break; \ } \ } bool lt(ast * n1, ast * n2) { unsigned num; start: if (n1 == n2) return false; check_value(n1->get_kind(), n2->get_kind()); switch(n1->get_kind()) { case AST_SORT: check_symbol(to_sort(n1)->get_name(), to_sort(n2)->get_name()); check_value(to_sort(n1)->get_num_parameters(), to_sort(n2)->get_num_parameters()); num = to_sort(n1)->get_num_parameters(); SASSERT(num > 0); for (unsigned i = 0; i < num; i++) { parameter p1 = to_sort(n1)->get_parameter(i); parameter p2 = to_sort(n2)->get_parameter(i); check_parameter(p1, p2); } UNREACHABLE(); return false; case AST_FUNC_DECL: check_symbol(to_func_decl(n1)->get_name(), to_func_decl(n2)->get_name()); check_value(to_func_decl(n1)->get_arity(), to_func_decl(n2)->get_arity()); check_value(to_func_decl(n1)->get_num_parameters(), to_func_decl(n2)->get_num_parameters()); num = to_func_decl(n1)->get_num_parameters(); for (unsigned i = 0; i < num; i++) { parameter p1 = to_func_decl(n1)->get_parameter(i); parameter p2 = to_func_decl(n2)->get_parameter(i); check_parameter(p1, p2); } num = to_func_decl(n1)->get_arity(); for (unsigned i = 0; i < num; i++) { ast * d1 = to_func_decl(n1)->get_domain(i); ast * d2 = to_func_decl(n2)->get_domain(i); check_ast(d1, d2); } n1 = to_func_decl(n1)->get_range(); n2 = to_func_decl(n2)->get_range(); goto start; case AST_APP: check_value(to_app(n1)->get_num_args(), to_app(n2)->get_num_args()); check_value(to_app(n1)->get_depth(), to_app(n2)->get_depth()); check_ast(to_app(n1)->get_decl(), to_app(n2)->get_decl()); num = to_app(n1)->get_num_args(); for (unsigned i = 0; i < num; i++) { expr * arg1 = to_app(n1)->get_arg(i); expr * arg2 = to_app(n2)->get_arg(i); check_ast(arg1, arg2); } UNREACHABLE(); return false; case AST_QUANTIFIER: check_value(to_quantifier(n1)->get_kind(), to_quantifier(n2)->get_kind()); check_value(to_quantifier(n1)->get_num_decls(), to_quantifier(n2)->get_num_decls()); check_value(to_quantifier(n1)->get_num_patterns(), to_quantifier(n2)->get_num_patterns()); check_value(to_quantifier(n1)->get_num_no_patterns(), to_quantifier(n2)->get_num_no_patterns()); check_value(to_quantifier(n1)->get_weight(), to_quantifier(n2)->get_weight()); num = to_quantifier(n1)->get_num_decls(); for (unsigned i = 0; i < num; i++) { check_symbol(to_quantifier(n1)->get_decl_name(i), to_quantifier(n2)->get_decl_name(i)); check_ast(to_quantifier(n1)->get_decl_sort(i), to_quantifier(n2)->get_decl_sort(i)); } num = to_quantifier(n1)->get_num_patterns(); for (unsigned i = 0; i < num; i++) { check_ast(to_quantifier(n1)->get_pattern(i), to_quantifier(n2)->get_pattern(i)); } num = to_quantifier(n1)->get_num_no_patterns(); for (unsigned i = 0; i < num; i++) { check_ast(to_quantifier(n1)->get_no_pattern(i), to_quantifier(n2)->get_no_pattern(i)); } n1 = to_quantifier(n1)->get_expr(); n2 = to_quantifier(n2)->get_expr(); goto start; case AST_VAR: check_value(to_var(n1)->get_idx(), to_var(n2)->get_idx()); n1 = to_var(n1)->get_sort(); n2 = to_var(n2)->get_sort(); goto start; default: UNREACHABLE(); return false; } } bool is_sorted(unsigned num, expr * const * ns) { for (unsigned i = 1; i < num; i++) { ast * prev = ns[i-1]; ast * curr = ns[i]; if (lt(curr, prev)) return false; } return true; } bool lex_lt(unsigned num, ast * const * n1, ast * const * n2) { for (unsigned i = 0; i < num; i ++) { if (n1[i] == n2[i]) continue; return lt(n1[i], n2[i]); } return false; } z3-z3-4.8.7/src/ast/ast_lt.h000066400000000000000000000014331356505360400154450ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: ast_lt.h Abstract: Total order on ASTs that does not depend on the internal ids. Author: Leonardo de Moura (leonardo) 2011-04-08 Revision History: --*/ #ifndef AST_LT_H_ #define AST_LT_H_ class ast; bool lt(ast * n1, ast * n2); bool is_sorted(unsigned num, expr * const * ns); struct ast_to_lt { bool operator()(ast * n1, ast * n2) const { return lt(n1, n2); } }; struct ast_lt { bool operator()(ast * n1, ast * n2) const { return n1->get_id() < n2->get_id(); } }; bool lex_lt(unsigned num, ast * const * n1, ast * const * n2); inline bool lex_lt(unsigned num, expr * const * n1, expr * const * n2) { return lex_lt(num, reinterpret_cast(n1), reinterpret_cast(n2)); } #endif z3-z3-4.8.7/src/ast/ast_pp.h000066400000000000000000000024451356505360400154510ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: ast_pp.h Abstract: Pretty printer Author: Leonardo de Moura 2008-01-20. Revision History: 2012-11-17 - ast_smt2_pp is the official pretty printer in Z3 --*/ #ifndef AST_PP_H_ #define AST_PP_H_ #include "ast/ast_smt2_pp.h" struct mk_pp : public mk_ismt2_pp { mk_pp(ast * t, ast_manager & m, params_ref const & p, unsigned indent = 0, unsigned num_vars = 0, char const * var_prefix = nullptr): mk_ismt2_pp(t, m, p, indent, num_vars, var_prefix) { } mk_pp(ast * t, ast_manager & m, unsigned indent = 0, unsigned num_vars = 0, char const * var_prefix = nullptr): mk_ismt2_pp(t, m, indent, num_vars, var_prefix) { } }; // expr2id; // temporary structure for traversing the proof and printing it struct ast_pp_dot_st { ast_manager & m_manager; std::ostream & m_out; const ast_pp_dot * m_pp; unsigned m_next_id; expr2id m_id_map; obj_hashtable m_printed; svector m_to_print; bool m_first; ast_pp_dot_st(const ast_pp_dot * pp, std::ostream & out) : m_manager(pp->get_manager()), m_out(out), m_pp(pp), m_next_id(0), m_id_map(), m_printed(), m_to_print(), m_first(true) {} ~ast_pp_dot_st() {}; void push_term(const expr * a) { m_to_print.push_back(a); } void pp_loop() { // DFS traversal while (!m_to_print.empty()) { const expr * a = m_to_print.back(); m_to_print.pop_back(); if (!m_printed.contains(a)) { m_printed.insert(a); if (m().is_proof(a)) pp_step(to_app(a)); else pp_atomic_step(a); } } } private: inline ast_manager & m() const { return m_manager; } // label for an expression std::string label_of_expr(const expr * e) const { expr_ref er((expr*)e, m()); std::ostringstream out; out << er << std::flush; return escape_dot(out.str()); } void pp_atomic_step(const expr * e) { unsigned id = get_id(e); m_out << "node_" << id << " [shape=box,color=\"yellow\",style=\"filled\",label=\"" << label_of_expr(e) << "\"] ;" << std::endl; } void pp_step(const proof * p) { TRACE("pp_ast_dot_step", tout << " :kind " << p->get_kind() << " :num-args " << p->get_num_args() << "\n";); if (m().has_fact(p)) { // print result expr* p_res = m().get_fact(p); // result of proof step unsigned id = get_id(p); unsigned num_parents = m().get_num_parents(p); const char* color = m_first ? (m_first=false,"color=\"red\"") : num_parents==0 ? "color=\"yellow\"": ""; m_out << "node_" << id << " [shape=box,style=\"filled\",label=\"" << label_of_expr(p_res) << "\"" << color << "]" << std::endl; // now print edges to parents (except last one, which is the result) std::string label = p->get_decl()->get_name().str(); for (unsigned i = 0 ; i < num_parents; ++i) { expr* parent = m().get_parent(p, i); // explore parent, also print a link to it push_term(to_app(parent)); m_out << "node_" << id << " -> " << "node_" << get_id((expr*)parent) << "[label=\"" << label << "\"];" << std::endl;; } } else { pp_atomic_step(p); } } // find a unique ID for this proof unsigned get_id(const expr * e) { unsigned id = 0; if (!m_id_map.find(e, id)) { id = m_next_id++; m_id_map.insert(e, id); } return id; } }; // main printer std::ostream & ast_pp_dot::pp(std::ostream & out) const { out << "digraph proof { " << std::endl; ast_pp_dot_st pp_st(this, out); pp_st.push_term(m_pr); pp_st.pp_loop(); out << std::endl << " } " << std::endl << std::flush; return out; } std::ostream &operator<<(std::ostream &out, const ast_pp_dot & p) { return p.pp(out); } z3-z3-4.8.7/src/ast/ast_pp_dot.h000066400000000000000000000012051356505360400163100ustar00rootroot00000000000000/*++ Abstract: Pretty-printer for proofs in Graphviz format --*/ #ifndef _AST_PP_DOT_ #define _AST_PP_DOT_ #include #include "ast/ast_pp.h" class ast_pp_dot { ast_manager & m_manager; proof * const m_pr; public: ast_pp_dot(proof *pr, ast_manager &m) : m_manager(m), m_pr(pr) {} ast_pp_dot(proof_ref &e) : m_manager(e.m()), m_pr(e.get()) {} std::ostream & pp(std::ostream & out) const; ast_manager & get_manager() const { return m_manager; } }; std::string escape_dot(std::string const & s); std::ostream &operator<<(std::ostream &out, const ast_pp_dot & p); #endif /* AST_PP_DOT */ z3-z3-4.8.7/src/ast/ast_pp_util.cpp000066400000000000000000000064361356505360400170450ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: ast_pp_util.cpp Abstract: Author: Nikolaj Bjorner (nbjorner) 2015-8-6. Revision History: --*/ #include "ast/ast_pp_util.h" #include "ast/ast_smt2_pp.h" #include "ast/ast_smt_pp.h" #include "ast/recfun_decl_plugin.h" void ast_pp_util::collect(expr* e) { coll.visit(e); } void ast_pp_util::collect(unsigned n, expr* const* es) { for (unsigned i = 0; i < n; ++i) { coll.visit(es[i]); } } void ast_pp_util::collect(expr_ref_vector const& es) { collect(es.size(), es.c_ptr()); } void ast_pp_util::display_decls(std::ostream& out) { ast_smt_pp pp(m); bool first = m_num_decls == 0; coll.order_deps(m_num_sorts); unsigned n = coll.get_num_sorts(); for (unsigned i = m_num_sorts; i < n; ++i) { pp.display_ast_smt2(out, coll.get_sorts()[i], 0, 0, nullptr); } m_num_sorts = n; n = coll.get_num_decls(); for (unsigned i = m_num_decls; i < n; ++i) { func_decl* f = coll.get_func_decls()[i]; if (f->get_family_id() == null_family_id && !m_removed.contains(f)) { ast_smt2_pp(out, f, m_env) << "\n"; } } m_num_decls = n; if (first) { vector> recfuns; recfun::util u(m); func_decl_ref_vector funs = u.get_rec_funs(); if (funs.empty()) return; for (func_decl * f : funs) { recfuns.push_back(std::make_pair(f, u.get_def(f).get_rhs())); } ast_smt2_pp_recdefs(out, recfuns, m_env); } } void ast_pp_util::remove_decl(func_decl* f) { m_removed.insert(f); } std::ostream& ast_pp_util::display_expr(std::ostream& out, expr* f, bool neat) { if (neat) { ast_smt2_pp(out, f, m_env); } else { ast_smt_pp ll_smt2_pp(m); ll_smt2_pp.display_expr_smt2(out, f); } return out; } void ast_pp_util::display_assert(std::ostream& out, expr* f, bool neat) { display_expr(out << "(assert ", f, neat) << ")\n"; } void ast_pp_util::display_assert_and_track(std::ostream& out, expr* f, expr* t, bool neat) { if (neat) { ast_smt2_pp(out << "(assert (=> ", t, m_env) << " "; ast_smt2_pp(out, f, m_env) << "))\n"; } else { ast_smt_pp ll_smt2_pp(m); ll_smt2_pp.display_expr_smt2(out << "(assert (=> ", t); out << " "; ll_smt2_pp.display_expr_smt2(out, f); out << "))\n"; } } void ast_pp_util::display_asserts(std::ostream& out, expr_ref_vector const& fmls, bool neat) { if (neat) { for (expr* f : fmls) { out << "(assert "; ast_smt2_pp(out, f, m_env); out << ")\n"; } } else { ast_smt_pp ll_smt2_pp(m); for (expr* f : fmls) { out << "(assert "; ll_smt2_pp.display_expr_smt2(out, f); out << ")\n"; } } } void ast_pp_util::push() { coll.push(); m_num_sorts_trail.push_back(m_num_sorts); m_num_decls_trail.push_back(m_num_decls); } void ast_pp_util::pop(unsigned n) { coll.pop(n); m_num_sorts = m_num_sorts_trail[m_num_sorts_trail.size() - n]; m_num_decls = m_num_decls_trail[m_num_decls_trail.size() - n]; m_num_sorts_trail.shrink(m_num_sorts_trail.size() - n); m_num_decls_trail.shrink(m_num_decls_trail.size() - n); } z3-z3-4.8.7/src/ast/ast_pp_util.h000066400000000000000000000027541356505360400165110ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: ast_pp_util.h Abstract: Utilities for printing SMT2 declarations and assertions. Author: Nikolaj Bjorner (nbjorner) 2015-8-6. Revision History: --*/ #ifndef AST_PP_UTIL_H_ #define AST_PP_UTIL_H_ #include "ast/decl_collector.h" #include "ast/ast_smt2_pp.h" #include "util/obj_hashtable.h" class ast_pp_util { ast_manager& m; obj_hashtable m_removed; smt2_pp_environment_dbg m_env; unsigned m_num_sorts, m_num_decls; unsigned_vector m_num_sorts_trail, m_num_decls_trail; public: decl_collector coll; ast_pp_util(ast_manager& m): m(m), m_env(m), m_num_sorts(0), m_num_decls(0), coll(m) {} void reset() { coll.reset(); m_removed.reset(); m_num_sorts = 0; m_num_decls = 0; } void collect(expr* e); void collect(unsigned n, expr* const* es); void collect(expr_ref_vector const& es); void remove_decl(func_decl* f); void display_decls(std::ostream& out); void display_asserts(std::ostream& out, expr_ref_vector const& fmls, bool neat = true); void display_assert(std::ostream& out, expr* f, bool neat = true); void display_assert_and_track(std::ostream& out, expr* f, expr* t, bool neat = true); std::ostream& display_expr(std::ostream& out, expr* f, bool neat = true); void push(); void pop(unsigned n); smt2_pp_environment& env() { return m_env; } }; #endif /* AST_PP_UTIL_H_ */ z3-z3-4.8.7/src/ast/ast_printer.cpp000066400000000000000000000040251356505360400170440ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: ast_printer.cpp Abstract: Abstract AST printer Author: Leonardo de Moura (leonardo) 2012-10-21 Revision History: --*/ #include "ast/ast_printer.h" #include "ast/pp.h" class simple_ast_printer_context : public ast_printer_context { ast_manager & m_manager; scoped_ptr m_env; smt2_pp_environment_dbg & env() const { return *(m_env.get()); } public: simple_ast_printer_context(ast_manager & m):m_manager(m) { m_env = alloc(smt2_pp_environment_dbg, m); } ~simple_ast_printer_context() override {} ast_manager & m() const { return m_manager; } ast_manager & get_ast_manager() override { return m_manager; } void display(std::ostream & out, sort * s, unsigned indent = 0) const override { out << mk_ismt2_pp(s, m(), indent); } void display(std::ostream & out, expr * n, unsigned indent = 0) const override { out << mk_ismt2_pp(n, m(), indent); } void display(std::ostream & out, func_decl * f, unsigned indent = 0) const override { out << f->get_name(); } void pp(sort * s, format_ns::format_ref & r) const override { mk_smt2_format(s, env(), params_ref(), r); } void pp(func_decl * f, format_ns::format_ref & r) const override { mk_smt2_format(f, env(), params_ref(), r, "declare-fun"); } void pp(expr * n, format_ns::format_ref & r) const override { sbuffer buf; mk_smt2_format(n, env(), params_ref(), 0, nullptr, r, buf); } void pp(expr * n, unsigned num_vars, char const * var_prefix, format_ns::format_ref & r, sbuffer & var_names) const override { mk_smt2_format(n, env(), params_ref(), num_vars, var_prefix, r, var_names); } void display(std::ostream & out, expr * n, unsigned indent, unsigned num_vars, char const * var_prefix, sbuffer & var_names) const override { NOT_IMPLEMENTED_YET(); } }; ast_printer_context * mk_simple_ast_printer_context(ast_manager & m) { return alloc(simple_ast_printer_context, m); } z3-z3-4.8.7/src/ast/ast_printer.h000066400000000000000000000033061356505360400165120ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: ast_printer.h Abstract: Abstract AST printer Author: Leonardo de Moura (leonardo) 2012-10-21 Revision History: --*/ #ifndef AST_PRINTER_H_ #define AST_PRINTER_H_ #include "ast/ast.h" #include "ast/ast_smt2_pp.h" class ast_printer { public: virtual ~ast_printer() {} virtual void pp(sort * s, format_ns::format_ref & r) const { UNREACHABLE(); } virtual void pp(func_decl * f, format_ns::format_ref & r) const { UNREACHABLE(); } virtual void pp(expr * n, unsigned num_vars, char const * var_prefix, format_ns::format_ref & r, sbuffer & var_names) const { UNREACHABLE(); } virtual void pp(expr * n, format_ns::format_ref & r) const { UNREACHABLE(); } virtual void display(std::ostream & out, sort * s, unsigned indent = 0) const { out << "#" << s->get_id() << "\n"; } virtual void display(std::ostream & out, expr * n, unsigned indent, unsigned num_vars, char const * var_prefix, sbuffer & var_names) const { out << "#" << n->get_id() << "\n"; } virtual void display(std::ostream & out, expr * n, unsigned indent = 0) const { out << "#" << n->get_id() << "\n"; } virtual void display(std::ostream & out, func_decl * f, unsigned indent = 0) const { out << "#" << f->get_id() << "\n"; } }; class ast_printer_context : public ast_printer { public: ~ast_printer_context() override {} virtual ast_manager & get_ast_manager() = 0; virtual std::ostream & regular_stream() { return std::cout; } virtual std::ostream & diagnostic_stream() { return std::cerr; } }; ast_printer_context * mk_simple_ast_printer_context(ast_manager & m); #endif z3-z3-4.8.7/src/ast/ast_smt2_pp.cpp000066400000000000000000001410771356505360400167560ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: ast_smt2_pp.cpp Abstract: Pretty printer of AST formulas using SMT2 format. This printer is more expensive than the one in ast_smt_pp.h, but is supposed to generated a "prettier" and SMT2 compliant output. Author: Leonardo de Moura (leonardo) Revision History: --*/ #include "ast/ast_smt2_pp.h" #include "ast/shared_occs.h" #include "ast/pp.h" #include "ast/ast_ll_pp.h" #include "ast/ast_pp.h" #include "math/polynomial/algebraic_numbers.h" #include "ast/pp_params.hpp" using namespace format_ns; #define ALIAS_PREFIX "a" #define MAX_INDENT 16 #define SMALL_INDENT 2 format * smt2_pp_environment::pp_fdecl_name(symbol const & s, unsigned & len, bool is_skolem) const { ast_manager & m = get_manager(); if (is_smt2_quoted_symbol(s)) { std::string str = mk_smt2_quoted_symbol(s); len = static_cast(str.length()); return mk_string(m, str.c_str()); } else if (s.is_numerical()) { std::string str = s.str(); len = static_cast(str.length()); return mk_string(m, str.c_str()); } else if (!s.bare_str()) { len = 4; return mk_string(m, "null"); } else { len = static_cast(strlen(s.bare_str())); return mk_string(m, s.bare_str()); } } format * smt2_pp_environment::pp_fdecl_name(func_decl * f, unsigned & len) const { ast_manager & m = get_manager(); if (m.is_implies(f)) { len = 2; return mk_string(m, "=>"); } else if (m.is_ite(f)) { len = 3; return mk_string(m, "ite"); } else { symbol s = f->get_name(); return pp_fdecl_name(s, len, f->is_skolem()); } } bool smt2_pp_environment::is_indexed_fdecl(func_decl * f) const { if (f->get_family_id() == null_family_id) return false; unsigned num = f->get_num_parameters(); unsigned i; for (i = 0; i < num; i++) { if (f->get_parameter(i).is_int()) continue; if (f->get_parameter(i).is_rational()) continue; if (f->get_parameter(i).is_ast() && is_func_decl(f->get_parameter(i).get_ast())) continue; break; } return i == num && num > 0; } bool smt2_pp_environment::is_sort_param(func_decl * f) const { return f->get_family_id() != null_family_id && f->get_num_parameters() == 1 && f->get_parameter(0).is_ast() && is_sort(f->get_parameter(0).get_ast()) && f->get_range() == to_sort(f->get_parameter(0).get_ast()); } format * smt2_pp_environment::pp_as(format * fname, sort * s) { format * buf[2] = { fname, pp_sort(s) }; SASSERT(buf[0] != 0 && buf[1] != 0); return mk_seq1(get_manager(), buf, buf + 2, f2f(), "as"); } format * smt2_pp_environment::pp_fdecl_params(format * fname, func_decl * f) { SASSERT(is_indexed_fdecl(f)); unsigned num = f->get_num_parameters(); ptr_buffer fs; fs.push_back(fname); for (unsigned i = 0; i < num; i++) { SASSERT(f->get_parameter(i).is_int() || f->get_parameter(i).is_rational() || (f->get_parameter(i).is_ast() && is_func_decl(f->get_parameter(i).get_ast()))); if (f->get_parameter(i).is_int()) fs.push_back(mk_int(get_manager(), f->get_parameter(i).get_int())); else if (f->get_parameter(i).is_rational()) { std::string str = f->get_parameter(i).get_rational().to_string(); fs.push_back(mk_string(get_manager(), str.c_str())); } else fs.push_back(pp_fdecl_ref(to_func_decl(f->get_parameter(i).get_ast()))); } return mk_seq1(get_manager(), fs.begin(), fs.end(), f2f(), "_"); } format * smt2_pp_environment::pp_fdecl(func_decl * f, unsigned & len) { format * fname = pp_fdecl_name(f, len); if (f->get_family_id() == null_family_id) return fname; if (is_sort_param(f)) { len = UINT_MAX; return pp_as(fname, f->get_range()); } if (is_indexed_fdecl(f)) { len = UINT_MAX; return pp_fdecl_params(fname, f); } return fname; } format * smt2_pp_environment::pp_signature(format * f_name, func_decl * f) { if (is_indexed_fdecl(f)) { f_name = pp_fdecl_params(f_name, f); } ptr_buffer f_domain; for (unsigned i = 0; i < f->get_arity(); i++) f_domain.push_back(pp_sort(f->get_domain(i))); ptr_buffer args; args.push_back(f_name); args.push_back(mk_seq5(get_manager(), f_domain.begin(), f_domain.end(), f2f())); args.push_back(pp_sort(f->get_range())); return mk_seq5(get_manager(), args.begin(), args.end(), f2f()); } format * smt2_pp_environment::pp_fdecl_ref(func_decl * f) { unsigned len; format * f_name = pp_fdecl_name(f, len); if (f->get_family_id() == null_family_id) { return f_name; } return pp_signature(f_name, f); } format * smt2_pp_environment::pp_bv_literal(app * t, bool use_bv_lits, bool bv_neg) { bv_util & u = get_bvutil(); SASSERT(u.is_numeral(t)); rational val; unsigned bv_size = 1; u.is_numeral(t, val, bv_size); SASSERT(val.is_int()); val = u.norm(val, bv_size, bv_neg); bool is_neg = false; if (val.is_neg()) { val.neg(); is_neg = true; } SASSERT(val.is_nonneg()); format * vf; if (!use_bv_lits) { string_buffer<> buf; buf << "(_ bv" << val.to_string().c_str() << " " << bv_size << ")"; vf = mk_string(get_manager(), buf.c_str()); } else { sbuffer buf; unsigned sz = 0; buf.push_back('#'); if (bv_size % 4 == 0) { buf.push_back('x'); while (val.is_pos()) { rational c = val % rational(16); val = div(val, rational(16)); SASSERT(rational(0) <= c && c < rational(16)); if (c <= rational(9)) buf.push_back('0' + c.get_unsigned()); else buf.push_back('a' + (c.get_unsigned() - 10)); sz+=4; } while (sz < bv_size) { buf.push_back('0'); sz+=4; } } else { buf.push_back('b'); while (val.is_pos()) { rational c = val % rational(2); val = div(val, rational(2)); SASSERT(rational(0) <= c && c < rational(2)); if (c.is_zero()) buf.push_back('0'); else buf.push_back('1'); sz += 1; } while (sz < bv_size) { buf.push_back('0'); sz += 1; } } SASSERT(sz == bv_size); std::reverse(buf.begin()+2, buf.end()); buf.push_back(0); vf = mk_string(get_manager(), buf.begin()); } if (is_neg) { format * buffer[1] = {vf}; return mk_seq1(get_manager(), buffer, buffer+1, f2f(), "bvneg"); } return vf; } format * smt2_pp_environment::pp_float_literal(app * t, bool use_bv_lits, bool use_float_real_lits) { mpf_manager & fm = get_futil().fm(); scoped_mpf v(fm); ast_manager & m = get_manager(); format * body = nullptr; 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() << ")"; return mk_string(m, buf.c_str()); } else if (fm.is_pinf(v)) { buf << "(_ +oo " << v.get().get_ebits() << " " << v.get().get_sbits() << ")"; return mk_string(m, buf.c_str()); } else if (fm.is_ninf(v)) { buf << "(_ -oo " << v.get().get_ebits() << " " << v.get().get_sbits() << ")"; return mk_string(m, buf.c_str()); } else if (fm.is_pzero(v)) { buf << "(_ +zero " << v.get().get_ebits() << " " << v.get().get_sbits() << ")"; return mk_string(m, buf.c_str()); } else if (fm.is_nzero(v)) { buf << "(_ -zero " << v.get().get_ebits() << " " << v.get().get_sbits() << ")"; return mk_string(m, buf.c_str()); } else if (use_float_real_lits) { buf << "((_ to_fp " << v.get().get_ebits() << " " << v.get().get_sbits() << ") RTZ " << fm.to_string(v).c_str() << ")"; return mk_string(m, buf.c_str()); } else { if (use_bv_lits) buf << "(fp #b" << (fm.sgn(v) ? 1 : 0); else buf << "(fp (_ bv" << (fm.sgn(v) ? 1 : 0) << " 1)"; body = mk_string(m, buf.c_str()); body = mk_compose(m, body, mk_string(m, " ")); mpf_exp_t exp = fm.exp(v); const mpz & bias = fm.m_powers2.m1(v.get().get_ebits() - 1); mpf_exp_t biased_exp = exp + fm.mpz_manager().get_int64(bias); app_ref e_biased_exp(m); e_biased_exp = get_bvutil().mk_numeral(biased_exp, v.get().get_ebits()); body = mk_compose(m, body, pp_bv_literal(e_biased_exp, use_bv_lits, false)); body = mk_compose(m, body, mk_string(m, " ")); scoped_mpz sig(fm.mpz_manager()); sig = fm.sig(v); app_ref e_sig(m); e_sig = get_bvutil().mk_numeral(rational(sig), v.get().get_sbits() - 1); body = mk_compose(m, body, pp_bv_literal(e_sig, use_bv_lits, false)); body = mk_compose(m, body, mk_string(m, ")")); return body; } } // generate (- f) format * smt2_pp_environment::mk_neg(format * f) const { format * buffer[1] = {f}; return mk_seq1(get_manager(), buffer, buffer+1, f2f(), "-"); } // Return the format string .0 where num is the value of val. format * smt2_pp_environment::mk_float(rational const & val) const { SASSERT(val.is_nonneg()); SASSERT(val.is_int()); std::string s = val.to_string(); s += ".0"; return mk_string(get_manager(), s.c_str()); } format * smt2_pp_environment::pp_arith_literal(app * t, bool decimal, unsigned decimal_prec) { arith_util & u = get_autil(); SASSERT(u.is_numeral(t) || u.is_irrational_algebraic_numeral(t)); rational val; bool is_int = true; if (u.is_numeral(t, val, is_int)) { if (is_int) { if (val.is_nonneg()) { return mk_string(get_manager(), val.to_string().c_str()); } else { val.neg(); return mk_neg(mk_string(get_manager(), val.to_string().c_str())); } } else { bool is_neg = val.is_neg(); if (is_neg) val.neg(); format * vf; if (val.is_int()) { vf = mk_float(val); } else if (decimal) { std::ostringstream buffer; val.display_decimal(buffer, decimal_prec); vf = mk_string(get_manager(), buffer.str().c_str()); } else { format * buffer[2] = { mk_float(numerator(val)), mk_float(denominator(val)) }; vf = mk_seq1(get_manager(), buffer, buffer+2, f2f(), "/"); } return is_neg ? mk_neg(vf) : vf; } } else { SASSERT(u.is_irrational_algebraic_numeral(t)); anum const & val2 = u.to_irrational_algebraic_numeral(t); algebraic_numbers::manager & am = u.am(); format * vf; std::ostringstream buffer; bool is_neg = false; if (decimal) { scoped_anum abs_val(am); am.set(abs_val, val2); if (am.is_neg(val2)) { is_neg = true; am.neg(abs_val); } am.display_decimal(buffer, abs_val, decimal_prec); } else { am.display_root_smt2(buffer, val2); } vf = mk_string(get_manager(), buffer.str().c_str()); return is_neg ? mk_neg(vf) : vf; } } format * smt2_pp_environment::pp_string_literal(app * t) { zstring s; std::string encs; VERIFY (get_sutil().str.is_string(t, s)); encs = s.encode(); std::ostringstream buffer; buffer << "\""; for (unsigned i = 0; i < encs.length(); ++i) { if (encs[i] == '\"') { buffer << "\"\""; } else { buffer << encs[i]; } } buffer << "\""; return mk_string(get_manager(), buffer.str().c_str()); } format * smt2_pp_environment::pp_datalog_literal(app * t) { uint64_t v; VERIFY (get_dlutil().is_numeral(t, v)); std::ostringstream buffer; buffer << v; return mk_string(get_manager(), buffer.str().c_str()); } format_ns::format * smt2_pp_environment::pp_sort(sort * s) { // Basic sort pretty printing. // This method is redefined in cmd_context::pp_env: support for parametric sorts. // Here, we just pretty print builtin sorts: Bool, Int, Real, BitVec and Array. ast_manager & m = get_manager(); if (m.is_bool(s)) return mk_string(m, "Bool"); if (get_autil().is_int(s)) return mk_string(m, "Int"); if (get_autil().is_real(s)) return mk_string(m, "Real"); if (get_bvutil().is_bv_sort(s)) { unsigned sz = get_bvutil().get_bv_size(s); ptr_buffer fs; fs.push_back(mk_string(m, "BitVec")); fs.push_back(mk_unsigned(m, sz)); return mk_seq1(m, fs.begin(), fs.end(), f2f(), "_"); } if (get_arutil().is_array(s)) { ptr_buffer fs; unsigned sz = get_array_arity(s); for (unsigned i = 0; i < sz; i++) { fs.push_back(pp_sort(get_array_domain(s, i))); } fs.push_back(pp_sort(get_array_range(s))); return mk_seq1(m, fs.begin(), fs.end(), f2f(), "Array"); } if (get_futil().is_float(s)) { unsigned ebits = get_futil().get_ebits(s); unsigned sbits = get_futil().get_sbits(s); ptr_buffer fs; fs.push_back(mk_string(m, "FloatingPoint")); fs.push_back(mk_unsigned(m, ebits)); fs.push_back(mk_unsigned(m, sbits)); return mk_seq1(m, fs.begin(), fs.end(), f2f(), "_"); } if ((get_sutil().is_seq(s) || get_sutil().is_re(s)) && !get_sutil().is_string(s)) { ptr_buffer fs; fs.push_back(pp_sort(to_sort(s->get_parameter(0).get_ast()))); return mk_seq1(m, fs.begin(), fs.end(), f2f(), get_sutil().is_seq(s)?"Seq":"RegEx"); } if (get_dtutil().is_datatype(s)) { unsigned sz = get_dtutil().get_datatype_num_parameter_sorts(s); if (sz > 0) { ptr_buffer fs; for (unsigned i = 0; i < sz; i++) { fs.push_back(pp_sort(get_dtutil().get_datatype_parameter_sort(s, i))); } return mk_seq1(m, fs.begin(), fs.end(), f2f(), s->get_name().str().c_str()); } } return format_ns::mk_string(get_manager(), s->get_name().str().c_str()); } typedef app_ref_vector format_ref_vector; class smt2_printer { ast_manager & m_manager; smt2_pp_environment & m_env; shared_occs m_soccs; expr * m_root; typedef obj_map expr2alias; // expr -> position @ m_aliased_exprs, m_aliased_pps, m_aliased_lvls_names. ptr_vector m_expr2alias_stack; expr2alias * m_expr2alias; // expr -> position @ m_aliased_exprs, m_aliased_pps, m_aliased_lvls_names. ptr_vector m_aliased_exprs; format_ref_vector m_aliased_pps; svector > m_aliased_lvls_names; unsigned m_next_alias_idx; struct scope { unsigned m_aliased_exprs_lim; unsigned m_old_next_alias_idx; expr * m_old_root; scope(unsigned lim, unsigned idx, expr * r):m_aliased_exprs_lim(lim), m_old_next_alias_idx(idx), m_old_root(r) {} }; svector m_scopes; // size of m_aliased_exprs, m_aliased_pps, m_aliased_lvls_names. svector m_var_names; typedef hashtable symbol_set; symbol_set m_var_names_set; struct frame { expr * m_curr; unsigned m_idx; unsigned m_spos; bool m_use_alias; // if new aliases can be created frame(expr * c, unsigned i, unsigned s, bool use_alias):m_curr(c), m_idx(i), m_spos(s), m_use_alias(use_alias) {} }; svector m_frame_stack; format_ref_vector m_format_stack; struct info { unsigned m_lvl; unsigned m_weight; unsigned m_depth; info(unsigned l, unsigned w, unsigned d):m_lvl(l), m_weight(w), m_depth(d) {} }; svector m_info_stack; string_buffer<> m_next_name_buffer; // Config bool m_pp_decimal; unsigned m_pp_decimal_precision; bool m_pp_bv_lits; bool m_pp_float_real_lits; bool m_pp_bv_neg; unsigned m_pp_max_depth; unsigned m_pp_min_alias_size; bool m_pp_flat_assoc; symbol next_name(char const * prefix, unsigned & idx) { while (true) { m_next_name_buffer.reset(); m_next_name_buffer.append(prefix); m_next_name_buffer.append("!"); m_next_name_buffer.append(idx); symbol r(m_next_name_buffer.c_str()); idx++; if (m_env.uses(r)) continue; if (m_var_names_set.contains(r)) continue; return r; } } symbol next_alias() { return next_name(ALIAS_PREFIX, m_next_alias_idx); } void register_alias(expr * n, format * nf, unsigned lvl, symbol const & name) { SASSERT(m_aliased_exprs.size() == m_aliased_pps.size()); SASSERT(m_aliased_exprs.size() == m_aliased_lvls_names.size()); unsigned idx = m_aliased_exprs.size(); m_expr2alias->insert(n, idx); m_aliased_exprs.push_back(n); m_aliased_pps.push_back(nf); m_aliased_lvls_names.push_back(std::make_pair(lvl, name)); } void push_frame(expr * n, bool use_alias) { m_frame_stack.push_back(frame(n, 0, m_format_stack.size(), use_alias)); } void pop_frame() { m_frame_stack.pop_back(); } ast_manager & m() const { return m_manager; } ast_manager & fm() const { return format_ns::fm(m()); } std::string ensure_quote(symbol const& s) { std::string str; if (is_smt2_quoted_symbol(s)) str = mk_smt2_quoted_symbol(s); else str = s.str(); return str; } symbol ensure_quote_sym(symbol const& s) { if (is_smt2_quoted_symbol(s)) { std::string str; str = mk_smt2_quoted_symbol(s); return symbol(str.c_str()); } else return s; } void pp_var(var * v) { format * f; if (v->get_idx() < m_var_names.size()) { symbol s = m_var_names[m_var_names.size() - v->get_idx() - 1]; std::string vname; if (is_smt2_quoted_symbol (s)) { vname = mk_smt2_quoted_symbol (s); } else { vname = s.str(); } f = mk_string(m(), vname.c_str ()); } else { // fallback... it is not supposed to happen when the printer is correctly used. string_buffer<> buf; buf.append("(:var "); buf.append(v->get_idx()); //buf.append(" "); //buf.append(v->get_sort()->get_name().str().c_str()); buf.append(")"); f = mk_string(m(), buf.c_str()); } m_format_stack.push_back(f); m_info_stack.push_back(info(0, 1, 1)); } format * pp_attribute(char const * attr, format * f) { return mk_compose(m(), mk_string(m(), attr), mk_indent(m(), static_cast(strlen(attr)), f)); } format * pp_simple_attribute(char const * attr, int v) { return mk_compose(m(), mk_string(m(), attr), mk_int(m(), v)); } format * pp_simple_attribute(char const * attr, symbol const & s) { std::string str = ensure_quote(s); return mk_compose(m(), mk_string(m(), attr), mk_string(m(), str.c_str())); } format * pp_labels(bool is_pos, buffer const & names, format * f) { if (names.empty()) return f; ptr_buffer buf; buf.push_back(f); for (symbol const& n : names) buf.push_back(pp_simple_attribute(is_pos ? ":lblpos " : ":lblneg ", n)); return mk_seq1(m(), buf.begin(), buf.end(), f2f(), "!"); } void pp_const(app * c) { format * f; if (m_env.get_autil().is_numeral(c) || m_env.get_autil().is_irrational_algebraic_numeral(c)) { f = m_env.pp_arith_literal(c, m_pp_decimal, m_pp_decimal_precision); } else if (m_env.get_sutil().str.is_string(c)) { f = m_env.pp_string_literal(c); } else if (m_env.get_bvutil().is_numeral(c)) { f = m_env.pp_bv_literal(c, m_pp_bv_lits, m_pp_bv_neg); } else if (m_env.get_futil().is_numeral(c)) { f = m_env.pp_float_literal(c, m_pp_bv_lits, m_pp_float_real_lits); } else if (m_env.get_dlutil().is_numeral(c)) { f = m_env.pp_datalog_literal(c); } else { buffer names; if (m().is_label_lit(c, names)) { f = pp_labels(true, names, mk_string(m(), "true")); } else { unsigned len; f = m_env.pp_fdecl(c->get_decl(), len); } } m_format_stack.push_back(f); m_info_stack.push_back(info(0, 1, 1)); } bool pp_aliased(expr * t) { unsigned idx; if (m_expr2alias->find(t, idx)) { unsigned lvl = m_aliased_lvls_names[idx].first; symbol const & s = m_aliased_lvls_names[idx].second; m_format_stack.push_back(mk_string(m(), s.str().c_str())); m_info_stack.push_back(info(lvl+1, 1, 1)); return true; } return false; } void process_var(var * v) { pp_var(v); pop_frame(); } bool process_args(app * t, frame & fr) { unsigned num = t->get_num_args(); while (fr.m_idx < num) { expr * arg = t->get_arg(fr.m_idx); fr.m_idx++; if (pp_aliased(arg)) continue; switch (arg->get_kind()) { case AST_VAR: pp_var(to_var(arg)); break; case AST_APP: if (to_app(arg)->get_num_args() == 0) { pp_const(to_app(arg)); } else { push_frame(arg, fr.m_use_alias); return false; } break; case AST_QUANTIFIER: push_frame(arg, fr.m_use_alias); return false; default: UNREACHABLE(); } } return true; } void store_result(expr * t, frame & fr, format * f, info & f_info) { m_format_stack.shrink(fr.m_spos); m_info_stack.shrink(fr.m_spos); if (fr.m_use_alias && m_root != t && ((f_info.m_depth >= m_pp_max_depth) || ((f_info.m_weight >= m_pp_min_alias_size || is_quantifier(t)) && m_soccs.is_shared(t)))) { symbol a = next_alias(); TRACE("smt2_pp", tout << "a: " << a << " depth: " << f_info.m_depth << ", weight: " << f_info.m_weight << ", lvl: " << f_info.m_lvl << " t: #" << t->get_id() << "\n" << mk_ll_pp(t, m()) << ", is-shared: " << m_soccs.is_shared(t) << "\n";); register_alias(t, f, f_info.m_lvl, a); m_format_stack.push_back(mk_string(m(), a.str().c_str())); m_info_stack.push_back(info(f_info.m_lvl + 1, 1, 1)); } else { m_format_stack.push_back(f); m_info_stack.push_back(f_info); } pop_frame(); } bool flat_assoc(app * t, frame const & fr) { if (!m_pp_flat_assoc) return false; func_decl * f = t->get_decl(); if (f->is_associative() && m_frame_stack.size() >= 2 && !m_soccs.is_shared(t)) { frame const & prev_fr = m_frame_stack[m_frame_stack.size() - 2]; return is_app(prev_fr.m_curr) && to_app(prev_fr.m_curr)->get_decl() == f; } return false; } void process_app(app * t, frame & fr) { if (fr.m_idx == 0) { if (pp_aliased(t)) { pop_frame(); return; } } if (!process_args(t, fr)) return; if (t->get_num_args() == 0) { pp_const(t); pop_frame(); return; } if (flat_assoc(t, fr)) { pop_frame(); return; } buffer labels; bool is_pos; format * f = nullptr; format ** it = m_format_stack.c_ptr() + fr.m_spos; format ** end = m_format_stack.c_ptr() + m_format_stack.size(); if (m().is_label(t, is_pos, labels)) { SASSERT(it + 1 == end); f = pp_labels(is_pos, labels, *it); } else if (m().is_pattern(t)) { f = mk_seq5(m(), it, end, f2f()); } else { unsigned len; SASSERT(it < end); format * fname = m_env.pp_fdecl(t->get_decl(), len); if (len > MAX_INDENT) { f = mk_group(m(), mk_compose(m(), mk_indent(m(), 1, mk_compose(m(), mk_string(m(), "("), fname)), mk_indent(m(), SMALL_INDENT, mk_compose(m(), mk_seq(m(), it, end, f2f()), mk_string(m(), ")"))))); } else { format * first = *it; ++it; f = mk_group(m(), mk_compose(m(), mk_indent(m(), 1, mk_compose(m(), mk_string(m(), "("), fname)), mk_indent(m(), len + 2, mk_compose(m(), mk_string(m(), " "), first, mk_seq(m(), it, end, f2f()), mk_string(m(), ")"))))); } } info f_info(0, 1, 1); info * it2 = m_info_stack.begin() + fr.m_spos; info * end2 = m_info_stack.end(); for (; it2 != end2; ++it2) { if (it2->m_lvl > f_info.m_lvl) f_info.m_lvl = it2->m_lvl; f_info.m_weight += it2->m_weight; if (it2->m_depth > f_info.m_depth) f_info.m_depth = it2->m_depth; } f_info.m_depth++; store_result(t, fr, f, f_info); } // Add let decls used to build f. format * pp_let(format * f, unsigned & num_lets) { unsigned old_sz = m_scopes.empty() ? 0 : m_scopes.back().m_aliased_exprs_lim; unsigned sz = m_aliased_exprs.size(); SASSERT(old_sz <= sz); num_lets = sz - old_sz; TRACE("pp_let", tout << "old_sz: " << old_sz << ", sz: " << sz << "\n";); if (old_sz == sz) return f; vector > decls; for (unsigned i = old_sz; i < sz; i++) { unsigned lvl = m_aliased_lvls_names[i].first; symbol f_name = m_aliased_lvls_names[i].second; format * f_def[1] = { m_aliased_pps.get(i) }; decls.reserve(lvl+1); ptr_vector & lvl_decls = decls[lvl]; lvl_decls.push_back(mk_seq1(m(), f_def, f_def+1, f2f(), f_name.str().c_str())); } TRACE("pp_let", tout << "decls.size(): " << decls.size() << "\n";); ptr_buffer buf; unsigned num_op = 0; vector >::iterator it = decls.begin(); vector >::iterator end = decls.end(); for (; it != end; ++it) { ptr_vector & lvl_decls = *it; if (lvl_decls.empty()) continue; if (num_op > 0) buf.push_back(mk_line_break(m())); num_op++; buf.push_back(mk_string(m(), "(let ")); buf.push_back(mk_indent(m(), 5, mk_seq5(m(), lvl_decls.begin(), lvl_decls.end(), f2f()))); } TRACE("pp_let", tout << "num_op: " << num_op << "\n";); if (num_op == 0) return f; buf.push_back(mk_indent(m(), SMALL_INDENT, mk_compose(m(), mk_line_break(m()), f))); for (unsigned i = 0; i < num_op; i++) buf.push_back(mk_string(m(), ")")); return mk_compose(m(), buf.size(), buf.c_ptr()); } format * pp_let(format * f) { unsigned num_lets; return pp_let(f, num_lets); } void begin_scope() { SASSERT(m_aliased_exprs.size() == m_aliased_pps.size()); SASSERT(m_aliased_exprs.size() == m_aliased_lvls_names.size()); TRACE("pp_scope", tout << "[begin-scope] sz: " << m_aliased_exprs.size() << ", m_root: " << m_root << "\n";); m_scopes.push_back(scope(m_aliased_exprs.size(), m_next_alias_idx, m_root)); unsigned lvl = m_scopes.size(); while (lvl >= m_expr2alias_stack.size()) m_expr2alias_stack.push_back(alloc(expr2alias)); m_expr2alias = m_expr2alias_stack[lvl]; m_next_alias_idx = 1; } void end_scope() { TRACE("pp_scope", tout << "[end-scope] before sz: " << m_aliased_exprs.size() << ", m_root: " << m_root << "\n";); m_expr2alias->reset(); scope & s = m_scopes.back(); unsigned old_sz = s.m_aliased_exprs_lim; m_root = s.m_old_root; m_next_alias_idx = s.m_old_next_alias_idx; m_scopes.pop_back(); unsigned new_lvl = m_scopes.size(); m_expr2alias = m_expr2alias_stack[new_lvl]; SASSERT(old_sz <= m_aliased_exprs.size()); m_aliased_exprs.shrink(old_sz); m_aliased_pps.shrink(old_sz); m_aliased_lvls_names.shrink(old_sz); TRACE("pp_scope", tout << "[end-scope] after sz: " << m_aliased_exprs.size() << ", m_root: " << m_root << "\n";); } void register_var_names(quantifier * q) { unsigned num_decls = q->get_num_decls(); for (unsigned i = 0; i < num_decls; i++) { symbol name = ensure_quote_sym(q->get_decl_name(i)); if (name.is_numerical()) { unsigned idx = 1; name = next_name("x", idx); } else if (m_env.uses(name) || m_var_names_set.contains(name)) { unsigned idx = 1; name = next_name(name.bare_str(), idx); } SASSERT(!m_var_names_set.contains(name)); m_var_names.push_back(name); m_var_names_set.insert(name); } } void register_var_names(unsigned n) { unsigned idx = 1; for (unsigned i = 0; i < n; i++) { symbol name = next_name("x", idx); SASSERT(!m_var_names_set.contains(name)); m_var_names.push_back(name); m_var_names_set.insert(name); } } void unregister_var_names(quantifier * q) { unregister_var_names(q->get_num_decls()); } void unregister_var_names(unsigned num_decls) { for (unsigned i = 0; i < num_decls; i++) { symbol s = m_var_names.back(); m_var_names.pop_back(); m_var_names_set.erase(s); } } format * pp_var_args(unsigned num_decls, sort* const* srts) { ptr_buffer buf; SASSERT(num_decls <= m_var_names.size()); symbol * it = m_var_names.end() - num_decls; for (unsigned i = 0; i < num_decls; i++, it++) { format * fs[1] = { m_env.pp_sort(srts[i]) }; std::string var_name; if (is_smt2_quoted_symbol (*it)) { var_name = mk_smt2_quoted_symbol (*it); } else { var_name = it->str (); } buf.push_back(mk_seq1(m(), fs, fs+1, f2f(), var_name.c_str ())); } return mk_seq5(m(), buf.begin(), buf.end(), f2f()); } format * pp_var_decls(quantifier * q) { return pp_var_args(q->get_num_decls(), q->get_decl_sorts()); } void process_quantifier(quantifier * q, frame & fr) { if (fr.m_idx == 0) { begin_scope(); m_root = q->get_expr(); register_var_names(q); } unsigned num_children = q->get_num_patterns() + q->get_num_no_patterns() + 1; if (fr.m_idx < num_children) { unsigned idx = fr.m_idx; fr.m_idx++; if (idx < q->get_num_patterns()) { push_frame(q->get_pattern(idx), false); } else if (idx < q->get_num_patterns() + q->get_num_no_patterns()) { push_frame(q->get_no_pattern(idx - q->get_num_patterns()), false); } else { push_frame(q->get_expr(), fr.m_use_alias); } return; } unsigned num_lets = 0; format * f_body = pp_let(m_format_stack.back(), num_lets); // The current SMT2 frontend uses weight 1 as default. #define DEFAULT_WEIGHT 1 if (q->has_patterns() || q->get_weight() != DEFAULT_WEIGHT || q->get_skid() != symbol::null || (q->get_qid() != symbol::null && !q->get_qid().is_numerical())) { ptr_buffer buf; buf.push_back(f_body); if (q->get_num_patterns() > 0) { format ** it = m_format_stack.c_ptr() + fr.m_spos; format ** end = it + q->get_num_patterns(); for (; it != end; ++it) { buf.push_back(pp_attribute(":pattern ", *it)); } } if (q->get_num_no_patterns() > 0) { format ** it = m_format_stack.c_ptr() + fr.m_spos + q->get_num_patterns(); format ** end = it + q->get_num_no_patterns(); for (; it != end; ++it) { buf.push_back(pp_attribute(":no-pattern ", *it)); } } if (q->get_weight() != DEFAULT_WEIGHT) { buf.push_back(pp_simple_attribute(":weight ", q->get_weight())); } if (q->get_skid() != symbol::null) { buf.push_back(pp_simple_attribute(":skolemid ", q->get_skid())); } if (q->get_qid() != symbol::null) { #if 0 buf.push_back(pp_simple_attribute(":qid ", q->get_qid())); #else if (!q->get_qid().is_numerical()) buf.push_back(pp_simple_attribute(":qid ", q->get_qid())); #endif } f_body = mk_seq1(m(), buf.begin(), buf.end(), f2f(), "!"); } format * f_decls = pp_var_decls(q); format * fs[2] = { f_decls, f_body }; char const * header = q->get_kind() == forall_k ? "forall" : (q->get_kind() == exists_k ? "exists" : "lambda"); format * f = mk_seq3(m(), fs, fs+2, f2f(), header, 1, SMALL_INDENT); info f_info = m_info_stack.back(); f_info.m_lvl = 0; // quantifiers don't depend on any let-decls, pp_let added all dependencies for the body. f_info.m_depth++; f_info.m_weight += q->get_num_decls()*2 + num_lets*8; unregister_var_names(q); end_scope(); store_result(q, fr, f, f_info); } void init_expr2alias_stack() { SASSERT(m_expr2alias_stack.empty()); expr2alias * new_map = alloc(expr2alias); m_expr2alias_stack.push_back(new_map); m_expr2alias = new_map; } void del_expr2alias_stack() { std::for_each(m_expr2alias_stack.begin(), m_expr2alias_stack.end(), delete_proc()); m_expr2alias_stack.reset(); m_expr2alias = nullptr; } void reset_expr2alias_stack() { SASSERT(!m_expr2alias_stack.empty()); for (expr2alias * e : m_expr2alias_stack) e->reset(); m_expr2alias = m_expr2alias_stack[0]; } void reset_stacks() { m_next_alias_idx = 1; reset_expr2alias_stack(); m_aliased_exprs.reset(); m_aliased_pps.reset(); m_aliased_lvls_names.reset(); m_scopes.reset(); m_frame_stack.reset(); m_format_stack.reset(); m_info_stack.reset(); } void process(expr * n, format_ref & r) { if (!n) { r = mk_string(m(), "null"); return; } reset_stacks(); SASSERT(&(r.get_manager()) == &(fm())); m_soccs(n); m_root = n; push_frame(n, true); while (!m_frame_stack.empty()) { frame & fr = m_frame_stack.back(); switch (fr.m_curr->get_kind()) { case AST_QUANTIFIER: process_quantifier(to_quantifier(fr.m_curr), fr); break; case AST_APP: process_app(to_app(fr.m_curr), fr); break; case AST_VAR: process_var(to_var(fr.m_curr)); break; default: UNREACHABLE(); } } r = pp_let(m_format_stack.back()); m_format_stack.pop_back(); } void reset_var_names() { m_var_names.reset(); m_var_names_set.reset(); } public: smt2_printer(smt2_pp_environment & env, params_ref const & params): m_manager(env.get_manager()), m_env(env), m_soccs(m_manager), m_root(nullptr), m_aliased_pps(fm()), m_next_alias_idx(1), m_format_stack(fm()) { init_expr2alias_stack(); pp_params p(params); m_pp_decimal = p.decimal(); m_pp_decimal_precision = p.decimal_precision(); m_pp_bv_lits = p.bv_literals(); m_pp_float_real_lits = p.fp_real_literals(); m_pp_bv_neg = p.bv_neg(); m_pp_max_depth = p.max_depth(); m_pp_min_alias_size = p.min_alias_size(); m_pp_flat_assoc = p.flat_assoc(); } ~smt2_printer() { del_expr2alias_stack(); } void operator()(expr * n, format_ref & r) { reset_var_names(); process(n, r); } void operator()(expr * n, unsigned num, char const * var_prefix, format_ref & r, sbuffer & var_names) { reset_var_names(); if (var_prefix == nullptr) var_prefix = "x"; if (strcmp(var_prefix, ALIAS_PREFIX) == 0) { var_prefix = "_a"; } unsigned idx = 0; for (unsigned i = 0; i < num; i++) { symbol name = next_name(var_prefix, idx); name = ensure_quote_sym(name); var_names.push_back(name); m_var_names_set.insert(name); m_var_names.push_back(name); } std::reverse(m_var_names.begin(), m_var_names.end()); process(n, r); } void operator()(sort * s, format_ref & r) { r = m_env.pp_sort(s); } void operator()(func_decl * f, format_ref & r, char const* cmd) { if (!f) { r = mk_string(m(), "null"); return; } unsigned arity = f->get_arity(); unsigned len; format * fname = m_env.pp_fdecl_name(f, len); format * args[3]; args[0] = fname; ptr_buffer buf; for (unsigned i = 0; i < arity; i++) { buf.push_back(m_env.pp_sort(f->get_domain(i))); } args[1] = mk_seq5(m(), buf.begin(), buf.end(), f2f()); args[2] = m_env.pp_sort(f->get_range()); r = mk_seq1(m(), args, args+3, f2f(), cmd); } void operator()(func_decl * f, expr * e, format_ref & r, char const* cmd) { unsigned len; format * fname = m_env.pp_fdecl_name(f, len); register_var_names(f->get_arity()); format * args[4]; args[0] = fname; args[1] = pp_var_args(f->get_arity(), f->get_domain()); args[2] = m_env.pp_sort(f->get_range()); process(e, r); args[3] = r; r = mk_seq1(m(), args, args+4, f2f(), cmd); unregister_var_names(f->get_arity()); } // format set of mutually recursive definitions void operator()(vector> const& funs, format_ref & r) { format_ref_vector decls(m()), bodies(m()); format_ref r1(m()), r2(m()); for (auto const& p : funs) { unsigned len; func_decl* f = p.first; expr* e = p.second; format * fname = m_env.pp_fdecl_name(f, len); register_var_names(f->get_arity()); format * args[3]; args[0] = fname; args[1] = pp_var_args(f->get_arity(), f->get_domain()); args[2] = m_env.pp_sort(f->get_range()); decls.push_back(mk_seq1(m(), args, args+3, f2f(), "")); process(e, r); bodies.push_back(r); unregister_var_names(f->get_arity()); } r1 = mk_seq1(m(), decls.begin(), decls.end(), f2f(), ""); r2 = mk_seq1(m(), bodies.begin(), bodies.end(), f2f(), ""); format * args[2]; args[0] = r1; args[1] = r2; r = mk_seq1(m(), args, args+2, f2f(), "define-funs-rec"); } }; void mk_smt2_format(expr * n, smt2_pp_environment & env, params_ref const & p, unsigned num_vars, char const * var_prefix, format_ref & r, sbuffer & var_names) { smt2_printer pr(env, p); pr(n, num_vars, var_prefix, r, var_names); } void mk_smt2_format(sort * s, smt2_pp_environment & env, params_ref const & p, format_ref & r) { smt2_printer pr(env, p); pr(s, r); } void mk_smt2_format(func_decl * f, smt2_pp_environment & env, params_ref const & p, format_ref & r, char const* cmd) { smt2_printer pr(env, p); pr(f, r, cmd); } void mk_smt2_format(func_decl * f, expr * e, smt2_pp_environment & env, params_ref const & p, format_ref & r, char const* cmd) { smt2_printer pr(env, p); pr(f, e, r, cmd); } void mk_smt2_format(unsigned sz, expr * const* es, smt2_pp_environment & env, params_ref const & p, unsigned num_vars, char const * var_prefix, format_ref & r, sbuffer & var_names) { smt2_printer pr(env, p); ast_manager & m = env.get_manager(); format_ref_vector fmts(fm(m)); for (unsigned i = 0; i < sz; ++i) { format_ref fr(fm(m)); pr(es[i], num_vars, var_prefix, fr, var_names); fmts.push_back(std::move(fr)); } r = mk_seq(m, fmts.c_ptr(), fmts.c_ptr() + fmts.size(), f2f()); } std::ostream & ast_smt2_pp(std::ostream & out, expr * n, smt2_pp_environment & env, params_ref const & p, unsigned indent, unsigned num_vars, char const * var_prefix) { if (!n) return out << "null"; ast_manager & m = env.get_manager(); format_ref r(fm(m)); sbuffer var_names; mk_smt2_format(n, env, p, num_vars, var_prefix, r, var_names); if (indent > 0) r = mk_indent(m, indent, r.get()); pp(out, r.get(), m, p); return out; } std::ostream & ast_smt2_pp(std::ostream & out, sort * s, smt2_pp_environment & env, params_ref const & p, unsigned indent) { if (s == nullptr) return out << "null"; ast_manager & m = env.get_manager(); format_ref r(fm(m)); sbuffer var_names; mk_smt2_format(s, env, p, r); if (indent > 0) r = mk_indent(m, indent, r.get()); pp(out, r.get(), m, p); return out; } std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, smt2_pp_environment & env, params_ref const & p, unsigned indent, char const* cmd) { if (!f) return out << "null"; ast_manager & m = env.get_manager(); format_ref r(fm(m)); sbuffer var_names; mk_smt2_format(f, env, p, r, cmd); if (indent > 0) r = mk_indent(m, indent, r.get()); pp(out, r.get(), m, p); return out; } std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, expr* e, smt2_pp_environment & env, params_ref const & p, unsigned indent, char const* cmd) { if (!f) return out << "null"; ast_manager & m = env.get_manager(); format_ref r(fm(m)); sbuffer var_names; mk_smt2_format(f, e, env, p, r, cmd); if (indent > 0) r = mk_indent(m, indent, r.get()); pp(out, r.get(), m, p); return out; } std::ostream & ast_smt2_pp(std::ostream & out, unsigned sz, expr * const* es, smt2_pp_environment & env, params_ref const & p, unsigned indent, unsigned num_vars, char const * var_prefix) { ast_manager & m = env.get_manager(); format_ref r(fm(m)); sbuffer var_names; mk_smt2_format(sz, es, env, p, num_vars, var_prefix, r, var_names); if (indent > 0) r = mk_indent(m, indent, r.get()); pp(out, r.get(), m, p); return out; } std::ostream & ast_smt2_pp(std::ostream & out, symbol const& s, bool is_skolem, smt2_pp_environment & env, params_ref const& p) { unsigned len; ast_manager & m = env.get_manager(); format_ref r(fm(m)); r = env.pp_fdecl_name(s, len, is_skolem); pp(out, r.get(), m, p); return out; } std::ostream & ast_smt2_pp_recdefs(std::ostream & out, vector> const& funs, smt2_pp_environment & env, params_ref const & p) { ast_manager & m = env.get_manager(); format_ref r(fm(m)); smt2_printer pr(env, p); pr(funs, r); pp(out, r.get(), m, p); return out << "\n"; } mk_ismt2_pp::mk_ismt2_pp(ast * t, ast_manager & m, params_ref const & p, unsigned indent, unsigned num_vars, char const * var_prefix): m_ast(t), m_manager(m), m_params(p), m_indent(indent), m_num_vars(num_vars), m_var_prefix(var_prefix) { } mk_ismt2_pp::mk_ismt2_pp(ast * t, ast_manager & m, unsigned indent, unsigned num_vars, char const * var_prefix): m_ast(t), m_manager(m), m_params(m_empty), m_indent(indent), m_num_vars(num_vars), m_var_prefix(var_prefix) { } std::ostream& operator<<(std::ostream& out, mk_ismt2_pp const & p) { smt2_pp_environment_dbg env(p.m_manager); if (p.m_ast == nullptr) { out << "null"; } else if (is_expr(p.m_ast)) { ast_smt2_pp(out, to_expr(p.m_ast), env, p.m_params, p.m_indent, p.m_num_vars, p.m_var_prefix); } else if (is_sort(p.m_ast)) { ast_smt2_pp(out, to_sort(p.m_ast), env, p.m_params, p.m_indent); } else { SASSERT(is_func_decl(p.m_ast)); ast_smt2_pp(out, to_func_decl(p.m_ast), env, p.m_params, p.m_indent); } return out; } std::ostream& operator<<(std::ostream& out, expr_ref const& e) { return out << mk_ismt2_pp(e.get(), e.get_manager()); } std::ostream& operator<<(std::ostream& out, app_ref const& e) { return out << mk_ismt2_pp(e.get(), e.get_manager()); } std::ostream& operator<<(std::ostream& out, func_decl_ref const& e) { return out << mk_ismt2_pp(e.get(), e.get_manager()); } std::ostream& operator<<(std::ostream& out, sort_ref const& e) { return out << mk_ismt2_pp(e.get(), e.get_manager()); } std::ostream& operator<<(std::ostream& out, expr_ref_vector const& e) { smt2_pp_environment_dbg env(e.get_manager()); params_ref p; return ast_smt2_pp(out, e.size(), e.c_ptr(), env, p, 0, 0, nullptr); } std::ostream& operator<<(std::ostream& out, app_ref_vector const& e) { smt2_pp_environment_dbg env(e.get_manager()); params_ref p; return ast_smt2_pp(out, e.size(), (expr*const*)e.c_ptr(), env, p, 0, 0, nullptr); } std::ostream& operator<<(std::ostream& out, func_decl_ref_vector const& e) { for (func_decl* f : e) out << mk_ismt2_pp(f, e.get_manager()) << "\n"; return out; } std::ostream& operator<<(std::ostream& out, sort_ref_vector const& e) { for (sort* s : e) out << mk_ismt2_pp(s, e.get_manager()) << "\n"; return out; } #ifdef Z3DEBUG void pp(expr const * n, ast_manager & m) { std::cout << mk_ismt2_pp(const_cast(n), m) << std::endl; } void pp(expr_ref const & n) { std::cout << mk_ismt2_pp(n.get(), n.m()) << std::endl; } #endif z3-z3-4.8.7/src/ast/ast_smt2_pp.h000066400000000000000000000142531356505360400164160ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: ast_smt2_pp.cpp Abstract: Pretty printer of AST formulas using SMT2 format. This printer is more expensive than the one in ast_smt_pp.h, but is supposed to generated a "prettier" and SMT2 compliant output. Author: Leonardo de Moura (leonardo) Revision History: --*/ #ifndef AST_SMT2_PP_H_ #define AST_SMT2_PP_H_ #include "ast/format.h" #include "util/params.h" #include "ast/arith_decl_plugin.h" #include "ast/bv_decl_plugin.h" #include "ast/array_decl_plugin.h" #include "ast/fpa_decl_plugin.h" #include "ast/dl_decl_plugin.h" #include "ast/seq_decl_plugin.h" #include "ast/datatype_decl_plugin.h" #include "ast/ast_smt_pp.h" #include "util/smt2_util.h" class smt2_pp_environment { protected: mutable smt_renaming m_renaming; format_ns::format * mk_neg(format_ns::format * f) const; format_ns::format * mk_float(rational const & val) const; bool is_indexed_fdecl(func_decl * f) const; format_ns::format * pp_fdecl_params(format_ns::format * fname, func_decl * f); bool is_sort_param(func_decl * f) const; format_ns::format * pp_as(format_ns::format * fname, sort * s); format_ns::format * pp_signature(format_ns::format * f_name, func_decl * f); public: virtual ~smt2_pp_environment() {} virtual ast_manager & get_manager() const = 0; virtual arith_util & get_autil() = 0; virtual bv_util & get_bvutil() = 0; virtual array_util & get_arutil() = 0; virtual fpa_util & get_futil() = 0; virtual seq_util & get_sutil() = 0; virtual datalog::dl_decl_util& get_dlutil() = 0; virtual datatype_util& get_dtutil() = 0; virtual bool uses(symbol const & s) const = 0; virtual format_ns::format * pp_fdecl(func_decl * f, unsigned & len); virtual format_ns::format * pp_bv_literal(app * t, bool use_bv_lits, bool bv_neg); virtual format_ns::format * pp_arith_literal(app * t, bool decimal, unsigned prec); virtual format_ns::format * pp_float_literal(app * t, bool use_bv_lits, bool use_float_real_lits); virtual format_ns::format * pp_datalog_literal(app * t); virtual format_ns::format * pp_string_literal(app * t); virtual format_ns::format * pp_sort(sort * s); virtual format_ns::format * pp_fdecl_ref(func_decl * f); format_ns::format * pp_fdecl_name(symbol const & fname, unsigned & len, bool is_skolem) const; format_ns::format * pp_fdecl_name(func_decl * f, unsigned & len) const; }; /** \brief Simple environment that ignores name clashes. Useful for debugging code. */ class smt2_pp_environment_dbg : public smt2_pp_environment { ast_manager & m_manager; arith_util m_autil; bv_util m_bvutil; array_util m_arutil; fpa_util m_futil; seq_util m_sutil; datatype_util m_dtutil; datalog::dl_decl_util m_dlutil; public: smt2_pp_environment_dbg(ast_manager & m):m_manager(m), m_autil(m), m_bvutil(m), m_arutil(m), m_futil(m), m_sutil(m), m_dtutil(m), m_dlutil(m) {} ast_manager & get_manager() const override { return m_manager; } arith_util & get_autil() override { return m_autil; } bv_util & get_bvutil() override { return m_bvutil; } seq_util & get_sutil() override { return m_sutil; } array_util & get_arutil() override { return m_arutil; } fpa_util & get_futil() override { return m_futil; } datalog::dl_decl_util& get_dlutil() override { return m_dlutil; } datatype_util& get_dtutil() override { return m_dtutil; } bool uses(symbol const & s) const override { return false; } }; void mk_smt2_format(expr * n, smt2_pp_environment & env, params_ref const & p, unsigned num_vars, char const * var_prefix, format_ns::format_ref & r, sbuffer & var_names); void mk_smt2_format(sort * s, smt2_pp_environment & env, params_ref const & p, format_ns::format_ref & r); void mk_smt2_format(func_decl * f, smt2_pp_environment & env, params_ref const & p, format_ns::format_ref & r, char const* cmd); std::ostream & ast_smt2_pp(std::ostream & out, expr * n, smt2_pp_environment & env, params_ref const & p = params_ref(), unsigned indent = 0, unsigned num_vars = 0, char const * var_prefix = nullptr); std::ostream & ast_smt2_pp(std::ostream & out, sort * s, smt2_pp_environment & env, params_ref const & p = params_ref(), unsigned indent = 0); std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, smt2_pp_environment & env, params_ref const & p = params_ref(), unsigned indent = 0, char const* cmd = "declare-fun"); std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, expr* e, smt2_pp_environment & env, params_ref const & p = params_ref(), unsigned indent = 0, char const* cmd = "define-fun"); std::ostream & ast_smt2_pp(std::ostream & out, symbol const& s, bool is_skolem, smt2_pp_environment & env, params_ref const& p = params_ref()); std::ostream & ast_smt2_pp_recdefs(std::ostream & out, vector> const& funs, smt2_pp_environment & env, params_ref const & p = params_ref()); /** \brief Internal wrapper (for debugging purposes only) */ struct mk_ismt2_pp { ast * m_ast; ast_manager & m_manager; params_ref m_empty; params_ref const & m_params; unsigned m_indent; unsigned m_num_vars; char const * m_var_prefix; mk_ismt2_pp(ast * t, ast_manager & m, params_ref const & p, unsigned indent = 0, unsigned num_vars = 0, char const * var_prefix = nullptr); mk_ismt2_pp(ast * t, ast_manager & m, unsigned indent = 0, unsigned num_vars = 0, char const * var_prefix = nullptr); }; std::ostream& operator<<(std::ostream& out, mk_ismt2_pp const & p); std::ostream& operator<<(std::ostream& out, expr_ref const& e); std::ostream& operator<<(std::ostream& out, app_ref const& e); std::ostream& operator<<(std::ostream& out, func_decl_ref const& e); std::ostream& operator<<(std::ostream& out, sort_ref const& e); std::ostream& operator<<(std::ostream& out, expr_ref_vector const& e); std::ostream& operator<<(std::ostream& out, app_ref_vector const& e); std::ostream& operator<<(std::ostream& out, func_decl_ref_vector const& e); std::ostream& operator<<(std::ostream& out, sort_ref_vector const& e); #endif z3-z3-4.8.7/src/ast/ast_smt_pp.cpp000066400000000000000000000705651356505360400166770ustar00rootroot00000000000000/*++ Copyright (c) 2008 Microsoft Corporation Module Name: ast_smt_pp.cpp Abstract: Pretty printer of AST formulas as SMT benchmarks. Author: Michal Moskal (micmo) 2008-04-09. Nikolaj Bjorner (nbjorner) Revision History: --*/ #include #include #include "util/vector.h" #include "util/smt2_util.h" #include "ast/ast_smt_pp.h" #include "ast/ast_smt2_pp.h" #include "ast/arith_decl_plugin.h" #include "ast/bv_decl_plugin.h" #include "ast/array_decl_plugin.h" #include "ast/datatype_decl_plugin.h" #include "ast/seq_decl_plugin.h" #include "ast/fpa_decl_plugin.h" #include "ast/for_each_ast.h" #include "ast/decl_collector.h" // --------------------------------------- // smt_renaming static const char m_predef_names[][8] = { "=", ">=", "<=", "+", "-", "*", ">", "<", "!=", "or", "and", "implies", "not", "iff", "xor", "true", "false", "forall", "exists", "let", "flet" }; symbol smt_renaming::fix_symbol(symbol s, int k) { std::ostringstream buffer; char const * data = s.is_numerical() ? "" : s.bare_str(); if (k == 0 && data && *data) { if (s.is_numerical()) { return s; } if (is_special(data)) { return s; } if (all_is_legal(data)) { return s; } } if (s.is_numerical()) { buffer << s << k; return symbol(buffer.str().c_str()); } if (!s.bare_str()) { buffer << "null"; } else if (is_smt2_quoted_symbol(s)) { buffer << mk_smt2_quoted_symbol(s); } else { buffer << s; } if (k > 0) { buffer << "!" << k; } return symbol(buffer.str().c_str()); } bool smt_renaming::is_legal(char c) { return c == '.' || c == '_' || c == '\'' || c == '?' || c == '!' || isalnum(c); } bool smt_renaming::is_special(char const* s) { if (!s) return false; if (s[0] != '|') return false; ++s; while (*s) { if (s[0] == '|') { return (0 == s[1]); } ++s; } return false; } bool smt_renaming::is_numerical(char const* s) { while (*s) { if (!isdigit(*s)) { return false; } ++s; } return true; } bool smt_renaming::all_is_legal(char const* s) { if (!s) return false; if (is_numerical(s)) return false; while (*s) { if (!is_legal(*s)) return false; ++s; } return true; } smt_renaming::smt_renaming() { for (unsigned i = 0; i < Z3_ARRAYSIZE(m_predef_names); ++i) { symbol s(m_predef_names[i]); m_translate.insert(s, sym_b(s, false)); m_rev_translate.insert(s, s); } } // Ensure that symbols that are used both with skolems and non-skolems are named apart. symbol smt_renaming::get_symbol(symbol s0, bool is_skolem) { sym_b sb; symbol s; if (m_translate.find(s0, sb)) { if (is_skolem == sb.is_skolem) return sb.name; if (sb.name_aux != symbol::null) { return sb.name_aux; } int k = 0; symbol s1; do { s = fix_symbol(s0, k++); } while (s == s0 || (m_rev_translate.find(s, s1) && s1 != s0)); m_rev_translate.insert(s, s0); sb.name_aux = s; m_translate.insert(s, sb); return s; } int k = 0; do { s = fix_symbol(s0, k++); } while (m_rev_translate.contains(s)); m_translate.insert(s0, sym_b(s, is_skolem)); m_rev_translate.insert(s, s0); return s; } // --------------------------------------- // smt_printer class smt_printer { std::ostream& m_out; ast_manager& m_manager; ptr_vector& m_qlists; smt_renaming& m_renaming; unsigned m_indent; unsigned m_num_var_names; char const* const* m_var_names; ptr_vector m_todo; ast_mark m_mark; unsigned m_num_lets; arith_util m_autil; bv_util m_bvutil; seq_util m_sutil; fpa_util m_futil; family_id m_basic_fid; family_id m_bv_fid; family_id m_arith_fid; family_id m_array_fid; family_id m_dt_fid; family_id m_fpa_fid; family_id m_label_fid; symbol m_logic; symbol m_AUFLIRA; bool m_no_lets; bool m_simplify_implies; expr* m_top; bool is_bool(sort* s) { return m_basic_fid == s->get_family_id() && s->get_decl_kind() == BOOL_SORT; } bool is_bool(expr* e) { return is_bool(m_manager.get_sort(e)); } bool is_proof(sort* s) { return m_basic_fid == s->get_family_id() && s->get_decl_kind() == PROOF_SORT; } bool is_proof(expr* e) { return is_proof(m_manager.get_sort(e)); } void pp_id(expr* n) { m_out << (is_bool(n)?"$x":(is_proof(n)?"@x":"?x")) << n->get_id(); } void pp_decl(func_decl* d) { symbol sym = m_renaming.get_symbol(d->get_name(), d->is_skolem()); if (d->get_family_id() == m_dt_fid) { datatype_util util(m_manager); if (util.is_recognizer(d)) { visit_params(false, sym, d->get_num_parameters(), d->get_parameters()); } else { m_out << sym; } } else if (m_manager.is_ite(d)) { m_out << "ite"; } else if (m_manager.is_implies(d)) { m_out << "=>"; } else if (is_decl_of(d, m_arith_fid, OP_UMINUS)) { m_out << "-"; } else { visit_params(false, sym, d->get_num_parameters(), d->get_parameters()); } m_out << " "; } bool is_sort_param(unsigned num_params, parameter const* params) { return num_params == 1 && params[0].is_ast() && is_sort(params[0].get_ast()); } void visit_params(bool is_sort_symbol, symbol const& sym, unsigned num_params, parameter const* params) { if (0 == num_params) { m_out << sym; return; } if (is_sort_symbol && sym == symbol("String")) { m_out << "String"; return; } if (is_sort_symbol && sym != symbol("BitVec") && sym != symbol("FloatingPoint") && sym != symbol("RoundingMode")) { m_out << "(" << sym << " "; } else if (!is_sort_symbol && is_sort_param(num_params, params)) { m_out << "(as " << sym << " "; } else { m_out << "(_ " << sym << " "; } for (unsigned i = 0; i < num_params; ++i) { parameter const& p = params[i]; if (p.is_ast()) { if (is_sort(p.get_ast())) { visit_sort(to_sort(p.get_ast())); } else if (is_expr(p.get_ast())) { pp_expr(to_expr(p.get_ast())); } else if (is_func_decl(p.get_ast())) { pp_decl(to_func_decl(p.get_ast())); } else { m_out << "#" << p.get_ast()->get_id(); } } else { m_out << p; } if (i + 1 < num_params) { m_out << " "; } } m_out << ")"; } bool is_auflira() const { return m_logic == m_AUFLIRA; } void visit_sort(sort* s, bool bool2int = false) { symbol sym; if (s->is_sort_of(m_bv_fid, BV_SORT)) { sym = symbol("BitVec"); } else if (s->is_sort_of(m_arith_fid, REAL_SORT)) { sym = s->get_name(); } else if (m_manager.is_bool(s)) { sym = symbol("Bool"); } else if (s->is_sort_of(m_arith_fid, INT_SORT)) { sym = s->get_name(); } else if (s->is_sort_of(m_array_fid, ARRAY_SORT)) { sym = "Array"; } else if (s->is_sort_of(m_dt_fid, DATATYPE_SORT)) { datatype_util util(m_manager); unsigned num_sorts = util.get_datatype_num_parameter_sorts(s); if (num_sorts > 0) { m_out << "("; } m_out << m_renaming.get_symbol(s->get_name(), false); if (num_sorts > 0) { for (unsigned i = 0; i < num_sorts; ++i) { m_out << " "; visit_sort(util.get_datatype_parameter_sort(s, i)); } m_out << ")"; } return; } else { sym = m_renaming.get_symbol(s->get_name(), false); } visit_params(true, sym, s->get_num_parameters(), s->get_parameters()); } void display_rational(rational const & r, bool is_int) { bool d = !is_int; if (r.is_int()) { m_out << r << (d ? ".0" : ""); } else { m_out << "(/ " << numerator(r) << (d ? ".0" : "") << " " << denominator(r) << (d ? ".0" : "") << ")"; } } void pp_arg(expr *arg, app *parent) { pp_marked_expr(arg); } void visit_app(app* n) { rational val; bool is_int, pos; buffer names; unsigned bv_size; zstring s; unsigned num_args = n->get_num_args(); func_decl* decl = n->get_decl(); scoped_mpf float_val(m_futil.fm()); if (m_autil.is_numeral(n, val, is_int)) { if (val.is_neg()) { val.neg(); m_out << "(- "; display_rational(val, is_int); m_out << ")"; } else { display_rational(val, is_int); } } else if (m_sutil.str.is_string(n, s)) { std::string encs = s.encode(); m_out << "\""; for (unsigned i = 0; i < encs.length(); ++i) { if (encs[i] == '\"') { m_out << "\"\""; } else { m_out << encs[i]; } } m_out << "\""; } else if (m_bvutil.is_numeral(n, val, bv_size)) { m_out << "(_ bv" << val << " " << bv_size << ")"; } else if (m_futil.is_numeral(n, float_val)) { m_out << mk_ismt2_pp(n, m_manager); } else if (m_bvutil.is_bit2bool(n)) { unsigned bit = n->get_decl()->get_parameter(0).get_int(); m_out << "(= ((_ extract " << bit << " " << bit << ") "; pp_marked_expr(n->get_arg(0)); m_out << ") (_ bv1 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.empty()) { m_out << "(! true :lblpos " << m_renaming.get_symbol(names[0], false) << ")"; } else if (num_args == 0) { if (decl->private_parameters()) { m_out << m_renaming.get_symbol(decl->get_name(), decl->is_skolem()); } else { symbol sym = m_renaming.get_symbol(decl->get_name(), decl->is_skolem()); visit_params(false, sym, decl->get_num_parameters(), decl->get_parameters()); } } else if (num_args == 1 && n->get_family_id() == m_label_fid) { expr* ch = n->get_arg(0); pp_marked_expr(ch); } else if (m_simplify_implies && m_manager.is_implies(decl) && m_manager.is_implies(n->get_arg(1))) { expr *curr = n; expr *arg; m_out << "(=> (and"; while (m_manager.is_implies(curr)) { arg = to_app(curr)->get_arg(0); m_out << " "; pp_arg(arg, n); curr = to_app(curr)->get_arg(1); } m_out << ") "; pp_arg(curr, n); m_out << ")"; } else if (m_manager.is_distinct(decl)) { ptr_vector args(num_args, n->get_args()); unsigned idx = 0; m_out << "(and"; while (true) { while (idx < args.size() && !args[idx]) idx++; if (idx >= args.size()) break; sort * s = m_manager.get_sort(args[idx]); unsigned next = idx + 1; // check if there is only a single one while (next < args.size() && (!args[next] || m_manager.get_sort(args[next]) != s)) next++; if (next >= args.size()) { args[idx] = 0; // if so, skip it continue; } // otherwise print all of the relevant sort m_out << " (distinct"; for (unsigned i = idx; i < args.size(); ++i) { if (args[i] && s == m_manager.get_sort(args[i])) { m_out << " "; pp_marked_expr(args[i]); args[i] = 0; } } m_out << ")"; } m_out << " true)"; } else { m_out << "("; pp_decl(decl); for (unsigned i = 0; i < num_args; ++i) { pp_arg(n->get_arg(i), n); if (i + 1 < num_args) { m_out << " "; } } m_out << ")"; } } void print_no_lets(expr *e) { smt_printer p(m_out, m_manager, m_qlists, m_renaming, m_logic, true, m_simplify_implies, m_indent, m_num_var_names, m_var_names); p(e); } void print_bound(symbol const& name) { m_out << name; } void visit_quantifier(quantifier* q) { m_qlists.push_back(q); m_out << "("; switch (q->get_kind()) { case forall_k: m_out << "forall "; break; case exists_k: m_out << "exists "; break; case lambda_k: m_out << "lambda "; break; } m_out << "("; for (unsigned i = 0; i < q->get_num_decls(); ++i) { sort* s = q->get_decl_sort(i); m_out << "("; print_bound(m_renaming.get_symbol(q->get_decl_name(i), false)); m_out << " "; visit_sort(s, true); m_out << ") "; } m_out << ")"; if ((q->get_num_patterns() > 0 || q->get_qid() != symbol::null)) { m_out << "(! "; } { smt_printer p(m_out, m_manager, m_qlists, m_renaming, m_logic, false, m_simplify_implies, m_indent, m_num_var_names, m_var_names); p(q->get_expr()); } for (unsigned i = 0; i < q->get_num_patterns(); ++i) { app *pat = reinterpret_cast (q->get_pattern(i)); if (pat->get_num_args() == 1 && is_app(pat->get_arg(0))) { app *app = to_app(pat->get_arg(0)); if (app->get_num_args() == 1 && app->get_decl()->get_name().str() == "sk_hack") { /* m_out << " :ex_act { "; print_no_lets(app->get_arg(0)); m_out << "}"; */ continue; } } m_out << " :pattern ( "; for (unsigned j = 0; j < pat->get_num_args(); ++j) { print_no_lets(pat->get_arg(j)); m_out << " "; } m_out << ")"; } if (q->get_qid() != symbol::null) m_out << " :qid " << q->get_qid(); if ((q->get_num_patterns() > 0 || q->get_qid() != symbol::null)) { m_out << ")"; } m_out << ")"; newline(); m_qlists.pop_back(); } void newline() { unsigned i = m_indent; m_out << "\n"; while (i > 0) { m_out << " "; --i; } } void visit_var(var* v) { unsigned idx = v->get_idx(); for (unsigned i = m_qlists.size(); ; --i) { if (i == 0) { break; } quantifier* q = m_qlists[i-1]; unsigned num_decls = q->get_num_decls(); if (idx < num_decls) { unsigned offs = num_decls-idx-1; symbol name = m_renaming.get_symbol(q->get_decl_name(offs), false); print_bound(name); return; } idx -= num_decls; } if (idx < m_num_var_names) { m_out << m_var_names[m_num_var_names - idx - 1]; } else { m_out << "?" << idx; } } void pp_marked_expr(expr* n) { if (m_mark.is_marked(n)) { pp_id(n); } else { pp_expr(n); } } void pp_expr(expr* n) { switch(n->get_kind()) { case AST_QUANTIFIER: visit_quantifier(to_quantifier(n)); break; case AST_APP: visit_app(to_app(n)); break; case AST_VAR: visit_var(to_var(n)); break; default: UNREACHABLE(); } } void visit_expr(expr* n) { m_out << "(let (("; pp_id(n); m_out << " "; pp_expr(n); m_out << ")"; m_out << ")"; newline(); } bool is_unit(expr* n) { if (n->get_ref_count() <= 2 && is_small(n)) { return true; } if (n == m_top) { return true; } switch(n->get_kind()) { case AST_VAR: return true; case AST_APP: return to_app(n)->get_num_args() == 0; default: return false; } } static const unsigned m_line_length = 80; bool is_small(expr* n) { unsigned sz = 0; return is_small(n, sz); } bool is_small(expr* n, unsigned& sz) { if (sz > m_line_length) { return false; } if (m_mark.is_marked(n)) { sz += 5; return sz <= m_line_length; } switch(n->get_kind()) { case AST_QUANTIFIER: return false; case AST_VAR: sz += 5; return sz <= m_line_length; case AST_APP: { app* a = to_app(n); func_decl* d = a->get_decl(); symbol const& s = d->get_name(); if (s.is_numerical()) { sz += 4; } if (s.is_numerical()) { sz += 7; } else if (s.bare_str()) { sz += 3 + static_cast(strlen(s.bare_str())); } for (unsigned i = 0; i < a->get_num_args() && sz <= m_line_length; ++i) { sz += 1; if (!is_small(a->get_arg(i), sz)) { return false; } } return sz <= m_line_length; } default: return false; } } bool visit_children(expr* n) { unsigned todo_size = m_todo.size(); switch(n->get_kind()) { case AST_QUANTIFIER: case AST_VAR: break; case AST_APP: { app* a = to_app(n); for (unsigned i = 0; i < a->get_num_args(); ++i) { expr* ch = a->get_arg(i); if (!is_unit(ch) && !m_mark.is_marked(ch)) { m_todo.push_back(ch); } } break; } default: UNREACHABLE(); break; } bool all_visited = todo_size == m_todo.size(); return all_visited; } public: smt_printer(std::ostream& out, ast_manager& m, ptr_vector& ql, smt_renaming& rn, symbol logic, bool no_lets, bool simplify_implies, unsigned indent, unsigned num_var_names = 0, char const* const* var_names = nullptr) : m_out(out), m_manager(m), m_qlists(ql), m_renaming(rn), m_indent(indent), m_num_var_names(num_var_names), m_var_names(var_names), m_num_lets(0), m_autil(m), m_bvutil(m), m_sutil(m), m_futil(m), m_logic(logic), m_AUFLIRA("AUFLIRA"), // It's much easier to read those testcases with that. m_no_lets(no_lets), m_simplify_implies(simplify_implies) { m_basic_fid = m.get_basic_family_id(); m_label_fid = m.mk_family_id("label"); m_bv_fid = m.mk_family_id("bv"); m_arith_fid = m.mk_family_id("arith"); m_array_fid = m.mk_family_id("array"); m_dt_fid = m.mk_family_id("datatype"); m_fpa_fid = m.mk_family_id("fpa"); } void operator()(expr* n) { m_top = n; if (!m_no_lets) { switch(n->get_kind()) { case AST_APP: for (unsigned i = 0; i < to_app(n)->get_num_args(); ++i) { m_todo.push_back(to_app(n)->get_arg(i)); } break; // Don't do this for quantifiers -- they need to have the body be // visited when the m_qlist contains the relevant quantifier. default: break; } } while (!m_todo.empty()) { expr* m = m_todo.back(); if (m_mark.is_marked(m)) { m_todo.pop_back(); } else if (is_unit(m)) { m_todo.pop_back(); } else if (visit_children(m)) { m_todo.pop_back(); m_mark.mark(m, true); visit_expr(m); ++m_num_lets; } } pp_marked_expr(n); for (unsigned i = 0; i < m_num_lets; ++i) { m_out << ")"; } m_mark.reset(); m_num_lets = 0; m_top = nullptr; } void pp_dt(ast_mark& mark, sort* s) { datatype_util util(m_manager); SASSERT(util.is_datatype(s)); sort_ref_vector ps(m_manager); ptr_vector defs; util.get_defs(s, defs); for (datatype::def* d : defs) { sort_ref sr = d->instantiate(ps); if (mark.is_marked(sr)) return; // already processed mark.mark(sr, true); } m_out << "(declare-datatypes ("; bool first_def = true; for (datatype::def* d : defs) { if (!first_def) m_out << "\n "; else first_def = false; m_out << "(" << d->name() << " " << d->params().size() << ")"; } m_out << ") ("; bool first_sort = true; for (datatype::def* d : defs) { if (!first_sort) m_out << "\n "; else first_sort = false; if (!d->params().empty()) { m_out << "(par ("; bool first_param = true; for (sort* s : d->params()) { if (!first_param) m_out << " "; else first_param = false; visit_sort(s); } m_out << ")"; } m_out << "("; bool first_constr = true; for (datatype::constructor* f : *d) { if (!first_constr) m_out << " "; else first_constr = false; m_out << "("; m_out << m_renaming.get_symbol(f->name(), false); for (datatype::accessor* a : *f) { m_out << " (" << m_renaming.get_symbol(a->name(), false) << " "; visit_sort(a->range()); m_out << ")"; } m_out << ")"; } if (!d->params().empty()) { m_out << ")"; } m_out << ")"; } m_out << "))"; newline(); } void pp_sort_decl(ast_mark& mark, sort* s) { if (mark.is_marked(s)) { return; } if (s->is_sort_of(m_dt_fid, DATATYPE_SORT)) { pp_dt(mark, s); } else { m_out << "(declare-sort "; visit_sort(s); m_out << " 0)"; newline(); } mark.mark(s, true); } void operator()(sort* s) { ast_mark mark; pp_sort_decl(mark, s); } void operator()(func_decl* d) { m_out << "(declare-fun "; pp_decl(d); m_out << "("; for (unsigned i = 0; i < d->get_arity(); ++i) { if (i > 0) m_out << " "; visit_sort(d->get_domain(i), true); } m_out << ") "; visit_sort(d->get_range()); m_out << ")"; } void visit_pred(func_decl* d) { m_out << "("; pp_decl(d); for (unsigned i = 0; i < d->get_arity(); ++i) { m_out << " "; visit_sort(d->get_domain(i), true); } m_out << ")"; } }; // --------------------------------------- // ast_smt_pp: ast_smt_pp::ast_smt_pp(ast_manager& m): m_manager(m), m_assumptions(m), m_assumptions_star(m), m_benchmark_name(), m_source_info(), m_status("unknown"), m_category(), m_logic(), m_dt_fid(m.mk_family_id("datatype")), m_is_declared(&m_is_declared_default), m_simplify_implies(true) {} void ast_smt_pp::display_expr_smt2(std::ostream& strm, expr* n, unsigned indent, unsigned num_var_names, char const* const* var_names) { ptr_vector ql; smt_renaming rn; smt_printer p(strm, m_manager, ql, rn, m_logic, false, m_simplify_implies, indent, num_var_names, var_names); p(n); } void ast_smt_pp::display_ast_smt2(std::ostream& strm, ast* a, unsigned indent, unsigned num_var_names, char const* const* var_names) { ptr_vector ql; smt_renaming rn; smt_printer p(strm, m_manager, ql, rn, m_logic, false, m_simplify_implies, indent, num_var_names, var_names); if (is_expr(a)) { p(to_expr(a)); } else if (is_func_decl(a)) { p(to_func_decl(a)); } else { SASSERT(is_sort(a)); p(to_sort(a)); } } void ast_smt_pp::display_smt2(std::ostream& strm, expr* n) { ptr_vector ql; ast_manager& m = m_manager; decl_collector decls(m); smt_renaming rn; for (expr* a : m_assumptions) { decls.visit(a); } for (expr* a : m_assumptions_star) { decls.visit(a); } decls.visit(n); if (m.is_proof(n)) { strm << "("; } if (m_benchmark_name != symbol::null) { strm << "; " << m_benchmark_name << "\n"; } if (m_source_info != symbol::null && m_source_info != symbol("")) { strm << "; :source { " << m_source_info << " }\n"; } if (m.is_bool(n)) { strm << "(set-info :status " << m_status << ")\n"; } if (m_category != symbol::null && m_category != symbol("")) { strm << "; :category { " << m_category << " }\n"; } if (m_logic != symbol::null && m_logic != symbol("")) { strm << "(set-logic " << m_logic << ")\n"; } if (!m_attributes.empty()) { strm << "; " << m_attributes; } #if 0 decls.display_decls(strm); #else decls.order_deps(0); ast_mark sort_mark; for (sort* s : decls.get_sorts()) { if (!(*m_is_declared)(s)) { smt_printer p(strm, m, ql, rn, m_logic, true, true, m_simplify_implies, 0); p.pp_sort_decl(sort_mark, s); } } for (unsigned i = 0; i < decls.get_num_decls(); ++i) { func_decl* d = decls.get_func_decls()[i]; if (!(*m_is_declared)(d)) { smt_printer p(strm, m, ql, rn, m_logic, true, true, m_simplify_implies, 0); p(d); strm << "\n"; } } #endif for (expr* a : m_assumptions) { smt_printer p(strm, m, ql, rn, m_logic, false, true, m_simplify_implies, 1); strm << "(assert\n "; p(a); strm << ")\n"; } for (expr* a : m_assumptions_star) { smt_printer p(strm, m, ql, rn, m_logic, false, true, m_simplify_implies, 1); strm << "(assert\n "; p(a); strm << ")\n"; } smt_printer p(strm, m, ql, rn, m_logic, false, true, m_simplify_implies, 0); if (m.is_bool(n)) { if (!m.is_true(n)) { strm << "(assert\n "; p(n); strm << ")\n"; } strm << "(check-sat)\n"; } else if (m.is_proof(n)) { strm << "(proof\n"; p(n); strm << "))\n"; } else { p(n); } } z3-z3-4.8.7/src/ast/ast_smt_pp.h000066400000000000000000000064761356505360400163440ustar00rootroot00000000000000/*++ Copyright (c) 2008 Microsoft Corporation Module Name: ast_smt_pp.h Abstract: Pretty printer of AST formulas as SMT benchmarks. Author: Nikolaj Bjorner 2008-04-09. Revision History: --*/ #ifndef AST_SMT_PP_H_ #define AST_SMT_PP_H_ #include "ast/ast.h" #include #include "util/map.h" class smt_renaming { struct sym_b { symbol name; bool is_skolem; symbol name_aux; sym_b(symbol n, bool s): name(n), is_skolem(s) {} sym_b():name(),is_skolem(false) {}}; typedef map symbol2symbol; typedef map symbol2sym_b; symbol2sym_b m_translate; symbol2symbol m_rev_translate; symbol fix_symbol(symbol s, int k); bool is_legal(char c); bool is_special(char const* s); bool is_numerical(char const* s); bool all_is_legal(char const* s); public: smt_renaming(); symbol get_symbol(symbol s0, bool is_skolem = false); symbol operator()(symbol const & s, bool is_skolem = false) { return get_symbol(s, is_skolem); } }; class ast_smt_pp { public: class is_declared { public: virtual bool operator()(func_decl* d) const { return false; } virtual bool operator()(sort* s) const { return false; } }; private: ast_manager& m_manager; expr_ref_vector m_assumptions; expr_ref_vector m_assumptions_star; symbol m_benchmark_name; symbol m_source_info; symbol m_status; symbol m_category; symbol m_logic; std::string m_attributes; family_id m_dt_fid; is_declared m_is_declared_default; is_declared* m_is_declared; bool m_simplify_implies; public: ast_smt_pp(ast_manager& m); void set_benchmark_name(const char* bn) { if (bn) m_benchmark_name = bn; } void set_source_info(const char* si) { if (si) m_source_info = si; } void set_status(const char* s) { if (s) m_status = s; } void set_category(const char* c) { if (c) m_category = c; } void set_logic(symbol const& l) { m_logic = l; } void add_attributes(const char* s) { if (s) m_attributes += s; } void add_assumption(expr* n) { m_assumptions.push_back(n); } void add_assumption_star(expr* n) { m_assumptions_star.push_back(n); } void set_simplify_implies(bool f) { m_simplify_implies = f; } void set_is_declared(is_declared* id) { m_is_declared = id; } void display_smt2(std::ostream& strm, expr* n); void display_expr_smt2(std::ostream& strm, expr* n, unsigned indent = 0, unsigned num_var_names = 0, char const* const* var_names = nullptr); void display_ast_smt2(std::ostream& strm, ast* n, unsigned indent = 0, unsigned num_var_names = 0, char const* const* var_names = nullptr); }; struct mk_smt_pp { ast* m_ast; ast_manager& m_manager; unsigned m_indent; unsigned m_num_var_names; char const* const* m_var_names; mk_smt_pp(ast* e, ast_manager & m, unsigned indent = 0, unsigned num_var_names = 0, char const* const* var_names = nullptr) : m_ast(e), m_manager(m), m_indent(indent), m_num_var_names(num_var_names), m_var_names(var_names) {} }; inline std::ostream& operator<<(std::ostream& out, const mk_smt_pp & p) { ast_smt_pp pp(p.m_manager); pp.display_ast_smt2(out, p.m_ast, p.m_indent, p.m_num_var_names, p.m_var_names); return out; } #endif z3-z3-4.8.7/src/ast/ast_trail.h000066400000000000000000000025321356505360400161420ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: ast_trail.h Abstract: Author: Leonardo de Moura (leonardo) 2008-06-02. Revision History: Extracted AST specific features from trail.h nbjorner 2014-9-28 --*/ #ifndef AST_TRAIL_H_ #define AST_TRAIL_H_ #include "ast/ast.h" #include "util/trail.h" template class ast2ast_trailmap { ref_vector m_domain; ref_vector m_range; obj_map m_map; public: ast2ast_trailmap(ast_manager& m): m_domain(m), m_range(m), m_map() {} bool find(S* s, T*& t) { return m_map.find(s,t); } void insert(S* s, T* t) { SASSERT(!m_map.contains(s)); m_domain.push_back(s); m_range.push_back(t); m_map.insert(s,t); } void pop() { SASSERT(!m_domain.empty()); m_map.remove(m_domain.back()); m_domain.pop_back(); m_range.pop_back(); } }; template class ast2ast_trail : public trail { ast2ast_trailmap& m_map; public: ast2ast_trail(ast2ast_trailmap& m, S* s, T* t) : m_map(m) { m.insert(s,t); } void undo(Ctx& ctx) override { m_map.pop(); } }; #endif /* AST_TRAIL_H_ */ z3-z3-4.8.7/src/ast/ast_translation.cpp000066400000000000000000000321431356505360400177210ustar00rootroot00000000000000/*++ Copyright (c) 2008 Microsoft Corporation Module Name: ast_translation.cpp Abstract: AST translation functions Author: Christoph Wintersteiger (t-cwinte) 2008-11-20 Revision History: --*/ #include "ast/arith_decl_plugin.h" #include "ast/bv_decl_plugin.h" #include "ast/datatype_decl_plugin.h" #include "ast/array_decl_plugin.h" #include "ast/format.h" #include "ast/ast_translation.h" #include "ast/ast_ll_pp.h" #include "ast/ast_pp.h" ast_translation::~ast_translation() { reset_cache(); } void ast_translation::cleanup() { reset_cache(); m_cache.finalize(); m_result_stack.finalize(); m_frame_stack.finalize(); m_extra_children_stack.finalize(); } void ast_translation::reset_cache() { for (auto & kv : m_cache) { m_from_manager.dec_ref(kv.m_key); m_to_manager.dec_ref(kv.m_value); } m_cache.reset(); } void ast_translation::cache(ast * s, ast * t) { SASSERT(!m_cache.contains(s)); if (s->get_ref_count() > 1) { m_from_manager.inc_ref(s); m_to_manager.inc_ref(t); m_cache.insert(s, t); ++m_insert_count; } } void ast_translation::collect_decl_extra_children(decl * d) { unsigned num_params = d->get_num_parameters(); for (unsigned i = 0; i < num_params; i++) { parameter const & p = d->get_parameter(i); if (p.is_ast()) m_extra_children_stack.push_back(p.get_ast()); } } void ast_translation::push_frame(ast * n) { m_frame_stack.push_back(frame(n, 0, m_extra_children_stack.size(), m_result_stack.size())); switch (n->get_kind()) { case AST_SORT: case AST_FUNC_DECL: collect_decl_extra_children(to_decl(n)); break; default: break; } } bool ast_translation::visit(ast * n) { if (n->get_ref_count() > 1) { ast * r; if (m_cache.find(n, r)) { m_result_stack.push_back(r); ++m_hit_count; return true; } else { ++m_miss_count; } } push_frame(n); return false; } void ast_translation::copy_params(decl * d, unsigned rpos, buffer & ps) { unsigned num = d->get_num_parameters(); unsigned j = rpos; for (unsigned i = 0; i < num; i++) { parameter const & p = d->get_parameter(i); if (p.is_ast()) { ps.push_back(parameter(m_result_stack[j])); j++; } else if (p.is_external()) { SASSERT(d->get_family_id() != null_family_id); decl_plugin & from_plugin = *(m_from_manager.get_plugin(d->get_family_id())); decl_plugin & to_plugin = *(m_to_manager.get_plugin(d->get_family_id())); ps.push_back(from_plugin.translate(p, to_plugin)); } else { ps.push_back(p); } } } void ast_translation::mk_sort(sort * s, frame & fr) { sort_info * si = s->get_info(); sort * new_s; if (si == nullptr) { // TODO: investigate: this branch is probably unreachable. // It became unreachable after we started using mk_uninterpreted_sort for creating uninterpreted sorts, // and mk_uninterpreted_sort actually creates a user_sort. new_s = m_to_manager.mk_uninterpreted_sort(s->get_name()); SASSERT(m_result_stack.size() == fr.m_rpos); } else { buffer ps; copy_params(s, fr.m_rpos, ps); new_s = m_to_manager.mk_sort(s->get_name(), sort_info(si->get_family_id(), si->get_decl_kind(), si->get_num_elements(), si->get_num_parameters(), ps.c_ptr(), s->private_parameters())); } m_result_stack.shrink(fr.m_rpos); m_result_stack.push_back(new_s); m_extra_children_stack.shrink(fr.m_cpos); cache(s, new_s); m_frame_stack.pop_back(); } void ast_translation::mk_func_decl(func_decl * f, frame & fr) { func_decl_info * fi = f->get_info(); SASSERT(fr.m_cpos <= m_extra_children_stack.size()); unsigned num_extra = m_extra_children_stack.size() - fr.m_cpos; sort ** new_domain = reinterpret_cast(m_result_stack.c_ptr() + fr.m_rpos + num_extra); sort * new_range = static_cast(m_result_stack.back()); func_decl * new_f; if (fi == nullptr) { new_f = m_to_manager.mk_func_decl(f->get_name(), f->get_arity(), new_domain, new_range); } else { buffer ps; copy_params(f, fr.m_rpos, ps); func_decl_info new_fi(fi->get_family_id(), fi->get_decl_kind(), fi->get_num_parameters(), ps.c_ptr()); new_fi.set_left_associative(fi->is_left_associative()); new_fi.set_right_associative(fi->is_right_associative()); new_fi.set_flat_associative(fi->is_flat_associative()); new_fi.set_commutative(fi->is_commutative()); new_fi.set_chainable(fi->is_chainable()); new_fi.set_pairwise(fi->is_pairwise()); new_fi.set_injective(fi->is_injective()); /// TBD new_fi.set_skolem(fi->is_skolem()); new_fi.set_idempotent(fi->is_idempotent()); new_f = m_to_manager.mk_func_decl(f->get_name(), f->get_arity(), new_domain, new_range, new_fi); } TRACE("ast_translation", tout << f->get_name() << " "; if (fi) tout << *fi; tout << "\n"; tout << "---->\n"; tout << new_f->get_name() << " "; if (new_f->get_info()) tout << *(new_f->get_info()); tout << "\n";); m_result_stack.shrink(fr.m_rpos); m_result_stack.push_back(new_f); m_extra_children_stack.shrink(fr.m_cpos); cache(f, new_f); m_frame_stack.pop_back(); } ast * ast_translation::process(ast const * _n) { if (!_n) return nullptr; SASSERT(m_result_stack.empty()); SASSERT(m_frame_stack.empty()); SASSERT(m_extra_children_stack.empty()); ++m_num_process; if (m_num_process > (1 << 14)) { reset_cache(); m_num_process = 0; } if (!visit(const_cast(_n))) { while (!m_frame_stack.empty()) { loop: ++m_loop_count; frame & fr = m_frame_stack.back(); ast * n = fr.m_n; ast * r; TRACE("ast_translation", tout << mk_ll_pp(n, m_from_manager, false) << "\n";); if (fr.m_idx == 0 && n->get_ref_count() > 1) { if (m_cache.find(n, r)) { SASSERT(m_result_stack.size() == fr.m_rpos); m_result_stack.push_back(r); m_extra_children_stack.shrink(fr.m_cpos); m_frame_stack.pop_back(); TRACE("ast_translation", tout << "hit\n";); m_hit_count++; continue; } else { m_miss_count++; } } switch (n->get_kind()) { case AST_VAR: { if (fr.m_idx == 0) { fr.m_idx = 1; if (!visit(to_var(n)->get_sort())) goto loop; } sort * new_s = to_sort(m_result_stack.back()); var * new_var = m_to_manager.mk_var(to_var(n)->get_idx(), new_s); m_result_stack.pop_back(); m_result_stack.push_back(new_var); cache(n, new_var); m_frame_stack.pop_back(); break; } case AST_APP: { if (fr.m_idx == 0) { fr.m_idx = 1; if (!visit(to_app(n)->get_decl())) goto loop; } unsigned num = to_app(n)->get_num_args(); while (fr.m_idx <= num) { expr * arg = to_app(n)->get_arg(fr.m_idx - 1); fr.m_idx++; if (!visit(arg)) goto loop; } func_decl * new_f = to_func_decl(m_result_stack[fr.m_rpos]); expr ** new_args = reinterpret_cast(m_result_stack.c_ptr() + fr.m_rpos + 1); expr * new_app = m_to_manager.mk_app(new_f, num, new_args); m_result_stack.shrink(fr.m_rpos); m_result_stack.push_back(new_app); cache(n, new_app); m_frame_stack.pop_back(); break; } case AST_QUANTIFIER: { unsigned num_decls = to_quantifier(n)->get_num_decls(); unsigned num = num_decls + to_quantifier(n)->get_num_children(); while (fr.m_idx < num) { ast * child; if (fr.m_idx < num_decls) child = to_quantifier(n)->get_decl_sort(fr.m_idx); else child = to_quantifier(n)->get_child(fr.m_idx - num_decls); fr.m_idx++; if (!visit(child)) goto loop; } symbol const * dnames = to_quantifier(n)->get_decl_names(); sort ** dsorts = reinterpret_cast(m_result_stack.c_ptr() + fr.m_rpos); expr * body = static_cast(m_result_stack[fr.m_rpos + num_decls]); unsigned num_pats = to_quantifier(n)->get_num_patterns(); expr ** pats = reinterpret_cast(m_result_stack.c_ptr() + fr.m_rpos + num_decls + 1); unsigned num_no_pats = to_quantifier(n)->get_num_no_patterns(); expr ** no_pats = pats + num_pats; quantifier * new_q = m_to_manager.mk_quantifier(to_quantifier(n)->get_kind(), num_decls, dsorts, dnames, body, to_quantifier(n)->get_weight(), to_quantifier(n)->get_qid(), to_quantifier(n)->get_skid(), num_pats, pats, num_no_pats, no_pats); m_result_stack.shrink(fr.m_rpos); m_result_stack.push_back(new_q); cache(n, new_q); m_frame_stack.pop_back(); break; } case AST_SORT: { SASSERT(fr.m_cpos <= m_extra_children_stack.size()); unsigned num = m_extra_children_stack.size() - fr.m_cpos; while (fr.m_idx < num) { ast * c = m_extra_children_stack[fr.m_cpos + fr.m_idx]; fr.m_idx++; if (!visit(c)) goto loop; } mk_sort(to_sort(n), fr); break; } case AST_FUNC_DECL: { SASSERT(fr.m_cpos <= m_extra_children_stack.size()); unsigned num_extra = m_extra_children_stack.size() - fr.m_cpos; unsigned arity = to_func_decl(n)->get_arity(); unsigned num = num_extra + arity + 1; while (fr.m_idx < num) { ast * c; if (fr.m_idx < num_extra) c = m_extra_children_stack[fr.m_cpos + fr.m_idx]; else if (fr.m_idx < num_extra + arity) c = to_func_decl(n)->get_domain(fr.m_idx - num_extra); else c = to_func_decl(n)->get_range(); fr.m_idx++; if (!visit(c)) goto loop; } mk_func_decl(to_func_decl(n), fr); break; } default: UNREACHABLE(); break; } } } SASSERT(m_result_stack.size() == 1); ast * r = m_result_stack.back(); m_result_stack.reset(); return r; } expr_dependency * expr_dependency_translation::operator()(expr_dependency * d) { if (d == nullptr) return d; m_buffer.reset(); m_translation.from().linearize(d, m_buffer); unsigned sz = m_buffer.size(); SASSERT(sz >= 1); for (unsigned i = 0; i < sz; i++) { m_buffer[i] = m_translation(m_buffer[i]); } return m_translation.to().mk_join(sz, m_buffer.c_ptr()); } z3-z3-4.8.7/src/ast/ast_translation.h000066400000000000000000000073241356505360400173710ustar00rootroot00000000000000/*++ Copyright (c) 2008 Microsoft Corporation Module Name: ast_translation.h Abstract: AST translation functions Author: Christoph Wintersteiger (t-cwinte) 2008-11-20 Revision History: 2011-05-26: New local translation class. --*/ #ifndef AST_TRANSLATION_H_ #define AST_TRANSLATION_H_ #include "ast/ast.h" class ast_translation { struct frame { ast * m_n; unsigned m_idx; unsigned m_cpos; unsigned m_rpos; frame(ast * n, unsigned idx, unsigned cpos, unsigned rpos):m_n(n), m_idx(idx), m_cpos(cpos), m_rpos(rpos) {} }; ast_manager & m_from_manager; ast_manager & m_to_manager; svector m_frame_stack; ptr_vector m_extra_children_stack; // for sort and func_decl, since they have nested AST in their parameters ptr_vector m_result_stack; obj_map m_cache; unsigned m_loop_count; unsigned m_hit_count; unsigned m_miss_count; unsigned m_insert_count; unsigned m_num_process; void cache(ast * s, ast * t); void collect_decl_extra_children(decl * d); void push_frame(ast * n); bool visit(ast * n); void copy_params(decl * d, unsigned rpos, buffer & ps); void mk_sort(sort * s, frame & fr); void mk_func_decl(func_decl * f, frame & fr); ast * process(ast const * n); public: ast_translation(ast_manager & from, ast_manager & to, bool copy_plugins = true) : m_from_manager(from), m_to_manager(to) { m_loop_count = 0; m_hit_count = 0; m_miss_count = 0; m_insert_count = 0; m_num_process = 0; if (&from != &to) { if (copy_plugins) m_to_manager.copy_families_plugins(m_from_manager); m_to_manager.update_fresh_id(m_from_manager); } } ~ast_translation(); template T * operator()(T const * n) { return translate(n); } template T * translate(T const * n) { if (&from() == &to()) return const_cast(n); SASSERT(!n || from().contains(const_cast(n))); ast * r = process(n); SASSERT((!n && !r) || to().contains(const_cast(r))); return static_cast(r); } ast_manager & from() const { return m_from_manager; } ast_manager & to() const { return m_to_manager; } template ref_vector operator()(ref_vector const& src) { ref_vector dst(to()); for (expr* v : src) dst.push_back(translate(v)); return dst; } void reset_cache(); void cleanup(); unsigned loop_count() const { return m_loop_count; } unsigned hit_count() const { return m_hit_count; } unsigned miss_count() const { return m_miss_count; } unsigned insert_count() const { return m_insert_count; } unsigned long long get_num_collision() const { return m_cache.get_num_collision(); } }; // Translation with non-persistent cache. inline ast * translate(ast const * a, ast_manager & from, ast_manager & to) { return ast_translation(from, to)(a); } inline expr * translate(expr const * e, ast_manager & from, ast_manager & to) { return ast_translation(from, to)(e); } class expr_dependency_translation { ast_translation & m_translation; ptr_vector m_buffer; public: expr_dependency_translation(ast_translation & t):m_translation(t) {} expr_dependency * operator()(expr_dependency * d); }; inline expr_dependency * translate(expr_dependency * d, ast_manager & from, ast_manager & to) { ast_translation t(from, to); expr_dependency_translation td(t); return td(d); } #endif z3-z3-4.8.7/src/ast/ast_util.cpp000066400000000000000000000240461356505360400163430ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: ast_util.cpp Abstract: Helper functions Author: Leonardo de Moura (leonardo) 2007-06-08. Revision History: --*/ #include "ast/ast_util.h" #include "ast/arith_decl_plugin.h" app * mk_list_assoc_app(ast_manager & m, func_decl * f, unsigned num_args, expr * const * args) { SASSERT(f->is_associative()); SASSERT(num_args >= 2); if (num_args > 2) { unsigned j = num_args - 1; app * r = m.mk_app(f, args[j-1], args[j]); -- j; while (j > 0) { --j; r = m.mk_app(f, args[j], r); } return r; } else { SASSERT(num_args == 2); return m.mk_app(f, args[0], args[1]); } } app * mk_list_assoc_app(ast_manager & m, family_id fid, decl_kind k, unsigned num_args, expr * const * args) { func_decl * decl = m.mk_func_decl(fid, k, 0, nullptr, num_args, args, nullptr); SASSERT(decl != 0); SASSERT(decl->is_associative()); return mk_list_assoc_app(m, decl, num_args, args); } bool is_well_formed_vars(ptr_vector& bound, expr * top) { ptr_vector todo; ast_mark mark; todo.push_back(top); while (!todo.empty()) { expr * e = todo.back(); todo.pop_back(); if (mark.is_marked(e)) { continue; } mark.mark(e, true); if (is_quantifier(e)) { quantifier* q = to_quantifier(e); unsigned depth = q->get_num_decls(); bound.append(depth, q->get_decl_sorts()); if (!is_well_formed_vars(bound, q->get_expr())) { return false; } bound.resize(bound.size()-depth); } else if (is_app(e)) { app* a = to_app(e); for (unsigned i = 0; i < a->get_num_args(); ++i) { todo.push_back(a->get_arg(i)); } } else if (is_var(e)) { var* v = to_var(e); unsigned index = v->get_idx(); sort* s = v->get_sort(); SASSERT(index < bound.size()); index = bound.size()-1-index; if (!bound[index]) { bound[index] = s; } if (bound[index] != s) { return false; } } else { UNREACHABLE(); } } return true; } bool is_atom(ast_manager & m, expr * n) { if (is_quantifier(n) || !m.is_bool(n)) return false; if (is_var(n)) return true; SASSERT(is_app(n)); if (to_app(n)->get_family_id() != m.get_basic_family_id()) { return true; } // the other operators of the basic family are not considered atomic: distinct, ite, and, or, iff, xor, not, implies. return (m.is_eq(n) && !m.is_bool(to_app(n)->get_arg(0))) || m.is_true(n) || m.is_false(n); } bool is_literal(ast_manager & m, expr * n) { return is_atom(m, n) || (m.is_not(n) && is_atom(m, to_app(n)->get_arg(0))); } void get_literal_atom_sign(ast_manager & m, expr * n, expr * & atom, bool & sign) { SASSERT(is_literal(m, n)); if (is_atom(m, n)) { atom = n; sign = false; } else { SASSERT(m.is_not(n)); atom = to_app(n)->get_arg(0); sign = true; } } bool is_clause(ast_manager & m, expr * n) { if (is_literal(m, n)) return true; if (m.is_or(n)) { for (expr* arg : *to_app(n)) if (!is_literal(m, arg)) return false; return true; } return false; } unsigned get_clause_num_literals(ast_manager & m, expr * cls) { SASSERT(is_clause(m, cls)); if (is_literal(m, cls)) return 1; SASSERT(m.is_or(cls)); return to_app(cls)->get_num_args(); } expr * get_clause_literal(ast_manager & m, expr * cls, unsigned idx) { SASSERT(is_clause(m, cls)); SASSERT(idx < get_clause_num_literals(m, cls)); if (is_literal(m, cls)) { SASSERT(idx == 0); return cls; } SASSERT(m.is_or(cls)); return to_app(cls)->get_arg(idx); } expr * mk_and(ast_manager & m, unsigned num_args, expr * const * args) { if (num_args == 0) return m.mk_true(); else if (num_args == 1) return args[0]; else return m.mk_and(num_args, args); } app* mk_and(ast_manager & m, unsigned num_args, app * const * args) { return to_app(mk_and(m, num_args, (expr* const*) args)); } expr * mk_or(ast_manager & m, unsigned num_args, expr * const * args) { if (num_args == 0) return m.mk_false(); else if (num_args == 1) return args[0]; else return m.mk_or(num_args, args); } app* mk_or(ast_manager & m, unsigned num_args, app * const * args) { return to_app(mk_or(m, num_args, (expr* const*) args)); } expr * mk_not(ast_manager & m, expr * arg) { expr * atom; if (m.is_not(arg, atom)) return atom; else if (m.is_true(arg)) return m.mk_false(); else if (m.is_false(arg)) return m.mk_true(); else return m.mk_not(arg); } expr_ref mk_not(const expr_ref& e) { return expr_ref(mk_not(e.m(), e), e.m()); } expr_ref push_not(const expr_ref& e) { ast_manager& m = e.get_manager(); if (!is_app(e)) { return expr_ref(m.mk_not(e), m); } app* a = to_app(e); if (m.is_and(a)) { if (a->get_num_args() == 0) { return expr_ref(m.mk_false(), m); } expr_ref_vector args(m); for (expr* arg : *a) { args.push_back(push_not(expr_ref(arg, m))); } return mk_or(args); } if (m.is_or(a)) { if (a->get_num_args() == 0) { return expr_ref(m.mk_true(), m); } expr_ref_vector args(m); for (expr* arg : *a) { args.push_back(push_not(expr_ref(arg, m))); } return mk_and(args); } return expr_ref(mk_not(m, e), m); } expr * expand_distinct(ast_manager & m, unsigned num_args, expr * const * args) { expr_ref_buffer new_diseqs(m); for (unsigned i = 0; i < num_args; i++) { for (unsigned j = i + 1; j < num_args; j++) new_diseqs.push_back(m.mk_not(m.mk_eq(args[i], args[j]))); } return mk_and(m, new_diseqs.size(), new_diseqs.c_ptr()); } expr* mk_distinct(ast_manager& m, unsigned num_args, expr * const * args) { switch (num_args) { case 0: case 1: return m.mk_true(); case 2: return m.mk_not(m.mk_eq(args[0], args[1])); default: return m.mk_distinct(num_args, args); } } expr_ref mk_distinct(expr_ref_vector const& args) { ast_manager& m = args.get_manager(); return expr_ref(mk_distinct(m, args.size(), args.c_ptr()), m); } void flatten_and(expr_ref_vector& result) { ast_manager& m = result.get_manager(); expr* e1, *e2, *e3; for (unsigned i = 0; i < result.size(); ++i) { if (m.is_and(result.get(i))) { app* a = to_app(result.get(i)); for (expr* arg : *a) result.push_back(arg); result[i] = result.back(); result.pop_back(); --i; } else if (m.is_not(result.get(i), e1) && m.is_not(e1, e2)) { result[i] = e2; --i; } else if (m.is_not(result.get(i), e1) && m.is_or(e1)) { app* a = to_app(e1); for (expr* arg : *a) result.push_back(m.mk_not(arg)); result[i] = result.back(); result.pop_back(); --i; } else if (m.is_not(result.get(i), e1) && m.is_implies(e1,e2,e3)) { result.push_back(e2); result[i] = m.mk_not(e3); --i; } else if (m.is_true(result.get(i)) || (m.is_not(result.get(i), e1) && m.is_false(e1))) { result[i] = result.back(); result.pop_back(); --i; } else if (m.is_false(result.get(i)) || (m.is_not(result.get(i), e1) && m.is_true(e1))) { result.reset(); result.push_back(m.mk_false()); return; } } } void flatten_and(expr* fml, expr_ref_vector& result) { SASSERT(result.get_manager().is_bool(fml)); result.push_back(fml); flatten_and(result); } void flatten_and(expr_ref& fml) { expr_ref_vector fmls(fml.get_manager()); fmls.push_back(fml); flatten_and(fmls); fml = mk_and(fmls); } void flatten_or(expr_ref_vector& result) { ast_manager& m = result.get_manager(); expr* e1, *e2, *e3; for (unsigned i = 0; i < result.size(); ++i) { if (m.is_or(result.get(i))) { app* a = to_app(result.get(i)); for (expr* arg : *a) result.push_back(arg); result[i] = result.back(); result.pop_back(); --i; } else if (m.is_not(result.get(i), e1) && m.is_not(e1, e2)) { result[i] = e2; --i; } else if (m.is_not(result.get(i), e1) && m.is_and(e1)) { app* a = to_app(e1); for (expr* arg : *a) result.push_back(m.mk_not(arg)); result[i] = result.back(); result.pop_back(); --i; } else if (m.is_implies(result.get(i),e2,e3)) { result.push_back(e3); result[i] = m.mk_not(e2); --i; } else if (m.is_false(result.get(i)) || (m.is_not(result.get(i), e1) && m.is_true(e1))) { result[i] = result.back(); result.pop_back(); --i; } else if (m.is_true(result.get(i)) || (m.is_not(result.get(i), e1) && m.is_false(e1))) { result.reset(); result.push_back(m.mk_true()); return; } } } void flatten_or(expr* fml, expr_ref_vector& result) { SASSERT(result.get_manager().is_bool(fml)); result.push_back(fml); flatten_or(result); } static app_ref plus(ast_manager& m, expr* a, expr* b) { arith_util arith(m); return app_ref(arith.mk_add(a, b), m); } app_ref operator+(expr_ref& a, expr_ref& b) { return plus(a.m(), a.get(), b.get()); } z3-z3-4.8.7/src/ast/ast_util.h000066400000000000000000000132031356505360400160010ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: ast_util.h Abstract: Helper functions Author: Leonardo de Moura (leonardo) 2007-06-08. Revision History: --*/ #ifndef AST_UTIL_H_ #define AST_UTIL_H_ #include "ast/ast.h" #include "util/obj_hashtable.h" template void remove_duplicates(C & v) { expr_fast_mark1 visited; if (!v.empty()) { unsigned sz = v.size(); unsigned j = 0; for (unsigned i = 0; i < sz; i++) { typename C::data curr = v.get(i); if (!visited.is_marked(curr)) { visited.mark(curr); if (i != j) v.set(j, curr); j++; } } v.shrink(j); } } app * mk_list_assoc_app(ast_manager & m, func_decl * f, unsigned num_args, expr * const * args); app * mk_list_assoc_app(ast_manager & m, family_id fid, decl_kind k, unsigned num_args, expr * const * args); bool is_well_formed_vars(ptr_vector& bound, expr* n); inline bool args_are_vars(app const * n) { unsigned sz = n->get_num_args(); for (unsigned i = 0; i < sz; i++) { if (!is_var(n->get_arg(i))) return false; } return true; } inline bool depth_leq_one(app * n) { unsigned sz = n->get_num_args(); for (unsigned i = 0; i < sz; i++) { expr * arg = n->get_arg(i); if (is_app(arg) && to_app(arg)->get_num_args() > 0) return false; } return true; } template void dec_ref(ast_manager & m, obj_hashtable & s) { typename obj_hashtable::iterator it = s.begin(); typename obj_hashtable::iterator end = s.end(); for (;it != end; ++it) { m.dec_ref(*it); } } template void inc_ref(ast_manager & m, obj_hashtable & s) { typename obj_hashtable::iterator it = s.begin(); typename obj_hashtable::iterator end = s.end(); for (;it != end; ++it) { m.inc_ref(*it); } } // ----------------------------------- // // Clauses (as ASTs) support // // ----------------------------------- bool is_atom(ast_manager & m, expr * n); bool is_literal(ast_manager & m, expr * n); void get_literal_atom_sign(ast_manager & m, expr * n, expr * & atom, bool & sign); bool is_clause(ast_manager & m, expr * n); unsigned get_clause_num_literals(ast_manager & m, expr * cls); expr * get_clause_literal(ast_manager & m, expr * cls, unsigned idx); // ----------------------------------- // // Goodies for creating Boolean expressions // // ----------------------------------- /** Return (and args[0] ... args[num_args-1]) if num_args >= 2 Return args[0] if num_args == 1 Return true if num_args == 0 */ expr * mk_and(ast_manager & m, unsigned num_args, expr * const * args); app * mk_and(ast_manager & m, unsigned num_args, app * const * args); inline app_ref mk_and(app_ref_vector const& args) { return app_ref(mk_and(args.get_manager(), args.size(), args.c_ptr()), args.get_manager()); } inline expr_ref mk_and(expr_ref_vector const& args) { return expr_ref(mk_and(args.get_manager(), args.size(), args.c_ptr()), args.get_manager()); } inline app_ref operator&(expr_ref& a, expr* b) { return app_ref(a.m().mk_and(a, b), a.m()); } inline app_ref operator&(app_ref& a, expr* b) { return app_ref(a.m().mk_and(a, b), a.m()); } inline app_ref operator&(var_ref& a, expr* b) { return app_ref(a.m().mk_and(a, b), a.m()); } inline app_ref operator&(quantifier_ref& a, expr* b) { return app_ref(a.m().mk_and(a, b), a.m()); } inline app_ref operator|(expr_ref& a, expr* b) { return app_ref(a.m().mk_or(a, b), a.m()); } inline app_ref operator|(app_ref& a, expr* b) { return app_ref(a.m().mk_or(a, b), a.m()); } inline app_ref operator|(var_ref& a, expr* b) { return app_ref(a.m().mk_or(a, b), a.m()); } inline app_ref operator|(quantifier_ref& a, expr* b) { return app_ref(a.m().mk_or(a, b), a.m()); } app_ref operator+(expr_ref& a, expr_ref& b); /** Return (or args[0] ... args[num_args-1]) if num_args >= 2 Return args[0] if num_args == 1 Return false if num_args == 0 */ expr * mk_or(ast_manager & m, unsigned num_args, expr * const * args); app * mk_or(ast_manager & m, unsigned num_args, app * const * args); inline app_ref mk_or(app_ref_vector const& args) { return app_ref(mk_or(args.get_manager(), args.size(), args.c_ptr()), args.get_manager()); } inline expr_ref mk_or(expr_ref_vector const& args) { return expr_ref(mk_or(args.get_manager(), args.size(), args.c_ptr()), args.get_manager()); } /** Return a if arg = (not a) Return (not arg) otherwise */ expr * mk_not(ast_manager & m, expr * arg); expr_ref mk_not(const expr_ref& e); inline app_ref mk_not(const app_ref& e) { return app_ref(e.m().mk_not(e), e.m()); } /** Negate and push over conjunction or disjunction. */ expr_ref push_not(const expr_ref& arg); /** Return the expression (and (not (= args[0] args[1])) (not (= args[0] args[2])) ... (not (= args[num_args-2] args[num_args-1]))) */ expr * expand_distinct(ast_manager & m, unsigned num_args, expr * const * args); /** Create simplified distinct term. Binary distinct becomes a single disequality. */ expr * mk_distinct(ast_manager& m, unsigned num_args, expr * const * args); expr_ref mk_distinct(expr_ref_vector const& args); /** \brief Collect top-level conjunctions and disjunctions. */ void flatten_and(expr_ref_vector& result); void flatten_and(expr_ref& fml); void flatten_and(expr* fml, expr_ref_vector& result); void flatten_or(expr_ref_vector& result); void flatten_or(expr* fml, expr_ref_vector& result); #endif /* AST_UTIL_H_ */ z3-z3-4.8.7/src/ast/bv_decl_plugin.cpp000066400000000000000000001040251356505360400174670ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: bv_decl_plugin.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-01-09. Revision History: --*/ #include #include "ast/bv_decl_plugin.h" #include "ast/arith_decl_plugin.h" #include "util/warning.h" #include "ast/ast_pp.h" #include "ast/ast_smt2_pp.h" bv_decl_plugin::bv_decl_plugin(): m_bv_sym("bv"), m_concat_sym("concat"), m_sign_extend_sym("sign_extend"), m_zero_extend_sym("zero_extend"), m_extract_sym("extract"), m_rotate_left_sym("rotate_left"), m_rotate_right_sym("rotate_right"), m_repeat_sym("repeat"), m_bit2bool_sym("bit2bool"), m_mkbv_sym("mkbv"), m_bit0(nullptr), m_bit1(nullptr), m_carry(nullptr), m_xor3(nullptr), m_int_sort(nullptr) { } void bv_decl_plugin::set_manager(ast_manager * m, family_id id) { decl_plugin::set_manager(m, id); for (unsigned i = 1; i <= 64; i++) { mk_bv_sort(i); } m_bit0 = m->mk_const_decl(symbol("bit0"), get_bv_sort(1), func_decl_info(m_family_id, OP_BIT0)); m_bit1 = m->mk_const_decl(symbol("bit1"), get_bv_sort(1), func_decl_info(m_family_id, OP_BIT1)); m->inc_ref(m_bit0); m->inc_ref(m_bit1); sort * b = m->mk_bool_sort(); sort * d[3] = {b, b, b}; m_carry = m_manager->mk_func_decl(symbol("carry"), 3, d, b, func_decl_info(m_family_id, OP_CARRY)); m_manager->inc_ref(m_carry); m_xor3 = m_manager->mk_func_decl(symbol("xor3"), 3, d, b, func_decl_info(m_family_id, OP_XOR3)); m_manager->inc_ref(m_xor3); m_int_sort = m_manager->mk_sort(m_manager->mk_family_id("arith"), INT_SORT); SASSERT(m_int_sort != 0); // arith_decl_plugin must be installed before bv_decl_plugin. m_manager->inc_ref(m_int_sort); } void bv_decl_plugin::finalize() { #define DEC_REF(FIELD) dec_range_ref(FIELD.begin(), FIELD.end(), *m_manager) if (m_bit0) { m_manager->dec_ref(m_bit0); } if (m_bit1) { m_manager->dec_ref(m_bit1); } if (m_carry) { m_manager->dec_ref(m_carry); } if (m_xor3) { m_manager->dec_ref(m_xor3); } if (m_int_sort) { m_manager->dec_ref(m_int_sort); } DEC_REF(m_bv_sorts); DEC_REF(m_bv_neg); DEC_REF(m_bv_add); DEC_REF(m_bv_sub); DEC_REF(m_bv_mul); DEC_REF(m_bv_sdiv); DEC_REF(m_bv_udiv); DEC_REF(m_bv_srem); DEC_REF(m_bv_urem); DEC_REF(m_bv_smod); DEC_REF(m_bv_sdiv0); DEC_REF(m_bv_udiv0); DEC_REF(m_bv_srem0); DEC_REF(m_bv_urem0); DEC_REF(m_bv_smod0); DEC_REF(m_bv_sdiv_i); DEC_REF(m_bv_udiv_i); DEC_REF(m_bv_srem_i); DEC_REF(m_bv_urem_i); DEC_REF(m_bv_smod_i); DEC_REF(m_bv_uleq); DEC_REF(m_bv_sleq); DEC_REF(m_bv_ugeq); DEC_REF(m_bv_sgeq); DEC_REF(m_bv_ult); DEC_REF(m_bv_slt); DEC_REF(m_bv_ugt); DEC_REF(m_bv_sgt); DEC_REF(m_bv_and); DEC_REF(m_bv_or); DEC_REF(m_bv_not); DEC_REF(m_bv_xor); DEC_REF(m_bv_nand); DEC_REF(m_bv_nor); DEC_REF(m_bv_xnor); DEC_REF(m_bv_redor); DEC_REF(m_bv_redand); DEC_REF(m_bv_comp); DEC_REF(m_bv_mul_ovfl); DEC_REF(m_bv_smul_ovfl); DEC_REF(m_bv_smul_udfl); DEC_REF(m_bv_shl); DEC_REF(m_bv_lshr); DEC_REF(m_bv_ashr); DEC_REF(m_ext_rotate_left); DEC_REF(m_ext_rotate_right); DEC_REF(m_int2bv); DEC_REF(m_bv2int); vector >::iterator it = m_bit2bool.begin(); vector >::iterator end = m_bit2bool.end(); for (; it != end; ++it) { ptr_vector & ds = *it; DEC_REF(ds); } DEC_REF(m_mkbv); } void bv_decl_plugin::mk_bv_sort(unsigned bv_size) { force_ptr_array_size(m_bv_sorts, bv_size + 1); if (m_bv_sorts[bv_size] == 0) { parameter p(bv_size); sort_size sz; if (sort_size::is_very_big_base2(bv_size)) { sz = sort_size::mk_very_big(); } else { sz = sort_size(rational::power_of_two(bv_size)); } m_bv_sorts[bv_size] = m_manager->mk_sort(symbol("bv"), sort_info(m_family_id, BV_SORT, sz, 1, &p)); m_manager->inc_ref(m_bv_sorts[bv_size]); } } inline sort * bv_decl_plugin::get_bv_sort(unsigned bv_size) { if (bv_size < (1 << 12)) { mk_bv_sort(bv_size); return m_bv_sorts[bv_size]; } parameter p(bv_size); sort_size sz(sort_size::mk_very_big()); return m_manager->mk_sort(symbol("bv"), sort_info(m_family_id, BV_SORT, sz, 1, &p)); } sort * bv_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) { if (!(num_parameters == 1 && parameters[0].is_int())) { m_manager->raise_exception("expecting one integer parameter to bit-vector sort"); } unsigned bv_size = parameters[0].get_int(); if (bv_size == 0) { m_manager->raise_exception("bit-vector size must be greater than zero"); } mk_bv_sort(bv_size); return m_bv_sorts[bv_size]; } func_decl * bv_decl_plugin::mk_binary(ptr_vector & decls, decl_kind k, char const * name, unsigned bv_size, bool ac, bool idempotent) { force_ptr_array_size(decls, bv_size + 1); if (decls[bv_size] == 0) { sort * s = get_bv_sort(bv_size); func_decl_info info(m_family_id, k); info.set_associative(ac); info.set_flat_associative(ac); info.set_commutative(ac); info.set_idempotent(idempotent); decls[bv_size] = m_manager->mk_func_decl(symbol(name), s, s, s, info); m_manager->inc_ref(decls[bv_size]); } return decls[bv_size]; } func_decl * bv_decl_plugin::mk_unary(ptr_vector & decls, decl_kind k, char const * name, unsigned bv_size) { force_ptr_array_size(decls, bv_size + 1); if (decls[bv_size] == 0) { sort * s = get_bv_sort(bv_size); decls[bv_size] = m_manager->mk_func_decl(symbol(name), s, s, func_decl_info(m_family_id, k)); m_manager->inc_ref(decls[bv_size]); } return decls[bv_size]; } func_decl * bv_decl_plugin::mk_int2bv(unsigned bv_size, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain) { if (bv_size == 0) { m_manager->raise_exception("bit-vector size must be greater than zero"); } force_ptr_array_size(m_int2bv, bv_size + 1); if (arity != 1) { m_manager->raise_exception("expecting one argument to int2bv"); return nullptr; } if (m_int2bv[bv_size] == 0) { sort * s = get_bv_sort(bv_size); m_int2bv[bv_size] = m_manager->mk_func_decl(symbol("int2bv"), domain[0], s, func_decl_info(m_family_id, OP_INT2BV, num_parameters, parameters)); m_manager->inc_ref(m_int2bv[bv_size]); } return m_int2bv[bv_size]; } func_decl * bv_decl_plugin::mk_bv2int(unsigned bv_size, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain) { force_ptr_array_size(m_bv2int, bv_size + 1); if (arity != 1) { m_manager->raise_exception("expecting one argument to bv2int"); return nullptr; } if (m_bv2int[bv_size] == 0) { m_bv2int[bv_size] = m_manager->mk_func_decl(symbol("bv2int"), domain[0], m_int_sort, func_decl_info(m_family_id, OP_BV2INT)); m_manager->inc_ref(m_bv2int[bv_size]); } return m_bv2int[bv_size]; } func_decl * bv_decl_plugin::mk_pred(ptr_vector & decls, decl_kind k, char const * name, unsigned bv_size) { force_ptr_array_size(decls, bv_size + 1); if (decls[bv_size] == 0) { sort * s = get_bv_sort(bv_size); decls[bv_size] = m_manager->mk_func_decl(symbol(name), s, s, m_manager->mk_bool_sort(), func_decl_info(m_family_id, k)); m_manager->inc_ref(decls[bv_size]); } return decls[bv_size]; } func_decl * bv_decl_plugin::mk_reduction(ptr_vector & decls, decl_kind k, char const * name, unsigned bv_size) { force_ptr_array_size(decls, bv_size + 1); if (decls[bv_size] == 0) { sort * d = get_bv_sort(bv_size); sort * r = get_bv_sort(1); decls[bv_size] = m_manager->mk_func_decl(symbol(name), d, r, func_decl_info(m_family_id, k)); m_manager->inc_ref(decls[bv_size]); } return decls[bv_size]; } func_decl * bv_decl_plugin::mk_comp(unsigned bv_size) { force_ptr_array_size(m_bv_comp, bv_size + 1); if (m_bv_comp[bv_size] == 0) { sort * d = get_bv_sort(bv_size); sort * r = get_bv_sort(1); func_decl_info info(m_family_id, OP_BCOMP); info.set_commutative(); m_bv_comp[bv_size] = m_manager->mk_func_decl(symbol("bvcomp"), d, d, r, info); m_manager->inc_ref(m_bv_comp[bv_size]); } return m_bv_comp[bv_size]; } func_decl * bv_decl_plugin::mk_func_decl(decl_kind k, unsigned bv_size) { switch (k) { case OP_BNEG: return mk_unary(m_bv_neg, k, "bvneg", bv_size); case OP_BADD: return mk_binary(m_bv_add, k, "bvadd", bv_size, true); case OP_BSUB: return mk_binary(m_bv_sub, k, "bvsub", bv_size, false); case OP_BMUL: return mk_binary(m_bv_mul, k, "bvmul", bv_size, true); case OP_BSDIV: return mk_binary(m_bv_sdiv, k, "bvsdiv", bv_size, false); case OP_BUDIV: return mk_binary(m_bv_udiv, k, "bvudiv", bv_size, false); case OP_BSREM: return mk_binary(m_bv_srem, k, "bvsrem", bv_size, false); case OP_BUREM: return mk_binary(m_bv_urem, k, "bvurem", bv_size, false); case OP_BSMOD: return mk_binary(m_bv_smod, k, "bvsmod", bv_size, false); case OP_BSDIV0: return mk_unary(m_bv_sdiv0, k, "bvsdiv0", bv_size); case OP_BUDIV0: return mk_unary(m_bv_udiv0, k, "bvudiv0", bv_size); case OP_BSREM0: return mk_unary(m_bv_srem0, k, "bvsrem0", bv_size); case OP_BUREM0: return mk_unary(m_bv_urem0, k, "bvurem0", bv_size); case OP_BSMOD0: return mk_unary(m_bv_smod0, k, "bvsmod0", bv_size); case OP_BSDIV_I: return mk_binary(m_bv_sdiv_i, k, "bvsdiv_i", bv_size, false); case OP_BUDIV_I: return mk_binary(m_bv_udiv_i, k, "bvudiv_i", bv_size, false); case OP_BSREM_I: return mk_binary(m_bv_srem_i, k, "bvsrem_i", bv_size, false); case OP_BUREM_I: return mk_binary(m_bv_urem_i, k, "bvurem_i", bv_size, false); case OP_BSMOD_I: return mk_binary(m_bv_smod_i, k, "bvsmod_i", bv_size, false); case OP_ULEQ: return mk_pred(m_bv_uleq, k, "bvule", bv_size); case OP_SLEQ: return mk_pred(m_bv_sleq, k, "bvsle", bv_size); case OP_UGEQ: return mk_pred(m_bv_ugeq, k, "bvuge", bv_size); case OP_SGEQ: return mk_pred(m_bv_sgeq, k, "bvsge", bv_size); case OP_ULT: return mk_pred(m_bv_ult, k, "bvult", bv_size); case OP_SLT: return mk_pred(m_bv_slt, k, "bvslt", bv_size); case OP_UGT: return mk_pred(m_bv_ugt, k, "bvugt", bv_size); case OP_SGT: return mk_pred(m_bv_sgt, k, "bvsgt", bv_size); case OP_BAND: return mk_binary(m_bv_and, k, "bvand", bv_size, true, true); case OP_BOR: return mk_binary(m_bv_or, k, "bvor", bv_size, true, true); case OP_BNOT: return mk_unary(m_bv_not, k, "bvnot", bv_size); case OP_BXOR: return mk_binary(m_bv_xor, k, "bvxor", bv_size, true); case OP_BNAND: return mk_binary(m_bv_nand, k, "bvnand", bv_size, false); case OP_BNOR: return mk_binary(m_bv_nor, k, "bvnor", bv_size, false); case OP_BXNOR: return mk_binary(m_bv_xnor, k, "bvxnor", bv_size, true); case OP_BREDOR: return mk_reduction(m_bv_redor, k, "bvredor", bv_size); case OP_BREDAND: return mk_reduction(m_bv_redand, k, "bvredand", bv_size); case OP_BCOMP: return mk_comp(bv_size); case OP_BUMUL_NO_OVFL: return mk_pred(m_bv_mul_ovfl, k, "bvumul_noovfl", bv_size); case OP_BSMUL_NO_OVFL: return mk_pred(m_bv_smul_ovfl, k, "bvsmul_noovfl", bv_size); case OP_BSMUL_NO_UDFL: return mk_pred(m_bv_smul_udfl, k, "bvsmul_noudfl", bv_size); case OP_BSHL: return mk_binary(m_bv_shl, k, "bvshl", bv_size, false); case OP_BLSHR: return mk_binary(m_bv_lshr, k, "bvlshr", bv_size, false); case OP_BASHR: return mk_binary(m_bv_ashr, k, "bvashr", bv_size, false); case OP_EXT_ROTATE_LEFT: return mk_binary(m_ext_rotate_left, k, "ext_rotate_left", bv_size, false); case OP_EXT_ROTATE_RIGHT: return mk_binary(m_ext_rotate_right, k, "ext_rotate_right", bv_size, false); default: return nullptr; } } inline bool bv_decl_plugin::get_bv_size(sort * s, int & result) { if (s->get_family_id() == m_family_id && s->get_decl_kind() == BV_SORT) { result = s->get_parameter(0).get_int(); return true; } return false; } inline bool bv_decl_plugin::get_bv_size(expr * t, int & result) { return get_bv_size(m_manager->get_sort(t), result); } bool bv_decl_plugin::get_concat_size(unsigned arity, sort * const * domain, int & result) { result = 0; for (unsigned i = 0; i < arity; i++) { int sz; if (!get_bv_size(domain[i], sz)) { return false; } result += sz; } return true; } bool bv_decl_plugin::get_extend_size(unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, int & result) { int arg_sz; if (arity != 1 || !get_bv_size(domain[0], arg_sz) || num_parameters != 1 || !parameters[0].is_int() || parameters[0].get_int() < 0) { return false; } result = arg_sz + parameters[0].get_int(); return true; } bool bv_decl_plugin::get_extract_size(unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, int & result) { int arg_sz; if (arity != 1 || !get_bv_size(domain[0], arg_sz) || num_parameters != 2 || !parameters[0].is_int() || !parameters[1].is_int() || parameters[1].get_int() > parameters[0].get_int() || parameters[0].get_int() >= arg_sz) { return false; } result = parameters[0].get_int() - parameters[1].get_int() + 1; return true; } bool bv_decl_plugin::get_int2bv_size(unsigned num_parameters, parameter const * parameters, int & result) { if (num_parameters != 1) { m_manager->raise_exception("int2bv expects one parameter"); return false; } const parameter &p = parameters[0]; if (p.is_int()) { result = p.get_int(); return true; } if (!p.is_ast() || !is_expr(p.get_ast())) { m_manager->raise_exception("int2bv expects one integer parameter"); return false; } return get_bv_size(to_expr(p.get_ast()), result); } func_decl * bv_decl_plugin::mk_num_decl(unsigned num_parameters, parameter const * parameters, unsigned arity) { if (!(num_parameters == 2 && arity == 0 && parameters[0].is_rational() && parameters[1].is_int())) { m_manager->raise_exception("invalid bit-vector numeral declaration"); return nullptr; } unsigned bv_size = parameters[1].get_int(); if (bv_size == 0) { m_manager->raise_exception("bit-vector size must be greater than zero"); } // TODO: sign an error if the parameters[0] is out of range, that is, it is a value not in [0, 2^{bv_size}) // This cannot be enforced now, since some Z3 modules try to generate these invalid numerals. // After SMT-COMP, I should find all offending modules. // For now, I will just simplify the numeral here. parameter p0(mod(parameters[0].get_rational(), rational::power_of_two(bv_size))); parameter ps[2] = { std::move(p0), parameters[1] }; sort * bv = get_bv_sort(bv_size); return m_manager->mk_const_decl(m_bv_sym, bv, func_decl_info(m_family_id, OP_BV_NUM, num_parameters, ps)); } func_decl * bv_decl_plugin::mk_bit2bool(unsigned bv_size, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain) { if (!(num_parameters == 1 && parameters[0].is_int() && arity == 1 && parameters[0].get_int() < static_cast(bv_size))) { m_manager->raise_exception("invalid bit2bool declaration"); return nullptr; } unsigned idx = parameters[0].get_int(); m_bit2bool.reserve(bv_size+1); ptr_vector & v = m_bit2bool[bv_size]; v.reserve(bv_size, 0); if (v[idx] == 0) { v[idx] = m_manager->mk_func_decl(m_bit2bool_sym, domain[0], m_manager->mk_bool_sort(), func_decl_info(m_family_id, OP_BIT2BOOL, num_parameters, parameters)); m_manager->inc_ref(v[idx]); } return v[idx]; } func_decl * bv_decl_plugin::mk_mkbv(unsigned arity, sort * const * domain) { for (unsigned i = 0; i < arity; i++) { if (!m_manager->is_bool(domain[i])) { m_manager->raise_exception("invalid mkbv operator"); return nullptr; } } unsigned bv_size = arity; m_mkbv.reserve(bv_size+1); if (m_mkbv[bv_size] == 0) { m_mkbv[bv_size] = m_manager->mk_func_decl(m_mkbv_sym, arity, domain, get_bv_sort(bv_size), func_decl_info(m_family_id, OP_MKBV)); m_manager->inc_ref(m_mkbv[bv_size]); } return m_mkbv[bv_size]; } func_decl * bv_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { int bv_size; if (k == OP_INT2BV && get_int2bv_size(num_parameters, parameters, bv_size)) { // bv_size is filled in. } else if (k == OP_BV_NUM) { return mk_num_decl(num_parameters, parameters, arity); } else if (k == OP_BIT0) { return m_bit0; } else if (k == OP_BIT1) { return m_bit1; } else if (k == OP_CARRY) { return m_carry; } else if (k == OP_XOR3) { return m_xor3; } else if (k == OP_MKBV) { return mk_mkbv(arity, domain); } else if (arity == 0) { m_manager->raise_exception("no arguments supplied to bit-vector operator"); return nullptr; } else if (!get_bv_size(domain[0], bv_size)) { m_manager->raise_exception("could not extract bit-vector size"); return nullptr; } func_decl * r = mk_func_decl(k, bv_size); if (r != nullptr) { if (arity != r->get_arity()) { if (r->get_info()->is_associative()) arity = r->get_arity(); else { m_manager->raise_exception("declared arity mismatches supplied arity"); return nullptr; } } for (unsigned i = 0; i < arity; ++i) { if (domain[i] != r->get_domain(i)) { m_manager->raise_exception("declared sorts do not match supplied sorts"); return nullptr; } } return r; } int r_size; switch (k) { case OP_BIT2BOOL: return mk_bit2bool(bv_size, num_parameters, parameters, arity, domain); case OP_INT2BV: return mk_int2bv(bv_size, num_parameters, parameters, arity, domain); case OP_BV2INT: return mk_bv2int(bv_size, num_parameters, parameters, arity, domain); case OP_CONCAT: if (!get_concat_size(arity, domain, r_size)) m_manager->raise_exception("invalid concat application"); return m_manager->mk_func_decl(m_concat_sym, arity, domain, get_bv_sort(r_size), func_decl_info(m_family_id, k)); case OP_SIGN_EXT: if (!get_extend_size(num_parameters, parameters, arity, domain, r_size)) m_manager->raise_exception("invalid sign_extend application"); return m_manager->mk_func_decl(m_sign_extend_sym, arity, domain, get_bv_sort(r_size), func_decl_info(m_family_id, k, num_parameters, parameters)); case OP_ZERO_EXT: if (!get_extend_size(num_parameters, parameters, arity, domain, r_size)) m_manager->raise_exception("invalid zero_extend application"); return m_manager->mk_func_decl(m_zero_extend_sym, arity, domain, get_bv_sort(r_size), func_decl_info(m_family_id, k, num_parameters, parameters)); case OP_EXTRACT: if (!get_extract_size(num_parameters, parameters, arity, domain, r_size)) m_manager->raise_exception("invalid extract application"); return m_manager->mk_func_decl(m_extract_sym, arity, domain, get_bv_sort(r_size), func_decl_info(m_family_id, k, num_parameters, parameters)); 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: if (arity != 1) m_manager->raise_exception("repeat expects one argument"); if (num_parameters != 1 || !parameters[0].is_int() || parameters[0].get_int() == 0) m_manager->raise_exception("repeat expects one nonzero integer parameter"); if (!get_bv_size(domain[0], bv_size)) m_manager->raise_exception("repeat expects an argument with bit-vector sort"); return m_manager->mk_func_decl(m_repeat_sym, arity, domain, get_bv_sort(bv_size * parameters[0].get_int()), func_decl_info(m_family_id, k, num_parameters, parameters)); default: return nullptr; } } func_decl * bv_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned num_args, expr * const * args, sort * range) { ast_manager& m = *m_manager; int bv_size; if (k == OP_INT2BV && get_int2bv_size(num_parameters, parameters, bv_size)) { // bv_size is filled in. } else if (k == OP_BV_NUM) { return mk_num_decl(num_parameters, parameters, num_args); } else if (k == OP_BIT0) { return m_bit0; } else if (k == OP_BIT1) { return m_bit1; } else if (k == OP_CARRY) { return m_carry; } else if (k == OP_XOR3) { return m_xor3; } else if (k == OP_MKBV) { return decl_plugin::mk_func_decl(k, num_parameters, parameters, num_args, args, range); } else if (num_args == 0 || !get_bv_size(args[0], bv_size)) { m.raise_exception("operator is applied to arguments of the wrong sort"); return nullptr; } func_decl * r = mk_func_decl(k, bv_size); if (r != nullptr) { if (num_args != r->get_arity()) { if (r->get_info()->is_associative()) { sort * fs = r->get_domain(0); for (unsigned i = 0; i < num_args; ++i) { if (m.get_sort(args[i]) != fs) { m_manager->raise_exception("declared sorts do not match supplied sorts"); return nullptr; } } return r; } else { m.raise_exception("declared arity mismatches supplied arity"); return nullptr; } } for (unsigned i = 0; i < num_args; ++i) { if (m.get_sort(args[i]) != r->get_domain(i)) { std::ostringstream buffer; buffer << "Argument " << mk_pp(args[i], m) << " at position " << i << " does not match declaration " << mk_pp(r, m); m.raise_exception(buffer.str()); return nullptr; } } return r; } return decl_plugin::mk_func_decl(k, num_parameters, parameters, num_args, args, range); } bool bv_decl_plugin::is_value(app* e) const { return is_app_of(e, m_family_id, OP_BV_NUM); } void bv_decl_plugin::get_offset_term(app * a, expr * & t, rational & offset) const { family_id fid = get_family_id(); if (a->get_num_args() == 2 && is_app_of(a, fid, OP_BADD) && is_app_of(a->get_arg(0), fid, OP_BV_NUM)) { unsigned sz; func_decl * decl = to_app(a->get_arg(0))->get_decl(); offset = decl->get_parameter(0).get_rational(); sz = decl->get_parameter(1).get_int(); t = a->get_arg(1); offset = mod(offset, rational::power_of_two(sz)); } else { t = a; offset = rational(0); } } bool bv_decl_plugin::are_distinct(app * a, app * b) const { #if 1 // Check for a + k1 != a + k2 when k1 != k2 rational a_offset; expr * a_term; rational b_offset; expr * b_term; get_offset_term(a, a_term, a_offset); get_offset_term(b, b_term, b_offset); TRACE("bv_are_distinct", tout << mk_ismt2_pp(a, *m_manager) << "\n" << mk_ismt2_pp(b, *m_manager) << "\n"; tout << "---->\n"; tout << "a: " << a_offset << " + " << mk_ismt2_pp(a_term, *m_manager) << "\n"; tout << "b: " << b_offset << " + " << mk_ismt2_pp(b_term, *m_manager) << "\n";); if (a_term == b_term && a_offset != b_offset) return true; #endif return decl_plugin::are_distinct(a, b); } void bv_decl_plugin::get_sort_names(svector & sort_names, symbol const & logic) { if (logic == symbol::null || logic == symbol("ALL")) sort_names.push_back(builtin_name("bv", BV_SORT)); sort_names.push_back(builtin_name("BitVec", BV_SORT)); } void bv_decl_plugin::get_op_names(svector & op_names, symbol const & logic) { op_names.push_back(builtin_name("bit1",OP_BIT1)); op_names.push_back(builtin_name("bit0",OP_BIT0)); op_names.push_back(builtin_name("bvneg",OP_BNEG)); op_names.push_back(builtin_name("bvadd",OP_BADD)); op_names.push_back(builtin_name("bvsub",OP_BSUB)); op_names.push_back(builtin_name("bvmul",OP_BMUL)); op_names.push_back(builtin_name("bvsdiv",OP_BSDIV)); op_names.push_back(builtin_name("bvudiv",OP_BUDIV)); op_names.push_back(builtin_name("bvsrem",OP_BSREM)); op_names.push_back(builtin_name("bvurem",OP_BUREM)); op_names.push_back(builtin_name("bvsmod",OP_BSMOD)); op_names.push_back(builtin_name("bvule",OP_ULEQ)); op_names.push_back(builtin_name("bvsle",OP_SLEQ)); op_names.push_back(builtin_name("bvuge",OP_UGEQ)); op_names.push_back(builtin_name("bvsge",OP_SGEQ)); op_names.push_back(builtin_name("bvult",OP_ULT)); op_names.push_back(builtin_name("bvslt",OP_SLT)); op_names.push_back(builtin_name("bvugt",OP_UGT)); op_names.push_back(builtin_name("bvsgt",OP_SGT)); op_names.push_back(builtin_name("bvand",OP_BAND)); op_names.push_back(builtin_name("bvor",OP_BOR)); op_names.push_back(builtin_name("bvnot",OP_BNOT)); op_names.push_back(builtin_name("bvxor",OP_BXOR)); op_names.push_back(builtin_name("bvnand",OP_BNAND)); op_names.push_back(builtin_name("bvnor",OP_BNOR)); op_names.push_back(builtin_name("bvxnor",OP_BXNOR)); op_names.push_back(builtin_name("concat",OP_CONCAT)); op_names.push_back(builtin_name("sign_extend",OP_SIGN_EXT)); op_names.push_back(builtin_name("zero_extend",OP_ZERO_EXT)); op_names.push_back(builtin_name("extract",OP_EXTRACT)); op_names.push_back(builtin_name("repeat",OP_REPEAT)); op_names.push_back(builtin_name("bvredor",OP_BREDOR)); op_names.push_back(builtin_name("bvredand",OP_BREDAND)); op_names.push_back(builtin_name("bvcomp",OP_BCOMP)); op_names.push_back(builtin_name("bvshl",OP_BSHL)); op_names.push_back(builtin_name("bvlshr",OP_BLSHR)); op_names.push_back(builtin_name("bvashr",OP_BASHR)); op_names.push_back(builtin_name("rotate_left",OP_ROTATE_LEFT)); op_names.push_back(builtin_name("rotate_right",OP_ROTATE_RIGHT)); op_names.push_back(builtin_name("bit2bool", OP_BIT2BOOL)); if (logic == symbol::null || logic == symbol("ALL") || logic == "QF_FD") { op_names.push_back(builtin_name("bvumul_noovfl",OP_BUMUL_NO_OVFL)); op_names.push_back(builtin_name("bvsmul_noovfl",OP_BSMUL_NO_OVFL)); op_names.push_back(builtin_name("bvsmul_noudfl",OP_BSMUL_NO_UDFL)); op_names.push_back(builtin_name("bvsdiv0", OP_BSDIV0)); op_names.push_back(builtin_name("bvudiv0", OP_BUDIV0)); op_names.push_back(builtin_name("bvsrem0", OP_BSREM0)); op_names.push_back(builtin_name("bvurem0", OP_BUREM0)); op_names.push_back(builtin_name("bvsmod0", OP_BSMOD0)); op_names.push_back(builtin_name("bvsdiv_i", OP_BSDIV_I)); op_names.push_back(builtin_name("bvudiv_i", OP_BUDIV_I)); op_names.push_back(builtin_name("bvsrem_i", OP_BSREM_I)); op_names.push_back(builtin_name("bvurem_i", OP_BUREM_I)); op_names.push_back(builtin_name("bvsmod_i", OP_BSMOD_I)); op_names.push_back(builtin_name("ext_rotate_left",OP_EXT_ROTATE_LEFT)); op_names.push_back(builtin_name("ext_rotate_right",OP_EXT_ROTATE_RIGHT)); op_names.push_back(builtin_name("int2bv",OP_INT2BV)); op_names.push_back(builtin_name("bv2int",OP_BV2INT)); op_names.push_back(builtin_name("bv2nat",OP_BV2INT)); op_names.push_back(builtin_name("mkbv",OP_MKBV)); } } expr * bv_decl_plugin::get_some_value(sort * s) { SASSERT(s->is_sort_of(m_family_id, BV_SORT)); unsigned bv_size = s->get_parameter(0).get_int(); parameter p[2] = { parameter(rational::zero()), parameter(static_cast(bv_size)) }; return m_manager->mk_app(m_family_id, OP_BV_NUM, 2, p, 0, nullptr); } rational bv_recognizers::norm(rational const & val, unsigned bv_size, bool is_signed) const { rational r = mod(val, rational::power_of_two(bv_size)); SASSERT(!r.is_neg()); if (is_signed) { if (r >= rational::power_of_two(bv_size - 1)) { r -= rational::power_of_two(bv_size); } if (r < -rational::power_of_two(bv_size - 1)) { r += rational::power_of_two(bv_size); } } return r; } bool bv_recognizers::has_sign_bit(rational const & n, unsigned bv_size) const { SASSERT(bv_size > 0); rational m = norm(n, bv_size, false); rational p = rational::power_of_two(bv_size - 1); return m >= p; } bool bv_recognizers::is_bv_sort(sort const * s) const { return (s->get_family_id() == get_fid() && s->get_decl_kind() == BV_SORT && s->get_num_parameters() == 1); } bool bv_recognizers::is_numeral(expr const * n, rational & val, unsigned & bv_size) const { if (!is_app_of(n, get_fid(), OP_BV_NUM)) { return false; } func_decl * decl = to_app(n)->get_decl(); val = decl->get_parameter(0).get_rational(); bv_size = decl->get_parameter(1).get_int(); return true; } bool bv_recognizers::is_numeral(expr const * n, rational & val) const { unsigned bv_size = 0; return is_numeral(n, val, bv_size); } bool bv_recognizers::is_allone(expr const * e) const { rational r; unsigned bv_size; if (!is_numeral(e, r, bv_size)) { return false; } bool result = (r == rational::power_of_two(bv_size) - rational(1)); TRACE("is_allone", tout << r << " " << result << "\n";); return result; } bool bv_recognizers::is_zero(expr const * n) const { if (!is_app_of(n, get_fid(), OP_BV_NUM)) { return false; } func_decl * decl = to_app(n)->get_decl(); return decl->get_parameter(0).get_rational().is_zero(); } bool bv_recognizers::is_extract(expr const* e, unsigned& low, unsigned& high, expr*& b) const { if (!is_extract(e)) return false; low = get_extract_low(e); high = get_extract_high(e); b = to_app(e)->get_arg(0); return true; } bool bv_recognizers::is_bv2int(expr const* e, expr*& r) const { if (!is_bv2int(e)) return false; r = to_app(e)->get_arg(0); return true; } bool bv_recognizers::mult_inverse(rational const & n, unsigned bv_size, rational & result) { if (n.is_one()) { result = n; return true; } if (!mod(n, rational(2)).is_one()) { return false; } rational g; rational x; rational y; g = gcd(n, rational::power_of_two(bv_size), x, y); if (x.is_neg()) { x = mod(x, rational::power_of_two(bv_size)); } SASSERT(x.is_pos()); SASSERT(mod(x * n, rational::power_of_two(bv_size)).is_one()); result = x; return true; } bv_util::bv_util(ast_manager & m): bv_recognizers(m.mk_family_id(symbol("bv"))), m_manager(m) { SASSERT(m.has_plugin(symbol("bv"))); m_plugin = static_cast(m.get_plugin(m.mk_family_id("bv"))); } app * bv_util::mk_numeral(rational const & val, sort* s) const { if (!is_bv_sort(s)) { return nullptr; } unsigned bv_size = get_bv_size(s); return mk_numeral(val, bv_size); } app * bv_util::mk_numeral(rational const & val, unsigned bv_size) const { parameter p[2] = { parameter(val), parameter(static_cast(bv_size)) }; app * r = m_manager.mk_app(get_fid(), OP_BV_NUM, 2, p, 0, nullptr); if (m_plugin->log_constant_meaning_prelude(r)) { if (bv_size % 4 == 0) { m_manager.trace_stream() << "#x"; val.display_hex(m_manager.trace_stream(), bv_size); m_manager.trace_stream() << "\n"; } else { m_manager.trace_stream() << "#b"; val.display_bin(m_manager.trace_stream(), bv_size); m_manager.trace_stream() << "\n"; } } return r; } sort * bv_util::mk_sort(unsigned bv_size) { parameter p[1] = { parameter(bv_size) }; return m_manager.mk_sort(get_fid(), BV_SORT, 1, p); } unsigned bv_util::get_int2bv_size(parameter const& p) { int sz; VERIFY(m_plugin->get_int2bv_size(1, &p, sz)); return static_cast(sz); } app * bv_util::mk_bv2int(expr* e) { sort* s = m_manager.mk_sort(m_manager.mk_family_id("arith"), INT_SORT); parameter p(s); return m_manager.mk_app(get_fid(), OP_BV2INT, 1, &p, 1, &e); } z3-z3-4.8.7/src/ast/bv_decl_plugin.h000066400000000000000000000450631356505360400171420ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: bv_decl_plugin.h Abstract: Author: Leonardo de Moura (leonardo) 2008-01-09. Revision History: --*/ #ifndef BV_DECL_PLUGIN_H_ #define BV_DECL_PLUGIN_H_ #include "ast/ast.h" enum bv_sort_kind { BV_SORT }; enum bv_op_kind { OP_BV_NUM, OP_BIT1, OP_BIT0, OP_BNEG, OP_BADD, OP_BSUB, OP_BMUL, OP_BSDIV, OP_BUDIV, OP_BSREM, OP_BUREM, OP_BSMOD, // special functions to record the division by 0 cases // these are internal functions OP_BSDIV0, OP_BUDIV0, OP_BSREM0, OP_BUREM0, OP_BSMOD0, // special functions where division by 0 has a fixed interpretation. OP_BSDIV_I, OP_BUDIV_I, OP_BSREM_I, OP_BUREM_I, OP_BSMOD_I, OP_ULEQ, OP_SLEQ, OP_UGEQ, OP_SGEQ, OP_ULT, OP_SLT, OP_UGT, OP_SGT, OP_BAND, OP_BOR, OP_BNOT, OP_BXOR, OP_BNAND, OP_BNOR, OP_BXNOR, OP_CONCAT, OP_SIGN_EXT, OP_ZERO_EXT, OP_EXTRACT, OP_REPEAT, OP_BREDOR, OP_BREDAND, OP_BCOMP, OP_BSHL, OP_BLSHR, OP_BASHR, OP_ROTATE_LEFT, OP_ROTATE_RIGHT, OP_EXT_ROTATE_LEFT, OP_EXT_ROTATE_RIGHT, OP_BUMUL_NO_OVFL, // no unsigned multiplication overflow predicate OP_BSMUL_NO_OVFL, // no signed multiplication overflow predicate OP_BSMUL_NO_UDFL, // no signed multiplication underflow predicate OP_BIT2BOOL, // predicate OP_MKBV, // bools to bv OP_INT2BV, OP_BV2INT, OP_CARRY, OP_XOR3, LAST_BV_OP }; // Assume k is a "div" operator. It returns the div0 uninterpreted function that // models the value of "div" it is underspecified (i.e., when the denominator is zero). inline bv_op_kind get_div0_op(bv_op_kind k) { switch (k) { case OP_BSDIV: return OP_BSDIV0; case OP_BUDIV: return OP_BUDIV0; case OP_BSREM: return OP_BSREM0; case OP_BUREM: return OP_BUREM0; case OP_BSMOD: return OP_BSMOD0; default: UNREACHABLE(); return LAST_BV_OP; } } // Assume decl is the declaration of a "div" operator. It returns the div0 declaration that // models the value of "div" it is underspecified (i.e., when the denominator is zero). inline func_decl * get_div0_decl(ast_manager & m, func_decl * decl) { return m.mk_func_decl(decl->get_family_id(), get_div0_op(static_cast(decl->get_decl_kind())), 0, nullptr, 1, decl->get_domain()); } class bv_decl_plugin : public decl_plugin { friend class bv_util; protected: symbol m_bv_sym; symbol m_concat_sym; symbol m_sign_extend_sym; symbol m_zero_extend_sym; symbol m_extract_sym; symbol m_rotate_left_sym; symbol m_rotate_right_sym; symbol m_repeat_sym; symbol m_bit2bool_sym; symbol m_mkbv_sym; func_decl * m_bit0; func_decl * m_bit1; func_decl * m_carry; func_decl * m_xor3; ptr_vector m_bv_sorts; sort * m_int_sort; ptr_vector m_bv_neg; ptr_vector m_bv_add; ptr_vector m_bv_sub; ptr_vector m_bv_mul; ptr_vector m_bv_sdiv; ptr_vector m_bv_udiv; ptr_vector m_bv_srem; ptr_vector m_bv_urem; ptr_vector m_bv_smod; ptr_vector m_bv_sdiv0; ptr_vector m_bv_udiv0; ptr_vector m_bv_srem0; ptr_vector m_bv_urem0; ptr_vector m_bv_smod0; ptr_vector m_bv_sdiv_i; ptr_vector m_bv_udiv_i; ptr_vector m_bv_srem_i; ptr_vector m_bv_urem_i; ptr_vector m_bv_smod_i; ptr_vector m_bv_uleq; ptr_vector m_bv_sleq; ptr_vector m_bv_ugeq; ptr_vector m_bv_sgeq; ptr_vector m_bv_ult; ptr_vector m_bv_slt; ptr_vector m_bv_ugt; ptr_vector m_bv_sgt; ptr_vector m_bv_and; ptr_vector m_bv_or; ptr_vector m_bv_not; ptr_vector m_bv_xor; ptr_vector m_bv_nand; ptr_vector m_bv_nor; ptr_vector m_bv_xnor; ptr_vector m_bv_redor; ptr_vector m_bv_redand; ptr_vector m_bv_comp; ptr_vector m_bv_mul_ovfl; ptr_vector m_bv_smul_ovfl; ptr_vector m_bv_smul_udfl; ptr_vector m_bv_shl; ptr_vector m_bv_lshr; ptr_vector m_bv_ashr; ptr_vector m_ext_rotate_left; ptr_vector m_ext_rotate_right; ptr_vector m_bv2int; ptr_vector m_int2bv; vector > m_bit2bool; ptr_vector m_mkbv; void set_manager(ast_manager * m, family_id id) override; void mk_bv_sort(unsigned bv_size); sort * get_bv_sort(unsigned bv_size); func_decl * mk_func_decl(decl_kind k, unsigned bv_size); func_decl * mk_binary(ptr_vector & decls, decl_kind k, char const * name, unsigned bv_size, bool ac, bool idempotent = false); func_decl * mk_unary(ptr_vector & decls, decl_kind k, char const * name, unsigned bv_size); func_decl * mk_pred(ptr_vector & decls, decl_kind k, char const * name, unsigned bv_size); func_decl * mk_reduction(ptr_vector & decls, decl_kind k, char const * name, unsigned bv_size); func_decl * mk_comp(unsigned bv_size); bool get_bv_size(sort * t, int & result); bool get_bv_size(expr * t, int & result); bool get_concat_size(unsigned arity, sort * const * domain, int & result); bool get_extend_size(unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, int & result); bool get_extract_size(unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, int & result); func_decl * mk_bv2int(unsigned bv_size, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain); func_decl * mk_int2bv(unsigned bv_size, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain); func_decl * mk_bit2bool(unsigned bv_size, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain); func_decl * mk_mkbv(unsigned arity, sort * const * domain); func_decl * mk_num_decl(unsigned num_parameters, parameter const * parameters, unsigned arity); void get_offset_term(app * a, expr * & t, rational & offset) const; public: bv_decl_plugin(); ~bv_decl_plugin() override {} void finalize() override; decl_plugin * mk_fresh() override { return alloc(bv_decl_plugin); } sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) override; func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) override; func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned num_args, expr * const * args, sort * range) override; bool is_value(app * e) const override; bool is_unique_value(app * e) const override { return is_value(e); } void get_op_names(svector & op_names, symbol const & logic) override; void get_sort_names(svector & sort_names, symbol const & logic) override; bool are_distinct(app* a, app* b) const override; expr * get_some_value(sort * s) override; bool get_int2bv_size(unsigned num_parameters, parameter const * parameters, int & result); bool is_considered_uninterpreted(func_decl * f) override { if (f->get_family_id() != get_family_id()) return false; switch (f->get_decl_kind()) { case OP_BSDIV0: case OP_BUDIV0: case OP_BSREM0: case OP_BUREM0: case OP_BSMOD0: return true; default: return false; } return false; } }; class bv_recognizers { family_id m_afid; public: bv_recognizers(family_id fid):m_afid(fid) {} family_id get_fid() const { return m_afid; } family_id get_family_id() const { return get_fid(); } bool is_numeral(expr const * n, rational & val) const; bool is_numeral(expr const * n, rational & val, unsigned & bv_size) const; bool is_numeral(expr const * n) const { return is_app_of(n, get_fid(), OP_BV_NUM); } bool is_allone(expr const * e) const; bool is_zero(expr const * e) const; bool is_bv_sort(sort const * s) const; bool is_bv(expr const* e) const { return is_bv_sort(get_sort(e)); } bool is_concat(expr const * e) const { return is_app_of(e, get_fid(), OP_CONCAT); } bool is_extract(func_decl const * f) const { return is_decl_of(f, get_fid(), OP_EXTRACT); } bool is_extract(expr const * e) const { return is_app_of(e, get_fid(), OP_EXTRACT); } unsigned get_extract_high(func_decl const * f) const { return f->get_parameter(0).get_int(); } unsigned get_extract_low(func_decl const * f) const { return f->get_parameter(1).get_int(); } unsigned get_extract_high(expr const * n) const { SASSERT(is_extract(n)); return get_extract_high(to_app(n)->get_decl()); } unsigned get_extract_low(expr const * n) const { SASSERT(is_extract(n)); return get_extract_low(to_app(n)->get_decl()); } bool is_extract(expr const * e, unsigned & low, unsigned & high, expr * & b) const; bool is_bv2int(expr const * e, expr * & r) const; bool is_bv_add(expr const * e) const { return is_app_of(e, get_fid(), OP_BADD); } bool is_bv_sub(expr const * e) const { return is_app_of(e, get_fid(), OP_BSUB); } bool is_bv_mul(expr const * e) const { return is_app_of(e, get_fid(), OP_BMUL); } bool is_bv_neg(expr const * e) const { return is_app_of(e, get_fid(), OP_BNEG); } bool is_bv_sdiv(expr const * e) const { return is_app_of(e, get_fid(), OP_BSDIV); } bool is_bv_udiv(expr const * e) const { return is_app_of(e, get_fid(), OP_BUDIV); } bool is_bv_srem(expr const * e) const { return is_app_of(e, get_fid(), OP_BSREM); } bool is_bv_urem(expr const * e) const { return is_app_of(e, get_fid(), OP_BUREM); } bool is_bv_smod(expr const * e) const { return is_app_of(e, get_fid(), OP_BSMOD); } bool is_bv_sdiv0(expr const * e) const { return is_app_of(e, get_fid(), OP_BSDIV0); } bool is_bv_udiv0(expr const * e) const { return is_app_of(e, get_fid(), OP_BUDIV0); } bool is_bv_srem0(expr const * e) const { return is_app_of(e, get_fid(), OP_BSREM0); } bool is_bv_urem0(expr const * e) const { return is_app_of(e, get_fid(), OP_BUREM0); } bool is_bv_smod0(expr const * e) const { return is_app_of(e, get_fid(), OP_BSMOD0); } bool is_bv_sdivi(expr const * e) const { return is_app_of(e, get_fid(), OP_BSDIV_I); } bool is_bv_udivi(expr const * e) const { return is_app_of(e, get_fid(), OP_BUDIV_I); } bool is_bv_sremi(expr const * e) const { return is_app_of(e, get_fid(), OP_BSREM_I); } bool is_bv_uremi(expr const * e) const { return is_app_of(e, get_fid(), OP_BUREM_I); } bool is_bv_smodi(expr const * e) const { return is_app_of(e, get_fid(), OP_BSMOD_I); } bool is_bv_and(expr const * e) const { return is_app_of(e, get_fid(), OP_BAND); } bool is_bv_or(expr const * e) const { return is_app_of(e, get_fid(), OP_BOR); } bool is_bv_xor(expr const * e) const { return is_app_of(e, get_fid(), OP_BXOR); } bool is_bv_nand(expr const * e) const { return is_app_of(e, get_fid(), OP_BNAND); } bool is_bv_nor(expr const * e) const { return is_app_of(e, get_fid(), OP_BNOR); } bool is_bv_not(expr const * e) const { return is_app_of(e, get_fid(), OP_BNOT); } bool is_bv_ule(expr const * e) const { return is_app_of(e, get_fid(), OP_ULEQ); } bool is_bv_sle(expr const * e) const { return is_app_of(e, get_fid(), OP_SLEQ); } bool is_bit2bool(expr const * e) const { return is_app_of(e, get_fid(), OP_BIT2BOOL); } bool is_bv2int(expr const* e) const { return is_app_of(e, get_fid(), OP_BV2INT); } bool is_int2bv(expr const* e) const { return is_app_of(e, get_fid(), OP_INT2BV); } bool is_mkbv(expr const * e) const { return is_app_of(e, get_fid(), OP_MKBV); } bool is_bv_ashr(expr const * e) const { return is_app_of(e, get_fid(), OP_BASHR); } bool is_bv_lshr(expr const * e) const { return is_app_of(e, get_fid(), OP_BLSHR); } bool is_bv_shl(expr const * e) const { return is_app_of(e, get_fid(), OP_BSHL); } bool is_sign_ext(expr const * e) const { return is_app_of(e, get_fid(), OP_SIGN_EXT); } MATCH_BINARY(is_bv_add); MATCH_BINARY(is_bv_mul); MATCH_BINARY(is_bv_sle); MATCH_BINARY(is_bv_ule); MATCH_BINARY(is_bv_ashr); MATCH_BINARY(is_bv_lshr); MATCH_BINARY(is_bv_shl); MATCH_BINARY(is_bv_urem); MATCH_BINARY(is_bv_srem); MATCH_BINARY(is_bv_sdiv); MATCH_BINARY(is_bv_udiv); MATCH_BINARY(is_bv_smod); MATCH_BINARY(is_bv_uremi); MATCH_BINARY(is_bv_sremi); MATCH_BINARY(is_bv_sdivi); MATCH_BINARY(is_bv_udivi); MATCH_BINARY(is_bv_smodi); rational norm(rational const & val, unsigned bv_size, bool is_signed) const ; rational norm(rational const & val, unsigned bv_size) const { return norm(val, bv_size, false); } bool has_sign_bit(rational const & n, unsigned bv_size) const; bool mult_inverse(rational const & n, unsigned bv_size, rational & result); }; class bv_util : public bv_recognizers { ast_manager & m_manager; bv_decl_plugin * m_plugin; public: bv_util(ast_manager & m); ast_manager & get_manager() const { return m_manager; } app * mk_numeral(rational const & val, sort* s) const; app * mk_numeral(rational const & val, unsigned bv_size) const; app * mk_numeral(uint64_t u, unsigned bv_size) const { return mk_numeral(rational(u, rational::ui64()), bv_size); } sort * mk_sort(unsigned bv_size); unsigned get_bv_size(sort const * s) const { SASSERT(is_bv_sort(s)); return static_cast(s->get_parameter(0).get_int()); } unsigned get_bv_size(expr const * n) const { return get_bv_size(m_manager.get_sort(n)); } unsigned get_int2bv_size(parameter const& p); app * mk_ule(expr * arg1, expr * arg2) { return m_manager.mk_app(get_fid(), OP_ULEQ, arg1, arg2); } app * mk_sle(expr * arg1, expr * arg2) { return m_manager.mk_app(get_fid(), OP_SLEQ, arg1, arg2); } app * mk_extract(unsigned high, unsigned low, expr * n) { parameter params[2] = { parameter(high), parameter(low) }; return m_manager.mk_app(get_fid(), OP_EXTRACT, 2, params, 1, &n); } app * mk_concat(unsigned num, expr * const * args) { return m_manager.mk_app(get_fid(), OP_CONCAT, num, args); } app * mk_concat(expr * arg1, expr * arg2) { expr * args[2] = { arg1, arg2 }; return mk_concat(2, args); } app * mk_bv_or(unsigned num, expr * const * args) { return m_manager.mk_app(get_fid(), OP_BOR, num, args); } app * mk_bv_not(expr * arg) { return m_manager.mk_app(get_fid(), OP_BNOT, arg); } app * mk_bv_xor(unsigned num, expr * const * args) { return m_manager.mk_app(get_fid(), OP_BXOR, num, args); } app * mk_bv_neg(expr * arg) { return m_manager.mk_app(get_fid(), OP_BNEG, arg); } app * mk_bv_urem(expr * arg1, expr * arg2) const { return m_manager.mk_app(get_fid(), OP_BUREM, arg1, arg2); } app * mk_bv_srem(expr * arg1, expr * arg2) const { return m_manager.mk_app(get_fid(), OP_BSREM, arg1, arg2); } app * mk_bv_add(expr * arg1, expr * arg2) const { return m_manager.mk_app(get_fid(), OP_BADD, arg1, arg2); } app * mk_bv_sub(expr * arg1, expr * arg2) const { return m_manager.mk_app(get_fid(), OP_BSUB, arg1, arg2); } app * mk_bv_mul(expr * arg1, expr * arg2) const { return m_manager.mk_app(get_fid(), OP_BMUL, arg1, arg2); } app * mk_zero_extend(unsigned n, expr* e) { parameter p(n); return m_manager.mk_app(get_fid(), OP_ZERO_EXT, 1, &p, 1, &e); } app * mk_sign_extend(unsigned n, expr* e) { parameter p(n); return m_manager.mk_app(get_fid(), OP_SIGN_EXT, 1, &p, 1, &e); } app * mk_bv_shl(expr* arg1, expr* arg2) { return m_manager.mk_app(get_fid(), OP_BSHL, arg1, arg2); } app * mk_bv_ashr(expr* arg1, expr* arg2) { return m_manager.mk_app(get_fid(), OP_BASHR, arg1, arg2); } app * mk_bv_lshr(expr* arg1, expr* arg2) { return m_manager.mk_app(get_fid(), OP_BLSHR, arg1, arg2); } app * mk_bv2int(expr* e); app * mk_bvsmul_no_ovfl(expr* m, expr* n) { return m_manager.mk_app(get_fid(), OP_BSMUL_NO_OVFL, n, m); } app * mk_bvsmul_no_udfl(expr* m, expr* n) { return m_manager.mk_app(get_fid(), OP_BSMUL_NO_UDFL, n, m); } app * mk_bvumul_no_ovfl(expr* m, expr* n) { return m_manager.mk_app(get_fid(), OP_BUMUL_NO_OVFL, n, m); } private: void log_bv_from_exprs(app * r, unsigned n, expr* const* es) { if (m_manager.has_trace_stream()) { for (unsigned i = 0; i < n; ++i) { if (!m_manager.is_true(es[i]) && !m_manager.is_false(es[i])) return; } if (m_plugin->log_constant_meaning_prelude(r)) { if (n % 4 == 0) { m_manager.trace_stream() << " #x"; m_manager.trace_stream() << std::hex; uint8_t hexDigit = 0; unsigned curLength = (4 - n % 4) % 4; for (unsigned i = 0; i < n; ++i) { hexDigit <<= 1; ++curLength; if (m_manager.is_true(es[i])) { hexDigit |= 1; } if (curLength == 4) { m_manager.trace_stream() << hexDigit; hexDigit = 0; } } m_manager.trace_stream() << std::dec; } else { m_manager.trace_stream() << " #b"; for (unsigned i = 0; i < n; ++i) { m_manager.trace_stream() << (m_manager.is_true(es[i]) ? 1 : 0); } } m_manager.trace_stream() << ")\n"; } } } public: app * mk_bv(unsigned n, expr* const* es) { app * r = m_manager.mk_app(get_fid(), OP_MKBV, n, es); log_bv_from_exprs(r, n, es); return r; } }; #endif /* BV_DECL_PLUGIN_H_ */ z3-z3-4.8.7/src/ast/csp_decl_plugin.cpp000066400000000000000000000364551356505360400176600ustar00rootroot00000000000000/*++ Copyright (c) 2018 Microsoft Corporation Module Name: csp_decl_plugin.h Abstract: Declarations used for a job-shop scheduling domain. Author: Nikolaj Bjorner (nbjorner) 2018-8-9 Revision History: --*/ #include "ast/csp_decl_plugin.h" #include "ast/arith_decl_plugin.h" void csp_decl_plugin::set_manager(ast_manager* m, family_id fid) { decl_plugin::set_manager(m, fid); m_int_sort = m_manager->mk_sort(m_manager->mk_family_id("arith"), INT_SORT); m_alist_sort = m_manager->mk_sort(symbol("AList"), sort_info(m_family_id, ALIST_SORT)); m_job_sort = m_manager->mk_sort(symbol("Job"), sort_info(m_family_id, JOB_SORT)); m_resource_sort = m_manager->mk_sort(symbol("Resource"), sort_info(m_family_id, RESOURCE_SORT)); m_manager->inc_ref(m_int_sort); m_manager->inc_ref(m_resource_sort); m_manager->inc_ref(m_job_sort); m_manager->inc_ref(m_alist_sort); } void csp_decl_plugin::finalize() { m_manager->dec_ref(m_alist_sort); m_manager->dec_ref(m_job_sort); m_manager->dec_ref(m_resource_sort); m_manager->dec_ref(m_int_sort); } sort * csp_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) { if (num_parameters != 0) { m_manager->raise_exception("no parameters expected with job-shop sort"); } switch (static_cast(k)) { case JOB_SORT: return m_job_sort; case RESOURCE_SORT: return m_resource_sort; case ALIST_SORT: return m_alist_sort; default: UNREACHABLE(); return nullptr; } } func_decl * csp_decl_plugin::mk_func_decl( decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort *) { symbol name; sort* rng = nullptr; switch (static_cast(k)) { case OP_JS_JOB: check_arity(arity); check_index1(num_parameters, parameters); name = symbol("job"); rng = m_job_sort; break; case OP_JS_RESOURCE: check_arity(arity); check_index1(num_parameters, parameters); name = symbol("resource"); rng = m_resource_sort; break; case OP_JS_RESOURCE_MAKESPAN: if (arity != 1 || domain[0] != m_resource_sort) m_manager->raise_exception("makespan expects a resource argument"); name = symbol("makespan"); rng = m_int_sort; break; case OP_JS_START: if (arity != 1 || domain[0] != m_job_sort) m_manager->raise_exception("start expects a job argument"); if (num_parameters > 0) m_manager->raise_exception("no parameters"); name = symbol("job-start"); rng = m_int_sort; break; case OP_JS_END: if (arity != 1 || domain[0] != m_job_sort) m_manager->raise_exception("resource expects a job argument"); if (num_parameters > 0) m_manager->raise_exception("no parameters"); name = symbol("job-end"); rng = m_int_sort; break; case OP_JS_JOB2RESOURCE: if (arity != 1 || domain[0] != m_job_sort) m_manager->raise_exception("job2resource expects a job argument"); if (num_parameters > 0) m_manager->raise_exception("no parameters"); name = symbol("job2resource"); rng = m_resource_sort; break; case OP_JS_MODEL: // has no parameters // all arguments are of sort alist name = symbol("js-model"); rng = m_manager->mk_bool_sort(); break; case OP_JS_JOB_RESOURCE: if (arity != 6) m_manager->raise_exception("add-job-resource expects 6 arguments"); if (domain[0] != m_job_sort) m_manager->raise_exception("first argument of add-job-resource expects should be a job"); if (domain[1] != m_resource_sort) m_manager->raise_exception("second argument of add-job-resource expects should be a resource"); if (domain[2] != m_int_sort) m_manager->raise_exception("3rd argument of add-job-resource expects should be an integer"); if (domain[3] != m_int_sort) m_manager->raise_exception("4th argument of add-job-resource expects should be an integer"); if (domain[4] != m_int_sort) m_manager->raise_exception("5th argument of add-job-resource expects should be an integer"); if (domain[5] != m_alist_sort) m_manager->raise_exception("6th argument of add-job-resource should be a list of properties"); name = symbol("add-job-resource"); rng = m_alist_sort; break; case OP_JS_RESOURCE_AVAILABLE: if (arity != 6) m_manager->raise_exception("add-resource-available expects 6 arguments"); if (domain[0] != m_resource_sort) m_manager->raise_exception("first argument of add-resource-available expects should be a resource"); if (domain[2] != m_int_sort) m_manager->raise_exception("2nd argument of add-resource-available expects should be an integer"); if (domain[2] != m_int_sort) m_manager->raise_exception("3rd argument of add-resource-available expects should be an integer"); if (domain[3] != m_int_sort) m_manager->raise_exception("4th argument of add-resource-available expects should be an integer"); if (domain[4] != m_int_sort) m_manager->raise_exception("5th argument of add-resource-available expects should be an integer"); if (domain[5] != m_alist_sort) m_manager->raise_exception("6th argument of add-resource-available should be a list of properties"); name = symbol("add-resource-available"); rng = m_alist_sort; break; case OP_JS_JOB_PREEMPTABLE: if (arity != 1 || domain[0] != m_job_sort) m_manager->raise_exception("set-preemptable expects one argument, which is a job"); name = symbol("set-preemptable"); rng = m_alist_sort; break; case OP_JS_PROPERTIES: if (arity != 0) m_manager->raise_exception("js-properties takes no arguments"); for (unsigned i = 0; i < num_parameters; ++i) { if (!parameters[i].is_symbol()) m_manager->raise_exception("js-properties expects a list of keyword parameters"); } name = symbol("js-properties"); rng = m_alist_sort; break; case OP_JS_JOB_GOAL: if (arity != 1 || domain[0] != m_job_sort) m_manager->raise_exception("add-job-goal expects one argument, which is a job"); if (num_parameters != 2 || !parameters[0].is_symbol() || !parameters[1].is_int()) m_manager->raise_exception("add-job-goal expects one symbol and one integer parameter"); name = symbol("add-job-goal"); rng = m_alist_sort; break; case OP_JS_OBJECTIVE: if (arity != 0) m_manager->raise_exception("add-optimization-objective expects no arguments"); if (num_parameters != 1 || !parameters[0].is_symbol()) m_manager->raise_exception("add-optimization-objective expects one symbol parameter"); name = symbol("add-optimization-objective"); rng = m_alist_sort; break; default: UNREACHABLE(); return nullptr; } return m_manager->mk_func_decl(name, arity, domain, rng, func_decl_info(m_family_id, k, num_parameters, parameters)); } void csp_decl_plugin::check_arity(unsigned arity) { if (arity > 0) m_manager->raise_exception("csp variables use parameters only and take no arguments"); } void csp_decl_plugin::check_index1(unsigned num_parameters, parameter const* ps) { if (num_parameters != 1 || !ps[0].is_int()) m_manager->raise_exception("csp variable expects a single integer parameter"); } void csp_decl_plugin::check_index2(unsigned num_parameters, parameter const* ps) { if (num_parameters != 2 || !ps[0].is_int() || !ps[1].is_int()) m_manager->raise_exception("csp variable expects two integer parameters"); } bool csp_decl_plugin::is_value(app * e) const { return is_app_of(e, m_family_id, OP_JS_JOB) || is_app_of(e, m_family_id, OP_JS_RESOURCE); } void csp_decl_plugin::get_op_names(svector & op_names, symbol const & logic) { if (logic == symbol("CSP")) { op_names.push_back(builtin_name("job", OP_JS_JOB)); op_names.push_back(builtin_name("resource", OP_JS_RESOURCE)); op_names.push_back(builtin_name("makespan", OP_JS_RESOURCE_MAKESPAN)); op_names.push_back(builtin_name("job-start", OP_JS_START)); op_names.push_back(builtin_name("job-end", OP_JS_END)); op_names.push_back(builtin_name("job2resource", OP_JS_JOB2RESOURCE)); op_names.push_back(builtin_name("js-model", OP_JS_MODEL)); op_names.push_back(builtin_name("add-job-resource", OP_JS_JOB_RESOURCE)); op_names.push_back(builtin_name("add-resource-available", OP_JS_RESOURCE_AVAILABLE)); op_names.push_back(builtin_name("set-preemptable", OP_JS_JOB_PREEMPTABLE)); op_names.push_back(builtin_name("js-properties", OP_JS_PROPERTIES)); op_names.push_back(builtin_name("add-job-goal", OP_JS_JOB_GOAL)); op_names.push_back(builtin_name("add-optimization-objective", OP_JS_OBJECTIVE)); } } void csp_decl_plugin::get_sort_names(svector & sort_names, symbol const & logic) { if (logic == symbol("CSP")) { sort_names.push_back(builtin_name("Job", JOB_SORT)); sort_names.push_back(builtin_name("Resource", RESOURCE_SORT)); } } expr * csp_decl_plugin::get_some_value(sort * s) { parameter p(0); if (is_sort_of(s, m_family_id, JOB_SORT)) return m_manager->mk_const(mk_func_decl(OP_JS_JOB, 1, &p, 0, nullptr, nullptr)); if (is_sort_of(s, m_family_id, RESOURCE_SORT)) return m_manager->mk_const(mk_func_decl(OP_JS_RESOURCE, 1, &p, 0, nullptr, nullptr)); UNREACHABLE(); return nullptr; } csp_util::csp_util(ast_manager& m): m(m) { m_fid = m.mk_family_id("csp"); m_plugin = static_cast(m.get_plugin(m_fid)); } sort* csp_util::mk_job_sort() { return m_plugin->mk_job_sort(); } sort* csp_util::mk_resource_sort() { return m_plugin->mk_resource_sort(); } app* csp_util::mk_job(unsigned j) { parameter p(j); return m.mk_const(m.mk_func_decl(m_fid, OP_JS_JOB, 1, &p, 0, (sort*const*)nullptr, nullptr)); } unsigned csp_util::job2id(expr* j) { if (is_app_of(j, m_fid, OP_JS_JOB)) { return to_app(j)->get_decl()->get_parameter(0).get_int(); } SASSERT(is_app_of(j, m_fid, OP_JS_START) || is_app_of(j, m_fid, OP_JS_END) || is_app_of(j, m_fid, OP_JS_JOB2RESOURCE)); return job2id(to_app(j)->get_arg(0)); } app* csp_util::mk_resource(unsigned r) { parameter p(r); return m.mk_const(m.mk_func_decl(m_fid, OP_JS_RESOURCE, 1, &p, 0, (sort*const*)nullptr, nullptr)); } unsigned csp_util::resource2id(expr* r) { SASSERT(is_app_of(r, m_fid, OP_JS_RESOURCE)); return to_app(r)->get_decl()->get_parameter(0).get_int(); } app* csp_util::mk_start(unsigned j) { app_ref job(mk_job(j), m); sort* js = m.get_sort(job); return m.mk_app(m.mk_func_decl(m_fid, OP_JS_START, 0, nullptr, 1, &js, nullptr), job); } app* csp_util::mk_end(unsigned j) { app_ref job(mk_job(j), m); sort* js = m.get_sort(job); return m.mk_app(m.mk_func_decl(m_fid, OP_JS_END, 0, nullptr, 1, &js, nullptr), job); } app* csp_util::mk_job2resource(unsigned j) { app_ref job(mk_job(j), m); sort* js = m.get_sort(job); return m.mk_app(m.mk_func_decl(m_fid, OP_JS_JOB2RESOURCE, 0, nullptr, 1, &js, nullptr), job); } app* csp_util::mk_makespan(unsigned r) { app_ref resource(mk_resource(r), m); sort* rs = m.get_sort(resource); return m.mk_app(m.mk_func_decl(m_fid, OP_JS_RESOURCE_MAKESPAN, 0, nullptr, 1, &rs, nullptr), resource); } bool csp_util::is_resource(expr* e, unsigned& r) { return is_app_of(e, m_fid, OP_JS_RESOURCE) && (r = resource2id(e), true); } bool csp_util::is_makespan(expr * e, unsigned& r) { return is_app_of(e, m_fid, OP_JS_RESOURCE_MAKESPAN) && is_resource(to_app(e)->get_arg(0), r); } bool csp_util::is_job(expr* e, unsigned& j) { return is_app_of(e, m_fid, OP_JS_JOB) && (j = job2id(e), true); } bool csp_util::is_job2resource(expr* e, unsigned& j) { return is_app_of(e, m_fid, OP_JS_JOB2RESOURCE) && (j = job2id(e), true); } bool csp_util::is_add_resource_available(expr * e, expr *& res, unsigned& loadpct, unsigned& cap_time, uint64_t& start, uint64_t& end, svector& properties) { if (!is_app_of(e, m_fid, OP_JS_RESOURCE_AVAILABLE)) return false; res = to_app(e)->get_arg(0); arith_util a(m); rational r; if (!a.is_numeral(to_app(e)->get_arg(1), r) || !r.is_unsigned()) return false; loadpct = r.get_unsigned(); if (!a.is_numeral(to_app(e)->get_arg(2), r) || !r.is_unsigned()) return false; cap_time = r.get_unsigned(); if (!a.is_numeral(to_app(e)->get_arg(3), r) || !r.is_uint64()) return false; start = r.get_uint64(); if (!a.is_numeral(to_app(e)->get_arg(4), r) || !r.is_uint64()) return false; end = r.get_uint64(); if (!is_js_properties(to_app(e)->get_arg(5), properties)) return false; return true; } bool csp_util::is_add_job_resource(expr * e, expr *& job, expr*& res, unsigned& loadpct, uint64_t& capacity, uint64_t& end, svector& properties) { if (!is_app_of(e, m_fid, OP_JS_JOB_RESOURCE)) return false; job = to_app(e)->get_arg(0); res = to_app(e)->get_arg(1); arith_util a(m); rational r; if (!a.is_numeral(to_app(e)->get_arg(2), r) || !r.is_unsigned()) return false; loadpct = r.get_unsigned(); if (!a.is_numeral(to_app(e)->get_arg(3), r) || !r.is_uint64()) return false; capacity = r.get_uint64(); if (!a.is_numeral(to_app(e)->get_arg(4), r) || !r.is_uint64()) return false; end = r.get_uint64(); if (!is_js_properties(to_app(e)->get_arg(5), properties)) return false; return true; } bool csp_util::is_set_preemptable(expr* e, expr *& job) { if (!is_app_of(e, m_fid, OP_JS_JOB_PREEMPTABLE)) return false; job = to_app(e)->get_arg(0); return true; } bool csp_util::is_js_properties(expr* e, svector& properties) { if (!is_app_of(e, m_fid, OP_JS_PROPERTIES)) return false; unsigned sz = to_app(e)->get_decl()->get_num_parameters(); for (unsigned i = 0; i < sz; ++i) { properties.push_back(to_app(e)->get_decl()->get_parameter(i).get_symbol()); } return true; } bool csp_util::is_job_goal(expr* e, js_job_goal& goal, unsigned& level, expr*& job) { if (!is_app_of(e, m_fid, OP_JS_JOB_GOAL)) return false; SASSERT(2 == to_app(e)->get_decl()->get_num_parameters()); SASSERT(1 == to_app(e)->get_num_args()); symbol g = to_app(e)->get_decl()->get_parameter(0).get_symbol(); level = to_app(e)->get_decl()->get_parameter(1).get_int(); if (g == ":earliest-end-time" || g == "earliest-end-time") goal = JS_JOB_GOAL_EARLIEST_END_TIME; else if (g == ":latest-start-time" || g == "latest-start-time") goal = JS_JOB_GOAL_LATEST_START_TIME; else return false; job = to_app(e)->get_arg(0); return true; } bool csp_util::is_objective(expr* e, js_optimization_objective& objective) { if (!is_app_of(e, m_fid, OP_JS_OBJECTIVE)) return false; SASSERT(1 == to_app(e)->get_decl()->get_num_parameters()); symbol obj = to_app(e)->get_decl()->get_parameter(0).get_symbol(); if (obj == ":duration" || obj == "duration") objective = JS_OBJECTIVE_DURATION; else if (obj == ":priority" || obj == "priority") objective = JS_OBJECTIVE_PRIORITY; else return false; return true; } z3-z3-4.8.7/src/ast/csp_decl_plugin.h000066400000000000000000000146141356505360400173160ustar00rootroot00000000000000/*++ Copyright (c) 2018 Microsoft Corporation Module Name: csp_decl_plugin.h Abstract: Declarations used for a job-shop scheduling domain. The job-shop domain comprises of constants job(j), resource(r) It finds values to variables: - start(j), end(j), job2resource(j) It assumes a background of: - resources : Job -> Resource -> Int * LoadPct - time to run job j on resource r assuming LoadPct - runtime : Job -> Int - time to run job j if not associated with any resource - capacity : Resource -> Int -> LoadPct - capacity of resource r at time t, given as sequence of time intervals // assume each job has at least one resource associated with it. // introduce a dummy resource if needed. // Theory: end(j) - start(j) = time-to-execute(j) time-to-execute(j) := time-to-execute(j, resource(j)) otherwise time-to-execute(j, r) := (T - start(j)) where capacity(j,r) = sum_{t = start(j)}^{T} load(loadpct(j,r), r, t) capacity(j, r) := cap where (cap, loadpct) = resources j r loadpct(j, r) := loadpct where (cap, loadpct) = resources j r load(loadpct, r, t) := min(capacity r t, loadpct) / loadpct capacity(r, t) >= sum_{j | job-on-resource(j, r, t) } min(capacity r t, loadpct(j, r)) // Macros: job-on-resource(j, r) := r = resource(j); job-on-resource(j, r, t) := (job-on-resource(j, r) & start(j) <= t <= end(j)); start_min(j, t) := start(j) >= t; end_max(j, t) := end(j) <= t; job_link(j1, j2, startstart, hard) := start(j1) = start(j2); job_link(j1, j2, startstart, soft) := start(j1) <= start(j2); job_link(j1, j2, endend, hard) := end(j1) = end(j2); job_link(j1, j2, endend, soft) := end(j2) <= end(j1); job_link(j1, j2, endstart, hard) := end(j1) = start(j2); job_link(j1, j2, endstart, soft) := end(j2) <= start(j1); job_link(j1, j2, startend, hard) := end(j2) = start(j1); job_link(j1, j2, startend, soft) := end(j1) <= start(j2); job_delay(j1, j2, t) := end(j1) + t <= end(j2); job_on_same_resource(j1, j2) := resource(j1) = resource(j2); job_not_on_same_resource(j1, j2) := resource(j1) != resource(j2); job_time_intersect(j1, j2) := start(j1) <= end(j2) <= end(j1) || start(j2) <= end(j1) <= end(j2); job-on-resource(j, r, t) => job-property(j) = null or job_property(j) in working_time_property(r, t); Author: Nikolaj Bjorner (nbjorner) 2018-8-9 Revision History: --*/ #pragma once #include "ast/ast.h" enum js_sort_kind { JOB_SORT, RESOURCE_SORT, ALIST_SORT }; enum js_op_kind { OP_JS_JOB, // value of type job OP_JS_RESOURCE, // value of type resource OP_JS_RESOURCE_MAKESPAN, // makespan of resource: the minimal resource time required for assigned jobs. OP_JS_START, // start time of a job OP_JS_END, // end time of a job OP_JS_JOB2RESOURCE, // resource associated with job OP_JS_MODEL, // jobscheduler model OP_JS_JOB_RESOURCE, // model declaration for job assignment to resource OP_JS_JOB_PREEMPTABLE, // model declaration for whether job is pre-emptable OP_JS_RESOURCE_AVAILABLE, // model declaration for availability intervals of resource OP_JS_PROPERTIES, // model declaration of a set of properties. Each property is a keyword. OP_JS_JOB_GOAL, // job goal objective :earliest-end-time or :latest-start-time OP_JS_OBJECTIVE // duration or completion-time }; enum js_job_goal { JS_JOB_GOAL_EARLIEST_END_TIME, JS_JOB_GOAL_LATEST_START_TIME }; enum js_optimization_objective { JS_OBJECTIVE_DURATION, JS_OBJECTIVE_PRIORITY }; class csp_decl_plugin : public decl_plugin { public: csp_decl_plugin() {} ~csp_decl_plugin() override {} void finalize() override; void set_manager(ast_manager* m, family_id fid) override; decl_plugin * mk_fresh() override { return alloc(csp_decl_plugin); } sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) override; func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) override; bool is_value(app * e) const override; bool is_unique_value(app * e) const override { return is_value(e); } void get_op_names(svector & op_names, symbol const & logic) override; void get_sort_names(svector & sort_names, symbol const & logic) override; expr * get_some_value(sort * s) override; sort * mk_job_sort() const { return m_job_sort; } sort * mk_resource_sort() const { return m_resource_sort; } sort * mk_alist_sort() const { return m_alist_sort; } private: sort* m_job_sort; sort* m_resource_sort; sort* m_alist_sort; sort* m_int_sort; void check_arity(unsigned arity); void check_index1(unsigned n, parameter const* ps); void check_index2(unsigned n, parameter const* ps); }; class csp_util { ast_manager& m; family_id m_fid; csp_decl_plugin* m_plugin; public: csp_util(ast_manager& m); sort* mk_job_sort(); sort* mk_resource_sort(); app* mk_job(unsigned j); app* mk_resource(unsigned r); app* mk_start(unsigned j); app* mk_end(unsigned j); app* mk_job2resource(unsigned j); app* mk_makespan(unsigned r); bool is_job(expr* e, unsigned& j); bool is_job2resource(expr* e, unsigned& j); 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& 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); bool is_job_goal(expr* e, js_job_goal& goal, unsigned& level, expr*& job); bool is_objective(expr* e, js_optimization_objective& objective); private: unsigned job2id(expr* j); unsigned resource2id(expr* r); }; z3-z3-4.8.7/src/ast/datatype_decl_plugin.cpp000066400000000000000000001503761356505360400207050ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: datatype_decl_plugin.cpp Abstract: Author: Nikolaj Bjorner (nbjorner) 2017-9-1 Revision History: --*/ #include "util/warning.h" #include "ast/array_decl_plugin.h" #include "ast/datatype_decl_plugin.h" #include "ast/ast_smt2_pp.h" #include "ast/ast_translation.h" namespace datatype { void accessor::fix_range(sort_ref_vector const& dts) { if (!m_range) { m_range = dts[m_index]; } } func_decl_ref accessor::instantiate(sort_ref_vector const& ps) const { ast_manager& m = ps.get_manager(); unsigned n = ps.size(); SASSERT(m_range); SASSERT(n == get_def().params().size()); sort_ref range(m.substitute(m_range, n, get_def().params().c_ptr(), ps.c_ptr()), m); sort_ref src(get_def().instantiate(ps)); sort* srcs[1] = { src.get() }; parameter pas[2] = { parameter(name()), parameter(get_constructor().name()) }; return func_decl_ref(m.mk_func_decl(u().get_family_id(), OP_DT_ACCESSOR, 2, pas, 1, srcs, range), m); } func_decl_ref accessor::instantiate(sort* dt) const { sort_ref_vector sorts = get_def().u().datatype_params(dt); return instantiate(sorts); } def const& accessor::get_def() const { return m_constructor->get_def(); } util& accessor::u() const { return m_constructor->u(); } accessor* accessor::translate(ast_translation& tr) { return alloc(accessor, tr.to(), name(), to_sort(tr(m_range.get()))); } constructor::~constructor() { for (accessor* a : m_accessors) dealloc(a); m_accessors.reset(); } util& constructor::u() const { return m_def->u(); } func_decl_ref constructor::instantiate(sort_ref_vector const& ps) const { ast_manager& m = ps.get_manager(); sort_ref_vector domain(m); for (accessor const* a : accessors()) { domain.push_back(a->instantiate(ps)->get_range()); } sort_ref range = get_def().instantiate(ps); parameter pas[1] = { parameter(name()) }; return func_decl_ref(m.mk_func_decl(u().get_family_id(), OP_DT_CONSTRUCTOR, 1, pas, domain.size(), domain.c_ptr(), range), m); } func_decl_ref constructor::instantiate(sort* dt) const { sort_ref_vector sorts = get_def().u().datatype_params(dt); return instantiate(sorts); } constructor* constructor::translate(ast_translation& tr) { constructor* result = alloc(constructor, m_name, m_recognizer); for (accessor* a : *this) { result->add(a->translate(tr)); } return result; } sort_ref def::instantiate(sort_ref_vector const& sorts) const { sort_ref s(m); TRACE("datatype", tout << "instantiate " << m_name << "\n";); if (!m_sort) { vector ps; ps.push_back(parameter(m_name)); for (sort * s : m_params) ps.push_back(parameter(s)); m_sort = m.mk_sort(u().get_family_id(), DATATYPE_SORT, ps.size(), ps.c_ptr()); } if (sorts.empty()) { return m_sort; } return sort_ref(m.substitute(m_sort, sorts.size(), m_params.c_ptr(), sorts.c_ptr()), m); } def* def::translate(ast_translation& tr, util& u) { SASSERT(&u.get_manager() == &tr.to()); sort_ref_vector ps(tr.to()); for (sort* p : m_params) { ps.push_back(to_sort(tr(p))); } def* result = alloc(def, tr.to(), u, m_name, m_class_id, ps.size(), ps.c_ptr()); for (constructor* c : *this) { result->add(c->translate(tr)); } if (m_sort) result->m_sort = to_sort(tr(m_sort.get())); return result; } enum status { GRAY, BLACK }; namespace param_size { void size::dec_ref() { --m_ref; if (m_ref == 0) dealloc(this); } size* size::mk_offset(sort_size const& s) { return alloc(offset, s); } size* size::mk_param(sort_ref& p) { return alloc(sparam, p); } size* size::mk_plus(size* a1, size* a2) { return alloc(plus, a1, a2); } size* size::mk_times(size* a1, size* a2) { return alloc(times, a1, a2); } size* size::mk_times(ptr_vector& szs) { if (szs.empty()) return mk_offset(sort_size(1)); if (szs.size() == 1) return szs[0]; size* r = szs[0]; for (unsigned i = 1; i < szs.size(); ++i) { r = mk_times(r, szs[i]); } return r; } size* size::mk_plus(ptr_vector& szs) { if (szs.empty()) return mk_offset(sort_size(0)); if (szs.size() == 1) return szs[0]; size* r = szs[0]; for (unsigned i = 1; i < szs.size(); ++i) { r = mk_plus(r, szs[i]); } return r; } 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 { plugin::~plugin() { finalize(); } void plugin::finalize() { for (auto& kv : m_defs) { dealloc(kv.m_value); } m_defs.reset(); m_util = nullptr; // force deletion } util & plugin::u() const { SASSERT(m_manager); SASSERT(m_family_id != null_family_id); if (m_util.get() == nullptr) { m_util = alloc(util, *m_manager); } return *(m_util.get()); } void plugin::inherit(decl_plugin* other_p, ast_translation& tr) { plugin* p = dynamic_cast(other_p); svector names; ptr_vector new_defs; SASSERT(p); for (auto& kv : p->m_defs) { def* d = kv.m_value; if (!m_defs.contains(kv.m_key)) { names.push_back(kv.m_key); new_defs.push_back(d->translate(tr, u())); } } for (def* d : new_defs) m_defs.insert(d->name(), d); m_class_id = m_defs.size(); u().compute_datatype_size_functions(names); } struct invalid_datatype {}; sort * plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) { try { if (k != DATATYPE_SORT) { TRACE("datatype", tout << "invalid kind parameter to datatype\n";); throw invalid_datatype(); } if (num_parameters < 1) { TRACE("datatype", tout << "at least one parameter expected to datatype declaration\n";); throw invalid_datatype(); } parameter const & name = parameters[0]; if (!name.is_symbol()) { TRACE("datatype", tout << "expected symol parameter at position " << 0 << " got: " << name << "\n";); throw invalid_datatype(); } for (unsigned i = 1; i < num_parameters; ++i) { parameter const& s = parameters[i]; if (!s.is_ast() || !is_sort(s.get_ast())) { TRACE("datatype", tout << "expected sort parameter at position " << i << " got: " << s << "\n";); throw invalid_datatype(); } } sort* s = m_manager->mk_sort(name.get_symbol(), sort_info(m_family_id, k, num_parameters, parameters, true)); def* d = nullptr; if (m_defs.find(s->get_name(), d) && d->sort_size()) { obj_map S; for (unsigned i = 0; i + 1 < num_parameters; ++i) { sort* r = to_sort(parameters[i + 1].get_ast()); TRACE("datatype", tout << "inserting " << mk_ismt2_pp(r, *m_manager) << " " << r->get_num_elements() << "\n";); S.insert(d->params()[i], r->get_num_elements()); } sort_size ts = d->sort_size()->eval(S); TRACE("datatype", tout << name << " has size " << ts << "\n";); s->set_num_elements(ts); } else { TRACE("datatype", tout << "not setting size for " << name << "\n";); } return s; } catch (const invalid_datatype &) { m_manager->raise_exception("invalid datatype"); return nullptr; } } func_decl * plugin::mk_update_field( unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { decl_kind k = OP_DT_UPDATE_FIELD; ast_manager& m = *m_manager; if (num_parameters != 1 || !parameters[0].is_ast()) { m.raise_exception("invalid parameters for datatype field update"); return nullptr; } if (arity != 2) { m.raise_exception("invalid number of arguments for datatype field update"); return nullptr; } func_decl* acc = nullptr; if (is_func_decl(parameters[0].get_ast())) { acc = to_func_decl(parameters[0].get_ast()); } if (acc && !u().is_accessor(acc)) { acc = nullptr; } if (!acc) { m.raise_exception("datatype field update requires a datatype accessor as the second argument"); return nullptr; } sort* dom = acc->get_domain(0); sort* rng = acc->get_range(); if (dom != domain[0]) { m.raise_exception("first argument to field update should be a data-type"); return nullptr; } if (rng != domain[1]) { std::ostringstream buffer; buffer << "second argument to field update should be " << mk_ismt2_pp(rng, m) << " instead of " << mk_ismt2_pp(domain[1], m); m.raise_exception(buffer.str()); return nullptr; } range = domain[0]; func_decl_info info(m_family_id, k, num_parameters, parameters); return m.mk_func_decl(symbol("update-field"), arity, domain, range, info); } #define VALIDATE_PARAM(_pred_) if (!(_pred_)) m_manager->raise_exception("invalid parameter to datatype function " #_pred_); func_decl * decl::plugin::mk_constructor(unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { ast_manager& m = *m_manager; VALIDATE_PARAM(num_parameters == 1 && parameters[0].is_symbol() && range && u().is_datatype(range)); // we blindly trust other conditions are met, including domain types. symbol name = parameters[0].get_symbol(); func_decl_info info(m_family_id, OP_DT_CONSTRUCTOR, num_parameters, parameters); info.m_private_parameters = true; return m.mk_func_decl(name, arity, domain, range, info); } func_decl * decl::plugin::mk_recognizer(unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort *) { ast_manager& m = *m_manager; VALIDATE_PARAM(arity == 1 && num_parameters == 2 && parameters[1].is_symbol() && parameters[0].is_ast() && is_func_decl(parameters[0].get_ast())); VALIDATE_PARAM(u().is_datatype(domain[0])); // blindly trust that parameter is a constructor sort* range = m_manager->mk_bool_sort(); func_decl_info info(m_family_id, OP_DT_RECOGNISER, num_parameters, parameters); info.m_private_parameters = true; return m.mk_func_decl(symbol(parameters[1].get_symbol()), arity, domain, range, info); } func_decl * decl::plugin::mk_is(unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort *) { ast_manager& m = *m_manager; VALIDATE_PARAM(arity == 1 && num_parameters == 1 && parameters[0].is_ast() && is_func_decl(parameters[0].get_ast())); VALIDATE_PARAM(u().is_datatype(domain[0])); // blindly trust that parameter is a constructor sort* range = m_manager->mk_bool_sort(); func_decl_info info(m_family_id, OP_DT_IS, num_parameters, parameters); info.m_private_parameters = true; return m.mk_func_decl(symbol("is"), arity, domain, range, info); } func_decl * decl::plugin::mk_accessor(unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { ast_manager& m = *m_manager; VALIDATE_PARAM(arity == 1 && num_parameters == 2 && parameters[0].is_symbol() && parameters[1].is_symbol()); VALIDATE_PARAM(u().is_datatype(domain[0])); SASSERT(range); func_decl_info info(m_family_id, OP_DT_ACCESSOR, num_parameters, parameters); info.m_private_parameters = true; symbol name = parameters[0].get_symbol(); return m.mk_func_decl(name, arity, domain, range, info); } func_decl * decl::plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { switch (k) { case OP_DT_CONSTRUCTOR: return mk_constructor(num_parameters, parameters, arity, domain, range); case OP_DT_RECOGNISER: return mk_recognizer(num_parameters, parameters, arity, domain, range); case OP_DT_IS: return mk_is(num_parameters, parameters, arity, domain, range); case OP_DT_ACCESSOR: return mk_accessor(num_parameters, parameters, arity, domain, range); case OP_DT_UPDATE_FIELD: return mk_update_field(num_parameters, parameters, arity, domain, range); default: m_manager->raise_exception("invalid datatype operator kind"); return nullptr; } } def* plugin::mk(symbol const& name, unsigned n, sort * const * params) { ast_manager& m = *m_manager; return alloc(def, m, u(), name, m_class_id, n, params); } void plugin::end_def_block() { ast_manager& m = *m_manager; sort_ref_vector sorts(m); for (symbol const& s : m_def_block) { def const& d = *m_defs[s]; sort_ref_vector ps(m); sorts.push_back(d.instantiate(ps)); } for (symbol const& s : m_def_block) { def& d = *m_defs[s]; for (constructor* c : d) { for (accessor* a : *c) { a->fix_range(sorts); } } } if (!u().is_well_founded(sorts.size(), sorts.c_ptr())) { m_manager->raise_exception("datatype is not well-founded"); } if (!u().is_covariant(sorts.size(), sorts.c_ptr())) { m_manager->raise_exception("datatype is not co-variant"); } array_util autil(m); for (sort* s : sorts) { for (constructor const* c : get_def(s)) { for (accessor const* a : *c) { if (autil.is_array(a->range())) { if (sorts.contains(get_array_range(a->range()))) { m_has_nested_arrays = true; } } } } } u().compute_datatype_size_functions(m_def_block); for (symbol const& s : m_def_block) { sort_ref_vector ps(m); m_defs[s]->instantiate(ps); } } void plugin::log_axiom_definitions(symbol const& s, sort * new_sort) { std::ostream& out = m_manager->trace_stream(); symbol const& family_name = m_manager->get_family_name(get_family_id()); for (constructor const* c : *m_defs[s]) { func_decl_ref f = c->instantiate(new_sort); const unsigned num_args = f->get_arity(); if (num_args == 0) continue; // log constructor with quantified variables as arguments for (unsigned i = 0; i < num_args; ++i) { out << "[mk-var] " << family_name << "#" << m_id_counter << " " << i << "\n"; ++m_id_counter; } const unsigned constructor_id = m_id_counter; out << "[mk-app] " << family_name << "#" << constructor_id << " " << f->get_name(); for (unsigned i = 0; i < num_args; ++i) { out << " " << family_name << "#" << constructor_id - num_args + i; } out << "\n"; ++m_id_counter; // axioms for all accessors are generated when a constructor is applied => use constructor as pattern out << "[mk-app] " << family_name << "#" << m_id_counter << " pattern " << family_name << "#" << constructor_id << "\n"; ++m_id_counter; m_axiom_bases.insert(f->get_name(), constructor_id + 4); std::ostringstream var_sorts; for (accessor const* a : *c) { var_sorts << " (;" << a->range()->get_name() << ")"; } std::string var_description = var_sorts.str(); // create axioms: the ith accessor returns the ith argument of the constructor unsigned i = 0; for (accessor const* a : *c) { func_decl_ref acc = a->instantiate(new_sort); out << "[mk-app] " << family_name << "#" << m_id_counter << " " << acc->get_name() << " " << family_name << "#" << constructor_id << "\n"; ++m_id_counter; out << "[mk-app] " << family_name << "#" << m_id_counter << " = " << family_name << "#" << constructor_id - num_args + i << " " << family_name << "#" << m_id_counter - 1 << "\n"; ++m_id_counter; out << "[mk-quant] " << family_name << "#" << m_id_counter << " constructor_accessor_axiom " << num_args << " " << family_name << "#" << constructor_id + 1 << " " << family_name << "#" << m_id_counter - 1 << "\n"; out << "[attach-var-names] " << family_name << "#" << m_id_counter << var_description << "\n"; ++m_id_counter; ++i; } } } bool plugin::mk_datatypes(unsigned num_datatypes, def * const * datatypes, unsigned num_params, sort* const* sort_params, sort_ref_vector & new_sorts) { begin_def_block(); for (unsigned i = 0; i < num_datatypes; ++i) { def* d = nullptr; TRACE("datatype", tout << "declaring " << datatypes[i]->name() << "\n";); if (m_defs.find(datatypes[i]->name(), d)) { TRACE("datatype", tout << "delete previous version for " << datatypes[i]->name() << "\n";); u().reset(); dealloc(d); } m_defs.insert(datatypes[i]->name(), datatypes[i]); m_def_block.push_back(datatypes[i]->name()); } end_def_block(); sort_ref_vector ps(*m_manager); for (symbol const& s : m_def_block) { new_sorts.push_back(m_defs[s]->instantiate(ps)); if (m_manager->has_trace_stream()) { log_axiom_definitions(s, new_sorts.back()); } } return true; } void plugin::remove(symbol const& s) { def* d = nullptr; if (m_defs.find(s, d)) { dealloc(d); } m_defs.remove(s); } bool plugin::is_value_visit(expr * arg, ptr_buffer & todo) const { if (!is_app(arg)) return false; family_id fid = to_app(arg)->get_family_id(); if (fid == m_family_id) { if (!u().is_constructor(to_app(arg))) return false; if (to_app(arg)->get_num_args() == 0) return true; todo.push_back(to_app(arg)); return true; } else { return m_manager->is_value(arg); } } bool plugin::is_value(app * e) const { TRACE("dt_is_value", tout << "checking\n" << mk_ismt2_pp(e, *m_manager) << "\n";); if (!u().is_constructor(e)) return false; if (e->get_num_args() == 0) return true; // REMARK: if the following check is too expensive, we should // cache the values in the decl::plugin. ptr_buffer todo; // potentially expensive check for common sub-expressions. for (expr* arg : *e) { if (!is_value_visit(arg, todo)) { TRACE("dt_is_value", tout << "not-value:\n" << mk_ismt2_pp(arg, *m_manager) << "\n";); return false; } } while (!todo.empty()) { app * curr = todo.back(); SASSERT(u().is_constructor(curr)); todo.pop_back(); for (expr* arg : *curr) { if (!is_value_visit(arg, todo)) { TRACE("dt_is_value", tout << "not-value:\n" << mk_ismt2_pp(arg, *m_manager) << "\n";); return false; } } } return true; } void plugin::get_op_names(svector & op_names, symbol const & logic) { op_names.push_back(builtin_name("is", OP_DT_IS)); if (logic == symbol::null || logic == symbol("ALL")) { op_names.push_back(builtin_name("update-field", OP_DT_UPDATE_FIELD)); } } expr * plugin::get_some_value(sort * s) { SASSERT(u().is_datatype(s)); func_decl * c = u().get_non_rec_constructor(s); ptr_buffer args; for (unsigned i = 0; i < c->get_arity(); i++) { args.push_back(m_manager->get_some_value(c->get_domain(i))); } return m_manager->mk_app(c, args.size(), args.c_ptr()); } bool plugin::is_fully_interp(sort * s) const { return u().is_fully_interp(s); } } sort_ref_vector util::datatype_params(sort * s) const { SASSERT(is_datatype(s)); sort_ref_vector result(m); for (unsigned i = 1; i < s->get_num_parameters(); ++i) { result.push_back(to_sort(s->get_parameter(i).get_ast())); } return result; } bool util::is_fully_interp(sort * s) const { SASSERT(is_datatype(s)); bool fi = true; return fi; if (m_is_fully_interp.find(s, fi)) { return fi; } unsigned sz = m_fully_interp_trail.size(); m_is_fully_interp.insert(s, true); def const& d = get_def(s); bool is_interp = true; m_fully_interp_trail.push_back(s); for (constructor const* c : d) { for (accessor const* a : *c) { func_decl_ref ac = a->instantiate(s); sort* r = ac->get_range(); if (!m.is_fully_interp(r)) { is_interp = false; break; } } if (!is_interp) break; } for (unsigned i = sz; i < m_fully_interp_trail.size(); ++i) { m_is_fully_interp.remove(m_fully_interp_trail[i]); } m_fully_interp_trail.shrink(sz); m_is_fully_interp.insert(s, is_interp); m_asts.push_back(s); return true; } /** \brief Return true if the inductive datatype is recursive. */ bool util::is_recursive_core(sort* s) const { obj_map already_found; ptr_vector todo, subsorts; todo.push_back(s); status st; while (!todo.empty()) { s = todo.back(); if (already_found.find(s, st) && st == BLACK) { todo.pop_back(); continue; } already_found.insert(s, GRAY); def const& d = get_def(s); bool can_process = true; for (constructor const* c : d) { for (accessor const* a : *c) { sort* d = a->range(); // check if d is a datatype sort subsorts.reset(); get_subsorts(d, subsorts); for (sort * s2 : subsorts) { if (is_datatype(s2)) { if (already_found.find(s2, st)) { // type is recursive if (st == GRAY) return true; } else { todo.push_back(s2); can_process = false; } } } } } if (can_process) { already_found.insert(s, BLACK); todo.pop_back(); } } return false; } unsigned util::get_datatype_num_parameter_sorts(sort * ty) { SASSERT(ty->get_num_parameters() >= 1); return ty->get_num_parameters() - 1; } sort* util::get_datatype_parameter_sort(sort * ty, unsigned idx) { SASSERT(idx < get_datatype_num_parameter_sorts(ty)); return to_sort(ty->get_parameter(idx+1).get_ast()); } param_size::size* util::get_sort_size(sort_ref_vector const& params, sort* s) { if (params.empty() && !is_datatype(s)) { return param_size::size::mk_offset(s->get_num_elements()); } if (is_datatype(s)) { 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); m_refs.push_back(sz); S.insert(d.params().get(i), sz); } auto ss = d.sort_size(); if (!ss) { d.set_sort_size(param_size::size::mk_offset(sort_size::mk_infinite())); ss = d.sort_size(); } return ss->subst(S); } array_util autil(m); if (autil.is_array(s)) { unsigned n = get_array_arity(s); ptr_vector szs; for (unsigned i = 0; i < n; ++i) { szs.push_back(get_sort_size(params, get_array_domain(s, i))); } param_size::size* sz1 = param_size::size::mk_times(szs); param_size::size* sz2 = get_sort_size(params, get_array_range(s)); return param_size::size::mk_power(sz2, sz1); } for (sort* p : params) { if (s == p) { sort_ref sr(s, m); return param_size::size::mk_param(sr); } } return param_size::size::mk_offset(s->get_num_elements()); } bool util::is_declared(sort* s) const { return m_plugin->is_declared(s); } void util::compute_datatype_size_functions(svector const& names) { map already_found; map szs; TRACE("datatype", for (auto const& s : names) tout << s << " "; tout << "\n";); svector todo(names); status st; while (!todo.empty()) { symbol s = todo.back(); TRACE("datatype", tout << "Sort size for " << s << "\n";); if (already_found.find(s, st) && st == BLACK) { todo.pop_back(); continue; } already_found.insert(s, GRAY); bool is_infinite = false; bool can_process = true; def& d = get_def(s); for (constructor const* c : d) { for (accessor const* a : *c) { sort* r = a->range(); if (is_datatype(r)) { symbol s2 = r->get_name(); if (already_found.find(s2, st)) { // type is infinite if (st == GRAY) { is_infinite = true; } } else if (names.contains(s2)) { todo.push_back(s2); can_process = false; } } } } if (!can_process) { continue; } todo.pop_back(); already_found.insert(s, BLACK); if (is_infinite) { TRACE("datatype", tout << "infinite " << s << "\n";); d.set_sort_size(param_size::size::mk_offset(sort_size::mk_infinite())); continue; } ptr_vector s_add; for (constructor const* c : d) { ptr_vector s_mul; for (accessor const* a : *c) { s_mul.push_back(get_sort_size(d.params(), a->range())); } s_add.push_back(param_size::size::mk_times(s_mul)); } TRACE("datatype", tout << "set sort size " << s << "\n";); d.set_sort_size(param_size::size::mk_plus(s_add)); m_refs.reset(); } } /** \brief Return true if the inductive datatype is well-founded. 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); obj_map sort2id; for (unsigned i = 0; i < num_types; ++i) { sort2id.insert(sorts[i], i); } unsigned num_well_founded = 0, id = 0; bool changed; ptr_vector subsorts; do { changed = false; for (unsigned tid = 0; tid < num_types; tid++) { if (well_founded[tid]) { continue; } sort* s = sorts[tid]; def const& d = get_def(s); for (constructor const* c : d) { for (accessor const* a : *c) { subsorts.reset(); get_subsorts(a->range(), subsorts); for (sort* srt : subsorts) { if (sort2id.find(srt, id) && !well_founded[id]) { goto next_constructor; } } } changed = true; well_founded[tid] = true; num_well_founded++; break; next_constructor: ; } } } while (changed && num_well_founded < num_types); return num_well_founded == num_types; } /** \brief Return true if the inductive datatype is co-variant. Pre-condition: The given argument constrains the parameters of an inductive datatype. */ bool util::is_covariant(unsigned num_types, sort* const* sorts) const { ast_mark mark; ptr_vector subsorts; for (unsigned tid = 0; tid < num_types; tid++) { mark.mark(sorts[tid], true); } for (unsigned tid = 0; tid < num_types; tid++) { sort* s = sorts[tid]; def const& d = get_def(s); for (constructor const* c : d) { for (accessor const* a : *c) { if (!is_covariant(mark, subsorts, a->range())) { return false; } } } } return true; } bool util::is_covariant(ast_mark& mark, ptr_vector& subsorts, sort* s) const { array_util autil(m); if (!autil.is_array(s)) { return true; } unsigned n = get_array_arity(s); subsorts.reset(); for (unsigned i = 0; i < n; ++i) { get_subsorts(get_array_domain(s, i), subsorts); } if (!is_datatype(get_array_range(s))) { get_subsorts(get_array_range(s), subsorts); } for (sort* r : subsorts) { if (mark.is_marked(r)) return false; } return true; } def const& util::get_def(sort* s) const { return m_plugin->get_def(s); } void util::get_subsorts(sort* s, ptr_vector& sorts) const { sorts.push_back(s); for (parameter const& p : s->parameters()) { if (p.is_ast() && is_sort(p.get_ast())) { get_subsorts(to_sort(p.get_ast()), sorts); } } } util::util(ast_manager & m): m(m), m_family_id(m.mk_family_id("datatype")), m_asts(m), m_start(0) { m_plugin = dynamic_cast(m.get_plugin(m_family_id)); SASSERT(m_plugin); } util::~util() { std::for_each(m_vectors.begin(), m_vectors.end(), delete_proc >()); } ptr_vector const * util::get_datatype_constructors(sort * ty) { SASSERT(is_datatype(ty)); ptr_vector * r = nullptr; if (m_datatype2constructors.find(ty, r)) return r; r = alloc(ptr_vector); m_asts.push_back(ty); m_vectors.push_back(r); m_datatype2constructors.insert(ty, r); def const& d = get_def(ty); for (constructor const* c : d) { func_decl_ref f = c->instantiate(ty); m_asts.push_back(f); r->push_back(f); } return r; } ptr_vector const * util::get_constructor_accessors(func_decl * con) { SASSERT(is_constructor(con)); ptr_vector * res = nullptr; if (m_constructor2accessors.find(con, res)) { return res; } res = alloc(ptr_vector); m_asts.push_back(con); m_vectors.push_back(res); m_constructor2accessors.insert(con, res); sort * datatype = con->get_range(); def const& d = get_def(datatype); for (constructor const* c : d) { if (c->name() == con->get_name()) { for (accessor const* a : *c) { func_decl_ref fn = a->instantiate(datatype); res->push_back(fn); m_asts.push_back(fn); } break; } } return res; } func_decl * util::get_constructor_is(func_decl * con) { SASSERT(is_constructor(con)); sort * datatype = con->get_range(); parameter ps[1] = { parameter(con)}; return m.mk_func_decl(m_family_id, OP_DT_IS, 1, ps, 1, &datatype); } func_decl * util::get_constructor_recognizer(func_decl * con) { SASSERT(is_constructor(con)); func_decl * d = nullptr; if (m_constructor2recognizer.find(con, d)) return d; sort * datatype = con->get_range(); def const& dd = get_def(datatype); symbol r; for (constructor const* c : dd) { if (c->name() == con->get_name()) { r = c->recognizer(); } } parameter ps[2] = { parameter(con), parameter(r) }; d = m.mk_func_decl(m_family_id, OP_DT_RECOGNISER, 2, ps, 1, &datatype); SASSERT(d); m_asts.push_back(con); m_asts.push_back(d); m_constructor2recognizer.insert(con, d); return d; } app* util::mk_is(func_decl * c, expr *f) { return m.mk_app(get_constructor_is(c), 1, &f); } func_decl * util::get_recognizer_constructor(func_decl * recognizer) const { SASSERT(is_recognizer(recognizer)); return to_func_decl(recognizer->get_parameter(0).get_ast()); } func_decl * util::get_update_accessor(func_decl * updt) const { SASSERT(is_update_field(updt)); return to_func_decl(updt->get_parameter(0).get_ast()); } bool util::is_recursive(sort * ty) { SASSERT(is_datatype(ty)); bool r = false; if (!m_is_recursive.find(ty, r)) { r = is_recursive_core(ty); m_is_recursive.insert(ty, r); m_asts.push_back(ty); } return r; } bool util::is_enum_sort(sort* s) { if (!is_datatype(s)) { return false; } bool r = false; if (m_is_enum.find(s, r)) return r; ptr_vector const& cnstrs = *get_datatype_constructors(s); r = true; for (unsigned i = 0; r && i < cnstrs.size(); ++i) { r = cnstrs[i]->get_arity() == 0; } m_is_enum.insert(s, r); m_asts.push_back(s); return r; } func_decl * util::get_accessor_constructor(func_decl * accessor) { SASSERT(is_accessor(accessor)); func_decl * r = nullptr; if (m_accessor2constructor.find(accessor, r)) return r; sort * datatype = accessor->get_domain(0); symbol c_id = accessor->get_parameter(1).get_symbol(); def const& d = get_def(datatype); func_decl_ref fn(m); for (constructor const* c : d) { if (c->name() == c_id) { fn = c->instantiate(datatype); break; } } r = fn; m_accessor2constructor.insert(accessor, r); m_asts.push_back(accessor); m_asts.push_back(r); return r; } void util::reset() { m_datatype2constructors.reset(); m_datatype2nonrec_constructor.reset(); m_constructor2accessors.reset(); m_constructor2recognizer.reset(); m_recognizer2constructor.reset(); m_accessor2constructor.reset(); m_is_recursive.reset(); m_is_enum.reset(); std::for_each(m_vectors.begin(), m_vectors.end(), delete_proc >()); m_vectors.reset(); m_asts.reset(); ++m_start; } /** \brief Return a constructor mk(T_1, ... T_n) where each T_i is not a datatype or it is a datatype that contains a constructor that will not contain directly or indirectly an element of the given sort. */ func_decl * util::get_non_rec_constructor(sort * ty) { SASSERT(is_datatype(ty)); cnstr_depth cd; if (m_datatype2nonrec_constructor.find(ty, cd)) return cd.first; ptr_vector forbidden_set; forbidden_set.push_back(ty); TRACE("util_bug", tout << "invoke get-non-rec: " << sort_ref(ty, m) << "\n";); cd = get_non_rec_constructor_core(ty, forbidden_set); SASSERT(forbidden_set.back() == ty); SASSERT(cd.first); return cd.first; } /** \brief Return a constructor mk(T_1, ..., T_n) where each T_i is not a datatype or it is a datatype t not in forbidden_set, and get_non_rec_constructor_core(T_i, forbidden_set union { T_i }) */ util::cnstr_depth util::get_non_rec_constructor_core(sort * ty, ptr_vector & forbidden_set) { // We must select a constructor c(T_1, ..., T_n):T such that // 1) T_i's are not recursive // If there is no such constructor, then we select one that // 2) each type T_i is not recursive or contains a constructor that does not depend on T ptr_vector const& constructors = *get_datatype_constructors(ty); array_util autil(m); cnstr_depth result(nullptr, 0); if (m_datatype2nonrec_constructor.find(ty, result)) return result; TRACE("util_bug", tout << "get-non-rec constructor: " << sort_ref(ty, m) << "\n"; tout << "forbidden: "; for (sort* s : forbidden_set) tout << sort_ref(s, m) << " "; tout << "\n"; tout << "constructors: " << constructors.size() << "\n"; for (func_decl* f : constructors) tout << func_decl_ref(f, m) << "\n"; ); unsigned min_depth = INT_MAX; for (func_decl * c : constructors) { TRACE("util_bug", tout << "non_rec_constructor c: " << func_decl_ref(c, m) << "\n";); unsigned num_args = c->get_arity(); unsigned i = 0; unsigned max_depth = 0; for (; i < num_args; i++) { sort * T_i = autil.get_array_range_rec(c->get_domain(i)); TRACE("util_bug", tout << "c: " << i << " " << sort_ref(T_i, m) << "\n";); if (!is_datatype(T_i)) { TRACE("util_bug", tout << sort_ref(T_i, m) << " is not a datatype\n";); continue; } if (std::find(forbidden_set.begin(), forbidden_set.end(), T_i) != forbidden_set.end()) { TRACE("util_bug", tout << sort_ref(T_i, m) << " is in forbidden_set\n";); break; } forbidden_set.push_back(T_i); cnstr_depth nested_c = get_non_rec_constructor_core(T_i, forbidden_set); SASSERT(forbidden_set.back() == T_i); forbidden_set.pop_back(); if (nested_c.first == nullptr) break; TRACE("util_bug", tout << "nested_c: " << nested_c.first->get_name() << "\n";); max_depth = std::max(nested_c.second + 1, max_depth); } if (i == num_args && max_depth < min_depth) { result.first = c; result.second = max_depth; min_depth = max_depth; } } if (result.first) { m_asts.push_back(result.first); m_asts.push_back(ty); m_datatype2nonrec_constructor.insert(ty, result); } return result; } unsigned util::get_constructor_idx(func_decl * f) const { unsigned idx = 0; def const& d = get_def(f->get_range()); for (constructor* c : d) { if (c->name() == f->get_name()) { return idx; } ++idx; } UNREACHABLE(); return 0; } unsigned util::get_recognizer_constructor_idx(func_decl * f) const { return get_constructor_idx(get_recognizer_constructor(f)); } /** \brief Two datatype sorts s1 and s2 are siblings if they were defined together in the same mutually recursive definition. */ bool util::are_siblings(sort * s1, sort * s2) { if (!is_datatype(s1) || !is_datatype(s2)) { return s1 == s2; } else { return get_def(s1).id() == get_def(s2).id(); } } unsigned util::get_datatype_num_constructors(sort * ty) { def const& d = get_def(ty->get_name()); return d.constructors().size(); } void util::get_defs(sort* s0, ptr_vector& defs) { svector mark; ptr_buffer todo; todo.push_back(s0); mark.push_back(s0->get_name()); while (!todo.empty()) { sort* s = todo.back(); todo.pop_back(); defs.push_back(&m_plugin->get_def(s->get_name())); def const& d = get_def(s); for (constructor* c : d) { for (accessor* a : *c) { sort* s = a->range(); if (are_siblings(s0, s) && !mark.contains(s->get_name())) { mark.push_back(s->get_name()); todo.push_back(s); } } } } } void util::display_datatype(sort *s0, std::ostream& out) { ast_mark mark; ptr_buffer todo; SASSERT(is_datatype(s0)); out << s0->get_name() << " where\n"; todo.push_back(s0); mark.mark(s0, true); while (!todo.empty()) { sort* s = todo.back(); todo.pop_back(); out << s->get_name() << " =\n"; ptr_vector const& cnstrs = *get_datatype_constructors(s); for (func_decl * cns : cnstrs) { out << " " << cns->get_name() << " :: "; ptr_vector const & accs = *get_constructor_accessors(cns); for (func_decl* acc : accs) { sort* s1 = acc->get_range(); out << "(" << acc->get_name() << ": " << s1->get_name() << ") "; if (is_datatype(s1) && are_siblings(s1, s0) && !mark.is_marked(s1)) { mark.mark(s1, true); todo.push_back(s1); } } out << "\n"; } } } sort_ref util::mk_list_datatype(sort* elem, symbol const& name, func_decl_ref& cons, func_decl_ref& is_cons, func_decl_ref& hd, func_decl_ref& tl, func_decl_ref& nil, func_decl_ref& is_nil) { accessor_decl* head_tail[2] = { mk_accessor_decl(m, symbol("head"), type_ref(elem)), mk_accessor_decl(m, symbol("tail"), type_ref(0)) }; constructor_decl* constrs[2] = { mk_constructor_decl(symbol("nil"), symbol("is_nil"), 0, nullptr), mk_constructor_decl(symbol("cons"), symbol("is_cons"), 2, head_tail) }; decl::plugin& p = *get_plugin(); sort_ref_vector sorts(m); datatype_decl * decl = mk_datatype_decl(*this, name, 0, nullptr, 2, constrs); bool is_ok = p.mk_datatypes(1, &decl, 0, nullptr, sorts); del_datatype_decl(decl); if (!is_ok) { return sort_ref(m); } sort* s = sorts.get(0); ptr_vector const& cnstrs = *get_datatype_constructors(s); SASSERT(cnstrs.size() == 2); nil = cnstrs[0]; is_nil = get_constructor_is(cnstrs[0]); cons = cnstrs[1]; is_cons = get_constructor_is(cnstrs[1]); ptr_vector const& acc = *get_constructor_accessors(cnstrs[1]); SASSERT(acc.size() == 2); hd = acc[0]; tl = acc[1]; return sort_ref(s, m); } sort_ref util::mk_pair_datatype(sort* a, sort* b, func_decl_ref& fst, func_decl_ref& snd, func_decl_ref& pair) { type_ref t1(a), t2(b); accessor_decl* fstd = mk_accessor_decl(m, symbol("fst"), t1); accessor_decl* sndd = mk_accessor_decl(m, symbol("snd"), t2); accessor_decl* accd[2] = { fstd, sndd }; auto * p = mk_constructor_decl(symbol("pair"), symbol("is-pair"), 2, accd); auto* dt = mk_datatype_decl(*this, symbol("pair"), 0, nullptr, 1, &p); sort_ref_vector sorts(m); VERIFY(get_plugin()->mk_datatypes(1, &dt, 0, nullptr, sorts)); del_datatype_decl(dt); sort* s = sorts.get(0); ptr_vector const& cnstrs = *get_datatype_constructors(s); SASSERT(cnstrs.size() == 1); ptr_vector const& acc = *get_constructor_accessors(cnstrs[0]); SASSERT(acc.size() == 2); fst = acc[0]; snd = acc[1]; pair = cnstrs[0]; return sort_ref(s, m); } sort_ref util::mk_tuple_datatype(svector> const& elems, symbol const& name, symbol const& test, func_decl_ref& tup, func_decl_ref_vector& accs) { ptr_vector accd; for (auto const& e : elems) { type_ref t(e.second); accd.push_back(mk_accessor_decl(m, e.first, t)); } auto* tuple = mk_constructor_decl(name, test, accd.size(), accd.c_ptr()); auto* dt = mk_datatype_decl(*this, name, 0, nullptr, 1, &tuple); sort_ref_vector sorts(m); VERIFY(get_plugin()->mk_datatypes(1, &dt, 0, nullptr, sorts)); del_datatype_decl(dt); sort* s = sorts.get(0); ptr_vector const& cnstrs = *get_datatype_constructors(s); SASSERT(cnstrs.size() == 1); ptr_vector const& acc = *get_constructor_accessors(cnstrs[0]); for (auto* f : acc) accs.push_back(f); tup = cnstrs[0]; return sort_ref(s, m); } } datatype_decl * mk_datatype_decl(datatype_util& u, symbol const & n, unsigned num_params, sort*const* params, unsigned num_constructors, constructor_decl * const * cs) { datatype::decl::plugin* p = u.get_plugin(); datatype::def* d = p->mk(n, num_params, params); for (unsigned i = 0; i < num_constructors; ++i) { d->add(cs[i]); } return d; } z3-z3-4.8.7/src/ast/datatype_decl_plugin.h000066400000000000000000000445331356505360400203470ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: datatype_decl_plugin.h Abstract: Author: Nikolaj Bjorner (nbjorner) 2017-9-1 Revision History: rewritten to support SMTLIB-2.6 parameters from Leonardo de Moura (leonardo) 2008-01-09. --*/ #ifndef DATATYPE_DECL_PLUGIN_H_ #define DATATYPE_DECL_PLUGIN_H_ #include "ast/ast.h" #include "util/buffer.h" #include "util/symbol_table.h" #include "util/obj_hashtable.h" enum sort_kind { DATATYPE_SORT }; enum op_kind { OP_DT_CONSTRUCTOR, OP_DT_RECOGNISER, OP_DT_IS, OP_DT_ACCESSOR, OP_DT_UPDATE_FIELD, LAST_DT_OP }; namespace datatype { class util; class def; class accessor; class constructor; class accessor { symbol m_name; sort_ref m_range; unsigned m_index; // reference to recursive data-type may only get resolved after all mutually recursive data-types are procssed. constructor* m_constructor; public: accessor(ast_manager& m, symbol const& n, sort* range): m_name(n), m_range(range, m), m_index(UINT_MAX) {} accessor(ast_manager& m, symbol const& n, unsigned index): m_name(n), m_range(m), m_index(index) {} sort* range() const { return m_range; } void fix_range(sort_ref_vector const& dts); symbol const& name() const { return m_name; } func_decl_ref instantiate(sort_ref_vector const& ps) const; func_decl_ref instantiate(sort* dt) const; void attach(constructor* d) { m_constructor = d; } constructor const& get_constructor() const { return *m_constructor; } def const& get_def() const; util& u() const; accessor* translate(ast_translation& tr); }; class constructor { symbol m_name; symbol m_recognizer; ptr_vector m_accessors; def* m_def; public: constructor(symbol n, symbol const& r): m_name(n), m_recognizer(r) {} ~constructor(); void add(accessor* a) { m_accessors.push_back(a); a->attach(this); } symbol const& name() const { return m_name; } symbol const& recognizer() const { return m_recognizer; } ptr_vector const& accessors() const { return m_accessors; } ptr_vector::const_iterator begin() const { return m_accessors.begin(); } ptr_vector::const_iterator end() const { return m_accessors.end(); } ptr_vector::iterator begin() { return m_accessors.begin(); } ptr_vector::iterator end() { return m_accessors.end(); } func_decl_ref instantiate(sort_ref_vector const& ps) const; func_decl_ref instantiate(sort* dt) const; void attach(def* d) { m_def = d; } def const& get_def() const { return *m_def; } util& u() const; constructor* translate(ast_translation& tr); }; namespace param_size { class size { unsigned m_ref; public: size(): m_ref(0) {} virtual ~size() {} void inc_ref() { ++m_ref; } void dec_ref(); static size* mk_offset(sort_size const& s); static size* mk_param(sort_ref& p); static size* mk_plus(size* a1, size* a2); static size* mk_times(size* a1, size* a2); static size* mk_plus(ptr_vector& szs); static size* mk_times(ptr_vector& szs); static size* mk_power(size* a1, size* a2); virtual size* subst(obj_map& S) = 0; virtual sort_size eval(obj_map const& S) = 0; }; struct offset : public size { sort_size m_offset; offset(sort_size const& s): m_offset(s) {} ~offset() override {} size* subst(obj_map& S) override { return this; } sort_size eval(obj_map const& S) override { return m_offset; } }; struct plus : public size { 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; 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; 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; 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; sort_size eval(obj_map const& S) override { return S[m_param]; } }; }; class def { ast_manager& m; util& m_util; symbol m_name; unsigned m_class_id; param_size::size* m_sort_size; sort_ref_vector m_params; mutable sort_ref m_sort; ptr_vector m_constructors; public: def(ast_manager& m, util& u, symbol const& n, unsigned class_id, unsigned num_params, sort * const* params): m(m), m_util(u), m_name(n), m_class_id(class_id), m_sort_size(nullptr), m_params(m, num_params, params), m_sort(m) {} ~def() { if (m_sort_size) m_sort_size->dec_ref(); for (constructor* c : m_constructors) dealloc(c); m_constructors.reset(); } void add(constructor* c) { m_constructors.push_back(c); c->attach(this); } symbol const& name() const { return m_name; } unsigned id() const { return m_class_id; } sort_ref instantiate(sort_ref_vector const& ps) const; ptr_vector const& constructors() const { return m_constructors; } ptr_vector::const_iterator begin() const { return m_constructors.begin(); } ptr_vector::const_iterator end() const { return m_constructors.end(); } ptr_vector::iterator begin() { return m_constructors.begin(); } ptr_vector::iterator end() { return m_constructors.end(); } sort_ref_vector const& params() const { return m_params; } util& u() const { return m_util; } param_size::size* sort_size() { return m_sort_size; } void set_sort_size(param_size::size* p) { m_sort_size = p; p->inc_ref(); m_sort = nullptr; } def* translate(ast_translation& tr, util& u); }; namespace decl { class plugin : public decl_plugin { mutable scoped_ptr m_util; map m_defs; map m_axiom_bases; unsigned m_id_counter; svector m_def_block; unsigned m_class_id; mutable bool m_has_nested_arrays; void inherit(decl_plugin* other_p, ast_translation& tr) override; void log_axiom_definitions(symbol const& s, sort * new_sort); public: plugin(): m_id_counter(0), m_class_id(0), m_has_nested_arrays(false) {} ~plugin() override; void finalize() override; decl_plugin * mk_fresh() override { return alloc(plugin); } sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) override; func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) override; expr * get_some_value(sort * s) override; bool is_fully_interp(sort * s) const override; bool is_value(app* e) const override; bool is_unique_value(app * e) const override { return is_value(e); } void get_op_names(svector & op_names, symbol const & logic) override; void begin_def_block() { m_class_id++; m_def_block.reset(); } void end_def_block(); def* mk(symbol const& name, unsigned n, sort * const * params); void remove(symbol const& d); bool mk_datatypes(unsigned num_datatypes, def * const * datatypes, unsigned num_params, sort* const* sort_params, sort_ref_vector & new_sorts); def const& get_def(sort* s) const { return *(m_defs[datatype_name(s)]); } def& get_def(symbol const& s) { return *(m_defs[s]); } bool is_declared(sort* s) const { return m_defs.contains(datatype_name(s)); } unsigned get_axiom_base_id(symbol const& s) { return m_axiom_bases[s]; } util & u() const; bool has_nested_arrays() const { return m_has_nested_arrays; } private: bool is_value_visit(expr * arg, ptr_buffer & todo) const; func_decl * mk_update_field( unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); func_decl * mk_constructor( unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); func_decl * mk_accessor( unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); func_decl * mk_recognizer( unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); func_decl * mk_is( unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); symbol datatype_name(sort * s) const { //SASSERT(u().is_datatype(s)); return s->get_parameter(0).get_symbol(); } }; } class util { ast_manager & m; family_id m_family_id; mutable decl::plugin* m_plugin; typedef std::pair cnstr_depth; obj_map *> m_datatype2constructors; obj_map m_datatype2nonrec_constructor; obj_map *> m_constructor2accessors; obj_map m_constructor2recognizer; obj_map m_recognizer2constructor; obj_map m_accessor2constructor; obj_map m_is_recursive; obj_map m_is_enum; mutable obj_map m_is_fully_interp; mutable ast_ref_vector m_asts; sref_vector m_refs; ptr_vector > m_vectors; unsigned m_start; mutable ptr_vector m_fully_interp_trail; cnstr_depth get_non_rec_constructor_core(sort * ty, ptr_vector & forbidden_set); friend class decl::plugin; bool is_recursive_core(sort * s) const; sort_size get_datatype_size(sort* s0); void compute_datatype_size_functions(svector const& names); param_size::size* get_sort_size(sort_ref_vector const& params, sort* s); bool is_well_founded(unsigned num_types, sort* const* sorts); bool is_covariant(unsigned num_types, sort* const* sorts) const; bool is_covariant(ast_mark& mark, ptr_vector& subsorts, sort* s) const; def& get_def(symbol const& s) { return m_plugin->get_def(s); } void get_subsorts(sort* s, ptr_vector& sorts) const; public: util(ast_manager & m); ~util(); ast_manager & get_manager() const { return m; } // sort * mk_datatype_sort(symbol const& name, unsigned n, sort* const* params); bool is_datatype(sort const* s) const { return is_sort_of(s, m_family_id, DATATYPE_SORT); } bool is_enum_sort(sort* s); bool is_recursive(sort * ty); bool is_constructor(func_decl * f) const { return is_decl_of(f, m_family_id, OP_DT_CONSTRUCTOR); } bool is_recognizer(func_decl * f) const { return is_recognizer0(f) || is_is(f); } bool is_recognizer0(func_decl * f) const { return is_decl_of(f, m_family_id, OP_DT_RECOGNISER); } bool is_is(func_decl * f) const { return is_decl_of(f, m_family_id, OP_DT_IS); } bool is_accessor(func_decl * f) const { return is_decl_of(f, m_family_id, OP_DT_ACCESSOR); } bool is_update_field(func_decl * f) const { return is_decl_of(f, m_family_id, OP_DT_UPDATE_FIELD); } bool is_constructor(app * f) const { return is_app_of(f, m_family_id, OP_DT_CONSTRUCTOR); } bool is_constructor(expr* e) const { return is_app(e) && is_constructor(to_app(e)); } bool is_recognizer0(app * f) const { return is_app_of(f, m_family_id, OP_DT_RECOGNISER);} bool is_is(app * f) const { return is_app_of(f, m_family_id, OP_DT_IS);} bool is_is(expr * e) const { return is_app(e) && is_is(to_app(e)); } bool is_recognizer(app * f) const { return is_recognizer0(f) || is_is(f); } bool is_accessor(app * f) const { return is_app_of(f, m_family_id, OP_DT_ACCESSOR); } bool is_update_field(app * f) const { return is_app_of(f, m_family_id, OP_DT_UPDATE_FIELD); } app* mk_is(func_decl * c, expr *f); ptr_vector const * get_datatype_constructors(sort * ty); unsigned get_datatype_num_constructors(sort * ty); unsigned get_datatype_num_parameter_sorts(sort * ty); sort* get_datatype_parameter_sort(sort * ty, unsigned idx); func_decl * get_non_rec_constructor(sort * ty); func_decl * get_constructor_recognizer(func_decl * constructor); func_decl * get_constructor_is(func_decl * constructor); ptr_vector const * get_constructor_accessors(func_decl * constructor); func_decl * get_accessor_constructor(func_decl * accessor); func_decl * get_recognizer_constructor(func_decl * recognizer) const; func_decl * get_update_accessor(func_decl * update) const; bool has_nested_arrays() const { return m_plugin->has_nested_arrays(); } family_id get_family_id() const { return m_family_id; } bool are_siblings(sort * s1, sort * s2); bool is_func_decl(op_kind k, unsigned num_params, parameter const* params, func_decl* f); bool is_constructor_of(unsigned num_params, parameter const* params, func_decl* f); void reset(); bool is_declared(sort* s) const; void display_datatype(sort *s, std::ostream& strm); bool is_fully_interp(sort * s) const; sort_ref_vector datatype_params(sort * s) const; unsigned get_constructor_idx(func_decl * f) const; unsigned get_recognizer_constructor_idx(func_decl * f) const; decl::plugin* get_plugin() { return m_plugin; } void get_defs(sort* s, ptr_vector& defs); def const& get_def(sort* s) const; sort_ref mk_list_datatype(sort* elem, symbol const& name, func_decl_ref& cons, func_decl_ref& is_cons, func_decl_ref& hd, func_decl_ref& tl, func_decl_ref& nil, func_decl_ref& is_nil); sort_ref mk_pair_datatype(sort* a, sort* b, func_decl_ref& fst, func_decl_ref& snd, func_decl_ref& pair); sort_ref mk_tuple_datatype(svector> const& elems, symbol const& name, symbol const& test, func_decl_ref& tup, func_decl_ref_vector& accs); }; }; typedef datatype::accessor accessor_decl; typedef datatype::constructor constructor_decl; typedef datatype::def datatype_decl; typedef datatype::decl::plugin datatype_decl_plugin; typedef datatype::util datatype_util; class type_ref { void * m_data; public: type_ref():m_data(TAG(void *, nullptr, 1)) {} type_ref(int idx):m_data(BOXINT(void *, idx)) {} type_ref(sort * s):m_data(TAG(void *, s, 1)) {} bool is_idx() const { return GET_TAG(m_data) == 0; } bool is_sort() const { return GET_TAG(m_data) == 1; } sort * get_sort() const { return UNTAG(sort *, m_data); } int get_idx() const { return UNBOXINT(m_data); } }; inline accessor_decl * mk_accessor_decl(ast_manager& m, symbol const & n, type_ref const & t) { if (t.is_idx()) { return alloc(accessor_decl, m, n, t.get_idx()); } else { return alloc(accessor_decl, m, n, t.get_sort()); } } inline constructor_decl * mk_constructor_decl(symbol const & n, symbol const & r, unsigned num_accessors, accessor_decl * * acs) { constructor_decl* c = alloc(constructor_decl, n, r); for (unsigned i = 0; i < num_accessors; ++i) { c->add(acs[i]); } return c; } // Remark: the datatype becomes the owner of the constructor_decls datatype_decl * mk_datatype_decl(datatype_util& u, symbol const & n, unsigned num_params, sort*const* params, unsigned num_constructors, constructor_decl * const * cs); inline void del_datatype_decl(datatype_decl * d) {} inline void del_datatype_decls(unsigned num, datatype_decl * const * ds) {} #endif /* DATATYPE_DECL_PLUGIN_H_ */ z3-z3-4.8.7/src/ast/decl_collector.cpp000066400000000000000000000125631356505360400174750ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: smt_decl_collector.cpp Abstract: Collect uninterpreted func_delcs and sorts. This class was originally in ast_smt_pp.h Author: Leonardo (leonardo) 2011-10-04 Revision History: --*/ #include "ast/decl_collector.h" #include "ast/ast_pp.h" void decl_collector::visit_sort(sort * n) { SASSERT(!m_visited.is_marked(n)); family_id fid = n->get_family_id(); if (m().is_uninterp(n)) m_sorts.push_back(n); else if (fid == m_dt_fid) { m_sorts.push_back(n); for (func_decl * cnstr : *m_dt_util.get_datatype_constructors(n)) { m_todo.push_back(cnstr); ptr_vector const & cnstr_acc = *m_dt_util.get_constructor_accessors(cnstr); unsigned num_cas = cnstr_acc.size(); for (unsigned j = 0; j < num_cas; j++) { m_todo.push_back(cnstr_acc.get(j)); } } } for (unsigned i = n->get_num_parameters(); i-- > 0; ) { parameter const& p = n->get_parameter(i); if (p.is_ast()) m_todo.push_back(p.get_ast()); } } bool decl_collector::is_bool(sort * s) { return m().is_bool(s); } void decl_collector::visit_func(func_decl * n) { if (!m_visited.is_marked(n)) { family_id fid = n->get_family_id(); if (fid == null_family_id) { m_decls.push_back(n); } m_visited.mark(n, true); m_trail.push_back(n); } } decl_collector::decl_collector(ast_manager & m): m_manager(m), m_trail(m), m_dt_util(m) { m_basic_fid = m_manager.get_basic_family_id(); m_dt_fid = m_dt_util.get_family_id(); } void decl_collector::visit(ast* n) { if (m_visited.is_marked(n)) return; datatype_util util(m()); m_todo.push_back(n); while (!m_todo.empty()) { n = m_todo.back(); m_todo.pop_back(); if (!m_visited.is_marked(n)) { switch(n->get_kind()) { case AST_APP: { app * a = to_app(n); for (expr* arg : *a) { m_todo.push_back(arg); } m_todo.push_back(a->get_decl()); break; } case AST_QUANTIFIER: { quantifier * q = to_quantifier(n); unsigned num_decls = q->get_num_decls(); for (unsigned i = 0; i < num_decls; ++i) { m_todo.push_back(q->get_decl_sort(i)); } m_todo.push_back(q->get_expr()); for (unsigned i = 0; i < q->get_num_patterns(); ++i) { m_todo.push_back(q->get_pattern(i)); } break; } case AST_SORT: visit_sort(to_sort(n)); break; case AST_FUNC_DECL: { func_decl * d = to_func_decl(n); for (sort* srt : *d) { m_todo.push_back(srt); } m_todo.push_back(d->get_range()); visit_func(d); break; } case AST_VAR: break; default: UNREACHABLE(); } m_visited.mark(n, true); m_trail.push_back(n); } } } void decl_collector::order_deps(unsigned n) { top_sort st; for (unsigned i = n; i < m_sorts.size(); ++i) { sort* s = m_sorts.get(i); st.insert(s, collect_deps(s)); } st.topological_sort(); m_sorts.shrink(n); for (sort* s : st.top_sorted()) { m_sorts.push_back(s); } } decl_collector::sort_set* decl_collector::collect_deps(sort* s) { sort_set* set = alloc(sort_set); collect_deps(s, *set); set->remove(s); return set; } void decl_collector::collect_deps(sort* s, sort_set& set) { if (set.contains(s)) return; set.insert(s); if (s->is_sort_of(m_dt_util.get_family_id(), DATATYPE_SORT)) { unsigned num_sorts = m_dt_util.get_datatype_num_parameter_sorts(s); for (unsigned i = 0; i < num_sorts; ++i) { set.insert(m_dt_util.get_datatype_parameter_sort(s, i)); } unsigned num_cnstr = m_dt_util.get_datatype_num_constructors(s); for (unsigned i = 0; i < num_cnstr; i++) { func_decl * cnstr = m_dt_util.get_datatype_constructors(s)->get(i); set.insert(cnstr->get_range()); for (unsigned j = 0; j < cnstr->get_arity(); ++j) set.insert(cnstr->get_domain(j)); } } for (unsigned i = s->get_num_parameters(); i-- > 0; ) { parameter const& p = s->get_parameter(i); if (p.is_ast() && is_sort(p.get_ast())) { set.insert(to_sort(p.get_ast())); } } } void decl_collector::push() { m_trail_lim.push_back(m_trail.size()); m_sorts_lim.push_back(m_sorts.size()); m_decls_lim.push_back(m_decls.size()); } void decl_collector::pop(unsigned n) { SASSERT(n > 0); unsigned sz = m_trail_lim[m_trail_lim.size() - n]; for (unsigned i = m_trail.size(); i-- > sz; ) { m_visited.mark(m_trail.get(i), false); } m_trail.shrink(sz); m_sorts.shrink(m_sorts_lim[m_sorts_lim.size() - n]); m_decls.shrink(m_decls_lim[m_decls_lim.size() - n]); m_trail_lim.shrink(m_trail_lim.size() - n); m_sorts_lim.shrink(m_sorts_lim.size() - n); m_decls_lim.shrink(m_decls_lim.size() - n); } z3-z3-4.8.7/src/ast/decl_collector.h000066400000000000000000000033771356505360400171450ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: decl_collector.h Abstract: Collect uninterpreted func_delcs and sorts. This class was originally in ast_smt_pp.h Author: Leonardo (leonardo) 2011-10-04 Revision History: --*/ #ifndef SMT_DECL_COLLECTOR_H_ #define SMT_DECL_COLLECTOR_H_ #include "util/top_sort.h" #include "ast/ast.h" #include "ast/datatype_decl_plugin.h" class decl_collector { ast_manager & m_manager; ptr_vector m_sorts; ptr_vector m_decls; ast_mark m_visited; ast_ref_vector m_trail; unsigned_vector m_trail_lim; unsigned_vector m_sorts_lim; unsigned_vector m_decls_lim; family_id m_basic_fid; family_id m_dt_fid; datatype_util m_dt_util; ptr_vector m_todo; void visit_sort(sort* n); bool is_bool(sort* s); typedef obj_hashtable sort_set; sort_set* collect_deps(sort* s); void collect_deps(top_sort& st); void collect_deps(sort* s, sort_set& set); public: decl_collector(ast_manager & m); ast_manager & m() { return m_manager; } void reset() { m_sorts.reset(); m_decls.reset(); m_visited.reset(); m_trail.reset(); } void visit_func(func_decl* n); void visit(ast * n); void visit(unsigned n, expr* const* es); void visit(expr_ref_vector const& es); void push(); void pop(unsigned n); void order_deps(unsigned n); unsigned get_num_sorts() const { return m_sorts.size(); } unsigned get_num_decls() const { return m_decls.size(); } ptr_vector const& get_sorts() const { return m_sorts; } ptr_vector const& get_func_decls() const { return m_decls; } }; #endif z3-z3-4.8.7/src/ast/display_dimacs.cpp000066400000000000000000000066771356505360400175160ustar00rootroot00000000000000/*++ 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(); bool is_from_dimacs = true; 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 (!is_uninterp_const(l)) { is_from_dimacs = false; break; } symbol const& s = to_app(l)->get_decl()->get_name(); if (s.is_numerical() && s.get_num() > 0) { if (expr2var.get(l->get_id(), UINT_MAX) == UINT_MAX) { ++num_vars; expr2var.setx(l->get_id(), s.get_num(), UINT_MAX); exprs.setx(l->get_id(), l, nullptr); } continue; } is_from_dimacs = false; break; } if (!is_from_dimacs) { num_vars = 0; expr2var.reset(); exprs.reset(); break; } } if (!is_from_dimacs) { 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"; } if (!is_from_dimacs) { 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; } z3-z3-4.8.7/src/ast/display_dimacs.h000066400000000000000000000006171356505360400171470ustar00rootroot00000000000000/*++ 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__ */ z3-z3-4.8.7/src/ast/dl_decl_plugin.cpp000066400000000000000000000666101356505360400174660ustar00rootroot00000000000000/*++ Copyright (c) 2010 Microsoft Corporation Module Name: dl_decl_plugin.h Abstract: Author: Nikolaj Bjorner (nbjorner) 2010-04-10 Revision History: --*/ #include #include "ast/ast_pp.h" #include "ast/array_decl_plugin.h" #include "ast/datatype_decl_plugin.h" #include "ast/dl_decl_plugin.h" #include "util/warning.h" #include "ast/reg_decl_plugins.h" namespace datalog { dl_decl_plugin::dl_decl_plugin() : m_store_sym("store"), m_empty_sym("empty"), m_is_empty_sym("is_empty"), m_join_sym("join"), m_union_sym("union"), m_widen_sym("widen"), m_project_sym("project"), m_filter_sym("filter"), m_negation_filter_sym("negation_filter"), m_rename_sym("rename"), m_complement_sym("complement"), m_select_sym("select"), m_clone_sym("clone"), m_num_sym("N"), m_lt_sym("<"), m_le_sym("<="), m_rule_sym("R") { } bool dl_decl_plugin::check_bounds(char const* msg, unsigned low, unsigned up, unsigned val) const { if (low <= val && val <= up) { return true; } std::ostringstream buffer; buffer << msg << ", value is not within bound " << low << " <= " << val << " <= " << up; m_manager->raise_exception(buffer.str()); return false; } bool dl_decl_plugin::check_domain(unsigned low, unsigned up, unsigned val) const { return check_bounds("unexpected number of arguments", low, up, val); } bool dl_decl_plugin::check_params(unsigned low, unsigned up, unsigned val) const { return check_bounds("unexpected number of parameters", low, up, val); } sort * dl_decl_plugin::mk_relation_sort( unsigned num_parameters, parameter const * parameters) { bool is_finite = true; rational r(1); for (unsigned i = 0; is_finite && i < num_parameters; ++i) { if (!parameters[i].is_ast() || !is_sort(parameters[i].get_ast())) { m_manager->raise_exception("expecting sort parameters"); return nullptr; } sort* s = to_sort(parameters[i].get_ast()); sort_size const& sz1 = s->get_num_elements(); if (sz1.is_finite()) { r *= rational(sz1.size(),rational::ui64()); } else { is_finite = false; } } sort_size sz; if (is_finite && r.is_uint64()) { sz = sort_size::mk_finite(r.get_uint64()); } else { sz = sort_size::mk_very_big(); } sort_info info(m_family_id, DL_RELATION_SORT, sz, num_parameters, parameters); return m_manager->mk_sort(symbol("Table"),info); } sort * dl_decl_plugin::mk_finite_sort(unsigned num_params, parameter const* params) { if (num_params != 2) { m_manager->raise_exception("expecting two parameters"); return nullptr; } if (!params[0].is_symbol()) { m_manager->raise_exception("expecting symbol"); return nullptr; } if (!params[1].is_rational() || !params[1].get_rational().is_uint64()) { m_manager->raise_exception("expecting rational"); return nullptr; } sort_size sz = sort_size::mk_finite(params[1].get_rational().get_uint64()); sort_info info(m_family_id, DL_FINITE_SORT, sz, num_params, params); return m_manager->mk_sort(params[0].get_symbol(),info); } sort* dl_decl_plugin::mk_rule_sort() { sort_size sz(sort_size::mk_infinite()); sort_info info(m_family_id, DL_RULE_SORT, sz, 0, nullptr); return m_manager->mk_sort(m_rule_sym, info); } sort * dl_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) { switch(k) { case DL_RELATION_SORT: return mk_relation_sort(num_parameters, parameters); case DL_FINITE_SORT: return mk_finite_sort(num_parameters, parameters); case DL_RULE_SORT: return mk_rule_sort(); default: UNREACHABLE(); } return nullptr; } bool dl_decl_plugin::is_rel_sort(sort* r) { ptr_vector sorts; return is_rel_sort(r, sorts); } bool dl_decl_plugin::is_rel_sort(sort* r, ptr_vector& sorts) { if (!is_sort_of(r, m_family_id, DL_RELATION_SORT)) { m_manager->raise_exception("expected relation sort"); return false; } unsigned n = r->get_num_parameters(); 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("expected sort parameter"); return false; } sorts.push_back(to_sort(p.get_ast())); } return true; } bool dl_decl_plugin::is_fin_sort(sort* r) { if (!is_sort_of(r, m_family_id, DL_FINITE_SORT)) { m_manager->raise_exception("expected finite sort"); return false; } return true; } func_decl* dl_decl_plugin::mk_store_select(decl_kind k, unsigned arity, sort* const* domain) { bool is_store = (k == OP_RA_STORE); ast_manager& m = *m_manager; symbol sym = is_store?m_store_sym:m_select_sym; sort * r = domain[0]; if (!is_store) { r = m.mk_bool_sort(); } ptr_vector sorts; if (!is_rel_sort(r, sorts)) { return nullptr; } if (sorts.size() + 1 != arity) { m_manager->raise_exception("wrong arity supplied to relational access"); return nullptr; } for (unsigned i = 0; i < sorts.size(); ++i) { if (sorts[i] != domain[i+1]) { IF_VERBOSE(0, 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 mismatch for relational access"); return nullptr; } } func_decl_info info(m_family_id, k, 0, nullptr); return m.mk_func_decl(sym, arity, domain, r, info); } func_decl * dl_decl_plugin::mk_empty(parameter const& p) { ast_manager& m = *m_manager; if (!p.is_ast() || !is_sort(p.get_ast())) { m_manager->raise_exception("expected sort parameter"); return nullptr; } sort* r = to_sort(p.get_ast()); if (!is_rel_sort(r)) { return nullptr; } func_decl_info info(m_family_id, OP_RA_EMPTY, 1, &p); return m.mk_func_decl(m_empty_sym, 0, (sort*const*)nullptr, r, info); } func_decl* dl_decl_plugin::mk_project(unsigned num_params, parameter const* params, sort* r) { ast_manager& m = *m_manager; ptr_vector sorts; vector ps; TRACE("dl_decl_plugin", tout << mk_pp(r, m) << " "; for (unsigned i = 0; i < num_params; ++i) { tout << params[i] << " "; } tout << "\n"; ); if (!is_rel_sort(r, sorts)) { return nullptr; } SASSERT(sorts.size() >= num_params); // populate ps unsigned j = 0, i = 0; for (; i < num_params; ++i) { if (!params[i].is_int()) { m_manager->raise_exception("expecting integer parameter"); return nullptr; } unsigned k = params[i].get_int(); if (j > k) { m_manager->raise_exception("arguments to projection should be increasing"); return nullptr; } while (j < k) { ps.push_back(parameter(sorts[j])); ++j; } ++j; } for (; j < sorts.size(); ++j) { ps.push_back(parameter(sorts[j])); } SASSERT(ps.size() + num_params == sorts.size()); sort* r2 = m.mk_sort(m_family_id, DL_RELATION_SORT, ps.size(), ps.c_ptr()); func_decl_info info(m_family_id, OP_RA_PROJECT, num_params, params); return m.mk_func_decl(m_project_sym, 1, &r, r2, info); } 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 mismatch for arguments to union"); return nullptr; } if (!is_rel_sort(s1)) { return nullptr; } sort* domain[2] = { s1, s2 }; func_decl_info info(m_family_id, k, 0, nullptr); return m.mk_func_decl(m_union_sym, 2, domain, s1, info); } func_decl * dl_decl_plugin::mk_filter(parameter const& p, sort* r) { ast_manager& m = *m_manager; ptr_vector sorts; if (!is_rel_sort(r, sorts)) { return nullptr; } if (!p.is_ast() || !is_expr(p.get_ast())) { m_manager->raise_exception("ast expression expected to filter"); } expr* f = to_expr(p.get_ast()); // 1. f is of Boolean type. // 2. the free variables in f correspond to column types of r. if (!m.is_bool(f)) { m_manager->raise_exception("filter predicate should be of Boolean type"); return nullptr; } ptr_vector todo; todo.push_back(f); ast_mark mark; while (!todo.empty()) { expr* e = todo.back(); todo.pop_back(); if (mark.is_marked(e)) { continue; } mark.mark(e, true); unsigned idx; switch(e->get_kind()) { case AST_VAR: idx = to_var(e)->get_idx(); if (idx >= sorts.size()) { m_manager->raise_exception("illegal index"); return nullptr; } if (sorts[idx] != m.get_sort(e)) { m_manager->raise_exception("sort mismatch in filter"); return nullptr; } break; case AST_APP: for (unsigned i = 0; i < to_app(e)->get_num_args(); ++i) { todo.push_back(to_app(e)->get_arg(i)); } break; case AST_QUANTIFIER: m_manager->raise_exception("quantifiers are not allowed in filter expressions"); return nullptr; default: m_manager->raise_exception("unexpected filter expression kind"); return nullptr; } } func_decl_info info(m_family_id, OP_RA_FILTER, 1, &p); return m.mk_func_decl(m_filter_sym, 1, &r, r, info); } func_decl * dl_decl_plugin::mk_rename(unsigned num_params, parameter const* params, sort* r) { ptr_vector sorts; if (!is_rel_sort(r, sorts)) { return nullptr; } unsigned index0 = 0; sort* last_sort = nullptr; SASSERT(num_params > 0); for (unsigned i = 0; i < num_params; ++i) { parameter const& p = params[i]; if (!p.is_int()) { m_manager->raise_exception("expected integer parameter"); return nullptr; } unsigned j = p.get_int(); if (j >= sorts.size()) { // We should not use ast_pp anymore on error messages. // m_manager->raise_exception("index %d out of bound %s : %d", j, ast_pp(r, *m_manager).c_str(), sorts.size()); m_manager->raise_exception("index out of bound"); return nullptr; } if (i == 0) { index0 = j; last_sort = sorts[j]; } else { std::swap(last_sort, sorts[j]); } } sorts[index0] = last_sort; vector params2; for (unsigned i = 0; i < sorts.size(); ++i) { params2.push_back(parameter(sorts[i])); } sort* rng = m_manager->mk_sort(m_family_id, DL_RELATION_SORT, params2.size(), params2.c_ptr()); func_decl_info info(m_family_id, OP_RA_RENAME, num_params, params); return m_manager->mk_func_decl(m_rename_sym, 1, &r, rng, info); } func_decl * dl_decl_plugin::mk_join(unsigned num_params, parameter const* params, sort* r1, sort* r2) { vector params2; ptr_vector sorts1, sorts2; if (!is_rel_sort(r1, sorts1)) { return nullptr; } if (!is_rel_sort(r2, sorts2)) { return nullptr; } for (unsigned i = 0; i < sorts1.size(); ++i) { params2.push_back(parameter(sorts1[i])); } for (unsigned i = 0; i < sorts2.size(); ++i) { params2.push_back(parameter(sorts2[i])); } if (0 != num_params % 2) { m_manager->raise_exception("expecting an even number of parameters to join"); return nullptr; } for (unsigned i = 0; i + 1 < num_params; i += 2) { parameter const& p1 = params[i]; parameter const& p2 = params[i+1]; if (!p1.is_int() || !p2.is_int()) { m_manager->raise_exception("encountered non-integer parameter"); return nullptr; } unsigned i1 = p1.get_int(); unsigned i2 = p2.get_int(); if (i1 >= sorts1.size() || i2 >= sorts2.size()) { m_manager->raise_exception("index out of bounds"); return nullptr; } if (sorts1[i1] != sorts2[i2]) { m_manager->raise_exception("sort mismatch in join"); return nullptr; } } sort* args[2] = { r1, r2 }; sort* rng = m_manager->mk_sort(m_family_id, DL_RELATION_SORT, params2.size(), params2.c_ptr()); func_decl_info info(m_family_id, OP_RA_JOIN, num_params, params); return m_manager->mk_func_decl(m_join_sym, 2, args, rng, info); } func_decl* dl_decl_plugin::mk_complement(sort* s) { if (!is_rel_sort(s)) { return nullptr; } func_decl_info info(m_family_id, OP_RA_COMPLEMENT, 0, nullptr); return m_manager->mk_func_decl(m_complement_sym, 1, &s, s, info); } func_decl * dl_decl_plugin::mk_negation_filter(unsigned num_params, parameter const* params, sort* r1, sort* r2) { ptr_vector sorts1, sorts2; if (!is_rel_sort(r1, sorts1)) { return nullptr; } if (!is_rel_sort(r2, sorts2)) { return nullptr; } if (0 != num_params % 2) { m_manager->raise_exception("expecting an even number of parameters to negation filter"); return nullptr; } for (unsigned i = 0; i + 1 < num_params; i += 2) { parameter const& p1 = params[i]; parameter const& p2 = params[i+1]; if (!p1.is_int() || !p2.is_int()) { m_manager->raise_exception("encountered non-integer parameter"); return nullptr; } unsigned i1 = p1.get_int(); unsigned i2 = p2.get_int(); if (i1 >= sorts1.size() || i2 >= sorts2.size()) { m_manager->raise_exception("index out of bounds"); return nullptr; } if (sorts1[i1] != sorts2[i2]) { m_manager->raise_exception("sort mismatch in join"); return nullptr; } } sort* args[2] = { r1, r2 }; func_decl_info info(m_family_id, OP_RA_NEGATION_FILTER, num_params, params); return m_manager->mk_func_decl(m_negation_filter_sym, 2, args, r1, info); } func_decl * dl_decl_plugin::mk_is_empty(sort* s) { if (!is_rel_sort(s)) { return nullptr; } func_decl_info info(m_family_id, OP_RA_IS_EMPTY, 0, nullptr); sort* rng = m_manager->mk_bool_sort(); return m_manager->mk_func_decl(m_is_empty_sym, 1, &s, rng, info); } func_decl * dl_decl_plugin::mk_constant(parameter const* params) { parameter const& p = params[0]; parameter const& ps = params[1]; if (!p.is_rational() || !p.get_rational().is_uint64()) { m_manager->raise_exception("first parameter should be a rational"); return nullptr; } if (!ps.is_ast() || !is_sort(ps.get_ast()) || !is_fin_sort(to_sort(ps.get_ast()))) { m_manager->raise_exception("second parameter should be a finite domain sort"); return nullptr; } sort* s = to_sort(ps.get_ast()); func_decl_info info(m_family_id, OP_DL_CONSTANT, 2, params); return m_manager->mk_func_decl(m_num_sym, 0, (sort*const*)nullptr, s, info); } func_decl * dl_decl_plugin::mk_compare(decl_kind k, symbol const& sym, sort *const* domain) { if (!is_sort_of(domain[0], m_family_id, DL_FINITE_SORT)) { m_manager->raise_exception("expecting finite domain sort"); return nullptr; } if (domain[0] != domain[1]) { m_manager->raise_exception("expecting two identical finite domain sorts"); return nullptr; } func_decl_info info(m_family_id, k, 0, nullptr); return m_manager->mk_func_decl(sym, 2, domain, m_manager->mk_bool_sort(), info); } func_decl * dl_decl_plugin::mk_clone(sort* s) { if (!is_rel_sort(s)) { return nullptr; } func_decl_info info(m_family_id, OP_RA_CLONE, 0, nullptr); return m_manager->mk_func_decl(m_clone_sym, 1, &s, s, info); } func_decl * dl_decl_plugin::mk_func_decl( decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { func_decl* result = nullptr; switch(k) { case OP_RA_STORE: case OP_RA_SELECT: if (!check_params(0, 0, num_parameters) || !check_domain(1, UINT_MAX, arity)) { return nullptr; } result = mk_store_select(k, arity, domain); break; case OP_RA_EMPTY: if (!check_params( 1, 1, num_parameters) || !check_domain(0, 0, arity)) { return nullptr; } result = mk_empty(parameters[0]); break; case OP_RA_JOIN: if (!check_params(0, UINT_MAX, num_parameters) || !check_domain(2, 2, arity)) { return nullptr; } result = mk_join(num_parameters, parameters, domain[0], domain[1]); break; case OP_RA_UNION: case OP_RA_WIDEN: if (!check_params( 0, 0, num_parameters) || !check_domain(2, 2, arity)) { return nullptr; } result = mk_unionw(k, domain[0], domain[1]); break; case OP_RA_PROJECT: if (!check_params( 1, UINT_MAX, num_parameters) || !check_domain(1, 1, arity)) { return nullptr; } result = mk_project(num_parameters, parameters, domain[0]); break; case OP_RA_FILTER: if (!check_params( 1, 1, num_parameters) || !check_domain(1, 1, arity)) { return nullptr; } result = mk_filter(parameters[0], domain[0]); break; case OP_RA_IS_EMPTY: if (!check_params( 0, 0, num_parameters) || !check_domain(1, 1, arity)) { return nullptr; } result = mk_is_empty(domain[0]); break; case OP_RA_RENAME: if (!check_params( 2, UINT_MAX, num_parameters) || !check_domain(1, 1, arity)) { return nullptr; } result = mk_rename(num_parameters, parameters, domain[0]); break; case OP_RA_COMPLEMENT: if (!check_params( 0, 0, num_parameters) || !check_domain(1, 1, arity)) { return nullptr; } result = mk_complement(domain[0]); break; case OP_RA_NEGATION_FILTER: if (!check_params(1, UINT_MAX, num_parameters) || !check_domain(2, 2, arity)) { return nullptr; } result = mk_negation_filter(num_parameters, parameters, domain[0], domain[1]); break; case OP_RA_CLONE: if (!check_params(0, 0, num_parameters) || !check_domain(1, 1, arity)) { return nullptr; } result = mk_clone(domain[0]); break; case OP_DL_CONSTANT: if (!check_params( 2, 2, num_parameters) || !check_domain(0, 0, arity)) { return nullptr; } result = mk_constant(parameters); break; case OP_DL_LT: if (!check_params( 0, 0, num_parameters) || !check_domain(2, 2, arity)) { return nullptr; } result = mk_compare(OP_DL_LT, m_lt_sym, domain); break; case OP_DL_REP: { if (!check_domain(0, 0, num_parameters) || !check_domain(1, 1, arity)) return nullptr; func_decl_info info(m_family_id, k, 0, nullptr); result = m_manager->mk_func_decl(symbol("rep"), 1, domain, range, info); break; } case OP_DL_ABS: { if (!check_domain(0, 0, num_parameters) || !check_domain(1, 1, arity)) return nullptr; func_decl_info info(m_family_id, k, 0, nullptr); result = m_manager->mk_func_decl(symbol("abs"), 1, domain, range, info); break; } default: m_manager->raise_exception("operator not recognized"); return nullptr; } TRACE("dl_decl_plugin", tout << mk_pp(result, *m_manager) << "\n";); return result; } void dl_decl_plugin::get_op_names(svector & op_names, symbol const & logic) { } void dl_decl_plugin::get_sort_names(svector & sort_names, symbol const & logic) { } dl_decl_util::ast_plugin_registrator::ast_plugin_registrator(ast_manager& m) { // ensure required plugins are installed into the ast_manager reg_decl_plugins(m); } dl_decl_util::dl_decl_util(ast_manager& m): m_plugin_registrator(m), m(m), m_arith(m), m_bv(m), m_fid(m.mk_family_id(symbol("datalog_relation"))) {} // create a constant belonging to a given finite domain. app* dl_decl_util::mk_numeral(uint64_t value, sort* s) { if (is_finite_sort(s)) { uint64_t sz = 0; if (try_get_size(s, sz) && sz <= value) { m.raise_exception("value is out of bounds"); } parameter params[2] = { parameter(rational(value, rational::ui64())), parameter(s) }; return m.mk_const(m.mk_func_decl(m_fid, OP_DL_CONSTANT, 2, params, 0, (sort*const*)nullptr)); } if (m_arith.is_int(s) || m_arith.is_real(s)) { return m_arith.mk_numeral(rational(value, rational::ui64()), s); } if (m_bv.is_bv_sort(s)) { return m_bv.mk_numeral(rational(value, rational::ui64()), s); } if (m.is_bool(s)) { if (value == 0) { return m.mk_false(); } SASSERT(value == 1); return m.mk_true(); } std::stringstream strm; strm << "sort '" << mk_pp(s, m) << "' is not recognized as a sort that contains numeric values.\nUse Bool, BitVec, Int, Real, or a Finite domain sort"; m.raise_exception(strm.str()); return nullptr; } bool dl_decl_util::is_numeral(const expr* e, uint64_t& v) const { if (is_numeral(e)) { const app* c = to_app(e); SASSERT(c->get_decl()->get_num_parameters() == 2); parameter const& p = c->get_decl()->get_parameter(0); SASSERT(p.is_rational()); SASSERT(p.get_rational().is_uint64()); v = p.get_rational().get_uint64(); return true; } return false; } bool dl_decl_util::is_numeral_ext(expr* e, uint64_t& v) const { if (is_numeral(e, v)) { return true; } rational val; unsigned bv_size = 0; if (m_bv.is_numeral(e, val, bv_size) && bv_size < 64) { SASSERT(val.is_uint64()); v = val.get_uint64(); return true; } if (m.is_true(e)) { v = 1; return true; } if (m.is_false(e)) { v = 0; return true; } return false; } bool dl_decl_util::is_numeral_ext(expr* c) const { if (is_numeral(c)) return true; rational val; unsigned bv_size = 0; if (m_arith.is_numeral(c, val) && val.is_uint64()) return true; if (m_bv.is_numeral(c, val, bv_size) && bv_size < 64) return true; return m.is_true(c) || m.is_false(c); } sort* dl_decl_util::mk_sort(const symbol& name, uint64_t domain_size) { if (domain_size == 0) { std::stringstream sstm; sstm << "Domain size of sort '" << name << "' may not be 0"; throw default_exception(sstm.str()); } parameter params[2] = { parameter(name), parameter(rational(domain_size, rational::ui64())) }; return m.mk_sort(m_fid, DL_FINITE_SORT, 2, params); } bool dl_decl_util::try_get_size(const sort * s, uint64_t& size) const { sort_size sz = s->get_info()->get_num_elements(); if (sz.is_finite()) { size = sz.size(); return true; } return false; } app* dl_decl_util::mk_lt(expr* a, expr* b) { expr* args[2] = { a, b }; return m.mk_app(m_fid, OP_DL_LT, 0, nullptr, 2, args); } app* dl_decl_util::mk_le(expr* a, expr* b) { expr* args[2] = { b, a }; return m.mk_not(m.mk_app(m_fid, OP_DL_LT, 0, nullptr, 2, args)); } sort* dl_decl_util::mk_rule_sort() { return m.mk_sort(m_fid, DL_RULE_SORT); } app* dl_decl_util::mk_rule(symbol const& name, unsigned num_args, expr* const* args) { ptr_buffer sorts; for (unsigned i = 0; i < num_args; ++i) { sorts.push_back(m.get_sort(args[i])); } func_decl* f = m.mk_func_decl(name, num_args, sorts.c_ptr(), mk_rule_sort()); return m.mk_app(f, num_args, args); } }; z3-z3-4.8.7/src/ast/dl_decl_plugin.h000066400000000000000000000155461356505360400171350ustar00rootroot00000000000000/*++ Copyright (c) 2010 Microsoft Corporation Module Name: dl_decl_plugin.h Abstract: Author: Nikolaj Bjorner (nbjorner) 2010-04-10 Revision History: --*/ #ifndef DL_DECL_PLUGIN_H_ #define DL_DECL_PLUGIN_H_ #include "ast/ast.h" #include "ast/arith_decl_plugin.h" #include "ast/bv_decl_plugin.h" namespace datalog { enum dl_sort_kind { DL_RELATION_SORT, DL_FINITE_SORT, DL_RULE_SORT }; enum dl_op_kind { OP_RA_STORE, OP_RA_EMPTY, OP_RA_IS_EMPTY, OP_RA_JOIN, OP_RA_UNION, OP_RA_WIDEN, OP_RA_PROJECT, OP_RA_FILTER, OP_RA_NEGATION_FILTER, OP_RA_RENAME, OP_RA_COMPLEMENT, OP_RA_SELECT, OP_RA_CLONE, OP_DL_CONSTANT, OP_DL_LT, OP_DL_REP, OP_DL_ABS, LAST_RA_OP }; class dl_decl_plugin : public decl_plugin { symbol m_store_sym; symbol m_empty_sym; symbol m_is_empty_sym; symbol m_join_sym; symbol m_union_sym; symbol m_widen_sym; symbol m_project_sym; symbol m_filter_sym; symbol m_negation_filter_sym; symbol m_rename_sym; symbol m_complement_sym; symbol m_select_sym; symbol m_clone_sym; symbol m_num_sym; symbol m_lt_sym; symbol m_le_sym; symbol m_rule_sym; bool check_bounds(char const* msg, unsigned low, unsigned up, unsigned val) const; bool check_domain(unsigned low, unsigned up, unsigned val) const; bool check_params(unsigned low, unsigned up, unsigned val) const; bool is_rel_sort(sort* s); bool is_rel_sort(sort* s, ptr_vector& sorts); bool is_fin_sort(sort* r); func_decl * mk_store_select(decl_kind k, unsigned arity, sort * const * domain); func_decl * mk_empty(parameter const& p); func_decl * mk_is_empty(sort* r); func_decl * mk_join(unsigned num_params, parameter const* params, sort* r1, sort* r2); func_decl * mk_unionw(decl_kind k, sort* s1, sort* s2); func_decl * mk_project(unsigned num_params, parameter const * params, sort* r); func_decl * mk_filter(parameter const& p, sort* r); func_decl * mk_rename(unsigned num_params, parameter const * params, sort* r); func_decl * mk_complement(sort* r); func_decl * mk_negation_filter(unsigned num_params, parameter const* params, sort* r1, sort* r2); func_decl * mk_constant(parameter const* params); func_decl * mk_compare(decl_kind k, symbol const& sym, sort*const* domain); func_decl * mk_clone(sort* r); func_decl * mk_rule(unsigned arity); sort * mk_finite_sort(unsigned num_params, parameter const* params); sort * mk_relation_sort(unsigned num_params, parameter const* params); sort * mk_rule_sort(); public: dl_decl_plugin(); ~dl_decl_plugin() override {} decl_plugin * mk_fresh() override { return alloc(dl_decl_plugin); } // // Contract for sort DL_RELATION_SORT // parameters[0] - 1st dimension // ... // parameters[n-1] - nth dimension // // Contract for sort DL_FINITE_SORT // parameters[0] - name // parameters[1] - uint64 // sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) override; // // Contract for func_decl: // parameters[0] - array sort // Contract for OP_DL_CONSTANT: // parameters[0] - rational containing uint64_t with constant value // parameters[1] - a DL_FINITE_SORT sort of the constant // Contract for others: // no parameters func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) override; void get_op_names(svector & op_names, symbol const & logic) override; void get_sort_names(svector & sort_names, symbol const & logic) override; bool is_value(app * e) const override { return is_app_of(e, m_family_id, OP_DL_CONSTANT); } bool is_unique_value(app * e) const override { return is_value(e); } }; class dl_decl_util { /** Some plugins need to be registered in the ast_manager before we create some of the member objects (the bv_util requires bv plugin installed). Doing this in the constructor of the dl_decl_plugin object is too late (as member objects are created before we get into the constructor), so we have this auxiliary object that is the first object of the context, so that it gets created first. It's only purpose is that in its constructor the required plugins are added to the ast manager. */ class ast_plugin_registrator { public: ast_plugin_registrator(ast_manager& m); }; ast_plugin_registrator m_plugin_registrator; ast_manager& m; arith_util m_arith; bv_util m_bv; family_id m_fid; public: dl_decl_util(ast_manager& m); // create a constant belonging to a given finite domain. // the options include the DL_FINITE_SORT, BV_SORT, and BOOL_SORT app* mk_numeral(uint64_t value, sort* s); app* mk_lt(expr* a, expr* b); app* mk_le(expr* a, expr* b); bool is_lt(const expr* a) const { return is_app_of(a, m_fid, OP_DL_LT); } bool is_numeral(const expr* c) const { return is_app_of(c, m_fid, OP_DL_CONSTANT); } bool is_numeral(const expr* e, uint64_t& v) const; // // Utilities for extracting constants // from bit-vectors and finite domains. // bool is_numeral_ext(expr* c, uint64_t& v) const; bool is_numeral_ext(expr* c) const; sort* mk_sort(const symbol& name, uint64_t domain_size); bool try_get_size(const sort *, uint64_t& size) const; bool is_finite_sort(sort* s) const { return is_sort_of(s, m_fid, DL_FINITE_SORT); } bool is_finite_sort(expr* e) const { return is_finite_sort(m.get_sort(e)); } bool is_rule_sort(sort* s) const { return is_sort_of(s, m_fid, DL_RULE_SORT); } sort* mk_rule_sort(); app* mk_rule(symbol const& name, unsigned num_args = 0, expr* const* args = nullptr); app* mk_fact(symbol const& name) { return mk_rule(name, 0, nullptr); } ast_manager& get_manager() const { return m; } family_id get_family_id() const { return m_fid; } }; }; #endif /* DL_DECL_PLUGIN_H_ */ z3-z3-4.8.7/src/ast/expr2polynomial.cpp000066400000000000000000000371601356505360400176640ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: expr2polynomial.cpp Abstract: Translator from Z3 expressions into multivariate polynomials (and back). Author: Leonardo (leonardo) 2011-12-23 Notes: --*/ #include "ast/expr2polynomial.h" #include "ast/expr2var.h" #include "ast/arith_decl_plugin.h" #include "ast/ast_smt2_pp.h" #include "util/z3_exception.h" #include "util/common_msgs.h" struct expr2polynomial::imp { struct frame { app * m_curr; unsigned m_idx; frame():m_curr(nullptr), m_idx(0) {} frame(app * t):m_curr(t), m_idx(0) {} }; expr2polynomial & m_wrapper; ast_manager & m_am; arith_util m_autil; polynomial::manager & m_pm; expr2var * m_expr2var; bool m_expr2var_owner; expr_ref_vector m_var2expr; obj_map m_cache; expr_ref_vector m_cached_domain; polynomial::polynomial_ref_vector m_cached_polynomials; polynomial::scoped_numeral_vector m_cached_denominators; svector m_frame_stack; polynomial::polynomial_ref_vector m_presult_stack; polynomial::scoped_numeral_vector m_dresult_stack; bool m_use_var_idxs; volatile bool m_cancel; imp(expr2polynomial & w, ast_manager & am, polynomial::manager & pm, expr2var * e2v, bool use_var_idxs): m_wrapper(w), m_am(am), m_autil(am), m_pm(pm), m_expr2var(e2v == nullptr && !use_var_idxs ? alloc(expr2var, am) : e2v), m_expr2var_owner(e2v == nullptr && !use_var_idxs), m_var2expr(am), m_cached_domain(am), m_cached_polynomials(pm), m_cached_denominators(pm.m()), m_presult_stack(pm), m_dresult_stack(pm.m()), m_use_var_idxs(use_var_idxs), m_cancel(false) { } ~imp() { if (m_expr2var_owner) dealloc(m_expr2var); } ast_manager & m() { return m_am; } polynomial::manager & pm() { return m_pm; } polynomial::numeral_manager & nm() { return pm().m(); } void reset() { m_frame_stack.reset(); m_presult_stack.reset(); m_dresult_stack.reset(); } void reset_cache() { m_cache.reset(); m_cached_domain.reset(); m_cached_polynomials.reset(); m_cached_denominators.reset(); } void checkpoint() { if (m_cancel) throw default_exception(Z3_CANCELED_MSG); } void throw_not_polynomial() { throw default_exception("the given expression is not a polynomial"); } void throw_no_int_var() { throw default_exception("integer variables are not allowed in the given polynomial"); } void push_frame(app * t) { m_frame_stack.push_back(frame(t)); } void cache_result(expr * t) { SASSERT(!m_cache.contains(t)); SASSERT(m_cached_denominators.size() == m_cached_polynomials.size()); SASSERT(m_cached_denominators.size() == m_cached_domain.size()); if (t->get_ref_count() <= 1) return; unsigned idx = m_cached_polynomials.size(); m_cache.insert(t, idx); m_cached_domain.push_back(t); m_cached_polynomials.push_back(m_presult_stack.back()); m_cached_denominators.push_back(m_dresult_stack.back()); } bool is_cached(expr * t) { return t->get_ref_count() > 1 && m_cache.contains(t); } bool is_int_real(expr * t) { return m_autil.is_int_real(t); } void store_result(expr * t, polynomial::polynomial * p, polynomial::numeral & d) { m_presult_stack.push_back(p); m_dresult_stack.push_back(d); cache_result(t); } void store_var_poly(expr * t) { polynomial::var x; if (m_use_var_idxs) { SASSERT(::is_var(t)); if (m_autil.is_int(t)) throw_no_int_var(); unsigned idx = to_var(t)->get_idx(); while (idx >= m_pm.num_vars()) m_pm.mk_var(); x = static_cast(idx); } else { x = m_expr2var->to_var(t); if (x == UINT_MAX) { bool is_int = m_autil.is_int(t); x = m_wrapper.mk_var(is_int); m_expr2var->insert(t, x); if (x >= m_var2expr.size()) m_var2expr.resize(x+1, nullptr); m_var2expr.set(x, t); } } polynomial::numeral one(1); store_result(t, pm().mk_polynomial(x), one); } void store_const_poly(app * n) { rational val; VERIFY(m_autil.is_numeral(n, val)); polynomial::scoped_numeral d(nm()); d = val.to_mpq().denominator(); store_result(n, pm().mk_const(numerator(val)), d); } bool visit_arith_app(app * t) { switch (t->get_decl_kind()) { case OP_NUM: store_const_poly(t); return true; case OP_ADD: case OP_SUB: case OP_MUL: case OP_UMINUS: case OP_TO_REAL: push_frame(t); return false; case OP_POWER: { rational k; SASSERT(t->get_num_args() == 2); if (!m_autil.is_numeral(t->get_arg(1), k) || !k.is_int() || !k.is_unsigned()) { if (m_use_var_idxs) throw_not_polynomial(); else store_var_poly(t); return true; } push_frame(t); return false; } default: // can't handle operator if (m_use_var_idxs) throw_not_polynomial(); store_var_poly(t); return true; } } bool visit(expr * t) { SASSERT(is_int_real(t)); if (is_cached(t)) { unsigned idx = m_cache.find(t); m_presult_stack.push_back(m_cached_polynomials.get(idx)); m_dresult_stack.push_back(m_cached_denominators.get(idx)); return true; } SASSERT(!is_quantifier(t)); if (::is_var(t)) { store_var_poly(t); return true; } SASSERT(is_app(t)); if (!m_autil.is_arith_expr(t)) { if (m_use_var_idxs) throw_not_polynomial(); store_var_poly(t); return true; } return visit_arith_app(to_app(t)); } void pop(unsigned num_args) { SASSERT(m_presult_stack.size() == m_dresult_stack.size()); SASSERT(m_presult_stack.size() >= num_args); m_presult_stack.shrink(m_presult_stack.size() - num_args); m_dresult_stack.shrink(m_dresult_stack.size() - num_args); } polynomial::polynomial * const * polynomial_args(unsigned num_args) { SASSERT(m_presult_stack.size() >= num_args); return m_presult_stack.c_ptr() + m_presult_stack.size() - num_args; } polynomial::numeral const * denominator_args(unsigned num_args) { SASSERT(m_dresult_stack.size() >= num_args); return m_dresult_stack.c_ptr() + m_dresult_stack.size() - num_args; } template void process_add_sub(app * t) { SASSERT(t->get_num_args() <= m_presult_stack.size()); unsigned num_args = t->get_num_args(); polynomial::polynomial * const * p_args = polynomial_args(num_args); polynomial::numeral const * d_args = denominator_args(num_args); polynomial::polynomial_ref p(pm()); polynomial::polynomial_ref p_aux(pm()); polynomial::scoped_numeral d(nm()); polynomial::scoped_numeral d_aux(nm()); d = 1; for (unsigned i = 0; i < num_args; i++) { nm().lcm(d, d_args[i], d); } p = pm().mk_zero(); for (unsigned i = 0; i < num_args; i++) { checkpoint(); nm().div(d, d_args[i], d_aux); p_aux = pm().mul(d_aux, p_args[i]); if (i == 0) p = p_aux; else if (is_add) p = pm().add(p, p_aux); else p = pm().sub(p, p_aux); } pop(num_args); store_result(t, p.get(), d.get()); } void process_add(app * t) { process_add_sub(t); } void process_sub(app * t) { process_add_sub(t); } void process_mul(app * t) { SASSERT(t->get_num_args() <= m_presult_stack.size()); unsigned num_args = t->get_num_args(); polynomial::polynomial * const * p_args = polynomial_args(num_args); polynomial::numeral const * d_args = denominator_args(num_args); polynomial::polynomial_ref p(pm()); polynomial::scoped_numeral d(nm()); p = pm().mk_const(rational(1)); d = 1; for (unsigned i = 0; i < num_args; i++) { checkpoint(); p = pm().mul(p, p_args[i]); d = d * d_args[i]; } pop(num_args); store_result(t, p.get(), d.get()); } void process_uminus(app * t) { SASSERT(t->get_num_args() <= m_presult_stack.size()); polynomial::polynomial_ref neg_p(pm()); neg_p = pm().neg(m_presult_stack.back()); m_presult_stack.pop_back(); m_presult_stack.push_back(neg_p); cache_result(t); } void process_power(app * t) { SASSERT(t->get_num_args() <= m_presult_stack.size()); rational _k; VERIFY(m_autil.is_numeral(t->get_arg(1), _k)); SASSERT(_k.is_int() && _k.is_unsigned()); unsigned k = _k.get_unsigned(); polynomial::polynomial_ref p(pm()); polynomial::scoped_numeral d(nm()); unsigned num_args = t->get_num_args(); polynomial::polynomial * const * p_args = polynomial_args(num_args); polynomial::numeral const * d_args = denominator_args(num_args); pm().pw(p_args[0], k, p); nm().power(d_args[0], k, d); pop(num_args); store_result(t, p.get(), d.get()); } void process_to_real(app * t) { // do nothing cache_result(t); } void process_app(app * t) { SASSERT(m_presult_stack.size() == m_dresult_stack.size()); switch (t->get_decl_kind()) { case OP_ADD: process_add(t); return; case OP_SUB: process_sub(t); return; case OP_MUL: process_mul(t); return; case OP_POWER: process_power(t); return; case OP_UMINUS: process_uminus(t); return; case OP_TO_REAL: process_to_real(t); return; default: UNREACHABLE(); } } bool to_polynomial(expr * t, polynomial::polynomial_ref & p, polynomial::scoped_numeral & d) { if (!is_int_real(t)) return false; reset(); if (!visit(t)) { while (!m_frame_stack.empty()) { begin_loop: checkpoint(); frame & fr = m_frame_stack.back(); app * a = fr.m_curr; TRACE("expr2polynomial", tout << "processing: " << fr.m_idx << "\n" << mk_ismt2_pp(a, m()) << "\n";); unsigned num_args = a->get_num_args(); while (fr.m_idx < num_args) { expr * arg = a->get_arg(fr.m_idx); fr.m_idx++; if (!visit(arg)) goto begin_loop; } process_app(a); m_frame_stack.pop_back(); } } p = m_presult_stack.back(); d = m_dresult_stack.back(); reset(); return true; } bool is_int_poly(polynomial::polynomial_ref const & p) { unsigned sz = size(p); for (unsigned i = 0; i < sz; i++) { polynomial::monomial * m = pm().get_monomial(p, i); unsigned msz = pm().size(m); for (unsigned j = 0; j < msz; j++) { polynomial::var x = pm().get_var(m, j); if (!m_wrapper.is_int(x)) return false; } } return true; } void to_expr(polynomial::polynomial_ref const & p, bool use_power, expr_ref & r) { expr_ref_buffer args(m()); expr_ref_buffer margs(m()); unsigned sz = size(p); bool is_int = is_int_poly(p); for (unsigned i = 0; i < sz; i++) { margs.reset(); polynomial::monomial * _m = pm().get_monomial(p, i); polynomial::numeral const & a = pm().coeff(p, i); if (!nm().is_one(a)) { margs.push_back(m_autil.mk_numeral(rational(a), is_int)); } unsigned msz = pm().size(_m); for (unsigned j = 0; j < msz; j++) { polynomial::var x = pm().get_var(_m, j); expr * t; if (m_use_var_idxs) { t = m().mk_var(x, m_autil.mk_real()); } else { t = m_var2expr.get(x); if (m_wrapper.is_int(x) && !is_int) { t = m_autil.mk_to_real(t); } } unsigned d = pm().degree(_m, j); if (use_power && d > 1) { margs.push_back(m_autil.mk_power(t, m_autil.mk_numeral(rational(d), is_int))); } else { for (unsigned k = 0; k < d; k++) margs.push_back(t); } } if (margs.empty()) { args.push_back(m_autil.mk_numeral(rational(1), is_int)); } else if (margs.size() == 1) { args.push_back(margs[0]); } else { args.push_back(m_autil.mk_mul(margs.size(), margs.c_ptr())); } } if (args.empty()) { r = m_autil.mk_numeral(rational(0), is_int); } else if (args.size() == 1) { r = args[0]; } else { r = m_autil.mk_add(args.size(), args.c_ptr()); } } void set_cancel(bool f) { m_cancel = f; } }; expr2polynomial::expr2polynomial(ast_manager & am, polynomial::manager & pm, expr2var * e2v, bool use_var_idxs) { m_imp = alloc(imp, *this, am, pm, e2v, use_var_idxs); } expr2polynomial::~expr2polynomial() { dealloc(m_imp); } ast_manager & expr2polynomial::m() const { return m_imp->m_am; } polynomial::manager & expr2polynomial::pm() const { return m_imp->m_pm; } bool expr2polynomial::to_polynomial(expr * t, polynomial::polynomial_ref & p, polynomial::scoped_numeral & d) { return m_imp->to_polynomial(t, p, d); } void expr2polynomial::to_expr(polynomial::polynomial_ref const & p, bool use_power, expr_ref & r) { m_imp->to_expr(p, use_power, r); } bool expr2polynomial::is_var(expr * t) const { SASSERT(!m_imp->m_use_var_idxs); return m_imp->m_expr2var->is_var(t); } expr2var const & expr2polynomial::get_mapping() const { SASSERT(!m_imp->m_use_var_idxs); return *(m_imp->m_expr2var); } void expr2polynomial::set_cancel(bool f) { m_imp->set_cancel(f); } default_expr2polynomial::default_expr2polynomial(ast_manager & am, polynomial::manager & pm): expr2polynomial(am, pm, nullptr) { } default_expr2polynomial::~default_expr2polynomial() { } bool default_expr2polynomial::is_int(polynomial::var x) const { return m_is_int[x]; } polynomial::var default_expr2polynomial::mk_var(bool is_int) { polynomial::var x = pm().mk_var(); m_is_int.reserve(x+1, false); m_is_int[x] = is_int; return x; } z3-z3-4.8.7/src/ast/expr2polynomial.h000066400000000000000000000063251356505360400173300ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: expr2polynomial.h Abstract: Translator from Z3 expressions into multivariate polynomials (and back). Author: Leonardo (leonardo) 2011-12-23 Notes: --*/ #ifndef EXPR2POLYNOMIAL_H_ #define EXPR2POLYNOMIAL_H_ #include "ast/ast.h" #include "math/polynomial/polynomial.h" class expr2var; class expr2polynomial { struct imp; imp * m_imp; public: expr2polynomial(ast_manager & am, polynomial::manager & pm, expr2var * e2v, /* If true, the expressions converted into polynomials should only contain Z3 free variables. A Z3 variable x, with idx i, is converted into the variable i of the polynomial manager pm. An exception is thrown if there is a mismatch between the sorts x and the variable in the polynomial manager. The argument e2v is ignored when use_var_idxs is true. Moreover, only real variables are allowed. */ bool use_var_idxs = false ); virtual ~expr2polynomial(); ast_manager & m() const; polynomial::manager & pm() const; /** \brief Convert a Z3 expression into a polynomial in Z[x0, ..., x_n]. Since Z3 expressions may be representing polynomials in Q[x0, ..., x_n], the method also returns a "denominator" d. Thus, we have that n is equal to p/d \remark Return false if t is not an integer or real expression. \pre The only supported operators are MUL, ADD, SUB, UMINUS, TO_REAL, TO_INT, POWER (with constants) */ bool to_polynomial(expr * t, polynomial::polynomial_ref & p, polynomial::scoped_numeral & d); /** \brief Convert a polynomial into a Z3 expression. \remark If the polynomial has one real variable, then the resultant expression is an real expression. Otherwise, it is an integer */ void to_expr(polynomial::polynomial_ref const & p, bool use_power, expr_ref & r); /** \brief Return true if t was encoded as a variable by the translator. */ bool is_var(expr * t) const; /** \brief Return the mapping from expressions to variables \pre the object was created using use_var_idxs = false. */ expr2var const & get_mapping() const; /** \brief Cancel/Interrupt execution. */ void set_cancel(bool f); /** \brief Return true if the variable is associated with an expression of integer sort. */ virtual bool is_int(polynomial::var x) const { UNREACHABLE(); return false; } protected: virtual polynomial::var mk_var(bool is_int) { UNREACHABLE(); return polynomial::null_var; } }; class default_expr2polynomial : public expr2polynomial { svector m_is_int; public: default_expr2polynomial(ast_manager & am, polynomial::manager & pm); ~default_expr2polynomial() override; bool is_int(polynomial::var x) const override; protected: polynomial::var mk_var(bool is_int) override; }; #endif z3-z3-4.8.7/src/ast/expr2var.cpp000066400000000000000000000052551356505360400162710ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: expr2var.h Abstract: The mapping between Z3 expressions and (low level) variables. Example of low level variables: - SAT solver - Polynomial - etc. Author: Leonardo (leonardo) 2011-12-23 Notes: --*/ #include "ast/expr2var.h" #include "ast/ast_smt2_pp.h" #include "util/ref_util.h" void expr2var::insert(expr * n, var v) { if (!is_uninterp_const(n)) { TRACE("expr2var", tout << "interpreted:\n" << mk_ismt2_pp(n, m()) << "\n";); m_interpreted_vars = true; } 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); } expr2var::expr2var(ast_manager & m): m_manager(m), m_interpreted_vars(false) { } expr2var::~expr2var() { for (auto & kv : m_mapping) { m().dec_ref(kv.m_key); } } expr2var::var expr2var::to_var(expr * n) const { 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 { for (auto const& kv : m_mapping) { out << mk_ismt2_pp(kv.m_key, m()) << " -> " << kv.m_value << "\n"; } } void expr2var::mk_inv(expr_ref_vector & var2expr) const { for (auto & kv : m_mapping) { expr * t = kv.m_key; var x = kv.m_value; if (x >= var2expr.size()) var2expr.resize(x+1, nullptr); var2expr.set(x, t); } } void expr2var::reset() { 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; } void expr2var::push() { m_recent_lim.push_back(m_recent_exprs.size()); } 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) { 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); } } z3-z3-4.8.7/src/ast/expr2var.h000066400000000000000000000037201356505360400157310ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: expr2var.h Abstract: The mapping between Z3 expressions and (low level) variables. Example of low level variables: - SAT solver - Polynomial - etc. Author: Leonardo (leonardo) 2011-12-23 Notes: --*/ #ifndef EXPR2VAR_H_ #define EXPR2VAR_H_ #include "ast/ast.h" #include "util/obj_hashtable.h" /** \brief The mapping between Z3 expressions and (low level) variables. */ class expr2var { public: typedef unsigned var; typedef obj_map::key_data key_value; typedef key_value const* iterator; typedef ptr_vector::const_iterator recent_iterator; protected: ast_manager & m_manager; unsigned_vector m_id2map; svector m_mapping; ptr_vector m_recent_exprs; unsigned_vector m_recent_lim; bool m_interpreted_vars; public: expr2var(ast_manager & m); ~expr2var(); ast_manager & m() const { return m_manager; } void insert(expr * n, var v); var to_var(expr * n) const; bool is_var(expr * n) const { return m_id2map.get(n->get_id(), UINT_MAX) != UINT_MAX; } void display(std::ostream & out) const; void mk_inv(expr_ref_vector & var2expr) const; // return true if the mapping contains interpreted vars. bool interpreted_vars() const { return m_interpreted_vars; } iterator begin() const { return m_mapping.begin(); } iterator end() const { return m_mapping.end(); } void reset_recent() { SASSERT(m_recent_lim.empty()); m_recent_exprs.reset(); } // Iterators for traversing the recently registered expressions. // The set of recent registered expressions is reset by using reset_recent(). recent_iterator begin_recent() const { return m_recent_exprs.begin(); } recent_iterator end_recent() const { return m_recent_exprs.end(); } void reset(); void push(); void pop(unsigned num_scopes); }; #endif z3-z3-4.8.7/src/ast/expr_abstract.cpp000066400000000000000000000104461356505360400173570ustar00rootroot00000000000000/*++ Copyright (c) 2007 Microsoft Corporation Module Name: expr_abstract.h Abstract: Abstract occurrences of constants to bound variables. Author: Nikolaj Bjorner (nbjorner) 2008-03-08 Notes: --*/ #include "ast/expr_abstract.h" #include "util/map.h" #include "ast/ast_pp.h" void expr_abstractor::operator()(unsigned base, unsigned num_bound, expr* const* bound, expr* n, expr_ref& result) { if (num_bound == 0) { result = n; return; } expr * curr = nullptr, *b = nullptr; SASSERT(n->get_ref_count() > 0); m_stack.push_back(n); for (unsigned i = 0; i < num_bound; ++i) { b = bound[i]; expr* v = m.mk_var(base + num_bound - i - 1, m.get_sort(b)); m_pinned.push_back(v); m_map.insert(b, v); } while(!m_stack.empty()) { curr = m_stack.back(); if (m_map.contains(curr)) { m_stack.pop_back(); continue; } switch(curr->get_kind()) { case AST_VAR: { m_map.insert(curr, curr); m_stack.pop_back(); break; } case AST_APP: { app* a = to_app(curr); bool all_visited = true; bool changed = false; m_args.reset(); for (unsigned i = 0; i < a->get_num_args(); ++i) { if (!m_map.find(a->get_arg(i), b)) { m_stack.push_back(a->get_arg(i)); all_visited = false; } else { changed |= b != a->get_arg(i); m_args.push_back(b); } } if (all_visited) { if (changed) { b = m.mk_app(a->get_decl(), m_args.size(), m_args.c_ptr()); m_pinned.push_back(b); } else { b = curr; } m_map.insert(curr, b); m_stack.pop_back(); } break; } case AST_QUANTIFIER: { quantifier* q = to_quantifier(curr); expr_ref_buffer patterns(m); expr_ref result1(m); unsigned new_base = base + q->get_num_decls(); for (unsigned i = 0; i < q->get_num_patterns(); ++i) { expr_abstract(m, new_base, num_bound, bound, q->get_pattern(i), result1); patterns.push_back(result1.get()); } expr_abstract(m, new_base, num_bound, bound, q->get_expr(), result1); b = m.update_quantifier(q, patterns.size(), patterns.c_ptr(), result1.get()); m_pinned.push_back(b); m_map.insert(curr, b); m_stack.pop_back(); break; } default: UNREACHABLE(); } } VERIFY (m_map.find(n, b)); result = b; m_pinned.reset(); m_map.reset(); m_stack.reset(); m_args.reset(); } void expr_abstract(ast_manager& m, unsigned base, unsigned num_bound, expr* const* bound, expr* n, expr_ref& result) { expr_abstractor abs(m); abs(base, num_bound, bound, n, result); TRACE("expr_abstract", tout << expr_ref(n, m) << "\n"; tout << result << "\n";); } expr_ref mk_quantifier(quantifier_kind k, ast_manager& m, unsigned num_bound, app* const* bound, expr* n) { expr_ref result(m); expr_abstract(m, 0, num_bound, (expr* const*)bound, n, result); if (num_bound > 0) { ptr_vector sorts; svector names; for (unsigned i = 0; i < num_bound; ++i) { sorts.push_back(m.get_sort(bound[i])); names.push_back(bound[i]->get_decl()->get_name()); } result = m.mk_quantifier(k, num_bound, sorts.c_ptr(), names.c_ptr(), result); } TRACE("expr_abstract", tout << expr_ref(n, m) << "\n"; for (unsigned i = 0; i < num_bound; ++i) tout << expr_ref(bound[i], m) << " "; tout << "\n"; tout << result << "\n";); return result; } expr_ref mk_forall(ast_manager& m, unsigned num_bound, app* const* bound, expr* n) { return mk_quantifier(forall_k, m, num_bound, bound, n); } expr_ref mk_exists(ast_manager& m, unsigned num_bound, app* const* bound, expr* n) { return mk_quantifier(exists_k, m, num_bound, bound, n); } z3-z3-4.8.7/src/ast/expr_abstract.h000066400000000000000000000016151356505360400170220ustar00rootroot00000000000000/*++ Copyright (c) 2007 Microsoft Corporation Module Name: expr_abstract.h Abstract: Abstract occurrences of constants to bound variables. Author: Nikolaj Bjorner (nbjorner) 2008-03-08 Notes: --*/ #ifndef EXPR_ABSTRACT_H_ #define EXPR_ABSTRACT_H_ #include "ast/ast.h" class expr_abstractor { ast_manager& m; expr_ref_vector m_pinned; ptr_vector m_stack, m_args; obj_map m_map; public: expr_abstractor(ast_manager& m): m(m), m_pinned(m) {} void operator()(unsigned base, unsigned num_bound, expr* const* bound, expr* n, expr_ref& result); }; void expr_abstract(ast_manager& m, unsigned base, unsigned num_bound, expr* const* bound, expr* n, expr_ref& result); expr_ref mk_forall(ast_manager& m, unsigned num_bound, app* const* bound, expr* n); expr_ref mk_exists(ast_manager& m, unsigned num_bound, app* const* bound, expr* n); #endif z3-z3-4.8.7/src/ast/expr_delta_pair.h000066400000000000000000000014051356505360400173200ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: expr_delta_pair.h Abstract: Author: Leonardo de Moura (leonardo) 2008-01-14. Revision History: --*/ #ifndef EXPR_DELTA_PAIR_H_ #define EXPR_DELTA_PAIR_H_ /** \brief Auxiliary structure used to cache the intermediate results of the variable substitution procedure. */ struct expr_delta_pair { expr * m_node; unsigned m_delta; expr_delta_pair():m_node(nullptr), m_delta(0) {} expr_delta_pair(expr * n, unsigned d):m_node(n), m_delta(d) {} unsigned hash() const { return hash_u_u(m_node->hash(), m_delta); } bool operator==(const expr_delta_pair & e) const { return m_node == e.m_node && m_delta == e.m_delta; } }; #endif /* EXPR_DELTA_PAIR_H_ */ z3-z3-4.8.7/src/ast/expr_functors.cpp000066400000000000000000000064531356505360400174220ustar00rootroot00000000000000/*++ Copyright (c) 2010 Microsoft Corporation Module Name: expr_functors.cpp Abstract: Functors on expressions. Author: Nikolaj Bjorner (nbjorner) 2010-02-19 Revision History: Hoisted from quant_elim. --*/ #include "ast/expr_functors.h" // ---------- // check_pred bool check_pred::operator()(expr* e) { if (!m_visited.is_marked(e)) { m_refs.push_back(e); visit(e); } SASSERT(m_visited.is_marked(e)); return m_pred_holds.is_marked(e); } void check_pred::visit(expr* e) { ptr_vector todo; todo.push_back(e); while (!todo.empty()) { e = todo.back(); if (m_pred(e)) { m_pred_holds.mark(e, true); } if (m_visited.is_marked(e)) { todo.pop_back(); continue; } switch(e->get_kind()) { case AST_APP: { app* a = to_app(e); bool all_visited = true; unsigned num_args = a->get_num_args(); for (unsigned i = 0; i < num_args; ++i) { expr* arg = a->get_arg(i); if (!m_visited.is_marked(arg)) { todo.push_back(arg); all_visited = false; } else if (m_pred_holds.is_marked(arg)) { m_pred_holds.mark(e, true); } } if (all_visited) { m_visited.mark(e, true); todo.pop_back(); } break; } case AST_QUANTIFIER: { quantifier* q = to_quantifier(e); expr* arg = q->get_expr(); if (!m_check_quantifiers) { todo.pop_back(); m_visited.mark(e, true); } else if (m_visited.is_marked(arg)) { todo.pop_back(); if (m_pred_holds.is_marked(arg)) { m_pred_holds.mark(e, true); } m_visited.mark(e, true); } else { todo.push_back(arg); } break; } case AST_VAR: todo.pop_back(); m_visited.mark(e, true); break; default: UNREACHABLE(); break; } } } // ------------ // contains_app bool contains_app::operator()(unsigned size, expr* const* es) { for (unsigned i = 0; i < size; ++i) { if ((*this)(es[i])) { return true; } } return false; } // ----------- // map_proc void map_proc::reconstruct(app* a) { m_args.reset(); bool is_new = false; for (unsigned i = 0; i < a->get_num_args(); ++i) { expr* e1 = a->get_arg(i); expr* e2 = get_expr(e1); m_args.push_back(e2); if (e1 != e2) { is_new = true; } } if (is_new) { expr* b = m.mk_app(a->get_decl(), m_args.size(), m_args.c_ptr()); m_map.insert(a, b, nullptr); } else { m_map.insert(a, a, nullptr); } } void map_proc::visit(quantifier* e) { expr_ref q(m); q = m.update_quantifier(e, get_expr(e->get_expr())); m_map.insert(e, q, nullptr); } expr* map_proc::get_expr(expr* e) { expr* result = nullptr; proof* p = nullptr; m_map.get(e, result, p); return result; } z3-z3-4.8.7/src/ast/expr_functors.h000066400000000000000000000047001356505360400170600ustar00rootroot00000000000000/*++ Copyright (c) 2010 Microsoft Corporation Module Name: expr_functors.h Abstract: Functors on expressions. Author: Nikolaj Bjorner (nbjorner) 2010-02-19 Revision History: Hoisted from quant_elim. --*/ #ifndef EXPR_FUNCTORS_H_ #define EXPR_FUNCTORS_H_ #include "ast/ast.h" #include "ast/expr_map.h" class i_expr_pred { public: virtual bool operator()(expr* e) = 0; virtual ~i_expr_pred() {} }; class i_sort_pred { public: virtual bool operator()(sort* s) = 0; virtual ~i_sort_pred() {} }; /** \brief Memoizing predicate functor on sub-expressions. The class is initialized with a predicate 'p' on expressions. The class is memoizing. */ class check_pred { i_expr_pred& m_pred; ast_mark m_pred_holds; ast_mark m_visited; expr_ref_vector m_refs; bool m_check_quantifiers; public: check_pred(i_expr_pred& p, ast_manager& m, bool check_quantifiers = true) : m_pred(p), m_refs(m), m_check_quantifiers(check_quantifiers) {} bool operator()(expr* e); void reset() { m_pred_holds.reset(); m_visited.reset(); m_refs.reset(); } private: void visit(expr* e); }; /** \brief Determine if expression 'e' or vector of expressions 'v' contains the app x */ class contains_app { class pred : public i_expr_pred { app* m_x; public: pred(app* x) : m_x(x) {} bool operator()(expr* e) override { return m_x == e; } }; app_ref m_x; pred m_pred; check_pred m_check; public: contains_app(ast_manager& m, app* x) : m_x(x, m), m_pred(x), m_check(m_pred, m) {} bool operator()(expr* e) { return m_check(e); } bool operator()(expr_ref_vector const& v) { return (*this)(v.size(), v.c_ptr()); } bool operator()(unsigned size, expr* const* es); app* x() const { return m_x; } }; /** \brief Base class of functor that applies map to expressions. */ class map_proc { protected: ast_manager& m; expr_map m_map; ptr_vector m_args; public: map_proc(ast_manager& m): m(m), m_map(m) {} void reset() { m_map.reset(); } void visit(var* e) { m_map.insert(e, e, nullptr); } void visit(quantifier* e); void reconstruct(app* a); expr* get_expr(expr* e); }; #endif z3-z3-4.8.7/src/ast/expr_map.cpp000066400000000000000000000042121356505360400163230ustar00rootroot00000000000000/*++ Copyright (c) 2007 Microsoft Corporation Module Name: expr_map.cpp Abstract: Mapping from expressions to expressions + proofs. This mapping is used to cache simplification results. For every entry [e1->(e2, p)] we have that p is a proof that (= e1 e2). Author: Leonardo (leonardo) 2008-01-03 Notes: --*/ #include "ast/expr_map.h" #include "util/dec_ref_util.h" expr_map::expr_map(ast_manager & m): m_manager(m), m_store_proofs(m.proofs_enabled()) { } expr_map::expr_map(ast_manager & m, bool store_proofs): m_manager(m), m_store_proofs(store_proofs) { } expr_map::~expr_map() { reset(); } void expr_map::insert(expr * k, expr * d, proof * p) { m_manager.inc_ref(d); obj_map::obj_map_entry * entry = m_expr2expr.find_core(k); if (entry != nullptr) { m_manager.dec_ref(entry->get_data().m_value); entry->get_data().m_value = d; if (m_store_proofs) { m_manager.inc_ref(p); obj_map::obj_map_entry * entry_pr = m_expr2pr.find_core(k); SASSERT(entry_pr != 0); m_manager.dec_ref(entry_pr->get_data().m_value); entry_pr->get_data().m_value = p; } } else { m_manager.inc_ref(k); m_expr2expr.insert(k, d); if (m_store_proofs) { m_manager.inc_ref(p); m_expr2pr.insert(k, p); } } } void expr_map::get(expr * k, expr * & d, proof * & p) const { if (m_expr2expr.find(k, d)) { p = nullptr; if (m_store_proofs) m_expr2pr.find(k, p); } } void expr_map::erase(expr * k) { expr * v; if (m_expr2expr.find(k, v)) { m_expr2expr.erase(k); m_manager.dec_ref(v); if (m_store_proofs) { proof * pr = nullptr; m_expr2pr.find(k, pr); m_expr2pr.erase(k); m_manager.dec_ref(pr); } m_manager.dec_ref(k); } } void expr_map::reset() { dec_ref_values(m_manager, m_expr2pr); dec_ref_key_values(m_manager, m_expr2expr); } void expr_map::flush() { reset(); m_expr2expr.finalize(); m_expr2pr.finalize(); } z3-z3-4.8.7/src/ast/expr_map.h000066400000000000000000000027331356505360400157760ustar00rootroot00000000000000/*++ Copyright (c) 2007 Microsoft Corporation Module Name: expr_map.h Abstract: Mapping from expressions to expressions + proofs. This mapping is used to cache simplification results. For every entry [e1->(e2, p)] we have that p is a proof that (= e1 e2). Author: Leonardo (leonardo) 2008-01-03 Notes: --*/ #ifndef EXPR_MAP_H_ #define EXPR_MAP_H_ #include "ast/ast.h" #include "util/obj_hashtable.h" /** \brief Map from expressions to expressions+proofs. When proof production is disabled, no extra space is used. */ class expr_map { ast_manager & m_manager; bool m_store_proofs; obj_map m_expr2expr; obj_map m_expr2pr; public: typedef obj_map Map; typedef Map::iterator iterator; typedef Map::key key; typedef Map::value value; typedef Map::data data; typedef Map::entry entry; expr_map(ast_manager & m); expr_map(ast_manager & m, bool store_proofs); ~expr_map(); void insert(expr * k, expr * d, proof * p); bool contains(expr * k) const { return m_expr2expr.contains(k); } void get(expr * k, expr * & d, proof * & p) const; void erase(expr * k); void reset(); void flush(); iterator begin () const { return m_expr2expr.begin (); } iterator end () const {return m_expr2expr.end (); } void set_store_proofs(bool f) { if (m_store_proofs != f) flush(); m_store_proofs = f; } }; #endif z3-z3-4.8.7/src/ast/expr_stat.cpp000066400000000000000000000041071356505360400165240ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: expr_stat.cpp Abstract: Expression statistics (symbol count, var count, depth, ...) All functions in these module assume expressions do not contain nested quantifiers. Author: Leonardo de Moura (leonardo) 2008-02-05. Revision History: --*/ #include "ast/for_each_expr.h" #include "ast/expr_stat.h" void get_expr_stat(expr * n, expr_stat & r) { typedef std::pair pair; buffer todo; todo.push_back(pair(n, 0)); while (!todo.empty()) { pair & p = todo.back(); n = p.first; unsigned depth = p.second; unsigned j; todo.pop_back(); r.m_sym_count++; if (depth > r.m_depth) r.m_depth = depth; switch (n->get_kind()) { case AST_APP: j = to_app(n)->get_num_args(); if (j == 0) r.m_const_count++; while (j > 0) { --j; todo.push_back(pair(to_app(n)->get_arg(j), depth + 1)); } break; case AST_VAR: if (to_var(n)->get_idx() > r.m_max_var_idx) r.m_max_var_idx = to_var(n)->get_idx(); r.m_ground = false; break; case AST_QUANTIFIER: todo.push_back(pair(to_quantifier(n)->get_expr(), depth+1)); break; default: UNREACHABLE(); } } } unsigned get_symbol_count(expr * n) { unsigned r = 0; ptr_buffer todo; todo.push_back(n); while (!todo.empty()) { n = todo.back(); unsigned j; todo.pop_back(); r++; switch (n->get_kind()) { case AST_APP: j = to_app(n)->get_num_args(); while (j > 0) { --j; todo.push_back(to_app(n)->get_arg(j)); } break; case AST_QUANTIFIER: todo.push_back(to_quantifier(n)->get_expr()); break; default: break; } } return r; } z3-z3-4.8.7/src/ast/expr_stat.h000066400000000000000000000020111356505360400161610ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: expr_stat.h Abstract: Expression statistics (symbol count, var count, depth, ...) All functions in these module assume expressions do not contain nested quantifiers. Author: Leonardo de Moura (leonardo) 2008-02-05. Revision History: --*/ #ifndef EXPR_STAT_H_ #define EXPR_STAT_H_ class expr; struct expr_stat { unsigned m_sym_count; // symbol count unsigned m_depth; // depth unsigned m_const_count; // constant count unsigned m_max_var_idx; bool m_ground; expr_stat():m_sym_count(0), m_depth(0), m_const_count(0), m_max_var_idx(0), m_ground(true) {} }; /** \brief Collect statistics regarding the given expression. \warning This function traverses the dag as a tree. */ void get_expr_stat(expr * n, expr_stat & r); /** \brief Return the number of symbols in \c n. \warning This function traverses the dag as a tree. */ unsigned get_symbol_count(expr * n); #endif /* EXPR_STAT_H_ */ z3-z3-4.8.7/src/ast/expr_substitution.cpp000066400000000000000000000110351356505360400203230ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: expr_substitution.cpp Abstract: expr -> expr substitution Author: Leonardo (leonardo) 2011-04-29 Notes: --*/ #include "util/ref_util.h" #include "ast/expr_substitution.h" #include "ast/ast_pp.h" typedef obj_map expr2proof; typedef obj_map expr2expr_dependency; void expr_substitution::init() { if (proofs_enabled()) m_subst_pr = alloc(expr2proof); if (unsat_core_enabled()) m_subst_dep = alloc(expr2expr_dependency); } expr_substitution::expr_substitution(ast_manager & m): m_manager(m), m_cores_enabled(false), m_proofs_enabled(m.proofs_enabled()) { init(); } expr_substitution::expr_substitution(ast_manager & m, bool core_enabled): m_manager(m), m_cores_enabled(core_enabled), m_proofs_enabled(m.proofs_enabled()) { init(); } expr_substitution::expr_substitution(ast_manager & m, bool core_enabled, bool proofs_enabled): m_manager(m), m_cores_enabled(core_enabled), m_proofs_enabled(proofs_enabled) { SASSERT(!proofs_enabled || m.proofs_enabled()); init(); } expr_substitution::~expr_substitution() { reset(); } std::ostream& expr_substitution::display(std::ostream& out) { for (auto & kv : m_subst) { out << mk_pp(kv.m_key, m()) << " |-> " << mk_pp(kv.m_value, m()) << "\n"; } return out; } void expr_substitution::insert(expr * c, expr * def, proof * def_pr, expr_dependency * def_dep) { obj_map::obj_map_entry * entry = m_subst.insert_if_not_there2(c, nullptr); if (entry->get_data().m_value == nullptr) { // new entry m_manager.inc_ref(c); m_manager.inc_ref(def); entry->get_data().m_value = def; if (proofs_enabled()) { SASSERT(!m_subst_pr->contains(c)); m_subst_pr->insert(c, def_pr); m_manager.inc_ref(def_pr); } if (unsat_core_enabled()) { SASSERT(!m_subst_dep->contains(c)); m_subst_dep->insert(c, def_dep); m_manager.inc_ref(def_dep); } } else { // replacing entry m_manager.inc_ref(def); m_manager.dec_ref(entry->get_data().m_value); entry->get_data().m_value = def; if (proofs_enabled()) { obj_map::obj_map_entry * entry_pr = m_subst_pr->find_core(c); SASSERT(entry_pr != nullptr); m_manager.inc_ref(def_pr); m_manager.dec_ref(entry_pr->get_data().m_value); entry_pr->get_data().m_value = def_pr; } if (unsat_core_enabled()) { obj_map::obj_map_entry * entry_dep = m_subst_dep->find_core(c); SASSERT(entry_dep != nullptr); m_manager.inc_ref(def_dep); m_manager.dec_ref(entry_dep->get_data().m_value); entry_dep->get_data().m_value = def_dep; } } } void expr_substitution::erase(expr * c) { if (proofs_enabled()) { proof * pr = nullptr; if (m_subst_pr->find(c, pr)) { m_manager.dec_ref(pr); m_subst_pr->erase(c); } } if (unsat_core_enabled()) { expr_dependency * dep = nullptr; if (m_subst_dep->find(c, dep)) { m_manager.dec_ref(dep); m_subst_dep->erase(c); } } expr * def = nullptr; if (m_subst.find(c, def)) { m_manager.dec_ref(def); m_subst.erase(c); m_manager.dec_ref(c); } } bool expr_substitution::find(expr * c, expr * & def, proof * & def_pr) { if (m_subst.find(c, def)) { if (proofs_enabled()) m_subst_pr->find(c, def_pr); return true; } return false; } bool expr_substitution::find(expr * c, expr * & def, proof * & def_pr, expr_dependency * & def_dep) { if (m_subst.find(c, def)) { if (proofs_enabled()) m_subst_pr->find(c, def_pr); if (unsat_core_enabled()) m_subst_dep->find(c, def_dep); return true; } return false; } bool expr_substitution::contains(expr * s) { return m_subst.contains(s); } void expr_substitution::reset() { dec_ref_map_key_values(m_manager, m_subst); if (proofs_enabled()) dec_ref_map_values(m_manager, *m_subst_pr); if (unsat_core_enabled()) dec_ref_map_values(m_manager, *m_subst_dep); } void expr_substitution::cleanup() { reset(); m_subst.finalize(); if (proofs_enabled()) m_subst_pr->finalize(); if (unsat_core_enabled()) m_subst_dep->finalize(); } z3-z3-4.8.7/src/ast/expr_substitution.h000066400000000000000000000062671356505360400200030ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: expr_substitution.h Abstract: expr -> expr substitution Author: Leonardo (leonardo) 2011-04-29 Notes: --*/ #ifndef EXPR_SUBSTITUTION_H_ #define EXPR_SUBSTITUTION_H_ #include "ast/ast.h" class expr_substitution { ast_manager & m_manager; obj_map m_subst; scoped_ptr > m_subst_pr; scoped_ptr > m_subst_dep; unsigned m_cores_enabled:1; unsigned m_proofs_enabled:1; void init(); public: expr_substitution(ast_manager & m); expr_substitution(ast_manager & m, bool cores_enabled); expr_substitution(ast_manager & m, bool cores_enabled, bool proofs_enabled); ~expr_substitution(); ast_manager & m() const { return m_manager; } bool proofs_enabled() const { return m_proofs_enabled; } bool unsat_core_enabled() const { return m_cores_enabled; } bool empty() const { return m_subst.empty(); } unsigned size() const { return m_subst.size(); } void insert(expr * s, expr * def, proof * def_pr = nullptr, expr_dependency * def_dep = nullptr); void erase(expr * s); bool find(expr * s, expr * & def, proof * & def_pr); bool find(expr * s, expr * & def, proof * & def_pr, expr_dependency * & def_dep); bool contains(expr * s); void reset(); void cleanup(); std::ostream& display(std::ostream& out); }; class scoped_expr_substitution { expr_substitution& m_subst; expr_ref_vector m_trail; unsigned_vector m_trail_lim; public: scoped_expr_substitution(expr_substitution& s): m_subst(s), m_trail(s.m()) {} ~scoped_expr_substitution() {} void insert(expr * s, expr * def, proof * def_pr = nullptr, expr_dependency * def_dep = nullptr) { if (!m_subst.contains(s)) { m_subst.insert(s, def, def_pr, def_dep); m_trail.push_back(s); } } void reset() { m_subst.reset(); m_trail.reset(); m_trail_lim.reset(); } void push() { m_trail_lim.push_back(m_trail.size()); } void pop(unsigned n) { if (n > 0) { unsigned new_sz = m_trail_lim.size() - n; unsigned old_sz = m_trail_lim[new_sz]; for (unsigned i = old_sz; i < m_trail.size(); ++i) m_subst.erase(m_trail[i].get()); m_trail.resize(old_sz); m_trail_lim.resize(new_sz); } } unsigned scope_level() const { return m_trail_lim.size(); } bool empty() const { return m_subst.empty(); } unsigned size() const { return m_subst.size(); } expr* find(expr * e) { proof* pr; expr* d = nullptr; if (find(e, d, pr)) return d; else return e; } bool find(expr * s, expr * & def, proof * & def_pr) { return m_subst.find(s, def, def_pr); } bool find(expr * s, expr * & def, proof * & def_pr, expr_dependency * & def_dep) { return m_subst.find(s, def, def_pr, def_dep); } bool contains(expr * s) { return m_subst.contains(s); } void cleanup() { m_subst.cleanup(); } std::ostream& display(std::ostream& out) { return m_subst.display(out); } }; #endif z3-z3-4.8.7/src/ast/for_each_ast.cpp000066400000000000000000000016021356505360400171250ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: for_each_ast.cpp Abstract: For each ast visitor Author: Leonardo de Moura (leonardo) 2006-10-18. Revision History: --*/ #include "ast/for_each_ast.h" struct ast_counter_proc { unsigned m_num; ast_counter_proc():m_num(0) {} void operator()(ast *) { m_num++; } }; unsigned get_num_nodes(ast * n) { for_each_ast_proc counter; for_each_ast(counter, n); return counter.m_num; } bool for_each_parameter(ptr_vector & stack, ast_mark & visited, unsigned num_args, parameter const * params) { bool result = true; for (unsigned i = 0; i < num_args; i++) { parameter const& p = params[i]; if (p.is_ast() && !visited.is_marked(p.get_ast())) { stack.push_back(p.get_ast()); result = false; } } return result; } z3-z3-4.8.7/src/ast/for_each_ast.h000066400000000000000000000215151356505360400165770ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: for_each_ast.h Abstract: Visitor for AST nodes Author: Leonardo de Moura (leonardo) 2006-10-18. Revision History: --*/ #ifndef FOR_EACH_AST_H_ #define FOR_EACH_AST_H_ #include "ast/ast.h" #include "util/trace.h" #include "util/map.h" template bool for_each_ast_args(ptr_vector & stack, ast_mark & visited, unsigned num_args, T * const * args) { bool result = true; for (unsigned i = 0; i < num_args; i++) { T * arg = args[i]; if (!visited.is_marked(arg)) { stack.push_back(arg); result = false; } } return result; } bool for_each_parameter(ptr_vector & stack, ast_mark & visited, unsigned num_args, parameter const * params); template void for_each_ast(ForEachProc & proc, ast_mark & visited, ast * n, bool visit_parameters = false) { ptr_vector stack; ast * curr; stack.push_back(n); while (!stack.empty()) { curr = stack.back(); TRACE("for_each_ast", tout << "visiting node: " << curr->get_id() << ", kind: " << get_ast_kind_name(curr->get_kind()) << ", stack size: " << stack.size() << "\n";); if (visited.is_marked(curr)) { stack.pop_back(); continue; } switch(curr->get_kind()) { case AST_SORT: if (visit_parameters && !for_each_parameter(stack, visited, to_sort(curr)->get_num_parameters(), to_sort(curr)->get_parameters())) { break; } proc(to_sort(curr)); visited.mark(curr, true); stack.pop_back(); break; case AST_VAR: { var* v = to_var(curr); proc(v); visited.mark(curr, true); stack.pop_back(); break; } case AST_FUNC_DECL: if (visit_parameters && !for_each_parameter(stack, visited, to_func_decl(curr)->get_num_parameters(), to_func_decl(curr)->get_parameters())) { break; } if (!for_each_ast_args(stack, visited, to_func_decl(curr)->get_arity(), to_func_decl(curr)->get_domain())) { break; } if (!visited.is_marked(to_func_decl(curr)->get_range())) { stack.push_back(to_func_decl(curr)->get_range()); break; } proc(to_func_decl(curr)); visited.mark(curr, true); stack.pop_back(); break; case AST_APP: if (!visited.is_marked(to_app(curr)->get_decl())) { stack.push_back(to_app(curr)->get_decl()); break; } if (for_each_ast_args(stack, visited, to_app(curr)->get_num_args(), to_app(curr)->get_args())) { proc(to_app(curr)); visited.mark(curr, true); stack.pop_back(); } break; case AST_QUANTIFIER: if (!for_each_ast_args(stack, visited, to_quantifier(curr)->get_num_patterns(), to_quantifier(curr)->get_patterns())) { break; } if (!for_each_ast_args(stack, visited, to_quantifier(curr)->get_num_no_patterns(), to_quantifier(curr)->get_no_patterns())) { break; } if (!visited.is_marked(to_quantifier(curr)->get_expr())) { stack.push_back(to_quantifier(curr)->get_expr()); break; } proc(to_quantifier(curr)); visited.mark(curr, true); stack.pop_back(); break; } } } template void for_each_ast(ForEachProc & proc, ast * n, bool visit_parameters = false) { ast_mark visited; for_each_ast(proc, visited, n, visit_parameters); } template struct for_each_ast_proc : public EscapeProc { void operator()(ast * n) { EscapeProc::operator()(n); } void operator()(sort * n) { operator()(static_cast(n)); } void operator()(func_decl * n) { operator()(static_cast(n)); } void operator()(var * n) { operator()(static_cast(n)); } void operator()(app * n) { operator()(static_cast(n)); } void operator()(quantifier * n) { operator()(static_cast(n)); } }; unsigned get_num_nodes(ast * n); template class recurse_ast { template class mem_map : public map, ptr_eq > {}; public: static T* recurse(Visitor & visit, ast * aArg) { unsigned arity; ast* a; ast * const * args; T* result; ptr_vector stack; mem_map memoize; ptr_vector results; stack.push_back(aArg); while (!stack.empty()) { a = stack.back(); results.reset(); if (memoize.find(a, result)) { stack.pop_back(); continue; } switch(a->get_kind()) { case AST_SORT: memoize.insert(a, visit.mk_sort(to_sort(a))); stack.pop_back(); break; case AST_FUNC_DECL: { arity = to_func_decl(a)->get_arity(); func_decl * func_decl_ast = to_func_decl(a); args = (ast * const *)(func_decl_ast->get_domain()); recurse_list(stack, arity, args, &memoize, results); if (!memoize.find(func_decl_ast->get_range(), result)) { stack.push_back(func_decl_ast->get_range()); } else if (results.size() == arity) { result = visit.mk_func_decl(func_decl_ast, result, results); memoize.insert(a, result); stack.pop_back(); } break; } case AST_APP: { app * app = to_app(a); arity = app->get_num_args(); args = (ast * const *)(app->get_args()); recurse_list(stack, arity, args, &memoize, results); if (arity == results.size()) { result = visit.mk_app(app, results); memoize.insert(a, result); stack.pop_back(); } break; } case AST_VAR: memoize.insert(a, visit.mk_var(to_var(a))); stack.pop_back(); break; case AST_QUANTIFIER: { quantifier * quantifier_ast = to_quantifier(a); ptr_vector decl_types; if (recurse_quantifier) { args = (ast * const *) quantifier_ast->get_decl_sorts(); arity = quantifier_ast->get_num_decls(); ast* body = quantifier_ast->get_expr(); recurse_list(stack, arity, args, &memoize, decl_types); if (!memoize.find(body, result)) { stack.push_back(body); } else if (decl_types.size() == arity) { result = visit.mk_quantifier(quantifier_ast, decl_types, result); memoize.insert(a, result); stack.pop_back(); } } else { result = visit.mk_quantifier(quantifier_ast, decl_types, result); memoize.insert(a, result); stack.pop_back(); } break; } default: UNREACHABLE(); break; } } if (!memoize.find(aArg, result)) { UNREACHABLE(); } return result; } private: template static void recurse_list(ptr_vector & stack, unsigned arity, AST * const * ast_list, mem_map * memoize, ptr_vector & results) { T * result; for (unsigned i = 0; i < arity; ++i) { if (memoize->find(ast_list[i], result)) { results.push_back(result); } else { stack.push_back(ast_list[i]); } } } }; #endif /* FOR_EACH_AST_H_ */ z3-z3-4.8.7/src/ast/for_each_expr.cpp000066400000000000000000000110431356505360400173140ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: for_each_expr.cpp Abstract: Author: Leonardo de Moura (leonardo) 2007-12-28. Revision History: --*/ #include "ast/for_each_expr.h" struct expr_counter_proc { unsigned m_num; expr_counter_proc():m_num(0) {} void operator()(var * n) { m_num++; } void operator()(app * n) { m_num++; if (n->get_decl()->is_associative()) m_num += n->get_num_args() - 2; } void operator()(quantifier * n) { m_num++; } }; unsigned get_num_exprs(expr * n, expr_mark & visited) { expr_counter_proc counter; for_each_expr(counter, visited, n); return counter.m_num; } unsigned get_num_exprs(expr * n, expr_fast_mark1 & visited) { expr_counter_proc counter; for_each_expr_core(counter, visited, n); return counter.m_num; } unsigned get_num_exprs(expr * n) { expr_fast_mark1 visited; return get_num_exprs(n, visited); } namespace has_skolem_functions_ns { struct found {}; struct proc { void operator()(var * n) const {} void operator()(app const * n) const { if (n->get_decl()->is_skolem() && n->get_num_args() > 0) throw found(); } void operator()(quantifier * n) const {} }; }; bool has_skolem_functions(expr * n) { has_skolem_functions_ns::proc p; try { for_each_expr(p, n); } catch (const has_skolem_functions_ns::found &) { return true; } return false; } subterms::subterms(expr_ref_vector const& es): m_es(es) {} subterms::subterms(expr_ref& e) : m_es(e.m()) { m_es.push_back(e); } subterms::iterator subterms::begin() { return iterator(*this, true); } subterms::iterator subterms::end() { return iterator(*this, false); } subterms::iterator::iterator(subterms& f, bool start): m_es(f.m_es) { if (!start) m_es.reset(); } expr* subterms::iterator::operator*() { return m_es.back(); } subterms::iterator subterms::iterator::operator++(int) { iterator tmp = *this; ++*this; return tmp; } subterms::iterator& subterms::iterator::operator++() { expr* e = m_es.back(); m_visited.mark(e, true); if (is_app(e)) { for (expr* arg : *to_app(e)) { m_es.push_back(arg); } } while (!m_es.empty() && m_visited.is_marked(m_es.back())) { m_es.pop_back(); } return *this; } bool subterms::iterator::operator==(iterator const& other) const { // ignore state of visited if (other.m_es.size() != m_es.size()) { return false; } for (unsigned i = m_es.size(); i-- > 0; ) { if (m_es.get(i) != other.m_es.get(i)) return false; } return true; } bool subterms::iterator::operator!=(iterator const& other) const { return !(*this == other); } subterms_postorder::subterms_postorder(expr_ref_vector const& es): m_es(es) {} subterms_postorder::subterms_postorder(expr_ref& e) : m_es(e.m()) { m_es.push_back(e); } subterms_postorder::iterator subterms_postorder::begin() { return iterator(*this, true); } subterms_postorder::iterator subterms_postorder::end() { return iterator(*this, false); } subterms_postorder::iterator::iterator(subterms_postorder& f, bool start): m_es(f.m_es) { if (!start) m_es.reset(); next(); } expr* subterms_postorder::iterator::operator*() { return m_es.back(); } subterms_postorder::iterator subterms_postorder::iterator::operator++(int) { iterator tmp = *this; ++*this; return tmp; } void subterms_postorder::iterator::next() { while (!m_es.empty()) { expr* e = m_es.back(); if (m_visited.is_marked(e)) { m_es.pop_back(); continue; } bool all_visited = true; if (is_app(e)) { for (expr* arg : *to_app(e)) { if (!m_visited.is_marked(arg)) { m_es.push_back(arg); all_visited = false; } } } if (all_visited) { m_visited.mark(e, true); break; } } } subterms_postorder::iterator& subterms_postorder::iterator::operator++() { next(); return *this; } bool subterms_postorder::iterator::operator==(iterator const& other) const { // ignore state of visited if (other.m_es.size() != m_es.size()) { return false; } for (unsigned i = m_es.size(); i-- > 0; ) { if (m_es.get(i) != other.m_es.get(i)) return false; } return true; } bool subterms_postorder::iterator::operator!=(iterator const& other) const { return !(*this == other); } z3-z3-4.8.7/src/ast/for_each_expr.h000066400000000000000000000142661356505360400167730ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: for_each_expr.h Abstract: Author: Leonardo de Moura (leonardo) 2007-12-28. Revision History: --*/ #ifndef FOR_EACH_EXPR_H_ #define FOR_EACH_EXPR_H_ #include "ast/ast.h" #include "util/trace.h" template void for_each_expr_core(ForEachProc & proc, ExprMark & visited, expr * n) { typedef std::pair frame; if (MarkAll || n->get_ref_count() > 1) { if (visited.is_marked(n)) return; visited.mark(n); } sbuffer stack; stack.push_back(frame(n, 0)); while (!stack.empty()) { start: frame & fr = stack.back(); expr * curr = fr.first; switch (curr->get_kind()) { case AST_VAR: proc(to_var(curr)); stack.pop_back(); break; case AST_APP: { unsigned num_args = to_app(curr)->get_num_args(); while (fr.second < num_args) { expr * arg = to_app(curr)->get_arg(fr.second); fr.second++; if (MarkAll || arg->get_ref_count() > 1) { if (visited.is_marked(arg)) continue; visited.mark(arg); } switch (arg->get_kind()) { case AST_VAR: proc(to_var(arg)); break; case AST_QUANTIFIER: stack.push_back(frame(arg, 0)); goto start; case AST_APP: if (to_app(arg)->get_num_args() == 0) { proc(to_app(arg)); } else { stack.push_back(frame(arg, 0)); goto start; } break; default: UNREACHABLE(); break; } } stack.pop_back(); proc(to_app(curr)); break; } case AST_QUANTIFIER: { quantifier * q = to_quantifier(curr); unsigned num_children = IgnorePatterns ? 1 : q->get_num_children(); while (fr.second < num_children) { expr * child = q->get_child(fr.second); fr.second++; if (MarkAll || child->get_ref_count() > 1) { if (visited.is_marked(child)) continue; visited.mark(child); } stack.push_back(frame(child, 0)); goto start; } stack.pop_back(); proc(to_quantifier(curr)); break; } default: UNREACHABLE(); break; } } } template bool for_each_expr_args(ptr_vector & stack, expr_mark & visited, unsigned num_args, T * const * args) { bool result = true; for (unsigned i = 0; i < num_args; i++) { T * arg = args[i]; if (!visited.is_marked(arg)) { stack.push_back(arg); result = false; } } return result; } template void for_each_expr(ForEachProc & proc, expr_mark & visited, expr * n) { for_each_expr_core(proc, visited, n); } template void for_each_expr(ForEachProc & proc, expr * n) { expr_mark visited; for_each_expr_core(proc, visited, n); } template void for_each_expr(ForEachProc & proc, unsigned n, expr * const* es) { expr_mark visited; for (unsigned i = 0; i < n; ++i) for_each_expr_core(proc, visited, es[i]); } template void for_each_expr(ForEachProc & proc, expr_ref_vector const& es) { expr_mark visited; for (expr* e : es) for_each_expr_core(proc, visited, e); } template void quick_for_each_expr(ForEachProc & proc, expr_fast_mark1 & visited, expr * n) { for_each_expr_core(proc, visited, n); } template void quick_for_each_expr(ForEachProc & proc, expr * n) { expr_fast_mark1 visited; for_each_expr_core(proc, visited, n); } template struct for_each_expr_proc : public EscapeProc { void operator()(expr * n) { EscapeProc::operator()(n); } void operator()(var * n) { operator()(static_cast(n)); } void operator()(app * n) { operator()(static_cast(n)); } void operator()(quantifier * n) { operator()(static_cast(n)); } }; unsigned get_num_exprs(expr * n); unsigned get_num_exprs(expr * n, expr_mark & visited); unsigned get_num_exprs(expr * n, expr_fast_mark1 & visited); bool has_skolem_functions(expr * n); // pre-order traversal of subterms class subterms { expr_ref_vector m_es; public: class iterator { expr_ref_vector m_es; expr_mark m_visited; public: iterator(subterms& f, bool start); expr* operator*(); iterator operator++(int); iterator& operator++(); bool operator==(iterator const& other) const; bool operator!=(iterator const& other) const; }; subterms(expr_ref_vector const& es); subterms(expr_ref& e); iterator begin(); iterator end(); }; class subterms_postorder { expr_ref_vector m_es; public: class iterator { expr_ref_vector m_es; expr_mark m_visited, m_seen; void next(); public: iterator(subterms_postorder& f, bool start); expr* operator*(); iterator operator++(int); iterator& operator++(); bool operator==(iterator const& other) const; bool operator!=(iterator const& other) const; }; subterms_postorder(expr_ref_vector const& es); subterms_postorder(expr_ref& e); iterator begin(); iterator end(); }; #endif /* FOR_EACH_EXPR_H_ */ z3-z3-4.8.7/src/ast/format.cpp000066400000000000000000000155351356505360400160120ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: format.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-01-20. Revision History: --*/ #include "ast/format.h" #include "ast/recurse_expr_def.h" namespace format_ns { class format_decl_plugin : public decl_plugin { protected: sort * m_format_sort; symbol m_nil; symbol m_string; symbol m_indent; symbol m_compose; symbol m_choice; symbol m_line_break; symbol m_line_break_ext; void set_manager(ast_manager * m, family_id id) override { SASSERT(m->is_format_manager()); decl_plugin::set_manager(m, id); m_format_sort = m->mk_sort(symbol("format"), sort_info(id, FORMAT_SORT)); m->inc_ref(m_format_sort); } public: format_decl_plugin(): m_format_sort(nullptr), m_nil("nil"), m_string("string"), m_indent("indent"), m_compose("compose"), m_choice("choice"), m_line_break("cr"), m_line_break_ext("cr++") { } ~format_decl_plugin() override {} void finalize() override { if (m_format_sort) m_manager->dec_ref(m_format_sort); } decl_plugin * mk_fresh() override { return alloc(format_decl_plugin); } sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const* parameters) override { SASSERT(k == FORMAT_SORT); return m_format_sort; } func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) override { switch (k) { case OP_NIL: return m_manager->mk_func_decl(m_nil, arity, domain, m_format_sort, func_decl_info(m_family_id, OP_NIL)); case OP_STRING: return m_manager->mk_func_decl(m_string, arity, domain, m_format_sort, func_decl_info(m_family_id, OP_STRING, num_parameters, parameters)); case OP_INDENT: return m_manager->mk_func_decl(m_indent, arity, domain, m_format_sort, func_decl_info(m_family_id, OP_INDENT, num_parameters, parameters)); case OP_COMPOSE: return m_manager->mk_func_decl(m_compose, arity, domain, m_format_sort, func_decl_info(m_family_id, OP_COMPOSE)); case OP_CHOICE: return m_manager->mk_func_decl(m_choice, arity, domain, m_format_sort, func_decl_info(m_family_id, OP_CHOICE)); case OP_LINE_BREAK: return m_manager->mk_func_decl(m_line_break, arity, domain, m_format_sort, func_decl_info(m_family_id, OP_LINE_BREAK)); case OP_LINE_BREAK_EXT: return m_manager->mk_func_decl(m_line_break_ext, arity, domain, m_format_sort, func_decl_info(m_family_id, OP_LINE_BREAK_EXT, num_parameters, parameters)); default: return nullptr; } } }; family_id get_format_family_id(ast_manager & m) { symbol f("format"); if (!fm(m).has_plugin(f)) fm(m).register_plugin(f, alloc(format_decl_plugin)); return fm(m).mk_family_id(f); } static family_id fid(ast_manager & m) { return get_format_family_id(m); } sort * fsort(ast_manager & m) { return fm(m).mk_sort(fid(m), FORMAT_SORT); } struct flat_visitor { ast_manager & m_manager; family_id m_fid; flat_visitor(ast_manager & m): m_manager(fm(m)), m_fid(fid(m)) { SASSERT(m_manager.is_format_manager()); } format * visit(var *) { UNREACHABLE(); return nullptr; } format * visit(quantifier * q, format *, format * const *, format * const *) { UNREACHABLE(); return nullptr; } format * visit(format * n, format * const * children) { if (is_app_of(n, m_fid, OP_LINE_BREAK)) return mk_string(m_manager, " "); else if (is_app_of(n, m_fid, OP_LINE_BREAK_EXT)) return mk_string(m_manager, n->get_decl()->get_parameter(0).get_symbol().bare_str()); else if (is_app_of(n, m_fid, OP_CHOICE)) return to_app(n->get_arg(0)); else return m_manager.mk_app(n->get_decl(), n->get_num_args(), (expr *const*) children); } }; format * flat(ast_manager & m, format * f) { flat_visitor v(m); recurse_expr r(v); return r(f); } format * mk_string(ast_manager & m, char const * str) { symbol s(str); parameter p(s); return fm(m).mk_app(fid(m), OP_STRING, 1, &p, 0, nullptr); } format * mk_int(ast_manager & m, int i) { char buffer[128]; SPRINTF_D(buffer, i); return mk_string(m, buffer); } format * mk_unsigned(ast_manager & m, unsigned u) { char buffer[128]; SPRINTF_U(buffer, u); return mk_string(m, buffer); } format * mk_indent(ast_manager & m, unsigned i, format * f) { parameter p(i); expr * e = static_cast(f); return fm(m).mk_app(fid(m), OP_INDENT, 1, &p, 1, &e); } format * mk_line_break(ast_manager & m) { return fm(m).mk_app(fid(m), OP_LINE_BREAK); } format * mk_choice(ast_manager & m, format * f1, format * f2) { return fm(m).mk_app(fid(m), OP_CHOICE, f1, f2); } format * mk_group(ast_manager & m, format * f) { return mk_choice(m, flat(m, f), f); } format * mk_compose(ast_manager & m, unsigned num_children, format * const * children) { return fm(m).mk_app(fid(m), OP_COMPOSE, num_children, (expr * const *) children); } format * mk_compose(ast_manager & m, format * f1, format * f2) { return fm(m).mk_app(fid(m), OP_COMPOSE, f1, f2); } format * mk_compose(ast_manager & m, format * f1, format * f2, format * f3) { return fm(m).mk_app(fid(m), OP_COMPOSE, f1, f2, f3); } format * mk_compose(ast_manager & m, format * f1, format * f2, format * f3, format * f4) { expr * f[4] = { f1, f2, f3, f4 }; return fm(m).mk_app(fid(m), OP_COMPOSE, 4, f); } format * mk_nil(ast_manager & m) { return fm(m).mk_app(fid(m), OP_NIL); } }; z3-z3-4.8.7/src/ast/format.h000066400000000000000000000154461356505360400154600ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: format.h Abstract: Author: Leonardo de Moura (leonardo) 2008-01-20. Revision History: --*/ #ifndef FORMAT_H_ #define FORMAT_H_ #include "ast/ast.h" namespace format_ns { typedef app format; typedef app_ref format_ref; enum format_sort_kind { FORMAT_SORT }; enum format_op_kind { OP_NIL, OP_STRING, OP_INDENT, OP_COMPOSE, OP_CHOICE, OP_LINE_BREAK, OP_LINE_BREAK_EXT }; struct f2f { format * operator()(format * f) { return f; } }; /** \brief Return the "format manager" associated with the given ast_manager. */ inline ast_manager & fm(ast_manager & m) { return m.get_format_manager(); } family_id get_format_family_id(ast_manager & m); format * mk_string(ast_manager & m, char const * str); format * mk_int(ast_manager & m, int i); format * mk_unsigned(ast_manager & m, unsigned u); format * mk_indent(ast_manager & m, unsigned i, format * f); format * mk_line_break(ast_manager & m); format * mk_group(ast_manager & m, format * f); format * mk_compose(ast_manager & m, unsigned num_children, format * const * children); format * mk_compose(ast_manager & m, format * f1, format * f2); format * mk_compose(ast_manager & m, format * f1, format * f2, format * f3); format * mk_compose(ast_manager & m, format * f1, format * f2, format * f3, format * f4); format * mk_nil(ast_manager & m); format * mk_choice(ast_manager & m, format * f1, format * f2); template format * mk_seq(ast_manager & m, It const & begin, It const & end, ToDoc proc) { app_ref_buffer children(fm(m)); for (It it = begin; it != end; ++it) { format * curr = proc(*it); if (curr->get_decl_kind() != OP_NIL) { children.push_back(mk_line_break(m)); children.push_back(curr); } } return mk_compose(m, children.size(), children.c_ptr()); } /** (header elem_1 elem_2 ... elem_n) */ template format * mk_seq1(ast_manager & m, It const & begin, It const & end, ToDoc proc, char const * header, char const * lp = "(", char const * rp = ")") { if (begin == end) return mk_compose(m, mk_string(m, lp), mk_string(m, header), mk_string(m, rp)); unsigned indent = static_cast(strlen(lp) + strlen(header) + 1); It it = begin; format * first = proc(*it); ++it; return mk_group(m, mk_compose(m, mk_string(m, lp), mk_string(m, header), mk_indent(m, indent, mk_compose(m, mk_string(m, " "), first, mk_seq(m, it, end, proc), mk_string(m, rp))))); } #define FORMAT_DEFAULT_INDENT 2 /** (header elem_1 ... elem_n) */ template format * mk_seq2(ast_manager & m, It const & begin, It const & end, ToDoc proc, char const * header, unsigned indent = FORMAT_DEFAULT_INDENT, char const * lp = "(", char const * rp = ")") { if (begin == end) return mk_compose(m, mk_string(m, lp), mk_string(m, header), mk_string(m, rp)); return mk_group(m, mk_compose(m, mk_indent(m, static_cast(strlen(lp)), mk_compose(m, mk_string(m, lp), mk_string(m, header))), mk_indent(m, indent, mk_compose(m, mk_seq(m, begin, end, proc), mk_string(m, rp))))); } /** (header elem_1 ... elem_i elem_{i+1} ... elem_n) */ template format * mk_seq3(ast_manager & m, It const & begin, It const & end, ToDoc proc, char const * header, unsigned i = 1, unsigned indent = FORMAT_DEFAULT_INDENT, char const * lp = "(", char const * rp = ")") { SASSERT(i >= 1); if (begin == end) return mk_compose(m, mk_string(m, lp), mk_string(m, header), mk_string(m, rp)); unsigned idx = 0; It end1 = begin; for (;end1 != end && idx < i; ++end1, ++idx) ; It it = begin; format * first = proc(*it); ++it; return mk_group(m, mk_compose(m, mk_compose(m, mk_string(m, lp), mk_string(m, header)), mk_group(m, mk_indent(m, static_cast(strlen(header) + strlen(lp) + 1), mk_compose(m, mk_string(m, " "), first, mk_seq(m, it, end1, proc)))), mk_indent(m, indent, mk_seq(m, end1, end, proc)), mk_string(m, rp))); } /** (elem_1 elem_2 ... elem_n) */ template format * mk_seq4(ast_manager & m, It const & begin, It const & end, ToDoc proc, unsigned indent = FORMAT_DEFAULT_INDENT, char const * lp = "(", char const * rp = ")") { if (begin == end) return mk_compose(m, mk_string(m, lp), mk_string(m, rp)); unsigned indent1 = static_cast(strlen(lp)); It it = begin; format * first = proc(*it); ++it; return mk_group(m, mk_compose(m, mk_indent(m, indent1, mk_compose(m, mk_string(m, lp), first)), mk_indent(m, indent, mk_compose(m, mk_seq(m, it, end, proc), mk_string(m, rp))))); } /** (elem_1 elem_2 ... elem_n) */ template format * mk_seq5(ast_manager & m, It const & begin, It const & end, ToDoc proc, char const * lp = "(", char const * rp = ")") { return mk_seq4(m, begin, end, proc, static_cast(strlen(lp)), lp, rp); } }; #endif /* FORMAT_H_ */ z3-z3-4.8.7/src/ast/fpa/000077500000000000000000000000001356505360400145535ustar00rootroot00000000000000z3-z3-4.8.7/src/ast/fpa/CMakeLists.txt000066400000000000000000000003271356505360400173150ustar00rootroot00000000000000z3_add_component(fpa SOURCES bv2fpa_converter.cpp fpa2bv_converter.cpp fpa2bv_rewriter.cpp COMPONENT_DEPENDENCIES ast rewriter model util PYG_FILES fpa2bv_rewriter_params.pyg ) z3-z3-4.8.7/src/ast/fpa/bv2fpa_converter.cpp000066400000000000000000000466731356505360400205460ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: bv2fpa_converter.cpp Abstract: Model conversion for fpa2bv_converter Author: Christoph (cwinter) 2016-10-15 Notes: --*/ #include #include "ast/ast_smt2_pp.h" #include "ast/well_sorted.h" #include "ast/rewriter/th_rewriter.h" #include "ast/rewriter/fpa_rewriter.h" #include "ast/fpa/bv2fpa_converter.h" bv2fpa_converter::bv2fpa_converter(ast_manager & m) : m(m), m_fpa_util(m), m_bv_util(m), m_th_rw(m) { } bv2fpa_converter::bv2fpa_converter(ast_manager & m, fpa2bv_converter & conv) : m(m), m_fpa_util(m), m_bv_util(m), m_th_rw(m) { for (auto const& kv : conv.m_const2bv) { m_const2bv.insert(kv.m_key, kv.m_value); m.inc_ref(kv.m_key); m.inc_ref(kv.m_value); } for (auto const& kv : conv.m_rm_const2bv) { m_rm_const2bv.insert(kv.m_key, kv.m_value); m.inc_ref(kv.m_key); m.inc_ref(kv.m_value); } for (auto const& kv : conv.m_uf2bvuf) { m_uf2bvuf.insert(kv.m_key, kv.m_value); m.inc_ref(kv.m_key); m.inc_ref(kv.m_value); } for (auto const& kv : conv.m_min_max_ufs) { m_min_max_specials.insert(kv.m_key, kv.m_value); m.inc_ref(kv.m_key); m.inc_ref(kv.m_value.first); m.inc_ref(kv.m_value.second); } } bv2fpa_converter::~bv2fpa_converter() { dec_ref_map_key_values(m, m_const2bv); dec_ref_map_key_values(m, m_rm_const2bv); for (auto const& kv : m_uf2bvuf) { m.dec_ref(kv.m_key); m.dec_ref(kv.m_value); } for (auto const& kv : m_min_max_specials) { m.dec_ref(kv.m_key); m.dec_ref(kv.m_value.first); m.dec_ref(kv.m_value.second); } m_uf2bvuf.reset(); m_min_max_specials.reset(); } expr_ref bv2fpa_converter::convert_bv2fp(sort * s, expr * sgn, expr * exp, expr * sig) { unsynch_mpz_manager & mpzm = m_fpa_util.fm().mpz_manager(); unsynch_mpq_manager & mpqm = m_fpa_util.fm().mpq_manager(); expr_ref res(m); mpf fp_val; unsigned ebits = m_fpa_util.get_ebits(s); unsigned sbits = m_fpa_util.get_sbits(s); unsigned sgn_sz = 1; unsigned exp_sz = ebits; unsigned sig_sz = sbits - 1; rational sgn_q(0), sig_q(0), exp_q(0); if (sgn) m_bv_util.is_numeral(sgn, sgn_q, sgn_sz); if (exp) m_bv_util.is_numeral(exp, exp_q, exp_sz); if (sig) m_bv_util.is_numeral(sig, sig_q, sig_sz); // un-bias exponent rational exp_unbiased_q; exp_unbiased_q = exp_q - m_fpa_util.fm().m_powers2.m1(ebits - 1); mpz sig_z; mpf_exp_t exp_z; mpzm.set(sig_z, sig_q.to_mpq().numerator()); exp_z = mpzm.get_int64(exp_unbiased_q.to_mpq().numerator()); m_fpa_util.fm().set(fp_val, ebits, sbits, !mpqm.is_zero(sgn_q.to_mpq()), exp_z, sig_z); mpzm.del(sig_z); res = m_fpa_util.mk_value(fp_val); TRACE("bv2fpa", tout << "[" << mk_ismt2_pp(sgn, m) << " " << mk_ismt2_pp(exp, m) << " " << mk_ismt2_pp(sig, m) << "] == " << mk_ismt2_pp(res, m) << std::endl;); m_fpa_util.fm().del(fp_val); return res; } expr_ref bv2fpa_converter::convert_bv2fp(model_core * mc, sort * s, expr * bv) { SASSERT(m_bv_util.is_bv(bv)); unsigned ebits = m_fpa_util.get_ebits(s); unsigned sbits = m_fpa_util.get_sbits(s); unsigned bv_sz = sbits + ebits; expr_ref bv_num(bv, m); if (!m_bv_util.is_numeral(bv) && is_app(bv)) { if (!mc->eval(to_app(bv)->get_decl(), bv_num)) { bv_num = m_bv_util.mk_numeral(0, m_bv_util.get_bv_size(bv)); } } expr_ref sgn(m), exp(m), sig(m); sgn = m_bv_util.mk_extract(bv_sz - 1, bv_sz - 1, bv_num); exp = m_bv_util.mk_extract(bv_sz - 2, sbits - 1, bv_num); sig = m_bv_util.mk_extract(sbits - 2, 0, bv_num); expr_ref v_sgn(m), v_exp(m), v_sig(m); m_th_rw(sgn, v_sgn); m_th_rw(exp, v_exp); m_th_rw(sig, v_sig); return convert_bv2fp(s, v_sgn, v_exp, v_sig); } expr_ref bv2fpa_converter::convert_bv2rm(expr * bv_rm) { expr_ref res(m); rational bv_val; if (m_bv_util.is_numeral(bv_rm, bv_val)) { SASSERT(bv_val.is_uint64()); switch (bv_val.get_uint64()) { case BV_RM_TIES_TO_AWAY: res = m_fpa_util.mk_round_nearest_ties_to_away(); break; case BV_RM_TIES_TO_EVEN: res = m_fpa_util.mk_round_nearest_ties_to_even(); break; case BV_RM_TO_NEGATIVE: res = m_fpa_util.mk_round_toward_negative(); break; case BV_RM_TO_POSITIVE: res = m_fpa_util.mk_round_toward_positive(); break; case BV_RM_TO_ZERO: default: res = m_fpa_util.mk_round_toward_zero(); } } else { std::cout << expr_ref(bv_rm, m) << " not converted\n"; } return res; } expr_ref bv2fpa_converter::convert_bv2rm(model_core * mc, expr * val) { expr_ref res(m); if (val) { expr_ref eval_v(m); if (m_bv_util.is_numeral(val)) res = convert_bv2rm(val); else if (is_app(val) && mc->eval(to_app(val)->get_decl(), eval_v)) res = convert_bv2rm(eval_v); else { // BUG: doesn't work for parametric definition // needs to be an ite expression. res = m_fpa_util.mk_round_toward_zero(); } } return res; } expr_ref bv2fpa_converter::rebuild_floats(model_core * mc, sort * s, expr * e) { expr_ref result(m); TRACE("bv2fpa_rebuild", tout << "rebuild floats in " << mk_ismt2_pp(s, m) << " for "; if (e) tout << mk_ismt2_pp(e, m); else tout << "nil"; tout << std::endl; ); if (m_fpa_util.is_float(s)) { if (e == nullptr) result = m_fpa_util.mk_pzero(s); else if (m_fpa_util.is_numeral(e)) result = e; else { SASSERT(m_bv_util.is_bv(e) && m_bv_util.get_bv_size(e) == (m_fpa_util.get_ebits(s) + m_fpa_util.get_sbits(s))); result = convert_bv2fp(mc, s, e); } } else if (m_fpa_util.is_rm(s)) { if (e == nullptr) result = m_fpa_util.mk_round_toward_zero(); else if (m_fpa_util.is_rm_numeral(e)) result = e; else { SASSERT(m_bv_util.is_bv(e) && m_bv_util.get_bv_size(e) == 3); result = convert_bv2rm(mc, e); } } else if (is_app(e)) { app * a = to_app(e); expr_ref_vector new_args(m); for (expr* arg : *a) { new_args.push_back(rebuild_floats(mc, m.get_sort(arg), arg)); } result = m.mk_app(a->get_decl(), new_args.size(), new_args.c_ptr()); } else if (is_var(e)) { result = e; } SASSERT(!result || m.get_sort(result) == s); return result; } bv2fpa_converter::array_model bv2fpa_converter::convert_array_func_interp(model_core * mc, func_decl * f, func_decl * bv_f) { SASSERT(f->get_arity() == 0); array_util arr_util(m); array_model am(m); sort_ref_vector array_domain(m); unsigned arity = f->get_range()->get_num_parameters()-1; expr_ref as_arr_mdl(m); as_arr_mdl = mc->get_const_interp(bv_f); if (as_arr_mdl == 0) return am; TRACE("bv2fpa", tout << "arity=0 func_interp for " << mk_ismt2_pp(f, m) << " := " << mk_ismt2_pp(as_arr_mdl, m) << std::endl;); SASSERT(arr_util.is_as_array(as_arr_mdl)); for (unsigned i = 0; i < arity; i++) array_domain.push_back(to_sort(f->get_range()->get_parameter(i).get_ast())); sort * rng = to_sort(f->get_range()->get_parameter(arity).get_ast()); bv_f = arr_util.get_as_array_func_decl(to_app(as_arr_mdl)); am.new_float_fd = m.mk_fresh_func_decl(arity, array_domain.c_ptr(), rng); am.new_float_fi = convert_func_interp(mc, am.new_float_fd, bv_f); am.bv_fd = bv_f; am.result = arr_util.mk_as_array(am.new_float_fd); return am; } func_interp * bv2fpa_converter::convert_func_interp(model_core * mc, func_decl * f, func_decl * bv_f) { SASSERT(f->get_arity() > 0); func_interp * result = nullptr; sort * rng = f->get_range(); sort * const * dmn = f->get_domain(); unsigned arity = bv_f->get_arity(); func_interp * bv_fi = mc->get_func_interp(bv_f); result = alloc(func_interp, m, arity); if (bv_fi) { fpa_rewriter rw(m); for (unsigned i = 0; i < bv_fi->num_entries(); i++) { func_entry const * bv_fe = bv_fi->get_entry(i); expr * const * bv_args = bv_fe->get_args(); expr_ref_buffer new_args(m); for (unsigned j = 0; j < arity; j++) { sort * ft_dj = dmn[j]; expr * bv_aj = bv_args[j]; expr_ref ai = rebuild_floats(mc, ft_dj, to_app(bv_aj)); m_th_rw(ai); new_args.push_back(std::move(ai)); } expr_ref bv_fres(m); bv_fres = bv_fe->get_result(); expr_ref ft_fres = rebuild_floats(mc, rng, to_app(bv_fres)); m_th_rw(ft_fres); TRACE("bv2fpa", tout << "func_interp entry #" << i << ":" << std::endl; tout << "(" << bv_f->get_name(); for (unsigned i = 0; i < bv_f->get_arity(); i++) tout << " " << mk_ismt2_pp(bv_args[i], m); tout << ") = " << mk_ismt2_pp(bv_fres, m) << std::endl; tout << " --> " << std::endl; tout << "(" << f->get_name(); for (unsigned i = 0; i < new_args.size(); i++) tout << " " << mk_ismt2_pp(new_args[i], m); tout << ") = " << mk_ismt2_pp(ft_fres, m) << std::endl;); func_entry * fe = result->get_entry(new_args.c_ptr()); if (fe == nullptr) { // Avoid over-specification of a partially interpreted theory function if (f->get_family_id() != m_fpa_util.get_family_id() || m_fpa_util.is_considered_uninterpreted(f, new_args.size(), new_args.c_ptr())) result->insert_new_entry(new_args.c_ptr(), ft_fres); } else { // The BV model may have multiple equivalent entries using different // representations of NaN. We can only keep one and we check that // the results for all those entries are the same. if (m_fpa_util.is_float(rng) && ft_fres != fe->get_result()) throw default_exception("BUG: UF function entries disagree with each other"); } } expr_ref bv_els(m); bv_els = bv_fi->get_else(); if (bv_els) { expr_ref ft_els = rebuild_floats(mc, rng, bv_els); m_th_rw(ft_els); TRACE("bv2fpa", tout << "else=" << mk_ismt2_pp(ft_els, m) << std::endl;); result->set_else(ft_els); } } return result; } void bv2fpa_converter::convert_consts(model_core * mc, model_core * target_model, obj_hashtable & seen) { for (auto const& kv : m_const2bv) { func_decl * var = kv.m_key; app * val = to_app(kv.m_value); SASSERT(m_fpa_util.is_float(var->get_range())); SASSERT(var->get_range()->get_num_parameters() == 2); unsigned ebits = m_fpa_util.get_ebits(var->get_range()); unsigned sbits = m_fpa_util.get_sbits(var->get_range()); app * a0 = to_app(val->get_arg(0)); expr_ref v0(m), v1(m), v2(m); #ifdef Z3DEBUG app * a1 = to_app(val->get_arg(1)); app * a2 = to_app(val->get_arg(2)); v0 = mc->get_const_interp(a0->get_decl()); v1 = mc->get_const_interp(a1->get_decl()); v2 = mc->get_const_interp(a2->get_decl()); #else expr * bv = mc->get_const_interp(to_app(to_app(a0)->get_arg(0))->get_decl()); if (bv == nullptr) { v0 = m_bv_util.mk_numeral(0, 1); v1 = m_bv_util.mk_numeral(0, ebits); v2 = m_bv_util.mk_numeral(0, sbits-1); } else { unsigned bv_sz = m_bv_util.get_bv_size(bv); v0 = m_bv_util.mk_extract(bv_sz-1, bv_sz-1, bv); v1 = m_bv_util.mk_extract(bv_sz-2, sbits-1, bv); v2 = m_bv_util.mk_extract(sbits-2, 0, bv); } #endif if (!v0) v0 = m_bv_util.mk_numeral(0, 1); if (!v1) v1 = m_bv_util.mk_numeral(0, ebits); if (!v2) v2 = m_bv_util.mk_numeral(0, sbits-1); expr_ref sgn(m), exp(m), sig(m); m_th_rw(v0, sgn); m_th_rw(v1, exp); m_th_rw(v2, sig); SASSERT(val->is_app_of(m_fpa_util.get_family_id(), OP_FPA_FP)); #ifdef Z3DEBUG SASSERT(to_app(val->get_arg(0))->get_decl()->get_arity() == 0); SASSERT(to_app(val->get_arg(1))->get_decl()->get_arity() == 0); SASSERT(to_app(val->get_arg(2))->get_decl()->get_arity() == 0); seen.insert(to_app(val->get_arg(0))->get_decl()); seen.insert(to_app(val->get_arg(1))->get_decl()); seen.insert(to_app(val->get_arg(2))->get_decl()); #else SASSERT(a->get_arg(0)->get_kind() == OP_EXTRACT); SASSERT(to_app(a->get_arg(0))->get_arg(0)->get_kind() == OP_EXTRACT); seen.insert(to_app(to_app(val->get_arg(0))->get_arg(0))->get_decl()); #endif if (!sgn && !sig && !exp) continue; expr_ref cv = convert_bv2fp(var->get_range(), sgn, exp, sig); target_model->register_decl(var, cv); TRACE("bv2fpa", tout << var->get_name() << " == " << mk_ismt2_pp(cv, m) << std::endl;); } } void bv2fpa_converter::convert_rm_consts(model_core * mc, model_core * target_model, obj_hashtable & seen) { for (auto const& kv : m_rm_const2bv) { func_decl * var = kv.m_key; SASSERT(m_fpa_util.is_rm(var->get_range())); expr * val = kv.m_value; SASSERT(m_fpa_util.is_bv2rm(val)); expr * bvval = to_app(val)->get_arg(0); expr_ref fv = convert_bv2rm(mc, to_app(bvval)); TRACE("bv2fpa", tout << var->get_name() << " == " << mk_ismt2_pp(fv, m) << std::endl;); target_model->register_decl(var, fv); seen.insert(to_app(bvval)->get_decl()); } } void bv2fpa_converter::convert_min_max_specials(model_core * mc, model_core * target_model, obj_hashtable & seen) { for (auto const& kv : m_min_max_specials) { func_decl * f = kv.m_key; app * pn_cnst = kv.m_value.first; app * np_cnst = kv.m_value.second; expr_ref pzero(m), nzero(m); pzero = m_fpa_util.mk_pzero(f->get_range()); nzero = m_fpa_util.mk_nzero(f->get_range()); expr_ref pn(m), np(m); if (!mc->eval(pn_cnst->get_decl(), pn)) pn = pzero; if (!mc->eval(np_cnst->get_decl(), np)) np = pzero; seen.insert(pn_cnst->get_decl()); seen.insert(np_cnst->get_decl()); rational pn_num, np_num; unsigned bv_sz; m_bv_util.is_numeral(pn, pn_num, bv_sz); m_bv_util.is_numeral(np, np_num, bv_sz); func_interp * flt_fi = alloc(func_interp, m, f->get_arity()); expr * pn_args[2] = { pzero, nzero }; if (pn != np) flt_fi->insert_new_entry(pn_args, (pn_num.is_one() ? nzero : pzero)); flt_fi->set_else(np_num.is_one() ? nzero : pzero); target_model->register_decl(f, flt_fi); TRACE("bv2fpa", tout << "fp.min/fp.max special: " << std::endl << mk_ismt2_pp(f, m) << " == " << mk_ismt2_pp(flt_fi->get_interp(), m) << std::endl;); } } void bv2fpa_converter::convert_uf2bvuf(model_core * mc, model_core * target_model, obj_hashtable & seen) { for (auto const& kv : m_uf2bvuf) { func_decl * f = kv.m_key; func_decl * f_uf = kv.m_value; seen.insert(f_uf); if (f->get_arity() == 0) { array_util au(m); if (au.is_array(f->get_range())) { array_model am = convert_array_func_interp(mc, f, f_uf); if (am.new_float_fd) target_model->register_decl(am.new_float_fd, am.new_float_fi); if (am.result) target_model->register_decl(f, am.result); if (am.bv_fd) seen.insert(am.bv_fd); } else { // Just keep. SASSERT(!m_fpa_util.is_float(f->get_range()) && !m_fpa_util.is_rm(f->get_range())); expr_ref val(m); if (mc->eval(f_uf, val)) target_model->register_decl(f, val); } } else if (f->get_family_id() == m_fpa_util.get_fid()) { // kv.m_value contains the model for the unspecified cases of kv.m_key in terms of bit-vectors. // convert_func_interp rebuilds a func_interp on floats. // f is a floating point function: f(x,y) : Float // f_uf is a bit-vector function: f_uf(xB,yB) : BitVec // then there is f_def: f_Bv(xB, yB) := if(range(xB),.., f_uf(xB,yB)) // f(x,y) := to_float(if(range(to_bv(x)), ... f_uf(to_bv(xB), to_bv(yB)))) - not practical // := if(range_fp(x), ...., to_float(f_uf(...)) - approach (via fpa_util::is_considered_uninterpreted) func_interp *fi = convert_func_interp(mc, f, f_uf); if (fi->num_entries() > 0 || fi->get_else() != nullptr) target_model->register_decl(f, fi); else dealloc(fi); } } TRACE("bv2fpa", tout << "Target model: " << *target_model; ); } void bv2fpa_converter::display(std::ostream & out) { for (auto const& kv : m_const2bv) { const symbol & n = kv.m_key->get_name(); out << "\n (" << n << " "; unsigned indent = n.size() + 4; out << mk_ismt2_pp(kv.m_value, m, indent) << ")"; } for (auto const& kv : m_rm_const2bv) { const symbol & n = kv.m_key->get_name(); out << "\n (" << n << " "; unsigned indent = n.size() + 4; out << mk_ismt2_pp(kv.m_value, m, indent) << ")"; } for (auto const& kv : m_uf2bvuf) { const symbol & n = kv.m_key->get_name(); out << "\n (" << n << " "; unsigned indent = n.size() + 4; out << mk_ismt2_pp(kv.m_value, m, indent) << ")"; } for (auto const& kv : m_min_max_specials) { const symbol & n = kv.m_key->get_name(); out << "\n (" << n << " "; unsigned indent = n.size() + 4; out << mk_ismt2_pp(kv.m_value.first, m, indent) << "; " << mk_ismt2_pp(kv.m_value.second, m, indent) << ")"; } } bv2fpa_converter * bv2fpa_converter::translate(ast_translation & translator) { bv2fpa_converter * res = alloc(bv2fpa_converter, translator.to()); for (auto const& kv : m_const2bv) { func_decl * k = translator(kv.m_key); expr * v = translator(kv.m_value); res->m_const2bv.insert(k, v); translator.to().inc_ref(k); translator.to().inc_ref(v); } for (auto const& kv : m_rm_const2bv) { func_decl * k = translator(kv.m_key); expr * v = translator(kv.m_value); res->m_rm_const2bv.insert(k, v); translator.to().inc_ref(k); translator.to().inc_ref(v); } for (auto const& kv : m_uf2bvuf) { func_decl * k = translator(kv.m_key); func_decl * v = translator(kv.m_value); res->m_uf2bvuf.insert(k, v); translator.to().inc_ref(k); translator.to().inc_ref(v); } for (auto const& kv : m_min_max_specials) { func_decl * k = translator(kv.m_key); app * v1 = translator(kv.m_value.first); app * v2 = translator(kv.m_value.second); res->m_min_max_specials.insert(k, std::pair(v1, v2)); translator.to().inc_ref(k); translator.to().inc_ref(v1); translator.to().inc_ref(v2); } return res; } z3-z3-4.8.7/src/ast/fpa/bv2fpa_converter.h000066400000000000000000000042561356505360400202020ustar00rootroot00000000000000/*++ Copyright (c) 2016 Microsoft Corporation Module Name: bv2fpa_converter.h Abstract: Model conversion for fpa2bv_converter Author: Christoph (cwinter) 2016-10-15 Notes: --*/ #ifndef BV2FPA_CONVERTER_H_ #define BV2FPA_CONVERTER_H_ #include "ast/fpa_decl_plugin.h" #include "ast/bv_decl_plugin.h" #include "ast/rewriter/th_rewriter.h" #include "model/model_core.h" #include "ast/fpa/fpa2bv_converter.h" class bv2fpa_converter { ast_manager & m; fpa_util m_fpa_util; bv_util m_bv_util; th_rewriter m_th_rw; obj_map m_const2bv; obj_map m_rm_const2bv; obj_map m_uf2bvuf; obj_map > m_min_max_specials; public: bv2fpa_converter(ast_manager & m); bv2fpa_converter(ast_manager & m, fpa2bv_converter & conv); virtual ~bv2fpa_converter(); void display(std::ostream & out); bv2fpa_converter * translate(ast_translation & translator); expr_ref convert_bv2fp(sort * s, expr * sgn, expr * exp, expr * sig); expr_ref convert_bv2fp(model_core * mc, sort * s, expr * bv); expr_ref convert_bv2rm(expr * eval_v); expr_ref convert_bv2rm(model_core * mc, expr * val); void convert_consts(model_core * mc, model_core * target_model, obj_hashtable & seen); void convert_rm_consts(model_core * mc, model_core * target_model, obj_hashtable & seen); void convert_min_max_specials(model_core * mc, model_core * target_model, obj_hashtable & seen); void convert_uf2bvuf(model_core * mc, model_core * target_model, obj_hashtable & seen); func_interp * convert_func_interp(model_core * mc, func_decl * f, func_decl * bv_f); expr_ref rebuild_floats(model_core * mc, sort * s, expr * e); class array_model { public: func_decl * new_float_fd; func_interp * new_float_fi; func_decl * bv_fd; expr_ref result; array_model(ast_manager & m) : new_float_fd(nullptr), new_float_fi(nullptr), bv_fd(nullptr), result(m) {} }; array_model convert_array_func_interp(model_core * mc, func_decl * f, func_decl * bv_f); }; #endif z3-z3-4.8.7/src/ast/fpa/fpa2bv_converter.cpp000066400000000000000000004707201356505360400205400ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: fpa2bv_converter.cpp Abstract: Conversion routines for Floating Point -> Bit-Vector Author: Christoph (cwinter) 2012-02-09 Notes: --*/ #include #include "ast/ast_smt2_pp.h" #include "ast/well_sorted.h" #include "ast/rewriter/th_rewriter.h" #include "ast/used_vars.h" #include "ast/rewriter/var_subst.h" #include "ast/fpa/fpa2bv_converter.h" #include "ast/rewriter/fpa_rewriter.h" #define BVULT(X,Y,R) { expr_ref t(m); t = m_bv_util.mk_ule(Y,X); m_simp.mk_not(t, R); } fpa2bv_converter::fpa2bv_converter(ast_manager & m) : m(m), m_simp(m), m_util(m), m_bv_util(m), m_arith_util(m), m_dt_util(m), m_seq_util(m), m_mpf_manager(m_util.fm()), m_mpz_manager(m_mpf_manager.mpz_manager()), m_hi_fp_unspecified(true), m_extra_assertions(m) { m_plugin = static_cast(m.get_plugin(m.mk_family_id("fpa"))); } fpa2bv_converter::~fpa2bv_converter() { reset(); } void fpa2bv_converter::mk_eq(expr * a, expr * b, expr_ref & result) { if (is_float(a) && is_float(b)) { SASSERT(m_util.is_fp(a) && m_util.is_fp(b)); TRACE("fpa2bv", tout << "mk_eq a=" << mk_ismt2_pp(a, m) << std::endl; tout << "mk_eq b=" << mk_ismt2_pp(b, m) << std::endl;); expr_ref eq_sgn(m), eq_exp(m), eq_sig(m); m_simp.mk_eq(to_app(a)->get_arg(0), to_app(b)->get_arg(0), eq_sgn); m_simp.mk_eq(to_app(a)->get_arg(1), to_app(b)->get_arg(1), eq_exp); m_simp.mk_eq(to_app(a)->get_arg(2), to_app(b)->get_arg(2), eq_sig); dbg_decouple("fpa2bv_eq_sgn", eq_sgn); dbg_decouple("fpa2bv_eq_exp", eq_exp); dbg_decouple("fpa2bv_eq_sig", eq_sig); expr_ref both_the_same(m); m_simp.mk_and(eq_sgn, eq_exp, eq_sig, both_the_same); dbg_decouple("fpa2bv_eq_both_the_same", both_the_same); // The SMT FPA theory asks for _one_ NaN value, but the bit-blasting // has many, like IEEE754. This encoding of equality makes it look like // a single NaN again. expr_ref a_is_nan(m), b_is_nan(m), both_are_nan(m); mk_is_nan(a, a_is_nan); mk_is_nan(b, b_is_nan); m_simp.mk_and(a_is_nan, b_is_nan, both_are_nan); dbg_decouple("fpa2bv_eq_both_are_nan", both_are_nan); m_simp.mk_or(both_are_nan, both_the_same, result); } else if (is_rm(a) && is_rm(b)) { SASSERT(m_util.is_bv2rm(b) && m_util.is_bv2rm(a)); TRACE("fpa2bv", tout << "mk_eq_rm a=" << mk_ismt2_pp(a, m) << std::endl; tout << "mk_eq_rm b=" << mk_ismt2_pp(b, m) << std::endl;); m_simp.mk_eq(to_app(a)->get_arg(0), to_app(b)->get_arg(0), result); } else UNREACHABLE(); } void fpa2bv_converter::mk_ite(expr * c, expr * t, expr * f, expr_ref & result) { if (m_util.is_fp(t) && m_util.is_fp(f)) { expr_ref t_sgn(m), t_sig(m), t_exp(m); expr_ref f_sgn(m), f_sig(m), f_exp(m); split_fp(t, t_sgn, t_exp, t_sig); split_fp(f, f_sgn, f_exp, f_sig); expr_ref sgn(m), s(m), e(m); m_simp.mk_ite(c, t_sgn, f_sgn, sgn); m_simp.mk_ite(c, t_sig, f_sig, s); m_simp.mk_ite(c, t_exp, f_exp, e); result = m_util.mk_fp(sgn, e, s); } else if (m_util.is_rm(t) && m_util.is_rm(f)) { SASSERT(m_util.is_bv2rm(t) && m_util.is_bv2rm(f)); TRACE("fpa2bv", tout << "ite rm: t=" << mk_ismt2_pp(t, m) << " f=" << mk_ismt2_pp(f, m) << std::endl; ); m_simp.mk_ite(c, to_app(t)->get_arg(0), to_app(f)->get_arg(0), result); result = m_util.mk_bv2rm(result); } else UNREACHABLE(); } void fpa2bv_converter::mk_distinct(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { // Note: in SMT there is only one NaN, so multiple of them are considered // equal, thus (distinct NaN NaN) is false, even if the two NaNs have // different bitwise representations (see also mk_eq). result = m.mk_true(); for (unsigned i = 0; i < num; i++) { for (unsigned j = i+1; j < num; j++) { expr_ref eq(m), neq(m); mk_eq(args[i], args[j], eq); neq = m.mk_not(eq); m_simp.mk_and(result, neq, result); } } } void fpa2bv_converter::mk_numeral(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 0); SASSERT(f->get_num_parameters() == 1); SASSERT(f->get_parameter(0).is_external()); unsigned p_id = f->get_parameter(0).get_ext_id(); mpf const & v = m_plugin->get_value(p_id); mk_numeral(f->get_range(), v, result); } void fpa2bv_converter::mk_numeral(sort * s, mpf const & v, expr_ref & result) { unsigned sbits = v.get_sbits(); unsigned ebits = v.get_ebits(); bool sign = m_util.fm().sgn(v); mpz const & sig = m_util.fm().sig(v); mpf_exp_t const & exp = m_util.fm().exp(v); if (m_util.fm().is_nan(v)) mk_nan(s, result); else if (m_util.fm().is_inf(v)) { if (m_util.fm().sgn(v)) mk_ninf(s, result); else mk_pinf(s, result); } else { expr_ref bv_sgn(m), bv_sig(m), e(m), biased_exp(m); bv_sgn = m_bv_util.mk_numeral((sign) ? 1 : 0, 1); bv_sig = m_bv_util.mk_numeral(rational(sig), sbits-1); e = m_bv_util.mk_numeral(exp, ebits); mk_bias(e, biased_exp); result = m_util.mk_fp(bv_sgn, biased_exp, bv_sig); TRACE("fpa2bv_dbg", tout << "value of [" << sign << " " << m_mpz_manager.to_string(sig) << " " << exp << "] is " << mk_ismt2_pp(result, m) << std::endl;); } } app * fpa2bv_converter::mk_fresh_const(char const * prefix, unsigned sz) { return m.mk_fresh_const(prefix, m_bv_util.mk_sort(sz)); } void fpa2bv_converter::mk_const(func_decl * f, expr_ref & result) { SASSERT(f->get_family_id() == null_family_id); SASSERT(f->get_arity() == 0); expr * r; if (m_const2bv.find(f, r)) { result = r; } else { sort * srt = f->get_range(); SASSERT(is_float(srt)); unsigned ebits = m_util.get_ebits(srt); unsigned sbits = m_util.get_sbits(srt); app_ref sgn(m), s(m), e(m); #ifdef Z3DEBUG std::string p("fpa2bv"); std::string name = f->get_name().str(); sgn = mk_fresh_const((p + "_sgn_" + name).c_str(), 1); e = mk_fresh_const((p + "_exp_" + name).c_str(), ebits); s = mk_fresh_const((p + "_sig_" + name).c_str(), sbits-1); #else app_ref bv(m); unsigned bv_sz = 1 + ebits + (sbits - 1); bv = mk_fresh_const(nullptr, bv_sz); sgn = m_bv_util.mk_extract(bv_sz - 1, bv_sz - 1, bv); e = m_bv_util.mk_extract(bv_sz - 2, sbits - 1, bv); s = m_bv_util.mk_extract(sbits - 2, 0, bv); SASSERT(m_bv_util.get_bv_size(sgn) == 1); SASSERT(m_bv_util.get_bv_size(s) == sbits-1); SASSERT(m_bv_util.get_bv_size(e) == ebits); #endif result = m_util.mk_fp(sgn, e, s); m_const2bv.insert(f, result); m.inc_ref(f); m.inc_ref(result); } } void fpa2bv_converter::mk_var(unsigned base_inx, sort * srt, expr_ref & result) { SASSERT(is_float(srt)); unsigned ebits = m_util.get_ebits(srt); unsigned sbits = m_util.get_sbits(srt); expr_ref sgn(m), s(m), e(m); sgn = m.mk_var(base_inx, m_bv_util.mk_sort(1)); s = m.mk_var(base_inx+1, m_bv_util.mk_sort(sbits-1)); e = m.mk_var(base_inx+2, m_bv_util.mk_sort(ebits)); result = m_util.mk_fp(sgn, e, s); } expr_ref fpa2bv_converter::extra_quantify(expr * e) { used_vars uv; unsigned nv; ptr_buffer new_decl_sorts; sbuffer new_decl_names; expr_ref_buffer subst_map(m); uv(e); nv = uv.get_num_vars(); subst_map.resize(uv.get_max_found_var_idx_plus_1()); if (nv == 0) return expr_ref(e, m); for (unsigned i = 0; i < nv; i++) { if (uv.contains(i)) { TRACE("fpa2bv", tout << "uv[" << i << "] = " << mk_ismt2_pp(uv.get(i), m) << std::endl; ); sort * s = uv.get(i); var * v = m.mk_var(i, s); new_decl_sorts.push_back(s); new_decl_names.push_back(symbol(i)); subst_map.set(i, v); } } expr_ref res(m); var_subst vsubst(m); res = vsubst.operator()(e, nv, subst_map.c_ptr()); TRACE("fpa2bv", tout << "subst'd = " << mk_ismt2_pp(res, m) << std::endl; ); res = m.mk_forall(nv, new_decl_sorts.c_ptr(), new_decl_names.c_ptr(), res); return res; } void fpa2bv_converter::mk_uf(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { TRACE("fpa2bv", tout << "UF: " << mk_ismt2_pp(f, m) << std::endl; ); expr_ref fapp(m); sort_ref rng(m); app_ref bv_app(m), flt_app(m); rng = f->get_range(); fapp = m.mk_app(f, num, args); if (m_util.is_float(rng)) { sort_ref bv_rng(m); expr_ref new_eq(m); unsigned ebits = m_util.get_ebits(rng); unsigned sbits = m_util.get_sbits(rng); unsigned bv_sz = ebits+sbits; bv_rng = m_bv_util.mk_sort(bv_sz); func_decl * bv_f = mk_bv_uf(f, f->get_domain(), bv_rng); bv_app = m.mk_app(bv_f, num, args); flt_app = m_util.mk_fp(m_bv_util.mk_extract(bv_sz-1, bv_sz-1, bv_app), m_bv_util.mk_extract(sbits+ebits-2, sbits-1, bv_app), m_bv_util.mk_extract(sbits-2, 0, bv_app)); new_eq = m.mk_eq(fapp, flt_app); m_extra_assertions.push_back(extra_quantify(new_eq)); result = flt_app; } else if (m_util.is_rm(rng)) { sort_ref bv_rng(m); expr_ref new_eq(m); bv_rng = m_bv_util.mk_sort(3); func_decl * bv_f = mk_bv_uf(f, f->get_domain(), bv_rng); bv_app = m.mk_app(bv_f, num, args); flt_app = m_util.mk_bv2rm(bv_app); new_eq = m.mk_eq(fapp, flt_app); m_extra_assertions.push_back(extra_quantify(new_eq)); result = flt_app; } else result = fapp; TRACE("fpa2bv", tout << "UF result: " << mk_ismt2_pp(result, m) << std::endl; ); SASSERT(is_well_sorted(m, result)); } void fpa2bv_converter::mk_rm_const(func_decl * f, expr_ref & result) { SASSERT(f->get_family_id() == null_family_id); SASSERT(f->get_arity() == 0); expr * r; if (m_rm_const2bv.find(f, r)) { result = r; } else { SASSERT(is_rm(f->get_range())); expr_ref bv3(m); bv3 = m.mk_fresh_const( #ifdef Z3DEBUG "fpa2bv_rm" #else nullptr #endif , m_bv_util.mk_sort(3)); result = m_util.mk_bv2rm(bv3); m_rm_const2bv.insert(f, result); m.inc_ref(f); m.inc_ref(result); expr_ref rcc(m); rcc = bu().mk_ule(bv3, bu().mk_numeral(4, 3)); m_extra_assertions.push_back(rcc); } } void fpa2bv_converter::mk_pinf(func_decl * f, expr_ref & result) { mk_pinf(f->get_range(), result); } void fpa2bv_converter::mk_pinf(sort * s, expr_ref & result) { SASSERT(is_float(s)); unsigned sbits = m_util.get_sbits(s); unsigned ebits = m_util.get_ebits(s); expr_ref top_exp(m); mk_top_exp(ebits, top_exp); result = m_util.mk_fp(m_bv_util.mk_numeral(0, 1), top_exp, m_bv_util.mk_numeral(0, sbits-1)); } void fpa2bv_converter::mk_ninf(func_decl * f, expr_ref & result) { mk_ninf(f->get_range(), result); } void fpa2bv_converter::mk_ninf(sort * s, expr_ref & result) { SASSERT(is_float(s)); unsigned sbits = m_util.get_sbits(s); unsigned ebits = m_util.get_ebits(s); expr_ref top_exp(m); mk_top_exp(ebits, top_exp); result = m_util.mk_fp(m_bv_util.mk_numeral(1, 1), top_exp, m_bv_util.mk_numeral(0, sbits-1)); } void fpa2bv_converter::mk_nan(func_decl * f, expr_ref & result) { mk_nan(f->get_range(), result); } void fpa2bv_converter::mk_nan(sort * s, expr_ref & result) { SASSERT(is_float(s)); unsigned sbits = m_util.get_sbits(s); unsigned ebits = m_util.get_ebits(s); expr_ref top_exp(m); mk_top_exp(ebits, top_exp); result = m_util.mk_fp(m_bv_util.mk_numeral(0, 1), top_exp, m_bv_util.mk_numeral(1, sbits - 1)); } void fpa2bv_converter::mk_nzero(func_decl * f, expr_ref & result) { mk_nzero(f->get_range(), result); } void fpa2bv_converter::mk_nzero(sort * s, expr_ref & result) { SASSERT(is_float(s)); unsigned sbits = m_util.get_sbits(s); unsigned ebits = m_util.get_ebits(s); expr_ref bot_exp(m); mk_bot_exp(ebits, bot_exp); result = m_util.mk_fp(m_bv_util.mk_numeral(1, 1), bot_exp, m_bv_util.mk_numeral(0, sbits - 1)); } void fpa2bv_converter::mk_pzero(func_decl * f, expr_ref & result) { mk_pzero(f->get_range(), result); } void fpa2bv_converter::mk_pzero(sort * s, expr_ref & result) { SASSERT(is_float(s)); unsigned sbits = m_util.get_sbits(s); unsigned ebits = m_util.get_ebits(s); expr_ref bot_exp(m); mk_bot_exp(ebits, bot_exp); result = m_util.mk_fp(m_bv_util.mk_numeral(0, 1), bot_exp, m_bv_util.mk_numeral(0, sbits-1)); } void fpa2bv_converter::mk_zero(sort * s, expr_ref & sgn, expr_ref & result) { expr_ref is_pos(m), pzero(m), nzero(m); is_pos = m.mk_eq(sgn, m_bv_util.mk_numeral(0, 1)); mk_pzero(s, pzero); mk_nzero(s, nzero); mk_ite(is_pos, pzero, nzero, result); } void fpa2bv_converter::mk_one(func_decl * f, expr_ref & sign, expr_ref & result) { mk_one(f->get_range(), sign, result); } void fpa2bv_converter::mk_one(sort * s, expr_ref & sign, expr_ref & result) { SASSERT(is_float(s)); unsigned sbits = m_util.get_sbits(s); unsigned ebits = m_util.get_ebits(s); result = m_util.mk_fp(sign, m_bv_util.mk_numeral(fu().fm().m_powers2.m1(ebits-1), ebits), m_bv_util.mk_numeral(0, sbits-1)); } void fpa2bv_converter::add_core(unsigned sbits, unsigned ebits, expr_ref & c_sgn, expr_ref & c_sig, expr_ref & c_exp, expr_ref & d_sgn, expr_ref & d_sig, expr_ref & d_exp, expr_ref & res_sgn, expr_ref & res_sig, expr_ref & res_exp) { // c/d are now such that c_exp >= d_exp. expr_ref exp_delta(m); exp_delta = m_bv_util.mk_bv_sub(c_exp, d_exp); dbg_decouple("fpa2bv_add_exp_delta", exp_delta); if (log2(sbits + 2) < ebits + 2) { // cap the delta expr_ref cap(m), cap_le_delta(m), exp_delta_ext(m); cap = m_bv_util.mk_numeral(sbits + 2, ebits + 2); cap_le_delta = m_bv_util.mk_ule(cap, m_bv_util.mk_zero_extend(2, exp_delta)); exp_delta_ext = m_bv_util.mk_zero_extend(2, exp_delta); m_simp.mk_ite(cap_le_delta, cap, exp_delta_ext, exp_delta); exp_delta = m_bv_util.mk_extract(ebits - 1, 0, exp_delta); dbg_decouple("fpa2bv_add_exp_cap", cap); } dbg_decouple("fpa2bv_add_exp_delta_capped", exp_delta); // Three extra bits for c/d c_sig = m_bv_util.mk_concat(c_sig, m_bv_util.mk_numeral(0, 3)); d_sig = m_bv_util.mk_concat(d_sig, m_bv_util.mk_numeral(0, 3)); SASSERT(is_well_sorted(m, c_sig)); SASSERT(is_well_sorted(m, d_sig)); // Alignment shift with sticky bit computation. expr_ref big_d_sig(m); big_d_sig = m_bv_util.mk_concat(d_sig, m_bv_util.mk_numeral(0, sbits+3)); SASSERT(is_well_sorted(m, big_d_sig)); expr_ref shifted_big(m), shifted_d_sig(m), sticky_raw(m), sticky(m); shifted_big = m_bv_util.mk_bv_lshr(big_d_sig, m_bv_util.mk_concat(m_bv_util.mk_numeral(0, (2*(sbits+3))-ebits), exp_delta)); shifted_d_sig = m_bv_util.mk_extract((2*(sbits+3)-1), (sbits+3), shifted_big); SASSERT(is_well_sorted(m, shifted_d_sig)); sticky_raw = m_bv_util.mk_extract(sbits+2, 0, shifted_big); expr_ref sticky_eq(m), nil_sbit3(m), one_sbit3(m); nil_sbit3 = m_bv_util.mk_numeral(0, sbits+3); one_sbit3 = m_bv_util.mk_numeral(1, sbits+3); m_simp.mk_eq(sticky_raw, nil_sbit3, sticky_eq); m_simp.mk_ite(sticky_eq, nil_sbit3, one_sbit3, sticky); SASSERT(is_well_sorted(m, sticky)); expr * or_args[2] = { shifted_d_sig, sticky }; shifted_d_sig = m_bv_util.mk_bv_or(2, or_args); SASSERT(is_well_sorted(m, shifted_d_sig)); expr_ref eq_sgn(m); m_simp.mk_eq(c_sgn, d_sgn, eq_sgn); dbg_decouple("fpa2bv_add_eq_sgn", eq_sgn); TRACE("fpa2bv_add_core", tout << "EQ_SGN = " << mk_ismt2_pp(eq_sgn, m) << std::endl; ); // two extra bits for catching the overflow. c_sig = m_bv_util.mk_zero_extend(2, c_sig); shifted_d_sig = m_bv_util.mk_zero_extend(2, shifted_d_sig); SASSERT(m_bv_util.get_bv_size(c_sig) == sbits+5); SASSERT(m_bv_util.get_bv_size(shifted_d_sig) == sbits+5); dbg_decouple("fpa2bv_add_c_sig", c_sig); dbg_decouple("fpa2bv_add_shifted_d_sig", shifted_d_sig); expr_ref sum(m), c_plus_d(m), c_minus_d(m); c_plus_d = m_bv_util.mk_bv_add(c_sig, shifted_d_sig); c_minus_d = m_bv_util.mk_bv_sub(c_sig, shifted_d_sig); m_simp.mk_ite(eq_sgn, c_plus_d, c_minus_d, sum); SASSERT(is_well_sorted(m, sum)); dbg_decouple("fpa2bv_add_sum", sum); expr_ref sign_bv(m), n_sum(m); sign_bv = m_bv_util.mk_extract(sbits+4, sbits+4, sum); n_sum = m_bv_util.mk_bv_neg(sum); dbg_decouple("fpa2bv_add_sign_bv", sign_bv); dbg_decouple("fpa2bv_add_n_sum", n_sum); family_id bvfid = m_bv_util.get_fid(); expr_ref res_sgn_c1(m), res_sgn_c2(m), res_sgn_c3(m); expr_ref not_c_sgn(m), not_d_sgn(m), not_sign_bv(m); not_c_sgn = m_bv_util.mk_bv_not(c_sgn); not_d_sgn = m_bv_util.mk_bv_not(d_sgn); not_sign_bv = m_bv_util.mk_bv_not(sign_bv); res_sgn_c1 = m.mk_app(bvfid, OP_BAND, not_c_sgn, d_sgn, sign_bv); res_sgn_c2 = m.mk_app(bvfid, OP_BAND, c_sgn, not_d_sgn, not_sign_bv); res_sgn_c3 = m.mk_app(bvfid, OP_BAND, c_sgn, d_sgn); expr * res_sgn_or_args[3] = { res_sgn_c1, res_sgn_c2, res_sgn_c3 }; res_sgn = m_bv_util.mk_bv_or(3, res_sgn_or_args); expr_ref res_sig_eq(m), sig_abs(m), one_1(m); one_1 = m_bv_util.mk_numeral(1, 1); m_simp.mk_eq(sign_bv, one_1, res_sig_eq); m_simp.mk_ite(res_sig_eq, n_sum, sum, sig_abs); dbg_decouple("fpa2bv_add_sig_abs", sig_abs); res_sig = m_bv_util.mk_extract(sbits+3, 0, sig_abs); res_exp = m_bv_util.mk_sign_extend(2, c_exp); // rounder requires 2 extra bits! } void fpa2bv_converter::mk_add(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 3); SASSERT(m_util.is_bv2rm(args[0])); expr_ref rm(m), x(m), y(m); rm = to_app(args[0])->get_arg(0); x = args[1]; y = args[2]; mk_add(f->get_range(), rm, x, y, result); } void fpa2bv_converter::mk_add(sort * s, expr_ref & rm, expr_ref & x, expr_ref & y, expr_ref & result) { expr_ref nan(m), nzero(m), pzero(m); mk_nan(s, nan); mk_nzero(s, nzero); mk_pzero(s, pzero); expr_ref x_is_nan(m), x_is_zero(m), x_is_pos(m), x_is_neg(m), x_is_inf(m); expr_ref y_is_nan(m), y_is_zero(m), y_is_pos(m), y_is_neg(m), y_is_inf(m); mk_is_nan(x, x_is_nan); mk_is_zero(x, x_is_zero); mk_is_pos(x, x_is_pos); mk_is_neg(x, x_is_neg); mk_is_inf(x, x_is_inf); mk_is_nan(y, y_is_nan); mk_is_zero(y, y_is_zero); mk_is_pos(y, y_is_pos); mk_is_neg(y, y_is_neg); mk_is_inf(y, y_is_inf); dbg_decouple("fpa2bv_add_x_is_nan", x_is_nan); dbg_decouple("fpa2bv_add_x_is_zero", x_is_zero); dbg_decouple("fpa2bv_add_x_is_pos", x_is_pos); dbg_decouple("fpa2bv_add_x_is_neg", x_is_neg); dbg_decouple("fpa2bv_add_x_is_inf", x_is_inf); dbg_decouple("fpa2bv_add_y_is_nan", y_is_nan); dbg_decouple("fpa2bv_add_y_is_zero", y_is_zero); dbg_decouple("fpa2bv_add_y_is_pos", y_is_pos); dbg_decouple("fpa2bv_add_y_is_neg", y_is_neg); dbg_decouple("fpa2bv_add_y_is_inf", y_is_inf); expr_ref c1(m), c2(m), c3(m), c4(m), c5(m), c6(m); expr_ref v1(m), v2(m), v3(m), v4(m), v5(m), v6(m), v7(m); m_simp.mk_or(x_is_nan, y_is_nan, c1); v1 = nan; mk_is_inf(x, c2); expr_ref nx(m), ny(m), nx_xor_ny(m), inf_xor(m); mk_is_neg(x, nx); mk_is_neg(y, ny); m_simp.mk_xor(nx, ny, nx_xor_ny); m_simp.mk_and(y_is_inf, nx_xor_ny, inf_xor); mk_ite(inf_xor, nan, x, v2); mk_is_inf(y, c3); expr_ref xy_is_neg(m), v3_and(m); m_simp.mk_xor(x_is_neg, y_is_neg, xy_is_neg); m_simp.mk_and(x_is_inf, xy_is_neg, v3_and); mk_ite(v3_and, nan, y, v3); expr_ref rm_is_to_neg(m), signs_and(m), signs_xor(m), v4_and(m), rm_and_xor(m), neg_cond(m); m_simp.mk_and(x_is_zero, y_is_zero, c4); m_simp.mk_and(x_is_neg, y_is_neg, signs_and); m_simp.mk_xor(x_is_neg, y_is_neg, signs_xor); mk_is_rm(rm, BV_RM_TO_NEGATIVE, rm_is_to_neg); m_simp.mk_and(rm_is_to_neg, signs_xor, rm_and_xor); m_simp.mk_or(signs_and, rm_and_xor, neg_cond); mk_ite(neg_cond, nzero, pzero, v4); m_simp.mk_and(x_is_neg, y_is_neg, v4_and); mk_ite(v4_and, x, v4, v4); c5 = x_is_zero; v5 = y; c6 = y_is_zero; v6 = x; // Actual addition. unsigned ebits = m_util.get_ebits(s); unsigned sbits = m_util.get_sbits(s); expr_ref a_sgn(m), a_sig(m), a_exp(m), a_lz(m), b_sgn(m), b_sig(m), b_exp(m), b_lz(m); unpack(x, a_sgn, a_sig, a_exp, a_lz, false); unpack(y, b_sgn, b_sig, b_exp, b_lz, false); dbg_decouple("fpa2bv_add_unpack_a_sgn", a_sgn); dbg_decouple("fpa2bv_add_unpack_a_sig", a_sig); dbg_decouple("fpa2bv_add_unpack_a_exp", a_exp); dbg_decouple("fpa2bv_add_unpack_b_sgn", b_sgn); dbg_decouple("fpa2bv_add_unpack_b_sig", b_sig); dbg_decouple("fpa2bv_add_unpack_b_exp", b_exp); expr_ref swap_cond(m); swap_cond = m_bv_util.mk_sle(a_exp, b_exp); expr_ref c_sgn(m), c_sig(m), c_exp(m), d_sgn(m), d_sig(m), d_exp(m); m_simp.mk_ite(swap_cond, b_sgn, a_sgn, c_sgn); m_simp.mk_ite(swap_cond, b_sig, a_sig, c_sig); // has sbits m_simp.mk_ite(swap_cond, b_exp, a_exp, c_exp); // has ebits m_simp.mk_ite(swap_cond, a_sgn, b_sgn, d_sgn); m_simp.mk_ite(swap_cond, a_sig, b_sig, d_sig); // has sbits m_simp.mk_ite(swap_cond, a_exp, b_exp, d_exp); // has ebits expr_ref res_sgn(m), res_sig(m), res_exp(m); add_core(sbits, ebits, c_sgn, c_sig, c_exp, d_sgn, d_sig, d_exp, res_sgn, res_sig, res_exp); expr_ref is_zero_sig(m), nil_sbit4(m); nil_sbit4 = m_bv_util.mk_numeral(0, sbits+4); m_simp.mk_eq(res_sig, nil_sbit4, is_zero_sig); SASSERT(is_well_sorted(m, is_zero_sig)); dbg_decouple("fpa2bv_add_is_zero_sig", is_zero_sig); expr_ref zero_case(m); mk_ite(rm_is_to_neg, nzero, pzero, zero_case); expr_ref rounded(m); round(s, rm, res_sgn, res_sig, res_exp, rounded); mk_ite(is_zero_sig, zero_case, rounded, v7); mk_ite(c6, v6, v7, result); mk_ite(c5, v5, result, result); mk_ite(c4, v4, result, result); mk_ite(c3, v3, result, result); mk_ite(c2, v2, result, result); mk_ite(c1, v1, result, result); SASSERT(is_well_sorted(m, result)); TRACE("fpa2bv_add", tout << "ADD = " << mk_ismt2_pp(result, m) << std::endl; ); } void fpa2bv_converter::mk_sub(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 3); expr_ref rm(m), x(m), y(m); rm = args[0]; x = args[1]; y = args[2]; mk_sub(f->get_range(), rm, x, y, result); } void fpa2bv_converter::mk_sub(sort * s, expr_ref & rm, expr_ref & x, expr_ref & y, expr_ref & result) { expr_ref t(m); mk_neg(s, y, t); mk_add(s, rm, x, t, result); } void fpa2bv_converter::mk_neg(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 1); expr_ref x(m); x = args[0]; mk_neg(f->get_range(), x, result); } void fpa2bv_converter::mk_neg(sort * srt, expr_ref & x, expr_ref & result) { expr_ref sgn(m), sig(m), e(m); split_fp(x, sgn, e, sig); expr_ref c(m), nsgn(m); mk_is_nan(x, c); nsgn = m_bv_util.mk_bv_not(sgn); expr_ref r_sgn(m); m_simp.mk_ite(c, sgn, nsgn, r_sgn); result = m_util.mk_fp(r_sgn, e, sig); } void fpa2bv_converter::mk_mul(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 3); SASSERT(m_util.is_bv2rm(args[0])); expr_ref rm(m), x(m), y(m); rm = to_app(args[0])->get_arg(0); x = args[1]; y = args[2]; mk_mul(f->get_range(), rm, x, y, result); } void fpa2bv_converter::mk_mul(sort * s, expr_ref & rm, expr_ref & x, expr_ref & y, expr_ref & result) { expr_ref nan(m), nzero(m), pzero(m), ninf(m), pinf(m); mk_nan(s, nan); mk_nzero(s, nzero); mk_pzero(s, pzero); mk_ninf(s, ninf); mk_pinf(s, pinf); expr_ref x_is_nan(m), x_is_zero(m), x_is_pos(m), x_is_inf(m); expr_ref y_is_nan(m), y_is_zero(m), y_is_pos(m), y_is_inf(m); mk_is_nan(x, x_is_nan); mk_is_zero(x, x_is_zero); mk_is_pos(x, x_is_pos); mk_is_inf(x, x_is_inf); mk_is_nan(y, y_is_nan); mk_is_zero(y, y_is_zero); mk_is_pos(y, y_is_pos); mk_is_inf(y, y_is_inf); dbg_decouple("fpa2bv_mul_x_is_nan", x_is_nan); dbg_decouple("fpa2bv_mul_x_is_zero", x_is_zero); dbg_decouple("fpa2bv_mul_x_is_pos", x_is_pos); dbg_decouple("fpa2bv_mul_x_is_inf", x_is_inf); dbg_decouple("fpa2bv_mul_y_is_nan", y_is_nan); dbg_decouple("fpa2bv_mul_y_is_zero", y_is_zero); dbg_decouple("fpa2bv_mul_y_is_pos", y_is_pos); dbg_decouple("fpa2bv_mul_y_is_inf", y_is_inf); expr_ref c1(m), c2(m), c3(m), c4(m), c5(m), c6(m); expr_ref v1(m), v2(m), v3(m), v4(m), v5(m), v6(m), v7(m); // (x is NaN) || (y is NaN) -> NaN m_simp.mk_or(x_is_nan, y_is_nan, c1); v1 = nan; // (x is +oo) -> if (y is 0) then NaN else inf with y's sign. mk_is_pinf(x, c2); expr_ref y_sgn_inf(m); mk_ite(y_is_pos, pinf, ninf, y_sgn_inf); mk_ite(y_is_zero, nan, y_sgn_inf, v2); // (y is +oo) -> if (x is 0) then NaN else inf with x's sign. mk_is_pinf(y, c3); expr_ref x_sgn_inf(m); mk_ite(x_is_pos, pinf, ninf, x_sgn_inf); mk_ite(x_is_zero, nan, x_sgn_inf, v3); // (x is -oo) -> if (y is 0) then NaN else inf with -y's sign. mk_is_ninf(x, c4); expr_ref neg_y_sgn_inf(m); mk_ite(y_is_pos, ninf, pinf, neg_y_sgn_inf); mk_ite(y_is_zero, nan, neg_y_sgn_inf, v4); // (y is -oo) -> if (x is 0) then NaN else inf with -x's sign. mk_is_ninf(y, c5); expr_ref neg_x_sgn_inf(m); mk_ite(x_is_pos, ninf, pinf, neg_x_sgn_inf); mk_ite(x_is_zero, nan, neg_x_sgn_inf, v5); // (x is 0) || (y is 0) -> x but with sign = x.sign ^ y.sign m_simp.mk_or(x_is_zero, y_is_zero, c6); expr_ref sign_xor(m); m_simp.mk_xor(x_is_pos, y_is_pos, sign_xor); mk_ite(sign_xor, nzero, pzero, v6); // else comes the actual multiplication. unsigned sbits = m_util.get_sbits(s); expr_ref a_sgn(m), a_sig(m), a_exp(m), a_lz(m), b_sgn(m), b_sig(m), b_exp(m), b_lz(m); unpack(x, a_sgn, a_sig, a_exp, a_lz, true); unpack(y, b_sgn, b_sig, b_exp, b_lz, true); dbg_decouple("fpa2bv_mul_a_sig", a_sig); dbg_decouple("fpa2bv_mul_a_exp", a_exp); dbg_decouple("fpa2bv_mul_b_sig", b_sig); dbg_decouple("fpa2bv_mul_b_exp", b_exp); expr_ref a_lz_ext(m), b_lz_ext(m); a_lz_ext = m_bv_util.mk_zero_extend(2, a_lz); b_lz_ext = m_bv_util.mk_zero_extend(2, b_lz); dbg_decouple("fpa2bv_mul_lz_a", a_lz); dbg_decouple("fpa2bv_mul_lz_b", b_lz); expr_ref a_sig_ext(m), b_sig_ext(m); a_sig_ext = m_bv_util.mk_zero_extend(sbits, a_sig); b_sig_ext = m_bv_util.mk_zero_extend(sbits, b_sig); expr_ref a_exp_ext(m), b_exp_ext(m); a_exp_ext = m_bv_util.mk_sign_extend(2, a_exp); b_exp_ext = m_bv_util.mk_sign_extend(2, b_exp); expr_ref res_sgn(m), res_sig(m), res_exp(m); expr * signs[2] = { a_sgn, b_sgn }; res_sgn = m_bv_util.mk_bv_xor(2, signs); dbg_decouple("fpa2bv_mul_res_sgn", res_sgn); res_exp = m_bv_util.mk_bv_add( m_bv_util.mk_bv_sub(a_exp_ext, a_lz_ext), m_bv_util.mk_bv_sub(b_exp_ext, b_lz_ext)); expr_ref product(m); product = m_bv_util.mk_bv_mul(a_sig_ext, b_sig_ext); dbg_decouple("fpa2bv_mul_product", product); SASSERT(m_bv_util.get_bv_size(product) == 2*sbits); expr_ref h_p(m), l_p(m), rbits(m); h_p = m_bv_util.mk_extract(2*sbits-1, sbits, product); l_p = m_bv_util.mk_extract(sbits-1, 0, product); if (sbits >= 4) { expr_ref sticky(m); sticky = m.mk_app(m_bv_util.get_fid(), OP_BREDOR, m_bv_util.mk_extract(sbits-4, 0, product)); rbits = m_bv_util.mk_concat(m_bv_util.mk_extract(sbits-1, sbits-3, product), sticky); } else rbits = m_bv_util.mk_concat(l_p, m_bv_util.mk_numeral(0, 4 - sbits)); SASSERT(m_bv_util.get_bv_size(rbits) == 4); res_sig = m_bv_util.mk_concat(h_p, rbits); round(s, rm, res_sgn, res_sig, res_exp, v7); // And finally, we tie them together. mk_ite(c6, v6, v7, result); mk_ite(c5, v5, result, result); mk_ite(c4, v4, result, result); mk_ite(c3, v3, result, result); mk_ite(c2, v2, result, result); mk_ite(c1, v1, result, result); SASSERT(is_well_sorted(m, result)); TRACE("fpa2bv_mul", tout << "MUL = " << mk_ismt2_pp(result, m) << std::endl; ); } void fpa2bv_converter::mk_div(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 3); SASSERT(m_util.is_bv2rm(args[0])); expr_ref rm(m), x(m), y(m); rm = to_app(args[0])->get_arg(0); x = args[1]; y = args[2]; mk_div(f->get_range(), rm, x, y, result); } void fpa2bv_converter::mk_div(sort * s, expr_ref & rm, expr_ref & x, expr_ref & y, expr_ref & result) { expr_ref nan(m), nzero(m), pzero(m), ninf(m), pinf(m); mk_nan(s, nan); mk_nzero(s, nzero); mk_pzero(s, pzero); mk_ninf(s, ninf); mk_pinf(s, pinf); expr_ref x_is_nan(m), x_is_zero(m), x_is_pos(m), x_is_inf(m); expr_ref y_is_nan(m), y_is_zero(m), y_is_pos(m), y_is_inf(m); mk_is_nan(x, x_is_nan); mk_is_zero(x, x_is_zero); mk_is_pos(x, x_is_pos); mk_is_inf(x, x_is_inf); mk_is_nan(y, y_is_nan); mk_is_zero(y, y_is_zero); mk_is_pos(y, y_is_pos); mk_is_inf(y, y_is_inf); dbg_decouple("fpa2bv_div_x_is_nan", x_is_nan); dbg_decouple("fpa2bv_div_x_is_zero", x_is_zero); dbg_decouple("fpa2bv_div_x_is_pos", x_is_pos); dbg_decouple("fpa2bv_div_x_is_inf", x_is_inf); dbg_decouple("fpa2bv_div_y_is_nan", y_is_nan); dbg_decouple("fpa2bv_div_y_is_zero", y_is_zero); dbg_decouple("fpa2bv_div_y_is_pos", y_is_pos); dbg_decouple("fpa2bv_div_y_is_inf", y_is_inf); expr_ref c1(m), c2(m), c3(m), c4(m), c5(m), c6(m), c7(m); expr_ref v1(m), v2(m), v3(m), v4(m), v5(m), v6(m), v7(m), v8(m); // (x is NaN) || (y is NaN) -> NaN m_simp.mk_or(x_is_nan, y_is_nan, c1); v1 = nan; // (x is +oo) -> if (y is oo) then NaN else inf with y's sign. mk_is_pinf(x, c2); expr_ref y_sgn_inf(m); mk_ite(y_is_pos, pinf, ninf, y_sgn_inf); mk_ite(y_is_inf, nan, y_sgn_inf, v2); // (y is +oo) -> if (x is oo) then NaN else 0 with sign x.sgn ^ y.sgn mk_is_pinf(y, c3); expr_ref xy_zero(m), signs_xor(m); m_simp.mk_xor(x_is_pos, y_is_pos, signs_xor); mk_ite(signs_xor, nzero, pzero, xy_zero); mk_ite(x_is_inf, nan, xy_zero, v3); // (x is -oo) -> if (y is oo) then NaN else inf with -y's sign. mk_is_ninf(x, c4); expr_ref neg_y_sgn_inf(m); mk_ite(y_is_pos, ninf, pinf, neg_y_sgn_inf); mk_ite(y_is_inf, nan, neg_y_sgn_inf, v4); // (y is -oo) -> if (x is oo) then NaN else 0 with sign x.sgn ^ y.sgn mk_is_ninf(y, c5); mk_ite(x_is_inf, nan, xy_zero, v5); // (y is 0) -> if (x is 0) then NaN else inf with xor sign. c6 = y_is_zero; expr_ref sgn_inf(m); mk_ite(signs_xor, ninf, pinf, sgn_inf); mk_ite(x_is_zero, nan, sgn_inf, v6); // (x is 0) -> result is zero with sgn = x.sgn^y.sgn // This is a special case to avoid problems with the unpacking of zero. c7 = x_is_zero; mk_ite(signs_xor, nzero, pzero, v7); // else comes the actual division. unsigned ebits = m_util.get_ebits(s); unsigned sbits = m_util.get_sbits(s); SASSERT(ebits <= sbits); expr_ref a_sgn(m), a_sig(m), a_exp(m), a_lz(m), b_sgn(m), b_sig(m), b_exp(m), b_lz(m); unpack(x, a_sgn, a_sig, a_exp, a_lz, true); unpack(y, b_sgn, b_sig, b_exp, b_lz, true); unsigned extra_bits = sbits+2; expr_ref a_sig_ext(m), b_sig_ext(m); a_sig_ext = m_bv_util.mk_concat(a_sig, m_bv_util.mk_numeral(0, sbits + extra_bits)); b_sig_ext = m_bv_util.mk_zero_extend(sbits + extra_bits, b_sig); expr_ref a_exp_ext(m), b_exp_ext(m); a_exp_ext = m_bv_util.mk_sign_extend(2, a_exp); b_exp_ext = m_bv_util.mk_sign_extend(2, b_exp); expr_ref res_sgn(m), res_sig(m), res_exp(m); expr * signs[2] = { a_sgn, b_sgn }; res_sgn = m_bv_util.mk_bv_xor(2, signs); expr_ref a_lz_ext(m), b_lz_ext(m); a_lz_ext = m_bv_util.mk_zero_extend(2, a_lz); b_lz_ext = m_bv_util.mk_zero_extend(2, b_lz); res_exp = m_bv_util.mk_bv_sub( m_bv_util.mk_bv_sub(a_exp_ext, a_lz_ext), m_bv_util.mk_bv_sub(b_exp_ext, b_lz_ext)); expr_ref quotient(m); // b_sig_ext can't be 0 here, so it's safe to use OP_BUDIV_I quotient = m.mk_app(m_bv_util.get_fid(), OP_BUDIV_I, a_sig_ext, b_sig_ext); dbg_decouple("fpa2bv_div_quotient", quotient); SASSERT(m_bv_util.get_bv_size(quotient) == (sbits + sbits + extra_bits)); expr_ref sticky(m); sticky = m.mk_app(m_bv_util.get_fid(), OP_BREDOR, m_bv_util.mk_extract(extra_bits-2, 0, quotient)); res_sig = m_bv_util.mk_concat(m_bv_util.mk_extract(extra_bits+sbits+1, extra_bits-1, quotient), sticky); SASSERT(m_bv_util.get_bv_size(res_sig) == (sbits + 4)); expr_ref res_sig_lz(m); mk_leading_zeros(res_sig, sbits + 4, res_sig_lz); dbg_decouple("fpa2bv_div_res_sig_lz", res_sig_lz); expr_ref res_sig_shift_amount(m); res_sig_shift_amount = m_bv_util.mk_bv_sub(res_sig_lz, m_bv_util.mk_numeral(1, sbits + 4)); dbg_decouple("fpa2bv_div_res_sig_shift_amount", res_sig_shift_amount); expr_ref shift_cond(m); shift_cond = m_bv_util.mk_ule(res_sig_lz, m_bv_util.mk_numeral(1, sbits + 4)); expr_ref res_sig_shifted(m), res_exp_shifted(m); res_sig_shifted = m_bv_util.mk_bv_shl(res_sig, res_sig_shift_amount); res_exp_shifted = m_bv_util.mk_bv_sub(res_exp, m_bv_util.mk_extract(ebits + 1, 0, res_sig_shift_amount)); m_simp.mk_ite(shift_cond, res_sig, res_sig_shifted, res_sig); m_simp.mk_ite(shift_cond, res_exp, res_exp_shifted, res_exp); round(s, rm, res_sgn, res_sig, res_exp, v8); // And finally, we tie them together. mk_ite(c7, v7, v8, result); mk_ite(c6, v6, result, result); mk_ite(c5, v5, result, result); mk_ite(c4, v4, result, result); mk_ite(c3, v3, result, result); mk_ite(c2, v2, result, result); mk_ite(c1, v1, result, result); SASSERT(is_well_sorted(m, result)); TRACE("fpa2bv_div", tout << "DIV = " << mk_ismt2_pp(result, m) << std::endl; ); } void fpa2bv_converter::mk_rem(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 2); expr_ref x(m), y(m); x = args[0]; y = args[1]; mk_rem(f->get_range(), x, y, result); } void fpa2bv_converter::mk_rem(sort * s, expr_ref & x, expr_ref & y, expr_ref & result) { TRACE("fpa2bv_rem", tout << "X = " << mk_ismt2_pp(x, m) << std::endl; tout << "Y = " << mk_ismt2_pp(y, m) << std::endl;); expr_ref nan(m), nzero(m), pzero(m), ninf(m), pinf(m); mk_nan(s, nan); mk_nzero(s, nzero); mk_pzero(s, pzero); mk_ninf(s, ninf); mk_pinf(s, pinf); expr_ref x_is_nan(m), x_is_zero(m), x_is_pos(m), x_is_neg(m), x_is_inf(m); expr_ref y_is_nan(m), y_is_zero(m), y_is_pos(m), y_is_neg(m), y_is_inf(m); mk_is_nan(x, x_is_nan); mk_is_zero(x, x_is_zero); mk_is_pos(x, x_is_pos); mk_is_neg(x, x_is_neg); mk_is_inf(x, x_is_inf); mk_is_nan(y, y_is_nan); mk_is_zero(y, y_is_zero); mk_is_pos(y, y_is_pos); mk_is_neg(y, y_is_neg); mk_is_inf(y, y_is_inf); dbg_decouple("fpa2bv_rem_x_is_nan", x_is_nan); dbg_decouple("fpa2bv_rem_x_is_zero", x_is_zero); dbg_decouple("fpa2bv_rem_x_is_pos", x_is_pos); dbg_decouple("fpa2bv_rem_x_is_inf", x_is_inf); dbg_decouple("fpa2bv_rem_y_is_nan", y_is_nan); dbg_decouple("fpa2bv_rem_y_is_zero", y_is_zero); dbg_decouple("fpa2bv_rem_y_is_pos", y_is_pos); dbg_decouple("fpa2bv_rem_y_is_inf", y_is_inf); expr_ref c1(m), c2(m), c3(m), c4(m), c5(m), c6(m); expr_ref v1(m), v2(m), v3(m), v4(m), v5(m), v6(m), v7(m); // (x is NaN) || (y is NaN) -> NaN m_simp.mk_or(x_is_nan, y_is_nan, c1); v1 = nan; // (x is +-oo) -> NaN c2 = x_is_inf; v2 = nan; // (y is +-oo) -> x c3 = y_is_inf; v3 = x; // (y is 0) -> NaN. c4 = y_is_zero; v4 = nan; // (x is 0) -> x c5 = x_is_zero; v5 = pzero; // exp(x) < exp(y) -> x unsigned ebits = m_util.get_ebits(s); unsigned sbits = m_util.get_sbits(s); expr_ref x_sgn(m), x_sig(m), x_exp(m); expr_ref y_sgn(m), y_sig(m), y_exp(m); split_fp(x, x_sgn, x_exp, x_sig); split_fp(y, y_sgn, y_exp, y_sig); expr_ref one_ebits(m), y_exp_m1(m), xe_lt_yem1(m), ye_neq_zero(m); one_ebits = m_bv_util.mk_numeral(1, ebits); y_exp_m1 = m_bv_util.mk_bv_sub(y_exp, one_ebits); BVULT(x_exp, y_exp_m1, xe_lt_yem1); ye_neq_zero = m.mk_not(m.mk_eq(y_exp, m_bv_util.mk_numeral(0, ebits))); c6 = m.mk_and(ye_neq_zero, xe_lt_yem1); v6 = x; expr_ref a_sgn(m), a_sig(m), a_exp(m), a_lz(m); expr_ref b_sgn(m), b_sig(m), b_exp(m), b_lz(m); unpack(x, a_sgn, a_sig, a_exp, a_lz, true); unpack(y, b_sgn, b_sig, b_exp, b_lz, true); dbg_decouple("fpa2bv_rem_a_sgn", a_sgn); dbg_decouple("fpa2bv_rem_a_sig", a_sig); dbg_decouple("fpa2bv_rem_a_exp", a_exp); dbg_decouple("fpa2bv_rem_a_lz", a_lz); dbg_decouple("fpa2bv_rem_b_sgn", b_sgn); dbg_decouple("fpa2bv_rem_b_sig", b_sig); dbg_decouple("fpa2bv_rem_b_exp", b_exp); dbg_decouple("fpa2bv_rem_b_lz", b_lz); // else the actual remainder. // max. exponent difference is (2^ebits) - 3 const mpz & two_to_ebits = fu().fm().m_powers2(ebits); mpz max_exp_diff; m_mpz_manager.sub(two_to_ebits, 3, max_exp_diff); SASSERT(m_mpz_manager.is_int64(max_exp_diff)); SASSERT(m_mpz_manager.get_uint64(max_exp_diff) <= UINT_MAX); uint64_t max_exp_diff_ui64 = m_mpz_manager.get_uint64(max_exp_diff); SASSERT(max_exp_diff_ui64 <= UINT_MAX); unsigned max_exp_diff_ui = (unsigned)max_exp_diff_ui64; m_mpz_manager.del(max_exp_diff); expr_ref a_exp_ext(m), b_exp_ext(m); a_exp_ext = m_bv_util.mk_sign_extend(2, a_exp); b_exp_ext = m_bv_util.mk_sign_extend(2, b_exp); expr_ref a_lz_ext(m), b_lz_ext(m); a_lz_ext = m_bv_util.mk_zero_extend(2, a_lz); b_lz_ext = m_bv_util.mk_zero_extend(2, b_lz); expr_ref exp_diff(m), neg_exp_diff(m), exp_diff_is_neg(m), exp_diff_ge_zero(m), exp_diff_is_zero(m); exp_diff = m_bv_util.mk_bv_sub( m_bv_util.mk_bv_sub(a_exp_ext, a_lz_ext), m_bv_util.mk_bv_sub(b_exp_ext, b_lz_ext)); neg_exp_diff = m_bv_util.mk_bv_neg(exp_diff); exp_diff_is_neg = m_bv_util.mk_sle(exp_diff, m_bv_util.mk_numeral(0, ebits+2)); exp_diff_is_zero = m.mk_eq(exp_diff, m_bv_util.mk_numeral(0, ebits+2)); exp_diff_ge_zero = m_bv_util.mk_sle(m_bv_util.mk_numeral(0, m_bv_util.get_bv_size(exp_diff)), exp_diff); dbg_decouple("fpa2bv_rem_exp_diff", exp_diff); // CMW: This creates _huge_ bit-vectors, which is potentially sub-optimal, // but calculating this via rem = x - y * nearest(x/y) creates huge // circuits, too. Lazy instantiation seems the way to go in the long run // (using the lazy bit-blaster helps on simple instances). expr_ref a_sig_ext(m), b_sig_ext(m), lshift(m), rshift(m), shifted(m), huge_rem(m), huge_div(m), huge_div_is_even(m); a_sig_ext = m_bv_util.mk_concat(m_bv_util.mk_zero_extend(max_exp_diff_ui, a_sig), m_bv_util.mk_numeral(0, 3)); b_sig_ext = m_bv_util.mk_concat(m_bv_util.mk_zero_extend(max_exp_diff_ui, b_sig), m_bv_util.mk_numeral(0, 3)); lshift = m_bv_util.mk_zero_extend(max_exp_diff_ui + sbits - (ebits+2) + 3, exp_diff); rshift = m_bv_util.mk_zero_extend(max_exp_diff_ui + sbits - (ebits+2) + 3, neg_exp_diff); shifted = m.mk_ite(exp_diff_is_neg, m_bv_util.mk_bv_ashr(a_sig_ext, rshift), m_bv_util.mk_bv_shl(a_sig_ext, lshift)); huge_rem = m_bv_util.mk_bv_urem(shifted, b_sig_ext); huge_div = m.mk_app(m_bv_util.get_fid(), OP_BUDIV_I, shifted, b_sig_ext); huge_div_is_even = m.mk_eq(m_bv_util.mk_extract(0, 0, huge_div), m_bv_util.mk_numeral(0, 1)); dbg_decouple("fpa2bv_rem_exp_diff_is_neg", exp_diff_is_neg); dbg_decouple("fpa2bv_rem_lshift", lshift); dbg_decouple("fpa2bv_rem_rshift", rshift); dbg_decouple("fpa2bv_rem_huge_rem", huge_rem); dbg_decouple("fpa2bv_rem_huge_div", huge_div); expr_ref rndd_sgn(m), rndd_exp(m), rndd_sig(m), rne_bv(m), rndd_sig_lz(m); rndd_sgn = a_sgn; rndd_exp = m_bv_util.mk_bv_sub(b_exp_ext, b_lz_ext); rndd_sig = m_bv_util.mk_extract(sbits+3, 0, huge_rem); rne_bv = m_bv_util.mk_numeral(BV_RM_TIES_TO_EVEN, 3); mk_leading_zeros(rndd_sig, ebits+2, rndd_sig_lz); dbg_decouple("fpa2bv_rem_rndd_sig", rndd_sig); dbg_decouple("fpa2bv_rem_rndd_sig_lz", rndd_sig_lz); SASSERT(m_bv_util.get_bv_size(rndd_exp) == ebits+2); SASSERT(m_bv_util.get_bv_size(y_exp_m1) == ebits); expr_ref rndd_exp_eq_y_exp(m), rndd_exp_eq_y_exp_m1(m), y_sig_le_rndd_sig(m); rndd_exp_eq_y_exp = m.mk_eq(rndd_sig_lz, m_bv_util.mk_numeral(1, ebits+2)); rndd_exp_eq_y_exp_m1 = m.mk_eq(rndd_sig_lz, m_bv_util.mk_numeral(2, ebits+2)); dbg_decouple("fpa2bv_rem_rndd_exp_eq_y_exp_m1", rndd_exp_eq_y_exp_m1); expr_ref y_sig_ext(m), y_sig_eq_rndd_sig(m); y_sig_ext = m_bv_util.mk_concat(m_bv_util.mk_zero_extend(2, b_sig), m_bv_util.mk_numeral(0, 2)); y_sig_le_rndd_sig = m_bv_util.mk_sle(y_sig_ext, rndd_sig); y_sig_eq_rndd_sig = m.mk_eq(y_sig_ext, rndd_sig); dbg_decouple("fpa2bv_rem_y_sig_ext", y_sig_ext); dbg_decouple("fpa2bv_rem_y_sig_le_rndd_sig", y_sig_le_rndd_sig); dbg_decouple("fpa2bv_rem_y_sig_eq_rndd_sig", y_sig_eq_rndd_sig); expr_ref sub_cnd(m); sub_cnd = m.mk_or(m.mk_and(rndd_exp_eq_y_exp, y_sig_le_rndd_sig), m.mk_and(rndd_exp_eq_y_exp_m1, y_sig_le_rndd_sig, m.mk_not(y_sig_eq_rndd_sig)), m.mk_and(rndd_exp_eq_y_exp_m1, y_sig_eq_rndd_sig, m.mk_not(huge_div_is_even))); dbg_decouple("fpa2bv_rem_sub_cnd", sub_cnd); expr_ref rndd(m), rounded_sub_y(m), rounded_add_y(m); round(s, rne_bv, rndd_sgn, rndd_sig, rndd_exp, rndd); mk_sub(s, rne_bv, rndd, y, rounded_sub_y); mk_ite(sub_cnd, rounded_sub_y, rndd, v7); // And finally, we tie them together. mk_ite(c6, v6, v7, result); mk_ite(c5, v5, result, result); mk_ite(c4, v4, result, result); mk_ite(c3, v3, result, result); mk_ite(c2, v2, result, result); mk_ite(c1, v1, result, result); expr_ref result_is_zero(m), zeros(m); mk_is_zero(result, result_is_zero); mk_ite(x_is_pos, pzero, nzero, zeros); mk_ite(result_is_zero, zeros, result, result); SASSERT(is_well_sorted(m, result)); TRACE("fpa2bv_rem", tout << "REM = " << mk_ismt2_pp(result, m) << std::endl; ); } void fpa2bv_converter::mk_abs(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 1); expr_ref x(m); x = args[0]; mk_abs(f->get_range(), x, result); } void fpa2bv_converter::mk_abs(sort * s, expr_ref & x, expr_ref & result) { expr_ref sgn(m), sig(m), exp(m); split_fp(x, sgn, exp, sig); result = m_util.mk_fp(m_bv_util.mk_numeral(0, 1), exp, sig); } void fpa2bv_converter::mk_min(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 2); expr * x = args[0], * y = args[1]; expr_ref x_sgn(m), x_sig(m), x_exp(m); expr_ref y_sgn(m), y_sig(m), y_exp(m); split_fp(x, x_sgn, x_exp, x_sig); split_fp(y, y_sgn, y_exp, y_sig); expr_ref bv0(m), bv1(m); bv0 = m_bv_util.mk_numeral(0, 1); bv1 = m_bv_util.mk_numeral(1, 1); expr_ref x_is_nan(m), y_is_nan(m), x_is_zero(m), y_is_zero(m), xy_are_zero(m); mk_is_nan(x, x_is_nan); mk_is_nan(y, y_is_nan); mk_is_zero(x, x_is_zero); mk_is_zero(y, y_is_zero); xy_are_zero = m.mk_and(x_is_zero, y_is_zero); expr_ref x_is_pos(m), x_is_neg(m); expr_ref y_is_pos(m), y_is_neg(m); expr_ref pn(m), np(m), pn_or_np_zeros(m); mk_is_pos(x, x_is_pos); mk_is_pos(y, y_is_pos); mk_is_neg(x, x_is_neg); mk_is_neg(y, y_is_neg); pn_or_np_zeros = m.mk_and(xy_are_zero, m.mk_not(m.mk_eq(x_sgn, y_sgn))); expr_ref unspec(m); unspec = mk_min_max_unspecified(f, x, y); expr_ref x_lt_y(m); mk_float_lt(f, num, args, x_lt_y); mk_ite(x_lt_y, x, y, result); mk_ite(xy_are_zero, y, result, result); mk_ite(pn_or_np_zeros, unspec, result, result); mk_ite(y_is_nan, x, result, result); mk_ite(x_is_nan, y, result, result); SASSERT(is_well_sorted(m, result)); } void fpa2bv_converter::mk_max(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 2); expr * x = args[0], *y = args[1]; expr_ref x_sgn(m), x_sig(m), x_exp(m); expr_ref y_sgn(m), y_sig(m), y_exp(m); split_fp(x, x_sgn, x_exp, x_sig); split_fp(y, y_sgn, y_exp, y_sig); expr_ref x_is_nan(m), y_is_nan(m), x_is_zero(m), y_is_zero(m), xy_are_zero(m); mk_is_nan(x, x_is_nan); mk_is_nan(y, y_is_nan); mk_is_zero(x, x_is_zero); mk_is_zero(y, y_is_zero); xy_are_zero = m.mk_and(x_is_zero, y_is_zero); expr_ref x_is_pos(m), x_is_neg(m); expr_ref y_is_pos(m), y_is_neg(m); expr_ref pn(m), np(m), pn_or_np_zeros(m); mk_is_pos(x, x_is_pos); mk_is_pos(y, y_is_pos); mk_is_neg(x, x_is_neg); mk_is_neg(y, y_is_neg); pn_or_np_zeros = m.mk_and(xy_are_zero, m.mk_not(m.mk_eq(x_sgn, y_sgn))); expr_ref unspec(m); unspec = mk_min_max_unspecified(f, x, y); expr_ref x_gt_y(m); mk_float_gt(f, num, args, x_gt_y); mk_ite(x_gt_y, x, y, result); mk_ite(xy_are_zero, y, result, result); mk_ite(pn_or_np_zeros, unspec, result, result); mk_ite(y_is_nan, x, result, result); mk_ite(x_is_nan, y, result, result); SASSERT(is_well_sorted(m, result)); } expr_ref fpa2bv_converter::mk_min_max_unspecified(func_decl * f, expr * x, expr * y) { unsigned ebits = m_util.get_ebits(f->get_range()); unsigned sbits = m_util.get_sbits(f->get_range()); expr_ref res(m); // The only cases in which min/max is unspecified for is when the arguments are +0.0 and -0.0. // There is no "hardware interpretation" for fp.min/fp.max. std::pair decls(0, 0); if (!m_min_max_ufs.find(f, decls)) { decls.first = m.mk_fresh_const(nullptr, m_bv_util.mk_sort(1)); decls.second = m.mk_fresh_const(nullptr, m_bv_util.mk_sort(1)); m_min_max_ufs.insert(f, decls); m.inc_ref(f); m.inc_ref(decls.first); m.inc_ref(decls.second); } expr_ref pn(m), np(m); pn = m_util.mk_fp(decls.first, m_bv_util.mk_numeral(0, ebits), m_bv_util.mk_numeral(0, sbits - 1)); np = m_util.mk_fp(decls.second, m_bv_util.mk_numeral(0, ebits), m_bv_util.mk_numeral(0, sbits - 1)); expr_ref x_is_pzero(m), y_is_nzero(m), xyzero(m); mk_is_pzero(x, x_is_pzero); mk_is_nzero(y, y_is_nzero); m_simp.mk_and(x_is_pzero, y_is_nzero, xyzero); mk_ite(xyzero, pn, np, res); return res; } void fpa2bv_converter::mk_fma(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 4); SASSERT(m_util.is_bv2rm(args[0])); // fusedma means (x * y) + z expr_ref rm(m), x(m), y(m), z(m); rm = to_app(args[0])->get_arg(0); x = args[1]; y = args[2]; z = args[3]; expr_ref nan(m), nzero(m), pzero(m), ninf(m), pinf(m); mk_nan(f, nan); mk_nzero(f, nzero); mk_pzero(f, pzero); mk_ninf(f, ninf); mk_pinf(f, pinf); expr_ref x_is_nan(m), x_is_zero(m), x_is_pos(m), x_is_neg(m), x_is_inf(m); expr_ref y_is_nan(m), y_is_zero(m), y_is_pos(m), y_is_neg(m), y_is_inf(m); expr_ref z_is_nan(m), z_is_zero(m), z_is_pos(m), z_is_neg(m), z_is_inf(m); mk_is_nan(x, x_is_nan); mk_is_zero(x, x_is_zero); mk_is_pos(x, x_is_pos); mk_is_neg(x, x_is_neg); mk_is_inf(x, x_is_inf); mk_is_nan(y, y_is_nan); mk_is_zero(y, y_is_zero); mk_is_pos(y, y_is_pos); mk_is_neg(y, y_is_neg); mk_is_inf(y, y_is_inf); mk_is_nan(z, z_is_nan); mk_is_zero(z, z_is_zero); mk_is_pos(z, z_is_pos); mk_is_neg(z, z_is_neg); mk_is_inf(z, z_is_inf); expr_ref rm_is_to_neg(m); mk_is_rm(rm, BV_RM_TO_NEGATIVE, rm_is_to_neg); dbg_decouple("fpa2bv_fma_x_is_nan", x_is_nan); dbg_decouple("fpa2bv_fma_x_is_zero", x_is_zero); dbg_decouple("fpa2bv_fma_x_is_pos", x_is_pos); dbg_decouple("fpa2bv_fma_x_is_inf", x_is_inf); dbg_decouple("fpa2bv_fma_y_is_nan", y_is_nan); dbg_decouple("fpa2bv_fma_y_is_zero", y_is_zero); dbg_decouple("fpa2bv_fma_y_is_pos", y_is_pos); dbg_decouple("fpa2bv_fma_y_is_inf", y_is_inf); dbg_decouple("fpa2bv_fma_z_is_nan", z_is_nan); dbg_decouple("fpa2bv_fma_z_is_zero", z_is_zero); dbg_decouple("fpa2bv_fma_z_is_pos", z_is_pos); dbg_decouple("fpa2bv_fma_z_is_inf", z_is_inf); expr_ref c1(m), c2(m), c3(m), c4(m), c5(m), c6(m), c7(m); expr_ref v1(m), v2(m), v3(m), v4(m), v5(m), v6(m), v7(m), v8(m); expr_ref inf_xor(m), inf_cond(m); m_simp.mk_xor(x_is_neg, y_is_neg, inf_xor); m_simp.mk_xor(inf_xor, z_is_neg, inf_xor); m_simp.mk_and(z_is_inf, inf_xor, inf_cond); // (x is NaN) || (y is NaN) || (z is Nan) -> NaN m_simp.mk_or(x_is_nan, y_is_nan, z_is_nan, c1); v1 = nan; // (x is +oo) -> if (y is 0) then NaN else inf with y's sign. mk_is_pinf(x, c2); expr_ref y_sgn_inf(m), inf_or(m); mk_ite(y_is_pos, pinf, ninf, y_sgn_inf); m_simp.mk_or(y_is_zero, inf_cond, inf_or); mk_ite(inf_or, nan, y_sgn_inf, v2); // (y is +oo) -> if (x is 0) then NaN else inf with x's sign. mk_is_pinf(y, c3); expr_ref x_sgn_inf(m); mk_ite(x_is_pos, pinf, ninf, x_sgn_inf); m_simp.mk_or(x_is_zero, inf_cond, inf_or); mk_ite(inf_or, nan, x_sgn_inf, v3); // (x is -oo) -> if (y is 0) then NaN else inf with -y's sign. mk_is_ninf(x, c4); expr_ref neg_y_sgn_inf(m); mk_ite(y_is_pos, ninf, pinf, neg_y_sgn_inf); m_simp.mk_or(y_is_zero, inf_cond, inf_or); mk_ite(inf_or, nan, neg_y_sgn_inf, v4); // (y is -oo) -> if (x is 0) then NaN else inf with -x's sign. mk_is_ninf(y, c5); expr_ref neg_x_sgn_inf(m); mk_ite(x_is_pos, ninf, pinf, neg_x_sgn_inf); m_simp.mk_or(x_is_zero, inf_cond, inf_or); mk_ite(inf_or, nan, neg_x_sgn_inf, v5); // z is +-INF -> z. mk_is_inf(z, c6); v6 = z; // (x is 0) || (y is 0) -> z expr_ref c71(m), xy_sgn(m), xyz_sgn(m); m_simp.mk_or(x_is_zero, y_is_zero, c7); m_simp.mk_xor(x_is_neg, y_is_neg, xy_sgn); m_simp.mk_xor(xy_sgn, z_is_neg, xyz_sgn); m_simp.mk_and(z_is_zero, xyz_sgn, c71); expr_ref zero_cond(m), rm_is_not_to_neg(m); rm_is_not_to_neg = m.mk_not(rm_is_to_neg); m_simp.mk_ite(rm_is_to_neg, nzero, pzero, zero_cond); mk_ite(c71, zero_cond, z, v7); // else comes the fused multiplication. expr_ref one_1(m), zero_1(m); one_1 = m_bv_util.mk_numeral(1, 1); zero_1 = m_bv_util.mk_numeral(0, 1); unsigned ebits = m_util.get_ebits(f->get_range()); unsigned sbits = m_util.get_sbits(f->get_range()); expr_ref a_sgn(m), a_sig(m), a_exp(m), a_lz(m); expr_ref b_sgn(m), b_sig(m), b_exp(m), b_lz(m); expr_ref c_sgn(m), c_sig(m), c_exp(m), c_lz(m); unpack(x, a_sgn, a_sig, a_exp, a_lz, true); unpack(y, b_sgn, b_sig, b_exp, b_lz, true); unpack(z, c_sgn, c_sig, c_exp, c_lz, true); expr_ref a_lz_ext(m), b_lz_ext(m), c_lz_ext(m); a_lz_ext = m_bv_util.mk_zero_extend(2, a_lz); b_lz_ext = m_bv_util.mk_zero_extend(2, b_lz); c_lz_ext = m_bv_util.mk_zero_extend(2, c_lz); expr_ref a_sig_ext(m), b_sig_ext(m); a_sig_ext = m_bv_util.mk_zero_extend(sbits, a_sig); b_sig_ext = m_bv_util.mk_zero_extend(sbits, b_sig); expr_ref a_exp_ext(m), b_exp_ext(m), c_exp_ext(m); a_exp_ext = m_bv_util.mk_sign_extend(2, a_exp); b_exp_ext = m_bv_util.mk_sign_extend(2, b_exp); c_exp_ext = m_bv_util.mk_sign_extend(2, c_exp); dbg_decouple("fpa2bv_fma_a_sig", a_sig_ext); dbg_decouple("fpa2bv_fma_b_sig", b_sig_ext); dbg_decouple("fpa2bv_fma_c_sig", c_sig); dbg_decouple("fpa2bv_fma_a_exp", a_exp_ext); dbg_decouple("fpa2bv_fma_b_exp", b_exp_ext); dbg_decouple("fpa2bv_fma_c_exp", c_exp_ext); dbg_decouple("fpa2bv_fma_a_lz", a_lz_ext); dbg_decouple("fpa2bv_fma_b_lz", b_lz_ext); dbg_decouple("fpa2bv_fma_c_lz", c_lz_ext); expr_ref mul_sgn(m), mul_sig(m), mul_exp(m); expr * signs[2] = { a_sgn, b_sgn }; mul_sgn = m_bv_util.mk_bv_xor(2, signs); dbg_decouple("fpa2bv_fma_mul_sgn", mul_sgn); mul_exp = m_bv_util.mk_bv_add( m_bv_util.mk_bv_sub(a_exp_ext, a_lz_ext), m_bv_util.mk_bv_sub(b_exp_ext, b_lz_ext)); dbg_decouple("fpa2bv_fma_mul_exp", mul_exp); mul_sig = m_bv_util.mk_bv_mul(a_sig_ext, b_sig_ext); dbg_decouple("fpa2bv_fma_mul_sig", mul_sig); SASSERT(m_bv_util.get_bv_size(mul_sig) == 2*sbits); SASSERT(m_bv_util.get_bv_size(mul_exp) == ebits + 2); // The product has the form [-1][0].[2*sbits - 2]. // Extend c expr_ref c_sig_ext(m); c_sig_ext = m_bv_util.mk_zero_extend(1, m_bv_util.mk_concat(c_sig, m_bv_util.mk_numeral(0, sbits+2))); c_exp_ext = m_bv_util.mk_bv_sub(c_exp_ext, c_lz_ext); mul_sig = m_bv_util.mk_concat(mul_sig, m_bv_util.mk_numeral(0, 3)); SASSERT(m_bv_util.get_bv_size(mul_sig) == 2*sbits+3); SASSERT(m_bv_util.get_bv_size(c_sig_ext) == 2*sbits+3); dbg_decouple("fpa2bv_fma_c_sig_ext", c_sig_ext); dbg_decouple("fpa2bv_fma_c_exp_ext", c_exp_ext); expr_ref swap_cond(m); swap_cond = m_bv_util.mk_sle(mul_exp, c_exp_ext); SASSERT(is_well_sorted(m, swap_cond)); dbg_decouple("fpa2bv_fma_swap_cond", swap_cond); expr_ref e_sgn(m), e_sig(m), e_exp(m), f_sgn(m), f_sig(m), f_exp(m); m_simp.mk_ite(swap_cond, c_sgn, mul_sgn, e_sgn); m_simp.mk_ite(swap_cond, c_sig_ext, mul_sig, e_sig); // has 2 * sbits + 3 m_simp.mk_ite(swap_cond, c_exp_ext, mul_exp, e_exp); // has ebits + 2 m_simp.mk_ite(swap_cond, mul_sgn, c_sgn, f_sgn); m_simp.mk_ite(swap_cond, mul_sig, c_sig_ext, f_sig); // has 2 * sbits + 3 m_simp.mk_ite(swap_cond, mul_exp, c_exp_ext, f_exp); // has ebits + 2 SASSERT(is_well_sorted(m, e_sgn)); SASSERT(is_well_sorted(m, e_sig)); SASSERT(is_well_sorted(m, e_exp)); SASSERT(is_well_sorted(m, f_sgn)); SASSERT(is_well_sorted(m, f_sig)); SASSERT(is_well_sorted(m, f_exp)); SASSERT(m_bv_util.get_bv_size(e_sig) == 2*sbits+3); SASSERT(m_bv_util.get_bv_size(f_sig) == 2*sbits+3); SASSERT(m_bv_util.get_bv_size(e_exp) == ebits + 2); SASSERT(m_bv_util.get_bv_size(f_exp) == ebits + 2); dbg_decouple("fpa2bv_fma_e_sig", e_sig); dbg_decouple("fpa2bv_fma_e_exp", e_exp); dbg_decouple("fpa2bv_fma_f_sig", f_sig); dbg_decouple("fpa2bv_fma_f_exp", f_exp); expr_ref res_sgn(m), res_sig(m), res_exp(m); expr_ref exp_delta(m); exp_delta = m_bv_util.mk_bv_sub(e_exp, f_exp); dbg_decouple("fpa2bv_fma_add_exp_delta", exp_delta); // cap the delta expr_ref cap(m), cap_le_delta(m); cap = m_bv_util.mk_numeral(2*sbits+3, ebits+2); cap_le_delta = m_bv_util.mk_ule(cap, exp_delta); m_simp.mk_ite(cap_le_delta, cap, exp_delta, exp_delta); SASSERT(m_bv_util.get_bv_size(exp_delta) == ebits+2); SASSERT(is_well_sorted(m, exp_delta)); dbg_decouple("fpa2bv_fma_add_exp_delta_capped", exp_delta); // Alignment shift with sticky bit computation. expr_ref shifted_big(m), shifted_f_sig(m); expr_ref alignment_sticky_raw(m), alignment_sticky(m); shifted_big = m_bv_util.mk_bv_lshr( m_bv_util.mk_concat(f_sig, m_bv_util.mk_numeral(0, sbits)), m_bv_util.mk_zero_extend((3*sbits+3)-(ebits+2), exp_delta)); shifted_f_sig = m_bv_util.mk_extract(3*sbits+2, sbits, shifted_big); alignment_sticky_raw = m_bv_util.mk_extract(sbits-1, 0, shifted_big); alignment_sticky = m.mk_app(m_bv_util.get_fid(), OP_BREDOR, alignment_sticky_raw.get()); dbg_decouple("fpa2bv_fma_shifted_f_sig", shifted_f_sig); dbg_decouple("fpa2bv_fma_f_sig_alignment_sticky", alignment_sticky); SASSERT(is_well_sorted(m, alignment_sticky)); SASSERT(m_bv_util.get_bv_size(shifted_f_sig) == 2*sbits+3); SASSERT(is_well_sorted(m, shifted_f_sig)); // Significant addition. // Two extra bits for the sign and for catching overflows. e_sig = m_bv_util.mk_zero_extend(2, e_sig); shifted_f_sig = m_bv_util.mk_zero_extend(2, shifted_f_sig); expr_ref eq_sgn(m); m_simp.mk_eq(e_sgn, f_sgn, eq_sgn); dbg_decouple("fpa2bv_fma_eq_sgn", eq_sgn); SASSERT(m_bv_util.get_bv_size(e_sig) == 2*sbits+5); SASSERT(m_bv_util.get_bv_size(shifted_f_sig) == 2*sbits+5); dbg_decouple("fpa2bv_fma_add_e_sig", e_sig); dbg_decouple("fpa2bv_fma_add_shifted_f_sig", shifted_f_sig); expr_ref sum(m), e_plus_f(m), e_minus_f(m), sticky_wide(m); sticky_wide = m_bv_util.mk_zero_extend(2*sbits+4, alignment_sticky); e_plus_f = m_bv_util.mk_bv_add(e_sig, shifted_f_sig); e_plus_f = m.mk_ite(m.mk_eq(m_bv_util.mk_extract(0, 0, e_plus_f), zero_1), m_bv_util.mk_bv_add(e_plus_f, sticky_wide), e_plus_f); e_minus_f = m_bv_util.mk_bv_sub(e_sig, shifted_f_sig); e_minus_f = m.mk_ite(m.mk_eq(m_bv_util.mk_extract(0, 0, e_minus_f), zero_1), m_bv_util.mk_bv_sub(e_minus_f, sticky_wide), e_minus_f); SASSERT(is_well_sorted(m, shifted_f_sig)); dbg_decouple("fpa2bv_fma_f_sig_or_sticky", shifted_f_sig); m_simp.mk_ite(eq_sgn, e_plus_f, e_minus_f, sum); SASSERT(m_bv_util.get_bv_size(sum) == 2*sbits+5); SASSERT(is_well_sorted(m, sum)); dbg_decouple("fpa2bv_fma_add_sum", sum); expr_ref sign_bv(m), n_sum(m); sign_bv = m_bv_util.mk_extract(2*sbits+4, 2*sbits+4, sum); n_sum = m_bv_util.mk_bv_neg(sum); dbg_decouple("fpa2bv_fma_add_sign_bv", sign_bv); dbg_decouple("fpa2bv_fma_add_n_sum", n_sum); expr_ref res_sig_eq(m), sig_abs(m); m_simp.mk_eq(sign_bv, one_1, res_sig_eq); m_simp.mk_ite(res_sig_eq, n_sum, sum, sig_abs); dbg_decouple("fpa2bv_fma_add_sig_abs", sig_abs); family_id bvfid = m_bv_util.get_fid(); expr_ref res_sgn_c1(m), res_sgn_c2(m), res_sgn_c3(m); expr_ref not_e_sgn(m), not_f_sgn(m), not_sign_bv(m); not_e_sgn = m_bv_util.mk_bv_not(e_sgn); not_f_sgn = m_bv_util.mk_bv_not(f_sgn); not_sign_bv = m_bv_util.mk_bv_not(sign_bv); res_sgn_c1 = m.mk_app(bvfid, OP_BAND, not_e_sgn, f_sgn, sign_bv); res_sgn_c2 = m.mk_app(bvfid, OP_BAND, e_sgn, not_f_sgn, not_sign_bv); res_sgn_c3 = m.mk_app(bvfid, OP_BAND, e_sgn, f_sgn); expr * res_sgn_or_args[3] = { res_sgn_c1, res_sgn_c2, res_sgn_c3 }; res_sgn = m_bv_util.mk_bv_or(3, res_sgn_or_args); dbg_decouple("fpa2bv_fma_res_sgn", res_sgn); expr_ref is_sig_neg(m); is_sig_neg = m.mk_eq(one_1, m_bv_util.mk_extract(2*sbits+4, 2*sbits+4, sig_abs)); sig_abs = m.mk_ite(is_sig_neg, m_bv_util.mk_bv_neg(sig_abs), sig_abs); dbg_decouple("fpa2bv_fma_is_sig_neg", is_sig_neg); // Result could have overflown into 4.xxx. SASSERT(m_bv_util.get_bv_size(sig_abs) == 2*sbits+5); expr_ref extra(m), extra_is_zero(m); extra = m_bv_util.mk_extract(2*sbits+4, 2*sbits+3, sig_abs); extra_is_zero = m.mk_eq(extra, m_bv_util.mk_numeral(0, 2)); dbg_decouple("fpa2bv_fma_extra", extra); res_exp = m.mk_ite(extra_is_zero, e_exp, m_bv_util.mk_bv_add(e_exp, m_bv_util.mk_numeral(1, ebits + 2))); // Renormalize expr_ref zero_e2(m), min_exp(m), sig_lz(m), max_exp_delta(m), sig_lz_capped(m), renorm_delta(m); zero_e2 = m_bv_util.mk_numeral(0, ebits + 2); mk_min_exp(ebits, min_exp); min_exp = m_bv_util.mk_sign_extend(2, min_exp); mk_leading_zeros(sig_abs, ebits+2, sig_lz); sig_lz = m_bv_util.mk_bv_sub(sig_lz, m_bv_util.mk_numeral(2, ebits+2)); max_exp_delta = m_bv_util.mk_bv_sub(res_exp, min_exp); sig_lz_capped = m.mk_ite(m_bv_util.mk_sle(sig_lz, max_exp_delta), sig_lz, max_exp_delta); renorm_delta = m.mk_ite(m_bv_util.mk_sle(zero_e2, sig_lz_capped), sig_lz_capped, zero_e2); res_exp = m_bv_util.mk_bv_sub(res_exp, renorm_delta); sig_abs = m_bv_util.mk_bv_shl(sig_abs, m_bv_util.mk_zero_extend(2*sbits+3-ebits, renorm_delta)); dbg_decouple("fpa2bv_fma_min_exp", min_exp); dbg_decouple("fpa2bv_fma_max_exp_delta", max_exp_delta); dbg_decouple("fpa2bv_fma_sig_lz", sig_lz); dbg_decouple("fpa2bv_fma_sig_lz_capped", sig_lz_capped); dbg_decouple("fpa2bv_fma_renorm_delta", renorm_delta); unsigned too_short = 0; if (sbits < 5) { too_short = 6 - sbits + 1; sig_abs = m_bv_util.mk_concat(sig_abs, m_bv_util.mk_numeral(0, too_short)); } expr_ref sig_abs_h1(m), sticky_h1(m), sticky_h1_red(m), sig_abs_h1_f(m), res_sig_1(m); sticky_h1 = m_bv_util.mk_extract(sbits+too_short-2, 0, sig_abs); sig_abs_h1 = m_bv_util.mk_extract(2*sbits+too_short+4, sbits-1+too_short, sig_abs); sticky_h1_red = m_bv_util.mk_zero_extend(sbits+5, m.mk_app(m_bv_util.get_fid(), OP_BREDOR, sticky_h1.get())); expr * sticky_h1_red_args[2] = { sig_abs_h1, sticky_h1_red }; sig_abs_h1_f = m_bv_util.mk_bv_or(2, sticky_h1_red_args); res_sig_1 = m_bv_util.mk_extract(sbits+3, 0, sig_abs_h1_f); SASSERT(m_bv_util.get_bv_size(sticky_h1) == sbits+too_short-1); SASSERT(m_bv_util.get_bv_size(sig_abs_h1) == sbits+6); SASSERT(m_bv_util.get_bv_size(sticky_h1_red) == sbits+6); SASSERT(m_bv_util.get_bv_size(sig_abs_h1_f) == sbits+6); SASSERT(m_bv_util.get_bv_size(res_sig_1) == sbits+4); expr_ref sig_abs_h2(m), sticky_h2(m), sticky_h2_red(m), sig_abs_h2_f(m), res_sig_2(m); sticky_h2 = m_bv_util.mk_extract(sbits+too_short-1, 0, sig_abs); sig_abs_h2 = m_bv_util.mk_extract(2*sbits+too_short+4, sbits+too_short, sig_abs); sticky_h2_red = m_bv_util.mk_zero_extend(sbits+4, m.mk_app(m_bv_util.get_fid(), OP_BREDOR, sticky_h1.get())); expr * sticky_h2_red_args[2] = { sig_abs_h2, sticky_h2_red }; sig_abs_h2_f = m_bv_util.mk_zero_extend(1, m_bv_util.mk_bv_or(2, sticky_h2_red_args)); res_sig_2 = m_bv_util.mk_extract(sbits+3, 0, sig_abs_h2_f); SASSERT(m_bv_util.get_bv_size(sticky_h2) == sbits+too_short); SASSERT(m_bv_util.get_bv_size(sig_abs_h2) == sbits+5); SASSERT(m_bv_util.get_bv_size(sticky_h2_red) == sbits+5); SASSERT(m_bv_util.get_bv_size(sig_abs_h2_f) == sbits+6); SASSERT(m_bv_util.get_bv_size(res_sig_2) == sbits+4); res_sig = m.mk_ite(extra_is_zero, res_sig_1, res_sig_2); dbg_decouple("fpa2bv_fma_res_sig", res_sig); dbg_decouple("fpa2bv_fma_res_exp", res_exp); SASSERT(m_bv_util.get_bv_size(res_sig) == sbits + 4); expr_ref is_zero_sig(m), nil_sbits4(m); nil_sbits4 = m_bv_util.mk_numeral(0, sbits+4); m_simp.mk_eq(res_sig, nil_sbits4, is_zero_sig); SASSERT(is_well_sorted(m, is_zero_sig)); dbg_decouple("fpa2bv_fma_is_zero_sig", is_zero_sig); expr_ref zero_case(m); mk_ite(rm_is_to_neg, nzero, pzero, zero_case); expr_ref rounded(m); round(f->get_range(), rm, res_sgn, res_sig, res_exp, rounded); mk_ite(is_zero_sig, zero_case, rounded, v8); // And finally, we tie them together. mk_ite(c7, v7, v8, result); mk_ite(c6, v6, result, result); mk_ite(c5, v5, result, result); mk_ite(c4, v4, result, result); mk_ite(c3, v3, result, result); mk_ite(c2, v2, result, result); mk_ite(c1, v1, result, result); SASSERT(is_well_sorted(m, result)); TRACE("fpa2bv_fma_", tout << "FMA = " << mk_ismt2_pp(result, m) << std::endl; ); } void fpa2bv_converter::mk_sqrt(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 2); SASSERT(m_util.is_bv2rm(args[0])); expr_ref rm(m), x(m); rm = to_app(args[0])->get_arg(0); x = args[1]; expr_ref nan(m), nzero(m), pzero(m), ninf(m), pinf(m); mk_nan(f, nan); mk_nzero(f, nzero); mk_pzero(f, pzero); mk_ninf(f, ninf); mk_pinf(f, pinf); expr_ref x_is_nan(m), x_is_zero(m), x_is_pos(m), x_is_inf(m); mk_is_nan(x, x_is_nan); mk_is_zero(x, x_is_zero); mk_is_pos(x, x_is_pos); mk_is_inf(x, x_is_inf); expr_ref zero1(m), one1(m); zero1 = m_bv_util.mk_numeral(0, 1); one1 = m_bv_util.mk_numeral(1, 1); expr_ref c1(m), c2(m), c3(m), c4(m), c5(m), c6(m); expr_ref v1(m), v2(m), v3(m), v4(m), v5(m), v6(m), v7(m); // (x is NaN) -> NaN c1 = x_is_nan; v1 = x; // (x is +oo) -> +oo mk_is_pinf(x, c2); v2 = x; // (x is +-0) -> +-0 mk_is_zero(x, c3); v3 = x; // (x < 0) -> NaN mk_is_neg(x, c4); v4 = nan; // else comes the actual square root. unsigned ebits = m_util.get_ebits(f->get_range()); unsigned sbits = m_util.get_sbits(f->get_range()); expr_ref a_sgn(m), a_sig(m), a_exp(m), a_lz(m); unpack(x, a_sgn, a_sig, a_exp, a_lz, true); dbg_decouple("fpa2bv_sqrt_sig", a_sig); dbg_decouple("fpa2bv_sqrt_exp", a_exp); SASSERT(m_bv_util.get_bv_size(a_sig) == sbits); SASSERT(m_bv_util.get_bv_size(a_exp) == ebits); expr_ref res_sgn(m), res_sig(m), res_exp(m); res_sgn = zero1; expr_ref real_exp(m); real_exp = m_bv_util.mk_bv_sub(m_bv_util.mk_sign_extend(1, a_exp), m_bv_util.mk_zero_extend(1, a_lz)); res_exp = m_bv_util.mk_sign_extend(2, m_bv_util.mk_extract(ebits, 1, real_exp)); expr_ref e_is_odd(m); e_is_odd = m.mk_eq(m_bv_util.mk_extract(0, 0, real_exp), one1); dbg_decouple("fpa2bv_sqrt_e_is_odd", e_is_odd); dbg_decouple("fpa2bv_sqrt_real_exp", real_exp); expr_ref sig_prime(m), a_z(m), z_a(m); a_z = m_bv_util.mk_concat(a_sig, zero1); z_a = m_bv_util.mk_concat(zero1, a_sig); m_simp.mk_ite(e_is_odd, a_z, z_a, sig_prime); SASSERT(m_bv_util.get_bv_size(sig_prime) == sbits+1); dbg_decouple("fpa2bv_sqrt_sig_prime", sig_prime); // This is algorithm 10.2 in the Handbook of Floating-Point Arithmetic expr_ref Q(m), R(m), S(m), T(m); const mpz & p2 = fu().fm().m_powers2(sbits+3); Q = m_bv_util.mk_numeral(p2, sbits+5); R = m_bv_util.mk_bv_sub(m_bv_util.mk_concat(sig_prime, m_bv_util.mk_numeral(0, 4)), Q); S = Q; for (unsigned i = 0; i < sbits + 3; i++) { dbg_decouple("fpa2bv_sqrt_Q", Q); dbg_decouple("fpa2bv_sqrt_R", R); S = m_bv_util.mk_concat(zero1, m_bv_util.mk_extract(sbits+4, 1, S)); expr_ref twoQ_plus_S(m); twoQ_plus_S = m_bv_util.mk_bv_add(m_bv_util.mk_concat(Q, zero1), m_bv_util.mk_concat(zero1, S)); T = m_bv_util.mk_bv_sub(m_bv_util.mk_concat(R, zero1), twoQ_plus_S); dbg_decouple("fpa2bv_sqrt_T", T); SASSERT(m_bv_util.get_bv_size(Q) == sbits + 5); SASSERT(m_bv_util.get_bv_size(R) == sbits + 5); SASSERT(m_bv_util.get_bv_size(S) == sbits + 5); SASSERT(m_bv_util.get_bv_size(T) == sbits + 6); expr_ref t_lt_0(m), T_lsds5(m); T_lsds5 = m_bv_util.mk_extract(sbits + 5, sbits + 5, T); m_simp.mk_eq(T_lsds5, one1, t_lt_0); expr * or_args[2] = { Q, S }; expr_ref Q_or_S(m), R_shftd(m), T_lsds4(m); Q_or_S = m_bv_util.mk_bv_or(2, or_args); m_simp.mk_ite(t_lt_0, Q, Q_or_S, Q); R_shftd = m_bv_util.mk_concat(m_bv_util.mk_extract(sbits + 3, 0, R), zero1); T_lsds4 = m_bv_util.mk_extract(sbits + 4, 0, T); m_simp.mk_ite(t_lt_0, R_shftd, T_lsds4, R); } expr_ref is_exact(m), zero_sbits5(m); zero_sbits5 = m_bv_util.mk_numeral(0, sbits + 5); m_simp.mk_eq(R, zero_sbits5, is_exact); dbg_decouple("fpa2bv_sqrt_is_exact", is_exact); expr_ref rest(m), last(m), q_is_odd(m), rest_ext(m); last = m_bv_util.mk_extract(0, 0, Q); rest = m_bv_util.mk_extract(sbits+3, 1, Q); dbg_decouple("fpa2bv_sqrt_last", last); dbg_decouple("fpa2bv_sqrt_rest", rest); rest_ext = m_bv_util.mk_zero_extend(1, rest); expr_ref sticky(m), last_ext(m), one_sbits4(m); last_ext = m_bv_util.mk_zero_extend(sbits + 3, last); one_sbits4 = m_bv_util.mk_numeral(1, sbits + 4); m_simp.mk_ite(is_exact, last_ext, one_sbits4, sticky); expr * or_args[2] = { rest_ext, sticky }; res_sig = m_bv_util.mk_bv_or(2, or_args); SASSERT(m_bv_util.get_bv_size(res_sig) == sbits + 4); expr_ref rounded(m); round(f->get_range(), rm, res_sgn, res_sig, res_exp, rounded); v5 = rounded; // And finally, we tie them together. mk_ite(c4, v4, v5, result); mk_ite(c3, v3, result, result); mk_ite(c2, v2, result, result); mk_ite(c1, v1, result, result); SASSERT(is_well_sorted(m, result)); } void fpa2bv_converter::mk_round_to_integral(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 2); SASSERT(m_util.is_bv2rm(args[0])); expr_ref rm(m), x(m); rm = to_app(args[0])->get_arg(0); x = args[1]; mk_round_to_integral(f->get_range(), rm, x, result); } void fpa2bv_converter::mk_round_to_integral(sort * s, expr_ref & rm, expr_ref & x, expr_ref & result) { expr_ref rm_is_rta(m), rm_is_rte(m), rm_is_rtp(m), rm_is_rtn(m), rm_is_rtz(m); mk_is_rm(rm, BV_RM_TIES_TO_AWAY, rm_is_rta); mk_is_rm(rm, BV_RM_TIES_TO_EVEN, rm_is_rte); mk_is_rm(rm, BV_RM_TO_POSITIVE, rm_is_rtp); mk_is_rm(rm, BV_RM_TO_NEGATIVE, rm_is_rtn); mk_is_rm(rm, BV_RM_TO_ZERO, rm_is_rtz); expr_ref nan(m), nzero(m), pzero(m), ninf(m), pinf(m); mk_nan(s, nan); mk_nzero(s, nzero); mk_pzero(s, pzero); expr_ref x_is_zero(m), x_is_pos(m), x_is_neg(m); mk_is_zero(x, x_is_zero); mk_is_pos(x, x_is_pos); mk_is_neg(x, x_is_neg); dbg_decouple("fpa2bv_r2i_x_is_zero", x_is_zero); dbg_decouple("fpa2bv_r2i_x_is_pos", x_is_pos); expr_ref c1(m), c2(m), c3(m), c4(m), c5(m); expr_ref v1(m), v2(m), v3(m), v4(m), v5(m), v6(m); // (x is NaN) -> NaN mk_is_nan(x, c1); v1 = nan; // (x is +-oo) -> x mk_is_inf(x, c2); v2 = x; // (x is +-0) -> x ; -0.0 -> -0.0, says IEEE754, Sec 5.9. mk_is_zero(x, c3); v3 = x; expr_ref one_1(m), zero_1(m); one_1 = m_bv_util.mk_numeral(1, 1); zero_1 = m_bv_util.mk_numeral(0, 1); unsigned ebits = m_util.get_ebits(s); unsigned sbits = m_util.get_sbits(s); expr_ref a_sgn(m), a_sig(m), a_exp(m), a_lz(m); unpack(x, a_sgn, a_sig, a_exp, a_lz, true); dbg_decouple("fpa2bv_r2i_unpacked_sgn", a_sgn); dbg_decouple("fpa2bv_r2i_unpacked_exp", a_exp); dbg_decouple("fpa2bv_r2i_unpacked_sig", a_sig); dbg_decouple("fpa2bv_r2i_unpacked_lz", a_lz); expr_ref xzero(m), sgn_eq_1(m); sgn_eq_1 = m.mk_eq(a_sgn, one_1); mk_ite(sgn_eq_1, nzero, pzero, xzero); // exponent < 0 -> 0/1 expr_ref exp_lt_zero(m), exp_h(m); exp_h = m_bv_util.mk_extract(ebits-1, ebits-1, a_exp); m_simp.mk_eq(exp_h, one_1, exp_lt_zero); dbg_decouple("fpa2bv_r2i_exp_lt_zero", exp_lt_zero); c4 = exp_lt_zero; expr_ref pone(m), none(m), xone(m), c421(m), c422(m), c423(m), t1(m), t2(m), tie(m), v42(m), exp_lt_m1(m); mk_one(s, zero_1, pone); mk_one(s, one_1, none); mk_ite(sgn_eq_1, none, pone, xone); expr_ref pow_2_sbitsm1(m), m1(m); pow_2_sbitsm1 = m_bv_util.mk_numeral(fu().fm().m_powers2(sbits - 1), sbits); m1 = m_bv_util.mk_bv_neg(m_bv_util.mk_numeral(1, ebits)); m_simp.mk_eq(a_sig, pow_2_sbitsm1, t1); m_simp.mk_eq(a_exp, m1, t2); m_simp.mk_and(t1, t2, tie); dbg_decouple("fpa2bv_r2i_c42_tie", tie); m_simp.mk_and(tie, rm_is_rte, c421); m_simp.mk_and(tie, rm_is_rta, c422); c423 = m_bv_util.mk_sle(a_exp, m_bv_util.mk_bv_neg(m_bv_util.mk_numeral(2, ebits))); dbg_decouple("fpa2bv_r2i_c421", c421); dbg_decouple("fpa2bv_r2i_c422", c422); dbg_decouple("fpa2bv_r2i_c423", c423); v42 = xone; mk_ite(c423, xzero, v42, v42); mk_ite(c422, xone, v42, v42); mk_ite(c421, xzero, v42, v42); expr_ref v4_rtn(m), v4_rtp(m); mk_ite(x_is_neg, nzero, pone, v4_rtp); mk_ite(x_is_neg, none, pzero, v4_rtn); mk_ite(rm_is_rtp, v4_rtp, v42, v4); mk_ite(rm_is_rtn, v4_rtn, v4, v4); mk_ite(rm_is_rtz, xzero, v4, v4); SASSERT(is_well_sorted(m, v4)); // exponent >= sbits-1 -> x expr_ref exp_is_large(m); exp_is_large = log2(sbits-1)+1 <= ebits-1 ? m_bv_util.mk_sle(m_bv_util.mk_numeral(sbits-1, ebits), a_exp) : m.mk_false(); dbg_decouple("fpa2bv_r2i_exp_is_large", exp_is_large); c5 = exp_is_large; v5 = x; // Actual conversion with rounding. // exponent >= 0 && exponent < sbits - 1 expr_ref res_sgn(m), res_sig(m), res_exp(m); res_sgn = a_sgn; res_exp = a_exp; SASSERT(m_bv_util.get_bv_size(a_sig) == sbits); SASSERT(m_bv_util.get_bv_size(a_exp) == ebits); expr_ref zero_s(m); zero_s = m_bv_util.mk_numeral(0, sbits); expr_ref shift(m), shifted_sig(m), div(m), rem(m); shift = m_bv_util.mk_bv_sub(m_bv_util.mk_numeral(sbits - 1, sbits), m_bv_util.mk_zero_extend(sbits-ebits, a_exp)); shifted_sig = m_bv_util.mk_bv_lshr(m_bv_util.mk_concat(a_sig, zero_s), m_bv_util.mk_concat(zero_s, shift)); div = m_bv_util.mk_extract(2*sbits-1, sbits, shifted_sig); rem = m_bv_util.mk_extract(sbits-1, 0, shifted_sig); SASSERT(is_well_sorted(m, div)); SASSERT(is_well_sorted(m, rem)); SASSERT(m_bv_util.get_bv_size(shift) == sbits); SASSERT(m_bv_util.get_bv_size(div) == sbits); SASSERT(m_bv_util.get_bv_size(rem) == sbits); dbg_decouple("fpa2bv_r2i_shifted_sig", shifted_sig); dbg_decouple("fpa2bv_r2i_shift", shift); dbg_decouple("fpa2bv_r2i_div", div); dbg_decouple("fpa2bv_r2i_rem", rem); expr_ref div_p1(m); div_p1 = m_bv_util.mk_bv_add(div, m_bv_util.mk_numeral(1, sbits)); expr_ref tie_pttrn(m), tie2(m), tie2_c(m), div_last(m), v51(m); tie_pttrn = m_bv_util.mk_concat(one_1, m_bv_util.mk_numeral(0, sbits-1)); m_simp.mk_eq(rem, tie_pttrn, tie2); div_last = m_bv_util.mk_extract(0, 0, div); expr_ref div_last_eq_1(m), rte_and_dl_eq_1(m), rte_and_dl_eq_1_or_rta(m), tie_pttrn_ule_rem(m); div_last_eq_1 = m.mk_eq(div_last, one_1); rte_and_dl_eq_1 = m.mk_and(rm_is_rte, div_last_eq_1); rte_and_dl_eq_1_or_rta = m.mk_or(rte_and_dl_eq_1, rm_is_rta); tie_pttrn_ule_rem = m_bv_util.mk_ule(tie_pttrn, rem); tie2_c = m.mk_ite(tie2, rte_and_dl_eq_1_or_rta, tie_pttrn_ule_rem); m_simp.mk_ite(tie2_c, div_p1, div, v51); dbg_decouple("fpa2bv_r2i_v51", v51); dbg_decouple("fpa2bv_r2i_tie2", tie2); dbg_decouple("fpa2bv_r2i_tie2_c", tie2_c); SASSERT(is_well_sorted(m, tie2)); SASSERT(is_well_sorted(m, tie2_c)); SASSERT(is_well_sorted(m, v51)); expr_ref c521(m), v52(m), rem_eq_0(m), sgn_eq_zero(m); rem_eq_0 = m.mk_eq(rem, m_bv_util.mk_numeral(0, sbits)); sgn_eq_zero = m.mk_eq(res_sgn, zero_1); m_simp.mk_not(rem_eq_0, c521); m_simp.mk_and(c521, sgn_eq_zero, c521); m_simp.mk_ite(c521, div_p1, div, v52); expr_ref c531(m), v53(m), sgn_eq_one(m); sgn_eq_one = m.mk_eq(res_sgn, one_1); m_simp.mk_not(rem_eq_0, c531); m_simp.mk_and(c531, sgn_eq_one, c531); m_simp.mk_ite(c531, div_p1, div, v53); expr_ref c51(m), c52(m), c53(m); c51 = m.mk_or(rm_is_rte, rm_is_rta); c52 = rm_is_rtp; c53 = rm_is_rtn; res_sig = div; m_simp.mk_ite(c53, v53, res_sig, res_sig); m_simp.mk_ite(c52, v52, res_sig, res_sig); m_simp.mk_ite(c51, v51, res_sig, res_sig); SASSERT(m_bv_util.get_bv_size(res_exp) == ebits); SASSERT(m_bv_util.get_bv_size(shift) == sbits); expr_ref e_shift(m); e_shift = (ebits + 2 <= sbits + 1) ? m_bv_util.mk_extract(ebits + 1, 0, shift) : m_bv_util.mk_sign_extend((ebits + 2) - (sbits), shift); SASSERT(m_bv_util.get_bv_size(e_shift) == ebits + 2); res_exp = m_bv_util.mk_bv_add(m_bv_util.mk_zero_extend(2, res_exp), e_shift); SASSERT(m_bv_util.get_bv_size(res_sgn) == 1); SASSERT(m_bv_util.get_bv_size(res_sig) == sbits); SASSERT(m_bv_util.get_bv_size(res_exp) == ebits + 2); // Renormalize expr_ref zero_e2(m), min_exp(m), sig_lz(m), max_exp_delta(m), sig_lz_capped(m), renorm_delta(m); zero_e2 = m_bv_util.mk_numeral(0, ebits+2); mk_min_exp(ebits, min_exp); min_exp = m_bv_util.mk_sign_extend(2, min_exp); mk_leading_zeros(res_sig, ebits+2, sig_lz); max_exp_delta = m_bv_util.mk_bv_sub(res_exp, min_exp); sig_lz_capped = m.mk_ite(m_bv_util.mk_sle(sig_lz, max_exp_delta), sig_lz, max_exp_delta); renorm_delta = m.mk_ite(m_bv_util.mk_sle(zero_e2, sig_lz_capped), sig_lz_capped, zero_e2); SASSERT(m_bv_util.get_bv_size(renorm_delta) == ebits + 2); res_exp = m_bv_util.mk_bv_sub(res_exp, renorm_delta); res_sig = m_bv_util.mk_bv_shl(res_sig, m_bv_util.mk_zero_extend(sbits-ebits-2, renorm_delta)); dbg_decouple("fpa2bv_r2i_renorm_delta", renorm_delta); res_exp = m_bv_util.mk_extract(ebits-1, 0, res_exp); mk_bias(res_exp, res_exp); res_sig = m_bv_util.mk_extract(sbits-2, 0, res_sig); v6 = m_util.mk_fp(res_sgn, res_exp, res_sig); dbg_decouple("fpa2bv_r2i_res_sgn", res_sgn); dbg_decouple("fpa2bv_r2i_res_sig", res_sig); dbg_decouple("fpa2bv_r2i_res_exp", res_exp); dbg_decouple("fpa2bv_r2i_c1", c1); dbg_decouple("fpa2bv_r2i_c2", c2); dbg_decouple("fpa2bv_r2i_c3", c3); dbg_decouple("fpa2bv_r2i_c4", c4); dbg_decouple("fpa2bv_r2i_c5", c5); // And finally, we tie them together. mk_ite(c5, v5, v6, result); mk_ite(c4, v4, result, result); mk_ite(c3, v3, result, result); mk_ite(c2, v2, result, result); mk_ite(c1, v1, result, result); SASSERT(is_well_sorted(m, result)); TRACE("fpa2bv_round_to_integral", tout << "ROUND2INTEGRAL = " << mk_ismt2_pp(result, m) << std::endl; ); } void fpa2bv_converter::mk_float_eq(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 2); expr_ref x(m), y(m); x = args[0]; y = args[1]; mk_float_eq(f->get_range(), x, y, result); } void fpa2bv_converter::mk_float_eq(sort * s, expr_ref & x, expr_ref & y, expr_ref & result) { TRACE("fpa2bv_float_eq", tout << "X = " << mk_ismt2_pp(x, m) << std::endl; tout << "Y = " << mk_ismt2_pp(y, m) << std::endl;); expr_ref c1(m), c2(m), x_is_nan(m), y_is_nan(m), x_is_zero(m), y_is_zero(m); mk_is_nan(x, x_is_nan); mk_is_nan(y, y_is_nan); m_simp.mk_or(x_is_nan, y_is_nan, c1); mk_is_zero(x, x_is_zero); mk_is_zero(y, y_is_zero); m_simp.mk_and(x_is_zero, y_is_zero, c2); expr_ref x_sgn(m), x_sig(m), x_exp(m); expr_ref y_sgn(m), y_sig(m), y_exp(m); split_fp(x, x_sgn, x_exp, x_sig); split_fp(y, y_sgn, y_exp, y_sig); expr_ref x_eq_y_sgn(m), x_eq_y_exp(m), x_eq_y_sig(m); m_simp.mk_eq(x_sgn, y_sgn, x_eq_y_sgn); m_simp.mk_eq(x_exp, y_exp, x_eq_y_exp); m_simp.mk_eq(x_sig, y_sig, x_eq_y_sig); expr_ref c3(m), t4(m); m_simp.mk_not(x_eq_y_sgn, c3); m_simp.mk_and(x_eq_y_exp, x_eq_y_sig, t4); expr_ref c3t4(m), c2else(m); m_simp.mk_ite(c3, m.mk_false(), t4, c3t4); m_simp.mk_ite(c2, m.mk_true(), c3t4, c2else); m_simp.mk_ite(c1, m.mk_false(), c2else, result); TRACE("fpa2bv_float_eq", tout << "FLOAT_EQ = " << mk_ismt2_pp(result, m) << std::endl; ); } void fpa2bv_converter::mk_float_lt(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 2); expr_ref x(m), y(m); x = args[0]; y = args[1]; mk_float_lt(f->get_range(), x, y, result); } void fpa2bv_converter::mk_float_lt(sort * s, expr_ref & x, expr_ref & y, expr_ref & result) { expr_ref c1(m), c2(m), x_is_nan(m), y_is_nan(m), x_is_zero(m), y_is_zero(m); mk_is_nan(x, x_is_nan); mk_is_nan(y, y_is_nan); m_simp.mk_or(x_is_nan, y_is_nan, c1); mk_is_zero(x, x_is_zero); mk_is_zero(y, y_is_zero); m_simp.mk_and(x_is_zero, y_is_zero, c2); expr_ref x_sgn(m), x_sig(m), x_exp(m); expr_ref y_sgn(m), y_sig(m), y_exp(m); split_fp(x, x_sgn, x_exp, x_sig); split_fp(y, y_sgn, y_exp, y_sig); expr_ref c3(m), t3(m), t4(m), one_1(m), nil_1(m); one_1 = m_bv_util.mk_numeral(1, 1); nil_1 = m_bv_util.mk_numeral(0, 1); m_simp.mk_eq(x_sgn, one_1, c3); expr_ref y_sgn_eq_0(m), y_lt_x_exp(m), y_lt_x_sig(m), y_eq_x_exp(m), y_le_x_sig_exp(m), t3_or(m); m_simp.mk_eq(y_sgn, nil_1, y_sgn_eq_0); BVULT(y_exp, x_exp, y_lt_x_exp); BVULT(y_sig, x_sig, y_lt_x_sig); m_simp.mk_eq(y_exp, x_exp, y_eq_x_exp); m_simp.mk_and(y_eq_x_exp, y_lt_x_sig, y_le_x_sig_exp); m_simp.mk_or(y_lt_x_exp, y_le_x_sig_exp, t3_or); m_simp.mk_ite(y_sgn_eq_0, m.mk_true(), t3_or, t3); expr_ref y_sgn_eq_1(m), x_lt_y_exp(m), x_eq_y_exp(m), x_lt_y_sig(m), x_le_y_sig_exp(m), t4_or(m); m_simp.mk_eq(y_sgn, one_1, y_sgn_eq_1); BVULT(x_exp, y_exp, x_lt_y_exp); m_simp.mk_eq(x_exp, y_exp, x_eq_y_exp); BVULT(x_sig, y_sig, x_lt_y_sig); m_simp.mk_and(x_eq_y_exp, x_lt_y_sig, x_le_y_sig_exp); m_simp.mk_or(x_lt_y_exp, x_le_y_sig_exp, t4_or); m_simp.mk_ite(y_sgn_eq_1, m.mk_false(), t4_or, t4); expr_ref c3t3t4(m), c2else(m); m_simp.mk_ite(c3, t3, t4, c3t3t4); m_simp.mk_ite(c2, m.mk_false(), c3t3t4, c2else); m_simp.mk_ite(c1, m.mk_false(), c2else, result); } void fpa2bv_converter::mk_float_gt(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 2); expr_ref x(m), y(m); x = args[0]; y = args[1]; mk_float_gt(f->get_range(), x, y, result); } void fpa2bv_converter::mk_float_gt(sort * s, expr_ref & x, expr_ref & y, expr_ref & result) { expr_ref t3(m); mk_float_le(s, x, y, t3); expr_ref nan_or(m), xy_zero(m), not_t3(m), r_else(m); expr_ref x_is_nan(m), y_is_nan(m), x_is_zero(m), y_is_zero(m); mk_is_nan(x, x_is_nan); mk_is_nan(y, y_is_nan); m_simp.mk_or(x_is_nan, y_is_nan, nan_or); mk_is_zero(x, x_is_zero); mk_is_zero(y, y_is_zero); m_simp.mk_and(x_is_zero, y_is_zero, xy_zero); m_simp.mk_not(t3, not_t3); m_simp.mk_ite(xy_zero, m.mk_false(), not_t3, r_else); m_simp.mk_ite(nan_or, m.mk_false(), r_else, result); } void fpa2bv_converter::mk_float_le(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 2); expr_ref x(m), y(m); x = args[0]; y = args[1]; mk_float_le(f->get_range(), x, y, result); } void fpa2bv_converter::mk_float_le(sort * s, expr_ref & x, expr_ref & y, expr_ref & result) { expr_ref a(m), b(m); mk_float_lt(s, x, y, a); mk_float_eq(s, x, y, b); m_simp.mk_or(a, b, result); } void fpa2bv_converter::mk_float_ge(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 2); expr_ref x(m), y(m); x = args[0]; y = args[1]; mk_float_ge(f->get_range(), x, y, result); } void fpa2bv_converter::mk_float_ge(sort * s, expr_ref & x, expr_ref & y, expr_ref & result) { expr_ref a(m), b(m); mk_float_gt(s, x, y, a); mk_float_eq(s, x, y, b); m_simp.mk_or(a, b, result); } void fpa2bv_converter::mk_is_zero(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 1); mk_is_zero(args[0], result); } void fpa2bv_converter::mk_is_nzero(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 1); expr_ref a0_is_neg(m), a0_is_zero(m); mk_is_neg(args[0], a0_is_neg); mk_is_zero(args[0], a0_is_zero); m_simp.mk_and(a0_is_neg, a0_is_zero, result); } void fpa2bv_converter::mk_is_pzero(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 1); expr_ref a0_is_pos(m), a0_is_zero(m); mk_is_pos(args[0], a0_is_pos); mk_is_zero(args[0], a0_is_zero); m_simp.mk_and(a0_is_pos, a0_is_zero, result); } void fpa2bv_converter::mk_is_nan(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 1); mk_is_nan(args[0], result); } void fpa2bv_converter::mk_is_inf(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 1); mk_is_inf(args[0], result); } void fpa2bv_converter::mk_is_normal(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 1); mk_is_normal(args[0], result); } void fpa2bv_converter::mk_is_subnormal(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 1); mk_is_denormal(args[0], result); } void fpa2bv_converter::mk_is_negative(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 1); expr_ref t1(m), t2(m), nt1(m); mk_is_nan(args[0], t1); mk_is_neg(args[0], t2); nt1 = m.mk_not(t1); result = m.mk_and(nt1, t2); TRACE("fpa2bv_is_negative", tout << "result = " << mk_ismt2_pp(result, m) << std::endl;); } void fpa2bv_converter::mk_is_positive(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 1); expr_ref t1(m), t2(m), nt1(m); mk_is_nan(args[0], t1); mk_is_pos(args[0], t2); nt1 = m.mk_not(t1); result = m.mk_and(nt1, t2); } void fpa2bv_converter::mk_to_fp(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { TRACE("fpa2bv_to_fp", for (unsigned i=0; i < num; i++) tout << "arg" << i << " = " << mk_ismt2_pp(args[i], m) << std::endl; ); if (num == 1 && m_bv_util.is_bv(args[0])) { sort * s = f->get_range(); unsigned to_sbits = m_util.get_sbits(s); unsigned to_ebits = m_util.get_ebits(s); expr * bv = args[0]; int sz = m_bv_util.get_bv_size(bv); (void)to_sbits; SASSERT((unsigned)sz == to_sbits + to_ebits); result = m_util.mk_fp(m_bv_util.mk_extract(sz - 1, sz - 1, bv), m_bv_util.mk_extract(sz - 2, sz - to_ebits - 1, bv), m_bv_util.mk_extract(sz - to_ebits - 2, 0, bv)); } else if (num == 2 && m_util.is_rm(args[0]) && m_util.is_float(m.get_sort(args[1]))) { // rm + float -> float mk_to_fp_float(f, f->get_range(), args[0], args[1], result); } else if (num == 2 && m_util.is_rm(args[0]) && (m_arith_util.is_int(args[1]) || m_arith_util.is_real(args[1]))) { // rm + real -> float mk_to_fp_real(f, f->get_range(), args[0], args[1], result); } else if (num == 2 && m_util.is_rm(args[0]) && m_bv_util.is_bv(args[1])) { // rm + signed bv -> float mk_to_fp_signed(f, num, args, result); } else if (num == 3 && m_bv_util.is_bv(args[0]) && m_bv_util.is_bv(args[1]) && m_bv_util.is_bv(args[2])) { // 3 BV -> float SASSERT(m_bv_util.get_bv_size(args[0]) == 1); SASSERT(m_util.get_ebits(f->get_range()) == m_bv_util.get_bv_size(args[1])); SASSERT(m_util.get_sbits(f->get_range()) == m_bv_util.get_bv_size(args[2])+1); result = m_util.mk_fp(args[0], args[1], args[2]); } else if (num == 3 && m_util.is_rm(args[0]) && m_arith_util.is_numeral(args[1]) && m_arith_util.is_numeral(args[2])) { // rm + real + int -> float mk_to_fp_real_int(f, num, args, result); } else UNREACHABLE(); SASSERT(is_well_sorted(m, result)); } void fpa2bv_converter::mk_to_fp_float(func_decl * f, sort * s, expr * rm, expr * x, expr_ref & result) { SASSERT(m_util.is_bv2rm(rm)); mk_to_fp_float(s, to_app(rm)->get_arg(0), x, result); } void fpa2bv_converter::mk_to_fp_float(sort * to_srt, expr * rm, expr * x, expr_ref & result) { unsigned from_sbits = m_util.get_sbits(m.get_sort(x)); unsigned from_ebits = m_util.get_ebits(m.get_sort(x)); unsigned to_sbits = m_util.get_sbits(to_srt); unsigned to_ebits = m_util.get_ebits(to_srt); if (from_sbits == to_sbits && from_ebits == to_ebits) result = x; else { expr_ref c1(m), c2(m), c3(m), c4(m), c5(m); expr_ref v1(m), v2(m), v3(m), v4(m), v5(m), v6(m); expr_ref one1(m); one1 = m_bv_util.mk_numeral(1, 1); expr_ref ninf(m), pinf(m); mk_pinf(to_srt, pinf); mk_ninf(to_srt, ninf); // NaN -> NaN mk_is_nan(x, c1); mk_nan(to_srt, v1); // +0 -> +0 mk_is_pzero(x, c2); mk_pzero(to_srt, v2); // -0 -> -0 mk_is_nzero(x, c3); mk_nzero(to_srt, v3); // +oo -> +oo mk_is_pinf(x, c4); v4 = pinf; // -oo -> -oo mk_is_ninf(x, c5); v5 = ninf; // otherwise: the actual conversion with rounding. expr_ref sgn(m), sig(m), exp(m), lz(m); unpack(x, sgn, sig, exp, lz, true); dbg_decouple("fpa2bv_to_float_x_sgn", sgn); dbg_decouple("fpa2bv_to_float_x_sig", sig); dbg_decouple("fpa2bv_to_float_x_exp", exp); dbg_decouple("fpa2bv_to_float_lz", lz); expr_ref res_sgn(m), res_sig(m), res_exp(m); res_sgn = sgn; SASSERT(m_bv_util.get_bv_size(sgn) == 1); SASSERT(m_bv_util.get_bv_size(sig) == from_sbits); SASSERT(m_bv_util.get_bv_size(exp) == from_ebits); SASSERT(m_bv_util.get_bv_size(lz) == from_ebits); if (from_sbits < (to_sbits + 3)) { // make sure that sig has at least to_sbits + 3 res_sig = m_bv_util.mk_concat(sig, m_bv_util.mk_numeral(0, to_sbits + 3 - from_sbits)); } else if (from_sbits > (to_sbits + 3)) { // collapse the extra bits into a sticky bit. expr_ref sticky(m), low(m), high(m); high = m_bv_util.mk_extract(from_sbits - 1, from_sbits - to_sbits - 2, sig); SASSERT(m_bv_util.get_bv_size(high) == to_sbits + 2); low = m_bv_util.mk_extract(from_sbits - to_sbits - 3, 0, sig); sticky = m.mk_app(m_bv_util.get_fid(), OP_BREDOR, low.get()); SASSERT(m_bv_util.get_bv_size(sticky) == 1); dbg_decouple("fpa2bv_to_float_sticky", sticky); res_sig = m_bv_util.mk_concat(high, sticky); SASSERT(m_bv_util.get_bv_size(res_sig) == to_sbits + 3); } else res_sig = sig; res_sig = m_bv_util.mk_zero_extend(1, res_sig); // extra zero in the front for the rounder. unsigned sig_sz = m_bv_util.get_bv_size(res_sig); (void) sig_sz; SASSERT(sig_sz == to_sbits + 4); expr_ref exponent_overflow(m), exponent_underflow(m); exponent_overflow = m.mk_false(); exponent_underflow = m.mk_false(); if (from_ebits < (to_ebits + 2)) { res_exp = m_bv_util.mk_sign_extend(to_ebits - from_ebits + 2, exp); // subtract lz for subnormal numbers. expr_ref lz_ext(m); lz_ext = m_bv_util.mk_zero_extend(to_ebits - from_ebits + 2, lz); res_exp = m_bv_util.mk_bv_sub(res_exp, lz_ext); } else if (from_ebits > (to_ebits + 2)) { unsigned ebits_diff = from_ebits - (to_ebits + 2); // subtract lz for subnormal numbers. expr_ref exp_sub_lz(m); exp_sub_lz = m_bv_util.mk_bv_sub(m_bv_util.mk_sign_extend(2, exp), m_bv_util.mk_sign_extend(2, lz)); dbg_decouple("fpa2bv_to_float_exp_sub_lz", exp_sub_lz); // check whether exponent is within roundable (to_ebits+2) range. expr_ref max_exp(m), min_exp(m), exp_in_range(m); const mpz & z = m_mpf_manager.m_powers2(to_ebits + 1, true); max_exp = m_bv_util.mk_concat( m_bv_util.mk_numeral(m_mpf_manager.m_powers2.m1(to_ebits, false), to_ebits + 1), m_bv_util.mk_numeral(0, 1)); min_exp = m_bv_util.mk_numeral(z + mpz(2), to_ebits+2); dbg_decouple("fpa2bv_to_float_max_exp", max_exp); dbg_decouple("fpa2bv_to_float_min_exp", min_exp); expr_ref first_ovf_exp(m), first_udf_exp(m); const mpz & ovft = m_mpf_manager.m_powers2.m1(to_ebits+1, false); first_ovf_exp = m_bv_util.mk_numeral(ovft, from_ebits+2); first_udf_exp = m_bv_util.mk_concat( m_bv_util.mk_bv_neg(m_bv_util.mk_numeral(1, ebits_diff + 3)), m_bv_util.mk_numeral(1, to_ebits + 1)); dbg_decouple("fpa2bv_to_float_first_ovf_exp", first_ovf_exp); dbg_decouple("fpa2bv_to_float_first_udf_exp", first_udf_exp); exp_in_range = m_bv_util.mk_extract(to_ebits + 1, 0, exp_sub_lz); SASSERT(m_bv_util.get_bv_size(exp_in_range) == to_ebits + 2); expr_ref ovf_cond(m), udf_cond(m); ovf_cond = m_bv_util.mk_sle(first_ovf_exp, exp_sub_lz); udf_cond = m_bv_util.mk_sle(exp_sub_lz, first_udf_exp); dbg_decouple("fpa2bv_to_float_exp_ovf", ovf_cond); dbg_decouple("fpa2bv_to_float_exp_udf", udf_cond); res_exp = exp_in_range; res_exp = m.mk_ite(ovf_cond, max_exp, res_exp); res_exp = m.mk_ite(udf_cond, min_exp, res_exp); } else { // from_ebits == (to_ebits + 2) res_exp = m_bv_util.mk_bv_sub(exp, lz); } SASSERT(m_bv_util.get_bv_size(res_exp) == to_ebits + 2); SASSERT(is_well_sorted(m, res_exp)); dbg_decouple("fpa2bv_to_float_res_sig", res_sig); dbg_decouple("fpa2bv_to_float_res_exp", res_exp); expr_ref rounded(m); expr_ref rm_e(rm, m); round(to_srt, rm_e, res_sgn, res_sig, res_exp, rounded); expr_ref is_neg(m), sig_inf(m); m_simp.mk_eq(sgn, one1, is_neg); mk_ite(is_neg, ninf, pinf, sig_inf); mk_ite(exponent_overflow, sig_inf, rounded, v6); // And finally, we tie them together. mk_ite(c5, v5, v6, result); mk_ite(c4, v4, result, result); mk_ite(c3, v3, result, result); mk_ite(c2, v2, result, result); mk_ite(c1, v1, result, result); } SASSERT(is_well_sorted(m, result)); } void fpa2bv_converter::mk_to_fp_real(func_decl * f, sort * s, expr * rm, expr * x, expr_ref & result) { TRACE("fpa2bv_to_fp_real", tout << "rm: " << mk_ismt2_pp(rm, m) << std::endl << "x: " << mk_ismt2_pp(x, m) << std::endl;); SASSERT(m_util.is_float(s)); SASSERT(au().is_real(x) || au().is_int(x)); SASSERT(m_util.is_bv2rm(rm)); expr * bv_rm = to_app(rm)->get_arg(0); unsigned ebits = m_util.get_ebits(s); unsigned sbits = m_util.get_sbits(s); if (m_bv_util.is_numeral(bv_rm) && m_util.au().is_numeral(x)) { rational tmp_rat; unsigned sz; m_bv_util.is_numeral(to_expr(bv_rm), tmp_rat, sz); SASSERT(tmp_rat.is_int32()); SASSERT(sz == 3); mpf_rounding_mode mrm; switch ((BV_RM_VAL)tmp_rat.get_unsigned()) { case BV_RM_TIES_TO_AWAY: mrm = MPF_ROUND_NEAREST_TAWAY; break; case BV_RM_TIES_TO_EVEN: mrm = MPF_ROUND_NEAREST_TEVEN; break; case BV_RM_TO_NEGATIVE: mrm = MPF_ROUND_TOWARD_NEGATIVE; break; case BV_RM_TO_POSITIVE: mrm = MPF_ROUND_TOWARD_POSITIVE; break; case BV_RM_TO_ZERO: mrm = MPF_ROUND_TOWARD_ZERO; break; default: UNREACHABLE(); } rational q; bool is_int; m_util.au().is_numeral(x, q, is_int); if (q.is_zero()) return mk_pzero(f, result); else { scoped_mpf v(m_mpf_manager); m_util.fm().set(v, ebits, sbits, mrm, q.to_mpq()); expr_ref sgn(m), sig(m), exp(m), unbiased_exp(m); sgn = m_bv_util.mk_numeral((m_util.fm().sgn(v)) ? 1 : 0, 1); sig = m_bv_util.mk_numeral(m_util.fm().sig(v), sbits - 1); unbiased_exp = m_bv_util.mk_numeral(m_util.fm().exp(v), ebits); mk_bias(unbiased_exp, exp); result = m_util.mk_fp(sgn, exp, sig); } } else if (m_util.au().is_numeral(x)) { rational q; bool is_int; m_util.au().is_numeral(x, q, is_int); if (m_util.au().is_zero(x)) mk_pzero(f, result); else { expr_ref rm_nta(m), rm_nte(m), rm_tp(m), rm_tn(m), rm_tz(m); mk_is_rm(bv_rm, BV_RM_TIES_TO_AWAY, rm_nta); mk_is_rm(bv_rm, BV_RM_TIES_TO_EVEN, rm_nte); mk_is_rm(bv_rm, BV_RM_TO_POSITIVE, rm_tp); mk_is_rm(bv_rm, BV_RM_TO_NEGATIVE, rm_tn); mk_is_rm(bv_rm, BV_RM_TO_ZERO, rm_tz); scoped_mpf v_nta(m_mpf_manager), v_nte(m_mpf_manager), v_tp(m_mpf_manager); scoped_mpf v_tn(m_mpf_manager), v_tz(m_mpf_manager); m_util.fm().set(v_nta, ebits, sbits, MPF_ROUND_NEAREST_TAWAY, q.to_mpq()); m_util.fm().set(v_nte, ebits, sbits, MPF_ROUND_NEAREST_TEVEN, q.to_mpq()); m_util.fm().set(v_tp, ebits, sbits, MPF_ROUND_TOWARD_POSITIVE, q.to_mpq()); m_util.fm().set(v_tn, ebits, sbits, MPF_ROUND_TOWARD_NEGATIVE, q.to_mpq()); m_util.fm().set(v_tz, ebits, sbits, MPF_ROUND_TOWARD_ZERO, q.to_mpq()); expr_ref v1(m), v2(m), v3(m), v4(m); expr_ref sgn(m), sig(m), exp(m), unbiased_exp(m); sgn = m_bv_util.mk_numeral((m_util.fm().sgn(v_nta)) ? 1 : 0, 1); sig = m_bv_util.mk_numeral(m_util.fm().sig(v_nta), sbits - 1); unbiased_exp = m_bv_util.mk_numeral(m_util.fm().exp(v_nta), ebits); mk_bias(unbiased_exp, exp); v1 = m_util.mk_fp(sgn, exp, sig); sgn = m_bv_util.mk_numeral((m_util.fm().sgn(v_nte)) ? 1 : 0, 1); sig = m_bv_util.mk_numeral(m_util.fm().sig(v_nte), sbits - 1); unbiased_exp = m_bv_util.mk_numeral(m_util.fm().exp(v_nte), ebits); mk_bias(unbiased_exp, exp); v2 = m_util.mk_fp(sgn, exp, sig); sgn = m_bv_util.mk_numeral((m_util.fm().sgn(v_tp)) ? 1 : 0, 1); sig = m_bv_util.mk_numeral(m_util.fm().sig(v_tp), sbits - 1); unbiased_exp = m_bv_util.mk_numeral(m_util.fm().exp(v_tp), ebits); mk_bias(unbiased_exp, exp); v3 = m_util.mk_fp(sgn, exp, sig); sgn = m_bv_util.mk_numeral((m_util.fm().sgn(v_tn)) ? 1 : 0, 1); sig = m_bv_util.mk_numeral(m_util.fm().sig(v_tn), sbits - 1); unbiased_exp = m_bv_util.mk_numeral(m_util.fm().exp(v_tn), ebits); mk_bias(unbiased_exp, exp); v4 = m_util.mk_fp(sgn, exp, sig); sgn = m_bv_util.mk_numeral((m_util.fm().sgn(v_tp)) ? 1 : 0, 1); sig = m_bv_util.mk_numeral(m_util.fm().sig(v_tp), sbits - 1); unbiased_exp = m_bv_util.mk_numeral(m_util.fm().exp(v_tp), ebits); mk_bias(unbiased_exp, exp); result = m_util.mk_fp(sgn, exp, sig); mk_ite(rm_tn, v4, result, result); mk_ite(rm_tp, v3, result, result); mk_ite(rm_nte, v2, result, result); mk_ite(rm_nta, v1, result, result); } } else { SASSERT(!m_arith_util.is_numeral(x)); bv_util & bu = m_bv_util; arith_util & au = m_arith_util; expr_ref bv0(m), bv1(m), zero(m), two(m); bv0 = bu.mk_numeral(0, 1); bv1 = bu.mk_numeral(1, 1); zero = au.mk_numeral(rational(0), false); two = au.mk_numeral(rational(2), false); expr_ref sgn(m), sig(m), exp(m); sgn = mk_fresh_const("fpa2bv_to_fp_real_sgn", 1); sig = mk_fresh_const("fpa2bv_to_fp_real_sig", sbits + 4); exp = mk_fresh_const("fpa2bv_to_fp_real_exp", ebits + 2); expr_ref rme(bv_rm, m); round(s, rme, sgn, sig, exp, result); expr * e = m.mk_eq(m_util.mk_to_real(result), x); m_extra_assertions.push_back(e); } SASSERT(is_well_sorted(m, result)); } void fpa2bv_converter::mk_to_fp_real_int(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { // rm + real + int -> float SASSERT(m_util.is_float(f->get_range())); unsigned ebits = m_util.get_ebits(f->get_range()); unsigned sbits = m_util.get_sbits(f->get_range()); SASSERT(m_util.is_bv2rm(args[0])); expr * bv_rm = to_app(args[0])->get_arg(0); SASSERT((m_arith_util.is_int(args[1]) && m_arith_util.is_real(args[2])) || (m_arith_util.is_real(args[1]) && m_arith_util.is_int(args[2]))); rational q, e; if (m_arith_util.is_int(args[1]) && m_arith_util.is_real(args[2])) { if (!m_arith_util.is_numeral(args[1], e) || !m_arith_util.is_numeral(args[2], q)) UNREACHABLE(); } else { if (!m_arith_util.is_numeral(args[2], e) || !m_arith_util.is_numeral(args[1], q)) UNREACHABLE(); } SASSERT(e.is_int64()); SASSERT(m_mpz_manager.eq(e.to_mpq().denominator(), 1)); if (q.is_zero()) return mk_pzero(f, result); else { scoped_mpf nte(m_mpf_manager), nta(m_mpf_manager), tp(m_mpf_manager), tn(m_mpf_manager), tz(m_mpf_manager); m_mpf_manager.set(nte, ebits, sbits, MPF_ROUND_NEAREST_TEVEN, e.to_mpq().numerator(), q.to_mpq()); m_mpf_manager.set(nta, ebits, sbits, MPF_ROUND_NEAREST_TAWAY, e.to_mpq().numerator(), q.to_mpq()); m_mpf_manager.set(tp, ebits, sbits, MPF_ROUND_TOWARD_POSITIVE, e.to_mpq().numerator(), q.to_mpq()); m_mpf_manager.set(tn, ebits, sbits, MPF_ROUND_TOWARD_NEGATIVE, e.to_mpq().numerator(), q.to_mpq()); m_mpf_manager.set(tz, ebits, sbits, MPF_ROUND_TOWARD_ZERO, e.to_mpq().numerator(), q.to_mpq()); app_ref a_nte(m), a_nta(m), a_tp(m), a_tn(m), a_tz(m); a_nte = m_plugin->mk_numeral(nte); a_nta = m_plugin->mk_numeral(nta); a_tp = m_plugin->mk_numeral(tp); a_tn = m_plugin->mk_numeral(tn); a_tz = m_plugin->mk_numeral(tz); expr_ref bv_nte(m), bv_nta(m), bv_tp(m), bv_tn(m), bv_tz(m); mk_numeral(a_nte->get_decl(), 0, nullptr, bv_nte); mk_numeral(a_nta->get_decl(), 0, nullptr, bv_nta); mk_numeral(a_tp->get_decl(), 0, nullptr, bv_tp); mk_numeral(a_tn->get_decl(), 0, nullptr, bv_tn); mk_numeral(a_tz->get_decl(), 0, nullptr, bv_tz); expr_ref c1(m), c2(m), c3(m), c4(m); c1 = m.mk_eq(bv_rm, m_bv_util.mk_numeral(BV_RM_TO_POSITIVE, 3)); c2 = m.mk_eq(bv_rm, m_bv_util.mk_numeral(BV_RM_TO_POSITIVE, 3)); c3 = m.mk_eq(bv_rm, m_bv_util.mk_numeral(BV_RM_TIES_TO_AWAY, 3)); c4 = m.mk_eq(bv_rm, m_bv_util.mk_numeral(BV_RM_TIES_TO_EVEN, 3)); mk_ite(c1, bv_tn, bv_tz, result); mk_ite(c2, bv_tp, result, result); mk_ite(c3, bv_nta, result, result); mk_ite(c4, bv_nte, result, result); } } void fpa2bv_converter::mk_to_real(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { TRACE("fpa2bv_to_real", for (unsigned i = 0; i < num; i++) tout << "arg" << i << " = " << mk_ismt2_pp(args[i], m) << std::endl;); SASSERT(num == 1); SASSERT(f->get_num_parameters() == 0); SASSERT(is_app_of(args[0], m_plugin->get_family_id(), OP_FPA_FP)); expr * x = args[0]; sort * s = m.get_sort(x); unsigned ebits = m_util.get_ebits(s); unsigned sbits = m_util.get_sbits(s); sort * rs = m_arith_util.mk_real(); expr_ref x_is_nan(m), x_is_inf(m), x_is_zero(m); mk_is_nan(x, x_is_nan); mk_is_inf(x, x_is_inf); mk_is_zero(x, x_is_zero); expr_ref sgn(m), sig(m), exp(m), lz(m); unpack(x, sgn, sig, exp, lz, true); // sig is of the form [1].[sigbits] SASSERT(m_bv_util.get_bv_size(sgn) == 1); SASSERT(m_bv_util.get_bv_size(sig) == sbits); SASSERT(m_bv_util.get_bv_size(exp) == ebits); expr_ref rsig(m), bit(m), bit_eq_1(m), rsig_mul_2(m), zero(m), one(m), two(m), bv0(m), bv1(m); zero = m_arith_util.mk_numeral(rational(0), rs); one = m_arith_util.mk_numeral(rational(1), rs); two = m_arith_util.mk_numeral(rational(2), rs); bv0 = m_bv_util.mk_numeral(0, 1); bv1 = m_bv_util.mk_numeral(1, 1); rsig = one; for (unsigned i = sbits - 2; i != (unsigned)-1; i--) { bit = m_bv_util.mk_extract(i, i, sig); bit_eq_1 = m.mk_eq(bit, bv1); rsig_mul_2 = m_arith_util.mk_mul(rsig, two); rsig = m_arith_util.mk_add(rsig_mul_2, m.mk_ite(bit_eq_1, one, zero)); } const mpz & p2 = fu().fm().m_powers2(sbits - 1); expr_ref ep2(m); ep2 = m_arith_util.mk_numeral(rational(p2), false); rsig = m_arith_util.mk_div(rsig, ep2); dbg_decouple("fpa2bv_to_real_ep2", ep2); dbg_decouple("fpa2bv_to_real_rsig", rsig); expr_ref exp_n(m), exp_p(m), exp_is_neg(m), exp_abs(m); exp_is_neg = m.mk_eq(m_bv_util.mk_extract(ebits - 1, ebits - 1, exp), bv1); dbg_decouple("fpa2bv_to_real_exp_is_neg", exp_is_neg); exp_p = m_bv_util.mk_sign_extend(1, exp); exp_n = m_bv_util.mk_bv_neg(exp_p); exp_abs = m.mk_ite(exp_is_neg, exp_n, exp_p); dbg_decouple("fpa2bv_to_real_exp_abs", exp); SASSERT(m_bv_util.get_bv_size(exp_abs) == ebits + 1); expr_ref exp2(m), exp2_mul_2(m), prev_bit(m); exp2 = zero; for (unsigned i = ebits; i != (unsigned)-1; i--) { bit = m_bv_util.mk_extract(i, i, exp_abs); bit_eq_1 = m.mk_eq(bit, bv1); exp2_mul_2 = m_arith_util.mk_mul(exp2, two); exp2 = m_arith_util.mk_add(exp2_mul_2, m.mk_ite(bit_eq_1, one, zero)); prev_bit = bit; } expr_ref one_div_exp2(m); one_div_exp2 = m_arith_util.mk_div(one, exp2); exp2 = m.mk_ite(exp_is_neg, one_div_exp2, exp2); dbg_decouple("fpa2bv_to_real_exp2", exp2); expr_ref res(m), two_exp2(m), minus_res(m), sgn_is_1(m); two_exp2 = m_arith_util.mk_power(two, exp2); res = m_arith_util.mk_mul(rsig, two_exp2); minus_res = m_arith_util.mk_uminus(res); sgn_is_1 = m.mk_eq(sgn, bv1); res = m.mk_ite(sgn_is_1, minus_res, res); dbg_decouple("fpa2bv_to_real_sig_times_exp2", res); TRACE("fpa2bv_to_real", tout << "rsig = " << mk_ismt2_pp(rsig, m) << std::endl; tout << "exp2 = " << mk_ismt2_pp(exp2, m) << std::endl;); expr_ref unspec(m); mk_to_real_unspecified(f, num, args, unspec); result = m.mk_ite(x_is_zero, zero, res); result = m.mk_ite(x_is_inf, unspec, result); result = m.mk_ite(x_is_nan, unspec, result); SASSERT(is_well_sorted(m, result)); } void fpa2bv_converter::mk_to_fp_signed(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { TRACE("fpa2bv_to_fp_signed", for (unsigned i = 0; i < num; i++) tout << "arg" << i << " = " << mk_ismt2_pp(args[i], m) << std::endl;); // This is a conversion from signed bitvector to float: // ; from signed machine integer, represented as a 2's complement bit vector // ((_ to_fp eb sb) RoundingMode (_ BitVec m) (_ FloatingPoint eb sb)) // Semantics: // Let b in[[(_ BitVec m)]] and let n be the signed integer represented by b (in 2's complement format). // [[(_ to_fp eb sb)]](r, b) = +/ -infinity if n is too large / too small to be represented as a finite // number of [[(_ FloatingPoint eb sb)]]; [[(_ to_fp eb sb)]](r, x) = y otherwise, where y is the finite // number such that [[fp.to_real]](y) is closest to n according to rounding mode r. SASSERT(num == 2); SASSERT(m_util.is_float(f->get_range())); SASSERT(m_util.is_bv2rm(args[0])); SASSERT(m_bv_util.is_bv(args[1])); expr_ref rm(m), x(m); rm = to_app(args[0])->get_arg(0); x = args[1]; dbg_decouple("fpa2bv_to_fp_signed_x", x); unsigned ebits = m_util.get_ebits(f->get_range()); unsigned sbits = m_util.get_sbits(f->get_range()); unsigned bv_sz = m_bv_util.get_bv_size(x); SASSERT(m_bv_util.get_bv_size(rm) == 3); expr_ref bv0_1(m), bv1_1(m), bv0_sz(m), bv1_sz(m); bv0_1 = m_bv_util.mk_numeral(0, 1); bv1_1 = m_bv_util.mk_numeral(1, 1); bv0_sz = m_bv_util.mk_numeral(0, bv_sz); bv1_sz = m_bv_util.mk_numeral(1, bv_sz); expr_ref is_zero(m), nzero(m), pzero(m), ninf(m), pinf(m); is_zero = m.mk_eq(x, bv0_sz); mk_nzero(f, nzero); mk_pzero(f, pzero); mk_ninf(f, ninf); mk_pinf(f, pinf); // Special case: x == 0 -> p/n zero expr_ref c1(m), v1(m); c1 = is_zero; v1 = pzero; // Special case: x != 0 expr_ref is_neg_bit(m), exp_too_large(m), sig_4(m), exp_2(m); expr_ref is_neg(m), x_abs(m), neg_x(m); is_neg_bit = m_bv_util.mk_extract(bv_sz - 1, bv_sz - 1, x); is_neg = m.mk_eq(is_neg_bit, bv1_1); neg_x = m_bv_util.mk_bv_neg(x); // overflow problem? x_abs = m.mk_ite(is_neg, neg_x, x); dbg_decouple("fpa2bv_to_fp_signed_is_neg", is_neg); // x_abs has an extra bit in the front. // x_abs is [bv_sz-1, bv_sz-2] . [bv_sz-3 ... 0] * 2^(bv_sz-2) // bv_sz-2 is the "1.0" bit for the rounder. expr_ref lz(m), e_bv_sz(m), e_rest_sz(m); mk_leading_zeros(x_abs, bv_sz, lz); e_bv_sz = m_bv_util.mk_numeral(bv_sz, bv_sz); e_rest_sz = m_bv_util.mk_bv_sub(e_bv_sz, lz); SASSERT(m_bv_util.get_bv_size(lz) == m_bv_util.get_bv_size(e_bv_sz)); dbg_decouple("fpa2bv_to_fp_signed_lz", lz); expr_ref shifted_sig(m); shifted_sig = m_bv_util.mk_bv_shl(x_abs, lz); expr_ref sticky(m); // shifted_sig is [bv_sz-1, bv_sz-2] . [bv_sz-3 ... 0] * 2^(bv_sz-2) * 2^(-lz) unsigned sig_sz = sbits + 4; // we want extra rounding bits. if (sig_sz <= bv_sz) { expr_ref sig_rest(m); sig_4 = m_bv_util.mk_extract(bv_sz - 1, bv_sz - sig_sz + 1, shifted_sig); // one short sig_rest = m_bv_util.mk_extract(bv_sz - sig_sz, 0, shifted_sig); sticky = m.mk_app(m_bv_util.get_fid(), OP_BREDOR, sig_rest.get()); sig_4 = m_bv_util.mk_concat(sig_4, sticky); } else { unsigned extra_bits = sig_sz - bv_sz; expr_ref extra_zeros(m); extra_zeros = m_bv_util.mk_numeral(0, extra_bits); sig_4 = m_bv_util.mk_concat(shifted_sig, extra_zeros); lz = m_bv_util.mk_bv_add(m_bv_util.mk_concat(extra_zeros, lz), m_bv_util.mk_numeral(extra_bits, sig_sz)); bv_sz = bv_sz + extra_bits; SASSERT(is_well_sorted(m, lz)); } SASSERT(m_bv_util.get_bv_size(sig_4) == sig_sz); dbg_decouple("fpa2bv_to_fp_signed_sig_4", sig_4); expr_ref s_exp(m), exp_rest(m); s_exp = m_bv_util.mk_bv_sub(m_bv_util.mk_numeral(bv_sz - 2, bv_sz), lz); // s_exp = (bv_sz-2) + (-lz) signed SASSERT(m_bv_util.get_bv_size(s_exp) == bv_sz); dbg_decouple("fpa2bv_to_fp_signed_s_exp", s_exp); unsigned exp_sz = ebits + 2; // (+2 for rounder) exp_2 = m_bv_util.mk_extract(exp_sz - 1, 0, s_exp); // the remaining bits are 0 if ebits is large enough. exp_too_large = m.mk_false(); // The exponent is at most bv_sz, i.e., we need ld(bv_sz)+1 ebits. // exp < bv_sz (+sign bit which is [0]) unsigned exp_worst_case_sz = (unsigned)((log((double)bv_sz) / log((double)2)) + 1.0); TRACE("fpa2bv_to_fp_signed", tout << "exp worst case sz: " << exp_worst_case_sz << std::endl;); if (exp_sz < exp_worst_case_sz) { // exp_sz < exp_worst_case_sz and exp >= 0. // Take the maximum legal exponent; this // allows us to keep the most precision. expr_ref max_exp(m), max_exp_bvsz(m), zero_sig_sz(m); mk_max_exp(exp_sz, max_exp); max_exp_bvsz = m_bv_util.mk_zero_extend(bv_sz - exp_sz, max_exp); exp_too_large = m_bv_util.mk_sle( m_bv_util.mk_bv_add(max_exp_bvsz, m_bv_util.mk_numeral(1, bv_sz)), s_exp); zero_sig_sz = m_bv_util.mk_numeral(0, sig_sz); sig_4 = m.mk_ite(exp_too_large, zero_sig_sz, sig_4); exp_2 = m.mk_ite(exp_too_large, max_exp, exp_2); } dbg_decouple("fpa2bv_to_fp_signed_exp_too_large", exp_too_large); expr_ref sgn(m), sig(m), exp(m); sgn = is_neg_bit; sig = sig_4; exp = exp_2; dbg_decouple("fpa2bv_to_fp_signed_sgn", sgn); dbg_decouple("fpa2bv_to_fp_signed_sig", sig); dbg_decouple("fpa2bv_to_fp_signed_exp", exp); SASSERT(m_bv_util.get_bv_size(sig) == sbits + 4); SASSERT(m_bv_util.get_bv_size(exp) == ebits + 2); expr_ref v2(m); round(f->get_range(), rm, sgn, sig, exp, v2); mk_ite(c1, v1, v2, result); } void fpa2bv_converter::mk_to_fp_unsigned(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { TRACE("fpa2bv_to_fp_unsigned", for (unsigned i = 0; i < num; i++) tout << "arg" << i << " = " << mk_ismt2_pp(args[i], m) << std::endl;); // This is a conversion from unsigned bitvector to float: // ((_ to_fp_unsigned eb sb) RoundingMode (_ BitVec m) (_ FloatingPoint eb sb)) // Semantics: // Let b in[[(_ BitVec m)]] and let n be the unsigned integer represented by b. // [[(_ to_fp_unsigned eb sb)]](r, x) = +infinity if n is too large to be // represented as a finite number of[[(_ FloatingPoint eb sb)]]; // [[(_ to_fp_unsigned eb sb)]](r, x) = y otherwise, where y is the finite number // such that[[fp.to_real]](y) is closest to n according to rounding mode r. SASSERT(num == 2); SASSERT(m_util.is_float(f->get_range())); SASSERT(m_util.is_bv2rm(args[0])); SASSERT(m_bv_util.is_bv(args[1])); expr_ref rm(m), x(m); rm = to_app(args[0])->get_arg(0); x = args[1]; dbg_decouple("fpa2bv_to_fp_unsigned_x", x); unsigned ebits = m_util.get_ebits(f->get_range()); unsigned sbits = m_util.get_sbits(f->get_range()); unsigned bv_sz = m_bv_util.get_bv_size(x); SASSERT(m_bv_util.get_bv_size(rm) == 3); expr_ref bv0_1(m), bv1_1(m), bv0_sz(m), bv1_sz(m); bv0_1 = m_bv_util.mk_numeral(0, 1); bv1_1 = m_bv_util.mk_numeral(1, 1); bv0_sz = m_bv_util.mk_numeral(0, bv_sz); bv1_sz = m_bv_util.mk_numeral(1, bv_sz); expr_ref is_zero(m), nzero(m), pzero(m), ninf(m), pinf(m); is_zero = m.mk_eq(x, bv0_sz); mk_nzero(f, nzero); mk_pzero(f, pzero); mk_ninf(f, ninf); mk_pinf(f, pinf); // Special case: x == 0 -> p/n zero expr_ref c1(m), v1(m); c1 = is_zero; v1 = pzero; // Special case: x != 0 expr_ref exp_too_large(m), sig_4(m), exp_2(m); // x is [bv_sz-1] . [bv_sz-2 ... 0] * 2^(bv_sz-1) // bv_sz-1 is the "1.0" bit for the rounder. expr_ref lz(m), e_bv_sz(m), e_rest_sz(m); mk_leading_zeros(x, bv_sz, lz); e_bv_sz = m_bv_util.mk_numeral(bv_sz, bv_sz); e_rest_sz = m_bv_util.mk_bv_sub(e_bv_sz, lz); SASSERT(m_bv_util.get_bv_size(lz) == m_bv_util.get_bv_size(e_bv_sz)); dbg_decouple("fpa2bv_to_fp_unsigned_lz", lz); expr_ref shifted_sig(m); shifted_sig = m_bv_util.mk_bv_shl(x, lz); expr_ref sticky(m); // shifted_sig is [bv_sz-1] . [bv_sz-2 ... 0] * 2^(bv_sz-1) * 2^(-lz) unsigned sig_sz = sbits + 4; // we want extra rounding bits. if (sig_sz <= bv_sz) { expr_ref sig_rest(m); sig_4 = m_bv_util.mk_extract(bv_sz - 1, bv_sz - sig_sz + 1, shifted_sig); // one short sig_rest = m_bv_util.mk_extract(bv_sz - sig_sz, 0, shifted_sig); sticky = m.mk_app(m_bv_util.get_fid(), OP_BREDOR, sig_rest.get()); sig_4 = m_bv_util.mk_concat(sig_4, sticky); } else { unsigned extra_bits = sig_sz - bv_sz; expr_ref extra_zeros(m); extra_zeros = m_bv_util.mk_numeral(0, extra_bits); sig_4 = m_bv_util.mk_concat(shifted_sig, extra_zeros); lz = m_bv_util.mk_bv_add(m_bv_util.mk_concat(extra_zeros, lz), m_bv_util.mk_numeral(extra_bits, sig_sz)); bv_sz = bv_sz + extra_bits; SASSERT(is_well_sorted(m, lz)); } SASSERT(m_bv_util.get_bv_size(sig_4) == sig_sz); expr_ref s_exp(m), exp_rest(m); s_exp = m_bv_util.mk_bv_sub(m_bv_util.mk_numeral(bv_sz - 2, bv_sz), lz); // s_exp = (bv_sz-2) + (-lz) signed SASSERT(m_bv_util.get_bv_size(s_exp) == bv_sz); unsigned exp_sz = ebits + 2; // (+2 for rounder) exp_2 = m_bv_util.mk_extract(exp_sz - 1, 0, s_exp); // the remaining bits are 0 if ebits is large enough. exp_too_large = m.mk_false(); // This is always in range. // The exponent is at most bv_sz, i.e., we need ld(bv_sz)+1 ebits. // exp < bv_sz (+sign bit which is [0]) unsigned exp_worst_case_sz = (unsigned)((log((double)bv_sz) / log((double)2)) + 1.0); if (exp_sz < exp_worst_case_sz) { // exp_sz < exp_worst_case_sz and exp >= 0. // Take the maximum legal exponent; this // allows us to keep the most precision. expr_ref max_exp(m), max_exp_bvsz(m), zero_sig_sz(m); mk_max_exp(exp_sz, max_exp); max_exp_bvsz = m_bv_util.mk_zero_extend(bv_sz - exp_sz, max_exp); exp_too_large = m_bv_util.mk_sle(m_bv_util.mk_bv_add( max_exp_bvsz, m_bv_util.mk_numeral(1, bv_sz)), s_exp); zero_sig_sz = m_bv_util.mk_numeral(0, sig_sz); sig_4 = m.mk_ite(exp_too_large, zero_sig_sz, sig_4); exp_2 = m.mk_ite(exp_too_large, max_exp, exp_2); } dbg_decouple("fpa2bv_to_fp_unsigned_exp_too_large", exp_too_large); expr_ref sgn(m), sig(m), exp(m); sgn = bv0_1; sig = sig_4; exp = exp_2; dbg_decouple("fpa2bv_to_fp_unsigned_sgn", sgn); dbg_decouple("fpa2bv_to_fp_unsigned_sig", sig); dbg_decouple("fpa2bv_to_fp_unsigned_exp", exp); SASSERT(m_bv_util.get_bv_size(sig) == sbits + 4); SASSERT(m_bv_util.get_bv_size(exp) == ebits + 2); expr_ref v2(m); round(f->get_range(), rm, sgn, sig, exp, v2); mk_ite(c1, v1, v2, result); } void fpa2bv_converter::mk_to_ieee_bv(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 1); expr_ref x(m), x_is_nan(m); expr_ref sgn(m), s(m), e(m); x = args[0]; split_fp(x, sgn, e, s); mk_is_nan(x, x_is_nan); expr_ref unspec(m); mk_to_ieee_bv_unspecified(f, num, args, unspec); expr_ref sgn_e_s(m); join_fp(x, sgn_e_s); m_simp.mk_ite(x_is_nan, unspec, sgn_e_s, result); TRACE("fpa2bv_to_ieee_bv", tout << "result=" << mk_ismt2_pp(result, m) << std::endl;); SASSERT(is_well_sorted(m, result)); } void fpa2bv_converter::mk_to_ieee_bv_unspecified(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 1); SASSERT(m_util.is_float(args[0])); unsigned ebits = f->get_domain()[0]->get_parameter(0).get_int(); unsigned sbits = f->get_domain()[0]->get_parameter(1).get_int(); if (m_hi_fp_unspecified) { mk_nan(f->get_domain()[0], result); join_fp(result, result); } else { expr_ref nw = nan_wrap(args[0]); sort * domain[1] = { m.get_sort(nw) }; func_decl * f_bv = mk_bv_uf(f, domain, f->get_range()); result = m.mk_app(f_bv, nw); expr_ref exp_bv(m), exp_all_ones(m); exp_bv = m_bv_util.mk_extract(ebits+sbits-2, sbits-1, result); exp_all_ones = m.mk_eq(exp_bv, m_bv_util.mk_bv_neg(m_bv_util.mk_numeral(1, ebits))); m_extra_assertions.push_back(std::move(exp_all_ones)); expr_ref sig_bv(m), sig_is_non_zero(m); sig_bv = m_bv_util.mk_extract(sbits-2, 0, result); sig_is_non_zero = m.mk_not(m.mk_eq(sig_bv, m_bv_util.mk_numeral(0, sbits-1))); m_extra_assertions.push_back(std::move(sig_is_non_zero)); } TRACE("fpa2bv_to_ieee_bv_unspecified", tout << "result=" << mk_ismt2_pp(result, m) << std::endl;); SASSERT(is_well_sorted(m, result)); } void fpa2bv_converter::mk_to_bv(func_decl * f, unsigned num, expr * const * args, bool is_signed, expr_ref & result) { TRACE("fpa2bv_to_bv", for (unsigned i = 0; i < num; i++) tout << "arg" << i << " = " << mk_ismt2_pp(args[i], m) << std::endl;); SASSERT(num == 2); SASSERT(m_util.is_bv2rm(args[0])); SASSERT(m_util.is_float(args[1])); expr * rm = to_app(args[0])->get_arg(0); expr * x = args[1]; sort * xs = m.get_sort(x); sort * bv_srt = f->get_range(); expr_ref sgn(m), sig(m), exp(m), lz(m); unpack(x, sgn, sig, exp, lz, true); unsigned ebits = m_util.get_ebits(xs); unsigned sbits = m_util.get_sbits(xs); unsigned bv_sz = (unsigned)f->get_parameter(0).get_int(); expr_ref bv0(m), bv1(m); bv0 = m_bv_util.mk_numeral(0, 1); bv1 = m_bv_util.mk_numeral(1, 1); expr_ref x_is_nan(m), x_is_inf(m), x_is_zero(m), x_is_neg(m), x_is_nzero(m); mk_is_nan(x, x_is_nan); mk_is_inf(x, x_is_inf); mk_is_zero(x, x_is_zero); mk_is_neg(x, x_is_neg); mk_is_nzero(x, x_is_nzero); // NaN, Inf, or negative (except -0) -> unspecified expr_ref c1(m), v1(m), unspec_v(m); c1 = m.mk_or(x_is_nan, x_is_inf); mk_to_bv_unspecified(f, num, args, unspec_v); v1 = unspec_v; dbg_decouple("fpa2bv_to_bv_c1", c1); // +-0 -> 0 expr_ref c2(m), v2(m); c2 = x_is_zero; v2 = m_bv_util.mk_numeral(rational(0), bv_srt); dbg_decouple("fpa2bv_to_bv_c2", c2); // Otherwise... dbg_decouple("fpa2bv_to_bv_sgn", sgn); dbg_decouple("fpa2bv_to_bv_sig", sig); dbg_decouple("fpa2bv_to_bv_exp", exp); dbg_decouple("fpa2bv_to_bv_lz", lz); // sig is of the form +- [1].[sig] * 2^(exp-lz) SASSERT(m_bv_util.get_bv_size(sgn) == 1); SASSERT(m_bv_util.get_bv_size(sig) == sbits); SASSERT(m_bv_util.get_bv_size(exp) == ebits); SASSERT(m_bv_util.get_bv_size(lz) == ebits); unsigned sig_sz = sbits; if (sig_sz < (bv_sz + 3)) sig = m_bv_util.mk_concat(sig, m_bv_util.mk_numeral(0, bv_sz-sig_sz+3)); sig_sz = m_bv_util.get_bv_size(sig); SASSERT(sig_sz >= (bv_sz + 3)); // x is of the form +- [1].[sig][r][g][s] ... and at least bv_sz + 3 long expr_ref exp_m_lz(m), e_m_lz_m_bv_sz(m), shift(m), is_neg_shift(m), big_sig(m); exp_m_lz = m_bv_util.mk_bv_sub(m_bv_util.mk_sign_extend(2, exp), m_bv_util.mk_zero_extend(2, lz)); // big_sig is +- [... bv_sz+2 bits ...][1].[r][ ... sbits-1 ... ] big_sig = m_bv_util.mk_concat(m_bv_util.mk_zero_extend(bv_sz + 2, sig), bv0); unsigned big_sig_sz = sig_sz+1+bv_sz+2; SASSERT(m_bv_util.get_bv_size(big_sig) == big_sig_sz); is_neg_shift = m_bv_util.mk_sle(exp_m_lz, m_bv_util.mk_numeral(0, ebits+2)); shift = m.mk_ite(is_neg_shift, m_bv_util.mk_bv_neg(exp_m_lz), exp_m_lz); if (ebits+2 < big_sig_sz) shift = m_bv_util.mk_zero_extend(big_sig_sz-ebits-2, shift); else if (ebits+2 > big_sig_sz) { expr_ref upper(m); upper = m_bv_util.mk_extract(big_sig_sz, ebits+2, shift); shift = m_bv_util.mk_extract(ebits+1, 0, shift); shift = m.mk_ite(m.mk_eq(upper, m_bv_util.mk_numeral(0, m_bv_util.get_bv_size(upper))), shift, m_bv_util.mk_numeral(big_sig_sz-1, ebits+2)); } dbg_decouple("fpa2bv_to_bv_shift_uncapped", shift); SASSERT(m_bv_util.get_bv_size(shift) == m_bv_util.get_bv_size(big_sig)); dbg_decouple("fpa2bv_to_bv_big_sig", big_sig); expr_ref shift_limit(m); shift_limit = m_bv_util.mk_numeral(bv_sz+2, m_bv_util.get_bv_size(shift)); shift = m.mk_ite(m_bv_util.mk_ule(shift, shift_limit), shift, shift_limit); dbg_decouple("fpa2bv_to_bv_shift_limit", shift_limit); dbg_decouple("fpa2bv_to_bv_is_neg_shift", is_neg_shift); dbg_decouple("fpa2bv_to_bv_shift", shift); expr_ref big_sig_shifted(m), int_part(m), last(m), round(m), stickies(m), sticky(m); big_sig_shifted = m.mk_ite(is_neg_shift, m_bv_util.mk_bv_lshr(big_sig, shift), m_bv_util.mk_bv_shl(big_sig, shift)); int_part = m_bv_util.mk_extract(big_sig_sz-1, big_sig_sz-(bv_sz+3), big_sig_shifted); SASSERT(m_bv_util.get_bv_size(int_part) == bv_sz+3); last = m_bv_util.mk_extract(big_sig_sz-(bv_sz+3), big_sig_sz-(bv_sz+3), big_sig_shifted); round = m_bv_util.mk_extract(big_sig_sz-(bv_sz+4), big_sig_sz-(bv_sz+4), big_sig_shifted); stickies = m_bv_util.mk_extract(big_sig_sz-(bv_sz+5), 0, big_sig_shifted); sticky = m.mk_app(m_bv_util.get_fid(), OP_BREDOR, stickies.get()); dbg_decouple("fpa2bv_to_bv_big_sig_shifted", big_sig_shifted); dbg_decouple("fpa2bv_to_bv_int_part", int_part); dbg_decouple("fpa2bv_to_bv_last", last); dbg_decouple("fpa2bv_to_bv_round", round); dbg_decouple("fpa2bv_to_bv_sticky", sticky); expr_ref rounding_decision(m); rounding_decision = mk_rounding_decision(rm, sgn, last, round, sticky); SASSERT(m_bv_util.get_bv_size(rounding_decision) == 1); dbg_decouple("fpa2bv_to_bv_rounding_decision", rounding_decision); expr_ref inc(m), pre_rounded(m); inc = m_bv_util.mk_zero_extend(bv_sz+2, rounding_decision); pre_rounded = m_bv_util.mk_bv_add(int_part, inc); dbg_decouple("fpa2bv_to_bv_inc", inc); dbg_decouple("fpa2bv_to_bv_pre_rounded", pre_rounded); expr_ref incd(m), pr_is_zero(m), ovfl(m); incd = m.mk_eq(rounding_decision, bv1); pr_is_zero = m.mk_eq(pre_rounded, m_bv_util.mk_numeral(0, bv_sz + 3)); ovfl = m.mk_and(incd, pr_is_zero); dbg_decouple("fpa2bv_to_bv_incd", incd); dbg_decouple("fpa2bv_to_bv_ovfl", ovfl); expr_ref ul(m), in_range(m); if (!is_signed) { ul = m_bv_util.mk_zero_extend(3, m_bv_util.mk_bv_neg(m_bv_util.mk_numeral(1, bv_sz))); in_range = m.mk_and(m.mk_or(m.mk_not(x_is_neg), m.mk_eq(pre_rounded, m_bv_util.mk_numeral(0, bv_sz+3))), m.mk_not(ovfl), m_bv_util.mk_ule(pre_rounded, ul)); } else { expr_ref ll(m); 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); } dbg_decouple("fpa2bv_to_bv_in_range_ovfl", ovfl); dbg_decouple("fpa2bv_to_bv_in_range_ul", ul); dbg_decouple("fpa2bv_to_bv_in_range", in_range); expr_ref rounded(m); rounded = m_bv_util.mk_extract(bv_sz-1, 0, pre_rounded); dbg_decouple("fpa2bv_to_bv_rounded", rounded); result = m.mk_ite(m.mk_not(in_range), unspec_v, rounded); result = m.mk_ite(c2, v2, result); result = m.mk_ite(c1, v1, result); SASSERT(is_well_sorted(m, result)); } void fpa2bv_converter::mk_to_ubv(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { TRACE("fpa2bv_to_ubv", for (unsigned i = 0; i < num; i++) tout << "arg" << i << " = " << mk_ismt2_pp(args[i], m) << std::endl;); mk_to_bv(f, num, args, false, result); } void fpa2bv_converter::mk_to_sbv(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { TRACE("fpa2bv_to_sbv", for (unsigned i = 0; i < num; i++) tout << "arg" << i << " = " << mk_ismt2_pp(args[i], m) << std::endl;); mk_to_bv(f, num, args, true, result); } expr_ref fpa2bv_converter::nan_wrap(expr * n) { expr_ref n_bv(m), arg_is_nan(m), nan(m), nan_bv(m), res(m); mk_is_nan(n, arg_is_nan); mk_nan(m.get_sort(n), nan); join_fp(nan, nan_bv); join_fp(n, n_bv); res = expr_ref(m.mk_ite(arg_is_nan, nan_bv, n_bv), m); SASSERT(is_well_sorted(m, res)); return res; } void fpa2bv_converter::mk_to_bv_unspecified(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 2); SASSERT(m_util.is_bv2rm(args[0])); SASSERT(m_util.is_float(args[1])); if (m_hi_fp_unspecified) result = m_bv_util.mk_numeral(0, m_bv_util.get_bv_size(f->get_range())); else { expr * rm_bv = to_app(args[0])->get_arg(0); expr_ref nw = nan_wrap(args[1]); sort * domain[2] = { m.get_sort(rm_bv), m.get_sort(nw) }; func_decl * f_bv = mk_bv_uf(f, domain, f->get_range()); result = m.mk_app(f_bv, rm_bv, nw); } TRACE("fpa2bv_to_bv_unspecified", tout << "result=" << mk_ismt2_pp(result, m) << std::endl;); SASSERT(is_well_sorted(m, result)); } void fpa2bv_converter::mk_to_real_unspecified(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 1); if (m_hi_fp_unspecified) result = m_arith_util.mk_numeral(rational(0), false); else { expr * n = args[0]; expr_ref nw = nan_wrap(n); sort * domain[1] = { m.get_sort(nw) }; func_decl * f_bv = mk_bv_uf(f, domain, f->get_range()); result = m.mk_app(f_bv, nw); } } void fpa2bv_converter::mk_fp(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 3); SASSERT(m_bv_util.get_bv_size(args[0]) == 1); SASSERT(m_util.get_sbits(f->get_range()) == m_bv_util.get_bv_size(args[2]) + 1); SASSERT(m_util.get_ebits(f->get_range()) == m_bv_util.get_bv_size(args[1])); result = m_util.mk_fp(args[0], args[1], args[2]); TRACE("fpa2bv_mk_fp", tout << "mk_fp result = " << mk_ismt2_pp(result, m) << std::endl;); } void fpa2bv_converter::split_fp(expr * e, expr_ref & sgn, expr_ref & exp, expr_ref & sig) const { expr* e_sgn = nullptr, *e_exp = nullptr, *e_sig = nullptr; VERIFY(m_util.is_fp(e, e_sgn, e_exp, e_sig)); sgn = e_sgn; exp = e_exp; sig = e_sig; } void fpa2bv_converter::join_fp(expr * e, expr_ref & res) { expr_ref sgn(m), exp(m), sig(m); split_fp(e, sgn, exp, sig); res = m_bv_util.mk_concat(m_bv_util.mk_concat(sgn, exp), sig); } void fpa2bv_converter::mk_is_nan(expr * e, expr_ref & result) { expr_ref sgn(m), sig(m), exp(m); split_fp(e, sgn, exp, sig); // exp == 1^n , sig != 0 expr_ref sig_is_zero(m), sig_is_not_zero(m), exp_is_top(m), top_exp(m), zero(m); mk_top_exp(m_bv_util.get_bv_size(exp), top_exp); zero = m_bv_util.mk_numeral(0, m_bv_util.get_bv_size(sig)); m_simp.mk_eq(sig, zero, sig_is_zero); m_simp.mk_not(sig_is_zero, sig_is_not_zero); m_simp.mk_eq(exp, top_exp, exp_is_top); m_simp.mk_and(exp_is_top, sig_is_not_zero, result); } void fpa2bv_converter::mk_is_inf(expr * e, expr_ref & result) { expr_ref sgn(m), sig(m), exp(m); split_fp(e, sgn, exp, sig); expr_ref eq1(m), eq2(m), top_exp(m), zero(m); mk_top_exp(m_bv_util.get_bv_size(exp), top_exp); zero = m_bv_util.mk_numeral(0, m_bv_util.get_bv_size(sig)); m_simp.mk_eq(sig, zero, eq1); m_simp.mk_eq(exp, top_exp, eq2); m_simp.mk_and(eq1, eq2, result); } void fpa2bv_converter::mk_is_pinf(expr * e, expr_ref & result) { expr_ref e_is_pos(m), e_is_inf(m); mk_is_pos(e, e_is_pos); mk_is_inf(e, e_is_inf); m_simp.mk_and(e_is_pos, e_is_inf, result); } void fpa2bv_converter::mk_is_ninf(expr * e, expr_ref & result) { expr_ref e_is_neg(m), e_is_inf(m); mk_is_neg(e, e_is_neg); mk_is_inf(e, e_is_inf); m_simp.mk_and(e_is_neg, e_is_inf, result); } void fpa2bv_converter::mk_is_pos(expr * e, expr_ref & result) { SASSERT(m_util.is_fp(e)); SASSERT(to_app(e)->get_num_args() == 3); expr * a0 = to_app(e)->get_arg(0); expr_ref zero(m); zero = m_bv_util.mk_numeral(0, m_bv_util.get_bv_size(a0)); m_simp.mk_eq(a0, zero, result); } void fpa2bv_converter::mk_is_neg(expr * e, expr_ref & result) { SASSERT(m_util.is_fp(e)); SASSERT(to_app(e)->get_num_args() == 3); expr * a0 = to_app(e)->get_arg(0); expr_ref one(m); one = m_bv_util.mk_numeral(1, m_bv_util.get_bv_size(a0)); m_simp.mk_eq(a0, one, result); } void fpa2bv_converter::mk_is_zero(expr * e, expr_ref & result) { expr_ref sgn(m), sig(m), exp(m); split_fp(e, sgn, exp, sig); expr_ref eq1(m), eq2(m), bot_exp(m), zero(m); mk_bot_exp(m_bv_util.get_bv_size(exp), bot_exp); zero = m_bv_util.mk_numeral(0, m_bv_util.get_bv_size(sig)); m_simp.mk_eq(sig, zero, eq1); m_simp.mk_eq(exp, bot_exp, eq2); m_simp.mk_and(eq1, eq2, result); } void fpa2bv_converter::mk_is_nzero(expr * e, expr_ref & result) { expr_ref sgn(m), sig(m), exp(m); split_fp(e, sgn, exp, sig); expr_ref e_is_zero(m), eq(m), one_1(m); mk_is_zero(e, e_is_zero); one_1 = m_bv_util.mk_numeral(1, 1); m_simp.mk_eq(sgn, one_1, eq); m_simp.mk_and(eq, e_is_zero, result); } void fpa2bv_converter::mk_is_pzero(expr * e, expr_ref & result) { expr_ref sgn(m), sig(m), exp(m); split_fp(e, sgn, exp, sig); expr_ref e_is_zero(m), eq(m), nil_1(m); mk_is_zero(e, e_is_zero); nil_1 = m_bv_util.mk_numeral(0, 1); m_simp.mk_eq(sgn, nil_1, eq); m_simp.mk_and(eq, e_is_zero, result); } void fpa2bv_converter::mk_is_denormal(expr * e, expr_ref & result) { expr_ref sgn(m), sig(m), exp(m); split_fp(e, sgn, exp, sig); expr_ref zero(m), zexp(m), is_zero(m), n_is_zero(m); zero = m_bv_util.mk_numeral(0, m_bv_util.get_bv_size(exp)); m_simp.mk_eq(exp, zero, result); m_simp.mk_eq(exp, zero, zexp); mk_is_zero(e, is_zero); m_simp.mk_not(is_zero, n_is_zero); m_simp.mk_and(n_is_zero, zexp, result); } void fpa2bv_converter::mk_is_normal(expr * e, expr_ref & result) { expr_ref sgn(m), sig(m), exp(m); split_fp(e, sgn, exp, sig); expr_ref is_special(m), is_denormal(m), p(m), is_zero(m); mk_is_denormal(e, is_denormal); mk_is_zero(e, is_zero); unsigned ebits = m_bv_util.get_bv_size(exp); p = m_bv_util.mk_numeral(fu().fm().m_powers2.m1(ebits), ebits); m_simp.mk_eq(exp, p, is_special); expr_ref or_ex(m); m_simp.mk_or(is_special, is_denormal, or_ex); m_simp.mk_or(is_zero, or_ex, or_ex); m_simp.mk_not(or_ex, result); } void fpa2bv_converter::mk_is_rm(expr * rme, BV_RM_VAL rm, expr_ref & result) { expr_ref rm_num(m); rm_num = m_bv_util.mk_numeral(rm, 3); switch(rm) { case BV_RM_TIES_TO_AWAY: case BV_RM_TIES_TO_EVEN: case BV_RM_TO_NEGATIVE: case BV_RM_TO_POSITIVE: case BV_RM_TO_ZERO: return m_simp.mk_eq(rme, rm_num, result); default: UNREACHABLE(); } } void fpa2bv_converter::mk_top_exp(unsigned sz, expr_ref & result) { result = m_bv_util.mk_numeral(fu().fm().m_powers2.m1(sz), sz); } void fpa2bv_converter::mk_bot_exp(unsigned sz, expr_ref & result) { result = m_bv_util.mk_numeral(0, sz); } void fpa2bv_converter::mk_min_exp(unsigned ebits, expr_ref & result) { SASSERT(ebits >= 2); const mpz & z = m_mpf_manager.m_powers2.m1(ebits-1, true); result = m_bv_util.mk_numeral(z + mpz(1), ebits); } void fpa2bv_converter::mk_max_exp(unsigned ebits, expr_ref & result) { SASSERT(ebits >= 2); result = m_bv_util.mk_numeral(m_mpf_manager.m_powers2.m1(ebits-1, false), ebits); } void fpa2bv_converter::mk_leading_zeros(expr * e, unsigned max_bits, expr_ref & result) { SASSERT(m_bv_util.is_bv(e)); unsigned bv_sz = m_bv_util.get_bv_size(e); if (bv_sz == 0) result = m_bv_util.mk_numeral(0, max_bits); else if (bv_sz == 1) { expr_ref eq(m), nil_1(m), one_m(m), nil_m(m); nil_1 = m_bv_util.mk_numeral(0, 1); one_m = m_bv_util.mk_numeral(1, max_bits); nil_m = m_bv_util.mk_numeral(0, max_bits); m_simp.mk_eq(e, nil_1, eq); m_simp.mk_ite(eq, one_m, nil_m, result); } else { expr_ref H(m), L(m); H = m_bv_util.mk_extract(bv_sz-1, bv_sz/2, e); L = m_bv_util.mk_extract(bv_sz/2-1, 0, e); unsigned H_size = m_bv_util.get_bv_size(H); // unsigned L_size = m_bv_util.get_bv_size(L); expr_ref lzH(m), lzL(m); mk_leading_zeros(H, max_bits, lzH); /* recursive! */ mk_leading_zeros(L, max_bits, lzL); expr_ref H_is_zero(m), nil_h(m); nil_h = m_bv_util.mk_numeral(0, H_size); m_simp.mk_eq(H, nil_h, H_is_zero); expr_ref sum(m), h_m(m); h_m = m_bv_util.mk_numeral(H_size, max_bits); sum = m_bv_util.mk_bv_add(h_m, lzL); m_simp.mk_ite(H_is_zero, sum, lzH, result); } SASSERT(is_well_sorted(m, result)); } void fpa2bv_converter::mk_bias(expr * e, expr_ref & result) { unsigned ebits = m_bv_util.get_bv_size(e); SASSERT(ebits >= 2); expr_ref bias(m); bias = m_bv_util.mk_numeral(fu().fm().m_powers2.m1(ebits-1), ebits); result = m_bv_util.mk_bv_add(e, bias); } void fpa2bv_converter::mk_unbias(expr * e, expr_ref & result) { unsigned ebits = m_bv_util.get_bv_size(e); SASSERT(ebits >= 2); expr_ref e_plus_one(m); e_plus_one = m_bv_util.mk_bv_add(e, m_bv_util.mk_numeral(1, ebits)); expr_ref leading(m), n_leading(m), rest(m); leading = m_bv_util.mk_extract(ebits-1, ebits-1, e_plus_one); n_leading = m_bv_util.mk_bv_not(leading); rest = m_bv_util.mk_extract(ebits-2, 0, e_plus_one); result = m_bv_util.mk_concat(n_leading, rest); } void fpa2bv_converter::unpack(expr * e, expr_ref & sgn, expr_ref & sig, expr_ref & exp, expr_ref & lz, bool normalize) { SASSERT(m_util.is_fp(e)); SASSERT(to_app(e)->get_num_args() == 3); sort * srt = to_app(e)->get_decl()->get_range(); SASSERT(is_float(srt)); unsigned sbits = m_util.get_sbits(srt); unsigned ebits = m_util.get_ebits(srt); split_fp(e, sgn, exp, sig); SASSERT(m_bv_util.get_bv_size(sgn) == 1); SASSERT(m_bv_util.get_bv_size(exp) == ebits); SASSERT(m_bv_util.get_bv_size(sig) == sbits-1); dbg_decouple("fpa2bv_unpack_sgn", sgn); dbg_decouple("fpa2bv_unpack_exp", exp); dbg_decouple("fpa2bv_unpack_sig", sig); expr_ref is_normal(m); mk_is_normal(e, is_normal); expr_ref normal_sig(m), normal_exp(m); normal_sig = m_bv_util.mk_concat(m_bv_util.mk_numeral(1, 1), sig); mk_unbias(exp, normal_exp); dbg_decouple("fpa2bv_unpack_normal_exp", normal_exp); expr_ref denormal_sig(m), denormal_exp(m); denormal_sig = m_bv_util.mk_zero_extend(1, sig); denormal_exp = m_bv_util.mk_numeral(1, ebits); mk_unbias(denormal_exp, denormal_exp); dbg_decouple("fpa2bv_unpack_denormal_exp", denormal_exp); expr_ref zero_e(m); zero_e = m_bv_util.mk_numeral(0, ebits); if (normalize) { expr_ref is_sig_zero(m), zero_s(m); zero_s = m_bv_util.mk_numeral(0, sbits); m_simp.mk_eq(zero_s, denormal_sig, is_sig_zero); expr_ref lz_d(m), norm_or_zero(m); mk_leading_zeros(denormal_sig, ebits, lz_d); norm_or_zero = m.mk_or(is_normal, is_sig_zero); m_simp.mk_ite(norm_or_zero, zero_e, lz_d, lz); dbg_decouple("fpa2bv_unpack_lz", lz); expr_ref shift(m); m_simp.mk_ite(is_sig_zero, zero_e, lz, shift); dbg_decouple("fpa2bv_unpack_shift", shift); SASSERT(is_well_sorted(m, is_sig_zero)); SASSERT(is_well_sorted(m, shift)); SASSERT(m_bv_util.get_bv_size(shift) == ebits); if (ebits <= sbits) { expr_ref q(m); q = m_bv_util.mk_zero_extend(sbits-ebits, shift); denormal_sig = m_bv_util.mk_bv_shl(denormal_sig, q); } else { // the maximum shift is `sbits', because after that the mantissa // would be zero anyways. So we can safely cut the shift variable down, // as long as we check the higher bits. expr_ref zero_ems(m), sh(m), is_sh_zero(m), sl(m), sbits_s(m), short_shift(m); zero_ems = m_bv_util.mk_numeral(0, ebits - sbits); sbits_s = m_bv_util.mk_numeral(sbits, sbits); sh = m_bv_util.mk_extract(ebits-1, sbits, shift); m_simp.mk_eq(zero_ems, sh, is_sh_zero); short_shift = m_bv_util.mk_extract(sbits-1, 0, shift); m_simp.mk_ite(is_sh_zero, short_shift, sbits_s, sl); denormal_sig = m_bv_util.mk_bv_shl(denormal_sig, sl); } } else lz = zero_e; SASSERT(is_well_sorted(m, normal_sig)); SASSERT(is_well_sorted(m, denormal_sig)); SASSERT(is_well_sorted(m, normal_exp)); SASSERT(is_well_sorted(m, denormal_exp)); dbg_decouple("fpa2bv_unpack_is_normal", is_normal); m_simp.mk_ite(is_normal, normal_sig, denormal_sig, sig); m_simp.mk_ite(is_normal, normal_exp, denormal_exp, exp); SASSERT(is_well_sorted(m, sgn)); SASSERT(is_well_sorted(m, sig)); SASSERT(is_well_sorted(m, exp)); SASSERT(m_bv_util.get_bv_size(sgn) == 1); SASSERT(m_bv_util.get_bv_size(sig) == sbits); SASSERT(m_bv_util.get_bv_size(exp) == ebits); TRACE("fpa2bv_unpack", tout << "UNPACK SGN = " << mk_ismt2_pp(sgn, m) << std::endl; ); TRACE("fpa2bv_unpack", tout << "UNPACK SIG = " << mk_ismt2_pp(sig, m) << std::endl; ); TRACE("fpa2bv_unpack", tout << "UNPACK EXP = " << mk_ismt2_pp(exp, m) << std::endl; ); } void fpa2bv_converter::mk_rounding_mode(decl_kind k, expr_ref & result) { switch(k) { case OP_FPA_RM_NEAREST_TIES_TO_EVEN: result = m_bv_util.mk_numeral(BV_RM_TIES_TO_EVEN, 3); break; case OP_FPA_RM_NEAREST_TIES_TO_AWAY: result = m_bv_util.mk_numeral(BV_RM_TIES_TO_AWAY, 3); break; case OP_FPA_RM_TOWARD_POSITIVE: result = m_bv_util.mk_numeral(BV_RM_TO_POSITIVE, 3); break; case OP_FPA_RM_TOWARD_NEGATIVE: result = m_bv_util.mk_numeral(BV_RM_TO_NEGATIVE, 3); break; case OP_FPA_RM_TOWARD_ZERO: result = m_bv_util.mk_numeral(BV_RM_TO_ZERO, 3); break; default: UNREACHABLE(); } result = m_util.mk_bv2rm(result); } void fpa2bv_converter::dbg_decouple(const char * prefix, expr_ref & e) { #ifdef Z3DEBUG return; // CMW: This works only for quantifier-free formulas. if (m_util.is_fp(e)) { expr_ref new_bv(m); expr_ref e_sgn(m), e_sig(m), e_exp(m); split_fp(e, e_sgn, e_exp, e_sig); unsigned ebits = m_bv_util.get_bv_size(e_exp); unsigned sbits = m_bv_util.get_bv_size(e_sig) + 1; unsigned bv_sz = ebits + sbits; new_bv = m.mk_fresh_const(prefix, m_bv_util.mk_sort(bv_sz)); expr_ref bv_sgn(m), bv_exp(m), bv_sig(m); bv_sgn = m_bv_util.mk_extract(bv_sz-1, bv_sz-1, new_bv); bv_exp = m_bv_util.mk_extract(bv_sz-2, bv_sz-ebits-1, new_bv); bv_sig = m_bv_util.mk_extract(sbits-2, 0, new_bv); expr_ref e_sgn_eq_bv_sgn(m), e_exp_eq_bv_exp(m), e_sig_eq_bv_sig(m); e_sgn_eq_bv_sgn = m.mk_eq(e_sgn, bv_sgn); e_exp_eq_bv_exp = m.mk_eq(e_exp, bv_exp); e_sig_eq_bv_sig = m.mk_eq(e_sig, bv_sig); m_extra_assertions.push_back(e_sgn_eq_bv_sgn); m_extra_assertions.push_back(e_exp_eq_bv_exp); m_extra_assertions.push_back(e_sig_eq_bv_sig); e = m_util.mk_fp(bv_sgn, bv_exp, bv_sig); } else { expr_ref new_e(m), new_e_eq_e(m); new_e = m.mk_fresh_const(prefix, m.get_sort(e)); new_e_eq_e = m.mk_eq(new_e, e); m_extra_assertions.push_back(new_e_eq_e); e = new_e; } #endif } expr_ref fpa2bv_converter::mk_rounding_decision(expr * rm, expr * sgn, expr * last, expr * round, expr * sticky) { expr_ref rmr(rm, m); expr_ref sgnr(sgn, m); expr_ref lastr(last, m); expr_ref roundr(round, m); expr_ref stickyr(sticky, m); dbg_decouple("fpa2bv_rnd_dec_rm", rmr); dbg_decouple("fpa2bv_rnd_dec_sgn", sgnr); dbg_decouple("fpa2bv_rnd_dec_last", lastr); dbg_decouple("fpa2bv_rnd_dec_round", roundr); dbg_decouple("fpa2bv_rnd_dec_sticky", stickyr); expr_ref last_or_sticky(m), round_or_sticky(m), not_last(m), not_round(m), not_sticky(m), not_lors(m), not_rors(m), not_sgn(m); expr * last_sticky[2] = { last, sticky }; expr * round_sticky[2] = { round, sticky }; last_or_sticky = m_bv_util.mk_bv_or(2, last_sticky); round_or_sticky = m_bv_util.mk_bv_or(2, round_sticky); not_last = m_bv_util.mk_bv_not(last); not_round = m_bv_util.mk_bv_not(round); not_sticky = m_bv_util.mk_bv_not(sticky); not_lors = m_bv_util.mk_bv_not(last_or_sticky); not_rors = m_bv_util.mk_bv_not(round_or_sticky); not_sgn = m_bv_util.mk_bv_not(sgn); expr * nround_lors[2] = { not_round, not_lors }; expr * pos_args[2] = { sgn, not_rors }; expr * neg_args[2] = { not_sgn, not_rors }; expr_ref inc_teven(m), inc_taway(m), inc_pos(m), inc_neg(m); inc_teven = m_bv_util.mk_bv_not(m_bv_util.mk_bv_or(2, nround_lors)); inc_taway = round; inc_pos = m_bv_util.mk_bv_not(m_bv_util.mk_bv_or(2, pos_args)); inc_neg = m_bv_util.mk_bv_not(m_bv_util.mk_bv_or(2, neg_args)); expr_ref res(m), inc_c2(m), inc_c3(m), inc_c4(m); expr_ref rm_is_to_neg(m), rm_is_to_pos(m), rm_is_away(m), rm_is_even(m), nil_1(m); nil_1 = m_bv_util.mk_numeral(0, 1); mk_is_rm(rm, BV_RM_TO_NEGATIVE, rm_is_to_neg); mk_is_rm(rm, BV_RM_TO_POSITIVE, rm_is_to_pos); mk_is_rm(rm, BV_RM_TIES_TO_AWAY, rm_is_away); mk_is_rm(rm, BV_RM_TIES_TO_EVEN, rm_is_even); m_simp.mk_ite(rm_is_to_neg, inc_neg, nil_1, inc_c4); m_simp.mk_ite(rm_is_to_pos, inc_pos, inc_c4, inc_c3); m_simp.mk_ite(rm_is_away, inc_taway, inc_c3, inc_c2); m_simp.mk_ite(rm_is_even, inc_teven, inc_c2, res); dbg_decouple("fpa2bv_rnd_dec_res", res); return res; } void fpa2bv_converter::round(sort * s, expr_ref & rm, expr_ref & sgn, expr_ref & sig, expr_ref & exp, expr_ref & result) { unsigned ebits = m_util.get_ebits(s); unsigned sbits = m_util.get_sbits(s); dbg_decouple("fpa2bv_rnd_rm", rm); dbg_decouple("fpa2bv_rnd_sgn", sgn); dbg_decouple("fpa2bv_rnd_sig", sig); dbg_decouple("fpa2bv_rnd_exp", exp); SASSERT(is_well_sorted(m, rm)); SASSERT(is_well_sorted(m, sgn)); SASSERT(is_well_sorted(m, sig)); SASSERT(is_well_sorted(m, exp)); TRACE("fpa2bv_dbg", tout << "RND: " << std::endl << "ebits = " << ebits << std::endl << "sbits = " << sbits << std::endl << "sgn = " << mk_ismt2_pp(sgn, m) << std::endl << "sig = " << mk_ismt2_pp(sig, m) << std::endl << "exp = " << mk_ismt2_pp(exp, m) << std::endl; ); // Assumptions: sig is of the form f[-1:0] . f[1:sbits-1] [guard,round,sticky], // i.e., it has 2 + (sbits-1) + 3 = sbits + 4 bits, where the first one is in sgn. // Furthermore, note that sig is an unsigned bit-vector, while exp is signed. SASSERT(m_bv_util.is_bv(rm) && m_bv_util.get_bv_size(rm) == 3); SASSERT(m_bv_util.is_bv(sgn) && m_bv_util.get_bv_size(sgn) == 1); SASSERT(m_bv_util.is_bv(sig) && m_bv_util.get_bv_size(sig) >= 5); SASSERT(m_bv_util.is_bv(exp) && m_bv_util.get_bv_size(exp) >= 4); SASSERT(m_bv_util.get_bv_size(sig) == sbits+4); SASSERT(m_bv_util.get_bv_size(exp) == ebits+2); expr_ref e_min(m), e_max(m); mk_min_exp(ebits, e_min); mk_max_exp(ebits, e_max); TRACE("fpa2bv_dbg", tout << "e_min = " << mk_ismt2_pp(e_min, m) << std::endl; tout << "e_max = " << mk_ismt2_pp(e_max, m) << std::endl;); expr_ref OVF1(m), e_top_three(m), sigm1(m), e_eq_emax_and_sigm1(m), e_eq_emax(m); expr_ref e3(m), ne3(m), e2(m), e1(m), e21(m), one_1(m), h_exp(m), sh_exp(m), th_exp(m); one_1 = m_bv_util.mk_numeral(1, 1); h_exp = m_bv_util.mk_extract(ebits+1, ebits+1, exp); sh_exp = m_bv_util.mk_extract(ebits, ebits, exp); th_exp = m_bv_util.mk_extract(ebits-1, ebits-1, exp); m_simp.mk_eq(h_exp, one_1, e3); m_simp.mk_eq(sh_exp, one_1, e2); m_simp.mk_eq(th_exp, one_1, e1); m_simp.mk_or(e2, e1, e21); m_simp.mk_not(e3, ne3); m_simp.mk_and(ne3, e21, e_top_three); expr_ref ext_emax(m), t_sig(m); ext_emax = m_bv_util.mk_zero_extend(2, e_max); t_sig = m_bv_util.mk_extract(sbits+3, sbits+3, sig); m_simp.mk_eq(ext_emax, exp, e_eq_emax); m_simp.mk_eq(t_sig, one_1, sigm1); m_simp.mk_and(e_eq_emax, sigm1, e_eq_emax_and_sigm1); m_simp.mk_or(e_top_three, e_eq_emax_and_sigm1, OVF1); dbg_decouple("fpa2bv_rnd_OVF1", OVF1); TRACE("fpa2bv_dbg", tout << "OVF1 = " << mk_ismt2_pp(OVF1, m) << std::endl;); SASSERT(is_well_sorted(m, OVF1)); expr_ref lz(m); mk_leading_zeros(sig, ebits+2, lz); // CMW: is this always large enough? dbg_decouple("fpa2bv_rnd_lz", lz); TRACE("fpa2bv_dbg", tout << "LZ = " << mk_ismt2_pp(lz, m) << std::endl;); expr_ref t(m); t = m_bv_util.mk_bv_add(exp, m_bv_util.mk_numeral(1, ebits+2)); t = m_bv_util.mk_bv_sub(t, lz); t = m_bv_util.mk_bv_sub(t, m_bv_util.mk_sign_extend(2, e_min)); dbg_decouple("fpa2bv_rnd_t", t); expr_ref TINY(m); TINY = m_bv_util.mk_sle(t, m_bv_util.mk_numeral((unsigned)-1, ebits+2)); dbg_decouple("fpa2bv_rnd_TINY", TINY); TRACE("fpa2bv_dbg", tout << "TINY = " << mk_ismt2_pp(TINY, m) << std::endl;); SASSERT(is_well_sorted(m, TINY)); expr_ref beta(m); beta = m_bv_util.mk_bv_add(m_bv_util.mk_bv_sub(exp, lz), m_bv_util.mk_numeral(1, ebits+2)); TRACE("fpa2bv_dbg", tout << "beta = " << mk_ismt2_pp(beta, m) << std::endl; ); SASSERT(is_well_sorted(m, beta)); dbg_decouple("fpa2bv_rnd_beta", beta); dbg_decouple("fpa2bv_rnd_e_min", e_min); dbg_decouple("fpa2bv_rnd_e_max", e_max); expr_ref sigma(m), sigma_add(m); sigma_add = m_bv_util.mk_bv_sub(exp, m_bv_util.mk_sign_extend(2, e_min)); sigma_add = m_bv_util.mk_bv_add(sigma_add, m_bv_util.mk_numeral(1, ebits+2)); m_simp.mk_ite(TINY, sigma_add, lz, sigma); dbg_decouple("fpa2bv_rnd_sigma", sigma); TRACE("fpa2bv_dbg", tout << "Shift distance: " << mk_ismt2_pp(sigma, m) << std::endl;); SASSERT(is_well_sorted(m, sigma)); // Normalization shift dbg_decouple("fpa2bv_rnd_sig_before_shift", sig); unsigned sig_size = m_bv_util.get_bv_size(sig); SASSERT(sig_size == sbits + 4); SASSERT(m_bv_util.get_bv_size(sigma) == ebits+2); unsigned sigma_size = ebits + 2; expr_ref sigma_neg(m), sigma_cap(m), sigma_neg_capped(m), sigma_lt_zero(m), sig_ext(m), rs_sig(m), ls_sig(m), big_sh_sig(m), sigma_le_cap(m); sigma_neg = m_bv_util.mk_bv_neg(sigma); sigma_cap = m_bv_util.mk_numeral(sbits+2, sigma_size); sigma_le_cap = m_bv_util.mk_ule(sigma_neg, sigma_cap); m_simp.mk_ite(sigma_le_cap, sigma_neg, sigma_cap, sigma_neg_capped); dbg_decouple("fpa2bv_rnd_sigma_neg", sigma_neg); dbg_decouple("fpa2bv_rnd_sigma_cap", sigma_cap); dbg_decouple("fpa2bv_rnd_sigma_neg_capped", sigma_neg_capped); sigma_lt_zero = m_bv_util.mk_sle(sigma, m_bv_util.mk_numeral((unsigned)-1, sigma_size)); dbg_decouple("fpa2bv_rnd_sigma_lt_zero", sigma_lt_zero); sig_ext = m_bv_util.mk_concat(sig, m_bv_util.mk_numeral(0, sig_size)); rs_sig = m_bv_util.mk_bv_lshr(sig_ext, m_bv_util.mk_zero_extend(2*sig_size - sigma_size, sigma_neg_capped)); ls_sig = m_bv_util.mk_bv_shl(sig_ext, m_bv_util.mk_zero_extend(2*sig_size - sigma_size, sigma)); m_simp.mk_ite(sigma_lt_zero, rs_sig, ls_sig, big_sh_sig); SASSERT(m_bv_util.get_bv_size(big_sh_sig) == 2*sig_size); dbg_decouple("fpa2bv_rnd_big_sh_sig", big_sh_sig); unsigned sig_extract_low_bit = (2*sig_size-1)-(sbits+2)+1; sig = m_bv_util.mk_extract(2*sig_size-1, sig_extract_low_bit, big_sh_sig); SASSERT(m_bv_util.get_bv_size(sig) == sbits+2); dbg_decouple("fpa2bv_rnd_shifted_sig", sig); expr_ref sticky(m); sticky = m.mk_app(m_bv_util.get_fid(), OP_BREDOR, m_bv_util.mk_extract(sig_extract_low_bit-1, 0, big_sh_sig)); SASSERT(is_well_sorted(m, sticky)); SASSERT(is_well_sorted(m, sig)); // put the sticky bit into the significand. expr_ref ext_sticky(m); ext_sticky = m_bv_util.mk_zero_extend(sbits+1, sticky); expr * tmp[2] = { sig, ext_sticky }; sig = m_bv_util.mk_bv_or(2, tmp); SASSERT(is_well_sorted(m, sig)); SASSERT(m_bv_util.get_bv_size(sig) == sbits+2); expr_ref ext_emin(m); ext_emin = m_bv_util.mk_zero_extend(2, e_min); m_simp.mk_ite(TINY, ext_emin, beta, exp); SASSERT(is_well_sorted(m, exp)); // Significand rounding expr_ref round(m), last(m); sticky = m_bv_util.mk_extract(0, 0, sig); // new sticky bit! round = m_bv_util.mk_extract(1, 1, sig); last = m_bv_util.mk_extract(2, 2, sig); TRACE("fpa2bv_dbg", tout << "sticky = " << mk_ismt2_pp(sticky, m) << std::endl;); dbg_decouple("fpa2bv_rnd_sticky", sticky); dbg_decouple("fpa2bv_rnd_round", round); dbg_decouple("fpa2bv_rnd_last", last); sig = m_bv_util.mk_extract(sbits+1, 2, sig); expr_ref inc(m); inc = mk_rounding_decision(rm, sgn, last, round, sticky); SASSERT(m_bv_util.get_bv_size(inc) == 1 && is_well_sorted(m, inc)); dbg_decouple("fpa2bv_rnd_inc", inc); sig = m_bv_util.mk_bv_add(m_bv_util.mk_zero_extend(1, sig), m_bv_util.mk_zero_extend(sbits, inc)); SASSERT(is_well_sorted(m, sig)); dbg_decouple("fpa2bv_rnd_sig_plus_inc", sig); // Post normalization SASSERT(m_bv_util.get_bv_size(sig) == sbits + 1); expr_ref SIGovf(m); t_sig = m_bv_util.mk_extract(sbits, sbits, sig); m_simp.mk_eq(t_sig, one_1, SIGovf); SASSERT(is_well_sorted(m, SIGovf)); dbg_decouple("fpa2bv_rnd_SIGovf", SIGovf); expr_ref hallbut1_sig(m), lallbut1_sig(m); hallbut1_sig = m_bv_util.mk_extract(sbits, 1, sig); lallbut1_sig = m_bv_util.mk_extract(sbits-1, 0, sig); m_simp.mk_ite(SIGovf, hallbut1_sig, lallbut1_sig, sig); SASSERT(m_bv_util.get_bv_size(exp) == ebits + 2); expr_ref exp_p1(m); exp_p1 = m_bv_util.mk_bv_add(exp, m_bv_util.mk_numeral(1, ebits+2)); m_simp.mk_ite(SIGovf, exp_p1, exp, exp); SASSERT(is_well_sorted(m, sig)); SASSERT(is_well_sorted(m, exp)); dbg_decouple("fpa2bv_rnd_sig_postnormalized", sig); dbg_decouple("fpa2bv_rnd_exp_postnormalized", exp); SASSERT(m_bv_util.get_bv_size(sig) == sbits); SASSERT(m_bv_util.get_bv_size(exp) == ebits + 2); SASSERT(m_bv_util.get_bv_size(e_max) == ebits); // Exponent adjustment and rounding expr_ref biased_exp(m); mk_bias(m_bv_util.mk_extract(ebits-1, 0, exp), biased_exp); dbg_decouple("fpa2bv_rnd_unbiased_exp", exp); dbg_decouple("fpa2bv_rnd_biased_exp", biased_exp); // AdjustExp SASSERT(is_well_sorted(m, OVF1)); SASSERT(m.is_bool(OVF1)); expr_ref preOVF2(m), OVF2(m), OVF(m), exp_redand(m), pem2m1(m); exp_redand = m.mk_app(m_bv_util.get_fid(), OP_BREDAND, biased_exp.get()); m_simp.mk_eq(exp_redand, one_1, preOVF2); m_simp.mk_and(SIGovf, preOVF2, OVF2); pem2m1 = m_bv_util.mk_numeral(fu().fm().m_powers2.m1(ebits-2), ebits); m_simp.mk_ite(OVF2, pem2m1, biased_exp, biased_exp); m_simp.mk_or(OVF1, OVF2, OVF); SASSERT(is_well_sorted(m, OVF2)); SASSERT(is_well_sorted(m, OVF)); SASSERT(m.is_bool(OVF2)); SASSERT(m.is_bool(OVF)); dbg_decouple("fpa2bv_rnd_OVF2", OVF2); dbg_decouple("fpa2bv_rnd_OVF", OVF); // ExpRnd expr_ref top_exp(m), bot_exp(m); mk_top_exp(ebits, top_exp); mk_bot_exp(ebits, bot_exp); expr_ref nil_1(m); nil_1 = m_bv_util.mk_numeral(0, 1); expr_ref rm_is_to_zero(m), rm_is_to_neg(m), rm_is_to_pos(m), rm_zero_or_neg(m), rm_zero_or_pos(m); mk_is_rm(rm, BV_RM_TO_ZERO, rm_is_to_zero); mk_is_rm(rm, BV_RM_TO_NEGATIVE, rm_is_to_neg); mk_is_rm(rm, BV_RM_TO_POSITIVE, rm_is_to_pos); m_simp.mk_or(rm_is_to_zero, rm_is_to_neg, rm_zero_or_neg); m_simp.mk_or(rm_is_to_zero, rm_is_to_pos, rm_zero_or_pos); dbg_decouple("fpa2bv_rnd_rm_is_to_zero", rm_is_to_zero); dbg_decouple("fpa2bv_rnd_rm_is_to_neg", rm_is_to_neg); dbg_decouple("fpa2bv_rnd_rm_is_to_pos", rm_is_to_pos); expr_ref sgn_is_zero(m), zero1(m); zero1 = m_bv_util.mk_numeral(0, 1); m_simp.mk_eq(sgn, zero1, sgn_is_zero); dbg_decouple("fpa2bv_rnd_sgn_is_zero", sgn_is_zero); expr_ref max_sig(m), max_exp(m), inf_sig(m), inf_exp(m); max_sig = m_bv_util.mk_numeral(fu().fm().m_powers2.m1(sbits-1, false), sbits-1); max_exp = m_bv_util.mk_concat(m_bv_util.mk_numeral(fu().fm().m_powers2.m1(ebits-1, false), ebits-1), m_bv_util.mk_numeral(0, 1)); inf_sig = m_bv_util.mk_numeral(0, sbits-1); inf_exp = top_exp; dbg_decouple("fpa2bv_rnd_max_exp", max_exp); dbg_decouple("fpa2bv_rnd_max_sig", max_sig); dbg_decouple("fpa2bv_rnd_inf_sig", inf_sig); dbg_decouple("fpa2bv_rnd_inf_exp", inf_exp); expr_ref ovfl_exp(m), max_inf_exp_neg(m), max_inf_exp_pos(m), n_d_check(m), n_d_exp(m); m_simp.mk_ite(rm_zero_or_pos, max_exp, inf_exp, max_inf_exp_neg); m_simp.mk_ite(rm_zero_or_neg, max_exp, inf_exp, max_inf_exp_pos); m_simp.mk_ite(sgn_is_zero, max_inf_exp_pos, max_inf_exp_neg, ovfl_exp); t_sig = m_bv_util.mk_extract(sbits-1, sbits-1, sig); m_simp.mk_eq(t_sig, nil_1, n_d_check); m_simp.mk_ite(n_d_check, bot_exp /* denormal */, biased_exp, n_d_exp); m_simp.mk_ite(OVF, ovfl_exp, n_d_exp, exp); expr_ref max_inf_sig_neg(m), max_inf_sig_pos(m), ovfl_sig(m), rest_sig(m); m_simp.mk_ite(rm_zero_or_pos, max_sig, inf_sig, max_inf_sig_neg); m_simp.mk_ite(rm_zero_or_neg, max_sig, inf_sig, max_inf_sig_pos); m_simp.mk_ite(sgn_is_zero, max_inf_sig_pos, max_inf_sig_neg, ovfl_sig); rest_sig = m_bv_util.mk_extract(sbits-2, 0, sig); m_simp.mk_ite(OVF, ovfl_sig, rest_sig, sig); dbg_decouple("fpa2bv_rnd_max_inf_sig_neg", max_inf_sig_neg); dbg_decouple("fpa2bv_rnd_max_inf_sig_pos", max_inf_sig_pos); dbg_decouple("fpa2bv_rnd_rm_zero_or_neg", rm_zero_or_neg); dbg_decouple("fpa2bv_rnd_rm_zero_or_pos", rm_zero_or_pos); dbg_decouple("fpa2bv_rnd_sgn_final", sgn); dbg_decouple("fpa2bv_rnd_sig_final", sig); dbg_decouple("fpa2bv_rnd_exp_final", exp); expr_ref res_sgn(m), res_sig(m), res_exp(m); res_sgn = sgn; res_sig = sig; res_exp = exp; SASSERT(m_bv_util.get_bv_size(res_sgn) == 1); SASSERT(is_well_sorted(m, res_sgn)); SASSERT(m_bv_util.get_bv_size(res_sig) == sbits-1); SASSERT(is_well_sorted(m, res_sig)); SASSERT(m_bv_util.get_bv_size(res_exp) == ebits); SASSERT(is_well_sorted(m, res_exp)); result = m_util.mk_fp(res_sgn, res_exp, res_sig); TRACE("fpa2bv_round", tout << "ROUND = " << mk_ismt2_pp(result, m) << std::endl; ); } void fpa2bv_converter::reset() { dec_ref_map_key_values(m, m_const2bv); dec_ref_map_key_values(m, m_rm_const2bv); for (auto const& kv : m_uf2bvuf) { m.dec_ref(kv.m_key); m.dec_ref(kv.m_value); } for (auto const& kv : m_min_max_ufs) { m.dec_ref(kv.m_key); m.dec_ref(kv.m_value.first); m.dec_ref(kv.m_value.second); } m_uf2bvuf.reset(); m_min_max_ufs.reset(); m_extra_assertions.reset(); } func_decl * fpa2bv_converter::mk_bv_uf(func_decl * f, sort * const * domain, sort * range) { func_decl* res; if (!m_uf2bvuf.find(f, res)) { res = m.mk_fresh_func_decl(nullptr, f->get_arity(), domain, range); m.inc_ref(f); m.inc_ref(res); m_uf2bvuf.insert(f, res); TRACE("fpa2bv", tout << "New UF func_decl: " << res->get_id() << std::endl << mk_ismt2_pp(res, m) << std::endl;); } return res; } z3-z3-4.8.7/src/ast/fpa/fpa2bv_converter.h000066400000000000000000000253441356505360400202030ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: fpa2bv_converter.h Abstract: Conversion routines for Floating Point -> Bit-Vector Author: Christoph (cwinter) 2012-02-09 Notes: --*/ #ifndef FPA2BV_CONVERTER_H_ #define FPA2BV_CONVERTER_H_ #include "ast/ast.h" #include "util/obj_hashtable.h" #include "util/ref_util.h" #include "ast/fpa_decl_plugin.h" #include "ast/bv_decl_plugin.h" #include "ast/array_decl_plugin.h" #include "ast/datatype_decl_plugin.h" #include "ast/dl_decl_plugin.h" #include "ast/pb_decl_plugin.h" #include "ast/seq_decl_plugin.h" #include "ast/rewriter/bool_rewriter.h" class fpa2bv_converter { public: typedef obj_map > special_t; typedef obj_map const2bv_t; typedef obj_map uf2bvuf_t; protected: ast_manager & m; bool_rewriter m_simp; fpa_util m_util; bv_util m_bv_util; arith_util m_arith_util; datatype_util m_dt_util; seq_util m_seq_util; mpf_manager & m_mpf_manager; unsynch_mpz_manager & m_mpz_manager; fpa_decl_plugin * m_plugin; bool m_hi_fp_unspecified; const2bv_t m_const2bv; const2bv_t m_rm_const2bv; uf2bvuf_t m_uf2bvuf; special_t m_min_max_ufs; friend class fpa2bv_model_converter; friend class bv2fpa_converter; public: fpa2bv_converter(ast_manager & m); ~fpa2bv_converter(); fpa_util & fu() { return m_util; } bv_util & bu() { return m_bv_util; } arith_util & au() { return m_arith_util; } bool is_float(sort * s) { return m_util.is_float(s); } bool is_float(expr * e) { return is_app(e) && m_util.is_float(to_app(e)->get_decl()->get_range()); } bool is_rm(expr * e) { return is_app(e) && m_util.is_rm(e); } bool is_rm(sort * s) { return m_util.is_rm(s); } bool is_float_family(func_decl * f) { return f->get_family_id() == m_util.get_family_id(); } void mk_fp(func_decl * f, unsigned num, expr * const * args, expr_ref & result); // void split_fp(expr * e, expr * & sgn, expr * & exp, expr * & sig) const; void split_fp(expr * e, expr_ref & sgn, expr_ref & exp, expr_ref & sig) const; void join_fp(expr * e, expr_ref & res); void mk_eq(expr * a, expr * b, expr_ref & result); void mk_ite(expr * c, expr * t, expr * f, expr_ref & result); void mk_distinct(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_rounding_mode(decl_kind k, expr_ref & result); void mk_numeral(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_numeral(sort * s, mpf const & v, expr_ref & result); virtual void mk_const(func_decl * f, expr_ref & result); virtual void mk_rm_const(func_decl * f, expr_ref & result); virtual void mk_uf(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_var(unsigned base_inx, sort * srt, expr_ref & result); void mk_pinf(func_decl * f, expr_ref & result); void mk_ninf(func_decl * f, expr_ref & result); void mk_nan(func_decl * f, expr_ref & result); void mk_nzero(func_decl *f, expr_ref & result); void mk_pzero(func_decl *f, expr_ref & result); void mk_add(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_sub(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_neg(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_mul(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_div(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_rem(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_abs(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_fma(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_sqrt(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_round_to_integral(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_abs(sort * s, expr_ref & x, expr_ref & result); void mk_float_eq(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_float_lt(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_float_gt(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_float_le(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_float_ge(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_float_eq(sort * s, expr_ref & x, expr_ref & y, expr_ref & result); void mk_float_lt(sort * s, expr_ref & x, expr_ref & y, expr_ref & result); void mk_float_gt(sort *, expr_ref & x, expr_ref & y, expr_ref & result); void mk_float_le(sort * s, expr_ref & x, expr_ref & y, expr_ref & result); void mk_float_ge(sort * s, expr_ref & x, expr_ref & y, expr_ref & result); void mk_is_zero(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_is_nzero(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_is_pzero(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_is_negative(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_is_positive(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_is_nan(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_is_inf(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_is_normal(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_is_subnormal(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_to_fp(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_to_fp_float(func_decl * f, sort * s, expr * rm, expr * x, expr_ref & result); void mk_to_fp_signed(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_to_fp_unsigned(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_to_ieee_bv(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_to_ieee_bv_unspecified(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_to_fp_real(func_decl * f, sort * s, expr * rm, expr * x, expr_ref & result); void mk_to_fp_real_int(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_to_ubv(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_to_sbv(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_to_bv_unspecified(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_to_real(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_to_real_unspecified(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void set_unspecified_fp_hi(bool v) { m_hi_fp_unspecified = v; } void mk_min(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_max(func_decl * f, unsigned num, expr * const * args, expr_ref & result); expr_ref mk_min_max_unspecified(func_decl * f, expr * x, expr * y); void reset(); void dbg_decouple(const char * prefix, expr_ref & e); expr_ref_vector m_extra_assertions; special_t const & get_min_max_specials() const { return m_min_max_ufs; }; const2bv_t const & get_const2bv() const { return m_const2bv; }; const2bv_t const & get_rm_const2bv() const { return m_rm_const2bv; }; uf2bvuf_t const & get_uf2bvuf() const { return m_uf2bvuf; }; protected: void mk_one(func_decl *f, expr_ref & sign, expr_ref & result); void mk_is_nan(expr * e, expr_ref & result); void mk_is_inf(expr * e, expr_ref & result); void mk_is_pinf(expr * e, expr_ref & result); void mk_is_ninf(expr * e, expr_ref & result); void mk_is_pos(expr * e, expr_ref & result); void mk_is_neg(expr * e, expr_ref & result); void mk_is_zero(expr * e, expr_ref & result); void mk_is_nzero(expr * e, expr_ref & result); void mk_is_pzero(expr * e, expr_ref & result); void mk_is_denormal(expr * e, expr_ref & result); void mk_is_normal(expr * e, expr_ref & result); void mk_is_rm(expr * e, BV_RM_VAL rm, expr_ref & result); void mk_top_exp(unsigned sz, expr_ref & result); void mk_bot_exp(unsigned sz, expr_ref & result); void mk_min_exp(unsigned ebits, expr_ref & result); void mk_max_exp(unsigned ebits, expr_ref & result); void mk_leading_zeros(expr * e, unsigned max_bits, expr_ref & result); void mk_bias(expr * e, expr_ref & result); void mk_unbias(expr * e, expr_ref & result); void unpack(expr * e, expr_ref & sgn, expr_ref & sig, expr_ref & exp, expr_ref & lz, bool normalize); void round(sort * s, expr_ref & rm, expr_ref & sgn, expr_ref & sig, expr_ref & exp, expr_ref & result); expr_ref mk_rounding_decision(expr * rm, expr * sgn, expr * last, expr * round, expr * sticky); void add_core(unsigned sbits, unsigned ebits, expr_ref & c_sgn, expr_ref & c_sig, expr_ref & c_exp, expr_ref & d_sgn, expr_ref & d_sig, expr_ref & d_exp, expr_ref & res_sgn, expr_ref & res_sig, expr_ref & res_exp); app * mk_fresh_const(char const * prefix, unsigned sz); void mk_to_bv(func_decl * f, unsigned num, expr * const * args, bool is_signed, expr_ref & result); private: void mk_nan(sort * s, expr_ref & result); void mk_nzero(sort * s, expr_ref & result); void mk_pzero(sort * s, expr_ref & result); void mk_zero(sort * s, expr_ref & sgn, expr_ref & result); void mk_ninf(sort * s, expr_ref & result); void mk_pinf(sort * s, expr_ref & result); void mk_one(sort * s, expr_ref & sign, expr_ref & result); void mk_neg(sort * s, expr_ref & x, expr_ref & result); void mk_add(sort * s, expr_ref & bv_rm, expr_ref & x, expr_ref & y, expr_ref & result); void mk_sub(sort * s, expr_ref & bv_rm, expr_ref & x, expr_ref & y, expr_ref & result); void mk_mul(sort * s, expr_ref & bv_rm, expr_ref & x, expr_ref & y, expr_ref & result); void mk_div(sort * s, expr_ref & bv_rm, expr_ref & x, expr_ref & y, expr_ref & result); void mk_rem(sort * s, expr_ref & x, expr_ref & y, expr_ref & result); void mk_round_to_integral(sort * s, expr_ref & rm, expr_ref & x, expr_ref & result); void mk_to_fp_float(sort * s, expr * rm, expr * x, expr_ref & result); func_decl * mk_bv_uf(func_decl * f, sort * const * domain, sort * range); expr_ref nan_wrap(expr * n); expr_ref extra_quantify(expr * e); }; #endif z3-z3-4.8.7/src/ast/fpa/fpa2bv_rewriter.cpp000066400000000000000000000256501356505360400203720ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: fpa2bv_rewriter.cpp Abstract: Rewriter for converting FPA to BV Author: Christoph (cwinter) 2012-02-09 Notes: --*/ #include "ast/rewriter/rewriter_def.h" #include "ast/fpa/fpa2bv_rewriter.h" #include "ast/fpa/fpa2bv_rewriter_params.hpp" fpa2bv_rewriter_cfg::fpa2bv_rewriter_cfg(ast_manager & m, fpa2bv_converter & c, params_ref const & p) : m_manager(m), m_out(m), m_conv(c), m_bindings(m) { updt_params(p); // We need to make sure that the manager has the BV plugin loaded. symbol s_bv("bv"); if (!m_manager.has_plugin(s_bv)) m_manager.register_plugin(s_bv, alloc(bv_decl_plugin)); } void fpa2bv_rewriter_cfg::updt_local_params(params_ref const & _p) { fpa2bv_rewriter_params p(_p); bool v = p.hi_fp_unspecified(); m_conv.set_unspecified_fp_hi(v); } void fpa2bv_rewriter_cfg::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); updt_local_params(p); } bool fpa2bv_rewriter_cfg::max_steps_exceeded(unsigned num_steps) const { return num_steps > m_max_steps; } br_status fpa2bv_rewriter_cfg::reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { TRACE("fpa2bv_rw", tout << "func: " << f->get_name() << std::endl; tout << "args: " << std::endl; for (unsigned i = 0; i < num; i++) tout << mk_ismt2_pp(args[i], m()) << std::endl;); if (num == 0 && f->get_family_id() == null_family_id && m_conv.is_float(f->get_range())) { m_conv.mk_const(f, result); return BR_DONE; } if (num == 0 && f->get_family_id() == null_family_id && m_conv.is_rm(f->get_range())) { m_conv.mk_rm_const(f, result); return BR_DONE; } if (m().is_eq(f)) { SASSERT(num == 2); TRACE("fpa2bv_rw", tout << "(= " << mk_ismt2_pp(args[0], m()) << " " << mk_ismt2_pp(args[1], m()) << ")" << std::endl;); SASSERT(m().get_sort(args[0]) == m().get_sort(args[1])); sort * ds = f->get_domain()[0]; if (m_conv.is_float(ds)) { m_conv.mk_eq(args[0], args[1], result); return BR_DONE; } else if (m_conv.is_rm(ds)) { result = m().mk_eq(args[0], args[1]); return BR_DONE; } return BR_FAILED; } else if (m().is_ite(f)) { SASSERT(num == 3); if (m_conv.is_float(args[1]) || m_conv.is_rm(args[1])) { m_conv.mk_ite(args[0], args[1], args[2], result); return BR_DONE; } return BR_FAILED; } else if (m().is_distinct(f)) { sort * ds = f->get_domain()[0]; if (m_conv.is_float(ds) || m_conv.is_rm(ds)) { m_conv.mk_distinct(f, num, args, result); return BR_DONE; } return BR_FAILED; } if (m_conv.is_float_family(f)) { switch (f->get_decl_kind()) { case OP_FPA_RM_NEAREST_TIES_TO_AWAY: case OP_FPA_RM_NEAREST_TIES_TO_EVEN: case OP_FPA_RM_TOWARD_NEGATIVE: case OP_FPA_RM_TOWARD_POSITIVE: case OP_FPA_RM_TOWARD_ZERO: m_conv.mk_rounding_mode(f->get_decl_kind(), result); return BR_DONE; case OP_FPA_NUM: m_conv.mk_numeral(f, num, args, result); return BR_DONE; case OP_FPA_PLUS_INF: m_conv.mk_pinf(f, result); return BR_DONE; case OP_FPA_MINUS_INF: m_conv.mk_ninf(f, result); return BR_DONE; case OP_FPA_PLUS_ZERO: m_conv.mk_pzero(f, result); return BR_DONE; case OP_FPA_MINUS_ZERO: m_conv.mk_nzero(f, result); return BR_DONE; case OP_FPA_NAN: m_conv.mk_nan(f, result); return BR_DONE; case OP_FPA_ADD: m_conv.mk_add(f, num, args, result); return BR_DONE; case OP_FPA_SUB: m_conv.mk_sub(f, num, args, result); return BR_DONE; case OP_FPA_NEG: m_conv.mk_neg(f, num, args, result); return BR_DONE; case OP_FPA_MUL: m_conv.mk_mul(f, num, args, result); return BR_DONE; case OP_FPA_DIV: m_conv.mk_div(f, num, args, result); return BR_DONE; case OP_FPA_REM: m_conv.mk_rem(f, num, args, result); return BR_DONE; case OP_FPA_ABS: m_conv.mk_abs(f, num, args, result); return BR_DONE; case OP_FPA_MIN: m_conv.mk_min(f, num, args, result); return BR_DONE; case OP_FPA_MAX: m_conv.mk_max(f, num, args, result); return BR_DONE; case OP_FPA_FMA: m_conv.mk_fma(f, num, args, result); return BR_DONE; case OP_FPA_SQRT: m_conv.mk_sqrt(f, num, args, result); return BR_DONE; case OP_FPA_ROUND_TO_INTEGRAL: m_conv.mk_round_to_integral(f, num, args, result); return BR_DONE; case OP_FPA_EQ: m_conv.mk_float_eq(f, num, args, result); return BR_DONE; case OP_FPA_LT: m_conv.mk_float_lt(f, num, args, result); return BR_DONE; case OP_FPA_GT: m_conv.mk_float_gt(f, num, args, result); return BR_DONE; case OP_FPA_LE: m_conv.mk_float_le(f, num, args, result); return BR_DONE; case OP_FPA_GE: m_conv.mk_float_ge(f, num, args, result); return BR_DONE; case OP_FPA_IS_ZERO: m_conv.mk_is_zero(f, num, args, result); return BR_DONE; case OP_FPA_IS_NAN: m_conv.mk_is_nan(f, num, args, result); return BR_DONE; case OP_FPA_IS_INF: m_conv.mk_is_inf(f, num, args, result); return BR_DONE; case OP_FPA_IS_NORMAL: m_conv.mk_is_normal(f, num, args, result); return BR_DONE; case OP_FPA_IS_SUBNORMAL: m_conv.mk_is_subnormal(f, num, args, result); return BR_DONE; case OP_FPA_IS_POSITIVE: m_conv.mk_is_positive(f, num, args, result); return BR_DONE; case OP_FPA_IS_NEGATIVE: m_conv.mk_is_negative(f, num, args, result); return BR_DONE; case OP_FPA_TO_FP: m_conv.mk_to_fp(f, num, args, result); return BR_DONE; case OP_FPA_TO_FP_UNSIGNED: m_conv.mk_to_fp_unsigned(f, num, args, result); return BR_DONE; case OP_FPA_FP: m_conv.mk_fp(f, num, args, result); return BR_DONE; case OP_FPA_TO_UBV: m_conv.mk_to_ubv(f, num, args, result); return BR_DONE; case OP_FPA_TO_SBV: m_conv.mk_to_sbv(f, num, args, result); return BR_DONE; case OP_FPA_TO_REAL: m_conv.mk_to_real(f, num, args, result); return BR_DONE; case OP_FPA_TO_IEEE_BV: m_conv.mk_to_ieee_bv(f, num, args, result); return BR_DONE; case OP_FPA_BVWRAP: case OP_FPA_BV2RM: return BR_FAILED; default: TRACE("fpa2bv", tout << "unsupported operator: " << f->get_name() << "\n"; for (unsigned i = 0; i < num; i++) tout << mk_ismt2_pp(args[i], m()) << std::endl;); NOT_IMPLEMENTED_YET(); } } else { SASSERT(!m_conv.is_float_family(f)); if (m_conv.fu().contains_floats(f)) { m_conv.mk_uf(f, num, args, result); return BR_DONE; } } return BR_FAILED; } bool fpa2bv_rewriter_cfg::pre_visit(expr * t) { TRACE("fpa2bv", tout << "pre_visit: " << mk_ismt2_pp(t, m()) << std::endl;); if (is_quantifier(t)) { quantifier * q = to_quantifier(t); TRACE("fpa2bv", tout << "pre_visit quantifier [" << q->get_id() << "]: " << mk_ismt2_pp(q->get_expr(), m()) << std::endl;); sort_ref_vector new_bindings(m_manager); for (unsigned i = 0 ; i < q->get_num_decls(); i++) new_bindings.push_back(q->get_decl_sort(i)); SASSERT(new_bindings.size() == q->get_num_decls()); m_bindings.append(new_bindings); } return true; } bool fpa2bv_rewriter_cfg::reduce_quantifier(quantifier * old_q, expr * new_body, expr * const * new_patterns, expr * const * new_no_patterns, expr_ref & result, proof_ref & result_pr) { if (is_lambda(old_q)) { return false; } unsigned curr_sz = m_bindings.size(); SASSERT(old_q->get_num_decls() <= curr_sz); unsigned num_decls = old_q->get_num_decls(); unsigned old_sz = curr_sz - num_decls; string_buffer<> name_buffer; ptr_buffer new_decl_sorts; sbuffer new_decl_names; for (unsigned i = 0; i < num_decls; i++) { symbol const & n = old_q->get_decl_name(i); sort * s = old_q->get_decl_sort(i); if (m_conv.is_float(s)) { unsigned ebits = m_conv.fu().get_ebits(s); unsigned sbits = m_conv.fu().get_sbits(s); name_buffer.reset(); name_buffer << n << ".bv"; new_decl_names.push_back(symbol(name_buffer.c_str())); new_decl_sorts.push_back(m_conv.bu().mk_sort(sbits+ebits)); } else if (m_conv.is_rm(s)) { name_buffer.reset(); name_buffer << n << ".bv"; new_decl_names.push_back(symbol(name_buffer.c_str())); new_decl_sorts.push_back(m_conv.bu().mk_sort(3)); } else { new_decl_sorts.push_back(s); new_decl_names.push_back(n); } } result = m().mk_quantifier(old_q->get_kind(), new_decl_sorts.size(), new_decl_sorts.c_ptr(), new_decl_names.c_ptr(), new_body, old_q->get_weight(), old_q->get_qid(), old_q->get_skid(), old_q->get_num_patterns(), new_patterns, old_q->get_num_no_patterns(), new_no_patterns); result_pr = nullptr; m_bindings.shrink(old_sz); TRACE("fpa2bv", tout << "reduce_quantifier[" << old_q->get_depth() << "]: " << mk_ismt2_pp(old_q->get_expr(), m()) << std::endl << " new body: " << mk_ismt2_pp(new_body, m()) << std::endl; tout << "result = " << mk_ismt2_pp(result, m()) << std::endl;); return true; } bool fpa2bv_rewriter_cfg::reduce_var(var * t, expr_ref & result, proof_ref & result_pr) { if (t->get_idx() >= m_bindings.size()) return false; // unsigned inx = m_bindings.size() - t->get_idx() - 1; expr_ref new_exp(m()); sort * s = t->get_sort(); if (m_conv.is_float(s)) { expr_ref new_var(m()); unsigned ebits = m_conv.fu().get_ebits(s); unsigned sbits = m_conv.fu().get_sbits(s); new_var = m().mk_var(t->get_idx(), m_conv.bu().mk_sort(sbits+ebits)); new_exp = m_conv.fu().mk_fp(m_conv.bu().mk_extract(sbits+ebits-1, sbits+ebits-1, new_var), m_conv.bu().mk_extract(ebits - 1, 0, new_var), m_conv.bu().mk_extract(sbits+ebits-2, ebits, new_var)); } else if (m_conv.is_rm(s)) { expr_ref new_var(m()); new_var = m().mk_var(t->get_idx(), m_conv.bu().mk_sort(3)); new_exp = m_conv.fu().mk_bv2rm(new_var); } else new_exp = m().mk_var(t->get_idx(), s); result = new_exp; result_pr = nullptr; TRACE("fpa2bv", tout << "reduce_var: " << mk_ismt2_pp(t, m()) << " -> " << mk_ismt2_pp(result, m()) << std::endl;); return true; } template class rewriter_tpl; z3-z3-4.8.7/src/ast/fpa/fpa2bv_rewriter.h000066400000000000000000000035651356505360400200400ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: fpa2bv_rewriter.h Abstract: Rewriter for converting FPA to BV Author: Christoph (cwinter) 2012-02-09 Notes: --*/ #ifndef FPA2BV_REWRITER_H_ #define FPA2BV_REWRITER_H_ #include "ast/rewriter/rewriter.h" #include "ast/bv_decl_plugin.h" #include "ast/fpa/fpa2bv_converter.h" struct fpa2bv_rewriter_cfg : public default_rewriter_cfg { ast_manager & m_manager; expr_ref_vector m_out; fpa2bv_converter & m_conv; sort_ref_vector m_bindings; unsigned long long m_max_memory; unsigned m_max_steps; ast_manager & m() const { return m_manager; } fpa2bv_rewriter_cfg(ast_manager & m, fpa2bv_converter & c, params_ref const & p); ~fpa2bv_rewriter_cfg() { } void cleanup_buffers() { m_out.finalize(); } void reset() { } void updt_local_params(params_ref const & _p); void updt_params(params_ref const & p); bool max_steps_exceeded(unsigned num_steps) const; br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr); bool pre_visit(expr * t); 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); bool reduce_var(var * t, expr_ref & result, proof_ref & result_pr); }; struct fpa2bv_rewriter : public rewriter_tpl { fpa2bv_rewriter_cfg m_cfg; fpa2bv_rewriter(ast_manager & m, fpa2bv_converter & c, params_ref const & p): rewriter_tpl(m, m.proofs_enabled(), m_cfg), m_cfg(m, c, p) { } }; #endif z3-z3-4.8.7/src/ast/fpa/fpa2bv_rewriter_params.pyg000066400000000000000000000004651356505360400217470ustar00rootroot00000000000000def_module_params(module_name='rewriter', class_name='fpa2bv_rewriter_params', export=True, params=(("hi_fp_unspecified", BOOL, False, "use the 'hardware interpretation' for unspecified values in fp.min, fp.max, fp.to_ubv, fp.to_sbv, and fp.to_real"), )) z3-z3-4.8.7/src/ast/fpa_decl_plugin.cpp000066400000000000000000001334361356505360400176360ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: fpa_decl_plugin.cpp Abstract: Floating point decl plugin Author: Leonardo de Moura (leonardo) 2012-01-15. Revision History: --*/ #include "ast/fpa_decl_plugin.h" #include "ast/arith_decl_plugin.h" #include "ast/bv_decl_plugin.h" #include "ast/ast_smt2_pp.h" fpa_decl_plugin::fpa_decl_plugin(): m_values(m_fm), m_value_table(mpf_hash_proc(m_values), mpf_eq_proc(m_values)) { m_real_sort = nullptr; m_int_sort = nullptr; m_bv_plugin = nullptr; } void fpa_decl_plugin::set_manager(ast_manager * m, family_id id) { decl_plugin::set_manager(m, id); m_arith_fid = m_manager->mk_family_id("arith"); m_real_sort = m_manager->mk_sort(m_arith_fid, REAL_SORT); SASSERT(m_real_sort != 0); // arith_decl_plugin must be installed before fpa_decl_plugin. m_manager->inc_ref(m_real_sort); m_int_sort = m_manager->mk_sort(m_arith_fid, INT_SORT); SASSERT(m_int_sort != 0); // arith_decl_plugin must be installed before fpa_decl_plugin. m_manager->inc_ref(m_int_sort); // BV is not optional anymore. SASSERT(m_manager->has_plugin(symbol("bv"))); m_bv_fid = m_manager->mk_family_id("bv"); m_bv_plugin = static_cast(m_manager->get_plugin(m_bv_fid)); } fpa_decl_plugin::~fpa_decl_plugin() { } unsigned fpa_decl_plugin::mk_id(mpf const & v) { unsigned new_id = m_id_gen.mk(); m_values.reserve(new_id+1); m_fm.set(m_values[new_id], v); unsigned old_id = m_value_table.insert_if_not_there(new_id); if (old_id != new_id) { m_id_gen.recycle(new_id); m_fm.del(m_values[new_id]); } return old_id; } void fpa_decl_plugin::recycled_id(unsigned id) { SASSERT(m_value_table.contains(id)); m_value_table.erase(id); m_id_gen.recycle(id); m_fm.del(m_values[id]); } bool fpa_decl_plugin::is_considered_uninterpreted(func_decl * f) { return false; } func_decl * fpa_decl_plugin::mk_numeral_decl(mpf const & v) { sort * s = mk_float_sort(v.get_ebits(), v.get_sbits()); func_decl * r = nullptr; if (m_fm.is_nan(v)) r = m_manager->mk_const_decl(symbol("NaN"), s, func_decl_info(m_family_id, OP_FPA_NAN)); else if (m_fm.is_pinf(v)) r = m_manager->mk_const_decl(symbol("+oo"), s, func_decl_info(m_family_id, OP_FPA_PLUS_INF)); else if (m_fm.is_ninf(v)) r = m_manager->mk_const_decl(symbol("-oo"), s, func_decl_info(m_family_id, OP_FPA_MINUS_INF)); else if (m_fm.is_pzero(v)) r = m_manager->mk_const_decl(symbol("+zero"), s, func_decl_info(m_family_id, OP_FPA_PLUS_ZERO)); else if (m_fm.is_nzero(v)) r = m_manager->mk_const_decl(symbol("-zero"), s, func_decl_info(m_family_id, OP_FPA_MINUS_ZERO)); else { SASSERT(m_fm.is_regular(v)); parameter p(mk_id(v), true); SASSERT(p.is_external()); sort * s = mk_float_sort(v.get_ebits(), v.get_sbits()); r = m_manager->mk_const_decl(symbol("fp.numeral"), s, func_decl_info(m_family_id, OP_FPA_NUM, 1, &p)); } return r; } app * fpa_decl_plugin::mk_numeral(mpf const & v) { app * r = m_manager->mk_const(mk_numeral_decl(v)); if (log_constant_meaning_prelude(r)) { m_fm.display_smt2(m_manager->trace_stream(), v, false); m_manager->trace_stream() << "\n"; } return r; } bool fpa_decl_plugin::is_numeral(expr * n, mpf & val) { if (is_app_of(n, m_family_id, OP_FPA_NUM)) { m_fm.set(val, m_values[to_app(n)->get_decl()->get_parameter(0).get_ext_id()]); return true; } else if (is_app_of(n, m_family_id, OP_FPA_MINUS_INF)) { unsigned ebits = to_app(n)->get_decl()->get_range()->get_parameter(0).get_int(); unsigned sbits = to_app(n)->get_decl()->get_range()->get_parameter(1).get_int(); m_fm.mk_ninf(ebits, sbits, val); return true; } else if (is_app_of(n, m_family_id, OP_FPA_PLUS_INF)) { unsigned ebits = to_app(n)->get_decl()->get_range()->get_parameter(0).get_int(); unsigned sbits = to_app(n)->get_decl()->get_range()->get_parameter(1).get_int(); m_fm.mk_pinf(ebits, sbits, val); return true; } else if (is_app_of(n, m_family_id, OP_FPA_NAN)) { unsigned ebits = to_app(n)->get_decl()->get_range()->get_parameter(0).get_int(); unsigned sbits = to_app(n)->get_decl()->get_range()->get_parameter(1).get_int(); m_fm.mk_nan(ebits, sbits, val); return true; } else if (is_app_of(n, m_family_id, OP_FPA_PLUS_ZERO)) { unsigned ebits = to_app(n)->get_decl()->get_range()->get_parameter(0).get_int(); unsigned sbits = to_app(n)->get_decl()->get_range()->get_parameter(1).get_int(); m_fm.mk_pzero(ebits, sbits, val); return true; } else if (is_app_of(n, m_family_id, OP_FPA_MINUS_ZERO)) { unsigned ebits = to_app(n)->get_decl()->get_range()->get_parameter(0).get_int(); unsigned sbits = to_app(n)->get_decl()->get_range()->get_parameter(1).get_int(); m_fm.mk_nzero(ebits, sbits, val); return true; } return false; } bool fpa_decl_plugin::is_numeral(expr * n) { scoped_mpf v(m_fm); return is_numeral(n, v); } bool fpa_decl_plugin::is_rm_numeral(expr * n, mpf_rounding_mode & val) { if (is_app_of(n, m_family_id, OP_FPA_RM_NEAREST_TIES_TO_AWAY)) { val = MPF_ROUND_NEAREST_TAWAY; return true; } else if (is_app_of(n, m_family_id, OP_FPA_RM_NEAREST_TIES_TO_EVEN)) { val = MPF_ROUND_NEAREST_TEVEN; return true; } else if (is_app_of(n, m_family_id, OP_FPA_RM_TOWARD_NEGATIVE)) { val = MPF_ROUND_TOWARD_NEGATIVE; return true; } else if (is_app_of(n, m_family_id, OP_FPA_RM_TOWARD_POSITIVE)) { val = MPF_ROUND_TOWARD_POSITIVE; return true; } else if (is_app_of(n, m_family_id, OP_FPA_RM_TOWARD_ZERO)) { val = MPF_ROUND_TOWARD_ZERO; return true; } return false; } bool fpa_decl_plugin::is_rm_numeral(expr * n) { mpf_rounding_mode t; return is_rm_numeral(n, t); } void fpa_decl_plugin::del(parameter const & p) { SASSERT(p.is_external()); recycled_id(p.get_ext_id()); } parameter fpa_decl_plugin::translate(parameter const & p, decl_plugin & target) { SASSERT(p.is_external()); fpa_decl_plugin & _target = static_cast(target); return parameter(_target.mk_id(m_values[p.get_ext_id()]), true); } void fpa_decl_plugin::finalize() { if (m_real_sort) { m_manager->dec_ref(m_real_sort); } if (m_int_sort) { m_manager->dec_ref(m_int_sort); } } decl_plugin * fpa_decl_plugin::mk_fresh() { return alloc(fpa_decl_plugin); } sort * fpa_decl_plugin::mk_float_sort(unsigned ebits, unsigned sbits) { if (sbits < 2) m_manager->raise_exception("minimum number of significand bits is 1"); if (ebits < 2) m_manager->raise_exception("minimum number of exponent bits is 2"); if (ebits > 63) m_manager->raise_exception("maximum number of exponent bits is 63"); parameter p1(ebits), p2(sbits); parameter ps[2] = { p1, p2 }; sort_size sz; sz = sort_size::mk_very_big(); // TODO: refine return m_manager->mk_sort(symbol("FloatingPoint"), sort_info(m_family_id, FLOATING_POINT_SORT, sz, 2, ps)); } sort * fpa_decl_plugin::mk_rm_sort() { return m_manager->mk_sort(symbol("RoundingMode"), sort_info(m_family_id, ROUNDING_MODE_SORT)); } sort * fpa_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) { switch (k) { case FLOATING_POINT_SORT: if (!(num_parameters == 2 && parameters[0].is_int() && parameters[1].is_int())) m_manager->raise_exception("expecting two integer parameters to floating point sort (ebits, sbits)"); return mk_float_sort(parameters[0].get_int(), parameters[1].get_int()); case ROUNDING_MODE_SORT: return mk_rm_sort(); case FLOAT16_SORT: return mk_float_sort(5, 11); case FLOAT32_SORT: return mk_float_sort(8, 24); case FLOAT64_SORT: return mk_float_sort(11, 53); case FLOAT128_SORT: return mk_float_sort(15, 113); default: m_manager->raise_exception("unknown floating point theory sort"); return nullptr; } } func_decl * fpa_decl_plugin::mk_rm_const_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { if (num_parameters != 0) m_manager->raise_exception("rounding mode constant does not have parameters"); if (arity != 0) m_manager->raise_exception("rounding mode is a constant"); sort * s = mk_rm_sort(); func_decl_info finfo(m_family_id, k); switch (k) { case OP_FPA_RM_NEAREST_TIES_TO_EVEN: return m_manager->mk_const_decl(symbol("roundNearestTiesToEven"), s, finfo); case OP_FPA_RM_NEAREST_TIES_TO_AWAY: return m_manager->mk_const_decl(symbol("roundNearestTiesToAway"), s, finfo); case OP_FPA_RM_TOWARD_POSITIVE: return m_manager->mk_const_decl(symbol("roundTowardPositive"), s, finfo); case OP_FPA_RM_TOWARD_NEGATIVE: return m_manager->mk_const_decl(symbol("roundTowardNegative"), s, finfo); case OP_FPA_RM_TOWARD_ZERO: return m_manager->mk_const_decl(symbol("roundTowardZero"), s, finfo); default: UNREACHABLE(); return nullptr; } } func_decl * fpa_decl_plugin::mk_float_const_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { sort * s = nullptr; if (num_parameters == 1 && parameters[0].is_ast() && is_sort(parameters[0].get_ast()) && is_float_sort(to_sort(parameters[0].get_ast()))) { s = to_sort(parameters[0].get_ast()); } else if (num_parameters == 2 && parameters[0].is_int() && parameters[1].is_int()) { s = mk_float_sort(parameters[0].get_int(), parameters[1].get_int()); } else if (range != nullptr && is_float_sort(range)) { s = range; } else { m_manager->raise_exception("sort of floating point constant was not specified"); UNREACHABLE(); } SASSERT(is_sort_of(s, m_family_id, FLOATING_POINT_SORT)); unsigned ebits = s->get_parameter(0).get_int(); unsigned sbits = s->get_parameter(1).get_int(); scoped_mpf val(m_fm); switch (k) { case OP_FPA_NAN: m_fm.mk_nan(ebits, sbits, val); SASSERT(m_fm.is_nan(val)); break; case OP_FPA_MINUS_INF: m_fm.mk_ninf(ebits, sbits, val); break; case OP_FPA_PLUS_INF: m_fm.mk_pinf(ebits, sbits, val); break; case OP_FPA_MINUS_ZERO: m_fm.mk_nzero(ebits, sbits, val); break; case OP_FPA_PLUS_ZERO: m_fm.mk_pzero(ebits, sbits, val); break; } return mk_numeral_decl(val); } func_decl * fpa_decl_plugin::mk_bin_rel_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { if (arity < 2) m_manager->raise_exception("invalid number of arguments to floating point relation"); if (domain[0] != domain[1] || !is_float_sort(domain[0])) m_manager->raise_exception("sort mismatch, expected equal FloatingPoint sorts as arguments"); symbol name; switch (k) { case OP_FPA_EQ: name = "fp.eq"; break; case OP_FPA_LT: name = "fp.lt"; break; case OP_FPA_GT: name = "fp.gt"; break; case OP_FPA_LE: name = "fp.leq"; break; case OP_FPA_GE: name = "fp.geq"; break; default: UNREACHABLE(); break; } func_decl_info finfo(m_family_id, k); finfo.set_chainable(true); return m_manager->mk_func_decl(name, domain[0], domain[1], m_manager->mk_bool_sort(), finfo); } func_decl * fpa_decl_plugin::mk_unary_rel_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { if (arity != 1) m_manager->raise_exception("invalid number of arguments to floating point relation"); if (!is_float_sort(domain[0])) m_manager->raise_exception("sort mismatch, expected argument of FloatingPoint sort"); symbol name; switch (k) { case OP_FPA_IS_ZERO: name = "fp.isZero"; break; case OP_FPA_IS_NEGATIVE: name = "fp.isNegative"; break; case OP_FPA_IS_POSITIVE: name = "fp.isPositive"; break; case OP_FPA_IS_NAN: name = "fp.isNaN"; break; case OP_FPA_IS_INF: name = "fp.isInfinite"; break; case OP_FPA_IS_NORMAL: name = "fp.isNormal"; break; case OP_FPA_IS_SUBNORMAL: name = "fp.isSubnormal"; break; default: UNREACHABLE(); break; } return m_manager->mk_func_decl(name, arity, domain, m_manager->mk_bool_sort(), func_decl_info(m_family_id, k)); } func_decl * fpa_decl_plugin::mk_unary_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { if (arity != 1) m_manager->raise_exception("invalid number of arguments to floating point operator"); if (!is_float_sort(domain[0])) m_manager->raise_exception("sort mismatch, expected argument of FloatingPoint sort"); symbol name; switch (k) { case OP_FPA_ABS: name = "fp.abs"; break; case OP_FPA_NEG: name = "fp.neg"; break; default: UNREACHABLE(); break; } return m_manager->mk_func_decl(name, arity, domain, domain[0], func_decl_info(m_family_id, k)); } func_decl * fpa_decl_plugin::mk_binary_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { if (arity != 2) m_manager->raise_exception("invalid number of arguments to floating point operator"); if (domain[0] != domain[1] || !is_float_sort(domain[0])) m_manager->raise_exception("sort mismatch, expected arguments of equal FloatingPoint sorts"); symbol name; switch (k) { case OP_FPA_REM: name = "fp.rem"; break; case OP_FPA_MIN: name = "fp.min"; break; case OP_FPA_MAX: name = "fp.max"; break; default: UNREACHABLE(); break; } return m_manager->mk_func_decl(name, arity, domain, domain[0], func_decl_info(m_family_id, k)); } func_decl * fpa_decl_plugin::mk_rm_binary_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { if (arity != 3) m_manager->raise_exception("invalid number of arguments to floating point operator"); if (!is_rm_sort(domain[0])) m_manager->raise_exception("sort mismatch, expected first argument of RoundingMode sort"); if (domain[1] != domain[2] || !is_float_sort(domain[1])) m_manager->raise_exception("sort mismatch, expected arguments 1 and 2 of equal FloatingPoint sorts"); symbol name; switch (k) { case OP_FPA_ADD: name = "fp.add"; break; case OP_FPA_SUB: name = "fp.sub"; break; case OP_FPA_MUL: name = "fp.mul"; break; case OP_FPA_DIV: name = "fp.div"; break; default: UNREACHABLE(); break; } return m_manager->mk_func_decl(name, arity, domain, domain[1], func_decl_info(m_family_id, k)); } func_decl * fpa_decl_plugin::mk_rm_unary_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { if (arity != 2) m_manager->raise_exception("invalid number of arguments to floating point operator"); if (!is_rm_sort(domain[0])) m_manager->raise_exception("sort mismatch, expected RoundingMode as first argument"); if (!is_float_sort(domain[1])) m_manager->raise_exception("sort mismatch, expected FloatingPoint as second argument"); symbol name; switch (k) { case OP_FPA_SQRT: name = "fp.sqrt"; break; case OP_FPA_ROUND_TO_INTEGRAL: name = "fp.roundToIntegral"; break; default: UNREACHABLE(); break; } return m_manager->mk_func_decl(name, arity, domain, domain[1], func_decl_info(m_family_id, k)); } func_decl * fpa_decl_plugin::mk_fma(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { if (arity != 4) m_manager->raise_exception("invalid number of arguments to fused_ma operator"); if (!is_rm_sort(domain[0])) m_manager->raise_exception("sort mismatch, expected RoundingMode as first argument"); if (domain[1] != domain[2] || domain[1] != domain[3] || !is_float_sort(domain[1])) m_manager->raise_exception("sort mismatch, expected arguments 1,2,3 of equal FloatingPoint sort"); symbol name("fp.fma"); return m_manager->mk_func_decl(name, arity, domain, domain[1], func_decl_info(m_family_id, k)); } func_decl * fpa_decl_plugin::mk_to_fp(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { if (m_bv_plugin && arity == 3 && is_sort_of(domain[0], m_bv_fid, BV_SORT) && is_sort_of(domain[1], m_bv_fid, BV_SORT) && is_sort_of(domain[2], m_bv_fid, BV_SORT)) { // 3 BVs -> 1 FP unsigned ebits = domain[1]->get_parameter(0).get_int(); unsigned sbits = domain[2]->get_parameter(0).get_int() + 1; parameter ps[] = { parameter(ebits), parameter(sbits) }; sort * fp = mk_float_sort(ebits, sbits); symbol name("to_fp"); return m_manager->mk_func_decl(name, arity, domain, fp, func_decl_info(m_family_id, k, 2, ps)); } else if (m_bv_plugin && arity == 1 && is_sort_of(domain[0], m_bv_fid, BV_SORT)) { // 1 BV -> 1 FP if (num_parameters != 2) m_manager->raise_exception("invalid number of parameters to to_fp"); if (!parameters[0].is_int() || !parameters[1].is_int()) m_manager->raise_exception("invalid parameter type to to_fp"); int ebits = parameters[0].get_int(); int sbits = parameters[1].get_int(); if (domain[0]->get_parameter(0).get_int() != (ebits + sbits)) m_manager->raise_exception("sort mismatch; invalid bit-vector size, expected bitvector of size (ebits+sbits)"); sort * fp = mk_float_sort(ebits, sbits); symbol name("to_fp"); return m_manager->mk_func_decl(name, arity, domain, fp, func_decl_info(m_family_id, k, num_parameters, parameters)); } else if (m_bv_plugin && arity == 2 && is_sort_of(domain[0], m_family_id, ROUNDING_MODE_SORT) && is_sort_of(domain[1], m_bv_fid, BV_SORT)) { // RoundingMode + 1 BV -> 1 FP if (num_parameters != 2) m_manager->raise_exception("invalid number of parameters to to_fp"); if (!parameters[0].is_int() || !parameters[1].is_int()) m_manager->raise_exception("invalid parameter type to to_fp"); int ebits = parameters[0].get_int(); int sbits = parameters[1].get_int(); sort * fp = mk_float_sort(ebits, sbits); symbol name("to_fp"); return m_manager->mk_func_decl(name, arity, domain, fp, func_decl_info(m_family_id, k, num_parameters, parameters)); } else if (arity == 2 && is_sort_of(domain[0], m_family_id, ROUNDING_MODE_SORT) && is_sort_of(domain[1], m_family_id, FLOATING_POINT_SORT)) { // Rounding + 1 FP -> 1 FP if (num_parameters != 2) m_manager->raise_exception("invalid number of parameters to to_fp"); if (!parameters[0].is_int() || !parameters[1].is_int()) m_manager->raise_exception("invalid parameter type to to_fp"); int ebits = parameters[0].get_int(); int sbits = parameters[1].get_int(); if (!is_rm_sort(domain[0])) m_manager->raise_exception("sort mismatch, expected first argument of RoundingMode sort"); if (!is_sort_of(domain[1], m_family_id, FLOATING_POINT_SORT)) m_manager->raise_exception("sort mismatch, expected second argument of FloatingPoint sort"); sort * fp = mk_float_sort(ebits, sbits); symbol name("to_fp"); return m_manager->mk_func_decl(name, arity, domain, fp, func_decl_info(m_family_id, k, num_parameters, parameters)); } else if (arity == 3 && is_sort_of(domain[0], m_family_id, ROUNDING_MODE_SORT) && is_sort_of(domain[1], m_arith_fid, REAL_SORT) && is_sort_of(domain[2], m_arith_fid, INT_SORT)) { // Rounding + 1 Real + 1 Int -> 1 FP if (!(num_parameters == 2 && parameters[0].is_int() && parameters[1].is_int())) m_manager->raise_exception("expecting two integer parameters to to_fp"); sort * fp = mk_float_sort(parameters[0].get_int(), parameters[1].get_int()); symbol name("to_fp"); return m_manager->mk_func_decl(name, arity, domain, fp, func_decl_info(m_family_id, k, num_parameters, parameters)); } else if (arity == 3 && is_sort_of(domain[0], m_family_id, ROUNDING_MODE_SORT) && is_sort_of(domain[1], m_arith_fid, INT_SORT) && is_sort_of(domain[2], m_arith_fid, REAL_SORT)) { // Rounding + 1 Int + 1 Real -> 1 FP if (!(num_parameters == 2 && parameters[0].is_int() && parameters[1].is_int())) m_manager->raise_exception("expecting two integer parameters to to_fp"); sort * fp = mk_float_sort(parameters[0].get_int(), parameters[1].get_int()); symbol name("to_fp"); return m_manager->mk_func_decl(name, arity, domain, fp, func_decl_info(m_family_id, k, num_parameters, parameters)); } else if (arity == 1 && is_sort_of(domain[0], m_arith_fid, REAL_SORT)) { // 1 Real -> 1 FP if (!(num_parameters == 2 && parameters[0].is_int() && parameters[1].is_int())) m_manager->raise_exception("expecting two integer parameters to to_fp"); if (domain[1] != m_real_sort) m_manager->raise_exception("sort mismatch, expected one argument of Real sort"); sort * fp = mk_float_sort(parameters[0].get_int(), parameters[1].get_int()); symbol name("to_fp"); return m_manager->mk_func_decl(name, arity, domain, fp, func_decl_info(m_family_id, k, num_parameters, parameters)); } else if (arity == 2 && is_sort_of(domain[0], m_family_id, ROUNDING_MODE_SORT) && is_sort_of(domain[1], m_arith_fid, REAL_SORT)) { // Rounding + 1 Real -> 1 FP if (!(num_parameters == 2 && parameters[0].is_int() && parameters[1].is_int())) m_manager->raise_exception("expecting two integer parameters to to_fp"); sort * fp = mk_float_sort(parameters[0].get_int(), parameters[1].get_int()); symbol name("to_fp"); return m_manager->mk_func_decl(name, arity, domain, fp, func_decl_info(m_family_id, k, num_parameters, parameters)); } else if (arity == 2 && is_sort_of(domain[0], m_family_id, ROUNDING_MODE_SORT) && is_sort_of(domain[1], m_arith_fid, INT_SORT)) { // Rounding + 1 Int -> 1 FP if (!(num_parameters == 2 && parameters[0].is_int() && parameters[1].is_int())) m_manager->raise_exception("expecting two integer parameters to to_fp"); sort * fp = mk_float_sort(parameters[0].get_int(), parameters[1].get_int()); symbol name("to_fp"); return m_manager->mk_func_decl(name, arity, domain, fp, func_decl_info(m_family_id, k, num_parameters, parameters)); } else { m_manager->raise_exception("Unexpected argument combination for (_ to_fp eb sb). Supported argument combinations are: " "((_ BitVec 1) (_ BitVec eb) (_ BitVec sb-1)), " "(_ BitVec (eb+sb)), " "(Real), " "(RoundingMode (_ BitVec (eb+sb))), " "(RoundingMode (_ FloatingPoint eb' sb')), " "(RoundingMode Int Real), " "(RoundingMode Real Int), " "(RoundingMode Int), and " "(RoundingMode Real)." ); } return nullptr; } func_decl * fpa_decl_plugin::mk_to_fp_unsigned(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { SASSERT(m_bv_plugin); if (arity != 2) m_manager->raise_exception("invalid number of arguments to to_fp_unsigned"); if (!is_sort_of(domain[0], m_family_id, ROUNDING_MODE_SORT)) m_manager->raise_exception("sort mismatch, expected first argument of RoundingMode sort"); if (!is_sort_of(domain[1], m_bv_fid, BV_SORT)) m_manager->raise_exception("sort mismatch, expected second argument of bit-vector sort"); // RoundingMode + 1 BV -> 1 FP if (num_parameters != 2) m_manager->raise_exception("invalid number of parameters to to_fp_unsigned"); if (!parameters[0].is_int() || !parameters[1].is_int()) m_manager->raise_exception("invalid parameter type to to_fp_unsigned"); int ebits = parameters[0].get_int(); int sbits = parameters[1].get_int(); sort * fp = mk_float_sort(ebits, sbits); symbol name("to_fp_unsigned"); return m_manager->mk_func_decl(name, arity, domain, fp, func_decl_info(m_family_id, k, num_parameters, parameters)); } func_decl * fpa_decl_plugin::mk_fp(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { if (arity != 3) m_manager->raise_exception("invalid number of arguments to fp"); if (!is_sort_of(domain[0], m_bv_fid, BV_SORT) || (domain[0]->get_parameter(0).get_int() != 1) || !is_sort_of(domain[1], m_bv_fid, BV_SORT) || !is_sort_of(domain[2], m_bv_fid, BV_SORT)) m_manager->raise_exception("sort mismatch, expected three bit-vectors, the first one of size 1."); int eb = (domain[1])->get_parameter(0).get_int(); int sb = (domain[2])->get_parameter(0).get_int() + 1; symbol name("fp"); sort * fp = mk_float_sort(eb, sb); return m_manager->mk_func_decl(name, arity, domain, fp, func_decl_info(m_family_id, k)); } func_decl * fpa_decl_plugin::mk_to_ubv(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { SASSERT(m_bv_plugin); if (arity != 2) m_manager->raise_exception("invalid number of arguments to fp.to_ubv"); if (num_parameters != 1) m_manager->raise_exception("invalid number of parameters to fp.to_ubv"); if (!parameters[0].is_int()) m_manager->raise_exception("invalid parameter type; fp.to_ubv expects an int parameter"); if (!is_rm_sort(domain[0])) m_manager->raise_exception("sort mismatch, expected first argument of RoundingMode sort"); if (!is_sort_of(domain[1], m_family_id, FLOATING_POINT_SORT)) m_manager->raise_exception("sort mismatch, expected second argument of FloatingPoint sort"); if (parameters[0].get_int() <= 0) m_manager->raise_exception("invalid parameter value; fp.to_ubv expects a parameter larger than 0"); symbol name("fp.to_ubv"); sort * bvs = m_bv_plugin->mk_sort(BV_SORT, 1, parameters); return m_manager->mk_func_decl(name, arity, domain, bvs, func_decl_info(m_family_id, k, num_parameters, parameters)); } func_decl * fpa_decl_plugin::mk_to_sbv(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { SASSERT(m_bv_plugin); if (arity != 2) m_manager->raise_exception("invalid number of arguments to fp.to_sbv"); if (num_parameters != 1) m_manager->raise_exception("invalid number of parameters to fp.to_sbv"); if (!parameters[0].is_int()) m_manager->raise_exception("invalid parameter type; fp.to_sbv expects an int parameter"); if (!is_rm_sort(domain[0])) m_manager->raise_exception("sort mismatch, expected first argument of RoundingMode sort"); if (!is_sort_of(domain[1], m_family_id, FLOATING_POINT_SORT)) m_manager->raise_exception("sort mismatch, expected second argument of FloatingPoint sort"); if (parameters[0].get_int() <= 0) m_manager->raise_exception("invalid parameter value; fp.to_sbv expects a parameter larger than 0"); symbol name("fp.to_sbv"); sort * bvs = m_bv_plugin->mk_sort(BV_SORT, 1, parameters); return m_manager->mk_func_decl(name, arity, domain, bvs, func_decl_info(m_family_id, k, num_parameters, parameters)); } func_decl * fpa_decl_plugin::mk_to_real(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { if (arity != 1) m_manager->raise_exception("invalid number of arguments to fp.to_real"); if (!is_float_sort(domain[0])) m_manager->raise_exception("sort mismatch, expected argument of FloatingPoint sort"); symbol name("fp.to_real"); return m_manager->mk_func_decl(name, 1, domain, m_real_sort, func_decl_info(m_family_id, k)); } func_decl * fpa_decl_plugin::mk_to_ieee_bv(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { if (arity != 1) m_manager->raise_exception("invalid number of arguments to fp.to_ieee_bv"); if (!is_float_sort(domain[0])) m_manager->raise_exception("sort mismatch, expected argument of FloatingPoint sort"); unsigned float_sz = domain[0]->get_parameter(0).get_int() + domain[0]->get_parameter(1).get_int(); parameter ps[] = { parameter(float_sz) }; sort * bv_srt = m_bv_plugin->mk_sort(BV_SORT, 1, ps); symbol name("fp.to_ieee_bv"); return m_manager->mk_func_decl(name, 1, domain, bv_srt, func_decl_info(m_family_id, k)); } func_decl * fpa_decl_plugin::mk_bv2rm(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { if (arity != 1) m_manager->raise_exception("invalid number of arguments to bv2rm"); if (!is_sort_of(domain[0], m_bv_fid, BV_SORT) || domain[0]->get_parameter(0).get_int() != 3) m_manager->raise_exception("sort mismatch, expected argument of sort bitvector, size 3"); if (!is_rm_sort(range)) m_manager->raise_exception("sort mismatch, expected range of RoundingMode sort"); parameter ps[] = { parameter(3) }; sort * bv_srt = m_bv_plugin->mk_sort(m_bv_fid, 1, ps); return m_manager->mk_func_decl(symbol("rm"), 1, &bv_srt, range, func_decl_info(m_family_id, k, num_parameters, parameters)); } func_decl * fpa_decl_plugin::mk_bv_wrap(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { if (arity != 1) m_manager->raise_exception("invalid number of arguments to bv_wrap"); if (!is_float_sort(domain[0]) && !is_rm_sort(domain[0])) m_manager->raise_exception("sort mismatch, expected argument of FloatingPoint or RoundingMode sort"); if (is_float_sort(domain[0])) { unsigned float_sz = domain[0]->get_parameter(0).get_int() + domain[0]->get_parameter(1).get_int(); parameter ps[] = { parameter(float_sz) }; sort * bv_srt = m_bv_plugin->mk_sort(m_bv_fid, 1, ps); return m_manager->mk_func_decl(symbol("bv_wrap"), 1, domain, bv_srt, func_decl_info(m_family_id, k, num_parameters, parameters)); } else { parameter ps[] = { parameter(3) }; sort * bv_srt = m_bv_plugin->mk_sort(m_bv_fid, 1, ps); return m_manager->mk_func_decl(symbol("bv_wrap"), 1, domain, bv_srt, func_decl_info(m_family_id, k, num_parameters, parameters)); } } func_decl * fpa_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { switch (k) { case OP_FPA_MINUS_INF: case OP_FPA_PLUS_INF: case OP_FPA_NAN: case OP_FPA_MINUS_ZERO: case OP_FPA_PLUS_ZERO: return mk_float_const_decl(k, num_parameters, parameters, arity, domain, range); case OP_FPA_RM_NEAREST_TIES_TO_EVEN: case OP_FPA_RM_NEAREST_TIES_TO_AWAY: case OP_FPA_RM_TOWARD_POSITIVE: case OP_FPA_RM_TOWARD_NEGATIVE: case OP_FPA_RM_TOWARD_ZERO: return mk_rm_const_decl(k, num_parameters, parameters, arity, domain, range); case OP_FPA_EQ: case OP_FPA_LT: case OP_FPA_GT: case OP_FPA_LE: case OP_FPA_GE: return mk_bin_rel_decl(k, num_parameters, parameters, arity, domain, range); case OP_FPA_IS_ZERO: case OP_FPA_IS_NEGATIVE: case OP_FPA_IS_POSITIVE: case OP_FPA_IS_NAN: case OP_FPA_IS_INF: case OP_FPA_IS_NORMAL: case OP_FPA_IS_SUBNORMAL: return mk_unary_rel_decl(k, num_parameters, parameters, arity, domain, range); case OP_FPA_ABS: case OP_FPA_NEG: return mk_unary_decl(k, num_parameters, parameters, arity, domain, range); case OP_FPA_REM: case OP_FPA_MIN: case OP_FPA_MAX: return mk_binary_decl(k, num_parameters, parameters, arity, domain, range); case OP_FPA_ADD: case OP_FPA_MUL: case OP_FPA_DIV: return mk_rm_binary_decl(k, num_parameters, parameters, arity, domain, range); case OP_FPA_SUB: if (arity == 1) return mk_unary_decl(OP_FPA_NEG, num_parameters, parameters, arity, domain, range); else return mk_rm_binary_decl(k, num_parameters, parameters, arity, domain, range); case OP_FPA_SQRT: case OP_FPA_ROUND_TO_INTEGRAL: return mk_rm_unary_decl(k, num_parameters, parameters, arity, domain, range); case OP_FPA_FMA: return mk_fma(k, num_parameters, parameters, arity, domain, range); case OP_FPA_FP: return mk_fp(k, num_parameters, parameters, arity, domain, range); case OP_FPA_TO_UBV: return mk_to_ubv(k, num_parameters, parameters, arity, domain, range); case OP_FPA_TO_SBV: return mk_to_sbv(k, num_parameters, parameters, arity, domain, range); case OP_FPA_TO_REAL: return mk_to_real(k, num_parameters, parameters, arity, domain, range); case OP_FPA_TO_FP: return mk_to_fp(k, num_parameters, parameters, arity, domain, range); case OP_FPA_TO_FP_UNSIGNED: return mk_to_fp_unsigned(k, num_parameters, parameters, arity, domain, range); case OP_FPA_TO_IEEE_BV: return mk_to_ieee_bv(k, num_parameters, parameters, arity, domain, range); case OP_FPA_BVWRAP: return mk_bv_wrap(k, num_parameters, parameters, arity, domain, range); case OP_FPA_BV2RM: return mk_bv2rm(k, num_parameters, parameters, arity, domain, range); default: m_manager->raise_exception("unsupported floating point operator"); return nullptr; } } void fpa_decl_plugin::get_op_names(svector & op_names, symbol const & logic) { // These are the operators from the final draft of the SMT FloatingPoint standard op_names.push_back(builtin_name("+oo", OP_FPA_PLUS_INF)); op_names.push_back(builtin_name("-oo", OP_FPA_MINUS_INF)); op_names.push_back(builtin_name("+zero", OP_FPA_PLUS_ZERO)); op_names.push_back(builtin_name("-zero", OP_FPA_MINUS_ZERO)); op_names.push_back(builtin_name("NaN", OP_FPA_NAN)); op_names.push_back(builtin_name("roundNearestTiesToEven", OP_FPA_RM_NEAREST_TIES_TO_EVEN)); op_names.push_back(builtin_name("roundNearestTiesToAway", OP_FPA_RM_NEAREST_TIES_TO_AWAY)); op_names.push_back(builtin_name("roundTowardPositive", OP_FPA_RM_TOWARD_POSITIVE)); op_names.push_back(builtin_name("roundTowardNegative", OP_FPA_RM_TOWARD_NEGATIVE)); op_names.push_back(builtin_name("roundTowardZero", OP_FPA_RM_TOWARD_ZERO)); op_names.push_back(builtin_name("RNE", OP_FPA_RM_NEAREST_TIES_TO_EVEN)); op_names.push_back(builtin_name("RNA", OP_FPA_RM_NEAREST_TIES_TO_AWAY)); op_names.push_back(builtin_name("RTP", OP_FPA_RM_TOWARD_POSITIVE)); op_names.push_back(builtin_name("RTN", OP_FPA_RM_TOWARD_NEGATIVE)); op_names.push_back(builtin_name("RTZ", OP_FPA_RM_TOWARD_ZERO)); op_names.push_back(builtin_name("fp.abs", OP_FPA_ABS)); op_names.push_back(builtin_name("fp.neg", OP_FPA_NEG)); op_names.push_back(builtin_name("fp.add", OP_FPA_ADD)); op_names.push_back(builtin_name("fp.sub", OP_FPA_SUB)); op_names.push_back(builtin_name("fp.mul", OP_FPA_MUL)); op_names.push_back(builtin_name("fp.div", OP_FPA_DIV)); op_names.push_back(builtin_name("fp.fma", OP_FPA_FMA)); op_names.push_back(builtin_name("fp.sqrt", OP_FPA_SQRT)); op_names.push_back(builtin_name("fp.rem", OP_FPA_REM)); op_names.push_back(builtin_name("fp.roundToIntegral", OP_FPA_ROUND_TO_INTEGRAL)); op_names.push_back(builtin_name("fp.min", OP_FPA_MIN)); op_names.push_back(builtin_name("fp.max", OP_FPA_MAX)); op_names.push_back(builtin_name("fp.leq", OP_FPA_LE)); op_names.push_back(builtin_name("fp.lt", OP_FPA_LT)); op_names.push_back(builtin_name("fp.geq", OP_FPA_GE)); op_names.push_back(builtin_name("fp.gt", OP_FPA_GT)); op_names.push_back(builtin_name("fp.eq", OP_FPA_EQ)); op_names.push_back(builtin_name("fp.isNormal", OP_FPA_IS_NORMAL)); op_names.push_back(builtin_name("fp.isSubnormal", OP_FPA_IS_SUBNORMAL)); op_names.push_back(builtin_name("fp.isZero", OP_FPA_IS_ZERO)); op_names.push_back(builtin_name("fp.isInfinite", OP_FPA_IS_INF)); op_names.push_back(builtin_name("fp.isNaN", OP_FPA_IS_NAN)); op_names.push_back(builtin_name("fp.isNegative", OP_FPA_IS_NEGATIVE)); op_names.push_back(builtin_name("fp.isPositive", OP_FPA_IS_POSITIVE)); op_names.push_back(builtin_name("fp", OP_FPA_FP)); op_names.push_back(builtin_name("fp.to_ubv", OP_FPA_TO_UBV)); op_names.push_back(builtin_name("fp.to_sbv", OP_FPA_TO_SBV)); op_names.push_back(builtin_name("fp.to_real", OP_FPA_TO_REAL)); op_names.push_back(builtin_name("to_fp", OP_FPA_TO_FP)); op_names.push_back(builtin_name("to_fp_unsigned", OP_FPA_TO_FP_UNSIGNED)); /* Extensions */ op_names.push_back(builtin_name("to_ieee_bv", OP_FPA_TO_IEEE_BV)); op_names.push_back(builtin_name("fp.to_ieee_bv", OP_FPA_TO_IEEE_BV)); } void fpa_decl_plugin::get_sort_names(svector & sort_names, symbol const & logic) { sort_names.push_back(builtin_name("FloatingPoint", FLOATING_POINT_SORT)); sort_names.push_back(builtin_name("RoundingMode", ROUNDING_MODE_SORT)); // The final theory supports three common FloatingPoint sorts sort_names.push_back(builtin_name("Float16", FLOAT16_SORT)); sort_names.push_back(builtin_name("Float32", FLOAT32_SORT)); sort_names.push_back(builtin_name("Float64", FLOAT64_SORT)); sort_names.push_back(builtin_name("Float128", FLOAT128_SORT)); } expr * fpa_decl_plugin::get_some_value(sort * s) { if (s->is_sort_of(m_family_id, FLOATING_POINT_SORT)) { mpf tmp; m_fm.mk_nan(s->get_parameter(0).get_int(), s->get_parameter(1).get_int(), tmp); expr * res = mk_numeral(tmp); m_fm.del(tmp); return res; } else if (s->is_sort_of(m_family_id, ROUNDING_MODE_SORT)) { func_decl * f = mk_rm_const_decl(OP_FPA_RM_TOWARD_ZERO, 0, nullptr, 0, nullptr, s); return m_manager->mk_const(f); } UNREACHABLE(); return nullptr; } bool fpa_decl_plugin::is_value(app * e) const { if (e->get_family_id() != m_family_id) return false; switch (e->get_decl_kind()) { case OP_FPA_RM_NEAREST_TIES_TO_EVEN: case OP_FPA_RM_NEAREST_TIES_TO_AWAY: case OP_FPA_RM_TOWARD_POSITIVE: case OP_FPA_RM_TOWARD_NEGATIVE: case OP_FPA_RM_TOWARD_ZERO: case OP_FPA_NUM: case OP_FPA_PLUS_INF: case OP_FPA_MINUS_INF: case OP_FPA_PLUS_ZERO: case OP_FPA_MINUS_ZERO: case OP_FPA_NAN: return true; case OP_FPA_FP: return m_manager->is_value(e->get_arg(0)) && m_manager->is_value(e->get_arg(1)) && m_manager->is_value(e->get_arg(2)); default: return false; } } bool fpa_decl_plugin::is_unique_value(app* e) const { if (e->get_family_id() != m_family_id) return false; switch (e->get_decl_kind()) { case OP_FPA_RM_NEAREST_TIES_TO_EVEN: case OP_FPA_RM_NEAREST_TIES_TO_AWAY: case OP_FPA_RM_TOWARD_POSITIVE: case OP_FPA_RM_TOWARD_NEGATIVE: case OP_FPA_RM_TOWARD_ZERO: return true; case OP_FPA_PLUS_INF: /* No; +oo == fp(#b0 #b11 #b00) */ case OP_FPA_MINUS_INF: /* No; -oo == fp #b1 #b11 #b00) */ case OP_FPA_PLUS_ZERO: /* No; +zero == fp #b0 #b00 #b000) */ case OP_FPA_MINUS_ZERO: /* No; -zero == fp #b1 #b00 #b000) */ case OP_FPA_NAN: /* No; NaN == (fp #b0 #b111111 #b0000001) */ case OP_FPA_NUM: /* see NaN */ return false; case OP_FPA_FP: return m_manager->is_unique_value(e->get_arg(0)) && m_manager->is_unique_value(e->get_arg(1)) && m_manager->is_unique_value(e->get_arg(2)); default: return false; } } fpa_util::fpa_util(ast_manager & m): m_manager(m), m_fid(m.mk_family_id("fpa")), m_a_util(m), m_bv_util(m) { m_plugin = static_cast(m.get_plugin(m_fid)); } fpa_util::~fpa_util() { } sort * fpa_util::mk_float_sort(unsigned ebits, unsigned sbits) { parameter ps[2] = { parameter(ebits), parameter(sbits) }; return m().mk_sort(m_fid, FLOATING_POINT_SORT, 2, ps); } unsigned fpa_util::get_ebits(sort * s) const { SASSERT(is_float(s)); return static_cast(s->get_parameter(0).get_int()); } unsigned fpa_util::get_sbits(sort * s) const { SASSERT(is_float(s)); return static_cast(s->get_parameter(1).get_int()); } app * fpa_util::mk_nan(unsigned ebits, unsigned sbits) { scoped_mpf v(fm()); fm().mk_nan(ebits, sbits, v); return mk_value(v); } app * fpa_util::mk_pinf(unsigned ebits, unsigned sbits) { scoped_mpf v(fm()); fm().mk_pinf(ebits, sbits, v); return mk_value(v); } app * fpa_util::mk_ninf(unsigned ebits, unsigned sbits) { scoped_mpf v(fm()); fm().mk_ninf(ebits, sbits, v); return mk_value(v); } app * fpa_util::mk_pzero(unsigned ebits, unsigned sbits) { scoped_mpf v(fm()); fm().mk_pzero(ebits, sbits, v); return mk_value(v); } app * fpa_util::mk_nzero(unsigned ebits, unsigned sbits) { scoped_mpf v(fm()); fm().mk_nzero(ebits, sbits, v); return mk_value(v); } bool fpa_util::contains_floats(ast * a) { switch (a->get_kind()) { case AST_APP: { app * aa = to_app(a); if (contains_floats(aa->get_decl())) return true; else for (unsigned i = 0; i < aa->get_num_args(); i++) if (contains_floats(aa->get_arg(i))) return true; break; } case AST_VAR: return contains_floats(to_var(a)->get_sort()); break; case AST_QUANTIFIER: { quantifier * q = to_quantifier(a); for (unsigned i = 0; i < q->get_num_children(); i++) if (contains_floats(q->get_child(i))) return true; for (unsigned i = 0; i < q->get_num_decls(); i++) if (contains_floats(q->get_decl_sort(i))) return true; if (contains_floats(q->get_expr())) return true; break; } case AST_SORT: { sort * s = to_sort(a); if (is_float(s) || is_rm(s)) return true; else { for (unsigned i = 0; i < s->get_num_parameters(); i++) { parameter const & pi = s->get_parameter(i); if (pi.is_ast() && contains_floats(pi.get_ast())) return true; } } break; } case AST_FUNC_DECL: { func_decl * f = to_func_decl(a); for (unsigned i = 0; i < f->get_arity(); i++) if (contains_floats(f->get_domain(i))) return true; if (contains_floats(f->get_range())) return true; for (unsigned i = 0; i < f->get_num_parameters(); i++) { parameter const & pi = f->get_parameter(i); if (pi.is_ast() && contains_floats(pi.get_ast())) return true; } break; } default: UNREACHABLE(); } return false; } bool fpa_util::is_considered_uninterpreted(func_decl * f, unsigned n, expr* const* args) { TRACE("fpa_util", expr_ref t(m().mk_app(f, n, args), m()); tout << mk_ismt2_pp(t, m()) << std::endl; ); family_id ffid = plugin().get_family_id(); if (f->get_family_id() != ffid) return false; if (is_decl_of(f, ffid, OP_FPA_TO_IEEE_BV)) { SASSERT(n == 1); expr* x = args[0]; return is_nan(x); } else if (is_decl_of(f, ffid, OP_FPA_TO_SBV) || is_decl_of(f, ffid, OP_FPA_TO_UBV)) { SASSERT(n == 2); SASSERT(f->get_num_parameters() == 1); bool is_signed = f->get_decl_kind() == OP_FPA_TO_SBV; expr* rm = args[0]; expr* x = args[1]; unsigned bv_sz = f->get_parameter(0).get_int(); mpf_rounding_mode rmv; mpf v; if (!is_rm_numeral(rm, rmv) || !is_numeral(x, v)) return false; if (is_nan(x) || is_inf(x)) return true; unsynch_mpq_manager& mpqm = plugin().fm().mpq_manager(); scoped_mpq r(mpqm); plugin().fm().to_sbv_mpq(rmv, v, r); if (is_signed) return mpqm.bitsize(r) >= bv_sz; else return mpqm.is_neg(r) || mpqm.bitsize(r) > bv_sz; } else if (is_decl_of(f, ffid, OP_FPA_TO_REAL)) { SASSERT(n == 1); expr* x = args[0]; return is_nan(x) || is_inf(x); } return plugin().is_considered_uninterpreted(f); } z3-z3-4.8.7/src/ast/fpa_decl_plugin.h000066400000000000000000000414731356505360400173020ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: fpa_decl_plugin.h Abstract: Floating point decl plugin Author: Leonardo de Moura (leonardo) 2012-01-15. Revision History: --*/ #ifndef fpa_decl_plugin_H_ #define fpa_decl_plugin_H_ #include "ast/ast.h" #include "util/id_gen.h" #include "ast/arith_decl_plugin.h" #include "ast/bv_decl_plugin.h" #include "util/mpf.h" enum fpa_sort_kind { FLOATING_POINT_SORT, ROUNDING_MODE_SORT, FLOAT16_SORT, FLOAT32_SORT, FLOAT64_SORT, FLOAT128_SORT }; typedef enum { BV_RM_TIES_TO_EVEN, BV_RM_TIES_TO_AWAY, BV_RM_TO_POSITIVE, BV_RM_TO_NEGATIVE, BV_RM_TO_ZERO = 4 } BV_RM_VAL; enum fpa_op_kind { OP_FPA_RM_NEAREST_TIES_TO_EVEN, OP_FPA_RM_NEAREST_TIES_TO_AWAY, OP_FPA_RM_TOWARD_POSITIVE, OP_FPA_RM_TOWARD_NEGATIVE, OP_FPA_RM_TOWARD_ZERO, OP_FPA_NUM, OP_FPA_PLUS_INF, OP_FPA_MINUS_INF, OP_FPA_NAN, OP_FPA_PLUS_ZERO, OP_FPA_MINUS_ZERO, OP_FPA_ADD, OP_FPA_SUB, OP_FPA_NEG, OP_FPA_MUL, OP_FPA_DIV, OP_FPA_REM, OP_FPA_ABS, OP_FPA_MIN, OP_FPA_MAX, OP_FPA_FMA, // x*y + z OP_FPA_SQRT, OP_FPA_ROUND_TO_INTEGRAL, OP_FPA_EQ, OP_FPA_LT, OP_FPA_GT, OP_FPA_LE, OP_FPA_GE, OP_FPA_IS_NAN, OP_FPA_IS_INF, OP_FPA_IS_ZERO, OP_FPA_IS_NORMAL, OP_FPA_IS_SUBNORMAL, OP_FPA_IS_NEGATIVE, OP_FPA_IS_POSITIVE, OP_FPA_FP, OP_FPA_TO_FP, OP_FPA_TO_FP_UNSIGNED, OP_FPA_TO_UBV, OP_FPA_TO_SBV, OP_FPA_TO_REAL, /* Extensions */ OP_FPA_TO_IEEE_BV, OP_FPA_BVWRAP, OP_FPA_BV2RM, LAST_FLOAT_OP }; class fpa_decl_plugin : public decl_plugin { struct mpf_hash_proc { scoped_mpf_vector const & m_values; mpf_hash_proc(scoped_mpf_vector const & values):m_values(values) {} unsigned operator()(unsigned id) const { return m_values.m().hash(m_values[id]); } }; struct mpf_eq_proc { scoped_mpf_vector const & m_values; mpf_eq_proc(scoped_mpf_vector const & values):m_values(values) {} bool operator()(unsigned id1, unsigned id2) const { return m_values.m().eq_core(m_values[id1], m_values[id2]); } }; typedef chashtable value_table; mpf_manager m_fm; id_gen m_id_gen; scoped_mpf_vector m_values; value_table m_value_table; sort * m_real_sort; sort * m_int_sort; family_id m_arith_fid; family_id m_bv_fid; bv_decl_plugin * m_bv_plugin; sort * mk_float_sort(unsigned ebits, unsigned sbits); sort * mk_rm_sort(); func_decl * mk_rm_const_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); func_decl * mk_float_const_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); func_decl * mk_bin_rel_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); func_decl * mk_unary_rel_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); func_decl * mk_unary_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); func_decl * mk_binary_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); func_decl * mk_rm_binary_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); func_decl * mk_rm_unary_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); func_decl * mk_fma(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); func_decl * mk_fp(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); func_decl * mk_to_fp(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); func_decl * mk_to_fp_unsigned(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); func_decl * mk_to_ubv(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); func_decl * mk_to_sbv(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); func_decl * mk_to_real(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); func_decl * mk_to_ieee_bv(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); func_decl * mk_bv2rm(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); func_decl * mk_bv_wrap(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); void set_manager(ast_manager * m, family_id id) override; unsigned mk_id(mpf const & v); void recycled_id(unsigned id); public: fpa_decl_plugin(); bool is_float_sort(sort * s) const { return is_sort_of(s, m_family_id, FLOATING_POINT_SORT); } bool is_rm_sort(sort * s) const { return is_sort_of(s, m_family_id, ROUNDING_MODE_SORT); } ~fpa_decl_plugin() override; void finalize() override; decl_plugin * mk_fresh() override; sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) override; func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) override; void get_op_names(svector & op_names, symbol const & logic) override; void get_sort_names(svector & sort_names, symbol const & logic) override; expr * get_some_value(sort * s) override; bool is_value(app* e) const override; bool is_unique_value(app* e) const override; mpf_manager & fm() { return m_fm; } func_decl * mk_numeral_decl(mpf const & v); app * mk_numeral(mpf const & v); bool is_numeral(expr * n); bool is_numeral(expr * n, mpf & val); bool is_rm_numeral(expr * n, mpf_rounding_mode & val); bool is_rm_numeral(expr * n); mpf const & get_value(unsigned id) const { SASSERT(m_value_table.contains(id)); return m_values[id]; } void del(parameter const & p) override; parameter translate(parameter const & p, decl_plugin & target) override; bool is_considered_uninterpreted(func_decl * f) override; }; class fpa_util { ast_manager & m_manager; fpa_decl_plugin * m_plugin; family_id m_fid; arith_util m_a_util; bv_util m_bv_util; public: fpa_util(ast_manager & m); ~fpa_util(); ast_manager & m() const { return m_manager; } mpf_manager & fm() const { return m_plugin->fm(); } family_id get_fid() const { return m_fid; } family_id get_family_id() const { return m_fid; } arith_util & au() { return m_a_util; } bv_util & bu() { return m_bv_util; } fpa_decl_plugin & plugin() { return *m_plugin; } sort * mk_float_sort(unsigned ebits, unsigned sbits); sort * mk_rm_sort() { return m().mk_sort(m_fid, ROUNDING_MODE_SORT); } bool is_float(sort * s) const { return is_sort_of(s, m_fid, FLOATING_POINT_SORT); } bool is_rm(sort * s) const { return is_sort_of(s, m_fid, ROUNDING_MODE_SORT); } bool is_float(expr * e) const { return is_float(m_manager.get_sort(e)); } bool is_rm(expr * e) const { return is_rm(m_manager.get_sort(e)); } bool is_fp(expr const * e) const { return is_app_of(e, m_fid, OP_FPA_FP); } unsigned get_ebits(sort * s) const; unsigned get_sbits(sort * s) const; app * mk_round_nearest_ties_to_even() { return m().mk_const(m_fid, OP_FPA_RM_NEAREST_TIES_TO_EVEN); } app * mk_round_nearest_ties_to_away() { return m().mk_const(m_fid, OP_FPA_RM_NEAREST_TIES_TO_AWAY); } app * mk_round_toward_positive() { return m().mk_const(m_fid, OP_FPA_RM_TOWARD_POSITIVE); } app * mk_round_toward_negative() { return m().mk_const(m_fid, OP_FPA_RM_TOWARD_NEGATIVE); } app * mk_round_toward_zero() { return m().mk_const(m_fid, OP_FPA_RM_TOWARD_ZERO); } app * mk_nan(unsigned ebits, unsigned sbits); app * mk_pinf(unsigned ebits, unsigned sbits); app * mk_ninf(unsigned ebits, unsigned sbits); app * mk_nan(sort * s) { return mk_nan(get_ebits(s), get_sbits(s)); } app * mk_pinf(sort * s) { return mk_pinf(get_ebits(s), get_sbits(s)); } app * mk_ninf(sort * s) { return mk_ninf(get_ebits(s), get_sbits(s)); } app * mk_value(mpf const & v) { return m_plugin->mk_numeral(v); } bool is_numeral(expr * n) { return m_plugin->is_numeral(n); } bool is_numeral(expr * n, mpf & v) { return m_plugin->is_numeral(n, v); } bool is_rm_numeral(expr * n) { return m_plugin->is_rm_numeral(n); } bool is_rm_numeral(expr * n, mpf_rounding_mode & v) { return m_plugin->is_rm_numeral(n, v); } app * mk_pzero(unsigned ebits, unsigned sbits); app * mk_nzero(unsigned ebits, unsigned sbits); app * mk_pzero(sort * s) { return mk_pzero(get_ebits(s), get_sbits(s)); } app * mk_nzero(sort * s) { return mk_nzero(get_ebits(s), get_sbits(s)); } bool is_nan(expr * n) { scoped_mpf v(fm()); return is_numeral(n, v) && fm().is_nan(v); } bool is_inf(expr * n) { scoped_mpf v(fm()); return is_numeral(n, v) && fm().is_inf(v); } bool is_pinf(expr * n) { scoped_mpf v(fm()); return is_numeral(n, v) && fm().is_pinf(v); } bool is_ninf(expr * n) { scoped_mpf v(fm()); return is_numeral(n, v) && fm().is_ninf(v); } bool is_zero(expr * n) { scoped_mpf v(fm()); return is_numeral(n, v) && fm().is_zero(v); } bool is_pzero(expr * n) { scoped_mpf v(fm()); return is_numeral(n, v) && fm().is_pzero(v); } bool is_nzero(expr * n) { scoped_mpf v(fm()); return is_numeral(n, v) && fm().is_nzero(v); } bool is_normal(expr * n) { scoped_mpf v(fm()); return is_numeral(n, v) && fm().is_normal(v); } bool is_subnormal(expr * n) { scoped_mpf v(fm()); return is_numeral(n, v) && fm().is_denormal(v); } bool is_positive(expr * n) { scoped_mpf v(fm()); return is_numeral(n, v) && fm().is_pos(v); } bool is_negative(expr * n) { scoped_mpf v(fm()); return is_numeral(n, v) && fm().is_neg(v); } app * mk_fp(expr * sgn, expr * exp, expr * sig) { SASSERT(m_bv_util.is_bv(sgn) && m_bv_util.get_bv_size(sgn) == 1); SASSERT(m_bv_util.is_bv(exp)); SASSERT(m_bv_util.is_bv(sig)); return m().mk_app(m_fid, OP_FPA_FP, sgn, exp, sig); } app * mk_to_fp(sort * s, expr * bv_t) { SASSERT(is_float(s) && s->get_num_parameters() == 2); return m().mk_app(m_fid, OP_FPA_TO_FP, 2, s->get_parameters(), 1, &bv_t); } app * mk_to_fp(sort * s, expr * rm, expr * t) { SASSERT(is_float(s) && s->get_num_parameters() == 2); expr * args[] = { rm, t }; return m().mk_app(m_fid, OP_FPA_TO_FP, 2, s->get_parameters(), 2, args); } app * mk_to_fp(sort * s, expr * rm, expr * exp, expr * sig) { SASSERT(is_float(s) && s->get_num_parameters() == 2); expr * args[] = { rm, exp, sig}; return m().mk_app(m_fid, OP_FPA_TO_FP, 2, s->get_parameters(), 3, args); } app * mk_to_fp_unsigned(sort * s, expr * rm, expr * t) { SASSERT(is_float(s) && s->get_num_parameters() == 2); expr * args[] = { rm, t }; return m().mk_app(m_fid, OP_FPA_TO_FP_UNSIGNED, 2, s->get_parameters(), 2, args); } bool is_to_fp(expr * n) { return is_app_of(n, m_fid, OP_FPA_TO_FP); } app * mk_to_ubv(expr * rm, expr * t, unsigned sz) { parameter ps[] = { parameter(sz) }; expr * args[] = { rm, t }; return m().mk_app(m_fid, OP_FPA_TO_UBV, 1, ps, 2, args); } app * mk_to_sbv(expr * rm, expr * t, unsigned sz) { parameter ps[] = { parameter(sz) }; expr * args[] = { rm, t }; return m().mk_app(m_fid, OP_FPA_TO_SBV, 1, ps, 2, args); } app * mk_to_real(expr * t) { return m().mk_app(m_fid, OP_FPA_TO_REAL, t); } app * mk_add(expr * arg1, expr * arg2, expr * arg3) { return m().mk_app(m_fid, OP_FPA_ADD, arg1, arg2, arg3); } app * mk_mul(expr * arg1, expr * arg2, expr * arg3) { return m().mk_app(m_fid, OP_FPA_MUL, arg1, arg2, arg3); } app * mk_sub(expr * arg1, expr * arg2, expr * arg3) { return m().mk_app(m_fid, OP_FPA_SUB, arg1, arg2, arg3); } app * mk_div(expr * arg1, expr * arg2, expr * arg3) { return m().mk_app(m_fid, OP_FPA_DIV, arg1, arg2, arg3); } app * mk_neg(expr * arg1) { return m().mk_app(m_fid, OP_FPA_NEG, arg1); } app * mk_rem(expr * arg1, expr * arg2) { return m().mk_app(m_fid, OP_FPA_REM, arg1, arg2); } app * mk_max(expr * arg1, expr * arg2) { return m().mk_app(m_fid, OP_FPA_MAX, arg1, arg2); } app * mk_min(expr * arg1, expr * arg2) { return m().mk_app(m_fid, OP_FPA_MIN, arg1, arg2); } app * mk_abs(expr * arg1) { return m().mk_app(m_fid, OP_FPA_ABS, arg1); } app * mk_sqrt(expr * arg1, expr * arg2) { return m().mk_app(m_fid, OP_FPA_SQRT, arg1, arg2); } app * mk_round_to_integral(expr * arg1, expr * arg2) { return m().mk_app(m_fid, OP_FPA_ROUND_TO_INTEGRAL, arg1, arg2); } app * mk_fma(expr * arg1, expr * arg2, expr * arg3, expr * arg4) { expr * args[4] = { arg1, arg2, arg3, arg4 }; return m().mk_app(m_fid, OP_FPA_FMA, 4, args); } app * mk_float_eq(expr * arg1, expr * arg2) { return m().mk_app(m_fid, OP_FPA_EQ, arg1, arg2); } app * mk_lt(expr * arg1, expr * arg2) { return m().mk_app(m_fid, OP_FPA_LT, arg1, arg2); } app * mk_gt(expr * arg1, expr * arg2) { return m().mk_app(m_fid, OP_FPA_GT, arg1, arg2); } app * mk_le(expr * arg1, expr * arg2) { return m().mk_app(m_fid, OP_FPA_LE, arg1, arg2); } app * mk_ge(expr * arg1, expr * arg2) { return m().mk_app(m_fid, OP_FPA_GE, arg1, arg2); } app * mk_is_nan(expr * arg1) { return m().mk_app(m_fid, OP_FPA_IS_NAN, arg1); } app * mk_is_inf(expr * arg1) { return m().mk_app(m_fid, OP_FPA_IS_INF, arg1); } app * mk_is_zero(expr * arg1) { return m().mk_app(m_fid, OP_FPA_IS_ZERO, arg1); } app * mk_is_normal(expr * arg1) { return m().mk_app(m_fid, OP_FPA_IS_NORMAL, arg1); } app * mk_is_subnormal(expr * arg1) { return m().mk_app(m_fid, OP_FPA_IS_SUBNORMAL, arg1); } app * mk_is_positive(expr * arg1) { return m().mk_app(m_fid, OP_FPA_IS_POSITIVE, arg1); } app * mk_is_negative(expr * arg1) { return m().mk_app(m_fid, OP_FPA_IS_NEGATIVE, arg1); } bool is_neg(expr * a) { return is_app_of(a, m_fid, OP_FPA_NEG); } app * mk_to_ieee_bv(expr * arg1) { return m().mk_app(m_fid, OP_FPA_TO_IEEE_BV, arg1); } app * mk_bv2rm(expr * bv3) { SASSERT(m_bv_util.is_bv(bv3) && m_bv_util.get_bv_size(bv3) == 3); return m().mk_app(m_fid, OP_FPA_BV2RM, 0, nullptr, 1, &bv3, mk_rm_sort()); } bool is_bvwrap(expr const * e) const { return is_app_of(e, get_family_id(), OP_FPA_BVWRAP); } bool is_bv2rm(expr const * e) const { return is_app_of(e, get_family_id(), OP_FPA_BV2RM); } bool is_to_ubv(expr const * e) const { return is_app_of(e, get_family_id(), OP_FPA_TO_UBV); } bool is_to_sbv(expr const * e) const { return is_app_of(e, get_family_id(), OP_FPA_TO_SBV); } bool is_bvwrap(func_decl const * f) const { return f->get_family_id() == get_family_id() && f->get_decl_kind() == OP_FPA_BVWRAP; } bool is_bv2rm(func_decl const * f) const { return f->get_family_id() == get_family_id() && f->get_decl_kind() == OP_FPA_BV2RM; } bool is_to_ubv(func_decl const * f) const { return f->get_family_id() == get_family_id() && f->get_decl_kind() == OP_FPA_TO_UBV; } bool is_to_sbv(func_decl const * f) const { return f->get_family_id() == get_family_id() && f->get_decl_kind() == OP_FPA_TO_SBV; } bool is_to_ieee_bv(func_decl const * f) const { return f->get_family_id() == get_family_id() && f->get_decl_kind() == OP_FPA_TO_IEEE_BV; } bool contains_floats(ast * a); bool is_considered_uninterpreted(func_decl* f, unsigned n, expr* const* args); MATCH_TERNARY(is_fp); }; #endif z3-z3-4.8.7/src/ast/func_decl_dependencies.cpp000066400000000000000000000135631356505360400211510ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: func_decl_dependencies.cpp Abstract: Author: Leonardo de Moura (leonardo) 2010-12-15. Revision History: --*/ #include "ast/func_decl_dependencies.h" #include "ast/for_each_expr.h" #include "ast/ast_util.h" struct collect_dependencies_proc { ast_manager & m_manager; func_decl_set & m_set; bool m_ng_only; // collect only declarations in non ground expressions collect_dependencies_proc(ast_manager & m, func_decl_set & s, bool ng_only): m_manager(m), m_set(s), m_ng_only(ng_only) {} void operator()(var * n) {} void operator()(quantifier * n) {} void operator()(app * n) { // We do not need to track dependencies on constants ... if (n->get_num_args()==0) return; if (m_ng_only && is_ground(n)) return; // ... and interpreted function symbols func_decl * d = n->get_decl(); if (d->get_family_id() == null_family_id) { m_set.insert(d); } } }; void collect_func_decls(ast_manager & m, expr * n, func_decl_set & r, bool ng_only) { collect_dependencies_proc proc(m, r, ng_only); for_each_expr(proc, n); } void func_decl_dependencies::reset() { dependency_graph::iterator it = m_deps.begin(); dependency_graph::iterator end = m_deps.end(); for (; it != end; ++it) { func_decl * f = (*it).m_key; func_decl_set * s = (*it).m_value; m_manager.dec_ref(f); dec_ref(m_manager, *s); dealloc(s); } m_deps.reset(); } void func_decl_dependencies::collect_func_decls(expr * n, func_decl_set * s) { ::collect_func_decls(m_manager, n, *s, false); } void func_decl_dependencies::collect_ng_func_decls(expr * n, func_decl_set * s) { ::collect_func_decls(m_manager, n, *s, true); } /** \brief Functor for finding cycles in macro definitions */ class func_decl_dependencies::top_sort { enum color { OPEN, IN_PROGRESS, CLOSED }; dependency_graph & m_deps; typedef obj_map color_map; color_map m_colors; ptr_vector m_todo; func_decl_set * definition(func_decl * f) const { func_decl_set * r = nullptr; m_deps.find(f, r); return r; } color get_color(func_decl * f) const { if (!f) return CLOSED; color_map::iterator it = m_colors.find_iterator(f); if (it != m_colors.end()) return it->m_value; return OPEN; } void set_color(func_decl * f, color c) { m_colors.insert(f, c); } void visit(func_decl * f, bool & visited) { if (get_color(f) != CLOSED) { m_todo.push_back(f); visited = false; } } bool visit_children(func_decl * f) { func_decl_set * def = definition(f); if (!def) return true; bool visited = true; func_decl_set::iterator it = def->begin(); func_decl_set::iterator end = def->end(); for (; it != end; ++it) { visit(*it, visited); } return visited; } bool all_children_closed(func_decl * f) const { func_decl_set * def = definition(f); if (!def) return true; func_decl_set::iterator it = def->begin(); func_decl_set::iterator end = def->end(); for (; it != end; ++it) { if (get_color(*it) != CLOSED) return false; } return true; } /** \brief Return \c true if a cycle is detected. */ bool main_loop(func_decl * f) { if (get_color(f) == CLOSED) return false; m_todo.push_back(f); while (!m_todo.empty()) { func_decl * cf = m_todo.back(); switch (get_color(cf)) { case CLOSED: m_todo.pop_back(); break; case OPEN: set_color(cf, IN_PROGRESS); if (visit_children(cf)) { SASSERT(m_todo.back() == cf); m_todo.pop_back(); set_color(cf, CLOSED); } break; case IN_PROGRESS: if (all_children_closed(cf)) { SASSERT(m_todo.back() == cf); set_color(cf, CLOSED); } else { m_todo.reset(); return true; } break; default: UNREACHABLE(); } } return false; } public: top_sort(dependency_graph & deps) : m_deps(deps) {} bool operator()(func_decl * new_decl) { // [Leo]: It is not trivial to reuse m_colors between different calls since we are update the graph. // To implement this optimization, we need an incremental topological sort algorithm. // The trick of saving the dependencies will save a lot of time. So, I don't think we really // need a incremental top-sort algo. m_colors.reset(); return main_loop(new_decl); } }; bool func_decl_dependencies::insert(func_decl * f, func_decl_set * s) { if (m_deps.contains(f)) { dealloc(s); return false; } m_deps.insert(f, s); top_sort cycle_detector(m_deps); if (cycle_detector(f)) { m_deps.erase(f); dealloc(s); return false; } m_manager.inc_ref(f); inc_ref(m_manager, *s); return true; } void func_decl_dependencies::erase(func_decl * f) { func_decl_set * s = nullptr; if (m_deps.find(f, s)) { m_manager.dec_ref(f); dec_ref(m_manager, *s); m_deps.erase(f); dealloc(s); } } void func_decl_dependencies::display(std::ostream & out) { // TODO } z3-z3-4.8.7/src/ast/func_decl_dependencies.h000066400000000000000000000054751356505360400206210ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: func_decl_dependencies.h Abstract: Author: Leonardo de Moura (leonardo) 2010-12-15. Revision History: --*/ #ifndef FUNC_DECL_DEPENDENCIES_H_ #define FUNC_DECL_DEPENDENCIES_H_ #include "ast/ast.h" #include "util/obj_hashtable.h" // Set of dependencies typedef obj_hashtable func_decl_set; /** \brief Collect uninterpreted function declarations (with arity > 0) occurring in \c n. */ void collect_func_decls(ast_manager & m, expr * n, func_decl_set & r, bool ng_only = false); /** \brief Auxiliary data-structure used for tracking dependencies between function declarations. The following pattern of use is expected: func_decl_dependencies & dm; func_decl_set * S = dm.mk_func_decl_set(); dm.collect_func_decls(t_1, S); ... dm.collect_func_decls(t_n, S); dm.insert(f, S); */ class func_decl_dependencies { typedef obj_map dependency_graph; ast_manager & m_manager; dependency_graph m_deps; class top_sort; public: func_decl_dependencies(ast_manager & m):m_manager(m) {} ~func_decl_dependencies() { reset(); } void reset(); /** \brief Create a dependency set. This set should be populated using #collect_func_decls. After populating the set, it must be used as an argument for the #insert method. \remark The manager owns the set. \warning Failure to call #insert will produce a memory leak. */ func_decl_set * mk_func_decl_set() { return alloc(func_decl_set); } /** \brief Store the uninterpreted function declarations used in \c n into \c s. */ void collect_func_decls(expr * n, func_decl_set * s); /** \brief Store the uninterpreted function declarations (in non ground terms) used in \c n into \c s. */ void collect_ng_func_decls(expr * n, func_decl_set * s); /** \brief Insert \c f in the manager with the given set of dependencies. The insertion succeeds iff 1- no cycle is created between the new entry and the already existing dependencies. 2- \c f was not already inserted into the manager. Return false in case of failure. \remark The manager is the owner of the dependency sets. */ bool insert(func_decl * f, func_decl_set * s); /** \brief Return true if \c f is registered in this manager. */ bool contains(func_decl * f) const { return m_deps.contains(f); } func_decl_set * get_dependencies(func_decl * f) const { func_decl_set * r = nullptr; m_deps.find(f, r); return r; } /** \brief Erase \c f (and its dependencies) from the manager. */ void erase(func_decl * f); void display(std::ostream & out); }; #endif z3-z3-4.8.7/src/ast/has_free_vars.cpp000066400000000000000000000046021356505360400173220ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: has_free_vars.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-06-23. Revision History: --*/ #include "ast/ast.h" #include "ast/expr_delta_pair.h" #include "util/hashtable.h" class contains_vars { typedef hashtable, default_eq > cache; cache m_cache; svector m_todo; bool m_contains; unsigned m_window; void visit(expr * n, unsigned delta, bool & visited) { expr_delta_pair e(n, delta); if (!m_cache.contains(e)) { m_todo.push_back(e); visited = false; } } bool visit_children(expr * n, unsigned delta) { bool visited = true; unsigned dw; unsigned j; switch (n->get_kind()) { case AST_VAR: dw = m_window <= UINT_MAX - delta ? m_window + delta : UINT_MAX; if (to_var(n)->get_idx() >= delta && to_var(n)->get_idx() <= dw) m_contains = true; break; case AST_APP: j = to_app(n)->get_num_args(); while (j > 0) { --j; visit(to_app(n)->get_arg(j), delta, visited); } break; case AST_QUANTIFIER: if (delta <= UINT_MAX - to_quantifier(n)->get_num_decls()) { visit(to_quantifier(n)->get_expr(), delta + to_quantifier(n)->get_num_decls(), visited); } break; default: break; } return visited; } public: // return true if n contains a variable in the range [begin, end] bool operator()(expr * n, unsigned begin = 0, unsigned end = UINT_MAX) { m_contains = false; m_window = end - begin; m_todo.reset(); m_cache.reset(); m_todo.push_back(expr_delta_pair(n, begin)); while (!m_todo.empty()) { expr_delta_pair e = m_todo.back(); if (visit_children(e.m_node, e.m_delta)) { m_cache.insert(e); m_todo.pop_back(); } if (m_contains) { return true; } } SASSERT(!m_contains); return false; } }; bool has_free_vars(expr * n) { contains_vars p; return p(n); } z3-z3-4.8.7/src/ast/has_free_vars.h000066400000000000000000000004721356505360400167700ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: has_free_vars.h Abstract: Author: Leonardo de Moura (leonardo) 2008-06-23. Revision History: --*/ #ifndef HAS_FREE_VARS_H_ #define HAS_FREE_VARS_H_ class expr; bool has_free_vars(expr * n); #endif /* HAS_FREE_VARS_H_ */ z3-z3-4.8.7/src/ast/justified_expr.h000066400000000000000000000022511356505360400172020ustar00rootroot00000000000000 #ifndef JUSTIFIED_EXPR_H_ #define JUSTIFIED_EXPR_H_ #include "ast/ast.h" class justified_expr { ast_manager& m; expr* m_fml; proof* m_proof; public: justified_expr(ast_manager& m, expr* fml, proof* p): m(m), m_fml(fml), m_proof(p) { SASSERT(fml); m.inc_ref(fml); m.inc_ref(p); } justified_expr& operator=(justified_expr const& other) { SASSERT(&m == &other.m); if (this != &other) { m.inc_ref(other.get_fml()); m.inc_ref(other.get_proof()); m.dec_ref(m_fml); m.dec_ref(m_proof); m_fml = other.get_fml(); m_proof = other.get_proof(); } return *this; } justified_expr(justified_expr const& other): m(other.m), m_fml(other.m_fml), m_proof(other.m_proof) { m.inc_ref(m_fml); m.inc_ref(m_proof); } ~justified_expr() { m.dec_ref(m_fml); m.dec_ref(m_proof); m_fml = nullptr; m_proof = nullptr; } expr* get_fml() const { return m_fml; } proof* get_proof() const { return m_proof; } }; #endif z3-z3-4.8.7/src/ast/macro_substitution.cpp000066400000000000000000000123351356505360400204520ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: macro_substitution.cpp Abstract: Mapping from func_decl to quantifiers of the form Forall X. f(X) = T[X] Forall X. f(X) iff C[X] Author: Leonardo (leonardo) 2012-02-17 Notes: --*/ #include "ast/macro_substitution.h" #include "util/ref_util.h" typedef obj_map func_decl2proof; typedef obj_map func_decl2expr_dependency; void macro_substitution::init() { if (proofs_enabled()) m_decl2macro_pr = alloc(func_decl2proof); if (unsat_core_enabled()) m_decl2macro_dep = alloc(func_decl2expr_dependency); } macro_substitution::macro_substitution(ast_manager & m): m_manager(m), m_cores_enabled(false), m_proofs_enabled(m.proofs_enabled()) { init(); } macro_substitution::macro_substitution(ast_manager & m, bool cores_enabled): m_manager(m), m_cores_enabled(cores_enabled), m_proofs_enabled(m.proofs_enabled()) { init(); } macro_substitution::macro_substitution(ast_manager & m, bool cores_enabled, bool proofs_enabled): m_manager(m), m_cores_enabled(cores_enabled), m_proofs_enabled(proofs_enabled) { SASSERT(!proofs_enabled || m.proofs_enabled()); init(); } macro_substitution::~macro_substitution() { reset(); } void macro_substitution::reset() { dec_ref_map_key_values(m_manager, m_decl2macro); if (proofs_enabled()) dec_ref_map_values(m_manager, *m_decl2macro_pr); if (unsat_core_enabled()) dec_ref_map_values(m_manager, *m_decl2macro_dep); } void macro_substitution::cleanup() { reset(); m_decl2macro.finalize(); if (proofs_enabled()) m_decl2macro_pr->finalize(); if (unsat_core_enabled()) m_decl2macro_dep->finalize(); } void macro_substitution::insert(func_decl * f, quantifier * q, proof * pr, expr_dependency * dep) { DEBUG_CODE({ app * body = to_app(q->get_expr()); SASSERT(m_manager.is_eq(body)); expr * lhs = body->get_arg(0); expr * rhs = body->get_arg(1); SASSERT(is_app_of(lhs, f) || is_app_of(rhs, f)); }); obj_map::obj_map_entry * entry = m_decl2macro.insert_if_not_there2(f, 0); if (entry->get_data().m_value == 0) { // new entry m_manager.inc_ref(f); m_manager.inc_ref(q); entry->get_data().m_value = q; if (proofs_enabled()) { SASSERT(!m_decl2macro_pr->contains(f)); m_decl2macro_pr->insert(f, pr); m_manager.inc_ref(pr); } if (unsat_core_enabled()) { SASSERT(!m_decl2macro_dep->contains(f)); m_decl2macro_dep->insert(f, dep); m_manager.inc_ref(dep); } } else { // replacing entry m_manager.inc_ref(q); m_manager.dec_ref(entry->get_data().m_value); entry->get_data().m_value = q; if (proofs_enabled()) { obj_map::obj_map_entry * entry_pr = m_decl2macro_pr->find_core(f); SASSERT(entry_pr != 0); m_manager.inc_ref(pr); m_manager.dec_ref(entry_pr->get_data().m_value); entry_pr->get_data().m_value = pr; } if (unsat_core_enabled()) { obj_map::obj_map_entry * entry_dep = m_decl2macro_dep->find_core(f); SASSERT(entry_dep != 0); m_manager.inc_ref(dep); m_manager.dec_ref(entry_dep->get_data().m_value); entry_dep->get_data().m_value = dep; } } } void macro_substitution::erase(func_decl * f) { if (proofs_enabled()) { proof * pr = nullptr; if (m_decl2macro_pr->find(f, pr)) { m_manager.dec_ref(pr); m_decl2macro_pr->erase(f); } } if (unsat_core_enabled()) { expr_dependency * dep = nullptr; if (m_decl2macro_dep->find(f, dep)) { m_manager.dec_ref(dep); m_decl2macro_dep->erase(f); } } quantifier * q = nullptr; if (m_decl2macro.find(f, q)) { m_manager.dec_ref(f); m_manager.dec_ref(q); m_decl2macro.erase(f); } } void macro_substitution::get_head_def(quantifier * q, func_decl * f, app * & head, expr * & def) { app * body = to_app(q->get_expr()); SASSERT(m_manager.is_eq(body)); expr * lhs = to_app(body)->get_arg(0); expr * rhs = to_app(body)->get_arg(1); SASSERT(is_app_of(lhs, f) || is_app_of(rhs, f)); SASSERT(!is_app_of(lhs, f) || !is_app_of(rhs, f)); if (is_app_of(lhs, f)) { head = to_app(lhs); def = rhs; } else { head = to_app(rhs); def = lhs; } } bool macro_substitution::find(func_decl * f, quantifier * & q, proof * & pr) { if (m_decl2macro.find(f, q)) { if (proofs_enabled()) m_decl2macro_pr->find(f, pr); return true; } return false; } bool macro_substitution::find(func_decl * f, quantifier * & q, proof * & pr, expr_dependency * & dep) { if (m_decl2macro.find(f, q)) { if (proofs_enabled()) m_decl2macro_pr->find(f, pr); if (unsat_core_enabled()) m_decl2macro_dep->find(f, dep); return true; } return false; } z3-z3-4.8.7/src/ast/macro_substitution.h000066400000000000000000000033551356505360400201210ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: macro_substitution.h Abstract: Mapping from func_decl to quantifiers of the form Forall X. f(X) = T[X] Forall X. f(X) iff C[X] Author: Leonardo (leonardo) 2012-02-17 Notes: --*/ #ifndef MACRO_SUBSTITUTION_H_ #define MACRO_SUBSTITUTION_H_ #include "ast/ast.h" class macro_substitution { ast_manager & m_manager; obj_map m_decl2macro; scoped_ptr > m_decl2macro_pr; scoped_ptr > m_decl2macro_dep; unsigned m_cores_enabled:1; unsigned m_proofs_enabled:1; void init(); public: macro_substitution(ast_manager & m); macro_substitution(ast_manager & m, bool cores_enabled); macro_substitution(ast_manager & m, bool cores_enabled, bool proofs_enabled); ~macro_substitution(); ast_manager & m() const { return m_manager; } bool proofs_enabled() const { return m_proofs_enabled; } bool unsat_core_enabled() const { return m_cores_enabled; } bool empty() const { return m_decl2macro.empty(); } void insert(func_decl * f, quantifier * m, proof * pr, expr_dependency * dep = nullptr); void erase(func_decl * f); bool contains(func_decl * f) { return m_decl2macro.contains(f); } bool find(func_decl * f, quantifier * & q, proof * & pr); bool find(func_decl * f, quantifier * & q, proof * & pr, expr_dependency * & dep); void get_head_def(quantifier * q, func_decl * f, app * & head, expr * & def); void reset(); void cleanup(); }; #endif z3-z3-4.8.7/src/ast/macros/000077500000000000000000000000001356505360400152715ustar00rootroot00000000000000z3-z3-4.8.7/src/ast/macros/CMakeLists.txt000066400000000000000000000002351356505360400200310ustar00rootroot00000000000000z3_add_component(macros SOURCES macro_finder.cpp macro_manager.cpp macro_util.cpp quasi_macros.cpp COMPONENT_DEPENDENCIES rewriter ) z3-z3-4.8.7/src/ast/macros/macro_finder.cpp000066400000000000000000000350221356505360400204270ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: macro_finder.cpp Abstract: Author: Leonardo de Moura (leonardo) 2010-04-05. Revision History: --*/ #include "ast/macros/macro_finder.h" #include "ast/occurs.h" #include "ast/ast_pp.h" #include "ast/ast_ll_pp.h" bool macro_finder::is_macro(expr * n, app_ref & head, expr_ref & def) { if (!is_forall(n)) return false; TRACE("macro_finder", tout << "processing: " << mk_pp(n, m) << "\n";); expr * body = to_quantifier(n)->get_expr(); unsigned num_decls = to_quantifier(n)->get_num_decls(); return m_util.is_simple_macro(body, num_decls, head, def); } /** \brief Detect macros of the form 1- (forall (X) (= (+ (f X) (R X)) c)) 2- (forall (X) (<= (+ (f X) (R X)) c)) 3- (forall (X) (>= (+ (f X) (R X)) c)) The second and third cases are first converted into (forall (X) (= (f X) (+ c (* -1 (R x)) (k X)))) and (forall (X) (<= (k X) 0)) when case 2 (forall (X) (>= (k X) 0)) when case 3 For case 2 & 3, the new quantifiers are stored in new_exprs and new_prs. */ bool macro_finder::is_arith_macro(expr * n, proof * pr, expr_dependency * dep, expr_ref_vector & new_exprs, proof_ref_vector & new_prs, expr_dependency_ref_vector & new_deps) { if (!is_forall(n)) return false; expr * body = to_quantifier(n)->get_expr(); unsigned num_decls = to_quantifier(n)->get_num_decls(); if (!m_autil.is_le(body) && !m_autil.is_ge(body) && !m.is_eq(body)) return false; if (!m_autil.is_add(to_app(body)->get_arg(0))) return false; app_ref head(m); expr_ref def(m); bool inv = false; if (!m_util.is_arith_macro(body, num_decls, head, def, inv)) return false; app_ref new_body(m); if (!inv || m.is_eq(body)) new_body = m.mk_app(to_app(body)->get_decl(), head, def); else if (m_autil.is_le(body)) new_body = m_autil.mk_ge(head, def); else new_body = m_autil.mk_le(head, def); quantifier_ref new_q(m); new_q = m.update_quantifier(to_quantifier(n), new_body); proof * new_pr = nullptr; if (m.proofs_enabled()) { proof * rw = m.mk_rewrite(n, new_q); new_pr = m.mk_modus_ponens(pr, rw); } expr_dependency * new_dep = dep; if (m.is_eq(body)) { return m_macro_manager.insert(head->get_decl(), new_q, new_pr, new_dep); } // is ge or le // TRACE("macro_finder", tout << "is_arith_macro: is_ge or is_le\n";); func_decl * f = head->get_decl(); func_decl * k = m.mk_fresh_func_decl(f->get_name(), symbol::null, f->get_arity(), f->get_domain(), f->get_range()); app * k_app = m.mk_app(k, head->get_num_args(), head->get_args()); expr_ref_buffer new_rhs_args(m); expr_ref new_rhs2(m_autil.mk_add(def, k_app), m); expr * body1 = m.mk_eq(head, new_rhs2); expr * body2 = m.mk_app(new_body->get_decl(), k_app, m_autil.mk_int(0)); quantifier * q1 = m.update_quantifier(new_q, body1); expr * patterns[1] = { m.mk_pattern(k_app) }; quantifier * q2 = m.update_quantifier(new_q, 1, patterns, body2); new_exprs.push_back(q1); new_exprs.push_back(q2); if (m.proofs_enabled()) { // new_pr : new_q // rw : [rewrite] new_q ~ q1 & q2 // mp : [modus_pones new_pr rw] q1 & q2 // pr1 : [and-elim mp] q1 // pr2 : [and-elim mp] q2 app * q1q2 = m.mk_and(q1,q2); proof * rw = m.mk_oeq_rewrite(new_q, q1q2); proof * mp = m.mk_modus_ponens(new_pr, rw); proof * pr1 = m.mk_and_elim(mp, 0); proof * pr2 = m.mk_and_elim(mp, 1); new_prs.push_back(pr1); new_prs.push_back(pr2); } if (dep) { new_deps.push_back(new_dep); new_deps.push_back(new_dep); } return true; } bool macro_finder::is_arith_macro(expr * n, proof * pr, vector& new_fmls) { if (!is_forall(n)) return false; expr * body = to_quantifier(n)->get_expr(); unsigned num_decls = to_quantifier(n)->get_num_decls(); if (!m_autil.is_le(body) && !m_autil.is_ge(body) && !m.is_eq(body)) return false; if (!m_autil.is_add(to_app(body)->get_arg(0))) return false; app_ref head(m); expr_ref def(m); bool inv = false; if (!m_util.is_arith_macro(body, num_decls, head, def, inv)) return false; app_ref new_body(m); if (!inv || m.is_eq(body)) new_body = m.mk_app(to_app(body)->get_decl(), head, def); else if (m_autil.is_le(body)) new_body = m_autil.mk_ge(head, def); else new_body = m_autil.mk_le(head, def); quantifier_ref new_q(m); new_q = m.update_quantifier(to_quantifier(n), new_body); proof * new_pr = nullptr; if (m.proofs_enabled()) { proof * rw = m.mk_rewrite(n, new_q); new_pr = m.mk_modus_ponens(pr, rw); } if (m.is_eq(body)) { return m_macro_manager.insert(head->get_decl(), new_q, new_pr); } // is ge or le // TRACE("macro_finder", tout << "is_arith_macro: is_ge or is_le\n";); func_decl * f = head->get_decl(); func_decl * k = m.mk_fresh_func_decl(f->get_name(), symbol::null, f->get_arity(), f->get_domain(), f->get_range()); app * k_app = m.mk_app(k, head->get_num_args(), head->get_args()); expr_ref_buffer new_rhs_args(m); expr_ref new_rhs2(m_autil.mk_add(def, k_app), m); expr * body1 = m.mk_eq(head, new_rhs2); expr * body2 = m.mk_app(new_body->get_decl(), k_app, m_autil.mk_int(0)); quantifier * q1 = m.update_quantifier(new_q, body1); expr * patterns[1] = { m.mk_pattern(k_app) }; quantifier * q2 = m.update_quantifier(new_q, 1, patterns, body2); proof* pr1 = nullptr, *pr2 = nullptr; if (m.proofs_enabled()) { // new_pr : new_q // rw : [rewrite] new_q ~ q1 & q2 // mp : [modus_pones new_pr rw] q1 & q2 // pr1 : [and-elim mp] q1 // pr2 : [and-elim mp] q2 app * q1q2 = m.mk_and(q1,q2); proof * rw = m.mk_oeq_rewrite(new_q, q1q2); proof * mp = m.mk_modus_ponens(new_pr, rw); pr1 = m.mk_and_elim(mp, 0); pr2 = m.mk_and_elim(mp, 1); } new_fmls.push_back(justified_expr(m, q1, pr1)); new_fmls.push_back(justified_expr(m, q2, pr2)); return true; } /** n is of the form: (forall (X) (iff (= (f X) t) def[X])) Convert it into: (forall (X) (= (f X) (ite def[X] t (k X)))) (forall (X) (not (= (k X) t))) where k is a fresh symbol. The new quantifiers and proofs are stored in new_exprs and new_prs */ static void pseudo_predicate_macro2macro(ast_manager & m, app * head, app * t, expr * def, quantifier * q, proof * pr, expr_dependency * dep, expr_ref_vector & new_exprs, proof_ref_vector & new_prs, expr_dependency_ref_vector & new_deps ) { func_decl * f = head->get_decl(); func_decl * k = m.mk_fresh_func_decl(f->get_name(), symbol::null, f->get_arity(), f->get_domain(), f->get_range()); app * k_app = m.mk_app(k, head->get_num_args(), head->get_args()); app * ite = m.mk_ite(def, t, k_app); app * body_1 = m.mk_eq(head, ite); app * body_2 = m.mk_not(m.mk_eq(k_app, t)); quantifier * q1 = m.update_quantifier(q, body_1); expr * pats[1] = { m.mk_pattern(k_app) }; quantifier * q2 = m.update_quantifier(q, 1, pats, body_2); // erase patterns new_exprs.push_back(q1); new_exprs.push_back(q2); if (m.proofs_enabled()) { // r : [rewrite] q ~ q1 & q2 // pr : q // mp : [modus_pones pr pr1] q1 & q2 // pr1 : [and-elim mp] q1 // pr2 : [and-elim mp] q2 app * q1q2 = m.mk_and(q1,q2); proof * r = m.mk_oeq_rewrite(q, q1q2); proof * mp = m.mk_modus_ponens(pr, r); proof * pr1 = m.mk_and_elim(mp, 0); proof * pr2 = m.mk_and_elim(mp, 1); new_prs.push_back(pr1); new_prs.push_back(pr2); } new_deps.push_back(dep); new_deps.push_back(dep); } static void pseudo_predicate_macro2macro(ast_manager & m, app * head, app * t, expr * def, quantifier * q, proof * pr, vector& new_fmls) { func_decl * f = head->get_decl(); func_decl * k = m.mk_fresh_func_decl(f->get_name(), symbol::null, f->get_arity(), f->get_domain(), f->get_range()); app * k_app = m.mk_app(k, head->get_num_args(), head->get_args()); app * ite = m.mk_ite(def, t, k_app); app * body_1 = m.mk_eq(head, ite); app * body_2 = m.mk_not(m.mk_eq(k_app, t)); quantifier * q1 = m.update_quantifier(q, body_1); proof * pr1 = nullptr, *pr2 = nullptr; expr * pats[1] = { m.mk_pattern(k_app) }; quantifier * q2 = m.update_quantifier(q, 1, pats, body_2); // erase patterns if (m.proofs_enabled()) { // r : [rewrite] q ~ q1 & q2 // pr : q // mp : [modus_pones pr pr1] q1 & q2 // pr1 : [and-elim mp] q1 // pr2 : [and-elim mp] q2 app * q1q2 = m.mk_and(q1,q2); proof * r = m.mk_oeq_rewrite(q, q1q2); proof * mp = m.mk_modus_ponens(pr, r); pr1 = m.mk_and_elim(mp, 0); pr2 = m.mk_and_elim(mp, 1); } new_fmls.push_back(justified_expr(m, q1, pr1)); new_fmls.push_back(justified_expr(m, q2, pr2)); } macro_finder::macro_finder(ast_manager & m, macro_manager & mm): m(m), m_macro_manager(mm), m_util(mm.get_util()), m_autil(m) { } macro_finder::~macro_finder() { } bool macro_finder::expand_macros(unsigned num, expr * const * exprs, proof * const * prs, expr_dependency * const * deps, expr_ref_vector & new_exprs, proof_ref_vector & new_prs, expr_dependency_ref_vector & new_deps) { TRACE("macro_finder", tout << "starting expand_macros:\n"; m_macro_manager.display(tout);); bool found_new_macro = false; for (unsigned i = 0; i < num; i++) { expr * n = exprs[i]; proof * pr = m.proofs_enabled() ? prs[i] : nullptr; expr_dependency * depi = deps != nullptr ? deps[i] : nullptr; expr_ref new_n(m), def(m); proof_ref new_pr(m); expr_dependency_ref new_dep(m); m_macro_manager.expand_macros(n, pr, depi, new_n, new_pr, new_dep); app_ref head(m), t(m); if (is_macro(new_n, head, def) && m_macro_manager.insert(head->get_decl(), to_quantifier(new_n.get()), new_pr, new_dep)) { TRACE("macro_finder_found", tout << "found new macro: " << head->get_decl()->get_name() << "\n" << new_n << "\n";); found_new_macro = true; } else if (is_arith_macro(new_n, new_pr, new_dep, new_exprs, new_prs, new_deps)) { TRACE("macro_finder_found", tout << "found new arith macro:\n" << new_n << "\n";); found_new_macro = true; } else if (m_util.is_pseudo_predicate_macro(new_n, head, t, def)) { TRACE("macro_finder_found", tout << "found new pseudo macro:\n" << head << "\n" << t << "\n" << def << "\n";); pseudo_predicate_macro2macro(m, head, t, def, to_quantifier(new_n), new_pr, new_dep, new_exprs, new_prs, new_deps); found_new_macro = true; } else { new_exprs.push_back(new_n); if (m.proofs_enabled()) new_prs.push_back(new_pr); if (deps != nullptr) new_deps.push_back(new_dep); } } return found_new_macro; } void macro_finder::operator()(unsigned num, expr * const * exprs, proof * const * prs, expr_dependency * const * deps, expr_ref_vector & new_exprs, proof_ref_vector & new_prs, expr_dependency_ref_vector & new_deps) { TRACE("macro_finder", tout << "processing macros...\n";); expr_ref_vector _new_exprs(m); proof_ref_vector _new_prs(m); expr_dependency_ref_vector _new_deps(m); if (expand_macros(num, exprs, prs, deps, _new_exprs, _new_prs, _new_deps)) { while (true) { expr_ref_vector old_exprs(m); proof_ref_vector old_prs(m); expr_dependency_ref_vector old_deps(m); _new_exprs.swap(old_exprs); _new_prs.swap(old_prs); _new_deps.swap(old_deps); SASSERT(_new_exprs.empty()); SASSERT(_new_prs.empty()); SASSERT(_new_deps.empty()); if (!expand_macros(old_exprs.size(), old_exprs.c_ptr(), old_prs.c_ptr(), old_deps.c_ptr(), _new_exprs, _new_prs, _new_deps)) break; } } new_exprs.append(_new_exprs); new_prs.append(_new_prs); new_deps.append(_new_deps); } bool macro_finder::expand_macros(unsigned num, justified_expr const * fmls, vector& new_fmls) { TRACE("macro_finder", tout << "starting expand_macros:\n"; m_macro_manager.display(tout);); bool found_new_macro = false; for (unsigned i = 0; i < num; i++) { expr * n = fmls[i].get_fml(); proof * pr = m.proofs_enabled() ? fmls[i].get_proof() : nullptr; expr_ref new_n(m), def(m); proof_ref new_pr(m); expr_dependency_ref new_dep(m); m_macro_manager.expand_macros(n, pr, nullptr, new_n, new_pr, new_dep); app_ref head(m), t(m); if (is_macro(new_n, head, def) && m_macro_manager.insert(head->get_decl(), to_quantifier(new_n.get()), new_pr)) { TRACE("macro_finder_found", tout << "found new macro: " << head->get_decl()->get_name() << "\n" << new_n << "\n";); found_new_macro = true; } else if (is_arith_macro(new_n, new_pr, new_fmls)) { TRACE("macro_finder_found", tout << "found new arith macro:\n" << new_n << "\n";); found_new_macro = true; } else if (m_util.is_pseudo_predicate_macro(new_n, head, t, def)) { TRACE("macro_finder_found", tout << "found new pseudo macro:\n" << head << "\n" << t << "\n" << def << "\n";); pseudo_predicate_macro2macro(m, head, t, def, to_quantifier(new_n), new_pr, new_fmls); found_new_macro = true; } else { new_fmls.push_back(justified_expr(m, new_n, new_pr)); } } return found_new_macro; } void macro_finder::operator()(unsigned n, justified_expr const* fmls, vector& new_fmls) { TRACE("macro_finder", tout << "processing macros...\n";); vector _new_fmls; if (expand_macros(n, fmls, _new_fmls)) { while (true) { vector old_fmls; _new_fmls.swap(old_fmls); SASSERT(_new_fmls.empty()); if (!expand_macros(old_fmls.size(), old_fmls.c_ptr(), _new_fmls)) break; } } new_fmls.append(_new_fmls); } z3-z3-4.8.7/src/ast/macros/macro_finder.h000066400000000000000000000033221356505360400200720ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: macro_finder.h Abstract: Author: Leonardo de Moura (leonardo) 2010-04-05. Revision History: --*/ #ifndef MACRO_FINDER_H_ #define MACRO_FINDER_H_ #include "ast/macros/macro_manager.h" /** \brief Macro finder is responsible for finding universally quantified sub-formulas that can be used as macros. */ class macro_finder { ast_manager & m; macro_manager & m_macro_manager; macro_util & m_util; arith_util m_autil; bool expand_macros(unsigned num, expr * const * exprs, proof * const * prs, expr_dependency * const* deps, expr_ref_vector & new_exprs, proof_ref_vector & new_prs, expr_dependency_ref_vector& new_deps); bool expand_macros(unsigned n, justified_expr const * fmls, vector& new_fmls); bool is_arith_macro(expr * n, proof * pr, expr_ref_vector & new_exprs, proof_ref_vector & new_prs); bool is_arith_macro(expr * n, proof * pr, vector& new_fmls); bool is_arith_macro(expr * n, proof * pr, expr_dependency * dep, expr_ref_vector & new_exprs, proof_ref_vector & new_prs, expr_dependency_ref_vector & new_deps); bool is_macro(expr * n, app_ref & head, expr_ref & def); public: macro_finder(ast_manager & m, macro_manager & mm); ~macro_finder(); void operator()(unsigned n, expr * const * exprs, proof * const * prs, expr_dependency * const* deps, expr_ref_vector & new_exprs, proof_ref_vector & new_prs, expr_dependency_ref_vector & new_deps); void operator()(unsigned n, justified_expr const* fmls, vector& new_fmls); }; #endif /* MACRO_FINDER_H_ */ z3-z3-4.8.7/src/ast/macros/macro_manager.cpp000066400000000000000000000270461356505360400206010ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: macro_manager.cpp Abstract: Author: Leonardo de Moura (leonardo) 2010-04-05. Revision History: Christoph Wintersteiger (t-cwinte), 2010-04-13: Added cycle detection for macro definitions Leonardo de Moura (leonardo) 2010-12-15: Moved dependency management to func_decl_dependencies.h --*/ #include "ast/macros/macro_manager.h" #include "ast/for_each_expr.h" #include "ast/rewriter/var_subst.h" #include "ast/rewriter/th_rewriter.h" #include "ast/rewriter/rewriter_def.h" #include "ast/ast_pp.h" #include "ast/recurse_expr_def.h" macro_manager::macro_manager(ast_manager & m): m(m), m_util(m), m_decls(m), m_macros(m), m_macro_prs(m), m_macro_deps(m), m_forbidden(m), m_deps(m) { m_util.set_forbidden_set(&m_forbidden_set); } macro_manager::~macro_manager() { } void macro_manager::push_scope() { m_scopes.push_back(scope()); scope & s = m_scopes.back(); s.m_decls_lim = m_decls.size(); s.m_forbidden_lim = m_forbidden.size(); } void macro_manager::pop_scope(unsigned num_scopes) { unsigned new_lvl = m_scopes.size() - num_scopes; scope & s = m_scopes[new_lvl]; restore_decls(s.m_decls_lim); restore_forbidden(s.m_forbidden_lim); m_scopes.shrink(new_lvl); } void macro_manager::restore_decls(unsigned old_sz) { unsigned sz = m_decls.size(); for (unsigned i = old_sz; i < sz; i++) { m_decl2macro.erase(m_decls.get(i)); m_deps.erase(m_decls.get(i)); if (m.proofs_enabled()) m_decl2macro_pr.erase(m_decls.get(i)); m_decl2macro_dep.erase(m_decls.get(i)); } m_decls.shrink(old_sz); m_macros.shrink(old_sz); if (m.proofs_enabled()) m_macro_prs.shrink(old_sz); m_macro_deps.shrink(old_sz); } void macro_manager::restore_forbidden(unsigned old_sz) { unsigned sz = m_forbidden.size(); for (unsigned i = old_sz; i < sz; i++) m_forbidden_set.erase(m_forbidden.get(i)); m_forbidden.shrink(old_sz); } void macro_manager::reset() { m_decl2macro.reset(); m_decl2macro_pr.reset(); m_decl2macro_dep.reset(); m_decls.reset(); m_macros.reset(); m_macro_prs.reset(); m_macro_deps.reset(); m_scopes.reset(); m_forbidden_set.reset(); m_forbidden.reset(); m_deps.reset(); } bool macro_manager::insert(func_decl * f, quantifier * q, proof * pr, expr_dependency* dep) { TRACE("macro_insert", tout << "trying to create macro: " << f->get_name() << "\n" << mk_pp(q, m) << "\n";); // if we already have a macro for f then return false; if (m_decls.contains(f)) { TRACE("macro_insert", tout << "we already have a macro for: " << f->get_name() << "\n";); return false; } app * head; expr * definition; get_head_def(q, f, head, definition); func_decl_set * s = m_deps.mk_func_decl_set(); m_deps.collect_func_decls(definition, s); if (!m_deps.insert(f, s)) { return false; } // add macro m_decl2macro.insert(f, q); m_decls.push_back(f); m_macros.push_back(q); if (m.proofs_enabled()) { m_macro_prs.push_back(pr); m_decl2macro_pr.insert(f, pr); } m_macro_deps.push_back(dep); m_decl2macro_dep.insert(f, dep); TRACE("macro_insert", tout << "A macro was successfully created for: " << f->get_name() << "\n";); // Nothing's forbidden anymore; if something's bad, we detected it earlier. // mark_forbidden(m->get_expr()); return true; } namespace macro_manager_ns { struct proc { obj_hashtable & m_forbidden_set; func_decl_ref_vector & m_forbidden; proc(obj_hashtable & s, func_decl_ref_vector & v):m_forbidden_set(s), m_forbidden(v) {} void operator()(var * n) {} void operator()(quantifier * n) {} void operator()(app * n) { func_decl * d = n->get_decl(); if (n->get_num_args() > 0 && n->get_family_id() == null_family_id && !m_forbidden_set.contains(d)) { m_forbidden_set.insert(d); m_forbidden.push_back(d); } } }; }; /** \brief Mark all func_decls used in exprs as forbidden. */ void macro_manager::mark_forbidden(unsigned n, expr * const * exprs) { expr_mark visited; macro_manager_ns::proc p(m_forbidden_set, m_forbidden); for (unsigned i = 0; i < n; i++) for_each_expr(p, visited, exprs[i]); } void macro_manager::mark_forbidden(unsigned n, justified_expr const * exprs) { expr_mark visited; macro_manager_ns::proc p(m_forbidden_set, m_forbidden); for (unsigned i = 0; i < n; i++) for_each_expr(p, visited, exprs[i].get_fml()); } void macro_manager::get_head_def(quantifier * q, func_decl * d, app * & head, expr * & def) const { app * body = to_app(q->get_expr()); expr * lhs = nullptr, *rhs = nullptr; VERIFY(m.is_eq(body, lhs, rhs)); SASSERT(is_app_of(lhs, d) || is_app_of(rhs, d)); SASSERT(!is_app_of(lhs, d) || !is_app_of(rhs, d)); if (is_app_of(lhs, d)) { head = to_app(lhs); def = rhs; } else { head = to_app(rhs); def = lhs; } } void macro_manager::display(std::ostream & out) { unsigned sz = m_decls.size(); for (unsigned i = 0; i < sz; i++) { func_decl * f = m_decls.get(i); quantifier * q = nullptr; m_decl2macro.find(f, q); app * head; expr * def; get_head_def(q, f, head, def); SASSERT(q); out << mk_pp(head, m) << " ->\n" << mk_pp(def, m) << "\n"; } } func_decl * macro_manager::get_macro_interpretation(unsigned i, expr_ref & interp) const { func_decl * f = m_decls.get(i); quantifier * q = m_macros.get(i); app * head; expr * def; get_head_def(q, f, head, def); TRACE("macro_bug", tout << f->get_name() << "\n" << mk_pp(head, m) << "\n" << mk_pp(q, m) << "\n";); m_util.mk_macro_interpretation(head, q->get_num_decls(), def, interp); return f; } struct macro_manager::macro_expander_cfg : public default_rewriter_cfg { ast_manager& m; macro_manager& mm; expr_dependency_ref m_used_macro_dependencies; expr_ref_vector m_trail; macro_expander_cfg(ast_manager& m, macro_manager& mm): m(m), mm(mm), m_used_macro_dependencies(m), m_trail(m) {} bool rewrite_patterns() const { return false; } bool flat_assoc(func_decl * f) 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; return BR_FAILED; } 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) { // If a macro was expanded in a pattern, we must erase it since it may not be a valid pattern anymore. // The MAM assumes valid patterns, and it crashes if invalid patterns are provided. // For example, it will crash if the pattern does not contain all variables. // // Alternative solution: use pattern_validation to check if the pattern is still valid. // I'm not sure if this is a good solution, since the pattern may be meaningless after the macro expansion. // So, I'm just erasing them. bool erase_patterns = false; for (unsigned i = 0; !erase_patterns && i < old_q->get_num_patterns(); i++) { if (old_q->get_pattern(i) != new_patterns[i]) erase_patterns = true; } for (unsigned i = 0; !erase_patterns && i < old_q->get_num_no_patterns(); i++) { if (old_q->get_no_pattern(i) != new_no_patterns[i]) erase_patterns = true; } if (erase_patterns) { result = m.update_quantifier(old_q, 0, nullptr, 0, nullptr, new_body); } return erase_patterns; } bool get_subst(expr * _n, expr* & r, proof* & p) { if (!is_app(_n)) return false; app * n = to_app(_n); quantifier * q = nullptr; func_decl * d = n->get_decl(); TRACE("macro_manager", tout << "trying to expand:\n" << mk_pp(n, m) << "\nd:\n" << d->get_name() << "\n";); if (mm.m_decl2macro.find(d, q)) { TRACE("macro_manager", tout << "expanding: " << mk_pp(n, m) << "\n";); app * head = nullptr; expr * def = nullptr; mm.get_head_def(q, d, head, def); unsigned num = n->get_num_args(); SASSERT(head && def); ptr_buffer subst_args; subst_args.resize(num, 0); for (unsigned i = 0; i < num; i++) { var * v = to_var(head->get_arg(i)); SASSERT(v->get_idx() < num); unsigned nidx = num - v->get_idx() - 1; SASSERT(subst_args[nidx] == 0); subst_args[nidx] = n->get_arg(i); } var_subst s(m); expr_ref rr = s(def, num, subst_args.c_ptr()); m_trail.push_back(rr); r = rr; if (m.proofs_enabled()) { expr_ref instance = s(q->get_expr(), num, subst_args.c_ptr()); proof * qi_pr = m.mk_quant_inst(m.mk_or(m.mk_not(q), instance), num, subst_args.c_ptr()); proof * q_pr = nullptr; mm.m_decl2macro_pr.find(d, q_pr); SASSERT(q_pr != 0); proof * prs[2] = { qi_pr, q_pr }; p = m.mk_unit_resolution(2, prs); } else { p = nullptr; } expr_dependency * ed = mm.m_decl2macro_dep.find(d); m_used_macro_dependencies = m.mk_join(m_used_macro_dependencies, ed); return true; } return false; } }; struct macro_manager::macro_expander_rw : public rewriter_tpl { macro_expander_cfg m_cfg; macro_expander_rw(ast_manager& m, macro_manager& mm): rewriter_tpl(m, m.proofs_enabled(), m_cfg), m_cfg(m, mm) {} }; void macro_manager::expand_macros(expr * n, proof * pr, expr_dependency * dep, expr_ref & r, proof_ref & new_pr, expr_dependency_ref & new_dep) { if (has_macros()) { // Expand macros with "real" proof production support (NO rewrite*) expr_ref old_n(m); proof_ref old_pr(m); expr_dependency_ref old_dep(m); old_n = n; old_pr = pr; old_dep = dep; bool change = false; for (;;) { macro_expander_rw proc(m, *this); proof_ref n_eq_r_pr(m); TRACE("macro_manager_bug", tout << "expand_macros:\n" << mk_pp(n, m) << "\n";); proc(old_n, r, n_eq_r_pr); new_pr = m.mk_modus_ponens(old_pr, n_eq_r_pr); new_dep = m.mk_join(old_dep, proc.m_cfg.m_used_macro_dependencies); if (r.get() == old_n.get()) break; old_n = r; old_pr = new_pr; old_dep = new_dep; change = true; } // apply th_rewrite to the result. if (change) { th_rewriter rw(m); proof_ref rw_pr(m); expr_ref r1(r, m); rw(r1, r, rw_pr); new_pr = m.mk_modus_ponens(new_pr, rw_pr); } } else { r = n; new_pr = pr; new_dep = dep; } } z3-z3-4.8.7/src/ast/macros/macro_manager.h000066400000000000000000000063541356505360400202450ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: macro_manager.h Abstract: Author: Leonardo de Moura (leonardo) 2010-04-05. Revision History: --*/ #ifndef MACRO_MANAGER_H_ #define MACRO_MANAGER_H_ #include "util/obj_hashtable.h" #include "ast/ast_util.h" #include "ast/justified_expr.h" #include "ast/recurse_expr.h" #include "ast/func_decl_dependencies.h" #include "ast/macros/macro_util.h" /** \brief Macros are universally quantified formulas of the form: (forall X (= (f X) T[X])) (forall X (iff (f X) T[X])) where T[X] does not contain X. This class is responsible for storing macros and expanding them. It has support for backtracking and tagging declarations in an expression as forbidded for being macros. */ class macro_manager { ast_manager & m; macro_util m_util; obj_map m_decl2macro; // func-decl -> quantifier obj_map m_decl2macro_pr; // func-decl -> quantifier_proof obj_map m_decl2macro_dep; // func-decl -> unsat core dependency func_decl_ref_vector m_decls; quantifier_ref_vector m_macros; proof_ref_vector m_macro_prs; expr_dependency_ref_vector m_macro_deps; obj_hashtable m_forbidden_set; func_decl_ref_vector m_forbidden; struct scope { unsigned m_decls_lim; unsigned m_forbidden_lim; }; svector m_scopes; func_decl_dependencies m_deps; void restore_decls(unsigned old_sz); void restore_forbidden(unsigned old_sz); struct macro_expander_cfg; struct macro_expander_rw; public: macro_manager(ast_manager & m); ~macro_manager(); ast_manager & get_manager() const { return m; } macro_util & get_util() { return m_util; } bool insert(func_decl * f, quantifier * m, proof * pr, expr_dependency * dep = nullptr); bool has_macros() const { return !m_macros.empty(); } void push_scope(); void pop_scope(unsigned num_scopes); void reset(); void mark_forbidden(unsigned n, expr * const * exprs); void mark_forbidden(unsigned n, justified_expr const * exprs); void mark_forbidden(expr * e) { mark_forbidden(1, &e); } bool is_forbidden(func_decl * d) const { return m_forbidden_set.contains(d); } obj_hashtable const & get_forbidden_set() const { return m_forbidden_set; } void display(std::ostream & out); unsigned get_num_macros() const { return m_decls.size(); } unsigned get_first_macro_last_level() const { return m_scopes.empty() ? 0 : m_scopes.back().m_decls_lim; } func_decl * get_macro_func_decl(unsigned i) const { return m_decls.get(i); } func_decl * get_macro_interpretation(unsigned i, expr_ref & interp) const; quantifier * get_macro_quantifier(func_decl * f) const { quantifier * q = nullptr; m_decl2macro.find(f, q); return q; } void get_head_def(quantifier * q, func_decl * d, app * & head, expr * & def) const; void expand_macros(expr * n, proof * pr, expr_dependency * dep, expr_ref & r, proof_ref & new_pr, expr_dependency_ref & new_dep); }; #endif /* MACRO_MANAGER_H_ */ z3-z3-4.8.7/src/ast/macros/macro_util.cpp000066400000000000000000000742451356505360400201470ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: macro_util.cpp Abstract: Macro finding goodies. They are used during preprocessing (MACRO_FINDER=true), and model building. Author: Leonardo de Moura (leonardo) 2010-12-15. Revision History: --*/ #include "ast/macros/macro_util.h" #include "ast/occurs.h" #include "ast/ast_util.h" #include "ast/rewriter/var_subst.h" #include "ast/ast_pp.h" #include "ast/ast_ll_pp.h" #include "ast/for_each_expr.h" #include "ast/well_sorted.h" #include "ast/rewriter/bool_rewriter.h" macro_util::macro_util(ast_manager & m): m_manager(m), m_bv(m), m_arith(m), m_arith_rw(m), m_bv_rw(m), m_forbidden_set(nullptr), m_curr_clause(nullptr) { } bool macro_util::is_bv(expr * n) const { return m_bv.is_bv(n); } bool macro_util::is_bv_sort(sort * s) const { return m_bv.is_bv_sort(s); } bool macro_util::is_add(expr * n) const { return m_arith.is_add(n) || m_bv.is_bv_add(n); } bool macro_util::is_times_minus_one(expr * n, expr * & arg) const { return m_arith_rw.is_times_minus_one(n, arg) || m_bv_rw.is_times_minus_one(n, arg); } bool macro_util::is_le(expr * n) const { return m_arith.is_le(n) || m_bv.is_bv_ule(n) || m_bv.is_bv_sle(n); } bool macro_util::is_le_ge(expr * n) const { return m_arith.is_ge(n) || m_arith.is_le(n) || m_bv.is_bv_ule(n) || m_bv.is_bv_sle(n); } bool macro_util::is_var_plus_ground(expr * n, bool & inv, var * & v, expr_ref & t) { return m_arith_rw.is_var_plus_ground(n, inv, v, t) || m_bv_rw.is_var_plus_ground(n, inv, v, t); } bool macro_util::is_zero_safe(expr * n) const { if (m_bv_rw.is_bv(n)) { return m_bv.is_zero(n); } else { return m_arith_rw.is_zero(n); } } app * macro_util::mk_zero(sort * s) const { if (m_bv.is_bv_sort(s)) { return m_bv.mk_numeral(rational(0), s); } else { return m_arith.mk_numeral(rational(0), s); } } void macro_util::mk_sub(expr * t1, expr * t2, expr_ref & r) const { if (is_bv(t1)) { m_bv_rw.mk_sub(t1, t2, r); } else { m_arith_rw.mk_sub(t1, t2, r); } } void macro_util::mk_add(expr * t1, expr * t2, expr_ref & r) const { if (is_bv(t1)) { m_bv_rw.mk_add(t1, t2, r); } else { m_arith_rw.mk_add(t1, t2, r); } } void macro_util::mk_add(unsigned num_args, expr * const * args, sort * s, expr_ref & r) const { switch (num_args) { case 0: r = mk_zero(s); break; case 1: r = args[0]; break; default: if (m_bv.is_bv_sort(s)) { r = args[0]; while (num_args >= 2) { --num_args; ++args; r = m_bv.mk_bv_add(r, args[0]); } } else { r = m_arith.mk_add(num_args, args); } break; } } /** \brief Return true if \c n is an application of the form (f x_{k_1}, ..., x_{k_n}) where f is uninterpreted n == num_decls x_{k_i}'s are variables and {k_1, ..., k_n } is equals to the set {0, ..., num_decls-1} */ bool macro_util::is_macro_head(expr * n, unsigned num_decls) const { if (is_app(n) && !to_app(n)->get_decl()->is_associative() && to_app(n)->get_family_id() == null_family_id && to_app(n)->get_num_args() == num_decls) { sbuffer var2pos; var2pos.resize(num_decls, -1); for (unsigned i = 0; i < num_decls; i++) { expr * c = to_app(n)->get_arg(i); if (!is_var(c)) return false; unsigned idx = to_var(c)->get_idx(); if (idx >= num_decls || var2pos[idx] != -1) return false; var2pos[idx] = i; } return true; } return false; } /** \brief Return true if n is of the form (= (f x_{k_1}, ..., x_{k_n}) t) OR (iff (f x_{k_1}, ..., x_{k_n}) t) where is_macro_head((f x_{k_1}, ..., x_{k_n})) returns true AND t does not contain f AND f is not in forbidden_set In case of success head will contain (f x_{k_1}, ..., x_{k_n}) AND def will contain t */ bool macro_util::is_left_simple_macro(expr * n, unsigned num_decls, app_ref & head, expr_ref & def) const { if (m_manager.is_eq(n)) { expr * lhs = to_app(n)->get_arg(0); expr * rhs = to_app(n)->get_arg(1); if (is_macro_head(lhs, num_decls) && !is_forbidden(to_app(lhs)->get_decl()) && !occurs(to_app(lhs)->get_decl(), rhs)) { head = to_app(lhs); def = rhs; return true; } } return false; } /** \brief Return true if n is of the form (= t (f x_{k_1}, ..., x_{k_n})) OR (iff t (f x_{k_1}, ..., x_{k_n})) where is_macro_head((f x_{k_1}, ..., x_{k_n})) returns true AND t does not contain f AND f is not in forbidden_set In case of success head will contain (f x_{k_1}, ..., x_{k_n}) AND def will contain t */ bool macro_util::is_right_simple_macro(expr * n, unsigned num_decls, app_ref & head, expr_ref & def) const { if (m_manager.is_eq(n)) { expr * lhs = to_app(n)->get_arg(0); expr * rhs = to_app(n)->get_arg(1); if (is_macro_head(rhs, num_decls) && !is_forbidden(to_app(rhs)->get_decl()) && !occurs(to_app(rhs)->get_decl(), lhs)) { head = to_app(rhs); def = lhs; return true; } } return false; } /** \brief Return true if n contains f. The method ignores the sub-expression \c exception. \remark n is a "polynomial". */ bool macro_util::poly_contains_head(expr * n, func_decl * f, expr * exception) const { unsigned num_args; expr * const * args; if (is_add(n)) { num_args = to_app(n)->get_num_args(); args = to_app(n)->get_args(); } else { num_args = 1; args = &n; } for (unsigned i = 0; i < num_args; i++) { expr * arg = args[i]; if (arg != exception && occurs(f, arg)) return true; } return false; } bool macro_util::is_arith_macro(expr * n, unsigned num_decls, app_ref & head, expr_ref & def, bool & inv) const { // TODO: obsolete... we should move to collect_arith_macro_candidates if (!m_manager.is_eq(n) && !m_arith.is_le(n) && !m_arith.is_ge(n)) return false; expr * lhs = to_app(n)->get_arg(0); expr * rhs = to_app(n)->get_arg(1); if (!m_arith.is_numeral(rhs)) return false; inv = false; ptr_buffer args; expr * h = nullptr; unsigned lhs_num_args; expr * const * lhs_args; if (is_add(lhs)) { lhs_num_args = to_app(lhs)->get_num_args(); lhs_args = to_app(lhs)->get_args(); } else { lhs_num_args = 1; lhs_args = &lhs; } for (unsigned i = 0; i < lhs_num_args; i++) { expr * arg = lhs_args[i]; expr * neg_arg; if (h == nullptr && is_macro_head(arg, num_decls) && !is_forbidden(to_app(arg)->get_decl()) && !poly_contains_head(lhs, to_app(arg)->get_decl(), arg)) { h = arg; } else if (h == nullptr && m_arith_rw.is_times_minus_one(arg, neg_arg) && is_macro_head(neg_arg, num_decls) && !is_forbidden(to_app(neg_arg)->get_decl()) && !poly_contains_head(lhs, to_app(neg_arg)->get_decl(), arg)) { h = neg_arg; inv = true; } else { args.push_back(arg); } } if (h == nullptr) return false; head = to_app(h); expr_ref tmp(m_manager); tmp = m_arith.mk_add(args.size(), args.c_ptr()); if (inv) mk_sub(tmp, rhs, def); else mk_sub(rhs, tmp, def); TRACE("macro_util", tout << def << "\n";); return true; } /** \brief Auxiliary function for is_pseudo_predicate_macro. It detects the pattern (= (f X) t) */ bool macro_util::is_pseudo_head(expr * n, unsigned num_decls, app_ref & head, app_ref & t) { if (!m_manager.is_eq(n)) return false; expr * lhs = to_app(n)->get_arg(0); expr * rhs = to_app(n)->get_arg(1); if (!is_ground(lhs) && !is_ground(rhs)) return false; sort * s = m_manager.get_sort(lhs); if (m_manager.is_uninterp(s)) return false; sort_size sz = s->get_num_elements(); if (sz.is_finite() && sz.size() == 1) return false; if (is_macro_head(lhs, num_decls)) { head = to_app(lhs); t = to_app(rhs); return true; } if (is_macro_head(rhs, num_decls)) { head = to_app(rhs); t = to_app(lhs); return true; } return false; } /** \brief Returns true if n if of the form (forall (X) (iff (= (f X) t) def[X])) where t is a ground term, (f X) is the head. */ bool macro_util::is_pseudo_predicate_macro(expr * n, app_ref & head, app_ref & t, expr_ref & def) { if (!is_forall(n)) return false; TRACE("macro_util", tout << "processing: " << mk_pp(n, m_manager) << "\n";); expr * body = to_quantifier(n)->get_expr(); unsigned num_decls = to_quantifier(n)->get_num_decls(); expr * lhs, *rhs; if (!m_manager.is_iff(body, lhs, rhs)) return false; if (is_pseudo_head(lhs, num_decls, head, t) && !is_forbidden(head->get_decl()) && !occurs(head->get_decl(), rhs)) { def = rhs; return true; } if (is_pseudo_head(rhs, num_decls, head, t) && !is_forbidden(head->get_decl()) && !occurs(head->get_decl(), lhs)) { def = lhs; return true; } return false; } /** \brief A quasi-macro head is of the form f[X_1, ..., X_n], where n == num_decls, f[X_1, ..., X_n] is a term starting with symbol f, f is uninterpreted, contains all universally quantified variables as arguments. Note that, some arguments of f[X_1, ..., X_n] may not be variables. Examples of quasi-macros: f(x_1, x_1 + x_2, x_2) for num_decls == 2 g(x_1, x_1) for num_decls == 1 Return true if \c n is a quasi-macro. Store the macro head in \c head, and the conditions to apply the macro in \c cond. */ bool macro_util::is_quasi_macro_head(expr * n, unsigned num_decls) const { if (is_app(n) && to_app(n)->get_family_id() == null_family_id && to_app(n)->get_num_args() >= num_decls) { unsigned num_args = to_app(n)->get_num_args(); sbuffer found_vars; found_vars.resize(num_decls, false); unsigned num_found_vars = 0; for (unsigned i = 0; i < num_args; i++) { expr * arg = to_app(n)->get_arg(i); if (is_var(arg)) { unsigned idx = to_var(arg)->get_idx(); if (idx >= num_decls) return false; if (found_vars[idx] == false) { found_vars[idx] = true; num_found_vars++; } } else { if (occurs(to_app(n)->get_decl(), arg)) return false; } } return num_found_vars == num_decls; } return false; } /** \brief Convert a quasi-macro head into a macro head, and store the conditions under which it is valid in cond. */ void macro_util::quasi_macro_head_to_macro_head(app * qhead, unsigned & num_decls, app_ref & head, expr_ref & cond) const { unsigned num_args = qhead->get_num_args(); sbuffer found_vars; found_vars.resize(num_decls, false); ptr_buffer new_args; ptr_buffer new_conds; unsigned next_var_idx = num_decls; for (unsigned i = 0; i < num_args; i++) { expr * arg = qhead->get_arg(i); if (is_var(arg)) { unsigned idx = to_var(arg)->get_idx(); SASSERT(idx < num_decls); if (found_vars[idx] == false) { found_vars[idx] = true; new_args.push_back(arg); continue; } } var * new_var = m_manager.mk_var(next_var_idx, m_manager.get_sort(arg)); next_var_idx++; expr * new_cond = m_manager.mk_eq(new_var, arg); new_args.push_back(new_var); new_conds.push_back(new_cond); } bool_rewriter(m_manager).mk_and(new_conds.size(), new_conds.c_ptr(), cond); head = m_manager.mk_app(qhead->get_decl(), new_args.size(), new_args.c_ptr()); num_decls = next_var_idx; } /** \brief Given a macro defined by head and def, stores an interpretation for head->get_decl() in interp. This method assumes is_macro_head(head, head->get_num_args()) returns true, and def does not contain head->get_decl(). See normalize_expr */ void macro_util::mk_macro_interpretation(app * head, unsigned num_decls, expr * def, expr_ref & interp) const { SASSERT(is_macro_head(head, head->get_num_args())); SASSERT(!occurs(head->get_decl(), def)); normalize_expr(head, num_decls, def, interp); } /** \brief The variables in head may be in the wrong order. Example: f(x_1, x_0) instead of f(x_0, x_1) This method is essentially renaming the variables in t. Suppose t is g(x_1, x_0 + x_1) This method will store g(x_0, x_1 + x_0) in norm_t. f(x_1, x_2) --> f(x_0, x_1) f(x_3, x_2) --> f(x_0, x_1) */ void macro_util::normalize_expr(app * head, unsigned num_decls, expr * t, expr_ref & norm_t) const { expr_ref_buffer var_mapping(m_manager); var_mapping.resize(num_decls); bool changed = false; unsigned num_args = head->get_num_args(); TRACE("macro_util", tout << "head: " << mk_pp(head, m_manager) << "\n"; tout << "applying substitution to:\n" << mk_bounded_pp(t, m_manager) << "\n";); for (unsigned i = 0; i < num_args; i++) { var * v = to_var(head->get_arg(i)); unsigned vi = v->get_idx(); SASSERT(vi < num_decls); if (vi != i) { changed = true; var_ref new_var(m_manager.mk_var(i, v->get_sort()), m_manager); var_mapping.setx(num_decls - vi - 1, new_var); } else var_mapping.setx(num_decls - i - 1, v); } if (changed) { // REMARK: t may have nested quantifiers... So, I must use the std order for variable substitution. var_subst subst(m_manager, true); TRACE("macro_util", tout << "head: " << mk_pp(head, m_manager) << "\n"; tout << "applying substitution to:\n" << mk_ll_pp(t, m_manager) << "\nsubstitution:\n"; for (unsigned i = 0; i < var_mapping.size(); i++) { if (var_mapping[i] != 0) tout << "#" << i << " -> " << mk_ll_pp(var_mapping[i], m_manager); }); norm_t = subst(t, var_mapping.size(), var_mapping.c_ptr()); } else { norm_t = t; } } // ----------------------------- // // "Hint" support // See comment at is_hint_atom // for a definition of what a hint is. // // ----------------------------- bool is_hint_head(expr * n, ptr_buffer & vars) { if (!is_app(n)) return false; if (to_app(n)->get_decl()->is_associative() || to_app(n)->get_family_id() != null_family_id) return false; unsigned num_args = to_app(n)->get_num_args(); for (unsigned i = 0; i < num_args; i++) { expr * arg = to_app(n)->get_arg(i); if (is_var(arg)) vars.push_back(to_var(arg)); } return !vars.empty(); } /** \brief Returns true if the variables in n is a subset of \c vars. */ bool vars_of_is_subset(expr * n, ptr_buffer const & vars) { if (is_ground(n)) return true; obj_hashtable visited; ptr_buffer todo; todo.push_back(n); while (!todo.empty()) { expr * curr = todo.back(); todo.pop_back(); if (is_var(curr)) { if (std::find(vars.begin(), vars.end(), to_var(curr)) == vars.end()) return false; } else if (is_app(curr)) { unsigned num_args = to_app(curr)->get_num_args(); for (unsigned i = 0; i < num_args; i++) { expr * arg = to_app(curr)->get_arg(i); if (is_ground(arg)) continue; if (visited.contains(arg)) continue; visited.insert(arg); todo.push_back(arg); } } else { SASSERT(is_quantifier(curr)); return false; // do no support nested quantifier... being conservative. } } return true; } /** \brief (= lhs rhs) is a hint atom if lhs is of the form (f t_1 ... t_n) and all variables occurring in rhs are direct arguments of lhs. */ bool is_hint_atom(expr * lhs, expr * rhs) { ptr_buffer vars; if (!is_hint_head(lhs, vars)) return false; return !occurs(to_app(lhs)->get_decl(), rhs) && vars_of_is_subset(rhs, vars); } void hint_to_macro_head(ast_manager & m, app * head, unsigned & num_decls, app_ref & new_head) { unsigned num_args = head->get_num_args(); ptr_buffer new_args; sbuffer found_vars; found_vars.resize(num_decls, false); unsigned next_var_idx = num_decls; for (unsigned i = 0; i < num_args; i++) { expr * arg = head->get_arg(i); if (is_var(arg)) { unsigned idx = to_var(arg)->get_idx(); SASSERT(idx < num_decls); if (found_vars[idx] == false) { found_vars[idx] = true; new_args.push_back(arg); continue; } } var * new_var = m.mk_var(next_var_idx, m.get_sort(arg)); next_var_idx++; new_args.push_back(new_var); } new_head = m.mk_app(head->get_decl(), new_args.size(), new_args.c_ptr()); num_decls = next_var_idx; } /** \brief Return true if n can be viewed as a polynomial "hint" based on head. That is, n (but the monomial exception) only uses the variables in head, and does not use head->get_decl(). is_hint_head(head, vars) must also return true */ bool macro_util::is_poly_hint(expr * n, app * head, expr * exception) { TRACE("macro_util", tout << "is_poly_hint n:\n" << mk_pp(n, m_manager) << "\nhead:\n" << mk_pp(head, m_manager) << "\nexception:\n"; if (exception) tout << mk_pp(exception, m_manager); else tout << ""; tout << "\n";); ptr_buffer vars; if (!is_hint_head(head, vars)) { TRACE("macro_util", tout << "failed because head is not hint head\n";); return false; } func_decl * f = head->get_decl(); unsigned num_args; expr * const * args; if (is_add(n)) { num_args = to_app(n)->get_num_args(); args = to_app(n)->get_args(); } else { num_args = 1; args = &n; } for (unsigned i = 0; i < num_args; i++) { expr * arg = args[i]; if (arg != exception && (occurs(f, arg) || !vars_of_is_subset(arg, vars))) { TRACE("macro_util", tout << "failed because of:\n" << mk_pp(arg, m_manager) << "\n";); return false; } } TRACE("macro_util", tout << "succeeded\n";); return true; } // ----------------------------- // // Macro candidates // // ----------------------------- macro_util::macro_candidates::macro_candidates(ast_manager & m): m_defs(m), m_conds(m) { } void macro_util::macro_candidates::reset() { m_fs.reset(); m_defs.reset(); m_conds.reset(); m_ineq.reset(); m_satisfy.reset(); m_hint.reset(); } void macro_util::macro_candidates::insert(func_decl * f, expr * def, expr * cond, bool ineq, bool satisfy_atom, bool hint) { m_fs.push_back(f); m_defs.push_back(def); m_conds.push_back(cond); m_ineq.push_back(ineq); m_satisfy.push_back(satisfy_atom); m_hint.push_back(hint); } // ----------------------------- // // Macro util // // ----------------------------- void macro_util::insert_macro(app * head, unsigned num_decls, expr * def, expr * cond, bool ineq, bool satisfy_atom, bool hint, macro_candidates & r) { expr_ref norm_def(m_manager); expr_ref norm_cond(m_manager); normalize_expr(head, num_decls, def, norm_def); if (cond != nullptr) normalize_expr(head, num_decls, cond, norm_cond); else if (!hint) norm_cond = m_manager.mk_true(); SASSERT(!hint || norm_cond.get() == 0); r.insert(head->get_decl(), norm_def.get(), norm_cond.get(), ineq, satisfy_atom, hint); } void macro_util::insert_quasi_macro(app * head, unsigned num_decls, expr * def, expr * cond, bool ineq, bool satisfy_atom, bool hint, macro_candidates & r) { if (!is_macro_head(head, head->get_num_args())) { app_ref new_head(m_manager); expr_ref extra_cond(m_manager); expr_ref new_cond(m_manager); if (!hint) { quasi_macro_head_to_macro_head(head, num_decls, new_head, extra_cond); if (cond == nullptr) new_cond = extra_cond; else bool_rewriter(m_manager).mk_and(cond, extra_cond, new_cond); } else { hint_to_macro_head(m_manager, head, num_decls, new_head); TRACE("macro_util", tout << "hint macro head: " << mk_ismt2_pp(new_head, m_manager) << std::endl; tout << "hint macro def: " << mk_ismt2_pp(def, m_manager) << std::endl; ); } insert_macro(new_head, num_decls, def, new_cond, ineq, satisfy_atom, hint, r); } else { insert_macro(head, num_decls, def, cond, ineq, satisfy_atom, hint, r); } } bool macro_util::rest_contains_decl(func_decl * f, expr * except_lit) { if (m_curr_clause == nullptr) return false; SASSERT(is_clause(m_manager, m_curr_clause)); unsigned num_lits = get_clause_num_literals(m_manager, m_curr_clause); for (unsigned i = 0; i < num_lits; i++) { expr * l = get_clause_literal(m_manager, m_curr_clause, i); if (l != except_lit && occurs(f, l)) return true; } return false; } void macro_util::get_rest_clause_as_cond(expr * except_lit, expr_ref & extra_cond) { if (m_curr_clause == nullptr) return; SASSERT(is_clause(m_manager, m_curr_clause)); expr_ref_buffer neg_other_lits(m_manager); unsigned num_lits = get_clause_num_literals(m_manager, m_curr_clause); for (unsigned i = 0; i < num_lits; i++) { expr * l = get_clause_literal(m_manager, m_curr_clause, i); if (l != except_lit) { expr_ref neg_l(m_manager); bool_rewriter(m_manager).mk_not(l, neg_l); neg_other_lits.push_back(neg_l); } } if (neg_other_lits.empty()) return; bool_rewriter(m_manager).mk_and(neg_other_lits.size(), neg_other_lits.c_ptr(), extra_cond); } void macro_util::collect_poly_args(expr * n, expr * exception, ptr_buffer & args) { args.reset(); unsigned num_args; expr * const * _args; if (is_add(n)) { num_args = to_app(n)->get_num_args(); _args = to_app(n)->get_args(); } else { num_args = 1; _args = &n; } for (unsigned i = 0; i < num_args; i++) { expr * arg = _args[i]; if (arg != exception) args.push_back(arg); } } void macro_util::add_arith_macro_candidate(app * head, unsigned num_decls, expr * def, expr * atom, bool ineq, bool hint, macro_candidates & r) { expr_ref cond(m_manager); if (!hint) get_rest_clause_as_cond(atom, cond); insert_quasi_macro(head, num_decls, def, cond, ineq, true, hint, r); } void macro_util::collect_arith_macro_candidates(expr * lhs, expr * rhs, expr * atom, unsigned num_decls, bool is_ineq, macro_candidates & r) { if (!is_add(lhs) && m_manager.is_eq(atom)) // this case is a simple macro. return; ptr_buffer args; unsigned lhs_num_args; expr * const * lhs_args; if (is_add(lhs)) { lhs_num_args = to_app(lhs)->get_num_args(); lhs_args = to_app(lhs)->get_args(); } else { lhs_num_args = 1; lhs_args = &lhs; } for (unsigned i = 0; i < lhs_num_args; i++) { expr * arg = lhs_args[i]; expr * neg_arg; if (!is_app(arg)) continue; func_decl * f = to_app(arg)->get_decl(); bool _is_arith_macro = is_quasi_macro_head(arg, num_decls) && !is_forbidden(f) && !poly_contains_head(lhs, f, arg) && !occurs(f, rhs) && !rest_contains_decl(f, atom); bool _is_poly_hint = !_is_arith_macro && is_poly_hint(lhs, to_app(arg), arg); if (_is_arith_macro || _is_poly_hint) { collect_poly_args(lhs, arg, args); expr_ref rest(m_manager); mk_add(args.size(), args.c_ptr(), m_manager.get_sort(arg), rest); expr_ref def(m_manager); mk_sub(rhs, rest, def); // If is_poly_hint, rhs may contain variables that do not occur in to_app(arg). // So, we should re-check. if (!_is_poly_hint || is_poly_hint(def, to_app(arg), nullptr)) add_arith_macro_candidate(to_app(arg), num_decls, def, atom, is_ineq, _is_poly_hint, r); } else if (is_times_minus_one(arg, neg_arg) && is_app(neg_arg)) { f = to_app(neg_arg)->get_decl(); bool _is_arith_macro = is_quasi_macro_head(neg_arg, num_decls) && !is_forbidden(f) && !poly_contains_head(lhs, f, arg) && !occurs(f, rhs) && !rest_contains_decl(f, atom); bool _is_poly_hint = !_is_arith_macro && is_poly_hint(lhs, to_app(neg_arg), arg); if (_is_arith_macro || _is_poly_hint) { collect_poly_args(lhs, arg, args); expr_ref rest(m_manager); mk_add(args.size(), args.c_ptr(), m_manager.get_sort(arg), rest); expr_ref def(m_manager); mk_sub(rest, rhs, def); // If is_poly_hint, rhs may contain variables that do not occur in to_app(neg_arg). // So, we should re-check. if (!_is_poly_hint || is_poly_hint(def, to_app(neg_arg), nullptr)) add_arith_macro_candidate(to_app(neg_arg), num_decls, def, atom, is_ineq, _is_poly_hint, r); } } } } void macro_util::collect_arith_macro_candidates(expr * atom, unsigned num_decls, macro_candidates & r) { TRACE("macro_util", tout << "collect_arith_macro_candidates:\n" << mk_pp(atom, m_manager) << "\n";); if (!m_manager.is_eq(atom) && !is_le_ge(atom)) return; expr * lhs = to_app(atom)->get_arg(0); expr * rhs = to_app(atom)->get_arg(1); bool is_ineq = !m_manager.is_eq(atom); collect_arith_macro_candidates(lhs, rhs, atom, num_decls, is_ineq, r); collect_arith_macro_candidates(rhs, lhs, atom, num_decls, is_ineq, r); } /** \brief Collect macro candidates for atom \c atom. The candidates are stored in \c r. The following post-condition holds: for each i in [0, r.size() - 1] we have a conditional macro of the form r.get_cond(i) IMPLIES f(x_1, ..., x_n) = r.get_def(i) where f == r.get_fs(i) .., x_n), f is uninterpreted and x_1, ..., x_n are variables. r.get_cond(i) and r.get_defs(i) do not contain f or variables not in {x_1, ..., x_n} The idea is to use r.get_defs(i) as the interpretation for f in a model M whenever r.get_cond(i) Given a model M and values { v_1, ..., v_n } Let M' be M{x_1 -> v_1, ..., v_n -> v_n} Note that M'(f(x_1, ..., x_n)) = M(f)(v_1, ..., v_n) Then, IF we have that M(f)(v_1, ..., v_n) = M'(r.get_def(i)) AND M'(r.get_cond(i)) = true THEN M'(atom) = true That is, if the conditional macro is used then the atom is satisfied when M'(r.get_cond(i)) = true IF r.is_ineq(i) = false, then M(f)(v_1, ..., v_n) ***MUST BE*** M'(r.get_def(i)) whenever M'(r.get_cond(i)) = true IF r.satisfy_atom(i) = true, then we have the stronger property: Then, IF we have that (M'(r.get_cond(i)) = true IMPLIES M(f)(v_1, ..., v_n) = M'(r.get_def(i))) THEN M'(atom) = true */ void macro_util::collect_macro_candidates_core(expr * atom, unsigned num_decls, macro_candidates & r) { expr* lhs, *rhs; TRACE("macro_util", tout << "Candidate check for: " << mk_ismt2_pp(atom, m_manager) << std::endl;); if (m_manager.is_eq(atom, lhs, rhs) || m_manager.is_iff(atom, lhs, rhs)) { if (is_quasi_macro_head(lhs, num_decls) && !is_forbidden(to_app(lhs)->get_decl()) && !occurs(to_app(lhs)->get_decl(), rhs) && !rest_contains_decl(to_app(lhs)->get_decl(), atom)) { expr_ref cond(m_manager); get_rest_clause_as_cond(atom, cond); insert_quasi_macro(to_app(lhs), num_decls, rhs, cond, false, true, false, r); } else if (is_hint_atom(lhs, rhs)) { insert_quasi_macro(to_app(lhs), num_decls, rhs, nullptr, false, true, true, r); } if (is_quasi_macro_head(rhs, num_decls) && !is_forbidden(to_app(rhs)->get_decl()) && !occurs(to_app(rhs)->get_decl(), lhs) && !rest_contains_decl(to_app(rhs)->get_decl(), atom)) { expr_ref cond(m_manager); get_rest_clause_as_cond(atom, cond); insert_quasi_macro(to_app(rhs), num_decls, lhs, cond, false, true, false, r); } else if (is_hint_atom(rhs, lhs)) { insert_quasi_macro(to_app(rhs), num_decls, lhs, nullptr, false, true, true, r); } } collect_arith_macro_candidates(atom, num_decls, r); } void macro_util::collect_macro_candidates(expr * atom, unsigned num_decls, macro_candidates & r) { m_curr_clause = nullptr; r.reset(); collect_macro_candidates_core(atom, num_decls, r); } void macro_util::collect_macro_candidates(quantifier * q, macro_candidates & r) { r.reset(); expr * n = q->get_expr(); if (has_quantifiers(n)) return; unsigned num_decls = q->get_num_decls(); SASSERT(m_curr_clause == 0); if (is_clause(m_manager, n)) { m_curr_clause = n; unsigned num_lits = get_clause_num_literals(m_manager, n); for (unsigned i = 0; i < num_lits; i++) collect_macro_candidates_core(get_clause_literal(m_manager, n, i), num_decls, r); m_curr_clause = nullptr; } else { collect_macro_candidates_core(n, num_decls, r); } } z3-z3-4.8.7/src/ast/macros/macro_util.h000066400000000000000000000136231356505360400176050ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: macro_util.h Abstract: Macro finding goodies. They are used during preprocessing (MACRO_FINDER=true), and model building. Author: Leonardo de Moura (leonardo) 2010-12-15. Revision History: --*/ #ifndef MACRO_UTIL_H_ #define MACRO_UTIL_H_ #include "ast/ast.h" #include "util/obj_hashtable.h" #include "ast/rewriter/arith_rewriter.h" #include "ast/rewriter/bv_rewriter.h" class macro_util { public: /** \brief See collect_macro_candidates. */ class macro_candidates { ptr_vector m_fs; expr_ref_vector m_defs; expr_ref_vector m_conds; svector m_ineq; // true if the macro is based on an inequality instead of equality. svector m_satisfy; svector m_hint; // macro did not contain all universal variables in the quantifier. friend class macro_util; ast_manager & get_manager() { return m_conds.get_manager(); } public: macro_candidates(ast_manager & m); ~macro_candidates() { reset(); } void reset(); void insert(func_decl * f, expr * def, expr * cond, bool ineq, bool satisfy_atom, bool hint); bool empty() const { return m_fs.empty(); } unsigned size() const { return m_fs.size(); } func_decl * get_f(unsigned i) const { return m_fs[i]; } expr * get_def(unsigned i) const { return m_defs.get(i); } expr * get_cond(unsigned i) const { return m_conds.get(i); } bool ineq(unsigned i) const { return m_ineq[i]; } bool satisfy_atom(unsigned i) const { return m_satisfy[i]; } bool hint(unsigned i) const { return m_hint[i]; } }; private: ast_manager & m_manager; bv_util m_bv; arith_util m_arith; mutable arith_rewriter m_arith_rw; mutable bv_rewriter m_bv_rw; obj_hashtable * m_forbidden_set; bool is_forbidden(func_decl * f) const { return m_forbidden_set != nullptr && m_forbidden_set->contains(f); } bool poly_contains_head(expr * n, func_decl * f, expr * exception) const; void collect_arith_macros(expr * n, unsigned num_decls, unsigned max_macros, bool allow_cond_macros, macro_candidates & r); void normalize_expr(app * head, unsigned num_decls, expr * t, expr_ref & norm_t) const; void insert_macro(app * head, unsigned num_decls, expr * def, expr * cond, bool ineq, bool satisfy_atom, bool hint, macro_candidates & r); void insert_quasi_macro(app * head, unsigned num_decls, expr * def, expr * cond, bool ineq, bool satisfy_atom, bool hint, macro_candidates & r); expr * m_curr_clause; // auxiliary var used in collect_macro_candidates. // Return true if m_curr_clause contains f in a literal different from except_lit bool rest_contains_decl(func_decl * f, expr * except_lit); // Store in extra_cond (and (not l_1) ... (not l_n)) where l_i's are the literals of m_curr_clause that are different from except_lit. void get_rest_clause_as_cond(expr * except_lit, expr_ref & extra_cond); void collect_poly_args(expr * n, expr * exception, ptr_buffer & args); void add_arith_macro_candidate(app * head, unsigned num_decls, expr * def, expr * atom, bool ineq, bool hint, macro_candidates & r); void collect_arith_macro_candidates(expr * lhs, expr * rhs, expr * atom, unsigned num_decls, bool ineq, macro_candidates & r); void collect_arith_macro_candidates(expr * atom, unsigned num_decls, macro_candidates & r); void collect_macro_candidates_core(expr * atom, unsigned num_decls, macro_candidates & r); bool is_poly_hint(expr * n, app * head, expr * exception); public: macro_util(ast_manager & m); void set_forbidden_set(obj_hashtable * s) { m_forbidden_set = s; } bool is_macro_head(expr * n, unsigned num_decls) const; bool is_left_simple_macro(expr * n, unsigned num_decls, app_ref & head, expr_ref & def) const; bool is_right_simple_macro(expr * n, unsigned num_decls, app_ref & head, expr_ref & def) const; bool is_simple_macro(expr * n, unsigned num_decls, app_ref& head, expr_ref & def) const { return is_left_simple_macro(n, num_decls, head, def) || is_right_simple_macro(n, num_decls, head, def); } bool is_arith_macro(expr * n, unsigned num_decls, app_ref & head, expr_ref & def, bool & inv) const; bool is_arith_macro(expr * n, unsigned num_decls, app_ref & head, expr_ref & def) const { bool inv; return is_arith_macro(n, num_decls, head, def, inv); } bool is_zero_safe(expr * n) const; bool is_var_plus_ground(expr * n, bool & inv, var * & v, expr_ref & t); bool is_pseudo_head(expr * n, unsigned num_decls, app_ref & head, app_ref & t); bool is_pseudo_predicate_macro(expr * n, app_ref & head, app_ref & t, expr_ref & def); bool is_quasi_macro_head(expr * n, unsigned num_decls) const; void quasi_macro_head_to_macro_head(app * qhead, unsigned & num_decls, app_ref & head, expr_ref & cond) const; void mk_macro_interpretation(app * head, unsigned num_decls, expr * def, expr_ref & interp) const; void collect_macro_candidates(expr * atom, unsigned num_decls, macro_candidates & r); void collect_macro_candidates(quantifier * q, macro_candidates & r); // // Auxiliary goodness that allows us to manipulate BV and Arith polynomials. // bool is_bv(expr * n) const; bool is_bv_sort(sort * s) const; app * mk_zero(sort * s) const; bool is_add(expr * n) const; bool is_times_minus_one(expr * n, expr * & arg) const; bool is_le(expr * n) const; bool is_le_ge(expr * n) const; void mk_sub(expr * t1, expr * t2, expr_ref & r) const; void mk_add(expr * t1, expr * t2, expr_ref & r) const; void mk_add(unsigned num_args, expr * const * args, sort * s, expr_ref & r) const; }; #endif z3-z3-4.8.7/src/ast/macros/quasi_macros.cpp000066400000000000000000000326041356505360400204700ustar00rootroot00000000000000/*++ Copyright (c) 2010 Microsoft Corporation Module Name: quasi_macros.cpp Abstract: Author: Christoph Wintersteiger (t-cwinte) 2010-04-23 Revision History: --*/ #include "ast/macros/quasi_macros.h" #include "ast/for_each_expr.h" #include "ast/ast_pp.h" #include "util/uint_set.h" #include "ast/rewriter/var_subst.h" quasi_macros::quasi_macros(ast_manager & m, macro_manager & mm) : m_manager(m), m_macro_manager(mm), m_rewriter(m), m_new_vars(m), m_new_eqs(m), m_new_qsorts(m) { } quasi_macros::~quasi_macros() { } void quasi_macros::find_occurrences(expr * e) { unsigned j; m_todo.reset(); m_todo.push_back(e); // we remember whether we have seen an expr once, or more than once; // when we see it the second time, we don't have to visit it another time, // as we are only interested in finding unique function applications. m_visited_once.reset(); m_visited_more.reset(); while (!m_todo.empty()) { expr * cur = m_todo.back(); m_todo.pop_back(); if (m_visited_more.is_marked(cur)) continue; if (m_visited_once.is_marked(cur)) m_visited_more.mark(cur, true); m_visited_once.mark(cur, true); switch (cur->get_kind()) { case AST_VAR: break; case AST_QUANTIFIER: m_todo.push_back(to_quantifier(cur)->get_expr()); break; case AST_APP: if (is_non_ground_uninterp(cur)) { func_decl * f = to_app(cur)->get_decl(); m_occurrences.insert_if_not_there(f, 0); occurrences_map::iterator it = m_occurrences.find_iterator(f); it->m_value++; } j = to_app(cur)->get_num_args(); while (j) m_todo.push_back(to_app(cur)->get_arg(--j)); break; default: UNREACHABLE(); } } }; bool quasi_macros::is_non_ground_uninterp(expr const * e) const { return is_non_ground(e) && is_uninterp(e); } bool quasi_macros::is_unique(func_decl * f) const { return m_occurrences.find(f) == 1; } struct var_dep_proc { bit_vector m_bitset; public: var_dep_proc(quantifier * q) { m_bitset.resize(q->get_num_decls(), false); } void operator()(var * n) { m_bitset.set(n->get_idx(), true); } void operator()(quantifier * n) {} void operator()(app * n) {} bool all_used() { for (unsigned i = 0; i < m_bitset.size() ; i++) if (!m_bitset.get(i)) return false; return true; } }; bool quasi_macros::fully_depends_on(app * a, quantifier * q) const { // CMW: This checks whether all variables in q are used _somewhere_ deep down in the children of a /* var_dep_proc proc(q); for_each_expr(proc, a); return proc.all_used(); */ // CMW: This code instead checks that all variables appear at least once as a // direct argument of a, i.e., a->get_arg(i) == v for some i bit_vector bitset; bitset.resize(q->get_num_decls(), false); for (unsigned i = 0 ; i < a->get_num_args() ; i++) { if (is_var(a->get_arg(i))) bitset.set(to_var(a->get_arg(i))->get_idx(), true); } for (unsigned i = 0; i < bitset.size() ; i++) { if (!bitset.get(i)) return false; } return true; } bool quasi_macros::depends_on(expr * e, func_decl * f) const { ptr_vector todo; expr_mark visited; todo.push_back(e); while(!todo.empty()) { expr * cur = todo.back(); todo.pop_back(); if (visited.is_marked(cur)) continue; if (is_app(cur)) { app * a = to_app(cur); if (a->get_decl() == f) return true; unsigned j = a->get_num_args(); while (j>0) todo.push_back(a->get_arg(--j)); } visited.mark(cur, true); } return false; } bool quasi_macros::is_quasi_macro(expr * e, app_ref & a, expr_ref & t) const { // Our definition of a quasi-macro: // Forall X. f[X] = T[X], where f[X] is a term starting with symbol f, f is uninterpreted, // f[X] contains all universally quantified variables, and f does not occur in T[X]. TRACE("quasi_macros", tout << "Checking for quasi macro: " << mk_pp(e, m_manager) << std::endl;); if (is_forall(e)) { quantifier * q = to_quantifier(e); expr * qe = q->get_expr(); if ((m_manager.is_eq(qe))) { expr * lhs = to_app(qe)->get_arg(0); expr * rhs = to_app(qe)->get_arg(1); if (is_non_ground_uninterp(lhs) && is_unique(to_app(lhs)->get_decl()) && !depends_on(rhs, to_app(lhs)->get_decl()) && fully_depends_on(to_app(lhs), q)) { a = to_app(lhs); t = rhs; return true; } else if (is_non_ground_uninterp(rhs) && is_unique(to_app(rhs)->get_decl()) && !depends_on(lhs, to_app(rhs)->get_decl()) && fully_depends_on(to_app(rhs), q)) { a = to_app(rhs); t = lhs; return true; } } else if (m_manager.is_not(qe) && is_non_ground_uninterp(to_app(qe)->get_arg(0)) && is_unique(to_app(to_app(qe)->get_arg(0))->get_decl())) { // this is like f(...) = false a = to_app(to_app(qe)->get_arg(0)); t = m_manager.mk_false(); return true; } else if (is_non_ground_uninterp(qe) && is_unique(to_app(qe)->get_decl())) { // this is like f(...) = true a = to_app(qe); t = m_manager.mk_true(); return true; } } return false; } void quasi_macros::quasi_macro_to_macro(quantifier * q, app * a, expr * t, quantifier_ref & macro) { m_new_var_names.reset(); m_new_vars.reset(); m_new_qsorts.reset(); m_new_eqs.reset(); func_decl * f = a->get_decl(); // CMW: we rely on the fact that all variables in q appear at least once as // a direct argument of `a'. bit_vector v_seen; v_seen.resize(q->get_num_decls(), false); for (unsigned i = 0 ; i < a->get_num_args() ; i++) { if (!is_var(a->get_arg(i)) || v_seen.get(to_var(a->get_arg(i))->get_idx())) { unsigned inx = m_new_var_names.size(); m_new_name.str(""); m_new_name << "X" << inx; m_new_var_names.push_back(symbol(m_new_name.str().c_str())); m_new_qsorts.push_back(f->get_domain()[i]); m_new_vars.push_back(m_manager.mk_var(inx + q->get_num_decls(), f->get_domain()[i])); m_new_eqs.push_back(m_manager.mk_eq(m_new_vars.back(), a->get_arg(i))); } else { var * v = to_var(a->get_arg(i)); m_new_vars.push_back(v); v_seen.set(v->get_idx(), true); } } // Reverse the new variable names and sorts. [CMW: There is a smarter way to do this.] vector new_var_names_rev; sort_ref_vector new_qsorts_rev(m_manager); unsigned i = m_new_var_names.size(); while (i > 0) { i--; new_var_names_rev.push_back(m_new_var_names.get(i)); new_qsorts_rev.push_back(m_new_qsorts.get(i)); } // We want to keep all the old variables [already reversed] for (unsigned i = 0 ; i < q->get_num_decls() ; i++) { new_var_names_rev.push_back(q->get_decl_name(i)); new_qsorts_rev.push_back(q->get_decl_sort(i)); } // Macro := Forall m_new_vars . appl = ITE( m_new_eqs, t, f_else) app_ref appl(m_manager); expr_ref eq(m_manager); appl = m_manager.mk_app(f, m_new_vars.size(), m_new_vars.c_ptr()); func_decl * fd = m_manager.mk_fresh_func_decl(f->get_name(), symbol("else"), f->get_arity(), f->get_domain(), f->get_range()); expr * f_else = m_manager.mk_app(fd, m_new_vars.size(), m_new_vars.c_ptr()); expr_ref ite(m_manager); ite = m_manager.mk_ite(m_manager.mk_and(m_new_eqs.size(), m_new_eqs.c_ptr()), t, f_else); eq = m_manager.mk_eq(appl, ite); macro = m_manager.mk_quantifier(forall_k, new_var_names_rev.size(), new_qsorts_rev.c_ptr(), new_var_names_rev.c_ptr(), eq); } bool quasi_macros::find_macros(unsigned n, expr * const * exprs) { TRACE("quasi_macros", tout << "Finding quasi-macros in: " << std::endl; for (unsigned i = 0 ; i < n ; i++) tout << i << ": " << mk_pp(exprs[i], m_manager) << std::endl; ); bool res = false; m_occurrences.reset(); // Find out how many non-ground appearances for each uninterpreted function there are for (unsigned i = 0 ; i < n ; i++) find_occurrences(exprs[i]); TRACE("quasi_macros", tout << "Occurrences: " << std::endl; for (auto & kd : m_occurrences) tout << kd.m_key->get_name() << ": " << kd.m_value << std::endl; ); // Find all macros for (unsigned i = 0 ; i < n ; i++) { app_ref a(m_manager); expr_ref t(m_manager); if (is_quasi_macro(exprs[i], a, t)) { quantifier_ref macro(m_manager); quasi_macro_to_macro(to_quantifier(exprs[i]), a, t, macro); TRACE("quasi_macros", tout << "Found quasi macro: " << mk_pp(exprs[i], m_manager) << std::endl; tout << "Macro: " << mk_pp(macro, m_manager) << std::endl; ); proof * pr = nullptr; if (m_manager.proofs_enabled()) pr = m_manager.mk_def_axiom(macro); expr_dependency * dep = nullptr; if (m_macro_manager.insert(a->get_decl(), macro, pr, dep)) res = true; } } return res; } bool quasi_macros::find_macros(unsigned n, justified_expr const * exprs) { TRACE("quasi_macros", tout << "Finding quasi-macros in: " << std::endl; for (unsigned i = 0 ; i < n ; i++) tout << i << ": " << mk_pp(exprs[i].get_fml(), m_manager) << std::endl; ); bool res = false; m_occurrences.reset(); // Find out how many non-ground appearances for each uninterpreted function there are for ( unsigned i = 0 ; i < n ; i++ ) find_occurrences(exprs[i].get_fml()); TRACE("quasi_macros", tout << "Occurrences: " << std::endl; for (occurrences_map::iterator it = m_occurrences.begin(); it != m_occurrences.end(); it++) tout << it->m_key->get_name() << ": " << it->m_value << std::endl; ); // Find all macros for ( unsigned i = 0 ; i < n ; i++ ) { app_ref a(m_manager); expr_ref t(m_manager); if (is_quasi_macro(exprs[i].get_fml(), a, t)) { quantifier_ref macro(m_manager); quasi_macro_to_macro(to_quantifier(exprs[i].get_fml()), a, t, macro); TRACE("quasi_macros", tout << "Found quasi macro: " << mk_pp(exprs[i].get_fml(), m_manager) << std::endl; tout << "Macro: " << mk_pp(macro, m_manager) << std::endl; ); proof * pr = nullptr; if (m_manager.proofs_enabled()) pr = m_manager.mk_def_axiom(macro); if (m_macro_manager.insert(a->get_decl(), macro, pr)) res = true; } } return res; } void quasi_macros::apply_macros(unsigned n, expr * const * exprs, proof * const * prs, expr_dependency * const* deps, expr_ref_vector & new_exprs, proof_ref_vector & new_prs, expr_dependency_ref_vector& new_deps) { for ( unsigned i = 0 ; i < n ; i++ ) { expr_ref r(m_manager), rs(m_manager); proof_ref pr(m_manager), ps(m_manager); expr_dependency_ref dep(m_manager); proof * p = m_manager.proofs_enabled() ? prs[i] : nullptr; m_macro_manager.expand_macros(exprs[i], p, deps[i], r, pr, dep); m_rewriter(r); new_exprs.push_back(r); new_prs.push_back(ps); new_deps.push_back(dep); } } bool quasi_macros::operator()(unsigned n, expr * const * exprs, proof * const * prs, expr_dependency * const * deps, expr_ref_vector & new_exprs, proof_ref_vector & new_prs, expr_dependency_ref_vector & new_deps) { if (find_macros(n, exprs)) { apply_macros(n, exprs, prs, deps, new_exprs, new_prs, new_deps); return true; } else { // just copy them over for ( unsigned i = 0 ; i < n ; i++ ) { new_exprs.push_back(exprs[i]); if (m_manager.proofs_enabled()) new_prs.push_back(prs[i]); } return false; } } void quasi_macros::apply_macros(unsigned n, justified_expr const* fmls, vector& new_fmls) { for ( unsigned i = 0 ; i < n ; i++ ) { expr_ref r(m_manager), rs(m_manager); proof_ref pr(m_manager), ps(m_manager); proof * p = m_manager.proofs_enabled() ? fmls[i].get_proof() : nullptr; expr_dependency_ref dep(m_manager); m_macro_manager.expand_macros(fmls[i].get_fml(), p, nullptr, r, pr, dep); m_rewriter(r); new_fmls.push_back(justified_expr(m_manager, r, pr)); } } bool quasi_macros::operator()(unsigned n, justified_expr const* fmls, vector& new_fmls) { if (find_macros(n, fmls)) { apply_macros(n, fmls, new_fmls); return true; } else { // just copy them over for ( unsigned i = 0 ; i < n ; i++ ) { new_fmls.push_back(fmls[i]); } return false; } } z3-z3-4.8.7/src/ast/macros/quasi_macros.h000066400000000000000000000046561356505360400201430ustar00rootroot00000000000000/*++ Copyright (c) 2010 Microsoft Corporation Module Name: quasi_macros.cpp Abstract: Author: Christoph Wintersteiger (t-cwinte) 2010-04-23 Revision History: --*/ #ifndef QUASI_MACROS_H_ #define QUASI_MACROS_H_ #include #include "ast/justified_expr.h" #include "ast/macros/macro_manager.h" #include "ast/rewriter/th_rewriter.h" /** \brief Finds quasi macros and applies them. */ class quasi_macros { typedef obj_map occurrences_map; ast_manager & m_manager; macro_manager & m_macro_manager; th_rewriter m_rewriter; occurrences_map m_occurrences; ptr_vector m_todo; vector m_new_var_names; expr_ref_vector m_new_vars; expr_ref_vector m_new_eqs; sort_ref_vector m_new_qsorts; std::stringstream m_new_name; expr_mark m_visited_once; expr_mark m_visited_more; bool is_unique(func_decl * f) const; bool is_non_ground_uninterp(expr const * e) const; bool fully_depends_on(app * a, quantifier * q) const; bool depends_on(expr * e, func_decl * f) const; bool is_quasi_macro(expr * e, app_ref & a, expr_ref &v) const; void quasi_macro_to_macro(quantifier * q, app * a, expr * t, quantifier_ref & macro); void find_occurrences(expr * e); bool find_macros(unsigned n, expr * const * exprs); bool find_macros(unsigned n, justified_expr const* expr); void apply_macros(unsigned n, expr * const * exprs, proof * const * prs, expr_dependency * const* deps, expr_ref_vector & new_exprs, proof_ref_vector & new_prs, expr_dependency_ref_vector& new_deps); void apply_macros(unsigned n, justified_expr const* fmls, vector& new_fmls); public: quasi_macros(ast_manager & m, macro_manager & mm); ~quasi_macros(); /** \brief Find pure function macros and apply them. */ // bool operator()(unsigned n, expr * const * exprs, proof * const * prs, expr_ref_vector & new_exprs, proof_ref_vector & new_prs); bool operator()(unsigned n, justified_expr const* fmls, vector& new_fmls); bool operator()(unsigned n, expr * const * exprs, proof * const * prs, expr_dependency * const * deps, expr_ref_vector & new_exprs, proof_ref_vector & new_prs, expr_dependency_ref_vector & new_deps); }; #endif z3-z3-4.8.7/src/ast/normal_forms/000077500000000000000000000000001356505360400165035ustar00rootroot00000000000000z3-z3-4.8.7/src/ast/normal_forms/CMakeLists.txt000066400000000000000000000003411356505360400212410ustar00rootroot00000000000000z3_add_component(normal_forms SOURCES defined_names.cpp name_exprs.cpp nnf.cpp pull_quant.cpp COMPONENT_DEPENDENCIES rewriter PYG_FILES nnf_params.pyg EXTRA_REGISTER_MODULE_HEADERS nnf.h ) z3-z3-4.8.7/src/ast/normal_forms/defined_names.cpp000066400000000000000000000315101356505360400217700ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: defined_names.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-01-14. Revision History: --*/ #include "util/obj_hashtable.h" #include "ast/normal_forms/defined_names.h" #include "ast/used_vars.h" #include "ast/rewriter/var_subst.h" #include "ast/ast_smt2_pp.h" #include "ast/ast_pp.h" #include "ast/ast_util.h" #include "ast/array_decl_plugin.h" struct defined_names::impl { typedef obj_map expr2name; typedef obj_map expr2proof; ast_manager & m; symbol m_z3name; /** \brief Mapping from expressions to their names. A name is an application. If the expression does not have free variables, then the name is just a constant. */ expr2name m_expr2name; /** \brief Mapping from expressions to the apply-def proof. That is, for each expression e, m_expr2proof[e] is the proof e and m_expr2name[2] are observ. equivalent. This mapping is not used if proof production is disabled. */ expr2proof m_expr2proof; /** \brief Domain of m_expr2name. It is used to keep the expressions alive and for backtracking */ expr_ref_vector m_exprs; expr_ref_vector m_names; //!< Range of m_expr2name. It is used to keep the names alive. proof_ref_vector m_apply_proofs; //!< Range of m_expr2proof. It is used to keep the def-intro proofs alive. unsigned_vector m_lims; //!< Backtracking support. impl(ast_manager & m, char const * prefix); virtual ~impl(); app * gen_name(expr * e, sort_ref_buffer & var_sorts, buffer & var_names); void cache_new_name(expr * e, app * name); void cache_new_name_intro_proof(expr * e, proof * pr); void bound_vars(sort_ref_buffer const & sorts, buffer const & names, expr * def_conjunct, app * name, expr_ref & result, symbol const& qid = symbol::null); void bound_vars(sort_ref_buffer const & sorts, buffer const & names, expr * def_conjunct, app * name, expr_ref_buffer & result, symbol const& qid = symbol::null); virtual void mk_definition(expr * e, app * n, sort_ref_buffer & var_sorts, buffer & var_names, expr_ref & new_def); bool mk_name(expr * e, expr_ref & new_def, proof_ref & new_def_pr, app_ref & n, proof_ref & pr); void push_scope(); void pop_scope(unsigned num_scopes); void reset(); unsigned get_num_names() const { return m_names.size(); } func_decl * get_name_decl(unsigned i) const { return to_app(m_names.get(i))->get_decl(); } }; struct defined_names::pos_impl : public defined_names::impl { pos_impl(ast_manager & m, char const * fresh_prefix):impl(m, fresh_prefix) {} void mk_definition(expr * e, app * n, sort_ref_buffer & var_sorts, buffer & var_names, expr_ref & new_def) override; }; defined_names::impl::impl(ast_manager & m, char const * prefix): m(m), m_exprs(m), m_names(m), m_apply_proofs(m) { if (prefix) m_z3name = prefix; } defined_names::impl::~impl() { } /** \brief Given an expression \c e that may contain free variables, return an application (sk x_1 ... x_n), where sk is a fresh variable name, and x_i's are the free variables of \c e. Store in var_sorts and var_names information about the free variables of \c e. This data is used to create an universal quantifier over the definition of the new name. */ app * defined_names::impl::gen_name(expr * e, sort_ref_buffer & var_sorts, buffer & var_names) { used_vars uv; uv(e); unsigned num_vars = uv.get_max_found_var_idx_plus_1(); ptr_buffer new_args; ptr_buffer domain; for (unsigned i = 0; i < num_vars; i++) { sort * s = uv.get(i); if (s) { domain.push_back(s); new_args.push_back(m.mk_var(i, s)); var_sorts.push_back(s); } else { var_sorts.push_back(m.mk_bool_sort()); // could be any sort. } var_names.push_back(symbol(i)); } sort * range = m.get_sort(e); func_decl * new_skolem_decl = m.mk_fresh_func_decl(m_z3name, symbol::null, domain.size(), domain.c_ptr(), range); app * n = m.mk_app(new_skolem_decl, new_args.size(), new_args.c_ptr()); if (is_lambda(e)) { m.add_lambda_def(new_skolem_decl, to_quantifier(e)); } return n; } /** \brief Cache \c n as a name for expression \c e. */ void defined_names::impl::cache_new_name(expr * e, app * n) { m_expr2name.insert(e, n); m_exprs.push_back(e); m_names.push_back(n); } /** \brief Cache \c pr as a proof that m_expr2name[e] is a name for expression \c e. */ void defined_names::impl::cache_new_name_intro_proof(expr * e, proof * pr) { SASSERT(m_expr2name.contains(e)); m_expr2proof.insert(e, pr); m_apply_proofs.push_back(pr); } /** \brief Given a definition conjunct \c def of the name \c name, store in \c result this definition. A quantifier is added around \c def_conjunct, if sorts and names are not empty. In this case, The application \c name is used as a pattern for the new quantifier. */ void defined_names::impl::bound_vars(sort_ref_buffer const & sorts, buffer const & names, expr * def_conjunct, app * name, expr_ref & result, symbol const& qid) { SASSERT(sorts.size() == names.size()); if (sorts.empty()) result = def_conjunct; else { expr * patterns[1] = { m.mk_pattern(name) }; quantifier_ref q(m); q = m.mk_forall(sorts.size(), sorts.c_ptr(), names.c_ptr(), def_conjunct, 1, qid, symbol::null, 1, patterns); TRACE("mk_definition_bug", tout << "before elim_unused_vars:\n" << mk_ismt2_pp(q, m) << "\n";); result = elim_unused_vars(m, q, params_ref()); TRACE("mk_definition_bug", tout << "after elim_unused_vars:\n" << result << "\n";); } } /** \brief Given a definition conjunct \c def of the name \c name, store in \c result this definition. A quantifier is added around \c def_conjunct, if sorts and names are not empty. In this case, The application \c name is used as a pattern for the new quantifier. */ void defined_names::impl::bound_vars(sort_ref_buffer const & sorts, buffer const & names, expr * def_conjunct, app * name, expr_ref_buffer & result, symbol const& qid) { expr_ref tmp(m); bound_vars(sorts, names, def_conjunct, name, tmp, qid); result.push_back(tmp); } #define MK_OR m.mk_or #define MK_NOT m.mk_not #define MK_EQ m.mk_eq void defined_names::impl::mk_definition(expr * e, app * n, sort_ref_buffer & var_sorts, buffer & var_names, expr_ref & new_def) { expr_ref_buffer defs(m); if (m.is_bool(e)) { bound_vars(var_sorts, var_names, MK_OR(MK_NOT(n), e), n, defs); bound_vars(var_sorts, var_names, MK_OR(n, MK_NOT(e)), n, defs); } else if (m.is_term_ite(e)) { bound_vars(var_sorts, var_names, MK_OR(MK_NOT(to_app(e)->get_arg(0)), MK_EQ(n, to_app(e)->get_arg(1))), n, defs); bound_vars(var_sorts, var_names, MK_OR(to_app(e)->get_arg(0), MK_EQ(n, to_app(e)->get_arg(2))), n, defs); } else if (is_lambda(e)) { // n(y) = \x . M[x,y] // => // n(y)[x] = M, forall x y // // NB. The pattern is incomplete. // consider store(a, i, v) == \lambda j . if i = j then v else a[j] // the instantiation rules for store(a, i, v) are: // store(a, i, v)[j] = if i = j then v else a[j] with patterns {a[j], store(a, i, v)} { store(a, i, v)[j] } // The first pattern is not included. // TBD use a model-based scheme for exracting instantiations instead of // using multi-patterns. // quantifier* q = to_quantifier(e); expr_ref_vector args(m); expr_ref n2(m), n3(m); var_shifter vs(m); vs(n, q->get_num_decls(), n2); args.push_back(n2); var_sorts.append(q->get_num_decls(), q->get_decl_sorts()); var_names.append(q->get_num_decls(), q->get_decl_names()); for (unsigned i = 0; i < q->get_num_decls(); ++i) { args.push_back(m.mk_var(q->get_num_decls() - i - 1, q->get_decl_sort(i))); } array_util autil(m); func_decl * f = nullptr; if (autil.is_as_array(n2, f)) { n3 = m.mk_app(f, args.size()-1, args.c_ptr() + 1); } else { n3 = autil.mk_select(args.size(), args.c_ptr()); } bound_vars(var_sorts, var_names, MK_EQ(q->get_expr(), n3), to_app(n3), defs, m.lambda_def_qid()); } else { bound_vars(var_sorts, var_names, MK_EQ(e, n), n, defs); } new_def = mk_and(m, defs.size(), defs.c_ptr()); } void defined_names::pos_impl::mk_definition(expr * e, app * n, sort_ref_buffer & var_sorts, buffer & var_names, expr_ref & new_def) { bound_vars(var_sorts, var_names, MK_OR(MK_NOT(n), e), n, new_def); } bool defined_names::impl::mk_name(expr * e, expr_ref & new_def, proof_ref & new_def_pr, app_ref & n, proof_ref & pr) { TRACE("mk_definition_bug", tout << "making name for:\n" << mk_ismt2_pp(e, m) << "\n";); app * n_ptr; if (m_expr2name.find(e, n_ptr)) { TRACE("mk_definition_bug", tout << "name for expression is already cached..., returning false...\n";); n = n_ptr; if (m.proofs_enabled()) { proof * pr_ptr = nullptr; m_expr2proof.find(e, pr_ptr); SASSERT(pr_ptr); pr = pr_ptr; } return false; } else { sort_ref_buffer var_sorts(m); buffer var_names; n = gen_name(e, var_sorts, var_names); cache_new_name(e, n); TRACE("mk_definition_bug", tout << "name: " << mk_ismt2_pp(n, m) << "\n";); // variables are in reverse order in quantifiers std::reverse(var_sorts.c_ptr(), var_sorts.c_ptr() + var_sorts.size()); std::reverse(var_names.c_ptr(), var_names.c_ptr() + var_names.size()); mk_definition(e, n, var_sorts, var_names, new_def); TRACE("mk_definition_bug", tout << "new_def:\n" << mk_ismt2_pp(new_def, m) << "\n";); if (m.proofs_enabled()) { new_def_pr = m.mk_def_intro(new_def); pr = m.mk_apply_def(e, n, new_def_pr); cache_new_name_intro_proof(e, pr); } return true; } } void defined_names::impl::push_scope() { SASSERT(m_exprs.size() == m_names.size()); m_lims.push_back(m_exprs.size()); } void defined_names::impl::pop_scope(unsigned num_scopes) { unsigned lvl = m_lims.size(); SASSERT(num_scopes <= lvl); unsigned new_lvl = lvl - num_scopes; unsigned old_sz = m_lims[new_lvl]; unsigned sz = m_exprs.size(); SASSERT(old_sz <= sz); SASSERT(sz == m_names.size()); while (old_sz != sz) { --sz; if (m.proofs_enabled()) { m_expr2proof.erase(m_exprs.back()); m_apply_proofs.pop_back(); } m_expr2name.erase(m_exprs.back()); m_exprs.pop_back(); m_names.pop_back(); } SASSERT(m_exprs.size() == old_sz); m_lims.shrink(new_lvl); } void defined_names::impl::reset() { m_expr2name.reset(); m_expr2proof.reset(); m_exprs.reset(); m_names.reset(); m_apply_proofs.reset(); m_lims.reset(); } defined_names::defined_names(ast_manager & m, char const * fresh_prefix) { m_impl = alloc(impl, m, fresh_prefix); m_pos_impl = alloc(pos_impl, m, fresh_prefix); } defined_names::~defined_names() { dealloc(m_impl); dealloc(m_pos_impl); } bool defined_names::mk_name(expr * e, expr_ref & new_def, proof_ref & new_def_pr, app_ref & n, proof_ref & pr) { return m_impl->mk_name(e, new_def, new_def_pr, n, pr); } bool defined_names::mk_pos_name(expr * e, expr_ref & new_def, proof_ref & new_def_pr, app_ref & n, proof_ref & pr) { return m_pos_impl->mk_name(e, new_def, new_def_pr, n, pr); } expr_ref defined_names::mk_definition(expr * e, app * n) { ast_manager& m = m_impl->m; sort_ref_buffer var_sorts(m); expr_ref new_def(m); buffer var_names; m_impl->mk_definition(e, n, var_sorts, var_names, new_def); return new_def; } void defined_names::push() { m_impl->push_scope(); m_pos_impl->push_scope(); } void defined_names::pop(unsigned num_scopes) { m_impl->pop_scope(num_scopes); m_pos_impl->pop_scope(num_scopes); } void defined_names::reset() { m_impl->reset(); m_pos_impl->reset(); } unsigned defined_names::get_num_names() const { return m_impl->get_num_names() + m_pos_impl->get_num_names(); } func_decl * defined_names::get_name_decl(unsigned i) const { SASSERT(i < get_num_names()); unsigned n1 = m_impl->get_num_names(); return i < n1 ? m_impl->get_name_decl(i) : m_pos_impl->get_name_decl(i - n1); } z3-z3-4.8.7/src/ast/normal_forms/defined_names.h000066400000000000000000000052011356505360400214330ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: defined_names.h Abstract: In some transformations, we need to name expressions. These expressions are stored in a table. Author: Leonardo de Moura (leonardo) 2008-01-14. Revision History: --*/ #ifndef DEFINED_NAMES_H_ #define DEFINED_NAMES_H_ #include "ast/ast.h" /** \brief Mapping from expressions to skolem functions that are used to name them. The mapping supports backtracking using the methods #push_scope and #pop_scope. */ class defined_names { struct impl; struct pos_impl; impl * m_impl; pos_impl * m_pos_impl; public: defined_names(ast_manager & m, char const * fresh_prefix = "z3name"); ~defined_names(); // ----------------------------------- // // High-level API // // ----------------------------------- /** \brief Create a name for expression \c e if it doesn't already exists. Return true if a new name was created, and false if a name already exists for \c e. The resultant new name is stored in n, and a [apply-def] proof that (= e n) is stored into pr. If true is returned, then the definition of the new name is stored into new_def, and a [def-intro] proof into new_def_pr. The proofs are not produced when proof generation is disabled. The definition of an expression e with name n is: - (and (or (not e) n) (or e (not n))) if e is an formula. - (and (or (not c) (= n t1)) (or c (= n t2))) if e is an if-then-else term of the form (ite c t1 t2) - (= n e) if e is a term. Remark: the definitions are closed with an universal quantifier if e contains free variables. */ bool mk_name(expr * e, expr_ref & new_def, proof_ref & new_def_pr, app_ref & n, proof_ref & pr); /** \brief Create a name for a positive occurrence of the expression \c e. Return true if a new pos-name was created, and false if a pos-name already exists for \c e. If true is returned, then the definition of the new name is stored into new_def. It has the form: (or (not n) e) Remark: the definitions are closed with an universal quantifier if e contains free variables. */ bool mk_pos_name(expr * e, expr_ref & new_def, proof_ref & new_def_pr, app_ref & n, proof_ref & pr); /** \brief Create a definition for 'n' using 'e'. */ expr_ref mk_definition(expr * e, app * n); void push(); void pop(unsigned num_scopes); void reset(); unsigned get_num_names() const; func_decl * get_name_decl(unsigned i) const; }; #endif /* DEFINED_NAMES_H_ */ z3-z3-4.8.7/src/ast/normal_forms/name_exprs.cpp000066400000000000000000000106341356505360400213540ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: name_exprs.h Abstract: Goodies for naming nested expressions. Author: Leonardo (leonardo) 2011-10-06 Notes: --*/ #include "ast/normal_forms/name_exprs.h" #include "ast/rewriter/rewriter_def.h" #include "ast/ast_smt2_pp.h" class name_exprs_core : public name_exprs { struct cfg : public default_rewriter_cfg { ast_manager & m; defined_names & m_defined_names; expr_predicate & m_pred; app_ref m_r; proof_ref m_pr; expr_ref_vector * m_def_exprs; proof_ref_vector * m_def_proofs; cfg(ast_manager & m, defined_names & n, expr_predicate & pred): m(m), m_defined_names(n), m_pred(pred), m_r(m), m_pr(m), m_def_exprs(nullptr), m_def_proofs(nullptr) { } void gen_name_for_expr(expr * n, expr * & t, proof * & t_pr) { expr_ref new_def(m); proof_ref new_def_pr(m); if (m_defined_names.mk_name(n, new_def, new_def_pr, m_r, m_pr)) { m_def_exprs->push_back(new_def); if (m.proofs_enabled()) m_def_proofs->push_back(new_def_pr); } t = m_r.get(); t_pr = m_pr.get(); } bool get_subst(expr * s, expr * & t, proof * & t_pr) { TRACE("name_exprs", tout << "get_subst:\n" << mk_ismt2_pp(s, m) << "\n";); if (m_pred(s)) { gen_name_for_expr(s, t, t_pr); return true; } return false; } }; typedef rewriter_tpl rw; cfg m_cfg; rw m_rw; public: name_exprs_core(ast_manager & m, defined_names & n, expr_predicate & pred): m_cfg(m, n, pred), m_rw(m, m.proofs_enabled(), m_cfg) { } ~name_exprs_core() override { } void operator()(expr * n, expr_ref_vector & new_defs, proof_ref_vector & new_def_proofs, expr_ref & r, proof_ref & p) override { m_cfg.m_def_exprs = &new_defs; m_cfg.m_def_proofs = &new_def_proofs; m_rw(n, r, p); TRACE("name_exprs", tout << mk_ismt2_pp(n, m_rw.m()) << "\n---->\n" << r << "\n";); } void reset() override { m_rw.reset(); } }; name_exprs * mk_expr_namer(ast_manager & m, defined_names & n, expr_predicate & pred) { return alloc(name_exprs_core, m, n, pred); } class name_quantifier_labels : public name_exprs_core { class pred : public expr_predicate { ast_manager & m; public: pred(ast_manager & m):m(m) {} bool operator()(expr * t) override { return is_quantifier(t) || m.is_label(t); } }; pred m_pred; public: name_quantifier_labels(ast_manager & m, defined_names & n): name_exprs_core(m, n, m_pred), m_pred(m) { } ~name_quantifier_labels() override { } }; name_exprs * mk_quantifier_label_namer(ast_manager & m, defined_names & n) { return alloc(name_quantifier_labels, m, n); } class name_nested_formulas : public name_exprs_core { struct pred : public expr_predicate { ast_manager & m; expr * m_root; pred(ast_manager & m):m(m), m_root(nullptr) {} bool operator()(expr * t) override { TRACE("name_exprs", tout << "name_nested_formulas::pred:\n" << mk_ismt2_pp(t, m) << "\n";); if (is_app(t)) return to_app(t)->get_family_id() == m.get_basic_family_id() && to_app(t)->get_num_args() > 0 && t != m_root; return m.is_label(t) || is_quantifier(t); } }; pred m_pred; public: name_nested_formulas(ast_manager & m, defined_names & n): name_exprs_core(m, n, m_pred), m_pred(m) { } ~name_nested_formulas() override { } void operator()(expr * n, expr_ref_vector & new_defs, proof_ref_vector & new_def_proofs, expr_ref & r, proof_ref & p) override { m_pred.m_root = n; TRACE("name_exprs", tout << "operator()\n";); name_exprs_core::operator()(n, new_defs, new_def_proofs, r, p); } }; name_exprs * mk_nested_formula_namer(ast_manager & m, defined_names & n) { return alloc(name_nested_formulas, m, n); } void del_name_exprs(name_exprs * functor) { dealloc(functor); } z3-z3-4.8.7/src/ast/normal_forms/name_exprs.h000066400000000000000000000032411356505360400210150ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: name_exprs.h Abstract: Goodies for naming nested expressions. Author: Leonardo (leonardo) 2011-10-06 Notes: --*/ #ifndef NAME_EXPRS_H_ #define NAME_EXPRS_H_ #include "ast/ast.h" #include "ast/normal_forms/defined_names.h" class expr_predicate { public: virtual bool operator()(expr * t) = 0; }; class name_exprs { public: virtual ~name_exprs() {} virtual void operator()(expr * n, // [IN] expression that contain the sub-expressions to be named expr_ref_vector & new_defs, // [OUT] new definitions proof_ref_vector & new_def_proofs, // [OUT] proofs of the new definitions expr_ref & r, // [OUT] resultant expression proof_ref & p // [OUT] proof for (iff n p) ) = 0; virtual void reset() = 0; }; /** \brief Create an expression "namer" that will create replace nested expressions that satisfy pred with new fresh declarations. */ name_exprs * mk_expr_namer(ast_manager & m, defined_names & n, expr_predicate & pred); /** \brief Create an expression "namer" that will replace quantifiers and labels with new fresh declarations. */ name_exprs * mk_quantifier_label_namer(ast_manager & m, defined_names & n); /** \brief Create an expression "namer" that will replace all nested formulas and term if-then-elses with fresh declarations. */ name_exprs * mk_nested_formula_namer(ast_manager & m, defined_names & n); void del_name_exprs(name_exprs * functor); #endif z3-z3-4.8.7/src/ast/normal_forms/nnf.cpp000066400000000000000000000751531356505360400200030ustar00rootroot00000000000000/*++ Copyright (c) 2007 Microsoft Corporation Module Name: nnf.cpp Abstract: Negation Normal Form & Skolemization Author: Leonardo (leonardo) 2008-01-11 Notes: Major revision on 2011-10-06 --*/ #include "util/warning.h" #include "ast/normal_forms/nnf.h" #include "ast/normal_forms/nnf_params.hpp" #include "ast/used_vars.h" #include "ast/well_sorted.h" #include "ast/act_cache.h" #include "ast/rewriter/var_subst.h" #include "ast/normal_forms/name_exprs.h" #include "ast/ast_smt2_pp.h" /** \brief NNF translation mode. The cheapest mode is NNF_SKOLEM, and the most expensive is NNF_FULL. */ enum nnf_mode { NNF_SKOLEM, /* A subformula is put into NNF only if it contains quantifiers or labels. The result of the transformation will be in skolem normal form. If a formula is too expensive to be put into NNF, then nested quantifiers and labels are renamed. This mode is sufficient when using E-matching. */ NNF_QUANT, /* A subformula is put into NNF if it contains quantifiers, labels, or is in the scope of a quantifier. The result of the transformation will be in skolem normal form, and the body of quantifiers will be in NNF. If a ground formula is too expensive to be put into NNF, then nested quantifiers and labels are renamed. This mode is sufficient when using Superposition Calculus. Remark: If the problem does not contain quantifiers, then NNF_QUANT is identical to NNF_SKOLEM. */ NNF_OPPORTUNISTIC, /* Similar to NNF_QUANT, but a subformula is also put into NNF, if it is cheap. Otherwise, the nested quantifiers and labels are renamed. */ NNF_FULL /* Everything is put into NNF. */ }; class skolemizer { typedef act_cache cache; ast_manager & m; symbol m_sk_hack; bool m_sk_hack_enabled; cache m_cache; cache m_cache_pr; bool m_proofs_enabled; void process(quantifier * q, expr_ref & r, proof_ref & p) { if (q->get_kind() == lambda_k) { TRACE("nnf", tout << expr_ref(q, m) << "\n";); r = q; p = nullptr; return; } used_vars uv; uv(q); SASSERT(is_well_sorted(m, q)); unsigned sz = uv.get_max_found_var_idx_plus_1(); ptr_buffer sorts; expr_ref_vector args(m); for (unsigned i = 0; i < sz; i++) { sort * s = uv.get(i); if (s != nullptr) { sorts.push_back(s); args.push_back(m.mk_var(i, s)); } } TRACE("skolemizer", tout << "skid: " << q->get_skid() << "\n";); expr_ref_vector substitution(m); unsigned num_decls = q->get_num_decls(); for (unsigned i = num_decls; i > 0; ) { --i; sort * r = q->get_decl_sort(i); func_decl * sk_decl = m.mk_fresh_func_decl(q->get_decl_name(i), q->get_skid(), sorts.size(), sorts.c_ptr(), r); app * sk = m.mk_app(sk_decl, args.size(), args.c_ptr()); substitution.push_back(sk); } // // (VAR 0) is in the first position of substitution. // (VAR num_decls-1) is in the last position. // for (unsigned i = 0; i < sz; i++) { sort * s = uv.get(i); if (s != nullptr) substitution.push_back(m.mk_var(i, s)); else substitution.push_back(nullptr); } // // (VAR num_decls) ... (VAR num_decls+sz-1) // are in positions num_decls .. num_decls+sz-1 // std::reverse(substitution.c_ptr(), substitution.c_ptr() + substitution.size()); // // (VAR 0) should be in the last position of substitution. // var_subst s(m); SASSERT(is_well_sorted(m, q->get_expr())); expr_ref tmp(m); expr * body = q->get_expr(); if (m_sk_hack_enabled) { unsigned num_patterns = q->get_num_patterns(); for (unsigned i = 0; i < num_patterns; ++i) { expr * p = q->get_pattern(i); if (is_sk_hack(p)) { expr * sk_hack = to_app(p)->get_arg(0); if (q->get_kind() == forall_k) // check whether is in negative/positive context. tmp = m.mk_or(body, m.mk_not(sk_hack)); // negative context else tmp = m.mk_and(body, sk_hack); // positive context body = tmp; } } } r = s(body, substitution.size(), substitution.c_ptr()); p = nullptr; if (m_proofs_enabled) { if (q->get_kind() == forall_k) p = m.mk_skolemization(m.mk_not(q), m.mk_not(r)); else p = m.mk_skolemization(q, r); } } public: skolemizer(ast_manager & m): m(m), m_sk_hack("sk_hack"), m_sk_hack_enabled(false), m_cache(m), m_cache_pr(m), m_proofs_enabled(m.proofs_enabled()) { } void set_sk_hack(bool f) { m_sk_hack_enabled = f; } void operator()(quantifier * q, expr_ref & r, proof_ref & p) { r = m_cache.find(q); if (r.get() != nullptr) { p = nullptr; if (m_proofs_enabled) p = static_cast(m_cache_pr.find(q)); } else { process(q, r, p); m_cache.insert(q, r); if (m_proofs_enabled) m_cache_pr.insert(q, p); } } bool is_sk_hack(expr * p) const { SASSERT(m.is_pattern(p)); if (to_app(p)->get_num_args() != 1) return false; expr * body = to_app(p)->get_arg(0); if (!is_app(body)) return false; func_decl * f = to_app(body)->get_decl(); if (!(f->get_name() == m_sk_hack && f->get_arity() == 1)) return false; if (!m.is_bool(body)) { warning_msg("sk_hack constant must return a Boolean"); return false; } return true; } }; typedef default_exception nnf_params_exception; typedef default_exception nnf_exception; struct nnf::imp { struct frame { expr_ref m_curr; unsigned m_i:28; unsigned m_pol:1; // pos/neg polarity unsigned m_in_q:1; // true if m_curr is nested in a quantifier unsigned m_new_child:1; unsigned m_cache_result:1; unsigned m_spos; // top of the result stack, when the frame was created. frame(expr_ref && n, bool pol, bool in_q, bool cache_res, unsigned spos): m_curr(std::move(n)), m_i(0), m_pol(pol), m_in_q(in_q), m_new_child(false), m_cache_result(cache_res), m_spos(spos) { } frame(frame && other): m_curr(std::move(other.m_curr)), m_i(other.m_i), m_pol(other.m_pol), m_in_q(other.m_in_q), m_new_child(other.m_new_child), m_cache_result(other.m_cache_result), m_spos(other.m_spos) { } }; // There are four caches: #define NEG_NQ_CIDX 0 // negative polarity and not nested in a quantifier #define POS_NQ_CIDX 1 // positive polarity and not nested in a quantifier #define NEG_Q_CIDX 2 // negative polarity and nested in a quantifier #define POS_Q_CIDX 3 // positive polarity and nested in a quantifier ast_manager & m; vector m_frame_stack; expr_ref_vector m_result_stack; typedef act_cache cache; cache * m_cache[4]; expr_ref_vector m_todo_defs; proof_ref_vector m_todo_proofs; // proof generation goodness ---- proof_ref_vector m_result_pr_stack; cache * m_cache_pr[4]; // ------------------------------ skolemizer m_skolemizer; // configuration ---------------- nnf_mode m_mode; bool m_ignore_labels; // ------------------------------ name_exprs * m_name_nested_formulas; name_exprs * m_name_quant; unsigned long long m_max_memory; // in bytes imp(ast_manager & m, defined_names & n, params_ref const & p): m(m), m_result_stack(m), m_todo_defs(m), m_todo_proofs(m), m_result_pr_stack(m), m_skolemizer(m) { updt_params(p); for (unsigned i = 0; i < 4; i++) { m_cache[i] = alloc(act_cache, m); if (proofs_enabled()) m_cache_pr[i] = alloc(act_cache, m); } m_name_nested_formulas = mk_nested_formula_namer(m, n); m_name_quant = mk_quantifier_label_namer(m, n); } bool proofs_enabled() const { return m.proofs_enabled(); } ~imp() { for (unsigned i = 0; i < 4; i++) { dealloc(m_cache[i]); if (proofs_enabled()) dealloc(m_cache_pr[i]); } del_name_exprs(m_name_nested_formulas); del_name_exprs(m_name_quant); } void updt_params(params_ref const & _p) { nnf_params p(_p); symbol mode_sym = p.mode(); if (mode_sym == "skolem") m_mode = NNF_SKOLEM; else if (mode_sym == "full") m_mode = NNF_FULL; else if (mode_sym == "quantifiers") m_mode = NNF_QUANT; else throw nnf_params_exception("invalid NNF mode"); TRACE("nnf", tout << "nnf-mode: " << m_mode << " " << mode_sym << "\n" << _p << "\n";); m_ignore_labels = p.ignore_labels(); m_max_memory = megabytes_to_bytes(p.max_memory()); m_skolemizer.set_sk_hack(p.sk_hack()); } static void get_param_descrs(param_descrs & r) { nnf_params::collect_param_descrs(r); } void reset() { m_frame_stack.reset(); m_result_stack.reset(); m_result_pr_stack.reset(); m_todo_defs.reset(); m_todo_proofs.reset(); } void reset_cache() { for (unsigned i = 0; i < 4; i++) { m_cache[i]->reset(); if (proofs_enabled()) m_cache_pr[i]->reset(); } } void push_frame(expr * t, bool pol, bool in_q, bool cache_res) { m_frame_stack.push_back(frame(expr_ref(t, m), pol, in_q, cache_res, m_result_stack.size())); } static unsigned get_cache_idx(bool pol, bool in_q) { return static_cast(in_q) * 2 + static_cast(pol); } void cache_result(expr * t, bool pol, bool in_q, expr * v, proof * pr) { unsigned idx = get_cache_idx(pol, in_q); m_cache[idx]->insert(t, v); if (proofs_enabled()) m_cache_pr[idx]->insert(t, pr); } expr * get_cached(expr * t, bool pol, bool in_q) const { return m_cache[get_cache_idx(pol, in_q)]->find(t); } proof * get_cached_pr(expr * t, bool pol, bool in_q) const { SASSERT(proofs_enabled()); return static_cast(m_cache_pr[get_cache_idx(pol, in_q)]->find(t)); } /** \brief Return true if the result for (t, pol, in_q) is already cached, and store the result on the stack. */ bool process_cached(expr * t, bool pol, bool in_q) { expr * r = get_cached(t, pol, in_q); if (r) { m_result_stack.push_back(r); if (proofs_enabled()) { proof * pr = get_cached_pr(t, pol, in_q); m_result_pr_stack.push_back(pr); SASSERT(m_result_stack.size() == m_result_pr_stack.size()); } m_frame_stack.pop_back(); set_new_child_flag(t, r); return true; } return false; } void checkpoint() { if (memory::get_allocation_size() > m_max_memory) throw nnf_exception(Z3_MAX_MEMORY_MSG); if (m.canceled()) throw nnf_exception(m.limit().get_cancel_msg()); } void set_new_child_flag() { if (!m_frame_stack.empty()) m_frame_stack.back().m_new_child = true; } void set_new_child_flag(expr * old_t, expr * new_t) { if (old_t != new_t) set_new_child_flag(); } void skip(expr * t, bool pol) { expr * r = pol ? t : m.mk_not(t); m_result_stack.push_back(r); if (proofs_enabled()) { m_result_pr_stack.push_back(m.mk_oeq_reflexivity(r)); SASSERT(m_result_stack.size() == m_result_pr_stack.size()); } } bool visit(expr * t, bool pol, bool in_q) { SASSERT(m.is_bool(t)); if (m_mode == NNF_SKOLEM || (m_mode == NNF_QUANT && !in_q)) { if (!has_quantifiers(t) && !has_labels(t)) { skip(t, pol); return true; // t does not need to be processed } } bool cache_res = t->get_ref_count() > 1; if (cache_res) { expr * r = get_cached(t, pol, in_q); if (r) { m_result_stack.push_back(r); set_new_child_flag(t, r); if (proofs_enabled()) { proof * pr = get_cached_pr(t, pol, in_q); m_result_pr_stack.push_back(pr); SASSERT(m_result_stack.size() == m_result_pr_stack.size()); } return true; // t was already processed } } switch (t->get_kind()) { case AST_APP: if (to_app(t)->get_num_args() == 0) { skip(t, pol); return true; } else { push_frame(t, pol, in_q, cache_res); return false; } case AST_QUANTIFIER: push_frame(t, pol, in_q, cache_res); return false; case AST_VAR: skip(t, pol); return true; default: UNREACHABLE(); return true; } } proof * mk_proof(bool pol, unsigned num_parents, proof * const * parents, app * old_e, app * new_e) { if (pol) { if (old_e->get_decl() == new_e->get_decl()) return m.mk_oeq_congruence(old_e, new_e, num_parents, parents); else return m.mk_nnf_pos(old_e, new_e, num_parents, parents); } else return m.mk_nnf_neg(old_e, new_e, num_parents, parents); } bool process_and_or(app * t, frame & fr) { unsigned num_args = t->get_num_args(); while (fr.m_i < num_args) { expr * arg = t->get_arg(fr.m_i); fr.m_i++; if (!visit(arg, fr.m_pol, fr.m_in_q)) return false; } app * r; if (m.is_and(t) == fr.m_pol) r = m.mk_and(t->get_num_args(), m_result_stack.c_ptr() + fr.m_spos); else r = m.mk_or(t->get_num_args(), m_result_stack.c_ptr() + fr.m_spos); m_result_stack.shrink(fr.m_spos); m_result_stack.push_back(r); if (proofs_enabled()) { proof * pr = mk_proof(fr.m_pol, t->get_num_args(), m_result_pr_stack.c_ptr() + fr.m_spos, t, r); m_result_pr_stack.shrink(fr.m_spos); m_result_pr_stack.push_back(pr); SASSERT(m_result_stack.size() == m_result_pr_stack.size()); } return true; } bool process_not(app * t, frame & fr) { if (fr.m_i == 0) { fr.m_i = 1; if (!visit(t->get_arg(0), !fr.m_pol, fr.m_in_q)) return false; } expr * r = m_result_stack.back(); proof * pr = nullptr; if (proofs_enabled()) { pr = m_result_pr_stack.back(); if (!fr.m_pol) { pr = m.mk_nnf_neg(t, r, 1, &pr); m_result_pr_stack.pop_back(); m_result_pr_stack.push_back(pr); SASSERT(m_result_stack.size() == m_result_pr_stack.size()); } } return true; } bool process_implies(app * t, frame & fr) { SASSERT(t->get_num_args() == 2); switch (fr.m_i) { case 0: fr.m_i = 1; if (!visit(t->get_arg(0), !fr.m_pol, fr.m_in_q)) return false; case 1: fr.m_i = 2; if (!visit(t->get_arg(1), fr.m_pol, fr.m_in_q)) return false; default: break; } app * r; if (fr.m_pol) r = m.mk_or(2, m_result_stack.c_ptr() + fr.m_spos); else r = m.mk_and(2, m_result_stack.c_ptr() + fr.m_spos); m_result_stack.shrink(fr.m_spos); m_result_stack.push_back(r); if (proofs_enabled()) { proof * pr = mk_proof(fr.m_pol, 2, m_result_pr_stack.c_ptr() + fr.m_spos, t, r); m_result_pr_stack.shrink(fr.m_spos); m_result_pr_stack.push_back(pr); SASSERT(m_result_stack.size() == m_result_pr_stack.size()); } return true; } bool process_ite(app * t, frame & fr) { SASSERT(t->get_num_args() == 3); switch (fr.m_i) { case 0: fr.m_i = 1; if (!visit(t->get_arg(0), true, fr.m_in_q)) return false; case 1: fr.m_i = 2; if (!visit(t->get_arg(0), false, fr.m_in_q)) return false; case 2: fr.m_i = 3; if (!visit(t->get_arg(1), fr.m_pol, fr.m_in_q)) return false; case 3: fr.m_i = 4; if (!visit(t->get_arg(2), fr.m_pol, fr.m_in_q)) return false; default: break; } expr * const * rs = m_result_stack.c_ptr() + fr.m_spos; expr * _cond = rs[0]; expr * _not_cond = rs[1]; expr * _then = rs[2]; expr * _else = rs[3]; app * r = m.mk_and(m.mk_or(_not_cond, _then), m.mk_or(_cond, _else)); m_result_stack.shrink(fr.m_spos); m_result_stack.push_back(r); if (proofs_enabled()) { proof * pr = mk_proof(fr.m_pol, 4, m_result_pr_stack.c_ptr() + fr.m_spos, t, r); m_result_pr_stack.shrink(fr.m_spos); m_result_pr_stack.push_back(pr); SASSERT(m_result_stack.size() == m_result_pr_stack.size()); } return true; } bool is_eq(app * t) const { return m.is_eq(t); } bool process_iff_xor(app * t, frame & fr) { SASSERT(t->get_num_args() == 2); switch (fr.m_i) { case 0: fr.m_i = 1; if (!visit(t->get_arg(0), true, fr.m_in_q)) return false; case 1: fr.m_i = 2; if (!visit(t->get_arg(0), false, fr.m_in_q)) return false; case 2: fr.m_i = 3; if (!visit(t->get_arg(1), true, fr.m_in_q)) return false; case 3: fr.m_i = 4; if (!visit(t->get_arg(1), false, fr.m_in_q)) return false; default: break; } expr * const * rs = m_result_stack.c_ptr() + fr.m_spos; expr * lhs = rs[0]; expr * not_lhs = rs[1]; expr * rhs = rs[2]; expr * not_rhs = rs[3]; app * r; if (is_eq(t) == fr.m_pol) r = m.mk_and(m.mk_or(not_lhs, rhs), m.mk_or(lhs, not_rhs)); else r = m.mk_and(m.mk_or(lhs, rhs), m.mk_or(not_lhs, not_rhs)); m_result_stack.shrink(fr.m_spos); m_result_stack.push_back(r); if (proofs_enabled()) { proof * pr = mk_proof(fr.m_pol, 4, m_result_pr_stack.c_ptr() + fr.m_spos, t, r); m_result_pr_stack.shrink(fr.m_spos); m_result_pr_stack.push_back(pr); SASSERT(m_result_stack.size() == m_result_pr_stack.size()); } return true; } bool process_eq(app * t, frame & fr) { if (m.is_bool(t->get_arg(0))) return process_iff_xor(t, fr); else return process_default(t, fr); } bool process_default(app * t, frame & fr) { SASSERT(fr.m_i == 0); if (m_mode == NNF_FULL || t->has_quantifiers() || t->has_labels()) { expr_ref n2(m); proof_ref pr2(m); if (m_mode == NNF_FULL || (m_mode != NNF_SKOLEM && fr.m_in_q)) m_name_nested_formulas->operator()(t, m_todo_defs, m_todo_proofs, n2, pr2); else m_name_quant->operator()(t, m_todo_defs, m_todo_proofs, n2, pr2); if (!fr.m_pol) n2 = m.mk_not(n2); m_result_stack.push_back(n2); if (proofs_enabled()) { if (!fr.m_pol) { proof * prs[1] = { pr2 }; pr2 = m.mk_oeq_congruence(m.mk_not(t), static_cast(n2.get()), 1, prs); } m_result_pr_stack.push_back(pr2); SASSERT(m_result_stack.size() == m_result_pr_stack.size()); } } else { skip(t, fr.m_pol); } return true; } bool process_label(app * t, frame & fr) { if (fr.m_i == 0) { fr.m_i = 1; if (!visit(t->get_arg(0), fr.m_pol, fr.m_in_q)) return false; } expr * arg = m_result_stack.back(); proof * arg_pr = proofs_enabled() ? m_result_pr_stack.back() : nullptr; if (m_ignore_labels && !proofs_enabled()) return true; // the result is already on the stack buffer names; bool pos; m.is_label(t, pos, names); expr_ref r(m); proof_ref pr(m); if (fr.m_pol == pos) { expr * lbl_lit = m.mk_label_lit(names.size(), names.c_ptr()); r = m.mk_and(arg, lbl_lit); if (proofs_enabled()) { expr_ref aux(m); aux = m.mk_label(true, names.size(), names.c_ptr(), arg); pr = m.mk_transitivity(mk_proof(fr.m_pol, 1, &arg_pr, t, to_app(aux)), m.mk_iff_oeq(m.mk_rewrite(aux, r))); } } else { r = arg; if (proofs_enabled()) { proof * p1 = m.mk_iff_oeq(m.mk_rewrite(t, t->get_arg(0))); pr = m.mk_transitivity(p1, arg_pr); } } m_result_stack.pop_back(); m_result_stack.push_back(r); if (proofs_enabled()) { m_result_pr_stack.pop_back(); m_result_pr_stack.push_back(pr); SASSERT(m_result_stack.size() == m_result_pr_stack.size()); } return true; } bool process_app(app * t, frame & fr) { TRACE("nnf", tout << mk_ismt2_pp(t, m) << "\n";); SASSERT(m.is_bool(t)); if (t->get_family_id() == m.get_basic_family_id()) { switch (static_cast(t->get_decl_kind())) { case OP_AND: case OP_OR: return process_and_or(t, fr); case OP_NOT: return process_not(t, fr); case OP_IMPLIES: return process_implies(t, fr); case OP_ITE: return process_ite(t, fr); case OP_XOR: return process_iff_xor(t, fr); case OP_EQ: return process_eq(t, fr); default: break; } } if (m.is_label(t)) { return process_label(t, fr); } return process_default(t, fr); } bool process_var(var * v, frame & fr) { skip(v, fr.m_pol); return true; } bool process_quantifier(quantifier * q, frame & fr) { TRACE("nnf", tout << expr_ref(q, m) << "\n";); expr_ref r(m); proof_ref pr(m); if (fr.m_i == 0) { fr.m_i = 1; if (is_lambda(q)) { // skip } else if (is_forall(q) == fr.m_pol) { if (!visit(q->get_expr(), fr.m_pol, true)) return false; } else { m_skolemizer(q, r, pr); if (!visit(r, !is_forall(q), fr.m_in_q)) return false; } } if (is_lambda(q)) { m_result_stack.push_back(q); if (proofs_enabled()) { m_result_pr_stack.push_back(nullptr); SASSERT(m_result_stack.size() == m_result_pr_stack.size()); } return true; } else if (is_forall(q) == fr.m_pol) { expr * new_expr = m_result_stack.back(); proof * new_expr_pr = proofs_enabled() ? m_result_pr_stack.back() : nullptr; ptr_buffer new_patterns; if (is_forall(q) == fr.m_pol) { // collect non sk_hack patterns unsigned num_patterns = q->get_num_patterns(); for (unsigned i = 0; i < num_patterns; i++) { expr * pat = q->get_pattern(i); if (!m_skolemizer.is_sk_hack(pat)) new_patterns.push_back(pat); } } else { // New quantifier has existential force. // So, ignore patterns } quantifier * new_q = nullptr; proof * new_q_pr = nullptr; if (fr.m_pol) { new_q = m.update_quantifier(q, new_patterns.size(), new_patterns.c_ptr(), new_expr); if (proofs_enabled()) { new_expr_pr = m.mk_bind_proof(q, new_expr_pr); new_q_pr = m.mk_nnf_pos(q, new_q, 1, &new_expr_pr); } } else { quantifier_kind k = is_forall(q)? exists_k : forall_k; new_q = m.update_quantifier(q, k, new_patterns.size(), new_patterns.c_ptr(), new_expr); if (proofs_enabled()) { new_expr_pr = m.mk_bind_proof(q, new_expr_pr); new_q_pr = m.mk_nnf_neg(q, new_q, 1, &new_expr_pr); } } m_result_stack.pop_back(); m_result_stack.push_back(new_q); if (proofs_enabled()) { m_result_pr_stack.pop_back(); m_result_pr_stack.push_back(new_q_pr); SASSERT(m_result_stack.size() == m_result_pr_stack.size()); } } else { // Quantifier was skolemized. // The result is already on the stack. // However, the proof must be updated if (proofs_enabled()) { m_skolemizer(q, r, pr); // retrieve the proof pr = m.mk_transitivity(pr, m_result_pr_stack.back()); m_result_pr_stack.pop_back(); m_result_pr_stack.push_back(pr); SASSERT(m_result_stack.size() == m_result_pr_stack.size()); } } return true; } void recover_result(expr * t, expr_ref & result, proof_ref & result_pr) { // recover result from the top of the stack. result = m_result_stack.back(); m_result_stack.pop_back(); SASSERT(m_result_stack.empty()); if (proofs_enabled()) { result_pr = m_result_pr_stack.back(); m_result_pr_stack.pop_back(); if (result_pr.get() == nullptr) result_pr = m.mk_reflexivity(t); SASSERT(m_result_pr_stack.empty()); } } void process(expr * t, expr_ref & result, proof_ref & result_pr) { TRACE("nnf", tout << "processing:\n" << mk_ismt2_pp(t, m) << "\n";); SASSERT(m.is_bool(t)); if (visit(t, true /* positive polarity */, false /* not nested in quantifier */)) { recover_result(t, result, result_pr); return; } SASSERT(!m_frame_stack.empty()); while (!m_frame_stack.empty()) { checkpoint(); frame & fr = m_frame_stack.back(); expr * t = fr.m_curr; if (fr.m_i == 0 && t->get_ref_count() > 1 && process_cached(t, fr.m_pol, fr.m_in_q)) continue; bool status; switch (t->get_kind()) { case AST_APP: status = process_app(to_app(t), fr); break; case AST_QUANTIFIER: status = process_quantifier(to_quantifier(t), fr); break; case AST_VAR: status = process_var(to_var(t), fr); break; default: UNREACHABLE(); status = true; break; } if (status) { if (fr.m_cache_result) cache_result(fr.m_curr, fr.m_pol, fr.m_in_q, m_result_stack.back(), proofs_enabled() ? m_result_pr_stack.back() : nullptr); m_frame_stack.pop_back(); } } recover_result(t, result, result_pr); } void operator()(expr * n, expr_ref_vector & new_defs, proof_ref_vector & new_def_proofs, expr_ref & r, proof_ref & pr) { reset(); process(n, r, pr); unsigned old_sz1 = new_defs.size(); unsigned old_sz2 = new_def_proofs.size(); for (unsigned i = 0; i < m_todo_defs.size(); i++) { expr_ref dr(m); proof_ref dpr(m); process(m_todo_defs.get(i), dr, dpr); new_defs.push_back(dr); if (proofs_enabled()) { proof * new_pr = m.mk_modus_ponens(m_todo_proofs.get(i), dpr); new_def_proofs.push_back(new_pr); } } std::reverse(new_defs.c_ptr() + old_sz1, new_defs.c_ptr() + new_defs.size()); std::reverse(new_def_proofs.c_ptr() + old_sz2, new_def_proofs.c_ptr() + new_def_proofs.size()); } }; nnf::nnf(ast_manager & m, defined_names & n, params_ref const & p) { TRACE("nnf", tout << "nnf constructor: " << p << "\n";); m_imp = alloc(imp, m, n, p); } nnf::~nnf() { dealloc(m_imp); } void nnf::operator()(expr * n, expr_ref_vector & new_defs, proof_ref_vector & new_def_proofs, expr_ref & r, proof_ref & p) { m_imp->operator()(n, new_defs, new_def_proofs, r, p); TRACE("nnf_result", tout << expr_ref(n, r.get_manager()) << "\nNNF result:\n" << new_defs << "\n" << r << "\n";); } void nnf::updt_params(params_ref const & p) { m_imp->updt_params(p); } void nnf::get_param_descrs(param_descrs & r) { imp::get_param_descrs(r); } void nnf::reset() { m_imp->reset(); } void nnf::reset_cache() { m_imp->reset_cache(); } z3-z3-4.8.7/src/ast/normal_forms/nnf.h000066400000000000000000000022501356505360400174340ustar00rootroot00000000000000/*++ Copyright (c) 2007 Microsoft Corporation Module Name: nnf.h Abstract: Negation Normal Form & Skolemization Author: Leonardo (leonardo) 2008-01-11 Notes: Major revision on 2011-10-06 --*/ #ifndef NNF_H_ #define NNF_H_ #include "ast/ast.h" #include "util/params.h" #include "ast/normal_forms/defined_names.h" class nnf { struct imp; imp * m_imp; public: nnf(ast_manager & m, defined_names & n, params_ref const & p = params_ref()); ~nnf(); void operator()(expr * n, // [IN] expression that should be put into NNF expr_ref_vector & new_defs, // [OUT] new definitions proof_ref_vector & new_def_proofs, // [OUT] proofs of the new definitions expr_ref & r, // [OUT] resultant expression proof_ref & p // [OUT] proof for (~ n r) ); void updt_params(params_ref const & p); /* REG_MODULE_PARAMS('nnf', 'nnf::get_param_descrs') */ static void get_param_descrs(param_descrs & r); void reset(); void reset_cache(); }; #endif /* NNF_H_ */ z3-z3-4.8.7/src/ast/normal_forms/nnf_params.pyg000066400000000000000000000010731356505360400213510ustar00rootroot00000000000000def_module_params('nnf', description='negation normal form', export=True, params=(max_memory_param(), ('sk_hack', BOOL, False, 'hack for VCC'), ('mode', SYMBOL, 'skolem', 'NNF translation mode: skolem (skolem normal form), quantifiers (skolem normal form + quantifiers in NNF), full'), ('ignore_labels', BOOL, False, 'remove/ignore labels in the input formula, this option is ignored if proofs are enabled'))) z3-z3-4.8.7/src/ast/normal_forms/pull_quant.cpp000066400000000000000000000362731356505360400214060ustar00rootroot00000000000000/*++ Copyright (c) 2007 Microsoft Corporation Module Name: pull_quant.cpp Abstract: Pull nested quantifiers. Author: Leonardo (leonardo) 2008-01-20 Notes: --*/ #include "ast/normal_forms/pull_quant.h" #include "ast/rewriter/var_subst.h" #include "ast/rewriter/rewriter_def.h" #include "ast/ast_pp.h" struct pull_quant::imp { struct rw_cfg : public default_rewriter_cfg { ast_manager & m; shift_vars m_shift; rw_cfg(ast_manager & m): m(m), m_shift(m) { } bool pull_quant1_core(func_decl * d, unsigned num_children, expr * const * children, expr_ref & result) { ptr_buffer var_sorts; buffer var_names; symbol qid; int w = INT_MAX; // The input formula is in Skolem normal form... // So all children are forall (positive context) or exists (negative context). // Remark: (AND a1 ...) may be represented (NOT (OR (NOT a1) ...))) // So, when pulling a quantifier over a NOT, it becomes an exists. if (m.is_not(d)) { SASSERT(num_children == 1); expr * child = children[0]; if (is_quantifier(child)) { quantifier * q = to_quantifier(child); expr * body = q->get_expr(); quantifier_kind k = q->get_kind() == forall_k ? exists_k : forall_k; result = m.update_quantifier(q, k, m.mk_not(body)); return true; } else { return false; } } bool found_quantifier = false; bool forall_children = false; for (unsigned i = 0; i < num_children; i++) { expr * child = children[i]; if (is_quantifier(child)) { if (!found_quantifier) { found_quantifier = true; forall_children = is_forall(child); } else { // Since the initial formula was in SNF, all children must be EXISTS or FORALL. SASSERT(forall_children == is_forall(child)); } quantifier * nested_q = to_quantifier(child); if (var_sorts.empty()) { // use the qid of one of the nested quantifiers. qid = nested_q->get_qid(); } w = std::min(w, nested_q->get_weight()); unsigned j = nested_q->get_num_decls(); while (j > 0) { --j; var_sorts.push_back(nested_q->get_decl_sort(j)); symbol s = nested_q->get_decl_name(j); if (std::find(var_names.begin(), var_names.end(), s) != var_names.end()) var_names.push_back(m.mk_fresh_var_name(s.is_numerical() ? nullptr : s.bare_str())); else var_names.push_back(s); } } } if (!var_sorts.empty()) { SASSERT(found_quantifier); // adjust the variable ids in formulas in new_children expr_ref_buffer new_adjusted_children(m); expr_ref adjusted_child(m); unsigned num_decls = var_sorts.size(); unsigned shift_amount = 0; TRACE("pull_quant", tout << "Result num decls:" << num_decls << "\n";); for (unsigned i = 0; i < num_children; i++) { expr * child = children[i]; if (!is_quantifier(child)) { // increment the free variables in child by num_decls because // child will be in the scope of num_decls bound variables. m_shift(child, num_decls, adjusted_child); TRACE("pull_quant", tout << "shifted by: " << num_decls << "\n" << mk_pp(child, m) << "\n---->\n" << mk_pp(adjusted_child, m) << "\n";); } else { quantifier * nested_q = to_quantifier(child); SASSERT(num_decls >= nested_q->get_num_decls()); // Assume nested_q is of the form // forall xs. P(xs, ys) // where xs (ys) represents the set of bound (free) variables. // // - the index of the variables xs must be increased by shift_amount. // That is, the number of new bound variables that will precede the bound // variables xs. // // - the index of the variables ys must be increased by num_decls - nested_q->get_num_decls. // That is, the total number of new bound variables that will be in the scope // of nested_q->get_expr(). m_shift(nested_q->get_expr(), nested_q->get_num_decls(), // bound for shift1/shift2 num_decls - nested_q->get_num_decls(), // shift1 (shift by this amount if var idx >= bound) shift_amount, // shift2 (shift by this amount if var idx < bound) adjusted_child); TRACE("pull_quant", tout << "shifted bound: " << nested_q->get_num_decls() << " shift1: " << shift_amount << " shift2: " << (num_decls - nested_q->get_num_decls()) << "\n" << mk_pp(nested_q->get_expr(), m) << "\n---->\n" << mk_pp(adjusted_child, m) << "\n";); shift_amount += nested_q->get_num_decls(); } new_adjusted_children.push_back(adjusted_child); } // Remark: patterns are ignored. // This is ok, since this functor is used in one of the following cases: // // 1) Superposition calculus is being used, so the // patterns are useless. // // 2) No patterns were provided, and the functor is used // to increase the effectiveness of the pattern inference // procedure. // // 3) MBQI std::reverse(var_sorts.begin(), var_sorts.end()); std::reverse(var_names.begin(), var_names.end()); result = m.mk_quantifier(forall_children ? forall_k : exists_k, var_sorts.size(), var_sorts.c_ptr(), var_names.c_ptr(), m.mk_app(d, new_adjusted_children.size(), new_adjusted_children.c_ptr()), w, qid); return true; } else { SASSERT(!found_quantifier); return false; } } void pull_quant1(func_decl * d, unsigned num_children, expr * const * children, expr_ref & result) { if (!pull_quant1_core(d, num_children, children, result)) { result = m.mk_app(d, num_children, children); } } void pull_quant1_core(quantifier * q, expr * new_expr, expr_ref & result) { // The original formula was in SNF, so the original quantifiers must be universal. SASSERT(is_forall(q)); SASSERT(is_forall(new_expr)); quantifier * nested_q = to_quantifier(new_expr); ptr_buffer var_sorts; buffer var_names; var_sorts.append(q->get_num_decls(), const_cast(q->get_decl_sorts())); var_sorts.append(nested_q->get_num_decls(), const_cast(nested_q->get_decl_sorts())); var_names.append(q->get_num_decls(), const_cast(q->get_decl_names())); var_names.append(nested_q->get_num_decls(), const_cast(nested_q->get_decl_names())); // Remark: patterns are ignored. // See comment in reduce1_app result = m.mk_forall(var_sorts.size(), var_sorts.c_ptr(), var_names.c_ptr(), nested_q->get_expr(), std::min(q->get_weight(), nested_q->get_weight()), q->get_qid()); } void pull_quant1(quantifier * q, expr * new_expr, expr_ref & result) { // The original formula was in SNF, so the original quantifiers must be universal. SASSERT(is_forall(q)); if (is_forall(new_expr)) { pull_quant1_core(q, new_expr, result); } else { SASSERT(!is_quantifier(new_expr)); result = m.update_quantifier(q, new_expr); } } void pull_quant1(expr * n, expr_ref & result) { if (is_app(n)) pull_quant1(to_app(n)->get_decl(), to_app(n)->get_num_args(), to_app(n)->get_args(), result); else if (is_quantifier(n)) pull_quant1(to_quantifier(n), to_quantifier(n)->get_expr(), result); else result = n; } // Code for proof generation... void pull_quant2(expr * n, expr_ref & r, proof_ref & pr) { pr = nullptr; if (is_app(n)) { expr_ref_buffer new_args(m); expr_ref new_arg(m); ptr_buffer proofs; for (expr * arg : *to_app(n)) { pull_quant1(arg , new_arg); new_args.push_back(new_arg); if (new_arg != arg) proofs.push_back(m.mk_pull_quant(arg, to_quantifier(new_arg))); } pull_quant1(to_app(n)->get_decl(), new_args.size(), new_args.c_ptr(), r); if (m.proofs_enabled()) { app * r1 = m.mk_app(to_app(n)->get_decl(), new_args.size(), new_args.c_ptr()); proof * p1 = proofs.empty() ? nullptr : m.mk_congruence(to_app(n), r1, proofs.size(), proofs.c_ptr()); proof * p2 = r1 == r ? nullptr : m.mk_pull_quant(r1, to_quantifier(r)); pr = m.mk_transitivity(p1, p2); } } else if (is_quantifier(n)) { expr_ref new_expr(m); pull_quant1(to_quantifier(n)->get_expr(), new_expr); pull_quant1(to_quantifier(n), new_expr, r); if (m.proofs_enabled()) { quantifier * q1 = m.update_quantifier(to_quantifier(n), new_expr); proof * p1 = nullptr; if (n != q1) { proof * p0 = m.mk_pull_quant(n, to_quantifier(new_expr)); p1 = m.mk_quant_intro(to_quantifier(n), q1, p0); } proof * p2 = q1 == r ? nullptr : m.mk_pull_quant(q1, to_quantifier(r)); pr = m.mk_transitivity(p1, p2); } } else { r = n; } } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { if (!m.is_or(f) && !m.is_and(f) && !m.is_not(f)) return BR_FAILED; if (!pull_quant1_core(f, num, args, result)) return BR_FAILED; if (m.proofs_enabled()) { result_pr = m.mk_pull_quant(m.mk_app(f, num, args), to_quantifier(result.get())); } return BR_DONE; } 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) { if (is_exists(old_q)) { result = m.mk_not(new_body); result = m.mk_not(m.update_quantifier(old_q, exists_k, result)); if (m.proofs_enabled()) m.mk_rewrite(old_q, result); return true; } if (is_lambda(old_q)) { return false; } if (!is_forall(new_body)) return false; pull_quant1_core(old_q, new_body, result); if (m.proofs_enabled()) result_pr = m.mk_pull_quant(old_q, to_quantifier(result.get())); return true; } }; struct rw : public rewriter_tpl { rw_cfg m_cfg; rw(ast_manager & m): rewriter_tpl(m, m.proofs_enabled(), m_cfg), m_cfg(m) { } }; rw m_rw; imp(ast_manager & m): m_rw(m) { } void operator()(expr * n, expr_ref & r, proof_ref & p) { m_rw(n, r, p); } }; pull_quant::pull_quant(ast_manager & m) { m_imp = alloc(imp, m); } pull_quant::~pull_quant() { dealloc(m_imp); } void pull_quant::operator()(expr * n, expr_ref & r, proof_ref & p) { (*m_imp)(n, r, p); } void pull_quant::reset() { m_imp->m_rw.reset(); } void pull_quant::pull_quant2(expr * n, expr_ref & r, proof_ref & pr) { m_imp->m_rw.cfg().pull_quant2(n, r, pr); } struct pull_nested_quant::imp { struct rw_cfg : public default_rewriter_cfg { pull_quant m_pull; expr_ref m_r; proof_ref m_pr; rw_cfg(ast_manager & m):m_pull(m), m_r(m), m_pr(m) {} bool get_subst(expr * s, expr * & t, proof * & t_pr) { if (!is_quantifier(s)) return false; m_pull(to_quantifier(s), m_r, m_pr); t = m_r.get(); t_pr = m_pr.get(); return true; } }; struct rw : public rewriter_tpl { rw_cfg m_cfg; rw(ast_manager & m): rewriter_tpl(m, m.proofs_enabled(), m_cfg), m_cfg(m) { } }; rw m_rw; imp(ast_manager & m): m_rw(m) { } void operator()(expr * n, expr_ref & r, proof_ref & p) { m_rw(n, r, p); } }; pull_nested_quant::pull_nested_quant(ast_manager & m) { m_imp = alloc(imp, m); } pull_nested_quant::~pull_nested_quant() { dealloc(m_imp); } void pull_nested_quant::operator()(expr * n, expr_ref & r, proof_ref & p) { (*m_imp)(n, r, p); } void pull_nested_quant::reset() { m_imp->m_rw.reset(); } z3-z3-4.8.7/src/ast/normal_forms/pull_quant.h000066400000000000000000000021761356505360400210460ustar00rootroot00000000000000/*++ Copyright (c) 2007 Microsoft Corporation Module Name: pull_quant.h Abstract: Pull nested quantifiers. Author: Leonardo (leonardo) 2008-01-20 Notes: --*/ #ifndef PULL_QUANT_H_ #define PULL_QUANT_H_ #include "ast/ast.h" /** \brief Pull nested quantifiers in a formula. \warning It assumes the input formula is in NNF. \remark pull_quant(F) is a quantifier if F contains a quantifier. \remark If pull_quant(F) is a quantifier then its weight is Min{weight(Q') | Q' is a quantifier nested in F} */ class pull_quant { struct imp; imp * m_imp; public: pull_quant(ast_manager & m); ~pull_quant(); void operator()(expr * n, expr_ref & r, proof_ref & p); void reset(); void pull_quant2(expr * n, expr_ref & r, proof_ref & pr); }; /** \brief After applying this transformation the formula will not contain nested quantifiers. */ class pull_nested_quant { struct imp; imp * m_imp; public: pull_nested_quant(ast_manager & m); ~pull_nested_quant(); void operator()(expr * n, expr_ref & r, proof_ref & p); void reset(); }; #endif /* PULL_QUANT_H_ */ z3-z3-4.8.7/src/ast/num_occurs.cpp000066400000000000000000000042141356505360400166670ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: num_occurs.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-01-27. Revision History: --*/ #include "ast/num_occurs.h" void num_occurs::process(expr * t, expr_fast_mark1 & visited) { ptr_buffer stack; #define VISIT(ARG) { \ if (!m_ignore_ref_count1 || ARG->get_ref_count() > 1) { \ obj_map::obj_map_entry * entry = m_num_occurs.insert_if_not_there2(ARG, 0); \ entry->get_data().m_value++; \ } \ if (!visited.is_marked(ARG)) { \ visited.mark(ARG, true); \ stack.push_back(ARG); \ } \ } VISIT(t); while (!stack.empty()) { expr * t = stack.back(); stack.pop_back(); unsigned j; switch (t->get_kind()) { case AST_APP: j = to_app(t)->get_num_args(); while (j > 0) { --j; expr * arg = to_app(t)->get_arg(j); VISIT(arg); } break; case AST_QUANTIFIER: if (!m_ignore_quantifiers) { expr * child = to_quantifier(t)->get_expr(); VISIT(child); } break; default: break; } } } void num_occurs::operator()(expr * t) { expr_fast_mark1 visited; process(t, visited); } void num_occurs::operator()(unsigned num, expr * const * ts) { expr_fast_mark1 visited; for (unsigned i = 0; i < num; i++) { process(ts[i], visited); } } z3-z3-4.8.7/src/ast/num_occurs.h000066400000000000000000000021201356505360400163260ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: num_occurs.h Abstract: Author: Leonardo de Moura (leonardo) 2008-01-27. Revision History: --*/ #ifndef NUM_OCCURS_H_ #define NUM_OCCURS_H_ #include "ast/ast.h" #include "util/obj_hashtable.h" /** \brief Functor for computing the number of occurrences of each sub-expression in a expression F. */ class num_occurs { protected: bool m_ignore_ref_count1; bool m_ignore_quantifiers; obj_map m_num_occurs; void process(expr * t, expr_fast_mark1 & visited); public: num_occurs(bool ignore_ref_count1 = false, bool ignore_quantifiers = false): m_ignore_ref_count1(ignore_ref_count1), m_ignore_quantifiers(ignore_quantifiers) { } void reset() { m_num_occurs.reset(); } void operator()(expr * t); void operator()(unsigned num, expr * const * ts); unsigned get_num_occs(expr * n) const { unsigned val; if (m_num_occurs.find(n, val)) return val; return 0; } }; #endif /* NUM_OCCURS_H_ */ z3-z3-4.8.7/src/ast/occurs.cpp000066400000000000000000000026441356505360400160150ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: occurs.cpp Abstract: Author: Leonardo de Moura (leonardo) 2007-06-07. Revision History: --*/ #include "ast/occurs.h" #include "ast/for_each_expr.h" // ----------------------------------- // // Occurs check // // ----------------------------------- namespace occurs_namespace { struct found {}; struct proc { expr * m_n; #define CHECK() { if (n == m_n) throw found(); } proc(expr * n):m_n(n) {} void operator()(var const * n) { CHECK(); } void operator()(app const * n) { CHECK(); } void operator()(quantifier const * n) { CHECK(); } }; struct decl_proc { func_decl * m_d; decl_proc(func_decl * d):m_d(d) {} void operator()(var const * n) { } void operator()(app const * n) { if (n->get_decl() == m_d) throw found(); } void operator()(quantifier const * n) { } }; }; // Return true if n1 occurs in n2 bool occurs(expr * n1, expr * n2) { occurs_namespace::proc p(n1); try { quick_for_each_expr(p, n2); } catch (const occurs_namespace::found &) { return true; } return false; } bool occurs(func_decl * d, expr * n) { occurs_namespace::decl_proc p(d); try { quick_for_each_expr(p, n); } catch (const occurs_namespace::found &) { return true; } return false; } z3-z3-4.8.7/src/ast/occurs.h000066400000000000000000000006731356505360400154620ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: occurs.h Abstract: Author: Leonardo de Moura (leonardo) 2007-06-07. Revision History: --*/ #ifndef OCCURS_H_ #define OCCURS_H_ class expr; class func_decl; /** \brief Return true if n1 occurs in n2 */ bool occurs(expr * n1, expr * n2); /** \brief Return true if d is used in n */ bool occurs(func_decl * d, expr * n); #endif /* OCCURS_H_ */ z3-z3-4.8.7/src/ast/pattern/000077500000000000000000000000001356505360400154625ustar00rootroot00000000000000z3-z3-4.8.7/src/ast/pattern/CMakeLists.txt000066400000000000000000000022641356505360400202260ustar00rootroot00000000000000# If this code for adding the rule to generate the database file is ever needed # for other components then we should refactor this code into # z3_add_component() if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/database.h") message(FATAL_ERROR "The generated file \"${CMAKE_CURRENT_SOURCE_DIR}/database.h\"" ${z3_polluted_tree_msg}) endif() add_custom_command(OUTPUT "database.h" COMMAND "${PYTHON_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/scripts/mk_pat_db.py" "${CMAKE_CURRENT_SOURCE_DIR}/database.smt2" "${CMAKE_CURRENT_BINARY_DIR}/database.h" MAIN_DEPENDENCY "${CMAKE_CURRENT_SOURCE_DIR}/database.smt2" DEPENDS "${PROJECT_SOURCE_DIR}/scripts/mk_pat_db.py" ${Z3_GENERATED_FILE_EXTRA_DEPENDENCIES} COMMENT "Generating \"database.h\"" ${ADD_CUSTOM_COMMAND_USES_TERMINAL_ARG} VERBATIM ) z3_add_component(pattern SOURCES expr_pattern_match.cpp pattern_inference.cpp pattern_inference_params.cpp # Let CMake know this target depends on this generated # header file ${CMAKE_CURRENT_BINARY_DIR}/database.h COMPONENT_DEPENDENCIES normal_forms rewriter smt2parser PYG_FILES pattern_inference_params_helper.pyg ) z3-z3-4.8.7/src/ast/pattern/database.smt2000066400000000000000000000362201356505360400200400ustar00rootroot00000000000000(declare-fun ?store (Int Int Int) Int) (declare-fun ?select (Int Int) Int) (declare-fun ?PO (Int Int) Int) (declare-fun ?asChild (Int Int) Int) (declare-fun ?classDown (Int Int) Int) (declare-fun ?array (Int) Int) (declare-fun ?elemtype (Int) Int) (declare-fun ?is (Int Int) Int) (declare-fun ?cast (Int Int) Int) (declare-fun ?Object () Int) (declare-fun ?null () Int) (declare-fun ?typeof (Int) Int) (declare-fun ?asElems (Int) Int) (declare-fun ?isAllocated (Int Int) Int) (declare-fun ?fClosedTime (Int) Int) (declare-fun ?eClosedTime (Int) Int) (declare-fun ?max (Int) Int) (declare-fun ?asLockSet (Int) Int) (declare-fun ?isNewArray (Int) Int) (declare-fun ?classLiteral (Int) Int) (declare-fun ?Class () Int) (declare-fun ?alloc () Int) (declare-fun ?arrayType () Int) (declare-fun ?f (Int) Int) (declare-fun ?finv (Int) Int) (declare-fun ?select2 (Int Int Int) Int) (declare-fun ?store2 (Int Int Int Int) Int) (declare-fun ?subtypes (Int Int) Bool) (declare-fun ?Unbox (Int) Int) (declare-fun ?UnboxedType (Int) Int) (declare-fun ?Box (Int Int) Int) (declare-fun ?System.Object () Int) (declare-fun ?Smt.true () Int) (declare-fun ?AsRepField (Int Int) Int) (declare-fun ?AsPeerField (Int) Int) (declare-fun ?nullObject () Int) (declare-fun ?ownerRef_ () Int) (declare-fun ?ownerFrame_ () Int) (declare-fun IntsHeap (Int) Int) (declare-fun ?localinv_ () Int) (declare-fun ?inv_ () Int) (declare-fun ?BaseClass_ (Int) Int) (declare-fun ?typeof_ (Int) Int) (declare-fun ?PeerGroupPlaceholder_ () Int) (declare-fun ?ClassRepr (Int) Int) (declare-fun ?RefArray (Int Int) Int) (declare-fun Ints_ (Int Int) Int) (declare-fun ?RefArrayGet (Int Int) Int) (declare-fun ?elements_ () Int) (declare-fun ?NonNullRefArray (Int Int) Int) (declare-fun IntsNotNull_ (Int Int) Int) (declare-fun ?Rank_ (Int) Int) (declare-fun ?ValueArray (Int Int) Int) (declare-fun ?ArrayCategory_ (Int) Int) (declare-fun ?ArrayCategoryValue_ () Int) (declare-fun ?ElementType_ (Int) Int) (declare-fun ?System.Array () Int) (declare-fun ?allocated_ () Int) (declare-fun ?StructGet_ (Int Int) Int) (declare-fun ?AsRangeField (Int Int) Int) (declare-fun IntsAllocated (Int Int) Int) (declare-fun IntnRange (Int Int) Bool) (declare-fun ?isAllocated_ (Int Int) Bool) (declare-fun ?AsDirectSubClass (Int Int) Int) (declare-fun ?OneClassDown (Int Int) Int) (assert (forall ((a Int) (i Int) (e Int)) (! (= (?select (?store a i e) i) e) :pattern (?store a i e) :weight 0))) (assert (forall ((a Int) (i Int) (j Int) (e Int)) (! (or (= i j) (= (?select (?store a i e) j) (?select a j))) :pattern (?select (?store a i e) j) :weight 0))) (assert (forall ((t0 Int) (t1 Int) (t2 Int)) (! (or (not (= (?PO t0 t1) 1)) (not (= (?PO t1 t2) 1)) (= (?PO t0 t2) 1)) :pattern ((?PO t0 t1) (?PO t1 t2))))) (assert (forall ((t0 Int) (t1 Int)) (! (or (not (= (?PO t0 t1) 1)) (not (= (?PO t1 t0) 1)) (= t0 t1)) :pattern ((?PO t0 t1) (?PO t1 t0))))) (assert (forall ((t0 Int) (t1 Int) (t2 Int)) (! (or (not (= (?PO t0 (?asChild t1 t2)) 1)) (= (?classDown t2 t0) (?asChild t1 t2))) :pattern (?PO t0 (?asChild t1 t2))))) (assert (forall ((t Int)) (! (= (?finv (?f t)) t) :pattern (?f t)))) (assert (forall ((t0 Int) (t1 Int) ) (! (iff (= (?PO t0 (?array t1)) 1) (not (or (not (= t0 (?array (?elemtype t0)))) (not (= (?PO (?elemtype t0) t1) 1))))) :pattern (?PO t0 (?array t1))))) (assert (forall ((x Int) (t Int)) (! (or (not (= (?is x t) 1)) (= (?cast x t) x)) :pattern (?cast x t)))) (assert (forall ((x Int) (t Int)) (! (or (not (= (?PO t ?Object) 1)) (iff (= (?is x t) 1) (or (= x ?null) (= (?PO (?typeof x) t) 1)))) :pattern ((?PO t ?Object) (?is x t))))) (assert (forall ((e Int) (a Int) (i Int)) (! (= (?is (?select (?select (?asElems e) a) i) (?elemtype (?typeof a))) 1) :pattern (?select (?select (?asElems e) a) i)))) (assert (forall ((x Int) (f Int) (a0 Int)) (! (or (<= (+ a0 (* (- 1) (?fClosedTime f))) 0) (not (= (?isAllocated x a0) 1)) (= (?isAllocated (?select f x) a0) 1)) :pattern (?isAllocated (?select f x) a0)))) (assert (forall ((a Int) (e Int) (i Int) (a0 Int)) (! (or (<= (+ a0 (* (- 1) (?eClosedTime e))) 0) (not (= (?isAllocated a a0) 1)) (= (?isAllocated (?select (?select e a) i) a0) 1)) :pattern (?isAllocated (?select (?select e a) i) a0)))) (assert (forall ((S Int)) (! (= (?select (?asLockSet S) (?max (?asLockSet S))) 1) :pattern (?select (?asLockSet S) (?max (?asLockSet S)))))) (assert (forall ((s Int)) (! (or (not (= 1 (?isNewArray s))) (= (?PO (?typeof s) ?arrayType) 1)) :pattern (?isNewArray s)))) (assert (forall ((t Int)) (! (not (or (= (?classLiteral t) ?null) (not (= (?is (?classLiteral t) ?Class) 1)) (not (= (?isAllocated (?classLiteral t) ?alloc) 1)))) :pattern (?classLiteral t)))) (assert (forall ((A Int) (o Int) (f Int) (v Int)) (! (= (?select2 (?store2 A o f v) o f) v) :pattern (?store2 A o f v) :weight 0))) (assert (forall ((A Int) (o Int) (f Int) (p Int) (g Int) (v Int)) (! (or (= o p) (= (?select2 (?store2 A o f v) p g) (?select2 A p g))) :pattern (?select2 (?store2 A o f v) p g) :weight 0))) (assert (forall ((A Int) (o Int) (f Int) (p Int) (g Int) (v Int)) (! (or (= f g) (= (?select2 (?store2 A o f v) p g) (?select2 A p g))) :pattern (?select2 (?store2 A o f v) p g) :weight 0))) (assert (forall ((t Int) (u Int) (v Int)) (! (or (not (?subtypes t u)) (not (?subtypes u v)) (?subtypes t v)) :pattern ((?subtypes t u) (?subtypes u v))))) (assert (forall ((t Int) (u Int)) (! (or (not (?subtypes t u)) (not (?subtypes u t)) (= t u)) :pattern ((?subtypes t u) (?subtypes u t))))) (assert (forall ((x Int) (p Int)) (! (or (not (?subtypes (?UnboxedType (?Box x p)) ?System.Object)) (not (= (?Box x p) p)) (= x p)) :pattern (?subtypes (?UnboxedType (?Box x p)) ?System.Object)))) (assert (forall ((h Int) (o Int) (f Int) (T Int)) (! (or (not (= (IntsHeap h) ?Smt.true)) (= (?select2 h o (?AsRepField f T)) ?nullObject) (not (or (not (= (?select2 h (?select2 h o (?AsRepField f T)) ?ownerRef_) o)) (not (= (?select2 h (?select2 h o (?AsRepField f T)) ?ownerFrame_) T))))) :pattern (?select2 h o (?AsRepField f T))))) (assert (forall ((h Int) (o Int) (f Int)) (! (or (not (= (IntsHeap h) ?Smt.true)) (= (?select2 h o (?AsPeerField f)) ?nullObject) (not (or (not (= (?select2 h (?select2 h o (?AsPeerField f)) ?ownerRef_) (?select2 h o ?ownerRef_))) (not (= (?select2 h (?select2 h o (?AsPeerField f)) ?ownerFrame_) (?select2 h o ?ownerFrame_)))))) :pattern (?select2 h o (?AsPeerField f))))) (assert (forall ((h Int) (o Int)) (! (or (not (= (IntsHeap h) ?Smt.true)) (= (?select2 h o ?ownerFrame_) ?PeerGroupPlaceholder_) (not (?subtypes (?select2 h (?select2 h o ?ownerRef_) ?inv_) (?select2 h o ?ownerFrame_))) (= (?select2 h (?select2 h o ?ownerRef_) ?localinv_) (?BaseClass_ (?select2 h o ?ownerFrame_))) (not (or (not (= (?select2 h o ?inv_) (?typeof_ o))) (not (= (?select2 h o ?localinv_) (?typeof_ o)))))) :pattern (?subtypes (?select2 h (?select2 h o ?ownerRef_) ?inv_) (?select2 h o ?ownerFrame_))))) (assert (forall ((T Int) (h Int)) (! (or (not (= (IntsHeap h) ?Smt.true)) (= (?select2 h (?ClassRepr T) ?ownerFrame_) ?PeerGroupPlaceholder_)) :pattern (?select2 h (?ClassRepr T) ?ownerFrame_)))) (assert (forall ((a Int) (T Int) (i Int) (r Int) (heap Int)) (! (or (not (= (IntsHeap heap) ?Smt.true)) (not (?subtypes (?typeof_ a) (?RefArray T r))) (= (Ints_ (?RefArrayGet (?select2 heap a ?elements_) i) T) ?Smt.true)) :pattern ((?subtypes (?typeof_ a) (?RefArray T r)) (?RefArrayGet (?select2 heap a ?elements_) i))))) (assert (forall ((a Int) (T Int) (r Int)) (! (or (= a ?nullObject) (not (?subtypes (?typeof_ a) (?RefArray T r))) (= (?Rank_ a) r)) :pattern (?subtypes (?typeof_ a) (?RefArray T r))))) (assert (forall ((T Int) (ET Int) (r Int)) (! (or (not (?subtypes T (?ValueArray ET r))) (= (?ArrayCategory_ T) ?ArrayCategoryValue_)) :pattern (?subtypes T (?ValueArray ET r))))) (assert (forall ((A Int) (r Int) (T Int)) (! (or (not (?subtypes T (?RefArray A r))) (not (or (not (= T (?RefArray (?ElementType_ T) r))) (not (?subtypes (?ElementType_ T) A))))) :pattern (?subtypes T (?RefArray A r))))) (assert (forall ((A Int) (r Int) (T Int)) (! (or (not (?subtypes T (?ValueArray A r))) (= T (?ValueArray A r))) :pattern (?subtypes T (?ValueArray A r))))) (assert (forall ((A Int) (B Int) (C Int)) (! (or (not (?subtypes C (?AsDirectSubClass B A))) (= (?OneClassDown C A) B)) :pattern (?subtypes C (?AsDirectSubClass B A))))) (assert (forall ((o Int) (T Int)) (! (iff (= (Ints_ o T) ?Smt.true) (or (= o ?nullObject) (?subtypes (?typeof_ o) T))) :pattern (Ints_ o T)))) (assert (forall ((o Int) (T Int)) (! (iff (= (IntsNotNull_ o T) ?Smt.true) (or (= o ?nullObject) (not (= (Ints_ o T) ?Smt.true)))) :pattern (IntsNotNull_ o T)))) (assert (forall ((h Int) (o Int)) (! (or (not (= (IntsHeap h) ?Smt.true)) (= o ?nullObject) (not (?subtypes (?typeof_ o) ?System.Array)) (not (or (not (= (?select2 h o ?inv_) (?typeof_ o))) (not (= (?select2 h o ?localinv_) (?typeof_ o)))))) :pattern ((?subtypes (?typeof_ o) ?System.Array) (?select2 h o ?inv_))))) (assert (forall ((h Int) (o Int) (f Int) (T Int)) (! (or (not (= (IntsHeap h) ?Smt.true)) (IntnRange (?select2 h o (?AsRangeField f T)) T)) :pattern (?select2 h o (?AsRangeField f T))))) (assert (forall ((h Int) (o Int) (f Int)) (! (or (not (= (IntsHeap h) ?Smt.true)) (not (= (?select2 h o ?allocated_) ?Smt.true)) (= (IntsAllocated h (?select2 h o f)) ?Smt.true)) :pattern (IntsAllocated h (?select2 h o f))))) (assert (forall ((h Int) (s Int) (f Int)) (! (or (not (= (IntsAllocated h s) ?Smt.true)) (= (IntsAllocated h (?StructGet_ s f)) ?Smt.true)) :pattern (IntsAllocated h (?StructGet_ s f))))) (assert (forall ((x Int) (f Int) (a0 Int)) (! (or (<= (+ a0 (* (- 1) (?fClosedTime f))) 0) (not (?isAllocated_ x a0)) (?isAllocated_ (?select f x) a0)) :pattern (?isAllocated_ (?select f x) a0)))) (assert (forall ((a Int) (e Int) (i Int) (a0 Int)) (! (or (<= (+ a0 (* (- 1) (?eClosedTime e))) 0) (not (?isAllocated_ a a0)) (?isAllocated_ (?select (?select e a) i) a0)) :pattern (?isAllocated_ (?select (?select e a) i) a0)))) (assert (forall ((e Int) (a Int) (i Int)) (! (= (?is (?select (?select (?asElems e) a) i) (?elemtype (?typeof a))) ?Smt.true) :pattern (?select (?select (?asElems e) a) i)))) (assert (forall ((t0 Int) (t1 Int)) (! (iff (?subtypes t0 (?array t1)) (not (or (not (= t0 (?array (?elemtype t0)))) (not (?subtypes (?elemtype t0) t1))))) :pattern (?subtypes t0 (?array t1))))) (assert (forall ((t0 Int) (t1 Int) (t2 Int)) (! (or (not (?subtypes t0 (?asChild t1 t2))) (= (?classDown t2 t0) (?asChild t1 t2))) :pattern (?subtypes t0 (?asChild t1 t2))))) (assert (forall ((t0 Int) (t1 Int)) (! (iff (?subtypes t0 (?array t1)) (not (or (not (= t0 (?array (?elemtype t0)))) (not (?subtypes (?elemtype t0) t1))))) :pattern (?subtypes t0 (?array t1))))) (assert (forall ((x Int) (t Int)) (! (or (not (= (?is x t) ?Smt.true)) (= (?cast x t) x)) :pattern (?cast x t)))) (assert (forall ((x Int) (t Int)) (! (or (not (?subtypes t ?Object)) (iff (= (?is x t) ?Smt.true) (or (= x ?null) (?subtypes (?typeof x) t)))) :pattern ((?subtypes t ?Object) (?is x t))))) (assert (forall ((e Int) (a Int) (i Int)) (! (= (?is (?select (?select (?asElems e) a) i) (?elemtype (?typeof a))) 1) :pattern (?select (?select (?asElems e) a) i)))) z3-z3-4.8.7/src/ast/pattern/expr_pattern_match.cpp000066400000000000000000000351051356505360400220610ustar00rootroot00000000000000/*++ Copyright (c) 2007 Microsoft Corporation Module Name: ast_pattern_match.cpp Abstract: Search for opportune pattern matching utilities. Author: Nikolaj Bjorner (nbjorner) 2007-04-10 Leonardo (leonardo) Notes: instead of the brute force enumeration of permutations we can add an instruction 'gate' which copies the ast into a register and creates another register with the same term. Matching against a 'gate' is a noop, apart from clearing the ast in the register. Then on backtracking we know how many terms were matched from the permutation. It does not make sense to enumerate all combinations of terms that were not considered, so skip these. Also, compilation should re-order terms to fail fast. --*/ #include "ast/ast.h" #include "ast/pattern/expr_pattern_match.h" #include "ast/for_each_ast.h" #include "ast/ast_ll_pp.h" #include "ast/ast_pp.h" #include "cmd_context/cmd_context.h" #include "parsers/smt2/smt2parser.h" expr_pattern_match::expr_pattern_match(ast_manager & manager): m_manager(manager), m_precompiled(manager) { } expr_pattern_match::~expr_pattern_match() { } bool expr_pattern_match::match_quantifier(quantifier* qf, app_ref_vector& patterns, unsigned& weight) { if (m_regs.empty()) { // HACK: the code crashes if database is empty. return false; } m_regs[0] = qf->get_expr(); for (unsigned i = 0; i < m_precompiled.size(); ++i) { if (match_quantifier(i, qf, patterns, weight)) return true; } return false; } bool expr_pattern_match::match_quantifier(unsigned i, quantifier* qf, app_ref_vector& patterns, unsigned& weight) { quantifier* qf2 = m_precompiled[i].get(); if (qf2->get_kind() != qf->get_kind() || is_lambda(qf)) { return false; } if (qf2->get_num_decls() != qf->get_num_decls()) { return false; } subst s; if (match(qf->get_expr(), m_first_instrs[i], s)) { for (unsigned j = 0; j < qf2->get_num_patterns(); ++j) { app* p = static_cast(qf2->get_pattern(j)); expr_ref p_result(m_manager); instantiate(p, qf->get_num_decls(), s, p_result); patterns.push_back(to_app(p_result.get())); } weight = qf2->get_weight(); return true; } return false; } bool expr_pattern_match::match_quantifier_index(quantifier* qf, app_ref_vector& patterns, unsigned& index) { if (m_regs.empty()) return false; m_regs[0] = qf->get_expr(); for (unsigned i = 0; i < m_precompiled.size(); ++i) { unsigned weight = 0; if (match_quantifier(i, qf, patterns, weight)) { index = i; return true; } } return false; } void expr_pattern_match::instantiate(expr* a, unsigned num_bound, subst& s, expr_ref& result) { bound b; for (unsigned i = 0; i < num_bound; ++i) { b.insert(m_bound_dom[i], m_bound_rng[i]); } TRACE("expr_pattern_match", tout << mk_pp(a, m_manager) << " " << num_bound << "\n";); inst_proc proc(m_manager, s, b, m_regs); for_each_ast(proc, a); expr* v = nullptr; proc.m_memoize.find(a, v); SASSERT(v); result = v; } void expr_pattern_match::compile(expr* q) { SASSERT(q->get_kind() == AST_QUANTIFIER); quantifier* qf = to_quantifier(q); unsigned ip = m_instrs.size(); m_first_instrs.push_back(ip); m_precompiled.push_back(qf); instr instr(BACKTRACK); unsigned_vector regs; ptr_vector pats; unsigned max_reg = 1; subst s; pats.push_back(qf->get_expr()); regs.push_back(0); unsigned num_bound = 0; obj_map bound; while (!pats.empty()) { unsigned reg = regs.back(); expr* pat = pats.back(); regs.pop_back(); pats.pop_back(); instr.m_pat = pat; instr.m_next = m_instrs.size()+1; instr.m_reg = reg; instr.m_offset = max_reg; switch(pat->get_kind()) { case AST_VAR: { var* b = to_var(pat); if (bound.find(b, instr.m_num_bound)) { instr.m_kind = CHECK_BOUND; } else { instr.m_kind = SET_BOUND; instr.m_num_bound = num_bound; bound.insert(b, num_bound); ++num_bound; } break; } case AST_APP: { unsigned r = 0; app* app = to_app(pat); func_decl* d = app->get_decl(); for (unsigned i = 0; i < app->get_num_args(); ++i) { regs.push_back(max_reg); pats.push_back(app->get_arg(i)); ++max_reg; } if (is_var(d)) { if (s.find(d, r)) { instr.m_kind = CHECK_VAR; instr.m_other_reg = r; } else { instr.m_kind = SET_VAR; s.insert(d, reg); } } else { if (d->is_associative() && d->is_commutative()) { instr.m_kind = BIND_AC; } else if (d->is_commutative()) { SASSERT(app->get_num_args() == 2); instr.m_kind = BIND_C; } else { instr.m_kind = BIND; } } break; } default: instr.m_kind = CHECK_TERM; break; } m_instrs.push_back(instr); } if (m_regs.size() <= max_reg) { m_regs.resize(max_reg+1); } if (m_bound_dom.size() <= num_bound) { m_bound_dom.resize(num_bound+1); m_bound_rng.resize(num_bound+1); } instr.m_kind = YIELD; m_instrs.push_back(instr); } bool expr_pattern_match::match(expr* a, unsigned init, subst& s) { svector bstack; instr pc = m_instrs[init]; while (true) { bool ok = false; switch(pc.m_kind) { case YIELD: // substitution s contains registers with matching declarations. return true; case CHECK_TERM: ok = (pc.m_pat == m_regs[pc.m_reg]); break; case SET_VAR: case CHECK_VAR: { app* app1 = to_app(pc.m_pat); a = m_regs[pc.m_reg]; if (a->get_kind() != AST_APP) { break; } app* app2 = to_app(a); if (app1->get_num_args() != app2->get_num_args()) { break; } if (pc.m_kind == CHECK_VAR && to_app(m_regs[pc.m_reg])->get_decl() != to_app(m_regs[pc.m_other_reg])->get_decl()) { break; } for (unsigned i = 0; i < app2->get_num_args(); ++i) { m_regs[pc.m_offset + i] = app2->get_arg(i); } if (pc.m_kind == SET_VAR) { s.insert(app1->get_decl(), pc.m_reg); } ok = true; break; } case SET_BOUND: { a = m_regs[pc.m_reg]; if (a->get_kind() != AST_VAR) { break; } ok = true; var* var_a = to_var(a); var* var_p = to_var(pc.m_pat); // check that the mapping of bound variables remains a bijection. for (unsigned i = 0; ok && i < pc.m_num_bound; ++i) { ok = (a != m_bound_rng[i]); } if (!ok) { break; } m_bound_dom[pc.m_num_bound] = var_p; m_bound_rng[pc.m_num_bound] = var_a; break; } case CHECK_BOUND: TRACE("expr_pattern_match", tout << "check bound " << pc.m_num_bound << " " << pc.m_reg << "\n";); ok = m_bound_rng[pc.m_num_bound] == m_regs[pc.m_reg]; break; case BIND: case BIND_AC: case BIND_C: { TRACE("expr_pattern_match", display(tout, pc); tout << mk_pp(m_regs[pc.m_reg],m_manager) << "\n";); app* app1 = to_app(pc.m_pat); a = m_regs[pc.m_reg]; if (a->get_kind() != AST_APP) { break; } app* app2 = to_app(a); if (app1->get_num_args() != app2->get_num_args()) { break; } if (!match_decl(app1->get_decl(), app2->get_decl())) { break; } switch(pc.m_kind) { case BIND: for (unsigned i = 0; i < app2->get_num_args(); ++i) { m_regs[pc.m_offset + i] = app2->get_arg(i); } ok = true; break; // process the next instruction. case BIND_AC: // push CHOOSE_AC on the backtracking stack. bstack.push_back(instr(CHOOSE_AC, pc.m_offset, pc.m_next, app2, 1)); break; case BIND_C: // push CHOOSE_C on the backtracking stack. ok = true; m_regs[pc.m_offset] = app2->get_arg(0); m_regs[pc.m_offset+1] = app2->get_arg(1); bstack.push_back(instr(CHOOSE_C, pc.m_offset, pc.m_next, app2, 2)); break; default: break; } break; } case CHOOSE_C: ok = true; SASSERT (pc.m_count == 2); m_regs[pc.m_offset+1] = pc.m_app->get_arg(0); m_regs[pc.m_offset] = pc.m_app->get_arg(1); break; case CHOOSE_AC: { ok = true; app* app2 = pc.m_app; for (unsigned i = 0; i < app2->get_num_args(); ++i) { m_regs[pc.m_offset + i] = app2->get_arg(i); } // generate the k'th permutation. unsigned k = pc.m_count; unsigned fac = 1; unsigned num_args = pc.m_app->get_num_args(); for (unsigned j = 2; j <= num_args; ++j) { fac *= (j-1); SASSERT(((k /fac) % j) + 1 <= j); std::swap(m_regs[pc.m_offset + j - 1], m_regs[pc.m_offset + j - ((k / fac) % j) - 1]); } if (k < fac*num_args) { bstack.push_back(instr(CHOOSE_AC, pc.m_offset, pc.m_next, app2, k+1)); } break; } case BACKTRACK: if (bstack.empty()) { return false; } pc = bstack.back(); bstack.pop_back(); continue; // with the loop. } if (ok) { pc = m_instrs[pc.m_next]; } else { TRACE("expr_pattern_match", tout << "backtrack\n";); pc = m_instrs[0]; } } } bool expr_pattern_match::match_decl(func_decl const * pat, func_decl const * d) const { if (pat == d) { return true; } if (pat->get_arity() != d->get_arity()) { return false; } // match families if (pat->get_family_id() == null_family_id) { return false; } if (d->get_family_id() != pat->get_family_id()) { return false; } if (d->get_decl_kind() != pat->get_decl_kind()) { return false; } if (d->get_num_parameters() != pat->get_num_parameters()) { return false; } for (unsigned i = 0; i < d->get_num_parameters(); ++i) { if (!(d->get_parameter(i) == pat->get_parameter(i))) { return false; } } return true; } bool expr_pattern_match::is_var(func_decl* d) { const char* s = d->get_name().bare_str(); return s && *s == '?'; } void expr_pattern_match::initialize(char const * spec_string) { if (!m_instrs.empty()) { return; } m_instrs.push_back(instr(BACKTRACK)); std::istringstream is(spec_string); cmd_context ctx(true, &m_manager); bool ps = ctx.print_success_enabled(); ctx.set_print_success(false); VERIFY(parse_smt2_commands(ctx, is)); ctx.set_print_success(ps); for (expr * e : ctx.assertions()) { compile(e); } } unsigned expr_pattern_match::initialize(quantifier* q) { if (m_instrs.empty()) { m_instrs.push_back(instr(BACKTRACK)); } compile(q); return m_precompiled.size() - 1; } void expr_pattern_match::display(std::ostream& out) const { for (unsigned i = 0; i < m_instrs.size(); ++i) { display(out, m_instrs[i]); } } void expr_pattern_match::display(std::ostream& out, instr const& pc) const { switch(pc.m_kind) { case BACKTRACK: out << "backtrack\n"; break; case BIND: out << "bind "; out << mk_pp(pc.m_pat, m_manager) << "\n"; out << "next: " << pc.m_next << "\n"; out << "offset: " << pc.m_offset << "\n"; out << "reg: " << pc.m_reg << "\n"; break; case BIND_AC: out << "bind_ac "; out << mk_pp(pc.m_pat, m_manager) << "\n"; out << "next: " << pc.m_next << "\n"; out << "offset: " << pc.m_offset << "\n"; out << "reg: " << pc.m_reg << "\n"; break; case BIND_C: out << "bind_c "; out << mk_pp(pc.m_pat, m_manager) << "\n"; out << "next: " << pc.m_next << "\n"; out << "offset: " << pc.m_offset << "\n"; out << "reg: " << pc.m_reg << "\n"; break; case CHOOSE_AC: out << "choose_ac\n"; out << "next: " << pc.m_next << "\n"; out << "count: " << pc.m_count << "\n"; break; case CHOOSE_C: out << "choose_c\n"; out << "next: " << pc.m_next << "\n"; //out << "reg: " << pc.m_reg << "\n"; break; case CHECK_VAR: out << "check_var "; out << mk_pp(pc.m_pat, m_manager) << "\n"; out << "next: " << pc.m_next << "\n"; out << "reg: " << pc.m_reg << "\n"; out << "other_reg: " << pc.m_other_reg << "\n"; break; case CHECK_TERM: out << "check "; out << mk_pp(pc.m_pat, m_manager) << "\n"; out << "next: " << pc.m_next << "\n"; out << "reg: " << pc.m_reg << "\n"; break; case YIELD: out << "yield\n"; break; case SET_VAR: out << "set_var "; out << mk_pp(pc.m_pat, m_manager) << "\n"; out << "next: " << pc.m_next << "\n"; break; default: break; } } // TBD: fix type overloading. // TBD: bound number of permutations. // TBD: forward pruning checks. z3-z3-4.8.7/src/ast/pattern/expr_pattern_match.h000066400000000000000000000074501356505360400215300ustar00rootroot00000000000000/*++ Copyright (c) 2007 Microsoft Corporation Module Name: expr_pattern_match.h Abstract: Search for opportune pattern matching utilities. Author: Nikolaj Bjorner (nbjorner) 2007-04-10 Leonardo (leonardo) Notes: --*/ #ifndef EXPR_PATTERN_MATCH_H_ #define EXPR_PATTERN_MATCH_H_ #include "ast/ast.h" #include "util/map.h" class expr_pattern_match { enum instr_kind { BACKTRACK, BIND, BIND_AC, BIND_C, CHOOSE_AC, CHOOSE_C, SET_VAR, CHECK_VAR, CHECK_TERM, SET_BOUND, CHECK_BOUND, YIELD }; struct instr { instr(instr_kind k) : m_kind(k) {} instr(instr_kind k, unsigned o, unsigned next, app* app, unsigned count): m_kind(k), m_offset(o), m_next(next), m_app(app), m_count(count) {} instr_kind m_kind; unsigned m_offset; unsigned m_next; app* m_app; expr* m_pat; unsigned m_reg; unsigned m_other_reg; unsigned m_count; unsigned m_num_bound; }; typedef obj_map subst; typedef obj_map bound; struct inst_proc { ast_manager& m_manager; expr_ref_vector m_pinned; subst& m_subst; bound& m_bound; obj_map m_memoize; ptr_vector& m_regs; inst_proc(ast_manager& m, subst& s, bound& b, ptr_vector& regs) : m_manager(m), m_pinned(m), m_subst(s), m_bound(b), m_regs(regs) {} void operator()(ast* a) { } void operator()(expr* a) { m_memoize.insert(a, a); } void operator()(var* v) { m_memoize.insert(v, m_bound[v]); } void operator()(app * n) { unsigned r; ptr_vector args; unsigned num_args = n->get_num_args(); func_decl * decl = n->get_decl(); expr* result; if (m_subst.find(decl, r)) { decl = to_app(m_regs[r])->get_decl(); } for (expr* arg : *n) { arg = m_memoize[arg]; args.push_back(arg); } if (m_manager.is_pattern(n)) { result = m_manager.mk_pattern(num_args, reinterpret_cast(args.c_ptr())); } else { result = m_manager.mk_app(decl, num_args, args.c_ptr()); } m_pinned.push_back(result); m_memoize.insert(n, result); } }; ast_manager & m_manager; quantifier_ref_vector m_precompiled; unsigned_vector m_first_instrs; svector m_instrs; ptr_vector m_regs; ptr_vector m_bound_dom; ptr_vector m_bound_rng; public: expr_pattern_match(ast_manager & manager); ~expr_pattern_match(); bool match_quantifier(quantifier * qf, app_ref_vector & patterns, unsigned & weight); bool match_quantifier_index(quantifier* qf, app_ref_vector & patterns, unsigned& index); unsigned initialize(quantifier* qf); void initialize(char const * database); void display(std::ostream& out) const; private: bool match_quantifier(unsigned i, quantifier * qf, app_ref_vector & patterns, unsigned & weight); void instantiate(expr* a, unsigned num_bound, subst& s, expr_ref& result); void compile(expr* q); bool match(expr* a, unsigned init, subst& s); bool match_decl(func_decl const * pat, func_decl const * d) const; bool is_var(func_decl* d); void display(std::ostream& out, instr const& pc) const; }; #endif z3-z3-4.8.7/src/ast/pattern/pattern_inference.cpp000066400000000000000000000636561356505360400217010ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: pattern_inference.cpp Abstract: Author: Leonardo de Moura (leonardo) 2006-12-08. Revision History: --*/ #include "util/warning.h" #include "ast/pattern/pattern_inference.h" #include "ast/ast_ll_pp.h" #include "ast/ast_pp.h" #include "ast/ast_util.h" #include "ast/arith_decl_plugin.h" #include "ast/normal_forms/pull_quant.h" #include "ast/well_sorted.h" #include "ast/for_each_expr.h" #include "ast/rewriter/rewriter_def.h" void smaller_pattern::save(expr * p1, expr * p2) { expr_pair e(p1, p2); if (!m_cache.contains(e)) { TRACE("smaller_pattern_proc", tout << "saving: " << p1->get_id() << " " << p2->get_id() << "\n";); m_cache.insert(e); m_todo.push_back(e); } } bool smaller_pattern::process(expr * p1, expr * p2) { m_todo.reset(); m_cache.reset(); save(p1, p2); while (!m_todo.empty()) { expr_pair & curr = m_todo.back(); p1 = curr.first; p2 = curr.second; m_todo.pop_back(); ast_kind k1 = p1->get_kind(); if (k1 != AST_VAR && k1 != p2->get_kind()) return false; switch (k1) { case AST_APP: { app * app1 = to_app(p1); app * app2 = to_app(p2); unsigned num1 = app1->get_num_args(); if (num1 != app2->get_num_args() || app1->get_decl() != app2->get_decl()) return false; for (unsigned i = 0; i < num1; i++) save(app1->get_arg(i), app2->get_arg(i)); break; } case AST_VAR: { unsigned idx = to_var(p1)->get_idx(); if (idx < m_bindings.size()) { if (m_bindings[idx] == 0) m_bindings[idx] = p2; else if (m_bindings[idx] != p2) return false; } // it is a variable bound by an external quantifier else if (p1 != p2) return false; break; } default: if (p1 != p2) return false; break; } } return true; } bool smaller_pattern::operator()(unsigned num_bindings, expr * p1, expr * p2) { m_bindings.resize(num_bindings); for (unsigned i = 0; i < num_bindings; i++) m_bindings[i] = 0; return process(p1, p2); } #ifdef _TRACE static void dump_app_vector(std::ostream & out, ptr_vector const & v, ast_manager & m) { for (app * e : v) out << mk_pp(e, m) << "\n"; } #endif #include "ast/pattern/database.h" pattern_inference_cfg::pattern_inference_cfg(ast_manager & m, pattern_inference_params & params): m(m), m_params(params), m_bfid(m.get_basic_family_id()), m_afid(m.mk_family_id("arith")), m_le(m), m_nested_arith_only(true), m_block_loop_patterns(params.m_pi_block_loop_patterns), m_candidates(m), m_pattern_weight_lt(m_candidates_info), m_collect(m, *this), m_contains_subpattern(*this), m_database(m) { if (params.m_pi_arith == AP_NO) register_forbidden_family(m_afid); } void pattern_inference_cfg::collect::operator()(expr * n, unsigned num_bindings) { SASSERT(m_info.empty()); SASSERT(m_todo.empty()); SASSERT(m_cache.empty()); m_num_bindings = num_bindings; m_todo.push_back(entry(n, 0)); while (!m_todo.empty()) { entry & e = m_todo.back(); n = e.m_node; unsigned delta = e.m_delta; TRACE("collect", tout << "processing: " << n->get_id() << " " << delta << " kind: " << n->get_kind() << "\n";); TRACE("collect_info", tout << mk_pp(n, m) << "\n";); if (visit_children(n, delta)) { m_todo.pop_back(); save_candidate(n, delta); } } reset(); } inline void pattern_inference_cfg::collect::visit(expr * n, unsigned delta, bool & visited) { entry e(n, delta); if (!m_cache.contains(e)) { m_todo.push_back(e); visited = false; } } bool pattern_inference_cfg::collect::visit_children(expr * n, unsigned delta) { bool visited = true; unsigned i; switch (n->get_kind()) { case AST_APP: i = to_app(n)->get_num_args(); while (i > 0) { --i; visit(to_app(n)->get_arg(i), delta, visited); } break; case AST_QUANTIFIER: visit(to_quantifier(n)->get_expr(), delta + to_quantifier(n)->get_num_decls(), visited); break; default: break; } return visited; } inline void pattern_inference_cfg::collect::save(expr * n, unsigned delta, info * i) { m_cache.insert(entry(n, delta), i); if (i != nullptr) m_info.push_back(i); } void pattern_inference_cfg::collect::save_candidate(expr * n, unsigned delta) { switch (n->get_kind()) { case AST_VAR: { unsigned idx = to_var(n)->get_idx(); if (idx >= delta) { idx = idx - delta; uint_set free_vars; if (idx < m_num_bindings) free_vars.insert(idx); info * i = nullptr; if (delta == 0) i = alloc(info, m, n, free_vars, 1); else i = alloc(info, m, m.mk_var(idx, to_var(n)->get_sort()), free_vars, 1); save(n, delta, i); } else { save(n, delta, nullptr); } return; } case AST_APP: { app * c = to_app(n); func_decl * decl = c->get_decl(); if (m_owner.is_forbidden(c)) { save(n, delta, nullptr); return; } if (c->get_num_args() == 0) { save(n, delta, alloc(info, m, n, uint_set(), 1)); return; } ptr_buffer buffer; bool changed = false; // false if none of the children is mapped to a node different from itself. uint_set free_vars; unsigned size = 1; unsigned num = c->get_num_args(); for (unsigned i = 0; i < num; i++) { expr * child = c->get_arg(i); info * child_info = nullptr; #ifdef Z3DEBUG bool found = #endif m_cache.find(entry(child, delta), child_info); SASSERT(found); if (child_info == nullptr) { save(n, delta, nullptr); return; } buffer.push_back(child_info->m_node.get()); free_vars |= child_info->m_free_vars; size += child_info->m_size; if (child != child_info->m_node.get()) changed = true; } app * new_node = nullptr; if (changed) new_node = m.mk_app(decl, buffer.size(), buffer.c_ptr()); else new_node = to_app(n); save(n, delta, alloc(info, m, new_node, free_vars, size)); // Remark: arithmetic patterns are only used if they are nested inside other terms. // That is, we never consider x + 1 as pattern. On the other hand, f(x+1) can be a pattern // if arithmetic is not in the forbidden list. // // Remark: The rule above has an exception. The operators (div, idiv, mod) are allowed to be // used as patterns even when they are not nested in other terms. The motivation is that // Z3 currently doesn't implement them (i.e., they are uninterpreted). So, some users add axioms // stating properties about these operators. family_id fid = c->get_family_id(); decl_kind k = c->get_decl_kind(); if (!free_vars.empty() && (fid != m_afid || (fid == m_afid && !m_owner.m_nested_arith_only && (k == OP_DIV || k == OP_IDIV || k == OP_MOD || k == OP_REM || k == OP_MUL)))) { TRACE("pattern_inference", tout << "potential candidate: \n" << mk_pp(new_node, m) << "\n";); m_owner.add_candidate(new_node, free_vars, size); } return; } default: save(n, delta, nullptr); return; } } void pattern_inference_cfg::collect::reset() { m_cache.reset(); std::for_each(m_info.begin(), m_info.end(), delete_proc()); m_info.reset(); SASSERT(m_todo.empty()); } void pattern_inference_cfg::add_candidate(app * n, uint_set const & free_vars, unsigned size) { for (unsigned i = 0; i < m_num_no_patterns; i++) { if (n == m_no_patterns[i]) return; } if (!m_candidates_info.contains(n)) { m_candidates_info.insert(n, info(free_vars, size)); m_candidates.push_back(n); } } /** \brief Copy the non-looping patterns in m_candidates to result when m_params.m_pi_block_loop_patterns = true. Otherwise, copy m_candidates to result. */ void pattern_inference_cfg::filter_looping_patterns(ptr_vector & result) { unsigned num = m_candidates.size(); for (unsigned i1 = 0; i1 < num; i1++) { app * n1 = m_candidates.get(i1); expr2info::obj_map_entry * e1 = m_candidates_info.find_core(n1); SASSERT(e1); uint_set const & s1 = e1->get_data().m_value.m_free_vars; if (m_block_loop_patterns) { bool smaller = false; for (unsigned i2 = 0; i2 < num; i2++) { if (i1 != i2) { app * n2 = m_candidates.get(i2); expr2info::obj_map_entry * e2 = m_candidates_info.find_core(n2); if (e2) { uint_set const & s2 = e2->get_data().m_value.m_free_vars; // Remark: the comparison operator only makes sense if both AST nodes // contain the same number of variables. // Example: // (f X Y) <: (f (g X Z W) Y) if (s1 == s2 && m_le(m_num_bindings, n1, n2) && !m_le(m_num_bindings, n2, n1)) { smaller = true; break; } } } } if (!smaller) result.push_back(n1); else m_candidates_info.erase(n1); } else { result.push_back(n1); } } } inline void pattern_inference_cfg::contains_subpattern::save(expr * n) { unsigned id = n->get_id(); m_already_processed.assure_domain(id); if (!m_already_processed.contains(id)) { m_todo.push_back(n); m_already_processed.insert(id); } } bool pattern_inference_cfg::contains_subpattern::operator()(expr * n) { m_already_processed.reset(); m_todo.reset(); expr2info::obj_map_entry * _e = m_owner.m_candidates_info.find_core(n); SASSERT(_e); uint_set const & s1 = _e->get_data().m_value.m_free_vars; save(n); unsigned num; while (!m_todo.empty()) { expr * curr = m_todo.back(); m_todo.pop_back(); switch (curr->get_kind()) { case AST_APP: if (curr != n) { expr2info::obj_map_entry * e = m_owner.m_candidates_info.find_core(curr); if (e) { uint_set const & s2 = e->get_data().m_value.m_free_vars; SASSERT(s2.subset_of(s1)); if (s1 == s2) { TRACE("pattern_inference", tout << mk_pp(n, m_owner.m) << "\nis bigger than\n" << mk_pp(to_app(curr), m_owner.m) << "\n";); return true; } } } num = to_app(curr)->get_num_args(); for (unsigned i = 0; i < num; i++) save(to_app(curr)->get_arg(i)); break; case AST_VAR: break; default: UNREACHABLE(); } } return false; } /** Return true if n contains a direct/indirect child that is also a pattern, and contains the same number of free variables. */ inline bool pattern_inference_cfg::contains_subpattern(expr * n) { return m_contains_subpattern(n); } /** \brief Copy a pattern p in patterns to result, if there is no direct/indirect child of p in patterns which contains the same set of variables. Remark: Every pattern p in patterns is also a member of m_pattern_map. */ void pattern_inference_cfg::filter_bigger_patterns(ptr_vector const & patterns, ptr_vector & result) { for (app * curr : patterns) { if (!contains_subpattern(curr)) result.push_back(curr); } } bool pattern_inference_cfg::pattern_weight_lt::operator()(expr * n1, expr * n2) const { expr2info::obj_map_entry * e1 = m_candidates_info.find_core(n1); expr2info::obj_map_entry * e2 = m_candidates_info.find_core(n2); SASSERT(e1 != 0); SASSERT(e2 != 0); info const & i1 = e1->get_data().m_value; info const & i2 = e2->get_data().m_value; unsigned num_free_vars1 = i1.m_free_vars.num_elems(); unsigned num_free_vars2 = i2.m_free_vars.num_elems(); return num_free_vars1 > num_free_vars2 || (num_free_vars1 == num_free_vars2 && i1.m_size < i2.m_size); } /** \brief Create unary patterns (single expressions that contain all bound variables). If a candidate does not contain all bound variables, then it is copied to remaining_candidate_patterns. The new patterns are stored in result. */ void pattern_inference_cfg::candidates2unary_patterns(ptr_vector const & candidate_patterns, ptr_vector & remaining_candidate_patterns, app_ref_buffer & result) { for (app * candidate : candidate_patterns) { expr2info::obj_map_entry * e = m_candidates_info.find_core(candidate); info const & i = e->get_data().m_value; if (i.m_free_vars.num_elems() == m_num_bindings) { app * new_pattern = m.mk_pattern(candidate); result.push_back(new_pattern); } else { remaining_candidate_patterns.push_back(candidate); } } } // TODO: this code is too inefficient when the number of candidate // patterns is too big. // HACK: limit the number of case-splits: #define MAX_SPLITS 32 void pattern_inference_cfg::candidates2multi_patterns(unsigned max_num_patterns, ptr_vector const & candidate_patterns, app_ref_buffer & result) { SASSERT(!candidate_patterns.empty()); m_pre_patterns.push_back(alloc(pre_pattern)); unsigned sz = candidate_patterns.size(); unsigned num_splits = 0; for (unsigned j = 0; j < m_pre_patterns.size(); j++) { pre_pattern * curr = m_pre_patterns[j]; if (curr->m_free_vars.num_elems() == m_num_bindings) { app * new_pattern = m.mk_pattern(curr->m_exprs.size(), curr->m_exprs.c_ptr()); result.push_back(new_pattern); if (result.size() >= max_num_patterns) return; } else if (curr->m_idx < sz) { app * n = candidate_patterns[curr->m_idx]; expr2info::obj_map_entry * e = m_candidates_info.find_core(n); uint_set const & s = e->get_data().m_value.m_free_vars; if (!s.subset_of(curr->m_free_vars)) { pre_pattern * new_p = alloc(pre_pattern,*curr); new_p->m_exprs.push_back(n); new_p->m_free_vars |= s; new_p->m_idx++; m_pre_patterns.push_back(new_p); if (num_splits < MAX_SPLITS) { m_pre_patterns[j] = 0; curr->m_idx++; m_pre_patterns.push_back(curr); num_splits++; } } else { m_pre_patterns[j] = 0; curr->m_idx++; m_pre_patterns.push_back(curr); } } TRACE("pattern_inference", tout << "m_pre_patterns.size(): " << m_pre_patterns.size() << "\nnum_splits: " << num_splits << "\n";); } } void pattern_inference_cfg::reset_pre_patterns() { std::for_each(m_pre_patterns.begin(), m_pre_patterns.end(), delete_proc()); m_pre_patterns.reset(); } bool pattern_inference_cfg::is_forbidden(app * n) const { func_decl const * decl = n->get_decl(); if (is_ground(n)) return false; // Remark: skolem constants should not be used in patterns, since they do not // occur outside of the quantifier. That is, Z3 will never match this kind of // pattern. if (m_params.m_pi_avoid_skolems && decl->is_skolem()) { CTRACE("pattern_inference_skolem", decl->is_skolem(), tout << "ignoring: " << mk_pp(n, m) << "\n";); return true; } if (is_forbidden(decl)) return true; return false; } bool pattern_inference_cfg::has_preferred_patterns(ptr_vector & candidate_patterns, app_ref_buffer & result) { if (m_preferred.empty()) return false; bool found = false; for (app * candidate : candidate_patterns) { if (m_preferred.contains(to_app(candidate)->get_decl())) { expr2info::obj_map_entry * e = m_candidates_info.find_core(candidate); info const & i = e->get_data().m_value; if (i.m_free_vars.num_elems() == m_num_bindings) { TRACE("pattern_inference", tout << "found preferred pattern:\n" << mk_pp(candidate, m) << "\n";); app * p = m.mk_pattern(candidate); result.push_back(p); found = true; } } } return found; } void pattern_inference_cfg::mk_patterns(unsigned num_bindings, expr * n, unsigned num_no_patterns, expr * const * no_patterns, app_ref_buffer & result) { m_num_bindings = num_bindings; m_num_no_patterns = num_no_patterns; m_no_patterns = no_patterns; m_collect(n, num_bindings); TRACE("pattern_inference", tout << mk_pp(n, m); tout << "\ncandidates:\n"; unsigned num = m_candidates.size(); for (unsigned i = 0; i < num; i++) { tout << mk_pp(m_candidates.get(i), m) << "\n"; }); if (!m_candidates.empty()) { m_tmp1.reset(); filter_looping_patterns(m_tmp1); TRACE("pattern_inference", tout << "candidates after removing looping-patterns:\n"; dump_app_vector(tout, m_tmp1, m);); SASSERT(!m_tmp1.empty()); if (!has_preferred_patterns(m_tmp1, result)) { // continue if there are no preferred patterns m_tmp2.reset(); filter_bigger_patterns(m_tmp1, m_tmp2); SASSERT(!m_tmp2.empty()); TRACE("pattern_inference", tout << "candidates after removing bigger patterns:\n"; dump_app_vector(tout, m_tmp2, m);); m_tmp1.reset(); candidates2unary_patterns(m_tmp2, m_tmp1, result); unsigned num_extra_multi_patterns = m_params.m_pi_max_multi_patterns; if (result.empty()) num_extra_multi_patterns++; if (num_extra_multi_patterns > 0 && !m_tmp1.empty()) { // m_pattern_weight_lt is not a total order std::stable_sort(m_tmp1.begin(), m_tmp1.end(), m_pattern_weight_lt); TRACE("pattern_inference", tout << "candidates after sorting:\n"; dump_app_vector(tout, m_tmp1, m);); candidates2multi_patterns(num_extra_multi_patterns, m_tmp1, result); } } } reset_pre_patterns(); m_candidates_info.reset(); m_candidates.reset(); } bool pattern_inference_cfg::reduce_quantifier( quantifier * q, expr * new_body, expr * const *, // new_patterns expr * const * new_no_patterns, expr_ref & result, proof_ref & result_pr) { TRACE("pattern_inference", tout << "processing:\n" << mk_pp(q, m) << "\n";); if (!is_forall(q)) { return false; } int weight = q->get_weight(); if (m_params.m_pi_use_database) { app_ref_vector new_patterns(m); m_database.initialize(g_pattern_database); 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))); }); 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";); result = m.update_quantifier_weight(q, new_weight); } else { 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(result, m) << "\n";); } if (m.proofs_enabled()) result_pr = m.mk_rewrite(q, result); return true; } } if (q->get_num_patterns() > 0) { return false; } if (m_params.m_pi_nopat_weight >= 0) weight = m_params.m_pi_nopat_weight; SASSERT(q->get_num_patterns() == 0); if (m_params.m_pi_arith == AP_CONSERVATIVE) m_forbidden.push_back(m_afid); app_ref_buffer new_patterns(m); unsigned num_no_patterns = q->get_num_no_patterns(); mk_patterns(q->get_num_decls(), new_body, num_no_patterns, new_no_patterns, new_patterns); if (new_patterns.empty() && num_no_patterns > 0) { if (new_patterns.empty()) { mk_patterns(q->get_num_decls(), new_body, 0, nullptr, new_patterns); if (m_params.m_pi_warnings && !new_patterns.empty()) { warning_msg("ignoring nopats annotation because Z3 couldn't find any other pattern (quantifier id: %s)", q->get_qid().str().c_str()); } } } if (m_params.m_pi_arith == AP_CONSERVATIVE) { m_forbidden.pop_back(); if (new_patterns.empty()) { flet l1(m_block_loop_patterns, false); // allow looping patterns mk_patterns(q->get_num_decls(), new_body, num_no_patterns, new_no_patterns, new_patterns); if (!new_patterns.empty()) { weight = std::max(weight, static_cast(m_params.m_pi_arith_weight)); if (m_params.m_pi_warnings) { warning_msg("using arith. in pattern (quantifier id: %s), the weight was increased to %d (this value can be modified using PI_ARITH_WEIGHT=).", q->get_qid().str().c_str(), weight); } } } } if (m_params.m_pi_arith != AP_NO && new_patterns.empty()) { if (new_patterns.empty()) { flet l1(m_nested_arith_only, false); // try to find a non-nested arith pattern flet l2(m_block_loop_patterns, false); // allow looping patterns mk_patterns(q->get_num_decls(), new_body, num_no_patterns, new_no_patterns, new_patterns); if (!new_patterns.empty()) { weight = std::max(weight, static_cast(m_params.m_pi_non_nested_arith_weight)); if (m_params.m_pi_warnings) { warning_msg("using non nested arith. pattern (quantifier id: %s), the weight was increased to %d (this value can be modified using PI_NON_NESTED_ARITH_WEIGHT=).", q->get_qid().str().c_str(), weight); } // verbose_stream() << mk_pp(q, m) << "\n"; } } } quantifier_ref new_q(m.update_quantifier(q, new_patterns.size(), (expr**) new_patterns.c_ptr(), new_body), m); if (weight != q->get_weight()) new_q = m.update_quantifier_weight(new_q, weight); if (m.proofs_enabled()) { proof* new_body_pr = m.mk_reflexivity(new_body); new_body_pr = m.mk_bind_proof(new_q, new_body_pr); result_pr = m.mk_quant_intro(q, new_q, new_body_pr); } if (new_patterns.empty() && m_params.m_pi_pull_quantifiers) { pull_quant pull(m); expr_ref new_expr(m); proof_ref new_pr(m); pull(new_q, new_expr, new_pr); quantifier * result2 = to_quantifier(new_expr); if (result2 != new_q) { mk_patterns(result2->get_num_decls(), result2->get_expr(), 0, nullptr, new_patterns); if (!new_patterns.empty()) { if (m_params.m_pi_warnings) { warning_msg("pulled nested quantifier to be able to find an usable pattern (quantifier id: %s)", q->get_qid().str().c_str()); } new_q = m.update_quantifier(result2, new_patterns.size(), (expr**) new_patterns.c_ptr(), result2->get_expr()); if (m.proofs_enabled()) { result_pr = m.mk_transitivity(new_pr, m.mk_quant_intro(result2, new_q, m.mk_bind_proof(new_q, m.mk_reflexivity(new_q->get_expr())))); } TRACE("pattern_inference", tout << "pulled quantifier:\n" << mk_pp(new_q, m) << "\n";); } } } if (new_patterns.empty()) { if (m_params.m_pi_warnings) { warning_msg("failed to find a pattern for quantifier (quantifier id: %s)", q->get_qid().str().c_str()); } TRACE("pi_failed", tout << mk_pp(q, m) << "\n";); } if (new_patterns.empty() && new_body == q->get_expr()) { return false; } result = new_q; IF_VERBOSE(10, verbose_stream() << "(smt.inferred-patterns :qid " << q->get_qid() << "\n"; for (unsigned i = 0; i < new_patterns.size(); i++) verbose_stream() << " " << mk_ismt2_pp(new_patterns[i], m, 2) << "\n"; verbose_stream() << ")\n"; ); return true; } pattern_inference_rw::pattern_inference_rw(ast_manager& m, pattern_inference_params & params): rewriter_tpl(m, m.proofs_enabled(), m_cfg), m_cfg(m, params) {} z3-z3-4.8.7/src/ast/pattern/pattern_inference.h000066400000000000000000000207151356505360400213330ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: pattern_inference.h Abstract: Author: Leonardo de Moura (leonardo) 2006-12-08. Revision History: --*/ #ifndef PATTERN_INFERENCE_H_ #define PATTERN_INFERENCE_H_ #include "ast/ast.h" #include "ast/rewriter/rewriter.h" #include "ast/pattern/pattern_inference_params.h" #include "util/vector.h" #include "util/uint_set.h" #include "util/nat_set.h" #include "util/obj_hashtable.h" #include "util/obj_pair_hashtable.h" #include "util/map.h" #include "ast/pattern/expr_pattern_match.h" /** \brief A pattern p_1 is smaller than a pattern p_2 iff every instance of p_2 is also an instance of p_1. Example: f(X) is smaller than f(g(X)) because every instance of f(g(X)) is also an instance of f(X). */ class smaller_pattern { ast_manager & m; ptr_vector m_bindings; typedef std::pair expr_pair; typedef obj_pair_hashtable cache; svector m_todo; cache m_cache; void save(expr * p1, expr * p2); bool process(expr * p1, expr * p2); smaller_pattern & operator=(smaller_pattern const &); public: smaller_pattern(ast_manager & m): m(m) { } bool operator()(unsigned num_bindings, expr * p1, expr * p2); }; class pattern_inference_cfg : public default_rewriter_cfg { ast_manager& m; pattern_inference_params & m_params; family_id m_bfid; family_id m_afid; svector m_forbidden; obj_hashtable m_preferred; smaller_pattern m_le; unsigned m_num_bindings; unsigned m_num_no_patterns; expr * const * m_no_patterns; bool m_nested_arith_only; bool m_block_loop_patterns; struct info { uint_set m_free_vars; unsigned m_size; info(uint_set const & vars, unsigned size): m_free_vars(vars), m_size(size) { } info(): m_free_vars(), m_size(0) { } }; typedef obj_map expr2info; expr2info m_candidates_info; // candidate -> set of free vars + size app_ref_vector m_candidates; ptr_vector m_tmp1; ptr_vector m_tmp2; ptr_vector m_todo; // Compare candidates patterns based on their usefulness // p1 < p2 if // - p1 has more free variables than p2 // - p1 and p2 has the same number of free variables, // and p1 is smaller than p2. struct pattern_weight_lt { expr2info & m_candidates_info; pattern_weight_lt(expr2info & i): m_candidates_info(i) { } bool operator()(expr * n1, expr * n2) const; }; pattern_weight_lt m_pattern_weight_lt; // // Functor for collecting candidates. // class collect { struct entry { expr * m_node; unsigned m_delta; entry():m_node(nullptr), m_delta(0) {} entry(expr * n, unsigned d):m_node(n), m_delta(d) {} unsigned hash() const { return hash_u_u(m_node->get_id(), m_delta); } bool operator==(entry const & e) const { return m_node == e.m_node && m_delta == e.m_delta; } }; struct info { expr_ref m_node; uint_set m_free_vars; unsigned m_size; info(ast_manager & m, expr * n, uint_set const & vars, unsigned sz): m_node(n, m), m_free_vars(vars), m_size(sz) {} }; ast_manager & m; pattern_inference_cfg & m_owner; family_id m_afid; unsigned m_num_bindings; typedef map, default_eq > cache; cache m_cache; ptr_vector m_info; svector m_todo; void visit(expr * n, unsigned delta, bool & visited); bool visit_children(expr * n, unsigned delta); void save(expr * n, unsigned delta, info * i); void save_candidate(expr * n, unsigned delta); void reset(); public: collect(ast_manager & m, pattern_inference_cfg & o):m(m), m_owner(o), m_afid(m.mk_family_id("arith")) {} void operator()(expr * n, unsigned num_bindings); }; collect m_collect; void add_candidate(app * n, uint_set const & s, unsigned size); void filter_looping_patterns(ptr_vector & result); bool has_preferred_patterns(ptr_vector & candidate_patterns, app_ref_buffer & result); void filter_bigger_patterns(ptr_vector const & patterns, ptr_vector & result); class contains_subpattern { pattern_inference_cfg & m_owner; nat_set m_already_processed; ptr_vector m_todo; void save(expr * n); public: contains_subpattern(pattern_inference_cfg & owner): m_owner(owner) {} bool operator()(expr * n); }; contains_subpattern m_contains_subpattern; bool contains_subpattern(expr * n); struct pre_pattern { ptr_vector m_exprs; // elements of the pattern. uint_set m_free_vars; // set of free variables in m_exprs unsigned m_idx; // idx of the next candidate to process. pre_pattern(): m_idx(0) { } }; ptr_vector m_pre_patterns; expr_pattern_match m_database; void candidates2unary_patterns(ptr_vector const & candidate_patterns, ptr_vector & remaining_candidate_patterns, app_ref_buffer & result); void candidates2multi_patterns(unsigned max_num_patterns, ptr_vector const & candidate_patterns, app_ref_buffer & result); void reset_pre_patterns(); /** \brief All minimal unary patterns (i.e., expressions that contain all bound variables) are copied to result. If there are unary patterns, then at most num_extra_multi_patterns multi patterns are created. If there are no unary pattern, then at most 1 + num_extra_multi_patterns multi_patterns are created. */ void mk_patterns(unsigned num_bindings, // IN number of bindings. expr * n, // IN node where the patterns are going to be extracted. unsigned num_no_patterns, // IN num. patterns that should not be used. expr * const * no_patterns, // IN patterns that should not be used. app_ref_buffer & result); // OUT result public: pattern_inference_cfg(ast_manager & m, pattern_inference_params & params); void register_forbidden_family(family_id fid) { SASSERT(fid != m_bfid); m_forbidden.push_back(fid); } /** \brief Register f as a preferred function symbol. The inference algorithm gives preference to patterns rooted by this kind of function symbol. */ void register_preferred(func_decl * f) { m_preferred.insert(f); } 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); void register_preferred(unsigned num, func_decl * const * fs) { for (unsigned i = 0; i < num; i++) register_preferred(fs[i]); } bool is_forbidden(func_decl const * decl) const { family_id fid = decl->get_family_id(); if (fid == m_bfid && decl->get_decl_kind() != OP_TRUE && decl->get_decl_kind() != OP_FALSE) return true; return std::find(m_forbidden.begin(), m_forbidden.end(), fid) != m_forbidden.end(); } bool is_forbidden(app * n) const; }; class pattern_inference_rw : public rewriter_tpl { pattern_inference_cfg m_cfg; public: pattern_inference_rw(ast_manager& m, pattern_inference_params & params); }; #endif /* PATTERN_INFERENCE_H_ */ z3-z3-4.8.7/src/ast/pattern/pattern_inference_params.cpp000066400000000000000000000026271356505360400232330ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: pattern_inference_params.h Abstract: Author: Leonardo de Moura (leonardo) 2012-12-02. Revision History: --*/ #include "ast/pattern/pattern_inference_params.h" #include "ast/pattern/pattern_inference_params_helper.hpp" void pattern_inference_params::updt_params(params_ref const & _p) { pattern_inference_params_helper p(_p); m_pi_max_multi_patterns = p.max_multi_patterns(); m_pi_block_loop_patterns = p.block_loop_patterns(); m_pi_arith = static_cast(p.arith()); m_pi_use_database = p.use_database(); m_pi_arith_weight = p.arith_weight(); m_pi_non_nested_arith_weight = p.non_nested_arith_weight(); m_pi_pull_quantifiers = p.pull_quantifiers(); m_pi_warnings = p.warnings(); } #define DISPLAY_PARAM(X) out << #X"=" << X << std::endl; void pattern_inference_params::display(std::ostream & out) const { DISPLAY_PARAM(m_pi_max_multi_patterns); DISPLAY_PARAM(m_pi_block_loop_patterns); DISPLAY_PARAM(m_pi_arith); DISPLAY_PARAM(m_pi_use_database); DISPLAY_PARAM(m_pi_arith_weight); DISPLAY_PARAM(m_pi_non_nested_arith_weight); DISPLAY_PARAM(m_pi_pull_quantifiers); DISPLAY_PARAM(m_pi_nopat_weight); DISPLAY_PARAM(m_pi_avoid_skolems); DISPLAY_PARAM(m_pi_warnings); }z3-z3-4.8.7/src/ast/pattern/pattern_inference_params.h000066400000000000000000000026571356505360400227030ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: pattern_inference_params.h Abstract: Author: Leonardo de Moura (leonardo) 2008-03-24. Revision History: --*/ #ifndef PATTERN_INFERENCE_PARAMS_H_ #define PATTERN_INFERENCE_PARAMS_H_ #include "util/params.h" enum arith_pattern_inference_kind { AP_NO, // do not infer patterns with arithmetic terms AP_CONSERVATIVE, // only infer patterns with arithmetic terms if there is no other option AP_FULL // always use patterns with arithmetic terms }; struct pattern_inference_params { unsigned m_pi_max_multi_patterns; bool m_pi_block_loop_patterns; arith_pattern_inference_kind m_pi_arith; bool m_pi_use_database; unsigned m_pi_arith_weight; unsigned m_pi_non_nested_arith_weight; bool m_pi_pull_quantifiers; int m_pi_nopat_weight; bool m_pi_avoid_skolems; bool m_pi_warnings; pattern_inference_params(params_ref const & p = params_ref()): m_pi_nopat_weight(-1), m_pi_avoid_skolems(true) { updt_params(p); } void updt_params(params_ref const & _p); void display(std::ostream & out) const; }; #endif /* PATTERN_INFERENCE_PARAMS_H_ */ z3-z3-4.8.7/src/ast/pattern/pattern_inference_params_helper.pyg000066400000000000000000000027431356505360400246060ustar00rootroot00000000000000def_module_params(class_name='pattern_inference_params_helper', module_name='pi', description='pattern inference (heuristics) for universal formulas (without annotation)', export=True, params=(('max_multi_patterns', UINT, 0, 'when patterns are not provided, the prover uses a heuristic to infer them, this option sets the threshold on the number of extra multi-patterns that can be created; by default, the prover creates at most one multi-pattern when there is no unary pattern'), ('block_loop_patterns', BOOL, True, 'block looping patterns during pattern inference'), ('arith', UINT, 1, '0 - do not infer patterns with arithmetic terms, 1 - use patterns with arithmetic terms if there is no other pattern, 2 - always use patterns with arithmetic terms'), ('use_database', BOOL, False, 'use pattern database'), ('arith_weight', UINT, 5, 'default weight for quantifiers where the only available pattern has nested arithmetic terms'), ('non_nested_arith_weight', UINT, 10, 'default weight for quantifiers where the only available pattern has non nested arithmetic terms'), ('pull_quantifiers', BOOL, True, 'pull nested quantifiers, if no pattern was found'), ('warnings', BOOL, False, 'enable/disable warning messages in the pattern inference module'))) z3-z3-4.8.7/src/ast/pb_decl_plugin.cpp000066400000000000000000000225221356505360400174620ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: pb_decl_plugin.cpp Abstract: Cardinality Constraints plugin Author: Nikolaj Bjorner (nbjorner) 2013-05-11 Revision History: --*/ #include "ast/pb_decl_plugin.h" #include "ast/ast_util.h" #include "ast/ast_pp.h" pb_decl_plugin::pb_decl_plugin(): m_at_most_sym("at-most"), m_at_least_sym("at-least"), m_pble_sym("pble"), m_pbge_sym("pbge"), m_pbeq_sym("pbeq") {} func_decl * pb_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { SASSERT(m_manager); ast_manager& m = *m_manager; for (unsigned i = 0; i < arity; ++i) { if (!m.is_bool(domain[i])) { m.raise_exception("invalid non-Boolean sort applied to 'at-most'"); } } symbol sym; switch(k) { case OP_AT_LEAST_K: sym = m_at_least_sym; break; case OP_AT_MOST_K: sym = m_at_most_sym; break; case OP_PB_LE: sym = m_pble_sym; break; case OP_PB_GE: sym = m_pbge_sym; break; case OP_PB_EQ: sym = m_pbeq_sym; break; default: break; } switch(k) { case OP_AT_LEAST_K: case OP_AT_MOST_K: { if (num_parameters != 1 || !parameters[0].is_int() || parameters[0].get_int() < 0) { m.raise_exception("function expects one non-negative integer parameter"); } func_decl_info info(m_family_id, k, 1, parameters); return m.mk_func_decl(sym, arity, domain, m.mk_bool_sort(), info); } case OP_PB_GE: case OP_PB_LE: case OP_PB_EQ: { if (num_parameters != 1 + arity) { m.raise_exception("function expects arity+1 rational parameters"); } vector params; for (unsigned i = 0; i < num_parameters; ++i) { parameter const& p = parameters[i]; if (p.is_int()) { params.push_back(p); } else if (p.is_rational()) { // HACK: ast pretty printer does not work with rationals. rational const& r = p.get_rational(); if (r.is_int32()) { params.push_back(parameter(r.get_int32())); } else { params.push_back(p); } } else { m.raise_exception("functions 'pble/pbge/pbeq' expect arity+1 integer parameters"); } } func_decl_info info(m_family_id, k, num_parameters, params.c_ptr()); return m.mk_func_decl(sym, arity, domain, m.mk_bool_sort(), info); } default: UNREACHABLE(); return nullptr; } } void pb_decl_plugin::get_op_names(svector & op_names, symbol const & logic) { if (logic == symbol::null || logic == "QF_FD" || logic == "ALL" || logic == "HORN") { op_names.push_back(builtin_name(m_at_most_sym.bare_str(), OP_AT_MOST_K)); op_names.push_back(builtin_name(m_at_least_sym.bare_str(), OP_AT_LEAST_K)); op_names.push_back(builtin_name(m_pble_sym.bare_str(), OP_PB_LE)); op_names.push_back(builtin_name(m_pbge_sym.bare_str(), OP_PB_GE)); op_names.push_back(builtin_name(m_pbeq_sym.bare_str(), OP_PB_EQ)); } } void pb_util::normalize(unsigned num_args, rational const* coeffs, rational const& k) { m_coeffs.reset(); bool all_ones = true; for (unsigned i = 0; i < num_args && all_ones; ++i) { all_ones = denominator(coeffs[i]).is_one(); } if (all_ones) { for (unsigned i = 0; i < num_args; ++i) { m_coeffs.push_back(coeffs[i]); } m_k = k; } else { rational d(1); for (unsigned i = 0; i < num_args; ++i) { d = lcm(d, denominator(coeffs[i])); } for (unsigned i = 0; i < num_args; ++i) { m_coeffs.push_back(d*coeffs[i]); } m_k = d*k; } } app * pb_util::mk_le(unsigned num_args, rational const * coeffs, expr * const * args, rational const& k) { normalize(num_args, coeffs, k); m_params.reset(); m_params.push_back(parameter(floor(m_k))); bool all_ones = true; for (unsigned i = 0; i < num_args; ++i) { all_ones &= m_coeffs[i].is_one(); m_params.push_back(parameter(m_coeffs[i])); } if (all_ones && k.is_unsigned() && floor(m_k).is_int32()) { m_params[0] = parameter(floor(m_k).get_int32()); return m.mk_app(m_fid, OP_AT_MOST_K, 1, m_params.c_ptr(), num_args, args, m.mk_bool_sort()); } return m.mk_app(m_fid, OP_PB_LE, m_params.size(), m_params.c_ptr(), num_args, args, m.mk_bool_sort()); } app * pb_util::mk_ge(unsigned num_args, rational const * coeffs, expr * const * args, rational const& k) { normalize(num_args, coeffs, k); m_params.reset(); m_params.push_back(parameter(ceil(m_k))); bool all_ones = true; for (unsigned i = 0; i < num_args; ++i) { all_ones &= m_coeffs[i].is_one(); m_params.push_back(parameter(m_coeffs[i])); } if (all_ones && k.is_unsigned()) { m_params[0] = parameter(ceil(m_k).get_unsigned()); return m.mk_app(m_fid, OP_AT_LEAST_K, 1, m_params.c_ptr(), num_args, args, m.mk_bool_sort()); } return m.mk_app(m_fid, OP_PB_GE, m_params.size(), m_params.c_ptr(), num_args, args, m.mk_bool_sort()); } app * pb_util::mk_eq(unsigned num_args, rational const * coeffs, expr * const * args, rational const& k) { normalize(num_args, coeffs, k); if (!m_k.is_int()) { return m.mk_false(); } if (num_args == 0) { return m_k.is_zero() ? m.mk_true() : m.mk_false(); } m_params.reset(); m_params.push_back(parameter(m_k)); for (unsigned i = 0; i < num_args; ++i) { m_params.push_back(parameter(m_coeffs[i])); } return m.mk_app(m_fid, OP_PB_EQ, m_params.size(), m_params.c_ptr(), num_args, args, m.mk_bool_sort()); } // ax + by < k // <=> // -ax - by >= -k + 1 // <=> // a(1-x) + b(1-y) >= -k + a + b + 1 app * pb_util::mk_lt(unsigned num_args, rational const * _coeffs, expr * const * _args, rational const& _k) { normalize(num_args, _coeffs, _k); expr_ref_vector args(m); for (unsigned i = 0; i < num_args; ++i) { args.push_back(mk_not(m, _args[i])); } m_k = floor(m_k); m_k.neg(); m_k += rational::one(); for (unsigned i = 0; i < num_args; ++i) { m_k += m_coeffs[i]; } return mk_ge(num_args, m_coeffs.c_ptr(), args.c_ptr(), m_k); } app * pb_util::mk_at_most_k(unsigned num_args, expr * const * args, unsigned k) { parameter param(k); return m.mk_app(m_fid, OP_AT_MOST_K, 1, ¶m, num_args, args, m.mk_bool_sort()); } bool pb_util::is_at_most_k(func_decl *a) const { return is_decl_of(a, m_fid, OP_AT_MOST_K); } bool pb_util::is_at_most_k(expr *a, rational& k) const { if (is_at_most_k(a)) { k = get_k(a); return true; } else { return false; } } app * pb_util::mk_at_least_k(unsigned num_args, expr * const * args, unsigned k) { parameter param(k); return m.mk_app(m_fid, OP_AT_LEAST_K, 1, ¶m, num_args, args, m.mk_bool_sort()); } bool pb_util::is_at_least_k(func_decl *a) const { return is_decl_of(a, m_fid, OP_AT_LEAST_K); } bool pb_util::is_at_least_k(expr *a, rational& k) const { if (is_at_least_k(a)) { k = get_k(a); return true; } else { return false; } } rational pb_util::get_k(func_decl *a) const { parameter const& p = a->get_parameter(0); if (is_at_most_k(a) || is_at_least_k(a)) { return to_rational(p); } else { SASSERT(is_le(a) || is_ge(a) || is_eq(a)); return to_rational(p); } } bool pb_util::is_le(func_decl *a) const { return is_decl_of(a, m_fid, OP_PB_LE); } bool pb_util::is_le(expr* a, rational& k) const { if (is_le(a)) { k = get_k(a); return true; } else { return false; } } bool pb_util::is_ge(func_decl *a) const { return is_decl_of(a, m_fid, OP_PB_GE); } bool pb_util::is_ge(expr* a, rational& k) const { if (is_ge(a)) { k = get_k(a); return true; } else { return false; } } bool pb_util::is_eq(func_decl *a) const { return is_decl_of(a, m_fid, OP_PB_EQ); } bool pb_util::is_eq(expr* a, rational& k) const { if (is_eq(a)) { k = get_k(a); return true; } else { return false; } } rational pb_util::get_coeff(func_decl* a, unsigned index) const { if (is_at_most_k(a) || is_at_least_k(a)) { return rational::one(); } SASSERT(is_le(a) || is_ge(a) || is_eq(a)); SASSERT(1 + index < a->get_num_parameters()); return to_rational(a->get_parameter(index + 1)); } rational pb_util::to_rational(parameter const& p) const { if (p.is_int()) { return rational(p.get_int()); } SASSERT(p.is_rational()); return p.get_rational(); } bool pb_util::has_unit_coefficients(func_decl* f) const { if (is_at_most_k(f) || is_at_least_k(f)) return true; unsigned sz = f->get_arity(); for (unsigned i = 0; i < sz; ++i) { if (!get_coeff(f, i).is_one()) return false; } return true; } app* pb_util::mk_fresh_bool() { symbol name = m.mk_fresh_var_name("pb"); func_decl_info info(m_fid, OP_PB_AUX_BOOL, 0, nullptr); return m.mk_const(m.mk_func_decl(name, 0, (sort *const*)nullptr, m.mk_bool_sort(), info)); } z3-z3-4.8.7/src/ast/pb_decl_plugin.h000066400000000000000000000111361356505360400171260ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: pb_decl_plugin.h Abstract: Pseudo-Boolean and Cardinality Constraints plugin Author: Nikolaj Bjorner (nbjorner) 2013-05-11 Notes: (at-most-k x1 .... x_n) means x1 + ... + x_n <= k hence: (not (at-most-k x1 .... x_n)) means x1 + ... + x_n >= k + 1 --*/ #ifndef PB_DECL_PLUGIN_H_ #define PB_DECL_PLUGIN_H_ #include "ast/ast.h" enum pb_op_kind { OP_AT_MOST_K, // at most K Booleans are true. OP_AT_LEAST_K, // at least K Booleans are true. OP_PB_LE, // pseudo-Boolean <= (generalizes at_most_k) OP_PB_GE, // pseudo-Boolean >= OP_PB_EQ, // equality OP_PB_AUX_BOOL, // auxiliary internal Boolean variable. LAST_PB_OP }; class pb_decl_plugin : public decl_plugin { symbol m_at_most_sym; symbol m_at_least_sym; symbol m_pble_sym; symbol m_pbge_sym; symbol m_pbeq_sym; func_decl * mk_at_most(unsigned arity, unsigned k); func_decl * mk_at_least(unsigned arity, unsigned k); func_decl * mk_le(unsigned arity, rational const* coeffs, int k); func_decl * mk_ge(unsigned arity, rational const* coeffs, int k); func_decl * mk_eq(unsigned arity, rational const* coeffs, int k); public: pb_decl_plugin(); ~pb_decl_plugin() override {} sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) override { UNREACHABLE(); return nullptr; } decl_plugin * mk_fresh() override { return alloc(pb_decl_plugin); } // // Contract for func_decl: // parameters[0] - integer (at most k elements) // all sorts are Booleans // parameters[1] .. parameters[arity] - coefficients func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) override; void get_op_names(svector & op_names, symbol const & logic) override; bool is_considered_uninterpreted(func_decl * f) override { return false; } }; class pb_util { ast_manager & m; family_id m_fid; vector m_coeffs; vector m_params; rational m_k; void normalize(unsigned num_args, rational const* coeffs, rational const& k); public: pb_util(ast_manager& m):m(m), m_fid(m.mk_family_id("pb")) {} ast_manager & get_manager() const { return m; } family_id get_family_id() const { return m_fid; } app * mk_at_most_k(unsigned num_args, expr * const * args, unsigned k); app * mk_at_least_k(unsigned num_args, expr * const * args, unsigned k); app * mk_le(unsigned num_args, rational const * coeffs, expr * const * args, rational const& k); app * mk_ge(unsigned num_args, rational const * coeffs, expr * const * args, rational const& k); app * mk_eq(unsigned num_args, rational const * coeffs, expr * const * args, rational const& k); app * mk_lt(unsigned num_args, rational const * coeffs, expr * const * args, rational const& k); bool is_at_most_k(func_decl *a) const; bool is_at_most_k(expr *a) const { return is_app(a) && is_at_most_k(to_app(a)->get_decl()); } bool is_at_most_k(expr *a, rational& k) const; bool is_at_least_k(func_decl *a) const; bool is_at_least_k(expr *a) const { return is_app(a) && is_at_least_k(to_app(a)->get_decl()); } bool is_at_least_k(expr *a, rational& k) const; rational get_k(func_decl *a) const; rational get_k(expr *a) const { return get_k(to_app(a)->get_decl()); } bool is_le(func_decl *a) const; bool is_le(expr *a) const { return is_app(a) && is_le(to_app(a)->get_decl()); } bool is_le(expr* a, rational& k) const; bool is_ge(func_decl* a) const; bool is_ge(expr* a) const { return is_app(a) && is_ge(to_app(a)->get_decl()); } bool is_ge(expr* a, rational& k) const; bool is_aux_bool(func_decl* f) const { return is_decl_of(f, m_fid, OP_PB_AUX_BOOL); } bool is_aux_bool(expr* e) const { return is_app_of(e, m_fid, OP_PB_AUX_BOOL); } rational get_coeff(expr* a, unsigned index) const { return get_coeff(to_app(a)->get_decl(), index); } rational get_coeff(func_decl* a, unsigned index) const; bool has_unit_coefficients(func_decl* f) const; bool has_unit_coefficients(expr* f) const { return is_app(f) && has_unit_coefficients(to_app(f)->get_decl()); } bool is_eq(func_decl* f) const; bool is_eq(expr* e) const { return is_app(e) && is_eq(to_app(e)->get_decl()); } bool is_eq(expr* e, rational& k) const; app* mk_fresh_bool(); private: rational to_rational(parameter const& p) const; }; #endif /* PB_DECL_PLUGIN_H_ */ z3-z3-4.8.7/src/ast/pp.cpp000066400000000000000000000105161356505360400151330ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: pp.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-01-20. Revision History: --*/ #include "ast/pp.h" #include "ast/pp_params.hpp" using namespace format_ns; static std::pair space_upto_line_break(ast_manager & m, format * f) { unsigned r; SASSERT(f->get_family_id() == fm(m).get_family_id("format")); decl_kind k = f->get_decl_kind(); switch(k) { case OP_STRING: { size_t len = strlen(f->get_decl()->get_parameter(0).get_symbol().bare_str()); return std::make_pair(static_cast(len), false); } case OP_CHOICE: return space_upto_line_break(m, to_app(f->get_arg(0))); case OP_COMPOSE: r = 0; for (unsigned i = 0; i < f->get_num_args(); i++) { std::pair pair = space_upto_line_break(m, to_app(f->get_arg(i))); r += pair.first; if (pair.second) return std::make_pair(r, true); } return std::make_pair(r, false); case OP_INDENT: return space_upto_line_break(m, to_app(f->get_arg(0))); case OP_LINE_BREAK: case OP_LINE_BREAK_EXT: return std::make_pair(0, true); default: return std::make_pair(0, false); } } inline bool fits(ast_manager & m, format * f, unsigned space_left) { unsigned s = space_upto_line_break(m, f).first; TRACE("fits", tout << "s: " << s << " space_left " << space_left << "\n";); return s <= space_left; } void pp(std::ostream & out, format * f, ast_manager & m, params_ref const & _p) { pp_params p(_p); unsigned max_width = p.max_width(); unsigned max_ribbon = p.max_ribbon(); unsigned max_num_lines = p.max_num_lines(); unsigned max_indent = p.max_indent(); bool bounded = p.bounded(); bool single_line = p.single_line(); unsigned pos = 0; unsigned ribbon_pos = 0; unsigned line = 0; unsigned len; unsigned i; int space_left; svector > todo; todo.push_back(std::make_pair(f, 0)); app_ref space(mk_string(m, " "), fm(m)); while (!todo.empty()) { if (line >= max_num_lines) return; std::pair pair = todo.back(); format * f = pair.first; unsigned indent = pair.second; todo.pop_back(); SASSERT(f->get_family_id() == fm(m).get_family_id("format")); switch (f->get_decl_kind()) { case OP_STRING: if (bounded && pos > max_width) break; len = static_cast(strlen(f->get_decl()->get_parameter(0).get_symbol().bare_str())); if (bounded && pos + len > max_width) { out << "..."; break; } pos += len; ribbon_pos += len; out << f->get_decl()->get_parameter(0).get_symbol(); break; case OP_INDENT: todo.push_back(std::make_pair(to_app(f->get_arg(0)), std::min(indent + f->get_decl()->get_parameter(0).get_int(), max_indent))); break; case OP_COMPOSE: i = f->get_num_args(); while (i > 0) { --i; todo.push_back(std::make_pair(to_app(f->get_arg(i)), indent)); } break; case OP_CHOICE: space_left = std::min(max_width - pos, max_ribbon - pos); if (space_left > 0 && fits(m, to_app(f->get_arg(0)), space_left)) todo.push_back(std::make_pair(to_app(f->get_arg(0)), indent)); else todo.push_back(std::make_pair(to_app(f->get_arg(1)), indent)); break; case OP_LINE_BREAK: case OP_LINE_BREAK_EXT: if (single_line) { todo.push_back(std::make_pair(space, indent)); break; } pos = indent; ribbon_pos = 0; line++; if (line < max_num_lines) { out << "\n"; for (unsigned i = 0; i < indent; i++) out << " "; } else out << "...\n"; break; default: break; } } } z3-z3-4.8.7/src/ast/pp.h000066400000000000000000000005771356505360400146060ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: pp.h Abstract: Author: Leonardo de Moura (leonardo) 2008-01-20. Revision History: --*/ #ifndef PP_H_ #define PP_H_ #include "ast/format.h" #include "util/params.h" void pp(std::ostream & out, format_ns::format * f, ast_manager & m, params_ref const & p = params_ref()); #endif /* PP_H_ */ z3-z3-4.8.7/src/ast/pp_params.pyg000066400000000000000000000040761356505360400165170ustar00rootroot00000000000000def_module_params('pp', export=True, description='pretty printer', params=(('max_indent', UINT, UINT_MAX, 'max. indentation in pretty printer'), ('max_num_lines', UINT, UINT_MAX, 'max. number of lines to be displayed in pretty printer'), ('max_width', UINT, 80, 'max. width in pretty printer'), ('max_ribbon', UINT, 80, 'max. ribbon (width - indentation) in pretty printer'), ('max_depth', UINT, 5, 'max. term depth (when pretty printing SMT2 terms/formulas)'), ('min_alias_size', UINT, 10, 'min. size for creating an alias for a shared term (when pretty printing SMT2 terms/formulas)'), ('decimal', BOOL, False, 'pretty print real numbers using decimal notation (the output may be truncated). Z3 adds a ? if the value is not precise'), ('decimal_precision', UINT, 10, 'maximum number of decimal places to be used when pp.decimal=true'), ('bv_literals', BOOL, True, 'use Bit-Vector literals (e.g, #x0F and #b0101) during pretty printing'), ('fp_real_literals', BOOL, False, 'use real-numbered floating point literals (e.g, +1.0p-1) during pretty printing'), ('bv_neg', BOOL, False, 'use bvneg when displaying Bit-Vector literals where the most significant bit is 1'), ('flat_assoc', BOOL, True, 'flat associative operators (when pretty printing SMT2 terms/formulas)'), ('fixed_indent', BOOL, False, 'use a fixed indentation for applications'), ('single_line', BOOL, False, 'ignore line breaks when true'), ('bounded', BOOL, False, 'ignore characters exceeding max width'), ('pretty_proof', BOOL, False, 'use slower, but prettier, printer for proofs'), ('simplify_implies', BOOL, True, 'simplify nested implications for pretty printing'))) z3-z3-4.8.7/src/ast/proofs/000077500000000000000000000000001356505360400153155ustar00rootroot00000000000000z3-z3-4.8.7/src/ast/proofs/CMakeLists.txt000066400000000000000000000001631356505360400200550ustar00rootroot00000000000000z3_add_component(proofs SOURCES proof_checker.cpp proof_utils.cpp COMPONENT_DEPENDENCIES rewriter )z3-z3-4.8.7/src/ast/proofs/proof_checker.cpp000066400000000000000000001272631356505360400206450ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "ast/proofs/proof_checker.h" #include "ast/ast_ll_pp.h" #include "ast/ast_pp.h" #include "ast/ast_smt_pp.h" #include "ast/arith_decl_plugin.h" #include "ast/rewriter/th_rewriter.h" #include "ast/rewriter/var_subst.h" #define IS_EQUIV(_e_) m.is_eq(_e_) #define SAME_OP(_d1_, _d2_) ((_d1_ == _d2_) || (IS_EQUIV(_d1_) && IS_EQUIV(_d2_))) proof_checker::hyp_decl_plugin::hyp_decl_plugin() : m_cons(nullptr), m_atom(nullptr), m_nil(nullptr), m_cell(nullptr) { } void proof_checker::hyp_decl_plugin::finalize() { m_manager->dec_ref(m_cons); m_manager->dec_ref(m_atom); m_manager->dec_ref(m_nil); m_manager->dec_ref(m_cell); } void proof_checker::hyp_decl_plugin::set_manager(ast_manager* m, family_id id) { decl_plugin::set_manager(m,id); m_cell = m->mk_sort(symbol("cell"), sort_info(id, CELL_SORT)); m_cons = m->mk_func_decl(symbol("cons"), m_cell, m_cell, m_cell, func_decl_info(id, OP_CONS)); m_atom = m->mk_func_decl(symbol("atom"), m->mk_bool_sort(), m_cell, func_decl_info(id, OP_ATOM)); m_nil = m->mk_const_decl(symbol("nil"), m_cell, func_decl_info(id, OP_NIL)); m->inc_ref(m_cell); m->inc_ref(m_cons); m->inc_ref(m_atom); m->inc_ref(m_nil); } sort * proof_checker::hyp_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter const* parameters) { SASSERT(k == CELL_SORT); return m_cell; } func_decl * proof_checker::hyp_decl_plugin::mk_func_decl(decl_kind k) { switch(k) { case OP_CONS: return m_cons; case OP_ATOM: return m_atom; case OP_NIL: return m_nil; default: UNREACHABLE(); return nullptr; } } func_decl * proof_checker::hyp_decl_plugin::mk_func_decl( decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { return mk_func_decl(k); } func_decl * proof_checker::hyp_decl_plugin::mk_func_decl( decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned num_args, expr * const * args, sort * range) { return mk_func_decl(k); } void proof_checker::hyp_decl_plugin::get_op_names(svector & op_names, symbol const & logic) { if (logic == symbol::null) { op_names.push_back(builtin_name("cons", OP_CONS)); op_names.push_back(builtin_name("atom", OP_ATOM)); op_names.push_back(builtin_name("nil", OP_NIL)); } } void proof_checker::hyp_decl_plugin::get_sort_names(svector & sort_names, symbol const & logic) { if (logic == symbol::null) { sort_names.push_back(builtin_name("cell", CELL_SORT)); } } proof_checker::proof_checker(ast_manager& m) : m(m), m_todo(m), m_marked(), m_pinned(m), m_nil(m), m_dump_lemmas(false), m_logic("AUFLIRA"), m_proof_lemma_id(0) { symbol fam_name("proof_hypothesis"); if (!m.has_plugin(fam_name)) { m.register_plugin(fam_name, alloc(hyp_decl_plugin)); } m_hyp_fid = m.mk_family_id(fam_name); // m_spc_fid = m.get_family_id("spc"); m_nil = m.mk_const(m_hyp_fid, OP_NIL); } bool proof_checker::check(proof* p, expr_ref_vector& side_conditions) { proof_ref curr(m); m_todo.push_back(p); bool result = true; while (result && !m_todo.empty()) { curr = m_todo.back(); m_todo.pop_back(); result = check1(curr.get(), side_conditions); if (!result) { IF_VERBOSE(0, ast_ll_pp(verbose_stream() << "Proof check failed\n", m, curr.get());); UNREACHABLE(); } } m_hypotheses.reset(); m_pinned.reset(); m_todo.reset(); m_marked.reset(); return result; } bool proof_checker::check1(proof* p, expr_ref_vector& side_conditions) { if (p->get_family_id() == m.get_basic_family_id()) { return check1_basic(p, side_conditions); } #if 0 if (p->get_family_id() == m_spc_fid) { return check1_spc(p, side_conditions); } #endif return false; } bool proof_checker::check1_spc(proof* p, expr_ref_vector& side_conditions) { #if 0 decl_kind k = p->get_decl_kind(); bool is_univ = false; expr_ref fact(m), fml(m); expr_ref body(m), fml1(m), fml2(m); sort_ref_vector sorts(m); proof_ref p1(m), p2(m); proof_ref_vector proofs(m); if (match_proof(p, proofs)) { for (proof* pr : proofs) { add_premise(pr); } } switch(k) { case PR_DEMODULATION: { if (match_proof(p, p1) && match_fact(p, fact) && match_fact(p1.get(), fml) && match_quantifier(fml.get(), is_univ, sorts, body) && is_univ) { // TBD: check that fml is an instance of body. return true; } return false; } case PR_SPC_REWRITE: case PR_SUPERPOSITION: case PR_EQUALITY_RESOLUTION: case PR_SPC_RESOLUTION: case PR_FACTORING: case PR_SPC_DER: { if (match_fact(p, fact)) { expr_ref_vector rewrite_eq(m); rewrite_eq.push_back(fact.get()); for (unsigned i = 0; i < proofs.size(); ++i) { if (match_fact(proofs[i].get(), fml)) { rewrite_eq.push_back(m.mk_not(fml.get())); } } expr_ref rewrite_cond(m); rewrite_cond = m.mk_or(rewrite_eq.size(), rewrite_eq.c_ptr()); side_conditions.push_back(rewrite_cond.get()); return true; } return false; } default: UNREACHABLE(); } return false; #else return true; #endif } bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { decl_kind k = p->get_decl_kind(); expr* fml0 = nullptr, *fml1 = nullptr, *fml2 = nullptr, *fml = nullptr; expr* t1 = nullptr, *t2 = nullptr; expr* s1 = nullptr, *s2 = nullptr; expr* u1 = nullptr, *u2 = nullptr; expr* fact = nullptr, *body1 = nullptr; expr* l1 = nullptr, *l2 = nullptr, *r1 = nullptr, *r2 = nullptr; func_decl* d1 = nullptr, *d2 = nullptr, *d3 = nullptr; proof* p0 = nullptr, *p1 = nullptr, *p2 = nullptr; proof_ref_vector proofs(m); func_decl* f1 = nullptr, *f2 = nullptr; ptr_vector terms1, terms2, terms; sort_ref_vector decls1(m), decls2(m); if (match_proof(p, proofs)) { for (proof* pr : proofs) { add_premise(pr); } } switch(k) { case PR_UNDEF: return true; case PR_TRUE: return true; case PR_ASSERTED: return true; case PR_GOAL: return true; case PR_MODUS_PONENS: { if (match_fact(p, fact) && match_proof(p, p0, p1) && match_fact(p0, fml0) && match_fact(p1, fml1) && (match_implies(fml1, t1, t2) || match_iff(fml1, t1, t2)) && (fml0 == t1) && (fact == t2)) { return true; } UNREACHABLE(); return false; } case PR_REFLEXIVITY: { if (match_fact(p, fact) && match_proof(p) && (match_equiv(fact, t1, t2) || match_oeq(fact, t1, t2)) && (t1 == t2)) { return true; } UNREACHABLE(); return false; } case PR_SYMMETRY: { if (match_fact(p, fact) && match_proof(p, p1) && match_fact(p1, fml) && match_binary(fact, d1, l1, r1) && match_binary(fml, d2, l2, r2) && SAME_OP(d1, d2) && l1 == r2 && r1 == l2) { // TBD d1, d2 is a symmetric predicate return true; } UNREACHABLE(); return false; } case PR_TRANSITIVITY: { if (match_fact(p, fact) && match_proof(p, p1, p2) && match_fact(p1, fml1) && match_fact(p2, fml2) && match_binary(fact, d1, t1, t2) && match_binary(fml1, d2, s1, s2) && match_binary(fml2, d3, u1, u2) && d1 == d2 && d2 == d3 && t1 == s1 && s2 == u1 && u2 == t2) { // TBD d1 is some transitive predicate. return true; } UNREACHABLE(); return false; } case PR_TRANSITIVITY_STAR: { if (match_fact(p, fact) && match_binary(fact, d1, t1, t2)) { u_map vertices; // TBD check that d1 is transitive, symmetric. for (proof* pr : proofs) { if (match_fact(pr, fml) && match_binary(fml, d2, s1, s2) && d1 == d2) { unsigned id1 = s1->get_id(); unsigned id2 = s2->get_id(); #define INSERT(_id) if (vertices.contains(_id)) vertices.remove(_id); else vertices.insert(_id, true); INSERT(id1); INSERT(id2); } else { UNREACHABLE(); return false; } } return vertices.size() == 2 && vertices.contains(t1->get_id()) && vertices.contains(t2->get_id()); } UNREACHABLE(); return false; } case PR_MONOTONICITY: { TRACE("proof_checker", tout << mk_bounded_pp(p, m, 3) << "\n";); if (match_fact(p, fact) && match_binary(fact, d1, t1, t2) && match_app(t1, f1, terms1) && match_app(t2, f2, terms2) && f1 == f2 && terms1.size() == terms2.size()) { // TBD: d1 is monotone. for (unsigned i = 0; i < terms1.size(); ++i) { expr* term1 = terms1[i]; expr* term2 = terms2[i]; if (term1 != term2) { bool found = false; for (proof* pr : proofs) { found |= match_fact(pr, fml) && match_binary(fml, d2, s1, s2) && SAME_OP(d1, d2) && s1 == term1 && s2 == term2; } if (!found) { UNREACHABLE(); return false; } } } return true; } UNREACHABLE(); return false; } case PR_QUANT_INTRO: { if (match_proof(p, p1) && match_fact(p, fact) && match_fact(p1, fml) && (is_lambda(fact) || is_lambda(fml))) return true; if (match_proof(p, p1) && match_fact(p, fact) && match_fact(p1, fml) && (match_iff(fact, t1, t2) || match_oeq(fact, t1, t2)) && (match_iff(fml, s1, s2) || match_oeq(fml, s1, s2)) && m.is_oeq(fact) == m.is_oeq(fml) && is_quantifier(t1) && is_quantifier(t2) && to_quantifier(t1)->get_expr() == s1 && to_quantifier(t2)->get_expr() == s2 && to_quantifier(t1)->get_num_decls() == to_quantifier(t2)->get_num_decls() && to_quantifier(t1)->get_kind() == to_quantifier(t2)->get_kind()) { quantifier* q1 = to_quantifier(t1); quantifier* q2 = to_quantifier(t2); for (unsigned i = 0; i < q1->get_num_decls(); ++i) { if (q1->get_decl_sort(i) != q2->get_decl_sort(i)) { // term is not well-typed. UNREACHABLE(); return false; } } return true; } UNREACHABLE(); return false; } case PR_BIND: // it is a lambda expression returning a proof object. if (!is_lambda(to_app(p)->get_arg(0))) return false; // check that body is a proof object. return true; case PR_DISTRIBUTIVITY: { if (match_fact(p, fact) && match_proof(p) && match_equiv(fact, t1, t2)) { side_conditions.push_back(fact); return true; } UNREACHABLE(); return false; } case PR_AND_ELIM: { if (match_proof(p, p1) && match_fact(p, fact) && match_fact(p1, fml) && match_and(fml, terms)) { for (expr* t : terms) if (t == fact) return true; } UNREACHABLE(); return false; } case PR_NOT_OR_ELIM: { if (match_proof(p, p1) && match_fact(p, fact) && match_fact(p1, fml) && match_not(fml, fml1) && match_or(fml1, terms)) { for (expr* t : terms) { if (match_negated(t, fact)) { return true; } } } UNREACHABLE(); return false; } case PR_REWRITE: { if (match_fact(p, fact) && match_proof(p) && match_equiv(fact, t1, t2)) { side_conditions.push_back(fact); return true; } IF_VERBOSE(0, verbose_stream() << "Expected proof of equality:\n" << mk_bounded_pp(p, m);); return false; } case PR_REWRITE_STAR: { if (match_fact(p, fact) && match_equiv(fact, t1, t2)) { expr_ref_vector rewrite_eq(m); rewrite_eq.push_back(fact); for (proof* pr : proofs) { if (match_fact(pr, fml)) { rewrite_eq.push_back(m.mk_not(fml)); } } expr_ref rewrite_cond(m); rewrite_cond = m.mk_or(rewrite_eq.size(), rewrite_eq.c_ptr()); side_conditions.push_back(rewrite_cond.get()); return true; } IF_VERBOSE(0, verbose_stream() << "Expected proof of equality:\n" << mk_bounded_pp(p, m);); return false; } case PR_PULL_QUANT: { if (match_proof(p) && match_fact(p, fact) && match_iff(fact, t1, t2) && is_quantifier(t2)) { // TBD: check the enchilada. return true; } IF_VERBOSE(0, verbose_stream() << "Expected proof of equivalence with a quantifier:\n" << mk_bounded_pp(p, m);); return false; } case PR_PUSH_QUANT: { if (match_proof(p) && match_fact(p, fact) && match_iff(fact, t1, t2) && is_quantifier(t1) && match_and(to_quantifier(t1)->get_expr(), terms1) && match_and(t2, terms2) && terms1.size() == terms2.size()) { quantifier * q1 = to_quantifier(t1); for (unsigned i = 0; i < terms1.size(); ++i) { if (is_quantifier(terms2[i]) && to_quantifier(terms2[i])->get_expr() == terms1[i] && to_quantifier(terms2[i])->get_num_decls() == q1->get_num_decls()) { // ok. } else { return false; } } } UNREACHABLE(); return false; } case PR_ELIM_UNUSED_VARS: { if (match_proof(p) && match_fact(p, fact) && match_iff(fact, t1, t2)) { // TBD: // match_quantifier(t1, is_forall1, decls1, body1) // filter out decls1 that occur in body1. // if list is empty, then t2 could be just body1. // otherwise t2 is also a quantifier. return true; } IF_VERBOSE(0, verbose_stream() << "does not match last rule: " << mk_pp(p, m) << "\n";); return false; } case PR_DER: { bool is_forall = false; if (match_proof(p) && match_fact(p, fact) && match_iff(fact, t1, t2) && match_quantifier(t1, is_forall, decls1, body1) && is_forall) { // TBD: check that terms are set of equalities. // t2 is an instance of a predicate in terms1 return true; } IF_VERBOSE(0, verbose_stream() << "does not match last rule: " << mk_pp(p, m) << "\n";); return false; } case PR_HYPOTHESIS: { // TBD all branches with hypotheses must be closed by a later lemma. if (match_proof(p) && match_fact(p, fml)) { return true; } return false; } case PR_LEMMA: { if (match_proof(p, p1) && match_fact(p, fact) && match_fact(p1, fml) && m.is_false(fml)) { expr_ref_vector hypotheses(m); expr_ref_vector ors(m); get_hypotheses(p1, hypotheses); if (hypotheses.size() == 1 && match_negated(hypotheses.get(0), fact)) { // Suppose fact is (or a b c) and hypothesis is (not (or a b c)) // That is, (or a b c) should be viewed as a 'quoted expression' and a unary clause, // instead of a clause with three literals. return true; } get_ors(fact, ors); for (unsigned i = 0; i < hypotheses.size(); ++i) { bool found = false; unsigned j; for (j = 0; !found && j < ors.size(); ++j) { found = match_negated(ors[j].get(), hypotheses[i].get()); } if (!found) { TRACE("pr_lemma_bug", tout << "i: " << i << "\n"; tout << "ORs:\n" << ors << "\n"; tout << "HYPOTHESIS:\n" << hypotheses << "\n"; ); UNREACHABLE(); return false; } TRACE("proof_checker", tout << "Matched:\n"; ast_ll_pp(tout, m, hypotheses[i].get()); ast_ll_pp(tout, m, ors[j-1].get());); } return true; } UNREACHABLE(); return false; } case PR_UNIT_RESOLUTION: { if (match_fact(p, fact) && proofs.size() == 2 && match_fact(proofs[0].get(), fml1) && match_fact(proofs[1].get(), fml2) && m.is_complement(fml1, fml2) && m.is_false(fact)) { return true; } if (match_fact(p, fact) && proofs.size() > 1 && match_fact(proofs.get(0), fml) && match_or(fml, terms1)) { for (unsigned i = 1; i < proofs.size(); ++i) { if (!match_fact(proofs.get(i), fml2)) { return false; } bool found = false; for (unsigned j = 0; !found && j < terms1.size(); ++j) { if (m.is_complement(terms1.get(j), fml2)) { found = true; if (j + 1 < terms1.size()) { terms1[j] = terms1.get(terms1.size()-1); } terms1.resize(terms1.size()-1); } } if (!found) { TRACE("pr_unit_bug", tout << "Parents:\n"; for (unsigned i = 0; i < proofs.size(); i++) { expr* p = nullptr; match_fact(proofs.get(i), p); tout << mk_pp(p, m) << "\n"; } tout << "Fact:\n"; tout << mk_pp(fact, m) << "\n"; tout << "Clause:\n"; tout << mk_pp(fml, m) << "\n"; tout << "Could not find premise " << mk_pp(fml2, m) << "\n"; ); UNREACHABLE(); return false; } } switch(terms1.size()) { case 0: return m.is_false(fact); case 1: return fact == terms1[0]; default: { if (match_or(fact, terms2)) { for (expr* term1 : terms1) { bool found = false; for (expr* term2 : terms2) { found = term1 == term2; if (found) break; } if (!found) { IF_VERBOSE(0, verbose_stream() << "Premise not found:" << mk_pp(term1, m) << "\n";); return false; } } return true; } IF_VERBOSE(0, verbose_stream() << "Conclusion is not a disjunction:\n"; verbose_stream() << mk_pp(fml, m) << "\n"; verbose_stream() << mk_pp(fact, m) << "\n";); return false; } } } UNREACHABLE(); return false; } case PR_IFF_TRUE: { // iff_true(?rule(?p1, ?fml), (iff ?fml true)) if (match_proof(p, p1) && match_fact(p, fact) && match_fact(p1, fml1) && match_iff(fact, l1, r1) && fml1 == l1 && r1 == m.mk_true()) { return true; } UNREACHABLE(); return false; } case PR_IFF_FALSE: { // iff_false(?rule(?p1, (not ?fml)), (iff ?fml false)) if (match_proof(p, p1) && match_fact(p, fact) && match_fact(p1, fml1) && match_iff(fact, l1, r1) && match_not(fml1, t1) && t1 == l1 && r1 == m.mk_false()) { return true; } UNREACHABLE(); return false; } case PR_COMMUTATIVITY: { // commutativity(= (?c ?t1 ?t2) (?c ?t2 ?t1)) if (match_fact(p, fact) && match_proof(p) && match_equiv(fact, t1, t2) && match_binary(t1, d1, s1, s2) && match_binary(t2, d2, u1, u2) && s1 == u2 && s2 == u1 && d1 == d2 && d1->is_commutative()) { return true; } UNREACHABLE(); return false; } case PR_DEF_AXIOM: { // axiom(?fml) if (match_fact(p, fact) && match_proof(p) && m.is_bool(fact)) { return true; } UNREACHABLE(); return false; } case PR_DEF_INTRO: { // def_intro(?fml) // // ?fml: forall x . ~p(x) or e(x) and forall x . ~e(x) or p(x) // : forall x . ~cond(x) or f(x) = then(x) and forall x . cond(x) or f(x) = else(x) // : forall x . f(x) = e(x) // if (match_fact(p, fact) && match_proof(p) && m.is_bool(fact)) { return true; } UNREACHABLE(); return false; } case PR_APPLY_DEF: { if (match_fact(p, fact) && match_oeq(fact, t1, t2)) { // TBD: must definitions be in proofs? return true; } UNREACHABLE(); return false; } case PR_IFF_OEQ: { // axiom(?rule(?p1,(iff ?t1 ?t2)), (~ ?t1 ?t2)) if (match_fact(p, fact) && match_proof(p, p1) && match_oeq(fact, t1, t2) && match_fact(p1, fml) && match_iff(fml, s1, s2) && s1 == t1 && s2 == t2) { return true; } UNREACHABLE(); return false; } case PR_NNF_POS: { // TBD: return true; } case PR_NNF_NEG: { // TBD: return true; } case PR_SKOLEMIZE: { // (exists ?x (p ?x y)) -> (p (sk y) y) // (not (forall ?x (p ?x y))) -> (not (p (sk y) y)) if (match_fact(p, fact) && match_oeq(fact, t1, t2)) { quantifier* q = nullptr; expr* e = t1; bool is_forall = false; if (match_not(t1, s1)) { e = s1; is_forall = true; } if (is_quantifier(e)) { SASSERT(!is_lambda(e)); q = to_quantifier(e); // TBD check that quantifier is properly instantiated return is_forall == ::is_forall(q); } } UNREACHABLE(); return false; } case PR_MODUS_PONENS_OEQ: { if (match_fact(p, fact) && match_proof(p, p0, p1) && match_fact(p0, fml0) && match_fact(p1, fml1) && match_oeq(fml1, t1, t2) && fml0 == t1 && fact == t2) { return true; } UNREACHABLE(); return false; } case PR_TH_LEMMA: { SASSERT(p->get_decl()->get_num_parameters() > 0); SASSERT(p->get_decl()->get_parameter(0).is_symbol()); if (symbol("arith") == p->get_decl()->get_parameter(0).get_symbol()) { return check_arith_proof(p); } dump_proof(p); return true; } case PR_QUANT_INST: { // TODO return true; } case PR_HYPER_RESOLVE: { proof_ref_vector premises(m); expr_ref_vector fmls(m); expr_ref conclusion(m), premise(m), premise0(m), premise1(m); svector > positions; vector substs; VERIFY(m.is_hyper_resolve(p, premises, conclusion, positions, substs)); var_subst vs(m, false); for (unsigned i = 0; i < premises.size(); ++i) { expr_ref_vector const& sub = substs[i]; premise = m.get_fact(premises[i].get()); if (!sub.empty()) { if (is_forall(premise)) { // SASSERT(to_quantifier(premise)->get_num_decls() == sub.size()); premise = to_quantifier(premise)->get_expr(); } premise = vs(premise, sub.size(), sub.c_ptr()); } fmls.push_back(premise.get()); TRACE("proof_checker", tout << mk_pp(premise.get(), m) << "\n"; for (unsigned j = 0; j < sub.size(); ++j) { tout << mk_pp(sub[j], m) << " "; } tout << "\n";); } premise0 = fmls[0].get(); for (unsigned i = 1; i < fmls.size(); ++i) { expr_ref lit1(m), lit2(m); expr* lit3 = nullptr; std::pair pos = positions[i-1]; premise1 = fmls[i].get(); set_false(premise0, pos.first, lit1); set_false(premise1, pos.second, lit2); if (m.is_not(lit1, lit3) && lit3 == lit2) { // ok } else if (m.is_not(lit2, lit3) && lit3 == lit1) { // ok } else { IF_VERBOSE(0, verbose_stream() << "Could not establish complementarity for:\n" << mk_pp(lit1, m) << "\n" << mk_pp(lit2, m) << "\n" << mk_pp(p, m) << "\n";); } fmls[i] = premise1; } fmls[0] = premise0; premise0 = m.mk_or(fmls.size(), fmls.c_ptr()); if (is_forall(conclusion)) { quantifier* q = to_quantifier(conclusion); premise0 = m.mk_iff(premise0, q->get_expr()); premise0 = m.mk_forall(q->get_num_decls(), q->get_decl_sorts(), q->get_decl_names(), premise0); } else { premise0 = m.mk_iff(premise0, conclusion); } side_conditions.push_back(premise0); return true; } default: UNREACHABLE(); return false; } } /** \brief Premises of the rules are of the form (or l0 l1 l2 .. ln) or (=> (and ln+1 ln+2 .. ln+m) l0) or in the most general (ground) form: (=> (and ln+1 ln+2 .. ln+m) (or l0 l1 .. ln-1)) In other words we use the following (Prolog style) convention for Horn implications: The head of a Horn implication is position 0, the first conjunct in the body of an implication is position 1 the second conjunct in the body of an implication is position 2 Set the position provided in the argument to 'false'. */ void proof_checker::set_false(expr_ref& e, unsigned position, expr_ref& lit) { app* a = to_app(e); expr* head, *body; expr_ref_vector args(m); if (m.is_or(e)) { SASSERT(position < a->get_num_args()); args.append(a->get_num_args(), a->get_args()); lit = args[position].get(); args[position] = m.mk_false(); e = m.mk_or(args.size(), args.c_ptr()); } else if (m.is_implies(e, body, head)) { expr* const* heads = &head; unsigned num_heads = 1; if (m.is_or(head)) { num_heads = to_app(head)->get_num_args(); heads = to_app(head)->get_args(); } expr*const* bodies = &body; unsigned num_bodies = 1; if (m.is_and(body)) { num_bodies = to_app(body)->get_num_args(); bodies = to_app(body)->get_args(); } if (position < num_heads) { args.append(num_heads, heads); lit = args[position].get(); args[position] = m.mk_false(); e = m.mk_implies(body, m.mk_or(args.size(), args.c_ptr())); } else { position -= num_heads; args.append(num_bodies, bodies); lit = m.mk_not(args[position].get()); args[position] = m.mk_true(); e = m.mk_implies(m.mk_and(args.size(), args.c_ptr()), head); } } else if (position == 0) { lit = e; e = m.mk_false(); } else { IF_VERBOSE(0, verbose_stream() << position << "\n" << mk_pp(e, m) << "\n";); UNREACHABLE(); } } bool proof_checker::match_fact(proof const* p, expr*& fact) const { if (m.is_proof(p) && m.has_fact(p)) { fact = m.get_fact(p); return true; } return false; } void proof_checker::add_premise(proof* p) { if (!m_marked.is_marked(p)) { m_marked.mark(p, true); m_todo.push_back(p); } } bool proof_checker::match_proof(proof const* p) const { return m.is_proof(p) && m.get_num_parents(p) == 0; } bool proof_checker::match_proof(proof const* p, proof*& p0) const { if (m.is_proof(p) && m.get_num_parents(p) == 1) { p0 = m.get_parent(p, 0); return true; } return false; } bool proof_checker::match_proof(proof const* p, proof*& p0, proof*& p1) const { if (m.is_proof(p) && m.get_num_parents(p) == 2) { p0 = m.get_parent(p, 0); p1 = m.get_parent(p, 1); return true; } return false; } bool proof_checker::match_proof(proof const* p, proof_ref_vector& parents) const { if (m.is_proof(p)) { for (unsigned i = 0; i < m.get_num_parents(p); ++i) { parents.push_back(m.get_parent(p, i)); } return true; } return false; } bool proof_checker::match_binary(expr const* e, func_decl*& d, expr*& t1, expr*& t2) const { if (e->get_kind() == AST_APP && to_app(e)->get_num_args() == 2) { d = to_app(e)->get_decl(); t1 = to_app(e)->get_arg(0); t2 = to_app(e)->get_arg(1); return true; } return false; } bool proof_checker::match_app(expr const* e, func_decl*& d, ptr_vector& terms) const { if (e->get_kind() == AST_APP) { d = to_app(e)->get_decl(); for (expr* arg : *to_app(e)) { terms.push_back(arg); } return true; } return false; } bool proof_checker::match_quantifier(expr const* e, bool& is_univ, sort_ref_vector& sorts, expr*& body) const { if (is_quantifier(e)) { quantifier const* q = to_quantifier(e); is_univ = is_forall(q); body = q->get_expr(); for (unsigned i = 0; i < q->get_num_decls(); ++i) { sorts.push_back(q->get_decl_sort(i)); } return true; } return false; } bool proof_checker::match_op(expr const* e, decl_kind k, expr*& t1, expr*& t2) const { if (e->get_kind() == AST_APP && to_app(e)->get_family_id() == m.get_basic_family_id() && to_app(e)->get_decl_kind() == k && to_app(e)->get_num_args() == 2) { t1 = to_app(e)->get_arg(0); t2 = to_app(e)->get_arg(1); return true; } return false; } bool proof_checker::match_op(expr const* e, decl_kind k, ptr_vector& terms) const { if (e->get_kind() == AST_APP && to_app(e)->get_family_id() == m.get_basic_family_id() && to_app(e)->get_decl_kind() == k) { for (expr* arg : *to_app(e)) terms.push_back(arg); return true; } return false; } bool proof_checker::match_op(expr const* e, decl_kind k, expr*& t) const { if (e->get_kind() == AST_APP && to_app(e)->get_family_id() == m.get_basic_family_id() && to_app(e)->get_decl_kind() == k && to_app(e)->get_num_args() == 1) { t = to_app(e)->get_arg(0); return true; } return false; } bool proof_checker::match_not(expr const* e, expr*& t) const { return match_op(e, OP_NOT, t); } bool proof_checker::match_or(expr const* e, ptr_vector& terms) const { return match_op(e, OP_OR, terms); } bool proof_checker::match_and(expr const* e, ptr_vector& terms) const { return match_op(e, OP_AND, terms); } bool proof_checker::match_iff(expr const* e, expr*& t1, expr*& t2) const { return match_op(e, OP_EQ, t1, t2) && m.is_bool(t1); } bool proof_checker::match_equiv(expr const* e, expr*& t1, expr*& t2) const { return match_oeq(e, t1, t2) || match_eq(e, t1, t2); } bool proof_checker::match_implies(expr const* e, expr*& t1, expr*& t2) const { return match_op(e, OP_IMPLIES, t1, t2); } bool proof_checker::match_eq(expr const* e, expr*& t1, expr*& t2) const { return match_op(e, OP_EQ, t1, t2); } bool proof_checker::match_oeq(expr const* e, expr*& t1, expr*& t2) const { return match_op(e, OP_OEQ, t1, t2); } bool proof_checker::match_negated(expr const* a, expr* b) const { expr* t = nullptr; return (match_not(a, t) && t == b) || (match_not(b, t) && t == a); } void proof_checker::get_ors(expr* e, expr_ref_vector& ors) { ptr_buffer buffer; if (m.is_or(e)) { app* a = to_app(e); ors.append(a->get_num_args(), a->get_args()); } else { ors.push_back(e); } } void proof_checker::get_hypotheses(proof* p, expr_ref_vector& ante) { ptr_vector stack; expr* h = nullptr, *hyp = nullptr; stack.push_back(p); while (!stack.empty()) { p = stack.back(); SASSERT(m.is_proof(p)); if (m_hypotheses.contains(p)) { stack.pop_back(); continue; } if (is_hypothesis(p) && match_fact(p, hyp)) { hyp = mk_atom(hyp); m_pinned.push_back(hyp); m_hypotheses.insert(p, hyp); stack.pop_back(); continue; } // in this system all hypotheses get bound by lemmas. if (m.is_lemma(p)) { m_hypotheses.insert(p, mk_nil()); stack.pop_back(); continue; } bool all_found = true; ptr_vector hyps; for (unsigned i = 0; i < m.get_num_parents(p); ++i) { proof* p_i = m.get_parent(p, i); if (m_hypotheses.find(p_i, h)) { hyps.push_back(h); } else { stack.push_back(p_i); all_found = false; } } if (all_found) { h = mk_hyp(hyps.size(), hyps.c_ptr()); m_pinned.push_back(h); m_hypotheses.insert(p, h); stack.pop_back(); } } // // dis-assemble the set of obtained hypotheses. // if (!m_hypotheses.find(p, h)) { UNREACHABLE(); } ptr_buffer hyps; ptr_buffer todo; expr_mark mark; todo.push_back(h); expr* a = nullptr, *b = nullptr; while (!todo.empty()) { h = todo.back(); todo.pop_back(); if (mark.is_marked(h)) { continue; } mark.mark(h, true); if (match_cons(h, a, b)) { todo.push_back(a); todo.push_back(b); } else if (match_atom(h, a)) { ante.push_back(a); } else { SASSERT(match_nil(h)); } } TRACE("proof_checker", { ast_ll_pp(tout << "Getting hypotheses from: ", m, p); tout << "Found hypotheses:\n"; for (unsigned i = 0; i < ante.size(); ++i) { ast_ll_pp(tout, m, ante[i].get()); } }); } bool proof_checker::match_nil(expr const* e) const { return is_app(e) && to_app(e)->get_family_id() == m_hyp_fid && to_app(e)->get_decl_kind() == OP_NIL; } bool proof_checker::match_cons(expr const* e, expr*& a, expr*& b) const { if (is_app(e) && to_app(e)->get_family_id() == m_hyp_fid && to_app(e)->get_decl_kind() == OP_CONS) { a = to_app(e)->get_arg(0); b = to_app(e)->get_arg(1); return true; } return false; } bool proof_checker::match_atom(expr const* e, expr*& a) const { if (is_app(e) && to_app(e)->get_family_id() == m_hyp_fid && to_app(e)->get_decl_kind() == OP_ATOM) { a = to_app(e)->get_arg(0); return true; } return false; } expr* proof_checker::mk_atom(expr* e) { return m.mk_app(m_hyp_fid, OP_ATOM, e); } expr* proof_checker::mk_cons(expr* a, expr* b) { return m.mk_app(m_hyp_fid, OP_CONS, a, b); } expr* proof_checker::mk_nil() { return m_nil.get(); } bool proof_checker::is_hypothesis(proof const* p) const { return m.is_proof(p) && p->get_decl_kind() == PR_HYPOTHESIS; } expr* proof_checker::mk_hyp(unsigned num_hyps, expr * const * hyps) { expr* result = nullptr; for (unsigned i = 0; i < num_hyps; ++i) { if (!match_nil(hyps[i])) { if (result) { result = mk_cons(result, hyps[i]); } else { result = hyps[i]; } } } if (result == nullptr) { return mk_nil(); } else { return result; } } void proof_checker::dump_proof(proof const* pr) { if (!m_dump_lemmas) return; SASSERT(m.has_fact(pr)); expr * consequent = m.get_fact(pr); unsigned num = m.get_num_parents(pr); ptr_buffer antecedents; for (unsigned i = 0; i < num; i++) { proof * a = m.get_parent(pr, i); SASSERT(m.has_fact(a)); antecedents.push_back(m.get_fact(a)); } dump_proof(antecedents.size(), antecedents.c_ptr(), consequent); } void proof_checker::dump_proof(unsigned num_antecedents, expr * const * antecedents, expr * consequent) { char buffer[128]; #ifdef _WINDOWS sprintf_s(buffer, Z3_ARRAYSIZE(buffer), "proof_lemma_%d.smt2", m_proof_lemma_id); #else sprintf(buffer, "proof_lemma_%d.smt2", m_proof_lemma_id); #endif std::ofstream out(buffer); ast_smt_pp pp(m); pp.set_benchmark_name("lemma"); pp.set_status("unsat"); pp.set_logic(symbol(m_logic.c_str())); for (unsigned i = 0; i < num_antecedents; i++) pp.add_assumption(antecedents[i]); expr_ref n(m); n = m.mk_not(consequent); pp.display_smt2(out, n); out.close(); m_proof_lemma_id++; } bool proof_checker::check_arith_literal(bool is_pos, app* lit0, rational const& coeff, expr_ref& sum, bool& is_strict) { arith_util a(m); app* lit = lit0; if (m.is_not(lit)) { lit = to_app(lit->get_arg(0)); is_pos = !is_pos; } if (!a.is_le(lit) && !a.is_lt(lit) && !a.is_ge(lit) && !a.is_gt(lit) && !m.is_eq(lit)) { IF_VERBOSE(2, verbose_stream() << "Not arith literal: " << mk_pp(lit, m) << "\n";); return false; } SASSERT(lit->get_num_args() == 2); sort* s = m.get_sort(lit->get_arg(0)); bool is_int = a.is_int(s); if (!is_int && a.is_int_expr(lit->get_arg(0))) { is_int = true; s = a.mk_int(); } if (!is_int && is_pos && (a.is_gt(lit) || a.is_lt(lit))) { is_strict = true; } if (!is_int && !is_pos && (a.is_ge(lit) || a.is_le(lit))) { is_strict = true; } SASSERT(a.is_int(s) || a.is_real(s)); expr_ref sign1(m), sign2(m), term(m); sign1 = a.mk_numeral(m.is_eq(lit)?coeff:abs(coeff), s); sign2 = a.mk_numeral(m.is_eq(lit)?-coeff:-abs(coeff), s); if (!sum.get()) { sum = a.mk_numeral(rational(0), s); } expr* a0 = lit->get_arg(0); expr* a1 = lit->get_arg(1); if (is_pos && (a.is_ge(lit) || a.is_gt(lit))) { std::swap(a0, a1); } if (!is_pos && (a.is_le(lit) || a.is_lt(lit))) { std::swap(a0, a1); } // // Multiplying by coefficients over strict // and non-strict inequalities: // // (a <= b) * 2 // (a - b <= 0) * 2 // (2a - 2b <= 0) // (a < b) * 2 <=> // (a +1 <= b) * 2 <=> // 2a + 2 <= 2b <=> // 2a+2-2b <= 0 bool strict_ineq = is_pos?(a.is_gt(lit) || a.is_lt(lit)):(a.is_ge(lit) || a.is_le(lit)); if (is_int && strict_ineq) { sum = a.mk_add(sum, sign1); } term = a.mk_mul(sign1, a0); sum = a.mk_add(sum, term); term = a.mk_mul(sign2, a1); sum = a.mk_add(sum, term); #if 1 { th_rewriter rw(m); rw(sum); } IF_VERBOSE(2, verbose_stream() << "coeff,lit,sum " << coeff << "\n" << mk_pp(lit0, m) << "\n" << mk_pp(sum, m) << "\n";); #endif return true; } bool proof_checker::check_arith_proof(proof* p) { func_decl* d = p->get_decl(); SASSERT(PR_TH_LEMMA == p->get_decl_kind()); SASSERT(d->get_parameter(0).get_symbol() == "arith"); unsigned num_params = d->get_num_parameters(); arith_util autil(m); SASSERT(num_params > 0); if (num_params == 1) { dump_proof(p); return true; } expr* fact = nullptr; proof_ref_vector proofs(m); if (!match_fact(p, fact)) { UNREACHABLE(); return false; } if (d->get_parameter(1).get_symbol() != symbol("farkas")) { dump_proof(p); return true; } expr_ref sum(m); bool is_strict = false; unsigned offset = 0; vector coeffs; rational lc(1); for (unsigned i = 2; i < d->get_num_parameters(); ++i) { parameter const& p = d->get_parameter(i); if (!p.is_rational()) { UNREACHABLE(); return false; } coeffs.push_back(p.get_rational()); lc = lcm(lc, denominator(coeffs.back())); } if (!lc.is_one()) { for (unsigned i = 0; i < coeffs.size(); ++i) { coeffs[i] = lc*coeffs[i]; } } unsigned num_parents = m.get_num_parents(p); for (unsigned i = 0; i < num_parents; i++) { proof * a = m.get_parent(p, i); SASSERT(m.has_fact(a)); if (!check_arith_literal(true, to_app(m.get_fact(a)), coeffs[offset++], sum, is_strict)) { return false; } } if (m.is_or(fact)) { app* disj = to_app(fact); unsigned num_args = disj->get_num_args(); for (unsigned i = 0; i < num_args; ++i) { app* lit = to_app(disj->get_arg(i)); if (!check_arith_literal(false, lit, coeffs[offset++], sum, is_strict)) { return false; } } } else if (!m.is_false(fact)) { if (!check_arith_literal(false, to_app(fact), coeffs[offset++], sum, is_strict)) { return false; } } if (!sum.get()) { return false; } sort* s = m.get_sort(sum); if (is_strict) { sum = autil.mk_lt(sum, autil.mk_numeral(rational(0), s)); } else { sum = autil.mk_le(sum, autil.mk_numeral(rational(0), s)); } th_rewriter rw(m); rw(sum); if (!m.is_false(sum)) { IF_VERBOSE(0, verbose_stream() << "Arithmetic proof check failed: " << mk_pp(sum, m) << "\n";); m_dump_lemmas = true; dump_proof(p); return false; } return true; } z3-z3-4.8.7/src/ast/proofs/proof_checker.h000066400000000000000000000106621356505360400203040ustar00rootroot00000000000000/*++ Copyright (c) 2008 Microsoft Corporation Module Name: proof_checker.h Abstract: Proof checker. Author: Nikolaj Bjorner (nbjorner) 2008-03-07. Revision History: --*/ #ifndef PROOF_CHECKER_H_ #define PROOF_CHECKER_H_ #include "ast/ast.h" #include "util/map.h" class proof_checker { ast_manager& m; proof_ref_vector m_todo; expr_mark m_marked; expr_ref_vector m_pinned; obj_map m_hypotheses; family_id m_hyp_fid; // family_id m_spc_fid; app_ref m_nil; bool m_dump_lemmas; std::string m_logic; unsigned m_proof_lemma_id; enum hyp_decl_kind { OP_CONS, OP_ATOM, OP_NIL }; enum hyp_sort_kind { CELL_SORT }; class hyp_decl_plugin : public decl_plugin { protected: func_decl* m_cons; func_decl* m_atom; func_decl* m_nil; sort* m_cell; void set_manager(ast_manager * m, family_id id) override; func_decl * mk_func_decl(decl_kind k); public: hyp_decl_plugin(); ~hyp_decl_plugin() override {} void finalize() override; decl_plugin * mk_fresh() override { return alloc(hyp_decl_plugin); } sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const* parameters) override; func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) override; func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned num_args, expr * const * args, sort * range) override; void get_op_names(svector & op_names, symbol const & logic) override; void get_sort_names(svector & sort_names, symbol const & logic) override; }; public: proof_checker(ast_manager& m); void set_dump_lemmas(char const * logic = "AUFLIA") { m_dump_lemmas = true; m_logic = logic; } bool check(proof* p, expr_ref_vector& side_conditions); private: bool check1(proof* p, expr_ref_vector& side_conditions); bool check1_basic(proof* p, expr_ref_vector& side_conditions); bool check1_spc(proof* p, expr_ref_vector& side_conditions); bool check_arith_proof(proof* p); bool check_arith_literal(bool is_pos, app* lit, rational const& coeff, expr_ref& sum, bool& is_strict); bool match_fact(proof const* p, expr*& fact) const; void add_premise(proof* p); bool match_proof(proof const* p) const; bool match_proof(proof const* p, proof*& p0) const; bool match_proof(proof const* p, proof*& p0, proof*& p1) const; bool match_proof(proof const* p, proof_ref_vector& parents) const; bool match_binary(expr const* e, func_decl*& d, expr*& t1, expr*& t2) const; bool match_op(expr const* e, decl_kind k, expr*& t1, expr*& t2) const; bool match_op(expr const* e, decl_kind k, expr*& t) const; bool match_op(expr const* e, decl_kind k, ptr_vector& terms) const; bool match_iff(expr const* e, expr*& t1, expr*& t2) const; bool match_implies(expr const* e, expr*& t1, expr*& t2) const; bool match_eq(expr const* e, expr*& t1, expr*& t2) const; bool match_oeq(expr const* e, expr*& t1, expr*& t2) const; bool match_not(expr const* e, expr*& t) const; bool match_or(expr const* e, ptr_vector& terms) const; bool match_and(expr const* e, ptr_vector& terms) const; bool match_app(expr const* e, func_decl*& d, ptr_vector& terms) const; bool match_quantifier(expr const*, bool& is_univ, sort_ref_vector&, expr*& body) const; bool match_negated(expr const* a, expr* b) const; bool match_equiv(expr const* a, expr*& t1, expr*& t2) const; void get_ors(expr* e, expr_ref_vector& ors); void get_hypotheses(proof* p, expr_ref_vector& ante); bool match_nil(expr const* e) const; bool match_cons(expr const* e, expr*& a, expr*& b) const; bool match_atom(expr const* e, expr*& a) const; expr* mk_nil(); expr* mk_cons(expr* a, expr* b); expr* mk_atom(expr* e); bool is_hypothesis(proof const* p) const; expr* mk_hyp(unsigned num_hyps, expr * const * hyps); void dump_proof(proof const* pr); void dump_proof(unsigned num_antecedents, expr * const * antecedents, expr * consequent); void set_false(expr_ref& e, unsigned idx, expr_ref& lit); }; #endif z3-z3-4.8.7/src/ast/proofs/proof_utils.cpp000066400000000000000000001003271356505360400203710ustar00rootroot00000000000000/*++ Copyright (c) 2017 Arie Gurfinkel Module Name: proof_utils.cpp Abstract: Utilities to traverse and manipulate proofs Author: Bernhard Gleiss Arie Gurfinkel Revision History: --*/ #include "ast/ast_util.h" #include "ast/ast_pp.h" #include "ast/proofs/proof_utils.h" #include "ast/proofs/proof_checker.h" #include "ast/rewriter/var_subst.h" #include "util/container_util.h" proof_post_order::proof_post_order(proof* root, ast_manager& manager) : m(manager) {m_todo.push_back(root);} bool proof_post_order::hasNext() {return !m_todo.empty();} /* * iterative post-order depth-first search (DFS) through the proof DAG */ proof* proof_post_order::next() { while (!m_todo.empty()) { proof* currentNode = m_todo.back(); // if we haven't already visited the current unit if (!m_visited.is_marked(currentNode)) { bool existsUnvisitedParent = false; // add unprocessed premises to stack for DFS. // If there is at least one unprocessed premise, don't compute the result // for currentProof now, but wait until those unprocessed premises are processed. for (unsigned i = 0; i < m.get_num_parents(currentNode); ++i) { SASSERT(m.is_proof(currentNode->get_arg(i))); proof* premise = to_app(currentNode->get_arg(i)); // if we haven't visited the current premise yet if (!m_visited.is_marked(premise)) { // add it to the stack m_todo.push_back(premise); existsUnvisitedParent = true; } } // if we already visited all parent-inferences, we can visit the inference too if (!existsUnvisitedParent) { m_visited.mark(currentNode, true); m_todo.pop_back(); return currentNode; } } else { m_todo.pop_back(); } } // we have already iterated through all inferences return nullptr; } class reduce_hypotheses { ast_manager &m; // tracking all created expressions expr_ref_vector m_pinned; // cache for the transformation obj_map m_cache; // map from unit literals to their hypotheses-free derivations obj_map m_units; // -- all hypotheses in the proof obj_hashtable m_hyps; // marks hypothetical proofs ast_mark m_hypmark; // stack ptr_vector m_todo; void reset() { m_cache.reset(); m_units.reset(); m_hyps.reset(); m_hypmark.reset(); m_pinned.reset(); } bool compute_mark1(proof *pr) { bool hyp_mark = false; // lemmas clear all hypotheses if (!m.is_lemma(pr)) { for (unsigned i = 0, sz = m.get_num_parents(pr); i < sz; ++i) { if (m_hypmark.is_marked(m.get_parent(pr, i))) { hyp_mark = true; break; } } } m_hypmark.mark(pr, hyp_mark); return hyp_mark; } void compute_marks(proof* pr) { proof *p; proof_post_order pit(pr, m); while (pit.hasNext()) { p = pit.next(); if (m.is_hypothesis(p)) { m_hypmark.mark(p, true); m_hyps.insert(m.get_fact(p)); } else { bool hyp_mark = compute_mark1(p); // collect units that are hyp-free and are used as hypotheses somewhere if (!hyp_mark && m.has_fact(p) && m_hyps.contains(m.get_fact(p))) { m_units.insert(m.get_fact(p), p); } } } } void find_units(proof *pr) { // optional. not implemented yet. } void reduce(proof* pf, proof_ref &out) { proof *res = nullptr; m_todo.reset(); m_todo.push_back(pf); ptr_buffer args; bool dirty = false; while (!m_todo.empty()) { proof *p, *tmp, *pp; unsigned todo_sz; p = m_todo.back(); if (m_cache.find(p, tmp)) { res = tmp; m_todo.pop_back(); continue; } dirty = false; args.reset(); todo_sz = m_todo.size(); for (unsigned i = 0, sz = m.get_num_parents(p); i < sz; ++i) { pp = m.get_parent(p, i); if (m_cache.find(pp, tmp)) { args.push_back(tmp); dirty = dirty || pp != tmp; } else { m_todo.push_back(pp); } } if (todo_sz < m_todo.size()) { continue; } else { m_todo.pop_back(); } if (m.is_hypothesis(p)) { // hyp: replace by a corresponding unit if (m_units.find(m.get_fact(p), tmp)) { res = tmp; } else { res = p; } } else if (!dirty) { res = p; } else if (m.is_lemma(p)) { //lemma: reduce the premise; remove reduced consequences from conclusion SASSERT(args.size() == 1); res = mk_lemma_core(args.get(0), m.get_fact(p)); compute_mark1(res); } else if (m.is_unit_resolution(p)) { // unit: reduce units; reduce the first premise; rebuild unit resolution res = mk_unit_resolution_core(args.size(), args.c_ptr()); compute_mark1(res); } else { // other: reduce all premises; reapply if (m.has_fact(p)) { args.push_back(to_app(m.get_fact(p))); } SASSERT(p->get_decl()->get_arity() == args.size()); res = m.mk_app(p->get_decl(), args.size(), (expr * const*)args.c_ptr()); m_pinned.push_back(res); compute_mark1(res); } SASSERT(res); m_cache.insert(p, res); if (m.has_fact(res) && m.is_false(m.get_fact(res))) { break; } } out = res; } // returns true if (hypothesis (not a)) would be reduced bool is_reduced(expr *a) { expr_ref e(m); if (m.is_not(a)) { e = to_app(a)->get_arg(0); } else { e = m.mk_not(a); } return m_units.contains(e); } proof *mk_lemma_core(proof *pf, expr *fact) { ptr_buffer args; expr_ref lemma(m); if (m.is_or(fact)) { for (unsigned i = 0, sz = to_app(fact)->get_num_args(); i < sz; ++i) { expr *a = to_app(fact)->get_arg(i); if (!is_reduced(a)) { args.push_back(a); } } } else if (!is_reduced(fact)) { args.push_back(fact); } if (args.empty()) { return pf; } else if (args.size() == 1) { lemma = args.get(0); } else { lemma = m.mk_or(args.size(), args.c_ptr()); } proof* res = m.mk_lemma(pf, lemma); m_pinned.push_back(res); if (m_hyps.contains(lemma)) { m_units.insert(lemma, res); } return res; } proof *mk_unit_resolution_core(unsigned num_args, proof* const *args) { ptr_buffer pf_args; pf_args.push_back(args [0]); app *cls_fact = to_app(m.get_fact(args[0])); ptr_buffer cls; if (m.is_or(cls_fact)) { for (unsigned i = 0, sz = cls_fact->get_num_args(); i < sz; ++i) { cls.push_back(cls_fact->get_arg(i)); } } else { cls.push_back(cls_fact); } // construct new resovent ptr_buffer new_fact_cls; bool found; // XXX quadratic for (unsigned i = 0, sz = cls.size(); i < sz; ++i) { found = false; for (unsigned j = 1; j < num_args; ++j) { if (m.is_complement(cls.get(i), m.get_fact(args [j]))) { found = true; pf_args.push_back(args [j]); break; } } if (!found) { new_fact_cls.push_back(cls.get(i)); } } SASSERT(new_fact_cls.size() + pf_args.size() - 1 == cls.size()); expr_ref new_fact(m); new_fact = mk_or(m, new_fact_cls.size(), new_fact_cls.c_ptr()); // create new proof step proof *res = m.mk_unit_resolution(pf_args.size(), pf_args.c_ptr(), new_fact); m_pinned.push_back(res); return res; } // reduce all units, if any unit reduces to false return true and put its proof into out bool reduce_units(proof_ref &out) { proof_ref res(m); for (auto entry : m_units) { reduce(entry.get_value(), res); if (m.is_false(m.get_fact(res))) { out = res; return true; } res.reset(); } return false; } public: reduce_hypotheses(ast_manager &m) : m(m), m_pinned(m) {} void operator()(proof_ref &pr) { compute_marks(pr); if (!reduce_units(pr)) { reduce(pr.get(), pr); } reset(); } }; void reduce_hypotheses(proof_ref &pr) { ast_manager &m = pr.get_manager(); class reduce_hypotheses hypred(m); hypred(pr); DEBUG_CODE(proof_checker pc(m); expr_ref_vector side(m); SASSERT(pc.check(pr, side)); ); } #include "ast/ast_smt2_pp.h" class reduce_hypotheses0 { typedef obj_hashtable expr_set; ast_manager& m; // reference for any expression created by the transformation expr_ref_vector m_refs; // currently computed result obj_map m_cache; // map conclusions to closed proofs that derive them obj_map m_units; // currently active units ptr_vector m_units_trail; // size of m_units_trail at the last push unsigned_vector m_limits; // map from proofs to active hypotheses obj_map m_hypmap; // reference train for hypotheses sets ptr_vector m_hyprefs; ptr_vector m_literals; void reset() { m_refs.reset(); m_cache.reset(); m_units.reset(); m_units_trail.reset(); m_limits.reset(); std::for_each(m_hyprefs.begin(), m_hyprefs.end(), delete_proc()); m_hypmap.reset(); m_hyprefs.reset(); m_literals.reset(); } void push() { m_limits.push_back(m_units_trail.size()); } void pop() { unsigned sz = m_limits.back(); while (m_units_trail.size() > sz) { m_units.remove(m_units_trail.back()); m_units_trail.pop_back(); } m_limits.pop_back(); } void get_literals(expr* clause) { m_literals.reset(); if (m.is_or(clause)) { m_literals.append(to_app(clause)->get_num_args(), to_app(clause)->get_args()); } else { m_literals.push_back(clause); } } void add_hypotheses(proof* p) { expr_set* hyps = nullptr; bool inherited = false; if (p->get_decl_kind() == PR_HYPOTHESIS) { hyps = alloc(expr_set); hyps->insert(m.get_fact(p)); m_hyprefs.push_back(hyps); } else { for (unsigned i = 0; i < m.get_num_parents(p); ++i) { expr_set* hyps1 = m_hypmap.find(m.get_parent(p, i)); if (hyps1) { if (!hyps) { hyps = hyps1; inherited = true; continue; } if (inherited) { hyps = alloc(expr_set,*hyps); m_hyprefs.push_back(hyps); inherited = false; } set_union(*hyps, *hyps1); } } } m_hypmap.insert(p, hyps); } expr_ref complement_lit(expr* e) { expr* e1; if (m.is_not(e, e1)) { return expr_ref(e1, m); } else { return expr_ref(m.mk_not(e), m); } } bool in_hypotheses(expr* e, expr_set* hyps) { if (!hyps) { return false; } expr_ref not_e = complement_lit(e); return hyps->contains(not_e); } bool contains_hypothesis(proof* p) { ptr_vector todo; ast_mark visit; todo.push_back(p); while (!todo.empty()) { p = todo.back(); todo.pop_back(); if (visit.is_marked(p)) { continue; } visit.mark(p, true); if (PR_HYPOTHESIS == p->get_decl_kind()) { return true; } for (unsigned i = 0; i < m.get_num_parents(p); ++i) { todo.push_back(m.get_parent(p, i)); } } return false; } bool is_closed(proof* p) { expr_set* hyps = m_hypmap.find(p); return !hyps || hyps->empty(); } public: reduce_hypotheses0(ast_manager& m): m(m), m_refs(m) {} void operator()(proof_ref& pr) { proof_ref tmp(m); tmp = pr; elim(pr); reset(); CTRACE("proof_utils", contains_hypothesis(pr), tout << "Contains hypothesis:\n"; tout << mk_ismt2_pp(tmp, m) << "\n====>\n"; tout << mk_ismt2_pp(pr, m) << "\n";); } void elim(proof_ref& p) { proof_ref tmp(m); proof* result = p.get(); if (m_cache.find(p, result)) { p = result; return; } //SASSERT (p.get () == result); switch(p->get_decl_kind()) { case PR_HYPOTHESIS: // replace result by m_units[m.get_fact (p)] if defined // AG: This is the main step. Replace a hypothesis by a derivation of its consequence if (!m_units.find(m.get_fact(p), result)) { // restore the result back to p result = p.get(); } // compute hypothesis of the result // not clear what 'result' is at this point. // probably the proof at the top of the call // XXX not clear why this is re-computed each time // XXX moreover, m_units are guaranteed to be closed! // XXX so no hypotheses are needed for them add_hypotheses(result); break; case PR_LEMMA: { SASSERT(m.get_num_parents(p) == 1); tmp = m.get_parent(p, 0); // eliminate hypothesis recursively in the proof of the lemma elim(tmp); expr_set* hyps = m_hypmap.find(tmp); expr_set* new_hyps = nullptr; // XXX if the proof is correct, the hypotheses of the tmp // XXX should be exactly those of the consequence of the lemma // XXX but if this code actually eliminates hypotheses, the set might be a subset if (hyps) { new_hyps = alloc(expr_set, *hyps); } expr* fact = m.get_fact(p); // when hypothesis is a single literal of the form // (or A B), and the fact of p is (or A B). if (hyps && hyps->size() == 1 && in_hypotheses(fact, hyps)) { m_literals.reset(); m_literals.push_back(fact); } else { get_literals(fact); } // go over all the literals in the consequence of the lemma for (unsigned i = 0; i < m_literals.size(); ++i) { expr* e = m_literals[i]; // if the literal is not in hypothesis, skip it if (!in_hypotheses(e, hyps)) { m_literals[i] = m_literals.back(); m_literals.pop_back(); --i; } // if the literal is in hypothesis remove it because // it is not in hypothesis set of the lemma // XXX but we assume that lemmas have empty hypothesis set. // XXX eventually every element of new_hyps must be removed! else { SASSERT(new_hyps); expr_ref not_e = complement_lit(e); SASSERT(new_hyps->contains(not_e)); new_hyps->remove(not_e); } } // killed all hypotheses, so can stop at the lemma since // we have a closed pf of false if (m_literals.empty()) { result = tmp; } else { // create a new lemma, but might be re-creating existing one expr_ref clause(m); if (m_literals.size() == 1) { clause = m_literals[0]; } else { clause = m.mk_or(m_literals.size(), m_literals.c_ptr()); } tmp = m.mk_lemma(tmp, clause); m_refs.push_back(tmp); result = tmp; } if (new_hyps && new_hyps->empty()) { dealloc(new_hyps); new_hyps = nullptr; } m_hypmap.insert(result, new_hyps); // might push 0 into m_hyprefs. No reason for that m_hyprefs.push_back(new_hyps); TRACE("proof_utils", tout << "New lemma: " << mk_pp(m.get_fact(p), m) << "\n==>\n" << mk_pp(m.get_fact(result), m) << "\n"; if (hyps) { expr_set::iterator it = hyps->begin(); expr_set::iterator end = hyps->end(); for (; it != end; ++it) { tout << "Hypothesis: " << mk_pp(*it, m) << "\n"; } }); break; } case PR_UNIT_RESOLUTION: { proof_ref_vector parents(m); // get the clause being resolved with parents.push_back(m.get_parent(p, 0)); // save state push(); bool found_false = false; // for every derivation of a unit literal for (unsigned i = 1; i < m.get_num_parents(p); ++i) { // see if it derives false tmp = m.get_parent(p, i); elim(tmp); if (m.is_false(m.get_fact(tmp))) { // if derived false, the whole pf is false and we can bail out result = tmp; found_false = true; break; } // -- otherwise, the fact has not changed. nothing to simplify SASSERT(m.get_fact(tmp) == m.get_fact(m.get_parent(p, i))); parents.push_back(tmp); // remember that we have this derivation while we have not poped the trail // but only if the proof is closed (i.e., a real unit) if (is_closed(tmp) && !m_units.contains(m.get_fact(tmp))) { m_units.insert(m.get_fact(tmp), tmp); m_units_trail.push_back(m.get_fact(tmp)); } } if (found_false) { pop(); break; } // look at the clause being resolved with tmp = m.get_parent(p, 0); // remember its fact expr* old_clause = m.get_fact(tmp); // attempt to reduce its fact elim(tmp); // update parents parents[0] = tmp; // if the new fact is false, bail out expr* clause = m.get_fact(tmp); if (m.is_false(clause)) { m_refs.push_back(tmp); result = tmp; pop(); break; } // // case where clause is a literal in the old clause. // i.e., reduce multi-literal clause to a unit // if (is_literal_in_clause(clause, old_clause)) { // if the resulting literal was resolved, get a pf of false and bail out bool found = false; for (unsigned i = 1; !found && i < parents.size(); ++i) { if (m.is_complement(clause, m.get_fact(parents[i].get()))) { parents[1] = parents[i].get(); parents.resize(2); result = m.mk_unit_resolution(parents.size(), parents.c_ptr()); m_refs.push_back(result); add_hypotheses(result); found = true; } } // else if the resulting literal is not resolved, it is the new consequence if (!found) { result = parents[0].get(); } pop(); break; } // // case where new clause is a subset of old clause. // the literals in clause should be a subset of literals in old_clause. // get_literals(clause); for (unsigned i = 1; i < parents.size(); ++i) { bool found = false; for (unsigned j = 0; j < m_literals.size(); ++j) { if (m.is_complement(m_literals[j], m.get_fact(parents[i].get()))) { found = true; break; } } if (!found) { // literal was removed as hypothesis. parents[i] = parents.back(); parents.pop_back(); --i; } } if (parents.size() == 1) { result = parents[0].get(); } else { result = m.mk_unit_resolution(parents.size(), parents.c_ptr()); m_refs.push_back(result); add_hypotheses(result); } pop(); break; } default: { ptr_buffer args; bool change = false; bool found_false = false; for (unsigned i = 0; i < m.get_num_parents(p); ++i) { tmp = m.get_parent(p, i); elim(tmp); if (m.is_false(m.get_fact(tmp))) { result = tmp; found_false = true; break; } // SASSERT(m.get_fact(tmp) == m.get_fact(m.get_parent(p, i))); change = change || (tmp != m.get_parent(p, i)); args.push_back(tmp); } if (found_false) { break; } if (m.has_fact(p)) { args.push_back(m.get_fact(p)); } if (change) { tmp = m.mk_app(p->get_decl(), args.size(), args.c_ptr()); m_refs.push_back(tmp); } else { tmp = p; } result = tmp; add_hypotheses(result); break; } } SASSERT(m_hypmap.contains(result)); m_cache.insert(p, result); p = result; } bool is_literal_in_clause(expr* fml, expr* clause) { if (!m.is_or(clause)) { return false; } app* cl = to_app(clause); for (unsigned i = 0; i < cl->get_num_args(); ++i) { if (cl->get_arg(i) == fml) { return true; } } return false; } }; void proof_utils::reduce_hypotheses(proof_ref& pr) { ast_manager& m = pr.get_manager(); class reduce_hypotheses0 reduce(m); reduce(pr); CTRACE("proof_utils", !is_closed(m, pr), tout << mk_pp(pr, m) << "\n";); } class proof_is_closed { ast_manager& m; ptr_vector m_literals; ast_mark m_visit; void reset() { m_literals.reset(); m_visit.reset(); } bool check(proof* p) { // really just a partial check because nodes may be visited // already under a different lemma scope. if (m_visit.is_marked(p)) { return true; } bool result = false; m_visit.mark(p, true); switch(p->get_decl_kind()) { case PR_LEMMA: { unsigned sz = m_literals.size(); expr* cls = m.get_fact(p); m_literals.push_back(cls); if (m.is_or(cls)) { m_literals.append(to_app(cls)->get_num_args(), to_app(cls)->get_args()); } SASSERT(m.get_num_parents(p) == 1); result = check(m.get_parent(p, 0)); m_literals.resize(sz); break; } case PR_HYPOTHESIS: { expr* fact = m.get_fact(p); for (unsigned i = 0; i < m_literals.size(); ++i) { if (m.is_complement(m_literals[i], fact)) { result = true; break; } } break; } default: result = true; for (unsigned i = 0; i < m.get_num_parents(p); ++i) { if (!check(m.get_parent(p, i))) { result = false; break; } } break; } return result; } public: proof_is_closed(ast_manager& m): m(m) {} bool operator()(proof *p) { bool ok = check(p); reset(); return ok; } }; bool proof_utils::is_closed(ast_manager& m, proof* p) { proof_is_closed checker(m); return checker(p); } static void permute_unit_resolution(expr_ref_vector& refs, obj_map& cache, proof_ref& pr) { ast_manager& m = pr.get_manager(); proof* pr2 = nullptr; proof_ref_vector parents(m); proof_ref prNew(pr); if (cache.find(pr, pr2)) { pr = pr2; return; } for (unsigned i = 0; i < m.get_num_parents(pr); ++i) { prNew = m.get_parent(pr, i); permute_unit_resolution(refs, cache, prNew); parents.push_back(prNew); } prNew = pr; if (pr->get_decl_kind() == PR_UNIT_RESOLUTION && parents[0]->get_decl_kind() == PR_TH_LEMMA) { /* Unit resolution: T1: (or l_1 ... l_n l_1' ... l_m') T2: (not l_1) ... T(n+1): (not l_n) [unit-resolution T1 ... T(n+1)]: (or l_1' ... l_m') Th lemma: T1: (not l_1) ... Tn: (not l_n) [th-lemma T1 ... Tn]: (or l_{n+1} ... l_m) Such that (or l_1 .. l_n l_{n+1} .. l_m) is a theory axiom. Implement conversion: T1 |- not l_1 ... Tn |- not l_n ------------------------------- TH_LEMMA (or k_1 .. k_m j_1 ... j_m) S1 |- not k_1 ... Sm |- not k_m -------------------------------------------------------------- UNIT_RESOLUTION (or j_1 .. j_m) |-> T1 |- not l_1 ... Tn |- not l_n S1 |- not k_1 ... Sm |- not k_m ---------------------------------------------------------------- TH_LEMMA (or j_1 .. j_m) */ proof_ref_vector premises(m); proof* thLemma = parents[0].get(); for (unsigned i = 0; i < m.get_num_parents(thLemma); ++i) { premises.push_back(m.get_parent(thLemma, i)); } for (unsigned i = 1; i < parents.size(); ++i) { premises.push_back(parents[i].get()); } parameter const* params = thLemma->get_decl()->get_parameters(); unsigned num_params = thLemma->get_decl()->get_num_parameters(); SASSERT(params[0].is_symbol()); family_id tid = m.mk_family_id(params[0].get_symbol()); SASSERT(tid != null_family_id); // AG: This can break a theory lemma. In particular, for Farkas lemmas the coefficients // AG: for the literals propagated from the unit resolution are missing. // AG: Why is this a good thing to do? // AG: This can lead to merging of the units with other terms in interpolation, // AG: but without farkas coefficients this does not make sense prNew = m.mk_th_lemma(tid, m.get_fact(pr), premises.size(), premises.c_ptr(), num_params-1, params+1); } else { ptr_vector args; for (unsigned i = 0; i < parents.size(); ++i) { args.push_back(parents[i].get()); } if (m.has_fact(pr)) { args.push_back(m.get_fact(pr)); } prNew = m.mk_app(pr->get_decl(), args.size(), args.c_ptr()); } cache.insert(pr, prNew); refs.push_back(prNew); pr = prNew; } // permute unit resolution over Theory lemmas to track premises. void proof_utils::permute_unit_resolution(proof_ref& pr) { expr_ref_vector refs(pr.get_manager()); obj_map cache; ::permute_unit_resolution(refs, cache, pr); } class push_instantiations_up_cl { ast_manager& m; public: push_instantiations_up_cl(ast_manager& m): m(m) {} void operator()(proof_ref& p) { expr_ref_vector s0(m); p = push(p, s0); } private: proof* push(proof* p, expr_ref_vector const& sub) { proof_ref_vector premises(m); expr_ref conclusion(m); svector > positions; vector substs; if (m.is_hyper_resolve(p, premises, conclusion, positions, substs)) { for (unsigned i = 0; i < premises.size(); ++i) { compose(substs[i], sub); premises[i] = push(premises[i].get(), substs[i]); substs[i].reset(); } instantiate(sub, conclusion); return m.mk_hyper_resolve(premises.size(), premises.c_ptr(), conclusion, positions, substs); } if (sub.empty()) { return p; } if (m.is_modus_ponens(p)) { SASSERT(m.get_num_parents(p) == 2); proof* p0 = m.get_parent(p, 0); proof* p1 = m.get_parent(p, 1); if (m.get_fact(p0) == m.get_fact(p)) { return push(p0, sub); } expr* e1, *e2; if (m.is_rewrite(p1, e1, e2) && is_quantifier(e1) && is_quantifier(e2) && to_quantifier(e1)->get_num_decls() == to_quantifier(e2)->get_num_decls()) { expr_ref r1(e1,m), r2(e2,m); instantiate(sub, r1); instantiate(sub, r2); p1 = m.mk_rewrite(r1, r2); return m.mk_modus_ponens(push(p0, sub), p1); } } premises.push_back(p); substs.push_back(sub); conclusion = m.get_fact(p); instantiate(sub, conclusion); return m.mk_hyper_resolve(premises.size(), premises.c_ptr(), conclusion, positions, substs); } void compose(expr_ref_vector& sub, expr_ref_vector const& s0) { for (unsigned i = 0; i < sub.size(); ++i) { sub[i] = var_subst(m, false)(sub[i].get(), s0.size(), s0.c_ptr()); } } void instantiate(expr_ref_vector const& sub, expr_ref& fml) { if (sub.empty()) { return; } if (!is_forall(fml)) { return; } quantifier* q = to_quantifier(fml); if (q->get_num_decls() != sub.size()) { TRACE("proof_utils", tout << "quantifier has different number of variables than substitution"; tout << mk_pp(q, m) << "\n"; tout << sub.size() << "\n";); return; } fml = var_subst(m, false)(q->get_expr(), sub.size(), sub.c_ptr()); } }; void proof_utils::push_instantiations_up(proof_ref& pr) { push_instantiations_up_cl push(pr.get_manager()); push(pr); } z3-z3-4.8.7/src/ast/proofs/proof_utils.h000066400000000000000000000163121356505360400200360ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: proof_utils.h Abstract: Utilities to traverse and manipulate proofs Author: Bernhard Gleiss Arie Gurfinkel Nikolaj Bjorner Revision History: --*/ #ifndef PROOF_UTILS_H_ #define PROOF_UTILS_H_ #include "ast/ast.h" #include "ast/ast_pp.h" #include "ast/rewriter/bool_rewriter.h" #include "ast/proofs/proof_checker.h" /* * iterator, which traverses the proof in depth-first post-order. */ class proof_post_order { public: proof_post_order(proof* refutation, ast_manager& manager); bool hasNext(); proof* next(); private: ptr_vector m_todo; ast_mark m_visited; // the proof nodes we have already visited ast_manager& m; }; void reduce_hypotheses(proof_ref &pr); class proof_utils { public: /** \brief reduce the set of hypotheses used in the proof. */ static void reduce_hypotheses(proof_ref& pr); /** \brief Check that a proof does not contain open hypotheses. */ static bool is_closed(ast_manager& m, proof* p); /** \brief Permute unit resolution rule with th-lemma */ static void permute_unit_resolution(proof_ref& pr); /** \brief Push instantiations created in hyper-resolutions up to leaves. This produces a "ground" proof where leaves are annotated by instantiations. */ static void push_instantiations_up(proof_ref& pr); }; class elim_aux_assertions { static bool matches_fact(expr_ref_vector &args, expr* &match) { ast_manager &m = args.get_manager(); expr *fact = args.back(); for (unsigned i = 0, sz = args.size() - 1; i < sz; ++i) { expr *arg = args.get(i); if (m.is_proof(arg) && m.has_fact(to_app(arg)) && m.get_fact(to_app(arg)) == fact) { match = arg; return true; } } return false; } app_ref m_aux; public: elim_aux_assertions(app_ref const& aux) : m_aux(aux) {} void mk_or_core(expr_ref_vector &args, expr_ref &res) { ast_manager &m = args.get_manager(); unsigned j = 0; for (unsigned i = 0, sz = args.size(); i < sz; ++i) { if (m.is_false(args.get(i))) { continue; } if (i != j) { args [j] = args.get(i); } ++j; } SASSERT(j >= 1); res = j > 1 ? m.mk_or(j, args.c_ptr()) : args.get(0); } void mk_app(func_decl *decl, expr_ref_vector &args, expr_ref &res) { ast_manager &m = args.get_manager(); bool_rewriter brwr(m); brwr.set_flat(false); if (m.is_or(decl)) { mk_or_core(args, res); } else if (m.is_eq(decl) && args.size() == 2) // avoiding simplifying equalities. In particular, // we don't want (= (not a) (not b)) to be reduced to (= a b) { res = m.mk_eq(args.get(0), args.get(1)); } else { brwr.mk_app(decl, args.size(), args.c_ptr(), res); } } void operator()(ast_manager &m, proof *pr, proof_ref &res) { DEBUG_CODE(proof_checker pc(m); expr_ref_vector side(m); SASSERT(pc.check(pr, side)); ); obj_map cache; bool_rewriter brwr(m); // for reference counting of new proofs app_ref_vector pinned(m); ptr_vector todo; todo.push_back(pr); expr_ref not_aux(m); not_aux = m.mk_not(m_aux); expr_ref_vector args(m); while (!todo.empty()) { app *p, *r; expr *a; p = todo.back(); if (cache.find(pr, r)) { todo.pop_back(); continue; } SASSERT(!todo.empty() || pr == p); bool dirty = false; unsigned todo_sz = todo.size(); args.reset(); for (unsigned i = 0, sz = p->get_num_args(); i < sz; ++i) { expr* arg = p->get_arg(i); if (arg == m_aux.get()) { dirty = true; args.push_back(m.mk_true()); } else if (arg == not_aux.get()) { dirty = true; args.push_back(m.mk_false()); } // skip (asserted m_aux) else if (m.is_asserted(arg, a) && a == m_aux.get()) { dirty = true; args.push_back(m.mk_true_proof()); } // skip (hypothesis m_aux) else if (m.is_hypothesis(arg, a) && a == m_aux.get()) { dirty = true; args.push_back(m.mk_true_proof()); } else if (is_app(arg) && cache.find(to_app(arg), r)) { dirty |= (arg != r); args.push_back(r); } else if (is_app(arg)) { todo.push_back(to_app(arg)); } else // -- not an app { args.push_back(arg); } } if (todo_sz < todo.size()) { // -- process parents args.reset(); continue; } // ready to re-create app_ref newp(m); if (!dirty) { newp = p; } else if (m.is_unit_resolution(p)) { ptr_buffer parents; for (unsigned i = 0, sz = args.size() - 1; i < sz; ++i) { app *arg = to_app(args.get(i)); if (!m.is_true(m.get_fact(arg))) parents.push_back(arg); } // unit resolution that collapsed to nothing if (parents.size() == 1) { newp = parents.get(0); } else { // rebuild unit resolution newp = m.mk_unit_resolution(parents.size(), parents.c_ptr()); // XXX the old and new facts should be // equivalent. The test here is much // stronger. It might need to be relaxed. SASSERT(m.get_fact(newp) == args.back()); pinned.push_back(newp); } } else if (matches_fact(args, a)) { newp = to_app(a); } else { expr_ref papp(m); mk_app(p->get_decl(), args, papp); newp = to_app(papp.get()); pinned.push_back(newp); } cache.insert(p, newp); todo.pop_back(); CTRACE("virtual", p->get_decl_kind() == PR_TH_LEMMA && p->get_decl()->get_parameter(0).get_symbol() == "arith" && p->get_decl()->get_num_parameters() > 1 && p->get_decl()->get_parameter(1).get_symbol() == "farkas", tout << "Old pf: " << mk_pp(p, m) << "\n" << "New pf: " << mk_pp(newp, m) << "\n";); } proof *r; VERIFY(cache.find(pr, r)); DEBUG_CODE( proof_checker pc(m); expr_ref_vector side(m); SASSERT(pc.check(r, side)); ); res = r ; } }; #endif z3-z3-4.8.7/src/ast/recfun_decl_plugin.cpp000066400000000000000000000367371356505360400203600ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation, Simon Cruanes Module Name: recfun_decl_plugin.cpp Abstract: Declaration and definition of (potentially recursive) functions Author: Simon Cruanes 2017-11 Revision History: --*/ #include #include #include "ast/expr_functors.h" #include "ast/recfun_decl_plugin.h" #include "ast/ast_pp.h" #include "util/scoped_ptr_vector.h" #define TRACEFN(x) TRACE("recfun", tout << x << '\n';) #define VALIDATE_PARAM(m, _pred_) if (!(_pred_)) m.raise_exception("invalid parameter to recfun " #_pred_); namespace recfun { case_def::case_def( ast_manager &m, family_id fid, def * d, std::string & name, unsigned case_index, sort_ref_vector const & arg_sorts, expr_ref_vector const& guards, expr* rhs) : m_pred(m), m_guards(guards), m_rhs(expr_ref(rhs,m)), m_def(d) { parameter p(case_index); func_decl_info info(fid, OP_FUN_CASE_PRED, 1, &p); m_pred = m.mk_func_decl(symbol(name.c_str()), arg_sorts.size(), arg_sorts.c_ptr(), m.mk_bool_sort(), info); } def::def(ast_manager &m, family_id fid, symbol const & s, unsigned arity, sort* const * domain, sort* range, bool is_generated) : m(m), m_name(s), m_domain(m, arity, domain), m_range(range, m), m_vars(m), m_cases(), m_decl(m), m_rhs(m), m_fid(fid) { SASSERT(arity == get_arity()); parameter p(is_generated); func_decl_info info(fid, OP_FUN_DEFINED, 1, &p); m_decl = m.mk_func_decl(s, arity, domain, range, info); } bool def::contains_def(util& u, expr * e) { struct def_find_p : public i_expr_pred { util& u; def_find_p(util& u): u(u) {} bool operator()(expr* a) override { return is_app(a) && u.is_defined(to_app(a)->get_decl()); } }; def_find_p p(u); check_pred cp(p, m, false); return cp(e); } // does `e` contain any `ite` construct? bool def::contains_ite(util& u, expr * e) { struct ite_find_p : public i_expr_pred { ast_manager & m; def& d; util& u; ite_find_p(ast_manager & m, def& d, util& u) : m(m), d(d), u(u) {} bool operator()(expr * e) override { return m.is_ite(e) && d.contains_def(u, e); } }; // ignore ites under quantifiers. // this is redundant as the code // that unfolds ites uses quantifier-free portion. ite_find_p p(m, *this, u); check_pred cp(p, m, false); return cp(e); } /* * compilation of functions to a list of cases. * * We use a backtracking algorithm in a relatively functional style, * where the multiple states (corresponding to alternatives) are stored in * a region, and deallocated at the end */ // immutable list of choices of `ite` terms (mapping each one's condition to true/false) struct choice_lst { app * ite; bool sign; choice_lst const * next; // or null for the last one choice_lst(app * ite, bool sign, choice_lst const * next) : ite(ite), sign(sign), next(next) {} }; struct ite_lst { app * ite; // invariant: `is_ite(e)` ite_lst const * next; ite_lst(app * ite, ite_lst const * next) : ite(ite), next(next) {} }; // immutable stack of expressions to unfold struct unfold_lst { expr * e; unfold_lst const * next; // or null for last one }; // main state for one branch of the search tree. struct branch { choice_lst const * path; // choices made so far ite_lst const * to_split; // `ite` terms to make a choice on unfold_lst const * to_unfold; // terms yet to unfold branch(unfold_lst const * to_unfold): path(nullptr), to_split(nullptr), to_unfold(to_unfold) {} branch(choice_lst const * path, ite_lst const * to_split, unfold_lst const * to_unfold) : path(path), to_split(to_split), to_unfold(to_unfold) {} branch(branch const & from) : path(from.path), to_split(from.to_split), to_unfold(from.to_unfold) {} }; // state for computing cases from the RHS of a functions' definition class case_state { region m_reg; vector m_branches; public: case_state() : m_reg(), m_branches() {} bool empty() const { return m_branches.empty(); } branch pop_branch() { branch res = m_branches.back(); m_branches.pop_back(); return res; } void push_branch(branch const & b) { m_branches.push_back(b); } unfold_lst const * cons_unfold(expr * e, unfold_lst const * next) { return new (m_reg) unfold_lst{e, next}; } unfold_lst const * cons_unfold(expr * e1, expr * e2, unfold_lst const * next) { return cons_unfold(e1, cons_unfold(e2, next)); } unfold_lst const * mk_unfold_lst(expr * e) { return cons_unfold(e, nullptr); } ite_lst const * cons_ite(app * ite, ite_lst const * next) { return new (m_reg) ite_lst{ite, next}; } choice_lst const * cons_choice(app * ite, bool sign, choice_lst const * next) { return new (m_reg) choice_lst{ite, sign, next}; } }; //next) { app * ite = choices->ite; expr* c = nullptr, *th = nullptr, *el = nullptr; VERIFY(m.is_ite(ite, c, th, el)); // condition to add to the guard conditions.push_back(choices->sign ? c : m.mk_not(c)); // binding to add to the substitution subst.insert(ite, choices->sign ? th : el); } } void def::add_case(std::string & name, unsigned case_index, expr_ref_vector const& conditions, expr * rhs, bool is_imm) { case_def c(m, m_fid, this, name, case_index, get_domain(), conditions, rhs); c.set_is_immediate(is_imm); TRACEFN("add_case " << name << " " << mk_pp(rhs, m) << " :is_imm " << is_imm << " :guards " << conditions); m_cases.push_back(c); } // Compute a set of cases, given the RHS void def::compute_cases(util& u, replace& subst, is_immediate_pred & is_i, unsigned n_vars, var *const * vars, expr* rhs) { VERIFY(m_cases.empty() && "cases cannot already be computed"); SASSERT(n_vars == m_domain.size()); TRACEFN("compute cases " << mk_pp(rhs, m)); unsigned case_idx = 0; std::string name("case-"); name.append(m_name.bare_str()); m_vars.append(n_vars, vars); m_rhs = rhs; expr_ref_vector conditions(m); // is the function a macro (unconditional body)? if (n_vars == 0 || !contains_ite(u, rhs)) { // constant function or trivial control flow, only one (dummy) case add_case(name, 0, conditions, rhs); return; } // analyze control flow of `rhs`, accumulating guards and // rebuilding a `ite`-free RHS on the fly for each path in `rhs`. // Each such `ite`-free term is converted into a case_def and added to definition. case_state st; st.push_branch(branch(st.mk_unfold_lst(rhs))); while (! st.empty()) { TRACEFN("main loop iter"); branch b = st.pop_branch(); // first: unfold expressions, stopping when we meet subterms that are `ite` while (b.to_unfold != nullptr) { ptr_vector stack; stack.push_back(b.to_unfold->e); b.to_unfold = b.to_unfold->next; while (! stack.empty()) { expr * e = stack.back(); stack.pop_back(); TRACEFN("unfold: " << mk_pp(e, m)); if (m.is_ite(e)) { // need to do a case split on `e`, forking the search space b.to_split = st.cons_ite(to_app(e), b.to_split); } else if (is_app(e)) { // explore arguments for (expr * arg : *to_app(e)) { if (contains_ite(u, arg)) { stack.push_back(arg); } } } } } if (b.to_split != nullptr) { // split one `ite`, which will lead to distinct (sets of) cases app * ite = b.to_split->ite; TRACEFN("split: " << mk_pp(ite, m)); expr* c = nullptr, *th = nullptr, *el = nullptr; VERIFY(m.is_ite(ite, c, th, el)); /* explore both positive choice and negative choice. * each contains a longer path, with `ite` mapping to `true` (resp. `false), * and must unfold the `then` (resp. `else`) branch. * We must also unfold the test itself, for it could contain * tests. */ branch b_pos(st.cons_choice(ite, true, b.path), b.to_split->next, st.cons_unfold(c, th, b.to_unfold)); branch b_neg(st.cons_choice(ite, false, b.path), b.to_split->next, st.cons_unfold(c, el, b.to_unfold)); st.push_branch(b_neg); st.push_branch(b_pos); } else { // leaf of the search tree conditions.reset(); subst.reset(); convert_path(m, b.path, conditions, subst); // substitute, to get rid of `ite` terms expr_ref case_rhs = subst(rhs); TRACEFN("case_rhs: " << case_rhs); for (unsigned i = 0; i < conditions.size(); ++i) { conditions[i] = subst(conditions.get(i)); } // yield new case bool is_imm = is_i(case_rhs); add_case(name, case_idx++, conditions, case_rhs, is_imm); } } TRACEFN("done analyzing " << get_name()); } /* * Main manager for defined functions */ util::util(ast_manager & m) : m_manager(m), m_fid(m.get_family_id("recfun")), m_plugin(dynamic_cast(m.get_plugin(m_fid))) { } util::~util() { } def * util::decl_fun(symbol const& name, unsigned n, sort *const * domain, sort * range, bool is_generated) { return alloc(def, m(), m_fid, name, n, domain, range, is_generated); } void util::set_definition(replace& subst, promise_def & d, unsigned n_vars, var * const * vars, expr * rhs) { d.set_definition(subst, n_vars, vars, rhs); } app_ref util::mk_depth_limit_pred(unsigned d) { parameter p(d); func_decl_info info(m_fid, OP_DEPTH_LIMIT, 1, &p); func_decl* decl = m().mk_const_decl(symbol("recfun-depth-limit"), m().mk_bool_sort(), info); return app_ref(m().mk_const(decl), m()); } // used to know which `app` are from this theory struct is_imm_pred : is_immediate_pred { util & u; is_imm_pred(util & u) : u(u) {} bool operator()(expr * rhs) override { // find an `app` that is an application of a defined function struct find : public i_expr_pred { util & u; find(util & u) : u(u) {} bool operator()(expr * e) override { //return is_app(e) ? u.owns_app(to_app(e)) : false; if (! is_app(e)) return false; app * a = to_app(e); return u.is_defined(a); } }; find f(u); check_pred cp(f, u.m()); bool contains_defined_fun = cp(rhs); return ! contains_defined_fun; } }; // set definition void promise_def::set_definition(replace& r, unsigned n_vars, var * const * vars, expr * rhs) { SASSERT(n_vars == d->get_arity()); is_imm_pred is_i(*u); d->compute_cases(*u, r, is_i, n_vars, vars, rhs); } namespace decl { plugin::plugin() : decl_plugin(), m_defs(), m_case_defs() {} plugin::~plugin() { finalize(); } void plugin::finalize() { for (auto& kv : m_defs) { dealloc(kv.m_value); } m_defs.reset(); // m_case_defs does not own its data, no need to deallocate m_case_defs.reset(); m_util = nullptr; // force deletion } util & plugin::u() const { SASSERT(m_manager); SASSERT(m_family_id != null_family_id); if (!m_util.get()) { m_util = alloc(util, *m_manager); } return *(m_util.get()); } promise_def plugin::mk_def(symbol const& name, unsigned n, sort *const * params, sort * range, bool is_generated) { def* d = u().decl_fun(name, n, params, range, is_generated); SASSERT(!m_defs.contains(d->get_decl())); m_defs.insert(d->get_decl(), d); return promise_def(&u(), d); } promise_def plugin::ensure_def(symbol const& name, unsigned n, sort *const * params, sort * range, bool is_generated) { def* d = u().decl_fun(name, n, params, range, is_generated); def* d2 = nullptr; if (m_defs.find(d->get_decl(), d2)) { dealloc(d2); } m_defs.insert(d->get_decl(), d); return promise_def(&u(), d); } void plugin::set_definition(replace& r, promise_def & d, unsigned n_vars, var * const * vars, expr * rhs) { u().set_definition(r, d, n_vars, vars, rhs); for (case_def & c : d.get_def()->get_cases()) { m_case_defs.insert(c.get_decl(), &c); } } bool plugin::has_defs() const { return !m_case_defs.empty(); } def* plugin::mk_def(replace& subst, symbol const& name, unsigned n, sort ** params, sort * range, unsigned n_vars, var ** vars, expr * rhs) { promise_def d = mk_def(name, n, params, range); SASSERT(! m_defs.contains(d.get_def()->get_decl())); set_definition(subst, d, n_vars, vars, rhs); return d.get_def(); } // generic declaration of symbols func_decl * plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { UNREACHABLE(); return nullptr; } } } z3-z3-4.8.7/src/ast/recfun_decl_plugin.h000066400000000000000000000241311356505360400200060ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation, Simon Cruanes Module Name: recfun_decl_plugin.h Abstract: Declaration and definition of (potentially recursive) functions Author: Simon Cruanes 2017-11 Revision History: --*/ #pragma once #include "ast/ast.h" #include "util/obj_hashtable.h" namespace recfun { class case_def; // const & args) const { ast_manager& m = m_pred.get_manager(); return app_ref(m.mk_app(m_pred, args.size(), args.c_ptr()), m); } def * get_def() const { return m_def; } expr_ref_vector const & get_guards() const { return m_guards; } expr * get_guards_c_ptr() const { return *m_guards.c_ptr(); } expr * get_guard(unsigned i) const { return m_guards[i]; } expr * get_rhs() const { return m_rhs; } unsigned num_guards() const { return m_guards.size(); } bool is_immediate() const { return m_immediate; }; void set_is_immediate(bool b) { m_immediate = b; } }; // closure for computing whether a `rhs` expression is immediate struct is_immediate_pred { virtual bool operator()(expr * rhs) = 0; }; class def { friend class util; friend class promise_def; typedef vector cases; ast_manager & m; symbol m_name; // def_map; typedef obj_map case_def_map; mutable scoped_ptr m_util; def_map m_defs; // function->def case_def_map m_case_defs; // case_pred->def ast_manager & m() { return *m_manager; } public: plugin(); ~plugin() override; void finalize() override; util & u() const; // build or return util bool is_fully_interp(sort * s) const override { return false; } // might depend on unin sorts decl_plugin * mk_fresh() override { return alloc(plugin); } sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) override { UNREACHABLE(); return nullptr; } func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) override; promise_def mk_def(symbol const& name, unsigned n, sort *const * params, sort * range, bool is_generated = false); promise_def ensure_def(symbol const& name, unsigned n, sort *const * params, sort * range, bool is_generated = false); void set_definition(replace& r, promise_def & d, unsigned n_vars, var * const * vars, expr * rhs); def* mk_def(replace& subst, symbol const& name, unsigned n, sort ** params, sort * range, unsigned n_vars, var ** vars, expr * rhs); bool has_def(func_decl* f) const { return m_defs.contains(f); } bool has_defs() const; def const& get_def(func_decl* f) const { return *(m_defs[f]); } promise_def get_promise_def(func_decl* f) const { return promise_def(&u(), m_defs[f]); } def& get_def(func_decl* f) { return *(m_defs[f]); } bool has_case_def(func_decl* f) const { return m_case_defs.contains(f); } case_def& get_case_def(func_decl* f) { SASSERT(has_case_def(f)); return *(m_case_defs[f]); } func_decl_ref_vector get_rec_funs() { func_decl_ref_vector result(m()); for (auto& kv : m_defs) result.push_back(kv.m_key); return result; } }; } // Various utils for recursive functions class util { friend class decl::plugin; ast_manager & m_manager; family_id m_fid; decl::plugin * m_plugin; bool compute_is_immediate(expr * rhs); void set_definition(replace& r, promise_def & d, unsigned n_vars, var * const * vars, expr * rhs); public: util(ast_manager &m); ~util(); ast_manager & m() { return m_manager; } decl::plugin& get_plugin() { return *m_plugin; } bool is_case_pred(expr * e) const { return is_app_of(e, m_fid, OP_FUN_CASE_PRED); } bool is_defined(expr * e) const { return is_app_of(e, m_fid, OP_FUN_DEFINED); } bool is_defined(func_decl* f) const { return is_decl_of(f, m_fid, OP_FUN_DEFINED); } bool is_generated(func_decl* f) const { return is_defined(f) && f->get_parameter(0).get_int() == 1; } bool is_depth_limit(expr * e) const { return is_app_of(e, m_fid, OP_DEPTH_LIMIT); } bool owns_app(app * e) const { return e->get_family_id() == m_fid; } //has_defs(); } //has_def(f)); return m_plugin->get_def(f); } case_def& get_case_def(expr* e) { SASSERT(is_case_pred(e)); return m_plugin->get_case_def(to_app(e)->get_decl()); } app* mk_fun_defined(def const & d, unsigned n_args, expr * const * args) { return m().mk_app(d.get_decl(), n_args, args); } app* mk_fun_defined(def const & d, ptr_vector const & args) { return mk_fun_defined(d, args.size(), args.c_ptr()); } func_decl_ref_vector get_rec_funs() { return m_plugin->get_rec_funs(); } app_ref mk_depth_limit_pred(unsigned d); }; } z3-z3-4.8.7/src/ast/recurse_expr.h000066400000000000000000000023251356505360400166660ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: recurse_expr.h Abstract: Traverse an expression applying a visitor. Author: Leonardo de Moura (leonardo) 2008-01-11. Revision History: --*/ #ifndef RECURSE_EXPR_H_ #define RECURSE_EXPR_H_ #include "ast/ast.h" #include "util/obj_hashtable.h" template class recurse_expr : public Visitor { obj_map m_cache; ptr_vector m_todo; vector m_results1; vector m_results2; bool is_cached(expr * n) const { T c; return m_cache.find(n, c); } T get_cached(expr * n) const { return m_cache.find(n); } void cache_result(expr * n, T c) { m_cache.insert(n, c); } void visit(expr * n, bool & visited); bool visit_children(expr * n); void process(expr * n); public: recurse_expr(Visitor const & v = Visitor()):Visitor(v) {} T operator()(expr * n); void reset() { m_cache.reset(); m_todo.reset(); } void finalize() { m_cache.finalize(); m_todo.finalize(); } }; #endif /* RECURSE_EXPR_H_ */ z3-z3-4.8.7/src/ast/recurse_expr_def.h000066400000000000000000000066451356505360400175150ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: recurse_expr_def.h Abstract: Traverse an expression applying a visitor. Author: Leonardo de Moura (leonardo) 2008-01-11. Revision History: --*/ #ifndef RECURSE_EXPR_DEF_H_ #define RECURSE_EXPR_DEF_H_ #include "ast/recurse_expr.h" template inline void recurse_expr::visit(expr * n, bool & visited) { if (!is_cached(n)) { m_todo.push_back(n); visited = false; } } template bool recurse_expr::visit_children(expr * n) { bool visited = true; unsigned num; switch (n->get_kind()) { case AST_APP: num = to_app(n)->get_num_args(); for (unsigned j = 0; j < num; j++) visit(to_app(n)->get_arg(j), visited); break; case AST_QUANTIFIER: if (!IgnorePatterns) { num = to_quantifier(n)->get_num_patterns(); for (unsigned j = 0; j < num; j++) visit(to_quantifier(n)->get_pattern(j), visited); num = to_quantifier(n)->get_num_no_patterns(); for (unsigned j = 0; j < num; j++) visit(to_quantifier(n)->get_no_pattern(j), visited); } visit(to_quantifier(n)->get_expr(), visited); break; default: break; } return visited; } template void recurse_expr::process(expr * n) { unsigned num; switch (n->get_kind()) { case AST_APP: m_results1.reset(); num = to_app(n)->get_num_args(); for (unsigned j = 0; j < num; j++) m_results1.push_back(get_cached(to_app(n)->get_arg(j))); cache_result(n, this->Visitor::visit(to_app(n), m_results1.c_ptr())); break; case AST_VAR: cache_result(n, this->Visitor::visit(to_var(n))); break; case AST_QUANTIFIER: if (IgnorePatterns) { cache_result(n, this->Visitor::visit(to_quantifier(n), get_cached(to_quantifier(n)->get_expr()), nullptr, nullptr)); } else { m_results1.reset(); m_results2.reset(); num = to_quantifier(n)->get_num_patterns(); for (unsigned j = 0; j < num; j++) m_results1.push_back(get_cached(to_quantifier(n)->get_pattern(j))); num = to_quantifier(n)->get_num_no_patterns(); for (unsigned j = 0; j < num; j++) m_results2.push_back(get_cached(to_quantifier(n)->get_no_pattern(j))); cache_result(n, this->Visitor::visit(to_quantifier(n), get_cached(to_quantifier(n)->get_expr()), m_results1.c_ptr(), m_results2.c_ptr())); } break; default: UNREACHABLE(); } } template T recurse_expr::operator()(expr * r) { m_todo.push_back(r); while (!m_todo.empty()) { expr * n = m_todo.back(); if (is_cached(n)) m_todo.pop_back(); else if (visit_children(n)) { m_todo.pop_back(); process(n); } } return get_cached(r); } #endif /* RECURSE_EXPR_DEF_H_ */ z3-z3-4.8.7/src/ast/reg_decl_plugins.cpp000066400000000000000000000040271356505360400200210ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: reg_decl_plugins Abstract: Goodie for installing all available declarations plugins in an ast_manager Author: Leonardo de Moura (leonardo) 2012-10-24. Revision History: --*/ #include "ast/ast.h" #include "ast/arith_decl_plugin.h" #include "ast/array_decl_plugin.h" #include "ast/bv_decl_plugin.h" #include "ast/datatype_decl_plugin.h" #include "ast/recfun_decl_plugin.h" #include "ast/dl_decl_plugin.h" #include "ast/seq_decl_plugin.h" #include "ast/pb_decl_plugin.h" #include "ast/fpa_decl_plugin.h" #include "ast/special_relations_decl_plugin.h" void reg_decl_plugins(ast_manager & m) { if (!m.get_plugin(m.mk_family_id(symbol("arith")))) { m.register_plugin(symbol("arith"), alloc(arith_decl_plugin)); } if (!m.get_plugin(m.mk_family_id(symbol("bv")))) { m.register_plugin(symbol("bv"), alloc(bv_decl_plugin)); } if (!m.get_plugin(m.mk_family_id(symbol("array")))) { m.register_plugin(symbol("array"), alloc(array_decl_plugin)); } if (!m.get_plugin(m.mk_family_id(symbol("datatype")))) { m.register_plugin(symbol("datatype"), alloc(datatype_decl_plugin)); } if (!m.get_plugin(m.mk_family_id(symbol("recfun")))) { m.register_plugin(symbol("recfun"), alloc(recfun::decl::plugin)); } if (!m.get_plugin(m.mk_family_id(symbol("datalog_relation")))) { m.register_plugin(symbol("datalog_relation"), alloc(datalog::dl_decl_plugin)); } if (!m.get_plugin(m.mk_family_id(symbol("seq")))) { m.register_plugin(symbol("seq"), alloc(seq_decl_plugin)); } if (!m.get_plugin(m.mk_family_id(symbol("fpa")))) { m.register_plugin(symbol("fpa"), alloc(fpa_decl_plugin)); } if (!m.get_plugin(m.mk_family_id(symbol("pb")))) { m.register_plugin(symbol("pb"), alloc(pb_decl_plugin)); } if (!m.get_plugin(m.mk_family_id(symbol("special_relations")))) { m.register_plugin(symbol("special_relations"), alloc(special_relations_decl_plugin)); } } z3-z3-4.8.7/src/ast/reg_decl_plugins.h000066400000000000000000000005761356505360400174730ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: reg_decl_plugins Abstract: Goodie for installing all available declarations plugins in an ast_manager Author: Leonardo de Moura (leonardo) 2012-10-24. Revision History: --*/ #ifndef REG_DECL_PLUGINS_H_ #define REG_DECL_PLUGINS_H_ class ast_manager; void reg_decl_plugins(ast_manager & m); #endif z3-z3-4.8.7/src/ast/rewriter/000077500000000000000000000000001356505360400156505ustar00rootroot00000000000000z3-z3-4.8.7/src/ast/rewriter/CMakeLists.txt000066400000000000000000000020421356505360400204060ustar00rootroot00000000000000z3_add_component(rewriter SOURCES arith_rewriter.cpp array_rewriter.cpp ast_counter.cpp bit2int.cpp bool_rewriter.cpp bv_bounds.cpp bv_elim.cpp bv_rewriter.cpp datatype_rewriter.cpp der.cpp distribute_forall.cpp dl_rewriter.cpp elim_bounds.cpp enum2bv_rewriter.cpp expr_replacer.cpp expr_safe_replace.cpp factor_equivs.cpp factor_rewriter.cpp fpa_rewriter.cpp func_decl_replace.cpp hoist_rewriter.cpp inj_axiom.cpp label_rewriter.cpp maximize_ac_sharing.cpp mk_simplified_app.cpp pb_rewriter.cpp pb2bv_rewriter.cpp push_app_ite.cpp quant_hoist.cpp rewriter.cpp seq_rewriter.cpp th_rewriter.cpp var_subst.cpp bv_trailing.cpp mk_extract_proc.cpp COMPONENT_DEPENDENCIES ast automata polynomial PYG_FILES arith_rewriter_params.pyg array_rewriter_params.pyg bool_rewriter_params.pyg bv_rewriter_params.pyg fpa_rewriter_params.pyg poly_rewriter_params.pyg rewriter_params.pyg ) z3-z3-4.8.7/src/ast/rewriter/arith_rewriter.cpp000066400000000000000000001756231356505360400214240ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: arith_rewriter.cpp Abstract: Basic rewriting rules for arithmetic Author: Leonardo (leonardo) 2011-04-10 Notes: --*/ #include "ast/rewriter/arith_rewriter.h" #include "ast/rewriter/arith_rewriter_params.hpp" #include "ast/rewriter/poly_rewriter_def.h" #include "math/polynomial/algebraic_numbers.h" #include "ast/ast_pp.h" void arith_rewriter::updt_local_params(params_ref const & _p) { arith_rewriter_params p(_p); m_arith_lhs = p.arith_lhs(); m_arith_ineq_lhs = p.arith_ineq_lhs(); m_gcd_rounding = p.gcd_rounding(); m_elim_to_real = p.elim_to_real(); m_push_to_real = p.push_to_real(); m_anum_simp = p.algebraic_number_evaluator(); m_max_degree = p.max_degree(); m_expand_power = p.expand_power(); m_mul2power = p.mul_to_power(); m_elim_rem = p.elim_rem(); m_expand_tan = p.expand_tan(); m_eq2ineq = p.eq2ineq(); set_sort_sums(p.sort_sums()); } void arith_rewriter::updt_params(params_ref const & p) { poly_rewriter::updt_params(p); updt_local_params(p); } void arith_rewriter::get_param_descrs(param_descrs & r) { poly_rewriter::get_param_descrs(r); arith_rewriter_params::collect_param_descrs(r); } br_status arith_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { br_status st = BR_FAILED; SASSERT(f->get_family_id() == get_fid()); switch (f->get_decl_kind()) { case OP_NUM: st = BR_FAILED; break; case OP_IRRATIONAL_ALGEBRAIC_NUM: st = BR_FAILED; break; case OP_LE: SASSERT(num_args == 2); st = mk_le_core(args[0], args[1], result); break; case OP_GE: SASSERT(num_args == 2); st = mk_ge_core(args[0], args[1], result); break; case OP_LT: SASSERT(num_args == 2); st = mk_lt_core(args[0], args[1], result); break; case OP_GT: SASSERT(num_args == 2); st = mk_gt_core(args[0], args[1], result); break; case OP_ADD: st = mk_add_core(num_args, args, result); break; case OP_MUL: st = mk_mul_core(num_args, args, result); break; case OP_SUB: st = mk_sub(num_args, args, result); break; case OP_DIV: if (num_args == 1) { result = args[0]; st = BR_DONE; break; } SASSERT(num_args == 2); st = mk_div_core(args[0], args[1], result); break; case OP_IDIV: if (num_args == 1) { result = args[0]; st = BR_DONE; break; } SASSERT(num_args == 2); st = mk_idiv_core(args[0], args[1], result); break; case OP_IDIVIDES: SASSERT(num_args == 1); st = mk_idivides(f->get_parameter(0).get_int(), args[0], result); break; case OP_MOD: SASSERT(num_args == 2); st = mk_mod_core(args[0], args[1], result); break; case OP_REM: SASSERT(num_args == 2); st = mk_rem_core(args[0], args[1], result); break; case OP_UMINUS: SASSERT(num_args == 1); st = mk_uminus(args[0], result); break; case OP_TO_REAL: SASSERT(num_args == 1); st = mk_to_real_core(args[0], result); break; case OP_TO_INT: SASSERT(num_args == 1); st = mk_to_int_core(args[0], result); break; case OP_IS_INT: SASSERT(num_args == 1); st = mk_is_int(args[0], result); break; case OP_POWER: SASSERT(num_args == 2); st = mk_power_core(args[0], args[1], result); break; case OP_ABS: SASSERT(num_args == 1); st = mk_abs_core(args[0], result); break; case OP_SIN: SASSERT(num_args == 1); st = mk_sin_core(args[0], result); break; case OP_COS: SASSERT(num_args == 1); st = mk_cos_core(args[0], result); break; case OP_TAN: SASSERT(num_args == 1); st = mk_tan_core(args[0], result); break; case OP_ASIN: SASSERT(num_args == 1); st = mk_asin_core(args[0], result); break; case OP_ACOS: SASSERT(num_args == 1); st = mk_acos_core(args[0], result); break; case OP_ATAN: SASSERT(num_args == 1); st = mk_atan_core(args[0], result); break; case OP_SINH: SASSERT(num_args == 1); st = mk_sinh_core(args[0], result); break; case OP_COSH: SASSERT(num_args == 1); st = mk_cosh_core(args[0], result); break; case OP_TANH: SASSERT(num_args == 1); st = mk_tanh_core(args[0], result); break; default: st = BR_FAILED; break; } CTRACE("arith_rewriter", st != BR_FAILED, tout << st << ": " << mk_pp(f, m()); for (unsigned i = 0; i < num_args; ++i) tout << mk_pp(args[i], m()) << " "; tout << "\n==>\n" << mk_pp(result.get(), m()) << "\n"; tout << "args: " << to_app(result)->get_num_args() << "\n"; ); return st; } void arith_rewriter::get_coeffs_gcd(expr * t, numeral & g, bool & first, unsigned & num_consts) { unsigned sz; expr * const * ms = get_monomials(t, sz); SASSERT(sz >= 1); numeral a; for (unsigned i = 0; i < sz; i++) { expr * arg = ms[i]; if (is_numeral(arg, a)) { if (!a.is_zero()) num_consts++; continue; } if (first) { get_power_product(arg, g); SASSERT(g.is_int()); first = false; } else { get_power_product(arg, a); SASSERT(a.is_int()); g = gcd(abs(a), g); } if (g.is_one()) return; } } bool arith_rewriter::div_polynomial(expr * t, numeral const & g, const_treatment ct, expr_ref & result) { SASSERT(m_util.is_int(t)); SASSERT(!g.is_one()); unsigned sz; expr * const * ms = get_monomials(t, sz); expr_ref_buffer new_args(m()); numeral a; for (unsigned i = 0; i < sz; i++) { expr * arg = ms[i]; if (is_numeral(arg, a)) { a /= g; if (!a.is_int()) { switch (ct) { case CT_FLOOR: a = floor(a); break; case CT_CEIL: a = ceil(a); break; case CT_FALSE: return false; } } if (!a.is_zero()) new_args.push_back(m_util.mk_numeral(a, true)); continue; } expr * pp = get_power_product(arg, a); a /= g; SASSERT(a.is_int()); if (!a.is_zero()) { if (a.is_one()) new_args.push_back(pp); else new_args.push_back(m_util.mk_mul(m_util.mk_numeral(a, true), pp)); } } switch (new_args.size()) { case 0: result = m_util.mk_numeral(numeral(0), true); return true; case 1: result = new_args[0]; return true; default: result = m_util.mk_add(new_args.size(), new_args.c_ptr()); return true; } } bool arith_rewriter::is_bound(expr * arg1, expr * arg2, op_kind kind, expr_ref & result) { numeral b, c; if (!is_add(arg1) && !m_util.is_mod(arg1) && is_numeral(arg2, c)) { numeral a; bool r = false; expr * pp = get_power_product(arg1, a); if (a.is_neg()) { a.neg(); c.neg(); kind = inv(kind); r = true; } if (!a.is_one()) r = true; if (!r) return false; c /= a; bool is_int = m_util.is_int(arg1); if (is_int && !c.is_int()) { switch (kind) { case LE: c = floor(c); break; case GE: c = ceil(c); break; case EQ: result = m().mk_false(); return true; } } expr * k = m_util.mk_numeral(c, is_int); switch (kind) { case LE: result = m_util.mk_le(pp, k); return true; case GE: result = m_util.mk_ge(pp, k); return true; case EQ: result = m_util.mk_eq(pp, k); return true; } } expr* t1, *t2; bool is_int = false; if (m_util.is_mod(arg2)) { std::swap(arg1, arg2); switch (kind) { case LE: kind = GE; break; case GE: kind = LE; break; case EQ: break; } } if (m_util.is_numeral(arg2, c, is_int) && is_int && m_util.is_mod(arg1, t1, t2) && m_util.is_numeral(t2, b, is_int) && !b.is_zero()) { // mod x b <= c = false if c < 0, b != 0, true if c >= b, b != 0 if (c.is_neg()) { switch (kind) { case EQ: case LE: result = m().mk_false(); return true; case GE: result = m().mk_true(); return true; } } if (c.is_zero() && kind == GE) { result = m().mk_true(); return true; } if (c.is_pos() && c >= abs(b)) { switch (kind) { case LE: result = m().mk_true(); return true; case EQ: case GE: result = m().mk_false(); return true; } } // mod x b <= b - 1 if (c + rational::one() == abs(b) && kind == LE) { result = m().mk_true(); return true; } } return false; } bool arith_rewriter::elim_to_real_var(expr * var, expr_ref & new_var) { numeral val; if (m_util.is_numeral(var, val)) { if (!val.is_int()) return false; new_var = m_util.mk_numeral(val, true); return true; } else if (m_util.is_to_real(var)) { new_var = to_app(var)->get_arg(0); return true; } return false; } bool arith_rewriter::elim_to_real_mon(expr * monomial, expr_ref & new_monomial) { if (m_util.is_mul(monomial)) { expr_ref_buffer new_vars(m()); expr_ref new_var(m()); unsigned num = to_app(monomial)->get_num_args(); for (unsigned i = 0; i < num; i++) { if (!elim_to_real_var(to_app(monomial)->get_arg(i), new_var)) return false; new_vars.push_back(new_var); } new_monomial = m_util.mk_mul(new_vars.size(), new_vars.c_ptr()); return true; } else { return elim_to_real_var(monomial, new_monomial); } } bool arith_rewriter::elim_to_real_pol(expr * p, expr_ref & new_p) { if (m_util.is_add(p)) { expr_ref_buffer new_monomials(m()); expr_ref new_monomial(m()); for (expr* arg : *to_app(p)) { if (!elim_to_real_mon(arg, new_monomial)) return false; new_monomials.push_back(new_monomial); } new_p = m_util.mk_add(new_monomials.size(), new_monomials.c_ptr()); return true; } else { return elim_to_real_mon(p, new_p); } } bool arith_rewriter::elim_to_real(expr * arg1, expr * arg2, expr_ref & new_arg1, expr_ref & new_arg2) { if (!m_util.is_real(arg1)) return false; return elim_to_real_pol(arg1, new_arg1) && elim_to_real_pol(arg2, new_arg2); } bool arith_rewriter::is_reduce_power_target(expr * arg, bool is_eq) { unsigned sz; expr * const * args; if (m_util.is_mul(arg)) { sz = to_app(arg)->get_num_args(); args = to_app(arg)->get_args(); } else { sz = 1; args = &arg; } for (unsigned i = 0; i < sz; i++) { expr * arg = args[i]; expr* arg0, *arg1; if (m_util.is_power(arg, arg0, arg1)) { rational k; if (m_util.is_numeral(arg1, k) && k.is_int() && ((is_eq && k > rational(1)) || (!is_eq && k > rational(2)))) return true; } } return false; } expr * arith_rewriter::reduce_power(expr * arg, bool is_eq) { if (is_zero(arg)) return arg; unsigned sz; expr * const * args; if (m_util.is_mul(arg)) { sz = to_app(arg)->get_num_args(); args = to_app(arg)->get_args(); } else { sz = 1; args = &arg; } ptr_buffer new_args; rational k; for (unsigned i = 0; i < sz; i++) { expr * arg = args[i]; expr * arg0, *arg1; if (m_util.is_power(arg, arg0, arg1) && m_util.is_numeral(arg1, k) && k.is_int() && ((is_eq && k > rational(1)) || (!is_eq && k > rational(2)))) { if (is_eq || !k.is_even()) { new_args.push_back(arg0); } else { new_args.push_back(m_util.mk_power(arg0, m_util.mk_numeral(rational(2), m_util.is_int(arg)))); } } else { new_args.push_back(arg); } } SASSERT(new_args.size() >= 1); if (new_args.size() == 1) return new_args[0]; else return m_util.mk_mul(new_args.size(), new_args.c_ptr()); } br_status arith_rewriter::reduce_power(expr * arg1, expr * arg2, op_kind kind, expr_ref & result) { expr * new_arg1 = reduce_power(arg1, kind == EQ); expr * new_arg2 = reduce_power(arg2, kind == EQ); switch (kind) { case LE: result = m_util.mk_le(new_arg1, new_arg2); return BR_REWRITE1; case GE: result = m_util.mk_ge(new_arg1, new_arg2); return BR_REWRITE1; default: result = m().mk_eq(new_arg1, new_arg2); return BR_REWRITE1; } } br_status arith_rewriter::mk_le_ge_eq_core(expr * arg1, expr * arg2, op_kind kind, expr_ref & result) { expr *orig_arg1 = arg1, *orig_arg2 = arg2; expr_ref new_arg1(m()); expr_ref new_arg2(m()); if ((is_zero(arg1) && is_reduce_power_target(arg2, kind == EQ)) || (is_zero(arg2) && is_reduce_power_target(arg1, kind == EQ))) return reduce_power(arg1, arg2, kind, result); br_status st = cancel_monomials(arg1, arg2, m_arith_ineq_lhs || m_arith_lhs, new_arg1, new_arg2); TRACE("mk_le_bug", tout << "st: " << st << " " << new_arg1 << " " << new_arg2 << "\n";); if (st != BR_FAILED) { arg1 = new_arg1; arg2 = new_arg2; } expr_ref new_new_arg1(m()); expr_ref new_new_arg2(m()); if (m_elim_to_real && elim_to_real(arg1, arg2, new_new_arg1, new_new_arg2)) { arg1 = new_new_arg1; arg2 = new_new_arg2; CTRACE("elim_to_real", m_elim_to_real, tout << "after_elim_to_real\n" << mk_ismt2_pp(arg1, m()) << "\n" << mk_ismt2_pp(arg2, m()) << "\n";); if (st == BR_FAILED) st = BR_DONE; } numeral a1, a2; if (is_numeral(arg1, a1) && is_numeral(arg2, a2)) { switch (kind) { case LE: result = a1 <= a2 ? m().mk_true() : m().mk_false(); return BR_DONE; case GE: result = a1 >= a2 ? m().mk_true() : m().mk_false(); return BR_DONE; default: result = a1 == a2 ? m().mk_true() : m().mk_false(); return BR_DONE; } } #define ANUM_LE_GE_EQ() { \ switch (kind) { \ case LE: result = am.le(v1, v2) ? m().mk_true() : m().mk_false(); return BR_DONE; \ case GE: result = am.ge(v1, v2) ? m().mk_true() : m().mk_false(); return BR_DONE; \ default: result = am.eq(v1, v2) ? m().mk_true() : m().mk_false(); return BR_DONE; \ } \ } if (m_anum_simp) { if (is_numeral(arg1, a1) && m_util.is_irrational_algebraic_numeral(arg2)) { anum_manager & am = m_util.am(); scoped_anum v1(am); am.set(v1, a1.to_mpq()); anum const & v2 = m_util.to_irrational_algebraic_numeral(arg2); ANUM_LE_GE_EQ(); } if (m_util.is_irrational_algebraic_numeral(arg1) && is_numeral(arg2, a2)) { anum_manager & am = m_util.am(); anum const & v1 = m_util.to_irrational_algebraic_numeral(arg1); scoped_anum v2(am); am.set(v2, a2.to_mpq()); ANUM_LE_GE_EQ(); } if (m_util.is_irrational_algebraic_numeral(arg1) && m_util.is_irrational_algebraic_numeral(arg2)) { anum_manager & am = m_util.am(); anum const & v1 = m_util.to_irrational_algebraic_numeral(arg1); anum const & v2 = m_util.to_irrational_algebraic_numeral(arg2); ANUM_LE_GE_EQ(); } } if (is_bound(arg1, arg2, kind, result)) return BR_DONE; if (is_bound(arg2, arg1, inv(kind), result)) return BR_DONE; bool is_int = m_util.is_int(arg1); if (is_int && m_gcd_rounding) { bool first = true; numeral g; unsigned num_consts = 0; get_coeffs_gcd(arg1, g, first, num_consts); TRACE("arith_rewriter_gcd", tout << "[step1] g: " << g << ", num_consts: " << num_consts << "\n";); if ((first || !g.is_one()) && num_consts <= 1) get_coeffs_gcd(arg2, g, first, num_consts); TRACE("arith_rewriter_gcd", tout << "[step2] g: " << g << ", num_consts: " << num_consts << "\n";); if (!first && !g.is_one() && num_consts <= 1) { bool is_sat = div_polynomial(arg1, g, (kind == LE ? CT_CEIL : (kind == GE ? CT_FLOOR : CT_FALSE)), new_arg1); if (!is_sat) { result = m().mk_false(); return BR_DONE; } is_sat = div_polynomial(arg2, g, (kind == LE ? CT_FLOOR : (kind == GE ? CT_CEIL : CT_FALSE)), new_arg2); if (!is_sat) { result = m().mk_false(); return BR_DONE; } arg1 = new_arg1.get(); arg2 = new_arg2.get(); st = BR_DONE; } } expr* e1 = nullptr, *e2 = nullptr; if (m_util.is_div(arg1, e1, e2) && (!is_numeral(e2, a2) || !a2.is_zero())) { new_arg1 = e1; new_arg2 = m_util.mk_mul(e2, arg2); expr_ref zero(m_util.mk_numeral(rational(0), m_util.is_int(arg1)), m()); expr_ref is_zero(m().mk_eq(zero, e2), m()); expr_ref div0(m_util.mk_div(e1, zero), m()); expr_ref mul2(m_util.mk_mul(e2, arg2), m()); switch (kind) { case LE: result = m().mk_or( m().mk_and(is_zero, m_util.mk_le(div0, arg2)), m().mk_and(m().mk_not(m_util.mk_le(e2, zero)), m_util.mk_le(e1, mul2)), m().mk_and(m().mk_not(m_util.mk_ge(e2, zero)), m_util.mk_ge(e1, mul2))); return BR_REWRITE_FULL; case GE: result = m().mk_or( m().mk_and(is_zero, m_util.mk_ge(div0, arg2)), m().mk_and(m().mk_not(m_util.mk_le(e2, zero)), m_util.mk_ge(e1, mul2)), m().mk_and(m().mk_not(m_util.mk_ge(e2, zero)), m_util.mk_le(e1, mul2))); return BR_REWRITE_FULL; case EQ: result = m().mk_ite(is_zero, m().mk_eq(div0, arg2), m().mk_eq(e1, mul2)); return BR_REWRITE_FULL; } } expr* c = nullptr, *t = nullptr, *e = nullptr; if (m().is_ite(arg1, c, t, e) && is_numeral(t, a1) && is_numeral(arg2, a2)) { switch (kind) { case LE: result = a1 <= a2 ? m().mk_or(c, m_util.mk_le(e, arg2)) : m().mk_and(m().mk_not(c), m_util.mk_le(e, arg2)); return BR_REWRITE2; case GE: result = a1 >= a2 ? m().mk_or(c, m_util.mk_ge(e, arg2)) : m().mk_and(m().mk_not(c), m_util.mk_ge(e, arg2)); return BR_REWRITE2; case EQ: result = a1 == a2 ? m().mk_or(c, m().mk_eq(e, arg2)) : m().mk_and(m().mk_not(c), m_util.mk_eq(e, arg2)); return BR_REWRITE2; } } if (m().is_ite(arg1, c, t, e) && is_numeral(e, a1) && is_numeral(arg2, a2)) { switch (kind) { case LE: result = a1 <= a2 ? m().mk_or(m().mk_not(c), m_util.mk_le(t, arg2)) : m().mk_and(c, m_util.mk_le(t, arg2)); return BR_REWRITE2; case GE: result = a1 >= a2 ? m().mk_or(m().mk_not(c), m_util.mk_ge(t, arg2)) : m().mk_and(c, m_util.mk_ge(t, arg2)); return BR_REWRITE2; case EQ: result = a1 == a2 ? m().mk_or(m().mk_not(c), m().mk_eq(t, arg2)) : m().mk_and(c, m_util.mk_eq(t, arg2)); return BR_REWRITE2; } } if (m().is_ite(arg1, c, t, e) && arg1->get_ref_count() == 1) { switch (kind) { case LE: result = m().mk_ite(c, m_util.mk_le(t, arg2), m_util.mk_le(e, arg2)); return BR_REWRITE2; case GE: result = m().mk_ite(c, m_util.mk_ge(t, arg2), m_util.mk_ge(e, arg2)); return BR_REWRITE2; case EQ: result = m().mk_ite(c, m().mk_eq(t, arg2), m().mk_eq(e, arg2)); return BR_REWRITE2; } } if ((m_arith_lhs || m_arith_ineq_lhs) && is_numeral(arg2, a2) && is_neg_poly(arg1, new_arg1)) { a2.neg(); new_arg2 = m_util.mk_numeral(a2, m_util.is_int(new_arg1)); switch (kind) { case LE: result = m_util.mk_ge(new_arg1, new_arg2); return BR_DONE; case GE: result = m_util.mk_le(new_arg1, new_arg2); return BR_DONE; case EQ: result = m_util.mk_eq(new_arg1, new_arg2); return BR_DONE; } } else if (st == BR_DONE && arg1 == orig_arg1 && arg2 == orig_arg2) { // Nothing new; return BR_FAILED to avoid rewriting loops. return BR_FAILED; } else if (st != BR_FAILED) { switch (kind) { case LE: result = m_util.mk_le(arg1, arg2); return BR_DONE; case GE: result = m_util.mk_ge(arg1, arg2); return BR_DONE; default: result = m().mk_eq(arg1, arg2); return BR_DONE; } } return BR_FAILED; } br_status arith_rewriter::mk_le_core(expr * arg1, expr * arg2, expr_ref & result) { return mk_le_ge_eq_core(arg1, arg2, LE, result); } br_status arith_rewriter::mk_lt_core(expr * arg1, expr * arg2, expr_ref & result) { result = m().mk_not(m_util.mk_le(arg2, arg1)); return BR_REWRITE2; } br_status arith_rewriter::mk_ge_core(expr * arg1, expr * arg2, expr_ref & result) { return mk_le_ge_eq_core(arg1, arg2, GE, result); } br_status arith_rewriter::mk_gt_core(expr * arg1, expr * arg2, expr_ref & result) { result = m().mk_not(m_util.mk_le(arg1, arg2)); return BR_REWRITE2; } bool arith_rewriter::is_arith_term(expr * n) const { return n->get_kind() == AST_APP && to_app(n)->get_family_id() == get_fid(); } br_status arith_rewriter::mk_eq_core(expr * arg1, expr * arg2, expr_ref & result) { br_status st = BR_FAILED; if (m_eq2ineq) { result = m().mk_and(m_util.mk_le(arg1, arg2), m_util.mk_ge(arg1, arg2)); st = BR_REWRITE2; } else if (m_arith_lhs || is_arith_term(arg1) || is_arith_term(arg2)) { st = mk_le_ge_eq_core(arg1, arg2, EQ, result); } return st; } expr_ref arith_rewriter::neg_monomial(expr* e) const { expr_ref_vector args(m()); rational a1; if (is_app(e) & m_util.is_mul(e)) { if (is_numeral(to_app(e)->get_arg(0), a1)) { if (!a1.is_minus_one()) { args.push_back(m_util.mk_numeral(-a1, m_util.is_int(e))); } args.append(to_app(e)->get_num_args() - 1, to_app(e)->get_args() + 1); } else { args.push_back(m_util.mk_numeral(rational(-1), m_util.is_int(e))); args.push_back(e); } } else { args.push_back(m_util.mk_numeral(rational(-1), m_util.is_int(e))); args.push_back(e); } if (args.size() == 1) { return expr_ref(args.back(), m()); } else { return expr_ref(m_util.mk_mul(args.size(), args.c_ptr()), m()); } } bool arith_rewriter::is_neg_poly(expr* t, expr_ref& neg) const { rational r; if (m_util.is_mul(t) && is_numeral(to_app(t)->get_arg(0), r) && r.is_neg()) { neg = neg_monomial(t); return true; } if (!m_util.is_add(t)) { return false; } expr * t2 = to_app(t)->get_arg(0); if (m_util.is_mul(t2) && is_numeral(to_app(t2)->get_arg(0), r) && r.is_neg()) { expr_ref_vector args1(m()); for (expr* e1 : *to_app(t)) { args1.push_back(neg_monomial(e1)); } neg = m_util.mk_add(args1.size(), args1.c_ptr()); return true; } return false; } bool arith_rewriter::is_anum_simp_target(unsigned num_args, expr * const * args) { if (!m_anum_simp) return false; unsigned num_irrat = 0; unsigned num_rat = 0; for (unsigned i = 0; i < num_args; i++) { if (m_util.is_numeral(args[i])) { num_rat++; if (num_irrat > 0) return true; } if (m_util.is_irrational_algebraic_numeral(args[i]) && m_util.am().degree(m_util.to_irrational_algebraic_numeral(args[i])) <= m_max_degree) { num_irrat++; if (num_irrat > 1 || num_rat > 0) return true; } } return false; } br_status arith_rewriter::mk_add_core(unsigned num_args, expr * const * args, expr_ref & result) { if (is_anum_simp_target(num_args, args)) { expr_ref_buffer new_args(m()); anum_manager & am = m_util.am(); scoped_anum r(am); scoped_anum arg(am); rational rarg; am.set(r, 0); for (unsigned i = 0; i < num_args; i ++) { unsigned d = am.degree(r); if (d > 1 && d > m_max_degree) { new_args.push_back(m_util.mk_numeral(r, false)); am.set(r, 0); } if (m_util.is_numeral(args[i], rarg)) { am.set(arg, rarg.to_mpq()); am.add(r, arg, r); continue; } if (m_util.is_irrational_algebraic_numeral(args[i])) { anum const & irarg = m_util.to_irrational_algebraic_numeral(args[i]); if (am.degree(irarg) <= m_max_degree) { am.add(r, irarg, r); continue; } } new_args.push_back(args[i]); } if (new_args.empty()) { result = m_util.mk_numeral(r, false); return BR_DONE; } new_args.push_back(m_util.mk_numeral(r, false)); br_status st = poly_rewriter::mk_add_core(new_args.size(), new_args.c_ptr(), result); if (st == BR_FAILED) { result = m().mk_app(get_fid(), OP_ADD, new_args.size(), new_args.c_ptr()); return BR_DONE; } return st; } else { return poly_rewriter::mk_add_core(num_args, args, result); } } br_status arith_rewriter::mk_mul_core(unsigned num_args, expr * const * args, expr_ref & result) { if (is_anum_simp_target(num_args, args)) { expr_ref_buffer new_args(m()); anum_manager & am = m_util.am(); scoped_anum r(am); scoped_anum arg(am); rational rarg; am.set(r, 1); for (unsigned i = 0; i < num_args; i ++) { unsigned d = am.degree(r); if (d > 1 && d > m_max_degree) { new_args.push_back(m_util.mk_numeral(r, false)); am.set(r, 1); } if (m_util.is_numeral(args[i], rarg)) { am.set(arg, rarg.to_mpq()); am.mul(r, arg, r); continue; } if (m_util.is_irrational_algebraic_numeral(args[i])) { anum const & irarg = m_util.to_irrational_algebraic_numeral(args[i]); if (am.degree(irarg) <= m_max_degree) { am.mul(r, irarg, r); continue; } } new_args.push_back(args[i]); } if (new_args.empty()) { result = m_util.mk_numeral(r, false); return BR_DONE; } new_args.push_back(m_util.mk_numeral(r, false)); br_status st = poly_rewriter::mk_mul_core(new_args.size(), new_args.c_ptr(), result); if (st == BR_FAILED) { result = m().mk_app(get_fid(), OP_MUL, new_args.size(), new_args.c_ptr()); return BR_DONE; } return st; } else { return poly_rewriter::mk_mul_core(num_args, args, result); } } br_status arith_rewriter::mk_div_irrat_rat(expr * arg1, expr * arg2, expr_ref & result) { SASSERT(m_util.is_real(arg1)); SASSERT(m_util.is_irrational_algebraic_numeral(arg1)); SASSERT(m_util.is_numeral(arg2)); anum_manager & am = m_util.am(); anum const & val1 = m_util.to_irrational_algebraic_numeral(arg1); rational rval2; VERIFY(m_util.is_numeral(arg2, rval2)); if (rval2.is_zero()) return BR_FAILED; scoped_anum val2(am); am.set(val2, rval2.to_mpq()); scoped_anum r(am); am.div(val1, val2, r); result = m_util.mk_numeral(r, false); return BR_DONE; } br_status arith_rewriter::mk_div_rat_irrat(expr * arg1, expr * arg2, expr_ref & result) { SASSERT(m_util.is_real(arg1)); SASSERT(m_util.is_numeral(arg1)); SASSERT(m_util.is_irrational_algebraic_numeral(arg2)); anum_manager & am = m_util.am(); rational rval1; VERIFY(m_util.is_numeral(arg1, rval1)); scoped_anum val1(am); am.set(val1, rval1.to_mpq()); anum const & val2 = m_util.to_irrational_algebraic_numeral(arg2); scoped_anum r(am); am.div(val1, val2, r); result = m_util.mk_numeral(r, false); return BR_DONE; } br_status arith_rewriter::mk_div_irrat_irrat(expr * arg1, expr * arg2, expr_ref & result) { SASSERT(m_util.is_real(arg1)); SASSERT(m_util.is_irrational_algebraic_numeral(arg1)); SASSERT(m_util.is_irrational_algebraic_numeral(arg2)); anum_manager & am = m_util.am(); anum const & val1 = m_util.to_irrational_algebraic_numeral(arg1); if (am.degree(val1) > m_max_degree) return BR_FAILED; anum const & val2 = m_util.to_irrational_algebraic_numeral(arg2); if (am.degree(val2) > m_max_degree) return BR_FAILED; scoped_anum r(am); am.div(val1, val2, r); result = m_util.mk_numeral(r, false); return BR_DONE; } br_status arith_rewriter::mk_div_core(expr * arg1, expr * arg2, expr_ref & result) { if (m_anum_simp) { if (m_util.is_irrational_algebraic_numeral(arg1) && m_util.is_numeral(arg2)) return mk_div_irrat_rat(arg1, arg2, result); if (m_util.is_irrational_algebraic_numeral(arg1) && m_util.is_irrational_algebraic_numeral(arg2)) return mk_div_irrat_irrat(arg1, arg2, result); if (m_util.is_irrational_algebraic_numeral(arg2) && m_util.is_numeral(arg1)) return mk_div_rat_irrat(arg1, arg2, result); } set_curr_sort(m().get_sort(arg1)); numeral v1, v2; bool is_int; if (m_util.is_numeral(arg2, v2, is_int)) { SASSERT(!is_int); if (v2.is_zero()) { return BR_FAILED; } else if (m_util.is_numeral(arg1, v1, is_int)) { result = m_util.mk_numeral(v1/v2, false); return BR_DONE; } else { numeral k(1); k /= v2; result = m().mk_app(get_fid(), OP_MUL, m_util.mk_numeral(k, false), arg1); return BR_REWRITE1; } } #if 0 if (!m_util.is_int(arg1)) { // (/ (* v1 b) (* v2 d)) --> (* v1/v2 (/ b d)) expr * a, * b, * c, * d; if (m_util.is_mul(arg1, a, b) && m_util.is_numeral(a, v1)) { // do nothing arg1 is of the form v1 * b } else { v1 = rational(1); b = arg1; } if (m_util.is_mul(arg2, c, d) && m_util.is_numeral(c, v2)) { // do nothing arg2 is of the form v2 * d } else { v2 = rational(1); d = arg2; } TRACE("div_bug", tout << "v1: " << v1 << ", v2: " << v2 << "\n";); if (!v1.is_one() || !v2.is_one()) { v1 /= v2; result = m_util.mk_mul(m_util.mk_numeral(v1, false), m_util.mk_div(b, d)); expr_ref z(m_util.mk_real(0), m()); result = m().mk_ite(m().mk_eq(d, z), m_util.mk_div(arg1, z), result); return BR_REWRITE2; } } #endif return BR_FAILED; } br_status arith_rewriter::mk_idivides(unsigned k, expr * arg, expr_ref & result) { result = m().mk_eq(m_util.mk_mod(arg, m_util.mk_int(k)), m_util.mk_int(0)); return BR_REWRITE2; } br_status arith_rewriter::mk_idiv_core(expr * arg1, expr * arg2, expr_ref & result) { set_curr_sort(m().get_sort(arg1)); numeral v1, v2; bool is_int; if (m_util.is_numeral(arg1, v1, is_int) && m_util.is_numeral(arg2, v2, is_int) && !v2.is_zero()) { result = m_util.mk_numeral(div(v1, v2), is_int); return BR_DONE; } if (m_util.is_numeral(arg2, v2, is_int) && v2.is_one()) { result = arg1; return BR_DONE; } if (m_util.is_numeral(arg2, v2, is_int) && v2.is_zero()) { return BR_FAILED; } if (arg1 == arg2) { expr_ref zero(m_util.mk_int(0), m()); result = m().mk_ite(m().mk_eq(arg1, zero), m_util.mk_idiv(zero, zero), m_util.mk_int(1)); return BR_REWRITE3; } if (m_util.is_numeral(arg2, v2, is_int) && v2.is_pos() && m_util.is_add(arg1)) { expr_ref_buffer args(m()); bool change = false; rational add(0); for (expr* arg : *to_app(arg1)) { rational arg_v; if (m_util.is_numeral(arg, arg_v) && arg_v.is_pos() && mod(arg_v, v2) != arg_v) { change = true; args.push_back(m_util.mk_numeral(mod(arg_v, v2), true)); add += div(arg_v, v2); } else { args.push_back(arg); } } if (change) { result = m_util.mk_idiv(m().mk_app(to_app(arg1)->get_decl(), args.size(), args.c_ptr()), arg2); result = m_util.mk_add(m_util.mk_numeral(add, true), result); TRACE("div_bug", tout << "mk_div result: " << result << "\n";); return BR_REWRITE3; } } if (divides(arg1, arg2, result)) { return BR_REWRITE_FULL; } return BR_FAILED; } // // implement div ab ac = floor( ab / ac) = floor (b / c) = div b c // bool arith_rewriter::divides(expr* num, expr* den, expr_ref& result) { expr_fast_mark1 mark; rational num_r(1), den_r(1); expr* num_e = nullptr, *den_e = nullptr; ptr_buffer args1, args2; flat_mul(num, args1); flat_mul(den, args2); for (expr * arg : args1) { mark.mark(arg); if (m_util.is_numeral(arg, num_r)) num_e = arg; } for (expr* arg : args2) { // dont remove divisor on (div (* -1 x) (* -1 y)) because rewriting would diverge. if (mark.is_marked(arg) && (!m_util.is_numeral(arg, num_r) || !num_r.is_minus_one())) { result = remove_divisor(arg, num, den); return true; } if (m_util.is_numeral(arg, den_r)) den_e = arg; } rational g = gcd(num_r, den_r); if (!g.is_one()) { SASSERT(g.is_pos()); // replace num_e, den_e by their gcd reduction. for (unsigned i = 0; i < args1.size(); ++i) { if (args1[i] == num_e) { args1[i] = m_util.mk_numeral(num_r / g, true); break; } } for (unsigned i = 0; i < args2.size(); ++i) { if (args2[i] == den_e) { args2[i] = m_util.mk_numeral(den_r / g, true); break; } } num = m_util.mk_mul(args1.size(), args1.c_ptr()); den = m_util.mk_mul(args2.size(), args2.c_ptr()); result = m_util.mk_idiv(num, den); return true; } return false; } expr_ref arith_rewriter::remove_divisor(expr* arg, expr* num, expr* den) { ptr_buffer args1, args2; flat_mul(num, args1); flat_mul(den, args2); remove_divisor(arg, args1); remove_divisor(arg, args2); expr_ref zero(m_util.mk_int(0), m()); num = args1.empty() ? m_util.mk_int(1) : m_util.mk_mul(args1.size(), args1.c_ptr()); den = args2.empty() ? m_util.mk_int(1) : m_util.mk_mul(args2.size(), args2.c_ptr()); expr_ref d(m_util.mk_idiv(num, den), m()); expr_ref nd(m_util.mk_idiv(m_util.mk_uminus(num), m_util.mk_uminus(den)), m()); return expr_ref(m().mk_ite(m().mk_eq(zero, arg), m_util.mk_idiv(zero, zero), m().mk_ite(m_util.mk_ge(arg, zero), d, nd)), m()); } void arith_rewriter::flat_mul(expr* e, ptr_buffer& args) { args.push_back(e); for (unsigned i = 0; i < args.size(); ++i) { e = args[i]; if (m_util.is_mul(e)) { args.append(to_app(e)->get_num_args(), to_app(e)->get_args()); args[i] = args.back(); args.shrink(args.size()-1); --i; } } } void arith_rewriter::remove_divisor(expr* d, ptr_buffer& args) { for (unsigned i = 0; i < args.size(); ++i) { if (args[i] == d) { args[i] = args.back(); args.shrink(args.size()-1); return; } } UNREACHABLE(); } br_status arith_rewriter::mk_mod_core(expr * arg1, expr * arg2, expr_ref & result) { set_curr_sort(m().get_sort(arg1)); numeral v1, v2; bool is_int; if (m_util.is_numeral(arg1, v1, is_int) && m_util.is_numeral(arg2, v2, is_int) && !v2.is_zero()) { result = m_util.mk_numeral(mod(v1, v2), is_int); return BR_DONE; } if (m_util.is_numeral(arg2, v2, is_int) && is_int && v2.is_one()) { result = m_util.mk_numeral(numeral(0), true); return BR_DONE; } if (arg1 == arg2 && !m_util.is_numeral(arg2)) { expr_ref zero(m_util.mk_int(0), m()); result = m().mk_ite(m().mk_eq(arg2, zero), m_util.mk_mod(zero, zero), zero); return BR_DONE; } // mod is idempotent on non-zero modulus. expr* t1, *t2; if (m_util.is_mod(arg1, t1, t2) && t2 == arg2 && m_util.is_numeral(arg2, v2, is_int) && is_int && !v2.is_zero()) { result = arg1; return BR_DONE; } // propagate mod inside only if there is something to reduce. if (m_util.is_numeral(arg2, v2, is_int) && is_int && v2.is_pos() && (is_add(arg1) || is_mul(arg1))) { TRACE("mod_bug", tout << "mk_mod:\n" << mk_ismt2_pp(arg1, m()) << "\n" << mk_ismt2_pp(arg2, m()) << "\n";); expr_ref_buffer args(m()); bool change = false; for (expr* arg : *to_app(arg1)) { rational arg_v; if (m_util.is_numeral(arg, arg_v) && mod(arg_v, v2) != arg_v) { change = true; args.push_back(m_util.mk_numeral(mod(arg_v, v2), true)); } else if (m_util.is_mod(arg, t1, t2) && t2 == arg2) { change = true; args.push_back(t1); } else { args.push_back(arg); } } if (!change) { return BR_FAILED; // did not find any target for applying simplification } result = m_util.mk_mod(m().mk_app(to_app(arg1)->get_decl(), args.size(), args.c_ptr()), arg2); TRACE("mod_bug", tout << "mk_mod result: " << mk_ismt2_pp(result, m()) << "\n";); return BR_REWRITE3; } return BR_FAILED; } br_status arith_rewriter::mk_rem_core(expr * arg1, expr * arg2, expr_ref & result) { set_curr_sort(m().get_sort(arg1)); numeral v1, v2; bool is_int; if (m_util.is_numeral(arg1, v1, is_int) && m_util.is_numeral(arg2, v2, is_int) && !v2.is_zero()) { numeral m = mod(v1, v2); // // rem(v1,v2) = if v2 >= 0 then mod(v1,v2) else -mod(v1,v2) // if (v2.is_neg()) { m.neg(); } result = m_util.mk_numeral(m, is_int); return BR_DONE; } else if (m_util.is_numeral(arg2, v2, is_int) && is_int && v2.is_one()) { result = m_util.mk_numeral(numeral(0), true); return BR_DONE; } else if (m_util.is_numeral(arg2, v2, is_int) && is_int && !v2.is_zero()) { if (is_add(arg1) || is_mul(arg1)) { return BR_FAILED; } else { if (v2.is_neg()) { result = m_util.mk_uminus(m_util.mk_mod(arg1, arg2)); return BR_REWRITE2; } else { result = m_util.mk_mod(arg1, arg2); return BR_REWRITE1; } } } else if (m_elim_rem) { expr * mod = m_util.mk_mod(arg1, arg2); result = m().mk_ite(m_util.mk_ge(arg2, m_util.mk_numeral(rational(0), true)), mod, m_util.mk_uminus(mod)); TRACE("elim_rem", tout << "result: " << mk_ismt2_pp(result, m()) << "\n";); return BR_REWRITE3; } return BR_FAILED; } br_status arith_rewriter::mk_power_core(expr * arg1, expr * arg2, expr_ref & result) { numeral x, y; bool is_num_x = m_util.is_numeral(arg1, x); bool is_num_y = m_util.is_numeral(arg2, y); bool is_int_sort = m_util.is_int(arg1); TRACE("arith", tout << mk_pp(arg1, m()) << " " << mk_pp(arg2, m()) << "\n";); if ((is_num_x && x.is_one()) || (is_num_y && y.is_one())) { result = arg1; return BR_DONE; } if (is_num_x && is_num_y) { if (x.is_zero() && y.is_zero()) return BR_FAILED; if (y.is_zero()) { result = m_util.mk_numeral(rational(1), m().get_sort(arg1)); return BR_DONE; } if (x.is_zero()) { result = arg1; return BR_DONE; } if (y.is_unsigned() && y.get_unsigned() <= m_max_degree) { x = power(x, y.get_unsigned()); result = m_util.mk_numeral(x, m().get_sort(arg1)); return BR_DONE; } if (!is_int_sort && (-y).is_unsigned() && (-y).get_unsigned() <= m_max_degree) { x = power(rational(1)/x, (-y).get_unsigned()); result = m_util.mk_numeral(x, m().get_sort(arg1)); return BR_DONE; } } expr* arg10, *arg11; if (m_util.is_power(arg1, arg10, arg11) && is_num_y && y.is_int() && !y.is_zero()) { // (^ (^ t y2) y) --> (^ t (* y2 y)) If y2 > 0 && y != 0 && y and y2 are integers rational y2; if (m_util.is_numeral(arg11, y2) && y2.is_int() && y2.is_pos()) { result = m_util.mk_power(arg10, m_util.mk_numeral(y*y2, is_int_sort)); return BR_REWRITE1; } } if (!is_int_sort && is_num_y && y.is_neg()) { // (^ t -k) --> (^ (/ 1 t) k) result = m_util.mk_power(m_util.mk_div(m_util.mk_numeral(rational(1), false), arg1), m_util.mk_numeral(-y, false)); return BR_REWRITE2; } if (!is_int_sort && is_num_y && !y.is_int() && !numerator(y).is_one()) { // (^ t (/ p q)) --> (^ (^ t (/ 1 q)) p) result = m_util.mk_power(m_util.mk_power(arg1, m_util.mk_numeral(rational(1)/denominator(y), false)), m_util.mk_numeral(numerator(y), false)); return BR_REWRITE2; } if ((m_expand_power || (m_som && is_app(arg1) && to_app(arg1)->get_family_id() == get_fid())) && is_num_y && y.is_unsigned() && 1 < y.get_unsigned() && y.get_unsigned() <= m_max_degree) { ptr_buffer args; unsigned k = y.get_unsigned(); for (unsigned i = 0; i < k; i++) { args.push_back(arg1); } result = m_util.mk_mul(args.size(), args.c_ptr()); return BR_REWRITE1; } if (!is_num_y) return BR_FAILED; bool is_irrat_x = m_util.is_irrational_algebraic_numeral(arg1); if (!is_num_x && !is_irrat_x) return BR_FAILED; rational num_y = numerator(y); rational den_y = denominator(y); bool is_neg_y = false; if (num_y.is_neg()) { num_y.neg(); is_neg_y = true; } SASSERT(num_y.is_pos()); SASSERT(den_y.is_pos()); if (is_neg_y && is_int_sort) return BR_FAILED; if (!num_y.is_unsigned() || !den_y.is_unsigned()) return BR_FAILED; unsigned u_num_y = num_y.get_unsigned(); unsigned u_den_y = den_y.get_unsigned(); if (u_num_y > m_max_degree || u_den_y > m_max_degree) return BR_FAILED; if (is_num_x) { rational xk, r; xk = power(x, u_num_y); if (xk.is_neg() && u_den_y % 2 == 0) { return BR_FAILED; } if (xk.root(u_den_y, r)) { if (is_neg_y) r = rational(1)/r; result = m_util.mk_numeral(r, m().get_sort(arg1)); return BR_DONE; } if (m_anum_simp) { anum_manager & am = m_util.am(); scoped_anum r(am); am.set(r, xk.to_mpq()); am.root(r, u_den_y, r); if (is_neg_y) am.inv(r); result = m_util.mk_numeral(r, false); return BR_DONE; } return BR_FAILED; } SASSERT(is_irrat_x); if (!m_anum_simp) return BR_FAILED; anum const & val = m_util.to_irrational_algebraic_numeral(arg1); anum_manager & am = m_util.am(); if (am.degree(val) > m_max_degree) return BR_FAILED; scoped_anum r(am); am.power(val, u_num_y, r); am.root(r, u_den_y, r); if (is_neg_y) am.inv(r); result = m_util.mk_numeral(r, false); return BR_DONE; } br_status arith_rewriter::mk_to_int_core(expr * arg, expr_ref & result) { numeral a; expr* x = nullptr; if (m_util.is_numeral(arg, a)) { result = m_util.mk_numeral(floor(a), true); return BR_DONE; } if (m_util.is_to_real(arg, x)) { result = x; return BR_DONE; } if (m_util.is_add(arg) || m_util.is_mul(arg) || m_util.is_power(arg)) { // Try to apply simplifications such as: // (to_int (+ 1.0 (to_real x)) y) --> (+ 1 x (to_int y)) expr_ref_buffer int_args(m()), real_args(m()); for (expr* c : *to_app(arg)) { if (m_util.is_numeral(c, a) && a.is_int()) { int_args.push_back(m_util.mk_numeral(a, true)); } else if (m_util.is_to_real(c, x)) { int_args.push_back(x); } else { real_args.push_back(c); } } if (real_args.empty()) { result = m().mk_app(get_fid(), to_app(arg)->get_decl()->get_decl_kind(), int_args.size(), int_args.c_ptr()); return BR_REWRITE1; } if (!int_args.empty() && m_util.is_add(arg)) { decl_kind k = to_app(arg)->get_decl()->get_decl_kind(); expr_ref t1(m().mk_app(get_fid(), k, int_args.size(), int_args.c_ptr()), m()); expr_ref t2(m().mk_app(get_fid(), k, real_args.size(), real_args.c_ptr()), m()); int_args.reset(); int_args.push_back(t1); int_args.push_back(m_util.mk_to_int(t2)); result = m().mk_app(get_fid(), k, int_args.size(), int_args.c_ptr()); return BR_REWRITE3; } } return BR_FAILED; } br_status arith_rewriter::mk_to_real_core(expr * arg, expr_ref & result) { numeral a; if (m_util.is_numeral(arg, a)) { result = m_util.mk_numeral(a, false); return BR_DONE; } // push to_real over OP_ADD, OP_MUL if (m_push_to_real) { if (m_util.is_add(arg) || m_util.is_mul(arg)) { ptr_buffer new_args; unsigned num = to_app(arg)->get_num_args(); for (unsigned i = 0; i < num; i++) { new_args.push_back(m_util.mk_to_real(to_app(arg)->get_arg(i))); } if (m_util.is_add(arg)) result = m().mk_app(get_fid(), OP_ADD, new_args.size(), new_args.c_ptr()); else result = m().mk_app(get_fid(), OP_MUL, new_args.size(), new_args.c_ptr()); return BR_REWRITE2; } } return BR_FAILED; } br_status arith_rewriter::mk_is_int(expr * arg, expr_ref & result) { numeral a; if (m_util.is_numeral(arg, a)) { result = a.is_int() ? m().mk_true() : m().mk_false(); return BR_DONE; } else if (m_util.is_to_real(arg)) { result = m().mk_true(); return BR_DONE; } else { result = m().mk_eq(m().mk_app(get_fid(), OP_TO_REAL, m().mk_app(get_fid(), OP_TO_INT, arg)), arg); return BR_REWRITE3; } } br_status arith_rewriter::mk_abs_core(expr * arg, expr_ref & result) { result = m().mk_ite(m_util.mk_ge(arg, m_util.mk_numeral(rational(0), m_util.is_int(arg))), arg, m_util.mk_uminus(arg)); return BR_REWRITE2; } // Return true if t is of the form c*Pi where c is a numeral. // Store c into k bool arith_rewriter::is_pi_multiple(expr * t, rational & k) { if (m_util.is_pi(t)) { k = rational(1); return true; } expr * a, * b; return m_util.is_mul(t, a, b) && m_util.is_pi(b) && m_util.is_numeral(a, k); } // Return true if t is of the form (+ s c*Pi) where c is a numeral. // Store c into k, and c*Pi into m. bool arith_rewriter::is_pi_offset(expr * t, rational & k, expr * & m) { if (m_util.is_add(t)) { unsigned num = to_app(t)->get_num_args(); for (unsigned i = 0; i < num; i++) { expr * arg = to_app(t)->get_arg(i); if (is_pi_multiple(arg, k)) { m = arg; return true; } } } return false; } // Return true if t is of the form 2*pi*to_real(s). bool arith_rewriter::is_2_pi_integer(expr * t) { expr * a, * m, * b, * c; rational k; return m_util.is_mul(t, a, m) && m_util.is_numeral(a, k) && k.is_int() && mod(k, rational(2)).is_zero() && m_util.is_mul(m, b, c) && ((m_util.is_pi(b) && m_util.is_to_real(c)) || (m_util.is_to_real(b) && m_util.is_pi(c))); } // Return true if t is of the form s + 2*pi*to_real(s). // Store 2*pi*to_real(s) into m. bool arith_rewriter::is_2_pi_integer_offset(expr * t, expr * & m) { if (m_util.is_add(t)) { unsigned num = to_app(t)->get_num_args(); for (unsigned i = 0; i < num; i++) { expr * arg = to_app(t)->get_arg(i); if (is_2_pi_integer(arg)) { m = arg; return true; } } } return false; } // Return true if t is of the form pi*to_real(s). bool arith_rewriter::is_pi_integer(expr * t) { expr * a, * b; if (m_util.is_mul(t, a, b)) { rational k; if (m_util.is_numeral(a, k)) { if (!k.is_int()) return false; expr * c, * d; if (!m_util.is_mul(b, c, d)) return false; a = c; b = d; } TRACE("tan", tout << "is_pi_integer " << mk_ismt2_pp(t, m()) << "\n"; tout << "a: " << mk_ismt2_pp(a, m()) << "\n"; tout << "b: " << mk_ismt2_pp(b, m()) << "\n";); return (m_util.is_pi(a) && m_util.is_to_real(b)) || (m_util.is_to_real(a) && m_util.is_pi(b)); } return false; } // Return true if t is of the form s + pi*to_real(s). // Store 2*pi*to_real(s) into m. bool arith_rewriter::is_pi_integer_offset(expr * t, expr * & m) { if (m_util.is_add(t)) { unsigned num = to_app(t)->get_num_args(); for (unsigned i = 0; i < num; i++) { expr * arg = to_app(t)->get_arg(i); if (is_pi_integer(arg)) { m = arg; return true; } } } return false; } app * arith_rewriter::mk_sqrt(rational const & k) { return m_util.mk_power(m_util.mk_numeral(k, false), m_util.mk_numeral(rational(1, 2), false)); } // Return a constant representing sin(k * pi). // Return 0 if failed. expr * arith_rewriter::mk_sin_value(rational const & k) { rational k_prime = mod(floor(k), rational(2)) + k - floor(k); TRACE("sine", tout << "k: " << k << ", k_prime: " << k_prime << "\n";); SASSERT(k_prime >= rational(0) && k_prime < rational(2)); bool neg = false; if (k_prime >= rational(1)) { neg = true; k_prime = k_prime - rational(1); } SASSERT(k_prime >= rational(0) && k_prime < rational(1)); if (k_prime.is_zero() || k_prime.is_one()) { // sin(0) == sin(pi) == 0 return m_util.mk_numeral(rational(0), false); } if (k_prime == rational(1, 2)) { // sin(pi/2) == 1, sin(3/2 pi) == -1 return m_util.mk_numeral(rational(neg ? -1 : 1), false); } if (k_prime == rational(1, 6) || k_prime == rational(5, 6)) { // sin(pi/6) == sin(5/6 pi) == 1/2 // sin(7 pi/6) == sin(11/6 pi) == -1/2 return m_util.mk_numeral(rational(neg ? -1 : 1, 2), false); } if (k_prime == rational(1, 4) || k_prime == rational(3, 4)) { // sin(pi/4) == sin(3/4 pi) == Sqrt(1/2) // sin(5/4 pi) == sin(7/4 pi) == - Sqrt(1/2) expr * result = mk_sqrt(rational(1, 2)); return neg ? m_util.mk_uminus(result) : result; } if (k_prime == rational(1, 3) || k_prime == rational(2, 3)) { // sin(pi/3) == sin(2/3 pi) == Sqrt(3)/2 // sin(4/3 pi) == sin(5/3 pi) == - Sqrt(3)/2 expr * result = m_util.mk_div(mk_sqrt(rational(3)), m_util.mk_numeral(rational(2), false)); return neg ? m_util.mk_uminus(result) : result; } if (k_prime == rational(1, 12) || k_prime == rational(11, 12)) { // sin(1/12 pi) == sin(11/12 pi) == [sqrt(6) - sqrt(2)]/4 // sin(13/12 pi) == sin(23/12 pi) == -[sqrt(6) - sqrt(2)]/4 expr * result = m_util.mk_div(m_util.mk_sub(mk_sqrt(rational(6)), mk_sqrt(rational(2))), m_util.mk_numeral(rational(4), false)); return neg ? m_util.mk_uminus(result) : result; } if (k_prime == rational(5, 12) || k_prime == rational(7, 12)) { // sin(5/12 pi) == sin(7/12 pi) == [sqrt(6) + sqrt(2)]/4 // sin(17/12 pi) == sin(19/12 pi) == -[sqrt(6) + sqrt(2)]/4 expr * result = m_util.mk_div(m_util.mk_add(mk_sqrt(rational(6)), mk_sqrt(rational(2))), m_util.mk_numeral(rational(4), false)); return neg ? m_util.mk_uminus(result) : result; } return nullptr; } br_status arith_rewriter::mk_sin_core(expr * arg, expr_ref & result) { expr * m, *x; if (m_util.is_asin(arg, x)) { // sin(asin(x)) == x result = x; return BR_DONE; } if (m_util.is_acos(arg, x)) { // sin(acos(x)) == sqrt(1 - x^2) result = m_util.mk_power(m_util.mk_sub(m_util.mk_real(1), m_util.mk_mul(x,x)), m_util.mk_numeral(rational(1,2), false)); return BR_REWRITE_FULL; } rational k; if (is_numeral(arg, k) && k.is_zero()) { // sin(0) == 0 result = arg; return BR_DONE; } if (is_pi_multiple(arg, k)) { result = mk_sin_value(k); if (result.get() != nullptr) return BR_REWRITE_FULL; } if (is_pi_offset(arg, k, m)) { rational k_prime = mod(floor(k), rational(2)) + k - floor(k); SASSERT(k_prime >= rational(0) && k_prime < rational(2)); if (k_prime.is_zero()) { // sin(x + 2*n*pi) == sin(x) result = m_util.mk_sin(m_util.mk_sub(arg, m)); return BR_REWRITE2; } if (k_prime == rational(1, 2)) { // sin(x + pi/2 + 2*n*pi) == cos(x) result = m_util.mk_cos(m_util.mk_sub(arg, m)); return BR_REWRITE2; } if (k_prime.is_one()) { // sin(x + pi + 2*n*pi) == -sin(x) result = m_util.mk_uminus(m_util.mk_sin(m_util.mk_sub(arg, m))); return BR_REWRITE3; } if (k_prime == rational(3, 2)) { // sin(x + 3/2*pi + 2*n*pi) == -cos(x) result = m_util.mk_uminus(m_util.mk_cos(m_util.mk_sub(arg, m))); return BR_REWRITE3; } } if (is_2_pi_integer_offset(arg, m)) { // sin(x + 2*pi*to_real(a)) == sin(x) result = m_util.mk_sin(m_util.mk_sub(arg, m)); return BR_REWRITE2; } return BR_FAILED; } br_status arith_rewriter::mk_cos_core(expr * arg, expr_ref & result) { expr* x; if (m_util.is_acos(arg, x)) { // cos(acos(x)) == x result = x; return BR_DONE; } if (m_util.is_asin(arg, x)) { // cos(asin(x)) == ... } rational k; if (is_numeral(arg, k) && k.is_zero()) { // cos(0) == 1 result = m_util.mk_numeral(rational(1), false); return BR_DONE; } if (is_pi_multiple(arg, k)) { k = k + rational(1, 2); result = mk_sin_value(k); if (result.get() != nullptr) return BR_REWRITE_FULL; } expr * m; if (is_pi_offset(arg, k, m)) { rational k_prime = mod(floor(k), rational(2)) + k - floor(k); SASSERT(k_prime >= rational(0) && k_prime < rational(2)); if (k_prime.is_zero()) { // cos(x + 2*n*pi) == cos(x) result = m_util.mk_cos(m_util.mk_sub(arg, m)); return BR_REWRITE2; } if (k_prime == rational(1, 2)) { // cos(x + pi/2 + 2*n*pi) == -sin(x) result = m_util.mk_uminus(m_util.mk_sin(m_util.mk_sub(arg, m))); return BR_REWRITE3; } if (k_prime.is_one()) { // cos(x + pi + 2*n*pi) == -cos(x) result = m_util.mk_uminus(m_util.mk_cos(m_util.mk_sub(arg, m))); return BR_REWRITE3; } if (k_prime == rational(3, 2)) { // cos(x + 3/2*pi + 2*n*pi) == sin(x) result = m_util.mk_sin(m_util.mk_sub(arg, m)); return BR_REWRITE2; } } if (is_2_pi_integer_offset(arg, m)) { // cos(x + 2*pi*to_real(a)) == cos(x) result = m_util.mk_cos(m_util.mk_sub(arg, m)); return BR_REWRITE2; } return BR_FAILED; } br_status arith_rewriter::mk_tan_core(expr * arg, expr_ref & result) { expr* x; if (m_util.is_atan(arg, x)) { // tan(atan(x)) == x result = x; return BR_DONE; } rational k; if (is_numeral(arg, k) && k.is_zero()) { // tan(0) == 0 result = arg; return BR_DONE; } if (is_pi_multiple(arg, k)) { expr_ref n(m()), d(m()); n = mk_sin_value(k); if (n.get() == nullptr) goto end; if (is_zero(n)) { result = n; return BR_DONE; } k = k + rational(1, 2); d = mk_sin_value(k); SASSERT(d.get() != 0); if (is_zero(d)) { goto end; } result = m_util.mk_div(n, d); return BR_REWRITE_FULL; } expr * m; if (is_pi_offset(arg, k, m)) { rational k_prime = k - floor(k); SASSERT(k_prime >= rational(0) && k_prime < rational(1)); if (k_prime.is_zero()) { // tan(x + n*pi) == tan(x) result = m_util.mk_tan(m_util.mk_sub(arg, m)); return BR_REWRITE2; } } if (is_pi_integer_offset(arg, m)) { // tan(x + pi*to_real(a)) == tan(x) result = m_util.mk_tan(m_util.mk_sub(arg, m)); return BR_REWRITE2; } end: if (m_expand_tan) { result = m_util.mk_div(m_util.mk_sin(arg), m_util.mk_cos(arg)); return BR_REWRITE2; } return BR_FAILED; } br_status arith_rewriter::mk_asin_core(expr * arg, expr_ref & result) { // Remark: we assume that ForAll x : asin(-x) == asin(x). // Mathematica uses this as an axiom. Although asin is an underspecified function for x < -1 or x > 1. // Actually, in Mathematica, asin(x) is a total function that returns a complex number fo x < -1 or x > 1. rational k; if (is_numeral(arg, k)) { if (k.is_zero()) { result = arg; return BR_DONE; } if (k < rational(-1)) { // asin(-2) == -asin(2) // asin(-3) == -asin(3) k.neg(); result = m_util.mk_uminus(m_util.mk_asin(m_util.mk_numeral(k, false))); return BR_REWRITE2; } if (k > rational(1)) return BR_FAILED; bool neg = false; if (k.is_neg()) { neg = true; k.neg(); } if (k.is_one()) { // asin(1) == pi/2 // asin(-1) == -pi/2 result = m_util.mk_mul(m_util.mk_numeral(rational(neg ? -1 : 1, 2), false), m_util.mk_pi()); return BR_REWRITE2; } if (k == rational(1, 2)) { // asin(1/2) == pi/6 // asin(-1/2) == -pi/6 result = m_util.mk_mul(m_util.mk_numeral(rational(neg ? -1 : 1, 6), false), m_util.mk_pi()); return BR_REWRITE2; } } expr * t; if (m_util.is_times_minus_one(arg, t)) { // See comment above // asin(-x) ==> -asin(x) result = m_util.mk_uminus(m_util.mk_asin(t)); return BR_REWRITE2; } return BR_FAILED; } br_status arith_rewriter::mk_acos_core(expr * arg, expr_ref & result) { rational k; if (is_numeral(arg, k)) { if (k.is_zero()) { // acos(0) = pi/2 result = m_util.mk_mul(m_util.mk_numeral(rational(1, 2), false), m_util.mk_pi()); return BR_REWRITE2; } if (k.is_one()) { // acos(1) = 0 result = m_util.mk_numeral(rational(0), false); return BR_DONE; } if (k.is_minus_one()) { // acos(-1) = pi result = m_util.mk_pi(); return BR_DONE; } if (k == rational(1, 2)) { // acos(1/2) = pi/3 result = m_util.mk_mul(m_util.mk_numeral(rational(1, 3), false), m_util.mk_pi()); return BR_REWRITE2; } if (k == rational(-1, 2)) { // acos(-1/2) = 2/3 pi result = m_util.mk_mul(m_util.mk_numeral(rational(2, 3), false), m_util.mk_pi()); return BR_REWRITE2; } } return BR_FAILED; } br_status arith_rewriter::mk_atan_core(expr * arg, expr_ref & result) { rational k; if (is_numeral(arg, k)) { if (k.is_zero()) { result = arg; return BR_DONE; } if (k.is_one()) { // atan(1) == pi/4 result = m_util.mk_mul(m_util.mk_numeral(rational(1, 4), false), m_util.mk_pi()); return BR_REWRITE2; } if (k.is_minus_one()) { // atan(-1) == -pi/4 result = m_util.mk_mul(m_util.mk_numeral(rational(-1, 4), false), m_util.mk_pi()); return BR_REWRITE2; } if (k < rational(-1)) { // atan(-2) == -tan(2) // atan(-3) == -tan(3) k.neg(); result = m_util.mk_uminus(m_util.mk_atan(m_util.mk_numeral(k, false))); return BR_REWRITE2; } return BR_FAILED; } expr * t; if (m_util.is_times_minus_one(arg, t)) { // atan(-x) ==> -atan(x) result = m_util.mk_uminus(m_util.mk_atan(t)); return BR_REWRITE2; } return BR_FAILED; } br_status arith_rewriter::mk_sinh_core(expr * arg, expr_ref & result) { expr* x; if (m_util.is_asinh(arg, x)) { // sinh(asinh(x)) == x result = x; return BR_DONE; } expr * t; if (m_util.is_times_minus_one(arg, t)) { // sinh(-t) == -sinh(t) result = m_util.mk_uminus(m_util.mk_sinh(t)); return BR_REWRITE2; } return BR_FAILED; } br_status arith_rewriter::mk_cosh_core(expr * arg, expr_ref & result) { expr* t; if (m_util.is_acosh(arg, t)) { // cosh(acosh(t)) == t result = t; return BR_DONE; } if (m_util.is_times_minus_one(arg, t)) { // cosh(-t) == cosh result = m_util.mk_cosh(t); return BR_DONE; } return BR_FAILED; } br_status arith_rewriter::mk_tanh_core(expr * arg, expr_ref & result) { expr * t; if (m_util.is_atanh(arg, t)) { // tanh(atanh(t)) == t result = t; return BR_DONE; } if (m_util.is_times_minus_one(arg, t)) { // tanh(-t) == -tanh(t) result = m_util.mk_uminus(m_util.mk_tanh(t)); return BR_REWRITE2; } return BR_FAILED; } template class poly_rewriter; z3-z3-4.8.7/src/ast/rewriter/arith_rewriter.h000066400000000000000000000173361356505360400210650ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: arith_rewriter.h Abstract: Basic rewriting rules for arithmetic Author: Leonardo (leonardo) 2011-04-10 Notes: --*/ #ifndef ARITH_REWRITER_H_ #define ARITH_REWRITER_H_ #include "ast/rewriter/poly_rewriter.h" #include "ast/arith_decl_plugin.h" class arith_rewriter_core { protected: typedef rational numeral; arith_util m_util; bool m_expand_power; bool m_mul2power; bool m_expand_tan; ast_manager & m() const { return m_util.get_manager(); } family_id get_fid() const { return m_util.get_family_id(); } bool is_numeral(expr * n) const { return m_util.is_numeral(n); } bool is_numeral(expr * n, numeral & r) const { return m_util.is_numeral(n, r); } bool is_minus_one(expr * n) const { return m_util.is_minus_one(n); } void normalize(numeral & c, sort * s) {} app * mk_numeral(numeral const & r, sort * s) { return m_util.mk_numeral(r, s); } decl_kind add_decl_kind() const { return OP_ADD; } decl_kind mul_decl_kind() const { return OP_MUL; } bool use_power() const { return m_mul2power && !m_expand_power; } decl_kind power_decl_kind() const { return OP_POWER; } public: arith_rewriter_core(ast_manager & m):m_util(m) {} bool is_zero(expr * n) const { return m_util.is_zero(n); } }; class arith_rewriter : public poly_rewriter { bool m_arith_lhs; bool m_arith_ineq_lhs; bool m_gcd_rounding; bool m_elim_to_real; bool m_push_to_real; bool m_anum_simp; bool m_elim_rem; bool m_eq2ineq; unsigned m_max_degree; void get_coeffs_gcd(expr * t, numeral & g, bool & first, unsigned & num_consts); enum const_treatment { CT_FLOOR, CT_CEIL, CT_FALSE }; bool div_polynomial(expr * t, numeral const & g, const_treatment ct, expr_ref & result); enum op_kind { LE, GE, EQ }; static op_kind inv(op_kind k) { return k == LE ? GE : (k == GE ? LE : EQ); } bool is_bound(expr * arg1, expr * arg2, op_kind kind, expr_ref & result); br_status mk_le_ge_eq_core(expr * arg1, expr * arg2, op_kind kind, expr_ref & result); bool elim_to_real_var(expr * var, expr_ref & new_var); bool elim_to_real_mon(expr * monomial, expr_ref & new_monomial); bool elim_to_real_pol(expr * p, expr_ref & new_p); bool elim_to_real(expr * arg1, expr * arg2, expr_ref & new_arg1, expr_ref & new_arg2); void updt_local_params(params_ref const & p); bool is_anum_simp_target(unsigned num_args, expr * const * args); br_status mk_div_irrat_rat(expr * arg1, expr * arg2, expr_ref & result); br_status mk_div_rat_irrat(expr * arg1, expr * arg2, expr_ref & result); br_status mk_div_irrat_irrat(expr * arg1, expr * arg2, expr_ref & result); bool is_reduce_power_target(expr * arg, bool is_eq); expr * reduce_power(expr * arg, bool is_eq); br_status reduce_power(expr * arg1, expr * arg2, op_kind kind, expr_ref & result); bool is_arith_term(expr * n) const; bool is_pi_multiple(expr * t, rational & k); bool is_pi_offset(expr * t, rational & k, expr * & m); bool is_2_pi_integer(expr * t); bool is_2_pi_integer_offset(expr * t, expr * & m); bool is_pi_integer(expr * t); bool is_pi_integer_offset(expr * t, expr * & m); bool is_neg_poly(expr* e, expr_ref& neg) const; expr_ref neg_monomial(expr * e) const; expr * mk_sin_value(rational const & k); app * mk_sqrt(rational const & k); bool divides(expr* d, expr* n, expr_ref& result); expr_ref remove_divisor(expr* arg, expr* num, expr* den); void flat_mul(expr* e, ptr_buffer& args); void remove_divisor(expr* d, ptr_buffer& args); public: arith_rewriter(ast_manager & m, params_ref const & p = params_ref()): poly_rewriter(m, p) { updt_local_params(p); } 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); void mk_app(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { if (mk_app_core(f, num_args, args, result) == BR_FAILED) result = m().mk_app(f, num_args, args); } br_status mk_eq_core(expr * arg1, expr * arg2, expr_ref & result); br_status mk_le_core(expr * arg1, expr * arg2, expr_ref & result); br_status mk_lt_core(expr * arg1, expr * arg2, expr_ref & result); br_status mk_ge_core(expr * arg1, expr * arg2, expr_ref & result); br_status mk_gt_core(expr * arg1, expr * arg2, expr_ref & result); br_status mk_add_core(unsigned num_args, expr * const * args, expr_ref & result); br_status mk_mul_core(unsigned num_args, expr * const * args, expr_ref & result); void mk_eq(expr * arg1, expr * arg2, expr_ref & result) { if (mk_eq_core(arg1, arg2, result) == BR_FAILED) result = m_util.mk_eq(arg1, arg2); } void mk_le(expr * arg1, expr * arg2, expr_ref & result) { if (mk_le_core(arg1, arg2, result) == BR_FAILED) result = m_util.mk_le(arg1, arg2); } void mk_lt(expr * arg1, expr * arg2, expr_ref & result) { mk_lt_core(arg1, arg2, result); } void mk_ge(expr * arg1, expr * arg2, expr_ref & result) { if (mk_ge_core(arg1, arg2, result) == BR_FAILED) result = m_util.mk_ge(arg1, arg2); } void mk_gt(expr * arg1, expr * arg2, expr_ref & result) { mk_gt_core(arg1, arg2, result); } br_status mk_abs_core(expr * arg, expr_ref & result); br_status mk_div_core(expr * arg1, expr * arg2, expr_ref & result); br_status mk_idiv_core(expr * arg1, expr * arg2, expr_ref & result); br_status mk_idivides(unsigned k, expr * arg, expr_ref & result); br_status mk_mod_core(expr * arg1, expr * arg2, expr_ref & result); br_status mk_rem_core(expr * arg1, expr * arg2, expr_ref & result); br_status mk_power_core(expr* arg1, expr* arg2, expr_ref & result); void mk_div(expr * arg1, expr * arg2, expr_ref & result) { if (mk_div_core(arg1, arg2, result) == BR_FAILED) result = m().mk_app(get_fid(), OP_DIV, arg1, arg2); } void mk_idiv(expr * arg1, expr * arg2, expr_ref & result) { if (mk_idiv_core(arg1, arg2, result) == BR_FAILED) result = m().mk_app(get_fid(), OP_IDIV, arg1, arg2); } void mk_mod(expr * arg1, expr * arg2, expr_ref & result) { if (mk_mod_core(arg1, arg2, result) == BR_FAILED) result = m().mk_app(get_fid(), OP_MOD, arg1, arg2); } void mk_rem(expr * arg1, expr * arg2, expr_ref & result) { if (mk_rem_core(arg1, arg2, result) == BR_FAILED) result = m().mk_app(get_fid(), OP_REM, arg1, arg2); } br_status mk_to_int_core(expr * arg, expr_ref & result); br_status mk_to_real_core(expr * arg, expr_ref & result); void mk_to_int(expr * arg, expr_ref & result) { if (mk_to_int_core(arg, result) == BR_FAILED) result = m().mk_app(get_fid(), OP_TO_INT, 1, &arg); } void mk_to_real(expr * arg, expr_ref & result) { if (mk_to_real_core(arg, result) == BR_FAILED) result = m().mk_app(get_fid(), OP_TO_REAL, 1, &arg); } br_status mk_is_int(expr * arg, expr_ref & result); br_status mk_sin_core(expr * arg, expr_ref & result); br_status mk_cos_core(expr * arg, expr_ref & result); br_status mk_tan_core(expr * arg, expr_ref & result); br_status mk_asin_core(expr * arg, expr_ref & result); br_status mk_acos_core(expr * arg, expr_ref & result); br_status mk_atan_core(expr * arg, expr_ref & result); br_status mk_sinh_core(expr * arg, expr_ref & result); br_status mk_cosh_core(expr * arg, expr_ref & result); br_status mk_tanh_core(expr * arg, expr_ref & result); }; #endif z3-z3-4.8.7/src/ast/rewriter/arith_rewriter_params.pyg000066400000000000000000000032041356505360400227650ustar00rootroot00000000000000def_module_params(module_name='rewriter', class_name='arith_rewriter_params', export=True, params=(("algebraic_number_evaluator", BOOL, True, "simplify/evaluate expressions containing (algebraic) irrational numbers."), ("mul_to_power", BOOL, False, "collpase (* t ... t) into (^ t k), it is ignored if expand_power is true."), ("expand_power", BOOL, False, "expand (^ t k) into (* t ... t) if 1 < k <= max_degree."), ("expand_tan", BOOL, False, "replace (tan x) with (/ (sin x) (cos x))."), ("max_degree", UINT, 64, "max degree of algebraic numbers (and power operators) processed by simplifier."), ("sort_sums", BOOL, False, "sort the arguments of + application."), ("gcd_rounding", BOOL, False, "use gcd rounding on integer arithmetic atoms."), ("arith_lhs", BOOL, False, "all monomials are moved to the left-hand-side, and the right-hand-side is just a constant."), ("arith_ineq_lhs", BOOL, False, "rewrite inequalities so that right-hand-side is a constant."), ("elim_to_real", BOOL, False, "eliminate to_real from arithmetic predicates that contain only integers."), ("push_to_real", BOOL, True, "distribute to_real over * and +."), ("eq2ineq", BOOL, False, "expand equalities into two inequalities"), ("elim_rem", BOOL, False, "replace (rem x y) with (ite (>= y 0) (mod x y) (- (mod x y)))."))) z3-z3-4.8.7/src/ast/rewriter/array_rewriter.cpp000066400000000000000000000620071356505360400214220ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: array_rewriter.cpp Abstract: Basic rewriting rules for Arrays. Author: Leonardo (leonardo) 2011-04-06 Notes: --*/ #include "ast/rewriter/array_rewriter.h" #include "ast/rewriter/array_rewriter_params.hpp" #include "ast/ast_lt.h" #include "ast/ast_util.h" #include "ast/ast_pp.h" #include "ast/rewriter/var_subst.h" void array_rewriter::updt_params(params_ref const & _p) { array_rewriter_params p(_p); m_sort_store = p.sort_store(); m_expand_select_store = p.expand_select_store(); m_expand_store_eq = p.expand_store_eq(); m_expand_nested_stores = p.expand_nested_stores(); m_expand_select_ite = false; } void array_rewriter::get_param_descrs(param_descrs & r) { array_rewriter_params::collect_param_descrs(r); } br_status array_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; switch (f->get_decl_kind()) { case OP_SELECT: st = mk_select_core(num_args, args, result); break; case OP_STORE: st = mk_store_core(num_args, args, result); break; case OP_ARRAY_MAP: st = mk_map_core(m_util.get_map_func_decl(f), num_args, args, result); break; case OP_SET_UNION: st = mk_set_union(num_args, args, result); break; case OP_SET_INTERSECT: st = mk_set_intersect(num_args, args, result); break; case OP_SET_SUBSET: SASSERT(num_args == 2); st = mk_set_subset(args[0], args[1], result); break; case OP_SET_COMPLEMENT: SASSERT(num_args == 1); st = mk_set_complement(args[0], result); break; case OP_SET_DIFFERENCE: SASSERT(num_args == 2); st = mk_set_difference(args[0], args[1], result); break; default: st = BR_FAILED; break; } CTRACE("array_rewriter", st != BR_FAILED, tout << mk_pp(f, m()) << "\n"; for (unsigned i = 0; i < num_args; ++i) { tout << mk_pp(args[i], m()) << "\n"; } tout << "\n --> " << result << "\n";); return st; } // l_true -- all equal // l_false -- at least one disequal // l_undef -- don't know template lbool array_rewriter::compare_args(unsigned num_args, expr * const * args1, expr * const * args2) { for (unsigned i = 0; i < num_args; i++) { if (args1[i] == args2[i]) continue; if (CHECK_DISEQ && m().are_distinct(args1[i], args2[i])) return l_false; return l_undef; } return l_true; } br_status array_rewriter::mk_store_core(unsigned num_args, expr * const * args, expr_ref & result) { SASSERT(num_args >= 3); if (m_util.is_store(args[0])) { lbool r = m_sort_store ? compare_args(num_args - 2, args + 1, to_app(args[0])->get_args() + 1) : compare_args(num_args - 2, args + 1, to_app(args[0])->get_args() + 1); switch (r) { case l_true: { // // store(store(a,i,v),i,w) --> store(a,i,w) // ptr_buffer new_args; new_args.push_back(to_app(args[0])->get_arg(0)); new_args.append(num_args-1, args+1); SASSERT(new_args.size() == num_args); result = m().mk_app(get_fid(), OP_STORE, num_args, new_args.c_ptr()); return BR_DONE; } case l_false: SASSERT(m_sort_store); // // store(store(a,i,v),j,w) -> store(store(a,j,w),i,v) // if i, j are different, lt(i,j) // if (lex_lt(num_args-2, args+1, to_app(args[0])->get_args() + 1)) { ptr_buffer new_args; new_args.push_back(to_app(args[0])->get_arg(0)); new_args.append(num_args-1, args+1); expr * nested_store = m().mk_app(get_fid(), OP_STORE, num_args, new_args.c_ptr()); new_args.reset(); new_args.push_back(nested_store); new_args.append(num_args - 1, to_app(args[0])->get_args() + 1); result = m().mk_app(get_fid(), OP_STORE, num_args, new_args.c_ptr()); return BR_REWRITE2; } break; case l_undef: break; } } // // store(const(v),i,v) --> const(v) // if (m_util.is_const(args[0]) && to_app(args[0])->get_arg(0) == args[num_args-1]) { result = args[0]; return BR_DONE; } expr * v = args[num_args-1]; // // store(a, i, select(a, i)) --> a // if (m_util.is_select(v) && compare_args(num_args-1, args, to_app(v)->get_args())) { result = args[0]; return BR_DONE; } return BR_FAILED; } br_status array_rewriter::mk_select_core(unsigned num_args, expr * const * args, expr_ref & result) { SASSERT(num_args >= 2); if (m_util.is_store(args[0])) { SASSERT(to_app(args[0])->get_num_args() == num_args+1); switch (compare_args(num_args - 1, args+1, to_app(args[0])->get_args()+1)) { case l_true: // select(store(a, I, v), I) --> v result = to_app(args[0])->get_arg(num_args); return BR_DONE; case l_false: { // select(store(a, I, v), J) --> select(a, J) if I != J ptr_buffer new_args; new_args.push_back(to_app(args[0])->get_arg(0)); new_args.append(num_args-1, args+1); result = m().mk_app(get_fid(), OP_SELECT, num_args, new_args.c_ptr()); return BR_REWRITE1; } default: if (m_expand_select_store) { // select(store(a, I, v), J) --> ite(I=J, v, select(a, J)) ptr_buffer new_args; new_args.push_back(to_app(args[0])->get_arg(0)); new_args.append(num_args-1, args+1); expr * sel_a_j = m().mk_app(get_fid(), OP_SELECT, num_args, new_args.c_ptr()); expr * v = to_app(args[0])->get_arg(num_args); ptr_buffer eqs; unsigned num_indices = num_args-1; for (unsigned i = 0; i < num_indices; i++) { eqs.push_back(m().mk_eq(to_app(args[0])->get_arg(i+1), args[i+1])); } if (num_indices == 1) { result = m().mk_ite(eqs[0], v, sel_a_j); return BR_REWRITE2; } else { result = m().mk_ite(m().mk_and(eqs.size(), eqs.c_ptr()), v, sel_a_j); return BR_REWRITE3; } } return BR_FAILED; } } if (m_util.is_const(args[0])) { // select(const(v), I) --> v result = to_app(args[0])->get_arg(0); return BR_DONE; } if (is_lambda(args[0])) { // anywhere lambda reduction as opposed to whnf // select(lambda(X) M, N) -> M[N/X] quantifier* q = to_quantifier(args[0]); SASSERT(q->get_num_decls() == num_args - 1); var_subst subst(m()); result = subst(q->get_expr(), num_args - 1, args + 1); return BR_REWRITE_FULL; } if (m_util.is_map(args[0])) { app* a = to_app(args[0]); func_decl* f0 = m_util.get_map_func_decl(a); expr_ref_vector args0(m()); for (expr* arg : *a) { ptr_vector args1; args1.push_back(arg); args1.append(num_args-1, args + 1); args0.push_back(m_util.mk_select(args1.size(), args1.c_ptr())); } result = m().mk_app(f0, args0.size(), args0.c_ptr()); return BR_REWRITE2; } if (m_util.is_as_array(args[0])) { // select(as-array[f], I) --> f(I) func_decl * f = m_util.get_as_array_func_decl(to_app(args[0])); result = m().mk_app(f, num_args - 1, args + 1); return BR_REWRITE1; } expr* c, *th, *el; if (m().is_ite(args[0], c, th, el) && (m_expand_select_ite || (th->get_ref_count() == 1 || el->get_ref_count() == 1))) { ptr_vector args1, args2; args1.push_back(th); args1.append(num_args-1, args + 1); args2.push_back(el); args2.append(num_args-1, args + 1); result = m().mk_ite(c, m_util.mk_select(num_args, args1.c_ptr()), m_util.mk_select(num_args, args2.c_ptr())); return BR_REWRITE2; } return BR_FAILED; } sort_ref array_rewriter::get_map_array_sort(func_decl* f, unsigned num_args, expr* const* args) { sort* s0 = m().get_sort(args[0]); unsigned sz = get_array_arity(s0); ptr_vector domain; for (unsigned i = 0; i < sz; ++i) domain.push_back(get_array_domain(s0, i)); return sort_ref(m_util.mk_array_sort(sz, domain.c_ptr(), f->get_range()), m()); } br_status array_rewriter::mk_map_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { app* store_expr = nullptr; unsigned num_indices = 0; bool same_store = true; for (unsigned i = 0; same_store && i < num_args; i++) { expr* a = args[i]; if (m_util.is_const(a)) { continue; } else if (!m_util.is_store(a)) { same_store = false; } else if (!store_expr) { num_indices = to_app(a)->get_num_args() - 2; store_expr = to_app(a); } else { for (unsigned j = 1; same_store && j < num_indices + 1; j++) { same_store = (store_expr->get_arg(j) == to_app(a)->get_arg(j)); } } } // // map_f (store a_1 j v_1) ... (store a_n j v_n) --> (store (map_f a_1 ... a_n) j (f v_1 ... v_n)) // if (same_store) { ptr_buffer arrays; ptr_buffer values; for (unsigned i = 0; i < num_args; i++) { expr* a = args[i]; if (m_util.is_const(a)) { arrays.push_back(a); values.push_back(to_app(a)->get_arg(0)); } else { arrays.push_back(to_app(a)->get_arg(0)); values.push_back(to_app(a)->get_arg(num_indices+1)); } } if (store_expr) { ptr_buffer new_args; new_args.push_back(m_util.mk_map(f, arrays.size(), arrays.c_ptr())); new_args.append(num_indices, store_expr->get_args() + 1); new_args.push_back(m().mk_app(f, values.size(), values.c_ptr())); result = m().mk_app(get_fid(), OP_STORE, new_args.size(), new_args.c_ptr()); } else { expr_ref value(m().mk_app(f, values.size(), values.c_ptr()), m()); sort_ref s = get_map_array_sort(f, num_args, args); result = m_util.mk_const_array(s, value); } TRACE("array", tout << result << "\n";); return BR_REWRITE2; } // // map_f (lambda x1 b1) ... (lambda x1 bn) -> lambda x1 (f b1 .. bn) // quantifier* lam = nullptr; for (unsigned i = 0; i < num_args; ++i) { if (is_lambda(args[i])) { lam = to_quantifier(args[i]); } } if (lam) { expr_ref_vector args1(m()); for (unsigned i = 0; i < num_args; ++i) { expr* a = args[i]; if (m_util.is_const(a)) { args1.push_back(to_app(a)->get_arg(0)); } else if (is_lambda(a)) { lam = to_quantifier(a); args1.push_back(lam->get_expr()); } else { expr_ref_vector sel(m()); sel.push_back(a); unsigned n = lam->get_num_decls(); for (unsigned i = 0; i < n; ++i) { sel.push_back(m().mk_var(n - i - 1, lam->get_decl_sort(i))); } args1.push_back(m_util.mk_select(sel.size(), sel.c_ptr())); } } result = m().mk_app(f, args1.size(), args1.c_ptr()); result = m().update_quantifier(lam, result); return BR_REWRITE3; } if (m().is_not(f) && m_util.is_map(args[0]) && m().is_not(m_util.get_map_func_decl(args[0]))) { result = to_app(args[0])->get_arg(0); return BR_DONE; } if (m().is_and(f)) { ast_mark mark; ptr_buffer es; bool change = false; unsigned j = 0; es.append(num_args, args); for (unsigned i = 0; i < es.size(); ++i) { expr* e = es[i]; if (mark.is_marked(e)) { change = true; } else if (m_util.is_map(e) && m().is_and(m_util.get_map_func_decl(e))) { mark.mark(e, true); es.append(to_app(e)->get_num_args(), to_app(e)->get_args()); } else { mark.mark(e, true); es[j++] = es[i]; } } es.shrink(j); j = 0; for (expr* e : es) { if (m_util.is_map(e) && m().is_not(m_util.get_map_func_decl(e))) { expr * arg = to_app(e)->get_arg(0); if (mark.is_marked(arg)) { sort_ref s = get_map_array_sort(f, num_args, args); result = m_util.mk_const_array(s, m().mk_false()); return BR_DONE; } // a & (!a & b & c) -> a & !(b & c) if (m_util.is_map(arg) && m().is_and(m_util.get_map_func_decl(arg))) { unsigned k = 0; ptr_buffer gs; bool and_change = false; gs.append(to_app(arg)->get_num_args(), to_app(arg)->get_args()); for (unsigned i = 0; i < gs.size(); ++i) { expr* g = gs[i]; if (mark.is_marked(g)) { change = true; and_change = true; } else if (m_util.is_map(g) && m().is_and(m_util.get_map_func_decl(g))) { gs.append(to_app(g)->get_num_args(), to_app(g)->get_args()); } else { gs[k++] = gs[i]; } } gs.shrink(k); if (and_change) { std::sort(gs.begin(), gs.end(), [](expr* a, expr* b) { return a->get_id() < b->get_id(); }); expr* arg = m_util.mk_map_assoc(f, gs.size(), gs.c_ptr()); es[j] = m_util.mk_map(m().mk_not_decl(), 1, &arg); } } } ++j; } if (change) { std::sort(es.begin(), es.end(), [](expr* a, expr* b) { return a->get_id() < b->get_id(); }); result = m_util.mk_map_assoc(f, es.size(), es.c_ptr()); return BR_REWRITE2; } } if (m().is_or(f)) { ast_mark mark; ptr_buffer es; es.append(num_args, args); unsigned j = 0; bool change = false; for (unsigned i = 0; i < es.size(); ++i) { expr* e = es[i]; if (mark.is_marked(e)) { change = true; } else if (m_util.is_map(e) && m().is_or(m_util.get_map_func_decl(e))) { mark.mark(e, true); es.append(to_app(e)->get_num_args(), to_app(e)->get_args()); } else { mark.mark(e, true); es[j++] = es[i]; } } es.shrink(j); for (expr* e : es) { if (m_util.is_map(e) && m().is_not(m_util.get_map_func_decl(e)) && mark.is_marked(to_app(e)->get_arg(0))) { sort_ref s = get_map_array_sort(f, num_args, args); result = m_util.mk_const_array(s, m().mk_true()); return BR_DONE; } } if (change) { result = m_util.mk_map_assoc(f, es.size(), es.c_ptr()); return BR_REWRITE1; } } return BR_FAILED; } void array_rewriter::mk_store(unsigned num_args, expr * const * args, expr_ref & result) { if (mk_store_core(num_args, args, result) == BR_FAILED) result = m().mk_app(get_fid(), OP_STORE, num_args, args); } void array_rewriter::mk_select(unsigned num_args, expr * const * args, expr_ref & result) { if (mk_select_core(num_args, args, result) == BR_FAILED) result = m().mk_app(get_fid(), OP_SELECT, num_args, args); } void array_rewriter::mk_map(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { if (mk_map_core(f, num_args, args, result) == BR_FAILED) result = m_util.mk_map(f, num_args, args); } br_status array_rewriter::mk_set_union(unsigned num_args, expr * const * args, expr_ref & result) { SASSERT(num_args > 0); if (num_args == 1) { result = args[0]; return BR_DONE; } SASSERT(num_args >= 2); br_status r = unsigned2br_status(num_args - 2); result = m_util.mk_map(m().mk_or_decl(), num_args, args); return r; } br_status array_rewriter::mk_set_intersect(unsigned num_args, expr * const * args, expr_ref & result) { SASSERT(num_args > 0); if (num_args == 1) { result = args[0]; return BR_DONE; } SASSERT(num_args >= 2); br_status r = unsigned2br_status(num_args - 2); result = m_util.mk_map(m().mk_and_decl(), num_args, args); return r; } br_status array_rewriter::mk_set_complement(expr * arg, expr_ref & result) { func_decl* fnot = m().mk_not_decl(); br_status st = mk_map_core(fnot, 1, &arg, result); if (BR_FAILED == st) { result = m_util.mk_map(fnot, 1, &arg); st = BR_DONE; } return st; } br_status array_rewriter::mk_set_difference(expr * arg1, expr * arg2, expr_ref & result) { expr * args[2] = { arg1, m_util.mk_map(m().mk_not_decl(), 1, &arg2) }; result = m_util.mk_map(m().mk_and_decl(), 2, args); return BR_REWRITE2; } br_status array_rewriter::mk_set_subset(expr * arg1, expr * arg2, expr_ref & result) { mk_set_difference(arg1, arg2, result); result = m().mk_eq(result.get(), m_util.mk_empty_set(m().get_sort(arg1))); return BR_REWRITE3; } void array_rewriter::mk_eq(expr* e, expr* lhs, expr* rhs, expr_ref_vector& fmls) { expr_ref tmp1(m()), tmp2(m()); expr_ref a(m()), v(m()); expr_ref_vector args0(m()), args(m()); while (m_util.is_store_ext(e, a, args0, v)) { args.reset(); args.push_back(lhs); args.append(args0); mk_select(args.size(), args.c_ptr(), tmp1); args[0] = rhs; mk_select(args.size(), args.c_ptr(), tmp2); fmls.push_back(m().mk_eq(tmp1, tmp2)); e = a; } } bool array_rewriter::has_index_set(expr* e, expr_ref& else_case, vector& stores) { expr_ref_vector args(m()); expr_ref a(m()), v(m()); a = e; while (m_util.is_store_ext(e, a, args, v)) { args.push_back(v); stores.push_back(args); e = a; } if (m_util.is_const(e, e)) { else_case = e; return true; } if (is_lambda(e)) { quantifier* q = to_quantifier(e); e = q->get_expr(); unsigned num_idxs = q->get_num_decls(); expr* e1, *e3, *store_val; if (!is_ground(e) && m().is_or(e)) { for (expr* arg : *to_app(e)) { if (!add_store(args, num_idxs, arg, m().mk_true(), stores)) { return false; } } else_case = m().mk_false(); return true; } if (!is_ground(e) && m().is_and(e)) { for (expr* arg : *to_app(e)) { if (!add_store(args, num_idxs, arg, m().mk_true(), stores)) { return false; } } else_case = m().mk_true(); return true; } while (!is_ground(e) && m().is_ite(e, e1, store_val, e3) && is_ground(store_val)) { if (!add_store(args, num_idxs, e1, store_val, stores)) { return false; } e = e3; } else_case = e; return is_ground(e); } return false; } bool array_rewriter::add_store(expr_ref_vector& args, unsigned num_idxs, expr* e, expr* store_val, vector& stores) { expr* e1, *e2; ptr_vector eqs; args.reset(); args.resize(num_idxs + 1, nullptr); bool is_not = m().is_bool(store_val) && m().is_not(e, e); eqs.push_back(e); for (unsigned i = 0; i < eqs.size(); ++i) { e = eqs[i]; if (m().is_and(e)) { eqs.append(to_app(e)->get_num_args(), to_app(e)->get_args()); continue; } if (m().is_eq(e, e1, e2)) { if (is_var(e2)) { std::swap(e1, e2); } if (is_var(e1) && is_ground(e2)) { unsigned idx = to_var(e1)->get_idx(); args[num_idxs - idx - 1] = e2; } else { return false; } continue; } return false; } for (unsigned i = 0; i < num_idxs; ++i) { if (!args.get(i)) return false; } if (is_not) { store_val = mk_not(m(), store_val); } args[num_idxs] = store_val; stores.push_back(args); return true; } bool array_rewriter::is_expandable_store(expr* s) { unsigned count = s->get_ref_count(); unsigned depth = 0; while (m_util.is_store(s)) { s = to_app(s)->get_arg(0); count += s->get_ref_count(); depth++; } return (depth >= 3 && count <= depth*2); } expr_ref array_rewriter::expand_store(expr* s) { ptr_vector stores; expr_ref result(m()); while (m_util.is_store(s)) { stores.push_back(to_app(s)); s = to_app(s)->get_arg(0); } stores.reverse(); expr_ref_vector args(m()), eqs(m()); ptr_vector sorts; svector names; sort* srt = m().get_sort(s); args.push_back(s); for (unsigned i = get_array_arity(srt); i-- > 0; ) { args.push_back(m().mk_var(i, get_array_domain(srt, i))); sorts.push_back(get_array_domain(srt, i)); names.push_back(symbol(i)); } names.reverse(); sorts.reverse(); result = m_util.mk_select(args); for (app* st : stores) { eqs.reset(); for (unsigned i = 1; i < args.size(); ++i) { eqs.push_back(m().mk_eq(args.get(i), st->get_arg(i))); } result = m().mk_ite(mk_and(eqs), st->get_arg(args.size()), result); } result = m().mk_lambda(sorts.size(), sorts.c_ptr(), names.c_ptr(), result); return result; } br_status array_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result) { TRACE("array_rewriter", tout << mk_pp(lhs, m()) << " " << mk_pp(rhs, m()) << "\n";); expr* v = nullptr; if (m_util.is_const(rhs) && is_lambda(lhs)) { std::swap(lhs, rhs); } if (m_util.is_const(lhs, v) && is_lambda(rhs)) { quantifier* lam = to_quantifier(rhs); expr_ref e(m().mk_eq(lam->get_expr(), v), m()); result = m().update_quantifier(lam, quantifier_kind::forall_k, e); return BR_REWRITE2; } expr_ref lh1(m()), rh1(m()); if (m_expand_nested_stores) { if (is_expandable_store(lhs)) { lh1 = expand_store(lhs); } if (is_expandable_store(rhs)) { rh1 = expand_store(rhs); } if (lh1 || rh1) { if (!lh1) lh1 = lhs; if (!rh1) rh1 = rhs; result = m().mk_eq(lh1, rh1); return BR_REWRITE_FULL; } } if (!m_expand_store_eq) { return BR_FAILED; } expr_ref_vector fmls(m()); #if 0 // lambda friendly version of array equality rewriting. vector indices; expr_ref lhs0(m()), rhs0(m()); expr_ref tmp1(m()), tmp2(m()); if (has_index_set(lhs, lhs0, indices) && has_index_set(rhs, rhs0, indices) && lhs0 == rhs0) { expr_ref_vector args(m()); for (auto const& idxs : indices) { args.reset(); args.push_back(lhs); idxs.pop_back(); args.append(idxs); mk_select(args.size(), args.c_ptr(), tmp1); args[0] = rhs; mk_select(args.size(), args.c_ptr(), tmp2); fmls.push_back(m().mk_eq(tmp1, tmp2)); } } #endif expr* lhs1 = lhs; while (m_util.is_store(lhs1)) { lhs1 = to_app(lhs1)->get_arg(0); } expr* rhs1 = rhs; while (m_util.is_store(rhs1)) { rhs1 = to_app(rhs1)->get_arg(0); } if (lhs1 != rhs1) { return BR_FAILED; } mk_eq(lhs, lhs, rhs, fmls); mk_eq(rhs, lhs, rhs, fmls); result = m().mk_and(fmls.size(), fmls.c_ptr()); return BR_REWRITE_FULL; } z3-z3-4.8.7/src/ast/rewriter/array_rewriter.h000066400000000000000000000062651356505360400210730ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: array_rewriter.h Abstract: Basic rewriting rules for Arrays. Author: Leonardo (leonardo) 2011-04-06 Notes: --*/ #ifndef ARRAY_REWRITER_H_ #define ARRAY_REWRITER_H_ #include "ast/array_decl_plugin.h" #include "ast/rewriter/rewriter_types.h" #include "util/lbool.h" #include "util/params.h" /** \brief Cheap rewrite rules for Arrays */ class array_rewriter { array_util m_util; bool m_sort_store; bool m_expand_select_store; bool m_expand_store_eq; bool m_expand_select_ite; bool m_expand_nested_stores; template lbool compare_args(unsigned num_args, expr * const * args1, expr * const * args2); void mk_eq(expr* e, expr* lhs, expr* rhs, expr_ref_vector& fmls); sort_ref get_map_array_sort(func_decl* f, unsigned num_args, expr* const* args); bool add_store(expr_ref_vector& args, unsigned num_idxs, expr* e, expr* store_val, vector& stores); bool is_expandable_store(expr* s); expr_ref expand_store(expr* s); public: array_rewriter(ast_manager & m, params_ref const & p = params_ref()): m_util(m) { updt_params(p); } ast_manager & m() const { return m_util.get_manager(); } family_id get_fid() const { return m_util.get_family_id(); } array_util& util() { return m_util; } void set_expand_select_store(bool f) { m_expand_select_store = f; } void set_expand_select_ite(bool f) { m_expand_select_ite = f; } 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); br_status mk_store_core(unsigned num_args, expr * const * args, expr_ref & result); br_status mk_select_core(unsigned num_args, expr * const * args, expr_ref & result); br_status mk_map_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result); void mk_store(unsigned num_args, expr * const * args, expr_ref & result); void mk_select(unsigned num_args, expr * const * args, expr_ref & result); void mk_map(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result); bool has_index_set(expr* e, expr_ref& e0, vector& indices); // The following methods never return BR_FAILED br_status mk_set_union(unsigned num_args, expr * const * args, expr_ref & result); br_status mk_set_intersect(unsigned num_args, expr * const * args, expr_ref & result); br_status mk_set_complement(expr * arg, expr_ref & result); br_status mk_set_difference(expr * arg1, expr * arg2, expr_ref & result); br_status mk_set_subset(expr * arg1, expr * arg2, expr_ref & result); br_status mk_eq_core(expr * lhs, expr * rhs, expr_ref & result); expr_ref mk_set_difference(expr* a, expr* b) { expr_ref result(m()); mk_set_difference(a, b, result); return result; } expr_ref mk_set_intersect(expr* a, expr* b) { expr_ref result(m()); expr* args[2] = { a, b }; mk_set_intersect(2, args, result); return result; } }; #endif z3-z3-4.8.7/src/ast/rewriter/array_rewriter_params.pyg000066400000000000000000000011541356505360400227760ustar00rootroot00000000000000def_module_params(module_name='rewriter', class_name='array_rewriter_params', export=True, params=(("expand_select_store", BOOL, False, "replace a (select (store ...) ...) term by an if-then-else term"), ("expand_nested_stores", BOOL, False, "replace nested stores by a lambda expression"), ("expand_store_eq", BOOL, False, "reduce (store ...) = (store ...) with a common base into selects"), ("sort_store", BOOL, False, "sort nested stores when the indices are known to be different"))) z3-z3-4.8.7/src/ast/rewriter/ast_counter.cpp000066400000000000000000000074571356505360400207170ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: ast_counter.cpp Abstract: Routines for counting features of terms, such as free variables. Author: Nikolaj Bjorner (nbjorner) 2013-03-18. Revision History: --*/ #include "ast/rewriter/ast_counter.h" void counter::update(unsigned el, int delta) { int & counter = get(el); counter += delta; } int & counter::get(unsigned el) { return m_data.insert_if_not_there2(el, 0)->get_data().m_value; } counter & counter::count(unsigned sz, const unsigned * els, int delta) { for(unsigned i=0; im_value>0 ) { cnt++; } } return cnt; } void counter::collect_positive(uint_set & acc) const { iterator eit = begin(); iterator eend = end(); for(; eit!=eend; ++eit) { if(eit->m_value>0) { acc.insert(eit->m_key); } } } bool counter::get_max_positive(unsigned & res) const { bool found = false; iterator eit = begin(); iterator eend = end(); for(; eit!=eend; ++eit) { if( eit->m_value>0 && (!found || eit->m_key>res) ) { found = true; res = eit->m_key; } } return found; } unsigned counter::get_max_positive() const { unsigned max_pos; VERIFY(get_max_positive(max_pos)); return max_pos; } int counter::get_max_counter_value() const { int res = 0; iterator eit = begin(); iterator eend = end(); for (; eit!=eend; ++eit) { if( eit->m_value>res ) { res = eit->m_value; } } return res; } void var_counter::count_vars(const app * pred, int coef) { unsigned n = pred->get_num_args(); for (unsigned i = 0; i < n; i++) { m_fv(pred->get_arg(i)); for (unsigned j = 0; j < m_fv.size(); ++j) { if (m_fv[j]) { update(j, coef); } } } m_fv.reset(); } unsigned var_counter::get_max_var(bool& has_var) { has_var = false; unsigned max_var = 0; ptr_vector qs; while (!m_todo.empty()) { expr* e = m_todo.back(); m_todo.pop_back(); if (m_visited.is_marked(e)) { continue; } m_visited.mark(e, true); switch(e->get_kind()) { case AST_QUANTIFIER: { qs.push_back(to_quantifier(e)); break; } case AST_VAR: { if (to_var(e)->get_idx() >= max_var) { has_var = true; max_var = to_var(e)->get_idx(); } break; } case AST_APP: { app* a = to_app(e); for (unsigned i = 0; i < a->get_num_args(); ++i) { m_todo.push_back(a->get_arg(i)); } break; } default: UNREACHABLE(); break; } } m_visited.reset(); while (!qs.empty()) { var_counter aux_counter; quantifier* q = qs.back(); qs.pop_back(); aux_counter.m_todo.push_back(q->get_expr()); bool has_var1 = false; unsigned max_v = aux_counter.get_max_var(has_var1); if (max_v >= max_var + q->get_num_decls()) { max_var = max_v - q->get_num_decls(); has_var = has_var || has_var1; } } return max_var; } unsigned var_counter::get_max_var(expr* e) { bool has_var = false; m_todo.push_back(e); return get_max_var(has_var); } unsigned var_counter::get_next_var(expr* e) { bool has_var = false; m_todo.push_back(e); unsigned mv = get_max_var(has_var); if (has_var) mv++; return mv; } z3-z3-4.8.7/src/ast/rewriter/ast_counter.h000066400000000000000000000047211356505360400203530ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: ast_counter.h Abstract: Routines for counting features of terms, such as free variables. Author: Nikolaj Bjorner (nbjorner) 2013-03-18. Krystof Hoder (t-khoder) 2010-10-10. Revision History: Hoisted from dl_util.h 2013-03-18. --*/ #ifndef AST_COUNTER_H_ #define AST_COUNTER_H_ #include "ast/ast.h" #include "util/map.h" #include "util/uint_set.h" #include "ast/rewriter/var_subst.h" class counter { protected: typedef u_map map_impl; map_impl m_data; public: typedef map_impl::iterator iterator; counter() {} void reset() { m_data.reset(); } iterator begin() const { return m_data.begin(); } iterator end() const { return m_data.end(); } void update(unsigned el, int delta); int & get(unsigned el); /** \brief Increase values of elements in \c els by \c delta. The function returns a reference to \c *this to allow for expressions like counter().count(sz, arr).get_positive_count() */ counter & count(unsigned sz, const unsigned * els, int delta = 1); counter & count(const unsigned_vector & els, int delta = 1) { return count(els.size(), els.c_ptr(), delta); } void collect_positive(uint_set & acc) const; unsigned get_positive_count() const; bool get_max_positive(unsigned & res) const; unsigned get_max_positive() const; /** Since the default counter value of a counter is zero, the result is never negative. */ int get_max_counter_value() const; }; class var_counter : public counter { protected: expr_fast_mark1 m_visited; expr_free_vars m_fv; ptr_vector m_todo; unsigned_vector m_scopes; unsigned get_max_var(bool & has_var); public: var_counter() {} void count_vars(const app * t, int coef = 1); unsigned get_max_var(expr* e); unsigned get_next_var(expr* e); }; class ast_counter { typedef obj_map map_impl; map_impl m_data; public: typedef map_impl::iterator iterator; ast_counter() {} iterator begin() const { return m_data.begin(); } iterator end() const { return m_data.end(); } int & get(ast * el) { return m_data.insert_if_not_there2(el, 0)->get_data().m_value; } void update(ast * el, int delta){ get(el) += delta; } void inc(ast * el) { update(el, 1); } void dec(ast * el) { update(el, -1); } }; #endif z3-z3-4.8.7/src/ast/rewriter/bit2int.cpp000066400000000000000000000314371356505360400177370ustar00rootroot00000000000000/*++ Copyright (c) 2009 Microsoft Corporation Module Name: bit2cpp.cpp Abstract: Routines for simplifying bit2int expressions. This propagates bv2int over arithmetical symbols as much as possible, converting arithmetic operations into bit-vector operations. Author: Nikolaj Bjorner (nbjorner) 2009-08-28 Revision History: --*/ #include "ast/ast_pp.h" #include "ast/ast_ll_pp.h" #include "ast/for_each_ast.h" #include "ast/rewriter/bit2int.h" #define CHECK(_x_) if (!(_x_)) { UNREACHABLE(); } bit2int::bit2int(ast_manager & m) : m_manager(m), m_bv_util(m), m_rewriter(m), m_arith_util(m), m_cache(m), m_bit0(m) { m_bit0 = m_bv_util.mk_numeral(0,1); } void bit2int::operator()(expr * m, expr_ref & result, proof_ref& p) { flush_cache(); expr_reduce emap(*this); for_each_ast(emap, m); result = get_cached(m); if (m_manager.proofs_enabled() && m != result.get()) { // TBD: rough p = m_manager.mk_rewrite(m, result); } TRACE("bit2int", tout << mk_pp(m, m_manager) << "======>\n" << mk_pp(result, m_manager) << "\n";); } unsigned bit2int::get_b2i_size(expr* n) { SASSERT(m_bv_util.is_bv2int(n)); return m_bv_util.get_bv_size(to_app(n)->get_arg(0)); } unsigned bit2int::get_numeral_bits(numeral const& k) { numeral two(2); numeral n(abs(k)); unsigned num_bits = 1; n = div(n, two); while (n.is_pos()) { ++num_bits; n = div(n, two); } return num_bits; } void bit2int::align_size(expr* e, unsigned sz, expr_ref& result) { unsigned sz1 = m_bv_util.get_bv_size(e); SASSERT(sz1 <= sz); result = m_rewriter.mk_zero_extend(sz-sz1, e); } void bit2int::align_sizes(expr_ref& a, expr_ref& b) { unsigned sz1 = m_bv_util.get_bv_size(a); unsigned sz2 = m_bv_util.get_bv_size(b); expr_ref tmp(m_manager); if (sz1 > sz2) { tmp = m_rewriter.mk_zero_extend(sz1-sz2, b); b = tmp; } else if (sz2 > sz1) { tmp = m_rewriter.mk_zero_extend(sz2-sz1, a); a = tmp; } } bool bit2int::extract_bv(expr* n, unsigned& sz, bool& sign, expr_ref& bv) { numeral k; bool is_int; if (m_bv_util.is_bv2int(n)) { bv = to_app(n)->get_arg(0); sz = m_bv_util.get_bv_size(bv); sign = false; return true; } else if (m_arith_util.is_numeral(n, k, is_int) && is_int) { sz = get_numeral_bits(k); bv = m_bv_util.mk_numeral(k, m_bv_util.mk_sort(sz)); sign = k.is_neg(); return true; } else { return false; } } bool bit2int::mk_add(expr* e1, expr* e2, expr_ref& result) { unsigned sz1, sz2; bool sign1, sign2; expr_ref tmp1(m_manager), tmp2(m_manager), tmp3(m_manager); if (extract_bv(e1, sz1, sign1, tmp1) && !sign1 && extract_bv(e2, sz2, sign2, tmp2) && !sign2) { unsigned sz; numeral k; if (m_bv_util.is_numeral(tmp1, k, sz) && k.is_zero()) { result = e2; return true; } if (m_bv_util.is_numeral(tmp2, k, sz) && k.is_zero()) { result = e1; return true; } align_sizes(tmp1, tmp2); tmp1 = m_rewriter.mk_zero_extend(1, tmp1); tmp2 = m_rewriter.mk_zero_extend(1, tmp2); SASSERT(m_bv_util.get_bv_size(tmp1) == m_bv_util.get_bv_size(tmp2)); tmp3 = m_rewriter.mk_bv_add(tmp1, tmp2); result = m_rewriter.mk_bv2int(tmp3); return true; } return false; } bool bit2int::mk_comp(eq_type ty, expr* e1, expr* e2, expr_ref& result) { unsigned sz1, sz2; bool sign1, sign2; expr_ref tmp1(m_manager), tmp2(m_manager), tmp3(m_manager); if (extract_bv(e1, sz1, sign1, tmp1) && !sign1 && extract_bv(e2, sz2, sign2, tmp2) && !sign2) { align_sizes(tmp1, tmp2); SASSERT(m_bv_util.get_bv_size(tmp1) == m_bv_util.get_bv_size(tmp2)); switch(ty) { case lt: tmp3 = m_rewriter.mk_ule(tmp2, tmp1); result = m_manager.mk_not(tmp3); break; case le: result = m_rewriter.mk_ule(tmp1, tmp2); break; case eq: result = m_manager.mk_eq(tmp1, tmp2); break; } return true; } return false; } bool bit2int::mk_mul(expr* e1, expr* e2, expr_ref& result) { unsigned sz1, sz2; bool sign1, sign2; expr_ref tmp1(m_manager), tmp2(m_manager); expr_ref tmp3(m_manager); if (extract_bv(e1, sz1, sign1, tmp1) && extract_bv(e2, sz2, sign2, tmp2)) { align_sizes(tmp1, tmp2); tmp1 = m_rewriter.mk_zero_extend(m_bv_util.get_bv_size(tmp1), tmp1); tmp2 = m_rewriter.mk_zero_extend(m_bv_util.get_bv_size(tmp2), tmp2); SASSERT(m_bv_util.get_bv_size(tmp1) == m_bv_util.get_bv_size(tmp2)); tmp3 = m_rewriter.mk_bv_mul(tmp1, tmp2); result = m_rewriter.mk_bv2int(tmp3); if (sign1 != sign2) { result = m_arith_util.mk_uminus(result); } return true; } return false; } bool bit2int::is_bv_poly(expr* n, expr_ref& pos, expr_ref& neg) { ptr_vector todo; expr_ref tmp(m_manager); numeral k; bool is_int; todo.push_back(n); neg = pos = m_rewriter.mk_bv2int(m_bit0); while (!todo.empty()) { n = todo.back(); todo.pop_back(); if (m_bv_util.is_bv2int(n)) { CHECK(mk_add(n, pos, pos)); } else if (m_arith_util.is_numeral(n, k, is_int) && is_int) { if (k.is_nonneg()) { CHECK(mk_add(n, pos, pos)); } else { tmp = m_arith_util.mk_numeral(-k, true); CHECK(mk_add(tmp, neg, neg)); } } else if (m_arith_util.is_add(n)) { for (unsigned i = 0; i < to_app(n)->get_num_args(); ++i) { todo.push_back(to_app(n)->get_arg(i)); } } else if (m_arith_util.is_mul(n) && to_app(n)->get_num_args() == 2 && m_arith_util.is_numeral(to_app(n)->get_arg(0), k, is_int) && is_int && k.is_minus_one() && m_bv_util.is_bv2int(to_app(n)->get_arg(1))) { CHECK(mk_add(to_app(n)->get_arg(1), neg, neg)); } else if (m_arith_util.is_mul(n) && to_app(n)->get_num_args() == 2 && m_arith_util.is_numeral(to_app(n)->get_arg(1), k, is_int) && is_int && k.is_minus_one() && m_bv_util.is_bv2int(to_app(n)->get_arg(0))) { CHECK(mk_add(to_app(n)->get_arg(0), neg, neg)); } else if (m_arith_util.is_uminus(n) && m_bv_util.is_bv2int(to_app(n)->get_arg(0))) { CHECK(mk_add(to_app(n)->get_arg(0), neg, neg)); } else { TRACE("bit2int", tout << "Not a poly: " << mk_pp(n, m_manager) << "\n";); return false; } } return true; } void bit2int::visit(quantifier* q) { expr_ref result(m_manager); result = get_cached(q->get_expr()); result = m_manager.update_quantifier(q, result); cache_result(q, result); } void bit2int::visit(app* n) { func_decl* f = n->get_decl(); unsigned num_args = n->get_num_args(); m_args.reset(); for (unsigned i = 0; i < num_args; ++i) { m_args.push_back(get_cached(n->get_arg(i))); } expr* const* args = m_args.c_ptr(); bool has_b2i = m_arith_util.is_le(n) || m_arith_util.is_ge(n) || m_arith_util.is_gt(n) || m_arith_util.is_lt(n) || m_manager.is_eq(n); expr_ref result(m_manager); for (unsigned i = 0; !has_b2i && i < num_args; ++i) { has_b2i = m_bv_util.is_bv2int(args[i]); } if (!has_b2i) { result = m_manager.mk_app(f, num_args, args); cache_result(n, result); return; } // // bv2int(x) + bv2int(y) -> bv2int(pad(x) + pad(y)) // bv2int(x) + k -> bv2int(pad(x) + pad(k)) // bv2int(x) * bv2int(y) -> bv2int(pad(x) * pad(y)) // bv2int(x) * k -> sign(k)*bv2int(pad(x) * pad(k)) // bv2int(x) - bv2int(y) <= z -> bv2int(x) <= bv2int(y) + z // bv2int(x) <= z - bv2int(y) -> bv2int(x) + bv2int(y) <= z // expr* e1 = nullptr, *e2 = nullptr; expr_ref tmp1(m_manager), tmp2(m_manager); expr_ref tmp3(m_manager); expr_ref pos1(m_manager), neg1(m_manager); expr_ref pos2(m_manager), neg2(m_manager); expr_ref e2bv(m_manager); bool sign2; numeral k; unsigned sz2; if (num_args >= 2) { e1 = args[0]; e2 = args[1]; } if (m_arith_util.is_add(n) && num_args >= 1) { result = e1; for (unsigned i = 1; i < num_args; ++i) { e1 = result; e2 = args[i]; if (!mk_add(e1, e2, result)) { result = m_manager.mk_app(f, num_args, args); cache_result(n, result); return; } } cache_result(n, result); } else if (m_arith_util.is_mul(n) && num_args >= 1) { result = e1; for (unsigned i = 1; i < num_args; ++i) { e1 = result; e2 = args[i]; if (!mk_mul(e1, e2, result)) { result = m_manager.mk_app(f, num_args, args); cache_result(n, result); return; } } cache_result(n, result); } else if (m_manager.is_eq(n) && is_bv_poly(e1, pos1, neg1) && is_bv_poly(e2, pos2, neg2) && mk_add(pos1, neg2, tmp1) && mk_add(neg1, pos2, tmp2) && mk_comp(eq, tmp1, tmp2, result)) { cache_result(n, result); } else if (m_arith_util.is_le(n) && is_bv_poly(e1, pos1, neg1) && is_bv_poly(e2, pos2, neg2) && mk_add(pos1, neg2, tmp1) && mk_add(neg1, pos2, tmp2) && mk_comp(le, tmp1, tmp2, result)) { cache_result(n, result); } else if (m_arith_util.is_lt(n) && is_bv_poly(e1, pos1, neg1) && is_bv_poly(e2, pos2, neg2) && mk_add(pos1, neg2, tmp1) && mk_add(neg1, pos2, tmp2) && mk_comp(lt, tmp1, tmp2, result)) { cache_result(n, result); } else if (m_arith_util.is_ge(n) && is_bv_poly(e1, pos1, neg1) && is_bv_poly(e2, pos2, neg2) && mk_add(pos1, neg2, tmp1) && mk_add(neg1, pos2, tmp2) && mk_comp(le, tmp2, tmp1, result)) { cache_result(n, result); } else if (m_arith_util.is_gt(n) && is_bv_poly(e1, pos1, neg1) && is_bv_poly(e2, pos2, neg2) && mk_add(pos1, neg2, tmp1) && mk_add(neg1, pos2, tmp2) && mk_comp(lt, tmp2, tmp1, result)) { cache_result(n, result); } else if (m_arith_util.is_mod(n) && is_bv_poly(e1, pos1, neg1) && extract_bv(e2, sz2, sign2, e2bv) && !sign2) { // // (pos1 - neg1) mod e2 = (pos1 + (e2 - (neg1 mod e2))) mod e2 // unsigned sz_p, sz_n, sz; bool sign_p, sign_n; expr_ref tmp_p(m_manager), tmp_n(m_manager); CHECK(extract_bv(pos1, sz_p, sign_p, tmp_p)); CHECK(extract_bv(neg1, sz_n, sign_n, tmp_n)); SASSERT(!sign_p && !sign_n); // pos1 mod e2 if (m_bv_util.is_numeral(tmp_n, k, sz) && k.is_zero()) { tmp1 = tmp_p; tmp2 = e2bv; align_sizes(tmp1, tmp2); tmp3 = m_rewriter.mk_bv_urem(tmp1, tmp2); result = m_rewriter.mk_bv2int(tmp3); cache_result(n, result); return; } // neg1 mod e2; tmp1 = tmp_n; tmp2 = e2bv; align_sizes(tmp1, tmp2); tmp3 = m_rewriter.mk_bv_urem(tmp1, tmp2); // e2 - (neg1 mod e2) tmp1 = e2bv; tmp2 = tmp3; align_sizes(tmp1, tmp2); tmp3 = m_rewriter.mk_bv_sub(tmp1, tmp2); // pos1 + (e2 - (neg1 mod e2)) tmp1 = tmp_p; tmp2 = tmp3; align_sizes(tmp1, tmp2); tmp_p = m_rewriter.mk_zero_extend(1, tmp1); tmp_n = m_rewriter.mk_zero_extend(1, tmp2); tmp1 = m_rewriter.mk_bv_add(tmp_p, tmp_n); // (pos1 + (e2 - (neg1 mod e2))) mod e2 tmp2 = e2bv; align_sizes(tmp1, tmp2); tmp3 = m_rewriter.mk_bv_urem(tmp1, tmp2); result = m_rewriter.mk_bv2int(tmp3); cache_result(n, result); } else { result = m_manager.mk_app(f, num_args, args); cache_result(n, result); } } expr * bit2int::get_cached(expr * n) const { return const_cast(this)->m_cache.find(n); } void bit2int::cache_result(expr * n, expr * r) { TRACE("bit2int_verbose", tout << "caching:\n" << mk_ll_pp(n, m_manager) << "======>\n" << mk_ll_pp(r, m_manager) << "\n";); m_cache.insert(n, r); } z3-z3-4.8.7/src/ast/rewriter/bit2int.h000066400000000000000000000043121356505360400173740ustar00rootroot00000000000000/*++ Copyright (c) 2009 Microsoft Corporation Module Name: bit2int.h Abstract: Routines for simplifying bit2int expressions. Author: Nikolaj Bjorner (nbjorner) 2009-08-28 Revision History: --*/ #ifndef BIT2INT_H_ #define BIT2INT_H_ #include "ast/bv_decl_plugin.h" #include "ast/arith_decl_plugin.h" #include "ast/act_cache.h" #include "ast/rewriter/bv_rewriter.h" class bit2int { protected: typedef rational numeral; enum eq_type { lt, le, eq }; class expr_reduce { bit2int& m_super; public: expr_reduce(bit2int& s) : m_super(s) {} void operator()(var* v) { m_super.cache_result(v, v); } void operator()(quantifier* q) { m_super.visit(q); } void operator()(app* a) { m_super.visit(a); } void operator()(ast* a) {} }; typedef act_cache expr_map; ast_manager & m_manager; bv_util m_bv_util; bv_rewriter m_rewriter; arith_util m_arith_util; expr_map m_cache; // map: ast -> ast ref. counters are incremented when inserted here. expr_ref m_bit0; ptr_vector m_args; void visit(app* n); void visit(quantifier* q); unsigned get_b2i_size(expr * n); bool extract_bv(expr* n, unsigned& sz, bool& sign, expr_ref& bv); unsigned get_numeral_bits(numeral const& k); bool is_bv_poly(expr* n, expr_ref& pos, expr_ref& neg); bool mk_mul(expr* a, expr* b, expr_ref& result); bool mk_comp(eq_type ty, expr* e1, expr* e2, expr_ref& result); bool mk_add(expr* e1, expr* e2, expr_ref& result); expr * get_cached(expr * n) const; bool is_cached(expr * n) const { return get_cached(n) != nullptr; } void cache_result(expr * n, expr * r); void reset_cache() { m_cache.reset(); } void flush_cache() { m_cache.cleanup(); } void align_size(expr* e, unsigned sz, expr_ref& result); void align_sizes(expr_ref& a, expr_ref& b); public: bit2int(ast_manager & m); void operator()(expr * m, expr_ref & result, proof_ref& p); }; #endif /* BIT2INT_H_ */ z3-z3-4.8.7/src/ast/rewriter/bit_blaster/000077500000000000000000000000001356505360400201425ustar00rootroot00000000000000z3-z3-4.8.7/src/ast/rewriter/bit_blaster/CMakeLists.txt000066400000000000000000000002001356505360400226720ustar00rootroot00000000000000z3_add_component(bit_blaster SOURCES bit_blaster.cpp bit_blaster_rewriter.cpp COMPONENT_DEPENDENCIES rewriter ) z3-z3-4.8.7/src/ast/rewriter/bit_blaster/bit_blaster.cpp000066400000000000000000000077441356505360400231540ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: bit_blaster.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-06-05. Revision History: --*/ #include "ast/rewriter/bit_blaster/bit_blaster.h" #include "ast/rewriter/bit_blaster/bit_blaster_tpl_def.h" #include "ast/ast_pp.h" #include "ast/bv_decl_plugin.h" bit_blaster_cfg::bit_blaster_cfg(bv_util & u, bit_blaster_params const & p, bool_rewriter& rw): m_util(u), m_params(p), m_rw(rw) { } static void sort_args(expr * & l1, expr * & l2, expr * & l3) { expr * args[3] = {l1, l2, l3}; // ast_lt_proc is based on the AST ids. So, it is a total order on AST nodes. // No need for stable_sort std::sort(args, args+3, ast_lt_proc()); l1 = args[0]; l2 = args[1]; l3 = args[2]; } void bit_blaster_cfg::mk_xor3(expr * l1, expr * l2, expr * l3, expr_ref & r) { TRACE("xor3", tout << "#" << l1->get_id() << " #" << l2->get_id() << " #" << l3->get_id();); sort_args(l1, l2, l3); TRACE("xor3_sorted", tout << "#" << l1->get_id() << " #" << l2->get_id() << " #" << l3->get_id();); if (m_params.m_bb_ext_gates) { if (l1 == l2) r = l3; else if (l1 == l3) r = l2; else if (l2 == l3) r = l1; else if (m().is_complement(l1, l2)) m_rw.mk_not(l3, r); else if (m().is_complement(l1, l3)) m_rw.mk_not(l2, r); else if (m().is_complement(l2, l3)) m_rw.mk_not(l1, r); else if (m().is_true(l1)) m_rw.mk_iff(l2, l3, r); else if (m().is_false(l1)) m_rw.mk_xor(l2, l3, r); else if (m().is_true(l2)) m_rw.mk_iff(l1, l3, r); else if (m().is_false(l2)) m_rw.mk_xor(l1, l3, r); else if (m().is_true(l3)) m_rw.mk_iff(l1, l2, r); else if (m().is_false(l3)) m_rw.mk_xor(l1, l2, r); else r = m().mk_app(m_util.get_family_id(), OP_XOR3, l1, l2, l3); } else { expr_ref t(m()); m_rw.mk_xor(l1, l2, t); m_rw.mk_xor(t, l3, r); } } void bit_blaster_cfg::mk_carry(expr * l1, expr * l2, expr * l3, expr_ref & r) { TRACE("carry", tout << "#" << l1->get_id() << " #" << l2->get_id() << " #" << l3->get_id();); sort_args(l1, l2, l3); TRACE("carry_sorted", tout << "#" << l1->get_id() << " #" << l2->get_id() << " #" << l3->get_id();); if (m_params.m_bb_ext_gates) { if ((m().is_false(l1) && m().is_false(l2)) || (m().is_false(l1) && m().is_false(l3)) || (m().is_false(l2) && m().is_false(l3))) r = m().mk_false(); else if ((m().is_true(l1) && m().is_true(l2)) || (m().is_true(l1) && m().is_true(l3)) || (m().is_true(l2) && m().is_true(l3))) r = m().mk_true(); else if (l1 == l2 && l1 == l3) r = l1; else if (m().is_false(l1)) m_rw.mk_and(l2, l3, r); else if (m().is_false(l2)) m_rw.mk_and(l1, l3, r); else if (m().is_false(l3)) m_rw.mk_and(l1, l2, r); else if (m().is_true(l1)) m_rw.mk_or(l2, l3, r); else if (m().is_true(l2)) m_rw.mk_or(l1, l3, r); else if (m().is_true(l3)) m_rw.mk_or(l1, l2, r); else if (m().is_complement(l1, l2)) r = l3; else if (m().is_complement(l1, l3)) r = l2; else if (m().is_complement(l2, l3)) r = l1; else r = m().mk_app(m_util.get_family_id(), OP_CARRY, l1, l2, l3); } else { expr_ref t1(m()), t2(m()), t3(m()); m_rw.mk_and(l1, l2, t1); m_rw.mk_and(l1, l3, t2); m_rw.mk_and(l2, l3, t3); m_rw.mk_or(t1, t2, t3, r); } } template class bit_blaster_tpl; bit_blaster::bit_blaster(ast_manager & m, bit_blaster_params const & params): bit_blaster_tpl(bit_blaster_cfg(m_util, params, m_rw)), m_util(m), m_rw(m) { } z3-z3-4.8.7/src/ast/rewriter/bit_blaster/bit_blaster.h000066400000000000000000000043521356505360400226110ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: bit_blaster.h Abstract: Author: Leonardo de Moura (leonardo) 2008-06-05. Revision History: --*/ #ifndef BIT_BLASTER_H_ #define BIT_BLASTER_H_ #include "ast/rewriter/bool_rewriter.h" #include "ast/rewriter/bit_blaster/bit_blaster_params.h" #include "ast/rewriter/bit_blaster/bit_blaster_tpl.h" #include "ast/bv_decl_plugin.h" #include "util/rational.h" class bit_blaster_cfg { public: typedef rational numeral; protected: bv_util & m_util; bit_blaster_params const & m_params; bool_rewriter & m_rw; public: bit_blaster_cfg(bv_util & u, bit_blaster_params const & p, bool_rewriter& rw); ast_manager & m() const { return m_util.get_manager(); } numeral power(unsigned n) const { return rational::power_of_two(n); } void mk_xor(expr * a, expr * b, expr_ref & r) { m_rw.mk_xor(a, b, r); } void mk_xor3(expr * a, expr * b, expr * c, expr_ref & r); void mk_carry(expr * a, expr * b, expr * c, expr_ref & r); void mk_iff(expr * a, expr * b, expr_ref & r) { m_rw.mk_iff(a, b, r); } void mk_and(expr * a, expr * b, expr_ref & r) { m_rw.mk_and(a, b, r); } void mk_and(expr * a, expr * b, expr * c, expr_ref & r) { m_rw.mk_and(a, b, c, r); } void mk_and(unsigned sz, expr * const * args, expr_ref & r) { m_rw.mk_and(sz, args, r); } void mk_or(expr * a, expr * b, expr_ref & r) { m_rw.mk_or(a, b, r); } void mk_or(expr * a, expr * b, expr * c, expr_ref & r) { m_rw.mk_or(a, b, c, r); } void mk_or(unsigned sz, expr * const * args, expr_ref & r) { m_rw.mk_or(sz, args, r); } void mk_not(expr * a, expr_ref & r) { m_rw.mk_not(a, r); } void mk_ite(expr * c, expr * t, expr * e, expr_ref & r) { m_rw.mk_ite(c, t, e, r); } void mk_nand(expr * a, expr * b, expr_ref & r) { m_rw.mk_nand(a, b, r); } void mk_nor(expr * a, expr * b, expr_ref & r) { m_rw.mk_nor(a, b, r); } }; class bit_blaster : public bit_blaster_tpl { bv_util m_util; bool_rewriter m_rw; public: bit_blaster(ast_manager & m, bit_blaster_params const & params); bit_blaster_params const & get_params() const { return this->m_params; } }; #endif /* BIT_BLASTER_H_ */ z3-z3-4.8.7/src/ast/rewriter/bit_blaster/bit_blaster_params.h000066400000000000000000000016601356505360400241530ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: bit_blaster_params.h Abstract: Author: Leonardo de Moura (leonardo) 2008-10-02. Revision History: --*/ #ifndef BIT_BLASTER_PARAMS_H_ #define BIT_BLASTER_PARAMS_H_ struct bit_blaster_params { bool m_bb_ext_gates; bool m_bb_quantifiers; bit_blaster_params() : m_bb_ext_gates(false), m_bb_quantifiers(false) { } #if 0 void register_params(ini_params & p) { p.register_bool_param("bb_ext_gates", m_bb_ext_gates, "use extended gates during bit-blasting"); p.register_bool_param("bb_quantifiers", m_bb_quantifiers, "convert bit-vectors to Booleans in quantifiers"); } #endif void display(std::ostream & out) const { out << "m_bb_ext_gates=" << m_bb_ext_gates << std::endl; out << "m_bb_quantifiers=" << m_bb_quantifiers << std::endl; } }; #endif /* BIT_BLASTER_PARAMS_H_ */ z3-z3-4.8.7/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.cpp000066400000000000000000000641411356505360400250710ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: bit_blaster_rewriter.cpp Abstract: Bit-blasting rewriter Author: Leonardo (leonardo) 2012-10-04 Notes: --*/ #include "ast/rewriter/bit_blaster/bit_blaster_rewriter.h" #include "ast/bv_decl_plugin.h" #include "ast/rewriter/bit_blaster/bit_blaster_tpl_def.h" #include "ast/rewriter/rewriter_def.h" #include "ast/rewriter/bool_rewriter.h" #include "util/ref_util.h" #include "ast/ast_smt2_pp.h" struct blaster_cfg { typedef rational numeral; bool_rewriter & m_rewriter; bv_util & m_util; blaster_cfg(bool_rewriter & r, bv_util & u):m_rewriter(r), m_util(u) {} ast_manager & m() const { return m_util.get_manager(); } numeral power(unsigned n) const { return rational::power_of_two(n); } void mk_xor(expr * a, expr * b, expr_ref & r) { m_rewriter.mk_xor(a, b, r); } void mk_xor3(expr * a, expr * b, expr * c, expr_ref & r) { expr_ref tmp(m()); mk_xor(b, c, tmp); mk_xor(a, tmp, r); } void mk_iff(expr * a, expr * b, expr_ref & r) { m_rewriter.mk_iff(a, b, r); } void mk_and(expr * a, expr * b, expr_ref & r) { m_rewriter.mk_and(a, b, r); } void mk_and(expr * a, expr * b, expr * c, expr_ref & r) { m_rewriter.mk_and(a, b, c, r); } void mk_and(unsigned sz, expr * const * args, expr_ref & r) { m_rewriter.mk_and(sz, args, r); } void mk_or(expr * a, expr * b, expr_ref & r) { m_rewriter.mk_or(a, b, r); } void mk_or(expr * a, expr * b, expr * c, expr_ref & r) { m_rewriter.mk_or(a, b, c, r); } void mk_or(unsigned sz, expr * const * args, expr_ref & r) { m_rewriter.mk_or(sz, args, r); } void mk_not(expr * a, expr_ref & r) { m_rewriter.mk_not(a, r); } void mk_carry(expr * a, expr * b, expr * c, expr_ref & r) { expr_ref t1(m()), t2(m()), t3(m()); #if 1 mk_and(a, b, t1); mk_and(a, c, t2); mk_and(b, c, t3); mk_or(t1, t2, t3, r); #else mk_or(a, b, t1); mk_or(a, c, t2); mk_or(b, c, t3); mk_and(t1, t2, t3, r); #endif } void mk_ite(expr * c, expr * t, expr * e, expr_ref & r) { m_rewriter.mk_ite(c, t, e, r); } void mk_nand(expr * a, expr * b, expr_ref & r) { m_rewriter.mk_nand(a, b, r); } void mk_nor(expr * a, expr * b, expr_ref & r) { m_rewriter.mk_nor(a, b, r); } }; class blaster : public bit_blaster_tpl { bool_rewriter m_rewriter; bv_util m_util; public: blaster(ast_manager & m): bit_blaster_tpl(blaster_cfg(m_rewriter, m_util)), m_rewriter(m), m_util(m) { m_rewriter.set_flat(false); m_rewriter.set_elim_and(true); } bv_util & butil() { return m_util; } }; struct blaster_rewriter_cfg : public default_rewriter_cfg { ast_manager & m_manager; blaster & m_blaster; expr_ref_vector m_in1; expr_ref_vector m_in2; expr_ref_vector m_out; obj_map m_const2bits; expr_ref_vector m_bindings; unsigned_vector m_shifts; func_decl_ref_vector m_keys; expr_ref_vector m_values; unsigned_vector m_keyval_lim; func_decl_ref_vector m_newbits; unsigned_vector m_newbits_lim; bool m_blast_mul; bool m_blast_add; bool m_blast_quant; bool m_blast_full; unsigned long long m_max_memory; unsigned m_max_steps; ast_manager & m() const { return m_manager; } bv_util & butil() { return m_blaster.butil(); } void cleanup_buffers() { m_in1.finalize(); m_in2.finalize(); m_out.finalize(); m_bindings.finalize(); } blaster_rewriter_cfg(ast_manager & m, blaster & b, params_ref const & p): m_manager(m), m_blaster(b), m_in1(m), m_in2(m), m_out(m), m_bindings(m), m_keys(m), m_values(m), m_newbits(m) { updt_params(p); } ~blaster_rewriter_cfg() { } 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); m_blast_add = p.get_bool("blast_add", true); m_blast_mul = p.get_bool("blast_mul", true); m_blast_full = p.get_bool("blast_full", false); m_blast_quant = p.get_bool("blast_quant", false); m_blaster.set_max_memory(m_max_memory); } bool rewrite_patterns() const { return true; } bool max_steps_exceeded(unsigned num_steps) const { if (memory::get_allocation_size() > m_max_memory) throw rewriter_exception(Z3_MAX_MEMORY_MSG); return num_steps > m_max_steps; } void get_bits(expr * t, expr_ref_vector & out_bits) { if (butil().is_mkbv(t)) { out_bits.append(to_app(t)->get_num_args(), to_app(t)->get_args()); } else { unsigned bv_size = butil().get_bv_size(t); for (unsigned i = 0; i < bv_size; i++) { parameter p(i); out_bits.push_back(m().mk_app(butil().get_family_id(), OP_BIT2BOOL, 1, &p, 1, &t)); } SASSERT(bv_size == out_bits.size()); } } void push() { m_keyval_lim.push_back(m_keys.size()); m_newbits_lim.push_back(m_newbits.size()); } unsigned get_num_scopes() const { return m_keyval_lim.size(); } void pop(unsigned num_scopes) { if (num_scopes > 0) { SASSERT(num_scopes <= m_keyval_lim.size()); unsigned new_sz = m_keyval_lim.size() - num_scopes; unsigned lim = m_keyval_lim[new_sz]; for (unsigned i = m_keys.size(); i > lim; ) { --i; m_const2bits.remove(m_keys[i].get()); } m_keys.resize(lim); m_values.resize(lim); m_keyval_lim.resize(new_sz); lim = m_newbits_lim[new_sz]; m_newbits.shrink(lim); m_newbits_lim.shrink(new_sz); } } unsigned m_keypos; void start_rewrite() { m_keypos = m_keys.size(); } void end_rewrite(obj_map& const2bits, ptr_vector & newbits) { for (unsigned i = m_keypos; i < m_keys.size(); ++i) { const2bits.insert(m_keys[i].get(), m_values[i].get()); } for (func_decl* f : m_newbits) newbits.push_back(f); } template app * mk_mkbv(V const & bits) { return m().mk_app(butil().get_family_id(), OP_MKBV, bits.size(), bits.c_ptr()); } void mk_const(func_decl * f, expr_ref & result) { SASSERT(f->get_family_id() == null_family_id); SASSERT(f->get_arity() == 0); expr * r; if (m_const2bits.find(f, r)) { result = r; return; } sort * s = f->get_range(); SASSERT(butil().is_bv_sort(s)); unsigned bv_size = butil().get_bv_size(s); sort * b = m().mk_bool_sort(); m_out.reset(); for (unsigned i = 0; i < bv_size; i++) { m_out.push_back(m().mk_fresh_const(nullptr, b)); m_newbits.push_back(to_app(m_out.back())->get_decl()); } r = mk_mkbv(m_out); m_const2bits.insert(f, r); m_keys.push_back(f); m_values.push_back(r); result = r; } #define MK_UNARY_REDUCE(OP, BB_OP) \ void OP(expr * arg, expr_ref & result) { \ m_in1.reset(); \ get_bits(arg, m_in1); \ m_out.reset(); \ m_blaster.BB_OP(m_in1.size(), m_in1.c_ptr(), m_out); \ result = mk_mkbv(m_out); \ } MK_UNARY_REDUCE(reduce_not, mk_not); MK_UNARY_REDUCE(reduce_redor, mk_redor); MK_UNARY_REDUCE(reduce_redand, mk_redand); #define MK_BIN_REDUCE(OP, BB_OP) \ void OP(expr * arg1, expr * arg2, expr_ref & result) { \ m_in1.reset(); m_in2.reset(); \ get_bits(arg1, m_in1); \ get_bits(arg2, m_in2); \ m_out.reset(); \ m_blaster.BB_OP(m_in1.size(), m_in1.c_ptr(), m_in2.c_ptr(), m_out); \ result = mk_mkbv(m_out); \ } MK_BIN_REDUCE(reduce_shl, mk_shl); MK_BIN_REDUCE(reduce_ashr, mk_ashr); MK_BIN_REDUCE(reduce_lshr, mk_lshr); MK_BIN_REDUCE(reduce_udiv, mk_udiv); MK_BIN_REDUCE(reduce_urem, mk_urem); MK_BIN_REDUCE(reduce_sdiv, mk_sdiv); MK_BIN_REDUCE(reduce_srem, mk_srem); MK_BIN_REDUCE(reduce_smod, mk_smod); MK_BIN_REDUCE(reduce_ext_rotate_left, mk_ext_rotate_left); MK_BIN_REDUCE(reduce_ext_rotate_right, mk_ext_rotate_right); #define MK_BIN_AC_REDUCE(OP, BIN_OP, BB_OP) \ MK_BIN_REDUCE(BIN_OP, BB_OP); \ void OP(unsigned num_args, expr * const * args, expr_ref & result) { \ SASSERT(num_args > 0); \ result = args[0]; \ expr_ref new_result(m_manager); \ for (unsigned i = 1; i < num_args; i++) { \ BIN_OP(result.get(), args[i], new_result); \ result = new_result; \ } \ } MK_BIN_AC_REDUCE(reduce_add, reduce_bin_add, mk_adder); MK_BIN_AC_REDUCE(reduce_mul, reduce_bin_mul, mk_multiplier); MK_BIN_AC_REDUCE(reduce_or, reduce_bin_or, mk_or); MK_BIN_AC_REDUCE(reduce_xor, reduce_bin_xor, mk_xor); #define MK_BIN_PRED_REDUCE(OP, BB_OP) \ void OP(expr * arg1, expr * arg2, expr_ref & result) { \ m_in1.reset(); m_in2.reset(); \ get_bits(arg1, m_in1); \ get_bits(arg2, m_in2); \ m_blaster.BB_OP(m_in1.size(), m_in1.c_ptr(), m_in2.c_ptr(), result); \ } MK_BIN_PRED_REDUCE(reduce_eq, mk_eq); MK_BIN_PRED_REDUCE(reduce_sle, mk_sle); MK_BIN_PRED_REDUCE(reduce_ule, mk_ule); MK_BIN_PRED_REDUCE(reduce_umul_no_overflow, mk_umul_no_overflow); MK_BIN_PRED_REDUCE(reduce_smul_no_overflow, mk_smul_no_overflow); MK_BIN_PRED_REDUCE(reduce_smul_no_underflow, mk_smul_no_underflow); #define MK_PARAMETRIC_UNARY_REDUCE(OP, BB_OP) \ void OP(expr * arg, unsigned n, expr_ref & result) { \ m_in1.reset(); \ get_bits(arg, m_in1); \ m_out.reset(); \ m_blaster.BB_OP(m_in1.size(), m_in1.c_ptr(), n, m_out); \ result = mk_mkbv(m_out); \ } MK_PARAMETRIC_UNARY_REDUCE(reduce_sign_extend, mk_sign_extend); void reduce_ite(expr * arg1, expr * arg2, expr * arg3, expr_ref & result) { m_in1.reset(); m_in2.reset(); get_bits(arg2, m_in1); get_bits(arg3, m_in2); m_out.reset(); m_blaster.mk_multiplexer(arg1, m_in1.size(), m_in1.c_ptr(), m_in2.c_ptr(), m_out); result = mk_mkbv(m_out); } void reduce_concat(unsigned num_args, expr * const * args, expr_ref & result) { m_out.reset(); unsigned i = num_args; while (i > 0) { i--; m_in1.reset(); get_bits(args[i], m_in1); m_out.append(m_in1.size(), m_in1.c_ptr()); } result = mk_mkbv(m_out); } void reduce_extract(unsigned start, unsigned end, expr * arg, expr_ref & result) { m_in1.reset(); get_bits(arg, m_in1); m_out.reset(); for (unsigned i = start; i <= end; ++i) m_out.push_back(m_in1.get(i)); result = mk_mkbv(m_out); } void reduce_num(func_decl * f, expr_ref & result) { SASSERT(f->get_num_parameters() == 2); SASSERT(f->get_parameter(0).is_rational()); SASSERT(f->get_parameter(1).is_int()); rational v = f->get_parameter(0).get_rational(); unsigned bv_sz = f->get_parameter(1).get_int(); m_out.reset(); m_blaster.num2bits(v, bv_sz, m_out); result = mk_mkbv(m_out); } void throw_unsupported() { throw rewriter_exception("operator is not supported, you must simplify the goal before applying bit-blasting"); } void blast_bv_term(expr * t, expr_ref & result, proof_ref & result_pr) { ptr_buffer bits; unsigned bv_size = butil().get_bv_size(t); for (unsigned i = 0; i < bv_size; i++) { parameter p(i); bits.push_back(m().mk_app(butil().get_family_id(), OP_BIT2BOOL, 1, &p, 1, &t)); } result = mk_mkbv(bits); result_pr = nullptr; } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { result_pr = nullptr; TRACE("bit_blaster", tout << f->get_name() << " "; for (unsigned i = 0; i < num; ++i) tout << mk_pp(args[i], m()) << " "; tout << "\n";); if (num == 0 && f->get_family_id() == null_family_id && butil().is_bv_sort(f->get_range())) { mk_const(f, result); return BR_DONE; } if (m().is_eq(f)) { SASSERT(num == 2); if (butil().is_bv(args[0])) { reduce_eq(args[0], args[1], result); return BR_DONE; } return BR_FAILED; } if (m().is_ite(f)) { SASSERT(num == 3); if (butil().is_bv(args[1])) { reduce_ite(args[0], args[1], args[2], result); return BR_DONE; } return BR_FAILED; } if (f->get_family_id() == butil().get_family_id()) { switch (f->get_decl_kind()) { case OP_BV_NUM: SASSERT(num == 0); reduce_num(f, result); return BR_DONE; case OP_BADD: if (!m_blast_add) return BR_FAILED; reduce_add(num, args, result); return BR_DONE; case OP_BMUL: if (!m_blast_mul) return BR_FAILED; reduce_mul(num, args, result); return BR_DONE; case OP_BSDIV: case OP_BUDIV: case OP_BSREM: case OP_BUREM: case OP_BSMOD: if (m_blast_mul) throw_unsupported(); // must simplify to DIV_I AND DIV0 return BR_FAILED; // keep them case OP_BSDIV0: case OP_BUDIV0: case OP_BSREM0: case OP_BUREM0: case OP_BSMOD0: return BR_FAILED; case OP_BSDIV_I: if (!m_blast_mul) return BR_FAILED; SASSERT(num == 2); reduce_sdiv(args[0], args[1], result); return BR_DONE; case OP_BUDIV_I: if (!m_blast_mul) return BR_FAILED; SASSERT(num == 2); reduce_udiv(args[0], args[1], result); return BR_DONE; case OP_BSREM_I: if (!m_blast_mul) return BR_FAILED; SASSERT(num == 2); reduce_srem(args[0], args[1], result); return BR_DONE; case OP_BUREM_I: if (!m_blast_mul) return BR_FAILED; SASSERT(num == 2); reduce_urem(args[0], args[1], result); return BR_DONE; case OP_BSMOD_I: if (!m_blast_mul) return BR_FAILED; SASSERT(num == 2); reduce_smod(args[0], args[1], result); return BR_DONE; case OP_ULEQ: SASSERT(num == 2); reduce_ule(args[0], args[1], result); return BR_DONE; case OP_SLEQ: SASSERT(num == 2); reduce_sle(args[0], args[1], result); return BR_DONE; case OP_BOR: reduce_or(num, args, result); return BR_DONE; case OP_BNOT: SASSERT(num == 1); reduce_not(args[0], result); return BR_DONE; case OP_BXOR: reduce_xor(num, args, result); return BR_DONE; case OP_CONCAT: reduce_concat(num, args, result); return BR_DONE; case OP_SIGN_EXT: SASSERT(num == 1); reduce_sign_extend(args[0], f->get_parameter(0).get_int(), result); return BR_DONE; case OP_EXTRACT: SASSERT(num == 1); reduce_extract(f->get_parameter(1).get_int(), f->get_parameter(0).get_int(), args[0], result); return BR_DONE; case OP_BREDOR: SASSERT(num == 1); reduce_redor(args[0], result); return BR_DONE; case OP_BREDAND: SASSERT(num == 1); reduce_redand(args[0], result); return BR_DONE; case OP_BSHL: SASSERT(num == 2); reduce_shl(args[0], args[1], result); return BR_DONE; case OP_BLSHR: SASSERT(num == 2); reduce_lshr(args[0], args[1], result); return BR_DONE; case OP_BASHR: SASSERT(num == 2); reduce_ashr(args[0], args[1], result); return BR_DONE; case OP_EXT_ROTATE_LEFT: SASSERT(num == 2); reduce_ext_rotate_left(args[0], args[1], result); return BR_DONE; case OP_EXT_ROTATE_RIGHT: SASSERT(num == 2); reduce_ext_rotate_right(args[0], args[1], result); return BR_DONE; case OP_BUMUL_NO_OVFL: SASSERT(num == 2); reduce_umul_no_overflow(args[0], args[1], result); return BR_DONE; case OP_BSMUL_NO_OVFL: SASSERT(num == 2); reduce_smul_no_overflow(args[0], args[1], result); return BR_DONE; case OP_BSMUL_NO_UDFL: SASSERT(num == 2); reduce_smul_no_underflow(args[0], args[1], result); return BR_DONE; case OP_BIT2BOOL: case OP_MKBV: case OP_INT2BV: case OP_BV2INT: return BR_FAILED; default: TRACE("bit_blaster", tout << "non-supported operator: " << f->get_name() << "\n"; for (unsigned i = 0; i < num; i++) tout << mk_ismt2_pp(args[i], m()) << std::endl;); throw_unsupported(); } } if (m_blast_full && butil().is_bv_sort(f->get_range())) { blast_bv_term(m().mk_app(f, num, args), result, result_pr); return BR_DONE; } return BR_FAILED; } bool pre_visit(expr * t) { if (m_blast_quant && is_quantifier(t)) { quantifier * q = to_quantifier(t); ptr_buffer new_bindings; ptr_buffer new_args; unsigned i = q->get_num_decls(); unsigned j = 0; while (i > 0) { --i; sort * s = q->get_decl_sort(i); if (butil().is_bv_sort(s)) { unsigned bv_size = butil().get_bv_size(s); new_args.reset(); for (unsigned k = 0; k < bv_size; k++) { new_args.push_back(m().mk_var(j, m().mk_bool_sort())); j++; } new_bindings.push_back(mk_mkbv(new_args)); } else { new_bindings.push_back(m().mk_var(j, s)); j++; } } SASSERT(new_bindings.size() == q->get_num_decls()); i = q->get_num_decls(); unsigned shift = j; if (!m_shifts.empty()) shift += m_shifts.back(); while (i > 0) { i--; m_bindings.push_back(new_bindings[i]); m_shifts.push_back(shift); } } return true; } bool reduce_var(var * t, expr_ref & result, proof_ref & result_pr) { if (m_blast_quant) { if (m_bindings.empty()) return false; unsigned shift = m_shifts.back(); if (t->get_idx() >= m_bindings.size()) { if (shift == 0) return false; result = m_manager.mk_var(t->get_idx() + shift, t->get_sort()); } else { unsigned offset = m_bindings.size() - t->get_idx() - 1; result = m_bindings.get(offset); shift = shift - m_shifts[offset]; if (shift > 0) { var_shifter vs(m_manager); vs(result, shift, result); } } result_pr = nullptr; return true; } if (m_blast_full && butil().is_bv(t)) { blast_bv_term(t, result, result_pr); return true; } return false; } 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) { if (!m_blast_quant) return false; unsigned curr_sz = m_bindings.size(); SASSERT(old_q->get_num_decls() <= curr_sz); unsigned num_decls = old_q->get_num_decls(); unsigned old_sz = curr_sz - num_decls; string_buffer<> name_buffer; ptr_buffer new_decl_sorts; sbuffer new_decl_names; for (unsigned i = 0; i < num_decls; i++) { symbol const & n = old_q->get_decl_name(i); sort * s = old_q->get_decl_sort(i); if (butil().is_bv_sort(s)) { unsigned bv_size = butil().get_bv_size(s); for (unsigned j = 0; j < bv_size; j++) { name_buffer.reset(); name_buffer << n << "." << j; new_decl_names.push_back(symbol(name_buffer.c_str())); new_decl_sorts.push_back(m().mk_bool_sort()); } } else { new_decl_sorts.push_back(s); new_decl_names.push_back(n); } } result = m().mk_quantifier(old_q->get_kind(), new_decl_sorts.size(), new_decl_sorts.c_ptr(), new_decl_names.c_ptr(), new_body, old_q->get_weight(), old_q->get_qid(), old_q->get_skid(), old_q->get_num_patterns(), new_patterns, old_q->get_num_no_patterns(), new_no_patterns); result_pr = nullptr; m_bindings.shrink(old_sz); m_shifts.shrink(old_sz); return true; } }; struct bit_blaster_rewriter::imp : public rewriter_tpl { blaster m_blaster; blaster_rewriter_cfg m_cfg; imp(ast_manager & m, params_ref const & p): rewriter_tpl(m, m.proofs_enabled(), m_cfg), m_blaster(m), m_cfg(m, m_blaster, p) { SASSERT(m_blaster.butil().get_family_id() == m.get_family_id("bv")); } void push() { m_cfg.push(); } void pop(unsigned s) { m_cfg.pop(s); } void start_rewrite() { m_cfg.start_rewrite(); } void end_rewrite(obj_map& const2bits, ptr_vector & newbits) { m_cfg.end_rewrite(const2bits, newbits); } unsigned get_num_scopes() const { return m_cfg.get_num_scopes(); } }; bit_blaster_rewriter::bit_blaster_rewriter(ast_manager & m, params_ref const & p): m_imp(alloc(imp, m, p)) { } bit_blaster_rewriter::~bit_blaster_rewriter() { dealloc(m_imp); } void bit_blaster_rewriter::updt_params(params_ref const& p) { m_imp->m_cfg.updt_params(p); } void bit_blaster_rewriter::push() { m_imp->push(); } void bit_blaster_rewriter::pop(unsigned num_scopes) { m_imp->pop(num_scopes); } ast_manager & bit_blaster_rewriter::m() const { return m_imp->m(); } unsigned bit_blaster_rewriter::get_num_steps() const { return m_imp->get_num_steps(); } void bit_blaster_rewriter::cleanup() { m_imp->cleanup(); } obj_map const & bit_blaster_rewriter::const2bits() const { return m_imp->m_cfg.m_const2bits; } void bit_blaster_rewriter::operator()(expr * e, expr_ref & result, proof_ref & result_proof) { m_imp->operator()(e, result, result_proof); } unsigned bit_blaster_rewriter::get_num_scopes() const { return m_imp->get_num_scopes(); } void bit_blaster_rewriter::start_rewrite() { m_imp->start_rewrite(); } void bit_blaster_rewriter::end_rewrite(obj_map& const2bits, ptr_vector & newbits) { m_imp->end_rewrite(const2bits, newbits); } z3-z3-4.8.7/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.h000066400000000000000000000017371356505360400245400ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: bit_blaster_rewriter.h Abstract: Bit-blasting rewriter Author: Leonardo (leonardo) 2012-10-04 Notes: --*/ #ifndef BIT_BLASTER_REWRITER_H_ #define BIT_BLASTER_REWRITER_H_ #include "ast/ast.h" #include "util/obj_hashtable.h" #include "util/params.h" class bit_blaster_rewriter { struct imp; imp * m_imp; public: bit_blaster_rewriter(ast_manager & m, params_ref const & p); ~bit_blaster_rewriter(); void updt_params(params_ref const & p); ast_manager & m() const; unsigned get_num_steps() const; void cleanup(); void start_rewrite(); void end_rewrite(obj_map& const2bits, ptr_vector & newbits); void operator()(expr * e, expr_ref & result, proof_ref & result_proof); void push(); void pop(unsigned num_scopes); unsigned get_num_scopes() const; private: obj_map const& const2bits() const; }; #endif z3-z3-4.8.7/src/ast/rewriter/bit_blaster/bit_blaster_tpl.h000066400000000000000000000173171356505360400234750ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: bit_blaster_tpl.h Abstract: Template for bit-blaster operations Author: Leonardo de Moura (leonardo) 2011-05-02. Revision History: --*/ #ifndef BIT_BLASTER_TPL_H_ #define BIT_BLASTER_TPL_H_ #include "util/rational.h" template class bit_blaster_tpl : public Cfg { public: typedef rational numeral; protected: template void mk_le(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref & out); template void mk_sdiv_srem_smod(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits); template void mk_ext_rotate_left_right(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits); unsigned long long m_max_memory; bool m_use_wtm; /* Wallace Tree Multiplier */ bool m_use_bcm; /* Booth Multiplier for constants */ void checkpoint(); public: bit_blaster_tpl(Cfg const & cfg = Cfg(), unsigned long long max_memory = UINT64_MAX, bool use_wtm = false, bool use_bcm=false): Cfg(cfg), m_max_memory(max_memory), m_use_wtm(use_wtm), m_use_bcm(use_bcm) { } void set_max_memory(unsigned long long max_memory) { m_max_memory = max_memory; } // Cfg required API ast_manager & m() const { return Cfg::m(); } numeral power(unsigned n) const { return Cfg::power(n); } void mk_xor(expr * a, expr * b, expr_ref & r) { Cfg::mk_xor(a, b, r); } void mk_xor3(expr * a, expr * b, expr * c, expr_ref & r) { Cfg::mk_xor3(a, b, c, r); } void mk_carry(expr * a, expr * b, expr * c, expr_ref & r) { Cfg::mk_carry(a, b, c, r); } void mk_iff(expr * a, expr * b, expr_ref & r) { Cfg::mk_iff(a, b, r); } void mk_and(expr * a, expr * b, expr_ref & r) { Cfg::mk_and(a, b, r); } void mk_and(expr * a, expr * b, expr * c, expr_ref & r) { Cfg::mk_and(a, b, c, r); } void mk_and(unsigned sz, expr * const * args, expr_ref & r) { Cfg::mk_and(sz, args, r); } void mk_or(expr * a, expr * b, expr_ref & r) { Cfg::mk_or(a, b, r); } void mk_or(expr * a, expr * b, expr * c, expr_ref & r) { Cfg::mk_or(a, b, c, r); } void mk_or(unsigned sz, expr * const * args, expr_ref & r) { Cfg::mk_or(sz, args, r); } void mk_not(expr * a, expr_ref & r) { Cfg::mk_not(a, r); } void mk_ite(expr * c, expr * t, expr * e, expr_ref & r) { Cfg::mk_ite(c, t, e, r); } void mk_nand(expr * a, expr * b, expr_ref & r) { Cfg::mk_nand(a, b, r); } void mk_nor(expr * a, expr * b, expr_ref & r) { Cfg::mk_nor(a, b, r); } // bool is_numeral(unsigned sz, expr * const * bits) const; bool is_numeral(unsigned sz, expr * const * bits, numeral & r) const; bool is_minus_one(unsigned sz, expr * const * bits) const; void num2bits(numeral const & v, unsigned sz, expr_ref_vector & out_bits) const; void mk_half_adder(expr * a, expr * b, expr_ref & out, expr_ref & cout); void mk_full_adder(expr * a, expr * b, expr * cin, expr_ref & out, expr_ref & cout); void mk_neg(unsigned sz, expr * const * a_bits, expr_ref_vector & out_bits); void mk_adder(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits); void mk_subtracter(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits, expr_ref & cout); void mk_multiplier(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits); void mk_udiv_urem(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & q_bits, expr_ref_vector & r_bits); void mk_udiv(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & q_bits); void mk_urem(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & r_bits); void mk_multiplexer(expr * c, unsigned sz, expr * const * t_bits, expr * const * e_bits, expr_ref_vector & out_bits); void mk_sdiv(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits); void mk_srem(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits); void mk_smod(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits); void mk_rotate_left(unsigned sz, expr * const * a_bits, unsigned n, expr_ref_vector & out_bits); void mk_rotate_right(unsigned sz, expr * const * a_bits, unsigned n, expr_ref_vector & out_bits); void mk_ext_rotate_left(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits); void mk_ext_rotate_right(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits); void mk_sign_extend(unsigned sz, expr * const * a_bits, unsigned n, expr_ref_vector & out_bits); void mk_zero_extend(unsigned sz, expr * const * a_bits, unsigned n, expr_ref_vector & out_bits); void mk_eq(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref & out); void mk_is_eq(unsigned sz, expr * const * a_bits, unsigned n, expr_ref & out); void mk_eqs(unsigned sz, expr * const * a_bits, expr_ref_vector & eqs); void mk_shl(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits); void mk_lshr(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits); void mk_ashr(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits); void mk_sle(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref & out); void mk_ule(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref & out); void mk_not(unsigned sz, expr * const * a_bits, expr_ref_vector & out_bits); void mk_and(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits); void mk_or(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits); void mk_xor(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits); void mk_xnor(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits); void mk_nand(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits); void mk_nor(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits); void mk_redand(unsigned sz, expr * const * a_bits, expr_ref_vector & out_bits); void mk_redor(unsigned sz, expr * const * a_bits, expr_ref_vector & out_bits); void mk_umul_no_overflow(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref & out); void mk_smul_no_overflow_core(unsigned sz, expr * const * a_bits, expr * const * b_bits, bool is_overflow, expr_ref & result); void mk_smul_no_overflow(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref & out); void mk_smul_no_underflow(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref & out); void mk_comp(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits); void mk_carry_save_adder(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr * const * c_bits, expr_ref_vector & sum_bits, expr_ref_vector & carry_bits); bool mk_const_multiplier(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits); bool mk_const_case_multiplier(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits); void mk_const_case_multiplier(bool is_a, unsigned i, unsigned sz, ptr_buffer& a_bits, ptr_buffer& b_bits, expr_ref_vector & out_bits); bool is_bool_const(expr* e) const { return m().is_true(e) || m().is_false(e); } void mk_abs(unsigned sz, expr * const * a_bits, expr_ref_vector & out_bits); }; #endif z3-z3-4.8.7/src/ast/rewriter/bit_blaster/bit_blaster_tpl_def.h000066400000000000000000001344461356505360400243160ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: bit_blaster_tpl_def.h Abstract: Template for bit-blaster operations Author: Leonardo de Moura (leonardo) 2011-05-02. Revision History: --*/ #include "ast/rewriter/bit_blaster/bit_blaster_tpl.h" #include "util/rational.h" #include "ast/ast_pp.h" #include "util/common_msgs.h" #include "ast/rewriter/rewriter_types.h" template void bit_blaster_tpl::checkpoint() { if (memory::get_allocation_size() > m_max_memory) throw rewriter_exception(Z3_MAX_MEMORY_MSG); if (m().canceled()) throw rewriter_exception(m().limit().get_cancel_msg()); } /** \brief Return true if all bits are true or false. */ template bool bit_blaster_tpl::is_numeral(unsigned sz, expr * const * bits) const { for (unsigned i = 0; i < sz; i++) if (!is_bool_const(bits[i])) return false; return true; } /** \brief Return true if all bits are true or false, and store the number represent by these bits in r. */ template bool bit_blaster_tpl::is_numeral(unsigned sz, expr * const * bits, numeral & r) const { r.reset(); for (unsigned i = 0; i < sz; i++) { if (m().is_true(bits[i])) r += power(i); else if (!m().is_false(bits[i])) return false; } return true; } /** \brief Return true if all bits are true. */ template bool bit_blaster_tpl::is_minus_one(unsigned sz, expr * const * bits) const { for (unsigned i = 0; i < sz; i++) if (!m().is_true(bits[i])) return false; return true; } // hack to avoid GCC compilation error. 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), base32(1ull << 32ull, rational::ui64()); for (unsigned i = 0; i < sz; i++) { 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()); aux = div(aux, two); } } template void bit_blaster_tpl::num2bits(numeral const & v, unsigned sz, expr_ref_vector & out_bits) const { _num2bits(m(), v, sz, out_bits); } template void bit_blaster_tpl::mk_half_adder(expr * a, expr * b, expr_ref & out, expr_ref & cout) { mk_xor(a, b, out); mk_and(a, b, cout); } template void bit_blaster_tpl::mk_full_adder(expr * a, expr * b, expr * cin, expr_ref & out, expr_ref & cout) { mk_xor3(a, b, cin, out); mk_carry(a, b, cin, cout); } template void bit_blaster_tpl::mk_neg(unsigned sz, expr * const * a_bits, expr_ref_vector & out_bits) { SASSERT(sz > 0); expr_ref cin(m()), cout(m()), out(m()); cin = m().mk_true(); for (unsigned idx = 0; idx < sz; idx++) { expr_ref not_a(m()); mk_not(a_bits[idx], not_a); if (idx < sz - 1) mk_half_adder(not_a, cin, out, cout); else mk_xor(not_a, cin, out); out_bits.push_back(out); cin = cout; } } template void bit_blaster_tpl::mk_adder(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits) { SASSERT(sz > 0); expr_ref cin(m()), cout(m()), out(m()); cin = m().mk_false(); for (unsigned idx = 0; idx < sz; idx++) { if (idx < sz - 1) mk_full_adder(a_bits[idx], b_bits[idx], cin, out, cout); else mk_xor3(a_bits[idx], b_bits[idx], cin, out); out_bits.push_back(out); cin = cout; } #if 0 static unsigned counter = 0; counter++; verbose_stream() << "MK_ADDER: " << counter << std::endl; #endif } template void bit_blaster_tpl::mk_subtracter(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits, expr_ref & cout) { SASSERT(sz > 0); expr_ref cin(m()), out(m()); cin = m().mk_true(); for (unsigned j = 0; j < sz; j++) { expr_ref not_b(m()); mk_not(b_bits[j], not_b); mk_full_adder(a_bits[j], not_b, cin, out, cout); out_bits.push_back(out); cin = cout; } SASSERT(out_bits.size() == sz); } template void bit_blaster_tpl::mk_multiplier(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits) { SASSERT(sz > 0); numeral n_a, n_b; out_bits.reset(); if (is_numeral(sz, a_bits, n_b)) std::swap(a_bits, b_bits); if (is_minus_one(sz, b_bits)) { mk_neg(sz, a_bits, out_bits); SASSERT(sz == out_bits.size()); return; } if (is_numeral(sz, a_bits, n_a)) { n_a *= n_b; num2bits(n_a, sz, out_bits); SASSERT(sz == out_bits.size()); return; } if (mk_const_multiplier(sz, a_bits, b_bits, out_bits)) { SASSERT(sz == out_bits.size()); return; } if (mk_const_multiplier(sz, b_bits, a_bits, out_bits)) { SASSERT(sz == out_bits.size()); return; } out_bits.reset(); if (!m_use_wtm) { #if 0 static unsigned counter = 0; counter++; verbose_stream() << "MK_MULTIPLIER: " << counter << std::endl; #endif expr_ref_vector cins(m()), couts(m()); expr_ref out(m()), cout(m()); mk_and(a_bits[0], b_bits[0], out); out_bits.push_back(out); /* out = a*b is encoded using the following circuit. a[0]&b[0] a[0]&b[1] a[0]&b[2] a[0]&b[3] ... | | | | | a[1]&b[0] - HA a[1]&b[1] - HA a[1]&b[2] - HA | | \ | \ | \ | | --------------- | -------------- | --- ... | | \| \ | | a[2]&b[0] - FA a[2]&b[1] - FA | | | \ | \ | | | -------------- | -- ... | | | \| | | | a[3]&b[0] - FA | | | | \ | | | | -- .... ... ... ... ... out[0] out[1] out[2] out[3] HA denotes a half-adder. FA denotes a full-adder. */ for (unsigned i = 1; i < sz; i++) { checkpoint(); couts.reset(); expr_ref i1(m()), i2(m()); mk_and(a_bits[0], b_bits[i], i1); mk_and(a_bits[1], b_bits[i-1], i2); if (i < sz - 1) { mk_half_adder(i1, i2, out, cout); couts.push_back(cout); for (unsigned j = 2; j <= i; j++) { expr_ref prev_out(m()); prev_out = out; expr_ref i3(m()); mk_and(a_bits[j], b_bits[i-j], i3); mk_full_adder(i3, prev_out, cins.get(j-2), out, cout); couts.push_back(cout); } out_bits.push_back(out); cins.swap(couts); } else { // last step --> I don't need to generate/store couts. mk_xor(i1, i2, out); for (unsigned j = 2; j <= i; j++) { expr_ref i3(m()); mk_and(a_bits[j], b_bits[i-j], i3); mk_xor3(i3, out, cins.get(j-2), out); } out_bits.push_back(out); } } } else { // WALLACE TREE MULTIPLIER if (sz == 1) { expr_ref t(m()); mk_and(a_bits[0], b_bits[0], t); out_bits.push_back(t); return; } // There are sz numbers to add and we use a Wallace tree to reduce that to two. // In this tree, we reduce as early as possible, as opposed to the Dada tree where some // additions may be delayed if they don't increase the propagation delay [which may be // a little bit more efficient, but it's tricky to find out which additions create // additional delays]. expr_ref zero(m()); zero = m().mk_false(); vector< expr_ref_vector > pps; pps.resize(sz, expr_ref_vector(m())); for (unsigned i = 0; i < sz; i++) { checkpoint(); // The partial product is a_bits AND b_bits[i] // [or alternatively ITE(b_bits[i], a_bits, bv0[sz])] expr_ref_vector & pp = pps[i]; expr_ref t(m()); for (unsigned j = 0; j < i; j++) pp.push_back(zero); // left shift by i bits for (unsigned j = 0; j < (sz - i); j++) { mk_and(a_bits[j], b_bits[i], t); pp.push_back(t); } SASSERT(pps[i].size() == sz); } while (pps.size() != 2) { unsigned save_inx = 0; unsigned i = 0; unsigned end = pps.size() - 3; for ( ; i <= end; i += 3) { checkpoint(); expr_ref_vector pp1(m()), pp2(m()), pp3(m()); pp1.swap(pps[i]); pp2.swap(pps[i+1]); pp3.swap(pps[i+2]); expr_ref_vector & sum_bits = pps[save_inx]; expr_ref_vector & carry_bits = pps[save_inx+1]; SASSERT(sum_bits.empty() && carry_bits.empty()); carry_bits.push_back(zero); mk_carry_save_adder(pp1.size(), pp1.c_ptr(), pp2.c_ptr(), pp3.c_ptr(), sum_bits, carry_bits); carry_bits.pop_back(); save_inx += 2; } if (i == pps.size()-2) { pps[save_inx++].swap(pps[i++]); pps[save_inx++].swap(pps[i++]); } else if (i == pps.size()-1) { pps[save_inx++].swap(pps[i++]); } SASSERT (save_inx < pps.size() && i == pps.size()); pps.shrink(save_inx); } SASSERT(pps.size() == 2); // Now there are only two numbers to add, we can use a ripple carry adder here. mk_adder(sz, pps[0].c_ptr(), pps[1].c_ptr(), out_bits); } } template void bit_blaster_tpl::mk_umul_no_overflow(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref & result) { SASSERT(sz > 0); expr_ref zero(m()); zero = m().mk_false(); ptr_buffer ext_a_bits; ptr_buffer ext_b_bits; ext_a_bits.append(sz, a_bits); ext_b_bits.append(sz, b_bits); ext_a_bits.push_back(zero); ext_b_bits.push_back(zero); SASSERT(ext_a_bits.size() == 1 + sz); SASSERT(ext_b_bits.size() == 1 + sz); expr_ref_vector mult_cout(m()); // // mk_multiplier will simplify output taking into account that // the most significant bits of ext_a_bits and ext_b_bits are zero. // mk_multiplier(1 + sz, ext_a_bits.c_ptr(), ext_b_bits.c_ptr(), mult_cout); expr_ref overflow1(m()), overflow2(m()), overflow(m()); // // ignore bits [0, sz-1] of mult_cout // overflow1 = mult_cout[sz].get(); expr_ref ovf(m()), v(m()), tmp(m()); ovf = m().mk_false(); v = m().mk_false(); for (unsigned i = 1; i < sz; ++i) { mk_or(ovf, a_bits[sz-i], ovf); mk_and(ovf, b_bits[i], tmp); mk_or(tmp, v, v); } overflow2 = v; mk_or(overflow1, overflow2, overflow); mk_not(overflow, result); } template void bit_blaster_tpl::mk_smul_no_overflow_core(unsigned sz, expr * const * a_bits, expr * const * b_bits, bool is_overflow, expr_ref & result) { SASSERT(sz > 0); expr_ref zero(m()); zero = m().mk_false(); ptr_buffer ext_a_bits; ptr_buffer ext_b_bits; ext_a_bits.append(sz, a_bits); ext_b_bits.append(sz, b_bits); ext_a_bits.push_back(a_bits[sz-1]); ext_b_bits.push_back(b_bits[sz-1]); SASSERT(ext_a_bits.size() == 1 + sz); SASSERT(ext_b_bits.size() == 1 + sz); expr_ref_vector mult_cout(m()); mk_multiplier(1 + sz, ext_a_bits.c_ptr(), ext_b_bits.c_ptr(), mult_cout); expr_ref overflow1(m()), overflow2(m()), overflow(m()); // // The two most significant bits are different. // mk_xor(mult_cout[sz].get(), mult_cout[sz-1].get(), overflow1); // // let // a_i = a[sz-1] xor a[i] // b_i = b[sz-1] xor b[i] // a_acc_i = a_{sz-2} or ... or a_{sz-1-i} // b = (a_acc_1 and b_1) or (a_acc_2 and b_2) or ... or (a_acc_{n-2} and b_{n-2}) // expr_ref v(m()), tmp(m()), a(m()), b(m()), a_acc(m()), sign(m()); a_acc = m().mk_false(); v = m().mk_false(); for (unsigned i = 1; i + 1 < sz; ++i) { mk_xor(b_bits[sz-1], b_bits[i], b); mk_xor(a_bits[sz-1], a_bits[sz-1-i], a); mk_or(a, a_acc, a_acc); mk_and(a_acc, b, tmp); mk_or(tmp, v, v); } overflow2 = v; mk_or(overflow1, overflow2, overflow); if (is_overflow) { // check for proper overflow // can only happen when the sign bits are the same. mk_iff(a_bits[sz-1], b_bits[sz-1], sign); } else { // check for proper underflow // can only happen when the sign bits are different. mk_xor(a_bits[sz-1], b_bits[sz-1], sign); } mk_and(sign, overflow, overflow); mk_not(overflow, result); } template void bit_blaster_tpl::mk_smul_no_overflow(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref & result) { mk_smul_no_overflow_core(sz, a_bits, b_bits, true, result); } template void bit_blaster_tpl::mk_smul_no_underflow(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref & result) { mk_smul_no_overflow_core(sz, a_bits, b_bits, false, result); } template void bit_blaster_tpl::mk_udiv_urem(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & q_bits, expr_ref_vector & r_bits) { SASSERT(sz > 0); // p is the residual of each stage of the division. expr_ref_vector & p = r_bits; // t is an auxiliary vector used to store the result of a subtraction expr_ref_vector t(m()); // init p p.push_back(a_bits[sz-1]); for (unsigned i = 1; i < sz; i++) p.push_back(m().mk_false()); q_bits.resize(sz); for (unsigned i = 0; i < sz; i++) { checkpoint(); // generate p - b expr_ref q(m()); t.reset(); mk_subtracter(sz, p.c_ptr(), b_bits, t, q); q_bits.set(sz - i - 1, q); // update p if (i < sz - 1) { for (unsigned j = sz - 1; j > 0; j--) { expr_ref ie(m()); mk_ite(q, t.get(j-1), p.get(j-1), ie); p.set(j, ie); } p.set(0, a_bits[sz - i - 2]); } else { // last step: p contains the remainder for (unsigned j = 0; j < sz; j++) { expr_ref ie(m()); mk_ite(q, t.get(j), p.get(j), ie); p.set(j, ie); } } } DEBUG_CODE({ for (unsigned i = 0; i < sz; i++) { SASSERT(q_bits.get(i) != 0); }}); TRACE("bit_blaster", tout << "a: "; for (unsigned i = 0; i < sz; ++i) tout << mk_pp(a_bits[i], m()) << " "; tout << "\nb: "; for (unsigned i = 0; i < sz; ++i) tout << mk_pp(b_bits[i], m()) << " "; tout << "\nq: "; for (unsigned i = 0; i < sz; ++i) tout << mk_pp(q_bits[i].get(), m()) << " "; tout << "\nr: "; for (unsigned i = 0; i < sz; ++i) tout << mk_pp(r_bits[i].get(), m()) << " "; tout << "\n"; ); } template void bit_blaster_tpl::mk_udiv(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & q_bits) { expr_ref_vector aux(m()); mk_udiv_urem(sz, a_bits, b_bits, q_bits, aux); } template void bit_blaster_tpl::mk_urem(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & r_bits) { expr_ref_vector aux(m()); mk_udiv_urem(sz, a_bits, b_bits, aux, r_bits); } template void bit_blaster_tpl::mk_multiplexer(expr * c, unsigned sz, expr * const * t_bits, expr * const * e_bits, expr_ref_vector & out_bits) { for (unsigned i = 0; i < sz; i++) { expr_ref t(m()); mk_ite(c, t_bits[i], e_bits[i], t); out_bits.push_back(t); } } template void bit_blaster_tpl::mk_abs(unsigned sz, expr * const * a_bits, expr_ref_vector & out_bits) { expr * a_msb = a_bits[sz - 1]; if (m().is_false(a_msb)) { out_bits.append(sz, a_bits); } else if (m().is_true(a_msb)) { mk_neg(sz, a_bits, out_bits); } else { expr_ref_vector neg_a_bits(m()); mk_neg(sz, a_bits, neg_a_bits); mk_multiplexer(a_msb, sz, neg_a_bits.c_ptr(), a_bits, out_bits); } } #define SDIV 0 #define SREM 1 #define SMOD 2 template template void bit_blaster_tpl::mk_sdiv_srem_smod(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits) { // This definition is only good when the most significant bits are set. // Otherwise, it will create 4 copies of the expensive sdiv/srem/smod SASSERT(k == SDIV || k == SREM || k == SMOD); expr * a_msb = a_bits[sz - 1]; expr * b_msb = b_bits[sz - 1]; expr_ref_vector neg_a_bits(m()); expr_ref_vector neg_b_bits(m()); mk_neg(sz, a_bits, neg_a_bits); mk_neg(sz, b_bits, neg_b_bits); expr_ref_vector pp_q(m()), pp_r(m()), pn_q(m()), pn_r(m()), np_q(m()), np_r(m()), nn_q(m()), nn_r(m()); if (!m().is_true(a_msb) && !m().is_true(b_msb)) { mk_udiv_urem(sz, a_bits, b_bits, pp_q, pp_r); } else { pp_q.resize(sz, m().mk_false()); pp_r.resize(sz, m().mk_false()); } if (!m().is_false(a_msb) && !m().is_true(b_msb)) { mk_udiv_urem(sz, neg_a_bits.c_ptr(), b_bits, np_q, np_r); } else { np_q.resize(sz, m().mk_false()); np_r.resize(sz, m().mk_false()); } if (!m().is_true(a_msb) && !m().is_false(b_msb)) { mk_udiv_urem(sz, a_bits, neg_b_bits.c_ptr(), pn_q, pn_r); } else { pn_q.resize(sz, m().mk_false()); pn_r.resize(sz, m().mk_false()); } if (!m().is_false(a_msb) && !m().is_false(b_msb)) { mk_udiv_urem(sz, neg_a_bits.c_ptr(), neg_b_bits.c_ptr(), nn_q, nn_r); } else { nn_q.resize(sz, m().mk_false()); nn_r.resize(sz, m().mk_false()); } expr_ref_vector ite1(m()), ite2(m()); if (k == SDIV) { expr_ref_vector & pp_out = pp_q; expr_ref_vector np_out(m()); expr_ref_vector pn_out(m()); expr_ref_vector & nn_out = nn_q; if (!m().is_false(a_msb) && !m().is_true(b_msb)) mk_neg(sz, np_q.c_ptr(), np_out); else np_out.resize(sz, m().mk_false()); if (!m().is_true(a_msb) && !m().is_false(b_msb)) mk_neg(sz, pn_q.c_ptr(), pn_out); else pn_out.resize(sz, m().mk_false()); #define MK_MULTIPLEXER() \ mk_multiplexer(b_msb, sz, nn_out.c_ptr(), np_out.c_ptr(), ite1); \ mk_multiplexer(b_msb, sz, pn_out.c_ptr(), pp_out.c_ptr(), ite2); \ mk_multiplexer(a_msb, sz, ite1.c_ptr(), ite2.c_ptr(), out_bits) MK_MULTIPLEXER(); } else if (k == SREM) { expr_ref_vector & pp_out = pp_r; expr_ref_vector np_out(m()); expr_ref_vector & pn_out = pn_r; expr_ref_vector nn_out(m()); if (!m().is_false(a_msb) && !m().is_true(b_msb)) mk_neg(sz, np_r.c_ptr(), np_out); else np_out.resize(sz, m().mk_false()); if (!m().is_false(a_msb) && !m().is_false(b_msb)) mk_neg(sz, nn_r.c_ptr(), nn_out); else nn_out.resize(sz, m().mk_false()); MK_MULTIPLEXER(); } else { SASSERT(k == SMOD); expr_ref_vector & pp_out = pp_r; expr_ref_vector np_out(m()); expr_ref_vector pn_out(m()); expr_ref_vector nn_out(m()); if (!m().is_false(a_msb) && !m().is_true(b_msb)) { expr_ref cout(m()); mk_subtracter(sz, b_bits, np_r.c_ptr(), np_out, cout); } else np_out.resize(sz, m().mk_false()); if (!m().is_true(a_msb) && !m().is_false(b_msb)) mk_adder(sz, b_bits, pn_r.c_ptr(), pn_out); else pn_out.resize(sz, m().mk_false()); if (!m().is_false(a_msb) && !m().is_false(b_msb)) mk_neg(sz, nn_r.c_ptr(), nn_out); else nn_out.resize(sz, m().mk_false()); MK_MULTIPLEXER(); } } template void bit_blaster_tpl::mk_sdiv(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits) { expr * a_msb = a_bits[sz - 1]; expr * b_msb = b_bits[sz - 1]; if (m().is_false(a_msb) && m().is_false(b_msb)) { mk_udiv(sz, a_bits, b_bits, out_bits); } else if (m().is_false(a_msb) && m().is_true(b_msb)) { expr_ref_vector neg_b_bits(m()); mk_neg(sz, b_bits, neg_b_bits); expr_ref_vector tmp(m()); mk_udiv(sz, a_bits, neg_b_bits.c_ptr(), tmp); mk_neg(sz, tmp.c_ptr(), out_bits); } else if (m().is_true(a_msb) && m().is_false(b_msb)) { expr_ref_vector neg_a_bits(m()); mk_neg(sz, a_bits, neg_a_bits); expr_ref_vector tmp(m()); mk_udiv(sz, neg_a_bits.c_ptr(), b_bits, tmp); mk_neg(sz, tmp.c_ptr(), out_bits); } else if (m().is_true(a_msb) && m().is_true(b_msb)) { expr_ref_vector neg_a_bits(m()); mk_neg(sz, a_bits, neg_a_bits); expr_ref_vector neg_b_bits(m()); mk_neg(sz, b_bits, neg_b_bits); mk_udiv(sz, neg_a_bits.c_ptr(), neg_b_bits.c_ptr(), out_bits); } else { #if 0 // creates 4 dividers mk_sdiv_srem_smod(sz, a_bits, b_bits, out_bits); #else // creates only 1 expr_ref_vector abs_a_bits(m()); expr_ref_vector abs_b_bits(m()); mk_abs(sz, a_bits, abs_a_bits); mk_abs(sz, b_bits, abs_b_bits); expr_ref_vector udiv_bits(m()); mk_udiv(sz, abs_a_bits.c_ptr(), abs_b_bits.c_ptr(), udiv_bits); expr_ref_vector neg_udiv_bits(m()); mk_neg(sz, udiv_bits.c_ptr(), neg_udiv_bits); expr_ref c(m()); mk_iff(a_msb, b_msb, c); mk_multiplexer(c, sz, udiv_bits.c_ptr(), neg_udiv_bits.c_ptr(), out_bits); #endif } } template void bit_blaster_tpl::mk_srem(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits) { expr * a_msb = a_bits[sz - 1]; expr * b_msb = b_bits[sz - 1]; if (m().is_false(a_msb) && m().is_false(b_msb)) { mk_urem(sz, a_bits, b_bits, out_bits); } else if (m().is_false(a_msb) && m().is_true(b_msb)) { expr_ref_vector neg_b_bits(m()); mk_neg(sz, b_bits, neg_b_bits); mk_urem(sz, a_bits, neg_b_bits.c_ptr(), out_bits); } else if (m().is_true(a_msb) && m().is_false(b_msb)) { expr_ref_vector neg_a_bits(m()); mk_neg(sz, a_bits, neg_a_bits); expr_ref_vector tmp(m()); mk_urem(sz, neg_a_bits.c_ptr(), b_bits, tmp); mk_neg(sz, tmp.c_ptr(), out_bits); } else if (m().is_true(a_msb) && m().is_true(b_msb)) { expr_ref_vector neg_a_bits(m()); mk_neg(sz, a_bits, neg_a_bits); expr_ref_vector neg_b_bits(m()); mk_neg(sz, b_bits, neg_b_bits); expr_ref_vector tmp(m()); mk_urem(sz, neg_a_bits.c_ptr(), neg_b_bits.c_ptr(), tmp); mk_neg(sz, tmp.c_ptr(), out_bits); } else { #if 0 // creates 4 urem mk_sdiv_srem_smod(sz, a_bits, b_bits, out_bits); #else // creates only 1 expr_ref_vector abs_a_bits(m()); expr_ref_vector abs_b_bits(m()); mk_abs(sz, a_bits, abs_a_bits); mk_abs(sz, b_bits, abs_b_bits); expr_ref_vector urem_bits(m()); numeral n_b; unsigned shift; // a urem 2^n -> a & ((2^n)-1) if (is_numeral(sz, abs_b_bits.c_ptr(), n_b) && n_b.is_power_of_two(shift)) { mk_zero_extend(shift, abs_a_bits.c_ptr(), sz - shift, urem_bits); } else { mk_urem(sz, abs_a_bits.c_ptr(), abs_b_bits.c_ptr(), urem_bits); } expr_ref_vector neg_urem_bits(m()); mk_neg(sz, urem_bits.c_ptr(), neg_urem_bits); mk_multiplexer(a_msb, sz, neg_urem_bits.c_ptr(), urem_bits.c_ptr(), out_bits); #endif } } /** \brief Generate circuit for signed mod. This function implements the semantics of bvsmod given below for two bits: (define-fun bvsmod_def ((s (_ BitVec 2)) (t (_ BitVec 2))) (_ BitVec 2) (let ((msb_s ((_ extract 1 1) s)) (msb_t ((_ extract 1 1) t))) (let ((abs_s (ite (= msb_s #b0) s (bvneg s))) (abs_t (ite (= msb_t #b0) t (bvneg t)))) (let ((u (bvurem abs_s abs_t))) (ite (= u (_ bv0 2)) u (ite (and (= msb_s #b0) (= msb_t #b0)) u (ite (and (= msb_s #b1) (= msb_t #b0)) (bvadd (bvneg u) t) (ite (and (= msb_s #b0) (= msb_t #b1)) (bvadd u t) (bvneg u))))))))) Note: The semantics is sensitive to the order of these tests. It is unsound to test first for whether the most significant bits of s and t are known and use the cases for those. If u is 0 then the result is 0. */ template void bit_blaster_tpl::mk_smod(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits) { expr * a_msb = a_bits[sz - 1]; expr * b_msb = b_bits[sz - 1]; expr_ref_vector abs_a_bits(m()); expr_ref_vector abs_b_bits(m()); mk_abs(sz, a_bits, abs_a_bits); mk_abs(sz, b_bits, abs_b_bits); expr_ref_vector u_bits(m()); mk_urem(sz, abs_a_bits.c_ptr(), abs_b_bits.c_ptr(), u_bits); expr_ref_vector neg_u_bits(m()); mk_neg(sz, u_bits.c_ptr(), neg_u_bits); expr_ref_vector neg_u_add_b(m()); mk_adder(sz, neg_u_bits.c_ptr(), b_bits, neg_u_add_b); expr_ref_vector u_add_b(m()); mk_adder(sz, u_bits.c_ptr(), b_bits, u_add_b); expr_ref_vector zero(m()); num2bits(numeral(0), sz, zero); expr_ref u_eq_0(m()); mk_eq(sz, u_bits.c_ptr(), zero.c_ptr(), u_eq_0); expr_ref_vector & pp_bits = u_bits; // pos & pos case expr_ref_vector & pn_bits = u_add_b; // pos & neg case expr_ref_vector & np_bits = neg_u_add_b; // neg & pos case expr_ref_vector & nn_bits = neg_u_bits; // neg & neg case expr_ref_vector ite1(m()); expr_ref_vector ite2(m()); expr_ref_vector body(m()); mk_multiplexer(b_msb, sz, nn_bits.c_ptr(), np_bits.c_ptr(), ite1); mk_multiplexer(b_msb, sz, pn_bits.c_ptr(), pp_bits.c_ptr(), ite2); mk_multiplexer(a_msb, sz, ite1.c_ptr(), ite2.c_ptr(), body); mk_multiplexer(u_eq_0, sz, u_bits.c_ptr(), body.c_ptr(), out_bits); } template void bit_blaster_tpl::mk_eq(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref & out) { expr_ref_vector out_bits(m()); for (unsigned i = 0; i < sz; i++) { mk_iff(a_bits[i], b_bits[i], out); out_bits.push_back(out); } mk_and(out_bits.size(), out_bits.c_ptr(), out); } template void bit_blaster_tpl::mk_rotate_left(unsigned sz, expr * const * a_bits, unsigned n, expr_ref_vector & out_bits) { TRACE("bit_blaster", tout << n << ": " << sz << " "; for (unsigned i = 0; i < sz; ++i) { tout << mk_pp(a_bits[i], m()) << " "; } tout << "\n"; ); n = n % sz; for (unsigned i = sz - n; i < sz; i++) out_bits.push_back(a_bits[i]); for (unsigned i = 0 ; i < sz - n; i++) out_bits.push_back(a_bits[i]); } template void bit_blaster_tpl::mk_rotate_right(unsigned sz, expr * const * a_bits, unsigned n, expr_ref_vector & out_bits) { n = n % sz; mk_rotate_left(sz, a_bits, sz - n, out_bits); } template void bit_blaster_tpl::mk_sign_extend(unsigned sz, expr * const * a_bits, unsigned n, expr_ref_vector & out_bits) { for (unsigned i = 0; i < sz; i++) out_bits.push_back(a_bits[i]); expr * high_bit = a_bits[sz - 1]; for (unsigned i = sz; i < sz + n; i++) out_bits.push_back(high_bit); } template void bit_blaster_tpl::mk_zero_extend(unsigned sz, expr * const * a_bits, unsigned n, expr_ref_vector & out_bits) { for (unsigned i = 0; i < sz; i++) out_bits.push_back(a_bits[i]); expr * high_bit = m().mk_false(); for (unsigned i = sz; i < sz + n; i++) out_bits.push_back(high_bit); } /** \brief Return an expression that is true iff a_bits represents the number n. */ template void bit_blaster_tpl::mk_is_eq(unsigned sz, expr * const * a_bits, unsigned n, expr_ref & out) { numeral two(2); expr_ref_vector out_bits(m()); for (unsigned i = 0; i < sz; i++) { if (n % 2 == 0) { expr_ref not_a(m()); mk_not(a_bits[i], not_a); out_bits.push_back(not_a); } else { out_bits.push_back(a_bits[i]); } n = n / 2; } mk_and(out_bits.size(), out_bits.c_ptr(), out); } /** \brief Store in eqs the equalities a_bits = 0, a_bits = 1, ..., a_bits = sz -1. */ template void bit_blaster_tpl::mk_eqs(unsigned sz, expr * const * a_bits, expr_ref_vector & eqs) { for (unsigned i = 0; i < sz; i++) { expr_ref eq(m()); mk_is_eq(sz, a_bits, i, eq); eqs.push_back(eq); } } template void bit_blaster_tpl::mk_shl(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits) { numeral k; if (is_numeral(sz, b_bits, k)) { if (k > numeral(sz)) k = numeral(sz); unsigned n = static_cast(k.get_int64()); if (n >= sz) n = sz; unsigned pos; for (pos = 0; pos < n; pos++) out_bits.push_back(m().mk_false()); for (unsigned i = 0; pos < sz; pos++, i++) out_bits.push_back(a_bits[i]); } else { out_bits.append(sz, a_bits); unsigned i = 0; expr_ref_vector new_out_bits(m()); for (; i < sz; ++i) { checkpoint(); unsigned shift_i = 1 << i; if (shift_i >= sz) break; for (unsigned j = 0; j < sz; ++j) { expr_ref new_out(m()); expr* a_j = m().mk_false(); if (shift_i <= j) a_j = out_bits[j-shift_i].get(); mk_ite(b_bits[i], a_j, out_bits[j].get(), new_out); new_out_bits.push_back(new_out); } out_bits.reset(); out_bits.append(new_out_bits); new_out_bits.reset(); } expr_ref is_large(m()); is_large = m().mk_false(); for (; i < sz; ++i) { mk_or(is_large, b_bits[i], is_large); } for (unsigned j = 0; j < sz; ++j) { expr_ref new_out(m()); mk_ite(is_large, m().mk_false(), out_bits[j].get(), new_out); out_bits[j] = new_out; } } } template void bit_blaster_tpl::mk_lshr(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits) { numeral k; if (is_numeral(sz, b_bits, k)) { if (k > numeral(sz)) k = numeral(sz); unsigned n = static_cast(k.get_int64()); unsigned pos = 0; for (unsigned i = n; i < sz; pos++, i++) out_bits.push_back(a_bits[i]); for (; pos < sz; pos++) out_bits.push_back(m().mk_false()); } else { out_bits.append(sz, a_bits); unsigned i = 0; for (; i < sz; ++i) { checkpoint(); expr_ref_vector new_out_bits(m()); unsigned shift_i = 1 << i; if (shift_i >= sz) break; for (unsigned j = 0; j < sz; ++j) { expr_ref new_out(m()); expr* a_j = m().mk_false(); if (shift_i + j < sz) a_j = out_bits[j+shift_i].get(); mk_ite(b_bits[i], a_j, out_bits[j].get(), new_out); new_out_bits.push_back(new_out); } out_bits.reset(); out_bits.append(new_out_bits); } expr_ref is_large(m()); is_large = m().mk_false(); for (; i < sz; ++i) { mk_or(is_large, b_bits[i], is_large); } for (unsigned j = 0; j < sz; ++j) { expr_ref new_out(m()); mk_ite(is_large, m().mk_false(), out_bits[j].get(), new_out); out_bits[j] = new_out; } } } template void bit_blaster_tpl::mk_ashr(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits) { numeral k; if (is_numeral(sz, b_bits, k)) { if (k > numeral(sz)) k = numeral(sz); unsigned n = static_cast(k.get_int64()); unsigned pos = 0; for (unsigned i = n; i < sz; pos++, i++) out_bits.push_back(a_bits[i]); for (; pos < sz; pos++) out_bits.push_back(a_bits[sz-1]); } else { out_bits.append(sz, a_bits); unsigned i = 0; for (; i < sz; ++i) { checkpoint(); expr_ref_vector new_out_bits(m()); unsigned shift_i = 1 << i; if (shift_i >= sz) break; for (unsigned j = 0; j < sz; ++j) { expr_ref new_out(m()); expr* a_j = a_bits[sz-1]; if (shift_i + j < sz) a_j = out_bits[j+shift_i].get(); mk_ite(b_bits[i], a_j, out_bits[j].get(), new_out); new_out_bits.push_back(new_out); } out_bits.reset(); out_bits.append(new_out_bits); } expr_ref is_large(m()); is_large = m().mk_false(); for (; i < sz; ++i) { mk_or(is_large, b_bits[i], is_large); } for (unsigned j = 0; j < sz; ++j) { expr_ref new_out(m()); mk_ite(is_large, a_bits[sz-1], out_bits[j].get(), new_out); out_bits[j] = new_out; } } } template template void bit_blaster_tpl::mk_ext_rotate_left_right(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits) { numeral k; if (is_numeral(sz, b_bits, k) && k.is_unsigned()) { if (Left) mk_rotate_left(sz, a_bits, static_cast(k.get_uint64()), out_bits); else mk_rotate_right(sz, a_bits, static_cast(k.get_uint64()), out_bits); } else { // // Review: a better tuned implementation is possible by using shifts by power of two. // e.g., looping over the bits of b_bits, then rotate by a power of two depending // on the bit-position. This would get rid of the mk_urem. // expr_ref_vector sz_bits(m()); expr_ref_vector masked_b_bits(m()); expr_ref_vector eqs(m()); numeral sz_numeral(sz); num2bits(sz_numeral, sz, sz_bits); mk_urem(sz, b_bits, sz_bits.c_ptr(), masked_b_bits); mk_eqs(sz, masked_b_bits.c_ptr(), eqs); for (unsigned i = 0; i < sz; i++) { checkpoint(); expr_ref out(m()); out = a_bits[i]; for (unsigned j = 1; j < sz; j++) { expr_ref new_out(m()); unsigned src = (Left ? (sz + i - j) : (i + j)) % sz; mk_ite(eqs.get(j), a_bits[src], out, new_out); out = new_out; } out_bits.push_back(out); } } } template void bit_blaster_tpl::mk_ext_rotate_left(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits) { mk_ext_rotate_left_right(sz, a_bits, b_bits, out_bits); } template void bit_blaster_tpl::mk_ext_rotate_right(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits) { mk_ext_rotate_left_right(sz, a_bits, b_bits, out_bits); } template template void bit_blaster_tpl::mk_le(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref & out) { SASSERT(sz > 0); expr_ref i1(m()), i2(m()), i3(m()), not_a(m()); mk_not(a_bits[0], not_a); mk_or(not_a, b_bits[0], out); for (unsigned idx = 1; idx < (Signed ? sz - 1 : sz); idx++) { mk_not(a_bits[idx], not_a); mk_and(not_a, b_bits[idx], i1); mk_and(not_a, out, i2); mk_and(b_bits[idx], out, i3); mk_or(i1, i2, i3, out); } if (Signed) { expr_ref not_b(m()); mk_not(b_bits[sz-1], not_b); mk_and(not_b, a_bits[sz-1], i1); mk_and(not_b, out, i2); mk_and(a_bits[sz-1], out, i3); mk_or(i1, i2, i3, out); } } template void bit_blaster_tpl::mk_sle(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref & out) { mk_le(sz, a_bits, b_bits, out); } template void bit_blaster_tpl::mk_ule(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref & out) { mk_le(sz, a_bits, b_bits, out); } template void bit_blaster_tpl::mk_not(unsigned sz, expr * const * a_bits, expr_ref_vector & out_bits) { for (unsigned i = 0; i < sz; i++) { expr_ref t(m()); mk_not(a_bits[i], t); out_bits.push_back(t); } } #define MK_BINARY(NAME, OP) \ template \ void bit_blaster_tpl::NAME(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits) { \ for (unsigned i = 0; i < sz; i++) { \ expr_ref t(m()); \ OP(a_bits[i], b_bits[i], t); \ out_bits.push_back(t); \ } \ } MK_BINARY(mk_and, mk_and); MK_BINARY(mk_or, mk_or); MK_BINARY(mk_xor, mk_xor); MK_BINARY(mk_xnor, mk_iff); MK_BINARY(mk_nand, mk_nand); MK_BINARY(mk_nor, mk_nor); template void bit_blaster_tpl::mk_redand(unsigned sz, expr * const * a_bits, expr_ref_vector & out_bits) { expr_ref tmp(m()); mk_and(sz, a_bits, tmp); out_bits.push_back(tmp); } template void bit_blaster_tpl::mk_redor(unsigned sz, expr * const * a_bits, expr_ref_vector & out_bits) { expr_ref tmp(m()); mk_or(sz, a_bits, tmp); out_bits.push_back(tmp); } template void bit_blaster_tpl::mk_comp(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits) { expr_ref tmp(m()); mk_eq(sz, a_bits, b_bits, tmp); out_bits.push_back(tmp); } template void bit_blaster_tpl::mk_carry_save_adder(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr * const * c_bits, expr_ref_vector & sum_bits, expr_ref_vector & carry_bits) { expr_ref t(m()); for (unsigned i = 0; i < sz; i++) { mk_xor3(a_bits[i], b_bits[i], c_bits[i], t); sum_bits.push_back(t); mk_carry(a_bits[i], b_bits[i], c_bits[i], t); carry_bits.push_back(t); } } template bool bit_blaster_tpl::mk_const_case_multiplier(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits) { unsigned case_size = 1; unsigned circuit_size = sz*sz*5; for (unsigned i = 0; case_size < circuit_size && i < sz; ++i) { if (!is_bool_const(a_bits[i])) { case_size *= 2; } if (!is_bool_const(b_bits[i])) { case_size *= 2; } } if (case_size >= circuit_size) { return false; } SASSERT(out_bits.empty()); ptr_buffer na_bits; na_bits.append(sz, a_bits); ptr_buffer nb_bits; nb_bits.append(sz, b_bits); mk_const_case_multiplier(true, 0, sz, na_bits, nb_bits, out_bits); return false; } template void bit_blaster_tpl::mk_const_case_multiplier(bool is_a, unsigned i, unsigned sz, ptr_buffer& a_bits, ptr_buffer& b_bits, expr_ref_vector & out_bits) { while (is_a && i < sz && is_bool_const(a_bits[i])) ++i; if (is_a && i == sz) { is_a = false; i = 0; } while (!is_a && i < sz && is_bool_const(b_bits[i])) ++i; if (i < sz) { expr_ref_vector out1(m()), out2(m()); expr_ref x(m()); x = is_a?a_bits[i]:b_bits[i]; if (is_a) a_bits[i] = m().mk_true(); else b_bits[i] = m().mk_true(); mk_const_case_multiplier(is_a, i+1, sz, a_bits, b_bits, out1); if (is_a) a_bits[i] = m().mk_false(); else b_bits[i] = m().mk_false(); mk_const_case_multiplier(is_a, i+1, sz, a_bits, b_bits, out2); if (is_a) a_bits[i] = x; else b_bits[i] = x; SASSERT(out_bits.empty()); for (unsigned j = 0; j < sz; ++j) { out_bits.push_back(m().mk_ite(x, out1[j].get(), out2[j].get())); } } else { numeral n_a, n_b; SASSERT(i == sz && !is_a); VERIFY(is_numeral(sz, a_bits.c_ptr(), n_a)); VERIFY(is_numeral(sz, b_bits.c_ptr(), n_b)); n_a *= n_b; num2bits(n_a, sz, out_bits); } SASSERT(out_bits.size() == sz); } template bool bit_blaster_tpl::mk_const_multiplier(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits) { numeral n_a; if (!is_numeral(sz, a_bits, n_a)) { return false; } SASSERT(out_bits.empty()); if (mk_const_case_multiplier(sz, a_bits, b_bits, out_bits)) { SASSERT(sz == out_bits.size()); return true; } out_bits.reset(); if (!m_use_bcm) { return false; } expr_ref_vector minus_b_bits(m()), tmp(m()); mk_neg(sz, b_bits, minus_b_bits); out_bits.resize(sz, m().mk_false()); #if 1 bool last = false, now; for (unsigned i = 0; i < sz; i++) { now = m().is_true(a_bits[i]); SASSERT(now || m().is_false(a_bits[i])); tmp.reset(); if (now && !last) { mk_adder(sz - i, out_bits.c_ptr() + i, minus_b_bits.c_ptr(), tmp); for (unsigned j = 0; j < (sz - i); j++) out_bits.set(i+j, tmp.get(j)); // do not use [], it does not work on Linux. } else if (!now && last) { mk_adder(sz - i, out_bits.c_ptr() + i, b_bits, tmp); for (unsigned j = 0; j < (sz - i); j++) out_bits.set(i+j, tmp.get(j)); // do not use [], it does not work on Linux. } last = now; } #else // Radix 4 Booth encoder // B = b_bits, -B = minus_b_bits // 2B = b2_bits, -2B = minus_b2_bits expr_ref_vector b2_bits(m()); expr_ref_vector minus_b2_bits(m()); b2_bits.push_back(m().mk_false()); minus_b2_bits.push_back(m().mk_false()); for (unsigned i = 0; i < sz-1; i++) { b2_bits.push_back(b_bits[i]); minus_b2_bits.push_back(minus_b_bits.get(i)); } bool last=false, now1, now2; for (unsigned i = 0; i < sz; i += 2) { now1 = m().is_true(a_bits[i]); now2 = m().is_true(a_bits[i+1]); SASSERT(now1 || m().is_false(a_bits[i])); SASSERT(now2 || m().is_false(a_bits[i+1])); tmp.reset(); if ((!now2 && !now1 && last) || (!now2 && now1 && !last)) { // Add B mk_adder(sz - i, out_bits.c_ptr() + i, b_bits, tmp); for (unsigned j = 0; j < (sz - i); j++) out_bits.set(i+j, tmp.get(j)); } else if (!now2 && now1 && last) { // Add 2B mk_adder(sz - i, out_bits.c_ptr() + i, b2_bits.c_ptr(), tmp); for (unsigned j = 0; j < (sz - i); j++) out_bits.set(i+j, tmp.get(j)); } else if (now2 && !now1 && !last) { // Add -2B mk_adder(sz - i, out_bits.c_ptr() + i, minus_b2_bits.c_ptr(), tmp); for (unsigned j = 0; j < (sz - i); j++) out_bits.set(i+j, tmp.get(j)); } else if ((now2 && !now1 && last) || (now2 && now1 && !last)) { // Add -B mk_adder(sz - i, out_bits.c_ptr() + i, minus_b_bits.c_ptr(), tmp); for (unsigned j = 0; j < (sz - i); j++) out_bits.set(i+j, tmp.get(j)); } last = now2; } #endif TRACE("bit_blaster_tpl_booth", for (unsigned i=0; i void bool_rewriter::updt_params(params_ref const & _p) { bool_rewriter_params p(_p); m_flat = p.flat(); m_elim_and = p.elim_and(); m_elim_ite = p.elim_ite(); m_local_ctx = p.local_ctx(); m_local_ctx_limit = p.local_ctx_limit(); m_blast_distinct = p.blast_distinct(); m_blast_distinct_threshold = p.blast_distinct_threshold(); m_ite_extra_rules = p.ite_extra_rules(); } void bool_rewriter::get_param_descrs(param_descrs & r) { bool_rewriter_params::collect_param_descrs(r); } br_status bool_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { SASSERT(f->get_family_id() == m().get_basic_family_id()); switch (f->get_decl_kind()) { case OP_EQ: SASSERT(num_args == 2); return mk_eq_core(args[0], args[1], result); case OP_DISTINCT: return mk_distinct_core(num_args, args, result); case OP_AND: return mk_and_core(num_args, args, result); case OP_OR: return mk_or_core(num_args, args, result); case OP_NOT: SASSERT(num_args == 1); return mk_not_core(args[0], result); case OP_ITE: SASSERT(num_args == 3); return mk_ite_core(args[0], args[1], args[2], result); case OP_IMPLIES: SASSERT(num_args == 2); mk_implies(args[0], args[1], result); return BR_DONE; case OP_XOR: switch (num_args) { case 0: return BR_FAILED; case 1: result = args[0]; return BR_DONE; case 2: mk_xor(args[0], args[1], result); return BR_DONE; default: UNREACHABLE(); return BR_FAILED; } default: return BR_FAILED; } } void bool_rewriter::mk_and_as_or(unsigned num_args, expr * const * args, expr_ref & result) { expr_ref_buffer new_args(m()); for (unsigned i = 0; i < num_args; i++) { expr_ref tmp(m()); mk_not(args[i], tmp); new_args.push_back(tmp); } expr_ref tmp(m()); mk_or(new_args.size(), new_args.c_ptr(), tmp); mk_not(tmp, result); } br_status bool_rewriter::mk_nflat_and_core(unsigned num_args, expr * const * args, expr_ref & result) { bool s = false; ptr_buffer buffer; expr_fast_mark1 neg_lits; expr_fast_mark2 pos_lits; for (unsigned i = 0; i < num_args; i++) { expr * arg = args[i]; if (m().is_true(arg)) { s = true; continue; } if (m().is_false(arg)) { result = m().mk_false(); return BR_DONE; } if (m().is_not(arg)) { expr * atom = to_app(arg)->get_arg(0); if (neg_lits.is_marked(atom)) { s = true; continue; } if (pos_lits.is_marked(atom)) { result = m().mk_false(); return BR_DONE; } neg_lits.mark(atom); } else { if (pos_lits.is_marked(arg)) { s = true; continue; } if (neg_lits.is_marked(arg)) { result = m().mk_false(); return BR_DONE; } pos_lits.mark(arg); } buffer.push_back(arg); } unsigned sz = buffer.size(); switch(sz) { case 0: result = m().mk_true(); return BR_DONE; case 1: result = buffer.back(); return BR_DONE; default: if (s) { result = m().mk_and(sz, buffer.c_ptr()); return BR_DONE; } return BR_FAILED; } } br_status bool_rewriter::mk_flat_and_core(unsigned num_args, expr * const * args, expr_ref & result) { unsigned i; for (i = 0; i < num_args; i++) { if (m().is_and(args[i])) break; } if (i < num_args) { // has nested ANDs ptr_buffer flat_args; flat_args.append(i, args); for (; i < num_args; i++) { expr * arg = args[i]; // Remark: all rewrites are depth 1. if (m().is_and(arg)) { unsigned num = to_app(arg)->get_num_args(); for (unsigned j = 0; j < num; j++) flat_args.push_back(to_app(arg)->get_arg(j)); } else { flat_args.push_back(arg); } } if (mk_nflat_and_core(flat_args.size(), flat_args.c_ptr(), result) == BR_FAILED) result = m().mk_and(flat_args.size(), flat_args.c_ptr()); return BR_DONE; } return mk_nflat_and_core(num_args, args, result); } br_status bool_rewriter::mk_nflat_or_core(unsigned num_args, expr * const * args, expr_ref & result) { bool s = false; ptr_buffer buffer; expr_fast_mark1 neg_lits; expr_fast_mark2 pos_lits; expr* prev = nullptr; for (unsigned i = 0; i < num_args; i++) { expr * arg = args[i]; if (m().is_true(arg)) { result = m().mk_true(); return BR_DONE; } if (m().is_false(arg)) { s = true; continue; } expr* atom = nullptr; if (m().is_not(arg, atom)) { if (neg_lits.is_marked(atom)) { s = true; continue; } if (pos_lits.is_marked(atom)) { result = m().mk_true(); return BR_DONE; } neg_lits.mark(atom); } else { if (pos_lits.is_marked(arg)) { s = true; continue; } if (neg_lits.is_marked(arg)) { result = m().mk_true(); return BR_DONE; } pos_lits.mark(arg); } buffer.push_back(arg); s |= prev && lt(arg, prev); prev = arg; } unsigned sz = buffer.size(); switch(sz) { case 0: result = m().mk_false(); return BR_DONE; case 1: result = buffer.back(); return BR_DONE; default: if (m_local_ctx && m_local_ctx_cost <= m_local_ctx_limit) { neg_lits.reset(); pos_lits.reset(); if (local_ctx_simp(sz, buffer.c_ptr(), result)) return BR_DONE; } if (s) { ast_lt lt; std::sort(buffer.begin(), buffer.end(), lt); result = m().mk_or(sz, buffer.c_ptr()); return BR_DONE; } return BR_FAILED; } } br_status bool_rewriter::mk_flat_or_core(unsigned num_args, expr * const * args, expr_ref & result) { unsigned i; for (i = 0; i < num_args; i++) { if (m().is_or(args[i])) break; } bool ordered = true; expr* prev = nullptr; if (i < num_args) { // has nested ORs ptr_buffer flat_args; flat_args.append(i, args); for (; i < num_args; i++) { expr * arg = args[i]; // Remark: all rewrites are depth 1. if (m().is_or(arg)) { ordered = false; unsigned num = to_app(arg)->get_num_args(); for (unsigned j = 0; j < num; j++) flat_args.push_back(to_app(arg)->get_arg(j)); } else { flat_args.push_back(arg); ordered &= !prev || !lt(arg, prev); prev = arg; } } if (mk_nflat_or_core(flat_args.size(), flat_args.c_ptr(), result) == BR_FAILED) { if (!ordered) { ast_lt lt; std::sort(flat_args.begin(), flat_args.end(), lt); } result = m().mk_or(flat_args.size(), flat_args.c_ptr()); } return BR_DONE; } return mk_nflat_or_core(num_args, args, result); } expr * bool_rewriter::mk_or_app(unsigned num_args, expr * const * args) { switch(num_args) { case 0: return m().mk_false(); case 1: return args[0]; default: return m().mk_or(num_args, args); } } /** \brief Auxiliary method for local_ctx_simp. Replace args[i] by true if marked in neg_lits. Replace args[i] by false if marked in pos_lits. */ bool bool_rewriter::simp_nested_not_or(unsigned num_args, expr * const * args, expr_fast_mark1 & neg_lits, expr_fast_mark2 & pos_lits, expr_ref & result) { ptr_buffer new_args; bool simp = false; m_local_ctx_cost += num_args; for (unsigned i = 0; i < num_args; i++) { expr * arg = args[i]; if (neg_lits.is_marked(arg)) { result = m().mk_false(); return true; } if (pos_lits.is_marked(arg)) { simp = true; continue; } if (m().is_not(arg)) { expr * atom = to_app(arg)->get_arg(0); if (neg_lits.is_marked(atom)) { simp = true; continue; } if (pos_lits.is_marked(atom)) { result = m().mk_false(); return true; } } new_args.push_back(arg); } if (simp) { switch(new_args.size()) { case 0: result = m().mk_true(); return true; case 1: mk_not(new_args[0], result); return true; default: result = m().mk_not(m().mk_or(new_args.size(), new_args.c_ptr())); return true; } } return false; } expr * bool_rewriter::simp_arg(expr * arg, expr_fast_mark1 & neg_lits, expr_fast_mark2 & pos_lits, bool & modified) { if (m().is_not(arg)) { expr * atom = to_app(arg)->get_arg(0); if (neg_lits.is_marked(atom)) { modified = true; return m().mk_false(); } if (pos_lits.is_marked(atom)) { modified = true; return m().mk_true(); } return arg; } else { if (neg_lits.is_marked(arg)) { modified = true; return m().mk_true(); } if (pos_lits.is_marked(arg)) { modified = true; return m().mk_false(); } return arg; } } /** \brief Simpler version of mk_ite, that will not invoke mk_or/mk_and. It is used byt local_ctx_simp to prevent a recursive call to local_ctx_simp. See comment at simp_nested_eq_ite. */ void bool_rewriter::mk_nested_ite(expr * c, expr * t, expr * e, expr_ref & result) { if (m().is_true(c)) { result = t; return; } if (m().is_false(c)) { result = e; return; } if (t == e) { result = t; return; } if (m().is_bool(t)) { if (m().is_true(t)) { if (m().is_false(e)) { result = c; return; } result = m().mk_or(c, e); return; } if (m().is_false(t)) { if (m().is_true(e)) { mk_not(c, result); return; } expr_ref tmp(m()); mk_not(e, tmp); result = m().mk_not(m().mk_or(c, tmp)); return; } if (m().is_true(e)) { expr_ref tmp(m()); mk_not(c, tmp); result = m().mk_or(tmp, t); return; } if (m().is_false(e) || c == e) { expr_ref tmp1(m()); expr_ref tmp2(m()); mk_not(c, tmp1); mk_not(t, tmp2); result = m().mk_not(m().mk_or(tmp1, tmp2)); return; } if (c == t) { result = m().mk_or(c, e); return; } if (m().is_complement_core(t, e)) { // t = not(e) mk_eq(c, t, result); return; } if (m().is_complement_core(e, t)) { // e = not(t) mk_eq(c, t, result); return; } } result = m().mk_ite(c, t, e); } bool bool_rewriter::simp_nested_eq_ite(expr * t, expr_fast_mark1 & neg_lits, expr_fast_mark2 & pos_lits, expr_ref & result) { bool neg = false; m_local_ctx_cost += 3; if (m().is_not(t)) { neg = true; t = to_app(t)->get_arg(0); } if (m().is_eq(t)) { bool modified = false; expr * new_lhs = simp_arg(to_app(t)->get_arg(0), neg_lits, pos_lits, modified); expr * new_rhs = simp_arg(to_app(t)->get_arg(1), neg_lits, pos_lits, modified); if (!modified) return false; mk_eq(new_lhs, new_rhs, result); if (neg) mk_not(result, result); return true; } if (m().is_ite(t)) { bool modified = false; expr * new_c = simp_arg(to_app(t)->get_arg(0), neg_lits, pos_lits, modified); expr * new_t = simp_arg(to_app(t)->get_arg(1), neg_lits, pos_lits, modified); expr * new_e = simp_arg(to_app(t)->get_arg(2), neg_lits, pos_lits, modified); if (!modified) return false; // It is not safe to invoke mk_ite here, since it can recursively call // local_ctx_simp by // - transforming the ITE into an OR // - and invoked mk_or, that will invoke local_ctx_simp // mk_ite(new_c, new_t, new_e, result); mk_nested_ite(new_c, new_t, new_e, result); if (neg) mk_not(result, result); return true; } return false; } void bool_rewriter::push_new_arg(expr* arg, expr_ref_vector& new_args, expr_fast_mark1& neg_lits, expr_fast_mark2& pos_lits) { expr* narg; if (m().is_not(arg, narg)) { if (!neg_lits.is_marked(narg)) { neg_lits.mark(narg); new_args.push_back(arg); } } else { if (!pos_lits.is_marked(arg)) { pos_lits.mark(arg); new_args.push_back(arg); } } } /** \brief Apply local context simplification at (OR args[0] ... args[num_args-1]) Basic idea: - Replace args[i] by false in the other arguments - If args[i] is of the form (not t), then replace t by true in the other arguments. To make sure the simplification is efficient we bound the depth. */ bool bool_rewriter::local_ctx_simp(unsigned num_args, expr * const * args, expr_ref & result) { expr_ref_vector old_args(m()); expr_ref_vector new_args(m()); expr_ref new_arg(m()); expr_fast_mark1 neg_lits; expr_fast_mark2 pos_lits; bool simp = false; bool modified = false; bool forward = true; unsigned rounds = 0; expr* narg; while (true) { rounds++; #if 0 if (rounds > 10) verbose_stream() << "rounds: " << rounds << "\n"; #endif #define PROCESS_ARG() \ { \ expr * arg = args[i]; \ if (m().is_not(arg, narg) && m().is_or(narg) && \ simp_nested_not_or(to_app(narg)->get_num_args(), \ to_app(narg)->get_args(), \ neg_lits, \ pos_lits, \ new_arg)) { \ modified = true; simp = true; \ arg = new_arg; \ } \ if (simp_nested_eq_ite(arg, neg_lits, pos_lits, new_arg)) { \ modified = true; simp = true; \ arg = new_arg; \ } \ if (m().is_false(arg)) \ continue; \ if (m().is_true(arg)) { \ result = arg; \ return true; \ } \ if (m_flat && m().is_or(arg)) { \ unsigned sz = to_app(arg)->get_num_args(); \ for (unsigned j = 0; j < sz; j++) { \ expr * arg_arg = to_app(arg)->get_arg(j); \ push_new_arg(arg_arg, new_args, neg_lits, pos_lits); \ } \ } \ else { \ push_new_arg(arg, new_args, neg_lits, pos_lits); \ } \ } m_local_ctx_cost += 2*num_args; #if 0 static unsigned counter = 0; counter++; if (counter % 10000 == 0) verbose_stream() << "local-ctx-cost: " << m_local_ctx_cost << " " << num_args << "\n"; #endif if (forward) { for (unsigned i = 0; i < num_args; i++) { PROCESS_ARG(); } forward = false; } else { unsigned i = num_args; while (i > 0) { --i; PROCESS_ARG(); } if (!modified) { if (simp) { result = mk_or_app(num_args, args); return true; } return false; // didn't simplify } // preserve the original order... std::reverse(new_args.c_ptr(), new_args.c_ptr() + new_args.size()); modified = false; forward = true; } pos_lits.reset(); neg_lits.reset(); old_args.reset(); old_args.swap(new_args); SASSERT(new_args.empty()); args = old_args.c_ptr(); num_args = old_args.size(); } } /** \brief Apply simplification if ite is an if-then-else tree where every leaf is a value. This is an efficient way to */ br_status bool_rewriter::try_ite_value(app * ite, app * val, expr_ref & result) { expr* cond = nullptr, *t = nullptr, *e = nullptr; VERIFY(m().is_ite(ite, cond, t, e)); SASSERT(m().is_value(val)); if (m().are_distinct(val, e)) { result = m().mk_and(mk_eq(t, val), cond); return BR_REWRITE2; } if (m().are_distinct(val, t)) { result = m().mk_and(mk_eq(e, val), m().mk_not(cond)); return BR_REWRITE2; } if (m().are_equal(val, t)) { if (m().are_equal(val, e)) { result = m().mk_true(); return BR_DONE; } else { result = m().mk_or(mk_eq(e, val), cond); } return BR_REWRITE2; } if (m().are_equal(val, e)) { result = m().mk_or(mk_eq(t, val), m().mk_not(cond)); return BR_REWRITE2; } expr* cond2 = nullptr, *t2 = nullptr, *e2 = nullptr; if (m().is_ite(t, cond2, t2, e2) && m().is_value(t2) && m().is_value(e2)) { VERIFY(BR_FAILED != try_ite_value(to_app(t), val, result)); result = m().mk_ite(cond, result, mk_eq(e, val)); return BR_REWRITE2; } if (m().is_ite(e, cond2, t2, e2) && m().is_value(t2) && m().is_value(e2)) { VERIFY(BR_FAILED != try_ite_value(to_app(e), val, result)); result = m().mk_ite(cond, mk_eq(t, val), result); return BR_REWRITE2; } return BR_FAILED; } app* bool_rewriter::mk_eq(expr* lhs, expr* rhs) { // degrades simplification // if (lhs->get_id() > rhs->get_id()) std::swap(lhs, rhs); return m().mk_eq(lhs, rhs); } br_status bool_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result) { if (m().are_equal(lhs, rhs)) { result = m().mk_true(); return BR_DONE; } if (m().are_distinct(lhs, rhs)) { result = m().mk_false(); return BR_DONE; } br_status r = BR_FAILED; if (m_ite_extra_rules) { if (m().is_ite(lhs) && m().is_value(rhs)) { r = try_ite_value(to_app(lhs), to_app(rhs), result); CTRACE("try_ite_value", r != BR_FAILED, tout << mk_bounded_pp(lhs, m()) << "\n" << mk_bounded_pp(rhs, m()) << "\n--->\n" << mk_bounded_pp(result, m()) << "\n";); } else if (m().is_ite(rhs) && m().is_value(lhs)) { r = try_ite_value(to_app(rhs), to_app(lhs), result); CTRACE("try_ite_value", r != BR_FAILED, tout << mk_bounded_pp(lhs, m()) << "\n" << mk_bounded_pp(rhs, m()) << "\n--->\n" << mk_bounded_pp(result, m()) << "\n";); } if (r != BR_FAILED) return r; } if (m().is_bool(lhs)) { bool unfolded = false; if (m().is_not(lhs) && m().is_not(rhs)) { lhs = to_app(lhs)->get_arg(0); rhs = to_app(rhs)->get_arg(0); unfolded = true; } if (m().is_true(lhs)) { result = rhs; return BR_DONE; } if (m().is_false(lhs)) { mk_not(rhs, result); return BR_DONE; } if (m().is_true(rhs)) { result = lhs; return BR_DONE; } if (m().is_false(rhs)) { mk_not(lhs, result); return BR_DONE; } if (m().is_complement(lhs, rhs)) { result = m().mk_false(); return BR_DONE; } if (unfolded) { result = mk_eq(lhs, rhs); return BR_DONE; } expr *la, *lb, *ra, *rb; // fold (iff (iff a b) (iff (not a) b)) to false if (m().is_eq(lhs, la, lb) && m().is_eq(rhs, ra, rb)) { expr *n; if ((la == ra && ((m().is_not(rb, n) && n == lb) || (m().is_not(lb, n) && n == rb))) || (lb == rb && ((m().is_not(ra, n) && n == la) || (m().is_not(la, n) && n == ra)))) { result = m().mk_false(); return BR_DONE; } } } return BR_FAILED; } br_status bool_rewriter::mk_distinct_core(unsigned num_args, expr * const * args, expr_ref & result) { if (num_args <= 1) { result = m().mk_true(); return BR_DONE; } if (num_args == 2) { expr_ref tmp(m()); result = m().mk_not(mk_eq(args[0], args[1])); return BR_REWRITE2; // mk_eq may be dispatched to other rewriters. } expr_fast_mark1 visited; bool all_value = true; for (unsigned i = 0; i < num_args; i++) { expr * arg = args[i]; if (visited.is_marked(arg)) { result = m().mk_false(); return BR_DONE; } visited.mark(arg); if (!m().is_unique_value(arg)) all_value = false; } if (all_value) { result = m().mk_true(); return BR_DONE; } SASSERT(num_args > 2); if (m().is_bool(args[0])) { result = m().mk_false(); return BR_DONE; } if (m_blast_distinct && num_args < m_blast_distinct_threshold) { ptr_buffer new_diseqs; for (unsigned i = 0; i < num_args; i++) { for (unsigned j = i + 1; j < num_args; j++) new_diseqs.push_back(m().mk_not(mk_eq(args[i], args[j]))); } result = m().mk_and(new_diseqs.size(), new_diseqs.c_ptr()); return BR_REWRITE3; } return BR_FAILED; } br_status bool_rewriter::mk_ite_core(expr * c, expr * t, expr * e, expr_ref & result) { bool s = false; // (ite (not c) a b) ==> (ite c b a) if (m().is_not(c)) { c = to_app(c)->get_arg(0); std::swap(t, e); s = true; } // (ite c (ite c t1 t2) t3) ==> (ite c t1 t3) if (m().is_ite(t) && to_app(t)->get_arg(0) == c) { // Remark: (ite c (ite (not c) t1 t2) t3) ==> (ite c t2 t3) does not happen if applying rewrites bottom up t = to_app(t)->get_arg(1); s = true; } // (ite c t1 (ite c t2 t3)) ==> (ite c t1 t3) if (m().is_ite(e) && to_app(e)->get_arg(0) == c) { // Remark: (ite c t1 (ite (not c) t2 t3)) ==> (ite c t1 t2) does not happen if applying rewrites bottom up e = to_app(e)->get_arg(2); s = true; } if (m().is_true(c)) { result = t; return BR_DONE; } if (m().is_false(c)) { result = e; return BR_DONE; } if (t == e) { result = t; return BR_DONE; } if (m().is_bool(t)) { if (m().is_true(t)) { if (m().is_false(e)) { result = c; return BR_DONE; } if (m_elim_ite) { mk_or(c, e, result); return BR_DONE; } } if (m().is_false(t)) { if (m().is_true(e)) { mk_not(c, result); return BR_DONE; } if (m_elim_ite) { expr_ref tmp(m()); mk_not(c, tmp); mk_and(tmp, e, result); return BR_DONE; } } if (m().is_true(e) && m_elim_ite) { expr_ref tmp(m()); mk_not(c, tmp); mk_or(tmp, t, result); return BR_DONE; } if (m().is_false(e) && m_elim_ite) { mk_and(c, t, result); return BR_DONE; } if (c == e && m_elim_ite) { mk_and(c, t, result); return BR_DONE; } if (c == t && m_elim_ite) { mk_or(c, e, result); return BR_DONE; } if (m().is_complement_core(t, e) && m_elim_ite) { // t = not(e) mk_eq(c, t, result); return BR_DONE; } if (m().is_complement_core(e, t) && m_elim_ite) { // e = not(t) mk_eq(c, t, result); return BR_DONE; } } if (m().is_ite(t) && m_ite_extra_rules && m_elim_ite) { // (ite c1 (ite c2 t1 t2) t1) ==> (ite (and c1 (not c2)) t2 t1) if (e == to_app(t)->get_arg(1)) { expr_ref not_c2(m()); mk_not(to_app(t)->get_arg(0), not_c2); expr_ref new_c(m()); mk_and(c, not_c2, new_c); result = m().mk_ite(new_c, to_app(t)->get_arg(2), e); return BR_REWRITE1; } // (ite c1 (ite c2 t1 t2) t2) ==> (ite (and c1 c2) t1 t2) if (e == to_app(t)->get_arg(2)) { expr_ref new_c(m()); mk_and(c, to_app(t)->get_arg(0), new_c); result = m().mk_ite(new_c, to_app(t)->get_arg(1), e); return BR_REWRITE1; } if (m().is_ite(e)) { // (ite c1 (ite c2 t1 t2) (ite c3 t1 t2)) ==> (ite (or (and c1 c2) (and (not c1) c3)) t1 t2) if (to_app(t)->get_arg(1) == to_app(e)->get_arg(1) && to_app(t)->get_arg(2) == to_app(e)->get_arg(2)) { expr_ref and1(m()); expr_ref and2(m()); expr_ref notc(m()); mk_and(c, to_app(t)->get_arg(0), and1); mk_not(c, notc); mk_and(notc, to_app(e)->get_arg(0), and2); expr_ref new_c(m()); mk_or(and1, and2, new_c); result = m().mk_ite(new_c, to_app(t)->get_arg(1), to_app(t)->get_arg(2)); return BR_REWRITE1; } // (ite c1 (ite c2 t1 t2) (ite c3 t2 t1)) ==> (ite (or (and c1 c2) (and (not c1) (not c3))) t1 t2) if (to_app(t)->get_arg(1) == to_app(e)->get_arg(2) && to_app(t)->get_arg(2) == to_app(e)->get_arg(1)) { expr_ref and1(m()); expr_ref and2(m()); expr_ref notc(m()); mk_and(c, to_app(t)->get_arg(0), and1); mk_not(c, notc); expr_ref notc3(m()); mk_not(to_app(e)->get_arg(0), notc3); mk_and(notc, notc3, and2); expr_ref new_c(m()); mk_or(and1, and2, new_c); result = m().mk_ite(new_c, to_app(t)->get_arg(1), to_app(t)->get_arg(2)); return BR_REWRITE1; } } } if (m().is_ite(e) && m_ite_extra_rules && m_elim_ite) { // (ite c1 t1 (ite c2 t1 t2)) ==> (ite (or c1 c2) t1 t2) if (t == to_app(e)->get_arg(1)) { expr_ref new_c(m()); mk_or(c, to_app(e)->get_arg(0), new_c); result = m().mk_ite(new_c, t, to_app(e)->get_arg(2)); return BR_REWRITE1; } // (ite c1 t1 (ite c2 t2 t1)) ==> (ite (or c1 (not c2)) t1 t2) if (t == to_app(e)->get_arg(2)) { expr_ref not_c2(m()); mk_not(to_app(e)->get_arg(0), not_c2); expr_ref new_c(m()); mk_or(c, not_c2, new_c); result = m().mk_ite(new_c, t, to_app(e)->get_arg(1)); return BR_REWRITE1; } } if (s) { result = m().mk_ite(c, t, e); return BR_DONE; } return BR_FAILED; } br_status bool_rewriter::mk_not_core(expr * t, expr_ref & result) { if (m().is_not(t)) { result = to_app(t)->get_arg(0); return BR_DONE; } if (m().is_true(t)) { result = m().mk_false(); return BR_DONE; } if (m().is_false(t)) { result = m().mk_true(); return BR_DONE; } if (is_eq(t) && m().is_bool(to_app(t)->get_arg(0))) { expr_ref tmp(m()); mk_not(to_app(t)->get_arg(0), tmp); mk_eq(tmp, to_app(t)->get_arg(1), result); return BR_DONE; } return BR_FAILED; } void bool_rewriter::mk_xor(expr * lhs, expr * rhs, expr_ref & result) { expr_ref tmp(m()); mk_not(lhs, tmp); mk_eq(tmp, rhs, result); } void bool_rewriter::mk_implies(expr * lhs, expr * rhs, expr_ref & result) { expr_ref tmp(m()); mk_not(lhs, tmp); mk_or(tmp, rhs, result); } void bool_rewriter::mk_nand(unsigned num_args, expr * const * args, expr_ref & result) { expr_ref tmp(m_manager); mk_and(num_args, args, tmp); mk_not(tmp, result); } void bool_rewriter::mk_nor(unsigned num_args, expr * const * args, expr_ref & result) { expr_ref tmp(m_manager); mk_or(num_args, args, tmp); mk_not(tmp, result); } void bool_rewriter::mk_nand(expr * arg1, expr * arg2, expr_ref & result) { expr_ref tmp(m_manager); mk_and(arg1, arg2, tmp); mk_not(tmp, result); } void bool_rewriter::mk_nor(expr * arg1, expr * arg2, expr_ref & result) { expr_ref tmp(m_manager); mk_or(arg1, arg2, tmp); mk_not(tmp, result); } template class rewriter_tpl; z3-z3-4.8.7/src/ast/rewriter/bool_rewriter.h000066400000000000000000000175141356505360400207070ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: bool_rewriter.h Abstract: Basic rewriting rules for Boolean operators. Author: Leonardo (leonardo) 2011-04-04 Notes: --*/ #ifndef BOOL_REWRITER_H_ #define BOOL_REWRITER_H_ #include "ast/ast.h" #include "ast/rewriter/rewriter.h" #include "util/params.h" /** \brief Apply basic Boolean rewriting operations. Only depth 1 simplifications are performed. Note: there are no recursive calls. Note: arguments of AC operators are not sorted. Note: arguments of = and xor are also not sorted. Note: By default, (AND A B) is not rewritten as (NOT (OR (NOT A) (NOT B))) Note: AND OR operators are flattened only if mk_flat_app, mk_flat_or, mk_flat_and are used. The following operators are expanded: - => (implies) - xor - nand - nor - iff All methods run in time almost linear on the number of arguments. Actually, this is not true when flattening is enabled. A better approximation is O(Sum_{t \in args} size1(t)). Where size1(t) = max{t->get_num_args(), 1}. */ class bool_rewriter { ast_manager & m_manager; bool m_flat; bool m_local_ctx; bool m_elim_and; bool m_blast_distinct; unsigned m_blast_distinct_threshold; bool m_ite_extra_rules; unsigned m_local_ctx_limit; unsigned m_local_ctx_cost; bool m_elim_ite; br_status mk_flat_and_core(unsigned num_args, expr * const * args, expr_ref & result); br_status mk_flat_or_core(unsigned num_args, expr * const * args, expr_ref & result); br_status mk_nflat_and_core(unsigned num_args, expr * const * args, expr_ref & result); br_status mk_nflat_or_core(unsigned num_args, expr * const * args, expr_ref & result); void mk_and_as_or(unsigned num_args, expr * const * args, expr_ref & result); expr * mk_or_app(unsigned num_args, expr * const * args); bool simp_nested_not_or(unsigned num_args, expr * const * args, expr_fast_mark1 & neg_lits, expr_fast_mark2 & pos_lits, expr_ref & result); expr * simp_arg(expr * arg, expr_fast_mark1 & neg_lits, expr_fast_mark2 & pos_lits, bool & modified); void mk_nested_ite(expr * new_c, expr * new_t, expr * new_e, expr_ref & result); bool simp_nested_eq_ite(expr * t, expr_fast_mark1 & neg_lits, expr_fast_mark2 & pos_lits, expr_ref & result); bool local_ctx_simp(unsigned num_args, expr * const * args, expr_ref & result); br_status try_ite_value(app * ite, app * val, expr_ref & result); void push_new_arg(expr* arg, expr_ref_vector& new_args, expr_fast_mark1& neg_lits, expr_fast_mark2& pos_lits); public: bool_rewriter(ast_manager & m, params_ref const & p = params_ref()):m_manager(m), m_local_ctx_cost(0) { updt_params(p); } 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); } bool flat() const { return m_flat; } void set_flat(bool f) { m_flat = f; } bool elim_and() const { return m_elim_and; } void set_elim_and(bool f) { m_elim_and = f; } void reset_local_ctx_cost() { m_local_ctx_cost = 0; } void updt_params(params_ref const & p); static void get_param_descrs(param_descrs & r); // The core methods return true if a rewrite-step/simplification was applied // to the arguments, and the result is stored in 'result'. Otherwise, they return false // and result.get == 0. br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result); void mk_app(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { if (mk_app_core(f, num_args, args, result) == BR_FAILED) result = m().mk_app(f, num_args, args); } br_status mk_eq_core(expr * lhs, expr * rhs, expr_ref & result); br_status mk_distinct_core(unsigned num_args, expr * const * args, expr_ref & result); br_status mk_iff_core(expr * lhs, expr * rhs, expr_ref & result) { return mk_eq_core(lhs, rhs, result); } br_status mk_and_core(unsigned num_args, expr * const * args, expr_ref & result) { if (m_elim_and) { mk_and_as_or(num_args, args, result); return BR_DONE; } else if (m_flat) { return mk_flat_and_core(num_args, args, result); } else { return mk_nflat_and_core(num_args, args, result); } } br_status mk_or_core(unsigned num_args, expr * const * args, expr_ref & result) { return m_flat ? mk_flat_or_core(num_args, args, result) : mk_nflat_or_core(num_args, args, result); } br_status mk_ite_core(expr * c, expr * t, expr * e, expr_ref & result); br_status mk_not_core(expr * t, expr_ref & result); app* mk_eq(expr* lhs, expr* rhs); void mk_eq(expr * lhs, expr * rhs, expr_ref & result) { if (mk_eq_core(lhs, rhs, result) == BR_FAILED) result = mk_eq(lhs, rhs); } void mk_iff(expr * lhs, expr * rhs, expr_ref & result) { mk_eq(lhs, rhs, result); } void mk_xor(expr * lhs, expr * rhs, expr_ref & result); void mk_and(unsigned num_args, expr * const * args, expr_ref & result) { if (mk_and_core(num_args, args, result) == BR_FAILED) { SASSERT(!m_elim_and); result = m().mk_and(num_args, args); } } void mk_or(unsigned num_args, expr * const * args, expr_ref & result) { if (mk_or_core(num_args, args, result) == BR_FAILED) result = m().mk_or(num_args, args); } void mk_and(expr * arg1, expr * arg2, expr_ref & result) { expr * args[2] = {arg1, arg2}; mk_and(2, args, result); } void mk_or(expr * arg1, expr * arg2, expr_ref & result) { expr * args[2] = {arg1, arg2}; mk_or(2, args, result); } void mk_and(expr * arg1, expr * arg2, expr * arg3, expr_ref & result) { expr * args[3] = {arg1, arg2, arg3}; mk_and(3, args, result); } void mk_or(expr * arg1, expr * arg2, expr * arg3, expr_ref & result) { expr * args[3] = {arg1, arg2, arg3}; mk_or(3, args, result); } void mk_implies(expr * lhs, expr * rhs, expr_ref & result); void mk_ite(expr * c, expr * t, expr * e, expr_ref & result) { if (mk_ite_core(c, t, e, result) == BR_FAILED) result = m().mk_ite(c, t, e); } void mk_distinct(unsigned num_args, expr * const * args, expr_ref & result) { if (mk_distinct_core(num_args, args, result) == BR_FAILED) result = m().mk_distinct(num_args, args); } void mk_not(expr * t, expr_ref & result) { if (mk_not_core(t, result) == BR_FAILED) result = m().mk_not(t); } void mk_nand(unsigned num_args, expr * const * args, expr_ref & result); void mk_nor(unsigned num_args, expr * const * args, expr_ref & result); void mk_nand(expr * arg1, expr * arg2, expr_ref & result); void mk_nor(expr * arg1, expr * arg2, expr_ref & result); }; struct bool_rewriter_cfg : public default_rewriter_cfg { bool_rewriter m_r; bool flat_assoc(func_decl * f) const { return m_r.flat() && (m_r.m().is_and(f) || m_r.m().is_or(f)); } 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); } bool_rewriter_cfg(ast_manager & m, params_ref const & p):m_r(m, p) {} }; class bool_rewriter_star : public rewriter_tpl { bool_rewriter_cfg m_cfg; public: bool_rewriter_star(ast_manager & m, params_ref const & p): rewriter_tpl(m, false, m_cfg), m_cfg(m, p) {} }; #endif z3-z3-4.8.7/src/ast/rewriter/bool_rewriter_params.pyg000066400000000000000000000022361356505360400226150ustar00rootroot00000000000000def_module_params(module_name='rewriter', class_name='bool_rewriter_params', export=True, params=(("ite_extra_rules", BOOL, False, "extra ite simplifications, these additional simplifications may reduce size locally but increase globally"), ("flat", BOOL, True, "create nary applications for and,or,+,*,bvadd,bvmul,bvand,bvor,bvxor"), ("elim_and", BOOL, False, "conjunctions are rewritten using negation and disjunctions"), ('elim_ite', BOOL, True, "eliminate ite in favor of and/or"), ("local_ctx", BOOL, False, "perform local (i.e., cheap) context simplifications"), ("local_ctx_limit", UINT, UINT_MAX, "limit for applying local context simplifier"), ("blast_distinct", BOOL, False, "expand a distinct predicate into a quadratic number of disequalities"), ("blast_distinct_threshold", UINT, UINT_MAX, "when blast_distinct is true, only distinct expressions with less than this number of arguments are blasted") )) z3-z3-4.8.7/src/ast/rewriter/bv_bounds.cpp000066400000000000000000000643501356505360400203450ustar00rootroot00000000000000/*++ Copyright (c) 2016 Microsoft Corporation Module Name: bv_bounds.cpp Abstract: Author: Mikolas Janota (MikolasJanota) Revision History: --*/ #include "ast/rewriter/bv_bounds.h" #include "ast/ast_smt2_pp.h" bv_bounds::~bv_bounds() { reset(); } bv_bounds::conv_res bv_bounds::record(app * v, numeral lo, numeral hi, bool negated, vector& nis) { TRACE("bv_bounds", tout << "record0 " << mk_ismt2_pp(v, m_m) << ":" << (negated ? "~[" : "[") << lo << ";" << hi << "]" << std::endl;); const unsigned bv_sz = m_bv_util.get_bv_size(v); const numeral& one = numeral::one(); SASSERT(numeral::zero() <= lo); SASSERT(lo <= hi); SASSERT(hi < numeral::power_of_two(bv_sz)); numeral vmax, vmin; const bool has_upper = m_unsigned_uppers.find(v, vmax); const bool has_lower = m_unsigned_lowers.find(v, vmin); if (!has_lower) vmin = numeral::zero(); if (!has_upper) vmax = (numeral::power_of_two(bv_sz) - one); bool lo_min = lo <= vmin; bool hi_max = hi >= vmax; if (negated) { if (lo_min && hi_max) return UNSAT; if (lo > vmax) return CONVERTED; if (hi < vmin) return CONVERTED; if (lo_min) { negated = false; lo = hi + one; hi = vmax; lo_min = lo <= vmin; hi_max = true; } else if (hi_max) { negated = false; hi = lo - one; lo = vmin; hi_max = hi >= vmax; lo_min = true; } SASSERT(lo.is_nonneg()); SASSERT(lo <= hi); SASSERT(hi < numeral::power_of_two(bv_sz)); } if (lo_min) lo = vmin; if (hi_max) hi = vmax; TRACE("bv_bounds", tout << "record1 " << mk_ismt2_pp(v, m_m) << ":" << (negated ? "~[" : "[") << lo << ";" << hi << "]" << std::endl;); if (lo > hi) return negated ? CONVERTED : UNSAT; if (lo_min && hi_max) return negated ? UNSAT : CONVERTED; nis.resize(nis.size() + 1); nis.back().v = v; nis.back().lo = lo; nis.back().hi = hi; nis.back().negated = negated; return CONVERTED; } bool bv_bounds::is_uleq(expr * e, expr * & v, numeral & c) { // To detect the following rewrite from bv_rewriter: // m().mk_and( // m().mk_eq(m_mk_extract(bv_sz - 1, first_non_zero + 1, a), m_util.mk_numeral(numeral(0), bv_sz - first_non_zero - 1)), // m_util.mk_ule(m_mk_extract(first_non_zero, 0, a), m_mk_extract(first_non_zero, 0, b))); expr * eq; expr * eql; expr * eqr; expr * ule; expr * ulel; expr * uler; numeral eqr_val, uleqr_val; unsigned eqr_sz, uleqr_sz; if (!m_m.is_and(e, eq, ule)) return false; if (!m_m.is_eq(eq, eql, eqr)) return false; if (!m_bv_util.is_bv_ule(ule, ulel, uler)) return false; if (!m_bv_util.is_extract(eql)) return false; expr * const eql0 = to_app(eql)->get_arg(0); const unsigned eql0_sz = m_bv_util.get_bv_size(eql0); if (m_bv_util.get_extract_high(eql) != (eql0_sz - 1)) return false; if (!m_bv_util.is_numeral(eqr, eqr_val, eqr_sz)) return false; if (!eqr_val.is_zero()) return false; if (!m_bv_util.is_extract(ulel)) return false; expr * const ulel0 = to_app(ulel)->get_arg(0); if (ulel0 != eql0) return false; if ((m_bv_util.get_extract_high(ulel) + 1) != m_bv_util.get_extract_low(eql)) return false; if (m_bv_util.get_extract_low(ulel) != 0) return false; if (!m_bv_util.is_numeral(uler, uleqr_val, uleqr_sz)) return false; SASSERT(m_bv_util.get_bv_size(ulel0) == uleqr_sz + eqr_sz); v = ulel0; c = uleqr_val; return true; } bv_bounds::conv_res bv_bounds::convert(expr * e, vector& nis, bool negated) { TRACE("bv_bounds", tout << "new constraint: " << (negated ? "~" : "" ) << mk_ismt2_pp(e, m_m) << std::endl;); if (m_m.is_not(e)) { negated = !negated; e = to_app(e)->get_arg(0); } expr *lhs, *rhs; numeral val, val1; unsigned bv_sz1; if (false) { if (m_m.is_eq(e, lhs, rhs) && to_bound(lhs) && m_bv_util.is_numeral(rhs, val, bv_sz1)) { return record(to_app(lhs), val, val, negated, nis); } if (m_m.is_eq(e, lhs, rhs) && to_bound(rhs) && m_bv_util.is_numeral(lhs, val, bv_sz1)) { return record(to_app(rhs), val, val, negated, nis); } } if (is_uleq(e, lhs, val) && to_bound(lhs)) { return record(to_app(lhs), numeral::zero(), val, negated, nis); } if (true) { numeral rhs_val; unsigned rhs_sz; if (m_m.is_eq(e, lhs, rhs) && m_bv_util.is_numeral(rhs, rhs_val, rhs_sz) && rhs_val.is_zero() && m_bv_util.is_extract(lhs)) { expr * const lhs0 = to_app(lhs)->get_arg(0); const unsigned lhs0_sz = m_bv_util.get_bv_size(lhs0); if (m_bv_util.get_extract_high(lhs)+1 == lhs0_sz) { const numeral u = numeral::power_of_two(m_bv_util.get_extract_low(lhs)) - numeral::one(); return record(to_app(lhs0), numeral::zero(), u, negated, nis); } } } if (m_bv_util.is_bv_ule(e, lhs, rhs)) { unsigned bv_sz = m_bv_util.get_bv_size(lhs); // unsigned inequality with one variable and a constant if (to_bound(lhs) && m_bv_util.is_numeral(rhs, val, bv_sz)) // v <= val return record(to_app(lhs), numeral::zero(), val, negated, nis); if (to_bound(rhs) && m_bv_util.is_numeral(lhs, val, bv_sz)) // val <= v return record(to_app(rhs), val, numeral::power_of_two(bv_sz) - numeral::one(), negated, nis); // unsigned inequality with one variable, constant, and addition expr *t1, *t2; if (m_bv_util.is_bv_add(lhs, t1, t2) && m_bv_util.is_numeral(t1, val, bv_sz) && to_bound(t2) && t2 == rhs) { // val + v <= v if (val.is_zero()) return negated ? UNSAT : CONVERTED; SASSERT(val.is_pos()); const numeral mod = numeral::power_of_two(bv_sz); return record(to_app(rhs), mod - val, mod - numeral::one(), negated, nis); } if (m_bv_util.is_bv_add(rhs, t1, t2) && m_bv_util.is_numeral(t1, val, bv_sz) && to_bound(t2) && m_bv_util.is_numeral(lhs, val1, bv_sz1)) { // val1 <= val + v SASSERT(bv_sz1 == bv_sz); const numeral mod = numeral::power_of_two(bv_sz); if (val1.is_zero()) return negated ? UNSAT : CONVERTED; if (val1 < val) { const numeral nl = mod - val; const numeral nh = mod + val1 - val - numeral::one(); return nl <= nh ? record(to_app(t2), nl, nh, !negated, nis) : (negated ? UNSAT : CONVERTED); } else { const numeral l = val1 - val; const numeral h = mod - val - numeral::one(); return l <= h ? record(to_app(t2), l, h, negated, nis) : (negated ? CONVERTED : UNSAT); } } if (m_bv_util.is_bv_add(lhs, t1, t2) && m_bv_util.is_numeral(t1, val, bv_sz) && to_bound(t2) && m_bv_util.is_numeral(rhs, val1, bv_sz1)) { // val + v <= val1 SASSERT(bv_sz1 == bv_sz); if (!val.is_pos() || !val1.is_pos()) return UNDEF; const numeral mod = numeral::power_of_two(bv_sz); if (val <= val1) { const numeral nl = val1 - val + numeral::one(); const numeral nh = mod - val - numeral::one(); return nl <= nh ? record(to_app(t2), nl, nh, !negated, nis) : (negated ? UNSAT : CONVERTED); } else { const numeral l = mod - val; const numeral h = l + val1; return record(to_app(t2), l, h, negated, nis); } } // v + c1 <= v + c2 app * v1(nullptr), *v2(nullptr); numeral val1, val2; if (is_constant_add(bv_sz, lhs, v1, val1) && is_constant_add(bv_sz, rhs, v2, val2) && v1 == v2) { if (val1 == val2) return negated ? UNSAT : CONVERTED; const numeral mod = numeral::power_of_two(bv_sz); if (val1 < val2) { SASSERT(val1 < (mod - numeral::one())); SASSERT(val2 > numeral::zero()); return record(v1, mod - val2, mod - val1 - numeral::one(), !negated, nis); } else { SASSERT(val1 > val2); SASSERT(val2 < (mod - numeral::one())); SASSERT(val1 > numeral::zero()); return record(v1, mod - val1, mod - val2 - numeral::one(), negated, nis); } } } if (m_bv_util.is_bv_sle(e, lhs, rhs)) { unsigned bv_sz = m_bv_util.get_bv_size(lhs); // signed inequality with one variable and a constant if (to_bound(lhs) && m_bv_util.is_numeral(rhs, val, bv_sz)) { // v <= val val = m_bv_util.norm(val, bv_sz, true); return convert_signed(to_app(lhs), -numeral::power_of_two(bv_sz - 1), val, negated, nis); } if (to_bound(rhs) && m_bv_util.is_numeral(lhs, val, bv_sz)) { // val <= v val = m_bv_util.norm(val, bv_sz, true); return convert_signed(to_app(rhs), val, numeral::power_of_two(bv_sz - 1) - numeral::one(), negated, nis); } } return UNDEF; } void bv_bounds::reset() { intervals_map::iterator it = m_negative_intervals.begin(); const intervals_map::iterator end = m_negative_intervals.end(); for (; it != end; ++it) dealloc(it->m_value); } br_status bv_bounds::rewrite(unsigned limit, func_decl * f, unsigned num, expr * const * args, expr_ref& result) { if (!m_m.is_bool(f->get_range())) return BR_FAILED; const decl_kind k = f->get_decl_kind(); if ((k != OP_OR && k != OP_AND) || num > limit) return BR_FAILED; const bool negated = k == OP_OR; vector nis; vector lengths; vector ignore; unsigned nis_head = 0; for (unsigned i = 0; i < num && m_okay; ++i) { expr * const curr = args[i]; const conv_res cr = convert(curr, nis, negated); ignore.push_back(cr == UNDEF); switch (cr) { case UNDEF: continue; case UNSAT: m_okay = false; break; case CONVERTED: { for (unsigned i = nis_head; i < nis.size(); ++i) { const ninterval& ni = nis[i]; m_okay = m_okay && add_bound_unsigned(ni.v, ni.lo, ni.hi, ni.negated); } lengths.push_back(nis.size()); nis_head = nis.size(); break; } default: UNREACHABLE(); } } if (!m_okay || !is_sat()) { result = negated ? m_m.mk_true() : m_m.mk_false(); return BR_DONE; } nis_head = 0; unsigned count = 0; expr_ref_vector nargs(m_m); bool has_singls = false; for (unsigned i = 0; i < num && m_okay; ++i) { TRACE("bv_bounds", tout << "check red: " << mk_ismt2_pp(args[i], m_m) << std::endl;); if (ignore[i]) { TRACE("bv_bounds", tout << "unprocessed" << std::endl;); nargs.push_back(args[i]); continue; } SASSERT(nis_head <= lengths[count]); const bool redundant = nis_head == lengths[count]; bool is_singl = false; if (nis_head < lengths[count]) { app * const v = nis[nis_head].v; numeral th, tl; const unsigned bv_sz = m_bv_util.get_bv_size(v); const bool has_upper = m_unsigned_uppers.find(v, th); const bool has_lower = m_unsigned_lowers.find(v, tl); const numeral& one = numeral::one(); if (!has_lower) tl = numeral::zero(); if (!has_upper) th = (numeral::power_of_two(bv_sz) - one); TRACE("bv_bounds", tout << "bounds: " << mk_ismt2_pp(v, m_m) << "[" << tl << "-" << th << "]" << std::endl;); is_singl = tl == th; nis_head = lengths[count]; } if (!redundant && !is_singl) nargs.push_back(args[i]); has_singls |= is_singl; CTRACE("bv_bounds", redundant, tout << "redundant: " << mk_ismt2_pp(args[i], m_m) << std::endl;); ++count; } if (nargs.size() == num && !has_singls) return BR_FAILED; expr_ref eq(m_m); for (bv_bounds::bound_map::iterator i = m_singletons.begin(); i != m_singletons.end(); ++i) { app * const v = i->m_key; const rational val = i->m_value; eq = m_m.mk_eq(v, bvu().mk_numeral(val, v->get_decl()->get_range())); if (negated) eq = m_m.mk_not(eq); nargs.push_back(eq); } switch (nargs.size()) { case 0: result = negated ? m_m.mk_false() : m_m.mk_true(); return BR_DONE; case 1: result = nargs.get(0); return BR_DONE; default: result = negated ? m_m.mk_or(nargs.size(), nargs.c_ptr()) : m_m.mk_and(nargs.size(), nargs.c_ptr()); return BR_DONE; } } bool bv_bounds::add_constraint(expr* e) { TRACE("bv_bounds", tout << "new constraint" << mk_ismt2_pp(e, m_m) << std::endl;); if (!m_okay) return false; bool negated = false; if (m_m.is_not(e)) { negated = true; e = to_app(e)->get_arg(0); } expr *lhs, *rhs; numeral val, val1; unsigned bv_sz1; if (false) { if (m_m.is_eq(e, lhs, rhs) && to_bound(lhs) && m_bv_util.is_numeral(rhs, val, bv_sz1)) { return add_bound_unsigned(to_app(lhs), val, val, negated); } if (m_m.is_eq(e, lhs, rhs) && to_bound(rhs) && m_bv_util.is_numeral(lhs, val, bv_sz1)) { return add_bound_unsigned(to_app(rhs), val, val, negated); } } if (m_bv_util.is_bv_ule(e, lhs, rhs)) { unsigned bv_sz = m_bv_util.get_bv_size(lhs); // unsigned inequality with one variable and a constant if (to_bound(lhs) && m_bv_util.is_numeral(rhs, val, bv_sz)) // v <= val return add_bound_unsigned(to_app(lhs), numeral::zero(), val, negated); if (to_bound(rhs) && m_bv_util.is_numeral(lhs, val, bv_sz)) // val <= v return add_bound_unsigned(to_app(rhs), val, numeral::power_of_two(bv_sz) - numeral::one(), negated); // unsigned inequality with one variable, constant, and addition expr *t1, *t2; if (m_bv_util.is_bv_add(lhs, t1, t2) && m_bv_util.is_numeral(t1, val, bv_sz) && to_bound(t2) && t2 == rhs) { // val + v <= v if (!val.is_pos()) return m_okay; const numeral mod = numeral::power_of_two(bv_sz); return add_bound_unsigned(to_app(rhs), mod - val, mod - numeral::one(), negated); } if (m_bv_util.is_bv_add(rhs, t1, t2) && m_bv_util.is_numeral(t1, val, bv_sz) && to_bound(t2) && m_bv_util.is_numeral(lhs, val1, bv_sz1)) { // val1 <= val + v SASSERT(bv_sz1 == bv_sz); if (!val.is_pos() || !val1.is_pos()) return m_okay; const numeral mod = numeral::power_of_two(bv_sz); if (val1 < val) { const numeral nl = mod - val; const numeral nh = mod + val1 - val - numeral::one(); return nl <= nh ? add_bound_unsigned(to_app(t2), nl, nh, !negated) : m_okay; } else { const numeral l = val1 - val; const numeral h = mod - val - numeral::one(); return l <= h ? add_bound_unsigned(to_app(t2), l, h, negated) : m_okay; } } if (m_bv_util.is_bv_add(lhs, t1, t2) && m_bv_util.is_numeral(t1, val, bv_sz) && to_bound(t2) && m_bv_util.is_numeral(rhs, val1, bv_sz1)) { // val + v <= val1 SASSERT(bv_sz1 == bv_sz); if (!val.is_pos() || !val1.is_pos()) return m_okay; const numeral mod = numeral::power_of_two(bv_sz); if (val <= val1) { const numeral nl = val1 - val + numeral::one(); const numeral nh = mod - val - numeral::one(); return nl <= nh ? add_bound_unsigned(to_app(t2), nl, nh, !negated) : m_okay; } else { const numeral l = mod - val; const numeral h = l + val1; return add_bound_unsigned(to_app(t2), l, h, negated); } } // v + c1 <= v + c2 app * v1(nullptr), *v2(nullptr); numeral val1, val2; if (is_constant_add(bv_sz, lhs, v1, val1) && is_constant_add(bv_sz, rhs, v2, val2) && v1 == v2) { if (val1 == val2) return m_okay; const numeral mod = numeral::power_of_two(bv_sz); if (val1 < val2) { SASSERT(val1 < (mod - numeral::one())); SASSERT(val2 > numeral::zero()); return add_bound_unsigned(v1, mod - val2, mod - val1 - numeral::one(), !negated); } else { SASSERT(val1 > val2); SASSERT(val2 < (mod - numeral::one())); SASSERT(val1 > numeral::zero()); return add_bound_unsigned(v1, mod - val1, mod - val2 - numeral::one(), negated); } } } if (m_bv_util.is_bv_sle(e, lhs, rhs)) { unsigned bv_sz = m_bv_util.get_bv_size(lhs); // signed inequality with one variable and a constant if (to_bound(lhs) && m_bv_util.is_numeral(rhs, val, bv_sz)) { // v <= val val = m_bv_util.norm(val, bv_sz, true); return add_bound_signed(to_app(lhs), -numeral::power_of_two(bv_sz - 1), val, negated); } if (to_bound(rhs) && m_bv_util.is_numeral(lhs, val, bv_sz)) { // val <= v val = m_bv_util.norm(val, bv_sz, true); return add_bound_signed(to_app(rhs), val, numeral::power_of_two(bv_sz - 1) - numeral::one(), negated); } } return m_okay; } bool bv_bounds::add_bound_unsigned(app * v, const numeral& a, const numeral& b, bool negate) { TRACE("bv_bounds", tout << "bound_unsigned " << mk_ismt2_pp(v, m_m) << ": " << (negate ? "~[" : "[") << a << ";" << b << "]" << std::endl;); const unsigned bv_sz = m_bv_util.get_bv_size(v); const numeral& zero = numeral::zero(); const numeral& one = numeral::one(); SASSERT(zero <= a); SASSERT(a <= b); SASSERT(b < numeral::power_of_two(bv_sz)); const bool a_min = a == zero; const bool b_max = b == (numeral::power_of_two(bv_sz) - one); if (negate) { if (a_min && b_max) return m_okay = false; if (a_min) return bound_lo(v, b + one); if (b_max) return bound_up(v, a - one); return add_neg_bound(v, a, b); } else { if (!a_min) m_okay &= bound_lo(v, a); if (!b_max) m_okay &= bound_up(v, b); return m_okay; } } bv_bounds::conv_res bv_bounds::convert_signed(app * v, const numeral& a, const numeral& b, bool negate, vector& nis) { TRACE("bv_bounds", tout << "convert_signed " << mk_ismt2_pp(v, m_m) << ":" << (negate ? "~[" : "[") << a << ";" << b << "]" << std::endl;); const unsigned bv_sz = m_bv_util.get_bv_size(v); SASSERT(a <= b); const numeral& zero = numeral::zero(); const numeral& one = numeral::one(); const bool a_neg = a < zero; const bool b_neg = b < zero; if (!a_neg && !b_neg) return record(v, a, b, negate, nis); const numeral mod = numeral::power_of_two(bv_sz); if (a_neg && b_neg) return record(v, mod + a, mod + b, negate, nis); SASSERT(a_neg && !b_neg); if (negate) { const conv_res r1 = record(v, mod + a, mod - one, true, nis); const conv_res r2 = record(v, zero, b, true, nis); return r1 == UNSAT || r2 == UNSAT ? UNSAT : CONVERTED; } else { const numeral l = b + one; const numeral u = mod + a - one; return l <= u ? record(v, l, u, true, nis) : CONVERTED; } } bool bv_bounds::add_bound_signed(app * v, const numeral& a, const numeral& b, bool negate) { TRACE("bv_bounds", tout << "bound_signed " << mk_ismt2_pp(v, m_m) << ":" << (negate ? "~" : " ") << a << ";" << b << std::endl;); const unsigned bv_sz = m_bv_util.get_bv_size(v); SASSERT(a <= b); const numeral& zero = numeral::zero(); const numeral& one = numeral::one(); const bool a_neg = a < zero; const bool b_neg = b < zero; if (!a_neg && !b_neg) return add_bound_unsigned(v, a, b, negate); const numeral mod = numeral::power_of_two(bv_sz); if (a_neg && b_neg) return add_bound_unsigned(v, mod + a, mod + b, negate); SASSERT(a_neg && !b_neg); if (negate) { return add_bound_unsigned(v, mod + a, mod - one, true) && add_bound_unsigned(v, zero, b, true); } else { const numeral l = b + one; const numeral u = mod + a - one; return (l <= u) ? add_bound_unsigned(v, l, u, true) : m_okay; } } bool bv_bounds::bound_lo(app * v, const numeral& l) { SASSERT(in_range(v, l)); TRACE("bv_bounds", tout << "lower " << mk_ismt2_pp(v, m_m) << ":" << l << std::endl;); // l <= v bound_map::obj_map_entry * const entry = m_unsigned_lowers.insert_if_not_there2(v, l); if (!(entry->get_data().m_value < l)) return m_okay; // improve bound entry->get_data().m_value = l; return m_okay; } bool bv_bounds::bound_up(app * v, const numeral& u) { SASSERT(in_range(v, u)); TRACE("bv_bounds", tout << "upper " << mk_ismt2_pp(v, m_m) << ":" << u << std::endl;); // v <= u bound_map::obj_map_entry * const entry = m_unsigned_uppers.insert_if_not_there2(v, u); if (!(u < entry->get_data().m_value)) return m_okay; // improve bound entry->get_data().m_value = u; return m_okay; } bool bv_bounds::add_neg_bound(app * v, const numeral& a, const numeral& b) { TRACE("bv_bounds", tout << "negative bound " << mk_ismt2_pp(v, m_m) << ":" << a << ";" << b << std::endl;); bv_bounds::interval negative_interval(a, b); SASSERT(m_bv_util.is_bv(v)); SASSERT(a >= numeral::zero()); SASSERT(b < numeral::power_of_two(m_bv_util.get_bv_size(v))); SASSERT(a <= b); intervals_map::obj_map_entry * const e = m_negative_intervals.find_core(v); intervals * ivs(nullptr); if (e == nullptr) { ivs = alloc(intervals); m_negative_intervals.insert(v, ivs); } else { ivs = e->get_data().get_value(); } ivs->push_back(negative_interval); return m_okay; } bool bv_bounds::is_sat() { if (!m_okay) return false; obj_hashtable seen; obj_hashtable::entry *dummy; for (bound_map::iterator i = m_unsigned_lowers.begin(); i != m_unsigned_lowers.end(); ++i) { app * const v = i->m_key; if (!seen.insert_if_not_there_core(v, dummy)) continue; if (!is_sat(v)) return false; } for (bound_map::iterator i = m_unsigned_uppers.begin(); i != m_unsigned_uppers.end(); ++i) { app * const v = i->m_key; if (!seen.insert_if_not_there_core(v, dummy)) continue; if (!is_sat(v)) return false; } for (intervals_map::iterator i = m_negative_intervals.begin(); i != m_negative_intervals.end(); ++i) { app * const v = i->m_key; if (!seen.insert_if_not_there_core(v, dummy)) continue; if (!is_sat(v)) return false; } return true; } struct interval_comp_t { bool operator() (bv_bounds::interval const& i, bv_bounds::interval const& j) { return (i.first < j.first); } } interval_comp; void bv_bounds::record_singleton(app * v, numeral& singleton_value) { TRACE("bv_bounds", tout << "singleton:" << mk_ismt2_pp(v, m_m) << ":" << singleton_value << std::endl;); SASSERT(!m_singletons.find(v, singleton_value)); m_singletons.insert(v, singleton_value); } bool bv_bounds::is_sat(app * v) { TRACE("bv_bounds", tout << "is_sat " << mk_ismt2_pp(v, m_m) << std::endl;); const bool rv = is_sat_core(v); TRACE("bv_bounds", tout << "is_sat " << mk_ismt2_pp(v, m_m) << "\nres: " << rv << std::endl;); return rv; } bool bv_bounds::is_sat_core(app * v) { SASSERT(m_bv_util.is_bv(v)); if (!m_okay) return false; unsigned const bv_sz = m_bv_util.get_bv_size(v); numeral lower, upper; const bool has_upper = m_unsigned_uppers.find(v, upper); const bool has_lower = m_unsigned_lowers.find(v, lower); if (has_upper && has_lower && lower > upper) return false; const numeral& one = numeral::one(); if (!has_lower) lower = numeral::zero(); if (!has_upper) upper = (numeral::power_of_two(bv_sz) - one); TRACE("bv_bounds", tout << "is_sat bound:" << lower << "-" << upper << std::endl;); intervals * negative_intervals(nullptr); const bool has_neg_intervals = m_negative_intervals.find(v, negative_intervals); bool is_sat(false); numeral new_lo = lower; numeral new_hi = lower - one; numeral ptr = lower; if (has_neg_intervals) { 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) { const numeral negative_lower = i->first; const numeral negative_upper = i->second; if (ptr > negative_upper) continue; if (ptr < negative_lower) { if (!is_sat) new_lo = ptr; new_hi = negative_lower - one; if (new_hi > upper) new_hi = upper; is_sat = true; } TRACE("bv_bounds", tout << "is_sat new_lo, new_hi:" << new_lo << "-" << new_hi << std::endl;); ptr = negative_upper + one; TRACE("bv_bounds", tout << "is_sat ptr, new_hi:" << ptr << "-" << new_hi << std::endl;); if (ptr > upper) break; } } if (ptr <= upper) { if (!is_sat) new_lo = ptr; new_hi = upper; is_sat = true; } if (new_hi < upper) bound_up(v, new_hi); if (new_lo > lower) bound_lo(v, new_lo); TRACE("bv_bounds", tout << "is_sat new_lo, new_hi:" << new_lo << "-" << new_hi << std::endl;); const bool is_singleton = is_sat && new_hi == new_lo; if (is_singleton) record_singleton(v, new_lo); return is_sat; } z3-z3-4.8.7/src/ast/rewriter/bv_bounds.h000066400000000000000000000110011356505360400177730ustar00rootroot00000000000000 /*++ Copyright (c) 2016 Microsoft Corporation Module Name: bv_bounds.h Abstract: A class used to determine bounds on bit-vector variables. The satisfiability procedure is polynomial. Author: Mikolas Janota (MikolasJanota) Revision History: --*/ #ifndef BV_BOUNDS_H_23754 #define BV_BOUNDS_H_23754 #include "ast/ast.h" #include "ast/bv_decl_plugin.h" #include "ast/rewriter/rewriter_types.h" /* \brief A class to analyze constraints on bit vectors. The objective is to identify inconsistencies in polynomial time. All bounds/intervals are closed. Methods that add new constraints return false if inconsistency has already been reached. Typical usage is to call repeatedly add_constraint(e) and call is_sat() in the end. */ class bv_bounds { public: typedef rational numeral; typedef std::pair interval; typedef obj_map bound_map; bv_bounds(ast_manager& m) : m_m(m), m_bv_util(m), m_okay(true) {}; ~bv_bounds(); public: // bounds addition methods br_status rewrite(unsigned limit, func_decl * f, unsigned num, expr * const * args, expr_ref& result); /** \brief Add a constraint to the system. The added constraints are to be considered by is_sat. Currently, only special types of inequalities are supported, e.g. v <= v+1. Other constraints are ignored. Returns false if the system became trivially unsatisfiable **/ bool add_constraint(expr* e); bool bound_up(app * v, const numeral& u); // v <= u bool bound_lo(app * v, const numeral& l); // l <= v inline bool add_neg_bound(app * v, const numeral& a, const numeral& b); // not (a<=v<=b) bool add_bound_signed(app * v, const numeral& a, const numeral& b, bool negate); bool add_bound_unsigned(app * v, const numeral& a, const numeral& b, bool negate); public: bool is_sat(); ///< Determine if the set of considered constraints is satisfiable. bool is_okay(); const bound_map& singletons() { return m_singletons; } bv_util& bvu() { return m_bv_util; } void reset(); protected: struct ninterval { //ninterval(app * v, numeral lo, numeral hi, bool negated) : v(v), lo(lo), hi(hi), negated(negated) {} app * v; numeral lo, hi; bool negated; }; enum conv_res { CONVERTED, UNSAT, UNDEF }; conv_res convert(expr * e, vector& nis, bool negated); conv_res record(app * v, numeral lo, numeral hi, bool negated, vector& nis); conv_res convert_signed(app * v, const numeral& a, const numeral& b, bool negate, vector& nis); typedef vector intervals; typedef obj_map intervals_map; ast_manager& m_m; bound_map m_unsigned_lowers; bound_map m_unsigned_uppers; intervals_map m_negative_intervals; bound_map m_singletons; bv_util m_bv_util; bool m_okay; bool is_sat(app * v); bool is_sat_core(app * v); inline bool in_range(app *v, const numeral& l); inline bool is_constant_add(unsigned bv_sz, expr * e, app*& v, numeral& val); void record_singleton(app * v, numeral& singleton_value); inline bool to_bound(const expr * e) const; bool is_uleq(expr * e, expr * & v, numeral & c); }; inline bool bv_bounds::is_okay() { return m_okay; } inline bool bv_bounds::to_bound(const expr * e) const { return is_app(e) && m_bv_util.is_bv(e) && !m_bv_util.is_bv_add(e) && !m_bv_util.is_numeral(e); } inline bool bv_bounds::in_range(app *v, const bv_bounds::numeral& n) { const unsigned bv_sz = m_bv_util.get_bv_size(v); const bv_bounds::numeral zero(0); const bv_bounds::numeral mod(rational::power_of_two(bv_sz)); return (zero <= n) && (n < mod); } inline bool bv_bounds::is_constant_add(unsigned bv_sz, expr * e, app*& v, numeral& val) { SASSERT(e && !v); SASSERT(m_bv_util.get_bv_size(e) == bv_sz); expr *lhs(nullptr), *rhs(nullptr); if (!m_bv_util.is_bv_add(e, lhs, rhs)) { v = to_app(e); val = rational(0); return true; } if (to_bound(lhs) && m_bv_util.is_numeral(rhs, val, bv_sz)) { v = to_app(lhs); return true; } if (to_bound(rhs) && m_bv_util.is_numeral(lhs, val, bv_sz)) { v = to_app(rhs); return true; } return false; } #endif /* BV_BOUNDS_H_23754 */ z3-z3-4.8.7/src/ast/rewriter/bv_elim.cpp000066400000000000000000000067421356505360400200020ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "ast/rewriter/bv_elim.h" #include "ast/bv_decl_plugin.h" #include "ast/rewriter/var_subst.h" #include "ast/rewriter/rewriter_def.h" #include bool bv_elim_cfg::reduce_quantifier(quantifier * q, expr * body, expr * const * new_patterns, expr * const * new_no_patterns, expr_ref & result, proof_ref & result_pr) { svector names, _names; sort_ref_buffer sorts(m), _sorts(m); expr_ref_buffer pats(m); expr_ref_buffer no_pats(m); expr_ref_buffer subst_map(m), _subst_map(m); var_subst subst(m); bv_util bv(m); expr_ref new_body(m); expr* old_body = body; unsigned num_decls = q->get_num_decls(); family_id bfid = m.mk_family_id("bv"); // // Traverse sequence of bound variables to eliminate // bit-vecctor variables and replace them by // Booleans. // unsigned var_idx = 0; bool found = false; for (unsigned i = num_decls; i > 0; ) { --i; sort* s = q->get_decl_sort(i); symbol nm = q->get_decl_name(i); if (bv.is_bv_sort(s)) { // convert n-bit bit-vector variable into sequence of n-Booleans. unsigned num_bits = bv.get_bv_size(s); expr_ref_buffer args(m); expr_ref bv(m); found = true; for (unsigned j = 0; j < num_bits; ++j) { std::ostringstream new_name; new_name << nm.str(); new_name << "_"; new_name << j; var* v = m.mk_var(var_idx++, m.mk_bool_sort()); args.push_back(v); _sorts.push_back(m.mk_bool_sort()); _names.push_back(symbol(new_name.str().c_str())); } bv = m.mk_app(bfid, OP_MKBV, 0, nullptr, args.size(), args.c_ptr()); _subst_map.push_back(bv.get()); } else { _subst_map.push_back(m.mk_var(var_idx++, s)); _sorts.push_back(s); _names.push_back(nm); } } if (!found) { return false; } // // reverse the vectors. // SASSERT(_names.size() == _sorts.size()); for (unsigned i = _names.size(); i > 0; ) { --i; names.push_back(_names[i]); sorts.push_back(_sorts[i]); } for (unsigned i = _subst_map.size(); i > 0; ) { --i; subst_map.push_back(_subst_map[i]); } expr* const* sub = subst_map.c_ptr(); unsigned sub_size = subst_map.size(); new_body = subst(old_body, sub_size, sub); for (unsigned j = 0; j < q->get_num_patterns(); j++) { pats.push_back(subst(new_patterns[j], sub_size, sub)); } for (unsigned j = 0; j < q->get_num_no_patterns(); j++) { no_pats.push_back(subst(new_no_patterns[j], sub_size, sub)); } result = m.mk_quantifier(forall_k, names.size(), sorts.c_ptr(), names.c_ptr(), new_body.get(), q->get_weight(), q->get_qid(), q->get_skid(), pats.size(), pats.c_ptr(), no_pats.size(), no_pats.c_ptr()); result_pr = m.mk_rewrite(q, result); return true; } z3-z3-4.8.7/src/ast/rewriter/bv_elim.h000066400000000000000000000020211356505360400174310ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: bv_elim.h Abstract: Eliminate bit-vectors variables from clauses, by replacing them by bound Boolean variables. Author: Nikolaj Bjorner (nbjorner) 2008-12-16. Revision History: --*/ #ifndef BV_ELIM_H_ #define BV_ELIM_H_ #include "ast/ast.h" #include "ast/rewriter/rewriter.h" class bv_elim_cfg : public default_rewriter_cfg { ast_manager& m; public: bv_elim_cfg(ast_manager& m) : m(m) {} 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); }; class bv_elim_rw : public rewriter_tpl { protected: bv_elim_cfg m_cfg; public: bv_elim_rw(ast_manager & m): rewriter_tpl(m, m.proofs_enabled(), m_cfg), m_cfg(m) {} }; #endif /* BV_ELIM_H_ */ z3-z3-4.8.7/src/ast/rewriter/bv_rewriter.cpp000066400000000000000000002664121356505360400207210ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: bv_rewriter.cpp Abstract: Basic rewriting rules for bit-vectors Author: Leonardo (leonardo) 2011-04-14 Notes: --*/ #include "ast/rewriter/bv_rewriter.h" #include "ast/rewriter/bv_rewriter_params.hpp" #include "ast/rewriter/poly_rewriter_def.h" #include "ast/ast_smt2_pp.h" #include "ast/ast_lt.h" void bv_rewriter::updt_local_params(params_ref const & _p) { bv_rewriter_params p(_p); m_hi_div0 = p.hi_div0(); m_elim_sign_ext = p.elim_sign_ext(); m_mul2concat = p.mul2concat(); m_bit2bool = p.bit2bool(); m_trailing = p.bv_trailing(); m_urem_simpl = p.bv_urem_simpl(); m_blast_eq_value = p.blast_eq_value(); m_split_concat_eq = p.split_concat_eq(); m_udiv2mul = p.udiv2mul(); m_bvnot2arith = p.bvnot2arith(); m_bvnot_simpl = p.bv_not_simpl(); m_bv_sort_ac = p.bv_sort_ac(); m_mkbv2num = _p.get_bool("mkbv2num", false); m_extract_prop = p.bv_extract_prop(); m_ite2id = p.bv_ite2id(); m_le_extra = p.bv_le_extra(); set_sort_sums(p.bv_sort_ac()); } void bv_rewriter::updt_params(params_ref const & p) { poly_rewriter::updt_params(p); updt_local_params(p); } void bv_rewriter::get_param_descrs(param_descrs & r) { poly_rewriter::get_param_descrs(r); bv_rewriter_params::collect_param_descrs(r); #ifndef _EXTERNAL_RELEASE r.insert("mkbv2num", CPK_BOOL, "(default: false) convert (mkbv [true/false]*) into a numeral"); #endif } br_status bv_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { SASSERT(f->get_family_id() == get_fid()); switch(f->get_decl_kind()) { case OP_BIT0: SASSERT(num_args == 0); result = m_util.mk_numeral(0, 1); return BR_DONE; case OP_BIT1: SASSERT(num_args == 0); result = m_util.mk_numeral(1, 1); return BR_DONE; case OP_ULEQ: SASSERT(num_args == 2); return mk_ule(args[0], args[1], result); case OP_UGEQ: SASSERT(num_args == 2); return mk_uge(args[0], args[1], result); case OP_ULT: SASSERT(num_args == 2); return mk_ult(args[0], args[1], result); case OP_UGT: SASSERT(num_args == 2); return mk_ult(args[1], args[0], result); case OP_SLEQ: SASSERT(num_args == 2); return mk_sle(args[0], args[1], result); case OP_SGEQ: SASSERT(num_args == 2); return mk_sge(args[0], args[1], result); case OP_SLT: SASSERT(num_args == 2); return mk_slt(args[0], args[1], result); case OP_SGT: SASSERT(num_args == 2); return mk_slt(args[1], args[0], result); case OP_BADD: SASSERT(num_args > 0); return mk_bv_add(num_args, args, result); case OP_BMUL: SASSERT(num_args > 0); return mk_bv_mul(num_args, args, result); case OP_BSUB: SASSERT(num_args > 0); return mk_sub(num_args, args, result); case OP_BNEG: SASSERT(num_args == 1); return mk_uminus(args[0], result); case OP_BSHL: SASSERT(num_args == 2); return mk_bv_shl(args[0], args[1], result); case OP_BLSHR: SASSERT(num_args == 2); return mk_bv_lshr(args[0], args[1], result); case OP_BASHR: SASSERT(num_args == 2); return mk_bv_ashr(args[0], args[1], result); case OP_BSDIV: SASSERT(num_args == 2); return mk_bv_sdiv(args[0], args[1], result); case OP_BUDIV: SASSERT(num_args == 2); return mk_bv_udiv(args[0], args[1], result); case OP_BSREM: SASSERT(num_args == 2); return mk_bv_srem(args[0], args[1], result); case OP_BUREM: SASSERT(num_args == 2); return mk_bv_urem(args[0], args[1], result); case OP_BSMOD: SASSERT(num_args == 2); return mk_bv_smod(args[0], args[1], result); case OP_BSDIV_I: SASSERT(num_args == 2); return mk_bv_sdiv_i(args[0], args[1], result); case OP_BUDIV_I: SASSERT(num_args == 2); return mk_bv_udiv_i(args[0], args[1], result); case OP_BSREM_I: SASSERT(num_args == 2); return mk_bv_srem_i(args[0], args[1], result); case OP_BUREM_I: SASSERT(num_args == 2); return mk_bv_urem_i(args[0], args[1], result); case OP_BSMOD_I: SASSERT(num_args == 2); return mk_bv_smod_i(args[0], args[1], result); case OP_CONCAT: return mk_concat(num_args, args, result); case OP_EXTRACT: SASSERT(num_args == 1); return mk_extract(m_util.get_extract_high(f), m_util.get_extract_low(f), args[0], result); case OP_REPEAT: SASSERT(num_args == 1); return mk_repeat(f->get_parameter(0).get_int(), args[0], result); case OP_ZERO_EXT: SASSERT(num_args == 1); return mk_zero_extend(f->get_parameter(0).get_int(), args[0], result); case OP_SIGN_EXT: SASSERT(num_args == 1); return mk_sign_extend(f->get_parameter(0).get_int(), args[0], result); case OP_BOR: return mk_bv_or(num_args, args, result); case OP_BXOR: return mk_bv_xor(num_args, args, result); case OP_BNOT: SASSERT(num_args == 1); return mk_bv_not(args[0], result); case OP_BAND: return mk_bv_and(num_args, args, result); case OP_BNAND: return mk_bv_nand(num_args, args, result); case OP_BNOR: return mk_bv_nor(num_args, args, result); case OP_BXNOR: return mk_bv_xnor(num_args, args, result); case OP_ROTATE_LEFT: SASSERT(num_args == 1); return mk_bv_rotate_left(f->get_parameter(0).get_int(), args[0], result); case OP_ROTATE_RIGHT: SASSERT(num_args == 1); return mk_bv_rotate_right(f->get_parameter(0).get_int(), args[0], result); case OP_EXT_ROTATE_LEFT: SASSERT(num_args == 2); return mk_bv_ext_rotate_left(args[0], args[1], result); case OP_EXT_ROTATE_RIGHT: SASSERT(num_args == 2); return mk_bv_ext_rotate_right(args[0], args[1], result); case OP_BV2INT: SASSERT(num_args == 1); return mk_bv2int(args[0], result); case OP_INT2BV: SASSERT(num_args == 1); return mk_int2bv(m_util.get_bv_size(f->get_range()), args[0], result); case OP_BREDOR: SASSERT(num_args == 1); return mk_bv_redor(args[0], result); case OP_BREDAND: SASSERT(num_args == 1); return mk_bv_redand(args[0], result); case OP_BCOMP: SASSERT(num_args == 2); 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: return mk_bvumul_no_overflow(num_args, args, result); case OP_BSMUL_NO_UDFL: return mk_bvsmul_no_underflow(num_args, args, result); default: return BR_FAILED; } } br_status bv_rewriter::mk_ule(expr * a, expr * b, expr_ref & result) { return mk_leq_core(false, a, b, result); } br_status bv_rewriter::mk_uge(expr * a, expr * b, expr_ref & result) { br_status st = mk_ule(b, a, result); if (st != BR_FAILED) return st; result = m_util.mk_ule(b, a); return BR_DONE; } br_status bv_rewriter::mk_ult(expr * a, expr * b, expr_ref & result) { result = m().mk_not(m_util.mk_ule(b, a)); return BR_REWRITE2; } br_status bv_rewriter::mk_sle(expr * a, expr * b, expr_ref & result) { return mk_leq_core(true, a, b, result); } br_status bv_rewriter::mk_sge(expr * a, expr * b, expr_ref & result) { br_status st = mk_sle(b, a, result); if (st != BR_FAILED) return st; result = m_util.mk_sle(b, a); return BR_DONE; } br_status bv_rewriter::mk_slt(expr * a, expr * b, expr_ref & result) { result = m().mk_not(m_util.mk_sle(b, a)); return BR_REWRITE2; } // short-circuited concat expr * bv_rewriter::concat(unsigned num_args, expr * const * args) { SASSERT(num_args); switch (num_args) { case 0: return m_util.mk_concat(num_args, args); case 1: return args[0]; default: return m_util.mk_concat(num_args, args); } } // finds a commonality in sums, e.g. 2 + x + y and 5 + x + y bool bv_rewriter::are_eq_upto_num(expr * _a, expr * _b, expr_ref& common, numeral& a0_val, numeral& b0_val) { const bool aadd = m_util.is_bv_add(_a); const bool badd = m_util.is_bv_add(_b); const bool has_num_a = aadd && to_app(_a)->get_num_args() && is_numeral(to_app(_a)->get_arg(0)); const bool has_num_b = badd && to_app(_b)->get_num_args() && is_numeral(to_app(_b)->get_arg(0)); a0_val = numeral::zero(); b0_val = numeral::zero(); if (!aadd && !badd) { if (_a == _b) { common = _a; return true; } else { return false; } } if (!aadd && badd) { if (to_app(_a)->get_num_args() != 2 || !has_num_a || to_app(_a)->get_arg(0) != _b) return false; common = _b; return true; } if (aadd && !badd) { if (to_app(_b)->get_num_args() != 2 || !has_num_b || to_app(_b)->get_arg(0) != _a) return false; common = _a; return true; } SASSERT(aadd && badd); app * const a = to_app(_a); app * const b = to_app(_b); const unsigned numa = a->get_num_args(); const unsigned numb = b->get_num_args(); if (!numa || !numb) return false; if ((numa - (has_num_a ? 1 : 0)) != (numb - (has_num_b ? 1 : 0))) return false; unsigned ai = has_num_a ? 1 : 0; unsigned bi = has_num_b ? 1 : 0; while (ai < numa) { if (a->get_arg(ai) != b->get_arg(bi)) return false; ++ai; ++bi; } a0_val = numeral::zero(); b0_val = numeral::zero(); const unsigned sz = m_util.get_bv_size(a); unsigned a0_sz(sz), b0_sz(sz); if (has_num_a) is_numeral(a->get_arg(0), a0_val, a0_sz); if (has_num_b) is_numeral(b->get_arg(0), b0_val, b0_sz); SASSERT(a0_sz == m_util.get_bv_size(a) && b0_sz == m_util.get_bv_size(a)); if (has_num_a && numa > 2) { common = m().mk_app(m_util.get_fid(), add_decl_kind(), numa - 1, a->get_args() + 1); } else { common = has_num_a ? a->get_arg(1) : a; } return true; } // simplifies expressions as (bvuleq (X + c1) (X + c2)) for some common expression X and numerals c1, c2 br_status bv_rewriter::rw_leq_overflow(bool is_signed, expr * a, expr * b, expr_ref & result) { if (is_signed) return BR_FAILED; expr_ref common(m()); numeral a0_val, b0_val; if (!are_eq_upto_num(a, b, common, a0_val, b0_val)) return BR_FAILED; SASSERT(a0_val.is_nonneg() && b0_val.is_nonneg()); const unsigned sz = m_util.get_bv_size(a); if (a0_val == b0_val) { result = m().mk_true(); return BR_DONE; } if (a0_val < b0_val) { result = m_util.mk_ule(m_util.mk_numeral(b0_val - a0_val, sz), b); return BR_REWRITE2; } SASSERT(a0_val > b0_val); SASSERT(!a0_val.is_zero()); const numeral lower = rational::power_of_two(sz) - a0_val; const numeral upper = rational::power_of_two(sz) - b0_val - numeral::one(); if (lower == upper) { result = m().mk_eq(common, mk_numeral(lower, sz)); } else if (b0_val.is_zero()) { result = m_util.mk_ule(mk_numeral(lower, sz), common); } else { SASSERT(lower.is_pos()); result = m().mk_and(m_util.mk_ule(mk_numeral(lower, sz), common), m_util.mk_ule(common, mk_numeral(upper, sz))); } return BR_REWRITE2; } // simplification for leq comparison between two concatenations br_status bv_rewriter::rw_leq_concats(bool is_signed, expr * _a, expr * _b, expr_ref & result) { if (!m_util.is_concat(_a) || !m_util.is_concat(_b)) return BR_FAILED; const app * const a = to_app(_a); const app * const b = to_app(_b); const unsigned numa = a->get_num_args(); const unsigned numb = b->get_num_args(); const unsigned num_min = std::min(numa, numb); if (numa && numb) { // first arg numeral numeral af, bf; unsigned af_sz, bf_sz; if ( is_numeral(a->get_arg(0), af, af_sz) && is_numeral(b->get_arg(0), bf, bf_sz) ) { const unsigned sz_min = std::min(af_sz, bf_sz); const numeral hi_af = m_util.norm(af_sz > sz_min ? div(af, rational::power_of_two(af_sz - sz_min)) : af, sz_min, is_signed); const numeral hi_bf = m_util.norm(bf_sz > sz_min ? div(bf, rational::power_of_two(bf_sz - sz_min)) : bf, sz_min, is_signed); if (hi_af != hi_bf) { result = hi_af < hi_bf ? m().mk_true() : m().mk_false(); return BR_DONE; } expr_ref new_a(m()); expr_ref new_b(m()); if (af_sz > sz_min) { ptr_buffer new_args; new_args.push_back(mk_numeral(af, af_sz - sz_min)); for (unsigned i = 1; i < numa; ++i) new_args.push_back(a->get_arg(i)); new_a = concat(new_args.size(), new_args.c_ptr()); } else { new_a = concat(numa - 1, a->get_args() + 1); } if (bf_sz > sz_min) { ptr_buffer new_args; new_args.push_back(mk_numeral(bf, bf_sz - sz_min)); for (unsigned i = 1; i < numb; ++i) new_args.push_back(b->get_arg(i)); new_b = concat(new_args.size(), new_args.c_ptr()); } else { new_b = concat(numb - 1, b->get_args() + 1); } result = m_util.mk_ule(new_a, new_b); return BR_REWRITE2; } } { // common prefix unsigned common = 0; while (common < num_min && m().are_equal(a->get_arg(common), b->get_arg(common))) ++common; SASSERT((common == numa) == (common == numb)); if (common == numa) { SASSERT(0); // shouldn't get here as both sides are equal result = m().mk_true(); return BR_DONE; } if (common > 0) { result = m_util.mk_ule(concat(numa - common, a->get_args() + common), concat(numb - common, b->get_args() + common)); return BR_REWRITE2; } } { // common postfix unsigned new_numa = a->get_num_args(); unsigned new_numb = b->get_num_args(); while (new_numa && new_numb) { expr * const last_a = a->get_arg(new_numa - 1); expr * const last_b = b->get_arg(new_numb - 1); if (!m().are_equal(last_a, last_b)) break; new_numa--; new_numb--; } if (new_numa == 0) { SASSERT(0); // shouldn't get here as both sides are equal result = m().mk_true(); return BR_DONE; } if (new_numa != numa) { result = is_signed ? m_util.mk_sle(concat(new_numa, a->get_args()), concat(new_numb, b->get_args())) : m_util.mk_ule(concat(new_numa, a->get_args()), concat(new_numb, b->get_args())); return BR_REWRITE2; } } return BR_FAILED; } br_status bv_rewriter::mk_leq_core(bool is_signed, expr * a, expr * b, expr_ref & result) { numeral r1, r2, r3; unsigned sz; bool is_num1 = is_numeral(a, r1, sz); bool is_num2 = is_numeral(b, r2, sz); if (a == b) { result = m().mk_true(); return BR_DONE; } if (is_num1) r1 = m_util.norm(r1, sz, is_signed); if (is_num2) r2 = m_util.norm(r2, sz, is_signed); if (is_num1 && is_num2) { result = m().mk_bool_val(r1 <= r2); return BR_DONE; } numeral lower, upper; if (is_num1 || is_num2) { if (is_signed) { lower = - rational::power_of_two(sz - 1); upper = rational::power_of_two(sz - 1) - numeral(1); } else { lower = numeral(0); upper = rational::power_of_two(sz) - numeral(1); } } if (is_num2) { if (r2 == lower) { result = m().mk_eq(a, b); return BR_REWRITE1; } if (r2 == upper) { result = m().mk_true(); return BR_DONE; } } if (is_num1) { // 0 <= b is true if (r1 == lower) { result = m().mk_true(); return BR_DONE; } // 2^n-1 <= b is a = b if (r1 == upper) { result = m().mk_eq(a, b); return BR_REWRITE1; } } expr* a1, *a2, *a3, *a4, *a5, *a6; // (bvsle (- x (srem x c1)) c2) -> (bvsle x (+ c1 c2 - 1)) // (bvsle (+ x (* -1 (srem_i x c1))) c2) // pre: (and (> c1 0) (> c2 0) (= c2 % c1 0) (<= (+ c1 c2 -1) max_int)) if (is_signed && is_num2 && m_util.is_bv_add(a, a1, a2) && m_util.is_bv_mul(a2, a3, a4) && is_numeral(a3, r1, sz) && m_util.norm(r1, sz, is_signed).is_minus_one() && m_util.is_bv_sremi(a4, a5, a6) && is_numeral(a6, r1, sz) && (r1 = m_util.norm(r1, sz, is_signed), r1.is_pos()) && r2.is_pos() && (a1 == a5) && (r2 % r1).is_zero() && r1 + r2 - rational::one() < rational::power_of_two(sz-1)) { result = m_util.mk_sle(a1, m_util.mk_numeral(r1 + r2 - rational::one(), sz)); return BR_REWRITE2; } if (m_le_extra) { const br_status cst = rw_leq_concats(is_signed, a, b, result); if (cst != BR_FAILED) { TRACE("le_extra", tout << (is_signed ? "bv_sle\n" : "bv_ule\n") << mk_ismt2_pp(a, m(), 2) << "\n" << mk_ismt2_pp(b, m(), 2) << "\n--->\n"<< mk_ismt2_pp(result, m(), 2) << "\n";); return cst; } } if (m_le_extra) { const br_status cst = rw_leq_overflow(is_signed, a, b, result); if (cst != BR_FAILED) { TRACE("le_extra", tout << (is_signed ? "bv_sle\n" : "bv_ule\n") << mk_ismt2_pp(a, m(), 2) << "\n" << mk_ismt2_pp(b, m(), 2) << "\n--->\n"<< mk_ismt2_pp(result, m(), 2) << "\n";); return cst; } } #if 0 if (!is_signed && m_util.is_concat(b) && to_app(b)->get_num_args() == 2 && m_util.is_zero(to_app(b)->get_arg(0))) { // // a <=_u (concat 0 c) ---> a[h:l] = 0 && a[l-1:0] <=_u c // expr * b_1 = to_app(b)->get_arg(0); expr * b_2 = to_app(b)->get_arg(1); unsigned sz1 = get_bv_size(b_1); unsigned sz2 = get_bv_size(b_2); result = m().mk_and(m().mk_eq(m_mk_extract(sz2+sz1-1, sz2, a), b_1), m_util.mk_ule(m_mk_extract(sz2-1, 0, a), b_2)); return BR_REWRITE3; } #else if (!is_signed) { // Extended version of the rule above using is_zero_bit. // It also catches examples atoms such as: // // a <=_u #x000f // unsigned bv_sz = m_util.get_bv_size(b); unsigned i = bv_sz; unsigned first_non_zero = UINT_MAX; while (i > 0) { --i; if (!is_zero_bit(b, i)) { first_non_zero = i; break; } } if (first_non_zero == UINT_MAX) { // all bits are zero result = m().mk_eq(a, m_util.mk_numeral(numeral(0), bv_sz)); return BR_REWRITE1; } else if (first_non_zero < bv_sz - 1) { result = m().mk_and(m().mk_eq(m_mk_extract(bv_sz - 1, first_non_zero + 1, a), m_util.mk_numeral(numeral(0), bv_sz - first_non_zero - 1)), m_util.mk_ule(m_mk_extract(first_non_zero, 0, a), m_mk_extract(first_non_zero, 0, b))); return BR_REWRITE3; } } #endif // Investigate if we need: // // k <=_s (concat 0 a) <=> (k[u:l] = 0 && k[l-1:0] <=_u a) || k[u:u] = bv1 // // (concat 0 a) <=_s k <=> k[u:u] = bv0 && (k[u:l] != 0 || a <=_u k[l-1:0]) // // (concat 0 a) <=_u k <=> k[u:l] != 0 || a <=_u k[l-1:0] // return BR_FAILED; } // attempt to chop off bits that are above the position high for bv_mul and bv_add, // returns how many bits were chopped off // e.g. (bvadd(concat #b11 p) #x1)) with high=1, returns 2 and sets result = p + #b01 // the sz of results is the sz of arg minus the return value unsigned bv_rewriter::propagate_extract(unsigned high, expr * arg, expr_ref & result) { if (!m_util.is_bv_add(arg) && !m_util.is_bv_mul(arg)) return 0; const unsigned sz = m_util.get_bv_size(arg); const unsigned to_remove = high + 1 < sz ? sz - high - 1 : 0; if (to_remove == 0) return 0; // high goes to the top, nothing to do const app * const a = to_app(arg); const unsigned num = a->get_num_args(); bool all_numerals = true; unsigned removable = to_remove; numeral val; unsigned curr_first_sz = -1; // calculate how much can be removed for (unsigned i = 0; i < num; i++) { expr * const curr = a->get_arg(i); const bool curr_is_conc = m_util.is_concat(curr); if (curr_is_conc && to_app(curr)->get_num_args() == 0) continue; expr * const curr_first = curr_is_conc ? to_app(curr)->get_arg(0) : curr; if (!all_numerals) { if (removable != m_util.get_bv_size(curr_first)) return 0; continue; } if (is_numeral(curr_first, val, curr_first_sz)) { removable = std::min(removable, curr_first_sz); } else { all_numerals = false; curr_first_sz = m_util.get_bv_size(curr_first); if (curr_first_sz > removable) return 0; removable = curr_first_sz; } if (removable == 0) return 0; } // perform removal SASSERT(removable <= to_remove); ptr_buffer new_args; ptr_buffer new_concat_args; for (unsigned i = 0; i < num; i++) { expr * const curr = a->get_arg(i); const bool curr_is_conc = m_util.is_concat(curr); if (curr_is_conc && to_app(curr)->get_num_args() == 0) continue; expr * const curr_first = curr_is_conc ? to_app(curr)->get_arg(0) : curr; expr * new_first = nullptr; if (is_numeral(curr_first, val, curr_first_sz)) { SASSERT(curr_first_sz >= removable); const unsigned new_num_sz = curr_first_sz - removable; new_first = new_num_sz ? mk_numeral(val, new_num_sz) : nullptr; } expr * new_arg = nullptr; if (curr_is_conc) { const unsigned conc_num = to_app(curr)->get_num_args(); if (new_first) { new_concat_args.reset(); new_concat_args.push_back(new_first); for (unsigned j = 1; j < conc_num; ++j) new_concat_args.push_back(to_app(curr)->get_arg(j)); new_arg = m_util.mk_concat(new_concat_args.size(), new_concat_args.c_ptr()); } else { // remove first element of concat expr * const * const old_conc_args = to_app(curr)->get_args(); switch (conc_num) { case 0: UNREACHABLE(); break; case 1: new_arg = nullptr; break; case 2: new_arg = to_app(curr)->get_arg(1); break; default: new_arg = m_util.mk_concat(conc_num - 1, old_conc_args + 1); } } } else { new_arg = new_first; } if (new_arg) new_args.push_back(new_arg); } result = m().mk_app(get_fid(), a->get_decl()->get_decl_kind(), new_args.size(), new_args.c_ptr()); SASSERT(m_util.is_bv(result)); return removable; } br_status bv_rewriter::mk_extract(unsigned high, unsigned low, expr * arg, expr_ref & result) { unsigned sz = get_bv_size(arg); SASSERT(sz > 0); if (low == 0 && high == sz - 1) { result = arg; return BR_DONE; } numeral v; if (is_numeral(arg, v, sz)) { sz = high - low + 1; if (v.is_neg()) mod(v, rational::power_of_two(sz), v); if (v.is_uint64()) { uint64_t u = v.get_uint64(); uint64_t e = shift_right(u, low) & (shift_left(1ull, sz) - 1ull); result = mk_numeral(numeral(e, numeral::ui64()), sz); return BR_DONE; } div(v, rational::power_of_two(low), v); result = mk_numeral(v, sz); return BR_DONE; } // (extract[high:low] (extract[high2:low2] x)) == (extract[high+low2 : low+low2] x) if (m_util.is_extract(arg)) { unsigned low2 = m_util.get_extract_low(arg); result = m_mk_extract(high + low2, low + low2, to_app(arg)->get_arg(0)); return BR_DONE; } // (extract (concat ....)) --> (concat (extract ...) ... (extract ...) ) if (m_util.is_concat(arg)) { unsigned num = to_app(arg)->get_num_args(); unsigned idx = sz; for (unsigned i = 0; i < num; i++) { expr * curr = to_app(arg)->get_arg(i); unsigned curr_sz = get_bv_size(curr); idx -= curr_sz; if (idx > high) continue; // found first argument if (idx <= low) { // result is a fragment of this argument if (low == idx && high - idx == curr_sz - 1) { result = curr; return BR_DONE; } else { result = m_mk_extract(high - idx, low - idx, curr); return BR_REWRITE1; } } else { // look for remaining arguments ptr_buffer new_args; bool used_extract = false; if (high - idx == curr_sz - 1) { new_args.push_back(curr); } else { used_extract = true; new_args.push_back(m_mk_extract(high - idx, 0, curr)); } for (unsigned j = i + 1; j < num; j++) { curr = to_app(arg)->get_arg(j); unsigned curr_sz = get_bv_size(curr); idx -= curr_sz; if (idx > low) { new_args.push_back(curr); continue; } if (idx == low) { new_args.push_back(curr); result = m_util.mk_concat(new_args.size(), new_args.c_ptr()); return used_extract ? BR_REWRITE2 : BR_DONE; } new_args.push_back(m_mk_extract(curr_sz - 1, low - idx, curr)); result = m_util.mk_concat(new_args.size(), new_args.c_ptr()); return BR_REWRITE2; } UNREACHABLE(); } } UNREACHABLE(); } if (m_util.is_bv_not(arg) || m_util.is_bv_or(arg) || m_util.is_bv_xor(arg) || (low == 0 && (m_util.is_bv_add(arg) || m_util.is_bv_mul(arg)))) { ptr_buffer new_args; unsigned num = to_app(arg)->get_num_args(); for (unsigned i = 0; i < num; i++) { expr * curr = to_app(arg)->get_arg(i); new_args.push_back(m_mk_extract(high, low, curr)); } result = m().mk_app(get_fid(), to_app(arg)->get_decl()->get_decl_kind(), new_args.size(), new_args.c_ptr()); return BR_REWRITE2; } if (m_extract_prop && (high >= low)) { expr_ref ep_res(m()); const unsigned ep_rm = propagate_extract(high, arg, ep_res); if (ep_rm != 0) { result = m_mk_extract(high, low, ep_res); TRACE("extract_prop", tout << mk_ismt2_pp(arg, m()) << "\n[" << high <<"," << low << "]\n" << ep_rm << "---->\n" << mk_ismt2_pp(result.get(), m()) << "\n";); return BR_REWRITE2; } } // issue #2359 led to relaxing condition for propagating extract over ite. // It is propagted inwards only in the case that it leads to at most one // branch of ite to be expanded or if one of the expanded ite branches have a single // reference count. expr* c = nullptr, *t = nullptr, *e = nullptr; if (m().is_ite(arg, c, t, e) && (t->get_ref_count() == 1 || e->get_ref_count() == 1 || !m().is_ite(t) || !m().is_ite(e))) { result = m().mk_ite(c, m_mk_extract(high, low, t), m_mk_extract(high, low, e)); return BR_REWRITE2; } return BR_FAILED; } br_status bv_rewriter::mk_bv_shl(expr * arg1, expr * arg2, expr_ref & result) { numeral r1, r2; unsigned bv_size = get_bv_size(arg1); unsigned sz; if (is_numeral(arg2, r2, sz)) { if (r2.is_zero()) { // x << 0 == x result = arg1; return BR_DONE; } if (r2 >= numeral(bv_size)) { result = mk_numeral(0, bv_size); return BR_DONE; } if (is_numeral(arg1, r1, sz)) { if (bv_size <= 64) { SASSERT(r1.is_uint64() && r2.is_uint64()); SASSERT(r2.get_uint64() < bv_size); uint64_t r = shift_left(r1.get_uint64(), r2.get_uint64()); numeral rn(r, numeral::ui64()); rn = m_util.norm(rn, bv_size); result = mk_numeral(rn, bv_size); return BR_DONE; } SASSERT(r2 < numeral(bv_size)); SASSERT(r2.is_unsigned()); r1 = m_util.norm(r1 * rational::power_of_two(r2.get_unsigned()), bv_size); result = mk_numeral(r1, bv_size); return BR_DONE; } SASSERT(r2.is_pos()); SASSERT(r2 < numeral(bv_size)); // (bvshl x k) -> (concat (extract [n-1-k:0] x) bv0:k) unsigned k = r2.get_unsigned(); expr * new_args[2] = { m_mk_extract(bv_size - k - 1, 0, arg1), mk_numeral(0, k) }; result = m_util.mk_concat(2, new_args); return BR_REWRITE2; } return BR_FAILED; } br_status bv_rewriter::mk_bv_lshr(expr * arg1, expr * arg2, expr_ref & result) { numeral r1, r2; unsigned bv_size = get_bv_size(arg1); unsigned sz; if (is_numeral(arg2, r2, sz)) { if (r2.is_zero()) { // x >> 0 == x result = arg1; return BR_DONE; } if (r2 >= numeral(bv_size)) { result = mk_numeral(0, bv_size); return BR_DONE; } if (is_numeral(arg1, r1, sz)) { if (bv_size <= 64) { SASSERT(r1.is_uint64()); SASSERT(r2.is_uint64()); uint64_t r = shift_right(r1.get_uint64(), r2.get_uint64()); numeral rn(r, numeral::ui64()); rn = m_util.norm(rn, bv_size); result = mk_numeral(rn, bv_size); return BR_DONE; } SASSERT(r2.is_unsigned()); unsigned sh = r2.get_unsigned(); div(r1, rational::power_of_two(sh), r1); result = mk_numeral(r1, bv_size); return BR_DONE; } SASSERT(r2.is_pos()); SASSERT(r2 < numeral(bv_size)); // (bvlshr x k) -> (concat bv0:k (extract [n-1:k] x)) SASSERT(r2.is_unsigned()); unsigned k = r2.get_unsigned(); expr * new_args[2] = { mk_numeral(0, k), m_mk_extract(bv_size - 1, k, arg1) }; result = m_util.mk_concat(2, new_args); return BR_REWRITE2; } return BR_FAILED; } br_status bv_rewriter::mk_bv_ashr(expr * arg1, expr * arg2, expr_ref & result) { numeral r1, r2; unsigned bv_size = get_bv_size(arg1); SASSERT(bv_size > 0); bool is_num2 = is_numeral(arg2, r2, bv_size); if (is_num2 && r2.is_zero()) { result = arg1; return BR_DONE; } bool is_num1 = is_numeral(arg1, r1, bv_size); if (bv_size <= 64 && is_num1 && is_num2) { uint64_t n1 = r1.get_uint64(); uint64_t n2_orig = r2.get_uint64(); uint64_t n2 = n2_orig % bv_size; SASSERT(n2 < bv_size); uint64_t r = shift_right(n1, n2); bool sign = (n1 & shift_left(1ull, bv_size - 1ull)) != 0; if (n2_orig > n2) { if (sign) { r = shift_left(1ull, bv_size) - 1ull; } else { r = 0; } } else if (sign) { uint64_t allone = shift_left(1ull, bv_size) - 1ull; uint64_t mask = ~(shift_left(1ull, bv_size - n2) - 1ull); mask &= allone; r |= mask; } result = mk_numeral(numeral(r, numeral::ui64()), bv_size); return BR_DONE; } if (is_num1 && is_num2 && numeral(bv_size) <= r2) { if (m_util.has_sign_bit(r1, bv_size)) result = mk_numeral(rational::power_of_two(bv_size) - numeral(1), bv_size); else result = mk_numeral(0, bv_size); return BR_DONE; } if (is_num1 && is_num2) { SASSERT(r2 < numeral(bv_size)); bool sign = m_util.has_sign_bit(r1, bv_size); div(r1, rational::power_of_two(r2.get_unsigned()), r1); if (sign) { // pad ones. numeral p(1); for (unsigned i = 0; i < bv_size; ++i) { if (r1 < p) { r1 += p; } p *= numeral(2); } } result = mk_numeral(r1, bv_size); return BR_DONE; } // (bvashr (bvashr x r1) r2) --> (bvashr x r1+r2) if (is_num2 && m_util.is_bv_ashr(arg1) && is_numeral(to_app(arg1)->get_arg(1), r1, bv_size)) { r1 += r2; if (r1 > numeral(bv_size)) r1 = numeral(bv_size); result = m().mk_app(get_fid(), OP_BASHR, to_app(arg1)->get_arg(0), mk_numeral(r1, bv_size)); return BR_REWRITE1; // not really needed at this time. } #if 0 // (bvashr x k) --> (concat extract[sz-1:sz-1](x) ... extract[sz-1:sz-1](x) extract[sz-1:k](x)) if (is_num2) { ptr_buffer new_args; if (r2 > numeral(bv_size)) r2 = numeral(bv_size); SASSERT(r2 <= numeral(bv_size)); unsigned k = r2.get_unsigned(); expr * sign = m_mk_extract(bv_size-1, bv_size-1, arg1); for (unsigned i = 0; i < k; i++) new_args.push_back(sign); if (k != bv_size) new_args.push_back(m_mk_extract(bv_size-1, k, arg1)); result = m_util.mk_concat(new_args.size(), new_args.c_ptr()); return BR_REWRITE2; } #endif return BR_FAILED; } br_status bv_rewriter::mk_bv_sdiv_core(expr * arg1, expr * arg2, bool hi_div0, expr_ref & result) { numeral r1, r2; unsigned bv_size; if (is_numeral(arg2, r2, bv_size)) { r2 = m_util.norm(r2, bv_size, true); if (r2.is_zero()) { if (!hi_div0) { result = m().mk_app(get_fid(), OP_BSDIV0, arg1); return BR_REWRITE1; } else { // The "hardware interpretation" for (bvsdiv x 0) is (ite (bvslt x #x0000) #x0001 #xffff) result = m().mk_ite(m().mk_app(get_fid(), OP_SLT, arg1, mk_numeral(0, bv_size)), mk_numeral(1, bv_size), mk_numeral(rational::power_of_two(bv_size) - numeral(1), bv_size)); return BR_REWRITE2; } } if (r2.is_one()) { result = arg1; return BR_DONE; } if (!r2.is_zero() && is_numeral(arg1, r1, bv_size)) { r1 = m_util.norm(r1, bv_size, true); result = mk_numeral(machine_div(r1, r2), bv_size); return BR_DONE; } result = m().mk_app(get_fid(), OP_BSDIV_I, arg1, arg2); return BR_DONE; } if (hi_div0) { result = m().mk_app(get_fid(), OP_BSDIV_I, arg1, arg2); return BR_DONE; } bv_size = get_bv_size(arg2); result = m().mk_ite(m().mk_eq(arg2, mk_numeral(0, bv_size)), m().mk_app(get_fid(), OP_BSDIV0, arg1), m().mk_app(get_fid(), OP_BSDIV_I, arg1, arg2)); return BR_REWRITE2; } br_status bv_rewriter::mk_bv_udiv_core(expr * arg1, expr * arg2, bool hi_div0, expr_ref & result) { numeral r1, r2; unsigned bv_size; TRACE("bv_udiv", tout << "hi_div0: " << hi_div0 << "\n";); TRACE("udiv2mul", tout << mk_ismt2_pp(arg2, m()) << " udiv2mul: " << m_udiv2mul << "\n";); if (is_numeral(arg2, r2, bv_size)) { r2 = m_util.norm(r2, bv_size); if (r2.is_zero()) { if (!hi_div0) { result = m().mk_app(get_fid(), OP_BUDIV0, arg1); return BR_REWRITE1; } else { // The "hardware interpretation" for (bvudiv x 0) is #xffff result = mk_numeral(rational::power_of_two(bv_size) - numeral(1), bv_size); return BR_DONE; } } if (r2.is_one()) { result = arg1; return BR_DONE; } if (!r2.is_zero() && is_numeral(arg1, r1, bv_size)) { r1 = m_util.norm(r1, bv_size); result = mk_numeral(machine_div(r1, r2), bv_size); return BR_DONE; } unsigned shift; if (r2.is_power_of_two(shift)) { result = m().mk_app(get_fid(), OP_BLSHR, arg1, mk_numeral(shift, bv_size)); return BR_REWRITE1; } if (m_udiv2mul) { TRACE("udiv2mul", tout << "using udiv2mul\n";); numeral inv_r2; if (m_util.mult_inverse(r2, bv_size, inv_r2)) { result = m().mk_app(get_fid(), OP_BMUL, mk_numeral(inv_r2, bv_size), arg1); return BR_REWRITE1; } } result = m().mk_app(get_fid(), OP_BUDIV_I, arg1, arg2); return BR_DONE; } if (hi_div0) { result = m().mk_app(get_fid(), OP_BUDIV_I, arg1, arg2); return BR_DONE; } bv_size = get_bv_size(arg2); result = m().mk_ite(m().mk_eq(arg2, mk_numeral(0, bv_size)), m().mk_app(get_fid(), OP_BUDIV0, arg1), m().mk_app(get_fid(), OP_BUDIV_I, arg1, arg2)); TRACE("bv_udiv", tout << mk_ismt2_pp(arg1, m()) << "\n" << mk_ismt2_pp(arg2, m()) << "\n---->\n" << mk_ismt2_pp(result, m()) << "\n";); return BR_REWRITE2; } br_status bv_rewriter::mk_bv_srem_core(expr * arg1, expr * arg2, bool hi_div0, expr_ref & result) { numeral r1, r2; unsigned bv_size; if (is_numeral(arg2, r2, bv_size)) { r2 = m_util.norm(r2, bv_size, true); if (r2.is_zero()) { if (!hi_div0) { result = m().mk_app(get_fid(), OP_BSREM0, arg1); return BR_REWRITE1; } else { // The "hardware interpretation" for (bvsrem x 0) is x result = arg1; return BR_DONE; } } if (r2.is_one()) { result = mk_numeral(0, bv_size); return BR_DONE; } if (!r2.is_zero() && is_numeral(arg1, r1, bv_size)) { r1 = m_util.norm(r1, bv_size, true); result = mk_numeral(r1 % r2, bv_size); return BR_DONE; } result = m().mk_app(get_fid(), OP_BSREM_I, arg1, arg2); return BR_DONE; } if (hi_div0) { result = m().mk_app(get_fid(), OP_BSREM_I, arg1, arg2); return BR_DONE; } bv_size = get_bv_size(arg2); result = m().mk_ite(m().mk_eq(arg2, mk_numeral(0, bv_size)), m().mk_app(get_fid(), OP_BSREM0, arg1), m().mk_app(get_fid(), OP_BSREM_I, arg1, arg2)); return BR_REWRITE2; } bool bv_rewriter::is_minus_one_core(expr * arg) const { numeral r; unsigned bv_size; if (is_numeral(arg, r, bv_size)) { return r == (rational::power_of_two(bv_size) - numeral(1)); } return false; } bool bv_rewriter::is_negatable(expr * arg, expr_ref& x) { numeral r; unsigned bv_size; if (is_numeral(arg, r, bv_size)) { r = bitwise_not(bv_size, r); x = mk_numeral(r, bv_size); return true; } if (m_util.is_bv_not(arg)) { SASSERT(to_app(arg)->get_num_args() == 1); x = to_app(arg)->get_arg(0); return true; } return false; } bool bv_rewriter::is_x_minus_one(expr * arg, expr * & x) { if (is_add(arg) && to_app(arg)->get_num_args() == 2) { if (is_minus_one_core(to_app(arg)->get_arg(0))) { x = to_app(arg)->get_arg(1); return true; } if (is_minus_one_core(to_app(arg)->get_arg(1))) { x = to_app(arg)->get_arg(0); return true; } } return false; } br_status bv_rewriter::mk_bv_urem_core(expr * arg1, expr * arg2, bool hi_div0, expr_ref & result) { numeral r1, r2; unsigned bv_size; bool is_num1 = is_numeral(arg1, r1, bv_size); if (is_numeral(arg2, r2, bv_size)) { r2 = m_util.norm(r2, bv_size); if (r2.is_zero()) { if (!hi_div0) { result = m().mk_app(get_fid(), OP_BUREM0, arg1); return BR_REWRITE1; } else { // The "hardware interpretation" for (bvurem x 0) is x result = arg1; return BR_DONE; } } if (r2.is_one()) { result = mk_numeral(0, bv_size); return BR_DONE; } if (!r2.is_zero() && is_num1) { r1 = m_util.norm(r1, bv_size); r1 %= r2; result = mk_numeral(r1, bv_size); return BR_DONE; } unsigned shift; if (r2.is_power_of_two(shift)) { expr * args[2] = { mk_numeral(0, bv_size - shift), m_mk_extract(shift-1, 0, arg1) }; result = m_util.mk_concat(2, args); return BR_REWRITE2; } result = m().mk_app(get_fid(), OP_BUREM_I, arg1, arg2); return BR_DONE; } if (!hi_div0) { // urem(0, x) ==> ite(x = 0, urem0(x), 0) if (is_num1 && r1.is_zero()) { expr * zero = arg1; result = m().mk_ite(m().mk_eq(arg2, zero), m().mk_app(get_fid(), OP_BUREM0, zero), zero); return BR_REWRITE2; } // urem(x - 1, x) ==> ite(x = 0, urem0(x-1), x - 1) ==> ite(x = 0, urem0(-1), x - 1) expr * x; if (is_x_minus_one(arg1, x) && x == arg2) { bv_size = get_bv_size(arg1); expr * x_minus_1 = arg1; expr * minus_one = mk_numeral(rational::power_of_two(bv_size) - numeral(1), bv_size); result = m().mk_ite(m().mk_eq(x, mk_numeral(0, bv_size)), m().mk_app(get_fid(), OP_BUREM0, minus_one), x_minus_1); return BR_REWRITE2; } } else { // Remark: when HI_DIV0=true is used, (bvurem x 0) --> x if (is_num1 && r1.is_zero()) { // urem(0, x) --> 0 expr * zero = arg1; result = zero; return BR_DONE; } // urem(x - 1, x) --> x - 1 expr * x; if (is_x_minus_one(arg1, x) && x == arg2) { expr * x_minus_1 = arg1; result = x_minus_1; return BR_DONE; } } if (hi_div0) { result = m().mk_app(get_fid(), OP_BUREM_I, arg1, arg2); return BR_DONE; } bv_size = get_bv_size(arg2); result = m().mk_ite(m().mk_eq(arg2, mk_numeral(0, bv_size)), m().mk_app(get_fid(), OP_BUREM0, arg1), m().mk_app(get_fid(), OP_BUREM_I, arg1, arg2)); return BR_REWRITE2; } br_status bv_rewriter::mk_bv_smod_core(expr * arg1, expr * arg2, bool hi_div0, expr_ref & result) { numeral r1, r2; unsigned bv_size; bool is_num1 = is_numeral(arg1, r1, bv_size); if (is_num1) { r1 = m_util.norm(r1, bv_size, true); if (r1.is_zero()) { result = m().mk_app(get_fid(), OP_BUREM, arg1, arg2); return BR_REWRITE1; } } if (is_numeral(arg2, r2, bv_size)) { r2 = m_util.norm(r2, bv_size, true); if (r2.is_zero()) { if (!hi_div0) result = m().mk_app(get_fid(), OP_BSMOD0, arg1); else result = arg1; return BR_DONE; } if (is_num1) { numeral abs_r1 = m_util.norm(abs(r1), bv_size); numeral abs_r2 = m_util.norm(abs(r2), bv_size); numeral u = m_util.norm(abs_r1 % abs_r2, bv_size); numeral r; if (u.is_zero()) r = u; else if (r1.is_pos() && r2.is_pos()) r = u; else if (r1.is_neg() && r2.is_pos()) r = m_util.norm(-u + r2, bv_size); else if (r1.is_pos() && r2.is_neg()) r = m_util.norm(u + r2, bv_size); else r = m_util.norm(-u, bv_size); result = mk_numeral(r, bv_size); return BR_DONE; } if (r2.is_one()) { // (bvsmod x 1) --> 0 result = mk_numeral(0, bv_size); return BR_REWRITE2; } } if (hi_div0) { result = m().mk_app(get_fid(), OP_BSMOD_I, arg1, arg2); return BR_DONE; } bv_size = get_bv_size(arg2); result = m().mk_ite(m().mk_eq(arg2, mk_numeral(0, bv_size)), m().mk_app(get_fid(), OP_BSMOD0, arg1), m().mk_app(get_fid(), OP_BSMOD_I, arg1, arg2)); return BR_REWRITE2; } br_status bv_rewriter::mk_int2bv(unsigned bv_size, expr * arg, expr_ref & result) { numeral val; bool is_int; if (m_autil.is_numeral(arg, val, is_int)) { val = m_util.norm(val, bv_size); result = mk_numeral(val, bv_size); return BR_DONE; } // (int2bv (bv2int x)) --> x if (m_util.is_bv2int(arg) && bv_size == get_bv_size(to_app(arg)->get_arg(0))) { result = to_app(arg)->get_arg(0); return BR_DONE; } return BR_FAILED; } br_status bv_rewriter::mk_bv2int(expr * arg, expr_ref & result) { numeral v; unsigned sz; if (is_numeral(arg, v, sz)) { result = m_autil.mk_numeral(v, true); return BR_DONE; } if (m_util.is_concat(arg)) { if (to_app(arg)->get_num_args() == 0) { result = m_autil.mk_int(0); return BR_DONE; } expr_ref_vector args(m()); unsigned num_args = to_app(arg)->get_num_args(); for (expr* x : *to_app(arg)) { args.push_back(m_util.mk_bv2int(x)); } unsigned sz = get_bv_size(to_app(arg)->get_arg(num_args-1)); for (unsigned i = num_args - 1; i > 0; ) { expr_ref tmp(m()); --i; tmp = args[i].get(); tmp = m_autil.mk_mul(m_autil.mk_numeral(power(numeral(2), sz), true), tmp); args[i] = std::move(tmp); sz += get_bv_size(to_app(arg)->get_arg(i)); } result = m_autil.mk_add(args.size(), args.c_ptr()); return BR_REWRITE2; } if (is_mul_no_overflow(arg)) { expr_ref_vector args(m()); for (expr* x : *to_app(arg)) args.push_back(m_util.mk_bv2int(x)); result = m_autil.mk_mul(args.size(), args.c_ptr()); return BR_REWRITE2; } if (is_add_no_overflow(arg)) { expr_ref_vector args(m()); for (expr* x : *to_app(arg)) args.push_back(m_util.mk_bv2int(x)); result = m_autil.mk_add(args.size(), args.c_ptr()); return BR_REWRITE2; } return BR_FAILED; } bool bv_rewriter::is_mul_no_overflow(expr* e) { if (!m_util.is_bv_mul(e)) return false; unsigned sz = get_bv_size(e); unsigned sum = 0; for (expr* x : *to_app(e)) sum += sz-num_leading_zero_bits(x); return sum < sz; } bool bv_rewriter::is_add_no_overflow(expr* e) { if (!is_add(e)) return false; for (expr* x : *to_app(e)) { if (0 == num_leading_zero_bits(x)) return false; } return true; } unsigned bv_rewriter::num_leading_zero_bits(expr* e) { numeral v; unsigned sz = get_bv_size(e); if (m_util.is_numeral(e, v)) { while (v.is_pos()) { SASSERT(sz > 0); --sz; v = div(v, numeral(2)); } return sz; } else if (m_util.is_concat(e)) { app* a = to_app(e); unsigned sz1 = get_bv_size(a->get_arg(0)); unsigned nb1 = num_leading_zero_bits(a->get_arg(0)); if (sz1 == nb1) { nb1 += num_leading_zero_bits(a->get_arg(1)); } return nb1; } return 0; } br_status bv_rewriter::mk_concat(unsigned num_args, expr * const * args, expr_ref & result) { expr_ref_buffer new_args(m()); numeral v1; numeral v2; unsigned sz1, sz2; bool fused_numeral = false; bool expanded = false; bool fused_extract = false; for (unsigned i = 0; i < num_args; i++) { expr * arg = args[i]; expr * prev = nullptr; if (i > 0) prev = new_args.back(); if (is_numeral(arg, v1, sz1) && prev != nullptr && is_numeral(prev, v2, sz2)) { v2 *= rational::power_of_two(sz1); v2 += v1; new_args.pop_back(); new_args.push_back(mk_numeral(v2, sz1+sz2)); fused_numeral = true; } else if (m_flat && m_util.is_concat(arg)) { unsigned num2 = to_app(arg)->get_num_args(); for (unsigned j = 0; j < num2; j++) { new_args.push_back(to_app(arg)->get_arg(j)); } expanded = true; } else if (m_util.is_extract(arg) && prev != nullptr && m_util.is_extract(prev) && to_app(arg)->get_arg(0) == to_app(prev)->get_arg(0) && m_util.get_extract_low(prev) == m_util.get_extract_high(arg) + 1) { // (concat (extract[h1,l1] a) (extract[h2,l2] a)) --> (extract[h1,l2] a) if l1 == h2+1 expr * new_arg = m_mk_extract(m_util.get_extract_high(prev), m_util.get_extract_low(arg), to_app(arg)->get_arg(0)); new_args.pop_back(); new_args.push_back(new_arg); fused_extract = true; } else { new_args.push_back(arg); } } if (!fused_numeral && !expanded && !fused_extract) return BR_FAILED; SASSERT(!new_args.empty()); if (new_args.size() == 1) { result = new_args.back(); return fused_extract ? BR_REWRITE1 : BR_DONE; } result = m_util.mk_concat(new_args.size(), new_args.c_ptr()); if (fused_extract) return BR_REWRITE2; else if (expanded) return BR_REWRITE1; else return BR_DONE; } br_status bv_rewriter::mk_zero_extend(unsigned n, expr * arg, expr_ref & result) { if (n == 0) { result = arg; return BR_DONE; } else { expr * args[2] = { mk_numeral(0, n), arg }; result = m_util.mk_concat(2, args); return BR_REWRITE1; } } br_status bv_rewriter::mk_sign_extend(unsigned n, expr * arg, expr_ref & result) { if (n == 0) { result = arg; return BR_DONE; } numeral r; unsigned bv_size; if (is_numeral(arg, r, bv_size)) { unsigned result_bv_size = bv_size + n; r = m_util.norm(r, bv_size, true); mod(r, rational::power_of_two(result_bv_size), r); result = mk_numeral(r, result_bv_size); return BR_DONE; } if (m_elim_sign_ext) { unsigned sz = get_bv_size(arg); expr * sign = m_mk_extract(sz-1, sz-1, arg); ptr_buffer args; for (unsigned i = 0; i < n; i++) args.push_back(sign); args.push_back(arg); result = m_util.mk_concat(args.size(), args.c_ptr()); return BR_REWRITE2; } return BR_FAILED; } br_status bv_rewriter::mk_repeat(unsigned n, expr * arg, expr_ref & result) { if (n == 1) { result = arg; return BR_DONE; } ptr_buffer args; for (unsigned i = 0; i < n; i++) args.push_back(arg); result = m_util.mk_concat(args.size(), args.c_ptr()); return BR_REWRITE1; } br_status bv_rewriter::mk_bv_or(unsigned num, expr * const * args, expr_ref & result) { SASSERT(num > 0); if (num == 1) { result = args[0]; return BR_DONE; } unsigned sz = get_bv_size(args[0]); bool flattened = false; ptr_buffer flat_args; if (m_flat) { for (unsigned i = 0; i < num; i++) { expr * arg = args[i]; if (m_util.is_bv_or(arg)) { unsigned num2 = to_app(arg)->get_num_args(); for (unsigned j = 0; j < num2; j++) flat_args.push_back(to_app(arg)->get_arg(j)); } else { flat_args.push_back(arg); } } if (flat_args.size() != num) { flattened = true; num = flat_args.size(); args = flat_args.c_ptr(); } } ptr_buffer new_args; expr_fast_mark1 pos_args; expr_fast_mark2 neg_args; bool merged = false; unsigned num_coeffs = 0; numeral v1, v2; for (unsigned i = 0; i < num; i++) { expr * arg = args[i]; if (is_numeral(arg, v2, sz)) { num_coeffs++; v1 = bitwise_or(v1, v2); continue; } if (m_util.is_bv_not(arg)) { expr * atom = to_app(arg)->get_arg(0); if (pos_args.is_marked(atom)) { result = mk_numeral(rational::power_of_two(sz) - numeral(1), sz); return BR_DONE; } else if (neg_args.is_marked(atom)) { merged = true; continue; } neg_args.mark(atom, true); new_args.push_back(arg); } else { if (pos_args.is_marked(arg)) { merged = true; continue; } else if (neg_args.is_marked(arg)) { result = mk_numeral(rational::power_of_two(sz) - numeral(1), sz); return BR_DONE; } pos_args.mark(arg, true); new_args.push_back(arg); } } if (v1 == rational::power_of_two(sz) - numeral(1)) { result = mk_numeral(v1, sz); return BR_DONE; } // Simplifications of the form: // (bvor (concat x #x00) (concat #x00 y)) --> (concat x y) if (new_args.size() == 2 && num_coeffs == 0 && m_util.is_concat(new_args[0]) && m_util.is_concat(new_args[1])) { app * concat1 = to_app(new_args[0]); app * concat2 = to_app(new_args[1]); unsigned i = 0; for (i = 0; i < sz; i++) if (!is_zero_bit(concat1, i) && !is_zero_bit(concat2, i)) break; if (i == sz) { // is target ptr_buffer non_zero_args; int j = sz; j--; while (j >= 0) { int high = j; while (j >= 0 && is_zero_bit(concat1, j)) --j; if (j != high) non_zero_args.push_back(m_mk_extract(high, j+1, concat2)); high = j; while (j >= 0 && is_zero_bit(concat2, j)) --j; if (j != high) non_zero_args.push_back(m_mk_extract(high, j+1, concat1)); } result = m_util.mk_concat(non_zero_args.size(), non_zero_args.c_ptr()); return BR_REWRITE2; } } if (!v1.is_zero() && new_args.size() == 1) { v1 = m_util.norm(v1, sz); #ifdef _TRACE numeral old_v1 = v1; #endif // OR is a mask expr * t = new_args[0]; numeral two(2); ptr_buffer exs; unsigned low = 0; unsigned i = 0; while (i < sz) { while (i < sz && mod(v1, two).is_one()) { i++; div(v1, two, v1); } if (i != low) { unsigned num_sz = i - low; exs.push_back(m_util.mk_numeral(rational::power_of_two(num_sz) - numeral(1), num_sz)); low = i; } while (i < sz && mod(v1, two).is_zero()) { i++; div(v1, two, v1); } if (i != low) { exs.push_back(m_mk_extract(i-1, low, t)); low = i; } } std::reverse(exs.begin(), exs.end()); result = m_util.mk_concat(exs.size(), exs.c_ptr()); TRACE("mask_bug", tout << "(assert (distinct (bvor (_ bv" << old_v1 << " " << sz << ")\n" << mk_ismt2_pp(t, m()) << ")\n"; tout << mk_ismt2_pp(result, m()) << "))\n";); return BR_REWRITE2; } if (!flattened && !merged && (num_coeffs == 0 || (num_coeffs == 1 && !v1.is_zero())) && (!m_bv_sort_ac || is_sorted(num, args))) { return BR_FAILED; } if (!v1.is_zero()) { new_args.push_back(mk_numeral(v1, sz)); } switch (new_args.size()) { case 0: result = mk_numeral(0, sz); return BR_DONE; case 1: result = new_args[0]; return BR_DONE; default: if (m_bv_sort_ac) std::sort(new_args.begin(), new_args.end(), ast_to_lt()); if (distribute_concat(OP_BOR, new_args.size(), new_args.c_ptr(), result)) { return BR_REWRITE3; } result = m_util.mk_bv_or(new_args.size(), new_args.c_ptr()); return BR_DONE; } } br_status bv_rewriter::mk_bv_xor(unsigned num, expr * const * args, expr_ref & result) { SASSERT(num > 0); if (num == 1) { result = args[0]; return BR_DONE; } unsigned sz = get_bv_size(args[0]); bool flattened = false; ptr_buffer flat_args; if (m_flat) { for (unsigned i = 0; i < num; i++) { expr * arg = args[i]; if (m_util.is_bv_xor(arg)) { unsigned num2 = to_app(arg)->get_num_args(); for (unsigned j = 0; j < num2; j++) flat_args.push_back(to_app(arg)->get_arg(j)); } else { flat_args.push_back(arg); } } if (flat_args.size() != num) { flattened = true; num = flat_args.size(); args = flat_args.c_ptr(); } } expr_fast_mark1 pos_args; expr_fast_mark2 neg_args; bool merged = false; numeral v1, v2; unsigned num_coeffs = 0; for (unsigned i = 0; i < num; i++) { expr * arg = args[i]; if (is_numeral(arg, v2, sz)) { v1 = bitwise_xor(v1, v2); num_coeffs++; continue; } if (m_util.is_bv_not(arg)) { expr * atom = to_app(arg)->get_arg(0); if (neg_args.is_marked(atom)) { neg_args.mark(atom, false); merged = true; } else if (pos_args.is_marked(atom)) { pos_args.mark(atom, false); merged = true; v1 = bitwise_xor(v1, rational::power_of_two(sz) - numeral(1)); } else { neg_args.mark(atom, true); } } else { if (pos_args.is_marked(arg)) { pos_args.mark(arg, false); merged = true; } else if (neg_args.is_marked(arg)) { neg_args.mark(arg, false); merged = true; v1 = bitwise_xor(v1, rational::power_of_two(sz) - numeral(1)); } else { pos_args.mark(arg, true); } } } // XOR is a mask // All arguments but one is a numeral. // // Apply a transformation of the form: // // (bvxor a 0011) --> (concat ((_ extract 3 2) a) ((_ extract 1 0) (bvnot a))) // if (!v1.is_zero() && num_coeffs == num - 1) { // find argument that is not a numeral expr * t = nullptr; for (unsigned i = 0; i < num; i++) { t = args[i]; if (!is_numeral(t)) break; } SASSERT(t != 0); numeral two(2); expr_ref_buffer exs(m()); expr_ref not_t(m()); not_t = m_util.mk_bv_not(t); unsigned low = 0; unsigned i = 0; while (i < sz) { while (i < sz && mod(v1, two).is_one()) { i++; div(v1, two, v1); } if (i != low) { exs.push_back(m_mk_extract(i-1, low, not_t)); low = i; } while (i < sz && mod(v1, two).is_zero()) { i++; div(v1, two, v1); } if (i != low) { exs.push_back(m_mk_extract(i-1, low, t)); low = i; } } std::reverse(exs.c_ptr(), exs.c_ptr() + exs.size()); if (exs.size() == 1) result = exs[0]; else result = m_util.mk_concat(exs.size(), exs.c_ptr()); return BR_REWRITE3; } if (!merged && !flattened && (num_coeffs == 0 || (num_coeffs == 1 && !v1.is_zero() && v1 != (rational::power_of_two(sz) - numeral(1)))) && (!m_bv_sort_ac || is_sorted(num, args))) { if (distribute_concat(OP_BXOR, num, args, result)) { return BR_REWRITE3; } return BR_FAILED; } ptr_buffer new_args; expr_ref c(m()); // may not be used if (!v1.is_zero()) { c = mk_numeral(v1, sz); new_args.push_back(c); } for (unsigned i = 0; i < num; i++) { expr * arg = args[i]; if (is_numeral(arg)) continue; if (m_util.is_bv_not(arg)) { expr * atom = to_app(arg)->get_arg(0); if (neg_args.is_marked(atom)) { new_args.push_back(arg); neg_args.mark(atom, false); } } else if (pos_args.is_marked(arg)) { new_args.push_back(arg); pos_args.mark(arg, false); } } switch (new_args.size()) { case 0: result = mk_numeral(0, sz); return BR_DONE; case 1: result = new_args[0]; return BR_DONE; case 2: if (m_util.is_allone(new_args[0])) { result = m_util.mk_bv_not(new_args[1]); return BR_DONE; } Z3_fallthrough; default: if (m_bv_sort_ac) std::sort(new_args.begin(), new_args.end(), ast_to_lt()); if (distribute_concat(OP_BXOR, new_args.size(), new_args.c_ptr(), result)) { return BR_REWRITE3; } result = m_util.mk_bv_xor(new_args.size(), new_args.c_ptr()); return BR_DONE; } } bool bv_rewriter::distribute_concat(decl_kind k, unsigned n, expr* const* args, expr_ref& result) { for (unsigned i = 0; i < n; ++i) { expr* arg = args[i]; if (m_util.is_concat(arg)) { expr* e = to_app(arg)->get_arg(0); unsigned sz1 = get_bv_size(e); unsigned sz2 = get_bv_size(arg); expr_ref_vector args1(m()), args2(m()); for (unsigned j = 0; j < n; ++j) { args1.push_back(m_mk_extract(sz2 - 1, sz2 - sz1, args[j])); args2.push_back(m_mk_extract(sz2 - sz1 - 1, 0, args[j])); } expr* arg1 = m().mk_app(get_fid(), k, args1.size(), args1.c_ptr()); expr* arg2 = m().mk_app(get_fid(), k, args2.size(), args2.c_ptr()); result = m_util.mk_concat(arg1, arg2); return true; } } return false; } br_status bv_rewriter::mk_bv_not(expr * arg, expr_ref & result) { if (m_util.is_bv_not(arg)) { result = to_app(arg)->get_arg(0); return BR_DONE; } numeral val; unsigned bv_size; if (is_numeral(arg, val, bv_size)) { val = bitwise_not(bv_size, val); result = mk_numeral(val, bv_size); return BR_DONE; } if (m_util.is_concat(arg)) { ptr_buffer new_args; for (expr* a : *to_app(arg)) { new_args.push_back(m_util.mk_bv_not(a)); } result = m_util.mk_concat(new_args.size(), new_args.c_ptr()); return BR_REWRITE2; } if (m_bvnot2arith) { // (bvnot x) --> (bvsub -1 x) bv_size = get_bv_size(arg); rational minus_one = (rational::power_of_two(bv_size) - numeral(1)); result = m_util.mk_bv_sub(m_util.mk_numeral(minus_one, bv_size), arg); return BR_REWRITE1; } if (m_bvnot_simpl) { expr *s(nullptr), *t(nullptr); if (m_util.is_bv_mul(arg, s, t)) { // ~(-1 * x) --> (x - 1) bv_size = m_util.get_bv_size(s); if (m_util.is_allone(s)) { rational minus_one = (rational::power_of_two(bv_size) - rational::one()); result = m_util.mk_bv_add(m_util.mk_numeral(minus_one, bv_size), t); return BR_REWRITE1; } if (m_util.is_allone(t)) { rational minus_one = (rational::power_of_two(bv_size) - rational::one()); result = m_util.mk_bv_add(m_util.mk_numeral(minus_one, bv_size), s); return BR_REWRITE1; } } if (m_util.is_bv_add(arg, s, t)) { expr_ref ns(m()); expr_ref nt(m()); // ~(x + y) --> (~x + ~y + 1) when x and y are easy to negate if (is_negatable(t, nt) && is_negatable(s, ns)) { bv_size = m_util.get_bv_size(s); expr * nargs[3] = { m_util.mk_numeral(rational::one(), bv_size), ns.get(), nt.get() }; result = m().mk_app(m_util.get_fid(), OP_BADD, 3, nargs); return BR_REWRITE1; } } } return BR_FAILED; } br_status bv_rewriter::mk_bv_and(unsigned num, expr * const * args, expr_ref & result) { ptr_buffer new_args; for (unsigned i = 0; i < num; i++) { new_args.push_back(m_util.mk_bv_not(args[i])); } SASSERT(num == new_args.size()); result = m_util.mk_bv_not(m_util.mk_bv_or(new_args.size(), new_args.c_ptr())); return BR_REWRITE3; } br_status bv_rewriter::mk_bv_nand(unsigned num, expr * const * args, expr_ref & result) { ptr_buffer new_args; for (unsigned i = 0; i < num; i++) { new_args.push_back(m_util.mk_bv_not(args[i])); } result = m_util.mk_bv_or(new_args.size(), new_args.c_ptr()); return BR_REWRITE2; } br_status bv_rewriter::mk_bv_nor(unsigned num, expr * const * args, expr_ref & result) { result = m_util.mk_bv_not(m_util.mk_bv_or(num, args)); return BR_REWRITE2; } br_status bv_rewriter::mk_bv_xnor(unsigned num_args, expr * const * args, expr_ref & result) { result = m_util.mk_bv_not(m_util.mk_bv_xor(num_args, args)); return BR_REWRITE2; } br_status bv_rewriter::mk_bv_rotate_left(unsigned n, expr * arg, expr_ref & result) { unsigned sz = get_bv_size(arg); SASSERT(sz > 0); n = n % sz; if (n == 0 || sz == 1) { result = arg; return BR_DONE; } expr * args[2] = { m_mk_extract(sz - n - 1, 0, arg), m_mk_extract(sz - 1, sz - n, arg) }; result = m_util.mk_concat(2, args); return BR_REWRITE2; } br_status bv_rewriter::mk_bv_rotate_right(unsigned n, expr * arg, expr_ref & result) { unsigned sz = get_bv_size(arg); SASSERT(sz > 0); n = n % sz; return mk_bv_rotate_left(sz - n, arg, result); } br_status bv_rewriter::mk_bv_ext_rotate_left(expr * arg1, expr * arg2, expr_ref & result) { numeral r2; unsigned bv_size; if (is_numeral(arg2, r2, bv_size)) { unsigned shift = static_cast((r2 % numeral(bv_size)).get_uint64() % static_cast(bv_size)); return mk_bv_rotate_left(shift, arg1, result); } return BR_FAILED; } br_status bv_rewriter::mk_bv_ext_rotate_right(expr * arg1, expr * arg2, expr_ref & result) { numeral r2; unsigned bv_size; if (is_numeral(arg2, r2, bv_size)) { unsigned shift = static_cast((r2 % numeral(bv_size)).get_uint64() % static_cast(bv_size)); return mk_bv_rotate_right(shift, arg1, result); } return BR_FAILED; } br_status bv_rewriter::mk_bv_redor(expr * arg, expr_ref & result) { if (is_numeral(arg)) { result = m_util.is_zero(arg) ? mk_numeral(0, 1) : mk_numeral(1, 1); return BR_DONE; } return BR_FAILED; } br_status bv_rewriter::mk_bv_redand(expr * arg, expr_ref & result) { numeral r; unsigned bv_size; if (is_numeral(arg, r, bv_size)) { result = (r == rational::power_of_two(bv_size) - numeral(1)) ? mk_numeral(1, 1) : mk_numeral(0, 1); return BR_DONE; } return BR_FAILED; } br_status bv_rewriter::mk_bv_comp(expr * arg1, expr * arg2, expr_ref & result) { if (arg1 == arg2) { result = mk_numeral(1,1); return BR_DONE; } if (is_numeral(arg1) && is_numeral(arg2)) { SASSERT(arg1 != arg2); result = mk_numeral(0, 1); return BR_DONE; } result = m().mk_ite(m().mk_eq(arg1, arg2), mk_numeral(1, 1), mk_numeral(0, 1)); return BR_REWRITE2; } br_status bv_rewriter::mk_bv_add(unsigned num_args, expr * const * args, expr_ref & result) { br_status st = mk_add_core(num_args, args, result); if (st != BR_FAILED && st != BR_DONE) { TRACE("bv", tout << result << "\n";); return st; } #if 0 expr * x; expr * y; if (st == BR_FAILED && num_args == 2) { x = args[0]; y = args[1]; } else if (st == BR_DONE && is_add(result) && to_app(result)->get_num_args() == 2) { x = to_app(result)->get_arg(0); y = to_app(result)->get_arg(1); } else { return st; } if (!m_util.is_concat(x) && !is_numeral(x)) return st; if (!m_util.is_concat(y) && !is_numeral(y)) return st; unsigned sz = get_bv_size(x); for (unsigned i = 0; i < sz; i++) { if (!is_zero_bit(x,i) && !is_zero_bit(y,i)) return st; } result = m().mk_app(get_fid(), OP_BOR, x, y); return BR_REWRITE1; #else unsigned _num_args; expr * const * _args; if (st == BR_FAILED) { _num_args = num_args; _args = args; } else if (st == BR_DONE && is_add(result)) { _num_args = to_app(result)->get_num_args(); _args = to_app(result)->get_args(); } else { return st; } if (_num_args < 2) return st; unsigned sz = get_bv_size(_args[0]); for (unsigned i = 0; i < sz; i++) { bool found_non_zero = false; for (unsigned j = 0; j < _num_args; j++) { if (!is_zero_bit(_args[j], i)) { // at most one of the arguments may have a non-zero bit. if (found_non_zero) return st; found_non_zero = true; } } } result = m().mk_app(get_fid(), OP_BOR, _num_args, _args); return BR_REWRITE1; #endif } bool bv_rewriter::is_zero_bit(expr * x, unsigned idx) { numeral val; unsigned bv_size; loop: if (is_numeral(x, val, bv_size)) { if (val.is_zero()) return true; div(val, rational::power_of_two(idx), val); return (val % numeral(2)).is_zero(); } if (m_util.is_concat(x)) { unsigned i = to_app(x)->get_num_args(); while (i > 0) { --i; expr * y = to_app(x)->get_arg(i); bv_size = get_bv_size(y); if (bv_size <= idx) { idx -= bv_size; } else { x = y; goto loop; } } UNREACHABLE(); } return false; } br_status bv_rewriter::mk_bv_mul(unsigned num_args, expr * const * args, expr_ref & result) { br_status st = mk_mul_core(num_args, args, result); if (st != BR_FAILED && st != BR_DONE) return st; expr * x; expr * y; if (st == BR_FAILED && num_args == 2) { x = args[0]; y = args[1]; } else if (st == BR_DONE && is_mul(result) && to_app(result)->get_num_args() == 2) { x = to_app(result)->get_arg(0); y = to_app(result)->get_arg(1); } else { return st; } if (m_mul2concat) { numeral v; unsigned bv_size; unsigned shift; if (is_numeral(x, v, bv_size) && v.is_power_of_two(shift)) { SASSERT(shift >= 1); expr * args[2] = { m_mk_extract(bv_size-shift-1, 0, y), mk_numeral(0, shift) }; result = m_util.mk_concat(2, args); return BR_REWRITE2; } } return st; } br_status bv_rewriter::mk_bit2bool(expr * n, int idx, expr_ref & result) { rational v, bit; unsigned sz = 0; if (m_util.is_mkbv(n)) { result = to_app(n)->get_arg(idx); return BR_DONE; } 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) return BR_FAILED; if (is_numeral(lhs)) std::swap(lhs, rhs); numeral v; if (!is_numeral(rhs, v, sz)) return BR_FAILED; if (is_numeral(lhs)) { SASSERT(is_numeral(rhs)); result = m().mk_bool_val(lhs == rhs); return BR_DONE; } if (m().is_ite(lhs)) { result = m().mk_ite(to_app(lhs)->get_arg(0), m().mk_eq(to_app(lhs)->get_arg(1), rhs), m().mk_eq(to_app(lhs)->get_arg(2), rhs)); return BR_REWRITE2; } if (m_util.is_bv_not(lhs)) { SASSERT(v.is_one() || v.is_zero()); result = m().mk_eq(to_app(lhs)->get_arg(0), mk_numeral(numeral(1) - v, 1)); return BR_REWRITE1; } bool is_one = v.is_one(); expr_ref bit1(m()); bit1 = is_one ? rhs : mk_numeral(numeral(1), 1); if (m_util.is_bv_or(lhs)) { ptr_buffer new_args; unsigned num = to_app(lhs)->get_num_args(); for (unsigned i = 0; i < num; i++) { new_args.push_back(m().mk_eq(to_app(lhs)->get_arg(i), bit1)); } result = m().mk_or(new_args.size(), new_args.c_ptr()); if (is_one) { return BR_REWRITE2; } else { result = m().mk_not(result); return BR_REWRITE3; } } if (m_util.is_bv_xor(lhs)) { ptr_buffer new_args; unsigned num = to_app(lhs)->get_num_args(); for (unsigned i = 0; i < num; i++) { new_args.push_back(m().mk_eq(to_app(lhs)->get_arg(i), bit1)); } // TODO: bool xor is not flat_assoc... must fix that. result = m().mk_xor(new_args.size(), new_args.c_ptr()); if (is_one) { return BR_REWRITE2; } else { result = m().mk_not(result); return BR_REWRITE3; } } return BR_FAILED; } br_status bv_rewriter::mk_blast_eq_value(expr * lhs, expr * rhs, expr_ref & result) { unsigned sz = get_bv_size(lhs); if (sz == 1) return BR_FAILED; TRACE("blast_eq_value", tout << "sz: " << sz << "\n" << mk_ismt2_pp(lhs, m()) << "\n";); if (is_numeral(lhs)) std::swap(lhs, rhs); numeral v; if (!is_numeral(rhs, v, sz)) return BR_FAILED; if (!m_util.is_bv_or(lhs) && !m_util.is_bv_xor(lhs) && !m_util.is_bv_not(lhs)) return BR_FAILED; numeral two(2); ptr_buffer new_args; for (unsigned i = 0; i < sz; i++) { bool bit0 = (v % two).is_zero(); new_args.push_back(m().mk_eq(m_mk_extract(i,i, lhs), mk_numeral(bit0 ? 0 : 1, 1))); div(v, two, v); } result = m().mk_and(new_args.size(), new_args.c_ptr()); return BR_REWRITE3; } br_status bv_rewriter::mk_eq_concat(expr * lhs, expr * rhs, expr_ref & result) { SASSERT(m_util.is_concat(lhs) || m_util.is_concat(rhs)); unsigned num1, num2; expr * const * args1, * const * args2; if (m_util.is_concat(lhs)) { num1 = to_app(lhs)->get_num_args(); args1 = to_app(lhs)->get_args(); } else { num1 = 1; args1 = &lhs; } if (m_util.is_concat(rhs)) { num2 = to_app(rhs)->get_num_args(); args2 = to_app(rhs)->get_args(); } else { num2 = 1; args2 = &rhs; } ptr_buffer new_eqs; unsigned low1 = 0; unsigned low2 = 0; unsigned i1 = num1; unsigned i2 = num2; while (i1 > 0 && i2 > 0) { expr * arg1 = args1[i1-1]; expr * arg2 = args2[i2-1]; unsigned sz1 = get_bv_size(arg1); unsigned sz2 = get_bv_size(arg2); SASSERT(low1 < sz1 && low2 < sz2); unsigned rsz1 = sz1 - low1; unsigned rsz2 = sz2 - low2; if (rsz1 == rsz2) { new_eqs.push_back(m().mk_eq(m_mk_extract(sz1 - 1, low1, arg1), m_mk_extract(sz2 - 1, low2, arg2))); low1 = 0; low2 = 0; --i1; --i2; continue; } else if (rsz1 < rsz2) { new_eqs.push_back(m().mk_eq(m_mk_extract(sz1 - 1, low1, arg1), m_mk_extract(rsz1 + low2 - 1, low2, arg2))); low1 = 0; low2 += rsz1; --i1; } else { new_eqs.push_back(m().mk_eq(m_mk_extract(rsz2 + low1 - 1, low1, arg1), m_mk_extract(sz2 - 1, low2, arg2))); low1 += rsz2; low2 = 0; --i2; } } SASSERT(i1 == 0 && i2 == 0); SASSERT(new_eqs.size() >= 1); result = m().mk_and(new_eqs.size(), new_eqs.c_ptr()); return BR_REWRITE3; } bool bv_rewriter::is_concat_split_target(expr * t) const { return m_split_concat_eq || m_util.is_concat(t) || m_util.is_numeral(t) || m_util.is_bv_or(t); } bool bv_rewriter::is_minus_one_times_t(expr * arg) { expr * t1, * t2; return (m_util.is_bv_mul(arg, t1, t2) && is_minus_one(t1)); } void bv_rewriter::mk_t1_add_t2_eq_c(expr * t1, expr * t2, expr * c, expr_ref & result) { SASSERT(is_numeral(c)); if (is_minus_one_times_t(t1)) result = m().mk_eq(t2, m_util.mk_bv_sub(c, t1)); else result = m().mk_eq(t1, m_util.mk_bv_sub(c, t2)); } #include "ast/ast_pp.h" bool bv_rewriter::isolate_term(expr* lhs, expr* rhs, expr_ref& result) { if (!m_util.is_numeral(lhs) || !is_add(rhs)) { std::swap(lhs, rhs); } if (!m_util.is_numeral(lhs) || !is_add(rhs)) { return false; } unsigned sz = to_app(rhs)->get_num_args(); expr * t1 = to_app(rhs)->get_arg(0); expr_ref t2(m()); if (sz > 2) { t2 = m().mk_app(get_fid(), OP_BADD, sz-1, to_app(rhs)->get_args()+1); } else { SASSERT(sz == 2); t2 = to_app(rhs)->get_arg(1); } mk_t1_add_t2_eq_c(t1, t2, lhs, result); return true; } bool bv_rewriter::is_add_mul_const(expr* e) const { if (!m_util.is_bv_add(e)) { return false; } unsigned num = to_app(e)->get_num_args(); for (unsigned i = 0; i < num; i++) { expr * arg = to_app(e)->get_arg(i); expr * c2, * x2; if (m_util.is_numeral(arg)) continue; if (m_util.is_bv_mul(arg, c2, x2) && m_util.is_numeral(c2)) continue; return false; } return true; } bool bv_rewriter::is_concat_target(expr* lhs, expr* rhs) const { return (m_util.is_concat(lhs) && is_concat_split_target(rhs)) || (m_util.is_concat(rhs) && is_concat_split_target(lhs)); } bool bv_rewriter::has_numeral(app* a) const { for (unsigned i = 0; i < a->get_num_args(); ++i) { if (is_numeral(a->get_arg(i))) { return true; } } return false; } br_status bv_rewriter::mk_mul_eq(expr * lhs, expr * rhs, expr_ref & result) { expr * c, * x; numeral c_val, c_inv_val; unsigned sz; if (m_util.is_bv_mul(lhs, c, x) && m_util.is_numeral(c, c_val, sz) && m_util.mult_inverse(c_val, sz, c_inv_val)) { SASSERT(m_util.norm(c_val * c_inv_val, sz).is_one()); numeral rhs_val; // c * x = a if (m_util.is_numeral(rhs, rhs_val, sz)) { // x = c_inv * a result = m().mk_eq(x, m_util.mk_numeral(c_inv_val * rhs_val, sz)); return BR_REWRITE1; } expr * c2, * x2; numeral c2_val; // c * x = c2 * x2 if (m_util.is_bv_mul(rhs, c2, x2) && m_util.is_numeral(c2, c2_val, sz)) { // x = c_inv * c2 * x2 numeral new_c2 = m_util.norm(c_inv_val * c2_val, sz); if (new_c2.is_one()) result = m().mk_eq(x, x2); else result = m().mk_eq(x, m_util.mk_bv_mul(m_util.mk_numeral(c_inv_val * c2_val, sz), x2)); return BR_REWRITE1; } // c * x = t_1 + ... + t_n // and t_i's have non-unary coefficients (this condition is used to make sure we are actually reducing the number of multipliers). if (is_add_mul_const(rhs)) { // Potential problem: this simplification may increase the number of adders by reducing the amount of sharing. result = m().mk_eq(x, m_util.mk_bv_mul(m_util.mk_numeral(c_inv_val, sz), rhs)); return BR_REWRITE2; } } if (m_util.is_numeral(lhs, c_val, sz) && is_add_mul_const(rhs)) { unsigned num_args = to_app(rhs)->get_num_args(); unsigned i = 0; expr* c2, *x2; numeral c2_val, c2_inv_val; bool found = false; for (; !found && i < num_args; ++i) { expr* arg = to_app(rhs)->get_arg(i); if (m_util.is_bv_mul(arg, c2, x2) && m_util.is_numeral(c2, c2_val, sz) && m_util.mult_inverse(c2_val, sz, c2_inv_val)) { found = true; } } if (found) { result = m().mk_eq(m_util.mk_numeral(c2_inv_val*c_val, sz), m_util.mk_bv_mul(m_util.mk_numeral(c2_inv_val, sz), rhs)); return BR_REWRITE3; } } return BR_FAILED; } bool bv_rewriter::is_urem_any(expr * e, expr * & dividend, expr * & divisor) { if (!m_util.is_bv_urem(e) && !m_util.is_bv_uremi(e)) return false; const app * const a = to_app(e); SASSERT(a->get_num_args() == 2); dividend = a->get_arg(0); divisor = a->get_arg(1); return true; } br_status bv_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result) { if (lhs == rhs) { result = m().mk_true(); return BR_DONE; } if (is_numeral(lhs) && is_numeral(rhs)) { result = m().mk_false(); return BR_DONE; } bool swapped = false; if (is_numeral(lhs)) { swapped = true; std::swap(lhs, rhs); } br_status st; if (m_bit2bool) { st = mk_bit2bool(lhs, rhs, result); if (st != BR_FAILED) return st; } if (m_trailing) { st = m_rm_trailing.eq_remove_trailing(lhs, rhs, result); m_rm_trailing.reset_cache(1 << 12); if (st != BR_FAILED) { TRACE("eq_remove_trailing", tout << mk_ismt2_pp(lhs, m()) << "\n=\n" << mk_ismt2_pp(rhs, m()) << "\n----->\n" << mk_ismt2_pp(result, m()) << "\n";); return st; } } st = mk_mul_eq(lhs, rhs, result); if (st != BR_FAILED) { TRACE("mk_mul_eq", tout << mk_ismt2_pp(lhs, m()) << "\n=\n" << mk_ismt2_pp(rhs, m()) << "\n----->\n" << mk_ismt2_pp(result,m()) << "\n";); return st; } st = mk_mul_eq(rhs, lhs, result); if (st != BR_FAILED) { TRACE("mk_mul_eq", tout << mk_ismt2_pp(lhs, m()) << "\n=\n" << mk_ismt2_pp(rhs, m()) << "\n----->\n" << mk_ismt2_pp(result,m()) << "\n";); return st; } if (m_blast_eq_value) { st = mk_blast_eq_value(lhs, rhs, result); if (st != BR_FAILED) return st; } if (m_urem_simpl) { expr * dividend; expr * divisor; numeral divisor_val, rhs_val; unsigned divisor_sz, rhs_sz; if (is_urem_any(lhs, dividend, divisor) && is_numeral(rhs, rhs_val, rhs_sz) && is_numeral(divisor, divisor_val, divisor_sz)) { if (rhs_val >= divisor_val) {//(= (bvurem x c1) c2) where c2 >= c1 result = m().mk_false(); return BR_DONE; } if ((divisor_val + rhs_val) >= rational::power_of_two(divisor_sz)) {//(= (bvurem x c1) c2) where c1+c2 >= 2^width result = m().mk_eq(dividend, rhs); return BR_REWRITE2; } } } expr_ref new_lhs(m()); expr_ref new_rhs(m()); if (m_util.is_bv_add(lhs) || m_util.is_bv_mul(lhs) || m_util.is_bv_add(rhs) || m_util.is_bv_mul(rhs)) { st = cancel_monomials(lhs, rhs, false, new_lhs, new_rhs); if (st != BR_FAILED) { if (is_numeral(new_lhs) && is_numeral(new_rhs)) { result = m().mk_bool_val(new_lhs == new_rhs); return BR_DONE; } lhs = new_lhs; rhs = new_rhs; } // Try to rewrite t1 + t2 = c --> t1 = c - t2 // Reason: it is much cheaper to bit-blast. if (isolate_term(lhs, rhs, result)) { return BR_REWRITE2; } if (is_concat_target(lhs, rhs)) { return mk_eq_concat(lhs, rhs, result); } if (st != BR_FAILED) { result = m().mk_eq(lhs, rhs); return BR_DONE; } } if (is_concat_target(lhs, rhs)) { return mk_eq_concat(lhs, rhs, result); } if (swapped) { result = m().mk_eq(lhs, rhs); return BR_DONE; } return BR_FAILED; } br_status bv_rewriter::mk_mkbv(unsigned num, expr * const * args, expr_ref & result) { if (m_mkbv2num) { unsigned i; for (i = 0; i < num; i++) if (!m().is_true(args[i]) && !m().is_false(args[i])) return BR_FAILED; numeral val; numeral two(2); i = num; while (i > 0) { --i; val *= two; if (m().is_true(args[i])) val++; } result = mk_numeral(val, num); return BR_DONE; } return BR_FAILED; } br_status bv_rewriter::mk_ite_core(expr * c, expr * t, expr * e, expr_ref & result) { TRACE("bv_ite", tout << "mk_ite_core:\n" << mk_ismt2_pp(c, m()) << "?\n" << mk_ismt2_pp(t, m()) << "\n:" << mk_ismt2_pp(e, m()) << "\n";); if (m().are_equal(t, e)) { result = e; return BR_REWRITE1; } if (m().is_not(c)) { result = m().mk_ite(to_app(c)->get_arg(0), e, t); return BR_REWRITE1; } if (m_ite2id && m().is_eq(c) && is_bv(t) && is_bv(e)) { // detect when ite is actually some simple function based on the pattern (lhs=rhs) ? t : e expr * lhs = to_app(c)->get_arg(0); expr * rhs = to_app(c)->get_arg(1); if (is_bv(rhs)) { if (is_numeral(lhs)) std::swap(lhs, rhs); if ( (m().are_equal(lhs, t) && m().are_equal(rhs, e)) || (m().are_equal(lhs, e) && m().are_equal(rhs, t))) { // (a = b ? a : b) is b. (a = b ? b : a) is a result = e; return BR_REWRITE1; } const unsigned sz = m_util.get_bv_size(rhs); if (sz == 1) { // detect (lhs = N) ? C : D, where N, C, D are 1 bit numerals numeral rhs_n, e_n, t_n; unsigned rhs_sz, e_sz, t_sz; if (is_numeral(rhs, rhs_n, rhs_sz) && is_numeral(t, t_n, t_sz) && is_numeral(e, e_n, e_sz)) { if (t_sz == 1) { SASSERT(rhs_sz == sz && e_sz == sz && t_sz == sz); SASSERT(!m().are_equal(t, e)); result = m().are_equal(rhs, t) ? lhs : m_util.mk_bv_not(lhs); return BR_REWRITE1; } if (rhs_n.is_one() && t_n.is_one() && e_n.is_zero()) { return mk_zero_extend(t_sz - 1, lhs, result); } if (rhs_n.is_zero() && t_n.is_one() && e_n.is_zero()) { return mk_zero_extend(t_sz - 1, m_util.mk_bv_not(lhs), result); } if (rhs_n.is_zero() && t_n.is_zero() && e_n.is_one()) { return mk_zero_extend(t_sz - 1, lhs, result); } if (rhs_n.is_one() && t_n.is_zero() && e_n.is_one()) { return mk_zero_extend(t_sz - 1, m_util.mk_bv_not(lhs), result); } } } } } return BR_FAILED; } br_status bv_rewriter::mk_bvsmul_no_overflow(unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 2); unsigned bv_sz; rational a0_val, a1_val; bool is_num1 = is_numeral(args[0], a0_val, bv_sz); bool is_num2 = is_numeral(args[1], a1_val, bv_sz); if (is_num1 && (a0_val.is_zero() || a0_val.is_one())) { result = m().mk_true(); return BR_DONE; } if (is_num2 && (a1_val.is_zero() || a1_val.is_one())) { result = m().mk_true(); return BR_DONE; } if (is_num1 && is_num2) { rational mr = a0_val * a1_val; rational lim = rational::power_of_two(bv_sz-1); result = m().mk_bool_val(mr < lim); return BR_DONE; } return BR_FAILED; } br_status bv_rewriter::mk_bvumul_no_overflow(unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 2); unsigned bv_sz; rational a0_val, a1_val; bool is_num1 = is_numeral(args[0], a0_val, bv_sz); bool is_num2 = is_numeral(args[1], a1_val, bv_sz); if (is_num1 && (a0_val.is_zero() || a0_val.is_one())) { result = m().mk_true(); return BR_DONE; } if (is_num2 && (a1_val.is_zero() || a1_val.is_one())) { result = m().mk_true(); return BR_DONE; } if (is_num1 && is_num2) { rational mr = a0_val * a1_val; rational lim = rational::power_of_two(bv_sz); result = m().mk_bool_val(mr < lim); return BR_DONE; } return BR_FAILED; } br_status bv_rewriter::mk_bvsmul_no_underflow(unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 2); unsigned bv_sz; rational a0_val, a1_val; bool is_num1 = is_numeral(args[0], a0_val, bv_sz); bool is_num2 = is_numeral(args[1], a1_val, bv_sz); if (is_num1 && (a0_val.is_zero() || a0_val.is_one())) { result = m().mk_true(); return BR_DONE; } if (is_num2 && (a1_val.is_zero() || a1_val.is_one())) { result = m().mk_true(); return BR_DONE; } if (is_num1 && is_num2) { rational ul = rational::power_of_two(bv_sz); rational lim = rational::power_of_two(bv_sz-1); if (a0_val >= lim) a0_val -= ul; if (a1_val >= lim) a1_val -= ul; rational mr = a0_val * a1_val; rational neg_lim = -lim; TRACE("bv_rewriter_bvsmul_no_underflow", tout << "a0:" << a0_val << " a1:" << a1_val << " mr:" << mr << " neg_lim:" << neg_lim << std::endl;); result = m().mk_bool_val(mr >= neg_lim); return BR_DONE; } return BR_FAILED; } template class poly_rewriter; z3-z3-4.8.7/src/ast/rewriter/bv_rewriter.h000066400000000000000000000254761356505360400203710ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: bv_rewriter.h Abstract: Basic rewriting rules for bit-vectors Author: Leonardo (leonardo) 2011-04-14 Notes: --*/ #ifndef BV_REWRITER_H_ #define BV_REWRITER_H_ #include "ast/rewriter/poly_rewriter.h" #include "ast/bv_decl_plugin.h" #include "ast/arith_decl_plugin.h" #include "ast/rewriter/mk_extract_proc.h" #include "ast/rewriter/bv_trailing.h" class bv_rewriter_core { protected: typedef rational numeral; bv_util m_util; ast_manager & m() const { return m_util.get_manager(); } family_id get_fid() const { return m_util.get_family_id(); } bool is_numeral(expr * n) const { return m_util.is_numeral(n); } bool is_numeral(expr * n, numeral & r) const { unsigned sz; return m_util.is_numeral(n, r, sz); } bool is_zero(expr * n) const { return m_util.is_zero(n); } bool is_minus_one(expr * n) const { return m_util.is_allone(n); } void normalize(numeral & c, sort * s) { unsigned bv_size = m_util.get_bv_size(s); c = m_util.norm(c, bv_size); } app * mk_numeral(numeral const & r, sort * s) { return m_util.mk_numeral(r, s); } decl_kind add_decl_kind() const { return OP_BADD; } decl_kind mul_decl_kind() const { return OP_BMUL; } bool use_power() const { return false; } decl_kind power_decl_kind() const { UNREACHABLE(); return static_cast(UINT_MAX); } public: bv_rewriter_core(ast_manager & m):m_util(m) {} }; class bv_rewriter : public poly_rewriter { mk_extract_proc m_mk_extract; bv_trailing m_rm_trailing; arith_util m_autil; bool m_hi_div0; bool m_elim_sign_ext; bool m_mul2concat; bool m_bit2bool; bool m_blast_eq_value; bool m_mkbv2num; bool m_ite2id; bool m_split_concat_eq; bool m_udiv2mul; bool m_bvnot2arith; bool m_bv_sort_ac; bool m_trailing; bool m_extract_prop; bool m_bvnot_simpl; bool m_le_extra; bool m_urem_simpl; bool is_zero_bit(expr * x, unsigned idx); br_status mk_ule(expr * a, expr * b, expr_ref & result); br_status mk_uge(expr * a, expr * b, expr_ref & result); br_status mk_ult(expr * a, expr * b, expr_ref & result); br_status mk_sle(expr * a, expr * b, expr_ref & result); br_status mk_sge(expr * a, expr * b, expr_ref & result); br_status mk_slt(expr * a, expr * b, expr_ref & result); br_status rw_leq_concats(bool is_signed, expr * a, expr * b, expr_ref & result); bool are_eq_upto_num(expr * a, expr * b, expr_ref& common, numeral& a0_val, numeral& b0_val); br_status rw_leq_overflow(bool is_signed, expr * _a, expr * _b, expr_ref & result); br_status mk_leq_core(bool is_signed, expr * a, expr * b, expr_ref & result); br_status mk_concat(unsigned num_args, expr * const * args, expr_ref & result); unsigned propagate_extract(unsigned high, expr * arg, expr_ref & result); br_status mk_extract(unsigned high, unsigned low, expr * arg, expr_ref & result); br_status mk_repeat(unsigned n, expr * arg, expr_ref & result); br_status mk_zero_extend(unsigned n, expr * arg, expr_ref & result); br_status mk_sign_extend(unsigned n, expr * arg, expr_ref & result); bool is_negatable(expr * arg, expr_ref& x); br_status mk_bv_not(expr * arg, expr_ref & result); br_status mk_bv_or(unsigned num, expr * const * args, expr_ref & result); br_status mk_bv_xor(unsigned num, expr * const * args, expr_ref & result); br_status mk_bv_and(unsigned num, expr * const * args, expr_ref & result); br_status mk_bv_nand(unsigned num, expr * const * args, expr_ref & result); br_status mk_bv_nor(unsigned num, expr * const * args, expr_ref & result); br_status mk_bv_xnor(unsigned num_args, expr * const * args, expr_ref & result); br_status mk_bv_rotate_left(unsigned n, expr * arg, expr_ref & result); br_status mk_bv_rotate_right(unsigned n, expr * arg, expr_ref & result); br_status mk_bv_ext_rotate_left(expr * arg1, expr * arg2, expr_ref & result); br_status mk_bv_ext_rotate_right(expr * arg1, expr * arg2, expr_ref & result); br_status mk_bv_add(expr* a, expr* b, expr_ref& result) { expr* args[2] = { a, b }; return mk_bv_add(2, args, result); } br_status mk_bv_sub(expr* a, expr* b, expr_ref& result) { expr* args[2] = { a, b }; return mk_sub(2, args, result); } br_status mk_bv_mul(expr* a, expr* b, expr_ref& result) { expr* args[2] = { a, b }; return mk_bv_mul(2, args, result); } br_status mk_bv_add(unsigned num_args, expr * const * args, expr_ref & result); br_status mk_bv_mul(unsigned num_args, expr * const * args, expr_ref & result); br_status mk_bv_shl(expr * arg1, expr * arg2, expr_ref & result); br_status mk_bv_lshr(expr * arg1, expr * arg2, expr_ref & result); br_status mk_bv_ashr(expr * arg1, expr * arg2, expr_ref & result); bool distribute_concat(decl_kind op, unsigned n, expr* const* args, expr_ref& result); bool is_minus_one_core(expr * arg) const; bool is_x_minus_one(expr * arg, expr * & x); bool is_add_no_overflow(expr* e); bool is_mul_no_overflow(expr* e); unsigned num_leading_zero_bits(expr* e); br_status mk_bv_sdiv_core(expr * arg1, expr * arg2, bool hi_div0, expr_ref & result); br_status mk_bv_udiv_core(expr * arg1, expr * arg2, bool hi_div0, expr_ref & result); br_status mk_bv_srem_core(expr * arg1, expr * arg2, bool hi_div0, expr_ref & result); br_status mk_bv_urem_core(expr * arg1, expr * arg2, bool hi_div0, expr_ref & result); br_status mk_bv_smod_core(expr * arg1, expr * arg2, bool hi_div0, expr_ref & result); br_status mk_bv_sdiv(expr * arg1, expr * arg2, expr_ref & result) { return mk_bv_sdiv_core(arg1, arg2, m_hi_div0, result); } br_status mk_bv_udiv(expr * arg1, expr * arg2, expr_ref & result) { return mk_bv_udiv_core(arg1, arg2, m_hi_div0, result); } br_status mk_bv_srem(expr * arg1, expr * arg2, expr_ref & result) { return mk_bv_srem_core(arg1, arg2, m_hi_div0, result); } br_status mk_bv_urem(expr * arg1, expr * arg2, expr_ref & result) { return mk_bv_urem_core(arg1, arg2, m_hi_div0, result); } br_status mk_bv_smod(expr * arg1, expr * arg2, expr_ref & result) { return mk_bv_smod_core(arg1, arg2, m_hi_div0, result); } br_status mk_bv_sdiv_i(expr * arg1, expr * arg2, expr_ref & result) { return mk_bv_sdiv_core(arg1, arg2, true, result); } br_status mk_bv_udiv_i(expr * arg1, expr * arg2, expr_ref & result) { return mk_bv_udiv_core(arg1, arg2, true, result); } br_status mk_bv_srem_i(expr * arg1, expr * arg2, expr_ref & result) { return mk_bv_srem_core(arg1, arg2, true, result); } br_status mk_bv_urem_i(expr * arg1, expr * arg2, expr_ref & result) { return mk_bv_urem_core(arg1, arg2, true, result); } br_status mk_bv_smod_i(expr * arg1, expr * arg2, expr_ref & result) { return mk_bv_smod_core(arg1, arg2, true, result); } br_status mk_int2bv(unsigned bv_size, expr * arg, expr_ref & result); br_status mk_bv2int(expr * arg, expr_ref & result); br_status mk_bv_redor(expr * arg, expr_ref & result); 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); br_status mk_bvsmul_no_overflow(unsigned num, expr * const * args, expr_ref & result); br_status mk_bvumul_no_overflow(unsigned num, expr * const * args, expr_ref & result); br_status mk_bvsmul_no_underflow(unsigned num, expr * const * args, expr_ref & result); bool is_minus_one_times_t(expr * arg); void mk_t1_add_t2_eq_c(expr * t1, expr * t2, expr * c, expr_ref & result); bool is_concat_split_target(expr * t) const; br_status mk_mul_eq(expr * lhs, expr * rhs, expr_ref & result); bool is_add_mul_const(expr* e) const; bool isolate_term(expr* lhs, expr* rhs, expr_ref & result); bool has_numeral(app* e) const; bool is_concat_target(expr* lhs, expr* rhs) const; void updt_local_params(params_ref const & p); expr * concat(unsigned num_args, expr * const * args); public: bv_rewriter(ast_manager & m, params_ref const & p = params_ref()): poly_rewriter(m, p), m_mk_extract(m_util), m_rm_trailing(m_mk_extract), m_autil(m) { updt_local_params(p); } void updt_params(params_ref const & p); static void get_param_descrs(param_descrs & r); void set_mkbv2num(bool f) { m_mkbv2num = f; } unsigned get_bv_size(expr * t) const {return m_util.get_bv_size(t); } bool is_numeral(expr * t) const { return m_util.is_numeral(t); } bool is_numeral(expr * t, numeral & r, unsigned & sz) const { return m_util.is_numeral(t, r, sz); } bool is_bv(expr * t) const { return m_util.is_bv(t); } expr * mk_numeral(numeral const & v, unsigned sz) { return m_util.mk_numeral(v, sz); } expr * mk_numeral(unsigned v, unsigned sz) { return m_util.mk_numeral(numeral(v), sz); } br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result); void mk_app(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { if (mk_app_core(f, num_args, args, result) == BR_FAILED) result = m().mk_app(f, num_args, args); } bool is_urem_any(expr * e, expr * & dividend, expr * & divisor); br_status mk_eq_core(expr * lhs, expr * rhs, expr_ref & result); br_status mk_ite_core(expr * c, expr * t, expr * e, expr_ref & resul); bool hi_div0() const { return m_hi_div0; } bv_util & get_util() { return m_util; } #define MK_BV_BINARY(OP) \ expr_ref OP(expr* a, expr* b) { \ expr_ref result(m()); \ if (BR_FAILED == OP(a, b, result)) \ result = m_util.OP(a, b); \ return result; \ } \ expr_ref mk_zero_extend(unsigned n, expr * arg) { expr_ref result(m()); if (BR_FAILED == mk_zero_extend(n, arg, result)) result = m_util.mk_zero_extend(n, arg); return result; } MK_BV_BINARY(mk_bv_urem); MK_BV_BINARY(mk_ule); MK_BV_BINARY(mk_bv_add); MK_BV_BINARY(mk_bv_mul); MK_BV_BINARY(mk_bv_sub); expr_ref mk_bv2int(expr* a) { expr_ref result(m()); if (BR_FAILED == mk_bv2int(a, result)) result = m_util.mk_bv2int(a); return result; } }; #endif z3-z3-4.8.7/src/ast/rewriter/bv_rewriter_params.pyg000066400000000000000000000032721356505360400222720ustar00rootroot00000000000000def_module_params(module_name='rewriter', class_name='bv_rewriter_params', export=True, params=(("udiv2mul", BOOL, False, "convert constant udiv to mul"), ("split_concat_eq", BOOL, False, "split equalities of the form (= (concat t1 t2) t3)"), ("bit2bool", BOOL, True, "try to convert bit-vector terms of size 1 into Boolean terms"), ("blast_eq_value", BOOL, False, "blast (some) Bit-vector equalities into bits"), ("elim_sign_ext", BOOL, True, "expand sign-ext operator using concat and extract"), ("hi_div0", BOOL, True, "use the 'hardware interpretation' for division by zero (for bit-vector terms)"), ("mul2concat", BOOL, False, "replace multiplication by a power of two into a concatenation"), ("bvnot2arith", BOOL, False, "replace (bvnot x) with (bvsub -1 x)"), ("bv_sort_ac", BOOL, False, "sort the arguments of all AC operators"), ("bv_trailing", BOOL, False, "lean removal of trailing zeros"), ("bv_extract_prop", BOOL, False, "attempt to partially propagate extraction inwards"), ("bv_not_simpl", BOOL, False, "apply simplifications for bvnot"), ("bv_ite2id", BOOL, False, "rewrite ite that can be simplified to identity"), ("bv_le_extra", BOOL, False, "additional bu_(u/s)le simplifications"), ("bv_urem_simpl", BOOL, False, "additional simplification for bvurem") )) z3-z3-4.8.7/src/ast/rewriter/bv_trailing.cpp000066400000000000000000000345311356505360400206620ustar00rootroot00000000000000/*++ Copyright (c) 2016 Microsoft Corporation Module Name: bv_trailing.cpp Abstract: Author: Mikolas Janota (MikolasJanota) Revision History: --*/ #include "ast/rewriter/bv_trailing.h" #include "ast/bv_decl_plugin.h" #include "ast/ast_smt2_pp.h" // The analyzer gives up analysis after going TRAILING_DEPTH deep. // This number shouldn't be too big. #define TRAILING_DEPTH 5 struct bv_trailing::imp { typedef rational numeral; typedef obj_map > map; mk_extract_proc& m_mk_extract; bv_util& m_util; ast_manager& m; // keep a cache for each depth, using the convention that m_count_cache[TRAILING_DEPTH] is top-level map* m_count_cache[TRAILING_DEPTH + 1]; imp(mk_extract_proc& mk_extract) : m_mk_extract(mk_extract) , m_util(mk_extract.bvutil()) , m(mk_extract.m()) { TRACE("bv-trailing", tout << "ctor\n";); for (unsigned i = 0; i <= TRAILING_DEPTH; ++i) m_count_cache[i] = nullptr; } virtual ~imp() { TRACE("bv-trailing", tout << "dtor\n";); reset_cache(0); } br_status eq_remove_trailing(expr * e1, expr * e2, expr_ref& result) { TRACE("bv-trailing", tout << mk_ismt2_pp(e1, m) << "\n=\n" << mk_ismt2_pp(e2, m) << "\n";); SASSERT(m_util.is_bv(e1) && m_util.is_bv(e2)); SASSERT(m_util.get_bv_size(e1) == m_util.get_bv_size(e2)); unsigned max1, min1, max2, min2; count_trailing(e1, min1, max1, TRAILING_DEPTH); count_trailing(e2, min2, max2, TRAILING_DEPTH); if (min1 > max2 || min2 > max1) { // bounds have empty intersection result = m.mk_false(); return BR_DONE; } const unsigned min = std::min(min1, min2); // remove the minimum of the two lower bounds if (min == 0) { // nothing to remove result = m.mk_eq(e1, e2); return BR_FAILED; } const unsigned sz = m_util.get_bv_size(e1); if (min == sz) { // everything removed, unlikely but we check anyhow for safety result = m.mk_true(); return BR_DONE; } expr_ref out1(m); expr_ref out2(m); VERIFY(min == remove_trailing(e1, min, out1, TRAILING_DEPTH)); VERIFY(min == remove_trailing(e2, min, out2, TRAILING_DEPTH)); const bool are_eq = m.are_equal(out1, out2); result = are_eq ? m.mk_true() : m.mk_eq(out1, out2); return are_eq ? BR_DONE : BR_REWRITE2; } // This routine needs to be implemented carefully so that whenever it // returns a lower bound on trailing zeros min, the routine remove_trailing // must be capable of removing at least that many zeros from the expression. void count_trailing(expr * e, unsigned& min, unsigned& max, unsigned depth) { SASSERT(e && m_util.is_bv(e)); if (is_cached(depth, e, min, max)) return; count_trailing_core(e, min, max, depth); TRACE("bv-trailing", tout << mk_ismt2_pp(e, m) << "\n:" << min << " - " << max << "\n";); SASSERT(min <= max); SASSERT(max <= m_util.get_bv_size(e)); cache(depth, e, min, max); // store result into the cache } unsigned remove_trailing(expr * e, unsigned n, expr_ref& result, unsigned depth) { const unsigned retv = remove_trailing_core(e, n, result, depth); CTRACE("bv-trailing", result.get(), tout << mk_ismt2_pp(e, m) << "\n--->\n" << mk_ismt2_pp(result.get(), m) << "\n";); CTRACE("bv-trailing", !result.get(), tout << mk_ismt2_pp(e, m) << "\n---> [EMPTY]\n";); return retv; } // Assumes that count_trailing gives me a lower bound that we can also remove from each summand. unsigned remove_trailing_add(app * a, unsigned n, expr_ref& result, unsigned depth) { SASSERT(m_util.is_bv_add(a)); const unsigned num = a->get_num_args(); if (depth <= 1) { result = a; return 0; } unsigned min, max; count_trailing(a, min, max, depth); // caching is important here const unsigned to_rm = std::min(min, n); if (to_rm == 0) { result = a; return 0; } const unsigned sz = m_util.get_bv_size(a); if (to_rm == sz) { result = nullptr; return sz; } expr_ref_vector new_args(m); expr_ref tmp(m); for (unsigned i = 0; i < num; ++i) { expr * const curr = a->get_arg(i); VERIFY(to_rm == remove_trailing(curr, to_rm, tmp, depth - 1)); new_args.push_back(std::move(tmp)); } result = m.mk_app(m_util.get_fid(), OP_BADD, new_args.size(), new_args.c_ptr()); return to_rm; } unsigned remove_trailing_mul(app * a, unsigned n, expr_ref& result, unsigned depth) { SASSERT(m_util.is_bv_mul(a)); const unsigned num = a->get_num_args(); if (depth <= 1 || !num) { result = a; return 0; } expr_ref tmp(m); expr * const coefficient = a->get_arg(0); const unsigned retv = remove_trailing(coefficient, n, tmp, depth - 1); SASSERT(retv <= n); if (retv == 0) { result = a; return 0; } expr_ref_vector new_args(m); numeral c_val; unsigned c_sz; if (!m_util.is_numeral(tmp, c_val, c_sz) || !c_val.is_one()) new_args.push_back(std::move(tmp)); const unsigned sz = m_util.get_bv_size(coefficient); const unsigned new_sz = sz - retv; if (!new_sz) { result = nullptr; return retv; } SASSERT(m_util.get_bv_size(tmp) == new_sz); for (unsigned i = 1; i < num; i++) { expr * const curr = a->get_arg(i); new_args.push_back(m_mk_extract(new_sz - 1, 0, curr)); } switch (new_args.size()) { case 0: result = m_util.mk_numeral(1, new_sz); break; case 1: result = new_args.get(0); break; default: result = m.mk_app(m_util.get_fid(), OP_BMUL, new_args.size(), new_args.c_ptr()); } return retv; } unsigned remove_trailing_concat(app * a, unsigned n, expr_ref& result, unsigned depth) { SASSERT(m_util.is_concat(a)); if (depth <= 1) { result = a; return 0; } const unsigned num = a->get_num_args(); unsigned retv = 0; unsigned i = num; expr_ref new_last(nullptr, m); while (i && retv < n) { i--; expr * const curr = a->get_arg(i); const unsigned cur_rm = remove_trailing(curr, n, new_last, depth - 1); const unsigned curr_sz = m_util.get_bv_size(curr); retv += cur_rm; if (cur_rm < curr_sz) break; } if (retv == 0) { result = a; return 0; } if (!i && !new_last) {// all args eaten completely SASSERT(retv == m_util.get_bv_size(a)); result = nullptr; return retv; } expr_ref_vector new_args(m); for (unsigned j = 0; j < i; ++j) new_args.push_back(a->get_arg(j)); if (new_last) new_args.push_back(std::move(new_last)); result = new_args.size() == 1 ? new_args.get(0) : m_util.mk_concat(new_args.size(), new_args.c_ptr()); return retv; } unsigned remove_trailing(size_t max_rm, numeral& a) { numeral two(2); unsigned retv = 0; while (max_rm && a.is_even()) { div(a, two, a); ++retv; --max_rm; } return retv; } unsigned remove_trailing_core(expr * e, unsigned n, expr_ref& result, unsigned depth) { SASSERT(m_util.is_bv(e)); if (!depth || !n) return 0; unsigned sz; unsigned retv = 0; numeral e_val; if (m_util.is_numeral(e, e_val, sz)) { retv = remove_trailing(std::min(n, sz), e_val); const unsigned new_sz = sz - retv; result = new_sz ? (retv ? m_util.mk_numeral(e_val, new_sz) : e) : nullptr; return retv; } if (m_util.is_bv_mul(e)) return remove_trailing_mul(to_app(e), n, result, depth); if (m_util.is_bv_add(e)) return remove_trailing_add(to_app(e), n, result, depth); if (m_util.is_concat(e)) return remove_trailing_concat(to_app(e), n, result, depth); return 0; } void count_trailing_concat(app * a, unsigned& min, unsigned& max, unsigned depth) { if (depth <= 1) { min = 0; max = m_util.get_bv_size(a); } max = min = 0; // treat empty concat as the empty string unsigned num = a->get_num_args(); bool update_min = true; bool update_max = true; unsigned tmp_min, tmp_max; while (num-- && update_max) { expr * const curr = a->get_arg(num); const unsigned curr_sz = m_util.get_bv_size(curr); count_trailing(curr, tmp_min, tmp_max, depth - 1); SASSERT(curr_sz != tmp_min || curr_sz == tmp_max); max += tmp_max; if (update_min) min += tmp_min; // continue updating only if eaten away completely update_min &= curr_sz == tmp_min; update_max &= curr_sz == tmp_max; } } void count_trailing_add(app * a, unsigned& min, unsigned& max, unsigned depth) { if (depth <= 1) { min = 0; max = m_util.get_bv_size(a); } const unsigned num = a->get_num_args(); const unsigned sz = m_util.get_bv_size(a); min = max = sz; // treat empty addition as 0 unsigned tmp_min; unsigned tmp_max; bool known_parity = true; bool is_odd = false; for (unsigned i = 0; i < num; ++i) { expr * const curr = a->get_arg(i); count_trailing(curr, tmp_min, tmp_max, depth - 1); min = std::min(min, tmp_min); known_parity = known_parity && (!tmp_max || tmp_min); if (known_parity && !tmp_max) is_odd = !is_odd; if (!known_parity && !min) break; // no more information can be gained } max = known_parity && is_odd ? 0 : sz; // max is known if parity is 1 } void count_trailing_mul(app * a, unsigned& min, unsigned& max, unsigned depth) { if (depth <= 1) { min = 0; max = m_util.get_bv_size(a); } const unsigned num = a->get_num_args(); if (!num) { max = min = 0; // treat empty multiplication as 1 return; } // assume that numerals are pushed in the front, count only for the first element expr * const curr = a->get_arg(0); unsigned tmp_max; count_trailing(curr, min, tmp_max, depth - 1); max = num == 1 ? tmp_max : m_util.get_bv_size(a); return; } void count_trailing_core(expr * e, unsigned& min, unsigned& max, unsigned depth) { if (!depth) { min = 0; max = m_util.get_bv_size(e); return; } unsigned sz; numeral e_val; if (m_util.is_numeral(e, e_val, sz)) { min = max = 0; numeral two(2); while (sz-- && e_val.is_even()) { ++max; ++min; div(e_val, two, e_val); } return; } if (m_util.is_bv_mul(e)) count_trailing_mul(to_app(e), min, max, depth); else if (m_util.is_bv_add(e)) count_trailing_add(to_app(e), min, max, depth); else if (m_util.is_concat(e)) count_trailing_concat(to_app(e), min, max, depth); else { min = 0; max = m_util.get_bv_size(e); } } void cache(unsigned depth, expr * e, unsigned min, unsigned max) { SASSERT(depth <= TRAILING_DEPTH); if (depth == 0) return; if (m_count_cache[depth] == nullptr) m_count_cache[depth] = alloc(map); SASSERT(!m_count_cache[depth]->contains(e)); m.inc_ref(e); m_count_cache[depth]->insert(e, std::make_pair(min, max)); TRACE("bv-trailing", tout << "caching@" << depth <<": " << mk_ismt2_pp(e, m) << '[' << m_util.get_bv_size(e) << "]\n: " << min << '-' << max << "\n";); } bool is_cached(unsigned depth, expr * e, unsigned& min, unsigned& max) { SASSERT(depth <= TRAILING_DEPTH); if (depth == 0) { min = 0; max = m_util.get_bv_size(e); return true; } if (m_count_cache[depth] == nullptr) return false; const map::obj_map_entry * const oe = m_count_cache[depth]->find_core(e); if (oe == nullptr) return false; min = oe->get_data().m_value.first; max = oe->get_data().m_value.second; TRACE("bv-trailing", tout << "cached@" << depth << ": " << mk_ismt2_pp(e, m) << '[' << m_util.get_bv_size(e) << "]\n: " << min << '-' << max << "\n";); return true; } void reset_cache(const unsigned condition) { 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";); if (condition && m_count_cache[i]->size() < condition) continue; TRACE("bv-trailing", tout << "reset cache " << i << "\n";); map::iterator it = m_count_cache[i]->begin(); map::iterator end = m_count_cache[i]->end(); for (; it != end; ++it) m.dec_ref(it->m_key); dealloc(m_count_cache[i]); m_count_cache[i] = nullptr; } } }; bv_trailing::bv_trailing(mk_extract_proc& mk_extract) { m_imp = alloc(imp, mk_extract); } bv_trailing::~bv_trailing() { dealloc(m_imp); } br_status bv_trailing::eq_remove_trailing(expr * e1, expr * e2, expr_ref& result) { return m_imp->eq_remove_trailing(e1, e2, result); } void bv_trailing::count_trailing(expr * e, unsigned& min, unsigned& max) { m_imp->count_trailing(e, min, max, TRAILING_DEPTH); } unsigned bv_trailing::remove_trailing(expr * e, unsigned n, expr_ref& result) { return m_imp->remove_trailing(e, n, result, TRAILING_DEPTH); } void bv_trailing::reset_cache(unsigned condition) { m_imp->reset_cache(condition); } z3-z3-4.8.7/src/ast/rewriter/bv_trailing.h000066400000000000000000000025071356505360400203250ustar00rootroot00000000000000 /*++ Copyright (c) 2016 Microsoft Corporation Module Name: bv_trailing.h Abstract: A utility to count trailing zeros of an expression. Treats 2x and x++0 equivalently. Author: Mikolas Janota (MikolasJanota) Revision History: --*/ #ifndef BV_TRAILING_H_ #define BV_TRAILING_H_ #include "ast/ast.h" #include "ast/rewriter/rewriter_types.h" #include "ast/rewriter/mk_extract_proc.h" class bv_trailing { public: bv_trailing(mk_extract_proc& ep); virtual ~bv_trailing(); public: // Remove trailing zeros from both sides of an equality (might give False). br_status eq_remove_trailing(expr * e1, expr * e2, expr_ref& result); // Gives a lower and upper bound on trailing zeros in e. void count_trailing(expr * e, unsigned& min, unsigned& max); // Attempts removing n trailing zeros from e. Returns how many were successfully removed. // We're assuming that it can remove at least as many zeros as min returned by count_training. // Removing the bit-width of e, sets result to NULL. unsigned remove_trailing(expr * e, unsigned n, expr_ref& result); // Reset cache(s) if it exceeded size condition. void reset_cache(unsigned condition); protected: struct imp; imp * m_imp; }; #endif /* BV_TRAILING_H_ */ z3-z3-4.8.7/src/ast/rewriter/datatype_rewriter.cpp000066400000000000000000000113171356505360400221150ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: datatype_rewriter.cpp Abstract: Basic rewriting rules for Datatypes. Author: Leonardo (leonardo) 2011-04-06 Notes: --*/ #include "ast/rewriter/datatype_rewriter.h" br_status datatype_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { SASSERT(f->get_family_id() == get_fid()); switch(f->get_decl_kind()) { case OP_DT_CONSTRUCTOR: return BR_FAILED; case OP_DT_RECOGNISER: SASSERT(num_args == 1); result = m_util.mk_is(m_util.get_recognizer_constructor(f), args[0]); return BR_REWRITE1; case OP_DT_IS: // // simplify is_cons(cons(x,y)) -> true // simplify is_cons(nil) -> false // SASSERT(num_args == 1); if (!is_app(args[0]) || !m_util.is_constructor(to_app(args[0]))) return BR_FAILED; if (to_app(args[0])->get_decl() == m_util.get_recognizer_constructor(f)) result = m().mk_true(); else result = m().mk_false(); return BR_DONE; case OP_DT_ACCESSOR: { // // simplify head(cons(x,y)) -> x // SASSERT(num_args == 1); if (!is_app(args[0]) || !m_util.is_constructor(to_app(args[0]))) return BR_FAILED; app * a = to_app(args[0]); func_decl * c_decl = a->get_decl(); if (c_decl != m_util.get_accessor_constructor(f)) return BR_FAILED; ptr_vector const & acc = *m_util.get_constructor_accessors(c_decl); SASSERT(acc.size() == a->get_num_args()); unsigned num = acc.size(); for (unsigned i = 0; i < num; ++i) { if (f == acc[i]) { // found it. result = a->get_arg(i); return BR_DONE; } } UNREACHABLE(); break; } case OP_DT_UPDATE_FIELD: { SASSERT(num_args == 2); if (!is_app(args[0]) || !m_util.is_constructor(to_app(args[0]))) return BR_FAILED; app * a = to_app(args[0]); func_decl * c_decl = a->get_decl(); func_decl * acc = m_util.get_update_accessor(f); if (c_decl != m_util.get_accessor_constructor(acc)) { result = a; return BR_DONE; } ptr_vector const & accs = *m_util.get_constructor_accessors(c_decl); SASSERT(accs.size() == a->get_num_args()); unsigned num = accs.size(); ptr_buffer new_args; for (unsigned i = 0; i < num; ++i) { if (acc == accs[i]) { new_args.push_back(args[1]); } else { new_args.push_back(a->get_arg(i)); } } result = m().mk_app(c_decl, num, new_args.c_ptr()); return BR_DONE; } default: UNREACHABLE(); } return BR_FAILED; } br_status datatype_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result) { if (!is_app(lhs) || !is_app(rhs) || !m_util.is_constructor(to_app(lhs)) || !m_util.is_constructor(to_app(rhs))) return BR_FAILED; if (to_app(lhs)->get_decl() != to_app(rhs)->get_decl()) { result = m().mk_false(); return BR_DONE; } // Remark: In datatype_simplifier_plugin, we used // m_basic_simplifier to create '=' and 'and' applications in the // following code. This trick not guarantee that the final expression // will be fully simplified. // // Example: // The assertion // (assert (= (cons a1 (cons a2 (cons a3 (cons (+ a4 1) (cons (+ a5 c5) (cons a6 nil)))))) // (cons b1 (cons b2 (cons b3 (cons b4 (cons b5 (cons b6 nil)))))))) // // After applying asserted_formulas::reduce(), the following formula was generated. // // (= a1 b1) // (= a2 b2) // (= a3 b3) // (= (+ a4 (* (- 1) b4)) (- 1)) // (= (+ c5 a5) b5) <<< NOT SIMPLIFIED WITH RESPECT TO ARITHMETIC // (= (cons a6 nil) (cons b6 nil))) <<< NOT SIMPLIFIED WITH RESPECT TO DATATYPE theory // // Note that asserted_formulas::reduce() applied the simplifier many times. // After the first simplification step we had: // (= a1 b1) // (= (cons a2 (cons a3 (cons (+ a4 1) (cons (+ a5 c5) (cons a6 nil)))))) // (cons b2 (cons b3 (cons b4 (cons b5 (cons b6 nil)))))) ptr_buffer eqs; unsigned num = to_app(lhs)->get_num_args(); SASSERT(num == to_app(rhs)->get_num_args()); for (unsigned i = 0; i < num; ++i) { eqs.push_back(m().mk_eq(to_app(lhs)->get_arg(i), to_app(rhs)->get_arg(i))); } result = m().mk_and(eqs.size(), eqs.c_ptr()); return BR_REWRITE2; } z3-z3-4.8.7/src/ast/rewriter/datatype_rewriter.h000066400000000000000000000013721356505360400215620ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: datatype_rewriter.h Abstract: Basic rewriting rules for Datatypes. Author: Leonardo (leonardo) 2011-04-06 Notes: --*/ #ifndef DATATYPE_REWRITER_H_ #define DATATYPE_REWRITER_H_ #include "ast/datatype_decl_plugin.h" #include "ast/rewriter/rewriter_types.h" class datatype_rewriter { datatype_util m_util; public: datatype_rewriter(ast_manager & m):m_util(m) {} ast_manager & m() const { return m_util.get_manager(); } family_id get_fid() const { return m_util.get_family_id(); } br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result); br_status mk_eq_core(expr * lhs, expr * rhs, expr_ref & result); }; #endif z3-z3-4.8.7/src/ast/rewriter/der.cpp000066400000000000000000000334621356505360400171360ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: der.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-01-27. Revision History: Christoph Wintersteiger, 2010-03-30: Added Destr. Multi-Equality Resolution --*/ #include "ast/rewriter/der.h" #include "ast/occurs.h" #include "ast/for_each_expr.h" #include "ast/rewriter/rewriter_def.h" #include "ast/ast_pp.h" #include "ast/ast_ll_pp.h" #include "ast/ast_smt2_pp.h" static bool is_var(expr * e, unsigned num_decls) { return is_var(e) && to_var(e)->get_idx() < num_decls; } static bool is_neg_var(ast_manager & m, expr * e, unsigned num_decls) { return m.is_not(e) && is_var(to_app(e)->get_arg(0)) && to_var(to_app(e)->get_arg(0))->get_idx() < num_decls; } /** \brief Return true if \c e is of the form (not (= VAR t)) or (not (iff VAR t)) or (iff VAR t) or (iff (not VAR) t) or (VAR IDX) or (not (VAR IDX)). The last case can be viewed */ bool der::is_var_diseq(expr * e, unsigned num_decls, var * & v, expr_ref & t) { // (not (= VAR t)) and (not (iff VAR t)) cases expr *eq, * lhs, *rhs; if (m_manager.is_not(e, eq) && m_manager.is_eq(eq, lhs, rhs)) { if (!is_var(lhs, num_decls) && !is_var(rhs, num_decls)) return false; if (!is_var(lhs, num_decls)) std::swap(lhs, rhs); SASSERT(is_var(lhs, num_decls)); // Remark: Occurs check is not necessary here... the top-sort procedure will check for cycles... // if (occurs(lhs, rhs)) { // return false; // } v = to_var(lhs); t = rhs; TRACE("der", tout << mk_pp(e, m_manager) << "\n";); return true; } // (iff VAR t) and (iff (not VAR) t) cases else if (m_manager.is_eq(e, lhs, rhs) && m_manager.is_bool(lhs)) { // (iff VAR t) case if (is_var(lhs, num_decls) || is_var(rhs, num_decls)) { if (!is_var(lhs, num_decls)) std::swap(lhs, rhs); SASSERT(is_var(lhs, num_decls)); // Remark: Occurs check is not necessary here... the top-sort procedure will check for cycles... // if (occurs(lhs, rhs)) { // return false; // } v = to_var(lhs); t = m_manager.mk_not(rhs); m_new_exprs.push_back(t); TRACE("der", tout << mk_pp(e, m_manager) << "\n";); return true; } // (iff (not VAR) t) case else if (is_neg_var(m_manager, lhs, num_decls) || is_neg_var(m_manager, rhs, num_decls)) { if (!is_neg_var(m_manager, lhs, num_decls)) std::swap(lhs, rhs); SASSERT(is_neg_var(m_manager, lhs, num_decls)); expr * lhs_var = to_app(lhs)->get_arg(0); // Remark: Occurs check is not necessary here... the top-sort procedure will check for cycles... // if (occurs(lhs_var, rhs)) { // return false; // } v = to_var(lhs_var); t = rhs; TRACE("der", tout << mk_pp(e, m_manager) << "\n";); return true; } else { return false; } } // VAR != false case else if (is_var(e, num_decls)) { t = m_manager.mk_false(); v = to_var(e); TRACE("der", tout << mk_pp(e, m_manager) << "\n";); return true; } // VAR != true case else if (is_neg_var(m_manager, e, num_decls)) { t = m_manager.mk_true(); v = to_var(to_app(e)->get_arg(0)); TRACE("der", tout << mk_pp(e, m_manager) << "\n";); return true; } else { return false; } } void der::operator()(quantifier * q, expr_ref & r, proof_ref & pr) { bool reduced = false; pr = nullptr; r = q; TRACE("der", tout << mk_pp(q, m_manager) << "\n";); // Keep applying it until r doesn't change anymore do { proof_ref curr_pr(m_manager); q = to_quantifier(r); reduce1(q, r, curr_pr); if (q != r) reduced = true; if (m_manager.proofs_enabled()) { pr = m_manager.mk_transitivity(pr, curr_pr); } } while (q != r && is_quantifier(r)); // Eliminate variables that have become unused if (reduced && is_forall(r)) { quantifier * q = to_quantifier(r); r = elim_unused_vars(m_manager, q, params_ref()); if (m_manager.proofs_enabled()) { proof * p1 = m_manager.mk_elim_unused_vars(q, r); pr = m_manager.mk_transitivity(pr, p1); } } m_new_exprs.reset(); } void der::reduce1(quantifier * q, expr_ref & r, proof_ref & pr) { if (!is_forall(q)) { pr = nullptr; r = q; return; } expr * e = q->get_expr(); unsigned num_decls = q->get_num_decls(); var * v = nullptr; expr_ref t(m_manager); if (m_manager.is_or(e)) { unsigned num_args = to_app(e)->get_num_args(); unsigned i = 0; unsigned diseq_count = 0; unsigned largest_vinx = 0; m_map.reset(); m_pos2var.reset(); m_inx2var.reset(); m_pos2var.reserve(num_args, -1); // Find all disequalities for (; i < num_args; i++) { if (is_var_diseq(to_app(e)->get_arg(i), num_decls, v, t)) { unsigned idx = v->get_idx(); if(m_map.get(idx, 0) == 0) { m_map.reserve(idx + 1, 0); m_inx2var.reserve(idx + 1, 0); m_map[idx] = t; m_inx2var[idx] = v; m_pos2var[i] = idx; diseq_count++; largest_vinx = (idx>largest_vinx) ? idx : largest_vinx; } } } if (diseq_count > 0) { get_elimination_order(); SASSERT(m_order.size() <= diseq_count); // some might be missing because of cycles if (!m_order.empty()) { create_substitution(largest_vinx + 1); apply_substitution(q, r); } } else { TRACE("der_bug", tout << "Did not find any diseq\n" << mk_pp(q, m_manager) << "\n";); r = q; } } // Remark: get_elimination_order/top-sort checks for cycles, but it is not invoked for unit clauses. // So, we must perform a occurs check here. else if (is_var_diseq(e, num_decls, v, t) && !occurs(v, t)) { r = m_manager.mk_false(); } else r = q; if (m_manager.proofs_enabled()) { pr = r == q ? nullptr : m_manager.mk_der(q, r); } } void der_sort_vars(ptr_vector & vars, ptr_vector & definitions, unsigned_vector & order) { order.reset(); // eliminate self loops, and definitions containing quantifiers. bool found = false; for (unsigned i = 0; i < definitions.size(); i++) { var * v = vars[i]; expr * t = definitions[i]; if (t == nullptr || has_quantifiers(t) || occurs(v, t)) definitions[i] = 0; else found = true; // found at least one candidate } if (!found) return; typedef std::pair frame; svector todo; expr_fast_mark1 visiting; expr_fast_mark2 done; unsigned vidx, num; for (unsigned i = 0; i < definitions.size(); i++) { if (definitions[i] == 0) continue; var * v = vars[i]; SASSERT(v->get_idx() == i); SASSERT(todo.empty()); todo.push_back(frame(v, 0)); while (!todo.empty()) { start: frame & fr = todo.back(); expr * t = fr.first; if (t->get_ref_count() > 1 && done.is_marked(t)) { todo.pop_back(); continue; } switch (t->get_kind()) { case AST_VAR: vidx = to_var(t)->get_idx(); if (fr.second == 0) { CTRACE("der_bug", vidx >= definitions.size(), tout << "vidx: " << vidx << "\n";); // Remark: The size of definitions may be smaller than the number of variables occurring in the quantified formula. if (definitions.get(vidx, 0) != 0) { if (visiting.is_marked(t)) { // cycle detected: remove t visiting.reset_mark(t); definitions[vidx] = 0; } else { visiting.mark(t); fr.second = 1; todo.push_back(frame(definitions[vidx], 0)); goto start; } } } else { SASSERT(fr.second == 1); if (definitions.get(vidx, 0) != 0) { visiting.reset_mark(t); order.push_back(vidx); } else { // var was removed from the list of candidate vars to elim cycle // do nothing } } if (t->get_ref_count() > 1) done.mark(t); todo.pop_back(); break; case AST_QUANTIFIER: UNREACHABLE(); todo.pop_back(); break; case AST_APP: num = to_app(t)->get_num_args(); while (fr.second < num) { expr * arg = to_app(t)->get_arg(fr.second); fr.second++; if (arg->get_ref_count() > 1 && done.is_marked(arg)) continue; todo.push_back(frame(arg, 0)); goto start; } if (t->get_ref_count() > 1) done.mark(t); todo.pop_back(); break; default: UNREACHABLE(); todo.pop_back(); break; } } } } void der::get_elimination_order() { m_order.reset(); TRACE("top_sort", tout << "DEFINITIONS: " << std::endl; for(unsigned i = 0; i < m_map.size(); i++) if(m_map[i]) tout << "VAR " << i << " = " << mk_pp(m_map[i], m_manager) << std::endl; ); // der::top_sort ts(m_manager); der_sort_vars(m_inx2var, m_map, m_order); TRACE("der", tout << "Elimination m_order:" << std::endl; for(unsigned i=0; iget_expr(); unsigned num_args=to_app(e)->get_num_args(); // get a new expression m_new_args.reset(); for(unsigned i = 0; i < num_args; i++) { int x = m_pos2var[i]; if (x != -1 && m_map[x] != 0) continue; // this is a disequality with definition (vanishes) m_new_args.push_back(to_app(e)->get_arg(i)); } unsigned sz = m_new_args.size(); expr_ref t(m_manager); t = (sz == 1) ? m_new_args[0] : m_manager.mk_or(sz, m_new_args.c_ptr()); expr_ref new_e = m_subst(t, m_subst_map.size(), m_subst_map.c_ptr()); // don't forget to update the quantifier patterns 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++) { 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++) { 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(), new_no_patterns.size(), new_no_patterns.c_ptr(), new_e); } struct der_rewriter_cfg : public default_rewriter_cfg { der m_der; der_rewriter_cfg(ast_manager & m):m_der(m) {} ast_manager & m() const { return m_der.m(); } 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) { quantifier_ref q1(m()); q1 = m().update_quantifier(old_q, old_q->get_num_patterns(), new_patterns, old_q->get_num_no_patterns(), new_no_patterns, new_body); m_der(q1, result, result_pr); return true; } }; template class rewriter_tpl; struct der_rewriter::imp : public rewriter_tpl { der_rewriter_cfg m_cfg; imp(ast_manager & m): rewriter_tpl(m, m.proofs_enabled(), m_cfg), m_cfg(m) { } }; der_rewriter::der_rewriter(ast_manager & m) { m_imp = alloc(imp, m); } der_rewriter::~der_rewriter() { dealloc(m_imp); } ast_manager & der_rewriter::m() const { return m_imp->m(); } void der_rewriter::operator()(expr * t, expr_ref & result, proof_ref & result_pr) { m_imp->operator()(t, result, result_pr); } void der_rewriter::cleanup() { ast_manager & m = m_imp->m(); dealloc(m_imp); m_imp = alloc(imp, m); } void der_rewriter::reset() { m_imp->reset(); } z3-z3-4.8.7/src/ast/rewriter/der.h000066400000000000000000000150051356505360400165740ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: der.h Abstract: Destructive equality resolution. Author: Leonardo de Moura (leonardo) 2008-01-27. Revision History: Christoph Wintersteiger, 2010-03-30: Added Destr. Multi-Equality Resolution --*/ #ifndef DER_H_ #define DER_H_ #include "ast/ast.h" #include "ast/rewriter/var_subst.h" /* New DER: the class DER (above) eliminates variables one by one. This is inefficient, and we should implement a new version that can handle efficiently examples with hundreds of variables. Suppose the target of the simplification is the quantifier (FORALL (x1 T1) (x2 T2) ... (xn Tn) (or ....)) So, the variables x1 ... xn are the candidates for elimination. First, we build a mapping of candidate substitutions Since variables x1 ... xn have ids 0 ... n-1, we can use an array m_map to implement this mapping. The idea is to traverse the children of the (or ...) looking for diseqs. The method is_var_diseq can be used for doing that. Given a child c, if is_var_diseq(c, num_decls, v, t) returns true, and m_map[v] is null, then we store t at m_map[v]. For performance reasons, we also store a mapping from child position (in the OR) to variable ID. Thus, m_pos2var[pos] contains the variable that is defined by the diseq at position pos. m_pos2var[pos] = -1, if this position does not contain a diseq. After doing that, m_map contains the variables that can be potentially eliminated using DER. We say m_map[v] is the definition of variable v. The next step is to perform a topological sort on these variables. The result is an array (m_order) of integers (variable ids) such that i < j implies that m_map[m_order[i]] does not depend on variable m_order[j]. For example, consider the case where m_map contains the following values m_map[0] = (+ (VAR 2) 0) m_map[1] = null m_map[2] = (+ (VAR 3) 0) m_map[3] = (+ (VAR 1) 1) m_map[4] = (* (VAR 5) 2) m_map[5] = null In this example, variable 0 depends on the definition of variable 2, which depends on the definition of variable 3, which depends on the definition of variable 0 (cycle). On the other hand, no cycle is found when starting at variable 4. Cycles can be broken by erasing entries from m_map. For example, the cycle above can be removed by setting m_map[0] = null. m_map[0] = null m_map[1] = null m_map[2] = (+ (VAR 3) 0) m_map[3] = (+ (VAR 1) 1) m_map[4] = (* (VAR 5) 2) m_map[5] = null The file asserted_formulas.cpp has a class top_sort for performing topological sort. This class cannot be used here, since it is meant for eliminating constants (instead of variables). We need to implement a new top_sort here, we do not need a separate class for doing that. Moreover, it is much simpler, since m_map is just an array. In the example above (after setting m_map[0] to null), top_sort will produce the following order m_order = [3, 2, 4] The next step is to use var_subst to update the definitions in var_subst. The idea is to process the variables in the order specified by m_order. When processing m_map[m_order[i]] we use the definitions of all variables in m_order[0 ... i-1]. For example: The first variable is 3, since it is at m_order[0], nothing needs to be done. Next we have variable 2, we use m_map[3] since 3 is before 2 in m_order. So, the new definition for 2 is (+ (+ (VAR 1) 1) 0). That is, we update m_map[2] with (+ (+ (VAR 1) 1) 0) Next we have variable 4, we use m_map[3] and m_map[2] since 3 and 2 are before 4 in m_order. In this case, var_subst will not do anything since m_map[4] does not contain variables 3 or 2. So, the new m_map is: m_map[0] = null m_map[1] = null m_map[2] = (+ (+ (VAR 1) 1) 0) m_map[3] = (+ (VAR 1) 1) m_map[4] = (* (VAR 5) 2) m_map[5] = null Now, we update the body of the quantifier using var_subst and the mapping above. The idea is to create a new set of children for the OR. For each child at position i, we do if m_map[m_pos2var[i]] != -1 skip this child, it is a diseq used during DER else apply var_subst using m_map to this child, and store the result in a new children array Create a new OR (new body of the quantifier) using the new children Then, we create a new quantifier using this new body, and use the function elim_unused_vars to eliminate the unused variables. Remark: let us implement the new version inside the class der. Use #if 0 ... #endif to comment the old version. Remark: after you are done, we can eliminate the call to occurs in is_var_diseq, since top_sort is already performing cycle detection. */ /** \brief Functor for applying Destructive Multi-Equality Resolution. (forall (X Y) (or X /= s C[X])) --> (forall (Y) C[Y]) */ class der { ast_manager & m_manager; var_subst m_subst; expr_ref_buffer m_new_exprs; ptr_vector m_map; int_vector m_pos2var; ptr_vector m_inx2var; unsigned_vector m_order; expr_ref_vector m_subst_map; expr_ref_buffer m_new_args; /** \brief Return true if e can be viewed as a variable disequality. Store the variable id in v and the definition in t. For example: if e is (not (= (VAR 1) T)), then v assigned to 1, and t to T. if e is (iff (VAR 2) T), then v is assigned to 2, and t to (not T). (not T) is used because this formula is equivalent to (not (iff (VAR 2) (not T))), and can be viewed as a disequality. */ bool is_var_diseq(expr * e, unsigned num_decls, var *& v, expr_ref & t); void get_elimination_order(); void create_substitution(unsigned sz); void apply_substitution(quantifier * q, expr_ref & r); void reduce1(quantifier * q, expr_ref & r, proof_ref & pr); public: der(ast_manager & m):m_manager(m),m_subst(m),m_new_exprs(m),m_subst_map(m),m_new_args(m) {} ast_manager & m() const { return m_manager; } void operator()(quantifier * q, expr_ref & r, proof_ref & pr); }; /** \brief Functor for applying Destructive Multi-Equality Resolution in all universal quantifiers in an expression. */ class der_rewriter { protected: struct imp; imp * m_imp; public: der_rewriter(ast_manager & m); ~der_rewriter(); ast_manager & m () const; void operator()(expr * t, expr_ref & result, proof_ref & result_pr); void cleanup(); void reset(); }; typedef der_rewriter der_star; #endif /* DER_H_ */ z3-z3-4.8.7/src/ast/rewriter/distribute_forall.cpp000066400000000000000000000106021356505360400220700ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: distribute_forall.cpp Abstract: Author: Leonardo de Moura (leonardo) 2010-04-02. Revision History: Christoph Wintersteiger 2010-04-06: Added implementation. --*/ #include "ast/rewriter/var_subst.h" #include "ast/ast_ll_pp.h" #include "ast/ast_util.h" #include "ast/rewriter/distribute_forall.h" #include "ast/rewriter/bool_rewriter.h" distribute_forall::distribute_forall(ast_manager & m) : m_manager(m), m_cache(m) { } void distribute_forall::visit(expr * n, bool & visited) { if (!is_cached(n)) { m_todo.push_back(n); visited = false; } } bool distribute_forall::visit_children(expr * n) { bool visited = true; unsigned j; switch(n->get_kind()) { case AST_VAR: break; case AST_APP: j = to_app(n)->get_num_args(); while (j > 0) { --j; visit(to_app(n)->get_arg(j), visited); } break; case AST_QUANTIFIER: visit(to_quantifier(n)->get_expr(), visited); break; default: UNREACHABLE(); } return visited; } void distribute_forall::reduce1(expr * n) { switch (n->get_kind()) { case AST_VAR: cache_result(n, n); break; case AST_APP: reduce1_app(to_app(n)); break; case AST_QUANTIFIER: reduce1_quantifier(to_quantifier(n)); break; default: UNREACHABLE(); } } void distribute_forall::reduce1_app(app * a) { SASSERT(a); unsigned num_args = a->get_num_args(); unsigned j = num_args; bool reduced = false; m_new_args.reserve(num_args); app * na = a; while(j > 0) { --j; SASSERT(is_cached(a->get_arg(j))); expr * c = get_cached(a->get_arg(j)); SASSERT(c!=0); if (c != a->get_arg(j)) reduced = true; m_new_args[j] = c; } if (reduced) { na = m_manager.mk_app(a->get_decl(), num_args, m_new_args.c_ptr()); } cache_result(a, na); } void distribute_forall::reduce1_quantifier(quantifier * q) { // This transformation is applied after skolemization/quantifier elimination. So, all quantifiers are universal. SASSERT(q->get_kind() == forall_k); // This transformation is applied after basic pre-processing steps. // So, we can assume that // 1) All (and f1 ... fn) are already encoded as (not (or (not f1 ... fn))) // 2) All or-formulas are flat (or f1 (or f2 f3)) is encoded as (or f1 f2 f3) expr * e = get_cached(q->get_expr()); if (m_manager.is_not(e) && m_manager.is_or(to_app(e)->get_arg(0))) { bool_rewriter br(m_manager); // found target for simplification // (forall X (not (or F1 ... Fn))) // --> // (and (forall X (not F1)) // ... // (forall X (not Fn))) app * or_e = to_app(to_app(e)->get_arg(0)); unsigned num_args = or_e->get_num_args(); expr_ref_buffer new_args(m_manager); for (unsigned i = 0; i < num_args; i++) { expr * arg = or_e->get_arg(i); expr_ref not_arg(m_manager); br.mk_not(arg, not_arg); quantifier_ref tmp_q(m_manager); tmp_q = m_manager.update_quantifier(q, not_arg); 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, // it will also apply basic simplifications. br.mk_and(new_args.size(), new_args.c_ptr(), result); cache_result(q, result); } else { cache_result(q, m_manager.update_quantifier(q, e)); } } void distribute_forall::operator()(expr * f, expr_ref & result) { m_todo.reset(); flush_cache(); m_todo.push_back(f); while (!m_todo.empty()) { expr * e = m_todo.back(); if (visit_children(e)) { m_todo.pop_back(); reduce1(e); } } result = get_cached(f); SASSERT(result!=0); TRACE("distribute_forall", tout << mk_ll_pp(f, m_manager) << "======>\n" << mk_ll_pp(result, m_manager);); } expr * distribute_forall::get_cached(expr * n) const { return const_cast(this)->m_cache.find(n); } void distribute_forall::cache_result(expr * n, expr * r) { SASSERT(r != 0); m_cache.insert(n, r); } z3-z3-4.8.7/src/ast/rewriter/distribute_forall.h000066400000000000000000000037631356505360400215470ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: distribute_forall.h Abstract: Author: Leonardo de Moura (leonardo) 2010-04-02. Revision History: Christoph Wintersteiger 2010-04-06: Added implementation --*/ #ifndef DISTRIBUTE_FORALL_H_ #define DISTRIBUTE_FORALL_H_ #include "ast/ast.h" #include "ast/act_cache.h" /** \brief Apply the following transformation (forall X (and F1 ... Fn)) --> (and (forall X F1) ... (forall X Fn)) The actual transformation is slightly different since the "and" connective is eliminated and replaced with a "not or". So, the actual transformation is: (forall X (not (or F1 ... Fn))) --> (not (or (not (forall X (not F1))) ... (not (forall X (not Fn))))) The implementation uses the visit_children/reduce1 idiom. A cache is used as usual. */ class distribute_forall { typedef act_cache expr_map; ast_manager & m_manager; ptr_vector m_todo; expr_map m_cache; ptr_vector m_new_args; // The new expressions are stored in a mapping that increments their reference counter. So, we do not need to store them in // m_new_exprs // expr_ref_vector m_new_exprs; public: distribute_forall(ast_manager & m); /** \brief Apply the distribute_forall transformation (when possible) to all universal quantifiers in \c f. Store the result in \c result. */ void operator()(expr * f, expr_ref & result); protected: inline void visit(expr * n, bool & visited); bool visit_children(expr * n); void reduce1(expr * n); void reduce1_quantifier(quantifier * q); void reduce1_app(app * a); expr * get_cached(expr * n) const; bool is_cached(expr * n) const { return get_cached(n) != nullptr; } void cache_result(expr * n, expr * r); void reset_cache() { m_cache.reset(); } void flush_cache() { m_cache.cleanup(); } }; #endif /* DISTRIBUTE_FORALL_H_ */ z3-z3-4.8.7/src/ast/rewriter/dl_rewriter.cpp000066400000000000000000000026471356505360400207070ustar00rootroot00000000000000/*++ Copyright (c) 2010 Microsoft Corporation Module Name: dl_rewriter.cpp Abstract: Author: Nikolaj Bjorner (nbjorner) 2010-08-10 Revision History: --*/ #include "ast/rewriter/dl_rewriter.h" br_status dl_rewriter::mk_app_core( func_decl * f, unsigned num_args, expr* const* args, expr_ref& result) { ast_manager& m = result.get_manager(); uint64_t v1, v2; switch(f->get_decl_kind()) { case datalog::OP_DL_LT: if (m_util.is_numeral_ext(args[0], v1) && m_util.is_numeral_ext(args[1], v2)) { result = (v1 < v2)?m.mk_true():m.mk_false(); return BR_DONE; } // x < x <=> false if (args[0] == args[1]) { result = m.mk_false(); return BR_DONE; } // x < 0 <=> false if (m_util.is_numeral_ext(args[1], v2) && v2 == 0) { result = m.mk_false(); return BR_DONE; } // 0 < x <=> 0 != x if (m_util.is_numeral_ext(args[1], v1) && v1 == 0) { result = m.mk_not(m.mk_eq(args[0], args[1])); return BR_DONE; } break; default: break; } return BR_FAILED; } z3-z3-4.8.7/src/ast/rewriter/dl_rewriter.h000066400000000000000000000011571356505360400203470ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: dl_rewriter.h Abstract: Basic rewriting rules for atalog finite domains. Author: Nikolaj Bjorner (nbjorner) 2012-03-09 Notes: --*/ #ifndef DL_REWRITER_H_ #define DL_REWRITER_H_ #include "ast/dl_decl_plugin.h" #include "ast/rewriter/rewriter_types.h" class dl_rewriter { datalog::dl_decl_util m_util; public: dl_rewriter(ast_manager & m):m_util(m) {} family_id get_fid() const { return m_util.get_family_id(); } br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result); }; #endif z3-z3-4.8.7/src/ast/rewriter/elim_bounds.cpp000066400000000000000000000130211356505360400206510ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: elim_bounds.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-06-28. Revision History: --*/ #ifndef ELIM_BOUNDS_H_ #define ELIM_BOUNDS_H_ #include "ast/used_vars.h" #include "util/obj_hashtable.h" #include "ast/rewriter/var_subst.h" #include "ast/rewriter/elim_bounds.h" #include "ast/ast_pp.h" elim_bounds_cfg::elim_bounds_cfg(ast_manager & m): m(m), m_util(m) { } /** \brief Find bounds of the form (<= x k) (<= (+ x (* -1 y)) k) (<= (+ x (* -1 t)) k) (<= (+ t (* -1 x)) k) x and y are a bound variables, t is a ground term and k is a numeral It also detects >=, and the atom can be negated. */ bool elim_bounds_cfg::is_bound(expr * n, var * & lower, var * & upper) { upper = nullptr; lower = nullptr; bool neg = false; if (m.is_not(n)) { n = to_app(n)->get_arg(0); neg = true; } expr* l = nullptr, *r = nullptr; bool le = false; if (m_util.is_le(n, l, r) && m_util.is_numeral(r)) { n = l; le = true; } else if (m_util.is_ge(n, l, r) && m_util.is_numeral(r)) { n = l; le = false; } else { return false; } if (neg) le = !le; if (is_var(n)) { upper = to_var(n); } else if (m_util.is_add(n, l, r)) { expr * arg1 = l; expr * arg2 = r; if (is_var(arg1)) upper = to_var(arg1); else if (!is_ground(arg1)) return false; rational k; bool is_int; if (m_util.is_mul(arg2) && m_util.is_numeral(to_app(arg2)->get_arg(0), k, is_int) && k.is_minus_one()) { arg2 = to_app(arg2)->get_arg(1); if (is_var(arg2)) lower = to_var(arg2); else if (!is_ground(arg2)) return false; // not supported } else { return false; // not supported } } else { return false; } if (!le) std::swap(upper, lower); return true; } bool elim_bounds_cfg::is_bound(expr * n) { var * lower, * upper; return is_bound(n, lower, upper); } bool elim_bounds_cfg::reduce_quantifier(quantifier * q, expr * n, expr * const * new_patterns, expr * const * new_no_patterns, expr_ref & result, proof_ref & result_pr) { if (!is_forall(q)) { return false; } unsigned num_vars = q->get_num_decls(); ptr_buffer atoms; if (m.is_or(n)) atoms.append(to_app(n)->get_num_args(), to_app(n)->get_args()); else atoms.push_back(n); used_vars used_vars; // collect non-candidates for (expr * a : atoms) { if (!is_bound(a)) used_vars.process(a); } if (used_vars.uses_all_vars(q->get_num_decls())) { return false; } // collect candidates obj_hashtable lowers; obj_hashtable uppers; obj_hashtable candidate_set; ptr_buffer candidates; #define ADD_CANDIDATE(V) if (!lowers.contains(V) && !uppers.contains(V)) { candidate_set.insert(V); candidates.push_back(V); } for (expr * a : atoms) { var * lower = nullptr; var * upper = nullptr; if (is_bound(a, lower, upper)) { if (lower != nullptr && !used_vars.contains(lower->get_idx()) && lower->get_idx() < num_vars) { ADD_CANDIDATE(lower); lowers.insert(lower); } if (upper != nullptr && !used_vars.contains(upper->get_idx()) && upper->get_idx() < num_vars) { ADD_CANDIDATE(upper); uppers.insert(upper); } } } TRACE("elim_bounds", tout << "candidates:\n"; for (unsigned i = 0; i < candidates.size(); i++) tout << mk_pp(candidates[i], m) << "\n";); // remove candidates that have lower and upper bounds for (var * v : candidates) { if (lowers.contains(v) && uppers.contains(v)) candidate_set.erase(v); } TRACE("elim_bounds", tout << "candidates after filter:\n"; for (unsigned i = 0; i < candidates.size(); i++) tout << mk_pp(candidates[i], m) << "\n";); if (candidate_set.empty()) { return false; } // remove bounds that contain variables in candidate_set unsigned j = 0; for (unsigned i = 0; i < atoms.size(); ++i) { expr * a = atoms[i]; var * lower = nullptr; var * upper = nullptr; if (is_bound(a, lower, upper) && ((lower != nullptr && candidate_set.contains(lower)) || (upper != nullptr && candidate_set.contains(upper)))) continue; atoms[j] = a; j++; } if (j == atoms.size()) { return false; } atoms.resize(j); expr * new_body = nullptr; switch (atoms.size()) { case 0: result = m.mk_false(); result_pr = m.mk_rewrite(q, result); TRACE("elim_bounds", tout << mk_pp(q, m) << "\n" << result << "\n";); return true; case 1: new_body = atoms[0]; break; default: new_body = m.mk_or(atoms.size(), atoms.c_ptr()); break; } quantifier_ref new_q(m); new_q = m.update_quantifier(q, new_body); result = elim_unused_vars(m, new_q, params_ref()); result_pr = m.mk_rewrite(q, result); TRACE("elim_bounds", tout << mk_pp(q, m) << "\n" << result << "\n";); return true; } #endif /* ELIM_BOUNDS_H_ */ z3-z3-4.8.7/src/ast/rewriter/elim_bounds.h000066400000000000000000000035261356505360400203270ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: elim_bounds2.h Abstract: Author: Leonardo de Moura (leonardo) 2008-06-28. Revision History: --*/ #ifndef ELIM_BOUNDS2_H_ #define ELIM_BOUNDS2_H_ #include "ast/ast.h" #include "ast/arith_decl_plugin.h" #include "ast/rewriter/rewriter.h" /** \brief Functor for eliminating irrelevant bounds in quantified formulas. Example: (forall (x Int) (y Int) (or (not (>= y x) (not (>= x 0)) (= (select a x) 1)))) The bound (>= y x) is irrelevant and can be eliminated. This can be easily proved by using Fourier-Motzkin elimination. Limitations & Assumptions: - It assumes the input formula was already simplified. - It can only handle bounds in the diff-logic fragment. \remark This operation is subsumed by Fourier-Motzkin elimination. */ class elim_bounds_cfg : public default_rewriter_cfg { ast_manager & m; arith_util m_util; bool is_bound(expr * n, var * & lower, var * & upper); bool is_bound(expr * n); public: elim_bounds_cfg(ast_manager & m); 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); }; /** \brief Functor for applying elim_bounds2 in all universal quantifiers in an expression. Assumption: the formula was already skolemized. */ class elim_bounds_rw : public rewriter_tpl { protected: elim_bounds_cfg m_cfg; public: elim_bounds_rw(ast_manager & m): rewriter_tpl(m, m.proofs_enabled(), m_cfg), m_cfg(m) {} ~elim_bounds_rw() override {} }; #endif /* ELIM_BOUNDS2_H_ */ z3-z3-4.8.7/src/ast/rewriter/enum2bv_rewriter.cpp000066400000000000000000000256211356505360400216630ustar00rootroot00000000000000/*++ Copyright (c) 2016 Microsoft Corporation Module Name: enum2bv_rewriter.cpp Abstract: Conversion from enumeration types to bit-vectors. Author: Nikolaj Bjorner (nbjorner) 2016-10-18 Notes: --*/ #include "ast/rewriter/rewriter.h" #include "ast/rewriter/rewriter_def.h" #include "ast/rewriter/enum2bv_rewriter.h" #include "ast/ast_util.h" #include "ast/ast_pp.h" struct enum2bv_rewriter::imp { ast_manager& m; params_ref m_params; obj_map m_enum2bv; obj_map m_bv2enum; obj_map m_enum2def; expr_ref_vector m_bounds; datatype_util m_dt; func_decl_ref_vector m_enum_consts; func_decl_ref_vector m_enum_bvs; expr_ref_vector m_enum_defs; unsigned_vector m_enum_consts_lim; unsigned m_num_translated; i_sort_pred* m_sort_pred; struct rw_cfg : public default_rewriter_cfg { imp& m_imp; ast_manager& m; datatype_util m_dt; bv_util m_bv; rw_cfg(imp& i, ast_manager & m) : m_imp(i), m(m), m_dt(m), m_bv(m) {} br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { expr_ref a0(m), a1(m); expr_ref_vector _args(m); if (m.is_eq(f) && reduce_arg(args[0], a0) && reduce_arg(args[1], a1)) { result = m.mk_eq(a0, a1); return BR_DONE; } else if (m.is_distinct(f) && reduce_args(num, args, _args)) { result = m.mk_distinct(_args.size(), _args.c_ptr()); return BR_DONE; } else if (m_dt.is_recognizer(f) && reduce_arg(args[0], a0)) { unsigned idx = m_dt.get_recognizer_constructor_idx(f); a1 = m_bv.mk_numeral(rational(idx), get_sort(a0)); result = m.mk_eq(a0, a1); return BR_DONE; } else { check_for_fd(num, args); return BR_FAILED; } } bool reduce_args(unsigned sz, expr*const* as, expr_ref_vector& result) { expr_ref tmp(m); for (unsigned i = 0; i < sz; ++i) { if (!reduce_arg(as[i], tmp)) return false; result.push_back(tmp); } return true; } void throw_non_fd(expr* e) { std::stringstream strm; strm << "unable to handle nested data-type expression " << mk_pp(e, m); throw rewriter_exception(strm.str()); } void check_for_fd(unsigned n, expr* const* args) { for (unsigned i = 0; i < n; ++i) { if (m_imp.is_fd(get_sort(args[i]))) { throw_non_fd(args[i]); } } } bool reduce_arg(expr* a, expr_ref& result) { sort* s = get_sort(a); if (!m_imp.is_fd(s)) { return false; } unsigned bv_size = get_bv_size(s); if (is_var(a)) { result = m.mk_var(to_var(a)->get_idx(), m_bv.mk_sort(bv_size)); return true; } SASSERT(is_app(a)); func_decl* f = to_app(a)->get_decl(); if (m_dt.is_constructor(f)) { unsigned idx = m_dt.get_constructor_idx(f); result = m_bv.mk_numeral(idx, bv_size); } else if (is_uninterp_const(a)) { func_decl* f_fresh; if (m_imp.m_enum2bv.find(f, f_fresh)) { result = m.mk_const(f_fresh); return true; } // create a fresh variable, add bounds constraints for it. unsigned nc = m_dt.get_datatype_num_constructors(s); result = m.mk_fresh_const(f->get_name(), m_bv.mk_sort(bv_size)); f_fresh = to_app(result)->get_decl(); if (!is_power_of_two(nc) || nc == 1) { m_imp.m_bounds.push_back(m_bv.mk_ule(result, m_bv.mk_numeral(nc-1, bv_size))); } expr_ref f_def(m); ptr_vector const& cs = *m_dt.get_datatype_constructors(s); f_def = m.mk_const(cs[nc-1]); for (unsigned i = nc - 1; i > 0; ) { --i; f_def = m.mk_ite(m.mk_eq(result, m_bv.mk_numeral(i,bv_size)), m.mk_const(cs[i]), f_def); } m_imp.m_enum2def.insert(f, f_def); m_imp.m_enum2bv.insert(f, f_fresh); m_imp.m_bv2enum.insert(f_fresh, f); m_imp.m_enum_consts.push_back(f); m_imp.m_enum_bvs.push_back(f_fresh); m_imp.m_enum_defs.push_back(f_def); } else { throw_non_fd(a); } ++m_imp.m_num_translated; return true; } ptr_buffer m_sorts; bool reduce_quantifier( quantifier * q, expr * old_body, expr * const * new_patterns, expr * const * new_no_patterns, expr_ref & result, proof_ref & result_pr) { if (q->get_kind() == lambda_k) return false; m_sorts.reset(); expr_ref_vector bounds(m); bool found = false; for (unsigned i = 0; i < q->get_num_decls(); ++i) { sort* s = q->get_decl_sort(i); if (m_imp.is_fd(s)) { unsigned bv_size = get_bv_size(s); m_sorts.push_back(m_bv.mk_sort(bv_size)); unsigned nc = m_dt.get_datatype_num_constructors(s); if (!is_power_of_two(nc) || nc == 1) { bounds.push_back(m_bv.mk_ule(m.mk_var(q->get_num_decls()-i-1, m_sorts[i]), m_bv.mk_numeral(nc-1, bv_size))); } found = true; } else { m_sorts.push_back(s); } } if (!found) { return false; } expr_ref new_body_ref(old_body, m), tmp(m); if (!bounds.empty()) { switch (q->get_kind()) { case forall_k: new_body_ref = m.mk_implies(mk_and(bounds), new_body_ref); break; case exists_k: bounds.push_back(new_body_ref); new_body_ref = mk_and(bounds); break; case lambda_k: UNREACHABLE(); break; } } result = m.mk_quantifier(q->get_kind(), q->get_num_decls(), m_sorts.c_ptr(), q->get_decl_names(), new_body_ref, q->get_weight(), q->get_qid(), q->get_skid(), q->get_num_patterns(), new_patterns, q->get_num_no_patterns(), new_no_patterns); result_pr = nullptr; return true; } unsigned get_bv_size(sort* s) { unsigned nc = m_dt.get_datatype_num_constructors(s); unsigned bv_size = 1; while ((unsigned)(1 << bv_size) < nc) { ++bv_size; } return bv_size; } }; struct rw : public rewriter_tpl { rw_cfg m_cfg; rw(imp& t, ast_manager & m, params_ref const & p) : rewriter_tpl(m, m.proofs_enabled(), m_cfg), m_cfg(t, m) { } }; rw m_rw; imp(ast_manager& m, params_ref const& p): m(m), m_params(p), m_bounds(m), m_dt(m), m_enum_consts(m), m_enum_bvs(m), m_enum_defs(m), m_num_translated(0), m_sort_pred(nullptr), m_rw(*this, m, p) { } void updt_params(params_ref const & p) {} unsigned get_num_steps() const { return m_rw.get_num_steps(); } void cleanup() { m_rw.cleanup(); } void operator()(expr * e, expr_ref & result, proof_ref & result_proof) { m_rw(e, result, result_proof); } void push() { m_enum_consts_lim.push_back(m_enum_consts.size()); } void pop(unsigned num_scopes) { SASSERT(m_bounds.empty()); // bounds must be flushed before pop. if (num_scopes > 0) { SASSERT(num_scopes <= m_enum_consts_lim.size()); unsigned new_sz = m_enum_consts_lim.size() - num_scopes; unsigned lim = m_enum_consts_lim[new_sz]; for (unsigned i = m_enum_consts.size(); i > lim; ) { --i; func_decl* f = m_enum_consts[i].get(); func_decl* f_fresh = m_enum2bv.find(f); m_bv2enum.erase(f_fresh); m_enum2bv.erase(f); m_enum2def.erase(f); } m_enum_consts_lim.resize(new_sz); m_enum_consts.resize(lim); m_enum_defs.resize(lim); m_enum_bvs.resize(lim); } m_rw.reset(); } void flush_side_constraints(expr_ref_vector& side_constraints) { side_constraints.append(m_bounds); m_bounds.reset(); } bool is_fd(sort* s) { return m_dt.is_enum_sort(s) && (!m_sort_pred || (*m_sort_pred)(s)); } void set_is_fd(i_sort_pred* sp) { m_sort_pred = sp; } }; enum2bv_rewriter::enum2bv_rewriter(ast_manager & m, params_ref const& p) { m_imp = alloc(imp, m, p); } enum2bv_rewriter::~enum2bv_rewriter() { dealloc(m_imp); } void enum2bv_rewriter::updt_params(params_ref const & p) { m_imp->updt_params(p); } ast_manager & enum2bv_rewriter::m() const { return m_imp->m; } unsigned enum2bv_rewriter::get_num_steps() const { return m_imp->get_num_steps(); } void enum2bv_rewriter::cleanup() { ast_manager& mgr = m(); params_ref p = m_imp->m_params; dealloc(m_imp); m_imp = alloc(imp, mgr, p); } obj_map const& enum2bv_rewriter::enum2bv() const { return m_imp->m_enum2bv; } obj_map const& enum2bv_rewriter::bv2enum() const { return m_imp->m_bv2enum; } obj_map const& enum2bv_rewriter::enum2def() const { return m_imp->m_enum2def; } void enum2bv_rewriter::operator()(expr * e, expr_ref & result, proof_ref & result_proof) { (*m_imp)(e, result, result_proof); } void enum2bv_rewriter::push() { m_imp->push(); } void enum2bv_rewriter::pop(unsigned num_scopes) { m_imp->pop(num_scopes); } void enum2bv_rewriter::flush_side_constraints(expr_ref_vector& side_constraints) { m_imp->flush_side_constraints(side_constraints); } unsigned enum2bv_rewriter::num_translated() const { return m_imp->m_num_translated; } void enum2bv_rewriter::set_is_fd(i_sort_pred* sp) const { m_imp->set_is_fd(sp); } z3-z3-4.8.7/src/ast/rewriter/enum2bv_rewriter.h000066400000000000000000000021361356505360400213240ustar00rootroot00000000000000/*++ Copyright (c) 2016 Microsoft Corporation Module Name: enum2bv_rewriter.h Abstract: Conversion from enumeration types to bit-vectors. Author: Nikolaj Bjorner (nbjorner) 2016-10-18 Notes: --*/ #ifndef ENUM_REWRITER_H_ #define ENUM_REWRITER_H_ #include "ast/datatype_decl_plugin.h" #include "ast/rewriter/rewriter_types.h" #include "ast/expr_functors.h" class enum2bv_rewriter { struct imp; imp* m_imp; public: enum2bv_rewriter(ast_manager & m, params_ref const& p); ~enum2bv_rewriter(); void updt_params(params_ref const & p); ast_manager & m() const; unsigned get_num_steps() const; void cleanup(); obj_map const& enum2bv() const; obj_map const& bv2enum() const; obj_map const& enum2def() const; void operator()(expr * e, expr_ref & result, proof_ref & result_proof); void push(); void pop(unsigned num_scopes); void flush_side_constraints(expr_ref_vector& side_constraints); unsigned num_translated() const; void set_is_fd(i_sort_pred* sp) const; }; #endif z3-z3-4.8.7/src/ast/rewriter/expr_replacer.cpp000066400000000000000000000101601356505360400212050ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: expr_replacer.cpp Abstract: Abstract (functor) for replacing constants with expressions. Author: Leonardo (leonardo) 2011-04-29 Notes: --*/ #include "ast/rewriter/expr_replacer.h" #include "ast/rewriter/rewriter_def.h" #include "ast/rewriter/th_rewriter.h" void expr_replacer::operator()(expr * t, expr_ref & result, proof_ref & result_pr) { expr_dependency_ref result_dep(m()); operator()(t, result, result_pr, result_dep); } void expr_replacer::operator()(expr * t, expr_ref & result) { proof_ref pr(m()); operator()(t, result, pr); } struct expr_replacer::scoped_set_subst { expr_replacer & m_r; scoped_set_subst(expr_replacer & r, expr_substitution & s):m_r(r) { m_r.set_substitution(&s); } ~scoped_set_subst() { m_r.set_substitution(nullptr); } }; void expr_replacer::apply_substitution(expr * s, expr * def, proof * def_pr, expr_ref & t) { expr_substitution sub(m()); sub.insert(s, def, def_pr); scoped_set_subst set(*this, sub); (*this)(t); } void expr_replacer::apply_substitution(expr * s, expr * def, expr_ref & t) { expr_substitution sub(m()); sub.insert(s, def); scoped_set_subst set(*this, sub); (*this)(t); } struct default_expr_replacer_cfg : public default_rewriter_cfg { ast_manager & m; expr_substitution * m_subst; expr_dependency_ref m_used_dependencies; default_expr_replacer_cfg(ast_manager & _m): m(_m), m_subst(nullptr), m_used_dependencies(_m) { } bool get_subst(expr * s, expr * & t, proof * & pr) { if (m_subst == nullptr) return false; expr_dependency * d = nullptr; if (m_subst->find(s, t, pr, d)) { m_used_dependencies = m.mk_join(m_used_dependencies, d); return true; } return false; } bool max_steps_exceeded(unsigned num_steps) const { return false; } }; template class rewriter_tpl; class default_expr_replacer : public expr_replacer { default_expr_replacer_cfg m_cfg; rewriter_tpl m_replacer; public: default_expr_replacer(ast_manager & m): m_cfg(m), m_replacer(m, m.proofs_enabled(), m_cfg) { } ast_manager & m() const override { return m_replacer.m(); } void set_substitution(expr_substitution * s) override { m_replacer.cleanup(); m_replacer.cfg().m_subst = s; } void operator()(expr * t, expr_ref & result, proof_ref & result_pr, expr_dependency_ref & result_dep) override { result_dep = nullptr; m_replacer.operator()(t, result, result_pr); if (m_cfg.m_used_dependencies != 0) { result_dep = m_cfg.m_used_dependencies; m_replacer.reset(); // reset cache m_cfg.m_used_dependencies = nullptr; } } unsigned get_num_steps() const override { return m_replacer.get_num_steps(); } void reset() override { m_replacer.reset(); } }; expr_replacer * mk_default_expr_replacer(ast_manager & m) { return alloc(default_expr_replacer, m); } /** \brief Adapter for using th_rewriter as an expr_replacer. */ class th_rewriter2expr_replacer : public expr_replacer { th_rewriter m_r; public: th_rewriter2expr_replacer(ast_manager & m, params_ref const & p): m_r(m, p) { } ~th_rewriter2expr_replacer() override {} ast_manager & m() const override { return m_r.m(); } void set_substitution(expr_substitution * s) override { m_r.set_substitution(s); } void operator()(expr * t, expr_ref & result, proof_ref & result_pr, expr_dependency_ref & result_dep) override { m_r(t, result, result_pr); result_dep = m_r.get_used_dependencies(); m_r.reset_used_dependencies(); } unsigned get_num_steps() const override { return m_r.get_num_steps(); } void reset() override { m_r.reset(); } }; expr_replacer * mk_expr_simp_replacer(ast_manager & m, params_ref const & p) { return alloc(th_rewriter2expr_replacer, m, p); } z3-z3-4.8.7/src/ast/rewriter/expr_replacer.h000066400000000000000000000027161356505360400206620ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: expr_replacer.h Abstract: Abstract (functor) for replacing expressions. Author: Leonardo (leonardo) 2011-04-29 Notes: --*/ #ifndef EXPR_REPLACER_H_ #define EXPR_REPLACER_H_ #include "ast/ast.h" #include "ast/expr_substitution.h" #include "util/params.h" /** \brief Abstract interface for functors that replace constants with expressions. */ class expr_replacer { struct scoped_set_subst; public: virtual ~expr_replacer() {} virtual ast_manager & m() const = 0; virtual void set_substitution(expr_substitution * s) = 0; virtual void operator()(expr * t, expr_ref & result, proof_ref & result_pr, expr_dependency_ref & deps) = 0; virtual void operator()(expr * t, expr_ref & result, proof_ref & result_pr); virtual void operator()(expr * t, expr_ref & result); virtual void operator()(expr_ref & t) { expr_ref s(t, m()); (*this)(s, t); } virtual unsigned get_num_steps() const { return 0; } virtual void reset() = 0; void apply_substitution(expr * s, expr * def, proof * def_pr, expr_ref & t); void apply_substitution(expr * s, expr * def, expr_ref & t); }; /** \brief Create a vanilla replacer. It just applies the substitution. */ expr_replacer * mk_default_expr_replacer(ast_manager & m); /** \brief Apply substitution and simplify. */ expr_replacer * mk_expr_simp_replacer(ast_manager & m, params_ref const & p = params_ref()); #endif z3-z3-4.8.7/src/ast/rewriter/expr_safe_replace.cpp000066400000000000000000000073331356505360400220310ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: expr_safe_replace.cpp Abstract: Version of expr_replace/expr_substitution that is safe for quantifiers. Author: Nikolaj Bjorner (nbjorner) 2012-11-30 Revision History: --*/ #include "ast/rewriter/expr_safe_replace.h" #include "ast/rewriter/var_subst.h" #include "ast/ast_pp.h" void expr_safe_replace::insert(expr* src, expr* dst) { SASSERT(m.get_sort(src) == m.get_sort(dst)); m_src.push_back(src); m_dst.push_back(dst); m_subst.insert(src, dst); } void expr_safe_replace::operator()(expr_ref_vector& es) { expr_ref val(m); for (unsigned i = 0; i < es.size(); ++i) { (*this)(es[i].get(), val); es[i] = val; } } void expr_safe_replace::operator()(expr* e, expr_ref& res) { m_todo.push_back(e); expr* a, *b; while (!m_todo.empty()) { a = m_todo.back(); if (m_cache.contains(a)) { m_todo.pop_back(); } else if (m_subst.find(a, b)) { m_cache.insert(a, b); m_todo.pop_back(); } else if (is_var(a)) { m_cache.insert(a, a); m_todo.pop_back(); } else if (is_app(a)) { app* c = to_app(a); unsigned n = c->get_num_args(); m_args.reset(); bool arg_differs = false; for (unsigned i = 0; i < n; ++i) { expr* d = nullptr, *arg = c->get_arg(i); if (m_cache.find(arg, d)) { m_args.push_back(d); arg_differs |= arg != d; SASSERT(m.get_sort(arg) == m.get_sort(d)); } else { m_todo.push_back(arg); } } if (m_args.size() == n) { if (arg_differs) { b = m.mk_app(c->get_decl(), m_args.size(), m_args.c_ptr()); m_refs.push_back(b); SASSERT(m.get_sort(a) == m.get_sort(b)); } else { b = a; } m_cache.insert(a, b); m_todo.pop_back(); } } else { SASSERT(is_quantifier(a)); quantifier* q = to_quantifier(a); expr_safe_replace replace(m); var_shifter shift(m); expr_ref new_body(m), src(m), dst(m), tmp(m); expr_ref_vector pats(m), nopats(m); unsigned num_decls = q->get_num_decls(); for (unsigned i = 0; i < m_src.size(); ++i) { shift(m_src[i].get(), num_decls, src); shift(m_dst[i].get(), num_decls, dst); replace.insert(src, dst); } unsigned np = q->get_num_patterns(); for (unsigned i = 0; i < np; ++i) { replace(q->get_pattern(i), tmp); pats.push_back(tmp); } np = q->get_num_no_patterns(); for (unsigned i = 0; i < np; ++i) { replace(q->get_no_pattern(i), tmp); nopats.push_back(tmp); } replace(q->get_expr(), new_body); b = m.update_quantifier(q, pats.size(), pats.c_ptr(), nopats.size(), nopats.c_ptr(), new_body); m_refs.push_back(b); m_cache.insert(a, b); m_todo.pop_back(); } } res = m_cache.find(e); m_cache.reset(); m_todo.reset(); m_args.reset(); m_refs.reset(); } void expr_safe_replace::reset() { m_src.reset(); m_dst.reset(); m_subst.reset(); } void expr_safe_replace::apply_substitution(expr* s, expr* def, expr_ref& t) { reset(); insert(s, def); (*this)(t, t); reset(); } z3-z3-4.8.7/src/ast/rewriter/expr_safe_replace.h000066400000000000000000000017711356505360400214760ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: expr_safe_replace.h Abstract: Version of expr_replace/expr_substitution that is safe for quantifiers. Author: Nikolaj Bjorner (nbjorner) 2012-11-30 Revision History: --*/ #ifndef EXPR_SAFE_REPLACE_H_ #define EXPR_SAFE_REPLACE_H_ #include "ast/ast.h" class expr_safe_replace { ast_manager& m; expr_ref_vector m_src; expr_ref_vector m_dst; obj_map m_subst; obj_map m_cache; ptr_vector m_todo, m_args; expr_ref_vector m_refs; public: expr_safe_replace(ast_manager& m): m(m), m_src(m), m_dst(m), m_refs(m) {} void insert(expr* src, expr* dst); void operator()(expr_ref& e) { (*this)(e.get(), e); } void operator()(expr* src, expr_ref& e); void operator()(expr_ref_vector& es); void apply_substitution(expr* s, expr* def, expr_ref& t); void reset(); bool empty() const { return m_subst.empty(); } }; #endif /* EXPR_SAFE_REPLACE_H_ */ z3-z3-4.8.7/src/ast/rewriter/factor_equivs.cpp000066400000000000000000000073131356505360400212320ustar00rootroot00000000000000/*++ Copyright (c) 2017 Arie Gurfinkel Module Name: factor_equivs.cpp Abstract: Factor equivalence classes out of an expression. "Equivalence class structure" for objs. Uses a union_find structure internally. Operations are : -Declare a new equivalence class with a single element -Merge two equivalence classes -Retrieve whether two elements are in the same equivalence class -Iterate on all the elements of the equivalence class of a given element -Iterate on all equivalence classes (and then within them) Author: Julien Braine Arie Gurfinkel Revision History: */ #include "ast/arith_decl_plugin.h" #include "ast/for_each_expr.h" #include "ast/ast_pp.h" #include "ast/rewriter/expr_safe_replace.h" #include "ast/rewriter/factor_equivs.h" /** Factors input vector v into equivalence classes and the rest */ void factor_eqs(expr_ref_vector &v, expr_equiv_class &equiv) { ast_manager &m = v.get_manager(); arith_util arith(m); expr *e1 = nullptr, *e2 = nullptr; flatten_and(v); unsigned j = 0; for (unsigned i = 0; i < v.size(); ++i) { if (m.is_eq(v.get(i), e1, e2)) { if (arith.is_zero(e1)) { std::swap(e1, e2); } // y + -1*x == 0 expr* a0 = nullptr, *a1 = nullptr, *x = nullptr; if (arith.is_zero(e2) && arith.is_add(e1, a0, a1)) { if (arith.is_times_minus_one(a1, x)) { e1 = a0; e2 = x; } else if (arith.is_times_minus_one(a0, x)) { e1 = a1; e2 = x; } } equiv.merge(e1, e2); } else { if (j < i) { v[j] = v.get(i); } j++; } } v.shrink(j); } /** * Chooses a representative of an equivalence class */ expr *choose_rep(expr_equiv_class::eq_class &clazz, ast_manager &m) { expr *rep = nullptr; unsigned rep_sz = 0, elem_sz; for (expr *elem : clazz) { if (!m.is_value(elem)) { elem_sz = get_num_exprs(elem); if (!rep || (rep && rep_sz > elem_sz)) { rep = elem; rep_sz = elem_sz; } } } TRACE("equiv", tout << "Rep: " << mk_pp(rep, m) << "\n"; for (expr *el : clazz) tout << mk_pp(el, m) << "\n"; tout << "RepEnd\n";); return rep; } void rewrite_eqs (expr_ref_vector &v, expr_equiv_class &equiv) { ast_manager &m = v.m(); expr_safe_replace sub(m); for (auto eq_class : equiv) { expr *rep = choose_rep(eq_class, m); for (expr *el : eq_class) { if (el != rep) { sub.insert (el, rep); } } } sub(v); } /** * converts equivalence classes to equalities */ void equiv_to_expr(expr_equiv_class &equiv, expr_ref_vector &out) { ast_manager &m = out.get_manager(); for (auto eq_class : equiv) { expr *rep = choose_rep(eq_class, m); SASSERT(rep); for (expr *elem : eq_class) { if (rep != elem) { out.push_back (m.mk_eq (rep, elem)); } } } } /** * expands equivalence classes to all derivable equalities */ bool equiv_to_expr_full(expr_equiv_class &equiv, expr_ref_vector &out) { ast_manager &m = out.get_manager(); bool dirty = false; for (auto eq_class : equiv) { for (auto a = eq_class.begin(), end = eq_class.end(); a != end; ++a) { expr_equiv_class::iterator b(a); for (++b; b != end; ++b) { out.push_back(m.mk_eq(*a, *b)); dirty = true; } } } return dirty; } z3-z3-4.8.7/src/ast/rewriter/factor_equivs.h000066400000000000000000000121061356505360400206730ustar00rootroot00000000000000/*++ Copyright (c) 2017 Arie Gurfinkel Module Name: factor_equivs.h Abstract: Factor equivalence classes out of an expression. "Equivalence class structure" for objs. Uses a union_find structure internally. Operations are : -Declare a new equivalence class with a single element -Merge two equivalence classes -Retrieve whether two elements are in the same equivalence class -Iterate on all the elements of the equivalence class of a given element -Iterate on all equivalence classes (and then within them) Author: Julien Braine Arie Gurfinkel Revision History: */ #ifndef FACTOR_EQUIVS_H_ #define FACTOR_EQUIVS_H_ #include "util/union_find.h" #include "ast/ast_util.h" ///All functions naturally add their parameters to the union_find class template class obj_equiv_class { basic_union_find m_uf; obj_map m_to_int; ref_vector m_to_obj; unsigned add_elem_impl(OBJ*o) { unsigned id = m_to_obj.size(); m_to_int.insert(o, id); m_to_obj.push_back(o); return id; } unsigned add_if_not_there(OBJ*o) { unsigned id; if(!m_to_int.find(o, id)) { id = add_elem_impl(o); } return id; } public: class iterator; class equiv_iterator; friend class iterator; friend class equiv_iterator; obj_equiv_class(Manager& m) : m_to_obj(m) {} Manager &m() const {return m_to_obj.m();} void add_elem(OBJ*o) { SASSERT(!m_to_int.find(o)); add_elem_impl(o); } //Invalidates all iterators void merge(OBJ* a, OBJ* b) { unsigned v1 = add_if_not_there(a); unsigned v2 = add_if_not_there(b); unsigned tmp1 = m_uf.find(v1); unsigned tmp2 = m_uf.find(v2); m_uf.merge(tmp1, tmp2); } void reset() { m_uf.reset(); m_to_int.reset(); m_to_obj.reset(); } bool are_equiv(OBJ*a, OBJ*b) { unsigned id1 = add_if_not_there(a); unsigned id2 = add_if_not_there(b); return m_uf.find(id1) == m_uf.find(id2); } class iterator { friend class obj_equiv_class; private : const obj_equiv_class& m_ouf; unsigned m_curr_id; bool m_first; iterator(const obj_equiv_class& uf, unsigned id, bool f) : m_ouf(uf), m_curr_id(id), m_first(f) {} public : OBJ*operator*() {return m_ouf.m_to_obj[m_curr_id];} iterator& operator++() { m_curr_id = m_ouf.m_uf.next(m_curr_id); m_first = false; return *this; } bool operator==(const iterator& o) { SASSERT(&m_ouf == &o.m_ouf); return m_first == o.m_first && m_curr_id == o.m_curr_id; } bool operator!=(const iterator& o) {return !(*this == o);} }; iterator begin(OBJ*o) { unsigned id = add_if_not_there(o); return iterator(*this, id, true); } iterator end(OBJ*o) { unsigned id = add_if_not_there(o); return iterator(*this, id, false); } class eq_class { private : iterator m_begin; iterator m_end; public : eq_class(const iterator& a, const iterator& b) : m_begin(a), m_end(b) {} iterator begin() {return m_begin;} iterator end() {return m_end;} }; class equiv_iterator { friend class obj_equiv_class; private : const obj_equiv_class& m_ouf; unsigned m_rootnb; equiv_iterator(const obj_equiv_class& uf, unsigned nb) : m_ouf(uf), m_rootnb(nb) { while(m_rootnb != m_ouf.m_to_obj.size() && m_ouf.m_uf.is_root(m_rootnb) != true) { m_rootnb++; } } public : eq_class operator*() { return eq_class(iterator(m_ouf, m_rootnb, true), iterator(m_ouf, m_rootnb, false)); } equiv_iterator& operator++() { do { m_rootnb++; } while(m_rootnb != m_ouf.m_to_obj.size() && m_ouf.m_uf.is_root(m_rootnb) != true); return *this; } bool operator==(const equiv_iterator& o) { SASSERT(&m_ouf == &o.m_ouf); return m_rootnb == o.m_rootnb; } bool operator!=(const equiv_iterator& o) {return !(*this == o);} }; equiv_iterator begin() {return equiv_iterator(*this, 0);} equiv_iterator end() {return equiv_iterator(*this, m_to_obj.size());} }; typedef obj_equiv_class expr_equiv_class; /** * Factors input vector v into equivalence classes and the rest */ void factor_eqs(expr_ref_vector &v, expr_equiv_class &equiv); /** * Rewrite expressions in v by choosing a representative from the * equivalence class. */ void rewrite_eqs(expr_ref_vector &v, expr_equiv_class &equiv); /** * converts equivalence classes to equalities */ void equiv_to_expr(expr_equiv_class &equiv, expr_ref_vector &out); /** * expands equivalence classes to all derivable equalities */ bool equiv_to_expr_full(expr_equiv_class &equiv, expr_ref_vector &out); #endif z3-z3-4.8.7/src/ast/rewriter/factor_rewriter.cpp000066400000000000000000000246401356505360400215630ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: factor_rewriter.cpp Abstract: Rewriting utilities for factoring polynomials in equations, and inequalities. Author: Nikolaj (nbjorner) 2011-19-05 Notes: --*/ #include "ast/rewriter/factor_rewriter.h" #include "ast/ast_pp.h" #include "ast/rewriter/rewriter_def.h" factor_rewriter::factor_rewriter(ast_manager & m): m_manager(m), m_arith(m), m_factors(m) { } br_status factor_rewriter::mk_app_core( func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { if (m().is_eq(f)) { SASSERT(num_args == 2); return mk_eq(args[0], args[1], result); } if(f->get_family_id() == a().get_family_id()) { switch (f->get_decl_kind()) { case OP_LE: SASSERT(num_args == 2); return mk_le(args[0], args[1], result); case OP_GE: SASSERT(num_args == 2); return mk_ge(args[0], args[1], result); case OP_LT: SASSERT(num_args == 2); return mk_lt(args[0], args[1], result); case OP_GT: SASSERT(num_args == 2); return mk_gt(args[0], args[1], result); default: return BR_FAILED; } } return BR_FAILED; } br_status factor_rewriter::mk_eq(expr * arg1, expr * arg2, expr_ref & result) { if (!a().is_real(arg1) && !m_arith.is_int(arg1)) { return BR_FAILED; } mk_adds(arg1, arg2); mk_muls(); if (m_muls.empty()) { result = m().mk_true(); return BR_DONE; } if (!extract_factors()) { TRACE("factor_rewriter", tout << mk_pp(arg1, m()) << " = " << mk_pp(arg2, m()) << "\n";); return BR_FAILED; } powers_t::iterator it = m_powers.begin(), end = m_powers.end(); expr_ref_vector eqs(m()); for(; it != end; ++it) { expr* e = it->m_key; eqs.push_back(m().mk_eq(e, a().mk_numeral(rational(0), m().get_sort(e)))); } result = m().mk_or(eqs.size(), eqs.c_ptr()); return BR_DONE; } br_status factor_rewriter::mk_le(expr * arg1, expr * arg2, expr_ref & result) { mk_adds(arg1, arg2); mk_muls(); if (m_muls.empty()) { result = m().mk_true(); return BR_DONE; } if (!extract_factors()) { TRACE("factor_rewriter", tout << mk_pp(arg1, m()) << " <= " << mk_pp(arg2, m()) << "\n";); return BR_FAILED; } // a^2 * b^3 * c <= 0 -> // a = 0 \/ (b = 0 \/ b > 0 & c <= 0 \/ b < 0 & c >= 0) // expr_ref neg(m()); expr_ref_vector eqs(m()); mk_is_negative(neg, eqs); eqs.push_back(neg); result = m().mk_or(eqs.size(), eqs.c_ptr()); TRACE("factor_rewriter", tout << mk_pp(arg1, m()) << " <= " << mk_pp(arg2, m()) << "\n"; tout << mk_pp(result.get(), m()) << "\n";); return BR_DONE; } br_status factor_rewriter::mk_lt(expr * arg1, expr * arg2, expr_ref & result) { mk_adds(arg1, arg2); mk_muls(); if (m_muls.empty()) { result = m().mk_false(); return BR_DONE; } if (!extract_factors()) { TRACE("factor_rewriter", tout << mk_pp(arg1, m()) << " < " << mk_pp(arg2, m()) << "\n";); return BR_FAILED; } // a^2 * b^3 * c < 0 -> // a != 0 /\ (b > 0 & c < 0 \/ b < 0 & c > 0) // expr_ref neg(m()); expr_ref_vector eqs(m()); mk_is_negative(neg, eqs); for (unsigned i = 0; i < eqs.size(); ++i) { eqs[i] = m().mk_not(eqs[i].get()); } eqs.push_back(neg); result = m().mk_and(eqs.size(), eqs.c_ptr()); TRACE("factor_rewriter", tout << mk_pp(result.get(), m()) << "\n";); return BR_DONE; } void factor_rewriter::mk_is_negative(expr_ref& result, expr_ref_vector& eqs) { powers_t::iterator it = m_powers.begin(), end = m_powers.end(); SASSERT(m_powers.size() >= 1); SASSERT(it != end); expr_ref neg0(m()), neg(m()), pos0(m()), pos(m()), tmp(m()); expr* e = it->m_key; expr_ref zero(a().mk_numeral(rational(0), m().get_sort(e)), m()); expr_ref_vector conjs(m()); pos0 = m().mk_true(); neg0 = m().mk_false(); for(; it != end; ++it) { e = it->m_key; eqs.push_back(m().mk_eq(zero, e)); if (!even(it->m_value)) { pos = a().mk_lt(zero, e); neg = a().mk_lt(e, zero); if (m().is_false(neg0)) { neg0 = neg; pos0 = pos; } else { tmp = m().mk_or(m().mk_and(pos, pos0), m().mk_and(neg, neg0)); neg0 = m().mk_or(m().mk_and(neg, pos0), m().mk_and(pos, neg0)); pos0 = tmp; } } } result = neg0; } // convert arg1 - arg2 into // sum of monomials // m_adds: sum of products. // m_muls: list of products void factor_rewriter::mk_adds(expr* arg1, expr* arg2) { m_adds.reset(); m_adds.push_back(std::make_pair(arg1, true)); m_adds.push_back(std::make_pair(arg2, false)); rational k; for (unsigned i = 0; i < m_adds.size();) { bool sign = m_adds[i].second; expr* _e = m_adds[i].first; TRACE("factor_rewriter", tout << i << " " << mk_pp(_e, m_manager) << "\n";); if (!is_app(_e)) { ++i; continue; } app* e = to_app(_e); if (a().is_add(e) && e->get_num_args() > 0) { m_adds[i].first = e->get_arg(0); for (unsigned j = 1; j < e->get_num_args(); ++j) { m_adds.push_back(std::make_pair(e->get_arg(j),sign)); } } else if (a().is_sub(e) && e->get_num_args() > 0) { m_adds[i].first = e->get_arg(0); for (unsigned j = 1; j < e->get_num_args(); ++j) { m_adds.push_back(std::make_pair(e->get_arg(j),!sign)); } } else if (a().is_uminus(e)) { m_adds[i].first = e->get_arg(0); m_adds[i].second = !sign; } else if (a().is_numeral(e, k) && k.is_zero()) { unsigned sz = m_adds.size(); m_adds[i] = m_adds[sz-1]; m_adds.resize(sz-1); } else { ++i; } } TRACE("factor_rewriter", for (unsigned i = 0; i < m_adds.size(); ++i) { if (!m_adds[i].second) tout << "-"; else tout << "+"; tout << mk_pp(m_adds[i].first, m()) << " "; } tout << "\n"; ); } void factor_rewriter::mk_muls() { m_muls.reset(); for (unsigned i = 0; i < m_adds.size(); ++i) { m_muls.push_back(ptr_vector()); m_muls.back().push_back(m_adds[i].first); mk_expand_muls(m_muls.back()); if (m_muls.back().empty()) { m_muls.pop_back(); m_adds.erase(m_adds.begin() + i); --i; } } TRACE("factor_rewriter", for (unsigned i = 0; i < m_muls.size(); ++i) { for (unsigned j = 0; j < m_muls[i].size(); ++j) { tout << mk_pp(m_muls[i][j], m()) << " "; } tout << "\n"; } tout << "\n"; ); } void factor_rewriter::mk_expand_muls(ptr_vector& muls) { for (unsigned i = 0; i < muls.size(); ) { expr* _e = muls[i]; if (!is_app(_e)) { ++i; continue; } app* e = to_app(_e); if (a().is_mul(e) && e->get_num_args() > 0) { muls[i] = e->get_arg(0); for (unsigned j = 1; j < e->get_num_args(); ++j) { muls.push_back(e->get_arg(j)); } } else { ++i; } } } bool factor_rewriter::extract_factors() { m_factors.reset(); unsigned_vector pos; expr* e; SASSERT(!m_muls.empty()); if (m_muls.size() == 1) { if (m_muls[0].size() > 1) { m_factors.append(m_muls[0].size(), m_muls[0].c_ptr()); if (!m_adds[0].second) { bool found_numeral = false; sort* s = m().get_sort(m_muls[0][0]); rational v; for (unsigned i = 0; !found_numeral && i < m_factors.size(); ++i) { if (a().is_numeral(m_factors[i].get(), v)) { m_factors[i] = a().mk_numeral(-v, s); found_numeral = true; } } if (!found_numeral) { m_factors.push_back(a().mk_numeral(rational(-1),s)); } } collect_powers(); return true; } return false; } for (unsigned i = 0; i < m_muls[0].size(); ++i) { pos.reset(); pos.push_back(i); e = m_muls[0][i]; bool ok = true; for (unsigned j = 1; ok && j < m_muls.size(); ++j) { ok = false; unsigned k = 0; for (k = 0; !ok && k < m_muls[j].size(); ++k) { ok = m_muls[j][k] == e; } pos.push_back(k-1); } if (ok) { SASSERT(pos.size() == m_muls.size()); m_factors.push_back(e); for (unsigned j = 0; j < pos.size(); ++j) { m_muls[j].erase(m_muls[j].begin() + pos[j]); } --i; } } if (m_factors.empty()) { return false; } SASSERT(m_muls.size() == m_adds.size()); expr_ref_vector trail(m()); sort* s = m().get_sort(m_factors[0].get()); for (unsigned i = 0; i < m_adds.size(); ++i) { switch(m_muls[i].size()) { case 0: e = a().mk_numeral(rational(1), s); break; case 1: e = m_muls[i][0]; break; default: e = a().mk_mul(m_muls[i].size(), m_muls[i].c_ptr()); break; } if (!m_adds[i].second) { e = a().mk_uminus(e); } trail.push_back(e); } switch(trail.size()) { case 0: break; case 1: m_factors.push_back(trail[0].get()); break; default: m_factors.push_back(a().mk_add(trail.size(), trail.c_ptr())); break; } TRACE("factor_rewriter", for (unsigned i = 0; i < m_factors.size(); ++i) { tout << mk_pp(m_factors[i].get(), m()) << " "; } tout << "\n"; ); collect_powers(); return true; } void factor_rewriter::collect_powers() { m_powers.reset(); for (unsigned i = 0; i < m_factors.size(); ++i) { obj_map::obj_map_entry* entry = m_powers.insert_if_not_there2(m_factors[i].get(), 0); if (entry) { ++(entry->get_data().m_value); } } } template class rewriter_tpl; z3-z3-4.8.7/src/ast/rewriter/factor_rewriter.h000066400000000000000000000044361356505360400212310ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: factor_rewriter.h Abstract: Rewriting utilities for factoring polynomials in equations, and inequalities. Author: Nikolaj (nbjorner) 2011-19-05 Notes: --*/ #ifndef FACTOR_REWRITER_H_ #define FACTOR_REWRITER_H_ #include "ast/ast.h" #include "ast/rewriter/rewriter.h" #include "ast/arith_decl_plugin.h" class factor_rewriter { typedef obj_map powers_t; ast_manager & m_manager; arith_util m_arith; powers_t m_powers; vector > m_adds; vector > m_muls; expr_ref_vector m_factors; public: factor_rewriter(ast_manager & m); ast_manager & m() const { return m_manager; } arith_util & a() { return m_arith; } br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result); private: br_status mk_eq(expr * arg1, expr * arg2, expr_ref & result); br_status mk_le(expr * arg1, expr * arg2, expr_ref & result); br_status mk_lt(expr * arg1, expr * arg2, expr_ref & result); br_status mk_ge(expr * a1, expr * a2, expr_ref & r) { return mk_le(a2,a1,r); } br_status mk_gt(expr * a1, expr * a2, expr_ref & r) { return mk_lt(a2,a1,r); } void mk_adds(expr* arg1, expr* arg2); void mk_muls(); void mk_expand_muls(ptr_vector& muls); void collect_powers(); bool extract_factors(); bool even(unsigned n) const { return 0 == (n & 0x1); } void mk_is_negative(expr_ref& result, expr_ref_vector& eqs); }; struct factor_rewriter_cfg : public default_rewriter_cfg { factor_rewriter m_r; bool rewrite_patterns() const { return false; } bool flat_assoc(func_decl * f) 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; return m_r.mk_app_core(f, num, args, result); } factor_rewriter_cfg(ast_manager & m):m_r(m) {} }; class factor_rewriter_star : public rewriter_tpl { factor_rewriter_cfg m_cfg; public: factor_rewriter_star(ast_manager & m): rewriter_tpl(m, false, m_cfg), m_cfg(m) {} }; #endif z3-z3-4.8.7/src/ast/rewriter/fpa_rewriter.cpp000066400000000000000000000670151356505360400210560ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: fpa_rewriter.cpp Abstract: Basic rewriting rules for floating point numbers. Author: Leonardo (leonardo) 2012-02-02 Notes: --*/ #include "ast/rewriter/fpa_rewriter.h" #include "ast/rewriter/fpa_rewriter_params.hpp" #include "ast/ast_smt2_pp.h" fpa_rewriter::fpa_rewriter(ast_manager & m, params_ref const & p) : m_util(m), m_fm(m_util.fm()), m_hi_fp_unspecified(false) { updt_params(p); } fpa_rewriter::~fpa_rewriter() { } void fpa_rewriter::updt_params(params_ref const & _p) { fpa_rewriter_params p(_p); m_hi_fp_unspecified = p.hi_fp_unspecified(); } void fpa_rewriter::get_param_descrs(param_descrs & r) { } br_status fpa_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { br_status st = BR_FAILED; SASSERT(f->get_family_id() == get_fid()); fpa_op_kind k = (fpa_op_kind)f->get_decl_kind(); switch (k) { case OP_FPA_RM_NEAREST_TIES_TO_EVEN: case OP_FPA_RM_NEAREST_TIES_TO_AWAY: case OP_FPA_RM_TOWARD_POSITIVE: case OP_FPA_RM_TOWARD_NEGATIVE: case OP_FPA_RM_TOWARD_ZERO: SASSERT(num_args == 0); result = m().mk_app(f, (expr * const *)nullptr); st = BR_DONE; break; case OP_FPA_PLUS_INF: case OP_FPA_MINUS_INF: case OP_FPA_NAN: case OP_FPA_PLUS_ZERO: case OP_FPA_MINUS_ZERO: SASSERT(num_args == 0); result = m().mk_app(f, (expr * const *)nullptr); st = BR_DONE; break; case OP_FPA_NUM: SASSERT(num_args == 0); result = m().mk_app(f, (expr * const *)nullptr); st = BR_DONE; break; case OP_FPA_ADD: SASSERT(num_args == 3); st = mk_add(args[0], args[1], args[2], result); break; case OP_FPA_SUB: SASSERT(num_args == 3); st = mk_sub(args[0], args[1], args[2], result); break; case OP_FPA_NEG: SASSERT(num_args == 1); st = mk_neg(args[0], result); break; case OP_FPA_MUL: SASSERT(num_args == 3); st = mk_mul(args[0], args[1], args[2], result); break; case OP_FPA_DIV: SASSERT(num_args == 3); st = mk_div(args[0], args[1], args[2], result); break; case OP_FPA_REM: SASSERT(num_args == 2); st = mk_rem(args[0], args[1], result); break; case OP_FPA_ABS: SASSERT(num_args == 1); st = mk_abs(args[0], result); break; case OP_FPA_MIN: SASSERT(num_args == 2); st = mk_min(args[0], args[1], result); break; case OP_FPA_MAX: SASSERT(num_args == 2); st = mk_max(args[0], args[1], result); break; case OP_FPA_FMA: SASSERT(num_args == 4); st = mk_fma(args[0], args[1], args[2], args[3], result); break; case OP_FPA_SQRT: SASSERT(num_args == 2); st = mk_sqrt(args[0], args[1], result); break; case OP_FPA_ROUND_TO_INTEGRAL: SASSERT(num_args == 2); st = mk_round_to_integral(args[0], args[1], result); break; case OP_FPA_EQ: SASSERT(num_args == 2); st = mk_float_eq(args[0], args[1], result); break; case OP_FPA_LT: SASSERT(num_args == 2); st = mk_lt(args[0], args[1], result); break; case OP_FPA_GT: SASSERT(num_args == 2); st = mk_gt(args[0], args[1], result); break; case OP_FPA_LE: SASSERT(num_args == 2); st = mk_le(args[0], args[1], result); break; case OP_FPA_GE: SASSERT(num_args == 2); st = mk_ge(args[0], args[1], result); break; case OP_FPA_IS_ZERO: SASSERT(num_args == 1); st = mk_is_zero(args[0], result); break; case OP_FPA_IS_NAN: SASSERT(num_args == 1); st = mk_is_nan(args[0], result); break; case OP_FPA_IS_INF: SASSERT(num_args == 1); st = mk_is_inf(args[0], result); break; case OP_FPA_IS_NORMAL: SASSERT(num_args == 1); st = mk_is_normal(args[0], result); break; case OP_FPA_IS_SUBNORMAL: SASSERT(num_args == 1); st = mk_is_subnormal(args[0], result); break; case OP_FPA_IS_NEGATIVE: SASSERT(num_args == 1); st = mk_is_negative(args[0], result); break; case OP_FPA_IS_POSITIVE: SASSERT(num_args == 1); st = mk_is_positive(args[0], result); break; case OP_FPA_FP: SASSERT(num_args == 3); st = mk_fp(args[0], args[1], args[2], result); break; case OP_FPA_TO_FP: st = mk_to_fp(f, num_args, args, result); break; case OP_FPA_TO_FP_UNSIGNED: SASSERT(num_args == 2); st = mk_to_fp_unsigned(f, args[0], args[1], result); break; case OP_FPA_TO_UBV: SASSERT(num_args == 2); st = mk_to_ubv(f, args[0], args[1], result); break; case OP_FPA_TO_SBV: SASSERT(num_args == 2); st = mk_to_sbv(f, args[0], args[1], result); break; case OP_FPA_TO_IEEE_BV: SASSERT(num_args == 1); st = mk_to_ieee_bv(f, args[0], result); break; case OP_FPA_TO_REAL: SASSERT(num_args == 1); st = mk_to_real(args[0], result); break; case OP_FPA_BVWRAP: SASSERT(num_args == 1); st = mk_bvwrap(args[0], result); break; case OP_FPA_BV2RM: SASSERT(num_args == 1); st = mk_bv2rm(args[0], result); break; default: NOT_IMPLEMENTED_YET(); } return st; } br_status fpa_rewriter::mk_to_fp(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { SASSERT(f->get_num_parameters() == 2); SASSERT(f->get_parameter(0).is_int()); SASSERT(f->get_parameter(1).is_int()); scoped_mpf v(m_fm); mpf_rounding_mode rmv; rational r1, r2, r3; unsigned bvs1, bvs2, bvs3; unsigned ebits = f->get_parameter(0).get_int(); unsigned sbits = f->get_parameter(1).get_int(); if (num_args == 1) { if (m_util.bu().is_numeral(args[0], r1, bvs1)) { // BV -> float SASSERT(bvs1 == sbits + ebits); unsynch_mpz_manager & mpzm = m_fm.mpz_manager(); scoped_mpz sig(mpzm), exp(mpzm); const mpz & sm1 = m_fm.m_powers2(sbits - 1); const mpz & em1 = m_fm.m_powers2(ebits); const mpq & q = r1.to_mpq(); SASSERT(mpzm.is_one(q.denominator())); scoped_mpz z(mpzm); z = q.numerator(); mpzm.rem(z, sm1, sig); mpzm.div(z, sm1, z); mpzm.rem(z, em1, exp); mpzm.div(z, em1, z); SASSERT(mpzm.is_int64(exp)); mpf_exp_t mpf_exp = mpzm.get_int64(exp); mpf_exp = m_fm.unbias_exp(ebits, mpf_exp); m_fm.set(v, ebits, sbits, !mpzm.is_zero(z), mpf_exp, sig); TRACE("fp_rewriter", tout << "sgn: " << !mpzm.is_zero(z) << std::endl; tout << "sig: " << mpzm.to_string(sig) << std::endl; tout << "exp: " << mpf_exp << std::endl; tout << "v: " << m_fm.to_string(v) << std::endl;); result = m_util.mk_value(v); return BR_DONE; } } else if (num_args == 2) { if (!m_util.is_rm_numeral(args[0], rmv)) return BR_FAILED; if (m_util.au().is_numeral(args[1], r1)) { // rm + real -> float TRACE("fp_rewriter", tout << "r: " << r1 << std::endl;); scoped_mpf v(m_fm); m_fm.set(v, ebits, sbits, rmv, r1.to_mpq()); result = m_util.mk_value(v); // TRACE("fp_rewriter", tout << "result: " << result << std::endl; ); return BR_DONE; } else if (m_util.is_numeral(args[1], v)) { // rm + float -> float TRACE("fp_rewriter", tout << "v: " << m_fm.to_string(v) << std::endl; ); scoped_mpf vf(m_fm); m_fm.set(vf, ebits, sbits, rmv, v); result = m_util.mk_value(vf); // TRACE("fp_rewriter", tout << "result: " << result << std::endl; ); return BR_DONE; } else if (m_util.bu().is_numeral(args[1], r1, bvs1)) { // rm + signed bv -> float TRACE("fp_rewriter", tout << "r1: " << r1 << std::endl;); r1 = m_util.bu().norm(r1, bvs1, true); TRACE("fp_rewriter", tout << "r1 norm: " << r1 << std::endl;); m_fm.set(v, ebits, sbits, rmv, r1.to_mpq()); result = m_util.mk_value(v); return BR_DONE; } } else if (num_args == 3) { if (m_util.is_rm_numeral(args[0], rmv) && m_util.au().is_real(args[1]) && m_util.au().is_int(args[2])) { // rm + real + int -> float if (!m_util.is_rm_numeral(args[0], rmv) || !m_util.au().is_numeral(args[1], r1) || !m_util.au().is_numeral(args[2], r2)) return BR_FAILED; TRACE("fp_rewriter", tout << "r1: " << r1 << ", r2: " << r2 << "\n";); m_fm.set(v, ebits, sbits, rmv, r2.to_mpq().numerator(), r1.to_mpq()); result = m_util.mk_value(v); return BR_DONE; } else if (m_util.is_rm_numeral(args[0], rmv) && m_util.au().is_int(args[1]) && m_util.au().is_real(args[2])) { // rm + int + real -> float if (!m_util.is_rm_numeral(args[0], rmv) || !m_util.au().is_numeral(args[1], r1) || !m_util.au().is_numeral(args[2], r2)) return BR_FAILED; TRACE("fp_rewriter", tout << "r1: " << r1 << ", r2: " << r2 << "\n";); m_fm.set(v, ebits, sbits, rmv, r1.to_mpq().numerator(), r2.to_mpq()); result = m_util.mk_value(v); return BR_DONE; } else if (m_util.bu().is_numeral(args[0], r1, bvs1) && m_util.bu().is_numeral(args[1], r2, bvs2) && m_util.bu().is_numeral(args[2], r3, bvs3)) { // 3 BV -> float SASSERT(m_fm.mpz_manager().is_one(r2.to_mpq().denominator())); SASSERT(m_fm.mpz_manager().is_one(r3.to_mpq().denominator())); SASSERT(m_fm.mpz_manager().is_int64(r3.to_mpq().numerator())); mpf_exp_t biased_exp = m_fm.mpz_manager().get_int64(r2.to_mpq().numerator()); m_fm.set(v, bvs2, bvs3 + 1, r1.is_one(), m_fm.unbias_exp(bvs2, biased_exp), r3.to_mpq().numerator()); TRACE("fp_rewriter", tout << "v = " << m_fm.to_string(v) << std::endl;); result = m_util.mk_value(v); return BR_DONE; } } return BR_FAILED; } br_status fpa_rewriter::mk_to_fp_unsigned(func_decl * f, expr * arg1, expr * arg2, expr_ref & result) { SASSERT(f->get_num_parameters() == 2); SASSERT(f->get_parameter(0).is_int()); SASSERT(f->get_parameter(1).is_int()); unsigned ebits = f->get_parameter(0).get_int(); unsigned sbits = f->get_parameter(1).get_int(); mpf_rounding_mode rmv; rational r; unsigned bvs; if (m_util.is_rm_numeral(arg1, rmv) && m_util.bu().is_numeral(arg2, r, bvs)) { scoped_mpf v(m_fm); m_fm.set(v, ebits, sbits, rmv, r.to_mpq()); result = m_util.mk_value(v); return BR_DONE; } return BR_FAILED; } br_status fpa_rewriter::mk_add(expr * arg1, expr * arg2, expr * arg3, expr_ref & result) { mpf_rounding_mode rm; if (m_util.is_rm_numeral(arg1, rm)) { scoped_mpf v2(m_fm), v3(m_fm); if (m_util.is_numeral(arg2, v2) && m_util.is_numeral(arg3, v3)) { scoped_mpf t(m_fm); m_fm.add(rm, v2, v3, t); result = m_util.mk_value(t); return BR_DONE; } } return BR_FAILED; } br_status fpa_rewriter::mk_sub(expr * arg1, expr * arg2, expr * arg3, expr_ref & result) { // a - b = a + (-b) result = m_util.mk_add(arg1, arg2, m_util.mk_neg(arg3)); return BR_REWRITE2; } br_status fpa_rewriter::mk_mul(expr * arg1, expr * arg2, expr * arg3, expr_ref & result) { mpf_rounding_mode rm; if (m_util.is_rm_numeral(arg1, rm)) { scoped_mpf v2(m_fm), v3(m_fm); if (m_util.is_numeral(arg2, v2) && m_util.is_numeral(arg3, v3)) { scoped_mpf t(m_fm); m_fm.mul(rm, v2, v3, t); result = m_util.mk_value(t); return BR_DONE; } } return BR_FAILED; } br_status fpa_rewriter::mk_div(expr * arg1, expr * arg2, expr * arg3, expr_ref & result) { mpf_rounding_mode rm; if (m_util.is_rm_numeral(arg1, rm)) { scoped_mpf v2(m_fm), v3(m_fm); if (m_util.is_numeral(arg2, v2) && m_util.is_numeral(arg3, v3)) { scoped_mpf t(m_fm); m_fm.div(rm, v2, v3, t); result = m_util.mk_value(t); return BR_DONE; } } return BR_FAILED; } br_status fpa_rewriter::mk_neg(expr * arg1, expr_ref & result) { if (m_util.is_nan(arg1)) { // -nan --> nan result = arg1; return BR_DONE; } if (m_util.is_pinf(arg1)) { // - +oo --> -oo result = m_util.mk_ninf(m().get_sort(arg1)); return BR_DONE; } if (m_util.is_ninf(arg1)) { // - -oo -> +oo result = m_util.mk_pinf(m().get_sort(arg1)); return BR_DONE; } if (m_util.is_neg(arg1)) { // - - a --> a result = to_app(arg1)->get_arg(0); return BR_DONE; } scoped_mpf v1(m_fm); if (m_util.is_numeral(arg1, v1)) { m_fm.neg(v1); result = m_util.mk_value(v1); return BR_DONE; } return BR_FAILED; } br_status fpa_rewriter::mk_rem(expr * arg1, expr * arg2, expr_ref & result) { scoped_mpf v1(m_fm), v2(m_fm); if (m_util.is_numeral(arg1, v1) && m_util.is_numeral(arg2, v2)) { scoped_mpf t(m_fm); m_fm.rem(v1, v2, t); result = m_util.mk_value(t); return BR_DONE; } return BR_FAILED; } br_status fpa_rewriter::mk_abs(expr * arg1, expr_ref & result) { if (m_util.is_nan(arg1)) { result = arg1; return BR_DONE; } scoped_mpf v(m_fm); if (m_util.is_numeral(arg1, v)) { if (m_fm.is_neg(v)) m_fm.neg(v); result = m_util.mk_value(v); return BR_DONE; } return BR_FAILED; } br_status fpa_rewriter::mk_min(expr * arg1, expr * arg2, expr_ref & result) { if (m_util.is_nan(arg1)) { result = arg2; return BR_DONE; } else if (m_util.is_nan(arg2)) { result = arg1; return BR_DONE; } scoped_mpf v1(m_fm), v2(m_fm); if (m_util.is_numeral(arg1, v1) && m_util.is_numeral(arg2, v2)) { if (m_fm.is_zero(v1) && m_fm.is_zero(v2) && m_fm.sgn(v1) != m_fm.sgn(v2)) return BR_FAILED; scoped_mpf r(m_fm); m_fm.minimum(v1, v2, r); result = m_util.mk_value(r); return BR_DONE; } return BR_FAILED; } br_status fpa_rewriter::mk_max(expr * arg1, expr * arg2, expr_ref & result) { if (m_util.is_nan(arg1)) { result = arg2; return BR_DONE; } else if (m_util.is_nan(arg2)) { result = arg1; return BR_DONE; } scoped_mpf v1(m_fm), v2(m_fm); if (m_util.is_numeral(arg1, v1) && m_util.is_numeral(arg2, v2)) { if (m_fm.is_zero(v1) && m_fm.is_zero(v2) && m_fm.sgn(v1) != m_fm.sgn(v2)) return BR_FAILED; scoped_mpf r(m_fm); m_fm.maximum(v1, v2, r); result = m_util.mk_value(r); return BR_DONE; } return BR_FAILED; } br_status fpa_rewriter::mk_fma(expr * arg1, expr * arg2, expr * arg3, expr * arg4, expr_ref & result) { mpf_rounding_mode rm; if (m_util.is_rm_numeral(arg1, rm)) { scoped_mpf v2(m_fm), v3(m_fm), v4(m_fm); if (m_util.is_numeral(arg2, v2) && m_util.is_numeral(arg3, v3) && m_util.is_numeral(arg4, v4)) { scoped_mpf t(m_fm); m_fm.fma(rm, v2, v3, v4, t); result = m_util.mk_value(t); return BR_DONE; } } return BR_FAILED; } br_status fpa_rewriter::mk_sqrt(expr * arg1, expr * arg2, expr_ref & result) { mpf_rounding_mode rm; if (m_util.is_rm_numeral(arg1, rm)) { scoped_mpf v2(m_fm); if (m_util.is_numeral(arg2, v2)) { scoped_mpf t(m_fm); m_fm.sqrt(rm, v2, t); result = m_util.mk_value(t); return BR_DONE; } } return BR_FAILED; } br_status fpa_rewriter::mk_round_to_integral(expr * arg1, expr * arg2, expr_ref & result) { mpf_rounding_mode rm; if (m_util.is_rm_numeral(arg1, rm)) { scoped_mpf v2(m_fm); if (m_util.is_numeral(arg2, v2)) { scoped_mpf t(m_fm); m_fm.round_to_integral(rm, v2, t); result = m_util.mk_value(t); return BR_DONE; } } return BR_FAILED; } // This the floating point theory == br_status fpa_rewriter::mk_float_eq(expr * arg1, expr * arg2, expr_ref & result) { scoped_mpf v1(m_fm), v2(m_fm); if (m_util.is_numeral(arg1, v1) && m_util.is_numeral(arg2, v2)) { result = (m_fm.eq(v1, v2)) ? m().mk_true() : m().mk_false(); return BR_DONE; } return BR_FAILED; } // Return (= arg NaN) app * fpa_rewriter::mk_eq_nan(expr * arg) { return m().mk_eq(arg, m_util.mk_nan(m().get_sort(arg))); } // Return (not (= arg NaN)) app * fpa_rewriter::mk_neq_nan(expr * arg) { return m().mk_not(mk_eq_nan(arg)); } br_status fpa_rewriter::mk_lt(expr * arg1, expr * arg2, expr_ref & result) { if (m_util.is_nan(arg1) || m_util.is_nan(arg2)) { result = m().mk_false(); return BR_DONE; } if (m_util.is_ninf(arg1)) { // -oo < arg2 --> not(arg2 = -oo) and not(arg2 = NaN) result = m().mk_and(m().mk_not(m().mk_eq(arg2, arg1)), mk_neq_nan(arg2)); return BR_REWRITE3; } if (m_util.is_ninf(arg2)) { // arg1 < -oo --> false result = m().mk_false(); return BR_DONE; } if (m_util.is_pinf(arg1)) { // +oo < arg2 --> false result = m().mk_false(); return BR_DONE; } if (m_util.is_pinf(arg2)) { // arg1 < +oo --> not(arg1 = +oo) and not(arg1 = NaN) result = m().mk_and(m().mk_not(m().mk_eq(arg1, arg2)), mk_neq_nan(arg1)); return BR_REWRITE3; } scoped_mpf v1(m_fm), v2(m_fm); if (m_util.is_numeral(arg1, v1) && m_util.is_numeral(arg2, v2)) { result = (m_fm.lt(v1, v2)) ? m().mk_true() : m().mk_false(); return BR_DONE; } return BR_FAILED; } br_status fpa_rewriter::mk_gt(expr * arg1, expr * arg2, expr_ref & result) { result = m_util.mk_lt(arg2, arg1); return BR_REWRITE1; } br_status fpa_rewriter::mk_le(expr * arg1, expr * arg2, expr_ref & result) { if (m_util.is_nan(arg1) || m_util.is_nan(arg2)) { result = m().mk_false(); return BR_DONE; } scoped_mpf v1(m_fm), v2(m_fm); if (m_util.is_numeral(arg1, v1) && m_util.is_numeral(arg2, v2)) { result = (m_fm.le(v1, v2)) ? m().mk_true() : m().mk_false(); return BR_DONE; } return BR_FAILED; } br_status fpa_rewriter::mk_ge(expr * arg1, expr * arg2, expr_ref & result) { result = m_util.mk_le(arg2, arg1); return BR_REWRITE1; } br_status fpa_rewriter::mk_is_zero(expr * arg1, expr_ref & result) { scoped_mpf v(m_fm); if (m_util.is_numeral(arg1, v)) { result = (m_fm.is_zero(v)) ? m().mk_true() : m().mk_false(); return BR_DONE; } return BR_FAILED; } br_status fpa_rewriter::mk_is_nzero(expr * arg1, expr_ref & result) { scoped_mpf v(m_fm); if (m_util.is_numeral(arg1, v)) { result = (m_fm.is_nzero(v)) ? m().mk_true() : m().mk_false(); return BR_DONE; } return BR_FAILED; } br_status fpa_rewriter::mk_is_pzero(expr * arg1, expr_ref & result) { scoped_mpf v(m_fm); if (m_util.is_numeral(arg1, v)) { result = (m_fm.is_pzero(v)) ? m().mk_true() : m().mk_false(); return BR_DONE; } return BR_FAILED; } br_status fpa_rewriter::mk_is_nan(expr * arg1, expr_ref & result) { scoped_mpf v(m_fm); if (m_util.is_numeral(arg1, v)) { result = (m_fm.is_nan(v)) ? m().mk_true() : m().mk_false(); return BR_DONE; } return BR_FAILED; } br_status fpa_rewriter::mk_is_inf(expr * arg1, expr_ref & result) { scoped_mpf v(m_fm); if (m_util.is_numeral(arg1, v)) { result = (m_fm.is_inf(v)) ? m().mk_true() : m().mk_false(); return BR_DONE; } return BR_FAILED; } br_status fpa_rewriter::mk_is_normal(expr * arg1, expr_ref & result) { scoped_mpf v(m_fm); if (m_util.is_numeral(arg1, v)) { result = (m_fm.is_normal(v)) ? m().mk_true() : m().mk_false(); return BR_DONE; } return BR_FAILED; } br_status fpa_rewriter::mk_is_subnormal(expr * arg1, expr_ref & result) { scoped_mpf v(m_fm); if (m_util.is_numeral(arg1, v)) { result = (m_fm.is_denormal(v)) ? m().mk_true() : m().mk_false(); return BR_DONE; } return BR_FAILED; } br_status fpa_rewriter::mk_is_negative(expr * arg1, expr_ref & result) { scoped_mpf v(m_fm); if (m_util.is_numeral(arg1, v)) { result = (m_fm.is_neg(v)) ? m().mk_true() : m().mk_false(); return BR_DONE; } return BR_FAILED; } br_status fpa_rewriter::mk_is_positive(expr * arg1, expr_ref & result) { scoped_mpf v(m_fm); if (m_util.is_numeral(arg1, v)) { result = (m_fm.is_neg(v) || m_fm.is_nan(v)) ? m().mk_false() : m().mk_true(); return BR_DONE; } return BR_FAILED; } // This the SMT = br_status fpa_rewriter::mk_eq_core(expr * arg1, expr * arg2, expr_ref & result) { scoped_mpf v1(m_fm), v2(m_fm); if (m_util.is_numeral(arg1, v1) && m_util.is_numeral(arg2, v2)) { // Note: == is the floats-equality, here we need normal equality. result = (m_fm.is_nan(v1) && m_fm.is_nan(v2)) ? m().mk_true() : (m_fm.is_zero(v1) && m_fm.is_zero(v2) && m_fm.sgn(v1)!=m_fm.sgn(v2)) ? m().mk_false() : (v1 == v2) ? m().mk_true() : m().mk_false(); return BR_DONE; } return BR_FAILED; } br_status fpa_rewriter::mk_bv2rm(expr * arg, expr_ref & result) { rational bv_val; unsigned sz = 0; if (m_util.bu().is_numeral(arg, bv_val, sz)) { SASSERT(bv_val.is_uint64()); switch (bv_val.get_uint64()) { case BV_RM_TIES_TO_AWAY: result = m_util.mk_round_nearest_ties_to_away(); break; case BV_RM_TIES_TO_EVEN: result = m_util.mk_round_nearest_ties_to_even(); break; case BV_RM_TO_NEGATIVE: result = m_util.mk_round_toward_negative(); break; case BV_RM_TO_POSITIVE: result = m_util.mk_round_toward_positive(); break; case BV_RM_TO_ZERO: default: result = m_util.mk_round_toward_zero(); } return BR_DONE; } return BR_FAILED; } br_status fpa_rewriter::mk_fp(expr * sgn, expr * exp, expr * sig, expr_ref & result) { unsynch_mpz_manager & mpzm = m_fm.mpz_manager(); rational rsgn, rexp, rsig; unsigned bvsz_sgn, bvsz_exp, bvsz_sig; if (m_util.bu().is_numeral(sgn, rsgn, bvsz_sgn) && m_util.bu().is_numeral(sig, rsig, bvsz_sig) && m_util.bu().is_numeral(exp, rexp, bvsz_exp)) { SASSERT(mpzm.is_one(rexp.to_mpq().denominator())); SASSERT(mpzm.is_one(rsig.to_mpq().denominator())); scoped_mpf v(m_fm); mpf_exp_t biased_exp = mpzm.get_int64(rexp.to_mpq().numerator()); m_fm.set(v, bvsz_exp, bvsz_sig + 1, rsgn.is_one(), m_fm.unbias_exp(bvsz_exp, biased_exp), rsig.to_mpq().numerator()); TRACE("fp_rewriter", tout << "simplified (fp ...) to " << m_fm.to_string(v) << std::endl;); result = m_util.mk_value(v); return BR_DONE; } return BR_FAILED; } br_status fpa_rewriter::mk_to_bv(func_decl * f, expr * arg1, expr * arg2, bool is_signed, expr_ref & result) { SASSERT(f->get_num_parameters() == 1); SASSERT(f->get_parameter(0).is_int()); int bv_sz = f->get_parameter(0).get_int(); mpf_rounding_mode rmv; scoped_mpf v(m_fm); if (m_util.is_rm_numeral(arg1, rmv) && m_util.is_numeral(arg2, v)) { if (m_fm.is_nan(v) || m_fm.is_inf(v)) return mk_to_bv_unspecified(f, result); bv_util bu(m()); scoped_mpq q(m_fm.mpq_manager()); m_fm.to_sbv_mpq(rmv, v, q); rational r(q); rational ul, ll; if (!is_signed) { ul = m_fm.m_powers2.m1(bv_sz); ll = rational(0); } else { ul = m_fm.m_powers2.m1(bv_sz - 1); ll = -m_fm.m_powers2(bv_sz - 1); } if (r >= ll && r <= ul) { result = bu.mk_numeral(r, bv_sz); return BR_DONE; } else return mk_to_bv_unspecified(f, result); } return BR_FAILED; } br_status fpa_rewriter::mk_to_bv_unspecified(func_decl * f, expr_ref & result) { if (m_hi_fp_unspecified) { unsigned bv_sz = m_util.bu().get_bv_size(f->get_range()); result = m_util.bu().mk_numeral(0, bv_sz); return BR_DONE; } else return BR_FAILED; } br_status fpa_rewriter::mk_to_ubv(func_decl * f, expr * arg1, expr * arg2, expr_ref & result) { return mk_to_bv(f, arg1, arg2, false, result); } br_status fpa_rewriter::mk_to_sbv(func_decl * f, expr * arg1, expr * arg2, expr_ref & result) { return mk_to_bv(f, arg1, arg2, true, result); } br_status fpa_rewriter::mk_to_ieee_bv(func_decl * f, expr * arg, expr_ref & result) { TRACE("fp_rewriter", tout << "to_ieee_bv of " << mk_ismt2_pp(arg, m()) << std::endl;); scoped_mpf v(m_fm); if (m_util.is_numeral(arg, v)) { TRACE("fp_rewriter", tout << "to_ieee_bv numeral: " << m_fm.to_string(v) << std::endl;); bv_util bu(m()); const mpf & x = v.get(); if (m_fm.is_nan(v)) { if (m_hi_fp_unspecified) { expr * args[4] = { bu.mk_numeral(0, 1), bu.mk_numeral(rational::minus_one(), x.get_ebits()), bu.mk_numeral(0, x.get_sbits() - 2), bu.mk_numeral(1, 1) }; result = bu.mk_concat(4, args); return BR_REWRITE1; } } else { scoped_mpz rz(m_fm.mpq_manager()); m_fm.to_ieee_bv_mpz(v, rz); result = bu.mk_numeral(rational(rz), x.get_ebits() + x.get_sbits()); return BR_DONE; } } return BR_FAILED; } br_status fpa_rewriter::mk_to_real(expr * arg, expr_ref & result) { scoped_mpf v(m_fm); if (m_util.is_numeral(arg, v)) { if (m_fm.is_nan(v) || m_fm.is_inf(v)) { if (m_hi_fp_unspecified) { result = m_util.au().mk_numeral(rational(0), false); return BR_DONE; } } else { scoped_mpq r(m_fm.mpq_manager()); m_fm.to_rational(v, r); result = m_util.au().mk_numeral(r.get(), false); return BR_DONE; } } return BR_FAILED; } br_status fpa_rewriter::mk_bvwrap(expr * arg, expr_ref & result) { if (is_app_of(arg, m_util.get_family_id(), OP_FPA_FP)) { bv_util bu(m()); SASSERT(to_app(arg)->get_num_args() == 3); sort_ref fpsrt(m()); fpsrt = to_app(arg)->get_decl()->get_range(); expr_ref a0(m()), a1(m()), a2(m()); a0 = to_app(arg)->get_arg(0); a1 = to_app(arg)->get_arg(1); a2 = to_app(arg)->get_arg(2); if (bu.is_extract(a0) && bu.is_extract(a1) && bu.is_extract(a2)) { unsigned w0 = bu.get_extract_high(a0) - bu.get_extract_low(a0) + 1; unsigned w1 = bu.get_extract_high(a1) - bu.get_extract_low(a1) + 1; unsigned w2 = bu.get_extract_high(a2) - bu.get_extract_low(a2) + 1; unsigned cw = w0 + w1 + w2; if (cw == m_util.get_ebits(fpsrt) + m_util.get_sbits(fpsrt)) { expr_ref aa0(m()), aa1(m()), aa2(m()); aa0 = to_app(a0)->get_arg(0); aa1 = to_app(a1)->get_arg(0); aa2 = to_app(a2)->get_arg(0); if (aa0 == aa1 && aa1 == aa2 && bu.get_bv_size(aa0) == cw) { result = aa0; return BR_DONE; } } } } return BR_FAILED; } z3-z3-4.8.7/src/ast/rewriter/fpa_rewriter.h000066400000000000000000000074261356505360400205230ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: float_rewriter.h Abstract: Basic rewriting rules for floating point numbers. Author: Leonardo (leonardo) 2012-02-02 Notes: --*/ #ifndef FLOAT_REWRITER_H_ #define FLOAT_REWRITER_H_ #include "ast/ast.h" #include "ast/rewriter/rewriter.h" #include "ast/fpa_decl_plugin.h" #include "ast/expr_map.h" #include "util/params.h" #include "util/mpf.h" class fpa_rewriter { fpa_util m_util; mpf_manager & m_fm; bool m_hi_fp_unspecified; app * mk_eq_nan(expr * arg); app * mk_neq_nan(expr * arg); br_status mk_to_bv(func_decl * f, expr * arg1, expr * arg2, bool is_signed, expr_ref & result); br_status mk_to_bv_unspecified(func_decl * f, expr_ref & result); public: fpa_rewriter(ast_manager & m, params_ref const & p = params_ref()); ~fpa_rewriter(); ast_manager & m() const { return m_util.m(); } family_id get_fid() const { return m_util.get_fid(); } 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); br_status mk_eq_core(expr * arg1, expr * arg2, expr_ref & result); br_status mk_add(expr * arg1, expr * arg2, expr * arg3, expr_ref & result); br_status mk_sub(expr * arg1, expr * arg2, expr * arg3, expr_ref & result); br_status mk_mul(expr * arg1, expr * arg2, expr * arg3, expr_ref & result); br_status mk_div(expr * arg1, expr * arg2, expr * arg3, expr_ref & result); br_status mk_neg(expr * arg1, expr_ref & result); br_status mk_rem(expr * arg1, expr * arg2, expr_ref & result); br_status mk_abs(expr * arg1, expr_ref & result); br_status mk_min(expr * arg1, expr * arg2, expr_ref & result); br_status mk_max(expr * arg1, expr * arg2, expr_ref & result); br_status mk_fma(expr * arg1, expr * arg2, expr * arg3, expr * arg4, expr_ref & result); br_status mk_sqrt(expr * arg1, expr * arg2, expr_ref & result); br_status mk_round_to_integral(expr * arg1, expr * arg2, expr_ref & result); br_status mk_float_eq(expr * arg1, expr * arg2, expr_ref & result); br_status mk_lt(expr * arg1, expr * arg2, expr_ref & result); br_status mk_gt(expr * arg1, expr * arg2, expr_ref & result); br_status mk_le(expr * arg1, expr * arg2, expr_ref & result); br_status mk_ge(expr * arg1, expr * arg2, expr_ref & result); br_status mk_is_zero(expr * arg1, expr_ref & result); br_status mk_is_nzero(expr * arg1, expr_ref & result); br_status mk_is_pzero(expr * arg1, expr_ref & result); br_status mk_is_nan(expr * arg1, expr_ref & result); br_status mk_is_inf(expr * arg1, expr_ref & result); br_status mk_is_normal(expr * arg1, expr_ref & result); br_status mk_is_subnormal(expr * arg1, expr_ref & result); br_status mk_is_negative(expr * arg1, expr_ref & result); br_status mk_is_positive(expr * arg1, expr_ref & result); br_status mk_to_fp(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result); br_status mk_to_fp_unsigned(func_decl * f, expr * arg1, expr * arg2, expr_ref & result); br_status mk_bv2rm(expr * arg, expr_ref & result); br_status mk_fp(expr * sgn, expr * exp, expr * sig, expr_ref & result); br_status mk_to_ubv(func_decl * f, expr * arg1, expr * arg2, expr_ref & result); br_status mk_to_sbv(func_decl * f, expr * arg1, expr * arg2, expr_ref & result); br_status mk_to_ieee_bv(func_decl * f, expr * arg, expr_ref & result); br_status mk_to_real(expr * arg, expr_ref & result); br_status mk_min_i(func_decl * f, expr * arg1, expr * arg2, expr_ref & result); br_status mk_max_i(func_decl * f, expr * arg1, expr * arg2, expr_ref & result); br_status mk_bvwrap(expr * arg, expr_ref & result); }; #endif z3-z3-4.8.7/src/ast/rewriter/fpa_rewriter_params.pyg000066400000000000000000000004611356505360400224260ustar00rootroot00000000000000def_module_params(module_name='rewriter', class_name='fpa_rewriter_params', export=True, params=(("hi_fp_unspecified", BOOL, False, "use the 'hardware interpretation' for unspecified values in fp.to_ubv, fp.to_sbv, fp.to_real, and fp.to_ieee_bv"), )) z3-z3-4.8.7/src/ast/rewriter/func_decl_replace.cpp000066400000000000000000000050501356505360400217710ustar00rootroot00000000000000/*++ Copyright (c) 2019 Microsoft Corporation Module Name: func_decl_replace.cpp Abstract: Replace functions in expressions. Author: Nikolaj Bjorner (nbjorner) 2019-03-28 Revision History: --*/ #include "ast/rewriter/func_decl_replace.h" expr_ref func_decl_replace::operator()(expr* e) { m_todo.push_back(e); while (!m_todo.empty()) { expr* a = m_todo.back(), *b; if (m_cache.contains(a)) { m_todo.pop_back(); } else if (is_var(a)) { m_cache.insert(a, a); m_todo.pop_back(); } else if (is_app(a)) { app* c = to_app(a); unsigned n = c->get_num_args(); m_args.reset(); bool arg_differs = false; for (unsigned i = 0; i < n; ++i) { expr* d = nullptr, *arg = c->get_arg(i); if (m_cache.find(arg, d)) { m_args.push_back(d); arg_differs |= arg != d; SASSERT(m.get_sort(arg) == m.get_sort(d)); } else { m_todo.push_back(arg); } } if (m_args.size() == n) { if (arg_differs) { b = m.mk_app(c->get_decl(), m_args.size(), m_args.c_ptr()); m_refs.push_back(b); SASSERT(m.get_sort(a) == m.get_sort(b)); } else { b = a; } func_decl* f = nullptr; if (m_subst.find(c->get_decl(), f)) { b = m.mk_app(f, m_args.size(), m_args.c_ptr()); m_refs.push_back(b); } m_cache.insert(a, b); m_todo.pop_back(); } } else { quantifier* q = to_quantifier(a); SASSERT(is_quantifier(a)); expr* body = q->get_expr(), *new_body; if (m_cache.find(body, new_body)) { if (new_body == body) { b = a; } else { b = m.update_quantifier(q, new_body); m_refs.push_back(b); } m_cache.insert(a, b); m_todo.pop_back(); } else { m_todo.push_back(body); } } } return expr_ref(m_cache.find(e), m); } void func_decl_replace::reset() { m_cache.reset(); m_subst.reset(); m_refs.reset(); m_funs.reset(); } z3-z3-4.8.7/src/ast/rewriter/func_decl_replace.h000066400000000000000000000015511356505360400214400ustar00rootroot00000000000000/*++ Copyright (c) 2019 Microsoft Corporation Module Name: func_decl_replace.h Abstract: Replace functions in expressions. Author: Nikolaj Bjorner (nbjorner) 2019-03-28 Revision History: --*/ #ifndef FUNC_DECL_REPLACE_H_ #define FUNC_DECL_REPLACE_H_ #include "ast/ast.h" class func_decl_replace { ast_manager& m; obj_map m_subst; obj_map m_cache; ptr_vector m_todo, m_args; expr_ref_vector m_refs; func_decl_ref_vector m_funs; public: func_decl_replace(ast_manager& m): m(m), m_refs(m), m_funs(m) {} void insert(func_decl* src, func_decl* dst) { m_subst.insert(src, dst); m_funs.push_back(src), m_funs.push_back(dst); } expr_ref operator()(expr* e); void reset(); bool empty() const { return m_subst.empty(); } }; #endif /* FUNC_DECL_REPLACE_H_ */ z3-z3-4.8.7/src/ast/rewriter/hoist_rewriter.cpp000066400000000000000000000117331356505360400214320ustar00rootroot00000000000000/*++ 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(); } } z3-z3-4.8.7/src/ast/rewriter/hoist_rewriter.h000066400000000000000000000045741356505360400211040ustar00rootroot00000000000000/*++ 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 z3-z3-4.8.7/src/ast/rewriter/inj_axiom.cpp000066400000000000000000000126421356505360400203360ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: inj_axiom.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-06-23. Revision History: --*/ #include "ast/rewriter/inj_axiom.h" #include "ast/ast_pp.h" #include "ast/ast_ll_pp.h" #include "ast/has_free_vars.h" #include "ast/well_sorted.h" /** \brief Little HACK for simplifying injectivity axioms \remark It is not covering all possible cases. */ bool simplify_inj_axiom(ast_manager & m, quantifier * q, expr_ref & result) { expr * n = q->get_expr(); expr* arg1 = nullptr, * arg2 = nullptr, *narg = nullptr; expr* app1 = nullptr, * app2 = nullptr; expr* var1 = nullptr, * var2 = nullptr; if (is_forall(q) && m.is_or(n, arg1, arg2)) { if (m.is_not(arg2)) std::swap(arg1, arg2); if (m.is_not(arg1, narg) && m.is_eq(narg, app1, app2) && m.is_eq(arg2, var1, var2)) { if (is_app(app1) && is_app(app2) && to_app(app1)->get_decl() == to_app(app2)->get_decl() && to_app(app1)->get_num_args() == to_app(app2)->get_num_args() && to_app(app1)->get_family_id() == null_family_id && to_app(app1)->get_num_args() > 0 && is_var(var1) && is_var(var2) && var1 != var2) { app * f1 = to_app(app1); app * f2 = to_app(app2); bool found_vars = false; unsigned num = f1->get_num_args(); unsigned idx = UINT_MAX; unsigned num_vars = 1; for (unsigned i = 0; i < num; i++) { expr * c1 = f1->get_arg(i); expr * c2 = f2->get_arg(i); if (!is_var(c1) && !is_uninterp_const(c1)) return false; if ((c1 == var1 && c2 == var2) || (c1 == var2 && c2 == var1)) { if (found_vars) return false; found_vars = true; idx = i; } else if (c1 == c2 && c1 != var1 && c1 != var2) { if (is_var(c1)) { ++num_vars; } } else { return false; } } if (found_vars && !has_free_vars(q)) { TRACE("inj_axiom", tout << "Cadidate for simplification:\n" << mk_ll_pp(q, m) << mk_pp(app1, m) << "\n" << mk_pp(app2, m) << "\n" << mk_pp(var1, m) << "\n" << mk_pp(var2, m) << "\nnum_vars: " << num_vars << "\n";); // Building new (optimized) axiom func_decl * decl = f1->get_decl(); unsigned var_idx = 0; ptr_buffer f_args, inv_vars; ptr_buffer decls; buffer names; expr * var = nullptr; for (unsigned i = 0; i < num; i++) { expr * c = f1->get_arg(i); if (is_var(c)) { names.push_back(symbol(i)); sort * s = decl->get_domain(i); decls.push_back(s); expr * new_c = m.mk_var(var_idx, s); var_idx++; f_args.push_back(new_c); if (i == idx) { var = new_c; } else { inv_vars.push_back(new_c); } } else { SASSERT(is_uninterp_const(c)); f_args.push_back(c); } } SASSERT(var != 0); app * f = m.mk_app(decl, f_args.size(), f_args.c_ptr()); ptr_vector domain; inv_vars.push_back(f); for (unsigned i = 0; i < inv_vars.size(); ++i) { domain.push_back(m.get_sort(inv_vars[i])); } sort * d = decl->get_domain(idx); func_decl * inv_decl = m.mk_fresh_func_decl("inj", domain.size(), domain.c_ptr(), d); expr * proj = m.mk_app(inv_decl, inv_vars.size(), inv_vars.c_ptr()); expr * eq = m.mk_eq(proj, var); expr * p = m.mk_pattern(f); // decls are in the wrong order... // Remark: the sort of the var 0 must be in the last position. std::reverse(decls.begin(), decls.end()); result = m.mk_forall(decls.size(), decls.c_ptr(), names.c_ptr(), eq, 0, symbol(), symbol(), 1, &p); TRACE("inj_axiom", tout << "new axiom:\n" << mk_pp(result, m) << "\n";); SASSERT(is_well_sorted(m, result)); return true; } } } } return false; } z3-z3-4.8.7/src/ast/rewriter/inj_axiom.h000066400000000000000000000005421356505360400177770ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: inj_axiom.h Abstract: Author: Leonardo de Moura (leonardo) 2008-06-23. Revision History: --*/ #ifndef INJ_AXIOM_H_ #define INJ_AXIOM_H_ #include "ast/ast.h" bool simplify_inj_axiom(ast_manager & m, quantifier * q, expr_ref & result); #endif /* INJ_AXIOM_H_ */ z3-z3-4.8.7/src/ast/rewriter/label_rewriter.cpp000066400000000000000000000021171356505360400213570ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: label_rewriter.cpp Abstract: Basic rewriting rules for removing labels. Author: Nikolaj Bjorner (nbjorner) 2015-19-10 Notes: --*/ #include "ast/rewriter/rewriter.h" #include "ast/rewriter/rewriter_def.h" #include "ast/rewriter/label_rewriter.h" label_rewriter::label_rewriter(ast_manager & m) : m_label_fid(m.get_label_family_id()), m_rwr(m, false, *this) {} label_rewriter::~label_rewriter() {} br_status label_rewriter::reduce_app( func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { if (is_decl_of(f, m_label_fid, OP_LABEL)) { SASSERT(num == 1); result = args[0]; return BR_DONE; } return BR_FAILED; } void label_rewriter::remove_labels(expr_ref& fml, proof_ref& pr) { ast_manager& m = fml.get_manager(); expr_ref tmp(m); m_rwr(fml, tmp); if (pr && fml != tmp) { pr = m.mk_modus_ponens(pr, m.mk_rewrite(fml, tmp)); } fml = tmp; } //template class rewriter_tpl; z3-z3-4.8.7/src/ast/rewriter/label_rewriter.h000066400000000000000000000013301356505360400210200ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: label_rewriter.h Abstract: Basic rewriting rules for labels. Author: Nikolaj Bjorner (nbjorner) 2015-19-10 Notes: --*/ #ifndef LABEL_REWRITER_H_ #define LABEL_REWRITER_H_ #include "ast/ast.h" #include "ast/rewriter/rewriter_types.h" class label_rewriter : public default_rewriter_cfg { family_id m_label_fid; rewriter_tpl m_rwr; public: label_rewriter(ast_manager & m); ~label_rewriter(); br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr); void remove_labels(expr_ref& fml, proof_ref& pr); }; #endif z3-z3-4.8.7/src/ast/rewriter/maximize_ac_sharing.cpp000066400000000000000000000107361356505360400223640ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: maximize_ac_sharing.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-10-22. Revision History: --*/ #include "ast/rewriter/maximize_ac_sharing.h" #include "ast/ast_pp.h" void maximize_ac_sharing::register_kind(decl_kind k) { m_kinds.push_back(k); } br_status maximize_ac_sharing::reduce_app(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result, proof_ref& result_pr) { decl_kind k = f->get_kind(); if (!f->is_associative()) return BR_FAILED; if (num_args <= 2) return BR_FAILED; if (std::find(m_kinds.begin(), m_kinds.end(), k) == m_kinds.end()) return BR_FAILED; ptr_buffer _args; expr * numeral = nullptr; if (is_numeral(args[0])) { numeral = args[0]; for (unsigned i = 1; i < num_args; i++) _args.push_back(args[i]); num_args--; } else { _args.append(num_args, args); } TRACE("ac_sharing_detail", tout << "before-reuse: num_args: " << num_args << "\n";); #define MAX_NUM_ARGS_FOR_OPT 128 // Try to reuse already created circuits. TRACE("ac_sharing_detail", tout << "args: "; for (unsigned i = 0; i < num_args; i++) tout << mk_pp(_args[i], m) << "\n";); try_to_reuse: if (num_args > 1 && num_args < MAX_NUM_ARGS_FOR_OPT) { for (unsigned i = 0; i + 1 < num_args; i++) { for (unsigned j = i + 1; j < num_args; j++) { if (contains(f, _args[i], _args[j])) { TRACE("ac_sharing_detail", tout << "reusing args: " << i << " " << j << "\n";); _args[i] = m.mk_app(f, _args[i], _args[j]); SASSERT(num_args > 1); for (unsigned w = j; w + 1 < num_args; w++) { _args[w] = _args[w+1]; } num_args--; goto try_to_reuse; } } } } // Create "tree-like circuit" while (true) { TRACE("ac_sharing_detail", tout << "tree-loop: num_args: " << num_args << "\n";); unsigned j = 0; for (unsigned i = 0; i < num_args; i += 2, j++) { if (i == num_args - 1) { _args[j] = _args[i]; } else { insert(f, _args[i], _args[i+1]); _args[j] = m.mk_app(f, _args[i], _args[i+1]); } } num_args = j; if (num_args == 1) { if (numeral == nullptr) { result = _args[0]; } else { result = m.mk_app(f, numeral, _args[0]); } TRACE("ac_sharing_detail", tout << "result: " << mk_pp(result, m) << "\n";); return BR_DONE; } } UNREACHABLE(); return BR_FAILED; } bool maximize_ac_sharing::contains(func_decl * f, expr * arg1, expr * arg2) { entry e(f, arg1, arg2); return m_cache.contains(&e); } void maximize_ac_sharing::insert(func_decl * f, expr * arg1, expr * arg2) { entry * e = new (m_region) entry(f, arg1, arg2); m_entries.push_back(e); m_cache.insert(e); m.inc_ref(arg1); m.inc_ref(arg2); } maximize_ac_sharing::maximize_ac_sharing(ast_manager & m): m(m), m_init(false) { } maximize_ac_sharing::~maximize_ac_sharing() { restore_entries(0); } void maximize_ac_sharing::push_scope() { init(); m_scopes.push_back(m_entries.size()); m_region.push_scope(); } void maximize_ac_sharing::pop_scope(unsigned num_scopes) { SASSERT(num_scopes <= m_scopes.size()); unsigned new_lvl = m_scopes.size() - num_scopes; unsigned old_lim = m_scopes[new_lvl]; restore_entries(old_lim); m_region.pop_scope(num_scopes); m_scopes.shrink(new_lvl); } void maximize_ac_sharing::restore_entries(unsigned old_lim) { unsigned i = m_entries.size(); while (i != old_lim) { --i; entry * e = m_entries[i]; m_cache.remove(e); m.dec_ref(e->m_arg1); m.dec_ref(e->m_arg2); } m_entries.shrink(old_lim); } void maximize_ac_sharing::reset() { m_cache.reset(); } void maximize_bv_sharing::init_core() { register_kind(OP_BADD); register_kind(OP_BMUL); register_kind(OP_BOR); register_kind(OP_BAND); } bool maximize_bv_sharing::is_numeral(expr * n) const { return m_util.is_numeral(n); } maximize_bv_sharing::maximize_bv_sharing(ast_manager & m): maximize_ac_sharing(m), m_util(m) { } z3-z3-4.8.7/src/ast/rewriter/maximize_ac_sharing.h000066400000000000000000000064271356505360400220330ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: maximize_ac_sharing.h Abstract: Author: Leonardo de Moura (leonardo) 2008-10-22. Revision History: --*/ #ifndef MAXIMIZE_AC_SHARING_H_ #define MAXIMIZE_AC_SHARING_H_ #include "util/hashtable.h" #include "util/region.h" #include "ast/bv_decl_plugin.h" #include "ast/rewriter/rewriter.h" /** \brief Functor used to maximize the amount of shared terms in an expression. The idea is to rewrite AC terms to maximize sharing. Example: (f (bvadd a (bvadd b c)) (bvadd a (bvadd b d))) is rewritten to: (f (bvadd (bvadd a b) c) (bvadd (bvadd a b) d)) \warning This class uses an opportunistic heuristic to maximize sharing. There is no guarantee that the optimal expression will be produced. */ class maximize_ac_sharing : public default_rewriter_cfg { struct entry { func_decl * m_decl; expr * m_arg1; expr * m_arg2; entry(func_decl * d = nullptr, expr * arg1 = nullptr, expr * arg2 = nullptr):m_decl(d), m_arg1(arg1), m_arg2(arg2) { SASSERT((d == 0 && arg1 == 0 && arg2 == 0) || (d != 0 && arg1 != 0 && arg2 != 0)); if (arg1->get_id() > arg2->get_id()) std::swap(m_arg1, m_arg2); } unsigned hash() const { unsigned a = m_decl->get_id(); unsigned b = m_arg1->get_id(); unsigned c = m_arg2->get_id(); mix(a,b,c); return c; } bool operator==(entry const & e) const { return m_decl == e.m_decl && m_arg1 == e.m_arg1 && m_arg2 == e.m_arg2; } }; typedef ptr_hashtable, deref_eq > cache; protected: void register_kind(decl_kind k); private: ast_manager & m; bool m_init; region m_region; cache m_cache; ptr_vector m_entries; unsigned_vector m_scopes; svector m_kinds; //!< kinds to be processed bool contains(func_decl * f, expr * arg1, expr * arg2); void insert(func_decl * f, expr * arg1, expr * arg2); void restore_entries(unsigned old_lim); void init() { if (!m_init) { init_core(); m_init = true; } } protected: virtual void init_core() = 0; virtual bool is_numeral(expr * n) const = 0; public: maximize_ac_sharing(ast_manager & m); virtual ~maximize_ac_sharing(); void push_scope(); void pop_scope(unsigned num_scopes); void reset(); br_status reduce_app(func_decl* f, unsigned n, expr * const* args, expr_ref& result, proof_ref& result_pr); }; class maximize_bv_sharing : public maximize_ac_sharing { bv_util m_util; protected: void init_core() override; bool is_numeral(expr * n) const override; public: maximize_bv_sharing(ast_manager & m); }; class maximize_bv_sharing_rw : public rewriter_tpl { maximize_bv_sharing m_cfg; public: maximize_bv_sharing_rw(ast_manager& m): rewriter_tpl(m, m.proofs_enabled(), m_cfg), m_cfg(m) {} void push_scope() { m_cfg.push_scope(); } void pop_scope(unsigned n) { m_cfg.pop_scope(n); } void reset() { m_cfg.reset(); } }; #endif /* MAXIMIZE_AC_SHARING_H_ */ z3-z3-4.8.7/src/ast/rewriter/mk_extract_proc.cpp000066400000000000000000000022351356505360400215420ustar00rootroot00000000000000/*++ Copyright (c) 2016 Microsoft Corporation Module Name: mk_extract_proc.cpp Abstract: Author: Mikolas Janota (MikolasJanota) Revision History: --*/ #include "ast/rewriter/mk_extract_proc.h" mk_extract_proc::mk_extract_proc(bv_util & u): m_util(u), m_high(0), m_low(UINT_MAX), m_domain(nullptr), m_f_cached(nullptr) { } mk_extract_proc::~mk_extract_proc() { if (m_f_cached) { // m_f_cached has a reference to m_domain, so, I don't need to inc_ref m_domain ast_manager & m = m_util.get_manager(); m.dec_ref(m_f_cached); } } app * mk_extract_proc::operator()(unsigned high, unsigned low, expr * arg) { ast_manager & m = m_util.get_manager(); sort * s = m.get_sort(arg); if (m_low == low && m_high == high && m_domain == s) return m.mk_app(m_f_cached, arg); // m_f_cached has a reference to m_domain, so, I don't need to inc_ref m_domain if (m_f_cached) m.dec_ref(m_f_cached); app * r = to_app(m_util.mk_extract(high, low, arg)); m_high = high; m_low = low; m_domain = s; m_f_cached = r->get_decl(); m.inc_ref(m_f_cached); return r; } z3-z3-4.8.7/src/ast/rewriter/mk_extract_proc.h000066400000000000000000000012651356505360400212110ustar00rootroot00000000000000 /*++ Copyright (c) 2016 Microsoft Corporation Module Name: mk_extract_proc.h Abstract: Author: Mikolas Janota (MikolasJanota) Revision History: --*/ #ifndef MK_EXTRACT_PROC_H_ #define MK_EXTRACT_PROC_H_ #include "ast/ast.h" #include "ast/bv_decl_plugin.h" class mk_extract_proc { bv_util & m_util; unsigned m_high; unsigned m_low; sort * m_domain; func_decl * m_f_cached; public: mk_extract_proc(bv_util & u); ~mk_extract_proc(); app * operator()(unsigned high, unsigned low, expr * arg); ast_manager & m() { return m_util.get_manager(); } bv_util & bvutil() { return m_util; } }; #endif /* MK_EXTRACT_PROC_H_ */ z3-z3-4.8.7/src/ast/rewriter/mk_simplified_app.cpp000066400000000000000000000066421356505360400220400ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: mk_simplified_app.cpp Abstract: Functor for creating new simplified applications Author: Leonardo (leonardo) 2011-06-06 Notes: --*/ #include "ast/rewriter/mk_simplified_app.h" #include "ast/rewriter/bool_rewriter.h" #include "ast/rewriter/arith_rewriter.h" #include "ast/rewriter/bv_rewriter.h" #include "ast/rewriter/datatype_rewriter.h" #include "ast/rewriter/array_rewriter.h" #include "ast/rewriter/fpa_rewriter.h" struct mk_simplified_app::imp { ast_manager & m; bool_rewriter m_b_rw; arith_rewriter m_a_rw; bv_rewriter m_bv_rw; array_rewriter m_ar_rw; datatype_rewriter m_dt_rw; fpa_rewriter m_f_rw; imp(ast_manager & _m, params_ref const & p): m(_m), m_b_rw(m, p), m_a_rw(m, p), m_bv_rw(m, p), m_ar_rw(m, p), m_dt_rw(m), m_f_rw(m, p) { } br_status mk_core(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { family_id fid = f->get_family_id(); if (fid == null_family_id) return BR_FAILED; br_status st = BR_FAILED; if (fid == m_b_rw.get_fid()) { decl_kind k = f->get_decl_kind(); if (k == OP_EQ) { // theory dispatch for = SASSERT(num == 2); family_id s_fid = m.get_sort(args[0])->get_family_id(); if (s_fid == m_a_rw.get_fid()) st = m_a_rw.mk_eq_core(args[0], args[1], result); else if (s_fid == m_bv_rw.get_fid()) st = m_bv_rw.mk_eq_core(args[0], args[1], result); else if (s_fid == m_dt_rw.get_fid()) st = m_dt_rw.mk_eq_core(args[0], args[1], result); else if (s_fid == m_f_rw.get_fid()) st = m_f_rw.mk_eq_core(args[0], args[1], result); else if (s_fid == m_ar_rw.get_fid()) st = m_ar_rw.mk_eq_core(args[0], args[1], result); if (st != BR_FAILED) return st; } return m_b_rw.mk_app_core(f, num, args, result); } if (fid == m_a_rw.get_fid()) return m_a_rw.mk_app_core(f, num, args, result); if (fid == m_bv_rw.get_fid()) return m_bv_rw.mk_app_core(f, num, args, result); if (fid == m_ar_rw.get_fid()) return m_ar_rw.mk_app_core(f, num, args, result); if (fid == m_dt_rw.get_fid()) return m_dt_rw.mk_app_core(f, num, args, result); if (fid == m_f_rw.get_fid()) return m_f_rw.mk_app_core(f, num, args, result); return BR_FAILED; } }; mk_simplified_app::mk_simplified_app(ast_manager & m, params_ref const & p): m_imp(alloc(imp, m, p)) { } mk_simplified_app::~mk_simplified_app() { dealloc(m_imp); } br_status mk_simplified_app::mk_core(func_decl * decl, unsigned num, expr * const * args, expr_ref & result) { return m_imp->mk_core(decl, num, args, result); } void mk_simplified_app::operator()(func_decl * decl, unsigned num, expr * const * args, expr_ref & result) { result = nullptr; mk_core(decl, num, args, result); if (!result) result = m_imp->m.mk_app(decl, num, args); // TODO: if the result of mk_core is different from BR_FAILED, then the // result is not really simplified. } z3-z3-4.8.7/src/ast/rewriter/mk_simplified_app.h000066400000000000000000000013261356505360400214770ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: mk_simplified_app.h Abstract: Functor for creating new simplified applications Author: Leonardo (leonardo) 2011-06-06 Notes: --*/ #ifndef MK_SIMPLIFIED_APP_H_ #define MK_SIMPLIFIED_APP_H_ #include "ast/ast.h" #include "util/params.h" #include "ast/rewriter/rewriter_types.h" class mk_simplified_app { struct imp; imp * m_imp; public: mk_simplified_app(ast_manager & m, params_ref const & p = params_ref()); ~mk_simplified_app(); br_status mk_core(func_decl * decl, unsigned num, expr * const * args, expr_ref & result); void operator()(func_decl * decl, unsigned num, expr * const * args, expr_ref & result); }; #endif z3-z3-4.8.7/src/ast/rewriter/pb2bv_rewriter.cpp000066400000000000000000001222721356505360400213200ustar00rootroot00000000000000/*++ Copyright (c) 2016 Microsoft Corporation Module Name: pb2bv_rewriter.cpp Abstract: Conversion from pseudo-booleans to bit-vectors. Author: Nikolaj Bjorner (nbjorner) 2016-10-23 Notes: --*/ #include "ast/rewriter/rewriter.h" #include "ast/rewriter/rewriter_def.h" #include "util/statistics.h" #include "ast/rewriter/pb2bv_rewriter.h" #include "util/sorting_network.h" #include "ast/ast_util.h" #include "ast/ast_pp.h" #include "util/lbool.h" #include "util/uint_set.h" #include "util/gparams.h" static const unsigned g_primes[7] = { 2, 3, 5, 7, 11, 13, 17}; struct pb2bv_rewriter::imp { ast_manager& m; params_ref m_params; expr_ref_vector m_lemmas; func_decl_ref_vector m_fresh; // all fresh variables unsigned_vector m_fresh_lim; unsigned m_num_translated; unsigned m_compile_bv; unsigned m_compile_card; struct card2bv_rewriter { typedef expr* pliteral; typedef ptr_vector pliteral_vector; psort_nw m_sort; ast_manager& m; imp& m_imp; arith_util au; pb_util pb; bv_util bv; expr_ref_vector m_trail; expr_ref_vector m_args; rational m_k; vector m_coeffs; bool m_keep_cardinality_constraints; symbol m_pb_solver; unsigned m_min_arity; template expr_ref mk_le_ge(expr_ref_vector& fmls, expr* a, expr* b, expr* bound) { expr_ref x(m), y(m), result(m); unsigned nb = bv.get_bv_size(a); x = bv.mk_zero_extend(1, a); y = bv.mk_zero_extend(1, b); result = bv.mk_bv_add(x, y); x = bv.mk_extract(nb, nb, result); result = bv.mk_extract(nb-1, 0, result); if (is_le != l_false) { fmls.push_back(m.mk_eq(x, bv.mk_numeral(rational::zero(), 1))); fmls.push_back(bv.mk_ule(result, bound)); } else { fmls.push_back(m.mk_eq(x, bv.mk_numeral(rational::one(), 1))); fmls.push_back(bv.mk_ule(bound, result)); } return result; } typedef std::pair ca; struct compare_coeffs { bool operator()(ca const& x, ca const& y) const { return x.first > y.first; } }; void sort_args() { vector cas; for (unsigned i = 0; i < m_args.size(); ++i) { cas.push_back(std::make_pair(m_coeffs[i], expr_ref(m_args[i].get(), m))); } std::sort(cas.begin(), cas.end(), compare_coeffs()); m_coeffs.reset(); m_args.reset(); for (ca const& ca : cas) { m_coeffs.push_back(ca.first); m_args.push_back(ca.second); } } // // create a circuit of size sz*log(k) // by forming a binary tree adding pairs of values that are assumed <= k, // and in each step we check that the result is <= k by checking the overflow // bit and that the non-overflow bits are <= k. // The procedure for checking >= k is symmetric and checking for = k is // achieved by checking <= k on intermediary addends and the resulting sum is = k. // // is_le = l_true - <= // is_le = l_undef - = // is_le = l_false - >= // template expr_ref mk_le_ge(rational const & k) { //sort_args(); unsigned sz = m_args.size(); expr * const* args = m_args.c_ptr(); TRACE("pb", for (unsigned i = 0; i < sz; ++i) { tout << m_coeffs[i] << "*" << mk_pp(args[i], m) << " "; if (i + 1 < sz && !m_coeffs[i+1].is_neg()) tout << "+ "; } switch (is_le) { case l_true: tout << "<= "; break; case l_undef: tout << "= "; break; case l_false: tout << ">= "; break; } tout << k << "\n";); if (k.is_zero()) { if (is_le != l_false) { return expr_ref(m.mk_not(::mk_or(m_args)), m); } else { return expr_ref(m.mk_true(), m); } } if (k.is_neg()) { return expr_ref((is_le == l_false)?m.mk_true():m.mk_false(), m); } if (m_pb_solver == "totalizer") { expr_ref result(m); switch (is_le) { case l_true: if (mk_le_tot(sz, args, k, result)) return result; else break; case l_false: if (mk_ge_tot(sz, args, k, result)) return result; else break; case l_undef: break; } } if (m_pb_solver == "sorting") { expr_ref result(m); switch (is_le) { case l_true: if (mk_le(sz, args, k, result)) return result; else break; case l_false: if (mk_ge(sz, args, k, result)) return result; else break; case l_undef: if (mk_eq(sz, args, k, result)) return result; else break; } } if (m_pb_solver == "segmented") { switch (is_le) { case l_true: return mk_seg_le(k); case l_false: return mk_seg_ge(k); case l_undef: break; } } if (m_pb_solver == "binary_merge") { expr_ref result = binary_merge(is_le, k); if (result) return result; } // fall back to divide and conquer encoding. SASSERT(k.is_pos()); expr_ref zero(m), bound(m); expr_ref_vector es(m), fmls(m); unsigned nb = k.get_num_bits(); zero = bv.mk_numeral(rational(0), nb); bound = bv.mk_numeral(k, nb); for (unsigned i = 0; i < sz; ++i) { SASSERT(!m_coeffs[i].is_neg()); if (m_coeffs[i] > k) { if (is_le != l_false) { fmls.push_back(m.mk_not(args[i])); } else { fmls.push_back(args[i]); } } else { es.push_back(mk_ite(args[i], bv.mk_numeral(m_coeffs[i], nb), zero)); } } while (es.size() > 1) { for (unsigned i = 0; i + 1 < es.size(); i += 2) { es[i/2] = mk_le_ge(fmls, es[i].get(), es[i+1].get(), bound); } if ((es.size() % 2) == 1) { es[es.size()/2] = es.back(); } es.shrink((1 + es.size())/2); } switch (is_le) { case l_true: return ::mk_and(fmls); case l_false: if (!es.empty()) { fmls.push_back(bv.mk_ule(bound, es.back())); } return ::mk_or(fmls); case l_undef: if (es.empty()) { fmls.push_back(m.mk_bool_val(k.is_zero())); } else { fmls.push_back(m.mk_eq(bound, es.back())); } return ::mk_and(fmls); default: UNREACHABLE(); return expr_ref(m.mk_true(), m); } } /** \brief Totalizer encoding. Based on a version by Miguel. */ bool mk_le_tot(unsigned sz, expr * const * args, rational const& _k, expr_ref& result) { SASSERT(sz == m_coeffs.size()); if (!_k.is_unsigned() || sz == 0) return false; unsigned k = _k.get_unsigned(); expr_ref_vector args1(m); rational bound; flip(sz, args, args1, _k, bound); if (bound.get_unsigned() < k) { return mk_ge_tot(sz, args1.c_ptr(), bound, result); } if (k > 20) { return false; } result = m.mk_not(bounded_addition(sz, args, k + 1)); TRACE("pb", tout << result << "\n";); return true; } bool mk_ge_tot(unsigned sz, expr * const * args, rational const& _k, expr_ref& result) { SASSERT(sz == m_coeffs.size()); if (!_k.is_unsigned() || sz == 0) return false; unsigned k = _k.get_unsigned(); expr_ref_vector args1(m); rational bound; flip(sz, args, args1, _k, bound); if (bound.get_unsigned() < k) { return mk_le_tot(sz, args1.c_ptr(), bound, result); } if (k > 20) { return false; } result = bounded_addition(sz, args, k); TRACE("pb", tout << result << "\n";); return true; } void flip(unsigned sz, expr* const* args, expr_ref_vector& args1, rational const& k, rational& bound) { bound = -k; for (unsigned i = 0; i < sz; ++i) { args1.push_back(mk_not(args[i])); bound += m_coeffs[i]; } } expr_ref bounded_addition(unsigned sz, expr * const * args, unsigned k) { SASSERT(sz > 0); expr_ref result(m); vector es; vector coeffs; for (unsigned i = 0; i < m_coeffs.size(); ++i) { unsigned_vector v; expr_ref_vector e(m); unsigned c = m_coeffs[i].get_unsigned(); v.push_back(c >= k ? k : c); e.push_back(args[i]); es.push_back(e); coeffs.push_back(v); } while (es.size() > 1) { for (unsigned i = 0; i + 1 < es.size(); i += 2) { expr_ref_vector o(m); unsigned_vector oc; tot_adder(es[i], coeffs[i], es[i + 1], coeffs[i + 1], k, o, oc); es[i / 2].set(o); coeffs[i / 2] = oc; } if ((es.size() % 2) == 1) { es[es.size() / 2].set(es.back()); coeffs[es.size() / 2] = coeffs.back(); } es.shrink((1 + es.size())/2); coeffs.shrink((1 + coeffs.size())/2); } SASSERT(coeffs.size() == 1); SASSERT(coeffs[0].back() <= k); if (coeffs[0].back() == k) { result = es[0].back(); } else { result = m.mk_false(); } return result; } void tot_adder(expr_ref_vector const& l, unsigned_vector const& lc, expr_ref_vector const& r, unsigned_vector const& rc, unsigned k, expr_ref_vector& o, unsigned_vector & oc) { SASSERT(l.size() == lc.size()); SASSERT(r.size() == rc.size()); uint_set sums; vector trail; u_map sum2def; for (unsigned i = 0; i <= l.size(); ++i) { for (unsigned j = (i == 0) ? 1 : 0; j <= r.size(); ++j) { unsigned sum = std::min(k, ((i == 0) ? 0 : lc[i - 1]) + ((j == 0) ? 0 : rc[j - 1])); sums.insert(sum); } } for (unsigned u : sums) { oc.push_back(u); } std::sort(oc.begin(), oc.end()); DEBUG_CODE( for (unsigned i = 0; i + 1 < oc.size(); ++i) { SASSERT(oc[i] < oc[i+1]); }); for (unsigned i = 0; i < oc.size(); ++i) { sum2def.insert(oc[i], i); trail.push_back(expr_ref_vector(m)); } for (unsigned i = 0; i <= l.size(); ++i) { for (unsigned j = (i == 0) ? 1 : 0; j <= r.size(); ++j) { if (i != 0 && j != 0 && (lc[i - 1] >= k || rc[j - 1] >= k)) continue; unsigned sum = std::min(k, ((i == 0) ? 0 : lc[i - 1]) + ((j == 0) ? 0 : rc[j - 1])); expr_ref_vector ands(m); if (i != 0) { ands.push_back(l[i - 1]); } if (j != 0) { ands.push_back(r[j - 1]); } trail[sum2def.find(sum)].push_back(::mk_and(ands)); } } for (unsigned i = 0; i < oc.size(); ++i) { o.push_back(::mk_or(trail[sum2def.find(oc[i])])); } } /** \brief MiniSat+ based encoding of PB constraints. Translating Pseudo-Boolean Constraints into SAT, Niklas Een, Niklas Soerensson, JSAT 2006. */ vector m_min_base; rational m_min_cost; vector m_base; void create_basis(vector const& seq, rational const& carry_in, rational const& cost) { if (cost >= m_min_cost) { return; } rational delta_cost(0); for (unsigned i = 0; i < seq.size(); ++i) { delta_cost += seq[i]; } if (cost + delta_cost < m_min_cost) { m_min_cost = cost + delta_cost; m_min_base = m_base; m_min_base.push_back(delta_cost + rational::one()); } for (unsigned i = 0; i < sizeof(g_primes)/sizeof(*g_primes); ++i) { vector seq1; rational p(g_primes[i]); rational rest = carry_in; // create seq1 for (unsigned j = 0; j < seq.size(); ++j) { rest += seq[j] % p; if (seq[j] >= p) { seq1.push_back(div(seq[j], p)); } } m_base.push_back(p); create_basis(seq1, div(rest, p), cost + rest); m_base.pop_back(); } } bool create_basis() { m_base.reset(); m_min_cost = rational(INT_MAX); m_min_base.reset(); rational cost(0); create_basis(m_coeffs, rational::zero(), cost); m_base = m_min_base; TRACE("pb", tout << "Base: "; for (unsigned i = 0; i < m_base.size(); ++i) { tout << m_base[i] << " "; } tout << "\n";); return !m_base.empty() && m_base.back().is_unsigned() && m_base.back().get_unsigned() <= 20*m_base.size(); } /** \brief Check if 'out mod n >= lim'. */ expr_ref mod_ge(ptr_vector const& out, unsigned n, unsigned lim) { TRACE("pb", for (unsigned i = 0; i < out.size(); ++i) tout << mk_pp(out[i], m) << " "; tout << "\n"; tout << "n:" << n << " lim: " << lim << "\n";); if (lim == n) { return expr_ref(m.mk_false(), m); } if (lim == 0) { return expr_ref(m.mk_true(), m); } SASSERT(0 < lim && lim < n); expr_ref_vector ors(m); for (unsigned j = 0; j + lim - 1 < out.size(); j += n) { expr_ref tmp(m); tmp = out[j + lim - 1]; if (j + n - 1 < out.size()) { tmp = m.mk_and(tmp, m.mk_not(out[j + n - 1])); } ors.push_back(tmp); } return ::mk_or(ors); } // x0 + 5x1 + 3x2 >= k // x0 x1 x1 -> s0 s1 s2 // s2 x1 x2 -> s3 s4 s5 // k = 7: s5 or (s4 & not s2 & s0) // k = 6: s4 // k = 5: s4 or (s3 & not s2 & s1) // k = 4: s4 or (s3 & not s2 & s0) // k = 3: s3 // bool mk_ge(unsigned sz, expr * const* args, rational bound, expr_ref& result) { if (!create_basis()) return false; if (!bound.is_unsigned()) return false; vector coeffs(m_coeffs); result = m.mk_true(); expr_ref_vector carry(m), new_carry(m); m_base.push_back(bound + rational::one()); for (const rational& b_i : m_base) { unsigned B = b_i.get_unsigned(); unsigned d_i = (bound % b_i).get_unsigned(); bound = div(bound, b_i); for (unsigned j = 0; j < coeffs.size(); ++j) { rational c = coeffs[j] % b_i; SASSERT(c.is_unsigned()); for (unsigned k = 0; k < c.get_unsigned(); ++k) { carry.push_back(args[j]); } coeffs[j] = div(coeffs[j], b_i); } TRACE("pb", tout << "Carry: " << carry << "\n"; for (auto c : coeffs) tout << c << " "; tout << "\n"; ); ptr_vector out; m_sort.sorting(carry.size(), carry.c_ptr(), out); expr_ref gt = mod_ge(out, B, d_i + 1); expr_ref ge = mod_ge(out, B, d_i); result = mk_and(ge, result); result = mk_or(gt, result); TRACE("pb", tout << "b: " << b_i << " d: " << d_i << " gt: " << gt << " ge: " << ge << " " << result << "\n";); new_carry.reset(); for (unsigned j = B - 1; j < out.size(); j += B) { new_carry.push_back(out[j]); } carry.reset(); carry.append(new_carry); } TRACE("pb", tout << "bound: " << bound << " Carry: " << carry << " result: " << result << "\n";); return true; } /** \brief binary merge encoding. */ expr_ref binary_merge(lbool is_le, rational const& k) { expr_ref result(m); unsigned_vector coeffs; for (rational const& c : m_coeffs) { if (c.is_unsigned()) { coeffs.push_back(c.get_unsigned()); } else { return result; } } if (!k.is_unsigned()) { return result; } switch (is_le) { case l_true: result = m_sort.le(k.get_unsigned(), coeffs.size(), coeffs.c_ptr(), m_args.c_ptr()); break; case l_false: result = m_sort.ge(k.get_unsigned(), coeffs.size(), coeffs.c_ptr(), m_args.c_ptr()); break; case l_undef: result = m_sort.eq(k.get_unsigned(), coeffs.size(), coeffs.c_ptr(), m_args.c_ptr()); break; } return result; } /** \brief Segment based encoding. The PB terms are partitoned into segments, such that each segment contains arguments with the same cofficient. The segments are sorted, such that the segment with highest coefficient is first. Then for each segment create circuits based on sorting networks the arguments of the segment. */ expr_ref mk_seg_ge(rational const& k) { rational bound(-k); for (unsigned i = 0; i < m_args.size(); ++i) { m_args[i] = mk_not(m_args[i].get()); bound += m_coeffs[i]; } return mk_seg_le(bound); } expr_ref mk_seg_le(rational const& k) { sort_args(); unsigned sz = m_args.size(); expr* const* args = m_args.c_ptr(); // Create sorted entries. vector> outs; vector coeffs; for (unsigned i = 0, seg_size = 0; i < sz; i += seg_size) { seg_size = segment_size(i); ptr_vector out; m_sort.sorting(seg_size, args + i, out); out.push_back(m.mk_false()); outs.push_back(out); coeffs.push_back(m_coeffs[i]); } return mk_seg_le_rec(outs, coeffs, 0, k); } expr_ref mk_seg_le_rec(vector> const& outs, vector const& coeffs, unsigned i, rational const& k) { rational const& c = coeffs[i]; ptr_vector const& out = outs[i]; if (k.is_neg()) { return expr_ref(m.mk_false(), m); } if (i == outs.size()) { return expr_ref(m.mk_true(), m); } if (i + 1 == outs.size() && k >= rational(out.size()-1)*c) { return expr_ref(m.mk_true(), m); } expr_ref_vector fmls(m); fmls.push_back(m.mk_implies(m.mk_not(out[0]), mk_seg_le_rec(outs, coeffs, i + 1, k))); rational k1; for (unsigned j = 0; j + 1 < out.size(); ++j) { k1 = k - rational(j+1)*c; if (k1.is_neg()) { fmls.push_back(m.mk_not(out[j])); break; } fmls.push_back(m.mk_implies(m.mk_and(out[j], m.mk_not(out[j+1])), mk_seg_le_rec(outs, coeffs, i + 1, k1))); } return ::mk_and(fmls); } // The number of arguments with the same coefficient. unsigned segment_size(unsigned start) const { unsigned i = start; while (i < m_args.size() && m_coeffs[i] == m_coeffs[start]) ++i; return i - start; } expr_ref mk_and(expr_ref& a, expr_ref& b) { if (m.is_true(a)) return b; if (m.is_true(b)) return a; if (m.is_false(a)) return a; if (m.is_false(b)) return b; return expr_ref(m.mk_and(a, b), m); } expr_ref mk_or(expr_ref& a, expr_ref& b) { if (m.is_true(a)) return a; if (m.is_true(b)) return b; if (m.is_false(a)) return b; if (m.is_false(b)) return a; return expr_ref(m.mk_or(a, b), m); } bool mk_le(unsigned sz, expr * const* args, rational const& k, expr_ref& result) { expr_ref_vector args1(m); rational bound(-k); for (unsigned i = 0; i < sz; ++i) { args1.push_back(mk_not(args[i])); bound += m_coeffs[i]; } return mk_ge(sz, args1.c_ptr(), bound, result); } bool mk_eq(unsigned sz, expr * const* args, rational const& k, expr_ref& result) { expr_ref r1(m), r2(m); if (mk_ge(sz, args, k, r1) && mk_le(sz, args, k, r2)) { result = m.mk_and(r1, r2); return true; } else { return false; } } expr_ref mk_bv(func_decl * f, unsigned sz, expr * const* args) { ++m_imp.m_compile_bv; decl_kind kind = f->get_decl_kind(); rational k = pb.get_k(f); m_coeffs.reset(); m_args.reset(); for (unsigned i = 0; i < sz; ++i) { m_coeffs.push_back(pb.get_coeff(f, i)); m_args.push_back(args[i]); } CTRACE("pb", k.is_neg(), tout << expr_ref(m.mk_app(f, sz, args), m) << "\n";); SASSERT(!k.is_neg()); switch (kind) { case OP_PB_GE: case OP_AT_LEAST_K: { dualize(f, m_args, k); SASSERT(!k.is_neg()); return mk_le_ge(k); } case OP_PB_LE: case OP_AT_MOST_K: return mk_le_ge(k); case OP_PB_EQ: return mk_le_ge(k); default: UNREACHABLE(); return expr_ref(m.mk_true(), m); } } void dualize(func_decl* f, expr_ref_vector & args, rational & k) { k.neg(); for (unsigned i = 0; i < args.size(); ++i) { k += pb.get_coeff(f, i); args[i] = ::mk_not(m, args[i].get()); } } expr* negate(expr* e) { if (m.is_not(e, e)) return e; return m.mk_not(e); } expr* mk_ite(expr* c, expr* hi, expr* lo) { while (m.is_not(c, c)) { std::swap(hi, lo); } if (hi == lo) return hi; if (m.is_true(hi) && m.is_false(lo)) return c; if (m.is_false(hi) && m.is_true(lo)) return negate(c); if (m.is_true(hi)) return m.mk_or(c, lo); if (m.is_false(lo)) return m.mk_and(c, hi); if (m.is_false(hi)) return m.mk_and(negate(c), lo); if (m.is_true(lo)) return m.mk_implies(c, hi); return m.mk_ite(c, hi, lo); } bool is_or(func_decl* f) { switch (f->get_decl_kind()) { case OP_AT_MOST_K: case OP_PB_LE: return false; case OP_AT_LEAST_K: case OP_PB_GE: return pb.get_k(f).is_one(); case OP_PB_EQ: return false; default: UNREACHABLE(); return false; } } public: card2bv_rewriter(imp& i, ast_manager& m): m_sort(*this), m(m), m_imp(i), au(m), pb(m), bv(m), m_trail(m), m_args(m), m_keep_cardinality_constraints(false), m_pb_solver(symbol("solver")), m_min_arity(9) {} void set_pb_solver(symbol const& s) { m_pb_solver = s; } bool mk_app(bool full, func_decl * f, unsigned sz, expr * const* args, expr_ref & result) { if (f->get_family_id() == pb.get_family_id() && mk_pb(full, f, sz, args, result)) { // skip } else if (au.is_le(f) && is_pb(args[0], args[1])) { result = mk_le_ge(m_k); } else if (au.is_lt(f) && is_pb(args[0], args[1])) { ++m_k; result = mk_le_ge(m_k); } else if (au.is_ge(f) && is_pb(args[1], args[0])) { result = mk_le_ge(m_k); } else if (au.is_gt(f) && is_pb(args[1], args[0])) { ++m_k; result = mk_le_ge(m_k); } else if (m.is_eq(f) && is_pb(args[0], args[1])) { result = mk_le_ge(m_k); } else { return false; } ++m_imp.m_num_translated; return true; } br_status mk_app_core(func_decl * f, unsigned sz, expr * const* args, expr_ref & result) { if (mk_app(true, f, sz, args, result)) { return BR_DONE; } else { return BR_FAILED; } } bool mk_app(bool full, expr* e, expr_ref& r) { app* a; return (is_app(e) && (a = to_app(e), mk_app(full, a->get_decl(), a->get_num_args(), a->get_args(), r))); } bool is_pb(expr* x, expr* y) { m_args.reset(); m_coeffs.reset(); m_k.reset(); return is_pb(x, rational::one()) && is_pb(y, rational::minus_one()); } bool is_pb(expr* e, rational const& mul) { if (!is_app(e)) { return false; } app* a = to_app(e); rational r, r1, r2; expr* c, *th, *el; unsigned sz = a->get_num_args(); if (a->get_family_id() == au.get_family_id()) { switch (a->get_decl_kind()) { case OP_ADD: for (unsigned i = 0; i < sz; ++i) { if (!is_pb(a->get_arg(i), mul)) return false; } return true; case OP_SUB: { if (!is_pb(a->get_arg(0), mul)) return false; r = -mul; for (unsigned i = 1; i < sz; ++i) { if (!is_pb(a->get_arg(1), r)) return false; } return true; } case OP_UMINUS: return is_pb(a->get_arg(0), -mul); case OP_NUM: VERIFY(au.is_numeral(a, r)); m_k -= mul * r; return true; case OP_MUL: if (sz != 2) { return false; } if (au.is_numeral(a->get_arg(0), r)) { r *= mul; return is_pb(a->get_arg(1), r); } if (au.is_numeral(a->get_arg(1), r)) { r *= mul; return is_pb(a->get_arg(0), r); } return false; default: return false; } } if (m.is_ite(a, c, th, el) && au.is_numeral(th, r1) && au.is_numeral(el, r2)) { r1 *= mul; r2 *= mul; if (r1 < r2) { m_args.push_back(::mk_not(m, c)); m_coeffs.push_back(r2-r1); m_k -= r1; } else { m_args.push_back(c); m_coeffs.push_back(r1-r2); m_k -= r2; } return true; } return false; } bool mk_pb(bool full, func_decl * f, unsigned sz, expr * const* args, expr_ref & result) { SASSERT(f->get_family_id() == pb.get_family_id()); if (is_or(f)) { result = m.mk_or(sz, args); } else if (pb.is_at_most_k(f) && pb.get_k(f).is_unsigned()) { if (m_keep_cardinality_constraints && f->get_arity() >= m_min_arity) return false; result = m_sort.le(full, pb.get_k(f).get_unsigned(), sz, args); ++m_imp.m_compile_card; } else if (pb.is_at_least_k(f) && pb.get_k(f).is_unsigned()) { if (m_keep_cardinality_constraints && f->get_arity() >= m_min_arity) return false; result = m_sort.ge(full, pb.get_k(f).get_unsigned(), sz, args); ++m_imp.m_compile_card; } else if (pb.is_eq(f) && pb.get_k(f).is_unsigned() && pb.has_unit_coefficients(f)) { if (m_keep_cardinality_constraints && f->get_arity() >= m_min_arity) return false; result = m_sort.eq(full, pb.get_k(f).get_unsigned(), sz, args); ++m_imp.m_compile_card; } else if (pb.is_le(f) && pb.get_k(f).is_unsigned() && pb.has_unit_coefficients(f)) { if (m_keep_cardinality_constraints && f->get_arity() >= m_min_arity) return false; result = m_sort.le(full, pb.get_k(f).get_unsigned(), sz, args); ++m_imp.m_compile_card; } else if (pb.is_ge(f) && pb.get_k(f).is_unsigned() && pb.has_unit_coefficients(f)) { if (m_keep_cardinality_constraints && f->get_arity() >= m_min_arity) return false; result = m_sort.ge(full, pb.get_k(f).get_unsigned(), sz, args); ++m_imp.m_compile_card; } else if (pb.is_eq(f) && pb.get_k(f).is_unsigned() && has_small_coefficients(f) && m_pb_solver == "solver") { return false; } else if (pb.is_le(f) && pb.get_k(f).is_unsigned() && has_small_coefficients(f) && m_pb_solver == "solver") { return false; } else if (pb.is_ge(f) && pb.get_k(f).is_unsigned() && has_small_coefficients(f) && m_pb_solver == "solver") { return false; } else { result = mk_bv(f, sz, args); } TRACE("pb", tout << "full: " << full << " " << expr_ref(m.mk_app(f, sz, args), m) << " " << result << "\n"; ); return true; } bool has_small_coefficients(func_decl* f) { unsigned sz = f->get_arity(); unsigned sum = 0; for (unsigned i = 0; i < sz; ++i) { rational c = pb.get_coeff(f, i); if (!c.is_unsigned()) return false; unsigned sum1 = sum + c.get_unsigned(); if (sum1 < sum) return false; sum = sum1; } return true; } // definitions used for sorting network pliteral mk_false() { return m.mk_false(); } pliteral mk_true() { return m.mk_true(); } pliteral mk_max(unsigned n, pliteral const* lits) { return trail(m.mk_or(n, lits)); } pliteral mk_min(unsigned n, pliteral const* lits) { return trail(m.mk_and(n, lits)); } pliteral mk_not(pliteral a) { if (m.is_not(a,a)) return a; return trail(m.mk_not(a)); } std::ostream& pp(std::ostream& out, pliteral lit) { return out << mk_ismt2_pp(lit, m); } pliteral trail(pliteral l) { m_trail.push_back(l); return l; } pliteral fresh(char const* n) { expr_ref fr(m.mk_fresh_const(n, m.mk_bool_sort()), m); m_imp.m_fresh.push_back(to_app(fr)->get_decl()); return trail(fr); } void mk_clause(unsigned n, pliteral const* lits) { m_imp.m_lemmas.push_back(::mk_or(m, n, lits)); } void keep_cardinality_constraints(bool f) { m_keep_cardinality_constraints = f; } void set_cardinality_encoding(sorting_network_encoding enc) { m_sort.cfg().m_encoding = enc; } void set_min_arity(unsigned ma) { m_min_arity = ma; } }; struct card2bv_rewriter_cfg : public default_rewriter_cfg { card2bv_rewriter m_r; bool rewrite_patterns() const { return false; } bool flat_assoc(func_decl * f) 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; return m_r.mk_app_core(f, num, args, result); } card2bv_rewriter_cfg(imp& i, ast_manager & m):m_r(i, m) {} void keep_cardinality_constraints(bool f) { m_r.keep_cardinality_constraints(f); } void set_pb_solver(symbol const& s) { m_r.set_pb_solver(s); } void set_cardinality_encoding(sorting_network_encoding enc) { m_r.set_cardinality_encoding(enc); } void set_min_arity(unsigned ma) { m_r.set_min_arity(ma); } }; class card_pb_rewriter : public rewriter_tpl { public: card2bv_rewriter_cfg m_cfg; card_pb_rewriter(imp& i, ast_manager & m): rewriter_tpl(m, false, m_cfg), m_cfg(i, m) {} void keep_cardinality_constraints(bool f) { m_cfg.keep_cardinality_constraints(f); } void set_pb_solver(symbol const& s) { m_cfg.set_pb_solver(s); } void set_cardinality_encoding(sorting_network_encoding e) { m_cfg.set_cardinality_encoding(e); } void set_min_arity(unsigned ma) { m_cfg.set_min_arity(ma); } void rewrite(bool full, expr* e, expr_ref& r, proof_ref& p) { expr_ref ee(e, m()); if (m_cfg.m_r.mk_app(full, e, r)) { ee = r; // mp proof? } (*this)(ee, r, p); } }; card_pb_rewriter m_rw; bool keep_cardinality() const { params_ref const& p = m_params; return p.get_bool("keep_cardinality_constraints", false) || p.get_bool("sat.cardinality.solver", false) || p.get_bool("cardinality.solver", false) || gparams::get_module("sat").get_bool("cardinality.solver", false); } symbol pb_solver() const { params_ref const& p = m_params; symbol s = p.get_sym("sat.pb.solver", symbol()); if (s != symbol()) return s; s = p.get_sym("pb.solver", symbol()); if (s != symbol()) return s; return gparams::get_module("sat").get_sym("pb.solver", symbol("solver")); } unsigned min_arity() const { params_ref const& p = m_params; unsigned r = p.get_uint("sat.pb.min_arity", UINT_MAX); if (r != UINT_MAX) return r; r = p.get_uint("pb.min_arity", UINT_MAX); if (r != UINT_MAX) return r; return gparams::get_module("sat").get_uint("pb.min_arity", 9); } sorting_network_encoding cardinality_encoding() const { symbol enc = m_params.get_sym("cardinality.encoding", symbol()); if (enc == symbol()) { enc = gparams::get_module("sat").get_sym("cardinality.encoding", symbol()); } if (enc == symbol("grouped")) return sorting_network_encoding::grouped_at_most; if (enc == symbol("bimander")) return sorting_network_encoding::bimander_at_most; if (enc == symbol("ordered")) return sorting_network_encoding::ordered_at_most; if (enc == symbol("unate")) return sorting_network_encoding::unate_at_most; if (enc == symbol("circuit")) return sorting_network_encoding::circuit_at_most; return grouped_at_most; } imp(ast_manager& m, params_ref const& p): m(m), m_params(p), m_lemmas(m), m_fresh(m), m_num_translated(0), m_rw(*this, m) { updt_params(p); m_compile_bv = 0; m_compile_card = 0; } void updt_params(params_ref const & p) { m_params.append(p); m_rw.keep_cardinality_constraints(keep_cardinality()); m_rw.set_pb_solver(pb_solver()); m_rw.set_cardinality_encoding(cardinality_encoding()); m_rw.set_min_arity(min_arity()); } void collect_param_descrs(param_descrs& r) const { r.insert("keep_cardinality_constraints", CPK_BOOL, "(default: false) retain cardinality constraints (don't bit-blast them) and use built-in cardinality solver"); r.insert("pb.solver", CPK_SYMBOL, "(default: solver) retain pb constraints (don't bit-blast them) and use built-in pb solver"); } unsigned get_num_steps() const { return m_rw.get_num_steps(); } void cleanup() { m_rw.cleanup(); } void operator()(bool full, expr * e, expr_ref & result, proof_ref & result_proof) { // m_rw(e, result, result_proof); m_rw.rewrite(full, e, result, result_proof); } void push() { m_fresh_lim.push_back(m_fresh.size()); } void pop(unsigned num_scopes) { SASSERT(m_lemmas.empty()); // lemmas must be flushed before pop. if (num_scopes > 0) { SASSERT(num_scopes <= m_fresh_lim.size()); unsigned new_sz = m_fresh_lim.size() - num_scopes; unsigned lim = m_fresh_lim[new_sz]; m_fresh.resize(lim); m_fresh_lim.resize(new_sz); } m_rw.reset(); } void flush_side_constraints(expr_ref_vector& side_constraints) { side_constraints.append(m_lemmas); m_lemmas.reset(); } void collect_statistics(statistics & st) const { st.update("pb-compile-bv", m_compile_bv); st.update("pb-compile-card", m_compile_card); st.update("pb-aux-variables", m_fresh.size()); st.update("pb-aux-clauses", m_rw.m_cfg.m_r.m_sort.m_stats.m_num_compiled_clauses); } }; pb2bv_rewriter::pb2bv_rewriter(ast_manager & m, params_ref const& p) { m_imp = alloc(imp, m, p); } pb2bv_rewriter::~pb2bv_rewriter() { dealloc(m_imp); } void pb2bv_rewriter::updt_params(params_ref const & p) { m_imp->updt_params(p); } void pb2bv_rewriter::collect_param_descrs(param_descrs& r) const { m_imp->collect_param_descrs(r); } ast_manager & pb2bv_rewriter::m() const { return m_imp->m; } unsigned pb2bv_rewriter::get_num_steps() const { return m_imp->get_num_steps(); } void pb2bv_rewriter::cleanup() { ast_manager& mgr = m(); params_ref p = m_imp->m_params; dealloc(m_imp); m_imp = alloc(imp, mgr, p); } func_decl_ref_vector const& pb2bv_rewriter::fresh_constants() const { return m_imp->m_fresh; } void pb2bv_rewriter::operator()(bool full, expr * e, expr_ref & result, proof_ref & result_proof) { (*m_imp)(full, e, result, result_proof); } void pb2bv_rewriter::push() { m_imp->push(); } void pb2bv_rewriter::pop(unsigned num_scopes) { m_imp->pop(num_scopes); } void pb2bv_rewriter::flush_side_constraints(expr_ref_vector& side_constraints) { m_imp->flush_side_constraints(side_constraints); } unsigned pb2bv_rewriter::num_translated() const { return m_imp->m_num_translated; } void pb2bv_rewriter::collect_statistics(statistics & st) const { m_imp->collect_statistics(st); } z3-z3-4.8.7/src/ast/rewriter/pb2bv_rewriter.h000066400000000000000000000020461356505360400207610ustar00rootroot00000000000000/*++ Copyright (c) 2016 Microsoft Corporation Module Name: pb2bv_rewriter.h Abstract: Conversion from pseudo-booleans to bit-vectors. Author: Nikolaj Bjorner (nbjorner) 2016-10-23 Notes: --*/ #ifndef PB2BV_REWRITER_H_ #define PB2BV_REWRITER_H_ #include "ast/pb_decl_plugin.h" #include "ast/rewriter/rewriter_types.h" #include "ast/expr_functors.h" class pb2bv_rewriter { struct imp; imp* m_imp; public: pb2bv_rewriter(ast_manager & m, params_ref const& p); ~pb2bv_rewriter(); void updt_params(params_ref const & p); void collect_param_descrs(param_descrs& r) const; ast_manager & m() const; unsigned get_num_steps() const; void cleanup(); func_decl_ref_vector const& fresh_constants() const; void operator()(bool full, expr * e, expr_ref & result, proof_ref & result_proof); void push(); void pop(unsigned num_scopes); void flush_side_constraints(expr_ref_vector& side_constraints); unsigned num_translated() const; void collect_statistics(statistics & st) const; }; #endif z3-z3-4.8.7/src/ast/rewriter/pb_rewriter.cpp000066400000000000000000000224271356505360400207070ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: pb_rewriter.cpp Abstract: Basic rewriting rules for PB constraints. Author: Nikolaj Bjorner (nbjorner) 2013-14-12 Notes: --*/ #include "ast/rewriter/pb_rewriter.h" #include "ast/rewriter/pb_rewriter_def.h" #include "ast/ast_pp.h" #include "ast/ast_util.h" #include "ast/ast_smt_pp.h" class pb_ast_rewriter_util { ast_manager& m; expr_ref_vector m_refs; public: typedef std::pair arg_t; typedef vector args_t; typedef rational numeral; pb_ast_rewriter_util(ast_manager& m): m(m), m_refs(m) {} expr* negate(expr* e) { if (m.is_true(e)) { return m.mk_false(); } if (m.is_false(e)) { return m.mk_true(); } if (m.is_not(e, e)) { return e; } m_refs.push_back(m.mk_not(e)); return m_refs.back(); } void display(std::ostream& out, expr* e) { out << mk_pp(e, m); } bool is_negated(expr* e) const { return m.is_not(e); } bool is_true(expr* e) const { return m.is_true(e); } bool is_false(expr* e) const { return m.is_false(e); } struct compare { bool operator()(std::pair const& a, std::pair const& b) { return a.first->get_id() < b.first->get_id(); } }; }; expr_ref pb_rewriter::translate_pb2lia(obj_map& vars, expr* fml) { pb_util util(m()); arith_util a(m()); expr_ref result(m()), tmp(m()); expr_ref_vector es(m()); expr*const* args = to_app(fml)->get_args(); unsigned sz = to_app(fml)->get_num_args(); for (unsigned i = 0; i < sz; ++i) { expr* e = args[i]; if (m().is_not(e, e)) { es.push_back(a.mk_sub(a.mk_numeral(rational(1),true),vars.find(e))); } else { es.push_back(vars.find(e)); } } if (util.is_at_most_k(fml) || util.is_at_least_k(fml)) { if (es.empty()) { tmp = a.mk_numeral(rational(0), true); } else { tmp = a.mk_add(es.size(), es.c_ptr()); } if (util.is_at_most_k(fml)) { result = a.mk_le(tmp, a.mk_numeral(util.get_k(fml), false)); } else { result = a.mk_ge(tmp, a.mk_numeral(util.get_k(fml), false)); } } else if (util.is_le(fml) || util.is_ge(fml) || util.is_eq(fml)) { for (unsigned i = 0; i < sz; ++i) { es[i] = a.mk_mul(a.mk_numeral(util.get_coeff(fml, i),false), es[i].get()); } if (es.empty()) { tmp = a.mk_numeral(rational(0), true); } else { tmp = a.mk_add(es.size(), es.c_ptr()); } rational k = util.get_k(fml); if (util.is_le(fml)) { result = a.mk_le(tmp, a.mk_numeral(k, false)); } else if (util.is_ge(fml)) { result = a.mk_ge(tmp, a.mk_numeral(k, false)); } else { result = m().mk_eq(tmp, a.mk_numeral(k, false)); } } else { result = fml; } return result; } expr_ref pb_rewriter::mk_validate_rewrite(app_ref& e1, app_ref& e2) { ast_manager& m = e1.get_manager(); arith_util a(m); symbol name; obj_map vars; expr_ref_vector trail(m), fmls(m); unsigned sz = to_app(e1)->get_num_args(); expr*const*args = to_app(e1)->get_args(); for (unsigned i = 0; i < sz; ++i) { expr* e = args[i]; if (m.is_true(e)) { if (!vars.contains(e)) { trail.push_back(a.mk_numeral(rational(1), true)); vars.insert(e, trail.back()); } continue; } if (m.is_false(e)) { if (!vars.contains(e)) { trail.push_back(a.mk_numeral(rational(0), true)); vars.insert(e, trail.back()); } continue; } std::ostringstream strm; strm << "x" << i; name = symbol(strm.str().c_str()); trail.push_back(m.mk_const(name, a.mk_int())); expr* x = trail.back(); m.is_not(e,e); vars.insert(e, x); fmls.push_back(a.mk_le(a.mk_numeral(rational(0), true), x)); fmls.push_back(a.mk_le(x, a.mk_numeral(rational(1), true))); } expr_ref tmp(m); expr_ref fml1 = translate_pb2lia(vars, e1); expr_ref fml2 = translate_pb2lia(vars, e2); tmp = m.mk_not(m.mk_eq(fml1, fml2)); fmls.push_back(tmp); tmp = m.mk_and(fmls.size(), fmls.c_ptr()); return tmp; } static unsigned s_lemma = 0; void pb_rewriter::validate_rewrite(func_decl* f, unsigned sz, expr*const* args, expr_ref& fml) { ast_manager& m = fml.get_manager(); app_ref tmp1(m), tmp2(m); tmp1 = m.mk_app(f, sz, args); tmp2 = to_app(fml); expr_ref tmp = mk_validate_rewrite(tmp1, tmp2); dump_pb_rewrite(tmp); } void pb_rewriter::dump_pb_rewrite(expr* fml) { std::ostringstream strm; strm << "pb_rewrite_" << (s_lemma++) << ".smt2"; std::ofstream out(strm.str().c_str()); ast_smt_pp pp(m()); pp.display_smt2(out, fml); out.close(); } br_status pb_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { if (m_util.is_aux_bool(f)) return BR_FAILED; ast_manager& m = result.get_manager(); rational sum(0), maxsum(0); for (unsigned i = 0; i < num_args; ++i) { if (m.is_true(args[i])) { sum += m_util.get_coeff(f, i); maxsum += m_util.get_coeff(f, i); } else if (!m.is_false(args[i])) { maxsum += m_util.get_coeff(f, i); } } rational k = m_util.get_k(f); vector > vec; for (unsigned i = 0; i < num_args; ++i) { vec.push_back(std::make_pair(args[i], m_util.get_coeff(f, i))); } switch(f->get_decl_kind()) { case OP_AT_MOST_K: case OP_PB_LE: for (unsigned i = 0; i < num_args; ++i) { vec[i].second.neg(); } k.neg(); break; case OP_AT_LEAST_K: case OP_PB_GE: case OP_PB_EQ: break; default: UNREACHABLE(); return BR_FAILED; } bool is_eq = f->get_decl_kind() == OP_PB_EQ; br_status st = BR_DONE; pb_ast_rewriter_util pbu(m); pb_rewriter_util util(pbu); util.unique(vec, k, is_eq); lbool is_sat = util.normalize(vec, k, is_eq); util.prune(vec, k, is_eq); switch (is_sat) { case l_true: result = m.mk_true(); break; case l_false: result = m.mk_false(); break; default: { bool all_unit = true; unsigned sz = vec.size(); rational slack(0); m_args.reset(); m_coeffs.reset(); for (auto const& kv : vec) { m_args.push_back(kv.first); m_coeffs.push_back(kv.second); SASSERT(kv.second.is_pos()); slack += kv.second; all_unit &= m_coeffs.back().is_one(); } if (is_eq) { if (sz == 0) { result = k.is_zero()?m.mk_true():m.mk_false(); } else if (k.is_zero()) { result = mk_not(m, mk_or(m, sz, m_args.c_ptr())); } else if (k.is_one() && all_unit && m_args.size() == 1) { result = m_args.back(); } else if (slack == k) { result = mk_and(m, sz, m_args.c_ptr()); } else { result = m_util.mk_eq(sz, m_coeffs.c_ptr(), m_args.c_ptr(), k); } } else if (all_unit && k.is_one() && sz < 10) { result = mk_or(m, sz, m_args.c_ptr()); } else if (all_unit && k == rational(sz)) { result = mk_and(m, sz, m_args.c_ptr()); } else { expr_ref_vector conj(m), disj(m); unsigned j = 0; sz = m_args.size(); for (unsigned i = 0; i < sz; ++i) { rational& c = m_coeffs[i]; if (slack < c + k) { conj.push_back(m_args[i]); slack -= c; k -= c; } else if (c >= k && k.is_pos()) { disj.push_back(m_args[i]); } else { m_args[j] = m_args[i]; m_coeffs[j] = m_coeffs[i]; ++j; } } m_args.shrink(j); m_coeffs.shrink(j); sz = j; if (sz > 0) { disj.push_back(m_util.mk_ge(sz, m_coeffs.c_ptr(), m_args.c_ptr(), k)); } if (!disj.empty()) { conj.push_back(mk_or(disj)); } result = mk_and(conj); if (disj.size() > 1 || conj.size() > 1) { st = BR_REWRITE3; } } break; } } TRACE("pb_verbose", expr_ref tmp(m); tmp = m.mk_app(f, num_args, args); tout << tmp << "\n"; tout << result << "\n"; ); TRACE("pb_validate", validate_rewrite(f, num_args, args, result);); return st; } z3-z3-4.8.7/src/ast/rewriter/pb_rewriter.h000066400000000000000000000032701356505360400203470ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: pb_rewriter.h Abstract: Basic rewriting rules for PB constraints. Author: Nikolaj Bjorner (nbjorner) 2013-14-12 Notes: --*/ #ifndef PB_REWRITER_H_ #define PB_REWRITER_H_ #include "ast/pb_decl_plugin.h" #include "ast/rewriter/rewriter_types.h" #include "util/params.h" #include "util/lbool.h" template class pb_rewriter_util { PBU& m_util; void display(std::ostream& out, typename PBU::args_t& args, typename PBU::numeral& k, bool is_eq); public: pb_rewriter_util(PBU& u) : m_util(u) {} void unique(typename PBU::args_t& args, typename PBU::numeral& k, bool is_eq); lbool normalize(typename PBU::args_t& args, typename PBU::numeral& k, bool is_eq); void prune(typename PBU::args_t& args, typename PBU::numeral& k, bool is_eq); }; /** \brief Cheap rewrite rules for PB constraints */ class pb_rewriter { pb_util m_util; vector m_coeffs; ptr_vector m_args; void validate_rewrite(func_decl* f, unsigned sz, expr*const* args, expr_ref& fml); public: pb_rewriter(ast_manager & m, params_ref const & p = params_ref()): m_util(m) { } ast_manager & m() const { return m_util.get_manager(); } family_id get_fid() const { return m_util.get_family_id(); } 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); expr_ref translate_pb2lia(obj_map& vars, expr* fml); expr_ref mk_validate_rewrite(app_ref& e1, app_ref& e2); void dump_pb_rewrite(expr* fml); }; #endif z3-z3-4.8.7/src/ast/rewriter/pb_rewriter_def.h000066400000000000000000000203671356505360400211730ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: pb_rewriter_def.h Abstract: Basic rewriting rules for PB constraints. Author: Nikolaj Bjorner (nbjorner) 2013-14-12 Notes: --*/ #ifndef PB_REWRITER_DEF_H_ #define PB_REWRITER_DEF_H_ #include "ast/rewriter/pb_rewriter.h" template void pb_rewriter_util::display(std::ostream& out, typename PBU::args_t& args, typename PBU::numeral& k, bool is_eq) { for (unsigned i = 0; i < args.size(); ++i) { out << args[i].second << " * "; m_util.display(out, args[i].first); out << " "; if (i+1 < args.size()) out << "+ "; } out << (is_eq?" = ":" >= ") << k << "\n"; } template void pb_rewriter_util::unique(typename PBU::args_t& args, typename PBU::numeral& k, bool is_eq) { TRACE("pb_verbose", display(tout << "pre-unique:", args, k, is_eq);); for (unsigned i = 0; i < args.size(); ++i) { if (m_util.is_negated(args[i].first)) { args[i].first = m_util.negate(args[i].first); k -= args[i].second; args[i].second = -args[i].second; } } // remove constants unsigned j = 0, sz = args.size(); for (unsigned i = 0; i < sz; ++i) { if (m_util.is_true(args[i].first)) { k -= args[i].second; } else if (m_util.is_false(args[i].first)) { // no-op } else { args[j++] = args[i]; } } args.shrink(j); // sort and coalesce arguments: typename PBU::compare cmp; std::sort(args.begin(), args.end(), cmp); // coallesce unsigned i; for (i = 0, j = 1; j < args.size(); ++j) { if (args[i].first == args[j].first) { args[i].second += args[j].second; } else { ++i; args[i] = args[j]; } } args.resize(i+1); // remove 0s. for (i = 0, j = 0; j < args.size(); ++j) { if (!args[j].second.is_zero()) { if (i != j) { args[i] = args[j]; } ++i; } } args.resize(i); TRACE("pb_verbose", display(tout << "post-unique:", args, k, is_eq);); } template lbool pb_rewriter_util::normalize(typename PBU::args_t& args, typename PBU::numeral& k, bool is_eq) { TRACE("pb_verbose", display(tout << "pre-normalize:", args, k, is_eq);); DEBUG_CODE( bool found = false; for (unsigned i = 0; !found && i < args.size(); ++i) { found = args[i].second.is_zero(); } if (found) display(verbose_stream(), args, k, is_eq); SASSERT(!found);); // // Ensure all coefficients are positive: // c*l + y >= k // <=> // c*(1-~l) + y >= k // <=> // c - c*~l + y >= k // <=> // -c*~l + y >= k - c // typename PBU::numeral sum(0); for (unsigned i = 0; i < args.size(); ++i) { typename PBU::numeral c = args[i].second; if (c.is_neg()) { args[i].second = -c; args[i].first = m_util.negate(args[i].first); k -= c; } sum += args[i].second; } // detect tautologies: if (!is_eq && k <= PBU::numeral::zero()) { args.reset(); k = PBU::numeral::zero(); return l_true; } if (is_eq && k.is_zero() && args.empty()) { return l_true; } // detect infeasible constraints: if (sum < k) { args.reset(); k = PBU::numeral::one(); return l_false; } if (is_eq && k == sum) { for (unsigned i = 0; i < args.size(); ++i) { args[i].second = PBU::numeral::one(); } typename PBU::numeral num(args.size()); k = num; return l_undef; } bool all_int = true; for (unsigned i = 0; all_int && i < args.size(); ++i) { all_int = args[i].second.is_int(); } if (!all_int) { // normalize to integers. typename PBU::numeral d(denominator(k)); for (unsigned i = 0; i < args.size(); ++i) { d = lcm(d, denominator(args[i].second)); } SASSERT(!d.is_one()); k *= d; for (unsigned i = 0; i < args.size(); ++i) { args[i].second *= d; } } if (is_eq) { TRACE("pb_verbose", display(tout << "post-normalize:", args, k, is_eq);); return l_undef; } // Ensure the largest coefficient is not larger than k: sum = PBU::numeral::zero(); for (unsigned i = 0; i < args.size(); ++i) { typename PBU::numeral c = args[i].second; if (c > k) { args[i].second = k; } sum += args[i].second; } SASSERT(!args.empty()); // normalize tight inequalities to unit coefficients. if (sum == k) { for (unsigned i = 0; i < args.size(); ++i) { args[i].second = PBU::numeral::one(); } typename PBU::numeral num(args.size()); k = num; } // apply cutting plane reduction: typename PBU::numeral g(0); for (unsigned i = 0; !g.is_one() && i < args.size(); ++i) { typename PBU::numeral c = args[i].second; if (c != k) { if (g.is_zero()) { g = c; } else { g = gcd(g, c); } } } if (g.is_zero()) { // all coefficients are equal to k. for (unsigned i = 0; i < args.size(); ++i) { SASSERT(args[i].second == k); args[i].second = PBU::numeral::one(); } k = PBU::numeral::one(); } else if (g > PBU::numeral::one()) { // // Example 5x + 5y + 2z + 2u >= 5 // becomes 3x + 3y + z + u >= 3 // typename PBU::numeral k_new = div(k, g); if (!(k % g).is_zero()) { // k_new is the ceiling of k / g. k_new++; } for (unsigned i = 0; i < args.size(); ++i) { SASSERT(args[i].second.is_pos()); typename PBU::numeral c = args[i].second; if (c == k) { c = k_new; } else { c = div(c, g); } args[i].second = c; SASSERT(args[i].second.is_pos()); } k = k_new; } // // normalize coefficients that fall within a range // k/n <= ... < k/(n-1) for some n = 1,2,... // // e.g, k/n <= min <= max < k/(n-1) // k/min <= n, n-1 < k/max // . floor(k/max) = ceil(k/min) - 1 // . floor(k/max) < k/max // // example: k = 5, min = 3, max = 4: 5/3 -> 2 5/4 -> 1, n = 2 // replace all coefficients by 1, and k by 2. // if (!k.is_one()) { typename PBU::numeral min = args[0].second, max = args[0].second; for (unsigned i = 1; i < args.size(); ++i) { if (args[i].second < min) min = args[i].second; if (args[i].second > max) max = args[i].second; } SASSERT(min.is_pos()); typename PBU::numeral n0 = k/max; typename PBU::numeral n1 = floor(n0); typename PBU::numeral n2 = ceil(k/min) - PBU::numeral::one(); if (n1 == n2 && !n0.is_int()) { IF_VERBOSE(3, display(verbose_stream() << "set cardinality\n", args, k, is_eq);); for (unsigned i = 0; i < args.size(); ++i) { args[i].second = PBU::numeral::one(); } k = n1 + PBU::numeral::one(); } } TRACE("pb_verbose", display(tout << "post-normalize:", args, k, is_eq);); return l_undef; } template void pb_rewriter_util::prune(typename PBU::args_t& args, typename PBU::numeral& k, bool is_eq) { if (is_eq) { return; } typename PBU::numeral nlt(0); unsigned occ = 0; for (unsigned i = 0; nlt < k && i < args.size(); ++i) { if (args[i].second < k) { nlt += args[i].second; ++occ; } } if (0 < occ && nlt < k) { for (unsigned i = 0; i < args.size(); ++i) { if (args[i].second < k) { args[i] = args.back(); args.pop_back(); --i; } } unique(args, k, is_eq); normalize(args, k, is_eq); } } #endif z3-z3-4.8.7/src/ast/rewriter/poly_rewriter.h000066400000000000000000000146611356505360400207370ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: poly_rewriter.h Abstract: Basic rewriting rules for Polynomials. Author: Leonardo (leonardo) 2011-04-08 Notes: --*/ #ifndef POLY_REWRITER_H_ #define POLY_REWRITER_H_ #include "ast/ast.h" #include "util/obj_hashtable.h" #include "ast/rewriter/rewriter_types.h" #include "util/params.h" template class poly_rewriter : public Config { protected: typedef typename Config::numeral numeral; sort * m_curr_sort; obj_map m_expr2pos; bool m_flat; bool m_som; unsigned m_som_blowup; bool m_sort_sums; bool m_hoist_mul; bool m_hoist_cmul; bool m_ast_order; bool m_hoist_ite; bool is_numeral(expr * n) const { return Config::is_numeral(n); } bool is_numeral(expr * n, numeral & r) const { return Config::is_numeral(n, r); } bool is_int_numeral(expr * n, numeral & r) const { return Config::is_numeral(n, r) && r.is_int(); } bool is_minus_one(expr * n) const { return Config::is_minus_one(n); } void normalize(numeral & c) { Config::normalize(c, m_curr_sort); } app * mk_numeral(numeral const & r) { return Config::mk_numeral(r, m_curr_sort); } decl_kind add_decl_kind() const { return Config::add_decl_kind(); } decl_kind mul_decl_kind() const { return Config::mul_decl_kind(); } bool use_power() const { return Config::use_power(); } decl_kind power_decl_kind() const { return Config::power_decl_kind(); } bool is_power(expr * t) const { return is_app_of(t, get_fid(), power_decl_kind()); } expr * get_power_body(expr * t, rational & k); expr * mk_mul_app(unsigned num_args, expr * const * args); expr * mk_mul_app(numeral const & c, expr * arg); expr * mk_add_app(unsigned num_args, expr * const * args); br_status mk_flat_mul_core(unsigned num_args, expr * const * args, expr_ref & result); br_status mk_nflat_mul_core(unsigned num_args, expr * const * args, expr_ref & result); expr * get_power_product(expr * t); expr * get_power_product(expr * t, numeral & a); br_status mk_flat_add_core(unsigned num_args, expr * const * args, expr_ref & result); br_status mk_nflat_add_core(unsigned num_args, expr * const * args, expr_ref & result); void set_curr_sort(sort * s) { m_curr_sort = s; } expr * const * get_monomials(expr * & t, unsigned & sz) { if (is_add(t)) { sz = to_app(t)->get_num_args(); return to_app(t)->get_args(); } else { sz = 1; return &t; } } br_status cancel_monomials(expr * lhs, expr * rhs, bool move, expr_ref & lhs_result, expr_ref & rhs_result); bool is_nontrivial_gcd(numeral const& g) const { return !g.is_zero() && !g.is_one(); } bool hoist_ite(expr_ref& e); bool hoist_ite(expr* e, obj_hashtable& shared, numeral& g); expr* apply_hoist(expr* e, numeral const& g, obj_hashtable const& shared); bool hoist_multiplication(expr_ref& som); expr* merge_muls(expr* x, expr* y); struct hoist_cmul_lt; bool is_mul(expr * t, numeral & c, expr * & pp); void hoist_cmul(expr_ref_buffer & args); class mon_lt { poly_rewriter& rw; int ordinal(expr* e) const; public: mon_lt(poly_rewriter& rw): rw(rw) {} bool operator()(expr* e1, expr * e2) const; }; public: poly_rewriter(ast_manager & m, params_ref const & p = params_ref()): Config(m), m_curr_sort(nullptr), m_sort_sums(false) { updt_params(p); SASSERT(!m_som || m_flat); // som of monomials form requires flattening to be enabled. SASSERT(!m_som || !m_hoist_mul); // som is mutually exclusive with hoisting multiplication. updt_params(p); } ast_manager & m() const { return Config::m(); } family_id get_fid() const { return Config::get_fid(); } void updt_params(params_ref const & p); static void get_param_descrs(param_descrs & r); void set_flat(bool f) { m_flat = f; } void set_sort_sums(bool f) { m_sort_sums = f; } bool is_add(expr * n) const { return is_app_of(n, get_fid(), add_decl_kind()); } bool is_mul(expr * n) const { return is_app_of(n, get_fid(), mul_decl_kind()); } bool is_add(func_decl * f) const { return is_decl_of(f, get_fid(), add_decl_kind()); } bool is_mul(func_decl * f) const { return is_decl_of(f, get_fid(), mul_decl_kind()); } bool is_times_minus_one(expr * n, expr*& r) const; bool is_var_plus_ground(expr * n, bool & inv, var * & v, expr_ref & t); bool is_zero(expr* e) const; br_status mk_mul_core(unsigned num_args, expr * const * args, expr_ref & result) { SASSERT(num_args > 0); if (num_args == 1) { result = args[0]; return BR_DONE; } set_curr_sort(m().get_sort(args[0])); return m_flat ? mk_flat_mul_core(num_args, args, result) : mk_nflat_mul_core(num_args, args, result); } br_status mk_add_core(unsigned num_args, expr * const * args, expr_ref & result) { SASSERT(num_args > 0); if (num_args == 1) { result = args[0]; return BR_DONE; } set_curr_sort(m().get_sort(args[0])); return m_flat ? mk_flat_add_core(num_args, args, result) : mk_nflat_add_core(num_args, args, result); } void mk_add(unsigned num_args, expr * const * args, expr_ref & result) { if (mk_add_core(num_args, args, result) == BR_FAILED) result = mk_add_app(num_args, args); } void mk_add(expr* a1, expr* a2, expr_ref& result) { expr* args[2] = { a1, a2 }; mk_add(2, args, result); } void mk_mul(unsigned num_args, expr * const * args, expr_ref & result) { if (mk_mul_core(num_args, args, result) == BR_FAILED) result = mk_mul_app(num_args, args); } void mk_mul(expr* a1, expr* a2, expr_ref& result) { expr* args[2] = { a1, a2 }; mk_mul(2, args, result); } // The result of the following functions is never BR_FAILED br_status mk_uminus(expr * arg, expr_ref & result); br_status mk_sub(unsigned num_args, expr * const * args, expr_ref & result); void mk_sub(expr* a1, expr* a2, expr_ref& result) { expr* args[2] = { a1, a2 }; mk_sub(2, args, result); } }; #endif z3-z3-4.8.7/src/ast/rewriter/poly_rewriter_def.h000066400000000000000000001122261356505360400215510ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: poly_rewriter_def.h Abstract: Basic rewriting rules for Polynomials. Author: Leonardo (leonardo) 2011-04-08 Notes: --*/ #include "util/container_util.h" #include "ast/rewriter/poly_rewriter.h" #include "ast/rewriter/poly_rewriter_params.hpp" #include "ast/rewriter/arith_rewriter_params.hpp" #include "ast/ast_lt.h" #include "ast/ast_ll_pp.h" #include "ast/ast_smt2_pp.h" template void poly_rewriter::updt_params(params_ref const & _p) { poly_rewriter_params p(_p); m_flat = p.flat(); m_som = p.som(); m_hoist_mul = p.hoist_mul(); m_hoist_cmul = p.hoist_cmul(); m_hoist_ite = p.hoist_ite(); m_som_blowup = p.som_blowup(); if (!m_flat) m_som = false; if (m_som) m_hoist_mul = false; arith_rewriter_params ap(_p); m_ast_order = !ap.arith_ineq_lhs(); } template void poly_rewriter::get_param_descrs(param_descrs & r) { poly_rewriter_params::collect_param_descrs(r); } template expr * poly_rewriter::mk_add_app(unsigned num_args, expr * const * args) { switch (num_args) { case 0: return mk_numeral(numeral(0)); case 1: return args[0]; default: return m().mk_app(get_fid(), add_decl_kind(), num_args, args); } } // t = (^ x y) --> return x, and set k = y if k is an integer >= 1 // Otherwise return t and set k = 1 template expr * poly_rewriter::get_power_body(expr * t, rational & k) { if (!is_power(t)) { k = rational(1); return t; } if (is_numeral(to_app(t)->get_arg(1), k) && k.is_int() && k > rational(1)) { return to_app(t)->get_arg(0); } k = rational(1); return t; } template bool poly_rewriter::is_zero(expr* e) const { rational v; return is_numeral(e, v) && v.is_zero(); } template expr * poly_rewriter::mk_mul_app(unsigned num_args, expr * const * args) { switch (num_args) { case 0: return mk_numeral(numeral(1)); case 1: return args[0]; default: if (use_power()) { rational k_prev; expr * prev = get_power_body(args[0], k_prev); rational k; ptr_buffer new_args; #define PUSH_POWER() { \ if (k_prev.is_one()) { \ new_args.push_back(prev); \ } \ else { \ expr * pargs[2] = { prev, mk_numeral(k_prev) }; \ new_args.push_back(m().mk_app(get_fid(), power_decl_kind(), 2, pargs)); \ } \ } for (unsigned i = 1; i < num_args; i++) { expr * arg = get_power_body(args[i], k); if (arg == prev) { k_prev += k; } else { PUSH_POWER(); prev = arg; k_prev = k; } } PUSH_POWER(); SASSERT(new_args.size() > 0); if (new_args.size() == 1) { return new_args[0]; } else { numeral a; if (new_args.size() > 2 && is_numeral(new_args.get(0), a)) { return mk_mul_app(a, mk_mul_app(new_args.size() - 1, new_args.c_ptr() + 1)); } return m().mk_app(get_fid(), mul_decl_kind(), new_args.size(), new_args.c_ptr()); } } else { numeral a; if (num_args > 2 && is_numeral(args[0], a)) { return mk_mul_app(a, mk_mul_app(num_args - 1, args + 1)); } return m().mk_app(get_fid(), mul_decl_kind(), num_args, args); } } } template expr * poly_rewriter::mk_mul_app(numeral const & c, expr * arg) { if (c.is_one()) { return arg; } else if (is_zero(arg)) { return arg; } else { expr * new_args[2] = { mk_numeral(c), arg }; return mk_mul_app(2, new_args); } } template br_status poly_rewriter::mk_flat_mul_core(unsigned num_args, expr * const * args, expr_ref & result) { SASSERT(num_args >= 2); // only try to apply flattening if it is not already in one of the flat monomial forms // - (* c x) // - (* c (* x_1 ... x_n)) if (num_args != 2 || !is_numeral(args[0]) || (is_mul(args[1]) && is_numeral(to_app(args[1])->get_arg(0)))) { unsigned i; for (i = 0; i < num_args; i++) { if (is_mul(args[i])) break; } if (i < num_args) { // input has nested monomials. ptr_buffer flat_args; // we need the todo buffer to handle: (* (* c (* x_1 ... x_n)) (* d (* y_1 ... y_n))) ptr_buffer todo; flat_args.append(i, args); for (unsigned j = i; j < num_args; j++) { if (is_mul(args[j])) { todo.push_back(args[j]); while (!todo.empty()) { expr * curr = todo.back(); todo.pop_back(); if (is_mul(curr)) { unsigned k = to_app(curr)->get_num_args(); while (k > 0) { --k; todo.push_back(to_app(curr)->get_arg(k)); } } else { flat_args.push_back(curr); } } } else { flat_args.push_back(args[j]); } } br_status st = mk_nflat_mul_core(flat_args.size(), flat_args.c_ptr(), result); TRACE("poly_rewriter", tout << "flat mul:\n"; for (unsigned i = 0; i < num_args; i++) tout << mk_bounded_pp(args[i], m()) << "\n"; tout << "---->\n"; for (unsigned i = 0; i < flat_args.size(); i++) tout << mk_bounded_pp(flat_args[i], m()) << "\n"; tout << st << "\n"; ); if (st == BR_FAILED) { result = mk_mul_app(flat_args.size(), flat_args.c_ptr()); return BR_DONE; } return st; } } return mk_nflat_mul_core(num_args, args, result); } template br_status poly_rewriter::mk_nflat_mul_core(unsigned num_args, expr * const * args, expr_ref & result) { mon_lt lt(*this); SASSERT(num_args >= 2); // cheap case numeral a; if (num_args == 2 && is_numeral(args[0], a) && !a.is_one() && !a.is_zero() && (is_var(args[1]) || to_app(args[1])->get_decl()->get_family_id() != get_fid())) return BR_FAILED; numeral c(1); unsigned num_coeffs = 0; unsigned num_add = 0; expr * var = nullptr; for (unsigned i = 0; i < num_args; i++) { expr * arg = args[i]; if (is_numeral(arg, a)) { num_coeffs++; c *= a; } else { var = arg; if (is_add(arg)) num_add++; } } normalize(c); // (* c_1 ... c_n) --> c_1*...*c_n if (num_coeffs == num_args) { result = mk_numeral(c); return BR_DONE; } // (* s ... 0 ... r) --> 0 if (c.is_zero()) { result = mk_numeral(c); return BR_DONE; } if (num_coeffs == num_args - 1) { SASSERT(var != 0); // (* c_1 ... c_n x) --> x if c_1*...*c_n == 1 if (c.is_one()) { result = var; return BR_DONE; } numeral c_prime; if (is_mul(var)) { // apply basic simplification even when flattening is not enabled. // (* c1 (* c2 x)) --> (* c1*c2 x) if (to_app(var)->get_num_args() == 2 && is_numeral(to_app(var)->get_arg(0), c_prime)) { c *= c_prime; normalize(c); result = mk_mul_app(c, to_app(var)->get_arg(1)); return BR_REWRITE1; } else { // var is a power-product return BR_FAILED; } } if (num_add == 0 || m_hoist_cmul) { SASSERT(!is_add(var) || m_hoist_cmul); if (num_args == 2 && args[1] == var) { DEBUG_CODE({ numeral c_prime; SASSERT(is_numeral(args[0], c_prime) && c == c_prime); }); // it is already simplified return BR_FAILED; } // (* c_1 ... c_n x) --> (* c_1*...*c_n x) result = mk_mul_app(c, var); return BR_DONE; } else { SASSERT(is_add(var)); // (* c_1 ... c_n (+ t_1 ... t_m)) --> (+ (* c_1*...*c_n t_1) ... (* c_1*...*c_n t_m)) ptr_buffer new_add_args; unsigned num = to_app(var)->get_num_args(); for (unsigned i = 0; i < num; i++) { new_add_args.push_back(mk_mul_app(c, to_app(var)->get_arg(i))); } result = mk_add_app(new_add_args.size(), new_add_args.c_ptr()); TRACE("mul_bug", tout << "result: " << mk_bounded_pp(result, m(),5) << "\n";); return BR_REWRITE2; } } if (num_coeffs > 1 || (num_coeffs == 1 && !is_numeral(args[0]))) { ptr_buffer m_args; for (unsigned i = 0; i < num_args; i ++) { if (!is_numeral(args[i])) { m_args.push_back(args[i]); } } result = mk_mul_app(c, mk_mul_app(m_args.size(), m_args.c_ptr())); return BR_REWRITE2; } SASSERT(num_coeffs <= num_args - 2); if (!m_som || num_add == 0) { ptr_buffer new_args; expr * prev = nullptr; bool ordered = true; for (unsigned i = 0; i < num_args; i++) { expr * curr = args[i]; if (is_numeral(curr)) continue; if (prev != nullptr && lt(curr, prev)) ordered = false; new_args.push_back(curr); prev = curr; } TRACE("poly_rewriter", for (unsigned i = 0; i < new_args.size(); i++) { if (i > 0) tout << (lt(new_args[i-1], new_args[i]) ? " < " : " !< "); tout << mk_ismt2_pp(new_args[i], m()); } tout << "\nordered: " << ordered << "\n";); if (ordered && num_coeffs == 0 && !use_power()) return BR_FAILED; if (!ordered) { std::sort(new_args.begin(), new_args.end(), lt); TRACE("poly_rewriter", tout << "after sorting:\n"; for (unsigned i = 0; i < new_args.size(); i++) { if (i > 0) tout << (lt(new_args[i-1], new_args[i]) ? " < " : " !< "); tout << mk_ismt2_pp(new_args[i], m()); } tout << "\n";); } SASSERT(new_args.size() >= 2); result = mk_mul_app(new_args.size(), new_args.c_ptr()); result = mk_mul_app(c, result); TRACE("poly_rewriter", tout << "mk_nflat_mul_core result:\n" << mk_ismt2_pp(result, m()) << "\n";); return BR_DONE; } SASSERT(m_som && num_add > 0); sbuffer szs; sbuffer it; sbuffer sums; for (unsigned i = 0; i < num_args; i ++) { it.push_back(0); expr * arg = args[i]; if (is_add(arg)) { sums.push_back(const_cast(to_app(arg)->get_args())); szs.push_back(to_app(arg)->get_num_args()); } else { sums.push_back(const_cast(args + i)); szs.push_back(1); SASSERT(sums.back()[0] == arg); } } unsigned orig_size = sums.size(); expr_ref_buffer sum(m()); // must be ref_buffer because we may throw an exception ptr_buffer m_args; TRACE("som", tout << "starting som...\n";); do { TRACE("som", for (unsigned i = 0; i < it.size(); i++) tout << it[i] << " "; tout << "\n";); if (sum.size() > m_som_blowup * orig_size) { return BR_FAILED; } m_args.reset(); for (unsigned i = 0; i < num_args; i++) { expr * const * v = sums[i]; expr * arg = v[it[i]]; m_args.push_back(arg); } sum.push_back(mk_mul_app(m_args.size(), m_args.c_ptr())); } while (product_iterator_next(szs.size(), szs.c_ptr(), it.c_ptr())); result = mk_add_app(sum.size(), sum.c_ptr()); return BR_REWRITE2; } template br_status poly_rewriter::mk_flat_add_core(unsigned num_args, expr * const * args, expr_ref & result) { unsigned i; for (i = 0; i < num_args; i++) { if (is_add(args[i])) break; } if (i < num_args) { // has nested ADDs ptr_buffer flat_args; flat_args.append(i, args); for (; i < num_args; i++) { expr * arg = args[i]; // Remark: all rewrites are depth 1. if (is_add(arg)) { unsigned num = to_app(arg)->get_num_args(); for (unsigned j = 0; j < num; j++) flat_args.push_back(to_app(arg)->get_arg(j)); } else { flat_args.push_back(arg); } } br_status st = mk_nflat_add_core(flat_args.size(), flat_args.c_ptr(), result); if (st == BR_FAILED) { result = mk_add_app(flat_args.size(), flat_args.c_ptr()); return BR_DONE; } return st; } return mk_nflat_add_core(num_args, args, result); } template inline expr * poly_rewriter::get_power_product(expr * t) { if (is_mul(t) && to_app(t)->get_num_args() == 2 && is_numeral(to_app(t)->get_arg(0))) return to_app(t)->get_arg(1); return t; } template inline expr * poly_rewriter::get_power_product(expr * t, numeral & a) { if (is_mul(t) && to_app(t)->get_num_args() == 2 && is_numeral(to_app(t)->get_arg(0), a)) return to_app(t)->get_arg(1); a = numeral(1); return t; } template bool poly_rewriter::is_mul(expr * t, numeral & c, expr * & pp) { if (!is_mul(t) || to_app(t)->get_num_args() != 2) return false; if (!is_numeral(to_app(t)->get_arg(0), c)) return false; pp = to_app(t)->get_arg(1); return true; } template struct poly_rewriter::hoist_cmul_lt { poly_rewriter & m_r; hoist_cmul_lt(poly_rewriter & r):m_r(r) {} bool operator()(expr * t1, expr * t2) const { expr * pp1 = nullptr; expr * pp2 = nullptr; numeral c1, c2; bool is_mul1 = m_r.is_mul(t1, c1, pp1); bool is_mul2 = m_r.is_mul(t2, c2, pp2); if (!is_mul1 && is_mul2) return true; if (is_mul1 && !is_mul2) return false; if (!is_mul1 && !is_mul2) return t1->get_id() < t2->get_id(); if (c1 < c2) return true; if (c1 > c2) return false; return pp1->get_id() < pp2->get_id(); } }; template void poly_rewriter::hoist_cmul(expr_ref_buffer & args) { unsigned sz = args.size(); std::sort(args.c_ptr(), args.c_ptr() + sz, hoist_cmul_lt(*this)); numeral c, c_prime; ptr_buffer pps; expr * pp, * pp_prime; unsigned j = 0; unsigned i = 0; while (i < sz) { expr * mon = args[i]; if (is_mul(mon, c, pp) && i < sz - 1) { expr * mon_prime = args[i+1]; if (is_mul(mon_prime, c_prime, pp_prime) && c == c_prime) { // found target pps.reset(); pps.push_back(pp); pps.push_back(pp_prime); i += 2; while (i < sz && is_mul(args[i], c_prime, pp_prime) && c == c_prime) { pps.push_back(pp_prime); i++; } SASSERT(is_numeral(to_app(mon)->get_arg(0), c_prime) && c == c_prime); expr * mul_args[2] = { to_app(mon)->get_arg(0), mk_add_app(pps.size(), pps.c_ptr()) }; args.set(j, mk_mul_app(2, mul_args)); j++; continue; } } args.set(j, mon); j++; i++; } args.resize(j); } template bool poly_rewriter::mon_lt::operator()(expr* e1, expr * e2) const { if (rw.m_ast_order) return lt(e1,e2); return ordinal(e1) < ordinal(e2); } inline bool is_essentially_var(expr * n, family_id fid) { SASSERT(is_var(n) || is_app(n)); return is_var(n) || to_app(n)->get_family_id() != fid; } template int poly_rewriter::mon_lt::ordinal(expr* e) const { rational k; if (is_essentially_var(e, rw.get_fid())) { return e->get_id(); } else if (rw.is_mul(e)) { if (rw.is_numeral(to_app(e)->get_arg(0))) return to_app(e)->get_arg(1)->get_id(); else return e->get_id(); } else if (rw.is_numeral(e)) { return -1; } else if (rw.use_power() && rw.is_power(e) && rw.is_numeral(to_app(e)->get_arg(1), k) && k > rational(1)) { return to_app(e)->get_arg(0)->get_id(); } else { return e->get_id(); } } template br_status poly_rewriter::mk_nflat_add_core(unsigned num_args, expr * const * args, expr_ref & result) { mon_lt lt(*this); SASSERT(num_args >= 2); numeral c; unsigned num_coeffs = 0; numeral a; expr_fast_mark1 visited; // visited.is_marked(power_product) if the power_product occurs in args expr_fast_mark2 multiple; // multiple.is_marked(power_product) if power_product occurs more than once bool has_multiple = false; expr * prev = nullptr; bool ordered = true; for (unsigned i = 0; i < num_args; i++) { expr * arg = args[i]; if (is_numeral(arg, a)) { num_coeffs++; c += a; ordered = !m_sort_sums || i == 0; } else if (m_sort_sums && ordered) { if (prev != nullptr && lt(arg, prev)) ordered = false; prev = arg; } arg = get_power_product(arg); if (visited.is_marked(arg)) { multiple.mark(arg); has_multiple = true; } else { visited.mark(arg); } } normalize(c); SASSERT(m_sort_sums || ordered); TRACE("rewriter", tout << "ordered: " << ordered << " sort sums: " << m_sort_sums << "\n"; for (unsigned i = 0; i < num_args; i++) tout << mk_ismt2_pp(args[i], m()) << "\n";); if (has_multiple) { // expensive case buffer coeffs; m_expr2pos.reset(); // compute the coefficient of power products that occur multiple times. for (unsigned i = 0; i < num_args; i++) { expr * arg = args[i]; if (is_numeral(arg)) continue; expr * pp = get_power_product(arg, a); if (!multiple.is_marked(pp)) continue; unsigned pos; if (m_expr2pos.find(pp, pos)) { coeffs[pos] += a; } else { m_expr2pos.insert(pp, coeffs.size()); coeffs.push_back(a); } } expr_ref_buffer new_args(m()); if (!c.is_zero()) { new_args.push_back(mk_numeral(c)); } // copy power products with non zero coefficients to new_args visited.reset(); for (unsigned i = 0; i < num_args; i++) { expr * arg = args[i]; if (is_numeral(arg)) continue; expr * pp = get_power_product(arg); if (!multiple.is_marked(pp)) { new_args.push_back(arg); } else if (!visited.is_marked(pp)) { visited.mark(pp); unsigned pos = UINT_MAX; m_expr2pos.find(pp, pos); SASSERT(pos != UINT_MAX); a = coeffs[pos]; normalize(a); if (!a.is_zero()) new_args.push_back(mk_mul_app(a, pp)); } } if (m_hoist_cmul) { hoist_cmul(new_args); } else if (m_sort_sums) { TRACE("rewriter_bug", tout << "new_args.size(): " << new_args.size() << "\n";); if (c.is_zero()) std::sort(new_args.c_ptr(), new_args.c_ptr() + new_args.size(), mon_lt(*this)); else std::sort(new_args.c_ptr() + 1, new_args.c_ptr() + new_args.size(), mon_lt(*this)); } result = mk_add_app(new_args.size(), new_args.c_ptr()); TRACE("rewriter", tout << result << "\n";); if (hoist_multiplication(result)) { return BR_REWRITE_FULL; } if (hoist_ite(result)) { return BR_REWRITE_FULL; } return BR_DONE; } else { SASSERT(!has_multiple); if (ordered && !m_hoist_mul && !m_hoist_cmul && !m_hoist_ite) { if (num_coeffs == 0) return BR_FAILED; if (num_coeffs == 1 && is_numeral(args[0], a) && !a.is_zero()) return BR_FAILED; } expr_ref_buffer new_args(m()); if (!c.is_zero()) new_args.push_back(mk_numeral(c)); for (unsigned i = 0; i < num_args; i++) { expr * arg = args[i]; if (is_numeral(arg)) continue; new_args.push_back(arg); } if (m_hoist_cmul) { hoist_cmul(new_args); } else if (!ordered) { if (c.is_zero()) std::sort(new_args.c_ptr(), new_args.c_ptr() + new_args.size(), lt); else std::sort(new_args.c_ptr() + 1, new_args.c_ptr() + new_args.size(), lt); } result = mk_add_app(new_args.size(), new_args.c_ptr()); if (hoist_multiplication(result)) { return BR_REWRITE_FULL; } if (hoist_ite(result)) { return BR_REWRITE_FULL; } return BR_DONE; } } template br_status poly_rewriter::mk_uminus(expr * arg, expr_ref & result) { numeral a; set_curr_sort(m().get_sort(arg)); if (is_numeral(arg, a)) { a.neg(); normalize(a); result = mk_numeral(a); return BR_DONE; } else { result = mk_mul_app(numeral(-1), arg); return BR_REWRITE1; } } template br_status poly_rewriter::mk_sub(unsigned num_args, expr * const * args, expr_ref & result) { SASSERT(num_args > 0); if (num_args == 1) { result = args[0]; return BR_DONE; } set_curr_sort(m().get_sort(args[0])); expr_ref minus_one(mk_numeral(numeral(-1)), m()); expr_ref_buffer new_args(m()); new_args.push_back(args[0]); for (unsigned i = 1; i < num_args; i++) { if (is_zero(args[i])) continue; expr * aux_args[2] = { minus_one, args[i] }; new_args.push_back(mk_mul_app(2, aux_args)); } result = mk_add_app(new_args.size(), new_args.c_ptr()); return BR_REWRITE2; } /** \brief Cancel/Combine monomials that occur is the left and right hand sides. \remark If move = true, then all non-constant monomials are moved to the left-hand-side. */ template br_status poly_rewriter::cancel_monomials(expr * lhs, expr * rhs, bool move, expr_ref & lhs_result, expr_ref & rhs_result) { set_curr_sort(m().get_sort(lhs)); mon_lt lt(*this); unsigned lhs_sz; expr * const * lhs_monomials = get_monomials(lhs, lhs_sz); unsigned rhs_sz; expr * const * rhs_monomials = get_monomials(rhs, rhs_sz); expr_fast_mark1 visited; // visited.is_marked(power_product) if the power_product occurs in lhs or rhs expr_fast_mark2 multiple; // multiple.is_marked(power_product) if power_product occurs more than once bool has_multiple = false; numeral c(0); numeral a; unsigned num_coeffs = 0; for (unsigned i = 0; i < lhs_sz; i++) { expr * arg = lhs_monomials[i]; if (is_numeral(arg, a)) { c += a; num_coeffs++; } else { visited.mark(get_power_product(arg)); } } if (move && num_coeffs == 0 && is_numeral(rhs)) { TRACE("mk_le_bug", tout << "no coeffs\n";); return BR_FAILED; } for (unsigned i = 0; i < rhs_sz; i++) { expr * arg = rhs_monomials[i]; if (is_numeral(arg, a)) { c -= a; num_coeffs++; } else { expr * pp = get_power_product(arg); if (visited.is_marked(pp)) { multiple.mark(pp); has_multiple = true; } } } normalize(c); if (!has_multiple && num_coeffs <= 1) { if (move) { if (is_numeral(rhs)) { return BR_FAILED; } } else { if (num_coeffs == 0 || is_numeral(rhs)) { return BR_FAILED; } } } buffer coeffs; m_expr2pos.reset(); for (unsigned i = 0; i < lhs_sz; i++) { expr * arg = lhs_monomials[i]; if (is_numeral(arg)) continue; expr * pp = get_power_product(arg, a); if (!multiple.is_marked(pp)) continue; unsigned pos; if (m_expr2pos.find(pp, pos)) { coeffs[pos] += a; } else { m_expr2pos.insert(pp, coeffs.size()); coeffs.push_back(a); } } for (unsigned i = 0; i < rhs_sz; i++) { expr * arg = rhs_monomials[i]; if (is_numeral(arg)) continue; expr * pp = get_power_product(arg, a); if (!multiple.is_marked(pp)) continue; unsigned pos = UINT_MAX; m_expr2pos.find(pp, pos); SASSERT(pos != UINT_MAX); coeffs[pos] -= a; } ptr_buffer new_lhs_monomials; new_lhs_monomials.push_back(0); // save space for coefficient if needed // copy power products with non zero coefficients to new_lhs_monomials visited.reset(); for (unsigned i = 0; i < lhs_sz; i++) { expr * arg = lhs_monomials[i]; if (is_numeral(arg)) continue; expr * pp = get_power_product(arg); if (!multiple.is_marked(pp)) { new_lhs_monomials.push_back(arg); } else if (!visited.is_marked(pp)) { visited.mark(pp); unsigned pos = UINT_MAX; m_expr2pos.find(pp, pos); SASSERT(pos != UINT_MAX); a = coeffs[pos]; if (!a.is_zero()) new_lhs_monomials.push_back(mk_mul_app(a, pp)); } } ptr_buffer new_rhs_monomials; new_rhs_monomials.push_back(0); // save space for coefficient if needed for (unsigned i = 0; i < rhs_sz; i++) { expr * arg = rhs_monomials[i]; if (is_numeral(arg)) continue; expr * pp = get_power_product(arg, a); if (!multiple.is_marked(pp)) { if (move) { if (!a.is_zero()) { if (a.is_minus_one()) { new_lhs_monomials.push_back(pp); } else { a.neg(); SASSERT(!a.is_one()); expr * args[2] = { mk_numeral(a), pp }; new_lhs_monomials.push_back(mk_mul_app(2, args)); } } } else { new_rhs_monomials.push_back(arg); } } } bool c_at_rhs = false; if (move) { if (m_sort_sums) { // + 1 to skip coefficient std::sort(new_lhs_monomials.begin() + 1, new_lhs_monomials.end(), lt); } c_at_rhs = true; } else if (new_rhs_monomials.size() == 1) { // rhs is empty c_at_rhs = true; } else if (new_lhs_monomials.size() > 1) { c_at_rhs = true; } if (c_at_rhs) { c.neg(); normalize(c); } // When recreating the lhs and rhs also insert coefficient on the appropriate side. // Ignore coefficient if it's 0 and there are no other summands. const bool insert_c_lhs = !c_at_rhs && (new_lhs_monomials.size() == 1 || !c.is_zero()); const bool insert_c_rhs = c_at_rhs && (new_rhs_monomials.size() == 1 || !c.is_zero()); const unsigned lhs_offset = insert_c_lhs ? 0 : 1; const unsigned rhs_offset = insert_c_rhs ? 0 : 1; new_rhs_monomials[0] = insert_c_rhs ? mk_numeral(c) : nullptr; new_lhs_monomials[0] = insert_c_lhs ? mk_numeral(c) : nullptr; lhs_result = mk_add_app(new_lhs_monomials.size() - lhs_offset, new_lhs_monomials.c_ptr() + lhs_offset); rhs_result = mk_add_app(new_rhs_monomials.size() - rhs_offset, new_rhs_monomials.c_ptr() + rhs_offset); TRACE("mk_le_bug", tout << lhs_result << " " << rhs_result << "\n";); return BR_DONE; } #define TO_BUFFER(_tester_, _buffer_, _e_) \ _buffer_.push_back(_e_); \ for (unsigned _i = 0; _i < _buffer_.size(); ) { \ expr* _e = _buffer_[_i]; \ if (_tester_(_e)) { \ app* a = to_app(_e); \ _buffer_[_i] = a->get_arg(0); \ for (unsigned _j = 1; _j < a->get_num_args(); ++_j) { \ _buffer_.push_back(a->get_arg(_j)); \ } \ } \ else { \ ++_i; \ } \ } \ template bool poly_rewriter::hoist_multiplication(expr_ref& som) { if (!m_hoist_mul) { return false; } ptr_buffer adds, muls; TO_BUFFER(is_add, adds, som); buffer valid(adds.size(), true); obj_map mul_map; unsigned j; bool change = false; for (unsigned k = 0; k < adds.size(); ++k) { expr* e = adds[k]; muls.reset(); TO_BUFFER(is_mul, muls, e); for (unsigned i = 0; i < muls.size(); ++i) { e = muls[i]; if (is_numeral(e)) { continue; } if (mul_map.find(e, j) && valid[j] && j != k) { m_curr_sort = m().get_sort(adds[k]); adds[j] = merge_muls(adds[j], adds[k]); adds[k] = mk_numeral(rational(0)); valid[j] = false; valid[k] = false; change = true; break; } else { mul_map.insert(e, k); } } } if (!change) { return false; } som = mk_add_app(adds.size(), adds.c_ptr()); return true; } template expr* poly_rewriter::merge_muls(expr* x, expr* y) { ptr_buffer m1, m2; TO_BUFFER(is_mul, m1, x); TO_BUFFER(is_mul, m2, y); unsigned k = 0; for (unsigned i = 0; i < m1.size(); ++i) { x = m1[i]; bool found = false; unsigned j; for (j = k; j < m2.size(); ++j) { found = m2[j] == x; if (found) break; } if (found) { std::swap(m1[i],m1[k]); std::swap(m2[j],m2[k]); ++k; } } m_curr_sort = m().get_sort(x); SASSERT(k > 0); SASSERT(m1.size() >= k); SASSERT(m2.size() >= k); expr* args[2] = { mk_mul_app(m1.size()-k, m1.c_ptr()+k), mk_mul_app(m2.size()-k, m2.c_ptr()+k) }; if (k == m1.size()) { m1.push_back(0); } m1[k] = mk_add_app(2, args); return mk_mul_app(k+1, m1.c_ptr()); } template bool poly_rewriter::hoist_ite(expr_ref& e) { if (!m_hoist_ite) { return false; } obj_hashtable shared; ptr_buffer adds; expr_ref_vector bs(m()), pinned(m()); TO_BUFFER(is_add, adds, e); unsigned i = 0; for (expr* a : adds) { if (m().is_ite(a)) { shared.reset(); numeral g(0); if (hoist_ite(a, shared, g) && (is_nontrivial_gcd(g) || !shared.empty())) { bs.reset(); if (!shared.empty()) { g = numeral(1); } bs.push_back(apply_hoist(a, g, shared)); if (is_nontrivial_gcd(g)) { bs.push_back(mk_numeral(g)); bs[0] = mk_mul_app(2, bs.c_ptr()); bs.pop_back(); } else { for (expr* s : shared) { bs.push_back(s); } } expr* a2 = mk_add_app(bs.size(), bs.c_ptr()); if (a != a2) { adds[i] = a2; pinned.push_back(a2); } } } ++i; } if (!pinned.empty()) { e = mk_add_app(adds.size(), adds.c_ptr()); return true; } return false; } template bool poly_rewriter::hoist_ite(expr* a, obj_hashtable& shared, numeral& g) { expr* c = nullptr, *t = nullptr, *e = nullptr; if (m().is_ite(a, c, t, e)) { return hoist_ite(t, shared, g) && hoist_ite(e, shared, g); } rational k, g1; if (is_int_numeral(a, k)) { return false; } ptr_buffer adds; TO_BUFFER(is_add, adds, a); if (g.is_zero()) { // first for (expr* e : adds) { shared.insert(e); } } else { obj_hashtable tmp; for (expr* e : adds) { tmp.insert(e); } set_intersection, obj_hashtable>(shared, tmp); } g = numeral(1); return !shared.empty(); } template expr* poly_rewriter::apply_hoist(expr* a, numeral const& g, obj_hashtable const& shared) { expr* c = nullptr, *t = nullptr, *e = nullptr; if (m().is_ite(a, c, t, e)) { return m().mk_ite(c, apply_hoist(t, g, shared), apply_hoist(e, g, shared)); } rational k; if (is_nontrivial_gcd(g) && is_int_numeral(a, k)) { return mk_numeral(k/g); } ptr_buffer adds; TO_BUFFER(is_add, adds, a); unsigned i = 0; for (expr* e : adds) { if (!shared.contains(e)) { adds[i++] = e; } } adds.shrink(i); return mk_add_app(adds.size(), adds.c_ptr()); } template bool poly_rewriter::is_times_minus_one(expr * n, expr* & r) const { if (is_mul(n) && to_app(n)->get_num_args() == 2 && is_minus_one(to_app(n)->get_arg(0))) { r = to_app(n)->get_arg(1); return true; } return false; } /** \brief Return true if n is can be put into the form (+ v t) or (+ (- v) t) \c inv = true will contain true if (- v) is found, and false otherwise. */ template bool poly_rewriter::is_var_plus_ground(expr * n, bool & inv, var * & v, expr_ref & t) { if (!is_add(n) || is_ground(n)) return false; ptr_buffer args; v = nullptr; expr * curr = to_app(n); bool stop = false; inv = false; while (!stop) { expr * arg; expr * neg_arg; if (is_add(curr)) { arg = to_app(curr)->get_arg(0); curr = to_app(curr)->get_arg(1); } else { arg = curr; stop = true; } if (is_ground(arg)) { args.push_back(arg); } else if (is_var(arg)) { if (v != nullptr) return false; // already found variable v = to_var(arg); } else if (is_times_minus_one(arg, neg_arg) && is_var(neg_arg)) { if (v != nullptr) return false; // already found variable v = to_var(neg_arg); inv = true; } else { return false; // non ground term. } } if (v == nullptr) return false; // did not find variable SASSERT(!args.empty()); mk_add(args.size(), args.c_ptr(), t); return true; } z3-z3-4.8.7/src/ast/rewriter/poly_rewriter_params.pyg000066400000000000000000000015411356505360400226430ustar00rootroot00000000000000def_module_params(module_name='rewriter', class_name='poly_rewriter_params', export=True, params=(("som", BOOL, False, "put polynomials in sum-of-monomials form"), ("som_blowup", UINT, 10, "maximum increase of monomials generated when putting a polynomial in sum-of-monomials normal form"), ("hoist_mul", BOOL, False, "hoist multiplication over summation to minimize number of multiplications"), ("hoist_cmul", BOOL, False, "hoist constant multiplication over summation to minimize number of multiplications"), ("hoist_ite", BOOL, False, "hoist shared summands under ite expressions"), ("flat", BOOL, True, "create nary applications for and,or,+,*,bvadd,bvmul,bvand,bvor,bvxor"))) z3-z3-4.8.7/src/ast/rewriter/push_app_ite.cpp000066400000000000000000000050701356505360400210360ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: push_app_ite.cpp Abstract: TODO: Write a better ite lifter Author: Leonardo de Moura (leonardo) 2008-05-14. Revision History: --*/ #include "ast/rewriter/push_app_ite.h" #include "ast/ast_pp.h" static int has_ite_arg(ast_manager& m, unsigned num_args, expr * const * args) { for (unsigned i = 0; i < num_args; i++) if (m.is_ite(args[i])) return i; return -1; } /** \brief Default (conservative) implementation. Return true if there one and only one ite-term argument. */ bool push_app_ite_cfg::is_target(func_decl * decl, unsigned num_args, expr * const * args) { if (m.is_ite(decl)) return false; bool found_ite = false; for (unsigned i = 0; i < num_args; i++) { if (m.is_ite(args[i]) && !m.is_bool(args[i])) { if (found_ite) { if (m_conservative) return false; } else { found_ite = true; } } } CTRACE("push_app_ite", found_ite, tout << "found target for push app ite:\n"; tout << decl->get_name(); for (unsigned i = 0; i < num_args; i++) tout << " " << mk_pp(args[i], m); tout << "\n";); return found_ite; } br_status push_app_ite_cfg::reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { if (!is_target(f, num, args)) { return BR_FAILED; } int ite_arg_idx = has_ite_arg(m, num, args); if (ite_arg_idx < 0) { return BR_FAILED; } app * ite = to_app(args[ite_arg_idx]); expr * c = nullptr, * t = nullptr, * e = nullptr; VERIFY(m.is_ite(ite, c, t, e)); expr ** args_prime = const_cast(args); expr * old = args_prime[ite_arg_idx]; args_prime[ite_arg_idx] = t; expr_ref t_new(m.mk_app(f, num, args_prime), m); args_prime[ite_arg_idx] = e; expr_ref e_new(m.mk_app(f, num, args_prime), m); args_prime[ite_arg_idx] = old; result = m.mk_ite(c, t_new, e_new); TRACE("push_app_ite", tout << result << "\n";); if (m.proofs_enabled()) { result_pr = m.mk_rewrite(m.mk_app(f, num, args), result); } return BR_REWRITE2; } bool ng_push_app_ite_cfg::is_target(func_decl * decl, unsigned num_args, expr * const * args) { bool r = push_app_ite_cfg::is_target(decl, num_args, args); if (!r) return false; for (unsigned i = 0; i < num_args; i++) if (!is_ground(args[i])) return true; return false; } z3-z3-4.8.7/src/ast/rewriter/push_app_ite.h000066400000000000000000000040061356505360400205010ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: push_app_ite.h Abstract: Author: Leonardo de Moura (leonardo) 2008-05-14. Revision History: --*/ #ifndef PUSH_APP_ITE_H_ #define PUSH_APP_ITE_H_ #include "ast/ast.h" #include "ast/rewriter/rewriter.h" /** \brief Functor for applying the following transformation: (f s (ite c t1 t2)) ==> (ite c (f s t1) (f s t2)) */ struct push_app_ite_cfg : public default_rewriter_cfg { ast_manager& m; bool m_conservative; virtual bool is_target(func_decl * decl, unsigned num_args, expr * const * args); br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr); push_app_ite_cfg(ast_manager& m, bool conservative = true): m(m), m_conservative(conservative) {} bool rewrite_patterns() const { return false; } }; /** \brief Variation of push_app_ite that applies the transformation on nonground terms only. \remark This functor uses the app::is_ground method. This method is not completely precise, for instance, any term containing a quantifier is marked as non ground. */ class ng_push_app_ite_cfg : public push_app_ite_cfg { protected: bool is_target(func_decl * decl, unsigned num_args, expr * const * args) override; public: ng_push_app_ite_cfg(ast_manager& m, bool conservative = true): push_app_ite_cfg(m, conservative) {} virtual ~ng_push_app_ite_cfg() {} }; struct push_app_ite_rw : public rewriter_tpl { push_app_ite_cfg m_cfg; public: push_app_ite_rw(ast_manager& m, bool conservative = true): rewriter_tpl(m, m.proofs_enabled(), m_cfg), m_cfg(m, conservative) {} }; struct ng_push_app_ite_rw : public rewriter_tpl { ng_push_app_ite_cfg m_cfg; public: ng_push_app_ite_rw(ast_manager& m, bool conservative = true): rewriter_tpl(m, m.proofs_enabled(), m_cfg), m_cfg(m, conservative) {} }; #endif /* PUSH_APP_ITE_H_ */ z3-z3-4.8.7/src/ast/rewriter/quant_hoist.cpp000066400000000000000000000264731356505360400207260ustar00rootroot00000000000000/*++ Copyright (c) 2010 Microsoft Corporation Module Name: quant_hoist.cpp Abstract: Quantifier hoisting utility. Author: Nikolaj Bjorner (nbjorner) 2010-02-19 Revision History: Hoisted from quant_elim. --*/ #include "ast/rewriter/quant_hoist.h" #include "ast/expr_functors.h" #include "ast/ast_smt_pp.h" #include "ast/rewriter/bool_rewriter.h" #include "ast/rewriter/var_subst.h" #include "ast/ast_pp.h" #include "ast/rewriter/ast_counter.h" #include "ast/rewriter/expr_safe_replace.h" // // Bring quantifiers of common type into prenex form. // class quantifier_hoister::impl { ast_manager& m; bool_rewriter m_rewriter; public: impl(ast_manager& m) : m(m), m_rewriter(m) {} void operator()(expr* fml, app_ref_vector& vars, bool& is_fa, expr_ref& result, bool use_fresh, bool rewrite_ok) { quantifier_type qt = Q_none_pos; pull_quantifier(fml, qt, vars, result, use_fresh, rewrite_ok); TRACE("qe_verbose", tout << mk_pp(fml, m) << "\n"; tout << mk_pp(result, m) << "\n";); SASSERT(is_positive(qt)); is_fa = (Q_forall_pos == qt); } void pull_exists(expr* fml, app_ref_vector& vars, expr_ref& result, bool use_fresh, bool rewrite_ok) { quantifier_type qt = Q_exists_pos; pull_quantifier(fml, qt, vars, result, use_fresh, rewrite_ok); TRACE("qe_verbose", tout << mk_pp(fml, m) << "\n"; tout << mk_pp(result, m) << "\n";); } void pull_quantifier(bool is_forall, expr_ref& fml, app_ref_vector& vars, bool use_fresh, bool rewrite_ok) { quantifier_type qt = is_forall?Q_forall_pos:Q_exists_pos; expr_ref result(m); pull_quantifier(fml, qt, vars, result, use_fresh, rewrite_ok); TRACE("qe_verbose", tout << mk_pp(fml, m) << "\n"; tout << mk_pp(result, m) << "\n";); fml = std::move(result); } void extract_quantifier(quantifier* q, app_ref_vector& vars, expr_ref& result, bool use_fresh) { unsigned nd = q->get_num_decls(); for (unsigned i = 0; i < nd; ++i) { sort* s = q->get_decl_sort(i); symbol const& sym = q->get_decl_name (i); app* a = use_fresh ? m.mk_fresh_const(sym.str ().c_str (), s) : m.mk_const (sym, s); vars.push_back(a); } expr * const * exprs = (expr* const*) (vars.c_ptr() + vars.size()- nd); result = instantiate(m, q, exprs); } unsigned pull_quantifier(bool _is_forall, expr_ref& fml, ptr_vector* sorts, svector* names, bool use_fresh, bool rewrite_ok) { unsigned index = var_counter().get_next_var(fml); while (is_quantifier(fml) && _is_forall == is_forall(fml)) { quantifier* q = to_quantifier(fml); index += q->get_num_decls(); if (names) { names->append(q->get_num_decls(), q->get_decl_names()); } if (sorts) { sorts->append(q->get_num_decls(), q->get_decl_sorts()); } fml = q->get_expr(); } if (!has_quantifiers(fml)) { return index; } app_ref_vector vars(m); pull_quantifier(_is_forall, fml, vars, use_fresh, rewrite_ok); if (vars.empty()) { return index; } // replace vars by de-bruijn indices expr_safe_replace rep(m); svector bound_names; ptr_vector bound_sorts; for (unsigned i = 0; i < vars.size(); ++i) { app* v = vars[i].get(); if (names) { bound_names.push_back(v->get_decl()->get_name()); } if (sorts) { bound_sorts.push_back(m.get_sort(v)); } rep.insert(v, m.mk_var(index++, m.get_sort(v))); } if (names && !bound_names.empty()) { bound_names.reverse(); bound_names.append(*names); names->reset(); names->append(bound_names); } if (sorts && !bound_sorts.empty()) { bound_sorts.reverse(); bound_sorts.append(*sorts); sorts->reset(); sorts->append(bound_sorts); } rep(fml); return index; } private: enum quantifier_type { Q_forall_pos = 0x10, Q_exists_pos = 0x20, Q_none_pos = 0x40, Q_forall_neg = 0x11, Q_exists_neg = 0x21, Q_none_neg = 0x41 }; void display(quantifier_type qt, std::ostream& out) { switch(qt) { case Q_forall_pos: out << "Forall+"; break; case Q_exists_pos: out << "Exists+"; break; case Q_none_pos: out << "None+"; break; case Q_forall_neg: out << "Forall-"; break; case Q_exists_neg: out << "Exists-"; break; case Q_none_neg: out << "None-"; break; } } quantifier_type& negate(quantifier_type& qt) { qt = static_cast(qt ^0x1); return qt; } static bool is_negative(quantifier_type qt) { return 0 != (qt & 0x1); } static bool is_positive(quantifier_type qt) { return 0 == (qt & 0x1); } static void set_quantifier_type(quantifier_type& qt, bool is_forall) { switch(qt) { case Q_forall_pos: SASSERT(is_forall); break; case Q_forall_neg: SASSERT(!is_forall); break; case Q_exists_pos: SASSERT(!is_forall); break; case Q_exists_neg: SASSERT(is_forall); break; case Q_none_pos: qt = is_forall?Q_forall_pos:Q_exists_pos; break; case Q_none_neg: qt = is_forall?Q_exists_neg:Q_forall_neg; break; } } bool is_compatible(quantifier_type qt, bool is_forall) { switch(qt) { case Q_forall_pos: return is_forall; case Q_forall_neg: return !is_forall; case Q_exists_pos: return !is_forall; case Q_exists_neg: return is_forall; case Q_none_pos: return true; case Q_none_neg: return true; default: UNREACHABLE(); } return false; } void pull_quantifier(expr* fml, quantifier_type& qt, app_ref_vector& vars, expr_ref& result, bool use_fresh, bool rewrite_ok) { if (!has_quantifiers(fml)) { result = fml; return; } switch(fml->get_kind()) { case AST_APP: { expr_ref_vector args(m); expr_ref tmp(m); expr* t1, *t2, *t3; unsigned num_args = 0; app* a = to_app(fml); if (m.is_and(fml)) { num_args = a->get_num_args(); for (unsigned i = 0; i < num_args; ++i) { pull_quantifier(a->get_arg(i), qt, vars, tmp, use_fresh, rewrite_ok); args.push_back(tmp); } if (rewrite_ok) { m_rewriter.mk_and(args.size(), args.c_ptr(), result); } else { result = m.mk_and (args.size (), args.c_ptr ()); } } else if (m.is_or(fml)) { num_args = to_app(fml)->get_num_args(); for (unsigned i = 0; i < num_args; ++i) { pull_quantifier(to_app(fml)->get_arg(i), qt, vars, tmp, use_fresh, rewrite_ok); args.push_back(tmp); } if (rewrite_ok) { m_rewriter.mk_or(args.size(), args.c_ptr(), result); } else { result = m.mk_or (args.size (), args.c_ptr ()); } } else if (m.is_not(fml)) { pull_quantifier(to_app(fml)->get_arg(0), negate(qt), vars, tmp, use_fresh, rewrite_ok); negate(qt); result = m.mk_not(tmp); } else if (m.is_implies(fml, t1, t2)) { pull_quantifier(t1, negate(qt), vars, tmp, use_fresh, rewrite_ok); negate(qt); pull_quantifier(t2, qt, vars, result, use_fresh, rewrite_ok); result = m.mk_implies(tmp, result); } else if (m.is_ite(fml, t1, t2, t3)) { expr_ref tt1(m), tt2(m), tt3(m), ntt1(m), nt1(m); pull_quantifier(t2, qt, vars, tt2, use_fresh, rewrite_ok); pull_quantifier(t3, qt, vars, tt3, use_fresh, rewrite_ok); if (has_quantifiers(t1)) { pull_quantifier(t1, qt, vars, tt1, use_fresh, rewrite_ok); nt1 = m.mk_not(t1); pull_quantifier(nt1, qt, vars, ntt1, use_fresh, rewrite_ok); result = m.mk_and(m.mk_or(ntt1, tt2), m.mk_or(tt1, tt3)); } else { result = m.mk_ite(t1, tt2, tt3); } } else if (m.is_eq(fml, t1, t2) && m.is_bool(t1)) { expr_ref tt1(m), tt2(m), ntt1(m), ntt2(m), nt1(m), nt2(m); pull_quantifier(t1, qt, vars, tt1, use_fresh, rewrite_ok); pull_quantifier(t2, qt, vars, tt2, use_fresh, rewrite_ok); nt1 = m.mk_not(t1); nt2 = m.mk_not(t2); pull_quantifier(nt1, qt, vars, ntt1, use_fresh, rewrite_ok); pull_quantifier(nt2, qt, vars, ntt2, use_fresh, rewrite_ok); result = m.mk_and(m.mk_or(ntt1, tt2), m.mk_or(ntt2, tt1)); } else { // the formula contains a quantifier, but it is "inaccessible" result = fml; } break; } case AST_QUANTIFIER: { quantifier* q = to_quantifier(fml); if (is_lambda(q)) { result = fml; break; } expr_ref tmp(m); if (!is_compatible(qt, is_forall(q))) { result = fml; break; } set_quantifier_type(qt, is_forall(q)); extract_quantifier(q, vars, tmp, use_fresh); pull_quantifier(tmp, qt, vars, result, use_fresh, rewrite_ok); break; } case AST_VAR: result = fml; break; default: UNREACHABLE(); result = fml; break; } } }; quantifier_hoister::quantifier_hoister(ast_manager& m) { m_impl = alloc(impl, m); } quantifier_hoister::~quantifier_hoister() { dealloc(m_impl); } void quantifier_hoister::operator()(expr* fml, app_ref_vector& vars, bool& is_fa, expr_ref& result, bool use_fresh, bool rewrite_ok) { (*m_impl)(fml, vars, is_fa, result, use_fresh, rewrite_ok); } void quantifier_hoister::pull_exists(expr* fml, app_ref_vector& vars, expr_ref& result, bool use_fresh, bool rewrite_ok) { m_impl->pull_exists(fml, vars, result, use_fresh, rewrite_ok); } void quantifier_hoister::pull_quantifier(bool is_forall, expr_ref& fml, app_ref_vector& vars, bool use_fresh, bool rewrite_ok) { m_impl->pull_quantifier(is_forall, fml, vars, use_fresh, rewrite_ok); } unsigned quantifier_hoister::pull_quantifier(bool is_forall, expr_ref& fml, ptr_vector* sorts, svector* names, bool use_fresh, bool rewrite_ok) { return m_impl->pull_quantifier(is_forall, fml, sorts, names, use_fresh, rewrite_ok); } z3-z3-4.8.7/src/ast/rewriter/quant_hoist.h000066400000000000000000000040271356505360400203620ustar00rootroot00000000000000/*++ Copyright (c) 2010 Microsoft Corporation Module Name: quant_hoist.h Abstract: Quantifier hoisting utility. Author: Nikolaj Bjorner (nbjorner) 2010-02-19 Revision History: Hoisted from quant_elim. --*/ #ifndef QUANTIFIER_HOISTER_H_ #define QUANTIFIER_HOISTER_H_ #include "ast/ast.h" class quantifier_hoister { class impl; impl* m_impl; public: quantifier_hoister(ast_manager& m); ~quantifier_hoister(); /** \brief Pull top-most quantifier up. Create fresh constants for the bound variables. Return the constants, the quantifier type (forall or exists), and the sub-formula under the quantifier. The list of variables is empty if the formula is quantifier free or if the existing quantifiers occur under a connective other than or, and, implies, ite (then and else branch only). */ void operator()(expr* fml, app_ref_vector& vars, bool& is_fa, expr_ref& result, bool use_fresh = true, bool rewrite_ok = true); /** \brief Pull top-most existential quantifier up. The list of variables is empty if there are no top-level existential quantifier. */ void pull_exists(expr* fml, app_ref_vector& vars, expr_ref& result, bool use_fresh = true, bool rewrite_ok = true); /** \brief Pull top-most universal (is_forall=true) or existential (is_forall=false) quantifier up. The list of variables is empty if there are no top-level universal/existential quantifier. */ void pull_quantifier(bool is_forall, expr_ref& fml, app_ref_vector& vars, bool use_fresh = true, bool rewrite_ok = true); /** \brief Pull top-most universal (is_forall true) or existential (is_forall=false) quantifier up. Return an expression with de-Bruijn indices and the list of names that were used. Return index of maximal variable. */ unsigned pull_quantifier(bool is_forall, expr_ref& fml, ptr_vector* sorts, svector* names, bool use_fresh = true, bool rewrite_ok = true); }; #endif z3-z3-4.8.7/src/ast/rewriter/recfun_replace.h000066400000000000000000000020601356505360400207740ustar00rootroot00000000000000/*++ Copyright (c) 2018 Microsoft Corporation Module Name: recfun_replace.h Abstract: replace function for recfun. recfun_decl_plugin relies on being able to do expression replacement. It uses expr_safe_replace from ast/rewriter, which depends on ast. To break the dependency cycle we hoist the relevant functionality into an argument to functionality exposed by recfun::set_definition Author: Nikolaj Bjorner (nbjorner) 2018-11-01 Revision History: --*/ #ifndef RECFUN_REPLACE_H_ #define RECFUN_REPLACE_H_ #include "ast/recfun_decl_plugin.h" #include "ast/rewriter/expr_safe_replace.h" class recfun_replace : public recfun::replace { ast_manager& m; expr_safe_replace m_replace; public: recfun_replace(ast_manager& m): m(m), m_replace(m) {} ~recfun_replace() override {} void reset() override { m_replace.reset(); } void insert(expr* s, expr* t) override { m_replace.insert(s, t); } expr_ref operator()(expr* e) override { expr_ref r(m); m_replace(e, r); return r; } }; #endif /* RECFUN_REPLACE_H_ */ z3-z3-4.8.7/src/ast/rewriter/rewriter.cpp000066400000000000000000000252741356505360400202310ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: rewriter.cpp Abstract: Lean and mean rewriter Author: Leonardo (leonardo) 2011-03-31 Notes: --*/ #include "ast/rewriter/rewriter_def.h" #include "ast/ast_ll_pp.h" #include "ast/ast_smt2_pp.h" void rewriter_core::init_cache_stack() { SASSERT(m_cache_stack.empty()); m_cache = alloc(cache, m()); m_cache_stack.push_back(m_cache); if (m_proof_gen) { SASSERT(m_cache_pr_stack.empty()); m_cache_pr = alloc(cache, m()); m_cache_pr_stack.push_back(m_cache_pr); } } void rewriter_core::del_cache_stack() { std::for_each(m_cache_stack.begin(), m_cache_stack.end(), delete_proc()); m_cache_stack.finalize(); m_cache = nullptr; if (m_proof_gen) { std::for_each(m_cache_pr_stack.begin(), m_cache_pr_stack.end(), delete_proc()); m_cache_pr_stack.finalize(); m_cache_pr = nullptr; } } 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; #endif SASSERT(!m_proof_gen); TRACE("rewriter_cache_result", tout << mk_ismt2_pp(k, m()) << "\n--->\n" << mk_ismt2_pp(v, m()) << "\n";); SASSERT(m().get_sort(k) == m().get_sort(v)); m_cache->insert(k, offset, v); #if 0 static unsigned num_cached = 0; num_cached ++; if (num_cached % 100000 == 0) verbose_stream() << "[rewriter] :num-cached " << num_cached << " :capacity " << m_cache->capacity() << " :size " << m_cache->size() << " :frame-stack-size " << m_frame_stack.size() << std::endl; #endif } void rewriter_core::cache_result(expr * k, expr * v, proof * pr) { m_cache->insert(k, v); SASSERT(m_proof_gen); m_cache_pr->insert(k, pr); } unsigned rewriter_core::get_cache_size() const { return m_cache->size(); } void rewriter_core::reset_cache() { m_cache = m_cache_stack[0]; m_cache->reset(); if (m_proof_gen) { m_cache_pr = m_cache_pr_stack[0]; m_cache_pr->reset(); } } // free memory allocated by the rewriter void rewriter_core::free_memory() { del_cache_stack(); m_frame_stack.finalize(); m_result_stack.finalize(); m_scopes.finalize(); } void rewriter_core::begin_scope() { m_scopes.push_back(scope(m_root, m_num_qvars)); unsigned lvl = m_scopes.size(); SASSERT(lvl <= m_cache_stack.size()); SASSERT(!m_proof_gen || m_cache_pr_stack.size() == m_cache_stack.size()); if (lvl == m_cache_stack.size()) { m_cache_stack.push_back(alloc(cache, m())); if (m_proof_gen) m_cache_pr_stack.push_back(alloc(cache, m())); } m_cache = m_cache_stack[lvl]; m_cache->reset(); SASSERT(m_cache->empty()); if (m_proof_gen) { m_cache_pr = m_cache_pr_stack[lvl]; m_cache_pr->reset(); SASSERT(m_cache_pr->empty()); } } void rewriter_core::end_scope() { m_cache->reset(); if (m_proof_gen) m_cache_pr->reset(); scope & s = m_scopes.back(); m_root = s.m_old_root; m_num_qvars = s.m_old_num_qvars; m_scopes.pop_back(); unsigned new_lvl = m_scopes.size(); m_cache = m_cache_stack[new_lvl]; if (m_proof_gen) m_cache_pr = m_cache_pr_stack[new_lvl]; } bool rewriter_core::is_child_of_top_frame(expr * t) const { if (m_frame_stack.empty()) return true; frame const & fr = m_frame_stack.back(); expr * parent = fr.m_curr; unsigned num; switch (parent->get_kind()) { case AST_APP: num = to_app(parent)->get_num_args(); for (unsigned i = 0; i < num; i++) { if (to_app(parent)->get_arg(i) == t) return true; } return false; case AST_QUANTIFIER: num = to_quantifier(parent)->get_num_children(); for (unsigned i = 0; i < num; i++) { if (to_quantifier(parent)->get_child(i) == t) return true; } return false; default: return false; } } /** \brief Eliminate (implicit) reflexivity proofs from m_result_pr_stack starting at position spos. The implicit reflexivity proof is 0. */ void rewriter_core::elim_reflex_prs(unsigned spos) { SASSERT(m_proof_gen); unsigned sz = m_result_pr_stack.size(); SASSERT(spos <= sz); unsigned j = spos; for (unsigned i = spos; i < sz; i++) { proof * pr = m_result_pr_stack.get(i); if (pr != nullptr) { if (i != j) m_result_pr_stack.set(j, pr); j++; } } m_result_pr_stack.shrink(j); } rewriter_core::rewriter_core(ast_manager & m, bool proof_gen): m_manager(m), m_proof_gen(proof_gen), m_cancel_check(true), m_result_stack(m), m_result_pr_stack(m), m_num_qvars(0) { init_cache_stack(); } rewriter_core::~rewriter_core() { del_cache_stack(); } // reset rewriter (macro definitions are not erased) void rewriter_core::reset() { SASSERT(!m_cache_stack.empty()); reset_cache(); m_frame_stack.reset(); m_result_stack.reset(); if (m_proof_gen) m_result_pr_stack.reset(); m_root = nullptr; m_num_qvars = 0; m_scopes.reset(); } // free memory & reset (macro definitions are not erased) void rewriter_core::cleanup() { free_memory(); init_cache_stack(); m_root = nullptr; m_num_qvars = 0; } #ifdef _TRACE void rewriter_core::display_stack(std::ostream & out, unsigned pp_depth) { for (frame& f : m_frame_stack) { out << mk_bounded_pp(f.m_curr, m(), pp_depth) << "\n"; out << "state: " << f.m_state << "\n"; out << "cache: " << f.m_cache_result << ", new_child: " << f.m_new_child << ", max-depth: " << f.m_max_depth << ", i: " << f.m_i << "\n"; out << "------------------\n"; } } #endif bool var_shifter_core::visit(expr * t) { if (is_ground(t)) { m_result_stack.push_back(t); return true; } bool c = must_cache(t); if (c) { expr * r = get_cached(t); if (r) { m_result_stack.push_back(r); set_new_child_flag(t, r); return true; } } switch (t->get_kind()) { case AST_APP: SASSERT(to_app(t)->get_num_args() > 0); push_frame(t, c); return false; case AST_VAR: process_var(to_var(t)); return true; case AST_QUANTIFIER: push_frame(t, c); return false; default: UNREACHABLE(); return true; } } void var_shifter_core::process_app(app * t, frame & fr) { unsigned num_args = t->get_num_args(); while (fr.m_i < num_args) { expr * arg = t->get_arg(fr.m_i); fr.m_i++; if (!visit(arg)) return; } SASSERT(fr.m_spos + num_args == m_result_stack.size()); expr * new_t; if (fr.m_new_child) { expr * const * new_args = m_result_stack.c_ptr() + fr.m_spos; new_t = m().mk_app(t->get_decl(), num_args, new_args); } else { new_t = t; } m_result_stack.shrink(fr.m_spos); m_result_stack.push_back(new_t); m_frame_stack.pop_back(); set_new_child_flag(t, new_t); if (fr.m_cache_result) cache_result(t, new_t); } void var_shifter_core::process_quantifier(quantifier * q, frame & fr) { if (fr.m_i == 0) { begin_scope(); m_num_qvars += q->get_num_decls(); m_root = q->get_expr(); } unsigned num_children = q->get_num_children(); while (fr.m_i < num_children) { expr * child = q->get_child(fr.m_i); fr.m_i++; if (!visit(child)) return; } SASSERT(fr.m_spos + num_children == m_result_stack.size()); expr * new_q; if (fr.m_new_child) { expr * const * it = m_result_stack.c_ptr() + fr.m_spos; expr * new_expr = *it; ++it; expr * const * new_pats = it; expr * const * new_no_pats = new_pats + q->get_num_patterns(); new_q = m().update_quantifier(q, q->get_num_patterns(), new_pats, q->get_num_no_patterns(), new_no_pats, new_expr); } else { new_q = q; } m_result_stack.shrink(fr.m_spos); m_result_stack.push_back(new_q); m_frame_stack.pop_back(); set_new_child_flag(q, new_q); end_scope(); if (fr.m_cache_result) cache_result(q, new_q); } void var_shifter_core::main_loop(expr * t, expr_ref & r) { SASSERT(m_cache == m_cache_stack[0]); SASSERT(m_frame_stack.empty()); SASSERT(m_result_stack.empty()); m_root = t; if (visit(t)) { r = m_result_stack.back(); m_result_stack.pop_back(); SASSERT(m_result_stack.empty()); return; } SASSERT(!m_frame_stack.empty()); while (!m_frame_stack.empty()) { frame & fr = m_frame_stack.back(); expr * t = fr.m_curr; if (fr.m_i == 0 && fr.m_cache_result) { expr * r = get_cached(t); if (r) { m_result_stack.push_back(r); m_frame_stack.pop_back(); set_new_child_flag(t, r); continue; } } switch (t->get_kind()) { case AST_APP: process_app(to_app(t), fr); break; case AST_QUANTIFIER: process_quantifier(to_quantifier(t), fr); break; default: UNREACHABLE(); } } r = m_result_stack.back(); m_result_stack.pop_back(); SASSERT(m_result_stack.empty()); } void var_shifter::operator()(expr * t, unsigned bound, unsigned shift1, unsigned shift2, expr_ref & r) { if (is_ground(t)) { r = t; return; } reset_cache(); m_bound = bound; m_shift1 = shift1; m_shift2 = shift2; main_loop(t, r); } void var_shifter::process_var(var * v) { unsigned vidx = v->get_idx(); if (vidx < m_num_qvars) { m_result_stack.push_back(v); } else { unsigned nvidx = vidx - m_num_qvars; if (nvidx >= m_bound) vidx += m_shift1; else vidx += m_shift2; m_result_stack.push_back(m().mk_var(vidx, v->get_sort())); set_new_child_flag(v); } } void inv_var_shifter::operator()(expr * t, unsigned shift, expr_ref & r) { if (is_ground(t)) { r = t; return; } reset_cache(); m_shift = shift; main_loop(t, r); } void inv_var_shifter::process_var(var * v) { unsigned vidx = v->get_idx(); if (vidx < m_num_qvars) { m_result_stack.push_back(v); } else { SASSERT(vidx >= m_num_qvars + m_shift); vidx -= m_shift; m_result_stack.push_back(m().mk_var(vidx, v->get_sort())); set_new_child_flag(v); } } template class rewriter_tpl; z3-z3-4.8.7/src/ast/rewriter/rewriter.h000066400000000000000000000352361356505360400176750ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: rewriter.h Abstract: Lean and mean rewriter Author: Leonardo (leonardo) 2011-03-31 Notes: --*/ #ifndef REWRITER_H_ #define REWRITER_H_ #include "ast/ast.h" #include "ast/rewriter/rewriter_types.h" #include "ast/act_cache.h" /** \brief Common infrastructure for AST rewriters. */ class rewriter_core { protected: struct frame { expr * m_curr; unsigned m_cache_result:1; // true if the result of rewriting m_expr must be cached. unsigned m_new_child:1; unsigned m_state:2; unsigned m_max_depth:2; // bounded rewrite... if m_max_depth == 0, then children are not rewritten. unsigned m_i:26; unsigned m_spos; // top of the result stack, when the frame was created. frame(expr * n, bool cache_res, unsigned st, unsigned max_depth, unsigned spos): m_curr(n), m_cache_result(cache_res), m_new_child(false), m_state(st), m_max_depth(max_depth), m_i(0), m_spos(spos) { } }; ast_manager & m_manager; bool m_proof_gen; bool m_cancel_check; typedef act_cache cache; ptr_vector m_cache_stack; cache * m_cache; // current cache. svector m_frame_stack; expr_ref_vector m_result_stack; // proof generation goodness ---- ptr_vector m_cache_pr_stack; cache * m_cache_pr; proof_ref_vector m_result_pr_stack; // -------------------------- expr * m_root; unsigned m_num_qvars; struct scope { expr * m_old_root; unsigned m_old_num_qvars; scope(expr * r, unsigned n):m_old_root(r), m_old_num_qvars(n) {} }; svector m_scopes; // Return true if the rewriting result of the given expression must be cached. bool must_cache(expr * t) const { return t->get_ref_count() > 1 && // t must be a shared expression t != m_root && // t must not be the root expression ((is_app(t) && to_app(t)->get_num_args() > 0) || is_quantifier(t)); // t is a (non-constant) application or a quantifier. } void push_frame_core(expr * t, bool cache_res, unsigned st = 0, unsigned max_depth = RW_UNBOUNDED_DEPTH) { SASSERT(!m_proof_gen || m_result_stack.size() == m_result_pr_stack.size()); m_frame_stack.push_back(frame(t, cache_res, st, max_depth, m_result_stack.size())); } void push_frame(expr * t, unsigned st = 0) { push_frame_core(t, must_cache(t), st); } void init_cache_stack(); void del_cache_stack(); void reset_cache(); 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)); } void free_memory(); void begin_scope(); void end_scope(); bool is_child_of_top_frame(expr * t) const; void set_new_child_flag(expr * old_t) { CTRACE("rewriter_bug", !is_child_of_top_frame(old_t), display_stack(tout, 3);); SASSERT(is_child_of_top_frame(old_t)); if (!m_frame_stack.empty()) m_frame_stack.back().m_new_child = true; } void set_new_child_flag(expr * old_t, expr * new_t) { if (old_t != new_t) set_new_child_flag(old_t); } void elim_reflex_prs(unsigned spos); public: rewriter_core(ast_manager & m, bool proof_gen); virtual ~rewriter_core(); ast_manager & m() const { return m_manager; } void reset(); void cleanup(); void set_cancel_check(bool f) { m_cancel_check = f; } #ifdef _TRACE void display_stack(std::ostream & out, unsigned pp_depth); #endif unsigned get_cache_size() const; }; class var_shifter_core : public rewriter_core { protected: bool visit(expr * t); void process_app(app * t, frame & fr); virtual void process_var(var * v) = 0; void process_quantifier(quantifier * q, frame & fr); void main_loop(expr * t, expr_ref & r); public: var_shifter_core(ast_manager & m):rewriter_core(m, false) {} }; /** \brief Functor used to shift the free variables of an AST by a given amount. This functor is used by the var_subst functor. This functor implements the following functions: 1) shift(n, s) will return a new AST where all free variables (VAR i) in n, are replaced by (VAR (+ i s)). 2) shift(n, b, s1, s2) is a variant of the previous function such that for a each free variable (VAR i) is shifted to: - (VAR i + s1) if i >= b - (VAR i + s2) if i < b */ class var_shifter : public var_shifter_core { unsigned m_bound; unsigned m_shift1; unsigned m_shift2; void process_var(var * v) override; public: var_shifter(ast_manager & m):var_shifter_core(m) {} void operator()(expr * t, unsigned bound, unsigned shift1, unsigned shift2, expr_ref & r); void operator()(expr * t, unsigned s, expr_ref & r) { operator()(t, 0, s, 0, r); } }; /** \brief Functor used to shift the free variables of an AST by a negative amount. Abstract implementation: inv_shift(f(c_1, ..., c_n), d, s) = f(inv_shift(c_1, d, s), ..., inv_shift(c_n, d, s)) inv_shift(var(i), d, s) = if (i < d) var(i) else assert(i - s >= d) var(i-s) inv_shift((forall (x) P), d, s) = (forall (x) inv_shift(P, d+1, s)) This functor assumes that if we are shifting an expression F by N, then F does not contain free variables #0, ... #N-1 See assertion above. */ class inv_var_shifter : public var_shifter_core { protected: unsigned m_shift; void process_var(var * v) override; public: inv_var_shifter(ast_manager & m):var_shifter_core(m) {} void operator()(expr * t, unsigned shift, expr_ref & r); }; template class rewriter_tpl : public rewriter_core { protected: // Rewriter maintains a stack of frames. // Each frame represents an expression that is being rewritten. // The resultant expressions are store on the Result stack. // Each frame is in a particular state. // Let f(t_0,...,t_n) be the expression is the frame Fr. Then Fr can be is one of the // following states: // PROCESS_CHILDREN(i) the children t_0, ..., t_{i-1} have already been rewritten, and the result is on the Result stack. // REWRITE_BUILTIN All t_0, ..., t_n have been rewritten to t_0',...,t_n', and the builtin rewriter (or plugin) produced a new term t // that can be further rewritten. // EXPAND_DEF All t_0, ..., t_n have been rewritten to t_0',...,t_n'. // The function symbol f is a macro, and the body of the macro needs to be rewritten using the t_0',...,t_n' as bindings. // REWRITE_RULE All t_0, ..., t_n have been rewritten to t_0',...,t_n'. // There is rewrite rule lhs -> rhs s.t. f(t_0', ..., t_n') matches lhs with substitution delta. // rhs is then rewritten using delta. enum state { PROCESS_CHILDREN, REWRITE_BUILTIN, EXPAND_DEF, REWRITE_RULE }; Config & m_cfg; unsigned m_num_steps; ptr_vector m_bindings; var_shifter m_shifter; inv_var_shifter m_inv_shifter; expr_ref m_r; proof_ref m_pr; proof_ref m_pr2; unsigned_vector m_shifts; svector & frame_stack() { return this->m_frame_stack; } svector const & frame_stack() const { return this->m_frame_stack; } expr_ref_vector & result_stack() { return this->m_result_stack; } expr_ref_vector const & result_stack() const { return this->m_result_stack; } proof_ref_vector & result_pr_stack() { return this->m_result_pr_stack; } proof_ref_vector const & result_pr_stack() const { return this->m_result_pr_stack; } void display_bindings(std::ostream& out); void set_new_child_flag(expr * old_t) { SASSERT(frame_stack().empty() || frame_stack().back().m_state != PROCESS_CHILDREN || this->is_child_of_top_frame(old_t)); if (!frame_stack().empty()) frame_stack().back().m_new_child = true; } void set_new_child_flag(expr * old_t, expr * new_t) { if (old_t != new_t) set_new_child_flag(old_t); } // cache the result of shared non atomic expressions. bool cache_results() const { return m_cfg.cache_results(); } // cache all results share and non shared expressions non atomic expressions. bool cache_all_results() const { return m_cfg.cache_all_results(); } // flat non shared AC terms bool flat_assoc(func_decl * f) const { return m_cfg.flat_assoc(f); } // rewrite patterns bool rewrite_patterns() const { return m_cfg.rewrite_patterns(); } // check maximum number of scopes void check_max_scopes() const { if (m_cfg.max_scopes_exceeded(this->m_scopes.size())) throw rewriter_exception(Z3_MAX_SCOPES_MSG); } // check maximum size of the frame stack void check_max_frames() const { if (m_cfg.max_frames_exceeded(frame_stack().size())) throw rewriter_exception(Z3_MAX_FRAMES_MSG); } // check maximum number of rewriting steps void check_max_steps() const { if (m_cfg.max_steps_exceeded(m_num_steps)) throw rewriter_exception(Z3_MAX_STEPS_MSG); } // If pre_visit returns false, then t children are not visited/rewritten. // This should be used with care, since it may produce incorrect results // when the rewriter is used to apply variable substitution. bool pre_visit(expr * t) { return m_cfg.pre_visit(t); } // Return true if the rewriting result of the given expression must be cached. bool must_cache(expr * t) const { if (cache_all_results()) return t != this->m_root && ((is_app(t) && to_app(t)->get_num_args() > 0) || is_quantifier(t)); if (cache_results()) return rewriter_core::must_cache(t); return false; } bool get_macro(func_decl * f, expr * & def, proof * & def_pr) { quantifier* q = nullptr; return m_cfg.get_macro(f, def, q, def_pr); } void push_frame(expr * t, bool mcache, unsigned max_depth) { check_max_frames(); push_frame_core(t, mcache, PROCESS_CHILDREN, max_depth); } void begin_scope() { check_max_scopes(); rewriter_core::begin_scope(); } template void process_var(var * v); template bool process_const(app * t); template bool visit(expr * t, unsigned max_depth); template void cache_result(expr * t, expr * new_t, proof * pr, bool c) { if (c) { if (!ProofGen) rewriter_core::cache_result(t, new_t); else rewriter_core::cache_result(t, new_t, pr); } } template void process_app(app * t, frame & fr); bool constant_fold(app* t, frame& fr); template void process_quantifier(quantifier * q, frame & fr); bool first_visit(frame & fr) const { return fr.m_state == PROCESS_CHILDREN && fr.m_i == 0; } bool not_rewriting() const; template void main_loop(expr * t, expr_ref & result, proof_ref & result_pr); template void resume_core(expr_ref & result, proof_ref & result_pr); public: rewriter_tpl(ast_manager & m, bool proof_gen, Config & cfg); ast_manager & m() const { return this->m_manager; } Config & cfg() { return m_cfg; } Config const & cfg() const { return m_cfg; } ~rewriter_tpl() override; void reset(); void cleanup(); void set_bindings(unsigned num_bindings, expr * const * bindings); void set_inv_bindings(unsigned num_bindings, expr * const * bindings); void update_binding_at(unsigned i, expr* binding); void update_inv_binding_at(unsigned i, expr* binding); void operator()(expr * t, expr_ref & result, proof_ref & result_pr); void operator()(expr * t, expr_ref & result) { operator()(t, result, m_pr); } expr_ref operator()(expr * n, unsigned num_bindings, expr * const * bindings) { expr_ref result(m()); SASSERT(!m_proof_gen); reset(); set_inv_bindings(num_bindings, bindings); operator()(n, result); return result; } void resume(expr_ref & result, proof_ref & result_pr); void resume(expr_ref & result) { resume(result, m_pr); } // Return the number of steps performed by the rewriter in the last call to operator(). unsigned get_num_steps() const { return m_num_steps; } }; struct default_rewriter_cfg { bool cache_all_results() const { return false; } bool cache_results() const { return true; } bool flat_assoc(func_decl * f) const { return false; } bool rewrite_patterns() const { return true; } bool max_scopes_exceeded(unsigned num_scopes) const { return false; } bool max_frames_exceeded(unsigned num_frames) const { return false; } bool max_steps_exceeded(unsigned num_steps) const { return false; } bool pre_visit(expr * t) { return true; } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { return BR_FAILED; } 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) { return false; } bool reduce_var(var * t, expr_ref & result, proof_ref & result_pr) { return false; } bool get_macro(func_decl * d, expr * & def, quantifier * & q, proof * & def_pr) { return false; } bool get_subst(expr * s, expr * & t, proof * & t_pr) { return false; } void reset() {} void cleanup() {} }; struct beta_reducer_cfg : public default_rewriter_cfg { bool pre_visit(expr * t) { return !is_ground(t); } }; class beta_reducer : public rewriter_tpl { beta_reducer_cfg m_cfg; public: beta_reducer(ast_manager & m): rewriter_tpl(m, false, m_cfg) {} }; #endif z3-z3-4.8.7/src/ast/rewriter/rewriter.txt000066400000000000000000000127051356505360400202610ustar00rootroot00000000000000 The following classes implement theory specific rewriting rules: - bool_rewriter - arith_rewriter - bv_rewriter - array_rewriter - datatype_rewriter - fpa_rewriter - seq_rewriter Each of them provide the method br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) where - f is expected to be a func_decl of the given theory - If the return value if different from BR_FAILED, the result is stored in result. The template rewriter_tpl can be instantiated to implement rewriters that traverse ASTs applying some kind of transformation/simplification. The parameter Cfg is expected to be a class with the following methods: bool cache_all_results() const; bool cache_results() const; bool flat_assoc(func_decl * f) const; bool rewrite_patterns() const; bool max_scopes_exceeded(unsigned num_scopes) const; bool max_frames_exceeded(unsigned num_frames) const; bool max_steps_exceeded(unsigned num_steps) const; bool pre_visit(expr * t); br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr); 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); bool get_macro(func_decl * d, expr * & def, quantifier * & q, proof * & def_pr); bool get_subst(expr * s, expr * & t, proof * & t_pr); void reset(); void cleanup(); The class default_rewriter_cfg provides a default implementation for these methods. The method reduce_app is the most important one. When implementing new rewriter, we usually redefine this method. We should return BR_FAILED, if we do not want to apply a "simplification" to (f args[0] ... args[num-1]) This is very important for performance reasons, since the rewriter tries to reuse AST when BR_FAILED is returned. If one wants to simply (f args[0] ... args[num-1]) to t, then use: result = t; return BR_DONE; Sometimes, by applying a simplification rule we may create new opportunites for rewriting. One can instruct the rewriter to revisit the result by returning: BR_REWRITE1, // rewrite the result (bounded by depth 1) BR_REWRITE2, // rewrite the result (bounded by depth 2) BR_REWRITE3, // rewrite the result (bounded by depth 3) BR_REWRITE_FULL, // rewrite the result unbounded Example: Suppose one wants to implement the rewriting rule (= (ite c t e) v) --> (ite c (= t v) (= e v)) This rule may create new opportunites for simplification since it creates the equalities (= t v) and (= e v). So, to force the rewriter to process these new equalities, BR_REWRITE2 should be returned. It is also correct (but unnecessary) to return BR_REWRITE3 and BR_REWRITE_FULL. To instantiate a new rewriter, one should 1) Define a new configuration class. class mycfg : public default_rewriter_cfg { ... } 2) Force template instantiation using the new cfg. templace class rewriter_tpl; 3) Define a subclass of rewriter_tpl that owns the new cfg. class myrewriter : public rewriter_tpl { mycfg m_cfg; public: myrewriter(ast_manager & m): rewriter_tpl(m, m.proofs_enabled(), m_cfg), m_cfg(...) { } }; The rewriter_tpl template supports proof production. It can be disabled even when proof productions is enabled in the ast_manager. Examples of rewriter_tpl instances: - th_rewriter.cpp - assertion_set_bit_blaster.cpp - model_evaluator.cpp - elim_distinct.cpp Notes for additional Cfg methods: - bool rewrite_patterns() const; If false is returned, then the rewriter will ignore patterns. - bool pre_visit(expr * t); If false is returned, then the rewriter will not process t. This is very useful, for example, for implementing rewriters that will only "process" the Boolean skeleton. - bool max_steps_exceeded(unsigned num_steps) const; If false, it interrupts the execution of the rewriter. The interruption is performed by throwing an exception. One can also used this callback to check whether the amount of memory used is still reasonable. - bool cache_all_results() const; By default, the rewriter only caches the results of non-atomic shared ASTs (get_ref_count() > 1). If true, is returned, the rewriter will cache all intermediate results. - bool flat_assoc(func_decl * f) const; If true is returned, then the rewriter will do flattening of non-shared nodes. For example, (+ a (+ b (+ c d))) will be treated as (+ a b c d) Shared nodes are not considered. Thus, exponential blowup in the rewriter stack is avoided. So, when implementing br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr); one should not assume that for an associative f, there is not f-application in args when true is returned by flat_assoc. when implementing br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr); If result_pr is set to 0, and proofs are enabled, then the rewriter will use a generic "rewrite" proof step for justifying that (f args[0] ... args[num-1]) is equal to result. The th_rewriter takes care of flattening. So, in principle, there is not need to return true in flat_assoc. z3-z3-4.8.7/src/ast/rewriter/rewriter_def.h000066400000000000000000000657011356505360400205130ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: rewriter_def.h Abstract: Lean and mean rewriter Author: Leonardo (leonardo) 2011-03-31 Notes: --*/ #include "ast/rewriter/rewriter.h" #include "ast/ast_smt2_pp.h" #include "ast/ast_ll_pp.h" template template void rewriter_tpl::process_var(var * v) { if (m_cfg.reduce_var(v, m_r, m_pr)) { result_stack().push_back(m_r); SASSERT(v->get_sort() == m().get_sort(m_r)); if (ProofGen) { result_pr_stack().push_back(m_pr); m_pr = nullptr; } set_new_child_flag(v); TRACE("rewriter", tout << mk_ismt2_pp(v, m()) << " -> " << m_r << "\n";); m_r = nullptr; return; } 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; 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()); tout << "index " << index << " bindings " << m_bindings.size() << "\n"; display_bindings(tout);); SASSERT(v->get_sort() == m().get_sort(r)); 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); TRACE("rewriter", tout << idx << " " << mk_ismt2_pp(r, m()) << "\n";); } set_new_child_flag(v); return; } } } result_stack().push_back(v); if (ProofGen) result_pr_stack().push_back(nullptr); // implicit reflexivity } template template bool rewriter_tpl::process_const(app * t0) { app_ref t(t0, m()); bool retried = false; retry: SASSERT(t->get_num_args() == 0); br_status st = m_cfg.reduce_app(t->get_decl(), 0, nullptr, m_r, m_pr); SASSERT(st != BR_DONE || m().get_sort(m_r) == m().get_sort(t)); switch (st) { case BR_FAILED: if (!retried) { result_stack().push_back(t); if (ProofGen) result_pr_stack().push_back(nullptr); // implicit reflexivity return true; } m_r = t; // fall through case BR_DONE: result_stack().push_back(m_r.get()); if (ProofGen) { if (m_pr) result_pr_stack().push_back(m_pr); else result_pr_stack().push_back(m().mk_rewrite(t0, m_r)); m_pr = nullptr; } m_r = nullptr; set_new_child_flag(t0); return true; default: if (is_app(m_r) && to_app(m_r)->get_num_args() == 0) { t = to_app(m_r); retried = true; goto retry; } return false; } } /** \brief visit expression t. Return true if t was rewritten and the result is on the top m_result_stack. We may skip t if: - pre_visit(t) returns false - max_depth == 0 - t is already in the cache Otherwise, return false and add a new frame stack for t with the updated max_depth. */ template template bool rewriter_tpl::visit(expr * t, unsigned max_depth) { // retry: TRACE("rewriter_visit", tout << "visiting\n" << mk_ismt2_pp(t, m()) << "\n";); expr * new_t = nullptr; proof * new_t_pr = nullptr; if (m_cfg.get_subst(t, new_t, new_t_pr)) { TRACE("rewriter_subst", tout << "subst\n" << mk_ismt2_pp(t, m()) << "\n---->\n" << mk_ismt2_pp(new_t, m()) << "\n";); SASSERT(m().get_sort(t) == m().get_sort(new_t)); result_stack().push_back(new_t); set_new_child_flag(t, new_t); if (ProofGen) result_pr_stack().push_back(new_t_pr); return true; } if (max_depth == 0) { result_stack().push_back(t); if (ProofGen) result_pr_stack().push_back(nullptr); // implicit reflexivity return true; // t is not going to be processed } SASSERT(max_depth > 0); SASSERT(max_depth <= RW_UNBOUNDED_DEPTH); bool c = must_cache(t); if (c) { #if 0 static unsigned checked_cache = 0; checked_cache ++; if (checked_cache % 100000 == 0) std::cerr << "[rewriter] num-cache-checks: " << checked_cache << std::endl; #endif expr * r = get_cached(t); if (r) { SASSERT(m().get_sort(r) == m().get_sort(t)); result_stack().push_back(r); set_new_child_flag(t, r); if (ProofGen) { proof * pr = get_cached_pr(t); result_pr_stack().push_back(pr); } return true; } } if (!pre_visit(t)) { result_stack().push_back(t); if (ProofGen) result_pr_stack().push_back(nullptr); // implicit reflexivity return true; // t is not going to be processed } switch (t->get_kind()) { case AST_APP: if (to_app(t)->get_num_args() == 0) { if (process_const(to_app(t))) return true; t = m_r; } if (max_depth != RW_UNBOUNDED_DEPTH) max_depth--; push_frame(t, c, max_depth); return false; case AST_VAR: process_var(to_var(t)); return true; case AST_QUANTIFIER: if (max_depth != RW_UNBOUNDED_DEPTH) max_depth--; push_frame(t, c, max_depth); return false; default: UNREACHABLE(); return true; } } template bool rewriter_tpl::constant_fold(app * t, frame & fr) { if (fr.m_i == 1 && m().is_ite(t)) { expr * cond = result_stack()[fr.m_spos].get(); expr* arg = nullptr; if (m().is_true(cond)) { arg = t->get_arg(1); } else if (m().is_false(cond)) { arg = t->get_arg(2); } if (arg) { result_stack().shrink(fr.m_spos); result_stack().push_back(arg); fr.m_state = REWRITE_BUILTIN; TRACE("rewriter_step", tout << "step\n" << mk_ismt2_pp(t, m()) << "\n";); if (visit(arg, fr.m_max_depth)) { m_r = result_stack().back(); result_stack().pop_back(); result_stack().pop_back(); result_stack().push_back(m_r); cache_result(t, m_r, m_pr, fr.m_cache_result); TRACE("rewriter_step", tout << "step 1\n" << mk_ismt2_pp(m_r, m()) << "\n";); frame_stack().pop_back(); set_new_child_flag(t); } m_r = nullptr; return true; } } return false; } template template void rewriter_tpl::process_app(app * t, frame & fr) { // SASSERT(t->get_num_args() > 0); SASSERT(!frame_stack().empty()); switch (fr.m_state) { case PROCESS_CHILDREN: { unsigned num_args = t->get_num_args(); while (fr.m_i < num_args) { if (!ProofGen && constant_fold(t, fr)) { return; } expr * arg = t->get_arg(fr.m_i); fr.m_i++; if (!visit(arg, fr.m_max_depth)) return; } func_decl * f = t->get_decl(); // If AC flattening is enabled, f is associative, t is not shared, and there is a previous frame on the stack. if (!ProofGen) { // this optimization is only used when Proof generation is disabled. if (f->is_associative() && t->get_ref_count() <= 1 && frame_stack().size() > 1) { frame & prev_fr = frame_stack()[frame_stack().size() - 2]; if (is_app(prev_fr.m_curr) && to_app(prev_fr.m_curr)->get_decl() == f && prev_fr.m_state == PROCESS_CHILDREN && flat_assoc(f)) { frame_stack().pop_back(); set_new_child_flag(t); return; } } } unsigned new_num_args = result_stack().size() - fr.m_spos; expr * const * new_args = result_stack().c_ptr() + fr.m_spos; app * new_t; if (ProofGen) { elim_reflex_prs(fr.m_spos); unsigned num_prs = result_pr_stack().size() - fr.m_spos; if (num_prs == 0) { new_t = t; m_pr = nullptr; } else { new_t = m().mk_app(f, new_num_args, new_args); m_pr = m().mk_congruence(t, new_t, num_prs, result_pr_stack().c_ptr() + fr.m_spos); } } br_status st = m_cfg.reduce_app(f, new_num_args, new_args, m_r, m_pr2); SASSERT(st != BR_DONE || m().get_sort(m_r) == m().get_sort(t)); CTRACE("reduce_app", st != BR_FAILED, tout << mk_bounded_pp(t, m()) << "\n"; tout << "st: " << st; if (m_r) tout << " --->\n" << mk_bounded_pp(m_r, m()); tout << "\n";); if (st != BR_FAILED) { result_stack().shrink(fr.m_spos); SASSERT(m().get_sort(m_r) == m().get_sort(t)); result_stack().push_back(m_r); if (ProofGen) { result_pr_stack().shrink(fr.m_spos); if (!m_pr2) m_pr2 = m().mk_rewrite(new_t, m_r); m_pr = m().mk_transitivity(m_pr, m_pr2); m_pr2 = nullptr; result_pr_stack().push_back(m_pr); } if (st == BR_DONE) { cache_result(t, m_r, m_pr, fr.m_cache_result); frame_stack().pop_back(); set_new_child_flag(t); m_r = nullptr; if (ProofGen) m_pr = nullptr; return; } else { fr.m_state = REWRITE_BUILTIN; SASSERT(st == BR_REWRITE1 || st == BR_REWRITE2 || st == BR_REWRITE3 || st == BR_REWRITE_FULL); unsigned max_depth = static_cast(st); SASSERT(0 <= max_depth && max_depth <= RW_UNBOUNDED_DEPTH); SASSERT((st == BR_REWRITE1) == (max_depth == 0)); SASSERT((st == BR_REWRITE2) == (max_depth == 1)); SASSERT((st == BR_REWRITE3) == (max_depth == 2)); SASSERT((st == BR_REWRITE_FULL) == (max_depth == RW_UNBOUNDED_DEPTH)); if (max_depth != RW_UNBOUNDED_DEPTH) max_depth++; if (visit(m_r, max_depth)) { if (ProofGen) { proof_ref pr2(m()), pr1(m()); pr2 = result_pr_stack().back(); result_pr_stack().pop_back(); pr1 = result_pr_stack().back(); result_pr_stack().pop_back(); m_pr = m().mk_transitivity(pr1, pr2); result_pr_stack().push_back(m_pr); } m_r = result_stack().back(); result_stack().pop_back(); result_stack().pop_back(); result_stack().push_back(m_r); cache_result(t, m_r, m_pr, fr.m_cache_result); frame_stack().pop_back(); set_new_child_flag(t); m_r = nullptr; if (ProofGen) m_pr = nullptr; return; } else { // frame was created for processing m_r m_r = nullptr; if (ProofGen) m_pr = nullptr; return; } } UNREACHABLE(); } // TODO: add rewrite rules support expr * def = nullptr; proof * def_pr = nullptr; // When get_macro succeeds, then // we know that: // forall X. f(X) = def[X] // and def_pr is a proof for this quantifier. // if (get_macro(f, def, def_pr)) { SASSERT(!f->is_associative() || !flat_assoc(f)); SASSERT(new_num_args == t->get_num_args()); SASSERT(m().get_sort(def) == m().get_sort(t)); if (is_ground(def)) { m_r = def; if (ProofGen) { m_pr = m().mk_transitivity(m_pr, def_pr); } } else { if (ProofGen) { NOT_IMPLEMENTED_YET(); // We do not support the use of bindings in proof generation mode. // Thus we have to apply the substitution here, and // beta_reducer subst(m()); // subst.set_bindings(new_num_args, new_args); // expr_ref r2(m()); // subst(def, r2); } else { fr.m_state = EXPAND_DEF; TRACE("get_macro", tout << "f: " << f->get_name() << ", def: \n" << mk_ismt2_pp(def, m()) << "\n"; tout << "Args num: " << num_args << "\n"; for (unsigned i = 0; i < num_args; i++) tout << mk_ismt2_pp(new_args[i], m()) << "\n";); unsigned sz = m_bindings.size(); unsigned i = num_args; while (i > 0) { --i; m_bindings.push_back(new_args[i]); // num_args - i - 1]); m_shifts.push_back(sz); } result_stack().push_back(def); TRACE("get_macro", display_bindings(tout);); begin_scope(); m_num_qvars += num_args; m_root = def; push_frame(def, false, RW_UNBOUNDED_DEPTH); return; } } } else { if (ProofGen) { m_r = new_t; } else { if (fr.m_new_child) { m_r = m().mk_app(f, new_num_args, new_args); } else { TRACE("rewriter_reuse", tout << "reusing:\n" << mk_ismt2_pp(t, m()) << "\n";); m_r = t; } } } result_stack().shrink(fr.m_spos); result_stack().push_back(m_r); cache_result(t, m_r, m_pr, fr.m_cache_result); if (ProofGen) { result_pr_stack().shrink(fr.m_spos); result_pr_stack().push_back(m_pr); m_pr = nullptr; } frame_stack().pop_back(); set_new_child_flag(t, m_r); m_r = nullptr; return; } case REWRITE_BUILTIN: SASSERT(fr.m_spos + 2 == result_stack().size()); if (ProofGen) { proof_ref pr2(m()), pr1(m()); pr2 = result_pr_stack().back(); result_pr_stack().pop_back(); pr1 = result_pr_stack().back(); result_pr_stack().pop_back(); m_pr = m().mk_transitivity(pr1, pr2); result_pr_stack().push_back(m_pr); } m_r = result_stack().back(); result_stack().pop_back(); result_stack().pop_back(); result_stack().push_back(m_r); cache_result(t, m_r, m_pr, fr.m_cache_result); frame_stack().pop_back(); set_new_child_flag(t); return; case EXPAND_DEF: SASSERT(fr.m_spos + t->get_num_args() + 2 == result_stack().size()); SASSERT(t->get_num_args() <= m_bindings.size()); if (!ProofGen) { expr_ref tmp(m()); unsigned num_args = t->get_num_args(); m_bindings.shrink(m_bindings.size() - num_args); m_shifts.shrink(m_shifts.size() - num_args); m_num_qvars -= num_args; end_scope(); m_r = result_stack().back(); if (!is_ground(m_r)) { m_inv_shifter(m_r, num_args, tmp); m_r = std::move(tmp); } result_stack().shrink(fr.m_spos); result_stack().push_back(m_r); cache_result(t, m_r, m_pr, fr.m_cache_result); frame_stack().pop_back(); set_new_child_flag(t); } else { // TODO NOT_IMPLEMENTED_YET(); } return; case REWRITE_RULE: // support for rewriting rules was not implemented yet. NOT_IMPLEMENTED_YET(); break; default: UNREACHABLE(); break; } } template template void rewriter_tpl::process_quantifier(quantifier * q, frame & fr) { SASSERT(fr.m_state == PROCESS_CHILDREN); unsigned num_decls = q->get_num_decls(); if (fr.m_i == 0) { if (!ProofGen) { begin_scope(); m_root = q->get_expr(); unsigned sz = m_bindings.size(); for (unsigned i = 0; i < num_decls; i++) { m_bindings.push_back(nullptr); m_shifts.push_back(sz); } } m_num_qvars += num_decls; } unsigned num_children = rewrite_patterns() ? q->get_num_children() : 1; while (fr.m_i < num_children) { expr * child = q->get_child(fr.m_i); fr.m_i++; if (!visit(child, fr.m_max_depth)) { return; } } SASSERT(fr.m_spos + num_children == result_stack().size()); expr * const * it = result_stack().c_ptr() + fr.m_spos; expr * new_body = *it; unsigned num_pats = q->get_num_patterns(); unsigned num_no_pats = q->get_num_no_patterns(); expr_ref_vector new_pats(m_manager, num_pats, q->get_patterns()); expr_ref_vector new_no_pats(m_manager, num_no_pats, q->get_no_patterns()); if (rewrite_patterns()) { TRACE("reduce_quantifier_bug", tout << "rewrite patterns\n";); expr * const * np = it + 1; expr * const * nnp = np + num_pats; for (unsigned i = 0; i < num_pats; i++) if (m_manager.is_pattern(np[i])) new_pats[i] = np[i]; for (unsigned i = 0; i < num_no_pats; i++) if (m_manager.is_pattern(nnp[i])) new_no_pats[i] = nnp[i]; } 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 = 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)) { m_pr = m().mk_transitivity(m_pr, pr2); } TRACE("reduce_quantifier_bug", tout << "m_pr is_null: " << (m_pr.get() == 0) << "\n"; if (m_pr) tout << mk_ismt2_pp(m_pr, m()) << "\n";); result_pr_stack().shrink(fr.m_spos); result_pr_stack().push_back(m_pr); } else { expr_ref tmp(m()); TRACE("reduce_quantifier_bug", tout << mk_ismt2_pp(q, m()) << " " << mk_ismt2_pp(new_body, m()) << "\n";); if (!m_cfg.reduce_quantifier(q, new_body, new_pats.c_ptr(), new_no_pats.c_ptr(), m_r, m_pr)) { if (fr.m_new_child) { m_r = m().update_quantifier(q, num_pats, new_pats.c_ptr(), num_no_pats, new_no_pats.c_ptr(), new_body); } else { TRACE("rewriter_reuse", tout << "reusing:\n" << mk_ismt2_pp(q, m()) << "\n";); m_r = q; } } } result_stack().shrink(fr.m_spos); result_stack().push_back(m_r.get()); SASSERT(m().get_sort(q) == m().get_sort(m_r)); if (!ProofGen) { SASSERT(num_decls <= m_bindings.size()); m_bindings.shrink(m_bindings.size() - num_decls); m_shifts.shrink(m_shifts.size() - num_decls); end_scope(); cache_result(q, m_r, m_pr, fr.m_cache_result); } else { cache_result(q, m_r, m_pr, fr.m_cache_result); m_pr = nullptr; } m_r = nullptr; frame_stack().pop_back(); set_new_child_flag(q, m_r); } template bool rewriter_tpl::not_rewriting() const { SASSERT(frame_stack().empty()); SASSERT(m_cache == m_cache_stack[0]); return true; } template rewriter_tpl::rewriter_tpl(ast_manager & m, bool proof_gen, Config & cfg): rewriter_core(m, proof_gen), m_cfg(cfg), m_num_steps(0), m_shifter(m), m_inv_shifter(m), m_r(m), m_pr(m), m_pr2(m) { } template rewriter_tpl::~rewriter_tpl() { } template void rewriter_tpl::reset() { m_cfg.reset(); rewriter_core::reset(); m_bindings.reset(); m_shifts.reset(); m_shifter.reset(); m_inv_shifter.reset(); } template void rewriter_tpl::cleanup() { m_cfg.cleanup(); rewriter_core::cleanup(); m_bindings.finalize(); m_shifter.cleanup(); m_shifts.finalize(); m_inv_shifter.cleanup(); } template void rewriter_tpl::display_bindings(std::ostream& out) { out << "bindings:\n"; for (unsigned i = 0; i < m_bindings.size(); i++) { if (m_bindings[i]) out << i << ": " << mk_ismt2_pp(m_bindings[i], m()) << "\n"; } } template void rewriter_tpl::set_bindings(unsigned num_bindings, expr * const * bindings) { SASSERT(!m_proof_gen); SASSERT(not_rewriting()); m_bindings.reset(); m_shifts.reset(); unsigned i = num_bindings; while (i > 0) { --i; m_bindings.push_back(bindings[i]); m_shifts.push_back(num_bindings); } TRACE("rewriter", display_bindings(tout);); } template void rewriter_tpl::set_inv_bindings(unsigned num_bindings, expr * const * bindings) { SASSERT(!m_proof_gen); SASSERT(not_rewriting()); m_bindings.reset(); m_shifts.reset(); for (unsigned i = 0; i < num_bindings; i++) { m_bindings.push_back(bindings[i]); m_shifts.push_back(num_bindings); } TRACE("rewriter", display_bindings(tout);); } template void rewriter_tpl::update_inv_binding_at(unsigned i, expr* binding) { m_bindings[i] = binding; } template void rewriter_tpl::update_binding_at(unsigned i, expr* binding) { m_bindings[m_bindings.size() - i - 1] = binding; } template template void rewriter_tpl::main_loop(expr * t, expr_ref & result, proof_ref & result_pr) { if (m_cancel_check && m().canceled()) { reset(); throw rewriter_exception(m().limit().get_cancel_msg()); } SASSERT(!ProofGen || result_stack().size() == result_pr_stack().size()); SASSERT(not_rewriting()); m_root = t; m_num_qvars = 0; m_num_steps = 0; if (visit(t, RW_UNBOUNDED_DEPTH)) { result = result_stack().back(); result_stack().pop_back(); SASSERT(result_stack().empty()); if (ProofGen) { result_pr = result_pr_stack().back(); result_pr_stack().pop_back(); if (result_pr.get() == nullptr) result_pr = m().mk_reflexivity(t); SASSERT(result_pr_stack().empty()); } } else { resume_core(result, result_pr); } TRACE("rewriter", tout << mk_ismt2_pp(t, m()) << "\n=>\n" << result << "\n";;); } /** \brief Resume the execution when it was interrupted by cancel() or check_max_steps(). */ template template void rewriter_tpl::resume_core(expr_ref & result, proof_ref & result_pr) { SASSERT(!frame_stack().empty()); while (!frame_stack().empty()) { if (m_cancel_check && m().canceled()) { reset(); throw rewriter_exception(m().limit().get_cancel_msg()); } SASSERT(!ProofGen || result_stack().size() == result_pr_stack().size()); frame & fr = frame_stack().back(); expr * t = fr.m_curr; TRACE("rewriter_step", tout << "step\n" << mk_ismt2_pp(t, m()) << "\n";); m_num_steps++; check_max_steps(); if (first_visit(fr) && fr.m_cache_result) { expr * r = get_cached(t); if (r) { SASSERT(m().get_sort(r) == m().get_sort(t)); result_stack().push_back(r); if (ProofGen) { proof * pr = get_cached_pr(t); result_pr_stack().push_back(pr); } frame_stack().pop_back(); set_new_child_flag(t, r); continue; } } switch (t->get_kind()) { case AST_APP: process_app(to_app(t), fr); break; case AST_QUANTIFIER: process_quantifier(to_quantifier(t), fr); break; case AST_VAR: frame_stack().pop_back(); process_var(to_var(t)); break; default: UNREACHABLE(); break; } } result = result_stack().back(); result_stack().pop_back(); SASSERT(result_stack().empty()); if (ProofGen) { result_pr = result_pr_stack().back(); result_pr_stack().pop_back(); if (result_pr.get() == nullptr) result_pr = m().mk_reflexivity(m_root); SASSERT(result_pr_stack().empty()); } } template void rewriter_tpl::operator()(expr * t, expr_ref & result, proof_ref & result_pr) { if (m_proof_gen) main_loop(t, result, result_pr); else main_loop(t, result, result_pr); } template void rewriter_tpl::resume(expr_ref & result, proof_ref & result_pr) { if (m_proof_gen) resume_core(result, result_pr); else resume_core(result, result_pr); } z3-z3-4.8.7/src/ast/rewriter/rewriter_params.pyg000066400000000000000000000022361356505360400216020ustar00rootroot00000000000000def_module_params('rewriter', description='new formula simplification module used in the tactic framework, and new solvers', export=True, params=(max_memory_param(), max_steps_param(), ("flat", BOOL, True, "create nary applications for and,or,+,*,bvadd,bvmul,bvand,bvor,bvxor"), ("push_ite_arith", BOOL, False, "push if-then-else over arithmetic terms."), ("push_ite_bv", BOOL, False, "push if-then-else over bit-vector terms."), ("pull_cheap_ite", BOOL, False, "pull if-then-else terms when cheap."), ("bv_ineq_consistency_test_max", UINT, 0, "max size of conjunctions on which to perform consistency test based on inequalities on bitvectors."), ("cache_all", BOOL, False, "cache all intermediate results."), ("rewrite_patterns", BOOL, False, "rewrite patterns."), ("ignore_patterns_on_ground_qbody", BOOL, True, "ignores patterns on quantifiers that don't mention their bound variables."))) z3-z3-4.8.7/src/ast/rewriter/rewriter_types.h000066400000000000000000000025071356505360400211140ustar00rootroot00000000000000/*++ Copyright (c) 2007 Microsoft Corporation Module Name: rewriter_types.h Abstract: Lean and mean rewriter Author: Leonardo (leonardo) 2011-04-10 Notes: --*/ #ifndef REWRITER_TYPES_H_ #define REWRITER_TYPES_H_ #include "util/z3_exception.h" #include "util/common_msgs.h" /** \brief Builtin rewrite result status */ enum br_status { BR_REWRITE1, // rewrite the result (bounded by depth 1) BR_REWRITE2, // rewrite the result (bounded by depth 2) BR_REWRITE3, // rewrite the result (bounded by depth 3) BR_REWRITE_FULL, // rewrite the result unbounded BR_DONE, // done, the result is simplified BR_FAILED // no builtin rewrite is available }; #define RW_UNBOUNDED_DEPTH 3 inline br_status unsigned2br_status(unsigned u) { br_status r = u >= RW_UNBOUNDED_DEPTH ? BR_REWRITE_FULL : static_cast(u); SASSERT((u == 0) == (r == BR_REWRITE1)); SASSERT((u == 1) == (r == BR_REWRITE2)); SASSERT((u == 2) == (r == BR_REWRITE3)); SASSERT((u >= 3) == (r == BR_REWRITE_FULL)); return r; } class rewriter_exception : public default_exception { public: rewriter_exception(std::string && msg) : default_exception(std::move(msg)) {} }; #endif z3-z3-4.8.7/src/ast/rewriter/seq_rewriter.cpp000066400000000000000000002317051356505360400210770ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: seq_rewriter.cpp Abstract: Basic rewriting rules for sequences constraints. Author: Nikolaj Bjorner (nbjorner) 2015-12-5 Murphy Berzish 2017-02-21 Notes: --*/ #include "util/uint_set.h" #include "ast/rewriter/seq_rewriter.h" #include "ast/arith_decl_plugin.h" #include "ast/ast_pp.h" #include "ast/ast_ll_pp.h" #include "ast/ast_util.h" #include "ast/well_sorted.h" #include "ast/rewriter/var_subst.h" #include "ast/rewriter/bool_rewriter.h" #include "math/automata/automaton.h" #include "math/automata/symbolic_automata_def.h" 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: 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: 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(u.mk_le(m_t, e), u.mk_le(e, m_s)); } break; } return result; } std::ostream& sym_expr::display(std::ostream& out) const { switch (m_ty) { 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"; } struct display_expr1 { ast_manager& m; display_expr1(ast_manager& m): m(m) {} std::ostream& display(std::ostream& out, sym_expr* e) const { return e->display(out); } }; 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_var(m) {} T mk_false() override { expr_ref fml(m.mk_false(), m); return sym_expr::mk_pred(fml, m.mk_bool_sort()); // use of Bool sort for bound variable is arbitrary } T mk_true() override { expr_ref fml(m.mk_true(), m); 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; } if (m.are_distinct(x->get_char(), y->get_char())) { expr_ref fml(m.mk_false(), m); 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); expr_ref fml1 = x->accept(v); expr_ref fml2 = y->accept(v); if (m.is_true(fml1)) { return y; } 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()) { return x; } if (x == y) return x; var_ref v(m.mk_var(0, x->get_sort()), m); expr_ref fml1 = x->accept(v); expr_ref fml2 = y->accept(v); if (m.is_false(fml1)) return y; if (m.is_false(fml2)) return x; bool_rewriter br(m); expr_ref fml(m); br.mk_or(fml1, fml2, fml); return sym_expr::mk_pred(fml, x->get_sort()); } T mk_and(unsigned sz, T const* ts) override { switch (sz) { case 0: return mk_true(); case 1: return ts[0]; default: { T t = ts[0]; for (unsigned i = 1; i < sz; ++i) { t = mk_and(t, ts[i]); } return t; } } } T mk_or(unsigned sz, T const* ts) override { switch (sz) { case 0: return mk_false(); case 1: return ts[0]; default: { T t = ts[0]; for (unsigned i = 1; i < sz; ++i) { t = mk_or(t, ts[i]); } return t; } } } lbool is_sat(T x) override { unsigned lo, hi; seq_util u(m); if (x->is_char()) { return l_true; } 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; } 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; } if (m.is_false(fml)) { return l_false; } return m_solver.check_sat(fml); } T mk_not(T x) override { return sym_expr::mk_not(m, x); } }; re2automaton::re2automaton(ast_manager& m): m(m), u(m), m_ba(nullptr), m_sa(nullptr) {} re2automaton::~re2automaton() {} void re2automaton::set_solver(expr_solver* solver) { m_solver = solver; m_ba = alloc(sym_expr_boolean_algebra, m, *solver); m_sa = alloc(symbolic_automata_t, sm, *m_ba.get()); } eautomaton* re2automaton::mk_product(eautomaton* a1, eautomaton* a2) { return m_sa->mk_product(*a1, *a2); } eautomaton* re2automaton::operator()(expr* e) { eautomaton* r = re2aut(e); if (r) { r->compress(); bool_rewriter br(m); TRACE("seq", display_expr1 disp(m); r->display(tout << mk_pp(e, m) << " -->\n", disp);); } return r; } eautomaton* re2automaton::re2aut(expr* e) { SASSERT(u.is_re(e)); expr *e0, *e1, *e2; scoped_ptr a, b; unsigned lo, hi; zstring s1, s2; if (u.re.is_to_re(e, e1)) { return seq2aut(e1); } else if (u.re.is_concat(e, e1, e2) && (a = re2aut(e1)) && (b = re2aut(e2))) { return eautomaton::mk_concat(*a, *b); } else if (u.re.is_union(e, e1, e2) && (a = re2aut(e1)) && (b = re2aut(e2))) { return eautomaton::mk_union(*a, *b); } else if (u.re.is_star(e, e1) && (a = re2aut(e1))) { a->add_final_to_init_moves(); a->add_init_to_final_states(); return a.detach(); } else if (u.re.is_plus(e, e1) && (a = re2aut(e1))) { a->add_final_to_init_moves(); return a.detach(); } else if (u.re.is_opt(e, e1) && (a = re2aut(e1))) { a = eautomaton::mk_opt(*a); return a.detach(); } else if (u.re.is_range(e, e1, e2)) { if (u.str.is_string(e1, s1) && u.str.is_string(e2, s2) && s1.length() == 1 && s2.length() == 1) { unsigned start = s1[0]; unsigned stop = s2[0]; 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(); } else { TRACE("seq", tout << "Range expression is not handled: " << mk_pp(e, m) << "\n";); } } else if (u.re.is_complement(e, e0) && (a = re2aut(e0)) && m_sa) { return m_sa->mk_complement(*a); } else if (u.re.is_loop(e, e1, lo, hi) && (a = re2aut(e1))) { scoped_ptr eps = eautomaton::mk_epsilon(sm); b = eautomaton::mk_epsilon(sm); while (hi > lo) { scoped_ptr c = eautomaton::mk_concat(*a, *b); b = eautomaton::mk_union(*eps, *c); --hi; } while (lo > 0) { b = eautomaton::mk_concat(*a, *b); --lo; } return b.detach(); } else if (u.re.is_loop(e, e1, lo) && (a = re2aut(e1))) { b = eautomaton::clone(*a); b->add_final_to_init_moves(); b->add_init_to_final_states(); while (lo > 0) { b = eautomaton::mk_concat(*a, *b); --lo; } return b.detach(); } else if (u.re.is_empty(e)) { return alloc(eautomaton, sm); } else if (u.re.is_full_seq(e)) { expr_ref tt(m.mk_true(), m); sort *seq_s = nullptr, *char_s = nullptr; VERIFY (u.is_re(m.get_sort(e), seq_s)); VERIFY (u.is_seq(seq_s, char_s)); sym_expr* _true = sym_expr::mk_pred(tt, char_s); return eautomaton::mk_loop(sm, _true); } else if (u.re.is_full_char(e)) { expr_ref tt(m.mk_true(), m); sort *seq_s = nullptr, *char_s = nullptr; VERIFY (u.is_re(m.get_sort(e), seq_s)); VERIFY (u.is_seq(seq_s, char_s)); sym_expr* _true = sym_expr::mk_pred(tt, char_s); a = alloc(eautomaton, sm, _true); return a.detach(); } else if (u.re.is_intersection(e, e1, e2) && m_sa && (a = re2aut(e1)) && (b = re2aut(e2))) { eautomaton* r = m_sa->mk_product(*a, *b); TRACE("seq", display_expr1 disp(m); a->display(tout << "a:", disp); b->display(tout << "b:", disp); r->display(tout << "intersection:", disp);); return r; } else { TRACE("seq", tout << "not handled " << mk_pp(e, m) << "\n";); } return nullptr; } eautomaton* re2automaton::seq2aut(expr* e) { SASSERT(u.is_seq(e)); zstring s; expr* e1, *e2; scoped_ptr a, b; if (u.str.is_concat(e, e1, e2) && (a = seq2aut(e1)) && (b = seq2aut(e2))) { return eautomaton::mk_concat(*a, *b); } else if (u.str.is_unit(e, e1)) { return alloc(eautomaton, sm, sym_expr::mk_char(m, e1)); } else if (u.str.is_empty(e)) { return eautomaton::mk_epsilon(sm); } else if (u.str.is_string(e, s)) { unsigned init = 0; eautomaton::moves mvs; unsigned_vector final; final.push_back(s.length()); for (unsigned k = 0; k < s.length(); ++k) { // reference count? mvs.push_back(eautomaton::move(sm, k, k+1, sym_expr::mk_char(m, u.str.mk_char(s, k)))); } return alloc(eautomaton, sm, init, final, mvs); } return nullptr; } 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); st = mk_seq_unit(args[0], result); break; case OP_SEQ_EMPTY: return BR_FAILED; case OP_RE_PLUS: SASSERT(num_args == 1); st = mk_re_plus(args[0], result); break; case OP_RE_STAR: SASSERT(num_args == 1); st = mk_re_star(args[0], result); break; case OP_RE_OPTION: SASSERT(num_args == 1); st = mk_re_opt(args[0], result); break; case OP_RE_CONCAT: if (num_args == 1) { result = args[0]; st = BR_DONE; } else { SASSERT(num_args == 2); st = mk_re_concat(args[0], args[1], result); } break; case OP_RE_UNION: if (num_args == 1) { result = args[0]; st = BR_DONE; } else { SASSERT(num_args == 2); st = mk_re_union(args[0], args[1], result); } break; case OP_RE_RANGE: SASSERT(num_args == 2); st = mk_re_range(args[0], args[1], result); break; case OP_RE_INTERSECT: SASSERT(num_args == 2); st = mk_re_inter(args[0], args[1], result); break; case OP_RE_COMPLEMENT: SASSERT(num_args == 1); st = mk_re_complement(args[0], result); break; case OP_RE_LOOP: st = mk_re_loop(f, num_args, args, result); break; case OP_RE_EMPTY_SET: return BR_FAILED; case OP_RE_FULL_SEQ_SET: return BR_FAILED; case OP_RE_FULL_CHAR_SET: return BR_FAILED; case OP_RE_OF_PRED: return BR_FAILED; case _OP_SEQ_SKOLEM: return BR_FAILED; case OP_SEQ_CONCAT: if (num_args == 1) { result = args[0]; st = BR_DONE; } else { SASSERT(num_args == 2); st = mk_seq_concat(args[0], args[1], result); } break; case OP_SEQ_LENGTH: SASSERT(num_args == 1); st = mk_seq_length(args[0], result); break; case OP_SEQ_EXTRACT: SASSERT(num_args == 3); st = mk_seq_extract(args[0], args[1], args[2], result); break; case OP_SEQ_CONTAINS: SASSERT(num_args == 2); st = mk_seq_contains(args[0], args[1], result); break; case OP_SEQ_AT: SASSERT(num_args == 2); st = mk_seq_at(args[0], args[1], result); break; case OP_SEQ_NTH: SASSERT(num_args == 2); return mk_seq_nth(args[0], args[1], result); case OP_SEQ_NTH_I: SASSERT(num_args == 2); return mk_seq_nth_i(args[0], args[1], result); case OP_SEQ_PREFIX: SASSERT(num_args == 2); st = mk_seq_prefix(args[0], args[1], result); break; case OP_SEQ_SUFFIX: SASSERT(num_args == 2); st = mk_seq_suffix(args[0], args[1], result); break; case OP_SEQ_INDEX: if (num_args == 2) { expr_ref arg3(m_autil.mk_int(0), m()); result = m_util.str.mk_index(args[0], args[1], arg3); st = BR_REWRITE1; } else { SASSERT(num_args == 3); st = mk_seq_index(args[0], args[1], args[2], result); } break; case OP_SEQ_LAST_INDEX: SASSERT(num_args == 2); st = mk_seq_last_index(args[0], args[1], result); break; case OP_SEQ_REPLACE: SASSERT(num_args == 3); st = mk_seq_replace(args[0], args[1], args[2], result); break; case OP_SEQ_TO_RE: SASSERT(num_args == 1); st = mk_str_to_regexp(args[0], result); break; case OP_SEQ_IN_RE: SASSERT(num_args == 2); st = mk_str_in_regexp(args[0], args[1], result); break; case OP_STRING_LE: SASSERT(num_args == 2); st = mk_str_le(args[0], args[1], result); break; case OP_STRING_CONST: return BR_FAILED; case OP_STRING_ITOS: SASSERT(num_args == 1); st = mk_str_itos(args[0], result); break; case OP_STRING_STOI: SASSERT(num_args == 1); st = mk_str_stoi(args[0], result); break; case _OP_STRING_CONCAT: case _OP_STRING_PREFIX: case _OP_STRING_SUFFIX: case _OP_STRING_STRCTN: case _OP_STRING_LENGTH: case _OP_STRING_CHARAT: case _OP_STRING_IN_REGEXP: case _OP_STRING_TO_REGEXP: case _OP_STRING_SUBSTR: case _OP_STRING_STRREPL: case _OP_STRING_STRIDOF: UNREACHABLE(); } CTRACE("seq_verbose", st != BR_FAILED, tout << f->get_name() << " " << result << "\n";); return st; } /* * (seq.unit (_ BitVector 8)) ==> String constant */ br_status seq_rewriter::mk_seq_unit(expr* e, expr_ref& result) { unsigned ch; // specifically we want (_ BitVector 8) if (m_util.is_const_char(e, ch)) { // convert to string constant 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; } return BR_FAILED; } /* string + string = string a + (b + c) = (a + b) + c a + "" = a "" + a = a (a + string) + string = a + string */ br_status seq_rewriter::mk_seq_concat(expr* a, expr* b, expr_ref& result) { zstring s1, s2; expr* c, *d; bool isc1 = m_util.str.is_string(a, s1); bool isc2 = m_util.str.is_string(b, s2); if (isc1 && isc2) { result = m_util.str.mk_string(s1 + s2); return BR_DONE; } if (m_util.str.is_concat(a, c, d)) { result = m_util.str.mk_concat(c, m_util.str.mk_concat(d, b)); return BR_REWRITE2; } if (m_util.str.is_empty(a)) { result = b; return BR_DONE; } if (m_util.str.is_empty(b)) { result = a; return BR_DONE; } // TBD concatenation is right-associative if (isc2 && m_util.str.is_concat(a, c, d) && m_util.str.is_string(d, s1)) { result = m_util.str.mk_concat(c, m_util.str.mk_string(s1 + s2)); return BR_DONE; } if (isc1 && m_util.str.is_concat(b, c, d) && m_util.str.is_string(c, s2)) { result = m_util.str.mk_concat(m_util.str.mk_string(s1 + s2), d); return BR_DONE; } return BR_FAILED; } br_status seq_rewriter::mk_seq_length(expr* a, expr_ref& result) { zstring b; m_es.reset(); m_util.str.get_concat(a, m_es); unsigned len = 0; unsigned j = 0; for (unsigned i = 0; i < m_es.size(); ++i) { if (m_util.str.is_string(m_es[i].get(), b)) { len += b.length(); } else if (m_util.str.is_unit(m_es[i].get())) { len += 1; } else if (m_util.str.is_empty(m_es[i].get())) { // skip } else { m_es[j] = m_es[i].get(); ++j; } } if (j == 0) { result = m_autil.mk_numeral(rational(len, rational::ui64()), true); return BR_DONE; } if (j != m_es.size() || j != 1) { expr_ref_vector es(m()); for (unsigned i = 0; i < j; ++i) { es.push_back(m_util.str.mk_length(m_es[i].get())); } if (len != 0) { es.push_back(m_autil.mk_numeral(rational(len, rational::ui64()), true)); } result = m_autil.mk_add(es.size(), es.c_ptr()); return BR_REWRITE2; } expr* c = nullptr, *t = nullptr, *e = nullptr; if (m().is_ite(a, c, t, e) && (t->get_ref_count() == 1 || e->get_ref_count() == 1)) { result = m().mk_ite(c, m_util.str.mk_length(t), m_util.str.mk_length(e)); return BR_REWRITE3; } #if 0 expr* s = nullptr, *offs = nullptr, *l = nullptr; // len(extract(s, offs, len(s) - offs)) = max(0, len(s) - offs) // if (m_util.str.is_extract(a, s, offs, l) && is_suffix(s, offs, l)) { expr_ref zero(m_autil.mk_int(0), m()); result = m_autil.mk_sub(m_util.str.mk_length(s), offs); result = m().mk_ite(m_autil.mk_ge(result, zero), result, zero); return BR_REWRITE_FULL; } #endif return BR_FAILED; } bool seq_rewriter::is_suffix(expr* s, expr* offset, expr* len) { expr_ref_vector lens(m()); rational a, b; return get_lengths(len, lens, a) && (a.neg(), m_autil.is_numeral(offset, b) && b.is_pos() && a == b); } br_status seq_rewriter::mk_seq_extract(expr* a, expr* b, expr* c, expr_ref& result) { zstring s; rational pos, len; TRACE("seq_verbose", tout << mk_pp(a, m()) << " " << mk_pp(b, m()) << " " << mk_pp(c, m()) << "\n";); bool constantBase = m_util.str.is_string(a, s); bool constantPos = m_autil.is_numeral(b, pos); bool constantLen = m_autil.is_numeral(c, len); bool lengthPos = m_util.str.is_length(b) || m_autil.is_add(b); // case 1: pos<0 or len<=0 // rewrite to "" if ( (constantPos && pos.is_neg()) || (constantLen && !len.is_pos()) ) { result = m_util.str.mk_empty(m().get_sort(a)); return BR_DONE; } // case 1.1: pos >= length(base) // rewrite to "" if (constantPos && constantBase && pos >= rational(s.length())) { result = m_util.str.mk_empty(m().get_sort(a)); return BR_DONE; } constantPos &= pos.is_unsigned(); constantLen &= len.is_unsigned(); if (constantPos && constantLen && constantBase) { unsigned _pos = pos.get_unsigned(); unsigned _len = len.get_unsigned(); 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; } 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; } // extract(a + b + c, len(a + b), s) -> extract(c, 0, s) // extract(a + b + c, len(a) + len(b), s) -> extract(c, 0, s) if (lengthPos) { m_lhs.reset(); expr_ref_vector lens(m()); m_util.str.get_concat(a, m_lhs); TRACE("seq", tout << m_lhs << " " << pos << " " << lens << "\n";); if (!get_lengths(b, lens, pos) || pos.is_neg()) { return BR_FAILED; } unsigned i = 0; for (; i < m_lhs.size(); ++i) { expr* lhs = m_lhs.get(i); if (lens.contains(lhs)) { lens.erase(lhs); } else if (m_util.str.is_unit(lhs) && pos.is_pos()) { pos -= rational(1); } else { break; } } if (i == 0) return BR_FAILED; expr_ref t1(m()), t2(m()); if (m_lhs.size() == i) { t1 = m_util.str.mk_empty(m().get_sort(a)); } else { t1 = m_util.str.mk_concat(m_lhs.size() - i, m_lhs.c_ptr() + i); } t2 = m_autil.mk_int(pos); for (expr* rhs : lens) { t2 = m_autil.mk_add(t2, m_util.str.mk_length(rhs)); } result = m_util.str.mk_substr(t1, t2, c); TRACE("seq", tout << result << "\n";); return BR_REWRITE2; } 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)) { m_lhs.reset(); m_util.str.get_concat(a, m_lhs); if (!m_lhs.empty() && m_lhs.get(0) == a2) { result = a2; return BR_DONE; } } expr* a1 = nullptr, *b1 = nullptr, *c1 = nullptr; if (m_util.str.is_extract(a, a1, b1, c1) && is_suffix(a1, b1, c1) && is_suffix(a, b, c)) { result = m_util.str.mk_substr(a1, m_autil.mk_add(b1, b), m_autil.mk_sub(c1, b)); return BR_REWRITE3; } 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 (i == as.size()) { result = m_util.str.mk_concat(as.size() - offset, 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::get_lengths(expr* e, expr_ref_vector& lens, rational& pos) { expr* arg = nullptr; rational pos1; if (m_autil.is_add(e)) { for (expr* arg1 : *to_app(e)) { if (!get_lengths(arg1, lens, pos)) return false; } } else if (m_util.str.is_length(e, arg)) { lens.push_back(arg); } else if (m_autil.is_numeral(e, pos1)) { pos += pos1; } else { return false; } return true; } bool seq_rewriter::cannot_contain_suffix(expr* a, expr* b) { if (m_util.str.is_unit(a) && m_util.str.is_unit(b) && m().are_distinct(a, b)) { return true; } zstring A, B; if (m_util.str.is_string(a, A) && m_util.str.is_string(b, B)) { // some prefix of a is a suffix of b bool found = false; for (unsigned i = 1; !found && i <= A.length(); ++i) { found = A.extract(0, i).suffixof(B); } return !found; } return false; } bool seq_rewriter::cannot_contain_prefix(expr* a, expr* b) { if (m_util.str.is_unit(a) && m_util.str.is_unit(b) && m().are_distinct(a, b)) { return true; } zstring A, B; if (m_util.str.is_string(a, A) && m_util.str.is_string(b, B)) { // some suffix of a is a prefix of b bool found = false; for (unsigned i = 0; !found && i < A.length(); ++i) { found = A.extract(i, A.length()-i).suffixof(B); } return !found; } return false; } br_status seq_rewriter::mk_seq_contains(expr* a, expr* b, expr_ref& result) { zstring c, d; if (m_util.str.is_string(a, c) && m_util.str.is_string(b, d)) { result = m().mk_bool_val(c.contains(d)); return BR_DONE; } expr* x = nullptr, *y, *z; if (m_util.str.is_extract(b, x, y, z) && x == a) { result = m().mk_true(); return BR_DONE; } // check if subsequence of a is in b. expr_ref_vector as(m()), bs(m()); m_util.str.get_concat_units(a, as); m_util.str.get_concat_units(b, bs); TRACE("seq", tout << mk_pp(a, m()) << " contains " << mk_pp(b, m()) << "\n";); if (bs.empty()) { result = m().mk_true(); return BR_DONE; } if (as.empty()) { result = m_util.str.mk_is_empty(b); return BR_REWRITE2; } for (unsigned i = 0; bs.size() + i <= as.size(); ++i) { unsigned j = 0; for (; j < bs.size() && as.get(j+i) == bs.get(j); ++j) {}; if (j == bs.size()) { result = m().mk_true(); return BR_DONE; } } std::function is_value = [&](expr* e) { return m().is_value(e); }; if (bs.forall(is_value) && as.forall(is_value)) { result = m().mk_false(); return BR_DONE; } unsigned lenA = 0, lenB = 0; bool lA = min_length(as.size(), as.c_ptr(), lenA); if (lA) { min_length(bs.size(), bs.c_ptr(), lenB); if (lenB > lenA) { result = m().mk_false(); return BR_DONE; } } unsigned offs = 0; unsigned sz = as.size(); expr* b0 = bs.get(0); expr* bL = bs.get(bs.size()-1); for (; offs < as.size() && cannot_contain_prefix(as[offs].get(), b0); ++offs) {} for (; sz > offs && cannot_contain_suffix(as.get(sz-1), bL); --sz) {} if (offs == sz) { result = m_util.str.mk_is_empty(b); return BR_REWRITE2; } if (offs > 0 || sz < as.size()) { SASSERT(sz > offs); result = m_util.str.mk_contains(m_util.str.mk_concat(sz-offs, as.c_ptr()+offs), b); return BR_REWRITE2; } std::function is_unit = [&](expr *e) { return m_util.str.is_unit(e); }; if (bs.forall(is_unit) && as.forall(is_unit)) { expr_ref_vector ors(m()); for (unsigned i = 0; i + bs.size() <= as.size(); ++i) { expr_ref_vector ands(m()); for (unsigned j = 0; j < bs.size(); ++j) { ands.push_back(m().mk_eq(as.get(i + j), bs.get(j))); } ors.push_back(::mk_and(ands)); } result = ::mk_or(ors); return BR_REWRITE_FULL; } if (bs.size() == 1 && bs.forall(is_unit) && as.size() > 1) { expr_ref_vector ors(m()); for (expr* ai : as) { ors.push_back(m_util.str.mk_contains(ai, bs.get(0))); } result = ::mk_or(ors); return BR_REWRITE_FULL; } return BR_FAILED; } /* * (str.at s i), constants s/i, i < 0 or i >= |s| ==> (str.at s i) = "" */ br_status seq_rewriter::mk_seq_at(expr* a, expr* b, expr_ref& result) { zstring c; rational r; expr_ref_vector lens(m()); if (!get_lengths(b, lens, r)) { return BR_FAILED; } if (lens.empty() && r.is_neg()) { result = m_util.str.mk_empty(m().get_sort(a)); return BR_DONE; } expr* a2 = nullptr, *i2 = nullptr; if (lens.empty() && m_util.str.is_at(a, a2, i2)) { if (r.is_pos()) { result = m_util.str.mk_empty(m().get_sort(a)); } else { result = a; } return BR_DONE; } m_lhs.reset(); m_util.str.get_concat_units(a, m_lhs); if (m_lhs.empty()) { result = m_util.str.mk_empty(m().get_sort(a)); return BR_DONE; } unsigned i = 0; for (; i < m_lhs.size(); ++i) { expr* lhs = m_lhs.get(i); if (lens.contains(lhs) && !r.is_neg()) { lens.erase(lhs); } else if (m_util.str.is_unit(lhs) && r.is_zero() && lens.empty()) { result = lhs; return BR_REWRITE1; } else if (m_util.str.is_unit(lhs) && r.is_pos()) { r -= rational(1); } else { break; } } if (i == 0) { return BR_FAILED; } if (m_lhs.size() == i) { result = m_util.str.mk_empty(m().get_sort(a)); return BR_DONE; } expr_ref pos(m_autil.mk_int(r), m()); for (expr* rhs : lens) { pos = m_autil.mk_add(pos, m_util.str.mk_length(rhs)); } result = m_util.str.mk_concat(m_lhs.size() - i , m_lhs.c_ptr() + i); result = m_util.str.mk_at(result, pos); return BR_REWRITE2; } br_status seq_rewriter::mk_seq_nth(expr* a, expr* b, expr_ref& result) { rational pos1, pos2; expr* s = nullptr, *p = nullptr, *len = nullptr; if (m_util.str.is_unit(a, s) && m_autil.is_numeral(b, pos1) && pos1.is_zero()) { result = s; return BR_DONE; } if (m_util.str.is_extract(a, s, p, len) && m_autil.is_numeral(p, pos1)) { expr_ref_vector lens(m()); rational pos2; if (get_lengths(len, lens, pos2) && (pos1 == -pos2) && (lens.size() == 1) && (lens.get(0) == s)) { expr_ref idx(m_autil.mk_int(pos1), m()); idx = m_autil.mk_add(b, idx); expr* es[2] = { s, idx }; result = m().mk_app(m_util.get_family_id(), OP_SEQ_NTH, 2, es); return BR_REWRITE_FULL; } } expr* es[2] = { a, b}; expr* la = m_util.str.mk_length(a); result = m().mk_ite(m().mk_and(m_autil.mk_ge(b, m_autil.mk_int(0)), m().mk_not(m_autil.mk_ge(b, la))), m().mk_app(m_util.get_family_id(), OP_SEQ_NTH_I, 2, es), m().mk_app(m_util.get_family_id(), OP_SEQ_NTH_U, 2, es)); return BR_REWRITE_FULL; } br_status seq_rewriter::mk_seq_nth_i(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_last_index(expr* a, expr* b, expr_ref& result) { zstring s1, s2; bool isc1 = m_util.str.is_string(a, s1); bool isc2 = m_util.str.is_string(b, s2); if (isc1 && isc2) { int idx = s1.last_indexof(s2); result = m_autil.mk_numeral(rational(idx), true); return BR_DONE; } return BR_FAILED; } br_status seq_rewriter::mk_seq_index(expr* a, expr* b, expr* c, expr_ref& result) { zstring s1, s2; rational r; bool isc1 = m_util.str.is_string(a, s1); bool isc2 = m_util.str.is_string(b, s2); if (isc1 && isc2 && m_autil.is_numeral(c, r) && r.is_unsigned()) { int idx = s1.indexof(s2, r.get_unsigned()); result = m_autil.mk_numeral(rational(idx), true); return BR_DONE; } if (m_autil.is_numeral(c, r) && r.is_neg()) { result = m_autil.mk_numeral(rational(-1), true); return BR_DONE; } if (m_util.str.is_empty(b) && m_autil.is_numeral(c, r) && r.is_zero()) { result = c; return BR_DONE; } // Enhancement: walk segments of a, determine which segments must overlap, must not overlap, may overlap. return BR_FAILED; } // (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. br_status seq_rewriter::mk_seq_replace(expr* a, expr* b, expr* c, expr_ref& result) { zstring s1, s2, s3; if (m_util.str.is_string(a, s1) && m_util.str.is_string(b, s2) && m_util.str.is_string(c, s3)) { result = m_util.str.mk_string(s1.replace(s2, s3)); return BR_DONE; } if (b == c) { result = a; return BR_DONE; } if (a == b) { result = c; return BR_DONE; } if (m_util.str.is_empty(b)) { result = m_util.str.mk_concat(c, a); return BR_REWRITE1; } m_lhs.reset(); m_util.str.get_concat(a, m_lhs); // a = "", |b| > 0 -> replace("",b,c) = "" if (m_lhs.empty()) { unsigned len = 0; m_util.str.get_concat(b, m_lhs); min_length(m_lhs.size(), m_lhs.c_ptr(), len); if (len > 0) { result = a; return BR_DONE; } return BR_FAILED; } // a := b + rest if (m_lhs.get(0) == b) { m_lhs[0] = c; result = m_util.str.mk_concat(m_lhs.size(), m_lhs.c_ptr()); return BR_REWRITE1; } // a : a' + rest string, b is string, c is string, a' contains b if (m_util.str.is_string(b, s2) && m_util.str.is_string(c, s3) && m_util.str.is_string(m_lhs.get(0), s1) && s1.contains(s2) ) { m_lhs[0] = m_util.str.mk_string(s1.replace(s2, s3)); result = m_util.str.mk_concat(m_lhs.size(), m_lhs.c_ptr()); return BR_REWRITE1; } return BR_FAILED; } br_status seq_rewriter::mk_seq_prefix(expr* a, expr* b, expr_ref& result) { TRACE("seq", tout << mk_pp(a, m()) << " " << mk_pp(b, m()) << "\n";); zstring s1, s2; bool isc1 = m_util.str.is_string(a, s1); bool isc2 = m_util.str.is_string(b, s2); if (isc1 && isc2) { result = m().mk_bool_val(s1.prefixof(s2)); TRACE("seq", tout << result << "\n";); return BR_DONE; } if (m_util.str.is_empty(a)) { result = m().mk_true(); return BR_DONE; } expr* a1 = m_util.str.get_leftmost_concat(a); expr* b1 = m_util.str.get_leftmost_concat(b); isc1 = m_util.str.is_string(a1, s1); isc2 = m_util.str.is_string(b1, s2); expr_ref_vector as(m()), bs(m()); if (a1 != b1 && isc1 && isc2) { if (s1.length() <= s2.length()) { if (s1.prefixof(s2)) { if (a == a1) { result = m().mk_true(); TRACE("seq", tout << s1 << " " << s2 << " " << result << "\n";); return BR_DONE; } m_util.str.get_concat(a, as); m_util.str.get_concat(b, bs); SASSERT(as.size() > 1); s2 = s2.extract(s1.length(), s2.length()-s1.length()); bs[0] = m_util.str.mk_string(s2); result = m_util.str.mk_prefix(m_util.str.mk_concat(as.size()-1, as.c_ptr()+1), m_util.str.mk_concat(bs.size(), bs.c_ptr())); TRACE("seq", tout << s1 << " " << s2 << " " << result << "\n";); return BR_REWRITE_FULL; } else { result = m().mk_false(); TRACE("seq", tout << s1 << " " << s2 << " " << result << "\n";); return BR_DONE; } } else { if (s2.prefixof(s1)) { if (b == b1) { result = m().mk_false(); TRACE("seq", tout << s1 << " " << s2 << " " << result << "\n";); return BR_DONE; } m_util.str.get_concat(a, as); m_util.str.get_concat(b, bs); SASSERT(bs.size() > 1); s1 = s1.extract(s2.length(), s1.length() - s2.length()); as[0] = m_util.str.mk_string(s1); result = m_util.str.mk_prefix(m_util.str.mk_concat(as.size(), as.c_ptr()), m_util.str.mk_concat(bs.size()-1, bs.c_ptr()+1)); TRACE("seq", tout << s1 << " " << s2 << " " << result << "\n";); return BR_REWRITE_FULL; } else { result = m().mk_false(); TRACE("seq", tout << s1 << " " << s2 << " " << result << "\n";); return BR_DONE; } } } m_util.str.get_concat_units(a, as); m_util.str.get_concat_units(b, bs); unsigned i = 0; expr_ref_vector eqs(m()); for (; i < as.size() && i < bs.size(); ++i) { expr* ai = as.get(i), *bi = bs.get(i); if (m().are_equal(ai, bi)) { continue; } if (m().are_distinct(ai, bi)) { result = m().mk_false(); return BR_DONE; } if (m_util.str.is_unit(ai) && m_util.str.is_unit(bi)) { eqs.push_back(m().mk_eq(ai, bi)); continue; } break; } if (i == as.size()) { result = mk_and(eqs); TRACE("seq", tout << result << "\n";); return BR_REWRITE3; } SASSERT(i < as.size()); if (i == bs.size()) { for (unsigned j = i; j < as.size(); ++j) { eqs.push_back(m_util.str.mk_is_empty(as.get(j))); } result = mk_and(eqs); TRACE("seq", tout << result << "\n";); return BR_REWRITE3; } if (i > 0) { SASSERT(i < as.size() && i < bs.size()); a = m_util.str.mk_concat(as.size() - i, as.c_ptr() + i); b = m_util.str.mk_concat(bs.size() - i, bs.c_ptr() + i); eqs.push_back(m_util.str.mk_prefix(a, b)); result = mk_and(eqs); TRACE("seq", tout << result << "\n";); return BR_REWRITE3; } return BR_FAILED; } br_status seq_rewriter::mk_seq_suffix(expr* a, expr* b, expr_ref& result) { if (a == b) { result = m().mk_true(); return BR_DONE; } zstring s1, s2; if (m_util.str.is_empty(a)) { result = m().mk_true(); return BR_DONE; } if (m_util.str.is_empty(b)) { result = m_util.str.mk_is_empty(a); return BR_REWRITE3; } bool isc1 = false; bool isc2 = false; expr *a1 = nullptr, *a2 = nullptr, *b1 = nullptr, *b2 = nullptr; if (m_util.str.is_concat(a, a1, a2) && m_util.str.is_string(a2, s1)) { isc1 = true; } else if (m_util.str.is_string(a, s1)) { isc1 = true; a2 = a; a1 = nullptr; } if (m_util.str.is_concat(b, b1, b2) && m_util.str.is_string(b2, s2)) { isc2 = true; } else if (m_util.str.is_string(b, s2)) { isc2 = true; b2 = b; b1 = nullptr; } if (isc1 && isc2) { if (s1.length() == s2.length()) { //SASSERT(s1 != s2); result = m().mk_false(); return BR_DONE; } else if (s1.length() < s2.length()) { bool suffix = s1.suffixof(s2); if (suffix && a1 == nullptr) { result = m().mk_true(); return BR_DONE; } else if (suffix) { s2 = s2.extract(0, s2.length()-s1.length()); b2 = m_util.str.mk_string(s2); result = m_util.str.mk_suffix(a1, b1?m_util.str.mk_concat(b1, b2):b2); return BR_DONE; } else { result = m().mk_false(); return BR_DONE; } } else { SASSERT(s1.length() > s2.length()); if (b1 == nullptr) { result = m().mk_false(); return BR_DONE; } bool suffix = s2.suffixof(s1); if (suffix) { s1 = s1.extract(0, s1.length()-s2.length()); a2 = m_util.str.mk_string(s1); result = m_util.str.mk_suffix(a1?m_util.str.mk_concat(a1, a2):a2, b1); return BR_DONE; } else { result = m().mk_false(); return BR_DONE; } } } expr_ref_vector as(m()), bs(m()), eqs(m()); m_util.str.get_concat_units(a, as); m_util.str.get_concat_units(b, bs); unsigned i = 1, sza = as.size(), szb = bs.size(); for (; i <= sza && i <= szb; ++i) { expr* ai = as.get(sza-i), *bi = bs.get(szb-i); if (m().are_equal(ai, bi)) { continue; } if (m().are_distinct(ai, bi)) { result = m().mk_false(); return BR_DONE; } if (m_util.str.is_unit(ai) && m_util.str.is_unit(bi)) { eqs.push_back(m().mk_eq(ai, bi)); continue; } break; } if (i > sza) { result = mk_and(eqs); TRACE("seq", tout << result << "\n";); return BR_REWRITE3; } if (i > szb) { for (unsigned j = i; j <= sza; ++j) { expr* aj = as.get(sza-j); eqs.push_back(m_util.str.mk_is_empty(aj)); } result = mk_and(eqs); TRACE("seq", tout << result << "\n";); return BR_REWRITE3; } if (i > 1) { SASSERT(i <= sza && i <= szb); a = m_util.str.mk_concat(sza - i + 1, as.c_ptr()); b = m_util.str.mk_concat(szb - i + 1, bs.c_ptr()); eqs.push_back(m_util.str.mk_suffix(a, b)); result = mk_and(eqs); TRACE("seq", tout << result << "\n";); return BR_REWRITE3; } return BR_FAILED; } br_status seq_rewriter::mk_str_le(expr* a, expr* b, expr_ref& result) { result = m().mk_not(m_util.str.mk_lex_lt(b, a)); return BR_DONE; } br_status seq_rewriter::mk_str_itos(expr* a, expr_ref& result) { rational r; if (m_autil.is_numeral(a, r)) { if (r.is_int() && !r.is_neg()) { result = m_util.str.mk_string(symbol(r.to_string().c_str())); } else { result = m_util.str.mk_string(symbol("")); } return BR_DONE; } return BR_FAILED; } /** \brief rewrite str.to.int according to the rules: - if the expression is a string which is a non-empty sequence of digits 0-9 extract the corresponding numeral. - if the expression is a string that contains any other character or is empty, produce -1 - if the expression is int.to.str(x) produce ite(x >= 0, x, -1) */ br_status seq_rewriter::mk_str_stoi(expr* a, expr_ref& result) { zstring str; if (m_util.str.is_string(a, str)) { std::string s = str.encode(); if (s.length() == 0) { result = m_autil.mk_int(-1); return BR_DONE; } for (unsigned i = 0; i < s.length(); ++i) { if (!('0' <= s[i] && s[i] <= '9')) { result = m_autil.mk_int(-1); return BR_DONE; } } rational r(s.c_str()); result = m_autil.mk_numeral(r, true); return BR_DONE; } expr* b; if (m_util.str.is_itos(a, b)) { result = m().mk_ite(m_autil.mk_ge(b, m_autil.mk_int(0)), b, m_autil.mk_int(-1)); return BR_DONE; } return BR_FAILED; } void seq_rewriter::add_next(u_map& next, expr_ref_vector& trail, unsigned idx, expr* cond) { expr* acc; if (!m().is_true(cond) && next.find(idx, acc)) { expr* args[2] = { cond, acc }; cond = mk_or(m(), 2, args); } trail.push_back(cond); next.insert(idx, cond); } bool seq_rewriter::is_sequence(eautomaton& aut, expr_ref_vector& seq) { seq.reset(); unsigned state = aut.init(); uint_set visited; eautomaton::moves mvs; unsigned_vector states; aut.get_epsilon_closure(state, states); bool has_final = false; for (unsigned i = 0; !has_final && i < states.size(); ++i) { has_final = aut.is_final_state(states[i]); } aut.get_moves_from(state, mvs, true); while (!has_final) { if (mvs.size() != 1) { return false; } if (visited.contains(state)) { return false; } if (aut.is_final_state(mvs[0].src())) { return false; } visited.insert(state); sym_expr* t = mvs[0].t(); if (!t || !t->is_char()) { return false; } seq.push_back(m_util.str.mk_unit(t->get_char())); state = mvs[0].dst(); mvs.reset(); aut.get_moves_from(state, mvs, true); states.reset(); has_final = false; aut.get_epsilon_closure(state, states); for (unsigned i = 0; !has_final && i < states.size(); ++i) { has_final = aut.is_final_state(states[i]); } } return mvs.empty(); } bool seq_rewriter::is_sequence(expr* e, expr_ref_vector& seq) { seq.reset(); zstring s; ptr_vector todo; expr *e1, *e2; todo.push_back(e); while (!todo.empty()) { e = todo.back(); todo.pop_back(); if (m_util.str.is_string(e, s)) { for (unsigned i = 0; i < s.length(); ++i) { seq.push_back(m_util.str.mk_char(s, i)); } } else if (m_util.str.is_empty(e)) { continue; } else if (m_util.str.is_unit(e, e1)) { seq.push_back(e1); } else if (m_util.str.is_concat(e, e1, e2)) { todo.push_back(e2); todo.push_back(e1); } else { return false; } } return true; } br_status seq_rewriter::mk_str_in_regexp(expr* a, expr* b, expr_ref& result) { if (m_util.re.is_empty(b)) { result = m().mk_false(); return BR_DONE; } if (m_util.re.is_full_seq(b)) { result = m().mk_true(); return BR_DONE; } 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; } if (is_sequence(*aut, seq)) { TRACE("seq", tout << seq << "\n";); if (seq.empty()) { result = m_util.str.mk_is_empty(a); } else { result = m().mk_eq(a, m_util.str.mk_concat(seq)); } return BR_REWRITE3; } if (!is_sequence(a, seq)) { TRACE("seq", tout << "not a sequence " << mk_pp(a, m()) << "\n";); return BR_FAILED; } expr_ref_vector trail(m()); u_map maps[2]; bool select_map = false; expr_ref ch(m()), cond(m()); eautomaton::moves mvs; maps[0].insert(aut->init(), m().mk_true()); // is_accepted(a, aut) & some state in frontier is final. for (unsigned i = 0; i < seq.size(); ++i) { u_map& frontier = maps[select_map]; u_map& next = maps[!select_map]; select_map = !select_map; ch = seq[i].get(); next.reset(); u_map::iterator it = frontier.begin(), end = frontier.end(); for (; it != end; ++it) { mvs.reset(); unsigned state = it->m_key; expr* acc = it->m_value; aut->get_moves_from(state, mvs, false); for (unsigned j = 0; j < mvs.size(); ++j) { eautomaton::move const& mv = mvs[j]; SASSERT(mv.t()); if (mv.t()->is_char() && m().is_value(mv.t()->get_char()) && m().is_value(ch)) { if (mv.t()->get_char() == ch) { add_next(next, trail, mv.dst(), acc); } else { continue; } } else { cond = mv.t()->accept(ch); if (m().is_false(cond)) { continue; } if (m().is_true(cond)) { add_next(next, trail, mv.dst(), acc); continue; } expr* args[2] = { cond, acc }; cond = mk_and(m(), 2, args); add_next(next, trail, mv.dst(), cond); } } } } u_map const& frontier = maps[select_map]; expr_ref_vector ors(m()); for (auto const& kv : frontier) { unsigned_vector states; bool has_final = false; 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(kv.m_value); } } result = mk_or(ors); TRACE("seq", tout << result << "\n";); return BR_REWRITE_FULL; } br_status seq_rewriter::mk_str_to_regexp(expr* a, expr_ref& result) { return BR_FAILED; } br_status seq_rewriter::mk_re_concat(expr* a, expr* b, expr_ref& result) { if (m_util.re.is_full_seq(a) && m_util.re.is_full_seq(b)) { result = a; return BR_DONE; } if (m_util.re.is_empty(a)) { result = a; return BR_DONE; } if (m_util.re.is_empty(b)) { result = b; return BR_DONE; } if (is_epsilon(a)) { result = b; return BR_DONE; } if (is_epsilon(b)) { result = a; return BR_DONE; } expr* a1 = nullptr, *b1 = nullptr; if (m_util.re.is_star(a, a1) && m_util.re.is_star(b, b1) && a1 == b1) { result = a; return BR_DONE; } if (m_util.re.is_star(a, a1) && a1 == b) { result = m_util.re.mk_concat(b, a); return BR_DONE; } unsigned lo1, hi1, lo2, hi2; if (m_util.re.is_loop(a, a1, lo1, hi1) && lo1 <= hi1 && m_util.re.is_loop(b, b1, lo2, hi2) && lo2 <= hi2 && a1 == b1) { result = m_util.re.mk_loop(a1, lo1 + lo2, hi1 + hi2); return BR_DONE; } if (m_util.re.is_loop(a, a1, lo1) && m_util.re.is_loop(b, b1, lo2) && a1 == b1) { result = m_util.re.mk_loop(a1, lo1 + lo2); return BR_DONE; } for (unsigned i = 0; i < 2; ++i) { // (loop a lo1) + (loop a lo2 hi2) = (loop a lo1 + lo2) if (m_util.re.is_loop(a, a1, lo1) && m_util.re.is_loop(b, b1, lo2, hi2) && lo2 <= hi2 && a1 == b1) { result = m_util.re.mk_loop(a1, lo1 + lo2); return BR_DONE; } // (loop a lo1 hi1) + a* = (loop a lo1) if (m_util.re.is_loop(a, a1, lo1, hi1) && m_util.re.is_star(b, b1) && a1 == b1) { result = m_util.re.mk_loop(a1, lo1); return BR_DONE; } // (loop a lo1) + a* = (loop a lo1) if (m_util.re.is_loop(a, a1, lo1) && m_util.re.is_star(b, b1) && a1 == b1) { result = a; return BR_DONE; } // (loop a lo1 hi1) + a = (loop a lo1+1 hi1+1) if (m_util.re.is_loop(a, a1, lo1, hi1) && lo1 <= hi1 && a1 == b) { result = m_util.re.mk_loop(a1, lo1+1, hi1+1); return BR_DONE; } std::swap(a, b); } return BR_FAILED; } /* (a + a) = a (a + eps) = a (eps + a) = a */ br_status seq_rewriter::mk_re_union(expr* a, expr* b, expr_ref& result) { if (a == b) { result = a; return BR_DONE; } if (m_util.re.is_empty(a)) { result = b; return BR_DONE; } if (m_util.re.is_empty(b)) { result = a; return BR_DONE; } if (m_util.re.is_full_seq(a)) { result = a; return BR_DONE; } if (m_util.re.is_full_seq(b)) { result = b; return BR_DONE; } if (m_util.re.is_star(a) && is_epsilon(b)) { result = a; return BR_DONE; } if (m_util.re.is_star(b) && is_epsilon(a)) { result = b; return BR_DONE; } return BR_FAILED; } br_status seq_rewriter::mk_re_complement(expr* a, expr_ref& result) { expr* e1, *e2; if (m_util.re.is_intersection(a, e1, e2)) { result = m_util.re.mk_union(m_util.re.mk_complement(e1), m_util.re.mk_complement(e2)); return BR_REWRITE2; } if (m_util.re.is_union(a, e1, e2)) { result = m_util.re.mk_inter(m_util.re.mk_complement(e1), m_util.re.mk_complement(e2)); return BR_REWRITE2; } if (m_util.re.is_empty(a)) { result = m_util.re.mk_full_seq(m().get_sort(a)); return BR_DONE; } if (m_util.re.is_full_seq(a)) { result = m_util.re.mk_empty(m().get_sort(a)); return BR_DONE; } return BR_FAILED; } /** (emp n r) = emp (r n emp) = emp (all n r) = r (r n all) = r (r n r) = r */ br_status seq_rewriter::mk_re_inter(expr* a, expr* b, expr_ref& result) { if (a == b) { result = a; return BR_DONE; } if (m_util.re.is_empty(a)) { result = a; return BR_DONE; } if (m_util.re.is_empty(b)) { result = b; return BR_DONE; } if (m_util.re.is_full_seq(a)) { result = b; return BR_DONE; } if (m_util.re.is_full_seq(b)) { 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; } br_status seq_rewriter::mk_re_loop(func_decl* f, unsigned num_args, expr* const* args, expr_ref& result) { rational n1, n2; unsigned lo, hi, lo2, hi2, np; expr* a = nullptr; switch (num_args) { case 1: np = f->get_num_parameters(); lo2 = np > 0 ? f->get_parameter(0).get_int() : 0; hi2 = np > 1 ? f->get_parameter(1).get_int() : 0; if (np == 2 && lo2 > hi2) { result = m_util.re.mk_loop(args[0], lo2, lo2); return BR_REWRITE1; } // (loop (loop a lo) lo2) = (loop lo*lo2) if (m_util.re.is_loop(args[0], a, lo) && np == 1) { result = m_util.re.mk_loop(a, lo2 * lo); return BR_REWRITE1; } // (loop (loop a l l) h h) = (loop a l*h l*h) if (m_util.re.is_loop(args[0], a, lo, hi) && np == 2 && lo == hi && lo2 == hi2) { result = m_util.re.mk_loop(a, lo2 * lo, hi2 * hi); return BR_REWRITE1; } // (loop a 0 0) = "" if (np == 2 && hi2 == 0) { result = m_util.re.mk_to_re(m_util.str.mk_empty(m_util.re.to_seq(m().get_sort(args[0])))); return BR_DONE; } // (loop a 1 1) = a if (np == 2 && lo2 == 1 && hi2 == 1) { result = args[0]; return BR_DONE; } // (loop a 0) = a* if (np == 1 && lo2 == 0) { result = m_util.re.mk_star(args[0]); return BR_DONE; } break; case 2: if (m_autil.is_numeral(args[1], n1) && n1.is_unsigned()) { result = m_util.re.mk_loop(args[0], n1.get_unsigned()); return BR_REWRITE1; } break; case 3: if (m_autil.is_numeral(args[1], n1) && n1.is_unsigned() && m_autil.is_numeral(args[2], n2) && n2.is_unsigned()) { result = m_util.re.mk_loop(args[0], n1.get_unsigned(), n2.get_unsigned()); return BR_REWRITE1; } break; default: break; } return BR_FAILED; } /* a** = a* (a* + b)* = (a + b)* (a + b*)* = (a + b)* (a*b*)* = (a + b)* a+* = a* emp* = "" all* = all */ br_status seq_rewriter::mk_re_star(expr* a, expr_ref& result) { expr* b, *c, *b1, *c1; if (m_util.re.is_star(a) || m_util.re.is_full_seq(a)) { result = a; return BR_DONE; } if (m_util.re.is_full_char(a)) { result = m_util.re.mk_full_seq(m().get_sort(a)); return BR_DONE; } if (m_util.re.is_empty(a)) { sort* seq_sort = nullptr; VERIFY(m_util.is_re(a, seq_sort)); result = m_util.re.mk_to_re(m_util.str.mk_empty(seq_sort)); return BR_DONE; } if (m_util.re.is_plus(a, b)) { result = m_util.re.mk_star(b); return BR_DONE; } if (m_util.re.is_union(a, b, c)) { if (m_util.re.is_star(b, b1)) { result = m_util.re.mk_star(m_util.re.mk_union(b1, c)); return BR_REWRITE2; } if (m_util.re.is_star(c, c1)) { result = m_util.re.mk_star(m_util.re.mk_union(b, c1)); return BR_REWRITE2; } if (is_epsilon(b)) { result = m_util.re.mk_star(c); return BR_REWRITE2; } if (is_epsilon(c)) { result = m_util.re.mk_star(b); return BR_REWRITE2; } } if (m_util.re.is_concat(a, b, c) && m_util.re.is_star(b, b1) && m_util.re.is_star(c, c1)) { result = m_util.re.mk_star(m_util.re.mk_union(b1, c1)); return BR_REWRITE2; } return BR_FAILED; } /* * (re.range c_1 c_n) */ br_status seq_rewriter::mk_re_range(expr* lo, expr* hi, expr_ref& result) { return BR_FAILED; } /* emp+ = emp all+ = all a*+ = a* a++ = a+ a+ = aa* */ br_status seq_rewriter::mk_re_plus(expr* a, expr_ref& result) { if (m_util.re.is_empty(a)) { result = a; return BR_DONE; } if (m_util.re.is_full_seq(a)) { result = a; return BR_DONE; } if (is_epsilon(a)) { result = a; return BR_DONE; } if (m_util.re.is_plus(a)) { result = a; return BR_DONE; } if (m_util.re.is_star(a)) { result = a; return BR_DONE; } //return BR_FAILED; result = m_util.re.mk_concat(a, m_util.re.mk_star(a)); return BR_REWRITE2; } br_status seq_rewriter::mk_re_opt(expr* a, expr_ref& result) { sort* s = nullptr; VERIFY(m_util.is_re(a, s)); result = m_util.re.mk_union(m_util.re.mk_to_re(m_util.str.mk_empty(s)), a); return BR_REWRITE1; } br_status seq_rewriter::mk_eq_core(expr * l, expr * r, expr_ref & result) { TRACE("seq", tout << mk_bounded_pp(l, m(), 2) << " = " << mk_bounded_pp(r, m(), 2) << "\n";); expr_ref_vector lhs(m()), rhs(m()), res(m()); bool changed = false; if (!reduce_eq(l, r, lhs, rhs, changed)) { result = m().mk_false(); TRACE("seq", tout << result << "\n";); return BR_DONE; } if (!changed) { return BR_FAILED; } for (unsigned i = 0; i < lhs.size(); ++i) { res.push_back(m().mk_eq(lhs.get(i), rhs.get(i))); } result = mk_and(res); TRACE("seq", tout << result << "\n";); return BR_REWRITE3; } bool seq_rewriter::reduce_eq(expr_ref_vector& ls, expr_ref_vector& rs, expr_ref_vector& lhs, expr_ref_vector& rhs, bool& change) { expr* a, *b; zstring s; bool lchange = false; SASSERT(lhs.empty()); TRACE("seq_verbose", tout << ls << "\n"; tout << rs << "\n";); // solve from back while (true) { while (!rs.empty() && m_util.str.is_empty(rs.back())) { rs.pop_back(); } while (!ls.empty() && m_util.str.is_empty(ls.back())) { ls.pop_back(); } if (ls.empty() || rs.empty()) { break; } expr* l = ls.back(); expr* r = rs.back(); if (m_util.str.is_unit(r) && m_util.str.is_string(l)) { std::swap(l, r); ls.swap(rs); } if (l == r) { ls.pop_back(); rs.pop_back(); } else if(m_util.str.is_unit(l, a) && m_util.str.is_unit(r, b)) { if (m().are_distinct(a, b)) { return false; } lhs.push_back(a); rhs.push_back(b); ls.pop_back(); rs.pop_back(); } else if (m_util.str.is_unit(l, a) && m_util.str.is_string(r, s)) { SASSERT(s.length() > 0); app* ch = m_util.str.mk_char(s, s.length()-1); SASSERT(m().get_sort(ch) == m().get_sort(a)); lhs.push_back(ch); rhs.push_back(a); ls.pop_back(); if (s.length() == 1) { rs.pop_back(); } else { expr_ref s2(m_util.str.mk_string(s.extract(0, s.length()-1)), m()); rs[rs.size()-1] = s2; } } else { break; } change = true; lchange = true; } // solve from front unsigned head1 = 0, head2 = 0; while (true) { while (head1 < ls.size() && m_util.str.is_empty(ls[head1].get())) { ++head1; } while (head2 < rs.size() && m_util.str.is_empty(rs[head2].get())) { ++head2; } if (head1 == ls.size() || head2 == rs.size()) { break; } SASSERT(head1 < ls.size() && head2 < rs.size()); expr* l = ls[head1].get(); expr* r = rs[head2].get(); if (m_util.str.is_unit(r) && m_util.str.is_string(l)) { std::swap(l, r); ls.swap(rs); std::swap(head1, head2); } if (l == r) { ++head1; ++head2; } else if(m_util.str.is_unit(l, a) && m_util.str.is_unit(r, b)) { if (m().are_distinct(a, b)) { return false; } lhs.push_back(a); rhs.push_back(b); ++head1; ++head2; } else if (m_util.str.is_unit(l, a) && m_util.str.is_string(r, s)) { SASSERT(s.length() > 0); app* ch = m_util.str.mk_char(s, 0); SASSERT(m().get_sort(ch) == m().get_sort(a)); lhs.push_back(ch); rhs.push_back(a); ++head1; if (s.length() == 1) { ++head2; } else { expr_ref s2(m_util.str.mk_string(s.extract(1, s.length()-1)), m()); rs[head2] = s2; } } else { break; } TRACE("seq_verbose", tout << ls << " == " << rs << "\n";); change = true; lchange = true; } // reduce strings zstring s1, s2; while (head1 < ls.size() && head2 < rs.size() && m_util.str.is_string(ls[head1].get(), s1) && m_util.str.is_string(rs[head2].get(), s2)) { TRACE("seq", tout << s1 << " - " << s2 << " " << s1.length() << " " << s2.length() << "\n";); unsigned l = std::min(s1.length(), s2.length()); for (unsigned i = 0; i < l; ++i) { if (s1[i] != s2[i]) { TRACE("seq", tout << "different at position " << i << " " << s1[i] << " " << s2[i] << "\n";); return false; } } if (l == s1.length()) { ++head1; } else { ls[head1] = m_util.str.mk_string(s1.extract(l, s1.length()-l)); } if (l == s2.length()) { ++head2; } else { rs[head2] = m_util.str.mk_string(s2.extract(l, s2.length()-l)); } TRACE("seq", tout << "change string\n";); change = true; lchange = true; } while (head1 < ls.size() && head2 < rs.size() && m_util.str.is_string(ls.back(), s1) && m_util.str.is_string(rs.back(), s2)) { unsigned l = std::min(s1.length(), s2.length()); for (unsigned i = 0; i < l; ++i) { if (s1[s1.length()-i-1] != s2[s2.length()-i-1]) { return false; } } ls.pop_back(); rs.pop_back(); if (l < s1.length()) { ls.push_back(m_util.str.mk_string(s1.extract(0, s1.length()-l))); } if (l < s2.length()) { rs.push_back(m_util.str.mk_string(s2.extract(0, s2.length()-l))); } TRACE("seq", tout << "change string back\n";); change = true; lchange = true; } bool is_sat = true; unsigned szl = ls.size() - head1, szr = rs.size() - head2; expr* const* _ls = ls.c_ptr() + head1, * const* _rs = rs.c_ptr() + head2; if (solve_itos(szl, _ls, szr, _rs, lhs, rhs, is_sat)) { ls.reset(); rs.reset(); change = true; return is_sat; } if (length_constrained(szl, _ls, szr, _rs, lhs, rhs, is_sat)) { ls.reset(); rs.reset(); change = true; return is_sat; } if (szr == 0 && szl == 0) { ls.reset(); rs.reset(); return true; } if (szr == 0 && szl > 0) { std::swap(szr, szl); std::swap(_ls, _rs); } if (szl == 0 && szr > 0) { if (set_empty(szr, _rs, true, lhs, rhs)) { lchange |= szr > 1; change |= szr > 1; if (szr == 1 && !lchange) { lhs.reset(); rhs.reset(); } else { ls.reset(); rs.reset(); } return true; } else { return false; } } SASSERT(szl > 0 && szr > 0); if (is_subsequence(szl, _ls, szr, _rs, lhs, rhs, is_sat)) { ls.reset(); rs.reset(); change = true; return is_sat; } if (lchange) { if (head1 > 0) { for (unsigned i = 0; i < szl; ++i) { ls[i] = ls[i + head1].get(); } } ls.shrink(szl); if (head2 > 0) { for (unsigned i = 0; i < szr; ++i) { rs[i] = rs[i + head2].get(); } } rs.shrink(szr); } SASSERT(rs.empty() == ls.empty()); change |= lchange; return true; } void seq_rewriter::add_seqs(expr_ref_vector const& ls, expr_ref_vector const& rs, expr_ref_vector& lhs, expr_ref_vector& rhs) { if (ls.empty() && !rs.empty()) { rhs.push_back(m_util.str.mk_concat(rs)); lhs.push_back(m_util.str.mk_empty(m().get_sort(rhs.back()))); } else if (rs.empty() && !ls.empty()) { lhs.push_back(m_util.str.mk_concat(ls)); rhs.push_back(m_util.str.mk_empty(m().get_sort(lhs.back()))); } else if (!rs.empty() && !ls.empty()) { lhs.push_back(m_util.str.mk_concat(ls)); rhs.push_back(m_util.str.mk_concat(rs)); } } bool seq_rewriter::reduce_eq(expr* l, expr* r, expr_ref_vector& lhs, expr_ref_vector& rhs, bool& changed) { m_lhs.reset(); m_rhs.reset(); m_util.str.get_concat(l, m_lhs); m_util.str.get_concat(r, m_rhs); bool change = false; if (reduce_eq(m_lhs, m_rhs, lhs, rhs, change)) { SASSERT(lhs.size() == rhs.size()); if (!change) { lhs.push_back(l); rhs.push_back(r); } else { add_seqs(m_lhs, m_rhs, lhs, rhs); } changed |= change; return true; } else { TRACE("seq", tout << mk_bounded_pp(l, m()) << " != " << mk_bounded_pp(r, m()) << "\n";); return false; } } bool seq_rewriter::reduce_contains(expr* a, expr* b, expr_ref_vector& disj) { m_lhs.reset(); m_util.str.get_concat(a, m_lhs); TRACE("seq", tout << expr_ref(a, m()) << " " << expr_ref(b, m()) << "\n";); zstring s; for (unsigned i = 0; i < m_lhs.size(); ++i) { expr* e = m_lhs.get(i); if (m_util.str.is_empty(e)) { continue; } if (m_util.str.is_string(e, s)) { unsigned sz = s.length(); expr_ref_vector es(m()); for (unsigned j = 0; j < sz; ++j) { es.push_back(m_util.str.mk_unit(m_util.str.mk_char(s, j))); } es.append(m_lhs.size() - i, m_lhs.c_ptr() + i); for (unsigned j = 0; j < sz; ++j) { disj.push_back(m_util.str.mk_prefix(b, m_util.str.mk_concat(es.size() - j, es.c_ptr() + j))); } continue; } if (m_util.str.is_unit(e)) { disj.push_back(m_util.str.mk_prefix(b, m_util.str.mk_concat(m_lhs.size() - i, m_lhs.c_ptr() + i))); continue; } if (m_util.str.is_string(b, s)) { expr* all = m_util.re.mk_full_seq(m_util.re.mk_re(m().get_sort(b))); disj.push_back(m_util.re.mk_in_re(m_util.str.mk_concat(m_lhs.size() - i, m_lhs.c_ptr() + i), m_util.re.mk_concat(all, m_util.re.mk_concat(m_util.re.mk_to_re(b), all)))); return true; } if (i == 0) { return false; } disj.push_back(m_util.str.mk_contains(m_util.str.mk_concat(m_lhs.size() - i, m_lhs.c_ptr() + i), b)); return true; } disj.push_back(m_util.str.mk_is_empty(b)); return true; } expr* seq_rewriter::concat_non_empty(unsigned n, expr* const* as) { SASSERT(n > 0); ptr_vector bs; for (unsigned i = 0; i < n; ++i) { if (m_util.str.is_unit(as[i]) || m_util.str.is_string(as[i])) { bs.push_back(as[i]); } } if (bs.empty()) { return m_util.str.mk_empty(m().get_sort(as[0])); } else { return m_util.str.mk_concat(bs.size(), bs.c_ptr()); } } bool seq_rewriter::set_empty(unsigned sz, expr* const* es, bool all, expr_ref_vector& lhs, expr_ref_vector& rhs) { zstring s; expr* emp = nullptr; for (unsigned i = 0; i < sz; ++i) { if (m_util.str.is_unit(es[i])) { if (all) return false; } else if (m_util.str.is_empty(es[i])) { continue; } else if (m_util.str.is_string(es[i], s)) { if (all) { SASSERT(s.length() > 0); return false; } } else { emp = emp?emp:m_util.str.mk_empty(m().get_sort(es[i])); lhs.push_back(emp); rhs.push_back(es[i]); } } return true; } bool seq_rewriter::min_length(unsigned n, expr* const* es, unsigned& len) { zstring s; bool bounded = true; len = 0; for (unsigned i = 0; i < n; ++i) { if (m_util.str.is_unit(es[i])) { ++len; } else if (m_util.str.is_empty(es[i])) { continue; } else if (m_util.str.is_string(es[i], s)) { len += s.length(); } else { bounded = false; } } return bounded; } bool seq_rewriter::is_string(unsigned n, expr* const* es, zstring& s) const { zstring s1; expr* e; 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) && m_util.is_const_char(e, ch)) { s = s + zstring(ch); } else { return false; } } return true; } bool seq_rewriter::solve_itos(unsigned szl, expr* const* ls, unsigned szr, expr* const* rs, expr_ref_vector& lhs, expr_ref_vector& rhs, bool& is_sat) { expr* l, *r; is_sat = true; if (szl == 1 && m_util.str.is_itos(ls[0], l)) { if (szr == 1 && m_util.str.is_itos(rs[0], r)) { lhs.push_back(l); rhs.push_back(r); return true; } zstring s; if (is_string(szr, rs, s)) { std::string s1 = s.encode(); rational r(s1.c_str()); if (s1 == r.to_string()) { lhs.push_back(l); rhs.push_back(m_autil.mk_numeral(r, true)); return true; } } } if (szr == 1 && szl >= 1 && m_util.str.is_itos(rs[0], r) && !m_util.str.is_itos(ls[0])) { return solve_itos(szr, rs, szl, ls, rhs, lhs, is_sat); } return false; } bool seq_rewriter::length_constrained(unsigned szl, expr* const* l, unsigned szr, expr* const* r, expr_ref_vector& lhs, expr_ref_vector& rhs, bool& is_sat) { is_sat = true; unsigned len1 = 0, len2 = 0; bool bounded1 = min_length(szl, l, len1); bool bounded2 = min_length(szr, r, len2); if (bounded1 && len1 < len2) { is_sat = false; return true; } if (bounded2 && len2 < len1) { is_sat = false; return true; } if (bounded1 && len1 == len2 && len1 > 0) { is_sat = set_empty(szr, r, false, lhs, rhs); if (is_sat) { lhs.push_back(concat_non_empty(szl, l)); rhs.push_back(concat_non_empty(szr, r)); //split_units(lhs, rhs); } return true; } if (bounded2 && len1 == len2 && len1 > 0) { is_sat = set_empty(szl, l, false, lhs, rhs); if (is_sat) { lhs.push_back(concat_non_empty(szl, l)); rhs.push_back(concat_non_empty(szr, r)); //split_units(lhs, rhs); } return true; } return false; } void seq_rewriter::split_units(expr_ref_vector& lhs, expr_ref_vector& rhs) { expr* a, *b, *a1, *b1, *a2, *b2; while (true) { if (m_util.str.is_unit(lhs.back(), a) && m_util.str.is_unit(rhs.back(), b)) { lhs[lhs.size()-1] = a; rhs[rhs.size()-1] = b; break; } if (m_util.str.is_concat(lhs.back(), a, a2) && m_util.str.is_unit(a, a1) && m_util.str.is_concat(rhs.back(), b, b2) && m_util.str.is_unit(b, b1)) { expr_ref _pin_a(lhs.back(), m()), _pin_b(rhs.back(), m()); lhs[lhs.size()-1] = a1; rhs[rhs.size()-1] = b1; lhs.push_back(a2); rhs.push_back(b2); } else { break; } } } bool seq_rewriter::is_epsilon(expr* e) const { expr* e1; return m_util.re.is_to_re(e, e1) && m_util.str.is_empty(e1); } bool seq_rewriter::is_subsequence(unsigned szl, expr* const* l, unsigned szr, expr* const* r, expr_ref_vector& lhs, expr_ref_vector& rhs, bool& is_sat) { is_sat = true; if (szl == szr) return false; if (szr < szl) { std::swap(szl, szr); std::swap(l, r); } uint_set rpos; for (unsigned i = 0; i < szl; ++i) { bool found = false; unsigned j = 0; bool is_unit = m_util.str.is_unit(l[i]); for (; !found && j < szr; ++j) { found = !rpos.contains(j) && (l[i] == r[j] || (is_unit && m_util.str.is_unit(r[j]))); } if (!found) { return false; } SASSERT(0 < j && j <= szr); rpos.insert(j-1); } // if we reach here, then every element of l is contained in r in some position. // or each non-unit in l is matched by a non-unit in r, and otherwise, the non-units match up. bool change = false; ptr_vector rs; for (unsigned j = 0; j < szr; ++j) { if (rpos.contains(j)) { rs.push_back(r[j]); } else if (!set_empty(1, r + j, true, lhs, rhs)) { is_sat = false; return true; } else { change = true; } } if (!change) { return false; } SASSERT(szl == rs.size()); if (szl > 0) { lhs.push_back(m_util.str.mk_concat(szl, l)); rhs.push_back(m_util.str.mk_concat(szl, rs.c_ptr())); } return true; } z3-z3-4.8.7/src/ast/rewriter/seq_rewriter.h000066400000000000000000000167071356505360400205470ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: seq_rewriter.h Abstract: Basic rewriting rules for sequences constraints. Author: Nikolaj Bjorner (nbjorner) 2015-12-5 Notes: --*/ #ifndef SEQ_REWRITER_H_ #define SEQ_REWRITER_H_ #include "ast/seq_decl_plugin.h" #include "ast/arith_decl_plugin.h" #include "ast/rewriter/rewriter_types.h" #include "util/params.h" #include "util/lbool.h" #include "math/automata/automaton.h" #include "math/automata/symbolic_automata.h" class sym_expr { enum ty { t_char, t_pred, t_not, t_range }; 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), 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, 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 { public: void inc_ref(sym_expr* s) { if (s) s->inc_ref(); } void dec_ref(sym_expr* s) { if (s) s->dec_ref(); } }; class expr_solver { public: virtual ~expr_solver() {} virtual lbool check_sat(expr* e) = 0; }; typedef automaton eautomaton; class re2automaton { typedef boolean_algebra boolean_algebra_t; typedef symbolic_automata symbolic_automata_t; ast_manager& m; sym_expr_manager sm; seq_util u; scoped_ptr m_solver; scoped_ptr m_ba; scoped_ptr m_sa; eautomaton* re2aut(expr* e); eautomaton* seq2aut(expr* e); public: re2automaton(ast_manager& m); ~re2automaton(); eautomaton* operator()(expr* e); void set_solver(expr_solver* solver); bool has_solver() const { return m_solver; } eautomaton* mk_product(eautomaton *a1, eautomaton *a2); }; /** \brief Cheap rewrite rules for seq constraints */ class seq_rewriter { seq_util m_util; arith_util m_autil; re2automaton m_re2aut; expr_ref_vector m_es, m_lhs, m_rhs; br_status mk_seq_unit(expr* e, expr_ref& result); br_status mk_seq_concat(expr* a, expr* b, expr_ref& result); br_status mk_seq_length(expr* a, expr_ref& result); 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_nth_i(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_last_index(expr* a, expr* b, 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); br_status mk_seq_suffix(expr* a, expr* b, expr_ref& result); br_status mk_str_itos(expr* a, expr_ref& result); br_status mk_str_stoi(expr* a, expr_ref& result); br_status mk_str_in_regexp(expr* a, expr* b, expr_ref& result); br_status mk_str_to_regexp(expr* a, expr_ref& result); br_status mk_str_le(expr* a, expr* b, expr_ref& result); br_status mk_re_concat(expr* a, expr* b, expr_ref& result); br_status mk_re_union(expr* a, expr* b, expr_ref& result); br_status mk_re_inter(expr* a, expr* b, expr_ref& result); br_status mk_re_complement(expr* a, expr_ref& result); br_status mk_re_star(expr* a, expr_ref& result); br_status mk_re_plus(expr* a, expr_ref& result); br_status mk_re_opt(expr* a, expr_ref& result); br_status mk_re_loop(func_decl* f, unsigned num_args, expr* const* args, expr_ref& result); br_status mk_re_range(expr* lo, expr* hi, expr_ref& result); bool cannot_contain_prefix(expr* a, expr* b); bool cannot_contain_suffix(expr* a, expr* b); bool is_suffix(expr* s, expr* offset, expr* len); bool set_empty(unsigned sz, expr* const* es, bool all, expr_ref_vector& lhs, expr_ref_vector& rhs); bool is_subsequence(unsigned n, expr* const* l, unsigned m, expr* const* r, expr_ref_vector& lhs, expr_ref_vector& rhs, bool& is_sat); bool length_constrained(unsigned n, expr* const* l, unsigned m, expr* const* r, expr_ref_vector& lhs, expr_ref_vector& rhs, bool& is_sat); bool solve_itos(unsigned n, expr* const* l, unsigned m, expr* const* r, expr_ref_vector& lhs, expr_ref_vector& rhs, bool& is_sat); bool min_length(unsigned n, expr* const* es, unsigned& len); expr* concat_non_empty(unsigned n, expr* const* es); bool is_string(unsigned n, expr* const* es, zstring& s) const; void add_next(u_map& next, expr_ref_vector& trail, unsigned idx, expr* cond); bool is_sequence(expr* e, expr_ref_vector& seq); bool is_sequence(eautomaton& aut, expr_ref_vector& seq); bool is_epsilon(expr* e) const; void split_units(expr_ref_vector& lhs, expr_ref_vector& rhs); bool get_lengths(expr* e, expr_ref_vector& lens, rational& pos); public: seq_rewriter(ast_manager & m, params_ref const & p = params_ref()): m_util(m), m_autil(m), m_re2aut(m), m_es(m), m_lhs(m), m_rhs(m) { } ast_manager & m() const { return m_util.get_manager(); } family_id get_fid() const { return m_util.get_family_id(); } void updt_params(params_ref const & p) {} static void get_param_descrs(param_descrs & r) {} void set_solver(expr_solver* solver) { m_re2aut.set_solver(solver); } bool has_solver() { return m_re2aut.has_solver(); } br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result); br_status mk_eq_core(expr * lhs, expr * rhs, expr_ref & result); bool reduce_eq(expr* l, expr* r, expr_ref_vector& lhs, expr_ref_vector& rhs, bool& change); bool reduce_eq(expr_ref_vector& ls, expr_ref_vector& rs, expr_ref_vector& lhs, expr_ref_vector& rhs, bool& change); bool reduce_contains(expr* a, expr* b, expr_ref_vector& disj); void add_seqs(expr_ref_vector const& ls, expr_ref_vector const& rs, expr_ref_vector& lhs, expr_ref_vector& rhs); }; #endif z3-z3-4.8.7/src/ast/rewriter/th_rewriter.cpp000066400000000000000000000765101356505360400207230ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: th_rewriter.h Abstract: Rewriter for applying all builtin (cheap) theory rewrite rules. Author: Leonardo (leonardo) 2011-04-07 Notes: --*/ #include "ast/rewriter/th_rewriter.h" #include "ast/rewriter/rewriter_params.hpp" #include "ast/rewriter/bool_rewriter.h" #include "ast/rewriter/arith_rewriter.h" #include "ast/rewriter/bv_rewriter.h" #include "ast/rewriter/datatype_rewriter.h" #include "ast/rewriter/array_rewriter.h" #include "ast/rewriter/fpa_rewriter.h" #include "ast/rewriter/dl_rewriter.h" #include "ast/rewriter/pb_rewriter.h" #include "ast/rewriter/seq_rewriter.h" #include "ast/rewriter/rewriter_def.h" #include "ast/rewriter/var_subst.h" #include "ast/expr_substitution.h" #include "ast/ast_smt2_pp.h" #include "ast/ast_pp.h" #include "ast/ast_util.h" #include "ast/well_sorted.h" namespace { struct th_rewriter_cfg : public default_rewriter_cfg { bool_rewriter m_b_rw; arith_rewriter m_a_rw; bv_rewriter m_bv_rw; array_rewriter m_ar_rw; datatype_rewriter m_dt_rw; fpa_rewriter m_f_rw; dl_rewriter m_dl_rw; pb_rewriter m_pb_rw; seq_rewriter m_seq_rw; arith_util m_a_util; bv_util m_bv_util; unsigned long long m_max_memory; // in bytes unsigned m_max_steps; bool m_pull_cheap_ite; bool m_flat; bool m_cache_all; bool m_push_ite_arith; bool m_push_ite_bv; bool m_ignore_patterns_on_ground_qbody; bool m_rewrite_patterns; // substitution support expr_dependency_ref m_used_dependencies; // set of dependencies of used substitutions expr_substitution * m_subst; ast_manager & m() const { return m_b_rw.m(); } void updt_local_params(params_ref const & _p) { rewriter_params p(_p); m_flat = p.flat(); m_max_memory = megabytes_to_bytes(p.max_memory()); m_max_steps = p.max_steps(); m_pull_cheap_ite = p.pull_cheap_ite(); m_cache_all = p.cache_all(); m_push_ite_arith = p.push_ite_arith(); m_push_ite_bv = p.push_ite_bv(); m_ignore_patterns_on_ground_qbody = p.ignore_patterns_on_ground_qbody(); m_rewrite_patterns = p.rewrite_patterns(); } void updt_params(params_ref const & p) { m_b_rw.updt_params(p); m_a_rw.updt_params(p); m_bv_rw.updt_params(p); m_ar_rw.updt_params(p); m_f_rw.updt_params(p); m_seq_rw.updt_params(p); updt_local_params(p); } bool flat_assoc(func_decl * f) const { if (!m_flat) return false; family_id fid = f->get_family_id(); if (fid == null_family_id) return false; decl_kind k = f->get_decl_kind(); if (fid == m_b_rw.get_fid()) return k == OP_AND || k == OP_OR; if (fid == m_a_rw.get_fid()) return k == OP_ADD; if (fid == m_bv_rw.get_fid()) return k == OP_BADD || k == OP_BOR || k == OP_BAND || k == OP_BXOR; return false; } bool rewrite_patterns() const { return m_rewrite_patterns; } bool cache_all_results() const { return m_cache_all; } bool max_steps_exceeded(unsigned num_steps) const { if (memory::get_allocation_size() > m_max_memory) throw rewriter_exception(Z3_MAX_MEMORY_MSG); return num_steps > m_max_steps; } // Return true if t is of the form // (= t #b0) // (= t #b1) // (= #b0 t) // (= #b1 t) bool is_eq_bit(expr * t, expr * & x, unsigned & val) { if (!m().is_eq(t)) return false; expr * lhs = to_app(t)->get_arg(0); if (!m_bv_rw.is_bv(lhs)) return false; if (m_bv_rw.get_bv_size(lhs) != 1) return false; expr * rhs = to_app(t)->get_arg(1); rational v; unsigned sz; if (m_bv_rw.is_numeral(lhs, v, sz)) { x = rhs; val = v.get_unsigned(); SASSERT(val == 0 || val == 1); return true; } if (m_bv_rw.is_numeral(rhs, v, sz)) { x = lhs; val = v.get_unsigned(); SASSERT(val == 0 || val == 1); return true; } return false; } // (iff (= x bit1) A) // ---> // (= x (ite A bit1 bit0)) br_status apply_tamagotchi(expr * lhs, expr * rhs, expr_ref & result) { expr * x; unsigned val; if (is_eq_bit(lhs, x, val)) { result = m().mk_eq(x, m().mk_ite(rhs, m_bv_rw.mk_numeral(val, 1), m_bv_rw.mk_numeral(1-val, 1))); return BR_REWRITE2; } if (is_eq_bit(rhs, x, val)) { result = m().mk_eq(x, m().mk_ite(lhs, m_bv_rw.mk_numeral(val, 1), m_bv_rw.mk_numeral(1-val, 1))); return BR_REWRITE2; } return BR_FAILED; } br_status reduce_app_core(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { family_id fid = f->get_family_id(); if (fid == null_family_id) return BR_FAILED; br_status st = BR_FAILED; if (fid == m_b_rw.get_fid()) { decl_kind k = f->get_decl_kind(); if (k == OP_EQ) { // theory dispatch for = SASSERT(num == 2); family_id s_fid = m().get_sort(args[0])->get_family_id(); if (s_fid == m_a_rw.get_fid()) st = m_a_rw.mk_eq_core(args[0], args[1], result); else if (s_fid == m_bv_rw.get_fid()) st = m_bv_rw.mk_eq_core(args[0], args[1], result); else if (s_fid == m_dt_rw.get_fid()) st = m_dt_rw.mk_eq_core(args[0], args[1], result); else if (s_fid == m_f_rw.get_fid()) st = m_f_rw.mk_eq_core(args[0], args[1], result); else if (s_fid == m_ar_rw.get_fid()) st = m_ar_rw.mk_eq_core(args[0], args[1], result); else if (s_fid == m_seq_rw.get_fid()) st = m_seq_rw.mk_eq_core(args[0], args[1], result); if (st != BR_FAILED) return st; } if (k == OP_EQ) { SASSERT(num == 2); st = apply_tamagotchi(args[0], args[1], result); if (st != BR_FAILED) return st; } if (k == OP_ITE) { SASSERT(num == 3); family_id s_fid = m().get_sort(args[1])->get_family_id(); if (s_fid == m_bv_rw.get_fid()) st = m_bv_rw.mk_ite_core(args[0], args[1], args[2], result); if (st != BR_FAILED) return st; } return m_b_rw.mk_app_core(f, num, args, result); } if (fid == m_a_rw.get_fid()) return m_a_rw.mk_app_core(f, num, args, result); if (fid == m_bv_rw.get_fid()) return m_bv_rw.mk_app_core(f, num, args, result); if (fid == m_ar_rw.get_fid()) return m_ar_rw.mk_app_core(f, num, args, result); if (fid == m_dt_rw.get_fid()) return m_dt_rw.mk_app_core(f, num, args, result); if (fid == m_f_rw.get_fid()) return m_f_rw.mk_app_core(f, num, args, result); if (fid == m_dl_rw.get_fid()) return m_dl_rw.mk_app_core(f, num, args, result); if (fid == m_pb_rw.get_fid()) return m_pb_rw.mk_app_core(f, num, args, result); if (fid == m_seq_rw.get_fid()) return m_seq_rw.mk_app_core(f, num, args, result); return BR_FAILED; } // auxiliary function for pull_ite_core expr * mk_eq_value(expr * lhs, expr * value) { if (m().are_equal(lhs, value)) { return m().mk_true(); } else if (m().are_distinct(lhs, value)) { return m().mk_false(); } return m().mk_eq(lhs, value); } template br_status pull_ite_core(func_decl * p, app * ite, app * value, expr_ref & result) { if (m().is_eq(p)) { result = m().mk_ite(ite->get_arg(0), mk_eq_value(ite->get_arg(1), value), mk_eq_value(ite->get_arg(2), value)); return BR_REWRITE2; } else { if (SWAP) { result = m().mk_ite(ite->get_arg(0), m().mk_app(p, value, ite->get_arg(1)), m().mk_app(p, value, ite->get_arg(2))); return BR_REWRITE2; } else { result = m().mk_ite(ite->get_arg(0), m().mk_app(p, ite->get_arg(1), value), m().mk_app(p, ite->get_arg(2), value)); return BR_REWRITE2; } } } // Return true if t is an ite-value-tree form defined as: // ite-value-tree := (ite c ) // subtree := value // | (ite c ) // bool is_ite_value_tree(expr * t) { if (!m().is_ite(t)) return false; ptr_buffer todo; todo.push_back(to_app(t)); while (!todo.empty()) { app * ite = todo.back(); todo.pop_back(); expr * arg1 = ite->get_arg(1); expr * arg2 = ite->get_arg(2); if (m().is_ite(arg1) && arg1->get_ref_count() == 1) // do not apply on shared terms, since it may blowup todo.push_back(to_app(arg1)); else if (!m().is_value(arg1)) return false; if (m().is_ite(arg2) && arg2->get_ref_count() == 1) // do not apply on shared terms, since it may blowup todo.push_back(to_app(arg2)); else if (!m().is_value(arg2)) return false; } return true; } br_status pull_ite(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { if (num == 2 && m().is_bool(f->get_range()) && !m().is_bool(args[0])) { if (m().is_ite(args[0])) { if (m().is_value(args[1])) return pull_ite_core(f, to_app(args[0]), to_app(args[1]), result); if (m().is_ite(args[1]) && to_app(args[0])->get_arg(0) == to_app(args[1])->get_arg(0)) { // (p (ite C A1 B1) (ite C A2 B2)) --> (ite (p A1 A2) (p B1 B2)) result = m().mk_ite(to_app(args[0])->get_arg(0), m().mk_app(f, to_app(args[0])->get_arg(1), to_app(args[1])->get_arg(1)), m().mk_app(f, to_app(args[0])->get_arg(2), to_app(args[1])->get_arg(2))); return BR_REWRITE2; } } if (m().is_ite(args[1]) && m().is_value(args[0])) return pull_ite_core(f, to_app(args[1]), to_app(args[0]), result); } family_id fid = f->get_family_id(); if (num == 2 && (fid == m().get_basic_family_id() || fid == m_a_rw.get_fid() || fid == m_bv_rw.get_fid())) { // (f v3 (ite c v1 v2)) --> (ite v (f v3 v1) (f v3 v2)) if (m().is_value(args[0]) && is_ite_value_tree(args[1])) return pull_ite_core(f, to_app(args[1]), to_app(args[0]), result); // (f (ite c v1 v2) v3) --> (ite v (f v1 v3) (f v2 v3)) if (m().is_value(args[1]) && is_ite_value_tree(args[0])) return pull_ite_core(f, to_app(args[0]), to_app(args[1]), result); } return BR_FAILED; } br_status pull_ite(expr_ref & result) { expr * t = result.get(); if (is_app(t)) { br_status st = pull_ite(to_app(t)->get_decl(), to_app(t)->get_num_args(), to_app(t)->get_args(), result); if (st != BR_FAILED) return st; } return BR_DONE; } bool is_arith_bv_app(expr * t) const { if (!is_app(t)) return false; family_id fid = to_app(t)->get_family_id(); return ((fid == m_a_rw.get_fid() && m_push_ite_arith) || (fid == m_bv_rw.get_fid() && m_push_ite_bv)); } bool get_neutral_elem(app * t, expr_ref & n) { family_id fid = t->get_family_id(); if (fid == m_a_rw.get_fid()) { switch (t->get_decl_kind()) { case OP_ADD: n = m_a_util.mk_numeral(rational::zero(), m().get_sort(t)); return true; case OP_MUL: n = m_a_util.mk_numeral(rational::one(), m().get_sort(t)); return true; default: return false; } } if (fid == m_bv_rw.get_fid()) { switch (t->get_decl_kind()) { case OP_BADD: n = m_bv_util.mk_numeral(rational::zero(), m().get_sort(t)); return true; case OP_BMUL: n = m_bv_util.mk_numeral(rational::one(), m().get_sort(t)); return true; default: return false; } } return false; } /** \brief Try to "unify" t1 and t2 Examples (+ 2 a) (+ 3 a) --> 2, 3, a (+ 2 a) a --> 2, 0, a ... */ bool unify_core(app * t1, expr * t2, expr_ref & new_t1, expr_ref & new_t2, expr_ref & c, bool & first) { if (t1->get_num_args() != 2) return false; expr * a1 = t1->get_arg(0); expr * b1 = t1->get_arg(1); if (t2 == b1) { if (get_neutral_elem(t1, new_t2)) { new_t1 = a1; c = b1; first = false; return true; } } else if (t2 == a1) { if (get_neutral_elem(t1, new_t2)) { new_t1 = b1; c = a1; first = true; return true; } } else if (is_app_of(t2, t1->get_decl()) && to_app(t2)->get_num_args() == 2) { expr * a2 = to_app(t2)->get_arg(0); expr * b2 = to_app(t2)->get_arg(1); if (b1 == b2) { new_t1 = a1; new_t2 = a2; c = b2; first = false; return true; } if (a1 == a2) { new_t1 = b1; new_t2 = b2; c = a1; first = true; return true; } if (t1->get_decl()->is_commutative()) { if (a1 == b2) { new_t1 = b1; new_t2 = a2; c = a1; first = true; // doesn't really matter for commutative ops. return true; } if (b1 == a2) { new_t1 = a1; new_t2 = b2; c = b1; first = false; // doesn't really matter for commutative ops. return true; } } } return false; } // Return true if t1 and t2 are of the form: // t + a1*x1 + ... + an*xn // t' + a1*x1 + ... + an*xn // Store t in new_t1, t' in new_t2 and (a1*x1 + ... + an*xn) in c. bool unify_add(app * t1, expr * t2, expr_ref & new_t1, expr_ref & new_t2, expr_ref & c) { unsigned num1 = t1->get_num_args(); expr * const * ms1 = t1->get_args(); if (num1 < 2) return false; unsigned num2; expr * const * ms2; if (m_a_util.is_add(t2)) { num2 = to_app(t2)->get_num_args(); ms2 = to_app(t2)->get_args(); } else { num2 = 1; ms2 = &t2; } if (num1 != num2 && num1 != num2 + 1 && num1 != num2 - 1) return false; new_t1 = nullptr; new_t2 = nullptr; expr_fast_mark1 visited1; expr_fast_mark2 visited2; for (unsigned i = 0; i < num1; i++) { expr * arg = ms1[i]; visited1.mark(arg); } for (unsigned i = 0; i < num2; i++) { expr * arg = ms2[i]; visited2.mark(arg); if (visited1.is_marked(arg)) continue; if (new_t2) return false; // more than one missing term new_t2 = arg; } for (unsigned i = 0; i < num1; i++) { expr * arg = ms1[i]; if (visited2.is_marked(arg)) continue; if (new_t1) return false; // more than one missing term new_t1 = arg; } // terms matched... bool is_int = m_a_util.is_int(t1); if (!new_t1) new_t1 = m_a_util.mk_numeral(rational::zero(), is_int); if (!new_t2) new_t2 = m_a_util.mk_numeral(rational::zero(), is_int); // mk common part ptr_buffer args; for (unsigned i = 0; i < num1; i++) { expr * arg = ms1[i]; if (arg == new_t1.get()) continue; args.push_back(arg); } SASSERT(!args.empty()); if (args.size() == 1) c = args[0]; else c = m_a_util.mk_add(args.size(), args.c_ptr()); return true; } bool unify(expr * t1, expr * t2, func_decl * & f, expr_ref & new_t1, expr_ref & new_t2, expr_ref & c, bool & first) { #if 0 // Did not work for ring benchmarks // Hack for handling more complex cases of + apps // such as (+ 2 t1 t2 t3) and (+ 3 t3 t2 t1) if (m_a_util.is_add(t1)) { first = true; // doesn't matter for AC ops f = to_app(t1)->get_decl(); if (unify_add(to_app(t1), t2, new_t1, new_t2, c)) return true; } if (m_a_util.is_add(t2)) { first = true; // doesn't matter for AC ops f = to_app(t2)->get_decl(); if (unify_add(to_app(t2), t1, new_t2, new_t1, c)) return true; } #endif if (is_arith_bv_app(t1)) { f = to_app(t1)->get_decl(); return unify_core(to_app(t1), t2, new_t1, new_t2, c, first); } else if (is_arith_bv_app(t2)) { f = to_app(t2)->get_decl(); return unify_core(to_app(t2), t1, new_t2, new_t1, c, first); } else { return false; } } // Apply transformations of the form // // (ite c (+ k1 a) (+ k2 a)) --> (+ (ite c k1 k2) a) // (ite c (* k1 a) (* k2 a)) --> (* (ite c k1 k2) a) // // These transformations are useful for bit-vector problems, since // they will minimize the number of adders/multipliers/etc br_status push_ite(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { if (!m().is_ite(f)) return BR_FAILED; expr * c = args[0]; expr * t = args[1]; expr * e = args[2]; func_decl * f_prime = nullptr; expr_ref new_t(m()), new_e(m()), common(m()); bool first; TRACE("push_ite", tout << "unifying:\n" << mk_ismt2_pp(t, m()) << "\n" << mk_ismt2_pp(e, m()) << "\n";); if (unify(t, e, f_prime, new_t, new_e, common, first)) { if (first) result = m().mk_app(f_prime, common, m().mk_ite(c, new_t, new_e)); else result = m().mk_app(f_prime, m().mk_ite(c, new_t, new_e), common); return BR_DONE; } TRACE("push_ite", tout << "failed\n";); return BR_FAILED; } br_status push_ite(expr_ref & result) { expr * t = result.get(); if (m().is_ite(t)) { br_status st = push_ite(to_app(t)->get_decl(), to_app(t)->get_num_args(), to_app(t)->get_args(), result); if (st != BR_FAILED) return st; } return BR_DONE; } void count_down_subterm_references(expr * e, map, ptr_eq> & reference_map) { if (is_app(e)) { app * a = to_app(e); for (unsigned i = 0; i < a->get_num_args(); ++i) { expr * child = a->get_arg(i); unsigned countdown = reference_map.get(child, child->get_ref_count()) - 1; reference_map.insert(child, countdown); if (countdown == 0) count_down_subterm_references(child, reference_map); } } } void log_rewrite_axiom_instantiation(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { family_id fid = f->get_family_id(); if (fid == m_b_rw.get_fid()) { decl_kind k = f->get_decl_kind(); if (k == OP_EQ) { SASSERT(num == 2); fid = m().get_sort(args[0])->get_family_id(); } else if (k == OP_ITE) { SASSERT(num == 3); fid = m().get_sort(args[1])->get_family_id(); } } app_ref tmp(m()); tmp = m().mk_app(f, num, args); m().trace_stream() << "[inst-discovered] theory-solving " << static_cast(nullptr) << " " << m().get_family_name(fid) << "# ; #" << tmp->get_id() << "\n"; if (m().proofs_enabled()) result_pr = m().mk_rewrite(tmp, result); tmp = m().mk_eq(tmp, result); m().trace_stream() << "[instance] " << static_cast(nullptr) << " #" << tmp->get_id() << "\n"; // Make sure that both the result term and equality were newly introduced. if (tmp->get_ref_count() == 1) { if (result->get_ref_count() == 1) { map, ptr_eq> reference_map; count_down_subterm_references(result, reference_map); // Any term that was newly introduced by the rewrite step is only referenced within / reachable from the result term. for (auto kv : reference_map) { if (kv.m_value == 0) { m().trace_stream() << "[attach-enode] #" << kv.m_key->get_id() << " 0\n"; } } m().trace_stream() << "[attach-enode] #" << result->get_id() << " 0\n"; } m().trace_stream() << "[attach-enode] #" << tmp->get_id() << " 0\n"; } m().trace_stream() << "[end-of-instance]\n"; m().trace_stream().flush(); } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { result_pr = nullptr; br_status st = reduce_app_core(f, num, args, result); if (st != BR_FAILED && m().has_trace_stream()) { log_rewrite_axiom_instantiation(f, num, args, result, result_pr); } if (st != BR_DONE && st != BR_FAILED) { CTRACE("th_rewriter_step", st != BR_FAILED, tout << f->get_name() << "\n"; for (unsigned i = 0; i < num; i++) tout << mk_ismt2_pp(args[i], m()) << "\n"; tout << "---------->\n" << mk_ismt2_pp(result, m()) << "\n";); return st; } if (m_push_ite_bv || m_push_ite_arith) { if (st == BR_FAILED) st = push_ite(f, num, args, result); else st = push_ite(result); } if (m_pull_cheap_ite) { if (st == BR_FAILED) st = pull_ite(f, num, args, result); else st = pull_ite(result); } CTRACE("th_rewriter_step", st != BR_FAILED, tout << f->get_name() << "\n"; for (unsigned i = 0; i < num; i++) tout << mk_ismt2_pp(args[i], m()) << "\n"; tout << "---------->\n" << mk_ismt2_pp(result, m()) << "\n";); return st; } expr_ref mk_app(func_decl* f, unsigned num_args, expr* const* args) { expr_ref result(m()); proof_ref pr(m()); if (BR_FAILED == reduce_app(f, num_args, args, result, pr)) { result = m().mk_app(f, num_args, args); } return result; } 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) { quantifier_ref q1(m()); proof * p1 = nullptr; if (is_quantifier(new_body) && to_quantifier(new_body)->get_kind() == old_q->get_kind() && to_quantifier(new_body)->get_kind() != lambda_k && !old_q->has_patterns() && !to_quantifier(new_body)->has_patterns()) { quantifier * nested_q = to_quantifier(new_body); ptr_buffer sorts; buffer names; sorts.append(old_q->get_num_decls(), old_q->get_decl_sorts()); names.append(old_q->get_num_decls(), old_q->get_decl_names()); sorts.append(nested_q->get_num_decls(), nested_q->get_decl_sorts()); names.append(nested_q->get_num_decls(), nested_q->get_decl_names()); q1 = m().mk_quantifier(old_q->get_kind(), sorts.size(), sorts.c_ptr(), names.c_ptr(), nested_q->get_expr(), std::min(old_q->get_weight(), nested_q->get_weight()), old_q->get_qid(), old_q->get_skid(), 0, nullptr, 0, nullptr); SASSERT(is_well_sorted(m(), q1)); if (m().proofs_enabled()) { SASSERT(old_q->get_expr() == new_body); p1 = m().mk_pull_quant(old_q, q1); } } else if ( old_q->get_kind() == lambda_k && is_ground(new_body)) { result = m_ar_rw.util().mk_const_array(old_q->get_sort(), new_body); result_pr = nullptr; return true; } else { ptr_buffer new_patterns_buf; ptr_buffer new_no_patterns_buf; new_patterns_buf.append(old_q->get_num_patterns(), new_patterns); new_no_patterns_buf.append(old_q->get_num_no_patterns(), new_no_patterns); remove_duplicates(new_patterns_buf); remove_duplicates(new_no_patterns_buf); q1 = m().update_quantifier(old_q, new_patterns_buf.size(), new_patterns_buf.c_ptr(), new_no_patterns_buf.size(), new_no_patterns_buf.c_ptr(), new_body); TRACE("reduce_quantifier", tout << mk_ismt2_pp(old_q, m()) << "\n----->\n" << mk_ismt2_pp(q1, m()) << "\n";); SASSERT(is_well_sorted(m(), q1)); } SASSERT(m().get_sort(old_q) == m().get_sort(q1)); result = elim_unused_vars(m(), q1, params_ref()); TRACE("reduce_quantifier", tout << "after elim_unused_vars:\n" << result << "\n";); result_pr = nullptr; if (m().proofs_enabled()) { proof * p2 = nullptr; if (q1.get() != result.get() && q1->get_kind() != lambda_k) p2 = m().mk_elim_unused_vars(q1, result); result_pr = m().mk_transitivity(p1, p2); } SASSERT(m().get_sort(old_q) == m().get_sort(result)); return true; } th_rewriter_cfg(ast_manager & m, params_ref const & p): m_b_rw(m, p), m_a_rw(m, p), m_bv_rw(m, p), m_ar_rw(m, p), m_dt_rw(m), m_f_rw(m, p), m_dl_rw(m), m_pb_rw(m), m_seq_rw(m), m_a_util(m), m_bv_util(m), m_used_dependencies(m), m_subst(nullptr) { updt_local_params(p); } void set_substitution(expr_substitution * s) { reset(); m_subst = s; } void reset() { m_subst = nullptr; } bool get_subst(expr * s, expr * & t, proof * & pr) { if (m_subst == nullptr) return false; expr_dependency * d = nullptr; if (m_subst->find(s, t, pr, d)) { m_used_dependencies = m().mk_join(m_used_dependencies, d); return true; } return false; } }; } template class rewriter_tpl; struct th_rewriter::imp : public rewriter_tpl { th_rewriter_cfg m_cfg; imp(ast_manager & m, params_ref const & p): rewriter_tpl(m, m.proofs_enabled(), m_cfg), m_cfg(m, p) { } expr_ref mk_app(func_decl* f, unsigned sz, expr* const* args) { return m_cfg.mk_app(f, sz, args); } void set_solver(expr_solver* solver) { m_cfg.m_seq_rw.set_solver(solver); } }; th_rewriter::th_rewriter(ast_manager & m, params_ref const & p): m_params(p) { m_imp = alloc(imp, m, p); } ast_manager & th_rewriter::m() const { return m_imp->m(); } void th_rewriter::updt_params(params_ref const & p) { m_params = p; m_imp->cfg().updt_params(p); } void th_rewriter::get_param_descrs(param_descrs & r) { bool_rewriter::get_param_descrs(r); arith_rewriter::get_param_descrs(r); bv_rewriter::get_param_descrs(r); array_rewriter::get_param_descrs(r); rewriter_params::collect_param_descrs(r); } th_rewriter::~th_rewriter() { dealloc(m_imp); } unsigned th_rewriter::get_cache_size() const { return m_imp->get_cache_size(); } unsigned th_rewriter::get_num_steps() const { return m_imp->get_num_steps(); } void th_rewriter::cleanup() { ast_manager & m = m_imp->m(); m_imp->~imp(); new (m_imp) imp(m, m_params); } void th_rewriter::reset() { m_imp->reset(); m_imp->cfg().reset(); } void th_rewriter::operator()(expr_ref & term) { expr_ref result(term.get_manager()); m_imp->operator()(term, result); term = std::move(result); } void th_rewriter::operator()(expr * t, expr_ref & result) { m_imp->operator()(t, result); } void th_rewriter::operator()(expr * t, expr_ref & result, proof_ref & result_pr) { m_imp->operator()(t, result, result_pr); } expr_ref th_rewriter::operator()(expr * n, unsigned num_bindings, expr * const * bindings) { return m_imp->operator()(n, num_bindings, bindings); } void th_rewriter::set_substitution(expr_substitution * s) { m_imp->reset(); // reset the cache m_imp->cfg().set_substitution(s); } expr_dependency * th_rewriter::get_used_dependencies() { return m_imp->cfg().m_used_dependencies; } void th_rewriter::reset_used_dependencies() { if (get_used_dependencies() != nullptr) { set_substitution(m_imp->cfg().m_subst); // reset cache preserving subst m_imp->cfg().m_used_dependencies = nullptr; } } expr_ref th_rewriter::mk_app(func_decl* f, unsigned num_args, expr* const* args) { return m_imp->mk_app(f, num_args, args); } void th_rewriter::set_solver(expr_solver* solver) { m_imp->set_solver(solver); } bool th_rewriter::reduce_quantifier(quantifier * old_q, expr * new_body, expr * const * new_patterns, expr * const * new_no_patterns, expr_ref & result, proof_ref & result_pr) { return m_imp->cfg().reduce_quantifier(old_q, new_body, new_patterns, new_no_patterns, result, result_pr); } z3-z3-4.8.7/src/ast/rewriter/th_rewriter.h000066400000000000000000000036301356505360400203610ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: th_rewriter.h Abstract: Rewriter for applying all builtin (cheap) theory rewrite rules. Author: Leonardo (leonardo) 2011-04-07 Notes: --*/ #ifndef TH_REWRITER_H_ #define TH_REWRITER_H_ #include "ast/ast.h" #include "ast/rewriter/rewriter_types.h" #include "util/params.h" class expr_substitution; class expr_solver; class th_rewriter { struct imp; imp * m_imp; params_ref m_params; public: th_rewriter(ast_manager & m, params_ref const & p = params_ref()); ~th_rewriter(); ast_manager & m () const; void updt_params(params_ref const & p); static void get_param_descrs(param_descrs & r); unsigned get_cache_size() const; unsigned get_num_steps() const; void operator()(expr_ref& term); void operator()(expr * t, expr_ref & result); void operator()(expr * t, expr_ref & result, proof_ref & result_pr); expr_ref operator()(expr * n, unsigned num_bindings, expr * const * bindings); expr_ref mk_app(func_decl* f, unsigned num_args, expr* const* args); 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); void cleanup(); void reset(); void set_substitution(expr_substitution * s); // Dependency tracking is very coarse. // The rewriter just keeps accumulating the dependencies of the used substitutions. // The following methods are used to recover and reset them. // Remark: reset_used_dependecies will reset the internal cache if get_used_dependencies() != 0 expr_dependency * get_used_dependencies(); void reset_used_dependencies(); void set_solver(expr_solver* solver); }; #endif z3-z3-4.8.7/src/ast/rewriter/var_subst.cpp000066400000000000000000000177641356505360400204030ustar00rootroot00000000000000/*++ Copyright (c) 2007 Microsoft Corporation Module Name: var_subst.cpp Abstract: Variable substitution. Author: Leonardo (leonardo) 2008-01-10 Notes: --*/ #include "ast/rewriter/var_subst.h" #include "ast/ast_ll_pp.h" #include "ast/ast_pp.h" #include "ast/ast_smt2_pp.h" #include "ast/well_sorted.h" #include "ast/for_each_expr.h" expr_ref var_subst::operator()(expr * n, unsigned num_args, expr * const * args) { expr_ref result(m_reducer.m()); if (is_ground(n)) { result = n; return result; } SASSERT(is_well_sorted(result.m(), n)); m_reducer.reset(); if (m_std_order) m_reducer.set_inv_bindings(num_args, args); else m_reducer.set_bindings(num_args, args); m_reducer(n, result); SASSERT(is_well_sorted(m_reducer.m(), result)); TRACE("var_subst_bug", tout << "m_std_order: " << m_std_order << "\n" << mk_ismt2_pp(n, m_reducer.m()) << "\nusing\n"; for (unsigned i = 0; i < num_args; i++) tout << mk_ismt2_pp(args[i], m_reducer.m()) << "\n"; tout << "\n------>\n"; tout << mk_ismt2_pp(result, m_reducer.m()) << "\n";); return result; } unused_vars_eliminator::unused_vars_eliminator(ast_manager & m, params_ref const & params) : m(m), m_subst(m), m_params(params) { m_ignore_patterns_on_ground_qbody = m_params.get_bool("ignore_patterns_on_ground_qbody", true); } expr_ref unused_vars_eliminator::operator()(quantifier* q) { expr_ref result(m); SASSERT(is_well_sorted(m, q)); TRACE("elim_unused_vars", tout << expr_ref(q, m) << "\n";); if (is_lambda(q)) { result = q; return result; } if (m_ignore_patterns_on_ground_qbody && is_ground(q->get_expr())) { // Ignore patterns if the body is a ground formula. result = q->get_expr(); return result; } if (!q->may_have_unused_vars()) { result = q; return result; } m_used.reset(); m_used.process(q->get_expr()); unsigned num_patterns = q->get_num_patterns(); for (unsigned i = 0; i < num_patterns; i++) m_used.process(q->get_pattern(i)); unsigned num_no_patterns = q->get_num_no_patterns(); for (unsigned i = 0; i < num_no_patterns; i++) m_used.process(q->get_no_pattern(i)); unsigned num_decls = q->get_num_decls(); if (m_used.uses_all_vars(num_decls)) { q->set_no_unused_vars(); result = q; return result; } ptr_buffer used_decl_sorts; buffer used_decl_names; for (unsigned i = 0; i < num_decls; ++i) { if (m_used.contains(num_decls - i - 1)) { used_decl_sorts.push_back(q->get_decl_sort(i)); used_decl_names.push_back(q->get_decl_name(i)); } } unsigned num_removed = 0; expr_ref_buffer var_mapping(m); int next_idx = 0; unsigned sz = m_used.get_max_found_var_idx_plus_1(); for (unsigned i = 0; i < num_decls; ++i) { sort * s = m_used.contains(i); if (s) { var_mapping.push_back(m.mk_var(next_idx, s)); next_idx++; } else { num_removed++; var_mapping.push_back(nullptr); } } // (VAR 0) is in the first position of var_mapping. for (unsigned i = num_decls; i < sz; i++) { sort * s = m_used.contains(i); if (s) var_mapping.push_back(m.mk_var(i - num_removed, s)); else var_mapping.push_back(nullptr); } // Remark: // (VAR 0) should be in the last position of var_mapping. // ... // (VAR (var_mapping.size() - 1)) should be in the first position. std::reverse(var_mapping.c_ptr(), var_mapping.c_ptr() + var_mapping.size()); expr_ref new_expr(m); new_expr = m_subst(q->get_expr(), var_mapping.size(), var_mapping.c_ptr()); if (num_removed == num_decls) { result = new_expr; return result; } expr_ref_buffer new_patterns(m); expr_ref_buffer new_no_patterns(m); for (unsigned i = 0; i < num_patterns; i++) { 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++) { 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(), used_decl_sorts.size(), used_decl_sorts.c_ptr(), used_decl_names.c_ptr(), new_expr, q->get_weight(), q->get_qid(), q->get_skid(), num_patterns, new_patterns.c_ptr(), num_no_patterns, new_no_patterns.c_ptr()); to_quantifier(result)->set_no_unused_vars(); SASSERT(is_well_sorted(m, result)); return result; } expr_ref elim_unused_vars(ast_manager & m, quantifier * q, params_ref const & params) { unused_vars_eliminator el(m, params); expr_ref result = el(q); TRACE("elim_unused_vars", tout << expr_ref(q, m) << " -> " << result << "\n";); return result; } expr_ref instantiate(ast_manager & m, quantifier * q, expr * const * exprs) { var_subst subst(m); expr_ref new_expr(m), result(m); new_expr = subst(q->get_expr(), q->get_num_decls(), exprs); TRACE("var_subst", tout << mk_pp(q, m) << "\n" << new_expr << "\n";); inv_var_shifter shift(m); shift(new_expr, q->get_num_decls(), result); SASSERT(is_well_sorted(m, result)); TRACE("instantiate_bug", tout << mk_ismt2_pp(q, m) << "\nusing\n"; for (unsigned i = 0; i < q->get_num_decls(); i++) tout << mk_ismt2_pp(exprs[i], m) << "\n"; tout << "\n----->\n" << mk_ismt2_pp(result, m) << "\n";); return result; } static void get_free_vars_offset(expr_sparse_mark& mark, ptr_vector& todo, unsigned offset, expr* e, ptr_vector& sorts) { todo.push_back(e); while (!todo.empty()) { e = todo.back(); todo.pop_back(); if (mark.is_marked(e)) { continue; } mark.mark(e, true); switch(e->get_kind()) { case AST_QUANTIFIER: { quantifier* q = to_quantifier(e); expr_sparse_mark mark1; ptr_vector todo1; get_free_vars_offset(mark1, todo1, offset+q->get_num_decls(), q->get_expr(), sorts); break; } case AST_VAR: { var* v = to_var(e); if (v->get_idx() >= offset) { unsigned idx = v->get_idx()-offset; if (sorts.size() <= idx) { sorts.resize(idx+1); } if (!sorts[idx]) { sorts[idx] = v->get_sort(); } SASSERT(sorts[idx] == v->get_sort()); } break; } case AST_APP: { app* a = to_app(e); for (unsigned i = 0; i < a->get_num_args(); ++i) { todo.push_back(a->get_arg(i)); } break; } default: UNREACHABLE(); } } } void get_free_vars(expr* e, ptr_vector& sorts) { expr_sparse_mark mark; ptr_vector todo; get_free_vars_offset(mark, todo, 0, e, sorts); } void get_free_vars(expr_sparse_mark& mark, ptr_vector& todo, expr* e, ptr_vector& sorts) { get_free_vars_offset(mark, todo, 0, e, sorts); } void expr_free_vars::reset() { m_mark.reset(); m_sorts.reset(); SASSERT(m_todo.empty()); } void expr_free_vars::set_default_sort(sort *s) { for (unsigned i = 0; i < m_sorts.size(); ++i) { if (!m_sorts[i]) m_sorts[i] = s; } } void expr_free_vars::operator()(expr* e) { reset(); get_free_vars_offset(m_mark, m_todo, 0, e, m_sorts); } void expr_free_vars::accumulate(expr* e) { SASSERT(m_todo.empty()); get_free_vars_offset(m_mark, m_todo, 0, e, m_sorts); } z3-z3-4.8.7/src/ast/rewriter/var_subst.h000066400000000000000000000054451356505360400200410ustar00rootroot00000000000000/*++ Copyright (c) 2007 Microsoft Corporation Module Name: var_subst.h Abstract: Variable substitution. Author: Leonardo (leonardo) 2008-01-10 Notes: --*/ #ifndef VAR_SUBST_H_ #define VAR_SUBST_H_ #include "ast/rewriter/rewriter.h" #include "ast/used_vars.h" #include "util/params.h" /** \brief Alias for var_shifter class. */ typedef var_shifter shift_vars; /** \brief Variable substitution functor. It substitutes variables by expressions. The expressions may contain variables. */ class var_subst { beta_reducer m_reducer; bool m_std_order; public: var_subst(ast_manager & m, bool std_order = true):m_reducer(m), m_std_order(std_order) {} bool std_order() const { return m_std_order; } /** When std_order() == true, I'm using the same standard used in quantifier instantiation. (VAR 0) is stored in the last position of the array. ... (VAR (num_args - 1)) is stored in the first position of the array. Otherwise, (VAR 0) is stored in the first position, (VAR 1) in the second, and so on. */ expr_ref operator()(expr * n, unsigned num_args, expr * const * args); void reset() { m_reducer.reset(); } }; /** \brief Eliminate the unused variables from \c q. Store the result in \c r. */ class unused_vars_eliminator { ast_manager & m; var_subst m_subst; used_vars m_used; params_ref m_params; bool m_ignore_patterns_on_ground_qbody; public: unused_vars_eliminator(ast_manager & m, params_ref const & params); expr_ref operator()(quantifier* q); }; expr_ref elim_unused_vars(ast_manager & m, quantifier * q, params_ref const & params); /** \brief Instantiate quantifier q using the given exprs. The vector exprs should contain at least q->get_num_decls() expressions. I'm using the same standard used in quantifier instantiation. (VAR 0) is stored in the last position of the array. ... (VAR (q->get_num_decls() - 1)) is stored in the first position of the array. */ expr_ref instantiate(ast_manager & m, quantifier * q, expr * const * exprs); /** \brief Enumerate set of free variables in expression. Return the sorts of the free variables. */ class expr_free_vars { expr_sparse_mark m_mark; ptr_vector m_sorts; ptr_vector m_todo; public: void reset(); void operator()(expr* e); void accumulate(expr* e); bool empty() const { return m_sorts.empty(); } unsigned size() const { return m_sorts.size(); } sort* operator[](unsigned idx) const { return m_sorts[idx]; } bool contains(unsigned idx) const { return idx < m_sorts.size() && m_sorts[idx] != 0; } void set_default_sort(sort* s); void reverse() { m_sorts.reverse(); } sort*const* c_ptr() const { return m_sorts.c_ptr(); } }; #endif z3-z3-4.8.7/src/ast/scoped_proof.h000066400000000000000000000020271356505360400166410ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: scoped_proof.h Abstract: Scoped proof environments. Toggles enabling proofs. Author: Nikolaj Bjorner (nbjorner) 2013-08-28 Revision History: --*/ #ifndef SCOPED_PROOF_H_ #define SCOPED_PROOF_H_ #include "ast/ast.h" class scoped_proof_mode { ast_manager& m; proof_gen_mode m_mode; public: scoped_proof_mode(ast_manager& m, proof_gen_mode mode): m(m) { m_mode = m.proof_mode(); m.toggle_proof_mode(mode); } ~scoped_proof_mode() { m.toggle_proof_mode(m_mode); } }; class scoped_proof : public scoped_proof_mode { public: scoped_proof(ast_manager& m): scoped_proof_mode(m, PGM_ENABLED) {} }; class scoped_no_proof : public scoped_proof_mode { public: scoped_no_proof(ast_manager& m): scoped_proof_mode(m, PGM_DISABLED) {} }; class scoped_restore_proof : public scoped_proof_mode { public: scoped_restore_proof(ast_manager& m): scoped_proof_mode(m, m.proof_mode()) {} }; #endif z3-z3-4.8.7/src/ast/seq_decl_plugin.cpp000066400000000000000000001174101356505360400176520ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: seq_decl_plugin.h Abstract: decl_plugin for the theory of sequences Author: Nikolaj Bjorner (nbjorner) 2011-14-11 Revision History: --*/ #include "ast/seq_decl_plugin.h" #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) { if ('0' <= ch && ch <= '9') { d = ch - '0'; return true; } if ('A' <= ch && ch <= 'F') { d = 10 + ch - 'A'; return true; } if ('a' <= ch && ch <= 'f') { d = 10 + ch - 'a'; return true; } return false; } static bool is_octal_digit(char ch, unsigned& d) { if ('0' <= ch && ch <= '7') { d = ch - '0'; return true; } return false; } static bool is_escape_char(char const *& s, unsigned& result) { unsigned d1, d2, d3; if (*s != '\\' || *(s + 1) == 0) { return false; } if (*(s + 1) == 'x' && is_hex_digit(*(s + 2), d1) && is_hex_digit(*(s + 3), d2)) { result = d1*16 + d2; s += 4; return true; } /* C-standard octal escapes: either 1, 2, or 3 octal digits, * stopping either at 3 digits or at the first non-digit character. */ /* 1 octal digit */ if (is_octal_digit(*(s + 1), d1) && !is_octal_digit(*(s + 2), d2)) { result = d1; s += 2; return true; } /* 2 octal digits */ if (is_octal_digit(*(s + 1), d1) && is_octal_digit(*(s + 2), d2) && !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)) { result = d1*64 + d2*8 + d3; s += 4; return true; } switch (*(s + 1)) { case 'a': result = '\a'; s += 2; return true; case 'b': result = '\b'; s += 2; return true; #if 0 case 'e': result = '\e'; s += 2; return true; #endif case 'f': result = '\f'; s += 2; return true; case 'n': result = '\n'; s += 2; return true; case 'r': result = '\r'; s += 2; return true; case 't': result = '\t'; s += 2; return true; case 'v': result = '\v'; s += 2; return true; default: result = *(s + 1); s += 2; return true; } return false; } zstring::zstring(encoding enc): m_encoding(enc) {} zstring::zstring(char const* s, encoding enc): m_encoding(enc) { unsigned mask = 0xFF; // TBD for UTF while (*s) { unsigned ch; if (is_escape_char(s, ch)) { m_buffer.push_back(ch & mask); } else { m_buffer.push_back(*s & mask); ++s; } } } zstring::zstring(zstring const& other) { m_buffer = other.m_buffer; m_encoding = other.m_encoding; } zstring::zstring(unsigned sz, unsigned const* s, encoding enc) { m_buffer.append(sz, s); m_encoding = enc; } zstring::zstring(unsigned num_bits, bool const* ch) { SASSERT(num_bits == 8 || num_bits == 16); m_encoding = (num_bits == 8)?ascii:unicode; unsigned n = 0; for (unsigned i = 0; i < num_bits; ++i) { n |= (((unsigned)ch[i]) << i); } m_buffer.push_back(n); } zstring::zstring(unsigned ch, encoding enc) { m_encoding = enc; m_buffer.push_back(ch & ((enc == ascii)?0x000000FF:0x0000FFFF)); } zstring& zstring::operator=(zstring const& other) { m_encoding = other.m_encoding; m_buffer.reset(); m_buffer.append(other.m_buffer); return *this; } zstring zstring::replace(zstring const& src, zstring const& dst) const { zstring result(m_encoding); if (length() < src.length()) { return zstring(*this); } if (src.length() == 0) { return dst + zstring(*this); } bool found = false; for (unsigned i = 0; i < length(); ++i) { bool eq = !found && i + src.length() <= length(); for (unsigned j = 0; eq && j < src.length(); ++j) { eq = m_buffer[i+j] == src[j]; } if (eq) { result.m_buffer.append(dst.m_buffer); found = true; i += src.length() - 1; } else { result.m_buffer.push_back(m_buffer[i]); } } return result; } static const char esc_table[32][6] = { "\\x00", "\\x01", "\\x02", "\\x03", "\\x04", "\\x05", "\\x06", "\\x07", "\\x08", "\\x09", "\\n", "\\v", "\\f", "\\r", "\\x0E", "\\x0F", "\\x10", "\\x11", "\\x12", "\\x13", "\\x14", "\\x15", "\\x16", "\\x17", "\\x18", "\\x19", "\\x1A", "\\x1B", "\\x1C", "\\x1D", "\\x1E", "\\x1F" }; std::string zstring::encode() const { SASSERT(m_encoding == ascii); std::ostringstream strm; for (unsigned i = 0; i < m_buffer.size(); ++i) { unsigned char ch = m_buffer[i]; if (0 <= ch && ch < 32) { strm << esc_table[ch]; } else if (ch == '\\') { strm << "\\\\"; } else if (ch >= 128) { strm << "\\x" << std::hex << (unsigned)ch << std::dec; } else { strm << (char)(ch); } } return strm.str(); } std::string zstring::as_string() const { SASSERT(m_encoding == ascii); std::ostringstream strm; for (unsigned i = 0; i < m_buffer.size(); ++i) { unsigned char ch = m_buffer[i]; strm << (char)(ch); } return strm.str(); } bool zstring::suffixof(zstring const& other) const { if (length() > other.length()) return false; bool suffix = true; for (unsigned i = 0; suffix && i < length(); ++i) { suffix = m_buffer[length()-i-1] == other[other.length()-i-1]; } return suffix; } bool zstring::prefixof(zstring const& other) const { if (length() > other.length()) return false; bool prefix = true; for (unsigned i = 0; prefix && i < length(); ++i) { prefix = m_buffer[i] == other[i]; } return prefix; } bool zstring::contains(zstring const& other) const { if (other.length() > length()) return false; unsigned last = length() - other.length(); bool cont = false; for (unsigned i = 0; !cont && i <= last; ++i) { cont = true; for (unsigned j = 0; cont && j < other.length(); ++j) { cont = other[j] == m_buffer[j+i]; } } return cont; } int zstring::indexof(zstring const& other, int offset) const { SASSERT(offset >= 0); if (static_cast(offset) <= length() && other.length() == 0) return offset; if (static_cast(offset) == length()) return -1; if (other.length() + offset > length()) return -1; unsigned last = length() - other.length(); for (unsigned i = static_cast(offset); i <= last; ++i) { bool prefix = true; for (unsigned j = 0; prefix && j < other.length(); ++j) { prefix = m_buffer[i + j] == other[j]; } if (prefix) { return static_cast(i); } } return -1; } int zstring::last_indexof(zstring const& other) const { if (other.length() == 0) return length(); if (other.length() > length()) return -1; for (unsigned last = length() - other.length(); last-- > 0; ) { bool suffix = true; for (unsigned j = 0; suffix && j < other.length(); ++j) { suffix = m_buffer[last + j] == other[j]; } if (suffix) { return static_cast(last); } } return -1; } zstring zstring::extract(int offset, int len) const { zstring result(m_encoding); SASSERT(0 <= offset && 0 <= len); int last = std::min(offset+len, static_cast(length())); for (int i = offset; i < last; ++i) { result.m_buffer.push_back(m_buffer[i]); } return result; } zstring zstring::operator+(zstring const& other) const { SASSERT(m_encoding == other.m_encoding); zstring result(*this); result.m_buffer.append(other.m_buffer); return result; } bool zstring::operator==(const zstring& other) const { // two strings are equal iff they have the same length and characters if (length() != other.length()) { return false; } for (unsigned i = 0; i < length(); ++i) { if (m_buffer[i] != other[i]) { return false; } } return true; } bool zstring::operator!=(const zstring& other) const { return !(*this == other); } std::ostream& operator<<(std::ostream &os, const zstring &str) { return os << str.encode(); } bool operator<(const zstring& lhs, const zstring& rhs) { // This has the same semantics as strcmp() unsigned len = lhs.length(); if (rhs.length() < len) { len = rhs.length(); } for (unsigned i = 0; i < len; ++i) { unsigned Li = lhs[i]; unsigned Ri = rhs[i]; if (Li < Ri) { return true; } else if (Li > Ri) { return false; } } // at this point, all compared characters are equal, // so decide based on the relative lengths return lhs.length() < rhs.length(); } seq_decl_plugin::seq_decl_plugin(): m_init(false), m_stringc_sym("String"), m_charc_sym("Char"), m_string(nullptr), m_char(nullptr), m_re(nullptr), m_has_re(false) {} void seq_decl_plugin::finalize() { for (unsigned i = 0; i < m_sigs.size(); ++i) dealloc(m_sigs[i]); m_manager->dec_ref(m_string); m_manager->dec_ref(m_char); m_manager->dec_ref(m_re); } bool seq_decl_plugin::is_sort_param(sort* s, unsigned& idx) { return s->get_name().is_numerical() && (idx = s->get_name().get_num(), true); } bool seq_decl_plugin::match(ptr_vector& binding, sort* s, sort* sP) { if (s == sP) return true; unsigned idx; if (is_sort_param(sP, idx)) { if (binding.size() <= idx) binding.resize(idx+1); if (binding[idx] && (binding[idx] != s)) return false; TRACE("seq_verbose", tout << "setting binding @ " << idx << " to " << mk_pp(s, *m_manager) << "\n";); binding[idx] = s; return true; } 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); 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; } } return true; } else { TRACE("seq", tout << "Could not match " << mk_pp(s, *m_manager) << " and " << mk_pp(sP, *m_manager) << "\n";); return false; } } /* \brief match right associative operator. */ void seq_decl_plugin::match_right_assoc(psig& sig, unsigned dsz, sort *const* dom, sort* range, sort_ref& range_out) { ptr_vector binding; ast_manager& m = *m_manager; TRACE("seq_verbose", tout << sig.m_name << ": "; for (unsigned i = 0; i < dsz; ++i) tout << mk_pp(dom[i], m) << " "; if (range) tout << " range: " << mk_pp(range, m); tout << "\n";); if (dsz == 0) { std::ostringstream strm; strm << "Unexpected number of arguments to '" << sig.m_name << "' "; strm << "at least one argument expected " << dsz << " given"; m.raise_exception(strm.str()); } bool is_match = true; for (unsigned i = 0; is_match && i < dsz; ++i) { SASSERT(dom[i]); is_match = match(binding, dom[i], sig.m_dom[0].get()); } if (range && is_match) { is_match = match(binding, range, sig.m_range); } if (!is_match) { std::ostringstream strm; strm << "Sort of function '" << sig.m_name << "' "; strm << "does not match the declared type. Given domain: "; for (unsigned i = 0; i < dsz; ++i) { strm << mk_pp(dom[i], m) << " "; } if (range) { strm << " and range: " << mk_pp(range, m); } m.raise_exception(strm.str()); } range_out = apply_binding(binding, sig.m_range); SASSERT(range_out); TRACE("seq_verbose", tout << mk_pp(range_out, m) << "\n";); } void seq_decl_plugin::match(psig& sig, unsigned dsz, sort *const* dom, sort* range, sort_ref& range_out) { m_binding.reset(); ast_manager& m = *m_manager; if (sig.m_dom.size() != dsz) { std::ostringstream strm; strm << "Unexpected number of arguments to '" << sig.m_name << "' "; strm << sig.m_dom.size() << " arguments expected " << dsz << " given"; m.raise_exception(strm.str()); } bool is_match = true; for (unsigned i = 0; is_match && i < dsz; ++i) { is_match = match(m_binding, dom[i], sig.m_dom[i].get()); } if (range && is_match) { is_match = match(m_binding, range, sig.m_range); } if (!is_match) { std::ostringstream strm; strm << "Sort of polymorphic function '" << sig.m_name << "' "; strm << "does not match the declared type. "; strm << "\nGiven domain: "; for (unsigned i = 0; i < dsz; ++i) { strm << mk_pp(dom[i], m) << " "; } if (range) { strm << " and range: " << mk_pp(range, m); } strm << "\nExpected domain: "; for (unsigned i = 0; i < dsz; ++i) { strm << mk_pp(sig.m_dom[i].get(), m) << " "; } m.raise_exception(strm.str()); } if (!range && dsz == 0) { std::ostringstream strm; strm << "Sort of polymorphic function '" << sig.m_name << "' "; strm << "is ambiguous. Function takes no arguments and sort of range has not been constrained"; m.raise_exception(strm.str()); } range_out = apply_binding(m_binding, sig.m_range); SASSERT(range_out); } sort* seq_decl_plugin::apply_binding(ptr_vector const& binding, sort* s) { unsigned i; if (is_sort_param(s, i)) { if (binding.size() <= i || !binding[i]) { m_manager->raise_exception("Expecting type parameter to be bound"); } return binding[i]; } if (is_sort_of(s, m_family_id, SEQ_SORT) || is_sort_of(s, m_family_id, RE_SORT)) { SASSERT(s->get_num_parameters() == 1); SASSERT(s->get_parameter(0).is_ast()); SASSERT(is_sort(s->get_parameter(0).get_ast())); sort* p = apply_binding(binding, to_sort(s->get_parameter(0).get_ast())); parameter param(p); return mk_sort(s->get_decl_kind(), 1, ¶m); } return s; } void seq_decl_plugin::init() { if(m_init) return; ast_manager& m = *m_manager; m_init = true; sort* A = m.mk_uninterpreted_sort(symbol((unsigned)0)); sort* strT = m_string; parameter paramA(A); parameter paramS(strT); sort* seqA = m.mk_sort(m_family_id, SEQ_SORT, 1, ¶mA); parameter paramSA(seqA); sort* reA = m.mk_sort(m_family_id, RE_SORT, 1, ¶mSA); sort* reT = m.mk_sort(m_family_id, RE_SORT, 1, ¶mS); sort* boolT = m.mk_bool_sort(); sort* intT = arith_util(m).mk_int(); sort* predA = array_util(m).mk_array_sort(A, boolT); sort* seqAseqA[2] = { seqA, seqA }; sort* seqAreA[2] = { seqA, reA }; sort* reAreA[2] = { reA, reA }; sort* seqAint2T[3] = { seqA, intT, intT }; sort* seq2AintT[3] = { seqA, seqA, intT }; sort* str2T[2] = { strT, strT }; sort* str3T[3] = { strT, strT, strT }; sort* strTint2T[3] = { strT, intT, intT }; sort* strTreT[2] = { strT, reT }; sort* str2TintT[3] = { strT, strT, intT }; sort* seqAintT[2] = { seqA, intT }; sort* seq3A[3] = { seqA, seqA, seqA }; sort* reTintT[2] = { reT, intT }; m_sigs.resize(LAST_SEQ_OP); // TBD: have (par ..) construct and load parameterized signature from premable. m_sigs[OP_SEQ_UNIT] = alloc(psig, m, "seq.unit", 1, 1, &A, seqA); m_sigs[OP_SEQ_EMPTY] = alloc(psig, m, "seq.empty", 1, 0, nullptr, seqA); m_sigs[OP_SEQ_CONCAT] = alloc(psig, m, "seq.++", 1, 2, seqAseqA, seqA); m_sigs[OP_SEQ_PREFIX] = alloc(psig, m, "seq.prefixof", 1, 2, seqAseqA, boolT); m_sigs[OP_SEQ_SUFFIX] = alloc(psig, m, "seq.suffixof", 1, 2, seqAseqA, boolT); m_sigs[OP_SEQ_CONTAINS] = alloc(psig, m, "seq.contains", 1, 2, seqAseqA, boolT); m_sigs[OP_SEQ_EXTRACT] = alloc(psig, m, "seq.extract", 1, 3, seqAint2T, seqA); m_sigs[OP_SEQ_REPLACE] = alloc(psig, m, "seq.replace", 1, 3, seq3A, seqA); m_sigs[OP_SEQ_INDEX] = alloc(psig, m, "seq.indexof", 1, 3, seq2AintT, intT); m_sigs[OP_SEQ_LAST_INDEX] = alloc(psig, m, "seq.last_indexof", 1, 2, seqAseqA, intT); m_sigs[OP_SEQ_AT] = alloc(psig, m, "seq.at", 1, 2, seqAintT, seqA); m_sigs[OP_SEQ_NTH] = alloc(psig, m, "seq.nth", 1, 2, seqAintT, A); m_sigs[OP_SEQ_NTH_I] = alloc(psig, m, "seq.nth_i", 1, 2, seqAintT, A); m_sigs[OP_SEQ_NTH_U] = alloc(psig, m, "seq.nth_u", 1, 2, seqAintT, A); m_sigs[OP_SEQ_LENGTH] = alloc(psig, m, "seq.len", 1, 1, &seqA, intT); m_sigs[OP_RE_PLUS] = alloc(psig, m, "re.+", 1, 1, &reA, reA); m_sigs[OP_RE_STAR] = alloc(psig, m, "re.*", 1, 1, &reA, reA); m_sigs[OP_RE_OPTION] = alloc(psig, m, "re.opt", 1, 1, &reA, reA); m_sigs[OP_RE_RANGE] = alloc(psig, m, "re.range", 1, 2, seqAseqA, reA); m_sigs[OP_RE_CONCAT] = alloc(psig, m, "re.++", 1, 2, reAreA, reA); m_sigs[OP_RE_UNION] = alloc(psig, m, "re.union", 1, 2, reAreA, reA); m_sigs[OP_RE_INTERSECT] = alloc(psig, m, "re.inter", 1, 2, reAreA, reA); m_sigs[OP_RE_LOOP] = alloc(psig, m, "re.loop", 1, 1, &reA, reA); m_sigs[OP_RE_COMPLEMENT] = alloc(psig, m, "re.complement", 1, 1, &reA, reA); m_sigs[OP_RE_EMPTY_SET] = alloc(psig, m, "re.empty", 1, 0, nullptr, reA); m_sigs[OP_RE_FULL_SEQ_SET] = alloc(psig, m, "re.all", 1, 0, nullptr, reA); m_sigs[OP_RE_FULL_CHAR_SET] = alloc(psig, m, "re.allchar", 1, 0, nullptr, reA); 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] = 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); m_sigs[OP_STRING_STOI] = alloc(psig, m, "str.to.int", 0, 1, &strT, intT); m_sigs[OP_STRING_LT] = alloc(psig, m, "str.<", 0, 2, str2T, boolT); m_sigs[OP_STRING_LE] = alloc(psig, m, "str.<=", 0, 2, str2T, boolT); m_sigs[_OP_STRING_CONCAT] = alloc(psig, m, "str.++", 1, 2, str2T, strT); m_sigs[_OP_STRING_LENGTH] = alloc(psig, m, "str.len", 0, 1, &strT, intT); m_sigs[_OP_STRING_STRCTN] = alloc(psig, m, "str.contains", 0, 2, str2T, boolT); m_sigs[_OP_STRING_CHARAT] = alloc(psig, m, "str.at", 0, 2, strTint2T, strT); m_sigs[_OP_STRING_PREFIX] = alloc(psig, m, "str.prefixof", 0, 2, str2T, boolT); m_sigs[_OP_STRING_SUFFIX] = alloc(psig, m, "str.suffixof", 0, 2, str2T, boolT); m_sigs[_OP_STRING_IN_REGEXP] = alloc(psig, m, "str.in.re", 0, 2, strTreT, boolT); m_sigs[_OP_STRING_TO_REGEXP] = alloc(psig, m, "str.to.re", 0, 1, &strT, reT); m_sigs[_OP_REGEXP_EMPTY] = alloc(psig, m, "re.nostr", 0, 0, nullptr, reT); m_sigs[_OP_REGEXP_FULL_CHAR] = alloc(psig, m, "re.allchar", 0, 0, nullptr, reT); m_sigs[_OP_STRING_SUBSTR] = alloc(psig, m, "str.substr", 0, 3, strTint2T, strT); m_sigs[_OP_RE_UNROLL] = alloc(psig, m, "_re.unroll", 0, 2, reTintT, strT); } void seq_decl_plugin::set_manager(ast_manager* m, family_id id) { decl_plugin::set_manager(m, id); bv_util bv(*m); m_char = bv.mk_sort(8); m->inc_ref(m_char); parameter param(m_char); m_string = m->mk_sort(symbol("String"), sort_info(m_family_id, SEQ_SORT, 1, ¶m)); m->inc_ref(m_string); parameter paramS(m_string); m_re = m->mk_sort(m_family_id, RE_SORT, 1, ¶mS); m->inc_ref(m_re); } sort * seq_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) { init(); ast_manager& m = *m_manager; switch (k) { case SEQ_SORT: if (num_parameters != 1) { m.raise_exception("Invalid sequence sort, expecting one parameter"); } if (!parameters[0].is_ast() || !is_sort(parameters[0].get_ast())) { m.raise_exception("invalid sequence sort, parameter is not a sort"); } if (parameters[0].get_ast() == m_char) { return m_string; } return m.mk_sort(symbol("Seq"), sort_info(m_family_id, SEQ_SORT, num_parameters, parameters)); case RE_SORT: { if (num_parameters != 1) { m.raise_exception("Invalid regex sort, expecting one parameter"); } if (!parameters[0].is_ast() || !is_sort(parameters[0].get_ast())) { m.raise_exception("invalid regex sort, parameter is not a sort"); } return m.mk_sort(symbol("RegEx"), sort_info(m_family_id, RE_SORT, num_parameters, parameters)); } case _STRING_SORT: return m_string; default: UNREACHABLE(); return nullptr; } } func_decl* seq_decl_plugin::mk_seq_fun(decl_kind k, unsigned arity, sort* const* domain, sort* range, decl_kind k_string) { ast_manager& m = *m_manager; sort_ref rng(m); match(*m_sigs[k], arity, domain, range, rng); return m.mk_func_decl(m_sigs[(domain[0] == m_string)?k_string:k]->m_name, arity, domain, rng, func_decl_info(m_family_id, k)); } func_decl* seq_decl_plugin::mk_str_fun(decl_kind k, unsigned arity, sort* const* domain, sort* range, decl_kind k_seq) { ast_manager& m = *m_manager; sort_ref rng(m); match(*m_sigs[k], arity, domain, range, rng); return m.mk_func_decl(m_sigs[k]->m_name, arity, domain, rng, func_decl_info(m_family_id, k_seq)); } func_decl* seq_decl_plugin::mk_assoc_fun(decl_kind k, unsigned arity, sort* const* domain, sort* range, decl_kind k_seq, decl_kind k_string) { ast_manager& m = *m_manager; sort_ref rng(m); if (arity == 0) { m.raise_exception("Invalid function application. At least one argument expected"); } match_right_assoc(*m_sigs[k], arity, domain, range, rng); func_decl_info info(m_family_id, k_seq); info.set_right_associative(true); info.set_left_associative(true); return m.mk_func_decl(m_sigs[(rng == m_string)?k_string:k_seq]->m_name, rng, rng, rng, info); } func_decl * seq_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { init(); ast_manager& m = *m_manager; sort_ref rng(m); switch(k) { case OP_SEQ_EMPTY: match(*m_sigs[k], arity, domain, range, rng); if (rng == m_string) { parameter param(symbol("")); return mk_func_decl(OP_STRING_CONST, 1, ¶m, 0, nullptr, m_string); } else { parameter param(rng.get()); func_decl_info info(m_family_id, k, 1, ¶m); return m.mk_func_decl(m_sigs[k]->m_name, arity, domain, rng, info); } case OP_RE_PLUS: case OP_RE_STAR: case OP_RE_OPTION: case OP_RE_RANGE: case OP_RE_OF_PRED: case OP_RE_COMPLEMENT: m_has_re = true; // fall-through case OP_SEQ_UNIT: case OP_STRING_ITOS: case OP_STRING_STOI: case OP_STRING_LT: case OP_STRING_LE: match(*m_sigs[k], arity, domain, range, rng); return m.mk_func_decl(m_sigs[k]->m_name, arity, domain, rng, func_decl_info(m_family_id, k)); case _OP_REGEXP_FULL_CHAR: m_has_re = true; if (!range) range = m_re; match(*m_sigs[k], arity, domain, range, rng); return m.mk_func_decl(symbol("re.allchar"), arity, domain, rng, func_decl_info(m_family_id, OP_RE_FULL_CHAR_SET)); case OP_RE_FULL_CHAR_SET: m_has_re = true; if (!range) range = m_re; if (range == m_re) { match(*m_sigs[k], arity, domain, range, rng); return m.mk_func_decl(symbol("re.allchar"), arity, domain, rng, func_decl_info(m_family_id, k)); } return m.mk_func_decl(m_sigs[k]->m_name, arity, domain, range, func_decl_info(m_family_id, k)); case OP_RE_FULL_SEQ_SET: m_has_re = true; if (!range) range = m_re; return m.mk_func_decl(m_sigs[k]->m_name, arity, domain, range, func_decl_info(m_family_id, k)); case _OP_REGEXP_EMPTY: m_has_re = true; if (!range) range = m_re; match(*m_sigs[k], arity, domain, range, rng); return m.mk_func_decl(symbol("re.nostr"), arity, domain, rng, func_decl_info(m_family_id, OP_RE_EMPTY_SET)); case OP_RE_EMPTY_SET: m_has_re = true; if (!range) range = m_re; if (range == m_re) { match(*m_sigs[k], arity, domain, range, rng); return m.mk_func_decl(symbol("re.nostr"), arity, domain, rng, func_decl_info(m_family_id, k)); } return m.mk_func_decl(m_sigs[k]->m_name, arity, domain, range, func_decl_info(m_family_id, k)); case OP_RE_LOOP: m_has_re = true; switch (arity) { case 1: match(*m_sigs[k], arity, domain, range, rng); if (num_parameters == 0 || num_parameters > 2 || !parameters[0].is_int() || (num_parameters == 2 && !parameters[1].is_int())) { m.raise_exception("Expecting two numeral parameters to function re-loop"); } return m.mk_func_decl(m_sigs[k]->m_name, arity, domain, rng, func_decl_info(m_family_id, k, num_parameters, parameters)); case 2: if (m_re != domain[0] || !arith_util(m).is_int(domain[1])) { m.raise_exception("Incorrect type of arguments passed to re.loop. Expecting regular expression and two integer parameters"); } return m.mk_func_decl(m_sigs[k]->m_name, arity, domain, domain[0], func_decl_info(m_family_id, k, num_parameters, parameters)); case 3: if (m_re != domain[0] || !arith_util(m).is_int(domain[1]) || !arith_util(m).is_int(domain[2])) { m.raise_exception("Incorrect type of arguments passed to re.loop. Expecting regular expression and two integer parameters"); } return m.mk_func_decl(m_sigs[k]->m_name, arity, domain, domain[0], func_decl_info(m_family_id, k, num_parameters, parameters)); default: m.raise_exception("Incorrect number of arguments passed to loop. Expected 1 regular expression and two integer parameters"); } case _OP_RE_UNROLL: m_has_re = true; match(*m_sigs[k], arity, domain, range, rng); return m.mk_func_decl(m_sigs[k]->m_name, arity, domain, rng, func_decl_info(m_family_id, k)); case OP_STRING_CONST: if (!(num_parameters == 1 && arity == 0 && parameters[0].is_symbol())) { m.raise_exception("invalid string declaration"); } return m.mk_const_decl(m_stringc_sym, m_string, func_decl_info(m_family_id, OP_STRING_CONST, num_parameters, parameters)); case OP_RE_UNION: case OP_RE_CONCAT: case OP_RE_INTERSECT: m_has_re = true; return mk_assoc_fun(k, arity, domain, range, k, k); case OP_SEQ_CONCAT: return mk_assoc_fun(k, arity, domain, range, k, _OP_STRING_CONCAT); case _OP_STRING_CONCAT: return mk_assoc_fun(k, arity, domain, range, OP_SEQ_CONCAT, k); case OP_SEQ_REPLACE: return mk_seq_fun(k, arity, domain, range, _OP_STRING_STRREPL); case _OP_STRING_STRREPL: return mk_str_fun(k, arity, domain, range, OP_SEQ_REPLACE); case OP_SEQ_INDEX: if (arity == 2) { sort* dom[3] = { domain[0], domain[1], arith_util(m).mk_int() }; sort_ref rng(m); match(*m_sigs[k], 3, dom, range, rng); return m.mk_func_decl(m_sigs[(dom[0] == m_string)?_OP_STRING_STRIDOF:k]->m_name, arity, domain, rng, func_decl_info(m_family_id, k)); } return mk_seq_fun(k, arity, domain, range, _OP_STRING_STRIDOF); case _OP_STRING_STRIDOF: if (arity == 2) { sort* dom[3] = { domain[0], domain[1], arith_util(m).mk_int() }; sort_ref rng(m); match(*m_sigs[k], 3, dom, range, rng); return m.mk_func_decl(m_sigs[k]->m_name, arity, domain, rng, func_decl_info(m_family_id, OP_SEQ_INDEX)); } return mk_str_fun(k, arity, domain, range, OP_SEQ_INDEX); case OP_SEQ_LAST_INDEX: if (arity != 2) { m.raise_exception("two arguments expected tin last_indexof"); } else { return mk_seq_fun(k, arity, domain, range, OP_SEQ_LAST_INDEX); } case OP_SEQ_PREFIX: return mk_seq_fun(k, arity, domain, range, _OP_STRING_PREFIX); case _OP_STRING_PREFIX: return mk_str_fun(k, arity, domain, range, OP_SEQ_PREFIX); case OP_SEQ_SUFFIX: return mk_seq_fun(k, arity, domain, range, _OP_STRING_SUFFIX); case _OP_STRING_SUFFIX: return mk_str_fun(k, arity, domain, range, OP_SEQ_SUFFIX); case OP_SEQ_LENGTH: return mk_seq_fun(k, arity, domain, range, _OP_STRING_LENGTH); case _OP_STRING_LENGTH: return mk_str_fun(k, arity, domain, range, OP_SEQ_LENGTH); case OP_SEQ_CONTAINS: return mk_seq_fun(k, arity, domain, range, _OP_STRING_STRCTN); case _OP_STRING_STRCTN: return mk_str_fun(k, arity, domain, range, OP_SEQ_CONTAINS); case OP_SEQ_TO_RE: m_has_re = true; return mk_seq_fun(k, arity, domain, range, _OP_STRING_TO_REGEXP); case _OP_STRING_TO_REGEXP: m_has_re = true; return mk_str_fun(k, arity, domain, range, OP_SEQ_TO_RE); case OP_SEQ_IN_RE: m_has_re = true; return mk_seq_fun(k, arity, domain, range, _OP_STRING_IN_REGEXP); case _OP_STRING_IN_REGEXP: m_has_re = true; return mk_str_fun(k, arity, domain, range, OP_SEQ_IN_RE); case OP_SEQ_AT: return mk_seq_fun(k, arity, domain, range, _OP_STRING_CHARAT); case _OP_STRING_CHARAT: return mk_str_fun(k, arity, domain, range, OP_SEQ_AT); case OP_SEQ_NTH: case OP_SEQ_NTH_I: case OP_SEQ_NTH_U: match(*m_sigs[k], arity, domain, range, rng); return m.mk_func_decl(m_sigs[k]->m_name, arity, domain, rng, func_decl_info(m_family_id, k)); case OP_SEQ_EXTRACT: return mk_seq_fun(k, arity, domain, range, _OP_STRING_SUBSTR); case _OP_STRING_SUBSTR: return mk_str_fun(k, arity, domain, range, OP_SEQ_EXTRACT); case _OP_SEQ_SKOLEM: { if (num_parameters != 1 || !parameters[0].is_symbol()) { m.raise_exception("one symbol parameter expected to skolem symbol"); } symbol s = parameters[0].get_symbol(); return m.mk_func_decl(s, arity, domain, range, func_decl_info(m_family_id, k, num_parameters, parameters)); } default: UNREACHABLE(); return nullptr; } } void seq_decl_plugin::get_op_names(svector & op_names, symbol const & logic) { init(); for (unsigned i = 0; i < m_sigs.size(); ++i) { if (m_sigs[i]) { op_names.push_back(builtin_name(m_sigs[i]->m_name.str().c_str(), i)); } } } void seq_decl_plugin::get_sort_names(svector & sort_names, symbol const & logic) { init(); sort_names.push_back(builtin_name("Seq", SEQ_SORT)); sort_names.push_back(builtin_name("RegEx", RE_SORT)); // SMT-LIB 2.5 compatibility sort_names.push_back(builtin_name("String", _STRING_SORT)); sort_names.push_back(builtin_name("StringSequence", _STRING_SORT)); } app* seq_decl_plugin::mk_string(symbol const& s) { zstring canonStr(s.bare_str()); symbol canonSym(canonStr.encode().c_str()); parameter param(canonSym); func_decl* f = m_manager->mk_const_decl(m_stringc_sym, m_string, func_decl_info(m_family_id, OP_STRING_CONST, 1, ¶m)); return m_manager->mk_const(f); } app* seq_decl_plugin::mk_string(zstring const& s) { symbol sym(s.encode().c_str()); parameter param(sym); func_decl* f = m_manager->mk_const_decl(m_stringc_sym, m_string, func_decl_info(m_family_id, OP_STRING_CONST, 1, ¶m)); return m_manager->mk_const(f); } bool seq_decl_plugin::is_considered_uninterpreted(func_decl * f) { seq_util util(*m_manager); return util.str.is_nth_u(f); } bool seq_decl_plugin::is_value(app* e) const { while (true) { if (is_app_of(e, m_family_id, OP_SEQ_EMPTY)) { return true; } if (is_app_of(e, m_family_id, OP_STRING_CONST)) { return true; } if (is_app_of(e, m_family_id, OP_SEQ_UNIT) && m_manager->is_value(e->get_arg(0))) { return true; } if (is_app_of(e, m_family_id, OP_SEQ_CONCAT)) { bool first = true; for (expr* arg : *e) { if (first) { first = false; } else if (is_app(arg) && !is_value(to_app(arg))) { return false; } } if (!is_app(e->get_arg(0))) return false; e = to_app(e->get_arg(0)); continue; } return false; } } bool seq_decl_plugin::are_equal(app* a, app* b) const { if (a == b) return true; // handle concatenations return false; } bool seq_decl_plugin::are_distinct(app* a, app* b) const { if (a == b) { return false; } if (is_app_of(a, m_family_id, OP_STRING_CONST) && is_app_of(b, m_family_id, OP_STRING_CONST)) { return true; } if (is_app_of(a, m_family_id, OP_SEQ_UNIT) && is_app_of(b, m_family_id, OP_SEQ_UNIT)) { return m_manager->are_distinct(a->get_arg(0), b->get_arg(0)); } if (is_app_of(a, m_family_id, OP_SEQ_EMPTY) && is_app_of(b, m_family_id, OP_SEQ_UNIT)) { return true; } if (is_app_of(b, m_family_id, OP_SEQ_EMPTY) && is_app_of(a, m_family_id, OP_SEQ_UNIT)) { return true; } return false; } expr* seq_decl_plugin::get_some_value(sort* s) { seq_util util(*m_manager); if (util.is_seq(s)) { return util.str.mk_empty(s); } sort* seq; if (util.is_re(s, seq)) { return util.re.mk_to_re(util.str.mk_empty(seq)); } UNREACHABLE(); return nullptr; } app* seq_util::mk_skolem(symbol const& name, unsigned n, expr* const* args, sort* range) { SASSERT(range); parameter param(name); func_decl* f = m.mk_func_decl(get_family_id(), _OP_SEQ_SKOLEM, 1, ¶m, n, args, range); return m.mk_app(f, n, args); } app* seq_util::str::mk_string(zstring const& s) const { return u.seq.mk_string(s); } app* seq_util::str::mk_char(zstring const& s, unsigned idx) const { bv_util bvu(m); return bvu.mk_numeral(s[idx], s.num_bits()); } app* seq_util::str::mk_char(char ch) const { zstring s(ch, zstring::ascii); 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); } app* seq_util::mk_lt(expr* ch1, expr* ch2) const { bv_util bv(m); return m.mk_not(bv.mk_ule(ch2, ch1)); } 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()); return true; } else { return false; } } bool seq_util::str::is_nth_i(expr const* n, expr*& s, unsigned& idx) const { expr* i = nullptr; if (!is_nth_i(n, s, i)) return false; return arith_util(m).is_unsigned(i, idx); } app* seq_util::str::mk_nth_i(expr* s, unsigned i) const { return mk_nth_i(s, arith_util(m).mk_int(i)); } void seq_util::str::get_concat(expr* e, expr_ref_vector& es) const { expr* e1, *e2; while (is_concat(e, e1, e2)) { get_concat(e1, es); e = e2; } if (!is_empty(e)) { es.push_back(e); } } void seq_util::str::get_concat_units(expr* e, expr_ref_vector& es) const { expr* e1, *e2; while (is_concat(e, e1, e2)) { get_concat_units(e1, es); e = e2; } zstring s; if (is_string(e, s)) { unsigned sz = s.length(); for (unsigned j = 0; j < sz; ++j) { es.push_back(mk_unit(mk_char(s, j))); } } else if (!is_empty(e)) { es.push_back(e); } } app* seq_util::str::mk_is_empty(expr* s) const { return m.mk_eq(s, mk_empty(get_sort(s))); } sort* seq_util::re::to_seq(sort* re) { SASSERT(u.is_re(re)); return to_sort(re->get_parameter(0).get_ast()); } app* seq_util::re::mk_loop(expr* r, unsigned lo) { parameter param(lo); return m.mk_app(m_fid, OP_RE_LOOP, 1, ¶m, 1, &r); } app* seq_util::re::mk_loop(expr* r, unsigned lo, unsigned hi) { parameter params[2] = { parameter(lo), parameter(hi) }; return m.mk_app(m_fid, OP_RE_LOOP, 2, params, 1, &r); } app* seq_util::re::mk_loop(expr* r, expr* lo) { expr* rs[2] = { r, lo }; return m.mk_app(m_fid, OP_RE_LOOP, 0, nullptr, 2, rs); } app* seq_util::re::mk_loop(expr* r, expr* lo, expr* hi) { expr* rs[3] = { r, lo, hi }; return m.mk_app(m_fid, OP_RE_LOOP, 0, nullptr, 3, rs); } app* seq_util::re::mk_full_char(sort* s) { return m.mk_app(m_fid, OP_RE_FULL_CHAR_SET, 0, nullptr, 0, nullptr, s); } app* seq_util::re::mk_full_seq(sort* s) { return m.mk_app(m_fid, OP_RE_FULL_SEQ_SET, 0, nullptr, 0, nullptr, s); } 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); if (a->get_num_args() == 1 && a->get_decl()->get_num_parameters() == 2) { body = a->get_arg(0); lo = a->get_decl()->get_parameter(0).get_int(); hi = a->get_decl()->get_parameter(1).get_int(); return true; } } return false; } bool seq_util::re::is_loop(expr const* n, expr*& body, unsigned& lo) { if (is_loop(n)) { app const* a = to_app(n); if (a->get_num_args() == 1 && a->get_decl()->get_num_parameters() == 1) { body = a->get_arg(0); lo = a->get_decl()->get_parameter(0).get_int(); return true; } } return false; } bool seq_util::re::is_loop(expr const* n, expr*& body, expr*& lo, expr*& hi) { if (is_loop(n)) { app const* a = to_app(n); if (a->get_num_args() == 3) { body = a->get_arg(0); lo = a->get_arg(1); hi = a->get_arg(2); return true; } } return false; } bool seq_util::re::is_loop(expr const* n, expr*& body, expr*& lo) { if (is_loop(n)) { app const* a = to_app(n); if (a->get_num_args() == 2) { body = a->get_arg(0); lo = a->get_arg(1); return true; } } return false; } z3-z3-4.8.7/src/ast/seq_decl_plugin.h000066400000000000000000000430571356505360400173240ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: seq_decl_plugin.h Abstract: decl_plugin for the theory of sequences Author: Nikolaj Bjorner (nbjorner) 2011-11-14 Revision History: Updated to string sequences 2015-12-5 --*/ #ifndef SEQ_DECL_PLUGIN_H_ #define SEQ_DECL_PLUGIN_H_ #include "ast/ast.h" enum seq_sort_kind { SEQ_SORT, RE_SORT, _STRING_SORT // internal only }; enum seq_op_kind { OP_SEQ_UNIT, OP_SEQ_EMPTY, OP_SEQ_CONCAT, OP_SEQ_PREFIX, OP_SEQ_SUFFIX, OP_SEQ_CONTAINS, OP_SEQ_EXTRACT, OP_SEQ_REPLACE, OP_SEQ_AT, OP_SEQ_NTH, // NTH function exposed over API. Rewritten to NTH(s,i) := if (0 <= i < len(s)) then NTH_I(s,i) else NTH_U(s,i) OP_SEQ_NTH_I, // Interpreted variant of Nth for indices within defined domain. OP_SEQ_NTH_U, // Uninterpreted variant of Nth for indices outside of uniquely defined domain. OP_SEQ_LENGTH, OP_SEQ_INDEX, OP_SEQ_LAST_INDEX, OP_SEQ_TO_RE, OP_SEQ_IN_RE, OP_RE_PLUS, OP_RE_STAR, OP_RE_OPTION, OP_RE_RANGE, OP_RE_CONCAT, OP_RE_UNION, OP_RE_INTERSECT, OP_RE_LOOP, OP_RE_COMPLEMENT, OP_RE_EMPTY_SET, OP_RE_FULL_SEQ_SET, OP_RE_FULL_CHAR_SET, OP_RE_OF_PRED, // string specific operators. OP_STRING_CONST, OP_STRING_ITOS, OP_STRING_STOI, OP_STRING_LT, OP_STRING_LE, // internal only operators. Converted to SEQ variants. _OP_STRING_STRREPL, _OP_STRING_CONCAT, _OP_STRING_LENGTH, _OP_STRING_STRCTN, _OP_STRING_PREFIX, _OP_STRING_SUFFIX, _OP_STRING_IN_REGEXP, _OP_STRING_TO_REGEXP, _OP_STRING_CHARAT, _OP_STRING_SUBSTR, _OP_STRING_STRIDOF, _OP_REGEXP_EMPTY, _OP_REGEXP_FULL_CHAR, _OP_SEQ_SKOLEM, _OP_RE_UNROLL, LAST_SEQ_OP }; class zstring { public: enum encoding { ascii, unicode }; private: buffer m_buffer; encoding m_encoding; public: zstring(encoding enc = ascii); zstring(char const* s, encoding enc = ascii); zstring(unsigned sz, unsigned const* s, encoding enc = ascii); zstring(zstring const& other); zstring(unsigned num_bits, bool const* ch); zstring(unsigned ch, encoding enc = ascii); zstring& operator=(zstring const& other); zstring replace(zstring const& src, zstring const& dst) const; unsigned num_bits() const { return (m_encoding==ascii)?8:16; } encoding get_encoding() const { return m_encoding; } std::string encode() const; std::string as_string() const; unsigned length() const { return m_buffer.size(); } unsigned operator[](unsigned i) const { return m_buffer[i]; } bool empty() const { return m_buffer.empty(); } bool suffixof(zstring const& other) const; bool prefixof(zstring const& other) const; bool contains(zstring const& other) const; int indexof(zstring const& other, int offset) const; int last_indexof(zstring const& other) const; zstring extract(int lo, int hi) const; zstring operator+(zstring const& other) const; bool operator==(const zstring& other) const; bool operator!=(const zstring& other) const; friend std::ostream& operator<<(std::ostream &os, const zstring &str); friend bool operator<(const zstring& lhs, const zstring& rhs); }; class seq_decl_plugin : public decl_plugin { struct psig { symbol m_name; unsigned m_num_params; sort_ref_vector m_dom; sort_ref m_range; psig(ast_manager& m, char const* name, unsigned n, unsigned dsz, sort* const* dom, sort* rng): m_name(name), m_num_params(n), m_dom(m), m_range(rng, m) { m_dom.append(dsz, dom); } }; ptr_vector m_sigs; ptr_vector m_binding; bool m_init; symbol m_stringc_sym; symbol m_charc_sym; sort* m_string; sort* m_char; sort* m_re; bool m_has_re; void match(psig& sig, unsigned dsz, sort* const* dom, sort* range, sort_ref& rng); void match_right_assoc(psig& sig, unsigned dsz, sort* const* dom, sort* range, sort_ref& rng); bool match(ptr_vector& binding, sort* s, sort* sP); sort* apply_binding(ptr_vector const& binding, sort* s); bool is_sort_param(sort* s, unsigned& idx); func_decl* mk_seq_fun(decl_kind k, unsigned arity, sort* const* domain, sort* range, decl_kind k_string); func_decl* mk_str_fun(decl_kind k, unsigned arity, sort* const* domain, sort* range, decl_kind k_seq); func_decl* mk_assoc_fun(decl_kind k, unsigned arity, sort* const* domain, sort* range, decl_kind k_string, decl_kind k_seq); void init(); void set_manager(ast_manager * m, family_id id) override; public: seq_decl_plugin(); ~seq_decl_plugin() override {} void finalize() override; decl_plugin * mk_fresh() override { return alloc(seq_decl_plugin); } sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) override; func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) override; void get_op_names(svector & op_names, symbol const & logic) override; void get_sort_names(svector & sort_names, symbol const & logic) override; bool is_value(app * e) const override; bool is_unique_value(app * e) const override { return false; } bool are_equal(app* a, app* b) const override; bool are_distinct(app* a, app* b) const override; expr * get_some_value(sort * s) override; bool is_char(ast* a) const { return a == m_char; } app* mk_string(symbol const& s); app* mk_string(zstring const& s); bool has_re() const { return m_has_re; } bool is_considered_uninterpreted(func_decl * f) override; }; class seq_util { ast_manager& m; seq_decl_plugin& seq; family_id m_fid; public: ast_manager& get_manager() const { return m; } bool is_char(sort* s) const { return seq.is_char(s); } bool is_string(sort* s) const { return is_seq(s) && seq.is_char(s->get_parameter(0).get_ast()); } bool is_seq(sort* s) const { return is_sort_of(s, m_fid, SEQ_SORT); } bool is_re(sort* s) const { return is_sort_of(s, m_fid, RE_SORT); } bool is_re(sort* s, sort*& seq) const { return is_sort_of(s, m_fid, RE_SORT) && (seq = to_sort(s->get_parameter(0).get_ast()), true); } bool is_seq(expr* e) const { return is_seq(m.get_sort(e)); } bool is_seq(sort* s, sort*& seq) { return is_seq(s) && (seq = to_sort(s->get_parameter(0).get_ast()), true); } 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_lt(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); } bool has_re() const { return seq.has_re(); } class str { seq_util& u; ast_manager& m; family_id m_fid; app* mk_string(char const* s) { return mk_string(symbol(s)); } app* mk_string(std::string const& s) { return mk_string(symbol(s.c_str())); } public: str(seq_util& u): u(u), m(u.m), m_fid(u.m_fid) {} sort* mk_seq(sort* s) const { parameter param(s); return m.mk_sort(m_fid, SEQ_SORT, 1, ¶m); } sort* mk_string_sort() const { return m.mk_sort(m_fid, _STRING_SORT, 0, nullptr); } app* mk_empty(sort* s) const { return m.mk_const(m.mk_func_decl(m_fid, OP_SEQ_EMPTY, 0, nullptr, 0, (expr*const*)nullptr, s)); } app* mk_string(zstring const& s) const; app* mk_string(symbol const& s) const { return u.seq.mk_string(s); } app* mk_char(char ch) const; app* mk_concat(expr* a, expr* b) const { expr* es[2] = { a, b }; return m.mk_app(m_fid, OP_SEQ_CONCAT, 2, es); } app* mk_concat(expr* a, expr* b, expr* c) const { return mk_concat(a, mk_concat(b, c)); } expr* mk_concat(unsigned n, expr* const* es) const { if (n == 1) return es[0]; SASSERT(n > 1); return m.mk_app(m_fid, OP_SEQ_CONCAT, n, es); } expr* mk_concat(expr_ref_vector const& es) const { return mk_concat(es.size(), es.c_ptr()); } app* mk_length(expr* a) const { return m.mk_app(m_fid, OP_SEQ_LENGTH, 1, &a); } app* mk_at(expr* s, expr* i) const { expr* es[2] = { s, i }; return m.mk_app(m_fid, OP_SEQ_AT, 2, es); } app* mk_nth_i(expr* s, expr* i) const { expr* es[2] = { s, i }; return m.mk_app(m_fid, OP_SEQ_NTH_I, 2, es); } app* mk_nth_i(expr* s, unsigned i) const; app* mk_substr(expr* a, expr* b, expr* c) const { expr* es[3] = { a, b, c }; return m.mk_app(m_fid, OP_SEQ_EXTRACT, 3, es); } app* mk_contains(expr* a, expr* b) const { expr* es[2] = { a, b }; return m.mk_app(m_fid, OP_SEQ_CONTAINS, 2, es); } app* mk_prefix(expr* a, expr* b) const { expr* es[2] = { a, b }; return m.mk_app(m_fid, OP_SEQ_PREFIX, 2, es); } app* mk_suffix(expr* a, expr* b) const { expr* es[2] = { a, b }; return m.mk_app(m_fid, OP_SEQ_SUFFIX, 2, es); } app* mk_index(expr* a, expr* b, expr* i) const { expr* es[3] = { a, b, i}; return m.mk_app(m_fid, OP_SEQ_INDEX, 3, es); } app* mk_last_index(expr* a, expr* b) const { expr* es[2] = { a, b}; return m.mk_app(m_fid, OP_SEQ_LAST_INDEX, 2, es); } app* mk_unit(expr* u) const { return m.mk_app(m_fid, OP_SEQ_UNIT, 1, &u); } app* mk_char(zstring const& s, unsigned idx) const; app* mk_itos(expr* i) const { return m.mk_app(m_fid, OP_STRING_ITOS, 1, &i); } app* mk_stoi(expr* s) const { return m.mk_app(m_fid, OP_STRING_STOI, 1, &s); } app* mk_is_empty(expr* s) const; app* mk_lex_lt(expr* a, expr* b) const { expr* es[2] = { a, b }; return m.mk_app(m_fid, OP_STRING_LT, 2, es); } app* mk_lex_le(expr* a, expr* b) const { expr* es[2] = { a, b }; return m.mk_app(m_fid, OP_STRING_LE, 2, es); } bool is_nth_i(func_decl const* f) const { return is_decl_of(f, m_fid, OP_SEQ_NTH_I); } bool is_nth_u(func_decl const* f) const { return is_decl_of(f, m_fid, OP_SEQ_NTH_U); } bool is_skolem(func_decl const* f) const { return is_decl_of(f, m_fid, _OP_SEQ_SKOLEM); } bool is_string(expr const * n) const { return is_app_of(n, m_fid, OP_STRING_CONST); } bool is_string(expr const* n, symbol& s) const { return is_string(n) && (s = to_app(n)->get_decl()->get_parameter(0).get_symbol(), true); } bool is_string(expr const* n, zstring& s) const; bool is_empty(expr const* n) const { symbol s; return is_app_of(n, m_fid, OP_SEQ_EMPTY) || (is_string(n, s) && !s.is_numerical() && *s.bare_str() == 0); } bool is_concat(expr const* n) const { return is_app_of(n, m_fid, OP_SEQ_CONCAT); } bool is_length(expr const* n) const { return is_app_of(n, m_fid, OP_SEQ_LENGTH); } bool is_extract(expr const* n) const { return is_app_of(n, m_fid, OP_SEQ_EXTRACT); } bool is_contains(expr const* n) const { return is_app_of(n, m_fid, OP_SEQ_CONTAINS); } bool is_at(expr const* n) const { return is_app_of(n, m_fid, OP_SEQ_AT); } bool is_nth_i(expr const* n) const { return is_app_of(n, m_fid, OP_SEQ_NTH_I); } bool is_nth_u(expr const* n) const { return is_app_of(n, m_fid, OP_SEQ_NTH_U); } bool is_nth_i(expr const* n, expr*& s, unsigned& idx) const; bool is_index(expr const* n) const { return is_app_of(n, m_fid, OP_SEQ_INDEX); } bool is_last_index(expr const* n) const { return is_app_of(n, m_fid, OP_SEQ_LAST_INDEX); } bool is_replace(expr const* n) const { return is_app_of(n, m_fid, OP_SEQ_REPLACE); } bool is_prefix(expr const* n) const { return is_app_of(n, m_fid, OP_SEQ_PREFIX); } bool is_suffix(expr const* n) const { return is_app_of(n, m_fid, OP_SEQ_SUFFIX); } bool is_itos(expr const* n) const { return is_app_of(n, m_fid, OP_STRING_ITOS); } bool is_stoi(expr const* n) const { return is_app_of(n, m_fid, OP_STRING_STOI); } bool is_in_re(expr const* n) const { return is_app_of(n, m_fid, OP_SEQ_IN_RE); } bool is_unit(expr const* n) const { return is_app_of(n, m_fid, OP_SEQ_UNIT); } bool is_lt(expr const* n) const { return is_app_of(n, m_fid, OP_STRING_LT); } bool is_le(expr const* n) const { return is_app_of(n, m_fid, OP_STRING_LE); } bool is_string_term(expr const * n) const { sort * s = get_sort(n); return u.is_string(s); } bool is_non_string_sequence(expr const * n) const { sort * s = get_sort(n); return (u.is_seq(s) && !u.is_string(s)); } MATCH_BINARY(is_concat); MATCH_UNARY(is_length); MATCH_TERNARY(is_extract); MATCH_BINARY(is_contains); MATCH_BINARY(is_at); MATCH_BINARY(is_nth_i); MATCH_BINARY(is_nth_u); MATCH_BINARY(is_index); MATCH_TERNARY(is_index); MATCH_BINARY(is_last_index); MATCH_TERNARY(is_replace); MATCH_BINARY(is_prefix); MATCH_BINARY(is_suffix); MATCH_BINARY(is_lt); MATCH_BINARY(is_le); MATCH_UNARY(is_itos); MATCH_UNARY(is_stoi); MATCH_BINARY(is_in_re); MATCH_UNARY(is_unit); void get_concat(expr* e, expr_ref_vector& es) const; void get_concat_units(expr* e, expr_ref_vector& es) const; expr* get_leftmost_concat(expr* e) const { expr* e1, *e2; while (is_concat(e, e1, e2)) e = e1; return e; } expr* get_rightmost_concat(expr* e) const { expr* e1, *e2; while (is_concat(e, e1, e2)) e = e2; return e; } }; class re { seq_util& u; ast_manager& m; family_id m_fid; public: re(seq_util& u): u(u), m(u.m), m_fid(u.m_fid) {} sort* mk_re(sort* seq) { parameter param(seq); return m.mk_sort(m_fid, RE_SORT, 1, ¶m); } sort* to_seq(sort* re); app* mk_to_re(expr* s) { return m.mk_app(m_fid, OP_SEQ_TO_RE, 1, &s); } app* mk_in_re(expr* s, expr* r) { return m.mk_app(m_fid, OP_SEQ_IN_RE, s, r); } app* mk_range(expr* s1, expr* s2) { return m.mk_app(m_fid, OP_RE_RANGE, s1, s2); } app* mk_concat(expr* r1, expr* r2) { return m.mk_app(m_fid, OP_RE_CONCAT, r1, r2); } app* mk_union(expr* r1, expr* r2) { return m.mk_app(m_fid, OP_RE_UNION, r1, r2); } app* mk_inter(expr* r1, expr* r2) { return m.mk_app(m_fid, OP_RE_INTERSECT, r1, r2); } app* mk_complement(expr* r) { return m.mk_app(m_fid, OP_RE_COMPLEMENT, r); } app* mk_star(expr* r) { return m.mk_app(m_fid, OP_RE_STAR, r); } app* mk_plus(expr* r) { return m.mk_app(m_fid, OP_RE_PLUS, r); } app* mk_opt(expr* r) { return m.mk_app(m_fid, OP_RE_OPTION, r); } app* mk_loop(expr* r, unsigned lo); app* mk_loop(expr* r, unsigned lo, unsigned hi); app* mk_loop(expr* r, expr* lo); app* mk_loop(expr* r, expr* lo, expr* hi); app* mk_full_char(sort* s); app* mk_full_seq(sort* s); app* mk_empty(sort* s); bool is_to_re(expr const* n) const { return is_app_of(n, m_fid, OP_SEQ_TO_RE); } bool is_concat(expr const* n) const { return is_app_of(n, m_fid, OP_RE_CONCAT); } bool is_union(expr const* n) const { return is_app_of(n, m_fid, OP_RE_UNION); } bool is_intersection(expr const* n) const { return is_app_of(n, m_fid, OP_RE_INTERSECT); } bool is_complement(expr const* n) const { return is_app_of(n, m_fid, OP_RE_COMPLEMENT); } bool is_star(expr const* n) const { return is_app_of(n, m_fid, OP_RE_STAR); } bool is_plus(expr const* n) const { return is_app_of(n, m_fid, OP_RE_PLUS); } bool is_opt(expr const* n) const { return is_app_of(n, m_fid, OP_RE_OPTION); } bool is_range(expr const* n) const { return is_app_of(n, m_fid, OP_RE_RANGE); } bool is_loop(expr const* n) const { return is_app_of(n, m_fid, OP_RE_LOOP); } bool is_empty(expr const* n) const { return is_app_of(n, m_fid, OP_RE_EMPTY_SET); } bool is_full_char(expr const* n) const { return is_app_of(n, m_fid, OP_RE_FULL_CHAR_SET); } bool is_full_seq(expr const* n) const { return is_app_of(n, m_fid, OP_RE_FULL_SEQ_SET); } MATCH_UNARY(is_to_re); MATCH_BINARY(is_concat); MATCH_BINARY(is_union); MATCH_BINARY(is_intersection); MATCH_BINARY(is_range); MATCH_UNARY(is_complement); MATCH_UNARY(is_star); MATCH_UNARY(is_plus); MATCH_UNARY(is_opt); bool is_loop(expr const* n, expr*& body, unsigned& lo, unsigned& hi); bool is_loop(expr const* n, expr*& body, unsigned& lo); bool is_loop(expr const* n, expr*& body, expr*& lo, expr*& hi); bool is_loop(expr const* n, expr*& body, expr*& lo); bool is_unroll(expr const* n) const { return is_app_of(n, m_fid, _OP_RE_UNROLL); } }; str str; re re; seq_util(ast_manager& m): m(m), seq(*static_cast(m.get_plugin(m.mk_family_id("seq")))), m_fid(seq.get_family_id()), str(*this), re(*this) { } ~seq_util() {} family_id get_family_id() const { return m_fid; } }; #endif /* SEQ_DECL_PLUGIN_H_ */ z3-z3-4.8.7/src/ast/shared_occs.cpp000066400000000000000000000067421356505360400167770ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: shared_occs.cpp Abstract: Functor for computing the shared subterms in a given term. Author: Leonardo de Moura (leonardo) 2011-04-01. Revision History: --*/ #include "ast/shared_occs.h" #include "ast/ast_smt2_pp.h" #include "util/ref_util.h" inline void shared_occs::insert(expr * t) { m_shared.reserve(t->get_id() + 1); m_shared[t->get_id()] = t; } void shared_occs::reset() { m_shared.reset(); } void shared_occs::cleanup() { reset(); m_shared.finalize(); m_stack.finalize(); } shared_occs::~shared_occs() { reset(); } inline bool shared_occs::process(expr * t, shared_occs_mark & visited) { switch (t->get_kind()) { case AST_APP: { unsigned num_args = to_app(t)->get_num_args(); if (t->get_ref_count() > 1 && (m_track_atomic || num_args > 0)) { if (visited.is_marked(t)) { insert(t); return true; } visited.mark(t); } if (num_args == 0) return true; // done with t m_stack.push_back(frame(t, 0)); // need to create frame if num_args > 0 return false; } case AST_VAR: if (m_track_atomic && t->get_ref_count() > 1) { if (visited.is_marked(t)) insert(t); else visited.mark(t); } return true; // done with t case AST_QUANTIFIER: if (t->get_ref_count() > 1) { if (visited.is_marked(t)) { insert(t); return true; // done with t } visited.mark(t); } if (!m_visit_quantifiers) return true; m_stack.push_back(frame(t, 0)); return false; default: UNREACHABLE(); return true; } } void shared_occs::operator()(expr * t, shared_occs_mark & visited) { SASSERT(m_stack.empty()); if (process(t, visited)) { return; } SASSERT(!m_stack.empty()); while (!m_stack.empty()) { start: frame & fr = m_stack.back(); expr * curr = fr.first; switch (curr->get_kind()) { case AST_APP: { unsigned num_args = to_app(curr)->get_num_args(); while (fr.second < num_args) { expr * arg = to_app(curr)->get_arg(fr.second); fr.second++; if (!process(arg, visited)) goto start; } break; } case AST_QUANTIFIER: { SASSERT(m_visit_quantifiers); unsigned num_children = m_visit_patterns ? to_quantifier(curr)->get_num_children() : 1; while (fr.second < num_children) { expr * child = to_quantifier(curr)->get_child(fr.second); fr.second++; if (!process(child, visited)) goto start; } break; } default: UNREACHABLE(); break; } m_stack.pop_back(); } } void shared_occs::operator()(expr * t) { SASSERT(m_stack.empty()); shared_occs_mark visited; reset(); operator()(t, visited); } void shared_occs::display(std::ostream & out, ast_manager & m) const { 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; } z3-z3-4.8.7/src/ast/shared_occs.h000066400000000000000000000041721356505360400164370ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: shared_occs.h Abstract: Functor for computing the shared subterms in a given term. Author: Leonardo de Moura (leonardo) 2011-04-01. Revision History: --*/ #ifndef SHARED_OCCS_H_ #define SHARED_OCCS_H_ #include "ast/ast.h" #include "util/obj_hashtable.h" class shared_occs_mark { ptr_buffer m_to_unmark; public: shared_occs_mark() {} ~shared_occs_mark() { reset(); } bool is_marked(ast * n) { return n->is_marked_so(); } void reset_mark(ast * n) { n->reset_mark_so(); } void mark(ast * n) { if (is_marked(n)) return; n->mark_so(true); m_to_unmark.push_back(n); } void reset() { ptr_buffer::iterator it = m_to_unmark.begin(); ptr_buffer::iterator end = m_to_unmark.end(); for (; it != end; ++it) { reset_mark(*it); } m_to_unmark.reset(); } void mark(ast * n, bool flag) { if (flag) mark(n); else reset_mark(n); } }; /** \brief Functor for computing the shared subterms in a given term. */ class shared_occs { ast_manager & m; bool m_track_atomic; bool m_visit_quantifiers; bool m_visit_patterns; expr_ref_vector m_shared; typedef std::pair frame; svector m_stack; bool process(expr * t, shared_occs_mark & visited); void insert(expr * t); public: typedef obj_hashtable::iterator iterator; shared_occs(ast_manager & _m, bool track_atomic = false, bool visit_quantifiers = true, bool visit_patterns = false): m(_m), m_track_atomic(track_atomic), m_visit_quantifiers(visit_quantifiers), 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.get(t->get_id(), nullptr) != nullptr; } unsigned num_shared() const; void reset(); void cleanup(); void display(std::ostream & out, ast_manager & mgr) const; }; #endif z3-z3-4.8.7/src/ast/special_relations_decl_plugin.cpp000066400000000000000000000051121356505360400225550ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: special_relations_decl_plugin.cpp Abstract: Author: Nikolaj Bjorner (nbjorner) 2015-15-9. Revision History: --*/ #include #include "ast/ast.h" #include "ast/special_relations_decl_plugin.h" special_relations_decl_plugin::special_relations_decl_plugin(): m_lo("linear-order"), m_po("partial-order"), m_plo("piecewise-linear-order"), m_to("tree-order"), m_tc("transitive-closure") {} func_decl * special_relations_decl_plugin::mk_func_decl( decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { if (arity != 2) { m_manager->raise_exception("special relations should have arity 2"); return nullptr; } if (domain[0] != domain[1]) { m_manager->raise_exception("argument sort missmatch. The two arguments should have the same sort"); return nullptr; } if (!range) { range = m_manager->mk_bool_sort(); } if (!m_manager->is_bool(range)) { m_manager->raise_exception("range type is expected to be Boolean for special relations"); } func_decl_info info(m_family_id, k, num_parameters, parameters); symbol name; switch(k) { case OP_SPECIAL_RELATION_PO: name = m_po; break; case OP_SPECIAL_RELATION_LO: name = m_lo; break; case OP_SPECIAL_RELATION_PLO: name = m_plo; break; case OP_SPECIAL_RELATION_TO: name = m_to; break; case OP_SPECIAL_RELATION_TC: name = m_tc; break; } return m_manager->mk_func_decl(name, arity, domain, range, info); } void special_relations_decl_plugin::get_op_names(svector & op_names, symbol const & logic) { if (logic == symbol::null) { op_names.push_back(builtin_name(m_po.bare_str(), OP_SPECIAL_RELATION_PO)); op_names.push_back(builtin_name(m_lo.bare_str(), OP_SPECIAL_RELATION_LO)); op_names.push_back(builtin_name(m_plo.bare_str(), OP_SPECIAL_RELATION_PLO)); op_names.push_back(builtin_name(m_to.bare_str(), OP_SPECIAL_RELATION_TO)); op_names.push_back(builtin_name(m_tc.bare_str(), OP_SPECIAL_RELATION_TC)); } } sr_property special_relations_util::get_property(func_decl* f) const { switch (f->get_decl_kind()) { case OP_SPECIAL_RELATION_PO: return sr_po; case OP_SPECIAL_RELATION_LO: return sr_lo; case OP_SPECIAL_RELATION_PLO: return sr_plo; case OP_SPECIAL_RELATION_TO: return sr_to; case OP_SPECIAL_RELATION_TC: return sr_tc; default: UNREACHABLE(); return sr_po; } } z3-z3-4.8.7/src/ast/special_relations_decl_plugin.h000066400000000000000000000103421356505360400222230ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: special_relations_decl_plugin.h Abstract: Author: Nikolaj Bjorner (nbjorner) 2015-15-9. Ashutosh Gupta 2016 Revision History: --*/ #ifndef SPECIAL_RELATIONS_DECL_PLUGIN_H_ #define SPECIAL_RELATIONS_DECL_PLUGIN_H_ #include "ast/ast.h" enum special_relations_op_kind { OP_SPECIAL_RELATION_LO, OP_SPECIAL_RELATION_PO, OP_SPECIAL_RELATION_PLO, OP_SPECIAL_RELATION_TO, OP_SPECIAL_RELATION_TC, LAST_SPECIAL_RELATIONS_OP }; class special_relations_decl_plugin : public decl_plugin { symbol m_lo; symbol m_po; symbol m_plo; symbol m_to; symbol m_tc; public: special_relations_decl_plugin(); ~special_relations_decl_plugin() override {} decl_plugin * mk_fresh() override { return alloc(special_relations_decl_plugin); } func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) override; void get_op_names(svector & op_names, symbol const & logic) override; sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) override { return nullptr; } }; enum sr_property { sr_none = 0x00, sr_transitive = 0x01, // Rxy & Ryz -> Rxz sr_reflexive = 0x02, // Rxx sr_antisymmetric = 0x04, // Rxy & Ryx -> x = y sr_lefttree = 0x08, // Ryx & Rzx -> Ryz | Rzy sr_righttree = 0x10, // Rxy & Rxz -> Ryx | Rzy sr_total = 0x20, // Rxy | Ryx sr_po = 0x01 | 0x02 | 0x04, // partial order sr_to = 0x01 | 0x02 | 0x04 | 0x10, // right-tree sr_plo = 0x01 | 0x02 | 0x04 | 0x08 | 0x10, // piecewise linear order sr_lo = 0x01 | 0x02 | 0x04 | 0x20, // linear order sr_tc = 0x40, // transitive closure of relation }; class special_relations_util { ast_manager& m; family_id m_fid; func_decl* mk_rel_decl(func_decl* f, decl_kind k) { parameter p(f); SASSERT(f->get_arity() == 2); return m.mk_func_decl(m_fid, k, 1, &p, 2, f->get_domain(), f->get_range()); } public: special_relations_util(ast_manager& m) : m(m), m_fid(m.get_family_id("special_relations")) {} bool is_special_relation(func_decl* f) const { return f->get_family_id() == m_fid; } bool is_special_relation(app* e) const { return is_special_relation(e->get_decl()); } sr_property get_property(func_decl* f) const; sr_property get_property(app* e) const { return get_property(e->get_decl()); } func_decl* mk_to_decl(func_decl* f) { return mk_rel_decl(f, OP_SPECIAL_RELATION_TO); } func_decl* mk_po_decl(func_decl* f) { return mk_rel_decl(f, OP_SPECIAL_RELATION_PO); } func_decl* mk_plo_decl(func_decl* f) { return mk_rel_decl(f, OP_SPECIAL_RELATION_PLO); } func_decl* mk_lo_decl(func_decl* f) { return mk_rel_decl(f, OP_SPECIAL_RELATION_LO); } func_decl* mk_tc_decl(func_decl* f) { return mk_rel_decl(f, OP_SPECIAL_RELATION_TC); } bool is_lo(expr const * e) const { return is_app_of(e, m_fid, OP_SPECIAL_RELATION_LO); } bool is_po(expr const * e) const { return is_app_of(e, m_fid, OP_SPECIAL_RELATION_PO); } bool is_plo(expr const * e) const { return is_app_of(e, m_fid, OP_SPECIAL_RELATION_PLO); } bool is_to(expr const * e) const { return is_app_of(e, m_fid, OP_SPECIAL_RELATION_TO); } bool is_tc(expr const * e) const { return is_app_of(e, m_fid, OP_SPECIAL_RELATION_TC); } app * mk_lo (expr * arg1, expr * arg2) { return m.mk_app( m_fid, OP_SPECIAL_RELATION_LO, arg1, arg2); } app * mk_po (expr * arg1, expr * arg2) { return m.mk_app( m_fid, OP_SPECIAL_RELATION_PO, arg1, arg2); } app * mk_plo(expr * arg1, expr * arg2) { return m.mk_app( m_fid, OP_SPECIAL_RELATION_PLO, arg1, arg2); } app * mk_to (expr * arg1, expr * arg2) { return m.mk_app( m_fid, OP_SPECIAL_RELATION_TO, arg1, arg2); } }; #endif /* SPECIAL_RELATIONS_DECL_PLUGIN_H_ */ z3-z3-4.8.7/src/ast/static_features.cpp000066400000000000000000000617201356505360400177040ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: static_features.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-05-16. Revision History: --*/ #include "ast/static_features.h" #include "ast/ast_pp.h" static_features::static_features(ast_manager & m): m(m), m_autil(m), m_bvutil(m), m_arrayutil(m), m_fpautil(m), m_sequtil(m), m_bfid(m.get_basic_family_id()), m_afid(m.mk_family_id("arith")), m_lfid(m.mk_family_id("label")), m_arrfid(m.mk_family_id("array")), m_srfid(m.mk_family_id("special_relations")), m_label_sym("label"), m_pattern_sym("pattern"), m_expr_list_sym("expr-list") { reset(); } void static_features::reset() { m_already_visited .reset(); m_cnf = true; m_num_exprs = 0; m_num_roots = 0; m_max_depth = 0; m_num_quantifiers = 0; m_num_quantifiers_with_patterns = 0; m_num_quantifiers_with_multi_patterns = 0; m_num_clauses = 0; m_num_bin_clauses = 0; m_num_units = 0; m_sum_clause_size = 0; m_num_nested_formulas = 0; m_num_bool_exprs = 0; m_num_bool_constants = 0; m_num_formula_trees = 0; m_max_formula_depth = 0; m_sum_formula_depth = 0; m_num_or_and_trees = 0; m_max_or_and_tree_depth = 0; m_sum_or_and_tree_depth = 0; m_num_ite_trees = 0; m_max_ite_tree_depth = 0; m_sum_ite_tree_depth = 0; m_num_ors = 0; m_num_ands = 0; m_num_iffs = 0; m_num_ite_formulas = 0; m_num_ite_terms = 0; m_num_sharing = 0; m_num_interpreted_exprs = 0; m_num_uninterpreted_exprs = 0; m_num_interpreted_constants = 0; m_num_uninterpreted_constants = 0; m_num_uninterpreted_functions = 0; m_num_eqs = 0; m_has_rational = false; m_has_int = false; m_has_real = false; m_has_bv = false; m_has_fpa = false; m_has_sr = false; m_has_str = false; m_has_seq_non_str = false; m_has_arrays = false; m_has_ext_arrays = false; m_arith_k_sum .reset(); m_num_arith_terms = 0; m_num_arith_eqs = 0; m_num_arith_ineqs = 0; m_num_diff_terms = 0; m_num_diff_eqs = 0; m_num_diff_ineqs = 0; m_num_simple_eqs = 0; m_num_simple_ineqs = 0; m_num_non_linear = 0; m_num_apps .reset(); m_num_theory_terms .reset(); m_num_theory_atoms .reset(); m_num_theory_constants .reset(); m_num_theory_eqs .reset(); m_num_aliens = 0; m_num_aliens_per_family .reset(); m_num_theories = 0; m_theories .reset(); m_max_stack_depth = 500; flush_cache(); } void static_features::flush_cache() { m_expr2depth.reset(); m_expr2or_and_depth.reset(); m_expr2ite_depth.reset(); m_expr2formula_depth.reset(); } bool static_features::is_diff_term(expr const * e, rational & r) const { // lhs can be 'x' or '(+ k x)' if (!is_arith_expr(e)) { r.reset(); return true; } if (is_numeral(e, r)) return true; expr* a1 = nullptr, *a2 = nullptr; return m_autil.is_add(e, a1, a2) && is_numeral(a1, r) && !is_arith_expr(a2) && !m.is_ite(a2); } bool static_features::is_diff_atom(expr const * e) const { if (!is_bool(e)) return false; if (!m.is_eq(e) && !is_arith_expr(e)) return false; SASSERT(to_app(e)->get_num_args() == 2); expr * lhs = to_app(e)->get_arg(0); expr * rhs = to_app(e)->get_arg(1); if (!is_arith_expr(lhs) && !is_arith_expr(rhs) && !m.is_ite(lhs) && !m.is_ite(rhs)) return true; if (!is_numeral(rhs)) return false; // lhs can be 'x' or '(+ x (* -1 y))' or '(+ (* -1 x) y)' if (!is_arith_expr(lhs) && !m.is_ite(lhs)) return true; expr* arg1, *arg2; if (!m_autil.is_add(lhs, arg1, arg2)) return false; expr* m1, *m2; if (!is_arith_expr(arg1) && m_autil.is_mul(arg2, m1, m2) && is_minus_one(m1) && !is_arith_expr(m2) && !m.is_ite(m2)) return true; if (!is_arith_expr(arg2) && m_autil.is_mul(arg1, m1, m2) && is_minus_one(m1) && !is_arith_expr(m2) && !m.is_ite(m2)) return true; return false; } bool static_features::is_gate(expr const * e) const { if (is_basic_expr(e)) { switch (to_app(e)->get_decl_kind()) { case OP_ITE: case OP_AND: case OP_OR: case OP_XOR: case OP_IMPLIES: return true; case OP_EQ: return m.is_bool(e); } } return false; } void static_features::update_core(expr * e) { m_num_exprs++; // even if a benchmark does not contain any theory interpreted function decls, we still have to install // the theory if the benchmark contains constants or function applications of an interpreted sort. sort * s = m.get_sort(e); if (!m.is_uninterp(s)) mark_theory(s->get_family_id()); bool _is_gate = is_gate(e); bool _is_eq = m.is_eq(e); if (_is_gate) { m_cnf = false; m_num_nested_formulas++; switch (to_app(e)->get_decl_kind()) { case OP_ITE: if (is_bool(e)) m_num_ite_formulas++; else { m_num_ite_terms++; // process then&else nodes for (unsigned i = 1; i < 3; i++) { expr * arg = to_app(e)->get_arg(i); acc_num(arg); // Must check whether arg is diff logic or not. // Otherwise, problem can be incorrectly tagged as diff logic. sort * arg_s = m.get_sort(arg); family_id fid_arg = arg_s->get_family_id(); if (fid_arg == m_afid) { m_num_arith_terms++; rational k; TRACE("diff_term", tout << "diff_term: " << is_diff_term(arg, k) << "\n" << mk_pp(arg, m) << "\n";); if (is_diff_term(arg, k)) { m_num_diff_terms++; acc_num(k); } } } } break; case OP_AND: m_num_ands++; break; case OP_OR: m_num_ors++; break; case OP_EQ: m_num_iffs++; break; } } if (is_bool(e)) { m_num_bool_exprs++; if (is_app(e) && to_app(e)->get_num_args() == 0) m_num_bool_constants++; } if (is_quantifier(e)) { m_num_quantifiers++; unsigned num_patterns = to_quantifier(e)->get_num_patterns(); if (num_patterns > 0) { m_num_quantifiers_with_patterns++; for (unsigned i = 0; i < num_patterns; i++) { expr * p = to_quantifier(e)->get_pattern(i); if (is_app(p) && to_app(p)->get_num_args() > 1) { m_num_quantifiers_with_multi_patterns++; break; } } } } bool _is_le_ge = m_autil.is_le(e) || m_autil.is_ge(e); if (_is_le_ge) { m_num_arith_ineqs++; TRACE("diff_atom", tout << "diff_atom: " << is_diff_atom(e) << "\n" << mk_pp(e, m) << "\n";); if (is_diff_atom(e)) m_num_diff_ineqs++; if (!is_arith_expr(to_app(e)->get_arg(0))) m_num_simple_ineqs++; acc_num(to_app(e)->get_arg(1)); } rational r; if (is_numeral(e, r)) { if (!r.is_int()) m_has_rational = true; } if (_is_eq) { m_num_eqs++; if (is_numeral(to_app(e)->get_arg(1))) { acc_num(to_app(e)->get_arg(1)); m_num_arith_eqs++; TRACE("diff_atom", tout << "diff_atom: " << is_diff_atom(e) << "\n" << mk_pp(e, m) << "\n";); if (is_diff_atom(e)) m_num_diff_eqs++; if (!is_arith_expr(to_app(e)->get_arg(0))) m_num_simple_eqs++; } sort * s = m.get_sort(to_app(e)->get_arg(0)); if (!m.is_uninterp(s)) { family_id fid = s->get_family_id(); if (fid != null_family_id && fid != m_bfid) inc_theory_eqs(fid); } } if (!m_has_int && m_autil.is_int(e)) m_has_int = true; if (!m_has_real && m_autil.is_real(e)) m_has_real = true; if (!m_has_bv && m_bvutil.is_bv(e)) m_has_bv = true; if (!m_has_fpa && (m_fpautil.is_float(e) || m_fpautil.is_rm(e))) m_has_fpa = true; if (is_app(e) && to_app(e)->get_family_id() == m_srfid) m_has_sr = true; if (!m_has_arrays && m_arrayutil.is_array(e)) m_has_arrays = true; if (!m_has_ext_arrays && m_arrayutil.is_array(e) && !m_arrayutil.is_select(e) && !m_arrayutil.is_store(e)) m_has_ext_arrays = true; if (!m_has_str && m_sequtil.str.is_string_term(e)) m_has_str = true; if (!m_has_seq_non_str && m_sequtil.str.is_non_string_sequence(e)) m_has_seq_non_str = true; if (is_app(e)) { family_id fid = to_app(e)->get_family_id(); mark_theory(fid); if (fid != null_family_id && fid != m_bfid) { m_num_interpreted_exprs++; if (is_bool(e)) inc_theory_atoms(fid); else inc_theory_terms(fid); if (to_app(e)->get_num_args() == 0) m_num_interpreted_constants++; } if (fid == m_afid) { // std::cout << mk_pp(e, m) << "\n"; switch (to_app(e)->get_decl_kind()) { case OP_MUL: if (!is_numeral(to_app(e)->get_arg(0)) || to_app(e)->get_num_args() > 2) { m_num_non_linear++; } break; case OP_DIV: case OP_IDIV: case OP_REM: case OP_MOD: if (!is_numeral(to_app(e)->get_arg(1))) m_num_non_linear++; break; } } if (fid == null_family_id) { m_num_uninterpreted_exprs++; if (to_app(e)->get_num_args() == 0) { m_num_uninterpreted_constants++; sort * s = m.get_sort(e); if (!m.is_uninterp(s)) { family_id fid = s->get_family_id(); if (fid != null_family_id && fid != m_bfid) inc_theory_constants(fid); } } } if (m_arrayutil.is_array(e)) { TRACE("sf_array", tout << mk_ismt2_pp(e, m) << "\n";); sort * ty = to_app(e)->get_decl()->get_range(); mark_theory(ty->get_family_id()); unsigned n = ty->get_num_parameters(); for (unsigned i = 0; i < n; i++) { sort * ds = to_sort(ty->get_parameter(i).get_ast()); update_core(ds); } } func_decl * d = to_app(e)->get_decl(); inc_num_apps(d); if (d->get_arity() > 0 && !is_marked(d)) { mark(d); if (fid == null_family_id) m_num_uninterpreted_functions++; } if (!_is_eq && !_is_gate) { for (expr * arg : *to_app(e)) { sort * arg_s = m.get_sort(arg); if (!m.is_uninterp(arg_s)) { family_id fid_arg = arg_s->get_family_id(); if (fid_arg != fid && fid_arg != null_family_id) { m_num_aliens++; inc_num_aliens(fid_arg); if (fid_arg == m_afid) { SASSERT(!_is_le_ge); m_num_arith_terms++; rational k; TRACE("diff_term", tout << "diff_term: " << is_diff_term(arg, k) << "\n" << mk_pp(arg, m) << "\n";); if (is_diff_term(arg, k)) { m_num_diff_terms++; acc_num(k); } } } } } } } } void static_features::update_core(sort * s) { mark_theory(s->get_family_id()); if (!m_has_int && m_autil.is_int(s)) m_has_int = true; if (!m_has_real && m_autil.is_real(s)) m_has_real = true; if (!m_has_bv && m_bvutil.is_bv_sort(s)) m_has_bv = true; if (!m_has_fpa && (m_fpautil.is_float(s) || m_fpautil.is_rm(s))) m_has_fpa = true; if (!m_has_arrays && m_arrayutil.is_array(s)) m_has_arrays = true; } void static_features::process(expr * e, bool form_ctx, bool or_and_ctx, bool ite_ctx, unsigned stack_depth) { TRACE("static_features", tout << "processing\n" << mk_pp(e, m) << "\n";); if (is_var(e)) return; if (is_marked(e)) { m_num_sharing++; return; } if (stack_depth > m_max_stack_depth) { return; } mark(e); update_core(e); if (is_quantifier(e)) { expr * body = to_quantifier(e)->get_expr(); process(body, false, false, false, stack_depth+1); set_depth(e, get_depth(body)+1); return; } bool form_ctx_new = false; bool or_and_ctx_new = false; bool ite_ctx_new = false; if (is_basic_expr(e)) { switch (to_app(e)->get_decl_kind()) { case OP_ITE: form_ctx_new = m.is_bool(e); ite_ctx_new = true; break; case OP_AND: case OP_OR: form_ctx_new = true; or_and_ctx_new = true; break; case OP_EQ: form_ctx_new = true; break; } } unsigned depth = 0; unsigned form_depth = 0; unsigned or_and_depth = 0; unsigned ite_depth = 0; unsigned num_args = to_app(e)->get_num_args(); for (unsigned i = 0; i < num_args; i++) { expr * arg = to_app(e)->get_arg(i); if (m.is_not(arg)) arg = to_app(arg)->get_arg(0); process(arg, form_ctx_new, or_and_ctx_new, ite_ctx_new, stack_depth+1); depth = std::max(depth, get_depth(arg)); if (form_ctx_new) form_depth = std::max(form_depth, get_form_depth(arg)); if (or_and_ctx_new) or_and_depth = std::max(or_and_depth, get_or_and_depth(arg)); if (ite_ctx_new) ite_depth = std::max(ite_depth, get_ite_depth(arg)); } depth++; set_depth(e, depth); if (depth > m_max_depth) m_max_depth = depth; if (form_ctx_new) { form_depth++; if (!form_ctx) { m_num_formula_trees++; m_sum_formula_depth += form_depth; if (form_depth > m_max_formula_depth) m_max_formula_depth = form_depth; } set_form_depth(e, form_depth); } if (or_and_ctx_new) { or_and_depth++; if (!or_and_ctx) { m_num_or_and_trees++; m_sum_or_and_tree_depth += or_and_depth; if (or_and_depth > m_max_or_and_tree_depth) m_max_or_and_tree_depth = or_and_depth; } set_or_and_depth(e, or_and_depth); } if (ite_ctx_new) { ite_depth++; if (!ite_ctx) { m_num_ite_trees++; m_sum_ite_tree_depth += ite_depth; if (ite_depth >= m_max_ite_tree_depth) m_max_ite_tree_depth = ite_depth; } set_ite_depth(e, ite_depth); } } void static_features::process_root(expr * e) { if (is_marked(e)) { m_num_sharing++; return; } m_num_roots++; if (m.is_or(e)) { mark(e); m_num_clauses++; m_num_bool_exprs++; unsigned num_args = to_app(e)->get_num_args(); m_sum_clause_size += num_args; if (num_args == 2) m_num_bin_clauses++; unsigned depth = 0; unsigned form_depth = 0; unsigned or_and_depth = 0; for (unsigned i = 0; i < num_args; i++) { expr * arg = to_app(e)->get_arg(i); if (m.is_not(arg)) arg = to_app(arg)->get_arg(0); process(arg, true, true, false, 0); depth = std::max(depth, get_depth(arg)); form_depth = std::max(form_depth, get_form_depth(arg)); or_and_depth = std::max(or_and_depth, get_or_and_depth(arg)); } depth++; set_depth(e, depth); if (depth > m_max_depth) m_max_depth = depth; form_depth++; m_num_formula_trees++; m_sum_formula_depth += form_depth; if (form_depth > m_max_formula_depth) m_max_formula_depth = form_depth; set_form_depth(e, form_depth); or_and_depth++; m_num_or_and_trees++; m_sum_or_and_tree_depth += or_and_depth; if (or_and_depth > m_max_or_and_tree_depth) m_max_or_and_tree_depth = or_and_depth; set_or_and_depth(e, or_and_depth); return; } if (!is_gate(e)) { m_sum_clause_size++; m_num_units++; m_num_clauses++; } process(e, false, false, false, 0); } void static_features::collect(unsigned num_formulas, expr * const * formulas) { for (unsigned i = 0; i < num_formulas; i++) process_root(formulas[i]); } bool static_features::internal_family(symbol const & f_name) const { return f_name == m_label_sym || f_name == m_pattern_sym || f_name == m_expr_list_sym; } void static_features::display_family_data(std::ostream & out, char const * prefix, unsigned_vector const & data) const { for (unsigned fid = 0; fid < data.size(); fid++) { symbol const & n = m.get_family_name(fid); if (!internal_family(n)) out << prefix << "_" << n << " " << data[fid] << "\n"; } } bool static_features::has_uf() const { return m_num_uninterpreted_functions > 0; } unsigned static_features::num_non_uf_theories() const { return m_num_theories; } unsigned static_features::num_theories() const { return (num_non_uf_theories() + (has_uf() ? 1 : 0)); } void static_features::display_primitive(std::ostream & out) const { out << "BEGIN_PRIMITIVE_STATIC_FEATURES" << "\n"; out << "CNF " << m_cnf << "\n"; out << "NUM_EXPRS " << m_num_exprs << "\n"; out << "NUM_ROOTS " << m_num_roots << "\n"; out << "MAX_DEPTH " << m_max_depth << "\n"; out << "NUM_QUANTIFIERS " << m_num_quantifiers << "\n"; out << "NUM_QUANTIFIERS_WITH_PATTERNS " << m_num_quantifiers_with_patterns << "\n"; out << "NUM_QUANTIFIERS_WITH_MULTI_PATTERNS " << m_num_quantifiers_with_multi_patterns << "\n"; out << "NUM_CLAUSES " << m_num_clauses << "\n"; out << "NUM_BIN_CLAUSES " << m_num_bin_clauses << "\n"; out << "NUM_UNITS " << m_num_units << "\n"; out << "SUM_CLAUSE_SIZE " << m_sum_clause_size << "\n"; out << "NUM_NESTED_FORMULAS " << m_num_nested_formulas << "\n"; out << "NUM_BOOL_EXPRS " << m_num_bool_exprs << "\n"; out << "NUM_BOOL_CONSTANTS " << m_num_bool_constants << "\n"; out << "NUM_FORMULA_TREES " << m_num_formula_trees << "\n"; out << "MAX_FORMULA_DEPTH " << m_max_formula_depth << "\n"; out << "SUM_FORMULA_DEPTH " << m_sum_formula_depth << "\n"; out << "NUM_OR_AND_TREES " << m_num_or_and_trees << "\n"; out << "MAX_OR_AND_TREE_DEPTH " << m_max_or_and_tree_depth << "\n"; out << "SUM_OR_AND_TREE_DEPTH " << m_sum_or_and_tree_depth << "\n"; out << "NUM_ITE_TREES " << m_num_ite_trees << "\n"; out << "MAX_ITE_TREE_DEPTH " << m_max_ite_tree_depth << "\n"; out << "SUM_ITE_TREE_DEPTH " << m_sum_ite_tree_depth << "\n"; out << "NUM_ORS " << m_num_ors << "\n"; out << "NUM_ANDS " << m_num_ands << "\n"; out << "NUM_IFFS " << m_num_iffs << "\n"; out << "NUM_ITE_FORMULAS " << m_num_ite_formulas << "\n"; out << "NUM_ITE_TERMS " << m_num_ite_terms << "\n"; out << "NUM_SHARING " << m_num_sharing << "\n"; out << "NUM_INTERPRETED_EXPRS " << m_num_interpreted_exprs << "\n"; out << "NUM_UNINTERPRETED_EXPRS " << m_num_uninterpreted_exprs << "\n"; out << "NUM_INTERPRETED_CONSTANTS " << m_num_interpreted_constants << "\n"; out << "NUM_UNINTERPRETED_CONSTANTS " << m_num_uninterpreted_constants << "\n"; out << "NUM_UNINTERPRETED_FUNCTIONS " << m_num_uninterpreted_functions << "\n"; out << "NUM_EQS " << m_num_eqs << "\n"; out << "HAS_RATIONAL " << m_has_rational << "\n"; out << "HAS_INT " << m_has_int << "\n"; out << "HAS_REAL " << m_has_real << "\n"; out << "ARITH_K_SUM " << m_arith_k_sum << "\n"; out << "NUM_ARITH_TERMS " << m_num_arith_terms << "\n"; out << "NUM_ARITH_EQS " << m_num_arith_eqs << "\n"; out << "NUM_ARITH_INEQS " << m_num_arith_ineqs << "\n"; out << "NUM_DIFF_TERMS " << m_num_diff_terms << "\n"; out << "NUM_DIFF_EQS " << m_num_diff_eqs << "\n"; out << "NUM_DIFF_INEQS " << m_num_diff_ineqs << "\n"; out << "NUM_SIMPLE_EQS " << m_num_simple_eqs << "\n"; out << "NUM_SIMPLE_INEQS " << m_num_simple_ineqs << "\n"; out << "NUM_NON_LINEAR " << m_num_non_linear << "\n"; out << "NUM_ALIENS " << m_num_aliens << "\n"; display_family_data(out, "NUM_TERMS", m_num_theory_terms); display_family_data(out, "NUM_ATOMS", m_num_theory_atoms); display_family_data(out, "NUM_CONSTANTS", m_num_theory_constants); display_family_data(out, "NUM_EQS", m_num_theory_eqs); display_family_data(out, "NUM_ALIENS", m_num_aliens_per_family); out << "NUM_THEORIES " << num_theories() << "\n"; out << "END_PRIMITIVE_STATIC_FEATURES" << "\n"; } void static_features::display(std::ostream & out) const { out << "BEGIN_STATIC_FEATURES" << "\n"; out << "CNF " << m_cnf << "\n"; out << "MAX_DEPTH " << m_max_depth << "\n"; out << "MAX_OR_AND_TREE_DEPTH " << m_max_or_and_tree_depth << "\n"; out << "MAX_ITE_TREE_DEPTH " << m_max_ite_tree_depth << "\n"; out << "HAS_INT " << m_has_int << "\n"; out << "HAS_REAL " << m_has_real << "\n"; out << "HAS_QUANTIFIERS " << (m_num_quantifiers > 0) << "\n"; out << "PERC_QUANTIFIERS_WITH_PATTERNS " << (m_num_quantifiers > 0 ? (double) m_num_quantifiers_with_patterns / (double) m_num_quantifiers : 0) << "\n"; out << "PERC_QUANTIFIERS_WITH_MULTI_PATTERNS " << (m_num_quantifiers > 0 ? (double) m_num_quantifiers_with_multi_patterns / (double) m_num_quantifiers : 0) << "\n"; out << "IS_NON_LINEAR " << (m_num_non_linear > 0) << "\n"; out << "THEORY_COMBINATION " << (num_theories() > 1) << "\n"; out << "AVG_CLAUSE_SIZE " << (m_num_clauses > 0 ? (double) m_sum_clause_size / (double) m_num_clauses : 0) << "\n"; out << "PERC_BOOL_CONSTANTS " << (m_num_uninterpreted_constants > 0 ? (double) m_num_bool_constants / (double) m_num_uninterpreted_constants : 0) << "\n"; out << "PERC_NESTED_FORMULAS " << (m_num_bool_exprs > 0 ? (double) m_num_nested_formulas / (double) m_num_bool_exprs : 0) << "\n"; out << "IS_DIFF " << (m_num_arith_eqs == m_num_diff_eqs && m_num_arith_ineqs == m_num_diff_ineqs && m_num_arith_terms == m_num_diff_terms) << "\n"; out << "INEQ_EQ_RATIO " << (m_num_arith_eqs > 0 ? (double) m_num_arith_ineqs / (double) m_num_arith_eqs : 0) << "\n"; out << "PERC_ARITH_EQS " << (m_num_eqs > 0 ? (double) m_num_arith_eqs / (double) m_num_eqs : 0) << "\n"; out << "PERC_DIFF_EQS " << (m_num_arith_eqs > 0 ? (double) m_num_diff_eqs / (double) m_num_arith_eqs : 0) << "\n"; out << "PERC_DIFF_INEQS " << (m_num_arith_ineqs > 0 ? (double) m_num_diff_ineqs / (double) m_num_arith_ineqs : 0) << "\n"; out << "PERC_SIMPLE_EQS " << (m_num_arith_eqs > 0 ? (double) m_num_simple_eqs / (double) m_num_arith_eqs : 0) << "\n"; out << "PERC_SIMPLE_INEQS " << (m_num_arith_ineqs > 0 ? (double) m_num_simple_ineqs / (double) m_num_arith_ineqs : 0) << "\n"; out << "PERC_ALIENS " << (m_num_exprs > 0 ? (double) m_num_aliens / (double) m_num_exprs : 0) << "\n"; out << "END_STATIC_FEATURES" << "\n"; } void static_features::get_feature_vector(vector & result) { } z3-z3-4.8.7/src/ast/static_features.h000066400000000000000000000220751356505360400173510ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: static_features.h Abstract: Author: Leonardo de Moura (leonardo) 2008-05-16. Revision History: --*/ #ifndef STATIC_FEATURES_H_ #define STATIC_FEATURES_H_ #include "ast/ast.h" #include "ast/arith_decl_plugin.h" #include "ast/bv_decl_plugin.h" #include "ast/array_decl_plugin.h" #include "ast/fpa_decl_plugin.h" #include "ast/seq_decl_plugin.h" #include "ast/special_relations_decl_plugin.h" #include "util/map.h" struct static_features { ast_manager & m; arith_util m_autil; bv_util m_bvutil; array_util m_arrayutil; fpa_util m_fpautil; seq_util m_sequtil; family_id m_bfid; family_id m_afid; family_id m_lfid; family_id m_arrfid; family_id m_srfid; ast_mark m_already_visited; bool m_cnf; unsigned m_num_exprs; // unsigned m_num_roots; // unsigned m_max_depth; unsigned m_num_quantifiers; // unsigned m_num_quantifiers_with_patterns; // unsigned m_num_quantifiers_with_multi_patterns; // unsigned m_num_clauses; unsigned m_num_bin_clauses; // unsigned m_num_units; // unsigned m_sum_clause_size; unsigned m_num_nested_formulas; // unsigned m_num_bool_exprs; // unsigned m_num_bool_constants; // unsigned m_num_formula_trees; unsigned m_max_formula_depth; unsigned m_sum_formula_depth; unsigned m_num_or_and_trees; unsigned m_max_or_and_tree_depth; unsigned m_sum_or_and_tree_depth; unsigned m_num_ite_trees; unsigned m_max_ite_tree_depth; unsigned m_sum_ite_tree_depth; unsigned m_num_ands; // unsigned m_num_ors; // num nested ors unsigned m_num_iffs; // unsigned m_num_ite_formulas; // unsigned m_num_ite_terms; // unsigned m_num_sharing; unsigned m_num_interpreted_exprs; // doesn't include bool_exprs unsigned m_num_uninterpreted_exprs; // unsigned m_num_interpreted_constants; // doesn't include bool_consts unsigned m_num_uninterpreted_constants; // unsigned m_num_uninterpreted_functions; // unsigned m_num_eqs; // bool m_has_rational; // bool m_has_int; // bool m_has_real; // bool m_has_bv; // bool m_has_fpa; // bool m_has_sr; // has special relations bool m_has_str; // has String-typed terms bool m_has_seq_non_str; // has non-String-typed Sequence terms bool m_has_arrays; // bool m_has_ext_arrays; // does this use extended array theory. rational m_arith_k_sum; // sum of the numerals in arith atoms. unsigned m_num_arith_terms; unsigned m_num_arith_eqs; // equalities of the form t = k where k is a numeral unsigned m_num_arith_ineqs; unsigned m_num_diff_terms; // <= m_num_arith_terms unsigned m_num_diff_eqs; // <= m_num_arith_eqs unsigned m_num_diff_ineqs; // <= m_num_arith_ineqs unsigned m_num_simple_eqs; // eqs of the form x = k unsigned m_num_simple_ineqs; // ineqs of the form x <= k or x >= k unsigned m_num_non_linear; unsigned_vector m_num_apps; // mapping decl_id -> num_apps; unsigned_vector m_num_theory_terms; // mapping family_id -> num_terms unsigned_vector m_num_theory_atoms; // mapping family_id -> num_atoms unsigned_vector m_num_theory_constants; // mapping family_id -> num_exprs unsigned_vector m_num_theory_eqs; // mapping family_id -> num_eqs unsigned m_num_aliens; // unsigned_vector m_num_aliens_per_family; // mapping family_id -> num_alies exprs unsigned_vector m_expr2depth; // expr-id -> depth unsigned m_max_stack_depth; // maximal depth of stack we are willing to walk. u_map m_expr2or_and_depth; u_map m_expr2ite_depth; u_map m_expr2formula_depth; unsigned m_num_theories; svector m_theories; // mapping family_id -> bool symbol m_label_sym; symbol m_pattern_sym; symbol m_expr_list_sym; bool is_marked(ast * e) const { return m_already_visited.is_marked(e); } void mark(ast * e) { m_already_visited.mark(e, true); } bool is_bool(expr const * e) const { return m.is_bool(e); } bool is_basic_expr(expr const * e) const { return is_app(e) && to_app(e)->get_family_id() == m_bfid; } bool is_arith_expr(expr const * e) const { return is_app(e) && to_app(e)->get_family_id() == m_afid; } bool is_numeral(expr const * e) const { return m_autil.is_numeral(e); } bool is_numeral(expr const * e, rational & r) const { return m_autil.is_numeral(e, r); } bool is_minus_one(expr const * e) const { rational r; return m_autil.is_numeral(e, r) && r.is_minus_one(); } bool is_diff_term(expr const * e, rational & r) const; bool is_diff_atom(expr const * e) const; bool is_gate(expr const * e) const; void mark_theory(family_id fid) { if (fid != null_family_id && !m.is_builtin_family_id(fid) && !m_theories.get(fid, false)) { m_theories.setx(fid, true, false); m_num_theories++; } } void acc_num(rational const & r) { if (r.is_neg()) m_arith_k_sum -= r; else m_arith_k_sum += r; } void acc_num(expr const * e) { rational r; if (is_numeral(e, r)) { acc_num(r); } } bool arith_k_sum_is_small() const { return m_arith_k_sum < rational(INT_MAX / 8); } void inc_num_apps(func_decl const * d) { unsigned id = d->get_decl_id(); m_num_apps.reserve(id+1, 0); m_num_apps[id]++; } void inc_theory_terms(family_id fid) { m_num_theory_terms.reserve(fid+1, 0); m_num_theory_terms[fid]++; } void inc_theory_atoms(family_id fid) { m_num_theory_atoms.reserve(fid+1, 0); m_num_theory_atoms[fid]++; } void inc_theory_constants(family_id fid) { m_num_theory_constants.reserve(fid+1, 0); m_num_theory_constants[fid]++; } void inc_theory_eqs(family_id fid) { m_num_theory_eqs.reserve(fid+1, 0); m_num_theory_eqs[fid]++; } void inc_num_aliens(family_id fid) { m_num_aliens_per_family.reserve(fid+1, 0); m_num_aliens_per_family[fid]++; } void update_core(expr * e); void update_core(sort * s); void process(expr * e, bool form_ctx, bool or_and_ctx, bool ite_ctx, unsigned stack_depth); void process_root(expr * e); unsigned get_depth(expr const * e) const { return m_expr2depth.get(e->get_id(), 1); } void set_depth(expr const * e, unsigned d) { m_expr2depth.setx(e->get_id(), d, 1); } unsigned get_or_and_depth(expr const * e) const { unsigned d = 0; m_expr2or_and_depth.find(e->get_id(), d); return d; } void set_or_and_depth(expr const * e, unsigned d) { m_expr2or_and_depth.insert(e->get_id(), d); } unsigned get_ite_depth(expr const * e) const { unsigned d = 0; m_expr2ite_depth.find(e->get_id(), d); return d; } void set_ite_depth(expr const * e, unsigned d) { m_expr2ite_depth.insert(e->get_id(), d); } unsigned get_form_depth(expr const * e) const { unsigned d = 0; m_expr2formula_depth.find(e->get_id(), d); return d; } void set_form_depth(expr const * e, unsigned d) { m_expr2formula_depth.insert(e->get_id(), d); } static_features(ast_manager & m); void reset(); void flush_cache(); void collect(unsigned num_formulas, expr * const * formulas); void collect(expr * f) { process_root(f); } bool internal_family(symbol const & f_name) const; void display_family_data(std::ostream & out, char const * prefix, unsigned_vector const & data) const; void display_primitive(std::ostream & out) const; void display(std::ostream & out) const; void get_feature_vector(vector & result); bool has_uf() const; unsigned num_theories() const; unsigned num_non_uf_theories() const; }; #endif /* STATIC_FEATURES_H_ */ z3-z3-4.8.7/src/ast/substitution/000077500000000000000000000000001356505360400165615ustar00rootroot00000000000000z3-z3-4.8.7/src/ast/substitution/CMakeLists.txt000066400000000000000000000002471356505360400213240ustar00rootroot00000000000000z3_add_component(substitution SOURCES matcher.cpp substitution.cpp substitution_tree.cpp unifier.cpp COMPONENT_DEPENDENCIES ast rewriter ) z3-z3-4.8.7/src/ast/substitution/expr_offset.h000066400000000000000000000026041356505360400212600ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: expr_offset.h Abstract: Expressions + Offsets. In order to avoid creating variants of terms, we use a pair (expression, offset), where offset is just a small integer. Non ground terms with different offsets are always considered disequal. For example, (f x):1 is different from (f x):2, and (f x):1 is unifiable with x:2. Author: Leonardo de Moura (leonardo) 2008-01-28. Revision History: --*/ #ifndef EXPR_OFFSET_H_ #define EXPR_OFFSET_H_ #include "ast/ast.h" class expr_offset { expr * m_expr; unsigned m_offset; public: expr_offset():m_expr(nullptr), m_offset(0) {} expr_offset(expr * e, unsigned o):m_expr(e), m_offset(o) {} expr * get_expr() const { return m_expr; } unsigned get_offset() const { return m_offset; } bool operator==(expr_offset const & other) const { return m_expr == other.m_expr && m_offset == other.m_offset; } bool operator!=(expr_offset const & other) const { return !operator==(other); } unsigned hash() const { unsigned a = m_expr->get_id(); unsigned b = m_offset; unsigned c = 17; mix(a, b, c); return c; } }; typedef std::pair expr_offset_pair; typedef pair_hash, obj_hash > expr_offset_pair_hash; #endif /* EXPR_OFFSET_H_ */ z3-z3-4.8.7/src/ast/substitution/expr_offset_map.h000066400000000000000000000047421356505360400221220ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: expr_offset_map.h Abstract: A generic mapping from (expression, offset) to a value T. Author: Leonardo de Moura (leonardo) 2008-02-01. Revision History: --*/ #ifndef EXPR_OFFSET_MAP_H_ #define EXPR_OFFSET_MAP_H_ #include "ast/substitution/expr_offset.h" #include "util/vector.h" /** \brief A mapping from expr_offset to some value of type T. */ template class expr_offset_map { struct data { T m_data; unsigned m_timestamp; data():m_timestamp(0) {} }; vector > m_map; unsigned m_timestamp; public: expr_offset_map(): m_timestamp(1) {} bool contains(expr_offset const & n) const { unsigned off = n.get_offset(); if (off < m_map.size()) { svector const & v = m_map[off]; unsigned id = n.get_expr()->get_id(); if (id < v.size()) return v[id].m_timestamp == m_timestamp; } return false; } bool find(expr_offset const & n, T & r) const { unsigned off = n.get_offset(); if (off < m_map.size()) { svector const & v = m_map[off]; unsigned id = n.get_expr()->get_id(); if (id < v.size() && v[id].m_timestamp == m_timestamp) { r = v[id].m_data; return true; } } return false; } void insert(expr_offset const & n, T const & r) { unsigned off = n.get_offset(); if (off >= m_map.size()) m_map.resize(off+1, svector()); svector & v = m_map[off]; unsigned id = n.get_expr()->get_id(); if (id >= v.size()) v.resize(id+1); v[id].m_data = r; v[id].m_timestamp = m_timestamp; } void reset() { m_timestamp++; if (m_timestamp == UINT_MAX) { typename vector >::iterator it = m_map.begin(); typename vector >::iterator end = m_map.end(); for (; it != end; ++it) { svector & v = *it; typename svector::iterator it2 = v.begin(); typename svector::iterator end2 = v.end(); for (; it2 != end2; ++it2) it2->m_timestamp = 0; } m_timestamp = 1; } } }; #endif /* EXPR_OFFSET_MAP_H_ */ z3-z3-4.8.7/src/ast/substitution/matcher.cpp000066400000000000000000000033121356505360400207070ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: matcher.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-02-02. Revision History: --*/ #include "ast/substitution/matcher.h" bool matcher::operator()(expr * e1, expr * e2, substitution & s) { reset(); m_subst = &s; m_todo.push_back(expr_pair(e1, e2)); while (!m_todo.empty()) { expr_pair const & p = m_todo.back(); // if (m_cache.contains(p)) { // m_todo.pop_back(); // continue; // } if (is_var(p.first)) { expr_offset r; if (m_subst->find(to_var(p.first), 0, r)) { if (r.get_expr() != p.second) return false; } else { m_subst->insert(to_var(p.first), 0, expr_offset(p.second, 1)); } m_todo.pop_back(); continue; } if (is_var(p.second)) return false; if (!is_app(p.first)) return false; if (!is_app(p.second)) return false; app * n1 = to_app(p.first); app * n2 = to_app(p.second); if (n1->get_decl() != n2->get_decl()) return false; unsigned num_args1 = n1->get_num_args(); if (num_args1 != n2->get_num_args()) return false; m_todo.pop_back(); if (num_args1 == 0) continue; // m_cache.insert(p); unsigned j = num_args1; while (j > 0) { --j; m_todo.push_back(expr_pair(n1->get_arg(j), n2->get_arg(j))); } } return true; } void matcher::reset() { // m_cache.reset(); m_todo.reset(); } z3-z3-4.8.7/src/ast/substitution/matcher.h000066400000000000000000000025241356505360400203600ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: matcher.h Abstract: Author: Leonardo de Moura (leonardo) 2008-02-02. Revision History: --*/ #ifndef MATCHER_H_ #define MATCHER_H_ #include "ast/substitution/substitution.h" #include "util/hashtable.h" /** \brief Functor for matching expressions. */ class matcher { typedef std::pair expr_pair; typedef pair_hash, obj_ptr_hash > expr_pair_hash; typedef hashtable > cache; substitution * m_subst; // cache m_cache; svector m_todo; void reset(); public: matcher() {} /** \brief Return true if e2 is an instance of e1. In case of success (result is true), it will store the substitution that makes e1 equals to e2 into s. For example: 1) e1 = f(g(x), x), e2 = f(g(h(a)), h(a)) The result is true, and s will contain x -> h(a) 2) e1 = f(a, x) e2 = f(x, a) The result is false. 3) e1 = f(x, x) e2 = f(y, a) The result is false 4) e1 = f(x, y) e2 = f(h(z), a) The result is true, and s contains x->h(z) and y->a */ bool operator()(expr * e1, expr * e2, substitution & s); }; #endif /* MATCHER_H_ */ z3-z3-4.8.7/src/ast/substitution/substitution.cpp000066400000000000000000000265631356505360400220550ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: substitution.cpp Abstract: A substitution, that is, a mapping from (variable, offset) to (expr, offset). We use offsets in order to avoid creating variants of terms. Author: Leonardo de Moura (leonardo) 2008-02-01. Revision History: --*/ #include "ast/substitution/substitution.h" #include "ast/ast_pp.h" #include "ast/ast_ll_pp.h" #include "ast/rewriter/rewriter.h" substitution::substitution(ast_manager & m): m_manager(m), m_refs(m), m_new_exprs(m), m_state(CLEAN) { } void substitution::reset() { m_subst.reset(); m_vars.reset(); m_refs.reset(); m_scopes.reset(); reset_cache(); } void substitution::reset_cache() { TRACE("subst_bug", tout << "substitution::reset_cache\n"; for (unsigned i = 0; i < m_new_exprs.size(); i++) { tout << mk_pp(m_new_exprs.get(i), m_manager) << "\nref_count: " << m_new_exprs.get(i)->get_ref_count() << "\n"; }); m_apply_cache.reset(); m_new_exprs.reset(); m_state = CLEAN; } void substitution::pop_scope(unsigned num_scopes) { unsigned lvl = m_scopes.size(); SASSERT(num_scopes <= lvl); unsigned new_lvl = lvl - num_scopes; unsigned old_sz = m_scopes[new_lvl]; unsigned curr_sz = m_vars.size(); SASSERT(old_sz <= curr_sz); for (unsigned i = old_sz; i < curr_sz; i++) { var_offset & curr = m_vars[i]; m_subst.erase(curr.first, curr.second); } m_vars.shrink(old_sz); m_refs.shrink(old_sz); m_scopes.shrink(new_lvl); reset_cache(); } inline void substitution::apply_visit(expr_offset const & n, bool & visited) { if (!m_apply_cache.contains(n)) { m_todo.push_back(n); visited = false; } } void substitution::apply(unsigned num_actual_offsets, unsigned const * deltas, expr_offset const & n, expr_offset const & s, expr_offset const & t, expr_ref & result) { TRACE("subst_bug", tout << "BEGIN substitution::apply\n";); // It is incorrect to cache results between different calls if we are applying a substitution // modulo a substitution s -> t. if (m_state == INSERT || s != expr_offset(nullptr,0)) reset_cache(); m_state = APPLY; unsigned j; expr * e = nullptr; unsigned off; expr_offset n1; bool visited; unsigned num_args; ptr_buffer new_args; m_todo.push_back(n); while (!m_todo.empty()) { expr_offset n = m_todo.back(); TRACE("subst_bug", tout << "n: " << mk_pp(n.get_expr(), m_manager) << " : " << n.get_offset() << "\n";); if (m_apply_cache.contains(n)) { m_todo.pop_back(); continue; } expr_offset n_prime = n == s ? t : n; TRACE("subst_bug", tout << "n_prime: " << mk_pp(n_prime.get_expr(), m_manager) << " : " << n_prime.get_offset() << "\n";); visited = true; e = n_prime.get_expr(); off = n_prime.get_offset(); switch (e->get_kind()) { case AST_VAR: if (find(to_var(e)->get_idx(), off, n1)) { apply_visit(n1, visited); TRACE("subst_bug", tout << "visited: " << visited << ", n1: " << mk_pp(n1.get_expr(), m_manager) << " : " << n1.get_offset() << "\n";); if (visited) { m_todo.pop_back(); expr * new_expr = nullptr; m_apply_cache.find(n1, new_expr); m_apply_cache.insert(n, new_expr); TRACE("subst_bug", tout << "1. insert n: " << mk_pp(n.get_expr(), m_manager) << " : " << n.get_offset() << " --> " << mk_pp(new_expr, m_manager) << "\n";); } } else { m_todo.pop_back(); SASSERT(off < num_actual_offsets); unsigned delta = deltas[off]; expr * new_expr = e; if (delta > 0) { new_expr = m_manager.mk_var(to_var(e)->get_idx() + delta, to_var(e)->get_sort()); m_new_exprs.push_back(new_expr); } m_apply_cache.insert(n, new_expr); TRACE("subst_bug", tout << "2. insert n: " << mk_pp(n.get_expr(), m_manager) << " : " << n.get_offset() << " --> " << mk_pp(new_expr, m_manager) << "\n";); } break; case AST_APP: num_args = to_app(e)->get_num_args(); j = num_args; while (j > 0) { --j; apply_visit(expr_offset(to_app(e)->get_arg(j), off), visited); } if (visited) { m_todo.pop_back(); new_args.reset(); bool has_new_args = false; for (unsigned i = 0; i < num_args; i++) { expr * arg = to_app(e)->get_arg(i); expr * new_arg = nullptr; VERIFY(m_apply_cache.find(expr_offset(arg, off), new_arg)); new_args.push_back(new_arg); if (arg != new_arg) has_new_args = true; } if (!has_new_args) { m_apply_cache.insert(n, e); TRACE("subst_bug", tout << "3. insert n: " << mk_pp(n.get_expr(), m_manager) << " : " << n.get_offset() << " --> " << mk_pp(e, m_manager) << "\n";); } else { expr * new_expr = m_manager.mk_app(to_app(e)->get_decl(), new_args.size(), new_args.c_ptr()); m_new_exprs.push_back(new_expr); m_apply_cache.insert(n, new_expr); TRACE("subst_bug", tout << "3. insert n: " << mk_pp(n.get_expr(), m_manager) << " : " << n.get_offset() << " --> " << mk_pp(new_expr, m_manager) << "\n";); } } break; case AST_QUANTIFIER: { quantifier* q = to_quantifier(e); unsigned num_vars = q->get_num_decls(); substitution subst(m_manager); expr_ref er(m_manager); subst.reserve(m_subst.offsets_capacity(), m_subst.vars_capacity() + num_vars); var_shifter var_sh(m_manager); expr_offset r; for (unsigned i = 0; i < m_subst.offsets_capacity(); i++) { for (unsigned j = 0; j < m_subst.vars_capacity(); j++) { if (find(j, i, r)) { var_sh(r.get_expr(), num_vars, er); subst.insert(j + num_vars, i, expr_offset(er, r.get_offset())); } } } expr_offset body(q->get_expr(), off); expr_ref s1_ref(m_manager), t1_ref(m_manager); if (s.get_expr() != nullptr) { var_sh(s.get_expr(), num_vars, s1_ref); } if (t.get_expr() != nullptr) { var_sh(t.get_expr(), num_vars, t1_ref); } expr_offset s1(s1_ref, s.get_offset()); expr_offset t1(t1_ref, t.get_offset()); expr_ref_vector pats(m_manager), no_pats(m_manager); for (unsigned i = 0; i < q->get_num_patterns(); ++i) { subst.apply(num_actual_offsets, deltas, expr_offset(q->get_pattern(i), off), s1, t1, er); pats.push_back(std::move(er)); } for (unsigned i = 0; i < q->get_num_no_patterns(); ++i) { subst.apply(num_actual_offsets, deltas, expr_offset(q->get_no_pattern(i), off), s1, t1, er); no_pats.push_back(std::move(er)); } subst.apply(num_actual_offsets, deltas, body, s1, t1, er); er = m_manager.update_quantifier(q, pats.size(), pats.c_ptr(), no_pats.size(), no_pats.c_ptr(), er); m_todo.pop_back(); m_apply_cache.insert(n, er); m_new_exprs.push_back(std::move(er)); break; } default: UNREACHABLE(); } } SASSERT(m_apply_cache.contains(n)); VERIFY(m_apply_cache.find(n, e)); m_new_exprs.push_back(e); result = e; if (s != expr_offset(nullptr,0)) reset_cache(); TRACE("subst_bug", tout << "END substitution::apply\nresult:\n" << mk_pp(e, m_manager) << "\nref_count: " << e->get_ref_count() << "\n";); } inline substitution::color substitution::get_color(expr_offset const & p) const { color c; if (m_color.find(p, c)) return c; return White; } inline void substitution::set_color(expr_offset const & p, color c) { m_color.insert(p, c); } inline void substitution::visit(expr_offset const & p, bool & visited) { if (get_color(p) != Black) { m_todo.push_back(p); visited = false; } } bool substitution::visit_children(expr_offset const & p) { bool visited = true; expr * n = p.get_expr(); unsigned off; expr_offset p1; unsigned j; switch (n->get_kind()) { case AST_VAR: if (find(p, p1) && p != p1) visit(p1, visited); break; case AST_APP: off = p.get_offset(); j = to_app(n)->get_num_args(); while (j > 0) { --j; visit(expr_offset(to_app(n)->get_arg(j), off), visited); } break; default: UNREACHABLE(); } return visited; } bool substitution::acyclic(expr_offset p) { if (get_color(p) == Black) return true; m_todo.reset(); m_todo.push_back(p); while (!m_todo.empty()) { expr_offset p = m_todo.back(); switch (get_color(p)) { case Black: m_todo.pop_back(); break; case White: set_color(p, Grey); if (visit_children(p)) { set_color(p, Black); SASSERT(m_todo.back() == p); m_todo.pop_back(); } break; case Grey: if (!visit_children(p)) return false; set_color(p, Black); SASSERT(m_todo.back() == p); m_todo.pop_back(); break; } } return true; } bool substitution::acyclic() { m_color.reset(); expr_offset r; svector::iterator it = m_vars.begin(); svector::iterator end = m_vars.end(); for (; it != end; ++it) { m_subst.find(it->first, it->second, r); if (!acyclic(r)) return false; } return true; } void substitution::display(std::ostream & out, unsigned num_actual_offsets, unsigned const * deltas) { reset_cache(); for (unsigned i = 0; i < num_actual_offsets; i++) for (unsigned j = 0; j < m_subst.vars_capacity(); j++) { expr_offset r; if (find(j, i, r)) { expr_ref tmp(m_manager); apply(num_actual_offsets, deltas, r, tmp); out << "VAR " << j << ":" << i << " -->\n" << mk_pp(tmp, m_manager) << "\n"; } } } void substitution::display(std::ostream & out) { for (unsigned i = 0; i < m_subst.offsets_capacity(); i++) for (unsigned j = 0; j < m_subst.vars_capacity(); j++) { expr_offset r; if (find(j, i, r)) out << "VAR " << j << ":" << i << " --> " << r.get_offset() << "\n" << mk_pp(r.get_expr(), m_manager) << "\n"; } } z3-z3-4.8.7/src/ast/substitution/substitution.h000066400000000000000000000163251356505360400215150ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: substitution.h Abstract: A substitution, that is, a mapping from (variable, offset) to (expr, offset). We use offsets in order to avoid creating variants of terms. Author: Leonardo de Moura (leonardo) 2008-02-01. Revision History: nbjorner 2013-01-21: - reset the apply cache on pop_scope to make sure that popped substitutions are invalidated. - reset_cache if a new binding was added after application of a substitution. - Add m_refs to make sure terms in the range of the substitution have the same life-time as the substitution. - Remove reset_subst() function. If called without resetting the cache, then results of applying substitutions are incoherent. - Replace UNREACHABLE segment for quantifiers by recursive invocation of substitution that updates expressions within quantifiers. It shifts all variables in the domain of the current substitution by the number of quantified variables. --*/ #ifndef SUBSTITUTION_H_ #define SUBSTITUTION_H_ #include "ast/substitution/expr_offset_map.h" #include "ast/substitution/var_offset_map.h" #include "ast/ast_pp.h" /** \brief A mapping from (variable,offset) to expr_offset. */ class substitution { ast_manager & m_manager; var_offset_map m_subst; // field for backtracking typedef std::pair var_offset; svector m_vars; expr_ref_vector m_refs; unsigned_vector m_scopes; // fields for applying substitutions svector m_todo; expr_offset_map m_apply_cache; expr_ref_vector m_new_exprs; // fields for checking for cycles enum color { White, Grey, Black }; expr_offset_map m_color; // keep track of how substitution state was last updated. enum state { CLEAN, APPLY, INSERT }; state m_state; #ifdef Z3DEBUG unsigned m_max_offset_since_reset; #endif void apply_visit(expr_offset const & n, bool & visited); color get_color(expr_offset const & p) const; void set_color(expr_offset const & p, color c); void visit(expr_offset const & p, bool & visited); bool visit_children(expr_offset const & p); bool acyclic(expr_offset p); public: substitution(ast_manager & m); ast_manager & get_manager() const { return m_manager; } // ----------------------------------- // // Reserve memory for the given number of // offsets and variables. // // ----------------------------------- void reserve(unsigned num_offsets, unsigned num_vars) { m_subst.reserve(num_offsets, num_vars); } void reserve_offsets(unsigned num_offsets) { m_subst.reserve_offsets(num_offsets); } void reserve_vars(unsigned num_vars) { m_subst.reserve_vars(num_vars); } // ----------------------------------- // // Reset functions // // ----------------------------------- // reset everything void reset(); // reset only the substitution application cache void reset_cache(); // ----------------------------------- // // Backtracking // // ----------------------------------- void push_scope() { m_scopes.push_back(m_vars.size()); } void pop_scope(unsigned num_scopes = 1); unsigned get_scope_lvl() { return m_scopes.size(); } bool top_scope_has_bindings() const { return m_scopes.empty() ? !m_vars.empty() : m_scopes.back() < m_vars.size(); } unsigned get_num_bindings() const { return m_vars.size(); } // ----------------------------------- // // Cycle detection // // ----------------------------------- bool acyclic(); // ----------------------------------- // // Insertion & Lookup // // get_binding supplies a way to inspect the substitution. // // ----------------------------------- void insert(unsigned v_idx, unsigned offset, expr_offset const & t) { TRACE("subst_insert", tout << "inserting: #" << v_idx << ":" << offset << " --> " << mk_pp(t.get_expr(), m_manager) << ":" << t.get_offset() << "\n";); m_vars.push_back(var_offset(v_idx, offset)); m_refs.push_back(t.get_expr()); m_subst.insert(v_idx, offset, t); m_state = INSERT; } void insert(var * v, unsigned offset, expr_offset const & t) { insert(v->get_idx(), offset, t); } void insert(expr_offset v, expr_offset const & t) { SASSERT(is_var(v.get_expr())); insert(to_var(v.get_expr()), v.get_offset(), t); } bool find(unsigned v_idx, unsigned offset, expr_offset & r) const { return m_subst.find(v_idx, offset, r); } bool find(var * v, unsigned offset, expr_offset & r) const { return find(v->get_idx(), offset, r); } bool find(expr_offset v, expr_offset & r) const { SASSERT(is_var(v.get_expr())); return find(to_var(v.get_expr()), v.get_offset(), r); } void get_binding(unsigned binding_num, var_offset& var, expr_offset& r) { var = m_vars[binding_num]; VERIFY(m_subst.find(var.first, var.second, r)); } bool contains(var * v, unsigned offset) { expr_offset r; return find(v, offset, r); } // ----------------------------------- // // Application // // ----------------------------------- /** \brief Apply the current substitution to the given expression+offset. The result is an expression. The argument num_actual_offsets is the maximum offset used in a insert method since the last reset. The argument deltas is an array of size num_actual_offsets. It contains the variable delta for each offset. A free variable x:i in an expression offset t:j is mapped to the variable x+delta[i]. */ void apply(unsigned num_actual_offsets, unsigned const * deltas, expr_offset const & n, expr_ref & result) { apply(num_actual_offsets, deltas, n, expr_offset(nullptr, 0), expr_offset(nullptr, 0), result); } /** \brief Similar to the previous method, but occurrences of s in n are substituted by t. If s != expr_offset(0,0), then the cache is reset before and after the execution of this procedure. */ void apply(unsigned num_actual_offsets, unsigned const* deltas, expr_offset const & n, expr_offset const & s, expr_offset const & t, expr_ref & result); void apply(expr * n, expr_ref & result) { unsigned deltas[1] = { 0 }; apply(1, deltas, expr_offset(n, 0), result); } // ----------------------------------- // // Debugging // // ----------------------------------- /** \brief Dump the current substitution (for debugging purposes). */ void display(std::ostream & out, unsigned num_actual_offsets, unsigned const * deltas); /** \brief Dump the current substitution without normalizing expressions (for debugging purposes). */ void display(std::ostream & out); // ----------------------------------- // // Compare terms modulo a substitution // // ----------------------------------- bool compare(expr_offset t1, expr_offset t2); }; #endif z3-z3-4.8.7/src/ast/substitution/substitution_tree.cpp000066400000000000000000000707441356505360400230740ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: substitution_tree.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-02-04. Revision History: --*/ #include "ast/substitution/substitution_tree.h" #include "ast/ast_pp.h" #include "ast/ast_smt2_pp.h" /** \brief Return the next available register. */ unsigned substitution_tree::next_reg() { while(true) { unsigned curr = m_next_reg; if (curr > m_max_reg) m_max_reg = curr; m_next_reg++; if (curr >= m_used_regs.size() || !m_used_regs.get(curr)) return curr; } } inline void substitution_tree::push(svector & sv, subst const & s) { sv.push_back(s); m_manager.inc_ref(s.first); m_manager.inc_ref(s.second); } inline expr * substitution_tree::get_reg_value(unsigned ridx) { return m_registers.get(ridx, 0); } inline void substitution_tree::set_reg_value(unsigned ridx, expr * e) { m_registers.setx(ridx, e, 0); } inline void substitution_tree::erase_reg_from_todo(unsigned ridx) { SASSERT(m_registers[ridx]); m_registers[ridx] = 0; SASSERT(m_todo.contains(ridx)); m_todo.erase(ridx); } /** \brief Linearize the expressions in the registers stored in m_todo. Store the result in \c result. Example: m_todo = { 3, 4 } m_registers[3] = (f (g a)) m_registers[4] = b next_regs are 5 6 7 result: #3 -> (f #5); #4 -> b; #5 -> (g #6); #6 -> a */ void substitution_tree::linearize(svector & result) { ptr_buffer new_args; for (unsigned i = 0; i < m_todo.size(); i++) { unsigned ireg_idx = m_todo[i]; expr * n = get_reg_value(ireg_idx); var * ireg = m_manager.mk_var(ireg_idx, m_manager.get_sort(n)); if (is_var(n)) push(result, subst(ireg, n)); else { SASSERT(is_app(n)); app * new_app; unsigned num = to_app(n)->get_num_args(); if (num == 0) new_app = to_app(n); else { for (unsigned j = 0; j < num; j++) { unsigned oreg = next_reg(); set_reg_value(oreg, to_app(n)->get_arg(j)); m_todo.push_back(oreg); sort * s = m_manager.get_sort(get_reg_value(oreg)); new_args.push_back(m_manager.mk_var(oreg, s)); } new_app = m_manager.mk_app(to_app(n)->get_decl(), new_args.size(), new_args.c_ptr()); new_args.reset(); } push(result, subst(ireg, new_app)); } } } /** \brief Process the pair in := (f t_1 ... t_n) and out := (f r_1 ... r_n), where r_i's are variables (register ids), and t_i's are arbitrary expressions. The r_i's are added to the m_todo list, and m_registers[r_i] is assigned to t_i. If save_set_registers == true, then r_i's are stored in m_to_reset. */ void substitution_tree::process_args(app * in, app * out) { CTRACE("subst_tree_bug", in->get_num_args() != out->get_num_args(), tout << mk_ismt2_pp(in, m_manager) << "\n" << mk_ismt2_pp(out, m_manager) << "\n";); unsigned num = out->get_num_args(); for (unsigned i = 0; i < num; i++) { expr * in_arg = in->get_arg(i); expr * out_arg = out->get_arg(i); SASSERT(is_var(out_arg)); unsigned oreg = to_var(out_arg)->get_idx(); set_reg_value(oreg, in_arg); m_todo.push_back(oreg); } } /** \brief Reset registers in m_todo at [old_size, m_todo.size()) */ void substitution_tree::reset_registers(unsigned old_size) { SASSERT(m_todo.size() >= old_size); unsigned_vector::iterator it2 = m_todo.begin() + old_size; unsigned_vector::iterator end2 = m_todo.end(); for (; it2 != end2; ++it2) m_registers[*it2] = 0; m_todo.shrink(old_size); } /** \brief Return a measure on how compatible sv and the expressions to be processed are. */ unsigned substitution_tree::get_compatibility_measure(svector const & sv) { unsigned old_size = m_todo.size(); unsigned measure = 0; svector::const_iterator it = sv.begin(); svector::const_iterator end = sv.end(); for (; it != end; ++it) { subst const & s = *it; unsigned ireg = s.first->get_idx(); expr * out = s.second; expr * in = get_reg_value(ireg); if (is_var(out)) { if (out == in) measure += 1; } else { SASSERT(is_app(out)); if (in && is_app(in) && to_app(out)->get_decl() == to_app(in)->get_decl()) { measure += 2; process_args(to_app(in), to_app(out)); } } } reset_registers(old_size); return measure; } /** \brief Find the child of r that is most compatible with the expressions stored in the registers in m_todo. Return 0 if none of the children has any compatible substitution entry. */ substitution_tree::node * substitution_tree::find_best_child(node * r) { SASSERT(!r->m_leaf); #ifdef Z3DEBUG unsigned todo_size = m_todo.size(); #endif node * best_child = nullptr; unsigned max_measure = 0; node * curr_child = r->m_first_child; while (curr_child) { unsigned measure = get_compatibility_measure(curr_child->m_subst); if (measure > max_measure) { best_child = curr_child; max_measure = measure; } curr_child = curr_child->m_next_sibling; } SASSERT(todo_size == m_todo.size()); return best_child; } /** \brief Reset datastructures used to insert/erase elements from the substitution tree. */ void substitution_tree::reset_compiler() { m_todo.reset(); m_used_regs.reset(); m_next_reg = 1; // register 0 is reserved for input. DEBUG_CODE({ ptr_vector::iterator it = m_registers.begin(); ptr_vector::iterator end = m_registers.end(); for (; it != end; ++it) { SASSERT(*it == 0); } }); } /** \brief Create a node with the linearization for all registers in todo. Attach new_expr to it. */ substitution_tree::node * substitution_tree::mk_node_for(expr * new_expr) { node * n = alloc(node, true); linearize(n->m_subst); n->m_expr = new_expr; m_manager.inc_ref(new_expr); return n; } /** \brief Mark register ridx as used. */ void substitution_tree::mark_used_reg(unsigned ridx) { if (ridx >= m_used_regs.size()) m_used_regs.resize(ridx+1); m_used_regs.set(ridx); } /** \brief Mark (m_used_regs) all registers used in \c sv. */ void substitution_tree::mark_used_regs(svector const & sv) { svector::const_iterator it = sv.begin(); svector::const_iterator end = sv.end(); for (; it != end; ++it) { subst const & s = *it; mark_used_reg(s.first->get_idx()); if (is_app(s.second)) { unsigned num_args = to_app(s.second)->get_num_args(); for (unsigned i = 0; i < num_args; i++) { expr * arg = to_app(s.second)->get_arg(i); SASSERT(is_var(arg)); mark_used_reg(to_var(arg)->get_idx()); } } } } /** \brief Insert a new expression in the substitution tree. */ void substitution_tree::insert(expr * new_expr) { if (is_app(new_expr)) { insert(to_app(new_expr)); } else { SASSERT(is_var(new_expr)); sort * s = to_var(new_expr)->get_sort(); unsigned id = s->get_decl_id(); if (id >= m_vars.size()) m_vars.resize(id+1); if (m_vars[id] == 0) m_vars[id] = alloc(var_ref_vector, m_manager); var_ref_vector * v = m_vars[id]; if (!v->contains(to_var(new_expr))) v->push_back(to_var(new_expr)); } } /** \brief Insert a new application in the substitution tree. */ void substitution_tree::insert(app * new_expr) { reset_compiler(); set_reg_value(0, new_expr); m_todo.push_back(0); func_decl * d = new_expr->get_decl(); unsigned id = d->get_decl_id(); if (id >= m_roots.size()) m_roots.resize(id+1); if (!m_roots[id]) { // there is no tree for the function symbol heading new_expr m_roots[id] = mk_node_for(new_expr); reset_registers(0); m_size++; return; } node * r = m_roots[id]; while (true) { m_compatible.reset(); m_incompatible.reset(); svector & sv = r->m_subst; // separate sv in the set of compatible & incompatible instructions svector::iterator it = sv.begin(); svector::iterator end = sv.end(); for (; it != end; ++it) { subst & s = *it; unsigned ireg = s.first->get_idx(); expr * out = s.second; expr * in = get_reg_value(ireg); SASSERT(is_var(out) || is_app(out)); if (is_var(out)) { if (out == in) { erase_reg_from_todo(ireg); m_compatible.push_back(s); } else { m_incompatible.push_back(s); } } else { if (in && is_app(in) && to_app(out)->get_decl() == to_app(in)->get_decl()) { erase_reg_from_todo(ireg); m_compatible.push_back(s); process_args(to_app(in), to_app(out)); } else { m_incompatible.push_back(s); } } } // process m_compatible & m_incompatible if (m_incompatible.empty()) { if (m_todo.empty()) { // nothing else to process // new_expr is already in the substitution tree SASSERT(r->m_leaf && r->m_expr == new_expr); reset_registers(0); return; } else { mark_used_regs(r->m_subst); node * best_child = find_best_child(r); if (best_child == nullptr) { // there is no compatible child node * n = mk_node_for(new_expr); n->m_next_sibling = r->m_first_child; r->m_first_child = n; reset_registers(0); m_size++; return; } else { // continue with best_child r = best_child; } } } else { SASSERT(!m_compatible.empty()); SASSERT(!m_incompatible.empty()); mark_used_regs(m_compatible); r->m_subst.swap(m_compatible); node * n = mk_node_for(new_expr); node * incomp = alloc(node, r->m_leaf); incomp->m_subst.swap(m_incompatible); if (r->m_leaf) { incomp->m_expr = r->m_expr; r->m_leaf = false; } else incomp->m_first_child = r->m_first_child; incomp->m_next_sibling = n; SASSERT(!r->m_leaf); r->m_first_child = incomp; reset_registers(0); m_size++; return; } } } /** \brief Return true if sv is fully compatible with the expressions in the registers in m_todo. */ bool substitution_tree::is_fully_compatible(svector const & sv) { unsigned old_size = m_todo.size(); svector::const_iterator it = sv.begin(); svector::const_iterator end = sv.end(); for (; it != end; ++it) { subst const & s = *it; unsigned ireg = s.first->get_idx(); expr * out = s.second; expr * in = get_reg_value(ireg); if (is_var(out)) { if (out != in) { reset_registers(old_size); return false; } } else { if (!in || !is_app(in) || to_app(in)->get_decl() != to_app(out)->get_decl()) { reset_registers(old_size); return false; } process_args(to_app(in), to_app(out)); } } reset_registers(old_size); return true; } /** \brief Return a child of r that is fully compatible with the expressions in the registers in m_todo. */ bool substitution_tree::find_fully_compatible_child(node * r, node * & prev, node * & child) { SASSERT(!r->m_leaf); prev = nullptr; child = r->m_first_child; while (child) { if (is_fully_compatible(child->m_subst)) return true; prev = child; child = child->m_next_sibling; } return false; } inline bool substitution_tree::at_least_3_children(node * r) { return !r->m_leaf && r->m_first_child->m_next_sibling && r->m_first_child->m_next_sibling->m_next_sibling; } /** \brief Remove expression from the substitution tree. Do nothing, if n is not in the tree. */ void substitution_tree::erase(expr * e) { if (is_app(e)) erase(to_app(e)); else { SASSERT(is_var(e)); sort * s = to_var(e)->get_sort(); unsigned id = s->get_decl_id(); if (id >= m_vars.size() || m_vars[id] == 0) return; var_ref_vector * v = m_vars[id]; v->erase(to_var(e)); } } /** \brief Remove application from the substitution tree. Do nothing, if n is not in the tree. */ void substitution_tree::erase(app * e) { func_decl * d = e->get_decl(); unsigned id = d->get_decl_id(); if (id >= m_roots.size() || !m_roots[id]) return; reset_compiler(); set_reg_value(0, e); m_todo.push_back(0); node * r = m_roots[id]; node * parent = nullptr; node * prev = nullptr; while (true) { svector & sv = r->m_subst; svector::iterator it = sv.begin(); svector::iterator end = sv.end(); for (; it != end; ++it) { subst & s = *it; unsigned ireg = s.first->get_idx(); expr * out = s.second; expr * in = get_reg_value(ireg); SASSERT(is_var(out) || is_app(out)); if (is_var(out)) { if (out != in) { reset_registers(0); return; // node is not in the substitution tree } erase_reg_from_todo(ireg); } else { if (!in || !is_app(in) || to_app(out)->get_decl() != to_app(in)->get_decl()) { reset_registers(0); return; // node is not in the substitution tree } erase_reg_from_todo(ireg); process_args(to_app(in), to_app(out)); } } if (m_todo.empty()) { reset_registers(0); SASSERT(r->m_expr == e); if (parent == nullptr) { delete_node(r); m_roots[id] = 0; } else if (at_least_3_children(parent)) { if (prev == nullptr) parent->m_first_child = r->m_next_sibling; else prev->m_next_sibling = r->m_next_sibling; delete_node(r); } else { SASSERT(parent->m_first_child && parent->m_first_child->m_next_sibling && !parent->m_first_child->m_next_sibling->m_next_sibling); node * other_child = prev ? prev : r->m_next_sibling; SASSERT(other_child); parent->m_subst.append(other_child->m_subst); parent->m_leaf = other_child->m_leaf; if (other_child->m_leaf) parent->m_expr = other_child->m_expr; else parent->m_first_child = other_child->m_first_child; delete_node(r); dealloc(other_child); // Remark: I didn't use delete_node since its resources were sent to parent. } m_size --; return; } else { parent = r; if (!find_fully_compatible_child(r, prev, r)) { // node is not in the substitution tree reset_registers(0); return; } // continue with fully compatible child } } } void substitution_tree::delete_node(node * n) { ptr_buffer todo; SASSERT(todo.empty()); todo.push_back(n); while (!todo.empty()) { node * n = todo.back(); todo.pop_back(); svector::iterator it2 = n->m_subst.begin(); svector::iterator end2 = n->m_subst.end(); for (; it2 != end2; ++it2) { m_manager.dec_ref(it2->first); m_manager.dec_ref(it2->second); } if (n->m_leaf) m_manager.dec_ref(n->m_expr); else { node * c = n->m_first_child; while (c) { todo.push_back(c); c = c->m_next_sibling; } } dealloc(n); } } void substitution_tree::reset() { ptr_vector::iterator it = m_roots.begin(); ptr_vector::iterator end = m_roots.end(); for (; it != end; ++it) { if (*it) delete_node(*it); } m_roots.reset(); std::for_each(m_vars.begin(), m_vars.end(), delete_proc()); m_vars.reset(); m_size = 0; } void substitution_tree::display(std::ostream & out, subst const & s) const { out << "r!" << s.first->get_idx() << " -> "; if (is_app(s.second)) { unsigned num = to_app(s.second)->get_num_args(); if (num == 0) out << to_app(s.second)->get_decl()->get_name(); else { out << "(" << to_app(s.second)->get_decl()->get_name(); for (unsigned i = 0; i < num; i++) out << " r!" << to_var(to_app(s.second)->get_arg(i))->get_idx(); out << ")"; } } else { out << mk_pp(s.second, m_manager); } } void substitution_tree::display(std::ostream & out, svector const & sv) const { svector::const_iterator it = sv.begin(); svector::const_iterator end = sv.end(); for (bool first = true; it != end; ++it, first = false) { subst const & s = *it; if (!first) out << "; "; display(out, s); } } void substitution_tree::display(std::ostream & out, node * n, unsigned delta) const { for (unsigned i = 0; i < delta; i++) out << " "; display(out, n->m_subst); if (n->m_leaf) { params_ref p; p.set_bool("single_line", true); out << " ==> "; out << mk_pp(n->m_expr, m_manager, p); out << "\n"; } else { out << "\n"; node * c = n->m_first_child; while (c) { display(out, c, delta+1); c = c->m_next_sibling; } } } bool substitution_tree::backtrack() { while (!m_bstack.empty()) { TRACE("st", tout << "backtracking...\n";); m_subst->pop_scope(); node * n = m_bstack.back(); if (n->m_next_sibling) { m_bstack.back() = n->m_next_sibling; return true; } m_bstack.pop_back(); } return false; } inline expr_offset substitution_tree::find(expr_offset p) { TRACE("substitution_tree_bug", tout << "find...\n";); while (is_var(p.get_expr())) { TRACE("substitution_tree_bug", tout << mk_pp(p.get_expr(), m_manager) << " " << p.get_offset() << "\n";); if (!m_subst->find(to_var(p.get_expr()), p.get_offset(), p)) return p; } return p; } template bool substitution_tree::bind_var(var * v, unsigned offset, expr_offset const & p) { TRACE("st", tout << "bind_var: " << mk_pp(v, m_manager) << " " << offset << "\n" << mk_pp(p.get_expr(), m_manager) << " " << p.get_offset() << "\n";); if (Mode == STV_INST && offset == m_st_offset) { SASSERT(!is_var(p.get_expr()) || p.get_offset() != m_reg_offset); if (is_var(p.get_expr()) && p.get_offset() == m_in_offset) { m_subst->insert(p, expr_offset(v, offset)); return true; } return false; } if (Mode == STV_GEN && offset == m_in_offset) { SASSERT(!is_var(p.get_expr()) || p.get_offset() != m_reg_offset); if (is_var(p.get_expr()) && p.get_offset() == m_st_offset) { m_subst->insert(p, expr_offset(v, offset)); return true; } return false; } m_subst->insert(v, offset, p); TRACE("st_bug", tout << "substitution updated\n"; m_subst->display(tout);); return true; } template bool substitution_tree::unify_match(expr_offset p1, expr_offset p2) { svector & todo = m_visit_todo; todo.reset(); todo.push_back(entry(p1, p2)); while (!todo.empty()) { entry const & e = todo.back(); p1 = find(e.first); p2 = find(e.second); todo.pop_back(); if (p1 != p2) { expr * n1 = p1.get_expr(); expr * n2 = p2.get_expr(); SASSERT(!is_quantifier(n1)); SASSERT(!is_quantifier(n2)); bool v1 = is_var(n1); bool v2 = is_var(n2); TRACE("st", tout << "n1: " << mk_pp(n1, m_manager) << " " << p1.get_offset() << "\n"; tout << "n2: " << mk_pp(n2, m_manager) << " " << p2.get_offset() << "\n";); if (v1 && v2) { if (p2.get_offset() == m_reg_offset) std::swap(p1, p2); if (!bind_var(to_var(p1.get_expr()), p1.get_offset(), p2)) return false; } else if (v1) { if (!bind_var(to_var(n1), p1.get_offset(), p2)) return false; } else if (v2) { if (!bind_var(to_var(n2), p2.get_offset(), p1)) return false; } else { app * a1 = to_app(n1); app * a2 = to_app(n2); unsigned off1 = p1.get_offset(); unsigned off2 = p2.get_offset(); if (a1->get_decl() != a2->get_decl() || a1->get_num_args() != a2->get_num_args()) return false; unsigned j = a1->get_num_args(); while (j > 0) { --j; entry new_e(expr_offset(a1->get_arg(j), off1), expr_offset(a2->get_arg(j), off2)); todo.push_back(new_e); } } } } return true; } template bool substitution_tree::visit_vars(expr * e, st_visitor & st) { if (m_vars.empty()) return true; // continue sort * s = m_manager.get_sort(e); unsigned s_id = s->get_decl_id(); if (s_id < m_vars.size()) { var_ref_vector * v = m_vars[s_id]; if (v && !v->empty()) { unsigned sz = v->size(); for (unsigned i = 0; i < sz; i++) { var * curr = v->get(i); m_subst->push_scope(); if (unify_match(expr_offset(curr, m_st_offset), expr_offset(e, m_in_offset))) { if (Mode != STV_UNIF || m_subst->acyclic()) { if (!st(curr)) { m_subst->pop_scope(); return false; // stop } } } m_subst->pop_scope(); } } } return true; // continue } template bool substitution_tree::visit(svector const & sv) { svector::const_iterator it = sv.begin(); svector::const_iterator end = sv.end(); for (; it != end; ++it) { subst const & s = *it; TRACE("st", tout << "processing subst:\n"; display(tout, s); tout << "\n";); var * rin = s.first; expr * out = s.second; expr_offset p1(rin, m_reg_offset); expr_offset p2(out, is_var(out) ? m_st_offset : m_reg_offset); if (!unify_match(p1, p2)) return false; } return true; } template bool substitution_tree::visit(expr * e, st_visitor & st, node * r) { m_bstack.reset(); m_bstack.push_back(r); m_subst->push_scope(); m_subst->insert(static_cast(0), m_reg_offset, expr_offset(e, m_in_offset)); while (true) { node * n = m_bstack.back(); TRACE("st", tout << "push scope...\n";); m_subst->push_scope(); TRACE("st", tout << "processing node:\n"; display(tout, n->m_subst); tout << "\n";); if (visit(n->m_subst)) { if (n->m_leaf) { // if searching for unifiers and the substitution is cyclic, then backtrack. if (Mode == STV_UNIF && !m_subst->acyclic()) { if (!backtrack()) break; } else { TRACE("st_bug", tout << "found match:\n"; m_subst->display(tout); tout << "m_subst: " << m_subst << "\n";); if (!st(n->m_expr)) { clear_stack(); return false; } if (!backtrack()) break; } } else { m_bstack.push_back(n->m_first_child); } } else if (!backtrack()) break; } clear_stack(); return true; } void substitution_tree::clear_stack() { while (!m_bstack.empty()) { m_subst->pop_scope(); m_bstack.pop_back(); } m_subst->pop_scope(); } template void substitution_tree::visit(expr * e, st_visitor & st, unsigned in_offset, unsigned st_offset, unsigned reg_offset) { m_in_offset = in_offset; m_st_offset = st_offset; m_reg_offset = reg_offset; m_subst = &(st.get_substitution()); m_subst->reserve_vars(get_approx_num_regs()); if (visit_vars(e, st)) { if (is_app(e)) { func_decl * d = to_app(e)->get_decl(); unsigned id = d->get_decl_id(); node * r = m_roots.get(id, 0); if (r) visit(e, st, r); } else { SASSERT(is_var(e)); ptr_vector::iterator it = m_roots.begin(); ptr_vector::iterator end = m_roots.end(); for (; it != end; ++it) { node * r = *it; if (r != nullptr) { var * v = r->m_subst[0].first; if (v->get_sort() == to_var(e)->get_sort()) if (!visit(e, st, r)) break; } } } } } void substitution_tree::unify(expr * e, st_visitor & v, unsigned in_offset, unsigned st_offset, unsigned reg_offset) { visit(e, v, in_offset, st_offset, reg_offset); } void substitution_tree::inst(expr * e, st_visitor & v, unsigned in_offset, unsigned st_offset, unsigned reg_offset) { visit(e, v, in_offset, st_offset, reg_offset); } void substitution_tree::gen(expr * e, st_visitor & v, unsigned in_offset, unsigned st_offset, unsigned reg_offset) { visit(e, v, in_offset, st_offset, reg_offset); } void substitution_tree::display(std::ostream & out) const { out << "substitution tree:\n"; ptr_vector::const_iterator it = m_roots.begin(); ptr_vector::const_iterator end = m_roots.end(); for (; it != end; ++it) if (*it) display(out, *it, 0); bool found_var = false; ptr_vector::const_iterator it2 = m_vars.begin(); ptr_vector::const_iterator end2 = m_vars.end(); for (; it2 != end2; ++it2) { var_ref_vector * v = *it2; if (v == nullptr) continue; // m_vars may contain null pointers. See substitution_tree::insert. unsigned num = v->size(); for (unsigned i = 0; i < num; i++) { if (!found_var) { found_var = true; out << "vars: "; } out << mk_pp(v->get(i), m_manager) << " "; } } if (found_var) out << "\n"; } substitution_tree::substitution_tree(ast_manager & m): m_manager(m), m_max_reg(0), m_size(0) { } substitution_tree::~substitution_tree() { reset(); } z3-z3-4.8.7/src/ast/substitution/substitution_tree.h000066400000000000000000000104001356505360400225200ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: substitution_tree.h Abstract: Substitution Trees Author: Leonardo de Moura (leonardo) 2008-02-03. Revision History: --*/ #ifndef SUBSTITUTION_TREE_H_ #define SUBSTITUTION_TREE_H_ #include "ast/ast.h" #include "ast/substitution/substitution.h" /** \brief Substitution tree visitor. */ class st_visitor { protected: substitution & m_subst; public: st_visitor(substitution & s):m_subst(s) {} virtual ~st_visitor() {} substitution & get_substitution() { return m_subst; } virtual bool operator()(expr * e) { return true; } }; /** \brief Substitution tree term index. */ class substitution_tree { typedef std::pair subst; struct node { bool m_leaf; svector m_subst; node * m_next_sibling; union { node * m_first_child; expr * m_expr; }; node(bool leaf):m_leaf(leaf), m_next_sibling(nullptr), m_first_child(nullptr) {} }; ast_manager & m_manager; ptr_vector m_roots; unsigned m_max_reg; ptr_vector m_registers; unsigned m_size; ptr_vector m_vars; // mapping from decl_id to var_ref_vector // Compilation time fields unsigned m_next_reg; bit_vector m_used_regs; unsigned_vector m_todo; svector m_compatible; svector m_incompatible; // Execution time fields substitution * m_subst; ptr_vector m_bstack; unsigned m_in_offset; unsigned m_st_offset; unsigned m_reg_offset; typedef std::pair entry; svector m_visit_todo; unsigned next_reg(); void push(svector & sv, subst const & s); expr * get_reg_value(unsigned ridx); void set_reg_value(unsigned ridx, expr * e); void erase_reg_from_todo(unsigned ridx); void linearize(svector & result); void process_args(app * in, app * out); void reset_registers(unsigned old_size); unsigned get_compatibility_measure(svector const & sv); node * find_best_child(node * r); void reset_compiler(); node * mk_node_for(expr * new_expr); void mark_used_reg(unsigned ridx); void mark_used_regs(svector const & sv); bool is_fully_compatible(svector const & sv); bool find_fully_compatible_child(node * r, node * & prev, node * & child); static bool at_least_3_children(node * r); void delete_node(node * n); void display(std::ostream & out, subst const & s) const; void display(std::ostream & out, svector const & sv) const; void display(std::ostream & out, node * n, unsigned delta) const; enum st_visit_mode { STV_UNIF, STV_INST, STV_GEN }; expr_offset find(expr_offset p); bool backtrack(); template bool bind_var(var * v, unsigned offset, expr_offset const & p); template bool unify_match(expr_offset p1, expr_offset p2); template bool visit_vars(expr * e, st_visitor & st); template bool visit(svector const & s); template bool visit(expr * e, st_visitor & st, node * r); template void visit(expr * e, st_visitor & st, unsigned in_offset, unsigned st_offset, unsigned reg_offset); void clear_stack(); public: substitution_tree(ast_manager & m); ~substitution_tree(); void insert(app * n); void insert(expr * n); void erase(app * n); void erase(expr * n); void reset(); bool empty() const { return m_size == 0; } void unify(expr * e, st_visitor & v, unsigned in_offset = 0, unsigned st_offset = 1, unsigned reg_offset = 2); void inst(expr * e, st_visitor & v, unsigned in_offset = 0, unsigned st_offset = 1, unsigned reg_offset = 2); void gen(expr * e, st_visitor & v, unsigned in_offset = 0, unsigned st_offset = 1, unsigned reg_offset = 2); unsigned get_approx_num_regs() const { return m_max_reg + 1; } void display(std::ostream & out) const; }; #endif /* SUBSTITUTION_TREE_H_ */ z3-z3-4.8.7/src/ast/substitution/unifier.cpp000066400000000000000000000117701356505360400207340ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: unifier.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-01-28. Revision History: --*/ #include "ast/substitution/unifier.h" #include "ast/ast_pp.h" void unifier::reset(unsigned num_offsets) { m_todo.reset(); m_find.reset(); m_size.reset(); } /** \brief Find with path compression. */ expr_offset unifier::find(expr_offset p) { buffer path; expr_offset next; while (m_find.find(p, next)) { path.push_back(p); p = next; } buffer::iterator it = path.begin(); buffer::iterator end = path.end(); for (; it != end; ++it) { expr_offset & prev = *it; m_find.insert(prev, p); } return p; } void unifier::save_var(expr_offset const & p, expr_offset const & t) { expr * n = p.get_expr(); if (is_var(n)) { unsigned off = p.get_offset(); m_subst->insert(to_var(n)->get_idx(), off, t); } } /** \brief Merge the equivalence classes of n1 and n2. n2 will be the root of the resultant equivalence class. */ void unifier::union1(expr_offset const & n1, expr_offset const & n2) { DEBUG_CODE({ expr_offset f; SASSERT(!m_find.find(n1, f)); SASSERT(!m_find.find(n2, f)); }); unsigned sz1 = 1; unsigned sz2 = 1; m_size.find(n1, sz1); m_size.find(n2, sz2); m_find.insert(n1, n2); m_size.insert(n2, sz1 + sz2); save_var(n1, n2); } /** \brief Merge the equivalence classes of n1 and n2. The root of the resultant equivalence class is the one with more elements. */ void unifier::union2(expr_offset n1, expr_offset n2) { DEBUG_CODE({ expr_offset f; SASSERT(!m_find.find(n1, f)); SASSERT(!m_find.find(n2, f)); }); unsigned sz1 = 1; unsigned sz2 = 1; m_size.find(n1, sz1); m_size.find(n2, sz2); if (sz1 > sz2) std::swap(n1, n2); m_find.insert(n1, n2); m_size.insert(n2, sz1 + sz2); save_var(n1, n2); } bool unifier::unify_core(expr_offset p1, expr_offset p2) { entry e(p1, p2); m_todo.push_back(e); while (!m_todo.empty()) { entry const & e = m_todo.back(); p1 = find(e.first); p2 = find(e.second); m_todo.pop_back(); if (p1 != p2) { expr * n1 = p1.get_expr(); expr * n2 = p2.get_expr(); SASSERT(!is_quantifier(n1)); SASSERT(!is_quantifier(n2)); bool v1 = is_var(n1); bool v2 = is_var(n2); if (v1 && v2) { union2(p1, p2); } else if (v1) { union1(p1, p2); } else if (v2) { union1(p2, p1); } else { app * a1 = to_app(n1); app * a2 = to_app(n2); unsigned off1 = p1.get_offset(); unsigned off2 = p2.get_offset(); if (a1->get_decl() != a2->get_decl() || a1->get_num_args() != a2->get_num_args()) return false; union2(p1, p2); unsigned j = a1->get_num_args(); while (j > 0) { --j; entry new_e(expr_offset(a1->get_arg(j), off1), expr_offset(a2->get_arg(j), off2)); m_todo.push_back(new_e); } } } } return true; } bool unifier::operator()(unsigned num_exprs, expr ** es, substitution & s, bool use_offsets) { SASSERT(num_exprs > 0); unsigned num_offsets = use_offsets ? num_exprs : 1; reset(num_offsets); m_subst = &s; #if 1 TRACE("unifier", for (unsigned i = 0; i < num_exprs; ++i) tout << mk_pp(es[i], m_manager) << "\n";); for (unsigned i = s.get_num_bindings(); i > 0; ) { --i; std::pair bound; expr_offset root, child; s.get_binding(i, bound, root); TRACE("unifier", tout << bound.first << " |-> " << mk_pp(root.get_expr(), m_manager) << "\n";); if (is_var(root.get_expr())) { var* v = m_manager.mk_var(bound.first,to_var(root.get_expr())->get_sort()); child = expr_offset(v, bound.second); unsigned sz1 = 1; unsigned sz2 = 1; m_size.find(child, sz1); m_size.find(root, sz2); m_find.insert(child, root); m_size.insert(root, sz1 + sz2); } } #endif for (unsigned i = 0; i < num_exprs - 1; i++) { if (!unify_core(expr_offset(es[i], use_offsets ? i : 0), expr_offset(es[i+1], use_offsets ? i + 1 : 0))) { m_last_call_succeeded = false; return m_last_call_succeeded; } } m_last_call_succeeded = m_subst->acyclic(); return m_last_call_succeeded; } bool unifier::operator()(expr * e1, expr * e2, substitution & s, bool use_offsets) { expr * es[2] = { e1, e2 }; return operator()(2, es, s, use_offsets); } z3-z3-4.8.7/src/ast/substitution/unifier.h000066400000000000000000000035121356505360400203740ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: unifier.h Abstract: Quasi-linear unification. Author: Leonardo de Moura (leonardo) 2008-01-28. Revision History: --*/ #ifndef UNIFIER_H_ #define UNIFIER_H_ #include "ast/ast.h" #include "ast/substitution/substitution.h" /** \brief Functor for unifying expressions. It implements a quasi-linear unification algorithm. It has support for two different variable banks: left and right. That is, variable i in left bank is considered different from variable i in the right bank. This feature allows us to avoid unnecessary variable renaming. */ class unifier { typedef std::pair entry; ast_manager & m_manager; substitution * m_subst; svector m_todo; expr_offset_map m_find; expr_offset_map m_size; bool m_last_call_succeeded; expr_offset find(expr_offset n); void save_var(expr_offset const & p, expr_offset const & t); void union1(expr_offset const & n1, expr_offset const & n2); void union2(expr_offset n1, expr_offset n2); void reset(unsigned num_offsets); bool unify_core(expr_offset p1, expr_offset p2); public: unifier(ast_manager & m):m_manager(m), m_last_call_succeeded(false) {} /** \brief Unify the given expressions. Return true if succeeded, and store the result in the given substitution. If use_offsets is true, then the variables in the given expressions are assumed to be in different banks. */ bool operator()(unsigned num_exprs, expr ** es, substitution & s, bool use_offsets = true); bool operator()(expr * e1, expr * e2, substitution & s, bool use_offsets = true); }; #endif /* UNIFIER_H_ */ z3-z3-4.8.7/src/ast/substitution/var_offset_map.h000066400000000000000000000056611356505360400217350ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: var_offset_map.h Abstract: A generic mapping from (var, offset) to a value T. Author: Leonardo de Moura (leonardo) 2008-02-01. Revision History: --*/ #ifndef VAR_OFFSET_MAP_H_ #define VAR_OFFSET_MAP_H_ #include "ast/ast.h" #include "util/vector.h" /** \brief A mapping from variable-id + offset to some value of type T. */ template class var_offset_map { protected: struct data { T m_data; unsigned m_timestamp; data():m_timestamp(0) {} }; svector m_map; unsigned m_num_offsets; unsigned m_num_vars; unsigned m_timestamp; public: var_offset_map(): m_num_offsets(0), m_num_vars(0), m_timestamp(1) { } void reset() { m_timestamp++; if (m_timestamp == UINT_MAX) { typename svector::iterator it = m_map.begin(); typename svector::iterator end = m_map.end(); for (; it != end; ++it) it->m_timestamp = 0; m_timestamp = 1; } } unsigned offsets_capacity() const { return m_num_offsets; } unsigned vars_capacity() const { return m_num_vars; } void reserve(unsigned num_offsets, unsigned num_vars) { if (num_offsets > m_num_offsets || num_vars > m_num_vars) { unsigned sz = num_offsets * num_vars; m_map.resize(sz); m_num_vars = num_vars; m_num_offsets = num_offsets; } reset(); } void reserve_offsets(unsigned num_offsets) { reserve(num_offsets, m_num_vars); } void reserve_vars(unsigned num_vars) { reserve(m_num_offsets, num_vars); } void insert(unsigned v_idx, unsigned offset, T const & t) { SASSERT(v_idx < m_num_vars); SASSERT(offset < m_num_offsets); unsigned idx = v_idx + offset * m_num_vars; SASSERT(idx < m_map.size()); data & d = m_map[idx]; d.m_data = t; d.m_timestamp = m_timestamp; } void insert(var * v, unsigned offset, T const & t) { insert(v->get_idx(), offset, t); } bool find(unsigned v_idx, unsigned offset, T & r) const { SASSERT(v_idx < m_num_vars); SASSERT(offset < m_num_offsets); unsigned idx = v_idx + offset * m_num_vars; data const & d = m_map[idx]; SASSERT(d.m_timestamp <= m_timestamp); if (d.m_timestamp == m_timestamp) { r = d.m_data; return true; } return false; } bool find(var * v, unsigned offset, T & r) const { return find(v->get_idx(), offset, r); } void erase(unsigned v_idx, unsigned offset) { SASSERT(v_idx < m_num_vars); SASSERT(offset < m_num_offsets); unsigned idx = v_idx + offset * m_num_vars; m_map[idx].m_timestamp = 0; } }; #endif /* VAR_OFFSET_MAP_H_ */ z3-z3-4.8.7/src/ast/used_symbols.h000066400000000000000000000057701356505360400166770ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: used_symbols.h Abstract: Collect the symbols used in an expression. Author: Leonardo de Moura (leonardo) 2011-01-11. Revision History: --*/ #ifndef USED_SYMBOLS_H_ #define USED_SYMBOLS_H_ #include "ast/ast.h" #include "util/hashtable.h" #include "util/obj_hashtable.h" struct do_nothing_rename_proc { symbol operator()(symbol const & s) const { return s; } }; /** \brief Functor for collecting the symbols used in an expression. */ template class used_symbols : public RENAME_PROC { typedef hashtable symbol_set; symbol_set m_used; obj_hashtable m_visited; ptr_vector m_todo; void found(symbol const & s) { m_used.insert(RENAME_PROC::operator()(s)); } void visit(expr * n) { if (!m_visited.contains(n)) { m_visited.insert(n); m_todo.push_back(n); } } public: used_symbols(RENAME_PROC const & p = RENAME_PROC()): RENAME_PROC(p) { } void operator()(expr * n, bool ignore_quantifiers = false) { m_visited.reset(); m_used.reset(); m_todo.reset(); visit(n); while (!m_todo.empty()) { n = m_todo.back(); m_todo.pop_back(); unsigned j; switch (n->get_kind()) { case AST_APP: found(to_app(n)->get_decl()->get_name()); j = to_app(n)->get_num_args(); while (j > 0) { --j; visit(to_app(n)->get_arg(j)); } break; case AST_QUANTIFIER: if (!ignore_quantifiers) { found(to_quantifier(n)->get_qid()); unsigned num_decls = to_quantifier(n)->get_num_decls(); for (unsigned i = 0; i < num_decls; i++) found(to_quantifier(n)->get_decl_name(i)); unsigned num_pats = to_quantifier(n)->get_num_patterns(); for (unsigned i = 0; i < num_pats; i++) visit(to_quantifier(n)->get_pattern(i)); unsigned num_no_pats = to_quantifier(n)->get_num_no_patterns(); for (unsigned i = 0; i < num_no_pats; i++) visit(to_quantifier(n)->get_no_pattern(i)); visit(to_quantifier(n)->get_expr()); } break; default: break; } } } bool contains(symbol const & s) const { return m_used.contains(RENAME_PROC::operator()(s)); } bool contains_core(symbol const & s) const { return m_used.contains(s); } void insert(symbol const & s) { m_used.insert(RENAME_PROC::operator()(s)); } void insert_core(symbol const & s) { m_used.insert(s); } void erase_core(symbol const & s) { m_used.erase(s); } }; #endif /* USED_SYMBOLS_H_ */ z3-z3-4.8.7/src/ast/used_vars.cpp000066400000000000000000000055201356505360400165060ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: used_vars.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-01-14. Revision History: --*/ #include "ast/used_vars.h" void used_vars::process(expr * n, unsigned delta) { unsigned j, idx; m_cache.reset(); m_todo.reset(); m_todo.push_back(expr_delta_pair(n, delta)); while (!m_todo.empty()) { expr_delta_pair const & p = m_todo.back(); n = p.m_node; if (n->get_ref_count() > 1 && m_cache.contains(p)) { m_todo.pop_back(); continue; } if (n->get_ref_count() > 1) { // cache only shared and non-constant nodes m_cache.insert(p); } delta = p.m_delta; m_todo.pop_back(); switch (n->get_kind()) { case AST_APP: j = to_app(n)->get_num_args(); while (j > 0) { --j; expr * arg = to_app(n)->get_arg(j); m_todo.push_back(expr_delta_pair(arg, delta)); } break; case AST_VAR: idx = to_var(n)->get_idx(); if (idx >= delta) { idx = idx - delta; if (idx >= m_found_vars.size()) m_found_vars.resize(idx + 1); m_found_vars[idx] = to_var(n)->get_sort(); } break; case AST_QUANTIFIER: // recurse so that memoization is correct with respect to 'delta'. delta += to_quantifier(n)->get_num_decls(); j = to_quantifier(n)->get_num_patterns(); while (j > 0) { --j; m_todo.push_back(expr_delta_pair(to_quantifier(n)->get_pattern(j), delta)); } j = to_quantifier(n)->get_num_no_patterns(); while (j > 0) { --j; m_todo.push_back(expr_delta_pair(to_quantifier(n)->get_no_pattern(j), delta)); } m_todo.push_back(expr_delta_pair(to_quantifier(n)->get_expr(), delta)); break; default: break; } } } bool used_vars::uses_all_vars(unsigned num_decls) const { if (num_decls > m_found_vars.size()) return false; for (unsigned i = 0; i < num_decls; i++) { if (!m_found_vars[i]) return false; } return true; } bool used_vars::uses_a_var(unsigned num_decls) const { num_decls = std::min(num_decls, m_found_vars.size()); for (unsigned i = 0; i < num_decls; i++) { if (m_found_vars[i]) return true; } return false; } unsigned used_vars::get_num_vars() const { unsigned r = 0; unsigned num = m_found_vars.size(); for (unsigned i = 0; i < num; i++) { if (m_found_vars[i]) r++; } return r; } z3-z3-4.8.7/src/ast/used_vars.h000066400000000000000000000023511356505360400161520ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: used_vars.h Abstract: Functor used to collect the set of used variables. Author: Leonardo de Moura (leonardo) 2008-01-14. Revision History: --*/ #ifndef USED_VARS_H_ #define USED_VARS_H_ #include "ast/ast.h" #include "ast/expr_delta_pair.h" class used_vars { ptr_vector m_found_vars; typedef hashtable, default_eq > cache; cache m_cache; svector m_todo; void process(expr * n, unsigned delta); public: void operator()(expr * n) { m_found_vars.reset(); process(n, 0); } void reset() { m_found_vars.reset(); } void process(expr * n) { process(n, 0); } unsigned get_max_found_var_idx_plus_1() const { return m_found_vars.size(); } sort * get(unsigned var_idx) const { return m_found_vars[var_idx]; } sort * contains(unsigned var_idx) const { return var_idx < m_found_vars.size() ? m_found_vars[var_idx] : 0; } bool uses_all_vars(unsigned num_decls) const; bool uses_a_var(unsigned num_decls) const; unsigned get_num_vars() const; }; #endif /* USED_VARS_H_ */ z3-z3-4.8.7/src/ast/well_sorted.cpp000066400000000000000000000054171356505360400170430ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: well_sorted.cpp Abstract: Author: Leonardo de Moura (leonardo) 2007-12-29. Revision History: --*/ #include #include "ast/for_each_expr.h" #include "ast/well_sorted.h" #include "ast/ast_ll_pp.h" #include "ast/ast_pp.h" #include "util/warning.h" #include "ast/ast_smt2_pp.h" struct well_sorted_proc { ast_manager & m_manager; bool m_error; well_sorted_proc(ast_manager & m):m_manager(m), m_error(false) {} void operator()(var * v) {} void operator()(quantifier * n) { expr const * e = n->get_expr(); if (!is_lambda(n) && !m_manager.is_bool(e)) { warning_msg("quantifier's body must be a boolean."); m_error = true; UNREACHABLE(); } } void operator()(app * n) { unsigned num_args = n->get_num_args(); func_decl * decl = n->get_decl(); if (num_args != decl->get_arity() && !decl->is_associative() && !decl->is_right_associative() && !decl->is_left_associative()) { TRACE("ws", tout << "unexpected number of arguments.\n" << mk_ismt2_pp(n, m_manager);); warning_msg("unexpected number of arguments."); m_error = true; return; } for (unsigned i = 0; i < num_args; i++) { sort * actual_sort = m_manager.get_sort(n->get_arg(i)); sort * expected_sort = decl->is_associative() ? decl->get_domain(0) : decl->get_domain(i); if (expected_sort != actual_sort) { TRACE("tc", tout << "sort mismatch on argument #" << i << ".\n" << mk_ismt2_pp(n, m_manager); tout << "Sort mismatch for argument " << i+1 << " of " << mk_ismt2_pp(n, m_manager, false) << "\n"; tout << "Expected sort: " << mk_pp(expected_sort, m_manager) << "\n"; tout << "Actual sort: " << mk_pp(actual_sort, m_manager) << "\n"; tout << "Function sort: " << mk_pp(decl, m_manager) << "."; ); std::ostringstream strm; strm << "Sort mismatch for argument " << i+1 << " of " << mk_ll_pp(n, m_manager, false) << "\n"; strm << "Expected sort: " << mk_pp(expected_sort, m_manager) << "\n"; strm << "Actual sort: " << mk_pp(actual_sort, m_manager) << "\n"; strm << "Function sort: " << mk_pp(decl, m_manager) << "."; warning_msg("%s", strm.str().c_str()); m_error = true; return; } } } }; bool is_well_sorted(ast_manager const & m, expr * n) { well_sorted_proc p(const_cast(m)); for_each_expr(p, n); return !p.m_error; } z3-z3-4.8.7/src/ast/well_sorted.h000066400000000000000000000005351356505360400165040ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: well_sorted.h Abstract: Author: Leonardo de Moura (leonardo) 2007-12-29. Revision History: --*/ #ifndef WELL_SORTED_H_ #define WELL_SORTED_H_ class ast_manager; class expr; bool is_well_sorted(ast_manager const & m, expr * n); #endif /* WELL_SORTED_H_ */ z3-z3-4.8.7/src/cmd_context/000077500000000000000000000000001356505360400155255ustar00rootroot00000000000000z3-z3-4.8.7/src/cmd_context/CMakeLists.txt000066400000000000000000000006301356505360400202640ustar00rootroot00000000000000z3_add_component(cmd_context SOURCES basic_cmds.cpp check_logic.cpp cmd_context.cpp cmd_context_to_goal.cpp cmd_util.cpp context_params.cpp echo_tactic.cpp eval_cmd.cpp parametric_cmd.cpp pdecl.cpp simplify_cmd.cpp tactic_cmds.cpp tactic_manager.cpp COMPONENT_DEPENDENCIES rewriter solver EXTRA_REGISTER_MODULE_HEADERS context_params.h ) z3-z3-4.8.7/src/cmd_context/README000066400000000000000000000002761356505360400164120ustar00rootroot00000000000000Command context provides the infrastructure for executing commands in front-ends such as SMT-LIB 2.0. It is also provides the solver abstraction to plugin solvers in this kind of front-end. z3-z3-4.8.7/src/cmd_context/basic_cmds.cpp000066400000000000000000001055121356505360400203240ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: basic_cmds.cpp Abstract: Basic commands for SMT2 front end. Author: Leonardo (leonardo) 2011-03-30 Notes: --*/ #include "util/gparams.h" #include "util/env_params.h" #include "util/z3_version.h" #include "ast/ast_smt_pp.h" #include "ast/ast_smt2_pp.h" #include "ast/ast_pp_dot.h" #include "ast/ast_pp.h" #include "ast/array_decl_plugin.h" #include "ast/pp.h" #include "ast/well_sorted.h" #include "ast/pp_params.hpp" #include "model/model_smt2_pp.h" #include "cmd_context/cmd_context.h" #include "cmd_context/cmd_util.h" #include "cmd_context/simplify_cmd.h" #include "cmd_context/eval_cmd.h" class help_cmd : public cmd { svector m_cmds; void display_cmd(cmd_context & ctx, symbol const & s, cmd * c) { char const * usage = c->get_usage(); char const * descr = c->get_descr(ctx); ctx.regular_stream() << " (" << s; if (usage) ctx.regular_stream() << " " << escaped(usage, true) << ")\n"; else ctx.regular_stream() << ")\n"; if (descr) { ctx.regular_stream() << " " << escaped(descr, true, 4) << "\n"; } } public: help_cmd():cmd("help") {} char const * get_usage() const override { return "*"; } char const * get_descr(cmd_context & ctx) const override { return "print this help."; } unsigned get_arity() const override { return VAR_ARITY; } void prepare(cmd_context & ctx) override { m_cmds.reset(); } cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { return CPK_SYMBOL; } void set_next_arg(cmd_context & ctx, symbol const & s) override { cmd * c = ctx.find_cmd(s); if (c == nullptr) { std::string err_msg("unknown command '"); err_msg = err_msg + s.bare_str() + "'"; throw cmd_exception(std::move(err_msg)); } m_cmds.push_back(s); } typedef std::pair named_cmd; struct named_cmd_lt { bool operator()(named_cmd const & c1, named_cmd const & c2) const { return c1.first.str() < c2.first.str(); } }; void execute(cmd_context & ctx) override { ctx.regular_stream() << "\""; if (m_cmds.empty()) { vector cmds; cmd_context::cmd_iterator it = ctx.begin_cmds(); cmd_context::cmd_iterator end = ctx.end_cmds(); for (; it != end; ++it) { cmds.push_back(named_cmd((*it).m_key, (*it).m_value)); } // named_cmd_lt is not a total order for commands, but this is irrelevant for Linux x Windows behavior std::sort(cmds.begin(), cmds.end(), named_cmd_lt()); for (named_cmd const& nc : cmds) { display_cmd(ctx, nc.first, nc.second); } } else { for (symbol const& s : m_cmds) { cmd * c = ctx.find_cmd(s); SASSERT(c); display_cmd(ctx, s, c); } } ctx.regular_stream() << "\"\n"; } }; ATOMIC_CMD(exit_cmd, "exit", "exit.", ctx.print_success(); throw stop_parser_exception();); class get_model_cmd : public cmd { unsigned m_index; public: get_model_cmd(): cmd("get-model"), m_index(0) {} char const * get_usage() const override { return "[]"; } char const * get_descr(cmd_context & ctx) const override { return "retrieve model for the last check-sat command.\nSupply optional index if retrieving a model corresponding to a box optimization objective"; } unsigned get_arity() const override { return VAR_ARITY; } cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { return CPK_UINT; } void set_next_arg(cmd_context & ctx, unsigned index) override { m_index = index; } void execute(cmd_context & ctx) override { model_ref m; if (ctx.ignore_check()) return; if (!ctx.is_model_available(m) || !ctx.get_check_sat_result()) throw cmd_exception("model is not available"); if (m_index > 0 && ctx.get_opt()) { ctx.get_opt()->get_box_model(m, m_index); } ctx.display_model(m); } void reset(cmd_context& ctx) override { m_index = 0; } }; ATOMIC_CMD(get_assignment_cmd, "get-assignment", "retrieve assignment", { model_ref m; if (!ctx.is_model_available(m) || ctx.get_check_sat_result() == 0) throw cmd_exception("model is not available"); ctx.regular_stream() << "("; dictionary const & macros = ctx.get_macros(); bool first = true; for (auto const& kv : macros) { symbol const & name = kv.m_key; macro_decls const & _m = kv.m_value; for (auto md : _m) { 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)) { if (first) first = false; else ctx.regular_stream() << " "; ctx.regular_stream() << "("; if (is_smt2_quoted_symbol(name)) { ctx.regular_stream() << mk_smt2_quoted_symbol(name); } else { ctx.regular_stream() << name; } ctx.regular_stream() << " " << (ctx.m().is_true(val) ? "true" : "false") << ")"; } } } } ctx.regular_stream() << ")" << std::endl; }); class cmd_is_declared : public ast_smt_pp::is_declared { cmd_context& m_ctx; public: cmd_is_declared(cmd_context& ctx): m_ctx(ctx) {} bool operator()(func_decl* d) const override { return m_ctx.is_func_decl(d->get_name()); } bool operator()(sort* s) const override { return m_ctx.is_sort_decl(s->get_name()); } }; ATOMIC_CMD(get_proof_cmd, "get-proof", "retrieve proof", { if (!ctx.has_manager()) throw cmd_exception("proof is not available"); if (ctx.ignore_check()) return; expr_ref pr(ctx.m()); auto* chsr = ctx.get_check_sat_result(); if (!chsr) throw cmd_exception("proof is not available"); pr = chsr->get_proof(); if (!pr && !ctx.produce_proofs()) throw cmd_exception("proof construction is not enabled, use command (set-option :produce-proofs true)"); if (!pr) throw cmd_exception("proof is not available"); if (ctx.well_sorted_check_enabled() && !is_well_sorted(ctx.m(), pr)) { throw cmd_exception("proof is not well sorted"); } pp_params params; if (params.pretty_proof()) { ctx.regular_stream() << mk_pp(pr, ctx.m()) << std::endl; } else { // TODO: reimplement a new SMT2 pretty printer ast_smt_pp pp(ctx.m()); cmd_is_declared isd(ctx); pp.set_is_declared(&isd); pp.set_logic(ctx.get_logic()); pp.display_smt2(ctx.regular_stream(), pr); ctx.regular_stream() << std::endl; } }); ATOMIC_CMD(get_proof_graph_cmd, "get-proof-graph", "retrieve proof and print it in graphviz", { if (!ctx.produce_proofs()) throw cmd_exception("proof construction is not enabled, use command (set-option :produce-proofs true)"); if (!ctx.has_manager() || ctx.cs_state() != cmd_context::css_unsat) throw cmd_exception("proof is not available"); proof_ref pr(ctx.m()); if (ctx.ignore_check()) return; pr = ctx.get_check_sat_result()->get_proof(); if (pr == 0) throw cmd_exception("proof is not available"); if (ctx.well_sorted_check_enabled() && !is_well_sorted(ctx.m(), pr)) { throw cmd_exception("proof is not well sorted"); } context_params& params = ctx.params(); const std::string& file = params.m_dot_proof_file; std::ofstream out(file); out << ast_pp_dot(pr) << std::endl; }); static void print_core(cmd_context& ctx) { expr_ref_vector core(ctx.m()); ctx.get_check_sat_result()->get_unsat_core(core); ctx.regular_stream() << "("; bool first = true; for (expr* e : core) { if (first) first = false; else ctx.regular_stream() << " "; ctx.regular_stream() << mk_ismt2_pp(e, ctx.m()); } ctx.regular_stream() << ")" << std::endl; } ATOMIC_CMD(get_unsat_core_cmd, "get-unsat-core", "retrieve unsat core", { if (ctx.ignore_check()) return; if (!ctx.produce_unsat_cores()) throw cmd_exception("unsat core construction is not enabled, use command (set-option :produce-unsat-cores true)"); if (!ctx.has_manager() || ctx.cs_state() != cmd_context::css_unsat) throw cmd_exception("unsat core is not available"); print_core(ctx); }); ATOMIC_CMD(get_unsat_assumptions_cmd, "get-unsat-assumptions", "retrieve subset of assumptions sufficient for unsatisfiability", { if (ctx.ignore_check()) return; if (!ctx.produce_unsat_assumptions()) throw cmd_exception("unsat assumptions construction is not enabled, use command (set-option :produce-unsat-assumptions true)"); if (!ctx.has_manager() || ctx.cs_state() != cmd_context::css_unsat) { throw cmd_exception("unsat assumptions is not available"); } print_core(ctx); }); ATOMIC_CMD(labels_cmd, "labels", "retrieve Simplify-like labels", { if (!ctx.has_manager() || (ctx.cs_state() != cmd_context::css_sat && ctx.cs_state() != cmd_context::css_unknown)) throw cmd_exception("labels are not available"); svector labels; ctx.get_check_sat_result()->get_labels(labels); ctx.regular_stream() << "(labels"; for (unsigned i = 0; i < labels.size(); i++) { ctx.regular_stream() << " " << labels[i]; } ctx.regular_stream() << ")" << std::endl; }); ATOMIC_CMD(get_assertions_cmd, "get-assertions", "retrieve asserted terms when in interactive mode", ctx.display_assertions();); ATOMIC_CMD(reset_assertions_cmd, "reset-assertions", "reset all asserted formulas (but retain definitions and declarations)", ctx.reset_assertions(); ctx.print_success();); UNARY_CMD(set_logic_cmd, "set-logic", "", "set the background logic.", CPK_SYMBOL, symbol const &, if (ctx.set_logic(arg)) ctx.print_success(); else { std::string msg = "ignoring unsupported logic " + arg.str(); ctx.print_unsupported(symbol(msg.c_str()), m_line, m_pos); } ); UNARY_CMD(pp_cmd, "display", "", "display the given term.", CPK_EXPR, expr *, { ctx.display(ctx.regular_stream(), arg); ctx.regular_stream() << std::endl; }); UNARY_CMD(echo_cmd, "echo", "", "display the given string", CPK_STRING, char const *, bool smt2c = ctx.params().m_smtlib2_compliant; ctx.regular_stream() << (smt2c ? "\"" : "") << arg << (smt2c ? "\"" : "") << std::endl;); class set_get_option_cmd : public cmd { protected: symbol m_true; symbol m_false; symbol m_print_success; symbol m_print_warning; symbol m_expand_definitions; symbol m_interactive_mode; // deprecated by produce-assertions symbol m_produce_proofs; symbol m_produce_unsat_cores; symbol m_produce_unsat_assumptions; symbol m_produce_models; symbol m_produce_assignments; symbol m_produce_assertions; symbol m_regular_output_channel; symbol m_diagnostic_output_channel; symbol m_random_seed; symbol m_verbosity; symbol m_global_decls; symbol m_global_declarations; symbol m_numeral_as_real; symbol m_error_behavior; symbol m_int_real_coercions; symbol m_reproducible_resource_limit; bool is_builtin_option(symbol const & s) const { 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_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; } public: set_get_option_cmd(char const * name): cmd(name), m_true("true"), m_false("false"), m_print_success(":print-success"), m_print_warning(":print-warning"), m_expand_definitions(":expand-definitions"), m_interactive_mode(":interactive-mode"), m_produce_proofs(":produce-proofs"), m_produce_unsat_cores(":produce-unsat-cores"), m_produce_unsat_assumptions(":produce-unsat-assumptions"), m_produce_models(":produce-models"), m_produce_assignments(":produce-assignments"), m_produce_assertions(":produce-assertions"), m_regular_output_channel(":regular-output-channel"), m_diagnostic_output_channel(":diagnostic-output-channel"), m_random_seed(":random-seed"), m_verbosity(":verbosity"), m_global_decls(":global-decls"), m_global_declarations(":global-declarations"), m_numeral_as_real(":numeral-as-real"), m_error_behavior(":error-behavior"), m_int_real_coercions(":int-real-coercions"), m_reproducible_resource_limit(":reproducible-resource-limit") { } ~set_get_option_cmd() override {} }; class set_option_cmd : public set_get_option_cmd { bool m_unsupported; symbol m_option; bool to_bool(symbol const & value) const { if (value != m_true && value != m_false) throw cmd_exception("invalid option value, true/false expected"); return value == m_true; } static unsigned to_unsigned(rational const & val) { if (!val.is_unsigned()) throw cmd_exception("option value is too big to fit in a machine integer."); return val.get_unsigned(); } static void check_not_initialized(cmd_context & ctx, symbol const & opt_name) { if (ctx.has_manager()) { std::string msg = "error setting '"; msg += opt_name.str(); msg += "', option value cannot be modified after initialization"; throw cmd_exception(std::move(msg)); } } void set_param(cmd_context & ctx, char const * value) { try { gparams::set(m_option, value); env_params::updt_params(); ctx.global_params_updated(); } catch (const gparams::exception & ex) { throw cmd_exception(ex.msg()); } } void set_symbol(cmd_context & ctx, symbol const & value) { if (m_option == m_print_success) { ctx.set_print_success(to_bool(value)); } else if (m_option == m_print_warning) { enable_warning_messages(to_bool(value)); } else if (m_option == m_expand_definitions) { m_unsupported = true; } else if (m_option == m_interactive_mode || m_option == m_produce_assertions) { check_not_initialized(ctx, m_produce_assertions); ctx.set_interactive_mode(to_bool(value)); } else if (m_option == m_produce_proofs) { check_not_initialized(ctx, m_produce_proofs); ctx.set_produce_proofs(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)); } else if (m_option == m_produce_unsat_assumptions) { check_not_initialized(ctx, m_produce_unsat_assumptions); ctx.set_produce_unsat_assumptions(to_bool(value)); } else if (m_option == m_produce_models) { ctx.set_produce_models(to_bool(value)); } else if (m_option == m_produce_assignments) { ctx.set_produce_assignments(to_bool(value)); } else if (m_option == m_global_decls || m_option == m_global_declarations) { check_not_initialized(ctx, m_global_decls); ctx.set_global_decls(to_bool(value)); } else if (m_option == m_numeral_as_real) { ctx.set_numeral_as_real(to_bool(value)); } else if (m_option == m_int_real_coercions) { ctx.m().enable_int_real_coercions(to_bool(value)); } #ifdef Z3DEBUG else if (m_option == ":enable-assertions") { enable_assertions(to_bool(value)); } #endif else if (m_option == m_error_behavior) { if (value == "immediate-exit") { ctx.set_exit_on_error(true); } else if (value == "continued-execution") { ctx.set_exit_on_error(false); } else { throw cmd_exception("error setting :error-behavior, 'immediate-execution' or 'continued-execution' expected"); } } else if (is_builtin_option(m_option)) { throw cmd_exception("option value is not a symbol"); } else { set_param(ctx, value.bare_str()); } } public: set_option_cmd(): set_get_option_cmd("set-option"), m_unsupported(false) { } char const * get_usage() const override { return " "; } char const * get_descr(cmd_context & ctx) const override { return "set configuration option."; } unsigned get_arity() const override { return 2; } void prepare(cmd_context & ctx) override { m_unsupported = false; m_option = symbol::null; } cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { return m_option == symbol::null ? CPK_KEYWORD : CPK_OPTION_VALUE; } void set_next_arg(cmd_context & ctx, symbol const & opt) override { if (m_option == symbol::null) { m_option = opt; } else { set_symbol(ctx, opt); } } void set_next_arg(cmd_context & ctx, rational const & val) override { if (m_option == m_random_seed) { ctx.set_random_seed(to_unsigned(val)); } else if (m_option == m_reproducible_resource_limit) { ctx.params().set_rlimit(to_unsigned(val)); } else if (m_option == m_verbosity) { set_verbosity_level(to_unsigned(val)); } else if (is_builtin_option(m_option)) { throw cmd_exception("option value is not a numeral"); } else { std::string str = val.to_string(); set_param(ctx, str.c_str()); } } void set_next_arg(cmd_context & ctx, char const * value) override { if (m_option == m_regular_output_channel) { ctx.set_regular_stream(value); } else if (m_option == m_diagnostic_output_channel) { ctx.set_diagnostic_stream(value); } else if (is_builtin_option(m_option)) { throw cmd_exception("option value is not a string"); } else { set_param(ctx, value); } } void execute(cmd_context & ctx) override { if (m_unsupported) ctx.print_unsupported(m_option, m_line, m_pos); else ctx.print_success(); } }; class get_option_cmd : public set_get_option_cmd { static void print_bool(cmd_context & ctx, bool b) { ctx.regular_stream() << (b ? "true" : "false") << std::endl; } static void print_unsigned(cmd_context & ctx, unsigned v) { ctx.regular_stream() << v << std::endl; } static void print_string(cmd_context & ctx, char const * str) { ctx.regular_stream() << str << std::endl; } public: get_option_cmd(): set_get_option_cmd("get-option") { } char const * get_usage() const override { return ""; } char const * get_descr(cmd_context & ctx) const override { return "get configuration option."; } unsigned get_arity() const override { return 1; } cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { return CPK_KEYWORD; } void set_next_arg(cmd_context & ctx, symbol const & opt) override { if (opt == m_print_success) { print_bool(ctx, ctx.print_success_enabled()); } // TODO: // else if (opt == m_print_warning) { // print_bool(ctx, ); // } else if (opt == m_expand_definitions) { ctx.print_unsupported(m_expand_definitions, m_line, m_pos); } else if (opt == m_interactive_mode || opt == m_produce_assertions) { print_bool(ctx, ctx.interactive_mode()); } else if (opt == m_produce_proofs) { print_bool(ctx, ctx.produce_proofs()); } else if (opt == m_produce_unsat_cores) { print_bool(ctx, ctx.produce_unsat_cores()); } else if (opt == m_produce_models) { print_bool(ctx, ctx.produce_models()); } else if (opt == m_produce_assignments) { print_bool(ctx, ctx.produce_assignments()); } else if (opt == m_global_decls || opt == m_global_declarations) { print_bool(ctx, ctx.global_decls()); } else if (opt == m_random_seed) { print_unsigned(ctx, ctx.random_seed()); } else if (opt == m_verbosity) { print_unsigned(ctx, get_verbosity_level()); } else if (opt == m_regular_output_channel) { print_string(ctx, ctx.get_regular_stream_name()); } else if (opt == m_diagnostic_output_channel) { print_string(ctx, ctx.get_diagnostic_stream_name()); } else if (opt == m_error_behavior) { if (ctx.exit_on_error()) { print_string(ctx, "immediate-exit"); } else { print_string(ctx, "continued-execution"); } } else if (opt == m_int_real_coercions) { print_bool(ctx, ctx.m().int_real_coercions()); } else { try { ctx.regular_stream() << gparams::get_value(opt) << std::endl; } catch (const gparams::exception &) { ctx.print_unsupported(opt, m_line, m_pos); } } } }; #define STRINGIZE(x) #x #define STRINGIZE_VALUE_OF(x) STRINGIZE(x) class get_info_cmd : public cmd { symbol m_error_behavior; symbol m_name; symbol m_authors; symbol m_version; symbol m_status; symbol m_reason_unknown; symbol m_all_statistics; symbol m_assertion_stack_levels; symbol m_rlimit; public: get_info_cmd(): cmd("get-info"), m_error_behavior(":error-behavior"), m_name(":name"), m_authors(":authors"), m_version(":version"), m_status(":status"), m_reason_unknown(":reason-unknown"), m_all_statistics(":all-statistics"), m_assertion_stack_levels(":assertion-stack-levels"), m_rlimit(":rlimit") { } char const * get_usage() const override { return ""; } char const * get_descr(cmd_context & ctx) const override { return "get information."; } unsigned get_arity() const override { return 1; } cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { return CPK_KEYWORD; } void set_next_arg(cmd_context & ctx, symbol const & opt) override { if (opt == m_error_behavior) { if (ctx.exit_on_error()) ctx.regular_stream() << "(:error-behavior immediate-exit)" << std::endl; else ctx.regular_stream() << "(:error-behavior continued-execution)" << std::endl; } else if (opt == m_name) { ctx.regular_stream() << "(:name \"Z3\")" << std::endl; } else if (opt == m_authors) { ctx.regular_stream() << "(:authors \"Leonardo de Moura, Nikolaj Bjorner and Christoph Wintersteiger\")" << std::endl; } else if (opt == m_version) { ctx.regular_stream() << "(:version \"" << Z3_MAJOR_VERSION << "." << Z3_MINOR_VERSION << "." << Z3_BUILD_NUMBER #ifdef Z3GITHASH << " - build hashcode " << STRINGIZE_VALUE_OF(Z3GITHASH) #endif << "\")" << std::endl; } else if (opt == m_status) { ctx.regular_stream() << "(:status " << ctx.get_status() << ")" << std::endl; } else if (opt == m_reason_unknown) { ctx.regular_stream() << "(:reason-unknown \"" << escaped(ctx.reason_unknown().c_str()) << "\")" << std::endl; } else if (opt == m_rlimit) { ctx.regular_stream() << "(:rlimit " << ctx.m().limit().count() << ")" << std::endl; } else if (opt == m_all_statistics) { ctx.display_statistics(); } else if (opt == m_assertion_stack_levels) { ctx.regular_stream() << "(:assertion-stack-levels " << ctx.num_scopes() << ")" << std::endl; } else { ctx.print_unsupported(opt, m_line, m_pos); } } }; class set_info_cmd : public cmd { symbol m_info; symbol m_status; symbol m_unsat; symbol m_sat; symbol m_unknown; public: set_info_cmd(): cmd("set-info"), m_status(":status"), m_unsat("unsat"), m_sat("sat"), m_unknown("unknown") { } char const * get_usage() const override { return " "; } char const * get_descr(cmd_context & ctx) const override { return "set information."; } unsigned get_arity() const override { return 2; } void prepare(cmd_context & ctx) override { m_info = symbol::null; } cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { return m_info == symbol::null ? CPK_KEYWORD : CPK_OPTION_VALUE; } void set_next_arg(cmd_context & ctx, rational const & val) override {} void set_next_arg(cmd_context & ctx, char const * val) override {} void set_next_arg(cmd_context & ctx, symbol const & s) override { if (m_info == symbol::null) { m_info = s; } else { if (m_info == m_status) { if (s == m_unsat) { ctx.set_status(cmd_context::UNSAT); } else if (s == m_sat) { ctx.set_status(cmd_context::SAT); } else if (s == m_unknown) { ctx.set_status(cmd_context::UNKNOWN); } else { throw cmd_exception("invalid ':status' attribute"); } } } } void execute(cmd_context & ctx) override { ctx.print_success(); } }; class declare_map_cmd : public cmd { symbol m_array_sort; symbol m_name; ptr_vector m_domain; func_decl * m_f; family_id m_array_fid; public: declare_map_cmd(): cmd("declare-map"), m_array_sort("Array"), m_array_fid(null_family_id) {} family_id get_array_fid(cmd_context & ctx) { if (m_array_fid == null_family_id) { m_array_fid = ctx.m().mk_family_id("array"); } return m_array_fid; } char const * get_usage() const override { return " (+) "; } char const * get_descr(cmd_context & ctx) const override { return "declare a new array map operator with name using the given function declaration.\n ::= \n | ( (*) )\n | ((_ +) (*) )\nThe last two cases are used to disumbiguate between declarations with the same name and/or select (indexed) builtin declarations.\nFor more details about the array map operator, see 'Generalized and Efficient Array Decision Procedures' (FMCAD 2009).\nExample: (declare-map set-union (Int) (or (Bool Bool) Bool))\nDeclares a new function (declare-fun set-union ((Array Int Bool) (Array Int Bool)) (Array Int Bool)).\nThe instance of the map axiom for this new declaration is:\n(forall ((a1 (Array Int Bool)) (a2 (Array Int Bool)) (i Int)) (= (select (set-union a1 a2) i) (or (select a1 i) (select a2 i))))"; } unsigned get_arity() const override { return 3; } void prepare(cmd_context & ctx) override { m_name = symbol::null; m_domain.reset(); } cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { if (m_name == symbol::null) return CPK_SYMBOL; if (m_domain.empty()) return CPK_SORT_LIST; return CPK_FUNC_DECL; } void set_next_arg(cmd_context & ctx, symbol const & s) override { m_name = s; } void set_next_arg(cmd_context & ctx, unsigned num, sort * const * slist) override { if (num == 0) throw cmd_exception("invalid map declaration, empty sort list"); m_domain.append(num, slist); } void set_next_arg(cmd_context & ctx, func_decl * f) override { m_f = f; if (m_f->get_arity() == 0) throw cmd_exception("invalid map declaration, function declaration must have arity > 0"); } void reset(cmd_context & ctx) override { m_array_fid = null_family_id; } void execute(cmd_context & ctx) override { psort_decl * array_sort = ctx.find_psort_decl(m_array_sort); if (array_sort == nullptr) throw cmd_exception("Array sort is not available"); ptr_vector & array_sort_args = m_domain; sort_ref_buffer domain(ctx.m()); unsigned arity = m_f->get_arity(); for (unsigned i = 0; i < arity; i++) { array_sort_args.push_back(m_f->get_domain(i)); domain.push_back(array_sort->instantiate(ctx.pm(), array_sort_args.size(), array_sort_args.c_ptr())); array_sort_args.pop_back(); } sort_ref range(ctx.m()); array_sort_args.push_back(m_f->get_range()); range = array_sort->instantiate(ctx.pm(), array_sort_args.size(), array_sort_args.c_ptr()); parameter p[1] = { parameter(m_f) }; func_decl_ref new_map(ctx.m()); new_map = ctx.m().mk_func_decl(get_array_fid(ctx), OP_ARRAY_MAP, 1, p, domain.size(), domain.c_ptr(), range.get()); if (new_map == 0) throw cmd_exception("invalid array map operator"); ctx.insert(m_name, new_map); } }; class get_consequences_cmd : public cmd { ptr_vector m_assumptions; ptr_vector m_variables; unsigned m_count; public: get_consequences_cmd(): cmd("get-consequences"), m_count(0) {} char const * get_usage() const override { return "(*) (*)"; } char const * get_descr(cmd_context & ctx) const override { return "retrieve consequences that fix values for supplied variables"; } unsigned get_arity() const override { return 2; } cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { return CPK_EXPR_LIST; } void set_next_arg(cmd_context & ctx, unsigned num, expr * const * tlist) override { if (m_count == 0) { m_assumptions.append(num, tlist); ++m_count; } else { m_variables.append(num, tlist); } } void failure_cleanup(cmd_context & ctx) override {} void execute(cmd_context & ctx) override { ast_manager& m = ctx.m(); expr_ref_vector assumptions(m), variables(m), consequences(m); assumptions.append(m_assumptions.size(), m_assumptions.c_ptr()); variables.append(m_variables.size(), m_variables.c_ptr()); ctx.get_consequences(assumptions, variables, consequences); ctx.regular_stream() << consequences << "\n"; } void prepare(cmd_context & ctx) override { reset(ctx); } void reset(cmd_context& ctx) override { m_assumptions.reset(); m_variables.reset(); m_count = 0; } void finalize(cmd_context & ctx) override {} }; // provides "help" for builtin cmds class builtin_cmd : public cmd { char const * m_usage; char const * m_descr; public: builtin_cmd(char const * name, char const * usage, char const * descr): cmd(name), m_usage(usage), m_descr(descr) {} char const * get_usage() const override { return m_usage; } char const * get_descr(cmd_context & ctx) const override { return m_descr; } }; void install_basic_cmds(cmd_context & ctx) { ctx.insert(alloc(set_logic_cmd)); ctx.insert(alloc(exit_cmd)); ctx.insert(alloc(get_assignment_cmd)); ctx.insert(alloc(get_assertions_cmd)); ctx.insert(alloc(get_proof_cmd)); ctx.insert(alloc(get_proof_graph_cmd)); ctx.insert(alloc(get_unsat_core_cmd)); ctx.insert(alloc(set_option_cmd)); ctx.insert(alloc(get_option_cmd)); ctx.insert(alloc(get_info_cmd)); ctx.insert(alloc(set_info_cmd)); ctx.insert(alloc(get_consequences_cmd)); ctx.insert(alloc(builtin_cmd, "assert", "", "assert term.")); ctx.insert(alloc(builtin_cmd, "check-sat", "*", "check if the current context is satisfiable. If a list of boolean constants B is provided, then check if the current context is consistent with assigning every constant in B to true.")); ctx.insert(alloc(builtin_cmd, "push", "?", "push 1 (or ) scopes.")); ctx.insert(alloc(builtin_cmd, "pop", "?", "pop 1 (or ) scopes.")); ctx.insert(alloc(builtin_cmd, "get-value", "(+)", "evaluate the given terms in the current model.")); ctx.insert(alloc(builtin_cmd, "declare-sort", " ?", "declare a new uninterpreted sort of arity , if arity is not provided, then it is assumed to be 0.")); ctx.insert(alloc(builtin_cmd, "define-sort", " (*) ", "define a new sort.")); ctx.insert(alloc(builtin_cmd, "declare-fun", " (*) ", "declare a new function/constant.")); ctx.insert(alloc(builtin_cmd, "declare-const", " ", "declare a new constant.")); ctx.insert(alloc(builtin_cmd, "declare-datatypes", "(*) (+)", "declare mutually recursive datatypes.\n ::= ( +)\n ::= ( *)\n ::= ( )\nexample: (declare-datatypes (T) ((BinTree (leaf (value T)) (node (left BinTree) (right BinTree)))))")); ctx.insert(alloc(builtin_cmd, "check-sat-assuming", "( hprop_literali* )", "check sat assuming a collection of literals")); ctx.insert(alloc(get_unsat_assumptions_cmd)); ctx.insert(alloc(reset_assertions_cmd)); } void install_ext_basic_cmds(cmd_context & ctx) { ctx.insert(alloc(help_cmd)); ctx.insert(alloc(pp_cmd)); ctx.insert(alloc(get_model_cmd)); ctx.insert(alloc(echo_cmd)); ctx.insert(alloc(labels_cmd)); ctx.insert(alloc(declare_map_cmd)); ctx.insert(alloc(builtin_cmd, "reset", nullptr, "reset the shell (all declarations and assertions will be erased)")); install_simplify_cmd(ctx); install_eval_cmd(ctx); } z3-z3-4.8.7/src/cmd_context/basic_cmds.h000066400000000000000000000005401356505360400177640ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: basic_cmds.h Abstract: Basic commands for SMT2 front end. Author: Leonardo (leonardo) 2011-03-30 Notes: --*/ #ifndef BASIC_CMDS_H_ #define BASIC_CMDS_H_ class cmd_context; void install_basic_cmds(cmd_context & ctx); void install_ext_basic_cmds(cmd_context & ctx); #endif z3-z3-4.8.7/src/cmd_context/check_logic.cpp000066400000000000000000000373741356505360400205010ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: check_logic.cpp Abstract: Check whether a given assertion is in the correct logic or not Author: Leonardo de Moura (leonardo) 2011-08-11. Revision History: --*/ #include "cmd_context/check_logic.h" #include "solver/smt_logics.h" #include "ast/arith_decl_plugin.h" #include "ast/array_decl_plugin.h" #include "ast/bv_decl_plugin.h" #include "ast/seq_decl_plugin.h" #include "ast/pb_decl_plugin.h" #include "ast/datatype_decl_plugin.h" #include "ast/ast_pp.h" #include "ast/for_each_expr.h" #include struct check_logic::imp { ast_manager & m; symbol m_logic; arith_util m_a_util; bv_util m_bv_util; array_util m_ar_util; seq_util m_seq_util; datatype_util m_dt_util; pb_util m_pb_util; bool m_uf; // true if the logic supports uninterpreted functions bool m_dt; // true if the lgoic supports dattypes bool m_arrays; // true if the logic supports arbitrary arrays bool m_bv_arrays; // true if the logic supports only bv arrays bool m_reals; // true if the logic supports reals bool m_ints; // true if the logic supports integers bool m_diff; // true if the logic supports difference logic only bool m_nonlinear; // true if the logic supports nonlinear arithmetic bool m_bvs; // true if the logic supports bit-vectors bool m_quantifiers; // true if the logic supports quantifiers bool m_unknown_logic; imp(ast_manager & _m):m(_m), m_a_util(m), m_bv_util(m), m_ar_util(m), m_seq_util(m), m_dt_util(m), m_pb_util(m) { reset(); } void reset() { m_uf = false; m_dt = false; m_arrays = false; m_bv_arrays = false; m_reals = false; m_ints = false; m_diff = false; m_nonlinear = false; m_bvs = false; m_quantifiers = false; m_unknown_logic = true; } void set_logic(symbol const & logic) { reset(); m_unknown_logic = false; if (logic == "AUFLIA") { m_uf = true; m_arrays = true; m_ints = true; m_quantifiers = true; } else if (logic == "AUFLIRA") { m_uf = true; m_arrays = true; m_reals = true; m_ints = true; m_quantifiers = true; } else if (logic == "AUFNIRA") { m_uf = true; m_arrays = true; m_reals = true; m_ints = true; m_nonlinear = true; m_quantifiers = true; } else if (logic == "LRA") { m_reals = true; m_quantifiers = true; } else if (logic == "QF_ABV") { m_bv_arrays = true; m_bvs = true; } else if (logic == "QF_AUFBV") { m_uf = true; m_bv_arrays = true; m_bvs = true; } else if (logic == "QF_UFBV") { m_uf = true; m_bvs = true; } else if (logic == "QF_DT") { m_uf = true; m_dt = true; } else if (logic == "QF_AUFLIA") { m_uf = true; m_arrays = true; m_ints = true; } else if (logic == "QF_AX") { m_arrays = true; } else if (logic == "QF_BV") { m_bvs = true; } else if (logic == "QF_IDL") { m_ints = true; m_diff = true; } else if (logic == "QF_RDL") { m_reals = true; m_diff = true; } else if (logic == "QF_LIA") { m_ints = true; } else if (logic == "QF_LRA") { m_reals = true; } else if (logic == "QF_NIA") { m_ints = true; m_nonlinear = true; } else if (logic == "QF_NRA") { m_reals = true; m_nonlinear = true; } else if (logic == "QF_UF") { m_uf = true; } else if (logic == "QF_UFIDL") { m_uf = true; m_ints = true; m_diff = true; } else if (logic == "QF_UFLIA") { m_uf = true; m_ints = true; } else if (logic == "QF_UFLRA") { m_uf = true; m_reals = true; } else if (logic == "QF_UFNRA") { m_uf = true; m_reals = true; m_nonlinear = true; } else if (logic == "UFLRA") { m_uf = true; m_reals = true; m_quantifiers = true; } else if (logic == "UFNIA") { m_uf = true; m_ints = true; m_quantifiers = true; m_nonlinear = true; } else if (logic == "UFBV") { m_uf = true; m_bvs = true; m_quantifiers = true; } else if (logic == "QF_S") { m_uf = true; m_bvs = true; m_ints = true; m_arrays = true; m_reals = true; m_quantifiers = false; } else if (logic == "QF_FD") { m_bvs = true; m_uf = true; m_ints = true; m_dt = true; m_nonlinear = true; // non-linear 0-1 variables may get eliminated } else { m_unknown_logic = true; } m_logic = logic; } struct failed {}; std::string m_last_error; void fail(char const * msg) { m_last_error = msg; throw failed(); } void check_sort(sort * s) { if (s->get_family_id() == null_family_id) { if (!m_uf) fail("logic does not support uninterpreted sorts"); } else if (m.is_bool(s)) { return; } else if (m_a_util.is_int(s)) { if (!m_ints) fail("logic does not support integers"); } else if (m_a_util.is_real(s)) { if (!m_reals) fail("logic does not support reals"); } else if (m_bv_util.is_bv_sort(s)) { if (!m_bvs) fail("logic does not support bitvectors"); } else if (m_ar_util.is_array(s)) { if (m_arrays) { return; } else if (m_bv_arrays) { 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"); } } } void operator()(var * n) { if (!m_quantifiers) fail("logic does not support quantifiers"); check_sort(m.get_sort(n)); } bool is_int(expr * t) { if (m_a_util.is_uminus(t)) t = to_app(t)->get_arg(0); // Take care of coercions automatically added by Z3 if (m_a_util.is_to_real(t)) t = to_app(t)->get_arg(0); return m_a_util.is_numeral(t); } bool is_numeral(expr * t) { if (m_a_util.is_uminus(t)) t = to_app(t)->get_arg(0); // c if (is_int(t)) return true; // c1/c2 if (m_a_util.is_div(t) && is_int(to_app(t)->get_arg(0)) && is_int(to_app(t)->get_arg(1))) return true; return false; } // check if n has at most one argument that is not numeral. void check_mul(app * n) { if (m_nonlinear) return; // nothing to check unsigned num_args = n->get_num_args(); bool found_non_numeral = false; for (unsigned i = 0; i < num_args; i++) { if (!is_numeral(n->get_arg(i))) { if (found_non_numeral) fail("logic does not support nonlinear arithmetic"); else found_non_numeral = true; } } } // check if the divisor is a numeral void check_div(app * n) { SASSERT(n->get_num_args() == 2); if (!m_nonlinear && !is_numeral(n->get_arg(1))) fail("logic does not support nonlinear arithmetic"); } bool is_diff_var(expr * t) const { if (is_app(t) && to_app(t)->get_decl()->get_family_id() == null_family_id) return true; if (m.is_ite(t)) return true; return false; } void fail_non_diff(expr * t) { TRACE("check_logic", tout << mk_pp(t, m) << "\n";); fail("logic only supports difference arithmetic"); } bool same_args(app * t) { unsigned num_args = t->get_num_args(); if (num_args == 0) return false; expr * arg = t->get_arg(0); for (unsigned i = 1; i < num_args; i++) { if (t->get_arg(i) != arg) return false; } return true; } bool is_arith(expr * t) const { return m.get_sort(t)->get_family_id() == m_a_util.get_family_id(); } bool is_offset(app * t) { while (true) { expr * non_numeral = nullptr; unsigned num_args = t->get_num_args(); for (unsigned i = 0; i < num_args; i++) { expr * arg = t->get_arg(i); if (is_numeral(arg)) continue; if (non_numeral != nullptr) return false; non_numeral = arg; } if (non_numeral == nullptr) return true; if (is_diff_var(non_numeral)) return true; if (!m_a_util.is_add(non_numeral) && !m_a_util.is_sub(non_numeral)) return false; t = to_app(non_numeral); } return true; } bool is_diff_arg(expr * t) { if (is_diff_var(t)) return true; if (is_numeral(t)) return true; if (m_a_util.is_add(t) || m_a_util.is_sub(t)) return is_offset(to_app(t)); return false; } // Check if n is a diff logic predicate void check_diff_predicate(app * n) { expr * lhs = n->get_arg(0); expr * rhs = n->get_arg(1); if (!is_arith(lhs)) return; // formula is not in arithmetic if (is_diff_arg(lhs) && is_diff_arg(rhs)) return; if (is_numeral(lhs)) std::swap(lhs, rhs); if (!is_numeral(rhs)) fail_non_diff(n); if (!m_a_util.is_sub(lhs) || to_app(lhs)->get_num_args() != 2) fail_non_diff(n); expr * t1 = to_app(lhs)->get_arg(0); expr * t2 = to_app(lhs)->get_arg(1); if (is_diff_var(t1) && is_diff_var(t2)) return; if (m_a_util.is_add(t1) && m_a_util.is_add(t2)) { // QF_RDL supports (<= (- (+ x ... x) (+ y ... y)) c) if (to_app(t1)->get_num_args() != to_app(t2)->get_num_args()) fail_non_diff(n); if (!same_args(to_app(t1)) || !same_args(to_app(t2))) fail_non_diff(n); return; } fail_non_diff(n); } void check_diff_arg(expr * t) { if (!is_diff_arg(t)) fail_non_diff(t); } // Check if the arith args of n are of the form (t + k) where k is a numeral. void check_diff_args(app * n) { unsigned num_args = n->get_num_args(); for (unsigned i = 0; i < num_args; i++) { if (is_arith(n)) check_diff_arg(n); } } void operator()(app * n) { sort * s = m.get_sort(n); check_sort(s); func_decl * f = n->get_decl(); family_id fid = f->get_family_id(); if (fid == null_family_id) { if (!m_uf && f->get_arity() > 0) fail("logic does not support uninterpreted functions"); if (m_diff) check_diff_args(n); } else if (fid == m_a_util.get_family_id()) { if (m_a_util.is_mul(n)) check_mul(n); else if (m_a_util.is_div(n) || m_a_util.is_idiv(n) || m_a_util.is_rem(n) || m_a_util.is_mod(n)) check_div(n); if (m_diff) { if (m_a_util.is_le(n) || m_a_util.is_lt(n) || m_a_util.is_ge(n) || m_a_util.is_gt(n)) check_diff_predicate(n); } if (!m_ints || !m_reals) { if (m_a_util.is_to_real(n) || m_a_util.is_to_int(n)) fail("logic does not support casting operators"); } } else if (fid == m_bv_util.get_family_id()) { // nothing to check... } else if (fid == m_ar_util.get_family_id()) { // nothing to check... if (m_diff) check_diff_args(n); } else if (fid == m.get_basic_family_id()) { // nothing to check... if (m_diff) { if (m.is_eq(n)) check_diff_predicate(n); else if (m.is_distinct(n) || m.is_ite(n)) check_diff_args(n); } } else if (m.is_builtin_family_id(fid)) { // nothing to check } else if (fid == m_seq_util.get_family_id()) { // nothing to check } else if (fid == m_dt_util.get_family_id() && m_dt) { // nothing to check } else if (fid == m_pb_util.get_family_id() && smt_logics::logic_has_pb(m_logic)) { // nothing to check } else { std::stringstream strm; strm << "logic does not support theory " << m.get_family_name(fid); fail(strm.str().c_str()); } } void operator()(quantifier * n) { if (!m_quantifiers) fail("logic does not support quantifiers"); } bool operator()(expr * n) { if (m_unknown_logic) return true; try { quick_for_each_expr(*this, n); return true; } catch (const failed &) { return false; } } bool operator()(func_decl * f) { if (m_unknown_logic) return true; try { unsigned arity = f->get_arity(); if (arity > 0) { if (!m_uf) fail("logic does not support uninterpreted functions"); for (unsigned i = 0; i < arity; i++) check_sort(f->get_domain(i)); } check_sort(f->get_range()); return true; } catch (const failed &) { return false; } } }; check_logic::check_logic() { m_imp = nullptr; } check_logic::~check_logic() { if (m_imp) dealloc(m_imp); } void check_logic::reset() { if (m_imp) dealloc(m_imp); m_imp = nullptr; } void check_logic::set_logic(ast_manager & m, symbol const & logic) { reset(); m_imp = alloc(imp, m); m_imp->set_logic(logic); } bool check_logic::operator()(expr * n) { if (m_imp) return m_imp->operator()(n); return true; } bool check_logic::operator()(func_decl * f) { if (m_imp) return m_imp->operator()(f); return true; } char const * check_logic::get_last_error() const { if (m_imp) return m_imp->m_last_error.c_str(); return ""; } z3-z3-4.8.7/src/cmd_context/check_logic.h000066400000000000000000000011331356505360400201260ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: check_logic.h Abstract: Check whether a given assertion is in the correct logic or not Author: Leonardo de Moura (leonardo) 2011-08-11. Revision History: --*/ #ifndef CHECK_LOGIC_H_ #define CHECK_LOGIC_H_ #include "ast/ast.h" class check_logic { struct imp; imp * m_imp; public: check_logic(); ~check_logic(); void reset(); void set_logic(ast_manager & m, symbol const & logic); bool operator()(expr * n); bool operator()(func_decl * f); char const * get_last_error() const; }; #endif z3-z3-4.8.7/src/cmd_context/cmd_context.cpp000066400000000000000000002111311356505360400205370ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: cmd_context.cpp Abstract: Command context. Author: Leonardo (leonardo) 2011-03-01 Notes: --*/ #include #include "util/tptr.h" #include "util/cancel_eh.h" #include "util/scoped_ctrl_c.h" #include "util/dec_ref_util.h" #include "util/scoped_timer.h" #include "ast/func_decl_dependencies.h" #include "ast/arith_decl_plugin.h" #include "ast/bv_decl_plugin.h" #include "ast/array_decl_plugin.h" #include "ast/datatype_decl_plugin.h" #include "ast/seq_decl_plugin.h" #include "ast/pb_decl_plugin.h" #include "ast/fpa_decl_plugin.h" #include "ast/csp_decl_plugin.h" #include "ast/special_relations_decl_plugin.h" #include "ast/ast_pp.h" #include "ast/rewriter/var_subst.h" #include "ast/pp.h" #include "ast/ast_smt2_pp.h" #include "ast/ast_ll_pp.h" #include "ast/decl_collector.h" #include "ast/well_sorted.h" #include "ast/for_each_expr.h" #include "ast/rewriter/th_rewriter.h" #include "ast/rewriter/recfun_replace.h" #include "model/model_evaluator.h" #include "model/model_smt2_pp.h" #include "model/model_v2_pp.h" #include "model/model_params.hpp" #include "tactic/tactic_exception.h" #include "tactic/generic_model_converter.h" #include "solver/smt_logics.h" #include "cmd_context/basic_cmds.h" #include "cmd_context/cmd_context.h" func_decls::func_decls(ast_manager & m, func_decl * f): m_decls(TAG(func_decl*, f, 0)) { m.inc_ref(f); } void func_decls::finalize(ast_manager & m) { TRACE("cmd_context_detail", tout << "finalizing func_decls...\n";); if (GET_TAG(m_decls) == 0) { m.dec_ref(UNTAG(func_decl *, m_decls)); } else { TRACE("func_decls", tout << "finalize...\n";); func_decl_set * fs = UNTAG(func_decl_set *, m_decls); for (func_decl * f : *fs) { TRACE("func_decls", tout << "dec_ref of " << f->get_name() << " ref_count: " << f->get_ref_count() << "\n";); m.dec_ref(f); } dealloc(fs); } m_decls = nullptr; } bool func_decls::signatures_collide(func_decl* f, func_decl* g) const { return f == g; } bool func_decls::signatures_collide(unsigned n, sort* const* domain, sort* range, func_decl* g) const { if (g->get_range() != range) return false; if (n != g->get_arity()) return false; for (unsigned i = 0; i < n; ++i) { if (domain[i] != g->get_domain(i)) return false; } return true; } bool func_decls::contains(func_decl * f) const { if (GET_TAG(m_decls) == 0) { func_decl* g = UNTAG(func_decl*, m_decls); return g && signatures_collide(f, g); } else { func_decl_set * fs = UNTAG(func_decl_set *, m_decls); for (func_decl* g : *fs) { if (signatures_collide(f, g)) return true; } } return false; } bool func_decls::contains(unsigned n, sort* const* domain, sort* range) const { if (GET_TAG(m_decls) == 0) { func_decl* g = UNTAG(func_decl*, m_decls); return g && signatures_collide(n, domain, range, g); } else { func_decl_set * fs = UNTAG(func_decl_set *, m_decls); for (func_decl* g : *fs) { if (signatures_collide(n, domain, range, g)) return true; } } return false; } bool func_decls::insert(ast_manager & m, func_decl * f) { if (contains(f)) return false; m.inc_ref(f); if (m_decls == nullptr) { m_decls = TAG(func_decl*, f, 0); } else if (GET_TAG(m_decls) == 0) { func_decl_set * new_fs = alloc(func_decl_set); new_fs->insert(UNTAG(func_decl*, m_decls)); new_fs->insert(f); m_decls = TAG(func_decl*, new_fs, 1); } else { func_decl_set * fs = UNTAG(func_decl_set*, m_decls); fs->insert(f); } return true; } void func_decls::erase(ast_manager & m, func_decl * f) { if (!contains(f)) return; if (GET_TAG(m_decls) == 0) { m.dec_ref(f); m_decls = nullptr; } else { func_decl_set * fs = UNTAG(func_decl_set *, m_decls); fs->erase(f); m.dec_ref(f); if (fs->empty()) { dealloc(fs); m_decls = nullptr; } } } /** \brief Return true if func_decls contains a declaration different from f, but with the same domain. */ bool func_decls::clash(func_decl * f) const { if (m_decls == nullptr) return false; if (GET_TAG(m_decls) == 0) return false; func_decl_set * fs = UNTAG(func_decl_set *, m_decls); for (func_decl * g : *fs) { if (g == f) continue; if (g->get_arity() != f->get_arity()) continue; unsigned num = g->get_arity(); unsigned i; for (i = 0; i < num; i++) if (g->get_domain(i) != f->get_domain(i)) break; if (i == num) return true; } return false; } bool func_decls::more_than_one() const { if (m_decls == nullptr || GET_TAG(m_decls) == 0) return false; func_decl_set * fs = UNTAG(func_decl_set *, m_decls); return fs->size() > 1; } func_decl * func_decls::first() const { if (m_decls == nullptr) return nullptr; if (GET_TAG(m_decls) == 0) return UNTAG(func_decl*, m_decls); func_decl_set * fs = UNTAG(func_decl_set *, m_decls); SASSERT(!fs->empty()); return *(fs->begin()); } func_decl * func_decls::find(unsigned arity, sort * const * domain, sort * range) const { if (!more_than_one()) return first(); func_decl_set * fs = UNTAG(func_decl_set *, m_decls); for (func_decl * f : *fs) { if (range != nullptr && f->get_range() != range) continue; if (f->get_arity() != arity) continue; unsigned i = 0; for (i = 0; domain && i < arity; i++) { if (f->get_domain(i) != domain[i]) break; } if (i == arity) return f; } return nullptr; } func_decl * func_decls::find(ast_manager & m, unsigned num_args, expr * const * args, sort * range) const { if (!more_than_one()) first(); ptr_buffer sorts; for (unsigned i = 0; i < num_args; i++) sorts.push_back(m.get_sort(args[i])); return find(num_args, sorts.c_ptr(), range); } unsigned func_decls::get_num_entries() const { if (!more_than_one()) return 1; func_decl_set * fs = UNTAG(func_decl_set *, m_decls); return fs->size(); } func_decl * func_decls::get_entry(unsigned inx) { if (!more_than_one()) { SASSERT(inx == 0); return first(); } else { func_decl_set * fs = UNTAG(func_decl_set *, m_decls); auto b = fs->begin(); for (unsigned i = 0; i < inx; i++) b++; return *b; } } void macro_decls::finalize(ast_manager& m) { for (auto v : *m_decls) m.dec_ref(v.m_body); dealloc(m_decls); } bool macro_decls::insert(ast_manager& m, unsigned arity, sort *const* domain, expr* body) { if (find(arity, domain)) return false; m.inc_ref(body); if (!m_decls) m_decls = alloc(vector); m_decls->push_back(macro_decl(arity, domain, body)); return true; } expr* macro_decls::find(unsigned arity, sort *const* domain) const { if (!m_decls) return nullptr; for (auto v : *m_decls) { if (v.m_domain.size() != arity) continue; bool eq = true; for (unsigned i = 0; eq && i < arity; ++i) { eq = domain[i] == v.m_domain[i]; } if (eq) return v.m_body; } return nullptr; } void macro_decls::erase_last(ast_manager& m) { SASSERT(m_decls); SASSERT(!m_decls->empty()); m.dec_ref(m_decls->back().m_body); m_decls->pop_back(); } bool cmd_context::contains_func_decl(symbol const& s, unsigned n, sort* const* domain, sort* range) const { func_decls fs; return m_func_decls.find(s, fs) && fs.contains(n, domain, range); } bool cmd_context::contains_macro(symbol const& s) const { return m_macros.contains(s); } bool cmd_context::contains_macro(symbol const& s, func_decl* f) const { return contains_macro(s, f->get_arity(), f->get_domain()); } bool cmd_context::contains_macro(symbol const& s, unsigned arity, sort *const* domain) const { macro_decls decls; return m_macros.find(s, decls) && nullptr != decls.find(arity, domain); } void cmd_context::insert_macro(symbol const& s, unsigned arity, sort*const* domain, expr* t) { macro_decls decls; if (!m_macros.find(s, decls)) { VERIFY(decls.insert(m(), arity, domain, t)); m_macros.insert(s, decls); } else { VERIFY(decls.insert(m(), arity, domain, t)); } } void cmd_context::erase_macro(symbol const& s) { macro_decls decls; VERIFY(m_macros.find(s, decls)); decls.erase_last(m()); } bool cmd_context::macros_find(symbol const& s, unsigned n, expr*const* args, expr*& t) const { macro_decls decls; if (!m_macros.find(s, decls)) { return false; } for (macro_decl const& d : decls) { if (d.m_domain.size() != n) continue; bool eq = true; for (unsigned i = 0; eq && i < n; ++i) { eq = m().compatible_sorts(d.m_domain[i], m().get_sort(args[i])); } if (eq) { t = d.m_body; return true; } } return false; } ast_object_ref::ast_object_ref(cmd_context & ctx, ast * a):m_ast(a) { ctx.m().inc_ref(a); } void ast_object_ref::finalize(cmd_context & ctx) { ctx.m().dec_ref(m_ast); } void stream_ref::set(std::ostream& out) { reset(); m_owner = false; m_name = "caller-owned"; m_stream = &out; } void stream_ref::set(char const * name) { if (!name) { throw cmd_exception("invalid stream name"); } reset(); SASSERT(!m_owner); if (strcmp(name, "stdout") == 0) { m_name = "stdout"; m_stream = &std::cout; } else if (strcmp(name, "stderr") == 0) { m_name = "stderr"; m_stream = &std::cerr; } else { m_stream = alloc(std::ofstream, name, std::ios_base::app); m_name = name; m_owner = true; if (m_stream->bad() || m_stream->fail()) { reset(); std::string msg = "failed to set output stream '"; msg += name; msg += "'"; throw cmd_exception(std::move(msg)); } SASSERT(m_stream); } } void stream_ref::reset() { if (m_owner) dealloc(m_stream); m_name = m_default_name; m_stream = &m_default; m_owner = false; } class cmd_context::pp_env : public smt2_pp_environment { protected: cmd_context & m_owner; arith_util m_autil; bv_util m_bvutil; array_util m_arutil; fpa_util m_futil; seq_util m_sutil; datatype_util m_dtutil; datalog::dl_decl_util m_dlutil; format_ns::format * pp_fdecl_name(symbol const & s, func_decls const & fs, func_decl * f, unsigned & len) { format_ns::format * f_name = smt2_pp_environment::pp_fdecl_name(s, len, f->is_skolem()); if (!fs.more_than_one()) return f_name; if (!fs.clash(f)) return f_name; return pp_as(f_name, f->get_range()); } format_ns::format * pp_fdecl_ref_core(symbol const & s, func_decls const & fs, func_decl * f) { unsigned len; format_ns::format * f_name = smt2_pp_environment::pp_fdecl_name(s, len, f->is_skolem()); if (!fs.more_than_one()) return f_name; return pp_signature(f_name, f); } public: pp_env(cmd_context & o):m_owner(o), m_autil(o.m()), m_bvutil(o.m()), m_arutil(o.m()), m_futil(o.m()), m_sutil(o.m()), m_dtutil(o.m()), m_dlutil(o.m()) {} ~pp_env() override {} ast_manager & get_manager() const override { return m_owner.m(); } arith_util & get_autil() override { return m_autil; } bv_util & get_bvutil() override { return m_bvutil; } array_util & get_arutil() override { return m_arutil; } fpa_util & get_futil() override { return m_futil; } seq_util & get_sutil() override { return m_sutil; } datatype_util & get_dtutil() override { return m_dtutil; } datalog::dl_decl_util& get_dlutil() override { return m_dlutil; } bool uses(symbol const & s) const override { return m_owner.m_builtin_decls.contains(s) || m_owner.m_func_decls.contains(s); } format_ns::format * pp_sort(sort * s) override { return m_owner.pp(s); } format_ns::format * pp_fdecl(func_decl * f, unsigned & len) override { symbol s = f->get_name(); func_decls fs; if (m_owner.m_func_decls.find(s, fs) && fs.contains(f)) { return pp_fdecl_name(s, fs, f, len); } if (m_owner.m_func_decl2alias.find(f, s) && m_owner.m_func_decls.find(s, fs) && fs.contains(f)) { return pp_fdecl_name(s, fs, f, len); } return smt2_pp_environment::pp_fdecl(f, len); } format_ns::format * pp_fdecl_ref(func_decl * f) override { symbol s = f->get_name(); func_decls fs; if (m_owner.m_func_decls.find(s, fs) && fs.contains(f)) { return pp_fdecl_ref_core(s, fs, f); } if (m_owner.m_func_decl2alias.find(f, s) && m_owner.m_func_decls.find(s, fs) && fs.contains(f)) { return pp_fdecl_ref_core(s, fs, f); } return smt2_pp_environment::pp_fdecl_ref(f); } }; cmd_context::cmd_context(bool main_ctx, ast_manager * m, symbol const & l): m_main_ctx(main_ctx), m_logic(l), m_interactive_mode(false), m_global_decls(false), m_print_success(m_params.m_smtlib2_compliant), m_random_seed(0), m_produce_unsat_cores(false), m_produce_unsat_assumptions(false), m_produce_assignments(false), m_status(UNKNOWN), m_numeral_as_real(false), m_ignore_check(false), m_processing_pareto(false), m_exit_on_error(false), m_manager(m), m_own_manager(m == nullptr), m_manager_initialized(false), m_pmanager(nullptr), m_sexpr_manager(nullptr), m_regular("stdout", std::cout), m_diagnostic("stderr", std::cerr) { SASSERT(m != 0 || !has_manager()); install_basic_cmds(*this); install_ext_basic_cmds(*this); install_core_tactic_cmds(*this); SASSERT(m != 0 || !has_manager()); if (m_main_ctx) { set_verbose_stream(diagnostic_stream()); } } cmd_context::~cmd_context() { if (m_main_ctx) { set_verbose_stream(std::cerr); } pop(m_scopes.size()); finalize_cmds(); finalize_tactic_cmds(); finalize_probes(); reset(true); m_mc0 = nullptr; m_solver = nullptr; m_check_sat_result = nullptr; } void cmd_context::set_cancel(bool f) { if (has_manager()) { if (f) { m().limit().cancel(); } else { m().limit().reset_cancel(); } } } opt_wrapper* cmd_context::get_opt() { return m_opt.get(); } void cmd_context::set_opt(opt_wrapper* opt) { m_opt = opt; for (unsigned i = 0; i < m_scopes.size(); ++i) { m_opt->push(); } m_opt->set_logic(m_logic); } void cmd_context::global_params_updated() { m_params.updt_params(); if (m_params.m_smtlib2_compliant) m_print_success = true; if (m_solver) { params_ref p; if (!m_params.m_auto_config) p.set_bool("auto_config", false); m_solver->updt_params(p); } if (m_opt) { get_opt()->updt_params(gparams::get_module("opt")); } } void cmd_context::set_produce_models(bool f) { if (m_solver) m_solver->set_produce_models(f); m_params.m_model = f; } void cmd_context::set_produce_unsat_cores(bool f) { // can only be set before initialization SASSERT(!has_manager()); m_params.m_unsat_core = f; } void cmd_context::set_produce_proofs(bool f) { // can only be set before initialization SASSERT(!has_manager()); m_params.m_proof = f; } bool cmd_context::produce_models() const { return m_params.m_model; } bool cmd_context::produce_proofs() const { return m_params.m_proof; } bool cmd_context::produce_unsat_cores() const { return m_params.m_unsat_core; } bool cmd_context::well_sorted_check_enabled() const { return m_params.m_well_sorted_check; } bool cmd_context::validate_model_enabled() const { return m_params.m_model_validate; } cmd_context::check_sat_state cmd_context::cs_state() const { if (m_check_sat_result.get() == nullptr) return css_clear; switch (m_check_sat_result->status()) { case l_true: return css_sat; case l_false: return css_unsat; default: return css_unknown; } } void cmd_context::register_builtin_sorts(decl_plugin * p) { svector names; p->get_sort_names(names, m_logic); family_id fid = p->get_family_id(); for (builtin_name const& n : names) { psort_decl * d = pm().mk_psort_builtin_decl(n.m_name, fid, n.m_kind); insert(d); } } void cmd_context::register_builtin_ops(decl_plugin * p) { svector names; p->get_op_names(names, m_logic); family_id fid = p->get_family_id(); for (builtin_name const& n : names) { if (m_builtin_decls.contains(n.m_name)) { builtin_decl & d = m_builtin_decls.find(n.m_name); builtin_decl * new_d = alloc(builtin_decl, fid, n.m_kind, d.m_next); d.m_next = new_d; m_extra_builtin_decls.push_back(new_d); } else { m_builtin_decls.insert(n.m_name, builtin_decl(fid, n.m_kind)); } } } void cmd_context::register_plugin(symbol const & name, decl_plugin * p, bool install_names) { m_manager->register_plugin(name, p); if (install_names) { register_builtin_sorts(p); register_builtin_ops(p); } } void cmd_context::load_plugin(symbol const & name, bool install, svector& fids) { family_id id = m_manager->get_family_id(name); decl_plugin* p = m_manager->get_plugin(id); if (install && p && fids.contains(id)) { register_builtin_sorts(p); register_builtin_ops(p); } fids.erase(id); } bool cmd_context::logic_has_arith() const { return !has_logic() || smt_logics::logic_has_arith(m_logic); } bool cmd_context::logic_has_bv() const { return !has_logic() || smt_logics::logic_has_bv(m_logic); } bool cmd_context::logic_has_seq() const { return !has_logic() || smt_logics::logic_has_seq(m_logic); } bool cmd_context::logic_has_pb() const { return !has_logic() || smt_logics::logic_has_pb(m_logic); } bool cmd_context::logic_has_fpa() const { return !has_logic() || smt_logics::logic_has_fpa(m_logic); } bool cmd_context::logic_has_array() const { return !has_logic() || smt_logics::logic_has_array(m_logic); } bool cmd_context::logic_has_datatype() const { return !has_logic() || smt_logics::logic_has_datatype(m_logic); } bool cmd_context::logic_has_recfun() const { return true; } void cmd_context::init_manager_core(bool new_manager) { SASSERT(m_manager != 0); SASSERT(m_pmanager != 0); if (new_manager) { decl_plugin * basic = m_manager->get_plugin(m_manager->get_basic_family_id()); register_builtin_sorts(basic); register_builtin_ops(basic); // the manager was created by the command context. register_plugin(symbol("arith"), alloc(arith_decl_plugin), logic_has_arith()); register_plugin(symbol("bv"), alloc(bv_decl_plugin), logic_has_bv()); register_plugin(symbol("array"), alloc(array_decl_plugin), logic_has_array()); register_plugin(symbol("datatype"), alloc(datatype_decl_plugin), logic_has_datatype()); register_plugin(symbol("recfun"), alloc(recfun::decl::plugin), logic_has_recfun()); register_plugin(symbol("seq"), alloc(seq_decl_plugin), logic_has_seq()); register_plugin(symbol("pb"), alloc(pb_decl_plugin), logic_has_pb()); register_plugin(symbol("fpa"), alloc(fpa_decl_plugin), logic_has_fpa()); register_plugin(symbol("datalog_relation"), alloc(datalog::dl_decl_plugin), !has_logic()); register_plugin(symbol("csp"), alloc(csp_decl_plugin), smt_logics::logic_is_csp(m_logic)); register_plugin(symbol("special_relations"), alloc(special_relations_decl_plugin), !has_logic()); } else { // the manager was created by an external module // we register all plugins available in the manager. // unless the logic specifies otherwise. svector fids; m_manager->get_range(fids); load_plugin(symbol("arith"), logic_has_arith(), fids); load_plugin(symbol("bv"), logic_has_bv(), fids); load_plugin(symbol("array"), logic_has_array(), fids); load_plugin(symbol("datatype"), logic_has_datatype(), fids); load_plugin(symbol("recfun"), logic_has_recfun(), fids); load_plugin(symbol("seq"), logic_has_seq(), fids); load_plugin(symbol("fpa"), logic_has_fpa(), fids); load_plugin(symbol("pb"), logic_has_pb(), fids); load_plugin(symbol("csp"), smt_logics::logic_is_csp(m_logic), fids); for (family_id fid : fids) { decl_plugin * p = m_manager->get_plugin(fid); if (p) { register_builtin_sorts(p); register_builtin_ops(p); } } } m_dt_eh = alloc(dt_eh, *this); m_pmanager->set_new_datatype_eh(m_dt_eh.get()); if (!has_logic() && new_manager) { TRACE("cmd_context", tout << "init manager " << m_logic << "\n";); // add list type only if the logic is not specified. // it prevents clashes with builtin types. insert(pm().mk_plist_decl()); } if (m_solver_factory) { mk_solver(); } m_check_logic.set_logic(m(), m_logic); } void cmd_context::init_manager() { if (m_manager_initialized) { // no-op } else if (m_manager) { m_manager_initialized = true; SASSERT(!m_own_manager); init_external_manager(); } else { m_manager_initialized = true; SASSERT(m_pmanager == 0); m_check_sat_result = nullptr; m_manager = m_params.mk_ast_manager(); m_pmanager = alloc(pdecl_manager, *m_manager); init_manager_core(true); } } void cmd_context::init_external_manager() { SASSERT(m_manager != 0); SASSERT(m_pmanager == 0); m_pmanager = alloc(pdecl_manager, *m_manager); init_manager_core(false); } bool cmd_context::set_logic(symbol const & s) { TRACE("cmd_context", tout << s << "\n";); if (has_logic()) throw cmd_exception("the logic has already been set"); if (has_manager() && m_main_ctx) throw cmd_exception("logic must be set before initialization"); if (!smt_logics::supported_logic(s)) { return false; } m_logic = s; if (smt_logics::logic_has_reals_only(s)) { m_numeral_as_real = true; } return true; } std::string cmd_context::reason_unknown() const { if (m_check_sat_result.get() == nullptr) return "state of the most recent check-sat command is not known"; return m_check_sat_result->reason_unknown(); } bool cmd_context::is_func_decl(symbol const & s) const { return m_builtin_decls.contains(s) || m_func_decls.contains(s); } void cmd_context::insert(symbol const & s, func_decl * f) { if (!m_check_logic(f)) { throw cmd_exception(m_check_logic.get_last_error()); } if (contains_macro(s, f)) { throw cmd_exception("invalid declaration, named expression already defined with this name ", s); } #if 0 if (m_builtin_decls.contains(s)) { throw cmd_exception("invalid declaration, builtin symbol ", s); } #endif dictionary::entry * e = m_func_decls.insert_if_not_there2(s, func_decls()); func_decls & fs = e->get_data().m_value; if (!fs.insert(m(), f)) { std::string msg = "invalid declaration, "; msg += f->get_arity() == 0 ? "constant" : "function"; msg += " '"; msg += s.str(); msg += "' (with the given signature) already declared"; throw cmd_exception(std::move(msg)); } if (s != f->get_name()) { TRACE("func_decl_alias", tout << "adding alias for: " << f->get_name() << ", alias: " << s << "\n";); m_func_decl2alias.insert(f, s); } if (!m_global_decls) { m_func_decls_stack.push_back(sf_pair(s, f)); } TRACE("cmd_context", tout << "new function decl\n" << mk_pp(f, m()) << "\n";); } void cmd_context::insert(symbol const & s, psort_decl * p) { if (m_psort_decls.contains(s)) { throw cmd_exception("sort already defined ", s); } pm().inc_ref(p); m_psort_decls.insert(s, p); if (!m_global_decls) { m_psort_decls_stack.push_back(s); } TRACE("cmd_context", tout << "new sort decl\n"; p->display(tout); tout << "\n";); } void cmd_context::insert(symbol const & s, unsigned arity, sort *const* domain, expr * t) { expr_ref _t(t, m()); #if 0 if (m_builtin_decls.contains(s)) { throw cmd_exception("invalid macro/named expression, builtin symbol ", s); } #endif if (contains_macro(s, arity, domain)) { throw cmd_exception("named expression already defined"); } if (contains_func_decl(s, arity, domain, m().get_sort(t))) { throw cmd_exception("invalid named expression, declaration already defined with this name ", s); } TRACE("insert_macro", tout << "new macro " << arity << "\n" << mk_pp(t, m()) << "\n";); insert_macro(s, arity, domain, t); if (!m_global_decls) { m_macros_stack.push_back(s); } } void cmd_context::insert(cmd * c) { symbol const & s = c->get_name(); cmd * old_c; if (m_cmds.find(s, old_c) && c != old_c) { old_c->finalize(*this); dealloc(old_c); } m_cmds.insert(s, c); } void cmd_context::insert_user_tactic(symbol const & s, sexpr * d) { sm().inc_ref(d); sexpr * old_d; if (m_user_tactic_decls.find(s, old_d)) { sm().dec_ref(old_d); } m_user_tactic_decls.insert(s, d); } void cmd_context::insert(symbol const & s, object_ref * r) { r->inc_ref(*this); object_ref * old_r = nullptr; if (m_object_refs.find(s, old_r)) { old_r->dec_ref(*this); } m_object_refs.insert(s, r); } void cmd_context::model_add(symbol const & s, unsigned arity, sort *const* domain, expr * t) { if (!m_mc0.get()) m_mc0 = alloc(generic_model_converter, m(), "cmd_context"); if (m_solver.get() && !m_solver->mc0()) m_solver->set_model_converter(m_mc0.get()); func_decl_ref fn(m().mk_func_decl(s, arity, domain, m().get_sort(t)), m()); dictionary::entry * e = m_func_decls.insert_if_not_there2(s, func_decls()); func_decls & fs = e->get_data().m_value; fs.insert(m(), fn); VERIFY(fn->get_range() == m().get_sort(t)); m_mc0->add(fn, t); } void cmd_context::model_del(func_decl* f) { if (!m_mc0.get()) m_mc0 = alloc(generic_model_converter, m(), "cmd_context"); if (m_solver.get() && !m_solver->mc0()) m_solver->set_model_converter(m_mc0.get()); m_mc0->hide(f); } recfun::decl::plugin& cmd_context::get_recfun_plugin() { recfun::util u(get_ast_manager()); return u.get_plugin(); } recfun::promise_def cmd_context::decl_rec_fun(const symbol &name, unsigned int arity, sort *const *domain, sort *range) { SASSERT(logic_has_recfun()); return get_recfun_plugin().mk_def(name, arity, domain, range); } // insert a recursive function as a regular quantified axiom void cmd_context::insert_rec_fun_as_axiom(func_decl *f, expr_ref_vector const& binding, svector const &ids, expr* e) { expr_ref eq(m()); app_ref lhs(m()); lhs = m().mk_app(f, binding.size(), binding.c_ptr()); eq = m().mk_eq(lhs, e); if (!ids.empty()) { if (is_var(e)) { ptr_vector domain; for (expr* b : binding) domain.push_back(m().get_sort(b)); insert_macro(f->get_name(), domain.size(), domain.c_ptr(), e); return; } if (!is_app(e)) { throw cmd_exception("Z3 only supports recursive definitions that are proper terms (not binders or variables)"); } expr* pats[2] = { m().mk_pattern(lhs), m().mk_pattern(to_app(e)) }; eq = m().mk_forall(ids.size(), f->get_domain(), ids.c_ptr(), eq, 0, m().rec_fun_qid(), symbol::null, 2, pats); } assert_expr(eq); } void cmd_context::insert_rec_fun(func_decl* f, expr_ref_vector const& binding, svector const& ids, expr* rhs) { if (gparams::get_value("smt.recfun.native") != "true") { // just use an axiom insert_rec_fun_as_axiom(f, binding, ids, rhs); return; } TRACE("recfun", tout<< "define recfun " << f->get_name() << " = " << mk_pp(rhs, m()) << "\n";); recfun::decl::plugin& p = get_recfun_plugin(); var_ref_vector vars(m()); for (expr* b : binding) { SASSERT(is_var(b)); vars.push_back(to_var(b)); } recfun::promise_def d = p.get_promise_def(f); recfun_replace replace(m()); p.set_definition(replace, d, vars.size(), vars.c_ptr(), rhs); } func_decl * cmd_context::find_func_decl(symbol const & s) const { if (contains_macro(s)) { throw cmd_exception("invalid function declaration reference, named expressions (aka macros) cannot be referenced ", s); } func_decls fs; if (m_func_decls.find(s, fs)) { if (fs.more_than_one()) throw cmd_exception("ambiguous function declaration reference, provide full signature to disumbiguate ( (*) ) ", s); return fs.first(); } builtin_decl d; if (m_builtin_decls.find(s, d)) { try { // Remark: ignoring m_next of d. We do not allow two different theories to define the same constant name. func_decl * f; f = m().mk_func_decl(d.m_fid, d.m_decl, 0, nullptr, 0, static_cast(nullptr), nullptr); if (f != nullptr) return f; } catch (ast_exception &) { } throw cmd_exception("invalid function declaration reference, must provide signature for builtin symbol ", s); } throw cmd_exception("invalid function declaration reference, unknown function ", s); return nullptr; } /** \brief Select a builtin_decl from the list starting at first. We select the decl d s.t. d->m_fid == target_id If there is none that satisfies this condition, we return first. This is a HACK for supporting arithmetic and floating-point arithmetic. These are two different theories in Z3, but they share builtin symbol names: +, -, *, /, <, <=, >, >= */ static builtin_decl const & peek_builtin_decl(builtin_decl const & first, family_id target_id) { builtin_decl const * curr = &first; while (curr != nullptr) { if (curr->m_fid == target_id) return *curr; curr = curr->m_next; } return first; } func_decl * cmd_context::find_func_decl(symbol const & s, unsigned num_indices, unsigned const * indices, unsigned arity, sort * const * domain, sort * range) const { if (domain && contains_macro(s, arity, domain)) throw cmd_exception("invalid function declaration reference, named expressions (aka macros) cannot be referenced ", s); func_decl * f = nullptr; func_decls fs; if (num_indices == 0 && m_func_decls.find(s, fs)) { f = fs.find(arity, domain, range); } if (f) { return f; } builtin_decl d; if (domain && m_builtin_decls.find(s, d)) { family_id fid = d.m_fid; decl_kind k = d.m_decl; // Hack: if d.m_next != 0, we use domain[0] (if available) to decide which plugin we use. if (d.m_decl != 0 && arity > 0) { builtin_decl const & d2 = peek_builtin_decl(d, domain[0]->get_family_id()); fid = d2.m_fid; k = d2.m_decl; } func_decl * f; if (num_indices == 0) { f = m().mk_func_decl(fid, k, 0, nullptr, arity, domain, range); } else { buffer ps; for (unsigned i = 0; i < num_indices; i++) ps.push_back(parameter(indices[i])); f = m().mk_func_decl(fid, k, num_indices, ps.c_ptr(), arity, domain, range); } if (f == nullptr) throw cmd_exception("invalid function declaration reference, invalid builtin reference ", s); return f; } throw cmd_exception("invalid function declaration reference, unknown function ", s); } psort_decl * cmd_context::find_psort_decl(symbol const & s) const { psort_decl * p = nullptr; m_psort_decls.find(s, p); return p; } cmd * cmd_context::find_cmd(symbol const & s) const { cmd * c = nullptr; m_cmds.find(s, c); return c; } sexpr * cmd_context::find_user_tactic(symbol const & s) const { sexpr * n = nullptr; m_user_tactic_decls.find(s, n); return n; } object_ref * cmd_context::find_object_ref(symbol const & s) const { object_ref * r = nullptr; m_object_refs.find(s, r); if (r == nullptr) throw cmd_exception("unknown global variable ", s); return r; } #define CHECK_SORT(T) if (well_sorted_check_enabled()) m().check_sorts_core(T) void cmd_context::mk_const(symbol const & s, expr_ref & result) const { mk_app(s, 0, nullptr, 0, nullptr, nullptr, result); } void cmd_context::mk_app(symbol const & s, unsigned num_args, expr * const * args, unsigned num_indices, parameter const * indices, sort * range, expr_ref & result) const { expr* _t; if (macros_find(s, num_args, args, _t)) { TRACE("macro_bug", tout << "well_sorted_check_enabled(): " << well_sorted_check_enabled() << "\n"; tout << "s: " << s << "\n"; tout << "body:\n" << mk_ismt2_pp(_t, m()) << "\n"; tout << "args:\n"; for (unsigned i = 0; i < num_args; i++) tout << mk_ismt2_pp(args[i], m()) << "\n" << mk_pp(m().get_sort(args[i]), m()) << "\n";); var_subst subst(m()); scoped_rlimit no_limit(m().limit(), 0); result = subst(_t, num_args, args); if (well_sorted_check_enabled() && !is_well_sorted(m(), result)) throw cmd_exception("invalid macro application, sort mismatch ", s); return; } func_decls fs; if (!m_func_decls.find(s, fs)) { builtin_decl d; if (m_builtin_decls.find(s, d)) { family_id fid = d.m_fid; decl_kind k = d.m_decl; // Hack: if d.m_next != 0, we use the sort of args[0] (if available) to decide which plugin we use. if (d.m_decl != 0 && num_args > 0) { builtin_decl const & d2 = peek_builtin_decl(d, m().get_sort(args[0])->get_family_id()); fid = d2.m_fid; k = d2.m_decl; } if (num_indices == 0) { result = m().mk_app(fid, k, 0, nullptr, num_args, args, range); } else { result = m().mk_app(fid, k, num_indices, indices, num_args, args, range); } if (result.get() == nullptr) throw cmd_exception("invalid builtin application ", s); CHECK_SORT(result.get()); return; } if (num_indices > 0) throw cmd_exception("invalid use of indexed identifier, unknown builtin function ", s); if (num_args == 0) { throw cmd_exception("unknown constant ", s); } else throw cmd_exception("unknown function/constant ", s); } if (num_args == 0 && range == nullptr) { if (fs.more_than_one()) throw cmd_exception("ambiguous constant reference, more than one constant with the same sort, use a qualified expression (as ) to disambiguate ", s); func_decl * f = fs.first(); if (f == nullptr) { throw cmd_exception("unknown constant ", s); } if (f->get_arity() != 0) { result = array_util(m()).mk_as_array(f); } else { result = m().mk_const(f); } } else { func_decl * f = fs.find(m(), num_args, args, range); if (f == nullptr) { std::ostringstream buffer; buffer << "unknown constant " << s << " "; buffer << " ("; bool first = true; for (unsigned i = 0; i < num_args; ++i, first = false) { if (!first) buffer << " "; buffer << mk_pp(m().get_sort(args[i]), m()); } buffer << ") "; if (range) buffer << mk_pp(range, m()) << " "; for (unsigned i = 0; i < fs.get_num_entries(); ++i) { buffer << "\ndeclared: " << mk_pp(fs.get_entry(i), m()) << " "; } throw cmd_exception(buffer.str()); } if (well_sorted_check_enabled()) m().check_sort(f, num_args, args); result = m().mk_app(f, num_args, args); } } void cmd_context::erase_func_decl(symbol const & s) { if (!global_decls()) { throw cmd_exception("function declarations can only be erased when global declarations (instead of scoped) are used"); } func_decls fs; m_func_decls.find(s, fs); while (!fs.empty()) { func_decl * f = fs.first(); if (s != f->get_name()) { SASSERT(m_func_decl2alias.contains(f)); m_func_decl2alias.erase(f); } fs.erase(m(), f); } fs.finalize(m()); m_func_decls.erase(s); } void cmd_context::erase_func_decl_core(symbol const & s, func_decl * f) { func_decls fs; m_func_decls.find(s, fs); if (fs.contains(f)) { if (s != f->get_name()) { SASSERT(m_func_decl2alias.contains(f)); m_func_decl2alias.erase(f); } fs.erase(m(), f); if (fs.empty()) m_func_decls.erase(s); } } void cmd_context::erase_func_decl(symbol const & s, func_decl * f) { if (!global_decls()) { throw cmd_exception("function declarations can only be erased when global (instead of scoped) declarations are used"); } erase_func_decl_core(s, f); } void cmd_context::erase_psort_decl_core(symbol const & s) { psort_decl * p; if (m_psort_decls.find(s, p)) { pm().dec_ref(p); m_psort_decls.erase(s); } } void cmd_context::erase_psort_decl(symbol const & s) { if (!global_decls()) { throw cmd_exception("sort declarations can only be erased when global (instead of scoped) declarations are used"); } erase_psort_decl_core(s); } void cmd_context::erase_cmd(symbol const & s) { cmd * c; if (m_cmds.find(s, c)) { c->finalize(*this); m_cmds.erase(s); dealloc(c); } } void cmd_context::erase_user_tactic(symbol const & s) { sexpr * d; if (m_user_tactic_decls.find(s, d)) { m_user_tactic_decls.erase(s); sm().dec_ref(d); } } void cmd_context::erase_object_ref(symbol const & s) { object_ref * r = nullptr; if (m_object_refs.find(s, r)) { r->dec_ref(*this); m_object_refs.erase(s); } } void cmd_context::reset_func_decls() { for (auto & kv : m_func_decls) { kv.m_value.finalize(m()); } m_func_decls.reset(); m_func_decls_stack.reset(); m_func_decl2alias.reset(); } void cmd_context::reset_psort_decls() { for (auto & kv : m_psort_decls) { psort_decl * p = kv.m_value; pm().dec_ref(p); } m_psort_decls.reset(); m_psort_decls_stack.reset(); } void cmd_context::reset_macros() { for (auto & kv : m_macros) { kv.m_value.finalize(m()); } m_macros.reset(); m_macros_stack.reset(); } void cmd_context::reset_cmds() { for (auto& kv : m_cmds) { kv.m_value->reset(*this); } } void cmd_context::finalize_cmds() { for (auto& kv : m_cmds) { cmd * c = kv.m_value; c->finalize(*this); dealloc(c); } m_cmds.reset(); } void cmd_context::reset_user_tactics() { dec_ref_values(sm(), m_user_tactic_decls); m_user_tactic_decls.reset(); } void cmd_context::reset_object_refs() { for (auto& kv : m_object_refs) { object_ref * r = kv.m_value; r->dec_ref(*this); } m_object_refs.reset(); } void cmd_context::insert_aux_pdecl(pdecl * p) { pm().inc_ref(p); m_aux_pdecls.push_back(p); } void cmd_context::reset(bool finalize) { m_processing_pareto = false; m_logic = symbol::null; m_check_sat_result = nullptr; m_numeral_as_real = false; m_builtin_decls.reset(); m_extra_builtin_decls.reset(); m_check_logic.reset(); reset_object_refs(); reset_cmds(); reset_psort_decls(); restore_aux_pdecls(0); reset_macros(); reset_func_decls(); restore_assertions(0); m_solver = nullptr; m_mc0 = nullptr; m_scopes.reset(); m_opt = nullptr; m_pp_env = nullptr; m_dt_eh = nullptr; if (m_manager) { dealloc(m_pmanager); m_pmanager = nullptr; if (m_own_manager) { dealloc(m_manager); m_manager = nullptr; m_manager_initialized = false; } else { // doesn't own manager... so it cannot be deleted // reinit cmd_context if this is not a finalization step if (!finalize) init_external_manager(); else m_manager_initialized = false; } } if (m_sexpr_manager) { dealloc(m_sexpr_manager); m_sexpr_manager = nullptr; } SASSERT(!m_own_manager || !has_manager()); } void cmd_context::assert_expr(expr * t) { scoped_rlimit no_limit(m().limit(), 0); m_processing_pareto = false; if (!m_check_logic(t)) throw cmd_exception(m_check_logic.get_last_error()); m_check_sat_result = nullptr; m().inc_ref(t); m_assertions.push_back(t); if (produce_unsat_cores()) m_assertion_names.push_back(nullptr); if (m_solver) m_solver->assert_expr(t); } void cmd_context::assert_expr(symbol const & name, expr * t) { m_processing_pareto = false; if (!m_check_logic(t)) throw cmd_exception(m_check_logic.get_last_error()); if (!produce_unsat_cores() || name == symbol::null) { assert_expr(t); return; } scoped_rlimit no_limit(m().limit(), 0); m_check_sat_result = nullptr; m().inc_ref(t); m_assertions.push_back(t); app * ans = m().mk_skolem_const(name, m().mk_bool_sort()); m().inc_ref(ans); m_assertion_names.push_back(ans); if (m_solver) m_solver->assert_expr(t, ans); } void cmd_context::push() { m_check_sat_result = nullptr; init_manager(); m_scopes.push_back(scope()); scope & s = m_scopes.back(); s.m_func_decls_stack_lim = m_func_decls_stack.size(); s.m_psort_decls_stack_lim = m_psort_decls_stack.size(); s.m_psort_inst_stack_lim = m_psort_inst_stack.size(); s.m_macros_stack_lim = m_macros_stack.size(); s.m_aux_pdecls_lim = m_aux_pdecls.size(); s.m_assertions_lim = m_assertions.size(); m().limit().push(m_params.rlimit()); if (m_solver) m_solver->push(); if (m_opt) m_opt->push(); } void cmd_context::push(unsigned n) { for (unsigned i = 0; i < n; i++) push(); } void cmd_context::restore_func_decls(unsigned old_sz) { SASSERT(old_sz <= m_func_decls_stack.size()); svector::iterator it = m_func_decls_stack.begin() + old_sz; svector::iterator end = m_func_decls_stack.end(); for (; it != end; ++it) { sf_pair const & p = *it; erase_func_decl_core(p.first, p.second); } m_func_decls_stack.resize(old_sz); } void cmd_context::restore_psort_inst(unsigned old_sz) { for (unsigned i = m_psort_inst_stack.size(); i-- > old_sz; ) { pdecl * s = m_psort_inst_stack[i]; s->reset_cache(pm()); pm().dec_ref(s); } m_psort_inst_stack.resize(old_sz); } void cmd_context::restore_psort_decls(unsigned old_sz) { SASSERT(old_sz <= m_psort_decls_stack.size()); svector::iterator it = m_psort_decls_stack.begin() + old_sz; svector::iterator end = m_psort_decls_stack.end(); for (; it != end; ++it) { symbol const & s = *it; psort_decl * d = nullptr; VERIFY(m_psort_decls.find(s, d)); pm().dec_ref(d); m_psort_decls.erase(s); } m_psort_decls_stack.shrink(old_sz); } void cmd_context::restore_macros(unsigned old_sz) { SASSERT(old_sz <= m_macros_stack.size()); svector::iterator it = m_macros_stack.begin() + old_sz; svector::iterator end = m_macros_stack.end(); for (; it != end; ++it) { symbol const & s = *it; erase_macro(s); } m_macros_stack.shrink(old_sz); } void cmd_context::restore_aux_pdecls(unsigned old_sz) { SASSERT(old_sz <= m_aux_pdecls.size()); ptr_vector::iterator it = m_aux_pdecls.begin() + old_sz; ptr_vector::iterator end = m_aux_pdecls.end(); for (; it != end; ++it) { pm().dec_ref(*it); } m_aux_pdecls.shrink(old_sz); } static void restore(ast_manager & m, ptr_vector & c, unsigned old_sz) { ptr_vector::iterator it = c.begin() + old_sz; ptr_vector::iterator end = c.end(); for (; it != end; ++it) { m.dec_ref(*it); } c.shrink(old_sz); } void cmd_context::restore_assertions(unsigned old_sz) { m_processing_pareto = false; if (!has_manager()) { // restore_assertions invokes m(), so if cmd_context does not have a manager, it will try to create one. SASSERT(old_sz == m_assertions.size()); SASSERT(m_assertions.empty()); return; } if (old_sz == m_assertions.size()) return; SASSERT(old_sz < m_assertions.size()); SASSERT(!m_interactive_mode || m_assertions.size() == m_assertion_strings.size()); restore(m(), m_assertions, old_sz); if (produce_unsat_cores()) restore(m(), m_assertion_names, old_sz); if (m_interactive_mode) m_assertion_strings.resize(old_sz); } void cmd_context::pop(unsigned n) { m_check_sat_result = nullptr; m_processing_pareto = false; if (n == 0) return; unsigned lvl = m_scopes.size(); if (n > lvl) throw cmd_exception("invalid pop command, argument is greater than the current stack depth"); if (m_solver) { m_solver->pop(n); } if (m_opt) m_opt->pop(n); unsigned new_lvl = lvl - n; scope & s = m_scopes[new_lvl]; restore_func_decls(s.m_func_decls_stack_lim); restore_psort_decls(s.m_psort_decls_stack_lim); restore_macros(s.m_macros_stack_lim); restore_aux_pdecls(s.m_aux_pdecls_lim); restore_assertions(s.m_assertions_lim); restore_psort_inst(s.m_psort_inst_stack_lim); m_scopes.shrink(new_lvl); while (n--) { m().limit().pop(); } } void cmd_context::check_sat(unsigned num_assumptions, expr * const * assumptions) { if (m_ignore_check) return; IF_VERBOSE(100, verbose_stream() << "(started \"check-sat\")" << std::endl;); TRACE("before_check_sat", dump_assertions(tout);); init_manager(); unsigned timeout = m_params.m_timeout; unsigned rlimit = m_params.rlimit(); scoped_watch sw(*this); lbool r; bool was_opt = false; if (m_opt && !m_opt->empty()) { was_opt = true; m_check_sat_result = get_opt(); cancel_eh eh(m().limit()); scoped_ctrl_c ctrlc(eh); scoped_timer timer(timeout, &eh); scoped_rlimit _rlimit(m().limit(), rlimit); expr_ref_vector asms(m()); asms.append(num_assumptions, assumptions); if (!m_processing_pareto) { expr_ref_vector assertions(m()); unsigned sz = m_assertions.size(); for (unsigned i = 0; i < sz; ++i) { if (m_assertion_names.size() > i && m_assertion_names[i]) { asms.push_back(m_assertion_names[i]); assertions.push_back(m().mk_implies(m_assertion_names[i], m_assertions[i])); } else { assertions.push_back(m_assertions[i]); } } get_opt()->set_hard_constraints(assertions); } try { r = get_opt()->optimize(asms); if (r == l_true && get_opt()->is_pareto()) { m_processing_pareto = true; } } catch (z3_error & ex) { throw ex; } catch (z3_exception & ex) { throw cmd_exception(ex.msg()); } if (m_processing_pareto && r != l_true) { m_processing_pareto = false; } get_opt()->set_status(r); } else if (m_solver) { m_check_sat_result = m_solver.get(); // solver itself stores the result. m_solver->set_progress_callback(this); cancel_eh eh(m().limit()); scoped_ctrl_c ctrlc(eh); scoped_timer timer(timeout, &eh); scoped_rlimit _rlimit(m().limit(), rlimit); try { r = m_solver->check_sat(num_assumptions, assumptions); if (r == l_undef && m().canceled()) { m_solver->set_reason_unknown(eh); } } catch (z3_error & ex) { throw ex; } catch (z3_exception & ex) { if (m().canceled()) { m_solver->set_reason_unknown(eh); } else { m_solver->set_reason_unknown(ex.msg()); } r = l_undef; } m_solver->set_status(r); } else { // There is no solver installed in the command context. regular_stream() << "unknown" << std::endl; return; } display_sat_result(r); if (r == l_true) { validate_model(); } validate_check_sat_result(r); if (was_opt && r != l_false && !m_processing_pareto) { // get_opt()->display_assignment(regular_stream()); } model_ref md; if (r == l_true && m_params.m_dump_models && is_model_available(md)) { display_model(md); } } void cmd_context::get_consequences(expr_ref_vector const& assumptions, expr_ref_vector const& vars, expr_ref_vector & conseq) { unsigned timeout = m_params.m_timeout; unsigned rlimit = m_params.rlimit(); lbool r; m_check_sat_result = m_solver.get(); // solver itself stores the result. m_solver->set_progress_callback(this); cancel_eh eh(m().limit()); scoped_ctrl_c ctrlc(eh); scoped_timer timer(timeout, &eh); scoped_rlimit _rlimit(m().limit(), rlimit); try { r = m_solver->get_consequences(assumptions, vars, conseq); } catch (z3_error & ex) { throw ex; } catch (z3_exception & ex) { m_solver->set_reason_unknown(ex.msg()); r = l_undef; } m_solver->set_status(r); display_sat_result(r); } void cmd_context::reset_assertions() { if (!m_global_decls) { reset(false); return; } if (m_opt) { m_opt = nullptr; } if (m_solver) { m_solver = nullptr; mk_solver(); } restore_assertions(0); for (scope& s : m_scopes) { s.m_assertions_lim = 0; if (m_solver) m_solver->push(); } } void cmd_context::display_dimacs() { if (m_solver) { try { gparams::set("sat.dimacs.display", "true"); params_ref p; m_solver->updt_params(p); m_solver->check_sat(0, nullptr); } catch (...) { gparams::set("sat.dimacs.display", "false"); params_ref p; m_solver->updt_params(p); throw; } gparams::set("sat.dimacs.display", "false"); params_ref p; m_solver->updt_params(p); } } void cmd_context::display_model(model_ref& mdl) { if (mdl) { if (m_mc0) (*m_mc0)(mdl); model_params p; if (p.compact()) mdl->compress(); add_declared_functions(*mdl); if (p.v1() || p.v2()) { std::ostringstream buffer; model_v2_pp(buffer, *mdl, p.partial()); regular_stream() << "\"" << escaped(buffer.str().c_str(), true) << "\"" << std::endl; } else { regular_stream() << "(model " << std::endl; model_smt2_pp(regular_stream(), *this, *mdl, 2); regular_stream() << ")" << std::endl; } } } void cmd_context::add_declared_functions(model& mdl) { for (auto const& kv : m_func_decls) { func_decl* f = kv.m_value.first(); if (f->get_family_id() == null_family_id && !mdl.has_interpretation(f)) { expr* val = mdl.get_some_value(f->get_range()); if (f->get_arity() == 0) { mdl.register_decl(f, val); } else { func_interp* fi = alloc(func_interp, m(), f->get_arity()); fi->set_else(val); mdl.register_decl(f, fi); } } } } void cmd_context::display_sat_result(lbool r) { switch (r) { case l_true: regular_stream() << "sat" << std::endl; break; case l_false: regular_stream() << "unsat" << std::endl; break; case l_undef: regular_stream() << "unknown" << std::endl; break; } } void cmd_context::validate_check_sat_result(lbool r) { switch (r) { case l_true: if (m_status == UNSAT) { #ifdef _EXTERNAL_RELEASE throw cmd_exception("check annotation that says unsat"); #else diagnostic_stream() << "BUG: incompleteness" << std::endl; // WORKAROUND: `exit()` causes LSan to be invoked and produce // many false positives. _Exit(ERR_INCOMPLETENESS); #endif } break; case l_false: if (m_status == SAT) { #ifdef _EXTERNAL_RELEASE throw cmd_exception("check annotation that says sat"); #else diagnostic_stream() << "BUG: unsoundness" << std::endl; // WORKAROUND: `exit()` causes LSan to be invoked and produce // many false positives. _Exit(ERR_UNSOUNDNESS); #endif } break; default: break; } } void cmd_context::set_diagnostic_stream(char const * name) { m_diagnostic.set(name); if (m_main_ctx) { set_warning_stream(&(*m_diagnostic)); set_verbose_stream(diagnostic_stream()); } } struct contains_underspecified_op_proc { struct found {}; family_id m_array_fid; datatype_util m_dt; seq_util m_seq; family_id m_seq_id; contains_underspecified_op_proc(ast_manager & m):m_array_fid(m.mk_family_id("array")), m_dt(m), m_seq(m), m_seq_id(m_seq.get_family_id()) {} void operator()(var * n) {} void operator()(app * n) { if (m_dt.is_accessor(n->get_decl())) throw found(); if (n->get_family_id() == m_array_fid) { decl_kind k = n->get_decl_kind(); if (k == OP_AS_ARRAY || k == OP_STORE || k == OP_ARRAY_MAP || k == OP_CONST_ARRAY) throw found(); } if (n->get_family_id() == m_seq_id && m_seq.is_re(n)) { throw found(); } } void operator()(quantifier * n) {} }; /** \brief Complete the model if necessary. */ void cmd_context::complete_model(model_ref& md) const { if (!md.get()) return; if (gparams::get_value("model.completion") != "true") return; params_ref p; p.set_uint("max_degree", UINT_MAX); // evaluate algebraic numbers of any degree. p.set_uint("sort_store", true); p.set_bool("completion", true); model_evaluator evaluator(*(md.get()), p); evaluator.set_expand_array_equalities(false); scoped_rlimit _rlimit(m().limit(), 0); cancel_eh eh(m().limit()); expr_ref r(m()); scoped_ctrl_c ctrlc(eh); for (auto kd : m_psort_decls) { symbol const & k = kd.m_key; psort_decl * v = kd.m_value; if (v->is_user_decl()) { SASSERT(!v->has_var_params()); IF_VERBOSE(12, verbose_stream() << "(model.completion " << k << ")\n"; ); ptr_vector param_sorts(v->get_num_params(), m().mk_bool_sort()); sort * srt = v->instantiate(*m_pmanager, param_sorts.size(), param_sorts.c_ptr()); if (!md->has_uninterpreted_sort(srt)) { expr * singleton = m().get_some_value(srt); md->register_usort(srt, 1, &singleton); } } } for (unsigned i = 0; i < md->get_num_functions(); i++) { func_decl * f = md->get_function(i); func_interp * fi = md->get_func_interp(f); IF_VERBOSE(12, verbose_stream() << "(model.completion " << f->get_name() << ")\n"; ); if (fi->is_partial()) { sort * range = f->get_range(); fi->set_else(m().get_some_value(range)); } } for (auto kd : m_func_decls) { symbol const & k = kd.m_key; func_decls & v = kd.m_value; IF_VERBOSE(12, verbose_stream() << "(model.completion " << k << ")\n"; ); for (unsigned i = 0; i < v.get_num_entries(); i++) { func_decl * f = v.get_entry(i); if (!md->has_interpretation(f)) { sort * range = f->get_range(); expr * some_val = m().get_some_value(range); if (f->get_arity() > 0) { func_interp * fi = alloc(func_interp, m(), f->get_arity()); fi->set_else(some_val); md->register_decl(f, fi); } else md->register_decl(f, some_val); } } } } /** \brief Check if the current model satisfies the quantifier free formulas. */ void cmd_context::validate_model() { model_ref md; if (!validate_model_enabled()) return; if (!is_model_available(md)) return; SASSERT(md.get() != 0); params_ref p; p.set_uint("max_degree", UINT_MAX); // evaluate algebraic numbers of any degree. p.set_uint("sort_store", true); p.set_bool("completion", true); model_evaluator evaluator(*(md.get()), p); evaluator.set_expand_array_equalities(false); contains_underspecified_op_proc contains_underspecified(m()); { scoped_rlimit _rlimit(m().limit(), 0); cancel_eh eh(m().limit()); expr_ref r(m()); scoped_ctrl_c ctrlc(eh); bool invalid_model = false; for (expr * a : assertions()) { if (is_ground(a)) { r = nullptr; evaluator(a, r); TRACE("model_validate", tout << "checking\n" << mk_ismt2_pp(a, m()) << "\nresult:\n" << mk_ismt2_pp(r, m()) << "\n";); if (m().is_true(r)) continue; analyze_failure(evaluator, a, true); IF_VERBOSE(11, model_smt2_pp(verbose_stream(), *this, *md, 0);); // The evaluator for array expressions is not complete // If r contains as_array/store/map/const expressions, then we do not generate the error. // TODO: improve evaluator for model expressions. // Note that, if "a" evaluates to false, then the error will be generated. if (has_quantifiers(r)) { continue; } try { for_each_expr(contains_underspecified, a); for_each_expr(contains_underspecified, r); } catch (const contains_underspecified_op_proc::found &) { continue; } TRACE("model_validate", model_smt2_pp(tout, *this, *md, 0);); invalid_model = true; } } if (invalid_model) { throw cmd_exception("an invalid model was generated"); } } } void cmd_context::analyze_failure(model_evaluator& ev, expr* a, bool expected_value) { expr* c = nullptr, *t = nullptr, *e = nullptr; if (m().is_not(a, e)) { analyze_failure(ev, e, !expected_value); return; } if (!expected_value && m().is_or(a)) { for (expr* arg : *to_app(a)) { if (ev.is_true(arg)) { analyze_failure(ev, arg, false); return; } } } if (expected_value && m().is_and(a)) { for (expr* arg : *to_app(a)) { if (ev.is_false(arg)) { analyze_failure(ev, arg, true); return; } } } if (expected_value && m().is_ite(a, c, t, e)) { if (ev.is_true(c) && ev.is_false(t)) { if (!m().is_true(c)) analyze_failure(ev, c, false); if (!m().is_false(t)) analyze_failure(ev, t, true); return; } if (ev.is_false(c) && ev.is_false(e)) { if (!m().is_false(c)) analyze_failure(ev, c, true); if (!m().is_false(e)) analyze_failure(ev, e, true); return; } } if (!expected_value && m().is_ite(a, c, t, e)) { if (ev.is_true(c) && ev.is_true(t)) { if (!m().is_true(c)) analyze_failure(ev, c, false); if (!m().is_true(t)) analyze_failure(ev, t, false); return; } if (ev.is_false(c) && ev.is_true(e)) { if (!m().is_false(c)) analyze_failure(ev, c, true); if (!m().is_true(e)) analyze_failure(ev, e, false); return; } } IF_VERBOSE(10, verbose_stream() << "model check failed on: " << " " << mk_pp(a, m()) << "\n";); IF_VERBOSE(10, verbose_stream() << "expected value: " << (expected_value?"true":"false") << "\n";); IF_VERBOSE(10, display_detailed_analysis(verbose_stream(), ev, a)); } void cmd_context::display_detailed_analysis(std::ostream& out, model_evaluator& ev, expr* e) { ptr_vector es; es.push_back(e); expr_mark visited; for (unsigned i = 0; i < es.size(); ++i) { e = es[i]; if (visited.is_marked(e)) { continue; } visited.mark(e, true); expr_ref val = ev(e); out << "#" << e->get_id() << ": " << mk_bounded_pp(e, m(), 1) << " " << val << "\n"; if (is_app(e)) { for (expr* arg : *to_app(e)) { es.push_back(arg); } } } } 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); m_solver = (*m_solver_factory)(m(), p, proofs_enabled, models_enabled, unsat_core_enabled, m_logic); } void cmd_context::set_solver_factory(solver_factory * f) { m_solver_factory = f; m_check_sat_result = nullptr; if (has_manager() && f != nullptr) { mk_solver(); // assert formulas and create scopes in the new solver. unsigned lim = 0; for (scope& s : m_scopes) { for (unsigned i = lim; i < s.m_assertions_lim; i++) { m_solver->assert_expr(m_assertions[i]); } lim = s.m_assertions_lim; m_solver->push(); } unsigned sz = m_assertions.size(); for (unsigned i = lim; i < sz; i++) { m_solver->assert_expr(m_assertions[i]); } } } void cmd_context::display_statistics(bool show_total_time, double total_time) { statistics st; if (show_total_time) st.update("total time", total_time); st.update("time", get_seconds()); get_memory_statistics(st); get_rlimit_statistics(m().limit(), st); if (m_check_sat_result) { m_check_sat_result->collect_statistics(st); } else if (m_solver) { m_solver->collect_statistics(st); } else if (m_opt) { m_opt->collect_statistics(st); } st.display_smt2(regular_stream()); } void cmd_context::display_assertions() { if (!m_interactive_mode) throw cmd_exception("command is only available in interactive mode, use command (set-option :interactive-mode true)"); regular_stream() << "("; bool first = true; for (std::string const& s : m_assertion_strings) { if (first) first = false; else regular_stream() << "\n "; regular_stream() << s; } regular_stream() << ")" << std::endl; } bool cmd_context::is_model_available(model_ref& md) const { if (produce_models() && has_manager() && (cs_state() == css_sat || cs_state() == css_unknown)) { get_check_sat_result()->get_model(md); params_ref p; if (md.get()) md->updt_params(p); complete_model(md); return md.get() != nullptr; } return false; } format_ns::format * cmd_context::pp(sort * s) const { TRACE("cmd_context", tout << "pp(sort * s), s: " << mk_pp(s, m()) << "\n";); return pm().pp(s); } cmd_context::pp_env & cmd_context::get_pp_env() const { if (m_pp_env.get() == nullptr) { const_cast(this)->m_pp_env = alloc(pp_env, *const_cast(this)); } return *(m_pp_env.get()); } void cmd_context::pp(expr * n, unsigned num_vars, char const * var_prefix, format_ns::format_ref & r, sbuffer & var_names) const { mk_smt2_format(n, get_pp_env(), params_ref(), num_vars, var_prefix, r, var_names); } void cmd_context::pp(expr * n, format_ns::format_ref & r) const { sbuffer buf; pp(n, 0, nullptr, r, buf); } void cmd_context::pp(func_decl * f, format_ns::format_ref & r) const { mk_smt2_format(f, get_pp_env(), params_ref(), r, "declare-fun"); } void cmd_context::display(std::ostream & out, sort * s, unsigned indent) const { format_ns::format_ref f(format_ns::fm(m())); f = pp(s); if (indent > 0) f = format_ns::mk_indent(m(), indent, f); ::pp(out, f.get(), m()); } void cmd_context::display(std::ostream & out, expr * n, unsigned indent, unsigned num_vars, char const * var_prefix, sbuffer & var_names) const { format_ns::format_ref f(format_ns::fm(m())); pp(n, num_vars, var_prefix, f, var_names); if (indent > 0) f = format_ns::mk_indent(m(), indent, f); ::pp(out, f.get(), m()); } void cmd_context::display(std::ostream & out, expr * n, unsigned indent) const { sbuffer buf; display(out, n, indent, 0, nullptr, buf); } void cmd_context::display(std::ostream & out, func_decl * d, unsigned indent) const { format_ns::format_ref f(format_ns::fm(m())); pp(d, f); if (indent > 0) f = format_ns::mk_indent(m(), indent, f); ::pp(out, f.get(), m()); } void cmd_context::dump_assertions(std::ostream & out) const { for (expr * e : m_assertions) { display(out, e); out << std::endl; } } void cmd_context::display_smt2_benchmark(std::ostream & out, unsigned num, expr * const * assertions, symbol const & logic) const { if (logic != symbol::null) out << "(set-logic " << logic << ")" << std::endl; // collect uninterpreted function declarations decl_collector decls(m()); for (unsigned i = 0; i < num; i++) { decls.visit(assertions[i]); } // TODO: display uninterpreted sort decls, and datatype decls. for (func_decl* f : decls.get_func_decls()) { display(out, f); out << std::endl; } for (unsigned i = 0; i < num; i++) { out << "(assert "; display(out, assertions[i], 8); out << ")" << std::endl; } out << "(check-sat)" << std::endl; } void cmd_context::slow_progress_sample() { SASSERT(m_solver); statistics st; regular_stream() << "(progress\n"; m_solver->collect_statistics(st); st.display_smt2(regular_stream()); svector labels; m_solver->get_labels(labels); regular_stream() << "(labels"; for (symbol const& s : labels) { regular_stream() << " " << s; } regular_stream() << "))" << std::endl; } void cmd_context::fast_progress_sample() { } cmd_context::dt_eh::dt_eh(cmd_context & owner): m_owner(owner), m_dt_util(owner.m()) { } cmd_context::dt_eh::~dt_eh() { } void cmd_context::dt_eh::operator()(sort * dt, pdecl* pd) { TRACE("new_dt_eh", tout << "new datatype: "; m_owner.pm().display(tout, dt); tout << "\n";); for (func_decl * c : *m_dt_util.get_datatype_constructors(dt)) { TRACE("new_dt_eh", tout << "new constructor: " << c->get_name() << "\n";); m_owner.insert(c); func_decl * r = m_dt_util.get_constructor_recognizer(c); m_owner.insert(r); // TRACE("new_dt_eh", tout << "new recognizer: " << r->get_name() << "\n";); for (func_decl * a : *m_dt_util.get_constructor_accessors(c)) { TRACE("new_dt_eh", tout << "new accessor: " << a->get_name() << "\n";); m_owner.insert(a); } } if (!m_owner.m_scopes.empty() && !m_owner.m_global_decls) { m_owner.pm().inc_ref(pd); m_owner.m_psort_inst_stack.push_back(pd); } } std::ostream & operator<<(std::ostream & out, cmd_context::status st) { switch (st) { case cmd_context::UNSAT: out << "unsat"; break; case cmd_context::SAT: out << "sat"; break; default: out << "unknown"; break; } return out; } z3-z3-4.8.7/src/cmd_context/cmd_context.h000066400000000000000000000523221356505360400202110ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: cmd_context.h Abstract: Ultra-light command context. It provides a generic command pluging infrastructure. A command context also provides names (aka symbols) to Z3 objects. These names are used to reference Z3 objects in commands. Author: Leonardo (leonardo) 2011-03-01 Notes: --*/ #ifndef CMD_CONTEXT_H_ #define CMD_CONTEXT_H_ #include #include #include "util/stopwatch.h" #include "util/cmd_context_types.h" #include "util/event_handler.h" #include "util/sexpr.h" #include "util/dictionary.h" #include "util/scoped_ptr_vector.h" #include "ast/ast.h" #include "ast/ast_printer.h" #include "ast/datatype_decl_plugin.h" #include "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" #include "cmd_context/pdecl.h" #include "cmd_context/tactic_manager.h" #include "cmd_context/check_logic.h" #include "cmd_context/context_params.h" class func_decls { func_decl * m_decls; bool signatures_collide(func_decl* f, func_decl* g) const; bool signatures_collide(unsigned n, sort*const* domain, sort* range, func_decl* g) const; public: func_decls():m_decls(nullptr) {} func_decls(ast_manager & m, func_decl * f); void finalize(ast_manager & m); bool contains(func_decl * f) const; bool contains(unsigned n, sort* const* domain, sort* range) const; bool insert(ast_manager & m, func_decl * f); void erase(ast_manager & m, func_decl * f); bool more_than_one() const; bool clash(func_decl * f) const; bool empty() const { return m_decls == nullptr; } func_decl * first() const; func_decl * find(unsigned arity, sort * const * domain, sort * range) const; func_decl * find(ast_manager & m, unsigned num_args, expr * const * args, sort * range) const; unsigned get_num_entries() const; func_decl * get_entry(unsigned inx); }; struct macro_decl { ptr_vector m_domain; expr* m_body; macro_decl(unsigned arity, sort *const* domain, expr* body): m_domain(arity, domain), m_body(body) {} void dec_ref(ast_manager& m) { m.dec_ref(m_body); } }; class macro_decls { vector* m_decls; public: macro_decls() { m_decls = nullptr; } void finalize(ast_manager& m); bool insert(ast_manager& m, unsigned arity, sort *const* domain, expr* body); expr* find(unsigned arity, sort *const* domain) const; void erase_last(ast_manager& m); vector::iterator begin() const { return m_decls->begin(); } vector::iterator end() const { return m_decls->end(); } }; /** \brief Generic wrapper. */ class object_ref { unsigned m_ref_count; public: object_ref():m_ref_count(0) {} virtual ~object_ref() {} virtual void finalize(cmd_context & ctx) = 0; void inc_ref(cmd_context & ctx) { m_ref_count++; } void dec_ref(cmd_context & ctx) { SASSERT(m_ref_count > 0); m_ref_count--; if (m_ref_count == 0) { finalize(ctx); dealloc(this); } } virtual char const * kind() const = 0; }; class ast_object_ref : public object_ref { ast * m_ast; public: ast_object_ref(cmd_context & ctx, ast * a); void finalize(cmd_context & ctx) override; ast * get_ast() const { return m_ast; } static char const * cls_kind() { return "AST"; } char const * kind() const override { return cls_kind(); } }; class stream_ref { std::string m_default_name; std::ostream & m_default; std::string m_name; std::ostream * m_stream; bool m_owner; public: stream_ref(const std::string& n, std::ostream & d):m_default_name(n), m_default(d), m_name(n), m_stream(&d), m_owner(false) {} ~stream_ref() { reset(); } void set(char const * name); void set(std::ostream& strm); void reset(); std::ostream & operator*() { return *m_stream; } char const * name() const { return m_name.c_str(); } }; struct builtin_decl { family_id m_fid; decl_kind m_decl; builtin_decl * m_next; builtin_decl():m_fid(null_family_id), m_decl(0), m_next(nullptr) {} builtin_decl(family_id fid, decl_kind k, builtin_decl * n = nullptr):m_fid(fid), m_decl(k), m_next(n) {} }; class opt_wrapper : public check_sat_result { public: virtual bool empty() = 0; virtual void push() = 0; virtual void pop(unsigned n) = 0; virtual lbool optimize(expr_ref_vector const& asms) = 0; virtual void set_hard_constraints(expr_ref_vector const & hard) = 0; virtual void display_assignment(std::ostream& out) = 0; virtual bool is_pareto() = 0; virtual void set_logic(symbol const& s) = 0; virtual void get_box_model(model_ref& mdl, unsigned index) = 0; virtual void updt_params(params_ref const& p) = 0; }; class cmd_context : public progress_callback, public tactic_manager, public ast_printer_context { public: enum status { UNSAT, SAT, UNKNOWN }; enum check_sat_state { css_unsat, css_sat, css_unknown, css_clear }; typedef std::pair macro; struct scoped_watch { cmd_context & m_ctx; public: scoped_watch(cmd_context & ctx):m_ctx(ctx) { m_ctx.m_watch.reset(); m_ctx.m_watch.start(); } ~scoped_watch() { m_ctx.m_watch.stop(); } }; protected: context_params m_params; bool m_main_ctx; symbol m_logic; bool m_interactive_mode; bool m_global_decls; bool m_print_success; unsigned m_random_seed; bool m_produce_unsat_cores; bool m_produce_unsat_assumptions; bool m_produce_assignments; status m_status; bool m_numeral_as_real; bool m_ignore_check; // used by the API to disable check-sat() commands when parsing SMT 2.0 files. bool m_processing_pareto; // used when re-entering check-sat for pareto front. bool m_exit_on_error; static std::ostringstream g_error_stream; generic_model_converter_ref m_mc0; ast_manager * m_manager; bool m_own_manager; bool m_manager_initialized; pdecl_manager * m_pmanager; sexpr_manager * m_sexpr_manager; check_logic m_check_logic; stream_ref m_regular; stream_ref m_diagnostic; dictionary m_cmds; dictionary m_builtin_decls; scoped_ptr_vector m_extra_builtin_decls; // make sure that dynamically allocated builtin_decls are deleted dictionary m_object_refs; // anything that can be named. dictionary m_user_tactic_decls; dictionary m_func_decls; obj_map m_func_decl2alias; dictionary m_psort_decls; dictionary m_macros; // the following fields m_func_decls_stack, m_psort_decls_stack and m_exprs_stack are used when m_global_decls == false typedef std::pair sf_pair; svector m_func_decls_stack; svector m_psort_decls_stack; svector m_macros_stack; ptr_vector m_psort_inst_stack; // ptr_vector m_aux_pdecls; ptr_vector m_assertions; std::vector m_assertion_strings; ptr_vector m_assertion_names; // named assertions are represented using boolean variables. struct scope { unsigned m_func_decls_stack_lim; unsigned m_psort_decls_stack_lim; unsigned m_macros_stack_lim; unsigned m_aux_pdecls_lim; unsigned m_psort_inst_stack_lim; // only m_assertions_lim is relevant when m_global_decls = true unsigned m_assertions_lim; }; svector m_scopes; scoped_ptr m_solver_factory; ref m_solver; ref m_check_sat_result; ref m_opt; stopwatch m_watch; class dt_eh : public new_datatype_eh { cmd_context & m_owner; datatype_util m_dt_util; public: dt_eh(cmd_context & owner); ~dt_eh() override; void operator()(sort * dt, pdecl* pd) override; }; friend class dt_eh; scoped_ptr m_dt_eh; class pp_env; friend class pp_env; scoped_ptr m_pp_env; pp_env & get_pp_env() const; void register_builtin_sorts(decl_plugin * p); void register_builtin_ops(decl_plugin * p); void load_plugin(symbol const & name, bool install_names, svector& fids); void init_manager_core(bool new_manager); void init_manager(); void init_external_manager(); void reset_cmds(); void finalize_cmds(); void add_declared_functions(model& mdl); void restore_func_decls(unsigned old_sz); void restore_psort_decls(unsigned old_sz); void restore_macros(unsigned old_sz); void restore_aux_pdecls(unsigned old_sz); void restore_assertions(unsigned old_sz); void restore_psort_inst(unsigned old_sz); void erase_func_decl_core(symbol const & s, func_decl * f); void erase_psort_decl_core(symbol const & s); bool logic_has_arith() const; bool logic_has_bv() const; bool logic_has_pb() const; bool logic_has_seq() const; bool logic_has_array() const; bool logic_has_datatype() const; bool logic_has_fpa() const; bool logic_has_recfun() const; void print_unsupported_msg() { regular_stream() << "unsupported" << std::endl; } void print_unsupported_info(symbol const& s, int line, int pos) { if (s != symbol::null) diagnostic_stream() << "; " << s << " line: " << line << " position: " << pos << std::endl;} void mk_solver(); bool contains_func_decl(symbol const& s, unsigned n, sort* const* domain, sort* range) const; bool contains_macro(symbol const& s) const; bool contains_macro(symbol const& s, func_decl* f) const; bool contains_macro(symbol const& s, unsigned arity, sort *const* domain) const; void insert_macro(symbol const& s, unsigned arity, sort*const* domain, expr* t); void erase_macro(symbol const& s); bool macros_find(symbol const& s, unsigned n, expr*const* args, expr*& t) const; recfun::decl::plugin& get_recfun_plugin(); public: cmd_context(bool main_ctx = true, ast_manager * m = nullptr, symbol const & l = symbol::null); ~cmd_context() override; void set_cancel(bool f); context_params & params() { return m_params; } solver_factory &get_solver_factory() { return *m_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. bool set_logic(symbol const & s); bool has_logic() const { return m_logic != symbol::null; } symbol const & get_logic() const { return m_logic; } bool is_logic(char const * l_name) const { return has_logic() && strcmp(m_logic.bare_str(), l_name) == 0; } bool numeral_as_real() const { return m_numeral_as_real; } void set_numeral_as_real(bool f) { m_numeral_as_real = f; } void set_interactive_mode(bool flag) { m_interactive_mode = flag; } void set_ignore_check(bool flag) { m_ignore_check = flag; } bool ignore_check() const { return m_ignore_check; } void set_exit_on_error(bool flag) { m_exit_on_error = flag; } bool exit_on_error() const { return m_exit_on_error; } bool interactive_mode() const { return m_interactive_mode; } void set_print_success(bool flag) { m_print_success = flag; } bool print_success_enabled() const { return m_print_success; } void print_success() { if (print_success_enabled()) regular_stream() << "success" << std::endl; } void print_unsupported(symbol const & s, int line, int pos) { print_unsupported_msg(); print_unsupported_info(s, line, pos); } bool global_decls() const { return m_global_decls; } void set_global_decls(bool flag) { SASSERT(!has_manager()); m_global_decls = flag; } unsigned random_seed() const { return m_random_seed; } void set_random_seed(unsigned s) { m_random_seed = s; } bool produce_models() const; bool produce_proofs() 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_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; } void set_produce_assignments(bool flag) { m_produce_assignments = flag; } void set_status(status st) { m_status = st; } status get_status() const { return m_status; } std::string reason_unknown() const; bool has_manager() const { return m_manager != nullptr; } ast_manager & m() const { const_cast(this)->init_manager(); return *m_manager; } ast_manager & get_ast_manager() override { return m(); } pdecl_manager & pm() const { if (!m_pmanager) const_cast(this)->init_manager(); return *m_pmanager; } 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_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; void complete_model(model_ref& mdl) const; void validate_model(); void analyze_failure(model_evaluator& ev, expr* e, bool expected_value); void display_detailed_analysis(std::ostream& out, model_evaluator& ev, expr* e); void display_model(model_ref& mdl); void register_plugin(symbol const & name, decl_plugin * p, bool install_names); bool is_func_decl(symbol const & s) const; bool is_sort_decl(symbol const& s) const { return m_psort_decls.contains(s); } void insert(cmd * c); void insert(symbol const & s, func_decl * f); void insert(func_decl * f) { insert(f->get_name(), f); } void insert(symbol const & s, psort_decl * p); void insert(psort_decl * p) { insert(p->get_name(), p); } void insert(symbol const & s, unsigned arity, sort *const* domain, expr * t); void insert(symbol const & s, object_ref *); void insert(tactic_cmd * c) { tactic_manager::insert(c); } void insert(probe_info * p) { tactic_manager::insert(p); } void insert_user_tactic(symbol const & s, sexpr * d); void insert_aux_pdecl(pdecl * p); void model_add(symbol const & s, unsigned arity, sort *const* domain, expr * t); void model_del(func_decl* f); void insert_rec_fun(func_decl* f, expr_ref_vector const& binding, svector const& ids, expr* e); void insert_rec_fun_as_axiom(func_decl* f, expr_ref_vector const& binding, svector const& ids, expr* e); func_decl * find_func_decl(symbol const & s) const; func_decl * find_func_decl(symbol const & s, unsigned num_indices, unsigned const * indices, unsigned arity, sort * const * domain, sort * range) const; recfun::promise_def decl_rec_fun(const symbol &name, unsigned int arity, sort *const *domain, sort *range); psort_decl * find_psort_decl(symbol const & s) const; cmd * find_cmd(symbol const & s) const; sexpr * find_user_tactic(symbol const & s) const; object_ref * find_object_ref(symbol const & s) const; void mk_const(symbol const & s, expr_ref & result) const; void mk_app(symbol const & s, unsigned num_args, expr * const * args, unsigned num_indices, parameter const * indices, sort * range, expr_ref & r) const; void erase_cmd(symbol const & s); void erase_func_decl(symbol const & s); void erase_func_decl(symbol const & s, func_decl * f); void erase_func_decl(func_decl * f) { erase_func_decl(f->get_name(), f); } void erase_psort_decl(symbol const & s); void erase_object_ref(symbol const & s); void erase_user_tactic(symbol const & s); void reset_func_decls(); void reset_psort_decls(); void reset_macros(); void reset_object_refs(); void reset_user_tactics(); void set_regular_stream(char const * name) { m_regular.set(name); } void set_regular_stream(std::ostream& out) { m_regular.set(out); } void set_diagnostic_stream(std::ostream& out) { m_diagnostic.set(out); } void set_diagnostic_stream(char const * name); std::ostream & regular_stream() override { return *m_regular; } std::ostream & diagnostic_stream() override { return *m_diagnostic; } char const * get_regular_stream_name() const { return m_regular.name(); } char const * get_diagnostic_stream_name() const { return m_diagnostic.name(); } typedef dictionary::iterator cmd_iterator; cmd_iterator begin_cmds() const { return m_cmds.begin(); } cmd_iterator end_cmds() const { return m_cmds.end(); } typedef dictionary::iterator user_tactic_iterator; user_tactic_iterator begin_user_tactics() const { return m_user_tactic_decls.begin(); } user_tactic_iterator end_user_tactics() const { return m_user_tactic_decls.end(); } void display_assertions(); void display_statistics(bool show_total_time = false, double total_time = 0.0); void display_dimacs(); void reset(bool finalize = false); void assert_expr(expr * t); void assert_expr(symbol const & name, expr * t); void push_assert_string(std::string const & s) { SASSERT(m_interactive_mode); m_assertion_strings.push_back(s); } void push(); void push(unsigned n); void pop(unsigned n); void check_sat(unsigned num_assumptions, expr * const * assumptions); void get_consequences(expr_ref_vector const& assumptions, expr_ref_vector const& vars, expr_ref_vector & conseq); void reset_assertions(); // display the result produced by a check-sat or check-sat-using commands in the regular stream void display_sat_result(lbool r); // check if result produced by check-sat or check-sat-using matches the known status void validate_check_sat_result(lbool r); unsigned num_scopes() const { return m_scopes.size(); } dictionary const & get_macros() const { return m_macros; } model_converter* get_model_converter() { return m_mc0.get(); } bool is_model_available(model_ref& md) const; double get_seconds() const { return m_watch.get_seconds(); } ptr_vector const& assertions() const { return m_assertions; } ptr_vector const& assertion_names() const { return m_assertion_names; } /** \brief Hack: consume assertions if there are no scopes. This method is useful for reducing memory consumption in huge benchmarks were incrementality is not an issue. */ bool consume_assertions() { if (num_scopes() > 0) return false; restore_assertions(0); return true; } format_ns::format * pp(sort * s) const; void pp(sort * s, format_ns::format_ref & r) const override { r = pp(s); } void pp(func_decl * f, format_ns::format_ref & r) const override; void pp(expr * n, unsigned num_vars, char const * var_prefix, format_ns::format_ref & r, sbuffer & var_names) const override; void pp(expr * n, format_ns::format_ref & r) const override; void display(std::ostream & out, sort * s, unsigned indent = 0) const override; void display(std::ostream & out, expr * n, unsigned indent, unsigned num_vars, char const * var_prefix, sbuffer & var_names) const override; void display(std::ostream & out, expr * n, unsigned indent = 0) const override; void display(std::ostream & out, func_decl * f, unsigned indent = 0) const override; // dump assertions in out using the pretty printer. void dump_assertions(std::ostream & out) const; // display assertions as a SMT2 benchmark. void display_smt2_benchmark(std::ostream & out, unsigned num, expr * const * assertions, symbol const & logic = symbol::null) const; void slow_progress_sample() override; void fast_progress_sample() override; }; 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 z3-z3-4.8.7/src/cmd_context/cmd_context_to_goal.cpp000066400000000000000000000025161356505360400222500ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: cmd_context_to_goal.cpp Abstract: Procedure for copying the assertions in the command context to a goal object. Author: Leonardo (leonardo) 2012-10-21 Notes: --*/ #include "cmd_context/cmd_context.h" #include "tactic/goal.h" /** \brief Assert expressions from ctx into t. */ void assert_exprs_from(cmd_context const & ctx, goal & t) { if (ctx.produce_proofs() && ctx.produce_unsat_cores()) throw cmd_exception("Frontend does not support simultaneous generation of proofs and unsat cores"); ast_manager & m = t.m(); bool proofs_enabled = t.proofs_enabled(); if (ctx.produce_unsat_cores()) { ptr_vector::const_iterator it = ctx.assertions().begin(); ptr_vector::const_iterator end = ctx.assertions().end(); ptr_vector::const_iterator it2 = ctx.assertion_names().begin(); SASSERT(ctx.assertions().size() == ctx.assertion_names().size()); for (; it != end; ++it, ++it2) { t.assert_expr(*it, proofs_enabled ? m.mk_asserted(*it) : nullptr, m.mk_leaf(*it2)); } } else { for (expr * e : ctx.assertions()) { t.assert_expr(e, proofs_enabled ? m.mk_asserted(e) : nullptr, nullptr); } SASSERT(ctx.assertion_names().empty()); } } z3-z3-4.8.7/src/cmd_context/cmd_context_to_goal.h000066400000000000000000000005651356505360400217170ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: cmd_context_to_goal.h Abstract: Procedure for copying the assertions in the command context to a goal object. Author: Leonardo (leonardo) 2012-10-21 Notes: --*/ #ifndef CMD_CONTEXT_TO_GOAL_H_ #define CMD_CONTEXT_TO_GOAL_H_ void assert_exprs_from(cmd_context const & ctx, goal & t); #endif z3-z3-4.8.7/src/cmd_context/cmd_util.cpp000066400000000000000000000015551356505360400200370ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: cmd_util.cpp Abstract: Macros for defining new SMT2 front-end cmds. Author: Leonardo (leonardo) 2011-04-01 Notes: --*/ #include "cmd_context/cmd_context.h" ast * get_ast_ref(cmd_context & ctx, symbol const & v) { object_ref * r = ctx.find_object_ref(v); SASSERT(r != 0); if (r->kind() != ast_object_ref::cls_kind()) throw cmd_exception("global variable does not reference an AST"); return static_cast(r)->get_ast(); } expr * get_expr_ref(cmd_context & ctx, symbol const & v) { ast * r = get_ast_ref(ctx, v); if (!is_expr(r)) throw cmd_exception("global variable does not reference a term"); return to_expr(r); } void store_expr_ref(cmd_context & ctx, symbol const & v, expr * t) { ctx.insert(v, alloc(ast_object_ref, ctx, t)); } z3-z3-4.8.7/src/cmd_context/cmd_util.h000066400000000000000000000070311356505360400174770ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: cmd_util.h Abstract: Macros for defining new SMT2 front-end cmds. Author: Leonardo (leonardo) 2011-04-01 Notes: --*/ #ifndef CMD_UTIL_H_ #define CMD_UTIL_H_ #define ATOMIC_CMD(CLS_NAME, NAME, DESCR, ACTION) \ class CLS_NAME : public cmd { \ public: \ CLS_NAME():cmd(NAME) {} \ virtual char const * get_usage() const { return 0; } \ virtual char const * get_descr(cmd_context & ctx) const { return DESCR; } \ virtual unsigned get_arity() const { return 0; } \ virtual void execute(cmd_context & ctx) { ACTION } \ }; #define UNARY_CMD(CLS_NAME, NAME, USAGE, DESCR, ARG_KIND, ARG_TYPE, ACTION) \ class CLS_NAME : public cmd { \ public: \ CLS_NAME():cmd(NAME) {} \ virtual char const * get_usage() const { return USAGE; } \ virtual char const * get_descr(cmd_context & ctx) const { return DESCR; } \ virtual unsigned get_arity() const { return 1; } \ virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { return ARG_KIND; } \ virtual void set_next_arg(cmd_context & ctx, ARG_TYPE arg) { ACTION } \ } // Macro for creating commands where the first argument is a symbol // The second argument cannot be a symbol #define BINARY_SYM_CMD(CLS_NAME, NAME, USAGE, DESCR, ARG_KIND, ARG_TYPE, ACTION) \ class CLS_NAME : public cmd { \ symbol m_sym; \ public: \ CLS_NAME():cmd(NAME) {} \ virtual char const * get_usage() const { return USAGE; } \ virtual char const * get_descr(cmd_context & ctx) const { return DESCR; } \ virtual unsigned get_arity() const { return 2; } \ virtual void prepare(cmd_context & ctx) { m_sym = symbol::null; } \ virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { \ return m_sym == symbol::null ? CPK_SYMBOL : ARG_KIND; \ } \ virtual void set_next_arg(cmd_context & ctx, symbol const & s) { m_sym = s; } \ virtual void set_next_arg(cmd_context & ctx, ARG_TYPE arg) { ACTION } \ }; class ast; class expr; class symbol; class cmd_context; /** \brief Return the AST that is referenced by the global variable v in the given command context. */ ast * get_ast_ref(cmd_context & ctx, symbol const & v); /** \brief Return the expression that is referenced by the global variable v in the given command context. */ expr * get_expr_ref(cmd_context & ctx, symbol const & v); /** \brief Store t in the global variable v. */ void store_expr_ref(cmd_context & ctx, symbol const & v, expr * t); #endif z3-z3-4.8.7/src/cmd_context/context_params.cpp000066400000000000000000000167011356505360400212650ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: context_params.cpp Abstract: Goodies for managing context parameters in the cmd_context and api_context Author: Leonardo (leonardo) 2012-12-01 Notes: --*/ #include "cmd_context/context_params.h" #include "util/gparams.h" #include "util/params.h" #include "ast/ast.h" #include "solver/solver.h" context_params::context_params() { m_unsat_core = false; m_model = true; m_model_validate = false; m_dump_models = false; m_auto_config = true; m_proof = false; m_trace = false; m_debug_ref_count = false; m_smtlib2_compliant = false; m_well_sorted_check = false; m_timeout = UINT_MAX; m_rlimit = 0; m_statistics = false; updt_params(); } void context_params::set_bool(bool & opt, char const * param, char const * value) { if (strcmp(value, "true") == 0) { opt = true; } else if (strcmp(value, "false") == 0) { opt = false; } else { std::stringstream strm; strm << "invalid value '" << value << "' for Boolean parameter '" << param << "'"; throw default_exception(strm.str()); } } void context_params::set_uint(unsigned & opt, char const * param, char const * value) { bool is_uint = true; size_t sz = strlen(value); for (unsigned i = 0; i < sz; i++) { if (!(value[i] >= '0' && value[i] <= '9')) is_uint = false; } if (is_uint) { long val = strtol(value, nullptr, 10); opt = static_cast(val); } else { std::stringstream strm; strm << "invalid value '" << value << "' for unsigned int parameter '" << param << "'"; throw default_exception(strm.str()); } } void context_params::set(char const * param, char const * value) { std::string p = param; unsigned n = static_cast(p.size()); for (unsigned i = 0; i < n; i++) { if (p[i] >= 'A' && p[i] <= 'Z') p[i] = p[i] - 'A' + 'a'; else if (p[i] == '-') p[i] = '_'; } if (p == "timeout") { set_uint(m_timeout, param, value); } else if (p == "rlimit") { set_uint(m_rlimit, param, value); } else if (p == "type_check" || p == "well_sorted_check") { set_bool(m_well_sorted_check, param, value); } else if (p == "auto_config") { set_bool(m_auto_config, param, value); } else if (p == "proof") { set_bool(m_proof, param, value); } else if (p == "model") { set_bool(m_model, param, value); } else if (p == "model_validate") { set_bool(m_model_validate, param, value); } else if (p == "dump_models") { set_bool(m_dump_models, param, value); } else if (p == "stats") { set_bool(m_statistics, param, value); } else if (p == "trace") { set_bool(m_trace, param, value); } else if (p == "trace_file_name") { m_trace_file_name = value; } else if (p == "dot_proof_file") { m_dot_proof_file = value; } else if (p == "unsat_core") { set_bool(m_unsat_core, param, value); } else if (p == "debug_ref_count") { set_bool(m_debug_ref_count, param, value); } else if (p == "smtlib2_compliant") { set_bool(m_smtlib2_compliant, param, value); } else { param_descrs d; collect_param_descrs(d); std::stringstream strm; strm << "unknown parameter '" << p << "'\n"; strm << "Legal parameters are:\n"; d.display(strm, 2, false, false); throw default_exception(strm.str()); } } void context_params::updt_params() { updt_params(gparams::get_ref()); } void context_params::updt_params(params_ref const & p) { m_timeout = p.get_uint("timeout", m_timeout); m_rlimit = p.get_uint("rlimit", m_rlimit); m_well_sorted_check = p.get_bool("type_check", p.get_bool("well_sorted_check", m_well_sorted_check)); m_auto_config = p.get_bool("auto_config", m_auto_config); m_proof = p.get_bool("proof", m_proof); m_model = p.get_bool("model", m_model); m_model_validate = p.get_bool("model_validate", m_model_validate); m_dump_models = p.get_bool("dump_models", m_dump_models); m_trace = p.get_bool("trace", m_trace); m_trace_file_name = p.get_str("trace_file_name", "z3.log"); m_dot_proof_file = p.get_str("dot_proof_file", "proof.dot"); m_unsat_core = p.get_bool("unsat_core", m_unsat_core); m_debug_ref_count = p.get_bool("debug_ref_count", m_debug_ref_count); m_smtlib2_compliant = p.get_bool("smtlib2_compliant", m_smtlib2_compliant); m_statistics = p.get_bool("stats", m_statistics); } void context_params::collect_param_descrs(param_descrs & d) { 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"); d.insert("model_validate", CPK_BOOL, "validate models produced by solvers", "false"); d.insert("dump_models", CPK_BOOL, "dump models whenever check-sat returns sat", "false"); d.insert("trace", CPK_BOOL, "trace generation for VCC", "false"); d.insert("trace_file_name", CPK_STRING, "trace out file name (see option 'trace')", "z3.log"); d.insert("dot_proof_file", CPK_STRING, "file in which to output graphical proofs", "proof.dot"); d.insert("debug_ref_count", CPK_BOOL, "debug support for AST reference counting", "false"); d.insert("smtlib2_compliant", CPK_BOOL, "enable/disable SMT-LIB 2.0 compliance", "false"); d.insert("stats", CPK_BOOL, "enable/disable statistics", "false"); // statistics are hidden as they are controlled by the /st option. collect_solver_param_descrs(d); } void context_params::collect_solver_param_descrs(param_descrs & d) { d.insert("proof", CPK_BOOL, "proof generation, it must be enabled when the Z3 context is created", "false"); d.insert("model", CPK_BOOL, "model generation for solvers, this parameter can be overwritten when creating a solver", "true"); d.insert("unsat_core", CPK_BOOL, "unsat-core generation for solvers, this parameter can be overwritten when creating a solver, not every solver in Z3 supports unsat core generation", "false"); } params_ref context_params::merge_default_params(params_ref const & p) { if (!m_auto_config && !p.contains("auto_config")) { params_ref new_p = p; new_p.set_bool("auto_config", false); return new_p; } else { return p; } } void context_params::get_solver_params(ast_manager const & m, params_ref & p, bool & proofs_enabled, bool & models_enabled, bool & unsat_core_enabled) { proofs_enabled = m.proofs_enabled() && p.get_bool("proof", m_proof); models_enabled = p.get_bool("model", m_model); unsat_core_enabled = p.get_bool("unsat_core", m_unsat_core); p = merge_default_params(p); } ast_manager * context_params::mk_ast_manager() { ast_manager * r = alloc(ast_manager, m_proof ? PGM_ENABLED : PGM_DISABLED, m_trace ? m_trace_file_name.c_str() : nullptr); if (m_smtlib2_compliant) r->enable_int_real_coercions(false); if (m_debug_ref_count) r->debug_ref_count(); return r; } z3-z3-4.8.7/src/cmd_context/context_params.h000066400000000000000000000041341356505360400207270ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: context_params.h Abstract: Goodies for managing context parameters in the cmd_context and api_context Author: Leonardo (leonardo) 2012-12-01 Notes: --*/ #ifndef CONTEXT_PARAMS_H_ #define CONTEXT_PARAMS_H_ #include "util/params.h" class ast_manager; class context_params { void set_bool(bool & opt, char const * param, char const * value); void set_uint(unsigned & opt, char const * param, char const * value); unsigned m_rlimit; public: bool m_auto_config; bool m_proof; std::string m_dot_proof_file; bool m_interpolants; bool m_debug_ref_count; bool m_trace; std::string m_trace_file_name; bool m_well_sorted_check; bool m_model; bool m_model_validate; bool m_dump_models; bool m_unsat_core; bool m_smtlib2_compliant; // it must be here because it enable/disable the use of coercions in the ast_manager. unsigned m_timeout; bool m_statistics; unsigned rlimit() const { return m_rlimit; } context_params(); void set(char const * param, char const * value); void set_rlimit(unsigned lim) { m_rlimit = lim; } void updt_params(); void updt_params(params_ref const & p); static void collect_param_descrs(param_descrs & d); /* REG_PARAMS('context_params::collect_param_descrs') */ /** \brief Goodies for extracting parameters for creating a solver object. */ void get_solver_params(ast_manager const & m, params_ref & p, bool & proofs_enabled, bool & models_enabled, bool & unsat_core_enabled); static void collect_solver_param_descrs(param_descrs & d); /** \brief Include in p parameters derived from this context_params. These are parameters that are meaningful for tactics and solvers. Example: auto_config */ params_ref merge_default_params(params_ref const & p); /** \brief Create an AST manager using this configuration. */ ast_manager * mk_ast_manager(); }; #endif z3-z3-4.8.7/src/cmd_context/echo_tactic.cpp000066400000000000000000000035611356505360400205030ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: echo_tactic.h Abstract: Tactic and probe for dumping data. Author: Leonardo (leonardo) 2012-10-20 Notes: --*/ #include "tactic/tactic.h" #include "tactic/probe.h" #include "cmd_context/cmd_context.h" class echo_tactic : public skip_tactic { cmd_context & m_ctx; char const * m_msg; bool m_newline; public: echo_tactic(cmd_context & ctx, char const * msg, bool newline):m_ctx(ctx), m_msg(msg), m_newline(newline) {} void operator()(goal_ref const & in, goal_ref_buffer & result) override { m_ctx.regular_stream() << m_msg; if (m_newline) m_ctx.regular_stream() << std::endl; skip_tactic::operator()(in, result); } }; tactic * mk_echo_tactic(cmd_context & ctx, char const * msg, bool newline) { return alloc(echo_tactic, ctx, msg, newline); } class probe_value_tactic : public skip_tactic { cmd_context & m_ctx; char const * m_msg; probe * m_p; bool m_newline; public: probe_value_tactic(cmd_context & ctx, char const * msg, probe * p, bool newline):m_ctx(ctx), m_msg(msg), m_p(p), m_newline(newline) { SASSERT(m_p); m_p->inc_ref(); } ~probe_value_tactic() override { m_p->dec_ref(); } void operator()(goal_ref const & in, goal_ref_buffer & result) override { double val = (*m_p)(*(in.get())).get_value(); if (m_msg) m_ctx.diagnostic_stream() << m_msg << " "; m_ctx.diagnostic_stream() << val; if (m_newline) m_ctx.diagnostic_stream() << std::endl; skip_tactic::operator()(in, result); } }; tactic * mk_probe_value_tactic(cmd_context & ctx, char const * msg, probe * p, bool newline) { return alloc(probe_value_tactic, ctx, msg, p, newline); } z3-z3-4.8.7/src/cmd_context/echo_tactic.h000066400000000000000000000010311356505360400201360ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: echo_tactic.h Abstract: Tactic and probe for dumping data. Author: Leonardo (leonardo) 2012-10-20 Notes: --*/ #ifndef ECHO_TACTICS_H_ #define ECHO_TACTICS_H_ class cmd_context; class tactic; class probe; tactic * mk_echo_tactic(cmd_context & ctx, char const * msg, bool newline = true); // Display the value returned by p in the diagnostic_stream tactic * mk_probe_value_tactic(cmd_context & ctx, char const * msg, probe * p, bool newline = true); #endif z3-z3-4.8.7/src/cmd_context/eval_cmd.cpp000066400000000000000000000054551356505360400200140ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: eval_cmd.cpp Abstract: eval_cmd Author: Leonardo (leonardo) 2011-04-30 Notes: --*/ #include "cmd_context/cmd_context.h" #include "model/model_evaluator.h" #include "cmd_context/parametric_cmd.h" #include "util/scoped_timer.h" #include "util/scoped_ctrl_c.h" #include "util/cancel_eh.h" class eval_cmd : public parametric_cmd { expr * m_target; symbol m_last; public: eval_cmd():parametric_cmd("eval") {} char const * get_usage() const override { return " ( )*"; } char const * get_main_descr() const override { return "evaluate the given term in the current model."; } void init_pdescrs(cmd_context & ctx, param_descrs & p) override { model_evaluator::get_param_descrs(p); insert_timeout(p); p.insert("model_index", CPK_UINT, "(default: 0) index of model from box optimization objective"); } void prepare(cmd_context & ctx) override { parametric_cmd::prepare(ctx); m_target = nullptr; } cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { if (m_target == nullptr) return CPK_EXPR; return parametric_cmd::next_arg_kind(ctx); } void set_next_arg(cmd_context & ctx, expr * arg) override { m_target = arg; } void execute(cmd_context & ctx) override { model_ref md; if (!ctx.is_model_available(md)) throw cmd_exception("model is not available"); if (!m_target) throw cmd_exception("no arguments passed to eval"); unsigned index = m_params.get_uint("model_index", 0); if (index == 0 || !ctx.get_opt()) { // already have model. } else { ctx.get_opt()->get_box_model(md, index); } expr_ref r(ctx.m()); unsigned timeout = m_params.get_uint("timeout", UINT_MAX); unsigned rlimit = m_params.get_uint("rlimit", 0); // md->compress(); 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); scoped_timer timer(timeout, &eh); scoped_rlimit _rlimit(ctx.m().limit(), rlimit); cmd_context::scoped_watch sw(ctx); try { ev(m_target, r); } catch (model_evaluator_exception & ex) { ctx.regular_stream() << "(error \"evaluator failed: " << ex.msg() << "\")" << std::endl; return; } } ctx.display(ctx.regular_stream(), r.get()); ctx.regular_stream() << std::endl; } }; void install_eval_cmd(cmd_context & ctx) { ctx.insert(alloc(eval_cmd)); } z3-z3-4.8.7/src/cmd_context/eval_cmd.h000066400000000000000000000004171356505360400174520ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: eval_cmd.h Abstract: eval_cmd Author: Leonardo (leonardo) 2011-04-30 Notes: --*/ #ifndef EVAL_CMD_H_ #define EVAL_CMD_H_ class cmd_context; void install_eval_cmd(cmd_context & ctx); #endif z3-z3-4.8.7/src/cmd_context/extra_cmds/000077500000000000000000000000001356505360400176565ustar00rootroot00000000000000z3-z3-4.8.7/src/cmd_context/extra_cmds/CMakeLists.txt000066400000000000000000000002701356505360400224150ustar00rootroot00000000000000z3_add_component(extra_cmds SOURCES dbg_cmds.cpp polynomial_cmds.cpp subpaving_cmds.cpp COMPONENT_DEPENDENCIES arith_tactics cmd_context subpaving_tactic ) z3-z3-4.8.7/src/cmd_context/extra_cmds/dbg_cmds.cpp000066400000000000000000000472411356505360400221340ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: dbg_cmds.cpp Abstract: SMT2 front-end commands for debugging purposes. Author: Leonardo (leonardo) 2011-04-01 Notes: --*/ #include #include "cmd_context/cmd_context.h" #include "cmd_context/cmd_util.h" #include "ast/rewriter/rewriter.h" #include "ast/shared_occs.h" #include "ast/for_each_expr.h" #include "ast/rewriter/rewriter.h" #include "ast/rewriter/bool_rewriter.h" #include "ast/ast_lt.h" #include "cmd_context/simplify_cmd.h" #include "ast/ast_smt2_pp.h" #include "tactic/arith/bound_manager.h" #include "ast/used_vars.h" #include "ast/rewriter/var_subst.h" #include "ast/ast_util.h" #include "util/gparams.h" #include "qe/qe_mbp.h" #include "qe/qe_mbi.h" #include "qe/qe_term_graph.h" BINARY_SYM_CMD(get_quantifier_body_cmd, "dbg-get-qbody", " ", "store the body of the quantifier in the global variable ", CPK_EXPR, expr *, { if (!is_quantifier(arg)) throw cmd_exception("invalid command, term must be a quantifier"); store_expr_ref(ctx, m_sym, to_quantifier(arg)->get_expr()); }); BINARY_SYM_CMD(set_cmd, "dbg-set", " ", "store in the global variable ", CPK_EXPR, expr *, { store_expr_ref(ctx, m_sym, arg); }); UNARY_CMD(pp_var_cmd, "dbg-pp-var", "", "pretty print a global variable that references an AST", CPK_SYMBOL, symbol const &, { expr * t = get_expr_ref(ctx, arg); SASSERT(t != 0); ctx.display(ctx.regular_stream(), t); ctx.regular_stream() << std::endl; }); BINARY_SYM_CMD(shift_vars_cmd, "dbg-shift-vars", " ", "shift the free variables by in the term referenced by the global variable , the result is stored in ", CPK_UINT, unsigned, { expr * t = get_expr_ref(ctx, m_sym); expr_ref r(ctx.m()); var_shifter s(ctx.m()); s(t, arg, r); store_expr_ref(ctx, m_sym, r.get()); }); 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(size_cmd, "dbg-size", "", "return the size of the given term", CPK_EXPR, expr *, { ctx.regular_stream() << get_num_exprs(arg) << std::endl; }); class subst_cmd : public cmd { unsigned m_idx; expr * m_source; symbol m_target; ptr_vector m_subst; public: subst_cmd():cmd("dbg-subst") {} char const * get_usage() const override { return " (*) "; } char const * get_descr(cmd_context & ctx) const override { return "substitute the free variables in the AST referenced by using the ASTs referenced by *"; } unsigned get_arity() const override { return 3; } void prepare(cmd_context & ctx) override { m_idx = 0; m_source = nullptr; } cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { if (m_idx == 1) return CPK_SYMBOL_LIST; return CPK_SYMBOL; } void set_next_arg(cmd_context & ctx, symbol const & s) override { if (m_idx == 0) { m_source = get_expr_ref(ctx, s); } else { m_target = s; } m_idx++; } void set_next_arg(cmd_context & ctx, unsigned num, symbol const * s) override { m_subst.reset(); unsigned i = num; while (i > 0) { --i; m_subst.push_back(get_expr_ref(ctx, s[i])); } m_idx++; } void execute(cmd_context & ctx) override { beta_reducer p(ctx.m()); expr_ref r = p(m_source, m_subst.size(), m_subst.c_ptr()); store_expr_ref(ctx, m_target, r.get()); } }; UNARY_CMD(bool_rewriter_cmd, "dbg-bool-rewriter", "", "apply the Boolean rewriter to the given term", CPK_EXPR, expr *, { expr_ref t(ctx.m()); params_ref p; p.set_bool("flat", false); SASSERT(p.get_bool("flat", true) == false); bool_rewriter_star r(ctx.m(), p); r(arg, t); ctx.display(ctx.regular_stream(), t); ctx.regular_stream() << std::endl; }); UNARY_CMD(bool_frewriter_cmd, "dbg-bool-flat-rewriter", "", "apply the Boolean (flattening) rewriter to the given term", CPK_EXPR, expr *, { expr_ref t(ctx.m()); { params_ref p; p.set_bool("flat", true); bool_rewriter_star r(ctx.m(), p); r(arg, t); } ctx.display(ctx.regular_stream(), t); ctx.regular_stream() << std::endl; }); UNARY_CMD(elim_and_cmd, "dbg-elim-and", "", "apply the Boolean rewriter (eliminating AND operator and flattening) to the given term", CPK_EXPR, expr *, { expr_ref t(ctx.m()); { params_ref p; p.set_bool("flat", true); p.set_bool("elim_and", true); bool_rewriter_star r(ctx.m(), p); r(arg, t); } ctx.display(ctx.regular_stream(), t); ctx.regular_stream() << std::endl; }); class lt_cmd : public cmd { expr * m_t1; expr * m_t2; public: lt_cmd():cmd("dbg-lt") {} char const * get_usage() const override { return " "; } char const * get_descr(cmd_context & ctx) const override { return "return true if the first term is smaller than the second one in the internal Z3 total order on terms."; } unsigned get_arity() const override { return 2; } void prepare(cmd_context & ctx) override { m_t1 = nullptr; } cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { return CPK_EXPR; } void set_next_arg(cmd_context & ctx, expr * arg) override { if (m_t1 == nullptr) m_t1 = arg; else m_t2 = arg; } void execute(cmd_context & ctx) override { bool r = lt(m_t1, m_t2); ctx.regular_stream() << (r ? "true" : "false") << std::endl; } }; UNARY_CMD(some_value_cmd, "dbg-some-value", "", "retrieve some value of the given sort", CPK_SORT, sort *, { ast_manager & m = ctx.m(); expr_ref val(m); val = m.get_some_value(arg); ctx.display(ctx.regular_stream(), val); ctx.regular_stream() << std::endl; }); void tst_params(cmd_context & ctx) { params_ref p1; params_ref p2; p1.set_uint("val", 100); p2 = p1; SASSERT(p2.get_uint("val", 0) == 100); p2.set_uint("val", 200); SASSERT(p2.get_uint("val", 0) == 200); SASSERT(p1.get_uint("val", 0) == 100); p2 = p1; SASSERT(p2.get_uint("val", 0) == 100); SASSERT(p1.get_uint("val", 0) == 100); ctx.regular_stream() << "worked" << std::endl; } ATOMIC_CMD(params_cmd, "dbg-params", "test parameters", tst_params(ctx);); UNARY_CMD(translator_cmd, "dbg-translator", "", "test AST translator", CPK_EXPR, expr *, { ast_manager & m = ctx.m(); scoped_ptr m2 = alloc(ast_manager, m.proof_mode()); ast_translation proc(m, *m2); expr_ref s(m); expr_ref t(*m2); s = arg; t = proc(s.get()); ctx.regular_stream() << mk_ismt2_pp(s, m) << "\n--->\n" << mk_ismt2_pp(t, *m2) << std::endl; }); UNARY_CMD(sexpr_cmd, "dbg-sexpr", "", "display an s-expr", CPK_SEXPR, sexpr *, { arg->display(ctx.regular_stream()); ctx.regular_stream() << std::endl; }); #define GUARDED_CODE(CODE) try { CODE } catch (z3_error & ex) { throw ex; } catch (z3_exception & ex) { ctx.regular_stream() << "(error \"" << escaped(ex.msg()) << "\")" << std::endl; } UNARY_CMD(set_next_id, "dbg-set-next-id", "", "set the next expression id to be at least the given value", CPK_UINT, unsigned, { ctx.m().set_next_expr_id(arg); }); UNARY_CMD(used_vars_cmd, "dbg-used-vars", "", "test used_vars functor", CPK_EXPR, expr *, { used_vars proc; if (is_quantifier(arg)) arg = to_quantifier(arg)->get_expr(); proc(arg); ctx.regular_stream() << "(vars"; for (unsigned i = 0; i < proc.get_max_found_var_idx_plus_1(); i++) { sort * s = proc.get(i); ctx.regular_stream() << "\n (" << std::left << std::setw(6) << i << " "; if (s != 0) ctx.display(ctx.regular_stream(), s, 10); else ctx.regular_stream() << ""; ctx.regular_stream() << ")"; } ctx.regular_stream() << ")" << std::endl; }); UNARY_CMD(elim_unused_vars_cmd, "dbg-elim-unused-vars", "", "eliminate unused vars from a quantifier", CPK_EXPR, expr *, { if (!is_quantifier(arg)) { ctx.display(ctx.regular_stream(), arg); return; } expr_ref r = elim_unused_vars(ctx.m(), to_quantifier(arg), gparams::get_ref()); SASSERT(!is_quantifier(r) || !to_quantifier(r)->may_have_unused_vars()); ctx.display(ctx.regular_stream(), r); ctx.regular_stream() << std::endl; }); class instantiate_cmd_core : public cmd { protected: quantifier * m_q; ptr_vector m_args; public: instantiate_cmd_core(char const * name):cmd(name) {} char const * get_usage() const override { return " (*)"; } char const * get_descr(cmd_context & ctx) const override { return "instantiate the quantifier using the given expressions."; } unsigned get_arity() const override { return 2; } void prepare(cmd_context & ctx) override { m_q = nullptr; m_args.reset(); } cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { if (m_q == nullptr) return CPK_EXPR; else return CPK_EXPR_LIST; } void set_next_arg(cmd_context & ctx, expr * s) override { if (!is_quantifier(s)) throw cmd_exception("invalid command, quantifier expected."); m_q = to_quantifier(s); } void set_next_arg(cmd_context & ctx, unsigned num, expr * const * ts) override { if (num != m_q->get_num_decls()) throw cmd_exception("invalid command, mismatch between the number of quantified variables and the number of arguments."); unsigned i = num; while (i-- > 0) { sort * s = ctx.m().get_sort(ts[i]); if (s != m_q->get_decl_sort(i)) { std::ostringstream buffer; buffer << "invalid command, sort mismatch at position " << i; throw cmd_exception(buffer.str()); } } m_args.append(num, ts); } void execute(cmd_context & ctx) override { expr_ref r = instantiate(ctx.m(), m_q, m_args.c_ptr()); ctx.display(ctx.regular_stream(), r); ctx.regular_stream() << std::endl; } }; class instantiate_cmd : public instantiate_cmd_core { public: instantiate_cmd():instantiate_cmd_core("dbg-instantiate") {} }; class instantiate_nested_cmd : public instantiate_cmd_core { public: instantiate_nested_cmd():instantiate_cmd_core("dbg-instantiate-nested") {} char const * get_descr(cmd_context & ctx) const override { return "instantiate the quantifier nested in the outermost quantifier, this command is used to test the instantiation procedure with quantifiers that contain free variables."; } void set_next_arg(cmd_context & ctx, expr * s) override { instantiate_cmd_core::set_next_arg(ctx, s); if (!is_quantifier(m_q->get_expr())) throw cmd_exception("invalid command, nested quantifier expected"); m_q = to_quantifier(m_q->get_expr()); } }; class print_dimacs_cmd : public cmd { public: print_dimacs_cmd():cmd("display-dimacs") {} char const * get_usage() const override { return ""; } char const * get_descr(cmd_context & ctx) const override { return "print benchmark in DIMACS format"; } unsigned get_arity() const override { return 0; } void prepare(cmd_context & ctx) override {} void execute(cmd_context & ctx) override { ctx.display_dimacs(); } }; class mbp_cmd : public cmd { expr* m_fml; ptr_vector m_vars; public: mbp_cmd():cmd("mbp") {} char const * get_usage() const override { return " ()"; } char const * get_descr(cmd_context & ctx) const override { return "perform model based projection"; } unsigned get_arity() const override { return 2; } cmd_arg_kind next_arg_kind(cmd_context& ctx) const override { if (m_fml == nullptr) return CPK_EXPR; return CPK_EXPR_LIST; } void set_next_arg(cmd_context& ctx, expr * arg) override { m_fml = arg; } void set_next_arg(cmd_context & ctx, unsigned num, expr * const * ts) override { m_vars.append(num, ts); } void prepare(cmd_context & ctx) override { m_fml = nullptr; m_vars.reset(); } void execute(cmd_context & ctx) override { ast_manager& m = ctx.m(); app_ref_vector vars(m); model_ref mdl; if (!ctx.is_model_available(mdl) || !ctx.get_check_sat_result()) { throw cmd_exception("model is not available"); } for (expr* v : m_vars) { if (!is_uninterp_const(v)) { throw cmd_exception("invalid variable argument. Uninterpreted variable expected"); } vars.push_back(to_app(v)); } qe::mbp mbp(m); expr_ref fml(m_fml, m); mbp.spacer(vars, *mdl.get(), fml); ctx.regular_stream() << fml << "\n"; } }; class mbi_cmd : public cmd { expr* m_a; expr* m_b; ptr_vector m_vars; public: mbi_cmd():cmd("mbi") {} char const * get_usage() const override { return " (vars)"; } char const * get_descr(cmd_context & ctx) const override { return "perform model based interpolation"; } unsigned get_arity() const override { return 3; } cmd_arg_kind next_arg_kind(cmd_context& ctx) const override { if (m_a == nullptr) return CPK_EXPR; if (m_b == nullptr) return CPK_EXPR; return CPK_FUNC_DECL_LIST; } void set_next_arg(cmd_context& ctx, expr * arg) override { if (m_a == nullptr) m_a = arg; else m_b = arg; } void set_next_arg(cmd_context & ctx, unsigned num, func_decl * const * ts) override { m_vars.append(num, ts); } void prepare(cmd_context & ctx) override { m_a = nullptr; m_b = nullptr; m_vars.reset(); } void execute(cmd_context & ctx) override { ast_manager& m = ctx.m(); func_decl_ref_vector vars(m); for (func_decl* v : m_vars) { vars.push_back(v); } qe::interpolator mbi(m); expr_ref a(m_a, m); expr_ref b(m_b, m); expr_ref itp(m); solver_factory& sf = ctx.get_solver_factory(); params_ref p; solver_ref sA = sf(m, p, false /* no proofs */, true, true, symbol::null); solver_ref sB = sf(m, p, false /* no proofs */, true, true, symbol::null); sA->assert_expr(a); sB->assert_expr(b); qe::prop_mbi_plugin pA(sA.get()); qe::prop_mbi_plugin pB(sB.get()); pA.set_shared(vars); pB.set_shared(vars); lbool res = mbi.pingpong(pA, pB, itp); ctx.regular_stream() << res << " " << itp << "\n"; } }; class eufi_cmd : public cmd { expr* m_a; expr* m_b; ptr_vector m_vars; public: eufi_cmd():cmd("eufi") {} char const * get_usage() const override { return " (vars)"; } char const * get_descr(cmd_context & ctx) const override { return "perform model based interpolation"; } unsigned get_arity() const override { return 3; } cmd_arg_kind next_arg_kind(cmd_context& ctx) const override { if (m_a == nullptr) return CPK_EXPR; if (m_b == nullptr) return CPK_EXPR; return CPK_FUNC_DECL_LIST; } void set_next_arg(cmd_context& ctx, expr * arg) override { if (m_a == nullptr) m_a = arg; else m_b = arg; } void set_next_arg(cmd_context & ctx, unsigned num, func_decl * const * ts) override { m_vars.append(num, ts); } void prepare(cmd_context & ctx) override { m_a = nullptr; m_b = nullptr; m_vars.reset(); } void execute(cmd_context & ctx) override { ast_manager& m = ctx.m(); func_decl_ref_vector vars(m); for (func_decl* v : m_vars) { vars.push_back(v); } qe::interpolator mbi(m); expr_ref a(m_a, m); expr_ref b(m_b, m); expr_ref itp(m); solver_factory& sf = ctx.get_solver_factory(); params_ref p; solver_ref sA = sf(m, p, false /* no proofs */, true, true, symbol::null); solver_ref sB = sf(m, p, false /* no proofs */, true, true, symbol::null); solver_ref sNotA = sf(m, p, false /* no proofs */, true, true, symbol::null); solver_ref sNotB = sf(m, p, false /* no proofs */, true, true, symbol::null); sA->assert_expr(a); sB->assert_expr(b); qe::euf_arith_mbi_plugin pA(sA.get(), sNotA.get()); qe::prop_mbi_plugin pB(sB.get()); pA.set_shared(vars); pB.set_shared(vars); lbool res = mbi.pogo(pA, pB, itp); ctx.regular_stream() << res << " " << itp << "\n"; } }; class euf_project_cmd : public cmd { unsigned m_arg_index; ptr_vector m_lits; ptr_vector m_vars; public: euf_project_cmd():cmd("euf-project") {} char const * get_usage() const override { return "(exprs) (vars)"; } char const * get_descr(cmd_context & ctx) const override { return "perform congruence projection"; } unsigned get_arity() const override { return 2; } cmd_arg_kind next_arg_kind(cmd_context& ctx) const override { if (m_arg_index == 0) return CPK_EXPR_LIST; return CPK_FUNC_DECL_LIST; } void set_next_arg(cmd_context& ctx, unsigned num, expr * const* args) override { m_lits.append(num, args); m_arg_index = 1; } void set_next_arg(cmd_context & ctx, unsigned num, func_decl * const * ts) override { m_vars.append(num, ts); } void prepare(cmd_context & ctx) override { m_arg_index = 0; m_lits.reset(); m_vars.reset(); } void execute(cmd_context & ctx) override { ast_manager& m = ctx.m(); func_decl_ref_vector vars(m); expr_ref_vector lits(m); for (func_decl* v : m_vars) vars.push_back(v); for (expr* e : m_lits) lits.push_back(e); flatten_and(lits); 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.set_shared(vars); plugin.project(mdl, lits); ctx.regular_stream() << lits << "\n"; } }; void install_dbg_cmds(cmd_context & ctx) { ctx.insert(alloc(print_dimacs_cmd)); ctx.insert(alloc(get_quantifier_body_cmd)); ctx.insert(alloc(set_cmd)); ctx.insert(alloc(pp_var_cmd)); ctx.insert(alloc(shift_vars_cmd)); ctx.insert(alloc(assert_not_cmd)); ctx.insert(alloc(size_cmd)); ctx.insert(alloc(subst_cmd)); ctx.insert(alloc(bool_rewriter_cmd)); ctx.insert(alloc(bool_frewriter_cmd)); ctx.insert(alloc(elim_and_cmd)); install_simplify_cmd(ctx, "dbg-th-rewriter"); ctx.insert(alloc(lt_cmd)); ctx.insert(alloc(some_value_cmd)); ctx.insert(alloc(params_cmd)); ctx.insert(alloc(translator_cmd)); ctx.insert(alloc(sexpr_cmd)); ctx.insert(alloc(used_vars_cmd)); ctx.insert(alloc(elim_unused_vars_cmd)); ctx.insert(alloc(instantiate_cmd)); ctx.insert(alloc(instantiate_nested_cmd)); ctx.insert(alloc(set_next_id)); ctx.insert(alloc(mbp_cmd)); ctx.insert(alloc(mbi_cmd)); ctx.insert(alloc(euf_project_cmd)); ctx.insert(alloc(eufi_cmd)); } z3-z3-4.8.7/src/cmd_context/extra_cmds/dbg_cmds.h000066400000000000000000000004651356505360400215760ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: dbg_cmds.h Abstract: SMT2 front-end commands for debugging purposes. Author: Leonardo (leonardo) 2011-04-01 Notes: --*/ #ifndef DBG_CMDS_H_ #define DBG_CMDS_H_ class cmd_context; void install_dbg_cmds(cmd_context & ctx); #endif z3-z3-4.8.7/src/cmd_context/extra_cmds/polynomial_cmds.cpp000066400000000000000000000175721356505360400235670ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: polynomial_cmds.cpp Abstract: Commands for debugging polynomial module. Author: Leonardo (leonardo) 2011-12-23 Notes: --*/ #include #include "cmd_context/cmd_context.h" #include "cmd_context/cmd_util.h" #include "util/scoped_timer.h" #include "util/scoped_ctrl_c.h" #include "util/cancel_eh.h" #include "ast/ast_smt2_pp.h" #include "ast/expr2polynomial.h" #include "cmd_context/parametric_cmd.h" #include "util/mpq.h" #include "math/polynomial/algebraic_numbers.h" #include "math/polynomial/polynomial_var2value.h" #include "ast/expr2var.h" #include "ast/pp.h" #include "ast/pp_params.hpp" static void to_poly(cmd_context & ctx, expr * t) { polynomial::numeral_manager nm; polynomial::manager pm(ctx.m().limit(), nm); default_expr2polynomial expr2poly(ctx.m(), pm); polynomial::polynomial_ref p(pm); polynomial::scoped_numeral d(nm); if (!expr2poly.to_polynomial(t, p, d)) { throw cmd_exception("expression is not a polynomial"); } expr_ref r(ctx.m()); expr2poly.to_expr(p, true, r); if (!nm.is_one(d)) ctx.regular_stream() << "(* " << nm.to_string(d) << " "; ctx.display(ctx.regular_stream(), r); if (!nm.is_one(d)) ctx.regular_stream() << ")"; ctx.regular_stream() << std::endl; } static void factor(cmd_context & ctx, expr * t, polynomial::factor_params const & ps) { polynomial::numeral_manager nm; polynomial::manager pm(ctx.m().limit(), nm); default_expr2polynomial expr2poly(ctx.m(), pm); polynomial::polynomial_ref p(pm); polynomial::scoped_numeral d(nm); if (!expr2poly.to_polynomial(t, p, d)) { throw cmd_exception("expression is not a polynomial"); } polynomial::factors fs(pm); factor(p, fs, ps); ctx.regular_stream() << "(factors"; rational f0(fs.get_constant()); f0 = f0 / rational(d); ctx.regular_stream() << std::endl << f0; unsigned num_factors = fs.distinct_factors(); expr_ref f(ctx.m()); for (unsigned i = 0; i < num_factors; i++) { ctx.regular_stream() << std::endl; if (fs.get_degree(i) > 1) ctx.regular_stream() << "(^ "; expr2poly.to_expr(fs[i], true, f); ctx.display(ctx.regular_stream(), f); if (fs.get_degree(i) > 1) ctx.regular_stream() << " " << fs.get_degree(i) << ")"; } ctx.regular_stream() << ")" << std::endl; } class poly_isolate_roots_cmd : public cmd { struct context { arith_util m_util; reslimit m_lim; unsynch_mpq_manager m_qm; polynomial::manager m_pm; algebraic_numbers::manager m_am; polynomial_ref m_p; default_expr2polynomial m_expr2poly; polynomial::var m_var; typedef polynomial::simple_var2value x2v; x2v m_x2v; context(ast_manager & m): m_util(m), m_pm(m.limit(), m_qm), m_am(m_lim, m_qm), m_p(m_pm), m_expr2poly(m, m_pm), m_var(polynomial::null_var), m_x2v(m_am) { } void set_next_arg(cmd_context & ctx, expr * arg) { if (m_p.get() == nullptr) { scoped_mpz d(m_qm); if (!m_expr2poly.to_polynomial(arg, m_p, d)) throw cmd_exception("expression is not a polynomial"); } else if (m_var == polynomial::null_var) { if (!m_expr2poly.is_var(arg)) throw cmd_exception("invalid assignment, argument is not a variable in the given polynomial"); m_var = m_expr2poly.get_mapping().to_var(arg); } else { rational k; scoped_anum v(m_am); if (m_util.is_numeral(arg, k)) { m_am.set(v, k.to_mpq()); } else if (m_util.is_irrational_algebraic_numeral(arg)) { m_am.set(v, m_util.to_irrational_algebraic_numeral(arg)); } else { throw cmd_exception("invalid assignment, argument is not a value"); } m_x2v.push_back(m_var, v); m_var = polynomial::null_var; } } void execute(cmd_context & ctx) { if (m_p.get() == nullptr) throw cmd_exception("polynomial expected"); polynomial::var_vector xs; m_pm.vars(m_p, xs); unsigned num_assigned = 0; for (unsigned i = 0; i < xs.size(); i++) { if (m_x2v.contains(xs[i])) num_assigned++; } if (num_assigned != xs.size() && num_assigned + 1 != xs.size()) throw cmd_exception("given assignment is not sufficient to make the given polynomial univariate"); scoped_anum_vector rs(m_am); m_am.isolate_roots(m_p, m_x2v, rs); ctx.regular_stream() << "(roots"; pp_params params; bool pp_decimal = params.decimal(); for (unsigned i = 0; i < rs.size(); i++) { ctx.regular_stream() << std::endl; if (!pp_decimal) m_am.display_root_smt2(ctx.regular_stream(), rs[i]); else m_am.display_decimal(ctx.regular_stream(), rs[i]); } ctx.regular_stream() << ")" << std::endl; } }; scoped_ptr m_ctx; public: poly_isolate_roots_cmd(char const * name = "poly/isolate-roots"):cmd(name), m_ctx(nullptr) {} char const * get_usage() const override { return " ( )*"; } char const * get_descr(cmd_context & ctx) const override { return "isolate the roots a multivariate polynomial modulo an assignment"; } unsigned get_arity() const override { return VAR_ARITY; } void prepare(cmd_context & ctx) override { m_ctx = alloc(context, ctx.m()); } void finalize(cmd_context & ctx) override { m_ctx = nullptr; } void failure_cleanup(cmd_context & ctx) override { m_ctx = nullptr; } cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { return CPK_EXPR; } void set_next_arg(cmd_context & ctx, expr * arg) override { m_ctx->set_next_arg(ctx, arg); } void execute(cmd_context & ctx) override { m_ctx->execute(ctx); m_ctx = nullptr; } }; UNARY_CMD(to_poly_cmd, "to-poly", "", "convert expression into sum-of-monomials form", CPK_EXPR, expr *, to_poly(ctx, arg);); class poly_factor_cmd : public parametric_cmd { expr * m_target; public: poly_factor_cmd(char const * name = "poly/factor"):parametric_cmd(name) {} char const * get_usage() const override { return " ( )*"; } char const * get_main_descr() const override { return "factor a polynomial"; } void init_pdescrs(cmd_context & ctx, param_descrs & p) override { polynomial::factor_params::get_param_descrs(p); } void prepare(cmd_context & ctx) override { parametric_cmd::prepare(ctx); m_target = nullptr; } cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { if (m_target == nullptr) return CPK_EXPR; return parametric_cmd::next_arg_kind(ctx); } void set_next_arg(cmd_context & ctx, expr * arg) override { m_target = arg; } void execute(cmd_context & ctx) override { polynomial::factor_params ps; ps.updt_params(m_params); factor(ctx, m_target, ps); } }; void install_polynomial_cmds(cmd_context & ctx) { #ifndef _EXTERNAL_RELEASE ctx.insert(alloc(to_poly_cmd)); ctx.insert(alloc(poly_factor_cmd)); ctx.insert(alloc(poly_isolate_roots_cmd)); #endif } z3-z3-4.8.7/src/cmd_context/extra_cmds/polynomial_cmds.h000066400000000000000000000005121356505360400232160ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: polynomial_cmds.h Abstract: Commands for debugging polynomial module. Author: Leonardo (leonardo) 2011-12-23 Notes: --*/ #ifndef POLYNOMIAL_CMDS_H_ #define POLYNOMIAL_CMDS_H_ class cmd_context; void install_polynomial_cmds(cmd_context & ctx); #endif z3-z3-4.8.7/src/cmd_context/extra_cmds/subpaving_cmds.cpp000066400000000000000000000030671356505360400233740ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: subpaving_cmds.cpp Abstract: Commands for debugging subpaving module. Author: Leonardo (leonardo) 2012-08-09 Notes: --*/ #include #include "cmd_context/cmd_context.h" #include "cmd_context/cmd_util.h" #include "math/subpaving/tactic/expr2subpaving.h" #include "ast/rewriter/th_rewriter.h" #include "ast/ast_smt2_pp.h" #include "ast/expr2var.h" static void to_subpaving(cmd_context & ctx, expr * t) { ast_manager & m = ctx.m(); unsynch_mpq_manager qm; scoped_ptr s; s = subpaving::mk_mpq_context(ctx.m().limit(), qm); expr2var e2v(m); expr2subpaving e2s(m, *s, &e2v); params_ref p; p.set_bool("mul_to_power", true); th_rewriter simp(m, p); expr_ref t_s(m); simp(t, t_s); scoped_mpz n(qm), d(qm); ctx.regular_stream() << mk_ismt2_pp(t_s, m) << "\n=======>" << std::endl; subpaving::var x = e2s.internalize_term(t_s, n, d); expr2var::iterator it = e2v.begin(); expr2var::iterator end = e2v.end(); for (; it != end; ++it) { ctx.regular_stream() << "x" << it->m_value << " := " << mk_ismt2_pp(it->m_key, m) << "\n"; } s->display_constraints(ctx.regular_stream()); ctx.regular_stream() << n << "/" << d << " x" << x << "\n"; } UNARY_CMD(to_subpaving_cmd, "to-subpaving", "", "internalize expression into subpaving module", CPK_EXPR, expr *, to_subpaving(ctx, arg);); void install_subpaving_cmds(cmd_context & ctx) { #ifndef _EXTERNAL_RELEASE ctx.insert(alloc(to_subpaving_cmd)); #endif } z3-z3-4.8.7/src/cmd_context/extra_cmds/subpaving_cmds.h000066400000000000000000000004121356505360400230300ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: subpaving_cmds.h Abstract: Commands for debugging subpaving module. Author: Leonardo (leonardo) 2012-08-09 Notes: --*/ class cmd_context; void install_subpaving_cmds(cmd_context & ctx); z3-z3-4.8.7/src/cmd_context/parametric_cmd.cpp000066400000000000000000000030771356505360400212120ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: parametric_cmd.h Abstract: A generic parametric cmd. Author: Leonardo (leonardo) 2011-04-22 Notes: --*/ #include #include "cmd_context/cmd_context.h" #include "cmd_context/parametric_cmd.h" char const * parametric_cmd::get_descr(cmd_context & ctx) const { if (m_descr == nullptr) { const_cast(this)->m_descr = alloc(string_buffer<>); m_descr->append(get_main_descr()); m_descr->append("\nThe following options are available:\n"); std::ostringstream buf; pdescrs(ctx).display(buf, 2); m_descr->append(buf.str().c_str()); } return m_descr->c_str(); } cmd_arg_kind parametric_cmd::next_arg_kind(cmd_context & ctx) const { if (m_last == symbol::null) return CPK_KEYWORD; return pdescrs(ctx).get_kind(m_last); } void parametric_cmd::set_next_arg(cmd_context & ctx, symbol const & s) { if (m_last == symbol::null) { m_last = symbol(norm_param_name(s).c_str()); if (pdescrs(ctx).get_kind(m_last.bare_str()) == CPK_INVALID) throw cmd_exception("invalid keyword argument"); return; } else { m_params.set_sym(m_last.bare_str(), s); m_last = symbol::null; } } param_descrs const & parametric_cmd::pdescrs(cmd_context & ctx) const { if (!m_pdescrs) { parametric_cmd * _this = const_cast(this); _this->m_pdescrs = alloc(param_descrs); _this->init_pdescrs(ctx, *(_this->m_pdescrs)); } return *m_pdescrs; } z3-z3-4.8.7/src/cmd_context/parametric_cmd.h000066400000000000000000000047051356505360400206560ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: parametric_cmd.h Abstract: A generic parametric cmd. Author: Leonardo (leonardo) 2011-04-22 Notes: --*/ #ifndef PARAMETRIC_CMD_H_ #define PARAMETRIC_CMD_H_ #include "util/params.h" #include "util/symbol.h" #include "util/string_buffer.h" #include "util/cmd_context_types.h" class parametric_cmd : public cmd { public: symbol m_last; string_buffer<> * m_descr; params_ref m_params; scoped_ptr m_pdescrs; public: parametric_cmd(char const * name):cmd(name), m_descr(nullptr) {} ~parametric_cmd() override { if (m_descr) dealloc(m_descr); } virtual void init_pdescrs(cmd_context & ctx, param_descrs & d) = 0; param_descrs const & pdescrs(cmd_context & ctx) const; params_ref const & ps() const { return m_params; } virtual char const * get_main_descr() const = 0; char const * get_descr(cmd_context & ctx) const override; unsigned get_arity() const override { return VAR_ARITY; } void prepare(cmd_context & ctx) override { m_last = symbol::null; m_params.reset(); } cmd_arg_kind next_arg_kind(cmd_context & ctx) const override; void set_next_arg(cmd_context & ctx, symbol const & s) override; void set_next_arg(cmd_context & ctx, unsigned val) override { m_params.set_uint(m_last, val); m_last = symbol::null; } void set_next_arg(cmd_context & ctx, bool val) override { m_params.set_bool(m_last, val); m_last = symbol::null; } void set_next_arg(cmd_context & ctx, rational const & val) override { m_params.set_rat(m_last, val); m_last = symbol::null; } void set_next_arg(cmd_context & ctx, char const * val) override { m_params.set_str(m_last, val); m_last = symbol::null; } void set_next_arg(cmd_context & ctx, sort * s) override { NOT_IMPLEMENTED_YET(); // m_params.set_sort(m_last, s); // m_last = symbol::null; } void set_next_arg(cmd_context & ctx, expr * t) override { NOT_IMPLEMENTED_YET(); // m_params.set_expr(m_last, t); // m_last = symbol::null; } void set_next_arg(cmd_context & ctx, func_decl * f) override { NOT_IMPLEMENTED_YET(); // m_params.set_func_decl(m_last, f); // m_last = symbol::null; } void set_next_arg(cmd_context & ctx, sexpr * n) override { UNREACHABLE(); } }; #endif z3-z3-4.8.7/src/cmd_context/pdecl.cpp000066400000000000000000001040701356505360400173220ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: pdecl.cpp Abstract: Parametric declarations for SMT-LIB 2.0 + inductive data-types. Author: Leonardo de Moura (leonardo) 2011-03-02. Revision History: --*/ #include "cmd_context/pdecl.h" #include "ast/datatype_decl_plugin.h" #include "ast/ast_pp.h" #include using namespace format_ns; class psort_inst_cache { unsigned m_num_params; sort * m_const; obj_map m_map; // if m_num_params == 1 value is a sort, otherwise it is a reference to another inst_cache public: psort_inst_cache(unsigned num_params):m_num_params(num_params), m_const(nullptr) { } ~psort_inst_cache() { SASSERT(m_map.empty()); SASSERT(m_const == 0); } void finalize(pdecl_manager & m) { if (m_num_params == 0) { SASSERT(m_map.empty()); if (m_const) m.m().dec_ref(m_const); m_const = nullptr; } else { SASSERT(m_const == 0); for (auto kv : m_map) { m.m().dec_ref(kv.m_key); if (m_num_params == 1) { m.m().dec_ref(static_cast(kv.m_value)); } else { psort_inst_cache * child = static_cast(kv.m_value); child->finalize(m); child->~psort_inst_cache(); m.a().deallocate(sizeof(psort_inst_cache), child); } } m_map.reset(); } } void insert(pdecl_manager & m, sort * const * s, sort * r) { if (m_num_params == 0) { SASSERT(m_const == 0); m.m().inc_ref(r); m_const = r; return; } psort_inst_cache * curr = this; while (true) { if (curr->m_num_params == 1) { SASSERT(!curr->m_map.contains(*s)); curr->m_map.insert(*s, r); m.m().inc_ref(*s); m.m().inc_ref(r); return; } void * next = nullptr; if (!curr->m_map.find(*s, next)) { next = new (m.a().allocate(sizeof(psort_inst_cache))) psort_inst_cache(curr->m_num_params-1); curr->m_map.insert(*s, next); m.m().inc_ref(*s); } SASSERT(next != 0); SASSERT(curr->m_num_params == static_cast(next)->m_num_params + 1); s++; curr = static_cast(next); } } sort * find(sort * const * s) const { if (m_num_params == 0) return m_const; psort_inst_cache const * curr = this; while (true) { if (curr->m_num_params == 1) { void * r = nullptr; curr->m_map.find(*s, r); return static_cast(r); } else { void * next = nullptr; curr->m_map.find(*s, next); if (next == nullptr) return nullptr; s++; curr = static_cast(next); } } } bool empty() const { return m_num_params == 0 ? m_const == nullptr : m_map.empty(); } }; void psort::cache(pdecl_manager & m, sort * const * s, sort * r) { if (!m_inst_cache) m_inst_cache = m.mk_inst_cache(m_num_params); m_inst_cache->insert(m, s, r); } sort * psort::find(sort * const * s) const { if (!m_inst_cache) return nullptr; return m_inst_cache->find(s); } void psort::finalize(pdecl_manager & m) { reset_cache(m); } void psort::reset_cache(pdecl_manager& m) { m.del_inst_cache(m_inst_cache); m_inst_cache = nullptr; } /** \brief wrapper for sorts. */ class psort_sort : public psort { friend class pdecl_manager; sort * m_sort; psort_sort(unsigned id, pdecl_manager & m, sort * s):psort(id, 0), m_sort(s) { m.m().inc_ref(m_sort); } void finalize(pdecl_manager & m) override { m.m().dec_ref(m_sort); psort::finalize(m); } bool check_num_params(pdecl * other) const override { return true; } size_t obj_size() const override { return sizeof(psort_sort); } sort * get_sort() const { return m_sort; } sort * instantiate(pdecl_manager & m, sort * const * s) override { return m_sort; } public: ~psort_sort() override {} bool is_sort_wrapper() const override { return true; } char const * hcons_kind() const override { return "psort_sort"; } unsigned hcons_hash() const override { return m_sort->get_id(); } bool hcons_eq(psort const * other) const override { if (other->hcons_kind() != hcons_kind()) return false; return m_sort == static_cast(other)->m_sort; } void display(std::ostream & out) const override { out << m_sort->get_name(); } }; class psort_var : public psort { friend class pdecl_manager; unsigned m_idx; psort_var(unsigned id, unsigned num_params, unsigned idx):psort(id, num_params), m_idx(idx) { SASSERT(idx < num_params); } sort * instantiate(pdecl_manager & m, sort * const * s) override { return s[m_idx]; } size_t obj_size() const override { return sizeof(psort_var); } public: ~psort_var() override {} char const * hcons_kind() const override { return "psort_var"; } unsigned hcons_hash() const override { return hash_u_u(m_num_params, m_idx); } bool hcons_eq(psort const * other) const override { return other->hcons_kind() == hcons_kind() && get_num_params() == other->get_num_params() && m_idx == static_cast(other)->m_idx; } void display(std::ostream & out) const override { out << "s_" << m_idx; } unsigned idx() const { return m_idx; } }; class psort_app : public psort { friend class pdecl_manager; psort_decl * m_decl; ptr_vector m_args; psort_app(unsigned id, unsigned num_params, pdecl_manager & m, psort_decl * d, unsigned num_args, psort * const * args): psort(id, num_params), m_decl(d), m_args(num_args, args) { m.inc_ref(d); m.inc_ref(num_args, args); SASSERT(num_args == m_decl->get_num_params() || m_decl->has_var_params()); DEBUG_CODE(if (num_args == num_params) { for (unsigned i = 0; i < num_params; i++) args[i]->check_num_params(this); }); } void finalize(pdecl_manager & m) override { m.lazy_dec_ref(m_decl); m.lazy_dec_ref(m_args.size(), m_args.c_ptr()); psort::finalize(m); } size_t obj_size() const override { return sizeof(psort_app); } struct khasher { unsigned operator()(psort_app const * d) const { return d->m_decl->hash(); } }; struct chasher { unsigned operator()(psort_app const * d, unsigned idx) const { return d->m_args[idx]->hash(); } }; sort * instantiate(pdecl_manager & m, sort * const * s) override { sort * r = find(s); if (r) return r; sort_ref_buffer args_i(m.m()); unsigned sz = m_args.size(); for (unsigned i = 0; i < sz; i++) { sort * a = m_args[i]->instantiate(m, s); args_i.push_back(a); } r = m_decl->instantiate(m, args_i.size(), args_i.c_ptr()); cache(m, s, r); return r; } public: ~psort_app() override {} char const * hcons_kind() const override { return "psort_app"; } unsigned hcons_hash() const override { return get_composite_hash(const_cast(this), m_args.size()); } bool hcons_eq(psort const * other) const override { if (other->hcons_kind() != hcons_kind()) return false; if (get_num_params() != other->get_num_params()) return false; psort_app const * _other = static_cast(other); if (m_decl != _other->m_decl) return false; SASSERT(m_args.size() == _other->m_args.size()); unsigned sz = m_args.size(); for (unsigned i = 0; i < sz; i++) { if (m_args[i] != _other->m_args[i]) return false; } return true; } void display(std::ostream & out) const override { if (m_args.empty()) { out << m_decl->get_name(); } else { out << "(" << m_decl->get_name(); unsigned sz = m_args.size(); for (unsigned i = 0; i < sz; i++) { out << " "; m_args[i]->display(out); } out << ")"; } } }; psort_decl::psort_decl(unsigned id, unsigned num_params, pdecl_manager & m, symbol const & n): pdecl(id, num_params), m_name(n), m_psort_kind(PSORT_BASE), m_inst_cache(nullptr) { } void psort_decl::finalize(pdecl_manager & m) { reset_cache(m); } void psort_decl::reset_cache(pdecl_manager& m) { m.del_inst_cache(m_inst_cache); m_inst_cache = nullptr; } void psort_decl::cache(pdecl_manager & m, sort * const * s, sort * r) { if (!m_inst_cache) m_inst_cache = m.mk_inst_cache(m_num_params); m_inst_cache->insert(m, s, r); } sort * psort_decl::find(sort * const * s) { if (!m_inst_cache) return nullptr; return m_inst_cache->find(s); } psort_user_decl::psort_user_decl(unsigned id, unsigned num_params, pdecl_manager & m, symbol const & n, psort * p) : psort_decl(id, num_params, m, n), m_def(p) { m_psort_kind = PSORT_USER; m.inc_ref(p); SASSERT(p == 0 || num_params == p->get_num_params()); } void psort_user_decl::finalize(pdecl_manager & m) { m.dec_ref(m_def); m_def = nullptr; psort_decl::finalize(m); } sort * psort_user_decl::instantiate(pdecl_manager & m, unsigned n, sort * const * s) { SASSERT(n == m_num_params); sort * r = find(s); if (r) return r; if (m_def == nullptr) { buffer ps; for (unsigned i = 0; i < n; i++) ps.push_back(parameter(s[i])); r = m.m().mk_uninterpreted_sort(m_name, ps.size(), ps.c_ptr()); } else { r = m_def->instantiate(m, s); } cache(m, s, r); m.save_info(r, this, n, s); return r; } void display_sort_args(std::ostream & out, unsigned num_params) { if (num_params > 0) out << " ("; for (unsigned i = 0; i < num_params; i++) { if (i > 0) out << " "; out << "s_" << i; } if (num_params > 0) out << ") "; } void psort_user_decl::display(std::ostream & out) const { out << "(declare-sort " << m_name; display_sort_args(out, m_num_params); if (m_def) m_def->display(out); out << ")"; } // ------------------- // psort_dt_decl psort_dt_decl::psort_dt_decl(unsigned id, unsigned num_params, pdecl_manager & m, symbol const & n) : psort_decl(id, num_params, m, n) { m_psort_kind = PSORT_DT; } sort * psort_dt_decl::instantiate(pdecl_manager & m, unsigned n, sort * const * s) { SASSERT(n == m_num_params); return m.instantiate_datatype(this, m_name, n, s); } void psort_dt_decl::display(std::ostream & out) const { out << "(datatype-sort " << m_name << ")"; } // ------------------- // psort_builtin_decl psort_builtin_decl::psort_builtin_decl(unsigned id, pdecl_manager & m, symbol const & n, family_id fid, decl_kind k): psort_decl(id, PSORT_DECL_VAR_PARAMS, m, n), m_fid(fid), m_kind(k) { m_psort_kind = PSORT_BUILTIN; } sort * psort_builtin_decl::instantiate(pdecl_manager & m, unsigned n, sort * const * s) { if (n == 0) { sort * r = m.m().mk_sort(m_fid, m_kind); m.save_info(r, this, 0, s); return r; } else { buffer params; for (unsigned i = 0; i < n; i++) params.push_back(parameter(s[i])); sort * r = m.m().mk_sort(m_fid, m_kind, n, params.c_ptr()); m.save_info(r, this, n, s); return r; } } sort * psort_builtin_decl::instantiate(pdecl_manager & m, unsigned n, unsigned const * s) { if (n == 0) { sort * r = m.m().mk_sort(m_fid, m_kind); m.save_info(r, this, 0, s); return r; } else { buffer params; for (unsigned i = 0; i < n; i++) params.push_back(parameter(s[i])); sort * r = m.m().mk_sort(m_fid, m_kind, n, params.c_ptr()); m.save_info(r, this, n, s); return r; } } void psort_builtin_decl::display(std::ostream & out) const { out << "(declare-builtin-sort " << m_name << ")"; } void ptype::display(std::ostream & out, pdatatype_decl const * const * dts) const { switch (kind()) { case PTR_PSORT: get_psort()->display(out); break; case PTR_REC_REF: out << dts[get_idx()]->get_name(); break; case PTR_MISSING_REF: out << get_missing_ref(); break; } } paccessor_decl::paccessor_decl(unsigned id, unsigned num_params, pdecl_manager & m, symbol const & n, ptype const & r): pdecl(id, num_params), m_name(n), m_type(r) { if (m_type.kind() == PTR_PSORT) { m.inc_ref(r.get_psort()); } } void paccessor_decl::finalize(pdecl_manager & m) { if (m_type.kind() == PTR_PSORT) { m.lazy_dec_ref(m_type.get_psort()); } } bool paccessor_decl::has_missing_refs(symbol & missing) const { if (m_type.kind() == PTR_MISSING_REF) { missing = m_type.get_missing_ref(); return true; } return false; } bool paccessor_decl::fix_missing_refs(dictionary const & symbol2idx, symbol & missing) { TRACE("fix_missing_refs", tout << "m_type.kind(): " << m_type.kind() << "\n"; if (m_type.kind() == PTR_MISSING_REF) tout << m_type.get_missing_ref() << "\n";); if (m_type.kind() != PTR_MISSING_REF) return true; int idx; if (symbol2idx.find(m_type.get_missing_ref(), idx)) { m_type = ptype(idx); SASSERT(m_type.kind() == PTR_REC_REF); return true; } missing = m_type.get_missing_ref(); return false; } accessor_decl * paccessor_decl::instantiate_decl(pdecl_manager & m, sort * const * s) { switch (m_type.kind()) { case PTR_REC_REF: return mk_accessor_decl(m.m(), m_name, type_ref(m_type.get_idx())); case PTR_PSORT: return mk_accessor_decl(m.m(), m_name, type_ref(m_type.get_psort()->instantiate(m, s))); default: // missing refs must have been eliminated. UNREACHABLE(); return nullptr; } } void paccessor_decl::display(std::ostream & out, pdatatype_decl const * const * dts) const { out << "(" << m_name << " "; m_type.display(out, dts); out << ")"; } pconstructor_decl::pconstructor_decl(unsigned id, unsigned num_params, pdecl_manager & m, symbol const & n, symbol const & r, unsigned num_accessors, paccessor_decl * const * accessors): pdecl(id, num_params), m_name(n), m_recogniser_name(r), m_accessors(num_accessors, accessors) { m.inc_ref(num_accessors, accessors); TRACE("pconstructor_decl", tout << "name: " << n << ", recognizer: " << r << "\n";); } void pconstructor_decl::finalize(pdecl_manager & m) { m.lazy_dec_ref(m_accessors.size(), m_accessors.c_ptr()); } bool pconstructor_decl::has_missing_refs(symbol & missing) const { for (paccessor_decl* a : m_accessors) { if (a->has_missing_refs(missing)) return true; } return false; } bool pconstructor_decl::fix_missing_refs(dictionary const & symbol2idx, symbol & missing) { for (paccessor_decl* a : m_accessors) { if (!a->fix_missing_refs(symbol2idx, missing)) return false; } return true; } constructor_decl * pconstructor_decl::instantiate_decl(pdecl_manager & m, sort * const * s) { ptr_buffer as; for (paccessor_decl* a : m_accessors) as.push_back(a->instantiate_decl(m, s)); return mk_constructor_decl(m_name, m_recogniser_name, as.size(), as.c_ptr()); } void pconstructor_decl::display(std::ostream & out, pdatatype_decl const * const * dts) const { out << "(" << m_name; for (paccessor_decl* a : m_accessors) { out << " "; a->display(out, dts); } out << ")"; } pdatatype_decl::pdatatype_decl(unsigned id, unsigned num_params, pdecl_manager & m, symbol const & n, unsigned num_constructors, pconstructor_decl * const * constructors): psort_decl(id, num_params, m, n), m_constructors(num_constructors, constructors), m_parent(nullptr) { m.inc_ref(num_constructors, constructors); } void pdatatype_decl::finalize(pdecl_manager & m) { m.lazy_dec_ref(m_constructors.size(), m_constructors.c_ptr()); psort_decl::finalize(m); } bool pdatatype_decl::has_missing_refs(symbol & missing) const { for (auto c : m_constructors) if (c->has_missing_refs(missing)) return true; return false; } bool pdatatype_decl::has_duplicate_accessors(symbol & duplicated) const { hashtable names; for (auto c : m_constructors) { for (auto a : c->m_accessors) { symbol const& name = a->get_name(); if (names.contains(name)) { duplicated = name; return true; } names.insert(name); } } return false; } bool pdatatype_decl::fix_missing_refs(dictionary const & symbol2idx, symbol & missing) { for (auto c : m_constructors) { if (!c->fix_missing_refs(symbol2idx, missing)) return false; } return true; } datatype_decl * pdatatype_decl::instantiate_decl(pdecl_manager & m, sort * const * s) { ptr_buffer cs; for (auto c : m_constructors) { cs.push_back(c->instantiate_decl(m, s)); } datatype_util util(m.m()); return mk_datatype_decl(util, m_name, m_num_params, s, cs.size(), cs.c_ptr()); } struct datatype_decl_buffer { ptr_buffer m_buffer; ~datatype_decl_buffer() { del_datatype_decls(m_buffer.size(), m_buffer.c_ptr()); } }; sort * pdatatype_decl::instantiate(pdecl_manager & m, unsigned n, sort * const * s) { sort * r = m.instantiate_datatype(this, m_name, n, s); datatype_util util(m.m()); if (r && n > 0 && util.is_declared(r)) { ast_mark mark; datatype::def const& d = util.get_def(r); mark.mark(r, true); sort_ref_vector params(m.m(), n, s); for (datatype::constructor* c : d) { for (datatype::accessor* a : *c) { sort* rng = a->range(); if (util.is_datatype(rng) && !mark.is_marked(rng) && m_parent) { mark.mark(rng, true); // TBD: search over more than just parents for (pdatatype_decl* p : *m_parent) { if (p->get_name() == rng->get_name()) { ptr_vector ps; func_decl_ref acc = a->instantiate(params); for (unsigned j = 0; j < util.get_datatype_num_parameter_sorts(rng); ++j) { ps.push_back(util.get_datatype_parameter_sort(acc->get_range(), j)); } m.instantiate_datatype(p, p->get_name(), ps.size(), ps.c_ptr()); break; } } } } } } return r; } void pdatatype_decl::display(std::ostream & out) const { out << "(declare-datatype " << m_name; display_sort_args(out, m_num_params); bool first = true; for (auto c : m_constructors) { if (!first) out << " "; if (m_parent) { c->display(out, m_parent->children()); } else { pdatatype_decl const * dts[1] = { this }; c->display(out, dts); } first = false; } out << ")"; } bool pdatatype_decl::commit(pdecl_manager& m) { TRACE("datatype", tout << m_name << "\n";); sort_ref_vector ps(m.m()); for (unsigned i = 0; i < m_num_params; ++i) { ps.push_back(m.m().mk_uninterpreted_sort(symbol(i), 0, nullptr)); } datatype_decl_buffer dts; dts.m_buffer.push_back(instantiate_decl(m, ps.c_ptr())); datatype_decl * d_ptr = dts.m_buffer[0]; sort_ref_vector sorts(m.m()); bool is_ok = m.get_dt_plugin()->mk_datatypes(1, &d_ptr, m_num_params, ps.c_ptr(), sorts); if (is_ok && m_num_params == 0) { m.notify_new_dt(sorts.get(0), this); } return is_ok; } pdatatypes_decl::pdatatypes_decl(unsigned id, unsigned num_params, pdecl_manager & m, unsigned num_datatypes, pdatatype_decl * const * dts): pdecl(id, num_params), m_datatypes(num_datatypes, dts) { m.inc_ref(num_datatypes, dts); for (auto d : m_datatypes) { SASSERT(d->m_parent == 0); d->m_parent = this; } } void pdatatypes_decl::finalize(pdecl_manager & m) { m.lazy_dec_ref(m_datatypes.size(), m_datatypes.c_ptr()); } bool pdatatypes_decl::fix_missing_refs(symbol & missing) { TRACE("fix_missing_refs", tout << "pdatatypes_decl::fix_missing_refs\n";); dictionary symbol2idx; int idx = 0; for (pdatatype_decl* d : m_datatypes) symbol2idx.insert(d->get_name(), idx++); for (pdatatype_decl* d : m_datatypes) if (!d->fix_missing_refs(symbol2idx, missing)) return false; return true; } sort* pdecl_manager::instantiate_datatype(psort_decl* p, symbol const& name, unsigned n, sort * const* s) { TRACE("datatype", tout << name << " "; for (unsigned i = 0; i < n; ++i) tout << s[i]->get_name() << " "; tout << "\n";); pdecl_manager& m = *this; sort * r = p->find(s); if (r) return r; buffer ps; ps.push_back(parameter(name)); for (unsigned i = 0; i < n; i++) ps.push_back(parameter(s[i])); datatype_util util(m.m()); r = m.m().mk_sort(util.get_family_id(), DATATYPE_SORT, ps.size(), ps.c_ptr()); p->cache(m, s, r); m.save_info(r, p, n, s); if (n > 0 && util.is_declared(r)) { bool has_typevar = false; // crude check .. for (unsigned i = 0; !has_typevar && i < n; ++i) { has_typevar = s[i]->get_name().is_numerical(); } if (!has_typevar) { m.notify_new_dt(r, p); } } return r; } bool pdatatypes_decl::instantiate(pdecl_manager & m, sort * const * s) { UNREACHABLE(); return false; } bool pdatatypes_decl::commit(pdecl_manager& m) { datatype_decl_buffer dts; for (pdatatype_decl* d : m_datatypes) { sort_ref_vector ps(m.m()); for (unsigned i = 0; i < d->get_num_params(); ++i) { ps.push_back(m.m().mk_uninterpreted_sort(symbol(i), 0, nullptr)); } dts.m_buffer.push_back(d->instantiate_decl(m, ps.c_ptr())); } sort_ref_vector sorts(m.m()); bool is_ok = m.get_dt_plugin()->mk_datatypes(m_datatypes.size(), dts.m_buffer.c_ptr(), 0, nullptr, sorts); if (is_ok) { for (unsigned i = 0; i < m_datatypes.size(); ++i) { pdatatype_decl* d = m_datatypes[i]; if (d->get_num_params() == 0) { m.notify_new_dt(sorts.get(i), this); } } } return is_ok; } struct pdecl_manager::sort_info { psort_decl * m_decl; sort_info(pdecl_manager & m, psort_decl * d): m_decl(d) { m.inc_ref(d); } virtual ~sort_info() {} virtual unsigned obj_size() const { return sizeof(sort_info); } virtual void finalize(pdecl_manager & m) { m.dec_ref(m_decl); } virtual void display(std::ostream & out, pdecl_manager const & m) const = 0; virtual format * pp(pdecl_manager const & m) const = 0; }; struct pdecl_manager::app_sort_info : public pdecl_manager::sort_info { ptr_vector m_args; app_sort_info(pdecl_manager & m, psort_decl * d, unsigned n, sort * const * s): sort_info(m, d), m_args(n, s) { m.m().inc_array_ref(n, s); } ~app_sort_info() override {} unsigned obj_size() const override { return sizeof(app_sort_info); } void finalize(pdecl_manager & m) override { sort_info::finalize(m); m.m().dec_array_ref(m_args.size(), m_args.c_ptr()); } void display(std::ostream & out, pdecl_manager const & m) const override { if (m_args.empty()) { out << m_decl->get_name(); } else { out << "(" << m_decl->get_name(); for (auto arg : m_args) { m.display(out << " ", arg); } out << ")"; } } format * pp(pdecl_manager const & m) const override { if (m_args.empty()) { return mk_string(m.m(), m_decl->get_name().str().c_str()); } else { ptr_buffer b; for (auto arg : m_args) b.push_back(m.pp(arg)); return mk_seq1(m.m(), b.begin(), b.end(), f2f(), m_decl->get_name().str().c_str()); } } }; struct pdecl_manager::indexed_sort_info : public pdecl_manager::sort_info { svector m_indices; indexed_sort_info(pdecl_manager & m, psort_decl * d, unsigned n, unsigned const * s): sort_info(m, d), m_indices(n, s) { } ~indexed_sort_info() override {} unsigned obj_size() const override { return sizeof(indexed_sort_info); } void display(std::ostream & out, pdecl_manager const & m) const override { if (m_indices.empty()) { out << m_decl->get_name(); } else { out << "(_ " << m_decl->get_name(); for (auto idx : m_indices) { out << " " << idx; } out << ")"; } } format * pp(pdecl_manager const & m) const override { if (m_indices.empty()) { return mk_string(m.m(), m_decl->get_name().str().c_str()); } else { ptr_buffer b; b.push_back(mk_string(m.m(), m_decl->get_name().str().c_str())); for (auto idx : m_indices) b.push_back(mk_unsigned(m.m(), idx)); return mk_seq1(m.m(), b.begin(), b.end(), f2f(), "_"); } } }; void pdecl_manager::init_list() { SASSERT(m_list == 0); psort * v = mk_psort_var(1, 0); ptype T(v); ptype ListT(0); paccessor_decl * as[2] = { mk_paccessor_decl(1, symbol("head"), T), mk_paccessor_decl(1, symbol("tail"), ListT) }; pconstructor_decl * cs[2] = { mk_pconstructor_decl(1, symbol("nil"), symbol("is-nil"), 0, nullptr), mk_pconstructor_decl(1, symbol("insert"), symbol("is-insert"), 2, as) }; m_list = mk_pdatatype_decl(1, symbol("List"), 2, cs); inc_ref(m_list); m_list->commit(*this); } pdecl_manager::pdecl_manager(ast_manager & m): m_manager(m), m_allocator(m.get_allocator()), m_new_dt_eh(nullptr) { m_list = nullptr; m_datatype_fid = m.mk_family_id("datatype"); } pdecl_manager::~pdecl_manager() { dec_ref(m_list); reset_sort_info(); for (auto const& kv : m_sort2psort) { del_decl_core(kv.m_value); TRACE("pdecl_manager", tout << "orphan: " << mk_pp(kv.m_key, m()) << "\n";); } for (auto* p : m_table) { del_decl_core(p); } m_sort2psort.reset(); m_table.reset(); SASSERT(m_sort2psort.empty()); SASSERT(m_table.empty()); } psort * pdecl_manager::mk_psort_cnst(sort * s) { psort * r = nullptr; if (m_sort2psort.find(s, r)) return r; r = new (a().allocate(sizeof(psort_sort))) psort_sort(m_id_gen.mk(), *this, s); m_sort2psort.insert(s, r); return r; } psort * pdecl_manager::register_psort(psort * n) { psort * r = m_table.insert_if_not_there(n); if (r != n) { del_decl_core(n); } return r; } psort * pdecl_manager::mk_psort_var(unsigned num_params, unsigned vidx) { psort_var * n = new (a().allocate(sizeof(psort_var))) psort_var(m_id_gen.mk(), num_params, vidx); return register_psort(n); } paccessor_decl * pdecl_manager::mk_paccessor_decl(unsigned num_params, symbol const & s, ptype const & p) { return new (a().allocate(sizeof(paccessor_decl))) paccessor_decl(m_id_gen.mk(), num_params, *this, s, p); } pconstructor_decl * pdecl_manager::mk_pconstructor_decl(unsigned num_params, symbol const & s, symbol const & r, unsigned num, paccessor_decl * const * as) { return new (a().allocate(sizeof(pconstructor_decl))) pconstructor_decl(m_id_gen.mk(), num_params, *this, s, r, num, as); } pdatatype_decl * pdecl_manager::mk_pdatatype_decl(unsigned num_params, symbol const & s, unsigned num, pconstructor_decl * const * cs) { TRACE("datatype", tout << s << " has " << num_params << " parameters\n";); return new (a().allocate(sizeof(pdatatype_decl))) pdatatype_decl(m_id_gen.mk(), num_params, *this, s, num, cs); } pdatatypes_decl * pdecl_manager::mk_pdatatypes_decl(unsigned num_params, unsigned num, pdatatype_decl * const * dts) { return new (a().allocate(sizeof(pdatatypes_decl))) pdatatypes_decl(m_id_gen.mk(), num_params, *this, num, dts); } psort * pdecl_manager::mk_psort_app(unsigned num_params, psort_decl * d, unsigned num_args, psort * const * args) { psort * n = new (a().allocate(sizeof(psort_app))) psort_app(m_id_gen.mk(), num_params, *this, d, num_args, args); return register_psort(n); } psort * pdecl_manager::mk_psort_app(psort_decl * d) { SASSERT(d->get_num_params() == 0 || d->get_num_params() == PSORT_DECL_VAR_PARAMS); sort * s = d->instantiate(*this, 0, static_cast(nullptr)); if (s == nullptr) return nullptr; return mk_psort_cnst(s); } psort_decl * pdecl_manager::mk_psort_user_decl(unsigned num_params, symbol const & n, psort * def) { return new (a().allocate(sizeof(psort_user_decl))) psort_user_decl(m_id_gen.mk(), num_params, *this, n, def); } psort_decl * pdecl_manager::mk_psort_dt_decl(unsigned num_params, symbol const & n) { return new (a().allocate(sizeof(psort_dt_decl))) psort_dt_decl(m_id_gen.mk(), num_params, *this, n); } psort_decl * pdecl_manager::mk_psort_builtin_decl(symbol const & n, family_id fid, decl_kind k) { return new (a().allocate(sizeof(psort_builtin_decl))) psort_builtin_decl(m_id_gen.mk(), *this, n, fid, k); } sort * pdecl_manager::instantiate(psort * p, unsigned num, sort * const * args) { // ignoring stack overflows... sorts are not deeply nested return p->instantiate(*this, args); } void pdecl_manager::del_decl_core(pdecl * p) { TRACE("pdecl_manager", tout << "del_decl_core:\n"; if (p->is_psort()) static_cast(p)->display(tout); else static_cast(p)->display(tout); tout << "\n";); size_t sz = p->obj_size(); m_id_gen.recycle(p->get_id()); p->finalize(*this); p->~pdecl(); m_allocator.deallocate(sz, p); } void pdecl_manager::del_decl(pdecl * p) { TRACE("pdecl_manager", tout << "del psort "; p->display(tout); tout << "\n";); if (p->is_psort()) { psort * _p = static_cast(p); if (_p->is_sort_wrapper()) { m_sort2psort.erase(static_cast(_p)->get_sort()); } else { m_table.erase(_p); } } del_decl_core(p); } void pdecl_manager::del_decls() { while (!m_to_delete.empty()) { pdecl * p = m_to_delete.back(); m_to_delete.pop_back(); del_decl(p); } } psort_inst_cache * pdecl_manager::mk_inst_cache(unsigned num_params) { return new (a().allocate(sizeof(psort_inst_cache))) psort_inst_cache(num_params); } void pdecl_manager::del_inst_cache(psort_inst_cache * c) { if (c) { c->finalize(*this); c->~psort_inst_cache(); a().deallocate(sizeof(psort_inst_cache), c); } } datatype_decl_plugin * pdecl_manager::get_dt_plugin() const { return static_cast(m().get_plugin(m_datatype_fid)); } void pdecl_manager::save_info(sort * s, psort_decl * d, unsigned num_args, sort * const * args) { if (m_sort2info.contains(s)) return; sort_info * info = new (a().allocate(sizeof(app_sort_info))) app_sort_info(*this, d, num_args, args); m().inc_ref(s); m_sort2info.insert(s, info); } void pdecl_manager::save_info(sort * s, psort_decl * d, unsigned num_indices, unsigned const * indices) { if (m_sort2info.contains(s)) return; sort_info * info = new (a().allocate(sizeof(indexed_sort_info))) indexed_sort_info(*this, d, num_indices, indices); m().inc_ref(s); m_sort2info.insert(s, info); } void pdecl_manager::reset_sort_info() { for (auto kv : m_sort2info) { sort * s = kv.m_key; sort_info * info = kv.m_value; m().dec_ref(s); unsigned sz = info->obj_size(); info->finalize(*this); info->~sort_info(); m_allocator.deallocate(sz, info); } m_sort2info.reset(); } void pdecl_manager::display(std::ostream & out, sort * s) const { sort_info * info = nullptr; if (m_sort2info.find(s, info)) { info->display(out, *this); return; } out << s->get_name(); } format * pdecl_manager::pp(sort * s) const { sort_info * info = nullptr; if (m_sort2info.find(s, info)) { return info->pp(*this); } unsigned num_params = s->get_num_parameters(); if (s->get_family_id() != null_family_id && num_params > 0) { // Small hack to display FP and BitVec sorts that were not explicitly referenced by the user. unsigned i = 0; for (i = 0; i < num_params; i++) { if (!s->get_parameter(i).is_int()) break; } if (i == num_params) { // all parameters are integer ptr_buffer b; b.push_back(mk_string(m(), s->get_name().str().c_str())); for (unsigned i = 0; i < num_params; i++) b.push_back(mk_unsigned(m(), s->get_parameter(i).get_int())); return mk_seq1(m(), b.begin(), b.end(), f2f(), "_"); } } return mk_string(m(), s->get_name().str().c_str()); } z3-z3-4.8.7/src/cmd_context/pdecl.h000066400000000000000000000360241356505360400167720ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: pdecl.h Abstract: Parametric declarations for SMT-LIB 2.0 + inductive data-types. Author: Leonardo de Moura (leonardo) 2011-03-02. Revision History: --*/ #ifndef PDECL_H_ #define PDECL_H_ #include "ast/ast.h" #include "util/obj_hashtable.h" #include "util/dictionary.h" #include "ast/format.h" #include "ast/datatype_decl_plugin.h" class pdecl_manager; class pdecl { protected: friend class pdecl_manager; unsigned m_id; unsigned m_num_params; unsigned m_ref_count; void inc_ref() { m_ref_count++; } void dec_ref() { SASSERT(m_ref_count > 0); --m_ref_count; } virtual bool is_psort() const { return false; } virtual size_t obj_size() const { UNREACHABLE(); return sizeof(*this); } pdecl(unsigned id, unsigned num_params):m_id(id), m_num_params(num_params), m_ref_count(0) {} virtual void finalize(pdecl_manager & m) {} virtual ~pdecl() {} public: virtual bool check_num_params(pdecl * other) const { return m_num_params == other->m_num_params; } unsigned get_num_params() const { return m_num_params; } unsigned get_id() const { return m_id; } unsigned get_ref_count() const { return m_ref_count; } unsigned hash() const { return m_id; } virtual void display(std::ostream & out) const {} virtual void reset_cache(pdecl_manager& m) {} }; class psort_inst_cache; #if defined(__APPLE__) && defined(__MACH__) // CMW: for some unknown reason, llvm on macOS does not like the name `psort' #define psort Z3_psort #endif /** \brief Parametric sorts. */ class psort : public pdecl { protected: psort_inst_cache * m_inst_cache; friend class pdecl_manager; psort(unsigned id, unsigned num_params):pdecl(id, num_params), m_inst_cache(nullptr) {} bool is_psort() const override { return true; } void finalize(pdecl_manager & m) override; ~psort() override {} virtual void cache(pdecl_manager & m, sort * const * s, sort * r); virtual sort * find(sort * const * s) const; public: virtual bool is_sort_wrapper() const { return false; } virtual sort * instantiate(pdecl_manager & m, sort * const * s) { return nullptr; } // we use hash-consing for psorts. virtual char const * hcons_kind() const { UNREACHABLE(); return nullptr; } virtual unsigned hcons_hash() const { UNREACHABLE(); return 0; } virtual bool hcons_eq(psort const * other) const { UNREACHABLE(); return false; } void reset_cache(pdecl_manager& m) override; }; // for hash consing struct psort_hash_proc { unsigned operator()(psort * p) const { return p->hcons_hash(); } }; struct psort_eq_proc { bool operator()(psort * p1, psort * p2) const { return p1->hcons_eq(p2); } }; typedef ptr_hashtable psort_table; #define PSORT_DECL_VAR_PARAMS UINT_MAX typedef enum { PSORT_BASE = 0, PSORT_USER, PSORT_BUILTIN, PSORT_DT } psort_decl_kind; class psort_decl : public pdecl { protected: friend class pdecl_manager; symbol m_name; psort_decl_kind m_psort_kind; psort_inst_cache * m_inst_cache; void cache(pdecl_manager & m, sort * const * s, sort * r); sort * find(sort * const * s); psort_decl(unsigned id, unsigned num_params, pdecl_manager & m, symbol const & n); void finalize(pdecl_manager & m) override; ~psort_decl() override {} public: virtual sort * instantiate(pdecl_manager & m, unsigned n, sort * const * s) = 0; virtual sort * instantiate(pdecl_manager & m, unsigned n, unsigned const * s) { return nullptr; } virtual sort * instantiate(pdecl_manager & m) { return instantiate(m, 0, static_cast(nullptr)); } // return true if the declaration accepts a variable number of parameters. // Only builtin declarations can have a variable number of parameters. bool has_var_params() const { return m_num_params == PSORT_DECL_VAR_PARAMS; } symbol const & get_name() const { return m_name; } void reset_cache(pdecl_manager& m) override; bool is_user_decl() const { return m_psort_kind == PSORT_USER; } bool is_builtin_decl() const { return m_psort_kind == PSORT_BUILTIN; } bool is_dt_decl() const { return m_psort_kind == PSORT_DT; } }; class psort_user_decl : public psort_decl { protected: friend class pdecl_manager; psort * m_def; psort_user_decl(unsigned id, unsigned num_params, pdecl_manager & m, symbol const & n, psort * p); size_t obj_size() const override { return sizeof(psort_user_decl); } void finalize(pdecl_manager & m) override; ~psort_user_decl() override {} public: sort * instantiate(pdecl_manager & m, unsigned n, sort * const * s) override; void display(std::ostream & out) const override; }; class psort_builtin_decl : public psort_decl { protected: friend class pdecl_manager; family_id m_fid; decl_kind m_kind; psort_builtin_decl(unsigned id, pdecl_manager & m, symbol const & n, family_id fid, decl_kind k); size_t obj_size() const override { return sizeof(psort_builtin_decl); } ~psort_builtin_decl() override {} public: sort * instantiate(pdecl_manager & m, unsigned n, sort * const * s) override; sort * instantiate(pdecl_manager & m, unsigned n, unsigned const * s) override; void display(std::ostream & out) const override; }; class psort_dt_decl : public psort_decl { protected: friend class pdecl_manager; psort_dt_decl(unsigned id, unsigned num_params, pdecl_manager & m, symbol const & n); size_t obj_size() const override { return sizeof(psort_dt_decl); } ~psort_dt_decl() override {} public: sort * instantiate(pdecl_manager & m, unsigned n, sort * const * s) override; void display(std::ostream & out) const override; }; class pdatatypes_decl; class pdatatype_decl; class pconstructor_decl; class paccessor_decl; enum ptype_kind { PTR_PSORT, // psort PTR_REC_REF, // recursive reference PTR_MISSING_REF // a symbol, it is useful for building parsers. }; class ptype { ptype_kind m_kind; union { psort * m_sort; int m_idx; }; symbol m_missing_ref; public: ptype():m_kind(PTR_PSORT), m_sort(nullptr) {} ptype(int idx):m_kind(PTR_REC_REF), m_idx(idx) {} ptype(psort * s):m_kind(PTR_PSORT), m_sort(s) {} ptype(symbol const & s):m_kind(PTR_MISSING_REF), m_missing_ref(s) {} ptype_kind kind() const { return m_kind; } psort * get_psort() const { SASSERT(kind() == PTR_PSORT); return m_sort; } int get_idx() const { SASSERT(kind() == PTR_REC_REF); return m_idx; } symbol const & get_missing_ref() const { SASSERT(kind() == PTR_MISSING_REF); return m_missing_ref; } void display(std::ostream & out, pdatatype_decl const * const * dts) const; }; class paccessor_decl : public pdecl { friend class pdecl_manager; friend class pconstructor_decl; friend class pdatatype_decl; symbol m_name; ptype m_type; paccessor_decl(unsigned id, unsigned num_params, pdecl_manager & m, symbol const & n, ptype const & r); void finalize(pdecl_manager & m) override; size_t obj_size() const override { return sizeof(paccessor_decl); } bool has_missing_refs(symbol & missing) const; bool fix_missing_refs(dictionary const & symbol2idx, symbol & missing); accessor_decl * instantiate_decl(pdecl_manager & m, sort * const * s); symbol const & get_name() const { return m_name; } ptype const & get_type() const { return m_type; } ~paccessor_decl() override {} public: void display(std::ostream & out) const override { pdecl::display(out); } void display(std::ostream & out, pdatatype_decl const * const * dts) const; }; class pconstructor_decl : public pdecl { friend class pdecl_manager; friend class pdatatype_decl; symbol m_name; symbol m_recogniser_name; ptr_vector m_accessors; pconstructor_decl(unsigned id, unsigned num_params, pdecl_manager & m, symbol const & n, symbol const & r, unsigned num_accessors, paccessor_decl * const * accessors); void finalize(pdecl_manager & m) override; size_t obj_size() const override { return sizeof(pconstructor_decl); } bool has_missing_refs(symbol & missing) const; bool fix_missing_refs(dictionary const & symbol2idx, symbol & missing); symbol const & get_name() const { return m_name; } symbol const & get_recognizer_name() const { return m_recogniser_name; } constructor_decl * instantiate_decl(pdecl_manager & m, sort * const * s); ~pconstructor_decl() override {} public: void display(std::ostream & out) const override { pdecl::display(out); } void display(std::ostream & out, pdatatype_decl const * const * dts) const; }; class pdatatype_decl : public psort_decl { friend class pdecl_manager; friend class pdatatypes_decl; ptr_vector m_constructors; pdatatypes_decl * m_parent; pdatatype_decl(unsigned id, unsigned num_params, pdecl_manager & m, symbol const & n, unsigned num_constructors, pconstructor_decl * const * constructors); void finalize(pdecl_manager & m) override; size_t obj_size() const override { return sizeof(pdatatype_decl); } bool fix_missing_refs(dictionary const & symbol2idx, symbol & missing); datatype_decl * instantiate_decl(pdecl_manager & m, sort * const * s); ~pdatatype_decl() override {} public: sort * instantiate(pdecl_manager & m, unsigned n, sort * const * s) override; void display(std::ostream & out) const override; bool has_missing_refs(symbol & missing) const; bool has_duplicate_accessors(symbol & repeated) const; bool commit(pdecl_manager& m); }; /** \brief Represents a set of parametric mutually recursive inductive data-types. */ class pdatatypes_decl : public pdecl { friend class pdecl_manager; friend class pdatatype_decl; ptr_vector m_datatypes; pdatatypes_decl(unsigned id, unsigned num_params, pdecl_manager & m, unsigned num_datatypes, pdatatype_decl * const * dts); void finalize(pdecl_manager & m) override; size_t obj_size() const override { return sizeof(pdatatypes_decl); } bool fix_missing_refs(symbol & missing); bool instantiate(pdecl_manager & m, sort * const * s); ~pdatatypes_decl() override {} public: pdatatype_decl const * const * children() const { return m_datatypes.c_ptr(); } pdatatype_decl * const * begin() const { return m_datatypes.begin(); } pdatatype_decl * const * end() const { return m_datatypes.end(); } // commit declaration bool commit(pdecl_manager& m); }; class new_datatype_eh { public: virtual ~new_datatype_eh() {} virtual void operator()(sort * dt, pdecl* pd) = 0; }; class pdecl_manager { ast_manager & m_manager; small_object_allocator & m_allocator; id_gen m_id_gen; obj_map m_sort2psort; psort_table m_table; ptr_vector m_to_delete; pdatatype_decl * m_list; family_id m_datatype_fid; new_datatype_eh * m_new_dt_eh; struct sort_info; struct app_sort_info; struct indexed_sort_info; obj_map m_sort2info; // for pretty printing sorts void init_list(); void del_decl_core(pdecl * p); void del_decl(pdecl * p); void del_decls(); psort * register_psort(psort * n); void reset_sort_info(); public: pdecl_manager(ast_manager & m); ~pdecl_manager(); ast_manager & m() const { return m_manager; } small_object_allocator & a() const { return m_allocator; } family_id get_datatype_fid() const { return m_datatype_fid; } void set_new_datatype_eh(new_datatype_eh * eh) { m_new_dt_eh = eh; } psort * mk_psort_cnst(sort * s); psort * mk_psort_var(unsigned num_params, unsigned vidx); psort * mk_psort_app(unsigned num_params, psort_decl * d, unsigned num_args, psort * const * args); psort * mk_psort_app(psort_decl * d); psort_decl * mk_psort_dt_decl(unsigned num_params, symbol const & n); psort_decl * mk_psort_user_decl(unsigned num_params, symbol const & n, psort * def); psort_decl * mk_psort_builtin_decl(symbol const & n, family_id fid, decl_kind k); paccessor_decl * mk_paccessor_decl(unsigned num_params, symbol const & s, ptype const & p); pconstructor_decl * mk_pconstructor_decl(unsigned num_params, symbol const & s, symbol const & r, unsigned num, paccessor_decl * const * as); pdatatype_decl * mk_pdatatype_decl(unsigned num_params, symbol const & s, unsigned num, pconstructor_decl * const * cs); pdatatypes_decl * mk_pdatatypes_decl(unsigned num_params, unsigned num, pdatatype_decl * const * dts); pdatatype_decl * mk_plist_decl() { if (!m_list) init_list(); return m_list; } bool fix_missing_refs(pdatatypes_decl * s, symbol & missing) { return s->fix_missing_refs(missing); } sort * instantiate_datatype(psort_decl* p, symbol const& name, unsigned n, sort * const* s); sort * instantiate(psort * s, unsigned num, sort * const * args); void lazy_dec_ref(pdecl * p) { p->dec_ref(); if (p->get_ref_count() == 0) m_to_delete.push_back(p); } template void lazy_dec_ref(unsigned num, T * const * ps) { for (unsigned i = 0; i < num; i++) lazy_dec_ref(ps[i]); } void inc_ref(pdecl * p) { if (p) { p->inc_ref(); } } void dec_ref(pdecl * p) { if (p) { lazy_dec_ref(p); del_decls(); } } template void inc_ref(unsigned num, T * const * ps) { for (unsigned i = 0; i < num; i++) inc_ref(ps[i]); } template void dec_ref(unsigned num, T * const * ps) { lazy_dec_ref(num, ps); del_decls(); } psort_inst_cache * mk_inst_cache(unsigned num_params); void del_inst_cache(psort_inst_cache * c); void notify_new_dt(sort * dt, pdecl* pd) { if (m_new_dt_eh) (*m_new_dt_eh)(dt, pd); } datatype_decl_plugin * get_dt_plugin() const; void save_info(sort * s, psort_decl * d, unsigned num_args, sort * const * args); void save_info(sort * s, psort_decl * d, unsigned num_indices, unsigned const * indices); void display(std::ostream & out, sort * s) const; format_ns::format * pp(sort * s) const; }; typedef obj_ref pdecl_ref; typedef obj_ref psort_ref; typedef obj_ref paccessor_decl_ref; typedef obj_ref pconstructor_decl_ref; typedef obj_ref pdatatype_decl_ref; typedef obj_ref pdatatypes_decl_ref; typedef ref_vector pdecl_ref_vector; typedef ref_vector psort_decl_ref_vector; typedef ref_vector psort_ref_vector; typedef ref_buffer paccessor_decl_ref_buffer; typedef ref_buffer pconstructor_decl_ref_buffer; typedef ref_buffer pdatatype_decl_ref_buffer; typedef ref_buffer pdatatypes_decl_ref_buffer; #endif z3-z3-4.8.7/src/cmd_context/simplify_cmd.cpp000066400000000000000000000113351356505360400207130ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: simplify_cmd.cpp Abstract: SMT2 front-end 'simplify' command. Author: Leonardo (leonardo) 2011-04-20 Notes: --*/ #include "cmd_context/cmd_context.h" #include "ast/rewriter/th_rewriter.h" #include "ast/shared_occs.h" #include "ast/ast_smt_pp.h" #include "ast/for_each_expr.h" #include "cmd_context/parametric_cmd.h" #include "util/scoped_timer.h" #include "util/scoped_ctrl_c.h" #include "util/cancel_eh.h" #include class simplify_cmd : public parametric_cmd { expr * m_target; public: simplify_cmd(char const * name = "simplify"):parametric_cmd(name) {} char const * get_usage() const override { return " ( )*"; } char const * get_main_descr() const override { return "simplify the given term using builtin theory simplification rules."; } void init_pdescrs(cmd_context & ctx, param_descrs & p) override { th_rewriter::get_param_descrs(p); insert_timeout(p); p.insert("print", CPK_BOOL, "(default: true) print the simplified term."); p.insert("print_proofs", CPK_BOOL, "(default: false) print a proof showing the original term is equal to the resultant one."); p.insert("print_statistics", CPK_BOOL, "(default: false) print statistics."); } ~simplify_cmd() override { } void prepare(cmd_context & ctx) override { parametric_cmd::prepare(ctx); m_target = nullptr; } cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { if (m_target == nullptr) return CPK_EXPR; return parametric_cmd::next_arg_kind(ctx); } void set_next_arg(cmd_context & ctx, expr * arg) override { m_target = arg; } void execute(cmd_context & ctx) override { if (m_target == nullptr) throw cmd_exception("invalid simplify command, argument expected"); expr_ref r(ctx.m()); proof_ref pr(ctx.m()); if (m_params.get_bool("som", false)) m_params.set_bool("flat", true); th_rewriter s(ctx.m(), m_params); th_solver solver(ctx); s.set_solver(alloc(th_solver, ctx)); unsigned cache_sz; unsigned num_steps = 0; unsigned timeout = m_params.get_uint("timeout", UINT_MAX); unsigned rlimit = m_params.get_uint("rlimit", UINT_MAX); bool failed = false; cancel_eh eh(ctx.m().limit()); { scoped_rlimit _rlimit(ctx.m().limit(), rlimit); scoped_ctrl_c ctrlc(eh); scoped_timer timer(timeout, &eh); cmd_context::scoped_watch sw(ctx); try { s(m_target, r, pr); } catch (z3_error & ex) { throw ex; } catch (z3_exception & ex) { ctx.regular_stream() << "(error \"simplifier failed: " << ex.msg() << "\")" << std::endl; failed = true; r = m_target; } cache_sz = s.get_cache_size(); num_steps = s.get_num_steps(); s.cleanup(); } if (m_params.get_bool("print", true)) { ctx.display(ctx.regular_stream(), r); ctx.regular_stream() << std::endl; } if (!failed && m_params.get_bool("print_proofs", false)) { ast_smt_pp pp(ctx.m()); pp.set_logic(ctx.get_logic()); pp.display_expr_smt2(ctx.regular_stream(), pr.get()); ctx.regular_stream() << std::endl; } if (m_params.get_bool("print_statistics", false)) { shared_occs s1(ctx.m()); if (!failed) s1(r); unsigned long long max_mem = memory::get_max_used_memory(); unsigned long long mem = memory::get_allocation_size(); ctx.regular_stream() << "(:time " << std::fixed << std::setprecision(2) << ctx.get_seconds() << " :num-steps " << num_steps << " :memory " << std::fixed << std::setprecision(2) << static_cast(mem)/static_cast(1024*1024) << " :max-memory " << std::fixed << std::setprecision(2) << static_cast(max_mem)/static_cast(1024*1024) << " :cache-size: " << cache_sz << " :num-nodes-before " << get_num_exprs(m_target); if (!failed) ctx.regular_stream() << " :num-shared " << s1.num_shared() << " :num-nodes " << get_num_exprs(r); ctx.regular_stream() << ")" << std::endl; } } }; void install_simplify_cmd(cmd_context & ctx, char const * cmd_name) { ctx.insert(alloc(simplify_cmd, cmd_name)); } z3-z3-4.8.7/src/cmd_context/simplify_cmd.h000066400000000000000000000005341356505360400203570ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: simplify_cmd.h Abstract: SMT2 front-end 'simplify' command. Author: Leonardo (leonardo) 2011-04-20 Notes: --*/ #ifndef SIMPLIFY_CMD_H_ #define SIMPLIFY_CMD_H_ class cmd_context; void install_simplify_cmd(cmd_context & ctx, char const * cmd_name = "simplify"); #endif z3-z3-4.8.7/src/cmd_context/tactic_cmds.cpp000066400000000000000000001060171356505360400205130ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: tactic_cmds.cpp Abstract: Support for tactics in SMT 2.0 frontend. Author: Leonardo (leonardo) 2011-10-20 Notes: --*/ #include #include "cmd_context/tactic_cmds.h" #include "cmd_context/cmd_context.h" #include "cmd_context/cmd_util.h" #include "cmd_context/parametric_cmd.h" #include "util/scoped_timer.h" #include "util/scoped_ctrl_c.h" #include "util/cancel_eh.h" #include "model/model_smt2_pp.h" #include "ast/ast_smt2_pp.h" #include "tactic/tactic.h" #include "tactic/tactical.h" #include "tactic/probe.h" #include "solver/check_sat_result.h" #include "cmd_context/cmd_context_to_goal.h" #include "cmd_context/echo_tactic.h" probe_info::probe_info(symbol const & n, char const * d, probe * p): m_name(n), m_descr(d), m_probe(p) { } probe_info::~probe_info() { } class declare_tactic_cmd : public cmd { symbol m_name; sexpr * m_decl; public: declare_tactic_cmd(): cmd("declare-tactic"), m_decl(nullptr) { } char const * get_usage() const override { return " "; } char const * get_descr(cmd_context & ctx) const override { return "declare a new tactic, use (help-tactic) for the tactic language syntax."; } unsigned get_arity() const override { return 2; } void prepare(cmd_context & ctx) override { m_name = symbol::null; m_decl = nullptr; } cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { if (m_name == symbol::null) return CPK_SYMBOL; return CPK_SEXPR; } void set_next_arg(cmd_context & ctx, symbol const & s) override { m_name = s; } void set_next_arg(cmd_context & ctx, sexpr * n) override { m_decl = n; } void execute(cmd_context & ctx) override { tactic_ref t = sexpr2tactic(ctx, m_decl); // make sure the tactic is well formed. ctx.insert_user_tactic(m_name, m_decl); } }; ATOMIC_CMD(get_user_tactics_cmd, "get-user-tactics", "display tactics defined using the declare-tactic command.", { ctx.regular_stream() << "("; std::ostringstream buf; cmd_context::user_tactic_iterator it = ctx.begin_user_tactics(); cmd_context::user_tactic_iterator end = ctx.end_user_tactics(); for (bool first = true; it != end; ++it) { if (first) first = false; else buf << "\n "; buf << "(declare-tactic " << it->m_key << " "; it->m_value->display(buf); buf << ")"; } std::string r = buf.str(); ctx.regular_stream() << escaped(r.c_str()); ctx.regular_stream() << ")\n"; }); void help_tactic(cmd_context & ctx) { std::ostringstream buf; buf << "combinators:\n"; buf << "- (and-then +) executes the given tactics sequentially.\n"; buf << "- (or-else +) tries the given tactics in sequence until one of them succeeds (i.e., the first that doesn't fail).\n"; buf << "- (par-or +) executes the given tactics in parallel until one of them succeeds (i.e., the first that doesn't fail).\n"; buf << "- (par-then ) executes tactic1 and then tactic2 to every subgoal produced by tactic1. All subgoals are processed in parallel.\n"; buf << "- (try-for ) executes the given tactic for at most milliseconds, it fails if the execution takes more than milliseconds.\n"; buf << "- (if ) if evaluates to true, then execute the first tactic. Otherwise execute the second.\n"; buf << "- (when ) shorthand for (if skip).\n"; buf << "- (fail-if ) fail if evaluates to true.\n"; buf << "- (using-params *) executes the given tactic using the given attributes, where ::= . ! is a syntax sugar for using-params.\n"; buf << "builtin tactics:\n"; cmd_context::tactic_cmd_iterator it = ctx.begin_tactic_cmds(); cmd_context::tactic_cmd_iterator end = ctx.end_tactic_cmds(); for (; it != end; ++it) { tactic_cmd * cmd = *it; buf << "- " << cmd->get_name() << " " << cmd->get_descr() << "\n"; tactic_ref t = cmd->mk(ctx.m()); param_descrs descrs; t->collect_param_descrs(descrs); descrs.display(buf, 4); } buf << "builtin probes:\n"; cmd_context::probe_iterator it2 = ctx.begin_probes(); cmd_context::probe_iterator end2 = ctx.end_probes(); for (; it2 != end2; ++it2) { probe_info * pinfo = *it2; buf << "- " << pinfo->get_name() << " " << pinfo->get_descr() << "\n"; } ctx.regular_stream() << "\"" << escaped(buf.str().c_str()) << "\"\n"; } ATOMIC_CMD(help_tactic_cmd, "help-tactic", "display the tactic combinators and primitives.", help_tactic(ctx);); class exec_given_tactic_cmd : public parametric_cmd { protected: sexpr * m_tactic; public: exec_given_tactic_cmd(char const * name): parametric_cmd(name) { } char const * get_usage() const override { return " ( )*"; } void prepare(cmd_context & ctx) override { parametric_cmd::prepare(ctx); m_tactic = nullptr; } cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { if (m_tactic == nullptr) return CPK_SEXPR; return parametric_cmd::next_arg_kind(ctx); } void set_next_arg(cmd_context & ctx, sexpr * arg) override { m_tactic = arg; } void init_pdescrs(cmd_context & ctx, param_descrs & p) override { insert_timeout(p); insert_max_memory(p); p.insert("print_statistics", CPK_BOOL, "(default: false) print statistics."); } void display_statistics(cmd_context & ctx, tactic * t) { statistics stats; get_memory_statistics(stats); get_rlimit_statistics(ctx.m().limit(), stats); stats.update("time", ctx.get_seconds()); t->collect_statistics(stats); stats.display_smt2(ctx.regular_stream()); } }; struct check_sat_tactic_result : public simple_check_sat_result { public: labels_vec labels; check_sat_tactic_result(ast_manager & m) : simple_check_sat_result(m) { } void get_labels(svector & r) override { r.append(labels); } virtual void add_labels(svector & r) { labels.append(r); } }; class check_sat_using_tactict_cmd : public exec_given_tactic_cmd { public: check_sat_using_tactict_cmd(): exec_given_tactic_cmd("check-sat-using") { } char const * get_main_descr() const override { return "check if the current context is satisfiable using the given tactic, use (help-tactic) for the tactic language syntax."; } void init_pdescrs(cmd_context & ctx, param_descrs & p) override { exec_given_tactic_cmd::init_pdescrs(ctx, p); p.insert("print_unsat_core", CPK_BOOL, "(default: false) print unsatisfiable core."); p.insert("print_proof", CPK_BOOL, "(default: false) print proof."); p.insert("print_model", CPK_BOOL, "(default: false) print model."); } void execute(cmd_context & ctx) override { if (!m_tactic) { throw cmd_exception("check-sat-using needs a tactic argument"); } if (ctx.ignore_check()) return; params_ref p = ctx.params().merge_default_params(ps()); tactic_ref tref = using_params(sexpr2tactic(ctx, m_tactic), p); tref->set_logic(ctx.get_logic()); ast_manager & m = ctx.m(); unsigned timeout = p.get_uint("timeout", ctx.params().m_timeout); unsigned rlimit = p.get_uint("rlimit", ctx.params().rlimit()); labels_vec labels; goal_ref g = alloc(goal, m, ctx.produce_proofs(), ctx.produce_models(), ctx.produce_unsat_cores()); assert_exprs_from(ctx, *g); TRACE("check_sat_using", g->display(tout);); model_ref md; proof_ref pr(m); expr_dependency_ref core(m); std::string reason_unknown; ref result = alloc(check_sat_tactic_result, m); ctx.set_check_sat_result(result.get()); { tactic & t = *tref; cancel_eh eh(m.limit()); { scoped_rlimit _rlimit(m.limit(), rlimit); scoped_ctrl_c ctrlc(eh); scoped_timer timer(timeout, &eh); cmd_context::scoped_watch sw(ctx); lbool r = l_undef; try { r = check_sat(t, g, md, result->labels, pr, core, reason_unknown); ctx.display_sat_result(r); result->set_status(r); if (r == l_undef) { if (!reason_unknown.empty()) { result->m_unknown = reason_unknown; // ctx.diagnostic_stream() << "\"" << escaped(reason_unknown.c_str(), true) << "\"" << std::endl; } else { result->m_unknown = "unknown"; } } } catch (z3_error & ex) { throw ex; } catch (z3_exception & ex) { result->set_status(l_undef); result->m_unknown = ex.msg(); ctx.regular_stream() << "(error \"tactic failed: " << ex.msg() << "\")" << std::endl; } ctx.validate_check_sat_result(r); } t.collect_statistics(result->m_stats); } if (ctx.produce_unsat_cores()) { ptr_vector core_elems; m.linearize(core, core_elems); result->m_core.append(core_elems.size(), core_elems.c_ptr()); if (p.get_bool("print_unsat_core", false)) { ctx.regular_stream() << "(unsat-core"; for (expr * e : core_elems) { ctx.regular_stream() << " "; ctx.display(ctx.regular_stream(), e); } ctx.regular_stream() << ")" << std::endl; } } if (ctx.produce_models() && md) { result->m_model = md; if (p.get_bool("print_model", false)) { ctx.regular_stream() << "(model " << std::endl; model_smt2_pp(ctx.regular_stream(), ctx, *md, 2); ctx.regular_stream() << ")" << std::endl; } if (result->status() == l_true) ctx.validate_model(); } if (ctx.produce_proofs() && pr) { result->m_proof = pr; if (p.get_bool("print_proof", false)) { ctx.regular_stream() << mk_ismt2_pp(pr, m) << "\n"; } } if (p.get_bool("print_statistics", false)) display_statistics(ctx, tref.get()); } }; class apply_tactic_cmd : public exec_given_tactic_cmd { public: apply_tactic_cmd(): exec_given_tactic_cmd("apply") { } char const * get_main_descr() const override { return "apply the given tactic to the current context, and print the resultant set of goals."; } void init_pdescrs(cmd_context & ctx, param_descrs & p) override { p.insert("print", CPK_BOOL, "(default: true) print resultant goals."); #ifndef _EXTERNAL_RELEASE p.insert("print_proof", CPK_BOOL, "(default: false) print proof associated with each assertion."); #endif p.insert("print_model_converter", CPK_BOOL, "(default: false) print model converter."); p.insert("print_benchmark", CPK_BOOL, "(default: false) display resultant goals as a SMT2 benchmark."); p.insert("print_dependencies", CPK_BOOL, "(default: false) print dependencies when displaying the resultant set of goals."); exec_given_tactic_cmd::init_pdescrs(ctx, p); } void execute(cmd_context & ctx) override { if (!m_tactic) { throw cmd_exception("apply needs a tactic argument"); } params_ref p = ctx.params().merge_default_params(ps()); tactic_ref tref = using_params(sexpr2tactic(ctx, m_tactic), p); { tactic & t = *(tref.get()); ast_manager & m = ctx.m(); goal_ref g = alloc(goal, m, ctx.produce_proofs(), ctx.produce_models(), ctx.produce_unsat_cores()); assert_exprs_from(ctx, *g); unsigned timeout = p.get_uint("timeout", ctx.params().m_timeout); unsigned rlimit = p.get_uint("rlimit", ctx.params().rlimit()); goal_ref_buffer result_goals; std::string reason_unknown; bool failed = false; cancel_eh eh(m.limit()); { scoped_rlimit _rlimit(m.limit(), rlimit); scoped_ctrl_c ctrlc(eh); scoped_timer timer(timeout, &eh); cmd_context::scoped_watch sw(ctx); try { exec(t, g, result_goals); } catch (tactic_exception & ex) { ctx.regular_stream() << "(error \"tactic failed: " << ex.msg() << "\")" << std::endl; failed = true; } } if (!failed && p.get_bool("print", true)) { bool print_dependencies = p.get_bool("print_dependencies", false); ctx.regular_stream() << "(goals\n"; unsigned sz = result_goals.size(); for (unsigned i = 0; i < sz; i++) { if (print_dependencies) result_goals[i]->display_with_dependencies(ctx); else result_goals[i]->display(ctx); } ctx.regular_stream() << ")\n"; } #ifndef _EXTERNAL_RELEASE if (!failed && ctx.produce_proofs() && p.get_bool("print_proof", false)) { // TODO } #endif if (!failed && p.get_bool("print_benchmark", false)) { unsigned num_goals = result_goals.size(); SASSERT(num_goals > 0); if (num_goals == 1) { // easy case goal * fg = result_goals[0]; unsigned sz = fg->size(); ptr_buffer assertions; for (unsigned i = 0; i < sz; i++) { assertions.push_back(fg->form(i)); } ctx.display_smt2_benchmark(ctx.regular_stream(), assertions.size(), assertions.c_ptr()); } else { // create a big OR expr_ref_buffer or_args(m); ptr_vector formulas; for (unsigned i = 0; i < num_goals; i++) { formulas.reset(); result_goals[i]->get_formulas(formulas); if (formulas.size() == 1) or_args.push_back(formulas[0]); else or_args.push_back(m.mk_and(formulas.size(), formulas.c_ptr())); } expr_ref assertion_ref(m); assertion_ref = m.mk_or(or_args.size(), or_args.c_ptr()); expr * assertions[1] = { assertion_ref.get() }; ctx.display_smt2_benchmark(ctx.regular_stream(), 1, assertions); } } if (!failed && g->mc() && p.get_bool("print_model_converter", false)) g->mc()->display(ctx.regular_stream()); if (p.get_bool("print_statistics", false)) display_statistics(ctx, tref.get()); } } }; // The install_tactics procedure is automatically generated for every // component that includes the cmd_context & tactic modules. void install_tactics(tactic_manager & ctx); void install_core_tactic_cmds(cmd_context & ctx) { ctx.insert(alloc(declare_tactic_cmd)); ctx.insert(alloc(get_user_tactics_cmd)); ctx.insert(alloc(help_tactic_cmd)); ctx.insert(alloc(check_sat_using_tactict_cmd)); ctx.insert(alloc(apply_tactic_cmd)); install_tactics(ctx); } static tactic * mk_and_then(cmd_context & ctx, sexpr * n) { SASSERT(n->is_composite()); unsigned num_children = n->get_num_children(); if (num_children < 2) throw cmd_exception("invalid and-then combinator, at least one argument expected", n->get_line(), n->get_pos()); if (num_children == 2) return sexpr2tactic(ctx, n->get_child(1)); tactic_ref_buffer args; for (unsigned i = 1; i < num_children; i++) args.push_back(sexpr2tactic(ctx, n->get_child(i))); return and_then(args.size(), args.c_ptr()); } static tactic * mk_or_else(cmd_context & ctx, sexpr * n) { SASSERT(n->is_composite()); unsigned num_children = n->get_num_children(); if (num_children < 2) throw cmd_exception("invalid or-else combinator, at least one argument expected", n->get_line(), n->get_pos()); if (num_children == 2) return sexpr2tactic(ctx, n->get_child(1)); tactic_ref_buffer args; for (unsigned i = 1; i < num_children; i++) args.push_back(sexpr2tactic(ctx, n->get_child(i))); return or_else(args.size(), args.c_ptr()); } static tactic * mk_par(cmd_context & ctx, sexpr * n) { SASSERT(n->is_composite()); unsigned num_children = n->get_num_children(); if (num_children < 2) throw cmd_exception("invalid par-or combinator, at least one argument expected", n->get_line(), n->get_pos()); if (num_children == 2) return sexpr2tactic(ctx, n->get_child(1)); tactic_ref_buffer args; for (unsigned i = 1; i < num_children; i++) args.push_back(sexpr2tactic(ctx, n->get_child(i))); return par(args.size(), args.c_ptr()); } static tactic * mk_par_then(cmd_context & ctx, sexpr * n) { SASSERT(n->is_composite()); unsigned num_children = n->get_num_children(); if (num_children < 2) throw cmd_exception("invalid par-then combinator, at least one argument expected", n->get_line(), n->get_pos()); if (num_children == 2) return sexpr2tactic(ctx, n->get_child(1)); tactic_ref_buffer args; for (unsigned i = 1; i < num_children; i++) args.push_back(sexpr2tactic(ctx, n->get_child(i))); return par_and_then(args.size(), args.c_ptr()); } static tactic * mk_try_for(cmd_context & ctx, sexpr * n) { SASSERT(n->is_composite()); unsigned num_children = n->get_num_children(); if (num_children != 3) throw cmd_exception("invalid try-for combinator, two arguments expected", n->get_line(), n->get_pos()); if (!n->get_child(2)->is_numeral() || !n->get_child(2)->get_numeral().is_unsigned()) throw cmd_exception("invalid try-for combinator, second argument must be an unsigned integer", n->get_line(), n->get_pos()); tactic * t = sexpr2tactic(ctx, n->get_child(1)); unsigned timeout = n->get_child(2)->get_numeral().get_unsigned(); return try_for(t, timeout); } static tactic * mk_repeat(cmd_context & ctx, sexpr * n) { SASSERT(n->is_composite()); unsigned num_children = n->get_num_children(); if (num_children != 3 && num_children != 2) throw cmd_exception("invalid repeat combinator, one or two arguments expected", n->get_line(), n->get_pos()); unsigned max = UINT_MAX; if (num_children == 3) { if (!n->get_child(2)->is_numeral() || !n->get_child(2)->get_numeral().is_unsigned()) throw cmd_exception("invalid repeat combinator, second argument must be an unsigned integer", n->get_line(), n->get_pos()); max = n->get_child(2)->get_numeral().get_unsigned(); } tactic * t = sexpr2tactic(ctx, n->get_child(1)); return repeat(t, max); } static tactic * mk_using_params(cmd_context & ctx, sexpr * n) { SASSERT(n->is_composite()); unsigned num_children = n->get_num_children(); if (num_children < 2) throw cmd_exception("invalid using-params combinator, at least one argument expected", n->get_line(), n->get_pos()); if (num_children == 2) return sexpr2tactic(ctx, n->get_child(1)); tactic_ref t = sexpr2tactic(ctx, n->get_child(1)); param_descrs descrs; t->collect_param_descrs(descrs); params_ref p; unsigned i = 2; while (i < num_children) { sexpr * c = n->get_child(i); i++; if (!c->is_keyword()) throw cmd_exception("invalid using-params combinator, keyword expected", c->get_line(), c->get_pos()); if (i == num_children) throw cmd_exception("invalid using-params combinator, parameter value expected", c->get_line(), c->get_pos()); symbol param_name = symbol(norm_param_name(c->get_symbol()).c_str()); c = n->get_child(i); i++; switch (descrs.get_kind_in_module(param_name)) { case CPK_INVALID: throw cmd_exception("invalid using-params combinator, unknown parameter ", param_name, c->get_line(), c->get_pos()); case CPK_BOOL: if (!c->is_symbol() || (c->get_symbol() != "true" && c->get_symbol() != "false")) throw cmd_exception("invalid parameter value, true or false expected", c->get_line(), c->get_pos()); p.set_bool(param_name, c->get_symbol() == "true"); break; case CPK_UINT: if (!c->is_numeral() || !c->get_numeral().is_unsigned()) throw cmd_exception("invalid parameter value, unsigned integer expected", c->get_line(), c->get_pos()); p.set_uint(param_name, c->get_numeral().get_unsigned()); break; case CPK_NUMERAL: if (!c->is_numeral()) throw cmd_exception("invalid parameter value, numeral expected", c->get_line(), c->get_pos()); p.set_rat(param_name, c->get_numeral()); break; case CPK_SYMBOL: if (!c->is_symbol()) throw cmd_exception("invalid parameter value, symbol expected", c->get_line(), c->get_pos()); p.set_sym(param_name, c->get_symbol()); break; case CPK_DOUBLE: if (!c->is_numeral()) throw cmd_exception("invalid parameter value, numeral expected", c->get_line(), c->get_pos()); p.set_double(param_name, c->get_numeral().get_double()); break; default: throw cmd_exception("invalid using-params combinator, unsupported parameter kind"); } } return using_params(t.get(), p); } static tactic * mk_if(cmd_context & ctx, sexpr * n) { SASSERT(n->is_composite()); unsigned num_children = n->get_num_children(); if (num_children != 4) throw cmd_exception("invalid if/conditional combinator, three arguments expected", n->get_line(), n->get_pos()); probe_ref c = sexpr2probe(ctx, n->get_child(1)); tactic_ref t = sexpr2tactic(ctx, n->get_child(2)); tactic_ref e = sexpr2tactic(ctx, n->get_child(3)); return cond(c.get(), t.get(), e.get()); } static tactic * mk_fail_if(cmd_context & ctx, sexpr * n) { SASSERT(n->is_composite()); unsigned num_children = n->get_num_children(); if (num_children != 2) throw cmd_exception("invalid fail-if tactic, one argument expected", n->get_line(), n->get_pos()); probe_ref c = sexpr2probe(ctx, n->get_child(1)); return fail_if(c.get()); } static tactic * mk_when(cmd_context & ctx, sexpr * n) { SASSERT(n->is_composite()); unsigned num_children = n->get_num_children(); if (num_children != 3) throw cmd_exception("invalid when combinator, two arguments expected", n->get_line(), n->get_pos()); probe_ref c = sexpr2probe(ctx, n->get_child(1)); tactic_ref t = sexpr2tactic(ctx, n->get_child(2)); return cond(c.get(), t.get(), mk_skip_tactic()); } static tactic * mk_echo(cmd_context & ctx, sexpr * n) { SASSERT(n->is_composite()); unsigned num_children = n->get_num_children(); if (num_children < 2) throw cmd_exception("invalid echo tactic, must have at least one argument", n->get_line(), n->get_pos()); tactic_ref res; for (unsigned i = 1; i < num_children; i++) { sexpr * curr = n->get_child(i); bool last = (i == num_children - 1); tactic * t; if (curr->is_string()) t = mk_echo_tactic(ctx, curr->get_string().c_str(), last); else t = mk_probe_value_tactic(ctx, nullptr, sexpr2probe(ctx, curr), last); tactic * new_res; if (res.get() == nullptr) new_res = t; else new_res = and_then(res.get(), t); if (last) return new_res; res = new_res; } UNREACHABLE(); return nullptr; } static tactic * mk_fail_if_branching(cmd_context & ctx, sexpr * n) { SASSERT(n->is_composite()); unsigned num_children = n->get_num_children(); if (num_children != 3 && num_children != 2) throw cmd_exception("invalid fail-if-branching combinator, one or two arguments expected", n->get_line(), n->get_pos()); unsigned threshold = 1; if (num_children == 3) { if (!n->get_child(2)->is_numeral() || !n->get_child(2)->get_numeral().is_unsigned()) throw cmd_exception("invalid fail-if-branching combinator, second argument must be an unsigned integer", n->get_line(), n->get_pos()); threshold = n->get_child(2)->get_numeral().get_unsigned(); } tactic * t = sexpr2tactic(ctx, n->get_child(1)); return fail_if_branching(t, threshold); } static tactic * mk_if_no_proofs(cmd_context & ctx, sexpr * n) { SASSERT(n->is_composite()); unsigned num_children = n->get_num_children(); if (num_children != 2) throw cmd_exception("invalid if-no-proofs combinator, one argument expected", n->get_line(), n->get_pos()); tactic * t = sexpr2tactic(ctx, n->get_child(1)); return if_no_proofs(t); } static tactic * mk_if_no_models(cmd_context & ctx, sexpr * n) { SASSERT(n->is_composite()); unsigned num_children = n->get_num_children(); if (num_children != 2) throw cmd_exception("invalid if-no-models combinator, one argument expected", n->get_line(), n->get_pos()); tactic * t = sexpr2tactic(ctx, n->get_child(1)); return if_no_models(t); } static tactic * mk_if_no_unsat_cores(cmd_context & ctx, sexpr * n) { SASSERT(n->is_composite()); unsigned num_children = n->get_num_children(); if (num_children != 2) throw cmd_exception("invalid if-no-unsat-cores combinator, one argument expected", n->get_line(), n->get_pos()); tactic * t = sexpr2tactic(ctx, n->get_child(1)); return if_no_unsat_cores(t); } static tactic * mk_skip_if_failed(cmd_context & ctx, sexpr * n) { SASSERT(n->is_composite()); unsigned num_children = n->get_num_children(); if (num_children != 2) throw cmd_exception("invalid skip-if-failed combinator, one argument expected", n->get_line(), n->get_pos()); tactic * t = sexpr2tactic(ctx, n->get_child(1)); return skip_if_failed(t); } tactic * sexpr2tactic(cmd_context & ctx, sexpr * n) { if (n->is_symbol()) { tactic_cmd * cmd = ctx.find_tactic_cmd(n->get_symbol()); if (cmd != nullptr) return cmd->mk(ctx.m()); sexpr * decl = ctx.find_user_tactic(n->get_symbol()); if (decl != nullptr) return sexpr2tactic(ctx, decl); throw cmd_exception("invalid tactic, unknown tactic ", n->get_symbol(), n->get_line(), n->get_pos()); } else if (n->is_composite()) { unsigned num_children = n->get_num_children(); if (num_children == 0) throw cmd_exception("invalid tactic, arguments expected", n->get_line(), n->get_pos()); sexpr * head = n->get_child(0); if (!head->is_symbol()) throw cmd_exception("invalid tactic, symbol expected", n->get_line(), n->get_pos()); symbol const & cmd_name = head->get_symbol(); if (cmd_name == "and-then" || cmd_name == "then") return mk_and_then(ctx, n); else if (cmd_name == "or-else") return mk_or_else(ctx, n); else if (cmd_name == "par") return mk_par(ctx, n); else if (cmd_name == "par-or") return mk_par(ctx, n); else if (cmd_name == "par-then") return mk_par_then(ctx, n); else if (cmd_name == "try-for") return mk_try_for(ctx, n); else if (cmd_name == "repeat") return mk_repeat(ctx, n); else if (cmd_name == "if" || cmd_name == "ite" || cmd_name == "cond") return mk_if(ctx, n); else if (cmd_name == "fail-if") return mk_fail_if(ctx, n); else if (cmd_name == "fail-if-branching") return mk_fail_if_branching(ctx, n); else if (cmd_name == "when") return mk_when(ctx, n); else if (cmd_name == "!" || cmd_name == "using-params" || cmd_name == "with") return mk_using_params(ctx, n); else if (cmd_name == "echo") return mk_echo(ctx, n); else if (cmd_name == "if-no-proofs") return mk_if_no_proofs(ctx, n); else if (cmd_name == "if-no-models") return mk_if_no_models(ctx, n); else if (cmd_name == "if-no-unsat-cores") return mk_if_no_unsat_cores(ctx, n); else if (cmd_name == "skip-if-failed") return mk_skip_if_failed(ctx, n); else throw cmd_exception("invalid tactic, unknown tactic combinator ", cmd_name, n->get_line(), n->get_pos()); } else { throw cmd_exception("invalid tactic, unexpected input", n->get_line(), n->get_pos()); } } static probe * mk_not_probe (cmd_context & ctx, sexpr * n) { SASSERT(n->is_composite()); unsigned num_children = n->get_num_children(); if (num_children != 2) throw cmd_exception("invalid probe expression, one argument expected", n->get_line(), n->get_pos()); return mk_not(sexpr2probe(ctx, n->get_child(1))); } #define MK_BIN_PROBE(NAME) \ static probe * NAME ## _probe (cmd_context & ctx, sexpr * n) { \ SASSERT(n->is_composite()); \ unsigned num_children = n->get_num_children(); \ if (num_children != 3) \ throw cmd_exception("invalid probe expression, two arguments expected", n->get_line(), n->get_pos()); \ ref p1 = sexpr2probe(ctx, n->get_child(1)); \ ref p2 = sexpr2probe(ctx, n->get_child(2)); \ return NAME(p1.get(), p2.get()); \ } MK_BIN_PROBE(mk_eq); MK_BIN_PROBE(mk_le); MK_BIN_PROBE(mk_lt); MK_BIN_PROBE(mk_ge); MK_BIN_PROBE(mk_gt); MK_BIN_PROBE(mk_implies); MK_BIN_PROBE(mk_div); MK_BIN_PROBE(mk_sub); #define MK_NARY_PROBE(NAME) \ static probe * NAME ## _probe(cmd_context & ctx, sexpr * n) { \ SASSERT(n->is_composite()); \ unsigned num_children = n->get_num_children(); \ if (num_children < 2) \ throw cmd_exception("invalid probe, at least one argument expected", n->get_line(), n->get_pos()); \ probe * r = sexpr2probe(ctx, n->get_child(1)); \ if (num_children == 2) \ return r; \ ref prev = r; \ unsigned i = 1; \ while (true) { \ r = NAME(prev.get(), sexpr2probe(ctx, n->get_child(i))); \ if (i == num_children - 1) \ return r; \ i++; \ prev = r; \ } \ } MK_NARY_PROBE(mk_and); MK_NARY_PROBE(mk_or); MK_NARY_PROBE(mk_add); MK_NARY_PROBE(mk_mul); probe * sexpr2probe(cmd_context & ctx, sexpr * n) { if (n->is_symbol()) { probe_info * pinfo = ctx.find_probe(n->get_symbol()); if (pinfo != nullptr) return pinfo->get(); throw cmd_exception("invalid probe, unknown builtin probe ", n->get_symbol(), n->get_line(), n->get_pos()); } else if (n->is_numeral()) { rational const & v = n->get_numeral(); if (!v.is_int32()) throw cmd_exception("invalid probe, constant is too big to fit in a fixed size integer", n->get_line(), n->get_pos()); return mk_const_probe(static_cast(v.get_int64())); } else if (n->is_composite()) { unsigned num_children = n->get_num_children(); if (num_children == 0) throw cmd_exception("invalid probe, arguments expected", n->get_line(), n->get_pos()); sexpr * head = n->get_child(0); if (!head->is_symbol()) throw cmd_exception("invalid probe, symbol expected", n->get_line(), n->get_pos()); symbol const & p_name = head->get_symbol(); if (p_name == "=") return mk_eq_probe(ctx, n); else if (p_name == "<=") return mk_le_probe(ctx, n); else if (p_name == ">=") return mk_ge_probe(ctx, n); else if (p_name == "<") return mk_lt_probe(ctx, n); else if (p_name == ">") return mk_gt_probe(ctx, n); else if (p_name == "and") return mk_and_probe(ctx, n); else if (p_name == "or") return mk_or_probe(ctx, n); else if (p_name == "=>" || p_name == "implies") return mk_implies_probe(ctx, n); else if (p_name == "not") return mk_not_probe(ctx, n); else if (p_name == "*") return mk_mul_probe(ctx, n); else if (p_name == "+") return mk_add_probe(ctx, n); else if (p_name == "-") return mk_sub_probe(ctx, n); else if (p_name == "/") return mk_div_probe(ctx, n); else throw cmd_exception("invalid probe, unknown probe expression ", p_name, n->get_line(), n->get_pos()); } else { throw cmd_exception("invalid probe, unexpected input", n->get_line(), n->get_pos()); } } z3-z3-4.8.7/src/cmd_context/tactic_cmds.h000066400000000000000000000025441356505360400201600ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: tactic_cmds.h Abstract: Support for tactics in SMT 2.0 frontend. Author: Leonardo (leonardo) 2011-11-20 Notes: --*/ #ifndef TACTIC_CMDS_H_ #define TACTIC_CMDS_H_ #include "ast/ast.h" #include "util/params.h" #include "util/cmd_context_types.h" #include "util/ref.h" class tactic; class probe; typedef tactic* (*tactic_factory)(ast_manager&, const params_ref&); class tactic_cmd { symbol m_name; char const * m_descr; tactic_factory m_factory; public: tactic_cmd(symbol const & n, char const * d, tactic_factory f): m_name(n), m_descr(d), m_factory(f) {} symbol get_name() const { return m_name; } char const * get_descr() const { return m_descr; } tactic * mk(ast_manager & m) { return m_factory(m, params_ref()); } }; void install_core_tactic_cmds(cmd_context & ctx); tactic * sexpr2tactic(cmd_context & ctx, sexpr * n); class probe_info { symbol m_name; char const * m_descr; ref m_probe; public: probe_info(symbol const & n, char const * d, probe * p); ~probe_info(); symbol get_name() const { return m_name; } char const * get_descr() const { return m_descr; } probe * get() const { return m_probe.get(); } }; probe * sexpr2probe(cmd_context & ctx, sexpr * n); #endif z3-z3-4.8.7/src/cmd_context/tactic_manager.cpp000066400000000000000000000024701356505360400211750ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: tactic_manager.cpp Abstract: Collection of tactics & probes Author: Leonardo (leonardo) 2012-03-06 Notes: --*/ #include "cmd_context/tactic_manager.h" tactic_manager::~tactic_manager() { finalize_tactic_cmds(); finalize_probes(); } void tactic_manager::insert(tactic_cmd * c) { symbol const & s = c->get_name(); SASSERT(!m_name2tactic.contains(s)); m_name2tactic.insert(s, c); m_tactics.push_back(c); } void tactic_manager::insert(probe_info * p) { symbol const & s = p->get_name(); SASSERT(!m_name2probe.contains(s)); m_name2probe.insert(s, p); m_probes.push_back(p); } tactic_cmd * tactic_manager::find_tactic_cmd(symbol const & s) const { tactic_cmd * c = nullptr; m_name2tactic.find(s, c); return c; } probe_info * tactic_manager::find_probe(symbol const & s) const { probe_info * p = nullptr; m_name2probe.find(s, p); return p; } void tactic_manager::finalize_tactic_cmds() { std::for_each(m_tactics.begin(), m_tactics.end(), delete_proc()); m_tactics.reset(); m_name2tactic.reset(); } void tactic_manager::finalize_probes() { std::for_each(m_probes.begin(), m_probes.end(), delete_proc()); m_probes.reset(); m_name2probe.reset(); } z3-z3-4.8.7/src/cmd_context/tactic_manager.h000066400000000000000000000027231356505360400206430ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: tactic_manager.h Abstract: Collection of tactics & probes Author: Leonardo (leonardo) 2012-03-06 Notes: --*/ #ifndef TACTIC_MANAGER_H_ #define TACTIC_MANAGER_H_ #include "cmd_context/tactic_cmds.h" #include "util/dictionary.h" class tactic_manager { protected: dictionary m_name2tactic; dictionary m_name2probe; ptr_vector m_tactics; ptr_vector m_probes; void finalize_tactic_cmds(); void finalize_probes(); public: ~tactic_manager(); void insert(tactic_cmd * c); void insert(probe_info * p); tactic_cmd * find_tactic_cmd(symbol const & s) const; probe_info * find_probe(symbol const & s) const; unsigned num_tactics() const { return m_tactics.size(); } unsigned num_probes() const { return m_probes.size(); } tactic_cmd * get_tactic(unsigned i) const { return m_tactics[i]; } probe_info * get_probe(unsigned i) const { return m_probes[i]; } typedef ptr_vector::const_iterator tactic_cmd_iterator; tactic_cmd_iterator begin_tactic_cmds() const { return m_tactics.begin(); } tactic_cmd_iterator end_tactic_cmds() const { return m_tactics.end(); } typedef ptr_vector::const_iterator probe_iterator; probe_iterator begin_probes() const { return m_probes.begin(); } probe_iterator end_probes() const { return m_probes.end(); } }; #endif z3-z3-4.8.7/src/math/000077500000000000000000000000001356505360400141475ustar00rootroot00000000000000z3-z3-4.8.7/src/math/automata/000077500000000000000000000000001356505360400157625ustar00rootroot00000000000000z3-z3-4.8.7/src/math/automata/CMakeLists.txt000066400000000000000000000001321356505360400205160ustar00rootroot00000000000000z3_add_component(automata SOURCES automaton.cpp COMPONENT_DEPENDENCIES util ) z3-z3-4.8.7/src/math/automata/automaton.cpp000066400000000000000000000004621356505360400204770ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: automaton.cpp Abstract: Symbolic Automaton, a la Margus Veanes Automata library. Author: Nikolaj Bjorner (nbjorner) 2015-12-23. Revision History: --*/ #include "math/automata/automaton.h" template class automaton; z3-z3-4.8.7/src/math/automata/automaton.h000066400000000000000000000612251356505360400201500ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: automaton.h Abstract: Symbolic Automaton, a la Margus Veanes Automata library. Author: Nikolaj Bjorner (nbjorner) 2015-12-23. Revision History: --*/ #ifndef AUTOMATON_H_ #define AUTOMATON_H_ #include "util/util.h" #include "util/vector.h" #include "util/uint_set.h" #include "util/trace.h" template class default_value_manager { public: void inc_ref(T* t) {} void dec_ref(T* t) {} }; template > class automaton { public: class move { M& m; T* m_t; unsigned m_src; unsigned m_dst; public: move(M& m, unsigned s, unsigned d, T* t = nullptr): m(m), m_t(t), m_src(s), m_dst(d) { if (t) m.inc_ref(t); } ~move() { if (m_t) m.dec_ref(m_t); } move(move const& other): m(other.m), m_t(other.m_t), m_src(other.m_src), m_dst(other.m_dst) { if (m_t) m.inc_ref(m_t); } move& operator=(move const& other) { SASSERT(&m == &other.m); T* t = other.m_t; if (t) m.inc_ref(t); if (m_t) m.dec_ref(m_t); m_t = t; m_src = other.m_src; m_dst = other.m_dst; return *this; } unsigned dst() const { return m_dst; } unsigned src() const { return m_src; } T* t() const { return m_t; } bool is_epsilon() const { return m_t == nullptr; } }; typedef vector moves; private: M& m; vector m_delta; vector m_delta_inv; unsigned m_init; uint_set m_final_set; unsigned_vector m_final_states; // local data-structures mutable uint_set m_visited; mutable unsigned_vector m_todo; struct default_display { std::ostream& display(std::ostream& out, T* t) { return out << t; } }; public: // The empty automaton: automaton(M& m): m(m), m_init(0) { m_delta.push_back(moves()); m_delta_inv.push_back(moves()); } // create an automaton from initial state, final states, and moves automaton(M& m, unsigned init, unsigned_vector const& final, moves const& mvs): m(m) { m_init = init; m_delta.push_back(moves()); m_delta_inv.push_back(moves()); for (unsigned f : final) { add_to_final_states(f); } for (move const& mv : mvs) { unsigned n = std::max(mv.src(), mv.dst()); if (n >= m_delta.size()) { m_delta.resize(n+1, moves()); m_delta_inv.resize(n+1, moves()); } add(mv); } } // create an automaton that accepts a sequence. automaton(M& m, ptr_vector const& seq): m(m), m_init(0) { m_delta.resize(seq.size()+1, moves()); m_delta_inv.resize(seq.size()+1, moves()); for (unsigned i = 0; i < seq.size(); ++i) { m_delta[i].push_back(move(m, i, i + 1, seq[i])); m_delta[i + 1].push_back(move(m, i, i + 1, seq[i])); } add_to_final_states(seq.size()); } // The automaton that accepts t automaton(M& m, T* t): m(m), m_init(0) { m_delta.resize(2, moves()); m_delta_inv.resize(2, moves()); add_to_final_states(1); add(move(m, 0, 1, t)); } automaton(automaton const& other): m(other.m), m_delta(other.m_delta), m_delta_inv(other.m_delta_inv), m_init(other.m_init), m_final_set(other.m_final_set), m_final_states(other.m_final_states) {} // create the automaton that accepts the empty string/sequence only. static automaton* mk_epsilon(M& m) { moves mvs; unsigned_vector final; final.push_back(0); return alloc(automaton, m, 0, final, mvs); } // create the automaton with a single state on condition t. static automaton* mk_loop(M& m, T* t) { moves mvs; unsigned_vector final; final.push_back(0); mvs.push_back(move(m, 0, 0, t)); return alloc(automaton, m, 0, final, mvs); } static automaton* clone(automaton const& a) { moves mvs; unsigned_vector final; append_moves(0, a, mvs); append_final(0, a, final); return alloc(automaton, a.m, a.init(), final, mvs); } automaton* clone() const { return clone(*this); } // create the sum of disjoint automata static automaton* mk_union(automaton const& a, automaton const& b) { SASSERT(&a.m == &b.m); M& m = a.m; if (a.is_empty()) { return b.clone(); } if (b.is_empty()) { return a.clone(); } moves mvs; unsigned_vector final; unsigned offset1 = 1; unsigned offset2 = a.num_states() + 1; mvs.push_back(move(m, 0, a.init() + offset1)); mvs.push_back(move(m, 0, b.init() + offset2)); append_moves(offset1, a, mvs); append_moves(offset2, b, mvs); append_final(offset1, a, final); append_final(offset2, b, final); return alloc(automaton, m, 0, final, mvs); } static automaton* mk_opt(automaton const& a) { M& m = a.m; moves mvs; unsigned_vector final; unsigned offset = 0; unsigned init = a.init(); if (!a.initial_state_is_source()) { offset = 1; init = 0; mvs.push_back(move(m, 0, a.init() + offset)); } if (a.is_empty()) { return a.clone(); } mvs.push_back(move(m, init, a.final_state() + offset)); append_moves(offset, a, mvs); append_final(offset, a, final); return alloc(automaton, m, init, final, mvs); } // concatenate accepting languages static automaton* mk_concat(automaton const& a, automaton const& b) { SASSERT(&a.m == &b.m); M& m = a.m; if (a.is_empty()) { return a.clone(); } if (b.is_empty()) { return b.clone(); } if (a.is_epsilon()) { return b.clone(); } if (b.is_epsilon()) { return a.clone(); } moves mvs; unsigned_vector final; unsigned init = 0; unsigned offset1 = 1; unsigned offset2 = a.num_states() + offset1; mvs.push_back(move(m, 0, a.init() + offset1)); append_moves(offset1, a, mvs); for (unsigned i = 0; i < a.m_final_states.size(); ++i) { mvs.push_back(move(m, a.m_final_states[i] + offset1, b.init() + offset2)); } append_moves(offset2, b, mvs); append_final(offset2, b, final); return alloc(automaton, m, init, final, mvs); } static automaton* mk_reverse(automaton const& a) { M& m = a.m; if (a.is_empty()) { return alloc(automaton, m); } moves mvs; for (unsigned i = 0; i < a.m_delta.size(); ++i) { moves const& mvs1 = a.m_delta[i]; for (unsigned j = 0; j < mvs1.size(); ++j) { move const& mv = mvs1[j]; mvs.push_back(move(m, mv.dst(), mv.src(), mv.t())); } } unsigned_vector final; unsigned init; final.push_back(a.init()); if (a.m_final_states.size() == 1) { init = a.m_final_states[0]; } else { init = a.num_states(); for (unsigned st : a.m_final_states) { mvs.push_back(move(m, init, st)); } } return alloc(automaton, m, init, final, mvs); } void add_to_final_states(unsigned s) { if (!is_final_state(s)) { m_final_set.insert(s); m_final_states.push_back(s); } } void remove_from_final_states(unsigned s) { if (is_final_state(s)) { m_final_set.remove(s); m_final_states.erase(s); } } bool is_sink_state(unsigned s) const { if (is_final_state(s)) return false; moves mvs; get_moves_from(s, mvs); for (move const& m : mvs) { if (s != m.dst()) return false; } return true; } void add_init_to_final_states() { add_to_final_states(init()); } void add_final_to_init_moves() { for (unsigned i = 0; i < m_final_states.size(); ++i) { unsigned state = m_final_states[i]; bool found = false; moves const& mvs = m_delta[state]; for (unsigned j = 0; found && j < mvs.size(); ++j) { found = (mvs[j].dst() == m_init) && mvs[j].is_epsilon(); } if (!found && state != m_init) { add(move(m, state, m_init)); } } } // remove epsilon transitions // src - e -> dst // in_degree(src) = 1, final(src) => final(dst), src0 != src // src0 - t -> src - e -> dst => src0 - t -> dst // out_degree(dst) = 1, final(dst) => final(src), dst != dst1 // src - e -> dst - t -> dst1 => src - t -> dst1 // Generalized: // Src - E -> dst - t -> dst1 => Src - t -> dst1 if dst is final => each Src is final // // src - e -> dst - ET -> Dst1 => src - ET -> Dst1 if in_degree(dst) = 1, src != dst // Src - E -> dst - et -> dst1 => Src - et -> dst1 if out_degree(dst) = 1, src != dst // // Some missing: // src - et -> dst - E -> Dst1 => src - et -> Dst1 if in_degree(dst) = 1 // Src - ET -> dst - e -> dst1 => Src - ET -> dst1 if out_degree(dst) = 1, // void compress() { SASSERT(!m_delta.empty()); TRACE("seq", display(tout);); for (unsigned i = 0; i < m_delta.size(); ++i) { for (unsigned j = 0; j < m_delta[i].size(); ++j) { move const& mv = m_delta[i][j]; unsigned src = mv.src(); unsigned dst = mv.dst(); SASSERT(src == i); if (mv.is_epsilon()) { if (src == dst) { // just remove this edge. } else if (1 == in_degree(src) && 1 == out_degree(src) && init() != src && (!is_final_state(src) || is_final_state(dst))) { move const& mv0 = m_delta_inv[src][0]; unsigned src0 = mv0.src(); T* t = mv0.t(); SASSERT(mv0.dst() == src); if (src0 == src) { continue; } add(move(m, src0, dst, t)); remove(src0, src, t); } else if (1 == out_degree(dst) && 1 == in_degree(dst) && init() != dst && (!is_final_state(dst) || is_final_state(src))) { move const& mv1 = m_delta[dst][0]; unsigned dst1 = mv1.dst(); T* t = mv1.t(); SASSERT(mv1.src() == dst); if (dst1 == dst) { continue; } add(move(m, src, dst1, t)); remove(dst, dst1, t); } else if (1 == in_degree(dst) && (!is_final_state(dst) || is_final_state(src)) && init() != dst) { moves const& mvs = m_delta[dst]; moves mvs1; for (move const& mv : mvs) { mvs1.push_back(move(m, src, mv.dst(), mv.t())); } for (move const& mv : mvs1) { remove(dst, mv.dst(), mv.t()); add(mv); } } // // Src - E -> dst - et -> dst1 => Src - et -> dst1 if out_degree(dst) = 1, src != dst // else if (1 == out_degree(dst) && all_epsilon_in(dst) && init() != dst && !is_final_state(dst)) { move const& mv = m_delta[dst][0]; unsigned dst1 = mv.dst(); T* t = mv.t(); unsigned_vector src0s; moves const& mvs = m_delta_inv[dst]; moves mvs1; for (move const& mv1 : mvs) { SASSERT(mv1.is_epsilon()); mvs1.push_back(move(m, mv1.src(), dst1, t)); } for (move const& mv1 : mvs1) { remove(mv1.src(), dst, nullptr); add(mv1); } remove(dst, dst1, t); --j; continue; } // // Src1 - ET -> src - e -> dst => Src1 - ET -> dst if out_degree(src) = 1, src != init() // else if (1 == out_degree(src) && init() != src && (!is_final_state(src) || is_final_state(dst))) { moves const& mvs = m_delta_inv[src]; moves mvs1; for (move const& mv : mvs) { mvs1.push_back(move(m, mv.src(), dst, mv.t())); } for (move const& mv : mvs1) { remove(mv.src(), src, mv.t()); add(mv); } } else if (1 == out_degree(src) && (is_final_state(src) || !is_final_state(dst))) { moves const& mvs = m_delta[dst]; moves mvs1; for (move const& mv : mvs) { mvs1.push_back(move(m, src, mv.dst(), mv.t())); } for (move const& mv : mvs1) { add(mv); } } else { TRACE("seq", tout << "epsilon not removed " << out_degree(src) << " " << is_final_state(src) << " " << is_final_state(dst) << "\n";); continue; } remove(src, dst, nullptr); --j; } } } SASSERT(!m_delta.empty()); while (true) { SASSERT(!m_delta.empty()); unsigned src = m_delta.size() - 1; if (in_degree(src) == 0 && init() != src) { remove_from_final_states(src); m_delta.pop_back(); } else { break; } } sinkify_dead_states(); TRACE("seq", display(tout);); } bool is_sequence(unsigned& length) const { if (is_final_state(m_init) && (out_degree(m_init) == 0 || (out_degree(m_init) == 1 && is_loop_state(m_init)))) { length = 0; return true; } if (is_empty() || in_degree(m_init) != 0 || out_degree(m_init) != 1) { return false; } length = 1; unsigned s = get_move_from(m_init).dst(); while (!is_final_state(s)) { if (out_degree(s) != 1 || in_degree(s) != 1) { return false; } s = get_move_from(s).dst(); ++length; } return out_degree(s) == 0 || (out_degree(s) == 1 && is_loop_state(s)); } unsigned init() const { return m_init; } unsigned_vector const& final_states() const { return m_final_states; } unsigned in_degree(unsigned state) const { return m_delta_inv[state].size(); } unsigned out_degree(unsigned state) const { return m_delta[state].size(); } move const& get_move_from(unsigned state) const { SASSERT(m_delta[state].size() == 1); return m_delta[state][0]; } move const& get_move_to(unsigned state) const { SASSERT(m_delta_inv[state].size() == 1); return m_delta_inv[state][0]; } moves const& get_moves_from(unsigned state) const { return m_delta[state]; } moves const& get_moves_to(unsigned state) const { return m_delta_inv[state]; } bool initial_state_is_source() const { return m_delta_inv[m_init].empty(); } bool is_final_state(unsigned s) const { return m_final_set.contains(s); } bool is_final_configuration(uint_set const& s) const { for (unsigned i : s) { if (is_final_state(i)) return true; } return false; } bool is_epsilon_free() const { for (moves const& mvs : m_delta) { for (move const & m : mvs) { if (!m.t()) return false; } } return true; } bool all_epsilon_in(unsigned s) { moves const& mvs = m_delta_inv[s]; for (move const& m : mvs) { if (m.t()) return false; } return true; } bool is_empty() const { return m_final_states.empty(); } bool is_epsilon() const { return m_final_states.size() == 1 && m_final_states.back() == init() && m_delta.empty(); } unsigned final_state() const { return m_final_states[0]; } bool has_single_final_sink() const { return m_final_states.size() == 1 && m_delta[final_state()].empty(); } unsigned num_states() const { return m_delta.size(); } bool is_loop_state(unsigned s) const { moves mvs; get_moves_from(s, mvs); for (move const& m : mvs) { if (s == m.dst()) return true; } return false; } unsigned move_count() const { unsigned result = 0; for (moves const& mvs : m_delta) result += mvs.size(); return result; } void get_epsilon_closure(unsigned state, unsigned_vector& states) { get_epsilon_closure(state, m_delta, states); } void get_inv_epsilon_closure(unsigned state, unsigned_vector& states) { get_epsilon_closure(state, m_delta_inv, states); } void get_moves_from(unsigned state, moves& mvs, bool epsilon_closure = true) const { get_moves(state, m_delta, mvs, epsilon_closure); } void get_moves_from_states(uint_set const& states, moves& mvs, bool epsilon_closure = true) const { for (unsigned i : states) { moves curr; get_moves(i, m_delta, curr, epsilon_closure); mvs.append(curr); } } void get_moves_to(unsigned state, moves& mvs, bool epsilon_closure = true) { get_moves(state, m_delta_inv, mvs, epsilon_closure); } template std::ostream& display(std::ostream& out, D& displayer = D()) const { out << "init: " << init() << "\n"; out << "final: " << m_final_states << "\n"; for (unsigned i = 0; i < m_delta.size(); ++i) { moves const& mvs = m_delta[i]; for (move const& mv : mvs) { out << i << " -> " << mv.dst() << " "; if (mv.t()) { out << "if "; displayer.display(out, mv.t()); } out << "\n"; } } return out; } private: std::ostream& display(std::ostream& out) const { out << "init: " << init() << "\n"; out << "final: " << m_final_states << "\n"; for (unsigned i = 0; i < m_delta.size(); ++i) { moves const& mvs = m_delta[i]; for (move const& mv : mvs) { out << i << " -> " << mv.dst() << " "; if (mv.t()) { out << "if *** "; } out << "\n"; } } return out; } void sinkify_dead_states() { uint_set dead_states; for (unsigned i = 0; i < m_delta.size(); ++i) { if (!m_final_states.contains(i)) { dead_states.insert(i); } } bool change = true; unsigned_vector to_remove; while (change) { change = false; to_remove.reset(); for (unsigned s : dead_states) { moves const& mvs = m_delta[s]; for (move const& mv : mvs) { if (!dead_states.contains(mv.dst())) { to_remove.push_back(s); break; } } } change = !to_remove.empty(); for (unsigned s : to_remove) { dead_states.remove(s); } to_remove.reset(); } TRACE("seq", tout << "remove: " << dead_states << "\n"; tout << "final: " << m_final_states << "\n"; ); for (unsigned s : dead_states) { CTRACE("seq", !m_delta[s].empty(), tout << "live state " << s << "\n";); m_delta[s].reset(); } } #if 0 void remove_dead_states() { unsigned_vector remap; for (unsigned i = 0; i < m_delta.size(); ++i) { } } #endif void add(move const& mv) { if (!is_duplicate_cheap(mv)) { m_delta[mv.src()].push_back(mv); m_delta_inv[mv.dst()].push_back(mv); } } bool is_duplicate_cheap(move const& mv) const { if (m_delta[mv.src()].empty()) return false; move const& mv0 = m_delta[mv.src()].back(); return mv0.src() == mv.src() && mv0.dst() == mv.dst() && mv0.t() == mv.t(); } unsigned find_move(unsigned src, unsigned dst, T* t, moves const& mvs) { for (unsigned i = 0; i < mvs.size(); ++i) { move const& mv = mvs[i]; if (mv.src() == src && mv.dst() == dst && t == mv.t()) { return i; } } UNREACHABLE(); return UINT_MAX; } void remove(unsigned src, unsigned dst, T* t, moves& mvs) { remove(find_move(src, dst, t, mvs), mvs); } void remove(unsigned src, unsigned dst, T* t) { remove(src, dst, t, m_delta[src]); remove(src, dst, t, m_delta_inv[dst]); } void remove(unsigned index, moves& mvs) { mvs[index] = mvs.back(); mvs.pop_back(); } mutable unsigned_vector m_states1, m_states2; void get_moves(unsigned state, vector const& delta, moves& mvs, bool epsilon_closure) const { m_states1.reset(); m_states2.reset(); get_epsilon_closure(state, delta, m_states1); for (unsigned i = 0; i < m_states1.size(); ++i) { state = m_states1[i]; moves const& mv1 = delta[state]; for (unsigned j = 0; j < mv1.size(); ++j) { move const& mv = mv1[j]; if (!mv.is_epsilon()) { if (epsilon_closure) { m_states2.reset(); get_epsilon_closure(mv.dst(), delta, m_states2); for (unsigned k = 0; k < m_states2.size(); ++k) { mvs.push_back(move(m, state, m_states2[k], mv.t())); } } else { mvs.push_back(move(m, state, mv.dst(), mv.t())); } } } } } void get_epsilon_closure(unsigned state, vector const& delta, unsigned_vector& states) const { m_todo.push_back(state); m_visited.insert(state); while (!m_todo.empty()) { state = m_todo.back(); states.push_back(state); m_todo.pop_back(); moves const& mvs = delta[state]; for (unsigned i = 0; i < mvs.size(); ++i) { unsigned tgt = mvs[i].dst(); if (mvs[i].is_epsilon() && !m_visited.contains(tgt)) { m_visited.insert(tgt); m_todo.push_back(tgt); } } } m_visited.reset(); SASSERT(m_todo.empty()); } static void append_moves(unsigned offset, automaton const& a, moves& mvs) { for (unsigned i = 0; i < a.num_states(); ++i) { moves const& mvs1 = a.m_delta[i]; for (unsigned j = 0; j < mvs1.size(); ++j) { move const& mv = mvs1[j]; mvs.push_back(move(a.m, mv.src() + offset, mv.dst() + offset, mv.t())); } } } static void append_final(unsigned offset, automaton const& a, unsigned_vector& final) { for (unsigned s : a.m_final_states) { final.push_back(s+offset); } } }; typedef automaton uautomaton; #endif z3-z3-4.8.7/src/math/automata/boolean_algebra.h000066400000000000000000000015201356505360400212250ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: boolean_algebra.h Abstract: Boolean Algebra, a la Margus Veanes Automata library. Author: Nikolaj Bjorner (nbjorner) 2016-2-27 Revision History: --*/ #ifndef BOOLEAN_ALGEBRA_H_ #define BOOLEAN_ALGEBRA_H_ #include "util/util.h" template class positive_boolean_algebra { public: virtual ~positive_boolean_algebra() {} virtual T mk_false() = 0; virtual T mk_true() = 0; virtual T mk_and(T x, T y) = 0; virtual T mk_or(T x, T y) = 0; virtual T mk_and(unsigned sz, T const* ts) = 0; virtual T mk_or(unsigned sz, T const* ts) = 0; virtual lbool is_sat(T x) = 0; }; template class boolean_algebra : public positive_boolean_algebra { public: ~boolean_algebra() override {} virtual T mk_not(T x) = 0; }; #endif z3-z3-4.8.7/src/math/automata/symbolic_automata.h000066400000000000000000000107711356505360400216550ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: symbolic_automata.h Abstract: Symbolic Automata over Boolean Algebras, a la Margus Veanes Automata library. Author: Nikolaj Bjorner (nbjorner) 2016-02-27. Revision History: --*/ #ifndef SYMBOLIC_AUTOMATA_H_ #define SYMBOLIC_AUTOMATA_H_ #include "math/automata/automaton.h" #include "math/automata/boolean_algebra.h" template > class symbolic_automata { typedef automaton automaton_t; typedef boolean_algebra ba_t; typedef typename automaton_t::move move_t; typedef vector moves_t; typedef obj_ref ref_t; typedef ref_vector refs_t; typedef std::pair unsigned_pair; template class u2_map : public map, default_eq > {}; M& m; ba_t& m_ba; class block { uint_set m_set; unsigned m_rep; bool m_rep_chosen; public: block(): m_rep(0), m_rep_chosen(false) {} block(uint_set const& s): m_set(s), m_rep(0), m_rep_chosen(false) { } block(unsigned_vector const& vs) { for (unsigned i = 0; i < vs.size(); ++i) { m_set.insert(vs[i]); } m_rep_chosen = false; m_rep = 0; } block& operator=(block const& b) { m_set = b.m_set; m_rep = 0; m_rep_chosen = false; return *this; } unsigned get_representative() { if (!m_rep_chosen) { uint_set::iterator it = m_set.begin(); if (m_set.end() != it) { m_rep = *it; } m_rep_chosen = true; } return m_rep; } void insert(unsigned i) { m_set.insert(i); } bool contains(unsigned i) const { return m_set.contains(i); } bool is_empty() const { return m_set.empty(); } unsigned size() const { return m_set.num_elems(); } void remove(unsigned i) { m_set.remove(i); m_rep_chosen = false; } void clear() { m_set.reset(); m_rep_chosen = false; } uint_set::iterator begin() const { return m_set.begin(); } uint_set::iterator end() const { return m_set.end(); } }; void add_block(block const& p1, unsigned p0_index, unsigned_vector& blocks, vector& pblocks, unsigned_vector& W); public: symbolic_automata(M& m, ba_t& ba): m(m), m_ba(ba) {} automaton_t* mk_determinstic(automaton_t& a); automaton_t* mk_complement(automaton_t& a); automaton_t* remove_epsilons(automaton_t& a); automaton_t* mk_total(automaton_t& a); automaton_t* mk_minimize(automaton_t& a); automaton_t* mk_minimize_total(automaton_t& a); automaton_t* mk_difference(automaton_t& a, automaton_t& b); automaton_t* mk_product(automaton_t& a, automaton_t& b); private: automaton_t* mk_determinstic_param(automaton_t& a, bool flip_acceptance); vector, ref_t> > generate_min_terms(vector &constraints) { vector, ref_t> > min_terms; ref_t curr_pred(m_ba.mk_true(), m); vector curr_bv; generate_min_terms_rec(constraints, min_terms, 0, curr_bv, curr_pred); return min_terms; } void generate_min_terms_rec(vector &constraints, vector, ref_t> > &min_terms, unsigned i, vector &curr_bv, ref_t &curr_pred) { lbool is_sat = m_ba.is_sat(curr_pred); if (is_sat != l_true) { return; } if (i == constraints.size()) { min_terms.push_back(std::pair, ref_t>(curr_bv, curr_pred)); } else { //true case curr_bv.push_back(true); ref_t new_pred_pos(m_ba.mk_and(curr_pred, constraints[i]), m); generate_min_terms_rec(constraints, min_terms, i + 1, curr_bv, new_pred_pos); curr_bv.pop_back(); //false case curr_bv.push_back(false); ref_t neg(m_ba.mk_not(constraints[i]), m); ref_t new_pred_neg(m_ba.mk_and(curr_pred, neg), m); generate_min_terms_rec(constraints, min_terms, i + 1, curr_bv, new_pred_neg); curr_bv.pop_back(); } } }; #endif z3-z3-4.8.7/src/math/automata/symbolic_automata_def.h000066400000000000000000000403561356505360400224750ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: symbolic_automata_def.h Abstract: Symbolic Automata over Boolean Algebras, a la Margus Veanes Automata library. Author: Nikolaj Bjorner (nbjorner) 2016-02-27. Revision History: --*/ #ifndef SYMBOLIC_AUTOMATA_DEF_H_ #define SYMBOLIC_AUTOMATA_DEF_H_ #include "math/automata/symbolic_automata.h" #include "util/hashtable.h" #include "util/vector.h" template typename symbolic_automata::automaton_t* symbolic_automata::mk_total(automaton_t& a) { unsigned dead_state = a.num_states(); moves_t mvs, new_mvs; for (unsigned i = 0; i < dead_state; ++i) { mvs.reset(); a.get_moves_from(i, mvs, true); refs_t vs(m); for (unsigned j = 0; j < mvs.size(); ++j) { vs.push_back(mvs[j].t()); } ref_t cond(m_ba.mk_not(m_ba.mk_or(vs.size(), vs.c_ptr())), m); lbool is_sat = m_ba.is_sat(cond); if (is_sat == l_undef) { return 0; } if (is_sat == l_true) { new_mvs.push_back(move_t(m, i, dead_state, cond)); } } if (new_mvs.empty()) { return a.clone(); } new_mvs.push_back(move_t(m, dead_state, dead_state, m_ba.mk_true())); // TBD private: automaton_t::append_moves(0, a, new_mvs); return alloc(automaton_t, m, a.init(), a.final_states(), new_mvs); } template typename symbolic_automata::automaton_t* symbolic_automata::mk_minimize(automaton_t& a) { if (a.is_empty()) { return a.clone(); } if (a.is_epsilon()) { return a.clone(); } // SASSERT(a.is_deterministic()); scoped_ptr fa = mk_total(a); if (!fa) { return 0; } return mk_minimize_total(*fa.get()); } template void symbolic_automata::add_block(block const& p1, unsigned p0_index, unsigned_vector& blocks, vector& pblocks, unsigned_vector& W) { block& p0 = pblocks[p0_index]; if (p1.size() < p0.size()) { unsigned p1_index = pblocks.size(); pblocks.push_back(p1); for (uint_set::iterator it = p1.begin(), end = p1.end(); it != end; ++it) { p0.remove(*it); blocks[*it] = p1_index; } if (W.contains(p0_index)) { W.push_back(p1_index); } else if (p0.size() <= p1.size()) { W.push_back(p0_index); } else { W.push_back(p1_index); } } } template typename symbolic_automata::automaton_t* symbolic_automata::mk_minimize_total(automaton_t& a) { vector pblocks; unsigned_vector blocks; unsigned_vector non_final; for (unsigned i = 0; i < a.num_states(); ++i) { if (!a.is_final_state(i)) { non_final.push_back(i); blocks.push_back(1); } else { blocks.push_back(0); } } pblocks.push_back(block(a.final_states())); // 0 |-> final states pblocks.push_back(block(non_final)); // 1 |-> non-final states unsigned_vector W; W.push_back(pblocks[0].size() > pblocks[1].size() ? 1 : 0); refs_t trail(m); u_map gamma; moves_t mvs; while (!W.empty()) { block R(pblocks[W.back()]); W.pop_back(); gamma.reset(); uint_set::iterator it = R.begin(), end = R.end(); for (; it != end; ++it) { unsigned dst = *it; mvs.reset(); a.get_moves_to(dst, mvs); for (unsigned i = 0; i < mvs.size(); ++i) { unsigned src = mvs[i].src(); if (pblocks[src].size() > 1) { T* t = mvs[i].t(); T* t1; if (gamma.find(src, t1)) { t = m_ba.mk_or(t, t1); trail.push_back(t); } gamma.insert(src, t); } } } uint_set relevant1; typedef typename u_map::iterator gamma_iterator; gamma_iterator gend = gamma.end(); for (gamma_iterator git = gamma.begin(); git != gend; ++git) { unsigned p0A_index = blocks[git->m_key]; if (relevant1.contains(p0A_index)) { continue; } relevant1.insert(p0A_index); block& p0A = pblocks[p0A_index]; block p1; for (gamma_iterator it = gamma.begin(); it != gend; ++it) { if (p0A.contains(it->m_key)) p1.insert(it->m_key); } add_block(p1, p0A_index, blocks, pblocks, W); bool iterate = true; while (iterate) { iterate = false; uint_set relevant2; for (gamma_iterator it = gamma.begin(); it != gend; ++it) { unsigned p0B_index = blocks[it->m_key]; if (pblocks[p0B_index].size() <= 1 || relevant2.contains(p0B_index)) { continue; } relevant2.insert(p0B_index); block const& p0B = pblocks[p0B_index]; uint_set::iterator bi = p0B.begin(), be = p0B.end(); block p1; p1.insert(*bi); bool split_found = false; ref_t psi(gamma[*bi], m); ++bi; for (; bi != be; ++bi) { unsigned q = *bi; ref_t phi(gamma[q], m); if (split_found) { ref_t phi_and_psi(m_ba.mk_and(phi, psi), m); switch (m_ba.is_sat(phi_and_psi)) { case l_true: p1.insert(q); break; case l_undef: return 0; default: break; } } else { ref_t psi_min_phi(m_ba.mk_and(psi, m_ba.mk_not(phi)), m); lbool is_sat = m_ba.is_sat(psi_min_phi); if (is_sat == l_undef) { return 0; } if (is_sat == l_true) { psi = psi_min_phi; split_found = true; continue; } // psi is a subset of phi ref_t phi_min_psi(m_ba.mk_and(phi, m_ba.mk_not(psi)), m); is_sat = m_ba.is_sat(phi_min_psi); if (is_sat == l_undef) { return 0; } else if (is_sat == l_false) { p1.insert(q); // psi and phi are equivalent } else { p1.clear(); p1.insert(q); psi = phi_min_psi; split_found = true; } } } if (p1.size() < p0B.size() && p0B.size() > 2) iterate = true; add_block(p1, p0B_index, blocks, pblocks, W); } } } } unsigned new_init = pblocks[blocks[a.init()]].get_representative(); // set moves u2_map conds; svector keys; moves_t new_moves; for (unsigned i = 0; i < a.num_states(); ++i) { unsigned src = pblocks[blocks[i]].get_representative(); typename automaton_t::moves const& mvs = a.get_moves_from(i); for (unsigned j = 0; j < mvs.size(); ++j) { unsigned dst = pblocks[blocks[mvs[j].dst()]].get_representative(); unsigned_pair st(src, dst); T* t = 0; if (conds.find(st, t)) { t = m_ba.mk_or(t, mvs[j].t()); trail.push_back(t); conds.insert(st, t); } else { conds.insert(st, mvs[j].t()); keys.push_back(st); } } } for (unsigned i = 0; i < keys.size(); ++i) { unsigned_pair st = keys[i]; new_moves.push_back(move_t(m, st.first, st.second, conds[st])); } // set final states. unsigned_vector new_final; uint_set new_final_set; for (unsigned i = 0; i < a.final_states().size(); ++i) { unsigned f = pblocks[blocks[a.final_states()[i]]].get_representative(); if (!new_final_set.contains(f)) { new_final_set.insert(f); new_final.push_back(f); } } return alloc(automaton_t, m, new_init, new_final, new_moves); } template typename symbolic_automata::automaton_t* symbolic_automata::mk_determinstic(automaton_t& a) { return mk_determinstic_param(a); } template typename symbolic_automata::automaton_t* symbolic_automata::mk_complement(automaton_t& a) { return mk_determinstic_param(a, true); } template typename symbolic_automata::automaton_t* symbolic_automata::mk_determinstic_param(automaton_t& a, bool flip_acceptance) { vector, ref_t> > min_terms; vector predicates; map s2id; // set of states to unique id vector id2s; // unique id to set of b-states uint_set set; unsigned_vector vector; moves_t new_mvs; // moves in the resulting automaton unsigned_vector new_final_states; // new final states unsigned p_state_id = 0; // next state identifier TRACE("seq", tout << "mk-deterministic " << flip_acceptance << " " << set << " " << a.is_final_configuration(set) << "\n";); // adds non-final states of a to final if flipping and final otherwise unsigned_vector init_states; a.get_epsilon_closure(a.init(), init_states); for (unsigned s : init_states) { set.insert(s); } if (a.is_final_configuration(set) != flip_acceptance) { new_final_states.push_back(p_state_id); } s2id.insert(set, p_state_id++); // the index to the initial state is 0 id2s.push_back(set); ::vector todo; //States to visit todo.push_back(set); uint_set state; moves_t mvsA; new_mvs.reset(); // or just make todo a vector whose indices coincide with state_id. while (!todo.empty()) { uint_set state = todo.back(); unsigned state_id = s2id[state]; todo.pop_back(); mvsA.reset(); min_terms.reset(); predicates.reset(); a.get_moves_from_states(state, mvsA); for (unsigned j = 0; j < mvsA.size(); ++j) { ref_t mv_guard(mvsA[j].t(),m); predicates.push_back(mv_guard); } min_terms = generate_min_terms(predicates); for (unsigned j = 0; j < min_terms.size(); ++j) { set = uint_set(); for (unsigned i = 0; i < mvsA.size(); ++i) { if (min_terms[j].first[i]) set.insert(mvsA[i].dst()); } bool is_new = !s2id.contains(set); if (is_new) { TRACE("seq", tout << "mk-deterministic " << flip_acceptance << " " << set << " " << a.is_final_configuration(set) << "\n";); if (a.is_final_configuration(set) != flip_acceptance) { new_final_states.push_back(p_state_id); } s2id.insert(set, p_state_id++); id2s.push_back(set); todo.push_back(set); } new_mvs.push_back(move_t(m, state_id, s2id[set], min_terms[j].second)); } } if (new_final_states.empty()) { return alloc(automaton_t, m); } return alloc(automaton_t, m, 0, new_final_states, new_mvs); } template typename symbolic_automata::automaton_t* symbolic_automata::mk_product(automaton_t& a, automaton_t& b) { u2_map pair2id; unsigned_pair init_pair(a.init(), b.init()); svector todo; todo.push_back(init_pair); pair2id.insert(init_pair, 0); moves_t mvs; unsigned_vector final; unsigned_vector a_init, b_init; a.get_epsilon_closure(a.init(), a_init); bool a_init_is_final = false, b_init_is_final = false; for (unsigned ia : a_init) { if (a.is_final_state(ia)) { a_init_is_final = true; b.get_epsilon_closure(b.init(), b_init); for (unsigned ib : b_init) { if (b.is_final_state(ib)) { b_init_is_final = true; final.push_back(0); break; } } break; } } unsigned n = 1; moves_t mvsA, mvsB; while (!todo.empty()) { unsigned_pair curr_pair = todo.back(); todo.pop_back(); unsigned src = pair2id[curr_pair]; mvsA.reset(); mvsB.reset(); a.get_moves_from(curr_pair.first, mvsA, true); b.get_moves_from(curr_pair.second, mvsB, true); for (unsigned i = 0; i < mvsA.size(); ++i) { for (unsigned j = 0; j < mvsB.size(); ++j) { ref_t ab(m_ba.mk_and(mvsA[i].t(), mvsB[j].t()), m); lbool is_sat = m_ba.is_sat(ab); if (is_sat == l_false) { continue; } else if (is_sat == l_undef) { return nullptr; } unsigned_pair tgt_pair(mvsA[i].dst(), mvsB[j].dst()); unsigned tgt; if (!pair2id.find(tgt_pair, tgt)) { tgt = n++; pair2id.insert(tgt_pair, tgt); todo.push_back(tgt_pair); if (a.is_final_state(tgt_pair.first) && b.is_final_state(tgt_pair.second)) { final.push_back(tgt); } } mvs.push_back(move_t(m, src, tgt, ab)); } } } if (final.empty()) { return alloc(automaton_t, m); } vector inv(n, moves_t()); for (unsigned i = 0; i < mvs.size(); ++i) { move_t const& mv = mvs[i]; inv[mv.dst()].push_back(move_t(m, mv.dst(), mv.src(), mv.t())); } svector back_reachable(n, false); for (unsigned f : final) { back_reachable[f] = true; } unsigned_vector stack(final); while (!stack.empty()) { unsigned state = stack.back(); stack.pop_back(); moves_t const& mv = inv[state]; for (unsigned i = 0; i < mv.size(); ++i) { state = mv[i].dst(); if (!back_reachable[state]) { back_reachable[state] = true; stack.push_back(state); } } } moves_t mvs1; for (unsigned i = 0; i < mvs.size(); ++i) { move_t const& mv = mvs[i]; if (back_reachable[mv.dst()]) { mvs1.push_back(mv); } } if (mvs1.empty()) { if (a_init_is_final && b_init_is_final) { // special case: automaton has no moves, but the initial state is final on both sides // this results in the automaton which accepts the empty sequence and nothing else final.clear(); final.push_back(0); return alloc(automaton_t, m, 0, final, mvs1); } else { return alloc(automaton_t, m); } } else { return alloc(automaton_t, m, 0, final, mvs1); } } template typename symbolic_automata::automaton_t* symbolic_automata::mk_difference(automaton_t& a, automaton_t& b) { return mk_product(a,mk_complement(b)); } #endif z3-z3-4.8.7/src/math/euclid/000077500000000000000000000000001356505360400154145ustar00rootroot00000000000000z3-z3-4.8.7/src/math/euclid/CMakeLists.txt000066400000000000000000000001371356505360400201550ustar00rootroot00000000000000z3_add_component(euclid SOURCES euclidean_solver.cpp COMPONENT_DEPENDENCIES util ) z3-z3-4.8.7/src/math/euclid/README000066400000000000000000000001341356505360400162720ustar00rootroot00000000000000Basic Euclidean solver for linear integer equations. This solver generates "explanations". z3-z3-4.8.7/src/math/euclid/euclidean_solver.cpp000066400000000000000000000711371356505360400214540ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: euclidean_solver.cpp Abstract: Euclidean Solver with support for explanations. Author: Leonardo de Moura (leonardo) 2011-07-08. Revision History: --*/ #include "math/euclid/euclidean_solver.h" #include "util/numeral_buffer.h" #include "util/heap.h" struct euclidean_solver::imp { typedef unsigned var; typedef unsigned justification; typedef unsynch_mpq_manager numeral_manager; typedef numeral_buffer mpz_buffer; typedef numeral_buffer mpq_buffer; typedef svector justification_vector; static const justification null_justification = UINT_MAX; #define null_var UINT_MAX #define null_eq_idx UINT_MAX typedef svector var_vector; typedef svector mpz_vector; typedef svector mpq_vector; struct elim_order_lt { unsigned_vector & m_solved; elim_order_lt(unsigned_vector & s):m_solved(s) {} bool operator()(var x1, var x2) const { return m_solved[x1] < m_solved[x2]; } }; typedef heap var_queue; // queue used for scheduling variables for applying substitution. static unsigned pos(unsigned_vector const & xs, unsigned x_i) { if (xs.empty()) return UINT_MAX; int low = 0; int high = xs.size() - 1; while (true) { int mid = low + ((high - low) / 2); var x_mid = xs[mid]; if (x_i > x_mid) { low = mid + 1; if (low > high) return UINT_MAX; } else if (x_i < x_mid) { high = mid - 1; if (low > high) return UINT_MAX; } else { return mid; } } } /** Equation as[0]*xs[0] + ... + as[n-1]*xs[n-1] + c = 0 with justification bs[0]*js[0] + ... + bs[m-1]*js[m-1] */ struct equation { mpz_vector m_as; var_vector m_xs; mpz m_c; // justification mpq_vector m_bs; justification_vector m_js; unsigned size() const { return m_xs.size(); } unsigned js_size() const { return m_js.size(); } var x(unsigned i) const { return m_xs[i]; } var & x(unsigned i) { return m_xs[i]; } mpz const & a(unsigned i) const { return m_as[i]; } mpz & a(unsigned i) { return m_as[i]; } mpz const & c() const { return m_c; } mpz & c() { return m_c; } var j(unsigned i) const { return m_js[i]; } var & j(unsigned i) { return m_js[i]; } mpq const & b(unsigned i) const { return m_bs[i]; } mpq & b(unsigned i) { return m_bs[i]; } unsigned pos_x(unsigned x_i) const { return pos(m_xs, x_i); } }; typedef ptr_vector equations; typedef svector occs; numeral_manager * m_manager; bool m_owns_m; equations m_equations; equations m_solution; svector m_parameter; unsigned_vector m_solved; // null_eq_idx if var is not solved, otherwise the position in m_solution vector m_occs; // occurrences of the variable in m_equations. unsigned m_inconsistent; // null_eq_idx if not inconsistent, otherwise it is the index of an unsatisfiable equality in m_equations. unsigned m_next_justification; mpz_buffer m_decompose_buffer; mpz_buffer m_as_buffer; mpq_buffer m_bs_buffer; var_vector m_tmp_xs; mpz_buffer m_tmp_as; mpq_buffer m_tmp_bs; var_vector m_norm_xs_vector; mpz_vector m_norm_as_vector; mpq_vector m_norm_bs_vector; var_queue m_var_queue; // next candidate unsigned m_next_eq; var m_next_x; mpz m_next_a; bool m_next_pos_a; numeral_manager & m() const { return *m_manager; } bool solved(var x) const { return m_solved[x] != null_eq_idx; } template void sort_core(svector & as, unsigned_vector & xs, numeral_buffer & buffer) { std::sort(xs.begin(), xs.end()); unsigned num = as.size(); for (unsigned i = 0; i < num; i++) { m().swap(as[i], buffer[xs[i]]); } } template void sort(svector & as, unsigned_vector & xs, numeral_buffer & buffer) { unsigned num = as.size(); for (unsigned i = 0; i < num; i++) { m().set(buffer[xs[i]], as[i]); } sort_core(as, xs, buffer); } equation * mk_eq(unsigned num, mpz const * as, var const * xs, mpz const & c, unsigned num_js, mpq const * bs, justification const * js, bool sort = true) { equation * new_eq = alloc(equation); for (unsigned i = 0; i < num; i++) { m().set(m_as_buffer[xs[i]], as[i]); new_eq->m_as.push_back(mpz()); new_eq->m_xs.push_back(xs[i]); } sort_core(new_eq->m_as, new_eq->m_xs, m_as_buffer); m().set(new_eq->m_c, c); for (unsigned i = 0; i < num_js; i++) { m().set(m_bs_buffer[js[i]], bs[i]); new_eq->m_bs.push_back(mpq()); new_eq->m_js.push_back(js[i]); } if (sort) sort_core(new_eq->m_bs, new_eq->m_js, m_bs_buffer); return new_eq; } template void div(svector & as, mpz const & g) { unsigned n = as.size(); for (unsigned i = 0; i < n; i++) m().div(as[i], g, as[i]); } void normalize_eq(unsigned eq_idx) { if (inconsistent()) return; equation & eq = *(m_equations[eq_idx]); TRACE("euclidean_solver", tout << "normalizing:\n"; display(tout, eq); tout << "\n";); unsigned num = eq.size(); if (num == 0) { // c == 0 inconsistency if (!m().is_zero(eq.c())) { TRACE("euclidean_solver", tout << "c = 0 inconsistency detected\n";); m_inconsistent = eq_idx; } else { del_eq(&eq); m_equations[eq_idx] = 0; } return; } mpz g; mpz a; m().set(g, eq.a(0)); m().abs(g); for (unsigned i = 1; i < num; i++) { if (m().is_one(g)) break; m().set(a, eq.a(i)); m().abs(a); m().gcd(g, a, g); } if (m().is_one(g)) return; if (!m().divides(g, eq.c())) { // g does not divide c TRACE("euclidean_solver", tout << "gcd inconsistency detected\n";); m_inconsistent = eq_idx; return; } div(eq.m_as, g); div(eq.m_bs, g); m().del(g); m().del(a); TRACE("euclidean_solver", tout << "after normalization:\n"; display(tout, eq); tout << "\n";); } bool is_better(mpz const & a, var x, unsigned eq_sz) { SASSERT(m().is_pos(a)); if (m_next_x == null_var) return true; if (m().lt(a, m_next_a)) return true; if (m().lt(m_next_a, a)) return false; if (m_occs[x].size() < m_occs[m_next_x].size()) return true; if (m_occs[x].size() > m_occs[m_next_x].size()) return false; return eq_sz < m_equations[m_next_eq]->size(); } void updt_next_candidate(unsigned eq_idx) { if (!m_equations[eq_idx]) return; mpz abs_a; equation const & eq = *(m_equations[eq_idx]); unsigned num = eq.size(); for (unsigned i = 0; i < num; i++) { mpz const & a = eq.a(i); m().set(abs_a, a); m().abs(abs_a); if (is_better(abs_a, eq.x(i), num)) { m().set(m_next_a, abs_a); m_next_x = eq.x(i); m_next_eq = eq_idx; m_next_pos_a = m().is_pos(a); } } m().del(abs_a); } /** \brief Select next variable to be eliminated. Return false if there is not variable to eliminate. The result is in m_next_x variable to be eliminated m_next_eq id of the equation containing x m_next_a absolute value of the coefficient of x in eq. m_next_pos_a true if the coefficient of x is positive in eq. */ bool select_next_var() { while (!m_equations.empty() && m_equations.back() == 0) m_equations.pop_back(); if (m_equations.empty()) return false; SASSERT(!m_equations.empty() && m_equations.back() != 0); m_next_x = null_var; unsigned eq_idx = m_equations.size(); while (eq_idx > 0) { --eq_idx; updt_next_candidate(eq_idx); // stop as soon as possible // TODO: use heuristics if (m_next_x != null_var && m().is_one(m_next_a)) return true; } CTRACE("euclidean_solver_bug", m_next_x == null_var, display(tout);); SASSERT(m_next_x != null_var); return true; } template void del_nums(svector & as) { unsigned sz = as.size(); for (unsigned i = 0; i < sz; i++) m().del(as[i]); as.reset(); } void del_eq(equation * eq) { m().del(eq->c()); del_nums(eq->m_as); del_nums(eq->m_bs); dealloc(eq); } void del_equations(equations & eqs) { unsigned sz = eqs.size(); for (unsigned i = 0; i < sz; i++) { if (eqs[i]) del_eq(eqs[i]); } } /** \brief Store the "solved" variables in xs into m_var_queue. */ void schedule_var_subst(unsigned num, var const * xs) { for (unsigned i = 0; i < num; i++) { if (solved(xs[i])) m_var_queue.insert(xs[i]); } } void schedule_var_subst(var_vector const & xs) { schedule_var_subst(xs.size(), xs.c_ptr()); } /** \brief Store as1*xs1 + k*as2*xs2 into new_as*new_xs If UpdateOcc == true, Then, 1) for each variable x occurring in xs2 but not in xs1: - eq_idx is added to m_occs[x] 2) for each variable that occurs in xs1 and xs2 and the resultant coefficient is zero, - eq_idx is removed from m_occs[x] IF x != except_var If UpdateQueue == true Then, 1) for each variable x occurring in xs2 but not in xs1: - if x is solved, then x is inserted into m_var_queue 2) for each variable that occurs in xs1 and xs2 and the resultant coefficient is zero, - if x is solved, then x is removed from m_var_queue */ template void addmul(svector const & as1, var_vector const & xs1, mpz const & k, svector const & as2, var_vector const & xs2, numeral_buffer & new_as, var_vector & new_xs, unsigned eq_idx = null_eq_idx, var except_var = null_var) { Numeral new_a; SASSERT(as1.size() == xs1.size()); SASSERT(as2.size() == xs2.size()); new_as.reset(); new_xs.reset(); unsigned sz1 = xs1.size(); unsigned sz2 = xs2.size(); unsigned i1 = 0; unsigned i2 = 0; while (true) { if (i1 == sz1) { // copy remaining entries from as2*xs2 while (i2 < sz2) { var x2 = xs2[i2]; if (UpdateOcc) m_occs[x2].push_back(eq_idx); if (UpdateQueue && solved(x2)) m_var_queue.insert(x2); new_as.push_back(Numeral()); m().mul(k, as2[i2], new_as.back()); new_xs.push_back(x2); i2++; } break; } if (i2 == sz2) { // copy remaining entries from as1*xs1 while (i1 < sz1) { new_as.push_back(as1[i1]); new_xs.push_back(xs1[i1]); i1++; } break; } var x1 = xs1[i1]; var x2 = xs2[i2]; if (x1 < x2) { new_as.push_back(as1[i1]); new_xs.push_back(xs1[i1]); i1++; } else if (x1 > x2) { if (UpdateOcc) m_occs[x2].push_back(eq_idx); if (UpdateQueue && solved(x2)) m_var_queue.insert(x2); new_as.push_back(Numeral()); m().mul(k, as2[i2], new_as.back()); new_xs.push_back(x2); i2++; } else { m().addmul(as1[i1], k, as2[i2], new_a); TRACE("euclidean_solver_add_mul", tout << "i1: " << i1 << ", i2: " << i2 << " new_a: " << m().to_string(new_a) << "\n"; tout << "as1: " << m().to_string(as1[i1]) << ", k: " << m().to_string(k) << ", as2: " << m().to_string(as2[i2]) << "\n";); if (m().is_zero(new_a)) { // variable was canceled if (UpdateOcc && x1 != except_var) m_occs[x1].erase(eq_idx); if (UpdateQueue && solved(x1) && m_var_queue.contains(x1)) m_var_queue.erase(x1); } else { new_as.push_back(new_a); new_xs.push_back(x1); } i1++; i2++; } } m().del(new_a); } template void apply_solution(var x, mpz_vector & as, var_vector & xs, mpz & c, mpq_vector & bs, justification_vector & js, unsigned eq_idx = null_eq_idx, var except_var = null_var) { SASSERT(solved(x)); unsigned idx = pos(xs, x); if (idx == UINT_MAX) return; mpz const & a1 = as[idx]; SASSERT(!m().is_zero(a1)); equation const & eq2 = *(m_solution[m_solved[x]]); SASSERT(eq2.pos_x(x) != UINT_MAX); SASSERT(m().is_minus_one(eq2.a(eq2.pos_x(x)))); TRACE("euclidean_solver_apply", tout << "applying: " << m().to_string(a1) << " * "; display(tout, eq2); tout << "\n"; for (unsigned i = 0; i < xs.size(); i++) tout << m().to_string(as[i]) << "*x" << xs[i] << " "; tout << "\n";); addmul(as, xs, a1, eq2.m_as, eq2.m_xs, m_tmp_as, m_tmp_xs, eq_idx, except_var); m().addmul(c, a1, eq2.m_c, c); m_tmp_as.swap(as); m_tmp_xs.swap(xs); SASSERT(as.size() == xs.size()); TRACE("euclidean_solver_apply", for (unsigned i = 0; i < xs.size(); i++) tout << m().to_string(as[i]) << "*x" << xs[i] << " "; tout << "\n";); addmul(bs, js, a1, eq2.m_bs, eq2.m_js, m_tmp_bs, m_tmp_xs); m_tmp_bs.swap(bs); m_tmp_xs.swap(js); SASSERT(pos(xs, x) == UINT_MAX); } void apply_solution(mpz_vector & as, var_vector & xs, mpz & c, mpq_vector & bs, justification_vector & js) { m_var_queue.reset(); schedule_var_subst(xs); while (!m_var_queue.empty()) { var x = m_var_queue.erase_min(); apply_solution(x, as, xs, c, bs, js); } } void apply_solution(equation & eq) { apply_solution(eq.m_as, eq.m_xs, eq.m_c, eq.m_bs, eq.m_js); } void display(std::ostream & out, equation const & eq) const { unsigned num = eq.js_size(); for (unsigned i = 0; i < num; i ++) { if (i > 0) out << " "; out << m().to_string(eq.b(i)) << "*j" << eq.j(i); } if (num > 0) out << " "; out << "|= "; num = eq.size(); for (unsigned i = 0; i < num; i++) { out << m().to_string(eq.a(i)) << "*x" << eq.x(i) << " + "; } out << m().to_string(eq.c()) << " = 0"; } void display(std::ostream & out, equations const & eqs) const { unsigned num = eqs.size(); for (unsigned i = 0; i < num; i++) { if (eqs[i]) { display(out, *(eqs[i])); out << "\n"; } } } void display(std::ostream & out) const { if (inconsistent()) { out << "inconsistent: "; display(out, *(m_equations[m_inconsistent])); out << "\n"; } out << "solution set:\n"; display(out, m_solution); out << "todo:\n"; display(out, m_equations); } void add_occs(unsigned eq_idx) { equation const & eq = *(m_equations[eq_idx]); unsigned sz = eq.size(); for (unsigned i = 0; i < sz; i++) m_occs[eq.x(i)].push_back(eq_idx); } imp(numeral_manager * m): m_manager(m == nullptr ? alloc(numeral_manager) : m), m_owns_m(m == nullptr), m_decompose_buffer(*m_manager), m_as_buffer(*m_manager), m_bs_buffer(*m_manager), m_tmp_as(*m_manager), m_tmp_bs(*m_manager), m_var_queue(16, elim_order_lt(m_solved)) { m_inconsistent = null_eq_idx; m_next_justification = 0; m_next_x = null_var; m_next_eq = null_eq_idx; } ~imp() { m().del(m_next_a); del_equations(m_equations); del_equations(m_solution); if (m_owns_m) dealloc(m_manager); } var mk_var(bool parameter) { var x = m_solved.size(); m_parameter.push_back(parameter); m_solved.push_back(null_eq_idx); m_occs.push_back(occs()); m_as_buffer.push_back(mpz()); m_var_queue.reserve(x+1); return x; } justification mk_justification() { justification r = m_next_justification; m_bs_buffer.push_back(mpq()); m_next_justification++; return r; } void assert_eq(unsigned num, mpz const * as, var const * xs, mpz const & c, justification j) { if (inconsistent()) return; equation * eq; if (j == null_justification) { eq = mk_eq(num, as, xs, c, 0, nullptr, nullptr); } else { mpq one(1); eq = mk_eq(num, as, xs, c, 1, &one, &j); } TRACE("euclidean_solver", tout << "new-eq:\n"; display(tout, *eq); tout << "\n";); unsigned eq_idx = m_equations.size(); m_equations.push_back(eq); apply_solution(*eq); normalize_eq(eq_idx); add_occs(eq_idx); TRACE("euclidean_solver", tout << "asserted:\n"; display(tout, *eq); tout << "\n";); } justification_vector const & get_justification() const { SASSERT(inconsistent()); return m_equations[m_inconsistent]->m_js; } template void neg_coeffs(svector & as) { unsigned sz = as.size(); for (unsigned i = 0; i < sz; i++) { m().neg(as[i]); } } void substitute_most_recent_solution(var x) { SASSERT(!m_solution.empty()); TRACE("euclidean_solver", tout << "applying solution for x" << x << "\n"; display(tout, *(m_solution.back())); tout << "\n";); occs & use_list = m_occs[x]; occs::iterator it = use_list.begin(); occs::iterator end = use_list.end(); for (; it != end; ++it) { unsigned eq_idx = *it; // remark we don't want to update the use_list of x while we are traversing it. equation & eq2 = *(m_equations[eq_idx]); TRACE("euclidean_solver", tout << "eq before substituting x" << x << "\n"; display(tout, eq2); tout << "\n";); apply_solution(x, eq2.m_as, eq2.m_xs, eq2.m_c, eq2.m_bs, eq2.m_js, eq_idx, x); TRACE("euclidean_solver", tout << "eq after substituting x" << x << "\n"; display(tout, eq2); tout << "\n";); normalize_eq(eq_idx); if (inconsistent()) break; } use_list.reset(); } void elim_unit() { SASSERT(m().is_one(m_next_a)); equation & eq = *(m_equations[m_next_eq]); TRACE("euclidean_solver", tout << "eliminating equation with unit coefficient:\n"; display(tout, eq); tout << "\n";); if (m_next_pos_a) { // neg coeffs... to make sure that m_next_x is -1 neg_coeffs(eq.m_as); neg_coeffs(eq.m_bs); m().neg(eq.m_c); } unsigned sz = eq.size(); for (unsigned i = 0; i < sz; i++) { m_occs[eq.x(i)].erase(m_next_eq); } m_solved[m_next_x] = m_solution.size(); m_solution.push_back(&eq); m_equations[m_next_eq] = 0; substitute_most_recent_solution(m_next_x); } void decompose(bool pos_a, mpz const & abs_a, mpz const & a_i, mpz & new_a_i, mpz & r_i) { mpz abs_a_i; bool pos_a_i = m().is_pos(a_i); m().set(abs_a_i, a_i); if (!pos_a_i) m().neg(abs_a_i); bool new_pos_a_i = pos_a_i; if (pos_a) new_pos_a_i = !new_pos_a_i; m().div(abs_a_i, abs_a, new_a_i); if (m().divides(abs_a, a_i)) { m().reset(r_i); } else { if (pos_a_i) m().submul(a_i, abs_a, new_a_i, r_i); else m().addmul(a_i, abs_a, new_a_i, r_i); } if (!new_pos_a_i) m().neg(new_a_i); m().del(abs_a_i); } void decompose_and_elim() { m_tmp_xs.reset(); mpz_buffer & buffer = m_decompose_buffer; buffer.reset(); var p = mk_var(true); mpz new_a_i; equation & eq = *(m_equations[m_next_eq]); TRACE("euclidean_solver", tout << "decompositing equation for x" << m_next_x << "\n"; display(tout, eq); tout << "\n";); unsigned sz = eq.size(); unsigned j = 0; for (unsigned i = 0; i < sz; i++) { var x_i = eq.x(i); if (x_i == m_next_x) { m().set(new_a_i, -1); buffer.push_back(new_a_i); m_tmp_xs.push_back(m_next_x); m_occs[x_i].erase(m_next_eq); } else { decompose(m_next_pos_a, m_next_a, eq.a(i), new_a_i, eq.m_as[j]); buffer.push_back(new_a_i); m_tmp_xs.push_back(x_i); if (m().is_zero(eq.m_as[j])) { m_occs[x_i].erase(m_next_eq); } else { eq.m_xs[j] = x_i; j++; } } } SASSERT(j < sz); // add parameter: p to new equality, and m_next_pos_a * m_next_a * p to current eq m().set(new_a_i, 1); buffer.push_back(new_a_i); m_tmp_xs.push_back(p); m().set(eq.m_as[j], m_next_a); if (!m_next_pos_a) m().neg(eq.m_as[j]); eq.m_xs[j] = p; j++; unsigned new_sz = j; // shrink current eq for (; j < sz; j++) m().del(eq.m_as[j]); eq.m_as.shrink(new_sz); eq.m_xs.shrink(new_sz); // adjust c mpz new_c; decompose(m_next_pos_a, m_next_a, eq.m_c, new_c, eq.m_c); // create auxiliary equation equation * new_eq = mk_eq(m_tmp_xs.size(), buffer.c_ptr(), m_tmp_xs.c_ptr(), new_c, 0, nullptr, nullptr, false); // new_eq doesn't need to normalized, since it has unit coefficients TRACE("euclidean_solver", tout << "decomposition: new parameter x" << p << " aux eq:\n"; display(tout, *new_eq); tout << "\n"; display(tout, eq); tout << "\n";); m_solved[m_next_x] = m_solution.size(); m_solution.push_back(new_eq); substitute_most_recent_solution(m_next_x); m().del(new_a_i); m().del(new_c); } bool solve() { if (inconsistent()) return false; TRACE("euclidean_solver", tout << "solving...\n"; display(tout);); while (select_next_var()) { CTRACE("euclidean_solver_bug", m_next_x == null_var, display(tout);); TRACE("euclidean_solver", tout << "eliminating x" << m_next_x << "\n";); if (m().is_one(m_next_a) || m().is_minus_one(m_next_a)) elim_unit(); else decompose_and_elim(); TRACE("euclidean_solver", display(tout);); if (inconsistent()) return false; } return true; } bool inconsistent() const { return m_inconsistent != null_eq_idx; } bool is_parameter(var x) const { return m_parameter[x]; } void normalize(unsigned num, mpz const * as, var const * xs, mpz const & c, mpz & a_prime, mpz & c_prime, justification_vector & js) { TRACE("euclidean_solver", tout << "before applying solution set\n"; for (unsigned i = 0; i < num; i++) { tout << m().to_string(as[i]) << "*x" << xs[i] << " "; } tout << "\n";); m_norm_xs_vector.reset(); m_norm_as_vector.reset(); for (unsigned i = 0; i < num; i++) { m_norm_xs_vector.push_back(xs[i]); m_norm_as_vector.push_back(mpz()); m().set(m_norm_as_vector.back(), as[i]); } sort(m_norm_as_vector, m_norm_xs_vector, m_as_buffer); m_norm_bs_vector.reset(); js.reset(); m().set(c_prime, c); apply_solution(m_norm_as_vector, m_norm_xs_vector, c_prime, m_norm_bs_vector, js); TRACE("euclidean_solver", tout << "after applying solution set\n"; for (unsigned i = 0; i < m_norm_as_vector.size(); i++) { tout << m().to_string(m_norm_as_vector[i]) << "*x" << m_norm_xs_vector[i] << " "; } tout << "\n";); // compute gcd of the result m_norm_as_vector if (m_norm_as_vector.empty()) { m().set(a_prime, 0); } else { mpz a; m().set(a_prime, m_norm_as_vector[0]); m().abs(a_prime); unsigned sz = m_norm_as_vector.size(); for (unsigned i = 1; i < sz; i++) { if (m().is_one(a_prime)) break; m().set(a, m_norm_as_vector[i]); m().abs(a); m().gcd(a_prime, a, a_prime); } m().del(a); } // REMARK: m_norm_bs_vector contains the linear combination of the justifications. It may be useful if we // decided (one day) to generate detailed proofs for this step. del_nums(m_norm_as_vector); del_nums(m_norm_bs_vector); } }; euclidean_solver::euclidean_solver(numeral_manager * m): m_imp(alloc(imp, m)) { } euclidean_solver::~euclidean_solver() { dealloc(m_imp); } euclidean_solver::numeral_manager & euclidean_solver::m() const { return m_imp->m(); } void euclidean_solver::reset() { numeral_manager * m = m_imp->m_manager; bool owns_m = m_imp->m_owns_m; m_imp->m_owns_m = false; dealloc(m_imp); m_imp = alloc(imp, m); m_imp->m_owns_m = owns_m; } euclidean_solver::var euclidean_solver::mk_var() { return m_imp->mk_var(false); } euclidean_solver::justification euclidean_solver::mk_justification() { return m_imp->mk_justification(); } void euclidean_solver::assert_eq(unsigned num, mpz const * as, var const * xs, mpz const & c, justification j) { m_imp->assert_eq(num, as, xs, c, j); } bool euclidean_solver::solve() { return m_imp->solve(); } euclidean_solver::justification_vector const & euclidean_solver::get_justification() const { return m_imp->get_justification(); } bool euclidean_solver::inconsistent() const { return m_imp->inconsistent(); } bool euclidean_solver::is_parameter(var x) const { return m_imp->is_parameter(x); } void euclidean_solver::normalize(unsigned num, mpz const * as, var const * xs, mpz const & c, mpz & a_prime, mpz & c_prime, justification_vector & js) { return m_imp->normalize(num, as, xs, c, a_prime, c_prime, js); } void euclidean_solver::display(std::ostream & out) const { m_imp->display(out); } z3-z3-4.8.7/src/math/euclid/euclidean_solver.h000066400000000000000000000051671356505360400211210ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: euclidean_solver.h Abstract: Euclidean Solver with support for explanations. Author: Leonardo de Moura (leonardo) 2011-07-08. Revision History: --*/ #ifndef EUCLIDEAN_SOLVER_H_ #define EUCLIDEAN_SOLVER_H_ #include "util/mpq.h" #include "util/vector.h" class euclidean_solver { struct imp; imp * m_imp; public: typedef unsigned var; typedef unsigned justification; typedef unsynch_mpq_manager numeral_manager; typedef svector justification_vector; static const justification null_justification = UINT_MAX; /** \brief If m == 0, then the solver will create its own numeral manager. */ euclidean_solver(numeral_manager * m); ~euclidean_solver(); numeral_manager & m() const; /** \brief Reset the state of the euclidean solver. */ void reset(); /** \brief Creates a integer variable. */ var mk_var(); /** \brief Creates a fresh justification id. */ justification mk_justification(); /** \brief Asserts an equation of the form as[0]*xs[0] + ... + as[num-1]*xs[num-1] + c = 0 with justification j. The numerals must be created using the numeral_manager m(). */ void assert_eq(unsigned num, mpz const * as, var const * xs, mpz const & c, justification j = null_justification); /** \brief Solve the current set of equations. Return false if it is inconsistent. */ bool solve(); /** \brief Return a set of justifications (proof) for the inconsitency. \pre inconsistent() */ justification_vector const & get_justification() const; bool inconsistent() const; /** \brief Return true if the variable is a "parameter" created by the Euclidean solver. */ bool is_parameter(var x) const; /** Given a linear polynomial as[0]*xs[0] + ... + as[num-1]*xs[num-1] + c and the current solution set, It applies the solution set to produce a polynomial of the for a_prime * p + c_prime, where a_prime * p represents a linear polynomial where the coefficient of every monomial is a multiple of a_prime. The justification is stored in js. Note that, this function does not return the actual p. The numerals must be created using the numeral_manager m(). */ void normalize(unsigned num, mpz const * as, var const * xs, mpz const & c, mpz & a_prime, mpz & c_prime, justification_vector & js); void display(std::ostream & out) const; }; #endif z3-z3-4.8.7/src/math/grobner/000077500000000000000000000000001356505360400156055ustar00rootroot00000000000000z3-z3-4.8.7/src/math/grobner/CMakeLists.txt000066400000000000000000000001261356505360400203440ustar00rootroot00000000000000z3_add_component(grobner SOURCES grobner.cpp COMPONENT_DEPENDENCIES ast ) z3-z3-4.8.7/src/math/grobner/grobner.cpp000066400000000000000000000727471356505360400177700ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: grobner.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-12-04. Revision History: --*/ #include "math/grobner/grobner.h" #include "ast/ast_pp.h" #include "ast/ast_ll_pp.h" #include "util/ref_util.h" // #define PROFILE_GB grobner::grobner(ast_manager & m, v_dependency_manager & d): m_manager(m), m_dep_manager(d), m_util(m), m_var_lt(m_var2weight), m_monomial_lt(m_var_lt), m_changed_leading_term(false), m_unsat(nullptr) { } grobner::~grobner() { flush(); } void grobner::flush() { dec_ref_map_keys(m_manager, m_var2weight); del_equations(0); } void grobner::del_equations(unsigned old_size) { SASSERT(m_equations_to_delete.size() >= old_size); equation_vector::iterator it = m_equations_to_delete.begin(); equation_vector::iterator end = m_equations_to_delete.end(); it += old_size; for (; it != end; ++it) { equation * eq = *it; if (eq) del_equation(eq); } m_equations_to_delete.shrink(old_size); } void grobner::del_equation(equation * eq) { m_processed.erase(eq); m_to_process.erase(eq); SASSERT(m_equations_to_delete[eq->m_bidx] == eq); m_equations_to_delete[eq->m_bidx] = 0; del_monomials(eq->m_monomials); dealloc(eq); } void grobner::del_monomials(ptr_vector& ms) { for (auto& m : ms) { del_monomial(m); } ms.reset(); } void grobner::del_monomial(monomial * m) { for (expr * v : m->m_vars) { m_manager.dec_ref(v); } dealloc(m); } void grobner::unfreeze_equations(unsigned old_size) { SASSERT(m_equations_to_unfreeze.size() >= old_size); equation_vector::iterator it = m_equations_to_unfreeze.begin(); equation_vector::iterator end = m_equations_to_unfreeze.end(); it += old_size; for (; it != end; ++it) { equation * eq = *it; m_to_process.insert(eq); } m_equations_to_unfreeze.shrink(old_size); } void grobner::push_scope() { m_scopes.push_back(scope()); scope & s = m_scopes.back(); s.m_equations_to_unfreeze_lim = m_equations_to_unfreeze.size(); s.m_equations_to_delete_lim = m_equations_to_delete.size(); } void grobner::pop_scope(unsigned num_scopes) { SASSERT(num_scopes >= get_scope_level()); unsigned new_lvl = get_scope_level() - num_scopes; scope & s = m_scopes[new_lvl]; unfreeze_equations(s.m_equations_to_unfreeze_lim); del_equations(s.m_equations_to_delete_lim); m_scopes.shrink(new_lvl); } void grobner::reset() { flush(); m_processed.reset(); m_to_process.reset(); m_equations_to_unfreeze.reset(); m_equations_to_delete.reset(); m_unsat = nullptr; } void grobner::display_var(std::ostream & out, expr * var) const { if (is_app(var) && to_app(var)->get_num_args() > 0) out << mk_bounded_pp(var, m_manager); else out << mk_pp(var, m_manager); } void grobner::display_vars(std::ostream & out, unsigned num_vars, expr * const * vars) const { for (unsigned i = 0; i < num_vars; i++) { display_var(out, vars[i]); out << " "; } } void grobner::display_monomial(std::ostream & out, monomial const & m) const { if (!m.m_coeff.is_one() || m.m_vars.empty()) { out << m.m_coeff; if (!m.m_vars.empty()) out << "*"; } if (!m.m_vars.empty()) { ptr_vector::const_iterator it = m.m_vars.begin(); ptr_vector::const_iterator end = m.m_vars.end(); unsigned power = 1; expr * prev = *it; it++; for (; it != end; ++it) { expr * curr = *it; if (curr == prev) { power++; } else { display_var(out, prev); if (power > 1) out << "^" << power; power = 1; prev = curr; out << "*"; } } display_var(out, prev); if (power > 1) out << "^" << power; } } void grobner::display_monomials(std::ostream & out, unsigned num_monomials, monomial * const * monomials) const { bool first = true; for (unsigned i = 0; i < num_monomials; i++) { monomial const * m = monomials[i]; if (first) first = false; else out << " + "; display_monomial(out, *m); } } void grobner::display_equation(std::ostream & out, equation const & eq) const { display_monomials(out, eq.m_monomials.size(), eq.m_monomials.c_ptr()); out << " = 0\n"; } void grobner::display_equations(std::ostream & out, equation_set const & v, char const * header) const { if (!v.empty()) { out << header << "\n"; for (equation const* eq : v) display_equation(out, *eq); } } void grobner::display(std::ostream & out) const { display_equations(out, m_processed, "processed:"); display_equations(out, m_to_process, "to process:"); } void grobner::set_weight(expr * n, int weight) { if (weight == 0) return; if (!m_var2weight.contains(n)) m_manager.inc_ref(n); m_var2weight.insert(n, weight); } /** \brief Update equation using the new variable order. Return true if the leading term was modified. */ bool grobner::update_order(equation * eq) { if (eq->get_num_monomials() == 0) return false; monomial * first = eq->m_monomials[0]; for (monomial* m : eq->m_monomials) { std::stable_sort(m->m_vars.begin(), m->m_vars.end(), m_var_lt); } std::stable_sort(eq->m_monomials.begin(), eq->m_monomials.end(), m_monomial_lt); return eq->m_monomials[0] != first; } void grobner::update_order(equation_set & s, bool processed) { ptr_buffer to_remove; for (equation * eq : s) { if (update_order(eq)) { if (processed) { to_remove.push_back(eq); m_to_process.insert(eq); } } } for (equation* e : to_remove) s.erase(e); } void grobner::update_order() { update_order(m_to_process, false); update_order(m_processed, true); } bool grobner::var_lt::operator()(expr * v1, expr * v2) const { if (v1 == v2) return false; int w1 = 0; int w2 = 0; m_var2weight.find(v1, w1); m_var2weight.find(v2, w2); return (w1 > w2) || (w1 == w2 && v1->get_id() < v2->get_id()); } bool grobner::monomial_lt::operator()(monomial * m1, monomial * m2) const { // Using graded lex order. if (m1->get_degree() > m2->get_degree()) return true; if (m1->get_degree() < m2->get_degree()) return false; ptr_vector::iterator it1 = m1->m_vars.begin(); ptr_vector::iterator it2 = m2->m_vars.begin(); ptr_vector::iterator end1 = m1->m_vars.end(); for (; it1 != end1; ++it1, ++it2) { expr * v1 = *it1; expr * v2 = *it2; if (v1 == v2) continue; if (m_var_lt(v1, v2)) return true; if (v1 != v2) return false; } return false; } inline void grobner::add_var(monomial * m, expr * v) { SASSERT(!m_util.is_numeral(v)); m_manager.inc_ref(v); m->m_vars.push_back(v); } grobner::monomial * grobner::mk_monomial(rational const & coeff, unsigned num_vars, expr * const * vars) { monomial * r = alloc(monomial); r->m_coeff = coeff; for (unsigned i = 0; i < num_vars; i++) add_var(r, vars[i]); std::stable_sort(r->m_vars.begin(), r->m_vars.end(), m_var_lt); return r; } grobner::monomial * grobner::mk_monomial(rational const & coeff, expr * m) { monomial * r = alloc(monomial); if (m_util.is_numeral(m, r->m_coeff)) { r->m_coeff *= coeff; return r; } if (m_util.is_mul(m)) { expr * body = m; SASSERT(!m_util.is_numeral(to_app(m)->get_arg(1))); // monomial is in normal form if (m_util.is_numeral(to_app(m)->get_arg(0), r->m_coeff)) { r->m_coeff *= coeff; body = to_app(m)->get_arg(1); } else { r->m_coeff = coeff; } while (m_util.is_mul(body)) { add_var(r, to_app(body)->get_arg(0)); body = to_app(body)->get_arg(1); } add_var(r, body); std::stable_sort(r->m_vars.begin(), r->m_vars.end(), m_var_lt); } else { r->m_coeff = coeff; r->m_vars.push_back(m); m_manager.inc_ref(m); } return r; } void grobner::init_equation(equation * eq, v_dependency * d) { eq->m_scope_lvl = get_scope_level(); unsigned bidx = m_equations_to_delete.size(); eq->m_bidx = bidx; eq->m_dep = d; eq->m_lc = true; m_equations_to_delete.push_back(eq); SASSERT(m_equations_to_delete[eq->m_bidx] == eq); } void grobner::assert_eq_0(unsigned num_monomials, monomial * const * monomials, v_dependency * ex) { ptr_vector ms; ms.append(num_monomials, monomials); std::stable_sort(ms.begin(), ms.end(), m_monomial_lt); merge_monomials(ms); if (!ms.empty()) { normalize_coeff(ms); equation * eq = alloc(equation); eq->m_monomials.swap(ms); init_equation(eq, ex); m_to_process.insert(eq); } } void grobner::assert_eq_0(unsigned num_monomials, rational const * coeffs, expr * const * monomials, v_dependency * ex) { #define MK_EQ(COEFF) \ ptr_vector ms; \ for (unsigned i = 0; i < num_monomials; i++) \ ms.push_back(mk_monomial(COEFF, monomials[i])); \ std::stable_sort(ms.begin(), ms.end(), m_monomial_lt); \ merge_monomials(ms); \ if (!ms.empty()) { \ equation * eq = alloc(equation); \ normalize_coeff(ms); \ eq->m_monomials.swap(ms); \ init_equation(eq, ex); \ m_to_process.insert(eq); \ } MK_EQ(coeffs[i]); } void grobner::assert_eq_0(unsigned num_monomials, expr * const * monomials, v_dependency * ex) { rational one(1); MK_EQ(one); } void grobner::extract_monomials(expr * lhs, ptr_buffer & monomials) { while (m_util.is_add(lhs)) { SASSERT(!m_util.is_add(to_app(lhs)->get_arg(0))); monomials.push_back(to_app(lhs)->get_arg(0)); lhs = to_app(lhs)->get_arg(1); } monomials.push_back(lhs); } void grobner::assert_eq(expr * eq, v_dependency * ex) { SASSERT(m_manager.is_eq(eq)); expr * lhs = to_app(eq)->get_arg(0); expr * rhs = to_app(eq)->get_arg(1); SASSERT(m_util.is_numeral(rhs)); ptr_buffer monomials; extract_monomials(lhs, monomials); rational c; bool is_int = false; m_util.is_numeral(rhs, c, is_int); expr_ref new_c(m_manager); if (!c.is_zero()) { c.neg(); new_c = m_util.mk_numeral(c, is_int); monomials.push_back(new_c); } assert_eq_0(monomials.size(), monomials.c_ptr(), ex); } void grobner::assert_monomial_tautology(expr * m) { equation * eq = alloc(equation); eq->m_monomials.push_back(mk_monomial(rational(1), m)); // create (quote m) monomial * m1 = alloc(monomial); m1->m_coeff = rational(-1); m_manager.inc_ref(m); m1->m_vars.push_back(m); eq->m_monomials.push_back(m1); normalize_coeff(eq->m_monomials); init_equation(eq, static_cast(nullptr)); \ m_to_process.insert(eq); } /** \brief Return true if the body of m1 and m2 are equal */ bool grobner::is_eq_monomial_body(monomial const * m1, monomial const * m2) { if (m1->get_degree() != m2->get_degree()) return false; ptr_vector::const_iterator it1 = m1->m_vars.begin(); ptr_vector::const_iterator it2 = m2->m_vars.begin(); ptr_vector::const_iterator end1 = m1->m_vars.end(); for (; it1 != end1; ++it1, ++it2) { expr * v1 = *it1; expr * v2 = *it2; if (v1 != v2) return false; } return true; } /** \brief Merge monomials (* c1 m) (* c2 m). \remark This method assumes the monomials are sorted. */ void grobner::merge_monomials(ptr_vector & monomials) { TRACE("grobner", tout << "before merging monomials:\n"; display_monomials(tout, monomials.size(), monomials.c_ptr()); tout << "\n";); unsigned j = 0; unsigned sz = monomials.size(); if (sz == 0) return; SASSERT(&m_del_monomials != &monomials); ptr_vector& to_delete = m_del_monomials; to_delete.reset(); m_manager.limit().inc(sz); for (unsigned i = 1; i < sz; ++i) { monomial * m1 = monomials[j]; monomial * m2 = monomials[i]; if (is_eq_monomial_body(m1, m2)) { m1->m_coeff += m2->m_coeff; to_delete.push_back(m2); } else { if (m1->m_coeff.is_zero()) to_delete.push_back(m1); else j++; monomials[j] = m2; } } SASSERT(j < sz); monomial * m1 = monomials[j]; if (m1->m_coeff.is_zero()) to_delete.push_back(m1); else j++; monomials.shrink(j); del_monomials(to_delete); TRACE("grobner", tout << "after merging monomials:\n"; display_monomials(tout, monomials.size(), monomials.c_ptr()); tout << "\n";); } /** \brief Divide the coefficients by the coefficient of the leading term. */ void grobner::normalize_coeff(ptr_vector & monomials) { if (monomials.empty()) return; rational c = monomials[0]->m_coeff; if (c.is_one()) return; unsigned sz = monomials.size(); for (unsigned i = 0; i < sz; i++) monomials[i]->m_coeff /= c; } /** \brief Simplify the given monomials */ void grobner::simplify(ptr_vector & monomials) { std::stable_sort(monomials.begin(), monomials.end(), m_monomial_lt); merge_monomials(monomials); normalize_coeff(monomials); } /** \brief Return true if the equation is of the form k = 0, where k is a numeral different from zero. \remark This method assumes the equation is simplified. */ inline bool grobner::is_inconsistent(equation * eq) const { SASSERT(!(eq->m_monomials.size() == 1 && eq->m_monomials[0]->get_degree() == 0) || !eq->m_monomials[0]->m_coeff.is_zero()); return eq->m_monomials.size() == 1 && eq->m_monomials[0]->get_degree() == 0; } /** \brief Return true if the equation is of the form 0 = 0. */ inline bool grobner::is_trivial(equation * eq) const { return eq->m_monomials.empty(); } /** \brief Sort monomials, and merge monomials with the same body. */ void grobner::simplify(equation * eq) { simplify(eq->m_monomials); if (is_inconsistent(eq) && !m_unsat) m_unsat = eq; } /** \brief Return true if monomial m1 is (* c1 M) and m2 is (* c2 M M'). Store M' in rest. \remark This method assumes the variables of m1 and m2 are sorted. */ bool grobner::is_subset(monomial const * m1, monomial const * m2, ptr_vector & rest) const { unsigned i1 = 0; unsigned i2 = 0; unsigned sz1 = m1->m_vars.size(); unsigned sz2 = m2->m_vars.size(); if (sz1 <= sz2) { while (true) { if (i1 >= sz1) { for (; i2 < sz2; i2++) rest.push_back(m2->m_vars[i2]); TRACE("grobner", tout << "monomail: "; display_monomial(tout, *m1); tout << " is a subset of "; display_monomial(tout, *m2); tout << "\n"; tout << "rest: "; display_vars(tout, rest.size(), rest.c_ptr()); tout << "\n";); return true; } if (i2 >= sz2) break; expr * var1 = m1->m_vars[i1]; expr * var2 = m2->m_vars[i2]; if (var1 == var2) { i1++; i2++; continue; } if (m_var_lt(var2, var1)) { i2++; rest.push_back(var2); continue; } SASSERT(m_var_lt(var1, var2)); break; } } // is not subset TRACE("grobner", tout << "monomail: "; display_monomial(tout, *m1); tout << " is not a subset of "; display_monomial(tout, *m2); tout << "\n";); return false; } /** \brief Multiply the monomials of source starting at position start_idx by (coeff * vars), and store the resultant monomials at result. */ void grobner::mul_append(unsigned start_idx, equation const * source, rational const & coeff, ptr_vector const & vars, ptr_vector & result) { unsigned sz = source->get_num_monomials(); for (unsigned i = start_idx; i < sz; i++) { monomial const * m = source->get_monomial(i); monomial * new_m = alloc(monomial); new_m->m_coeff = m->m_coeff; new_m->m_coeff *= coeff; new_m->m_vars.append(m->m_vars.size(), m->m_vars.c_ptr()); new_m->m_vars.append(vars.size(), vars.c_ptr()); for (expr* e : new_m->m_vars) m_manager.inc_ref(e); std::stable_sort(new_m->m_vars.begin(), new_m->m_vars.end(), m_var_lt); result.push_back(new_m); } } /** \brief Copy the given monomial. */ grobner::monomial * grobner::copy_monomial(monomial const * m) { monomial * r = alloc(monomial); r->m_coeff = m->m_coeff; for (expr* e : m->m_vars) add_var(r, e); return r; } /** \brief Copy the given equation. */ grobner::equation * grobner::copy_equation(equation const * eq) { equation * r = alloc(equation); unsigned sz = eq->get_num_monomials(); for (unsigned i = 0; i < sz; i++) r->m_monomials.push_back(copy_monomial(eq->get_monomial(i))); init_equation(r, eq->m_dep); r->m_lc = eq->m_lc; return r; } /** \brief Simplify the target equation using the source as a rewrite rule. Return 0 if target was not simplified. Return target if target was simplified but source->m_scope_lvl <= target->m_scope_lvl. Return new_equation if source->m_scope_lvl > target->m_scope_lvl, moreover target is freezed, and new_equation contains the result. */ grobner::equation * grobner::simplify(equation const * source, equation * target) { TRACE("grobner", tout << "simplifying: "; display_equation(tout, *target); tout << "using: "; display_equation(tout, *source);); if (source->get_num_monomials() == 0) return nullptr; m_stats.m_simplify++; bool result = false; bool simplified; do { simplified = false; unsigned i = 0; unsigned j = 0; unsigned sz = target->m_monomials.size(); monomial const * LT = source->get_monomial(0); ptr_vector & new_monomials = m_tmp_monomials; new_monomials.reset(); ptr_vector & rest = m_tmp_vars1; for (; i < sz; i++) { monomial * curr = target->m_monomials[i]; rest.reset(); if (is_subset(LT, curr, rest)) { if (i == 0) m_changed_leading_term = true; if (source->m_scope_lvl > target->m_scope_lvl) { target = copy_equation(target); SASSERT(target->m_scope_lvl >= source->m_scope_lvl); } if (!result) { // first time that source is being applied. target->m_dep = m_dep_manager.mk_join(target->m_dep, source->m_dep); } simplified = true; result = true; rational coeff = curr->m_coeff; coeff /= LT->m_coeff; coeff.neg(); if (!rest.empty()) target->m_lc = false; mul_append(1, source, coeff, rest, new_monomials); del_monomial(curr); target->m_monomials[i] = 0; } else { target->m_monomials[j] = curr; j++; } } if (simplified) { target->m_monomials.shrink(j); target->m_monomials.append(new_monomials.size(), new_monomials.c_ptr()); simplify(target); } } while (simplified && !m_manager.canceled()); TRACE("grobner", tout << "result: "; display_equation(tout, *target);); return result ? target : nullptr; } /** \brief Simplify given equation using processed equalities. Return 0, if the equation was not simplified. Return eq, if the equation was simplified using destructive updates. Return new_eq otherwise. */ grobner::equation * grobner::simplify_using_processed(equation * eq) { bool result = false; bool simplified; TRACE("grobner", tout << "simplifying: "; display_equation(tout, *eq); tout << "using already processed equalities\n";); do { simplified = false; for (equation const* p : m_processed) { equation * new_eq = simplify(p, eq); if (new_eq) { result = true; simplified = true; eq = new_eq; } if (m_manager.canceled()) { return nullptr; } } } while (simplified); TRACE("grobner", tout << "simplification result: "; display_equation(tout, *eq);); return result ? eq : nullptr; } /** \brief Return true if eq1 is a better than e2, for being the next equation to be processed. */ bool grobner::is_better_choice(equation * eq1, equation * eq2) { if (!eq2) return true; if (eq1->m_monomials.empty()) return true; if (eq2->m_monomials.empty()) return false; if (eq1->m_monomials[0]->get_degree() < eq2->m_monomials[0]->get_degree()) return true; if (eq1->m_monomials[0]->get_degree() > eq2->m_monomials[0]->get_degree()) return false; return eq1->m_monomials.size() < eq2->m_monomials.size(); } /** \brief Pick next unprocessed equation */ grobner::equation * grobner::pick_next() { equation * r = nullptr; ptr_buffer to_delete; for (equation * curr : m_to_process) { if (is_trivial(curr)) to_delete.push_back(curr); else if (is_better_choice(curr, r)) r = curr; } for (equation * e : to_delete) del_equation(e); if (r) m_to_process.erase(r); TRACE("grobner", tout << "selected equation: "; if (!r) tout << "\n"; else display_equation(tout, *r);); return r; } /** \brief Use the given equation to simplify processed terms. */ bool grobner::simplify_processed(equation * eq) { ptr_buffer to_insert; ptr_buffer to_remove; ptr_buffer to_delete; equation_set::iterator it = m_processed.begin(); equation_set::iterator end = m_processed.end(); for (; it != end && !m_manager.canceled(); ++it) { equation * curr = *it; m_changed_leading_term = false; // if the leading term is simplified, then the equation has to be moved to m_to_process equation * new_curr = simplify(eq, curr); if (new_curr != nullptr) { if (new_curr != curr) { m_equations_to_unfreeze.push_back(curr); to_remove.push_back(curr); if (m_changed_leading_term) { m_to_process.insert(new_curr); to_remove.push_back(curr); } else { to_insert.push_back(new_curr); } curr = new_curr; } else { if (m_changed_leading_term) { m_to_process.insert(curr); to_remove.push_back(curr); } } } if (is_trivial(curr)) to_delete.push_back(curr); } for (equation* eq : to_insert) m_processed.insert(eq); for (equation* eq : to_remove) m_processed.erase(eq); for (equation* eq : to_delete) del_equation(eq); return !m_manager.canceled(); } /** \brief Use the given equation to simplify to-process terms. */ void grobner::simplify_to_process(equation * eq) { ptr_buffer to_insert; ptr_buffer to_remove; ptr_buffer to_delete; for (equation* curr : m_to_process) { equation * new_curr = simplify(eq, curr); if (new_curr != nullptr && new_curr != curr) { m_equations_to_unfreeze.push_back(curr); to_insert.push_back(new_curr); to_remove.push_back(curr); curr = new_curr; } if (is_trivial(curr)) to_delete.push_back(curr); } for (equation* eq : to_insert) m_to_process.insert(eq); for (equation* eq : to_remove) m_to_process.erase(eq); for (equation* eq : to_delete) del_equation(eq); } /** \brief If m1 = (* c M M1) and m2 = (* d M M2) and M is non empty, then return true and store M1 in rest1 and M2 in rest2. */ bool grobner::unify(monomial const * m1, monomial const * m2, ptr_vector & rest1, ptr_vector & rest2) { TRACE("grobner", tout << "unifying: "; display_monomial(tout, *m1); tout << " "; display_monomial(tout, *m2); tout << "\n";); bool found_M = false; unsigned i1 = 0; unsigned i2 = 0; unsigned sz1 = m1->m_vars.size(); unsigned sz2 = m2->m_vars.size(); while (true) { if (i1 >= sz1) { if (found_M) { for (; i2 < sz2; i2++) rest2.push_back(m2->m_vars[i2]); return true; } return false; } if (i2 >= sz2) { if (found_M) { for (; i1 < sz1; i1++) rest1.push_back(m1->m_vars[i1]); return true; } return false; } expr * var1 = m1->m_vars[i1]; expr * var2 = m2->m_vars[i2]; if (var1 == var2) { found_M = true; i1++; i2++; } else if (m_var_lt(var2, var1)) { i2++; rest2.push_back(var2); } else { i1++; rest1.push_back(var1); } } } /** \brief Superpose the given two equations. */ void grobner::superpose(equation * eq1, equation * eq2) { if (eq1->m_monomials.empty() || eq2->m_monomials.empty()) return; m_stats.m_superpose++; ptr_vector & rest1 = m_tmp_vars1; rest1.reset(); ptr_vector & rest2 = m_tmp_vars2; rest2.reset(); if (unify(eq1->m_monomials[0], eq2->m_monomials[0], rest1, rest2)) { TRACE("grobner", tout << "superposing:\n"; display_equation(tout, *eq1); display_equation(tout, *eq2); tout << "rest1: "; display_vars(tout, rest1.size(), rest1.c_ptr()); tout << "\n"; tout << "rest2: "; display_vars(tout, rest2.size(), rest2.c_ptr()); tout << "\n";); ptr_vector & new_monomials = m_tmp_monomials; new_monomials.reset(); mul_append(1, eq1, eq2->m_monomials[0]->m_coeff, rest2, new_monomials); rational c = eq1->m_monomials[0]->m_coeff; c.neg(); mul_append(1, eq2, c, rest1, new_monomials); simplify(new_monomials); TRACE("grobner", tout << "resulting monomials: "; display_monomials(tout, new_monomials.size(), new_monomials.c_ptr()); tout << "\n";); if (new_monomials.empty()) return; m_num_new_equations++; equation * new_eq = alloc(equation); new_eq->m_monomials.swap(new_monomials); init_equation(new_eq, m_dep_manager.mk_join(eq1->m_dep, eq2->m_dep)); new_eq->m_lc = false; m_to_process.insert(new_eq); } } /** \brief Superpose the given equations with the equations in m_processed. */ void grobner::superpose(equation * eq) { for (equation * curr : m_processed) { superpose(eq, curr); } } void grobner::compute_basis_init() { m_stats.m_compute_basis++; m_num_new_equations = 0; } bool grobner::compute_basis_step() { equation * eq = pick_next(); if (!eq) return true; m_stats.m_num_processed++; #ifdef PROFILE_GB if (m_stats.m_num_processed % 100 == 0) { verbose_stream() << "[grobner] " << m_processed.size() << " " << m_to_process.size() << "\n"; } #endif equation * new_eq = simplify_using_processed(eq); if (new_eq != nullptr && eq != new_eq) { // equation was updated using non destructive updates m_equations_to_unfreeze.push_back(eq); eq = new_eq; } if (m_manager.canceled()) return false; if (!simplify_processed(eq)) return false; superpose(eq); m_processed.insert(eq); simplify_to_process(eq); TRACE("grobner", tout << "end of iteration:\n"; display(tout);); return false; } bool grobner::compute_basis(unsigned threshold) { compute_basis_init(); while (m_num_new_equations < threshold && !m_manager.canceled()) { if (compute_basis_step()) return true; } return false; } void grobner::copy_to(equation_set const & s, ptr_vector & result) const { for (equation * e : s) result.push_back(e); } /** \brief Copy the equations in m_processed and m_to_process to result. \warning This equations can be deleted when compute_basis is invoked. */ void grobner::get_equations(ptr_vector & result) const { copy_to(m_processed, result); copy_to(m_to_process, result); } z3-z3-4.8.7/src/math/grobner/grobner.h000066400000000000000000000220011356505360400174070ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: grobner.h Abstract: Author: Leonardo de Moura (leonardo) 2008-12-04. Revision History: --*/ #ifndef GROBNER_H_ #define GROBNER_H_ #include "ast/ast.h" #include "ast/arith_decl_plugin.h" #include "util/heap.h" #include "util/obj_hashtable.h" #include "util/region.h" #include "util/dependency.h" struct grobner_stats { long m_simplify; long m_superpose; long m_compute_basis; long m_num_processed; void reset() { memset(this, 0, sizeof(grobner_stats)); } grobner_stats() { reset(); } }; /** \brief Simple Grobner basis implementation with no indexing. */ class grobner { protected: struct monomial_lt; public: grobner_stats m_stats; class monomial { rational m_coeff; ptr_vector m_vars; //!< sorted variables friend class grobner; friend struct monomial_lt; monomial() {} public: rational const & get_coeff() const { return m_coeff; } unsigned get_degree() const { return m_vars.size(); } unsigned get_size() const { return get_degree(); } expr * get_var(unsigned idx) const { return m_vars[idx]; } }; class equation { unsigned m_scope_lvl; //!< scope level when this equation was created. unsigned m_bidx:31; //!< position at m_equations_to_delete unsigned m_lc:1; //!< true if equation if a linear combination of the input equations. ptr_vector m_monomials; //!< sorted monomials v_dependency * m_dep; //!< justification for the equality friend class grobner; equation() {} public: unsigned get_num_monomials() const { return m_monomials.size(); } monomial const * get_monomial(unsigned idx) const { return m_monomials[idx]; } monomial * const * get_monomials() const { return m_monomials.c_ptr(); } v_dependency * get_dependency() const { return m_dep; } unsigned hash() const { return m_bidx; } bool is_linear_combination() const { return m_lc; } }; protected: static bool is_eq_monomial_body(monomial const * m1, monomial const * m2); struct var_lt { obj_map & m_var2weight; var_lt(obj_map & m):m_var2weight(m) {} bool operator()(expr * v1, expr * v2) const; }; struct monomial_lt { var_lt & m_var_lt; monomial_lt(var_lt & lt):m_var_lt(lt) {} bool operator()(monomial * m1, monomial * m2) const; }; typedef obj_hashtable equation_set; typedef ptr_vector equation_vector; ast_manager & m_manager; v_dependency_manager & m_dep_manager; arith_util m_util; obj_map m_var2weight; var_lt m_var_lt; monomial_lt m_monomial_lt; equation_set m_processed; equation_set m_to_process; equation_vector m_equations_to_unfreeze; equation_vector m_equations_to_delete; bool m_changed_leading_term; // set to true, if the leading term was simplified. equation * m_unsat; struct scope { unsigned m_equations_to_unfreeze_lim; unsigned m_equations_to_delete_lim; }; svector m_scopes; ptr_vector m_tmp_monomials; ptr_vector m_del_monomials; ptr_vector m_tmp_vars1; ptr_vector m_tmp_vars2; unsigned m_num_new_equations; // temporary variable bool is_monomial_lt(monomial const & m1, monomial const & m2) const; void display_vars(std::ostream & out, unsigned num_vars, expr * const * vars) const; void display_var(std::ostream & out, expr * var) const; void display_monomials(std::ostream & out, unsigned num_monomials, monomial * const * monomials) const; void display_equations(std::ostream & out, equation_set const & v, char const * header) const; void del_equations(unsigned old_size); void del_monomials(ptr_vector& monomials); void unfreeze_equations(unsigned old_size); void del_equation(equation * eq); void flush(); bool update_order(equation * eq); void update_order(equation_set & s, bool processed); void add_var(monomial * m, expr * v); monomial * mk_monomial(rational const & coeff, expr * m); void init_equation(equation * eq, v_dependency * d); void extract_monomials(expr * lhs, ptr_buffer & monomials); void merge_monomials(ptr_vector & monomials); bool is_inconsistent(equation * eq) const; bool is_trivial(equation * eq) const; void normalize_coeff(ptr_vector & monomials); void simplify(ptr_vector & monomials); void simplify(equation * eq); bool is_subset(monomial const * m1, monomial const * m2, ptr_vector & rest) const; void mul_append(unsigned start_idx, equation const * source, rational const & coeff, ptr_vector const & vars, ptr_vector & result); monomial * copy_monomial(monomial const * m); equation * copy_equation(equation const * eq); equation * simplify(equation const * source, equation * target); equation * simplify_using_processed(equation * eq); bool is_better_choice(equation * eq1, equation * eq2); equation * pick_next(); bool simplify_processed(equation * eq); void simplify_to_process(equation * eq); bool unify(monomial const * m1, monomial const * m2, ptr_vector & rest1, ptr_vector & rest2); void superpose(equation * eq1, equation * eq2); void superpose(equation * eq); void copy_to(equation_set const & s, ptr_vector & result) const; public: grobner(ast_manager & m, v_dependency_manager & dep_m); ~grobner(); unsigned get_scope_level() const { return m_scopes.size(); } /** \brief Set the weight of a term that is viewed as a variable by this module. The weight is used to order monomials. If the weight is not set for a term t, then the weight of t is assumed to be 0. */ void set_weight(expr * n, int weight); int get_weight(expr * n) const { int w = 0; m_var2weight.find(n, w); return w; } /** \brief Update equations after set_weight was invoked once or more. */ void update_order(); /** \brief Create a new monomial. The caller owns the monomial until it invokes assert_eq_0. A monomial cannot be use to create several equations. */ monomial * mk_monomial(rational const & coeff, unsigned num_vars, expr * const * vars); void del_monomial(monomial * m); /** \brief Assert the given equality. This method assumes eq is simplified. */ void assert_eq(expr * eq, v_dependency * ex = nullptr); /** \brief Assert the equality monomials[0] + ... + monomials[num_monomials - 1] = 0. This method assumes the monomials were simplified. */ void assert_eq_0(unsigned num_monomials, expr * const * monomials, v_dependency * ex = nullptr); /** \brief Assert the equality monomials[0] + ... + monomials[num_monomials - 1] = 0. This method assumes the monomials were simplified. */ void assert_eq_0(unsigned num_monomials, monomial * const * monomials, v_dependency * ex = nullptr); /** \brief Assert the equality coeffs[0] * monomials[0] + ... + coeffs[num_monomials-1] * monomials[num_monomials - 1] = 0. This method assumes the monomials were simplified. */ void assert_eq_0(unsigned num_monomials, rational const * coeffs, expr * const * monomials, v_dependency * ex = nullptr); /** \brief Assert the monomial tautology (quote (x_1 * ... * x_n)) - x_1 * ... * x_n = 0 */ void assert_monomial_tautology(expr * m); /** \brief Compute Grobner basis. Return true if the threshold was not reached. */ bool compute_basis(unsigned threshold); /** \brief Compute one step Grobner basis. Return true if there is no new equation to take. */ void compute_basis_init(); bool compute_basis_step(); unsigned get_num_new_equations() { return m_num_new_equations; } /** \brief Return true if an inconsistency was detected. */ bool inconsistent() const { return m_unsat != nullptr; } /** \brief Simplify the given expression using the equalities asserted using assert_eq. Store the result in 'result'. */ void simplify(expr * n, expr_ref & result); /** \brief Reset state. Remove all equalities asserted with assert_eq. */ void reset(); void get_equations(ptr_vector & result) const; void push_scope(); void pop_scope(unsigned num_scopes); void display_equation(std::ostream & out, equation const & eq) const; void display_monomial(std::ostream & out, monomial const & m) const; void display(std::ostream & out) const; }; #endif /* GROBNER_H_ */ z3-z3-4.8.7/src/math/hilbert/000077500000000000000000000000001356505360400156005ustar00rootroot00000000000000z3-z3-4.8.7/src/math/hilbert/CMakeLists.txt000066400000000000000000000001351356505360400203370ustar00rootroot00000000000000z3_add_component(hilbert SOURCES hilbert_basis.cpp COMPONENT_DEPENDENCIES util ) z3-z3-4.8.7/src/math/hilbert/heap_trie.h000066400000000000000000000506711356505360400177220ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: heap_trie.h Abstract: Heap trie structure. Structure that lets you retrieve point-wise smaller entries of a tuple. A lookup is to identify entries whose keys are point-wise dominated by the lookup key. Author: Nikolaj Bjorner (nbjorner) 2013-02-15. Notes: tries are unordered vectors of keys. This could be enhanced to use either heaps or sorting. The problem with using the heap implementation directly is that there is no way to retrieve elements less or equal to a key that is not already in the heap. If nodes have only a few elements, then this would also be a bloated data-structure to maintain. Nodes are not de-allocated. Their reference count indicates if they are valid. Possibly, add garbage collection. Maintaining sorted ranges for larger domains is another option. Another possible enhancement is to resplay the tree. Keep current key index in the nodes. --*/ #ifndef HEAP_TRIE_H_ #define HEAP_TRIE_H_ #include "util/map.h" #include "util/vector.h" #include "util/buffer.h" #include "util/statistics.h" #include "util/small_object_allocator.h" template class heap_trie { struct stats { unsigned m_num_inserts; unsigned m_num_removes; unsigned m_num_find_eq; unsigned m_num_find_le; unsigned m_num_find_le_nodes; stats() { reset(); } void reset() { memset(this, 0, sizeof(*this)); } }; enum node_t { trie_t, leaf_t }; class node { node_t m_type; unsigned m_ref; public: node(node_t t): m_type(t), m_ref(0) {} virtual ~node() {} node_t type() const { return m_type; } void inc_ref() { ++m_ref; } void dec_ref() { SASSERT(m_ref > 0); --m_ref; } unsigned ref_count() const { return m_ref; } virtual void display(std::ostream& out, unsigned indent) const = 0; virtual unsigned num_nodes() const = 0; virtual unsigned num_leaves() const = 0; }; class leaf : public node { Value m_value; public: leaf(): node(leaf_t) {} ~leaf() override {} Value const& get_value() const { return m_value; } void set_value(Value const& v) { m_value = v; } void display(std::ostream& out, unsigned indent) const override { out << " value: " << m_value; } unsigned num_nodes() const override { return 1; } unsigned num_leaves() const override { return this->ref_count()>0?1:0; } }; typedef buffer, true, 2> children_t; // lean trie node class trie : public node { children_t m_nodes; public: trie(): node(trie_t) {} ~trie() override { } node* find_or_insert(Key k, node* n) { for (unsigned i = 0; i < m_nodes.size(); ++i) { if (m_nodes[i].first == k) { return m_nodes[i].second; } } m_nodes.push_back(std::make_pair(k, n)); return n; } bool find(Key k, node*& n) const { for (unsigned i = 0; i < m_nodes.size(); ++i) { if (m_nodes[i].first == k) { n = m_nodes[i].second; return n->ref_count() > 0; } } return false; } // push nodes whose keys are <= key into vector. void find_le(KeyLE& le, Key key, ptr_vector& nodes) { for (unsigned i = 0; i < m_nodes.size(); ++i) { if (le.le(m_nodes[i].first, key)) { node* n = m_nodes[i].second; if (n->ref_count() > 0){ nodes.push_back(n); } } } } children_t const& nodes() const { return m_nodes; } children_t & nodes() { return m_nodes; } void display(std::ostream& out, unsigned indent) const override { for (unsigned j = 0; j < m_nodes.size(); ++j) { if (j != 0 || indent > 0) { out << "\n"; } for (unsigned i = 0; i < indent; ++i) { out << " "; } node* n = m_nodes[j].second; out << m_nodes[j].first << " refs: " << n->ref_count(); n->display(out, indent + 1); } } unsigned num_nodes() const override { unsigned sz = 1; for (unsigned j = 0; j < m_nodes.size(); ++j) { sz += m_nodes[j].second->num_nodes(); } return sz; } unsigned num_leaves() const override { unsigned sz = 0; for (unsigned j = 0; j < m_nodes.size(); ++j) { sz += m_nodes[j].second->num_leaves(); } return sz; } private: bool contains(Key k) { for (unsigned j = 0; j < m_nodes.size(); ++j) { if (m_nodes[j].first == k) { return true; } } return false; } }; small_object_allocator m_alloc; KeyLE& m_le; unsigned m_num_keys; unsigned_vector m_keys; unsigned m_do_reshuffle; node* m_root; stats m_stats; node* m_spare_leaf; node* m_spare_trie; public: heap_trie(KeyLE& le): m_alloc("heap_trie"), m_le(le), m_num_keys(0), m_do_reshuffle(4), m_root(nullptr), m_spare_leaf(nullptr), m_spare_trie(nullptr) {} ~heap_trie() { del_node(m_root); del_node(m_spare_leaf); del_node(m_spare_trie); } unsigned size() const { return m_root?m_root->num_leaves():0; } void reset(unsigned num_keys) { del_node(m_root); del_node(m_spare_leaf); del_node(m_spare_trie); m_num_keys = num_keys; m_keys.resize(num_keys); for (unsigned i = 0; i < num_keys; ++i) { m_keys[i] = i; } m_root = mk_trie(); m_spare_trie = mk_trie(); m_spare_leaf = mk_leaf(); } void insert(Key const* keys, Value const& val) { ++m_stats.m_num_inserts; insert(m_root, num_keys(), keys, m_keys.c_ptr(), val); #if 0 if (m_stats.m_num_inserts == (1 << m_do_reshuffle)) { m_do_reshuffle++; reorder_keys(); } #endif } bool find_eq(Key const* keys, Value& value) { ++m_stats.m_num_find_eq; node* n = m_root; node* m; for (unsigned i = 0; i < num_keys(); ++i) { if (!to_trie(n)->find(get_key(keys, i), m)) { return false; } n = m; } value = to_leaf(n)->get_value(); return true; } void find_all_le(Key const* keys, vector& values) { ++m_stats.m_num_find_le; ptr_vector todo[2]; todo[0].push_back(m_root); bool index = false; for (unsigned i = 0; i < num_keys(); ++i) { for (unsigned j = 0; j < todo[index].size(); ++j) { ++m_stats.m_num_find_le_nodes; to_trie(todo[index][j])->find_le(m_le, get_key(keys, i), todo[!index]); } todo[index].reset(); index = !index; } for (unsigned j = 0; j < todo[index].size(); ++j) { values.push_back(to_leaf(todo[index][j])->get_value()); } } // callback based find function class check_value { public: virtual bool operator()(Value const& v) = 0; }; bool find_le(Key const* keys, check_value& check) { ++m_stats.m_num_find_le; ++m_stats.m_num_find_le_nodes; return find_le(m_root, 0, keys, check); } void remove(Key const* keys) { ++m_stats.m_num_removes; // assumption: key is in table. node* n = m_root; node* m = nullptr; for (unsigned i = 0; i < num_keys(); ++i) { n->dec_ref(); VERIFY (to_trie(n)->find(get_key(keys, i), m)); n = m; } n->dec_ref(); } void reset_statistics() { m_stats.reset(); } void collect_statistics(statistics& st) const { st.update("heap_trie.num_inserts", m_stats.m_num_inserts); st.update("heap_trie.num_removes", m_stats.m_num_removes); st.update("heap_trie.num_find_eq", m_stats.m_num_find_eq); st.update("heap_trie.num_find_le", m_stats.m_num_find_le); st.update("heap_trie.num_find_le_nodes", m_stats.m_num_find_le_nodes); if (m_root) st.update("heap_trie.num_nodes", m_root->num_nodes()); unsigned_vector nums; ptr_vector todo; if (m_root) todo.push_back(m_root); while (!todo.empty()) { node* n = todo.back(); todo.pop_back(); if (is_trie(n)) { trie* t = to_trie(n); unsigned sz = t->nodes().size(); if (nums.size() <= sz) { nums.resize(sz+1); } ++nums[sz]; for (unsigned i = 0; i < sz; ++i) { todo.push_back(t->nodes()[i].second); } } } if (nums.size() < 16) nums.resize(16); st.update("heap_trie.num_1_children", nums[1]); st.update("heap_trie.num_2_children", nums[2]); st.update("heap_trie.num_3_children", nums[3]); st.update("heap_trie.num_4_children", nums[4]); st.update("heap_trie.num_5_children", nums[5]); st.update("heap_trie.num_6_children", nums[6]); st.update("heap_trie.num_7_children", nums[7]); st.update("heap_trie.num_8_children", nums[8]); st.update("heap_trie.num_9_children", nums[9]); st.update("heap_trie.num_10_children", nums[10]); st.update("heap_trie.num_11_children", nums[11]); st.update("heap_trie.num_12_children", nums[12]); st.update("heap_trie.num_13_children", nums[13]); st.update("heap_trie.num_14_children", nums[14]); st.update("heap_trie.num_15_children", nums[15]); unsigned sz = 0; for (unsigned i = 16; i < nums.size(); ++i) { sz += nums[i]; } st.update("heap_trie.num_16+_children", sz); } void display(std::ostream& out) const { m_root->display(out, 0); out << "\n"; } class iterator { ptr_vector m_path; unsigned_vector m_idx; vector m_keys; unsigned m_count; public: iterator(node* n) { if (!n) { m_count = UINT_MAX; } else { m_count = 0; first(n); } } Key const* keys() { return m_keys.c_ptr(); } Value const& value() const { return to_leaf(m_path.back())->get_value(); } iterator& operator++() { fwd(); return *this; } iterator operator++(int) { iterator tmp = *this; ++*this; return tmp; } bool operator==(iterator const& it) const {return m_count == it.m_count; } bool operator!=(iterator const& it) const {return m_count != it.m_count; } private: void first(node* r) { SASSERT(r->ref_count() > 0); while (is_trie(r)) { trie* t = to_trie(r); m_path.push_back(r); unsigned sz = t->nodes().size(); for (unsigned i = 0; i < sz; ++i) { r = t->nodes()[i].second; if (r->ref_count() > 0) { m_idx.push_back(i); m_keys.push_back(t->nodes()[i].first); break; } } } SASSERT(is_leaf(r)); m_path.push_back(r); } void fwd() { if (m_path.empty()) { m_count = UINT_MAX; return; } m_path.pop_back(); while (!m_path.empty()) { trie* t = to_trie(m_path.back()); unsigned idx = m_idx.back(); unsigned sz = t->nodes().size(); m_idx.pop_back(); m_keys.pop_back(); for (unsigned i = idx+1; i < sz; ++i) { node* r = t->nodes()[i].second; if (r->ref_count() > 0) { m_idx.push_back(i); m_keys.push_back(t->nodes()[i].first); first(r); ++m_count; return; } } m_path.pop_back(); } m_count = UINT_MAX; } }; iterator begin() const { return iterator(m_root); } iterator end() const { return iterator(0); } private: inline unsigned num_keys() const { return m_num_keys; } inline Key const& get_key(Key const* keys, unsigned i) const { return keys[m_keys[i]]; } struct KeyEq { bool operator()(Key const& k1, Key const& k2) const { return k1 == k2; } }; typedef hashtable key_set; struct key_info { unsigned m_index; unsigned m_index_size; key_info(unsigned i, unsigned sz): m_index(i), m_index_size(sz) {} bool operator<(key_info const& other) const { return (m_index_size < other.m_index_size) || ((m_index_size == other.m_index_size) && (m_index < other.m_index)); } }; void reorder_keys() { vector weights; weights.resize(num_keys()); unsigned_vector depth; ptr_vector nodes; depth.push_back(0); nodes.push_back(m_root); while (!nodes.empty()) { node* n = nodes.back(); unsigned d = depth.back(); nodes.pop_back(); depth.pop_back(); if (is_trie(n)) { trie* t = to_trie(n); unsigned sz = t->nodes().size(); for (unsigned i = 0; i < sz; ++i) { nodes.push_back(t->nodes()[i].second); depth.push_back(d+1); weights[d].insert(t->nodes()[i].first); } } } SASSERT(weights.size() == num_keys()); svector infos; unsigned sz = 0; bool is_sorted = true; for (unsigned i = 0; i < weights.size(); ++i) { unsigned sz2 = weights[i].size(); if (sz > sz2) { is_sorted = false; } sz = sz2; infos.push_back(key_info(i, sz)); } if (is_sorted) { return; } std::sort(infos.begin(), infos.end()); unsigned_vector sorted_keys, new_keys; for (unsigned i = 0; i < num_keys(); ++i) { unsigned j = infos[i].m_index; sorted_keys.push_back(j); new_keys.push_back(m_keys[j]); } // m_keys: i |-> key_index // new_keys: i |-> new_key_index // permutation: key_index |-> new_key_index SASSERT(sorted_keys.size() == num_keys()); SASSERT(new_keys.size() == num_keys()); SASSERT(m_keys.size() == num_keys()); iterator it = begin(); trie* new_root = mk_trie(); IF_VERBOSE(2, verbose_stream() << "before reshuffle: " << m_root->num_nodes() << " nodes\n";); for (; it != end(); ++it) { IF_VERBOSE(2, for (unsigned i = 0; i < num_keys(); ++i) { for (unsigned j = 0; j < num_keys(); ++j) { if (m_keys[j] == i) { verbose_stream() << it.keys()[j] << " "; break; } } } verbose_stream() << " |-> " << it.value() << "\n";); insert(new_root, num_keys(), it.keys(), sorted_keys.c_ptr(), it.value()); } del_node(m_root); m_root = new_root; for (unsigned i = 0; i < m_keys.size(); ++i) { m_keys[i] = new_keys[i]; } IF_VERBOSE(2, verbose_stream() << "after reshuffle: " << new_root->num_nodes() << " nodes\n";); IF_VERBOSE(2, it = begin(); for (; it != end(); ++it) { for (unsigned i = 0; i < num_keys(); ++i) { for (unsigned j = 0; j < num_keys(); ++j) { if (m_keys[j] == i) { verbose_stream() << it.keys()[j] << " "; break; } } } verbose_stream() << " |-> " << it.value() << "\n"; }); } bool find_le(node* n, unsigned index, Key const* keys, check_value& check) { if (index == num_keys()) { SASSERT(n->ref_count() > 0); bool r = check(to_leaf(n)->get_value()); IF_VERBOSE(2, for (unsigned j = 0; j < index; ++j) { verbose_stream() << " "; } verbose_stream() << to_leaf(n)->get_value() << (r?" hit\n":" miss\n");); return r; } else { Key const& key = get_key(keys, index); children_t& nodes = to_trie(n)->nodes(); for (unsigned i = 0; i < nodes.size(); ++i) { ++m_stats.m_num_find_le_nodes; node* m = nodes[i].second; IF_VERBOSE(2, for (unsigned j = 0; j < index; ++j) { verbose_stream() << " "; } verbose_stream() << nodes[i].first << " <=? " << key << " rc:" << m->ref_count() << "\n";); if (m->ref_count() > 0 && m_le.le(nodes[i].first, key) && find_le(m, index+1, keys, check)) { if (i > 0) { std::swap(nodes[i], nodes[0]); } return true; } } return false; } } void insert(node* n, unsigned num_keys, Key const* keys, unsigned const* permutation, Value const& val) { // assumption: key is not in table. for (unsigned i = 0; i < num_keys; ++i) { n->inc_ref(); n = insert_key(to_trie(n), (i + 1 == num_keys), keys[permutation[i]]); } n->inc_ref(); to_leaf(n)->set_value(val); SASSERT(n->ref_count() == 1); } node* insert_key(trie* n, bool is_leaf, Key const& key) { node* m1 = is_leaf?m_spare_leaf:m_spare_trie; node* m2 = n->find_or_insert(key, m1); if (m1 == m2) { if (is_leaf) { m_spare_leaf = mk_leaf(); } else { m_spare_trie = mk_trie(); } } return m2; } leaf* mk_leaf() { void* mem = m_alloc.allocate(sizeof(leaf)); return new (mem) leaf(); } trie* mk_trie() { void* mem = m_alloc.allocate(sizeof(trie)); return new (mem) trie(); } void del_node(node* n) { if (!n) { return; } if (is_trie(n)) { trie* t = to_trie(n); for (unsigned i = 0; i < t->nodes().size(); ++i) { del_node(t->nodes()[i].second); } t->~trie(); m_alloc.deallocate(sizeof(trie), t); } else { leaf* l = to_leaf(n); l->~leaf(); m_alloc.deallocate(sizeof(leaf), l); } } static trie* to_trie(node* n) { SASSERT(is_trie(n)); return static_cast(n); } static leaf* to_leaf(node* n) { SASSERT(is_leaf(n)); return static_cast(n); } static bool is_leaf(node* n) { return n->type() == leaf_t; } static bool is_trie(node* n) { return n->type() == trie_t; } }; #endif z3-z3-4.8.7/src/math/hilbert/hilbert_basis.cpp000066400000000000000000001076021356505360400211240ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: hilbert_basis.cpp Abstract: Basic Hilbert Basis computation. Author: Nikolaj Bjorner (nbjorner) 2013-02-09. Revision History: --*/ #include "math/hilbert/hilbert_basis.h" #include "util/heap.h" #include "util/map.h" #include "math/hilbert/heap_trie.h" #include "util/stopwatch.h" typedef int_hashtable > int_table; class hilbert_basis::value_index1 { struct stats { unsigned m_num_comparisons; unsigned m_num_hit; unsigned m_num_miss; stats() { reset(); } void reset() { memset(this, 0, sizeof(*this)); } }; hilbert_basis& hb; int_table m_table; stats m_stats; public: value_index1(hilbert_basis& hb): hb(hb) {} void insert(offset_t idx, values const& vs) { m_table.insert(idx.m_offset); } void remove(offset_t idx, values const& vs) { m_table.remove(idx.m_offset); } void reset() { m_table.reset(); } bool find(offset_t idx, values const& vs) { // display_profile(idx, std::cout); int_table::iterator it = m_table.begin(), end = m_table.end(); for (; it != end; ++it) { offset_t offs(*it); ++m_stats.m_num_comparisons; if (*it != static_cast(idx.m_offset) && hb.is_subsumed(idx, offs)) { ++m_stats.m_num_hit; return true; } } ++m_stats.m_num_miss; return false; } void collect_statistics(statistics& st) const { st.update("hb.index.num_comparisons", m_stats.m_num_comparisons); st.update("hb.index.num_hit", m_stats.m_num_hit); st.update("hb.index.num_miss", m_stats.m_num_miss); } void reset_statistics() { m_stats.reset(); } unsigned size() const { return m_table.size(); } void display(std::ostream& out) const { int_table::iterator it = m_table.begin(), end = m_table.end(); for (; it != end; ++it) { offset_t offs(*it); hb.display(out, offs); } display_profile(out); } private: typedef hashtable numeral_set; void display_profile(std::ostream& out) const { vector weights; weights.resize(hb.get_num_vars()+1); int_table::iterator it = m_table.begin(), end = m_table.end(); for (; it != end; ++it) { offset_t offs(*it); values const& ws = hb.vec(offs); weights[0].insert(ws.weight()); for (unsigned i = 0; i < hb.get_num_vars(); ++i) { weights[i+1].insert(ws[i]); } } out << "profile: "; for (unsigned i = 0; i < weights.size(); ++i) { out << weights[i].size() << " "; } out << "\n"; } void display_profile(offset_t idx, std::ostream& out) { unsigned_vector leq; unsigned nv = hb.get_num_vars(); values const& vs = hb.vec(idx); leq.resize(nv+1); numeral maxw(0); for (unsigned i = 0; i < nv; ++i) { if (!hb.is_abs_geq(maxw, vs[i])) { maxw = vs[i]; } } unsigned num_below_max = 0; int_table::iterator it = m_table.begin(), end = m_table.end(); for (; it != end; ++it) { offset_t offs(*it); values const& ws = hb.vec(offs); if (ws.weight() <= vs.weight()) { leq[0]++; } bool filtered = false; for (unsigned i = 0; i < nv; ++i) { if (hb.is_abs_geq(vs[i], ws[i])) { leq[i+1]++; } if (!hb.is_abs_geq(maxw, ws[i])) { filtered = true; } } if (!filtered) { ++num_below_max; } } out << vs.weight() << ":" << leq[0] << " "; for (unsigned i = 0; i < nv; ++i) { out << vs[i] << ":" << leq[i+1] << " "; } out << " max<= " << num_below_max; out << "\n"; } }; class hilbert_basis::value_index2 { struct key_le { hilbert_basis& hb; key_le(hilbert_basis& hb): hb(hb) {} bool le(numeral const& n1, numeral const& n2) const { return hb.is_abs_geq(n2, n1); } }; typedef heap_trie ht; struct checker : public ht::check_value { hilbert_basis* hb; offset_t m_value; checker(): hb(nullptr) {} bool operator()(unsigned const& v) override { if (m_value.m_offset != v) { // && hb->is_subsumed(m_value, offset_t(v))) { return true; } else { return false; } } }; hilbert_basis& hb; key_le m_le; ht m_trie; checker m_checker; unsigned m_offset; numeral const* get_keys(values const& vs) { return vs()-m_offset; } public: value_index2(hilbert_basis& hb): hb(hb), m_le(hb), m_trie(m_le), m_offset(1) { m_checker.hb = &hb; } void insert(offset_t idx, values const& vs) { m_trie.insert(get_keys(vs), idx.m_offset); } void remove(offset_t idx, values const& vs) { m_trie.remove(get_keys(vs)); } void reset(unsigned offset) { m_offset = offset; m_trie.reset(hb.get_num_vars()+m_offset); } bool find(offset_t idx, values const& vs) { m_checker.m_value = idx; return m_trie.find_le(get_keys(vs), m_checker); } void collect_statistics(statistics& st) const { m_trie.collect_statistics(st); } void reset_statistics() { m_trie.reset_statistics(); } unsigned size() const { return m_trie.size(); } void display(std::ostream& out) const { // m_trie.display(out); } }; class hilbert_basis::index { // for each non-positive weight a separate index. // for positive weights a shared value index. // typedef value_index1 value_index; typedef value_index2 value_index; // typedef value_index3 value_index; struct stats { unsigned m_num_find; unsigned m_num_insert; stats() { reset(); } void reset() { memset(this, 0, sizeof(*this)); } }; template class numeral_map : public map {}; typedef numeral_map value_map; hilbert_basis& hb; value_map m_neg; value_index m_pos; value_index m_zero; stats m_stats; unsigned m_num_ineqs; public: index(hilbert_basis& hb): hb(hb), m_pos(hb), m_zero(hb), m_num_ineqs(0) {} void insert(offset_t idx, values const& vs) { ++m_stats.m_num_insert; if (vs.weight().is_pos()) { m_pos.insert(idx, vs); } else if (vs.weight().is_zero()) { m_zero.insert(idx, vs); } else { value_index* map = nullptr; if (!m_neg.find(vs.weight(), map)) { map = alloc(value_index, hb); map->reset(m_num_ineqs); m_neg.insert(vs.weight(), map); } map->insert(idx, vs); } } void remove(offset_t idx, values const& vs) { if (vs.weight().is_pos()) { m_pos.remove(idx, vs); } else if (vs.weight().is_zero()) { m_zero.remove(idx, vs); } else { m_neg.find(vs.weight())->remove(idx, vs); } } bool find(offset_t idx, values const& vs) { ++m_stats.m_num_find; if (vs.weight().is_pos()) { return m_pos.find(idx, vs); } else if (vs.weight().is_zero()) { return m_zero.find(idx, vs); } else { value_index* map; return m_neg.find(vs.weight(), map) && map->find(idx, vs); } } void reset(unsigned num_ineqs) { value_map::iterator it = m_neg.begin(), end = m_neg.end(); for (; it != end; ++it) { dealloc(it->m_value); } m_pos.reset(num_ineqs); m_zero.reset(num_ineqs); m_num_ineqs = num_ineqs; m_neg.reset(); } void collect_statistics(statistics& st) const { m_pos.collect_statistics(st); m_zero.collect_statistics(st); value_map::iterator it = m_neg.begin(), end = m_neg.end(); for (; it != end; ++it) { it->m_value->collect_statistics(st); } st.update("hb.index.num_find", m_stats.m_num_find); st.update("hb.index.num_insert", m_stats.m_num_insert); st.update("hb.index.size", size()); } void reset_statistics() { m_pos.reset_statistics(); m_zero.reset_statistics(); value_map::iterator it = m_neg.begin(), end = m_neg.end(); for (; it != end; ++it) { it->m_value->reset_statistics(); } } void display(std::ostream& out) const { m_pos.display(out); m_zero.display(out); value_map::iterator it = m_neg.begin(), end = m_neg.end(); for (; it != end; ++it) { it->m_value->display(out); } } private: unsigned size() const { unsigned sz = m_pos.size(); sz += m_zero.size(); value_map::iterator it = m_neg.begin(), end = m_neg.end(); for (; it != end; ++it) { sz += it->m_value->size(); } return sz; } }; /** \brief priority queue for passive list. */ class hilbert_basis::passive { struct lt { passive** p; lt(passive** p): p(p) {} bool operator()(int v1, int v2) const { return (**p)(v1, v2); } }; hilbert_basis& hb; svector m_passive; unsigned_vector m_free_list; passive* m_this; lt m_lt; heap m_heap; // binary heap over weights numeral sum_abs(offset_t idx) const { numeral w(0); unsigned nv = hb.get_num_vars(); for (unsigned i = 0; i < nv; ++i) { w += abs(hb.vec(idx)[i]); } return w; } public: passive(hilbert_basis& hb): hb(hb), m_lt(&m_this), m_heap(10, m_lt) { m_this = this; } void reset() { m_heap.reset(); m_free_list.reset(); m_passive.reset(); } bool empty() const { return m_heap.empty(); } offset_t pop() { SASSERT(!empty()); unsigned val = static_cast(m_heap.erase_min()); offset_t result = m_passive[val]; m_free_list.push_back(val); m_passive[val] = mk_invalid_offset(); return result; } void insert(offset_t idx) { unsigned v; if (m_free_list.empty()) { v = m_passive.size(); m_passive.push_back(idx); m_heap.set_bounds(v+1); } else { v = m_free_list.back(); m_free_list.pop_back(); m_passive[v] = idx; } m_heap.insert(v); } class iterator { passive& p; unsigned m_idx; void fwd() { while (m_idx < p.m_passive.size() && is_invalid_offset(p.m_passive[m_idx])) { ++m_idx; } } public: iterator(passive& p, unsigned i): p(p), m_idx(i) { fwd(); } offset_t operator*() const { return p.m_passive[m_idx]; } iterator& operator++() { ++m_idx; fwd(); return *this; } iterator operator++(int) { iterator tmp = *this; ++*this; return tmp; } bool operator==(iterator const& it) const {return m_idx == it.m_idx; } bool operator!=(iterator const& it) const {return m_idx != it.m_idx; } }; iterator begin() { return iterator(*this, 0); } iterator end() { return iterator(*this, m_passive.size()); } bool operator()(int v1, int v2) const { offset_t idx1 = m_passive[v1]; offset_t idx2 = m_passive[v2]; return sum_abs(idx1) < sum_abs(idx2); } }; class hilbert_basis::vector_lt_t { hilbert_basis& hb; public: vector_lt_t(hilbert_basis& hb): hb(hb) {} bool operator()(offset_t idx1, offset_t idx2) const { return hb.vector_lt(idx1, idx2); } }; class hilbert_basis::passive2 { struct lt { passive2** p; lt(passive2** p): p(p) {} bool operator()(int v1, int v2) const { return (**p)(v1, v2); } }; hilbert_basis& hb; svector m_pos_sos; svector m_neg_sos; vector m_pos_sos_sum; vector m_neg_sos_sum; vector m_sum_abs; unsigned_vector m_psos; svector m_pas; vector m_weight; unsigned_vector m_free_list; passive2* m_this; lt m_lt; heap m_heap; // binary heap over weights numeral sum_abs(offset_t idx) const { numeral w(0); unsigned nv = hb.get_num_vars(); for (unsigned i = 0; i < nv; ++i) { w += abs(hb.vec(idx)[i]); } return w; } public: passive2(hilbert_basis& hb): hb(hb), m_lt(&m_this), m_heap(10, m_lt) { m_this = this; } void init(svector const& I) { for (unsigned i = 0; i < I.size(); ++i) { numeral const& w = hb.vec(I[i]).weight(); if (w.is_pos()) { m_pos_sos.push_back(I[i]); m_pos_sos_sum.push_back(sum_abs(I[i])); } else { m_neg_sos.push_back(I[i]); m_neg_sos_sum.push_back(sum_abs(I[i])); } } } void reset() { m_heap.reset(); m_free_list.reset(); m_psos.reset(); m_pas.reset(); m_sum_abs.reset(); m_pos_sos.reset(); m_neg_sos.reset(); m_pos_sos_sum.reset(); m_neg_sos_sum.reset(); m_weight.reset(); } void insert(offset_t idx, unsigned offset) { SASSERT(!m_pos_sos.empty()); unsigned v; if (m_free_list.empty()) { v = m_pas.size(); m_pas.push_back(idx); m_psos.push_back(offset); m_weight.push_back(numeral(0)); m_heap.set_bounds(v+1); m_sum_abs.push_back(sum_abs(idx)); } else { v = m_free_list.back(); m_free_list.pop_back(); m_pas[v] = idx; m_psos[v] = offset; m_weight[v] = numeral(0); m_sum_abs[v] = sum_abs(idx); } next_resolvable(hb.vec(idx).weight().is_pos(), v); } bool empty() const { return m_heap.empty(); } unsigned pop(offset_t& sos, offset_t& pas) { SASSERT (!empty()); unsigned val = static_cast(m_heap.erase_min()); pas = m_pas[val]; numeral old_weight = hb.vec(pas).weight(); bool is_positive = old_weight.is_pos(); unsigned psos = m_psos[val]; sos = is_positive?m_neg_sos[psos]:m_pos_sos[psos]; m_psos[val]++; next_resolvable(is_positive, val); numeral new_weight = hb.vec(sos).weight() + old_weight; if (new_weight.is_pos() != old_weight.is_pos()) { psos = 0; } return psos; } bool operator()(int v1, int v2) const { return m_weight[v1] < m_weight[v2]; } class iterator { passive2& p; unsigned m_idx; void fwd() { while (m_idx < p.m_pas.size() && is_invalid_offset(p.m_pas[m_idx])) { ++m_idx; } } public: iterator(passive2& p, unsigned i): p(p), m_idx(i) { fwd(); } offset_t pas() const { return p.m_pas[m_idx]; } offset_t sos() const { return (p.hb.vec(pas()).weight().is_pos()?p.m_neg_sos:p.m_pos_sos)[p.m_psos[m_idx]]; } iterator& operator++() { ++m_idx; fwd(); return *this; } iterator operator++(int) { iterator tmp = *this; ++*this; return tmp; } bool operator==(iterator const& it) const {return m_idx == it.m_idx; } bool operator!=(iterator const& it) const {return m_idx != it.m_idx; } }; iterator begin() { return iterator(*this, 0); } iterator end() { return iterator(*this, m_pas.size()); } private: void next_resolvable(bool is_positive, unsigned v) { offset_t pas = m_pas[v]; svector const& soss = is_positive?m_neg_sos:m_pos_sos; while (m_psos[v] < soss.size()) { unsigned psos = m_psos[v]; offset_t sos = soss[psos]; if (hb.can_resolve(sos, pas, false)) { m_weight[v] = m_sum_abs[v] + (is_positive?m_neg_sos_sum[psos]:m_pos_sos_sum[psos]); m_heap.insert(v); return; } ++m_psos[v]; } // add pas to free-list for hb if it is not in sos. m_free_list.push_back(v); m_psos[v] = UINT_MAX; m_pas[v] = mk_invalid_offset(); } }; hilbert_basis::hilbert_basis(reslimit& lim): m_limit(lim), m_use_support(true), m_use_ordered_support(true), m_use_ordered_subsumption(true) { m_index = alloc(index, *this); m_passive = alloc(passive, *this); m_passive2 = alloc(passive2, *this); } hilbert_basis::~hilbert_basis() { dealloc(m_index); dealloc(m_passive); dealloc(m_passive2); } hilbert_basis::offset_t hilbert_basis::mk_invalid_offset() { return offset_t(UINT_MAX); } bool hilbert_basis::is_invalid_offset(offset_t offs) { return offs.m_offset == UINT_MAX; } void hilbert_basis::reset() { m_ineqs.reset(); m_iseq.reset(); m_store.reset(); m_basis.reset(); m_free_list.reset(); m_sos.reset(); m_zero.reset(); m_active.reset(); if (m_passive) { m_passive->reset(); } if (m_passive2) { m_passive2->reset(); } if (m_index) { m_index->reset(1); } m_ints.reset(); m_current_ineq = 0; } void hilbert_basis::collect_statistics(statistics& st) const { st.update("hb.num_subsumptions", m_stats.m_num_subsumptions); st.update("hb.num_resolves", m_stats.m_num_resolves); st.update("hb.num_saturations", m_stats.m_num_saturations); st.update("hb.basis_size", get_basis_size()); m_index->collect_statistics(st); } void hilbert_basis::reset_statistics() { m_stats.reset(); m_index->reset_statistics(); } void hilbert_basis::add_ge(rational_vector const& v, rational const& b) { SASSERT(m_ineqs.empty() || v.size() + 1 == m_ineqs.back().size()); num_vector w; w.push_back(to_numeral(-b)); for (unsigned i = 0; i < v.size(); ++i) { w.push_back(to_numeral(v[i])); } m_ineqs.push_back(w); m_iseq.push_back(false); } void hilbert_basis::add_le(rational_vector const& v, rational const& b) { rational_vector w(v); for (unsigned i = 0; i < w.size(); ++i) { w[i].neg(); } add_ge(w, -b); } void hilbert_basis::add_eq(rational_vector const& v, rational const& b) { SASSERT(m_ineqs.empty() || v.size() + 1 == m_ineqs.back().size()); num_vector w; w.push_back(to_numeral(-b)); for (unsigned i = 0; i < v.size(); ++i) { w.push_back(to_numeral(v[i])); } m_ineqs.push_back(w); m_iseq.push_back(true); } void hilbert_basis::add_ge(rational_vector const& v) { add_ge(v, rational(0)); } void hilbert_basis::add_le(rational_vector const& v) { add_le(v, rational(0)); } void hilbert_basis::add_eq(rational_vector const& v) { add_eq(v, rational(0)); } void hilbert_basis::set_is_int(unsigned var_index) { // // The 0'th index is reserved for the constant // coefficient. Shift indices by 1. // m_ints.push_back(var_index+1); } bool hilbert_basis::get_is_int(unsigned var_index) const { return m_ints.contains(var_index+1); } unsigned hilbert_basis::get_num_vars() const { if (m_ineqs.empty()) { return 0; } else { SASSERT(m_ineqs.back().size() > 1); return m_ineqs.back().size(); } } hilbert_basis::values hilbert_basis::vec(offset_t offs) const { return values(m_ineqs.size(), m_store.c_ptr() + offs.m_offset); } void hilbert_basis::init_basis() { m_basis.reset(); m_store.reset(); m_free_list.reset(); unsigned nv = get_num_vars(); for (unsigned i = 0; i < nv; ++i) { add_unit_vector(i, numeral(1)); } for (unsigned i = 0; i < m_ints.size(); ++i) { add_unit_vector(m_ints[i], numeral(-1)); } } void hilbert_basis::add_unit_vector(unsigned i, numeral const& e) { unsigned num_vars = get_num_vars(); num_vector w(num_vars, numeral(0)); w[i] = e; offset_t idx = alloc_vector(); values v = vec(idx); for (unsigned j = 0; j < num_vars; ++j) { v[j] = w[j]; } m_basis.push_back(idx); } lbool hilbert_basis::saturate() { init_basis(); m_current_ineq = 0; while (checkpoint() && m_current_ineq < m_ineqs.size()) { select_inequality(); stopwatch sw; sw.start(); lbool r = saturate(m_ineqs[m_current_ineq], m_iseq[m_current_ineq]); IF_VERBOSE(3, { statistics st; collect_statistics(st); st.display(verbose_stream()); sw.stop(); verbose_stream() << "time: " << sw.get_seconds() << "\n"; }); ++m_stats.m_num_saturations; if (r != l_true) { return r; } ++m_current_ineq; } if (!checkpoint()) { return l_undef; } return l_true; } lbool hilbert_basis::saturate_orig(num_vector const& ineq, bool is_eq) { m_active.reset(); m_passive->reset(); m_zero.reset(); m_index->reset(m_current_ineq+1); int_table support; TRACE("hilbert_basis", display_ineq(tout, ineq, is_eq);); iterator it = begin(); for (; it != end(); ++it) { offset_t idx = *it; values v = vec(idx); v.weight() = get_weight(v, ineq); for (unsigned k = 0; k < m_current_ineq; ++k) { v.weight(k) = get_weight(v, m_ineqs[k]); } add_goal(idx); if (m_use_support) { support.insert(idx.m_offset); } } TRACE("hilbert_basis", display(tout);); // resolve passive into active offset_t j = alloc_vector(); while (!m_passive->empty()) { if (!checkpoint()) { return l_undef; } offset_t idx = m_passive->pop(); TRACE("hilbert_basis", display(tout);); if (is_subsumed(idx)) { recycle(idx); continue; } for (unsigned i = 0; checkpoint() && i < m_active.size(); ++i) { if ((!m_use_support || support.contains(m_active[i].m_offset)) && can_resolve(idx, m_active[i], true)) { resolve(idx, m_active[i], j); if (add_goal(j)) { j = alloc_vector(); } } } m_active.push_back(idx); } m_free_list.push_back(j); // Move positive from active and zeros to new basis. m_basis.reset(); m_basis.append(m_zero); for (unsigned i = 0; !is_eq && i < m_active.size(); ++i) { offset_t idx = m_active[i]; if (vec(idx).weight().is_pos()) { m_basis.push_back(idx); } else { m_free_list.push_back(idx); } } m_active.reset(); m_passive->reset(); m_zero.reset(); TRACE("hilbert_basis", display(tout);); return m_basis.empty()?l_false:l_true; } bool hilbert_basis::vector_lt(offset_t idx1, offset_t idx2) const { values v = vec(idx1); values w = vec(idx2); numeral a(0), b(0); for (unsigned i = 0; i < get_num_vars(); ++i) { a += abs(v[i]); b += abs(w[i]); } return a < b; } lbool hilbert_basis::saturate(num_vector const& ineq, bool is_eq) { m_zero.reset(); m_index->reset(m_current_ineq+1); m_passive2->reset(); m_sos.reset(); TRACE("hilbert_basis", display_ineq(tout, ineq, is_eq);); unsigned init_basis_size = 0; for (unsigned i = 0; i < m_basis.size(); ++i) { offset_t idx = m_basis[i]; values v = vec(idx); v.weight() = get_weight(v, ineq); for (unsigned k = 0; k < m_current_ineq; ++k) { v.weight(k) = get_weight(v, m_ineqs[k]); } m_index->insert(idx, v); if (v.weight().is_zero()) { m_zero.push_back(idx); } else { if (v.weight().is_pos()) { m_basis[init_basis_size++] = idx; } m_sos.push_back(idx); } } m_basis.resize(init_basis_size); m_passive2->init(m_sos); // ASSERT basis is sorted by weight. // initialize passive for (unsigned i = 0; (init_basis_size > 0) && i < m_sos.size(); ++i) { if (vec(m_sos[i]).weight().is_neg()) { m_passive2->insert(m_sos[i], 0); } } TRACE("hilbert_basis", display(tout);); // resolve passive into active offset_t idx = alloc_vector(); while (checkpoint() && !m_passive2->empty()) { offset_t sos, pas; TRACE("hilbert_basis", display(tout); ); unsigned offset = m_passive2->pop(sos, pas); SASSERT(can_resolve(sos, pas, true)); resolve(sos, pas, idx); if (is_subsumed(idx)) { continue; } values v = vec(idx); m_index->insert(idx, v); if (v.weight().is_zero()) { m_zero.push_back(idx); } else { if (!m_use_ordered_support) { offset = 0; } m_passive2->insert(idx, offset); if (v.weight().is_pos()) { m_basis.push_back(idx); } } idx = alloc_vector(); } if (!checkpoint()) { return l_undef; } m_free_list.push_back(idx); // remove positive values from basis if we are looking for an equality. while (is_eq && !m_basis.empty()) { m_free_list.push_back(m_basis.back()); m_basis.pop_back(); } m_basis.append(m_zero); std::sort(m_basis.begin(), m_basis.end(), vector_lt_t(*this)); m_zero.reset(); TRACE("hilbert_basis", display(tout);); return m_basis.empty()?l_false:l_true; } void hilbert_basis::get_basis_solution(unsigned i, rational_vector& v, bool& is_initial) { offset_t offs = m_basis[i]; v.reset(); for (unsigned i = 1; i < get_num_vars(); ++i) { v.push_back(to_rational(vec(offs)[i])); } is_initial = !vec(offs)[0].is_zero(); } void hilbert_basis::get_ge(unsigned i, rational_vector& v, rational& b, bool& is_eq) { v.reset(); for (unsigned j = 1; j < m_ineqs[i].size(); ++j) { v.push_back(to_rational(m_ineqs[i][j])); } b = to_rational(-m_ineqs[i][0]); is_eq = m_iseq[i]; } void hilbert_basis::select_inequality() { SASSERT(m_current_ineq < m_ineqs.size()); unsigned best = m_current_ineq; unsigned non_zeros = get_num_nonzeros(m_ineqs[best]); unsigned prod = get_ineq_product(m_ineqs[best]); for (unsigned j = best+1; prod != 0 && j < m_ineqs.size(); ++j) { unsigned non_zeros2 = get_num_nonzeros(m_ineqs[j]); unsigned prod2 = get_ineq_product(m_ineqs[j]); if (prod2 == 0) { prod = prod2; non_zeros = non_zeros2; best = j; break; } if (non_zeros2 < non_zeros || (non_zeros2 == non_zeros && prod2 < prod)) { prod = prod2; non_zeros = non_zeros2; best = j; } } if (best != m_current_ineq) { std::swap(m_ineqs[m_current_ineq], m_ineqs[best]); std::swap(m_iseq[m_current_ineq], m_iseq[best]); } } unsigned hilbert_basis::get_num_nonzeros(num_vector const& ineq) { unsigned count = 0; for (unsigned i = 0; i < ineq.size(); ++i) { if (!ineq[i].is_zero()) { ++count; } } return count; } unsigned hilbert_basis::get_ineq_product(num_vector const& ineq) { unsigned num_pos = 0, num_neg = 0; iterator it = begin(); for (; it != end(); ++it) { values v = vec(*it); numeral w = get_weight(v, ineq); if (w.is_pos()) { ++num_pos; } else if (w.is_neg()) { ++num_neg; } } return num_pos * num_neg; } hilbert_basis::numeral hilbert_basis::get_ineq_diff(num_vector const& ineq) { numeral max_pos(0), min_neg(0); iterator it = begin(); for (; it != end(); ++it) { values v = vec(*it); numeral w = get_weight(v, ineq); if (w > max_pos) { max_pos = w; } else if (w < min_neg) { min_neg = w; } } return max_pos - min_neg; } void hilbert_basis::recycle(offset_t idx) { m_index->remove(idx, vec(idx)); m_free_list.push_back(idx); } void hilbert_basis::resolve(offset_t i, offset_t j, offset_t r) { ++m_stats.m_num_resolves; values v = vec(i); values w = vec(j); values u = vec(r); unsigned nv = get_num_vars(); for (unsigned k = 0; k < nv; ++k) { u[k] = v[k] + w[k]; } u.weight() = v.weight() + w.weight(); for (unsigned k = 0; k < m_current_ineq; ++k) { u.weight(k) = v.weight(k) + w.weight(k); } TRACE("hilbert_basis_verbose", display(tout, i); display(tout, j); display(tout, r); ); } hilbert_basis::offset_t hilbert_basis::alloc_vector() { if (m_free_list.empty()) { unsigned sz = m_ineqs.size() + get_num_vars(); unsigned idx = m_store.size(); m_store.resize(idx + sz); // std::cout << "alloc vector: " << idx << " " << sz << " " << m_store.c_ptr() + idx << " " << m_ineqs.size() << "\n"; return offset_t(idx); } else { offset_t result = m_free_list.back(); m_free_list.pop_back(); return result; } } bool hilbert_basis::checkpoint() { return m_limit.inc(); } bool hilbert_basis::add_goal(offset_t idx) { TRACE("hilbert_basis", display(tout, idx);); values v = vec(idx); if (is_subsumed(idx)) { return false; } m_index->insert(idx, v); if (v.weight().is_zero()) { m_zero.push_back(idx); } else { m_passive->insert(idx); } return true; } bool hilbert_basis::is_subsumed(offset_t idx) { if (m_index->find(idx, vec(idx))) { ++m_stats.m_num_subsumptions; return true; } return false; } bool hilbert_basis::can_resolve(offset_t i, offset_t j, bool check_sign) const { if (check_sign && get_sign(i) == get_sign(j)) { return false; } SASSERT(get_sign(i) != get_sign(j)); values const& v1 = vec(i); values const& v2 = vec(j); if (v1[0].is_one() && v2[0].is_one()) { return false; } for (unsigned i = 0; i < m_ints.size(); ++i) { unsigned j = m_ints[i]; if (v1[j].is_pos() && v2[j].is_neg()) { return false; } if (v1[j].is_neg() && v2[j].is_pos()) { return false; } } return true; } hilbert_basis::sign_t hilbert_basis::get_sign(offset_t idx) const { numeral const& val = vec(idx).weight(); if (val.is_pos()) { return pos; } if (val.is_neg()) { return neg; } return zero; } hilbert_basis::numeral hilbert_basis::get_weight(values const & val, num_vector const& ineq) const { numeral result(0); unsigned num_vars = get_num_vars(); for (unsigned i = 0; i < num_vars; ++i) { result += val[i]*ineq[i]; } return result; } void hilbert_basis::display(std::ostream& out) const { out << "inequalities:\n"; for (unsigned i = 0; i < m_ineqs.size(); ++i) { display_ineq(out, m_ineqs[i], m_iseq[i]); } if (!m_basis.empty()) { out << "basis:\n"; for (iterator it = begin(); it != end(); ++it) { display(out, *it); } } if (!m_active.empty()) { out << "active:\n"; for (unsigned i = 0; i < m_active.size(); ++i) { display(out, m_active[i]); } } if (!m_passive->empty()) { passive::iterator it = m_passive->begin(); passive::iterator end = m_passive->end(); out << "passive:\n"; for (; it != end; ++it) { display(out, *it); } } if (!m_passive2->empty()) { passive2::iterator it = m_passive2->begin(); passive2::iterator end = m_passive2->end(); out << "passive:\n"; for (; it != end; ++it) { display(out << "sos:", it.sos()); display(out << "pas:", it.pas()); } } if (!m_zero.empty()) { out << "zero:\n"; for (unsigned i = 0; i < m_zero.size(); ++i) { display(out, m_zero[i]); } } if (m_index) { m_index->display(out); } } void hilbert_basis::display(std::ostream& out, offset_t o) const { display(out, vec(o)); out << " -> " << vec(o).weight() << "\n"; } void hilbert_basis::display(std::ostream& out, values const& v) const { unsigned nv = get_num_vars(); for (unsigned j = 0; j < nv; ++j) { out << v[j] << " "; } } void hilbert_basis::display_ineq(std::ostream& out, num_vector const& v, bool is_eq) const { unsigned nv = v.size(); for (unsigned j = 1; j < nv; ++j) { if (!v[j].is_zero()) { if (j > 0) { if (v[j].is_pos()) { out << " + "; } else { out << " - "; } } else if (j == 0 && v[0].is_neg()) { out << "-"; } if (!v[j].is_one() && !v[j].is_minus_one()) { out << abs(v[j]) << "*"; } out << "x" << j; } } if (is_eq) { out << " = " << -v[0] << "\n"; } else { out << " >= " << -v[0] << "\n"; } } /** Vector v is subsumed by vector w if v[i] >= w[i] for each index i. a*v >= a*w for the evaluation of vectors with respect to a. . a*v < 0 => a*v = a*w . a*v > 0 => a*w > 0 . a*v = 0 => a*w = 0 Justification: let u := v - w, then u[i] >= 0 for each index i a*u = a*(v-w) >= 0 So v = u + w, where a*u >= 0, a*w >= 0. If a*v >= a*w >= 0 then v and w are linear solutions of e_i, and also v-w is a solution. If a*v = a*w < 0, then a*(v-w) = 0, so v can be obtained from w + (v - w). */ bool hilbert_basis::is_subsumed(offset_t i, offset_t j) const { values v = vec(i); values w = vec(j); numeral const& n = v.weight(); numeral const& m = w.weight(); bool r = i.m_offset != j.m_offset && n >= m && (!m.is_neg() || n == m) && is_geq(v, w); for (unsigned k = 0; r && k < m_current_ineq; ++k) { r = v.weight(k) >= w.weight(k); } CTRACE("hilbert_basis", r, display(tout, i); tout << " <= \n"; display(tout, j); tout << "\n";); return r; } bool hilbert_basis::is_geq(values const& v, values const& w) const { unsigned nv = get_num_vars(); for (unsigned i = 0; i < nv; ++i) { if (!is_abs_geq(v[i], w[i])) { return false; } } return true; } bool hilbert_basis::is_abs_geq(numeral const& v, numeral const& w) const { if (w.is_neg()) { return v <= w; } else { return v >= w; } } z3-z3-4.8.7/src/math/hilbert/hilbert_basis.h000066400000000000000000000157111356505360400205700ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: hilbert_basis.h Abstract: Basic Hilbert Basis computation. hilbert_basis computes a Hilbert basis for linear homogeneous inequalities over naturals. Author: Nikolaj Bjorner (nbjorner) 2013-02-09. Revision History: Hilbert basis can be templatized based on traits that define numeral: as rational, mpz, checked_int64 (checked or unchecked). --*/ #ifndef HILBERT_BASIS_H_ #define HILBERT_BASIS_H_ #include "util/rational.h" #include "util/lbool.h" #include "util/statistics.h" #include "util/checked_int64.h" #include "util/rlimit.h" typedef vector rational_vector; class hilbert_basis { static const bool check = true; typedef checked_int64 numeral; typedef vector num_vector; static checked_int64 to_numeral(rational const& r) { if (!r.is_int64()) { throw checked_int64::overflow_exception(); } return checked_int64(r.get_int64()); } static rational to_rational(checked_int64 const& i) { return rational(i.get_int64(), rational::i64()); } class value_index1; class value_index2; class value_index3; class index; class passive; class passive2; struct offset_t { unsigned m_offset; offset_t(unsigned o) : m_offset(o) {} offset_t(): m_offset(0) {} bool operator<(offset_t const& other) const { return m_offset < other.m_offset; } }; enum sign_t { pos, neg, zero }; struct stats { unsigned m_num_subsumptions; unsigned m_num_resolves; unsigned m_num_saturations; stats() { reset(); } void reset() { memset(this, 0, sizeof(*this)); } }; class values { numeral* m_values; public: values(unsigned offset, numeral* v): m_values(v+offset) { } numeral& weight() { return m_values[-1]; } // value of a*x numeral const& weight() const { return m_values[-1]; } // value of a*x numeral& weight(int i) { return m_values[-2-i]; } // value of b_i*x for 0 <= i < current inequality. numeral const& weight(int i) const { return m_values[-2-i]; } // value of b_i*x numeral& operator[](unsigned i) { return m_values[i]; } // value of x_i numeral const& operator[](unsigned i) const { return m_values[i]; } // value of x_i numeral const* operator()() const { return m_values; } }; reslimit& m_limit; vector m_ineqs; // set of asserted inequalities svector m_iseq; // inequalities that are equalities num_vector m_store; // store of vectors svector m_basis; // vector of current basis svector m_free_list; // free list of unused storage svector m_active; // active set svector m_sos; // set of support svector m_zero; // zeros passive* m_passive; // passive set passive2* m_passive2; // passive set stats m_stats; index* m_index; // index of generated vectors unsigned_vector m_ints; // indices that can be both positive and negative unsigned m_current_ineq; bool m_use_support; // parameter: (associativity) resolve only against vectors that are initially in basis. bool m_use_ordered_support; // parameter: (commutativity) resolve in order bool m_use_ordered_subsumption; // parameter class iterator { hilbert_basis const& hb; unsigned m_idx; public: iterator(hilbert_basis const& hb, unsigned idx): hb(hb), m_idx(idx) {} offset_t operator*() const { return hb.m_basis[m_idx]; } iterator& operator++() { ++m_idx; return *this; } iterator operator++(int) { iterator tmp = *this; ++*this; return tmp; } bool operator==(iterator const& it) const {return m_idx == it.m_idx; } bool operator!=(iterator const& it) const {return m_idx != it.m_idx; } }; static offset_t mk_invalid_offset(); static bool is_invalid_offset(offset_t offs); lbool saturate(num_vector const& ineq, bool is_eq); lbool saturate_orig(num_vector const& ineq, bool is_eq); void init_basis(); void select_inequality(); unsigned get_num_nonzeros(num_vector const& ineq); unsigned get_ineq_product(num_vector const& ineq); numeral get_ineq_diff(num_vector const& ineq); void add_unit_vector(unsigned i, numeral const& e); unsigned get_num_vars() const; numeral get_weight(values const & val, num_vector const& ineq) const; bool is_geq(values const& v, values const& w) const; bool is_abs_geq(numeral const& v, numeral const& w) const; bool is_subsumed(offset_t idx); bool is_subsumed(offset_t i, offset_t j) const; void recycle(offset_t idx); bool can_resolve(offset_t i, offset_t j, bool check_sign) const; sign_t get_sign(offset_t idx) const; bool add_goal(offset_t idx); bool checkpoint(); offset_t alloc_vector(); void resolve(offset_t i, offset_t j, offset_t r); iterator begin() const { return iterator(*this,0); } iterator end() const { return iterator(*this, m_basis.size()); } class vector_lt_t; bool vector_lt(offset_t i, offset_t j) const; values vec(offset_t offs) const; void display(std::ostream& out, offset_t o) const; void display(std::ostream& out, values const & v) const; void display_ineq(std::ostream& out, num_vector const& v, bool is_eq) const; public: hilbert_basis(reslimit& rl); ~hilbert_basis(); void reset(); void set_use_support(bool b) { m_use_support = b; } void set_use_ordered_support(bool b) { m_use_ordered_support = b; } void set_use_ordered_subsumption(bool b) { m_use_ordered_subsumption = b; } // add inequality v*x >= 0 // add inequality v*x <= 0 // add equality v*x = 0 void add_ge(rational_vector const& v); void add_le(rational_vector const& v); void add_eq(rational_vector const& v); // add inequality v*x >= b // add inequality v*x <= b // add equality v*x = b void add_ge(rational_vector const& v, rational const& b); void add_le(rational_vector const& v, rational const& b); void add_eq(rational_vector const& v, rational const& b); void set_is_int(unsigned var_index); bool get_is_int(unsigned var_index) const; lbool saturate(); unsigned get_basis_size() const { return m_basis.size(); } void get_basis_solution(unsigned i, rational_vector& v, bool& is_initial); unsigned get_num_ineqs() const { return m_ineqs.size(); } void get_ge(unsigned i, rational_vector& v, rational& b, bool& is_eq); void display(std::ostream& out) const; void collect_statistics(statistics& st) const; void reset_statistics(); }; #endif z3-z3-4.8.7/src/math/interval/000077500000000000000000000000001356505360400157735ustar00rootroot00000000000000z3-z3-4.8.7/src/math/interval/CMakeLists.txt000066400000000000000000000001351356505360400205320ustar00rootroot00000000000000z3_add_component(interval SOURCES interval_mpq.cpp COMPONENT_DEPENDENCIES util ) z3-z3-4.8.7/src/math/interval/README000066400000000000000000000005011356505360400166470ustar00rootroot00000000000000Template for interval arithmetic. The template can be instantiated using different numeral (integers/mpz, rationals/mpq, floating-point/mpf, etc) packages. The class im_default_config defines a default configuration for the template that uses rationals. It also shows what is the expected signature used by the template. z3-z3-4.8.7/src/math/interval/interval.h000066400000000000000000000342611356505360400177760ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: interval.h Abstract: Goodies/Templates for interval arithmetic Author: Leonardo de Moura (leonardo) 2012-07-19. Revision History: --*/ #ifndef INTERVAL_H_ #define INTERVAL_H_ #include "util/mpq.h" #include "util/ext_numeral.h" #include "util/rlimit.h" /** \brief Default configuration for interval manager. It is used for documenting the required interface. */ class im_default_config { unsynch_mpq_manager & m_manager; public: typedef unsynch_mpq_manager numeral_manager; typedef mpq numeral; // Every configuration object must provide an interval type. // The actual fields are irrelevant, the interval manager // accesses interval data using the following API. struct interval { numeral m_lower; numeral m_upper; unsigned m_lower_open:1; unsigned m_upper_open:1; unsigned m_lower_inf:1; unsigned m_upper_inf:1; }; // Should be NOOPs for precise numeral types. // For imprecise types (e.g., floats) it should set the rounding mode. void round_to_minus_inf() {} void round_to_plus_inf() {} void set_rounding(bool to_plus_inf) {} // Getters numeral const & lower(interval const & a) const { return a.m_lower; } numeral const & upper(interval const & a) const { return a.m_upper; } numeral & lower(interval & a) { return a.m_lower; } numeral & upper(interval & a) { return a.m_upper; } bool lower_is_open(interval const & a) const { return a.m_lower_open; } bool upper_is_open(interval const & a) const { return a.m_upper_open; } bool lower_is_inf(interval const & a) const { return a.m_lower_inf; } bool upper_is_inf(interval const & a) const { return a.m_upper_inf; } // Setters void set_lower(interval & a, numeral const & n) { m_manager.set(a.m_lower, n); } void set_upper(interval & a, numeral const & n) { m_manager.set(a.m_upper, n); } void set_lower_is_open(interval & a, bool v) { a.m_lower_open = v; } void set_upper_is_open(interval & a, bool v) { a.m_upper_open = v; } void set_lower_is_inf(interval & a, bool v) { a.m_lower_inf = v; } void set_upper_is_inf(interval & a, bool v) { a.m_upper_inf = v; } // Reference to numeral manager numeral_manager & m() const { return m_manager; } im_default_config(numeral_manager & m):m_manager(m) {} }; #define DEP_IN_LOWER1 1 #define DEP_IN_UPPER1 2 #define DEP_IN_LOWER2 4 #define DEP_IN_UPPER2 8 typedef short bound_deps; inline bool dep_in_lower1(bound_deps d) { return (d & DEP_IN_LOWER1) != 0; } inline bool dep_in_lower2(bound_deps d) { return (d & DEP_IN_LOWER2) != 0; } inline bool dep_in_upper1(bound_deps d) { return (d & DEP_IN_UPPER1) != 0; } inline bool dep_in_upper2(bound_deps d) { return (d & DEP_IN_UPPER2) != 0; } inline bound_deps dep1_to_dep2(bound_deps d) { SASSERT(!dep_in_lower2(d) && !dep_in_upper2(d)); bound_deps r = d << 2; SASSERT(dep_in_lower1(d) == dep_in_lower2(r)); SASSERT(dep_in_upper1(d) == dep_in_upper2(r)); SASSERT(!dep_in_lower1(r) && !dep_in_upper1(r)); return r; } /** \brief Interval dependencies for unary and binary operations on intervals. It contains the dependencies for the output lower and upper bounds for the resultant interval. */ struct interval_deps { bound_deps m_lower_deps; bound_deps m_upper_deps; }; template class interval_manager { public: typedef typename C::numeral_manager numeral_manager; typedef typename numeral_manager::numeral numeral; typedef typename C::interval interval; private: reslimit& m_limit; C m_c; numeral m_result_lower; numeral m_result_upper; numeral m_mul_ad; numeral m_mul_bc; numeral m_mul_ac; numeral m_mul_bd; numeral m_one; numeral m_minus_one; numeral m_inv_k; unsigned m_pi_n; interval m_pi_div_2; interval m_pi; interval m_3_pi_div_2; interval m_2_pi; void round_to_minus_inf() { m_c.round_to_minus_inf(); } void round_to_plus_inf() { m_c.round_to_plus_inf(); } void set_rounding(bool to_plus_inf) { m_c.set_rounding(to_plus_inf); } ext_numeral_kind lower_kind(interval const & a) const { return m_c.lower_is_inf(a) ? EN_MINUS_INFINITY : EN_NUMERAL; } ext_numeral_kind upper_kind(interval const & a) const { return m_c.upper_is_inf(a) ? EN_PLUS_INFINITY : EN_NUMERAL; } void set_lower(interval & a, numeral const & n) { m_c.set_lower(a, n); } void set_upper(interval & a, numeral const & n) { m_c.set_upper(a, n); } void set_lower_is_open(interval & a, bool v) { m_c.set_lower_is_open(a, v); } void set_upper_is_open(interval & a, bool v) { m_c.set_upper_is_open(a, v); } void set_lower_is_inf(interval & a, bool v) { m_c.set_lower_is_inf(a, v); } void set_upper_is_inf(interval & a, bool v) { m_c.set_upper_is_inf(a, v); } void nth_root_slow(numeral const & a, unsigned n, numeral const & p, numeral & lo, numeral & hi); void A_div_x_n(numeral const & A, numeral const & x, unsigned n, bool to_plus_inf, numeral & r); void rough_approx_nth_root(numeral const & a, unsigned n, numeral & o); void approx_nth_root(numeral const & a, unsigned n, numeral const & p, numeral & o); void nth_root_pos(numeral const & A, unsigned n, numeral const & p, numeral & lo, numeral & hi); void nth_root(numeral const & a, unsigned n, numeral const & p, numeral & lo, numeral & hi); void pi_series(int x, numeral & r, bool to_plus_inf); void fact(unsigned n, numeral & o); void sine_series(numeral const & a, unsigned k, bool upper, numeral & o); void cosine_series(numeral const & a, unsigned k, bool upper, numeral & o); void e_series(unsigned k, bool upper, numeral & o); void div_mul(numeral const & k, interval const & a, interval & b, bool inv_k); void checkpoint(); public: interval_manager(reslimit& lim, C && c); ~interval_manager(); numeral_manager & m() const { return m_c.m(); } void del(interval & a); numeral const & lower(interval const & a) const { return m_c.lower(a); } numeral const & upper(interval const & a) const { return m_c.upper(a); } numeral & lower(interval & a) { return m_c.lower(a); } numeral & upper(interval & a) { return m_c.upper(a); } bool lower_is_open(interval const & a) const { return m_c.lower_is_open(a); } bool upper_is_open(interval const & a) const { return m_c.upper_is_open(a); } bool lower_is_inf(interval const & a) const { return m_c.lower_is_inf(a); } bool upper_is_inf(interval const & a) const { return m_c.upper_is_inf(a); } bool lower_is_neg(interval const & a) const { return ::is_neg(m(), lower(a), lower_kind(a)); } bool lower_is_pos(interval const & a) const { return ::is_pos(m(), lower(a), lower_kind(a)); } bool lower_is_zero(interval const & a) const { return ::is_zero(m(), lower(a), lower_kind(a)); } bool upper_is_neg(interval const & a) const { return ::is_neg(m(), upper(a), upper_kind(a)); } bool upper_is_pos(interval const & a) const { return ::is_pos(m(), upper(a), upper_kind(a)); } bool upper_is_zero(interval const & a) const { return ::is_zero(m(), upper(a), upper_kind(a)); } bool is_P(interval const & n) const { return lower_is_pos(n) || lower_is_zero(n); } bool is_P0(interval const & n) const { return lower_is_zero(n) && !lower_is_open(n); } bool is_P1(interval const & n) const { return lower_is_pos(n) || (lower_is_zero(n) && lower_is_open(n)); } bool is_N(interval const & n) const { return upper_is_neg(n) || upper_is_zero(n); } bool is_N0(interval const & n) const { return upper_is_zero(n) && !upper_is_open(n); } bool is_N1(interval const & n) const { return upper_is_neg(n) || (upper_is_zero(n) && upper_is_open(n)); } bool is_M(interval const & n) const { return lower_is_neg(n) && upper_is_pos(n); } bool is_zero(interval const & n) const { return lower_is_zero(n) && upper_is_zero(n); } void set(interval & t, interval const & s); bool eq(interval const & a, interval const & b) const; /** \brief Return true if all values in 'a' are less than all values in 'b'. */ bool before(interval const & a, interval const & b) const; /** \brief Set lower bound to -oo. */ void reset_lower(interval & a); /** \brief Set upper bound to +oo. */ void reset_upper(interval & a); /** \brief Set interval to (-oo, oo) */ void reset(interval & a); /** \brief Return true if the given interval contains 0. */ bool contains_zero(interval const & n) const; /** \brief Return true if n contains v. */ bool contains(interval const & n, numeral const & v) const; void display(std::ostream & out, interval const & n) const; void display_pp(std::ostream & out, interval const & n) const; bool check_invariant(interval const & n) const; /** \brief b <- k */ void set(numeral const& k, interval & b); /** \brief b <- -a */ void neg(interval const & a, interval & b, interval_deps & b_deps); void neg(interval const & a, interval & b); void neg_jst(interval const & a, interval_deps & b_deps); /** \brief c <- a + b */ void add(interval const & a, interval const & b, interval & c, interval_deps & c_deps); void add(interval const & a, interval const & b, interval & c); void add_jst(interval const & a, interval const & b, interval_deps & c_deps); /** \brief c <- a - b */ void sub(interval const & a, interval const & b, interval & c, interval_deps & c_deps); void sub(interval const & a, interval const & b, interval & c); void sub_jst(interval const & a, interval const & b, interval_deps & c_deps); /** \brief b <- k * a */ void mul(numeral const & k, interval const & a, interval & b, interval_deps & b_deps); void mul(numeral const & k, interval const & a, interval & b) { div_mul(k, a, b, false); } void mul_jst(numeral const & k, interval const & a, interval_deps & b_deps); /** \brief b <- (n/d) * a */ void mul(int n, int d, interval const & a, interval & b); /** \brief b <- a/k \remark For imprecise numerals, this is not equivalent to m().inv(k) mul(k, a, b) That is, we must invert k rounding towards +oo or -oo depending whether we are computing a lower or upper bound. */ void div(interval const & a, numeral const & k, interval & b, interval_deps & b_deps); void div(interval const & a, numeral const & k, interval & b) { div_mul(k, a, b, true); } void div_jst(interval const & a, numeral const & k, interval_deps & b_deps) { mul_jst(k, a, b_deps); } /** \brief c <- a * b */ void mul(interval const & a, interval const & b, interval & c, interval_deps & c_deps); void mul(interval const & a, interval const & b, interval & c); void mul_jst(interval const & a, interval const & b, interval_deps & c_deps); /** \brief b <- a^n */ void power(interval const & a, unsigned n, interval & b, interval_deps & b_deps); void power(interval const & a, unsigned n, interval & b); void power_jst(interval const & a, unsigned n, interval_deps & b_deps); /** \brief b <- a^(1/n) with precision p. \pre if n is even, then a must not contain negative numbers. */ void nth_root(interval const & a, unsigned n, numeral const & p, interval & b, interval_deps & b_deps); void nth_root(interval const & a, unsigned n, numeral const & p, interval & b); void nth_root_jst(interval const & a, unsigned n, numeral const & p, interval_deps & b_deps); /** \brief Given an equation x^n = y and an interval for y, compute the solution set for x with precision p. \pre if n is even, then !lower_is_neg(y) */ void xn_eq_y(interval const & y, unsigned n, numeral const & p, interval & x, interval_deps & x_deps); void xn_eq_y(interval const & y, unsigned n, numeral const & p, interval & x); void xn_eq_y_jst(interval const & y, unsigned n, numeral const & p, interval_deps & x_deps); /** \brief b <- 1/a \pre !contains_zero(a) */ void inv(interval const & a, interval & b, interval_deps & b_deps); void inv(interval const & a, interval & b); void inv_jst(interval const & a, interval_deps & b_deps); /** \brief c <- a/b \pre !contains_zero(b) \pre &a == &c (that is, c should not be an alias for a) */ void div(interval const & a, interval const & b, interval & c, interval_deps & c_deps); void div(interval const & a, interval const & b, interval & c); void div_jst(interval const & a, interval const & b, interval_deps & c_deps); /** \brief Store in r an interval that contains the number pi. The size of the interval is (1/15)*(1/16^n) */ void pi(unsigned n, interval & r); /** \brief Set the precision of the internal interval representing pi. */ void set_pi_prec(unsigned n); /** \brief Set the precision of the internal interval representing pi to a precision of at least n. */ void set_pi_at_least_prec(unsigned n); void sine(numeral const & a, unsigned k, numeral & lo, numeral & hi); void cosine(numeral const & a, unsigned k, numeral & lo, numeral & hi); /** \brief Store in r the Euler's constant e. The size of the interval is 4/(k+1)! */ void e(unsigned k, interval & r); }; template class _scoped_interval { public: typedef typename Manager::interval interval; private: Manager & m_manager; interval m_interval; public: _scoped_interval(Manager & m):m_manager(m) {} ~_scoped_interval() { m_manager.del(m_interval); } Manager & m() const { return m_manager; } operator interval const &() const { return m_interval; } operator interval&() { return m_interval; } interval const & get() const { return m_interval; } interval & get() { return m_interval; } interval * operator->() { return &m_interval; } interval const * operator->() const { return &m_interval; } }; #endif z3-z3-4.8.7/src/math/interval/interval_def.h000066400000000000000000002076011356505360400206140ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: interval_def.h Abstract: Goodies/Templates for interval arithmetic Author: Leonardo de Moura (leonardo) 2012-07-19. Revision History: --*/ #ifndef INTERVAL_DEF_H_ #define INTERVAL_DEF_H_ #include "math/interval/interval.h" #include "util/debug.h" #include "util/trace.h" #include "util/scoped_numeral.h" #include "util/common_msgs.h" #define DEFAULT_PI_PRECISION 2 // #define TRACE_NTH_ROOT template interval_manager::interval_manager(reslimit& lim, C && c): m_limit(lim), m_c(std::move(c)) { m().set(m_minus_one, -1); m().set(m_one, 1); m_pi_n = 0; } template interval_manager::~interval_manager() { del(m_pi_div_2); del(m_pi); del(m_3_pi_div_2); del(m_2_pi); m().del(m_result_lower); m().del(m_result_upper); m().del(m_mul_ad); m().del(m_mul_bc); m().del(m_mul_ac); m().del(m_mul_bd); m().del(m_minus_one); m().del(m_one); m().del(m_inv_k); } template void interval_manager::del(interval & a) { m().del(lower(a)); m().del(upper(a)); } template void interval_manager::checkpoint() { if (!m_limit.inc()) throw default_exception(Z3_CANCELED_MSG); } /* Compute the n-th root of a with precision p. The result hi - lo <= p lo and hi are lower/upper bounds for the value of the n-th root of a. That is, the n-th root is in the interval [lo, hi] If n is even, then a is assumed to be nonnegative. If numeral_manager is not precise, the procedure does not guarantee the precision p. */ template void interval_manager::nth_root_slow(numeral const & a, unsigned n, numeral const & p, numeral & lo, numeral & hi) { #ifdef TRACE_NTH_ROOT static unsigned counter = 0; static unsigned loop_counter = 0; counter++; if (counter % 1000 == 0) std::cerr << "[nth-root] " << counter << " " << loop_counter << " " << ((double)loop_counter)/((double)counter) << std::endl; #endif bool n_is_even = (n % 2 == 0); SASSERT(!n_is_even || m().is_nonneg(a)); if (m().is_zero(a) || m().is_one(a) || (!n_is_even && m().eq(a, m_minus_one))) { m().set(lo, a); m().set(hi, a); return; } if (m().lt(a, m_minus_one)) { m().set(lo, a); m().set(hi, -1); } else if (m().is_neg(a)) { m().set(lo, -1); m().set(hi, 0); } else if (m().lt(a, m_one)) { m().set(lo, 0); m().set(hi, 1); } else { m().set(lo, 1); m().set(hi, a); } SASSERT(m().le(lo, hi)); _scoped_numeral c(m()), cn(m()); _scoped_numeral two(m()); m().set(two, 2); while (true) { checkpoint(); #ifdef TRACE_NTH_ROOT loop_counter++; #endif m().add(hi, lo, c); m().div(c, two, c); if (m().precise()) { m().power(c, n, cn); if (m().gt(cn, a)) { m().set(hi, c); } else if (m().eq(cn, a)) { // root is precise m().set(lo, c); m().set(hi, c); return; } else { m().set(lo, c); } } else { round_to_minus_inf(); m().power(c, n, cn); if (m().gt(cn, a)) { m().set(hi, c); } else { round_to_plus_inf(); m().power(c, n, cn); if (m().lt(cn, a)) { m().set(lo, c); } else { // can't improve, numeral_manager is not precise enough, // a is between round-to-minus-inf(c^n) and round-to-plus-inf(c^n) return; } } } round_to_plus_inf(); m().sub(hi, lo, c); if (m().le(c, p)) return; // result is precise enough } } /** \brief Store in o a rough approximation of a^1/n. It uses 2^Floor[Floor(Log2(a))/n] \pre is_pos(a) */ template void interval_manager::rough_approx_nth_root(numeral const & a, unsigned n, numeral & o) { SASSERT(m().is_pos(a)); SASSERT(n > 0); round_to_minus_inf(); unsigned k = m().prev_power_of_two(a); m().set(o, 2); m().power(o, k/n, o); } /* Compute the n-th root of \c a with (suggested) precision p. The only guarantee provided by this method is that a^(1/n) is in [lo, hi]. If n is even, then a is assumed to be nonnegative. */ template void interval_manager::nth_root(numeral const & a, unsigned n, numeral const & p, numeral & lo, numeral & hi) { // nth_root_slow(a, n, p, lo, hi); // return; SASSERT(n > 0); SASSERT(n % 2 != 0 || m().is_nonneg(a)); if (n == 1 || m().is_zero(a) || m().is_one(a) || m().is_minus_one(a)) { // easy cases: 1, -1, 0 m().set(lo, a); m().set(hi, a); return; } bool is_neg = m().is_neg(a); _scoped_numeral A(m()); m().set(A, a); m().abs(A); nth_root_pos(A, n, p, lo, hi); STRACE("nth_root_trace", tout << "[nth-root] ("; m().display(tout, A); tout << ")^(1/" << n << ") >= "; m().display(tout, lo); tout << "\n"; tout << "[nth-root] ("; m().display(tout, A); tout << ")^(1/" << n << ") <= "; m().display(tout, hi); tout << "\n";); if (is_neg) { m().swap(lo, hi); m().neg(lo); m().neg(hi); } } /** r <- A/(x^n) If to_plus_inf, then r >= A/(x^n) If not to_plus_inf, then r <= A/(x^n) */ template void interval_manager::A_div_x_n(numeral const & A, numeral const & x, unsigned n, bool to_plus_inf, numeral & r) { if (n == 1) { if (m().precise()) { m().div(A, x, r); } else { set_rounding(to_plus_inf); m().div(A, x, r); } } else { if (m().precise()) { m().power(x, n, r); m().div(A, r, r); } else { set_rounding(!to_plus_inf); m().power(x, n, r); set_rounding(to_plus_inf); m().div(A, r, r); } } } /** \brief Compute an approximation of A^(1/n) using the sequence x' = 1/n((n-1)*x + A/(x^(n-1))) The computation stops when the difference between current and new x is less than p. The procedure may not terminate if m() is not precise and p is very small. */ template void interval_manager::approx_nth_root(numeral const & A, unsigned n, numeral const & p, numeral & x) { SASSERT(m().is_pos(A)); SASSERT(n > 1); #ifdef TRACE_NTH_ROOT static unsigned counter = 0; static unsigned loop_counter = 0; counter++; if (counter % 1000 == 0) std::cerr << "[nth-root] " << counter << " " << loop_counter << " " << ((double)loop_counter)/((double)counter) << std::endl; #endif _scoped_numeral x_prime(m()), d(m()); m().set(d, 1); if (m().lt(A, d)) m().set(x, A); else rough_approx_nth_root(A, n, x); round_to_minus_inf(); if (n == 2) { _scoped_numeral two(m()); m().set(two, 2); while (true) { checkpoint(); #ifdef TRACE_NTH_ROOT loop_counter++; #endif m().div(A, x, x_prime); m().add(x, x_prime, x_prime); m().div(x_prime, two, x_prime); m().sub(x_prime, x, d); m().abs(d); m().swap(x, x_prime); if (m().lt(d, p)) return; } } else { _scoped_numeral _n(m()), _n_1(m()); m().set(_n, n); // _n contains n m().set(_n_1, n); m().dec(_n_1); // _n_1 contains n-1 while (true) { checkpoint(); #ifdef TRACE_NTH_ROOT loop_counter++; #endif m().power(x, n-1, x_prime); m().div(A, x_prime, x_prime); m().mul(_n_1, x, d); m().add(d, x_prime, x_prime); m().div(x_prime, _n, x_prime); m().sub(x_prime, x, d); m().abs(d); TRACE("nth_root", tout << "A: "; m().display(tout, A); tout << "\n"; tout << "x: "; m().display(tout, x); tout << "\n"; tout << "x_prime: "; m().display(tout, x_prime); tout << "\n"; tout << "d: "; m().display(tout, d); tout << "\n"; ); m().swap(x, x_prime); if (m().lt(d, p)) return; } } } template void interval_manager::nth_root_pos(numeral const & A, unsigned n, numeral const & p, numeral & lo, numeral & hi) { approx_nth_root(A, n, p, hi); if (m().precise()) { // Assuming hi has a upper bound for A^(n-1) // Then, A/(x^(n-1)) must be lower bound A_div_x_n(A, hi, n-1, false, lo); // Check if we were wrong if (m().lt(hi, lo)) { // swap if wrong m().swap(lo, hi); } } else { // Check if hi is really a upper bound for A^(n-1) A_div_x_n(A, hi, n-1, true /* lo will be greater than the actual lower bound */, lo); TRACE("nth_root_bug", tout << "Assuming upper\n"; tout << "A: "; m().display(tout, A); tout << "\n"; tout << "hi: "; m().display(tout, hi); tout << "\n"; tout << "lo: "; m().display(tout, hi); tout << "\n";); if (m().le(lo, hi)) { // hi is really the upper bound // Must compute lo again but approximating to -oo A_div_x_n(A, hi, n-1, false, lo); } else { // hi should be lower bound m().swap(lo, hi); // check if lo is lower bound A_div_x_n(A, lo, n-1, false /* hi will less than the actual upper bound */, hi); if (m().le(lo, hi)) { // lo is really the lower bound // Must compute hi again but approximating to +oo A_div_x_n(A, lo, n-1, true, hi); } else { // we don't have anything due to rounding errors // Be supper conservative // This should not really happen very often. _scoped_numeral one(m()); if (m().lt(A, one)) { m().set(lo, 0); m().set(hi, 1); } else { m().set(lo, 1); m().set(hi, A); } } } } } /** \brief o <- n! */ template void interval_manager::fact(unsigned n, numeral & o) { _scoped_numeral aux(m()); m().set(o, 1); for (unsigned i = 2; i <= n; i++) { m().set(aux, static_cast(i)); m().mul(aux, o, o); TRACE("fact_bug", tout << "i: " << i << ", o: " << m().to_rational_string(o) << "\n";); } } template void interval_manager::sine_series(numeral const & a, unsigned k, bool upper, numeral & o) { SASSERT(k % 2 == 1); // Compute sine using taylor series up to k // x - x^3/3! + x^5/5! - x^7/7! + ... // The result should be greater than or equal to the actual value if upper == true // Otherwise it must be less than or equal to the actual value. // The argument upper only matter if the numeral_manager is not precise. // Taylor series up to k with rounding to _scoped_numeral f(m()); _scoped_numeral aux(m()); m().set(o, a); bool sign = true; bool upper_factor = !upper; // since the first sign is negative, we must minimize factor to maximize result for (unsigned i = 3; i <= k; i+=2) { TRACE("sine_bug", tout << "[begin-loop] o: " << m().to_rational_string(o) << "\ni: " << i << "\n"; tout << "upper: " << upper << ", upper_factor: " << upper_factor << "\n"; tout << "o (default): " << m().to_string(o) << "\n";); set_rounding(upper_factor); m().power(a, i, f); TRACE("sine_bug", tout << "a^i " << m().to_rational_string(f) << "\n";); set_rounding(!upper_factor); fact(i, aux); TRACE("sine_bug", tout << "i! " << m().to_rational_string(aux) << "\n";); set_rounding(upper_factor); m().div(f, aux, f); TRACE("sine_bug", tout << "a^i/i! " << m().to_rational_string(f) << "\n";); set_rounding(upper); if (sign) m().sub(o, f, o); else m().add(o, f, o); TRACE("sine_bug", tout << "o: " << m().to_rational_string(o) << "\n";); sign = !sign; upper_factor = !upper_factor; } } template void interval_manager::sine(numeral const & a, unsigned k, numeral & lo, numeral & hi) { TRACE("sine", tout << "sine(a), a: " << m().to_rational_string(a) << "\na: " << m().to_string(a) << "\n";); SASSERT(&lo != &hi); if (m().is_zero(a)) { m().reset(lo); m().reset(hi); return; } // Compute sine using taylor series // x - x^3/3! + x^5/5! - x^7/7! + ... // // Note that, the coefficient of even terms is 0. // So, we force k to be odd to make sure the error is minimized. if (k % 2 == 0) k++; // Taylor series error = |x|^(k+1)/(k+1)! _scoped_numeral error(m()); _scoped_numeral aux(m()); round_to_plus_inf(); m().set(error, a); if (m().is_neg(error)) m().neg(error); m().power(error, k+1, error); TRACE("sine", tout << "a^(k+1): " << m().to_rational_string(error) << "\nk : " << k << "\n";); round_to_minus_inf(); fact(k+1, aux); TRACE("sine", tout << "(k+1)!: " << m().to_rational_string(aux) << "\n";); round_to_plus_inf(); m().div(error, aux, error); TRACE("sine", tout << "error: " << m().to_rational_string(error) << "\n";); // Taylor series up to k with rounding to -oo sine_series(a, k, false, lo); if (m().precise()) { m().set(hi, lo); m().sub(lo, error, lo); if (m().lt(lo, m_minus_one)) { m().set(lo, -1); m().set(hi, 1); } else { m().add(hi, error, hi); } } else { // We must recompute the series with rounding to +oo TRACE("sine", tout << "lo before -error: " << m().to_rational_string(lo) << "\n";); round_to_minus_inf(); m().sub(lo, error, lo); TRACE("sine", tout << "lo: " << m().to_rational_string(lo) << "\n";); if (m().lt(lo, m_minus_one)) { m().set(lo, -1); m().set(hi, 1); return; } sine_series(a, k, true, hi); round_to_plus_inf(); m().add(hi, error, hi); TRACE("sine", tout << "hi: " << m().to_rational_string(hi) << "\n";); } } template void interval_manager::cosine_series(numeral const & a, unsigned k, bool upper, numeral & o) { SASSERT(k % 2 == 0); // Compute cosine using taylor series up to k // 1 - x^2/2! + x^4/4! - x^6/6! + ... // The result should be greater than or equal to the actual value if upper == true // Otherwise it must be less than or equal to the actual value. // The argument upper only matter if the numeral_manager is not precise. // Taylor series up to k with rounding to -oo _scoped_numeral f(m()); _scoped_numeral aux(m()); m().set(o, 1); bool sign = true; bool upper_factor = !upper; // since the first sign is negative, we must minimize factor to maximize result for (unsigned i = 2; i <= k; i+=2) { set_rounding(upper_factor); m().power(a, i, f); set_rounding(!upper_factor); fact(i, aux); set_rounding(upper_factor); m().div(f, aux, f); set_rounding(upper); if (sign) m().sub(o, f, o); else m().add(o, f, o); sign = !sign; upper_factor = !upper_factor; } } template void interval_manager::cosine(numeral const & a, unsigned k, numeral & lo, numeral & hi) { TRACE("cosine", tout << "cosine(a): "; m().display_decimal(tout, a, 32); tout << "\n";); SASSERT(&lo != &hi); if (m().is_zero(a)) { m().set(lo, 1); m().set(hi, 1); return; } // Compute cosine using taylor series // 1 - x^2/2! + x^4/4! - x^6/6! + ... // // Note that, the coefficient of odd terms is 0. // So, we force k to be even to make sure the error is minimized. if (k % 2 == 1) k++; // Taylor series error = |x|^(k+1)/(k+1)! _scoped_numeral error(m()); _scoped_numeral aux(m()); round_to_plus_inf(); m().set(error, a); if (m().is_neg(error)) m().neg(error); m().power(error, k+1, error); round_to_minus_inf(); fact(k+1, aux); round_to_plus_inf(); m().div(error, aux, error); TRACE("sine", tout << "error: "; m().display_decimal(tout, error, 32); tout << "\n";); // Taylor series up to k with rounding to -oo cosine_series(a, k, false, lo); if (m().precise()) { m().set(hi, lo); m().sub(lo, error, lo); if (m().lt(lo, m_minus_one)) { m().set(lo, -1); m().set(hi, 1); } else { m().add(hi, error, hi); } } else { // We must recompute the series with rounding to +oo round_to_minus_inf(); m().sub(lo, error, lo); if (m().lt(lo, m_minus_one)) { m().set(lo, -1); m().set(hi, 1); return; } cosine_series(a, k, true, hi); round_to_plus_inf(); m().add(hi, error, hi); } } template void interval_manager::reset_lower(interval & a) { m().reset(lower(a)); set_lower_is_open(a, true); set_lower_is_inf(a, true); } template void interval_manager::reset_upper(interval & a) { m().reset(upper(a)); set_upper_is_open(a, true); set_upper_is_inf(a, true); } template void interval_manager::reset(interval & a) { reset_lower(a); reset_upper(a); } template bool interval_manager::contains_zero(interval const & n) const { return (lower_is_neg(n) || (lower_is_zero(n) && !lower_is_open(n))) && (upper_is_pos(n) || (upper_is_zero(n) && !upper_is_open(n))); } template bool interval_manager::contains(interval const & n, numeral const & v) const { if (!lower_is_inf(n)) { if (m().lt(v, lower(n))) return false; if (m().eq(v, lower(n)) && lower_is_open(n)) return false; } if (!upper_is_inf(n)) { if (m().gt(v, upper(n))) return false; if (m().eq(v, upper(n)) && upper_is_open(n)) return false; } return true; } template void interval_manager::display(std::ostream & out, interval const & n) const { out << (lower_is_open(n) ? "(" : "["); ::display(out, m(), lower(n), lower_kind(n)); out << ", "; ::display(out, m(), upper(n), upper_kind(n)); out << (upper_is_open(n) ? ")" : "]"); } template void interval_manager::display_pp(std::ostream & out, interval const & n) const { out << (lower_is_open(n) ? "(" : "["); ::display_pp(out, m(), lower(n), lower_kind(n)); out << ", "; ::display_pp(out, m(), upper(n), upper_kind(n)); out << (upper_is_open(n) ? ")" : "]"); } template bool interval_manager::check_invariant(interval const & n) const { if (::eq(m(), lower(n), lower_kind(n), upper(n), upper_kind(n))) { SASSERT(!lower_is_open(n)); SASSERT(!upper_is_open(n)); } else { SASSERT(lt(m(), lower(n), lower_kind(n), upper(n), upper_kind(n))); } return true; } template void interval_manager::set(numeral const& k, interval & b) { set_lower_is_inf(b, false); set_upper_is_inf(b, false); m().set(lower(b), k); m().set(upper(b), k); set_lower_is_open(b, false); set_upper_is_open(b, false); } template void interval_manager::set(interval & t, interval const & s) { if (&t == &const_cast(s)) return; if (lower_is_inf(s)) { set_lower_is_inf(t, true); } else { m().set(lower(t), lower(s)); set_lower_is_inf(t, false); } if (upper_is_inf(s)) { set_upper_is_inf(t, true); } else { m().set(upper(t), upper(s)); set_upper_is_inf(t, false); } set_lower_is_open(t, lower_is_open(s)); set_upper_is_open(t, upper_is_open(s)); SASSERT(check_invariant(t)); } template bool interval_manager::eq(interval const & a, interval const & b) const { return ::eq(m(), lower(a), lower_kind(a), lower(b), lower_kind(b)) && ::eq(m(), upper(a), upper_kind(a), upper(b), upper_kind(b)) && lower_is_open(a) == lower_is_open(b) && upper_is_open(a) == upper_is_open(b); } template bool interval_manager::before(interval const & a, interval const & b) const { if (upper_is_inf(a) || lower_is_inf(b)) return false; return m().lt(upper(a), lower(b)) || (upper_is_open(a) && m().eq(upper(a), lower(b))); } template void interval_manager::neg_jst(interval const & a, interval_deps & b_deps) { if (lower_is_inf(a)) { if (upper_is_inf(a)) { b_deps.m_lower_deps = 0; b_deps.m_upper_deps = 0; } else { b_deps.m_lower_deps = DEP_IN_UPPER1; b_deps.m_upper_deps = 0; } } else { if (upper_is_inf(a)) { b_deps.m_lower_deps = 0; b_deps.m_upper_deps = DEP_IN_LOWER1; } else { b_deps.m_lower_deps = DEP_IN_UPPER1; b_deps.m_upper_deps = DEP_IN_LOWER1; } } } template void interval_manager::neg(interval const & a, interval & b, interval_deps & b_deps) { neg_jst(a, b_deps); neg(a, b); } template void interval_manager::neg(interval const & a, interval & b) { if (lower_is_inf(a)) { if (upper_is_inf(a)) { reset(b); } else { m().set(lower(b), upper(a)); m().neg(lower(b)); set_lower_is_inf(b, false); set_lower_is_open(b, upper_is_open(a)); m().reset(upper(b)); set_upper_is_inf(b, true); set_upper_is_open(b, true); } } else { if (upper_is_inf(a)) { m().set(upper(b), lower(a)); m().neg(upper(b)); set_upper_is_inf(b, false); set_upper_is_open(b, lower_is_open(a)); m().reset(lower(b)); set_lower_is_inf(b, true); set_lower_is_open(b, true); } else { if (&a == &b) { m().swap(lower(b), upper(b)); } else { m().set(lower(b), upper(a)); m().set(upper(b), lower(a)); } m().neg(lower(b)); m().neg(upper(b)); set_lower_is_inf(b, false); set_upper_is_inf(b, false); bool l_o = lower_is_open(a); bool u_o = upper_is_open(a); set_lower_is_open(b, u_o); set_upper_is_open(b, l_o); } } SASSERT(check_invariant(b)); } template void interval_manager::add_jst(interval const & a, interval const & b, interval_deps & c_deps) { c_deps.m_lower_deps = DEP_IN_LOWER1 | DEP_IN_LOWER2; c_deps.m_upper_deps = DEP_IN_UPPER1 | DEP_IN_UPPER2; } template void interval_manager::add(interval const & a, interval const & b, interval & c, interval_deps & c_deps) { add_jst(a, b, c_deps); add(a, b, c); } template void interval_manager::add(interval const & a, interval const & b, interval & c) { ext_numeral_kind new_l_kind, new_u_kind; round_to_minus_inf(); ::add(m(), lower(a), lower_kind(a), lower(b), lower_kind(b), lower(c), new_l_kind); round_to_plus_inf(); ::add(m(), upper(a), upper_kind(a), upper(b), upper_kind(b), upper(c), new_u_kind); set_lower_is_inf(c, new_l_kind == EN_MINUS_INFINITY); set_upper_is_inf(c, new_u_kind == EN_PLUS_INFINITY); set_lower_is_open(c, lower_is_open(a) || lower_is_open(b)); set_upper_is_open(c, upper_is_open(a) || upper_is_open(b)); SASSERT(check_invariant(c)); } template void interval_manager::sub_jst(interval const & a, interval const & b, interval_deps & c_deps) { c_deps.m_lower_deps = DEP_IN_LOWER1 | DEP_IN_UPPER2; c_deps.m_upper_deps = DEP_IN_UPPER1 | DEP_IN_LOWER2; } template void interval_manager::sub(interval const & a, interval const & b, interval & c, interval_deps & c_deps) { sub_jst(a, b, c_deps); sub(a, b, c); } template void interval_manager::sub(interval const & a, interval const & b, interval & c) { ext_numeral_kind new_l_kind, new_u_kind; round_to_minus_inf(); ::sub(m(), lower(a), lower_kind(a), upper(b), upper_kind(b), lower(c), new_l_kind); round_to_plus_inf(); ::sub(m(), upper(a), upper_kind(a), lower(b), lower_kind(b), upper(c), new_u_kind); set_lower_is_inf(c, new_l_kind == EN_MINUS_INFINITY); set_upper_is_inf(c, new_u_kind == EN_PLUS_INFINITY); set_lower_is_open(c, lower_is_open(a) || upper_is_open(b)); set_upper_is_open(c, upper_is_open(a) || lower_is_open(b)); SASSERT(check_invariant(c)); } template void interval_manager::mul_jst(numeral const & k, interval const & a, interval_deps & b_deps) { if (m().is_zero(k)) { b_deps.m_lower_deps = 0; b_deps.m_upper_deps = 0; } else if (m().is_neg(k)) { b_deps.m_lower_deps = DEP_IN_UPPER1; b_deps.m_upper_deps = DEP_IN_LOWER1; } else { b_deps.m_lower_deps = DEP_IN_LOWER1; b_deps.m_upper_deps = DEP_IN_UPPER1; } } template void interval_manager::div_mul(numeral const & k, interval const & a, interval & b, bool inv_k) { if (m().is_zero(k)) { reset(b); } else { numeral const & l = lower(a); ext_numeral_kind l_k = lower_kind(a); numeral const & u = upper(a); ext_numeral_kind u_k = upper_kind(a); numeral & new_l_val = m_result_lower; numeral & new_u_val = m_result_upper; ext_numeral_kind new_l_kind, new_u_kind; bool l_o = lower_is_open(a); bool u_o = upper_is_open(a); if (m().is_pos(k)) { set_lower_is_open(b, l_o); set_upper_is_open(b, u_o); if (inv_k) { round_to_minus_inf(); m().inv(k, m_inv_k); ::mul(m(), l, l_k, m_inv_k, EN_NUMERAL, new_l_val, new_l_kind); round_to_plus_inf(); m().inv(k, m_inv_k); ::mul(m(), u, u_k, m_inv_k, EN_NUMERAL, new_u_val, new_u_kind); } else { round_to_minus_inf(); ::mul(m(), l, l_k, k, EN_NUMERAL, new_l_val, new_l_kind); round_to_plus_inf(); ::mul(m(), u, u_k, k, EN_NUMERAL, new_u_val, new_u_kind); } } else { set_lower_is_open(b, u_o); set_upper_is_open(b, l_o); if (inv_k) { round_to_minus_inf(); m().inv(k, m_inv_k); ::mul(m(), u, u_k, m_inv_k, EN_NUMERAL, new_l_val, new_l_kind); round_to_plus_inf(); m().inv(k, m_inv_k); ::mul(m(), l, l_k, m_inv_k, EN_NUMERAL, new_u_val, new_u_kind); } else { round_to_minus_inf(); ::mul(m(), u, u_k, k, EN_NUMERAL, new_l_val, new_l_kind); round_to_plus_inf(); ::mul(m(), l, l_k, k, EN_NUMERAL, new_u_val, new_u_kind); } } m().swap(lower(b), new_l_val); m().swap(upper(b), new_u_val); set_lower_is_inf(b, new_l_kind == EN_MINUS_INFINITY); set_upper_is_inf(b, new_u_kind == EN_PLUS_INFINITY); } } template void interval_manager::mul(numeral const & k, interval const & a, interval & b, interval_deps & b_deps) { mul_jst(k, a, b_deps); mul(k, a, b); } template void interval_manager::mul(int n, int d, interval const & a, interval & b) { _scoped_numeral aux(m()); m().set(aux, n, d); mul(aux, a, b); } template void interval_manager::div(interval const & a, numeral const & k, interval & b, interval_deps & b_deps) { div_jst(a, k, b_deps); div(a, k, b); } template void interval_manager::mul_jst(interval const & i1, interval const & i2, interval_deps & r_deps) { if (is_zero(i1)) { r_deps.m_lower_deps = DEP_IN_LOWER1 | DEP_IN_UPPER1; r_deps.m_upper_deps = DEP_IN_LOWER1 | DEP_IN_UPPER1; } else if (is_zero(i2)) { r_deps.m_lower_deps = DEP_IN_LOWER2 | DEP_IN_UPPER2; r_deps.m_upper_deps = DEP_IN_LOWER2 | DEP_IN_UPPER2; } else if (is_N(i1)) { if (is_N(i2)) { // x <= b <= 0, y <= d <= 0 --> b*d <= x*y // a <= x <= b <= 0, c <= y <= d <= 0 --> x*y <= a*c (we can use the fact that x or y is always negative (i.e., b is neg or d is neg)) r_deps.m_lower_deps = DEP_IN_UPPER1 | DEP_IN_UPPER2; r_deps.m_upper_deps = DEP_IN_LOWER1 | DEP_IN_LOWER2 | DEP_IN_UPPER1; // we can replace DEP_IN_UPPER1 with DEP_IN_UPPER2 } else if (is_M(i2)) { // a <= x <= b <= 0, y <= d, d > 0 --> a*d <= x*y (uses the fact that b is not positive) // a <= x <= b <= 0, c <= y, c < 0 --> x*y <= a*c (uses the fact that b is not positive) r_deps.m_lower_deps = DEP_IN_LOWER1 | DEP_IN_UPPER2 | DEP_IN_UPPER1; r_deps.m_upper_deps = DEP_IN_LOWER1 | DEP_IN_LOWER2 | DEP_IN_UPPER1; } else { // a <= x <= b <= 0, 0 <= c <= y <= d --> a*d <= x*y (uses the fact that x is neg (b is not positive) or y is pos (c is not negative)) // x <= b <= 0, 0 <= c <= y --> x*y <= b*c r_deps.m_lower_deps = DEP_IN_LOWER1 | DEP_IN_UPPER2 | DEP_IN_UPPER1; // we can replace DEP_IN_UPPER1 with DEP_IN_UPPER2 r_deps.m_upper_deps = DEP_IN_UPPER1 | DEP_IN_LOWER2; } } else if (is_M(i1)) { if (is_N(i2)) { // b > 0, x <= b, c <= y <= d <= 0 --> b*c <= x*y (uses the fact that d is not positive) // a < 0, a <= x, c <= y <= d <= 0 --> x*y <= a*c (uses the fact that d is not positive) r_deps.m_lower_deps = DEP_IN_UPPER1 | DEP_IN_LOWER2 | DEP_IN_UPPER2; r_deps.m_upper_deps = DEP_IN_LOWER1 | DEP_IN_LOWER2 | DEP_IN_UPPER2; } else if (is_M(i2)) { r_deps.m_lower_deps = DEP_IN_LOWER1 | DEP_IN_UPPER1 | DEP_IN_LOWER2 | DEP_IN_UPPER2; r_deps.m_upper_deps = DEP_IN_LOWER1 | DEP_IN_UPPER1 | DEP_IN_LOWER2 | DEP_IN_UPPER2; } else { // a < 0, a <= x, 0 <= c <= y <= d --> a*d <= x*y (uses the fact that c is not negative) // b > 0, x <= b, 0 <= c <= y <= d --> x*y <= b*d (uses the fact that c is not negative) r_deps.m_lower_deps = DEP_IN_LOWER1 | DEP_IN_UPPER2 | DEP_IN_LOWER2; r_deps.m_upper_deps = DEP_IN_UPPER1 | DEP_IN_UPPER2 | DEP_IN_LOWER2; } } else { SASSERT(is_P(i1)); if (is_N(i2)) { // 0 <= a <= x <= b, c <= y <= d <= 0 --> x*y <= b*c (uses the fact that x is pos (a is not neg) or y is neg (d is not pos)) // 0 <= a <= x, y <= d <= 0 --> a*d <= x*y r_deps.m_lower_deps = DEP_IN_UPPER1 | DEP_IN_LOWER2 | DEP_IN_LOWER1; // we can replace DEP_IN_LOWER1 with DEP_IN_UPPER2 r_deps.m_upper_deps = DEP_IN_LOWER1 | DEP_IN_UPPER2; } else if (is_M(i2)) { // 0 <= a <= x <= b, c <= y --> b*c <= x*y (uses the fact that a is not negative) // 0 <= a <= x <= b, y <= d --> x*y <= b*d (uses the fact that a is not negative) r_deps.m_lower_deps = DEP_IN_UPPER1 | DEP_IN_LOWER2 | DEP_IN_LOWER1; r_deps.m_upper_deps = DEP_IN_UPPER1 | DEP_IN_UPPER2 | DEP_IN_LOWER1; } else { SASSERT(is_P(i2)); // 0 <= a <= x, 0 <= c <= y --> a*c <= x*y // x <= b, y <= d --> x*y <= b*d (uses the fact that x is pos (a is not negative) or y is pos (c is not negative)) r_deps.m_lower_deps = DEP_IN_LOWER1 | DEP_IN_LOWER2; r_deps.m_upper_deps = DEP_IN_UPPER1 | DEP_IN_UPPER2 | DEP_IN_LOWER1; // we can replace DEP_IN_LOWER1 with DEP_IN_LOWER2 } } } template void interval_manager::mul(interval const & i1, interval const & i2, interval & r, interval_deps & r_deps) { mul_jst(i1, i2, r_deps); mul(i1, i2, r); } template void interval_manager::mul(interval const & i1, interval const & i2, interval & r) { #ifdef _TRACE static unsigned call_id = 0; #endif #if Z3DEBUG bool i1_contains_zero = contains_zero(i1); bool i2_contains_zero = contains_zero(i2); #endif if (is_zero(i1)) { set(r, i1); return; } if (is_zero(i2)) { set(r, i2); return; } numeral const & a = lower(i1); ext_numeral_kind a_k = lower_kind(i1); numeral const & b = upper(i1); ext_numeral_kind b_k = upper_kind(i1); numeral const & c = lower(i2); ext_numeral_kind c_k = lower_kind(i2); numeral const & d = upper(i2); ext_numeral_kind d_k = upper_kind(i2); bool a_o = lower_is_open(i1); bool b_o = upper_is_open(i1); bool c_o = lower_is_open(i2); bool d_o = upper_is_open(i2); numeral & new_l_val = m_result_lower; numeral & new_u_val = m_result_upper; ext_numeral_kind new_l_kind, new_u_kind; if (is_N(i1)) { if (is_N(i2)) { // x <= b <= 0, y <= d <= 0 --> b*d <= x*y // a <= x <= b <= 0, c <= y <= d <= 0 --> x*y <= a*c (we can use the fact that x or y is always negative (i.e., b is neg or d is neg)) TRACE("interval_bug", tout << "(N, N) #" << call_id << "\n"; display(tout, i1); tout << "\n"; display(tout, i2); tout << "\n"; tout << "a: "; m().display(tout, a); tout << "\n"; tout << "b: "; m().display(tout, b); tout << "\n"; tout << "c: "; m().display(tout, c); tout << "\n"; tout << "d: "; m().display(tout, d); tout << "\n"; tout << "is_N0(i1): " << is_N0(i1) << "\n"; tout << "is_N0(i2): " << is_N0(i2) << "\n"; ); set_lower_is_open(r, (is_N0(i1) || is_N0(i2)) ? false : (b_o || d_o)); set_upper_is_open(r, a_o || c_o); // if b = 0 (and the interval is closed), then the lower bound is closed round_to_minus_inf(); ::mul(m(), b, b_k, d, d_k, new_l_val, new_l_kind); round_to_plus_inf(); ::mul(m(), a, a_k, c, c_k, new_u_val, new_u_kind); } else if (is_M(i2)) { // a <= x <= b <= 0, y <= d, d > 0 --> a*d <= x*y (uses the fact that b is not positive) // a <= x <= b <= 0, c <= y, c < 0 --> x*y <= a*c (uses the fact that b is not positive) TRACE("interval_bug", tout << "(N, M) #" << call_id << "\n";); set_lower_is_open(r, a_o || d_o); set_upper_is_open(r, a_o || c_o); round_to_minus_inf(); ::mul(m(), a, a_k, d, d_k, new_l_val, new_l_kind); round_to_plus_inf(); ::mul(m(), a, a_k, c, c_k, new_u_val, new_u_kind); } else { // a <= x <= b <= 0, 0 <= c <= y <= d --> a*d <= x*y (uses the fact that x is neg (b is not positive) or y is pos (c is not negative)) // x <= b <= 0, 0 <= c <= y --> x*y <= b*c TRACE("interval_bug", tout << "(N, P) #" << call_id << "\n";); SASSERT(is_P(i2)); // must update upper_is_open first, since value of is_N0(i1) and is_P0(i2) may be affected by update set_upper_is_open(r, (is_N0(i1) || is_P0(i2)) ? false : (b_o || c_o)); set_lower_is_open(r, a_o || d_o); round_to_minus_inf(); ::mul(m(), a, a_k, d, d_k, new_l_val, new_l_kind); round_to_plus_inf(); ::mul(m(), b, b_k, c, c_k, new_u_val, new_u_kind); } } else if (is_M(i1)) { if (is_N(i2)) { // b > 0, x <= b, c <= y <= d <= 0 --> b*c <= x*y (uses the fact that d is not positive) // a < 0, a <= x, c <= y <= d <= 0 --> x*y <= a*c (uses the fact that d is not positive) TRACE("interval_bug", tout << "(M, N) #" << call_id << "\n";); set_lower_is_open(r, b_o || c_o); set_upper_is_open(r, a_o || c_o); round_to_minus_inf(); ::mul(m(), b, b_k, c, c_k, new_l_val, new_l_kind); round_to_plus_inf(); ::mul(m(), a, a_k, c, c_k, new_u_val, new_u_kind); } else if (is_M(i2)) { numeral & ad = m_mul_ad; ext_numeral_kind ad_k; numeral & bc = m_mul_bc; ext_numeral_kind bc_k; numeral & ac = m_mul_ac; ext_numeral_kind ac_k; numeral & bd = m_mul_bd; ext_numeral_kind bd_k; bool ad_o = a_o || d_o; bool bc_o = b_o || c_o; bool ac_o = a_o || c_o; bool bd_o = b_o || d_o; round_to_minus_inf(); ::mul(m(), a, a_k, d, d_k, ad, ad_k); ::mul(m(), b, b_k, c, c_k, bc, bc_k); round_to_plus_inf(); ::mul(m(), a, a_k, c, c_k, ac, ac_k); ::mul(m(), b, b_k, d, d_k, bd, bd_k); if (::lt(m(), ad, ad_k, bc, bc_k) || (::eq(m(), ad, ad_k, bc, bc_k) && !ad_o && bc_o)) { m().swap(new_l_val, ad); new_l_kind = ad_k; set_lower_is_open(r, ad_o); } else { m().swap(new_l_val, bc); new_l_kind = bc_k; set_lower_is_open(r, bc_o); } if (::gt(m(), ac, ac_k, bd, bd_k) || (::eq(m(), ac, ac_k, bd, bd_k) && !ac_o && bd_o)) { m().swap(new_u_val, ac); new_u_kind = ac_k; set_upper_is_open(r, ac_o); } else { m().swap(new_u_val, bd); new_u_kind = bd_k; set_upper_is_open(r, bd_o); } } else { // a < 0, a <= x, 0 <= c <= y <= d --> a*d <= x*y (uses the fact that c is not negative) // b > 0, x <= b, 0 <= c <= y <= d --> x*y <= b*d (uses the fact that c is not negative) TRACE("interval_bug", tout << "(M, P) #" << call_id << "\n";); SASSERT(is_P(i2)); set_lower_is_open(r, a_o || d_o); set_upper_is_open(r, b_o || d_o); round_to_minus_inf(); ::mul(m(), a, a_k, d, d_k, new_l_val, new_l_kind); round_to_plus_inf(); ::mul(m(), b, b_k, d, d_k, new_u_val, new_u_kind); } } else { SASSERT(is_P(i1)); if (is_N(i2)) { // 0 <= a <= x <= b, c <= y <= d <= 0 --> x*y <= b*c (uses the fact that x is pos (a is not neg) or y is neg (d is not pos)) // 0 <= a <= x, y <= d <= 0 --> a*d <= x*y TRACE("interval_bug", tout << "(P, N) #" << call_id << "\n";); // must update upper_is_open first, since value of is_P0(i1) and is_N0(i2) may be affected by update set_upper_is_open(r, (is_P0(i1) || is_N0(i2)) ? false : a_o || d_o); set_lower_is_open(r, b_o || c_o); round_to_minus_inf(); ::mul(m(), b, b_k, c, c_k, new_l_val, new_l_kind); round_to_plus_inf(); ::mul(m(), a, a_k, d, d_k, new_u_val, new_u_kind); } else if (is_M(i2)) { // 0 <= a <= x <= b, c <= y --> b*c <= x*y (uses the fact that a is not negative) // 0 <= a <= x <= b, y <= d --> x*y <= b*d (uses the fact that a is not negative) TRACE("interval_bug", tout << "(P, M) #" << call_id << "\n";); set_lower_is_open(r, b_o || c_o); set_upper_is_open(r, b_o || d_o); round_to_minus_inf(); ::mul(m(), b, b_k, c, c_k, new_l_val, new_l_kind); round_to_plus_inf(); ::mul(m(), b, b_k, d, d_k, new_u_val, new_u_kind); } else { SASSERT(is_P(i2)); // 0 <= a <= x, 0 <= c <= y --> a*c <= x*y // x <= b, y <= d --> x*y <= b*d (uses the fact that x is pos (a is not negative) or y is pos (c is not negative)) TRACE("interval_bug", tout << "(P, P) #" << call_id << "\n";); set_lower_is_open(r, (is_P0(i1) || is_P0(i2)) ? false : a_o || c_o); set_upper_is_open(r, b_o || d_o); round_to_minus_inf(); ::mul(m(), a, a_k, c, c_k, new_l_val, new_l_kind); round_to_plus_inf(); ::mul(m(), b, b_k, d, d_k, new_u_val, new_u_kind); } } m().swap(lower(r), new_l_val); m().swap(upper(r), new_u_val); set_lower_is_inf(r, new_l_kind == EN_MINUS_INFINITY); set_upper_is_inf(r, new_u_kind == EN_PLUS_INFINITY); SASSERT(!(i1_contains_zero || i2_contains_zero) || contains_zero(r)); TRACE("interval_bug", tout << "result: "; display(tout, r); tout << "\n";); #ifdef _TRACE call_id++; #endif } template void interval_manager::power_jst(interval const & a, unsigned n, interval_deps & b_deps) { if (n == 1) { b_deps.m_lower_deps = DEP_IN_LOWER1; b_deps.m_upper_deps = DEP_IN_UPPER1; } else if (n % 2 == 0) { if (lower_is_pos(a)) { // [l, u]^n = [l^n, u^n] if l > 0 // 0 < l <= x --> l^n <= x^n (lower bound guarantees that is positive) // 0 < l <= x <= u --> x^n <= u^n (use lower and upper bound -- need the fact that x is positive) b_deps.m_lower_deps = DEP_IN_LOWER1; if (upper_is_inf(a)) b_deps.m_upper_deps = 0; else b_deps.m_upper_deps = DEP_IN_LOWER1 | DEP_IN_UPPER1; } else if (upper_is_neg(a)) { // [l, u]^n = [u^n, l^n] if u < 0 // l <= x <= u < 0 --> x^n <= l^n (use lower and upper bound -- need the fact that x is negative) // x <= u < 0 --> u^n <= x^n b_deps.m_lower_deps = DEP_IN_UPPER1; if (lower_is_inf(a)) b_deps.m_upper_deps = 0; else b_deps.m_upper_deps = DEP_IN_LOWER1 | DEP_IN_UPPER1; } else { // [l, u]^n = [0, max{l^n, u^n}] otherwise // we need both bounds to justify upper bound b_deps.m_upper_deps = DEP_IN_LOWER1 | DEP_IN_UPPER1; b_deps.m_lower_deps = 0; } } else { // Remark: when n is odd x^n is monotonic. if (lower_is_inf(a)) b_deps.m_lower_deps = 0; else b_deps.m_lower_deps = DEP_IN_LOWER1; if (upper_is_inf(a)) b_deps.m_upper_deps = 0; else b_deps.m_upper_deps = DEP_IN_UPPER1; } } template void interval_manager::power(interval const & a, unsigned n, interval & b, interval_deps & b_deps) { power_jst(a, n, b_deps); power(a, n, b); } template void interval_manager::power(interval const & a, unsigned n, interval & b) { #ifdef _TRACE static unsigned call_id = 0; #endif if (n == 1) { set(b, a); } else if (n % 2 == 0) { if (lower_is_pos(a)) { // [l, u]^n = [l^n, u^n] if l > 0 // 0 < l <= x --> l^n <= x^n (lower bound guarantees that is positive) // 0 < l <= x <= u --> x^n <= u^n (use lower and upper bound -- need the fact that x is positive) SASSERT(!lower_is_inf(a)); round_to_minus_inf(); m().power(lower(a), n, lower(b)); set_lower_is_inf(b, false); set_lower_is_open(b, lower_is_open(a)); if (upper_is_inf(a)) { reset_upper(b); } else { round_to_plus_inf(); m().power(upper(a), n, upper(b)); set_upper_is_inf(b, false); set_upper_is_open(b, upper_is_open(a)); } } else if (upper_is_neg(a)) { // [l, u]^n = [u^n, l^n] if u < 0 // l <= x <= u < 0 --> x^n <= l^n (use lower and upper bound -- need the fact that x is negative) // x <= u < 0 --> u^n <= x^n SASSERT(!upper_is_inf(a)); bool lower_a_open = lower_is_open(a), upper_a_open = upper_is_open(a); bool lower_a_inf = lower_is_inf(a); m().set(lower(b), lower(a)); m().set(upper(b), upper(a)); m().swap(lower(b), upper(b)); // we use a swap because a and b can be aliased round_to_minus_inf(); m().power(lower(b), n, lower(b)); set_lower_is_open(b, upper_a_open); set_lower_is_inf(b, false); if (lower_a_inf) { reset_upper(b); } else { round_to_plus_inf(); m().power(upper(b), n, upper(b)); set_upper_is_inf(b, false); set_upper_is_open(b, lower_a_open); } } else { // [l, u]^n = [0, max{l^n, u^n}] otherwise // we need both bounds to justify upper bound TRACE("interval_bug", tout << "(M) #" << call_id << "\n"; display(tout, a); tout << "\nn:" << n << "\n";); ext_numeral_kind un1_kind = lower_kind(a), un2_kind = upper_kind(a); numeral & un1 = m_result_lower; numeral & un2 = m_result_upper; m().set(un1, lower(a)); m().set(un2, upper(a)); round_to_plus_inf(); ::power(m(), un1, un1_kind, n); ::power(m(), un2, un2_kind, n); if (::gt(m(), un1, un1_kind, un2, un2_kind) || (::eq(m(), un1, un1_kind, un2, un2_kind) && !lower_is_open(a) && upper_is_open(a))) { m().swap(upper(b), un1); set_upper_is_inf(b, un1_kind == EN_PLUS_INFINITY); set_upper_is_open(b, lower_is_open(a)); } else { m().swap(upper(b), un2); set_upper_is_inf(b, un2_kind == EN_PLUS_INFINITY); set_upper_is_open(b, upper_is_open(a)); } m().reset(lower(b)); set_lower_is_inf(b, false); set_lower_is_open(b, false); } } else { // Remark: when n is odd x^n is monotonic. if (lower_is_inf(a)) { reset_lower(b); } else { m().power(lower(a), n, lower(b)); set_lower_is_inf(b, false); set_lower_is_open(b, lower_is_open(a)); } if (upper_is_inf(a)) { reset_upper(b); } else { m().power(upper(a), n, upper(b)); set_upper_is_inf(b, false); set_upper_is_open(b, upper_is_open(a)); } } TRACE("interval_bug", tout << "result: "; display(tout, b); tout << "\n";); #ifdef _TRACE call_id++; #endif } template void interval_manager::nth_root(interval const & a, unsigned n, numeral const & p, interval & b, interval_deps & b_deps) { nth_root_jst(a, n, p, b_deps); nth_root(a, n, p, b); } template void interval_manager::nth_root(interval const & a, unsigned n, numeral const & p, interval & b) { SASSERT(n % 2 != 0 || !lower_is_neg(a)); if (n == 1) { set(b, a); return; } if (lower_is_inf(a)) { SASSERT(n % 2 != 0); // n must not be even. m().reset(lower(b)); set_lower_is_inf(b, true); set_lower_is_open(b, true); } else { numeral & lo = m_result_lower; numeral & hi = m_result_upper; nth_root(lower(a), n, p, lo, hi); set_lower_is_inf(b, false); set_lower_is_open(b, lower_is_open(a) && m().eq(lo, hi)); m().set(lower(b), lo); } if (upper_is_inf(a)) { m().reset(upper(b)); set_upper_is_inf(b, true); set_upper_is_open(b, true); } else { numeral & lo = m_result_lower; numeral & hi = m_result_upper; nth_root(upper(a), n, p, lo, hi); set_upper_is_inf(b, false); set_upper_is_open(b, upper_is_open(a) && m().eq(lo, hi)); m().set(upper(b), hi); } TRACE("interval_nth_root", display(tout, a); tout << " --> "; display(tout, b); tout << "\n";); } template void interval_manager::nth_root_jst(interval const & a, unsigned n, numeral const & p, interval_deps & b_deps) { b_deps.m_lower_deps = DEP_IN_LOWER1; if (n % 2 == 0) b_deps.m_upper_deps = DEP_IN_LOWER1 | DEP_IN_UPPER1; else b_deps.m_upper_deps = DEP_IN_UPPER1; } template void interval_manager::xn_eq_y(interval const & y, unsigned n, numeral const & p, interval & x, interval_deps & x_deps) { xn_eq_y_jst(y, n, p, x_deps); xn_eq_y(y, n, p, x); } template void interval_manager::xn_eq_y(interval const & y, unsigned n, numeral const & p, interval & x) { SASSERT(n % 2 != 0 || !lower_is_neg(y)); if (n % 2 == 0) { SASSERT(!lower_is_inf(y)); if (upper_is_inf(y)) { reset(x); } else { numeral & lo = m_result_lower; numeral & hi = m_result_upper; nth_root(upper(y), n, p, lo, hi); // result is [-hi, hi] // result is open if upper(y) is open and lo == hi TRACE("interval_xn_eq_y", tout << "x^n = "; display(tout, y); tout << "\n"; tout << "sqrt(y) in "; m().display(tout, lo); tout << " "; m().display(tout, hi); tout << "\n";); bool open = upper_is_open(y) && m().eq(lo, hi); set_lower_is_inf(x, false); set_upper_is_inf(x, false); set_lower_is_open(x, open); set_upper_is_open(x, open); m().set(upper(x), hi); round_to_minus_inf(); m().set(lower(x), hi); m().neg(lower(x)); TRACE("interval_xn_eq_y", tout << "interval for x: "; display(tout, x); tout << "\n";); } } else { SASSERT(n % 2 == 1); // n is odd nth_root(y, n, p, x); } } template void interval_manager::xn_eq_y_jst(interval const & y, unsigned n, numeral const & p, interval_deps & x_deps) { if (n % 2 == 0) { x_deps.m_lower_deps = DEP_IN_LOWER1 | DEP_IN_UPPER1; x_deps.m_upper_deps = DEP_IN_LOWER1 | DEP_IN_UPPER1; } else { x_deps.m_lower_deps = DEP_IN_LOWER1; x_deps.m_upper_deps = DEP_IN_UPPER1; } } template void interval_manager::inv_jst(interval const & a, interval_deps & b_deps) { SASSERT(!contains_zero(a)); if (is_P1(a)) { b_deps.m_lower_deps = DEP_IN_LOWER1 | DEP_IN_UPPER1; b_deps.m_upper_deps = DEP_IN_LOWER1; } else if (is_N1(a)) { // x <= u < 0 --> 1/u <= 1/x // l <= x <= u < 0 --> 1/l <= 1/x (use lower and upper bounds) b_deps.m_lower_deps = DEP_IN_UPPER1; b_deps.m_upper_deps = DEP_IN_LOWER1 | DEP_IN_UPPER1; } else { UNREACHABLE(); } } template void interval_manager::inv(interval const & a, interval & b, interval_deps & b_deps) { inv_jst(a, b_deps); inv(a, b); } template void interval_manager::inv(interval const & a, interval & b) { #ifdef _TRACE static unsigned call_id = 0; #endif // If the interval [l,u] does not contain 0, then 1/[l,u] = [1/u, 1/l] SASSERT(!contains_zero(a)); TRACE("interval_bug", tout << "(inv) #" << call_id << "\n"; display(tout, a); tout << "\n";); numeral & new_l_val = m_result_lower; numeral & new_u_val = m_result_upper; ext_numeral_kind new_l_kind, new_u_kind; if (is_P1(a)) { // 0 < l <= x --> 1/x <= 1/l // 0 < l <= x <= u --> 1/u <= 1/x (use lower and upper bounds) round_to_minus_inf(); m().set(new_l_val, upper(a)); new_l_kind = upper_kind(a); ::inv(m(), new_l_val, new_l_kind); SASSERT(new_l_kind == EN_NUMERAL); bool new_l_open = upper_is_open(a); if (lower_is_zero(a)) { SASSERT(lower_is_open(a)); m().reset(upper(b)); set_upper_is_inf(b, true); set_upper_is_open(b, true); } else { round_to_plus_inf(); m().set(new_u_val, lower(a)); m().inv(new_u_val); m().swap(upper(b), new_u_val); set_upper_is_inf(b, false); set_upper_is_open(b, lower_is_open(a)); } m().swap(lower(b), new_l_val); set_lower_is_inf(b, false); set_lower_is_open(b, new_l_open); } else if (is_N1(a)) { // x <= u < 0 --> 1/u <= 1/x // l <= x <= u < 0 --> 1/l <= 1/x (use lower and upper bounds) round_to_plus_inf(); m().set(new_u_val, lower(a)); new_u_kind = lower_kind(a); ::inv(m(), new_u_val, new_u_kind); SASSERT(new_u_kind == EN_NUMERAL); bool new_u_open = lower_is_open(a); if (upper_is_zero(a)) { SASSERT(upper_is_open(a)); m().reset(lower(b)); set_lower_is_open(b, true); set_lower_is_inf(b, true); } else { round_to_minus_inf(); m().set(new_l_val, upper(a)); m().inv(new_l_val); m().swap(lower(b), new_l_val); set_lower_is_inf(b, false); set_lower_is_open(b, upper_is_open(a)); } m().swap(upper(b), new_u_val); set_upper_is_inf(b, false); set_upper_is_open(b, new_u_open); } else { UNREACHABLE(); } TRACE("interval_bug", tout << "result: "; display(tout, b); tout << "\n";); #ifdef _TRACE call_id++; #endif } template void interval_manager::div_jst(interval const & i1, interval const & i2, interval_deps & r_deps) { SASSERT(!contains_zero(i2)); if (is_zero(i1)) { if (is_P1(i2)) { r_deps.m_lower_deps = DEP_IN_LOWER1 | DEP_IN_LOWER2; r_deps.m_upper_deps = DEP_IN_UPPER1 | DEP_IN_LOWER2; } else { r_deps.m_lower_deps = DEP_IN_UPPER1 | DEP_IN_UPPER2; r_deps.m_upper_deps = DEP_IN_LOWER1 | DEP_IN_UPPER2; } } else { if (is_N(i1)) { if (is_N1(i2)) { // x <= b <= 0, c <= y <= d < 0 --> b/c <= x/y // a <= x <= b <= 0, y <= d < 0 --> x/y <= a/d r_deps.m_lower_deps = DEP_IN_UPPER1 | DEP_IN_LOWER2 | DEP_IN_UPPER2; r_deps.m_upper_deps = DEP_IN_LOWER1 | DEP_IN_UPPER2; } else { // a <= x, a < 0, 0 < c <= y --> a/c <= x/y // x <= b <= 0, 0 < c <= y <= d --> x/y <= b/d r_deps.m_lower_deps = DEP_IN_LOWER1 | DEP_IN_LOWER2; r_deps.m_upper_deps = DEP_IN_UPPER1 | DEP_IN_LOWER2 | DEP_IN_UPPER2; } } else if (is_M(i1)) { if (is_N1(i2)) { // 0 < a <= x <= b < 0, y <= d < 0 --> b/d <= x/y // 0 < a <= x <= b < 0, y <= d < 0 --> x/y <= a/d r_deps.m_lower_deps = DEP_IN_UPPER1 | DEP_IN_UPPER2; r_deps.m_upper_deps = DEP_IN_LOWER1 | DEP_IN_UPPER2; } else { // 0 < a <= x <= b < 0, 0 < c <= y --> a/c <= x/y // 0 < a <= x <= b < 0, 0 < c <= y --> x/y <= b/c r_deps.m_lower_deps = DEP_IN_LOWER1 | DEP_IN_LOWER2; r_deps.m_upper_deps = DEP_IN_UPPER1 | DEP_IN_LOWER2; } } else { SASSERT(is_P(i1)); if (is_N1(i2)) { // b > 0, x <= b, c <= y <= d < 0 --> b/d <= x/y // 0 <= a <= x, c <= y <= d < 0 --> x/y <= a/c r_deps.m_lower_deps = DEP_IN_UPPER1 | DEP_IN_UPPER2; r_deps.m_upper_deps = DEP_IN_LOWER1 | DEP_IN_LOWER2 | DEP_IN_UPPER2; } else { SASSERT(is_P1(i2)); // 0 <= a <= x, 0 < c <= y <= d --> a/d <= x/y // b > 0 x <= b, 0 < c <= y --> x/y <= b/c r_deps.m_lower_deps = DEP_IN_LOWER1 | DEP_IN_LOWER2 | DEP_IN_UPPER2; r_deps.m_upper_deps = DEP_IN_UPPER1 | DEP_IN_LOWER2; } } } } template void interval_manager::div(interval const & i1, interval const & i2, interval & r, interval_deps & r_deps) { div_jst(i1, i2, r_deps); div(i1, i2, r); } template void interval_manager::div(interval const & i1, interval const & i2, interval & r) { #ifdef _TRACE static unsigned call_id = 0; #endif SASSERT(!contains_zero(i2)); SASSERT(&i1 != &r); if (is_zero(i1)) { TRACE("interval_bug", tout << "div #" << call_id << "\n"; display(tout, i1); tout << "\n"; display(tout, i2); tout << "\n";); // 0/other = 0 if other != 0 m().reset(lower(r)); m().reset(upper(r)); set_lower_is_inf(r, false); set_upper_is_inf(r, false); set_lower_is_open(r, false); set_upper_is_open(r, false); } else { numeral const & a = lower(i1); ext_numeral_kind a_k = lower_kind(i1); numeral const & b = upper(i1); ext_numeral_kind b_k = upper_kind(i1); numeral const & c = lower(i2); ext_numeral_kind c_k = lower_kind(i2); numeral const & d = upper(i2); ext_numeral_kind d_k = upper_kind(i2); bool a_o = lower_is_open(i1); bool b_o = upper_is_open(i1); bool c_o = lower_is_open(i2); bool d_o = upper_is_open(i2); numeral & new_l_val = m_result_lower; numeral & new_u_val = m_result_upper; ext_numeral_kind new_l_kind, new_u_kind; TRACE("interval_bug", tout << "div #" << call_id << "\n"; display(tout, i1); tout << "\n"; display(tout, i2); tout << "\n"; tout << "a: "; m().display(tout, a); tout << "\n"; tout << "b: "; m().display(tout, b); tout << "\n"; tout << "c: "; m().display(tout, c); tout << "\n"; tout << "d: "; m().display(tout, d); tout << "\n"; ); if (is_N(i1)) { if (is_N1(i2)) { // x <= b <= 0, c <= y <= d < 0 --> b/c <= x/y // a <= x <= b <= 0, y <= d < 0 --> x/y <= a/d TRACE("interval_bug", tout << "(N, N) #" << call_id << "\n";); set_lower_is_open(r, is_N0(i1) ? false : b_o || c_o); set_upper_is_open(r, a_o || d_o); round_to_minus_inf(); ::div(m(), b, b_k, c, c_k, new_l_val, new_l_kind); if (m().is_zero(d)) { SASSERT(d_o); m().reset(new_u_val); new_u_kind = EN_PLUS_INFINITY; } else { round_to_plus_inf(); ::div(m(), a, a_k, d, d_k, new_u_val, new_u_kind); } } else { // a <= x, a < 0, 0 < c <= y --> a/c <= x/y // x <= b <= 0, 0 < c <= y <= d --> x/y <= b/d TRACE("interval_bug", tout << "(N, P) #" << call_id << "\n";); SASSERT(is_P1(i2)); set_upper_is_open(r, is_N0(i1) ? false : (b_o || d_o)); set_lower_is_open(r, a_o || c_o); if (m().is_zero(c)) { SASSERT(c_o); m().reset(new_l_val); new_l_kind = EN_MINUS_INFINITY; } else { round_to_minus_inf(); ::div(m(), a, a_k, c, c_k, new_l_val, new_l_kind); } round_to_plus_inf(); ::div(m(), b, b_k, d, d_k, new_u_val, new_u_kind); } } else if (is_M(i1)) { if (is_N1(i2)) { // 0 < a <= x <= b < 0, y <= d < 0 --> b/d <= x/y // 0 < a <= x <= b < 0, y <= d < 0 --> x/y <= a/d TRACE("interval_bug", tout << "(M, N) #" << call_id << "\n";); set_lower_is_open(r, b_o || d_o); set_upper_is_open(r, a_o || d_o); if (m().is_zero(d)) { SASSERT(d_o); m().reset(new_l_val); m().reset(new_u_val); new_l_kind = EN_MINUS_INFINITY; new_u_kind = EN_PLUS_INFINITY; } else { round_to_minus_inf(); ::div(m(), b, b_k, d, d_k, new_l_val, new_l_kind); round_to_plus_inf(); ::div(m(), a, a_k, d, d_k, new_u_val, new_u_kind); TRACE("interval_bug", tout << "new_l_kind: " << new_l_kind << ", new_u_kind: " << new_u_kind << "\n";); } } else { // 0 < a <= x <= b < 0, 0 < c <= y --> a/c <= x/y // 0 < a <= x <= b < 0, 0 < c <= y --> x/y <= b/c TRACE("interval_bug", tout << "(M, P) #" << call_id << "\n";); SASSERT(is_P1(i2)); set_lower_is_open(r, a_o || c_o); set_upper_is_open(r, b_o || c_o); if (m().is_zero(c)) { SASSERT(c_o); m().reset(new_l_val); m().reset(new_u_val); new_l_kind = EN_MINUS_INFINITY; new_u_kind = EN_PLUS_INFINITY; } else { round_to_minus_inf(); ::div(m(), a, a_k, c, c_k, new_l_val, new_l_kind); round_to_plus_inf(); ::div(m(), b, b_k, c, c_k, new_u_val, new_u_kind); } } } else { SASSERT(is_P(i1)); if (is_N1(i2)) { // b > 0, x <= b, c <= y <= d < 0 --> b/d <= x/y // 0 <= a <= x, c <= y <= d < 0 --> x/y <= a/c TRACE("interval_bug", tout << "(P, N) #" << call_id << "\n";); set_upper_is_open(r, is_P0(i1) ? false : a_o || c_o); set_lower_is_open(r, b_o || d_o); if (m().is_zero(d)) { SASSERT(d_o); m().reset(new_l_val); new_l_kind = EN_MINUS_INFINITY; } else { round_to_minus_inf(); ::div(m(), b, b_k, d, d_k, new_l_val, new_l_kind); } round_to_plus_inf(); ::div(m(), a, a_k, c, c_k, new_u_val, new_u_kind); } else { SASSERT(is_P1(i2)); // 0 <= a <= x, 0 < c <= y <= d --> a/d <= x/y // b > 0 x <= b, 0 < c <= y --> x/y <= b/c TRACE("interval_bug", tout << "(P, P) #" << call_id << "\n";); set_lower_is_open(r, is_P0(i1) ? false : a_o || d_o); set_upper_is_open(r, b_o || c_o); round_to_minus_inf(); ::div(m(), a, a_k, d, d_k, new_l_val, new_l_kind); if (m().is_zero(c)) { SASSERT(c_o); m().reset(new_u_val); new_u_kind = EN_PLUS_INFINITY; } else { round_to_plus_inf(); ::div(m(), b, b_k, c, c_k, new_u_val, new_u_kind); } } } m().swap(lower(r), new_l_val); m().swap(upper(r), new_u_val); set_lower_is_inf(r, new_l_kind == EN_MINUS_INFINITY); set_upper_is_inf(r, new_u_kind == EN_PLUS_INFINITY); } TRACE("interval_bug", tout << "result: "; display(tout, r); tout << "\n";); #ifdef _TRACE call_id++; #endif } template void interval_manager::pi_series(int x, numeral & r, bool up) { // Store in r the value: 1/16^x (4/(8x + 1) - 2/(8x + 4) - 1/(8x + 5) - 1/(8x + 6)) _scoped_numeral f(m()); set_rounding(up); m().set(r, 4, 8*x + 1); set_rounding(!up); m().set(f, 2, 8*x + 4); set_rounding(up); m().sub(r, f, r); set_rounding(!up); m().set(f, 1, 8*x + 5); set_rounding(up); m().sub(r, f, r); set_rounding(!up); m().set(f, 1, 8*x + 6); set_rounding(up); m().sub(r, f, r); m().set(f, 1, 16); m().power(f, x, f); m().mul(r, f, r); } template void interval_manager::pi(unsigned n, interval & r) { // Compute an interval that contains pi using the series // P[0] + P[1] + ... + P[n] // where // P[n] := 1/16^x (4/(8x + 1) - 2/(8x + 4) - 1/(8x + 5) - 1/(8x + 6)) // // The size of the interval is 1/15 * 1/(16^n) // // Lower is P[0] + P[1] + ... + P[n] // Upper is Lower + 1/15 * 1/(16^n) // compute size of the resulting interval round_to_plus_inf(); // overestimate size of the interval _scoped_numeral len(m()); _scoped_numeral p(m()); m().set(len, 1, 16); m().power(len, n, len); m().set(p, 1, 15); m().mul(p, len, len); // compute lower bound numeral & l_val = m_result_lower; m().reset(l_val); for (unsigned i = 0; i <= n; i++) { pi_series(i, p, false); round_to_minus_inf(); m().add(l_val, p, l_val); } // computer upper bound numeral & u_val = m_result_upper; if (m().precise()) { // the numeral manager is precise, so we do not need to recompute the series m().add(l_val, len, u_val); } else { // recompute the sum rounding to plus infinite m().reset(u_val); for (unsigned i = 0; i <= n; i++) { pi_series(i, p, true); round_to_plus_inf(); m().add(u_val, p, u_val); } round_to_plus_inf(); m().add(u_val, len, u_val); } set_lower_is_open(r, false); set_upper_is_open(r, false); set_lower_is_inf(r, false); set_upper_is_inf(r, false); m().set(lower(r), l_val); m().set(upper(r), u_val); } template void interval_manager::set_pi_prec(unsigned n) { SASSERT(n > 0); m_pi_n = n; pi(n, m_pi); mul(1, 2, m_pi, m_pi_div_2); mul(3, 2, m_pi, m_3_pi_div_2); mul(2, 1, m_pi, m_2_pi); } template void interval_manager::set_pi_at_least_prec(unsigned n) { if (n > m_pi_n) set_pi_prec(n); } template void interval_manager::e_series(unsigned k, bool upper, numeral & o) { _scoped_numeral d(m()), a(m()); m().set(o, 2); m().set(d, 1); for (unsigned i = 2; i <= k; i++) { set_rounding(!upper); m().set(a, static_cast(i)); m().mul(d, a, d); // d == i! m().set(a, d); set_rounding(upper); m().inv(a); // a == 1/i! m().add(o, a, o); } } template void interval_manager::e(unsigned k, interval & r) { // Store in r lower and upper bounds for Euler's constant. // // The procedure uses the series // // V = 1 + 1/1 + 1/2! + 1/3! + ... + 1/k! // // The error in the approximation above is <= E = 4/(k+1)! // Thus, e must be in the interval [V, V+E] numeral & lo = m_result_lower; numeral & hi = m_result_upper; e_series(k, false, lo); _scoped_numeral error(m()), aux(m()); round_to_minus_inf(); fact(k+1, error); round_to_plus_inf(); m().inv(error); // error == 1/(k+1)! m().set(aux, 4); m().mul(aux, error, error); // error == 4/(k+1)! if (m().precise()) { m().set(hi, lo); m().add(hi, error, hi); } else { e_series(k, true, hi); round_to_plus_inf(); m().add(hi, error, hi); } set_lower_is_open(r, false); set_upper_is_open(r, false); set_lower_is_inf(r, false); set_upper_is_inf(r, false); m().set(lower(r), lo); m().set(upper(r), hi); } #endif z3-z3-4.8.7/src/math/interval/interval_mpq.cpp000066400000000000000000000004641356505360400212040ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: interval_mpq.cpp Abstract: Instantiate template using defaults. Author: Leonardo de Moura (leonardo) 2012-10-31. Revision History: --*/ #include "math/interval/interval_def.h" template class interval_manager; z3-z3-4.8.7/src/math/polynomial/000077500000000000000000000000001356505360400163325ustar00rootroot00000000000000z3-z3-4.8.7/src/math/polynomial/CMakeLists.txt000066400000000000000000000005131356505360400210710ustar00rootroot00000000000000z3_add_component(polynomial SOURCES algebraic_numbers.cpp polynomial_cache.cpp polynomial.cpp rpolynomial.cpp sexpr2upolynomial.cpp upolynomial.cpp upolynomial_factorization.cpp COMPONENT_DEPENDENCIES util PYG_FILES algebraic_params.pyg EXTRA_REGISTER_MODULE_HEADERS polynomial.h ) z3-z3-4.8.7/src/math/polynomial/README000066400000000000000000000003531356505360400172130ustar00rootroot00000000000000Polynomial manipulation package. It contains support for univariate (upolynomial.*) and multivariate polynomials (polynomial.*). Multivariate polynomial factorization does not work yet (polynomial_factorization.*), and it is disabled. z3-z3-4.8.7/src/math/polynomial/algebraic_numbers.cpp000066400000000000000000003617721356505360400225220ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: algebraic_numbers.cpp Abstract: Real Algebraic Numbers Author: Leonardo (leonardo) 2011-11-22 Notes: --*/ #include "util/mpbq.h" #include "util/basic_interval.h" #include "util/scoped_ptr_vector.h" #include "util/mpbqi.h" #include "util/timeit.h" #include "util/common_msgs.h" #include "math/polynomial/algebraic_numbers.h" #include "math/polynomial/upolynomial.h" #include "math/polynomial/sexpr2upolynomial.h" #include "math/polynomial/algebraic_params.hpp" namespace algebraic_numbers { struct basic_cell { mpq m_value; }; // Each algebraic number is associated with two // isolating (refinable) intervals. The second // interval just caches refinements of the first one. struct algebraic_cell { // polynomial unsigned m_p_sz; mpz * m_p; mpbqi m_interval; // isolating/refinable interval // sign of p at the lower and upper bounds of m_interval unsigned m_minimal:1; // true if p is a minimal polynomial for representing the number unsigned m_sign_lower:1; unsigned m_not_rational:1; // if true we know for sure it is not a rational unsigned m_i:29; // number is the i-th root of p, 0 if it is not known which root of p the number is. algebraic_cell():m_p_sz(0), m_p(nullptr), m_minimal(false), m_not_rational(false), m_i(0) {} bool is_minimal() const { return m_minimal != 0; } }; typedef polynomial::manager poly_manager; typedef upolynomial::manager upoly_manager; typedef upolynomial::numeral_vector upoly; typedef upolynomial::scoped_numeral_vector scoped_upoly; typedef upolynomial::factors factors; void manager::get_param_descrs(param_descrs & r) { algebraic_params::collect_param_descrs(r); } struct manager::imp { reslimit& m_limit; manager & m_wrapper; small_object_allocator & m_allocator; unsynch_mpq_manager & m_qmanager; mpbq_manager m_bqmanager; mpbqi_manager m_bqimanager; poly_manager m_pmanager; upoly_manager m_upmanager; mpq m_zero; scoped_mpz m_is_rational_tmp; scoped_upoly m_isolate_tmp1; scoped_upoly m_isolate_tmp2; scoped_upoly m_isolate_tmp3; scoped_upoly m_eval_sign_tmp; factors m_isolate_factors; scoped_mpbq_vector m_isolate_roots; scoped_mpbq_vector m_isolate_lowers; scoped_mpbq_vector m_isolate_uppers; scoped_upoly m_add_tmp; polynomial::var m_x; polynomial::var m_y; // configuration int m_min_magnitude; bool m_factor; polynomial::factor_params m_factor_params; int m_zero_accuracy; // statistics unsigned m_compare_cheap; unsigned m_compare_sturm; unsigned m_compare_refine; unsigned m_compare_poly_eq; imp(reslimit& lim, manager & w, unsynch_mpq_manager & m, params_ref const & p, small_object_allocator & a): m_limit(lim), m_wrapper(w), m_allocator(a), m_qmanager(m), m_bqmanager(m), m_bqimanager(m_bqmanager), m_pmanager(lim, m, &a), m_upmanager(lim, m), m_is_rational_tmp(m), m_isolate_tmp1(upm()), m_isolate_tmp2(upm()), m_isolate_tmp3(upm()), m_eval_sign_tmp(upm()), m_isolate_factors(upm()), m_isolate_roots(bqm()), m_isolate_lowers(bqm()), m_isolate_uppers(bqm()), m_add_tmp(upm()) { updt_params(p); reset_statistics(); m_x = pm().mk_var(); m_y = pm().mk_var(); } ~imp() { } bool acell_inv(algebraic_cell const& c) { auto s = upm().eval_sign_at(c.m_p_sz, c.m_p, lower(&c)); return s == polynomial::sign_zero || c.m_sign_lower == (s == polynomial::sign_neg); } void checkpoint() { if (!m_limit.inc()) throw algebraic_exception(Z3_CANCELED_MSG); } void reset_statistics() { m_compare_cheap = 0; m_compare_sturm = 0; m_compare_refine = 0; m_compare_poly_eq = 0; } void collect_statistics(statistics & st) { #ifndef _EXTERNAL_RELEASE st.update("algebraic compare cheap", m_compare_cheap); st.update("algebraic compare sturm", m_compare_sturm); st.update("algebraic compare refine", m_compare_refine); st.update("algebraic compare poly", m_compare_poly_eq); #endif } void updt_params(params_ref const & _p) { algebraic_params p(_p); m_min_magnitude = -static_cast(p.min_mag()); m_factor = p.factor(); m_factor_params.m_max_p = p.factor_max_prime(); m_factor_params.m_p_trials = p.factor_num_primes(); m_factor_params.m_max_search_size = p.factor_search_size(); m_zero_accuracy = -static_cast(p.zero_accuracy()); } unsynch_mpq_manager & qm() { return m_qmanager; } mpbq_manager & bqm() { return m_bqmanager; } mpbqi_manager & bqim() { return m_bqimanager; } poly_manager & pm() { return m_pmanager; } upoly_manager & upm() { return m_upmanager; } void del(basic_cell * c) { qm().del(c->m_value); m_allocator.deallocate(sizeof(basic_cell), c); } void del_poly(algebraic_cell * c) { for (unsigned i = 0; i < c->m_p_sz; i++) qm().del(c->m_p[i]); m_allocator.deallocate(sizeof(mpz)*c->m_p_sz, c->m_p); c->m_p = nullptr; c->m_p_sz = 0; } void del_interval(algebraic_cell * c) { bqim().del(c->m_interval); } void del(algebraic_cell * c) { del_poly(c); del_interval(c); m_allocator.deallocate(sizeof(algebraic_cell), c); } void del(numeral & a) { if (a.m_cell == nullptr) return; if (a.is_basic()) del(a.to_basic()); else del(a.to_algebraic()); a.m_cell = nullptr; } void reset(numeral & a) { del(a); } bool is_zero(numeral const & a) { return a.m_cell == nullptr; } bool is_pos(numeral const & a) { if (a.is_basic()) return qm().is_pos(basic_value(a)); else return bqim().is_pos(a.to_algebraic()->m_interval); } bool is_neg(numeral const & a) { if (a.is_basic()) return qm().is_neg(basic_value(a)); else return bqim().is_neg(a.to_algebraic()->m_interval); } mpq const & basic_value(numeral const & a) { SASSERT(a.is_basic()); if (is_zero(a)) return m_zero; else return a.to_basic()->m_value; } bool is_int(numeral & a) { if (a.is_basic()) return qm().is_int(basic_value(a)); if (a.to_algebraic()->m_not_rational) return false; // we know for sure a is not a rational (and consequently an integer) // make sure the isolating interval has at most one integer if (!refine_until_prec(a, 1)) { SASSERT(a.is_basic()); // a became basic return qm().is_int(basic_value(a)); } // Find unique integer in the isolating interval algebraic_cell * c = a.to_algebraic(); scoped_mpz candidate(qm()); bqm().floor(qm(), upper(c), candidate); SASSERT(bqm().ge(upper(c), candidate)); if (bqm().lt(lower(c), candidate) && upm().eval_sign_at(c->m_p_sz, c->m_p, candidate) == polynomial::sign_zero) { m_wrapper.set(a, candidate); return true; } return false; } /* In our representation, non-basic numbers are encoded by polynomials of the form: a_n * x^n + ... + a_0 where a_0 != 0. Thus, we can find whether a non-basic number is actually a rational by using the Rational root theorem. p/q is a root of a_n * x^n + ... + a_0 If p is a factor of a_0, and q is a factor of a_n. If the isolating interval (lower, upper) has size less than 1/a_n, then (a_n*lower, a_n*upper) contains at most one integer. Let u be this integer, then the non-basic number is a rational iff u/a_n is the actual root. */ bool is_rational(numeral & a) { if (a.is_basic()) return true; if (a.to_algebraic()->m_not_rational) return false; // we know for sure a is not a rational TRACE("algebraic_bug", tout << "is_rational(a):\n"; display_root(tout, a); tout << "\n"; display_interval(tout, a); tout << "\n";); algebraic_cell * c = a.to_algebraic(); save_intervals saved_a(*this, c); mpz & a_n = c->m_p[c->m_p_sz - 1]; scoped_mpz & abs_a_n = m_is_rational_tmp; qm().set(abs_a_n, a_n); qm().abs(abs_a_n); // 1/2^{log2(a_n)+1} <= 1/a_n unsigned k = qm().log2(abs_a_n); k++; TRACE("algebraic_bug", tout << "abs(an): " << qm().to_string(abs_a_n) << ", k: " << k << "\n";); // make sure the isolating interval size is less than 1/2^k if (!refine_until_prec(a, k)) { SASSERT(a.is_basic()); // a became basic return true; } TRACE("algebraic_bug", tout << "interval after refinement: "; display_interval(tout, a); tout << "\n";); // Find unique candidate rational in the isolating interval scoped_mpbq a_n_lower(bqm()); scoped_mpbq a_n_upper(bqm()); bqm().mul(lower(c), abs_a_n, a_n_lower); bqm().mul(upper(c), abs_a_n, a_n_upper); scoped_mpz zcandidate(qm()); bqm().floor(qm(), a_n_upper, zcandidate); scoped_mpq candidate(qm()); qm().set(candidate, zcandidate, abs_a_n); SASSERT(bqm().ge(upper(c), candidate)); // Find if candidate is an actual root if (bqm().lt(lower(c), candidate) && upm().eval_sign_at(c->m_p_sz, c->m_p, candidate) == polynomial::sign_zero) { saved_a.restore_if_too_small(); set(a, candidate); return true; } else { saved_a.restore_if_too_small(); c->m_not_rational = true; return false; } } void to_rational(numeral & a, mpq & r) { VERIFY(is_rational(a)); SASSERT(a.is_basic()); qm().set(r, basic_value(a)); } void to_rational(numeral & a, rational & r) { scoped_mpq tmp(qm()); to_rational(a, tmp); rational tmp2(tmp); r = tmp2; } unsigned degree(numeral const & a) { if (is_zero(a)) return 0; if (a.is_basic()) return 1; return a.to_algebraic()->m_p_sz - 1; } void swap(numeral & a, numeral & b) { std::swap(a.m_cell, b.m_cell); } basic_cell * mk_basic_cell(mpq & n) { if (qm().is_zero(n)) return nullptr; void * mem = static_cast(m_allocator.allocate(sizeof(basic_cell))); basic_cell * c = new (mem) basic_cell(); qm().swap(c->m_value, n); return c; } polynomial::sign sign_lower(algebraic_cell * c) const { return c->m_sign_lower == 0 ? polynomial::sign_pos : polynomial::sign_neg; } mpbq const & lower(algebraic_cell const * c) const { return c->m_interval.lower(); } mpbq const & upper(algebraic_cell const * c) const { return c->m_interval.upper(); } mpbq & lower(algebraic_cell * c) { return c->m_interval.lower(); } mpbq & upper(algebraic_cell * c) { return c->m_interval.upper(); } void update_sign_lower(algebraic_cell * c) { polynomial::sign sl = upm().eval_sign_at(c->m_p_sz, c->m_p, lower(c)); // The isolating intervals are refinable. Thus, the polynomial has opposite signs at lower and upper. SASSERT(sl != polynomial::sign_zero); SASSERT(upm().eval_sign_at(c->m_p_sz, c->m_p, upper(c)) == -sl); c->m_sign_lower = sl == polynomial::sign_neg; SASSERT(acell_inv(*c)); } // Make sure the GCD of the coefficients is one and the leading coefficient is positive void normalize_coeffs(algebraic_cell * c) { SASSERT(c->m_p_sz > 2); upm().normalize(c->m_p_sz, c->m_p); if (upm().m().is_neg(c->m_p[c->m_p_sz-1])) { upm().neg(c->m_p_sz, c->m_p); c->m_sign_lower = !(c->m_sign_lower); SASSERT(acell_inv(*c)); } } algebraic_cell * mk_algebraic_cell(unsigned sz, mpz const * p, mpbq const & lower, mpbq const & upper, bool minimal) { SASSERT(sz > 2); void * mem = static_cast(m_allocator.allocate(sizeof(algebraic_cell))); algebraic_cell * c = new (mem) algebraic_cell(); c->m_p_sz = sz; c->m_p = static_cast(m_allocator.allocate(sizeof(mpz)*sz)); for (unsigned i = 0; i < sz; i++) { new (c->m_p + i) mpz(); qm().set(c->m_p[i], p[i]); } bqim().set(c->m_interval, lower, upper); update_sign_lower(c); c->m_minimal = minimal; SASSERT(c->m_i == 0); SASSERT(c->m_not_rational == false); if (c->m_minimal) c->m_not_rational = true; normalize_coeffs(c); return c; } void set(numeral & a, mpq & n) { if (qm().is_zero(n)) { reset(a); SASSERT(is_zero(a)); return; } if (a.is_basic()) { if (is_zero(a)) a.m_cell = mk_basic_cell(n); else qm().set(a.to_basic()->m_value, n); } else { del(a); a.m_cell = mk_basic_cell(n); } } void set(numeral & a, mpq const & n) { scoped_mpq tmp(qm()); qm().set(tmp, n); set(a, tmp); } void copy_poly(algebraic_cell * c, unsigned sz, mpz const * p) { SASSERT(c->m_p == 0); SASSERT(c->m_p_sz == 0); c->m_p_sz = sz; c->m_p = static_cast(m_allocator.allocate(sizeof(mpz)*sz)); for (unsigned i = 0; i < sz; i++) { new (c->m_p + i) mpz(); qm().set(c->m_p[i], p[i]); } } void set_interval(algebraic_cell * c, mpbqi const & i) { bqim().set(c->m_interval, i); } void set_interval(algebraic_cell * c, mpbq const & l, mpbq const & u) { bqim().set(c->m_interval, l, u); } // Copy fields from source to target. // It assumes that fields target->m_p is NULL or was deleted. void copy(algebraic_cell * target, algebraic_cell const * source) { copy_poly(target, source->m_p_sz, source->m_p); set_interval(target, source->m_interval); target->m_minimal = source->m_minimal; target->m_sign_lower = source->m_sign_lower; target->m_not_rational = source->m_not_rational; target->m_i = source->m_i; SASSERT(acell_inv(*source)); SASSERT(acell_inv(*target)); } void set(numeral & a, unsigned sz, mpz const * p, mpbq const & lower, mpbq const & upper, bool minimal) { SASSERT(sz > 1); if (sz == 2) { // it is linear scoped_mpq tmp(qm()); qm().set(tmp, p[0], p[1]); qm().neg(tmp); set(a, tmp); } else { if (a.is_basic()) { del(a); a.m_cell = TAG(void*, mk_algebraic_cell(sz, p, lower, upper, minimal), ROOT); } else { SASSERT(sz > 2); algebraic_cell * c = a.to_algebraic(); del_poly(c); copy_poly(c, sz, p); set_interval(c, lower, upper); c->m_minimal = minimal; c->m_not_rational = false; if (c->m_minimal) c->m_not_rational = true; c->m_i = 0; update_sign_lower(c); normalize_coeffs(c); } SASSERT(acell_inv(*a.to_algebraic())); } TRACE("algebraic", tout << "a: "; display_root(tout, a); tout << "\n";); } void set(numeral & a, numeral const & b) { if (&a == &b) return; if (a.is_basic()) { if (b.is_basic()) { SASSERT(a.is_basic() && b.is_basic()); set(a, basic_value(b)); } else { SASSERT(a.is_basic() && !b.is_basic()); del(a); void * mem = m_allocator.allocate(sizeof(algebraic_cell)); algebraic_cell * c = new (mem) algebraic_cell(); a.m_cell = TAG(void *, c, ROOT); copy(c, b.to_algebraic()); SASSERT(acell_inv(*c)); } } else { if (b.is_basic()) { SASSERT(!a.is_basic() && b.is_basic()); del(a); set(a, basic_value(b)); } else { SASSERT(!a.is_basic() && !b.is_basic()); del_poly(a.to_algebraic()); del_interval(a.to_algebraic()); copy(a.to_algebraic(), b.to_algebraic()); SASSERT(acell_inv(*a.to_algebraic())); } } } bool factor(scoped_upoly const & up, factors & r) { if (m_factor) { return upm().factor(up, r, m_factor_params); } else { scoped_upoly & up_sqf = m_isolate_tmp3; up_sqf.reset(); upm().square_free(up.size(), up.c_ptr(), up_sqf); TRACE("algebraic", upm().display(tout, up_sqf.size(), up_sqf.c_ptr()); tout << "\n";); r.push_back(up_sqf, 1); return false; } } struct lt_proc { manager & m; lt_proc(manager & _m):m(_m) {} bool operator()(numeral const & a1, numeral const & a2) const { return m.lt(a1, a2); } }; void sort_roots(numeral_vector & r) { if (m_limit.inc()) { std::sort(r.begin(), r.end(), lt_proc(m_wrapper)); } } void isolate_roots(scoped_upoly const & up, numeral_vector & roots) { TRACE("algebraic", upm().display(tout, up); tout << "\n";); if (up.empty()) return; // ignore the zero polynomial factors & fs = m_isolate_factors; fs.reset(); bool full_fact; if (upm().has_zero_roots(up.size(), up.c_ptr())) { roots.push_back(numeral()); scoped_upoly & nz_up = m_isolate_tmp2; upm().remove_zero_roots(up.size(), up.c_ptr(), nz_up); full_fact = factor(nz_up, fs); } else { full_fact = factor(up, fs); } unsigned num_factors = fs.distinct_factors(); for (unsigned i = 0; i < num_factors; i++) { upolynomial::numeral_vector const & f = fs[i]; // polynomial f contains the non zero roots unsigned d = upm().degree(f); TRACE("algebraic", tout << "factor " << i << " degree: " << d << "\n";); if (d == 0) continue; // found all roots of f scoped_mpq r(qm()); if (d == 1) { TRACE("algebraic", tout << "linear polynomial...\n";); // f is a linear polynomial ax + b // set r <- -b/a qm().set(r, f[0]); qm().div(r, f[1], r); qm().neg(r); roots.push_back(numeral(mk_basic_cell(r))); continue; } SASSERT(m_isolate_roots.empty() && m_isolate_lowers.empty() && m_isolate_uppers.empty()); upm().sqf_isolate_roots(f.size(), f.c_ptr(), bqm(), m_isolate_roots, m_isolate_lowers, m_isolate_uppers); // collect rational/basic roots unsigned sz = m_isolate_roots.size(); TRACE("algebraic", tout << "isolated roots: " << sz << "\n";); for (unsigned i = 0; i < sz; i++) { to_mpq(qm(), m_isolate_roots[i], r); roots.push_back(numeral(mk_basic_cell(r))); } SASSERT(m_isolate_uppers.size() == m_isolate_lowers.size()); // collect non-basic roots sz = m_isolate_lowers.size(); for (unsigned i = 0; i < sz; i++) { mpbq & lower = m_isolate_lowers[i]; mpbq & upper = m_isolate_uppers[i]; if (!upm().isolating2refinable(f.size(), f.c_ptr(), bqm(), lower, upper)) { // found rational root... it is stored in lower to_mpq(qm(), lower, r); roots.push_back(numeral(mk_basic_cell(r))); } else { algebraic_cell * c = mk_algebraic_cell(f.size(), f.c_ptr(), lower, upper, full_fact); roots.push_back(numeral(c)); } } m_isolate_roots.reset(); m_isolate_lowers.reset(); m_isolate_uppers.reset(); } sort_roots(roots); } void isolate_roots(polynomial_ref const & p, numeral_vector & roots) { SASSERT(is_univariate(p)); TRACE("algebraic", tout << "isolating roots of: " << p << "\n";); if (::is_zero(p)) return; // ignore the zero polynomial scoped_upoly & up = m_isolate_tmp1; upm().to_numeral_vector(p, up); isolate_roots(up, roots); } void mk_root(scoped_upoly const & up, unsigned i, numeral & r) { // TODO: implement version that finds i-th root without isolating all roots. if (i == 0) throw algebraic_exception("invalid root object, root index must be greater than 0"); if (up.empty()) throw algebraic_exception("invalid root object, polynomial must not be the zero polynomial"); SASSERT(i != 0); scoped_numeral_vector roots(m_wrapper); isolate_roots(up, roots); unsigned num_roots = roots.size(); TRACE("algebraic", tout << "num-roots: " << num_roots << "\n"; for (unsigned i = 0; i < num_roots; i++) { display_interval(tout, roots[i]); tout << "\n"; }); if (i > num_roots) throw algebraic_exception("invalid root object, polynomial does have sufficient roots"); set(r, roots[i-1]); } void mk_root(polynomial_ref const & p, unsigned i, numeral & r) { SASSERT(i != 0); SASSERT(is_univariate(p)); TRACE("algebraic", tout << "isolating roots of: " << p << "\n";); scoped_upoly & up = m_isolate_tmp1; upm().to_numeral_vector(p, up); mk_root(up, i, r); } void mk_root(sexpr const * p, unsigned i, numeral & r) { SASSERT(i != 0); scoped_upoly & up = m_isolate_tmp1; sexpr2upolynomial(upm(), p, up); TRACE("algebraic", tout << "mk_root " << i << "\n"; upm().display(tout, up); tout << "\n";); mk_root(up, i, r); } /** \brief Make sure that if a is 0, then a.m_cell == 0 */ void normalize(numeral & a) { if (is_zero(a)) return; if (a.is_basic()) { if (qm().is_zero(a.to_basic()->m_value)) reset(a); } else { algebraic_cell * c = a.to_algebraic(); if (!upm().normalize_interval_core(c->m_p_sz, c->m_p, sign_lower(c), bqm(), lower(c), upper(c))) reset(a); SASSERT(acell_inv(*c)); } } /** \brief Return the magnitude of the given interval. The magnitude is an approximation of the size of the interval. */ int magnitude(mpbq const & l, mpbq const & u) { SASSERT(bqm().is_nonneg(l) || bqm().is_nonpos(u)); int l_k = l.k(); int u_k = u.k(); if (l_k == u_k) return bqm().magnitude_ub(l); if (bqm().is_nonneg(l)) return qm().log2(u.numerator()) - qm().log2(l.numerator()) - u_k + l_k - u_k; else return qm().mlog2(u.numerator()) - qm().mlog2(l.numerator()) - u_k + l_k - u_k; } /** \brief Return the magnitude of the isolating interval associated with the given algebraic_cell */ int magnitude(algebraic_cell * c) { return magnitude(lower(c), upper(c)); } /** \brief Refine isolating interval associated with algebraic number. The new interval will half of the size of the original one. Return TRUE, if interval was refined Return FALSE, if actual root was found. */ bool refine_core(algebraic_cell * c) { bool r = upm().refine_core(c->m_p_sz, c->m_p, sign_lower(c), bqm(), lower(c), upper(c)); SASSERT(acell_inv(*c)); return r; } /** \brief Refine isolating interval associated with algebraic number. This procedure is a noop if algebraic number is basic. This method essentially updates the field m_interval The new interval will half of the size of the original one. Remark: a root object may become basic when invoking this method, since we may find the actual rational root. This can only happen when non minimal polynomials are used to encode root objects. */ bool refine(numeral & a) { if (a.is_basic()) return false; algebraic_cell * c = a.to_algebraic(); if (refine_core(c)) { return true; } else { // root was found scoped_mpq r(qm()); to_mpq(qm(), lower(c), r); del(c); a.m_cell = mk_basic_cell(r); return false; } } bool refine(numeral & a, unsigned k) { for (unsigned i = 0; i < k; i++) if (!refine(a)) return false; return true; } bool refine_until_prec(numeral & a, unsigned prec) { if (a.is_basic()) return true; algebraic_cell * c = a.to_algebraic(); if (!upm().refine(c->m_p_sz, c->m_p, bqm(), lower(c), upper(c), prec)) { // actual root was found scoped_mpq r(qm()); to_mpq(qm(), lower(c), r); del(c); a.m_cell = mk_basic_cell(r); return false; } SASSERT(acell_inv(*c)); return true; } /** Functor for computing the polynomial resultant_y(pa(x-y), pb(y)) where pa and pb are the polynomials for algebraic cells: a and b. Remark: If alpha and beta are roots of pa and pb, then alpha + beta is a root of the new polynomial. Remark: If the argument IsAdd == false, then the functor computes resultant_y(pa(x+y), pb(y)) */ template struct mk_add_polynomial { imp & m; mk_add_polynomial(imp & _m):m(_m) {} void operator()(algebraic_cell * a, algebraic_cell * b, scoped_upoly & r) const { polynomial_ref pa_x(m.pm()); // pa(x) polynomial_ref pa_x_y(m.pm()); // pa(x-y) for addition and pa(x+y) for subtraction polynomial_ref pb_y(m.pm()); // pb(y) polynomial_ref r_x(m.pm()); // r(x) = resultant_y(pa(x-y), pb(y)) pa_x = m.pm().to_polynomial(a->m_p_sz, a->m_p, m.m_x); pb_y = m.pm().to_polynomial(b->m_p_sz, b->m_p, m.m_y); if (IsAdd) m.pm().compose_x_minus_y(pa_x, m.m_y, pa_x_y); else m.pm().compose_x_plus_y(pa_x, m.m_y, pa_x_y); m.pm().resultant(pa_x_y, pb_y, m.m_y, r_x); m.upm().to_numeral_vector(r_x, r); } }; /** Functor for computing the polynomial resultant_y(y^n * pa(x/y), pb(y)) where pa and pb are the polynomials for algebraic cells: a and b. n is degree of pa. */ struct mk_mul_polynomial { imp & m; mk_mul_polynomial(imp & _m):m(_m) {} void operator()(algebraic_cell * a, algebraic_cell * b, scoped_upoly & r) const { polynomial_ref pa_x(m.pm()); // pa(x) polynomial_ref pa_x_div_y(m.pm()); // y^n * pa(x/y) polynomial_ref pb_y(m.pm()); // pb(y) polynomial_ref r_x(m.pm()); // r(x) = resultant_y(y^n * pa(x/y), pb(y)) pa_x = m.pm().to_polynomial(a->m_p_sz, a->m_p, m.m_x); pb_y = m.pm().to_polynomial(b->m_p_sz, b->m_p, m.m_y); pa_x_div_y = m.pm().compose_x_div_y(pa_x, m.m_y); m.pm().resultant(pa_x_div_y, pb_y, m.m_y, r_x); m.upm().to_numeral_vector(r_x, r); } }; /** \brief Return the sum (interval) of the intervals of algebraic cells a and b. */ template struct add_interval_proc { imp & m; add_interval_proc(imp & _m):m(_m) {} void operator()(algebraic_cell * a, algebraic_cell * b, mpbqi & r) const { if (IsAdd) m.bqim().add(a->m_interval, b->m_interval, r); else m.bqim().sub(a->m_interval, b->m_interval, r); } }; /** \brief Return the product of the intervals of algebraic cells a and b. */ struct mul_interval_proc { imp & m; mul_interval_proc(imp & _m):m(_m) {} void operator()(algebraic_cell * a, algebraic_cell * b, mpbqi & r) const { m.bqim().mul(a->m_interval, b->m_interval, r); } }; /** \brief Functor for c <- a + b */ struct add_proc { imp & m; add_proc(imp & _m):m(_m) {} void operator()(numeral & a, numeral & b, numeral & c) const { return m.add(a, b, c); } }; /** \brief Functor for c <- a - b */ struct sub_proc { imp & m; sub_proc(imp & _m):m(_m) {} void operator()(numeral & a, numeral & b, numeral & c) const { return m.sub(a, b, c); } }; /** \brief Functor for c <- a * b */ struct mul_proc { imp & m; mul_proc(imp & _m):m(_m) {} void operator()(numeral & a, numeral & b, numeral & c) const { return m.mul(a, b, c); } }; // Save the isolating interval of an algebraic cell. struct save_intervals { imp & m_owner; numeral const & m_num; mpbqi m_old_interval; bool m_restore_invoked; // true if restore_if_too_small was invoked save_intervals(imp & o, numeral const & num): m_owner(o), m_num(num), m_restore_invoked(false) { SASSERT(!num.is_basic()); m_owner.bqim().set(m_old_interval, num.to_algebraic()->m_interval); } ~save_intervals() { if (!m_restore_invoked) restore_if_too_small(); m_owner.bqim().del(m_old_interval); } // Restore the intervals of m_cell, if its current magnitude is too small void restore_if_too_small() { m_restore_invoked = true; if (m_num.is_basic()) return; // m_num is not algebraic anymore algebraic_cell * cell = m_num.to_algebraic(); if (m_owner.magnitude(cell) < m_owner.m_min_magnitude) { // restore old interval m_owner.bqim().swap(cell->m_interval, m_old_interval); } } }; /** \brief Set c with the algebraic number associated with polynomial p and isolating interval r_i == (l, u). The isolating interval is not normalized, that is, it may contain zero. The method also requires the following (redundant) additional information: - seq: The Sturm sequence for p - lV: The Number of sign variations (in seq) at l - lU: The Number of sign variations (in seq) at u \pre p must be square free \pre r_i must be an isolating interval for p \pre seq must be the Sturm sequence for p \pre lV and uV are the sign variations (in seq) for r_i.lower() and r_i.upper() */ void set_core(numeral & c, scoped_upoly & p, mpbqi & r_i, upolynomial::scoped_upolynomial_sequence & seq, int lV, int uV, bool minimal) { TRACE("algebraic", tout << "set_core p: "; upm().display(tout, p); tout << "\n";); if (bqim().contains_zero(r_i)) { if (upm().has_zero_roots(p.size(), p.c_ptr())) { // zero is a root of p, and r_i is an isolating interval containing zero, // then c is zero reset(c); TRACE("algebraic", tout << "resetting\nresult: "; display_root(tout, c); tout << "\n";); return; } int zV = upm().sign_variations_at_zero(seq); if (lV == zV) { // root is in the second half bqim().set_lower(r_i, mpbq()); } else { SASSERT(zV == uV); // root is in the first half bqim().set_upper(r_i, mpbq()); } SASSERT(bqm().lt(r_i.lower(), r_i.upper())); } // make sure 0 is not a root of p scoped_upoly & nz_p = m_add_tmp; if (upm().has_zero_roots(p.size(), p.c_ptr())) { // remove zero root upm().remove_zero_roots(p.size(), p.c_ptr(), nz_p); } else { p.swap(nz_p); } if (!upm().isolating2refinable(nz_p.size(), nz_p.c_ptr(), bqm(), r_i.lower(), r_i.upper())) { // found actual root scoped_mpq r(qm()); to_mpq(qm(), r_i.lower(), r); set(c, r); } else { TRACE("algebraic", tout << "set_core...\n";); set(c, nz_p.size(), nz_p.c_ptr(), r_i.lower(), r_i.upper(), minimal); } } /** \brief Apply a binary operation on the given algebraic numbers. \pre !a.is_basic() and !b.is_basic() The template arguments: MkResultPoly: functor for constructing a polynomial p(x) s.t. p(u) = 0 (where u is the result of the operation). MkResultInterval: functor for computing an approximation of the resultant interval using the interval of the arguments. The functor must be "monotonic". That is, if we provide better (smaller) input intervals, it produces a better (smaller) output interval. MkBasic: functor for applying the operation if a or b become a basic cell. The numerals a and b may become basic during refinement. */ template void mk_binary(numeral & a, numeral & b, numeral & c, MkResultPoly const & mk_poly, MkResultInterval const & mk_interval, MkBasic const & mk_basic) { SASSERT(!a.is_basic()); SASSERT(!b.is_basic()); algebraic_cell * cell_a = a.to_algebraic(); algebraic_cell * cell_b = b.to_algebraic(); scoped_upoly p(upm()); scoped_upoly f(upm()); mk_poly(cell_a, cell_b, p); TRACE("anum_mk_binary", tout << "a: "; display_root(tout, a); tout << "\nb: "; display_root(tout, b); tout << "\np: "; upm().display(tout, p); tout << "\n";); factors fs(upm()); bool full_fact = factor(p, fs); unsigned num_fs = fs.distinct_factors(); scoped_ptr_vector seqs; for (unsigned i = 0; i < num_fs; i++) { TRACE("anum_mk_binary", tout << "factor " << i << "\n"; upm().display(tout, fs[i]); tout << "\n";); typename upolynomial::scoped_upolynomial_sequence * seq = alloc(typename upolynomial::scoped_upolynomial_sequence, upm()); upm().sturm_seq(fs[i].size(), fs[i].c_ptr(), *seq); seqs.push_back(seq); } SASSERT(seqs.size() == num_fs); save_intervals saved_a(*this, a); save_intervals saved_b(*this, b); scoped_mpbqi r_i(bqim()); while (true) { checkpoint(); SASSERT(!a.is_basic()); SASSERT(!b.is_basic()); mk_interval(cell_a, cell_b, r_i); unsigned num_rem = 0; // number of remaining sequences unsigned target_i = UINT_MAX; // index of sequence that is isolating int target_lV = 0, target_uV = 0; for (unsigned i = 0; i < num_fs; i++) { if (seqs[i] == nullptr) continue; // sequence was discarded because it does not contain the root. TRACE("anum_mk_binary", tout << "sequence " << i << "\n"; upm().display(tout, *(seqs[i])); tout << "\n";); int lV = upm().sign_variations_at(*(seqs[i]), r_i.lower()); int uV = upm().sign_variations_at(*(seqs[i]), r_i.upper()); int V = lV - uV; TRACE("algebraic", tout << "r_i: "; bqim().display(tout, r_i); tout << "\n"; tout << "lV: " << lV << ", uV: " << uV << "\n"; tout << "a.m_interval: "; bqim().display(tout, cell_a->m_interval); tout << "\n"; tout << "b.m_interval: "; bqim().display(tout, cell_b->m_interval); tout << "\n"; ); if (V <= 0) { // discard sequence, since factor does not contain the root seqs.set(i, nullptr); } else if (V == 1) { target_i = i; target_lV = lV; target_uV = uV; num_rem++; } else { num_rem++; } } if (num_rem == 1 && target_i != UINT_MAX) { // found isolating interval TRACE("anum_mk_binary", tout << "target_i: " << target_i << "\n";); saved_a.restore_if_too_small(); saved_b.restore_if_too_small(); upm().set(fs[target_i].size(), fs[target_i].c_ptr(), f); set_core(c, f, r_i, *(seqs[target_i]), target_lV, target_uV, full_fact); return; } if (!refine(a) || !refine(b)) { // a or b became basic SASSERT(a.is_basic() || b.is_basic()); saved_a.restore_if_too_small(); saved_a.restore_if_too_small(); return mk_basic(a, b, c); } } } template void mk_unary(numeral & a, numeral & b, MkResultPoly const & mk_poly, MkResultInterval const & mk_interval, MkBasic const & mk_basic) { SASSERT(!a.is_basic()); algebraic_cell * cell_a = a.to_algebraic(); scoped_upoly p(upm()); scoped_upoly f(upm()); mk_poly(cell_a, p); factors fs(upm()); bool full_fact = factor(p, fs); unsigned num_fs = fs.distinct_factors(); scoped_ptr_vector seqs; for (unsigned i = 0; i < num_fs; i++) { typename upolynomial::scoped_upolynomial_sequence * seq = alloc(typename upolynomial::scoped_upolynomial_sequence, upm()); upm().sturm_seq(fs[i].size(), fs[i].c_ptr(), *seq); seqs.push_back(seq); } SASSERT(seqs.size() == num_fs); save_intervals saved_a(*this, a); scoped_mpbqi r_i(bqim()); while (true) { checkpoint(); SASSERT(!a.is_basic()); mk_interval(cell_a, r_i); unsigned num_rem = 0; // number of remaining sequences unsigned target_i = UINT_MAX; // index of sequence that is isolating int target_lV = 0, target_uV = 0; for (unsigned i = 0; i < num_fs; i++) { if (seqs[i] == nullptr) continue; // sequence was discarded because it does not contain the root. int lV = upm().sign_variations_at(*(seqs[i]), r_i.lower()); int uV = upm().sign_variations_at(*(seqs[i]), r_i.upper()); int V = lV - uV; TRACE("algebraic", tout << "r_i: "; bqim().display(tout, r_i); tout << "\n"; tout << "lV: " << lV << ", uV: " << uV << "\n"; tout << "a.m_interval: "; bqim().display(tout, cell_a->m_interval); tout << "\n"; ); if (V <= 0) { // discard sequence, since factor does not contain the root seqs.set(i, nullptr); } else if (V == 1) { target_i = i; target_lV = lV; target_uV = uV; num_rem++; } else { num_rem++; } } if (num_rem == 1 && target_i != UINT_MAX) { // found isolating interval saved_a.restore_if_too_small(); upm().set(fs[target_i].size(), fs[target_i].c_ptr(), f); set_core(b, f, r_i, *(seqs[target_i]), target_lV, target_uV, full_fact); return; } if (!refine(a)) { // a became basic SASSERT(a.is_basic()); saved_a.restore_if_too_small(); return mk_basic(a, b); } } } /** Functor for computing the polynomial resultant_y(x^k - y, pa(y)) where pa is the polynomial for algebraic cell: a. k is a parameter. */ struct mk_root_polynomial { imp & m; unsigned k; mk_root_polynomial(imp & _m, unsigned _k):m(_m), k(_k) {} void operator()(algebraic_cell * a, scoped_upoly & r) const { // Let p be the polynomial associated with a. // Then, r(x) := Resultant(x^k - y, p(y), y) // is a polynomial s.t. a^{1/k} is a root of r(x). // Create r(x) polynomial_ref p_y(m.pm()); polynomial_ref xk_y(m.pm()); polynomial_ref y(m.pm()); polynomial_ref r_x(m.pm()); p_y = m.pm().to_polynomial(a->m_p_sz, a->m_p, m.m_y); y = m.pm().mk_polynomial(m.m_y); xk_y = m.pm().mk_polynomial(m.m_x, k); xk_y = xk_y - y; m.pm().resultant(xk_y, p_y, m.m_y, r_x); m.upm().to_numeral_vector(r_x, r); } }; /** \brief Return the k-th root of the interval of an algebraic cell a. */ struct root_interval_proc { imp & m; unsigned k; root_interval_proc(imp & _m, unsigned _k):m(_m), k(_k) {} void operator()(algebraic_cell * a, mpbqi & r) const { m.bqm().root_lower(m.lower(a), k, r.lower()); m.bqm().root_upper(m.upper(a), k, r.upper()); } }; /** \brief Functor for b <- a^{1/k} */ struct root_proc { imp & m; unsigned k; root_proc(imp & _m, unsigned _k):m(_m), k(_k) {} void operator()(numeral & a, numeral & b) const { return m.root(a, k, b); } }; /** Functor for computing the polynomial resultant_y(x - y^k, pa(y)) where pa is the polynomial for algebraic cell: a. k is a parameter. */ struct mk_power_polynomial { imp & m; unsigned k; mk_power_polynomial(imp & _m, unsigned _k):m(_m), k(_k) {} void operator()(algebraic_cell * a, scoped_upoly & r) const { polynomial_ref p_y(m.pm()); polynomial_ref x(m.pm()); polynomial_ref x_yk(m.pm()); polynomial_ref r_x(m.pm()); p_y = m.pm().to_polynomial(a->m_p_sz, a->m_p, m.m_y); x = m.pm().mk_polynomial(m.m_x); x_yk = m.pm().mk_polynomial(m.m_y, k); x_yk = x - x_yk; m.pm().resultant(x_yk, p_y, m.m_y, r_x); m.upm().to_numeral_vector(r_x, r); } }; /** \brief Return the ^k of the interval of an algebraic cell a. */ struct power_interval_proc { imp & m; unsigned k; power_interval_proc(imp & _m, unsigned _k):m(_m), k(_k) {} void operator()(algebraic_cell * a, mpbqi & r) const { m.bqim().power(a->m_interval, k, r); } }; /** \brief Functor for b <- a^k */ struct power_proc { imp & m; unsigned k; power_proc(imp & _m, unsigned _k):m(_m), k(_k) {} void operator()(numeral & a, numeral & b) const { return m.power(a, k, b); } }; void root_core(basic_cell * a, unsigned k, numeral & b) { SASSERT(!qm().is_zero(a->m_value)); SASSERT(k > 1); mpq & a_val = a->m_value; scoped_mpq r_a_val(qm()); if (qm().root(a_val, k, r_a_val)) { // the result is rational TRACE("root_core", tout << "r_a_val: " << r_a_val << " a_val: "; qm().display(tout, a_val); tout << "\n";); set(b, r_a_val); return; } // Let a_val be of the form n/d // create polynomial p: d*x^k - n // a_val > 0 --> (0, a_val+1) is an isolating interval // a_val < 0 --> (a_val-1, 0) is an isolating interval // Create p scoped_upoly p(upm()); p.push_back(mpz()); qm().set(p.back(), a_val.numerator()); qm().neg(p.back()); for (unsigned i = 0; i < k; i++) p.push_back(mpz()); qm().set(p.back(), a_val.denominator()); // Create isolating interval scoped_mpbq lower(bqm()); scoped_mpbq upper(bqm()); if (qm().is_neg(a_val)) { if (!bqm().to_mpbq(a_val, lower)) { // a_val is not a binary rational, lower is just an approximation. // lower == a_val.numerator() / 2^{log2(a_val.denominator()) + 1} // Thus, 2*lower <= a_val <= lower bqm().mul2(lower); // make sure lower <= a_val } bqm().sub(lower, mpz(1), lower); // make sure lower < (a_val)^{1/k} } else { if (!bqm().to_mpbq(a_val, upper)) { // a_val is not a binary rational, upper is just an approximation. // upper == a_val.numerator() / 2^{log2(a_val.denominator()) + 1} // Thus, upper <= a_val <= 2*upper bqm().mul2(upper); // make sure a_val <= upper } bqm().add(upper, mpz(1), upper); // make sure (a_val)^{1/k} < upper } SASSERT(bqm().lt(lower, upper)); TRACE("algebraic", tout << "root_core:\n"; upm().display(tout, p.size(), p.c_ptr()); tout << "\n";); // p is not necessarily a minimal polynomial. // So, we set the m_minimal flag to false. TODO: try to factor. set(b, p.size(), p.c_ptr(), lower, upper, false); } void root(numeral & a, unsigned k, numeral & b) { if (k == 0) throw algebraic_exception("0-th root is indeterminate"); if (k == 1 || is_zero(a)) { set(b, a); return; } if (is_neg(a) && k % 2 == 0) { // Remark: some computer algebra systems (e.g., Mathematica) define the // k-th root of a negative number as a complex number for any k. // We should check if our definition will not confuse users. throw algebraic_exception("even root of negative number is not real"); } if (a.is_basic()) root_core(a.to_basic(), k, b); else mk_unary(a, b, mk_root_polynomial(*this, k), root_interval_proc(*this, k), root_proc(*this, k)); } void power_core(basic_cell * a, unsigned k, numeral & b) { scoped_mpq r(qm()); qm().power(basic_value(a), k, r); set(b, r); } void power(numeral & a, unsigned k, numeral & b) { if (is_zero(a) && k == 0) throw algebraic_exception("0^0 is indeterminate"); if (k == 0) { set(b, 1); return; } if (k == 1) { set(b, a); return; } if (is_zero(a)) { reset(b); return; } if (a.is_basic()) { scoped_mpq r(qm()); qm().power(basic_value(a), k, r); set(b, r); } else { mk_unary(a, b, mk_power_polynomial(*this, k), power_interval_proc(*this, k), power_proc(*this, k)); } } void add(basic_cell * a, basic_cell * b, numeral & c) { scoped_mpq r(qm()); qm().add(basic_value(a), basic_value(b), r); set(c, r); normalize(c); } void sub(basic_cell * a, basic_cell * b, numeral & c) { scoped_mpq r(qm()); qm().sub(basic_value(a), basic_value(b), r); set(c, r); normalize(c); } template void add(algebraic_cell * a, basic_cell * b, numeral & c) { TRACE("algebraic", tout << "adding algebraic and basic cells:\n"; tout << "a: "; upm().display(tout, a->m_p_sz, a->m_p); tout << " "; bqim().display(tout, a->m_interval); tout << "\n"; tout << "b: "; qm().display(tout, b->m_value); tout << "\n";); scoped_mpq nbv(qm()); qm().set(nbv, b->m_value); if (IsAdd) qm().neg(nbv); m_add_tmp.reset(); upm().translate_q(a->m_p_sz, a->m_p, nbv, m_add_tmp); mpbqi const & i = a->m_interval; scoped_mpbq l(bqm()); scoped_mpbq u(bqm()); qm().neg(nbv); if (bqm().to_mpbq(nbv, l)) { bqm().add(i.upper(), l, u); bqm().add(i.lower(), l, l); } else { // failed to convert to binary rational scoped_mpq il(qm()); scoped_mpq iu(qm()); to_mpq(qm(), i.lower(), il); to_mpq(qm(), i.upper(), iu); TRACE("algebraic", tout << "nbv: " << nbv << "\n"; tout << "il: " << il << ", iu: " << iu << "\n";); qm().add(il, nbv, il); qm().add(iu, nbv, iu); // (il, iu) is an isolating refinable (rational) interval for the new polynomial. upm().convert_q2bq_interval(m_add_tmp.size(), m_add_tmp.c_ptr(), il, iu, bqm(), l, u); } TRACE("algebraic", upm().display(tout, m_add_tmp.size(), m_add_tmp.c_ptr()); tout << ", l: " << l << ", u: " << u << "\n"; tout << "l_sign: " << upm().eval_sign_at(m_add_tmp.size(), m_add_tmp.c_ptr(), l) << "\n"; tout << "u_sign: " << upm().eval_sign_at(m_add_tmp.size(), m_add_tmp.c_ptr(), u) << "\n"; ); set(c, m_add_tmp.size(), m_add_tmp.c_ptr(), l, u, a->m_minimal /* minimality is preserved */); normalize(c); } void add(numeral & a, numeral & b, numeral & c) { if (is_zero(a)) { set(c, b); return; } if (is_zero(b)) { set(c, a); return; } if (a.is_basic()) { if (b.is_basic()) add(a.to_basic(), b.to_basic(), c); else add(b.to_algebraic(), a.to_basic(), c); } else { if (b.is_basic()) add(a.to_algebraic(), b.to_basic(), c); else mk_binary(a, b, c, mk_add_polynomial(*this), add_interval_proc(*this), add_proc(*this)); } } void sub(numeral & a, numeral & b, numeral & c) { if (is_zero(a)) { set(c, b); neg(c); return; } if (is_zero(b)) { set(c, a); return; } if (a.is_basic()) { if (b.is_basic()) sub(a.to_basic(), b.to_basic(), c); else { // c <- b - a add(b.to_algebraic(), a.to_basic(), c); // c <- -c = a - b neg(c); } } else { if (b.is_basic()) add(a.to_algebraic(), b.to_basic(), c); else mk_binary(a, b, c, mk_add_polynomial(*this), add_interval_proc(*this), sub_proc(*this)); } } void mul(basic_cell * a, basic_cell * b, numeral & c) { scoped_mpq r(qm()); qm().mul(basic_value(a), basic_value(b), r); set(c, r); normalize(c); } void mul(algebraic_cell * a, basic_cell * b, numeral & c) { TRACE("algebraic", tout << "mult algebraic and basic cells:\n"; tout << "a: "; upm().display(tout, a->m_p_sz, a->m_p); tout << " "; bqim().display(tout, a->m_interval); tout << "\n"; tout << "b: "; qm().display(tout, b->m_value); tout << "\n";); SASSERT(upm().eval_sign_at(a->m_p_sz, a->m_p, lower(a)) == -upm().eval_sign_at(a->m_p_sz, a->m_p, upper(a))); scoped_mpq nbv(qm()); qm().set(nbv, b->m_value); qm().inv(nbv); scoped_upoly & mulp = m_add_tmp; upm().set(a->m_p_sz, a->m_p, mulp); upm().compose_p_q_x(mulp.size(), mulp.c_ptr(), nbv); mpbqi const & i = a->m_interval; scoped_mpbq l(bqm()); scoped_mpbq u(bqm()); qm().inv(nbv); bool is_neg = qm().is_neg(nbv); if (bqm().to_mpbq(nbv, l)) { bqm().mul(i.upper(), l, u); bqm().mul(i.lower(), l, l); if (is_neg) bqm().swap(l, u); } else { // failed to convert to binary rational scoped_mpq il(qm()); scoped_mpq iu(qm()); to_mpq(qm(), i.lower(), il); to_mpq(qm(), i.upper(), iu); TRACE("algebraic", tout << "nbv: " << nbv << "\n"; tout << "il: " << il << ", iu: " << iu << "\n";); qm().mul(il, nbv, il); qm().mul(iu, nbv, iu); if (is_neg) qm().swap(il, iu); // (il, iu) is an isolating refinable (rational) interval for the new polynomial. upm().convert_q2bq_interval(mulp.size(), mulp.c_ptr(), il, iu, bqm(), l, u); } TRACE("algebraic", upm().display(tout, mulp.size(), mulp.c_ptr()); tout << ", l: " << l << ", u: " << u << "\n"; tout << "l_sign: " << upm().eval_sign_at(mulp.size(), mulp.c_ptr(), l) << "\n"; tout << "u_sign: " << upm().eval_sign_at(mulp.size(), mulp.c_ptr(), u) << "\n"; ); set(c, mulp.size(), mulp.c_ptr(), l, u, a->m_minimal /* minimality is preserved */); normalize(c); } void mul(numeral & a, numeral & b, numeral & c) { if (is_zero(a) || is_zero(b)) { reset(c); return; } if (a.is_basic()) { if (b.is_basic()) mul(a.to_basic(), b.to_basic(), c); else mul(b.to_algebraic(), a.to_basic(), c); } else { if (b.is_basic()) mul(a.to_algebraic(), b.to_basic(), c); else mk_binary(a, b, c, mk_mul_polynomial(*this), mul_interval_proc(*this), mul_proc(*this)); } } void neg(numeral & a) { if (is_zero(a)) return; if (a.is_basic()) { qm().neg(a.to_basic()->m_value); } else { algebraic_cell * c = a.to_algebraic(); upm().p_minus_x(c->m_p_sz, c->m_p); bqim().neg(c->m_interval); update_sign_lower(c); SASSERT(acell_inv(*c)); } } /** Make sure lower != 0 and upper != 0 if a is non-basic algebraic number. */ void refine_nz_bound(numeral & a) { if (a.is_basic()) return; algebraic_cell * cell_a = a.to_algebraic(); SASSERT(acell_inv(*cell_a)); mpbq & _lower = cell_a->m_interval.lower(); mpbq & _upper = cell_a->m_interval.upper(); if (!bqm().is_zero(_lower) && !bqm().is_zero(_upper)) return; auto sign_l = sign_lower(cell_a); SASSERT(!polynomial::is_zero(sign_l)); auto sign_u = -sign_l; #define REFINE_LOOP(BOUND, TARGET_SIGN) \ while (true) { \ bqm().div2(BOUND); \ polynomial::sign new_sign = upm().eval_sign_at(cell_a->m_p_sz, cell_a->m_p, BOUND); \ if (new_sign == polynomial::sign_zero) { \ /* found actual root */ \ scoped_mpq r(qm()); \ to_mpq(qm(), BOUND, r); \ set(a, r); \ break; \ } \ if (new_sign == TARGET_SIGN) { \ break; \ } \ } if (bqm().is_zero(_lower)) { bqm().set(_lower, _upper); REFINE_LOOP(_lower, sign_l); } else { SASSERT(bqm().is_zero(_upper)); bqm().set(_upper, _lower); REFINE_LOOP(_upper, sign_u); } SASSERT(acell_inv(*cell_a)); } void inv(numeral & a) { if (is_zero(a)) { UNREACHABLE(); throw algebraic_exception("division by zero"); } refine_nz_bound(a); if (a.is_basic()) { qm().inv(a.to_basic()->m_value); } else { TRACE("algebraic_bug", tout << "before inv: "; display_root(tout, a); tout << "\n"; display_interval(tout, a); tout << "\n";); algebraic_cell * cell_a = a.to_algebraic(); upm().p_1_div_x(cell_a->m_p_sz, cell_a->m_p); // convert binary rational bounds into rational bounds scoped_mpq inv_lower(qm()), inv_upper(qm()); to_mpq(qm(), lower(cell_a), inv_lower); to_mpq(qm(), upper(cell_a), inv_upper); // (1/upper, 1/lower) is an isolating interval for the new polynomial qm().inv(inv_lower); qm().inv(inv_upper); qm().swap(inv_lower, inv_upper); TRACE("algebraic_bug", tout << "inv new_bounds: " << qm().to_string(inv_lower) << ", " << qm().to_string(inv_upper) << "\n";); // convert isolating interval back as a binary rational bound upm().convert_q2bq_interval(cell_a->m_p_sz, cell_a->m_p, inv_lower, inv_upper, bqm(), lower(cell_a), upper(cell_a)); TRACE("algebraic_bug", tout << "after inv: "; display_root(tout, a); tout << "\n"; display_interval(tout, a); tout << "\n";); update_sign_lower(cell_a); SASSERT(acell_inv(*cell_a)); } } void div(numeral & a, numeral & b, numeral & c) { if (is_zero(b)) { UNREACHABLE(); throw algebraic_exception("division by zero"); } // div is not used by the nonlinear procedure. // I implemented just to make sure that all field operations are available in the algebraic number module. // It is also useful to allow users to evaluate expressions containing algebraic numbers. // // We can avoid computing invb, by having a procedure similar to mul // that uses // Resultant(pa(xy), pb(y), y) instead of // Resultant(y^n * pa(x/y), pb(y), y) // scoped_anum invb(m_wrapper); set(invb, b); inv(invb); mul(a, invb, c); } // Todo: move to MPQ int compare(mpq const & a, mpq const & b) { if (qm().eq(a, b)) return 0; return qm().lt(a, b) ? -1 : 1; } /** Comparing algebraic_cells with rationals Given an algebraic cell c with isolating interval (l, u) for p and a rational b Then, u <= b implies c < b b <= l implies c > b Otherwise, l < b < u, and p(b) < 0 --> If p(l) < 0 then c > b else c < b p(b) == 0 --> c = b p(b) > 0 --> if p(l) < 0 then c < b else c > b We can simplify the rules above as: p(b) == 0 then c == b (p(b) < 0) == (p(l) < 0) then c > b else c < b */ int compare(algebraic_cell * c, mpq const & b) { mpbq const & l = lower(c); mpbq const & u = upper(c); if (bqm().le(u, b)) return -1; if (bqm().ge(l, b)) return 1; // b is in the isolating interval (l, u) auto sign_b = upm().eval_sign_at(c->m_p_sz, c->m_p, b); if (sign_b == polynomial::sign_zero) return 0; return sign_b == sign_lower(c) ? 1 : -1; } // Return true if the polynomials of cell_a and cell_b are the same. bool compare_p(algebraic_cell const * cell_a, algebraic_cell const * cell_b) { return upm().eq(cell_a->m_p_sz, cell_a->m_p, cell_b->m_p_sz, cell_b->m_p); } int compare_core(numeral & a, numeral & b) { SASSERT(!a.is_basic() && !b.is_basic()); algebraic_cell * cell_a = a.to_algebraic(); algebraic_cell * cell_b = b.to_algebraic(); mpbq const & a_lower = lower(cell_a); mpbq const & a_upper = upper(cell_a); mpbq const & b_lower = lower(cell_b); mpbq const & b_upper = upper(cell_b); #define COMPARE_INTERVAL() \ if (bqm().le(a_upper, b_lower)) { \ m_compare_cheap++; \ return -1; \ } \ if (bqm().ge(a_lower, b_upper)) { \ m_compare_cheap++; \ return 1; \ } COMPARE_INTERVAL(); // if cell_a and cell_b, contain the same polynomial, // and the intervals are overlapping, then they are // the same root. if (compare_p(cell_a, cell_b)) { m_compare_poly_eq++; return 0; } TRACE("algebraic", tout << "comparing\n"; tout << "a: "; upm().display(tout, cell_a->m_p_sz, cell_a->m_p); tout << "\n"; bqim().display(tout, cell_a->m_interval); tout << "\ncell_a->m_minimal: " << cell_a->m_minimal << "\n"; tout << "b: "; upm().display(tout, cell_b->m_p_sz, cell_b->m_p); tout << "\n"; bqim().display(tout, cell_b->m_interval); tout << "\ncell_b->m_minimal: " << cell_b->m_minimal << "\n";); if (cell_a->m_minimal && cell_b->m_minimal) { // Minimal polynomial special case. // This branch is only executed when polynomial // factorization is turned on. // If a and b are defined by minimal distinct polynomials, // then they MUST BE DIFFERENT. // Thus, if we keep refining the interval of a and b, // eventually they will not overlap while (m_limit.inc()) { refine(a); refine(b); m_compare_refine++; // refine can't reduce a and b to rationals, // since the polynomial is minimal and it is not linear. // So, the roots are NOT rational. SASSERT(!a.is_basic()); SASSERT(!b.is_basic()); COMPARE_INTERVAL(); } } 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); int target_m = std::max(m_min_magnitude, std::min(a_m, b_m)); if (b_m > target_m) { if (!refine(b, b_m - target_m)) return compare(a, b); m_compare_refine += b_m - target_m; COMPARE_INTERVAL(); } if (a_m > target_m) { if (!refine(a, a_m - target_m)) return compare(a, b); m_compare_refine += a_m - target_m; COMPARE_INTERVAL(); } if (target_m > m_min_magnitude) { int num_refinements = target_m - m_min_magnitude; for (int i = 0; i < num_refinements; i++) { if (!refine(a) || !refine(b)) return compare(a, b); m_compare_refine++; COMPARE_INTERVAL(); } } // EXPENSIVE CASE // Let seq be the Sturm-Tarski sequence for // p_a, p_a' * p_b // Let s_l be the number of sign variations at a_lower. // Let s_u be the number of sign variations at a_upper. // By Sturm-Tarski Theorem, we have that // V = s_l - s_u = #(p_b(r) > 0) - #(p_b(r) < 0) at roots r of p_a // Since there is only one root of p_a in (a_lower, b_lower), // we are evaluating the sign of p_b at a. // That is V is the sign of p_b at a. // // We have // V < 0 -> p_b(a) < 0 -> if p_b(b_lower) < 0 then b > a else b < a // V == 0 -> p_b(a) == 0 -> a = b // V > 0 -> p_b(a) > 0 -> if p_b(b_lower) > 0 then b > a else b < a // Simplifying we have: // 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); int V = upm().sign_variations_at(seq, a_lower) - upm().sign_variations_at(seq, a_upper); TRACE("algebraic", tout << "comparing using sturm\n"; display_interval(tout, a); tout << "\n"; display_interval(tout, b); tout << "\n"; tout << "V: " << V << ", sign_lower(a): " << sign_lower(cell_a) << ", sign_lower(b): " << sign_lower(cell_b) << "\n";); if (V == 0) return 0; if ((V < 0) == (sign_lower(cell_b) < 0)) return -1; else return 1; // Here is an unexplored option for comparing numbers. // // The isolating intervals of a and b are still overlapping // Then we compute // r(x) = Resultant(x - y1 + y2, p1(y1), p2(y2)) // where p1(y1) and p2(y2) are the polynomials defining a and b. // Remarks: // 1) The resultant r(x) must not be the zero polynomial, // since the polynomial x - y1 + y2 does not vanish in any of the roots of p1(y1) and p2(y2) // // 2) By resultant calculus, If alpha, beta1, beta2 are roots of x - y1 + y2, p1(y1), p2(y2) // then alpha is a root of r(x). // Thus, we have that a - b is a root of r(x) // // 3) If 0 is not a root of r(x), then a != b (by remark 2) // // 4) Let (l1, u1) and (l2, u2) be the intervals of a and b. // Then, a - b must be in (l1 - u2, u1 - l2) // // 5) Assume a != b, then if we keep refining the isolating intervals for a and b, // then eventually, (l1, u1) and (l2, u2) will not overlap. // Thus, if 0 is not a root of r(x), we can keep refining until // the intervals do not overlap. // // 6) If 0 is a root of r(x), we have two possibilities: // a) Isolate roots of r(x) in the interval (l1 - u2, u1 - l2), // and then keep refining (l1, u1) and (l2, u2) until they // (l1 - u2, u1 - l2) "convers" only one root. // // b) Compute the sturm sequence for r(x), // keep refining the (l1, u1) and (l2, u2) until // (l1 - u2, u1 - l2) contains only one root of r(x) } int compare(numeral & a, numeral & b) { TRACE("algebraic", tout << "comparing: "; display_interval(tout, a); tout << " "; display_interval(tout, b); tout << "\n";); if (a.is_basic()) { if (b.is_basic()) return compare(basic_value(a), basic_value(b)); else return -compare(b.to_algebraic(), basic_value(a)); } else { if (b.is_basic()) return compare(a.to_algebraic(), basic_value(b)); else return compare_core(a, b); } } bool eq(numeral & a, numeral & b) { return compare(a, b) == 0; } bool eq(numeral & a, mpq const & b) { if (a.is_basic()) return qm().eq(basic_value(a), b); else return compare(a.to_algebraic(), b) == 0; } bool lt(numeral & a, numeral & b) { return compare(a, b) < 0; } bool lt(numeral & a, mpq const & b) { if (a.is_basic()) return qm().lt(basic_value(a), b); else return compare(a.to_algebraic(), b) < 0; } bool gt(numeral & a, mpq const & b) { if (a.is_basic()) return qm().gt(basic_value(a), b); else return compare(a.to_algebraic(), b) > 0; } void get_polynomial(numeral const & a, svector & r) { if (a.is_basic()) { r.reserve(2); if (is_zero(a)) { qm().set(r[0], 0); qm().set(r[1], 1); } else { mpq const & v = basic_value(a); qm().set(r[0], v.numerator()); qm().set(r[1], v.denominator()); qm().neg(r[0]); } upm().set_size(2, r); } else { algebraic_cell * c = a.to_algebraic(); upm().set(c->m_p_sz, c->m_p, r); } } /** \brief "Optimistic" mapping: it assumes all variables are mapped to basic_values (rationals). Throws an exception if that is not the case. */ struct opt_var2basic : public polynomial::var2mpq { struct failed {}; imp & m_imp; polynomial::var2anum const & m_x2v; opt_var2basic(imp & i, polynomial::var2anum const & x2v):m_imp(i), m_x2v(x2v) {} unsynch_mpq_manager & m() const override { return m_imp.qm(); } bool contains(polynomial::var x) const override { return m_x2v.contains(x); } mpq const & operator()(polynomial::var x) const override { anum const & v = m_x2v(x); if (!v.is_basic()) throw failed(); return m_imp.basic_value(v); } }; /** \brief Reduced mapping which contains only the rational values */ struct var2basic : public polynomial::var2mpq { imp & m_imp; polynomial::var2anum const & m_x2v; var2basic(imp & i, polynomial::var2anum const & x2v):m_imp(i), m_x2v(x2v) {} unsynch_mpq_manager & m() const override { return m_imp.qm(); } bool contains(polynomial::var x) const override { return m_x2v.contains(x) && m_x2v(x).is_basic(); } mpq const & operator()(polynomial::var x) const override { anum const & v = m_x2v(x); SASSERT(v.is_basic()); TRACE("var2basic", tout << "getting value of x" << x << " -> " << m().to_string(m_imp.basic_value(v)) << "\n";); return m_imp.basic_value(v); } }; /** \brief Reduced mapping which contains only the non-basic values as intervals */ struct var2interval : public polynomial::var2mpbqi { imp & m_imp; polynomial::var2anum const & m_x2v; var2interval(imp & i, polynomial::var2anum const & x2v):m_imp(i), m_x2v(x2v) {} mpbqi_manager & m() const override { return m_imp.bqim(); } bool contains(polynomial::var x) const override { return m_x2v.contains(x) && !m_x2v(x).is_basic(); } mpbqi const & operator()(polynomial::var x) const override { anum const & v = m_x2v(x); SASSERT(!v.is_basic()); return v.to_algebraic()->m_interval; } }; polynomial::var_vector m_eval_sign_vars; polynomial::sign eval_sign_at(polynomial_ref const & p, polynomial::var2anum const & x2v) { polynomial::manager & ext_pm = p.m(); TRACE("anum_eval_sign", tout << "evaluating sign of: " << p << "\n";); while (true) { bool restart = false; // Optimistic: maybe x2v contains only rational values try { opt_var2basic x2v_basic(*this, x2v); scoped_mpq r(qm()); ext_pm.eval(p, x2v_basic, r); TRACE("anum_eval_sign", tout << "all variables are assigned to rationals, value of p: " << r << "\n";); return polynomial::to_sign(qm().sign(r)); } catch (const opt_var2basic::failed &) { // continue } // Eliminate rational values from p polynomial_ref p_prime(ext_pm); var2basic x2v_basic(*this, x2v); p_prime = ext_pm.substitute(p, x2v_basic); TRACE("anum_eval_sign", tout << "p after eliminating rationals: " << p_prime << "\n";); if (ext_pm.is_zero(p_prime)) { // polynomial vanished after substituting rational values. return polynomial::sign_zero; } if (is_const(p_prime)) { // polynomial became the constant polynomial after substitution. SASSERT(size(p_prime) == 1); return polynomial::to_sign(ext_pm.m().sign(ext_pm.coeff(p_prime, 0))); } // Try to find sign using intervals polynomial::var_vector & xs = m_eval_sign_vars; xs.reset(); ext_pm.vars(p_prime, xs); SASSERT(!xs.empty()); var2interval x2v_interval(*this, x2v); scoped_mpbqi ri(bqim()); while (true) { checkpoint(); ext_pm.eval(p_prime, x2v_interval, ri); TRACE("anum_eval_sign", tout << "evaluating using intervals: " << ri << "\n";); if (!bqim().contains_zero(ri)) { return bqim().is_pos(ri) ? polynomial::sign_pos : polynomial::sign_neg; } // refine intervals if magnitude > m_min_magnitude bool refined = false; for (unsigned i = 0; i < xs.size(); i++) { polynomial::var x = xs[i]; SASSERT(x2v.contains(x)); anum const & v = x2v(x); SASSERT(!v.is_basic()); algebraic_cell * c = v.to_algebraic(); int m = magnitude(c); if (m > m_min_magnitude || (m_zero_accuracy > 0 && m > m_zero_accuracy)) { if (!refine(const_cast(v))) { // v became a basic value restart = true; break; } TRACE("anum_eval_sign", tout << "refined algebraic interval\n";); SASSERT(!v.is_basic()); refined = true; } } if (!refined || restart) { // Stop if no interval was refined OR some algebraic cell became basic break; } } if (restart) { // Some non-basic value became basic. // So, restarting the whole process TRACE("anum_eval_sign", tout << "restarting some algebraic_cell became basic\n";); continue; } // At this point, we are almost sure that p is zero at x2n // That is, rin is probably a very small interval that contains zero. // Remark: m_zero_accuracy == 0 means use precise computation. if (m_zero_accuracy > 0) { // assuming the value is 0, since the result is in (-1/2^k, 1/2^k), where m_zero_accuracy = k return polynomial::sign_zero; } #if 0 // Evaluating sign using algebraic arithmetic scoped_anum ra(m_wrapper); ext_pm.eval(p_prime, x2v, ra); TRACE("anum_eval_sign", tout << "value of p as algebraic number " << ra << "\n";); if (is_zero(ra)) return 0; return is_pos(ra) ? 1 : -1; #else // Evaluating the sign using Resultants // Basic idea: // We want to evaluate the sign of // p(x_1, ..., x_n) // at x_1 -> v_1, ..., x_n -> v_n // // Let v be p(v_1, ..., v_n). // We want to know the sign of v. // // Assume v_i's are defined by the polynomials q_i(x_i) // Then, we have that // the polynomials // y - p(x_1, ..., x_n), q_1(x_1), ..., q_n(x_n) // are zero at y -> v, x_1 -> v_1, ..., x_n -> v_n // // Thus, by resultant theory, v is also a root of // R(y) = Resultant(p(x_1, ..., x_n), q_1(x_1), ..., q_n(x_n)) // Remark: R(y) is not the zero polynomial, since // the coefficient of y in the polynomial y - p(x_1, ..., x_n) // is (the constant polynomial) one. // // Now, let L be a lower bound on the nonzero roots of R(y). // Thus, any root alpha of R(y) is zero or |alpha| > L // // Therefore, we have that |v| > L // Now, using L, we can keep refining the interval ri which contains v. // Eventually, ri will not contain zero (and consequently v != 0), // or ri is in (-L, L), and consequently v = 0. polynomial::var y = get_max_var(xs) + 1; ensure_num_vars(y+1); // we create all polynomials in the local polynomial manager because we need an extra variable y polynomial_ref yp(pm()); yp = pm().mk_polynomial(y); polynomial_ref p_prime_aux(pm()); p_prime_aux = convert(ext_pm, p_prime, pm()); polynomial_ref R(pm()); R = yp - p_prime_aux; // compute the resultants polynomial_ref q_i(pm()); std::stable_sort(xs.begin(), xs.end(), var_degree_lt(*this, x2v)); for (unsigned i = 0; i < xs.size(); i++) { checkpoint(); polynomial::var x_i = xs[i]; SASSERT(x2v.contains(x_i)); anum const & v_i = x2v(x_i); SASSERT(!v_i.is_basic()); algebraic_cell * c = v_i.to_algebraic(); q_i = pm().to_polynomial(c->m_p_sz, c->m_p, x_i); pm().resultant(R, q_i, x_i, R); SASSERT(!pm().is_zero(R)); } SASSERT(pm().is_univariate(R)); scoped_upoly & _R = m_eval_sign_tmp; upm().to_numeral_vector(R, _R); unsigned k = upm().nonzero_root_lower_bound(_R.size(), _R.c_ptr()); TRACE("anum_eval_sign", tout << "R: " << R << "\nk: " << k << "\nri: "<< ri << "\n";); scoped_mpbq mL(bqm()), L(bqm()); bqm().set(mL, -1); bqm().set(L, 1); bqm().div2k(mL, k); bqm().div2k(L, k); if (bqm().lt(mL, ri.lower()) && bqm().lt(ri.upper(), L)) return polynomial::sign_zero; // keep refining ri until ri is inside (-L, L) or // ri does not contain zero. // The invervals (for the values of the variables in xs) are going to get too small. // So, we save them before refining... scoped_ptr_vector saved_intervals; for (unsigned i = 0; i < xs.size(); i++) { polynomial::var x_i = xs[i]; SASSERT(x2v.contains(x_i)); anum const & v_i = x2v(x_i); SASSERT(!v_i.is_basic()); saved_intervals.push_back(alloc(save_intervals, *this, v_i)); } // Actual refinement loop restart = false; while (!restart) { checkpoint(); ext_pm.eval(p_prime, x2v_interval, ri); TRACE("anum_eval_sign", tout << "evaluating using intervals: " << ri << "\n"; tout << "zero lower bound is: " << L << "\n";); if (!bqim().contains_zero(ri)) { return bqim().is_pos(ri) ? polynomial::sign_pos : polynomial::sign_neg; } if (bqm().lt(mL, ri.lower()) && bqm().lt(ri.upper(), L)) return polynomial::sign_zero; for (auto x : xs) { SASSERT(x2v.contains(x)); anum const & v = x2v(x); SASSERT(!v.is_basic()); if (!refine(const_cast(v))) { // v became a basic value restart = true; break; } TRACE("anum_eval_sign", tout << "refined algebraic interval\n";); SASSERT(!v.is_basic()); } } #endif } } // Functor used to sort variables by the degree of the polynomial used to represent their value. struct var_degree_lt { imp & m_imp; polynomial::var2anum const & m_x2v; var_degree_lt(imp & i, polynomial::var2anum const & x2v): m_imp(i), m_x2v(x2v) { } unsigned degree(polynomial::var x) const { if (!m_x2v.contains(x)) return UINT_MAX; return m_imp.degree(m_x2v(x)); } bool operator()(polynomial::var x1, polynomial::var x2) const { return degree(x1) < degree(x2); } }; // Add entry x->v to existing mapping struct ext_var2num : public polynomial::var2anum { manager & m_am; polynomial::var2anum const & m_x2v; polynomial::var m_x; anum const & m_v; ext_var2num(manager & am, polynomial::var2anum const & x2v, polynomial::var x, anum const & v): m_am(am), m_x2v(x2v), m_x(x), m_v(v) { } manager & m() const override { return m_am; } bool contains(polynomial::var x) const override { return x == m_x || m_x2v.contains(x); } anum const & operator()(polynomial::var x) const override { if (x == m_x) return m_v; else return m_x2v(x); } }; // Remove from roots any solution r such that p does not evaluate to 0 at x2v extended with x->r. void filter_roots(polynomial_ref const & p, polynomial::var2anum const & x2v, polynomial::var x, numeral_vector & roots) { TRACE("isolate_roots", tout << "before filtering roots, x: x" << x << "\n"; for (unsigned i = 0; i < roots.size(); i++) { display_root(tout, roots[i]); tout << "\n"; }); unsigned sz = roots.size(); unsigned j = 0; for (unsigned i = 0; i < sz; i++) { checkpoint(); ext_var2num ext_x2v(m_wrapper, x2v, x, roots[i]); TRACE("isolate_roots", tout << "filter_roots i: " << i << ", ext_x2v: x" << x << " -> "; display_root(tout, roots[i]); tout << "\n";); polynomial::sign sign = eval_sign_at(p, ext_x2v); TRACE("isolate_roots", tout << "filter_roots i: " << i << ", result sign: " << sign << "\n";); if (sign != 0) continue; if (i != j) set(roots[j], roots[i]); j++; } for (unsigned i = j; i < sz; i++) del(roots[i]); roots.shrink(j); TRACE("isolate_roots", tout << "after filtering roots:\n"; for (unsigned i = 0; i < roots.size(); i++) { display_root(tout, roots[i]); tout << "\n"; }); } // Return the maximal variable in xs. static polynomial::var get_max_var(polynomial::var_vector const & xs) { SASSERT(!xs.empty()); polynomial::var x = xs[0]; for (unsigned i = 1; i < xs.size(); i++) { if (xs[i] > x) x = xs[i]; } return x; } // Ensure that local polynomial manager has at least sz vars void ensure_num_vars(unsigned sz) { while (sz > pm().num_vars()) pm().mk_var(); SASSERT(pm().num_vars() >= sz); } polynomial::var_vector m_isolate_roots_vars; void isolate_roots(polynomial_ref const & p, polynomial::var2anum const & x2v, numeral_vector & roots, bool nested_call = false) { TRACE("isolate_roots", tout << "isolating roots of: " << p << "\n";); SASSERT(roots.empty()); polynomial::manager & ext_pm = p.m(); if (ext_pm.is_zero(p) || ext_pm.is_const(p)) { TRACE("isolate_roots", tout << "p is zero or the constant polynomial\n";); return; } if (ext_pm.is_univariate(p)) { TRACE("isolate_roots", tout << "p is univariate, using univariate procedure\n";); isolate_roots(p, roots); return; } // eliminate rationals polynomial_ref p_prime(ext_pm); var2basic x2v_basic(*this, x2v); p_prime = ext_pm.substitute(p, x2v_basic); TRACE("isolate_roots", tout << "p after applying (rational fragment of) x2v:\n" << p_prime << "\n";); if (ext_pm.is_zero(p_prime) || ext_pm.is_const(p_prime)) { TRACE("isolate_roots", tout << "p is zero or the constant polynomial after applying (rational fragment of) x2v\n";); return; } if (ext_pm.is_univariate(p_prime)) { polynomial::var x = ext_pm.max_var(p_prime); if (x2v.contains(x)) { // The remaining variable is assigned, the actual unassigned variable vanished when we replaced rational values. // So, the polynomial does not have any roots return; } TRACE("isolate_roots", tout << "p is univariate after applying (rational fragment of) x2v... using univariate procedure\n";); isolate_roots(p_prime, roots); return; } polynomial::var_vector & xs = m_isolate_roots_vars; xs.reset(); ext_pm.vars(p_prime, xs); SASSERT(xs.size() > 1); // sort variables by the degree of the values std::stable_sort(xs.begin(), xs.end(), var_degree_lt(*this, x2v)); TRACE("isolate_roots", tout << "there are " << (xs.size() - 1) << " variables assigned to nonbasic numbers...\n";); // last variables is the one not assigned by x2v, or the unassigned variable vanished polynomial::var x = xs.back(); if (x2v.contains(x)) { // all remaining variables are assigned. // the unassigned variable vanished when we replaced the rational values. DEBUG_CODE({ for (unsigned i = 0; i < xs.size(); i++) { SASSERT(x2v.contains(xs[i])); } }); return; } // construct univariate polynomial q which contains all roots of p_prime at x2v. polynomial_ref q(ext_pm); q = p_prime; polynomial_ref p_y(ext_pm); for (unsigned i = 0; i < xs.size() - 1; i++) { checkpoint(); polynomial::var y = xs[i]; SASSERT(x2v.contains(y)); anum const & v = x2v(y); SASSERT(!v.is_basic()); algebraic_cell * c = v.to_algebraic(); p_y = ext_pm.to_polynomial(c->m_p_sz, c->m_p, y); ext_pm.resultant(q, p_y, y, q); TRACE("isolate_roots", tout << "resultant loop i: " << i << ", y: x" << y << "\np_y: " << p_y << "\n"; tout << "q: " << q << "\n";); if (ext_pm.is_zero(q)) { SASSERT(!nested_call); break; } } if (ext_pm.is_zero(q)) { TRACE("isolate_roots", tout << "q vanished\n";); // q may vanish at some of the other roots of the polynomial defining the values. // To decide if p_prime vanishes at x2v or not, we start evaluating each coefficient of p_prime at x2v // until we find one that is not zero at x2v. // In the process we will copy p_prime to the local polynomial manager, since we will need to create // an auxiliary variable. SASSERT(!nested_call); unsigned n = ext_pm.degree(p_prime, x); SASSERT(n > 0); if (n == 1) { // p_prime is linear on p, so we just evaluate the coefficients... TRACE("isolate_roots", tout << "p is linear after applying (rational fragment) of x2v\n";); polynomial_ref c0(ext_pm); polynomial_ref c1(ext_pm); c0 = ext_pm.coeff(p_prime, x, 0); c1 = ext_pm.coeff(p_prime, x, 1); scoped_anum a0(m_wrapper); scoped_anum a1(m_wrapper); ext_pm.eval(c0, x2v, a0); ext_pm.eval(c1, x2v, a1); // the root must be - a0/a1 if a1 != 0 if (is_zero(a1)) { TRACE("isolate_roots", tout << "coefficient of degree 1 vanished, so p does not have roots at x2v\n";); // p_prime does not have any root return; } roots.push_back(anum()); div(a0, a1, roots[0]); neg(roots[0]); TRACE("isolate_roots", tout << "after trivial solving p has only one root:\n"; display_root(tout, roots[0]); tout << "\n";); } else { polynomial_ref c(ext_pm); scoped_anum a(m_wrapper); int i = n; for (; i >= 1; i--) { c = ext_pm.coeff(p_prime, x, i); ext_pm.eval(c, x2v, a); if (!is_zero(a)) break; } if (i == 0) { // all coefficients of x vanished, so // the polynomial has no roots TRACE("isolate_roots", tout << "all coefficients vanished... polynomial does not have roots\n";); return; } SASSERT(!is_zero(a)); polynomial::var z = get_max_var(xs) + 1; ensure_num_vars(z+1); // create polynomial q2 in the local manager // z * x^i + c_{i-1} * x^{i-1} + ... + c_1 * x + c_0 // where c's are the coefficients of p_prime. // Then we invoke isolate_roots with q2 and x2v extended with z->a. // The resultant will not vanish again because // 0 is not a root of the polynomial defining a. polynomial_ref q2(pm()); polynomial_ref z_p(pm()); // z poly polynomial_ref xi_p(pm()); // x^i poly polynomial_ref zxi_p(pm()); // z*x^i SASSERT(i >= 1); q2 = convert(ext_pm, p_prime, pm(), x, static_cast(i-1)); xi_p = pm().mk_polynomial(x, i); z_p = pm().mk_polynomial(z); q2 = z_p*xi_p + q2; TRACE("isolate_roots", tout << "invoking isolate_roots with q2:\n" << q2 << "\n"; tout << "z: x" << z << " -> "; display_root(tout, a); tout << "\n";); // extend x2p with z->a ext_var2num ext_x2v(m_wrapper, x2v, z, a); isolate_roots(q2, ext_x2v, roots, true /* nested call */); } } else if (ext_pm.is_const(q)) { // q does not have any roots, so p_prime also does not have roots at x2v. TRACE("isolate_roots", tout << "q is the constant polynomial, so p does not contain any roots at x2v\n";); } else { SASSERT(is_univariate(q)); isolate_roots(q, roots); // some roots of q may not be roots of p_prime filter_roots(p_prime, x2v, x, roots); } } polynomial::sign eval_at_mpbq(polynomial_ref const & p, polynomial::var2anum const & x2v, polynomial::var x, mpbq const & v) { scoped_mpq qv(qm()); to_mpq(qm(), v, qv); scoped_anum av(m_wrapper); set(av, qv); ext_var2num ext_x2v(m_wrapper, x2v, x, av); return eval_sign_at(p, ext_x2v); } // Make sure that lower and upper of prev and curr don't touch each other void separate(numeral & prev, numeral & curr) { SASSERT(lt(prev, curr)); if (prev.is_basic()) { if (curr.is_basic()) { // do nothing } else { while (bqm().le(lower(curr.to_algebraic()), basic_value(prev))) { refine(curr); if (curr.is_basic()) break; // curr became basic } } } else { if (curr.is_basic()) { while (bqm().ge(upper(prev.to_algebraic()), basic_value(curr))) { refine(prev); if (prev.is_basic()) break; } } else { while (bqm().ge(upper(prev.to_algebraic()), lower(curr.to_algebraic()))) { refine(prev); refine(curr); if (prev.is_basic() || curr.is_basic()) break; } } } } void int_lt(numeral const & a, numeral & b) { scoped_mpz v(qm()); if (a.is_basic()) { qm().floor(basic_value(a), v); qm().dec(v); } else { bqm().floor(qm(), lower(a.to_algebraic()), v); } m_wrapper.set(b, v); } void int_gt(numeral const & a, numeral & b) { scoped_mpz v(qm()); if (a.is_basic()) { qm().ceil(basic_value(a), v); qm().inc(v); } else { bqm().ceil(qm(), upper(a.to_algebraic()), v); } m_wrapper.set(b, v); } // Select a numeral between prev and curr. // Pre: prev < curr void select(numeral & prev, numeral & curr, numeral & result) { TRACE("algebraic_select", tout << "prev: "; display_interval(tout, prev); tout << "\n"; tout << "curr: "; display_interval(tout, curr); tout << "\n";); SASSERT(lt(prev, curr)); separate(prev, curr); scoped_mpbq w(bqm()); if (prev.is_basic()) { if (curr.is_basic()) bqm().select_small_core(qm(), basic_value(prev), basic_value(curr), w); else bqm().select_small_core(qm(), basic_value(prev), lower(curr.to_algebraic()), w); } else { if (curr.is_basic()) bqm().select_small_core(qm(), upper(prev.to_algebraic()), basic_value(curr), w); else bqm().select_small_core(upper(prev.to_algebraic()), lower(curr.to_algebraic()), w); } scoped_mpq qw(qm()); to_mpq(qm(), w, qw); set(result, qw); } // Similar to ext_var2num but all variables that are not mapped by x2v are mapped to the same value. struct ext2_var2num : public polynomial::var2anum { manager & m_am; polynomial::var2anum const & m_x2v; anum const & m_v; ext2_var2num(manager & am, polynomial::var2anum const & x2v, anum const & v): m_am(am), m_x2v(x2v), m_v(v) { } manager & m() const override { return m_am; } bool contains(polynomial::var x) const override { return true; } anum const & operator()(polynomial::var x) const override { if (m_x2v.contains(x)) return m_x2v(x); else return m_v; } }; #define DEFAULT_PRECISION 2 void isolate_roots(polynomial_ref const & p, polynomial::var2anum const & x2v, numeral_vector & roots, svector & signs) { isolate_roots(p, x2v, roots); unsigned num_roots = roots.size(); if (num_roots == 0) { anum zero; ext2_var2num ext_x2v(m_wrapper, x2v, zero); polynomial::sign s = eval_sign_at(p, ext_x2v); signs.push_back(s); } else { TRACE("isolate_roots_bug", tout << "p: " << p << "\n"; polynomial::var_vector xs; p.m().vars(p, xs); for (unsigned i = 0; i < xs.size(); i++) { if (x2v.contains(xs[i])) { tout << "x" << xs[i] << " -> "; display_root(tout, x2v(xs[i])); tout << " "; display_interval(tout, x2v(xs[i])); tout << "\n"; } } for (unsigned i = 0; i < roots.size(); i++) { tout << "root[i]: "; display_root(tout, roots[i]); tout << "\n"; }); for (unsigned i = 0; i < num_roots; i++) refine_until_prec(roots[i], DEFAULT_PRECISION); scoped_anum w(m_wrapper); int_lt(roots[0], w); TRACE("isolate_roots_bug", tout << "w: "; display_root(tout, w); tout << "\n";); { ext2_var2num ext_x2v(m_wrapper, x2v, w); auto s = eval_sign_at(p, ext_x2v); SASSERT(s != polynomial::sign_zero); signs.push_back(s); } for (unsigned i = 1; i < num_roots; i++) { numeral & prev = roots[i-1]; numeral & curr = roots[i]; select(prev, curr, w); ext2_var2num ext_x2v(m_wrapper, x2v, w); auto s = eval_sign_at(p, ext_x2v); SASSERT(s != polynomial::sign_zero); signs.push_back(s); } int_gt(roots[num_roots - 1], w); { ext2_var2num ext_x2v(m_wrapper, x2v, w); auto s = eval_sign_at(p, ext_x2v); SASSERT(s != polynomial::sign_zero); signs.push_back(s); } } } void display_root(std::ostream & out, numeral const & a) { if (is_zero(a)) { out << "(#, 1)"; // first root of polynomial # } else if (a.is_basic()) { mpq const & v = basic_value(a); mpz neg_n; qm().set(neg_n, v.numerator()); qm().neg(neg_n); mpz coeffs[2] = { std::move(neg_n), qm().dup(v.denominator()) }; out << "("; upm().display(out, 2, coeffs, "#"); out << ", 1)"; // first root of the polynomial d*# - n qm().del(coeffs[0]); qm().del(coeffs[1]); } else { algebraic_cell * c = a.to_algebraic(); out << "("; upm().display(out, c->m_p_sz, c->m_p, "#"); if (c->m_i == 0) { // undefined c->m_i = upm().get_root_id(c->m_p_sz, c->m_p, lower(c)) + 1; } SASSERT(c->m_i > 0); out << ", " << c->m_i; out << ")"; } } void display_mathematica(std::ostream & out, numeral const & a) { if (a.is_basic()) { qm().display(out, basic_value(a)); } else { algebraic_cell * c = a.to_algebraic(); out << "Root["; upm().display(out, c->m_p_sz, c->m_p, "#1", true); if (c->m_i == 0) { // undefined c->m_i = upm().get_root_id(c->m_p_sz, c->m_p, lower(c)) + 1; } SASSERT(c->m_i > 0); out << " &, " << c->m_i << "]"; } } void display_root_smt2(std::ostream & out, numeral const & a) { if (is_zero(a)) { out << "(root-obj x 1)"; } else if (a.is_basic()) { mpq const & v = basic_value(a); mpz neg_n; qm().set(neg_n, v.numerator()); qm().neg(neg_n); mpz coeffs[2] = { std::move(neg_n), qm().dup(v.denominator()) }; out << "(root-obj "; upm().display_smt2(out, 2, coeffs, "x"); out << " 1)"; // first root of the polynomial d*# - n qm().del(coeffs[0]); qm().del(coeffs[1]); } else { algebraic_cell * c = a.to_algebraic(); out << "(root-obj "; upm().display_smt2(out, c->m_p_sz, c->m_p, "x"); if (c->m_i == 0) { // undefined c->m_i = upm().get_root_id(c->m_p_sz, c->m_p, lower(c)) + 1; } SASSERT(c->m_i > 0); out << " " << c->m_i; out << ")"; } } void display_interval(std::ostream & out, numeral const & a) { if (a.is_basic()) { out << "["; qm().display(out, basic_value(a)); out << ", "; qm().display(out, basic_value(a)); out << "]"; } else { bqim().display(out, a.to_algebraic()->m_interval); } } bool get_interval(numeral const & a, mpbq & l, mpbq & u, unsigned precision) { SASSERT(!a.is_basic()); algebraic_cell * c = a.to_algebraic(); mpbqi const & i = c->m_interval; bqm().set(l, i.lower()); bqm().set(u, i.upper()); // the precision on refine is base 2 return upm().refine(c->m_p_sz, c->m_p, bqm(), l, u, precision * 4); } void display_decimal(std::ostream & out, numeral const & a, unsigned precision) { if (a.is_basic()) { qm().display_decimal(out, basic_value(a), precision); } else { scoped_mpbq l(bqm()), u(bqm()); if (get_interval(a, l, u, precision)) { // this is a hack... fix it bqm().display_decimal(out, u, precision); } else { // actual root was found bqm().display_decimal(out, l, precision); } } } void get_lower(numeral const & a, mpq & l, unsigned precision) { if (a.is_basic()) { qm().set(l, basic_value(a)); } else { scoped_mpbq _l(bqm()), _u(bqm()); get_interval(a, _l, _u, precision); to_mpq(qm(), _l, l); } } void get_upper(numeral const & a, mpq & u, unsigned precision) { if (a.is_basic()) { qm().set(u, basic_value(a)); } else { scoped_mpbq _l(bqm()), _u(bqm()); get_interval(a, _l, _u, precision); to_mpq(qm(), _u, u); } } }; manager::manager(reslimit& lim, unsynch_mpq_manager & m, params_ref const & p, small_object_allocator * a) { m_own_allocator = false; m_allocator = a; if (m_allocator == nullptr) { m_own_allocator = true; m_allocator = alloc(small_object_allocator, "algebraic"); } m_imp = alloc(imp, lim, *this, m, p, *m_allocator); } manager::~manager() { dealloc(m_imp); if (m_own_allocator) dealloc(m_allocator); } void manager::updt_params(params_ref const & p) { } unsynch_mpq_manager & manager::qm() const { return m_imp->qm(); } mpbq_manager & manager::bqm() const { return m_imp->bqm(); } void manager::del(numeral & a) { return m_imp->del(a); } void manager::reset(numeral & a) { return m_imp->reset(a); } bool manager::is_zero(numeral const & a) { return m_imp->is_zero(const_cast(a)); } bool manager::is_pos(numeral const & a) { return m_imp->is_pos(const_cast(a)); } bool manager::is_neg(numeral const & a) { return m_imp->is_neg(const_cast(a)); } bool manager::is_rational(numeral const & a) { return m_imp->is_rational(const_cast(a)); } bool manager::is_int(numeral const & a) { return m_imp->is_int(const_cast(a)); } unsigned manager::degree(numeral const & a) { return m_imp->degree(const_cast(a)); } void manager::to_rational(numeral const & a, mpq & r) { return m_imp->to_rational(const_cast(a), r); } void manager::to_rational(numeral const & a, rational & r) { return m_imp->to_rational(const_cast(a), r); } void manager::swap(numeral & a, numeral & b) { return m_imp->swap(a, b); } void manager::int_lt(numeral const & a, numeral & b) { m_imp->int_lt(const_cast(a), b); } void manager::int_gt(numeral const & a, numeral & b) { m_imp->int_gt(const_cast(a), b); } void manager::select(numeral const & prev, numeral const & curr, numeral & result) { m_imp->select(const_cast(prev), const_cast(curr), result); } void manager::set(numeral & a, int n) { scoped_mpq _n(qm()); qm().set(_n, n); set(a, _n); } void manager::set(numeral & a, mpz const & n) { scoped_mpq _n(qm()); qm().set(_n, n); set(a, _n); } void manager::set(numeral & a, mpq const & n) { m_imp->set(a, n); } void manager::set(numeral & a, numeral const & n) { m_imp->set(a, n); } void manager::isolate_roots(polynomial_ref const & p, numeral_vector & roots) { m_imp->isolate_roots(p, roots); } void manager::isolate_roots(polynomial_ref const & p, polynomial::var2anum const & x2v, numeral_vector & roots) { m_imp->isolate_roots(p, x2v, roots); } void manager::isolate_roots(polynomial_ref const & p, polynomial::var2anum const & x2v, numeral_vector & roots, svector & signs) { m_imp->isolate_roots(p, x2v, roots, signs); } void manager::mk_root(polynomial_ref const & p, unsigned i, numeral & r) { m_imp->mk_root(p, i, r); } void manager::mk_root(sexpr const * p, unsigned i, numeral & r) { m_imp->mk_root(p, i, r); } void manager::root(numeral const & a, unsigned k, numeral & b) { m_imp->root(const_cast(a), k, b); } void manager::power(numeral const & a, unsigned k, numeral & b) { TRACE("anum_detail", display_root(tout, a); tout << "^" << k << "\n";); m_imp->power(const_cast(a), k, b); TRACE("anum_detail", tout << "^ result: "; display_root(tout, b); tout << "\n";); } void manager::add(numeral const & a, numeral const & b, numeral & c) { TRACE("anum_detail", display_root(tout, a); tout << " + "; display_root(tout, b); tout << "\n";); m_imp->add(const_cast(a), const_cast(b), c); TRACE("anum_detail", tout << "+ result: "; display_root(tout, c); tout << "\n";); } void manager::add(numeral const & a, mpz const & b, numeral & c) { scoped_anum tmp(*this); set(tmp, b); add(a, tmp, c); } void manager::sub(numeral const & a, numeral const & b, numeral & c) { TRACE("anum_detail", display_root(tout, a); tout << " - "; display_root(tout, b); tout << "\n";); m_imp->sub(const_cast(a), const_cast(b), c); TRACE("anum_detail", tout << "- result: "; display_root(tout, c); tout << "\n";); } void manager::mul(numeral const & a, numeral const & b, numeral & c) { TRACE("anum_detail", display_root(tout, a); tout << " * "; display_root(tout, b); tout << "\n";); m_imp->mul(const_cast(a), const_cast(b), c); TRACE("anum_detail", tout << "* result: "; display_root(tout, c); tout << "\n";); } void manager::div(numeral const & a, numeral const & b, numeral & c) { m_imp->div(const_cast(a), const_cast(b), c); } void manager::neg(numeral & a) { m_imp->neg(a); } void manager::inv(numeral & a) { m_imp->inv(a); } int manager::compare(numeral const & a, numeral const & b) { return m_imp->compare(const_cast(a), const_cast(b)); } bool manager::eq(numeral const & a, numeral const & b) { return m_imp->eq(const_cast(a), const_cast(b)); } bool manager::eq(numeral const & a, mpq const & b) { return m_imp->eq(const_cast(a), b); } bool manager::eq(numeral const & a, mpz const & b) { scoped_mpq _b(qm()); qm().set(_b, b); return eq(const_cast(a), _b); } bool manager::lt(numeral const & a, numeral const & b) { return m_imp->lt(const_cast(a), const_cast(b)); } bool manager::lt(numeral const & a, mpq const & b) { return m_imp->lt(const_cast(a), b); } bool manager::lt(numeral const & a, mpz const & b) { scoped_mpq _b(qm()); qm().set(_b, b); return lt(const_cast(a), _b); } bool manager::gt(numeral const & a, mpq const & b) { return m_imp->gt(const_cast(a), b); } bool manager::gt(numeral const & a, mpz const & b) { scoped_mpq _b(qm()); qm().set(_b, b); return gt(const_cast(a), _b); } void manager::get_polynomial(numeral const & a, svector & r) { m_imp->get_polynomial(a, r); } void manager::get_lower(numeral const & a, mpbq & l) { SASSERT(!is_rational(a)); bqm().set(l, a.to_algebraic()->m_interval.lower()); } void manager::get_lower(numeral const & a, mpq & l) { scoped_mpbq bq(bqm()); get_lower(a, bq); to_mpq(qm(), bq, l); } void manager::get_lower(numeral const & a, rational & l) { scoped_mpq q(m_imp->qm()); get_lower(a, q); l = rational(q); } void manager::get_lower(numeral const & a, mpq & l, unsigned precision) { m_imp->get_lower(a, l, precision); } void manager::get_lower(numeral const & a, rational & l, unsigned precision) { scoped_mpq _l(qm()); m_imp->get_lower(a, _l, precision); l = rational(_l); } void manager::get_upper(numeral const & a, mpbq & u) { SASSERT(!is_rational(a)); bqm().set(u, a.to_algebraic()->m_interval.upper()); } void manager::get_upper(numeral const & a, mpq & u) { scoped_mpbq bq(bqm()); get_upper(a, bq); to_mpq(qm(), bq, u); } void manager::get_upper(numeral const & a, rational & u) { scoped_mpq q(m_imp->qm()); get_upper(a, q); u = rational(q); } void manager::get_upper(numeral const & a, mpq & l, unsigned precision) { m_imp->get_upper(a, l, precision); } void manager::get_upper(numeral const & a, rational & l, unsigned precision) { scoped_mpq _l(qm()); m_imp->get_upper(a, _l, precision); l = rational(_l); } polynomial::sign manager::eval_sign_at(polynomial_ref const & p, polynomial::var2anum const & x2v) { SASSERT(&(x2v.m()) == this); return m_imp->eval_sign_at(p, x2v); } void manager::display_interval(std::ostream & out, numeral const & a) const { m_imp->display_interval(out, a); } void manager::display_decimal(std::ostream & out, numeral const & a, unsigned precision) const { m_imp->display_decimal(out, a, precision); } void manager::display_root(std::ostream & out, numeral const & a) const { m_imp->display_root(out, a); } void manager::display_mathematica(std::ostream & out, numeral const & a) const { m_imp->display_mathematica(out, a); } void manager::display_root_smt2(std::ostream & out, numeral const & a) const { m_imp->display_root_smt2(out, a); } void manager::reset_statistics() { m_imp->reset_statistics(); } void manager::collect_statistics(statistics & st) const { m_imp->collect_statistics(st); } }; z3-z3-4.8.7/src/math/polynomial/algebraic_numbers.h000066400000000000000000000367161356505360400221640ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: algebraic_numbers.h Abstract: Real Algebraic Numbers Author: Leonardo (leonardo) 2011-11-22 Notes: --*/ #ifndef ALGEBRAIC_NUMBERS_H_ #define ALGEBRAIC_NUMBERS_H_ #include "util/rational.h" #include "util/mpq.h" #include "math/polynomial/polynomial.h" #include "util/z3_exception.h" #include "util/scoped_numeral.h" #include "util/scoped_numeral_vector.h" #include "util/tptr.h" #include "util/statistics.h" #include "util/params.h" #include "util/rlimit.h" class small_object_allocator; class mpbq_manager; class mpbq; namespace algebraic_numbers { class anum; class manager; class algebraic_exception : public default_exception { public: algebraic_exception(char const * msg):default_exception(msg) {} }; class manager { public: struct imp; private: imp * m_imp; small_object_allocator * m_allocator; bool m_own_allocator; public: static bool precise() { return true; } static bool field() { return true; } typedef anum numeral; typedef svector numeral_vector; typedef _scoped_numeral scoped_numeral; typedef _scoped_numeral_vector scoped_numeral_vector; manager(reslimit& rl, unsynch_mpq_manager & m, params_ref const & p = params_ref(), small_object_allocator * a = nullptr); ~manager(); static void get_param_descrs(param_descrs & r); static void collect_param_descrs(param_descrs & r) { get_param_descrs(r); } void updt_params(params_ref const & p); unsynch_mpq_manager & qm() const; mpbq_manager & bqm() const; void del(numeral & a); /** \brief a <- 0 */ void reset(numeral & a); /** \brief Return true if a is zero. */ bool is_zero(numeral const & a); /** \brief Return true if a is positive. */ bool is_pos(numeral const & a); /** \brief Return true if a is negative. */ bool is_neg(numeral const & a); /** \brief Return true if a is a rational number. */ bool is_rational(numeral const & a); /** \brief Return true if a is an integer. */ bool is_int(numeral const & a); /** \brief Degree of the algebraic number. That is, degree of the polynomial that is used to encode \c a. */ unsigned degree(numeral const & a); /** \brief Convert a into a rational number. \pre is_rational(a) */ void to_rational(numeral const & a, mpq & r); /** \brief Convert a into a rational number. \pre is_rational(a) */ void to_rational(numeral const & a, rational & r); /** \brief a <- n */ void set(numeral & a, int n); void set(numeral & a, mpz const & n); void set(numeral & a, mpq const & n); void set(numeral & a, numeral const & n); void swap(numeral & a, numeral & b); /** \brief Store in b an integer value smaller than 'a'. Remark: this is not the floor, but b <= floor(a) */ void int_lt(numeral const & a, numeral & b); /** \brief Store in b an integer value bigger than 'a' Remark: this is not the ceil, but b >= ceil(a) */ void int_gt(numeral const & a, numeral & b); /** \brief Store in result a value in the interval (prev, next) \pre lt(pre,v next) */ void select(numeral const & prev, numeral const & curr, numeral & result); /** \brief Isolate the roots of (an univariate polynomial) p, and store them as algebraic numbers in \c root. That is, p is in Z[x]. */ void isolate_roots(polynomial_ref const & p, numeral_vector & roots); /** \brief Isolate the roots of a multivariate polynomial p such that all but one variable of p is fixed by x2v, and store them as algebraic numbers in \c root. That is, we are viewing p as a polynomial in Z[y_1, ..., y_n][x]: q_n(y_1, ..., y_n)x^n + ... + q_1(y_1, ..., y_n)*x + q_0 And we are returning the roots of q_n(x2v(y_1), ..., x2v(y_n))x^n + ... + q_1(x2v(y_1), ..., x2v(y_n))*x + q_0 */ void isolate_roots(polynomial_ref const & p, polynomial::var2anum const & x2v, numeral_vector & roots); /** \brief Isolate the roots of the given polynomial, and compute its sign between them. */ void isolate_roots(polynomial_ref const & p, polynomial::var2anum const & x2v, numeral_vector & roots, svector & signs); /** \brief Store in r the i-th root of p. This method throws an exception if p does not have at least i roots. This method is not really used in the nonlinear procedure. It is mainly used for debugging purposes, and creating regression tests \pre i > 0 */ void mk_root(polynomial_ref const & p, unsigned i, numeral & r); /** \brief Store in r the i-th root of p. This method throws an exception if the s-expression p does not represent an univariate polynomial, of if p does not have at least i roots. This method is not really used in the nonlinear procedure. It is mainly used for debugging purposes, and "reading" root objects in the SMT 2.0 front-end. \pre i > 0 */ void mk_root(sexpr const * p, unsigned i, numeral & r); /** \brief Return a^{1/k} Throws an exception if the result is not a real. That is, (a is negative and k is even) or (k is zero). */ void root(numeral const & a, unsigned k, numeral & b); /** \brief Return a^k Throws an exception if 0^0. */ void power(numeral const & a, unsigned k, numeral & b); /** \brief c <- a + b */ void add(numeral const & a, numeral const & b, numeral & c); void add(numeral const & a, mpz const & b, numeral & c); /** \brief c <- a - b */ void sub(numeral const & a, numeral const & b, numeral & c); /** \brief c <- a * b */ void mul(numeral const & a, numeral const & b, numeral & c); /** \brief a <- -a */ void neg(numeral & a); /** \brief a <- 1/a if a != 0 */ void inv(numeral & a); /** \brief c <- a/b if b != 0 */ void div(numeral const & a, numeral const & b, numeral & c); /** Return -1 if a < b Return 0 if a == b Return 1 if a > b */ int compare(numeral const & a, numeral const & b); /** \brief a == b */ bool eq(numeral const & a, numeral const & b); bool eq(numeral const & a, mpq const & b); bool eq(numeral const & a, mpz const & b); /** \brief a != b */ bool neq(numeral const & a, numeral const & b) { return !eq(a, b); } bool neq(numeral const & a, mpq const & b) { return !eq(a, b); } bool neq(numeral const & a, mpz const & b) { return !eq(a, b); } /** \brief a < b */ bool lt(numeral const & a, numeral const & b); bool lt(numeral const & a, mpq const & b); bool lt(numeral const & a, mpz const & b); /** \brief a > b */ bool gt(numeral const & a, numeral const & b) { return lt(b, a); } bool gt(numeral const & a, mpq const & b); bool gt(numeral const & a, mpz const & b); /** \brief a <= b */ bool le(numeral const & a, numeral const & b) { return !gt(a, b); } bool le(numeral const & a, mpq const & b) { return !gt(a, b); } bool le(numeral const & a, mpz const & b) { return !gt(a, b); } /** \brief a >= b */ bool ge(numeral const & a, numeral const & b) { return !lt(a, b); } bool ge(numeral const & a, mpq const & b) { return !lt(a, b); } bool ge(numeral const & a, mpz const & b) { return !lt(a, b); } /** \brief Evaluate the sign of a multivariate polynomial p(x_1, ..., x_n) at assignment x2v: [x_1 -> alpha_1, ..., x_n -> alpha_n]. \remark forall variable x in p, we have that x2v.contains(x) is true Return negative number if p(alpha_1, ..., alpha_n) < 0 Return 0 if p(alpha_1, ..., alpha_n) == 0 Return positive number if p(alpha_1, ..., alpha_n) > 0 */ polynomial::sign eval_sign_at(polynomial_ref const & p, polynomial::var2anum const & x2v); void get_polynomial(numeral const & a, svector & r); // Procedures for getting lower and upper bounds for irrational numbers void get_lower(numeral const & a, mpbq & l); void get_lower(numeral const & a, mpq & l); void get_lower(numeral const & a, rational & l); void get_lower(numeral const & a, mpq & l, unsigned precision); void get_lower(numeral const & a, rational & l, unsigned precision); void get_upper(numeral const & a, mpbq & u); void get_upper(numeral const & a, mpq & u); void get_upper(numeral const & a, rational & u); void get_upper(numeral const & a, mpq & l, unsigned precision); void get_upper(numeral const & a, rational & l, unsigned precision); /** \brief Display algebraic number as a rational if is_rational(n) Otherwise, display it as an interval. */ void display_interval(std::ostream & out, numeral const & a) const; /** \brief Display algebraic number in decimal notation. A question mark is added based on the precision requested. */ void display_decimal(std::ostream & out, numeral const & a, unsigned precision = 10) const; /** \brief Display algebraic number as a root object: (p, i) That is, 'a' is the i-th root of p. */ void display_root(std::ostream & out, numeral const & a) const; /** \brief Display algebraic number as a root object in SMT 2.0 style: (root-obj p i) That is, 'a' is the i-th root of p. */ void display_root_smt2(std::ostream & out, numeral const & a) const; /** \brief Display algebraic number in Mathematica format. */ void display_mathematica(std::ostream & out, numeral const & a) const; void display(std::ostream & out, numeral const & a) { return display_decimal(out, a); } void reset_statistics(); void collect_statistics(statistics & st) const; }; struct basic_cell; struct algebraic_cell; enum anum_kind { BASIC = 0, ROOT }; class anum { friend struct manager::imp; friend class manager; void * m_cell; anum(basic_cell * cell):m_cell(TAG(void*, cell, BASIC)) {} anum(algebraic_cell * cell):m_cell(TAG(void*, cell, ROOT)) {} bool is_basic() const { return GET_TAG(m_cell) == BASIC; } basic_cell * to_basic() const { SASSERT(is_basic()); return UNTAG(basic_cell*, m_cell); } algebraic_cell * to_algebraic() const { SASSERT(!is_basic()); return UNTAG(algebraic_cell*, m_cell); } public: anum():m_cell(nullptr) {} }; }; typedef algebraic_numbers::manager anum_manager; typedef algebraic_numbers::manager::numeral anum; typedef algebraic_numbers::manager::numeral_vector anum_vector; typedef algebraic_numbers::manager::scoped_numeral scoped_anum; typedef algebraic_numbers::manager::scoped_numeral_vector scoped_anum_vector; #define AN_MK_COMPARISON_CORE(EXTERNAL, INTERNAL, TYPE) \ inline bool EXTERNAL(scoped_anum const & a, TYPE const & b) { \ anum_manager & m = a.m(); \ scoped_anum _b(m); \ m.set(_b, b); \ return m.INTERNAL(a, _b); \ } #define AN_MK_COMPARISON(EXTERNAL, INTERNAL) \ AN_MK_COMPARISON_CORE(EXTERNAL, INTERNAL, int) \ AN_MK_COMPARISON_CORE(EXTERNAL, INTERNAL, mpz) \ AN_MK_COMPARISON_CORE(EXTERNAL, INTERNAL, mpq) AN_MK_COMPARISON(operator==, eq); AN_MK_COMPARISON(operator!=, neq); AN_MK_COMPARISON(operator<, lt); AN_MK_COMPARISON(operator<=, le); AN_MK_COMPARISON(operator>, gt); AN_MK_COMPARISON(operator>=, ge); #undef AN_MK_COMPARISON #undef AN_MK_COMPARISON_CORE #define AN_MK_BINARY_CORE(EXTERNAL, INTERNAL, TYPE) \ inline scoped_anum EXTERNAL(scoped_anum const & a, TYPE const & b) { \ anum_manager & m = a.m(); \ scoped_anum _b(m); \ m.set(_b, b); \ scoped_anum r(m); \ m.INTERNAL(a, _b, r); \ return r; \ } #define AN_MK_BINARY(EXTERNAL, INTERNAL) \ AN_MK_BINARY_CORE(EXTERNAL, INTERNAL, int) \ AN_MK_BINARY_CORE(EXTERNAL, INTERNAL, mpz) \ AN_MK_BINARY_CORE(EXTERNAL, INTERNAL, mpq) AN_MK_BINARY(operator+, add) AN_MK_BINARY(operator-, sub) AN_MK_BINARY(operator*, mul) AN_MK_BINARY(operator/, div) #undef AN_MK_BINARY #undef AN_MK_BINARY_CORE inline scoped_anum root(scoped_anum const & a, unsigned k) { scoped_anum r(a.m()); a.m().root(a, k, r); return r; } inline scoped_anum power(scoped_anum const & a, unsigned k) { scoped_anum r(a.m()); a.m().power(a, k, r); return r; } inline scoped_anum operator^(scoped_anum const & a, unsigned k) { return power(a, k); } inline bool is_int(scoped_anum const & a) { return a.m().is_int(a); } inline bool is_rational(scoped_anum const & a) { return a.m().is_rational(a); } struct root_obj_pp { anum_manager & m; anum const & n; root_obj_pp(anum_manager & _m, anum const & _n):m(_m), n(_n) {} root_obj_pp(scoped_anum const & _n):m(_n.m()), n(_n.get()) {} }; inline std::ostream & operator<<(std::ostream & out, root_obj_pp const & n) { n.m.display_root(out, n.n); return out; } struct decimal_pp { anum_manager & m; anum const & n; unsigned prec; decimal_pp(anum_manager & _m, anum const & _n, unsigned p):m(_m), n(_n), prec(p) {} decimal_pp(scoped_anum const & _n, unsigned p):m(_n.m()), n(_n.get()), prec(p) {} }; inline std::ostream & operator<<(std::ostream & out, decimal_pp const & n) { n.m.display_decimal(out, n.n, n.prec); return out; } struct interval_pp { anum_manager & m; anum const & n; interval_pp(anum_manager & _m, anum const & _n):m(_m), n(_n) {} interval_pp(scoped_anum const & _n):m(_n.m()), n(_n.get()) {} }; inline std::ostream & operator<<(std::ostream & out, interval_pp const & n) { n.m.display_interval(out, n.n); return out; } #endif z3-z3-4.8.7/src/math/polynomial/algebraic_params.pyg000066400000000000000000000042061356505360400223310ustar00rootroot00000000000000def_module_params('algebraic', description='real algebraic number package', export=True, params=(('zero_accuracy', UINT, 0, 'one of the most time-consuming operations in the real algebraic number module is determining the sign of a polynomial evaluated at a sample point with non-rational algebraic number values. Let k be the value of this option. If k is 0, Z3 uses precise computation. Otherwise, the result of a polynomial evaluation is considered to be 0 if Z3 can show it is inside the interval (-1/2^k, 1/2^k)'), ('min_mag', UINT, 16, 'Z3 represents algebraic numbers using a (square-free) polynomial p and an isolating interval (which contains one and only one root of p). This interval may be refined during the computations. This parameter specifies whether to cache the value of a refined interval or not. It says the minimal size of an interval for caching purposes is 1/2^16'), ('factor', BOOL, True, 'use polynomial factorization to simplify polynomials representing algebraic numbers'), ('factor_max_prime', UINT, 31, 'parameter for the polynomial factorization procedure in the algebraic number module. Z3 polynomial factorization is composed of three steps: factorization in GF(p), lifting and search. This parameter limits the maximum prime number p to be used in the first step'), ('factor_num_primes', UINT, 1, 'parameter for the polynomial factorization procedure in the algebraic number module. Z3 polynomial factorization is composed of three steps: factorization in GF(p), lifting and search. The search space may be reduced by factoring the polynomial in different GF(p)\'s. This parameter specify the maximum number of finite factorizations to be considered, before lifiting and searching'), ('factor_search_size', UINT, 5000, 'parameter for the polynomial factorization procedure in the algebraic number module. Z3 polynomial factorization is composed of three steps: factorization in GF(p), lifting and search. This parameter can be used to limit the search space'))) z3-z3-4.8.7/src/math/polynomial/linear_eq_solver.h000066400000000000000000000106321356505360400220360ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: linear_eq_solver.h Abstract: Simple equational solver template for any number field. No special optimization, just the basics for solving small systems. It is a solver target to dense system of equations. Main client: Sparse Modular GCD algorithm. Author: Leonardo (leonardo) 2012-01-22 Notes: --*/ #ifndef LINEAR_EQ_SOLVER_H_ #define LINEAR_EQ_SOLVER_H_ template class linear_eq_solver { typedef typename numeral_manager::numeral numeral; numeral_manager & m; unsigned n; // number of variables vector > A; svector b; public: linear_eq_solver(numeral_manager & _m):m(_m), n(0) { SASSERT(m.field()); } ~linear_eq_solver() { flush(); } void flush() { SASSERT(b.size() == A.size()); unsigned sz = A.size(); for (unsigned i = 0; i < sz; i++) { svector & as = A[i]; m.del(b[i]); SASSERT(as.size() == n); for (unsigned j = 0; j < n; j++) m.del(as[j]); } A.reset(); b.reset(); n = 0; } void resize(unsigned _n) { if (n != _n) { flush(); n = _n; for (unsigned i = 0; i < n; i++) { A.push_back(svector()); svector & as = A.back(); for (unsigned j = 0; j < n; j++) { as.push_back(numeral()); } b.push_back(numeral()); } } } void reset() { for (unsigned i = 0; i < n; i++) { svector & A_i = A[i]; for (unsigned j = 0; j < n; j++) { m.set(A_i[j], 0); } m.set(b[i], 0); } } // Set row i with _as[0]*x_0 + ... + _as[n-1]*x_{n-1} = b void add(unsigned i, numeral const * _as, numeral const & _b) { SASSERT(i < n); m.set(b[i], _b); svector & A_i = A[i]; for (unsigned j = 0; j < n; j++) { m.set(A_i[j], _as[j]); } } // Return true if the system of equations has a solution. // Return false if the matrix is singular bool solve(numeral * xs) { for (unsigned k = 0; k < n; k++) { TRACE("linear_eq_solver", tout << "iteration " << k << "\n"; display(tout);); // find pivot unsigned i = k; for (; i < n; i++) { if (!m.is_zero(A[i][k])) break; } if (i == n) return false; // matrix is singular A[k].swap(A[i]); // swap rows svector & A_k = A[k]; numeral & A_k_k = A_k[k]; SASSERT(!m.is_zero(A_k_k)); // normalize row for (unsigned i = k+1; i < n; i++) m.div(A_k[i], A_k_k, A_k[i]); m.div(b[k], A_k_k, b[k]); m.set(A_k_k, 1); // check if first k-1 positions are zero DEBUG_CODE({ for (unsigned i = 0; i < k; i++) { SASSERT(m.is_zero(A_k[i])); } }); // for all rows below pivot for (unsigned i = k+1; i < n; i++) { svector & A_i = A[i]; numeral & A_i_k = A_i[k]; for (unsigned j = k+1; j < n; j++) { m.submul(A_i[j], A_i_k, A_k[j], A_i[j]); } m.submul(b[i], A_i_k, b[k], b[i]); m.set(A_i_k, 0); } } unsigned k = n; while (k > 0) { --k; TRACE("linear_eq_solver", tout << "iteration " << k << "\n"; display(tout);); SASSERT(m.is_one(A[k][k])); // save result m.set(xs[k], b[k]); // back substitute unsigned i = k; while (i > 0) { --i; m.submul(b[i], A[i][k], b[k], b[i]); m.set(A[i][k], 0); } } return true; } void display(std::ostream & out) const { for (unsigned i = 0; i < A.size(); i++) { SASSERT(A[i].size() == n); for (unsigned j = 0; j < n; j++) { m.display(out, A[i][j]); out << " "; } m.display(out, b[i]); out << "\n"; } } }; #endif z3-z3-4.8.7/src/math/polynomial/polynomial.cpp000066400000000000000000010610461356505360400212310ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: polynomial.cpp Abstract: Goodies for creating and handling polynomials. Author: Leonardo (leonardo) 2011-11-15 Notes: --*/ #include "math/polynomial/polynomial.h" #include "util/vector.h" #include "util/chashtable.h" #include "util/small_object_allocator.h" #include "util/id_gen.h" #include "util/buffer.h" #include "util/scoped_ptr_vector.h" #include "math/polynomial/upolynomial_factorization.h" #include "math/polynomial/polynomial_primes.h" #include "util/permutation.h" #include "math/polynomial/algebraic_numbers.h" #include "util/mpzzp.h" #include "util/timeit.h" #include "math/polynomial/linear_eq_solver.h" #include "util/scoped_numeral_buffer.h" #include "util/ref_buffer.h" #include "util/common_msgs.h" namespace polynomial { factor_params::factor_params(): m_max_p(UINT_MAX), m_p_trials(1), m_max_search_size(UINT_MAX) { } factor_params::factor_params(unsigned max_p, unsigned p_trials, unsigned max_search_size): m_max_p(max_p), m_p_trials(p_trials), m_max_search_size(max_search_size) { } void factor_params::updt_params(params_ref const & p) { m_max_p = p.get_uint("max_prime", UINT_MAX); m_p_trials = p.get_uint("num_primes", 1); m_max_search_size = p.get_uint("max_search_size", UINT_MAX); } void factor_params::get_param_descrs(param_descrs & r) { r.insert("max_search_size", CPK_UINT, "(default: infty) Z3 polynomial factorization is composed of three steps: factorization in GF(p), lifting and search. This parameter can be used to limit the search space."); r.insert("max_prime", CPK_UINT, "(default: infty) Z3 polynomial factorization is composed of three steps: factorization in GF(p), lifting and search. This parameter limits the maximum prime number p to be used in the first step."); r.insert("num_primes", CPK_UINT, "(default: 1) Z3 polynomial factorization is composed of three steps: factorization in GF(p), lifting and search. The search space may be reduced by factoring the polynomial in different GF(p)'s. This parameter specify the maximum number of finite factorizations to be considered, before lifiting and searching."); } typedef ptr_vector monomial_vector; void var2degree::display(std::ostream & out) const { bool first = true; out << "["; for (unsigned i = 0; i < m_var2degree.size(); ++ i) { if (!m_var2degree.empty()) { if (!first) { out << ","; } out << "x" << i << "^" << m_var2degree[i]; if (first) { first = false; } } } out << "]"; } // ----------------------------------- // // Monomials // // ----------------------------------- /** \brief power: var + exponent */ class power : public std::pair { public: power():std::pair() {} power(var v, unsigned d):std::pair(v, d) {} var get_var() const { return first; } unsigned degree() const { return second; } unsigned & degree() { return second; } void set_var(var x) { first = x; } struct lt_var { bool operator()(power const & p1, power const & p2) { // CMW: The assertion below does not hold on macOS, because // their implementation of std::sort will try to compare // two items at the same index instead of comparing // the indices directly. I suspect that the purpose of // this assertion was to make sure that there are // no duplicates, so I replaced it with a new assertion at // the end of var_degrees(...). // SASSERT(p1.get_var() != p2.get_var()); return p1.get_var() < p2.get_var(); } }; struct lt_degree { bool operator()(power const & p1, power const & p2) { return p1.degree() < p2.degree(); } }; }; std::ostream & operator<<(std::ostream & out, power const & p) { out << "x" << p.get_var(); if (p.degree() != 1) out << "^" << p.degree(); return out; } /** \brief Return true if the variables in pws are sorted in increasing order and are distinct. */ bool is_valid_power_product(unsigned sz, power const * pws) { for (unsigned i = 1; i < sz; i++) { if (pws[i-1].get_var() >= pws[i].get_var()) return false; } return true; } /** \brief Return total degree of the given power product. */ unsigned power_product_total_degree(unsigned sz, power const * pws) { unsigned r = 0; for (unsigned i = 0; i < sz; i++) r += pws[i].degree(); return r; } /** \brief Monomials (power products) */ class monomial { unsigned m_ref_count; unsigned m_id; //!< unique id unsigned m_total_degree; //!< total degree of the monomial unsigned m_size; //!< number of powers unsigned m_hash; power m_powers[0]; friend class tmp_monomial; void sort() { std::sort(m_powers, m_powers + m_size, power::lt_var()); } public: static unsigned hash_core(unsigned sz, power const * pws) { return string_hash(reinterpret_cast(const_cast(pws)), sz*sizeof(power), 11); } struct hash_proc { unsigned operator()(monomial const * m) const { return m->m_hash; } }; struct eq_proc { bool operator()(monomial const * m1, monomial const * m2) const { if (m1->size() != m2->size() || m1->hash() != m2->hash()) return false; // m_total_degree must not be used as a filter, because it is not updated in temporary monomials. for (unsigned i = 0; i < m1->m_size; i++) { if (m1->get_power(i) != m2->get_power(i)) return false; } return true; } }; static unsigned get_obj_size(unsigned sz) { return sizeof(monomial) + sz * sizeof(power); } monomial(unsigned id, unsigned sz, power const * pws, unsigned h): m_ref_count(0), m_id(id), m_total_degree(0), m_size(sz), m_hash(h) { for (unsigned i = 0; i < sz; i ++) { power const & pw = pws[i]; m_powers[i] = pw; SASSERT(i == 0 || get_var(i) > get_var(i-1)); SASSERT(degree(i) > 0); m_total_degree += degree(i); } } unsigned hash() const { return m_hash; } unsigned ref_count() const { return m_ref_count; } void inc_ref() { m_ref_count++; } void dec_ref() { SASSERT(m_ref_count > 0); m_ref_count--; } bool is_valid() const { return is_valid_power_product(m_size, m_powers) && power_product_total_degree(m_size, m_powers) == m_total_degree; } unsigned id() const { return m_id; } unsigned size() const { return m_size; } unsigned total_degree() const { return m_total_degree; } power const & get_power(unsigned idx) const { SASSERT(idx < size()); return m_powers[idx]; } power const * get_powers() const { return m_powers; } var get_var(unsigned idx) const { return get_power(idx).get_var(); } unsigned degree(unsigned idx) const { return get_power(idx).degree(); } var max_var() const { if (m_size == 0) return null_var; return get_var(m_size - 1); } unsigned max_var_degree() const { if (m_size == 0) return 0; return degree(m_size - 1); } #define SMALL_MONOMIAL 8 unsigned index_of(var x) const { if (m_size == 0) return UINT_MAX; unsigned last = m_size - 1; if (get_var(last) == x) return last; if (m_size < SMALL_MONOMIAL) { // use linear search for small monomials // search backwards since we usually ask for the degree of "big" variables for (unsigned i = last; i-- > 0; ) { if (get_var(i) == x) return i; } return UINT_MAX; } else { // use binary search for big monomials int low = 0; int high = last; while (true) { int mid = low + ((high - low)/2); var x_mid = get_var(mid); if (x > x_mid) { low = mid + 1; } else if (x < x_mid) { high = mid - 1; } else { return mid; } if (low > high) return UINT_MAX; } } } unsigned degree_of(var x) const { unsigned pos = index_of(x); if (pos == UINT_MAX) return 0; return degree(pos); } // Given the subset S of variables that are smaller than x, // then return the maximal one. var max_smaller_than_core(var x) const { if (m_size == 0) return null_var; if (m_size < SMALL_MONOMIAL) { // use linear search for small monomials // search backwards since we usually ask for the degree of "big" variables unsigned i = m_size; while (i > 0) { --i; if (get_var(i) < x) return get_var(i); } return null_var; } else { // use binary search for big monomials int low = 0; int high = m_size-1; if (x <= get_var(low)) { return null_var; } if (x > get_var(high)) { return get_var(high); } if (x == get_var(high)) { SASSERT(high > 0); return get_var(high-1); } while (true) { SASSERT(0 <= low && high < static_cast(m_size)); SASSERT(get_var(low) < x); SASSERT(x < get_var(high)); SASSERT(low < high); if (high == low + 1) { SASSERT(get_var(low) < x); SASSERT(x < get_var(low+1)); return get_var(low); } SASSERT(high > low + 1); int mid = low + ((high - low)/2); SASSERT(low < mid && mid < high); var x_mid = get_var(mid); if (x_mid == x) { SASSERT(low < mid && mid < high && high < static_cast(m_size)); SASSERT(get_var(mid-1) < x && x == get_var(mid)); return get_var(mid-1); } if (x < x_mid) { high = mid; } else { SASSERT(x_mid < x); low = mid; } } } } var max_smaller_than(var x) const { SASSERT(x != null_var); var y = max_smaller_than_core(x); DEBUG_CODE({ bool found = false; for (unsigned i = 0; i < m_size; i++) { if (get_var(i) < x) { CTRACE("poly_bug", !(y != null_var && get_var(i) <= y), tout << "m: "; display(tout); tout << "\n"; tout << "x: " << x << "\n"; tout << "y: " << y << "\n"; tout << "i: " << i << "\n"; tout << "get_var(i): " << get_var(i) << "\n";); SASSERT(y != null_var && get_var(i) <= y); } if (get_var(i) == y) found = true; } SASSERT(y == null_var || (y < x && found)); }); return y; } std::ostream& display(std::ostream & out, display_var_proc const & proc = display_var_proc(), bool use_star = false) const { if (m_size == 0) { out << "1"; return out; } for (unsigned i = 0; i < m_size; i++) { if (i > 0) { if (use_star) out << "*"; else out << " "; } proc(out, get_var(i)); if (degree(i) > 1) out << "^" << degree(i); } return out; } void display_smt2(std::ostream & out, display_var_proc const & proc = display_var_proc()) const { if (m_size == 0) { out << "1"; } else if (m_size == 1 && degree(0) == 1) { proc(out, get_var(0)); } else { out << "(*"; for (unsigned i = 0; i < m_size; i++) { var x = get_var(i); unsigned k = degree(i); SASSERT(k > 0); for (unsigned j = 0; j < k; j++) { out << " "; proc(out, x); } } out << ")"; } } bool is_unit() const { return m_size == 0; } /** \brief Return true if the degree of every variable is even. */ bool is_power_of_two() const { for (unsigned i = 0; i < m_size; i++) { if (degree(i) % 2 == 1) return false; } return true; } bool is_square() const { for (unsigned i = 0; i < m_size; i++) { if (degree(i) % 2 != 0) return false; } return true; } void rename(unsigned sz, var const * xs) { for (unsigned i = 0; i < m_size; i++) { power & pw = m_powers[i]; pw.set_var(xs[pw.get_var()]); } sort(); m_hash = hash_core(m_size, m_powers); } }; inline void swap(monomial * & m1, monomial * & m2) { std::swap(m1, m2); } typedef chashtable monomial_table; /** \brief Mapping from monomials to positions. */ class monomial2pos { unsigned_vector m_m2pos; public: unsigned get(monomial const * m) { unsigned id = m->id(); m_m2pos.reserve(id+1, UINT_MAX); return m_m2pos[id]; } void reset(monomial const * m) { unsigned id = m->id(); SASSERT(id < m_m2pos.size()); m_m2pos[id] = UINT_MAX; } void set(monomial const * m, unsigned pos) { unsigned id = m->id(); m_m2pos.reserve(id+1, UINT_MAX); SASSERT(m_m2pos[id] == UINT_MAX); m_m2pos[id] = pos; } /** \brief Save the position of the monomials in p. */ template void set(Poly const * p) { unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { set(p->m(i), i); } } /** \brief Undo the effects of save_pos. */ template void reset(Poly const * p) { unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { reset(p->m(i)); } } }; #define TMP_INITIAL_CAPACITY 128 /** \brief Wrapper for temporary monomials. */ class tmp_monomial { monomial * m_ptr; unsigned m_capacity; //!< maximum number of arguments supported by m_ptr; monomial * allocate(unsigned capacity) { void * mem = memory::allocate(monomial::get_obj_size(capacity)); return new (mem) monomial(UINT_MAX, 0, nullptr, 0); } void deallocate(monomial * ptr, unsigned capacity) { memory::deallocate(ptr); } void increase_capacity(unsigned new_capacity) { SASSERT(new_capacity > m_capacity); deallocate(m_ptr, m_capacity); m_ptr = allocate(new_capacity); m_capacity = new_capacity; } void expand_capacity(unsigned new_capacity) { SASSERT(new_capacity > m_capacity); monomial * new_ptr = allocate(new_capacity); new_ptr->m_size = m_ptr->m_size; memcpy(new_ptr->m_powers, m_ptr->m_powers, sizeof(power)*m_ptr->m_size); deallocate(m_ptr, m_capacity); m_ptr = new_ptr; m_capacity = new_capacity; } public: tmp_monomial(): m_ptr(allocate(TMP_INITIAL_CAPACITY)), m_capacity(TMP_INITIAL_CAPACITY) { } ~tmp_monomial() { deallocate(m_ptr, m_capacity); } void init(unsigned sz, power const * pws) { if (sz > m_capacity) increase_capacity(sz * 2); SASSERT(sz < m_capacity); m_ptr->m_size = sz; if (sz == 0) return; memcpy(m_ptr->m_powers, pws, sizeof(power) * sz); } void reset() { m_ptr->m_size = 0; } unsigned size() const { return m_ptr->m_size; } void push_back(power const & pw) { if (m_ptr->m_size >= m_capacity) expand_capacity(m_ptr->m_size * 2); m_ptr->m_powers[m_ptr->m_size] = pw; m_ptr->m_size++; } monomial * get_ptr() { unsigned sz = m_ptr->m_size; m_ptr->m_hash = monomial::hash_core(sz, m_ptr->m_powers); return m_ptr; } void reserve(unsigned capacity) { if (capacity > m_capacity) increase_capacity(capacity * 2); } void set_size(unsigned sz) { SASSERT(sz <= m_capacity); m_ptr->m_size = sz; } void set_power(unsigned idx, power const & pw) { SASSERT(idx < m_capacity); m_ptr->m_powers[idx] = pw; } power const & get_power(unsigned idx) const { return m_ptr->m_powers[idx]; } power const * get_powers() const { return m_ptr->m_powers; } }; /** \brief Compare m1 and m2 using a lexicographical order Return - -1 if m1 <_lex m2, - 0 if m1 = m2, - 1 if m1 >_lex m2 The biggest variable dominates x3^3 > x3^2 x1^2 > x3 x2^2 x_1 > x1^3 Remark: in out representation the biggest variable is in the last position. */ int lex_compare(monomial const * m1, monomial const * m2) { if (m1 == m2) return 0; int sz1 = m1->size(); int sz2 = m2->size(); int idx1 = sz1 - 1; int idx2 = sz2 - 1; while (idx1 >= 0 && idx2 >= 0) { power const & pw1 = m1->get_power(idx1); power const & pw2 = m2->get_power(idx2); if (pw1.get_var() == pw2.get_var()) { if (pw1.degree() == pw2.degree()) { idx1--; idx2--; continue; } return pw1.degree() < pw2.degree() ? -1 : 1; } return pw1.get_var() > pw2.get_var() ? 1 : -1; } SASSERT(idx1 >= 0 || idx2 >= 0); SASSERT(idx1 < 0 || idx2 < 0); return idx1 < 0 ? -1 : 1; } /** Similar to lex_compare, but min_var is assumed to be the minimal variable. */ int lex_compare2(monomial const * m1, monomial const * m2, var min_var) { if (m1 == m2) return 0; int sz1 = m1->size(); int sz2 = m2->size(); int idx1 = sz1 - 1; int idx2 = sz2 - 1; unsigned min_var_degree1 = 0; unsigned min_var_degree2 = 0; while (idx1 >= 0 && idx2 >= 0) { power const & pw1 = m1->get_power(idx1); power const & pw2 = m2->get_power(idx2); if (pw1.get_var() == min_var) { min_var_degree1 = pw1.degree(); idx1--; if (pw2.get_var() == min_var) { min_var_degree2 = pw2.degree(); idx2--; } continue; } if (pw2.get_var() == min_var) { min_var_degree2 = pw2.degree(); idx2--; continue; } if (pw1.get_var() == pw2.get_var()) { if (pw1.degree() == pw2.degree()) { idx1--; idx2--; continue; } return pw1.degree() < pw2.degree() ? -1 : 1; } return pw1.get_var() > pw2.get_var() ? 1 : -1; } if (idx1 == idx2) { SASSERT(min_var_degree1 != min_var_degree2); return min_var_degree1 < min_var_degree2 ? -1 : 1; } return idx1 < 0 ? -1 : 1; } struct lex_lt2 { var m_min; lex_lt2(var m):m_min(m) {} bool operator()(monomial * m1, monomial * m2) const { TRACE("lex_bug", tout << "min: x" << m_min << "\n"; m1->display(tout); tout << "\n"; m2->display(tout); tout << "\n";); return lex_compare2(m1, m2, m_min) < 0; } }; /** \brief Compare m1 and m2 using a graded lexicographical order \see lex_compare */ int graded_lex_compare(monomial const * m1, monomial const * m2) { unsigned t1 = m1->total_degree(); unsigned t2 = m2->total_degree(); if (t1 == t2) return lex_compare(m1, m2); else return t1 < t2 ? -1 : 1; } /** \brief Compare submonomials m1[start1, end1) and m2[start2, end2) using reverse lexicographical order */ int rev_lex_compare(monomial const * m1, unsigned start1, unsigned end1, monomial const * m2, unsigned start2, unsigned end2) { SASSERT(end1 >= start1); SASSERT(end2 >= start2); unsigned idx1 = end1; unsigned idx2 = end2; while(idx1 > start1 && idx2 > start2) { --idx1; --idx2; power const & pw1 = m1->get_power(idx1); power const & pw2 = m2->get_power(idx2); if (pw1.get_var() == pw2.get_var()) { if (pw1.degree() == pw2.degree()) { // Remark: the submonomials have the same total degree, but they are not equal. So, idx1 > 0 and idx2 > 0. SASSERT(idx1 > start1 && idx2 > start2); continue; } return pw1.degree() > pw2.degree() ? -1 : 1; } return pw1.get_var() > pw2.get_var() ? -1 : 1; } SASSERT(idx1 == start1 || idx2 == start2); if (idx1 == start1) return idx2 == start2 ? 0 : -1; SASSERT(idx2 == start2 && idx1 != start1); return 1; } /** \brief Compare m1 and m2 using reverse lexicographical order. \see lex_compare */ int rev_lex_compare(monomial const * m1, monomial const * m2) { if (m1 == m2) return 0; return rev_lex_compare(m1, 0, m1->size(), m2, 0, m2->size()); } /** \brief Compare m1 and m2 using graded reverse lexicographical order. \see lex_compare */ int graded_rev_lex_compare(monomial const * m1, monomial const * m2) { unsigned t1 = m1->total_degree(); unsigned t2 = m2->total_degree(); if (t1 == t2) return rev_lex_compare(m1, m2); else return t1 < t2 ? -1 : 1; } struct graded_lex_gt { bool operator()(monomial const * m1, monomial const * m2) { return graded_lex_compare(m1, m2) < 0; } }; /** \brief */ class monomial_manager { unsigned m_ref_count; small_object_allocator * m_allocator; bool m_own_allocator; monomial_table m_monomials; id_gen m_mid_gen; // id generator for monomials unsigned m_next_var; monomial * m_unit; tmp_monomial m_mk_tmp; tmp_monomial m_tmp1; tmp_monomial m_tmp2; tmp_monomial m_tmp3; svector m_powers_tmp; public: monomial_manager(small_object_allocator * a = nullptr) { m_ref_count = 0; m_next_var = 0; if (a == nullptr) { m_allocator = alloc(small_object_allocator, "polynomial"); m_own_allocator = true; } else { m_allocator = a; m_own_allocator = false; } m_unit = mk_monomial(0, static_cast(nullptr)); inc_ref(m_unit); } ~monomial_manager() { dec_ref(m_unit); CTRACE("polynomial", !m_monomials.empty(), tout << "monomials leaked\n"; for (auto * m : m_monomials) { m->display(tout << m->id() << " " << m->ref_count() << " ") << "\n"; }); SASSERT(m_monomials.empty()); if (m_own_allocator) dealloc(m_allocator); } void inc_ref() { m_ref_count++; } void dec_ref() { SASSERT(m_ref_count > 0); m_ref_count--; if (m_ref_count == 0) dealloc(this); } small_object_allocator & allocator() { return *m_allocator; } var mk_var() { var r = m_next_var; m_next_var++; return r; } unsigned num_vars() const { return m_next_var; } bool is_valid(var x) const { return x < m_next_var; } void del(monomial * m) { unsigned obj_sz = monomial::get_obj_size(m->size()); m_monomials.erase(m); m_mid_gen.recycle(m->id()); m_allocator->deallocate(obj_sz, m); } void inc_ref(monomial * m) { m->inc_ref(); } void dec_ref(monomial * m) { m->dec_ref(); if (m->ref_count() == 0) del(m); } monomial * mk_unit() { return m_unit; } monomial * mk_monomial(tmp_monomial & tmp) { monomial * tmp_ptr = tmp.get_ptr(); monomial * & m = m_monomials.insert_if_not_there(tmp_ptr); if (m != tmp_ptr) return m; void * mem = m_allocator->allocate(monomial::get_obj_size(tmp_ptr->size())); unsigned id = m_mid_gen.mk(); monomial * r = new (mem) monomial(id, tmp_ptr->size(), tmp_ptr->get_powers(), tmp_ptr->hash()); m = r; SASSERT(m_monomials.contains(r)); SASSERT(*(m_monomials.find_core(r)) == r); return r; } monomial * mk_monomial(unsigned sz, power const * pws) { SASSERT(is_valid_power_product(sz, pws)); m_mk_tmp.init(sz, pws); return mk_monomial(m_mk_tmp); } monomial * convert(monomial const * src) { unsigned sz = src->size(); for (unsigned i = 0; i < sz; i++) { var x = src->get_var(i); while (x >= num_vars()) { mk_var(); } SASSERT(x < num_vars()); } return mk_monomial(src->size(), src->get_powers()); } monomial * mk_monomial(var x) { SASSERT(is_valid(x)); power pw(x, 1); return mk_monomial(1, &pw); } monomial * mk_monomial(var x, unsigned k) { if (k == 0) return m_unit; SASSERT(is_valid(x)); power pw(x, k); return mk_monomial(1, &pw); } monomial * mk_monomial(unsigned sz, var * xs) { if (sz == 0) return m_unit; if (sz == 1) return mk_monomial(xs[0]); m_powers_tmp.reset(); std::sort(xs, xs+sz); SASSERT(is_valid(xs[0])); m_powers_tmp.push_back(power(xs[0], 1)); for (unsigned i = 1; i < sz; i++) { var x = xs[i]; SASSERT(is_valid(x)); power & last = m_powers_tmp.back(); if (x == last.get_var()) last.degree()++; else m_powers_tmp.push_back(power(x, 1)); } return mk_monomial(m_powers_tmp.size(), m_powers_tmp.c_ptr()); } monomial * mul(unsigned sz1, power const * pws1, unsigned sz2, power const * pws2) { SASSERT(is_valid_power_product(sz1, pws1)); SASSERT(is_valid_power_product(sz2, pws2)); tmp_monomial & product_tmp = m_tmp1; product_tmp.reserve(sz1 + sz2); // product has at most sz1 + sz2 powers unsigned i1 = 0, i2 = 0; unsigned j = 0; while (true) { if (i1 == sz1) { // copy 2 for (; i2 < sz2; i2++, j++) product_tmp.set_power(j, pws2[i2]); break; } if (i2 == sz2) { // copy 1 for (; i1 < sz1; i1++, j++) product_tmp.set_power(j, pws1[i1]); break; } power const & pw1 = pws1[i1]; power const & pw2 = pws2[i2]; unsigned v1 = pw1.get_var(); unsigned v2 = pw2.get_var(); if (v1 == v2) { product_tmp.set_power(j, power(v1, pw1.degree() + pw2.degree())); i1++; i2++; } else if (v1 < v2) { product_tmp.set_power(j, pw1); i1++; } else { SASSERT(v1 > v2); product_tmp.set_power(j, pw2); i2++; } j++; } product_tmp.set_size(j); TRACE("monomial_mul_bug", tout << "before mk_monomial\n"; tout << "pws1: "; for (unsigned i = 0; i < sz1; i++) tout << pws1[i] << " "; tout << "\n"; tout << "pws2: "; for (unsigned i = 0; i < sz2; i++) tout << pws2[i] << " "; tout << "\n"; tout << "product_tmp: "; for (unsigned i = 0; i < product_tmp.size(); i++) tout << product_tmp.get_power(i) << " "; tout << "\n";); monomial * r = mk_monomial(product_tmp); TRACE("monomial_mul_bug", tout << "j: " << j << "\n"; tout << "r: "; r->display(tout); tout << "\n"; tout << "pws1: "; for (unsigned i = 0; i < sz1; i++) tout << pws1[i] << " "; tout << "\n"; tout << "pws2: "; for (unsigned i = 0; i < sz2; i++) tout << pws2[i] << " "; tout << "\n"; tout << "product_tmp: "; for (unsigned i = 0; i < product_tmp.size(); i++) tout << product_tmp.get_power(i) << " "; tout << "\n";); SASSERT(r->is_valid()); SASSERT(r->total_degree() == power_product_total_degree(sz1, pws1) + power_product_total_degree(sz2, pws2)); return r; } monomial * mul(monomial const * m1, monomial const * m2) { if (m1 == m_unit) return const_cast(m2); if (m2 == m_unit) return const_cast(m1); return mul(m1->size(), m1->get_powers(), m2->size(), m2->get_powers()); } template bool div_core(unsigned sz1, power const * pws1, unsigned sz2, power const * pws2, tmp_monomial & r) { if (STORE_RESULT) r.reserve(sz1); // r has at most sz1 arguments. unsigned i1 = 0; unsigned i2 = 0; unsigned j = 0; if (sz1 < sz2) return false; // pws2 does not divide pws1 while (true) { if (i2 == sz2) { if (STORE_RESULT) { for (; i1 < sz1; i1++, j++) r.set_power(j, pws1[i1]); r.set_size(j); } return true; } if (i1 == sz1) return false; // pws2 does not divide pws1 power const & pw1 = pws1[i1]; power const & pw2 = pws2[i2]; unsigned v1 = pw1.get_var(); unsigned v2 = pw2.get_var(); if (v1 == v2) { unsigned d1 = pw1.degree(); unsigned d2 = pw2.degree(); if (d1 < d2) return false; // pws2 does not divide pws1 if (STORE_RESULT) { if (d1 > d2) { r.set_power(j, power(v1, d1 - d2)); j++; } } i1++; i2++; } else if (v1 < v2) { if (STORE_RESULT) { r.set_power(j, pw1); j++; } i1++; } else { SASSERT(v1 > v2); return false; // pws2 does not divide pws1 } } } bool div(monomial const * m1, monomial const * m2) { if (m1->total_degree() < m2->total_degree()) return false; if (m1 == m2) return true; return div_core(m1->size(), m1->get_powers(), m2->size(), m2->get_powers(), m_tmp1); } bool div(monomial const * m1, monomial const * m2, monomial_ref & r) { if (m1->total_degree() < m2->total_degree()) return false; if (m1 == m2) { r = m_unit; return true; } tmp_monomial & div_tmp = m_tmp1; if (div_core(m1->size(), m1->get_powers(), m2->size(), m2->get_powers(), div_tmp)) { r = mk_monomial(div_tmp); return true; } return false; } /** \brief Compute the gcd of pws1 and pws2, store it in g, and pws1/g in r1, and pws2/g in r2 Return true if the gcd is not 1. If the result is false, then g, r1 and r2 should not be used. */ bool gcd_core(unsigned sz1, power const * pws1, unsigned sz2, power const * pws2, tmp_monomial & g, tmp_monomial & r1, tmp_monomial & r2) { g.reserve(std::min(sz1, sz2)); r1.reserve(sz2); // r1 has at most num_args2 arguments r2.reserve(sz1); // r2 has at most num_args1 arguments bool found = false; unsigned i1 = 0; unsigned i2 = 0; unsigned j1 = 0; unsigned j2 = 0; unsigned j3 = 0; while (true) { if (i1 == sz1) { if (found) { for (; i2 < sz2; i2++, j2++) r2.set_power(j2, pws2[i2]); r1.set_size(j1); r2.set_size(j2); g.set_size(j3); return true; } return false; } if (i2 == sz2) { if (found) { for (; i1 < sz1; i1++, j1++) r1.set_power(j1, pws1[i1]); r1.set_size(j1); r2.set_size(j2); g.set_size(j3); return true; } return false; } power const & pw1 = pws1[i1]; power const & pw2 = pws2[i2]; unsigned v1 = pw1.get_var(); unsigned v2 = pw2.get_var(); if (v1 == v2) { found = true; unsigned d1 = pw1.degree(); unsigned d2 = pw2.degree(); if (d1 > d2) { r1.set_power(j1, power(v1, d1 - d2)); g.set_power(j3, pw2); j1++; j3++; } else if (d2 > d1) { r2.set_power(j2, power(v2, d2 - d1)); g.set_power(j3, pw1); j2++; j3++; } else { SASSERT(d1 == d2); g.set_power(j3, pw1); j3++; } i1++; i2++; } else if (v1 < v2) { r1.set_power(j1, pw1); j1++; i1++; } else { SASSERT(v1 > v2); r2.set_power(j2, pw2); j2++; i2++; } } } monomial * gcd(monomial const * m1, monomial const * m2, monomial * & q1, monomial * & q2) { if (gcd_core(m1->size(), m1->get_powers(), m2->size(), m2->get_powers(), m_tmp1, m_tmp2, m_tmp3)) { q1 = mk_monomial(m_tmp2); q2 = mk_monomial(m_tmp3); return mk_monomial(m_tmp1); } else { // gcd is one q1 = const_cast(m2); q2 = const_cast(m1); return m_unit; } } bool unify(monomial const * m1, monomial const * m2, monomial * & q1, monomial * & q2) { if (gcd_core(m1->size(), m1->get_powers(), m2->size(), m2->get_powers(), m_tmp1, m_tmp2, m_tmp3)) { q1 = mk_monomial(m_tmp2); q2 = mk_monomial(m_tmp3); return true; } return false; } monomial * pw(monomial const * m, unsigned k) { if (k == 0) return m_unit; if (k == 1) return const_cast(m); unsigned sz = m->size(); tmp_monomial & pw_tmp = m_tmp1; pw_tmp.reserve(sz); for (unsigned i = 0; i < sz; i++) pw_tmp.set_power(i, power(m->get_var(i), m->degree(i)*k)); pw_tmp.set_size(sz); return mk_monomial(pw_tmp); } monomial * sqrt(monomial const * m) { SASSERT(m_unit != 0); if (m == m_unit) return m_unit; unsigned sz = m->size(); tmp_monomial & sqrt_tmp = m_tmp1; sqrt_tmp.reserve(sz); for (unsigned i = 0; i < sz; i++) { if (m->degree(i) % 2 == 1) return nullptr; sqrt_tmp.set_power(i, power(m->get_var(i), m->degree(i) / 2)); } sqrt_tmp.set_size(sz); return mk_monomial(sqrt_tmp); } /** \brief Return m/x^k */ monomial * div_x_k(monomial const * m, var x, unsigned k) { SASSERT(is_valid(x)); unsigned sz = m->size(); tmp_monomial & elim_tmp = m_tmp1; elim_tmp.reserve(sz); unsigned j = 0; for (unsigned i = 0; i < sz; i++) { power const & pw = m->get_power(i); var y = pw.get_var(); if (x != y) { elim_tmp.set_power(j, pw); j++; } else { SASSERT(k <= pw.degree()); unsigned d = pw.degree(); if (k < d) { elim_tmp.set_power(j, power(y, d - k)); j++; } } } elim_tmp.set_size(j); return mk_monomial(elim_tmp); } /** \brief Return m/x^n where n == m->degree_of(x) */ monomial * div_x(monomial const * m, var x) { SASSERT(is_valid(x)); unsigned sz = m->size(); tmp_monomial & elim_tmp = m_tmp1; elim_tmp.reserve(sz); unsigned j = 0; for (unsigned i = 0; i < sz; i++) { power const & pw = m->get_power(i); var y = pw.get_var(); if (x != y) { elim_tmp.set_power(j, pw); j++; } } elim_tmp.set_size(j); return mk_monomial(elim_tmp); } monomial * derivative(monomial const * m, var x) { SASSERT(is_valid(x)); unsigned sz = m->size(); tmp_monomial & derivative_tmp = m_tmp1; derivative_tmp.reserve(sz); unsigned j = 0; for (unsigned i = 0; i < sz; i++) { power const & pw = m->get_power(i); var y = pw.get_var(); if (x == y) { unsigned d = pw.degree(); if (d > 1) { derivative_tmp.set_power(j, power(y, d-1)); j++; } } else { derivative_tmp.set_power(j, pw); j++; } } derivative_tmp.set_size(j); return mk_monomial(derivative_tmp); } void rename(unsigned sz, var const * xs) { SASSERT(m_ref_count <= 1); SASSERT(sz == num_vars()); DEBUG_CODE({ // check whether xs is really a permutation svector found; found.resize(num_vars(), false); for (unsigned i = 0; i < sz; i++) { SASSERT(xs[i] < num_vars()); SASSERT(!found[xs[i]]); found[xs[i]] = true; } }); monomial_table new_table; monomial_table::iterator it = m_monomials.begin(); monomial_table::iterator end = m_monomials.end(); for (; it != end; ++it) { monomial * m = *it; m->rename(sz, xs); SASSERT(!new_table.contains(m)); new_table.insert(m); } m_monomials.swap(new_table); } }; /** We maintain the following invariant: The first monomial m of every non-zero polynomial p contains: 1) the maximal variable x of p, 2) and the degree of x in m is maximal in p. */ class polynomial { public: typedef manager::numeral numeral; private: unsigned m_ref_count; unsigned m_id:31; unsigned m_lex_sorted:1; unsigned m_size; numeral * m_as; monomial ** m_ms; void lex_sort(unsigned start, unsigned end, var x, vector & buckets, unsigned_vector & p) { SASSERT(end > start); unsigned max_degree = 0; for (unsigned i = start, j = 0; i < end; i++, j++) { monomial * m = m_ms[i]; unsigned d = m->degree_of(x); buckets.reserve(d+1); buckets[d].push_back(j); if (d > max_degree) max_degree = d; } p.reset(); unsigned i = max_degree + 1; while (i > 0) { --i; p.append(buckets[i]); buckets[i].reset(); } SASSERT(p.size() == end - start); apply_permutation(p.size(), m_as + start, p.c_ptr()); apply_permutation_core(p.size(), m_ms + start, p.c_ptr()); // p is not needed anymore after this command i = start; while (i < end) { monomial * m = m_ms[i]; unsigned d = m->degree_of(x); if (d == 0) { // x does not occur in m // since we sorted, x should not in the rest // we should find the maximal variable variable smaller than x in [i, end) var y = max_smaller_than(i, end, x); if (y != null_var) lex_sort(i, end, y, buckets, p); return; } unsigned j = i + 1; for (; j < end; j++) { unsigned d_j = m_ms[j]->degree_of(x); SASSERT(d_j <= d); // it is sorted if (d_j < d) break; } SASSERT(j == end || m_ms[j]->degree_of(x) < d); // sort interval [i, j) using the maximal variable y smaller than x if (j > i + 1) { // only need to sort if the interval has more than one element. var y = max_smaller_than(i, j, x); if (y != null_var) lex_sort(i, j, y, buckets, p); } i = j; } } public: unsigned ref_count() const { return m_ref_count; } void inc_ref() { m_ref_count++; } void dec_ref() { SASSERT(m_ref_count > 0); m_ref_count--; } static unsigned get_obj_size(unsigned n) { return sizeof(polynomial) + n * (sizeof(numeral) + sizeof(monomial*)); } /** \brief Partial order used to implement the polynomial invariant that guarantees that the first monomial contains the maximal variable in the polynomial, and it occurs with maximal degree. Return true if m1 > m2 in this partial order. */ static bool po_gt(monomial const * m1, monomial const * m2) { if (m1->size() == 0) return false; if (m2->size() == 0) return true; if (m1->max_var() < m2->max_var()) return false; if (m1->max_var() > m2->max_var()) return true; SASSERT(m1->max_var() == m2->max_var()); return m1->max_var_degree() > m2->max_var_degree(); } // swap monomials at positions 0 and pos void swap_0_pos(unsigned pos) { if (pos != 0) { swap(m_as[0], m_as[pos]); std::swap(m_ms[0], m_ms[pos]); } } polynomial(mpzzp_manager & nm, unsigned id, unsigned sz, numeral * as, monomial * const * ms, numeral * as_mem, monomial ** ms_mem): m_ref_count(0), m_id(id), m_lex_sorted(false), m_size(sz), m_as(as_mem), m_ms(ms_mem) { if (sz > 0) { unsigned max_pos = 0; for (unsigned i = 0; i < sz; i++) { new (m_as + i) numeral(); // initialize the big number at m_as[i] swap(m_as[i], as[i]); SASSERT(ms[i]->ref_count() > 0); m_ms[i] = ms[i]; if (i > 0 && po_gt(m_ms[i], m_ms[max_pos])) max_pos = i; } swap_0_pos(max_pos); } } // Return the maximal variable y occurring in [m_ms + start, m_ms + end) that is smaller than x var max_smaller_than(unsigned start, unsigned end, var x) { var max = null_var; for (unsigned i = start; i < end; i++) { var y = m_ms[i]->max_smaller_than(x); if (y != null_var && (max == null_var || y > max)) max = y; } return max; } bool lex_sorted() const { return m_lex_sorted; } // Put monomials in lexicographical order void lex_sort(vector & buckets, unsigned_vector & p, mpzzp_manager & nm) { if (m_lex_sorted) return; if (size() <= 1) { m_lex_sorted = true; return; } lex_sort(0, size(), m(0)->max_var(), buckets, p); m_lex_sorted = true; DEBUG_CODE({ for (unsigned i = 0; i < m_size - 1; i++) { CTRACE("poly_bug", lex_compare(m_ms[i], m_ms[i+1]) <= 0, tout << "i: " << i << "\npoly: "; display(tout, nm); tout << "\n";); SASSERT(lex_compare(m_ms[i], m_ms[i+1]) > 0); } }); } /** \brief Make sure that the first monomial contains the maximal variable x occurring in the polynomial, and x occurs with maximal degree. */ void make_first_maximal() { if (m_size <= 1) return; unsigned max_pos = 0; for (unsigned i = 1; i < m_size; i++) { if (po_gt(m_ms[i], m_ms[max_pos])) max_pos = i; } swap_0_pos(max_pos); m_lex_sorted = false; } /** \brief Return the position of the maximal monomial with respect to graded lexicographical order. Return UINT_MAX if polynomial is zero. */ unsigned graded_lex_max_pos() const { if (m_size == 0) return UINT_MAX; unsigned max_pos = 0; for (unsigned i = 1; i < m_size; i++) { if (graded_lex_compare(m_ms[i], m_ms[max_pos]) > 0) max_pos = i; } return max_pos; } /** \brief Return the position of the minimal monomial with respect to graded lexicographical order. Return UINT_MAX if polynomial is zero. */ unsigned graded_lex_min_pos() const { if (m_size == 0) return UINT_MAX; unsigned min_pos = 0; for (unsigned i = 1; i < m_size; i++) { if (graded_lex_compare(m_ms[i], m_ms[min_pos]) < 0) min_pos = i; } return min_pos; } unsigned id() const { return m_id; } unsigned size() const { return m_size; } monomial * m(unsigned idx) const { SASSERT(idx < size()); return m_ms[idx]; } monomial *const* begin() const { return m_ms; } monomial *const* end() const { return m_ms + size(); } numeral const & a(unsigned idx) const { SASSERT(idx < size()); return m_as[idx]; } numeral & a(unsigned idx) { SASSERT(idx < size()); return m_as[idx]; } numeral const * as() const { return m_as; } bool is_zero() const { return m_size == 0; } std::ostream& display(std::ostream & out, mpzzp_manager & nm, display_var_proc const & proc = display_var_proc(), bool use_star = false) const { if (is_zero()) { out << "0"; return out; } for (unsigned i = 0; i < m_size; i++) { numeral const & a_i = a(i); _scoped_numeral abs_a_i(nm); nm.set(abs_a_i, a_i); nm.abs(abs_a_i); numeral const & a_prime = abs_a_i; if (i > 0) { if (nm.is_neg(a_i)) out << " - "; else out << " + "; } else { if (nm.is_neg(a_i)) out << "- "; } if (m(i)->is_unit()) { out << nm.to_string(a_prime); } else if (nm.is_one(a_prime)) { m(i)->display(out, proc, use_star); } else { out << nm.to_string(a_prime); if (use_star) out << "*"; else out << " "; m(i)->display(out, proc, use_star); } } return out; } static void display_num_smt2(std::ostream & out, mpzzp_manager & nm, numeral const & a) { if (nm.is_neg(a)) { out << "(- "; _scoped_numeral abs_a(nm); nm.set(abs_a, a); nm.neg(abs_a); nm.display(out, abs_a); out << ")"; } else { nm.display(out, a); } } void display_mon_smt2(std::ostream & out, mpzzp_manager & nm, display_var_proc const & proc, unsigned i) const { SASSERT(i < m_size); monomial const * m_i = m(i); numeral const & a_i = a(i); if (m_i->size() == 0) { display_num_smt2(out, nm, a_i); } else if (nm.is_one(a_i)) { if (m_i->size() == 1) { m_i->display_smt2(out, proc); } else { out << "(* "; m_i->display_smt2(out, proc); out << ")"; } } else { out << "(* "; display_num_smt2(out, nm, a_i); out << " "; m_i->display_smt2(out, proc); out << ")"; } } void display_smt2(std::ostream & out, mpzzp_manager & nm, display_var_proc const & proc = display_var_proc()) const { if (m_size == 0) { out << "0"; } else if (m_size == 1) { display_mon_smt2(out, nm, proc, 0); } else { out << "(+"; for (unsigned i = 0; i < m_size; i++) { out << " "; display_mon_smt2(out, nm, proc, i); } out << ")"; } } void display(std::ostream & out, mpzzp_manager & nm, bool use_star) const { display(out, nm, display_var_proc(), use_star); } }; manager::factors::factors(manager & _m):m_manager(_m), m_total_factors(0) { m().m().set(m_constant, 1); } manager::factors::~factors() { reset(); m().m().del(m_constant); } void manager::factors::reset() { for (unsigned i = 0; i < m_factors.size(); ++ i) { m().dec_ref(m_factors[i]); } m_factors.reset(); m_degrees.reset(); m_total_factors = 0; m().m().set(m_constant, 1); } void manager::factors::push_back(polynomial * p, unsigned degree) { SASSERT(p != 0 && degree > 0); m_factors.push_back(p); m_degrees.push_back(degree); m_total_factors += degree; m().inc_ref(p); } void manager::factors::multiply(polynomial_ref & out) const { if (m_factors.empty()) { out = m().mk_const(m_constant); } else { // multiply the factors for (unsigned i = 0; i < m_factors.size(); ++ i) { polynomial_ref current(m_factors[i], m()); if (m_degrees[i] > 1) { m().pw(current, m_degrees[i], current); } if (i == 0) { out = current; } else { out = m().mul(out, current); } } // multiply the constant out = m().mul(m_constant, out); } } void manager::factors::display(std::ostream & out) const { out << m().m().to_string(get_constant()); for (unsigned i = 0; i < m_factors.size(); ++ i) { out << " * ("; m_manager.display(out, m_factors[i]); out << ")^" << m_degrees[i]; } } void manager::factors::set_constant(numeral const & constant) { m().m().set(m_constant, constant); } void manager::factors::set_degree(unsigned i, unsigned degree) { SASSERT(i > 0); m_total_factors -= m_degrees[i]; m_total_factors += m_degrees[i] = degree; } polynomial_ref manager::factors::operator[](unsigned i) const { return polynomial_ref(m_factors[i], m()); } unsigned manager::id(monomial const * m) { return m->id(); } unsigned manager::id(polynomial const * p) { return p->id(); } bool manager::is_unit(monomial const * m) { return m->size() == 0; } bool manager::is_zero(polynomial const * p) { return p->size() == 0; } bool manager::is_const(polynomial const * p) { return is_zero(p) || (p->size() == 1 && is_unit(p->m(0))); } bool manager::is_univariate(monomial const * m) { return m->size() <= 1; } bool manager::is_univariate(polynomial const * p) { unsigned sz = p->size(); if (is_const(p)) return true; monomial * m = p->m(0); var x = max_var(p); for (unsigned i = 0; i < sz; i++) { m = p->m(i); if (m->size() == 1 && m->get_var(0) == x) continue; if (m->size() == 0) continue; return false; } return true; } unsigned manager::size(polynomial const * p) { return p->size(); } polynomial::numeral const & manager::coeff(polynomial const * p, unsigned i) { return p->a(i); } polynomial::numeral const & manager::univ_coeff(polynomial const * p, unsigned k) { static numeral zero(0); SASSERT(is_univariate(p)); unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { if (p->m(i)->total_degree() == k) return p->a(i); } return zero; } monomial * manager::get_monomial(polynomial const * p, unsigned i) { return p->m(i); } unsigned manager::total_degree(monomial const * m) { return m->total_degree(); } unsigned manager::size(monomial const * m) { return m->size(); } var manager::get_var(monomial const * m, unsigned i) { return m->get_var(i); } unsigned manager::degree(monomial const * m, unsigned i) { return m->degree(i); } unsigned manager::degree_of(monomial const * m, var x) { return m->degree_of(x); } bool manager::is_linear(monomial const * m) { return m->size() == 0 || (m->size() == 1 && m->degree(0) == 1); } bool manager::is_linear(polynomial const * p) { for (monomial* m : *p) if (!is_linear(m)) return false; return true; } unsigned manager::degree(polynomial const * p, var x) { unsigned sz = p->size(); if (sz == 0) return 0; monomial * m = p->m(0); unsigned msz = m->size(); if (msz == 0) return 0; // see polynomial invariant. if (m->get_var(msz - 1) == x) { // x is the maximal variable in p return m->degree(msz - 1); } unsigned r = 0; // use slow (linear) scan. for (unsigned i = 0; i < sz; i++) { unsigned d = p->m(i)->degree_of(x); if (d > r) r = d; } return r; } var manager::max_var(polynomial const * p) { if (p->size() == 0) return null_var; monomial * m = p->m(0); return m->max_var(); } unsigned manager::total_degree(polynomial const * p) { // use linear scan... if it turns out to be too slow, I should cache total_degree in polynomial unsigned r = 0; unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { unsigned t = p->m(i)->total_degree(); if (t > r) r = t; } return r; } struct manager::imp { typedef upolynomial::manager up_manager; typedef mpzzp_manager numeral_manager; // refine numeral_manager typedef _scoped_numeral scoped_numeral; typedef _scoped_numeral_vector scoped_numeral_vector; reslimit& m_limit; manager & m_wrapper; numeral_manager m_manager; up_manager m_upm; monomial_manager * m_monomial_manager; polynomial_vector m_polynomials; id_gen m_pid_gen; // id generator for polynomials del_eh * m_del_eh; polynomial * m_zero; numeral m_zero_numeral; polynomial * m_unit_poly; monomial2pos m_m2pos; tmp_monomial m_tmp1; numeral_vector m_rat2numeral; numeral_vector m_tmp_linear_as; monomial_vector m_tmp_linear_ms; unsigned_vector m_degree2pos; bool m_use_sparse_gcd; bool m_use_prs_gcd; // Debugging method: check if the coefficients of p are in the numeral_manager. bool consistent_coeffs(polynomial const * p) { scoped_numeral a(m_manager); unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { m_manager.set(a, p->a(i)); SASSERT(m_manager.eq(a, p->a(i))); } return true; } /** \brief Divide as by the GCD of as. Return true, if the GCD is not 1. */ static bool normalize_numerals(numeral_manager & m, numeral_vector & as) { unsigned sz = as.size(); if (sz == 0) return false; scoped_numeral g(m); m.gcd(as.size(), as.c_ptr(), g); if (m.is_one(g)) return false; SASSERT(m.is_pos(g)); for (unsigned i = 0; i < sz; i++) { m.div(as[i], g, as[i]); } return true; } /** \brief Som-of-monomials buffer. This a temporary datastructure for building polynomials. The following idiom should be used: Invoke add(...), addmul(...) several times, and then invoke mk() to obtain the final polynomial. */ class som_buffer { imp * m_owner; monomial2pos m_m2pos; numeral_vector m_tmp_as; monomial_vector m_tmp_ms; /** \brief Remove zeros from m_tmp_as & m_tmp_ms. The reference counters of eliminated m_tmp_ms are decremented. m_m2pos is reset. That is for every m in m_tmp_ms, m_m2pos[m->id()] == UINT_MAX */ void remove_zeros(bool normalize) { numeral_manager & mng = m_owner->m_manager; SASSERT(m_tmp_ms.size() == m_tmp_as.size()); unsigned sz = m_tmp_ms.size(); unsigned j = 0; for (unsigned i = 0; i < sz; i++) { monomial * m = m_tmp_ms[i]; m_m2pos.reset(m); if (mng.is_zero(m_tmp_as[i])) { mng.reset(m_tmp_as[i]); m_owner->dec_ref(m_tmp_ms[i]); } else { if (i != j) { SASSERT(m_tmp_ms[j] != m); m_tmp_ms[j] = m; swap(m_tmp_as[j], m_tmp_as[i]); } j++; } } DEBUG_CODE({ for (unsigned i = j; i < sz; i++) { SASSERT(mng.is_zero(m_tmp_as[i])); } }); m_tmp_as.shrink(j); m_tmp_ms.shrink(j); if (normalize) { normalize_numerals(mng, m_tmp_as); } } public: som_buffer():m_owner(nullptr) {} void reset() { if (empty()) return; numeral_manager & mng = m_owner->m_manager; SASSERT(m_tmp_ms.size() == m_tmp_as.size()); unsigned sz = m_tmp_ms.size(); for (unsigned i = 0; i < sz; i++) { monomial * m = m_tmp_ms[i]; m_m2pos.reset(m); mng.reset(m_tmp_as[i]); m_owner->dec_ref(m_tmp_ms[i]); } m_tmp_as.reset(); m_tmp_ms.reset(); } void set_owner(imp * o) { m_owner = o; } unsigned size() const { return m_tmp_ms.size(); } bool empty() const { return m_tmp_ms.empty(); } monomial * m(unsigned i) const { return m_tmp_ms[i]; } numeral const & a(unsigned i) const { return m_tmp_as[i]; } /** \brief Return the position of the maximal monomial with respect to graded lexicographical order. Return UINT_MAX if empty. */ unsigned graded_lex_max_pos() const { numeral_manager & mng = m_owner->m_manager; unsigned max_pos = UINT_MAX; unsigned sz = m_tmp_as.size(); for (unsigned i = 0; i < sz; i++) { if (!mng.is_zero(m_tmp_as[i])) { if (max_pos == UINT_MAX) { max_pos = i; } else { if (graded_lex_compare(m_tmp_ms[i], m_tmp_ms[max_pos]) > 0) max_pos = i; } } } return max_pos; } /** \brief Store a*m*p into the buffer. m_m2pos is updated with the position of the monomials in m_tmp_ms. The reference counter of new monomials added into the buffer is increased. */ template void addmul_core(numeral const & a, monomial const * m, PolyType const * p) { numeral_manager & mng = m_owner->m_manager; if (mng.is_zero(a)) return; unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { if (CheckZeros && mng.is_zero(p->a(i))) continue; monomial * m2 = p->m(i); m2 = m_owner->mul(m, m2); unsigned pos = m_m2pos.get(m2); if (pos == UINT_MAX) { m_m2pos.set(m2, m_tmp_ms.size()); m_tmp_ms.push_back(m2); m_owner->inc_ref(m2); m_tmp_as.push_back(numeral()); mng.mul(a, p->a(i), m_tmp_as.back()); } else { mng.addmul(m_tmp_as[pos], a, p->a(i), m_tmp_as[pos]); } } } void addmul(numeral const & a, monomial const * m, polynomial const * p) { return addmul_core(a, m, p); } void addmul(numeral const & a, monomial const * m, som_buffer const * p) { return addmul_core(a, m, p); } void addmul(numeral const & a, monomial const * m, som_buffer const & p) { return addmul(a, m, &p); } void addmul(numeral const & a, som_buffer const * p) { return addmul(a, m_owner->mk_unit(), p); } void addmul(numeral const & a, som_buffer const & p) { return addmul(a, &p); } void addmul(monomial const * m, som_buffer const * p) { numeral one(1); return addmul(one, m, p); } void addmul(monomial const * m, som_buffer const & p) { return addmul(m, &p); } /** \brief Store p into the buffer. m_m2pos is updated with the position of the monomials in m_tmp_ms. The reference counter of new monomials added into the buffer is increased. */ void add(polynomial const * p) { numeral_manager & mng = m_owner->m_manager; unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { monomial * m2 = p->m(i); unsigned pos = m_m2pos.get(m2); if (pos == UINT_MAX) { m_m2pos.set(m2, m_tmp_ms.size()); m_tmp_ms.push_back(m2); m_owner->inc_ref(m2); m_tmp_as.push_back(numeral()); mng.set(m_tmp_as.back(), p->a(i)); } else { mng.add(m_tmp_as[pos], p->a(i), m_tmp_as[pos]); } } } /** \brief Add 'a*m' into m_tmp_as and m_tmp_ms. m_m2pos is updated with the position of the monomials in m_tmp_ms. The reference counter of m is increased. */ void add(numeral const & a, monomial * m) { numeral_manager & mng = m_owner->m_manager; if (mng.is_zero(a)) return; unsigned pos = m_m2pos.get(m); if (pos == UINT_MAX) { m_m2pos.set(m, m_tmp_ms.size()); m_owner->inc_ref(m); m_tmp_ms.push_back(m); m_tmp_as.push_back(numeral()); mng.set(m_tmp_as.back(), a); } else { mng.add(m_tmp_as[pos], a, m_tmp_as[pos]); } } /** \brief Add 'a' (that is, a*m_unit) into m_tmp_as and m_tmp_ms. m_m2pos is updated with the position of the monomials in m_tmp_ms. The reference counter of m_unit is increased. */ void add(numeral const & a) { add(a, m_owner->mk_unit()); } void sort_graded_lex() { std::sort(m_tmp_ms.begin(), m_tmp_ms.end(), graded_lex_gt()); numeral_vector new_as; unsigned sz = m_tmp_ms.size(); for (unsigned i = 0; i < sz; i++) { monomial * m = m_tmp_ms[i]; unsigned pos = m_m2pos.get(m); new_as.push_back(numeral()); swap(new_as.back(), m_tmp_as[pos]); m_m2pos.reset(m); m_m2pos.set(m, i); } m_tmp_as.swap(new_as); } // For each monomial m // If m contains x^k and k >= x2d[x] and x2d[x] != 0, then set coefficient of m to 0. void mod_d(var2degree const & x2d) { numeral_manager & mng = m_owner->m_manager; unsigned sz = m_tmp_ms.size(); for (unsigned i = 0; i < sz; i++) { if (mng.is_zero(m_tmp_as[i])) continue; monomial * m = m_tmp_ms[i]; unsigned msz = m->size(); unsigned j; for (j = 0; j < msz; j++) { var x = m->get_var(j); unsigned dx = x2d.degree(x); if (dx == 0) continue; if (m->degree(j) >= dx) break; } if (j < msz) { mng.reset(m_tmp_as[i]); } } } polynomial * mk(bool normalize = false) { remove_zeros(normalize); polynomial * p = m_owner->mk_polynomial_core(m_tmp_as.size(), m_tmp_as.c_ptr(), m_tmp_ms.c_ptr()); m_tmp_as.reset(); m_tmp_ms.reset(); return p; } void display(std::ostream & out) const { SASSERT(m_tmp_ms.size() == m_tmp_as.size()); numeral_manager & mng = m_owner->m_manager; for (unsigned i = 0; i < m_tmp_as.size(); i++) { if (i > 0) out << " + "; out << mng.to_string(m_tmp_as[i]) << "*"; m_tmp_ms[i]->display(out); } out << "\n"; } }; class som_buffer_vector { imp * m_owner; ptr_vector m_buffers; void ensure_capacity(unsigned sz) { unsigned old_sz = m_buffers.size(); for (unsigned i = old_sz; i < sz; i++) { som_buffer * new_buffer = alloc(som_buffer); if (m_owner) new_buffer->set_owner(m_owner); m_buffers.push_back(new_buffer); } SASSERT(m_buffers.size() >= sz); } public: som_buffer_vector() { m_owner = nullptr; } ~som_buffer_vector() { clear(); } void clear() { reset(); unsigned sz = m_buffers.size(); for (unsigned i = 0; i < sz; i++) { dealloc(m_buffers[i]); } m_buffers.reset(); } void set_owner(imp * owner) { SASSERT(m_owner == owner || m_owner == 0); if (m_owner == nullptr) { m_owner = owner; unsigned sz = m_buffers.size(); for (unsigned i = 0; i < sz; i++) { m_buffers[i]->set_owner(m_owner); } } } som_buffer * operator[](unsigned idx) { ensure_capacity(idx+1); return m_buffers[idx]; } void reset(unsigned sz) { if (sz > m_buffers.size()) sz = m_buffers.size(); for (unsigned i = 0; i < sz; i++) { m_buffers[i]->reset(); } } void reset() { reset(m_buffers.size()); } }; /** \brief Cheap version of som_buffer. In this buffer, each monomial can be added at most once. */ class cheap_som_buffer { imp * m_owner; numeral_vector m_tmp_as; monomial_vector m_tmp_ms; public: cheap_som_buffer():m_owner(nullptr) {} void set_owner(imp * o) { m_owner = o; } bool empty() const { return m_tmp_ms.empty(); } /** \brief Add a*m to the buffer, the content of a is reset. */ void add_reset(numeral & a, monomial * m) { SASSERT(std::find(m_tmp_ms.begin(), m_tmp_ms.end(), m) == m_tmp_ms.end()); numeral_manager & mng = m_owner->m_manager; if (mng.is_zero(a)) return; m_tmp_as.push_back(numeral()); swap(m_tmp_as.back(), a); m_owner->inc_ref(m); m_tmp_ms.push_back(m); } /** \brief Add a*m to the buffer. */ void add(numeral const & a, monomial * m) { SASSERT(std::find(m_tmp_ms.begin(), m_tmp_ms.end(), m) == m_tmp_ms.end()); numeral_manager & mng = m_owner->m_manager; if (mng.is_zero(a)) return; m_tmp_as.push_back(numeral()); mng.set(m_tmp_as.back(), a); m_owner->inc_ref(m); m_tmp_ms.push_back(m); } /** \brief Add a*m*p to the buffer. */ void addmul(numeral const & a, monomial const * m, polynomial const * p) { numeral_manager & mng = m_owner->m_manager; if (mng.is_zero(a)) return; unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { monomial * m2 = p->m(i); m2 = m_owner->mul(m, m2); // m2 is not in m_tmp_ms SASSERT(std::find(m_tmp_ms.begin(), m_tmp_ms.end(), m2) == m_tmp_ms.end()); m_owner->inc_ref(m2); m_tmp_ms.push_back(m2); m_tmp_as.push_back(numeral()); mng.mul(a, p->a(i), m_tmp_as.back()); } } bool normalize() { return normalize_numerals(m_owner->m_manager, m_tmp_as); } void reset() { if (empty()) return; numeral_manager & mng = m_owner->m_manager; unsigned sz = m_tmp_ms.size(); for (unsigned i = 0; i < sz; i++) { mng.del(m_tmp_as[i]); m_owner->dec_ref(m_tmp_ms[i]); } m_tmp_as.reset(); m_tmp_ms.reset(); } polynomial * mk() { polynomial * new_p = m_owner->mk_polynomial_core(m_tmp_as.size(), m_tmp_as.c_ptr(), m_tmp_ms.c_ptr()); m_tmp_as.reset(); m_tmp_ms.reset(); return new_p; } }; som_buffer m_som_buffer; som_buffer m_som_buffer2; cheap_som_buffer m_cheap_som_buffer; cheap_som_buffer m_cheap_som_buffer2; void init() { m_del_eh = nullptr; m_som_buffer.set_owner(this); m_som_buffer2.set_owner(this); m_cheap_som_buffer.set_owner(this); m_cheap_som_buffer2.set_owner(this); m_zero = mk_polynomial_core(0, nullptr, nullptr); m().set(m_zero_numeral, 0); inc_ref(m_zero); numeral one(1); m_unit_poly = mk_const_core(one); inc_ref(m_unit_poly); m_use_sparse_gcd = true; m_use_prs_gcd = false; } imp(reslimit& lim, manager & w, unsynch_mpz_manager & m, monomial_manager * mm): m_limit(lim), m_wrapper(w), m_manager(m), m_upm(lim, m) { if (mm == nullptr) mm = alloc(monomial_manager); m_monomial_manager = mm; m_monomial_manager->inc_ref(); init(); } imp(reslimit& lim, manager & w, unsynch_mpz_manager & m, small_object_allocator * a): m_limit(lim), m_wrapper(w), m_manager(m), m_upm(lim, m) { m_monomial_manager = alloc(monomial_manager, a); m_monomial_manager->inc_ref(); init(); } ~imp() { dec_ref(m_zero); dec_ref(m_unit_poly); m_som_buffer.reset(); m_som_buffer2.reset(); m_cheap_som_buffer.reset(); m_cheap_som_buffer2.reset(); m_manager.del(m_zero_numeral); m_mgcd_iterpolators.flush(); m_mgcd_skeletons.reset(); CTRACE("polynomial", !m_polynomials.empty(), tout << "leaked polynomials\n"; for (auto* p : m_polynomials) { if (p) p->display(tout, m_manager) << "\n"; }); m_polynomials.reset(); SASSERT(m_polynomials.empty()); m_iccp_ZpX_buffers.clear(); m_monomial_manager->dec_ref(); } void checkpoint() { if (!m_limit.inc()) { throw polynomial_exception(Z3_CANCELED_MSG); } } mpzzp_manager & m() const { return const_cast(this)->m_manager; } manager & pm() const { return m_wrapper; } up_manager & upm() { return m_upm; } monomial_manager & mm() const { return *m_monomial_manager; } var mk_var() { return mm().mk_var(); } unsigned num_vars() const { return mm().num_vars(); } bool is_valid(var x) const { return mm().is_valid(x); } void add_del_eh(del_eh * eh) { eh->m_next = m_del_eh; m_del_eh = eh; } void remove_del_eh(del_eh * eh) { SASSERT(eh != 0); SASSERT(m_del_eh != 0); if (m_del_eh == eh) { m_del_eh = m_del_eh->m_next; } else { del_eh * curr = m_del_eh; while (curr) { if (curr->m_next == eh) { curr->m_next = curr->m_next->m_next; return; } curr = curr->m_next; } UNREACHABLE(); } } void del(polynomial * p) { TRACE("polynomial", tout << "deleting: "; p->display(tout, m_manager); tout << "\n";); if (m_del_eh != nullptr) { del_eh * curr = m_del_eh; do { (*curr)(p); curr = curr->m_next; } while (curr != nullptr); } unsigned sz = p->size(); unsigned obj_sz = polynomial::get_obj_size(sz); for (unsigned i = 0; i < sz; i++) { m_manager.del(p->a(i)); dec_ref(p->m(i)); } unsigned id = p->id(); m_pid_gen.recycle(id); m_polynomials[id] = 0; mm().allocator().deallocate(obj_sz, p); } void inc_ref(monomial * m) { mm().inc_ref(m); } void dec_ref(monomial * m) { mm().dec_ref(m); } void inc_ref(polynomial * p) { p->inc_ref(); } void dec_ref(polynomial * p) { p->dec_ref(); if (p->ref_count() == 0) del(p); } vector m_lex_sort_buckets; unsigned_vector m_lex_sort_permutation; void lex_sort(polynomial const * p) { const_cast(p)->lex_sort(m_lex_sort_buckets, m_lex_sort_permutation, m_manager); } struct poly_khasher { unsigned operator()(polynomial const * p) const { return 17; } }; struct poly_chasher { unsigned operator()(polynomial const * p, unsigned idx) const { return hash_u_u(p->m(idx)->hash(), numeral_manager::hash(p->a(idx))); } }; unsigned hash(polynomial const * p) { if (p->size() == 0) return 31; lex_sort(const_cast(p)); return get_composite_hash(p, p->size(), poly_khasher(), poly_chasher()); } polynomial * mk_polynomial_core(unsigned sz, numeral * as, monomial * const * ms) { unsigned obj_sz = polynomial::get_obj_size(sz); void * mem = mm().allocator().allocate(obj_sz); void * as_mem = static_cast(mem) + sizeof(polynomial); void * ms_mem = static_cast(as_mem) + sizeof(numeral)*sz; unsigned id = m_pid_gen.mk(); polynomial * p = new (mem) polynomial(m_manager, id, sz, as, ms, static_cast(as_mem), static_cast(ms_mem)); m_polynomials.reserve(id+1); SASSERT(m_polynomials[id] == 0); m_polynomials[id] = p; return p; } void gcd_simplify(polynomial * p) { if (m_manager.finite()) return; auto& m = m_manager.m(); unsigned sz = p->size(); if (sz == 0) return; unsigned g = 0; for (unsigned i = 0; i < sz; i++) { if (!m.is_int(p->a(i))) { return; } int j = m.get_int(p->a(i)); if (j == INT_MIN || j == 1 || j == -1) return; g = u_gcd(abs(j), g); if (g == 1) return; } scoped_mpz r(m), gg(m); m.set(gg, g); for (unsigned i = 0; i < sz; ++i) { m.div_gcd(p->a(i), gg, r); m.set(p->a(i), r); } } polynomial * mk_zero() { return m_zero; } polynomial * mk_one() { return m_unit_poly; } monomial * mk_unit() { return mm().mk_unit(); } monomial * mk_monomial(tmp_monomial & tmp) { return mm().mk_monomial(tmp); } monomial * mk_monomial(var x) { return mm().mk_monomial(x); } monomial * mk_monomial(var x, unsigned k) { return mm().mk_monomial(x, k); } monomial * mk_monomial(unsigned sz, var * xs) { return mm().mk_monomial(sz, xs); } monomial * mk_monomial(unsigned sz, power const * pws) { return mm().mk_monomial(sz, pws); } monomial * convert(monomial const * src) { return mm().convert(src); } polynomial * mk_const_core(numeral & a) { monomial * u = mk_unit(); inc_ref(u); return mk_polynomial_core(1, &a, &u); } polynomial * mk_const(numeral & a) { if (m_manager.is_zero(a)) return mk_zero(); if (m_manager.is_one(a)) return mk_one(); return mk_const_core(a); } polynomial * mk_const(rational const & a) { SASSERT(a.is_int()); scoped_numeral tmp(m_manager); m_manager.set(tmp, a.to_mpq().numerator()); polynomial * p = mk_const(tmp); return p; } polynomial * mk_polynomial(var x, unsigned k) { SASSERT(is_valid(x)); numeral one(1); monomial * m = mk_monomial(x, k); inc_ref(m); return mk_polynomial_core(1, &one, &m); } polynomial * mk_polynomial(unsigned sz, numeral * as, monomial * const * ms) { m_som_buffer.reset(); for (unsigned i = 0; i < sz; i++) { m_som_buffer.add(as[i], ms[i]); } return m_som_buffer.mk(); } /** \brief Convert rationals into numerals at m_rat2numeral */ void rational2numeral(unsigned sz, rational const * as) { SASSERT(m_rat2numeral.empty()); for (unsigned i = 0; i < sz; i++) { SASSERT(as[i].is_int()); m_rat2numeral.push_back(numeral()); m_manager.set(m_rat2numeral.back(), as[i].to_mpq().numerator()); } } void reset_tmp_as2() { DEBUG_CODE({ for (unsigned i = 0; i < m_rat2numeral.size(); i++) { SASSERT(m_manager.is_zero(m_rat2numeral[i])); } }); m_rat2numeral.reset(); } polynomial * mk_polynomial(unsigned sz, rational const * as, monomial * const * ms) { rational2numeral(sz, as); polynomial * p = mk_polynomial(sz, m_rat2numeral.c_ptr(), ms); reset_tmp_as2(); return p; } polynomial * mk_univariate(var x, unsigned n, numeral * as) { SASSERT(m_cheap_som_buffer.empty()); unsigned k = n+1; while (k > 0) { --k; if (m_manager.is_zero(as[k])) { m_manager.del(as[k]); continue; } m_cheap_som_buffer.add_reset(as[k], mk_monomial(x, k)); } return m_cheap_som_buffer.mk(); } polynomial * mk_univariate(var x, unsigned n, rational const * as) { SASSERT(is_valid(x)); rational2numeral(n+1, as); polynomial * p = mk_univariate(x, n, m_rat2numeral.c_ptr()); reset_tmp_as2(); return p; } polynomial * mk_linear(unsigned sz, numeral * as, var const * xs, numeral & c) { SASSERT(m_tmp_linear_as.empty()); SASSERT(m_tmp_linear_ms.empty()); for (unsigned i = 0; i < sz; i++) { if (m_manager.is_zero(as[i])) continue; m_tmp_linear_as.push_back(numeral()); swap(m_tmp_linear_as.back(), as[i]); m_tmp_linear_ms.push_back(mk_monomial(xs[i])); } if (!m_manager.is_zero(c)) { m_tmp_linear_as.push_back(numeral()); swap(m_tmp_linear_as.back(), c); m_tmp_linear_ms.push_back(mk_unit()); } polynomial * p = mk_polynomial(m_tmp_linear_as.size(), m_tmp_linear_as.c_ptr(), m_tmp_linear_ms.c_ptr()); m_tmp_linear_as.reset(); m_tmp_linear_ms.reset(); return p; } polynomial * mk_linear(unsigned sz, rational const * as, var const * xs, rational const & c) { SASSERT(c.is_int()); rational2numeral(sz, as); numeral tmp_c; m_manager.set(tmp_c, c.to_mpq().numerator()); polynomial * p = mk_linear(sz, m_rat2numeral.c_ptr(), xs, tmp_c); SASSERT(m_manager.is_zero(tmp_c)); reset_tmp_as2(); return p; } monomial * mul(monomial const * m1, monomial const * m2) { return mm().mul(m1, m2); } bool div(monomial const * m1, monomial const * m2) { return mm().div(m1, m2); } bool div(monomial const * m1, monomial const * m2, monomial_ref & r) { return mm().div(m1, m2, r); } monomial * gcd(monomial const * m1, monomial const * m2, monomial * & q1, monomial * & q2) { return mm().gcd(m1, m2, q1, q2); } bool unify(monomial const * m1, monomial const * m2, monomial * & q1, monomial * & q2) { return mm().unify(m1, m2, q1, q2); } monomial * pw(monomial const * m, unsigned k) { return mm().pw(m, k); } monomial * sqrt(monomial const * m) { return mm().sqrt(m); } polynomial * addmul(numeral const & a1, monomial const * m1, polynomial const * p1, numeral const & a2, monomial const * m2, polynomial const * p2) { m_som_buffer.reset(); m_som_buffer.addmul(a1, m1, p1); m_som_buffer.addmul(a2, m2, p2); return m_som_buffer.mk(); } polynomial * addmul(polynomial const * p1, numeral const & a2, monomial const * m2, polynomial const * p2) { numeral one(1); return addmul(one, mk_unit(), p1, a2, m2, p2); } polynomial * addmul(polynomial const * p1, numeral const & a2, polynomial const * p2) { return addmul(p1, a2, mk_unit(), p2); } polynomial * add(polynomial const * p1, polynomial const * p2) { numeral one(1); return addmul(one, mk_unit(), p1, one, mk_unit(), p2); } polynomial * sub(polynomial const * p1, polynomial const * p2) { numeral one(1); numeral minus_one; // It is incorrect to initialize with -1 when numeral_manager is GF_2 m_manager.set(minus_one, -1); return addmul(one, mk_unit(), p1, minus_one, mk_unit(), p2); } /** \brief Return p1*p2 + a */ polynomial * muladd(polynomial const * p1, polynomial const * p2, numeral const & a) { if (is_zero(p1) || is_zero(p2)) { return mk_const(a); } m_som_buffer.reset(); unsigned sz1 = p1->size(); for (unsigned i = 0; i < sz1; i++) { checkpoint(); numeral const & a1 = p1->a(i); monomial * m1 = p1->m(i); m_som_buffer.addmul(a1, m1, p2); } m_som_buffer.add(a); return m_som_buffer.mk(); } polynomial * mul(polynomial const * p1, polynomial const * p2) { numeral zero(0); return muladd(p1, p2, zero); } polynomial * mul(numeral const & a, monomial const * m, polynomial const * p) { if (m_manager.is_zero(a)) return m_zero; if (m_manager.is_one(a) && m == mk_unit()) return const_cast(p); SASSERT(m_cheap_som_buffer.empty()); m_cheap_som_buffer.addmul(a, m, p); return m_cheap_som_buffer.mk(); } polynomial * mul(monomial const * m, polynomial const * p) { numeral one(1); return mul(one, m, p); } polynomial * mul(numeral const & a, polynomial const * p) { return mul(a, mk_unit(), p); } /** \brief Return a*p1*p2 */ polynomial * mul(numeral const & a, polynomial const * p1, polynomial const * p2) { if (m_manager.is_zero(a) || is_zero(p1) || is_zero(p2)) return mk_zero(); scoped_numeral new_a1(m_manager); m_som_buffer.reset(); unsigned sz1 = p1->size(); for (unsigned i = 0; i < sz1; i++) { checkpoint(); numeral const & a1 = p1->a(i); m_manager.mul(a, a1, new_a1); monomial * m1 = p1->m(i); m_som_buffer.addmul(new_a1, m1, p2); } return m_som_buffer.mk(); } // Divide coefficients of p by d. // This methods assumes that all coefficients of p are divisible by d. polynomial * div(polynomial * p, numeral const & d) { SASSERT(m_cheap_som_buffer.empty()); unsigned sz = p->size(); scoped_numeral a(m_manager); for (unsigned i = 0; i < sz; i++) { m_manager.div(p->a(i), d, a); m_cheap_som_buffer.add(a, p->m(i)); } return m_cheap_som_buffer.mk(); } polynomial * mul(rational const & a, polynomial const * p) { SASSERT(a.is_int()); scoped_numeral tmp(m_manager); m_manager.set(tmp, a.to_mpq().numerator()); polynomial * new_p = mul(tmp, p); return new_p; } /** \brief Return m/x^k */ monomial * div_x_k(monomial const * m, var x, unsigned k) { return mm().div_x_k(m, x, k); } /** \brief Return m/x^n where n == m->degree_of(x) */ monomial * div_x(monomial const * m, var x) { return mm().div_x(m, x); } bool is_p_normalized(polynomial const * p) const { for (unsigned i = 0; i < p->size(); i++) { SASSERT(m().is_p_normalized(p->a(i))); } return true; } /** \brief (Incremental) Newton interpolation for multivariate polynomials. Creates a polynomial on x of degree at most d with coefficients in Z[y1, ..., ym], using d+1 sample points. Sample points are provided using the method add, and the interpolating polynomial is created using mk() method. \pre manager must be configured in Zp (modular) mode. We need this requeriment because we use the inverse operation. */ class newton_interpolator { imp & pm; scoped_numeral_vector m_inputs; scoped_numeral_vector m_invs; polynomial_ref_vector m_vs; mpzzp_manager & m() const { return pm.m(); } public: newton_interpolator(imp & _pm):pm(_pm), m_inputs(m()), m_invs(m()), m_vs(pm.m_wrapper) { m_invs.push_back(numeral(0)); } void reset() { m_inputs.reset(); m_invs.shrink(1); m_vs.reset(); SASSERT(m().is_zero(m_invs[0])); } scoped_numeral_vector const & inputs() const { return m_inputs; } unsigned num_sample_points() const { return m_inputs.size(); } /** \brief Add a new datapoint */ void add(numeral const & input, polynomial const * output) { TRACE("newton", tout << m().to_string(input) << " -> "; output->display(tout, m()); tout << "\n";); SASSERT(m().modular()); unsigned sz = num_sample_points(); if (sz > 0) { unsigned k = sz; // add new inverse scoped_numeral product(m()); scoped_numeral aux(m()); SASSERT(!m().eq(input, m_inputs[0])); m().sub(input, m_inputs[0], product); for (unsigned i = 1; i <= k - 1; i++) { SASSERT(!m().eq(input, m_inputs[i])); m().sub(input, m_inputs[i], aux); m().mul(product, aux, product); } m().inv(product); m_inputs.push_back(input); m_invs.push_back(product); TRACE("newton", tout << "invs[" << k << "]: " << product << "\n";); SASSERT(m().eq(m_inputs[k], input)); // Compute newton's coefficient polynomial_ref temp(pm.m_wrapper); polynomial_ref aux_poly(pm.m_wrapper); temp = m_vs.get(k-1); for (int j = k - 2; j >= 0; j--) { // temp <- temp*(input - m_inputs[j]) + vs[j] m().sub(input, m_inputs[j], aux); SASSERT(m().is_p_normalized(aux)); aux_poly = pm.mul(aux, temp); temp = pm.add(aux_poly, m_vs.get(j)); SASSERT(pm.is_p_normalized(temp)); } // new vs <- (output - temp)*invs[sz] aux_poly = pm.sub(output, temp); SASSERT(pm.is_p_normalized(aux_poly)); aux_poly = pm.mul(m_invs[sz], aux_poly); SASSERT(pm.is_p_normalized(aux_poly)); m_vs.push_back(aux_poly); TRACE("newton", tout << "vs[" << k << "]: " << aux_poly << "\n";); } else { m_inputs.push_back(input); m_vs.push_back(const_cast(output)); } } // Convert newton form to standard form void mk(var x, polynomial_ref & r) { SASSERT(m().modular()); polynomial_ref u(pm.m_wrapper); polynomial_ref aux_poly(pm.m_wrapper); int num = num_sample_points(); int d = num - 1; SASSERT(num > 0); u = m_vs.get(d); scoped_numeral c(m()); for (int k = d - 1; k >= 0; k--) { TRACE("newton", tout << "u: " << u << "\n";); // create polynomial (x - inputs[k]) m().set(c, m_inputs[k]); m().neg(c); numeral one(1); aux_poly = pm.mk_linear(1, &one, &x, c); TRACE("newton", tout << "(x - inputs[k]): " << aux_poly << "\n";); // u <- u * (x - inputs[k]) + vs[k] aux_poly = pm.mul(u, aux_poly); u = pm.add(aux_poly, m_vs.get(k)); } TRACE("newton", tout << "result u: " << u << "\n";); r = u; } }; /** \brief Newton interpolation for multivariate polynomials. Creates a polynomial on x of degree at most d with coefficients in Z[y1, ..., ym], using d+1 sample points. The sample points are store in the vectors inputs and outputs. Both must have size d+1. \pre manager must be configured in Zp (modular) mode. We need this requeriment because we use the inverse operation. */ void newton_interpolation(var x, unsigned d, numeral const * inputs, polynomial * const * outputs, polynomial_ref & r) { SASSERT(m().modular()); newton_interpolator interpolator(*this); for (unsigned i = 0; i <= d; i++) interpolator.add(inputs[i], outputs[i]); interpolator.mk(x, r); } class newton_interpolator_vector { imp * m_imp; ptr_vector m_data; public: newton_interpolator_vector():m_imp(nullptr) {} ~newton_interpolator_vector() { flush(); } void flush() { unsigned sz = m_data.size(); for (unsigned i = 0; i < sz; i++) dealloc(m_data[i]); m_data.reset(); } void set_owner(imp * owner) { SASSERT(m_imp == 0 || m_imp == owner); m_imp = owner; } newton_interpolator & operator[](unsigned idx) { SASSERT(m_imp); while (idx >= m_data.size()) { m_data.push_back(alloc(newton_interpolator, *m_imp)); } return *(m_data[idx]); } }; /** \brief Represents a polynomial skeleton of a multivariate polynomial Z[Y1, ..., Yn] with coefficients in Z[X] */ struct skeleton { struct entry { monomial * m_monomial; // a monomial in Z[Y1, ..., Y1] unsigned m_first_power_idx; // position (in m_powers) of the powers of X that are the coefficient of this monomial unsigned m_num_powers; // size of the coefficient of this monomial. entry(monomial * m, unsigned first_idx): m_monomial(m), m_first_power_idx(first_idx), m_num_powers(1) { } unsigned num_powers() const { return m_num_powers; } monomial * m() const { return m_monomial; } }; imp & pm; var m_x; svector m_entries; unsigned_vector m_powers; ptr_vector m_orig_monomials; unsigned m_max_powers; // maximal number of powers associated with an entry skeleton(imp & _pm, polynomial * p, var x):pm(_pm), m_x(x) { m_max_powers = 0; ptr_buffer ms; unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { ms.push_back(p->m(i)); } std::sort(ms.begin(), ms.end(), lex_lt2(x)); monomial * prev = nullptr; for (unsigned i = 0; i < sz; i++) { monomial * orig_m = ms[i]; monomial * m; unsigned k = orig_m->degree_of(x); if (k > 0) m = pm.div_x(orig_m, x); else m = orig_m; if (m == prev) { unsigned & num_powers = m_entries.back().m_num_powers; num_powers++; if (num_powers > m_max_powers) m_max_powers = num_powers; } else { prev = m; pm.inc_ref(m); m_entries.push_back(entry(m, m_powers.size())); if (m_max_powers == 0) m_max_powers = 1; } pm.inc_ref(orig_m); m_orig_monomials.push_back(orig_m); m_powers.push_back(k); } TRACE("skeleton", tout << "x: x" << m_x << "\n"; tout << "max: " << m_max_powers << "\n"; tout << "p: "; p->display(tout, pm.m()); tout << "\n"; tout << "skeleton: "; display(tout); tout << "\n";); DEBUG_CODE({ unsigned sz = m_entries.size(); for (unsigned i = 1; i < sz; i++) { SASSERT(lex_compare(m_entries[i-1].m_monomial, m_entries[i].m_monomial) < 0); } }); } ~skeleton() { unsigned sz = m_entries.size(); for (unsigned i = 0; i < sz; i++) { pm.dec_ref(m_entries[i].m_monomial); } sz = m_orig_monomials.size(); for (unsigned i = 0; i < sz; i++) { pm.dec_ref(m_orig_monomials[i]); } } unsigned get_entry_idx(monomial * m) { unsigned sz = m_entries.size(); for (unsigned i = 0; i < sz; i++) { if (m_entries[i].m_monomial == m) return i; } return UINT_MAX; } unsigned num_entries() const { return m_entries.size(); } entry const & operator[](unsigned idx) const { return m_entries[idx]; } unsigned ith_power(entry const & e, unsigned i) const { SASSERT(i < e.m_num_powers); return m_powers[e.m_first_power_idx + i]; } monomial * ith_orig_monomial(entry const & e, unsigned i) const { SASSERT(i < e.m_num_powers); return m_orig_monomials[e.m_first_power_idx + i]; } unsigned max_num_powers() const { return m_max_powers; } void display(std::ostream & out) { unsigned sz = m_entries.size(); for (unsigned i = 0; i < sz; i++) { entry & e = m_entries[i]; if (i > 0) out << " "; out << "("; for (unsigned j = 0; j < e.m_num_powers; j++) { if (j > 0) out << " "; out << "x" << m_x << "^"; out << m_powers[e.m_first_power_idx + j]; } out << ")*"; e.m_monomial->display(out); } } }; class sparse_interpolator { skeleton * m_skeleton; numeral_vector m_inputs; numeral_vector m_outputs; public: sparse_interpolator(skeleton * sk):m_skeleton(sk) { // reserve space output values associated with each entry if (sk) { unsigned sz = sk->num_entries(); for (unsigned i = 0; i < sz; i++) { unsigned num_powers = (*sk)[i].num_powers(); for (unsigned j = 0; j < num_powers; j++) { m_outputs.push_back(numeral()); } } } } ~sparse_interpolator() { if (m_skeleton) { numeral_manager & m = m_skeleton->pm.m(); for (unsigned i = 0; i < m_inputs.size(); i++) m.del(m_inputs[i]); for (unsigned i = 0; i < m_outputs.size(); i++) m.del(m_outputs[i]); } } void reset() { numeral_manager & m = m_skeleton->pm.m(); for (unsigned i = 0; i < m_inputs.size(); i++) { m.del(m_inputs[i]); } m_inputs.reset(); } bool ready() const { return m_inputs.size() == m_skeleton->max_num_powers(); } bool add(numeral const & in, polynomial const * q) { SASSERT(m_skeleton); SASSERT(m_inputs.size() < m_skeleton->max_num_powers()); numeral_manager & m = m_skeleton->pm.m(); unsigned input_idx = m_inputs.size(); m_inputs.push_back(numeral()); m.set(m_inputs.back(), in); unsigned sz = q->size(); for (unsigned i = 0; i < sz; i++) { monomial * mon = q->m(i); unsigned entry_idx = m_skeleton->get_entry_idx(mon); if (entry_idx == UINT_MAX) return false; skeleton::entry const & e = (*m_skeleton)[entry_idx]; if (input_idx >= e.num_powers()) continue; unsigned output_idx = e.m_first_power_idx + input_idx; m.set(m_outputs[output_idx], q->a(i)); } return true; } bool mk(polynomial_ref & r) { SASSERT(m_skeleton); numeral_manager & m = m_skeleton->pm.m(); scoped_numeral_vector cs(m); scoped_numeral_vector new_as(m); scoped_numeral_vector as(m); ptr_buffer mons; scoped_numeral aux(m); linear_eq_solver solver(m); unsigned sz = m_skeleton->num_entries(); for (unsigned k = 0; k < sz; k++) { skeleton::entry const & e = (*m_skeleton)[k]; unsigned num_pws = e.num_powers(); solver.resize(num_pws); new_as.resize(num_pws); for (unsigned i = 0; i < num_pws; i++) { numeral & in = m_inputs[i]; cs.reset(); for (unsigned j = 0; j < num_pws; j++) { m.power(in, m_skeleton->ith_power(e, j), aux); cs.push_back(aux); } unsigned output_idx = e.m_first_power_idx + i; TRACE("sparse_interpolator", tout << "adding new equation:\n"; for (unsigned i = 0; i < num_pws; i++) { tout << m.to_string(cs[i]) << " "; } tout << "\n";); solver.add(i, cs.c_ptr(), m_outputs[output_idx]); } TRACE("sparse_interpolator", tout << "find coefficients of:\n"; for (unsigned i = 0; i < num_pws; i++) { m_skeleton->ith_orig_monomial(e, i)->display(tout); tout << "\n"; } tout << "system of equations:\n"; solver.display(tout);); if (!solver.solve(new_as.c_ptr())) return false; for (unsigned i = 0; i < num_pws; i++) { if (!m.is_zero(new_as[i])) { as.push_back(new_as[i]); mons.push_back(m_skeleton->ith_orig_monomial(e, i)); } } } r = m_skeleton->pm.mk_polynomial(as.size(), as.c_ptr(), mons.c_ptr()); return true; } }; svector m_found_vars; void vars(polynomial const * p, var_vector & xs) { xs.reset(); m_found_vars.reserve(num_vars(), false); unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { monomial * m = p->m(i); unsigned msz = m->size(); for (unsigned j = 0; j < msz; j++) { var x = m->get_var(j); if (!m_found_vars[x]) { m_found_vars[x] = true; xs.push_back(x); } } } // reset m_found_vars sz = xs.size(); for (unsigned i = 0; i < sz; i++) m_found_vars[xs[i]] = false; } typedef sbuffer power_buffer; typedef sbuffer unsigned_buffer; typedef sbuffer var_buffer; /** Store in pws the variables occurring in p and their (minimal or maximal) degrees. */ unsigned_vector m_var_degrees_tmp; template void var_degrees(polynomial const * p, power_buffer & pws) { pws.reset(); unsigned_vector & var2pos = m_var_degrees_tmp; var2pos.reserve(num_vars(), UINT_MAX); unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { monomial * m = p->m(i); unsigned msz = m->size(); for (unsigned j = 0; j < msz; j++) { var x = m->get_var(j); unsigned k = m->degree(j); unsigned pos = var2pos[x]; if (pos == UINT_MAX) { var2pos[x] = pws.size(); pws.push_back(power(x, k)); } else if (Max && k > pws[pos].degree()) { pws[pos].degree() = k; } else if (!Max && k < pws[pos].degree()) { pws[pos].degree() = k; } } } sz = pws.size(); for (unsigned i = 0; i < sz; i++) { SASSERT(var2pos[pws[i].get_var()] != UINT_MAX); var2pos[pws[i].get_var()] = UINT_MAX; } DEBUG_CODE({ for (unsigned i = 0; i < pws.size(); i++) { for (unsigned j = i + 1; j < pws.size(); j++) SASSERT(pws[i].first != pws[j].first); } }); } void var_max_degrees(polynomial const * p, power_buffer & pws) { var_degrees(p, pws); } void var_min_degrees(polynomial const * p, power_buffer & pws) { var_degrees(p, pws); } polynomial * coeff(polynomial const * p, var x, unsigned k) { SASSERT(is_valid(x)); SASSERT(m_cheap_som_buffer.empty()); TRACE("coeff_bug", tout << "p: "; p->display(tout, m_manager); tout << "\nx: " << x << ", k: " << k << "\n";); unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { monomial * m = p->m(i); unsigned d = m->degree_of(x); if (d == k) m_cheap_som_buffer.add(p->a(i), div_x(m, x)); } return m_cheap_som_buffer.mk(); } /** Let p be of the form q_k(yvec)*x^k + ...+ q_0(yvec) Store the polynomials q_k(yvec), ..., q_0(yvec) in the som_buffer_vector. */ void coeffs(polynomial const * p, var x, som_buffer_vector & cs) { cs.set_owner(this); unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { monomial * m = p->m(i); unsigned d = m->degree_of(x); som_buffer * c = cs[d]; c->add(p->a(i), div_x(m, x)); } } /** \brief Return a polynomial h that is the coefficient of x^k in p. Store the reduct (p - h x^k) into \c reduct. */ polynomial * coeff(polynomial const * p, var x, unsigned k, polynomial_ref & reduct) { SASSERT(is_valid(x)); SASSERT(m_cheap_som_buffer.empty()); SASSERT(m_cheap_som_buffer2.empty()); unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { monomial * m = p->m(i); unsigned d = m->degree_of(x); if (d == k) m_cheap_som_buffer.add(p->a(i), div_x(m, x)); else m_cheap_som_buffer2.add(p->a(i), m); } reduct = m_cheap_som_buffer2.mk(); return m_cheap_som_buffer.mk(); } /** \brief Return true if the coefficient of x^k is just a constant. Store it in c. */ bool const_coeff(polynomial const * p, var x, unsigned k, numeral & c) { SASSERT(is_valid(x)); m_manager.reset(c); unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { monomial * m = p->m(i); unsigned d = m->degree_of(x); if (d == k) { unsigned msz = m->size(); if ((k > 0 && msz > 1) || (k == 0 && msz > 0)) return false; m_manager.set(c, p->a(i)); } } return true; } bool nonzero_const_coeff(polynomial const * p, var x, unsigned k) { scoped_numeral c(m_manager); return const_coeff(p, x, k, c) && !m_manager.is_zero(c); } /** \brief Extract the integer content of p. */ void ic(polynomial const * p, numeral & a) { if (is_zero(p)) { m_manager.reset(a); return; } if (is_const(p)) { m_manager.set(a, p->a(0)); return; } m_manager.set(a, p->a(0)); unsigned sz = p->size(); for (unsigned i = 1; i < sz; i++) { if (m_manager.is_one(a)) return; m_manager.gcd(a, p->a(i), a); } } /** \brief Sum of the absolute values of the coefficients. */ void abs_norm(polynomial const * p, numeral & norm) { m_manager.reset(norm); scoped_numeral tmp(m_manager); unsigned sz = p->size(); for (unsigned i = 0; i < sz; ++ i) { m_manager.set(tmp, p->a(i)); m_manager.abs(tmp); m_manager.add(norm, tmp, norm); } } /** \brief Arbitrary leading integer coefficient. */ numeral const & numeral_lc(polynomial const * p, var x) { int sz = p->size(); if (sz == 0) { return m_zero_numeral; } else { return p->a(0); } } numeral const & numeral_tc(polynomial const * p) { int sz = p->size(); if (sz == 0) { return m_zero_numeral; } else { monomial * u = mk_unit(); for (int i = 0; i < sz; ++ i) { if (p->m(i) == u) return p->a(i); } return m_zero_numeral; } } /** \brief Extract the integer content of p. p = a*c s.t. the GCD of the coefficients of c is one. */ void ic(polynomial const * p, numeral & a, polynomial_ref & c) { if (is_zero(p)) { m_manager.reset(a); c = const_cast(p); return; } if (is_const(p)) { m_manager.set(a, p->a(0)); c = mk_one(); return; } unsigned sz = p->size(); m_manager.gcd(sz, p->as(), a); if (m_manager.is_one(a)) { c = const_cast(p); return; } m_cheap_som_buffer.reset(); scoped_numeral ai(m_manager); for (unsigned i = 0; i < sz; i++) { monomial * m = p->m(i); m_manager.div(p->a(i), a, ai); m_cheap_som_buffer.add_reset(ai, m); } c = m_cheap_som_buffer.mk(); } // Flip the sign of p, if the leading monomial is negative polynomial * flip_sign_if_lm_neg_core(polynomial const * p) { if (is_zero(p)) return const_cast(p); unsigned glex_max_pos = p->graded_lex_max_pos(); SASSERT(glex_max_pos != UINT_MAX); if (m_manager.is_neg(p->a(glex_max_pos))) return neg(p); else return const_cast(p); } void flip_sign_if_lm_neg(polynomial_ref & p) { p = flip_sign_if_lm_neg_core(p); } /** \brief Extract the integer content, content and primitive part of p with respect to variable x. */ void iccp(polynomial const * p, var x, numeral & i, polynomial_ref & c, polynomial_ref & pp) { TRACE("polynomial", tout << "iccp x" << x << "\n"; p->display(tout, m_manager); tout << "\n";); if (is_zero(p)) { m_manager.set(i, 0); c = mk_one(); pp = const_cast(p); return; } if (is_const(p)) { m_manager.set(i, p->a(0)); c = mk_one(); pp = mk_one(); return; } unsigned d = degree(p, x); if (d == 0) { ic(p, i, c); pp = mk_one(); return; } // Apply filter and collect powers of x occurring in p // The quick filter is the following: // If p contains a monomial x^k and no monomial of the form m*x^k m != 1, then // c = m_unit_poly // To detect that we use a map (iccp_powers) from k to counters. // We traverse p and update the map using the following rules: // - found monomial x^k then iccp_powers[k]++; // - found monomial m*x^k then iccp_powers[k]+=2; // If after traversing p, there is a k s.t. iccp_powers[k] == 1, we know c == 1 // We store iccp_powers the powers of x occurring in p. sbuffer iccp_filter; sbuffer iccp_powers; iccp_filter.resize(d+1, 0); iccp_powers.reset(); for (unsigned j = 0; j <= d; j++) iccp_filter[j] = 0; unsigned sz = p->size(); for (unsigned j = 0; j < sz; j++) { monomial * m = p->m(j); unsigned k = m->degree_of(x); TRACE("polynomial", tout << "degree of x" << x << " at "; m->display(tout); tout << " is " << k << "\n";); if (iccp_filter[k] == 0) iccp_powers.push_back(k); if (m->size() == (k > 0 ? 1 : 0)) iccp_filter[k]++; else iccp_filter[k]+=2; } SASSERT(!iccp_powers.empty()); unsigned num_powers = iccp_powers.size(); for (unsigned j = 0; j < num_powers; j++) { SASSERT(iccp_filter[iccp_powers[j]] > 0); if (iccp_filter[iccp_powers[j]] == 1) { ic(p, i, pp); c = mk_one(); return; } } // Extract integer content ic(p, i, pp); TRACE("polynomial", tout << "p: "; p->display(tout, m_manager); tout << "\ni: " << m_manager.to_string(i) << "\npp: " << pp << "\n";); // Compute c using the gcd of coeffs of x^k for k's in iccp_powers polynomial_ref ci(pm()); c = coeff(pp, x, iccp_powers[0]); for (unsigned j = 1; j < num_powers; j++) { ci = coeff(pp, x, iccp_powers[j]); gcd(c, ci, c); if (is_const(c)) { c = mk_one(); return; } } SASSERT(!is_const(c)); // make sure the sign of the leading monomial is positive flip_sign_if_lm_neg(c); TRACE("polynomial", tout << "pp: " << pp << "\nc: " << c << "\n";); pp = exact_div(pp, c); } void iccp(polynomial const * p, numeral & i, polynomial_ref & c, polynomial_ref & pp) { iccp(p, max_var(p), i, c, pp); } polynomial_ref pp(polynomial const * p, var x) { scoped_numeral i(m_manager); polynomial_ref c(pm()), result(pm()); iccp(p, x, i, c, result); return result; } bool is_primitive(polynomial const * p, var x) { scoped_numeral i(m_manager); polynomial_ref c(pm()); polynomial_ref pp(pm()); iccp(p, x, i, c, pp); return eq(p, pp); } polynomial * lc(polynomial const * p, var x) { return coeff(p, x, degree(p, x)); } void gcd_prs(polynomial const * u, polynomial const * v, var x, polynomial_ref & r) { TRACE("polynomial_gcd", tout << "gcd prs with x" << x << " for\nu: "; u->display(tout, m_manager); tout << "\nv: "; v->display(tout, m_manager); tout << "\n";); if (degree(u, x) < degree(v, x)) std::swap(u, v); scoped_numeral i_u(m_manager), i_v(m_manager); polynomial_ref c_u(pm()), c_v(pm()); polynomial_ref pp_u(pm()), pp_v(pm()); scoped_numeral d_a(m_manager); polynomial_ref d_r(pm()); polynomial_ref g(pm()), h(pm()), rem(pm()), new_h(pm()); iccp(u, x, i_u, c_u, pp_u); iccp(v, x, i_v, c_v, pp_v); gcd(c_u, c_v, d_r); m_manager.gcd(i_u, i_v, d_a); TRACE("polynomial_gcd_detail", tout << "After GCD of the content\n"; tout << "u: "; u->display(tout, m_manager); tout << "\n"; tout << "v: "; v->display(tout, m_manager); tout << "\n"; tout << "i_u: " << i_u << "\n"; tout << "i_v: " << i_v << "\n"; tout << "c_u: " << c_u << "\n"; tout << "c_v: " << c_v << "\n"; tout << "pp_u: " << pp_u << "\n"; tout << "pp_v: " << pp_v << "\n"; tout << "d_r: " << d_r << "\nd_a: " << d_a << "\n";); g = mk_one(); h = mk_one(); unsigned counter = 0; while (true) { SASSERT(degree(pp_u, x) >= degree(pp_v, x)); unsigned delta = degree(pp_u, x) - degree(pp_v, x); TRACE("polynomial_gcd_detail", tout << "iteration: " << counter << "\n"; tout << "gcd loop\npp_u: " << pp_u << "\npp_v: " << pp_v << "\ndelta: " << delta << "\n";); counter++; exact_pseudo_remainder(pp_u, pp_v, x, rem); if (is_zero(rem)) { TRACE("polynomial", tout << "rem is zero...\npp_v: " << pp_v << "\n";); flip_sign_if_lm_neg(pp_v); r = pp(pp_v, x); r = mul(d_a, d_r, r); return; } if (is_const(rem)) { TRACE("polynomial", tout << "rem is a constant: " << rem << "\nr: " << d_r << "\nd_a: " << d_a << "\n";); r = mul(d_a, d_r); return; } pp_u = pp_v; // pp_v <- rem/g*h^{delta} pp_v = exact_div(rem, g); // delta is usually a small number, so I do not compute h^delta for (unsigned i = 0; i < delta; i++) pp_v = exact_div(pp_v, h); g = lc(pp_u, x); // h <- h^{1-delta}*g^{delta} new_h = mk_one(); for (unsigned i = 0; i < delta; i++) new_h = mul(new_h, g); if (delta > 1) { for (unsigned i = 0; i < delta - 1; i++) new_h = exact_div(new_h, h); } h = new_h; } } // Store in r <- gcd(content(u, x), v) void gcd_content(polynomial const * u, var x, polynomial const * v, polynomial_ref & r) { scoped_numeral i_u(m_manager); polynomial_ref c_u(pm()); polynomial_ref pp_u(pm()); iccp(u, x, i_u, c_u, pp_u); c_u = mul(i_u, c_u); gcd(c_u, v, r); } // TODO: implement euclid algorithm when m_manager in Zp mode void euclid_gcd(polynomial const * u, polynomial const * v, polynomial_ref & r) { SASSERT(m().modular()); CTRACE("mgcd", !is_univariate(u) || !is_univariate(v), tout << "euclid_gcd, polynomials are not univariate\n"; u->display(tout, m()); tout << "\n"; v->display(tout, m()); tout << "\n";); SASSERT(is_univariate(u)); SASSERT(is_univariate(v)); if (is_zero(u)) { r = const_cast(v); flip_sign_if_lm_neg(r); return; } if (is_zero(v)) { r = const_cast(u); flip_sign_if_lm_neg(r); return; } if (u == v) { r = const_cast(u); flip_sign_if_lm_neg(r); return; } if (is_const(u) || is_const(v)) { scoped_numeral i_u(m_manager), i_v(m_manager); ic(v, i_v); ic(u, i_u); scoped_numeral a(m_manager); m_manager.gcd(i_v, i_u, a); r = mk_const(a); return; } // Maybe map it to univariate case gcd_prs(u, v, max_var(u), r); } // Combine two different modular images using Chinese Remainder theorem // The new bound is stored in b2 void CRA_combine_images(polynomial const * C1, scoped_numeral const & b1, polynomial const * C2, scoped_numeral & b2, polynomial_ref & r) { lex_sort(C1); lex_sort(C2); TRACE("CRA", tout << "C1: "; C1->display(tout, m()); tout << "\nC2: "; C2->display(tout, m()); tout << "\n";); SASSERT(m_cheap_som_buffer.empty()); SASSERT(!m().m().is_even(b1)); SASSERT(!m().m().is_even(b2)); cheap_som_buffer & R = m_cheap_som_buffer; scoped_numeral inv1(m()); scoped_numeral inv2(m()); scoped_numeral g(m()); m().gcd(b1, b2, inv1, inv2, g); SASSERT(m().is_one(g)); TRACE("CRA", tout << "b1: " << b1 << ", b2: " << b2 << ", inv1: " << inv1 << ", inv2: " << inv2 << "\n";); // b1*inv1 + b2.inv2 = 1 // inv1 is the inverse of b1 mod b2 // inv2 is the inverse of b2 mod b1 m().m().mod(inv1, b2, inv1); m().m().mod(inv2, b1, inv2); TRACE("CRA", tout << "inv1: " << inv1 << ", inv2: " << inv2 << "\n";); scoped_numeral a1(m()); scoped_numeral a2(m()); m().mul(b2, inv2, a1); // a1 is the multiplicator for coefficients of C1 m().mul(b1, inv1, a2); // a2 is the multiplicator for coefficients of C2 TRACE("CRA", tout << "a1: " << a1 << ", a2: " << a2 << "\n";); // new bound scoped_numeral new_bound(m()); m().mul(b1, b2, new_bound); scoped_numeral lower(m()); scoped_numeral upper(m()); scoped_numeral new_a(m()), tmp1(m()), tmp2(m()), tmp3(m()); m().div(new_bound, 2, upper); m().set(lower, upper); m().neg(lower); TRACE("CRA", tout << "lower: " << lower << ", upper: " << upper << "\n";); #define ADD(A1, A2, M) { \ m().mul(A1, a1, tmp1); \ m().mul(A2, a2, tmp2); \ m().add(tmp1, tmp2, tmp3); \ m().m().mod(tmp3, new_bound, new_a); \ if (m().gt(new_a, upper)) \ m().sub(new_a, new_bound, new_a); \ R.add(new_a, M); \ } numeral zero(0); unsigned i1 = 0; unsigned i2 = 0; unsigned sz1 = C1->size(); unsigned sz2 = C2->size(); while (true) { if (i1 == sz1) { while (i2 < sz2) { TRACE("CRA", tout << "adding C2 rest\n";); ADD(zero, C2->a(i2), C2->m(i2)); i2++; } break; } if (i2 == sz2) { while (i1 < sz1) { TRACE("CRA", tout << "adding C1 rest\n";); ADD(C1->a(i1), zero, C1->m(i1)); i1++; } break; } monomial * m1 = C1->m(i1); monomial * m2 = C2->m(i2); int s = lex_compare(m1, m2); if (s == 0) { ADD(C1->a(i1), C2->a(i2), m1); TRACE("CRA", tout << "C1->a(i1): " << m().to_string(C1->a(i1)) << ", C2->a(i2): " << m().to_string(C2->a(i2)) << ", new_a: " << new_a << "\n"; tout << "tmp1: " << tmp1 << ", tmp2: " << tmp2 << ", tmp3: " << tmp3 << "\n";); i1++; i2++; } else if (s > 0) { TRACE("CRA", tout << "C1 mon biggerr, adding it...\n";); ADD(C1->a(i1), zero, m1); i1++; } else { TRACE("CRA", tout << "C2 mon bigger, adding it...\n";); ADD(zero, C2->a(i2), m2); i2++; } } m().set(b2, new_bound); r = R.mk(); } void uni_mod_gcd(polynomial const * u, polynomial const * v, polynomial_ref & r) { TRACE("mgcd", tout << "univ_modular_gcd\nu: "; u->display(tout, m_manager); tout << "\nv: "; v->display(tout, m_manager); tout << "\n";); SASSERT(!m().modular()); SASSERT(is_univariate(u)); SASSERT(!is_const(u) && !is_const(v)); SASSERT(max_var(u) == max_var(v)); var x = max_var(u); scoped_numeral c_u(m()), c_v(m()); polynomial_ref pp_u(pm()), pp_v(pm()); ic(u, c_u, pp_u); ic(v, c_v, pp_v); scoped_numeral d_a(m()); m_manager.gcd(c_u, c_v, d_a); scoped_numeral lc_u(m()); scoped_numeral lc_v(m()); unsigned d_u = degree(pp_u, x); unsigned d_v = degree(pp_v, x); lc_u = univ_coeff(pp_u, d_u); lc_v = univ_coeff(pp_v, d_v); scoped_numeral lc_g(m()); m().gcd(lc_u, lc_v, lc_g); polynomial_ref u_Zp(m_wrapper); polynomial_ref v_Zp(m_wrapper); polynomial_ref C_star(m_wrapper); scoped_numeral bound(m()); polynomial_ref q(m_wrapper); polynomial_ref candidate(m_wrapper); scoped_numeral p(m()); for (unsigned i = 0; i < NUM_BIG_PRIMES; i++) { m().set(p, g_big_primes[i]); TRACE("mgcd", tout << "trying prime: " << p << "\n";); { scoped_set_zp setZp(m_wrapper, p); u_Zp = normalize(pp_u); v_Zp = normalize(pp_v); if (degree(u_Zp, x) < d_u) { TRACE("mgcd", tout << "bad prime, leading coefficient vanished\n";); continue; // bad prime } if (degree(v_Zp, x) < d_v) { TRACE("mgcd", tout << "bad prime, leading coefficient vanished\n";); continue; // bad prime } euclid_gcd(u_Zp, v_Zp, q); // normalize so that lc_g is leading coefficient of q q = mk_glex_monic(q); scoped_numeral c(m()); m().set(c, lc_g); q = mul(c, q); } TRACE("mgcd", tout << "new q:\n" << q << "\n";); if (is_const(q)) { TRACE("mgcd", tout << "done, modular gcd is one\n";); if (m().is_one(d_a)) r = q; // GCD is one else r = mk_const(d_a); return; } if (C_star.get() == nullptr) { C_star = q; m().set(bound, p); } else { if (degree(q, x) < degree(C_star, x)) { // discard accumulated image, it was affected by unlucky primes TRACE("mgcd", tout << "discarding image\n";); C_star = q; m().set(bound, p); } else { CRA_combine_images(q, p, C_star, bound, C_star); TRACE("mgcd", tout << "new combined:\n" << C_star << "\n";); } } candidate = pp(C_star, x); TRACE("mgcd", tout << "candidate:\n" << candidate << "\n";); scoped_numeral lc_candidate(m()); lc_candidate = univ_coeff(candidate, degree(candidate, x)); if (m().divides(lc_candidate, lc_g) && divides(candidate, pp_u) && divides(candidate, pp_v)) { TRACE("mgcd", tout << "found GCD\n";); r = mul(d_a, candidate); flip_sign_if_lm_neg(r); TRACE("mgcd", tout << "r: " << r << "\n";); return; } } // Oops, modular GCD failed, not enough primes // fallback to prs gcd_prs(u, v, x, r); } typedef ref_buffer polynomial_ref_buffer; /** Compute the content and primitive parts of p, when p is viewed as a multivariate polynomial Zp[y_1, ..., y_n] with coefficients in Zp[x]. */ som_buffer_vector m_iccp_ZpX_buffers; void iccp_ZpX(polynomial const * p, var x, numeral & ci, polynomial_ref & c, polynomial_ref & pp) { SASSERT(m().modular()); TRACE("mgcd_detail", tout << "iccp_ZpX, p: "; p->display(tout, m()); tout << "\nvar x" << x << "\n";); if (is_zero(p)) { TRACE("mgcd_detail", tout << "iccp_ZpX, p is zero\n";); m_manager.set(ci, 0); c = mk_one(); pp = const_cast(p); return; } if (is_const(p)) { TRACE("mgcd_detail", tout << "iccp_ZpX, p is constant\n";); m_manager.set(ci, p->a(0)); c = mk_one(); pp = mk_one(); return; } unsigned d = degree(p, x); if (d == 0) { TRACE("mgcd_detail", tout << "iccp_ZpX, degree(p, x) == 0\n";); ic(p, ci, pp); c = mk_one(); return; } // 1) traverse monomials of p, and mark the monomials that contain p, also compute the minimal degree of x in p. ref_buffer no_x_ms(m_wrapper); // monomials that do not contains x unsigned min_degree = UINT_MAX; // min degree of x in p unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { monomial * m = p->m(i); unsigned k = m->degree_of(x); if (k == 0) { // if m is not marked if (m_m2pos.get(m) == UINT_MAX) { no_x_ms.push_back(m); m_m2pos.set(m, 1); // it is just a mark } } if (k < min_degree) min_degree = k; } SASSERT(min_degree == 0 || no_x_ms.empty()); if (min_degree > 0) { SASSERT(no_x_ms.empty()); // nothing was marked. // divide by x^min_degree TRACE("mgcd_detail", tout << "iccp_ZpX, all monomials contain x" << x << ", dividing by x" << x << "^" << min_degree << "\n";); polynomial_ref xmin(m_wrapper); polynomial_ref new_p(m_wrapper); xmin = mk_polynomial(x, min_degree); new_p = exact_div(p, xmin); iccp_ZpX(new_p, x, ci, c, pp); c = mul(xmin, c); return; } // 2) if for some marked monomial m (i.e., the ones that do not contain x), there is no monomial m*x^k in p, // then c = 1 unsigned num_marked = no_x_ms.size(); unsigned num_unmarked = 0; monomial_ref tmp_m(m_wrapper); for (unsigned i = 0; i < sz; i++) { monomial * m = p->m(i); unsigned k = m->degree_of(x); if (k == 0) continue; tmp_m = div_x(m, x); SASSERT(tmp_m != m); // since x is in m, but not in tmp_m if (m_m2pos.get(tmp_m) == 1) { num_unmarked++; m_m2pos.reset(tmp_m); SASSERT(m_m2pos.get(tmp_m) == UINT_MAX); } } SASSERT(num_unmarked <= num_marked); if (num_unmarked < num_marked) { // reset remaining marks for (unsigned i = 0; i < num_marked; i++) m_m2pos.reset(no_x_ms[i]); TRACE("mgcd_detail", tout << "iccp_ZpX, cheap case... invoking ic\n";); ic(p, ci, pp); c = mk_one(); return; } // 3) expensive case // Basic idea: separate a*m*x^k into a*x^k and m, put a*x^k into the som_buffer associated with m. // The mapping between m is som_buffers is given by m_m2pos // Extract integer content ic(p, ci, pp); no_x_ms.reset(); som_buffer_vector & som_buffers = m_iccp_ZpX_buffers; som_buffers.set_owner(this); for (unsigned i = 0; i < sz; i++) { monomial * m = pp->m(i); unsigned k = m->degree_of(x); if (k != 0) { tmp_m = div_x(m, x); m = tmp_m.get(); } unsigned pos = m_m2pos.get(m); if (pos == UINT_MAX) { pos = no_x_ms.size(); no_x_ms.push_back(m); m_m2pos.set(m, pos); } som_buffer * som = som_buffers[pos]; som->add(pp->a(i), mk_monomial(x, k)); } unsigned num_ms = no_x_ms.size(); for (unsigned i = 0; i < num_ms; i++) m_m2pos.reset(no_x_ms[i]); SASSERT(num_ms > 0); // Compute GCD of all som_buffers polynomial_ref g(m_wrapper); polynomial_ref new_g(m_wrapper); g = som_buffers[0]->mk(); for (unsigned i = 1; i < num_ms; i++) { polynomial_ref a(m_wrapper); a = som_buffers[i]->mk(); SASSERT(is_univariate(a)); euclid_gcd(g, a, new_g); g = new_g; if (is_const(g)) break; } if (!is_const(g)) { CTRACE("content_bug", !divides(g, pp), tout << "GF(" << m().m().to_string(m().p()) << ")\n"; tout << "pp: "; pp->display(tout, m()); tout << "\n"; tout << "var: x" << x << "\n"; tout << "content: " << g << "\n";); c = g; pp = exact_div(pp, c); } else { c = mk_one(); } som_buffers.reset(num_ms); } // Return the leading coefficient (with respect to glex) of p when // p is viewed as a multivariate polynomial Zp[y_1, ..., y_n] with coefficients in Zp[x]. polynomial * lc_glex_ZpX(polynomial const * p, var x) { // collect a*x^k of maximal monomial with respect to glex m_som_buffer.reset(); monomial_ref max_m(m_wrapper); monomial_ref tmp_m(m_wrapper); unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { monomial * m = p->m(i); unsigned k = m->degree_of(x); if (k != 0) { tmp_m = div_x(m, x); m = tmp_m.get(); } if (max_m == 0 || graded_lex_compare(m, max_m) > 0) { // found new maximal monomial m_som_buffer.reset(); max_m = m; m_som_buffer.add(p->a(i), mk_monomial(x, k)); } else if (max_m == m) { // found another a*x^k of max_m m_som_buffer.add(p->a(i), mk_monomial(x, k)); } } SASSERT(!m_som_buffer.empty()); TRACE("mgcd_detail", tout << "maximal monomial: "; max_m->display(tout); tout << "\n";); return m_som_buffer.mk(); } // Wrapper for iccp_ZpX void primitive_ZpX(polynomial const * p, var x, polynomial_ref & pp) { scoped_numeral ci(m()); polynomial_ref c(m_wrapper); iccp_ZpX(p, x, ci, c, pp); } // select a new random value in GF(p) that is not in vals, and store it in r void peek_fresh(scoped_numeral_vector const & vals, unsigned p, scoped_numeral & r) { SASSERT(vals.size() < p); // otherwise we can't keep the fresh value unsigned sz = vals.size(); while (true) { m().set(r, rand() % p); // check if fresh value... unsigned k = 0; for (; k < sz; k++) { if (m().eq(vals[k], r)) break; } if (k == sz) return; // value is fresh } } newton_interpolator_vector m_mgcd_iterpolators; scoped_ptr_vector m_mgcd_skeletons; struct sparse_mgcd_failed {}; // Auxiliary recursive function used in multivariate modular GCD void mod_gcd_rec(polynomial const * u, polynomial const * v, unsigned p, unsigned idx, var_buffer const & vars, polynomial_ref & r) { TRACE("mgcd", tout << "mod_gcd_rec\nu: "; u->display(tout, m_manager, true); tout << "\nv: "; v->display(tout, m_manager, true); tout << "\n";); unsigned num_vars = vars.size(); SASSERT(num_vars > 0); if (idx == num_vars - 1) { SASSERT(is_univariate(u)); SASSERT(is_univariate(v)); euclid_gcd(u, v, r); TRACE("mgcd", tout << "mod_gcd_rec result: "; r->display(tout, m_manager, true); tout << "\n";); return; } var x = vars[idx]; scoped_numeral ci_u(m()), ci_v(m()); polynomial_ref c_u(m_wrapper), pp_u(m_wrapper), lc_u(m_wrapper); polynomial_ref c_v(m_wrapper), pp_v(m_wrapper), lc_v(m_wrapper); iccp_ZpX(u, x, ci_u, c_u, pp_u); iccp_ZpX(v, x, ci_v, c_v, pp_v); lc_u = lc_glex_ZpX(pp_u, x); lc_v = lc_glex_ZpX(pp_v, x); scoped_numeral ci_g(m()); polynomial_ref c_g(m_wrapper); polynomial_ref lc_g(m_wrapper); TRACE("mgcd_detail", tout << "idx: " << idx << "\n"; tout << "x" << x << "\n"; tout << "pp_u = "; pp_u->display(tout, m_manager, true); tout << "\n"; tout << "pp_v = "; pp_v->display(tout, m_manager, true); tout << "\n"; tout << "c_u = "; c_u->display(tout, m_manager, true); tout << "\n"; tout << "c_v = "; c_v->display(tout, m_manager, true); tout << "\n"; tout << "lc_u = "; lc_u->display(tout, m_manager, true); tout << "\n"; tout << "lc_v = "; lc_v->display(tout, m_manager, true); tout << "\n"; tout << "ci_u = " << ci_u << "\n"; tout << "ci_v = " << ci_v << "\n";); m().gcd(ci_u, ci_v, ci_g); euclid_gcd(c_u, c_v, c_g); euclid_gcd(lc_u, lc_v, lc_g); TRACE("mgcd_detail", tout << "c_g = "; c_g->display(tout, m_manager, true); tout << "\n"; tout << "lc_g = "; lc_g->display(tout, m_manager, true); tout << "\n"; tout << "ci_g = " << ci_g << "\n";); skeleton * sk = m_mgcd_skeletons[idx]; // use dense interpolation if skeleton is not available newton_interpolator & interpolator = m_mgcd_iterpolators[idx]; sparse_interpolator sinterpolator(sk); polynomial_ref u1(m_wrapper), v1(m_wrapper), q(m_wrapper); scoped_numeral val(m()); scoped_numeral lc_g_val(m()); polynomial_ref H(m_wrapper), C(m_wrapper); polynomial_ref lc_H(m_wrapper); unsigned min_deg_q = UINT_MAX; unsigned counter = 0; for (;; counter++) { while (true) { peek_fresh(interpolator.inputs(), p, val); // the selected value must satisfy lc_g(val) != 0 univ_eval(lc_g, x, val, lc_g_val); if (!m().is_zero(lc_g_val)) break; } TRACE("mgcd", tout << "x" << x << " -> " << val << "\n";); u1 = substitute(pp_u, 1, &x, &(val.get())); v1 = substitute(pp_v, 1, &x, &(val.get())); mod_gcd_rec(u1, v1, p, idx+1, vars, q); q = mk_glex_monic(q); q = mul(lc_g_val, q); var q_var = max_var(q); unsigned deg_q = q_var == null_var ? 0 : degree(q, q_var); TRACE("mgcd_detail", tout << "counter: " << counter << "\nidx: " << idx << "\nq: " << q << "\ndeg_q: " << deg_q << "\nmin_deg_q: " << min_deg_q << "\nnext_x: x" << vars[idx+1] << "\nmax_var(q): " << q_var << "\n";); if (deg_q < min_deg_q) { TRACE("mgcd_detail", tout << "resetting...\n";); counter = 0; min_deg_q = deg_q; // start from scratch if (sk == nullptr) { interpolator.reset(); interpolator.add(val, q); } else { sinterpolator.reset(); if (!sinterpolator.add(val, q)) throw sparse_mgcd_failed(); } } else if (deg_q == min_deg_q) { TRACE("mgcd_detail", tout << "adding sample point...\n";); if (sk == nullptr) { interpolator.add(val, q); } else { if (!sinterpolator.add(val, q)) throw sparse_mgcd_failed(); } } else { TRACE("mgcd", tout << "skipping q...\n";); continue; } bool found_candidate = false; if (sk == nullptr) { SASSERT(interpolator.num_sample_points() > 0); interpolator.mk(x, H); TRACE("mgcd_detail", tout << "idx: " << idx << "\ncandidate H: " << H << "\n";); lc_H = lc_glex_ZpX(H, x); TRACE("mgcd_detail", tout << "idx: " << idx << "\nlc_H: " << lc_H << "\nlc_g: " << lc_g << "\n";); if (eq(lc_H, lc_g)) { found_candidate = true; } } else { if (sinterpolator.ready()) { if (!sinterpolator.mk(H)) throw sparse_mgcd_failed(); found_candidate = true; } } bool done = false; if (found_candidate) { if (degree(H, x) > 0) primitive_ZpX(H, x, C); else C = normalize(H); TRACE("mgcd_detail", tout << "C: " << C << "\npp_u: " << pp_u << "\npp_v: " << pp_v << "\ndivides(C, pp_u): " << divides(C, pp_u) << "\ndivides(C, pp_v): " << divides(C, pp_v) << "\n";); if (divides(C, pp_u) && divides(C, pp_v)) { r = mul(c_g, C); r = mul(ci_g, r); done = true; } else if (min_deg_q == 0) { r = c_g; r = mul(ci_g, r); done = true; } else if (sk != nullptr) { throw sparse_mgcd_failed(); } } if (done) { TRACE("mgcd", tout << "idx: " << idx << "\nresult: " << r << "\n";); if (sk == nullptr && m_use_sparse_gcd) { // save skeleton skeleton * new_sk = alloc(skeleton, *this, H, x); m_mgcd_skeletons.set(idx, new_sk); } return; } } } // Multivariate modular GCD algorithm void mod_gcd(polynomial const * u, polynomial const * v, power_buffer const & u_var_degrees, power_buffer const & v_var_degrees, polynomial_ref & r) { m_mgcd_iterpolators.set_owner(this); TRACE("mgcd", tout << "mod_gcd\nu: "; u->display(tout, m_manager, true); tout << "\nv: "; v->display(tout, m_manager, true); tout << "\n";); TRACE("mgcd_call", tout << "mod_gcd\nu: "; u->display(tout, m_manager, true); tout << "\nv: "; v->display(tout, m_manager, true); tout << "\n";); SASSERT(!m().modular()); // u and v contain the same set of variables SASSERT(u_var_degrees.size() == v_var_degrees.size()); unsigned num_vars = u_var_degrees.size(); SASSERT(num_vars > 1); // should use uni_mod_gcd if univariate var_buffer vars; power_buffer var_min_degrees; for (unsigned i = 0; i < num_vars; i++) { SASSERT(u_var_degrees[i].get_var() == v_var_degrees[i].get_var()); var x = u_var_degrees[i].get_var(); unsigned d = std::min(u_var_degrees[i].degree(), v_var_degrees[i].degree()); var_min_degrees.push_back(power(x, d)); } std::sort(var_min_degrees.begin(), var_min_degrees.end(), power::lt_degree()); m_mgcd_skeletons.reset(); for (unsigned i = 0; i < num_vars; i++) { vars.push_back(var_min_degrees[i].get_var()); m_mgcd_skeletons.push_back(nullptr); } scoped_numeral c_u(m()), c_v(m()); polynomial_ref pp_u(pm()), pp_v(pm()); ic(u, c_u, pp_u); ic(v, c_v, pp_v); scoped_numeral d_a(m()); m_manager.gcd(c_u, c_v, d_a); unsigned mm_u_pos = pp_u->graded_lex_max_pos(); // position of the maximal monomial in u unsigned mm_v_pos = pp_v->graded_lex_max_pos(); // position of the maximal monomial in v scoped_numeral lc_u(m()); scoped_numeral lc_v(m()); lc_u = pp_u->a(mm_u_pos); lc_v = pp_v->a(mm_v_pos); scoped_numeral lc_g(m()); m().gcd(lc_u, lc_v, lc_g); polynomial_ref u_Zp(m_wrapper); polynomial_ref v_Zp(m_wrapper); polynomial_ref C_star(m_wrapper); scoped_numeral bound(m()); polynomial_ref q(m_wrapper); polynomial_ref candidate(m_wrapper); scoped_numeral p(m()); for (unsigned i = 0; i < NUM_BIG_PRIMES; i++) { m().set(p, g_big_primes[i]); TRACE("mgcd", tout << "trying prime: " << p << "\n";); { scoped_set_zp setZp(m_wrapper, p); u_Zp = normalize(pp_u); if (u_Zp->size() != pp_u->size()) { TRACE("mgcd", tout << "bad prime, coefficient(s) vanished\n";); continue; // bad prime some monomial vanished } v_Zp = normalize(pp_v); if (v_Zp->size() != pp_v->size()) { TRACE("mgcd", tout << "bad prime, coefficient(s) vanished\n";); continue; // bad prime some monomial vanished } TRACE("mgcd", tout << "u_Zp: " << u_Zp << "\nv_Zp: " << v_Zp << "\n";); mod_gcd_rec(u_Zp, v_Zp, g_big_primes[i], 0, vars, q); q = mk_glex_monic(q); scoped_numeral c(m()); m().set(c, lc_g); q = mul(c, q); } TRACE("mgcd", tout << "new q:\n" << q << "\n";); if (is_const(q)) { TRACE("mgcd", tout << "done, modular gcd is one\n";); r = mk_const(d_a); return; } if (C_star.get() == nullptr) { C_star = q; m().set(bound, p); } else { monomial * max_C_star = C_star->m(C_star->graded_lex_max_pos()); monomial * max_q = q->m(q->graded_lex_max_pos()); if (graded_lex_compare(max_q, max_C_star) < 0) { // Discard accumulated image, it was affected by unlucky primes // maximal monomial of q is smaller than maximal monomial of C_star TRACE("mgcd", tout << "discarding image\n";); C_star = q; m().set(bound, p); } else { CRA_combine_images(q, p, C_star, bound, C_star); TRACE("mgcd", tout << "new combined:\n" << C_star << "\n";); } } candidate = normalize(C_star); TRACE("mgcd", tout << "candidate:\n" << candidate << "\n";); scoped_numeral lc_candidate(m()); lc_candidate = candidate->a(candidate->graded_lex_max_pos()); if (m().divides(lc_candidate, lc_g) && divides(candidate, pp_u) && divides(candidate, pp_v)) { TRACE("mgcd", tout << "found GCD\n";); r = mul(d_a, candidate); flip_sign_if_lm_neg(r); TRACE("mgcd", tout << "r: " << r << "\n";); return; } } // Oops, modular GCD failed, not enough primes // fallback to prs gcd_prs(u, v, max_var(u), r); } void gcd(polynomial const * u, polynomial const * v, polynomial_ref & r) { power_buffer u_var_degrees; power_buffer v_var_degrees; TRACE("gcd_calls", tout << "gcd\nu: "; u->display(tout, m_manager); tout << "\nv: "; v->display(tout, m_manager); tout << "\n";); TRACE("polynomial_gcd", tout << "gcd\nu: "; u->display(tout, m_manager); tout << "\nv: "; v->display(tout, m_manager); tout << "\nis_zero(u): " << is_zero(u) << ", is_const(u): " << is_const(u) << "\n"; tout << "is_zero(v): " << is_zero(v) << ", is_const(v): " << is_const(v) << "\n"; tout << "modular: " << m().modular() << "\n";); if (is_zero(u)) { r = const_cast(v); flip_sign_if_lm_neg(r); return; } if (is_zero(v)) { r = const_cast(u); flip_sign_if_lm_neg(r); return; } if (u == v) { r = const_cast(u); flip_sign_if_lm_neg(r); return; } if (is_const(u) || is_const(v)) { scoped_numeral i_u(m_manager), i_v(m_manager); ic(v, i_v); ic(u, i_u); scoped_numeral a(m_manager); m_manager.gcd(i_v, i_u, a); r = mk_const(a); return; } // Search for a variable x that occurs only in u or v. var_max_degrees(u, u_var_degrees); std::sort(u_var_degrees.begin(), u_var_degrees.end(), power::lt_var()); var_max_degrees(v, v_var_degrees); std::sort(v_var_degrees.begin(), v_var_degrees.end(), power::lt_var()); TRACE("polynomial_gcd", tout << "u var info\n"; for (unsigned i = 0; i < u_var_degrees.size(); i++) tout << u_var_degrees[i] << " "; tout << "\n"; tout << "v var info\n"; for (unsigned i = 0; i < v_var_degrees.size(); i++) tout << v_var_degrees[i] << " "; tout << "\n";); var x = null_var; bool u_found = false; bool v_found = false; unsigned i = 0; unsigned u_sz = u_var_degrees.size(); unsigned v_sz = v_var_degrees.size(); unsigned sz = std::min(u_sz, v_sz); for (; i < sz; i++) { var xu = u_var_degrees[i].get_var(); var xv = v_var_degrees[i].get_var(); if (xu < xv) { x = xu; u_found = true; break; } if (xu > xv) { x = xv; v_found = true; break; } } if (!u_found && !v_found && i < u_sz) { x = u_var_degrees[i].get_var(); u_found = true; } if (!u_found && !v_found && i < v_sz) { x = v_var_degrees[i].get_var(); v_found = true; } if (u_found) { // u has a variable x that v doesn't have. // Thus, gcd(u, v) = gcd(content(u, x), v) gcd_content(u, x, v, r); return; } if (v_found) { // v has a variable x that u doesn't have. // Thus, gcd(u, v) = gcd(u, content(v, x)) gcd_content(v, x, u, r); return; } // TODO: // Try to find a variable x that occurs linearly in u or v // In this case, the GCD is linear or constant in x. // Assume x occurs linearly in u. Then, // gcd(u, v) = gcd(content(u, x), content(v, x)) if pp(u, x) does not divide pp(v, x) // gcd(u, v) = gcd(content(u, x), content(v, x))*pp(u,x) if pp(u, x) divides pp(v, x) // // select variable with minimal degree x = u_var_degrees[sz - 1].get_var(); // give preference to maximal variable SASSERT(u_var_degrees[sz - 1].get_var() == v_var_degrees[sz - 1].get_var()); SASSERT(max_var(u) == max_var(v)); SASSERT(max_var(u) == x); #if 0 unsigned min_k = std::max(m_u_var_degrees[sz - 1].degree(), m_v_var_degrees[sz - 1].degree()); unsigned max_var_bias = 2; // the basic procedures are optimized for operating on the maximal variable. So, we have a bias to select the maximal one if (min_k > max_var_bias) { min_k -= max_var_bias; i = sz - 1; while (i > 0) { --i; SASSERT(m_u_var_degrees[i].get_var() == m_v_var_degrees[i].get_var()); unsigned k = std::max(m_u_var_degrees[i].degree(), m_v_var_degrees[i].degree()); if (k < min_k) { x = m_u_var_degrees[i].get_var(); min_k = k; } } } #endif if (!m().modular() && !m_use_prs_gcd) { SASSERT(max_var(u) == max_var(v)); if (is_univariate(u)) { SASSERT(is_univariate(v)); uni_mod_gcd(u, v, r); } else { try { #ifdef Z3DEBUG polynomial_ref orig_u(m_wrapper); polynomial_ref orig_v(m_wrapper); if (is_debug_enabled("mgcd_check")) { orig_u = const_cast(u); orig_v = const_cast(v); } #endif mod_gcd(u, v, u_var_degrees, v_var_degrees, r); #ifdef Z3DEBUG if (is_debug_enabled("mgcd_check")) { polynomial_ref r2(m_wrapper); flet use_prs(m_use_prs_gcd, false); gcd_prs(orig_u, orig_v, x, r2); CTRACE("mgcd_bug", !eq(r, r2), tout << "u: " << orig_u << "\nv: " << orig_v << "\nr1: " << r << "\nr2: " << r2 << "\n";); SASSERT(eq(r, r2)); } #endif } catch (const sparse_mgcd_failed &) { flet use_prs(m_use_prs_gcd, false); gcd_prs(u, v, x, r); } } } else { gcd_prs(u, v, x, r); } } monomial * derivative(monomial const * m, var x) { return mm().derivative(m, x); } polynomial * derivative(polynomial const * p, var x) { SASSERT(is_valid(x)); SASSERT(m_cheap_som_buffer.empty()); unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { monomial * m = p->m(i); unsigned d = m->degree_of(x); TRACE("polynomial", m->display(tout); tout << " degree_of x" << x << ": " << d << "\n";); if (d > 0) { scoped_numeral n(m_manager); m_manager.set(n, d); scoped_numeral a(m_manager); m_manager.mul(p->a(i), n, a); m_cheap_som_buffer.add_reset(a, derivative(m, x)); } } return m_cheap_som_buffer.mk(); } void square_free(polynomial const * p, polynomial_ref & r) { if (is_zero(p)) { r = mk_zero(); return; } if (is_const(p)) { r = const_cast(p); return; } var x = max_var(p); scoped_numeral i(m_manager); polynomial_ref c(pm()), pp(pm()); iccp(p, x, i, c, pp); polynomial_ref sqf_c(pm()); square_free(c, sqf_c); polynomial_ref pp_prime(pm()); pp_prime = derivative(pp, x); polynomial_ref g(pm()); gcd(pp, pp_prime, g); if (is_const(g)) { if (eq(sqf_c, c)) { r = const_cast(p); return; } } else { pp = exact_div(pp, g); } r = mul(i, sqf_c); r = mul(r, pp); } bool is_square_free(polynomial const * p) { polynomial_ref r(pm()); square_free(p, r); SASSERT(!eq(p, r) || p == r.get()); // this is a property of the square_free procedure return p == r.get(); } void square_free(polynomial const * p, var x, polynomial_ref & r) { if (is_zero(p)) { r = mk_zero(); return; } if (is_const(p)) { r = const_cast(p); return; } polynomial_ref p_prime(pm()); p_prime = derivative(p, x); polynomial_ref g(pm()); gcd(p, p_prime, g); if (is_const(g)) { r = const_cast(p); } else { r = exact_div(p, g); } } bool is_square_free(polynomial const * p, var x) { polynomial_ref r(pm()); square_free(p, x, r); SASSERT(!eq(p, r) || p == r.get()); // this is a property of the square_free procedure return p == r.get(); } void pw(polynomial const * p, unsigned k, polynomial_ref & r) { if (k == 0) { r = mk_one(); return; } if (k == 1) { r = const_cast(p); return; } polynomial_ref result(pm()); result = const_cast(p); for (unsigned i = 1; i < k; i++) result = mul(result, const_cast(p)); r = result; #if 0 SASSERT(k >= 2); unsigned mask = 1; polynomial_ref p2(pm()); p2 = const_cast(p); r = mk_one(); while (true) { if (mask & k) r = mul(r, p2); mask = mask << 1; if (mask > k) return; p2 = mul(p2, p2); } #endif } bool eq(polynomial const * p1, polynomial const * p2) { if (p1 == p2) return true; unsigned sz1 = p1->size(); unsigned sz2 = p2->size(); if (sz1 != sz2) return false; if (sz1 == 0) return true; if (max_var(p1) != max_var(p2)) return false; m_m2pos.set(p1); for (unsigned i = 0; i < sz2; i++) { unsigned pos1 = m_m2pos.get(p2->m(i)); if (pos1 == UINT_MAX || !m_manager.eq(p1->a(pos1), p2->a(i))) { m_m2pos.reset(p1); return false; } } m_m2pos.reset(p1); return true; } polynomial * compose_1_div_x(polynomial const * p) { SASSERT(is_univariate(p)); if (is_const(p)) return const_cast(p); SASSERT(m_cheap_som_buffer.empty()); var x = max_var(p); unsigned n = degree(p, x); unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { monomial * m = p->m(i); SASSERT(m->size() <= 1); monomial * new_m = mk_monomial(x, n - m->degree_of(x)); m_cheap_som_buffer.add(p->a(i), new_m); } return m_cheap_som_buffer.mk(); } void push_power(sbuffer & pws, var x, unsigned d) { if (d > 0) pws.push_back(power(x, d)); } polynomial * compose_x_div_y(polynomial const * p, var y) { SASSERT(is_univariate(p)); SASSERT(max_var(p) != y); if (is_const(p)) return const_cast(p); SASSERT(m_cheap_som_buffer.empty()); var x = max_var(p); unsigned n = degree(p, x); unsigned sz = p->size(); sbuffer pws; for (unsigned i = 0; i < sz; i++) { unsigned k = p->m(i)->degree_of(x); pws.reset(); if (x < y) { push_power(pws, x, k); push_power(pws, y, n - k); } else { push_power(pws, y, n - k); push_power(pws, x, k); } monomial * new_m = mk_monomial(pws.size(), pws.c_ptr()); m_cheap_som_buffer.add(p->a(i), new_m); } return m_cheap_som_buffer.mk(); } polynomial * compose_y(polynomial const * p, var y) { SASSERT(is_valid(y)); SASSERT(is_univariate(p)); if (y == max_var(p) || is_const(p)) return const_cast(p); SASSERT(m_cheap_som_buffer.empty()); unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { monomial * m = p->m(i); SASSERT(m->size() <= 1); monomial * new_m; if (m->size() == 0) new_m = m; else new_m = mk_monomial(y, m->degree(0)); m_cheap_som_buffer.add(p->a(i), new_m); } return m_cheap_som_buffer.mk(); } polynomial * compose_minus_x(polynomial const * p) { SASSERT(is_univariate(p)); if (is_const(p)) return const_cast(p); SASSERT(m_cheap_som_buffer.empty()); scoped_numeral a(m_manager); unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { monomial * m = p->m(i); if (m->total_degree() % 2 == 0) { m_cheap_som_buffer.add(p->a(i), p->m(i)); } else { m_manager.set(a, p->a(i)); m_manager.neg(a); m_cheap_som_buffer.add(a, p->m(i)); } } return m_cheap_som_buffer.mk(); } /** \brief Store the positions (in m_degree2pos) of the monomials of an univariate polynomial. */ void save_degree2pos(polynomial const * p) { SASSERT(is_univariate(p)); var x = max_var(p); unsigned n = degree(p, x); m_degree2pos.reserve(n+1, UINT_MAX); unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { monomial * m = p->m(i); SASSERT(m->size() <= 1); SASSERT(m_degree2pos[m->total_degree()] == UINT_MAX); m_degree2pos[m->total_degree()] = i; } } /** \brief Undo the modifications in m_degree2pos performed by save_degree2pos. */ void reset_degree2pos(polynomial const * p) { SASSERT(is_univariate(p)); unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { monomial * m = p->m(i); SASSERT(m->size() <= 1); SASSERT(m_degree2pos[m->total_degree()] == i); m_degree2pos[m->total_degree()] = UINT_MAX; } } // muladd may throw if the cancel flag is set. // So we wrap the degree2pos set and reset // in a scoped class to ensure the state is clean // on exit. struct scoped_degree2pos { imp& pm; polynomial const* p; scoped_degree2pos(imp& pm, polynomial const* p): pm(pm), p(p) { pm.save_degree2pos(p); } ~scoped_degree2pos() { pm.reset_degree2pos(p); } }; /** \brief Given an univariate polynomial p(x) and a polynomial q(y_1, ..., y_n), return a polynomial r(y_1, ..., y_n) = p(q(y_1, ..., y_n)). */ void compose(polynomial const * p, polynomial const * q, polynomial_ref & r) { SASSERT(is_univariate(p)); if (is_const(p)) { r = const_cast(p); return; } var x = max_var(p); unsigned d = degree(p, x); scoped_degree2pos _sd2pos(*this, p); scoped_numeral a(m()); m_manager.set(a, p->a(m_degree2pos[d])); r = mk_const(a); for (unsigned i = 1; i <= d; i++) { unsigned pos = m_degree2pos[d-i]; if (pos != UINT_MAX) m_manager.set(a, p->a(pos)); else m_manager.reset(a); r = muladd(q, r, a); } } polynomial * mk_x_minus_y(var x, var y) { numeral zero(0); numeral minus_one; // It is not safe to initialize with -1 when numeral_manager is GF_2 m_manager.set(minus_one, -1); numeral as[2] = { numeral(1), std::move(minus_one) }; var xs[2] = { x, y }; return mk_linear(2, as, xs, zero); } void compose_x_minus_y(polynomial const * p, var y, polynomial_ref & r) { SASSERT(is_valid(y)); SASSERT(is_univariate(p)); var x = max_var(p); if (y == max_var(p)) { r = coeff(p, x, 0); return; } polynomial_ref x_minus_y(pm()); x_minus_y = mk_x_minus_y(x, y); return compose(p, x_minus_y, r); } polynomial * mk_x_plus_y(var x, var y) { numeral zero(0); numeral as[2] = { numeral(1), numeral(1) }; var xs[2] = { x, y }; return mk_linear(2, as, xs, zero); } void compose_x_plus_y(polynomial const * p, var y, polynomial_ref & r) { SASSERT(is_valid(y)); SASSERT(is_univariate(p)); var x = max_var(p); polynomial_ref x_plus_y(pm()); x_plus_y = mk_x_plus_y(x, y); return compose(p, x_plus_y, r); } // Return the polynomial x - c polynomial * mk_x_minus_c(var x, numeral const & c) { numeral as[2]; m_manager.set(as[0], c); m_manager.set(as[1], 1); m_manager.neg(as[0]); polynomial * p = mk_univariate(x, 1, as); TRACE("polynomial", tout << "x - c: "; p->display(tout, m_manager); tout << "\n";); m_manager.del(as[0]); m_manager.del(as[1]); return p; } void compose_x_minus_c(polynomial const * p, numeral const & c, polynomial_ref & r) { SASSERT(is_univariate(p)); if (m_manager.is_zero(c)) { r = const_cast(p); return; } var x = max_var(p); polynomial_ref x_minus_c(pm()); x_minus_c = mk_x_minus_c(x, c); return compose(p, x_minus_c, r); } /** \brief Template for computing several variations of pseudo division algorithm. If degree(p) < degree(q) --> Q = m_zero, d = 0, R = p The following property is guaranteed by this method l(q)^d * p = Q * q + R where l(q) is coeff(q, x, degree(q, x)) Possible configurations: Exact_d == true, then d = degree(p) - degree(q) + 1. If Exact_d == false, then d <= degree(p) - degree(q) + 1. If Quotient == false, Q is not computed. If ModD == true, then x2d must be different from 0. Moreover, p and q are assumed to be normalized modulo x2d. For all x, x2d->degree(x) > 0 implies degree(p, x) < x2d->degree(x) and degree(q, x) < x2d->degree(x) Moreover, the division algorithm will compute Q and R modulo x2d. */ template void pseudo_division_core(polynomial const * p, polynomial const * q, var x, unsigned & d, polynomial_ref & Q, polynomial_ref & R, var2degree const * x2d = nullptr) { SASSERT(is_valid(x)); SASSERT(!ModD || x2d != 0); TRACE("polynomial", tout << "pseudo_division\np: "; p->display(tout, m_manager); tout << "\nq: "; q->display(tout, m_manager); tout << "\nx: " << x << "\n";); polynomial * A = const_cast(p); polynomial * B = const_cast(q); unsigned deg_A = degree(A, x); unsigned deg_B = degree(B, x); if (deg_B == 0) { R = m_zero; if (Quotient) { if (Exact_d) { d = deg_A /* - deg_B */ + 1; // Since degree(B) = 0, lc(B) == B // lc(B)^d * A = Q * B + R --> (using R == 0, lc(B) == B) // B^d * A = Q * B // Thus, Q = A * B^{d-1} if (d == 1) { Q = A; return; } polynomial_ref Bdm1(pm()); pw(B, d - 1, Bdm1); Q = mul(A, Bdm1); if (ModD) Q = mod_d(Q, *x2d); } else { d = 1; Q = A; } } return; } if (deg_B > deg_A) { Q = m_zero; R = A; d = 0; } scoped_numeral minus_a(m_manager); polynomial_ref l_B(pm()); // leading coefficient of B (that is, coefficient of x^(deg_B)) polynomial_ref r_B(pm()); // reduct of B (that is, B without leading coefficient) l_B = coeff(B, x, deg_B, r_B); d = 0; R = A; Q = m_zero; while (true) { checkpoint(); TRACE("polynomial", tout << "A: "; A->display(tout, m_manager); tout << "\n"; tout << "B: "; B->display(tout, m_manager); tout << "\n"; tout << "l_B: "; l_B->display(tout, m_manager); tout << "\n"; tout << "r_B: "; r_B->display(tout, m_manager); tout << "\n"; tout << "Q: "; Q->display(tout, m_manager); tout << "\n"; tout << "R: "; R->display(tout, m_manager); tout << "\n"; tout << "d: " << d << "\n";); unsigned deg_R = degree(R, x); if (deg_B > deg_R) { if (Exact_d) { // Adjust Q and R unsigned exact_d = deg_A - deg_B + 1; SASSERT(d <= exact_d); if (d < exact_d) { unsigned e = exact_d - d; polynomial_ref l_B_e(pm()); pw(l_B, e, l_B_e); TRACE("polynomial", tout << "l_B_e: " << l_B_e << "\n";); if (Quotient) { Q = mul(l_B_e, Q); if (ModD) Q = mod_d(Q, *x2d); } R = mul(l_B_e, R); if (ModD) R = mod_d(R, *x2d); } } return; } // S <- l_R * x^(deg_R - deg_B) // R <- l_B * R - S * B // Note that the goal is to cancel x^deg_R in R. // m_som_buffer will contain the new monomials of R. m_som_buffer.reset(); // We can accomplish that by traversing the current R, and // - For each monomial a * m * x^deg_R --> m_som_buffer.addmul(-a, m * x^(deg_R - deg_B), r_B) // Note that m*x^(deg_R - deg_B) is div_x_k(m*x^deg_R, deg_B) // - For other monomials a*m, ---> m_som_buffer.addmul(a, m, l_B) // Note that, with this trick and don't need to create the temporary polynomials l_B * R and S * B // // If the quotient needs to be computed, we have that // S <- l_R * x^(deg_R - deg_B) // Q <- l_B * Q + S // The new monomials of Q are stored in m_som_buffer2 // When traversing R, for each monomial a*m*x^deg_R we add m_som_buffer2.add(a, m*x^(deg_R - deg_B)) m_som_buffer2.reset(); // unsigned sz = R->size(); for (unsigned i = 0; i < sz; i++) { monomial * m = R->m(i); numeral const & a = R->a(i); if (m->degree_of(x) == deg_R) { monomial_ref m_prime(pm()); m_prime = div_x_k(m, x, deg_B); CTRACE("polynomial", m->degree_of(x) != deg_R - deg_B, tout << "deg_R: " << deg_R << ", deg_B: " << deg_B << ", x: " << x << "\n"; m->display(tout); tout << ", "; m_prime->display(tout); tout << "\n";); SASSERT(m->degree_of(x) == deg_R); SASSERT(m_prime->degree_of(x) == deg_R - deg_B); if (Quotient) { m_som_buffer2.add(a, m_prime); } m_manager.set(minus_a, a); m_manager.neg(minus_a); m_som_buffer.addmul(minus_a, m_prime, r_B); } else { m_som_buffer.addmul(a, m, l_B); } } if (ModD) m_som_buffer.mod_d(*x2d); R = m_som_buffer.mk(); if (Quotient) { // m_som_buffer2 contains new monomials of Q <- l_B Q + S // We have already copied S to m_som_buffer2. // To add l_B * Q, we just traverse Q executing addmul(Q->a(i), Q->m(i), l_B) unsigned sz = Q->size(); for (unsigned i = 0; i < sz; i++) { m_som_buffer2.addmul(Q->a(i), Q->m(i), l_B); } if (ModD) m_som_buffer2.mod_d(*x2d); Q = m_som_buffer2.mk(); } d++; } } void exact_pseudo_remainder(polynomial const * p, polynomial const * q, var x, polynomial_ref & R) { unsigned d; polynomial_ref Q(pm()); pseudo_division_core(p, q, x, d, Q, R); } void pseudo_remainder(polynomial const * p, polynomial const * q, var x, unsigned & d, polynomial_ref & R) { #ifdef Z3DEBUG polynomial_ref old_p(pm()); polynomial_ref old_q(pm()); old_p = const_cast(p); // R may be aliasing p and q old_q = const_cast(q); polynomial_ref Q(pm()); pseudo_division_core(p, q, x, d, Q, R); // debugging code // check if lc(q)^d * p = Q * q + R polynomial_ref l(pm()); l = coeff(old_q, x, degree(q, x)); polynomial_ref ld(pm()); pw(l, d, ld); polynomial_ref lhs(pm()); lhs = mul(ld, old_p); polynomial_ref rhs(pm()); rhs = mul(Q, old_q); rhs = add(rhs, R); bool is_eq = eq(lhs, rhs); TRACE("pseudo_remainder", tout << "pseudo_division bug\n"; tout << "p: "; old_p->display(tout, m_manager); tout << "\n"; tout << "q: "; old_q->display(tout, m_manager); tout << "\n"; tout << "Q: " << Q << "\nR: " << R << "\n"; tout << "l^d: " << ld << "\nlhs: " << lhs << "\nrhs: " << rhs << "\n";); SASSERT(is_eq); #else polynomial_ref Q(pm()); pseudo_division_core(p, q, x, d, Q, R); #endif } void exact_pseudo_division(polynomial const * p, polynomial const * q, var x, polynomial_ref & Q, polynomial_ref & R) { unsigned d; pseudo_division_core(p, q, x, d, Q, R); } void pseudo_division(polynomial const * p, polynomial const * q, var x, unsigned & d, polynomial_ref & Q, polynomial_ref & R) { pseudo_division_core(p, q, x, d, Q, R); } polynomial * exact_div(polynomial const * p, numeral const & c) { SASSERT(!m().is_zero(c)); som_buffer & R = m_som_buffer; R.reset(); numeral tmp; unsigned sz = p->size(); for (unsigned i = 0; i < sz; ++ i) { SASSERT(m().divides(c, p->a(i))); m().div(p->a(i), c, tmp); if (!m().is_zero(tmp)) { R.add(tmp, p->m(i)); } } m().del(tmp); return R.mk(); } polynomial * exact_div(polynomial const * p, polynomial const * q) { TRACE("polynomial", tout << "exact division\np: "; p->display(tout, m_manager); tout << "\nq: "; q->display(tout, m_manager); tout << "\n";); if (is_zero(p)) return const_cast(p); SASSERT(!is_zero(q)); m_som_buffer.reset(); m_som_buffer2.reset(); som_buffer & R = m_som_buffer; som_buffer & C = m_som_buffer2; R.add(p); unsigned max_q = q->graded_lex_max_pos(); monomial * m_q = q->m(max_q); numeral const & a_q = q->a(max_q); monomial_ref m_r_q_ref(pm()); scoped_numeral a_r_q(m_manager); while (true) { checkpoint(); unsigned max_R = R.graded_lex_max_pos(); if (max_R == UINT_MAX) { // R is empty R.reset(); return C.mk(); } monomial const * m_r = R.m(max_R); numeral const & a_r = R.a(max_R); monomial_ref m_r_q(pm()); VERIFY(div(m_r, m_q, m_r_q)); TRACE("polynomial", tout << "m_r: "; m_r->display(tout); tout << "\nm_q: "; m_q->display(tout); tout << "\n"; if (m_r_q) { tout << "m_r_q: "; m_r_q->display(tout); tout << "\n"; }); m_r_q_ref = m_r_q; m_manager.div(a_r, a_q, a_r_q); C.add(a_r_q, m_r_q); // C <- C + (a_r/a_q)*(m_r/m_q) m_manager.neg(a_r_q); R.addmul(a_r_q, m_r_q, q); // R <- R - (a_r/a_q)*(m_r/m_q)*Q } } // Return true if q divides p. bool divides(polynomial const * q, polynomial const * p) { TRACE("polynomial", tout << "divides\nq: "; q->display(tout, m_manager); tout << "\np: "; p->display(tout, m_manager); tout << "\n";); TRACE("divides", tout << "divides\nq: "; q->display(tout, m_manager); tout << "\np: "; p->display(tout, m_manager); tout << "\n";); if (is_zero(p)) return true; SASSERT(!is_zero(q)); m_som_buffer.reset(); m_som_buffer2.reset(); som_buffer & R = m_som_buffer; R.add(p); unsigned max_q = q->graded_lex_max_pos(); monomial * m_q = q->m(max_q); numeral const & a_q = q->a(max_q); monomial_ref m_r_q_ref(pm()); scoped_numeral a_r_q(m_manager); while (true) { checkpoint(); unsigned max_R = R.graded_lex_max_pos(); if (max_R == UINT_MAX) { // R is empty return true; } monomial const * m_r = R.m(max_R); numeral const & a_r = R.a(max_R); monomial_ref m_r_q(pm()); bool q_div_r = div(m_r, m_q, m_r_q); m_r_q_ref = m_r_q; TRACE("polynomial", tout << "m_r: "; m_r->display(tout); tout << "\nm_q: "; m_q->display(tout); tout << "\n"; if (m_r_q) { tout << "m_r_q: "; m_r_q->display(tout); tout << "\n"; }); if (!q_div_r) return false; if (!m_manager.divides(a_q, a_r)) return false; m_manager.div(a_r, a_q, a_r_q); m_manager.neg(a_r_q); R.addmul(a_r_q, m_r_q, q); // R <- R - (a_r/a_q)*(m_r/m_q)*Q } } void quasi_resultant(polynomial const * p, polynomial const * q, var x, polynomial_ref & r) { // For h_0 = p and h_1 = q, we compute the following sequence // using the pseudo remainder procedure // // l(h_1)^d_1 * h_0 = q_1 * h_1 + h_2 // l(h_2)^d_2 * h_1 = q_2 * h_2 + h_3 // l(h_3)^d_3 * h_2 = q_3 * h_3 + h_4 // ... // l(h_n)^d_n * h_{n-1} = q_n * h_n + h_{n+1} // // where // degree(h_i, x) > 0 for all i in [0, n], and // degree(h_{n+1}, x) == 0 // // l(h_i) is the leading coefficient of h_i with respect to variable x. // l(h_i) is in general a polynomial. // For example, l( y*x^2 + x^2 + y^2 x + 1 ) = y + 1 // // d^i is an unsigned integer. It is introduce by the pseudo remainder procedure // because the coefficients of x^k do not form a field. That is, they are elements of a polynomial ring Q[y_1, ..., y_n]. // Their values are irrelevant for the correctness of this procedure. // // Observation 1: // If h_0 and h_1 are polynomials in Q[y_1, ..., y_n, x], // then h_{n+1} is a polynomial in Q[y_1, ..., y_n]. // // Observation 2: // For any (complex values) a_1, ..., a_n, b, // if h_0(a_1, ..., a_n, b) = h_1(a_1, ..., a_n, b) = 0 // then for any h_i in the sequence, h_i(a_1, ..., a_n, b) = 0. // In particular, h_{n+1}(a_1, ..., a_n) = 0 // // Observation 3: // The procedure terminates because degree(h_i, x) > degree(h_{i+1}, x) // for all i >= 1 // // Observation 4: // If h_{n+1} is the zero polynomial, then // For any complex numbers a_1, ..., a_n // the univariate polynomials p(a_1, ..., a_n, x) and q(a_1, ..., a_n, x) in C[x] // have a common root. // Reason: // If h_n(a_1, ..., a_n, x) is not the zero polynomial, then it is the GCD of p(a_1, ..., a_n, x) and q(a_1, ..., a_n, x), // and it contains the common roots. // If h_n(a_1, ..., a_n, x) is the zero polynomial, then // we consider h_{n-1}(a_1, ..., a_n, x). If it is not the zero polynomial then it is the GCD and we are done, // otherwise we consider h_{n-2}(a_1, ..., a_n, x), and we continue the same process. // Thus, eventually we find a h_i(a_1, ..., a_n, x) for i > 1 that is the GCD, or q(a_1, ..., a_n, x) is the zero polynomial, // and any polynomial p(a_1, ..., a_n, x) has a common root with the zero polynomial. // SASSERT(degree(p, x) > 0); SASSERT(degree(q, x) > 0); polynomial_ref h_0(pm()); polynomial_ref h_1(pm()); polynomial_ref h_2(pm()); if (degree(p, x) < degree(q, x)) std::swap(p, q); h_0 = const_cast(p); h_1 = const_cast(q); unsigned d; while (true) { SASSERT(degree(h_1, x) <= degree(h_0, x)); pseudo_remainder(h_0, h_1, x, d, h_2); TRACE("polynomial", tout << "h_0: " << h_0 << "\nh_1: " << h_1 << "\nh_2: " << h_2 << "\n";); SASSERT(degree(h_2, x) < degree(h_1, x)); // We have that // l(h_1)^d h_0 = Q h_1 + h_2. // Q is the quotient of the division. // l(h_1) is the leading coefficient of h_1. // From this equation, we have that any zero of h_0 and h_1 is also a zero of h_2 if (degree(h_2, x) == 0) { r = h_2; return; } h_0 = h_1; h_1 = h_2; // this computation will terminate since the pseudo_remainder guarantees that // degree(h_2, x) < degree(h_1, x) } } // sign = sign * (-1)^(deg_A * deg_B) static void update_sign(unsigned deg_A, unsigned deg_B, bool & sign) { if (deg_A % 2 == 0 || deg_B % 2 == 0) return; sign = !sign; } /** \brief Compute the resultant of p and q with respect to r. The Resultant is usually defined as the determinant of the Sylvester matrix for p and q. This matrix is built using the coefficients of p and q with respect to x. Given p and q polynomials in Q[y_1, ..., y_n, x], r is a polynomial in Q[y_1, ..., y_n]. Property 1) If p and q can be written as p = a_m * (x - alpha_1) * ... * (x - alpha_m) q = b_n * (x - beta_1) * ... * (x - beta_n) Then, Res(p, q, x) = a_m^n * b_n^m * Product_{i in [1,m], j in [1, n]} (alpha_i - beta_j) Remark: if p and q are univariate polynomials, then alpha_i's and beta_j's are the roots of p and q, then Res(p, q, x) is the product of the differences of their roots modulo a constant. Property 2) For any (complex values) a_1, ..., a_n, b, if p(a_1, ..., a_n, b) = q(a_1, ..., a_n, b) = 0, then r(a_1, ..., b_n) = 0 Property 3) There are polynomials U and V in Q[y_1, ..., y_n, x] s.t. p*U + q*V = r s.t. deg(U, x) < deg(q, x) and deg(V, x) < deg(p, x) We use Res(p, q, x) to denote the resultant of p and q. Property 4) (follows from 1) If Res(p, q, x) = 0, then p and q have a common divisor. Resultant Calculus: Let A and B be two polynomials of degree m and n on variable x. Let c and d be numerals. Res(A, B, x) = (-1)^(m*n) * Res(B, A, x) Res(cA, B, x) = c^n * Res(A, B, x) Res(c, B, x) = c^n Res(0, B, x) = 0 if n > 0 Res(c, d, x) = 1 Res(A, B1*B2, x) = Res(A, B1, x)*Res(A, B2, x) Res(A, A*Q + B, x) = coeff(A, x)^(l-n) * Res(A, B, x) where l = deg(A*Q + R) The last equation suggests an approach for computing the Resultant using pseudo-division instead of determinants. Given A and B s.t. degree(A, x) = m >= n = degree(B, x) Then lc^d * A = Q * B + R where lc = coeff(B, x), and pseudo_division(A, B, x, d, Q, R); r = degree(R) Then we have: (lc^d)^n * Res(A, B) = Res(A * l^d, B) = Res(Q * B + R, B) = (-1)^(m*n) * Res(B, Q * B + R) = (-1)^(m*n) * lc^(m-r) * Res(B, R) So, lc^(d*n) * Res(A, B) = (-1)^(m*n) * lc^(m-r) * Res(B, R) From the pseudo-division algorithm, we have that: 1) 1 <= d <= m - n + 1 2) 0 <= r < n <= m d*n >= m >= m-r So, if d*n == m-r Res(A, B) = (-1)^(m*n) * Res(R, B) if d*n > m-r Res(A, B) = (-1)^(m*n) * exact_div(Res(R, B), lc^(d*n - m + r)) if d*n < m-r Res(A, B) = (-1)^(m*n) * mul(Res(R, B), lc^(m - r - d * n)) */ void resultant(polynomial const * p, polynomial const * q, var x, polynomial_ref & result) { polynomial_ref A(pm()); polynomial_ref B(pm()); A = const_cast(p); B = const_cast(q); TRACE("resultant", tout << "resultant(A, B, x)\nA: " << A << "\nB: " << B << "\nx: " << x << "\n";); // Res(0, B) = Res(A, 0) = 0 if (is_zero(A) || is_zero(B)) { result = mk_zero(); return; } // Res(c, d, x) = 1 c and d are constants // Res(c, B, x) = c^{deg(B)} if (is_const(A)) { if (is_const(B)) result = mk_one(); else pw(A, degree(B, x), result); return; } // Res(A, d, x) = d^{deg(A)} if (is_const(B)) { pw(B, degree(A, x), result); return; } // decompose A and B into // A = iA*cA*ppA // B = iB*cB*ppB scoped_numeral iA(m_manager), iB(m_manager); polynomial_ref cA(pm()), cB(pm()); polynomial_ref ppA(pm()), ppB(pm()); iccp(A, x, iA, cA, ppA); iccp(B, x, iB, cB, ppB); cA = mul(iA, cA); cB = mul(iB, cB); // At this point, A = cA*ppA and B = cB*ppB, where cA and cB are the content of A and B, and ppA and ppB the primitive polynomials polynomial_ref t(pm()); // t <- cA^{deg(B)}*cB^{deg(A)} pw(cA, degree(B, x), cA); pw(cB, degree(A, x), cB); t = mul(cA, cB); A = ppA; B = ppB; // TRACE("resultant", tout << "resultant(A, B, x) after normalization\nA: " << A << "\nB: " << B << "\nx: " << x << "\n"; tout << "t: " << t << "\n";); int s = 1; unsigned degA = degree(A, x); unsigned degB = degree(B, x); if (degA < degB) { A.swap(B); if (degA % 2 == 1 && degB % 2 == 1) s = -1; } polynomial_ref R(pm()); polynomial_ref g(pm()); polynomial_ref h(pm()); polynomial_ref new_h(pm()); // g <- 1 g = mk_one(); // h <- 1 h = mk_one(); while (true) { TRACE("resultant", tout << "A: " << A << "\nB: " << B << "\n";); degA = degree(A, x); degB = degree(B, x); SASSERT(degA >= degB); unsigned delta = degA - degB; if (degA % 2 == 1 && degB % 2 == 1) s = -s; // lc(B)^delta+1 A = BQ + R exact_pseudo_remainder(A, B, x, R); A = B; // B <- R/g*h^{delta} B = exact_div(R, g); for (unsigned i = 0; i < delta; i++) B = exact_div(B, h); // g <- lc(A) g = lc(A, x); // h <- g^delta * h^{1-delta} new_h = mk_one(); pw(g, delta, new_h); if (delta > 1) { for (unsigned i = 0; i < delta - 1; i++) new_h = exact_div(new_h, h); } h = new_h; if (degree(B, x) == 0) { unsigned degA = degree(A, x); // h <- lc(B)^{deg(A)} * h^{1-deg(A)} new_h = lc(B, x); pw(new_h, degA, new_h); if (degA > 1) { for (unsigned i = 0; i < degA - 1; i++) new_h = exact_div(new_h, h); } h = new_h; // result <- s*t*h result = mul(t, h); if (s < 0) result = neg(result); return; } } } /** \brief Return the discriminant of p with respect to x. Disc(p, x) = (-1)^(m * (m-1)/2) * Resultant(p, dp/dx, x) / coeff(p, x) dp/dx is the derivative of p with respect to x. */ void discriminant(polynomial const * p, var x, polynomial_ref & r) { polynomial_ref p_prime(pm()); unsigned m = degree(p, x); if (m == 0) { r = m_zero; return; } p_prime = derivative(p, x); resultant(p, p_prime, x, r); bool sign = (static_cast(m) * static_cast(m-1))%4 != 0; TRACE("resultant", tout << "discriminant sign: " << sign << "\n";); scoped_numeral lc(m_manager); if (const_coeff(p, x, m, lc)) { if (sign) m_manager.neg(lc); r = div(r, lc); } else { if (sign) r = neg(r); polynomial_ref c(pm()); c = coeff(p, x, m); r = exact_div(r, c); } } void subresultant_chain(polynomial const * p, polynomial const * q, var x, polynomial_ref_vector & sRes) { // REMARK: the code does not work if deg_p == deg_q unsigned deg_p = degree(p, x); unsigned deg_q = degree(q, x); if (deg_p < deg_q) { std::swap(p, q); std::swap(deg_p, deg_q); } SASSERT(deg_p > 0); unsigned n = deg_p; sRes.reset(); sRes.resize(n + 1); // the sequence is from 0 ... n sRes.set(n, const_cast(p)); sRes.set(n - 1, const_cast(q)); polynomial_ref R_j_plus_1(pm()); polynomial_ref prem(pm()); polynomial_ref newS(pm()); unsigned j = n - 1; while (j > 0) { SASSERT(degree(sRes.get(j+1), x) == j+1); // sRes_{j+1} is regular if (j == n-1) R_j_plus_1 = mk_one(); // by definition of PSC chain else R_j_plus_1 = coeff(sRes.get(j+1), x, j+1); unsigned r = degree(sRes.get(j), x); if (r == j) { // sRes_j is regular exact_pseudo_remainder(sRes.get(j+1), sRes.get(j), x, prem); TRACE("psc", tout << "j: " << j << "\nsRes_j+1: "; sRes.get(j+1)->display(tout, m_manager); tout << "\nsRes_j: "; sRes.get(j)->display(tout, m_manager); tout << "\nprem: " << prem << "\n";); // sRes_{j-1} = prem/R_j_plus_1^2 newS = exact_div(prem, R_j_plus_1); newS = exact_div(newS, R_j_plus_1); sRes.set(j-1, newS); j--; } else { SASSERT(r < j); // sRes_j is defective // sRes_{j-1} = sRes_{j-2} = ... = sRes_{r+1} = 0 for (int i = j-1; i >= static_cast(r+1); i--) sRes.set(i, mk_zero()); // sRes_r = lc(sRes_j)^{j-r} * sRes_j / R_j_plus_1^{j-r} newS = lc(sRes.get(j), x); pw(newS, j-r, newS); newS = mul(newS, sRes.get(j)); for (unsigned i = 0; i < j-r; i++) newS = exact_div(newS, R_j_plus_1); sRes.set(r, newS); // If r > 0, we also compute sRes_{r-1} if (r > 0) { exact_pseudo_remainder(sRes.get(j+1), sRes.get(j), x, prem); // sRes_{r-1} = prem/(-R_j_plus_1)^{j-r+2} newS = prem; for (unsigned i = 0; i < j-r+2; i++) newS = exact_div(newS, R_j_plus_1); if ((j-r+2)%2 == 1) newS = neg(newS); sRes.set(r-1, newS); j = r - 1; } else { j = 0; } } } } void psc_chain1(polynomial const * p, polynomial const * q, var x, polynomial_ref_vector & S) { subresultant_chain(p, q, x, S); unsigned sz = S.size(); TRACE("psc", tout << "subresultant_chain\n"; for (unsigned i = 0; i < sz; i++) { tout << "i: " << i << " "; S.get(i)->display(tout, m_manager); tout << "\n"; }); for (unsigned i = 0; i < sz - 1; i++) { S.set(i, coeff(S.get(i), x, i)); } S.set(sz-1, mk_one()); } // Store in S a list of the non-zero principal subresultant coefficients of A and B // If i < j then psc_{i}(A,B) precedes psc_{j}(A,B) in S. // The leading coefficients of A and B are not included in S. void psc_chain2(polynomial const * A, polynomial const * B, var x, polynomial_ref_vector & S) { polynomial_ref G1(pm()); polynomial_ref G2(pm()); polynomial_ref G3(pm()); polynomial_ref Gh3(pm()); polynomial_ref g1(pm()), h0(pm()), hs0(pm()), h1(pm()), hs1(pm()); unsigned n1 = degree(A, x); unsigned n2 = degree(B, x); if (n1 > n2) { G1 = const_cast(A); G2 = const_cast(B); } else { G1 = const_cast(B); G2 = const_cast(A); std::swap(n1, n2); } unsigned d0 = 0; unsigned d1 = n1 - n2; unsigned i = 1; unsigned n3; S.reset(); while (true) { // Compute Gh_{i+2} if (!is_zero(G2)) { exact_pseudo_remainder(G1, G2, x, Gh3); n3 = degree(Gh3, x); if (!is_zero(Gh3) && d1%2 == 0) Gh3 = neg(Gh3); } // Compute hi if (i > 1) { g1 = lc(G1, x); pw(g1, d0, h1); if (i > 2) { pw(h0, d0 - 1, hs0); h1 = exact_div(h1, hs0); S.push_back(h1); if (is_zero(G2)) { std::reverse(S.c_ptr(), S.c_ptr() + S.size()); return; } } } // Compute G_{i+2} if (i == 1 || is_zero(Gh3)) { G3 = Gh3; } else { pw(h1, d1, hs1); hs1 = mul(g1, hs1); G3 = exact_div(Gh3, hs1); hs1 = nullptr; } // prepare for next iteration n1 = n2; n2 = n3; d0 = d1; d1 = n1 - n2; G1 = G2; G2 = G3; if (i > 1) h0 = h1; i = i + 1; } } // Optimized calculation of S_e using "Dichotomous Lazard" void Se_Lazard(unsigned d, polynomial const * lc_S_d, polynomial const * S_d_1, var x, polynomial_ref & S_e) { unsigned n = d - degree(S_d_1, x) - 1; TRACE("Lazard", tout << "lc_S_d: "; lc_S_d->display(tout, m_manager); tout << "\nS_d_1: "; S_d_1->display(tout, m_manager); tout << "\nn: " << n << "\n";); if (n == 0) { S_e = const_cast(S_d_1); return; } polynomial_ref X(pm()); X = lc(S_d_1, x); polynomial const * Y = lc_S_d; unsigned a = 1 << log2(n); TRACE("Lazard", tout << "a: " << a << "\n";); SASSERT(a <= n); SASSERT(n < 2*a); polynomial_ref C(pm()); C = X; n = n - a; while (a != 1) { a = a / 2; // C <- C^2/Y C = mul(C, C); C = exact_div(C, Y); TRACE("Lazard", tout << "loop a: " << a << "\nC : " << C << "\n";); if (n >= a) { // C <- C*X/Y C = mul(C, X); C = exact_div(C, Y); n = n - a; TRACE("Lazard", tout << "if, C: " << C << "\n";); } } TRACE("Lazard", tout << "C: " << C << "\nY: " << Y << "\n";); S_e = mul(C, S_d_1); S_e = exact_div(S_e, Y); } // Optimized calculation of S_{e-1} for optimized psc_chain void optimized_S_e_1(unsigned d, unsigned e, polynomial const * A, polynomial const * S_d_1, polynomial const * S_e, polynomial const * s, var x, polynomial_ref & S_e_1) { SASSERT(d == degree(A, x)); SASSERT(e == degree(S_d_1, x)); polynomial_ref c_d_1(pm()), s_e(pm()), x_j(pm()), tmp(pm()); c_d_1 = lc(S_d_1, x); s_e = lc(S_e, x); polynomial_ref_buffer H(pm()); x_j = mk_one(); for (unsigned j = 0; j <= e - 1; j++) { // H_j <- s_e * x^j x_j = mk_polynomial(x, j); H.push_back(mul(s_e, x_j)); } SASSERT(H.size() == e); // H_e <- s_e * x^e - S_e x_j = mk_polynomial(x, e); x_j = mul(s_e, x_j); H.push_back(sub(x_j, S_e)); SASSERT(H.size() == e+1); polynomial_ref x_pol(pm()), xH(pm()), xHe(pm()); x_pol = mk_polynomial(x, 1); for (unsigned j = e + 1; j <= d - 1; j++) { // H_j <- x H_{j-1} - (coeff(x H_{j-1}, e) * S_{d-1})/c_{d-1} xH = mul(x_pol, H[j-1]); xHe = coeff(xH, x, e); tmp = mul(xHe, S_d_1); tmp = exact_div(tmp, c_d_1); H.push_back(sub(xH, tmp)); } SASSERT(H.size() == d); // D <- (Sum coeff(A,j) * H[j])/lc(A) polynomial_ref D(pm()); D = mk_zero(); for (unsigned j = 0; j < d; j++) { tmp = coeff(A, x, j); tmp = mul(tmp, H[j]); D = add(D, tmp); } polynomial_ref lc_A(pm()); lc_A = lc(A, x); D = exact_div(D, lc_A); // S_e_1 = (-1)^(d-e+1) [c_{d-1} (x H[j-1] + D) - coeff(x H[j-1], e)*S_d-1]/s xH = mul(x_pol, H[d-1]); xHe = coeff(xH, x, e); xHe = mul(xHe, S_d_1); S_e_1 = add(xH, D); S_e_1 = mul(c_d_1, S_e_1); S_e_1 = sub(S_e_1, xHe); S_e_1 = exact_div(S_e_1, s); if ((d-e+1) % 2 == 1) S_e_1 = neg(S_e_1); } void psc_chain_optimized_core(polynomial const * P, polynomial const * Q, var x, polynomial_ref_vector & S) { TRACE("psc_chain_classic", tout << "P: "; P->display(tout, m_manager); tout << "\nQ: "; Q->display(tout, m_manager); tout << "\n";); unsigned degP = degree(P, x); unsigned degQ = degree(Q, x); SASSERT(degP >= degQ); polynomial_ref A(pm()), B(pm()), C(pm()), minus_Q(pm()), lc_Q(pm()), ps(pm()); lc_Q = lc(Q, x); polynomial_ref s(pm()); // s <- lc(Q)^(deg(P)-deg(Q)) pw(lc_Q, degP - degQ, s); minus_Q = neg(Q); // A <- Q A = const_cast(Q); // B <- prem(P, -Q) exact_pseudo_remainder(P, minus_Q, x, B); while (true) { unsigned d = degree(A, x); unsigned e = degree(B, x); if (is_zero(B)) return; TRACE("psc_chain_classic", tout << "A: " << A << "\nB: " << B << "\ns: " << s << "\nd: " << d << ", e: " << e << "\n";); // B is S_{d-1} ps = coeff(B, x, d-1); if (!is_zero(ps)) S.push_back(ps); SASSERT(d >= e); unsigned delta = d - e; if (delta > 1) { // C <- S_e // Optimized S_e calculation // s = lc(S_d) at this point Se_Lazard(d, s, B, x, C); // C is S_e ps = coeff(C, x, e); if (!is_zero(ps)) S.push_back(ps); } else { SASSERT(delta == 0 || delta == 1); C = B; } if (e == 0) return; // B <- optimized S_e_1 optimized_S_e_1(d, e, A, B, C, s, x, B); // A <- C A = C; // s <- lc(A) s = lc(A, x); } } void psc_chain_optimized(polynomial const * P, polynomial const * Q, var x, polynomial_ref_vector & S) { SASSERT(degree(P, x) > 0); SASSERT(degree(Q, x) > 0); S.reset(); if (degree(P, x) >= degree(Q, x)) psc_chain_optimized_core(P, Q, x, S); else psc_chain_optimized_core(Q, P, x, S); if (S.empty()) S.push_back(mk_zero()); std::reverse(S.c_ptr(), S.c_ptr() + S.size()); } void psc_chain_classic_core(polynomial const * P, polynomial const * Q, var x, polynomial_ref_vector & S) { TRACE("psc_chain_classic", tout << "P: "; P->display(tout, m_manager); tout << "\nQ: "; Q->display(tout, m_manager); tout << "\n";); unsigned degP = degree(P, x); unsigned degQ = degree(Q, x); SASSERT(degP >= degQ); polynomial_ref A(pm()), B(pm()), C(pm()), minus_Q(pm()), lc_Q(pm()), lc_B(pm()), lc_A(pm()); polynomial_ref tmp1(pm()), tmp2(pm()), s_delta(pm()), minus_B(pm()), ps(pm()); lc_Q = lc(Q, x); polynomial_ref s(pm()); // s <- lc(Q)^(deg(P)-deg(Q)) pw(lc_Q, degP - degQ, s); minus_Q = neg(Q); // A <- Q A = const_cast(Q); // B <- prem(P, -Q) exact_pseudo_remainder(P, minus_Q, x, B); while (true) { unsigned d = degree(A, x); unsigned e = degree(B, x); if (is_zero(B)) return; TRACE("psc_chain_classic", tout << "A: " << A << "\nB: " << B << "\ns: " << s << "\nd: " << d << ", e: " << e << "\n";); // B is S_{d-1} ps = coeff(B, x, d-1); if (!is_zero(ps)) S.push_back(ps); unsigned delta = d - e; if (delta > 1) { // C <- S_e // Standard S_e calculation // C <- (lc(B)^(delta-1) B) / s^(delta-1) lc_B = lc(B, x); pw(lc_B, delta-1, lc_B); lc_B = mul(lc_B, B); pw(s, delta - 1, s_delta); // s_delta <- s^(delta-1) C = exact_div(lc_B, s_delta); // s_delta <- s^delta s_delta = mul(s_delta, s); // C is S_e ps = coeff(C, x, e); if (!is_zero(ps)) S.push_back(ps); } else { SASSERT(delta == 0 || delta == 1); C = B; // s_delta <- s^delta pw(s, delta, s_delta); } if (e == 0) return; // B <- prem(A, -B)/(s^delta * lc(A) lc_A = lc(A, x); minus_B = neg(B); exact_pseudo_remainder(A, minus_B, x, tmp1); tmp2 = mul(lc_A, s_delta); B = exact_div(tmp1, tmp2); // A <- C A = C; // s <- lc(A) s = lc(A, x); } } void psc_chain_classic(polynomial const * P, polynomial const * Q, var x, polynomial_ref_vector & S) { SASSERT(degree(P, x) > 0); SASSERT(degree(Q, x) > 0); S.reset(); if (degree(P, x) >= degree(Q, x)) psc_chain_classic_core(P, Q, x, S); else psc_chain_classic_core(Q, P, x, S); if (S.empty()) S.push_back(mk_zero()); std::reverse(S.c_ptr(), S.c_ptr() + S.size()); } void psc_chain(polynomial const * A, polynomial const * B, var x, polynomial_ref_vector & S) { // psc_chain1(A, B, x, S); //psc_chain2(A, B, x, S); //psc_chain_classic(A, B, x, S); psc_chain_optimized(A, B, x, S); } polynomial * normalize(polynomial const * p) { if (is_zero(p)) return const_cast(p); unsigned sz = p->size(); if (m().modular()) { unsigned i = 0; for (; i < sz; i++) { if (!m().is_p_normalized(p->a(i))) break; } if (i < sz) { m_cheap_som_buffer.reset(); scoped_numeral a(m_manager); for (unsigned i = 0; i < sz; i++) { monomial * m = p->m(i); m_manager.set(a, p->a(i)); m_cheap_som_buffer.add_reset(a, m); } m_cheap_som_buffer.normalize(); return m_cheap_som_buffer.mk(); } } scoped_numeral g(m_manager); m_manager.gcd(sz, p->as(), g); if (m_manager.is_one(g)) return const_cast(p); m_cheap_som_buffer.reset(); scoped_numeral a(m_manager); for (unsigned i = 0; i < sz; i++) { monomial * m = p->m(i); m_manager.div(p->a(i), g, a); m_cheap_som_buffer.add_reset(a, m); } return m_cheap_som_buffer.mk(); } polynomial * neg(polynomial const * p) { SASSERT(m_cheap_som_buffer.empty()); scoped_numeral minus_a(m_manager); unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { m_manager.set(minus_a, p->a(i)); m_manager.neg(minus_a); m_cheap_som_buffer.add(minus_a, p->m(i)); } return m_cheap_som_buffer.mk(); } // Return true if is a(i)*m(i) is a perfect square. // Store sqrt of a(i) in sqrt_a bool is_perfect_square(polynomial const * p, unsigned i, numeral & sqrt_a) { monomial * m = p->m(i); if (!m->is_square()) return false; numeral const & a = p->a(i); if (!m_manager.is_perfect_square(a, sqrt_a)) return false; return true; } bool sqrt(polynomial const * p, polynomial_ref & r) { SASSERT(p != 0); if (is_zero(p)) { r = const_cast(p); return true; } scoped_numeral a(m_manager); TRACE("sqrt_bug", tout << "sqrt: "; p->display(tout, m_manager); tout << "\n"; tout << "min pos: " << p->graded_lex_min_pos() << "\n"; tout << "max pos: " << p->graded_lex_max_pos() << "\n";); // Quick Check: the minimal monomial must be a perfect square unsigned min_pos = p->graded_lex_min_pos(); if (!is_perfect_square(p, min_pos, a)) return false; // Quick Check: the maximal monomial must be a perfect square unsigned max_pos = p->graded_lex_max_pos(); if (!is_perfect_square(p, max_pos, a)) return false; // Compute square root using // (m_1 + ... + m_k)^2 == // (m_1)m_1 // (2m_1 + m_2)m_2 // (2m_1 + 2m_2 + m_3)m_3 // ... // // R <- m1 // C <- p - m1*m1 // while (true) { // if (is_zero(C)) // return true; // m <- biggest monomial in C // if (m is not divisible by 2*m1) // return false; // m' <- m/2m1 // C <- C - 2*R*m' - m'*m' // R <- R + m' // } // // The loop above terminates because total degree of the // maximal monomial in C decreases in each iteration. monomial * m1 = sqrt(p->m(max_pos)); SASSERT(m1 != 0); som_buffer & R = m_som_buffer; som_buffer & C = m_som_buffer2; R.reset(); C.reset(); numeral two; m_manager.set(two, 2); scoped_numeral two_a(m_manager); m_manager.mul(a, two, two_a); // R <- a * m1 R.add(a, m1); // C <- p - m1*m1 unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { if (i == max_pos) continue; C.add(p->a(i), p->m(i)); } scoped_numeral a_i(m_manager); scoped_numeral aux(m_manager); monomial_ref m_aux(pm()); while (true) { checkpoint(); TRACE("sqrt_bug", tout << "R: "; R.display(tout); tout << "C: "; C.display(tout);); unsigned curr_max = C.graded_lex_max_pos(); if (curr_max == UINT_MAX) { // C is empty C.reset(); r = R.mk(); return true; } monomial * m = C.m(curr_max); monomial_ref m_i(pm()); if (!div(m, m1, m_i)) { // m1 does not divide maximal monomial of C. R.reset(); C.reset(); return false; } // 2*a does not divide maximal coefficient of C if (!m_manager.divides(two_a, C.a(curr_max))) return false; m_manager.div(C.a(curr_max), two_a, a_i); // C <- C - 2*R*a_i*m_i - a_i*a_i*m_i*m_i unsigned R_sz = R.size(); for (unsigned j = 0; j < R_sz; j++) { if (m_manager.is_zero(R.a(j))) continue; m_manager.mul(R.a(j), a_i, aux); m_manager.mul(aux, two, aux); m_manager.neg(aux); m_aux = mul(R.m(j), m_i); C.add(aux, m_aux); } m_manager.mul(a_i, a_i, aux); m_manager.neg(aux); m_aux = mul(m_i, m_i); C.add(aux, m_aux); // R <- R + a_i*m_i R.add(a_i, m_i); } } void rename(unsigned sz, var const * xs) { TRACE("rename", for (unsigned i = 0; i < sz; i++) tout << xs[i] << " "; tout << "\n"; tout << "polynomials before rename\n"; for (unsigned i = 0; i < m_polynomials.size(); i++) { if (m_polynomials[i] == 0) continue; m_polynomials[i]->display(tout, m_manager); tout << "\n"; }); mm().rename(sz, xs); // we must traverse the polynomial vector, and update the first monomial, // since it may not contain anymore the maximal variable with maximal degree. polynomial_vector::iterator it2 = m_polynomials.begin(); polynomial_vector::iterator end2 = m_polynomials.end(); for (; it2 != end2; ++it2) { polynomial * p = *it2; if (p == nullptr) continue; p->make_first_maximal(); SASSERT(p->size() <= 1 || !p->lex_sorted()); } TRACE("rename", tout << "polynomials after rename\n"; for (unsigned i = 0; i < m_polynomials.size(); i++) { if (m_polynomials[i] == 0) continue; m_polynomials[i]->display(tout, m_manager); tout << "\n"; }); } lbool sign(monomial* m, numeral const& c, svector const& sign_of_vars) { unsigned sz = size(m); lbool sign1 = m_manager.is_pos(c) ? l_true : l_false; for (unsigned i = 0; i < sz; ++i) { var v = get_var(m, i); unsigned d = degree(m, i); lbool sign2 = sign_of_vars.get(v, l_undef); if (sign2 == l_undef) return l_undef; else if (1 == (d % 2) && sign2 == l_false) { sign1 = sign1 == l_true ? l_false : l_true; } } return sign1; } lbool sign(polynomial const * p, svector const& sign_of_vars) { unsigned sz = size(p); if (sz == 0) return l_undef; lbool sign1 = sign(p->m(0), p->a(0), sign_of_vars); for (unsigned i = 1; sign1 != l_undef && i < sz; ++i) { if (sign(p->m(i), p->a(i), sign_of_vars) != sign1) return l_undef; } return sign1; } bool is_pos(polynomial const * p) { bool found_unit = false; unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { if (!p->m(i)->is_power_of_two()) return false; if (p->m(i) == mk_unit()) found_unit = true; if (!m_manager.is_pos(p->a(i))) return false; } return found_unit; } bool is_neg(polynomial const * p) { bool found_unit = false; unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { if (!p->m(i)->is_power_of_two()) return false; if (p->m(i) == mk_unit()) found_unit = true; if (!m_manager.is_neg(p->a(i))) return false; } return found_unit; } bool is_nonpos(polynomial const * p) { unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { if (!p->m(i)->is_power_of_two()) return false; if (!m_manager.is_neg(p->a(i))) return false; } return true; } bool is_nonneg(polynomial const * p) { unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { if (!p->m(i)->is_power_of_two()) return false; if (!m_manager.is_pos(p->a(i))) return false; } return true; } // Functor used to compute the maximal degree of each variable in a polynomial p. class var_max_degree { unsigned_vector m_max_degree; var_vector m_xs; public: void init(polynomial const * p) { unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { monomial * m = p->m(i); unsigned msz = m->size(); for (unsigned j = 0; j < msz; j++) { var x = m->get_var(j); unsigned k = m->degree(j); unsigned max_k = m_max_degree.get(x, 0); if (k > max_k) { if (max_k == 0) m_xs.push_back(x); m_max_degree.setx(x, m->degree(j), 0); } } } } void reset() { unsigned sz = m_xs.size(); for (unsigned i = 0; i < sz; i++) { m_max_degree[m_xs[i]] = 0; } m_xs.reset(); } unsigned operator()(var x) const { return m_max_degree.get(x, 0); } unsigned num_vars() const { return m_xs.size(); } var const * vars() const { return m_xs.c_ptr(); } }; struct scoped_var_max_degree { var_max_degree & m; scoped_var_max_degree(var_max_degree & _m, polynomial const * p): m(_m) { m.init(p); } ~scoped_var_max_degree() { m.reset(); } unsigned operator()(var x) const { return m(x); } unsigned num_vars() const { return m.num_vars(); } var const * vars() const { return m.vars(); } }; var_max_degree m_var_max_degree; // This method uses the tmp fields: m_found_vars, m_var_max_degree. polynomial * substitute(polynomial const * p, var2mpq const & x2v) { scoped_var_max_degree var2max_degree(m_var_max_degree, p); unsigned xs_sz = var2max_degree.num_vars(); var const * xs = var2max_degree.vars(); bool found = false; for (unsigned i = 0; i < xs_sz; i++) { var x = xs[i]; if (x2v.contains(x) && var2max_degree(x) > 0) { found = true; break; } } if (!found) return const_cast(p); scoped_numeral new_a(m_manager); scoped_numeral tmp(m_manager); m_found_vars.reserve(num_vars(), false); m_som_buffer.reset(); som_buffer & R = m_som_buffer; tmp_monomial & new_m = m_tmp1; unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { monomial * m = p->m(i); unsigned msz = m->size(); unsigned new_msz = 0; m_manager.set(new_a, p->a(i)); new_m.reserve(msz); for (unsigned j = 0; j < msz; j++) { var x = m->get_var(j); unsigned k = m->degree(j); if (x2v.contains(x)) { unsigned max_k = var2max_degree(x); m_found_vars[x] = true; mpq const & x_value = x2v(x); m_manager.power(x_value.numerator(), k, tmp); m_manager.mul(tmp, new_a, new_a); if (k < max_k) { m_manager.power(x_value.denominator(), max_k - k, tmp); m_manager.mul(tmp, new_a, new_a); } } else { new_m.set_power(new_msz, m->get_power(j)); new_msz++; } } // For each variable x in xs that does not occur in m, I // should include (x2v(x).denominator())^{var2max_degree(x)} to new_a for (unsigned j = 0; j < xs_sz; j++) { var x = xs[j]; if (m_found_vars[x]) continue; if (x2v.contains(x)) { m_manager.power(x2v(x).denominator(), var2max_degree(x), tmp); m_manager.mul(tmp, new_a, new_a); } } // Reset m_found_vars for (unsigned j = 0; j < msz; j++) { var x = m->get_var(j); m_found_vars[x] = false; } // Add new_a*new_m to R if (!m_manager.is_zero(new_a)) { new_m.set_size(new_msz); R.add(new_a, mk_monomial(new_m)); } } return R.mk(true); } struct var_pos { unsigned_vector m_pos; void init(unsigned sz, var const * xs) { for (unsigned i = 0; i < sz; i++) { SASSERT(m_pos.get(xs[i], UINT_MAX) == UINT_MAX); m_pos.setx(xs[i], i, UINT_MAX); } } void reset(unsigned sz, var const * xs) { for (unsigned i = 0; i < sz; i++) { SASSERT(m_pos.get(xs[i], UINT_MAX) != UINT_MAX); m_pos[xs[i]] = UINT_MAX; } } unsigned operator()(var x) const { return m_pos.get(x, UINT_MAX); } }; struct scoped_var_pos { var_pos & m; unsigned m_xs_sz; var const * m_xs; scoped_var_pos(var_pos & p, unsigned xs_sz, var const * xs): m(p), m_xs_sz(xs_sz), m_xs(xs) { m.init(m_xs_sz, m_xs); } ~scoped_var_pos() { m.reset(m_xs_sz, m_xs); } unsigned operator()(var x) const { return m(x); } }; var_pos m_var_pos; struct var2mpq_wrapper : public var2mpq { scoped_var_pos m_var_pos; mpq const * m_vs; var2mpq_wrapper(unsigned xs_sz, var const * xs, mpq const * vs, var_pos & buffer): m_var_pos(buffer, xs_sz, xs), m_vs(vs) { } unsynch_mpq_manager & m() const override { UNREACHABLE(); static unsynch_mpq_manager m; return m; } bool contains(var x) const override { return m_var_pos(x) != UINT_MAX; } mpq const & operator()(var x) const override { return m_vs[m_var_pos(x)]; } }; polynomial * substitute(polynomial const * p, unsigned xs_sz, var const * xs, mpq const * vs) { var2mpq_wrapper x2v(xs_sz, xs, vs, m_var_pos); return substitute(p, x2v); } polynomial * substitute(polynomial const * p, unsigned xs_sz, var const * xs, numeral const * vs) { TRACE("polynomial", tout << "substitute num_vars: " << xs_sz << "\n"; for (unsigned i = 0; i < xs_sz; i++) { tout << "x" << xs[i] << " -> " << m_manager.to_string(vs[i]) << "\n"; }); scoped_var_pos var2pos(m_var_pos, xs_sz, xs); scoped_numeral new_a(m_manager); scoped_numeral tmp(m_manager); m_som_buffer.reset(); som_buffer & R = m_som_buffer; tmp_monomial & new_m = m_tmp1; unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { monomial * m = p->m(i); unsigned msz = m->size(); unsigned new_msz = 0; m_manager.set(new_a, p->a(i)); new_m.reserve(msz); for (unsigned j = 0; j < msz; j++) { var x = m->get_var(j); unsigned k = m->degree(j); unsigned pos = var2pos(x); if (pos != UINT_MAX) { m_manager.power(vs[pos], k, tmp); m_manager.mul(tmp, new_a, new_a); } else { SASSERT(var2pos(x) == UINT_MAX); // x is not in xs new_m.set_power(new_msz, m->get_power(j)); new_msz++; } } new_m.set_size(new_msz); TRACE("polynomial", tout << "processing " << m_manager.to_string(p->a(i)) << " "; m->display(tout); tout << "\n"; tout << "new_a: " << m_manager.to_string(new_a) << " "; mk_monomial(new_m)->display(tout); tout << "\n";); R.add(new_a, mk_monomial(new_m)); } return R.mk(); } void substitute(polynomial const* r, var x, polynomial const* p, polynomial const* q, polynomial_ref& result) { unsigned md = degree(r, x); if (md == 0) { result = const_cast(r); return; } result = nullptr; polynomial_ref p1(pm()), q1(pm()); polynomial_ref_buffer ps(pm()); unsigned sz = r->size(); for (unsigned i = 0; i < sz; i++) { monomial * m0 = r->m(i); unsigned dm = m0->degree_of(x); SASSERT(md >= dm); monomial_ref m1(div_x(m0, x), pm()); pw(p, dm, p1); pw(q, md - dm, q1); p1 = mul(r->a(i), m1, p1 * q1); if (result) result = add(result, p1); else result = p1; } } /** Auxiliary method used to implement t_eval. If evaluates the sub-polynomial p composed of the monomials at positions [start, end) where all variables > x are ignored. var2pos is a mapping from variables to the positions. vs[var2pos(x)] contains the value of x. */ template void t_eval_core(polynomial * p, ValManager & vm, var2value const & x2v, unsigned start, unsigned end, var x, typename ValManager::numeral & r) { TRACE("eval_bug", tout << "p: "; p->display(tout, m()); tout << "\n"; tout << "start: " << start << ", end: " << end << ", x: " << x << "\n";); SASSERT(start < end); SASSERT(end <= p->size()); SASSERT(is_valid(x)); _scoped_numeral aux(vm); if (end == start + 1) { vm.set(r, p->a(start)); monomial * m = p->m(start); SASSERT(m->degree_of(x) > 0); unsigned sz = m->size(); for (unsigned i = 0; i < sz; i++) { var y = m->get_var(i); if (y > x) break; SASSERT(x2v.contains(y)); unsigned d = m->degree(i); vm.power(x2v(y), d, aux); vm.mul(r, aux, r); } } else { SASSERT(x2v.contains(x)); typename ValManager::numeral const & x_value = x2v(x); vm.reset(r); unsigned i = start; while (i < end) { checkpoint(); unsigned d = p->m(i)->degree_of(x); if (d == 0) { var y = p->max_smaller_than(i, end, x); if (y == null_var) { SASSERT(end == i+1); vm.add(r, p->a(i), r); } else { t_eval_core(p, vm, x2v, i, end, y, aux.get()); vm.add(r, aux, r); } break; } unsigned j = i+1; unsigned next_d = 0; for (; j < end; j++) { unsigned d_j = p->m(j)->degree_of(x); SASSERT(d_j <= d); if (d_j < d) { next_d = d_j; break; } } SASSERT(j == end || p->m(j)->degree_of(x) < d); var y = p->max_smaller_than(i, j, x); if (y == null_var) { SASSERT(j == i+1); vm.set(aux, p->a(i)); } else { t_eval_core(p, vm, x2v, i, j, y, aux.get()); } vm.add(r, aux, r); vm.power(x_value, d - next_d, aux); vm.mul(r, aux, r); i = j; } } TRACE("eval_bug", tout << "result for start: " << start << ", end: " << end << ", x: " << x << "\n"; tout << "r: "; vm.display(tout, r); tout << "\n";); } template void t_eval(polynomial * p, var2value const & x2v, typename ValManager::numeral & r) { ValManager & vm = x2v.m(); if (is_zero(p)) { vm.reset(r); return; } if (is_const(p)) { SASSERT(size(p)==1); vm.set(r, p->a(0)); return; } lex_sort(p); // lex_sort just reorders the monomials of p. That is, p still represents the same polynomial t_eval_core(p, vm, x2v, 0, p->size(), max_var(p), r); } class single_var2value : public var2value { numeral_manager & m_manager; var m_x; numeral const & m_val; public: single_var2value(numeral_manager & m, var x, numeral const & val):m_manager(m), m_x(x), m_val(val) {} numeral_manager & m() const override { return m_manager; } bool contains(var x) const override { return m_x == x; } numeral const & operator()(var x) const override { SASSERT(m_x == x); return m_val; } }; void univ_eval(polynomial const * p, var x, numeral const & val, numeral & r) { SASSERT(is_univariate(p)); if (is_zero(p)) m().set(r, 0); else if (is_const(p)) m().set(r, p->a(0)); else t_eval(const_cast(p), single_var2value(m(),x, val), r); } void eval(polynomial const * p, var2mpbqi const & x2v, mpbqi & r) { t_eval(const_cast(p), x2v, r); } void eval(polynomial const * p, var2mpq const & x2v, mpq & r) { t_eval(const_cast(p), x2v, r); } void eval(polynomial const * p, var2anum const & x2v, anum & r) { t_eval(const_cast(p), x2v, r); } // Return the variable with minimal degree in p // That is min_x s.t. forall x in p degree(p, min_x) <= degree(p, x) var get_min_degree_var(polynomial const * p) { SASSERT(!is_const(p)); scoped_var_max_degree var2max_degree(m_var_max_degree, p); unsigned num_vars = var2max_degree.num_vars(); var const * xs = var2max_degree.vars(); var min_x = null_var; unsigned deg_min = UINT_MAX; for (unsigned i = 0; i < num_vars; i++) { var x_i = xs[i]; unsigned deg_x_i = var2max_degree(x_i); if (deg_x_i < deg_min) { min_x = x_i; deg_min = deg_x_i; } } return min_x; } void acc_constant(factors & r, numeral const & c) { TRACE("factor_bug", tout << "acc_constant, c: "; m_manager.display(tout, c); tout << "\n";); scoped_numeral new_c(m_manager); m_manager.mul(r.get_constant(), c, new_c); r.set_constant(new_c); } void flip_sign(factors & r) { scoped_numeral new_c(m_manager); m_manager.set(new_c, r.get_constant()); m_manager.neg(new_c); r.set_constant(new_c); } void factor_1_sqf_pp(polynomial const * p, factors & r, var x, unsigned k) { SASSERT(degree(p, x) == 1); SASSERT(is_primitive(p, x)); SASSERT(is_square_free(p, x)); TRACE("factor", tout << "factor square free (degree == 1):\n"; p->display(tout, m_manager); tout << "\n";); // easy case r.push_back(const_cast(p), k); } void factor_2_sqf_pp(polynomial const * p, factors & r, var x, unsigned k) { SASSERT(degree(p, x) == 2); SASSERT(is_primitive(p, x)); SASSERT(is_square_free(p, x)); TRACE("factor", tout << "factor square free (degree == 2):\n"; p->display(tout, m_manager); tout << "\n";); polynomial_ref a(pm()); polynomial_ref b(pm()); polynomial_ref c(pm()); a = coeff(p, x, 2); b = coeff(p, x, 1); c = coeff(p, x, 0); TRACE("factor", tout << "a: " << a << "\nb: " << b << "\nc: " << c << "\n";); // make sure the leading monomoal of a is positive bool flipped_coeffs = false; SASSERT(!is_zero(a)); unsigned a_glex_max_pos = a->graded_lex_max_pos(); SASSERT(a_glex_max_pos != UINT_MAX); if (m_manager.is_neg(a->a(a_glex_max_pos))) { a = neg(a); b = neg(b); c = neg(c); flipped_coeffs = true; } // Create the discriminant: b^2 - 4*a*c polynomial_ref b2(pm()); b2 = mul(b, b); polynomial_ref ac(pm()); ac = mul(a, c); polynomial_ref disc(pm()); numeral m_four; m_manager.set(m_four, -4); disc = addmul(b2, m_four, mk_unit(), ac); // discriminant must be different from 0, since p is square free SASSERT(!is_zero(disc)); polynomial_ref disc_sqrt(pm()); TRACE("factor", tout << "disc: " << disc << "\n";); if (!sqrt(disc, disc_sqrt)) { // p is irreducible r.push_back(const_cast(p), k); return; } if (flipped_coeffs && k % 2 == 1) { // if k is ODD, and we flipped the coefficients, // we must also flip the sign of r. flip_sign(r); } DEBUG_CODE({ polynomial_ref tmp(pm()); tmp = mul(disc_sqrt, disc_sqrt); SASSERT(eq(disc, tmp)); }); TRACE("factor", tout << "disc_sqrt: " << disc_sqrt << "\n";); // p = cont*(2*a*x + b - disc_sqrt)*(2*a*x + b + disc_sqrt) numeral two; m_manager.set(two, 2); polynomial_ref f1(pm()); polynomial_ref f2(pm()); monomial_ref mx(pm()); mx = mk_monomial(x); polynomial_ref two_ax(pm()); two_ax = mul(two, mx, a); f1 = add(two_ax, b); f2 = f1; f1 = sub(f1, disc_sqrt); f2 = add(f2, disc_sqrt); TRACE("factor", tout << "before pp\nf1: " << f1 << "\nf2: " << f2 << "\n"; polynomial_ref cf1(pm()); m_wrapper.content(f1, x, cf1); polynomial_ref cf2(pm()); m_wrapper.content(f2, x, cf2); tout << "content(f1): " << cf1 << "\ncontent(f2): " << cf2 << "\n";); f1 = pp(f1, x); f2 = pp(f2, x); TRACE("factor", tout << "f1: " << f1 << "\nf2: " << f2 << "\n";); DEBUG_CODE({ polynomial_ref f1f2(pm()); f1f2 = mul(f1, f2); if (flipped_coeffs) f1f2 = neg(f1f2); SASSERT(eq(f1f2, p)); }); r.push_back(f1, k); r.push_back(f2, k); } void factor_sqf_pp_univ(polynomial const * p, factors & r, unsigned k, factor_params const & params) { SASSERT(is_univariate(p)); SASSERT(is_square_free(p, max_var(p))); SASSERT(is_primitive(p, max_var(p))); SASSERT(!is_zero(p)); TRACE("factor", tout << "factor square free univariate:\n"; p->display(tout, m_manager); tout << "\n";); // Convert polynomial into a upolynomial, and execute univariate factorization. var x = max_var(p); up_manager::scoped_numeral_vector p1(upm().m()); polynomial_ref p_ref(pm()); p_ref = const_cast(p); upm().to_numeral_vector(p_ref, p1); up_manager::factors fs(upm()); upolynomial::factor_square_free(upm(), p1, fs, params); SASSERT(m().is_one(fs.get_constant()) || m().is_minus_one(fs.get_constant())); if (fs.distinct_factors() == 1 && fs.get_degree(0) == 1) { // p is irreducible r.push_back(const_cast(p), k); } else { // Convert factors back into polynomial objects TRACE("factor_bug", tout << "factoring fs constant: " << m().to_string(fs.get_constant()) << "\np:\n"; p->display(tout, m()); tout << "\n";); polynomial_ref f(pm()); unsigned num_factors = fs.distinct_factors(); for (unsigned i = 0; i < num_factors; i++) { numeral_vector const & f1 = fs[i]; unsigned k1 = fs.get_degree(i); f = to_polynomial(f1.size(), f1.c_ptr(), x); TRACE("factor_bug", tout << "uni-factor:\n"; upm().display(tout, f1); tout << "\n"; tout << "factor:\n" << f << "\n";); r.push_back(f, k*k1); } TRACE("factor_bug", tout << "end-factors...\n";); SASSERT(m().is_one(fs.get_constant()) || m().is_minus_one(fs.get_constant())); if (m().is_minus_one(fs.get_constant()) && k % 2 == 1) flip_sign(r); } } void factor_n_sqf_pp(polynomial const * p, factors & r, var x, unsigned k) { SASSERT(degree(p, x) > 2); SASSERT(is_primitive(p, x)); SASSERT(is_square_free(p, x)); TRACE("factor", tout << "factor square free (degree > 2):\n"; p->display(tout, m_manager); tout << "\n";); // TODO: invoke Dejan's procedure r.push_back(const_cast(p), k); } void factor_sqf_pp(polynomial const * p, factors & r, var x, unsigned k, factor_params const & params) { SASSERT(degree(p, x) > 0); SASSERT(is_primitive(p, x)); SASSERT(is_square_free(p, x)); SASSERT(!is_zero(p)); unsigned deg_x = degree(p, x); if (deg_x == 1) factor_1_sqf_pp(p, r, x, k); else if (is_univariate(p)) factor_sqf_pp_univ(p, r, k, params); else if (deg_x == 2) factor_2_sqf_pp(p, r, x, k); else factor_n_sqf_pp(p, r, x, k); } void factor_core(polynomial const * p, factors & r, factor_params const & params) { TRACE("factor", tout << "factor_core\np: "; p->display(tout, m_manager); tout << "\n";); TRACE("factor_bug", tout << "factors r.get_constant(): " << m_manager.to_string(r.get_constant()) << "\n";); SASSERT(!is_zero(p)); if (is_const(p)) { SASSERT(!is_zero(p)); SASSERT(p->size() == 1); acc_constant(r, p->a(0)); return; } var x = get_min_degree_var(p); SASSERT(degree(p, x) > 0); scoped_numeral i(m_manager); polynomial_ref c(pm()), pp(pm()); iccp(p, x, i, c, pp); TRACE("factor", tout << "i: " << i << "\n";); acc_constant(r, i); factor_core(c, r, params); polynomial_ref C(pm()); C = pp; // Let C be a primitive polynomial of the form: P_1^1 * P_2^2 * ... * P_k^k, where each P_i is square free polynomial_ref C_prime(pm()); C_prime = derivative(C, x); polynomial_ref B(pm()), A(pm()), D(pm()); gcd(C, C_prime, B); if (is_const(B)) { // C must be of the form P_1 (square free) SASSERT(degree(C, x) > 0); factor_sqf_pp(C, r, x, 1, params); } else { // B is of the form P_2 * P_3^2 * ... * P_k^{k-1} A = exact_div(C, B); // A is of the form P_1 * P_2 * ... * P_k unsigned j = 1; while (!is_const(A)) { SASSERT(is_primitive(A, x)); SASSERT(is_square_free(A, x)); SASSERT(degree(A, x) > 0); checkpoint(); TRACE("factor", tout << "factor_core main loop j: " << j << "\nA: " << A << "\nB: " << B << "\n";); // A is of the form P_j * P_{j+1} * P_{j+2} * ... * P_k // B is of the form P_{j+1} * P_{j+2}^2 * ... * P_k^{k - j - 2} gcd(A, B, D); // D is of the form P_{j+1} * P_{j+2} * ... * P_k C = exact_div(A, D); // C is of the form P_j if (!is_const(C)) { SASSERT(degree(C, x) > 0); factor_sqf_pp(C, r, x, j, params); } else { TRACE("factor", tout << "const C: " << C << "\n";); SASSERT(C->size() == 1); SASSERT(m_manager.is_one(C->a(0)) || m_manager.is_minus_one(C->a(0))); if (m_manager.is_minus_one(C->a(0)) && j % 2 == 1) flip_sign(r); } B = exact_div(B, D); // B is of the form P_{j+2} * ... * P_k^{k - j - 3} A = D; // D is of the form P_{j+1} * P_{j+2} * ... * P_k j++; } SASSERT(eq(mk_one(), A)); } } void factor(polynomial const * p, factors & r, factor_params const & params) { if (is_zero(p)) { r.set_constant(mpz(0)); return; } factor_core(p, r, params); TRACE("factor_bug", tout << "[factor] end, r.get_constant(): " << m_manager.to_string(r.get_constant()) << "\n";); } polynomial * to_polynomial(unsigned sz, numeral const * p, var x) { if (sz == 0) return mk_zero(); _scoped_numeral_buffer coeffs(m_manager); for (unsigned i = 0; i < sz; i++) { coeffs.push_back(numeral()); m_manager.set(coeffs.back(), p[i]); } return mk_univariate(x, sz-1, coeffs.c_ptr()); } polynomial * mk_glex_monic(polynomial const * p) { SASSERT(m_manager.field()); if (is_zero(p)) return const_cast(p); unsigned pos = p->graded_lex_max_pos(); if (m_manager.is_one(p->a(pos))) return const_cast(p); scoped_numeral inv_c(m()); scoped_numeral new_a(m()); m().set(inv_c, p->a(pos)); m().inv(inv_c); m_cheap_som_buffer.reset(); cheap_som_buffer & R = m_cheap_som_buffer; unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { m().set(new_a, p->a(i)); m().mul(new_a, inv_c, new_a); R.add(new_a, p->m(i)); } return R.mk(); } som_buffer_vector m_translate_buffers; polynomial * translate(polynomial const * p, var x, numeral const & v) { unsigned deg_x = degree(p, x); if (deg_x == 0 || m().is_zero(v)) return const_cast(p); som_buffer_vector & as = m_translate_buffers; m_translate_buffers.reset(deg_x+1); coeffs(p, x, as); for (unsigned i = 1; i <= deg_x; i++) { checkpoint(); for (unsigned k = deg_x-i; k <= deg_x-1; k++) { as[k]->addmul(v, as[k+1]); } } monomial_ref xk(pm()); som_buffer & R = m_som_buffer; R.reset(); for (unsigned k = 0; k <= deg_x; k++) { xk = mk_monomial(x, k); R.addmul(xk, as[k]); } m_translate_buffers.reset(deg_x+1); return R.mk(); } void translate(polynomial const * p, unsigned xs_sz, var const * xs, numeral const * vs, polynomial_ref & r) { r = const_cast(p); if (xs_sz == 0 || is_const(p)) return; for (unsigned i = 0; i < xs_sz; i++) r = translate(r, xs[i], vs[i]); } polynomial * mod_d(polynomial const * p, var2degree const & x2d) { if (is_const(p)) return const_cast(p); cheap_som_buffer & R = m_cheap_som_buffer; R.reset(); unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { monomial * m = p->m(i); unsigned msz = m->size(); unsigned j; for (j = 0; j < msz; j++) { var x = m->get_var(j); unsigned dx = x2d.degree(x); if (dx == 0) continue; if (m->degree(j) >= dx) break; } if (j == msz) { R.add(p->a(i), p->m(i)); } } return R.mk(); } void exact_pseudo_division_mod_d(polynomial const * p, polynomial const * q, var x, var2degree const & x2d, polynomial_ref & Q, polynomial_ref & R) { unsigned d; pseudo_division_core(p, q, x, d, Q, R, &x2d); } }; manager::manager(reslimit& lim, numeral_manager & m, monomial_manager * mm) { m_imp = alloc(imp, lim, *this, m, mm); } manager::manager(reslimit& lim, numeral_manager & m, small_object_allocator * a) { m_imp = alloc(imp, lim, *this, m, a); } manager::~manager() { dealloc(m_imp); } numeral_manager & manager::m() const { return m_imp->m_manager.m(); } monomial_manager & manager::mm() const { return m_imp->mm(); } bool manager::modular() const { return m_imp->m().modular(); } numeral const & manager::p() const { return m_imp->m().p(); } void manager::set_z() { return m_imp->m().set_z(); } void manager::set_zp(numeral const & p) { return m_imp->m().set_zp(p); } void manager::set_zp(uint64_t p) { return m_imp->m().set_zp(p); } bool manager::is_var(polynomial const* p, var& v) { return p->size() == 1 && is_var(p->m(0), v) && m_imp->m().is_one(p->a(0)); } bool manager::is_var(monomial const* m, var& v) { return m->size() == 1 && m->degree(0) == 1 && (v = m->get_var(0), true); } bool manager::is_var_num(polynomial const* p, var& v, scoped_numeral& n) { return p->size() == 2 && m_imp->m().is_one(p->a(0)) && is_var(p->m(0), v) && is_unit(p->m(1)) && (n = p->a(1), true); } small_object_allocator & manager::allocator() const { return m_imp->mm().allocator(); } void manager::add_del_eh(del_eh * eh) { m_imp->add_del_eh(eh); } void manager::remove_del_eh(del_eh * eh) { m_imp->remove_del_eh(eh); } var manager::mk_var() { return m_imp->mk_var(); } unsigned manager::num_vars() const { return m_imp->num_vars(); } void manager::inc_ref(monomial * m) { if (m) m->inc_ref(); } void manager::dec_ref(monomial * m) { if (m) m_imp->dec_ref(m); } void manager::inc_ref(polynomial * p) { if (p) p->inc_ref(); } void manager::dec_ref(polynomial * p) { if (p) m_imp->dec_ref(p); } void manager::lex_sort(polynomial * p) { m_imp->lex_sort(p); } unsigned manager::hash(monomial const * m) { return m->hash(); } unsigned manager::hash(polynomial const * p) { return m_imp->hash(p); } void manager::gcd_simplify(polynomial * p) { m_imp->gcd_simplify(p); } polynomial * manager::coeff(polynomial const * p, var x, unsigned k) { return m_imp->coeff(p, x, k); } polynomial * manager::coeff(polynomial const * p, var x, unsigned k, polynomial_ref & reduct) { return m_imp->coeff(p, x, k, reduct); } bool manager::nonzero_const_coeff(polynomial const * p, var x, unsigned k) { return m_imp->nonzero_const_coeff(p, x, k); } bool manager::const_coeff(polynomial const * p, var x, unsigned k, numeral & c) { return m_imp->const_coeff(p, x, k, c); } monomial * manager::mk_unit() { return m_imp->mk_unit(); } monomial * manager::mk_monomial(var x) { return m_imp->mk_monomial(x); } monomial * manager::mk_monomial(var x, unsigned k) { return m_imp->mk_monomial(x, k); } monomial * manager::mk_monomial(unsigned sz, var * xs) { return m_imp->mk_monomial(sz, xs); } monomial * manager::convert(monomial const * src) { return m_imp->convert(src); } monomial * manager::mul(monomial const * m1, monomial const * m2) { return m_imp->mul(m1, m2); } bool manager::div(monomial const * m1, monomial const * m2) { return m_imp->div(m1, m2); } bool manager::div(monomial const * m1, monomial const * m2, monomial_ref & r) { return m_imp->div(m1, m2, r); } void manager::newton_interpolation(var x, unsigned d, numeral const * inputs, polynomial * const * outputs, polynomial_ref & r) { return m_imp->newton_interpolation(x, d, inputs, outputs, r); } monomial * manager::gcd(monomial const * m1, monomial const * m2, monomial * & q1, monomial * & q2) { return m_imp->gcd(m1, m2, q1, q2); } bool manager::unify(monomial const * m1, monomial const * m2, monomial * & q1, monomial * & q2) { return m_imp->unify(m1, m2, q1, q2); } monomial * manager::pw(monomial const * m, unsigned k) { return m_imp->pw(m, k); } polynomial * manager::mk_zero() { return m_imp->mk_zero(); } polynomial * manager::mk_const(numeral & a) { return m_imp->mk_const(a); } polynomial * manager::mk_const(rational const & a) { return m_imp->mk_const(a); } polynomial * manager::mk_polynomial(var x, unsigned k) { return m_imp->mk_polynomial(x, k); } polynomial * manager::mk_polynomial(unsigned sz, numeral * as, monomial * const * ms) { return m_imp->mk_polynomial(sz, as, ms); } polynomial * manager::mk_polynomial(unsigned sz, rational const * as, monomial * const * ms) { return m_imp->mk_polynomial(sz, as, ms); } polynomial * manager::mk_linear(unsigned sz, rational const * as, var const * xs, rational const & c) { return m_imp->mk_linear(sz, as, xs, c); } polynomial * manager::mk_linear(unsigned sz, numeral * as, var const * xs, numeral & c) { return m_imp->mk_linear(sz, as, xs, c); } polynomial * manager::mk_univariate(var x, unsigned n, numeral * as) { return m_imp->mk_univariate(x, n, as); } polynomial * manager::neg(polynomial const * p) { return m_imp->neg(p); } polynomial * manager::add(polynomial const * p1, polynomial const * p2) { return m_imp->add(p1, p2); } polynomial * manager::sub(polynomial const * p1, polynomial const * p2) { return m_imp->sub(p1, p2); } polynomial * manager::addmul(numeral const & a1, monomial const * m1, polynomial const * p1, numeral const & a2, monomial const * m2, polynomial const * p2) { return m_imp->addmul(a1, m1, p1, a2, m2, p2); } polynomial * manager::addmul(polynomial const * p1, numeral const & a2, monomial const * m2, polynomial const * p2) { return m_imp->addmul(p1, a2, m2, p2); } polynomial * manager::addmul(polynomial const * p1, numeral const & a2, polynomial const * p2) { return m_imp->addmul(p1, a2, p2); } polynomial * manager::mul(numeral const & a, polynomial const * p) { return m_imp->mul(a, p); } polynomial * manager::mul(rational const & a, polynomial const * p) { return m_imp->mul(a, p); } polynomial * manager::mul(polynomial const * p1, polynomial const * p2) { return m_imp->mul(p1, p2); } polynomial * manager::mul(numeral const & a, monomial const * m, polynomial const * p) { return m_imp->mul(a, m, p); } polynomial * manager::mul(monomial const * m, polynomial const * p) { return m_imp->mul(m, p); } void manager::pw(polynomial const * p, unsigned k, polynomial_ref & r) { m_imp->pw(p, k, r); } void manager::int_content(polynomial const * p, numeral & c) { m_imp->ic(p, c); } void manager::abs_norm(polynomial const * p, numeral & norm) { m_imp->abs_norm(p, norm); } polynomial::numeral const & manager::numeral_lc(polynomial const * p, var x) { return m_imp->numeral_lc(p, x); } polynomial::numeral const & manager::numeral_tc(polynomial const * p) { return m_imp->numeral_tc(p); } void manager::content(polynomial const * p, var x, numeral & i, polynomial_ref & c) { polynomial_ref pp(*this); m_imp->iccp(p, x, i, c, pp); } void manager::content(polynomial const * p, var x, polynomial_ref & c) { scoped_numeral i(m_imp->m_manager.m()); content(p, x, i, c); if (!m_imp->m_manager.is_one(i)) { c = mul(i, c); } } void manager::primitive(polynomial const * p, var x, polynomial_ref & pp) { pp = m_imp->pp(p, x); } void manager::icpp(polynomial const * p, var x, numeral & i, polynomial_ref & c, polynomial_ref & pp) { m_imp->iccp(p, x, i, c, pp); } polynomial * manager::flip_sign_if_lm_neg(polynomial const * p) { return m_imp->flip_sign_if_lm_neg_core(p); } void manager::gcd(polynomial const * p, polynomial const * q, polynomial_ref & g) { m_imp->gcd(p, q, g); } polynomial * manager::derivative(polynomial const * p, var x) { return m_imp->derivative(p, x); } void manager::square_free(polynomial const * p, var x, polynomial_ref & r) { m_imp->square_free(p, x, r); } bool manager::is_square_free(polynomial const * p, var x) { return m_imp->is_square_free(p, x); } void manager::square_free(polynomial const * p, polynomial_ref & r) { m_imp->square_free(p, r); } bool manager::is_square_free(polynomial const * p) { return m_imp->is_square_free(p); } bool manager::eq(polynomial const * p1, polynomial const * p2) { return m_imp->eq(p1, p2); } polynomial * manager::compose_y(polynomial const * p, var y) { return m_imp->compose_y(p, y); } polynomial * manager::compose_minus_x(polynomial const * p) { return m_imp->compose_minus_x(p); } polynomial * manager::compose_1_div_x(polynomial const * p) { return m_imp->compose_1_div_x(p); } polynomial * manager::compose_x_div_y(polynomial const * p, var y) { return m_imp->compose_x_div_y(p, y); } void manager::compose(polynomial const * p, polynomial const * q, polynomial_ref & r) { m_imp->compose(p, q, r); } void manager::compose_x_minus_y(polynomial const * p, var y, polynomial_ref & r) { m_imp->compose_x_minus_y(p, y, r); } void manager::compose_x_plus_y(polynomial const * p, var y, polynomial_ref & r) { m_imp->compose_x_plus_y(p, y, r); } void manager::compose_x_minus_c(polynomial const * p, numeral const & c, polynomial_ref & r) { m_imp->compose_x_minus_c(p, c, r); } bool manager::sqrt(polynomial const * p, polynomial_ref & r) { return m_imp->sqrt(p, r); } polynomial * manager::normalize(polynomial const * p) { return m_imp->normalize(p); } void manager::exact_pseudo_remainder(polynomial const * p, polynomial const * q, var x, polynomial_ref & R) { m_imp->exact_pseudo_remainder(p, q, x, R); } void manager::pseudo_remainder(polynomial const * p, polynomial const * q, var x, unsigned & d, polynomial_ref & R) { m_imp->pseudo_remainder(p, q, x, d, R); } void manager::exact_pseudo_division(polynomial const * p, polynomial const * q, var x, polynomial_ref & Q, polynomial_ref & R) { m_imp->exact_pseudo_division(p, q, x, Q, R); } void manager::pseudo_division(polynomial const * p, polynomial const * q, var x, unsigned & d, polynomial_ref & Q, polynomial_ref & R) { m_imp->pseudo_division(p, q, x, d, Q, R); } polynomial * manager::exact_div(polynomial const * p, polynomial const * q) { return m_imp->exact_div(p, q); } polynomial * manager::exact_div(polynomial const * p, numeral const & c) { return m_imp->exact_div(p, c); } bool manager::divides(polynomial const * q, polynomial const * p) { return m_imp->divides(q, p); } void manager::quasi_resultant(polynomial const * p, polynomial const * q, var x, polynomial_ref & r) { m_imp->quasi_resultant(p, q, x, r); } void manager::resultant(polynomial const * p, polynomial const * q, var x, polynomial_ref & r) { m_imp->resultant(p, q, x, r); } void manager::discriminant(polynomial const * p, var x, polynomial_ref & r) { m_imp->discriminant(p, x, r); } void manager::psc_chain(polynomial const * p, polynomial const * q, var x, polynomial_ref_vector & S) { m_imp->psc_chain(p, q, x, S); } lbool manager::sign(polynomial const * p, svector const& sign_of_vars) { return m_imp->sign(p, sign_of_vars); } bool manager::is_pos(polynomial const * p) { return m_imp->is_pos(p); } bool manager::is_neg(polynomial const * p) { return m_imp->is_neg(p); } bool manager::is_nonpos(polynomial const * p) { return m_imp->is_nonpos(p); } bool manager::is_nonneg(polynomial const * p) { return m_imp->is_nonneg(p); } void manager::rename(unsigned sz, var const * xs) { return m_imp->rename(sz, xs); } void manager::vars(polynomial const * p, var_vector & xs) { m_imp->vars(p, xs); } polynomial * manager::substitute(polynomial const * p, var2mpq const & x2v) { return m_imp->substitute(p, x2v); } polynomial * manager::substitute(polynomial const * p, unsigned xs_sz, var const * xs, mpq const * vs) { return m_imp->substitute(p, xs_sz, xs, vs); } polynomial * manager::substitute(polynomial const * p, unsigned xs_sz, var const * xs, numeral const * vs) { return m_imp->substitute(p, xs_sz, xs, vs); } void manager::substitute(polynomial const* r, var x, polynomial const* p, polynomial const* q, polynomial_ref& result) { m_imp->substitute(r, x, p, q, result); } void manager::factor(polynomial const * p, factors & r, factor_params const & params) { m_imp->factor(p, r, params); } polynomial * manager::to_polynomial(unsigned sz, numeral const * p, var x) { return m_imp->to_polynomial(sz, p, x); } polynomial * manager::mk_glex_monic(polynomial const * p) { return m_imp->mk_glex_monic(p); } polynomial * manager::translate(polynomial const * p, var x, numeral const & v) { return m_imp->translate(p, x, v); } void manager::translate(polynomial const * p, unsigned xs_sz, var const * xs, numeral const * vs, polynomial_ref & r) { return m_imp->translate(p, xs_sz, xs, vs, r); } polynomial * manager::mod_d(polynomial const * p, var2degree const & x2d) { return m_imp->mod_d(p, x2d); } void manager::exact_pseudo_division_mod_d(polynomial const * p, polynomial const * q, var x, var2degree const & x2d, polynomial_ref & Q, polynomial_ref & R) { m_imp->exact_pseudo_division_mod_d(p, q, x, x2d, Q, R); } void manager::eval(polynomial const * p, var2mpbqi const & x2v, mpbqi & r) { return m_imp->eval(p, x2v, r); } void manager::eval(polynomial const * p, var2mpq const & x2v, mpq & r) { return m_imp->eval(p, x2v, r); } void manager::eval(polynomial const * p, var2anum const & x2v, algebraic_numbers::anum & r) { return m_imp->eval(p, x2v, r); } void manager::display(std::ostream & out, monomial const * m, display_var_proc const & proc, bool user_star) const { m->display(out, proc, user_star); } void manager::display(std::ostream & out, polynomial const * p, display_var_proc const & proc, bool use_star) const { SASSERT(m_imp->consistent_coeffs(p)); p->display(out, m_imp->m_manager, proc, use_star); } void manager::display_smt2(std::ostream & out, polynomial const * p, display_var_proc const & proc) const { p->display_smt2(out, m_imp->m_manager, proc); } }; polynomial::polynomial * convert(polynomial::manager & sm, polynomial::polynomial * p, polynomial::manager & tm, polynomial::var x, unsigned max_d) { ptr_buffer ms; polynomial::numeral_manager & nm = tm.m(); _scoped_numeral_buffer as(nm); unsigned sz = sm.size(p); if (&sm == &tm) { // same source and target manager. // So, we just return p return p; } else if (&(sm.mm()) == &(tm.mm())) { // polynomial managers share the same monomial manager. // So, we don't need to convert monomials. for (unsigned i = 0; i < sz; i++) { polynomial::monomial * m = sm.get_monomial(p, i); if (x == polynomial::null_var || sm.degree_of(m, x) <= max_d) { ms.push_back(m); as.push_back(polynomial::numeral()); nm.set(as.back(), sm.coeff(p, i)); } } } else { for (unsigned i = 0; i < sz; i++) { polynomial::monomial * m = sm.get_monomial(p, i); if (x == polynomial::null_var || sm.degree_of(m, x) <= max_d) { ms.push_back(tm.convert(m)); as.push_back(polynomial::numeral()); nm.set(as.back(), sm.coeff(p, i)); } } } return tm.mk_polynomial(as.size(), as.c_ptr(), ms.c_ptr()); } std::ostream & operator<<(std::ostream & out, polynomial_ref_vector const & seq) { unsigned sz = seq.size(); for (unsigned i = 0; i < sz; i++) { seq.m().display(out, seq.get(i)); out << "\n"; } return out; } z3-z3-4.8.7/src/math/polynomial/polynomial.h000066400000000000000000001355721356505360400207030ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: polynomial.h Abstract: Goodies for creating and handling polynomials. Author: Leonardo (leonardo) 2011-11-15 Notes: --*/ #ifndef POLYNOMIAL_H_ #define POLYNOMIAL_H_ #include "util/mpz.h" #include "util/rational.h" #include "util/obj_ref.h" #include "util/ref_vector.h" #include "util/z3_exception.h" #include "util/scoped_numeral.h" #include "util/scoped_numeral_vector.h" #include "util/params.h" #include "util/mpbqi.h" #include "util/rlimit.h" #include "util/lbool.h" class small_object_allocator; namespace algebraic_numbers { class anum; class manager; }; namespace polynomial { typedef unsigned var; const var null_var = UINT_MAX; typedef svector var_vector; class monomial; typedef enum { sign_neg = -1, sign_zero = 0, sign_pos = 1} sign; inline sign operator-(sign s) { switch (s) { case sign_neg: return sign_pos; case sign_pos: return sign_neg; default: return sign_zero; } }; inline sign to_sign(int s) { return s == 0 ? sign_zero : (s > 0 ? sign_pos : sign_neg); } inline sign operator*(sign a, sign b) { return to_sign((int)a * (int)b); } inline bool is_zero(sign s) { return s == sign_zero; } int lex_compare(monomial const * m1, monomial const * m2); int lex_compare2(monomial const * m1, monomial const * m2, var min_var); int graded_lex_compare(monomial const * m1, monomial const * m2); int rev_lex_compare(monomial const * m1, monomial const * m2); int graded_rev_lex_compare(monomial const * m1, monomial const * m2); // It is used only for signing cancellation. class polynomial_exception : public default_exception { public: polynomial_exception(char const * msg):default_exception(msg) {} }; /** \brief A mapping from variables to degree */ class var2degree { unsigned_vector m_var2degree; public: void set_degree(var x, unsigned d) { m_var2degree.setx(x, d, 0); } unsigned degree(var x) const { return m_var2degree.get(x, 0); } void display(std::ostream & out) const; friend std::ostream & operator<<(std::ostream & out, var2degree const & ideal) { ideal.display(out); return out; } }; template class var2value { public: virtual ValManager & m() const = 0; virtual bool contains(var x) const = 0; virtual Value const & operator()(var x) const = 0; }; typedef var2value var2mpq; typedef var2value var2mpbqi; typedef var2value var2anum; class monomial_manager; /** \brief Parameters for polynomial factorization. */ struct factor_params { unsigned m_max_p; //!< factor in GF_p using primes p <= m_max_p (default UINT_MAX) unsigned m_p_trials; //!< Number of different finite factorizations: G_p1 ... G_pk, where k < m_p_trials unsigned m_max_search_size; //!< Threshold on the search space. factor_params(); factor_params(unsigned max_p, unsigned p_trials, unsigned max_search_size); void updt_params(params_ref const & p); /* REG_MODULE_PARAMS('factor', polynomial::factor_params::get_param_descrs') */ static void get_param_descrs(param_descrs & r); }; struct display_var_proc { virtual std::ostream& operator()(std::ostream & out, var x) const { return out << "x" << x; } }; class polynomial; class manager; typedef obj_ref monomial_ref; typedef obj_ref polynomial_ref; typedef ref_vector polynomial_ref_vector; typedef ptr_vector polynomial_vector; class manager { public: typedef unsynch_mpz_manager numeral_manager; typedef numeral_manager::numeral numeral; typedef svector numeral_vector; typedef _scoped_numeral scoped_numeral; typedef _scoped_numeral_vector scoped_numeral_vector; /** \brief Contains a factorization of a polynomial of the form c * (f_1)^k_1 * ... (f_n)^k_n */ class factors { vector m_factors; svector m_degrees; manager & m_manager; numeral m_constant; unsigned m_total_factors; public: factors(manager & m); ~factors(); /** \brief Number of distinct factors (not counting multiplicities). */ unsigned distinct_factors() const { return m_factors.size(); } /** \brief Number of distinct factors (counting multiplicities). */ unsigned total_factors() const { return m_total_factors; } /** \brief Returns the factor at given position. */ polynomial_ref operator[](unsigned i) const; /** \brief Returns the constant (c above). */ numeral const & get_constant() const { return m_constant; } /** \brief Sets the constant. */ void set_constant(numeral const & constant); /** \brief Returns the degree of a factor (k_i above). */ unsigned get_degree(unsigned i) const { return m_degrees[i]; } /** \brief Sets the degree of a factor. */ void set_degree(unsigned i, unsigned degree); /** \brief Adds a polynomial to the factorization. */ void push_back(polynomial * p, unsigned degree); /** \brief Returns the polynomial that this factorization represents. */ void multiply(polynomial_ref & out) const; manager & m() const { return m_manager; } manager & pm() const { return m_manager; } void display(std::ostream& out) const; void reset(); friend std::ostream & operator<<(std::ostream & out, factors const & f) { f.display(out); return out; } }; struct imp; private: imp * m_imp; public: manager(reslimit& lim, numeral_manager & m, monomial_manager * mm = nullptr); manager(reslimit& lim, numeral_manager & m, small_object_allocator * a); ~manager(); numeral_manager & m() const; monomial_manager & mm() const; small_object_allocator & allocator() const; /** \brief Return true if Z_p[X1, ..., Xn] */ bool modular() const; /** \brief Return p in Z_p[X1, ..., Xn] \pre modular */ numeral const & p() const; /** \brief Set manager as Z[X1, ..., Xn] */ void set_z(); /** \brief Set manager as Z_p[X1, ..., Xn] */ void set_zp(numeral const & p); void set_zp(uint64_t p); /** \brief Abstract event handler. */ class del_eh { friend class manager; del_eh * m_next; public: del_eh():m_next(nullptr) {} virtual void operator()(polynomial * p) = 0; }; /** \brief Install a "delete polynomial" event handler. The event handler is not owned by the polynomial manager. If eh = 0, then it uninstall the event handler. */ void add_del_eh(del_eh * eh); void remove_del_eh(del_eh * eh); /** \brief Create a new variable. */ var mk_var(); /** \brief Return the number of variables in the manager. */ unsigned num_vars() const; /** \brief Return true if x is a valid variable in this manager. */ bool is_valid(var x) const { return x < num_vars(); } /** \brief Increment reference counter. */ void inc_ref(polynomial * p); void inc_ref(monomial * m); /** \brief Decrement reference counter. */ void dec_ref(polynomial * p); void dec_ref(monomial * m); /** \brief Return an unique id associated with \c m. This id can be used to implement efficient mappings from monomial to data. */ static unsigned id(monomial const * m); /** \brief Return an unique id associated with \c m. This id can be used to implement efficient mappings from polynomial to data. */ static unsigned id(polynomial const * p); /** \brief Normalize coefficients by dividing by their gcd */ void gcd_simplify(polynomial* p); /** \brief Return true if \c m is the unit monomial. */ static bool is_unit(monomial const * m); /** \brief Return true if \c p is the zero polynomial. */ static bool is_zero(polynomial const * p); /** \brief Return true if \c p is the constant polynomial. */ static bool is_const(polynomial const * p); /** \brief Return true if \c m is univariate. */ static bool is_univariate(monomial const * m); /** \brief Return true if \c p is an univariate polynomial. */ static bool is_univariate(polynomial const * p); /** \brief Return true if m is linear (i.e., it is of the form 1 or x). */ static bool is_linear(monomial const * m); /** \brief Return true if all monomials in p are linear. */ static bool is_linear(polynomial const * p); /** \brief Return true if the monomial is a variable. */ static bool is_var(monomial const* p, var& v); /** \brief Return true if the polynomial is a variable. */ bool is_var(polynomial const* p, var& v); /** \brief Return true if the polynomial is of the form x + k */ bool is_var_num(polynomial const* p, var& v, scoped_numeral& n); /** \brief Return the degree of variable x in p. */ static unsigned degree(polynomial const * p, var x); /** \brief Return the polynomial total degree. That is, the degree of the monomial of maximal degree in p. */ static unsigned total_degree(polynomial const * p); /** \brief Return the number of monomials in p. */ static unsigned size(polynomial const * p); /** \brief Return the maximal variable occurring in p. Return null_var if p is a constant polynomial. */ static var max_var(polynomial const * p); /** \brief Return the coefficient of the i-th monomial in p. \pre i < size(p) */ static numeral const & coeff(polynomial const * p, unsigned i); /** \brief Given an univariate polynomial, return the coefficient of x_k */ static numeral const & univ_coeff(polynomial const * p, unsigned k); /** \brief Return a polynomial h that is the coefficient of x^k in p. if p does not contain any monomial containing x^k, then return 0. */ polynomial * coeff(polynomial const * p, var x, unsigned k); polynomial * coeff(polynomial const * p, var x, unsigned k, polynomial_ref & reduct); /** \brief Return true if the coefficient of x^k in p is an integer != 0. */ bool nonzero_const_coeff(polynomial const * p, var x, unsigned k); /** \brief Return true if the coefficient of x^k in p is an integer, and store it in c. */ bool const_coeff(polynomial const * p, var x, unsigned k, numeral & c); /** \brief Store in c the integer content of p. */ void int_content(polynomial const * p, numeral & c); /** \brief Returns sum of the absolute value of the coefficients. */ void abs_norm(polynomial const * p, numeral & norm); /** \brief Return the leading integer coefficient (among the loeding power of x, one is picked arbitrary). */ numeral const & numeral_lc(polynomial const * p, var x); /** \brief Return the trailing integer coefficient (i.e. the constant term). */ numeral const & numeral_tc(polynomial const * p); /** \brief Return the content i*c of p with respect to variable x. If p is a polynomial in Z[y_1, ..., y_k, x], then c is a polynomial in Z[y_1, ..., y_k] */ void content(polynomial const * p, var x, numeral & i, polynomial_ref & c); void content(polynomial const * p, var x, polynomial_ref & c); /** \brief Return the primitive polynomial of p with respect to variable x. If p is a polynomial in Z[y_1, ..., y_k, x], then pp is a polynomial in Z[y_1, ..., y_k, x] */ void primitive(polynomial const * p, var x, polynomial_ref & pp); /** \brief Return the integer content, content, and primitive polynomials of p with respect to x. i*c*pp = p If p is a polynomial in Z[y_1, ..., y_k, x], then c is a polynomial in Z[y_1, ..., y_k] pp is a polynomial in Z[y_1, ..., y_k, x] */ void icpp(polynomial const * p, var x, numeral & i, polynomial_ref & c, polynomial_ref & pp); polynomial * flip_sign_if_lm_neg(polynomial const * p); /** \brief Return the gcd g of p and q. */ void gcd(polynomial const * p, polynomial const * q, polynomial_ref & g); /** \brief Return the i-th monomial of p. */ static monomial * get_monomial(polynomial const * p, unsigned i); /** \brief Return the total degree of the given monomial. */ static unsigned total_degree(monomial const * m); /** \brief Return the size (number of variables) of the given monomial. */ static unsigned size(monomial const * m); /** \brief Convert a monomial created in a different manager. */ monomial * convert(monomial const * m); /** \brief Return the i-th variable in the given monomial. \pre i < size(m) */ static var get_var(monomial const * m, unsigned i); /** \brief Return the degree of the i-th variable in the given monomial. \pre i < size(m) */ static unsigned degree(monomial const * m, unsigned i); /** \brief Return the degree of x in the given monomial. */ static unsigned degree_of(monomial const * m, var x); /** \brief Return hash code for the given monomial. */ static unsigned hash(monomial const * m); /** \brief Return hash code for the given polynomial. */ unsigned hash(polynomial const * p); /** \brief Create the unit monomial. That is, the monomial of size zero. */ monomial * mk_unit(); /** \brief Create the zero polynomial. That is, the polynomial of size zero. */ polynomial * mk_zero(); /** \brief Create the constant polynomial \c r. \warning r is a number managed by the numeral_manager in the polynomial manager \warning r is reset. */ polynomial * mk_const(numeral & r); /** \brief Create the constant polynomial \c r. \pre r must be an integer */ polynomial * mk_const(rational const & r); /** \brief Create an univariate monomial. */ monomial * mk_monomial(var x); /** \brief Create an univariate monomial. */ monomial * mk_monomial(var x, unsigned k); /** \brief Create the monomial xs[0]*...*xs[sz-1] Remark: xs may contain duplicate variables. \warning The elements of xs will be reordered. */ monomial * mk_monomial(unsigned sz, var * xs); /** \brief Create the polynomial x^k */ polynomial * mk_polynomial(var x, unsigned k = 1); /** \brief Create the polynomial as[0]*ms[0] + ... + as[sz-1]*ms[sz - 1] \pre as's must be integers */ polynomial * mk_polynomial(unsigned sz, rational const * as, monomial * const * ms); /** \brief Create the polynomial as[0]*ms[0] + ... + as[sz-1]*ms[sz - 1] \warning as's are numbers managed by mpq_manager in the polynomial manager \warning the numerals in as are reset. */ polynomial * mk_polynomial(unsigned sz, numeral * as, monomial * const * ms); /** \brief Create the linear polynomial as[0]*xs[0] + ... + as[sz-1]*xs[sz-1] + c \pre as's must be integers */ polynomial * mk_linear(unsigned sz, rational const * as, var const * xs, rational const & c); /** \brief Create the linear polynomial as[0]*xs[0] + ... + as[sz-1]*xs[sz-1] + c \warning as's are numbers managed by mpq_manager in the polynomial manager \warning the numerals in as are reset. */ polynomial * mk_linear(unsigned sz, numeral * as, var const * xs, numeral & c); /** \brief Create an univariate polynomial of degree n as[0] + as[1]*x + as[2]*x^2 + ... + as[n]*x^n \warning \c as must contain n+1 elements. */ polynomial * mk_univariate(var x, unsigned n, numeral * as); /** \brief Return -p */ polynomial * neg(polynomial const * p); /** \brief Return p1 + p2 */ polynomial * add(polynomial const * p1, polynomial const * p2); /** \brief Return p1 - p2 */ polynomial * sub(polynomial const * p1, polynomial const * p2); /** \brief Return a1*m1*p1 + a2*m2*p2 */ polynomial * addmul(numeral const & a1, monomial const * m1, polynomial const * p1, numeral const & a2, monomial const * m2, polynomial const * p2); /** \brief Return p1 + a2*m2*p2 */ polynomial * addmul(polynomial const * p1, numeral const & a2, monomial const * m2, polynomial const * p2); /** \brief Return p1 + a2*p2 */ polynomial * addmul(polynomial const * p1, numeral const & a2, polynomial const * p2); /** \brief Return a * p */ polynomial * mul(numeral const & a, polynomial const * p); polynomial * mul(rational const & a, polynomial const * p); /** \brief Return p1 * p2 */ polynomial * mul(polynomial const * p1, polynomial const * p2); /** \brief Return m1 * m2 */ monomial * mul(monomial const * m1, monomial const * m2); /** \brief Return a * m * p */ polynomial * mul(numeral const & a, monomial const * m, polynomial const * p); /** \brief Return m * p */ polynomial * mul(monomial const * m, polynomial const * p); /** \brief Return true if m2 divides m1 */ bool div(monomial const * m1, monomial const * m2); /** \brief Return true if m2 divides m1, and store the result in r. */ bool div(monomial const * m1, monomial const * m2, monomial_ref & r); /** \brief Newton interpolation algorithm for multivariate polynomials. */ void newton_interpolation(var x, unsigned d, numeral const * inputs, polynomial * const * outputs, polynomial_ref & r); /** \brief Return the GCD of m1 and m2. Store in q1 and q2 monomials s.t. m1 = gcd * q1 m2 = gcd * q2 */ monomial * gcd(monomial const * m1, monomial const * m2, monomial * & q1, monomial * & q2); /** \brief Return true if the gcd of m1 and m2 is not 1. In that case, store in q1 and q2 monomials s.t. m1 = gcd * q1 m2 = gcd * q2 */ bool unify(monomial const * m1, monomial const * m2, monomial * & q1, monomial * & q2); /** \brief Return m^k */ monomial * pw(monomial const * m, unsigned k); /** \brief Return the polynomial p^k */ void pw(polynomial const * p, unsigned k, polynomial_ref & r); /** \brief Return dp/dx */ polynomial * derivative(polynomial const * p, var x); /** \brief r := square free part of p with respect to x */ void square_free(polynomial const * p, var x, polynomial_ref & r); /** \brief Return true if p is square free with respect to x */ bool is_square_free(polynomial const * p, var x); /** \brief r := square free part of p */ void square_free(polynomial const * p, polynomial_ref & r); /** \brief Return true if p is square free */ bool is_square_free(polynomial const * p); /** \brief Return true if p1 == p2. */ bool eq(polynomial const * p1, polynomial const * p2); /** \brief Rename variables using the given permutation. sz must be num_vars() */ void rename(unsigned sz, var const * xs); /** \brief Given an univariate polynomial p(x), return the polynomial x^n * p(1/x), where n = degree(p) If u is a nonzero root of p, then 1/u is a root the resultant polynomial. */ polynomial * compose_1_div_x(polynomial const * p); /** \brief Given an univariate polynomial p(x), return the polynomial y^n * p(x/y), where n = degree(p) */ polynomial * compose_x_div_y(polynomial const * p, var y); /** \brief Given an univariate polynomial p(x), return p(-x) */ polynomial * compose_minus_x(polynomial const * p); /** \brief Given an univariate polynomial p(x) and a polynomial q(y_1, ..., y_n), return a polynomial r(y_1, ..., y_n) = p(q(y_1, ..., y_n)). */ void compose(polynomial const * p, polynomial const * q, polynomial_ref & r); /** \brief Given an univariate polynomial p(x), return the polynomial r(y) = p(y) */ polynomial * compose_y(polynomial const * p, var y); /** \brief Given an univariate polynomial p(x), return the polynomial r(x, y) = p(x - y). The result is stored in r. */ void compose_x_minus_y(polynomial const * p, var y, polynomial_ref & r); /** \brief Given an univariate polynomial p(x), return the polynomial r(x, y) = p(x + y). The result is stored in r. */ void compose_x_plus_y(polynomial const * p, var y, polynomial_ref & r); /** \brief Given an univariate polynomial p(x), return the polynomial r(x) = p(x - c). The result is stored in r. */ void compose_x_minus_c(polynomial const * p, numeral const & c, polynomial_ref & r); /** \brief Return the exact pseudo remainder of p by q, assuming x is the maximal variable. See comments at pseudo_division_core at polynomial.cpp for a description of exact pseudo division. */ void exact_pseudo_remainder(polynomial const * p, polynomial const * q, var x, polynomial_ref & R); void exact_pseudo_remainder(polynomial const * p, polynomial const * q, polynomial_ref & R) { exact_pseudo_remainder(p, q, max_var(q), R); } /** \brief Return the pseudo remainder of p by q, assuming x is the maximal variable. See comments at pseudo_division_core at polynomial.cpp for a description of exact pseudo division. */ void pseudo_remainder(polynomial const * p, polynomial const * q, var x, unsigned & d, polynomial_ref & R); void pseudo_remainder(polynomial const * p, polynomial const * q, unsigned & d, polynomial_ref & R) { pseudo_remainder(p, q, max_var(q), d, R); } /** \brief Return the exact pseudo division quotient and remainder. See comments at pseudo_division_core at polynomial.cpp for a description of exact pseudo division. */ void exact_pseudo_division(polynomial const * p, polynomial const * q, var x, polynomial_ref & Q, polynomial_ref & R); void exact_pseudo_division(polynomial const * p, polynomial const * q, polynomial_ref & Q, polynomial_ref & R) { exact_pseudo_division(p, q, max_var(q), Q, R); } /** \brief Return the pseudo division quotient and remainder. See comments at pseudo_division_core at polynomial.cpp for a description of exact pseudo division. */ void pseudo_division(polynomial const * p, polynomial const * q, var x, unsigned & d, polynomial_ref & Q, polynomial_ref & R); void pseudo_division(polynomial const * p, polynomial const * q, unsigned & d, polynomial_ref & Q, polynomial_ref & R) { pseudo_division(p, q, max_var(q), d, Q, R); } /** \brief Return p/q if q divides p. \pre q divides p. \remark p and q may be multivariate polynomials. */ polynomial * exact_div(polynomial const * p, polynomial const * q); /** \brief Return true if q divides p. */ bool divides(polynomial const * q, polynomial const * p); /** \brief Return p/c if c divides p. \pre c divides p. \remark p may be multivariate polynomial. */ polynomial * exact_div(polynomial const * p, numeral const & c); /** \brief Store in r the quasi-resultant of p and q with respect to variable x. Assume p and q are polynomials in Q[y_1, ..., y_n, x]. Then r is a polynomial in Q[y_1, ..., y_n]. Moreover, Forall a_1, ..., a_n, b, if p(a_1, ..., a_n, b) = q(a_1, ..., a_n, b) = 0 then r(a_1, ..., a_n) = 0 \pre p and q must contain x. \remark if r is the zero polynomial, then for any complex numbers a_1, ..., a_n, the univariate polynomials p(a_1, ..., a_n, x) and q(a_1, ..., a_n, x) in C[x] have a common root. C is the field of the complex numbers. */ void quasi_resultant(polynomial const * p, polynomial const * q, var x, polynomial_ref & r); /** \brief Store in r the resultant of p and q with respect to variable x. See comments in polynomial.cpp for more details */ void resultant(polynomial const * p, polynomial const * q, var x, polynomial_ref & r); /** \brief Store in r the discriminant of p with respect to variable x. discriminant(p, x, r) == resultant(p, derivative(p, x), x, r) */ void discriminant(polynomial const * p, var x, polynomial_ref & r); /** \brief Store in S the principal subresultant coefficients for p and q. */ void psc_chain(polynomial const * p, polynomial const * q, var x, polynomial_ref_vector & S); /** \brief Make sure the GCD of the coefficients is one. Make sure that the polynomial is a member of the polynomial ring of this manager. If manager is Z_p[X1, ..., Xn], and polynomial is in Z[X1, ..., Xn] return a new one where all coefficients are in Z_p */ polynomial * normalize(polynomial const * p); /** \brief Return true if p is a square, and store its square root in r. */ bool sqrt(polynomial const * p, polynomial_ref & r); /** \brief obtain the sign of the polynomial given sign of variables. */ lbool sign(polynomial const* p, svector const& sign_of_vars); /** \brief Return true if p is always positive for any assignment of its variables. This is an incomplete check. This method just check if all monomials are powers of two, and the coefficients are positive. */ bool is_pos(polynomial const * p); /** \brief Return true if p is always negative for any assignment of its variables. This is an incomplete check. */ bool is_neg(polynomial const * p); /** \brief Return true if p is always non-positive for any assignment of its variables. This is an incomplete check. */ bool is_nonpos(polynomial const * p); /** \brief Return true if p is always non-negative for any assignment of its variables. This is an incomplete check. */ bool is_nonneg(polynomial const * p); /** \brief Make sure the monomials in p are sorted using lexicographical order. Remark: the maximal monomial is at position 0. */ void lex_sort(polynomial * p); /** \brief Collect variables that occur in p into xs */ void vars(polynomial const * p, var_vector & xs); /** \brief Evaluate polynomial p using the assignment [x_1 -> v_1, ..., x_n -> v_n]. The result is store in r. All variables occurring in p must be in xs. */ void eval(polynomial const * p, var2mpbqi const & x2v, mpbqi & r); void eval(polynomial const * p, var2mpq const & x2v, mpq & r); void eval(polynomial const * p, var2anum const & x2v, algebraic_numbers::anum & r); /** \brief Apply substitution [x_1 -> v_1, ..., x_n -> v_n]. That is, given p \in Z[x_1, ..., x_n, y_1, ..., y_m] return a polynomial r \in Z[y_1, ..., y_m]. Moreover forall a_1, ..., a_m in Q sign(r(a_1, ..., a_m)) == sign(p(v_1, ..., v_n, a_1, ..., a_m)) */ polynomial * substitute(polynomial const * p, var2mpq const & x2v); polynomial * substitute(polynomial const * p, unsigned xs_sz, var const * xs, mpq const * vs); /** \brief Apply substitution [x_1 -> v_1, ..., x_n -> v_n]. That is, given p \in Z[x_1, ..., x_n, y_1, ..., y_m] return polynomial r(y_1, ..., y_m) = p(v_1, ..., v_n, y_1, ..., y_m) in Z[y_1, ..., y_m]. */ polynomial * substitute(polynomial const * p, unsigned xs_sz, var const * xs, numeral const * vs); /** \brief Apply substitution [x -> v]. That is, given p \in Z[x, y_1, ..., y_m] return polynomial r(y_1, ..., y_m) = p(v, y_1, ..., y_m) in Z[y_1, ..., y_m]. */ polynomial * substitute(polynomial const * p, var x, numeral const & v) { return substitute(p, 1, &x, &v); } /** \brief Apply substitution [x -> p/q] in r. That is, given r \in Z[x, y_1, .., y_m] return polynomial q^k * r(p/q, y_1, .., y_m), where k is the maximal degree of x in r. */ void substitute(polynomial const* r, var x, polynomial const* p, polynomial const* q, polynomial_ref& result); /** \brief Factorize the given polynomial p and store its factors in r. */ void factor(polynomial const * p, factors & r, factor_params const & params = factor_params()); /** \brief Dense univariate polynomial to sparse polynomial. */ polynomial * to_polynomial(unsigned sz, numeral const * p, var x); polynomial * to_polynomial(numeral_vector const & p, var x) { return to_polynomial(p.size(), p.c_ptr(), x); } /** \brief Make the leading monomial (with respect to graded lexicographical order) monic. \pre numeral_manager must be a field. */ polynomial * mk_glex_monic(polynomial const * p); /** \brief Return p'(y_1, ..., y_n, x) = p(y_1, ..., y_n, x + v) */ polynomial * translate(polynomial const * p, var x, numeral const & v); /** \brief Store p'(y_1, ..., y_n, x_1, ..., x_m) = p(y_1, ..., y_n, x_1 + v_1, ..., x_m + v_m) into r. */ void translate(polynomial const * p, unsigned xs_sz, var const * xs, numeral const * vs, polynomial_ref & r); void translate(polynomial const * p, var_vector const & xs, numeral_vector const & vs, polynomial_ref & r) { SASSERT(xs.size() == vs.size()); translate(p, xs.size(), xs.c_ptr(), vs.c_ptr(), r); } /** \brief Remove monomials m if it contains x^k and x2d[x] >= k */ polynomial * mod_d(polynomial const * p, var2degree const & x2d); /** \brief (exact) Pseudo division modulo var->degree mapping. */ void exact_pseudo_division_mod_d(polynomial const * p, polynomial const * q, var x, var2degree const & x2d, polynomial_ref & Q, polynomial_ref & R); void display(std::ostream & out, monomial const * m, display_var_proc const & proc = display_var_proc(), bool use_star = true) const; void display(std::ostream & out, polynomial const * p, display_var_proc const & proc = display_var_proc(), bool use_star = false) const; void display_smt2(std::ostream & out, polynomial const * p, display_var_proc const & proc = display_var_proc()) const; friend std::ostream & operator<<(std::ostream & out, polynomial_ref const & p) { p.m().display(out, p); return out; } }; typedef manager::factors factors; typedef manager::numeral numeral; typedef manager::numeral_manager numeral_manager; typedef manager::scoped_numeral scoped_numeral; typedef manager::scoped_numeral_vector scoped_numeral_vector; class scoped_set_z { manager & m; bool m_modular; scoped_numeral m_p; public: scoped_set_z(manager & _m):m(_m), m_modular(m.modular()), m_p(m.m()) { m_p = m.p(); m.set_z(); } ~scoped_set_z() { if (m_modular) m.set_zp(m_p); } }; class scoped_set_zp { manager & m; bool m_modular; scoped_numeral m_p; public: scoped_set_zp(manager & _m, numeral const & p):m(_m), m_modular(m.modular()), m_p(m.m()) { m_p = m.p(); m.set_zp(p); } scoped_set_zp(manager & _m, uint64_t p):m(_m), m_modular(m.modular()), m_p(m.m()) { m_p = m.p(); m.set_zp(p); } ~scoped_set_zp() { if (m_modular) m.set_zp(m_p); else m.set_z(); } }; }; typedef polynomial::polynomial_ref polynomial_ref; typedef polynomial::polynomial_ref_vector polynomial_ref_vector; polynomial::polynomial * convert(polynomial::manager & sm, polynomial::polynomial * p, polynomial::manager & tm, polynomial::var x = polynomial::null_var, unsigned max_d = UINT_MAX); inline polynomial::polynomial * convert(polynomial::manager & sm, polynomial_ref const & p, polynomial::manager & tm) { SASSERT(&sm == &(p.m())); return convert(sm, p.get(), tm); } inline polynomial_ref neg(polynomial_ref const & p) { polynomial::manager & m = p.m(); return polynomial_ref(m.neg(p), m); } inline polynomial_ref operator-(polynomial_ref const & p) { polynomial::manager & m = p.m(); return polynomial_ref(m.neg(p), m); } inline polynomial_ref operator+(polynomial_ref const & p1, polynomial_ref const & p2) { polynomial::manager & m = p1.m(); return polynomial_ref(m.add(p1, p2), m); } inline polynomial_ref operator+(polynomial_ref const & p1, int n) { polynomial::manager & m = p1.m(); polynomial_ref tmp(m.mk_const(rational(n)), m); return p1 + tmp; } inline polynomial_ref operator+(polynomial_ref const & p1, rational const & n) { polynomial::manager & m = p1.m(); polynomial_ref tmp(m.mk_const(n), m); return p1 + tmp; } inline polynomial_ref operator+(polynomial::numeral const & n, polynomial_ref const & p) { polynomial::manager & m = p.m(); polynomial_ref tmp(m.mk_const(n), m); return p + tmp; } inline polynomial_ref operator+(polynomial_ref const & p, polynomial::numeral const & n) { return operator+(n, p); } inline polynomial_ref operator-(polynomial_ref const & p1, polynomial_ref const & p2) { polynomial::manager & m = p1.m(); return polynomial_ref(m.sub(p1, p2), m); } inline polynomial_ref operator-(polynomial_ref const & p1, int n) { polynomial::manager & m = p1.m(); polynomial_ref tmp(m.mk_const(rational(n)), m); return p1 - tmp; } inline polynomial_ref operator-(polynomial_ref const & p1, rational const & n) { polynomial::manager & m = p1.m(); polynomial_ref tmp(m.mk_const(n), m); return p1 - tmp; } inline polynomial_ref operator-(polynomial::numeral const & n, polynomial_ref const & p) { polynomial::manager & m = p.m(); polynomial_ref tmp(m.mk_const(n), m); return p - tmp; } inline polynomial_ref operator-(polynomial_ref const & p, polynomial::numeral const & n) { return operator-(n, p); } inline polynomial_ref operator*(polynomial_ref const & p1, polynomial_ref const & p2) { polynomial::manager & m = p1.m(); return polynomial_ref(m.mul(p1, p2), m); } inline polynomial_ref operator*(rational const & n, polynomial_ref const & p) { polynomial::manager & m = p.m(); return polynomial_ref(m.mul(n, p), m); } inline polynomial_ref operator*(int n, polynomial_ref const & p) { polynomial::manager & m = p.m(); return polynomial_ref(m.mul(rational(n), p), m); } inline polynomial_ref operator*(polynomial::numeral const & n, polynomial_ref const & p) { polynomial::manager & m = p.m(); return polynomial_ref(m.mul(n, p), m); } inline polynomial_ref operator*(polynomial_ref const & p, polynomial::numeral const & n) { return operator*(n, p); } inline bool eq(polynomial_ref const & p1, polynomial_ref const & p2) { polynomial::manager & m = p1.m(); return m.eq(p1, p2); } inline polynomial_ref operator^(polynomial_ref const & p, int k) { SASSERT(k > 0); polynomial::manager & m = p.m(); polynomial_ref r(m); m.pw(p, k, r); return polynomial_ref(r); } inline polynomial_ref operator^(polynomial_ref const & p, unsigned k) { return operator^(p, static_cast(k)); } inline polynomial_ref derivative(polynomial_ref const & p, polynomial::var x) { polynomial::manager & m = p.m(); return polynomial_ref(m.derivative(p, x), m); } inline bool is_zero(polynomial_ref const & p) { return p.m().is_zero(p); } inline bool is_const(polynomial_ref const & p) { return p.m().is_const(p); } inline bool is_linear(polynomial_ref const & p) { return p.m().is_linear(p); } inline bool is_univariate(polynomial_ref const & p) { return p.m().is_univariate(p); } inline unsigned total_degree(polynomial_ref const & p) { return p.m().total_degree(p); } inline polynomial::var max_var(polynomial_ref const & p) { return p.m().max_var(p); } inline unsigned degree(polynomial_ref const & p, polynomial::var x) { return p.m().degree(p, x); } inline unsigned size(polynomial_ref const & p) { return p.m().size(p); } inline polynomial_ref coeff(polynomial_ref const & p, polynomial::var x, unsigned k) { polynomial::manager & m = p.m(); return polynomial_ref(m.coeff(p, x, k), m); } inline polynomial_ref lc(polynomial_ref const & p, polynomial::var x) { polynomial::manager & m = p.m(); return polynomial_ref(m.coeff(p, x, degree(p, x)), m); } inline polynomial::numeral const & univ_coeff(polynomial_ref const & p, unsigned k) { return p.m().univ_coeff(p, k); } inline polynomial_ref content(polynomial_ref const & p, polynomial::var x) { polynomial::manager & m = p.m(); polynomial_ref r(m); m.content(p, x, r); return r; } inline polynomial_ref primitive(polynomial_ref const & p, polynomial::var x) { polynomial::manager & m = p.m(); polynomial_ref r(m); m.primitive(p, x, r); return r; } inline polynomial_ref gcd(polynomial_ref const & p, polynomial_ref const & q) { polynomial::manager & m = p.m(); polynomial_ref r(m); m.gcd(p, q, r); return r; } inline bool is_square_free(polynomial_ref const & p, polynomial::var x) { return p.m().is_square_free(p, x); } inline polynomial_ref square_free(polynomial_ref const & p, polynomial::var x) { polynomial::manager & m = p.m(); polynomial_ref r(m); m.square_free(p, x, r); return r; } inline bool is_square_free(polynomial_ref const & p) { return p.m().is_square_free(p); } inline polynomial_ref square_free(polynomial_ref const & p) { polynomial::manager & m = p.m(); polynomial_ref r(m); m.square_free(p, r); return r; } inline polynomial_ref compose_y(polynomial_ref const & p, polynomial::var y) { polynomial::manager & m = p.m(); return polynomial_ref(m.compose_y(p, y), m); } inline polynomial_ref compose_1_div_x(polynomial_ref const & p) { polynomial::manager & m = p.m(); return polynomial_ref(m.compose_1_div_x(p), m); } inline polynomial_ref compose_x_div_y(polynomial_ref const & p, polynomial::var y) { polynomial::manager & m = p.m(); return polynomial_ref(m.compose_x_div_y(p, y), m); } inline polynomial_ref compose_minus_x(polynomial_ref const & p) { polynomial::manager & m = p.m(); return polynomial_ref(m.compose_minus_x(p), m); } inline polynomial_ref compose(polynomial_ref const & p, polynomial_ref const & g) { polynomial::manager & m = p.m(); polynomial_ref r(m); m.compose(p, g, r); return polynomial_ref(r); } inline polynomial_ref compose_x_minus_y(polynomial_ref const & p, polynomial::var y) { polynomial::manager & m = p.m(); polynomial_ref r(m); m.compose_x_minus_y(p, y, r); return polynomial_ref(r); } inline polynomial_ref compose_x_plus_y(polynomial_ref const & p, polynomial::var y) { polynomial::manager & m = p.m(); polynomial_ref r(m); m.compose_x_plus_y(p, y, r); return polynomial_ref(r); } inline polynomial_ref compose_x_minus_c(polynomial_ref const & p, polynomial::numeral const & c) { polynomial::manager & m = p.m(); polynomial_ref r(m); m.compose_x_minus_c(p, c, r); return polynomial_ref(r); } inline polynomial_ref exact_div(polynomial_ref const & p, polynomial_ref const & q) { polynomial::manager & m = p.m(); return polynomial_ref(m.exact_div(p, q), m); } inline polynomial_ref normalize(polynomial_ref const & p) { polynomial::manager & m = p.m(); return polynomial_ref(m.normalize(p), m); } inline bool sqrt(polynomial_ref const & p, polynomial_ref & r) { return p.m().sqrt(p, r); } inline polynomial_ref exact_pseudo_remainder(polynomial_ref const & p, polynomial_ref const & q, polynomial::var x) { polynomial::manager & m = p.m(); polynomial_ref r(m); m.exact_pseudo_remainder(p, q, x, r); return polynomial_ref(r); } inline polynomial_ref exact_pseudo_remainder(polynomial_ref const & p, polynomial_ref const & q) { return exact_pseudo_remainder(p, q, max_var(q)); } inline polynomial_ref pseudo_remainder(polynomial_ref const & p, polynomial_ref const & q, polynomial::var x, unsigned & d) { polynomial::manager & m = p.m(); polynomial_ref r(m); m.pseudo_remainder(p, q, x, d, r); return polynomial_ref(r); } inline polynomial_ref pseudo_remainder(polynomial_ref const & p, polynomial_ref const & q, unsigned & d) { return pseudo_remainder(p, q, max_var(q), d); } inline polynomial_ref exact_pseudo_division(polynomial_ref const & p, polynomial_ref const & q, polynomial::var x, polynomial_ref & R) { polynomial::manager & m = p.m(); polynomial_ref Q(m); m.exact_pseudo_division(p, q, x, Q, R); return polynomial_ref(Q); } inline polynomial_ref exact_pseudo_division(polynomial_ref const & p, polynomial_ref const & q, polynomial_ref & R) { return exact_pseudo_division(p, q, max_var(q), R); } inline polynomial_ref pseudo_division(polynomial_ref const & p, polynomial_ref const & q, polynomial::var x, unsigned & d, polynomial_ref & R) { polynomial::manager & m = p.m(); polynomial_ref Q(m); m.pseudo_division(p, q, x, d, Q, R); return polynomial_ref(Q); } inline polynomial_ref pseudo_division(polynomial_ref const & p, polynomial_ref const & q, unsigned & d, polynomial_ref & R) { return pseudo_division(p, q, max_var(q), d, R); } inline polynomial_ref quasi_resultant(polynomial_ref const & p, polynomial_ref const & q, unsigned x) { polynomial::manager & m = p.m(); polynomial_ref r(m); m.quasi_resultant(p, q, x, r); return polynomial_ref(r); } inline polynomial_ref resultant(polynomial_ref const & p, polynomial_ref const & q, unsigned x) { polynomial::manager & m = p.m(); polynomial_ref r(m); m.resultant(p, q, x, r); return polynomial_ref(r); } inline polynomial_ref discriminant(polynomial_ref const & p, unsigned x) { polynomial::manager & m = p.m(); polynomial_ref r(m); m.discriminant(p, x, r); return polynomial_ref(r); } inline bool is_pos(polynomial_ref const & p) { return p.m().is_pos(p); } inline bool is_neg(polynomial_ref const & p) { return p.m().is_neg(p); } inline bool is_nonpos(polynomial_ref const & p) { return p.m().is_nonpos(p); } inline bool is_nonneg(polynomial_ref const & p) { return p.m().is_nonneg(p); } inline void factor(polynomial_ref const & p, polynomial::factors & r, polynomial::factor_params const & params = polynomial::factor_params()) { p.m().factor(p, r, params); } std::ostream & operator<<(std::ostream & out, polynomial_ref_vector const & seq); #endif z3-z3-4.8.7/src/math/polynomial/polynomial_cache.cpp000066400000000000000000000176531356505360400223600ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: polynomial_cache.cpp Abstract: "Hash-consing" for polynomials Author: Leonardo (leonardo) 2012-01-07 Notes: --*/ #include "math/polynomial/polynomial_cache.h" #include "util/chashtable.h" namespace polynomial { struct poly_hash_proc { manager & m; poly_hash_proc(manager & _m):m(_m) {} unsigned operator()(polynomial const * p) const { return m.hash(p); } }; struct poly_eq_proc { manager & m; poly_eq_proc(manager & _m):m(_m) {} bool operator()(polynomial const * p1, polynomial const * p2) const { return m.eq(p1, p2); } }; struct psc_chain_entry { polynomial const * m_p; polynomial const * m_q; var m_x; unsigned m_hash; unsigned m_result_sz; polynomial ** m_result; psc_chain_entry(polynomial const * p, polynomial const * q, var x, unsigned h): m_p(p), m_q(q), m_x(x), m_hash(h), m_result_sz(0), m_result(nullptr) { } struct hash_proc { unsigned operator()(psc_chain_entry const * entry) const { return entry->m_hash; } }; struct eq_proc { bool operator()(psc_chain_entry const * e1, psc_chain_entry const * e2) const { return e1->m_p == e2->m_p && e1->m_q == e2->m_q && e1->m_x == e2->m_x; } }; }; struct factor_entry { polynomial const * m_p; unsigned m_hash; unsigned m_result_sz; polynomial ** m_result; factor_entry(polynomial const * p, unsigned h): m_p(p), m_hash(h), m_result_sz(0), m_result(nullptr) { } struct hash_proc { unsigned operator()(factor_entry const * entry) const { return entry->m_hash; } }; struct eq_proc { bool operator()(factor_entry const * e1, factor_entry const * e2) const { return e1->m_p == e2->m_p; } }; }; typedef chashtable polynomial_table; typedef chashtable psc_chain_cache; typedef chashtable factor_cache; struct cache::imp { manager & m; polynomial_table m_poly_table; psc_chain_cache m_psc_chain_cache; factor_cache m_factor_cache; polynomial_ref_vector m_cached_polys; svector m_in_cache; small_object_allocator & m_allocator; imp(manager & _m):m(_m), m_poly_table(poly_hash_proc(m), poly_eq_proc(m)), m_cached_polys(m), m_allocator(m.allocator()) { } ~imp() { reset_psc_chain_cache(); reset_factor_cache(); } void del_psc_chain_entry(psc_chain_entry * entry) { if (entry->m_result_sz != 0) m_allocator.deallocate(sizeof(polynomial*)*entry->m_result_sz, entry->m_result); entry->~psc_chain_entry(); m_allocator.deallocate(sizeof(psc_chain_entry), entry); } void del_factor_entry(factor_entry * entry) { if (entry->m_result_sz != 0) m_allocator.deallocate(sizeof(polynomial*)*entry->m_result_sz, entry->m_result); entry->~factor_entry(); m_allocator.deallocate(sizeof(factor_entry), entry); } void reset_psc_chain_cache() { psc_chain_cache::iterator it = m_psc_chain_cache.begin(); psc_chain_cache::iterator end = m_psc_chain_cache.end(); for (; it != end; ++it) { del_psc_chain_entry(*it); } m_psc_chain_cache.reset(); } void reset_factor_cache() { factor_cache::iterator it = m_factor_cache.begin(); factor_cache::iterator end = m_factor_cache.end(); for (; it != end; ++it) { del_factor_entry(*it); } m_factor_cache.reset(); } unsigned pid(polynomial * p) const { return m.id(p); } polynomial * mk_unique(polynomial * p) { if (m_in_cache.get(pid(p), false)) return p; // m.gcd_simplify(p); polynomial * p_prime = m_poly_table.insert_if_not_there(p); if (p == p_prime) { m_cached_polys.push_back(p_prime); m_in_cache.setx(pid(p_prime), true, false); } return p_prime; } void psc_chain(polynomial * p, polynomial * q, var x, polynomial_ref_vector & S) { p = mk_unique(p); q = mk_unique(q); unsigned h = hash_u_u(pid(p), pid(q)); psc_chain_entry * entry = new (m_allocator.allocate(sizeof(psc_chain_entry))) psc_chain_entry(p, q, x, h); psc_chain_entry * old_entry = m_psc_chain_cache.insert_if_not_there(entry); if (entry != old_entry) { entry->~psc_chain_entry(); m_allocator.deallocate(sizeof(psc_chain_entry), entry); S.reset(); for (unsigned i = 0; i < old_entry->m_result_sz; i++) { S.push_back(old_entry->m_result[i]); } } else { m.psc_chain(p, q, x, S); unsigned sz = S.size(); entry->m_result_sz = sz; entry->m_result = static_cast(m_allocator.allocate(sizeof(polynomial*)*sz)); for (unsigned i = 0; i < sz; i++) { polynomial * h = mk_unique(S.get(i)); S.set(i, h); entry->m_result[i] = h; } } } void factor(polynomial * p, polynomial_ref_vector & distinct_factors) { distinct_factors.reset(); p = mk_unique(p); unsigned h = hash_u(pid(p)); factor_entry * entry = new (m_allocator.allocate(sizeof(factor_entry))) factor_entry(p, h); factor_entry * old_entry = m_factor_cache.insert_if_not_there(entry); if (entry != old_entry) { entry->~factor_entry(); m_allocator.deallocate(sizeof(factor_entry), entry); distinct_factors.reset(); for (unsigned i = 0; i < old_entry->m_result_sz; i++) { distinct_factors.push_back(old_entry->m_result[i]); } } else { factors fs(m); m.factor(p, fs); unsigned sz = fs.distinct_factors(); entry->m_result_sz = sz; entry->m_result = static_cast(m_allocator.allocate(sizeof(polynomial*)*sz)); for (unsigned i = 0; i < sz; i++) { polynomial * h = mk_unique(fs[i]); distinct_factors.push_back(h); entry->m_result[i] = h; } } } }; cache::cache(manager & m) { m_imp = alloc(imp, m); } cache::~cache() { dealloc(m_imp); } manager & cache::m() const { return m_imp->m; } polynomial * cache::mk_unique(polynomial * p) { return m_imp->mk_unique(p); } void cache::psc_chain(polynomial const * p, polynomial const * q, var x, polynomial_ref_vector & S) { m_imp->psc_chain(const_cast(p), const_cast(q), x, S); } void cache::factor(polynomial const * p, polynomial_ref_vector & distinct_factors) { m_imp->factor(const_cast(p), distinct_factors); } void cache::reset() { manager & _m = m(); dealloc(m_imp); m_imp = alloc(imp, _m); } }; z3-z3-4.8.7/src/math/polynomial/polynomial_cache.h000066400000000000000000000015611356505360400220140ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: polynomial_cache.h Abstract: "Hash-consing" for polynomials Author: Leonardo (leonardo) 2012-01-07 Notes: --*/ #ifndef POLYNOMIAL_CACHE_H_ #define POLYNOMIAL_CACHE_H_ #include "math/polynomial/polynomial.h" namespace polynomial { /** \brief Functor for creating unique polynomials and caching results of operations */ class cache { struct imp; imp * m_imp; public: cache(manager & m); ~cache(); manager & m() const; manager & pm() const { return m(); } polynomial * mk_unique(polynomial * p); void psc_chain(polynomial const * p, polynomial const * q, var x, polynomial_ref_vector & S); void factor(polynomial const * p, polynomial_ref_vector & distinct_factors); void reset(); }; }; #endif z3-z3-4.8.7/src/math/polynomial/polynomial_primes.h000066400000000000000000000063471356505360400222570ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: polynomial_primes.h Abstract: Some prime numbers for modular computations. Author: Leonardo (leonardo) 2011-12-21 Notes: --*/ #ifndef POLYNOMIAL_PRIMES_H_ #define POLYNOMIAL_PRIMES_H_ namespace polynomial { #define NUM_SMALL_PRIMES 11 const unsigned g_small_primes[NUM_SMALL_PRIMES] = { 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53 }; #if 0 #define NUM_BIG_PRIMES 77 const unsigned g_big_primes[NUM_BIG_PRIMES] = { 16777259, 16777289, 16777291, 16777331, 16777333, 16777337, 16777381, 16777421, 16777441, 16777447, 16777469, 16777499, 16777507, 16777531, 16777571, 16777577, 16777597, 16777601, 16777619, 16777633, 16777639, 16777643, 16777669, 16777679, 16777681, 16777699, 16777711, 16777721, 16777723, 16777729, 16777751, 16777777, 16777781, 16777807, 16777811, 16777823, 16777829, 16777837, 16777853, 16777903, 16777907, 16777949, 16777967, 16777973, 16777987, 16777991, 16778009, 16778023, 16778071, 16778077, 16778089, 16778123, 16778129, 16778137, 16778147, 16778173, 16778227, 16778231, 16778291, 16778309, 16778357, 16778383, 16778401, 16778413, 16778429, 16778441, 16778449, 16778453, 16778497, 16778521, 16778537, 16778543, 16778549, 16778561, 16778603, 16778623, 16778627 }; #else #define NUM_BIG_PRIMES 231 const unsigned g_big_primes[NUM_BIG_PRIMES] = { 39103, 39107, 39113, 39119, 39133, 39139, 39157, 39161, 39163, 39181, 39191, 39199, 39209, 39217, 39227, 39229, 39233, 39239, 39241, 39251, 39293, 39301, 39313, 39317, 39323, 39341, 39343, 39359, 39367, 39371, 39373, 39383, 39397, 39409, 39419, 39439, 39443, 39451, 39461, 39499, 39503, 39509, 39511, 39521, 39541, 39551, 39563, 39569, 39581, 39607, 39619, 39623, 39631, 39659, 39667, 39671, 39679, 39703, 39709, 39719, 39727, 39733, 39749, 39761, 39769, 39779, 39791, 39799, 39821, 39827, 39829, 39839, 39841, 39847, 39857, 39863, 39869, 39877, 39883, 39887, 39901, 39929, 39937, 39953, 39971, 39979, 39983, 39989, 40009, 40013, 40031, 40037, 40039, 40063, 40087, 40093, 40099, 40111, 40123, 40127, 40129, 40151, 40153, 40163, 40169, 40177, 40189, 40193, 40213, 40231, 40237, 40241, 40253, 40277, 40283, 40289, 40343, 40351, 40357, 40361, 40387, 40423, 40427, 40429, 40433, 40459, 40471, 40483, 40487, 40493, 40499, 40507, 40519, 40529, 40531, 40543, 40559, 40577, 40583, 40591, 40597, 40609, 40627, 40637, 40639, 40693, 40697, 40699, 40709, 40739, 40751, 40759, 40763, 40771, 40787, 40801, 40813, 40819, 40823, 40829, 40841, 40847, 40849, 40853, 40867, 40879, 40883, 40897, 40903, 40927, 40933, 40939, 40949, 40961, 40973, 40993, 41011, 41017, 41023, 41039, 41047, 41051, 41057, 41077, 41081, 41113, 41117, 41131, 41141, 41143, 41149, 41161, 41177, 41179, 41183, 41189, 41201, 41203, 41213, 41221, 41227, 41231, 41233, 41243, 41257, 41263, 41269, 41281, 41299, 41333, 41341, 41351, 41357, 41381, 41387, 41389, 41399, 41411, 41413, 41443, 41453, 41467, 41479, 41491, 41507, 41513, 41519, 41521, 41539, 41543, 41549 }; #endif }; #endif z3-z3-4.8.7/src/math/polynomial/polynomial_var2value.h000066400000000000000000000025131356505360400226560ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: polynomial_var2value.h Abstract: Simple implementations of the var2value interface. Author: Leonardo (leonardo) 2012-01-05 Notes: --*/ #ifndef POLYNOMIAL_VAR2VALUE_H_ #define POLYNOMIAL_VAR2VALUE_H_ #include "math/polynomial/polynomial.h" #include "util/scoped_numeral_vector.h" namespace polynomial { // default implementation used for debugging purposes template class simple_var2value : public var2value { var_vector m_xs; _scoped_numeral_vector m_vs; public: simple_var2value(ValManager & m):m_vs(m) {} void push_back(var x, typename ValManager::numeral const & v) { m_xs.push_back(x); m_vs.push_back(v); } ValManager & m() const override { return m_vs.m(); } bool contains(var x) const override { return std::find(m_xs.begin(), m_xs.end(), x) != m_xs.end(); } typename ValManager::numeral const & operator()(var x) const override { for (unsigned i = 0; i < m_xs.size(); i++) if (m_xs[i] == x) return m_vs[i]; UNREACHABLE(); static typename ValManager::numeral zero; return zero; } }; }; #endif z3-z3-4.8.7/src/math/polynomial/rpolynomial.cpp000066400000000000000000000667141356505360400214210ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: rpolynomial.cpp Abstract: Goodies for creating and handling polynomials in dense recursive representation. Author: Leonardo (leonardo) 2012-06-11 Notes: --*/ #include "math/polynomial/rpolynomial.h" #include "util/tptr.h" #include "util/buffer.h" namespace rpolynomial { typedef void poly_or_num; inline bool is_poly(poly_or_num * p) { return GET_TAG(p) == 0; } inline bool is_num(poly_or_num * p) { return GET_TAG(p) == 1; } polynomial * to_poly(poly_or_num * p) { SASSERT(is_poly(p)); return UNTAG(polynomial*, p); } manager::numeral * to_num_ptr(poly_or_num * p) { SASSERT(is_num(p)); return (UNTAG(manager::numeral *, p)); } manager::numeral & to_num(poly_or_num * p) { return *to_num_ptr(p); } poly_or_num * to_poly_or_num(polynomial * p) { return TAG(poly_or_num*, p, 0); } poly_or_num * to_poly_or_num(manager::numeral * n) { return TAG(poly_or_num*, n, 1); } class polynomial { friend class manager; friend struct manager::imp; unsigned m_ref_count; var m_var; unsigned m_size; poly_or_num * m_args[0]; public: unsigned ref_count() const { return m_ref_count; } void inc_ref() { m_ref_count++; } void dec_ref() { SASSERT(m_ref_count > 0); m_ref_count--; } static unsigned get_obj_size(unsigned n) { return sizeof(polynomial) + n*sizeof(void*); } var max_var() const { return m_var; } unsigned size() const { return m_size; } poly_or_num * arg(unsigned i) const { SASSERT(i < size()); return m_args[i]; } }; struct manager::imp { manager & m_wrapper; numeral_manager & m_manager; small_object_allocator * m_allocator; bool m_own_allocator; imp(manager & w, numeral_manager & m, small_object_allocator * a): m_wrapper(w), m_manager(m), m_allocator(a), m_own_allocator(a == nullptr) { if (a == nullptr) m_allocator = alloc(small_object_allocator, "rpolynomial"); } ~imp() { if (m_own_allocator) dealloc(m_allocator); } // Remark: recursive calls should be fine since I do not expect to have polynomials with more than 100 variables. manager & pm() const { return m_wrapper; } numeral * mk_numeral() { void * mem = m_allocator->allocate(sizeof(numeral)); return new (mem) numeral(); } void del_numeral(numeral * n) { m_manager.del(*n); m_allocator->deallocate(sizeof(numeral), n); } static void inc_ref(polynomial * p) { if (p) p->inc_ref(); } static void inc_ref(poly_or_num * p) { if (p && is_poly(p)) inc_ref(to_poly(p)); } void del_poly(polynomial * p) { SASSERT(p != 0); ptr_buffer todo; todo.push_back(p); while (!todo.empty()) { p = todo.back(); todo.pop_back(); unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { poly_or_num * pn = p->arg(i); if (pn == nullptr) continue; if (is_num(pn)) { del_numeral(to_num_ptr(pn)); } else { SASSERT(is_poly(p)); polynomial * p_arg = to_poly(p); p_arg->dec_ref(); if (p_arg->ref_count() == 0) { todo.push_back(p_arg); } } } unsigned obj_sz = polynomial::get_obj_size(sz); m_allocator->deallocate(obj_sz, p); } } void dec_ref(polynomial * p) { if (p) { p->dec_ref(); if (p->ref_count() == 0) del_poly(p); } } void dec_ref(poly_or_num * p) { if (p && is_poly(p)) dec_ref(to_poly(p)); } static bool is_const(polynomial const * p) { SASSERT(p == 0 || (p->max_var() == null_var) == (p->size() == 1 && p->arg(0) != 0 && is_num(p->arg(0)))); return p == nullptr || p->max_var() == null_var; } bool is_zero(polynomial const * p) { return p == nullptr; } static bool is_univariate(polynomial const * p) { if (is_const(p)) return false; unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { poly_or_num * pn = p->arg(i); if (pn == nullptr) continue; if (is_poly(pn)) return false; } return true; } bool is_monomial(polynomial const * p) { if (is_const(p)) return true; unsigned sz = p->size(); SASSERT(sz > 0); SASSERT(p->arg(sz - 1) != 0); for (unsigned i = 0; i < sz - 1; i++) { if (p->arg(i) != nullptr) return false; } SASSERT(is_poly(p->arg(sz - 1))); return is_monomial(to_poly(p->arg(sz-1))); } unsigned degree(polynomial const * p) { SASSERT(p != 0); SASSERT(p->size() > 0); return p == nullptr ? 0 : p->size() - 1; } bool eq(polynomial const * p1, polynomial const * p2) { if (p1 == p2) return true; if (p1 == nullptr || p2 == nullptr) return false; if (p1->size() != p2->size()) return false; if (p1->max_var() != p2->max_var()) return false; unsigned sz = p1->size(); for (unsigned i = 0; i < sz; i++) { poly_or_num * pn1 = p1->arg(i); poly_or_num * pn2 = p2->arg(i); if (pn1 == nullptr && pn2 == nullptr) continue; if (pn1 == nullptr || pn2 == nullptr) return false; if (is_num(pn1) && is_num(pn2)) { if (!m_manager.eq(to_num(pn1), to_num(pn2))) return false; } else if (is_poly(pn1) && is_poly(pn2)) { if (!eq(to_poly(pn1), to_poly(pn2))) return false; } else { return false; } } return true; } void inc_ref_args(unsigned sz, poly_or_num * const * args) { for (unsigned i = 0; i < sz; i++) { poly_or_num * pn = args[i]; if (pn == nullptr || is_num(pn)) continue; inc_ref(to_poly(pn)); } } void dec_ref_args(unsigned sz, poly_or_num * const * args) { for (unsigned i = 0; i < sz; i++) { poly_or_num * pn = args[i]; if (pn == nullptr || is_num(pn)) continue; dec_ref(to_poly(pn)); } } unsigned trim(unsigned sz, poly_or_num * const * args) { while (sz > 0) { if (args[sz - 1] != nullptr) return sz; sz--; } UNREACHABLE(); return UINT_MAX; } polynomial * allocate_poly(unsigned sz, poly_or_num * const * args, var max_var) { SASSERT(sz > 0); SASSERT((max_var == null_var) == (sz == 1 && is_num(args[0]))); unsigned obj_sz = polynomial::get_obj_size(sz); void * mem = m_allocator->allocate(obj_sz); polynomial * new_pol = new (mem) polynomial(); new_pol->m_ref_count = 0; new_pol->m_var = max_var; new_pol->m_size = sz; for (unsigned i = 0; i < sz; i++) { poly_or_num * pn = args[i]; if (is_poly(pn)) { inc_ref(to_poly(pn)); new_pol->m_args[i] = pn; SASSERT(max_var == null_var || to_poly(pn)->max_var() < max_var); } else { SASSERT(!m_manager.is_zero(to_num(pn))); new_pol->m_args[i] = pn; } } return new_pol; } poly_or_num * mk_poly_core(unsigned sz, poly_or_num * const * args, var max_var) { sz = trim(sz, args); SASSERT(sz > 0); if (sz == 1) { poly_or_num * pn0 = args[0]; SASSERT(!is_num(pn0) || !m_manager.is_zero(to_num(pn0))); return pn0; } SASSERT((max_var == null_var) == (sz == 1 && is_num(args[0]))); SASSERT(sz > 1); return to_poly_or_num(allocate_poly(sz, args, max_var)); } polynomial * mk_poly(unsigned sz, poly_or_num * const * args, var max_var) { poly_or_num * _p = mk_poly_core(sz, args, max_var); if (_p == nullptr) return nullptr; else if (is_num(_p)) return allocate_poly(1, &_p, null_var); else return to_poly(_p); } polynomial * mk_const(numeral const & n) { if (m_manager.is_zero(n)) return nullptr; numeral * a = mk_numeral(); m_manager.set(*a, n); poly_or_num * _a = to_poly_or_num(a); return allocate_poly(1, &_a, null_var); } polynomial * mk_const(rational const & a) { SASSERT(a.is_int()); scoped_numeral tmp(m_manager); m_manager.set(tmp, a.to_mpq().numerator()); return mk_const(tmp); } polynomial * mk_polynomial(var x, unsigned k) { SASSERT(x != null_var); if (k == 0) { numeral one; m_manager.set(one, 1); return mk_const(one); } ptr_buffer new_args; for (unsigned i = 0; i < k; i++) new_args.push_back(0); numeral * new_arg = mk_numeral(); m_manager.set(*new_arg, 1); new_args.push_back(to_poly_or_num(new_arg)); return mk_poly(new_args.size(), new_args.c_ptr(), x); } poly_or_num * unpack(polynomial const * p) { if (p == nullptr) { return nullptr; } else if (is_const(p)) { SASSERT(p->size() == 1); SASSERT(p->max_var() == null_var); return p->arg(0); } else { return to_poly_or_num(const_cast(p)); } } polynomial * pack(poly_or_num * p) { if (p == nullptr) return nullptr; else if (is_num(p)) return mk_poly(1, &p, null_var); else return to_poly(p); } poly_or_num * mul_core(numeral const & c, poly_or_num * p) { if (m_manager.is_zero(c) || p == nullptr) { return nullptr; } else if (is_num(p)) { numeral * r = mk_numeral(); m_manager.mul(c, to_num(p), *r); return to_poly_or_num(r); } else { polynomial * _p = to_poly(p); unsigned sz = _p->size(); SASSERT(sz > 1); ptr_buffer new_args; for (unsigned i = 0; i < sz; i++) { new_args.push_back(mul_core(c, _p->arg(i))); } return mk_poly_core(new_args.size(), new_args.c_ptr(), _p->max_var()); } } polynomial * mul(numeral const & c, polynomial const * p) { return pack(mul_core(c, unpack(p))); } polynomial * neg(polynomial const * p) { numeral minus_one; m_manager.set(minus_one, -1); return pack(mul_core(minus_one, unpack(p))); } poly_or_num * add_core(numeral const & c, poly_or_num * p) { if (m_manager.is_zero(c)) { return p; } else if (p == nullptr) { numeral * r = mk_numeral(); m_manager.set(*r, c); return to_poly_or_num(r); } else if (is_num(p)) { numeral a; m_manager.add(c, to_num(p), a); if (m_manager.is_zero(a)) return nullptr; numeral * new_arg = mk_numeral(); m_manager.swap(*new_arg, a); return to_poly_or_num(new_arg); } else { polynomial * _p = to_poly(p); unsigned sz = _p->size(); SASSERT(sz > 1); ptr_buffer new_args; new_args.push_back(add_core(c, _p->arg(0))); for (unsigned i = 1; i < sz; i++) new_args.push_back(_p->arg(1)); return mk_poly_core(new_args.size(), new_args.c_ptr(), _p->max_var()); } } polynomial * add(numeral const & c, polynomial const * p) { return pack(add_core(c, unpack(p))); } #if 0 polynomial * add_lt(polynomial const * p1, polynomial const * p2) { // Add non-constant polynomials p1 and p2 when max_var(p1) < max_var(p2) SASSERT(p1->max_var() != null_var); SASSERT(p2->max_var() != null_var); SASSERT(p1->max_var() < p2->max_var()); unsigned sz = p2->size(); ptr_buffer new_args; poly_or_num * pn0 = p2->arg(0); if (pn0 == 0) { new_args.push_back(to_poly_or_num(const_cast(p1))); } else if (is_num(pn0)) { SASSERT(!is_const(p1)); polynomial * new_arg = add(to_num(pn0), p1); SASSERT(!is_zero(new_arg)); SASSERT(!is_const(new_arg)); new_args.push_back(to_poly_or_num(new_arg)); } else { SASSERT(is_poly(pn0)); polynomial * new_arg = add(p1, to_poly(pn0)); new_args.push_back(to_poly_or_num(new_arg)); } for (unsigned i = 1; i < sz; i++) new_args.push_back(p2->arg(i)); return mk_poly(sz, new_args.c_ptr(), p2->max_var()); } polynomial * add(polynomial const * p1, polynomial const * p2) { if (is_zero(p1)) return const_cast(p2); if (is_zero(p2)) return const_cast(p1); var x1 = p1->max_var(); var x2 = p2->max_var(); if (x1 == null_var) { SASSERT(is_const(p1)); return add(to_num(p1->arg(0)), p2); } if (x2 == null_var) { SASSERT(is_const(p2)); return add(to_num(p2->arg(0)), p1); } if (x1 < x2) return add_lt(p1, p2); if (x2 < x1) return add_lt(p2, p1); SASSERT(x1 == x2); unsigned sz1 = p1->size(); unsigned sz2 = p2->size(); unsigned msz = std::min(sz1, sz2); ptr_buffer new_args; for (unsigned i = 0; i < msz; i++) { poly_or_num * pn1 = p1->arg(i); poly_or_num * pn2 = p2->arg(i); if (pn1 == 0) { new_args.push_back(pn2); continue; } if (pn2 == 0) { new_args.push_back(pn1); continue; } SASSERT(pn1 != 0 && pn2 != 0); if (is_num(pn1)) { if (is_num(pn2)) { SASSERT(is_num(pn1) && is_num(pn2)); numeral a; m_manager.add(to_num(pn1), to_num(pn2), a); if (m_manager.is_zero(a)) { new_args.push_back(0); } else { numeral * new_arg = mk_numeral(); m_manager.swap(*new_arg, a); new_args.push_back(to_poly_or_num(new_arg)); } } else { SASSERT(is_num(pn1) && is_poly(pn2)); new_args.push_back(to_poly_or_num(add(to_num(pn1), to_poly(pn2)))); } } else { if (is_num(pn2)) { SASSERT(is_poly(pn1) && is_num(pn2)); new_args.push_back(to_poly_or_num(add(to_num(pn2), to_poly(pn1)))); } else { SASSERT(is_poly(pn1) && is_poly(pn2)); new_args.push_back(to_poly_or_num(add(to_poly(pn1), to_poly(pn2)))); } } } SASSERT(new_args.size() == sz1 || new_args.size() == sz2); for (unsigned i = msz; i < sz1; i++) { new_args.push_back(p1->arg(i)); } for (unsigned i = msz; i < sz2; i++) { new_args.push_back(p2->arg(i)); } SASSERT(new_args.size() == std::max(sz1, sz2)); return mk_poly(new_args.size(), new_args.c_ptr(), x1); } class resetter_mul_buffer; friend class resetter_mul_buffer; class resetter_mul_buffer { imp & m_owner; ptr_buffer m_buffer; public: resetter_mul_buffer(imp & o, ptr_buffer & b):m_owner(o), m_buffer(b) {} ~resetter_mul_buffer() { m_owner.dec_ref_args(m_buffer.size(), m_buffer.c_ptr()); m_buffer.reset(); } }; void acc_mul_xk(ptr_buffer & mul_buffer, unsigned k, polynomial * p) { if (mul_buffer[k] == 0) { mul_buffer[k] = to_poly_or_num(p); inc_ref(p); } else { polynomial * new_p; if (is_num(mul_buffer[k])) new_p = add(to_num(mul_buffer.get(k)), p); else new_p = add(p, to_poly(mul_buffer.get(k))); if (is_zero(new_p)) { dec_ref(mul_buffer[k]); mul_buffer[k] = 0; } else { inc_ref(new_p); dec_ref(mul_buffer[k]); mul_buffer[k] = to_poly_or_num(new_p); } } } void acc_mul_xk(ptr_buffer & mul_buffer, unsigned k, numeral & a) { if (mul_buffer.get(k) == 0) { numeral * new_arg = mk_numeral(); m_manager.swap(*new_arg, a); mul_buffer[k] = to_poly_or_num(new_arg); } else { if (is_num(mul_buffer[k])) { m_manager.add(to_num(mul_buffer[k]), a, to_num(mul_buffer[k])); if (m_manager.is_zero(to_num(mul_buffer[k]))) { del_numeral(to_num_ptr(mul_buffer[k])); mul_buffer[k] = 0; } } else { polynomial * new_p = add(a, to_poly(mul_buffer.get(k))); if (is_zero(new_p)) { dec_ref(mul_buffer[k]); mul_buffer[k] = 0; } else { inc_ref(new_p); dec_ref(mul_buffer[k]); mul_buffer[k] = to_poly_or_num(new_p); } } } } polynomial * mul_lt(polynomial const * p1, polynomial const * p2) { unsigned sz2 = p2->size(); // TODO return 0; } polynomial * mul(polynomial const * p1, polynomial const * p2) { var x1 = p1->max_var(); var x2 = p2->max_var(); if (x1 == null_var) { SASSERT(is_const(p1)); return mul(to_num(p1->arg(0)), p2); } if (x2 == null_var) { SASSERT(is_const(p2)); return mul(to_num(p2->arg(0)), p1); } if (x1 < x2) return mul_lt(p1, p2); if (x2 < x1) return mul_lt(p2, p1); SASSERT(x1 == x2); if (degree(p1) < degree(p2)) std::swap(p1, p2); unsigned sz = degree(p1) * degree(p2) + 1; ptr_buffer mul_buffer; resetter_mul_buffer resetter(*this, mul_buffer); mul_buffer.resize(sz); unsigned sz1 = p1->size(); unsigned sz2 = p2->size(); for (unsigned i1 = 0; i1 < sz1; i1++) { poly_or_num * pn1 = p1->arg(i1); if (pn1 == 0) continue; for (unsigned i2 = 0; i2 < sz2; i2++) { poly_or_num * pn2 = p2->arg(i2); if (pn2 == 0) continue; unsigned i = i1+i2; if (is_num(pn1)) { if (is_num(pn2)) { SASSERT(is_num(pn1) && is_num(pn2)); scoped_numeral a(m_manager); m_manager.mul(to_num(pn1), to_num(pn2), a); acc_mul_xk(mul_buffer, i, a); } else { SASSERT(is_num(pn1) && is_poly(pn2)); polynomial_ref p(pm()); p = mul(to_num(pn1), to_poly(pn2)); acc_mul_xk(mul_buffer, i, p); } } else { if (is_num(pn2)) { SASSERT(is_poly(pn1) && is_num(pn2)); polynomial_ref p(pm()); p = mul(to_num(pn2), to_poly(pn1)); acc_mul_xk(mul_buffer, i, p); } else { SASSERT(is_poly(pn1) && is_poly(pn2)); polynomial_ref p(pm()); p = mul(to_poly(pn2), to_poly(pn1)); acc_mul_xk(mul_buffer, i, p); } } } } return mk_poly(mul_buffer.size(), mul_buffer.c_ptr(), x1); } #endif void display(std::ostream & out, polynomial const * p, display_var_proc const & proc, bool use_star) { var x = p->max_var(); bool first = true; unsigned i = p->size(); while (i > 0) { --i; poly_or_num * pn = p->arg(i); if (pn == nullptr) continue; if (first) first = false; else out << " + "; if (is_num(pn)) { numeral & a = to_num(pn); if (i == 0) { m_manager.display(out, a); } else { if (m_manager.is_one(a)) { proc(out, x); if (i > 1) out << "^" << i; } else { m_manager.display(out, a); if (use_star) out << "*"; else out << " "; proc(out, x); if (i > 1) out << "^" << i; } } } else { SASSERT(is_poly(pn)); if (i == 0) { display(out, to_poly(pn), proc, use_star); } else { bool add_paren = false; if (i > 0) add_paren = !is_monomial(to_poly(pn)); if (add_paren) out << "("; display(out, to_poly(pn), proc, use_star); if (add_paren) out << ")"; if (i > 0) { if (use_star) out << "*"; else out << " "; proc(out, x); if (i > 1) out << "^" << i; } } } } } }; manager:: manager(numeral_manager & m, small_object_allocator * a) { m_imp = alloc(imp, *this, m, a); } manager::~manager() { dealloc(m_imp); } bool manager::is_zero(polynomial const * p) { return p == nullptr; } #if 0 bool manager::is_const(polynomial const * p) { return imp::is_const(p); } bool manager::is_univariate(polynomial const * p) { return imp::is_univariate(p); } bool manager::is_monomial(polynomial const * p) const { return m_imp->is_monomial(p); } bool manager::eq(polynomial const * p1, polynomial const * p2) { return m_imp->eq(p1, p2); } polynomial * manager::mk_zero() { return m_imp->mk_zero(); } polynomial * manager::mk_const(numeral const & r) { return m_imp->mk_const(r); } polynomial * manager::mk_const(rational const & a) { return m_imp->mk_const(a); } polynomial * manager::mk_polynomial(var x, unsigned k) { return m_imp->mk_polynomial(x, k); } polynomial * manager::mul(numeral const & r, polynomial const * p) { return m_imp->mul(r, p); } polynomial * manager::neg(polynomial const * p) { return m_imp->neg(p); } polynomial * manager::add(numeral const & r, polynomial const * p) { return m_imp->add(r, p); } polynomial * manager::add(polynomial const * p1, polynomial const * p2) { return m_imp->add(p1, p2); } var manager::max_var(polynomial const * p) { return p->max_var(); } unsigned manager::size(polynomial const * p) { return p->size(); } void manager::display(std::ostream & out, polynomial const * p, display_var_proc const & proc, bool use_star) const { return m_imp->display(out, p, proc, use_star); } #endif }; z3-z3-4.8.7/src/math/polynomial/rpolynomial.h000066400000000000000000000134661356505360400210620ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: rpolynomial.h Abstract: Goodies for creating and handling polynomials in dense recursive representation. Author: Leonardo (leonardo) 2012-06-11 Notes: --*/ #ifndef RPOLYNOMIAL_H_ #define RPOLYNOMIAL_H_ #include "util/mpz.h" #include "util/rational.h" #include "util/obj_ref.h" #include "util/ref_vector.h" #include "util/z3_exception.h" #include "math/polynomial/polynomial.h" namespace rpolynomial { typedef polynomial::var var; const var null_var = polynomial::null_var; typedef polynomial::var_vector var_vector; typedef polynomial::display_var_proc display_var_proc; typedef polynomial::polynomial som_polynomial; class polynomial; class manager; typedef obj_ref polynomial_ref; typedef ref_vector polynomial_ref_vector; typedef ptr_vector polynomial_vector; class manager { public: typedef unsynch_mpz_manager numeral_manager; typedef numeral_manager::numeral numeral; typedef svector numeral_vector; typedef _scoped_numeral scoped_numeral; typedef _scoped_numeral_vector scoped_numeral_vector; struct imp; private: imp * m_imp; public: manager(numeral_manager & m, small_object_allocator * a = nullptr); ~manager(); numeral_manager & m() const; small_object_allocator & allocator() const; /** \brief Create a new variable. */ var mk_var(); /** \brief Return the number of variables in the manager. */ unsigned num_vars() const; /** \brief Return true if x is a valid variable in this manager. */ bool is_valid(var x) const { return x < num_vars(); } /** \brief Increment reference counter. */ void inc_ref(polynomial * p); /** \brief Decrement reference counter. */ void dec_ref(polynomial * p); /** \brief Return true if \c p is the zero polynomial. */ bool is_zero(polynomial const * p); /** \brief Return true if p1 == p2. */ bool eq(polynomial const * p1, polynomial const * p2); /** \brief Return true if \c p is the constant polynomial. */ static bool is_const(polynomial const * p); /** \brief Return true if \c p is an univariate polynomial. */ static bool is_univariate(polynomial const * p); /** \brief Return true if \c p is a monomial. */ bool is_monomial(polynomial const * p) const; /** \brief Return the maximal variable occurring in p. Return null_var if p is a constant polynomial. */ static var max_var(polynomial const * p); /** \brief Return the size of the polynomail p. It is the degree(p) on max_var(p) + 1. */ static unsigned size(polynomial const * p); /** \brief Return a polynomial h that is the coefficient of max_var(p)^k in p. if p does not contain any monomial containing max_var(p)^k, then return 0. */ polynomial * coeff(polynomial const * p, unsigned k); /** \brief Create the zero polynomial. */ polynomial * mk_zero(); /** \brief Create the constant polynomial \c r. \warning r is a number managed by the numeral_manager in the polynomial manager \warning r is reset. */ polynomial * mk_const(numeral const & r); /** \brief Create the constant polynomial \c r. \pre r must be an integer */ polynomial * mk_const(rational const & r); /** \brief Create the polynomial x^k */ polynomial * mk_polynomial(var x, unsigned k = 1); polynomial * mul(numeral const & r, polynomial const * p); polynomial * neg(polynomial const * p); polynomial * add(numeral const & r, polynomial const * p); polynomial * add(int c, polynomial const * p); polynomial * add(polynomial const * p1, polynomial const * p2); /** \brief Convert the given polynomial in sum-of-monomials form into a polynomial in dense recursive form. */ polynomial * translate(som_polynomial const * p); void display(std::ostream & out, polynomial const * p, display_var_proc const & proc = display_var_proc(), bool use_star = false) const; void display_smt2(std::ostream & out, polynomial const * p, display_var_proc const & proc = display_var_proc()) const; friend std::ostream & operator<<(std::ostream & out, polynomial_ref const & p) { p.m().display(out, p); return out; } }; }; typedef rpolynomial::polynomial_ref rpolynomial_ref; typedef rpolynomial::polynomial_ref_vector rpolynomial_ref_vector; inline rpolynomial_ref neg(rpolynomial_ref const & p) { rpolynomial::manager & m = p.m(); return rpolynomial_ref(m.neg(p), m); } inline rpolynomial_ref operator-(rpolynomial_ref const & p) { rpolynomial::manager & m = p.m(); return rpolynomial_ref(m.neg(p), m); } inline rpolynomial_ref operator+(int a, rpolynomial_ref const & p) { rpolynomial::manager & m = p.m(); return rpolynomial_ref(m.add(a, p), m); } inline rpolynomial_ref operator+(rpolynomial_ref const & p, int a) { rpolynomial::manager & m = p.m(); return rpolynomial_ref(m.add(a, p), m); } #endif z3-z3-4.8.7/src/math/polynomial/sexpr2upolynomial.cpp000066400000000000000000000105131356505360400225520ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sexpr2upolynomial.cpp Abstract: sexpr to upolynomial converter Author: Leonardo (leonardo) 2011-12-28 Notes: --*/ #include "math/polynomial/sexpr2upolynomial.h" #include "util/sexpr.h" sexpr2upolynomial_exception::sexpr2upolynomial_exception(char const * msg, sexpr const * s): cmd_exception(msg, s->get_line(), s->get_pos()) { } #define MAX_POLYNOMIAL_DEPTH 1 << 16 // Simple recursive parser void sexpr2upolynomial(upolynomial::manager & m, sexpr const * s, upolynomial::numeral_vector & p, unsigned depth) { if (depth > MAX_POLYNOMIAL_DEPTH) throw sexpr2upolynomial_exception("invalid univariate polynomial, too complex", s); if (s->is_composite()) { unsigned num = s->get_num_children(); if (num == 0) throw sexpr2upolynomial_exception("invalid univariate polynomial, symbol expected", s); sexpr * h = s->get_child(0); if (!h->is_symbol()) throw sexpr2upolynomial_exception("invalid univariate polynomial, symbol expected", s); symbol op = h->get_symbol(); if (op == "+") { if (num <= 1) throw sexpr2upolynomial_exception("invalid univariate polynomial, '+' operator expects at least one argument", s); sexpr2upolynomial(m, s->get_child(1), p, depth+1); upolynomial::scoped_numeral_vector arg(m); for (unsigned i = 2; i < num; i++) { m.reset(arg); sexpr2upolynomial(m, s->get_child(i), arg, depth+1); m.add(arg.size(), arg.c_ptr(), p.size(), p.c_ptr(), p); } } else if (op == "-") { if (num <= 1) throw sexpr2upolynomial_exception("invalid univariate polynomial, '-' operator expects at least one argument", s); sexpr2upolynomial(m, s->get_child(1), p, depth+1); if (num == 2) { m.neg(p); return; } upolynomial::scoped_numeral_vector arg(m); for (unsigned i = 2; i < num; i++) { m.reset(arg); sexpr2upolynomial(m, s->get_child(i), arg, depth+1); m.sub(p.size(), p.c_ptr(), arg.size(), arg.c_ptr(), p); } } else if (op == "*") { if (num <= 1) throw sexpr2upolynomial_exception("invalid univariate polynomial, '*' operator expects at least one argument", s); sexpr2upolynomial(m, s->get_child(1), p, depth+1); upolynomial::scoped_numeral_vector arg(m); for (unsigned i = 2; i < num; i++) { m.reset(arg); sexpr2upolynomial(m, s->get_child(i), arg, depth+1); m.mul(arg.size(), arg.c_ptr(), p.size(), p.c_ptr(), p); } } else if (op == "^") { if (num != 3) throw sexpr2upolynomial_exception("invalid univariate polynomial, '^' operator expects two arguments", s); sexpr2upolynomial(m, s->get_child(1), p, depth+1); sexpr * arg2 = s->get_child(2); if (!arg2->is_numeral() || !arg2->get_numeral().is_unsigned()) throw sexpr2upolynomial_exception("invalid univariate polynomial, exponent must be an unsigned integer", arg2); unsigned k = arg2->get_numeral().get_unsigned(); m.pw(p.size(), p.c_ptr(), k, p); } else { throw sexpr2upolynomial_exception("invalid univariate polynomial, '+', '-', '^' or '*' expected", s); } } else if (s->is_numeral()) { // make constant polynomial rational a = s->get_numeral(); if (!a.is_int()) throw sexpr2upolynomial_exception("invalid univariate polynomial, integer coefficient expected", s); m.set(1, &a, p); } else if (s->is_symbol()) { if (s->get_symbol() != "x") throw sexpr2upolynomial_exception("invalid univariate polynomial, variable 'x' expected", s); // make identity rational as[2] = { rational(0), rational(1) }; m.set(2, as, p); } else { throw sexpr2upolynomial_exception("invalid univariate polynomial, unexpected ", s); } } void sexpr2upolynomial(upolynomial::manager & m, sexpr const * s, upolynomial::numeral_vector & p) { sexpr2upolynomial(m, s, p, 0); } z3-z3-4.8.7/src/math/polynomial/sexpr2upolynomial.h000066400000000000000000000011201356505360400222110ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sexpr2upolynomial.h Abstract: sexpr to upolynomial converter Author: Leonardo (leonardo) 2011-12-28 Notes: --*/ #ifndef SEXPR2UPOLYNOMIAL_H_ #define SEXPR2UPOLYNOMIAL_H_ #include "math/polynomial/upolynomial.h" #include "util/cmd_context_types.h" class sexpr; class sexpr2upolynomial_exception : public cmd_exception { public: sexpr2upolynomial_exception(char const * msg, sexpr const * s); }; void sexpr2upolynomial(upolynomial::manager & m, sexpr const * s, upolynomial::numeral_vector & p); #endif z3-z3-4.8.7/src/math/polynomial/upolynomial.cpp000066400000000000000000003446201356505360400214170ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: upolynomial.cpp Abstract: Goodies for creating and handling univariate polynomials. A dense representation is much better for Root isolation algorithms, encoding algebraic numbers, factorization, etc. We also use integers as the coefficients of univariate polynomials. Author: Leonardo (leonardo) 2011-11-29 Notes: --*/ #include "math/polynomial/upolynomial.h" #include "math/polynomial/upolynomial_factorization.h" #include "math/polynomial/polynomial_primes.h" #include "util/buffer.h" #include "util/common_msgs.h" namespace upolynomial { core_manager::factors::factors(core_manager & upm): m_upm(upm), m_total_factors(0), m_total_degree(0) { nm().set(m_constant, 1); } core_manager::factors::~factors() { clear(); nm().del(m_constant); } void core_manager::factors::clear() { for (unsigned i = 0; i < m_factors.size(); ++ i) { m_upm.reset(m_factors[i]); } m_factors.reset(); m_degrees.reset(); nm().set(m_constant, 1); m_total_factors = 0; m_total_degree = 0; } void core_manager::factors::push_back(numeral_vector const & p, unsigned degree) { SASSERT(degree > 0); m_factors.push_back(numeral_vector()); m_degrees.push_back(degree); m_upm.set(p.size(), p.c_ptr(), m_factors.back()); m_total_factors += degree; m_total_degree += m_upm.degree(p)*degree; } void core_manager::factors::push_back_swap(numeral_vector & p, unsigned degree) { SASSERT(degree > 0); m_factors.push_back(numeral_vector()); m_degrees.push_back(degree); p.swap(m_factors.back()); m_total_factors += degree; m_total_degree += m_upm.degree(p)*degree; } void core_manager::factors::multiply(numeral_vector & out) const { // set the first one to be just the constant m_upm.reset(out); if (nm().is_zero(m_constant)) { SASSERT(m_factors.empty()); return; } out.push_back(numeral()); nm().set(out.back(), m_constant); // now multiply them all in for (unsigned i = 0; i < m_factors.size(); ++ i) { if (m_degrees[i] > 1) { numeral_vector power; m_upm.pw(m_factors[i].size(), m_factors[i].c_ptr(), m_degrees[i], power); m_upm.mul(out.size(), out.c_ptr(), power.size(), power.c_ptr(), out); m_upm.reset(power); } else { m_upm.mul(out.size(), out.c_ptr(), m_factors[i].size(), m_factors[i].c_ptr(), out); } } } void core_manager::factors::display(std::ostream & out) const { out << nm().to_string(m_constant); if (!m_factors.empty()) { for (unsigned i = 0; i < m_factors.size(); ++ i) { out << " * ("; m_upm.display(out, m_factors[i]); out << ")^" << m_degrees[i]; } } } numeral_manager & core_manager::factors::nm() const { return m_upm.m(); } void core_manager::factors::set_constant(numeral const & constant) { nm().set(m_constant, constant); } void core_manager::factors::set_degree(unsigned i, unsigned degree) { m_total_degree -= m_upm.degree(m_factors[i])*m_degrees[i]; m_total_factors -= m_degrees[i]; m_degrees[i] = degree; m_total_factors += degree; m_total_degree += m_upm.degree(m_factors[i])*degree; } void core_manager::factors::swap_factor(unsigned i, numeral_vector & p) { m_total_degree -= m_upm.degree(m_factors[i])*m_degrees[i]; m_total_degree += m_upm.degree(p)*m_degrees[i]; m_factors[i].swap(p); } void core_manager::factors::swap(factors & other) { m_factors.swap(other.m_factors); m_degrees.swap(other.m_degrees); nm().swap(m_constant, other.m_constant); std::swap(m_total_factors, other.m_total_factors); std::swap(m_total_degree, other.m_total_degree); } core_manager::core_manager(reslimit& lim, unsynch_mpz_manager & m): m_limit(lim), m_manager(m) { } core_manager::~core_manager() { reset(m_basic_tmp); reset(m_div_tmp1); reset(m_div_tmp2); reset(m_exact_div_tmp); reset(m_gcd_tmp1); reset(m_gcd_tmp2); reset(m_CRA_tmp); for (unsigned i = 0; i < UPOLYNOMIAL_MGCD_TMPS; i++) reset(m_mgcd_tmp[i]); reset(m_sqf_tmp1); reset(m_sqf_tmp2); reset(m_pw_tmp); } void core_manager::checkpoint() { if (!m_limit.inc()) throw upolynomial_exception(Z3_CANCELED_MSG); } // Eliminate leading zeros from buffer. void core_manager::trim(numeral_vector & buffer) { unsigned sz = buffer.size(); while (sz > 0 && m().is_zero(buffer[sz - 1])) { m().del(buffer[sz - 1]); // 0 may be a big number when using GMP. sz--; } buffer.shrink(sz); } // Remove old entries from buffer [sz, buffer.size()), shrink size, and remove leading zeros. // buffer must have at least size sz. void core_manager::set_size(unsigned sz, numeral_vector & buffer) { unsigned old_sz = buffer.size(); SASSERT(old_sz >= sz); // delete old entries for (unsigned i = sz; i < old_sz; i++) { m().del(buffer[i]); } buffer.shrink(sz); trim(buffer); } // Set size to zero. void core_manager::reset(numeral_vector & buffer) { set_size(0, buffer); } // Copy elements from p to buffer. void core_manager::set(unsigned sz, numeral const * p, numeral_vector & buffer) { if (p != nullptr && buffer.c_ptr() == p) { SASSERT(buffer.size() == sz); return; } buffer.reserve(sz); for (unsigned i = 0; i < sz; i++) { m().set(buffer[i], p[i]); } set_size(sz, buffer); } void core_manager::set(unsigned sz, rational const * p, numeral_vector & buffer) { buffer.reserve(sz); for (unsigned i = 0; i < sz; i++) { SASSERT(p[i].is_int()); m().set(buffer[i], p[i].to_mpq().numerator()); } set_size(sz, buffer); } void core_manager::get_primitive_and_content(unsigned f_sz, numeral const * f, numeral_vector & pp, numeral & cont) { SASSERT(f_sz > 0); m().gcd(f_sz, f, cont); SASSERT(m().is_pos(cont)); if (m().is_one(cont)) { set(f_sz, f, pp); } else { pp.reserve(f_sz); for (unsigned i = 0; i < f_sz; i++) { if (!m().is_zero(f[i])) { m().div(f[i], cont, pp[i]); } else { m().set(pp[i], 0); } } set_size(f_sz, pp); } } // Negate coefficients of p. void core_manager::neg(unsigned sz, numeral * p) { for (unsigned i = 0; i < sz; i++) { m().neg(p[i]); } } // buffer := -p void core_manager::neg_core(unsigned sz, numeral const * p, numeral_vector & buffer) { SASSERT(!is_alias(p, buffer)); buffer.reserve(sz); for (unsigned i = 0; i < sz; i++) { m().set(buffer[i], p[i]); m().neg(buffer[i]); } set_size(sz, buffer); } void core_manager::neg(unsigned sz, numeral const * p, numeral_vector & buffer) { neg_core(sz, p, m_basic_tmp); buffer.swap(m_basic_tmp); } // buffer := p1 + p2 void core_manager::add_core(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & buffer) { SASSERT(!is_alias(p1, buffer)); SASSERT(!is_alias(p2, buffer)); unsigned min_sz = std::min(sz1, sz2); unsigned max_sz = std::max(sz1, sz2); unsigned i = 0; buffer.reserve(max_sz); for (; i < min_sz; i++) { m().add(p1[i], p2[i], buffer[i]); } for (; i < sz1; i++) { m().set(buffer[i], p1[i]); } for (; i < sz2; i++) { m().set(buffer[i], p2[i]); } set_size(max_sz, buffer); } void core_manager::add(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & buffer) { add_core(sz1, p1, sz2, p2, m_basic_tmp); buffer.swap(m_basic_tmp); } // buffer := p1 - p2 void core_manager::sub_core(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & buffer) { SASSERT(!is_alias(p1, buffer)); SASSERT(!is_alias(p2, buffer)); unsigned min_sz = std::min(sz1, sz2); unsigned max_sz = std::max(sz1, sz2); unsigned i = 0; buffer.reserve(max_sz); for (; i < min_sz; i++) { m().sub(p1[i], p2[i], buffer[i]); } for (; i < sz1; i++) { m().set(buffer[i], p1[i]); } for (; i < sz2; i++) { m().set(buffer[i], p2[i]); m().neg(buffer[i]); } set_size(max_sz, buffer); } void core_manager::sub(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & buffer) { sub_core(sz1, p1, sz2, p2, m_basic_tmp); buffer.swap(m_basic_tmp); } // buffer := p1 * p2 void core_manager::mul_core(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & buffer) { SASSERT(!is_alias(p1, buffer)); SASSERT(!is_alias(p2, buffer)); if (sz1 == 0) { reset(buffer); } else if (sz2 == 0) { reset(buffer); } else { unsigned new_sz = sz1 + sz2 - 1; buffer.reserve(new_sz); for (unsigned i = 0; i < new_sz; i++) { m().reset(buffer[i]); } if (sz1 < sz2) { std::swap(sz1, sz2); std::swap(p1, p2); } for (unsigned i = 0; i < sz1; i++) { checkpoint(); numeral const & a_i = p1[i]; if (m().is_zero(a_i)) continue; for (unsigned j = 0; j < sz2; j++) { numeral const & b_j = p2[j]; if (m().is_zero(b_j)) continue; m().addmul(buffer[i+j], a_i, b_j, buffer[i+j]); } } set_size(new_sz, buffer); } } void core_manager::mul(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & buffer) { mul_core(sz1, p1, sz2, p2, m_basic_tmp); buffer.swap(m_basic_tmp); } // buffer := dp/dx void core_manager::derivative(unsigned sz, numeral const * p, numeral_vector & buffer) { if (sz <= 1) { reset(buffer); return; } buffer.reserve(sz - 1); for (unsigned i = 1; i < sz; i++) { numeral d; m().set(d, i); m().mul(p[i], d, buffer[i-1]); } set_size(sz-1, buffer); } // Divide coefficients of p by their GCD void core_manager::normalize(unsigned sz, numeral * p) { if (sz == 0) return; if (sz == 1) { if (m().is_pos(p[0])) m().set(p[0], 1); else m().set(p[0], -1); return; } scoped_numeral g(m()); m().gcd(sz, p, g); if (m().is_one(g)) return; for (unsigned i = 0; i < sz; i++) { #ifdef Z3DEBUG scoped_numeral old_p_i(m()); old_p_i = p[i]; #endif // Actual code m().div(p[i], g, p[i]); #ifdef Z3DEBUG scoped_numeral tmp(m()); m().mul(g, p[i], tmp); CTRACE("div_bug", !m().eq(tmp, old_p_i), tout << "old(p[i]): " << m().to_string(old_p_i) << ", g: " << m().to_string(g) << ", p[i]: " << m().to_string(p[i]) << ", tmp: " << m().to_string(tmp) << "\n";); SASSERT(tmp == old_p_i); #endif } } // Divide coefficients of p by their GCD void core_manager::normalize(numeral_vector & p) { normalize(p.size(), p.c_ptr()); } void core_manager::div(unsigned sz, numeral * p, numeral const & b) { SASSERT(!m().is_zero(b)); if (m().is_one(b)) return; for (unsigned i = 0; i < sz; i++) { CTRACE("upolynomial", !m().divides(b, p[i]), tout << "b: " << m().to_string(b) << ", p[i]: " << m().to_string(p[i]) << "\n";); SASSERT(m().divides(b, p[i])); m().div(p[i], b, p[i]); } } void core_manager::mul(unsigned sz, numeral * p, numeral const & b) { SASSERT(!m().is_zero(b)); if (m().is_one(b)) return; for (unsigned i = 0; i < sz; i++) { m().mul(p[i], b, p[i]); } } void core_manager::mul(numeral_vector & p, numeral const & b) { if (m().is_zero(b)) { reset(p); return; } mul(p.size(), p.c_ptr(), b); } // Pseudo division void core_manager::div_rem_core(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, unsigned & d, numeral_vector & q, numeral_vector & r) { SASSERT(!is_alias(p1, q)); SASSERT(!is_alias(p2, q)); SASSERT(!is_alias(p1, r)); SASSERT(!is_alias(p2, r)); d = 0; SASSERT(sz2 > 0); if (sz2 == 1) { set(sz1, p1, q); if (field()) { div(q, *p2); } reset(r); return; } reset(q); set(sz1, p1, r); if (sz1 <= 1) return; unsigned qsz; if (sz1 >= sz2) { q.reserve(sz1 - sz2 + 1); qsz = sz1 - sz2 + 1; } else { qsz = 0; } numeral const & b_n = p2[sz2-1]; SASSERT(!m().is_zero(b_n)); scoped_numeral a_m(m()); while (true) { checkpoint(); sz1 = r.size(); if (sz1 < sz2) { set_size(qsz, q); return; } unsigned m_n = sz1 - sz2; // m-n if (field()) { numeral & ratio = a_m; m().div(r[sz1 - 1], b_n, ratio); m().add(q[m_n], ratio, q[m_n]); for (unsigned i = 0; i < sz2 - 1; i++) { m().submul(r[i + m_n], ratio, p2[i], r[i + m_n]); } } else { d++; m().set(a_m, r[sz1 - 1]); for (unsigned i = 0; i < sz1 - 1; i++) { m().mul(r[i], b_n, r[i]); } for (unsigned i = 0; i < qsz; i++) { m().mul(q[i], b_n, q[i]); } m().add(q[m_n], a_m, q[m_n]); for (unsigned i = 0; i < sz2 - 1; i++) { m().submul(r[i + m_n], a_m, p2[i], r[i + m_n]); } } set_size(sz1 - 1, r); } } // Pseudo division void core_manager::div_rem(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, unsigned & d, numeral_vector & q, numeral_vector & r) { numeral_vector & _r = m_div_tmp1; numeral_vector & _q = m_div_tmp2; div_rem_core(sz1, p1, sz2, p2, d, _q, _r); r.swap(_r); q.swap(_q); } // Pseudo division void core_manager::div(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & q) { unsigned d; numeral_vector & _r = m_div_tmp1; numeral_vector & _q = m_div_tmp2; div_rem_core(sz1, p1, sz2, p2, d, _q, _r); reset(_r); q.swap(_q); } // Pseudo remainder void core_manager::rem(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, unsigned & d, numeral_vector & buffer) { SASSERT(!is_alias(p1, buffer)); SASSERT(!is_alias(p2, buffer)); d = 0; SASSERT(sz2 > 0); if (sz2 == 1) { reset(buffer); return; } 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 (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) { TRACE("rem_bug", tout << "finished\n";); return; } unsigned m_n = sz1 - sz2; if (field()) { numeral & ratio = a_m; m().div(buffer[sz1 - 1], b_n, ratio); for (unsigned i = 0; i < sz2 - 1; i++) { m().submul(buffer[i + m_n], ratio, p2[i], buffer[i + m_n]); } } else { // buffer: a_m * x^m + a_{m-1} * x^{m-1} + ... + a_0 // p2: b_n * x^n + b_{n-1} * x^{n-1} + ... + b_0 d++; m().set(a_m, buffer[sz1 - 1]); TRACE("rem_bug", tout << "a_m: " << m().to_string(a_m) << ", b_n: " << m().to_string(b_n) << "\n";); // don't need to update position sz1 - 1, since it will become 0 for (unsigned i = 0; i < sz1 - 1; i++) { m().mul(buffer[i], b_n, buffer[i]); } // buffer: a_m * x^m + b_n * a_{m-1} * x^{m-1} + ... + b_n * a_0 // don't need to process i = sz2 - 1, because buffer[sz1 - 1] will become 0. for (unsigned i = 0; i < sz2 - 1; i++) { m().submul(buffer[i + m_n], a_m, p2[i], buffer[i + m_n]); } } set_size(sz1 - 1, buffer); } } // Signed pseudo remainder void core_manager::srem(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & buffer) { SASSERT(!is_alias(p1, buffer)); SASSERT(!is_alias(p2, buffer)); unsigned d; rem(sz1, p1, sz2, p2, d, buffer); // We don't need to flip the sign if d is odd and leading coefficient of p2 is negative if (d % 2 == 0 || (sz2 > 0 && m().is_pos(p2[sz2-1]))) neg(buffer.size(), buffer.c_ptr()); } // Exact division for univariate polynomials. // Return false if p2 does not divide p1 bool core_manager::divides(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2) { if (sz2 == 0) return false; if (sz1 == 0) return true; if (sz2 > sz1 || !m().divides(p2[sz2-1], p1[sz1-1])) return false; scoped_numeral b(m()); numeral_vector & _p1 = m_div_tmp1; set(sz1, p1, _p1); while (true) { if (sz1 == 0) return true; if (sz2 > sz1 || !m().divides(p2[sz2-1], _p1[sz1-1])) return false; unsigned delta = sz1 - sz2; m().div(_p1[sz1-1], p2[sz2-1], b); for (unsigned i = 0; i < sz2 - 1; i++) { if (!m().is_zero(p2[i])) m().submul(_p1[i+delta], b, p2[i], _p1[i+delta]); } m().reset(_p1[sz1-1]); trim(_p1); sz1 = _p1.size(); } return true; } // Exact division for univariate polynomials. // Return false if p2 does not divide p1 bool core_manager::exact_div(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & r) { if (sz2 == 0) return false; if (sz1 == 0) { reset(r); return true; } if (sz2 > sz1 || !m().divides(p2[sz2-1], p1[sz1-1]) || !m().divides(p2[0], p1[0])) return false; numeral_vector & _r = m_exact_div_tmp; reset(_r); unsigned deg = sz1 - sz2; _r.reserve(deg+1); numeral_vector & _p1 = m_div_tmp1; // std::cerr << "dividing with "; display(std::cerr, _p1); std::cerr << std::endl; TRACE("factor_bug", tout << "sz1: " << sz1 << " p1: " << p1 << ", _p1.c_ptr(): " << _p1.c_ptr() << ", _p1.size(): " << _p1.size() << "\n";); set(sz1, p1, _p1); SASSERT(_p1.size() == sz1); while (true) { TRACE("upolynomial", tout << "exact_div loop...\n"; display(tout, _p1); tout << "\n"; display(tout, _r); tout << "\n";); // std::cerr << "dividing with "; display(std::cerr, _p1); std::cerr << std::endl; if (sz1 == 0) { set_size(deg+1, _r); r.swap(_r); return true; } if (sz2 > sz1 || !m().divides(p2[sz2-1], _p1[sz1-1]) || !m().divides(p2[0], _p1[0])) { reset(r); return false; } unsigned delta = sz1 - sz2; numeral & a_r = _r[delta]; m().div(_p1[sz1-1], p2[sz2-1], a_r); for (unsigned i = 0; i < sz2 - 1; i++) { if (!m().is_zero(p2[i])) m().submul(_p1[i+delta], a_r, p2[i], _p1[i+delta]); } m().reset(_p1[sz1-1]); trim(_p1); sz1 = _p1.size(); } return true; } void core_manager::flip_sign_if_lm_neg(numeral_vector & buffer) { unsigned sz = buffer.size(); if (sz == 0) return; if (m().is_neg(buffer[sz - 1])) { for (unsigned i = 0; i < sz; i++) m().neg(buffer[i]); } } void core_manager::CRA_combine_images(numeral_vector const & C1, numeral const & b1, numeral_vector & C2, numeral & b2) { SASSERT(!m().m().is_even(b1)); SASSERT(!m().m().is_even(b2)); numeral_vector & R = m_CRA_tmp; reset(R); scoped_numeral inv1(m()); scoped_numeral inv2(m()); scoped_numeral g(m()); m().gcd(b1, b2, inv1, inv2, g); SASSERT(m().is_one(g)); // b1*inv1 + b2.inv2 = 1 // inv1 is the inverse of b1 mod b2 // inv2 is the inverse of b2 mod b1 m().m().mod(inv1, b2, inv1); m().m().mod(inv2, b1, inv2); TRACE("CRA", tout << "inv1: " << inv1 << ", inv2: " << inv2 << "\n";); scoped_numeral a1(m()); scoped_numeral a2(m()); m().mul(b2, inv2, a1); // a1 is the multiplicator for coefficients of C1 m().mul(b1, inv1, a2); // a2 is the multiplicator for coefficients of C2 TRACE("CRA", tout << "a1: " << a1 << ", a2: " << a2 << "\n";); // new bound scoped_numeral new_bound(m()); m().mul(b1, b2, new_bound); scoped_numeral lower(m()); scoped_numeral upper(m()); scoped_numeral new_a(m()), tmp1(m()), tmp2(m()), tmp3(m()); m().div(new_bound, 2, upper); m().set(lower, upper); m().neg(lower); TRACE("CRA", tout << "lower: " << lower << ", upper: " << upper << "\n";); #define ADD(A1, A2) { \ m().mul(A1, a1, tmp1); \ m().mul(A2, a2, tmp2); \ m().add(tmp1, tmp2, tmp3); \ m().m().mod(tmp3, new_bound, new_a); \ if (m().gt(new_a, upper)) \ m().sub(new_a, new_bound, new_a); \ R.push_back(numeral()); \ m().set(R.back(), new_a); \ } numeral zero(0); unsigned i = 0; unsigned sz1 = C1.size(); unsigned sz2 = C2.size(); unsigned sz = std::min(sz1, sz2); for (; i < sz; i++) { ADD(C1[i], C2[i]); } for (; i < sz1; i++) { ADD(C1[i], zero); } for (; i < sz2; i++) { ADD(zero, C2[i]); } m().set(b2, new_bound); R.swap(C2); } void core_manager::mod_gcd(unsigned sz_u, numeral const * u, unsigned sz_v, numeral const * v, numeral_vector & result) { TRACE("mgcd", tout << "u: "; display_star(tout, sz_u, u); tout << "\nv: "; display_star(tout, sz_v, v); tout << "\n";); SASSERT(sz_u > 0 && sz_v > 0); SASSERT(!m().modular()); scoped_numeral c_u(m()), c_v(m()); numeral_vector & pp_u = m_mgcd_tmp[0]; numeral_vector & pp_v = m_mgcd_tmp[1]; get_primitive_and_content(sz_u, u, pp_u, c_u); get_primitive_and_content(sz_v, v, pp_v, c_v); scoped_numeral c_g(m()); m().gcd(c_u, c_v, c_g); unsigned d_u = sz_u - 1; unsigned d_v = sz_v - 1; numeral const & lc_u = pp_u[d_u]; numeral const & lc_v = pp_v[d_v]; scoped_numeral lc_g(m()); m().gcd(lc_u, lc_v, lc_g); scoped_numeral p(m()); scoped_numeral bound(m()); numeral_vector & u_Zp = m_mgcd_tmp[2]; numeral_vector & v_Zp = m_mgcd_tmp[3]; numeral_vector & q = m_mgcd_tmp[4]; numeral_vector & C = m_mgcd_tmp[5]; for (unsigned i = 0; i < NUM_BIG_PRIMES; i++) { m().set(p, polynomial::g_big_primes[i]); TRACE("mgcd", tout << "trying prime: " << p << "\n";); { scoped_set_zp setZp(*this, p); set(pp_u.size(), pp_u.c_ptr(), u_Zp); set(pp_v.size(), pp_v.c_ptr(), v_Zp); if (degree(u_Zp) < d_u) { TRACE("mgcd", tout << "bad prime, leading coefficient vanished\n";); continue; // bad prime } if (degree(v_Zp) < d_v) { TRACE("mgcd", tout << "bad prime, leading coefficient vanished\n";); continue; // bad prime } euclid_gcd(u_Zp.size(), u_Zp.c_ptr(), v_Zp.size(), v_Zp.c_ptr(), q); // normalize so that lc_g is leading coefficient of q mk_monic(q.size(), q.c_ptr()); scoped_numeral c(m()); m().set(c, lc_g); mul(q, c); } TRACE("mgcd", tout << "new q:\n"; display_star(tout, q); tout << "\n";); if (is_const(q)) { TRACE("mgcd", tout << "done, modular gcd is one\n";); reset(result); result.push_back(numeral()); m().set(result.back(), c_g); return; } if (i == 0) { set(q.size(), q.c_ptr(), C); m().set(bound, p); } else if (q.size() < C.size() || m().m().is_even(p) || m().m().is_even(bound)) { // discard accumulated image, it was affected by unlucky primes TRACE("mgcd", tout << "discarding image\n";); set(q.size(), q.c_ptr(), C); m().set(bound, p); } else { CRA_combine_images(q, p, C, bound); TRACE("mgcd", tout << "new combined:\n"; display_star(tout, C); tout << "\n";); } numeral_vector & candidate = q; get_primitive(C, candidate); TRACE("mgcd", tout << "candidate:\n"; display_star(tout, candidate); tout << "\n";); SASSERT(candidate.size() > 0); numeral const & lc_candidate = candidate[candidate.size() - 1]; if (m().divides(lc_candidate, lc_g) && divides(pp_u, candidate) && divides(pp_v, candidate)) { TRACE("mgcd", tout << "found GCD\n";); mul(candidate, c_g); flip_sign_if_lm_neg(candidate); candidate.swap(result); TRACE("mgcd", tout << "r: "; display_star(tout, result); tout << "\n";); return; } } // Oops, modular GCD failed, not enough primes // fallback to euclid_gcd euclid_gcd(sz_u, u, sz_v, v, result); } void core_manager::euclid_gcd(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & buffer) { SASSERT(!is_alias(p1, buffer)); SASSERT(!is_alias(p2, buffer)); if (sz1 == 0) { set(sz2, p2, buffer); flip_sign_if_lm_neg(buffer); return; } if (sz2 == 0) { set(sz1, p1, buffer); flip_sign_if_lm_neg(buffer); return; } bool is_field = field(); numeral_vector & A = m_gcd_tmp1; numeral_vector & B = m_gcd_tmp2; numeral_vector & R = buffer; set(sz1, p1, A); set(sz2, p2, B); TRACE("upolynomial", tout << "sz1: " << sz1 << ", p1: " << p1 << ", sz2: " << sz2 << ", p2: " << p2 << "\nB.size(): " << B.size() << ", B.c_ptr(): " << B.c_ptr() << "\n";); while (m_limit.inc()) { TRACE("upolynomial", tout << "A: "; display(tout, A); tout <<"\nB: "; display(tout, B); tout << "\n";); if (B.empty()) { normalize(A); buffer.swap(A); // to be consistent, if in a field, we make gcd monic if (is_field) { mk_monic(buffer.size(), buffer.c_ptr()); } else { flip_sign_if_lm_neg(buffer); } TRACE("upolynomial", tout << "GCD\n"; display(tout, sz1, p1); tout << "\n"; display(tout, sz2, p2); tout << "\n--->\n"; display(tout, buffer); tout << "\n";); return; } rem(A.size(), A.c_ptr(), B.size(), B.c_ptr(), R); normalize(R); A.swap(B); B.swap(R); } throw upolynomial_exception(Z3_CANCELED_MSG); } void core_manager::gcd(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & buffer) { SASSERT(!is_alias(p1, buffer)); SASSERT(!is_alias(p2, buffer)); if (sz1 == 0) { set(sz2, p2, buffer); flip_sign_if_lm_neg(buffer); return; } if (sz2 == 0) { set(sz1, p1, buffer); flip_sign_if_lm_neg(buffer); return; } if (!modular()) mod_gcd(sz1, p1, sz2, p2, buffer); else euclid_gcd(sz1, p1, sz2, p2, buffer); } void core_manager::subresultant_gcd(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & buffer) { SASSERT(!is_alias(p1, buffer)); SASSERT(!is_alias(p2, buffer)); if (sz1 == 0) { set(sz2, p2, buffer); flip_sign_if_lm_neg(buffer); return; } if (sz2 == 0) { set(sz1, p1, buffer); flip_sign_if_lm_neg(buffer); return; } numeral_vector & A = m_gcd_tmp1; numeral_vector & B = m_gcd_tmp2; numeral_vector & R = buffer; scoped_numeral g(m()); scoped_numeral h(m()); scoped_numeral aux(m()); m().set(g, 1); m().set(h, 1); unsigned d; set(sz1, p1, A); set(sz2, p2, B); if (A.size() < B.size()) A.swap(B); while (true) { SASSERT(A.size() >= B.size()); TRACE("upolynomial", tout << "A: "; display(tout, A); tout <<"\nB: "; display(tout, B); tout << "\n"; tout << "g: " << m().to_string(g) << ", h: " << m().to_string(h) << "\n";); if (B.empty()) { normalize(A); buffer.swap(A); // to be consistent, if in a field, we make gcd monic if (field()) { mk_monic(buffer.size(), buffer.c_ptr()); } else { flip_sign_if_lm_neg(buffer); } TRACE("upolynomial", tout << "subresultant GCD\n"; display(tout, sz1, p1); tout << "\n"; display(tout, sz2, p2); tout << "\n--->\n"; display(tout, buffer); tout << "\n";); return; } rem(A.size(), A.c_ptr(), B.size(), B.c_ptr(), d, R); unsigned pseudo_div_d = A.size() - B.size(); if (d < pseudo_div_d + 1) { // I used a standard subresultant implementation. // TODO: investigate how to avoid the following adjustment. // // adjust R to make sure the following equation holds: // l(B)^{deg(A) - deg(B) + 1) * A = Q * B + R m().power(B[B.size() - 1], pseudo_div_d + 1 - d, aux); mul(R, aux); } d = pseudo_div_d; TRACE("upolynomial", tout << "R: "; display(tout, R); tout << "\nd: " << d << "\n";); // aux <- g*h^d m().power(h, d, aux); m().mul(g, aux, aux); div(R, aux); A.swap(B); B.swap(R); // g <- l(A) m().set(g, A[A.size() - 1]); m().power(g, d, aux); // h <- h^{1-d} * g^d if (d == 0) { // h <- h^1 * g^0 // do nothing } else if (d == 1) { // h <- h^0 * g^1 m().set(h, g); } else { // h <- (g^d)/(h^{d-1}) SASSERT(d > 1); d--; m().power(h, d, h); SASSERT(m().divides(h, aux)); m().div(aux, h, h); } } } // buffer := square-free(p) void core_manager::square_free(unsigned sz, numeral const * p, numeral_vector & buffer) { SASSERT(!is_alias(p, buffer)); if (sz <= 1) { set(sz, p, buffer); return; } numeral_vector & p_prime = m_sqf_tmp1; numeral_vector & g = m_sqf_tmp2; derivative(sz, p, p_prime); gcd(sz, p, p_prime.size(), p_prime.c_ptr(), g); // subresultant_gcd(sz, p, p_prime.size(), p_prime.c_ptr(), g); if (g.size() <= 1) { set(sz, p, buffer); } else { div(sz, p, g.size(), g.c_ptr(), buffer); normalize(buffer); } } bool core_manager::is_square_free(unsigned sz, numeral const * p) { if (sz <= 1) { return true; } numeral_vector & p_prime = m_sqf_tmp1; numeral_vector & g = m_sqf_tmp2; derivative(sz, p, p_prime); gcd(sz, p, p_prime.size(), p_prime.c_ptr(), g); return g.size() <= 1; } void core_manager::pw(unsigned sz, numeral const * p, unsigned k, numeral_vector & r) { if (k == 0) { SASSERT(sz != 0); r.reserve(1); m().set(r[0], 1); set_size(1, r); return; } if (k == 1 || sz == 0 || (sz == 1 && m().is_one(p[0]))) { set(sz, p, r); return; } numeral_vector & result = m_pw_tmp; set(sz, p, result); for (unsigned i = 1; i < k; i++) mul(m_pw_tmp.size(), m_pw_tmp.c_ptr(), sz, p, m_pw_tmp); r.swap(result); #if 0 unsigned mask = 1; numeral_vector & p2 = m_pw_tmp; set(sz, p, p2); reset(r); r.push_back(numeral(1)); while (mask <= k) { if (mask & k) { // r := r * p2 mul(r.size(), r.c_ptr(), p2.size(), p2.c_ptr(), r); } mul(p2.size(), p2.c_ptr(), p2.size(), p2.c_ptr(), p2); mask = mask << 1; } #endif } void core_manager::mk_monic(unsigned sz, numeral * p, numeral & lc, numeral & lc_inv) { m().set(lc, 1); m().set(lc_inv, 1); if (sz > 0 && !m().is_one(p[sz-1])) { int d = sz-1; m().swap(lc, p[d--]); m().set(lc_inv, lc); m().inv(lc_inv); for (; d >= 0; -- d) { m().mul(p[d], lc_inv, p[d]); } } } // Extended GCD void core_manager::ext_gcd(unsigned szA, numeral const * A, unsigned szB, numeral const * B, numeral_vector & U, numeral_vector & V, numeral_vector & D) { SASSERT(!is_alias(A, U)); SASSERT(!is_alias(A, V)); SASSERT(!is_alias(A, D)); SASSERT(!is_alias(B, U)); SASSERT(!is_alias(B, V)); SASSERT(!is_alias(B, D)); SASSERT(field()); scoped_numeral_vector V1(m()), V3(m()), Q(m()), R(m()), T(m()), V1Q(m()); // since we are in a field define gcd(A, B) to be monic // if AU + BV = D and D is not monic we make it monic, and then divide U and V by the same inverse // initialization // U <- 1 reset(U); U.push_back(numeral()); m().set(U.back(), 1); // D <- A set(szA, A, D); mk_monic(szA, D.c_ptr()); // V1 <- 0 reset(V1); // V3 <- B set(szB, B, V3); while (true) { if (V3.empty()) { // V3 is the zero polynomial numeral_vector & AU = V1; numeral_vector & D_AU = V3; // V <- (D - AU)/B mul(szA, A, U.size(), U.c_ptr(), AU); sub(D.size(), D.c_ptr(), AU.size(), AU.c_ptr(), D_AU); div(D_AU.size(), D_AU.c_ptr(), szB, B, V); DEBUG_CODE({ scoped_numeral_vector BV(m()); scoped_numeral_vector expected_D(m()); mul(szB, B, V.size(), V.c_ptr(), BV); add(AU.size(), AU.c_ptr(), BV.size(), BV.c_ptr(), expected_D); SASSERT(eq(expected_D, D)); }); // if D is not monic, make it monic scoped_numeral lc_inv(m()), lc(m()); mk_monic(D.size(), D.c_ptr(), lc, lc_inv); mul(U, lc_inv); mul(V, lc_inv); return; } // D = QV3 + R div_rem(D.size(), D.c_ptr(), V3.size(), V3.c_ptr(), Q, R); // T <- U - V1Q mul(V1.size(), V1.c_ptr(), Q.size(), Q.c_ptr(), V1Q); sub(U.size(), U.c_ptr(), V1Q.size(), V1Q.c_ptr(), T); // U <- V1 U.swap(V1); // D <- V3 D.swap(V3); // V1 <- T V1.swap(T); // V3 <- R V3.swap(R); } } // Display p std::ostream& core_manager::display(std::ostream & out, unsigned sz, numeral const * p, char const * var_name, bool use_star) const { bool displayed = false; unsigned i = sz; scoped_numeral a(m()); while (i > 0) { --i; if (!m().is_zero(p[i])) { m().set(a, p[i]); if (displayed) { m().abs(a); if (m().is_pos(p[i])) out << " + "; else out << " - "; } displayed = true; if (i == 0) { out << m().to_string(a); } else { SASSERT(i > 0); if (!m().is_one(a)) { out << m().to_string(a); if (use_star) out << "*"; else out << " "; } out << var_name; if (i > 1) out << "^" << i; } } } if (!displayed) out << "0"; return out; } static void display_smt2_mumeral(std::ostream & out, numeral_manager & m, mpz const & n) { if (m.is_neg(n)) { out << "(- "; mpz abs_n; m.set(abs_n, n); m.neg(abs_n); m.display(out, abs_n); m.del(abs_n); out << ")"; } else { m.display(out, n); } } static void display_smt2_monomial(std::ostream & out, numeral_manager & m, mpz const & n, unsigned k, char const * var_name) { if (k == 0) { display_smt2_mumeral(out, m, n); } else if (m.is_one(n)) { if (k == 1) out << var_name; else out << "(^ " << var_name << " " << k << ")"; } else { out << "(* "; display_smt2_mumeral(out, m, n); out << " "; if (k == 1) out << var_name; else out << "(^ " << var_name << " " << k << ")"; out << ")"; } } // Display p as an s-expression std::ostream& core_manager::display_smt2(std::ostream & out, unsigned sz, numeral const * p, char const * var_name) const { if (sz == 0) { out << "0"; return out; } if (sz == 1) { display_smt2_mumeral(out, m(), p[0]); return out; } unsigned non_zero_idx = UINT_MAX; unsigned num_non_zeros = 0; for (unsigned i = 0; i < sz; i++) { if (m().is_zero(p[i])) continue; non_zero_idx = i; num_non_zeros ++; } if (num_non_zeros == 1) { SASSERT(non_zero_idx != UINT_MAX && non_zero_idx >= 1); display_smt2_monomial(out, m(), p[non_zero_idx], non_zero_idx, var_name); } out << "(+"; unsigned i = sz; while (i > 0) { --i; if (!m().is_zero(p[i])) { out << " "; display_smt2_monomial(out, m(), p[i], i, var_name); } } return out << ")"; } bool core_manager::eq(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2) { if (sz1 != sz2) return false; for (unsigned i = 0; i < sz1; i++) { if (!m().eq(p1[i], p2[i])) return false; } return true; } void upolynomial_sequence::push(unsigned sz, numeral * p) { m_begins.push_back(m_seq_coeffs.size()); m_szs.push_back(sz); for (unsigned i = 0; i < sz; i++) { m_seq_coeffs.push_back(numeral()); swap(m_seq_coeffs.back(), p[i]); } } void upolynomial_sequence::push(numeral_manager & m, unsigned sz, numeral const * p) { m_begins.push_back(m_seq_coeffs.size()); m_szs.push_back(sz); for (unsigned i = 0; i < sz; i++) { m_seq_coeffs.push_back(numeral()); m.set(m_seq_coeffs.back(), p[i]); } } scoped_numeral_vector::scoped_numeral_vector(manager & m): _scoped_numeral_vector(m.m()) { } scoped_upolynomial_sequence::~scoped_upolynomial_sequence() { m_manager.reset(*this); } manager::~manager() { reset(m_db_tmp); reset(m_dbab_tmp1); reset(m_dbab_tmp2); reset(m_tr_tmp); reset(m_push_tmp); } void manager::reset(upolynomial_sequence & seq) { reset(seq.m_seq_coeffs); seq.m_szs.reset(); seq.m_begins.reset(); } // Remove zero roots from p void manager::remove_zero_roots(unsigned sz, numeral const * p, numeral_vector & buffer) { SASSERT(sz > 0); if (!m().is_zero(p[0])) { // zero is not a root of p set(sz, p, buffer); return; } unsigned i = 0; while (true) { // p must not be the zero polynomial SASSERT(i < sz); if (!m().is_zero(p[i])) break; i++; } unsigned new_sz = sz - i; buffer.reserve(new_sz); for (unsigned j = 0; j < new_sz; j++) { m().set(buffer[j], p[j + i]); } set_size(new_sz, buffer); } // Evaluate the sign of p(1/2) bool manager::has_one_half_root(unsigned sz, numeral const * p) { // Actually, given b = c/2^k, we compute the sign of 2^n*p(1/2) // Original Horner Sequence // ((a_n * 1/2 + a_{n-1})*1/2 + a_{n-2})*1/2 + a_{n-3} ... // Variation of the Horner Sequence for 2^n*p(1/2) // a_n + a_{n-1}*2 + a_{n-2}*(2^2) + a_{n-3}*(2^3) ... if (sz == 0) return true; if (sz == 1) return false; unsigned k = 1; scoped_numeral r(m()); scoped_numeral ak(m()); m().set(r, p[sz-1]); unsigned i = sz - 1; while (i > 0) { --i; numeral const & a = p[i]; m().mul2k(a, k, ak); m().add(r, ak, r); k++; } return m().is_zero(r); } // Remove 1/2 root from p void manager::remove_one_half_root(unsigned sz, numeral const * p, numeral_vector & buffer) { SASSERT(has_one_half_root(sz, p)); numeral two_x_1[2] = { numeral(-1), numeral(2) }; div(sz, p, 2, two_x_1, buffer); } polynomial::sign manager::sign_of(numeral const & c) { if (m().is_zero(c)) return polynomial::sign_zero; if (m().is_pos(c)) return polynomial::sign_pos; else return polynomial::sign_neg; } // Return the number of sign changes in the coefficients of p unsigned manager::sign_changes(unsigned sz, numeral const * p) { unsigned r = 0; auto prev_sign = polynomial::sign_zero; unsigned i = 0; for (; i < sz; i++) { auto sign = sign_of(p[i]); if (sign == polynomial::sign_zero) continue; if (sign != prev_sign && prev_sign != polynomial::sign_zero) r++; prev_sign = sign; } return r; } // Return the descartes bound for (0, +oo) unsigned manager::descartes_bound(unsigned sz, numeral const * p) { return sign_changes(sz, p); } // Return the descartes bound for the number of roots in the interval (0, 1) unsigned manager::descartes_bound_0_1(unsigned sz, numeral const * p) { if (sz <= 1) return 0; numeral_vector & Q = m_db_tmp; set(sz, p, Q); #if 0 // slow version unsigned n = Q.size() - 1; unsigned i; for (unsigned i = 1; i <= n; i++) { for (unsigned k = i; k >= 1; k--) { m().add(Q[k], Q[k-1], Q[k]); } } return sign_changes(Q.size(), Q.c_ptr()); #endif polynomial::sign prev_sign = polynomial::sign_zero; unsigned num_vars = 0; // a0 a1 a2 a3 // a0 a0+a1 a0+a1+a2 a0+a1+a2+a3 // a0 2a0+a1 3a0+2a1+a2 // a0 3a0+a1 // a0 for (unsigned i = 0; i < sz; i++) { checkpoint(); unsigned k; for (k = 1; k < sz - i; k++) { m().add(Q[k], Q[k-1], Q[k]); } auto sign = sign_of(Q[k-1]); if (polynomial::is_zero(sign)) continue; if (sign != prev_sign && !polynomial::is_zero(prev_sign)) { num_vars++; if (num_vars > 1) return num_vars; } prev_sign = sign; } return num_vars; } // Return the descartes bound for the number of roots in the interval (a, b) unsigned manager::descartes_bound_a_b(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq const & a, mpbq const & b) { if (bqm.is_nonneg(a)) { // Basic idea: apply descartes_bound_0_1 to p2(x) where // p1(x) = p(x+a) // p2(x) = p1((b-a)*x) TRACE("upolynomial", tout << "pos interval... " << bqm.to_string(a) << ", " << bqm.to_string(b) << "\n"; display(tout, sz, p); tout << "\n";); numeral_vector & p_aux = m_dbab_tmp1; translate_bq(sz, p, a, p_aux); TRACE("upolynomial", tout << "after translation\n"; display(tout, p_aux); tout << "\n";); scoped_mpbq b_a(bqm); bqm.sub(b, a, b_a); compose_p_b_x(p_aux.size(), p_aux.c_ptr(), b_a); TRACE("upolynomial", tout << "after composition: " << bqm.to_string(b_a) << "\n"; display(tout, p_aux); tout << "\n";); unsigned result = descartes_bound_0_1(p_aux.size(), p_aux.c_ptr()); return result; } else if (bqm.is_nonpos(b)) { // Basic idea: apply descartes_bound_a_b to p(-x) with intervals (-b, -a) numeral_vector & p_aux = m_dbab_tmp2; set(sz, p, p_aux); p_minus_x(p_aux.size(), p_aux.c_ptr()); scoped_mpbq mb(bqm); scoped_mpbq ma(bqm); bqm.set(mb, b); bqm.neg(mb); bqm.set(ma, a); bqm.neg(ma); unsigned result = descartes_bound_a_b(p_aux.size(), p_aux.c_ptr(), bqm, mb, ma); return result; } else if (!has_zero_roots(sz, p)) { mpbq zero(0); unsigned r1 = descartes_bound_a_b(sz, p, bqm, a, zero); if (r1 > 1) return r1; // if p has more than 1 root in (a, zero), then we don't even evaluate (zero, b) unsigned r2 = descartes_bound_a_b(sz, p, bqm, zero, b); if (r1 == 0) return r2; if (r2 == 0) return r1; return 2; // p has more than one root in (a, b) } else { // zero is a root of p mpbq zero(0); if (descartes_bound_a_b(sz, p, bqm, a, zero) > 0) return 2; // p has more than one root in (a, b) if (descartes_bound_a_b(sz, p, bqm, zero, b) > 0) return 2; // p has more than one root in (a, b) return 1; // zero is the only root in (a, b) } } // p(x) := p(x+1) void manager::translate(unsigned sz, numeral * p) { if (sz <= 1) return; unsigned n = sz - 1; for (unsigned i = 1; i <= n; i++) { checkpoint(); for (unsigned k = n-i; k <= n-1; k++) m().add(p[k], p[k+1], p[k]); } } // p(x) := p(x+2^k) void manager::translate_k(unsigned sz, numeral * p, unsigned k) { if (sz <= 1) return; scoped_numeral aux(m()); unsigned n = sz - 1; for (unsigned i = 1; i <= n; i++) { checkpoint(); for (unsigned k = n-i; k <= n-1; k++) { m().mul2k(p[k+1], k, aux); m().add(p[k], aux, p[k]); } } } // p(x) := p(x+c) void manager::translate_z(unsigned sz, numeral * p, numeral const & c) { if (sz <= 1) return; unsigned n = sz - 1; for (unsigned i = 1; i <= n; i++) { checkpoint(); for (unsigned k = n-i; k <= n-1; k++) m().addmul(p[k], c, p[k+1], p[k]); } } // Given b of the form c/(2^k) // p(x) := (2^k)^n * p(x + c/(2^k)) where b = c/2^k // // Given p: a_n * x^n + ... + a_0 // // buffer := a_n * (2^k * x + c)^n + a_{n-1} * 2^k * (2^k * x + c)^{n-1} + ... + a_1 * (2^k)^{n-1} * (2^k * x + c) + a_0 * (2^k)^n // // We perform the transformation in two steps: // 1) a_n * x^n + a_{n-1} * 2^k * x^{n-1} + ... + a_1 * (2^k)^{n-1} + a_0 * (2^k)^n // Let d_n, ..., d_0 be the coefficients after this step // Then, we have // d_n * x^n + ... + d_0 // 2) d_n * (2^k * x + c)^n + ... + d_0 // This step is like the translation with integers, but the coefficients must be adjusted by 2^k in each iteration. // // That is, it is a special translation such as: // a_n*(b*x + c)^n + a_{n-1}*(b*x + c)^{n-1} + ... + a_1*(b*x + c) + a_0 // This kind of translation can be computed by applying the following recurrence: // To simplify the notation, we fix n = 4 // Moreover we use a_i[k] to represent the coefficient a_i at iteration k // a_i[0] = a_i // // a_4*(b*x + c)^4 + a_3*(b*x + c)^3 + a_2*(b*x + c)^2 + a_1*(b*x + c) + a_0 // --> // a_4*b*x*(b*x + c)^3 + (a_3 + a_4*c)*(b*x + c)^3 + a_2*(b*x + c)^2 + a_1*(b*x + c) + a_0 // Thus a_4[1] = a_4[0]*b, a_3[1] = a_3[0] + a_4[0]*c, a_2[1] = a_2[0], a_1[1] = a_1[0], a_0[1] = a_0[0] // // a_4[1]*x*(b*x + c)^3 + a_3[1]*(b*x + c)^3 + a_2[1]*(b*x + c)^2 + a_1[1]*(b*x + c) + a_0[1] // --> // a_4[1]*b*x^2*(b*x + c)^2 + (a_3[1]*b + a_4[1]*c)*x*(b*x + c)^2 + (a_2[1] + a_3[1]*c)*(b*x + c)^2 + a_1[1]*(b*x + c) + a_0[1] // Thus a_4[2] = a_4[1]*b, a_3[2] = a_3[1]*b + a_4[1]*c, a_2[2] = a_2[1] + a_3[1]*c, a_1[2] = a_1[1], a_0[2] = a_0[1] // // a_4[2]*x^2*(b*x + c)^2 + a_3[2]*x*(b*x + c)^2 + a_2[2]*(b*x + c)^2 + a_1[2]*(b*x + c) + a_0[2] // --> // a_4[2]*b*x^3*(b*x + c) + (a_3[2]*b + a_4[2]*c)*x^2*(b*x + c) + (a_2[2]*b + a_3[2]*c)*x*(b*x + c) + (a_1[2] + a_2[2]*c)*(b*x + c) + a_0[2] // Thus a_4[3] = a_4[2]*b, a_3[3] = a_3[2]*b + a_4[2]*c, a_2[3] = a_2[2]*b + a_3[2]*c, a_1[3] = a_1[2] + a_2[2]*c, a_0[3] = a_1[3] // // a_4[3]*x^3*(b*x + c) + a_3[3]*x^2*(b*x + c) + a_2[3]*x*(b*x + c) + a_1[3]*(b*x + c) + a_0[3] // ---> // a_4[3]*b*x^4 + (a_3[3]*b + a_4[3]*c)*x^3 + (a_2[3]*b + a_3[3]*c)*x^2 + (a_1[3]*b + a_2[3]*c)*x + (a_0[3] + a_1[3]*c) // void manager::translate_bq(unsigned sz, numeral * p, mpbq const & b) { if (sz <= 1) return; // Step 1 compose_2kn_p_x_div_2k(sz, p, b.k()); TRACE("upolynomial", tout << "after compose 2kn_p_x_div_2k\n"; display(tout, sz, p); tout << "\n";); // Step 2 numeral const & c = b.numerator(); unsigned n = sz - 1; for (unsigned i = 1; i <= n; i++) { checkpoint(); m().addmul(p[n - i], c, p[n - i + 1], p[n - i]); for (unsigned k = n - i + 1; k <= n - 1; k++) { m().mul2k(p[k], b.k()); m().addmul(p[k], c, p[k + 1], p[k]); } m().mul2k(p[n], b.k()); } TRACE("upolynomial", tout << "after special translation\n"; display(tout, sz, p); tout << "\n";); } // Similar to translate_bq but for rationals void manager::translate_q(unsigned sz, numeral * p, mpq const & b) { if (sz <= 1) return; // Step 1 compose_an_p_x_div_a(sz, p, b.denominator()); TRACE("upolynomial", tout << "after compose_an_p_x_div_a\n"; display(tout, sz, p); tout << "\n";); // Step 2 numeral const & c = b.numerator(); unsigned n = sz - 1; for (unsigned i = 1; i <= n; i++) { checkpoint(); m().addmul(p[n - i], c, p[n - i + 1], p[n - i]); for (unsigned k = n - i + 1; k <= n - 1; k++) { m().mul(p[k], b.denominator(), p[k]); m().addmul(p[k], c, p[k + 1], p[k]); } m().mul(p[n], b.denominator(), p[n]); } TRACE("upolynomial", tout << "after special translation\n"; display(tout, sz, p); tout << "\n";); } // p(x) := 2^n*p(x/2) where n = sz-1 void manager::compose_2n_p_x_div_2(unsigned sz, numeral * p) { if (sz <= 1) return; // a_n * x^n + 2 * a_{n-1} * x^{n-1} + ... + (2^n)*a_0 unsigned k = sz-1; // k = n for (unsigned i = 0; i < sz - 1; i++) { m().mul2k(p[i], k); k--; } } // p(x) := p(-x) void manager::p_minus_x(unsigned sz, numeral * p) { for (unsigned i = 0; i < sz; i++) { if (m().is_zero(p[i])) continue; if (i % 2 == 0) continue; m().neg(p[i]); } } // p(x) := x^n * p(1/x) void manager::p_1_div_x(unsigned sz, numeral * p) { if (sz <= 1) return; unsigned i = 0; unsigned j = sz-1; SASSERT(j >= 1); while (true) { swap(p[i], p[j]); i++; j--; if (i >= j) break; } } // p(x) := p(2^k * x) void manager::compose_p_2k_x(unsigned sz, numeral * p, unsigned k) { // (2^k)^n*a_n*x^n + (2^k)^(n-1)*x^{n-1} + ... + a_0 if (sz <= 1) return; unsigned k_i = k; for (unsigned i = 1; i < sz; i++) { m().mul2k(p[i], k_i); k_i += k; } } // p(x) := (2^k)^n * p(x/2^k) // // Let old(p) be of the form: // a_n * x^n + a_{n-1}*x^{n-1} + ... + a_1 * x + a_0 // // Then p is of the form: // a_n * x^n + a_{n-1} * 2^k * x^{n-1} + a_{n-2} * (2^k)^2 * x^{n-2} + ... + a_0 * (2^k)^n void manager::compose_2kn_p_x_div_2k(unsigned sz, numeral * p, unsigned k) { if (sz <= 1) return; unsigned k_i = k*sz; for (unsigned i = 0; i < sz; i++) { k_i -= k; if (!m().is_zero(p[i])) m().mul2k(p[i], k_i); } } // p(x) := a^n * p(x/a) // See compose_2kn_p_x_div_2k void manager::compose_an_p_x_div_a(unsigned sz, numeral * p, numeral const & a) { if (sz <= 1) return; unsigned i = sz-1; scoped_numeral a_i(m()); m().set(a_i, a); while (i > 0) { --i; if (!m().is_zero(p[i])) m().mul(p[i], a_i, p[i]); m().mul(a_i, a, a_i); } } // p(x) := p(b * x) void manager::compose_p_b_x(unsigned sz, numeral * p, numeral const & b) { if (sz <= 1) return; scoped_numeral b_i(m()); m().set(b_i, b); for (unsigned i = 1; i < sz; i++) { if (!m().is_zero(p[i])) m().mul(p[i], b_i, p[i]); m().mul(b_i, b, b_i); } } // Let b be of the form c/(2^k), then this operation is equivalent to: // (2^k)^n*p(c*x/(2^k)) // // Let old(p) be of the form: // a_n * x^n + a_{n-1}*x^{n-1} + ... + a_1 * x + a_0 // // Then p is of the form: // a_n * c^n * x^n + a_{n-1} * c^{n-1} * 2^k * x^{n-1} + ... + a_1 * c * (2^k)^(n-1) * x + a_0 * (2^k)^n void manager::compose_p_b_x(unsigned sz, numeral * p, mpbq const & b) { if (sz <= 1) return; unsigned k = b.k(); numeral const & c = b.numerator(); scoped_numeral c_i(m()); m().set(c_i, 1); unsigned k_i = k*sz; for (unsigned i = 0; i < sz; i++) { k_i -= k; if (!m().is_zero(p[i])) { m().mul2k(p[i], k_i); m().mul(p[i], c_i, p[i]); } m().mul(c_i, c, c_i); } SASSERT(k_i == 0); } // Let q be of the form b/c, then this operation is equivalent to: // p(x) := c^n*p(b*x/c) // // If u is a root of old(p), then u*(c/b) is a root of new p. // // Let old(p) be of the form: // a_n * x^n + a_{n-1}*x^{n-1} + ... + a_1 * x + a_0 // // Then p is of the form: // a_n * b^n * x^n + a_{n-1} * b^{n-1} * c * x^{n-1} + ... + a_1 * b * c^(n-1) * x + a_0 * c^n void manager::compose_p_q_x(unsigned sz, numeral * p, mpq const & q) { if (sz <= 1) return; numeral const & b = q.numerator(); numeral const & c = q.denominator(); scoped_numeral bc(m()); m().power(c, sz-1, bc); // bc = b^n for (unsigned i = 0; i < sz; i++) { if (!m().is_zero(p[i])) m().mul(p[i], bc, p[i]); if (i < sz - 1) { // bc <- (bc/b)*c m().div(bc, c, bc); m().mul(bc, b, bc); } } } // Evaluate the sign of p(b) polynomial::sign manager::eval_sign_at(unsigned sz, numeral const * p, mpbq const & b) { // Actually, given b = c/2^k, we compute the sign of (2^k)^n*p(b) // Original Horner Sequence // ((a_n * b + a_{n-1})*b + a_{n-2})*b + a_{n-3} ... // Variation of the Horner Sequence for (2^k)^n*p(b) // ((a_n * c + a_{n-1}*2_k)*c + a_{n-2}*(2_k)^2)*c + a_{n-3}*(2_k)^3 ... + a_0*(2_k)^n if (sz == 0) return polynomial::sign_zero; if (sz == 1) return sign_of(p[0]); numeral const & c = b.numerator(); unsigned k = b.k(); unsigned k_i = k; scoped_numeral r(m()); scoped_numeral ak(m()); m().set(r, p[sz-1]); unsigned i = sz-1; while (i > 0) { --i; numeral const & a = p[i]; if (m().is_zero(a)) { m().mul(r, c, r); } else { m().mul2k(a, k_i, ak); m().addmul(ak, r, c, r); } k_i += k; } return sign_of(r); } // Evaluate the sign of p(b) polynomial::sign manager::eval_sign_at(unsigned sz, numeral const * p, mpq const & b) { // Actually, given b = c/d, we compute the sign of (d^n)*p(b) // Original Horner Sequence // ((a_n * b + a_{n-1})*b + a_{n-2})*b + a_{n-3} ... // Variation of the Horner Sequence for (d^n)*p(b) // ((a_n * c + a_{n-1}*d)*c + a_{n-2}*d^2)*c + a_{n-3}*d^3 ... + a_0*d^n if (sz == 0) return polynomial::sign_zero; if (sz == 1) return sign_of(p[0]); numeral const & c = b.numerator(); numeral const & d = b.denominator(); scoped_numeral d_i(m()); m().set(d_i, d); scoped_numeral r(m()); scoped_numeral ak(m()); m().set(r, p[sz-1]); unsigned i = sz-1; while (i > 0) { --i; numeral const & a = p[i]; if (m().is_zero(a)) { m().mul(r, c, r); } else { m().mul(a, d_i, ak); m().addmul(ak, r, c, r); } m().mul(d_i, d, d_i); } return sign_of(r); } // Evaluate the sign of p(b) polynomial::sign manager::eval_sign_at(unsigned sz, numeral const * p, mpz const & b) { // Using Horner Sequence // ((a_n * b + a_{n-1})*b + a_{n-2})*b + a_{n-3} ... if (sz == 0) return polynomial::sign_zero; if (sz == 1) return sign_of(p[0]); scoped_numeral r(m()); m().set(r, p[sz-1]); unsigned i = sz-1; while (i > 0) { --i; numeral const & a = p[i]; if (m().is_zero(a)) m().mul(r, b, r); else m().addmul(a, r, b, r); } return sign_of(r); } polynomial::sign manager::eval_sign_at_zero(unsigned sz, numeral const * p) { if (sz == 0) return polynomial::sign_zero; return sign_of(p[0]); } polynomial::sign manager::eval_sign_at_plus_inf(unsigned sz, numeral const * p) { if (sz == 0) return polynomial::sign_zero; return sign_of(p[sz-1]); } polynomial::sign manager::eval_sign_at_minus_inf(unsigned sz, numeral const * p) { if (sz == 0) return polynomial::sign_zero; unsigned degree = sz - 1; if (degree % 2 == 0) return sign_of(p[sz-1]); else return -sign_of(p[sz-1]); } template unsigned manager::sign_variations_at_core(upolynomial_sequence const & seq, mpbq const & b) { unsigned sz = seq.size(); if (sz <= 1) return 0; unsigned r = 0; int sign, prev_sign; sign = 0; prev_sign = 0; unsigned i = 0; for (; i < sz; i++) { // find next nonzero unsigned psz = seq.size(i); numeral const * p = seq.coeffs(i); switch (loc) { case PLUS_INF: sign = eval_sign_at_plus_inf(psz, p); break; case MINUS_INF: sign = eval_sign_at_minus_inf(psz, p); break; case ZERO: sign = eval_sign_at_zero(psz, p); break; case MPBQ: sign = eval_sign_at(psz, p, b); break; default: UNREACHABLE(); break; } if (sign == 0) continue; SASSERT(sign == 1 || sign == -1); // in the first iteration prev_sign == 0, then r is never incremented. if (sign != prev_sign && prev_sign != 0) r++; // move to the next prev_sign = sign; } return r; } unsigned manager::sign_variations_at_minus_inf(upolynomial_sequence const & seq) { mpbq dummy(0); return sign_variations_at_core(seq, dummy); } unsigned manager::sign_variations_at_plus_inf(upolynomial_sequence const & seq) { mpbq dummy(0); return sign_variations_at_core(seq, dummy); } unsigned manager::sign_variations_at_zero(upolynomial_sequence const & seq) { mpbq dummy(0); return sign_variations_at_core(seq, dummy); } unsigned manager::sign_variations_at(upolynomial_sequence const & seq, mpbq const & b) { return sign_variations_at_core(seq, b); } void manager::root_upper_bound(unsigned sz, numeral const * p, numeral & r) { // Using Cauchy's Inequality // We have that any root u of p must satisfy // |u| < (max(p) + min(p))/min(p) // |u| < (max(p) + |a_n|)/|a_n| // where: max(p) is the maximum coefficient in absolute value. // min(p) is the minimum (nonzero) coefficient in absolute value SASSERT(sz > 0); SASSERT(!m().is_zero(p[sz - 1])); bool init = false; scoped_numeral max(m()); scoped_numeral min(m()); scoped_numeral a_n(m()); scoped_numeral r2(m()); m().set(a_n, p[sz - 1]); m().abs(a_n); scoped_numeral c(m()); for (unsigned i = 0; i < sz; i++) { if (m().is_zero(p[i])) continue; m().set(c, p[i]); m().abs(c); if (!init) { m().set(max, c); m().set(min, c); init = true; continue; } if (m().gt(c, max)) m().set(max, c); if (m().lt(c, min)) m().set(min, c); } // first bound m().add(min, max, r); m().div(r, min, r); m().add(r, numeral(1), r); // second bound m().add(a_n, max, r2); m().div(r2, a_n, r2); m().add(r2, numeral(1), r2); // use the best bound if (m().lt(r2, r)) swap(r, r2); SASSERT(m().le(r, r2)); } /** \brief Find positive root upper bound using Knuth's approach. Given p(x) = a_n * x^n + a_{n-1} * x^{n-1} + ... + a_0 If a_n is positive, Let B = max({ (-a_{n-k}/a_n)^{1/k} | 1 <= k <= n, a_{n-k} < 0 }) Then, 2*B is a bound for the positive roots Similarly, if a_n is negative Let B = max({ (-a_{n-k}/a_n)^{1/k} | 1 <= k <= n, a_{n-k} > 0 }) Then, 2*B is a bound for the positive roots This procedure returns a k s.t. 2*B <= 2^k The procedure actually computes max of log2(abs(a_{n-k})) + 1 - log2(abs(a_n))/k Proof Sketch: Let u > 0 be a root of p(x). If u <= B, then we are done. So, let us assume u > B Assume, a_n > 0 (the proof is similar for a_n < 0) Then: a_n * u^n + a_{n-1} * u^{n-1} + ... + a0 = 0 u^n = 1/a_n (-a_{n-1} * u^{n-1} - ... - a_0) Note that, if we remove the monomials s.t. a_i is positive, then we are increasing the value of (-a_{n-1} * u^{n-1} - ... - a_0). Thus, u^n <= 1/a_n(SUM_{a_i < 0, 0 <= i < n} (-a_i * u^i)) Dividing by u_n which is positive, we get 1 <= 1/a_n(SUM_{a_i < 0, 0 <= i < n} (-a_i * u^i)) By replacing, i = n - k we have 1 <= 1/a_n(SUM_{a_{n-k} < 0, n >= k > 0} (-a_{n-k} * u^{n-k})) = 1/a_n(SUM_{a_{n-k} < 0, 1 <= k <= n} (-a_i * u^{n-k})) < Sum_{1 <= k < +oo}(B/u)^k Since u > B, we have that Sum_{1 <= k < +oo}(B/u)^k = B/(u - B) Thus, we have 1 < B/(u - B) and u < 2B */ unsigned manager::knuth_positive_root_upper_bound(unsigned sz, numeral const * p) { if (sz == 0) return 0; unsigned max = 0; unsigned n = sz - 1; bool pos_a_n = m().is_pos(p[n]); unsigned log2_a_n = pos_a_n ? m().log2(p[n]) : m().mlog2(p[n]); for (unsigned k = 1; k <= n; k++) { numeral const & a_n_k = p[n - k]; if (m().is_zero(a_n_k)) continue; bool pos_a_n_k = m().is_pos(a_n_k); if (pos_a_n_k == pos_a_n) continue; // must have opposite signs unsigned log2_a_n_k = pos_a_n_k ? m().log2(a_n_k) : m().mlog2(a_n_k); if (log2_a_n > log2_a_n_k) continue; unsigned curr = log2_a_n_k + 1 - log2_a_n; if (curr % k == 0) { curr /= k; } else { curr /= k; curr ++; } if (curr > max) max = curr; } return max + 1; } /** It is essentially applying knuth_positive_root_upper_bound for p(-x) */ unsigned manager::knuth_negative_root_upper_bound(unsigned sz, numeral const * p) { numeral * _p = const_cast(p); p_minus_x(sz, _p); unsigned r = knuth_positive_root_upper_bound(sz, _p); p_minus_x(sz, _p); return r; } /** Return a lower bound for the nonzero roots of p(x). Let k be the result of this procedure, Then for any nonzero root alpha of p(x), we have that |alpha| > 1/2^k We essentially compute the upper bound for the roots of x^n*p(1/x) where n is the degree of p. Remark: alpha is a nonzero root of p(x) iff 1/alpha is a root of x^n*p(1/x). Thus, if 2^k is upper bound for the root of x^n*p(1/x). Then we have that -2^k < 1/alpha < 2^k and consequently alpha < -1/2^k or 1/2^k < alpha /pre p is not the zero polynomial. */ unsigned manager::nonzero_root_lower_bound(unsigned sz, numeral const * p) { SASSERT(sz > 0); // consume zeros unsigned i = 0; while (true) { SASSERT(i < sz); if (!m().is_zero(p[i])) break; i++; } SASSERT(i < sz); SASSERT(!m().is_zero(p[i])); unsigned nz_sz = sz - i; numeral const * nz_p = p + i; // nz_p does not have zero roots; numeral * _nz_p = const_cast(nz_p); // destructive update for quickly computing x^n*nz_p(1/x) std::reverse(_nz_p, _nz_p + nz_sz); unsigned k1 = knuth_positive_root_upper_bound(nz_sz, _nz_p); unsigned k2 = knuth_negative_root_upper_bound(nz_sz, _nz_p); // undo destructive update std::reverse(_nz_p, _nz_p + nz_sz); return std::max(k1, k2); } // Frames for implementing drs_isolate_0_1_roots. // The frames are used to avoid recursive calls and potential stack overflows. // Each frame has a polynomial associated with it. The polynomials // are stored in a separate stack. struct manager::drs_frame { unsigned m_parent_idx; // position of the parent frame, UINT_MAX if it doesn't have a parent frame unsigned m_size:30; // size of the polynomial associated with this frame unsigned m_first:1; // first time visiting the frame? unsigned m_left:1; // true if the frame is the left child of the parent frame. drs_frame(unsigned pidx, unsigned sz, bool left): m_parent_idx(pidx), m_size(sz), m_first(true), m_left(left) { } }; // Pop the top frame from the frame_stack, remove the coefficients of the polynomial associated with the top frame from p_stack. void manager::pop_top_frame(numeral_vector & p_stack, svector & frame_stack) { SASSERT(!frame_stack.empty()); unsigned sz = frame_stack.back().m_size; SASSERT(sz <= p_stack.size()); for (unsigned i = 0; i < sz; i++) { m().del(p_stack.back()); p_stack.pop_back(); } frame_stack.pop_back(); } // Auxiliary method for isolating the roots of p in the interval (0, 1). // The basic idea is to split the interval in: (0, 1/2) and (1/2, 1). // This is accomplished by analyzing the roots in the interval (0, 1) of the following polynomials. // p1(x) := 2^n * p(x/2) where n = sz-1 // p2(x) := p1(x+1) // We say p1(x) is the left child, and p2 the right child. The coefficients p1 and p2 are stored in p_stack. // A new frame is created for each child. void manager::push_child_frames(unsigned sz, numeral const * p, numeral_vector & p_stack, svector & frame_stack) { // I don't really need the following test, because 0 - 1 == UINT_MAX unsigned parent_idx = frame_stack.empty() ? UINT_MAX : frame_stack.size() - 1; numeral_vector & p_aux = m_push_tmp; // Possible optimization: the coefficients of the parent frame are not needed anymore. // So, we could reuse/steal them. // left child set(sz, p, p_aux); compose_2n_p_x_div_2(p_aux.size(), p_aux.c_ptr()); normalize(p_aux); for (unsigned i = 0; i < sz; i++) { p_stack.push_back(numeral()); m().set(p_stack.back(), p_aux[i]); } frame_stack.push_back(drs_frame(parent_idx, sz, true)); // right child translate(sz, p_stack.end() - sz, p_aux); normalize(p_aux); for (unsigned i = 0; i < sz; i++) { p_stack.push_back(numeral()); swap(p_stack.back(), p_aux[i]); } frame_stack.push_back(drs_frame(parent_idx, sz, false)); } // (0,1) is an isolating interval for the polynomial associated with the top frame. // Apply transformations for obtaining isolating interval for the original polynomial: // We use the following transformations: // Left child: (l, u) -> (l/2, u/2) // Right child: (l, u) -> ((l+1)/2, (u+1)/2) void manager::add_isolating_interval(svector const & frame_stack, mpbq_manager & bqm, mpbq_vector & lowers, mpbq_vector & uppers) { mpbq l(0); mpbq u(1); unsigned idx = frame_stack.size() - 1; while (idx != UINT_MAX) { drs_frame const & fr = frame_stack[idx]; TRACE("upolynomial", tout << "normalizing...\n"; tout << "idx: " << idx << ", left: " << fr.m_left << ", l: " << bqm.to_string(l) << ", u: " << bqm.to_string(u) << "\n";); if (fr.m_left) { bqm.div2(l); bqm.div2(u); } else { bqm.add(l, mpz(1), l); bqm.add(u, mpz(1), u); bqm.div2(l); bqm.div2(u); } idx = fr.m_parent_idx; } TRACE("upolynomial", tout << "adding normalized interval (" << bqm.to_string(l) << ", " << bqm.to_string(u) << ")\n";); lowers.push_back(mpbq()); uppers.push_back(mpbq()); swap(lowers.back(), l); swap(uppers.back(), u); } // 1/2 is a root of the polynomial associated with the top frame. // Apply transformations for obtaining root of the original polynomial: // We use the following transformations: // Left child: u -> u/2 // Right child: u -> (u+1)/2 void manager::add_root(svector const & frame_stack, mpbq_manager & bqm, mpbq_vector & roots) { mpbq u(1,1); unsigned idx = frame_stack.size() - 1; while (idx != UINT_MAX) { drs_frame const & fr = frame_stack[idx]; if (fr.m_left) { bqm.div2(u); } else { bqm.add(u, mpz(1), u); bqm.div2(u); } idx = fr.m_parent_idx; } TRACE("upolynomial", tout << "adding normalized root: " << bqm.to_string(u) << "\n";); roots.push_back(mpbq()); swap(roots.back(), u); } // Isolate roots in the interval (0, 1) void manager::drs_isolate_0_1_roots(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers) { TRACE("upolynomial", tout << "isolating (0,1) roots of:\n"; display(tout, sz, p); tout << "\n";); unsigned k = descartes_bound_0_1(sz, p); // easy cases... if (k == 0) { TRACE("upolynomial", tout << "polynomial does not have any roots\n";); return; } if (k == 1) { TRACE("upolynomial", tout << "polynomial has one root in (0, 1)\n";); lowers.push_back(mpbq(0)); uppers.push_back(mpbq(1)); return; } TRACE("upolynomial", tout << "polynomial has more than one root in (0, 1), starting search...\n";); scoped_numeral_vector q(m()); scoped_numeral_vector p_stack(m()); svector frame_stack; if (has_one_half_root(sz, p)) { TRACE("upolynomial", tout << "polynomial has a 1/2 root\n";); roots.push_back(mpbq(1, 1)); remove_one_half_root(sz, p, q); push_child_frames(q.size(), q.c_ptr(), p_stack, frame_stack); } else { push_child_frames(sz, p, p_stack, frame_stack); } SASSERT(!frame_stack.empty()); while (!frame_stack.empty()) { checkpoint(); drs_frame & fr = frame_stack.back(); unsigned sz = fr.m_size; SASSERT(sz <= p_stack.size()); numeral const * p = p_stack.end() - sz; TRACE("upolynomial", tout << "processing frame #" << frame_stack.size() - 1 << "\n" << "first: " << fr.m_first << ", left: " << fr.m_left << ", sz: " << fr.m_size << ", parent_idx: "; if (fr.m_parent_idx == UINT_MAX) tout << ""; else tout << fr.m_parent_idx; tout << "\np: "; display(tout, sz, p); tout << "\n";); if (!fr.m_first) { pop_top_frame(p_stack, frame_stack); continue; } fr.m_first = false; unsigned k = descartes_bound_0_1(sz, p); if (k == 0) { TRACE("upolynomial", tout << "(0, 1) does not have roots\n";); pop_top_frame(p_stack, frame_stack); continue; } if (k == 1) { TRACE("upolynomial", tout << "(0, 1) is isolating interval\n";); add_isolating_interval(frame_stack, bqm, lowers, uppers); pop_top_frame(p_stack, frame_stack); continue; } TRACE("upolynomial", tout << "polynomial has more than one root in (0, 1) creating child frames...\n";); if (has_one_half_root(sz, p)) { TRACE("upolynomial", tout << "1/2 is a root\n";); add_root(frame_stack, bqm, roots); remove_one_half_root(sz, p, q); push_child_frames(q.size(), q.c_ptr(), p_stack, frame_stack); } else { push_child_frames(sz, p, p_stack, frame_stack); } } } // Foreach i in [starting_at, v.size()) v[i] := 2^k*v[i] static void adjust_pos(mpbq_manager & bqm, mpbq_vector & v, unsigned starting_at, unsigned k) { unsigned sz = v.size(); for (unsigned i = starting_at; i < sz; i++) bqm.mul2k(v[i], k); } // Foreach i in [starting_at, v.size()) v[i] := -2^k*v[i] static void adjust_neg(mpbq_manager & bqm, mpbq_vector & v, unsigned starting_at, unsigned k) { unsigned sz = v.size(); for (unsigned i = starting_at; i < sz; i++) { bqm.mul2k(v[i], k); bqm.neg(v[i]); } } static void swap_lowers_uppers(unsigned starting_at, mpbq_vector & lowers, mpbq_vector & uppers) { SASSERT(lowers.size() == uppers.size()); unsigned sz = lowers.size(); for (unsigned i = starting_at; i < sz; i++) { swap(lowers[i], uppers[i]); } } // Isolate roots in an interval (-2^neg_k, 2^pos_k) using an approach based on Descartes rule of signs. void manager::drs_isolate_roots(unsigned sz, numeral * p, unsigned neg_k, unsigned pos_k, mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers) { scoped_numeral_vector aux_p(m()); set(sz, p, aux_p); pos_k = std::max(neg_k, pos_k); compose_p_2k_x(sz, aux_p.c_ptr(), pos_k); // p(x) := p(2^{pos_k} * x) // Since the desired positive roots of p(x) are in (0, 2^pos_k), TRACE("upolynomial", tout << "searching at (0, 1)\n";); unsigned old_roots_sz = roots.size(); unsigned old_lowers_sz = lowers.size(); drs_isolate_0_1_roots(sz, aux_p.c_ptr(), bqm, roots, lowers, uppers); SASSERT(lowers.size() == uppers.size()); adjust_pos(bqm, roots, old_roots_sz, pos_k); adjust_pos(bqm, lowers, old_lowers_sz, pos_k); adjust_pos(bqm, uppers, old_lowers_sz, pos_k); // Isolate roots in (-2^{neg_k}, 0) // p(x) := p(-x) p_minus_x(sz, p); compose_p_2k_x(sz, p, neg_k); TRACE("upolynomial", tout << "searching at (-1, 0) using:\n"; display(tout, sz, p); tout << "\n";); old_roots_sz = roots.size(); old_lowers_sz = lowers.size(); drs_isolate_0_1_roots(sz, p, bqm, roots, lowers, uppers); SASSERT(lowers.size() == uppers.size()); adjust_neg(bqm, roots, old_roots_sz, neg_k); adjust_neg(bqm, lowers, old_lowers_sz, neg_k); adjust_neg(bqm, uppers, old_lowers_sz, neg_k); swap_lowers_uppers(old_lowers_sz, lowers, uppers); } // Isolate roots using an approach based on Descartes rule of signs. void manager::drs_isolate_roots(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers) { SASSERT(lowers.size() == uppers.size()); SASSERT(!has_zero_roots(sz, p)); scoped_numeral_vector p1(m()); set(sz, p, p1); normalize(p1); TRACE("upolynomial", scoped_numeral U(m()); root_upper_bound(p1.size(), p1.c_ptr(), U); unsigned U_k = m().log2(U) + 1; tout << "Cauchy U: 2^" << U_k << "\n"; tout << "Knuth pos U: 2^" << knuth_positive_root_upper_bound(sz, p) << "\n"; tout << "Knuth neg U: 2^" << knuth_negative_root_upper_bound(sz, p) << "\n";); #if 1 unsigned pos_k = knuth_positive_root_upper_bound(sz, p); unsigned neg_k = knuth_negative_root_upper_bound(sz, p); #else scoped_numeral U(m()); root_upper_bound(p1.size(), p1.c_ptr(), U); unsigned pos_k = m().log2(U) + 1; unsigned neg_k = pos_k; #endif drs_isolate_roots(p1.size(), p1.c_ptr(), neg_k, pos_k, bqm, roots, lowers, uppers); } // Frame for root isolation in sturm_isolate_roots. // It stores (lower, upper, num. sign variations at lower, num. sign variations at upper) struct ss_frame { mpbq m_lower; mpbq m_upper; unsigned m_lower_sv; // number of sign variations in the sturm sequence at the lower bound unsigned m_upper_sv; // number of sign variations in the sturm sequence at the upper bound }; class ss_frame_stack : public svector { mpbq_manager & m; public: ss_frame_stack(mpbq_manager & _m):m(_m) {} ~ss_frame_stack() { iterator it = begin(); iterator e = end(); for (; it != e; ++it) { ss_frame & f = *it; m.del(f.m_lower); m.del(f.m_upper); } } }; inline void pop_ss_frame(mpbq_manager & m, ss_frame_stack & s) { m.del(s.back().m_lower); m.del(s.back().m_upper); s.pop_back(); } void ss_add_isolating_interval(mpbq_manager & m, mpbq const & lower, mpbq const & upper, mpbq_vector & lowers, mpbq_vector & uppers) { lowers.push_back(mpbq()); uppers.push_back(mpbq()); m.set(lowers.back(), lower); m.set(uppers.back(), upper); } // Isolate roots in an interval (-2^neg_k, 2^pos_k) using an approach based on Descartes rule of signs. void manager::sturm_isolate_roots_core(unsigned sz, numeral * p, unsigned neg_k, unsigned pos_k, mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers) { SASSERT(!has_zero_roots(sz, p)); scoped_upolynomial_sequence seq(*this); scoped_mpbq mid(bqm); scoped_mpbq curr_lower(bqm); scoped_mpbq curr_upper(bqm); sturm_seq(sz, p, seq); ss_frame_stack s(bqm); TRACE("upolynomial", tout << "p: "; display(tout, sz, p); tout << "\nSturm seq:\n"; display(tout, seq); tout << "\n";); unsigned lower_sv = sign_variations_at_minus_inf(seq); unsigned zero_sv = sign_variations_at_zero(seq); unsigned upper_sv = sign_variations_at_plus_inf(seq); if (upper_sv >= lower_sv) return; // no roots bqm.power(mpbq(2), neg_k, curr_lower); bqm.neg(curr_lower); bqm.power(mpbq(2), pos_k, curr_upper); #define PUSH_SS_FRAME(LOWER_SV, UPPER_SV, LOWER, UPPER) { \ SASSERT(!bqm.eq(LOWER, UPPER)); \ if (LOWER_SV == UPPER_SV) { \ \ } \ else if (LOWER_SV == UPPER_SV + 1) { \ /* Sturm is for half-open intervals (a, b] */ \ /* We must check if upper is the root */ \ if (eval_sign_at(sz, p, UPPER) == 0) { \ /* found precise root */ \ roots.push_back(mpbq()); \ bqm.set(roots.back(), UPPER); \ } \ else { \ ss_add_isolating_interval(bqm, LOWER, UPPER, lowers, uppers); \ } \ } \ else { \ s.push_back(ss_frame()); \ ss_frame & f = s.back(); \ bqm.set(f.m_lower, LOWER); \ bqm.set(f.m_upper, UPPER); \ f.m_lower_sv = LOWER_SV; \ f.m_upper_sv = UPPER_SV; \ } \ } ((void) 0) mpbq zero(0); PUSH_SS_FRAME(lower_sv, zero_sv, curr_lower, zero); PUSH_SS_FRAME(zero_sv, upper_sv, zero, curr_upper); while (!s.empty()) { checkpoint(); ss_frame & f = s.back(); lower_sv = f.m_lower_sv; upper_sv = f.m_upper_sv; bqm.swap(curr_lower, f.m_lower); bqm.swap(curr_upper, f.m_upper); pop_ss_frame(bqm, s); SASSERT(lower_sv > upper_sv + 1); bqm.add(curr_lower, curr_upper, mid); bqm.div2(mid); TRACE("upolynomial", tout << "depth: " << s.size() << "\n"; tout << "lower_sv: " << lower_sv << "\n"; tout << "upper_sv: " << upper_sv << "\n"; tout << "curr_lower: " << curr_lower << "\n"; tout << "curr_upper: " << curr_upper << "\n"; tout << "mid: " << mid << "\n";); unsigned mid_sv = sign_variations_at(seq, mid); PUSH_SS_FRAME(lower_sv, mid_sv, curr_lower, mid); PUSH_SS_FRAME(mid_sv, upper_sv, mid, curr_upper); } } void manager::sturm_isolate_roots(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers) { SASSERT(lowers.size() == uppers.size()); scoped_numeral_vector p1(m()); set(sz, p, p1); normalize(p1); #if 1 unsigned pos_k = knuth_positive_root_upper_bound(sz, p); unsigned neg_k = knuth_negative_root_upper_bound(sz, p); #else scoped_numeral U(m()); root_upper_bound(p1.size(), p1.c_ptr(), U); unsigned pos_k = m().log2(U) + 1; unsigned neg_k = pos_k; #endif sturm_isolate_roots_core(p1.size(), p1.c_ptr(), neg_k, pos_k, bqm, roots, lowers, uppers); } // Isolate roots of a square free polynomial that does not have zero roots void manager::sqf_nz_isolate_roots(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers) { SASSERT(!has_zero_roots(sz, p)); drs_isolate_roots(sz, p, bqm, roots, lowers, uppers); // sturm_isolate_roots(sz, p, bqm, roots, lowers, uppers); } void manager::sqf_isolate_roots(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers) { bqm.reset(roots); bqm.reset(lowers); bqm.reset(uppers); if (has_zero_roots(sz, p)) { roots.push_back(mpbq(0)); scoped_numeral_vector nz_p(m()); remove_zero_roots(sz, p, nz_p); TRACE("upolynomial", tout << "after removing zero root:\n"; display(tout, nz_p); tout << "\n";); SASSERT(!has_zero_roots(nz_p.size(), nz_p.c_ptr())); sqf_nz_isolate_roots(nz_p.size(), nz_p.c_ptr(), bqm, roots, lowers, uppers); } else { sqf_nz_isolate_roots(sz, p, bqm, roots, lowers, uppers); } } void manager::isolate_roots(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers) { SASSERT(sz > 0); TRACE("upolynomial", tout << "isolating roots of:\n"; display(tout, sz, p); tout << "\n";); scoped_numeral_vector sqf_p(m()); square_free(sz, p, sqf_p); TRACE("upolynomial", tout << "square free part:\n"; display(tout, sqf_p); tout << "\n";); sqf_isolate_roots(sqf_p.size(), sqf_p.c_ptr(), bqm, roots, lowers, uppers); } // Keep expanding the Sturm sequence starting at seq void manager::sturm_seq_core(upolynomial_sequence & seq) { scoped_numeral_vector r(m()); 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)) return; normalize(r); seq.push(r.size(), r.c_ptr()); } } void manager::sturm_seq(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, upolynomial_sequence & seq) { reset(seq); seq.push(m(), sz1, p1); seq.push(m(), sz2, p2); sturm_seq_core(seq); } void manager::sturm_seq(unsigned sz, numeral const * p, upolynomial_sequence & seq) { reset(seq); scoped_numeral_vector p_prime(m()); seq.push(m(), sz, p); derivative(sz, p, p_prime); seq.push(p_prime.size(), p_prime.c_ptr()); sturm_seq_core(seq); } void manager::sturm_tarski_seq(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, upolynomial_sequence & seq) { reset(seq); scoped_numeral_vector p1p2(m()); seq.push(m(), sz1, p1); derivative(sz1, p1, p1p2); mul(p1p2.size(), p1p2.c_ptr(), sz2, p2, p1p2); seq.push(p1p2.size(), p1p2.c_ptr()); sturm_seq_core(seq); } void manager::fourier_seq(unsigned sz, numeral const * p, upolynomial_sequence & seq) { reset(seq); scoped_numeral_vector p_prime(m()); seq.push(m(), sz, p); if (sz == 0) return; unsigned degree = sz - 1; for (unsigned i = 0; i < degree; i++) { unsigned sz = seq.size(); derivative(seq.size(sz-1), seq.coeffs(sz-1), p_prime); normalize(p_prime); seq.push(p_prime.size(), p_prime.c_ptr()); } } /** We say an interval (a, b) of a polynomial p is ISOLATING if p has only one root in the interval (a, b). We say an isolating interval (a, b) of a square free polynomial p is REFINABLE if sign(p(a)) = -sign(p(b)) Not every isolating interval (a, b) of a square free polynomial p is refinable, because sign(p(a)) or sign(p(b)) may be zero. Refinable intervals of square free polynomials are useful, because we can increase precision ("squeeze" the interval) by just evaluating p at (a+b)/2 This procedure converts an isolating interval of a square free polynomial p, into a refinable interval, or an actual root. The method returns TRUE if it produced a REFINABLE interval (a', b'). The new interval is stored in input variables a and b. The method returns FALSE if it found the root a' in the interval (a, b). The root is stored in the input variable a. \pre p MUST BE SQUARE FREE. \warning This method may loop if p is not square free or if (a,b) is not an isolating interval. */ bool manager::isolating2refinable(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq & a, mpbq & b) { int sign_a = eval_sign_at(sz, p, a); int sign_b = eval_sign_at(sz, p, b); TRACE("upolynomial", tout << "sign_a: " << sign_a << ", sign_b: " << sign_b << "\n";); if (sign_a != 0 && sign_b != 0) { // CASE 1 SASSERT(sign_a == -sign_b); // p is square free return true; } if (sign_a == 0 && sign_b != 0) { // CASE 2 scoped_mpbq new_a(bqm); // new_a <- (a+b)/2 bqm.add(a, b, new_a); bqm.div2(new_a); while (true) { TRACE("upolynomial", tout << "CASE 2, a: " << bqm.to_string(a) << ", b: " << bqm.to_string(b) << ", new_a: " << bqm.to_string(new_a) << "\n";); int sign_new_a = eval_sign_at(sz, p, new_a); if (sign_new_a != sign_b) { swap(new_a, a); SASSERT(sign_new_a == 0 || // found the actual root sign_new_a == -sign_b); // found refinable interval return sign_new_a != 0; } else { SASSERT(sign_new_a == sign_b); // b <- new_a // new_a <- (a+b)/2 swap(b, new_a); bqm.add(b, a, new_a); bqm.div2(new_a); } } } if (sign_a != 0 && sign_b == 0 ) { // CASE 3 scoped_mpbq new_b(bqm); // new_b <- (a+b)/2 bqm.add(a, b, new_b); bqm.div2(new_b); while (true) { TRACE("upolynomial", tout << "CASE 3, a: " << bqm.to_string(a) << ", b: " << bqm.to_string(b) << ", new_b: " << bqm.to_string(new_b) << "\n";); int sign_new_b = eval_sign_at(sz, p, new_b); if (sign_new_b != sign_a) { SASSERT(sign_new_b == 0 || // found the actual root sign_new_b == -sign_a); // found refinable interval if (sign_new_b == 0) swap(new_b, a); // store root in a else swap(new_b, b); return sign_new_b != 0; } else { SASSERT(sign_new_b == sign_a); // a <- new_b // new_b <- (a+b)/2 swap(a, new_b); bqm.add(b, a, new_b); bqm.div2(new_b); } } } SASSERT(sign_a == 0 && sign_b == 0); // CASE 4 // we do cases 3 and 4 in "parallel" mpbq & a1 = a; scoped_mpbq b1(bqm); scoped_mpbq a2(bqm); mpbq & b2 = b; scoped_mpbq new_a1(bqm); scoped_mpbq new_b2(bqm); // b1 <- (a+b)/2 bqm.add(a, b, b1); bqm.div2(b1); // a2 <- (a+b)/2 bqm.set(a2, b1); int sign_b1 = eval_sign_at(sz, p, b1); int sign_a2 = sign_b1; bool result; if (sign_b1 == 0) { // found root swap(b1, a); result = false; goto end; } // new_a1 <- (a1+b1)/2 bqm.add(a1, b1, new_a1); bqm.div2(new_a1); // new_b2 <- (a2+b2)/2 bqm.add(a2, b2, new_b2); bqm.div2(new_b2); while (true) { TRACE("upolynomial", tout << "CASE 4\na1: " << bqm.to_string(a1) << ", b1: " << bqm.to_string(b1) << ", new_a1: " << bqm.to_string(new_a1) << "\n"; tout << "a2: " << bqm.to_string(a2) << ", b2: " << bqm.to_string(b2) << ", new_b2: " << bqm.to_string(new_b2) << "\n";); int sign_new_a1 = eval_sign_at(sz, p, new_a1); if (sign_new_a1 == 0) { // found root swap(new_a1, a); result = false; goto end; } if (sign_new_a1 == -sign_b1) { // found interval swap(new_a1, a); swap(b1, b); result = true; goto end; } int sign_new_b2 = eval_sign_at(sz, p, new_b2); if (sign_new_b2 == 0) { // found root swap(new_b2, a); result = false; goto end; } if (sign_new_b2 == -sign_a2) { // found interval swap(a2, a); swap(new_b2, b); result = true; goto end; } SASSERT(sign_new_a1 == sign_b1); // b1 <- new_a1 // new_a1 <- (a1+b1)/2 swap(b1, new_a1); bqm.add(b1, a1, new_a1); bqm.div2(new_a1); SASSERT(sign_new_b2 == sign_a2); // a2 <- new_b2 // new_b2 <- (a2+b2)/2 swap(a2, new_b2); bqm.add(b2, a2, new_b2); bqm.div2(new_b2); } end: return result; } /** Given a square-free polynomial p, and a refinable interval (a,b), then "squeeze" (a,b). That is, return a new interval (a',b') s.t. b' - a' = (b - a)/2 See isolating2refinable for a definition of refinable interval. Return TRUE, if interval was squeezed, and new interval is stored in (a,b). Return FALSE, if the actual root was found, it is stored in a. The arguments sign_a and sign_b must contain the values returned by eval_sign_at(sz, p, a) and eval_sign_at(sz, p, b). */ bool manager::refine_core(unsigned sz, numeral const * p, polynomial::sign sign_a, mpbq_manager & bqm, mpbq & a, mpbq & b) { SASSERT(sign_a == eval_sign_at(sz, p, a)); SASSERT(-sign_a == eval_sign_at(sz, p, b)); SASSERT(sign_a != 0); scoped_mpbq mid(bqm); bqm.add(a, b, mid); bqm.div2(mid); auto sign_mid = eval_sign_at(sz, p, mid); if (polynomial::is_zero(sign_mid)) { swap(mid, a); return false; } if (sign_mid == sign_a) { swap(mid, a); return true; } SASSERT(sign_mid == -sign_a); swap(mid, b); return true; } // See refine_core bool manager::refine(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq & a, mpbq & b) { polynomial::sign sign_a = eval_sign_at(sz, p, a); SASSERT(!polynomial::is_zero(sign_a)); return refine_core(sz, p, sign_a, bqm, a, b); } // Keeps reducing the interval until b - a < 1/2^k or a root is found. // See comments in refine_core above. // // Return TRUE, if interval was squeezed, and new interval is stored in (a,b). // Return FALSE, if the actual root was found, it is stored in a. bool manager::refine_core(unsigned sz, numeral const * p, polynomial::sign sign_a, mpbq_manager & bqm, mpbq & a, mpbq & b, unsigned prec_k) { SASSERT(sign_a != polynomial::sign_zero); SASSERT(sign_a == eval_sign_at(sz, p, a)); SASSERT(-sign_a == eval_sign_at(sz, p, b)); scoped_mpbq w(bqm); while (true) { checkpoint(); bqm.sub(b, a, w); if (bqm.lt_1div2k(w, prec_k)) { return true; } if (!refine_core(sz, p, sign_a, bqm, a, b)) { return false; // found root } } } bool manager::refine(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq & a, mpbq & b, unsigned prec_k) { polynomial::sign sign_a = eval_sign_at(sz, p, a); SASSERT(eval_sign_at(sz, p, b) == -sign_a); SASSERT(sign_a != 0); return refine_core(sz, p, sign_a, bqm, a, b, prec_k); } bool manager::convert_q2bq_interval(unsigned sz, numeral const * p, mpq const & a, mpq const & b, mpbq_manager & bqm, mpbq & c, mpbq & d) { polynomial::sign sign_a = eval_sign_at(sz, p, a); polynomial::sign sign_b = eval_sign_at(sz, p, b); SASSERT(!polynomial::is_zero(sign_a) && !polynomial::is_zero(sign_b)); SASSERT(sign_a == -sign_b); bool found_d = false; TRACE("convert_bug", tout << "a: " << m().to_string(a.numerator()) << "/" << m().to_string(a.denominator()) << "\n"; tout << "b: " << m().to_string(b.numerator()) << "/" << m().to_string(b.denominator()) << "\n"; tout << "sign_a: " << sign_a << "\n"; tout << "sign_b: " << sign_b << "\n";); scoped_mpbq lower(bqm), upper(bqm); if (bqm.to_mpbq(a, lower)) { TRACE("convert_bug", tout << "found c: " << lower << "\n";); // found c swap(c, lower); SASSERT(bqm.eq(c, a)); } else { // lower = a.numerator()/2^(k+1) where k is log2(a) bqm.set(upper, lower); bqm.mul2(upper); if (m_manager.is_neg(a.numerator())) ::swap(lower, upper); TRACE("convert_bug", tout << "a: "; m().display(tout, a.numerator()); tout << "/"; m().display(tout, a.denominator()); tout << "\n"; tout << "lower: "; bqm.display(tout, lower); tout << ", upper: "; bqm.display(tout, upper); tout << "\n";); SASSERT(bqm.lt(lower, a)); SASSERT(bqm.gt(upper, a)); while (bqm.ge(upper, b)) { bqm.refine_upper(a, lower, upper); } SASSERT(bqm.lt(upper, b)); while (true) { auto sign_upper = eval_sign_at(sz, p, upper); if (polynomial::is_zero(sign_upper)) { // found root bqm.swap(c, upper); bqm.del(lower); bqm.del(upper); return false; } else if (sign_upper == sign_a) { // found c bqm.swap(c, upper); break; } else { SASSERT(sign_upper == sign_b); // found d if (!found_d) { found_d = true; bqm.set(d, upper); } } bqm.refine_upper(a, lower, upper); } } if (!found_d) { if (bqm.to_mpbq(b, lower)) { // found d swap(d, lower); SASSERT(bqm.eq(d, b)); } else { // lower = b.numerator()/2^(k+1) where k is log2(b) bqm.set(upper, lower); bqm.mul2(upper); if (m_manager.is_neg(b.numerator())) ::swap(lower, upper); SASSERT(bqm.gt(upper, b)); SASSERT(bqm.lt(lower, b)); while (bqm.le(lower, c)) { bqm.refine_lower(b, lower, upper); } SASSERT(bqm.lt(c, lower)); SASSERT(bqm.lt(lower, upper)); SASSERT(bqm.lt(lower, b)); while (true) { polynomial::sign sign_lower = eval_sign_at(sz, p, lower); if (polynomial::is_zero(sign_lower)) { // found root bqm.swap(c, lower); bqm.del(lower); bqm.del(upper); return false; } else if (sign_lower == sign_b) { // found d bqm.swap(d, lower); break; } bqm.refine_lower(b, lower, upper); } } } SASSERT(bqm.lt(c, d)); SASSERT(eval_sign_at(sz, p, c) == -eval_sign_at(sz, p, d)); SASSERT(bqm.ge(c, a)); SASSERT(bqm.le(d, b)); return true; } bool manager::normalize_interval_core(unsigned sz, numeral const * p, int sign_a, mpbq_manager & m, mpbq & a, mpbq & b) { if (m.is_neg(a) && m.is_pos(b)) { // See bool manager::normalize_interval(unsigned sz, numeral const * p, mpbq_manager & m, mpbq const & a, mpbq const & b) { if (sign_a == INT_MIN) { sign_a = eval_sign_at(sz, p, a); } else { SASSERT(sign_a == eval_sign_at(sz, p, a)); } SASSERT(-sign_a == eval_sign_at(sz, p, b)); SASSERT(sign_a != 0); if (has_zero_roots(sz, p)) { return false; // zero is the root } auto sign_zero = eval_sign_at_zero(sz, p); if (sign_a == sign_zero) { m.reset(a); } else { m.reset(b); } SASSERT(eval_sign_at(sz, p, a) == -eval_sign_at(sz, p, b)); } return true; } bool manager::normalize_interval(unsigned sz, numeral const * p, mpbq_manager & m, mpbq & a, mpbq & b) { return normalize_interval_core(sz, p, INT_MIN, m, a, b); } unsigned manager::get_root_id(unsigned sz, numeral const * p, mpbq const & l) { scoped_upolynomial_sequence seq(*this); sturm_seq(sz, p, seq); unsigned s0 = sign_variations_at_minus_inf(seq); unsigned s1 = sign_variations_at(seq, l); SASSERT(s0 >= s1); return s0 - s1; } void manager::flip_sign(factors & r) { scoped_numeral new_c(m_manager); m_manager.set(new_c, r.get_constant()); m_manager.neg(new_c); r.set_constant(new_c); } void manager::factor_2_sqf_pp(numeral_vector & p, factors & r, unsigned k) { SASSERT(p.size() == 3); // p has degree 2 TRACE("factor", tout << "factor square free (degree == 2):\n"; display(tout, p); tout << "\n";); numeral const & a = p[2]; numeral const & b = p[1]; numeral const & c = p[0]; TRACE("factor", tout << "a: " << m().to_string(a) << "\nb: " << m().to_string(b) << "\nc: " << m().to_string(c) << "\n";); // Create the discriminant: b^2 - 4*a*c scoped_numeral b2(m()); scoped_numeral ac(m()); scoped_numeral disc(m()); m().power(b, 2, b2); m().mul(a, c, ac); m().addmul(b2, numeral(-4), ac, disc); // discriminant must be different from 0, since p is square free SASSERT(!m().is_zero(disc)); scoped_numeral disc_sqrt(m()); if (!m().is_perfect_square(disc, disc_sqrt)) { // p is irreducible r.push_back(p, k); return; } TRACE("factor", tout << "disc_sqrt: " << m().to_string(disc_sqrt) << "\n";); // p = cont*(2*a*x + b - disc_sqrt)*(2*a*x + b + disc_sqrt) scoped_numeral_vector f1(m()); scoped_numeral_vector f2(m()); f1.reserve(2); f2.reserve(2); m().sub(b, disc_sqrt, f1[0]); m().add(b, disc_sqrt, f2[0]); m().mul(a, numeral(2), f1[1]); m().mul(a, numeral(2), f2[1]); set_size(2, f1); set_size(2, f2); normalize(f1); normalize(f2); TRACE("factor", tout << "f1: "; display(tout, f1); tout << "\nf2: "; display(tout, f2); tout << "\n";); DEBUG_CODE({ scoped_numeral_vector f1f2(m()); mul(f1, f2, f1f2); SASSERT(eq(f1f2, p)); }); r.push_back(f1, k); r.push_back(f2, k); } bool manager::factor_sqf_pp(numeral_vector & p, factors & r, unsigned k, factor_params const & params) { unsigned sz = p.size(); SASSERT(sz == 0 || m().is_pos(p[sz-1])); if (sz <= 2) { // linear or constant r.push_back(p, k); return true; } else if (sz == 3) { factor_2_sqf_pp(p, r, k); return true; } else { return factor_square_free(*this, p, r, k, params); } } void manager::flip_factor_sign_if_lm_neg(numeral_vector & p, factors & r, unsigned k) { unsigned sz = p.size(); if (sz == 0) return; if (m().is_neg(p[sz - 1])) { for (unsigned i = 0; i < sz; i++) m().neg(p[i]); if (k % 2 == 1) flip_sign(r); } } bool manager::factor_core(unsigned sz, numeral const * p, factors & r, factor_params const & params) { if (sz == 0) { r.set_constant(numeral(0)); return true; } if (sz == 1) { r.set_constant(p[0]); return true; } // extract content & primitive part scoped_numeral content(m()); scoped_numeral_vector pp(m()); get_primitive_and_content(sz, p, pp, content); r.set_constant(content); // scoped_numeral_vector & C = pp; // Let C be a primitive polynomial of the form: P_1^1 * P_2^2 * ... * P_k^k, where each P_i is square free scoped_numeral_vector C_prime(m()); derivative(C, C_prime); scoped_numeral_vector A(m()), B(m()), D(m()); gcd(C, C_prime, B); bool result = true; if (is_const(B)) { // C must be of the form P_1 (square free) SASSERT(!is_const(C)); flip_factor_sign_if_lm_neg(C, r, 1); if (!factor_sqf_pp(C, r, 1, params)) result = false; } else { // B is of the form P_2 * P_3^2 * ... * P_k^{k-1} VERIFY(exact_div(C, B, A)); TRACE("factor_bug", tout << "C: "; display(tout, C); tout << "\nB: "; display(tout, B); tout << "\nA: "; display(tout, A); tout << "\n";); // A is of the form P_1 * P_2 * ... * P_k unsigned j = 1; while (!is_const(A)) { checkpoint(); TRACE("factor", tout << "factor_core main loop j: " << j << "\nA: "; display(tout, A); tout << "\nB: "; display(tout, B); tout << "\n";); // A is of the form P_j * P_{j+1} * P_{j+2} * ... * P_k // B is of the form P_{j+1} * P_{j+2}^2 * ... * P_k^{k - j - 2} gcd(A, B, D); // D is of the form P_{j+1} * P_{j+2} * ... * P_k VERIFY(exact_div(A, D, C)); // C is of the form P_j if (!is_const(C)) { flip_factor_sign_if_lm_neg(C, r, j); if (!factor_sqf_pp(C, r, j, params)) result = false; } else { TRACE("factor", tout << "const C: "; display(tout, C); tout << "\n";); SASSERT(C.size() == 1); SASSERT(m().is_one(C[0]) || m().is_minus_one(C[0])); if (m().is_minus_one(C[0]) && j % 2 == 1) flip_sign(r); } TRACE("factor_bug", tout << "B: "; display(tout, B); tout << "\nD: "; display(tout, D); tout << "\n";); VERIFY(exact_div(B, D, B)); // B is of the form P_{j+2} * ... * P_k^{k - j - 3} A.swap(D); // D is of the form P_{j+1} * P_{j+2} * ... * P_k j++; } TRACE("factor_bug", tout << "A: "; display(tout, A); tout << "\n";); SASSERT(A.size() == 1 && m().is_one(A[0])); } return result; } bool manager::factor(unsigned sz, numeral const * p, factors & r, factor_params const & params) { bool result = factor_core(sz, p, r, params); #ifndef _EXTERNAL_RELEASE IF_VERBOSE(FACTOR_VERBOSE_LVL, verbose_stream() << "(polynomial-factorization :distinct-factors " << r.distinct_factors() << ")" << std::endl;); #endif return result; } std::ostream& manager::display(std::ostream & out, upolynomial_sequence const & seq, char const * var_name) const { for (unsigned i = 0; i < seq.size(); i++) { display(out, seq.size(i), seq.coeffs(i), var_name); out << "\n"; } return out; } }; z3-z3-4.8.7/src/math/polynomial/upolynomial.h000066400000000000000000001120561356505360400210600ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: upolynomial.h Abstract: Goodies for creating and handling univariate polynomials. A dense representation is much better for Root isolation algorithms, encoding algebraic numbers, factorization, etc. We also use integers as the coefficients of univariate polynomials. Author: Leonardo (leonardo) 2011-11-29 Notes: --*/ #ifndef UPOLYNOMIAL_H_ #define UPOLYNOMIAL_H_ #include "util/mpzzp.h" #include "util/rational.h" #include "math/polynomial/polynomial.h" #include "util/z3_exception.h" #include "util/mpbq.h" #include "util/rlimit.h" #define FACTOR_VERBOSE_LVL 1000 namespace upolynomial { typedef polynomial::factor_params factor_params; // It is used only for signing cancellation. class upolynomial_exception : public default_exception { public: upolynomial_exception(char const * msg):default_exception(msg) {} }; typedef mpz numeral; typedef mpzzp_manager numeral_manager; typedef mpzzp_manager zp_numeral_manager; typedef unsynch_mpz_manager z_numeral_manager; typedef svector numeral_vector; class core_manager { public: typedef _scoped_numeral_vector scoped_numeral_vector; typedef _scoped_numeral scoped_numeral; /** \brief Convenient vector of polynomials that manages its own memory and keeps the degree, of each polynomial. Polynomial is c*f_1^k_1*...*f_n^k_n. */ class factors { private: vector m_factors; svector m_degrees; core_manager & m_upm; numeral m_constant; unsigned m_total_factors; unsigned m_total_degree; public: factors(core_manager & upm); ~factors(); core_manager & upm() const { return m_upm; } core_manager & pm() const { return m_upm; } numeral_manager & nm() const; unsigned distinct_factors() const { return m_factors.size(); } unsigned total_factors() const { return m_total_factors; } void clear(); void reset() { clear(); } numeral_vector const & operator[](unsigned i) const { return m_factors[i]; } numeral const & get_constant() const { return m_constant; } void set_constant(numeral const & constant); unsigned get_degree() const { return m_total_degree; } unsigned get_degree(unsigned i) const { return m_degrees[i]; } void set_degree(unsigned i, unsigned degree); void push_back(numeral_vector const & p, unsigned degree); // push p to vectors and kill it void push_back_swap(numeral_vector & p, unsigned degree); void swap_factor(unsigned i, numeral_vector & p); void swap(factors & other); void multiply(numeral_vector & out) const; void display(std::ostream & out) const; friend std::ostream & operator<<(std::ostream & out, factors const & fs) { fs.display(out); return out; } }; protected: reslimit& m_limit; numeral_manager m_manager; numeral_vector m_basic_tmp; numeral_vector m_div_tmp1; numeral_vector m_div_tmp2; numeral_vector m_exact_div_tmp; numeral_vector m_gcd_tmp1; numeral_vector m_gcd_tmp2; numeral_vector m_CRA_tmp; #define UPOLYNOMIAL_MGCD_TMPS 6 numeral_vector m_mgcd_tmp[UPOLYNOMIAL_MGCD_TMPS]; numeral_vector m_sqf_tmp1; numeral_vector m_sqf_tmp2; numeral_vector m_pw_tmp; static bool is_alias(numeral const * p, numeral_vector & buffer) { return buffer.c_ptr() != nullptr && buffer.c_ptr() == p; } void neg_core(unsigned sz1, numeral const * p1, numeral_vector & buffer); void add_core(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & buffer); void sub_core(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & buffer); void mul_core(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & buffer); void flip_sign_if_lm_neg(numeral_vector & buffer); void mod_gcd(unsigned sz_u, numeral const * u, unsigned sz_v, numeral const * v, numeral_vector & result); void CRA_combine_images(numeral_vector const & q, numeral const & p, numeral_vector & C, numeral & bound); public: core_manager(reslimit& lim, z_numeral_manager & m); ~core_manager(); z_numeral_manager & zm() const { return m_manager.m(); } numeral_manager & m() const { return const_cast(this)->m_manager; } reslimit& lim() const { return m_limit; } /** \brief Return true if Z_p[X] */ bool modular() const { return m().modular(); } bool field() const { return m().field(); } /** \brief Return p in Z_p[X] \pre modular */ numeral const & p() const { return m().p(); } /** \brief Set manager as Z[X] */ void set_z() { m().set_z(); } /** \brief Set manager as Z_p[X] */ void set_zp(numeral const & p) { m().set_zp(p); } void set_zp(uint64_t p) { m().set_zp(p); } void checkpoint(); /** \brief set p size to 0. That is, p is the zero polynomial after this operation. */ void reset(numeral_vector & p); /** \brief Remove zero leading coefficients. After applying this method, we have that p is empty() or p[p.size()-1] is not zero. */ void trim(numeral_vector & p); void set_size(unsigned sz, numeral_vector & buffer); /** \brief Return the actual degree of p. */ unsigned degree(numeral_vector const & p) { unsigned sz = p.size(); return sz == 0 ? 0 : sz - 1; } /** \brief Return true if p is the zero polynomial. */ bool is_zero(numeral_vector & p) { trim(p); return p.empty(); } /** \brief Return true if p is a constant polynomial */ bool is_const(numeral_vector & p) { trim(p); return p.size() <= 1; } /** \brief Copy p to buffer. */ void set(unsigned sz, numeral const * p, numeral_vector & buffer); void set(numeral_vector & target, numeral_vector const & source) { set(source.size(), source.c_ptr(), target); } /** \brief Copy p to buffer. Coefficients of p must be integer. */ void set(unsigned sz, rational const * p, numeral_vector & buffer); /** \brief Compute the primitive part and the content of f (pp can alias f). */ void get_primitive_and_content(unsigned f_sz, numeral const * f, numeral_vector & pp, numeral & cont); void get_primitive_and_content(numeral_vector const & f, numeral_vector & pp, numeral & cont) { get_primitive_and_content(f.size(), f.c_ptr(), pp, cont); } void get_primitive(numeral_vector const & f, numeral_vector & pp) { scoped_numeral cont(m()); get_primitive_and_content(f.size(), f.c_ptr(), pp, cont); } /** \brief p := -p */ void neg(unsigned sz, numeral * p); void neg(numeral_vector & p) { neg(p.size(), p.c_ptr()); } /** \brief buffer := -p */ void neg(unsigned sz, numeral const * p, numeral_vector & buffer); void neg(numeral_vector const & p, numeral_vector & p_neg) { neg(p.size(), p.c_ptr(), p_neg); } /** \brief buffer := p1 + p2 */ void add(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & buffer); void add(numeral_vector const & a, numeral_vector const & b, numeral_vector & c) { add(a.size(), a.c_ptr(), b.size(), b.c_ptr(), c); } /** \brief buffer := p1 - p2 */ void sub(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & buffer); void sub(numeral_vector const & a, numeral_vector const & b, numeral_vector & c) { sub(a.size(), a.c_ptr(), b.size(), b.c_ptr(), c); } /** \brief buffer := p1 * p2 */ void mul(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & buffer); void mul(numeral_vector const & a, numeral_vector const & b, numeral_vector & c) { mul(a.size(), a.c_ptr(), b.size(), b.c_ptr(), c); } /** \brief r := p^k */ void pw(unsigned sz, numeral const * p, unsigned k, numeral_vector & r); /** \brief buffer := dp/dx */ void derivative(unsigned sz1, numeral const * p, numeral_vector & buffer); void derivative(numeral_vector const & p, numeral_vector & d_p) { derivative(p.size(), p.c_ptr(), d_p); } /** \brief Divide coefficients of p by their GCD */ void normalize(unsigned sz, numeral * p); /** \brief Divide coefficients of p by their GCD */ void normalize(numeral_vector & p); /** \brief Divide the coefficients of p by b. This method assumes that every coefficient of p is a multiple of b, and b != 0. */ void div(unsigned sz, numeral * p, numeral const & b); void div(numeral_vector & p, numeral const & b) { div(p.size(), p.c_ptr(), b); } /** \brief Multiply the coefficients of p by b. This method assume b != 0. */ void mul(unsigned sz, numeral * p, numeral const & b); /** \brief Multiply the coefficients of p by b. If b == 0, it is equivalent to a reset() */ void mul(numeral_vector & p, numeral const & b); /** \brief Similar to div_rem but p1 and p2 must not be q and r. */ void div_rem_core(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, unsigned & d, numeral_vector & q, numeral_vector & r); /** \brief If numeral is a field, then return q and r s.t. p1 = q*p2 + r And degree(r) < degree(p2). If numeral is not a field, then return q and r s.t. (b_m)^d * p1 = q * p2 + r where b_m is the leading coefficient of p2 and d <= sz1 - sz2 + 1 if sz1 >= sz2. The output value d is irrelevant if numeral is a field. */ void div_rem(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, unsigned & d, numeral_vector & q, numeral_vector & r); /** \see div_rem */ void div_rem(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & q, numeral_vector & r) { unsigned d = 0; div_rem(sz1, p1, sz2, p2, d, q, r); } void div_rem(numeral_vector const & p1, numeral_vector const & p2, numeral_vector & q, numeral_vector & r) { div_rem(p1.size(), p1.c_ptr(), p2.size(), p2.c_ptr(), q, r); } /** \see div_rem */ void div(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & q); /** \see div_rem */ void rem(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, unsigned & d, numeral_vector & r); /** \see div_rem */ void rem(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & r) { unsigned d = 0; rem(sz1, p1, sz2, p2, d, r); } /** \brief Signed pseudo-remainder. Alias for rem(sz1, p1, sz2, p2, r); neg(r); */ void srem(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & r); /** \brief Return true if p2 divides p1. */ bool divides(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2); bool divides(numeral_vector const & p1, numeral_vector const & p2) { return divides(p1.size(), p1.c_ptr(), p2.size(), p2.c_ptr()); } /** \brief Return true if p2 divides p1, and store the quotient in q. */ bool exact_div(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & q); bool exact_div(numeral_vector const & p1, numeral_vector const & p2, numeral_vector & q) { return exact_div(p1.size(), p1.c_ptr(), p2.size(), p2.c_ptr(), q); } /** \brief Assuming that we can, make the polynomial monic by dividing with the leading coefficient. It puts the leading coefficient into lc, and it's inverse into lc_inv. */ void mk_monic(unsigned sz, numeral * p, numeral & lc, numeral & lc_inv); void mk_monic(unsigned sz, numeral * p, numeral & lc) { numeral lc_inv; mk_monic(sz, p, lc, lc_inv); m().del(lc_inv); } void mk_monic(unsigned sz, numeral * p) { numeral lc, lc_inv; mk_monic(sz, p, lc, lc_inv); m().del(lc); m().del(lc_inv); } void mk_monic(numeral_vector & p) { mk_monic(p.size(), p.c_ptr()); } /** \brief g := gcd(p1, p2) If in a field the coefficients don't matter, so we also make sure that D is monic. */ void gcd(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & g); void euclid_gcd(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & g); void subresultant_gcd(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & g); void gcd(numeral_vector const & p1, numeral_vector const & p2, numeral_vector & g) { gcd(p1.size(), p1.c_ptr(), p2.size(), p2.c_ptr(), g); } void subresultant_gcd(numeral_vector const & p1, numeral_vector const & p2, numeral_vector & g) { subresultant_gcd(p1.size(), p1.c_ptr(), p2.size(), p2.c_ptr(), g); } /** \brief g := square free part of p */ void square_free(unsigned sz, numeral const * p, numeral_vector & g); /** \brief Return true if p is a square-free polynomial. */ bool is_square_free(unsigned sz, numeral const * p); /** \brief Return true if p is a square-free polynomial. */ bool is_square_free(numeral_vector const & p) { return is_square_free(p.size(), p.c_ptr()); } /** \brief Convert a multi-variate polynomial (that is) actually representing an univariate polynomial into a vector of coefficients. */ template void to_numeral_vector(polynomial_ref const & p, numeral_vector & r) { typename polynomial_ref::manager & pm = p.m(); SASSERT(pm.is_univariate(p)); polynomial_ref np(pm); np = pm.normalize(p); unsigned sz = pm.size(p); unsigned deg = pm.total_degree(p); r.reserve(deg+1); for (unsigned i = 0; i <= deg; i++) { m().reset(r[i]); } for (unsigned i = 0; i < sz; i++) { unsigned k = pm.total_degree(pm.get_monomial(p, i)); SASSERT(k <= deg); m().set(r[k], pm.coeff(p, i)); } set_size(deg+1, r); } /** \brief Convert a multi-variate polynomial in [x, y1, ..., yn] to a univariate polynomial in just x by removing everything multivariate. */ template void to_numeral_vector(polynomial_ref const & p, polynomial::var x, numeral_vector & r) { typename polynomial_ref::manager & pm = p.m(); polynomial_ref np(pm); np = pm.normalize(p); unsigned sz = pm.size(p); unsigned deg = pm.degree(p, x); r.reserve(deg+1); for (unsigned i = 0; i <= deg; i++) { m().reset(r[i]); } for (unsigned i = 0; i < sz; i++) { typename polynomial::monomial * mon = pm.get_monomial(p, i); if (pm.size(mon) == 0) { m().set(r[0], pm.coeff(p, i)); } else if (pm.size(mon) == 1 && pm.get_var(mon, 0) == x) { unsigned m_deg_x = pm.degree(mon, 0); m().set(r[m_deg_x], pm.coeff(p, i)); } } set_size(deg+1, r); } /** \brief Extended GCD This method assumes that numeral is a field. It determines U, V, D such that A*U + B*V = D and D is the GCD of A and B. Since in a field the coefficients don't matter, we also make sure that D is monic. */ void ext_gcd(unsigned szA, numeral const * A, unsigned szB, numeral const * B, numeral_vector & U, numeral_vector & V, numeral_vector & D); void ext_gcd(numeral_vector const & A, numeral_vector const & B, numeral_vector & U, numeral_vector & V, numeral_vector & D) { ext_gcd(A.size(), A.c_ptr(), B.size(), B.c_ptr(), U, V, D); } bool eq(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2); bool eq(numeral_vector const & p1, numeral_vector const & p2) { return eq(p1.size(), p1.c_ptr(), p2.size(), p2.c_ptr()); } std::ostream& display(std::ostream & out, unsigned sz, numeral const * p, char const * var_name = "x", bool use_star = false) const; std::ostream& display(std::ostream & out, numeral_vector const & p, char const * var_name = "x") const { return display(out, p.size(), p.c_ptr(), var_name); } std::ostream& display_star(std::ostream & out, unsigned sz, numeral const * p) { return display(out, sz, p, "x", true); } std::ostream& display_star(std::ostream & out, numeral_vector const & p) { return display_star(out, p.size(), p.c_ptr()); } std::ostream& display_smt2(std::ostream & out, unsigned sz, numeral const * p, char const * var_name = "x") const; std::ostream& display_smt2(std::ostream & out, numeral_vector const & p, char const * var_name = "x") const { return display_smt2(out, p.size(), p.c_ptr(), var_name); } }; class scoped_set_z { core_manager & m; bool m_modular; core_manager::scoped_numeral m_p; public: scoped_set_z(core_manager & _m):m(_m), m_modular(m.modular()), m_p(m.m()) { m_p = m.p(); m.set_z(); } ~scoped_set_z() { if (m_modular) m.set_zp(m_p); } }; class scoped_set_zp { core_manager & m; bool m_modular; core_manager::scoped_numeral m_p; public: scoped_set_zp(core_manager & _m, numeral const & p):m(_m), m_modular(m.modular()), m_p(m.m()) { m_p = m.p(); m.set_zp(p); } scoped_set_zp(core_manager & _m, uint64_t p):m(_m), m_modular(m.modular()), m_p(m.m()) { m_p = m.p(); m.set_zp(p); } ~scoped_set_zp() { if (m_modular) m.set_zp(m_p); else m.set_z(); } }; class manager; typedef core_manager z_manager; typedef core_manager zp_manager; typedef z_manager::factors factors; typedef zp_manager::factors zp_factors; typedef svector numeral_vector; class scoped_numeral_vector : public _scoped_numeral_vector { public: scoped_numeral_vector(numeral_manager & m):_scoped_numeral_vector(m) {} scoped_numeral_vector(manager & m); }; class upolynomial_sequence { numeral_vector m_seq_coeffs; // coefficients of all polynomials in the sequence unsigned_vector m_begins; // start position (in m_seq_coeffs) of each polynomial in the sequence unsigned_vector m_szs; // size of each polynomial in the sequence friend class manager; public: /** \brief Add a new polynomial to the sequence. The contents of p is erased. */ void push(unsigned sz, numeral * p); /** \brief Add a new polynomial to the sequence. The contents of p is preserved. */ void push(numeral_manager & m, unsigned sz, numeral const * p); /** \brief Return the number of polynomials in the sequence. */ unsigned size() const { return m_szs.size(); } /** \brief Return the vector of coefficients for the i-th polynomial in the sequence. */ numeral const * coeffs(unsigned i) const { return m_seq_coeffs.c_ptr() + m_begins[i]; } /** \brief Return the size of the i-th polynomial in the sequence. */ unsigned size(unsigned i) const { return m_szs[i]; } }; class scoped_upolynomial_sequence : public upolynomial_sequence { manager & m_manager; public: scoped_upolynomial_sequence(manager & m):m_manager(m) {} ~scoped_upolynomial_sequence(); }; class manager : public core_manager { numeral_vector m_db_tmp; numeral_vector m_dbab_tmp1; numeral_vector m_dbab_tmp2; numeral_vector m_tr_tmp; numeral_vector m_push_tmp; polynomial::sign sign_of(numeral const & c); struct drs_frame; void pop_top_frame(numeral_vector & p_stack, svector & frame_stack); void push_child_frames(unsigned sz, numeral const * p, numeral_vector & p_stack, svector & frame_stack); void add_isolating_interval(svector const & frame_stack, mpbq_manager & bqm, mpbq_vector & lowers, mpbq_vector & uppers); void add_root(svector const & frame_stack, mpbq_manager & bqm, mpbq_vector & roots); void drs_isolate_0_1_roots(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers); void drs_isolate_roots(unsigned sz, numeral * p, numeral & U, mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers); void drs_isolate_roots(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers); void sqf_nz_isolate_roots(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers); void sturm_seq_core(upolynomial_sequence & seq); enum location { PLUS_INF, MINUS_INF, ZERO, MPBQ }; template unsigned sign_variations_at_core(upolynomial_sequence const & seq, mpbq const & b); void flip_sign(factors & r); void flip_factor_sign_if_lm_neg(numeral_vector & p, factors & r, unsigned k); void factor_2_sqf_pp(numeral_vector & p, factors & r, unsigned k); bool factor_sqf_pp(numeral_vector & p, factors & r, unsigned k, factor_params const & params); bool factor_core(unsigned sz, numeral const * p, factors & r, factor_params const & params); public: manager(reslimit& lim, z_numeral_manager & m):core_manager(lim, m) {} ~manager(); void reset(numeral_vector & p) { core_manager::reset(p); } void reset(upolynomial_sequence & seq); /** \brief Return true if 0 is a root of p. */ bool has_zero_roots(unsigned sz, numeral const * p) { SASSERT(sz > 0); return m().is_zero(p[0]); } /** \brief Store in buffer a polynomial that has the same roots of p but the zero roots. We have that: forall u, p(u) = 0 and u != 0 implies buffer(u) = 0 forall u, buffer(u) = 0 implies p(u) = 0 This method assumes p is not the zero polynomial */ void remove_zero_roots(unsigned sz, numeral const * p, numeral_vector & buffer); /** \brief Return true if 1/2 is a root of p. */ bool has_one_half_root(unsigned sz, numeral const * p); /** \brief Store in buffer a polynomial that has the same roots of p, but a 1/2 root is removed. This method assumes that 1/2 is a root of p. */ void remove_one_half_root(unsigned sz, numeral const * p, numeral_vector & buffer); /** \brief Return the number of sign changes in the coefficients of p. Zero coefficients are ignored. */ unsigned sign_changes(unsigned sz, numeral const * p); /** \brief Return the descartes bound for the number of roots of p in the interval (0, +oo) Result: 0 - p has no roots in (0,1) 1 - p has one root in (0,1) >1 - p has more than one root in (0,1) */ unsigned descartes_bound(unsigned sz, numeral const * p); /** \brief Return the descartes bound for the number of roots of p in the interval (0, 1) \see descartes_bound */ unsigned descartes_bound_0_1(unsigned sz, numeral const * p); /** \brief Return the descartes bound for the number of roots of p in the interval (a, b) \see descartes_bound */ unsigned descartes_bound_a_b(unsigned sz, numeral const * p, mpbq_manager & m, mpbq const & a, mpbq const & b); /** \brief p(x) := p(x+1) */ void translate(unsigned sz, numeral * p); void translate(unsigned sz, numeral const * p, numeral_vector & buffer) { set(sz, p, buffer); translate(sz, buffer.c_ptr()); } /** \brief p(x) := p(x+2^k) */ void translate_k(unsigned sz, numeral * p, unsigned k); void translate_k(unsigned sz, numeral const * p, unsigned k, numeral_vector & buffer) { set(sz, p, buffer); translate_k(sz, buffer.c_ptr(), k); } /** \brief p(x) := p(x+c) */ void translate_z(unsigned sz, numeral * p, numeral const & c); void translate_z(unsigned sz, numeral const * p, numeral const & c, numeral_vector & buffer) { set(sz, p, buffer); translate_z(sz, buffer.c_ptr(), c); } /** \brief p(x) := p(x+b) where b = c/2^k buffer := (2^k)^n * p(x + c/(2^k)) */ void translate_bq(unsigned sz, numeral * p, mpbq const & b); void translate_bq(unsigned sz, numeral const * p, mpbq const & b, numeral_vector & buffer) { set(sz, p, buffer); translate_bq(sz, buffer.c_ptr(), b); } /** \brief p(x) := p(x+b) where b = c/d buffer := d^n * p(x + c/d) */ void translate_q(unsigned sz, numeral * p, mpq const & b); void translate_q(unsigned sz, numeral const * p, mpq const & b, numeral_vector & buffer) { set(sz, p, buffer); translate_q(sz, buffer.c_ptr(), b); } /** \brief p(x) := 2^n*p(x/2) where n = sz-1 */ void compose_2n_p_x_div_2(unsigned sz, numeral * p); /** \brief p(x) := (2^k)^n * p(x/(2^k)) */ void compose_2kn_p_x_div_2k(unsigned sz, numeral * p, unsigned k); /** \brief p(x) := p(2^k * x) If u is a root of old(p), then u/2^k is a root of p */ void compose_p_2k_x(unsigned sz, numeral * p, unsigned k); /** \brief p(x) := p(b * x) If u is a root of old(p), then u/b is a root of p */ void compose_p_b_x(unsigned sz, numeral * p, numeral const & b); /** \brief p(x) := p(b * x) If u is a root of old(p), then u/b is a root of p Let b be of the form c/(2^k), then this operation is equivalent to: (2^k)^n*p(c*x/(2^k)) Let old(p) be of the form: a_n * x^n + a_{n-1}*x^{n-1} + ... + a_1 * x + a_0 Then p is of the form: a_n * c^n * x^n + a_{n-1} * c^{n-1} * 2^k * x^{n-1} + ... + a_1 * c * (2^k)^(n-1) * x + a_0 */ void compose_p_b_x(unsigned sz, numeral * p, mpbq const & b); /** \brief p(x) := p(q*x) */ void compose_p_q_x(unsigned sz, numeral * p, mpq const & q); /** \brief p(x) := a^n * p(x/a) */ void compose_an_p_x_div_a(unsigned sz, numeral * p, numeral const & a); /** \brief p(x) := p(-x) */ void p_minus_x(unsigned sz, numeral * p); /** \brief p(x) := x^n * p(1/x) */ void p_1_div_x(unsigned sz, numeral * p); /** \brief Evaluate the sign of p(b) */ polynomial::sign eval_sign_at(unsigned sz, numeral const * p, mpbq const & b); /** \brief Evaluate the sign of p(b) */ polynomial::sign eval_sign_at(unsigned sz, numeral const * p, mpq const & b); /** \brief Evaluate the sign of p(b) */ polynomial::sign eval_sign_at(unsigned sz, numeral const * p, mpz const & b); /** \brief Evaluate the sign of p(0) */ polynomial::sign eval_sign_at_zero(unsigned sz, numeral const * p); /** \brief Evaluate the sign of p(+oo) */ polynomial::sign eval_sign_at_plus_inf(unsigned sz, numeral const * p); /** \brief Evaluate the sign of p(-oo) */ polynomial::sign eval_sign_at_minus_inf(unsigned sz, numeral const * p); /** \brief Evaluate the sign variations in the polynomial sequence at -oo */ unsigned sign_variations_at_minus_inf(upolynomial_sequence const & seq); /** \brief Evaluate the sign variations in the polynomial sequence at +oo */ unsigned sign_variations_at_plus_inf(upolynomial_sequence const & seq); /** \brief Evaluate the sign variations in the polynomial sequence at 0 */ unsigned sign_variations_at_zero(upolynomial_sequence const & seq); /** \brief Evaluate the sign variations in the polynomial sequence at b */ unsigned sign_variations_at(upolynomial_sequence const & seq, mpbq const & b); /** \brief Return an upper bound U for all roots of p. U is a positive value. We have that if u is a root of p, then |u| < U */ void root_upper_bound(unsigned sz, numeral const * p, numeral & U); unsigned knuth_positive_root_upper_bound(unsigned sz, numeral const * p); unsigned knuth_negative_root_upper_bound(unsigned sz, numeral const * p); /** \brief Return k s.t. for any nonzero root alpha of p(x): |alpha| > 1/2^k */ unsigned nonzero_root_lower_bound(unsigned sz, numeral const * p); /** \brief Isolate roots of a square free polynomial p. The result is stored in three vectors: roots, lowers and uppers. The vector roots contains actual roots of p. The vectors lowers and uppers have the same size, and For all i in [0, lowers.size()), we have that there is only and only one root of p in the interval (lowers[i], uppers[i]). Every root of p in roots or in an interval (lowers[i], uppers[i]) The total number of roots of p is roots.size() + lowers.size() \pre p is not the zero polynomial, that is, sz > 0 */ void sqf_isolate_roots(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers); /** \brief Isolate roots of an arbitrary polynomial p. \see sqf_isolate_roots. \pre p is not the zero polynomial, that is, sz > 0 */ void isolate_roots(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers); void drs_isolate_roots(unsigned sz, numeral * p, unsigned neg_k, unsigned pos_k, mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers); void sturm_isolate_roots_core(unsigned sz, numeral * p, unsigned neg_k, unsigned pos_k, mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers); void sturm_isolate_roots(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers); /** \brief Compute the sturm sequence for p1 and p2. */ void sturm_seq(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, upolynomial_sequence & seq); /** \brief Compute the sturm sequence for p and p'. */ void sturm_seq(unsigned sz, numeral const * p, upolynomial_sequence & seq); /** \brief Compute the sturm tarski sequence for p1 and p1'*p2. */ void sturm_tarski_seq(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, upolynomial_sequence & seq); /** \brief Compute the Fourier sequence for p. */ void fourier_seq(unsigned sz, numeral const * p, upolynomial_sequence & seq); /** \brief Convert an isolating interval into a refinable one. See comments in upolynomial.cpp. */ bool isolating2refinable(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq & a, mpbq & b); // // Interval refinement procedures // They all assume p is square free and (a, b) is a refinable isolating interval. // // Return TRUE, if interval was squeezed, and new interval is stored in (a,b). // Return FALSE, if the actual root was found, it is stored in a. // // See upolynomial.cpp for additional comments bool refine_core(unsigned sz, numeral const * p, polynomial::sign sign_a, mpbq_manager & bqm, mpbq & a, mpbq & b); bool refine(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq & a, mpbq & b); bool refine_core(unsigned sz, numeral const * p, polynomial::sign sign_a, mpbq_manager & bqm, mpbq & a, mpbq & b, unsigned prec_k); bool refine(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq & a, mpbq & b, unsigned prec_k); ///////////////////// /** \brief Convert a isolating (refinable) rational interval into a isolating refinable binary rational interval. Return TRUE, if interval was found and the result is stored in (c, d). Return FALSE, if the actual root was found, it is stored in c. */ bool convert_q2bq_interval(unsigned sz, numeral const * p, mpq const & a, mpq const & b, mpbq_manager & bqm, mpbq & c, mpbq & d); /** \brief Given a polynomial p, and a lower bound l. Return the root id i. That is, the first root u > l is the i-th root of p. */ unsigned get_root_id(unsigned sz, numeral const * p, mpbq const & l); /** \brief Make sure that isolating interval (a, b) for p does not contain zero. Return TRUE, if updated (a, b) does not contain zero. Return FALSE, if zero is a root of p */ bool normalize_interval_core(unsigned sz, numeral const * p, int sign_a, mpbq_manager & m, mpbq & a, mpbq & b); /** \brief Similar to normalize_interval_core, but sign_a does not need to be provided. */ bool normalize_interval(unsigned sz, numeral const * p, mpbq_manager & m, mpbq & a, mpbq & b); /** \brief Return true if all irreducible factors were found. That is, if the result if false, there is no guarantee that the factors in r are irreducible. This can happen when limits (e.g., on the search space size) are set in params. */ bool factor(unsigned sz, numeral const * p, factors & r, factor_params const & params = factor_params()); bool factor(numeral_vector const & p, factors & r, factor_params const & params = factor_params()) { return factor(p.size(), p.c_ptr(), r, params); } std::ostream& display(std::ostream & out, unsigned sz, numeral const * p, char const * var_name = "x", bool use_star = false) const { return core_manager::display(out, sz, p, var_name); } std::ostream& display(std::ostream & out, numeral_vector const & p, char const * var_name = "x") const { return core_manager::display(out, p, var_name); } std::ostream& display(std::ostream & out, upolynomial_sequence const & seq, char const * var_name = "x") const; }; }; #endif z3-z3-4.8.7/src/math/polynomial/upolynomial_factorization.cpp000066400000000000000000001453311356505360400243510ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: polynomial_factorization.cpp Abstract: Implementation of polynomial factorization. Author: Dejan (t-dejanj) 2011-11-15 Notes: [1] Elwyn Ralph Berlekamp. Factoring Polynomials over Finite Fields. Bell System Technical Journal, 46(8-10):1853-1859, 1967. [2] Donald Ervin Knuth. The Art of Computer Programming, volume 2: Seminumerical Algorithms. Addison Wesley, third edition, 1997. [3] Henri Cohen. A Course in Computational Algebraic Number Theory. Springer Verlag, 1993. --*/ #include "util/trace.h" #include "util/util.h" #include "math/polynomial/upolynomial_factorization_int.h" #include "util/prime_generator.h" using namespace std; namespace upolynomial { // get the prime as unsigned while throwing exceptions if anything goes bad` unsigned get_p_from_manager(zp_numeral_manager const & zp_nm) { z_numeral_manager & nm = zp_nm.m(); numeral const & p = zp_nm.p(); if (!nm.is_uint64(p)) { throw upolynomial_exception("The prime number attempted in factorization is too big!"); } uint64_t p_uint64 = nm.get_uint64(p); unsigned p_uint = static_cast(p_uint64); if (((uint64_t)p_uint) != p_uint64) { throw upolynomial_exception("The prime number attempted in factorization is too big!"); } return p_uint; } /** \brief The Q-I matrix from Berelkamp's algorithm [1,2]. Given a polynomial f = \sum f_k x^k of degree n, with f_i in Z_p[x], the i-th row of Q is a representation of x^(p*i) modulo f, i.e. x^(p*i) modulo f = \sum_j q[i][j] x^j If f is of degree n, the matrix is square nxn. When the this matrix is constructed we obtain Q - I, because this is what we need in the algorithm. After construction, the null space vectors can be extracted one-by one using the next_null_space_vector method. */ class berlekamp_matrix { zp_manager & m_upm; mpzzp_manager & m_zpm; svector m_matrix; unsigned m_size; unsigned m_null_row; // 0, ..., m_size - 1, state for null vectors svector m_column_pivot; // position of pivots in the columns svector m_row_pivot; // position of pivots in the rows mpz & get(unsigned i, unsigned j) { SASSERT(i < m_size && j < m_size); return m_matrix[i*m_size + j]; } mpz const & get(unsigned i, unsigned j) const { SASSERT(i < m_size && j < m_size); return m_matrix[i*m_size + j]; } public: /** \brief Construct the matrix as explained above, f should be in the Z_p. */ berlekamp_matrix(zp_manager & upm, numeral_vector const & f) : m_upm(upm), m_zpm(upm.m()), m_size(m_upm.degree(f)), m_null_row(1), m_column_pivot(m_size, -1), m_row_pivot(m_size, -1) { unsigned p = get_p_from_manager(m_zpm); TRACE("polynomial::factorization::bughunt", tout << "polynomial::berlekamp_matrix("; m_upm.display(tout, f); tout << ", " << p << ")" << endl;); // the first row is always the vector [1, 0, ..., 0], since x^0 = 0 (modulo f) m_matrix.push_back(1); for (unsigned j = 0; j < m_size; ++ j) { m_matrix.push_back(0); } // the rest of the rows, we can get as follows, given f = x^n + f_{n-1} x^{n-1} + ... + f_1 x + f_0 // if x^k = \sum a_{k,j} x^j (modulo p), hence 0 <= j <= n-1 then // x^{k+1} = a_{k,n-1}(-f_{n-1} x^{n-1} + ... + f_0) + a_{k,n-2} x^{n-1} + ... + a_{k, 0} x // so we can compute a_{k+1,j} = a_{k, j-1} - a_{k,n-1}*f_j // every p-th row we add to the matrix scoped_numeral tmp(m_zpm); unsigned row = 0, previous_row = 0; for (unsigned k = 1; true; previous_row = row, ++ k) { // add another row if we need it if (k % p == 1) { if (++ row >= m_size) { break; } for (unsigned j = 0; j < m_size; ++ j) { m_matrix.push_back(0); } } // the multiplier m_zpm.set(tmp, get(previous_row, m_size - 1)); // go down the row and shift it for (unsigned j = m_size - 1; j > 0; -- j) { m_zpm.submul(get(previous_row, j-1), tmp, f[j], get(row, j)); } // add the 0 element (same formula with a_{k,-1} = 0) m_zpm.mul(f[0], tmp, get(row, 0)); m_zpm.neg(get(row, 0)); } // do Q - I for (unsigned i = 0; i < m_size; ++ i) { m_zpm.dec(get(i, i)); } TRACE("polynomial::factorization::bughunt", tout << "polynomial::berlekamp_matrix():" << endl; display(tout); tout << endl;); } /** \brief Destructor, just removing the numbers */ ~berlekamp_matrix() { for (unsigned k = 0; k < m_matrix.size(); ++ k) { m_zpm.del(m_matrix[k]); } } /** \brief 'Diagonalizes' the matrix using only column operations. The resulting matrix will have -1 at pivot elements. Returns the rank of the null space. */ unsigned diagonalize() { scoped_numeral multiplier(m_zpm); unsigned null_rank = 0; for (unsigned i = 0; i < m_size; ++ i) { // get the first non-zero entry in the m_null_row row bool column_found = false; for (unsigned j = 0; j < m_size; ++ j) { if (m_column_pivot[j] < 0 && !m_zpm.is_zero(get(i, j))) { column_found = true; m_column_pivot[j] = i; m_row_pivot[i] = j; // found a pivot, to make it -1 we compute the multiplier -p^-1 m_zpm.set(multiplier, get(i, j)); m_zpm.inv(multiplier); m_zpm.neg(multiplier); // multiply the pivot column with the multiplier for (unsigned k = m_null_row; k < m_size; ++ k) { m_zpm.mul(get(k, j), multiplier, get(k, j)); } // pivot is -1 so we can add it to the rest of the columns to eliminate the row for (unsigned other_j = 0; other_j < m_size; ++ other_j) { if (j != other_j) { m_zpm.set(multiplier, get(i, other_j)); for (unsigned k = m_null_row; k < m_size; ++ k) { m_zpm.addmul(get(k, other_j), multiplier, get(k, j), get(k, other_j)); } } } } } if (!column_found) { null_rank ++; } } TRACE("polynomial::factorization::bughunt", tout << "polynomial::diagonalize():" << endl; display(tout); tout << endl;); return null_rank; } /** If rank of the matrix is n - r, we are interested in linearly independent vectors v_1, ..., v_r (the basis of the null space), such that v_k A = 0. This method will give one at a time. The method returns true if vector has been computed properly. The first vector [1, 0, ..., 0] is ignored (m_null_row starts from 1). */ bool next_null_space_vector(numeral_vector & v) { SASSERT(v.size() <= m_size); v.resize(m_size); for (; m_null_row < m_size; ++ m_null_row) { if (m_row_pivot[m_null_row] < 0) { // output the vector for (unsigned j = 0; j < m_size; ++ j) { if (m_row_pivot[j] >= 0) { m_zpm.set(v[j], get(m_null_row, m_row_pivot[j])); } else { if (j == m_null_row) { m_zpm.set(v[j], 1); } else { m_zpm.set(v[j], 0); } } } ++ m_null_row; return true; } } // didn't find the vector return false; } /** \brief Display the matrix on the output stream. */ void display(std::ostream & out) const { for (unsigned i = 0; i < m_matrix.size() / m_size; ++ i) { for (unsigned j = 0; j < m_size; ++ j) { out << m_zpm.to_string(get(i, j)) << "\t"; } out << endl; } } }; /** See [3] p. 125. */ void zp_square_free_factor(zp_manager & upm, numeral_vector const & f, zp_factors & sq_free_factors) { zp_numeral_manager & nm = upm.m(); unsigned p = get_p_from_manager(upm.m()); TRACE("polynomial::factorization", tout << "polynomial::square_free_factor("; upm.display(tout, f); tout << ") over Z_" << p << endl;); scoped_numeral_vector div_tmp(nm); // [initialize] T_0 = f, e = 1 // trim and get the make it monic if not already SASSERT(f.size() > 1); scoped_numeral_vector T_0(nm); upm.set(f.size(), f.c_ptr(), T_0); scoped_numeral constant(nm); upm.mk_monic(T_0.size(), T_0.c_ptr(), constant); sq_free_factors.set_constant(constant); TRACE("polynomial::factorization::bughunt", tout << "Initial factors: " << sq_free_factors << endl; tout << "R. = GF(" << p << ")['x']" << endl; tout << "T_0 = "; upm.display(tout, T_0); tout << endl; ); unsigned e = 1; // we repeat until we get a constant scoped_numeral_vector T_0_d(nm); scoped_numeral_vector T(nm); scoped_numeral_vector V(nm); scoped_numeral_vector W(nm); scoped_numeral_vector A_ek(nm); while (T_0.size() > 1) { // [initialize e-loop] T = gcd(T_0, T_0'), V / T_0/T, k = 0 unsigned k = 0; TRACE("polynomial::factorization::bughunt", tout << "k = 0" << endl;); // T_0_d = T_0' upm.derivative(T_0.size(), T_0.c_ptr(), T_0_d); TRACE("polynomial::factorization::bughunt", tout << "T_0_d = T_0.derivative(x)" << endl; tout << "T_0_d == "; upm.display(tout, T_0_d); tout << endl; ); // T = gcd(T_0, T_0') upm.gcd(T_0.size(), T_0.c_ptr(), T_0_d.size(), T_0_d.c_ptr(), T); TRACE("polynomial::factorization::bughunt", tout << "T = T_0.gcd(T_0_d)" << endl; tout << "T == "; upm.display(tout, T); tout << endl; ); // V = T_0 / T upm.div(T_0.size(), T_0.c_ptr(), T.size(), T.c_ptr(), V); TRACE("polynomial::factorization::bughunt", tout << "V = T_0.quo_rem(T)[0]" << endl; tout << "V == "; upm.display(tout, V); tout << endl; ); while (V.size() > 1) { // [special case] if ((++k) % p == 0) { ++ k; // T = T/V upm.div(T.size(), T.c_ptr(), V.size(), V.c_ptr(), T); TRACE("polynomial::factorization::bughunt", tout << "T = T.quo_rem(V)[0]" << endl; tout << "T == "; upm.display(tout, T); tout << endl; ); } // [compute A_ek] // W = gcd(T, V) upm.gcd(T.size(), T.c_ptr(), V.size(), V.c_ptr(), W); TRACE("polynomial::factorization::bughunt", tout << "W = T.gcd(V)" << endl; upm.display(tout, W); tout << endl; ); // A_ek = V/W upm.div(V.size(), V.c_ptr(), W.size(), W.c_ptr(), A_ek); TRACE("polynomial::factorization::bughunt", tout << "A_ek = V.quo_rem(W)[0]" << endl; tout << "A_ek == "; upm.display(tout, A_ek); tout << endl; ); // V = W V.swap(W); TRACE("polynomial::factorization::bughunt", tout << "V = W" << endl; tout << "V == "; upm.display(tout, V); tout << endl; ); // T = T/V upm.div(T.size(), T.c_ptr(), V.size(), V.c_ptr(), T); TRACE("polynomial::factorization::bughunt", tout << "T = T.quo_rem(V)[0]" << endl; tout << "T == "; upm.display(tout, T); tout << endl; ); // if not constant, we output it if (A_ek.size() > 1) { TRACE("polynomial::factorization::bughunt", tout << "Factor: ("; upm.display(tout, A_ek); tout << ")^" << e*k << endl;); sq_free_factors.push_back(A_ek, e*k); } } // [finished e-loop] T_0 = \sum_{p div j} t_j x^j, set T_0 \sum_{p div j} t_j x^{j/p}, e = pe e *= p; T_0.reset(); for (unsigned deg_p = 0; deg_p < T.size(); deg_p += p) { T_0.push_back(numeral()); nm.set(T_0.back(), T[deg_p]); } TRACE("polynomial::factorization::bughunt", tout << "T_0 = "; upm.display(tout, T_0); tout << endl; ); } TRACE("polynomial::factorization", tout << "polynomial::square_free_factor("; upm.display(tout, f); tout << ") => " << sq_free_factors << endl;); } bool zp_factor(zp_manager & upm, numeral_vector const & f, zp_factors & factors) { TRACE("polynomial::factorization", unsigned p = get_p_from_manager(upm.m()); tout << "polynomial::factor("; upm.display(tout, f); tout << ") over Z_" << p << endl;); // get the sq-free parts (all of them will be monic) zp_factors sq_free_factors(upm); zp_square_free_factor(upm, f, sq_free_factors); // factor the sq-free parts individually for (unsigned i = 0; i < sq_free_factors.distinct_factors(); ++ i) { unsigned j = factors.distinct_factors(); if (upm.degree(sq_free_factors[i]) > 1) { zp_factor_square_free(upm, sq_free_factors[i], factors); // monic from aq-free decomposition for (; j < factors.distinct_factors(); ++ j) { factors.set_degree(j, sq_free_factors.get_degree(i)*factors.get_degree(j)); } } else { factors.push_back(sq_free_factors[i], sq_free_factors.get_degree(i)); } } // add the constant factors.set_constant(sq_free_factors.get_constant()); TRACE("polynomial::factorization", tout << "polynomial::factor("; upm.display(tout, f); tout << ") => " << factors << endl;); return factors.total_factors() > 1; } bool zp_factor_square_free(zp_manager & upm, numeral_vector const & f, zp_factors & factors) { return zp_factor_square_free_berlekamp(upm, f, factors, false); } bool zp_factor_square_free_berlekamp(zp_manager & upm, numeral_vector const & f, zp_factors & factors, bool randomized) { SASSERT(upm.degree(f) > 1); mpzzp_manager & zpm = upm.m(); unsigned p = get_p_from_manager(zpm); TRACE("polynomial::factorization", tout << "upolynomial::factor_square_free_berlekamp("; upm.display(tout, f); tout << ", " << p << ")" << endl;); SASSERT(zpm.is_one(f.back())); // construct the berlekamp Q matrix to get the null space berlekamp_matrix Q_I(upm, f); // copy the initial polynomial to factors unsigned first_factor = factors.distinct_factors(); factors.push_back(f, 1); // rank of the null-space (and the number of factors) unsigned r = Q_I.diagonalize(); if (r == 1) { // since r == 1 == number of factors, then f is irreducible TRACE("polynomial::factorization", tout << "upolynomial::factor_square_free_berlekamp("; upm.display(tout, f); tout << ", " << p << ") => " << factors << endl;); return false; } TRACE("polynomial::factorization::bughunt", tout << "upolynomial::factor_square_free_berlekamp(): computing factors, expecting " << r << endl;); scoped_numeral_vector gcd(zpm); scoped_numeral_vector div(zpm); // process the null space vectors (skip first one, it's [1, 0, ..., 0]) while generating the factors unsigned d = upm.degree(f); (void)d; scoped_numeral_vector v_k(zpm); while (Q_I.next_null_space_vector(v_k)) { TRACE("polynomial::factorization::bughunt", tout << "null vector: "; for(unsigned j = 0; j < d; ++ j) { tout << zpm.to_string(v_k[j]) << " "; } tout << endl; ); upm.trim(v_k); // TRACE("polynomial::factorization", tout << "v_k = "; upm.display(tout, v_k); tout << endl;); unsigned current_factor_end = factors.distinct_factors(); for (unsigned current_factor_i = first_factor; current_factor_i < current_factor_end; ++ current_factor_i) { // we have v such that vQ = v, viewing v as a polynomial, we get that v^n - v = 0 (mod f) // since v^n -v = v*(v-1)*...*(v - p-1) we compute the gcd(v - s, f) to extract the // factors. it also holds that g = \prod gcd(v - s, f), so we just accumulate them // if it's of degree 1, we're done (have to index the array as we are adding to it), as if (factors[current_factor_i].size() == 2) { continue; } for (unsigned s = 0; s < p; ++ s) { numeral_vector const & current_factor = factors[current_factor_i]; // we just take one off v_k each time to get all of them zpm.dec(v_k[0]); // get the gcd upm.gcd(v_k.size(), v_k.c_ptr(), current_factor.size(), current_factor.c_ptr(), gcd); // if the gcd is 1, or the gcd is f, we just ignore it if (gcd.size() != 1 && gcd.size() != current_factor.size()) { // get the divisor also (no need to normalize the div, both are monic) upm.div(current_factor.size(), current_factor.c_ptr(), gcd.size(), gcd.c_ptr(), div); TRACE("polynomial::factorization::bughunt", tout << "current_factor = "; upm.display(tout, current_factor); tout << endl; tout << "gcd_norm = "; upm.display(tout, gcd); tout << endl; tout << "div = "; upm.display(tout, div); tout << endl; ); // continue with the rest factors.swap_factor(current_factor_i, div); // add the new factor(s) factors.push_back(gcd, 1); } // at the point where we have all the factors, we are done if (factors.distinct_factors() - first_factor == r) { TRACE("polynomial::factorization", tout << "polynomial::factor("; upm.display(tout, f); tout << ", " << p << ") => " << factors << " of degree " << factors.get_degree() << endl;); return true; } } } } // should never get here SASSERT(false); return true; } /** Check if the hensel lifting was correct, i.e. that C = A*B (mod br). */ bool check_hansel_lift(z_manager & upm, numeral_vector const & C, numeral const & a, numeral const & b, numeral const & r, numeral_vector const & A, numeral_vector const & B, numeral_vector const & A_lifted, numeral_vector const & B_lifted) { z_numeral_manager & nm = upm.zm(); scoped_mpz br(nm); nm.mul(b, r, br); zp_manager br_upm(upm.lim(), upm.zm()); br_upm.set_zp(br); if (A_lifted.size() != A.size()) return false; if (B_lifted.size() != B.size()) return false; if (!nm.eq(A.back(), A_lifted.back())) return false; // test1: check that C = A_lifted * B_lifted (mod b*r) scoped_mpz_vector test1(nm); 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.empty()) { TRACE("polynomial::factorization::bughunt", tout << "sage: R. = ZZ['x']" << endl; tout << "sage: A = "; upm.display(tout, A); tout << endl; tout << "sage: B = "; upm.display(tout, B); tout << endl; tout << "sage: C = "; upm.display(tout, C); tout << endl; tout << "sage: test1 = C - AB" << endl; tout << "sage: print test1.change_ring(GF(" << nm.to_string(br) << "))" << endl; tout << "sage: print 'expected 0'" << endl; ); return false; } zp_manager b_upm(upm.lim(), nm); b_upm.set_zp(b); // test2: A_lifted = A (mod b) scoped_mpz_vector test2a(nm), test2b(nm); to_zp_manager(b_upm, A, test2a); to_zp_manager(b_upm, A_lifted, test2b); if (!upm.eq(test2a, test2b)) { return false; } // test3: B_lifted = B (mod b) scoped_mpz_vector test3a(nm), test3b(nm); to_zp_manager(b_upm, B, test3a); to_zp_manager(b_upm, B_lifted, test3b); if (!upm.eq(test3a, test3b)) { return false; } return true; } /** Performs a Hensel lift of A and B in Z_a to Z_b, where p is prime and a = p^{a_k}, b = p^{b_k}, r = (a, b), with the following assumptions: (1) UA + VB = 1 (mod a) (2) C = A*B (mod b) (3) (l(A), r) = 1 (important in order to divide by A, i.e. to invert l(A)) (4) deg(A) + deg(B) = deg(C) The output of is two polynomials A1, B1 such that A1 = A (mod b), B1 = B (mod b), l(A1) = l(A), deg(A1) = deg(A), deg(B1) = deg(B) and C = A1 B1 (mod b*r). Such A1, B1 are unique if r is prime. See [3] p. 138. */ void hensel_lift(z_manager & upm, numeral const & a, numeral const & b, numeral const & r, numeral_vector const & U, numeral_vector const & A, numeral_vector const & V, numeral_vector const & B, numeral_vector const & C, numeral_vector & A_lifted, numeral_vector & B_lifted) { z_numeral_manager & nm = upm.zm(); TRACE("polynomial::factorization::bughunt", tout << "polynomial::hensel_lift("; tout << "a = " << nm.to_string(a) << ", "; tout << "b = " << nm.to_string(b) << ", "; tout << "r = " << nm.to_string(r) << ", "; tout << "U = "; upm.display(tout, U); tout << ", "; tout << "A = "; upm.display(tout, A); tout << ", "; tout << "V = "; upm.display(tout, V); tout << ", "; tout << "B = "; upm.display(tout, B); tout << ", "; tout << "C = "; upm.display(tout, C); tout << ")" << endl; ); zp_manager r_upm(upm.lim(), nm); r_upm.set_zp(r); SASSERT(upm.degree(C) == upm.degree(A) + upm.degree(B)); SASSERT(upm.degree(U) < upm.degree(B) && upm.degree(V) < upm.degree(A)); // by (2) C = AB (mod b), hence (C - AB) is divisible by b // define thus let f = (C - AB)/b in Z_r scoped_numeral_vector f(upm.m()); upm.mul(A.size(), A.c_ptr(), B.size(), B.c_ptr(), f); upm.sub(C.size(), C.c_ptr(), f.size(), f.c_ptr(), f); upm.div(f, b); to_zp_manager(r_upm, f); TRACE("polynomial::factorization", tout << "f = "; upm.display(tout, f); tout << endl; ); // we need to get A1 = A (mod b), B1 = B (mode b) so we know that we need // A1 = A + b*S, B1 = B + b*T in Z[x] for some S and T with deg(S) <= deg(A), deg(T) <= deg(B) // we also need (mod b*r) C = A1*B1 = (A + b*S)*(B + b*T) = AB + b(AT + BS) + b^2*ST // if we find S and T, we will have found our A1 and B1 // since r divides b, then b^2 contains b*r and we know that it must be that C = AB + b(AT + BS) (mod b*r) // which is equivalent to // (5) f = (C - AB)/b = AT + BS (mod r) // having (1) AU + BV = 1 (mod r) and (5) AT + BS = f (mod r), we know that // A*(fU) + B*(fV) = f (mod r), i.e. T = fU, S = fV is a solution // but we also know that we need an S with deg(S) <= deg(A) so we can do the following // we know that l(A) is invertible so we can find the exact remainder of fV with A, i.e. find the quotient // t in the division and set // A*(fU + tB) + B*(fV - tA) = f // T = fU + tB, S = fU - tA // since l(A) is invertible in Z_r, we can (in Z_r) use exact division to get Vf = At + R with deg(R) < A // we now know that deg(A+bS) = deg(A), but we also know (4) which will guarantee that deg(B+bT) = deg(B) // compute the S, T (compute in Z_r[x]) scoped_numeral_vector Vf(r_upm.m()), t(r_upm.m()), S(r_upm.m()); TRACE("polynomial::factorization::bughunt", tout << "V == "; upm.display(tout, V); tout << endl; ); r_upm.mul(V.size(), V.c_ptr(), f.size(), f.c_ptr(), Vf); TRACE("polynomial::factorization::bughunt", tout << "Vf = V*f" << endl; tout << "Vf == "; upm.display(tout, Vf); tout << endl; ); r_upm.div_rem(Vf.size(), Vf.c_ptr(), A.size(), A.c_ptr(), t, S); TRACE("polynomial::factorization::bughunt", tout << "[t, S] = Vf.quo_rem(A)" << endl; tout << "t == "; upm.display(tout, t); tout << endl; tout << "S == "; upm.display(tout, S); tout << endl; ); scoped_numeral_vector T(r_upm.m()), tmp(r_upm.m()); r_upm.mul(U.size(), U.c_ptr(), f.size(), f.c_ptr(), T); // T = fU TRACE("polynomial::factorization::bughunt", tout << "T == U*f" << endl; tout << "T == "; upm.display(tout, T); tout << endl; ); r_upm.mul(B.size(), B.c_ptr(), t.size(), t.c_ptr(), tmp); // tmp = Bt TRACE("polynomial::factorization::bughunt", tout << "tmp = B*t" << endl; tout << "tmp == "; upm.display(tout, tmp); tout << endl; ); r_upm.add(T.size(), T.c_ptr(), tmp.size(), tmp.c_ptr(), T); // T = Uf + Bt TRACE("polynomial::factorization::bughunt", tout << "T = B*tmp" << endl; tout << "T == "; upm.display(tout, T); tout << endl; ); // set the result, A1 = A + b*S, B1 = B + b*T (now we compute in Z[x]) upm.mul(S, b); upm.mul(T, b); upm.add(A.size(), A.c_ptr(), S.size(), S.c_ptr(), A_lifted); upm.add(B.size(), B.c_ptr(), T.size(), T.c_ptr(), B_lifted); CASSERT("polynomial::factorizatio::bughunt", check_hansel_lift(upm, C, a, b, r, A, B, A_lifted, B_lifted)); } bool check_quadratic_hensel(zp_manager & zpe_upm, numeral_vector const & U, numeral_vector const & A, numeral_vector const & V, numeral_vector const & B) { z_numeral_manager & nm = zpe_upm.zm(); // compute UA+BV expecting to get 1 (in Z_pe[x]) scoped_mpz_vector tmp1(nm); scoped_mpz_vector tmp2(nm); zpe_upm.mul(U.size(), U.c_ptr(), A.size(), A.c_ptr(), tmp1); zpe_upm.mul(V.size(), V.c_ptr(), B.size(), B.c_ptr(), tmp2); scoped_mpz_vector one(nm); zpe_upm.add(tmp1.size(), tmp1.c_ptr(), tmp2.size(), tmp2.c_ptr(), one); if (one.size() != 1 || !nm.is_one(one[0])) { TRACE("polynomial::factorization::bughunt", tout << "sage: R. = Zmod(" << nm.to_string(zpe_upm.m().p()) << ")['x']" << endl; tout << "sage: A = "; zpe_upm.display(tout, A); tout << endl; tout << "sage: B = "; zpe_upm.display(tout, B); tout << endl; tout << "sage: U = "; zpe_upm.display(tout, U); tout << endl; tout << "sage: V = "; zpe_upm.display(tout, V); tout << endl; tout << "sage: print (1 - UA - VB)" << endl; tout << "sage: print 'expected 0'" << endl; ); return false; } return true; } /** Lift C = A*B from Z_p[x] to Z_{p^e}[x] such that: * A = A_lift (mod p) * B = B_lift (mod p) * C = A*B (mod p^e) */ void hensel_lift_quadratic(z_manager& upm, numeral_vector const & C, zp_manager & zpe_upm, numeral_vector & A, numeral_vector & B, unsigned e) { z_numeral_manager & nm = upm.zm(); TRACE("polynomial::factorization::bughunt", tout << "polynomial::hansel_lift_quadratic("; tout << "A = "; upm.display(tout, A); tout << ", "; tout << "B = "; upm.display(tout, B); tout << ", "; tout << "C = "; upm.display(tout, C); tout << ", "; tout << "p = " << nm.to_string(zpe_upm.m().p()) << ", e = " << e << ")" << endl; ); // we create a new Z_p manager, since we'll be changing the input one zp_manager zp_upm(upm.lim(), nm); zp_upm.set_zp(zpe_upm.m().p()); // get the U, V, such that A*U + B*V = 1 (mod p) scoped_mpz_vector U(nm), V(nm), D(nm); zp_upm.ext_gcd(A.size(), A.c_ptr(), B.size(), B.c_ptr(), U, V, D); SASSERT(D.size() == 1 && zp_upm.m().is_one(D[0])); // we start lifting from (a = p, b = p, r = p) scoped_mpz_vector A_lifted(nm), B_lifted(nm); for (unsigned k = 1; k < e; k *= 2) { upm.checkpoint(); // INVARIANT(a = p^e, b = p^e, r = gcd(a, b) = p^e): // C = AB (mod b), UA + VB = 1 (mod a) // deg(U) < deg(B), dev(V) < deg(A), deg(C) = deg(A) + deg(V) // gcd(l(A), r) = 1 // regular hensel lifting from a to b*r, here from pe -> pk*pk = p^{k*k} numeral const & pe = zpe_upm.m().p(); hensel_lift(upm, pe, pe, pe, U, A, V, B, C, A_lifted, B_lifted); // now we have C = AB (mod b*r) TRACE("polynomial::factorization::bughunt", tout << "A_lifted = "; upm.display(tout, A_lifted); tout << endl; tout << "B_lifted = "; upm.display(tout, B_lifted); tout << endl; tout << "C = "; upm.display(tout, C); tout << endl; ); // we employ similar reasoning as in the regular hansel lemma now // we need to lift UA + VB = 1 (mod a) // since after the lift, we still have UA + VB = 1 (mod a) we know that (1 - UA - VB)/a is in Z[x] // so we can compute g = (1 - UA - VB)/a // we need U1 and V1 such that U1A + V1B = 1 (mod a^2), with U1 = U mod a, V1 = V mod a // hence U1 = U + aS, V1 = V + aT and we need // (U + aS)A + (V + aT)B = 1 (mod a^2) same as // UA + VB + a(SA + TB) = 1 (mod a^2) same as // SA + TB = g (mod a) hence // (gU + tB)A + (gV - tA)B = g (mod a) will be a solution and we pick t such that deg(gV - tA) < deg(A) // compute g scoped_mpz_vector tmp1(nm), g(nm); g.push_back(numeral()); nm.set(g.back(), 1); // g = 1 upm.mul(A_lifted.size(), A_lifted.c_ptr(), U.size(), U.c_ptr(), tmp1); // tmp1 = AU upm.sub(g.size(), g.c_ptr(), tmp1.size(), tmp1.c_ptr(), g); // g = 1 - UA upm.mul(B_lifted.size(), B_lifted.c_ptr(), V.size(), V.c_ptr(), tmp1); // tmp1 = BV upm.sub(g.size(), g.c_ptr(), tmp1.size(), tmp1.c_ptr(), g); // g = 1 - UA - VB upm.div(g, pe); to_zp_manager(zpe_upm, g); TRACE("polynomial::factorization::bughunt", tout << "g = (1 - A_lifted*U - B_lifted*V)/" << nm.to_string(pe) << endl; tout << "g == "; upm.display(tout, g); tout << endl; ); // compute the S, T scoped_mpz_vector S(nm), T(nm), t(nm), tmp2(nm); zpe_upm.mul(g.size(), g.c_ptr(), V.size(), V.c_ptr(), tmp1); // tmp1 = gV zpe_upm.div_rem(tmp1.size(), tmp1.c_ptr(), A.size(), A.c_ptr(), t, T); // T = gV - tA, deg(T) < deg(A) zpe_upm.mul(g.size(), g.c_ptr(), U.size(), U.c_ptr(), tmp1); // tmp1 = gU zpe_upm.mul(t.size(), t.c_ptr(), B.size(), B.c_ptr(), tmp2); // tmp2 = tB zpe_upm.add(tmp1.size(), tmp1.c_ptr(), tmp2.size(), tmp2.c_ptr(), S); // now update U = U + a*S and V = V + a*T upm.mul(S.size(), S.c_ptr(), pe); upm.mul(T.size(), T.c_ptr(), pe); upm.add(U.size(), U.c_ptr(), S.size(), S.c_ptr(), U); upm.add(V.size(), V.c_ptr(), T.size(), T.c_ptr(), V); // deg(V) < deg(A), deg(T) < deg(A) => deg(V') < deg(A) // we go quadratic zpe_upm.m().set_p_sq(); to_zp_manager(zpe_upm, U); to_zp_manager(zpe_upm, V); to_zp_manager(zpe_upm, A_lifted); to_zp_manager(zpe_upm, B_lifted); // at this point we have INVARIANT(a = (p^e)^2, b = (p^e)^2, r = (p^e)^2) A.swap(A_lifted); B.swap(B_lifted); CASSERT("polynomial::factorizatio::bughunt", check_quadratic_hensel(zpe_upm, U, A, V, B)); } } bool check_hensel_lift(z_manager & upm, numeral_vector const & f, zp_factors const & zp_fs, zp_factors const & zpe_fs, unsigned e) { numeral_manager & nm(upm.m()); zp_manager & zp_upm = zp_fs.upm(); zp_manager & zpe_upm = zpe_fs.upm(); numeral const & p = zp_fs.nm().p(); numeral const & pe = zpe_fs.nm().p(); scoped_numeral power(nm); nm.power(p, e, power); if (!nm.ge(pe, power)) { return false; } // check f = lc(f) * zp_fs (mod p) scoped_numeral_vector mult_zp(nm), f_zp(nm); zp_fs.multiply(mult_zp); to_zp_manager(zp_upm, f, f_zp); zp_upm.mul(mult_zp, f_zp.back()); if (!upm.eq(mult_zp, f_zp)) { TRACE("polynomial::factorization::bughunt", tout << "f = "; upm.display(tout, f); tout << endl; tout << "zp_fs = " << zp_fs << endl; tout << "sage: R. = Zmod(" << nm.to_string(p) << ")['x']" << endl; tout << "sage: mult_zp = "; upm.display(tout, mult_zp); tout << endl; tout << "sage: f_zp = "; upm.display(tout, f_zp); tout << endl; tout << "sage: mult_zp == f_zp" << endl; ); return false; } // check individual factors if (zpe_fs.distinct_factors() != zp_fs.distinct_factors()) { return false; } // check f = lc(f) * zpe_fs (mod p^e) scoped_numeral_vector mult_zpe(nm), f_zpe(nm); zpe_fs.multiply(mult_zpe); to_zp_manager(zpe_upm, f, f_zpe); zpe_upm.mul(mult_zpe, f_zpe.back()); if (!upm.eq(mult_zpe, f_zpe)) { TRACE("polynomial::factorization::bughunt", tout << "f = "; upm.display(tout, f); tout << endl; tout << "zpe_fs = " << zpe_fs << endl; tout << "sage: R. = Zmod(" << nm.to_string(pe) << ")['x']" << endl; tout << "sage: mult_zpe = "; upm.display(tout, mult_zpe); tout << endl; tout << "sage: f_zpe = "; upm.display(tout, f_zpe); tout << endl; tout << "sage: mult_zpe == f_zpe" << endl; ); return false; } return true; } bool check_individual_lift(zp_manager & zp_upm, numeral_vector const & A_p, zp_manager & zpe_upm, numeral_vector const & A_pe) { scoped_numeral_vector A_pe_p(zp_upm.m()); to_zp_manager(zp_upm, A_pe, A_pe_p); if (!zp_upm.eq(A_p, A_pe_p)) { return false; } return true; } void hensel_lift(z_manager & upm, numeral_vector const & f, zp_factors const & zp_fs, unsigned e, zp_factors & zpe_fs) { SASSERT(zp_fs.total_factors() > 0); zp_numeral_manager & zp_nm = zp_fs.nm(); zp_manager & zp_upm = zp_fs.upm(); z_numeral_manager & nm = zp_nm.m(); SASSERT(nm.is_one(zp_fs.get_constant())); zp_numeral_manager & zpe_nm = zpe_fs.nm(); zp_manager & zpe_upm = zpe_fs.upm(); zpe_nm.set_zp(zp_nm.p()); TRACE("polynomial::factorization::bughunt", tout << "polynomial::hansel_lift("; upm.display(tout, f); tout << ", " << zp_fs << ")" << endl; ); // lift the factors one by one scoped_mpz_vector A(nm), B(nm), C(nm), f_parts(nm); // these will all be in Z_p // copy of f, that we'll be cutting parts of upm.set(f.size(), f.c_ptr(), f_parts); // F_k are factors mod Z_p, A_k the factors mod p^e // the invariant we keep is that: // (1) f_parts = C = lc(f) * \prod_{k = i}^n F_k, C in Z_p[x] // (2) A_k = F_k (mod p), for k < i // (3) f = (\prod_{k < i} A_k) * f_parts (mod p^e) for (int i = 0, i_end = zp_fs.distinct_factors()-1; i < i_end; ++ i) { SASSERT(zp_fs.get_degree(i) == 1); // p was chosen so that f is square-free // F_i = A (mod Z_p) zp_upm.set(zp_fs[i].size(), zp_fs[i].c_ptr(), A); TRACE("polynomial::factorization::bughunt", tout << "A = "; upm.display(tout, A); tout << endl; ); // C = f_parts (mod Z_p) if (i > 0) { to_zp_manager(zp_upm, f_parts, C); } else { // first time around, we don't have p^e yet, so first time we just compute C zp_fs.multiply(C); scoped_mpz lc(nm); zp_nm.set(lc, f.back()); zp_upm.mul(C, lc); } TRACE("polynomial::factorization::bughunt", tout << "C = "; upm.display(tout, C); tout << endl; ); // we take B to be what's left from C and A zp_upm.div(C.size(), C.c_ptr(), A.size(), A.c_ptr(), B); TRACE("polynomial::factorization::bughunt", tout << "B = "; upm.display(tout, B); tout << endl; ); // lift A and B to p^e (this will change p^e and put it zp_upm) zpe_nm.set_zp(zp_nm.p()); hensel_lift_quadratic(upm, f_parts, zpe_upm, A, B, e); CASSERT("polynomial::factorizatio::bughunt", check_individual_lift(zp_upm, zp_fs[i], zpe_upm, A)); TRACE("polynomial::factorization", tout << "lifted to " << nm.to_string(zpe_upm.m().p()) << endl; tout << "A = "; upm.display(tout, A); tout << endl; tout << "B = "; upm.display(tout, B); tout << endl; ); // if this is the first time round, we also construct f_parts (now we have correct p^e) if (i == 0) { to_zp_manager(zpe_upm, f, f_parts); } // take the lifted A out of f_parts zpe_upm.div(f_parts.size(), f_parts.c_ptr(), A.size(), A.c_ptr(), f_parts); // add the lifted factor (kills A) zpe_fs.push_back_swap(A, 1); } // we have one last factor, but it also contains lc(f), so take it out scoped_mpz lc_inv(nm); zpe_nm.set(lc_inv, f.back()); zpe_nm.inv(lc_inv); zpe_upm.mul(B, lc_inv); zpe_fs.push_back_swap(B, 1); TRACE("polynomial::factorization::bughunt", tout << "polynomial::hansel_lift("; upm.display(tout, f); tout << ", " << zp_fs << ") => " << zpe_fs << endl; ); CASSERT("polynomial::factorizatio::bughunt", check_hensel_lift(upm, f, zp_fs, zpe_fs, e)); } // get a bound on B for the factors of f with degree less or equal to deg(f)/2 // and then choose e to be smallest such that p^e > 2*lc(f)*B, we use the mignotte // // from [3, pg 134] // |p| = sqrt(\sum |p_i|^2). If a = \sum a_i x^i, b = \sum b_j, deg b = n, and b divides a, then for all j // // |b_j| <= (n-1 over j)|a| + (n-1 over j-1)|lc(a)| // // when factoring a polynomial, we find a bound B for a factor of f of degree <= deg(f)/2 // allowing both positive and negative as coefficients of b, we now want a power p^e such that all coefficients // can be represented, i.e. [-B, B] \subset [-\ceil(p^e/2), \ceil(p^e)/2], so we peek e, such that p^e >= 2B static unsigned mignotte_bound(z_manager & upm, numeral_vector const & f, numeral const & p) { numeral_manager & nm = upm.m(); SASSERT(upm.degree(f) >= 2); unsigned n = upm.degree(f)/2; // >= 1 // get the approximation for the norm of a scoped_numeral f_norm(nm); for (unsigned i = 0; i < f.size(); ++ i) { if (!nm.is_zero(f[i])) { nm.addmul(f_norm, f[i], f[i], f_norm); } } nm.root(f_norm, 2); // by above we can pick (n-1 over (n-1/2))|a| + (n-1 over (n-1)/2)lc(a) // we approximate both binomial-coefficients with 2^(n-1), so to get 2B we use 2^n(f_norm + lc(f)) scoped_numeral bound(nm); nm.set(bound, 1); nm.mul2k(bound, n, bound); scoped_numeral tmp(nm); nm.set(tmp, f.back()); nm.abs(tmp); nm.add(f_norm, tmp, f_norm); nm.mul(bound, f_norm, bound); // we need e such that p^e >= B nm.set(tmp, p); unsigned e; for (e = 1; nm.le(tmp, bound); e *= 2) { nm.mul(tmp, tmp, tmp); } return e; } /** \brief Given f from Z[x] that is square free, it factors it. This method also assumes f is primitive. */ bool factor_square_free(z_manager & upm, numeral_vector const & f, factors & fs, unsigned k, factor_params const & params) { TRACE("polynomial::factorization::bughunt", tout << "sage: f = "; upm.display(tout, f); tout << endl; tout << "sage: if (not f.is_squarefree()): print 'Error, f is not square-free'" << endl; tout << "sage: print 'Factoring :', f" << endl; tout << "sage: print 'Expected factors: ', f.factor()" << endl; ); numeral_manager & nm = upm.m(); // This method assumes f is primitive. Thus, the content of f must be one DEBUG_CODE({ scoped_numeral f_cont(nm); nm.gcd(f.size(), f.c_ptr(), f_cont); SASSERT(f.size() == 0 || nm.is_one(f_cont)); }); scoped_numeral_vector f_pp(nm); upm.set(f.size(), f.c_ptr(), f_pp); // make sure the leading coefficient is positive if (!f_pp.empty() && nm.is_neg(f_pp[f_pp.size() - 1])) { for (unsigned i = 0; i < f_pp.size(); i++) nm.neg(f_pp[i]); // flip sign constant if k is odd if (k % 2 == 1) { scoped_numeral c(nm); nm.set(c, fs.get_constant()); nm.neg(c); fs.set_constant(c); } } TRACE("polynomial::factorization::bughunt", tout << "sage: f_pp = "; upm.display(tout, f_pp); tout << endl; tout << "sage: if (not (f_pp * 1 == f): print 'Error, content computation wrong'" << endl; ); // the variables we'll be using and updating in Z_p scoped_numeral p(nm); nm.set(p, 2); zp_manager zp_upm(upm.lim(), nm.m()); zp_upm.set_zp(p); zp_factors zp_fs(zp_upm); scoped_numeral zp_fs_p(nm); nm.set(zp_fs_p, 2); // we keep all the possible sets of degrees of factors in this set factorization_degree_set degree_set(zp_upm); // we try get some number of factorizations in Z_p, for some primes // get the prime p such that // (1) (f_prim mod p) stays square-free // (2) l(f_prim) mod p doesn't vanish, i.e. we don't get a polynomial of smaller degree prime_iterator prime_it; scoped_numeral gcd_tmp(nm); unsigned trials = 0; 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(); if (next_prime > params.m_max_p) { fs.push_back(f_pp, k); return false; } nm.set(p, next_prime); zp_upm.set_zp(p); // we need gcd(lc(f_pp), p) = 1 nm.gcd(p, f_pp.back(), gcd_tmp); TRACE("polynomial::factorization::bughunt", tout << "sage: if (not (gcd(" << nm.to_string(p) << ", " << nm.to_string(f_pp.back()) << ")) == " << nm.to_string(gcd_tmp) << "): print 'Error, wrong gcd'" << endl; ); if (!nm.is_one(gcd_tmp)) { continue; } // if it's not square free, we also try something else scoped_numeral_vector f_pp_zp(nm); to_zp_manager(zp_upm, f_pp, f_pp_zp); TRACE("polynomial::factorization::bughunt", tout << "sage: Rp. = GF(" << nm.to_string(p) << ")['x_p']"; tout << endl; tout << "sage: f_pp_zp = "; zp_upm.display(tout, f_pp_zp, "x_p"); tout << endl; ); if (!zp_upm.is_square_free(f_pp_zp.size(), f_pp_zp.c_ptr())) continue; // we make it monic zp_upm.mk_monic(f_pp_zp.size(), f_pp_zp.c_ptr()); // found a candidate, factorize in Z_p and add back the constant zp_factors current_fs(zp_upm); bool factored = zp_factor_square_free(zp_upm, f_pp_zp, current_fs); if (!factored) { fs.push_back(f_pp, k); return true; } // get the factors degrees set factorization_degree_set current_degree_set(current_fs); if (degree_set.max_degree() == 0) { // first time, initialize degree_set.swap(current_degree_set); } else { degree_set.intersect(current_degree_set); } // if the degree set is trivial, we are done if (degree_set.is_trivial()) { fs.push_back(f_pp, k); return true; } // we found a candidate, lets keep it if it has less factors than the current best trials ++; if (zp_fs.distinct_factors() == 0 || zp_fs.total_factors() > current_fs.total_factors()) { zp_fs.swap(current_fs); nm.set(zp_fs_p, p); TRACE("polynomial::factorization::bughunt", tout << "best zp factorization (Z_" << nm.to_string(zp_fs_p) << "): "; tout << zp_fs << endl; tout << "best degree set: "; degree_set.display(tout); tout << endl; ); } } #ifndef _EXTERNAL_RELEASE IF_VERBOSE(FACTOR_VERBOSE_LVL, verbose_stream() << "(polynomial-factorization :at GF_" << nm.to_string(zp_upm.p()) << ")" << std::endl;); #endif // make sure to set the zp_manager back to modulo zp_fs_p zp_upm.set_zp(zp_fs_p); TRACE("polynomial::factorization::bughunt", tout << "best zp factorization (Z_" << nm.to_string(zp_fs_p) << "): " << zp_fs << endl; tout << "best degree set: "; degree_set.display(tout); tout << endl; ); // get a bound on B for the factors of f_pp with degree less or equal to deg(f)/2 // and then choose e to be smallest such that p^e > 2*lc(f)*B, we use the mignotte unsigned e = mignotte_bound(upm, f_pp, zp_fs_p); TRACE("polynomial::factorization::bughunt", tout << "out p = " << nm.to_string(zp_fs_p) << ", and we'll work p^e for e = " << e << endl; ); // we got a prime factoring, so we do the lifting now zp_manager zpe_upm(upm.lim(), nm.m()); zpe_upm.set_zp(zp_fs_p); zp_numeral_manager & zpe_nm = zpe_upm.m(); zp_factors zpe_fs(zpe_upm); // this might give something bigger than p^e, but the lifting procedure will update the zpe_nm // zp factors are monic, so will be the zpe factors, i.e. f_pp = zpe_fs * lc(f_pp) (mod p^e) hensel_lift(upm, f_pp, zp_fs, e, zpe_fs); #ifndef _EXTERNAL_RELEASE IF_VERBOSE(FACTOR_VERBOSE_LVL, verbose_stream() << "(polynomial-factorization :num-candidate-factors " << zpe_fs.distinct_factors() << ")" << std::endl;); #endif // the leading coefficient of f_pp mod p^e scoped_numeral f_pp_lc(nm); zpe_nm.set(f_pp_lc, f_pp.back()); // we always keep in f_pp the actual primitive part f_pp*lc(f_pp) upm.mul(f_pp, f_pp_lc); // now we go through the combinations of factors to check construct the factorization ufactorization_combination_iterator it(zpe_fs, degree_set); scoped_numeral_vector trial_factor(nm), trial_factor_quo(nm); scoped_numeral trial_factor_cont(nm); TRACE("polynomial::factorization::bughunt", tout << "STARTING TRIAL DIVISION" << endl; tout << "zpe_fs" << zpe_fs << endl; tout << "degree_set = "; degree_set.display(tout); tout << endl; ); bool result = true; bool remove = false; unsigned counter = 0; while (it.next(remove)) { upm.checkpoint(); counter++; if (counter > params.m_max_search_size) { // stop search result = false; break; } // // our bound ensures we can extract the right factors of degree at most 1/2 of the original // so, if out trial factor has degree bigger than 1/2, we need to take the rest of the factors // but, if we take the rest and it works, it doesn't mean that the rest is factorized, so we still take out // the original factor bool using_left = it.current_degree() <= zp_fs.get_degree()/2; if (using_left) { // do a quick check first scoped_numeral tmp(nm); it.get_left_tail_coeff(f_pp_lc, tmp); if (!nm.divides(tmp, f_pp[0])) { // don't remove this combination remove = false; continue; } it.left(trial_factor); } else { // do a quick check first scoped_numeral tmp(nm); it.get_right_tail_coeff(f_pp_lc, tmp); if (!nm.divides(tmp, f_pp[0])) { // don't remove this combination remove = false; continue; } it.right(trial_factor); } // add the lc(f_pp) to the trial divisor zpe_upm.mul(trial_factor, f_pp_lc); TRACE("polynomial::factorization::bughunt", tout << "f_pp*lc(f_pp) = "; upm.display(tout, f_pp); tout << endl; tout << "trial_factor = "; upm.display(tout, trial_factor); tout << endl; ); bool true_factor = upm.exact_div(f_pp, trial_factor, trial_factor_quo); TRACE("polynomial::factorization::bughunt", tout << "trial_factor = "; upm.display(tout, trial_factor); tout << endl; tout << "trial_factor_quo = "; upm.display(tout, trial_factor_quo); tout << endl; tout << "result = " << (true_factor ? "true" : "false") << endl; ); // if division is precise we have a factor if (true_factor) { if (!using_left) { // as noted above, we still use the original factor trial_factor.swap(trial_factor_quo); } // We need to get the content out of the factor upm.get_primitive_and_content(trial_factor, trial_factor, trial_factor_cont); // add the factor fs.push_back(trial_factor, k); // we continue with the quotient (with the content added back) // but we also have to keep lc(f_pp)*f_pp upm.get_primitive_and_content(trial_factor_quo, f_pp, trial_factor_cont); nm.set(f_pp_lc, f_pp.back()); upm.mul(f_pp, f_pp_lc); // but we also remove it from the iterator remove = true; } else { // don't remove this combination remove = false; } TRACE("polynomial::factorization::bughunt", tout << "factors = " << fs << endl; tout << "f_pp*lc(f_pp) = "; upm.display(tout, f_pp); tout << endl; tout << "lc(f_pp) = " << f_pp_lc << endl; ); } #ifndef _EXTERNAL_RELEASE IF_VERBOSE(FACTOR_VERBOSE_LVL, verbose_stream() << "(polynomial-factorization :search-size " << counter << ")" << std::endl;); #endif // add the what's left to the factors (if not a constant) if (f_pp.size() > 1) { upm.div(f_pp, f_pp_lc); fs.push_back(f_pp, k); } else { // if a constant it must be 1 (it was primitive) SASSERT(f_pp.size() == 1 && nm.is_one(f_pp.back())); } return result; } bool factor_square_free(z_manager & upm, numeral_vector const & f, factors & fs, factor_params const & params) { return factor_square_free(upm, f, fs, 1, params); } }; // end upolynomial namespace z3-z3-4.8.7/src/math/polynomial/upolynomial_factorization.h000066400000000000000000000100261356505360400240060ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: upolynomial_factorization.h Abstract: Methods for factoring polynomials. Author: Dejan (t-dejanj) 2011-11-29 Notes: [1] Elwyn Ralph Berlekamp. Factoring Polynomials over Finite Fields. Bell System Technical Journal, 46(8-10):1853-1859, 1967. [2] Donald Ervin Knuth. The Art of Computer Programming, volume 2: Seminumerical Algorithms. Addison Wesley, third edition, 1997. [3] Henri Cohen. A Course in Computational Algebraic Number Theory. Springer Verlag, 1993. --*/ #ifndef UPOLYNOMIAL_FACTORIZATION_H_ #define UPOLYNOMIAL_FACTORIZATION_H_ #include "math/polynomial/upolynomial.h" #include "math/polynomial/polynomial.h" #include "util/bit_vector.h" #include "util/z3_exception.h" namespace upolynomial { typedef manager::scoped_numeral scoped_numeral; /** \brief Factor f into f = f_1^k_1 * ... * p_n^k_n, such that p_i are square-free and coprime. */ void zp_square_free_factor(zp_manager & zp_upm, numeral_vector const & f, zp_factors & sq_free_factors); /** \brief Factor the monic square-free polynomial f from Z_p[x]. Returns true if factorization was successful, or false if f is an irreducible square-free polynomial in Z_p[x]. */ bool zp_factor_square_free(zp_manager & zp_upm, numeral_vector const & f, zp_factors & factors); inline bool zp_factor_square_free(zp_manager & zp_upm, numeral_vector const & f, zp_factors & factors, factor_params const & params) { return zp_factor_square_free(zp_upm, f, factors); } /** \brief Factor the monic square-free polynomial f from Z_p[x] using the Berlekamp algorithm. If randomized is true the factor splitting is done randomly [3], otherwise it is done as in the original Berlekamp [1]. */ bool zp_factor_square_free_berlekamp(zp_manager & zp_upm, numeral_vector const & f, zp_factors & factors, bool randomized = true); /** \brief Factor the polynomial f from Z_p[x]. Returns true if factorization was successful, or false if f is an irreducible polynomial in Z_p[x] */ bool zp_factor(zp_manager & zp_upm, numeral_vector const & f, zp_factors & factors); /** \brief Performs a Hensel lift of A and B in Z_a to Z_b, where p is prime and a = p^{a_k}, b = p^{b_k}, r = (a, b), with the following assumptions: * UA + VB = 1 (mod a) * C = AB (mod b) * (l(A), r) = 1 (important in order to divide by A, i.e. to invert l(A)) the output of is two polynomials A1, B1 (replacing A and B) such that A1 = A (mod b), B1 = B (mod b), l(A1) = l(A), deg(A1) = deg(A), deg(B1) = deg(B) and C = A1 B1 (mod b*r). Such A1, B1 are unique if r is prime. See [3] p. 138. The method will also change the zp_manager's module from b to b*r */ void hensel_lift(z_manager & upm, numeral const & a, numeral const & b, numeral const & r, numeral_vector const & U, numeral_vector const & A, numeral_vector const & V, numeral_vector const & B, numeral_vector const & C, numeral_vector & A_lifted, numeral_vector & B_lifted); /** \brief Performs the Hensel lift for the (monic!) factors_p of f in Z_p to Z_{p^e}. */ void hensel_lift(z_manager & upm, numeral_vector const & f, zp_factors const & factors_p, unsigned e, zp_factors & factors_pe); /** \brief Factor the square-free polynomial f from Z[x]. Returns true if factorization was successful, or false if f is an irreducible polynomial in Z[x]. The vector of factors is cleared. */ bool factor_square_free(z_manager & upm, numeral_vector const & f, factors & fs, factor_params const & ps = factor_params()); /** Similar to factor_square_free, but it is used to factor the k-th component f^k of a polynomial. That is, the factors of f are inserted as factors of degree k into fs. */ bool factor_square_free(z_manager & upm, numeral_vector const & f, factors & fs, unsigned k, factor_params const & ps = factor_params()); }; #endif z3-z3-4.8.7/src/math/polynomial/upolynomial_factorization_int.h000066400000000000000000000357301356505360400246710ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: upolynomial_factorization_int.h Abstract: (Internal) header file for univariate polynomial factorization. This classes are exposed for debugging purposes only. Author: Dejan (t-dejanj) 2011-11-29 Notes: [1] Elwyn Ralph Berlekamp. Factoring Polynomials over Finite Fields. Bell System Technical Journal, 46(8-10):1853-1859, 1967. [2] Donald Ervin Knuth. The Art of Computer Programming, volume 2: Seminumerical Algorithms. Addison Wesley, third edition, 1997. [3] Henri Cohen. A Course in Computational Algebraic Number Theory. Springer Verlag, 1993. --*/ #ifndef UPOLYNOMIAL_FACTORIZATION_INT_H_ #define UPOLYNOMIAL_FACTORIZATION_INT_H_ #include "math/polynomial/upolynomial_factorization.h" namespace upolynomial { // copy p from some manager to zp_p in Z_p[x] inline void to_zp_manager(zp_manager & zp_upm, numeral_vector & p) { zp_numeral_manager & zp_nm(zp_upm.m()); for (unsigned i = 0; i < p.size(); ++ i) { zp_nm.p_normalize(p[i]); } zp_upm.trim(p); } // copy p from some manager to zp_p in Z_p[x] inline void to_zp_manager(zp_manager & zp_upm, numeral_vector const & p, numeral_vector & zp_p) { zp_numeral_manager & zp_nm(zp_upm.m()); zp_upm.reset(zp_p); for (unsigned i = 0; i < p.size(); ++ i) { numeral p_i; // no need to delete, we keep it pushed in zp_p zp_nm.set(p_i, p[i]); zp_p.push_back(std::move(p_i)); } zp_upm.trim(zp_p); } /** \brief Contains all possible degrees of a factorization of a polynomial. If p = p1^{k_1} * ... * pn^{k_n} with p_i of degree d_i then it is represents numbers of the for \sum a_i*d_i, where a_i <= k_i. Two numbers always in the set are deg(p) and 0. */ class factorization_degree_set { // the set itself, a (m_max_degree)-binary number bit_vector m_set; public: factorization_degree_set() { } factorization_degree_set(zp_factors const & factors) { zp_manager & upm = factors.upm(); // the set contains only {0} m_set.push_back(true); for (unsigned i = 0; i < factors.distinct_factors(); ++ i) { unsigned degree = upm.degree(factors[i]); unsigned multiplicity = factors.get_degree(i); for (unsigned k = 0; k < multiplicity; ++ k) { bit_vector tmp(m_set); m_set.shift_right(degree); m_set |= tmp; } } SASSERT(in_set(0) && in_set(factors.get_degree())); } unsigned max_degree() const { return m_set.size() - 1; } void swap(factorization_degree_set & other) { m_set.swap(other.m_set); } bool is_trivial() const { // check if set = {0, n} for (int i = 1; i < (int) m_set.size() - 1; ++ i) { if (m_set.get(i)) return false; } return true; } void remove(unsigned k) { m_set.set(k, false); } bool in_set(unsigned k) const { return m_set.get(k); } void intersect(const factorization_degree_set& other) { m_set &= other.m_set; } void display(std::ostream & out) const { out << "[0"; for (unsigned i = 1; i <= max_degree(); ++ i) { if (in_set(i)) { out << ", " << i; } } out << "] represented by " << m_set; } }; /** \brief A to iterate through all combinations of factors. This is only needed for the factorization, and we always iterate through the */ template class factorization_combination_iterator_base { protected: // total size of available factors int m_total_size; // maximal size of the selection int m_max_size; // the factors to select from factors_type const & m_factors; // which factors are enabled svector m_enabled; // the size of the current selection int m_current_size; // the current selection: indices at positions < m_current_size, other values are maxed out svector m_current; /** Assuming a valid selection m_current[0], ..., m_current[position], try to find the next option for m_current[position], i.e. the first bigger one that's enabled. */ int find(int position, int upper_bound) { int current = m_current[position] + 1; while (current < upper_bound && !m_enabled[current]) { current ++; } if (current == upper_bound) { return -1; } else { return current; } } public: factorization_combination_iterator_base(factors_type const & factors) : m_total_size(factors.distinct_factors()), m_max_size(factors.distinct_factors()/2), m_factors(factors) { SASSERT(factors.total_factors() > 1); SASSERT(factors.total_factors() == factors.distinct_factors()); // enable all to start with m_enabled.resize(m_factors.distinct_factors(), true); // max out the m_current so that it always fits m_current.resize(m_factors.distinct_factors()+1, m_factors.distinct_factors()); m_current_size = 0; } /** \brief Returns the factors we are enumerating through. */ factors_type const & get_factors() const { return m_factors; } /** \brief Computes the next combination of factors and returns true if it exists. If remove current is true it will eliminate the current selected elements from any future selection. */ bool next(bool remove_current) { int max_upper_bound = m_factors.distinct_factors(); do { // the index we are currently trying to fix int current_i = m_current_size - 1; // the value we found as plausible (-1 we didn't find anything) int current_value = -1; if (remove_current) { SASSERT(m_current_size > 0); // disable the elements of the current selection from ever appearing again for (current_i = m_current_size - 1; current_i > 0; -- current_i) { SASSERT(m_enabled[m_current[current_i]]); m_enabled[m_current[current_i]] = false; m_current[current_i] = max_upper_bound; } // the last one SASSERT(m_enabled[m_current[0]]); m_enabled[m_current[0]] = false; // not removing current anymore remove_current = false; // out max size is also going down m_total_size -= m_current_size; m_max_size = m_total_size/2; } // we go back to the first one that can be increased (if removing current go all the way) while (current_i >= 0) { current_value = find(current_i, m_current[current_i + 1]); if (current_value >= 0) { // found one m_current[current_i] = current_value; break; } else { // go back some more current_i --; } } do { if (current_value == -1) { // we couldn't find any options, we have to increse size and start from the first one of that size if (m_current_size >= m_max_size) { return false; } else { m_current_size ++; m_current[0] = -1; current_i = 0; current_value = find(current_i, max_upper_bound); // if we didn't find any, we are done if (current_value == -1) { return false; } else { m_current[current_i] = current_value; } } } // ok we have a new selection for the current one for (current_i ++; current_i < m_current_size; ++ current_i) { // start from the previous one m_current[current_i] = m_current[current_i-1]; current_value = find(current_i, max_upper_bound); if (current_value == -1) { // screwed, didn't find the next one, this means we need to increase the size m_current[0] = -1; break; } else { m_current[current_i] = current_value; } } } while (current_value == -1); } while (filter_current()); // found the next one, hurray return true; } /** \brief A function that returns true if the current combination should be ignored. */ virtual bool filter_current() const = 0; /** \brief Returns the size of the current selection (cardinality) */ unsigned left_size() const { return m_current_size; } /** \brief Returns the size of the rest of the current selection (cardinality) */ unsigned right_size() const { return m_total_size - m_current_size; } void display(std::ostream& out) const { out << "[ "; for (unsigned i = 0; i < m_current.size(); ++ i) { out << m_current[i] << " "; } out << "] from [ "; for (unsigned i = 0; i < m_factors.distinct_factors(); ++ i) { if (m_enabled[i]) { out << i << " "; } } out << "]" << std::endl; } }; class ufactorization_combination_iterator : public factorization_combination_iterator_base { // the degree sets to choose from factorization_degree_set const & m_degree_set; public: ufactorization_combination_iterator(zp_factors const & factors, factorization_degree_set const & degree_set) : factorization_combination_iterator_base(factors), m_degree_set(degree_set) {} /** \brief Filter the ones not in the degree set. */ bool filter_current() const override { // select only the ones that have degrees in the degree set if (!m_degree_set.in_set(current_degree())) { return true; } return false; } /** \brief Returns the degree of the current selection. */ unsigned current_degree() const { unsigned degree = 0; zp_manager & upm = m_factors.pm(); for (unsigned i = 0; i < left_size(); ++ i) { degree += upm.degree(m_factors[m_current[i]]); } return degree; } void left(numeral_vector & out) const { SASSERT(m_current_size > 0); zp_manager & upm = m_factors.upm(); upm.set(m_factors[m_current[0]].size(), m_factors[m_current[0]].c_ptr(), out); for (int i = 1; i < m_current_size; ++ i) { upm.mul(out.size(), out.c_ptr(), m_factors[m_current[i]].size(), m_factors[m_current[i]].c_ptr(), out); } } void get_left_tail_coeff(numeral const & m, numeral & out) { zp_numeral_manager & nm = m_factors.upm().m(); nm.set(out, m); for (int i = 0; i < m_current_size; ++ i) { nm.mul(out, m_factors[m_current[i]][0], out); } } void get_right_tail_coeff(numeral const & m, numeral & out) { zp_numeral_manager & nm = m_factors.upm().m(); nm.set(out, m); unsigned current = 0; unsigned selection_i = 0; // selection is ordered, so we just take the ones in between that are not disable while (current < m_factors.distinct_factors()) { if (!m_enabled[current]) { // by skipping the disabled we never skip a selected one current ++; } else { if (selection_i >= m_current.size() || (int) current < m_current[selection_i]) { SASSERT(m_factors.get_degree(current) == 1); nm.mul(out, m_factors[current][0], out); current ++; } else { current ++; selection_i ++; } } } } void right(numeral_vector & out) const { SASSERT(m_current_size > 0); zp_manager & upm = m_factors.upm(); upm.reset(out); unsigned current = 0; unsigned selection_i = 0; // selection is ordered, so we just take the ones in between that are not disable while (current < m_factors.distinct_factors()) { if (!m_enabled[current]) { // by skipping the disabled we never skip a selected one current ++; } else { if (selection_i >= m_current.size() || (int) current < m_current[selection_i]) { SASSERT(m_factors.get_degree(current) == 1); 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); } current ++; } else { current ++; selection_i ++; } } } } }; }; #endif z3-z3-4.8.7/src/math/realclosure/000077500000000000000000000000001356505360400164675ustar00rootroot00000000000000z3-z3-4.8.7/src/math/realclosure/CMakeLists.txt000066400000000000000000000002251356505360400212260ustar00rootroot00000000000000z3_add_component(realclosure SOURCES mpz_matrix.cpp realclosure.cpp COMPONENT_DEPENDENCIES interval PYG_FILES rcf_params.pyg ) z3-z3-4.8.7/src/math/realclosure/mpz_matrix.cpp000066400000000000000000000317551356505360400214000ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: mpz_matrix.h Abstract: Matrix with integer coefficients. This is not a general purpose module for handling matrices with integer coefficients. Instead, it is a custom package that only contains operations needed to implement Sign Determination (Algorithm 10.11) in the Book: "Algorithms in real algebraic geometry", Basu, Pollack, Roy Design choices: - Dense representation. The matrices in Alg 10.11 are small and dense. - Integer coefficients instead of rational coefficients (it only complicates the solver a little bit). Remark: in Algorithm 10.11, the coefficients of the input matrices are always in {-1, 0, 1}. During solving, bigger coefficients are produced, but they are usually very small. It may be an overkill to use mpz instead of int. We use mpz just to be safe. Remark: We do not use rational arithmetic. The solver is slightly more complicated with integers, but is saves space. Author: Leonardo (leonardo) 2013-01-07 Notes: --*/ #include "math/realclosure/mpz_matrix.h" #include "util/buffer.h" mpz_matrix_manager::mpz_matrix_manager(unsynch_mpz_manager & nm, small_object_allocator & a): m_nm(nm), m_allocator(a) { } mpz_matrix_manager::~mpz_matrix_manager() { } void mpz_matrix_manager::mk(unsigned m, unsigned n, mpz_matrix & A) { SASSERT(m > 0 && n > 0); del(A); A.m = m; A.n = n; void * mem = m_allocator.allocate(sizeof(mpz)*m*n); A.a_ij = new (mem) mpz[m*n]; } void mpz_matrix_manager::del(mpz_matrix & A) { if (A.a_ij != nullptr) { for (unsigned i = 0; i < A.m; i++) for (unsigned j = 0; j < A.n; j++) nm().del(A(i,j)); unsigned sz = sizeof(mpz) * A.m * A.n; m_allocator.deallocate(sz, A.a_ij); A.m = 0; A.n = 0; A.a_ij = nullptr; } } void mpz_matrix_manager::set(mpz_matrix & A, mpz_matrix const & B) { if (&A == &B) return; if (A.m != B.m || A.n != B.n) { del(A); mk(B.m, B.n, A); } SASSERT(A.m == B.m && A.n == B.n); for (unsigned i = 0; i < B.m; i++) for (unsigned j = 0; j < B.n; j++) nm().set(A(i, j), B(i, j)); } void mpz_matrix_manager::tensor_product(mpz_matrix const & A, mpz_matrix const & B, mpz_matrix & C) { scoped_mpz_matrix CC(*this); mk(A.m * B.m, A.n * B.n, CC); for (unsigned i = 0; i < CC.m(); i++) for (unsigned j = 0; j < CC.n(); j++) nm().mul(A(i / B.m, j / B.n), B(i % B.m, j % B.n), CC(i, j)); C.swap(CC); } void mpz_matrix_manager::swap_rows(mpz_matrix & A, unsigned i, unsigned j) { if (i != j) { for (unsigned k = 0; k < A.n; k++) ::swap(A(i, k), A(j, k)); } } // If b_i == 0, then method just divides the given row by its GCD // If b_i != 0 // If the GCD of the row divides *b_i // divide the row and *b_i by the GCD // Else // If int_solver == true ==> return false (the system is unsolvable) bool mpz_matrix_manager::normalize_row(mpz * A_i, unsigned n, mpz * b_i, bool int_solver) { scoped_mpz g(nm()); bool first = true; for (unsigned j = 0; j < n; j++) { if (nm().is_zero(A_i[j])) continue; if (first) { nm().set(g, A_i[j]); nm().abs(g); first = false; } else { nm().gcd(g, A_i[j], g); } if (nm().is_one(g)) return true; } if (first) return true; // zero row if (!nm().is_one(g)) { if (b_i) { if (nm().divides(g, *b_i)) { for (unsigned j = 0; j < n; j++) { nm().div(A_i[j], g, A_i[j]); } nm().div(*b_i, g, *b_i); } else { if (int_solver) return false; // system does not have an integer solution } } else { for (unsigned j = 0; j < n; j++) { nm().div(A_i[j], g, A_i[j]); } } } return true; } /* Given a matrix of the form k2 | V X X ... X X ... X 0 X ... X X ... X ... ... X X ... X k1=> 0 0 ... 0 X ... X 0 0 ... 0 X ... X ... ... 0 X ... X 0 0 ... 0 X ... X It will "zero" the elements a_{k1+1, k2} ... a_{m, k2} by addining multiples of the row k1 to multiples of the rows k1+1, ..., m The resultant matrix will look like k2 | V X X ... X X ... X 0 X ... X X ... X ... ... X X ... X k1=> 0 0 ... 0 X ... X 0 0 ... 0 0 ... X ... ... 0 0 ... X 0 0 ... 0 0 ... X If b != 0, then the transformations are also applied to b. If int_solver == true and b != 0, then the method returns false if when performing the transformations it detected that it is impossible to solve the integer system of equations A x = b. */ bool mpz_matrix_manager::eliminate(mpz_matrix & A, mpz * b, unsigned k1, unsigned k2, bool int_solver) { // check if first k2-1 positions of row k1 are 0 DEBUG_CODE(for (unsigned j = 0; j < k2; j++) { SASSERT(nm().is_zero(A(k1, j))); }); mpz & a_kk = A(k1, k2); SASSERT(!nm().is_zero(a_kk)); scoped_mpz t1(nm()), t2(nm()); scoped_mpz a_ik_prime(nm()), a_kk_prime(nm()), lcm_a_kk_a_ik(nm()); // for all rows below pivot for (unsigned i = k1+1; i < A.m; i++) { // check if first k-1 positions of row k are 0 DEBUG_CODE(for (unsigned j = 0; j < k2; j++) { SASSERT(nm().is_zero(A(i, j))); }); mpz & a_ik = A(i, k2); if (!nm().is_zero(a_ik)) { // a_ik' = lcm(a_kk, a_ik)/a_kk // a_kk' = lcm(a_kk, a_ik)/a_ik nm().lcm(a_kk, a_ik, lcm_a_kk_a_ik); nm().div(lcm_a_kk_a_ik, a_kk, a_ik_prime); nm().div(lcm_a_kk_a_ik, a_ik, a_kk_prime); for (unsigned j = k2+1; j < A.n; j++) { // a_ij <- a_kk' * a_ij - a_ik' * a_kj nm().mul(a_ik_prime, A(k1, j), t1); nm().mul(a_kk_prime, A(i, j), t2); nm().sub(t2, t1, A(i, j)); } if (b) { // b_i <- a_kk' * b_i - a_ik' * b_k nm().mul(a_ik_prime, b[k1], t1); nm().mul(a_kk_prime, b[i], t2); nm().sub(t2, t1, b[i]); } // a_ik <- 0 nm().set(A(i, k2), 0); // normalize row i if (!normalize_row(A.row(i), A.n, b ? &(b[i]) : nullptr, int_solver)) return false; } SASSERT(nm().is_zero(A(i, k2))); } return true; } bool mpz_matrix_manager::solve_core(mpz_matrix const & _A, mpz * b, bool int_solver) { SASSERT(_A.n == _A.m); scoped_mpz_matrix A(*this); set(A, _A); for (unsigned k = 0; k < A.m(); k++) { TRACE("mpz_matrix", tout << "k: " << k << "\n" << A; tout << "b:"; for (unsigned i = 0; i < A.m(); i++) { tout << " "; nm().display(tout, b[i]); } tout << "\n";); // find pivot unsigned i = k; for (; i < A.m(); i++) { if (!nm().is_zero(A(i, k))) break; } if (i == A.m()) return false; // matrix is singular // swap rows k and i swap_rows(A, k, i); swap(b[k], b[i]); // if (!eliminate(A, b, k, k, int_solver)) return false; } // Back substitution unsigned k = A.m(); while (k > 0) { --k; DEBUG_CODE(for (unsigned j = 0; j < A.n(); j++) { SASSERT(j == k || nm().is_zero(A(k, j))); }); SASSERT(!nm().is_zero(A(k, k))); if (nm().divides(A(k, k), b[k])) { nm().div(b[k], A(k, k), b[k]); nm().set(A(k, k), 1); } else { if (int_solver) return false; // no integer solution if (nm().is_neg(A(k, k))) { nm().neg(A(k, k)); nm().neg(b[k]); } } if (!int_solver) { // REMARK: // For the sign determination algorithm, we only use int_solver == true. // // TODO: implement backward substitution when int_solver == false // In this case, A(k, k) may not be 1. NOT_IMPLEMENTED_YET(); } SASSERT(!int_solver || nm().is_one(A(k, k))); // back substitute unsigned i = k; while (i > 0) { --i; // Assuming int_solver == true SASSERT(int_solver); // See comment above // b_i <- b_i - a_ik * b_k nm().submul(b[i], A(i, k), b[k], b[i]); nm().set(A(i, k), 0); } } return true; } bool mpz_matrix_manager::solve(mpz_matrix const & A, mpz * b, mpz const * c) { for (unsigned i = 0; i < A.n; i++) nm().set(b[i], c[i]); return solve_core(A, b, true); } bool mpz_matrix_manager::solve(mpz_matrix const & A, int * b, int const * c) { scoped_mpz_matrix _b(*this); mk(A.n, 1, _b); for (unsigned i = 0; i < A.n; i++) nm().set(_b(i,0), c[i]); bool r = solve_core(A, _b.A.a_ij, true); if (r) { for (unsigned i = 0; i < A.n; i++) b[i] = _b.get_int(i, 0); } return r; } void mpz_matrix_manager::filter_cols(mpz_matrix const & A, unsigned num_cols, unsigned const * cols, mpz_matrix & B) { SASSERT(num_cols <= A.n); // Check pre-condition: // - All elements in cols are smaller than A.n // - cols is sorted // - cols does not contain repeated elements DEBUG_CODE({ for (unsigned i = 0; i < num_cols; i ++) { SASSERT(cols[i] < A.n); SASSERT(i == 0 || cols[i-1] < cols[i]); } }); if (num_cols == A.n) { // keep everything set(B, A); } else { SASSERT(num_cols < A.n); scoped_mpz_matrix C(*this); mk(A.m, num_cols, C); for (unsigned i = 0; i < A.m; i++) for (unsigned j = 0; j < num_cols; j++) nm().set(C(i, j), A(i, cols[j])); B.swap(C); } } void mpz_matrix_manager::permute_rows(mpz_matrix const & A, unsigned const * p, mpz_matrix & B) { // Check if p is really a permutation DEBUG_CODE({ buffer seen; seen.resize(A.m, false); for (unsigned i = 0; i < A.m; i++) { SASSERT(p[i] < A.m); SASSERT(!seen[p[i]]); seen[p[i]] = true; } }); scoped_mpz_matrix C(*this); mk(A.m, A.n, C); for (unsigned i = 0; i < A.m; i++) for (unsigned j = 0; j < A.n; j++) nm().set(C(i, j), A(p[i], j)); B.swap(C); } unsigned mpz_matrix_manager::linear_independent_rows(mpz_matrix const & _A, unsigned * r, mpz_matrix & B) { unsigned r_sz = 0; scoped_mpz_matrix A(*this); scoped_mpz g(nm()); scoped_mpz t1(nm()), t2(nm()); scoped_mpz a_ik_prime(nm()), a_kk_prime(nm()), lcm_a_kk_a_ik(nm()); set(A, _A); sbuffer rows; rows.resize(A.m(), 0); for (unsigned i = 0; i < A.m(); i++) rows[i] = i; for (unsigned k1 = 0, k2 = 0; k1 < A.m(); k1++) { TRACE("mpz_matrix", tout << "k1: " << k1 << ", k2: " << k2 << "\n" << A;); // find pivot unsigned pivot = UINT_MAX; for (unsigned i = k1; i < A.m(); i++) { if (!nm().is_zero(A(i, k2))) { if (pivot == UINT_MAX) { pivot = i; } else { if (rows[i] < rows[pivot]) pivot = i; } } } if (pivot == UINT_MAX) continue; // swap rows k and pivot swap_rows(A, k1, pivot); std::swap(rows[k1], rows[pivot]); // r[r_sz] = rows[k1]; r_sz++; if (r_sz >= A.n()) break; eliminate(A, nullptr, k1, k2, false); k2++; } std::sort(r, r + r_sz); // Copy linear independent rows to B mpz_matrix & C = A; mk(r_sz, _A.n, C); for (unsigned i = 0; i < r_sz; i++ ) { for (unsigned j = 0; j < _A.n; j++) { nm().set(C(i, j), _A(r[i], j)); } } B.swap(C); return r_sz; } void mpz_matrix_manager::display(std::ostream & out, mpz_matrix const & A, unsigned cell_width) const { out << A.m << " x " << A.n << " Matrix\n"; for (unsigned i = 0; i < A.m; i++) { for (unsigned j = 0; j < A.n; j++) { if (j > 0) out << " "; std::string s = nm().to_string(A(i, j)); if (s.size() < cell_width) { unsigned space = cell_width - static_cast(s.size()); for (unsigned k = 0; k < space; k++) out << " "; } out << s; } out << "\n"; } } z3-z3-4.8.7/src/math/realclosure/mpz_matrix.h000066400000000000000000000132551356505360400210400ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: mpz_matrix.h Abstract: Matrix with integer coefficients. This is not a general purpose module for handling matrices with integer coefficients. Instead, it is a custom package that only contains operations needed to implement Sign Determination (Algorithm 10.11) in the Book: "Algorithms in real algebraic geometry", Basu, Pollack, Roy Design choices: - Dense representation. The matrices in Alg 10.11 are small and dense. - Integer coefficients instead of rational coefficients (it only complicates the solver a little bit). Remark: in Algorithm 10.11, the coefficients of the input matrices are always in {-1, 0, 1}. During solving, bigger coefficients are produced, but they are usually very small. It may be an overkill to use mpz instead of int. We use mpz just to be safe. Remark: We do not use rational arithmetic. The solver is slightly more complicated with integers, but is saves space. Author: Leonardo (leonardo) 2013-01-07 Notes: --*/ #ifndef MPZ_MATRIX_H_ #define MPZ_MATRIX_H_ #include "util/mpz.h" /** \brief A mxn matrix. Remark: Algorithm 10.11 only uses square matrices, but supporting arbitrary matrices does not increase the complexity of this module. */ class mpz_matrix { friend class mpz_matrix_manager; friend class scoped_mpz_matrix; unsigned m; unsigned n; mpz * a_ij; public: mpz_matrix():m(0), n(0), a_ij(nullptr) {} mpz const & operator()(unsigned i, unsigned j) const { SASSERT(i < m); SASSERT(j < n); return a_ij[i*n + j]; } mpz & operator()(unsigned i, unsigned j) { SASSERT(i < m); SASSERT(j < n); return a_ij[i*n + j]; } void swap(mpz_matrix & B) { std::swap(m, B.m); std::swap(n, B.n); std::swap(a_ij, B.a_ij); } mpz * row(unsigned i) const { SASSERT(i < m); return a_ij + i*n; } }; class mpz_matrix_manager { unsynch_mpz_manager & m_nm; small_object_allocator & m_allocator; static void swap_rows(mpz_matrix & A, unsigned i, unsigned j); bool normalize_row(mpz * A_i, unsigned n, mpz * b_i, bool int_solver); bool eliminate(mpz_matrix & A, mpz * b, unsigned k1, unsigned k2, bool int_solver); bool solve_core(mpz_matrix const & A, mpz * b, bool int_solver); public: mpz_matrix_manager(unsynch_mpz_manager & nm, small_object_allocator & a); ~mpz_matrix_manager(); unsynch_mpz_manager & nm() const { return m_nm; } void mk(unsigned m, unsigned n, mpz_matrix & A); void del(mpz_matrix & r); void set(mpz_matrix & A, mpz_matrix const & B); void tensor_product(mpz_matrix const & A, mpz_matrix const & B, mpz_matrix & C); /** \brief Solve A*b = c Return false if the system does not have an integer solution. \pre A is a square matrix \pre b and c are vectors of size A.n (== A.m) */ bool solve(mpz_matrix const & A, mpz * b, mpz const * c); /** \brief Solve A*b = c Return false if the system does not have an integer solution. \pre A is a square matrix \pre b and c are vectors of size A.n (== A.m) */ bool solve(mpz_matrix const & A, int * b, int const * c); /** \brief Store in B that contains the subset cols of columns of A. \pre num_cols <= A.n \pre Forall i < num_cols, cols[i] < A.n \pre Forall 0 < i < num_cols, cols[i-1] < cols[i] */ void filter_cols(mpz_matrix const & A, unsigned num_cols, unsigned const * cols, mpz_matrix & B); /** \brief Store in B the matrix obtained after applying the given permutation to the rows of A. */ void permute_rows(mpz_matrix const & A, unsigned const * p, mpz_matrix & B); /** \brief Store in r the row (ids) of A that are linear independent. \remark If there is an option between rows i and j, this method will give preference to the row that occurs first. \remark The vector r must have at least A.n() capacity The number of linear independent rows is returned. Store the new matrix in B. */ unsigned linear_independent_rows(mpz_matrix const & A, unsigned * r, mpz_matrix & B); // method for debugging purposes void display(std::ostream & out, mpz_matrix const & A, unsigned cell_width=4) const; }; class scoped_mpz_matrix { friend class mpz_matrix_manager; mpz_matrix_manager & m_manager; mpz_matrix A; public: scoped_mpz_matrix(mpz_matrix_manager & m):m_manager(m) {} scoped_mpz_matrix(mpz_matrix const & B, mpz_matrix_manager & m):m_manager(m) { m_manager.set(A, B); } ~scoped_mpz_matrix() { m_manager.del(A); } mpz_matrix_manager & mm() const { return m_manager; } unsynch_mpz_manager & nm() const { return mm().nm(); } unsigned m() const { return A.m; } unsigned n() const { return A.n; } mpz * row(unsigned i) const { return A.row(i); } operator mpz_matrix const &() const { return A; } operator mpz_matrix &() { return A; } mpz_matrix const & get() const { return A; } mpz_matrix & get() { return A; } void swap(mpz_matrix & B) { A.swap(B); } void set(unsigned i, unsigned j, mpz const & v) { nm().set(A(i, j), v); } void set(unsigned i, unsigned j, int v) { nm().set(A(i, j), v); } mpz const & operator()(unsigned i, unsigned j) const { return A(i, j); } mpz & operator()(unsigned i, unsigned j) { return A(i, j); } int get_int(unsigned i, unsigned j) const { SASSERT(nm().is_int(A(i, j))); return nm().get_int(A(i, j)); } }; inline std::ostream & operator<<(std::ostream & out, scoped_mpz_matrix const & m) { m.mm().display(out, m); return out; } #endif z3-z3-4.8.7/src/math/realclosure/rcf_params.pyg000066400000000000000000000022121356505360400213220ustar00rootroot00000000000000def_module_params('rcf', description='real closed fields', export=True, params=(('use_prem', BOOL, True, "use pseudo-remainder instead of remainder when computing GCDs and Sturm-Tarski sequences"), ('clean_denominators', BOOL, True, "clean denominators before root isolation"), ('initial_precision', UINT, 24, "a value k that is the initial interval size (as 1/2^k) when creating transcendentals and approximated division"), ('inf_precision', UINT, 24, "a value k that is the initial interval size (i.e., (0, 1/2^l)) used as an approximation for infinitesimal values"), ('max_precision', UINT, 128, "during sign determination we switch from interval arithmetic to complete methods when the interval size is less than 1/2^k, where k is the max_precision"), ('lazy_algebraic_normalization', BOOL, True, "during sturm-seq and square-free polynomial computations, only normalize algebraic polynomial expressions when the defining polynomial is monic") )) z3-z3-4.8.7/src/math/realclosure/realclosure.cpp000066400000000000000000007416331356505360400215310ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: realclosure.cpp Abstract: Package for computing with elements of the realclosure of a field containing - all rationals - extended with computable transcendental real numbers (e.g., pi and e) - infinitesimals Author: Leonardo (leonardo) 2013-01-02 Notes: --*/ #include "math/realclosure/realclosure.h" #include "math/realclosure/rcf_params.hpp" #include "util/array.h" #include "util/mpbq.h" #include "math/realclosure/mpz_matrix.h" #include "math/interval/interval_def.h" #include "util/obj_ref.h" #include "util/ref_vector.h" #include "util/ref_buffer.h" #include "util/common_msgs.h" #ifndef REALCLOSURE_INI_BUFFER_SIZE #define REALCLOSURE_INI_BUFFER_SIZE 32 #endif #ifndef REALCLOSURE_INI_SEQ_SIZE #define REALCLOSURE_INI_SEQ_SIZE 256 #endif #ifndef REALCLOSURE_INI_DIV_PRECISION #define REALCLOSURE_INI_DIV_PRECISION 24 #endif namespace realclosure { // --------------------------------- // // Intervals with binary rational endpoints // // --------------------------------- struct mpbq_config { struct numeral_manager : public mpbq_manager { // division is not precise static bool precise() { return false; } static bool field() { return true; } unsigned m_div_precision; bool m_to_plus_inf; numeral_manager(unsynch_mpq_manager & qm):mpbq_manager(qm), m_div_precision(REALCLOSURE_INI_DIV_PRECISION), m_to_plus_inf(true) { } void div(mpbq const & a, mpbq const & b, mpbq & c) { approx_div(a, b, c, m_div_precision, m_to_plus_inf); } void inv(mpbq & a) { mpbq one(1); scoped_mpbq r(*this); approx_div(one, a, r, m_div_precision, m_to_plus_inf); swap(a, r); } }; typedef mpbq numeral; numeral_manager & m_manager; struct interval { numeral m_lower; numeral m_upper; unsigned char m_lower_inf; unsigned char m_upper_inf; unsigned char m_lower_open; unsigned char m_upper_open; interval():m_lower_inf(true), m_upper_inf(true), m_lower_open(true), m_upper_open(true) {} interval(numeral & l, numeral & u):m_lower_inf(false), m_upper_inf(false), m_lower_open(true), m_upper_open(true) { swap(m_lower, l); swap(m_upper, u); } numeral & lower() { return m_lower; } numeral & upper() { return m_upper; } void set_lower_is_inf(bool f) { m_lower_inf = f; } void set_upper_is_inf(bool f) { m_upper_inf = f; } void set_lower_is_open(bool f) { m_lower_open = f; } void set_upper_is_open(bool f) { m_upper_open = f; } numeral const & lower() const { return m_lower; } numeral const & upper() const { return m_upper; } bool lower_is_inf() const { return m_lower_inf != 0; } bool upper_is_inf() const { return m_upper_inf != 0; } bool lower_is_open() const { return m_lower_open != 0; } bool upper_is_open() const { return m_upper_open != 0; } }; void set_rounding(bool to_plus_inf) { m_manager.m_to_plus_inf = to_plus_inf; } void round_to_minus_inf() { set_rounding(false); } void round_to_plus_inf() { set_rounding(true); } // Getters numeral const & lower(interval const & a) const { return a.m_lower; } numeral const & upper(interval const & a) const { return a.m_upper; } numeral & lower(interval & a) { return a.m_lower; } numeral & upper(interval & a) { return a.m_upper; } bool lower_is_open(interval const & a) const { return a.lower_is_open(); } bool upper_is_open(interval const & a) const { return a.upper_is_open(); } bool lower_is_inf(interval const & a) const { return a.lower_is_inf(); } bool upper_is_inf(interval const & a) const { return a.upper_is_inf(); } // Setters void set_lower(interval & a, numeral const & n) { m_manager.set(a.m_lower, n); } void set_upper(interval & a, numeral const & n) { m_manager.set(a.m_upper, n); } void set_lower_is_open(interval & a, bool v) { a.m_lower_open = v; } void set_upper_is_open(interval & a, bool v) { a.m_upper_open = v; } void set_lower_is_inf(interval & a, bool v) { a.m_lower_inf = v; } void set_upper_is_inf(interval & a, bool v) { a.m_upper_inf = v; } // Reference to numeral manager numeral_manager & m() const { return m_manager; } mpbq_config(numeral_manager & m):m_manager(m) {} }; typedef interval_manager mpbqi_manager; typedef mpbqi_manager::interval mpbqi; void swap(mpbqi & a, mpbqi & b) { swap(a.m_lower, b.m_lower); swap(a.m_upper, b.m_upper); std::swap(a.m_lower_inf, b.m_lower_inf); std::swap(a.m_upper_inf, b.m_upper_inf); std::swap(a.m_lower_open, b.m_lower_open); std::swap(a.m_upper_open, b.m_upper_open); } // --------------------------------- // // Values are represented as // - arbitrary precision rationals (mpq) // - rational functions on field extensions // // --------------------------------- struct value { unsigned m_ref_count; //!< Reference counter 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, // This unnecessary precision will only slowdown the subsequent operations that do not need it. // To cope with this issue, we cache the value m_interval in m_old_interval whenever the width of m_interval is below // a give threshold. Then, after finishing OP, we restore the old_interval. mpbqi * m_old_interval; value(bool rat):m_ref_count(0), m_rational(rat), m_old_interval(nullptr) {} bool is_rational() const { return m_rational; } mpbqi const & interval() const { return m_interval; } mpbqi & interval() { return m_interval; } }; struct rational_value : public value { mpq m_value; rational_value():value(true) {} }; typedef ptr_array polynomial; struct extension; bool rank_lt(extension * r1, extension * r2); struct rational_function_value : public value { polynomial m_numerator; polynomial m_denominator; // it is only needed if the extension is not algebraic. extension * m_ext; bool m_depends_on_infinitesimals; //!< True if the polynomial expression depends on infinitesimal values. rational_function_value(extension * ext):value(false), m_ext(ext), m_depends_on_infinitesimals(false) {} polynomial const & num() const { return m_numerator; } polynomial & num() { return m_numerator; } polynomial const & den() const { return m_denominator; } polynomial & den() { return m_denominator; } extension * ext() const { return m_ext; } bool depends_on_infinitesimals() const { return m_depends_on_infinitesimals; } void set_depends_on_infinitesimals(bool f) { m_depends_on_infinitesimals = f; } }; // --------------------------------- // // Field Extensions // // --------------------------------- typedef int sign; typedef std::pair p2s; typedef sarray signs; struct extension { enum kind { TRANSCENDENTAL = 0, INFINITESIMAL = 1, ALGEBRAIC = 2 }; unsigned m_ref_count; unsigned m_kind:2; unsigned m_idx:30; mpbqi m_interval; mpbqi * m_old_interval; extension(kind k, unsigned idx):m_ref_count(0), m_kind(k), m_idx(idx), m_old_interval(nullptr) {} unsigned idx() const { return m_idx; } kind knd() const { return static_cast(m_kind); } bool is_algebraic() const { return knd() == ALGEBRAIC; } bool is_infinitesimal() const { return knd() == INFINITESIMAL; } bool is_transcendental() const { return knd() == TRANSCENDENTAL; } mpbqi const & interval() const { return m_interval; } mpbqi & interval() { return m_interval; } }; bool rank_lt(extension * r1, extension * r2) { return r1->knd() < r2->knd() || (r1->knd() == r2->knd() && r1->idx() < r2->idx()); } bool rank_eq(extension * r1, extension * r2) { return r1->knd() == r2->knd() && r1->idx() == r2->idx(); } struct rank_lt_proc { bool operator()(extension * r1, extension * r2) const { return rank_lt(r1, r2); } }; /** \brief Sign condition object, it encodes one conjunct of a sign assignment. If has to keep following m_prev to obtain the whole sign condition */ struct sign_condition { unsigned m_q_idx:31; // Sign condition for the polynomial at position m_q_idx in the field m_qs of sign_det structure unsigned m_mark:1; // auxiliary mark used during deletion int m_sign; // Sign of the polynomial associated with m_q_idx sign_condition * m_prev; // Antecedent sign_condition():m_q_idx(0), m_mark(false), m_sign(0), m_prev(nullptr) {} sign_condition(unsigned qidx, int sign, sign_condition * prev):m_q_idx(qidx), m_mark(false), m_sign(sign), m_prev(prev) {} sign_condition * prev() const { return m_prev; } unsigned qidx() const { return m_q_idx; } int sign() const { return m_sign; } }; struct sign_det { unsigned m_ref_count; // sign_det objects may be shared between different roots of the same polynomial. mpz_matrix M_s; // Matrix used in the sign determination array m_prs; // Polynomials associated with the rows of M array m_taqrs; // Result of the tarski query for each polynomial in m_prs array m_sign_conditions; // Sign conditions associated with the columns of M array m_qs; // Polynomials used in the sign conditions. sign_det():m_ref_count(0) {} array const & qs() const { return m_qs; } sign_condition * sc(unsigned idx) const { return m_sign_conditions[idx]; } unsigned num_roots() const { return m_prs.size(); } array const & taqrs() const { return m_taqrs; } array const & prs() const { return m_prs; } }; struct algebraic : public extension { polynomial m_p; mpbqi m_iso_interval; 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. algebraic(unsigned idx):extension(ALGEBRAIC, idx), m_sign_det(nullptr), m_sc_idx(0), m_depends_on_infinitesimals(false) {} polynomial const & p() const { return m_p; } bool depends_on_infinitesimals() const { return m_depends_on_infinitesimals; } sign_det * sdt() const { return m_sign_det; } unsigned sc_idx() const { return m_sc_idx; } unsigned num_roots_inside_interval() const { return m_sign_det == nullptr ? 1 : m_sign_det->num_roots(); } mpbqi & iso_interval() { return m_iso_interval; } }; struct transcendental : public extension { symbol m_name; symbol m_pp_name; unsigned m_k; mk_interval & m_proc; transcendental(unsigned idx, symbol const & n, symbol const & pp_n, mk_interval & p): extension(TRANSCENDENTAL, idx), m_name(n), m_pp_name(pp_n), m_k(0), m_proc(p) {} void display(std::ostream & out, bool pp = false) const { if (pp) out << m_pp_name; else out << m_name; } }; struct infinitesimal : public extension { symbol m_name; symbol m_pp_name; infinitesimal(unsigned idx, symbol const & n, symbol const & pp_n):extension(INFINITESIMAL, idx), m_name(n), m_pp_name(pp_n) {} void display(std::ostream & out, bool pp = false) const { if (pp) { if (m_pp_name.is_numerical()) out << "ε" << m_pp_name.get_num() << ""; else out << m_pp_name; } else { if (m_name.is_numerical()) out << "eps!" << m_name.get_num(); else out << m_name; } } }; // --------------------------------- // // Predefined transcendental mk_interval procs // // --------------------------------- struct mk_pi_interval : public mk_interval { void operator()(unsigned k, mpqi_manager & im, mpqi_manager::interval & r) override { im.pi(k, r); } }; struct mk_e_interval : public mk_interval { void operator()(unsigned k, mpqi_manager & im, mpqi_manager::interval & r) override { im.e(k, r); } }; // --------------------------------- // // Manager // // --------------------------------- struct manager::imp { typedef ref_vector value_ref_vector; typedef ref_buffer value_ref_buffer; typedef obj_ref value_ref; typedef _scoped_interval scoped_mpqi; typedef _scoped_interval scoped_mpbqi; typedef sbuffer int_buffer; typedef sbuffer unsigned_buffer; reslimit& m_limit; small_object_allocator * m_allocator; bool m_own_allocator; unsynch_mpq_manager & m_qm; mpz_matrix_manager m_mm; mpbq_config::numeral_manager m_bqm; mpqi_manager m_qim; mpbqi_manager m_bqim; ptr_vector m_extensions[3]; value * m_one; mk_pi_interval m_mk_pi_interval; value * m_pi; mk_e_interval m_mk_e_interval; value * m_e; ptr_vector m_to_restore; //!< Set of values v s.t. v->m_old_interval != 0 ptr_vector m_ex_to_restore; // Parameters bool m_use_prem; //!< use pseudo-remainder when computing sturm sequences bool m_clean_denominators; unsigned m_ini_precision; //!< initial precision for transcendentals, infinitesimals, etc. unsigned m_max_precision; //!< Maximum precision for interval arithmetic techniques, it switches to complete methods after that unsigned m_inf_precision; //!< 2^m_inf_precision is used as the lower bound of oo and -2^m_inf_precision is used as the upper_bound of -oo scoped_mpbq m_plus_inf_approx; // lower bound for binary rational intervals used to approximate an infinite positive value scoped_mpbq m_minus_inf_approx; // upper bound for binary rational intervals used to approximate an infinite negative value bool m_lazy_algebraic_normalization; // Tracing unsigned m_exec_depth; bool m_in_aux_values; // True if we are computing SquareFree polynomials or Sturm sequences. That is, the values being computed will be discarded. struct scoped_polynomial_seq { typedef ref_buffer value_seq; value_seq m_seq_coeffs; sbuffer m_begins; // start position (in m_seq_coeffs) of each polynomial in the sequence sbuffer m_szs; // size of each polynomial in the sequence public: scoped_polynomial_seq(imp & m):m_seq_coeffs(m) {} ~scoped_polynomial_seq() { } /** \brief Add a new polynomial to the sequence. The contents of p is erased. */ void push(unsigned sz, value * const * p) { m_begins.push_back(m_seq_coeffs.size()); m_szs.push_back(sz); m_seq_coeffs.append(sz, p); } /** \brief Return the number of polynomials in the sequence. */ unsigned size() const { return m_szs.size(); } /** \brief Return the vector of coefficients for the i-th polynomial in the sequence. */ value * const * coeffs(unsigned i) const { return m_seq_coeffs.c_ptr() + m_begins[i]; } /** \brief Return the size of the i-th polynomial in the sequence. */ unsigned size(unsigned i) const { return m_szs[i]; } void reset() { m_seq_coeffs.reset(); m_begins.reset(); m_szs.reset(); } scoped_polynomial_seq & operator=(scoped_polynomial_seq & s) { if (this == &s) return *this; reset(); m_seq_coeffs.append(s.m_seq_coeffs); m_begins.append(s.m_begins); m_szs.append(s.m_szs); return *this; } }; struct scoped_sign_conditions { imp & m_imp; ptr_buffer m_scs; scoped_sign_conditions(imp & m):m_imp(m) {} ~scoped_sign_conditions() { m_imp.del_sign_conditions(m_scs.size(), m_scs.c_ptr()); } sign_condition * & operator[](unsigned idx) { return m_scs[idx]; } unsigned size() const { return m_scs.size(); } bool empty() const { return m_scs.empty(); } void push_back(sign_condition * sc) { m_scs.push_back(sc); } void release() { // release ownership m_scs.reset(); } void copy_from(scoped_sign_conditions & scs) { SASSERT(this != &scs); release(); m_scs.append(scs.m_scs.size(), scs.m_scs.c_ptr()); scs.release(); } sign_condition * const * c_ptr() { return m_scs.c_ptr(); } }; struct scoped_inc_depth { imp & m_imp; scoped_inc_depth(imp & m):m_imp(m) { m_imp.m_exec_depth++; } ~scoped_inc_depth() { m_imp.m_exec_depth--; } }; #ifdef _TRACE #define INC_DEPTH() scoped_inc_depth __inc(*this) #else #define INC_DEPTH() ((void) 0) #endif imp(reslimit& lim, unsynch_mpq_manager & qm, params_ref const & p, small_object_allocator * a): m_limit(lim), m_allocator(a == nullptr ? alloc(small_object_allocator, "realclosure") : a), m_own_allocator(a == nullptr), m_qm(qm), m_mm(m_qm, *m_allocator), m_bqm(m_qm), m_qim(lim, m_qm), m_bqim(lim, m_bqm), m_plus_inf_approx(m_bqm), m_minus_inf_approx(m_bqm) { mpq one(1); m_one = mk_rational(one); inc_ref(m_one); m_pi = nullptr; m_e = nullptr; m_exec_depth = 0; m_in_aux_values = false; updt_params(p); } ~imp() { restore_saved_intervals(); // to free memory dec_ref(m_one); dec_ref(m_pi); dec_ref(m_e); if (m_own_allocator) dealloc(m_allocator); } // Rational number manager unsynch_mpq_manager & qm() const { return m_qm; } // Binary rational number manager mpbq_config::numeral_manager & bqm() { return m_bqm; } // Rational interval manager mpqi_manager & qim() { return m_qim; } // Binary rational interval manager mpbqi_manager & bqim() { return m_bqim; } mpbqi_manager const & bqim() const { return m_bqim; } // Integer matrix manager mpz_matrix_manager & mm() { return m_mm; } small_object_allocator & allocator() { return *m_allocator; } void checkpoint() { if (!m_limit.inc()) throw exception(Z3_CANCELED_MSG); } value * one() const { return m_one; } /** \brief Return the magnitude of the given interval. The magnitude is an approximation of the size of the interval. */ int magnitude(mpbq const & l, mpbq const & u) { SASSERT(bqm().ge(u, l)); scoped_mpbq w(bqm()); bqm().sub(u, l, w); if (bqm().is_zero(w)) return INT_MIN; SASSERT(bqm().is_pos(w)); return bqm().magnitude_ub(w); } /** \brief Return the magnitude of the given interval. The magnitude is an approximation of the size of the interval. */ int magnitude(mpbqi const & i) { if (i.lower_is_inf() || i.upper_is_inf()) return INT_MAX; else return magnitude(i.lower(), i.upper()); } /** \brief Return the magnitude of the given interval. The magnitude is an approximation of the size of the interval. */ int magnitude(mpq const & l, mpq const & u) { SASSERT(qm().ge(u, l)); scoped_mpq w(qm()); qm().sub(u, l, w); if (qm().is_zero(w)) return INT_MIN; SASSERT(qm().is_pos(w)); return static_cast(qm().log2(w.get().numerator())) + 1 - static_cast(qm().log2(w.get().denominator())); } int magnitude(scoped_mpqi const & i) { SASSERT(!i->m_lower_inf && !i->m_upper_inf); return magnitude(i->m_lower, i->m_upper); } /** \brief Return true if the magnitude of the given interval is less than the parameter m_max_precision. */ bool too_small(mpbqi const & i) { return magnitude(i) < -static_cast(m_max_precision); } #define SMALL_UNSIGNED 1 << 16 static unsigned inc_precision(unsigned prec, unsigned inc) { if (prec < SMALL_UNSIGNED) return prec + inc; else return prec; } struct scoped_set_div_precision { mpbq_config::numeral_manager & m_bqm; unsigned m_old_precision; scoped_set_div_precision(mpbq_config::numeral_manager & bqm, unsigned prec):m_bqm(bqm) { m_old_precision = m_bqm.m_div_precision; m_bqm.m_div_precision = prec; } ~scoped_set_div_precision() { m_bqm.m_div_precision = m_old_precision; } }; /** \brief c <- a/b with precision prec. */ void div(mpbqi const & a, mpbqi const & b, unsigned prec, mpbqi & c) { SASSERT(!contains_zero(a)); SASSERT(!contains_zero(b)); scoped_set_div_precision set(bqm(), prec); bqim().div(a, b, c); SASSERT(!contains_zero(c)); } /** \brief c <- a/b with precision prec. */ void div(mpbqi const & a, mpz const & b, unsigned prec, mpbqi & c) { SASSERT(!contains_zero(a)); SASSERT(!qm().is_zero(b)); scoped_mpbqi bi(bqim()); set_interval(bi, b); scoped_mpbqi r(bqim()); div(a, bi, prec, r); swap(c, r); } /** \brief Save the current interval (i.e., approximation) of the given value or extension. */ template void save_interval(T * v, ptr_vector & to_restore) { if (v->m_old_interval != nullptr) return; // interval was already saved. to_restore.push_back(v); inc_ref(v); v->m_old_interval = new (allocator()) mpbqi(); set_interval(*(v->m_old_interval), v->m_interval); } void save_interval(value * v) { save_interval(v, m_to_restore); } void save_interval(extension * x) { save_interval(x, m_ex_to_restore); } /** \brief Save the current interval (i.e., approximation) of the given value IF it is too small. */ void save_interval_if_too_small(value * v, unsigned new_prec) { if (new_prec > m_max_precision && !contains_zero(interval(v))) save_interval(v); } /** \brief Save the current interval (i.e., approximation) of the given value IF it is too small. */ void save_interval_if_too_small(extension * x, unsigned new_prec) { if (new_prec > m_max_precision && !contains_zero(x->m_interval)) save_interval(x); } /** \brief Restore the saved intervals (approximations) of RCF values and extensions */ template void restore_saved_intervals(ptr_vector & to_restore) { unsigned sz = to_restore.size(); for (unsigned i = 0; i < sz; i++) { T * v = to_restore[i]; set_interval(v->m_interval, *(v->m_old_interval)); bqim().del(*(v->m_old_interval)); allocator().deallocate(sizeof(mpbqi), v->m_old_interval); v->m_old_interval = nullptr; dec_ref(v); } to_restore.reset(); } void restore_saved_intervals() { restore_saved_intervals(m_to_restore); restore_saved_intervals(m_ex_to_restore); } void cleanup(extension::kind k) { ptr_vector & exts = m_extensions[k]; // keep removing unused slots while (!exts.empty() && exts.back() == 0) { exts.pop_back(); } } unsigned next_transcendental_idx() { cleanup(extension::TRANSCENDENTAL); return m_extensions[extension::TRANSCENDENTAL].size(); } unsigned next_infinitesimal_idx() { cleanup(extension::INFINITESIMAL); return m_extensions[extension::INFINITESIMAL].size(); } unsigned next_algebraic_idx() { cleanup(extension::ALGEBRAIC); return m_extensions[extension::ALGEBRAIC].size(); } void updt_params(params_ref const & _p) { rcf_params p(_p); m_use_prem = p.use_prem(); m_clean_denominators = p.clean_denominators(); m_ini_precision = p.initial_precision(); m_inf_precision = p.inf_precision(); m_max_precision = p.max_precision(); m_lazy_algebraic_normalization = p.lazy_algebraic_normalization(); bqm().power(mpbq(2), m_inf_precision, m_plus_inf_approx); bqm().set(m_minus_inf_approx, m_plus_inf_approx); bqm().neg(m_minus_inf_approx); } /** \brief Reset the given polynomial. That is, after the call p is the 0 polynomial. */ void reset_p(polynomial & p) { dec_ref(p.size(), p.c_ptr()); p.finalize(allocator()); } void del_rational(rational_value * v) { bqim().del(v->m_interval); qm().del(v->m_value); allocator().deallocate(sizeof(rational_value), v); } void del_rational_function(rational_function_value * v) { bqim().del(v->m_interval); reset_p(v->num()); reset_p(v->den()); dec_ref(v->ext()); allocator().deallocate(sizeof(rational_function_value), v); } void del_value(value * v) { if (v->is_rational()) del_rational(static_cast(v)); else del_rational_function(static_cast(v)); } void finalize(array & ps) { for (unsigned i = 0; i < ps.size(); i++) reset_p(ps[i]); ps.finalize(allocator()); } void del_sign_condition(sign_condition * sc) { allocator().deallocate(sizeof(sign_condition), sc); } void del_sign_conditions(unsigned sz, sign_condition * const * to_delete) { ptr_buffer all_to_delete; for (unsigned i = 0; i < sz; i++) { sign_condition * sc = to_delete[i]; while (sc && sc->m_mark == false) { sc->m_mark = true; all_to_delete.push_back(sc); sc = sc->m_prev; } } for (unsigned i = 0; i < all_to_delete.size(); i++) { del_sign_condition(all_to_delete[i]); } } void del_sign_det(sign_det * sd) { mm().del(sd->M_s); del_sign_conditions(sd->m_sign_conditions.size(), sd->m_sign_conditions.c_ptr()); sd->m_sign_conditions.finalize(allocator()); finalize(sd->m_prs); sd->m_taqrs.finalize(allocator()); finalize(sd->m_qs); allocator().deallocate(sizeof(sign_det), sd); } void inc_ref_sign_det(sign_det * sd) { if (sd != nullptr) sd->m_ref_count++; } void dec_ref_sign_det(sign_det * sd) { if (sd != nullptr) { sd->m_ref_count--; if (sd->m_ref_count == 0) { del_sign_det(sd); } } } void del_algebraic(algebraic * a) { reset_p(a->m_p); bqim().del(a->m_interval); bqim().del(a->m_iso_interval); dec_ref_sign_det(a->m_sign_det); allocator().deallocate(sizeof(algebraic), a); } void del_transcendental(transcendental * t) { bqim().del(t->m_interval); allocator().deallocate(sizeof(transcendental), t); } void del_infinitesimal(infinitesimal * i) { bqim().del(i->m_interval); allocator().deallocate(sizeof(infinitesimal), i); } void inc_ref(extension * ext) { SASSERT(ext != 0); ext->m_ref_count++; } void dec_ref(extension * ext) { SASSERT(m_extensions[ext->knd()][ext->idx()] == ext); SASSERT(ext->m_ref_count > 0); ext->m_ref_count--; if (ext->m_ref_count == 0) { m_extensions[ext->knd()][ext->idx()] = 0; switch (ext->knd()) { case extension::TRANSCENDENTAL: del_transcendental(static_cast(ext)); break; case extension::INFINITESIMAL: del_infinitesimal(static_cast(ext)); break; case extension::ALGEBRAIC: del_algebraic(static_cast(ext)); break; } } } void inc_ref(value * v) { if (v) v->m_ref_count++; } void inc_ref(unsigned sz, value * const * p) { for (unsigned i = 0; i < sz; i++) inc_ref(p[i]); } void dec_ref(value * v) { if (v) { SASSERT(v->m_ref_count > 0); v->m_ref_count--; if (v->m_ref_count == 0) del_value(v); } } void dec_ref(unsigned sz, value * const * p) { for (unsigned i = 0; i < sz; i++) dec_ref(p[i]); } void del(numeral & a) { dec_ref(a.m_value); a.m_value = nullptr; } void del(numeral_vector & v) { for (unsigned i = 0; i < v.size(); i++) del(v[i]); } /** \brief Return true if the given interval is smaller than 1/2^k */ bool check_precision(mpbqi const & interval, unsigned k) { if (interval.lower_is_inf() || interval.upper_is_inf()) return false; scoped_mpbq w(bqm()); bqm().sub(interval.upper(), interval.lower(), w); return bqm().lt_1div2k(w, k); } /** \brief Return true if v is zero. */ static bool is_zero(value * v) { return v == nullptr; } /** \brief Return true if v is represented using a nonzero arbitrary precision rational value. */ static bool is_nz_rational(value * v) { SASSERT(v != 0); return v->is_rational(); } /** \brief Return true if v is represented as rational value one. */ bool is_rational_one(value * v) const { return !is_zero(v) && is_nz_rational(v) && qm().is_one(to_mpq(v)); } /** \brief Return true if v is represented as rational value minus one. */ bool is_rational_minus_one(value * v) const { return !is_zero(v) && is_nz_rational(v) && qm().is_minus_one(to_mpq(v)); } /** \brief Return true if v is the value one; */ bool is_one(value * v) const { return const_cast(this)->compare(v, one()) == 0; } /** \brief Return true if p is the constant polynomial where the coefficient is the rational value 1. \remark This is NOT checking whether p is actually equal to 1. That is, it is just checking the representation. */ bool is_rational_one(polynomial const & p) const { return p.size() == 1 && is_rational_one(p[0]); } bool is_rational_one(value_ref_buffer const & p) const { return p.size() == 1 && is_rational_one(p[0]); } bool is_denominator_one(rational_function_value * v) const { if (v->ext()->is_algebraic()) { SASSERT(v->den().size() == 0); // we do not use denominator for algebraic extensions return true; } else { return is_rational_one(v->den()); } } template bool is_one(polynomial const & p) const { return p.size() == 1 && is_one(p[0]); } /** \brief Return true if v is a represented as a rational function of the set of field extensions. */ static bool is_rational_function(value * v) { SASSERT(v != 0); return !(v->is_rational()); } static rational_value * to_nz_rational(value * v) { SASSERT(is_nz_rational(v)); return static_cast(v); } static rational_function_value * to_rational_function(value * v) { SASSERT(!is_nz_rational(v)); return static_cast(v); } static bool is_zero(numeral const & a) { return is_zero(a.m_value); } static bool is_nz_rational(numeral const & a) { SASSERT(!is_zero(a)); return is_nz_rational(a.m_value); } /** \brief Return true if v is not a shared value. That is, we can perform destructive updates. */ static bool is_unique(value * v) { SASSERT(v); return v->m_ref_count <= 1; } static bool is_unique(numeral const & a) { return is_unique(a.m_value); } static bool is_unique_nz_rational(value * v) { return is_nz_rational(v) && is_unique(v); } static bool is_unique_nz_rational(numeral const & a) { return is_unique_nz_rational(a.m_value); } static rational_value * to_nz_rational(numeral const & a) { SASSERT(is_nz_rational(a)); return to_nz_rational(a.m_value); } static bool is_rational_function(numeral const & a) { return is_rational_function(a.m_value); } static rational_function_value * to_rational_function(numeral const & a) { SASSERT(is_rational_function(a)); return to_rational_function(a.m_value); } static mpq & to_mpq(value * v) { SASSERT(is_nz_rational(v)); return to_nz_rational(v)->m_value; } static mpq & to_mpq(numeral const & a) { SASSERT(is_nz_rational(a)); return to_nz_rational(a)->m_value; } static int compare_rank(value * a, value * b) { SASSERT(a); SASSERT(b); if (is_nz_rational(a)) return is_nz_rational(b) ? 0 : -1; else if (is_nz_rational(b)) { SASSERT(is_rational_function(a)); return 1; } else if (rank_eq(to_rational_function(a)->ext(), to_rational_function(b)->ext())) return 0; else return rank_lt(to_rational_function(a)->ext(), to_rational_function(b)->ext()) ? -1 : 1; } static transcendental * to_transcendental(extension * ext) { SASSERT(ext->is_transcendental()); return static_cast(ext); } static infinitesimal * to_infinitesimal(extension * ext) { SASSERT(ext->is_infinitesimal()); return static_cast(ext); } static algebraic * to_algebraic(extension * ext) { SASSERT(ext->is_algebraic()); return static_cast(ext); } /** \brief Return True if the given extension depends on infinitesimal extensions. If it doesn't, then it is definitely a real value. If it does, then it may or may not be a real value. Example: Assume eps is an infinitesimal, and pi is 3.14... . Assume also that ext is the unique root between (3, 4) of the following polynomial: x^2 - (pi + eps)*x + pi*ext Thus, x is pi, but the system will return true, since its defining polynomial has infinitesimal coefficients. In the future, we should be able to factor the polynomial above as (x - eps)*(x - pi) and then detect that x is actually the root of (x - pi). */ bool depends_on_infinitesimals(extension * ext) { switch (ext->knd()) { case extension::TRANSCENDENTAL: return false; case extension::INFINITESIMAL: return true; case extension::ALGEBRAIC: return to_algebraic(ext)->depends_on_infinitesimals(); default: UNREACHABLE(); return false; } } /** \brief Return true if v is definitely a real value. */ bool depends_on_infinitesimals(value * v) const { if (is_zero(v) || is_nz_rational(v)) return false; else return to_rational_function(v)->depends_on_infinitesimals(); } bool depends_on_infinitesimals(unsigned sz, value * const * p) const { for (unsigned i = 0; i < sz; i++) if (depends_on_infinitesimals(p[i])) return true; return false; } /** \brief Set the polynomial p with the given coefficients as[0], ..., as[n-1] */ void set_p(polynomial & p, unsigned n, value * const * as) { SASSERT(n > 0); SASSERT(!is_zero(as[n - 1])); reset_p(p); p.set(allocator(), n, as); inc_ref(n, as); } /** \brief Return true if a is an open interval. */ static bool is_open_interval(mpbqi const & a) { return a.lower_is_inf() && a.upper_is_inf(); } /** \brief Return true if the interval contains zero. */ bool contains_zero(mpbqi const & a) const { return bqim().contains_zero(a); } /** \brief Set the lower bound of the given interval. */ void set_lower_core(mpbqi & a, mpbq const & k, bool open, bool inf) { bqm().set(a.lower(), k); a.set_lower_is_open(open); a.set_lower_is_inf(inf); } /** \brief a.lower <- k */ void set_lower(mpbqi & a, mpbq const & k, bool open = true) { set_lower_core(a, k, open, false); } /** \brief a.lower <- -oo */ void set_lower_inf(mpbqi & a) { bqm().reset(a.lower()); a.set_lower_is_open(true); a.set_lower_is_inf(true); } /** \brief a.lower <- 0 */ void set_lower_zero(mpbqi & a) { bqm().reset(a.lower()); a.set_lower_is_open(true); a.set_lower_is_inf(false); } /** \brief Set the upper bound of the given interval. */ void set_upper_core(mpbqi & a, mpbq const & k, bool open, bool inf) { bqm().set(a.upper(), k); a.set_upper_is_open(open); a.set_upper_is_inf(inf); } /** \brief a.upper <- k */ void set_upper(mpbqi & a, mpbq const & k, bool open = true) { set_upper_core(a, k, open, false); } /** \brief a.upper <- oo */ void set_upper_inf(mpbqi & a) { bqm().reset(a.upper()); a.set_upper_is_open(true); a.set_upper_is_inf(true); } /** \brief a.upper <- 0 */ void set_upper_zero(mpbqi & a) { bqm().reset(a.upper()); a.set_upper_is_open(true); a.set_upper_is_inf(false); } /** \brief a <- b */ void set_interval(mpbqi & a, mpbqi const & b) { set_lower_core(a, b.lower(), b.lower_is_open(), b.lower_is_inf()); set_upper_core(a, b.upper(), b.upper_is_open(), b.upper_is_inf()); } /** \brief a <- [b, b] */ void set_interval(mpbqi & a, mpbq const & b) { set_lower_core(a, b, false, false); set_upper_core(a, b, false, false); } /** \brief a <- [b, b] */ void set_interval(mpbqi & a, mpz const & b) { scoped_mpbq _b(bqm()); bqm().set(_b, b); set_lower_core(a, _b, false, false); set_upper_core(a, _b, false, false); } sign_condition * mk_sign_condition(unsigned qidx, int sign, sign_condition * prev_sc) { return new (allocator()) sign_condition(qidx, sign, prev_sc); } /** \brief Make a rational_function_value using the given extension, numerator and denominator. This method does not set the interval. It remains (-oo, oo) */ rational_function_value * mk_rational_function_value_core(extension * ext, unsigned num_sz, value * const * num, unsigned den_sz, value * const * den) { rational_function_value * r = new (allocator()) rational_function_value(ext); inc_ref(ext); set_p(r->num(), num_sz, num); if (ext->is_algebraic()) { // Avoiding wasteful allocation... // We do not use the denominator for algebraic extensions SASSERT(den_sz == 0 || (den_sz == 1 && is_rational_one(den[0]))); SASSERT(r->den().size() == 0); } else { set_p(r->den(), den_sz, den); } r->set_depends_on_infinitesimals(depends_on_infinitesimals(ext) || depends_on_infinitesimals(num_sz, num) || depends_on_infinitesimals(den_sz, den)); return r; } rational_function_value * mk_rational_function_value_core(algebraic * ext, unsigned num_sz, value * const * num) { return mk_rational_function_value_core(ext, num_sz, num, 0, nullptr); } /** \brief Create a value using the given extension. */ rational_function_value * mk_rational_function_value(extension * ext) { value * num[2] = { nullptr, one() }; value * den[1] = { one() }; rational_function_value * v = mk_rational_function_value_core(ext, 2, num, 1, den); set_interval(v->interval(), ext->interval()); return v; } /** \brief Create a new infinitesimal. */ void mk_infinitesimal(symbol const & n, symbol const & pp_n, numeral & r) { unsigned idx = next_infinitesimal_idx(); infinitesimal * eps = new (allocator()) infinitesimal(idx, n, pp_n); m_extensions[extension::INFINITESIMAL].push_back(eps); set_lower(eps->interval(), mpbq(0)); set_upper(eps->interval(), mpbq(1, m_ini_precision)); set(r, mk_rational_function_value(eps)); SASSERT(sign(r) > 0); SASSERT(depends_on_infinitesimals(r)); } void mk_infinitesimal(char const * n, char const * pp_n, numeral & r) { mk_infinitesimal(symbol(n), symbol(pp_n), r); } void mk_infinitesimal(numeral & r) { mk_infinitesimal(symbol(next_infinitesimal_idx()+1), symbol(next_infinitesimal_idx()+1), r); } void refine_transcendental_interval(transcendental * t) { scoped_mpqi i(qim()); t->m_k++; t->m_proc(t->m_k, qim(), i); int m = magnitude(i); TRACE("rcf_transcendental", tout << "refine_transcendental_interval rational: " << m << "\nrational interval: "; qim().display(tout, i); tout << std::endl;); unsigned k; if (m >= 0) k = m_ini_precision; else k = inc_precision(-m, 8); scoped_mpbq l(bqm()); mpq_to_mpbqi(i->m_lower, t->interval(), k); // save lower bqm().set(l, t->interval().lower()); mpq_to_mpbqi(i->m_upper, t->interval(), k); bqm().set(t->interval().lower(), l); } void refine_transcendental_interval(transcendental * t, unsigned prec) { while (!check_precision(t->interval(), prec)) { TRACE("rcf_transcendental", tout << "refine_transcendental_interval: " << magnitude(t->interval()) << std::endl;); checkpoint(); save_interval_if_too_small(t, prec); refine_transcendental_interval(t); } } void mk_transcendental(symbol const & n, symbol const & pp_n, mk_interval & proc, numeral & r) { unsigned idx = next_transcendental_idx(); transcendental * t = new (allocator()) transcendental(idx, n, pp_n, proc); m_extensions[extension::TRANSCENDENTAL].push_back(t); while (contains_zero(t->interval())) { checkpoint(); refine_transcendental_interval(t); } set(r, mk_rational_function_value(t)); SASSERT(!depends_on_infinitesimals(r)); } void mk_transcendental(char const * p, char const * pp_n, mk_interval & proc, numeral & r) { mk_transcendental(symbol(p), symbol(pp_n), proc, r); } void mk_transcendental(mk_interval & proc, numeral & r) { mk_transcendental(symbol(next_transcendental_idx()+1), symbol(next_transcendental_idx()+1), proc, r); } void mk_pi(numeral & r) { if (m_pi) { set(r, m_pi); } else { mk_transcendental(symbol("pi"), symbol("π"), m_mk_pi_interval, r); m_pi = r.m_value; inc_ref(m_pi); } } void mk_e(numeral & r) { if (m_e) { set(r, m_e); } else { mk_transcendental(symbol("e"), symbol("e"), m_mk_e_interval, r); m_e = r.m_value; inc_ref(m_e); } } // --------------------------------- // // Root isolation // // --------------------------------- /** \brief r <- magnitude of the lower bound of |i|. That is, 2^r <= |i|.lower() Another way to view it is: 2^r is smaller than the absolute value of any element in the interval i. Return true if succeeded, and false if i contains elements that are infinitely close to 0. \pre !contains_zero(i) */ bool abs_lower_magnitude(mpbqi const & i, int & r) { SASSERT(!contains_zero(i)); if (bqim().is_P(i)) { if (bqm().is_zero(i.lower())) return false; r = bqm().magnitude_lb(i.lower()); return true; } else { SASSERT(bqim().is_N(i)); if (bqm().is_zero(i.upper())) return false; scoped_mpbq tmp(bqm()); tmp = i.upper(); bqm().neg(tmp); r = bqm().magnitude_lb(tmp); return true; } } /** \brief r <- magnitude of the upper bound of |i|. That is, |i|.upper <= 2^r Another way to view it is: 2^r is bigger than the absolute value of any element in the interval i. Return true if succeeded, and false if i is unbounded. \pre !contains_zero(i) */ bool abs_upper_magnitude(mpbqi const & i, int & r) { SASSERT(!contains_zero(i)); if (bqim().is_P(i)) { if (i.upper_is_inf()) return false; r = bqm().magnitude_ub(i.upper()); return true; } else { SASSERT(bqim().is_N(i)); if (i.lower_is_inf()) return false; scoped_mpbq tmp(bqm()); tmp = i.lower(); bqm().neg(tmp); r = bqm().magnitude_ub(tmp); return true; } } /** \brief Find positive root upper bound using Knuth's approach. Given p(x) = a_n * x^n + a_{n-1} * x^{n-1} + ... + a_0 If a_n is positive, Let B = max({ (-a_{n-k}/a_n)^{1/k} | 1 <= k <= n, a_{n-k} < 0 }) Then, 2*B is a bound for the positive roots Similarly, if a_n is negative Let B = max({ (-a_{n-k}/a_n)^{1/k} | 1 <= k <= n, a_{n-k} > 0 }) Then, 2*B is a bound for the positive roots This procedure returns a N s.t. 2*B <= 2^N The computation is performed using the intervals associated with the coefficients of the polynomial. The procedure may fail if the interval for a_n is of the form (l, 0) or (0, u). Similarly, the procedure will fail if one of the a_{n-k} has an interval of the form (l, oo) or (-oo, u). Both cases can only happen if the values of the coefficients depend on infinitesimal values. */ bool pos_root_upper_bound(unsigned n, value * const * p, int & N) { SASSERT(n > 1); SASSERT(!is_zero(p[n-1])); int lc_sign = sign(p[n-1]); SASSERT(lc_sign != 0); int lc_mag; if (!abs_lower_magnitude(interval(p[n-1]), lc_mag)) return false; N = -static_cast(m_ini_precision); for (unsigned k = 2; k <= n; k++) { value * a = p[n - k]; if (!is_zero(a) && sign(a) != lc_sign) { int a_mag; if (!abs_upper_magnitude(interval(a), a_mag)) return false; int C = (a_mag - lc_mag)/static_cast(k) + 1 /* compensate imprecision on division */ + 1 /* Knuth's bound is 2 x Max {... } */; if (C > N) N = C; } } return true; } /** \brief Auxiliary method for creating the intervals of the coefficients of the polynomials p(-x) without actually creating p(-x). 'a' is the interval of the i-th coefficient of a polynomial a_n * x^n + ... + a_0 */ void neg_root_adjust(mpbqi const & a, unsigned i, mpbqi & r) { if (i % 2 == 0) bqim().neg(a, r); else bqim().set(r, a); } /** \brief Find negative root lower bound using Knuth's approach. This is similar to pos_root_upper_bound. In principle, we can use the same algorithm. We just have to adjust the coefficients by using the transformation p(-x). */ bool neg_root_lower_bound(unsigned n, value * const * as, int & N) { SASSERT(n > 1); SASSERT(!is_zero(as[n-1])); scoped_mpbqi aux(bqim()); neg_root_adjust(interval(as[n-1]), n-1, aux); int lc_sign = bqim().is_P(aux) ? 1 : -1; int lc_mag; if (!abs_lower_magnitude(aux, lc_mag)) return false; N = -static_cast(m_ini_precision); for (unsigned k = 2; k <= n; k++) { value * a = as[n - k]; if (!is_zero(a)) { neg_root_adjust(interval(as[n-k]), n-k, aux); int a_sign = bqim().is_P(aux) ? 1 : -1; if (a_sign != lc_sign) { int a_mag; if (!abs_upper_magnitude(aux, a_mag)) return false; int C = (a_mag - lc_mag)/static_cast(k) + 1 /* compensate imprecision on division */ + 1 /* Knuth's bound is 2 x Max {... } */; if (C > N) N = C; } } } return true; } /** \brief q <- x^{n-1}*p(1/x) Given p(x) a_{n-1} * x^{n-1} + ... + a_0, this method stores a_0 * x^{n-1} + ... + a_{n-1} into q. */ void reverse(unsigned n, value * const * p, value_ref_buffer & q) { unsigned i = n; while (i > 0) { --i; q.push_back(p[i]); } } /** \brief To compute the lower bound for positive roots we computer the upper bound for the polynomial q(x) = x^{n-1}*p(1/x). Assume U is an upper bound for roots of q(x), i.e., (r > 0 and q(r) = 0) implies r < U. Note that if r is a root for q(x), then 1/r is a root for p(x) and 1/U is a lower bound for positive roots of p(x). The polynomial q(x) is just p(x) "reversed". */ bool pos_root_lower_bound(unsigned n, value * const * p, int & N) { value_ref_buffer q(*this); reverse(n, p, q); if (pos_root_upper_bound(n, q.c_ptr(), N)) { N = -N; return true; } else { return false; } } /** \brief See comment on pos_root_lower_bound. */ bool neg_root_upper_bound(unsigned n, value * const * p, int & N) { value_ref_buffer q(*this); reverse(n, p, q); if (neg_root_lower_bound(n, q.c_ptr(), N)) { N = -N; return true; } else { return false; } } /** \brief Store in ds all (non-constant) derivatives of p. \post d.size() == n-2 */ void mk_derivatives(unsigned n, value * const * p, scoped_polynomial_seq & ds) { SASSERT(n >= 3); // p is at least quadratic SASSERT(!is_zero(p[0])); SASSERT(!is_zero(p[n-1])); value_ref_buffer p_prime(*this); derivative(n, p, p_prime); ds.push(p_prime.size(), p_prime.c_ptr()); SASSERT(n >= 3); for (unsigned i = 0; i < n - 2; i++) { SASSERT(ds.size() > 0); unsigned prev = ds.size() - 1; n = ds.size(prev); p = ds.coeffs(prev); derivative(n, p, p_prime); ds.push(p_prime.size(), p_prime.c_ptr()); } } /** \brief Auxiliary function for count_signs_at_zeros. (See comments at count_signs_at_zeros). - taq_p_q contains TaQ(p, q; interval) */ void count_signs_at_zeros_core(// Input values int taq_p_q, unsigned p_sz, value * const * p, // polynomial p unsigned q_sz, value * const * q, // polynomial q mpbqi const & interval, int num_roots, // number of roots of p in the given interval // Output values int & q_eq_0, int & q_gt_0, int & q_lt_0, value_ref_buffer & q2) { if (taq_p_q == num_roots) { // q is positive in all roots of p q_eq_0 = 0; q_gt_0 = num_roots; q_lt_0 = 0; } else if (taq_p_q == -num_roots) { // q is negative in all roots of p q_eq_0 = 0; q_gt_0 = 0; q_lt_0 = num_roots; } if (taq_p_q == num_roots - 1) { // The following assignment is the only possibility q_eq_0 = 1; q_gt_0 = num_roots - 1; q_lt_0 = 0; } else if (taq_p_q == -(num_roots - 1)) { // The following assignment is the only possibility q_eq_0 = 1; q_gt_0 = 0; q_lt_0 = num_roots - 1; } else { // Expensive case // q2 <- q^2 mul(q_sz, q, q_sz, q, q2); int taq_p_q2 = TaQ(p_sz, p, q2.size(), q2.c_ptr(), interval); SASSERT(0 <= taq_p_q2 && taq_p_q2 <= num_roots); // taq_p_q2 == q_gt_0 + q_lt_0 SASSERT((taq_p_q2 + taq_p_q) % 2 == 0); SASSERT((taq_p_q2 - taq_p_q) % 2 == 0); q_eq_0 = num_roots - taq_p_q2; q_gt_0 = (taq_p_q2 + taq_p_q)/2; q_lt_0 = (taq_p_q2 - taq_p_q)/2; } SASSERT(q_eq_0 + q_gt_0 + q_lt_0 == num_roots); } /** \brief Given polynomials p and q, and an interval, compute the number of roots of p in the interval such that: - q is zero - q is positive - q is negative \pre num_roots is the number of roots of p in the given interval. \remark num_roots == q_eq_0 + q_gt_0 + q_lt_0 */ void count_signs_at_zeros(// Input values unsigned p_sz, value * const * p, // polynomial p unsigned q_sz, value * const * q, // polynomial q mpbqi const & interval, int num_roots, // number of roots of p in the given interval // Output values int & q_eq_0, int & q_gt_0, int & q_lt_0, value_ref_buffer & q2) { TRACE("rcf_count_signs", tout << "p: "; display_poly(tout, p_sz, p); tout << "\n"; tout << "q: "; display_poly(tout, q_sz, q); tout << "\n";); SASSERT(num_roots > 0); int taq_p_q = TaQ(p_sz, p, q_sz, q, interval); count_signs_at_zeros_core(taq_p_q, p_sz, p, q_sz, q, interval, num_roots, q_eq_0, q_gt_0, q_lt_0, q2); } /** \brief Expand the set of Tarski Queries used in the sign determination algorithm. taqrs contains the results of TaQ(p, prs[i]; interval) We have that taqrs.size() == prs.size() We produce a new_taqrs and new_prs For each pr in new_prs we have pr in new_prs, TaQ(p, pr; interval) in new_taqrs pr*q in new_prs, TaQ(p, pr*q; interval) in new_taqrs if q2_sz != 0, we also have pr*q^2 in new_prs, TaQ(p, pr*q^2; interval) in new_taqrs */ void expand_taqrs(// Input values int_buffer const & taqrs, scoped_polynomial_seq const & prs, unsigned p_sz, value * const * p, unsigned q_sz, value * const * q, bool use_q2, unsigned q2_sz, value * const * q2, mpbqi const & interval, // Output values int_buffer & new_taqrs, scoped_polynomial_seq & new_prs ) { SASSERT(taqrs.size() == prs.size()); new_taqrs.reset(); new_prs.reset(); for (unsigned i = 0; i < taqrs.size(); i++) { // Add prs * 1 new_taqrs.push_back(taqrs[i]); new_prs.push(prs.size(i), prs.coeffs(i)); // Add prs * q value_ref_buffer prq(*this); mul(prs.size(i), prs.coeffs(i), q_sz, q, prq); new_taqrs.push_back(TaQ(p_sz, p, prq.size(), prq.c_ptr(), interval)); new_prs.push(prq.size(), prq.c_ptr()); // If use_q2, // Add prs * q^2 if (use_q2) { value_ref_buffer prq2(*this); mul(prs.size(i), prs.coeffs(i), q2_sz, q2, prq2); new_taqrs.push_back(TaQ(p_sz, p, prq2.size(), prq2.c_ptr(), interval)); new_prs.push(prq2.size(), prq2.c_ptr()); } } SASSERT(new_prs.size() == new_taqrs.size()); SASSERT(use_q2 || new_prs.size() == 2*prs.size()); SASSERT(!use_q2 || new_prs.size() == 3*prs.size()); } /** \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 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 sc1_1 and q == 0, sc_1 and q > 0, sc_1 and q < 0, ..., sc_n and q == 0, sc_n and q > 0, sc_n and q < 0 If q2_used is false, then we considered only two possible signs of q. 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. 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); SASSERT(!q2_used || sz % 3 == 0); if (q2_used) { for (unsigned i = 0; i < sz; i += 3) { unsigned c = 0; if (sc_cardinalities[i] > 0) c++; if (sc_cardinalities[i+1] > 0) c++; if (sc_cardinalities[i+2] > 0) c++; if (c >= 2) return true; } } else { for (unsigned i = 0; i < sz; i += 2) { if (sc_cardinalities[i] > 0 && sc_cardinalities[i+1] > 0) return true; } } return false; } /** \brief Store the polynomials in prs into the array of polynomials ps. */ void set_array_p(array & ps, scoped_polynomial_seq const & prs) { unsigned sz = prs.size(); ps.set(allocator(), sz, polynomial()); for (unsigned i = 0; i < sz; i++) { unsigned pi_sz = prs.size(i); value * const * pi = prs.coeffs(i); set_p(ps[i], pi_sz, pi); } } /** \brief Create a "sign determination" data-structure for an algebraic extension. The new object will assume the ownership of the elements stored in M and scs. M and scs will be empty after this operation. */ sign_det * mk_sign_det(mpz_matrix & M_s, scoped_polynomial_seq const & prs, int_buffer const & taqrs, scoped_polynomial_seq const & qs, scoped_sign_conditions & scs) { sign_det * r = new (allocator()) sign_det(); r->M_s.swap(M_s); set_array_p(r->m_prs, prs); r->m_taqrs.set(allocator(), taqrs.size(), taqrs.c_ptr()); set_array_p(r->m_qs, qs); r->m_sign_conditions.set(allocator(), scs.size(), scs.c_ptr()); scs.release(); return r; } /** \brief Create a new algebraic extension */ algebraic * mk_algebraic(unsigned p_sz, value * const * p, mpbqi const & interval, mpbqi const & iso_interval, sign_det * sd, unsigned sc_idx) { unsigned idx = next_algebraic_idx(); algebraic * r = new (allocator()) algebraic(idx); m_extensions[extension::ALGEBRAIC].push_back(r); set_p(r->m_p, p_sz, p); set_interval(r->m_interval, interval); set_interval(r->m_iso_interval, iso_interval); r->m_sign_det = sd; inc_ref_sign_det(sd); r->m_sc_idx = sc_idx; r->m_depends_on_infinitesimals = depends_on_infinitesimals(p_sz, p); return r; } /** \brief Add a new root of p that is isolated by (interval, sd, sc_idx) to roots. */ void add_root(unsigned p_sz, value * const * p, mpbqi const & interval, mpbqi const & iso_interval, sign_det * sd, unsigned sc_idx, numeral_vector & roots) { algebraic * a = mk_algebraic(p_sz, p, interval, iso_interval, sd, sc_idx); numeral r; set(r, mk_rational_function_value(a)); roots.push_back(r); } /** \brief Simpler version of add_root that does not use sign_det data-structure. That is, interval contains only one root of p. */ void add_root(unsigned p_sz, value * const * p, mpbqi const & interval, mpbqi const & iso_interval, numeral_vector & roots) { add_root(p_sz, p, interval, iso_interval, nullptr, UINT_MAX, roots); } /** \brief Create (the square) matrix for sign determination of q on the roots of p. It builds matrix based on the number of root of p where q is == 0, > 0 and < 0. The resultant matrix is stored in M. Return false if the sign of q is already determined, that is only one of the q_eq_0, q_gt_0, q_lt_0 is greater than zero. If the return value is true, then the resultant matrix M has size 2x2 or 3x3 - q_eq_0 > 0, q_gt_0 > 0, q_lt_0 == 0 M <- {{1, 1}, {0, 1}} Meaning: M . [ #(q == 0), #(q > 0) ]^t == [ TaQ(p, 1), TaQ(p, q) ]^t [ ... ]^t represents a column matrix. - q_eq_0 > 0, q_gt_0 == 0, q_lt_0 > 0 M <- {{1, 1}, {0, -1}} Meaning: M . [ #(q == 0), #(q < 0) ]^t == [ TaQ(p, 1), TaQ(p, q) ]^t - q_eq_0 == 0, q_gt_0 > 0, q_lt_0 > 0 M <- {{1, 1}, {1, -1}} Meaning: M . [ #(q > 0), #(q < 0) ]^t == [ TaQ(p, 1), TaQ(p, q) ]^t - q_eq_0 > 0, q_gt_0 > 0, q_lt_0 > 0 M <- {{1, 1, 1}, {0, 1, -1}, {0, 1, 1}} Meaning: M . [ #(q == 0), #(q > 0), #(q < 0) ]^t == [ TaQ(p, 1), TaQ(p, q), TaQ(p, q^2) ]^t */ bool mk_sign_det_matrix(int q_eq_0, int q_gt_0, int q_lt_0, scoped_mpz_matrix & M) { if (q_eq_0 > 0 && q_gt_0 > 0 && q_lt_0 == 0) { // M <- {{1, 1}, // {0, 1}} mm().mk(2,2,M); M.set(0,0, 1); M.set(0,1, 1); M.set(1,0, 0); M.set(1,1, 1); return true; } else if (q_eq_0 > 0 && q_gt_0 == 0 && q_lt_0 > 0) { // M <- {{1, 1}, // {0, -1}} mm().mk(2,2,M); M.set(0,0, 1); M.set(0,1, 1); M.set(1,0, 0); M.set(1,1, -1); return true; } else if (q_eq_0 == 0 && q_gt_0 > 0 && q_lt_0 > 0) { // M <- {{1, 1}, // {1, -1}} mm().mk(2,2,M); M.set(0,0, 1); M.set(0,1, 1); M.set(1,0, 1); M.set(1,1, -1); return true; } else if (q_eq_0 > 0 && q_gt_0 > 0 && q_lt_0 > 0) { // M <- {{1, 1, 1}, // {0, 1, -1}, // {0, 1, 1}} mm().mk(3,3,M); M.set(0,0, 1); M.set(0,1, 1); M.set(0,2, 1); M.set(1,0, 0); M.set(1,1, 1); M.set(1,2, -1); M.set(2,0, 0); M.set(2,1, 1); M.set(2,2, 1); return true; } else { // Sign of q is already determined. return false; } } /** \brief Isolate roots of p in the given interval using sign conditions to distinguish between them. We need this method when the polynomial contains roots that are infinitesimally close to each other. For example, given an infinitesimal eps, the polynomial (x - 1)(x - 1 - eps) == (1 + eps) + (- 2 - eps)*x + x^2 has two roots 1 and 1+eps, we can't isolate these roots using intervals with binary rational end points. In this case, we use the signs of (some of the) derivatives in the roots. By Thom's lemma, we know we can always use the signs of the derivatives to distinguish between different roots. Remark: the polynomials do not need to be the derivatives of p. We use derivatives because a simple sequential search can be used to find the set of polynomials that can be used to distinguish between the different roots. \pre num_roots is the number of roots in the given interval */ void sign_det_isolate_roots(unsigned p_sz, value * const * p, int num_roots, mpbqi const & interval, mpbqi const & iso_interval, numeral_vector & roots) { SASSERT(num_roots >= 2); scoped_polynomial_seq der_seq(*this); mk_derivatives(p_sz, p, der_seq); CASSERT("rcf_isolate_roots", TaQ_1(p_sz, p, interval) == num_roots); scoped_mpz_matrix M_s(mm()); mm().mk(1, 1, M_s); M_s.set(0, 0, 1); // Sequence of polynomials associated with each row of M_s scoped_polynomial_seq prs(*this); value * one_p[] = { one() }; prs.push(1, one_p); // start with the polynomial one // For i in [0, poly_rows.size()), taqrs[i] == TaQ(prs[i], p; interval) int_buffer taqrs; taqrs.push_back(num_roots); // Sequence of polynomials used to discriminate the roots of p scoped_polynomial_seq qs(*this); // Sequence of sign conditions associated with the columns of M_s // These are sign conditions on the polynomials in qs. scoped_sign_conditions scs(*this); scs.push_back(nullptr); // Starting configuration // // M_s = {{1}} Matrix of size 1x1 containing the value 1 // prs = [1] Sequence of size 1 containing the constant polynomial 1 (one is always the first element of this sequence) // taqrs = [num_roots] Sequence of size 1 containing the integer num_roots // scs = [0] Sequence of size 1 with the empty sign condition (i.e., NULL pointer) // qs = {} Empty set // scoped_mpz_matrix new_M_s(mm()); int_buffer new_taqrs; scoped_polynomial_seq new_prs(*this); scoped_sign_conditions new_scs(*this); int_buffer sc_cardinalities; unsigned_buffer cols_to_keep; unsigned_buffer new_row_idxs; unsigned i = der_seq.size(); // We perform the search backwards because the degrees are in decreasing order. while (i > 0) { --i; checkpoint(); SASSERT(M_s.m() == M_s.n()); SASSERT(M_s.m() == taqrs.size()); SASSERT(M_s.m() == scs.size()); TRACE("rcf_sign_det", tout << M_s; for (unsigned j = 0; j < scs.size(); j++) { display_sign_conditions(tout, scs[j]); tout << " = " << taqrs[j] << "\n"; } tout << "qs:\n"; for (unsigned j = 0; j < qs.size(); j++) { display_poly(tout, qs.size(j), qs.coeffs(j)); tout << "\n"; }); // We keep executing this loop until we have only one root for each sign condition in scs. // When this happens the polynomials in qs are the ones used to discriminate the roots of p. unsigned q_sz = der_seq.size(i); value * const * q = der_seq.coeffs(i); // q is a derivative of p. int q_eq_0, q_gt_0, q_lt_0; value_ref_buffer q2(*this); count_signs_at_zeros(p_sz, p, q_sz, q, iso_interval, num_roots, q_eq_0, q_gt_0, q_lt_0, q2); TRACE("rcf_sign_det", tout << "q: "; display_poly(tout, q_sz, q); tout << "\n"; tout << "#(q == 0): " << q_eq_0 << ", #(q > 0): " << q_gt_0 << ", #(q < 0): " << q_lt_0 << "\n";); scoped_mpz_matrix M(mm()); if (!mk_sign_det_matrix(q_eq_0, q_gt_0, q_lt_0, M)) { // skip q since its sign does not discriminate the roots of p continue; } bool use_q2 = M.n() == 3; mm().tensor_product(M_s, M, new_M_s); expand_taqrs(taqrs, prs, p_sz, p, q_sz, q, use_q2, q2.size(), q2.c_ptr(), iso_interval, // ---> new_taqrs, new_prs); SASSERT(new_M_s.n() == new_M_s.m()); // it is a square matrix SASSERT(new_M_s.m() == new_taqrs.size()); SASSERT(new_M_s.m() == new_prs.size()); // The system must have a solution sc_cardinalities.resize(new_taqrs.size(), 0); // Solve // new_M_s * sc_cardinalities = new_taqrs VERIFY(mm().solve(new_M_s, sc_cardinalities.c_ptr(), new_taqrs.c_ptr())); TRACE("rcf_sign_det", tout << "solution: "; for (unsigned i = 0; i < sc_cardinalities.size(); i++) { tout << sc_cardinalities[i] << " "; } tout << "\n";); // The solution must contain only positive values <= num_roots DEBUG_CODE(for (unsigned j = 0; j < sc_cardinalities.size(); j++) { SASSERT(0 <= sc_cardinalities[j] && sc_cardinalities[j] <= num_roots); }); // 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. 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; } // keep q unsigned q_idx = qs.size(); qs.push(q_sz, q); // We remove the columns associated with sign conditions that have cardinality zero, // and create new extended sign condition objects for the ones that have cardinality > 0. cols_to_keep.reset(); unsigned j = 0; unsigned k = 0; unsigned step_sz = use_q2 ? 3 : 2; bool all_one = true; while (j < sc_cardinalities.size()) { sign_condition * sc = scs[k]; k++; for (unsigned s = 0; s < step_sz; s++) { // Remark: the second row of M contains the sign for q if (sc_cardinalities[j] > 0) { new_scs.push_back(mk_sign_condition(q_idx, M.get_int(1, s), sc)); cols_to_keep.push_back(j); } if (sc_cardinalities[j] > 1) all_one = false; j++; } } // Update scs with new_scs scs.copy_from(new_scs); SASSERT(new_scs.empty()); // Update M_s mm().filter_cols(new_M_s, cols_to_keep.size(), cols_to_keep.c_ptr(), M_s); SASSERT(M_s.n() == cols_to_keep.size()); new_row_idxs.resize(cols_to_keep.size(), 0); unsigned new_num_rows = mm().linear_independent_rows(M_s, new_row_idxs.c_ptr(), M_s); SASSERT(new_num_rows == cols_to_keep.size()); // Update taqrs and prs prs.reset(); taqrs.reset(); for (unsigned j = 0; j < new_num_rows; j++) { unsigned rid = new_row_idxs[j]; prs.push(new_prs.size(rid), new_prs.coeffs(rid)); taqrs.push_back(new_taqrs[rid]); } if (all_one) { // Stop each remaining sign condition in scs has cardinality one // So, they are discriminating the roots of p. break; } } TRACE("rcf_sign_det", tout << "Final state\n"; display_poly(tout, p_sz, p); tout << "\n"; tout << M_s; for (unsigned j = 0; j < scs.size(); j++) { display_sign_conditions(tout, scs[j]); tout << " = " << taqrs[j] << "\n"; } tout << "qs:\n"; for (unsigned j = 0; j < qs.size(); j++) { display_poly(tout, qs.size(j), qs.coeffs(j)); tout << "\n"; } tout << "prs:\n"; for (unsigned j = 0; j < prs.size(); j++) { display_poly(tout, prs.size(j), prs.coeffs(j)); tout << "\n"; }); SASSERT(M_s.n() == M_s.m()); SASSERT(M_s.n() == static_cast(num_roots)); sign_det * sd = mk_sign_det(M_s, prs, taqrs, qs, scs); for (unsigned idx = 0; idx < static_cast(num_roots); idx++) { add_root(p_sz, p, interval, iso_interval, sd, idx, roots); } } /** \brief Return true if p is a polynomial of the form a_{n-1}*x^{n-1} + a_0 */ bool is_nz_binomial(unsigned n, value * const * p) { SASSERT(n >= 2); SASSERT(!is_zero(p[0])); SASSERT(!is_zero(p[n-1])); for (unsigned i = 1; i < n - 1; i++) { if (!is_zero(p[i])) return false; } return true; } /** \brief magnitude -> mpbq */ void magnitude_to_mpbq(int mag, bool sign, mpbq & r) { if (mag < 0) { bqm().set(r, mpbq(1, -mag)); } else { bqm().set(r, mpbq(2)); bqm().power(r, mag); } if (sign) bqm().neg(r); } /** \brief Convert magnitudes for negative roots lower and upper bounds into an interval. */ void mk_neg_interval(bool has_neg_lower, int neg_lower_N, bool has_neg_upper, int neg_upper_N, mpbqi & r) { scoped_mpbq aux(bqm()); if (!has_neg_lower) { set_lower_inf(r); } else { magnitude_to_mpbq(neg_lower_N, true, aux); set_lower(r, aux); } if (!has_neg_upper) { set_upper_zero(r); } else { magnitude_to_mpbq(neg_upper_N, true, aux); set_upper(r, aux); } } /** \brief Convert magnitudes for negative roots lower and upper bounds into an interval. */ void mk_pos_interval(bool has_pos_lower, int pos_lower_N, bool has_pos_upper, int pos_upper_N, mpbqi & r) { scoped_mpbq aux(bqm()); if (!has_pos_lower) { set_lower_zero(r); } else { magnitude_to_mpbq(pos_lower_N, false, aux); set_lower(r, aux); } if (!has_pos_upper) { set_upper_inf(r); } else { magnitude_to_mpbq(pos_upper_N, false, aux); set_upper(r, aux); } } struct bisect_ctx { unsigned m_p_sz; value * const * m_p; bool m_depends_on_infinitesimals; scoped_polynomial_seq & m_sturm_seq; numeral_vector & m_result_roots; bisect_ctx(unsigned p_sz, value * const * p, bool dinf, scoped_polynomial_seq & seq, numeral_vector & roots): m_p_sz(p_sz), m_p(p), m_depends_on_infinitesimals(dinf), m_sturm_seq(seq), m_result_roots(roots) {} }; void bisect_isolate_roots(mpbqi & interval, mpbqi & iso_interval, int lower_sv, int upper_sv, bisect_ctx & ctx) { SASSERT(lower_sv >= upper_sv); int num_roots = lower_sv - upper_sv; if (num_roots == 0) { // interval does not contain roots } else if (num_roots == 1) { // Sturm sequences are for half-open intervals (a, b] // We must check if upper is the root if (eval_sign_at(ctx.m_p_sz, ctx.m_p, interval.upper()) == 0) { // found precise root numeral r; set(r, mk_rational(interval.upper())); ctx.m_result_roots.push_back(r); } else { // interval is an isolating interval add_root(ctx.m_p_sz, ctx.m_p, interval, iso_interval, ctx.m_result_roots); } } else if (ctx.m_depends_on_infinitesimals && check_precision(interval, m_max_precision)) { // IF // - The polynomial depends on infinitesimals // - The interval contains more than one root // - The size of the interval is less than 1/2^m_max_precision // THEN // - We switch to expensive sign determination procedure, since // the roots may be infinitely close to each other. // sign_det_isolate_roots(ctx.m_p_sz, ctx.m_p, num_roots, interval, iso_interval, ctx.m_result_roots); } else { scoped_mpbq mid(bqm()); bqm().add(interval.lower(), interval.upper(), mid); bqm().div2(mid); int mid_sv = sign_variations_at(ctx.m_sturm_seq, mid); int num_left_roots = lower_sv - mid_sv; int num_right_roots = mid_sv - upper_sv; if (num_left_roots == 0) { scoped_mpbqi right_interval(bqim()); set_lower(right_interval, mid); set_upper(right_interval, interval.upper()); bisect_isolate_roots(right_interval, iso_interval, mid_sv, upper_sv, ctx); } else if (num_right_roots == 0) { scoped_mpbqi left_interval(bqim()); set_lower(left_interval, interval.lower()); set_upper(left_interval, mid); bisect_isolate_roots(left_interval, iso_interval, lower_sv, mid_sv, ctx); } else { scoped_mpbqi left_interval(bqim()); scoped_mpbqi right_interval(bqim()); set_lower(left_interval, interval.lower()); set_upper(left_interval, mid); set_lower(right_interval, mid); set_upper(right_interval, interval.upper()); bisect_isolate_roots(left_interval, left_interval, lower_sv, mid_sv, ctx); bisect_isolate_roots(right_interval, right_interval, mid_sv, upper_sv, ctx); } } } /** \brief Entry point for the root isolation procedure based on bisection. */ void bisect_isolate_roots(// Input values unsigned p_sz, value * const * p, mpbqi & interval, mpbqi & iso_interval, // Extra Input values with already computed information scoped_polynomial_seq & sturm_seq, // sturm sequence for p int lower_sv, // number of sign variations at the lower bound of interval int upper_sv, // number of sign variations at the upper bound of interval // Output values numeral_vector & roots) { bool dinf = depends_on_infinitesimals(p_sz, p); bisect_ctx ctx(p_sz, p, dinf, sturm_seq, roots); bisect_isolate_roots(interval, iso_interval, lower_sv, upper_sv, ctx); } /** \brief Root isolation for polynomials that are - nonlinear (degree > 2) - zero is not a root - square free - nonconstant */ void nl_nz_sqf_isolate_roots(unsigned n, value * const * p, numeral_vector & roots) { SASSERT(n > 2); SASSERT(!is_zero(p[0])); SASSERT(!is_zero(p[n-1])); int pos_lower_N, pos_upper_N, neg_lower_N, neg_upper_N; bool has_neg_lower = neg_root_lower_bound(n, p, neg_lower_N); bool has_neg_upper = neg_root_upper_bound(n, p, neg_upper_N); bool has_pos_lower = pos_root_lower_bound(n, p, pos_lower_N); bool has_pos_upper = pos_root_upper_bound(n, p, pos_upper_N); TRACE("rcf_isolate", display_poly(tout, n, p); tout << "\n"; if (has_neg_lower) tout << "-2^" << neg_lower_N; else tout << "-oo"; tout << " < neg-roots < "; if (has_neg_upper) tout << "-2^" << neg_upper_N; else tout << "0"; tout << "\n"; if (has_pos_lower) tout << "2^" << pos_lower_N; else tout << "0"; tout << " < pos-roots < "; if (has_pos_upper) tout << "2^" << pos_upper_N; else tout << "oo"; tout << "\n";); // Compute the number of positive and negative roots scoped_polynomial_seq seq(*this); sturm_seq(n, p, seq); int num_sv_minus_inf = sign_variations_at_minus_inf(seq); int num_sv_zero = sign_variations_at_zero(seq); int num_sv_plus_inf = sign_variations_at_plus_inf(seq); int num_neg_roots = num_sv_minus_inf - num_sv_zero; int num_pos_roots = num_sv_zero - num_sv_plus_inf; TRACE("rcf_isolate", tout << "num_neg_roots: " << num_neg_roots << "\n"; tout << "num_pos_roots: " << num_pos_roots << "\n";); scoped_mpbqi pos_interval(bqim()); scoped_mpbqi neg_interval(bqim()); mk_neg_interval(has_neg_lower, neg_lower_N, has_neg_upper, neg_upper_N, neg_interval); mk_pos_interval(has_pos_lower, pos_lower_N, has_pos_upper, pos_upper_N, pos_interval); scoped_mpbqi minf_zero(bqim()); set_lower_inf(minf_zero); set_upper_zero(minf_zero); scoped_mpbqi zero_inf(bqim()); set_lower_zero(zero_inf); set_upper_inf(zero_inf); if (num_neg_roots > 0) { if (num_neg_roots == 1) { add_root(n, p, neg_interval, minf_zero, nullptr, UINT_MAX, roots); } else { if (has_neg_lower) { bisect_isolate_roots(n, p, neg_interval, minf_zero, seq, num_sv_minus_inf, num_sv_zero, roots); } else { sign_det_isolate_roots(n, p, num_neg_roots, minf_zero, minf_zero, roots); } } } if (num_pos_roots > 0) { if (num_pos_roots == 1) { add_root(n, p, pos_interval, zero_inf, nullptr, UINT_MAX, roots); } else { if (has_pos_upper) { bisect_isolate_roots(n, p, pos_interval, zero_inf, seq, num_sv_zero, num_sv_plus_inf, roots); } else { sign_det_isolate_roots(n, p, num_pos_roots, zero_inf, zero_inf, roots); } } } } /** \brief Root isolation for polynomials that are - zero is not a root - square free - nonconstant */ void nz_sqf_isolate_roots(unsigned n, value * const * p, numeral_vector & roots) { SASSERT(n > 1); SASSERT(!is_zero(p[0])); SASSERT(!is_zero(p[n-1])); if (n == 2) { // we don't need a field extension for linear polynomials. numeral r; value_ref v(*this); neg(p[0], v); div(v, p[1], v); set(r, v); roots.push_back(r); } else { nl_nz_sqf_isolate_roots(n, p, roots); } } /** \brief Root isolation for polynomials where 0 is not a root, and the denominators have been cleaned when m_clean_denominators == true */ void nz_cd_isolate_roots(unsigned n, value * const * p, numeral_vector & roots) { SASSERT(n > 0); SASSERT(!is_zero(p[0])); SASSERT(!is_zero(p[n-1])); SASSERT(!m_clean_denominators || has_clean_denominators(n, p)); if (n == 1) { // constant polynomial return; } value_ref_buffer sqf(*this); square_free(n, p, sqf); nz_sqf_isolate_roots(sqf.size(), sqf.c_ptr(), roots); } /** \brief Root isolation for polynomials where 0 is not a root. */ void nz_isolate_roots(unsigned n, value * const * p, numeral_vector & roots) { TRACE("rcf_isolate", tout << "nz_isolate_roots\n"; display_poly(tout, n, p); tout << "\n";); if (m_clean_denominators) { value_ref d(*this); value_ref_buffer norm_p(*this); clean_denominators(n, p, norm_p, d); nz_cd_isolate_roots(norm_p.size(), norm_p.c_ptr(), roots); } else { nz_cd_isolate_roots(n, p, roots); } } /** \brief Root isolation entry point. */ void isolate_roots(unsigned n, numeral const * p, numeral_vector & roots) { TRACE("rcf_isolate_bug", tout << "isolate_roots: "; for (unsigned i = 0; i < n; i++) { display(tout, p[i]); tout << " "; } tout << "\n";); SASSERT(n > 0); SASSERT(!is_zero(p[n-1])); if (n == 1) { // constant polynomial return; } unsigned i = 0; for (; i < n; i++) { if (!is_zero(p[i])) break; } SASSERT(i < n); SASSERT(!is_zero(p[i])); ptr_buffer nz_p; for (; i < n; i++) nz_p.push_back(p[i].m_value); nz_isolate_roots(nz_p.size(), nz_p.c_ptr(), roots); if (nz_p.size() < n) { // zero is a root roots.push_back(numeral()); } } // --------------------------------- // // Basic operations // // --------------------------------- void reset(numeral & a) { del(a); SASSERT(is_zero(a)); } int sign(value * a) { if (is_zero(a)) return 0; else if (is_nz_rational(a)) { return qm().is_pos(to_mpq(a)) ? 1 : -1; } else { SASSERT(!contains_zero(a->interval())); return bqim().is_P(a->interval()) ? 1 : -1; } } int sign(numeral const & a) { return sign(a.m_value); } /** \brief Return true the given rational function value is actually an integer. \pre a is a rational function (algebraic) extension. \remark If a is actually an integer, this method also updates its representation. */ bool is_algebraic_int(numeral const & a) { SASSERT(is_rational_function(a)); SASSERT(to_rational_function(a)->ext()->is_algebraic()); // TODO return false; } /** \brief Return true if a is an integer */ bool is_int(numeral const & a) { if (is_zero(a)) return true; else if (is_nz_rational(a)) return qm().is_int(to_mpq(a)); else { rational_function_value * rf = to_rational_function(a); switch (rf->ext()->knd()) { case extension::TRANSCENDENTAL: return false; case extension::INFINITESIMAL: return false; case extension::ALGEBRAIC: return is_algebraic_int(a); default: UNREACHABLE(); return false; } } } /** \brief Return true if a depends on infinitesimal extensions. */ bool depends_on_infinitesimals(numeral const & a) const { return depends_on_infinitesimals(a.m_value); } static void swap(mpbqi & a, mpbqi & b) { realclosure::swap(a, b); } /** \brief Store in interval an approximation of the rational number q with precision k. interval has binary rational end-points and the width is <= 1/2^k */ void mpq_to_mpbqi(mpq const & q, mpbqi & interval, unsigned k) { interval.set_lower_is_inf(false); interval.set_upper_is_inf(false); if (bqm().to_mpbq(q, interval.lower())) { bqm().set(interval.upper(), interval.lower()); interval.set_lower_is_open(false); interval.set_upper_is_open(false); } else { bqm().set(interval.upper(), interval.lower()); bqm().mul2(interval.upper()); interval.set_lower_is_open(true); interval.set_upper_is_open(true); if (qm().is_neg(q)) { ::swap(interval.lower(), interval.upper()); } while (contains_zero(interval) || !check_precision(interval, k) || bqm().is_zero(interval.lower()) || bqm().is_zero(interval.upper())) { checkpoint(); bqm().refine_lower(q, interval.lower(), interval.upper()); bqm().refine_upper(q, interval.lower(), interval.upper()); } } } void initialize_rational_value_interval(value * a) { // For rational values, we only compute the binary intervals if needed. SASSERT(is_nz_rational(a)); mpq_to_mpbqi(to_mpq(a), a->m_interval, m_ini_precision); } mpbqi & interval(value * a) const { SASSERT(a != 0); if (contains_zero(a->m_interval)) { SASSERT(is_nz_rational(a)); const_cast(this)->initialize_rational_value_interval(a); } return a->m_interval; } rational_value * mk_rational() { return new (allocator()) rational_value(); } /** \brief Make a rational and swap its value with v */ rational_value * mk_rational_and_swap(mpq & v) { SASSERT(!qm().is_zero(v)); rational_value * r = mk_rational(); ::swap(r->m_value, v); return r; } rational_value * mk_rational(mpq const & v) { SASSERT(!qm().is_zero(v)); rational_value * r = mk_rational(); qm().set(r->m_value, v); return r; } rational_value * mk_rational(mpz const & v) { SASSERT(!qm().is_zero(v)); rational_value * r = mk_rational(); qm().set(r->m_value, v); return r; } rational_value * mk_rational(mpbq const & v) { SASSERT(!bqm().is_zero(v)); scoped_mpq v_q(qm()); // v as a rational ::to_mpq(qm(), v, v_q); return mk_rational(v_q); } void reset_interval(value * a) { bqim().reset(a->m_interval); } template void update_mpq_value(value * a, T & v) { SASSERT(is_nz_rational(a)); qm().set(to_mpq(a), v); reset_interval(a); } template void update_mpq_value(numeral & a, T & v) { update_mpq_value(a.m_value, v); } /** \brief a <- n */ void set(numeral & a, int n) { if (n == 0) { reset(a); return; } del(a); a.m_value = mk_rational(); inc_ref(a.m_value); update_mpq_value(a, n); } /** \brief a <- n */ void set(numeral & a, mpz const & n) { if (qm().is_zero(n)) { reset(a); return; } del(a); a.m_value = mk_rational(); inc_ref(a.m_value); update_mpq_value(a, n); } /** \brief a <- n */ void set(numeral & a, mpq const & n) { if (qm().is_zero(n)) { reset(a); return; } del(a); a.m_value = mk_rational(); inc_ref(a.m_value); update_mpq_value(a, n); } /** \brief a <- n */ void set(numeral & a, numeral const & n) { inc_ref(n.m_value); dec_ref(a.m_value); a.m_value = n.m_value; } // --------------------------------- // // Polynomial arithmetic in RCF // // --------------------------------- /** \brief Remove 0s */ void adjust_size(value_ref_buffer & r) { while (!r.empty() && r.back() == nullptr) { r.pop_back(); } } /** \brief r <- p1 + p2 */ void add(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & r) { SASSERT(p1 != r.c_ptr()); SASSERT(p2 != r.c_ptr()); r.reset(); value_ref a_i(*this); unsigned min = std::min(sz1, sz2); unsigned i = 0; for (; i < min; i++) { add(p1[i], p2[i], a_i); r.push_back(a_i); } for (; i < sz1; i++) r.push_back(p1[i]); for (; i < sz2; i++) r.push_back(p2[i]); SASSERT(r.size() == std::max(sz1, sz2)); adjust_size(r); } /** \brief r <- p + a */ void add(unsigned sz, value * const * p, value * a, value_ref_buffer & r) { SASSERT(p != r.c_ptr()); SASSERT(sz > 0); r.reset(); value_ref a_0(*this); add(p[0], a, a_0); r.push_back(a_0); r.append(sz - 1, p + 1); adjust_size(r); } /** \brief r <- p1 - p2 */ void sub(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & r) { SASSERT(p1 != r.c_ptr()); SASSERT(p2 != r.c_ptr()); r.reset(); value_ref a_i(*this); unsigned min = std::min(sz1, sz2); unsigned i = 0; for (; i < min; i++) { sub(p1[i], p2[i], a_i); r.push_back(a_i); } for (; i < sz1; i++) r.push_back(p1[i]); for (; i < sz2; i++) { neg(p2[i], a_i); r.push_back(a_i); } SASSERT(r.size() == std::max(sz1, sz2)); adjust_size(r); } /** \brief r <- p - a */ void sub(unsigned sz, value * const * p, value * a, value_ref_buffer & r) { SASSERT(p != r.c_ptr()); SASSERT(sz > 0); r.reset(); value_ref a_0(*this); sub(p[0], a, a_0); r.push_back(a_0); r.append(sz - 1, p + 1); adjust_size(r); } /** \brief r <- a * p */ void mul(value * a, unsigned sz, value * const * p, value_ref_buffer & r) { SASSERT(p != r.c_ptr()); r.reset(); if (a == nullptr) return; value_ref a_i(*this); for (unsigned i = 0; i < sz; i++) { mul(a, p[i], a_i); r.push_back(a_i); } } /** \brief r <- p1 * p2 */ void mul(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & r) { SASSERT(p1 != r.c_ptr()); SASSERT(p2 != r.c_ptr()); r.reset(); unsigned sz = sz1*sz2; r.resize(sz); if (sz1 < sz2) { std::swap(sz1, sz2); std::swap(p1, p2); } value_ref tmp(*this); for (unsigned i = 0; i < sz1; i++) { checkpoint(); if (p1[i] == nullptr) continue; for (unsigned j = 0; j < sz2; j++) { // r[i+j] <- r[i+j] + p1[i]*p2[j] mul(p1[i], p2[j], tmp); add(r[i+j], tmp, tmp); r.set(i+j, tmp); } } adjust_size(r); } /** \brief p <- p/a */ void div(value_ref_buffer & p, value * a) { SASSERT(!is_zero(a)); if (is_rational_one(a)) return; value_ref a_i(*this); unsigned sz = p.size(); for (unsigned i = 0; i < sz; i++) { div(p[i], a, a_i); p.set(i, a_i); } } /** \brief q <- quotient(p1, p2); r <- rem(p1, p2) */ void div_rem(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & q, value_ref_buffer & r) { SASSERT(sz2 > 0); if (sz2 == 1) { q.reset(); q.append(sz1, p1); div(q, *p2); r.reset(); } else { q.reset(); r.reset(); r.append(sz1, p1); if (sz1 > 1) { if (sz1 >= sz2) { q.resize(sz1 - sz2 + 1); } else { SASSERT(q.empty()); } value * b_n = p2[sz2-1]; SASSERT(!is_zero(b_n)); value_ref ratio(*this); value_ref aux(*this); while (true) { checkpoint(); sz1 = r.size(); if (sz1 < sz2) { adjust_size(q); break; } unsigned m_n = sz1 - sz2; // m-n div(r[sz1 - 1], b_n, ratio); // q[m_n] <- q[m_n] + r[sz1 - 1]/b_n add(q[m_n], ratio, aux); q.set(m_n, aux); for (unsigned i = 0; i < sz2 - 1; i++) { // r[i + m_n] <- r[i + m_n] - ratio * p2[i] mul(ratio, p2[i], aux); sub(r[i + m_n], aux, aux); r.set(i + m_n, aux); } r.shrink(sz1 - 1); adjust_size(r); } } } } /** \brief q <- quotient(p1, p2) */ void div(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & q) { value_ref_buffer r(*this); div_rem(sz1, p1, sz2, p2, q, r); } /** \brief r <- p/a */ void div(unsigned sz, value * const * p, value * a, value_ref_buffer & r) { r.reset(); value_ref a_i(*this); for (unsigned i = 0; i < sz; i++) { div(p[i], a, a_i); r.push_back(a_i); } } /** \brief r <- rem(p1, p2) */ void rem(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & r) { SASSERT(p1 != r.c_ptr()); SASSERT(p2 != r.c_ptr()); TRACE("rcf_rem", tout << "rem\n"; display_poly(tout, sz1, p1); tout << "\n"; display_poly(tout, sz2, p2); tout << "\n";); r.reset(); SASSERT(sz2 > 0); if (sz2 == 1) return; r.append(sz1, p1); if (sz1 <= 1) return; // r is p1 value * b_n = p2[sz2 - 1]; value_ref ratio(*this); value_ref new_a(*this); SASSERT(b_n != 0); while (true) { checkpoint(); sz1 = r.size(); if (sz1 < sz2) { TRACE("rcf_rem", tout << "rem result\n"; display_poly(tout, r.size(), r.c_ptr()); tout << "\n";); return; } unsigned m_n = sz1 - sz2; div(r[sz1 - 1], b_n, ratio); for (unsigned i = 0; i < sz2 - 1; i++) { mul(ratio, p2[i], new_a); sub(r[i + m_n], new_a, new_a); r.set(i + m_n, new_a); } r.shrink(sz1 - 1); adjust_size(r); } } /** \brief r <- prem(p1, p2) Pseudo-remainder We are working on a field, but it is useful to use the pseudo-remainder algorithm because it does not create rational function values. That is, if has_clean_denominators(p1) and has_clean_denominators(p2) then has_clean_denominators(r). */ void prem(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, unsigned & d, value_ref_buffer & r) { SASSERT(p1 != r.c_ptr()); SASSERT(p2 != r.c_ptr()); TRACE("rcf_prem", tout << "prem\n"; display_poly(tout, sz1, p1); tout << "\n"; display_poly(tout, sz2, p2); tout << "\n";); d = 0; r.reset(); SASSERT(sz2 > 0); if (sz2 == 1) return; r.append(sz1, p1); if (sz1 <= 1) return; // r is p1 value * b_n = p2[sz2 - 1]; SASSERT(b_n != 0); value_ref a_m(*this); value_ref new_a(*this); while (true) { checkpoint(); sz1 = r.size(); if (sz1 < sz2) { TRACE("rcf_prem", tout << "prem result\n"; display_poly(tout, r.size(), r.c_ptr()); tout << "\n";); return; } unsigned m_n = sz1 - sz2; // r: a_m * x^m + a_{m-1} * x^{m-1} + ... + a_0 // p2: b_n * x^n + b_{n-1} * x^{n-1} + ... + b_0 d++; a_m = r[sz1 - 1]; // don't need to update position sz1 - 1, since it will become 0 if (!is_rational_one(b_n)) { for (unsigned i = 0; i < sz1 - 1; i++) { mul(r[i], b_n, new_a); r.set(i, new_a); } } // buffer: a_m * x^m + b_n * a_{m-1} * x^{m-1} + ... + b_n * a_0 // don't need to process i = sz2 - 1, because r[sz1 - 1] will become 0. for (unsigned i = 0; i < sz2 - 1; i++) { mul(a_m, p2[i], new_a); sub(r[i + m_n], new_a, new_a); r.set(i + m_n, new_a); } r.shrink(sz1 - 1); adjust_size(r); } } void prem(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & r) { unsigned d; prem(sz1, p1, sz2, p2, d, r); } /** \brief r <- -p */ void neg(unsigned sz, value * const * p, value_ref_buffer & r) { SASSERT(p != r.c_ptr()); r.reset(); value_ref a_i(*this); for (unsigned i = 0; i < sz; i++) { neg(p[i], a_i); r.push_back(a_i); } } /** \brief r <- -r */ void neg(value_ref_buffer & r) { value_ref a_i(*this); unsigned sz = r.size(); for (unsigned i = 0; i < sz; i++) { neg(r[i], a_i); r.set(i, a_i); } } /** \brief p <- -p */ void neg(polynomial & p) { value_ref a_i(*this); unsigned sz = p.size(); for (unsigned i = 0; i < sz; i++) { neg(p[i], a_i); inc_ref(a_i.get()); dec_ref(p[i]); p[i] = a_i.get(); } } /** \brief r <- srem(p1, p2) Signed remainder */ void srem(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & r) { SASSERT(p1 != r.c_ptr()); SASSERT(p2 != r.c_ptr()); rem(sz1, p1, sz2, p2, r); neg(r); } /** \brief r <- sprem(p1, p2) Signed pseudo remainder */ void sprem(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & r) { SASSERT(p1 != r.c_ptr()); SASSERT(p2 != r.c_ptr()); unsigned d; prem(sz1, p1, sz2, p2, d, r); // We should not flip the sign if d is odd and leading coefficient of p2 is negative. if (d % 2 == 0 || (sz2 > 0 && sign(p2[sz2-1]) > 0)) neg(r); } // --------------------------------- // // Structural equality // // --------------------------------- /** \brief Values a and b are said to be "structurally" equal if: - a and b are 0. - a and b are rationals and compare(a, b) == 0 - a and b are rational function values p_a(x)/q_a(x) and p_b(y)/q_b(y) where x and y are field extensions, and * x == y (pointer equality, i.e., they are the same field extension object). * Every coefficient of p_a is structurally equal to every coefficient of p_b * Every coefficient of q_a is structurally equal to every coefficient of q_b Clearly structural equality implies equality, but the reverse is not true. */ bool struct_eq(value * a, value * b) const { if (a == b) return true; else if (a == nullptr || b == nullptr) return false; else if (is_nz_rational(a) && is_nz_rational(b)) return qm().eq(to_mpq(a), to_mpq(b)); else if (is_nz_rational(a) || is_nz_rational(b)) return false; else { SASSERT(is_rational_function(a)); SASSERT(is_rational_function(b)); rational_function_value * rf_a = to_rational_function(a); rational_function_value * rf_b = to_rational_function(b); if (rf_a->ext() != rf_b->ext()) return false; return struct_eq(rf_a->num(), rf_b->num()) && struct_eq(rf_a->den(), rf_b->den()); } } /** Auxiliary method for bool struct_eq(value * a, value * b) */ bool struct_eq(unsigned sz_a, value * const * p_a, unsigned sz_b, value * const * p_b) const { if (sz_a != sz_b) return false; for (unsigned i = 0; i < sz_a; i++) { if (!struct_eq(p_a[i], p_b[i])) return false; } return true; } /** Auxiliary method for bool struct_eq(value * a, value * b) */ bool struct_eq(polynomial const & p_a, polynomial const & p_b) const { return struct_eq(p_a.size(), p_a.c_ptr(), p_b.size(), p_b.c_ptr()); } // --------------------------------- // // Clean denominators // // --------------------------------- /** \brief We say 'a' has "clean" denominators if - a is 0 - a is a rational_value that is an integer - a is a rational_function_value of the form p_a(x)/1 where the coefficients of p_a also have clean denominators. */ bool has_clean_denominators(value * a) const { if (a == nullptr) return true; else if (is_nz_rational(a)) return qm().is_int(to_mpq(a)); else { rational_function_value * rf_a = to_rational_function(a); return is_denominator_one(rf_a) && has_clean_denominators(rf_a->num()); } } /** \brief See comment at has_clean_denominators(value * a) */ bool has_clean_denominators(unsigned sz, value * const * p) const { for (unsigned i = 0; i < sz; i++) { if (!has_clean_denominators(p[i])) return false; } return true; } /** \brief See comment at has_clean_denominators(value * a) */ bool has_clean_denominators(polynomial const & p) const { return has_clean_denominators(p.size(), p.c_ptr()); } /** \brief "Clean" the denominators of 'a'. That is, return p and q s.t. a == p/q and has_clean_denominators(p) and has_clean_denominators(q) */ void clean_denominators_core(value * a, value_ref & p, value_ref & q) { INC_DEPTH(); TRACE("rcf_clean", tout << "clean_denominators_core [" << m_exec_depth << "]\na: "; display(tout, a, false); tout << "\n";); p.reset(); q.reset(); if (a == nullptr) { p = a; q = one(); } else if (is_nz_rational(a)) { p = mk_rational(to_mpq(a).numerator()); q = mk_rational(to_mpq(a).denominator()); } else { rational_function_value * rf_a = to_rational_function(a); value_ref_buffer p_num(*this), p_den(*this); value_ref d_num(*this), d_den(*this); clean_denominators_core(rf_a->num(), p_num, d_num); if (is_denominator_one(rf_a)) { p_den.push_back(one()); d_den = one(); } else { clean_denominators_core(rf_a->den(), p_den, d_den); } value_ref x(*this); x = mk_rational_function_value(rf_a->ext()); mk_polynomial_value(p_num.size(), p_num.c_ptr(), x, p); mk_polynomial_value(p_den.size(), p_den.c_ptr(), x, q); if (!struct_eq(d_den, d_num)) { mul(p, d_den, p); mul(q, d_num, q); } if (sign(q) < 0) { // make sure the denominator is positive neg(p, p); neg(q, q); } } } /** \brief Clean the denominators of the polynomial p, it returns clean_p and d s.t. p = clean_p/d and has_clean_denominators(clean_p) && has_clean_denominators(d) */ void clean_denominators_core(unsigned p_sz, value * const * p, value_ref_buffer & norm_p, value_ref & d) { SASSERT(p_sz >= 1); value_ref_buffer nums(*this), dens(*this); value_ref a_n(*this), a_d(*this); bool all_one = true; for (unsigned i = 0; i < p_sz; i++) { if (p[i]) { clean_denominators_core(p[i], a_n, a_d); nums.push_back(a_n); if (!is_rational_one(a_d)) all_one = false; dens.push_back(a_d); } else { nums.push_back(nullptr); dens.push_back(nullptr); } } if (all_one) { norm_p = nums; d = one(); } else { // Compute lcm of the integer elements in dens. // This is a little trick to control the coefficient growth. // We don't compute lcm of the other elements of dens because it is too expensive. scoped_mpq lcm_z(qm()); bool found_z = false; SASSERT(nums.size() == p_sz); SASSERT(dens.size() == p_sz); for (unsigned i = 0; i < p_sz; i++) { if (!dens[i]) continue; if (is_nz_rational(dens[i])) { mpq const & _d = to_mpq(dens[i]); SASSERT(qm().is_int(_d)); if (!found_z) { found_z = true; qm().set(lcm_z, _d); } else { qm().lcm(lcm_z, _d, lcm_z); } } } value_ref lcm(*this); if (found_z) { lcm = mk_rational(lcm_z); } else { lcm = one(); } // Compute the multipliers for nums. // Compute norm_p and d // // We do NOT use GCD to compute the LCM of the denominators of non-rational values. // However, we detect structurally equivalent denominators. // // Thus a/(b+1) + c/(b+1) is converted into a*c/(b+1) instead of (a*(b+1) + c*(b+1))/(b+1)^2 norm_p.reset(); d = lcm; value_ref_buffer multipliers(*this); value_ref m(*this); for (unsigned i = 0; i < p_sz; i++) { if (!nums[i]) { norm_p.push_back(nullptr); } else { SASSERT(dens[i]); bool is_z; if (!is_nz_rational(dens[i])) { m = lcm; is_z = false; } else { scoped_mpq num_z(qm()); qm().div(lcm_z, to_mpq(dens[i]), num_z); SASSERT(qm().is_int(num_z)); m = mk_rational_and_swap(num_z); is_z = true; } bool found_lt_eq = false; for (unsigned j = 0; j < p_sz; j++) { TRACE("rcf_clean_bug", tout << "j: " << j << " "; display(tout, m, false); tout << "\n";); if (!dens[j]) continue; if (i != j && !is_nz_rational(dens[j])) { if (struct_eq(dens[i], dens[j])) { if (j < i) found_lt_eq = true; } else { mul(m, dens[j], m); } } } if (!is_z && !found_lt_eq) { mul(dens[i], d, d); } mul(m, nums[i], m); norm_p.push_back(m); } } } SASSERT(norm_p.size() == p_sz); } void clean_denominators_core(polynomial const & p, value_ref_buffer & norm_p, value_ref & d) { clean_denominators_core(p.size(), p.c_ptr(), norm_p, d); } void clean_denominators(value * a, value_ref & p, value_ref & q) { if (has_clean_denominators(a)) { p = a; q = one(); } else { clean_denominators_core(a, p, q); } } void clean_denominators(unsigned sz, value * const * p, value_ref_buffer & norm_p, value_ref & d) { if (has_clean_denominators(sz, p)) { norm_p.append(sz, p); d = one(); } else { clean_denominators_core(sz, p, norm_p, d); } } void clean_denominators(polynomial const & p, value_ref_buffer & norm_p, value_ref & d) { clean_denominators(p.size(), p.c_ptr(), norm_p, d); } void clean_denominators(numeral const & a, numeral & p, numeral & q) { value_ref _p(*this), _q(*this); clean_denominators(a.m_value, _p, _q); set(p, _p); set(q, _q); } // --------------------------------- // // GCD of integer coefficients // // --------------------------------- /** \brief If has_clean_denominators(a), then this method store the gcd of the integer coefficients in g. If !has_clean_denominators(a) it returns false. If g != 0, then it will compute the gcd of g and the coefficients in a. */ bool gcd_int_coeffs(value * a, mpz & g) { if (a == nullptr) { return false; } else if (is_nz_rational(a)) { if (!qm().is_int(to_mpq(a))) return false; else if (qm().is_zero(g)) { qm().set(g, to_mpq(a).numerator()); qm().abs(g); } else { qm().gcd(g, to_mpq(a).numerator(), g); } return true; } else { rational_function_value * rf_a = to_rational_function(a); if (!is_denominator_one(rf_a)) return false; else return gcd_int_coeffs(rf_a->num(), g); } } /** \brief See comment in gcd_int_coeffs(value * a, mpz & g) */ bool gcd_int_coeffs(unsigned p_sz, value * const * p, mpz & g) { if (p_sz == 0) { return false; } else { for (unsigned i = 0; i < p_sz; i++) { if (p[i]) { if (!gcd_int_coeffs(p[i], g)) return false; if (qm().is_one(g)) return true; } } return true; } } /** \brief See comment in gcd_int_coeffs(value * a, mpz & g) */ bool gcd_int_coeffs(polynomial const & p, mpz & g) { return gcd_int_coeffs(p.size(), p.c_ptr(), g); } /** \brief Compute gcd_int_coeffs and divide p by it (if applicable). */ void normalize_int_coeffs(value_ref_buffer & p) { scoped_mpz g(qm()); if (gcd_int_coeffs(p.size(), p.c_ptr(), g) && !qm().is_one(g)) { SASSERT(qm().is_pos(g)); value_ref a(*this); for (unsigned i = 0; i < p.size(); i++) { if (p[i]) { a = p[i]; p.set(i, nullptr); exact_div_z(a, g); p.set(i, a); } } } } /** \brief a <- a/b where b > 0 Auxiliary function for normalize_int_coeffs. It assumes has_clean_denominators(a), and that b divides all integer coefficients. FUTURE: perform the operation using destructive updates when a is not shared. */ void exact_div_z(value_ref & a, mpz const & b) { if (a == 0) { return; } else if (is_nz_rational(a)) { scoped_mpq r(qm()); SASSERT(qm().is_int(to_mpq(a))); qm().div(to_mpq(a), b, r); a = mk_rational_and_swap(r); } else { rational_function_value * rf = to_rational_function(a); SASSERT(is_denominator_one(rf)); value_ref_buffer new_ais(*this); value_ref ai(*this); polynomial const & p = rf->num(); for (unsigned i = 0; i < p.size(); i++) { if (p[i]) { ai = p[i]; exact_div_z(ai, b); new_ais.push_back(ai); } else { new_ais.push_back(nullptr); } } rational_function_value * r = mk_rational_function_value_core(rf->ext(), new_ais.size(), new_ais.c_ptr(), 1, &m_one); set_interval(r->m_interval, rf->m_interval); a = r; // divide upper and lower by b div(r->m_interval, b, m_ini_precision, r->m_interval); } } // --------------------------------- // // GCD // // --------------------------------- bool is_monic(value_ref_buffer const & p) { return !p.empty() && is_rational_one(p[p.size() - 1]); } bool is_monic(polynomial const & p) { return !p.empty() && is_rational_one(p[p.size() - 1]); } /** \brief Force the leading coefficient of p to be 1. */ void mk_monic(value_ref_buffer & p) { unsigned sz = p.size(); if (sz > 0) { value_ref a_i(*this); SASSERT(p[sz-1] != 0); if (!is_rational_one(p[sz-1])) { for (unsigned i = 0; i < sz - 1; i++) { div(p[i], p[sz-1], a_i); p.set(i, a_i); } p.set(sz-1, one()); } } } /** \brief r <- gcd(p1, p2) */ void gcd(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & r) { INC_DEPTH(); TRACE("rcf_gcd", tout << "GCD [" << m_exec_depth << "]\n"; display_poly(tout, sz1, p1); tout << "\n"; display_poly(tout, sz2, p2); tout << "\n";); if (sz1 == 0) { r.append(sz2, p2); mk_monic(r); } else if (sz2 == 0) { r.append(sz1, p1); mk_monic(r); } else { value_ref_buffer A(*this); value_ref_buffer B(*this); value_ref_buffer R(*this); A.append(sz1, p1); B.append(sz2, p2); while (true) { TRACE("rcf_gcd", tout << "A: "; display_poly(tout, A.size(), A.c_ptr()); tout << "\n"; tout << "B: "; display_poly(tout, B.size(), B.c_ptr()); tout << "\n";); if (B.empty()) { mk_monic(A); r = A; TRACE("rcf_gcd", tout << "gcd result: "; display_poly(tout, r.size(), r.c_ptr()); tout << "\n";); return; } rem(A.size(), A.c_ptr(), B.size(), B.c_ptr(), R); A = B; B = R; } } } void flip_sign_if_lc_neg(value_ref_buffer & r) { unsigned sz = r.size(); if (sz == 0) return; if (sign(r[sz - 1]) < 0) neg(r); } void prem_gcd(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & r) { INC_DEPTH(); TRACE("rcf_gcd", tout << "prem-GCD [" << m_exec_depth << "]\n"; display_poly(tout, sz1, p1); tout << "\n"; display_poly(tout, sz2, p2); tout << "\n";); SASSERT(p1 != r.c_ptr()); SASSERT(p2 != r.c_ptr()); if (sz1 == 0) { r.append(sz2, p2); flip_sign_if_lc_neg(r); } else if (sz2 == 0) { r.append(sz1, p1); flip_sign_if_lc_neg(r); } else { value_ref_buffer A(*this); value_ref_buffer B(*this); value_ref_buffer R(*this); A.append(sz1, p1); B.append(sz2, p2); while (true) { TRACE("rcf_gcd", tout << "A: "; display_poly(tout, A.size(), A.c_ptr()); tout << "\n"; tout << "B: "; display_poly(tout, B.size(), B.c_ptr()); tout << "\n";); if (B.empty()) { normalize_int_coeffs(A); flip_sign_if_lc_neg(A); r = A; TRACE("rcf_gcd", tout << "gcd result: "; display_poly(tout, r.size(), r.c_ptr()); tout << "\n";); return; } prem(A.size(), A.c_ptr(), B.size(), B.c_ptr(), R); normalize_int_coeffs(R); A = B; B = R; } } } // --------------------------------- // // Derivatives and Sturm-Tarski Sequences // // --------------------------------- /** \brief r <- dp/dx */ void derivative(unsigned sz, value * const * p, value_ref_buffer & r) { r.reset(); if (sz > 1) { for (unsigned i = 1; i < sz; i++) { mpq i_mpq(i); value_ref a_i(*this); a_i = mk_rational_and_swap(i_mpq); mul(a_i, p[i], a_i); r.push_back(a_i); } adjust_size(r); } } /** \brief r <- squarefree(p) Store in r the square free factors of p. */ void square_free(unsigned sz, value * const * p, value_ref_buffer & r) { flet set(m_in_aux_values, true); if (sz <= 1) { r.append(sz, p); } else { value_ref_buffer p_prime(*this); value_ref_buffer g(*this); derivative(sz, p, p_prime); if (m_use_prem) prem_gcd(sz, p, p_prime.size(), p_prime.c_ptr(), g); else gcd(sz, p, p_prime.size(), p_prime.c_ptr(), g); if (g.size() <= 1) { r.append(sz, p); } else { div(sz, p, g.size(), g.c_ptr(), r); if (m_use_prem) normalize_int_coeffs(r); } } } /** \brief Keep expanding the Sturm sequence starting at seq */ void sturm_seq_core(scoped_polynomial_seq & seq) { INC_DEPTH(); flet set(m_in_aux_values, true); SASSERT(seq.size() >= 2); TRACE("rcf_sturm_seq", unsigned sz = seq.size(); tout << "sturm_seq_core [" << m_exec_depth << "]\n"; display_poly(tout, seq.size(sz-2), seq.coeffs(sz-2)); tout << "\n"; display_poly(tout, seq.size(sz-1), seq.coeffs(sz-1)); tout << "\n";); value_ref_buffer r(*this); while (true) { unsigned sz = seq.size(); if (m_use_prem) { sprem(seq.size(sz-2), seq.coeffs(sz-2), seq.size(sz-1), seq.coeffs(sz-1), r); normalize_int_coeffs(r); } else { srem(seq.size(sz-2), seq.coeffs(sz-2), seq.size(sz-1), seq.coeffs(sz-1), r); } TRACE("rcf_sturm_seq", tout << "sturm_seq_core [" << m_exec_depth << "], new polynomial\n"; display_poly(tout, r.size(), r.c_ptr()); tout << "\n";); if (r.empty()) return; seq.push(r.size(), r.c_ptr()); } } /** \brief Store in seq the Sturm sequence for (p1; p2) */ void sturm_seq(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, scoped_polynomial_seq & seq) { seq.reset(); seq.push(sz1, p1); seq.push(sz2, p2); sturm_seq_core(seq); } /** \brief Store in seq the Sturm sequence for (p; p') */ void sturm_seq(unsigned sz, value * const * p, scoped_polynomial_seq & seq) { seq.reset(); value_ref_buffer p_prime(*this); seq.push(sz, p); derivative(sz, p, p_prime); seq.push(p_prime.size(), p_prime.c_ptr()); sturm_seq_core(seq); } /** \brief Store in seq the Sturm sequence for (p1; p1' * p2) */ void sturm_tarski_seq(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, scoped_polynomial_seq & seq) { seq.reset(); value_ref_buffer p1_prime(*this); value_ref_buffer p1_prime_p2(*this); seq.push(sz1, p1); derivative(sz1, p1, p1_prime); mul(p1_prime.size(), p1_prime.c_ptr(), sz2, p2, p1_prime_p2); seq.push(p1_prime_p2.size(), p1_prime_p2.c_ptr()); sturm_seq_core(seq); } // --------------------------------- // // Sign evaluation for polynomials // That is, sign of p(x) at b // // --------------------------------- /** \brief Return the sign of p(0) */ int eval_sign_at_zero(unsigned n, value * const * p) { if (n == 0) return 0; return sign(p[0]); } /** \brief Return the sign of p(oo) */ int eval_sign_at_plus_inf(unsigned n, value * const * p) { if (n == 0) return 0; SASSERT(!is_zero(p[n-1])); // p is well formed return sign(p[n-1]); } /** \brief Return the sign of p(-oo) */ int eval_sign_at_minus_inf(unsigned n, value * const * p) { if (n == 0) return 0; SASSERT(!is_zero(p[n-1])); // p is well formed unsigned degree = n - 1; if (degree % 2 == 0) return sign(p[n - 1]); else return -sign(p[n - 1]); } /** \brief Store in r an approximation (as an interval) for the interval p(b). \pre n >= 2 */ void eval_sign_at_approx(unsigned n, value * const * p, mpbq const & b, mpbqi & r) { SASSERT(n >= 2); // We compute r using the Horner Sequence // ((a_{n-1}*b + a_{n-2})*b + a_{n-3})*b + a_{n-4} ... // where a_i's are the intervals associated with coefficients of p. SASSERT(n > 0); SASSERT(p[n - 1] != 0); scoped_mpbqi bi(bqim()); set_interval(bi, b); // bi <- [b, b] // r <- a_n * bi bqim().mul(interval(p[n - 1]), bi, r); unsigned i = n - 1; while (i > 0) { checkpoint(); --i; if (p[i] != nullptr) bqim().add(r, interval(p[i]), r); if (i > 0) bqim().mul(r, bi, r); } } /** \brief We say a polynomial has "refinable" approximated coefficients if the intervals approximating the coefficients do not have -oo or oo as lower/upper bounds. */ bool has_refineable_approx_coeffs(unsigned n, value * const * p) { for (unsigned i = 0; i < n; i++) { if (p[i] != nullptr) { mpbqi & a_i = interval(p[i]); if (a_i.lower_is_inf() || a_i.upper_is_inf()) return false; } } return true; } /** \brief r <- p(b) */ void mk_polynomial_value(unsigned n, value * const * p, value * b, value_ref & r) { SASSERT(n > 0); if (n == 1 || b == nullptr) { r = p[0]; } else { SASSERT(n >= 2); // We compute the result using the Horner Sequence // ((a_{n-1}*b + a_{n-2})*b + a_{n-3})*b + a_{n-4} ... // where a_i's are the coefficients of p. mul(p[n - 1], b, r); // r <- a_{n-1} * b unsigned i = n - 1; while (i > 0) { --i; if (p[i] != nullptr) add(r, p[i], r); // r <- r + a_i if (i > 0) mul(r, b, r); // r <- r * b } } } /** \brief Evaluate the sign of p(b) by computing a value object. */ int expensive_eval_sign_at(unsigned n, value * const * p, mpbq const & b) { flet set(m_in_aux_values, true); SASSERT(n > 1); SASSERT(p[n - 1] != 0); // Actually, given b = c/2^k, we compute the sign of (2^k)^n*p(c) // Original Horner Sequence // ((a_n * b + a_{n-1})*b + a_{n-2})*b + a_{n-3} ... // Variation of the Horner Sequence for (2^k)^n*p(b) // ((a_n * c + a_{n-1}*2_k)*c + a_{n-2}*(2_k)^2)*c + a_{n-3}*(2_k)^3 ... + a_0*(2_k)^n scoped_mpz mpz_twok(qm()); qm().mul2k(mpz(1), b.k(), mpz_twok); value_ref twok(*this), twok_i(*this); twok = mk_rational(mpz_twok); twok_i = twok; value_ref c(*this); c = mk_rational(b.numerator()); value_ref r(*this), ak(*this), rc(*this); r = p[n-1]; unsigned i = n-1; while (i > 0) { --i; if (is_zero(p[i])) { mul(r, c, r); } else { // ak <- a_i * (2^k)^(n-i) mul(p[i], twok_i, ak); // r <- r * c + a_i * (2^k)^(n-i) mul(r, c, rc); add(ak, rc, r); } mul(twok_i, twok, twok_i); } return sign(r); } /** \brief Find the magnitude of the biggest interval use to approximate coefficients of p. \pre has_refineable_approx_coeffs(n, p) */ int find_biggest_interval_magnitude(unsigned n, value * const * p) { int r = INT_MIN; for (unsigned i = 0; i < n; i++) { if (p[i] != nullptr) { mpbqi & a_i = interval(p[i]); SASSERT(!a_i.lower_is_inf() && !a_i.upper_is_inf()); int m = magnitude(a_i); if (m > r) r = m; } } return r; } /** \brief Return the sign of p(b) */ int eval_sign_at(unsigned n, value * const * p, mpbq const & b) { if (n == 0) return 0; else if (n == 1) return sign(p[0]); else { scoped_mpbqi r(bqim()); eval_sign_at_approx(n, p, b, r); if (!contains_zero(r)) { // we are done return bqim().is_P(r) ? 1 : -1; } else if (!has_refineable_approx_coeffs(n, p)) { return expensive_eval_sign_at(n, p, b); } else { int m = find_biggest_interval_magnitude(n, p); unsigned prec; if (m >= 0) prec = 1; else prec = -m; SASSERT(prec >= 1); while (prec <= m_max_precision) { checkpoint(); if (!refine_coeffs_interval(n, p, prec)) { // Failed to refine intervals, p must depend on infinitesimal values. // This can happen even if all intervals of coefficients of p are bounded. return expensive_eval_sign_at(n, p, b); } eval_sign_at_approx(n, p, b, r); if (!contains_zero(r)) { // we are done return bqim().is_P(r) ? 1 : -1; } prec++; // increase precision and try again. } return expensive_eval_sign_at(n, p, b); } } } // --------------------------------- // // Sign variations in polynomial sequences. // // --------------------------------- enum location { ZERO, MINUS_INF, PLUS_INF, MPBQ }; /** \brief Compute the number of sign variations at position (loc, b) in the given polynomial sequence. The position (loc, b) should be interpreted in the following way: - (ZERO, *) -> number of sign variations at 0. - (MINUS_INF, *) -> number of sign variations at -oo. - (PLUS_INF, *) -> number of sign variations at oo. - (MPBQ, b) -> number of sign variations at binary rational b. */ unsigned sign_variations_at_core(scoped_polynomial_seq const & seq, location loc, mpbq const & b) { unsigned sz = seq.size(); if (sz <= 1) return 0; unsigned r = 0; int sign, prev_sign; sign = 0; prev_sign = 0; unsigned i = 0; for (; i < sz; i++) { // find next nonzero unsigned psz = seq.size(i); value * const * p = seq.coeffs(i); switch (loc) { case PLUS_INF: sign = eval_sign_at_plus_inf(psz, p); break; case MINUS_INF: sign = eval_sign_at_minus_inf(psz, p); break; case ZERO: sign = eval_sign_at_zero(psz, p); break; case MPBQ: sign = eval_sign_at(psz, p, b); break; default: UNREACHABLE(); break; } if (sign == 0) continue; SASSERT(sign == 1 || sign == -1); // in the first iteration prev_sign == 0, then r is never incremented. if (sign != prev_sign && prev_sign != 0) r++; // move to the next prev_sign = sign; } return r; } unsigned sign_variations_at_minus_inf(scoped_polynomial_seq const & seq) { mpbq dummy(0); return sign_variations_at_core(seq, MINUS_INF, dummy); } unsigned sign_variations_at_plus_inf(scoped_polynomial_seq const & seq) { mpbq dummy(0); return sign_variations_at_core(seq, PLUS_INF, dummy); } unsigned sign_variations_at_zero(scoped_polynomial_seq const & seq) { mpbq dummy(0); return sign_variations_at_core(seq, ZERO, dummy); } unsigned sign_variations_at(scoped_polynomial_seq const & seq, mpbq const & b) { return sign_variations_at_core(seq, MPBQ, b); } int sign_variations_at_lower(scoped_polynomial_seq & seq, mpbqi const & interval) { if (interval.lower_is_inf()) return sign_variations_at_minus_inf(seq); else if (bqm().is_zero(interval.lower())) return sign_variations_at_zero(seq); else return sign_variations_at(seq, interval.lower()); } int sign_variations_at_upper(scoped_polynomial_seq & seq, mpbqi const & interval) { if (interval.upper_is_inf()) return sign_variations_at_plus_inf(seq); else if (bqm().is_zero(interval.upper())) return sign_variations_at_zero(seq); else return sign_variations_at(seq, interval.upper()); } // --------------------------------- // // Tarski-Queries (see BPR book) // // --------------------------------- /** \brief Given a polynomial Sturm sequence seq for (P; P' * Q) and an interval (a, b], it returns TaQ(Q, P; a, b) = #{ x \in (a, b] | P(x) = 0 and Q(x) > 0 } - #{ x \in (a, b] | P(x) = 0 and Q(x) < 0 } \remark This method ignores whether the interval end-points are closed or open. */ int TaQ(scoped_polynomial_seq & seq, mpbqi const & interval) { return sign_variations_at_lower(seq, interval) - sign_variations_at_upper(seq, interval); } /** \brief Return TaQ(Q, P; a, b) = #{ x \in (a, b] | P(x) = 0 and Q(x) > 0 } - #{ x \in (a, b] | P(x) = 0 and Q(x) < 0 } \remark This method ignores whether the interval end-points are closed or open. */ int TaQ(unsigned p_sz, value * const * p, unsigned q_sz, value * const * q, mpbqi const & interval) { INC_DEPTH(); TRACE("rcf_TaQ", tout << "TaQ [" << m_exec_depth << "]\n"; display_poly(tout, p_sz, p); tout << "\n"; display_poly(tout, q_sz, q); tout << "\n";); scoped_polynomial_seq seq(*this); sturm_tarski_seq(p_sz, p, q_sz, q, seq); return TaQ(seq, interval); } /** \brief Return TaQ(1, P; a, b) = #{ x \in (a, b] | P(x) = 0 } \remark This method ignores whether the interval end-points are closed or open. */ int TaQ_1(unsigned p_sz, value * const * p, mpbqi const & interval) { INC_DEPTH(); TRACE("rcf_TaQ", tout << "TaQ_1 [" << m_exec_depth << "]\n"; display_poly(tout, p_sz, p); tout << "\n";); scoped_polynomial_seq seq(*this); sturm_seq(p_sz, p, seq); return TaQ(seq, interval); } // --------------------------------- // // Interval refinement // // --------------------------------- void refine_rational_interval(rational_value * v, unsigned prec) { mpbqi & i = interval(v); if (!i.lower_is_open() && !i.upper_is_open()) { SASSERT(bqm().eq(i.lower(), i.upper())); return; } while (!check_precision(i, prec)) { checkpoint(); bqm().refine_lower(to_mpq(v), i.lower(), i.upper()); bqm().refine_upper(to_mpq(v), i.lower(), i.upper()); } } /** \brief Refine the interval for each coefficient of in the polynomial p. */ bool refine_coeffs_interval(unsigned n, value * const * p, unsigned prec) { for (unsigned i = 0; i < n; i++) { if (p[i] != nullptr && !refine_interval(p[i], prec)) return false; } return true; } /** \brief Refine the interval for each coefficient of in the polynomial p. */ bool refine_coeffs_interval(polynomial const & p, unsigned prec) { return refine_coeffs_interval(p.size(), p.c_ptr(), prec); } /** \brief Store in r the interval p(v). */ void polynomial_interval(polynomial const & p, mpbqi const & v, mpbqi & r) { // We compute r using the Horner Sequence // ((a_n * v + a_{n-1})*v + a_{n-2})*v + a_{n-3} ... // where a_i's are the coefficients of p. unsigned sz = p.size(); if (sz == 1) { bqim().set(r, interval(p[0])); } else { SASSERT(sz > 0); SASSERT(p[sz - 1] != 0); // r <- a_n * v bqim().mul(interval(p[sz-1]), v, r); unsigned i = sz - 1; while (i > 0) { --i; if (p[i] != 0) bqim().add(r, interval(p[i]), r); if (i > 0) bqim().mul(r, v, r); } } } /** \brief Update the interval of v by using the intervals of extension and coefficients of the rational function. */ void update_rf_interval(rational_function_value * v, unsigned prec) { if (is_denominator_one(v)) { polynomial_interval(v->num(), v->ext()->interval(), v->interval()); } else { scoped_mpbqi num_i(bqim()), den_i(bqim()); polynomial_interval(v->num(), v->ext()->interval(), num_i); polynomial_interval(v->den(), v->ext()->interval(), den_i); if (!contains_zero(num_i) && !contains_zero(den_i)) { div(num_i, den_i, inc_precision(prec, 2), v->interval()); } } } void refine_transcendental_interval(rational_function_value * v, unsigned prec) { SASSERT(v->ext()->is_transcendental()); polynomial const & n = v->num(); polynomial const & d = v->den(); unsigned _prec = prec; while (true) { VERIFY(refine_coeffs_interval(n, _prec)); // must return true because a transcendental never depends on an infinitesimal VERIFY(refine_coeffs_interval(d, _prec)); // must return true because a transcendental never depends on an infinitesimal refine_transcendental_interval(to_transcendental(v->ext()), _prec); update_rf_interval(v, prec); TRACE("rcf_transcendental", tout << "after update_rf_interval: " << magnitude(v->interval()) << " "; bqim().display(tout, v->interval()); tout << std::endl;); if (check_precision(v->interval(), prec)) return; _prec++; } } bool refine_infinitesimal_interval(rational_function_value * v, unsigned prec) { SASSERT(v->ext()->is_infinitesimal()); polynomial const & numerator = v->num(); polynomial const & denominator = v->den(); unsigned num_idx = first_non_zero(numerator); unsigned den_idx = first_non_zero(denominator); if (num_idx == 0 && den_idx == 0) { unsigned _prec = prec; while (true) { refine_interval(numerator[num_idx], _prec); refine_interval(denominator[num_idx], _prec); mpbqi const & num_i = interval(numerator[num_idx]); mpbqi const & den_i = interval(denominator[num_idx]); SASSERT(!contains_zero(num_i)); SASSERT(!contains_zero(den_i)); if (is_open_interval(num_i) && is_open_interval(den_i)) { // This case is simple because adding/subtracting infinitesimal quantities, will // not change the interval. div(num_i, den_i, inc_precision(prec, 2), v->interval()); } else { // The intervals num_i and den_i may not be open. // Example: numerator[num_idx] or denominator[num_idx] are rationals // that can be precisely represented as binary rationals. scoped_mpbqi new_num_i(bqim()); scoped_mpbqi new_den_i(bqim()); mpbq tiny_value(1, _prec*2); if (numerator.size() > 1) add_infinitesimal(num_i, sign_of_first_non_zero(numerator, 1) > 0, tiny_value, new_num_i); else bqim().set(new_num_i, num_i); if (denominator.size() > 1) add_infinitesimal(den_i, sign_of_first_non_zero(denominator, 1) > 0, tiny_value, new_den_i); else bqim().set(new_den_i, den_i); div(new_num_i, new_den_i, inc_precision(prec, 2), v->interval()); } if (check_precision(v->interval(), prec)) return true; _prec++; } } else { // The following condition must hold because gcd(numerator, denominator) == 1 // If num_idx > 0 and den_idx > 0, eps^{min(num_idx, den_idx)} is a factor of gcd(numerator, denominator) SASSERT(num_idx == 0 || den_idx == 0); int s = sign(numerator[num_idx]) * sign(denominator[den_idx]); // The following must hold since numerator[num_idx] and denominator[den_idx] are not zero. SASSERT(s != 0); if (num_idx == 0) { SASSERT(den_idx > 0); // |v| is bigger than any binary rational // Interval can't be refined. There is no way to isolate an infinity with an interval with binary rational end points. return false; } else { SASSERT(num_idx > 0); SASSERT(den_idx == 0); // |v| is infinitely close to zero. if (s == 1) { // it is close from above set_lower(v->interval(), mpbq(0)); set_upper(v->interval(), mpbq(1, prec)); } else { // it is close from below set_lower(v->interval(), mpbq(-1, prec)); set_upper(v->interval(), mpbq(0)); } return true; } } } bool refine_algebraic_interval(algebraic * a, unsigned prec) { save_interval_if_too_small(a, prec); if (a->sdt() != nullptr) { // We don't bisect the interval, since it contains more than one root. // To bisect this kind of interval we would have to use Tarski queries. return false; } else { mpbqi & a_i = a->interval(); if (a_i.lower_is_inf() || a_i.upper_is_inf()) { // we can't bisect the infinite intervals return false; } else { mpbqi & a_i = a->interval(); SASSERT(!a_i.lower_is_inf() && !a_i.upper_is_inf()); int lower_sign = INT_MIN; while (!check_precision(a_i, prec)) { checkpoint(); SASSERT(!bqm().eq(a_i.lower(), a_i.upper())); scoped_mpbq m(bqm()); bqm().add(a_i.lower(), a_i.upper(), m); bqm().div2(m); int mid_sign = eval_sign_at(a->p().size(), a->p().c_ptr(), m); if (mid_sign == 0) { // found the actual root // set interval [m, m] set_lower(a_i, m, false); set_upper(a_i, m, false); return true; } else { SASSERT(mid_sign == 1 || mid_sign == -1); if (lower_sign == INT_MIN) { // initialize lower_sign lower_sign = eval_sign_at(a->p().size(), a->p().c_ptr(), a_i.lower()); } SASSERT(lower_sign == 1 || lower_sign == -1); if (mid_sign == lower_sign) { // improved lower bound set_lower(a_i, m); } else { // improved upper bound set_upper(a_i, m); } } } return true; } } } bool refine_algebraic_interval(rational_function_value * v, unsigned prec) { SASSERT(v->ext()->is_algebraic()); polynomial const & n = v->num(); SASSERT(is_denominator_one(v)); unsigned _prec = prec; while (true) { if (!refine_coeffs_interval(n, _prec) || !refine_algebraic_interval(to_algebraic(v->ext()), _prec)) return false; update_rf_interval(v, prec); TRACE("rcf_algebraic", tout << "after update_rf_interval: " << magnitude(v->interval()) << " "; bqim().display(tout, v->interval()); tout << std::endl;); if (check_precision(v->interval(), prec)) return true; _prec++; } } /** \brief Refine the interval of v to the desired precision (1/2^prec). Return false in case of failure. A failure can only happen if v depends on infinitesimal values. */ bool refine_interval(value * v, unsigned prec) { checkpoint(); SASSERT(!is_zero(v)); int m = magnitude(interval(v)); if (m == INT_MIN || (m < 0 && static_cast(-m) > prec)) return true; save_interval_if_too_small(v, prec); if (is_nz_rational(v)) { refine_rational_interval(to_nz_rational(v), prec); return true; } else { rational_function_value * rf = to_rational_function(v); if (rf->ext()->is_transcendental()) { refine_transcendental_interval(rf, prec); return true; } else if (rf->ext()->is_infinitesimal()) return refine_infinitesimal_interval(rf, prec); else return refine_algebraic_interval(rf, prec); } } // --------------------------------- // // Sign determination // // --------------------------------- /** \brief Return the position of the first non-zero coefficient of p. */ static unsigned first_non_zero(polynomial const & p) { unsigned sz = p.size(); for (unsigned i = 0; i < sz; i++) { if (p[i] != 0) return i; } UNREACHABLE(); return UINT_MAX; } /** \brief Return the sign of the first non zero coefficient starting at position start_idx */ int sign_of_first_non_zero(polynomial const & p, unsigned start_idx) { unsigned sz = p.size(); SASSERT(start_idx < sz); for (unsigned i = start_idx; i < sz; i++) { if (p[i] != 0) return sign(p[i]); } UNREACHABLE(); return 0; } /** out <- in + infinitesimal (if plus_eps == true) out <- in - infinitesimal (if plus_eps == false) We use the following rules for performing the assignment If plus_eps == True If lower(in) == v (closed or open), then lower(out) == v and open If upper(in) == v and open, then upper(out) == v and open If upper(in) == v and closed, then upper(out) == new_v and open where new_v is v + tiny_value / 2^k, where k is the smallest natural such that sign(new_v) == sign(v) If plus_eps == False If lower(in) == v and open, then lower(out) == v and open If lower(in) == v and closed, then lower(out) == new_v and open If upper(in) == v (closed or open), then upper(out) == v and open where new_v is v - tiny_value / 2^k, where k is the smallest natural such that sign(new_v) == sign(v) */ void add_infinitesimal(mpbqi const & in, bool plus_eps, mpbq const & tiny_value, mpbqi & out) { set_interval(out, in); out.set_lower_is_open(true); out.set_upper_is_open(true); if (plus_eps) { if (!in.upper_is_open()) { scoped_mpbq tval(bqm()); tval = tiny_value; while (true) { bqm().add(in.upper(), tval, out.upper()); if (bqm().is_pos(in.upper()) == bqm().is_pos(out.upper())) return; bqm().div2(tval); checkpoint(); } } } else { if (!in.lower_is_open()) { scoped_mpbq tval(bqm()); tval = tiny_value; while (true) { bqm().sub(in.lower(), tval, out.lower()); if (bqm().is_pos(in.lower()) == bqm().is_pos(out.lower())) return; bqm().div2(tval); checkpoint(); } } } } /** \brief Determine the sign of an element of Q(trans_0, ..., trans_n) */ void determine_transcendental_sign(rational_function_value * v) { // Remark: the sign of a rational function value on an transcendental is never zero. // Reason: The transcendental can be the root of a polynomial. SASSERT(v->ext()->is_transcendental()); int m = magnitude(v->interval()); unsigned prec = 1; if (m < 0) prec = static_cast(-m) + 1; while (contains_zero(v->interval())) { refine_transcendental_interval(v, prec); prec++; } } /** \brief Determine the sign of an element of Q(trans_0, ..., trans_n, eps_0, ..., eps_m) */ void determine_infinitesimal_sign(rational_function_value * v) { // Remark: the sign of a rational function value on an infinitesimal is never zero. // Reason: An infinitesimal eps is transcendental in any field K. So, it can't be the root // of a polynomial. SASSERT(v->ext()->is_infinitesimal()); polynomial const & numerator = v->num(); polynomial const & denominator = v->den(); unsigned num_idx = first_non_zero(numerator); unsigned den_idx = first_non_zero(denominator); if (num_idx == 0 && den_idx == 0) { mpbqi const & num_i = interval(numerator[num_idx]); mpbqi const & den_i = interval(denominator[num_idx]); SASSERT(!contains_zero(num_i)); SASSERT(!contains_zero(den_i)); if (is_open_interval(num_i) && is_open_interval(den_i)) { // This case is simple because adding/subtracting infinitesimal quantities, will // not change the interval. div(num_i, den_i, m_ini_precision, v->interval()); } else { // The intervals num_i and den_i may not be open. // Example: numerator[num_idx] or denominator[num_idx] are rationals // that can be precisely represented as binary rationals. scoped_mpbqi new_num_i(bqim()); scoped_mpbqi new_den_i(bqim()); mpbq tiny_value(1, m_ini_precision); // 1/2^{m_ini_precision} if (numerator.size() > 1) add_infinitesimal(num_i, sign_of_first_non_zero(numerator, 1) > 0, tiny_value, new_num_i); else bqim().set(new_num_i, num_i); if (denominator.size() > 1) add_infinitesimal(den_i, sign_of_first_non_zero(denominator, 1) > 0, tiny_value, new_den_i); else bqim().set(new_den_i, den_i); div(new_num_i, new_den_i, m_ini_precision, v->interval()); } } else { // The following condition must hold because gcd(numerator, denominator) == 1 // If num_idx > 0 and den_idx > 0, eps^{min(num_idx, den_idx)} is a factor of gcd(numerator, denominator) SASSERT(num_idx == 0 || den_idx == 0); int s = sign(numerator[num_idx]) * sign(denominator[den_idx]); // The following must hold since numerator[num_idx] and denominator[den_idx] are not zero. SASSERT(s != 0); if (num_idx == 0) { SASSERT(den_idx > 0); // |v| is bigger than any binary rational if (s == 1) { // it is "oo" set_lower(v->interval(), m_plus_inf_approx); set_upper_inf(v->interval()); } else { // it is "-oo" set_lower_inf(v->interval()); set_upper(v->interval(), m_minus_inf_approx); } } else { SASSERT(num_idx > 0); SASSERT(den_idx == 0); // |v| is infinitely close to zero. if (s == 1) { // it is close from above set_lower(v->interval(), mpbq(0)); set_upper(v->interval(), mpbq(1, m_ini_precision)); } else { // it is close from below set_lower(v->interval(), mpbq(-1, m_ini_precision)); set_upper(v->interval(), mpbq(0)); } } } SASSERT(!contains_zero(v->interval())); } /** \brief Return true if x and q depend on infinitesimal values. That is, q(x) does not depend on infinitesimal values. */ bool depends_on_infinitesimals(polynomial const & q, algebraic * x) { return x->depends_on_infinitesimals() || depends_on_infinitesimals(q.size(), q.c_ptr()); } /** \brief This method is invoked when we know that q(x) is not zero and q(x) does not depend on infinitesimal values. The procedure will keep refining the intervals associated with x and coefficients of q until the interval of q(x) is of the form (l > 0, u) OR (l, u < 0) */ void refine_until_sign_determined(polynomial const & q, algebraic * x, mpbqi & r) { SASSERT(!depends_on_infinitesimals(q, x)); // If x and q do not depend on infinitesimals, must make sure that r satisfies our invariant // for intervals of polynomial values that do not depend on infinitesimals. // that is, // Given r == (l, u), l != 0 and u != 0 int m = magnitude(r); unsigned prec; if (m >= 0) prec = m_ini_precision; else prec = -m; while (true) { checkpoint(); VERIFY(refine_coeffs_interval(q, prec)); // can't fail because q does not depend on infinitesimals VERIFY(refine_algebraic_interval(x, prec)); // can't fail because x does not depend on infinitesimals // Update r polynomial_interval(q, x->interval(), r); // Since q and r do not depend on infinitesimals -oo and +oo will never be end-points. SASSERT(!r.lower_is_inf()); SASSERT(!r.upper_is_inf()); if (!contains_zero(r) && !bqm().is_zero(r.lower()) && !bqm().is_zero(r.upper())) return; // the interval r satisfies our requirements. prec++; } } /** \brief If q(x) != 0, return true and store in r an interval that contains the value q(x), but does not contain 0. If q(x) == 0, return false */ bool expensive_algebraic_poly_interval(polynomial const & q, algebraic * x, mpbqi & r) { polynomial_interval(q, x->interval(), r); if (!contains_zero(r)) { if (!depends_on_infinitesimals(q, x) && (bqm().is_zero(r.lower()) || bqm().is_zero(r.upper()))) { // we don't want intervals of the form (l, 0) and (0, u) when // q(x) does not depend on infinitesimals. refine_until_sign_determined(q, x, r); } return true; } int num_roots = x->num_roots_inside_interval(); SASSERT(x->sdt() != 0 || num_roots == 1); polynomial const & p = x->p(); int taq_p_q = TaQ(p.size(), p.c_ptr(), q.size(), q.c_ptr(), x->iso_interval()); if (num_roots == 1 && taq_p_q == 0) return false; // q(x) is zero if (taq_p_q == num_roots) { // q(x) is positive if (!depends_on_infinitesimals(q, x)) refine_until_sign_determined(q, x, r); else set_lower_zero(r); SASSERT(!contains_zero(r)); return true; } else if (taq_p_q == -num_roots) { // q(x) is negative if (!depends_on_infinitesimals(q, x)) refine_until_sign_determined(q, x, r); else set_upper_zero(r); SASSERT(!contains_zero(r)); return true; } else { SASSERT(num_roots > 1); SASSERT(x->sdt() != 0); int q_eq_0, q_gt_0, q_lt_0; value_ref_buffer q2(*this); count_signs_at_zeros_core(taq_p_q, p.size(), p.c_ptr(), q.size(), q.c_ptr(), x->iso_interval(), num_roots, q_eq_0, q_gt_0, q_lt_0, q2); if (q_eq_0 > 0 && q_gt_0 == 0 && q_lt_0 == 0) { // q(x) is zero return false; } else if (q_eq_0 == 0 && q_gt_0 > 0 && q_lt_0 == 0) { // q(x) is positive set_lower_zero(r); return true; } else if (q_eq_0 == 0 && q_gt_0 == 0 && q_lt_0 > 0) { // q(x) is negative set_upper_zero(r); return true; } else { sign_det & sdt = *(x->sdt()); // Remark: // By definition of algebraic and sign_det, we know that // sdt.M_s * [1, ..., 1]^t = sdt.taqrs()^t // That is, // [1, ..., 1]^t = sdt.M_s^-1 * sdt.taqrs()^t // Moreover the number of roots in x->iso_interval() is equal to the number of rows and columns in sdt.M_s. // The column j of std.M_s is associated with the sign condition sdt.m_scs[j]. // The row i of sdt.M_s is associated with the polynomial sdt.prs()[i]. // // The extension x is encoded using the sign condition x->sc_idx() of std.m_scs // scoped_mpz_matrix M(mm()); VERIFY(mk_sign_det_matrix(q_eq_0, q_gt_0, q_lt_0, M)); bool use_q2 = M.n() == 3; scoped_mpz_matrix new_M_s(mm()); mm().tensor_product(sdt.M_s, M, new_M_s); array const & prs = sdt.prs(); // polynomials associated with the rows of M_s array const & taqrs = sdt.taqrs(); // For each i in [0, taqrs.size()) TaQ(p, prs[i]; x->iso_interval()) == taqrs[i] SASSERT(prs.size() == taqrs.size()); int_buffer new_taqrs; value_ref_buffer prq(*this); // fill new_taqrs using taqrs and the new tarski queries containing q (and q^2 when use_q2 == true). for (unsigned i = 0; i < taqrs.size(); i++) { // Add TaQ(p, prs[i] * 1; x->iso_interval()) new_taqrs.push_back(taqrs[i]); // Add TaQ(p, prs[i] * q; x->iso_interval()) mul(prs[i].size(), prs[i].c_ptr(), q.size(), q.c_ptr(), prq); new_taqrs.push_back(TaQ(p.size(), p.c_ptr(), prq.size(), prq.c_ptr(), x->iso_interval())); if (use_q2) { // Add TaQ(p, prs[i] * q^2; x->iso_interval()) mul(prs[i].size(), prs[i].c_ptr(), q2.size(), q2.c_ptr(), prq); new_taqrs.push_back(TaQ(p.size(), p.c_ptr(), prq.size(), prq.c_ptr(), x->iso_interval())); } } int_buffer sc_cardinalities; sc_cardinalities.resize(new_taqrs.size(), 0); // Solve // new_M_s * sc_cardinalities = new_taqrs VERIFY(mm().solve(new_M_s, sc_cardinalities.c_ptr(), new_taqrs.c_ptr())); DEBUG_CODE({ // check if sc_cardinalities has the expected structure // - contains only 0 or 1 // - !use_q2 IMPLIES for all i in [0, taqrs.size()) (sc_cardinalities[2*i] == 1) + (sc_cardinalities[2*i + 1] == 1) == 1 // - use_q2 IMPLIES for all i in [0, taqrs.size()) (sc_cardinalities[3*i] == 1) + (sc_cardinalities[3*i + 1] == 1) + (sc_cardinalities[3*i + 2] == 1) == 1 for (unsigned i = 0; i < sc_cardinalities.size(); i++) { SASSERT(sc_cardinalities[i] == 0 || sc_cardinalities[i] == 1); } if (!use_q2) { for (unsigned i = 0; i < taqrs.size(); i++) { SASSERT((sc_cardinalities[2*i] == 1) + (sc_cardinalities[2*i + 1] == 1) == 1); } } else { for (unsigned i = 0; i < taqrs.size(); i++) { SASSERT((sc_cardinalities[3*i] == 1) + (sc_cardinalities[3*i + 1] == 1) + (sc_cardinalities[3*i + 2] == 1) == 1); } } }); // Remark: // Note that we found the sign of q for every root of p in the interval x->iso_interval() :) unsigned sc_idx = x->sc_idx(); if (use_q2) { if (sc_cardinalities[3*sc_idx] == 1) { // q(x) is zero return false; } else if (sc_cardinalities[3*sc_idx + 1] == 1) { // q(x) is positive set_lower_zero(r); return true; } else { SASSERT(sc_cardinalities[3*sc_idx + 2] == 1); // q(x) is negative set_upper_zero(r); return true; } } else { if (q_eq_0 == 0) { if (sc_cardinalities[2*sc_idx] == 1) { // q(x) is positive set_lower_zero(r); return true; } else { SASSERT(sc_cardinalities[2*sc_idx + 1] == 1); // q(x) is negative set_upper_zero(r); return true; } } else if (q_gt_0 == 0) { if (sc_cardinalities[2*sc_idx] == 1) { // q(x) is zero return false; } else { SASSERT(sc_cardinalities[2*sc_idx + 1] == 1); // q(x) is negative set_upper_zero(r); return true; } } else { SASSERT(q_lt_0 == 0); if (sc_cardinalities[2*sc_idx] == 1) { // q(x) is zero return false; } else { SASSERT(sc_cardinalities[2*sc_idx + 1] == 1); // q(x) is positive set_lower_zero(r); return true; } } } } } } bool expensive_determine_algebraic_sign(rational_function_value * v) { SASSERT(contains_zero(v->interval())); SASSERT(v->ext()->is_algebraic()); TRACE("rcf_algebraic_sign", tout << "expensive_determine_algebraic_sign\n"; display(tout, v, false); tout << "\ninterval: "; bqim().display(tout, v->interval()); tout << "\n";); algebraic * x = to_algebraic(v->ext()); scoped_mpbqi num_interval(bqim()); SASSERT(is_denominator_one(v)); if (!expensive_algebraic_poly_interval(v->num(), x, num_interval)) return false; // it is zero SASSERT(!contains_zero(num_interval)); set_interval(v->interval(), num_interval); SASSERT(!contains_zero(v->interval())); return true; // it is not zero } /** \brief Determine the sign of an rational function value p(x)/q(x) when x is an algebraic extension. */ bool determine_algebraic_sign(rational_function_value * v) { SASSERT(v->ext()->is_algebraic()); mpbqi & interval = v->interval(); if (interval.lower_is_inf() || interval.upper_is_inf()) { return expensive_determine_algebraic_sign(v); } else { int m = magnitude(v->interval()); unsigned prec = 1; if (m < 0) prec = static_cast(-m) + 1; while (contains_zero(v->interval())) { if (!refine_algebraic_interval(v, prec)) return expensive_determine_algebraic_sign(v); prec++; if (prec > m_max_precision) return expensive_determine_algebraic_sign(v); } SASSERT(!contains_zero(v->interval())); return true; } } /** \brief Determine the sign of the new rational function value. The idea is to keep refining the interval until interval of v does not contain 0. After a couple of steps we switch to expensive sign determination procedure. Return false if v is actually zero. */ bool determine_sign(rational_function_value * v) { if (!contains_zero(v->interval())) return true; bool r; switch (v->ext()->knd()) { case extension::TRANSCENDENTAL: determine_transcendental_sign(v); r = true; break; // it is never zero case extension::INFINITESIMAL: determine_infinitesimal_sign(v); r = true; break; // it is never zero case extension::ALGEBRAIC: r = determine_algebraic_sign(v); break; default: UNREACHABLE(); r = false; } TRACE("rcf_determine_sign_bug", tout << "result: " << r << "\n"; display_compact(tout, v); tout << "\n"; tout << "sign: " << sign(v) << "\n";); return r; } bool determine_sign(value_ref & r) { SASSERT(is_rational_function(r.get())); return determine_sign(to_rational_function(r.get())); } // --------------------------------- // // Arithmetic operations // // --------------------------------- /** \brief Compute polynomials new_p1 and new_p2 s.t. - p1/p2 == new_p1/new_p2, AND - new_p2 is a Monic polynomial, AND - gcd(new_p1, new_p2) == 1 */ void normalize_fraction(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & new_p1, value_ref_buffer & new_p2) { INC_DEPTH(); TRACE("rcf_arith", tout << "normalize [" << m_exec_depth << "]\n"; display_poly(tout, sz1, p1); tout << "\n"; display_poly(tout, sz2, p2); tout << "\n";); SASSERT(sz1 > 0 && sz2 > 0); if (sz2 == 1) { // - new_p1 <- p1/p2[0]; new_p2 <- one IF sz2 == 1 div(sz1, p1, p2[0], new_p1); new_p2.reset(); new_p2.push_back(one()); } else { value * lc = p2[sz2 - 1]; if (is_rational_one(lc)) { // p2 is monic normalize_num_monic_den(sz1, p1, sz2, p2, new_p1, new_p2); } else { // p2 is not monic value_ref_buffer tmp1(*this); value_ref_buffer tmp2(*this); div(sz1, p1, lc, tmp1); div(sz2, p2, lc, tmp2); normalize_num_monic_den(tmp1.size(), tmp1.c_ptr(), tmp2.size(), tmp2.c_ptr(), new_p1, new_p2); } } TRACE("normalize_fraction_bug", display_poly(tout, sz1, p1); tout << "\n"; display_poly(tout, sz2, p2); tout << "\n"; tout << "====>\n"; display_poly(tout, new_p1.size(), new_p1.c_ptr()); tout << "\n"; display_poly(tout, new_p2.size(), new_p2.c_ptr()); tout << "\n";); } /** \brief Auxiliary function for normalize_fraction. It produces new_p1 and new_p2 s.t. new_p1/new_p2 == p1/p2 gcd(new_p1, new_p2) == 1 Assumptions: \pre p2 is monic \pre sz2 > 1 */ void normalize_num_monic_den(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & new_p1, value_ref_buffer & new_p2) { SASSERT(sz2 > 1); SASSERT(is_rational_one(p2[sz2-1])); value_ref_buffer g(*this); gcd(sz1, p1, sz2, p2, g); SASSERT(is_monic(g)); if (is_rational_one(g)) { new_p1.append(sz1, p1); new_p2.append(sz2, p2); } else { div(sz1, p1, g.size(), g.c_ptr(), new_p1); div(sz2, p2, g.size(), g.c_ptr(), new_p2); SASSERT(is_monic(new_p2)); } } /** \brief Simplify p1(x) using x's defining polynomial. By definition of polynomial division, we have: new_p1(x) == quotient(p1,p)(x) * p(x) + rem(p1,p)(x) Since p(x) == 0, we have that new_p1(x) = rem(p1,p)(x) */ void normalize_algebraic(algebraic * x, unsigned sz1, value * const * p1, value_ref_buffer & new_p1) { polynomial const & p = x->p(); if (!m_lazy_algebraic_normalization || !m_in_aux_values || is_monic(p)) { rem(sz1, p1, p.size(), p.c_ptr(), new_p1); } else { new_p1.reset(); new_p1.append(sz1, p1); } } /** \brief Create a new value using the a->ext(), and the given numerator and denominator. Use interval(a) + interval(b) as an initial approximation for the interval of the result, and invoke determine_sign() */ void mk_add_value(rational_function_value * a, value * b, unsigned num_sz, value * const * num, unsigned den_sz, value * const * den, value_ref & r) { SASSERT(num_sz > 0); // den_sz may be zero for algebraic extensions. // We do not use denominators for algebraic extensions. if (num_sz == 1 && den_sz <= 1) { // In this case, the normalization rules guarantee that den is one. SASSERT(den_sz == 0 || is_rational_one(den[0])); r = num[0]; } else { scoped_mpbqi ri(bqim()); bqim().add(interval(a), interval(b), ri); r = mk_rational_function_value_core(a->ext(), num_sz, num, den_sz, den); swap(r->interval(), ri); if (determine_sign(r)) { SASSERT(!contains_zero(r->interval())); } else { // The new value is 0 r = nullptr; } } } /** \brief Add a value of 'a' the form n/1 with b where rank(a) > rank(b) */ void add_p_v(rational_function_value * a, value * b, value_ref & r) { SASSERT(is_denominator_one(a)); SASSERT(compare_rank(a, b) > 0); polynomial const & an = a->num(); polynomial const & one = a->den(); SASSERT(an.size() > 1); value_ref_buffer new_num(*this); add(an.size(), an.c_ptr(), b, new_num); SASSERT(new_num.size() == an.size()); mk_add_value(a, b, new_num.size(), new_num.c_ptr(), one.size(), one.c_ptr(), r); } /** \brief Add a value 'a' of the form n/d with b where rank(a) > rank(b) */ void add_rf_v(rational_function_value * a, value * b, value_ref & r) { value_ref_buffer b_ad(*this); value_ref_buffer num(*this); polynomial const & an = a->num(); if (is_denominator_one(a)) { add_p_v(a, b, r); } else { SASSERT(!a->ext()->is_algebraic()); polynomial const & ad = a->den(); // b_ad <- b * ad mul(b, ad.size(), ad.c_ptr(), b_ad); // num <- a + b * ad add(an.size(), an.c_ptr(), b_ad.size(), b_ad.c_ptr(), num); if (num.empty()) r = nullptr; else { value_ref_buffer new_num(*this); value_ref_buffer new_den(*this); normalize_fraction(num.size(), num.c_ptr(), ad.size(), ad.c_ptr(), new_num, new_den); SASSERT(!new_num.empty()); mk_add_value(a, b, new_num.size(), new_num.c_ptr(), new_den.size(), new_den.c_ptr(), r); } } } /** \brief Add values 'a' and 'b' of the form n/1 and rank(a) == rank(b) */ void add_p_p(rational_function_value * a, rational_function_value * b, value_ref & r) { SASSERT(is_denominator_one(a)); SASSERT(is_denominator_one(b)); SASSERT(compare_rank(a, b) == 0); polynomial const & an = a->num(); polynomial const & one = a->den(); polynomial const & bn = b->num(); value_ref_buffer new_num(*this); add(an.size(), an.c_ptr(), bn.size(), bn.c_ptr(), new_num); if (new_num.empty()) r = nullptr; else { // We don't need to invoke normalize_algebraic even if x (== a->ext()) is algebraic. // Reason: by construction the polynomials a->num() and b->num() are "normalized". // That is, their degrees are < degree of the polynomial defining x. // Moreover, when we add polynomials, the degree can only decrease. // So, degree of new_num must be < degree of x's defining polynomial. mk_add_value(a, b, new_num.size(), new_num.c_ptr(), one.size(), one.c_ptr(), r); } } /** \brief Add values 'a' and 'b' of the form n/d and rank(a) == rank(b) */ void add_rf_rf(rational_function_value * a, rational_function_value * b, value_ref & r) { SASSERT(compare_rank(a, b) == 0); polynomial const & an = a->num(); polynomial const & bn = b->num(); if (is_denominator_one(a) && is_denominator_one(b)) { add_p_p(a, b, r); } else { SASSERT(!a->ext()->is_algebraic()); polynomial const & ad = a->den(); polynomial const & bd = b->den(); value_ref_buffer an_bd(*this); value_ref_buffer bn_ad(*this); mul(an.size(), an.c_ptr(), bd.size(), bd.c_ptr(), an_bd); mul(bn.size(), bn.c_ptr(), ad.size(), ad.c_ptr(), bn_ad); value_ref_buffer num(*this); add(an_bd.size(), an_bd.c_ptr(), bn_ad.size(), bn_ad.c_ptr(), num); if (num.empty()) { r = nullptr; } else { value_ref_buffer den(*this); mul(ad.size(), ad.c_ptr(), bd.size(), bd.c_ptr(), den); value_ref_buffer new_num(*this); value_ref_buffer new_den(*this); normalize_fraction(num.size(), num.c_ptr(), den.size(), den.c_ptr(), new_num, new_den); SASSERT(!new_num.empty()); mk_add_value(a, b, new_num.size(), new_num.c_ptr(), new_den.size(), new_den.c_ptr(), r); } } } void add(value * a, value * b, value_ref & r) { if (a == nullptr) { r = b; } else if (b == nullptr) { r = a; } else if (is_nz_rational(a) && is_nz_rational(b)) { scoped_mpq v(qm()); qm().add(to_mpq(a), to_mpq(b), v); if (qm().is_zero(v)) r = nullptr; else r = mk_rational_and_swap(v); } else { INC_DEPTH(); TRACE("rcf_arith", tout << "add [" << m_exec_depth << "]\n"; display(tout, a, false); tout << "\n"; display(tout, b, false); tout << "\n";); switch (compare_rank(a, b)) { case -1: add_rf_v(to_rational_function(b), a, r); break; case 0: add_rf_rf(to_rational_function(a), to_rational_function(b), r); break; case 1: add_rf_v(to_rational_function(a), b, r); break; default: UNREACHABLE(); } } } void sub(value * a, value * b, value_ref & r) { if (a == nullptr) { neg(b, r); } else if (b == nullptr) { r = a; } else if (is_nz_rational(a) && is_nz_rational(b)) { scoped_mpq v(qm()); qm().sub(to_mpq(a), to_mpq(b), v); if (qm().is_zero(v)) r = nullptr; else r = mk_rational_and_swap(v); } else { value_ref neg_b(*this); neg(b, neg_b); switch (compare_rank(a, neg_b)) { case -1: add_rf_v(to_rational_function(neg_b), a, r); break; case 0: add_rf_rf(to_rational_function(a), to_rational_function(neg_b), r); break; case 1: add_rf_v(to_rational_function(a), neg_b, r); break; default: UNREACHABLE(); } } } void neg_rf(rational_function_value * a, value_ref & r) { polynomial const & an = a->num(); polynomial const & ad = a->den(); value_ref_buffer new_num(*this); neg(an.size(), an.c_ptr(), new_num); scoped_mpbqi ri(bqim()); bqim().neg(interval(a), ri); r = mk_rational_function_value_core(a->ext(), new_num.size(), new_num.c_ptr(), ad.size(), ad.c_ptr()); swap(r->interval(), ri); SASSERT(!contains_zero(r->interval())); } void neg(value * a, value_ref & r) { if (a == nullptr) { r = nullptr; } else if (is_nz_rational(a)) { scoped_mpq v(qm()); qm().set(v, to_mpq(a)); qm().neg(v); r = mk_rational_and_swap(v); } else { neg_rf(to_rational_function(a), r); } } /** \brief Create a new value using the a->ext(), and the given numerator and denominator. Use interval(a) * interval(b) as an initial approximation for the interval of the result, and invoke determine_sign() */ void mk_mul_value(rational_function_value * a, value * b, unsigned num_sz, value * const * num, unsigned den_sz, value * const * den, value_ref & r) { SASSERT(num_sz > 0); if (num_sz == 1 && den_sz <= 1) { // den_sz may be zero for algebraic extensions. // We do not use denominators for algebraic extensions. // In this case, the normalization rules guarantee that den is one. SASSERT(den_sz == 0 || is_rational_one(den[0])); r = num[0]; } else { scoped_mpbqi ri(bqim()); bqim().mul(interval(a), interval(b), ri); r = mk_rational_function_value_core(a->ext(), num_sz, num, den_sz, den); swap(ri, r->interval()); if (determine_sign(r)) { SASSERT(!contains_zero(r->interval())); } else { // The new value is 0 r = nullptr; } } } /** \brief Multiply a value of 'a' the form n/1 with b where rank(a) > rank(b) */ void mul_p_v(rational_function_value * a, value * b, value_ref & r) { SASSERT(is_denominator_one(a)); SASSERT(b != 0); SASSERT(compare_rank(a, b) > 0); polynomial const & an = a->num(); polynomial const & one = a->den(); SASSERT(an.size() > 1); value_ref_buffer new_num(*this); mul(b, an.size(), an.c_ptr(), new_num); SASSERT(new_num.size() == an.size()); mk_mul_value(a, b, new_num.size(), new_num.c_ptr(), one.size(), one.c_ptr(), r); } /** \brief Multiply a value 'a' of the form n/d with b where rank(a) > rank(b) */ void mul_rf_v(rational_function_value * a, value * b, value_ref & r) { polynomial const & an = a->num(); if (is_denominator_one(a)) { mul_p_v(a, b, r); } else { SASSERT(!a->ext()->is_algebraic()); polynomial const & ad = a->den(); value_ref_buffer num(*this); // num <- b * an mul(b, an.size(), an.c_ptr(), num); SASSERT(num.size() == an.size()); value_ref_buffer new_num(*this); value_ref_buffer new_den(*this); normalize_fraction(num.size(), num.c_ptr(), ad.size(), ad.c_ptr(), new_num, new_den); SASSERT(!new_num.empty()); mk_mul_value(a, b, new_num.size(), new_num.c_ptr(), new_den.size(), new_den.c_ptr(), r); } } /** \brief Multiply values 'a' and 'b' of the form n/1 and rank(a) == rank(b) */ void mul_p_p(rational_function_value * a, rational_function_value * b, value_ref & r) { SASSERT(is_denominator_one(a)); SASSERT(is_denominator_one(b)); SASSERT(compare_rank(a, b) == 0); polynomial const & an = a->num(); polynomial const & one = a->den(); polynomial const & bn = b->num(); value_ref_buffer new_num(*this); mul(an.size(), an.c_ptr(), bn.size(), bn.c_ptr(), new_num); SASSERT(!new_num.empty()); extension * x = a->ext(); if (x->is_algebraic()) { value_ref_buffer new_num2(*this); normalize_algebraic(to_algebraic(x), new_num.size(), new_num.c_ptr(), new_num2); SASSERT(!new_num.empty()); mk_mul_value(a, b, new_num2.size(), new_num2.c_ptr(), one.size(), one.c_ptr(), r); } else { mk_mul_value(a, b, new_num.size(), new_num.c_ptr(), one.size(), one.c_ptr(), r); } } /** \brief Multiply values 'a' and 'b' of the form n/d and rank(a) == rank(b) */ void mul_rf_rf(rational_function_value * a, rational_function_value * b, value_ref & r) { SASSERT(compare_rank(a, b) == 0); polynomial const & an = a->num(); polynomial const & bn = b->num(); if (is_denominator_one(a) && is_denominator_one(b)) { mul_p_p(a, b, r); } else { SASSERT(!a->ext()->is_algebraic()); polynomial const & ad = a->den(); polynomial const & bd = b->den(); value_ref_buffer num(*this); value_ref_buffer den(*this); mul(an.size(), an.c_ptr(), bn.size(), bn.c_ptr(), num); mul(ad.size(), ad.c_ptr(), bd.size(), bd.c_ptr(), den); SASSERT(!num.empty()); SASSERT(!den.empty()); value_ref_buffer new_num(*this); value_ref_buffer new_den(*this); normalize_fraction(num.size(), num.c_ptr(), den.size(), den.c_ptr(), new_num, new_den); SASSERT(!new_num.empty()); mk_mul_value(a, b, new_num.size(), new_num.c_ptr(), new_den.size(), new_den.c_ptr(), r); } } void mul(value * a, value * b, value_ref & r) { if (a == nullptr || b == nullptr) { r = nullptr; } else if (is_rational_one(a)) { r = b; } else if (is_rational_one(b)) { r = a; } else if (is_rational_minus_one(a)) { neg(b, r); } else if (is_rational_minus_one(b)) { neg(a, r); } else if (is_nz_rational(a) && is_nz_rational(b)) { scoped_mpq v(qm()); qm().mul(to_mpq(a), to_mpq(b), v); r = mk_rational_and_swap(v); } else { INC_DEPTH(); TRACE("rcf_arith", tout << "mul [" << m_exec_depth << "]\n"; display(tout, a, false); tout << "\n"; display(tout, b, false); tout << "\n";); switch (compare_rank(a, b)) { case -1: mul_rf_v(to_rational_function(b), a, r); break; case 0: mul_rf_rf(to_rational_function(a), to_rational_function(b), r); break; case 1: mul_rf_v(to_rational_function(a), b, r); break; default: UNREACHABLE(); } } } void div(value * a, value * b, value_ref & r) { if (a == nullptr) { r = nullptr; } else if (b == nullptr) { throw exception("division by zero"); } else if (is_rational_one(b)) { r = a; } else if (is_rational_one(a)) { inv(b, r); } else if (is_rational_minus_one(b)) { neg(a, r); } else if (is_nz_rational(a) && is_nz_rational(b)) { scoped_mpq v(qm()); qm().div(to_mpq(a), to_mpq(b), v); r = mk_rational_and_swap(v); } else { value_ref inv_b(*this); inv(b, inv_b); switch (compare_rank(a, inv_b)) { case -1: mul_rf_v(to_rational_function(inv_b), a, r); break; case 0: mul_rf_rf(to_rational_function(a), to_rational_function(inv_b), r); break; case 1: mul_rf_v(to_rational_function(a), inv_b, r); break; default: UNREACHABLE(); } } } /** \brief Invert 1/q(alpha) given that p(alpha) = 0. That is, we find h s.t. q(alpha) * h(alpha) = 1 The procedure succeeds (and returns true) if the GCD(q, p) = 1. If the GCD(q, p) != 1, then it returns false, and store the GCD in g. The following procedure is essentially a special case of the extended polynomial GCD algorithm. */ bool inv_algebraic(unsigned q_sz, value * const * q, unsigned p_sz, value * const * p, value_ref_buffer & g, value_ref_buffer & h) { TRACE("inv_algebraic", tout << "q: "; display_poly(tout, q_sz, q); tout << "\n"; tout << "p: "; display_poly(tout, p_sz, p); tout << "\n";); SASSERT(q_sz > 0); SASSERT(q_sz < p_sz); // Q <- q value_ref_buffer Q(*this); Q.append(q_sz, q); // R <- 1 value_ref_buffer R(*this); R.push_back(one()); value_ref_buffer Quo(*this), Rem(*this), aux(*this); // We find h(alpha), by rewriting the equation // q(alpha) * h(alpha) = 1 // until we have // 1 * h(alpha) = R(alpha) while (true) { // In every iteration of the loop we have // Q(alpha) * h(alpha) = R(alpha) TRACE("inv_algebraic", tout << "Q: "; display_poly(tout, Q.size(), Q.c_ptr()); tout << "\n"; tout << "R: "; display_poly(tout, R.size(), R.c_ptr()); tout << "\n";); if (Q.size() == 1) { // If the new Q is the constant polynomial, they we are done. // We just divide R by Q[0]. // h(alpha) = R(alpha) / Q[0] div(R.size(), R.c_ptr(), Q[0], h); TRACE("inv_algebraic", tout << "h: "; display_poly(tout, h.size(), h.c_ptr()); tout << "\n";); // g <- 1 g.reset(); g.push_back(one()); return true; } else { div_rem(p_sz, p, Q.size(), Q.c_ptr(), Quo, Rem); if (Rem.empty()) { // failed // GCD(q, p) != 1 g = Q; mk_monic(g); return false; } else { // By the definition of polynomial division, we have // p == Quo * Q + Rem // Since, we have p(alpha) = 0 // Quo(alpha) * Q(alpha) = -Rem(alpha) (*) // Now, if we multiply the equation // Q(alpha) * h(alpha) = R(alpha) // by Quo(alpha) and apply (*), we get // -Rem(alpha) * h(alpha) = R(alpha) * Quo(alpha) // Thus, we update Q, and R for the next iteration, as // Q <- -REM // R <- R * Quo // Q <- -Rem neg(Rem.size(), Rem.c_ptr(), Q); mul(R.size(), R.c_ptr(), Quo.size(), Quo.c_ptr(), aux); // Moreover since p(alpha) = 0, we can simplify Q, by using // Q(alpha) = REM(Q, p)(alpha) rem(aux.size(), aux.c_ptr(), p_sz, p, R); SASSERT(R.size() < p_sz); // } } } } /** \brief r <- 1/a specialized version when a->ext() is algebraic. It avoids the use of rational functions. */ void inv_algebraic(rational_function_value * a, value_ref & r) { SASSERT(a->ext()->is_algebraic()); SASSERT(is_denominator_one(a)); scoped_mpbqi ri(bqim()); bqim().inv(interval(a), ri); algebraic * alpha = to_algebraic(a->ext()); polynomial const & q = a->num(); polynomial const & p = alpha->p(); value_ref_buffer norm_q(*this); // since p(alpha) = 0, we have that q(alpha) = rem(q, p)(alpha) rem(q.size(), q.c_ptr(), p.size(), p.c_ptr(), norm_q); SASSERT(norm_q.size() < p.size()); value_ref_buffer new_num(*this), g(*this); if (inv_algebraic(norm_q.size(), norm_q.c_ptr(), p.size(), p.c_ptr(), g, new_num)) { if (new_num.size() == 1) { r = new_num[0]; } else { r = mk_rational_function_value_core(alpha, new_num.size(), new_num.c_ptr()); swap(r->interval(), ri); SASSERT(!contains_zero(r->interval())); } } else { // We failed to compute 1/a // because q and p are not co-prime // This can happen because we don't use minimal // polynomials to represent algebraic extensions such // as alpha. // We recover from the failure by refining the defining polynomial of alpha // with p/gcd(p, q) // Remark: g contains the gcd of p, q // And try again :) value_ref_buffer new_p(*this); div(p.size(), p.c_ptr(), g.size(), g.c_ptr(), new_p); if (m_clean_denominators) { value_ref_buffer tmp(*this); value_ref d(*this); clean_denominators(new_p.size(), new_p.c_ptr(), tmp, d); new_p = tmp; } SASSERT(new_p.size() >= 2); if (new_p.size() == 2) { // Easy case: alpha is actually equal to // -new_p[0]/new_p[1] value_ref alpha_val(*this); alpha_val = new_p[0]; neg(alpha_val, alpha_val); div(alpha_val, new_p[1], alpha_val); // Thus, a is equal to q(alpha_val) value_ref new_a(*this); mk_polynomial_value(q.size(), q.c_ptr(), alpha_val, new_a); // Remark new_a does not depend on alpha anymore // r == 1/inv(new_a) inv(new_a, r); } else if (alpha->sdt() == nullptr) { // Another easy case: we just have to replace // alpha->p() with new_p. // The m_iso_interval for p() is also an isolating interval for new_p, // since the roots of new_p() are a subset of the roots of p reset_p(alpha->m_p); set_p(alpha->m_p, new_p.size(), new_p.c_ptr()); // The new call will succeed because q and new_p are co-prime inv_algebraic(a, r); } else { // Let sdt be alpha->sdt(); // In principal, the signs of the polynomials sdt->qs can be used // to discriminate the roots of new_p. The signs of this polynomials // depend only on alpha, and not on the polynomial used to define alpha // So, in principle, we can reuse m_qs and m_sign_conditions. // However, we have to recompute the tarski queries with respect to new_p. // This values will be different, since new_p has less roots than p. // // Instead of trying to reuse the information in sdt, we simply // isolate the roots of new_p, and check the one that is equal to alpha. // and copy all the information from them. SASSERT(new_p.size() > 2); // we can invoke nl_nz_sqf_isolate_roots, because we know // - new_p is not linear // - new_p is square free (it is a factor of the square free polynomial p) // - 0 is not a root of new_p (it is a factor of p, and 0 is not a root of p) numeral_vector roots; nl_nz_sqf_isolate_roots(new_p.size(), new_p.c_ptr(), roots); SASSERT(roots.size() > 0); algebraic * new_alpha; if (roots.size() == 1) { new_alpha = to_algebraic(to_rational_function(roots[0].m_value)->ext()); } else { value_ref alpha_val(*this); alpha_val = mk_rational_function_value(alpha); // search for the root that is equal to alpha unsigned i = 0; for (i = 0; i < roots.size(); i++) { if (compare(alpha_val, roots[i].m_value) == 0) { // found it; break; } } new_alpha = to_algebraic(to_rational_function(roots[i].m_value)->ext()); } SASSERT(new_alpha->p().size() == new_p.size()); // We now that alpha and new_alpha represent the same value. // Thus, we update alpha fields with the fields from new_alpha. // copy new_alpha->m_p reset_p(alpha->m_p); set_p(alpha->m_p, new_alpha->m_p.size(), new_alpha->m_p.c_ptr()); // copy new_alpha->m_sign_det inc_ref_sign_det(new_alpha->m_sign_det); dec_ref_sign_det(alpha->m_sign_det); alpha->m_sign_det = new_alpha->m_sign_det; // copy remaining fields set_interval(alpha->m_iso_interval, new_alpha->m_iso_interval); alpha->m_sc_idx = new_alpha->m_sc_idx; alpha->m_depends_on_infinitesimals = new_alpha->m_depends_on_infinitesimals; // The new call will succeed because q and new_p are co-prime inv_algebraic(a, r); } } } void inv_rf(rational_function_value * a, value_ref & r) { if (a->ext()->is_algebraic()) { inv_algebraic(a, r); } else { SASSERT(!a->ext()->is_algebraic()); polynomial const & an = a->num(); polynomial const & ad = a->den(); scoped_mpbqi ri(bqim()); bqim().inv(interval(a), ri); // The GCD of an and ad is one, we may use a simpler version of normalize value_ref_buffer new_num(*this); value_ref_buffer new_den(*this); normalize_fraction(ad.size(), ad.c_ptr(), an.size(), an.c_ptr(), new_num, new_den); r = mk_rational_function_value_core(a->ext(), new_num.size(), new_num.c_ptr(), new_den.size(), new_den.c_ptr()); swap(r->interval(), ri); SASSERT(!contains_zero(r->interval())); } } void inv(value * a, value_ref & r) { if (a == nullptr) { throw exception("division by zero"); } if (is_nz_rational(a)) { scoped_mpq v(qm()); qm().inv(to_mpq(a), v); r = mk_rational_and_swap(v); } else { inv_rf(to_rational_function(a), r); } } void set(numeral & n, value * v) { inc_ref(v); dec_ref(n.m_value); n.m_value = v; } void set(numeral & n, value_ref const & v) { set(n, v.get()); } void neg(numeral & a) { value_ref r(*this); neg(a.m_value, r); set(a, r); } void neg(numeral const & a, numeral & b) { value_ref r(*this); neg(a.m_value, r); set(b, r); } void inv(numeral & a) { value_ref r(*this); inv(a.m_value, r); set(a, r); } void inv(numeral const & a, numeral & b) { value_ref r(*this); inv(a.m_value, r); set(b, r); } void add(numeral const & a, numeral const & b, numeral & c) { value_ref r(*this); add(a.m_value, b.m_value, r); set(c, r); } void sub(numeral const & a, numeral const & b, numeral & c) { value_ref r(*this); sub(a.m_value, b.m_value, r); set(c, r); } void mul(numeral const & a, numeral const & b, numeral & c) { value_ref r(*this); mul(a.m_value, b.m_value, r); set(c, r); } void div(numeral const & a, numeral const & b, numeral & c) { value_ref r(*this); div(a.m_value, b.m_value, r); set(c, r); } /** \brief a <- b^{1/k} */ void root(numeral const & a, unsigned k, numeral & b) { if (k == 0) throw exception("0-th root is indeterminate"); if (k == 1 || is_zero(a)) { set(b, a); return; } if (sign(a) < 0 && k % 2 == 0) throw exception("even root of negative number"); // create the polynomial p of the form x^k - a value_ref_buffer p(*this); value_ref neg_a(*this); neg(a.m_value, neg_a); p.push_back(neg_a); for (unsigned i = 0; i < k - 1; i++) p.push_back(nullptr); p.push_back(one()); numeral_vector roots; nz_isolate_roots(p.size(), p.c_ptr(), roots); SASSERT(roots.size() == 1 || roots.size() == 2); if (roots.size() == 1 || sign(roots[0].m_value) > 0) { set(b, roots[0]); } else { SASSERT(roots.size() == 2); SASSERT(sign(roots[1].m_value) > 0); set(b, roots[1]); } del(roots); } /** \brief a <- b^k */ void power(numeral const & a, unsigned k, numeral & b) { unsigned mask = 1; value_ref power(*this); value_ref _b(*this); power = a.m_value; _b = one(); while (mask <= k) { checkpoint(); if (mask & k) mul(_b, power, _b); mul(power, power, power); mask = mask << 1; } set(b, _b); } // --------------------------------- // // Comparison // // --------------------------------- int compare(value * a, value * b) { if (a == nullptr) return -sign(b); else if (b == nullptr) return sign(a); else if (is_nz_rational(a) && is_nz_rational(b)) { if (qm().eq(to_mpq(a), to_mpq(b))) return 0; else return qm().lt(to_mpq(a), to_mpq(b)) ? -1 : 1; } else { // FUTURE: try to refine interval before switching to sub+sign approach if (bqim().before(interval(a), interval(b))) return -1; else if (bqim().before(interval(b), interval(a))) return 1; else { value_ref diff(*this); sub(a, b, diff); return sign(diff); } } } int compare(numeral const & a, numeral const & b) { return compare(a.m_value, b.m_value); } // --------------------------------- // // "Pretty printing" // // --------------------------------- struct collect_algebraic_refs { char_vector m_visited; // Set of visited algebraic extensions. ptr_vector m_found; // vector/list of visited algebraic extensions. void mark(extension * ext) { if (ext->is_algebraic()) { m_visited.reserve(ext->idx() + 1, false); if (!m_visited[ext->idx()]) { m_visited[ext->idx()] = true; algebraic * a = to_algebraic(ext); m_found.push_back(a); mark(a->p()); } } } void mark(polynomial const & p) { for (unsigned i = 0; i < p.size(); i++) { mark(p[i]); } } void mark(value * v) { if (v == nullptr || is_nz_rational(v)) return; rational_function_value * rf = to_rational_function(v); mark(rf->ext()); mark(rf->num()); mark(rf->den()); } }; static unsigned num_nz_coeffs(polynomial const & p) { unsigned r = 0; for (unsigned i = 0; i < p.size(); i++) { if (p[i]) r++; } return r; } bool use_parenthesis(value * v) const { if (is_zero(v) || is_nz_rational(v)) return false; rational_function_value * rf = to_rational_function(v); return num_nz_coeffs(rf->num()) > 1 || !is_denominator_one(rf); } template void display_polynomial(std::ostream & out, unsigned sz, value * const * p, DisplayVar const & display_var, bool compact, bool pp) const { if (sz == 0) { out << "0"; return; } unsigned i = sz; bool first = true; while (i > 0) { --i; if (p[i] == nullptr) continue; if (first) first = false; else out << " + "; if (i == 0) display(out, p[i], compact, pp); else { if (!is_rational_one(p[i])) { if (use_parenthesis(p[i])) { out << "("; display(out, p[i], compact, pp); out << ")"; if (pp) out << " "; else out << "*"; } else { display(out, p[i], compact, pp); if (pp) out << " "; else out << "*"; } } display_var(out, compact, pp); if (i > 1) { if (pp) out << "" << i << ""; else out << "^" << i; } } } } template void display_polynomial(std::ostream & out, polynomial const & p, DisplayVar const & display_var, bool compact, bool pp) const { display_polynomial(out, p.size(), p.c_ptr(), display_var, compact, pp); } struct display_free_var_proc { void operator()(std::ostream & out, bool compact, bool pp) const { out << "x"; } }; struct display_ext_proc { imp const & m; extension * m_ref; display_ext_proc(imp const & _m, extension * r):m(_m), m_ref(r) {} void operator()(std::ostream & out, bool compact, bool pp) const { m.display_ext(out, m_ref, compact, pp); } }; void display_polynomial_expr(std::ostream & out, polynomial const & p, extension * ext, bool compact, bool pp) const { display_polynomial(out, p, display_ext_proc(*this, ext), compact, pp); } static void display_poly_sign(std::ostream & out, int s) { if (s < 0) out << " < 0"; else if (s == 0) out << " = 0"; else out << " > 0"; } void display_sign_conditions(std::ostream & out, sign_condition * sc) const { bool first = true; out << "{"; while (sc) { if (first) first = false; else out << ", "; out << "q(" << sc->qidx() << ")"; display_poly_sign(out, sc->sign()); sc = sc->prev(); } out << "}"; } void display_sign_conditions(std::ostream & out, sign_condition * sc, array const & qs, bool compact, bool pp) const { bool first = true; out << "{"; while (sc) { if (first) first = false; else out << ", "; display_polynomial(out, qs[sc->qidx()], display_free_var_proc(), compact, pp); display_poly_sign(out, sc->sign()); sc = sc->prev(); } out << "}"; } void display_interval(std::ostream & out, mpbqi const & i, bool pp) const { if (pp) bqim().display_pp(out, i); else bqim().display(out, i); } void display_algebraic_def(std::ostream & out, algebraic * a, bool compact, bool pp) const { out << "root("; display_polynomial(out, a->p(), display_free_var_proc(), compact, pp); out << ", "; display_interval(out, a->iso_interval(), pp); out << ", "; if (a->sdt() != nullptr) display_sign_conditions(out, a->sdt()->sc(a->sc_idx()), a->sdt()->qs(), compact, pp); else out << "{}"; out << ")"; } void display_poly(std::ostream & out, unsigned n, value * const * p) const { collect_algebraic_refs c; for (unsigned i = 0; i < n; i++) c.mark(p[i]); display_polynomial(out, n, p, display_free_var_proc(), true, false); std::sort(c.m_found.begin(), c.m_found.end(), rank_lt_proc()); for (unsigned i = 0; i < c.m_found.size(); i++) { algebraic * ext = c.m_found[i]; out << "\n r!" << ext->idx() << " := "; display_algebraic_def(out, ext, true, false); } } void display_ext(std::ostream & out, extension * r, bool compact, bool pp) const { switch (r->knd()) { case extension::TRANSCENDENTAL: to_transcendental(r)->display(out, pp); break; case extension::INFINITESIMAL: to_infinitesimal(r)->display(out, pp); break; case extension::ALGEBRAIC: if (compact) { if (pp) out << "α" << r->idx() << ""; else out << "r!" << r->idx(); } else { display_algebraic_def(out, to_algebraic(r), compact, pp); } } } void display(std::ostream & out, value * v, bool compact, bool pp=false) const { if (v == nullptr) out << "0"; else if (is_nz_rational(v)) qm().display(out, to_mpq(v)); else { rational_function_value * rf = to_rational_function(v); if (is_denominator_one(rf)) { display_polynomial_expr(out, rf->num(), rf->ext(), compact, pp); } else if (is_rational_one(rf->num())) { out << "1/("; display_polynomial_expr(out, rf->den(), rf->ext(), compact, pp); out << ")"; } else { out << "("; display_polynomial_expr(out, rf->num(), rf->ext(), compact, pp); out << ")/("; display_polynomial_expr(out, rf->den(), rf->ext(), compact, pp); out << ")"; } } } void display_compact(std::ostream & out, value * a, bool pp=false) const { collect_algebraic_refs c; c.mark(a); if (c.m_found.empty()) { display(out, a, true, pp); } else { std::sort(c.m_found.begin(), c.m_found.end(), rank_lt_proc()); out << "["; display(out, a, true, pp); for (unsigned i = 0; i < c.m_found.size(); i++) { algebraic * ext = c.m_found[i]; if (pp) out << "; α" << ext->idx() << " := "; else out << "; r!" << ext->idx() << " := "; display_algebraic_def(out, ext, true, pp); } out << "]"; } } void display(std::ostream & out, numeral const & a, bool compact=false, bool pp=false) const { if (compact) display_compact(out, a.m_value, pp); else display(out, a.m_value, false, pp); } void display_non_rational_in_decimal(std::ostream & out, numeral const & a, unsigned precision) { SASSERT(!is_zero(a)); SASSERT(!is_nz_rational(a)); mpbqi const & i = interval(a.m_value); if (refine_interval(a.m_value, precision*4)) { // hack if (bqm().is_int(i.lower())) bqm().display_decimal(out, i.upper(), precision); else bqm().display_decimal(out, i.lower(), precision); } else { if (sign(a.m_value) > 0) out << "?"; else out << "-?"; } } void display_decimal(std::ostream & out, numeral const & a, unsigned precision) const { if (is_zero(a)) { out << "0"; } else if (is_nz_rational(a)) { qm().display_decimal(out, to_mpq(a), precision); } else { const_cast(this)->display_non_rational_in_decimal(out, a, precision); } } void display_interval(std::ostream & out, numeral const & a) const { if (is_zero(a)) out << "[0, 0]"; else display_interval(out, interval(a.m_value), false); } }; // Helper object for restoring the value intervals. class save_interval_ctx { manager::imp * m; public: save_interval_ctx(manager const * _this):m(_this->m_imp) { SASSERT (m); } ~save_interval_ctx() { m->restore_saved_intervals(); } }; manager::manager(reslimit& lim, unsynch_mpq_manager & m, params_ref const & p, small_object_allocator * a) { m_imp = alloc(imp, lim, m, p, a); } manager::~manager() { dealloc(m_imp); } void manager::get_param_descrs(param_descrs & r) { rcf_params::collect_param_descrs(r); } void manager::updt_params(params_ref const & p) { m_imp->updt_params(p); } unsynch_mpq_manager & manager::qm() const { return m_imp->m_qm; } void manager::del(numeral & a) { m_imp->del(a); } void manager::mk_infinitesimal(char const * n, char const * pp_n, numeral & r) { m_imp->mk_infinitesimal(n, pp_n, r); } void manager::mk_infinitesimal(numeral & r) { m_imp->mk_infinitesimal(r); } void manager::mk_transcendental(char const * n, char const * pp_n, mk_interval & proc, numeral & r) { m_imp->mk_transcendental(n, pp_n, proc, r); } void manager::mk_transcendental(mk_interval & proc, numeral & r) { m_imp->mk_transcendental(proc, r); } void manager::mk_pi(numeral & r) { m_imp->mk_pi(r); } void manager::mk_e(numeral & r) { m_imp->mk_e(r); } void manager::isolate_roots(unsigned n, numeral const * as, numeral_vector & roots) { save_interval_ctx ctx(this); m_imp->isolate_roots(n, as, roots); } void manager::reset(numeral & a) { m_imp->reset(a); } int manager::sign(numeral const & a) { save_interval_ctx ctx(this); return m_imp->sign(a); } bool manager::is_zero(numeral const & a) { return sign(a) == 0; } bool manager::is_pos(numeral const & a) { return sign(a) > 0; } bool manager::is_neg(numeral const & a) { return sign(a) < 0; } bool manager::is_int(numeral const & a) { return m_imp->is_int(a); } bool manager::depends_on_infinitesimals(numeral const & a) { return m_imp->depends_on_infinitesimals(a); } void manager::set(numeral & a, int n) { m_imp->set(a, n); } void manager::set(numeral & a, mpz const & n) { m_imp->set(a, n); } void manager::set(numeral & a, mpq const & n) { m_imp->set(a, n); } void manager::set(numeral & a, numeral const & n) { m_imp->set(a, n); } void manager::swap(numeral & a, numeral & b) { std::swap(a.m_value, b.m_value); } void manager::root(numeral const & a, unsigned k, numeral & b) { save_interval_ctx ctx(this); m_imp->root(a, k, b); } void manager::power(numeral const & a, unsigned k, numeral & b) { save_interval_ctx ctx(this); m_imp->power(a, k, b); } void manager::add(numeral const & a, numeral const & b, numeral & c) { save_interval_ctx ctx(this); m_imp->add(a, b, c); } void manager::add(numeral const & a, mpz const & b, numeral & c) { scoped_numeral _b(*this); set(_b, b); add(a, _b, c); } void manager::sub(numeral const & a, numeral const & b, numeral & c) { save_interval_ctx ctx(this); m_imp->sub(a, b, c); } void manager::mul(numeral const & a, numeral const & b, numeral & c) { save_interval_ctx ctx(this); m_imp->mul(a, b, c); } void manager::neg(numeral & a) { save_interval_ctx ctx(this); m_imp->neg(a); } void manager::neg(numeral const & a, numeral & b) { save_interval_ctx ctx(this); m_imp->neg(a, b); } void manager::inv(numeral & a) { save_interval_ctx ctx(this); m_imp->inv(a); } void manager::inv(numeral const & a, numeral & b) { save_interval_ctx ctx(this); m_imp->inv(a, b); } void manager::div(numeral const & a, numeral const & b, numeral & c) { save_interval_ctx ctx(this); m_imp->div(a, b, c); } int manager::compare(numeral const & a, numeral const & b) { save_interval_ctx ctx(this); return m_imp->compare(a, b); } bool manager::eq(numeral const & a, numeral const & b) { return compare(a, b) == 0; } bool manager::eq(numeral const & a, mpq const & b) { scoped_numeral _b(*this); set(_b, b); return eq(a, _b); } bool manager::eq(numeral const & a, mpz const & b) { scoped_numeral _b(*this); set(_b, b); return eq(a, _b); } bool manager::lt(numeral const & a, numeral const & b) { return compare(a, b) < 0; } bool manager::lt(numeral const & a, mpq const & b) { scoped_numeral _b(*this); set(_b, b); return lt(a, _b); } bool manager::lt(numeral const & a, mpz const & b) { scoped_numeral _b(*this); set(_b, b); return lt(a, _b); } bool manager::gt(numeral const & a, mpq const & b) { scoped_numeral _b(*this); set(_b, b); return gt(a, _b); } bool manager::gt(numeral const & a, mpz const & b) { scoped_numeral _b(*this); set(_b, b); return gt(a, _b); } void manager::display(std::ostream & out, numeral const & a, bool compact, bool pp) const { save_interval_ctx ctx(this); m_imp->display(out, a, compact, pp); } void manager::display_decimal(std::ostream & out, numeral const & a, unsigned precision) const { save_interval_ctx ctx(this); m_imp->display_decimal(out, a, precision); } void manager::display_interval(std::ostream & out, numeral const & a) const { save_interval_ctx ctx(this); m_imp->display_interval(out, a); } void manager::clean_denominators(numeral const & a, numeral & p, numeral & q) { save_interval_ctx ctx(this); m_imp->clean_denominators(a, p, q); } }; void pp(realclosure::manager::imp * imp, realclosure::polynomial const & p, realclosure::extension * ext) { imp->display_polynomial_expr(std::cout, p, ext, false, false); std::cout << std::endl; } void pp(realclosure::manager::imp * imp, realclosure::value * v) { imp->display(std::cout, v, false); std::cout << std::endl; } void pp(realclosure::manager::imp * imp, unsigned sz, realclosure::value * const * p) { for (unsigned i = 0; i < sz; i++) pp(imp, p[i]); } void pp(realclosure::manager::imp * imp, realclosure::manager::imp::value_ref_buffer const & p) { for (unsigned i = 0; i < p.size(); i++) pp(imp, p[i]); } void pp(realclosure::manager::imp * imp, realclosure::manager::imp::value_ref const & v) { pp(imp, v.get()); } void pp(realclosure::manager::imp * imp, realclosure::mpbqi const & i) { imp->bqim().display(std::cout, i); std::cout << std::endl; } void pp(realclosure::manager::imp * imp, realclosure::manager::imp::scoped_mpqi const & i) { imp->qim().display(std::cout, i); std::cout << std::endl; } void pp(realclosure::manager::imp * imp, mpbq const & n) { imp->bqm().display(std::cout, n); std::cout << std::endl; } void pp(realclosure::manager::imp * imp, mpq const & n) { imp->qm().display(std::cout, n); std::cout << std::endl; } void pp(realclosure::manager::imp * imp, realclosure::extension * x) { imp->display_ext(std::cout, x, false, false); std::cout << std::endl; } z3-z3-4.8.7/src/math/realclosure/realclosure.h000066400000000000000000000304771356505360400211730ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: realclosure.h Abstract: Package for computing with elements of the realclosure of a field containing - all rationals - extended with computable transcendental real numbers (e.g., pi and e) - infinitesimals Author: Leonardo (leonardo) 2013-01-02 Notes: --*/ #ifndef REALCLOSURE_H_ #define REALCLOSURE_H_ #include "util/mpq.h" #include "util/params.h" #include "util/scoped_numeral.h" #include "util/scoped_numeral_vector.h" #include "math/interval/interval.h" #include "util/z3_exception.h" #include "util/rlimit.h" namespace realclosure { class num; typedef interval_manager mpqi_manager; typedef default_exception exception; class mk_interval { public: virtual void operator()(unsigned k, mpqi_manager & im, mpqi_manager::interval & r) = 0; }; class manager { public: struct imp; private: friend class save_interval_ctx; imp * m_imp; public: manager(reslimit& lim, unsynch_mpq_manager & m, params_ref const & p = params_ref(), small_object_allocator * a = nullptr); ~manager(); typedef num numeral; typedef svector numeral_vector; typedef _scoped_numeral scoped_numeral; typedef _scoped_numeral_vector scoped_numeral_vector; static void get_param_descrs(param_descrs & r); static void collect_param_descrs(param_descrs & r) { get_param_descrs(r); } void updt_params(params_ref const & p); unsynch_mpq_manager & qm() const; void del(numeral & a); /** \brief Add a new infinitesimal to the current field. The new infinitesimal is smaller than any positive element in the field. */ void mk_infinitesimal(char const * name, char const * pp_name, numeral & r); void mk_infinitesimal(numeral & r); /** \brief Add a new transcendental real value to the field. The functor \c mk_interval is used to compute approximations of the transcendental value. This procedure should be used with care, if the value is not really transcendental with respect to the current field, computations with the new numeral may not terminate. Example: we extended the field with Pi. Pi is transcendental with respect to a field that contains only algebraic real numbers. So, this step is fine. Let us call the resultant field F. Then, we extend the field F with 1 - Pi. 1 - Pi is transcendental with respect to algebraic real numbers, but it is NOT transcendental with respect to F, since F contains Pi. */ void mk_transcendental(char const * name, char const * pp_name, mk_interval & proc, numeral & r); void mk_transcendental(mk_interval & proc, numeral & r); /** \brief r <- pi */ void mk_pi(numeral & r); /** \brief r <- e (Euler's constant) */ void mk_e(numeral & r); /** \brief Isolate the roots of the univariate polynomial as[0] + as[1]*x + ... + as[n-1]*x^{n-1} The roots are stored in \c roots. */ void isolate_roots(unsigned n, numeral const * as, numeral_vector & roots); /** \brief a <- 0 */ void reset(numeral & a); /** \brief Return the sign of a. */ int sign(numeral const & a); /** \brief Return true if a is zero. */ bool is_zero(numeral const & a); /** \brief Return true if a is positive. */ bool is_pos(numeral const & a); /** \brief Return true if a is negative. */ bool is_neg(numeral const & a); /** \brief Return true if a is an integer. */ bool is_int(numeral const & a); /** \brief Return true if the representation of \c a depends on infinitesimal extensions. */ bool depends_on_infinitesimals(numeral const & a); /** \brief a <- n */ void set(numeral & a, int n); void set(numeral & a, mpz const & n); void set(numeral & a, mpq const & n); void set(numeral & a, numeral const & n); void swap(numeral & a, numeral & b); /** \brief Return a^{1/k} Throws an exception if (a is negative and k is even) or (k is zero). */ void root(numeral const & a, unsigned k, numeral & b); /** \brief Return a^k Throws an exception if 0^0. */ void power(numeral const & a, unsigned k, numeral & b); /** \brief c <- a + b */ void add(numeral const & a, numeral const & b, numeral & c); void add(numeral const & a, mpz const & b, numeral & c); /** \brief c <- a - b */ void sub(numeral const & a, numeral const & b, numeral & c); /** \brief c <- a * b */ void mul(numeral const & a, numeral const & b, numeral & c); /** \brief a <- -a */ void neg(numeral & a); /** \brief b <- -a */ void neg(numeral const & a, numeral & b); /** \brief a <- 1/a if a != 0 */ void inv(numeral & a); /** \brief b <- 1/a if a != 0 */ void inv(numeral const & a, numeral & b); /** \brief c <- a/b if b != 0 */ void div(numeral const & a, numeral const & b, numeral & c); /** Return -1 if a < b Return 0 if a == b Return 1 if a > b */ int compare(numeral const & a, numeral const & b); /** \brief a == b */ bool eq(numeral const & a, numeral const & b); bool eq(numeral const & a, mpq const & b); bool eq(numeral const & a, mpz const & b); /** \brief a != b */ bool neq(numeral const & a, numeral const & b) { return !eq(a, b); } bool neq(numeral const & a, mpq const & b) { return !eq(a, b); } bool neq(numeral const & a, mpz const & b) { return !eq(a, b); } /** \brief a < b */ bool lt(numeral const & a, numeral const & b); bool lt(numeral const & a, mpq const & b); bool lt(numeral const & a, mpz const & b); /** \brief a > b */ bool gt(numeral const & a, numeral const & b) { return lt(b, a); } bool gt(numeral const & a, mpq const & b); bool gt(numeral const & a, mpz const & b); /** \brief a <= b */ bool le(numeral const & a, numeral const & b) { return !gt(a, b); } bool le(numeral const & a, mpq const & b) { return !gt(a, b); } bool le(numeral const & a, mpz const & b) { return !gt(a, b); } /** \brief a >= b */ bool ge(numeral const & a, numeral const & b) { return !lt(a, b); } bool ge(numeral const & a, mpq const & b) { return !lt(a, b); } bool ge(numeral const & a, mpz const & b) { return !lt(a, b); } void display(std::ostream & out, numeral const & a, bool compact=false, bool pp=false) const; /** \brief Display a real number in decimal notation. A question mark is added based on the precision requested. This procedure throws an exception if the \c a is not a real. */ void display_decimal(std::ostream & out, numeral const & a, unsigned precision = 10) const; void display_interval(std::ostream & out, numeral const & a) const; void clean_denominators(numeral const & a, numeral & p, numeral & q); }; struct value; class num { friend class manager; friend struct manager::imp; value * m_value; public: num():m_value(nullptr) {} // Low level functions for implementing the C API void * c_ptr() { return m_value; } static num mk(void * ptr) { num r; r.m_value = reinterpret_cast(ptr); return r; } }; }; typedef realclosure::manager rcmanager; typedef rcmanager::numeral rcnumeral; typedef rcmanager::numeral_vector rcnumeral_vector; typedef rcmanager::scoped_numeral scoped_rcnumeral; typedef rcmanager::scoped_numeral_vector scoped_rcnumeral_vector; #define RCF_MK_COMPARISON_CORE(EXTERNAL, INTERNAL, TYPE) \ inline bool EXTERNAL(scoped_rcnumeral const & a, TYPE const & b) { \ rcmanager & m = a.m(); \ scoped_rcnumeral _b(m); \ m.set(_b, b); \ return m.INTERNAL(a, _b); \ } #define RCF_MK_COMPARISON(EXTERNAL, INTERNAL) \ RCF_MK_COMPARISON_CORE(EXTERNAL, INTERNAL, int) \ RCF_MK_COMPARISON_CORE(EXTERNAL, INTERNAL, mpz) \ RCF_MK_COMPARISON_CORE(EXTERNAL, INTERNAL, mpq) RCF_MK_COMPARISON(operator==, eq); RCF_MK_COMPARISON(operator!=, neq); RCF_MK_COMPARISON(operator<, lt); RCF_MK_COMPARISON(operator<=, le); RCF_MK_COMPARISON(operator>, gt); RCF_MK_COMPARISON(operator>=, ge); #undef RCF_MK_COMPARISON #undef RCF_MK_COMPARISON_CORE #define RCF_MK_BINARY_CORE(EXTERNAL, INTERNAL, TYPE) \ inline scoped_rcnumeral EXTERNAL(scoped_rcnumeral const & a, TYPE const & b) { \ rcmanager & m = a.m(); \ scoped_rcnumeral _b(m); \ m.set(_b, b); \ scoped_rcnumeral r(m); \ m.INTERNAL(a, _b, r); \ return r; \ } #define RCF_MK_BINARY(EXTERNAL, INTERNAL) \ RCF_MK_BINARY_CORE(EXTERNAL, INTERNAL, int) \ RCF_MK_BINARY_CORE(EXTERNAL, INTERNAL, mpz) \ RCF_MK_BINARY_CORE(EXTERNAL, INTERNAL, mpq) RCF_MK_BINARY(operator+, add) RCF_MK_BINARY(operator-, sub) RCF_MK_BINARY(operator*, mul) RCF_MK_BINARY(operator/, div) #undef RCF_MK_BINARY #undef RCF_MK_BINARY_CORE inline scoped_rcnumeral root(scoped_rcnumeral const & a, unsigned k) { scoped_rcnumeral r(a.m()); a.m().root(a, k, r); return r; } inline scoped_rcnumeral power(scoped_rcnumeral const & a, unsigned k) { scoped_rcnumeral r(a.m()); a.m().power(a, k, r); return r; } inline scoped_rcnumeral operator^(scoped_rcnumeral const & a, unsigned k) { return power(a, k); } inline bool is_int(scoped_rcnumeral const & a) { return a.m().is_int(a); } struct rc_sym_pp { rcmanager & m; rcnumeral const & n; rc_sym_pp(rcmanager & _m, rcnumeral const & _n):m(_m), n(_n) {} rc_sym_pp(scoped_rcnumeral const & _n):m(_n.m()), n(_n.get()) {} }; inline rc_sym_pp sym_pp(scoped_rcnumeral const & _n) { return rc_sym_pp(_n); } inline std::ostream & operator<<(std::ostream & out, rc_sym_pp const & n) { n.m.display(out, n.n); return out; } struct rc_decimal_pp { rcmanager & m; rcnumeral const & n; unsigned prec; rc_decimal_pp(rcmanager & _m, rcnumeral const & _n, unsigned p):m(_m), n(_n), prec(p) {} rc_decimal_pp(scoped_rcnumeral const & _n, unsigned p):m(_n.m()), n(_n.get()), prec(p) {} }; inline std::ostream & operator<<(std::ostream & out, rc_decimal_pp const & n) { n.m.display_decimal(out, n.n, n.prec); return out; } inline rc_decimal_pp decimal_pp(scoped_rcnumeral const & n, unsigned prec = 10) { return rc_decimal_pp(n, prec); } struct rc_interval_pp { rcmanager & m; rcnumeral const & n; rc_interval_pp(rcmanager & _m, rcnumeral const & _n):m(_m), n(_n) {} rc_interval_pp(scoped_rcnumeral const & _n):m(_n.m()), n(_n.get()) {} }; inline std::ostream & operator<<(std::ostream & out, rc_interval_pp const & n) { n.m.display_interval(out, n.n); return out; } inline rc_interval_pp interval_pp(scoped_rcnumeral const & n) { return rc_interval_pp(n); } #endif z3-z3-4.8.7/src/math/simplex/000077500000000000000000000000001356505360400156305ustar00rootroot00000000000000z3-z3-4.8.7/src/math/simplex/CMakeLists.txt000066400000000000000000000001571356505360400203730ustar00rootroot00000000000000z3_add_component(simplex SOURCES simplex.cpp model_based_opt.cpp COMPONENT_DEPENDENCIES util ) z3-z3-4.8.7/src/math/simplex/model_based_opt.cpp000066400000000000000000001225501356505360400214610ustar00rootroot00000000000000/*++ Copyright (c) 2016 Microsoft Corporation Module Name: model_based_opt.cpp Abstract: Model-based optimization and projection for linear real, integer arithmetic. Author: Nikolaj Bjorner (nbjorner) 2016-27-4 Revision History: --*/ #include "math/simplex/model_based_opt.h" #include "util/uint_set.h" #include "util/z3_exception.h" std::ostream& operator<<(std::ostream& out, opt::ineq_type ie) { switch (ie) { case opt::t_eq: return out << " = "; case opt::t_lt: return out << " < "; case opt::t_le: return out << " <= "; case opt::t_mod: return out << " mod "; } return out; } namespace opt { /** * Convert a row ax + coeffs + coeff = value into a definition for x * x = (value - coeffs - coeff)/a * as backdrop we have existing assignments to x and other variables that * satisfy the equality with value, and such that value satisfies * the row constraint ( = , <= , < , mod) */ model_based_opt::def::def(row const& r, unsigned x) { for (var const & v : r.m_vars) { if (v.m_id != x) { m_vars.push_back(v); } else { m_div = -v.m_coeff; } } m_coeff = r.m_coeff; switch (r.m_type) { case opt::t_lt: m_coeff += m_div; break; case opt::t_le: // for: ax >= t, then x := (t + a - 1) div a if (m_div.is_pos()) { m_coeff += m_div; m_coeff -= rational::one(); } break; default: break; } normalize(); SASSERT(m_div.is_pos()); } model_based_opt::def model_based_opt::def::operator+(def const& other) const { def result; vector const& vs1 = m_vars; vector const& vs2 = other.m_vars; vector & vs = result.m_vars; rational c1(1), c2(1); if (m_div != other.m_div) { c1 = other.m_div; c2 = m_div; } unsigned i = 0, j = 0; while (i < vs1.size() || j < vs2.size()) { unsigned v1 = UINT_MAX, v2 = UINT_MAX; if (i < vs1.size()) v1 = vs1[i].m_id; if (j < vs2.size()) v2 = vs2[j].m_id; if (v1 == v2) { vs.push_back(vs1[i]); vs.back().m_coeff *= c1; vs.back().m_coeff += c2 * vs2[j].m_coeff; ++i; ++j; if (vs.back().m_coeff.is_zero()) { vs.pop_back(); } } else if (v1 < v2) { vs.push_back(vs1[i]); vs.back().m_coeff *= c1; } else { vs.push_back(vs2[j]); vs.back().m_coeff *= c2; } } result.m_div = c1*m_div; result.m_coeff = (m_coeff*c1) + (other.m_coeff*c2); result.normalize(); return result; } model_based_opt::def model_based_opt::def::operator/(rational const& r) const { def result(*this); result.m_div *= r; result.normalize(); return result; } model_based_opt::def model_based_opt::def::operator*(rational const& n) const { def result(*this); for (var& v : result.m_vars) { v.m_coeff *= n; } result.m_coeff *= n; result.normalize(); return result; } model_based_opt::def model_based_opt::def::operator+(rational const& n) const { def result(*this); result.m_coeff += n * result.m_div; result.normalize(); return result; } void model_based_opt::def::normalize() { if (m_div.is_one()) return; rational g(m_div); g = gcd(g, m_coeff); for (var const& v : m_vars) { g = gcd(g, abs(v.m_coeff)); if (g.is_one()) break; } if (m_div.is_neg()) { g.neg(); } if (!g.is_one()) { for (var& v : m_vars) { v.m_coeff /= g; } m_coeff /= g; m_div /= g; } } model_based_opt::model_based_opt() { m_rows.push_back(row()); } bool model_based_opt::invariant() { for (unsigned i = 0; i < m_rows.size(); ++i) { if (!invariant(i, m_rows[i])) { return false; } } return true; } #define PASSERT(_e_) if (!(_e_)) { TRACE("opt1", display(tout, r); display(tout);); SASSERT(_e_); } bool model_based_opt::invariant(unsigned index, row const& r) { vector const& vars = r.m_vars; for (unsigned i = 0; i < vars.size(); ++i) { // variables in each row are sorted and have non-zero coefficients PASSERT(i + 1 == vars.size() || vars[i].m_id < vars[i+1].m_id); PASSERT(!vars[i].m_coeff.is_zero()); PASSERT(index == 0 || m_var2row_ids[vars[i].m_id].contains(index)); } PASSERT(r.m_value == eval(r)); PASSERT(r.m_type != t_eq || r.m_value.is_zero()); // values satisfy constraints PASSERT(index == 0 || r.m_type != t_lt || r.m_value.is_neg()); PASSERT(index == 0 || r.m_type != t_le || !r.m_value.is_pos()); PASSERT(index == 0 || r.m_type != t_mod || (mod(r.m_value, r.m_mod).is_zero())); return true; } // a1*x + obj // a2*x + t2 <= 0 // a3*x + t3 <= 0 // a4*x + t4 <= 0 // a1 > 0, a2 > 0, a3 > 0, a4 < 0 // x <= -t2/a2 // x <= -t2/a3 // determine lub among these. // then resolve lub with others // e.g., -t2/a2 <= -t3/a3, then // replace inequality a3*x + t3 <= 0 by -t2/a2 + t3/a3 <= 0 // mark a4 as invalid. // // a1 < 0, a2 < 0, a3 < 0, a4 > 0 // x >= t2/a2 // x >= t3/a3 // determine glb among these // the resolve glb with others. // e.g. t2/a2 >= t3/a3 // then replace a3*x + t3 by t3/a3 - t2/a2 <= 0 // inf_eps model_based_opt::maximize() { SASSERT(invariant()); unsigned_vector bound_trail, bound_vars; TRACE("opt", display(tout << "tableau\n");); while (!objective().m_vars.empty()) { var v = objective().m_vars.back(); unsigned x = v.m_id; rational const& coeff = v.m_coeff; unsigned bound_row_index; rational bound_coeff; if (find_bound(x, bound_row_index, bound_coeff, coeff.is_pos())) { SASSERT(!bound_coeff.is_zero()); TRACE("opt", display(tout << "update: " << v << " ", objective()); for (unsigned above : m_above) { display(tout << "resolve: ", m_rows[above]); }); for (unsigned above : m_above) { resolve(bound_row_index, bound_coeff, above, x); } for (unsigned below : m_below) { resolve(bound_row_index, bound_coeff, below, x); } // coeff*x + objective <= ub // a2*x + t2 <= 0 // => coeff*x <= -t2*coeff/a2 // objective + t2*coeff/a2 <= ub mul_add(false, m_objective_id, - coeff/bound_coeff, bound_row_index); retire_row(bound_row_index); bound_trail.push_back(bound_row_index); bound_vars.push_back(x); } else { TRACE("opt", display(tout << "unbound: " << v << " ", objective());); update_values(bound_vars, bound_trail); return inf_eps::infinity(); } } // // update the evaluation of variables to satisfy the bound. // update_values(bound_vars, bound_trail); rational value = objective().m_value; if (objective().m_type == t_lt) { return inf_eps(inf_rational(value, rational(-1))); } else { return inf_eps(inf_rational(value)); } } void model_based_opt::update_value(unsigned x, rational const& val) { rational old_val = m_var2value[x]; m_var2value[x] = val; SASSERT(val.is_int() || !is_int(x)); unsigned_vector const& row_ids = m_var2row_ids[x]; for (unsigned row_id : row_ids) { rational coeff = get_coefficient(row_id, x); if (coeff.is_zero()) { continue; } row & r = m_rows[row_id]; rational delta = coeff * (val - old_val); r.m_value += delta; SASSERT(invariant(row_id, r)); } } void model_based_opt::update_values(unsigned_vector const& bound_vars, unsigned_vector const& bound_trail) { for (unsigned i = bound_trail.size(); i-- > 0; ) { unsigned x = bound_vars[i]; row& r = m_rows[bound_trail[i]]; rational val = r.m_coeff; rational old_x_val = m_var2value[x]; rational new_x_val; rational x_coeff, eps(0); vector const& vars = r.m_vars; for (var const& v : vars) { if (x == v.m_id) { x_coeff = v.m_coeff; } else { val += m_var2value[v.m_id]*v.m_coeff; } } SASSERT(!x_coeff.is_zero()); new_x_val = -val/x_coeff; if (r.m_type == t_lt) { eps = abs(old_x_val - new_x_val)/rational(2); eps = std::min(rational::one(), eps); SASSERT(!eps.is_zero()); // // ax + t < 0 // <=> x < -t/a // <=> x := -t/a - epsilon // if (x_coeff.is_pos()) { new_x_val -= eps; } // // -ax + t < 0 // <=> -ax < -t // <=> -x < -t/a // <=> x > t/a // <=> x := t/a + epsilon // else { new_x_val += eps; } } TRACE("opt", display(tout << "v" << x << " coeff_x: " << x_coeff << " old_x_val: " << old_x_val << " new_x_val: " << new_x_val << " eps: " << eps << " ", r); ); m_var2value[x] = new_x_val; r.m_value = eval(r); SASSERT(invariant(bound_trail[i], r)); } // update and check bounds for all other affected rows. for (unsigned i = bound_trail.size(); i-- > 0; ) { unsigned x = bound_vars[i]; unsigned_vector const& row_ids = m_var2row_ids[x]; for (unsigned row_id : row_ids) { row & r = m_rows[row_id]; r.m_value = eval(r); SASSERT(invariant(row_id, r)); } } SASSERT(invariant()); } bool model_based_opt::find_bound(unsigned x, unsigned& bound_row_index, rational& bound_coeff, bool is_pos) { bound_row_index = UINT_MAX; rational lub_val; rational const& x_val = m_var2value[x]; unsigned_vector const& row_ids = m_var2row_ids[x]; uint_set visited; m_above.reset(); m_below.reset(); for (unsigned row_id : row_ids) { SASSERT(row_id != m_objective_id); if (visited.contains(row_id)) { continue; } visited.insert(row_id); row& r = m_rows[row_id]; if (r.m_alive) { rational a = get_coefficient(row_id, x); if (a.is_zero()) { // skip } else if (a.is_pos() == is_pos || r.m_type == t_eq) { rational value = x_val - (r.m_value/a); if (bound_row_index == UINT_MAX) { lub_val = value; bound_row_index = row_id; bound_coeff = a; } else if ((value == lub_val && r.m_type == opt::t_lt) || (is_pos && value < lub_val) || (!is_pos && value > lub_val)) { m_above.push_back(bound_row_index); lub_val = value; bound_row_index = row_id; bound_coeff = a; } else { m_above.push_back(row_id); } } else { m_below.push_back(row_id); } } } return bound_row_index != UINT_MAX; } void model_based_opt::retire_row(unsigned row_id) { m_rows[row_id].m_alive = false; m_retired_rows.push_back(row_id); } rational model_based_opt::eval(unsigned x) const { return m_var2value[x]; } rational model_based_opt::eval(def const& d) const { vector const& vars = d.m_vars; rational val = d.m_coeff; for (var const& v : vars) { val += v.m_coeff * eval(v.m_id); } val /= d.m_div; return val; } rational model_based_opt::eval(row const& r) const { vector const& vars = r.m_vars; rational val = r.m_coeff; for (var const& v : vars) { val += v.m_coeff * eval(v.m_id); } return val; } rational model_based_opt::get_coefficient(unsigned row_id, unsigned var_id) const { return m_rows[row_id].get_coefficient(var_id); } rational model_based_opt::row::get_coefficient(unsigned var_id) const { if (m_vars.empty()) { return rational::zero(); } unsigned lo = 0, hi = m_vars.size(); while (lo < hi) { unsigned mid = lo + (hi - lo)/2; SASSERT(mid < hi); unsigned id = m_vars[mid].m_id; if (id == var_id) { lo = mid; break; } if (id < var_id) { lo = mid + 1; } else { hi = mid; } } if (lo == m_vars.size()) { return rational::zero(); } unsigned id = m_vars[lo].m_id; if (id == var_id) { return m_vars[lo].m_coeff; } else { return rational::zero(); } } // // Let // row1: t1 + a1*x <= 0 // row2: t2 + a2*x <= 0 // // assume a1, a2 have the same signs: // (t2 + a2*x) <= (t1 + a1*x)*a2/a1 // <=> t2*a1/a2 - t1 <= 0 // <=> t2 - t1*a2/a1 <= 0 // // assume a1 > 0, -a2 < 0: // t1 + a1*x <= 0, t2 - a2*x <= 0 // t2/a2 <= -t1/a1 // t2 + t1*a2/a1 <= 0 // assume -a1 < 0, a2 > 0: // t1 - a1*x <= 0, t2 + a2*x <= 0 // t1/a1 <= -t2/a2 // t2 + t1*a2/a1 <= 0 // // the resolvent is the same in all cases (simpler proof should exist) // void model_based_opt::resolve(unsigned row_src, rational const& a1, unsigned row_dst, unsigned x) { SASSERT(a1 == get_coefficient(row_src, x)); SASSERT(!a1.is_zero()); SASSERT(row_src != row_dst); if (m_rows[row_dst].m_alive) { rational a2 = get_coefficient(row_dst, x); if (is_int(x)) { TRACE("opt", tout << a1 << " " << a2 << ": "; display(tout, m_rows[row_dst]); display(tout, m_rows[row_src]);); if (a1.is_pos() != a2.is_pos() || m_rows[row_src].m_type == opt::t_eq) { mul_add(x, a1, row_src, a2, row_dst); } else { mul(row_dst, abs(a1)); mul_add(false, row_dst, -abs(a2), row_src); } TRACE("opt", display(tout, m_rows[row_dst]);); normalize(row_dst); } else { mul_add(row_dst != m_objective_id && a1.is_pos() == a2.is_pos(), row_dst, -a2/a1, row_src); } } } void model_based_opt::solve(unsigned row_src, rational const& a1, unsigned row_dst, unsigned x) { SASSERT(a1 == get_coefficient(row_src, x)); SASSERT(a1.is_pos()); SASSERT(row_src != row_dst); if (!m_rows[row_dst].m_alive) return; rational a2 = get_coefficient(row_dst, x); mul(row_dst, a1); mul_add(false, row_dst, -a2, row_src); SASSERT(get_coefficient(row_dst, x).is_zero()); } // resolution for integer rows. void model_based_opt::mul_add( unsigned x, rational const& src_c, unsigned row_src, rational const& dst_c, unsigned row_dst) { row& dst = m_rows[row_dst]; row const& src = m_rows[row_src]; SASSERT(is_int(x)); SASSERT(t_le == dst.m_type && t_le == src.m_type); SASSERT(src_c.is_int()); SASSERT(dst_c.is_int()); SASSERT(m_var2value[x].is_int()); rational abs_src_c = abs(src_c); rational abs_dst_c = abs(dst_c); rational x_val = m_var2value[x]; rational slack = (abs_src_c - rational::one()) * (abs_dst_c - rational::one()); rational dst_val = dst.m_value - x_val*dst_c; rational src_val = src.m_value - x_val*src_c; rational distance = abs_src_c * dst_val + abs_dst_c * src_val + slack; bool use_case1 = distance.is_nonpos() || abs_src_c.is_one() || abs_dst_c.is_one(); #if 0 if (distance.is_nonpos() && !abs_src_c.is_one() && !abs_dst_c.is_one()) { unsigned r = copy_row(row_src); mul_add(false, r, rational::one(), row_dst); del_var(r, x); add(r, slack); TRACE("qe", tout << m_rows[r];); SASSERT(!m_rows[r].m_value.is_pos()); } #endif if (use_case1) { TRACE("opt", tout << "slack: " << slack << " " << src_c << " " << dst_val << " " << dst_c << " " << src_val << "\n";); // dst <- abs_src_c*dst + abs_dst_c*src + slack mul(row_dst, abs_src_c); add(row_dst, slack); mul_add(false, row_dst, abs_dst_c, row_src); return; } // // create finite disjunction for |b|. // exists x, z in [0 .. |b|-2] . b*x + s + z = 0 && ax + t <= 0 && bx + s <= 0 // <=> // exists x, z in [0 .. |b|-2] . b*x = -z - s && ax + t <= 0 && bx + s <= 0 // <=> // exists x, z in [0 .. |b|-2] . b*x = -z - s && a|b|x + |b|t <= 0 && bx + s <= 0 // <=> // exists x, z in [0 .. |b|-2] . b*x = -z - s && a|b|x + |b|t <= 0 && -z - s + s <= 0 // <=> // exists x, z in [0 .. |b|-2] . b*x = -z - s && a|b|x + |b|t <= 0 && -z <= 0 // <=> // exists x, z in [0 .. |b|-2] . b*x = -z - s && a|b|x + |b|t <= 0 // <=> // exists x, z in [0 .. |b|-2] . b*x = -z - s && a*n_sign(b)(s + z) + |b|t <= 0 // <=> // exists z in [0 .. |b|-2] . |b| | (z + s) && a*n_sign(b)(s + z) + |b|t <= 0 // TRACE("qe", tout << "finite disjunction " << distance << " " << src_c << " " << dst_c << "\n";); vector coeffs; if (abs_dst_c <= abs_src_c) { rational z = mod(dst_val, abs_dst_c); if (!z.is_zero()) z = abs_dst_c - z; mk_coeffs_without(coeffs, dst.m_vars, x); add_divides(coeffs, dst.m_coeff + z, abs_dst_c); add(row_dst, z); mul(row_dst, src_c * n_sign(dst_c)); mul_add(false, row_dst, abs_dst_c, row_src); } else { // z := b - (s + bx) mod b // := b - s mod b // b | s + z <=> b | s + b - s mod b <=> b | s - s mod b rational z = mod(src_val, abs_src_c); if (!z.is_zero()) z = abs_src_c - z; mk_coeffs_without(coeffs, src.m_vars, x); add_divides(coeffs, src.m_coeff + z, abs_src_c); mul(row_dst, abs_src_c); add(row_dst, z * dst_c * n_sign(src_c)); mul_add(false, row_dst, dst_c * n_sign(src_c), row_src); } } 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); } } rational model_based_opt::n_sign(rational const& b) const { return rational(b.is_pos()?-1:1); } void model_based_opt::mul(unsigned dst, rational const& c) { if (c.is_one()) return; row& r = m_rows[dst]; for (auto & v : r.m_vars) { v.m_coeff *= c; } r.m_coeff *= c; r.m_value *= c; } void model_based_opt::add(unsigned dst, rational const& c) { row& r = m_rows[dst]; r.m_coeff += c; r.m_value += c; } void model_based_opt::sub(unsigned dst, rational const& c) { row& r = m_rows[dst]; r.m_coeff -= c; r.m_value -= c; } void model_based_opt::del_var(unsigned dst, unsigned x) { row& r = m_rows[dst]; unsigned j = 0; for (var & v : r.m_vars) { if (v.m_id == x) { r.m_value -= eval(x)*r.m_coeff; } else { r.m_vars[j++] = v; } } r.m_vars.shrink(j); } void model_based_opt::normalize(unsigned row_id) { row& r = m_rows[row_id]; if (r.m_vars.empty()) { retire_row(row_id); return; } if (r.m_type == t_mod) return; rational g(abs(r.m_vars[0].m_coeff)); bool all_int = g.is_int(); for (unsigned i = 1; all_int && !g.is_one() && i < r.m_vars.size(); ++i) { rational const& coeff = r.m_vars[i].m_coeff; if (coeff.is_int()) { g = gcd(g, abs(coeff)); } else { all_int = false; } } if (all_int && !r.m_coeff.is_zero()) { if (r.m_coeff.is_int()) { g = gcd(g, abs(r.m_coeff)); } else { all_int = false; } } if (all_int && !g.is_one()) { SASSERT(!g.is_zero()); mul(row_id, rational::one()/g); } } // // set row1 <- row1 + c*row2 // void model_based_opt::mul_add(bool same_sign, unsigned row_id1, rational const& c, unsigned row_id2) { if (c.is_zero()) { return; } m_new_vars.reset(); row& r1 = m_rows[row_id1]; row const& r2 = m_rows[row_id2]; unsigned i = 0, j = 0; while (i < r1.m_vars.size() || j < r2.m_vars.size()) { if (j == r2.m_vars.size()) { m_new_vars.append(r1.m_vars.size() - i, r1.m_vars.c_ptr() + i); break; } if (i == r1.m_vars.size()) { for (; j < r2.m_vars.size(); ++j) { m_new_vars.push_back(r2.m_vars[j]); m_new_vars.back().m_coeff *= c; if (row_id1 != m_objective_id) { m_var2row_ids[r2.m_vars[j].m_id].push_back(row_id1); } } break; } unsigned v1 = r1.m_vars[i].m_id; unsigned v2 = r2.m_vars[j].m_id; if (v1 == v2) { m_new_vars.push_back(r1.m_vars[i]); m_new_vars.back().m_coeff += c*r2.m_vars[j].m_coeff; ++i; ++j; if (m_new_vars.back().m_coeff.is_zero()) { m_new_vars.pop_back(); } } else if (v1 < v2) { m_new_vars.push_back(r1.m_vars[i]); ++i; } else { m_new_vars.push_back(r2.m_vars[j]); m_new_vars.back().m_coeff *= c; if (row_id1 != m_objective_id) { m_var2row_ids[r2.m_vars[j].m_id].push_back(row_id1); } ++j; } } r1.m_coeff += c*r2.m_coeff; r1.m_vars.swap(m_new_vars); r1.m_value += c*r2.m_value; if (!same_sign && r2.m_type == t_lt) { r1.m_type = t_lt; } else if (same_sign && r1.m_type == t_lt && r2.m_type == t_lt) { r1.m_type = t_le; } SASSERT(invariant(row_id1, r1)); } void model_based_opt::display(std::ostream& out) const { for (auto const& r : m_rows) { display(out, r); } for (unsigned i = 0; i < m_var2row_ids.size(); ++i) { unsigned_vector const& rows = m_var2row_ids[i]; out << i << ": "; for (auto const& r : rows) { out << r << " "; } out << "\n"; } } void model_based_opt::display(std::ostream& out, vector const& vars, rational const& coeff) { unsigned i = 0; for (var const& v : vars) { if (i > 0 && v.m_coeff.is_pos()) { out << "+ "; } ++i; if (v.m_coeff.is_one()) { out << "v" << v.m_id << " "; } else { out << v.m_coeff << "*v" << v.m_id << " "; } } if (coeff.is_pos()) { out << " + " << coeff << " "; } else if (coeff.is_neg()) { out << coeff << " "; } } std::ostream& model_based_opt::display(std::ostream& out, row const& r) { out << (r.m_alive?"+":"-") << " "; display(out, r.m_vars, r.m_coeff); if (r.m_type == opt::t_mod) { out << r.m_type << " " << r.m_mod << " = 0; value: " << r.m_value << "\n"; } else { out << r.m_type << " 0; value: " << r.m_value << "\n"; } return out; } std::ostream& model_based_opt::display(std::ostream& out, def const& r) { display(out, r.m_vars, r.m_coeff); if (!r.m_div.is_one()) { out << " / " << r.m_div; } return out; } unsigned model_based_opt::add_var(rational const& value, bool is_int) { unsigned v = m_var2value.size(); m_var2value.push_back(value); m_var2is_int.push_back(is_int); SASSERT(value.is_int() || !is_int); m_var2row_ids.push_back(unsigned_vector()); return v; } rational model_based_opt::get_value(unsigned var) { return m_var2value[var]; } void model_based_opt::set_row(unsigned row_id, vector const& coeffs, rational const& c, rational const& m, ineq_type rel) { row& r = m_rows[row_id]; rational val(c); SASSERT(r.m_vars.empty()); r.m_vars.append(coeffs.size(), coeffs.c_ptr()); bool is_int_row = true; std::sort(r.m_vars.begin(), r.m_vars.end(), var::compare()); for (auto const& c : coeffs) { val += m_var2value[c.m_id] * c.m_coeff; SASSERT(!is_int(c.m_id) || c.m_coeff.is_int()); is_int_row &= is_int(c.m_id); } r.m_alive = true; r.m_coeff = c; r.m_value = val; r.m_type = rel; r.m_mod = m; if (is_int_row && rel == t_lt) { r.m_type = t_le; r.m_coeff += rational::one(); r.m_value += rational::one(); } } unsigned model_based_opt::new_row() { unsigned row_id = 0; if (m_retired_rows.empty()) { row_id = m_rows.size(); m_rows.push_back(row()); } else { row_id = m_retired_rows.back(); m_retired_rows.pop_back(); m_rows[row_id].reset(); m_rows[row_id].m_alive = true; } return row_id; } unsigned model_based_opt::copy_row(unsigned src) { unsigned dst = new_row(); row const& r = m_rows[src]; set_row(dst, r.m_vars, r.m_coeff, r.m_mod, r.m_type); for (auto const& v : r.m_vars) { m_var2row_ids[v.m_id].push_back(dst); } SASSERT(invariant(dst, m_rows[dst])); return dst; } void model_based_opt::add_constraint(vector const& coeffs, rational const& c, ineq_type rel) { add_constraint(coeffs, c, rational::zero(), rel); } void model_based_opt::add_divides(vector const& coeffs, rational const& c, rational const& m) { add_constraint(coeffs, c, m, t_mod); } void model_based_opt::add_constraint(vector const& coeffs, rational const& c, rational const& m, ineq_type rel) { unsigned row_id = new_row(); set_row(row_id, coeffs, c, m, rel); for (var const& coeff : coeffs) { m_var2row_ids[coeff.m_id].push_back(row_id); } SASSERT(invariant(row_id, m_rows[row_id])); } void model_based_opt::set_objective(vector const& coeffs, rational const& c) { set_row(m_objective_id, coeffs, c, rational::zero(), t_le); } void model_based_opt::get_live_rows(vector& rows) { for (row const& r : m_rows) { if (r.m_alive) { rows.push_back(r); } } } // // pick glb and lub representative. // The representative is picked such that it // represents the fewest inequalities. // The constraints that enforce a glb or lub are not forced. // The constraints that separate the glb from ub or the lub from lb // are not forced. // In other words, suppose there are // . N inequalities of the form t <= x // . M inequalities of the form s >= x // . t0 is glb among N under valuation. // . s0 is lub among M under valuation. // If N < M // create the inequalities: // t <= t0 for each t other than t0 (N-1 inequalities). // t0 <= s for each s (M inequalities). // If N >= M the construction is symmetric. // model_based_opt::def model_based_opt::project(unsigned x, bool compute_def) { unsigned_vector& lub_rows = m_lub; unsigned_vector& glb_rows = m_glb; unsigned_vector& mod_rows = m_mod; unsigned lub_index = UINT_MAX, glb_index = UINT_MAX; bool lub_strict = false, glb_strict = false; rational lub_val, glb_val; rational const& x_val = m_var2value[x]; unsigned_vector const& row_ids = m_var2row_ids[x]; uint_set visited; lub_rows.reset(); glb_rows.reset(); mod_rows.reset(); bool lub_is_unit = false, glb_is_unit = false; unsigned eq_row = UINT_MAX; // select the lub and glb. for (unsigned row_id : row_ids) { if (visited.contains(row_id)) { continue; } visited.insert(row_id); row& r = m_rows[row_id]; if (!r.m_alive) { continue; } rational a = get_coefficient(row_id, x); if (a.is_zero()) { continue; } if (r.m_type == t_eq) { eq_row = row_id; continue; } if (r.m_type == t_mod) { mod_rows.push_back(row_id); } else if (a.is_pos()) { rational lub_value = x_val - (r.m_value/a); if (lub_rows.empty() || lub_value < lub_val || (lub_value == lub_val && r.m_type == t_lt && !lub_strict)) { lub_val = lub_value; lub_index = row_id; lub_strict = r.m_type == t_lt; } lub_rows.push_back(row_id); lub_is_unit &= a.is_one(); } else { SASSERT(a.is_neg()); rational glb_value = x_val - (r.m_value/a); if (glb_rows.empty() || glb_value > glb_val || (glb_value == glb_val && r.m_type == t_lt && !glb_strict)) { glb_val = glb_value; glb_index = row_id; glb_strict = r.m_type == t_lt; } glb_rows.push_back(row_id); glb_is_unit &= a.is_minus_one(); } } if (!mod_rows.empty()) { return solve_mod(x, mod_rows, compute_def); } if (eq_row != UINT_MAX) { return solve_for(eq_row, x, compute_def); } def result; unsigned lub_size = lub_rows.size(); unsigned glb_size = glb_rows.size(); unsigned row_index = (lub_size <= glb_size) ? lub_index : glb_index; // There are only upper or only lower bounds. if (row_index == UINT_MAX) { if (compute_def) { if (lub_index != UINT_MAX) { result = solve_for(lub_index, x, true); } else if (glb_index != UINT_MAX) { result = solve_for(glb_index, x, true); } else { result = def(); m_var2value[x] = rational::zero(); } SASSERT(eval(result) == eval(x)); } else { for (unsigned row_id : lub_rows) retire_row(row_id); for (unsigned row_id : glb_rows) retire_row(row_id); } return result; } SASSERT(lub_index != UINT_MAX); SASSERT(glb_index != UINT_MAX); if (compute_def) { if (lub_size <= glb_size) { result = def(m_rows[lub_index], x); } else { result = def(m_rows[glb_index], x); } } // The number of matching lower and upper bounds is small. if ((lub_size <= 2 || glb_size <= 2) && (lub_size <= 3 && glb_size <= 3) && (!is_int(x) || lub_is_unit || glb_is_unit)) { for (unsigned i = 0; i < lub_size; ++i) { unsigned row_id1 = lub_rows[i]; bool last = i + 1 == lub_size; rational coeff = get_coefficient(row_id1, x); for (unsigned row_id2 : glb_rows) { if (last) { resolve(row_id1, coeff, row_id2, x); } else { unsigned row_id3 = copy_row(row_id2); resolve(row_id1, coeff, row_id3, x); } } } for (unsigned row_id : lub_rows) retire_row(row_id); return result; } // General case. rational coeff = get_coefficient(row_index, x); for (unsigned row_id : lub_rows) { if (row_id != row_index) { resolve(row_index, coeff, row_id, x); } } for (unsigned row_id : glb_rows) { if (row_id != row_index) { resolve(row_index, coeff, row_id, x); } } retire_row(row_index); return result; } // // compute D and u. // // D = lcm(d1, d2) // u = eval(x) mod D // // d1 | (a1x + t1) & d2 | (a2x + t2) // = // d1 | (a1(D*x' + u) + t1) & d2 | (a2(D*x' + u) + t2) // = // d1 | (a1*u + t1) & d2 | (a2*u + t2) // // x := D*x' + u // model_based_opt::def model_based_opt::solve_mod(unsigned x, unsigned_vector const& mod_rows, bool compute_def) { SASSERT(!mod_rows.empty()); rational D(1); for (unsigned idx : mod_rows) { D = lcm(D, m_rows[idx].m_mod); } if (D.is_zero()) { throw default_exception("modulo 0 is not defined"); } TRACE("opt1", display(tout << "lcm: " << D << " x: v" << x << " tableau\n");); rational val_x = m_var2value[x]; rational u = mod(val_x, D); SASSERT(u.is_nonneg() && u < D); for (unsigned idx : mod_rows) { replace_var(idx, x, u); SASSERT(invariant(idx, m_rows[idx])); normalize(idx); } TRACE("opt1", display(tout << "tableau after replace x under mod\n");); // // update inequalities such that u is added to t and // D is multiplied to coefficient of x. // the interpretation of the new version of x is (x-u)/D // // a*x + t <= 0 // a*(D*x' + u) + t <= 0 // a*D*x' + a*u + t <= 0 // rational new_val = (val_x - u) / D; SASSERT(new_val.is_int()); unsigned y = add_var(new_val, true); unsigned_vector const& row_ids = m_var2row_ids[x]; uint_set visited; for (unsigned row_id : row_ids) { if (!visited.contains(row_id)) { // x |-> D*y + u replace_var(row_id, x, D, y, u); visited.insert(row_id); normalize(row_id); } } TRACE("opt1", display(tout << "tableau after replace x by y := v" << y << "\n");); def result = project(y, compute_def); if (compute_def) { result = (result * D) + u; } SASSERT(!compute_def || eval(result) == eval(x)); return result; } // update row with: x |-> C void model_based_opt::replace_var(unsigned row_id, unsigned x, rational const& C) { row& r = m_rows[row_id]; SASSERT(!get_coefficient(row_id, x).is_zero()); unsigned sz = r.m_vars.size(); unsigned i = 0, j = 0; rational coeff(0); for (; i < sz; ++i) { if (r.m_vars[i].m_id == x) { coeff = r.m_vars[i].m_coeff; } else { if (i != j) { r.m_vars[j] = r.m_vars[i]; } ++j; } } if (j != sz) { r.m_vars.shrink(j); } r.m_coeff += coeff*C; r.m_value += coeff*(C - m_var2value[x]); } // update row with: x |-> A*y + B void model_based_opt::replace_var(unsigned row_id, unsigned x, rational const& A, unsigned y, rational const& B) { row& r = m_rows[row_id]; rational coeff = get_coefficient(row_id, x); if (coeff.is_zero()) return; if (!r.m_alive) return; replace_var(row_id, x, B); r.m_vars.push_back(var(y, coeff*A)); r.m_value += coeff*A*m_var2value[y]; if (!r.m_vars.empty() && r.m_vars.back().m_id > y) { std::sort(r.m_vars.begin(), r.m_vars.end(), var::compare()); } m_var2row_ids[y].push_back(row_id); SASSERT(invariant(row_id, r)); } // 3x + t = 0 & 7 | (c*x + s) & ax <= u // 3 | -t & 21 | (-ct + 3s) & a-t <= 3u model_based_opt::def model_based_opt::solve_for(unsigned row_id1, unsigned x, bool compute_def) { TRACE("opt", tout << "v" << x << "\n" << m_rows[row_id1] << "\n";); rational a = get_coefficient(row_id1, x), b; ineq_type ty = m_rows[row_id1].m_type; SASSERT(!a.is_zero()); SASSERT(m_rows[row_id1].m_alive); if (a.is_neg()) { a.neg(); m_rows[row_id1].neg(); } SASSERT(a.is_pos()); if (ty == t_lt) { SASSERT(compute_def); m_rows[row_id1].m_coeff += a; } if (m_var2is_int[x] && !a.is_one()) { row& r1 = m_rows[row_id1]; vector coeffs; mk_coeffs_without(coeffs, r1.m_vars, x); rational c = r1.m_coeff; add_divides(coeffs, c, a); } unsigned_vector const& row_ids = m_var2row_ids[x]; uint_set visited; visited.insert(row_id1); for (unsigned row_id2 : row_ids) { if (!visited.contains(row_id2)) { visited.insert(row_id2); b = get_coefficient(row_id2, x); if (!b.is_zero()) { row& dst = m_rows[row_id2]; switch (dst.m_type) { case t_eq: case t_lt: case t_le: solve(row_id1, a, row_id2, x); break; case t_mod: // mod reduction already done. UNREACHABLE(); break; } } } } def result; if (compute_def) { result = def(m_rows[row_id1], x); m_var2value[x] = eval(result); } retire_row(row_id1); return result; } vector model_based_opt::project(unsigned num_vars, unsigned const* vars, bool compute_def) { vector result; for (unsigned i = 0; i < num_vars; ++i) { result.push_back(project(vars[i], compute_def)); TRACE("opt", display(tout << "After projecting: v" << vars[i] << "\n");); } return result; } } z3-z3-4.8.7/src/math/simplex/model_based_opt.h000066400000000000000000000157741356505360400211370ustar00rootroot00000000000000/*++ Copyright (c) 2016 Microsoft Corporation Module Name: model_based_opt.h Abstract: Model-based optimization for linear real arithmetic. Author: Nikolaj Bjorner (nbjorner) 2016-27-4 Revision History: --*/ #ifndef __MODEL_BASED_OPT_H__ #define __MODEL_BASED_OPT_H__ #include "util/util.h" #include "util/rational.h" #include "util/inf_eps_rational.h" namespace opt { enum ineq_type { t_eq, t_lt, t_le, t_mod }; typedef inf_eps_rational inf_eps; class model_based_opt { public: struct var { unsigned m_id; rational m_coeff; var(unsigned id, rational const& c): m_id(id), m_coeff(c) {} struct compare { bool operator()(var x, var y) { return x.m_id < y.m_id; } }; }; struct row { row(): m_type(t_le), m_value(0), m_alive(false) {} vector m_vars; // variables with coefficients rational m_coeff; // constant in inequality rational m_mod; // value the term divide ineq_type m_type; // inequality type rational m_value; // value of m_vars + m_coeff under interpretation of m_var2value. bool m_alive; // rows can be marked dead if they have been processed. void reset() { m_vars.reset(); m_coeff.reset(); m_value.reset(); } void neg() { for (var & v : m_vars) v.m_coeff.neg(); m_coeff.neg(); m_value.neg(); } rational get_coefficient(unsigned x) const; }; // A definition is a linear term of the form (vars + coeff) / div struct def { def(): m_div(1) {} def(row const& r, unsigned x); def(def const& other): m_vars(other.m_vars), m_coeff(other.m_coeff), m_div(other.m_div) {} vector m_vars; rational m_coeff; rational m_div; def operator+(def const& other) const; def operator/(unsigned n) const { return *this / rational(n); } def operator/(rational const& n) const; def operator*(rational const& n) const; def operator+(rational const& n) const; void normalize(); }; private: vector m_rows; static const unsigned m_objective_id = 0; vector m_var2row_ids; vector m_var2value; svector m_var2is_int; vector m_new_vars; unsigned_vector m_lub, m_glb, m_mod; unsigned_vector m_above, m_below; unsigned_vector m_retired_rows; bool invariant(); bool invariant(unsigned index, row const& r); row& objective() { return m_rows[0]; } bool find_bound(unsigned x, unsigned& bound_index, rational& bound_coeff, bool is_pos); rational get_coefficient(unsigned row_id, unsigned var_id) const; rational eval(row const& r) const; rational eval(unsigned x) const; rational eval(def const& d) const; void resolve(unsigned row_src, rational const& a1, unsigned row_dst, unsigned x); void solve(unsigned row_src, rational const& a1, unsigned row_dst, unsigned x); void mul_add(bool same_sign, unsigned row_id1, rational const& c, unsigned row_id2); void mul_add(unsigned x, rational const& a1, unsigned row_src, rational const& a2, unsigned row_dst); void mul(unsigned dst, rational const& c); void add(unsigned dst, rational const& c); void sub(unsigned dst, rational const& c); void del_var(unsigned dst, unsigned x); void set_row(unsigned row_id, vector const& coeffs, rational const& c, rational const& m, ineq_type rel); void add_constraint(vector const& coeffs, rational const& c, rational const& m, ineq_type r); void replace_var(unsigned row_id, unsigned x, rational const& A, unsigned y, rational const& B); void replace_var(unsigned row_id, unsigned x, rational const& C); void normalize(unsigned row_id); void mk_coeffs_without(vector& dst, vector const& src, unsigned x); unsigned new_row(); unsigned copy_row(unsigned row_id); rational n_sign(rational const& b) const; void update_values(unsigned_vector const& bound_vars, unsigned_vector const& bound_trail); void update_value(unsigned x, rational const& val); def project(unsigned var, bool compute_def); def solve_for(unsigned row_id, unsigned x, bool compute_def); def solve_mod(unsigned x, unsigned_vector const& mod_rows, bool compute_def); bool is_int(unsigned x) const { return m_var2is_int[x]; } void retire_row(unsigned row_id); public: model_based_opt(); // add a fresh variable with value 'value'. unsigned add_var(rational const& value, bool is_int = false); // retrieve updated value of variable. rational get_value(unsigned var_id); // add a constraint. We assume that the constraint is // satisfied under the values provided to the variables. void add_constraint(vector const& coeffs, rational const& c, ineq_type r); // add a divisibility constraint. The row should divide m. void add_divides(vector const& coeffs, rational const& c, rational const& m); // Set the objective function (linear). void set_objective(vector const& coeffs, rational const& c); // // find a maximal value for the objective function over the current values. // in other words, the returned maximal value may not be globally optimal, // but the current evaluation of variables are used to select a local // optimal. // inf_eps maximize(); // // Project set of variables from inequalities. // vector project(unsigned num_vars, unsigned const* vars, bool compute_def); // // Extract current rows (after projection). // void get_live_rows(vector& rows); void display(std::ostream& out) const; static std::ostream& display(std::ostream& out, row const& r); static std::ostream& display(std::ostream& out, def const& r); static void display(std::ostream& out, vector const& vars, rational const& coeff); }; } std::ostream& operator<<(std::ostream& out, opt::ineq_type ie); inline std::ostream& operator<<(std::ostream& out, opt::model_based_opt::def const& d) { return opt::model_based_opt::display(out, d); } inline std::ostream& operator<<(std::ostream& out, opt::model_based_opt::row const& r) { return opt::model_based_opt::display(out, r); } inline std::ostream& operator<<(std::ostream& out, opt::model_based_opt::var const v) { return out << "v" << v.m_id; } #endif z3-z3-4.8.7/src/math/simplex/network_flow.h000066400000000000000000000154561356505360400205340ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: network_flow.h Abstract: Implements Network Simplex algorithm for min cost flow problem Author: Anh-Dung Phan (t-anphan) 2013-10-24 Notes: This will be used to solve the dual of min cost flow problem i.e. optimization of difference constraint. We need a function to reduce DL constraints to min cost flow problem and another function to convert from min cost flow solution to DL solution. It remains unclear how to convert DL assignment to a basic feasible solution of Network Simplex. A naive approach is to run an algorithm on max flow in order to get a spanning tree. --*/ #ifndef NETWORK_FLOW_H_ #define NETWORK_FLOW_H_ #include "util/inf_rational.h" #include "smt/diff_logic.h" #include "smt/spanning_tree.h" namespace smt { enum min_flow_result { // Min cost flow problem is infeasible. // Diff logic optimization could be unbounded or infeasible. INFEASIBLE, // Min cost flow and diff logic optimization are both optimal. OPTIMAL, // Min cost flow problem is unbounded. // Diff logic optimization has to be infeasible. UNBOUNDED, }; enum pivot_rule { // First eligible edge pivot rule // Edges are traversed in a wraparound fashion FIRST_ELIGIBLE, // Best eligible edge pivot rule // The best edge is selected in every iteration BEST_ELIGIBLE, // Candidate list pivot rule // Major iterations: candidate list is built from eligible edges (in a wraparound way) // Minor iterations: the best edge is selected from the list CANDIDATE_LIST }; // Solve minimum cost flow problem using Network Simplex algorithm template class network_flow : private Ext { private: enum edge_state { LOWER = 1, BASIS = 0, }; typedef dl_var node; typedef dl_edge edge; typedef dl_graph graph; typedef typename Ext::numeral numeral; typedef typename Ext::fin_numeral fin_numeral; class pivot_rule_impl { protected: graph & m_graph; svector & m_states; vector & m_potentials; edge_id & m_enter_id; bool edge_in_tree(edge_id id) const { return m_states[id] == BASIS; } public: pivot_rule_impl(graph & g, vector & potentials, svector & states, edge_id & enter_id) : m_graph(g), m_potentials(potentials), m_states(states), m_enter_id(enter_id) { } virtual ~pivot_rule_impl() {} virtual bool choose_entering_edge() = 0; virtual pivot_rule rule() const = 0; }; class first_eligible_pivot : public pivot_rule_impl { edge_id m_next_edge; public: first_eligible_pivot(graph & g, vector & potentials, svector & states, edge_id & enter_id) : pivot_rule_impl(g, potentials, states, enter_id), m_next_edge(0) { } virtual bool choose_entering_edge(); virtual pivot_rule rule() const { return FIRST_ELIGIBLE; } }; class best_eligible_pivot : public pivot_rule_impl { public: best_eligible_pivot(graph & g, vector & potentials, svector & states, edge_id & enter_id) : pivot_rule_impl(g, potentials, states, enter_id) { } virtual pivot_rule rule() const { return BEST_ELIGIBLE; } virtual bool choose_entering_edge(); }; class candidate_list_pivot : public pivot_rule_impl { private: edge_id m_next_edge; svector m_candidates; unsigned m_num_candidates; unsigned m_minor_step; unsigned m_current_length; static const unsigned NUM_CANDIDATES = 10; static const unsigned MINOR_STEP_LIMIT = 5; public: candidate_list_pivot(graph & g, vector & potentials, svector & states, edge_id & enter_id) : pivot_rule_impl(g, potentials, states, enter_id), m_next_edge(0), m_minor_step(0), m_current_length(0), m_num_candidates(NUM_CANDIDATES), m_candidates(m_num_candidates) { } virtual pivot_rule rule() const { return CANDIDATE_LIST; } virtual bool choose_entering_edge(); }; graph m_graph; scoped_ptr m_tree; scoped_ptr m_pivot; vector m_balances; // nodes + 1 |-> [b -1b] Denote supply/demand b_i on node i vector m_potentials; // nodes + 1 |-> initial: +/- 1 // Duals of flows which are convenient to compute dual solutions // become solutions to Dual simplex. vector m_flows; // edges + nodes |-> assignment Basic feasible flows svector m_states; unsigned m_step; edge_id m_enter_id; edge_id m_leave_id; optional m_delta; // Initialize the network with a feasible spanning tree void initialize(); void update_potentials(); void update_flows(); bool choose_entering_edge(pivot_rule pr); // Send as much flow as possible around the cycle, the first basic edge with flow 0 will leave // Return false if the problem is unbounded bool choose_leaving_edge(); void update_spanning_tree(); numeral get_cost() const; bool edge_in_tree(edge_id id) const; bool is_infeasible(); bool check_well_formed(); bool check_optimal(); void display_primal(std::ofstream & os); void display_dual(std::ofstream & os); void display_spanning_tree(std::ofstream & os); void display_system(std::ofstream & os); public: network_flow(graph & g, vector const & balances); // Minimize cost flows // Return true if found an optimal solution, and return false if unbounded min_flow_result min_cost(pivot_rule pr = FIRST_ELIGIBLE); // Compute the optimal solution numeral get_optimal_solution(vector & result, bool is_dual); }; } #endif z3-z3-4.8.7/src/math/simplex/network_flow_def.h000066400000000000000000000467171356505360400213560ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: network_flow_def.h Abstract: Implements Network Simplex algorithm for min cost flow problem Author: Anh-Dung Phan (t-anphan) 2013-10-24 Notes: --*/ #ifndef NETWORK_FLOW_DEF_H_ #define NETWORK_FLOW_DEF_H_ #include "math/simplex/network_flow.h" #include "util/uint_set.h" #include "smt/spanning_tree_def.h" namespace smt { template bool network_flow::first_eligible_pivot::choose_entering_edge() { numeral cost = numeral::zero(); int num_edges = m_graph.get_num_edges(); for (int i = m_next_edge; i < m_next_edge + num_edges; ++i) { edge_id id = i % num_edges; if (edge_in_tree(id)) { continue; } node src = m_graph.get_source(id); node tgt = m_graph.get_target(id); cost = m_potentials[src] - m_potentials[tgt] - m_graph.get_weight(id); if (cost.is_pos()) { m_next_edge = m_enter_id = id; return true; } } return false; }; template bool network_flow::best_eligible_pivot::choose_entering_edge() { unsigned num_edges = m_graph.get_num_edges(); numeral cost = numeral::zero(); for (unsigned i = 0; i < num_edges; ++i) { node src = m_graph.get_source(i); node tgt = m_graph.get_target(i); if (!edge_in_tree(i)) { numeral new_cost = m_potentials[src] - m_potentials[tgt] - m_graph.get_weight(i); if (new_cost > cost) { cost = new_cost; m_enter_id = i; } } } return cost.is_pos(); }; template bool network_flow::candidate_list_pivot::choose_entering_edge() { numeral cost = numeral::zero(); if (m_current_length == 0 || m_minor_step == MINOR_STEP_LIMIT) { // Build the candidate list unsigned num_edges = m_graph.get_num_edges(); m_current_length = 0; for (unsigned i = m_next_edge; i < m_next_edge + num_edges; ++i) { edge_id id = (i >= num_edges) ? i - num_edges : i; node src = m_graph.get_source(id); node tgt = m_graph.get_target(id); if (!edge_in_tree(id)) { numeral new_cost = m_potentials[src] - m_potentials[tgt] - m_graph.get_weight(id); if (new_cost.is_pos()) { m_candidates[m_current_length] = id; ++m_current_length; if (new_cost > cost) { cost = new_cost; m_enter_id = id; } } if (m_current_length >= m_num_candidates) break; } } m_next_edge = m_enter_id; m_minor_step = 1; return cost.is_pos(); } ++m_minor_step; for (unsigned i = 0; i < m_current_length; ++i) { edge_id id = m_candidates[i]; node src = m_graph.get_source(id); node tgt = m_graph.get_target(id); if (!edge_in_tree(id)) { numeral new_cost = m_potentials[src] - m_potentials[tgt] - m_graph.get_weight(id); if (new_cost > cost) { cost = new_cost; m_enter_id = id; } // Remove stale candidates if (!new_cost.is_pos()) { --m_current_length; m_candidates[i] = m_candidates[m_current_length]; --i; } } } return cost.is_pos(); }; template network_flow::network_flow(graph & g, vector const & balances) : m_balances(balances) { // Network flow graph has the edges in the reversed order compared to constraint graph // We only take enabled edges from the original graph for (unsigned i = 0; i < g.get_num_nodes(); ++i) { m_graph.init_var(i); } vector const & es = g.get_all_edges(); for (unsigned i = 0; i < es.size(); ++i) { edge const & e = es[i]; if (e.is_enabled()) { m_graph.add_edge(e.get_target(), e.get_source(), e.get_weight(), explanation()); } } TRACE("network_flow", { tout << "Difference logic optimization:" << std::endl; display_dual(tout); tout << "Minimum cost flow:" << std::endl; display_primal(tout); };); m_step = 0; m_tree = alloc(basic_spanning_tree, m_graph); } template void network_flow::initialize() { TRACE("network_flow", tout << "initialize...\n";); // Create an artificial root node to construct initial spanning tree unsigned num_nodes = m_graph.get_num_nodes(); unsigned num_edges = m_graph.get_num_edges(); node root = num_nodes; m_graph.init_var(root); m_potentials.resize(num_nodes + 1); m_potentials[root] = numeral::zero(); m_balances.resize(num_nodes + 1); fin_numeral sum_supply = fin_numeral::zero(); for (unsigned i = 0; i < num_nodes; ++i) { sum_supply += m_balances[i]; } m_balances[root] = -sum_supply; m_flows.resize(num_nodes + num_edges); m_flows.fill(numeral::zero()); m_states.resize(num_nodes + num_edges); m_states.fill(LOWER); // Create artificial edges from/to root node to/from other nodes and initialize the spanning tree svector tree; for (unsigned i = 0; i < num_nodes; ++i) { bool is_forward = !m_balances[i].is_neg(); m_states[num_edges + i] = BASIS; node src = is_forward ? i : root; node tgt = is_forward ? root : i; m_flows[num_edges + i] = is_forward ? m_balances[i] : -m_balances[i]; m_potentials[i] = is_forward ? numeral::one() : -numeral::one(); tree.push_back(m_graph.add_edge(src, tgt, numeral::one(), explanation())); } m_tree->initialize(tree); TRACE("network_flow", tout << pp_vector("Potentials", m_potentials); tout << pp_vector("Flows", m_flows); tout << "Cost: " << get_cost() << "\n"; tout << "Spanning tree:\n"; display_spanning_tree(tout); display_primal(tout);); SASSERT(check_well_formed()); } template void network_flow::update_potentials() { node src = m_graph.get_source(m_enter_id); node tgt = m_graph.get_target(m_enter_id); numeral cost = m_potentials[src] - m_potentials[tgt] - m_graph.get_weight(m_enter_id); numeral change; node start; if (m_tree->in_subtree_t2(tgt)) { change = cost; start = tgt; } else { change = -cost; start = src; } SASSERT(m_tree->in_subtree_t2(start)); TRACE("network_flow", tout << "update_potentials of T_" << start << " with change = " << change << "...\n";); svector descendants; m_tree->get_descendants(start, descendants); SASSERT(descendants.size() >= 1); for (unsigned i = 0; i < descendants.size(); ++i) { node u = descendants[i]; m_potentials[u] += change; } TRACE("network_flow", tout << pp_vector("Potentials", m_potentials);); } template void network_flow::update_flows() { m_flows[m_enter_id] += *m_delta; node src = m_graph.get_source(m_enter_id); node tgt = m_graph.get_target(m_enter_id); svector path; svector against; m_tree->get_path(src, tgt, path, against); SASSERT(path.size() >= 1); for (unsigned i = 0; i < path.size(); ++i) { edge_id e_id = path[i]; m_flows[e_id] += against[i] ? - *m_delta : *m_delta; } TRACE("network_flow", tout << pp_vector("Flows", m_flows);); } template bool network_flow::choose_leaving_edge() { node src = m_graph.get_source(m_enter_id); node tgt = m_graph.get_target(m_enter_id); m_delta.set_invalid(); edge_id leave_id = null_edge_id; svector path; svector against; m_tree->get_path(src, tgt, path, against); SASSERT(path.size() >= 1); for (unsigned i = 0; i < path.size(); ++i) { edge_id e_id = path[i]; if (against[i] && (!m_delta || m_flows[e_id] < *m_delta)) { m_delta = m_flows[e_id]; leave_id = e_id; } } m_leave_id = leave_id; return m_delta; } template void network_flow::update_spanning_tree() { m_tree->update(m_enter_id, m_leave_id); } template bool network_flow::choose_entering_edge(pivot_rule pr) { if (!m_pivot || pr != m_pivot->rule()) { switch (pr) { case FIRST_ELIGIBLE: m_pivot = alloc(first_eligible_pivot, m_graph, m_potentials, m_states, m_enter_id); break; case BEST_ELIGIBLE: m_pivot = alloc(best_eligible_pivot, m_graph, m_potentials, m_states, m_enter_id); break; case CANDIDATE_LIST: m_pivot = alloc(candidate_list_pivot, m_graph, m_potentials, m_states, m_enter_id); break; default: UNREACHABLE(); } } return m_pivot->choose_entering_edge(); } // Minimize cost flows template min_flow_result network_flow::min_cost(pivot_rule pr) { initialize(); while (choose_entering_edge(pr)) { bool bounded = choose_leaving_edge(); if (!bounded) return UNBOUNDED; vectorconst& es = m_graph.get_all_edges(); TRACE("network_flow", { edge const& e_in = es[m_enter_id]; edge const& e_out = es[m_leave_id]; node src_in = e_in.get_source(); node tgt_in = e_in.get_target(); node src_out = e_out.get_source(); node tgt_out = e_out.get_target(); numeral c1 = m_potentials[src_in] - m_potentials[tgt_in] - m_graph.get_weight(m_enter_id); numeral c2 = m_potentials[src_out] - m_potentials[tgt_out] - m_graph.get_weight(m_leave_id); tout << "new base: y_" << src_in << "_" << tgt_in << " cost: " << c1 << " delta: " << *m_delta << "\n"; tout << "old base: y_" << src_out << "_" << tgt_out << " cost: " << c2 << "\n"; } ); update_flows(); if (m_enter_id != m_leave_id) { SASSERT(edge_in_tree(m_leave_id)); SASSERT(!edge_in_tree(m_enter_id)); m_states[m_enter_id] = BASIS; m_states[m_leave_id] = LOWER; update_spanning_tree(); update_potentials(); TRACE("network_flow", tout << "Spanning tree:\n"; display_spanning_tree(tout); tout << "Cost: " << get_cost() << "\n"; display_primal(tout); ); SASSERT(check_well_formed()); } } TRACE("network_flow", tout << "Spanning tree:\n"; display_spanning_tree(tout); tout << "Cost: " << get_cost() << "\n"; display_primal(tout); ); if (is_infeasible()) return INFEASIBLE; TRACE("network_flow", tout << "Found optimal solution.\n";); SASSERT(check_optimal()); return OPTIMAL; } template bool network_flow::is_infeasible() { // Flows of artificial arcs should be zero unsigned num_nodes = m_graph.get_num_nodes(); unsigned num_edges = m_graph.get_num_edges(); SASSERT(m_flows.size() == num_edges); for (unsigned i = 1; i < num_nodes; ++i) { if (m_flows[num_edges - i].is_pos()) return true; } return false; } // Get the optimal solution template typename network_flow::numeral network_flow::get_optimal_solution(vector & result, bool is_dual) { numeral objective_value = get_cost(); result.reset(); if (is_dual) { result.append(m_potentials); } else { result.append(m_flows); } return objective_value; } template typename network_flow::numeral network_flow::get_cost() const { numeral objective_value = numeral::zero(); unsigned num_edges = m_graph.get_num_edges(); for (unsigned i = 0; i < num_edges; ++i) { if (edge_in_tree(i)) { objective_value += m_flows[i].get_rational() * m_graph.get_weight(i); } } return objective_value; } template bool network_flow::edge_in_tree(edge_id id) const { return m_states[id] == BASIS; } template bool network_flow::check_well_formed() { SASSERT(m_tree->check_well_formed()); SASSERT(!m_delta || !(*m_delta).is_neg()); // m_flows are zero on non-basic edges for (unsigned i = 0; i < m_flows.size(); ++i) { SASSERT(!m_flows[i].is_neg()); SASSERT(edge_in_tree(i) || m_flows[i].is_zero()); } unsigned num_edges = m_graph.get_num_edges(); for (unsigned i = 0; i < num_edges; ++i) { if (edge_in_tree(i)) { dl_var src = m_graph.get_source(i); dl_var tgt = m_graph.get_target(i); numeral weight = m_graph.get_weight(i); SASSERT(m_potentials[src] - m_potentials[tgt] == weight); } } return true; } template bool network_flow::check_optimal() { numeral total_cost = get_cost(); unsigned num_edges = m_graph.get_num_edges(); for (unsigned i = 0; i < num_edges; ++i) { dl_var src = m_graph.get_source(i); dl_var tgt = m_graph.get_target(i); numeral weight = m_graph.get_weight(i); SASSERT(m_potentials[src] - m_potentials[tgt] <= weight); } // m_flows are zero on non-basic edges for (unsigned i = 0; i < m_flows.size(); ++i) { SASSERT(edge_in_tree(i) || m_flows[i].is_zero()); } numeral total_balance = numeral::zero(); for (unsigned i = 0; i < m_potentials.size(); ++i) { total_balance += m_balances[i] * m_potentials[i]; } TRACE("network_flow", tout << "Total balance: " << total_balance << ", total cost: " << total_cost << std::endl;); return total_cost == total_balance; } // display methods template void network_flow::display_primal(std::ofstream & os) { vector const & es = m_graph.get_all_edges(); for (unsigned i = 0; i < m_graph.get_num_edges(); ++i) { edge const & e = es[i]; os << "(declare-fun y_" << e.get_source() << "_" << e.get_target() << " () Real)" << std::endl; }; for (unsigned i = 0; i < m_graph.get_num_edges(); ++i) { edge const & e = es[i]; os << "(assert (>= y_" << e.get_source() << "_" << e.get_target() << " 0))" << std::endl; }; for (unsigned i = 0; i < m_graph.get_num_nodes(); ++i) { bool initialized = false; for (unsigned j = 0; j < m_graph.get_num_edges(); ++j) { edge const & e = es[j]; if (e.get_target() == i || e.get_source() == i) { if (!initialized) { os << "(assert (= (+"; } initialized = true; if (e.get_target() == i) { os << " y_" << e.get_source() << "_" << e.get_target(); } else { os << " (- y_" << e.get_source() << "_" << e.get_target() << ")"; } } } if(initialized) { os << " " << m_balances[i] << ") 0))" << std::endl; } } os << "(minimize (+"; for (unsigned i = 0; i < m_graph.get_num_edges(); ++i) { edge const & e = es[i]; os << " (* " << e.get_weight() << " y_" << e.get_source() << "_" << e.get_target() << ")"; }; os << "))" << std::endl; os << "(optimize)" << std::endl; if (!m_flows.empty()) { for (unsigned i = 0; i < m_graph.get_num_edges(); ++i) { edge const & e = es[i]; os << "; y_" << e.get_source() << "_" << e.get_target() << " = " << m_flows[i] << "\n"; } } } template void network_flow::display_dual(std::ofstream & os) { for (unsigned i = 0; i < m_graph.get_num_nodes(); ++i) { os << "(declare-fun v" << i << " () Real)" << std::endl; } vector const & es = m_graph.get_all_edges(); for (unsigned i = 0; i < m_graph.get_num_edges(); ++i) { edge const & e = es[i]; os << "(assert (<= (- v" << e.get_source() << " v" << e.get_target() << ") " << e.get_weight() << "))" << std::endl; }; os << "(assert (= v0 0))" << std::endl; os << "(maximize (+"; for (unsigned i = 0; i < m_balances.size(); ++i) { os << " (* " << m_balances[i] << " v" << i << ")"; }; os << "))" << std::endl; os << "(optimize)" << std::endl; } template void network_flow::display_spanning_tree(std::ofstream & os) { ++m_step;; std::string prefix = "T"; prefix.append(std::to_string(m_step)); prefix.append("_"); unsigned root = m_graph.get_num_nodes() - 1; for (unsigned i = 0; i < root; ++i) { os << prefix << i << "[shape=circle,label=\"" << prefix << i << " ["; os << m_potentials[i] << "/" << m_balances[i] << "]\"];\n"; } os << prefix << root << "[shape=doublecircle,label=\"" << prefix << root << " ["; os << m_potentials[root] << "/" << m_balances[root] << "]\"];\n"; unsigned num_edges = m_graph.get_num_edges(); for (unsigned i = 0; i < num_edges; ++i) { os << prefix << m_graph.get_source(i) << " -> " << prefix << m_graph.get_target(i); if (edge_in_tree(i)) { os << "[color=red,penwidth=3.0,label=\"" << m_flows[i] << "/" << m_graph.get_weight(i) << "\"];\n"; } else { os << "[label=\"" << m_flows[i] << "/" << m_graph.get_weight(i) << "\"];\n"; } } os << std::endl; } } #endif z3-z3-4.8.7/src/math/simplex/simplex.cpp000066400000000000000000000006241356505360400200170ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: simplex.h Abstract: Multi-precision simplex tableau. Author: Nikolaj Bjorner (nbjorner) 2014-01-15 Notes: --*/ #include "math/simplex/simplex.h" #include "math/simplex/sparse_matrix_def.h" #include "math/simplex/simplex_def.h" namespace simplex { template class simplex; template class simplex; }; z3-z3-4.8.7/src/math/simplex/simplex.h000066400000000000000000000162051356505360400174660ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: simplex.h Abstract: Multi-precision simplex tableau. - It uses code from theory_arith where applicable. - It is detached from the theory class and ASTs. - It uses non-shared mpz/mpq's avoiding global locks and operations on rationals. - It follows the same sparse tableau layout (no LU yet). - It does not include features for non-linear arithmetic. - Branch/bound/cuts is external. Author: Nikolaj Bjorner (nbjorner) 2014-01-15 Notes: --*/ #ifndef SIMPLEX_H_ #define SIMPLEX_H_ #include "math/simplex/sparse_matrix.h" #include "util/mpq_inf.h" #include "util/heap.h" #include "util/lbool.h" #include "util/uint_set.h" namespace simplex { template class simplex { typedef unsigned var_t; typedef typename Ext::eps_numeral eps_numeral; typedef typename Ext::numeral numeral; typedef typename Ext::manager manager; typedef typename Ext::eps_manager eps_manager; typedef typename Ext::scoped_numeral scoped_numeral; typedef _scoped_numeral scoped_eps_numeral; typedef _scoped_numeral_vector scoped_eps_numeral_vector; typedef sparse_matrix matrix; struct var_lt { bool operator()(var_t v1, var_t v2) const { return v1 < v2; } }; typedef heap var_heap; struct stats { unsigned m_num_pivots; unsigned m_num_infeasible; unsigned m_num_checks; stats() { reset(); } void reset() { memset(this, 0, sizeof(*this)); } }; enum pivot_strategy_t { S_BLAND, S_GREATEST_ERROR, S_LEAST_ERROR, S_DEFAULT }; struct var_info { unsigned m_base2row:29; unsigned m_is_base:1; unsigned m_lower_valid:1; unsigned m_upper_valid:1; eps_numeral m_value; eps_numeral m_lower; eps_numeral m_upper; numeral m_base_coeff; var_info(): m_base2row(0), m_is_base(false), m_lower_valid(false), m_upper_valid(false) {} }; static const var_t null_var; reslimit& m_limit; mutable manager m; mutable eps_manager em; mutable matrix M; unsigned m_max_iterations; var_heap m_to_patch; vector m_vars; svector m_row2base; bool m_bland; unsigned m_blands_rule_threshold; random_gen m_random; uint_set m_left_basis; unsigned m_infeasible_var; unsigned_vector m_base_vars; stats m_stats; public: simplex(reslimit& lim): m_limit(lim), M(m), m_max_iterations(UINT_MAX), m_to_patch(1024), m_bland(false), m_blands_rule_threshold(1000) {} typedef typename matrix::row row; typedef typename matrix::row_iterator row_iterator; typedef typename matrix::col_iterator col_iterator; void ensure_var(var_t v); row add_row(var_t base, unsigned num_vars, var_t const* vars, numeral const* coeffs); row get_infeasible_row(); var_t get_base_var(row const& r) const { return m_row2base[r.id()]; } numeral const& get_base_coeff(row const& r) const { return m_vars[m_row2base[r.id()]].m_base_coeff; } void del_row(var_t base_var); void set_lower(var_t var, eps_numeral const& b); void set_upper(var_t var, eps_numeral const& b); void get_lower(var_t var, scoped_eps_numeral& b) const { b = m_vars[var].m_lower; } void get_upper(var_t var, scoped_eps_numeral& b) const { b = m_vars[var].m_upper; } bool above_lower(var_t var, eps_numeral const& b) const; bool below_upper(var_t var, eps_numeral const& b) const; bool below_lower(var_t v) const; bool above_upper(var_t v) const; bool lower_valid(var_t var) const { return m_vars[var].m_lower_valid; } bool upper_valid(var_t var) const { return m_vars[var].m_upper_valid; } void unset_lower(var_t var); void unset_upper(var_t var); void set_value(var_t var, eps_numeral const& b); void set_max_iterations(unsigned n) { m_max_iterations = n; } void reset(); lbool make_feasible(); lbool minimize(var_t var); eps_numeral const& get_value(var_t v); void display(std::ostream& out) const; void display_row(std::ostream& out, row const& r, bool values = true); unsigned get_num_vars() const { return m_vars.size(); } row_iterator row_begin(row const& r) { return M.row_begin(r); } row_iterator row_end(row const& r) { return M.row_end(r); } void collect_statistics(::statistics & st) const; private: void del_row(row const& r); var_t select_var_to_fix(); pivot_strategy_t pivot_strategy(); var_t select_smallest_var() { return m_to_patch.empty()?null_var:m_to_patch.erase_min(); } var_t select_error_var(bool least); void check_blands_rule(var_t v, unsigned& num_repeated); bool make_var_feasible(var_t x_i); void update_and_pivot(var_t x_i, var_t x_j, numeral const& a_ij, eps_numeral const& new_value); void update_value(var_t v, eps_numeral const& delta); void update_value_core(var_t v, eps_numeral const& delta); void pivot(var_t x_i, var_t x_j, numeral const& a_ij); void move_to_bound(var_t x, bool to_lower); var_t select_pivot(var_t x_i, bool is_below, scoped_numeral& out_a_ij); var_t select_pivot_blands(var_t x_i, bool is_below, scoped_numeral& out_a_ij); var_t select_pivot_core(var_t x_i, bool is_below, scoped_numeral& out_a_ij); int get_num_non_free_dep_vars(var_t x_j, int best_so_far); var_t pick_var_to_leave(var_t x_j, bool is_pos, scoped_eps_numeral& gain, scoped_numeral& new_a_ij, bool& inc); void select_pivot_primal(var_t v, var_t& x_i, var_t& x_j, scoped_numeral& a_ij, bool& inc_x_i, bool& inc_x_j); bool at_lower(var_t v) const; bool at_upper(var_t v) const; bool above_lower(var_t v) const; bool below_upper(var_t v) const; bool outside_bounds(var_t v) const { return below_lower(v) || above_upper(v); } bool is_free(var_t v) const { return !m_vars[v].m_lower_valid && !m_vars[v].m_upper_valid; } bool is_non_free(var_t v) const { return !is_free(v); } bool is_base(var_t x) const { return m_vars[x].m_is_base; } void add_patch(var_t v); bool well_formed() const; bool well_formed_row(row const& r) const; bool is_feasible() const; }; }; #endif z3-z3-4.8.7/src/math/simplex/simplex_def.h000066400000000000000000001062261356505360400203070ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: simplex_def.h Abstract: Author: Nikolaj Bjorner (nbjorner) 2014-01-15 Notes: Sign of base variables can vary. Sign could possibly be normalized to positive. Otherwise, sign could be accounted in pivoting. --*/ #ifndef SIMPLEX_DEF_H_ #define SIMPLEX_DEF_H_ namespace simplex { template const typename simplex::var_t simplex::null_var = UINT_MAX; template typename simplex::row simplex::add_row(var_t base_var, unsigned num_vars, var_t const* vars, numeral const* coeffs) { m_base_vars.reset(); row r = M.mk_row(); for (unsigned i = 0; i < num_vars; ++i) { if (!m.is_zero(coeffs[i])) { var_t v = vars[i]; if (is_base(v)) { m_base_vars.push_back(i); } M.add_var(r, coeffs[i], v); } } scoped_numeral mul(m), a(m), b(m), c(m); m.set(mul, 1); for (unsigned i = 0; i < m_base_vars.size(); ++i) { var_t v = vars[m_base_vars[i]]; m.mul(coeffs[m_base_vars[i]], mul, a); m.set(b, m_vars[v].m_base_coeff); m.lcm(a, b, c); TRACE("simplex", m.display(tout << " a: ", a); m.display(tout << " b v" << v << " : ", b); m.display(tout << " c: ", c); tout << "\n"; M.display_row(tout, r); M.display_row(tout, row(m_vars[v].m_base2row)); if (m.is_zero(b)) { display(tout); }); SASSERT(is_base(v)); m.abs(c); m.div(c, a, b); m.div(c, m_vars[v].m_base_coeff, a); m.mul(mul, b, mul); M.mul(r, b); m.neg(a); M.add(r, a, row(m_vars[v].m_base2row)); TRACE("simplex", M.display_row(tout, r);); } scoped_numeral base_coeff(m); scoped_eps_numeral value(em), tmp(em); row_iterator it = M.row_begin(r), end = M.row_end(r); for (; it != end; ++it) { var_t v = it->m_var; if (v == base_var) { m.set(base_coeff, it->m_coeff); } else { SASSERT(!is_base(v)); em.mul(m_vars[v].m_value, it->m_coeff, tmp); em.add(value, tmp, value); } } SASSERT(!m.is_zero(base_coeff)); TRACE("simplex", for (unsigned i = 0; i < num_vars; ++i) { m.display(tout << "v" << vars[i] << " * ", coeffs[i]); tout << " "; if (i + 1 < num_vars) tout << " + "; } tout << "\n"; row_iterator it2 = M.row_begin(r); bool first = true; for (; it2 != end; ++it2) { if (!first) tout << " + "; tout << "v" << it2->m_var << " * "; m.display(tout, it2->m_coeff); tout << " "; first = false; } tout << "\n"; ); SASSERT(!is_base(base_var)); em.neg(value); em.div(value, base_coeff, value); while (m_row2base.size() <= r.id()) { m_row2base.push_back(null_var); } m_row2base[r.id()] = base_var; m_vars[base_var].m_base2row = r.id(); m_vars[base_var].m_is_base = true; m.set(m_vars[base_var].m_base_coeff, base_coeff); em.set(m_vars[base_var].m_value, value); add_patch(base_var); SASSERT(well_formed_row(r)); SASSERT(well_formed()); return r; } template typename simplex::row simplex::get_infeasible_row() { SASSERT(is_base(m_infeasible_var)); unsigned row_id = m_vars[m_infeasible_var].m_base2row; return row(row_id); } template void simplex::add_patch(var_t v) { SASSERT(is_base(v)); if (outside_bounds(v)) { TRACE("simplex", tout << "Add patch: v" << v << "\n";); m_to_patch.insert(v); } } template void simplex::del_row(row const& r) { var_t var = m_row2base[r.id()]; m_vars[var].m_is_base = false; m_vars[var].m_lower_valid = false; m_vars[var].m_upper_valid = false; m_row2base[r.id()] = null_var; M.del(r); SASSERT(M.col_begin(var) == M.col_end(var)); SASSERT(well_formed()); } template void simplex::del_row(var_t var) { TRACE("simplex", tout << var << "\n";); row r; if (is_base(var)) { r = row(m_vars[var].m_base2row); } else { col_iterator it = M.col_begin(var), end = M.col_end(var); if (it == end) { return; } typename matrix::row_entry const& re = it.get_row_entry(); r = it.get_row(); var_t old_base = m_row2base[r.id()]; scoped_eps_numeral new_value(em); var_info& vi = m_vars[old_base]; if (below_lower(old_base)) { new_value = vi.m_lower; } else if (above_upper(old_base)) { new_value = vi.m_upper; } else { new_value = vi.m_value; } // need to move var such that old_base comes in bound. update_and_pivot(old_base, var, re.m_coeff, new_value); SASSERT(is_base(var)); SASSERT(m_vars[var].m_base2row == r.id()); SASSERT(!below_lower(old_base) && !above_upper(old_base)); } del_row(r); TRACE("simplex", display(tout);); SASSERT(well_formed()); } template bool simplex::above_lower(var_t var, eps_numeral const& b) const { var_info const& vi = m_vars[var]; return !vi.m_lower_valid || em.gt(b, vi.m_lower); } template bool simplex::below_upper(var_t var, eps_numeral const& b) const { var_info const& vi = m_vars[var]; return !vi.m_upper_valid || em.lt(b, vi.m_upper); } template void simplex::set_lower(var_t var, eps_numeral const& b) { var_info& vi = m_vars[var]; em.set(vi.m_lower, b); vi.m_lower_valid = true; TRACE("simplex", em.display(tout << "v" << var << " lower: ", b); em.display(tout << " value: ", vi.m_value);); SASSERT(!vi.m_upper_valid || em.le(b, vi.m_upper)); if (!vi.m_is_base && em.lt(vi.m_value, b)) { scoped_eps_numeral delta(em); em.sub(b, vi.m_value, delta); update_value(var, delta); } else if (vi.m_is_base && em.lt(vi.m_value, b)) { SASSERT(outside_bounds(var)); add_patch(var); } SASSERT(well_formed()); } template void simplex::set_upper(var_t var, eps_numeral const& b) { var_info& vi = m_vars[var]; em.set(vi.m_upper, b); vi.m_upper_valid = true; SASSERT(!vi.m_lower_valid || em.le(vi.m_lower, b)); if (!vi.m_is_base && em.gt(vi.m_value, b)) { scoped_eps_numeral delta(em); em.sub(b, vi.m_value, delta); update_value(var, delta); } else if (vi.m_is_base && em.lt(b, vi.m_value)) { SASSERT(outside_bounds(var)); add_patch(var); } SASSERT(well_formed()); } template void simplex::unset_lower(var_t var) { m_vars[var].m_lower_valid = false; } template void simplex::unset_upper(var_t var) { m_vars[var].m_upper_valid = false; } template void simplex::set_value(var_t var, eps_numeral const& b) { scoped_eps_numeral delta(em); em.sub(b, m_vars[var].m_value, delta); update_value(var, delta); SASSERT(well_formed()); } template typename simplex::eps_numeral const& simplex::get_value(var_t v) { return m_vars[v].m_value; } template void simplex::display(std::ostream& out) const { M.display(out); for (unsigned i = 0; i < m_vars.size(); ++i) { var_info const& vi = m_vars[i]; out << "v" << i << " "; out << em.to_string(vi.m_value); out << " ["; if (vi.m_lower_valid) out << em.to_string(vi.m_lower); else out << "-oo"; out << ":"; if (vi.m_upper_valid) out << em.to_string(vi.m_upper); else out << "oo"; out << "] "; if (vi.m_is_base) out << "b:" << vi.m_base2row << " "; //col_iterator it = M.col_begin(i), end = M.col_end(i); //for (; it != end; ++it) { // out << "r" << it.get_row().id() << " "; //} out << "\n"; } } template void simplex::display_row(std::ostream& out, row const& r, bool values) { row_iterator it = M.row_begin(r), end = M.row_end(r); for (; it != end; ++it) { m.display(out, it->m_coeff); out << "*v" << it->m_var << " "; if (values) { var_info const& vi = m_vars[it->m_var]; out << em.to_string(vi.m_value); out << " ["; if (vi.m_lower_valid) out << em.to_string(vi.m_lower); else out << "-oo"; out << ":"; if (vi.m_upper_valid) out << em.to_string(vi.m_upper); else out << "oo"; out << "] "; } } out << "\n"; } template void simplex::ensure_var(var_t v) { while (v >= m_vars.size()) { M.ensure_var(m_vars.size()); m_vars.push_back(var_info()); } if (m_to_patch.get_bounds() <= v) { m_to_patch.set_bounds(2*v+1); } } template void simplex::reset() { M.reset(); m_to_patch.reset(); m_vars.reset(); m_row2base.reset(); m_left_basis.reset(); m_base_vars.reset(); } template lbool simplex::make_feasible() { ++m_stats.m_num_checks; m_left_basis.reset(); m_infeasible_var = null_var; unsigned num_iterations = 0; unsigned num_repeated = 0; var_t v = null_var; m_bland = false; SASSERT(well_formed()); while ((v = select_var_to_fix()) != null_var) { TRACE("simplex", display(tout << "v" << v << "\n");); if (!m_limit.inc() || num_iterations > m_max_iterations) { return l_undef; } check_blands_rule(v, num_repeated); if (!make_var_feasible(v)) { m_to_patch.insert(v); m_infeasible_var = v; ++m_stats.m_num_infeasible; return l_false; } ++num_iterations; } SASSERT(well_formed()); return l_true; } /** \brief Make x_j the new base variable for row of x_i. x_i is assumed base variable of row r_i. x_j is assumed to have coefficient a_ij in r_i. a_ii*x_i + a_ij*x_j + r_i = 0 current value of x_i is v_i new value of x_i is new_value a_ii*(x_i + new_value - x_i) + a_ij*((x_i - new_value)*a_ii/a_ij + x_j) + r_i = 0 Let r_k be a row where x_j has coefficient x_kj != 0. r_k <- r_k * a_ij - r_i * a_kj */ template void simplex::update_and_pivot(var_t x_i, var_t x_j, numeral const& a_ij, eps_numeral const& new_value) { SASSERT(is_base(x_i)); SASSERT(!is_base(x_j)); var_info& x_iI = m_vars[x_i]; scoped_eps_numeral theta(em); theta = x_iI.m_value; theta -= new_value; numeral const& a_ii = x_iI.m_base_coeff; em.mul(theta, a_ii, theta); em.div(theta, a_ij, theta); update_value(x_j, theta); SASSERT(em.eq(x_iI.m_value, new_value)); pivot(x_i, x_j, a_ij); } template void simplex::pivot(var_t x_i, var_t x_j, numeral const& a_ij) { ++m_stats.m_num_pivots; var_info& x_iI = m_vars[x_i]; var_info& x_jI = m_vars[x_j]; unsigned r_i = x_iI.m_base2row; m_row2base[r_i] = x_j; x_jI.m_base2row = r_i; m.set(x_jI.m_base_coeff, a_ij); x_jI.m_is_base = true; x_iI.m_is_base = false; add_patch(x_j); SASSERT(well_formed_row(row(r_i))); col_iterator it = M.col_begin(x_j), end = M.col_end(x_j); scoped_numeral a_kj(m), g(m); for (; it != end; ++it) { row r_k = it.get_row(); if (r_k.id() != r_i) { a_kj = it.get_row_entry().m_coeff; a_kj.neg(); M.mul(r_k, a_ij); M.add(r_k, a_kj, row(r_i)); var_t s = m_row2base[r_k.id()]; numeral& coeff = m_vars[s].m_base_coeff; m.mul(coeff, a_ij, coeff); M.gcd_normalize(r_k, g); if (!m.is_one(g)) { m.div(coeff, g, coeff); } SASSERT(well_formed_row(row(r_k))); } } SASSERT(well_formed()); } template void simplex::update_value(var_t v, eps_numeral const& delta) { if (em.is_zero(delta)) { return; } update_value_core(v, delta); col_iterator it = M.col_begin(v), end = M.col_end(v); // v <- v + delta // s*s_coeff + v*v_coeff + R = 0 // -> // (v + delta)*v_coeff + (s - delta*v_coeff/s_coeff)*v + R = 0 for (; it != end; ++it) { row r = it.get_row(); var_t s = m_row2base[r.id()]; var_info& si = m_vars[s]; scoped_eps_numeral delta2(em); numeral const& coeff = it.get_row_entry().m_coeff; em.mul(delta, coeff, delta2); em.div(delta2, si.m_base_coeff, delta2); delta2.neg(); update_value_core(s, delta2); } } template void simplex::update_value_core(var_t v, eps_numeral const& delta) { eps_numeral& val = m_vars[v].m_value; em.add(val, delta, val); if (is_base(v)) { add_patch(v); } } template bool simplex::below_lower(var_t v) const { var_info const& vi = m_vars[v]; return vi.m_lower_valid && em.lt(vi.m_value, vi.m_lower); } template bool simplex::above_upper(var_t v) const { var_info const& vi = m_vars[v]; return vi.m_upper_valid && em.gt(vi.m_value, vi.m_upper); } template bool simplex::above_lower(var_t v) const { var_info const& vi = m_vars[v]; return !vi.m_lower_valid || em.gt(vi.m_value, vi.m_lower); } template bool simplex::below_upper(var_t v) const { var_info const& vi = m_vars[v]; return !vi.m_upper_valid || em.lt(vi.m_value, vi.m_upper); } template bool simplex::at_lower(var_t v) const { var_info const& vi = m_vars[v]; return vi.m_lower_valid && em.eq(vi.m_value, vi.m_lower); } template bool simplex::at_upper(var_t v) const { var_info const& vi = m_vars[v]; return vi.m_upper_valid && em.eq(vi.m_value, vi.m_upper); } template bool simplex::make_var_feasible(var_t x_i) { scoped_numeral a_ij(m); scoped_eps_numeral value(em); bool is_below; if (below_lower(x_i)) { SASSERT(is_base(x_i)); is_below = m.is_pos(m_vars[x_i].m_base_coeff); value = m_vars[x_i].m_lower; } else if (above_upper(x_i)) { SASSERT(is_base(x_i)); is_below = m.is_neg(m_vars[x_i].m_base_coeff); value = m_vars[x_i].m_upper; } else { // x_i is already feasible return true; } var_t x_j = select_pivot(x_i, is_below, a_ij); if (x_j != null_var) { update_and_pivot(x_i, x_j, a_ij, value); } return x_j != null_var; } /** \brief Wrapper for select_pivot_blands and select_pivot_core */ template typename simplex::var_t simplex::select_pivot(var_t x_i, bool is_below, scoped_numeral& out_a_ij) { if (m_bland) { return select_pivot_blands(x_i, is_below, out_a_ij); } return select_pivot_core(x_i, is_below, out_a_ij); } /** \brief Select a variable x_j in the row r defining the base var x_i, s.t. x_j can be used to patch the error in x_i. Return null_theory_var if there is no x_j. Otherwise, return x_j and store its coefficient in out_a_ij. The argument is_below is true (false) if x_i is below its lower bound (above its upper bound). */ template typename simplex::var_t simplex::select_pivot_core(var_t x_i, bool is_below, scoped_numeral & out_a_ij) { SASSERT(is_base(x_i)); var_t max = get_num_vars(); var_t result = max; row r = row(m_vars[x_i].m_base2row); int n = 0; unsigned best_col_sz = UINT_MAX; int best_so_far = INT_MAX; row_iterator it = M.row_begin(r), end = M.row_end(r); for (; it != end; ++it) { var_t x_j = it->m_var; if (x_i == x_j) continue; numeral const & a_ij = it->m_coeff; bool is_neg = is_below ? m.is_neg(a_ij) : m.is_pos(a_ij); bool is_pos = !is_neg; bool can_pivot = ((is_pos && above_lower(x_j)) || (is_neg && below_upper(x_j))); if (can_pivot) { int num = get_num_non_free_dep_vars(x_j, best_so_far); unsigned col_sz = M.column_size(x_j); if (num < best_so_far || (num == best_so_far && col_sz < best_col_sz)) { result = x_j; out_a_ij = a_ij; best_so_far = num; best_col_sz = col_sz; n = 1; } else if (num == best_so_far && col_sz == best_col_sz) { n++; if (m_random()%n == 0) { result = x_j; out_a_ij = a_ij; } } } } return result < max ? result : null_var; } /** \brief Return the number of base variables that are non free and are v dependent. The function adds 1 to the result if v is non free. The function returns with a partial result r if r > best_so_far. This function is used to select the pivot variable. */ template int simplex::get_num_non_free_dep_vars(var_t x_j, int best_so_far) { int result = is_non_free(x_j); col_iterator it = M.col_begin(x_j), end = M.col_end(x_j); for (; it != end; ++it) { var_t s = m_row2base[it.get_row().id()]; result += is_non_free(s); if (result > best_so_far) return result; } return result; } /** \brief Using Bland's rule, select a variable x_j in the row r defining the base var x_i, s.t. x_j can be used to patch the error in x_i. Return null_var if there is no x_j. Otherwise, return x_j and store its coefficient in out_a_ij. */ template typename simplex::var_t simplex::select_pivot_blands(var_t x_i, bool is_below, scoped_numeral & out_a_ij) { SASSERT(is_base(x_i)); unsigned max = get_num_vars(); var_t result = max; row r(m_vars[x_i].m_base2row); row_iterator it = M.row_begin(r), end = M.row_end(r); for (; it != end; ++it) { var_t x_j = it->m_var; numeral const & a_ij = it->m_coeff; bool is_neg = is_below ? m.is_neg(a_ij) : m.is_pos(a_ij); if (x_i != x_j && ((!is_neg && above_lower(x_j)) || (is_neg && below_upper(x_j)))) { SASSERT(!is_base(x_j)); if (x_j < result) { result = x_j; out_a_ij = a_ij; } } } return result < max ? result : null_var; } template lbool simplex::minimize(var_t v) { // minimize v, such that tableau is feasible. // Assume there are no bounds on v. // Let k*v + c*x = 0 e.g, maximize c*x over // tableau constraints: // // max { c*x | A*x = 0 and l <= x <= u } // // start with feasible assignment // A*x0 = 0 and l <= x0 <= u // // Identify pivot: i, j: such that x_i is base, // there is a row k1*x_i + k2*x_j + R = 0 // and a delta such that: // // x_i' <- x_i + delta // x_j' <- x_j - delta*k1/k2 // l_i <= x_i' <= u_i // l_j <= x_j' <= u_j // and c*x' > c*x // e.g., c*x := c_i*x_i + c_j*x_j + ... // and c_i*delta > c_j*delta*k1/k2 // and x_i < u_i (if delta > 0), l_i < x_i (if delta < 0) // and l_j < x_j (if delta > 0), x_j < u_j (if delta < 0) // // update all rows, including c*x, using the pivot. // // If there is c_i*x_i in c*x such that c_i > 0 // and upper_i = oo and complementary lower_j = -oo // then the objective is unbounded. // // There is a singularity if there is a pivot such that // c_i*delta == c_j*delta*k1/k2, e.g., nothing is improved, // pivot, but use bland's rule to ensure // convergence in the limit. // SASSERT(is_feasible()); scoped_eps_numeral delta(em); scoped_numeral a_ij(m); var_t x_i, x_j; bool inc_x_i, inc_x_j; while (true) { if (!m_limit.inc()) { return l_undef; } select_pivot_primal(v, x_i, x_j, a_ij, inc_x_i, inc_x_j); if (x_j == null_var) { // optimal return l_true; } TRACE("simplex", tout << "x_i: v" << x_i << " x_j: v" << x_j << "\n";); var_info& vj = m_vars[x_j]; if (x_i == null_var) { if (inc_x_j && vj.m_upper_valid) { delta = vj.m_upper; delta -= vj.m_value; update_value(x_j, delta); } else if (!inc_x_j && vj.m_lower_valid) { delta = vj.m_lower; delta -= vj.m_value; update_value(x_j, delta); } else { // unbounded return l_false; } continue; } // TBD: Change the value of x_j directly without pivoting: // // if (!vj.is_fixed() && vj.bounded() && gain >= upper - lower) { // // } // pivot(x_i, x_j, a_ij); TRACE("simplex", display(tout << "after pivot\n");); move_to_bound(x_i, !inc_x_i); SASSERT(well_formed_row(row(m_vars[x_j].m_base2row))); TRACE("simplex", display(tout);); SASSERT(is_feasible()); } return l_true; } template void simplex::move_to_bound(var_t x, bool to_lower) { scoped_eps_numeral delta(em), delta2(em); var_info& vi = m_vars[x]; if (to_lower) { em.sub(vi.m_value, vi.m_lower, delta); } else { em.sub(vi.m_upper, vi.m_value, delta); } TRACE("simplex", tout << "move " << (to_lower?"to_lower":"to_upper") << " v" << x << " delta: " << em.to_string(delta) << "\n";); col_iterator it = M.col_begin(x), end = M.col_end(x); for (; it != end && is_pos(delta); ++it) { // // base_coeff*s + coeff*x + R = 0 // // to_lower coeff > 0 base_coeff > 0 bound(s) // ------------------------------------------------------ // T T T !to_lower // T T F to_lower // T F T to_lower // T F F !to_lower // var_t s = m_row2base[it.get_row().id()]; var_info& vs = m_vars[s]; numeral const& coeff = it.get_row_entry().m_coeff; numeral const& base_coeff = vs.m_base_coeff; SASSERT(!m.is_zero(coeff)); bool base_to_lower = (m.is_pos(coeff) != m.is_pos(base_coeff)) == to_lower; eps_numeral const* bound = nullptr; if (!base_to_lower && vs.m_upper_valid) { bound = &vs.m_upper; } else if (base_to_lower && vs.m_lower_valid) { bound = &vs.m_lower; } if (bound) { // |delta2*coeff| = |(bound-value)*base_coeff| em.sub(*bound, vs.m_value, delta2); em.mul(delta2, base_coeff, delta2); em.div(delta2, coeff, delta2); em.abs(delta2); TRACE("simplex", tout << "Delta for v" << s << " " << delta2 << "\n";); if (delta2 < delta) { delta = delta2; } } } if (to_lower) { delta.neg(); } update_value(x, delta); } /** \brief Arguments: v - base variable of row(v) to optimize x_i - base variable of row(x_i) to become non-base x_j - variable in row(v) to make a base variable a_ij - coefficient to x_j in row(x_i) inc - whether to increment x_i */ template void simplex::select_pivot_primal(var_t v, var_t& x_i, var_t& x_j, scoped_numeral& a_ij, bool& inc_x_i, bool& inc_x_j) { row r(m_vars[v].m_base2row); row_iterator it = M.row_begin(r), end = M.row_end(r); scoped_eps_numeral gain(em), new_gain(em); scoped_numeral new_a_ij(m); x_i = null_var; x_j = null_var; inc_x_i = false; bool inc_y = false; for (; it != end; ++it) { var_t x = it->m_var; if (x == v) continue; bool inc_x = m.is_pos(it->m_coeff) == m.is_pos(m_vars[v].m_base_coeff); if ((inc_x && at_upper(x)) || (!inc_x && at_lower(x))) { TRACE("simplex", tout << "v" << x << " pos: " << inc_x << " at upper: " << at_upper(x) << " at lower: " << at_lower(x) << "\n";); continue; // variable cannot be used for improving bounds. // TBD check? } var_t y = pick_var_to_leave(x, inc_x, new_gain, new_a_ij, inc_y); if (y == null_var) { // unbounded. x_i = y; x_j = x; inc_x_i = inc_y; inc_x_j = inc_x; a_ij = new_a_ij; break; } bool better = (new_gain > gain) || ((is_zero(new_gain) && is_zero(gain) && (x_i == null_var || y < x_i))); if (better) { TRACE("simplex", em.display(tout << "gain:", gain); em.display(tout << " new gain:", new_gain); tout << " base x_i: " << y << ", new base x_j: " << x << ", inc x_j: " << inc_x << "\n";); x_i = y; x_j = x; inc_x_i = inc_y; inc_x_j = inc_x; gain = new_gain; a_ij = new_a_ij; } } } // // y is a base variable. // v is a base variable. // v*a_v + x*a_x + E = 0 // y*b_y + x*b_x + F = 0 // inc(x) := sign(a_v) == sign(a_x) // sign_eq := sign(b_y) == sign(b_x) // sign_eq => (inc(x) != inc(y)) // !sign_eq => (inc(x) = inc(y)) // -> // inc(y) := sign_eq != inc(x) // template typename simplex::var_t simplex::pick_var_to_leave( var_t x_j, bool inc_x_j, scoped_eps_numeral& gain, scoped_numeral& new_a_ij, bool& inc_x_i) { var_t x_i = null_var; gain.reset(); scoped_eps_numeral curr_gain(em); col_iterator it = M.col_begin(x_j), end = M.col_end(x_j); for (; it != end; ++it) { row r = it.get_row(); var_t s = m_row2base[r.id()]; var_info& vi = m_vars[s]; numeral const& a_ij = it.get_row_entry().m_coeff; numeral const& a_ii = vi.m_base_coeff; bool sign_eq = (m.is_pos(a_ii) == m.is_pos(a_ij)); bool inc_s = sign_eq != inc_x_j; TRACE("simplex", tout << "x_j: v" << x_j << ", base x_i: v" << s << ", inc_x_i: " << inc_s << ", inc_x_j: " << inc_x_j << ", upper valid:" << vi.m_upper_valid << ", lower valid:" << vi.m_lower_valid << "\n"; display_row(tout, r);); if ((inc_s && !vi.m_upper_valid) || (!inc_s && !vi.m_lower_valid)) { continue; } // // current gain: (value(x_i)-bound)*a_ii/a_ij // curr_gain = vi.m_value; curr_gain -= inc_s?vi.m_upper:vi.m_lower; em.mul(curr_gain, a_ii, curr_gain); em.div(curr_gain, a_ij, curr_gain); if (is_neg(curr_gain)) { curr_gain.neg(); } if (x_i == null_var || (curr_gain < gain) || (is_zero(gain) && is_zero(curr_gain) && s < x_i)) { x_i = s; gain = curr_gain; new_a_ij = a_ij; inc_x_i = inc_s; TRACE("simplex", tout << "x_j v" << x_j << " x_i v" << x_i << " gain: "; tout << curr_gain << "\n";); } } return x_i; } template void simplex::check_blands_rule(var_t v, unsigned& num_repeated) { if (m_bland) return; if (m_left_basis.contains(v)) { num_repeated++; if (num_repeated > m_blands_rule_threshold) { TRACE("simplex", tout << "using blands rule, " << num_repeated << "\n";); // std::cerr << "BLANDS RULE...\n"; m_bland = true; } } else { m_left_basis.insert(v); } } template typename simplex::pivot_strategy_t simplex::pivot_strategy() { if (m_bland) { return S_BLAND; } return S_DEFAULT; } template typename simplex::var_t simplex::select_var_to_fix() { switch (pivot_strategy()) { case S_BLAND: return select_smallest_var(); case S_GREATEST_ERROR: return select_error_var(false); case S_LEAST_ERROR: return select_error_var(true); default: return select_smallest_var(); } } template typename simplex::var_t simplex::select_error_var(bool least) { var_t best = null_var; scoped_eps_numeral best_error(em); scoped_eps_numeral curr_error(em); typename var_heap::iterator it = m_to_patch.begin(); typename var_heap::iterator end = m_to_patch.end(); for (; it != end; ++it) { var_t v = *it; var_info const& vi = m_vars[v]; if (below_lower(v)) em.sub(vi.m_lower, vi.m_value, curr_error); else if (above_upper(v)) em.sub(vi.m_value, vi.m_lower, curr_error); else continue; SASSERT(is_pos(curr_error)); if ((best == null_var) || (!least && curr_error > best_error) || (least && curr_error < best_error)) { best = v; best_error = curr_error; } } if (best == null_var) m_to_patch.clear(); // all variables are satisfied else m_to_patch.erase(best); return best; } template bool simplex::well_formed() const { SASSERT(M.well_formed()); for (unsigned i = 0; i < m_row2base.size(); ++i) { var_t s = m_row2base[i]; if (s == null_var) continue; SASSERT(i == m_vars[s].m_base2row); VERIFY(well_formed_row(row(i))); } for (unsigned i = 0; i < m_vars.size(); ++i) { if (!is_base(i)) { SASSERT(!above_upper(i)); SASSERT(!below_lower(i)); } } return true; } template bool simplex::is_feasible() const { for (unsigned i = 0; i < m_vars.size(); ++i) { if (below_lower(i) || above_upper(i)) return false; } return true; } template bool simplex::well_formed_row(row const& r) const { var_t s = m_row2base[r.id()]; (void)s; SASSERT(m_vars[s].m_base2row == r.id()); SASSERT(m_vars[s].m_is_base); // SASSERT(m.is_neg(m_vars[s].m_base_coeff)); row_iterator it = M.row_begin(r), end = M.row_end(r); scoped_eps_numeral sum(em), tmp(em); for (; it != end; ++it) { em.mul(m_vars[it->m_var].m_value, it->m_coeff, tmp); sum += tmp; SASSERT(s != it->m_var || m.eq(m_vars[s].m_base_coeff, it->m_coeff)); } if (!em.is_zero(sum)) { IF_VERBOSE(0, M.display_row(verbose_stream(), r);); TRACE("pb", display(tout << "non-well formed row\n"); M.display_row(tout << "row: ", r);); throw default_exception("non-well formed row"); } return true; } template void simplex::collect_statistics(::statistics & st) const { M.collect_statistics(st); st.update("simplex num pivots", m_stats.m_num_pivots); st.update("simplex num infeasible", m_stats.m_num_infeasible); st.update("simplex num checks", m_stats.m_num_checks); } }; #endif z3-z3-4.8.7/src/math/simplex/sparse_matrix.h000066400000000000000000000226321356505360400206670ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: sparse_matrix.h Abstract: Author: Nikolaj Bjorner (nbjorner) 2014-01-15 Notes: --*/ #ifndef SPARSE_MATRIX_H_ #define SPARSE_MATRIX_H_ #include "util/mpq_inf.h" #include "util/statistics.h" namespace simplex { template class sparse_matrix { public: typedef typename Ext::numeral numeral; typedef typename Ext::scoped_numeral scoped_numeral; typedef typename Ext::manager manager; typedef unsigned var_t; struct row_entry { numeral m_coeff; var_t m_var; row_entry(numeral && c, var_t v) : m_coeff(std::move(c)), m_var(v) {} }; private: struct stats { unsigned m_add_rows; stats() { reset(); } void reset() { memset(this, 0, sizeof(*this)); } }; static const unsigned dead_id = UINT_MAX; /** \brief A row_entry is: m_var*m_coeff m_col_idx points to the place in the column where the variable occurs. */ struct _row_entry : public row_entry { union { int m_col_idx; int m_next_free_row_entry_idx; }; _row_entry(numeral && c, var_t v) : row_entry(std::move(c), v), m_col_idx(0) {} _row_entry() : row_entry(numeral(), dead_id), m_col_idx(0) {} bool is_dead() const { return row_entry::m_var == dead_id; } }; /** \brief A column entry points to the row and the row_entry within the row that has a non-zero coefficient on the variable associated with the column entry. */ struct col_entry { int m_row_id; union { int m_row_idx; int m_next_free_col_entry_idx; }; col_entry(int r, int i): m_row_id(r), m_row_idx(i) {} col_entry(): m_row_id(0), m_row_idx(0) {} bool is_dead() const { return (unsigned) m_row_id == dead_id; } }; struct column; /** \brief A row contains a base variable and set of row_entries. The base variable must occur in the set of row_entries with coefficient 1. */ struct _row { vector<_row_entry> m_entries; unsigned m_size; // the real size, m_entries contains dead row_entries. int m_first_free_idx; // first available position. _row(); unsigned size() const { return m_size; } unsigned num_entries() const { return m_entries.size(); } void reset(manager& m); _row_entry & add_row_entry(unsigned & pos_idx); void del_row_entry(unsigned idx); void compress(manager& m, vector & cols); void compress_if_needed(manager& _m, vector & cols); void save_var_pos(svector & result_map, unsigned_vector& idxs) const; //bool is_coeff_of(var_t v, numeral const & expected) const; int get_idx_of(var_t v) const; }; /** \brief A column stores in which rows a variable occurs. The column may have free/dead entries. The field m_first_free_idx is a reference to the first free/dead entry. */ struct column { svector m_entries; unsigned m_size; int m_first_free_idx; mutable unsigned m_refs; column():m_size(0), m_first_free_idx(-1), m_refs(0) {} unsigned size() const { return m_size; } unsigned num_entries() const { return m_entries.size(); } void reset(); void compress(vector<_row> & rows); void compress_if_needed(vector<_row> & rows); //void compress_singleton(vector<_row> & rows, unsigned singleton_pos); col_entry const * get_first_col_entry() const; col_entry & add_col_entry(int & pos_idx); void del_col_entry(unsigned idx); }; manager& m; vector<_row> m_rows; svector m_dead_rows; // rows to recycle vector m_columns; // per var svector m_var_pos; // temporary map from variables to positions in row unsigned_vector m_var_pos_idx; // indices in m_var_pos stats m_stats; bool well_formed_row(unsigned row_id) const; bool well_formed_column(unsigned column_id) const; void del_row_entry(_row& r, unsigned pos); public: sparse_matrix(manager& _m): m(_m) {} ~sparse_matrix(); void reset(); class row { unsigned m_id; public: explicit row(unsigned r):m_id(r) {} row():m_id(UINT_MAX) {} bool operator!=(row const& other) const { return m_id != other.m_id; } unsigned id() const { return m_id; } }; void ensure_var(var_t v); row mk_row(); void add_var(row r, numeral const& n, var_t var); void add(row r, numeral const& n, row src); void mul(row r, numeral const& n); void neg(row r); void del(row r); void gcd_normalize(row const& r, scoped_numeral& g); class row_iterator { friend class sparse_matrix; unsigned m_curr; _row & m_row; void move_to_used() { while (m_curr < m_row.num_entries() && m_row.m_entries[m_curr].is_dead()) { ++m_curr; } } row_iterator(_row & r, bool begin): m_curr(0), m_row(r) { if (begin) { move_to_used(); } else { m_curr = m_row.num_entries(); } } public: row_entry & operator*() const { return m_row.m_entries[m_curr]; } row_entry * operator->() const { return &(operator*()); } row_iterator & operator++() { ++m_curr; move_to_used(); return *this; } row_iterator operator++(int) { row_iterator tmp = *this; ++*this; return tmp; } bool operator==(row_iterator const & it) const { return m_curr == it.m_curr; } bool operator!=(row_iterator const & it) const { return m_curr != it.m_curr; } }; row_iterator row_begin(row const& r) { return row_iterator(m_rows[r.id()], true); } row_iterator row_end(row const& r) { return row_iterator(m_rows[r.id()], false); } unsigned column_size(var_t v) const { return m_columns[v].size(); } class col_iterator { friend class sparse_matrix; unsigned m_curr; column const& m_col; vector<_row> const& m_rows; void move_to_used() { while (m_curr < m_col.num_entries() && m_col.m_entries[m_curr].is_dead()) { ++m_curr; } } col_iterator(column const& c, vector<_row> const& r, bool begin): m_curr(0), m_col(c), m_rows(r) { ++m_col.m_refs; if (begin) { move_to_used(); } else { m_curr = m_col.num_entries(); } } public: ~col_iterator() { --m_col.m_refs; } row get_row() { return row(m_col.m_entries[m_curr].m_row_id); } row_entry const& get_row_entry() { col_entry const& c = m_col.m_entries[m_curr]; int row_id = c.m_row_id; return m_rows[row_id].m_entries[c.m_row_idx]; } col_iterator & operator++() { ++m_curr; move_to_used(); return *this; } col_iterator operator++(int) { col_iterator tmp = *this; ++*this; return tmp; } bool operator==(col_iterator const & it) const { return m_curr == it.m_curr; } bool operator!=(col_iterator const & it) const { return m_curr != it.m_curr; } }; col_iterator col_begin(int v) const { return col_iterator(m_columns[v], m_rows, true); } col_iterator col_end(int v) const { return col_iterator(m_columns[v], m_rows, false); } void display(std::ostream& out); void display_row(std::ostream& out, row const& r); bool well_formed() const; void collect_statistics(::statistics & st) const; }; struct mpz_ext { typedef mpz numeral; typedef scoped_mpz scoped_numeral; typedef unsynch_mpz_manager manager; typedef mpq_inf eps_numeral; typedef unsynch_mpq_inf_manager eps_manager; }; struct mpq_ext { typedef mpq numeral; typedef scoped_mpq scoped_numeral; typedef unsynch_mpq_manager manager; typedef mpq_inf eps_numeral; typedef unsynch_mpq_inf_manager eps_manager; }; }; #endif z3-z3-4.8.7/src/math/simplex/sparse_matrix_def.h000066400000000000000000000452741356505360400215140ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: sparse_matrix_def.h Abstract: Author: Nikolaj Bjorner (nbjorner) 2014-01-15 Notes: mainly hoisted from theory_arith.h and theory_arith_aux.h --*/ #ifndef SPARSE_MATRIX_DEF_H_ #define SPARSE_MATRIX_DEF_H_ #include "math/simplex/sparse_matrix.h" #include "util/uint_set.h" namespace simplex { // ----------------------------------- // // Rows // // ----------------------------------- template sparse_matrix::_row::_row(): m_size(0), m_first_free_idx(-1) { } template void sparse_matrix::_row::reset(manager& m) { for (unsigned i = 0; i < m_entries.size(); ++i) { m.reset(m_entries[i].m_coeff); } m_entries.reset(); m_size = 0; m_first_free_idx = -1; } /** \brief Add a new row_entry. The result is a reference to the new row_entry. The position of the new row_entry in the row is stored in pos_idx. */ template typename sparse_matrix::_row_entry & sparse_matrix::_row::add_row_entry(unsigned & pos_idx) { m_size++; if (m_first_free_idx == -1) { pos_idx = m_entries.size(); m_entries.push_back(_row_entry()); return m_entries.back(); } else { SASSERT(m_first_free_idx >= 0); pos_idx = static_cast(m_first_free_idx); _row_entry & result = m_entries[pos_idx]; SASSERT(result.is_dead()); m_first_free_idx = result.m_next_free_row_entry_idx; return result; } } /** \brief Delete row_entry at position idx. */ template void sparse_matrix::_row::del_row_entry(unsigned idx) { _row_entry & t = m_entries[idx]; SASSERT(!t.is_dead()); t.m_next_free_row_entry_idx = (unsigned)m_first_free_idx; t.m_var = dead_id; m_size--; m_first_free_idx = idx; SASSERT(t.is_dead()); } /** \brief Remove holes (i.e., dead entries) from the row. */ template void sparse_matrix::_row::compress(manager& m, vector & cols) { unsigned i = 0; unsigned j = 0; unsigned sz = m_entries.size(); for (; i < sz; i++) { _row_entry & t1 = m_entries[i]; if (!t1.is_dead()) { if (i != j) { _row_entry & t2 = m_entries[j]; t2.m_coeff.swap(t1.m_coeff); t2.m_var = t1.m_var; t2.m_col_idx = t1.m_col_idx; SASSERT(!t2.is_dead()); column & col = cols[t2.m_var]; col.m_entries[t2.m_col_idx].m_row_idx = j; } j++; } } SASSERT(j == m_size); // // alternative: update the free-list to point to the // tail and avoid shrinking. // if m.does not allocate memory (for wrapper around // double), also bypass this step. // for (unsigned i = m_size; i < m_entries.size(); ++i) { m.reset(m_entries[i].m_coeff); } m_entries.shrink(m_size); m_first_free_idx = -1; } template void sparse_matrix::_row::compress_if_needed(manager& m, vector & cols) { if (size() *2 < num_entries()) { compress(m, cols); } } /** \brief Fill the map var -> pos/idx */ template inline void sparse_matrix::_row::save_var_pos(svector & result_map, unsigned_vector& idxs) const { typename vector<_row_entry>::const_iterator it = m_entries.begin(); typename vector<_row_entry>::const_iterator end = m_entries.end(); unsigned idx = 0; for (; it != end; ++it, ++idx) { if (!it->is_dead()) { result_map[it->m_var] = idx; idxs.push_back(it->m_var); } } } template int sparse_matrix::_row::get_idx_of(var_t v) const { typename vector<_row_entry>::const_iterator it = m_entries.begin(); typename vector<_row_entry>::const_iterator end = m_entries.end(); for (unsigned idx = 0; it != end; ++it, ++idx) { if (!it->is_dead() && it->m_var == v) return idx; } return -1; } // ----------------------------------- // // Columns // // ----------------------------------- template void sparse_matrix::column::reset() { m_entries.reset(); m_size = 0; m_first_free_idx = -1; } /** \brief Remove holes (i.e., dead entries) from the column. */ template void sparse_matrix::column::compress(vector<_row> & rows) { unsigned i = 0; unsigned j = 0; unsigned sz = m_entries.size(); for (; i < sz; i++) { col_entry & e1 = m_entries[i]; if (!e1.is_dead()) { if (i != j) { m_entries[j] = e1; _row & r = rows[e1.m_row_id]; r.m_entries[e1.m_row_idx].m_col_idx = j; } j++; } } SASSERT(j == m_size); m_entries.shrink(m_size); m_first_free_idx = -1; } /** \brief Invoke compress if the column contains too many holes (i.e., dead entries). */ template inline void sparse_matrix::column::compress_if_needed(vector<_row> & rows) { if (size() * 2 < num_entries() && m_refs == 0) { compress(rows); } } #if 0 /** \brief Special version of compress, that is used when the column contain only one entry located at position singleton_pos. */ template void sparse_matrix::column::compress_singleton(vector<_row> & rows, unsigned singleton_pos) { SASSERT(m_size == 1); if (singleton_pos != 0) { col_entry & s = m_entries[singleton_pos]; m_entries[0] = s; row & r = rows[s.m_row_id]; r[s.m_row_idx].m_col_idx = 0; } m_first_free_idx = -1; m_entries.shrink(1); } #endif template const typename sparse_matrix::col_entry * sparse_matrix::column::get_first_col_entry() const { typename svector::const_iterator it = m_entries.begin(); typename svector::const_iterator end = m_entries.end(); for (; it != end; ++it) { if (!it->is_dead()) { return it; } } return nullptr; } template typename sparse_matrix::col_entry & sparse_matrix::column::add_col_entry(int & pos_idx) { m_size++; if (m_first_free_idx == -1) { pos_idx = m_entries.size(); m_entries.push_back(col_entry()); return m_entries.back(); } else { pos_idx = m_first_free_idx; col_entry & result = m_entries[pos_idx]; SASSERT(result.is_dead()); m_first_free_idx = result.m_next_free_col_entry_idx; return result; } } template void sparse_matrix::column::del_col_entry(unsigned idx) { col_entry & c = m_entries[idx]; SASSERT(!c.is_dead()); c.m_row_id = dead_id; c.m_next_free_col_entry_idx = m_first_free_idx; m_first_free_idx = idx; m_size--; } // ----------------------------------- // // Matrix // // ----------------------------------- template sparse_matrix::~sparse_matrix() { for (unsigned i = 0; i < m_rows.size(); ++i) { _row& r = m_rows[i]; for (unsigned j = 0; j < r.m_entries.size(); ++j) { m.reset(r.m_entries[j].m_coeff); } } } template void sparse_matrix::reset() { m_rows.reset(); m_dead_rows.reset(); m_columns.reset(); m_var_pos.reset(); m_var_pos_idx.reset(); } template void sparse_matrix::ensure_var(var_t v) { while (m_columns.size() <= v) { m_columns.push_back(column()); m_var_pos.push_back(-1); } } template typename sparse_matrix::row sparse_matrix::mk_row() { if (m_dead_rows.empty()) { row r(m_rows.size()); m_rows.push_back(_row()); return r; } else { row r(m_dead_rows.back()); m_dead_rows.pop_back(); return r; } } template void sparse_matrix::add_var(row dst, numeral const& n, var_t v) { _row& r = m_rows[dst.id()]; column& c = m_columns[v]; unsigned r_idx; int c_idx; _row_entry & r_entry = r.add_row_entry(r_idx); col_entry& c_entry = c.add_col_entry(c_idx); r_entry.m_var = v; m.set(r_entry.m_coeff, n); r_entry.m_col_idx = c_idx; c_entry.m_row_id = dst.id(); c_entry.m_row_idx = r_idx; } /** \brief Set row1 <- row1 + row2 * n */ template void sparse_matrix::add(row row1, numeral const& n, row row2) { m_stats.m_add_rows++; _row & r1 = m_rows[row1.id()]; r1.save_var_pos(m_var_pos, m_var_pos_idx); // // loop over variables in row2, // add terms in row2 to row1. // #define ADD_ROW(_SET_COEFF_, _ADD_COEFF_) \ row_iterator it = row_begin(row2); \ row_iterator end = row_end(row2); \ for (; it != end; ++it) { \ var_t v = it->m_var; \ int pos = m_var_pos[v]; \ if (pos == -1) { \ /* variable v is not in row1 */ \ unsigned row_idx; \ _row_entry & r_entry = r1.add_row_entry(row_idx); \ r_entry.m_var = v; \ m.set(r_entry.m_coeff, it->m_coeff); \ _SET_COEFF_; \ column & c = m_columns[v]; \ int col_idx; \ col_entry & c_entry = c.add_col_entry(col_idx); \ r_entry.m_col_idx = col_idx; \ c_entry.m_row_id = row1.id(); \ c_entry.m_row_idx = row_idx; \ } \ else { \ /* variable v is in row1 */ \ _row_entry & r_entry = r1.m_entries[pos]; \ SASSERT(r_entry.m_var == v); \ _ADD_COEFF_; \ if (m.is_zero(r_entry.m_coeff)) { \ del_row_entry(r1, pos); \ } \ } \ } \ ((void) 0) if (m.is_one(n)) { ADD_ROW({}, m.add(r_entry.m_coeff, it->m_coeff, r_entry.m_coeff)); } else if (m.is_minus_one(n)) { ADD_ROW(m.neg(r_entry.m_coeff), m.sub(r_entry.m_coeff, it->m_coeff, r_entry.m_coeff)); } else { scoped_numeral tmp(m); ADD_ROW(m.mul(r_entry.m_coeff, n, r_entry.m_coeff), m.mul(it->m_coeff, n, tmp); m.add(r_entry.m_coeff, tmp, r_entry.m_coeff)); } // reset m_var_pos: for (unsigned i = 0; i < m_var_pos_idx.size(); ++i) { m_var_pos[m_var_pos_idx[i]] = -1; } m_var_pos_idx.reset(); r1.compress_if_needed(m, m_columns); } template void sparse_matrix::del_row_entry(_row& r, unsigned pos) { _row_entry & r_entry = r.m_entries[pos]; var_t v = r_entry.m_var; int col_idx = r_entry.m_col_idx; r.del_row_entry(pos); column & c = m_columns[v]; c.del_col_entry(col_idx); c.compress_if_needed(m_rows); } /** \brief Set row <- -row */ template void sparse_matrix::neg(row r) { row_iterator it = row_begin(r); row_iterator end = row_end(r); for (; it != end; ++it) { m.neg(it->m_coeff); } } /** \brief Set row <- n*row */ template void sparse_matrix::mul(row r, numeral const& n) { SASSERT(!m.is_zero(n)); if (m.is_one(n)) { // no op } else if (m.is_minus_one(n)) { neg(r); } else { row_iterator it = row_begin(r); row_iterator end = row_end(r); for (; it != end; ++it) { m.mul(it->m_coeff, n, it->m_coeff); } } } /** \brief Delete row. */ template void sparse_matrix::del(row r) { _row& rw = m_rows[r.id()]; for (unsigned i = 0; i < rw.m_entries.size(); ++i) { _row_entry& e = rw.m_entries[i]; if (!e.is_dead()) { del_row_entry(rw, i); } } SASSERT(rw.m_size == 0); m_dead_rows.push_back(r.id()); } /** \brief normalize coefficients by dividing with they coefficients. return the gcd. */ template void sparse_matrix::gcd_normalize(row const& r, scoped_numeral& g) { g.reset(); row_iterator it = row_begin(r), end = row_end(r); for (; it != end && !m.is_one(g); ++it) { if (m.is_zero(g)) g = it->m_coeff; else m.gcd(g, it->m_coeff, g); } if (m.is_zero(g)) { g = numeral(1); } if (!m.is_one(g)) { row_iterator it2 = row_begin(r); for (; it2 != end; ++it2) { m.div(it2->m_coeff, g, it2->m_coeff); } } } /** \brief well_formed check */ template bool sparse_matrix::well_formed() const { for (unsigned i = 0; i < m_rows.size(); ++i) { well_formed_row(i); } for (unsigned i = 0; i < m_columns.size(); ++i) { well_formed_column(i); } return true; } /** \brief well_formed row check */ template bool sparse_matrix::well_formed_row(unsigned row_id) const { uint_set vars, dead; _row const& r = m_rows[row_id]; for (unsigned i = 0; i < r.num_entries(); ++i) { _row_entry const& e = r.m_entries[i]; if (e.is_dead()) { dead.insert(i); continue; } DEBUG_CODE( SASSERT(!vars.contains(e.m_var)); SASSERT(!m.is_zero(e.m_coeff)); SASSERT(e.m_var != dead_id); col_entry const& c = m_columns[e.m_var].m_entries[e.m_col_idx]; SASSERT((unsigned)c.m_row_id == row_id); SASSERT((unsigned)c.m_row_idx == i);); vars.insert(e.m_var); } int idx = r.m_first_free_idx; while (idx != -1) { SASSERT(dead.contains(idx)); dead.remove(idx); idx = r.m_entries[idx].m_next_free_row_entry_idx; } SASSERT(dead.empty()); return true; } /** \brief well_formed column check */ template bool sparse_matrix::well_formed_column(var_t v) const { uint_set rows, dead; column const& col = m_columns[v]; for (unsigned i = 0; i < col.num_entries(); ++i) { col_entry const& c = col.m_entries[i]; if (c.is_dead()) { dead.insert(i); continue; } SASSERT(!rows.contains(c.m_row_id)); DEBUG_CODE( _row const& row = m_rows[c.m_row_id]; _row_entry const& r = row.m_entries[c.m_row_idx]; SASSERT(r.m_var == v); SASSERT((unsigned)r.m_col_idx == i);); rows.insert(c.m_row_id); } int idx = col.m_first_free_idx; while (idx != -1) { SASSERT(dead.contains(idx)); dead.remove(idx); idx = col.m_entries[idx].m_next_free_col_entry_idx; } SASSERT(dead.empty()); return true; } /** \brief statistics */ template void sparse_matrix::collect_statistics(::statistics & st) const { st.update("simplex add rows", m_stats.m_add_rows); } /** \brief display method */ template void sparse_matrix::display(std::ostream& out) { for (unsigned i = 0; i < m_rows.size(); ++i) { if (m_rows[i].size() == 0) continue; display_row(out, row(i)); } } template void sparse_matrix::display_row(std::ostream& out, row const& r) { row_iterator it = row_begin(r), end = row_end(r); for (; it != end; ++it) { m.display(out, it->m_coeff); out << "*v" << it->m_var << " "; } out << "\n"; } }; #endif z3-z3-4.8.7/src/math/subpaving/000077500000000000000000000000001356505360400161455ustar00rootroot00000000000000z3-z3-4.8.7/src/math/subpaving/CMakeLists.txt000066400000000000000000000003171356505360400207060ustar00rootroot00000000000000z3_add_component(subpaving SOURCES subpaving.cpp subpaving_hwf.cpp subpaving_mpf.cpp subpaving_mpff.cpp subpaving_mpfx.cpp subpaving_mpq.cpp COMPONENT_DEPENDENCIES interval ) z3-z3-4.8.7/src/math/subpaving/subpaving.cpp000066400000000000000000000261441356505360400206560ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: subpaving.cpp Abstract: Subpaving for non-linear arithmetic. This is a wrapper for the different implementations of the subpaving module. This wrapper is the main interface between Z3 other modules and subpaving. Thus, it assumes that polynomials have precise integer coefficients, and bounds are rationals. If a particular implementation uses floats, then internally the bounds are approximated. Author: Leonardo de Moura (leonardo) 2012-08-07. Revision History: --*/ #include "math/subpaving/subpaving.h" #include "math/subpaving/subpaving_types.h" #include "math/subpaving/subpaving_mpq.h" #include "math/subpaving/subpaving_mpf.h" #include "math/subpaving/subpaving_hwf.h" #include "math/subpaving/subpaving_mpff.h" #include "math/subpaving/subpaving_mpfx.h" namespace subpaving { template class context_wrapper : public context { protected: CTX m_ctx; public: context_wrapper(reslimit& lim, typename CTX::numeral_manager & m, params_ref const & p, small_object_allocator * a):m_ctx(lim, m, p, a) {} ~context_wrapper() override {} unsigned num_vars() const override { return m_ctx.num_vars(); } var mk_var(bool is_int) override { return m_ctx.mk_var(is_int); } bool is_int(var x) const override { return m_ctx.is_int(x); } var mk_monomial(unsigned sz, power const * pws) override { return m_ctx.mk_monomial(sz, pws); } void inc_ref(ineq * a) override { m_ctx.inc_ref(reinterpret_cast(a)); } void dec_ref(ineq * a) override { m_ctx.dec_ref(reinterpret_cast(a)); } void add_clause(unsigned sz, ineq * const * atoms) override { m_ctx.add_clause(sz, reinterpret_cast(atoms)); } void display_constraints(std::ostream & out, bool use_star) const override { m_ctx.display_constraints(out, use_star); } void set_display_proc(display_var_proc * p) override { m_ctx.set_display_proc(p); } void reset_statistics() override { m_ctx.reset_statistics(); } void collect_statistics(statistics & st) const override { m_ctx.collect_statistics(st); } void collect_param_descrs(param_descrs & r) override { m_ctx.collect_param_descrs(r); } void updt_params(params_ref const & p) override { m_ctx.updt_params(p); } void operator()() override { m_ctx(); } void display_bounds(std::ostream & out) const override { m_ctx.display_bounds(out); } }; class context_mpq_wrapper : public context_wrapper { scoped_mpq m_c; scoped_mpq_vector m_as; public: context_mpq_wrapper(reslimit& lim, unsynch_mpq_manager & m, params_ref const & p, small_object_allocator * a): context_wrapper(lim, m, p, a), m_c(m), m_as(m) {} ~context_mpq_wrapper() override {} unsynch_mpq_manager & qm() const override { return m_ctx.nm(); } var mk_sum(mpz const & c, unsigned sz, mpz const * as, var const * xs) override { m_as.reserve(sz); for (unsigned i = 0; i < sz; i++) { m_ctx.nm().set(m_as[i], as[i]); } m_ctx.nm().set(m_c, c); return m_ctx.mk_sum(m_c, sz, m_as.c_ptr(), xs); } ineq * mk_ineq(var x, mpq const & k, bool lower, bool open) override { return reinterpret_cast(m_ctx.mk_ineq(x, k, lower, open)); } }; class context_mpf_wrapper : public context_wrapper { unsynch_mpq_manager & m_qm; scoped_mpf m_c; scoped_mpf_vector m_as; scoped_mpq m_q1, m_q2; // Convert the mpz (integer) into a mpf, and throws an exception if the conversion is not precise. void int2mpf(mpz const & a, mpf & o) { m_qm.set(m_q1, a); m_ctx.nm().set(o, m_q1); m_ctx.nm().m().to_rational(o, m_q2); if (!m_qm.eq(m_q1, m_q2)) throw subpaving::exception(); } public: context_mpf_wrapper(reslimit& lim, f2n & fm, params_ref const & p, small_object_allocator * a): context_wrapper(lim, fm, p, a), m_qm(fm.m().mpq_manager()), m_c(fm.m()), m_as(fm.m()), m_q1(m_qm), m_q2(m_qm) { } ~context_mpf_wrapper() override {} unsynch_mpq_manager & qm() const override { return m_qm; } var mk_sum(mpz const & c, unsigned sz, mpz const * as, var const * xs) override { try { m_as.reserve(sz); for (unsigned i = 0; i < sz; i++) { int2mpf(as[i], m_as[i]); } int2mpf(c, m_c); return m_ctx.mk_sum(m_c, sz, m_as.c_ptr(), xs); } catch (const f2n::exception &) { throw subpaving::exception(); } } ineq * mk_ineq(var x, mpq const & k, bool lower, bool open) override { try { f2n & m = m_ctx.nm(); if (lower) m.round_to_minus_inf(); else m.round_to_plus_inf(); m.set(m_c, k); return reinterpret_cast(m_ctx.mk_ineq(x, m_c, lower, open)); } catch (const f2n::exception &) { throw subpaving::exception(); } } }; class context_hwf_wrapper : public context_wrapper { unsynch_mpq_manager & m_qm; hwf m_c; svector m_as; // Convert the mpz (integer) into a hwf, and throws an exception if the conversion is not precise. void int2hwf(mpz const & a, hwf & o) { if (!m_qm.is_int64(a)) throw subpaving::exception(); int64_t val = m_qm.get_int64(a); double dval = static_cast(val); m_ctx.nm().set(o, dval); double _dval = m_ctx.nm().m().to_double(o); // TODO check the following test if (static_cast(_dval) != val) throw subpaving::exception(); } public: context_hwf_wrapper(reslimit& lim,f2n & fm, unsynch_mpq_manager & qm, params_ref const & p, small_object_allocator * a): context_wrapper(lim, fm, p, a), m_qm(qm) { } ~context_hwf_wrapper() override {} unsynch_mpq_manager & qm() const override { return m_qm; } var mk_sum(mpz const & c, unsigned sz, mpz const * as, var const * xs) override { try { m_as.reserve(sz); for (unsigned i = 0; i < sz; i++) { int2hwf(as[i], m_as[i]); } int2hwf(c, m_c); return m_ctx.mk_sum(m_c, sz, m_as.c_ptr(), xs); } catch (const f2n::exception &) { throw subpaving::exception(); } } ineq * mk_ineq(var x, mpq const & k, bool lower, bool open) override { try { f2n & m = m_ctx.nm(); if (lower) m.round_to_minus_inf(); else m.round_to_plus_inf(); m.set(m_c, k); return reinterpret_cast(m_ctx.mk_ineq(x, m_c, lower, open)); } catch (const f2n::exception &) { throw subpaving::exception(); } } }; template class context_fpoint_wrapper : public context_wrapper { unsynch_mpq_manager & m_qm; _scoped_numeral m_c; _scoped_numeral_vector m_as; scoped_mpz m_z1, m_z2; void int2fpoint(mpz const & a, typename context_fpoint::numeral & o) { m_qm.set(m_z1, a); this->m_ctx.nm().set(o, m_qm, m_z1); this->m_ctx.nm().to_mpz(o, m_qm, m_z2); if (!m_qm.eq(m_z1, m_z2)) throw subpaving::exception(); } public: context_fpoint_wrapper(reslimit& lim, typename context_fpoint::numeral_manager & m, unsynch_mpq_manager & qm, params_ref const & p, small_object_allocator * a): context_wrapper(lim, m, p, a), m_qm(qm), m_c(m), m_as(m), m_z1(m_qm), m_z2(m_qm) { } ~context_fpoint_wrapper() override {} unsynch_mpq_manager & qm() const override { return m_qm; } var mk_sum(mpz const & c, unsigned sz, mpz const * as, var const * xs) override { try { m_as.reserve(sz); for (unsigned i = 0; i < sz; i++) { int2fpoint(as[i], m_as[i]); } int2fpoint(c, m_c); return this->m_ctx.mk_sum(m_c, sz, m_as.c_ptr(), xs); } catch (const typename context_fpoint::numeral_manager::exception &) { throw subpaving::exception(); } } ineq * mk_ineq(var x, mpq const & k, bool lower, bool open) override { try { typename context_fpoint::numeral_manager & m = this->m_ctx.nm(); if (lower) m.round_to_minus_inf(); else m.round_to_plus_inf(); m.set(m_c, m_qm, k); return reinterpret_cast(this->m_ctx.mk_ineq(x, m_c, lower, open)); } catch (const typename context_fpoint::numeral_manager::exception &) { throw subpaving::exception(); } } }; typedef context_fpoint_wrapper context_mpff_wrapper; typedef context_fpoint_wrapper context_mpfx_wrapper; context * mk_mpq_context(reslimit& lim, unsynch_mpq_manager & m, params_ref const & p, small_object_allocator * a) { return alloc(context_mpq_wrapper, lim, m, p, a); } context * mk_mpf_context(reslimit& lim, f2n & m, params_ref const & p, small_object_allocator * a) { return alloc(context_mpf_wrapper, lim, m, p, a); } context * mk_hwf_context(reslimit& lim, f2n & m, unsynch_mpq_manager & qm, params_ref const & p, small_object_allocator * a) { return alloc(context_hwf_wrapper, lim, m, qm, p, a); } context * mk_mpff_context(reslimit& lim, mpff_manager & m, unsynch_mpq_manager & qm, params_ref const & p, small_object_allocator * a) { return alloc(context_mpff_wrapper, lim, m, qm, p, a); } context * mk_mpfx_context(reslimit& lim, mpfx_manager & m, unsynch_mpq_manager & qm, params_ref const & p, small_object_allocator * a) { return alloc(context_mpfx_wrapper, lim, m, qm, p, a); } }; z3-z3-4.8.7/src/math/subpaving/subpaving.h000066400000000000000000000071651356505360400203250ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: subpaving.h Abstract: Subpaving for non-linear arithmetic. This is a wrapper for the different implementations of the subpaving module. This wrapper is the main interface between Z3 other modules and subpaving. Thus, it assumes that polynomials have precise integer coefficients, and bounds are rationals. If a particular implementation uses floats, then internally the bounds are approximated. Author: Leonardo de Moura (leonardo) 2012-08-07. Revision History: --*/ #ifndef SUBPAVING_H_ #define SUBPAVING_H_ #include "util/mpq.h" #include "math/subpaving/subpaving_types.h" #include "util/params.h" #include "util/statistics.h" template class f2n; class mpf_manager; class hwf_manager; class mpff_manager; class mpfx_manager; namespace subpaving { class context { public: virtual ~context() {} virtual unsynch_mpq_manager & qm() const = 0; /** \brief Return the number of variables in this subpaving object. */ virtual unsigned num_vars() const = 0; /** \brief Create a new variable. */ virtual var mk_var(bool is_int) = 0; /** \brief Return true if \c x is an integer variable. */ virtual bool is_int(var x) const = 0; /** \brief Create the monomial xs[0]^ks[0] * ... * xs[sz-1]^ks[sz-1]. The result is a variable y s.t. y = xs[0]^ks[0] * ... * xs[sz-1]^ks[sz-1]. \pre for all i \in [0, sz-1] : ks[i] > 0 \pre sz > 0 */ virtual var mk_monomial(unsigned sz, power const * pws) = 0; /** \brief Create the sum c + as[0]*xs[0] + ... + as[sz-1]*xs[sz-1]. The result is a variable y s.t. y = c + as[0]*xs[0] + ... + as[sz-1]*xs[sz-1]. \pre sz > 0 \pre for all i \in [0, sz-1] : as[i] != 0 */ virtual var mk_sum(mpz const & c, unsigned sz, mpz const * as, var const * xs) = 0; /** \brief Create an inequality. */ virtual ineq * mk_ineq(var x, mpq const & k, bool lower, bool open) = 0; virtual void inc_ref(ineq * a) = 0; virtual void dec_ref(ineq * a) = 0; /** \brief Assert the clause atoms[0] \/ ... \/ atoms[sz-1] \pre sz >= 1 */ virtual void add_clause(unsigned sz, ineq * const * atoms) = 0; /** \brief Display constraints asserted in the subpaving. */ virtual void display_constraints(std::ostream & out, bool use_star = false) const = 0; virtual void collect_param_descrs(param_descrs & r) = 0; virtual void updt_params(params_ref const & p) = 0; virtual void set_display_proc(display_var_proc * p) = 0; virtual void reset_statistics() = 0; virtual void collect_statistics(statistics & st) const = 0; virtual void operator()() = 0; virtual void display_bounds(std::ostream & out) const = 0; }; context * mk_mpq_context(reslimit& lim, unsynch_mpq_manager & m, params_ref const & p = params_ref(), small_object_allocator * a = nullptr); context * mk_mpf_context(reslimit& lim, f2n & m, params_ref const & p = params_ref(), small_object_allocator * a = nullptr); context * mk_hwf_context(reslimit& lim, f2n & m, unsynch_mpq_manager & qm, params_ref const & p = params_ref(), small_object_allocator * a = nullptr); context * mk_mpff_context(reslimit& lim, mpff_manager & m, unsynch_mpq_manager & qm, params_ref const & p = params_ref(), small_object_allocator * a = nullptr); context * mk_mpfx_context(reslimit& lim, mpfx_manager & m, unsynch_mpq_manager & qm, params_ref const & p = params_ref(), small_object_allocator * a = nullptr); }; #endif z3-z3-4.8.7/src/math/subpaving/subpaving_hwf.cpp000066400000000000000000000006411356505360400215140ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: subpaving_hwf.cpp Abstract: Subpaving for non-linear arithmetic using hardware floats. Author: Leonardo de Moura (leonardo) 2012-08-06. Revision History: --*/ #include "math/subpaving/subpaving_hwf.h" #include "math/subpaving/subpaving_t_def.h" // force template instantiation template class subpaving::context_t; z3-z3-4.8.7/src/math/subpaving/subpaving_hwf.h000066400000000000000000000022641356505360400211640ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: subpaving_hwf.h Abstract: Subpaving for non-linear arithmetic using hardware floats. Author: Leonardo de Moura (leonardo) 2012-08-06. Revision History: --*/ #ifndef SUBPAVING_HWF_H_ #define SUBPAVING_HWF_H_ #include "math/subpaving/subpaving_t.h" #include "util/f2n.h" #include "util/hwf.h" namespace subpaving { struct config_hwf { f2n & m_manager; public: typedef f2n numeral_manager; typedef f2n::exception exception; static void round_to_minus_inf(numeral_manager & m) { m.round_to_minus_inf(); } static void round_to_plus_inf(numeral_manager & m) { m.round_to_plus_inf(); } static void set_rounding(numeral_manager & m, bool to_plus_inf) { m.set_rounding(to_plus_inf); } config_hwf(f2n & m):m_manager(m) {} f2n & m() const { return const_cast &>(m_manager); } }; class context_hwf : public context_t { public: context_hwf(reslimit& lim, f2n & m, params_ref const & p, small_object_allocator * a):context_t(lim, config_hwf(m), p, a) {} }; }; #endif z3-z3-4.8.7/src/math/subpaving/subpaving_mpf.cpp000066400000000000000000000006501356505360400215120ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: subpaving_mpf.cpp Abstract: Subpaving for non-linear arithmetic using multi-precision floats. Author: Leonardo de Moura (leonardo) 2012-07-31. Revision History: --*/ #include "math/subpaving/subpaving_mpf.h" #include "math/subpaving/subpaving_t_def.h" // force template instantiation template class subpaving::context_t; z3-z3-4.8.7/src/math/subpaving/subpaving_mpf.h000066400000000000000000000023541356505360400211620ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: subpaving_mpf.h Abstract: Subpaving for non-linear arithmetic using multi-precision floats. Author: Leonardo de Moura (leonardo) 2012-07-31. Revision History: --*/ #ifndef SUBPAVING_MPF_H_ #define SUBPAVING_MPF_H_ #include "math/subpaving/subpaving_t.h" #include "util/mpf.h" #include "util/f2n.h" namespace subpaving { struct config_mpf { f2n & m_manager; public: typedef f2n numeral_manager; typedef mpf numeral; typedef f2n::exception exception; static void round_to_minus_inf(numeral_manager & m) { m.round_to_minus_inf(); } static void round_to_plus_inf(numeral_manager & m) { m.round_to_plus_inf(); } static void set_rounding(numeral_manager & m, bool to_plus_inf) { m.set_rounding(to_plus_inf); } config_mpf(f2n & m):m_manager(m) {} f2n & m() const { return const_cast &>(m_manager); } }; class context_mpf : public context_t { public: context_mpf(reslimit& lim, f2n & m, params_ref const & p, small_object_allocator * a):context_t(lim, config_mpf(m), p, a) {} }; }; #endif z3-z3-4.8.7/src/math/subpaving/subpaving_mpff.cpp000066400000000000000000000006421356505360400216610ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: subpaving_mpff.cpp Abstract: Subpaving for non-linear arithmetic using mpff numerals. Author: Leonardo de Moura (leonardo) 2012-09-18. Revision History: --*/ #include "math/subpaving/subpaving_mpff.h" #include "math/subpaving/subpaving_t_def.h" // force template instantiation template class subpaving::context_t; z3-z3-4.8.7/src/math/subpaving/subpaving_mpff.h000066400000000000000000000017061356505360400213300ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: subpaving_mpff.h Abstract: Subpaving for non-linear arithmetic using mpff numerals Author: Leonardo de Moura (leonardo) 2012-09-18. Revision History: --*/ #ifndef SUBPAVING_MPFF_H_ #define SUBPAVING_MPFF_H_ #include "math/subpaving/subpaving_t.h" #include "util/mpff.h" namespace subpaving { struct config_mpff { typedef mpff_manager numeral_manager; typedef mpff_manager::exception exception; static void round_to_minus_inf(numeral_manager & m) { m.round_to_minus_inf(); } static void round_to_plus_inf(numeral_manager & m) { m.round_to_plus_inf(); } static void set_rounding(numeral_manager & m, bool to_plus_inf) { m.set_rounding(to_plus_inf); } numeral_manager & m_manager; config_mpff(numeral_manager & m):m_manager(m) {} numeral_manager & m() const { return m_manager; } }; typedef context_t context_mpff; }; #endif z3-z3-4.8.7/src/math/subpaving/subpaving_mpfx.cpp000066400000000000000000000006421356505360400217030ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: subpaving_mpfx.cpp Abstract: Subpaving for non-linear arithmetic using mpfx numerals. Author: Leonardo de Moura (leonardo) 2012-09-18. Revision History: --*/ #include "math/subpaving/subpaving_mpfx.h" #include "math/subpaving/subpaving_t_def.h" // force template instantiation template class subpaving::context_t; z3-z3-4.8.7/src/math/subpaving/subpaving_mpfx.h000066400000000000000000000017061356505360400213520ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: subpaving_mpfx.h Abstract: Subpaving for non-linear arithmetic using mpfx numerals Author: Leonardo de Moura (leonardo) 2012-09-20. Revision History: --*/ #ifndef SUBPAVING_MPFX_H_ #define SUBPAVING_MPFX_H_ #include "math/subpaving/subpaving_t.h" #include "util/mpfx.h" namespace subpaving { struct config_mpfx { typedef mpfx_manager numeral_manager; typedef mpfx_manager::exception exception; static void round_to_minus_inf(numeral_manager & m) { m.round_to_minus_inf(); } static void round_to_plus_inf(numeral_manager & m) { m.round_to_plus_inf(); } static void set_rounding(numeral_manager & m, bool to_plus_inf) { m.set_rounding(to_plus_inf); } numeral_manager & m_manager; config_mpfx(numeral_manager & m):m_manager(m) {} numeral_manager & m() const { return m_manager; } }; typedef context_t context_mpfx; }; #endif z3-z3-4.8.7/src/math/subpaving/subpaving_mpq.cpp000066400000000000000000000006331356505360400215260ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: subpaving_mpq.cpp Abstract: Subpaving for non-linear arithmetic using rationals. Author: Leonardo de Moura (leonardo) 2012-07-31. Revision History: --*/ #include "math/subpaving/subpaving_mpq.h" #include "math/subpaving/subpaving_t_def.h" // force template instantiation template class subpaving::context_t; z3-z3-4.8.7/src/math/subpaving/subpaving_mpq.h000066400000000000000000000015201356505360400211670ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: subpaving_mpq.h Abstract: Subpaving for non-linear arithmetic using rationals Author: Leonardo de Moura (leonardo) 2012-07-31. Revision History: --*/ #ifndef SUBPAVING_MPQ_H_ #define SUBPAVING_MPQ_H_ #include "math/subpaving/subpaving_t.h" #include "util/mpq.h" namespace subpaving { struct config_mpq { typedef unsynch_mpq_manager numeral_manager; struct exception {}; static void round_to_minus_inf(numeral_manager & m) {} static void round_to_plus_inf(numeral_manager & m) {} static void set_rounding(numeral_manager & m, bool to_plus_info) {} numeral_manager & m_manager; config_mpq(numeral_manager & m):m_manager(m) {} numeral_manager & m() const { return m_manager; } }; typedef context_t context_mpq; }; #endif z3-z3-4.8.7/src/math/subpaving/subpaving_t.h000066400000000000000000000722571356505360400206540ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: subpaving_t.h Abstract: Subpaving template for non-linear arithmetic. Author: Leonardo de Moura (leonardo) 2012-07-31. Revision History: --*/ #ifndef SUBPAVING_T_H_ #define SUBPAVING_T_H_ #include #include "util/tptr.h" #include "util/small_object_allocator.h" #include "util/chashtable.h" #include "util/parray.h" #include "math/interval/interval.h" #include "util/scoped_numeral_vector.h" #include "math/subpaving/subpaving_types.h" #include "util/params.h" #include "util/statistics.h" #include "util/lbool.h" #include "util/id_gen.h" #include "util/rlimit.h" #ifdef _MSC_VER #pragma warning(disable : 4200) #pragma warning(disable : 4355) #endif namespace subpaving { template class context_t { public: typedef typename C::numeral_manager numeral_manager; typedef typename numeral_manager::numeral numeral; /** \brief Inequalities used to encode a problem. */ class ineq { friend class context_t; var m_x; numeral m_val; unsigned m_ref_count:30; unsigned m_lower:1; unsigned m_open:1; public: var x() const { return m_x; } numeral const & value() const { return m_val; } bool is_lower() const { return m_lower; } bool is_open() const { return m_open; } void display(std::ostream & out, numeral_manager & nm, display_var_proc const & proc = display_var_proc()); struct lt_var_proc { bool operator()(ineq const * a, ineq const * b) const { return a->m_x < b->m_x; } }; }; class node; class constraint { public: enum kind { CLAUSE, MONOMIAL, POLYNOMIAL // TODO: add SIN, COS, TAN, ... }; protected: kind m_kind; uint64_t m_timestamp; public: constraint(kind k):m_kind(k), m_timestamp(0) {} kind get_kind() const { return m_kind; } // Return the timestamp of the last propagation visit uint64_t timestamp() const { return m_timestamp; } // Reset propagation visit time void set_visited(uint64_t ts) { m_timestamp = ts; } }; /** \brief Clauses in the problem description and lemmas learned during paving. */ class clause : public constraint { friend class context_t; unsigned m_size; //!< Number of atoms in the clause. unsigned m_lemma:1; //!< True if it is a learned clause. unsigned m_watched:1; //!< True if it we are watching this clause. All non-lemmas are watched. unsigned m_num_jst:30; //!< Number of times it is used to justify some bound. ineq * m_atoms[0]; static unsigned get_obj_size(unsigned sz) { return sizeof(clause) + sz*sizeof(ineq*); } public: clause():constraint(constraint::CLAUSE) {} unsigned size() const { return m_size; } bool watched() const { return m_watched; } ineq * operator[](unsigned i) const { SASSERT(i < size()); return m_atoms[i]; } void display(std::ostream & out, numeral_manager & nm, display_var_proc const & proc = display_var_proc()); }; class justification { void * m_data; public: enum kind { AXIOM = 0, ASSUMPTION, CLAUSE, VAR_DEF }; justification(bool axiom = true) { m_data = axiom ? reinterpret_cast(static_cast(AXIOM)) : reinterpret_cast(static_cast(ASSUMPTION)); } justification(justification const & source) { m_data = source.m_data; } explicit justification(clause * c) { m_data = TAG(void*, c, CLAUSE); } explicit justification(var x) { m_data = BOXTAGINT(void*, x, VAR_DEF); } kind get_kind() const { return static_cast(GET_TAG(m_data)); } bool is_clause() const { return get_kind() == CLAUSE; } bool is_axiom() const { return get_kind() == AXIOM; } bool is_assumption() const { return get_kind() == ASSUMPTION; } bool is_var_def() const { return get_kind() == VAR_DEF; } clause * get_clause() const { SASSERT(is_clause()); return UNTAG(clause*, m_data); } var get_var() const { SASSERT(is_var_def()); return UNBOXINT(m_data); } bool operator==(justification const & other) const { return m_data == other.m_data; } bool operator!=(justification const & other) const { return !operator==(other); } }; class bound { friend class context_t; numeral m_val; unsigned m_x:29; unsigned m_lower:1; unsigned m_open:1; unsigned m_mark:1; uint64_t m_timestamp; bound * m_prev; justification m_jst; void set_timestamp(uint64_t ts) { m_timestamp = ts; } public: var x() const { return static_cast(m_x); } numeral const & value() const { return m_val; } numeral & value() { return m_val; } bool is_lower() const { return m_lower; } bool is_open() const { return m_open; } uint64_t timestamp() const { return m_timestamp; } bound * prev() const { return m_prev; } justification jst() const { return m_jst; } void display(std::ostream & out, numeral_manager & nm, display_var_proc const & proc = display_var_proc()); }; struct bound_array_config { typedef context_t value_manager; typedef small_object_allocator allocator; typedef bound * value; static const bool ref_count = false; static const bool preserve_roots = true; static const unsigned max_trail_sz = 16; static const unsigned factor = 2; }; // auxiliary declarations for parray_manager void dec_ref(bound *) {} void inc_ref(bound *) {} typedef parray_manager bound_array_manager; typedef typename bound_array_manager::ref bound_array; /** \brief Node in the context_t. */ class node { bound_array_manager & m_bm; bound_array m_lowers; bound_array m_uppers; var m_conflict; unsigned m_id; unsigned m_depth; bound * m_trail; node * m_parent; //!< parent node node * m_first_child; node * m_next_sibling; // Doubly linked list of leaves to be processed node * m_prev; node * m_next; public: node(context_t & s, unsigned id); node(node * parent, unsigned id); // return unique identifier. unsigned id() const { return m_id; } bound_array_manager & bm() const { return m_bm; } bound_array & lowers() { return m_lowers; } bound_array & uppers() { return m_uppers; } bool inconsistent() const { return m_conflict != null_var; } void set_conflict(var x) { SASSERT(!inconsistent()); m_conflict = x; } bound * trail_stack() const { return m_trail; } bound * parent_trail_stack() const { return m_parent == nullptr ? nullptr : m_parent->m_trail; } bound * lower(var x) const { return bm().get(m_lowers, x); } bound * upper(var x) const { return bm().get(m_uppers, x); } node * parent() const { return m_parent; } node * first_child() const { return m_first_child; } node * next_sibling() const { return m_next_sibling; } node * prev() const { return m_prev; } node * next() const { return m_next; } /** \brief Return true if x is unbounded in this node */ bool is_unbounded(var x) const { return lower(x) == nullptr && upper(x) == nullptr; } void push(bound * b); void set_first_child(node * n) { m_first_child = n; } void set_next_sibling(node * n) { m_next_sibling = n; } void set_next(node * n) { m_next = n; } void set_prev(node * n) { m_prev = n; } unsigned depth() const { return m_depth; } }; /** \brief Intervals are just temporary place holders. The pavers maintain bounds. */ struct interval { bool m_constant; // Flag: constant intervals are pairs // constant intervals node * m_node; var m_x; // mutable intervals numeral m_l_val; bool m_l_inf; bool m_l_open; numeral m_u_val; bool m_u_inf; bool m_u_open; interval():m_constant(false) {} void set_constant(node * n, var x) { m_constant = true; m_node = n; m_x = x; } void set_mutable() { m_constant = false; } }; class interval_config { public: typedef typename C::numeral_manager numeral_manager; typedef typename numeral_manager::numeral numeral; typedef typename context_t::interval interval; private: numeral_manager & m_manager; public: interval_config(numeral_manager & m):m_manager(m) {} numeral_manager & m() const { return m_manager; } void round_to_minus_inf() { C::round_to_minus_inf(m()); } void round_to_plus_inf() { C::round_to_plus_inf(m()); } void set_rounding(bool to_plus_inf) { C::set_rounding(m(), to_plus_inf); } numeral const & lower(interval const & a) const { if (a.m_constant) { bound * b = a.m_node->lower(a.m_x); return b == nullptr ? a.m_l_val /* don't care */ : b->value(); } return a.m_l_val; } numeral const & upper(interval const & a) const { if (a.m_constant) { bound * b = a.m_node->upper(a.m_x); return b == nullptr ? a.m_u_val /* don't care */ : b->value(); } return a.m_u_val; } numeral & lower(interval & a) { SASSERT(!a.m_constant); return a.m_l_val; } numeral & upper(interval & a) { SASSERT(!a.m_constant); return a.m_u_val; } bool lower_is_inf(interval const & a) const { return a.m_constant ? a.m_node->lower(a.m_x) == nullptr : a.m_l_inf; } bool upper_is_inf(interval const & a) const { return a.m_constant ? a.m_node->upper(a.m_x) == nullptr : a.m_u_inf; } bool lower_is_open(interval const & a) const { if (a.m_constant) { bound * b = a.m_node->lower(a.m_x); return b == nullptr || b->is_open(); } return a.m_l_open; } bool upper_is_open(interval const & a) const { if (a.m_constant) { bound * b = a.m_node->upper(a.m_x); return b == nullptr || b->is_open(); } return a.m_u_open; } // Setters void set_lower(interval & a, numeral const & n) { SASSERT(!a.m_constant); m().set(a.m_l_val, n); } void set_upper(interval & a, numeral const & n) { SASSERT(!a.m_constant); m().set(a.m_u_val, n); } void set_lower_is_open(interval & a, bool v) { SASSERT(!a.m_constant); a.m_l_open = v; } void set_upper_is_open(interval & a, bool v) { SASSERT(!a.m_constant); a.m_u_open = v; } void set_lower_is_inf(interval & a, bool v) { SASSERT(!a.m_constant); a.m_l_inf = v; } void set_upper_is_inf(interval & a, bool v) { SASSERT(!a.m_constant); a.m_u_inf = v; } }; typedef ::interval_manager interval_manager; class definition : public constraint { public: definition(typename constraint::kind k):constraint(k) {} }; class monomial : public definition { friend class context_t; unsigned m_size; power m_powers[0]; monomial(unsigned sz, power const * pws); static unsigned get_obj_size(unsigned sz) { return sizeof(monomial) + sz*sizeof(power); } public: unsigned size() const { return m_size; } power const & get_power(unsigned idx) const { SASSERT(idx < size()); return m_powers[idx]; } power const * get_powers() const { return m_powers; } var x(unsigned idx) const { return get_power(idx).x(); } unsigned degree(unsigned idx) const { return get_power(idx).degree(); } void display(std::ostream & out, display_var_proc const & proc = display_var_proc(), bool use_star = false) const; }; class polynomial : public definition { friend class context_t; unsigned m_size; numeral m_c; numeral * m_as; var * m_xs; static unsigned get_obj_size(unsigned sz) { return sizeof(polynomial) + sz*sizeof(numeral) + sz*sizeof(var); } public: polynomial():definition(constraint::POLYNOMIAL) {} unsigned size() const { return m_size; } numeral const & a(unsigned i) const { return m_as[i]; } var x(unsigned i) const { return m_xs[i]; } var const * xs() const { return m_xs; } numeral const * as() const { return m_as; } numeral const & c() const { return m_c; } void display(std::ostream & out, numeral_manager & nm, display_var_proc const & proc = display_var_proc(), bool use_star = false) const; }; /** \brief Watched element (aka occurrence) can be: - A clause - A definition (i.e., a variable) Remark: we cannot use the two watched literal approach since we process multiple nodes. */ class watched { public: enum kind { CLAUSE=0, DEFINITION }; private: void * m_data; public: watched():m_data(nullptr) {} explicit watched(var x) { m_data = BOXTAGINT(void*, x, DEFINITION); } explicit watched(clause * c) { m_data = TAG(void*, c, CLAUSE); } kind get_kind() const { return static_cast(GET_TAG(m_data)); } bool is_clause() const { return get_kind() != DEFINITION; } bool is_definition() const { return get_kind() == DEFINITION; } clause * get_clause() const { SASSERT(is_clause()); return UNTAG(clause*, m_data); } var get_var() const { SASSERT(is_definition()); return UNBOXINT(m_data); } bool operator==(watched const & other) const { return m_data == other.m_data; } bool operator!=(watched const & other) const { return !operator==(other); } }; /** \brief Abstract functor for selecting the next leaf node to be explored. */ class node_selector { context_t * m_ctx; public: node_selector(context_t * ctx):m_ctx(ctx) {} virtual ~node_selector() {} context_t * ctx() const { return m_ctx; } // Return the next leaf node to be processed. // Front and back are the first and last nodes in the doubly linked list of // leaf nodes. // Remark: new nodes are always inserted in the front of the list. virtual node * operator()(node * front, node * back) = 0; }; /** \brief Abstract functor for selecting the next variable to branch. */ class var_selector { context_t * m_ctx; public: var_selector(context_t * ctx):m_ctx(ctx) {} virtual ~var_selector() {} context_t * ctx() const { return m_ctx; } // Return the next variable to branch. virtual var operator()(node * n) = 0; // ----------------------------------- // // Event handlers // // ----------------------------------- // Invoked when a new variable is created. virtual void new_var_eh(var x) {} // Invoked when node n is created virtual void new_node_eh(node * n) {} // Invoked before deleting node n. virtual void del_node_eh(node * n) {} // Invoked when variable x is used during conflict resolution. virtual void used_var_eh(node * n, var x) {} }; class node_splitter; friend class node_splitter; /** \brief Abstract functor for creating children for node n by branching on a given variable. */ class node_splitter { context_t * m_ctx; public: node_splitter(context_t * ctx):m_ctx(ctx) {} virtual ~node_splitter() {} context_t * ctx() const { return m_ctx; } node * mk_node(node * p) { return ctx()->mk_node(p); } bound * mk_decided_bound(var x, numeral const & val, bool lower, bool open, node * n) { return ctx()->mk_bound(x, val, lower, open, n, justification()); } /** \brief Create children nodes for n by splitting on x. \pre n is a leaf. The interval for x in n has more than one element. */ virtual void operator()(node * n, var x) = 0; }; /** \brief Return most recent splitting var for node n. */ var splitting_var(node * n) const; /** \brief Return true if x is a definition. */ bool is_definition(var x) const { return m_defs[x] != 0; } typedef svector watch_list; typedef _scoped_numeral_vector scoped_numeral_vector; private: reslimit& m_limit; C m_c; bool m_arith_failed; //!< True if the arithmetic module produced an exception. bool m_own_allocator; small_object_allocator * m_allocator; bound_array_manager m_bm; interval_manager m_im; scoped_numeral_vector m_num_buffer; svector m_is_int; ptr_vector m_defs; vector m_wlist; ptr_vector m_unit_clauses; ptr_vector m_clauses; ptr_vector m_lemmas; id_gen m_node_id_gen; uint64_t m_timestamp; node * m_root; // m_leaf_head is the head of a doubly linked list of leaf nodes to be processed. node * m_leaf_head; node * m_leaf_tail; var m_conflict; ptr_vector m_queue; unsigned m_qhead; display_var_proc m_default_display_proc; display_var_proc * m_display_proc; scoped_ptr m_node_selector; scoped_ptr m_var_selector; scoped_ptr m_node_splitter; svector m_pws; // Configuration numeral m_epsilon; //!< If upper - lower < epsilon, then new bound is not propagated. bool m_zero_epsilon; numeral m_max_bound; //!< Bounds > m_max and < -m_max are not propagated numeral m_minus_max_bound; //!< -m_max_bound numeral m_nth_root_prec; //!< precision for computing the nth root unsigned m_max_depth; //!< Maximum depth unsigned m_max_nodes; //!< Maximum number of nodes in the tree unsigned long long m_max_memory; // in bytes // Counters unsigned m_num_nodes; // Statistics unsigned m_num_conflicts; unsigned m_num_mk_bounds; unsigned m_num_splits; unsigned m_num_visited; // Temporary numeral m_tmp1, m_tmp2, m_tmp3; interval m_i_tmp1, m_i_tmp2, m_i_tmp3; friend class node; void set_arith_failed() { m_arith_failed = true; } void checkpoint(); bound_array_manager & bm() { return m_bm; } interval_manager & im() { return m_im; } small_object_allocator & allocator() const { return *m_allocator; } bound * mk_bound(var x, numeral const & val, bool lower, bool open, node * n, justification jst); void del_bound(bound * b); // Create a new bound and add it to the propagation queue. void propagate_bound(var x, numeral const & val, bool lower, bool open, node * n, justification jst); bool is_int(monomial const * m) const; bool is_int(polynomial const * p) const; bool is_monomial(var x) const { return m_defs[x] != 0 && m_defs[x]->get_kind() == constraint::MONOMIAL; } monomial * get_monomial(var x) const { SASSERT(is_monomial(x)); return static_cast(m_defs[x]); } bool is_polynomial(var x) const { return m_defs[x] != 0 && m_defs[x]->get_kind() == constraint::POLYNOMIAL; } polynomial * get_polynomial(var x) const { SASSERT(is_polynomial(x)); return static_cast(m_defs[x]); } static void display(std::ostream & out, numeral_manager & nm, display_var_proc const & proc, var x, numeral & k, bool lower, bool open); void display(std::ostream & out, var x) const; void display_definition(std::ostream & out, definition const * d, bool use_star = false) const; void display(std::ostream & out, constraint * a, bool use_star = false) const; void display(std::ostream & out, bound * b) const; void display(std::ostream & out, ineq * a) const; void display_params(std::ostream & out) const; void add_unit_clause(ineq * a, bool axiom); // Remark: Not all lemmas need to be watched. Some of them can be used to justify clauses only. void add_clause_core(unsigned sz, ineq * const * atoms, bool lemma, bool watched); void del_clause(clause * cls); node * mk_node(node * parent = nullptr); void del_node(node * n); void del_nodes(); void del(interval & a); void del_clauses(ptr_vector & cs); void del_unit_clauses(); void del_clauses(); void del_monomial(monomial * m); void del_sum(polynomial * p); void del_definitions(); /** \brief Insert n in the beginning of the doubly linked list of leaves. \pre n is a leaf, and it is not already in the list. */ void push_front(node * n); /** \brief Insert n in the end of the doubly linked list of leaves. \pre n is a leaf, and it is not already in the list. */ void push_back(node * n); /** \brief Remove n from the doubly linked list of leaves. \pre n is a leaf, and it is in the list. */ void remove_from_leaf_dlist(node * n); /** \brief Remove all nodes from the leaf dlist. */ void reset_leaf_dlist(); /** \brief Add all leaves back to the leaf dlist. */ void rebuild_leaf_dlist(node * n); // ----------------------------------- // // Propagation // // ----------------------------------- /** \brief Return true if the given node is in an inconsistent state. */ bool inconsistent(node * n) const { return n->inconsistent(); } /** \brief Set a conflict produced by the bounds of x at the given node. */ void set_conflict(var x, node * n); /** \brief Return true if bound b may propagate a new bound using constraint c at node n. */ bool may_propagate(bound * b, constraint * c, node * n); /** \brief Normalize bound if x is integer. Examples: x < 2 --> x <= 1 x <= 2.3 --> x <= 2 */ void normalize_bound(var x, numeral & val, bool lower, bool & open); /** \brief Return true if (x, k, lower, open) is a relevant new bound at node n. That is, it improves the current bound, and satisfies m_epsilon and m_max_bound. */ bool relevant_new_bound(var x, numeral const & k, bool lower, bool open, node * n); /** \brief Return true if the lower and upper bounds of x are 0 at node n. */ bool is_zero(var x, node * n) const; /** \brief Return true if upper bound of x is 0 at node n. */ bool is_upper_zero(var x, node * n) const; /** \brief Return true if lower and upper bounds of x are conflicting at node n. That is, upper(x) < lower(x) */ bool conflicting_bounds(var x, node * n) const; /** \brief Return true if x is unbounded at node n. */ bool is_unbounded(var x, node * n) const { return n->is_unbounded(x); } /** \brief Return true if b is the most recent lower/upper bound for variable b->x() at node n. */ bool most_recent(bound * b, node * n) const; /** \brief Add most recent bounds of node n into the propagation queue. That is, all bounds b s.t. b is in the trail of n, but not in the tail of parent(n), and most_recent(b, n). */ void add_recent_bounds(node * n); /** \brief Propagate new bounds at node n using get_monomial(x) \pre is_monomial(x) */ void propagate_monomial(var x, node * n); void propagate_monomial_upward(var x, node * n); void propagate_monomial_downward(var x, node * n, unsigned i); /** \brief Propagate new bounds at node n using get_polynomial(x) \pre is_polynomial(x) */ void propagate_polynomial(var x, node * n); // Propagate a new bound for y using the polynomial associated with x. x may be equal to y. void propagate_polynomial(var x, node * n, var y); /** \brief Propagate new bounds at node n using clause c. */ void propagate_clause(clause * c, node * n); /** \brief Return the truth value of inequaliy t at node n. */ lbool value(ineq * t, node * n); /** \brief Propagate new bounds at node n using the definition of variable x. \pre is_definition(x) */ void propagate_def(var x, node * n); /** \brief Propagate constraints in b->x()'s watch list. */ void propagate(node * n, bound * b); /** \brief Perform bound propagation at node n. */ void propagate(node * n); /** \brief Try to propagate at node n using all definitions. */ void propagate_all_definitions(node * n); // ----------------------------------- // // Main // // ----------------------------------- void init(); /** \brief Assert unit clauses in the node n. */ void assert_units(node * n); // ----------------------------------- // // Debugging support // // ----------------------------------- /** \brief Return true if b is a bound for node n. */ bool is_bound_of(bound * b, node * n) const; /** \brief Check the consistency of the doubly linked list of leaves. */ bool check_leaf_dlist() const; /** \brief Check paving tree structure. */ bool check_tree() const; /** \brief Check main invariants. */ bool check_invariant() const; public: context_t(reslimit& lim, C const & c, params_ref const & p, small_object_allocator * a); ~context_t(); /** \brief Return true if the arithmetic module failed. */ bool arith_failed() const { return m_arith_failed; } numeral_manager & nm() const { return m_c.m(); } unsigned num_vars() const { return m_is_int.size(); } bool is_int(var x) const { SASSERT(x < num_vars()); return m_is_int[x]; } /** \brief Create a new variable. */ var mk_var(bool is_int); /** \brief Create the monomial xs[0]^ks[0] * ... * xs[sz-1]^ks[sz-1]. The result is a variable y s.t. y = xs[0]^ks[0] * ... * xs[sz-1]^ks[sz-1]. \pre for all i \in [0, sz-1] : ks[i] > 0 \pre sz > 0 */ var mk_monomial(unsigned sz, power const * pws); /** \brief Create the sum c + as[0]*xs[0] + ... + as[sz-1]*xs[sz-1]. The result is a variable y s.t. y = c + as[0]*xs[0] + ... + as[sz-1]*xs[sz-1]. \pre sz > 0 \pre for all i \in [0, sz-1] : as[i] != 0 */ var mk_sum(numeral const & c, unsigned sz, numeral const * as, var const * xs); /** \brief Create an inequality. */ ineq * mk_ineq(var x, numeral const & k, bool lower, bool open); void inc_ref(ineq * a); void dec_ref(ineq * a); /** \brief Assert the clause atoms[0] \/ ... \/ atoms[sz-1] \pre sz > 1 */ void add_clause(unsigned sz, ineq * const * atoms) { add_clause_core(sz, atoms, false, true); } /** \brief Assert a constraint of one of the forms: x < k, x > k, x <= k, x >= k. If axiom == true, then the constraint is not tracked in proofs. */ void add_ineq(var x, numeral const & k, bool lower, bool open, bool axiom); /** \brief Store in the given vector all leaves of the paving tree. */ void collect_leaves(ptr_vector & leaves) const; /** \brief Display constraints asserted in the subpaving. */ void display_constraints(std::ostream & out, bool use_star = false) const; /** \brief Display bounds for each leaf of the tree. */ void display_bounds(std::ostream & out) const; void display_bounds(std::ostream & out, node * n) const; void set_display_proc(display_var_proc * p) { m_display_proc = p; } void updt_params(params_ref const & p); static void collect_param_descrs(param_descrs & d); void reset_statistics(); void collect_statistics(statistics & st) const; void operator()(); }; }; #endif z3-z3-4.8.7/src/math/subpaving/subpaving_t_def.h000066400000000000000000001721661356505360400214720ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: subpaving_t_def.h Abstract: Subpaving template for non-linear arithmetic. Author: Leonardo de Moura (leonardo) 2012-07-31. Revision History: --*/ #include "math/subpaving/subpaving_t.h" #include "math/interval/interval_def.h" #include "util/buffer.h" #include "util/z3_exception.h" #include "util/common_msgs.h" namespace subpaving { /** \brief Node selector for breadth-first search. */ template class breadth_first_node_selector : public context_t::node_selector { typedef typename context_t::node node; public: breadth_first_node_selector(context_t * ctx): context_t::node_selector(ctx) { } node * operator()(node * front, node * back) override { return back; } }; /** \brief Node selector for depth-first search. */ template class depth_first_node_selector : public context_t::node_selector { typedef typename context_t::node node; public: depth_first_node_selector(context_t * ctx): context_t::node_selector(ctx) { } virtual node * operator()(node * front, node * back) { return front; } }; /** Round robing variable selector. If only_non_def is true, then variable definitions (aka auxiliary variables) are ignored. */ template class round_robing_var_selector : public context_t::var_selector { typedef typename context_t::bound bound; bool m_only_non_def; void next(var & x) const { x++; if (x >= this->ctx()->num_vars()) x = 0; } public: round_robing_var_selector(context_t * ctx, bool only_non_def = true): context_t::var_selector(ctx), m_only_non_def(only_non_def) { } // Return the next variable to branch. var operator()(typename context_t::node * n) override { typename context_t::numeral_manager & nm = this->ctx()->nm(); SASSERT(this->ctx()->num_vars() > 0); var x = this->ctx()->splitting_var(n); if (x == null_var) x = 0; else next(x); var start = x; do { if (!m_only_non_def || !this->ctx()->is_definition(x)) { bound * lower = n->lower(x); bound * upper = n->upper(x); if (lower == nullptr || upper == nullptr || !nm.eq(lower->value(), upper->value())) { return x; } } next(x); } while (x != start); return null_var; } }; /** Selector that uses the following strategy: - If only_non_def is true, then variable definitions (aka auxiliary variables) are ignored. - All variables x s.t. lower(x) == upper(x) are ignored. - If node contains an unbounded variable (-oo, oo), then return the smallest unbounded variable. - Otherwise, select the smallest variable x with the largest width, where width is defined as: If x has lower and upper bound, then width = upper(x) - lower(x). If x has only lower, width = penalty/max(|lower(x)|, 1) If x has only upper, width = penalty/max(|upper(x)|, 1) penaly is a parameter of this class. This strategy guarantees fairness. */ template class largest_interval_var_selector : public context_t::var_selector { unsigned m_penalty; bool m_only_non_def; public: largest_interval_var_selector(context_t * ctx, unsigned unbounded_penalty = 10, bool only_non_def = true): context_t::var_selector(ctx), m_penalty(unbounded_penalty), m_only_non_def(only_non_def) { } // Return the next variable to branch. virtual var operator()(typename context_t::node * n) { var y = null_var; typename context_t::numeral_manager & nm = this->ctx()->nm(); _scoped_numeral::numeral_manager> largest(nm), width(nm), penalty(nm), one(nm); nm.set(penalty, m_penalty); nm.set(one, 1); unsigned num = this->ctx()->num_vars(); for (var x = 0; x < num; x++) { if (m_only_non_def && this->ctx()->is_definition(x)) continue; typename context_t::bound * l = n->lower(x); typename context_t::bound * u = n->upper(x); // variables without lower and upper bounds are selected immediately. if (l == 0 && u == 0) return x; if (l != 0 && u != 0) { // ignore variables s.t. lower(x) == upper(x) if (nm.eq(l->value(), u->value())) continue; // if x has lower and upper bounds, set width to upper(x) - lower(x) C::round_to_plus_inf(nm); nm.sub(u->value(), l->value(), width); } else { // if x does not have lower or upper, then // set width to penalty/|value| where value is the existing bound. if (l != 0) nm.set(width, l->value()); else nm.set(width, u->value()); C::round_to_plus_inf(nm); if (nm.is_neg(width)) nm.neg(width); if (nm.lt(width, one)) nm.set(width, one); nm.mul(penalty, width, width); } if (y == null_var || nm.gt(width, largest)) { y = x; nm.set(largest, width); } } return y; } }; template class midpoint_node_splitter : public context_t::node_splitter { typedef typename context_t::numeral_manager numeral_manager; typedef typename numeral_manager::numeral numeral; typedef typename context_t::node node; typedef typename context_t::bound bound; bool m_left_open; unsigned m_delta; public: midpoint_node_splitter(context_t * ctx, bool left_open = true, unsigned delta = 1): context_t::node_splitter(ctx), m_left_open(left_open), m_delta(delta) { SASSERT(m_delta < INT_MAX); } void operator()(node * n, var x) override { SASSERT(!n->inconsistent()); numeral_manager & nm = this->ctx()->nm(); node * left = this->mk_node(n); node * right = this->mk_node(n); bound * lower = n->lower(x); bound * upper = n->upper(x); _scoped_numeral mid(nm); if (lower == nullptr && upper == nullptr) { nm.set(mid, 0); // mid == 0 } else if (lower == nullptr) { _scoped_numeral delta(nm); SASSERT(upper != 0); nm.set(delta, static_cast(m_delta)); nm.set(mid, upper->value()); C::round_to_minus_inf(nm); nm.sub(mid, delta, mid); // mid == upper - delta } else if (upper == nullptr) { _scoped_numeral delta(nm); SASSERT(lower != 0); nm.set(delta, static_cast(m_delta)); nm.set(mid, lower->value()); C::round_to_plus_inf(nm); nm.add(mid, delta, mid); // mid == lower + delta } else { _scoped_numeral two(nm); SASSERT(!nm.eq(lower->value(), upper->value())); nm.set(two, 2); nm.add(lower->value(), upper->value(), mid); nm.div(mid, two, mid); if (!(nm.lt(lower->value(), mid) && nm.lt(mid, upper->value()))) throw subpaving::exception(); // mid == (lower + upper)/2 } this->mk_decided_bound(x, mid, false, m_left_open, left); this->mk_decided_bound(x, mid, true, !m_left_open, right); TRACE("subpaving_int_split", tout << "LEFT:\n"; this->ctx()->display_bounds(tout, left); tout << "\nRIGHT:\n"; this->ctx()->display_bounds(tout, right);); } }; /** \brief Auxiliary static method used to display a bound specified by (x, k, lower, open). */ template void context_t::display(std::ostream & out, numeral_manager & nm, display_var_proc const & proc, var x, numeral & k, bool lower, bool open) { if (lower) { out << nm.to_rational_string(k) << " <"; if (!open) out << "="; out << " "; proc(out, x); } else { proc(out, x); out << " <"; if (!open) out << "="; out << " " << nm.to_rational_string(k); } } template void context_t::ineq::display(std::ostream & out, numeral_manager & nm, display_var_proc const & proc) { context_t::display(out, nm, proc, m_x, m_val, is_lower(), is_open()); } template void context_t::bound::display(std::ostream & out, numeral_manager & nm, display_var_proc const & proc) { context_t::display(out, nm, proc, m_x, m_val, is_lower(), is_open()); } template void context_t::clause::display(std::ostream & out, numeral_manager & nm, display_var_proc const & proc) { for (unsigned i = 0; i < size(); i++) { if (i > 0) out << " or "; m_atoms[i]->display(out, nm, proc); } } template context_t::node::node(context_t & s, unsigned id): m_bm(s.bm()) { m_id = id; m_depth = 0; unsigned num_vars = s.num_vars(); m_conflict = null_var; m_trail = nullptr; m_parent = nullptr; m_first_child = nullptr; m_next_sibling = nullptr; m_prev = nullptr; m_next = nullptr; bm().mk(m_lowers); bm().mk(m_uppers); for (unsigned i = 0; i < num_vars; i++) { bm().push_back(m_lowers, nullptr); bm().push_back(m_uppers, nullptr); } } template context_t::node::node(node * parent, unsigned id): m_bm(parent->m_bm) { m_id = id; m_depth = parent->depth() + 1; bm().copy(parent->m_lowers, m_lowers); bm().copy(parent->m_uppers, m_uppers); m_conflict = parent->m_conflict; m_trail = parent->m_trail; m_parent = parent; m_first_child = nullptr; m_next_sibling = parent->m_first_child; m_prev = nullptr; m_next = nullptr; parent->m_first_child = this; } /** \brief Add a new bound b at this node. */ template void context_t::node::push(bound * b) { SASSERT(b->prev() == m_trail); m_trail = b; if (b->is_lower()) { bm().set(m_lowers, b->x(), b); SASSERT(lower(b->x()) == b); } else { bm().set(m_uppers, b->x(), b); SASSERT(upper(b->x()) == b); } } /** \brief Return the most recent variable that was used for splitting on node n. */ template var context_t::splitting_var(node * n) const { if (n == m_root) return null_var; bound * b = n->trail_stack(); while (b != nullptr) { if (b->jst().is_axiom()) return b->x(); b = b->prev(); } UNREACHABLE(); return null_var; } template context_t::monomial::monomial(unsigned sz, power const * pws): definition(constraint::MONOMIAL), m_size(sz) { memcpy(m_powers, pws, sz*sizeof(power)); std::sort(m_powers, m_powers+sz, typename power::lt_proc()); DEBUG_CODE({ for (unsigned i = 0; i < sz; i ++) { SASSERT(i == 0 || x(i) > x(i-1)); SASSERT(degree(i) > 0); }}); } template void context_t::monomial::display(std::ostream & out, display_var_proc const & proc, bool use_star) const { SASSERT(m_size > 0); for (unsigned i = 0; i < m_size; i++) { if (i > 0) { if (use_star) out << "*"; else out << " "; } proc(out, x(i)); if (degree(i) > 1) out << "^" << degree(i); } } template void context_t::polynomial::display(std::ostream & out, numeral_manager & nm, display_var_proc const & proc, bool use_star) const { bool first = true; if (!nm.is_zero(m_c)) { out << nm.to_rational_string(m_c); first = false; } for (unsigned i = 0; i < m_size; i++) { if (first) first = false; else out << " + "; if (!nm.is_one(a(i))) { out << nm.to_rational_string(a(i)); if (use_star) out << "*"; else out << " "; } proc(out, x(i)); } } template context_t::context_t(reslimit& lim, C const & c, params_ref const & p, small_object_allocator * a): m_limit(lim), m_c(c), m_own_allocator(a == nullptr), m_allocator(a == nullptr ? alloc(small_object_allocator, "subpaving") : a), m_bm(*this, *m_allocator), m_im(lim, interval_config(m_c.m())), m_num_buffer(nm()) { m_arith_failed = false; m_timestamp = 0; m_root = nullptr; m_leaf_head = nullptr; m_leaf_tail = nullptr; m_conflict = null_var; m_qhead = 0; m_display_proc = &m_default_display_proc; m_node_selector = alloc(breadth_first_node_selector, this); m_var_selector = alloc(round_robing_var_selector, this); m_node_splitter = alloc(midpoint_node_splitter, this); m_num_nodes = 0; updt_params(p); reset_statistics(); } template context_t::~context_t() { nm().del(m_epsilon); nm().del(m_max_bound); nm().del(m_minus_max_bound); nm().del(m_nth_root_prec); nm().del(m_tmp1); nm().del(m_tmp2); nm().del(m_tmp3); del(m_i_tmp1); del(m_i_tmp2); del(m_i_tmp3); del_nodes(); del_unit_clauses(); del_clauses(); del_definitions(); if (m_own_allocator) dealloc(m_allocator); } template void context_t::checkpoint() { if (!m_limit.inc()) throw default_exception(Z3_CANCELED_MSG); if (memory::get_allocation_size() > m_max_memory) throw default_exception(Z3_MAX_MEMORY_MSG); } template void context_t::del(interval & a) { nm().del(a.m_l_val); nm().del(a.m_u_val); } template void context_t::updt_params(params_ref const & p) { unsigned epsilon = p.get_uint("epsilon", 20); if (epsilon != 0) { nm().set(m_epsilon, static_cast(epsilon)); nm().inv(m_epsilon); m_zero_epsilon = false; } else { nm().reset(m_epsilon); m_zero_epsilon = true; } unsigned max_power = p.get_uint("max_bound", 10); nm().set(m_max_bound, 10); nm().power(m_max_bound, max_power, m_max_bound); nm().set(m_minus_max_bound, m_max_bound); nm().neg(m_minus_max_bound); m_max_depth = p.get_uint("max_depth", 128); m_max_nodes = p.get_uint("max_nodes", 8192); m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); unsigned prec = p.get_uint("nth_root_precision", 8192); if (prec == 0) prec = 1; nm().set(m_nth_root_prec, static_cast(prec)); nm().inv(m_nth_root_prec); } template void context_t::collect_param_descrs(param_descrs & d) { d.insert("max_nodes", CPK_UINT, "(default: 8192) maximum number of nodes in the subpaving tree."); d.insert("max_depth", CPK_UINT, "(default: 128) maximum depth of the subpaving tree."); d.insert("epsilon", CPK_UINT, "(default: 20) value k s.t. a new lower (upper) bound for x is propagated only new-lower(x) > lower(k) + 1/k * max(min(upper(x) - lower(x), |lower|), 1) (new-upper(x) < upper(x) - 1/k * max(min(upper(x) - lower(x), |lower|), 1)). If k = 0, then this restriction is ignored."); d.insert("max_bound", CPK_UINT, "(default 10) value k s.t. a new upper (lower) bound for x is propagated only if upper(x) > -10^k or lower(x) = -oo (lower(x) < 10^k or upper(x) = oo)"); d.insert("nth_root_precision", CPK_UINT, "(default 8192) value k s.t. 1/k is the precision for computing the nth root in the subpaving module."); } template void context_t::display_params(std::ostream & out) const { out << "max_nodes " << m_max_nodes << "\n"; out << "max_depth " << m_max_depth << "\n"; out << "epsilon " << nm().to_rational_string(m_epsilon) << "\n"; out << "max_bound " << nm().to_rational_string(m_max_bound) << "\n"; out << "max_memory " << m_max_memory << "\n"; } template typename context_t::bound * context_t::mk_bound(var x, numeral const & val, bool lower, bool open, node * n, justification jst) { SASSERT(!inconsistent(n)); m_num_mk_bounds++; void * mem = allocator().allocate(sizeof(bound)); bound * r = new (mem) bound(); r->m_x = x; if (is_int(x)) { // adjust integer bound if (!nm().is_int(val)) open = false; // performing ceil/floor if (lower) { nm().ceil(val, r->m_val); } else { nm().floor(val, r->m_val); } if (open) { open = false; if (lower) { C::round_to_minus_inf(nm()); nm().inc(r->m_val); } else { C::round_to_plus_inf(nm()); nm().dec(r->m_val); } } } else { nm().set(r->m_val, val); } r->m_lower = lower; r->m_open = open; r->m_mark = false; r->m_timestamp = m_timestamp; r->m_prev = n->trail_stack(); r->m_jst = jst; n->push(r); TRACE("subpaving_mk_bound", tout << "mk_bound: "; display(tout, r); tout << "\ntimestamp: " << r->m_timestamp << "\n";); if (conflicting_bounds(x, n)) { TRACE("subpaving_mk_bound", tout << "conflict\n"; display_bounds(tout, n);); set_conflict(x, n); } m_timestamp++; if (m_timestamp == UINT64_MAX) throw subpaving::exception(); // subpaving failed. return r; } template void context_t::propagate_bound(var x, numeral const & val, bool lower, bool open, node * n, justification jst) { bound * b = mk_bound(x, val, lower, open, n, jst); m_queue.push_back(b); SASSERT(!lower || n->lower(x) == b); SASSERT(lower || n->upper(x) == b); SASSERT(is_int(x) || !lower || nm().eq(n->lower(x)->value(), val)); SASSERT(is_int(x) || lower || nm().eq(n->upper(x)->value(), val)); SASSERT(open || !nm().is_int(val) || !lower || nm().eq(n->lower(x)->value(), val)); SASSERT(open || !nm().is_int(val) || lower || nm().eq(n->upper(x)->value(), val)); SASSERT(!lower || nm().ge(n->lower(x)->value(), val)); SASSERT(lower || nm().le(n->upper(x)->value(), val)); } template void context_t::del_bound(bound * b) { nm().del(b->m_val); b->~bound(); allocator().deallocate(sizeof(bound), b); } template void context_t::display(std::ostream & out, var x) const { if (x == null_var) out << "[null]"; else (*m_display_proc)(out, x); } template void context_t::display(std::ostream & out, bound * b) const { b->display(out, nm(), *m_display_proc); } template void context_t::display(std::ostream & out, ineq * a) const { a->display(out, nm(), *m_display_proc); } template void context_t::display_definition(std::ostream & out, definition const * d, bool use_star) const { switch (d->get_kind()) { case constraint::MONOMIAL: static_cast(d)->display(out, *m_display_proc, use_star); break; case constraint::POLYNOMIAL: static_cast(d)->display(out, nm(), *m_display_proc, use_star); break; default: UNREACHABLE(); }; } template void context_t::display(std::ostream & out, constraint * c, bool use_star) const { if (c->get_kind() == constraint::CLAUSE) static_cast(c)->display(out, nm(), *m_display_proc); else display_definition(out, static_cast(c), use_star); } template void context_t::display_bounds(std::ostream & out, node * n) const { unsigned num = num_vars(); for (unsigned x = 0; x < num; x++) { bound * l = n->lower(x); bound * u = n->upper(x); if (l != nullptr) { display(out, l); out << " "; } if (u != nullptr) { display(out, u); } if (l != nullptr || u != nullptr) out << "\n"; } } /** \brief Return true if all variables in m are integer. */ template bool context_t::is_int(monomial const * m) const { for (unsigned i = 0; i < m->size(); i++) { if (is_int(m->x(i))) return true; } return false; } /** \brief Return true if all variables in p are integer, and all coefficients in p are integer. */ template bool context_t::is_int(polynomial const * p) const { for (unsigned i = 0; i < p->size(); i++) { if (!is_int(p->x(i)) || !nm().is_int(p->a(i))) { TRACE("subpaving_is_int", tout << "polynomial is not integer due to monomial at i: " << i << "\n"; tout.flush(); display(tout, p->x(i)); tout << " "; nm().display(tout, p->a(i)); tout << "\n";); return false; } } return nm().is_int(p->c()); } template var context_t::mk_var(bool is_int) { var r = static_cast(m_is_int.size()); m_is_int.push_back(is_int); m_defs.push_back(0); m_wlist.push_back(watch_list()); m_var_selector->new_var_eh(r); return r; } template void context_t::del_monomial(monomial * m) { unsigned mem_sz = monomial::get_obj_size(m->size()); m->~monomial(); allocator().deallocate(mem_sz, m); } template var context_t::mk_monomial(unsigned sz, power const * pws) { SASSERT(sz > 0); m_pws.reset(); m_pws.append(sz, pws); std::sort(m_pws.begin(), m_pws.end(), power::lt_proc()); unsigned j = 0; for (unsigned i = 1; i < sz; i++) { if (m_pws[j].x() == m_pws[i].x()) { m_pws[j].degree() += m_pws[i].degree(); } else { j++; SASSERT(j <= i); m_pws[j] = m_pws[i]; } } sz = j + 1; pws = m_pws.c_ptr(); unsigned mem_sz = monomial::get_obj_size(sz); void * mem = allocator().allocate(mem_sz); monomial * r = new (mem) monomial(sz, pws); var new_var = mk_var(is_int(r)); m_defs[new_var] = r; for (unsigned i = 0; i < sz; i++) { var x = pws[i].x(); m_wlist[x].push_back(watched(new_var)); } return new_var; } template void context_t::del_sum(polynomial * p) { unsigned sz = p->size(); unsigned mem_sz = polynomial::get_obj_size(sz); for (unsigned i = 0; i < sz; i++) { nm().del(p->m_as[i]); } nm().del(p->m_c); p->~polynomial(); allocator().deallocate(mem_sz, p); } template var context_t::mk_sum(numeral const & c, unsigned sz, numeral const * as, var const * xs) { m_num_buffer.reserve(num_vars()); for (unsigned i = 0; i < sz; i++) { SASSERT(xs[i] < num_vars()); nm().set(m_num_buffer[xs[i]], as[i]); } unsigned mem_sz = polynomial::get_obj_size(sz); void * mem = allocator().allocate(mem_sz); polynomial * p = new (mem) polynomial(); p->m_size = sz; nm().set(p->m_c, c); p->m_as = reinterpret_cast(static_cast(mem) + sizeof(polynomial)); p->m_xs = reinterpret_cast(reinterpret_cast(p->m_as) + sizeof(numeral)*sz); memcpy(p->m_xs, xs, sizeof(var)*sz); std::sort(p->m_xs, p->m_xs+sz); for (unsigned i = 0; i < sz; i++) { numeral * curr = p->m_as + i; new (curr) numeral(); var x = p->m_xs[i]; nm().swap(m_num_buffer[x], *curr); } TRACE("subpaving_mk_sum", tout << "new variable is integer: " << is_int(p) << "\n";); var new_var = mk_var(is_int(p)); for (unsigned i = 0; i < sz; i++) { var x = p->m_xs[i]; m_wlist[x].push_back(watched(new_var)); } m_defs[new_var] = p; return new_var; } template typename context_t::ineq * context_t::mk_ineq(var x, numeral const & k, bool lower, bool open) { void * mem = allocator().allocate(sizeof(ineq)); ineq * r = new (mem) ineq(); r->m_ref_count = 0; r->m_x = x; nm().set(r->m_val, k); r->m_lower = lower; r->m_open = open; return r; } template void context_t::inc_ref(ineq * a) { TRACE("subpaving_ref_count", tout << "inc-ref: " << a << " " << a->m_ref_count << "\n";); if (a) a->m_ref_count++; } template void context_t::dec_ref(ineq * a) { if (a) { TRACE("subpaving_ref_count", tout << "dec-ref: " << a << " " << a->m_ref_count << "\n"; a->display(tout, nm()); tout << "\n";); SASSERT(a->m_ref_count > 0); a->m_ref_count--; if (a->m_ref_count == 0) { nm().del(a->m_val); a->~ineq(); allocator().deallocate(sizeof(ineq), a); } } } template void context_t::add_clause_core(unsigned sz, ineq * const * atoms, bool lemma, bool watch) { SASSERT(lemma || watch); SASSERT(sz > 0); if (sz == 1) { add_unit_clause(atoms[0], true); return; } void * mem = allocator().allocate(clause::get_obj_size(sz)); clause * c = new (mem) clause(); c->m_size = sz; for (unsigned i = 0; i < sz; i++) { inc_ref(atoms[i]); c->m_atoms[i] = atoms[i]; } std::stable_sort(c->m_atoms, c->m_atoms + sz, typename ineq::lt_var_proc()); if (watch) { for (unsigned i = 0; i < sz; i++) { var x = c->m_atoms[i]->x(); if (i == 0 || x != c->m_atoms[i-1]->x()) m_wlist[x].push_back(watched(c)); } } c->m_lemma = lemma; c->m_num_jst = 0; c->m_watched = watch; if (!lemma) { m_clauses.push_back(c); } else if (watch) { m_lemmas.push_back(c); } TRACE("subpaving_clause", tout << "new clause:\n"; display(tout, c); tout << "\n";); } template void context_t::del_clause(clause * c) { SASSERT(c->m_num_jst == 0); // We cannot delete a clause that is being used to justify some bound bool watch = c->watched(); var prev_x = null_var; unsigned sz = c->size(); for (unsigned i = 0; i < sz; i++) { var x = c->m_atoms[i]->x(); if (watch) { if (x != prev_x) m_wlist[x].erase(watched(c)); prev_x = x; } dec_ref((*c)[i]); } unsigned mem_sz = clause::get_obj_size(sz); c->~clause(); allocator().deallocate(mem_sz, c); } template void context_t::add_unit_clause(ineq * a, bool axiom) { TRACE("subpaving", a->display(tout, nm(), *m_display_proc); tout << "\n";); inc_ref(a); m_unit_clauses.push_back(TAG(ineq*, a, axiom)); } template void context_t::add_ineq(var x, numeral const & k, bool lower, bool open, bool axiom) { ineq * unit = mk_ineq(x, k, lower, open); add_unit_clause(unit, axiom); } template typename context_t::node * context_t::mk_node(node * parent) { void * mem = allocator().allocate(sizeof(node)); node * r; if (parent == nullptr) r = new (mem) node(*this, m_node_id_gen.mk()); else r = new (mem) node(parent, m_node_id_gen.mk()); m_var_selector->new_node_eh(r); // Add node in the leaf dlist push_front(r); m_num_nodes++; return r; } template void context_t::del_node(node * n) { SASSERT(n->first_child() == 0); SASSERT(m_num_nodes > 0); m_num_nodes--; m_var_selector->del_node_eh(n); // recycle id m_node_id_gen.recycle(n->id()); // disconnect n from list of leaves. remove_from_leaf_dlist(n); // disconnect n from parent node * p = n->parent(); bound * b = n->trail_stack(); bound * b_old; if (p != nullptr) { node * c = p->first_child(); if (c == n) { // n is the first child p->set_first_child(n->next_sibling()); } else { SASSERT(c->next_sibling() != 0); while (c->next_sibling() != n) { c = c->next_sibling(); SASSERT(c->next_sibling() != 0); } SASSERT(c->next_sibling() == n); c->set_next_sibling(n->next_sibling()); } b_old = p->trail_stack(); } else { b_old = nullptr; } while (b != b_old) { bound * old = b; b = b->prev(); del_bound(old); } bm().del(n->uppers()); bm().del(n->lowers()); n->~node(); allocator().deallocate(sizeof(node), n); } template void context_t::del_nodes() { ptr_buffer todo; if (m_root == nullptr) return; todo.push_back(m_root); while (!todo.empty()) { node * n = todo.back(); node * c = n->first_child(); if (c == nullptr) { del_node(n); todo.pop_back(); } else { while (c != nullptr) { todo.push_back(c); c = c->next_sibling(); } } } } template void context_t::push_front(node * n) { SASSERT(n->first_child() == 0); SASSERT(n->next() == 0); SASSERT(n->prev() == 0); n->set_next(m_leaf_head); if (m_leaf_head != nullptr) { SASSERT(m_leaf_head->prev() == 0); m_leaf_head->set_prev(n); } else { SASSERT(m_leaf_head == 0); m_leaf_tail = n; } m_leaf_head = n; } template void context_t::push_back(node * n) { SASSERT(n->first_child() == 0); SASSERT(n->next() == 0); SASSERT(n->prev() == 0); n->set_prev(m_leaf_tail); if (m_leaf_tail != nullptr) { SASSERT(m_leaf_tail->next() == 0); m_leaf_tail->set_next(n); } else { SASSERT(m_leaf_tail == 0); m_leaf_head = n; } m_leaf_tail = n; } template void context_t::reset_leaf_dlist() { // Remove all nodes from the lead doubly linked list node * n = m_leaf_head; while (n != nullptr) { node * next = n->next(); n->set_next(nullptr); n->set_prev(nullptr); n = next; } m_leaf_head = nullptr; m_leaf_tail = nullptr; } template void context_t::rebuild_leaf_dlist(node * n) { reset_leaf_dlist(); // Reinsert all leaves in the leaf dlist. ptr_buffer todo; if (m_root != nullptr) todo.push_back(m_root); while (!todo.empty()) { node * n = todo.back(); todo.pop_back(); node * c = n->first_child(); if (c == nullptr) { if (!n->inconsistent()) push_front(n); } else { while (c != nullptr) { SASSERT(c->parent() == n); todo.push_back(c); c = c->next_sibling(); } } } } template void context_t::remove_from_leaf_dlist(node * n) { node * prev = n->prev(); node * next = n->next(); SASSERT(prev == 0 || prev != next); SASSERT(next == 0 || prev != next); SASSERT(prev != n); SASSERT(next != n); if (prev != nullptr) { SASSERT(m_leaf_head != n); prev->set_next(next); n->set_prev(nullptr); } else if (m_leaf_head == n) { m_leaf_head = next; } if (next != nullptr) { SASSERT(m_leaf_tail != n); next->set_prev(prev); n->set_next(nullptr); } else if (m_leaf_tail == n) { m_leaf_tail = prev; } SASSERT(n->prev() == 0 && n->next() == 0); } template void context_t::collect_leaves(ptr_vector & leaves) const { // Copy all leaves to the given vector. ptr_buffer todo; if (m_root != nullptr) todo.push_back(m_root); while (!todo.empty()) { node * n = todo.back(); todo.pop_back(); node * c = n->first_child(); if (c == nullptr) { if (!n->inconsistent()) leaves.push_back(n); } else { while (c != nullptr) { SASSERT(c->parent() == n); todo.push_back(c); c = c->next_sibling(); } } } } template void context_t::del_unit_clauses() { unsigned sz = m_unit_clauses.size(); for (unsigned i = 0; i < sz; i++) dec_ref(UNTAG(ineq*, m_unit_clauses[i])); m_unit_clauses.reset(); } template void context_t::del_clauses(ptr_vector & cs) { unsigned sz = cs.size(); for (unsigned i = 0; i < sz; i++) { del_clause(cs[i]); } cs.reset(); } template void context_t::del_clauses() { del_clauses(m_clauses); del_clauses(m_lemmas); } template void context_t::del_definitions() { unsigned sz = num_vars(); for (unsigned i = 0; i < sz; i++) { definition * d = m_defs[i]; if (d == nullptr) continue; switch (d->get_kind()) { case constraint::MONOMIAL: del_monomial(static_cast(d)); break; case constraint::POLYNOMIAL: del_sum(static_cast(d)); break; default: UNREACHABLE(); break; } } } template void context_t::display_constraints(std::ostream & out, bool use_star) const { // display definitions for (unsigned i = 0; i < num_vars(); i++) { if (is_definition(i)) { (*m_display_proc)(out, i); out << " = "; display_definition(out, m_defs[i], use_star); out << "\n"; } } // display units for (unsigned i = 0; i < m_unit_clauses.size(); i++) { ineq * a = UNTAG(ineq*, m_unit_clauses[i]); a->display(out, nm(), *m_display_proc); out << "\n"; } // display clauses for (unsigned i = 0; i < m_clauses.size(); i++) { m_clauses[i]->display(out, nm(), *m_display_proc); out << "\n"; } } // ----------------------------------- // // Propagation // // ----------------------------------- template void context_t::set_conflict(var x, node * n) { m_num_conflicts++; n->set_conflict(x); remove_from_leaf_dlist(n); } template bool context_t::may_propagate(bound * b, constraint * c, node * n) { SASSERT(b != 0 && c != 0); TRACE("may_propagate_bug", display(tout, b); tout << " | "; display(tout, c); tout << "\nresult: " << (b->timestamp() > c->timestamp()) << ", " << b->timestamp() << ", " << c->timestamp() << "\n";); return b->timestamp() >= c->timestamp(); } // Normalization for integer bounds template void context_t::normalize_bound(var x, numeral & val, bool lower, bool & open) { if (is_int(x)) { // adjust integer bound if (!nm().is_int(val)) open = false; // performing ceil/floor if (lower) { nm().ceil(val, val); } else { nm().floor(val, val); } if (open) { open = false; if (lower) { C::round_to_minus_inf(nm()); nm().inc(val); } else { C::round_to_plus_inf(nm()); nm().dec(val); } } } } template bool context_t::relevant_new_bound(var x, numeral const & k, bool lower, bool open, node * n) { try { bound * curr_lower = n->lower(x); bound * curr_upper = n->upper(x); SASSERT(curr_lower == 0 || curr_lower->x() == x); SASSERT(curr_upper == 0 || curr_upper->x() == x); TRACE("subpaving_relevant_bound", display(tout, x); tout << " " << (lower ? ">" : "<") << (open ? "" : "=") << " "; nm().display(tout, k); tout << "\n"; tout << "existing bounds:\n"; if (curr_lower) { display(tout, curr_lower); tout << "\n"; } if (curr_upper) { display(tout, curr_upper); tout << "\n"; }); if (lower) { // If new bound triggers a conflict, then it is relevant. if (curr_upper && (nm().gt(k, curr_upper->value()) || ((open || curr_upper->is_open()) && nm().eq(k, curr_upper->value())))) { TRACE("subpaving_relevant_bound", tout << "relevant because triggers conflict.\n";); return true; } // If m_epsilon is zero, then bound is relevant only if it improves existing bound. if (m_zero_epsilon && curr_lower != nullptr && (nm().lt(k, curr_lower->value()) || ((curr_lower->is_open() || !open) && nm().eq(k, curr_lower->value())))) { // new lower bound does not improve existing bound TRACE("subpaving_relevant_bound", tout << "irrelevant because does not improve existing bound.\n";); return false; } if (curr_upper == nullptr && nm().lt(m_max_bound, k)) { // new lower bound exceeds the :max-bound threshold TRACE("subpaving_relevant_bound", tout << "irrelevant because exceeds :max-bound threshold.\n";); return false; } if (!m_zero_epsilon && curr_lower != nullptr) { // check if: // new-lower > lower + m_epsilon * max(min(upper - lower, |lower|), 1) numeral & min = m_tmp1; numeral & abs_lower = m_tmp2; nm().set(abs_lower, curr_lower->value()); nm().abs(abs_lower); if (curr_upper != nullptr) { nm().sub(curr_upper->value(), curr_lower->value(), min); if (nm().lt(abs_lower, min)) nm().set(min, abs_lower); } else { nm().set(min, abs_lower); } numeral & delta = m_tmp3; nm().set(delta, 1); if (nm().gt(min, delta)) nm().set(delta, min); nm().mul(delta, m_epsilon, delta); nm().add(curr_lower->value(), delta, delta); TRACE("subpaving_relevant_bound_bug", tout << "k: "; nm().display(tout, k); tout << ", delta: "; nm().display(tout, delta); tout << "\n"; tout << "curr_lower: "; nm().display(tout, curr_lower->value()); tout << ", min: "; nm().display(tout, min); tout << "\n";); if (nm().le(k, delta)) { TRACE("subpaving_relevant_bound", tout << "irrelevant because does not improve existing bound to at least "; nm().display(tout, delta); tout << "\n";); return false; } } } else { // If new bound triggers a conflict, then it is relevant. if (curr_lower && (nm().gt(curr_lower->value(), k) || ((open || curr_lower->is_open()) && nm().eq(k, curr_lower->value())))) { TRACE("subpaving_relevant_bound", tout << "relevant because triggers conflict.\n";); return true; } // If m_epsilon is zero, then bound is relevant only if it improves existing bound. if (m_zero_epsilon && curr_upper != nullptr && (nm().lt(curr_upper->value(), k) || ((curr_upper->is_open() || !open) && nm().eq(k, curr_upper->value())))) { // new upper bound does not improve existing bound TRACE("subpaving_relevant_bound", tout << "irrelevant because does not improve existing bound.\n";); return false; } if (curr_lower == nullptr && nm().lt(k, m_minus_max_bound)) { // new upper bound exceeds the -:max-bound threshold TRACE("subpaving_relevant_bound", tout << "irrelevant because exceeds -:max-bound threshold.\n";); return false; } if (!m_zero_epsilon && curr_upper != nullptr) { // check if: // new-upper < upper - m_epsilon * max(min(upper - lower, |upper|), 1) numeral & min = m_tmp1; numeral & abs_upper = m_tmp2; nm().set(abs_upper, curr_upper->value()); nm().abs(abs_upper); if (curr_lower != nullptr) { nm().sub(curr_upper->value(), curr_lower->value(), min); if (nm().lt(abs_upper, min)) nm().set(min, abs_upper); } else { nm().set(min, abs_upper); } numeral & delta = m_tmp3; nm().set(delta, 1); if (nm().gt(min, delta)) nm().set(delta, min); nm().mul(delta, m_epsilon, delta); nm().sub(curr_upper->value(), delta, delta); if (nm().ge(k, delta)) { TRACE("subpaving_relevant_bound", tout << "irrelevant because does not improve existing bound to at least "; nm().display(tout, delta); tout << "\n";); return false; } } } TRACE("subpaving_relevant_bound", tout << "new bound is relevant\n";); return true; } catch (const typename C::exception &) { // arithmetic module failed. set_arith_failed(); return false; } } template bool context_t::is_zero(var x, node * n) const { // Return true if lower(x) == upper(x) == 0 at n bound * l = n->lower(x); bound * u = n->upper(x); return l != nullptr && u != nullptr && nm().is_zero(l->value()) && nm().is_zero(u->value()) && !l->is_open() && !u->is_open(); } template bool context_t::is_upper_zero(var x, node * n) const { // Return true if upper(x) is zero at node n bound * u = n->upper(x); return u != nullptr && nm().is_zero(u->value()) && !u->is_open(); } template bool context_t::conflicting_bounds(var x, node * n) const { // Return true if upper(x) < lower(x) at node n bound * l = n->lower(x); bound * u = n->upper(x); return l != nullptr && u != nullptr && (nm().lt(u->value(), l->value()) || ((l->is_open() || u->is_open()) && nm().eq(u->value(), l->value()))); } /** \brief Return the truth value of the inequality t in node n. The result may be l_true (True), l_false (False), or l_undef(Unknown). */ template lbool context_t::value(ineq * t, node * n) { var x = t->x(); bound * u = n->upper(x); bound * l = n->lower(x); if (u == nullptr && l == nullptr) return l_undef; else if (t->is_lower()) { if (u != nullptr && (nm().lt(u->value(), t->value()) || ((u->is_open() || t->is_open()) && nm().eq(u->value(), t->value())))) return l_false; else if (l != nullptr && (nm().gt(l->value(), t->value()) || ((l->is_open() || !t->is_open()) && nm().eq(l->value(), t->value())))) return l_true; else return l_undef; } else { if (l != nullptr && (nm().gt(l->value(), t->value()) || ((l->is_open() || t->is_open()) && nm().eq(l->value(), t->value())))) return l_false; else if (u != nullptr && (nm().lt(u->value(), t->value()) || ((u->is_open() || !t->is_open()) && nm().eq(u->value(), t->value())))) return l_true; else return l_undef; } } template void context_t::propagate_clause(clause * c, node * n) { TRACE("propagate_clause", tout << "propagate using:\n"; display(tout, c); tout << "\n";); m_num_visited++; c->set_visited(m_timestamp); unsigned sz = c->size(); unsigned j = UINT_MAX; for (unsigned i = 0; i < sz; i++) { ineq * atom = (*c)[i]; switch (value(atom, n)) { case l_true: return; // clause was already satisfied at n case l_false: break; case l_undef: if (j != UINT_MAX) return; // clause has more than one unassigned literal j = i; break; } } if (j == UINT_MAX) { // Clause is in conflict, use first atom to trigger inconsistency j = 0; } ineq * a = (*c)[j]; TRACE("propagate_clause", tout << "propagating inequality: "; display(tout, a); tout << "\n";); propagate_bound(a->x(), a->value(), a->is_lower(), a->is_open(), n, justification(c)); // A clause can propagate only once. // So, we can safely set its timestamp again to avoid another useless visit. c->set_visited(m_timestamp); } template void context_t::propagate_polynomial(var x, node * n, var y) { SASSERT(y != null_var); SASSERT(is_polynomial(x)); polynomial * p = get_polynomial(x); unsigned sz = p->size(); interval & r = m_i_tmp1; r.set_mutable(); interval & v = m_i_tmp2; interval & av = m_i_tmp3; av.set_mutable(); if (x == y) { for (unsigned i = 0; i < sz; i++) { var z = p->x(i); v.set_constant(n, z); im().mul(p->a(i), v, av); if (i == 0) im().set(r, av); else im().add(r, av, r); } // r contains the deduced bounds for x == y } else { v.set_constant(n, x); numeral & a = m_tmp1; im().set(r, v); for (unsigned i = 0; i < sz; i++) { var z = p->x(i); if (z != y) { v.set_constant(n, z); im().mul(p->a(i), v, av); im().sub(r, av, r); } else { nm().set(a, p->a(i)); TRACE("propagate_polynomial_bug", tout << "a: "; nm().display(tout, a); tout << "\n";); } } TRACE("propagate_polynomial_bug", tout << "r before mul 1/a: "; im().display(tout, r); tout << "\n";); im().div(r, a, r); TRACE("propagate_polynomial_bug", tout << "r after mul 1/a: "; im().display(tout, r); tout << "\n";); // r contains the deduced bounds for y. } // r contains the deduced bounds for y. if (!r.m_l_inf) { normalize_bound(y, r.m_l_val, true, r.m_l_open); if (relevant_new_bound(y, r.m_l_val, true, r.m_l_open, n)) { propagate_bound(y, r.m_l_val, true, r.m_l_open, n, justification(x)); if (inconsistent(n)) return; } } if (!r.m_u_inf) { normalize_bound(y, r.m_u_val, false, r.m_u_open); if (relevant_new_bound(y, r.m_u_val, false, r.m_u_open, n)) propagate_bound(y, r.m_u_val, false, r.m_u_open, n, justification(x)); } } template void context_t::propagate_polynomial(var x, node * n) { TRACE("propagate_polynomial", tout << "propagate_polynomial: "; display(tout, x); tout << "\n";); TRACE("propagate_polynomial_detail", display_bounds(tout, n);); SASSERT(is_polynomial(x)); polynomial * p = get_polynomial(x); p->set_visited(m_timestamp); var unbounded_var = null_var; if (is_unbounded(x, n)) unbounded_var = x; unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { var y = p->x(i); if (is_unbounded(y, n)) { if (unbounded_var != null_var) return; // no propagation is possible. unbounded_var = y; } } TRACE("propagate_polynomial", tout << "unbounded_var: "; display(tout, unbounded_var); tout << "\n";); if (unbounded_var != null_var) { propagate_polynomial(x, n, unbounded_var); } else { propagate_polynomial(x, n, x); for (unsigned i = 0; i < sz; i++) { if (inconsistent(n)) return; propagate_polynomial(x, n, p->x(i)); } } } template void context_t::propagate_monomial(var x, node * n) { TRACE("propagate_monomial", tout << "propagate_monomial: "; display(tout, x); tout << "\n";); SASSERT(is_monomial(x)); SASSERT(!inconsistent(n)); monomial * m = get_monomial(x); m->set_visited(m_timestamp); bool found_unbounded = false; bool found_zero = false; bool x_is_unbounded = false; unsigned sz = m->size(); for (unsigned i = 0; i < sz; i++) { var y = m->x(i); if (is_zero(y, n)) { found_zero = true; } if (m->degree(i) % 2 == 0) { if (is_upper_zero(y, n)) { found_zero = true; } continue; // elements with even power always produce a lower bound } if (is_unbounded(y, n)) { found_unbounded = true; } } TRACE("propagate_monomial", tout << "found_zero: " << found_zero << ", found_unbounded: " << found_unbounded << "\n";); if (found_zero) { if (!is_zero(x, n)) { // x must be zero numeral & zero = m_tmp1; nm().set(zero, 0); propagate_bound(x, zero, true, false, n, justification(x)); if (inconsistent(n)) return; propagate_bound(x, zero, false, false, n, justification(x)); } // no need to downward propagation return; } x_is_unbounded = n->is_unbounded(x); if (!found_unbounded) propagate_monomial_upward(x, n); if (inconsistent(n)) return; if (!x_is_unbounded) { unsigned bad_pos = UINT_MAX; interval & aux = m_i_tmp1; for (unsigned i = 0; i < sz; i++) { aux.set_constant(n, m->x(i)); if (im().contains_zero(aux)) { if (bad_pos != UINT_MAX) return; // there is more than one position that contains zero, so downward propagation is not possible. bad_pos = i; } } if (bad_pos == UINT_MAX) { // we can use all variables for downward propagation. for (unsigned i = 0; i < sz; i++) { if (inconsistent(n)) return; propagate_monomial_downward(x, n, i); } } else { propagate_monomial_downward(x, n, bad_pos); } } } template void context_t::propagate_monomial_upward(var x, node * n) { SASSERT(is_monomial(x)); monomial * m = get_monomial(x); unsigned sz = m->size(); interval & r = m_i_tmp1; r.set_mutable(); interval & y = m_i_tmp2; interval & yk = m_i_tmp3; yk.set_mutable(); for (unsigned i = 0; i < sz; i++) { y.set_constant(n, m->x(i)); im().power(y, m->degree(i), yk); if (i == 0) im().set(r, yk); else im().mul(r, yk, r); } // r contains the new bounds for x if (!r.m_l_inf) { normalize_bound(x, r.m_l_val, true, r.m_l_open); if (relevant_new_bound(x, r.m_l_val, true, r.m_l_open, n)) { propagate_bound(x, r.m_l_val, true, r.m_l_open, n, justification(x)); if (inconsistent(n)) return; } } if (!r.m_u_inf) { normalize_bound(x, r.m_u_val, false, r.m_u_open); if (relevant_new_bound(x, r.m_u_val, false, r.m_u_open, n)) propagate_bound(x, r.m_u_val, false, r.m_u_open, n, justification(x)); } } template void context_t::propagate_monomial_downward(var x, node * n, unsigned j) { TRACE("propagate_monomial", tout << "propagate_monomial_downward: "; display(tout, x); tout << ", j: " << j << "\n"; display(tout, get_monomial(x)); tout << "\n";); SASSERT(is_monomial(x)); monomial * m = get_monomial(x); SASSERT(j < m->size()); unsigned sz = m->size(); interval & r = m_i_tmp3; if (sz > 1) { interval & d = m_i_tmp1; d.set_mutable(); interval & y = m_i_tmp2; interval & yk = m_i_tmp3; yk.set_mutable(); bool first = true; for (unsigned i = 0; i < sz; i++) { if (i == j) continue; y.set_constant(n, m->x(i)); im().power(y, m->degree(i), yk); if (first) im().set(d, yk); else im().mul(d, yk, r); } interval & aux = m_i_tmp2; aux.set_constant(n, x); im().div(aux, d, r); } else { SASSERT(sz == 1); SASSERT(j == 0); interval & aux = m_i_tmp2; aux.set_constant(n, x); im().set(r, aux); } unsigned d = m->degree(j); if (d > 1) { if (d % 2 == 0 && im().lower_is_neg(r)) return; // If d is even, we can't take the nth-root when lower(r) is negative. im().xn_eq_y(r, d, m_nth_root_prec, r); } var y = m->x(j); // r contains the new bounds for y if (!r.m_l_inf) { normalize_bound(y, r.m_l_val, true, r.m_l_open); if (relevant_new_bound(y, r.m_l_val, true, r.m_l_open, n)) { propagate_bound(y, r.m_l_val, true, r.m_l_open, n, justification(x)); if (inconsistent(n)) return; } } if (!r.m_u_inf) { normalize_bound(y, r.m_u_val, false, r.m_u_open); if (relevant_new_bound(y, r.m_u_val, false, r.m_u_open, n)) propagate_bound(y, r.m_u_val, false, r.m_u_open, n, justification(x)); } } template bool context_t::most_recent(bound * b, node * n) const { var x = b->x(); if (b->is_lower()) return n->lower(x) == b; else return n->upper(x) == b; } template void context_t::add_recent_bounds(node * n) { SASSERT(m_queue.empty()); bound * old_b = n->parent_trail_stack(); bound * b = n->trail_stack(); while (b != old_b) { if (most_recent(b, n)) { b->set_timestamp(m_timestamp); m_queue.push_back(b); } b = b->prev(); } } template void context_t::propagate_def(var x, node * n) { SASSERT(is_definition(x)); m_num_visited++; definition * d = m_defs[x]; switch (d->get_kind()) { case constraint::MONOMIAL: propagate_monomial(x, n); break; case constraint::POLYNOMIAL: propagate_polynomial(x, n); break; default: break; } } template void context_t::propagate(node * n, bound * b) { var x = b->x(); TRACE("subpaving_propagate", tout << "propagate: "; display(tout, b); tout << ", timestamp: " << b->timestamp() << "\n";); typename watch_list::const_iterator it = m_wlist[x].begin(); typename watch_list::const_iterator end = m_wlist[x].end(); for (; it != end; ++it) { if (inconsistent(n)) return; watched const & w = *it; try { if (w.is_clause()) { clause * c = w.get_clause(); if (may_propagate(b, c, n)) { propagate_clause(c, n); } } else { var y = w.get_var(); definition * d = m_defs[y]; if (may_propagate(b, d, n)) { propagate_def(y, n); } } } catch (const typename C::exception &) { // arithmetic module failed, ignore constraint set_arith_failed(); } } if (inconsistent(n)) return; if (is_definition(x)) { definition * d = m_defs[x]; if (may_propagate(b, d, n)) { propagate_def(x, n); } } } template void context_t::propagate(node * n) { while (m_qhead < m_queue.size()) { if (inconsistent(n)) break; checkpoint(); bound * b = m_queue[m_qhead]; SASSERT(is_bound_of(b, n)); m_qhead++; propagate(n, b); } m_queue.reset(); m_qhead = 0; } template void context_t::propagate_all_definitions(node * n) { unsigned num = num_vars(); for (unsigned x = 0; x < num; x++) { if (inconsistent(n)) break; if (is_definition(x)) propagate_def(x, n); } } // ----------------------------------- // // Main // // ----------------------------------- template void context_t::assert_units(node * n) { typename ptr_vector::const_iterator it = m_unit_clauses.begin(); typename ptr_vector::const_iterator end = m_unit_clauses.end(); for (; it != end; ++it) { checkpoint(); ineq * a = UNTAG(ineq*, *it); bool axiom = GET_TAG(*it) != 0; TRACE("subpaving_init", tout << "asserting: "; display(tout, a); tout << ", axiom: " << axiom << "\n";); propagate_bound(a->x(), a->value(), a->is_lower(), a->is_open(), n, justification(axiom)); if (inconsistent(n)) break; } TRACE("subpaving_init", tout << "bounds after init\n"; display_bounds(tout, n);); } template void context_t::init() { SASSERT(m_root == 0); SASSERT(m_leaf_head == 0); SASSERT(m_leaf_tail == 0); m_timestamp = 0; m_root = mk_node(); SASSERT(m_leaf_head == m_root); SASSERT(m_leaf_tail == m_root); TRACE("subpaving_init", display_constraints(tout);); assert_units(m_root); propagate_all_definitions(m_root); propagate(m_root); TRACE("subpaving_init", tout << "root bounds after propagation\n"; display_bounds(tout, m_root);); SASSERT(check_invariant()); } template void context_t::operator()() { if (m_root == nullptr) init(); TRACE("subpaving_stats", statistics st; collect_statistics(st); tout << "statistics:\n"; st.display_smt2(tout);); TRACE("subpaving_main", display_params(tout);); while (m_leaf_head != nullptr) { checkpoint(); SASSERT(m_queue.empty()); if (m_num_nodes > m_max_nodes) break; node * n = (*m_node_selector)(m_leaf_head, m_leaf_tail); if (n == nullptr) break; TRACE("subpaving_main", tout << "selected node: #" << n->id() << ", depth: " << n->depth() << "\n";); remove_from_leaf_dlist(n); if (n != m_root) { add_recent_bounds(n); propagate(n); } TRACE("subpaving_main", tout << "node #" << n->id() << " after propagation\n"; display_bounds(tout, n);); if (n->inconsistent()) { TRACE("subpaving_main", tout << "node #" << n->id() << " is inconsistent.\n";); // TODO: conflict resolution continue; } if (n->depth() >= m_max_depth) { TRACE("subpaving_main", tout << "maximum depth reached, skipping node #" << n->id() << "\n";); continue; } var x = (*m_var_selector)(n); TRACE("subpaving_main", tout << "splitting variable: "; display(tout, x); tout << "\n";); if (x != null_var) { (*m_node_splitter)(n, x); m_num_splits++; // remove inconsistent children } } TRACE("subpaving_stats", statistics st; collect_statistics(st); tout << "statistics:\n"; st.display_smt2(tout);); } template void context_t::display_bounds(std::ostream & out) const { ptr_vector leaves; collect_leaves(leaves); typename ptr_vector::const_iterator it = leaves.begin(); typename ptr_vector::const_iterator end = leaves.end(); for (bool first = true; it != end; ++it) { node * n = *it; if (first) first = false; else out << "=========\n"; display_bounds(out, n); } } // ----------------------------------- // // Statistics // // ----------------------------------- template void context_t::reset_statistics() { m_num_conflicts = 0; m_num_mk_bounds = 0; m_num_splits = 0; m_num_visited = 0; } template void context_t::collect_statistics(statistics & st) const { st.update("conflicts", m_num_conflicts); st.update("new bounds", m_num_mk_bounds); st.update("splits", m_num_splits); st.update("nodes", m_num_nodes); st.update("visited", m_num_visited); } // ----------------------------------- // // Debugging support // // ----------------------------------- template bool context_t::is_bound_of(bound * b, node * n) const { bound * c = n->trail_stack(); while (c != nullptr) { if (c == b) return true; if (c->timestamp() <= b->timestamp()) return false; c = c->prev(); } return false; } template bool context_t::check_leaf_dlist() const { node * n = m_leaf_head; while (n != nullptr) { node * next = n->next(); SASSERT(next != 0 || m_leaf_tail == n); SASSERT(next == 0 || next->prev() == n); n = next; } return true; } template bool context_t::check_tree() const { ptr_buffer todo; if (m_root != nullptr) todo.push_back(m_root); while (!todo.empty()) { node * n = todo.back(); todo.pop_back(); node * c = n->first_child(); while (c != nullptr) { SASSERT(c->parent() == n); todo.push_back(c); c = c->next_sibling(); } } return true; } template bool context_t::check_invariant() const { SASSERT(check_tree()); SASSERT(check_leaf_dlist()); return true; } }; z3-z3-4.8.7/src/math/subpaving/subpaving_types.h000066400000000000000000000020231356505360400215350ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: subpaving_types.h Abstract: Subpaving auxiliary types. Author: Leonardo de Moura (leonardo) 2012-08-07. Revision History: --*/ #ifndef SUBPAVING_TYPES_H_ #define SUBPAVING_TYPES_H_ namespace subpaving { class ineq; typedef unsigned var; const var null_var = UINT_MAX; class exception { }; class power : public std::pair { public: power():std::pair() {} power(var v, unsigned d):std::pair(v, d) {} power(power const & p):std::pair(p) {} var x() const { return first; } var get_var() const { return first; } unsigned degree() const { return second; } unsigned & degree() { return second; } void set_var(var x) { first = x; } struct lt_proc { bool operator()(power const & p1, power const & p2) { return p1.get_var() < p2.get_var(); } }; }; struct display_var_proc { virtual void operator()(std::ostream & out, var x) const { out << "x" << x; } }; }; #endif z3-z3-4.8.7/src/math/subpaving/tactic/000077500000000000000000000000001356505360400174145ustar00rootroot00000000000000z3-z3-4.8.7/src/math/subpaving/tactic/CMakeLists.txt000066400000000000000000000002761356505360400221610ustar00rootroot00000000000000z3_add_component(subpaving_tactic SOURCES expr2subpaving.cpp subpaving_tactic.cpp COMPONENT_DEPENDENCIES core_tactics subpaving TACTIC_HEADERS subpaving_tactic.h ) z3-z3-4.8.7/src/math/subpaving/tactic/expr2subpaving.cpp000066400000000000000000000260141356505360400231020ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: expr2subpaving.cpp Abstract: Translator from Z3 expressions into generic subpaving data-structure. Author: Leonardo (leonardo) 2012-08-08 Notes: --*/ #include "math/subpaving/tactic/expr2subpaving.h" #include "ast/expr2var.h" #include "util/ref_util.h" #include "util/z3_exception.h" #include "ast/arith_decl_plugin.h" #include "util/scoped_numeral_buffer.h" #include "util/common_msgs.h" struct expr2subpaving::imp { struct frame { app * m_curr; unsigned m_idx; frame():m_curr(nullptr), m_idx(0) {} frame(app * t):m_curr(t), m_idx(0) {} }; ast_manager & m_manager; subpaving::context & m_subpaving; unsynch_mpq_manager & m_qm; arith_util m_autil; expr2var * m_expr2var; bool m_expr2var_owner; expr_ref_vector m_var2expr; typedef svector var_vector; obj_map m_cache; var_vector m_cached_vars; scoped_mpz_vector m_cached_numerators; scoped_mpz_vector m_cached_denominators; obj_map m_lit_cache; imp(ast_manager & m, subpaving::context & s, expr2var * e2v): m_manager(m), m_subpaving(s), m_qm(s.qm()), m_autil(m), m_var2expr(m), m_cached_numerators(m_qm), m_cached_denominators(m_qm) { if (e2v == nullptr) { m_expr2var = alloc(expr2var, m); m_expr2var_owner = true; } else { m_expr2var = e2v; m_expr2var_owner = false; } } ~imp() { reset_cache(); if (m_expr2var_owner) dealloc(m_expr2var); } ast_manager & m() { return m_manager; } subpaving::context & s() { return m_subpaving; } unsynch_mpq_manager & qm() const { return m_qm; } void reset_cache() { dec_ref_map_keys(m(), m_cache); m_cached_vars.reset(); m_cached_numerators.reset(); m_cached_denominators.reset(); dec_ref_map_key_values(m(), s(), m_lit_cache); } void checkpoint() { if (m().canceled()) throw default_exception(Z3_CANCELED_MSG); } subpaving::var mk_var_for(expr * t) { SASSERT(!m_autil.is_numeral(t)); subpaving::var x = m_expr2var->to_var(t); if (x == subpaving::null_var) { bool is_int = m_autil.is_int(t); x = s().mk_var(is_int); m_expr2var->insert(t, x); if (x >= m_var2expr.size()) m_var2expr.resize(x+1, nullptr); m_var2expr.set(x, t); } return x; } void found_non_simplified() { throw default_exception("you must apply simplifier before internalizing expressions into the subpaving module."); } bool is_cached(expr * t) { return t->get_ref_count() > 1 && m_cache.contains(t); } bool is_int_real(expr * t) { return m_autil.is_int_real(t); } void cache_result(expr * t, subpaving::var x, mpz const & n, mpz const & d) { SASSERT(!m_cache.contains(t)); SASSERT(m_cached_numerators.size() == m_cached_vars.size()); SASSERT(m_cached_denominators.size() == m_cached_vars.size()); if (t->get_ref_count() <= 1) return; unsigned idx = m_cached_vars.size(); m_cache.insert(t, idx); m().inc_ref(t); m_cached_vars.push_back(x); m_cached_numerators.push_back(n); m_cached_denominators.push_back(d); } subpaving::var process_num(app * t, unsigned depth, mpz & n, mpz & d) { rational k; VERIFY(m_autil.is_numeral(t, k)); qm().set(n, k.to_mpq().numerator()); qm().set(d, k.to_mpq().denominator()); return subpaving::null_var; } // Put t as a^k. void as_power(expr * t, expr * & a, unsigned & k) { if (!m_autil.is_power(t)) { a = t; k = 1; return; } rational _k; if (!m_autil.is_numeral(to_app(t)->get_arg(1), _k) || !_k.is_int() || !_k.is_unsigned()) { a = t; k = 1; return; } a = to_app(t)->get_arg(0); k = _k.get_unsigned(); } subpaving::var process_mul(app * t, unsigned depth, mpz & n, mpz & d) { unsigned num_args = t->get_num_args(); if (num_args <= 1) found_non_simplified(); rational k; expr * m; if (m_autil.is_numeral(t->get_arg(0), k)) { if (num_args != 2) found_non_simplified(); qm().set(n, k.to_mpq().numerator()); qm().set(d, k.to_mpq().denominator()); m = t->get_arg(1); } else { qm().set(n, 1); qm().set(d, 1); m = t; } expr * const * margs; unsigned sz; if (m_autil.is_mul(m)) { margs = to_app(m)->get_args(); sz = to_app(m)->get_num_args(); } else { margs = &m; sz = 1; } scoped_mpz n_arg(qm()); scoped_mpz d_arg(qm()); sbuffer pws; for (unsigned i = 0; i < sz; i++) { expr * arg = margs[i]; unsigned k; as_power(arg, arg, k); subpaving::var x_arg = process(arg, depth+1, n_arg, d_arg); qm().power(n_arg, k, n_arg); qm().power(d_arg, k, d_arg); qm().mul(n, n_arg, n); qm().mul(d, d_arg, d); if (x_arg != subpaving::null_var) pws.push_back(subpaving::power(x_arg, k)); } subpaving::var x; if (pws.empty()) x = subpaving::null_var; else if (pws.size() == 1 && pws[0].degree() == 1) x = pws[0].get_var(); else x = s().mk_monomial(pws.size(), pws.c_ptr()); cache_result(t, x, n, d); return x; } typedef _scoped_numeral_buffer mpz_buffer; typedef sbuffer var_buffer; subpaving::var process_add(app * t, unsigned depth, mpz & n, mpz & d) { unsigned num_args = t->get_num_args(); mpz_buffer ns(qm()), ds(qm()); var_buffer xs; scoped_mpq c(qm()), c_arg(qm()); scoped_mpz n_arg(qm()), d_arg(qm()); for (unsigned i = 0; i < num_args; i++) { expr * arg = t->get_arg(i); subpaving::var x_arg = process(arg, depth+1, n_arg, d_arg); if (x_arg == subpaving::null_var) { qm().set(c_arg, n_arg, d_arg); qm().add(c, c_arg, c); } else { xs.push_back(x_arg); ns.push_back(n_arg); ds.push_back(d_arg); } } qm().set(d, c.get().denominator()); unsigned sz = xs.size(); for (unsigned i = 0; i < sz; i++) { qm().lcm(d, ds[i], d); } scoped_mpz & k = d_arg; qm().div(d, c.get().denominator(), k); scoped_mpz sum_c(qm()); qm().mul(c.get().numerator(), k, sum_c); for (unsigned i = 0; i < sz; i++) { qm().div(d, ds[i], k); qm().mul(ns[i], k, ns[i]); } subpaving::var x; if (sz == 0) { qm().set(n, sum_c); x = subpaving::null_var; } else { x = s().mk_sum(sum_c, sz, ns.c_ptr(), xs.c_ptr()); qm().set(n, 1); } cache_result(t, x, n, d); return x; } subpaving::var process_power(app * t, unsigned depth, mpz & n, mpz & d) { rational k; SASSERT(t->get_num_args() == 2); if (!m_autil.is_numeral(t->get_arg(1), k) || !k.is_int() || !k.is_unsigned()) { qm().set(n, 1); qm().set(d, 1); return mk_var_for(t); } unsigned _k = k.get_unsigned(); subpaving::var x = process(t->get_arg(0), depth+1, n, d); if (x != subpaving::null_var) { subpaving::power p(x, _k); x = s().mk_monomial(1, &p); } qm().power(n, _k, n); qm().power(d, _k, d); cache_result(t, x, n, d); return x; } subpaving::var process_arith_app(app * t, unsigned depth, mpz & n, mpz & d) { SASSERT(m_autil.is_arith_expr(t)); switch (t->get_decl_kind()) { case OP_NUM: return process_num(t, depth, n, d); case OP_ADD: return process_add(t, depth, n, d); case OP_MUL: return process_mul(t, depth, n, d); case OP_POWER: return process_power(t, depth, n, d); case OP_TO_REAL: return process(t->get_arg(0), depth+1, n, d); case OP_SUB: case OP_UMINUS: found_non_simplified(); break; case OP_TO_INT: case OP_DIV: case OP_IDIV: case OP_MOD: case OP_REM: case OP_IRRATIONAL_ALGEBRAIC_NUM: throw default_exception("you must apply arithmetic purifier before internalizing expressions into the subpaving module."); case OP_SIN: case OP_COS: case OP_TAN: case OP_ASIN: case OP_ACOS: case OP_ATAN: case OP_SINH: case OP_COSH: case OP_TANH: case OP_ASINH: case OP_ACOSH: case OP_ATANH: // TODO throw default_exception("transcendental and hyperbolic functions are not supported yet."); default: UNREACHABLE(); } return subpaving::null_var; } subpaving::var process(expr * t, unsigned depth, mpz & n, mpz & d) { SASSERT(is_int_real(t)); checkpoint(); if (is_cached(t)) { unsigned idx = m_cache.find(t); qm().set(n, m_cached_numerators[idx]); qm().set(d, m_cached_denominators[idx]); return m_cached_vars[idx]; } SASSERT(!is_quantifier(t)); if (::is_var(t) || !m_autil.is_arith_expr(t)) { qm().set(n, 1); qm().set(d, 1); return mk_var_for(t); } return process_arith_app(to_app(t), depth, n, d); } bool is_var(expr * t) const { return m_expr2var->is_var(t); } subpaving::var internalize_term(expr * t, mpz & n, mpz & d) { return process(t, 0, n, d); } }; expr2subpaving::expr2subpaving(ast_manager & m, subpaving::context & s, expr2var * e2v) { m_imp = alloc(imp, m, s, e2v); } expr2subpaving::~expr2subpaving() { dealloc(m_imp); } ast_manager & expr2subpaving::m() const { return m_imp->m(); } subpaving::context & expr2subpaving::s() const { return m_imp->s(); } bool expr2subpaving::is_var(expr * t) const { return m_imp->is_var(t); } subpaving::var expr2subpaving::internalize_term(expr * t, mpz & n, mpz & d) { return m_imp->internalize_term(t, n, d); } z3-z3-4.8.7/src/math/subpaving/tactic/expr2subpaving.h000066400000000000000000000021111356505360400225370ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: expr2subpaving.h Abstract: Translator from Z3 expressions into generic subpaving data-structure. Author: Leonardo (leonardo) 2012-08-08 Notes: --*/ #ifndef EXPR2SUBPAVING_H_ #define EXPR2SUBPAVING_H_ #include "ast/ast.h" #include "math/subpaving/subpaving.h" class expr2var; class expr2subpaving { struct imp; imp * m_imp; public: expr2subpaving(ast_manager & m, subpaving::context & s, expr2var * e2v = nullptr); ~expr2subpaving(); ast_manager & m() const; subpaving::context & s() const; /** \brief Return true if t was encoded as a variable by the translator. */ bool is_var(expr * t) const; /** \brief Internalize a Z3 arithmetical expression into the subpaving data-structure. \remark throws subpaving::exception there is a translation error (when using imprecise representations, i.e. floats, in the subpaving module) */ subpaving::var internalize_term(expr * t, /* out */ mpz & n, /* out */ mpz & d); }; #endif z3-z3-4.8.7/src/math/subpaving/tactic/subpaving_tactic.cpp000066400000000000000000000216771356505360400234620ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: subpaving_tactic.cpp Abstract: "Fake" tactic used to test subpaving module. Author: Leonardo de Moura (leonardo) 2012-08-07. Revision History: --*/ #include "tactic/tactical.h" #include "tactic/core/simplify_tactic.h" #include "math/subpaving/tactic/expr2subpaving.h" #include "ast/expr2var.h" #include "ast/arith_decl_plugin.h" #include "ast/ast_smt2_pp.h" #include "util/hwf.h" #include "util/mpff.h" #include "util/mpfx.h" #include "util/f2n.h" class subpaving_tactic : public tactic { struct display_var_proc : public subpaving::display_var_proc { expr_ref_vector m_inv; display_var_proc(expr2var & e2v):m_inv(e2v.m()) { e2v.mk_inv(m_inv); } virtual ~display_var_proc() {} ast_manager & m() const { return m_inv.get_manager(); } void operator()(std::ostream & out, subpaving::var x) const override { expr * t = m_inv.get(x, nullptr); if (t != nullptr) out << mk_ismt2_pp(t, m()); else out << "k!" << x; } }; struct imp { enum engine_kind { MPQ, MPF, HWF, MPFF, MPFX, NONE }; ast_manager & m_manager; unsynch_mpq_manager m_qm; mpf_manager m_fm_core; f2n m_fm; hwf_manager m_hm_core; f2n m_hm; mpff_manager m_ffm; mpfx_manager m_fxm; arith_util m_autil; engine_kind m_kind; scoped_ptr m_ctx; scoped_ptr m_proc; expr2var m_e2v; scoped_ptr m_e2s; bool m_display; imp(ast_manager & m, params_ref const & p): m_manager(m), m_fm(m_fm_core), m_hm(m_hm_core), m_autil(m), m_kind(NONE), m_e2v(m) { updt_params(p); } ast_manager & m() const { return m_manager; } void collect_param_descrs(param_descrs & r) { m_ctx->collect_param_descrs(r); // #ifndef _EXTERNAL_RELEASE r.insert("numeral", CPK_SYMBOL, "(default: mpq) options: mpq, mpf, hwf, mpff, mpfx."); r.insert("print_nodes", CPK_BOOL, "(default: false) display subpaving tree leaves."); // #endif } void updt_params(params_ref const & p) { m_display = p.get_bool("print_nodes", false); symbol engine = p.get_sym("numeral", symbol("mpq")); engine_kind new_kind; if (engine == "mpq") new_kind = MPQ; else if (engine == "mpf") new_kind = MPF; else if (engine == "mpff") new_kind = MPFF; else if (engine == "mpfx") new_kind = MPFX; else new_kind = HWF; if (m_kind != new_kind) { m_kind = new_kind; switch (m_kind) { case MPQ: m_ctx = subpaving::mk_mpq_context(m().limit(), m_qm); break; case MPF: m_ctx = subpaving::mk_mpf_context(m().limit(), m_fm); break; case HWF: m_ctx = subpaving::mk_hwf_context(m().limit(), m_hm, m_qm); break; case MPFF: m_ctx = subpaving::mk_mpff_context(m().limit(), m_ffm, m_qm); break; case MPFX: m_ctx = subpaving::mk_mpfx_context(m().limit(), m_fxm, m_qm); break; default: UNREACHABLE(); break; } m_e2s = alloc(expr2subpaving, m_manager, *m_ctx, &m_e2v); } m_ctx->updt_params(p); } void collect_statistics(statistics & st) const { m_ctx->collect_statistics(st); } void reset_statistics() { m_ctx->reset_statistics(); } subpaving::ineq * mk_ineq(expr * a) { bool neg = false; while (m().is_not(a, a)) neg = !neg; bool lower; bool open = false; if (m_autil.is_le(a)) { lower = false; } else if (m_autil.is_ge(a)) { lower = true; } else { throw tactic_exception("unsupported atom"); } if (neg) { lower = !lower; open = !open; } rational _k; if (!m_autil.is_numeral(to_app(a)->get_arg(1), _k)) throw tactic_exception("use simplify tactic with option :arith-lhs true"); scoped_mpq k(m_qm); k = _k.to_mpq(); scoped_mpz n(m_qm), d(m_qm); subpaving::var x = m_e2s->internalize_term(to_app(a)->get_arg(0), n, d); m_qm.mul(d, k, k); m_qm.div(k, n, k); if (is_neg(n)) lower = !lower; TRACE("subpaving_tactic", tout << x << " " << k << " " << lower << " " << open << "\n";); return m_ctx->mk_ineq(x, k, lower, open); } void process_clause(expr * c) { expr * const * args = nullptr; unsigned sz; if (m().is_or(c)) { args = to_app(c)->get_args(); sz = to_app(c)->get_num_args(); } else { args = &c; sz = 1; } ref_buffer ineq_buffer(*m_ctx); for (unsigned i = 0; i < sz; i++) { ineq_buffer.push_back(mk_ineq(args[i])); } m_ctx->add_clause(sz, ineq_buffer.c_ptr()); } void internalize(goal const & g) { try { for (unsigned i = 0; i < g.size(); i++) { process_clause(g.form(i)); } } catch (const subpaving::exception &) { throw tactic_exception("failed to internalize goal into subpaving module"); } } void process(goal const & g) { internalize(g); m_proc = alloc(display_var_proc, m_e2v); m_ctx->set_display_proc(m_proc.get()); try { (*m_ctx)(); } catch (const subpaving::exception &) { throw tactic_exception("failed building subpaving tree..."); } if (m_display) { m_ctx->display_constraints(std::cout); std::cout << "bounds at leaves: \n"; m_ctx->display_bounds(std::cout); } } }; imp * m_imp; params_ref m_params; statistics m_stats; public: subpaving_tactic(ast_manager & m, params_ref const & p): m_imp(alloc(imp, m, p)), m_params(p) { } ~subpaving_tactic() override { dealloc(m_imp); } tactic * translate(ast_manager & m) override { return alloc(subpaving_tactic, m, m_params); } void updt_params(params_ref const & p) override { m_params = p; m_imp->updt_params(p); } void collect_param_descrs(param_descrs & r) override { m_imp->collect_param_descrs(r); } void collect_statistics(statistics & st) const override { st.copy(m_stats); } void reset_statistics() override { m_stats.reset(); } void operator()(goal_ref const & in, goal_ref_buffer & result) override { try { m_imp->process(*in); m_imp->collect_statistics(m_stats); result.reset(); result.push_back(in.get()); } catch (z3_exception & ex) { // convert all Z3 exceptions into tactic exceptions throw tactic_exception(ex.msg()); } } void cleanup() override { ast_manager & m = m_imp->m(); dealloc(m_imp); m_imp = alloc(imp, m, m_params); } }; tactic * mk_subpaving_tactic_core(ast_manager & m, params_ref const & p) { return alloc(subpaving_tactic, m, p); } tactic * mk_subpaving_tactic(ast_manager & m, params_ref const & p) { params_ref simp_p = p; simp_p.set_bool("arith_lhs", true); simp_p.set_bool("expand_power", true); simp_p.set_uint("max_power", UINT_MAX); simp_p.set_bool("som", true); simp_p.set_bool("eq2ineq", true); simp_p.set_bool("elim_and", true); simp_p.set_bool("blast_distinct", true); params_ref simp2_p = p; simp2_p.set_bool("mul_to_power", true); return and_then(using_params(mk_simplify_tactic(m, p), simp_p), using_params(mk_simplify_tactic(m, p), simp2_p), mk_subpaving_tactic_core(m, p)); } z3-z3-4.8.7/src/math/subpaving/tactic/subpaving_tactic.h000066400000000000000000000010261356505360400231110ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: subpaving_tactic.h Abstract: "Fake" tactic used to test subpaving module. Author: Leonardo de Moura (leonardo) 2012-08-07. Revision History: --*/ #ifndef SUBPAVING_TACTIC_H_ #define SUBPAVING_TACTIC_H_ #include "util/params.h" class ast_manager; class tactic; tactic * mk_subpaving_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("subpaving", "tactic for testing subpaving module.", "mk_subpaving_tactic(m, p)") */ #endif z3-z3-4.8.7/src/model/000077500000000000000000000000001356505360400143165ustar00rootroot00000000000000z3-z3-4.8.7/src/model/CMakeLists.txt000066400000000000000000000006611356505360400170610ustar00rootroot00000000000000z3_add_component(model SOURCES array_factory.cpp datatype_factory.cpp func_interp.cpp model2expr.cpp model_core.cpp model.cpp model_evaluator.cpp model_implicant.cpp model_pp.cpp model_smt2_pp.cpp model_v2_pp.cpp numeral_factory.cpp struct_factory.cpp value_factory.cpp COMPONENT_DEPENDENCIES rewriter PYG_FILES model_evaluator_params.pyg model_params.pyg ) z3-z3-4.8.7/src/model/array_factory.cpp000066400000000000000000000151721356505360400176750ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: array_factory.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-10-28. Revision History: --*/ #include "ast/array_decl_plugin.h" #include "ast/ast_pp.h" #include "model/func_interp.h" #include "model/model_core.h" #include "model/array_factory.h" func_decl * mk_aux_decl_for_array_sort(ast_manager & m, sort * s) { ptr_buffer domain; sort * range = get_array_range(s); unsigned arity = get_array_arity(s); for (unsigned i = 0; i < arity; i++) { domain.push_back(get_array_domain(s, i)); } return m.mk_fresh_func_decl(symbol::null, symbol::null, arity, domain.c_ptr(), range); } array_factory::array_factory(ast_manager & m, model_core & md): struct_factory(m, m.mk_family_id("array"), md) { } /** \brieft Return as-array[f] where f is a fresh function symbol with the right domain and range for the array sort s. Store in fi the function interpretation for f. */ expr * array_factory::mk_array_interp(sort * s, func_interp * & fi) { func_decl * f = mk_aux_decl_for_array_sort(m_manager, s); fi = alloc(func_interp, m_manager, get_array_arity(s)); m_model.register_decl(f, fi); parameter p[1] = { parameter(f) }; expr * val = m_manager.mk_app(get_family_id(), OP_AS_ARRAY, 1, p); register_value(val); return val; } void array_factory::get_some_args_for(sort * s, ptr_buffer & args) { unsigned arity = get_array_arity(s); for (unsigned i = 0; i < arity; i++) { sort * d = get_array_domain(s, i); expr * a = m_model.get_some_value(d); args.push_back(a); } } expr * array_factory::get_some_value(sort * s) { TRACE("array_factory", tout << mk_pp(s, m_manager) << "\n";); value_set * set = nullptr; if (m_sort2value_set.find(s, set) && !set->empty()) return *(set->begin()); func_interp * fi; expr * val = mk_array_interp(s, fi); fi->set_else(m_model.get_some_value(get_array_range(s))); return val; } bool array_factory::mk_two_diff_values_for(sort * s) { DEBUG_CODE({ value_set * set = 0; SASSERT(!m_sort2value_set.find(s, set) || set->size() == 0); }); expr_ref r1(m_manager); expr_ref r2(m_manager); sort * range = get_array_range(s); if (!m_model.get_some_values(range, r1, r2)) return false; // failed... the range is probably unit. ptr_buffer args; get_some_args_for(s, args); func_interp * fi1; func_interp * fi2; mk_array_interp(s, fi1); mk_array_interp(s, fi2); fi1->insert_entry(args.c_ptr(), r1); fi2->insert_entry(args.c_ptr(), r2); DEBUG_CODE({ value_set * set = 0; SASSERT(m_sort2value_set.find(s, set) && set->size() == 2); }); return true; } 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->empty()) { if (!mk_two_diff_values_for(s)) return false; } m_sort2value_set.find(s, set); SASSERT(set != 0); SASSERT(set->size() > 0); if (set->size() == 1) { v1 = *(set->begin()); v2 = get_fresh_value(s); return v2.get() != nullptr; } else { SASSERT(set->size() >= 2); value_set::iterator it = set->begin(); v1 = *it; ++it; v2 = *it; return true; } } // // TODO: I have to check if the following procedure is really correct. // I'm supporting partial arrays where the "else" can be set later by the model_finder or model classes. // Projection functions may be also used. // // If projections are not used, then the following code should work if the "else" of a partial array // is set with the result of some entry. // expr * array_factory::get_fresh_value(sort * s) { value_set * set = get_value_set(s); if (set->empty()) { // easy case return get_some_value(s); } sort * range = get_array_range(s); expr * range_val = m_model.get_fresh_value(range); if (range_val != nullptr) { // easy case func_interp * fi; expr * val = mk_array_interp(s, fi); fi->set_else(range_val); return val; } else { TRACE("array_factory_bug", tout << "array fresh value: using fresh index, range: " << mk_pp(range, m_manager) << "\n";); expr_ref v1(m_manager); expr_ref v2(m_manager); if (m_model.get_some_values(range, v1, v2)) { // Claim: A is fresh if A[i1] = v1 and A[i2] = v2 where i1 and i2 are fresh values, // and v1 and v2 are distinct. // // Proof: let assume there is an Array A' such that A' = A. // Then A[i1] == A'[i1] and A[i2] == A'[i2]. Since, i1 and i2 are fresh, // A' does not have an entry for i1 or i2, So A'[i1] == A'[i2] == A'.m_else. // Thus, A[i1] == A[i2] which is a contradiction since v1 != v2 and A[i1] = v1 and A[i2] = v2. TRACE("array_factory_bug", tout << "v1: " << mk_pp(v1, m_manager) << " v2: " << mk_pp(v2, m_manager) << "\n";); ptr_buffer args1; ptr_buffer args2; bool found = false; unsigned arity = get_array_arity(s); for (unsigned i = 0; i < arity; i++) { sort * d = get_array_domain(s, i); if (!found) { expr * arg1 = m_model.get_fresh_value(d); expr * arg2 = m_model.get_fresh_value(d); if (arg1 != nullptr && arg2 != nullptr) { found = true; args1.push_back(arg1); args2.push_back(arg2); continue; } } expr * arg = m_model.get_some_value(d); args1.push_back(arg); args2.push_back(arg); } if (found) { func_interp * fi; expr * val = mk_array_interp(s, fi); fi->insert_entry(args1.c_ptr(), v1); fi->insert_entry(args2.c_ptr(), v2); return val; } } } // TODO: use more expensive procedures to create a fresh array value. // Example: track the values used in the domain. // Remark: in the current implementation, this function // will never fail, since if a type is finite, then // type_pred will be applied and get_fresh_value will not // need to be used. // failed to create a fresh array value TRACE("array_factory_bug", tout << "failed to build fresh array value\n";); return nullptr; } z3-z3-4.8.7/src/model/array_factory.h000066400000000000000000000015511356505360400173360ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: array_factory.h Abstract: Author: Leonardo de Moura (leonardo) 2008-10-28. Revision History: --*/ #ifndef ARRAY_FACTORY_H_ #define ARRAY_FACTORY_H_ #include "model/struct_factory.h" class func_interp; func_decl * mk_aux_decl_for_array_sort(ast_manager & m, sort * s); class array_factory : public struct_factory { expr * mk_array_interp(sort * s, func_interp * & fi); void get_some_args_for(sort * s, ptr_buffer & args); bool mk_two_diff_values_for(sort * s); public: array_factory(ast_manager & m, model_core & md); ~array_factory() override {} expr * get_some_value(sort * s) override; bool get_some_values(sort * s, expr_ref & v1, expr_ref & v2) override; expr * get_fresh_value(sort * s) override; }; #endif /* ARRAY_FACTORY_H_ */ z3-z3-4.8.7/src/model/datatype_factory.cpp000066400000000000000000000235551356505360400203760ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: datatype_factory.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-11-06. Revision History: --*/ #include "model/datatype_factory.h" #include "model/model_core.h" #include "ast/ast_pp.h" #include "ast/expr_functors.h" datatype_factory::datatype_factory(ast_manager & m, model_core & md): struct_factory(m, m.mk_family_id("datatype"), md), m_util(m) { } expr * datatype_factory::get_some_value(sort * s) { value_set * set = nullptr; if (m_sort2value_set.find(s, set) && !set->empty()) return *(set->begin()); func_decl * c = m_util.get_non_rec_constructor(s); ptr_vector args; unsigned num = c->get_arity(); for (unsigned i = 0; i < num; i++) { args.push_back(m_model.get_some_value(c->get_domain(i))); } expr * r = m_manager.mk_app(c, args.size(), args.c_ptr()); register_value(r); TRACE("datatype", tout << mk_pp(r, m_util.get_manager()) << "\n";); return r; } /** \brief Return the last fresh (or almost) fresh value of sort s. */ expr * datatype_factory::get_last_fresh_value(sort * s) { expr * val = nullptr; if (m_last_fresh_value.find(s, val)) { TRACE("datatype", tout << "cached fresh value: " << mk_pp(val, m_manager) << "\n";); return val; } value_set * set = get_value_set(s); if (set->empty()) val = get_some_value(s); else val = *(set->begin()); if (m_util.is_recursive(s)) m_last_fresh_value.insert(s, val); return val; } bool datatype_factory::is_subterm_of_last_value(app* e) { expr* last; if (!m_last_fresh_value.find(m_manager.get_sort(e), last)) { return false; } contains_app contains(m_manager, e); bool result = contains(last); TRACE("datatype", tout << mk_pp(e, m_manager) << " in " << mk_pp(last, m_manager) << " " << result << "\n";); return result; } /** \brief Create an almost fresh value. If s is recursive, then the result is not 0. It also updates m_last_fresh_value */ expr * datatype_factory::get_almost_fresh_value(sort * s) { value_set * set = get_value_set(s); if (set->empty()) { expr * val = get_some_value(s); SASSERT(val); if (m_util.is_recursive(s)) m_last_fresh_value.insert(s, val); return val; } // Traverse constructors, and try to invoke get_fresh_value of one of the arguments (if the argument is not a sibling datatype of s). // If the argumet is a sibling datatype of s, then // use get_last_fresh_value. ptr_vector const & constructors = *m_util.get_datatype_constructors(s); for (func_decl * constructor : constructors) { expr_ref_vector args(m_manager); bool found_fresh_arg = false; bool recursive = false; unsigned num = constructor->get_arity(); for (unsigned i = 0; i < num; i++) { sort * s_arg = constructor->get_domain(i); if (!found_fresh_arg && (!m_util.is_datatype(s_arg) || !m_util.are_siblings(s, s_arg))) { expr * new_arg = m_model.get_fresh_value(s_arg); if (new_arg != nullptr) { found_fresh_arg = true; args.push_back(new_arg); continue; } } if (!found_fresh_arg && m_util.is_datatype(s_arg) && m_util.are_siblings(s, s_arg)) { recursive = true; expr * last_fresh = get_last_fresh_value(s_arg); args.push_back(last_fresh); } else { expr * some_arg = m_model.get_some_value(s_arg); args.push_back(some_arg); } } if (recursive || found_fresh_arg) { app * new_value = m_manager.mk_app(constructor, args.size(), args.c_ptr()); SASSERT(!found_fresh_arg || !set->contains(new_value)); register_value(new_value); if (m_util.is_recursive(s)) { if (is_subterm_of_last_value(new_value)) { new_value = static_cast(m_last_fresh_value.find(s)); } else { m_last_fresh_value.insert(s, new_value); } } TRACE("datatype", tout << "almost fresh: " << mk_pp(new_value, m_manager) << "\n";); return new_value; } } SASSERT(!m_util.is_recursive(s)); return nullptr; } expr * datatype_factory::get_fresh_value(sort * s) { TRACE("datatype", tout << "generating fresh value for: " << s->get_name() << "\n";); value_set * set = get_value_set(s); // Approach 0) // if no value for s was generated so far, then used get_some_value if (set->empty()) { expr * val = get_some_value(s); if (m_util.is_recursive(s)) m_last_fresh_value.insert(s, val); TRACE("datatype", tout << "0. result: " << mk_pp(val, m_manager) << "\n";); return val; } // Approach 1) // Traverse constructors, and try to invoke get_fresh_value of one of the // arguments (if the argument is not a sibling datatype of s). // Two datatypes are siblings if they were defined together in the same mutually recursive definition. ptr_vector const & constructors = *m_util.get_datatype_constructors(s); for (func_decl * constructor : constructors) { expr_ref_vector args(m_manager); bool found_fresh_arg = false; unsigned num = constructor->get_arity(); for (unsigned i = 0; i < num; i++) { sort * s_arg = constructor->get_domain(i); if (!found_fresh_arg && (!m_util.is_recursive(s) || !m_util.is_datatype(s_arg) || !m_util.are_siblings(s, s_arg))) { expr * new_arg = m_model.get_fresh_value(s_arg); if (new_arg != nullptr) { found_fresh_arg = true; args.push_back(new_arg); continue; } } expr * some_arg = m_model.get_some_value(s_arg); args.push_back(some_arg); } expr_ref new_value(m_manager); new_value = m_manager.mk_app(constructor, args.size(), args.c_ptr()); CTRACE("datatype", found_fresh_arg && set->contains(new_value), tout << mk_pp(new_value, m_manager) << "\n";); SASSERT(!found_fresh_arg || !set->contains(new_value)); if (!set->contains(new_value)) { register_value(new_value); if (m_util.is_recursive(s)) m_last_fresh_value.insert(s, new_value); TRACE("datatype", tout << "1. result: " << mk_pp(new_value, m_manager) << "\n";); return new_value; } } // Approach 2) // For recursive datatypes. // search for constructor... unsigned num_iterations = 0; if (m_util.is_recursive(s)) { while(true) { ++num_iterations; TRACE("datatype", tout << mk_pp(get_last_fresh_value(s), m_manager) << "\n";); ptr_vector const & constructors = *m_util.get_datatype_constructors(s); for (func_decl * constructor : constructors) { expr_ref_vector args(m_manager); bool found_sibling = false; unsigned num = constructor->get_arity(); TRACE("datatype", tout << "checking constructor: " << constructor->get_name() << "\n";); for (unsigned i = 0; i < num; i++) { sort * s_arg = constructor->get_domain(i); TRACE("datatype", tout << mk_pp(s, m_manager) << " " << mk_pp(s_arg, m_manager) << " are_siblings " << m_util.are_siblings(s, s_arg) << " is_datatype " << m_util.is_datatype(s_arg) << " found_sibling " << found_sibling << "\n";); if (!found_sibling && m_util.is_datatype(s_arg) && m_util.are_siblings(s, s_arg)) { found_sibling = true; expr * maybe_new_arg = nullptr; if (num_iterations <= 1) { maybe_new_arg = get_almost_fresh_value(s_arg); } else { maybe_new_arg = get_fresh_value(s_arg); } if (!maybe_new_arg) { TRACE("datatype", tout << "no argument found for " << mk_pp(s_arg, m_manager) << "\n";); maybe_new_arg = m_model.get_some_value(s_arg); found_sibling = false; } SASSERT(maybe_new_arg); args.push_back(maybe_new_arg); } else { expr * some_arg = m_model.get_some_value(s_arg); SASSERT(some_arg); args.push_back(some_arg); } } if (found_sibling) { expr_ref new_value(m_manager); new_value = m_manager.mk_app(constructor, args.size(), args.c_ptr()); TRACE("datatype", tout << "potential new value: " << mk_pp(new_value, m_manager) << "\n";); m_last_fresh_value.insert(s, new_value); if (!set->contains(new_value)) { register_value(new_value); TRACE("datatype", tout << "2. result: " << mk_pp(new_value, m_manager) << "\n";); return new_value; } } } } } // Approach 3) // for non-recursive datatypes. // Search for value that was not created before. SASSERT(!m_util.is_recursive(s)); return nullptr; } z3-z3-4.8.7/src/model/datatype_factory.h000066400000000000000000000014631356505360400200350ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: datatype_factory.h Abstract: Author: Leonardo de Moura (leonardo) 2008-11-06. Revision History: --*/ #ifndef DATATYPE_FACTORY_H_ #define DATATYPE_FACTORY_H_ #include "model/struct_factory.h" #include "ast/datatype_decl_plugin.h" class datatype_factory : public struct_factory { datatype_util m_util; obj_map m_last_fresh_value; expr * get_last_fresh_value(sort * s); expr * get_almost_fresh_value(sort * s); bool is_subterm_of_last_value(app* e); public: datatype_factory(ast_manager & m, model_core & md); ~datatype_factory() override {} expr * get_some_value(sort * s) override; expr * get_fresh_value(sort * s) override; }; #endif /* DATATYPE_FACTORY_H_ */ z3-z3-4.8.7/src/model/func_interp.cpp000066400000000000000000000311171356505360400173410ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: func_interp.cpp Abstract: See func_interp.h Author: Leonardo de Moura (leonardo) 2010-12-30. Revision History: --*/ #include "util/obj_hashtable.h" #include "ast/rewriter/var_subst.h" #include "ast/ast_pp.h" #include "ast/ast_smt2_pp.h" #include "ast/ast_util.h" #include "model/func_interp.h" #include "ast/array_decl_plugin.h" 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)); m.inc_ref(result); for (unsigned i = 0; i < arity; i++) { expr * arg = args[i]; //SASSERT(is_ground(arg)); if (!m.is_value(arg)) m_args_are_values = false; m.inc_ref(arg); m_args[i] = arg; } } func_entry * func_entry::mk(ast_manager & m, unsigned arity, expr * const * args, expr * result) { small_object_allocator & allocator = m.get_allocator(); unsigned sz = get_obj_size(arity); void * mem = allocator.allocate(sz); return new (mem) func_entry(m, arity, args, result); } void func_entry::set_result(ast_manager & m, expr * r) { m.inc_ref(r); m.dec_ref(m_result); m_result = r; } bool func_entry::eq_args(ast_manager & m, unsigned arity, expr * const * args) const { unsigned i = 0; for (; i < arity; i++) { if (!m.are_equal(m_args[i], args[i])) return false; } return true; } void func_entry::deallocate(ast_manager & m, unsigned arity) { for (unsigned i = 0; i < arity; i++) { m.dec_ref(m_args[i]); } m.dec_ref(m_result); small_object_allocator & allocator = m.get_allocator(); unsigned sz = get_obj_size(arity); allocator.deallocate(sz, this); } func_interp::func_interp(ast_manager & m, unsigned arity): m_manager(m), m_arity(arity), m_else(nullptr), m_args_are_values(true), m_interp(nullptr), m_array_interp(nullptr) { } func_interp::~func_interp() { for (func_entry* curr : m_entries) { curr->deallocate(m_manager, m_arity); } m_manager.dec_ref(m_else); m_manager.dec_ref(m_interp); m_manager.dec_ref(m_array_interp); } func_interp * func_interp::copy() const { func_interp * new_fi = alloc(func_interp, m_manager, m_arity); for (func_entry * curr : m_entries) { new_fi->insert_new_entry(curr->get_args(), curr->get_result()); } new_fi->set_else(m_else); return new_fi; } void func_interp::reset_interp_cache() { m_manager.dec_ref(m_interp); m_manager.dec_ref(m_array_interp); m_interp = nullptr; m_array_interp = nullptr; } bool func_interp::is_fi_entry_expr(expr * e, ptr_vector & args) { args.reset(); expr* c, *t, *f, *a0, *a1; if (!m().is_ite(e, c, t, f)) { return false; } if (!is_ground(t) || (m_arity == 0) || (m_arity == 1 && !m().is_eq(c, a0, a1)) || (m_arity > 1 && (!m().is_and(c) || to_app(c)->get_num_args() != m_arity))) return false; args.resize(m_arity); for (unsigned i = 0; i < m_arity; i++) { expr * ci = (m_arity == 1 && i == 0) ? c : to_app(c)->get_arg(i); if (!m().is_eq(ci, a0, a1)) return false; if (is_var(a0) && to_var(a0)->get_idx() == i) args[i] = a1; else if (is_var(a1) && to_var(a1)->get_idx() == i) args[i] = a0; else return false; } return true; } void func_interp::set_else(expr * e) { if (e == m_else) return; reset_interp_cache(); TRACE("func_interp", tout << "set_else: " << expr_ref(e, m()) << "\n";); ptr_vector args; while (e && is_fi_entry_expr(e, args)) { insert_entry(args.c_ptr(), to_app(e)->get_arg(1)); e = to_app(e)->get_arg(2); } m_manager.inc_ref(e); m_manager.dec_ref(m_else); m_else = e; } /** \brief Return true if the interpretation represents the constant function. */ bool func_interp::is_constant() const { if (is_partial()) return false; if (!is_ground(m_else)) return false; for (func_entry* curr : m_entries) { if (curr->get_result() != m_else) return false; } return true; } /** \brief Return a func_entry e such that m().are_equal(e.m_args[i], args[i]) for all i in [0, m_arity). If such entry does not exist then return 0, and store set args_are_values to true if for all entries e e.args_are_values() is true. */ func_entry * func_interp::get_entry(expr * const * args) const { for (func_entry* curr : m_entries) { if (curr->eq_args(m(), m_arity, args)) return curr; } return nullptr; } void func_interp::insert_entry(expr * const * args, expr * r) { reset_interp_cache(); func_entry * entry = get_entry(args); if (entry != nullptr) { entry->set_result(m_manager, r); return; } insert_new_entry(args, r); } void func_interp::insert_new_entry(expr * const * args, expr * r) { reset_interp_cache(); CTRACE("func_interp_bug", get_entry(args) != 0, tout << "Old: " << mk_ismt2_pp(get_entry(args)->m_result, m_manager) << "\n"; tout << "Args:"; for (unsigned i = 0; i < m_arity; i++) { tout << mk_ismt2_pp(get_entry(args)->get_arg(i), m_manager) << "\n"; } tout << "New: " << mk_ismt2_pp(r, m_manager) << "\n"; tout << "Args:"; for (unsigned i = 0; i < m_arity; i++) { tout << mk_ismt2_pp(args[i], m_manager) << "\n"; } tout << "Old: " << mk_ismt2_pp(get_entry(args)->get_result(), m_manager) << "\n"; ); SASSERT(get_entry(args) == nullptr); func_entry * new_entry = func_entry::mk(m_manager, m_arity, args, r); if (!new_entry->args_are_values()) m_args_are_values = false; m_entries.push_back(new_entry); } bool func_interp::eval_else(expr * const * args, expr_ref & result) const { if (m_else == nullptr) return false; var_subst s(m_manager, false); SASSERT(!s.std_order()); // (VAR 0) <- args[0], (VAR 1) <- args[1], ... result = s(m_else, m_arity, args); return true; } /** \brief Return the result with the maximal number of occurrencies in m_entries. */ expr * func_interp::get_max_occ_result() const { if (m_entries.empty()) return nullptr; obj_map num_occs; expr * r_max = nullptr; unsigned max = 0; for (func_entry * curr : m_entries) { expr * r = curr->get_result(); unsigned occs = 0; num_occs.find(r, occs); occs++; num_occs.insert(r, occs); if (occs > max) { max = occs; r_max = r; } } return r_max; } /** \brief Remove entries e such that e.get_result() == m_else. */ void func_interp::compress() { if (m_else == nullptr || m_entries.empty()) return; // nothing to be done if (!is_ground(m_else)) return; // forall entries e in m_entries e.get_result() is ground unsigned j = 0; m_args_are_values = true; for (func_entry * curr : m_entries) { if (curr->get_result() != m_else) { m_entries[j++] = curr; if (!curr->args_are_values()) m_args_are_values = false; } else { curr->deallocate(m_manager, m_arity); } } if (j < m_entries.size()) { reset_interp_cache(); m_entries.shrink(j); } // other compression, if else is a default branch. // or function encode identity. if (m_manager.is_false(m_else)) { expr_ref new_else(get_interp(), m_manager); for (func_entry * curr : m_entries) { curr->deallocate(m_manager, m_arity); } m_entries.reset(); reset_interp_cache(); m_manager.inc_ref(new_else); m_manager.dec_ref(m_else); m_else = new_else; } else if (!m_entries.empty() && is_identity()) { for (func_entry * curr : m_entries) { curr->deallocate(m_manager, m_arity); } m_entries.reset(); reset_interp_cache(); expr_ref new_else(m_manager.mk_var(0, m_manager.get_sort(m_else)), m_manager); m_manager.inc_ref(new_else); m_manager.dec_ref(m_else); m_else = new_else; } } /** * \brief check if function is identity */ bool func_interp::is_identity() const { if (m_arity != 1) return false; if (m_else == nullptr) return false; // all entries map a value to itself for (func_entry * curr : m_entries) { if (curr->get_arg(0) != curr->get_result()) return false; if (curr->get_result() == m_else) return false; } if (is_var(m_else)) return true; if (!m_manager.is_value(m_else)) return false; sort_size const& sz = m_manager.get_sort(m_else)->get_num_elements(); if (!sz.is_finite()) return false; // // the else entry is a value not covered by other entries // it, together with the entries covers the entire domain. // return (sz.size() == m_entries.size() + 1); } expr * func_interp::get_interp_core() const { if (m_else == nullptr) return nullptr; expr * r = m_else; ptr_buffer vars; for (func_entry * curr : m_entries) { if (m_else == curr->get_result()) { continue; } if (vars.empty()) { for (unsigned i = 0; i < m_arity; i++) { vars.push_back(m_manager.mk_var(i, m_manager.get_sort(curr->get_arg(i)))); } } ptr_buffer eqs; for (unsigned i = 0; i < m_arity; i++) { eqs.push_back(m_manager.mk_eq(vars[i], curr->get_arg(i))); } SASSERT(eqs.size() == m_arity); expr * cond = mk_and(m_manager, eqs.size(), eqs.c_ptr()); expr * th = curr->get_result(); if (m_manager.is_true(th)) { r = m_manager.mk_or(cond, r); } else if (m_manager.is_false(th)) { r = m_manager.mk_and(m_manager.mk_not(cond), r); } else { r = m_manager.mk_ite(cond, th, r); } } return r; } expr* func_interp::get_array_interp_core(func_decl * f) const { if (m_else == nullptr) return nullptr; ptr_vector domain; for (sort* s : *f) { domain.push_back(s); } expr* r; bool ground = is_ground(m_else); for (func_entry * curr : m_entries) { ground &= is_ground(curr->get_result()); for (unsigned i = 0; i < m_arity; i++) { ground &= is_ground(curr->get_arg(i)); } } if (!ground) { r = get_interp(); if (!r) return r; sort_ref_vector vars(m_manager); svector var_names; for (unsigned i = 0; i < m_arity; ++i) { var_names.push_back(symbol(i)); vars.push_back(domain.get(m_arity - i - 1)); } r = m_manager.mk_lambda(vars.size(), vars.c_ptr(), var_names.c_ptr(), r); return r; } expr_ref_vector args(m_manager); array_util autil(m_manager); sort_ref A(autil.mk_array_sort(domain.size(), domain.c_ptr(), m_manager.get_sort(m_else)), m_manager); r = autil.mk_const_array(A, m_else); for (func_entry * curr : m_entries) { expr * res = curr->get_result(); if (m_else == res) { continue; } args.reset(); args.push_back(r); for (unsigned i = 0; i < m_arity; i++) { args.push_back(curr->get_arg(i)); } args.push_back(res); r = autil.mk_store(args); } return r; } expr * func_interp::get_interp() const { if (m_interp != nullptr) return m_interp; expr * r = get_interp_core(); if (r != nullptr) { const_cast(this)->m_interp = r; m_manager.inc_ref(m_interp); } return r; } expr * func_interp::get_array_interp(func_decl * f) const { if (m_array_interp != nullptr) return m_array_interp; expr* r = get_array_interp_core(f); if (r != nullptr) { const_cast(this)->m_array_interp = r; m_manager.inc_ref(m_array_interp); } return r; } func_interp * func_interp::translate(ast_translation & translator) const { func_interp * new_fi = alloc(func_interp, translator.to(), m_arity); for (func_entry * curr : m_entries) { ptr_buffer new_args; for (unsigned i = 0; i < m_arity; i++) new_args.push_back(translator(curr->get_arg(i))); new_fi->insert_new_entry(new_args.c_ptr(), translator(curr->get_result())); } new_fi->set_else(translator(m_else)); return new_fi; } z3-z3-4.8.7/src/model/func_interp.h000066400000000000000000000103561356505360400170100ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: func_interp.h Abstract: Support for function graphs (aka interpretations for functions). They are used during model construction, and for evaluating expressions modulo a model. Main goal: Remove some code duplication and make the evaluator more efficient. Example of code duplication that existed in Z3: - smt_model_generator was creating func_values that were essentially partial func_interps - smt_model_generator was creating if-then-else (lambda) exprs representing func_values - the model object was converting these lambdas back into func_graphs (a limited version of func_interps). - the smt_model_finder needs to manipulate func_interps, but the func_values in smt_model_generator were private and too restrictive. Author: Leonardo de Moura (leonardo) 2010-12-30. Revision History: --*/ #ifndef FUNC_INTERP_H_ #define FUNC_INTERP_H_ #include "ast/ast.h" #include "ast/ast_translation.h" class func_interp; class func_entry { bool m_args_are_values; //!< true if is_value(m_args[i]) is true for all i in [0, arity) // m_result and m_args[i] must be ground terms. expr * m_result; expr * m_args[]; static unsigned get_obj_size(unsigned arity) { return sizeof(func_entry) + arity * sizeof(expr*); } func_entry(ast_manager & m, unsigned arity, expr * const * args, expr * result); friend class func_interp; void set_result(ast_manager & m, expr * r); public: static func_entry * mk(ast_manager & m, unsigned arity, expr * const * args, expr * result); bool args_are_values() const { return m_args_are_values; } void deallocate(ast_manager & m, unsigned arity); expr * get_result() const { return m_result; } expr * get_arg(unsigned idx) const { return m_args[idx]; } expr * const * get_args() const { return m_args; } /** \brief Return true if m.are_equal(m_args[i], args[i]) for all i in [0, arity) */ bool eq_args(ast_manager & m, unsigned arity, expr * const * args) const; }; class func_interp { ast_manager & m_manager; unsigned m_arity; ptr_vector m_entries; expr * m_else; bool m_args_are_values; //!< true if forall e in m_entries e.args_are_values() == true expr * m_interp; //!< cache for representing the whole interpretation as a single expression (it uses ite terms). expr * m_array_interp; // ::const_iterator begin() const { return m_entries.begin(); } ptr_vector::const_iterator end() const { return m_entries.end(); } func_entry const * const * get_entries() const { return m_entries.c_ptr(); } func_entry const * get_entry(unsigned idx) const { return m_entries[idx]; } expr * get_max_occ_result() const; void compress(); expr * get_interp() const; expr * get_array_interp(func_decl* f) const; func_interp * translate(ast_translation & translator) const; private: bool is_fi_entry_expr(expr * e, ptr_vector & args); bool is_identity() const; }; #endif z3-z3-4.8.7/src/model/model.cpp000066400000000000000000000356461356505360400161400ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: model.cpp Abstract: Author: Leonardo de Moura (leonardo) 2011-04-30. Revision History: --*/ #include "util/top_sort.h" #include "ast/ast_pp.h" #include "ast/ast_ll_pp.h" #include "ast/rewriter/var_subst.h" #include "ast/rewriter/th_rewriter.h" #include "ast/array_decl_plugin.h" #include "ast/well_sorted.h" #include "ast/used_symbols.h" #include "ast/for_each_expr.h" #include "ast/for_each_ast.h" #include "model/model.h" #include "model/model_params.hpp" #include "model/model_evaluator.h" #include "model/array_factory.h" #include "model/value_factory.h" #include "model/seq_factory.h" #include "model/datatype_factory.h" #include "model/numeral_factory.h" model::model(ast_manager & m): model_core(m), m_mev(*this), m_cleaned(false), m_inline(false) { } model::~model() { for (auto & kv : m_usort2universe) { m.dec_ref(kv.m_key); m.dec_array_ref(kv.m_value->size(), kv.m_value->c_ptr()); dealloc(kv.m_value); } } void model::updt_params(params_ref const & p) { model_params mp(p); m_inline = mp.inline_def(); m_mev.updt_params(p); } void model::copy_const_interps(model const & source) { for (auto const& kv : source.m_interp) register_decl(kv.m_key, kv.m_value); } void model::copy_func_interps(model const & source) { for (auto const& kv : source.m_finterp) register_decl(kv.m_key, kv.m_value->copy()); } void model::copy_usort_interps(model const & source) { for (auto const& kv : source.m_usort2universe) register_usort(kv.m_key, kv.m_value->size(), kv.m_value->c_ptr()); } model * model::copy() const { model * mdl = alloc(model, m); mdl->copy_const_interps(*this); mdl->copy_func_interps(*this); mdl->copy_usort_interps(*this); return mdl; } bool model::eval_expr(expr * e, expr_ref & result, bool model_completion) { scoped_model_completion _smc(*this, model_completion); try { result = (*this)(e); return true; } catch (model_evaluator_exception & ex) { (void)ex; TRACE("model_evaluator", tout << ex.msg() << "\n";); return false; } } value_factory* model::get_factory(sort* s) { if (m_factories.plugins().empty()) { seq_util su(m); m_factories.register_plugin(alloc(array_factory, m, *this)); m_factories.register_plugin(alloc(datatype_factory, m, *this)); m_factories.register_plugin(alloc(bv_factory, m)); m_factories.register_plugin(alloc(arith_factory, m)); m_factories.register_plugin(alloc(seq_factory, m, su.get_family_id(), *this)); } family_id fid = s->get_family_id(); return m_factories.get_plugin(fid); } expr * model::get_some_value(sort * s) { ptr_vector * u = nullptr; if (m_usort2universe.find(s, u)) { if (!u->empty()) return u->get(0); } return m.get_some_value(s); } expr * model::get_fresh_value(sort * s) { return get_factory(s)->get_fresh_value(s); } bool model::get_some_values(sort * s, expr_ref& v1, expr_ref& v2) { return get_factory(s)->get_some_values(s, v1, v2); } ptr_vector const & model::get_universe(sort * s) const { return *m_usort2universe[s]; } bool model::has_uninterpreted_sort(sort * s) const { ptr_vector * u = nullptr; m_usort2universe.find(s, u); return u != nullptr; } unsigned model::get_num_uninterpreted_sorts() const { return m_usorts.size(); } sort * model::get_uninterpreted_sort(unsigned idx) const { return m_usorts[idx]; } void model::register_usort(sort * s, unsigned usize, expr * const * universe) { sort2universe::obj_map_entry * entry = m_usort2universe.insert_if_not_there2(s, 0); m.inc_array_ref(usize, universe); if (entry->get_data().m_value == 0) { // new entry m_usorts.push_back(s); m.inc_ref(s); ptr_vector * new_u = alloc(ptr_vector); new_u->append(usize, universe); entry->get_data().m_value = new_u; } else { // updating ptr_vector * u = entry->get_data().m_value; SASSERT(u); m.dec_array_ref(u->size(), u->c_ptr()); u->append(usize, universe); } } model * model::translate(ast_translation & translator) const { model * res = alloc(model, translator.to()); // Translate const interps for (auto const& kv : m_interp) res->register_decl(translator(kv.m_key), translator(kv.m_value)); // Translate func interps for (auto const& kv : m_finterp) { func_interp * fi = kv.m_value; res->register_decl(translator(kv.m_key), fi->translate(translator)); } // Translate usort interps for (auto const& kv : m_usort2universe) { ptr_vector new_universe; for (expr* e : *kv.m_value) { new_universe.push_back(translator(e)); } res->register_usort(translator(kv.m_key), new_universe.size(), new_universe.c_ptr()); } return res; } struct model::top_sort : public ::top_sort { th_rewriter m_rewrite; obj_map m_occur_count; top_sort(ast_manager& m): m_rewrite(m) { params_ref p; p.set_bool("elim_ite", false); m_rewrite.updt_params(p); } void add_occurs(func_decl* f) { m_occur_count.insert(f, occur_count(f) + 1); } unsigned occur_count(func_decl* f) const { unsigned count = 0; m_occur_count.find(f, count); return count; } ~top_sort() override {} }; void model::compress() { if (m_cleaned) return; // stratify m_finterp and m_decls in a topological sort // such that functions f1 < f2 then f1 does not use f2. // then for each function in order clean-up the interpretations // by substituting in auxiliary definitions that can be eliminated. func_decl_ref_vector pinned(m); while (true) { top_sort ts(m); collect_deps(ts); ts.topological_sort(); for (func_decl * f : ts.top_sorted()) { cleanup_interp(ts, f); } func_decl_set removed; ts.m_occur_count.reset(); for (func_decl * f : ts.top_sorted()) { collect_occs(ts, f); } // remove auxiliary declarations that are not used. for (func_decl * f : ts.top_sorted()) { if (f->is_skolem() && ts.occur_count(f) == 0) { pinned.push_back(f); unregister_decl(f); removed.insert(f); } } if (removed.empty()) break; TRACE("model", tout << "remove\n"; for (func_decl* f : removed) tout << f->get_name() << "\n";); remove_decls(m_decls, removed); remove_decls(m_func_decls, removed); remove_decls(m_const_decls, removed); } m_cleaned = true; reset_eval_cache(); } void model::collect_deps(top_sort& ts) { for (auto const& kv : m_finterp) { ts.insert(kv.m_key, collect_deps(ts, kv.m_value)); } for (auto const& kv : m_interp) { ts.insert(kv.m_key, collect_deps(ts, kv.m_value)); } } struct model::deps_collector { model& m; top_sort& ts; func_decl_set& s; array_util autil; deps_collector(model& m, top_sort& ts, func_decl_set& s): m(m), ts(ts), s(s), autil(m.get_manager()) {} void operator()(app* a) { func_decl* f = a->get_decl(); if (autil.is_as_array(f)) { f = autil.get_as_array_func_decl(a); } if (m.has_interpretation(f)) { s.insert(f); ts.add_occurs(f); } } void operator()(expr* ) {} }; struct model::occs_collector { top_sort& ts; occs_collector(top_sort& ts): ts(ts) {} void operator()(func_decl* f) { ts.add_occurs(f); } void operator()(ast*) {} }; model::func_decl_set* model::collect_deps(top_sort& ts, expr * e) { func_decl_set* s = alloc(func_decl_set); deps_collector collector(*this, ts, *s); if (e) for_each_expr(collector, e); return s; } model::func_decl_set* model::collect_deps(top_sort& ts, func_interp * fi) { func_decl_set* s = alloc(func_decl_set); deps_collector collector(*this, ts, *s); 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; } /** \brief Inline interpretations of skolem functions */ void model::cleanup_interp(top_sort& ts, func_decl* f) { unsigned pid = ts.partition_id(f); expr * e1 = get_const_interp(f); if (e1) { expr_ref e2 = cleanup_expr(ts, e1, pid); if (e2 != e1) register_decl(f, e2); return; } func_interp* fi = get_func_interp(f); if (fi) { e1 = fi->get_else(); expr_ref e2 = cleanup_expr(ts, e1, pid); if (e1 != e2) fi->set_else(e2); for (auto& fe : *fi) { e2 = cleanup_expr(ts, fe->get_result(), pid); if (e2 != fe->get_result()) { fi->insert_entry(fe->get_args(), e2); } } } } void model::collect_occs(top_sort& ts, func_decl* f) { expr * e = get_const_interp(f); if (e) { collect_occs(ts, e); } else { func_interp* fi = get_func_interp(f); if (fi) { e = fi->get_else(); if (e != nullptr) collect_occs(ts, e); for (auto const& fe : *fi) { collect_occs(ts, fe->get_result()); for (unsigned i = 0; i < fi->get_arity(); ++i) { collect_occs(ts, fe->get_arg(i)); } } } } } void model::collect_occs(top_sort& ts, expr* e) { occs_collector collector(ts); for_each_ast(collector, e, true); } bool model::can_inline_def(top_sort& ts, func_decl* f) { if (ts.occur_count(f) <= 1) return true; func_interp* fi = get_func_interp(f); if (!fi) return false; if (fi->get_else() == nullptr) return false; if (m_inline) return true; expr* e = fi->get_else(); obj_hashtable subs; ptr_buffer todo; todo.push_back(e); while (!todo.empty()) { if (fi->num_entries() + subs.size() > 8) return false; expr* e = todo.back(); todo.pop_back(); if (subs.contains(e)) continue; subs.insert(e); if (is_app(e)) { for (expr* arg : *to_app(e)) { todo.push_back(arg); } } else if (is_quantifier(e)) { todo.push_back(to_quantifier(e)->get_expr()); } } return true; } expr_ref model::cleanup_expr(top_sort& ts, expr* e, unsigned current_partition) { if (!e) return expr_ref(nullptr, m); TRACE("model", tout << "cleaning up:\n" << mk_pp(e, m) << "\n";); obj_map cache; expr_ref_vector trail(m); ptr_buffer todo; ptr_buffer args; todo.push_back(e); array_util autil(m); func_interp* fi = nullptr; unsigned pid = 0; expr_ref new_t(m); while (!todo.empty()) { expr* a = todo.back(); switch(a->get_kind()) { case AST_APP: { app * t = to_app(a); func_decl* f = t->get_decl(); bool visited = true; args.reset(); for (expr* t_arg : *t) { expr * arg = nullptr; if (!cache.find(t_arg, arg)) { visited = false; todo.push_back(t_arg); } else { args.push_back(arg); } } if (!visited) { continue; } fi = nullptr; new_t = nullptr; sort_ref_vector domain(m); if (autil.is_as_array(a)) { func_decl* f = autil.get_as_array_func_decl(a); // only expand auxiliary definitions that occur once. if (can_inline_def(ts, f)) { fi = get_func_interp(f); new_t = fi->get_array_interp(f); TRACE("model", tout << "array interpretation:" << new_t << "\n";); } } if (new_t) { // noop } else if (f->is_skolem() && can_inline_def(ts, f) && (fi = get_func_interp(f)) && fi->get_interp() && (!ts.partition_ids().find(f, pid) || pid != current_partition)) { var_subst vs(m, false); new_t = vs(fi->get_interp(), args.size(), args.c_ptr()); } #if 0 else if (is_uninterp_const(a) && !get_const_interp(f)) { new_t = get_some_value(f->get_range()); register_decl(f, new_t); } #endif else { new_t = ts.m_rewrite.mk_app(f, args.size(), args.c_ptr()); } if (t != new_t.get()) trail.push_back(new_t); todo.pop_back(); cache.insert(t, new_t); break; } default: SASSERT(a != nullptr); cache.insert(a, a); todo.pop_back(); break; } } ts.m_rewrite(cache[e], new_t); return new_t; } void model::remove_decls(ptr_vector & decls, func_decl_set const & s) { unsigned j = 0; for (func_decl* f : decls) { if (!s.contains(f)) { decls[j++] = f; } } decls.shrink(j); } expr_ref model::get_inlined_const_interp(func_decl* f) { expr* v = get_const_interp(f); if (!v) return expr_ref(nullptr, m); top_sort st(m); expr_ref result1(v, m); expr_ref result2 = cleanup_expr(st, v, UINT_MAX); while (result1 != result2) { result1 = result2; result2 = cleanup_expr(st, result1, UINT_MAX); } return result2; } expr_ref model::operator()(expr* t) { return m_mev(t); } void model::set_solver(expr_solver* s) { m_mev.set_solver(s); } bool model::has_solver() { return m_mev.has_solver(); } expr_ref_vector model::operator()(expr_ref_vector const& ts) { expr_ref_vector rs(m); for (expr* t : ts) rs.push_back((*this)(t)); return rs; } bool model::is_true(expr* t) { return m.is_true((*this)(t)); } bool model::is_false(expr* t) { return m.is_false((*this)(t)); } bool model::is_true(expr_ref_vector const& ts) { for (expr* t : ts) if (!is_true(t)) return false; return true; } bool model::is_false(expr_ref_vector const& ts) { for (expr* t : ts) if (is_false(t)) return true; return false; } bool model::are_equal(expr* s, expr* t) { return m_mev.are_equal(s, t); } void model::reset_eval_cache() { m_mev.reset(); } z3-z3-4.8.7/src/model/model.h000066400000000000000000000073011356505360400155700ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: model.h Abstract: Model for satisfiable formulas Author: Leonardo de Moura (leonardo) 2011-04-30. Revision History: --*/ #ifndef MODEL_H_ #define MODEL_H_ #include "util/ref.h" #include "util/vector.h" #include "ast/ast_translation.h" #include "util/plugin_manager.h" #include "model/model_core.h" #include "model/model_evaluator.h" #include "model/value_factory.h" class model; typedef ref model_ref; class model : public model_core { protected: typedef obj_map*> sort2universe; typedef obj_hashtable func_decl_set; ptr_vector m_usorts; sort2universe m_usort2universe; model_evaluator m_mev; bool m_cleaned; bool m_inline; plugin_manager m_factories; struct deps_collector; struct occs_collector; struct top_sort; func_decl_set* collect_deps(top_sort& ts, expr * e); func_decl_set* collect_deps(top_sort& ts, func_interp* fi); void collect_deps(top_sort& ts); void collect_occs(top_sort& ts, func_decl* f); void collect_occs(top_sort& ts, expr* e); void cleanup_interp(top_sort& ts, func_decl * f); expr_ref cleanup_expr(top_sort& ts, expr* e, unsigned current_partition); void remove_decls(ptr_vector & decls, func_decl_set const & s); bool can_inline_def(top_sort& ts, func_decl* f); value_factory* get_factory(sort* s); public: model(ast_manager & m); ~model() override; void copy_func_interps(model const & source); void copy_const_interps(model const & source); void copy_usort_interps(model const & source); model * copy() const; bool eval_expr(expr * e, expr_ref & result, bool model_completion = false); expr * get_some_value(sort * s) override; expr * get_fresh_value(sort * s) override; bool get_some_values(sort * s, expr_ref & v1, expr_ref & v2) override; ptr_vector const & get_universe(sort * s) const override; unsigned get_num_uninterpreted_sorts() const override; sort * get_uninterpreted_sort(unsigned idx) const override; bool has_uninterpreted_sort(sort * s) const; expr_ref get_inlined_const_interp(func_decl* f); // // Primitives for building models // void register_usort(sort * s, unsigned usize, expr * const * universe); // Model translation // model * translate(ast_translation & translator) const; void compress(); void set_model_completion(bool f) { m_mev.set_model_completion(f); } void updt_params(params_ref const & p); /** * evaluation using the model evaluator. Caches results. */ expr_ref operator()(expr* t); expr_ref_vector operator()(expr_ref_vector const& ts); bool is_true(expr* t); bool is_false(expr* t); bool is_true(expr_ref_vector const& ts); bool is_false(expr_ref_vector const& ts); bool are_equal(expr* s, expr* t); void reset_eval_cache(); bool has_solver(); void set_solver(expr_solver* solver); class scoped_model_completion { bool m_old_completion; model& m_model; public: scoped_model_completion(model& m, bool c): m_old_completion(m.m_mev.get_model_completion()), m_model(m) { m.set_model_completion(c); } scoped_model_completion(model_ref& m, bool c): m_old_completion(m->m_mev.get_model_completion()), m_model(*m.get()) { m->set_model_completion(c); } ~scoped_model_completion() { m_model.set_model_completion(m_old_completion); } }; }; #endif /* MODEL_H_ */ z3-z3-4.8.7/src/model/model2expr.cpp000066400000000000000000000111371356505360400171060ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: model2expr.cpp Abstract: Convert model to logical formula that forces it. Author: Nikolaj Bjorner (nbjorner) 2012-09-17 Revision History: --*/ #include "model/model2expr.h" #include "ast/for_each_ast.h" #include "ast/rewriter/bool_rewriter.h" #include "ast/rewriter/var_subst.h" struct for_each_symbol_proc { symbol_set& m_symbols; for_each_symbol_proc(symbol_set& syms): m_symbols(syms) {} void operator()(func_decl* n) { m_symbols.insert(n->get_name()); } void operator()(quantifier* n) { for (unsigned i = 0; i < n->get_num_decls(); ++i) { m_symbols.insert(n->get_decl_name(i)); } } void operator()(var* n) {} void operator()(sort* s) {} void operator()(app* a) {} }; void mk_fresh_name::add(ast* a) { for_each_symbol_proc proc(m_symbols); for_each_ast(proc, a); } symbol mk_fresh_name::next() { for (; ; ++m_num) { for(; m_char <= 'Z'; ++m_char) { std::stringstream _name; _name << m_char; if (m_num > 0) _name << m_num; ++m_char; symbol name(_name.str().c_str()); if (!m_symbols.contains(name)) { return name; } } m_char = 'A'; } } static void mk_entry_cond(unsigned arity, func_entry const* entry, expr_ref& result) { ast_manager& m = result.get_manager(); expr_ref_vector conjs(m); for (unsigned i = 0; i < arity; ++i) { expr* e = entry->get_arg(i); if (is_var(e) && to_var(e)->get_idx() == i) { // no-op } else { conjs.push_back(m.mk_eq(m.mk_var(i, m.get_sort(e)), e)); } } bool_rewriter(m).mk_and(conjs.size(), conjs.c_ptr(), result); } void model2expr(model& md, expr_ref& result) { ast_manager& m = result.get_manager(); expr_ref_vector conjs(m); expr_ref tmp(m); unsigned sz; sz = md.get_num_constants(); for (unsigned i = 0; i < sz; ++i) { func_decl* c = md.get_constant(i); expr* v = md.get_const_interp(c); conjs.push_back(m.mk_eq(m.mk_const(c), v)); } sz = md.get_num_functions(); for (unsigned i = 0; i < sz; ++i) { func_decl* f = md.get_function(i); func_interp* fi = md.get_func_interp(f); // Register names. mk_fresh_name fresh_name; unsigned num_entries = fi->num_entries(); fresh_name.add(f); for (unsigned j = 0; j < num_entries; ++j) { func_entry const* entry = fi->get_entry(j); fresh_name.add(entry->get_result()); for (unsigned k = 0; k < f->get_arity(); ++k) { fresh_name.add(entry->get_arg(k)); } } expr_ref func(m), cond(m); expr_ref_vector args(m); for (unsigned j = 0; j < f->get_arity(); ++j) { args.push_back(m.mk_var(j, f->get_domain(j))); } func = m.mk_app(f, args.size(), args.c_ptr()); if (fi->is_partial()) { if (num_entries == 0) { continue; } mk_entry_cond(f->get_arity(), fi->get_entry(num_entries-1), cond); tmp = m.mk_implies(cond, m.mk_eq(func, fi->get_entry(num_entries-1)->get_result())); for (unsigned j = num_entries-1; j > 0; ) { --j; mk_entry_cond(f->get_arity(), fi->get_entry(j), cond); tmp = m.mk_ite(cond, m.mk_eq(func, fi->get_entry(j)->get_result()), tmp); } } else { fresh_name.add(fi->get_else()); tmp = fi->get_else(); for (unsigned j = num_entries; j > 0; ) { --j; mk_entry_cond(f->get_arity(), fi->get_entry(j), cond); tmp = m.mk_ite(cond, fi->get_entry(j)->get_result(), tmp); } tmp = m.mk_eq(func, tmp); } ptr_vector sorts; expr_ref_vector rev_vars(m); svector names; unsigned sz = f->get_arity(); for (unsigned j = 0; j < sz; ++j) { sorts.push_back(f->get_domain(j)); rev_vars.push_back(m.mk_var(sz-j-1, f->get_domain(j))); names.push_back(fresh_name.next()); } if (f->get_arity() > 0) { var_subst vs(m, false); tmp = vs(tmp, rev_vars.size(), rev_vars.c_ptr()); tmp = m.mk_forall(sorts.size(), sorts.c_ptr(), names.c_ptr(), tmp); } conjs.push_back(tmp); } bool_rewriter(m).mk_and(conjs.size(), conjs.c_ptr(), result); } z3-z3-4.8.7/src/model/model2expr.h000066400000000000000000000015431356505360400165530ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: model2expr.h Abstract: Convert model to logical formula that forces it. Author: Nikolaj Bjorner (nbjorner) 2012-09-17 Revision History: --*/ #ifndef MODEL2EXPR_H_ #define MODEL2EXPR_H_ #include "model/model.h" void model2expr(model& m, expr_ref& result); inline void model2expr(model_ref& md, expr_ref& result) { model2expr(*md.get(), result); } // TODO: move typedef hashtable symbol_set; class mk_fresh_name { symbol_set m_symbols; char m_char; unsigned m_num; public: mk_fresh_name(): m_char('A'), m_num(0) {} void add(ast* a); void add(symbol const& s) { m_symbols.insert(s); } symbol next(); bool contains(symbol const& s) const { return m_symbols.contains(s); } }; #endif /* MODEL2EXPR_H_ */ z3-z3-4.8.7/src/model/model_core.cpp000066400000000000000000000053071356505360400171370ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: model_core.cpp Abstract: Base class for models. Author: Leonardo de Moura (leonardo) 2011-04-30. Revision History: --*/ #include "model/model_core.h" #include "ast/ast_pp.h" model_core::~model_core() { for (auto & kv : m_interp) { m.dec_ref(kv.m_key); m.dec_ref(kv.m_value); } for (auto & kv : m_finterp) { m.dec_ref(kv.m_key); dealloc(kv.m_value); } } bool model_core::eval(func_decl* f, expr_ref & r) const { if (f->get_arity() == 0) { r = get_const_interp(f); return r != 0; } else { func_interp * fi = get_func_interp(f); if (fi != nullptr) { r = fi->get_interp(); return r != 0; } return false; } } void model_core::register_decl(func_decl * d, expr * v) { SASSERT(d->get_arity() == 0); TRACE("model", tout << "register " << d->get_name() << "\n"; if (v) tout << mk_pp(v, m) << "\n"; ); decl2expr::obj_map_entry * entry = m_interp.insert_if_not_there2(d, nullptr); if (entry->get_data().m_value == nullptr) { // new entry m_decls.push_back(d); m_const_decls.push_back(d); m.inc_ref(d); m.inc_ref(v); entry->get_data().m_value = v; } else { // replacing entry m.inc_ref(v); m.dec_ref(entry->get_data().m_value); entry->get_data().m_value = v; } } void model_core::register_decl(func_decl * d, func_interp * fi) { TRACE("model", tout << "register " << d->get_name() << "\n";); SASSERT(d->get_arity() > 0); SASSERT(&fi->m() == &m); decl2finterp::obj_map_entry * entry = m_finterp.insert_if_not_there2(d, nullptr); if (entry->get_data().m_value == nullptr) { // new entry m_decls.push_back(d); m_func_decls.push_back(d); m.inc_ref(d); entry->get_data().m_value = fi; } else { // replacing entry if (fi != entry->get_data().m_value) dealloc(entry->get_data().m_value); entry->get_data().m_value = fi; } } void model_core::unregister_decl(func_decl * d) { decl2expr::obj_map_entry * ec = m_interp.find_core(d); if (ec) { auto k = ec->get_data().m_key; auto v = ec->get_data().m_value; m_interp.remove(d); m_const_decls.erase(d); m.dec_ref(k); m.dec_ref(v); return; } decl2finterp::obj_map_entry * ef = m_finterp.find_core(d); if (ef) { auto k = ef->get_data().m_key; auto v = ef->get_data().m_value; m_finterp.remove(d); m_func_decls.erase(d); m.dec_ref(k); dealloc(v); } } z3-z3-4.8.7/src/model/model_core.h000066400000000000000000000060261356505360400166030ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: model_core.h Abstract: Base class for models. Author: Leonardo de Moura (leonardo) 2011-04-30. Revision History: --*/ #ifndef MODEL_CORE_H_ #define MODEL_CORE_H_ #include "ast/ast.h" #include "util/obj_hashtable.h" #include "model/func_interp.h" class model_core { protected: typedef obj_map decl2expr; typedef obj_map decl2finterp; ast_manager & m; unsigned m_ref_count; decl2expr m_interp; //!< interpretation for uninterpreted constants decl2finterp m_finterp; //!< interpretation for uninterpreted functions ptr_vector m_decls; //!< domain of m_interp ptr_vector m_const_decls; ptr_vector m_func_decls; public: model_core(ast_manager & m):m(m), m_ref_count(0) { } virtual ~model_core(); ast_manager & get_manager() const { return m; } unsigned get_num_decls() const { return m_decls.size(); } func_decl * get_decl(unsigned i) const { return m_decls[i]; } bool has_interpretation(func_decl * d) const { return m_interp.contains(d) || m_finterp.contains(d); } expr * get_const_interp(func_decl * d) const { expr * v; return m_interp.find(d, v) ? v : nullptr; } func_interp * get_func_interp(func_decl * d) const { func_interp * fi; return m_finterp.find(d, fi) ? fi : nullptr; } bool eval(func_decl * f, expr_ref & r) const; bool is_true_decl(func_decl *f) const { expr_ref r(m); return eval(f, r) && m.is_true(r); } bool is_false_decl(func_decl *f) const { expr_ref r(m); return eval(f, r) && m.is_false(r); } unsigned get_num_constants() const { return m_const_decls.size(); } unsigned get_num_functions() const { return m_func_decls.size(); } func_decl * get_constant(unsigned i) const { return m_const_decls[i]; } func_decl * get_function(unsigned i) const { return m_func_decls[i]; } virtual ptr_vector const & get_universe(sort * s) const = 0; virtual unsigned get_num_uninterpreted_sorts() const = 0; virtual sort * get_uninterpreted_sort(unsigned idx) const = 0; void register_decl(func_decl * d, expr * v); void register_decl(func_decl * f, func_interp * fi); void unregister_decl(func_decl * d); virtual expr * get_some_value(sort * s) = 0; virtual expr * get_fresh_value(sort * s) = 0; virtual bool get_some_values(sort * s, expr_ref & v1, expr_ref & v2) = 0; expr * get_some_const_interp(func_decl * d) { expr * r = get_const_interp(d); if (r) return r; return get_some_value(d->get_range()); } // // Reference counting // void inc_ref() { ++m_ref_count; } void dec_ref() { --m_ref_count; if (m_ref_count == 0) { dealloc(this); } } }; std::ostream& operator<<(std::ostream& out, model_core const& m); #endif z3-z3-4.8.7/src/model/model_evaluator.cpp000066400000000000000000000615731356505360400202200ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: model_evaluator.cpp Abstract: Evaluate expressions in a given model. Author: Leonardo de Moura (leonardo) 2011-04-30. Revision History: --*/ #include "ast/ast_pp.h" #include "ast/ast_util.h" #include "ast/rewriter/rewriter_types.h" #include "ast/rewriter/bool_rewriter.h" #include "ast/rewriter/arith_rewriter.h" #include "ast/rewriter/bv_rewriter.h" #include "ast/rewriter/pb_rewriter.h" #include "ast/rewriter/seq_rewriter.h" #include "ast/rewriter/datatype_rewriter.h" #include "ast/rewriter/array_rewriter.h" #include "ast/rewriter/fpa_rewriter.h" #include "ast/rewriter/th_rewriter.h" #include "ast/rewriter/rewriter_def.h" #include "ast/rewriter/var_subst.h" #include "model/model_smt2_pp.h" #include "model/model.h" #include "model/model_evaluator_params.hpp" #include "model/model_evaluator.h" #include "model/model_v2_pp.h" namespace { 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; array_rewriter m_ar_rw; datatype_rewriter m_dt_rw; pb_rewriter m_pb_rw; fpa_rewriter m_f_rw; seq_rewriter m_seq_rw; array_util m_ar; arith_util m_au; fpa_util m_fpau; unsigned long long m_max_memory; unsigned m_max_steps; bool m_model_completion; 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. m_a_rw(m, p), m_bv_rw(m), // See comment above. We want to allow customers to set :sort-store m_ar_rw(m, p), m_dt_rw(m), m_pb_rw(m), m_f_rw(m), m_seq_rw(m), m_ar(m), m_au(m), m_fpau(m), m_pinned(m) { bool flat = true; m_b_rw.set_flat(flat); m_a_rw.set_flat(flat); m_bv_rw.set_flat(flat); m_bv_rw.set_mkbv2num(true); m_ar_rw.set_expand_select_store(true); m_ar_rw.set_expand_select_ite(true); updt_params(p); //add_unspecified_function_models(md); } void updt_params(params_ref const & _p) { model_evaluator_params p(_p); m_max_memory = megabytes_to_bytes(p.max_memory()); m_max_steps = p.max_steps(); m_model_completion = p.completion(); m_array_equalities = p.array_equalities(); m_array_as_stores = p.array_as_stores(); } bool evaluate(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { func_interp * fi = m_model.get_func_interp(f); bool r = (fi != nullptr) && eval_fi(fi, num, args, result); CTRACE("model_evaluator", r, tout << "reduce_app " << f->get_name() << "\n"; for (unsigned i = 0; i < num; i++) tout << mk_ismt2_pp(args[i], m) << "\n"; tout << "---->\n" << mk_ismt2_pp(result, m) << "\n";); return r; } // Try to use the entries to quickly evaluate the fi bool eval_fi(func_interp * fi, unsigned num, expr * const * args, expr_ref & result) { if (fi->num_entries() == 0) return false; // let get_macro handle it. SASSERT(fi->get_arity() == num); bool actuals_are_values = true; for (unsigned i = 0; actuals_are_values && i < num; i++) actuals_are_values = m.is_value(args[i]); if (!actuals_are_values) return false; // let get_macro handle it func_entry * entry = fi->get_entry(args); if (entry != nullptr) { result = entry->get_result(); return true; } return false; } 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) { th_rewriter th(m); return th.reduce_quantifier(old_q, new_body, new_patterns, new_no_patterns, result, result_pr); } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { result_pr = nullptr; family_id fid = f->get_family_id(); bool is_uninterp = fid != null_family_id && m.get_plugin(fid)->is_considered_uninterpreted(f); br_status st = BR_FAILED; TRACE("model_evaluator", tout << f->get_name() << " " << is_uninterp << "\n";); if (num == 0 && (fid == null_family_id || is_uninterp)) { // || m_ar.is_as_array(f) expr * val = m_model.get_const_interp(f); if (val != nullptr) { result = val; return m_ar.is_as_array(val)? BR_REWRITE1 : BR_DONE; } else if (m_model_completion) { sort * s = f->get_range(); expr * val = m_model.get_some_value(s); m_model.register_decl(f, val); result = val; return BR_DONE; } else { return BR_FAILED; } } if (fid == m_b_rw.get_fid()) { decl_kind k = f->get_decl_kind(); if (k == OP_EQ) { // theory dispatch for = SASSERT(num == 2); sort* s = m.get_sort(args[0]); family_id s_fid = s->get_family_id(); if (s_fid == m_a_rw.get_fid()) st = m_a_rw.mk_eq_core(args[0], args[1], result); else if (s_fid == m_bv_rw.get_fid()) st = m_bv_rw.mk_eq_core(args[0], args[1], result); else if (s_fid == m_dt_rw.get_fid()) st = m_dt_rw.mk_eq_core(args[0], args[1], result); else if (s_fid == m_f_rw.get_fid()) st = m_f_rw.mk_eq_core(args[0], args[1], result); else if (s_fid == m_seq_rw.get_fid()) st = m_seq_rw.mk_eq_core(args[0], args[1], result); else if (s_fid == m_ar_rw.get_fid()) st = mk_array_eq(args[0], args[1], result); else if (m.are_equal(args[0], args[1])) { result = m.mk_true(); st = BR_DONE; } else if (m.are_distinct(args[0], args[1])) { result = m.mk_false(); st = BR_DONE; } TRACE("model_evaluator", tout << st << " " << mk_pp(s, m) << " " << s_fid << " " << m_ar_rw.get_fid() << " " << mk_pp(args[0], m) << " " << mk_pp(args[1], m) << " " << result << "\n";); if (st != BR_FAILED) return st; } return m_b_rw.mk_app_core(f, num, args, result); } CTRACE("model_evaluator", st != BR_FAILED, tout << result << "\n";); if (fid == m_a_rw.get_fid()) st = m_a_rw.mk_app_core(f, num, args, result); else if (fid == m_bv_rw.get_fid()) st = m_bv_rw.mk_app_core(f, num, args, result); else if (fid == m_ar_rw.get_fid()) st = m_ar_rw.mk_app_core(f, num, args, result); else if (fid == m_dt_rw.get_fid()) st = m_dt_rw.mk_app_core(f, num, args, result); else if (fid == m_pb_rw.get_fid()) st = m_pb_rw.mk_app_core(f, num, args, result); else if (fid == m_f_rw.get_fid()) st = m_f_rw.mk_app_core(f, num, args, result); else if (fid == m_seq_rw.get_fid()) st = m_seq_rw.mk_app_core(f, num, args, result); else if (fid == m.get_label_family_id() && num == 1) { result = args[0]; st = BR_DONE; } else if (evaluate(f, num, args, result)) { st = BR_REWRITE1; } CTRACE("model_evaluator", st != BR_FAILED, tout << result << "\n";); if (st == BR_FAILED && !m.is_builtin_family_id(fid)) { st = evaluate_partial_theory_func(f, num, args, result, result_pr); CTRACE("model_evaluator", st != BR_FAILED, tout << result << "\n";); } if (st == BR_DONE && is_app(result)) { app* a = to_app(result); if (evaluate(a->get_decl(), a->get_num_args(), a->get_args(), result)) { st = BR_REWRITE1; } } 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; if (m_def_cache.find(g, def)) { result = def; return BR_DONE; } func_interp * fi = m_model.get_func_interp(g); if (fi && (result = fi->get_array_interp(g))) { model_evaluator ev(m_model, m_params); result = ev(result); m_pinned.push_back(result); m_def_cache.insert(g, result); return BR_DONE; } } CTRACE("model_evaluator", st != BR_FAILED, tout << result << "\n";); return st; } void expand_stores(expr_ref& val) { TRACE("model_evaluator", tout << val << "\n";); vector stores; expr_ref else_case(m); bool _unused; if (m_array_as_stores && m_ar.is_array(val) && extract_array_func_interp(val, stores, else_case, _unused)) { sort* srt = m.get_sort(val); val = m_ar.mk_const_array(srt, else_case); for (unsigned i = stores.size(); i-- > 0; ) { expr_ref_vector args(m); args.push_back(val); args.append(stores[i].size(), stores[i].c_ptr()); val = m_ar.mk_store(args); } } } 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";); func_interp * fi = m_model.get_func_interp(f); if (fi != nullptr) { TRACE_MACRO; if (fi->is_partial()) { if (m_model_completion) { sort * s = f->get_range(); expr * val = m_model.get_some_value(s); fi->set_else(val); } else return false; } def = fi->get_interp(); SASSERT(def != nullptr); return true; } if (m_model_completion && (f->get_family_id() == null_family_id || m.get_plugin(f->get_family_id())->is_considered_uninterpreted(f))) { TRACE_MACRO; sort * s = f->get_range(); expr * val = m_model.get_some_value(s); func_interp * new_fi = alloc(func_interp, m, f->get_arity()); new_fi->set_else(val); m_model.register_decl(f, new_fi); def = val; return true; } return false; } br_status evaluate_partial_theory_func(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { SASSERT(f != nullptr); SASSERT(!m.is_builtin_family_id(f->get_family_id())); result = nullptr; result_pr = nullptr; if (f->get_family_id() == m_fpau.get_family_id() && !m_fpau.is_considered_uninterpreted(f, num, args)) { // cwinter: should this be unreachable? return BR_FAILED; } func_interp * fi = m_model.get_func_interp(f); func_decl_ref f_ui(m); if (!fi && m_au.is_considered_uninterpreted(f, num, args, f_ui)) { if (f_ui) { fi = m_model.get_func_interp(f_ui); } if (!fi) { result = m_au.mk_numeral(rational(0), f->get_range()); return BR_DONE; } } else if (!fi && m_fpau.is_considered_uninterpreted(f, num, args)) { result = m.get_some_value(f->get_range()); return BR_DONE; } if (fi) { if (fi->is_partial()) fi->set_else(m.get_some_value(f->get_range())); var_subst vs(m, false); result = vs(fi->get_interp(), num, args); return BR_REWRITE_FULL; } return BR_FAILED; } bool max_steps_exceeded(unsigned num_steps) const { if (memory::get_allocation_size() > m_max_memory) throw rewriter_exception(Z3_MAX_MEMORY_MSG); return num_steps > m_max_steps; } br_status mk_array_eq(expr* a, expr* b, expr_ref& result) { TRACE("model_evaluator", tout << "mk_array_eq " << m_array_equalities << "\n";); if (a == b) { result = m.mk_true(); return BR_DONE; } if (!m_array_equalities) { return m_ar_rw.mk_eq_core(a, b, result); } vector stores1, stores2; bool args_are_unique1, args_are_unique2; expr_ref else1(m), else2(m); if (extract_array_func_interp(a, stores1, else1, args_are_unique1) && extract_array_func_interp(b, stores2, else2, args_are_unique2)) { expr_ref_vector conj(m), args1(m), args2(m); if (m.are_equal(else1, else2)) { // no op } else if (m.are_distinct(else1, else2) && !(m.get_sort(else1)->get_info()->get_num_elements().is_finite())) { result = m.mk_false(); return BR_DONE; } else { conj.push_back(m.mk_eq(else1, else2)); } if (args_are_unique1 && args_are_unique2 && !stores1.empty()) { TRACE("model_evalator", tout << "argss are unique";); return mk_array_eq_core(stores1, else1, stores2, else2, conj, result); } // TBD: this is too inefficient. args1.push_back(a); args2.push_back(b); stores1.append(stores2); for (unsigned i = 0; i < stores1.size(); ++i) { args1.resize(1); args1.append(stores1[i].size() - 1, stores1[i].c_ptr()); args2.resize(1); args2.append(stores1[i].size() - 1, stores1[i].c_ptr()); expr_ref s1(m_ar.mk_select(args1.size(), args1.c_ptr()), m); expr_ref s2(m_ar.mk_select(args2.size(), args2.c_ptr()), m); conj.push_back(m.mk_eq(s1, s2)); } result = mk_and(conj); TRACE("model_evaluator", tout << mk_pp(a, m) << " == " << mk_pp(b, m) << " -> " << conj << "\n"; for (auto& s : stores1) tout << "store: " << s << "\n"; ); return BR_REWRITE_FULL; } return m_ar_rw.mk_eq_core(a, b, result); } struct args_eq { unsigned m_arity; args_eq(unsigned arity): m_arity(arity) {} bool operator()(expr * const* args1, expr* const* args2) const { for (unsigned i = 0; i < m_arity; ++i) { if (args1[i] != args2[i]) { return false; } } return true; } }; struct args_hash { unsigned m_arity; args_hash(unsigned arity): m_arity(arity) {} unsigned operator()(expr * const* args) const { return get_composite_hash(args, m_arity, default_kind_hash_proc(), *this); } unsigned operator()(expr* const* args, unsigned idx) const { return args[idx]->hash(); } }; typedef hashtable args_table; br_status mk_array_eq_core(vector const& stores1, expr* else1, vector const& stores2, expr* else2, expr_ref_vector& conj, expr_ref& result) { unsigned arity = stores1[0].size()-1; // TBD: fix arity. args_hash ah(arity); args_eq ae(arity); args_table table1(DEFAULT_HASHTABLE_INITIAL_CAPACITY, ah, ae); args_table table2(DEFAULT_HASHTABLE_INITIAL_CAPACITY, ah, ae); // stores with smaller index take precedence for (unsigned i = stores1.size(); i-- > 0; ) { table1.insert(stores1[i].c_ptr()); } for (unsigned i = 0, sz = stores2.size(); i < sz; ++i) { if (table2.contains(stores2[i].c_ptr())) { // first insertion takes precedence. continue; } table2.insert(stores2[i].c_ptr()); expr * const* args = nullptr; expr* val = stores2[i][arity]; if (table1.find(stores2[i].c_ptr(), args)) { switch (compare(args[arity], val)) { case l_true: table1.remove(args); break; case l_false: result = m.mk_false(); return BR_DONE; default: conj.push_back(m.mk_eq(val, args[arity])); break; } } else { switch (compare(else1, val)) { case l_true: break; case l_false: result = m.mk_false(); return BR_DONE; default: conj.push_back(m.mk_eq(else1, val)); break; } } } for (auto const& t : table1) { switch (compare((t)[arity], else2)) { case l_true: break; case l_false: result = m.mk_false(); return BR_DONE; default: conj.push_back(m.mk_eq((t)[arity], else2)); break; } } result = mk_and(conj); return BR_REWRITE_FULL; } lbool compare(expr* a, expr* b) { if (m.are_equal(a, b)) return l_true; if (m.are_distinct(a, b)) return l_false; return l_undef; } bool args_are_values(expr_ref_vector const& store, bool& are_unique) { bool are_values = true; for (unsigned j = 0; are_values && j + 1 < store.size(); ++j) { are_values = m.is_value(store[j]); are_unique &= m.is_unique_value(store[j]); } SASSERT(!are_unique || are_values); return are_values; } bool extract_array_func_interp(expr* a, vector& stores, expr_ref& else_case, bool& are_unique) { SASSERT(m_ar.is_array(a)); bool are_values = true; are_unique = true; TRACE("model_evaluator", tout << mk_pp(a, m) << "\n";); while (m_ar.is_store(a)) { expr_ref_vector store(m); store.append(to_app(a)->get_num_args()-1, to_app(a)->get_args()+1); are_values &= args_are_values(store, are_unique); stores.push_back(store); a = to_app(a)->get_arg(0); } if (m_ar.is_const(a)) { else_case = to_app(a)->get_arg(0); return true; } if (m_ar_rw.has_index_set(a, else_case, stores)) { for (auto const& store : stores) { are_values &= args_are_values(store, are_unique); } return true; } if (!m_ar.is_as_array(a)) { TRACE("model_evaluator", tout << "no translation: " << mk_pp(a, m) << "\n";); TRACE("model_evaluator", tout << m_model << "\n";); return false; } func_decl* f = m_ar.get_as_array_func_decl(to_app(a)); func_interp* g = m_model.get_func_interp(f); if (!g) return false; else_case = g->get_else(); if (!else_case) { TRACE("model_evaluator", tout << "no else case " << mk_pp(a, m) << "\n";); return false; } bool ground = is_ground(else_case); unsigned sz = g->num_entries(); expr_ref_vector store(m); for (unsigned i = 0; i < sz; ++i) { store.reset(); func_entry const* fe = g->get_entry(i); expr* res = fe->get_result(); if (m.are_equal(else_case, res)) { continue; } ground &= is_ground(res); store.append(g->get_arity(), fe->get_args()); store.push_back(res); for (expr* arg : store) { ground &= is_ground(arg); } stores.push_back(store); } if (!ground) { TRACE("model_evaluator", tout << "could not extract ground array interpretation: " << mk_pp(a, m) << "\n";); return false; } return true; } }; } struct model_evaluator::imp : public rewriter_tpl { evaluator_cfg m_cfg; imp(model_core & md, params_ref const & p): rewriter_tpl(md.get_manager(), false, // no proofs for evaluator m_cfg), m_cfg(md.get_manager(), md, p) { set_cancel_check(false); } void expand_stores(expr_ref &val) {m_cfg.expand_stores(val);} void reset() { rewriter_tpl::reset(); m_cfg.reset(); m_cfg.m_def_cache.reset(); } }; model_evaluator::model_evaluator(model_core & md, params_ref const & p) { m_imp = alloc(imp, md, p); } ast_manager & model_evaluator::m() const { return m_imp->m(); } model_evaluator::~model_evaluator() { dealloc(m_imp); } void model_evaluator::updt_params(params_ref const & p) { m_imp->cfg().updt_params(p); } void model_evaluator::get_param_descrs(param_descrs & r) { model_evaluator_params::collect_param_descrs(r); } void model_evaluator::set_model_completion(bool f) { if (m_imp->cfg().m_model_completion != f) { reset(); m_imp->cfg().m_model_completion = f; } } bool model_evaluator::get_model_completion() const { return m_imp->cfg().m_model_completion; } void model_evaluator::set_expand_array_equalities(bool f) { m_imp->cfg().m_array_equalities = f; } unsigned model_evaluator::get_num_steps() const { return m_imp->get_num_steps(); } void model_evaluator::cleanup(params_ref const & p) { model_core & md = m_imp->cfg().m_model; m_imp->~imp(); new (m_imp) imp(md, p); } void model_evaluator::reset(params_ref const & p) { m_imp->reset(); updt_params(p); } void model_evaluator::reset(model_core &model, params_ref const& p) { m_imp->~imp(); new (m_imp) imp(model, p); } void model_evaluator::operator()(expr * t, expr_ref & result) { TRACE("model_evaluator", tout << mk_ismt2_pp(t, m()) << "\n";); m_imp->operator()(t, result); TRACE("model_evaluator", tout << result << "\n";); m_imp->expand_stores(result); } expr_ref model_evaluator::operator()(expr * t) { TRACE("model_evaluator", tout << mk_ismt2_pp(t, m()) << "\n";); expr_ref result(m()); this->operator()(t, result); return result; } expr_ref_vector model_evaluator::operator()(expr_ref_vector const& ts) { expr_ref_vector rs(m()); for (expr* t : ts) rs.push_back((*this)(t)); return rs; } bool model_evaluator::is_true(expr* t) { expr_ref tmp(m()); return eval(t, tmp, true) && m().is_true(tmp); } bool model_evaluator::is_false(expr* t) { expr_ref tmp(m()); return eval(t, tmp, true) && m().is_false(tmp); } bool model_evaluator::is_true(expr_ref_vector const& ts) { for (expr* t : ts) if (!is_true(t)) return false; return true; } bool model_evaluator::are_equal(expr* s, expr* t) { if (m().are_equal(s, t)) return true; if (m().are_distinct(s, t)) return false; expr_ref t1(m()), t2(m()); eval(t, t1, true); eval(s, t2, true); return m().are_equal(t1, t2); } bool model_evaluator::eval(expr* t, expr_ref& r, bool model_completion) { set_model_completion(model_completion); try { r = (*this)(t); return true; } catch (model_evaluator_exception &ex) { (void)ex; TRACE("model_evaluator", tout << ex.msg () << "\n";); return false; } } bool model_evaluator::eval(expr_ref_vector const& ts, expr_ref& r, bool model_completion) { expr_ref tmp(m()); 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); } bool model_evaluator::has_solver() { return m_imp->m_cfg.m_seq_rw.has_solver(); } z3-z3-4.8.7/src/model/model_evaluator.h000066400000000000000000000033541356505360400176560ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: model_evaluator.h Abstract: Evaluate expressions in a given model. Author: Leonardo de Moura (leonardo) 2011-04-30. Revision History: --*/ #ifndef MODEL_EVALUATOR_H_ #define MODEL_EVALUATOR_H_ #include "ast/ast.h" #include "ast/rewriter/rewriter_types.h" #include "util/params.h" class model; class model_core; class expr_solver; typedef rewriter_exception model_evaluator_exception; class model_evaluator { struct imp; imp * m_imp; public: model_evaluator(model_core & m, params_ref const & p = params_ref()); ~model_evaluator(); ast_manager & m () const; void set_model_completion(bool f); bool get_model_completion() const; void set_expand_array_equalities(bool f); void updt_params(params_ref const & p); static void get_param_descrs(param_descrs & r); void operator()(expr * t, expr_ref & r); expr_ref operator()(expr* t); expr_ref_vector operator()(expr_ref_vector const& ts); // exception safe bool eval(expr* t, expr_ref& r, bool model_completion = true); bool eval(expr_ref_vector const& ts, expr_ref& r, bool model_completion = true); bool is_true(expr * t); bool is_false(expr * t); bool is_true(expr_ref_vector const& ts); bool are_equal(expr* s, expr* t); void set_solver(expr_solver* solver); bool has_solver(); /** * best effort evaluator of extensional array equality. */ expr_ref eval_array_eq(app* e, expr* arg1, expr* arg2); void cleanup(params_ref const & p = params_ref()); void reset(params_ref const & p = params_ref()); void reset(model_core& model, params_ref const & p = params_ref()); unsigned get_num_steps() const; }; #endif z3-z3-4.8.7/src/model/model_evaluator_params.pyg000066400000000000000000000010601356505360400215610ustar00rootroot00000000000000def_module_params('model_evaluator', export=True, params=(max_memory_param(), max_steps_param(), ('completion', BOOL, False, 'assigns an interptetation to symbols that do not have one in the current model, when evaluating expressions in the current model'), ('array_equalities', BOOL, True, 'evaluate array equalities'), ('array_as_stores', BOOL, True, 'return array as a set of stores'), )) z3-z3-4.8.7/src/model/model_implicant.cpp000066400000000000000000000630531356505360400201710ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: model_implicant.cpp Abstract: Facility to extract prime implicant from model. Author: Krystof Hoder (t-khoder) 2011-8-19. Revision History: Notes: --*/ #include #include "ast/array_decl_plugin.h" #include "ast/ast_pp.h" #include "ast/rewriter/bool_rewriter.h" #include "ast/for_each_expr.h" #include "model/model.h" #include "util/ref_vector.h" #include "ast/rewriter/rewriter.h" #include "ast/rewriter/rewriter_def.h" #include "util/util.h" #include "model/model_implicant.h" #include "ast/arith_decl_plugin.h" #include "ast/rewriter/expr_replacer.h" #include "model/model_smt2_pp.h" #include "ast/rewriter/poly_rewriter.h" #include "ast/rewriter/poly_rewriter_def.h" #include "ast/rewriter/arith_rewriter.h" #include "ast/scoped_proof.h" ///////////////////////// // model_implicant // void model_implicant::assign_value(expr* e, expr* val) { rational r; if (m.is_true(val)) { set_true(e); } else if (m.is_false(val)) { set_false(e); } else if (m_arith.is_numeral(val, r)) { set_number(e, r); } else if (m.is_value(val)) { set_value(e, val); } else { IF_VERBOSE(3, verbose_stream() << "Not evaluated " << mk_pp(e, m) << " := " << mk_pp(val, m) << "\n";); TRACE("pdr", tout << "Variable is not tracked: " << mk_pp(e, m) << " := " << mk_pp(val, m) << "\n";); set_x(e); } } void model_implicant::setup_model(model_ref& model) { m_numbers.reset(); m_values.reset(); m_model = model; rational r; unsigned sz = model->get_num_constants(); for (unsigned i = 0; i < sz; i++) { func_decl * d = model->get_constant(i); expr* val = model->get_const_interp(d); expr* e = m.mk_const(d); m_refs.push_back(e); assign_value(e, val); } } void model_implicant::reset() { m1.reset(); m2.reset(); m_values.reset(); m_visited.reset(); m_numbers.reset(); m_refs.reset(); m_model = nullptr; } expr_ref_vector model_implicant::minimize_model(ptr_vector const & formulas, model_ref& mdl) { setup_model(mdl); TRACE("pdr_verbose", tout << "formulas:\n"; for (unsigned i = 0; i < formulas.size(); ++i) tout << mk_pp(formulas[i], m) << "\n"; ); expr_ref_vector model = prune_by_cone_of_influence(formulas); TRACE("pdr_verbose", tout << "pruned model:\n"; for (unsigned i = 0; i < model.size(); ++i) tout << mk_pp(model[i].get(), m) << "\n";); reset(); DEBUG_CODE( setup_model(mdl); VERIFY(check_model(formulas)); reset();); return model; } expr_ref_vector model_implicant::minimize_literals(ptr_vector const& formulas, model_ref& mdl) { TRACE("pdr", tout << "formulas:\n"; for (unsigned i = 0; i < formulas.size(); ++i) tout << mk_pp(formulas[i], m) << "\n"; ); expr_ref_vector result(m); expr_ref tmp(m); ptr_vector tocollect; setup_model(mdl); collect(formulas, tocollect); for (unsigned i = 0; i < tocollect.size(); ++i) { expr* e = tocollect[i]; expr* e1, *e2; SASSERT(m.is_bool(e)); SASSERT(is_true(e) || is_false(e)); if (is_true(e)) { result.push_back(e); } // hack to break disequalities for arithmetic variables. else if (m.is_eq(e, e1, e2) && m_arith.is_int_real(e1)) { if (get_number(e1) < get_number(e2)) { result.push_back(m_arith.mk_lt(e1,e2)); } else { result.push_back(m_arith.mk_lt(e2,e1)); } } else { result.push_back(m.mk_not(e)); } } reset(); TRACE("pdr", tout << "minimized model:\n"; for (unsigned i = 0; i < result.size(); ++i) tout << mk_pp(result[i].get(), m) << "\n"; ); return result; } void model_implicant::process_formula(app* e, ptr_vector& todo, ptr_vector& tocollect) { SASSERT(m.is_bool(e)); SASSERT(is_true(e) || is_false(e)); unsigned v = is_true(e); unsigned sz = e->get_num_args(); expr* const* args = e->get_args(); if (e->get_family_id() == m.get_basic_family_id()) { switch(e->get_decl_kind()) { case OP_TRUE: break; case OP_FALSE: break; case OP_EQ: if (args[0] == args[1]) { SASSERT(v); // no-op } else if (m.is_bool(args[0])) { todo.append(sz, args); } else { tocollect.push_back(e); } break; case OP_DISTINCT: tocollect.push_back(e); break; case OP_ITE: if (args[1] == args[2]) { tocollect.push_back(args[1]); } else if (is_true(args[1]) && is_true(args[2])) { todo.append(2, args+1); } else if (is_false(args[1]) && is_false(args[2])) { todo.append(2, args+1); } else if (is_true(args[0])) { todo.append(2, args); } else { SASSERT(is_false(args[0])); todo.push_back(args[0]); todo.push_back(args[2]); } break; case OP_AND: if (v) { todo.append(sz, args); } else { unsigned i = 0; for (; !is_false(args[i]) && i < sz; ++i); if (i == sz) { fatal_error(1); } VERIFY(i < sz); todo.push_back(args[i]); } break; case OP_OR: if (v) { unsigned i = 0; for (; !is_true(args[i]) && i < sz; ++i); if (i == sz) { fatal_error(1); } VERIFY(i < sz); todo.push_back(args[i]); } else { todo.append(sz, args); } break; case OP_XOR: case OP_NOT: todo.append(sz, args); break; case OP_IMPLIES: if (v) { if (is_true(args[1])) { todo.push_back(args[1]); } else if (is_false(args[0])) { todo.push_back(args[0]); } else { IF_VERBOSE(0, verbose_stream() << "Term not handled " << mk_pp(e, m) << "\n";); UNREACHABLE(); } } else { todo.append(sz, args); } break; default: IF_VERBOSE(0, verbose_stream() << "Term not handled " << mk_pp(e, m) << "\n";); UNREACHABLE(); } } else { tocollect.push_back(e); } } void model_implicant::collect(ptr_vector const& formulas, ptr_vector& tocollect) { ptr_vector todo; todo.append(formulas); m_visited.reset(); VERIFY(check_model(formulas)); while (!todo.empty()) { app* e = to_app(todo.back()); todo.pop_back(); if (!m_visited.is_marked(e)) { process_formula(e, todo, tocollect); m_visited.mark(e, true); } } m_visited.reset(); } expr_ref_vector model_implicant::prune_by_cone_of_influence(ptr_vector const & formulas) { ptr_vector tocollect; collect(formulas, tocollect); m1.reset(); m2.reset(); for (unsigned i = 0; i < tocollect.size(); ++i) { TRACE("pdr_verbose", tout << "collect: " << mk_pp(tocollect[i], m) << "\n";); for_each_expr(*this, m_visited, tocollect[i]); } unsigned sz = m_model->get_num_constants(); expr_ref e(m), eq(m), val(m); expr_ref_vector model(m); for (unsigned i = 0; i < sz; i++) { e = m.mk_const(m_model->get_constant(i)); if (m_visited.is_marked(e)) { val = eval(m_model, e); eq = m.mk_eq(e, val); model.push_back(eq); } } m_visited.reset(); TRACE("pdr", tout << sz << " ==> " << model.size() << "\n";); return model; } void model_implicant::eval_arith(app* e) { rational r, r2; #define ARG1 e->get_arg(0) #define ARG2 e->get_arg(1) unsigned arity = e->get_num_args(); for (unsigned i = 0; i < arity; ++i) { expr* arg = e->get_arg(i); if (is_x(arg)) { set_x(e); return; } SASSERT(!is_unknown(arg)); } switch(e->get_decl_kind()) { case OP_NUM: VERIFY(m_arith.is_numeral(e, r)); set_number(e, r); break; case OP_IRRATIONAL_ALGEBRAIC_NUM: set_x(e); break; case OP_LE: set_bool(e, get_number(ARG1) <= get_number(ARG2)); break; case OP_GE: set_bool(e, get_number(ARG1) >= get_number(ARG2)); break; case OP_LT: set_bool(e, get_number(ARG1) < get_number(ARG2)); break; case OP_GT: set_bool(e, get_number(ARG1) > get_number(ARG2)); break; case OP_ADD: r = rational::zero(); for (unsigned i = 0; i < arity; ++i) { r += get_number(e->get_arg(i)); } set_number(e, r); break; case OP_SUB: r = get_number(e->get_arg(0)); for (unsigned i = 1; i < arity; ++i) { r -= get_number(e->get_arg(i)); } set_number(e, r); break; case OP_UMINUS: SASSERT(arity == 1); set_number(e, get_number(e->get_arg(0))); break; case OP_MUL: r = rational::one(); for (unsigned i = 0; i < arity; ++i) { r *= get_number(e->get_arg(i)); } set_number(e, r); break; case OP_DIV: SASSERT(arity == 2); r = get_number(ARG2); if (r.is_zero()) { set_x(e); } else { set_number(e, get_number(ARG1) / r); } break; case OP_IDIV: SASSERT(arity == 2); r = get_number(ARG2); if (r.is_zero()) { set_x(e); } else { set_number(e, div(get_number(ARG1), r)); } break; case OP_REM: // rem(v1,v2) = if v2 >= 0 then mod(v1,v2) else -mod(v1,v2) SASSERT(arity == 2); r = get_number(ARG2); if (r.is_zero()) { set_x(e); } else { r2 = mod(get_number(ARG1), r); if (r.is_neg()) r2.neg(); set_number(e, r2); } break; case OP_MOD: SASSERT(arity == 2); r = get_number(ARG2); if (r.is_zero()) { set_x(e); } else { set_number(e, mod(get_number(ARG1), r)); } break; case OP_TO_REAL: SASSERT(arity == 1); set_number(e, get_number(ARG1)); break; case OP_TO_INT: SASSERT(arity == 1); set_number(e, floor(get_number(ARG1))); break; case OP_IS_INT: SASSERT(arity == 1); set_bool(e, get_number(ARG1).is_int()); break; case OP_POWER: set_x(e); break; default: IF_VERBOSE(0, verbose_stream() << "Term not handled " << mk_pp(e, m) << "\n";); UNREACHABLE(); break; } } void model_implicant::inherit_value(expr* e, expr* v) { expr* w; SASSERT(!is_unknown(v)); SASSERT(m.get_sort(e) == m.get_sort(v)); if (is_x(v)) { set_x(e); } else if (m.is_bool(e)) { SASSERT(m.is_bool(v)); if (is_true(v)) set_true(e); else if (is_false(v)) set_false(e); else { TRACE("pdr", tout << "not inherited:\n" << mk_pp(e, m) << "\n" << mk_pp(v, m) << "\n";); set_x(e); } } else if (m_arith.is_int_real(e)) { set_number(e, get_number(v)); } else if (m.is_value(v)) { set_value(e, v); } else if (m_values.find(v, w)) { set_value(e, w); } else { TRACE("pdr", tout << "not inherited:\n" << mk_pp(e, m) << "\n" << mk_pp(v, m) << "\n";); set_x(e); } } void model_implicant::eval_exprs(expr_ref_vector& es) { model_ref mr(m_model); for (unsigned j = 0; j < es.size(); ++j) { if (m_array.is_as_array(es[j].get())) { es[j] = eval(mr, es[j].get()); } } } bool model_implicant::extract_array_func_interp(expr* a, vector& stores, expr_ref& else_case) { SASSERT(m_array.is_array(a)); TRACE("pdr", tout << mk_pp(a, m) << "\n";); while (m_array.is_store(a)) { expr_ref_vector store(m); store.append(to_app(a)->get_num_args()-1, to_app(a)->get_args()+1); eval_exprs(store); stores.push_back(store); a = to_app(a)->get_arg(0); } if (m_array.is_const(a)) { else_case = to_app(a)->get_arg(0); return true; } while (m_array.is_as_array(a)) { func_decl* f = m_array.get_as_array_func_decl(to_app(a)); func_interp* g = m_model->get_func_interp(f); unsigned sz = g->num_entries(); unsigned arity = f->get_arity(); for (unsigned i = 0; i < sz; ++i) { expr_ref_vector store(m); func_entry const* fe = g->get_entry(i); store.append(arity, fe->get_args()); store.push_back(fe->get_result()); for (unsigned j = 0; j < store.size(); ++j) { if (!is_ground(store[j].get())) { TRACE("pdr", tout << "could not extract array interpretation: " << mk_pp(a, m) << "\n" << mk_pp(store[j].get(), m) << "\n";); return false; } } eval_exprs(store); stores.push_back(store); } else_case = g->get_else(); if (!else_case) { TRACE("pdr", tout << "no else case " << mk_pp(a, m) << "\n";); return false; } if (!is_ground(else_case)) { TRACE("pdr", tout << "non-ground else case " << mk_pp(a, m) << "\n" << mk_pp(else_case, m) << "\n";); return false; } if (m_array.is_as_array(else_case)) { model_ref mr(m_model); else_case = eval(mr, else_case); } TRACE("pdr", tout << "else case: " << mk_pp(else_case, m) << "\n";); return true; } TRACE("pdr", tout << "no translation: " << mk_pp(a, m) << "\n";); return false; } /** best effort evaluator of extensional array equality. */ void model_implicant::eval_array_eq(app* e, expr* arg1, expr* arg2) { TRACE("pdr", tout << "array equality: " << mk_pp(e, m) << "\n";); expr_ref v1 = (*m_model)(arg1); expr_ref v2 = (*m_model)(arg2); if (v1 == v2) { set_true(e); return; } sort* s = m.get_sort(arg1); sort* r = get_array_range(s); // give up evaluating finite domain/range arrays if (!r->is_infinite() && !r->is_very_big() && !s->is_infinite() && !s->is_very_big()) { TRACE("pdr", tout << "equality is unknown: " << mk_pp(e, m) << "\n";); set_x(e); return; } vector store; expr_ref else1(m), else2(m); if (!extract_array_func_interp(v1, store, else1) || !extract_array_func_interp(v2, store, else2)) { TRACE("pdr", tout << "equality is unknown: " << mk_pp(e, m) << "\n";); set_x(e); return; } if (else1 != else2) { if (m.is_value(else1) && m.is_value(else2)) { TRACE("pdr", tout << "defaults are different: " << mk_pp(e, m) << " " << mk_pp(else1, m) << " " << mk_pp(else2, m) << "\n";); set_false(e); } else if (m_array.is_array(else1)) { eval_array_eq(e, else1, else2); } else { TRACE("pdr", tout << "equality is unknown: " << mk_pp(e, m) << "\n";); set_x(e); } return; } expr_ref s1(m), s2(m), w1(m), w2(m); expr_ref_vector args1(m), args2(m); args1.push_back(v1); args2.push_back(v2); for (unsigned i = 0; i < store.size(); ++i) { args1.resize(1); args2.resize(1); args1.append(store[i].size()-1, store[i].c_ptr()); args2.append(store[i].size()-1, store[i].c_ptr()); s1 = m_array.mk_select(args1.size(), args1.c_ptr()); s2 = m_array.mk_select(args2.size(), args2.c_ptr()); w1 = (*m_model)(s1); w2 = (*m_model)(s2); if (w1 == w2) { continue; } if (m.is_value(w1) && m.is_value(w2)) { TRACE("pdr", tout << "Equality evaluation: " << mk_pp(e, m) << "\n"; tout << mk_pp(s1, m) << " |-> " << mk_pp(w1, m) << "\n"; tout << mk_pp(s2, m) << " |-> " << mk_pp(w2, m) << "\n";); set_false(e); } else if (m_array.is_array(w1)) { eval_array_eq(e, w1, w2); if (is_true(e)) { continue; } } else { TRACE("pdr", tout << "equality is unknown: " << mk_pp(e, m) << "\n";); set_x(e); } return; } set_true(e); } void model_implicant::eval_eq(app* e, expr* arg1, expr* arg2) { if (arg1 == arg2) { set_true(e); } else if (m_array.is_array(arg1)) { eval_array_eq(e, arg1, arg2); } else if (is_x(arg1) || is_x(arg2)) { expr_ref eq(m); eq = m.mk_eq(arg1, arg2); expr_ref vl = (*m_model)(eq); if (m.is_true(vl)) { set_bool(e, true); } else if (m.is_false(vl)) { set_bool(e, false); } else { TRACE("pdr", tout << "cannot evaluate: " << mk_pp(vl, m) << "\n";); set_x(e); } } else if (m.is_bool(arg1)) { bool val = is_true(arg1) == is_true(arg2); SASSERT(val == (is_false(arg1) == is_false(arg2))); if (val) { set_true(e); } else { set_false(e); } } else if (m_arith.is_int_real(arg1)) { set_bool(e, get_number(arg1) == get_number(arg2)); } else { expr* e1 = get_value(arg1); expr* e2 = get_value(arg2); if (m.is_value(e1) && m.is_value(e2)) { set_bool(e, e1 == e2); } else if (e1 == e2) { set_bool(e, true); } else { TRACE("pdr", tout << "not value equal:\n" << mk_pp(e1, m) << "\n" << mk_pp(e2, m) << "\n";); set_x(e); } } } void model_implicant::eval_basic(app* e) { expr* arg1 = nullptr, *arg2 = nullptr; expr *argCond = nullptr, *argThen = nullptr, *argElse = nullptr, *arg = nullptr; bool has_x = false; unsigned arity = e->get_num_args(); switch(e->get_decl_kind()) { case OP_AND: for (unsigned j = 0; j < arity; ++j) { expr * arg = e->get_arg(j); if (is_false(arg)) { set_false(e); return; } else if (is_x(arg)) { has_x = true; } else { SASSERT(is_true(arg)); } } if (has_x) { set_x(e); } else { set_true(e); } break; case OP_OR: for (unsigned j = 0; j < arity; ++j) { expr * arg = e->get_arg(j); if (is_true(arg)) { set_true(e); return; } else if (is_x(arg)) { has_x = true; } else { SASSERT(is_false(arg)); } } if (has_x) { set_x(e); } else { set_false(e); } break; case OP_NOT: VERIFY(m.is_not(e, arg)); if (is_true(arg)) { set_false(e); } else if (is_false(arg)) { set_true(e); } else { SASSERT(is_x(arg)); set_x(e); } break; case OP_IMPLIES: VERIFY(m.is_implies(e, arg1, arg2)); if (is_false(arg1) || is_true(arg2)) { set_true(e); } else if (arg1 == arg2) { set_true(e); } else if (is_true(arg1) && is_false(arg2)) { set_false(e); } else { SASSERT(is_x(arg1) || is_x(arg2)); set_x(e); } break; case OP_ITE: VERIFY(m.is_ite(e, argCond, argThen, argElse)); if (is_true(argCond)) { inherit_value(e, argThen); } else if (is_false(argCond)) { inherit_value(e, argElse); } else if (argThen == argElse) { inherit_value(e, argThen); } else if (m.is_bool(e)) { SASSERT(is_x(argCond)); if (is_x(argThen) || is_x(argElse)) { set_x(e); } else if (is_true(argThen) == is_true(argElse)) { inherit_value(e, argThen); } else { set_x(e); } } else { set_x(e); } break; case OP_TRUE: set_true(e); break; case OP_FALSE: set_false(e); break; case OP_EQ: VERIFY(m.is_eq(e, arg1, arg2)); eval_eq(e, arg1, arg2); break; case OP_DISTINCT: { vector values; for (unsigned i = 0; i < arity; ++i) { expr* arg = e->get_arg(i); if (is_x(arg)) { set_x(e); return; } values.push_back(get_number(arg)); } std::sort(values.begin(), values.end()); for (unsigned i = 0; i + 1 < values.size(); ++i) { if (values[i] == values[i+1]) { set_false(e); return; } } set_true(e); break; } default: IF_VERBOSE(0, verbose_stream() << "Term not handled " << mk_pp(e, m) << "\n";); UNREACHABLE(); } } bool model_implicant::check_model(ptr_vector const& formulas) { ptr_vector todo(formulas); while (!todo.empty()) { expr * curr_e = todo.back(); if (!is_app(curr_e)) { todo.pop_back(); continue; } app * curr = to_app(curr_e); if (!is_unknown(curr)) { todo.pop_back(); continue; } unsigned arity = curr->get_num_args(); for (unsigned i = 0; i < arity; ++i) { if (is_unknown(curr->get_arg(i))) { todo.push_back(curr->get_arg(i)); } } if (todo.back() != curr) { continue; } todo.pop_back(); if (curr->get_family_id() == m_arith.get_family_id()) { eval_arith(curr); } else if (curr->get_family_id() == m.get_basic_family_id()) { eval_basic(curr); } else { expr_ref vl = (*m_model)(curr); assign_value(curr, vl); } IF_VERBOSE(35,verbose_stream() << "assigned "<get_arity() == 0); expr_ref result(m); if (m_array.is_array(d->get_range())) { expr_ref e(m); e = m.mk_const(d); result = eval(model, e); } else { result = model->get_const_interp(d); } return result; } expr_ref model_implicant::eval(model_ref& model, expr* e) { expr_ref result(m); m_model = model; result = (*m_model)(e); if (m_array.is_array(e)) { vector stores; expr_ref_vector args(m); expr_ref else_case(m); if (extract_array_func_interp(result, stores, else_case)) { result = m_array.mk_const_array(m.get_sort(e), else_case); while (!stores.empty() && stores.back().back() == else_case) { stores.pop_back(); } for (unsigned i = stores.size(); i > 0; ) { --i; args.resize(1); args[0] = result; args.append(stores[i]); result = m_array.mk_store(args); } return result; } } return result; } z3-z3-4.8.7/src/model/model_implicant.h000066400000000000000000000072221356505360400176320ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: model_implicant.h Abstract: Facility to extract prime implicant from model. Author: Krystof Hoder (t-khoder) 2011-8-19. Revision History: --*/ #ifndef MODEL_IMPLICANT_H_ #define MODEL_IMPLICANT_H_ #include "ast/ast.h" #include "ast/ast_pp.h" #include "util/obj_hashtable.h" #include "util/ref_vector.h" #include "util/trace.h" #include "util/vector.h" #include "ast/arith_decl_plugin.h" #include "ast/array_decl_plugin.h" class model; class model_core; class model_implicant { ast_manager& m; arith_util m_arith; array_util m_array; obj_map m_numbers; expr_ref_vector m_refs; obj_map m_values; model_ref m_model; //00 -- non-visited //01 -- X //10 -- false //11 -- true expr_mark m1; expr_mark m2; expr_mark m_visited; void reset(); void setup_model(model_ref& model); void assign_value(expr* e, expr* v); void collect(ptr_vector const& formulas, ptr_vector& tocollect); void process_formula(app* e, ptr_vector& todo, ptr_vector& tocollect); expr_ref_vector prune_by_cone_of_influence(ptr_vector const & formulas); void eval_arith(app* e); void eval_basic(app* e); void eval_eq(app* e, expr* arg1, expr* arg2); void eval_array_eq(app* e, expr* arg1, expr* arg2); void inherit_value(expr* e, expr* v); inline bool is_unknown(expr* x) { return !m1.is_marked(x) && !m2.is_marked(x); } inline void set_unknown(expr* x) { m1.mark(x, false); m2.mark(x, false); } inline bool is_x(expr* x) { return !m1.is_marked(x) && m2.is_marked(x); } inline bool is_false(expr* x) { return m1.is_marked(x) && !m2.is_marked(x); } inline bool is_true(expr* x) { return m1.is_marked(x) && m2.is_marked(x); } inline void set_x(expr* x) { SASSERT(is_unknown(x)); m2.mark(x); } inline void set_v(expr* x) { SASSERT(is_unknown(x)); m1.mark(x); } inline void set_false(expr* x) { SASSERT(is_unknown(x)); m1.mark(x); } inline void set_true(expr* x) { SASSERT(is_unknown(x)); m1.mark(x); m2.mark(x); } inline void set_bool(expr* x, bool v) { if (v) { set_true(x); } else { set_false(x); } } inline rational const& get_number(expr* x) const { return m_numbers.find(x); } inline void set_number(expr* x, rational const& v) { set_v(x); TRACE("pdr_verbose", tout << mk_pp(x,m) << " " << v << "\n";); m_numbers.insert(x,v); } inline expr* get_value(expr* x) { return m_values.find(x); } inline void set_value(expr* x, expr* v) { set_v(x); m_refs.push_back(v); m_values.insert(x, v); } bool check_model(ptr_vector const & formulas); bool extract_array_func_interp(expr* a, vector& stores, expr_ref& else_case); void eval_exprs(expr_ref_vector& es); public: model_implicant(ast_manager& m) : m(m), m_arith(m), m_array(m), m_refs(m) {} /** \brief extract equalities from model that suffice to satisfy formula. \pre model satisfies formulas */ expr_ref_vector minimize_model(ptr_vector const & formulas, model_ref& mdl); /** \brief extract literals from model that satisfy formulas. \pre model satisfies formulas */ expr_ref_vector minimize_literals(ptr_vector const & formulas, model_ref& mdl); /** for_each_expr visitor. */ void operator()(expr* e) {} expr_ref eval(model_ref& mdl, expr* e); expr_ref eval(model_ref& mdl, func_decl* d); }; #endif z3-z3-4.8.7/src/model/model_params.pyg000066400000000000000000000013171356505360400175040ustar00rootroot00000000000000def_module_params('model', export=True, params=(('partial', BOOL, False, 'enable/disable partial function interpretations'), ('v1', BOOL, False, 'use Z3 version 1.x pretty printer'), ('v2', BOOL, False, 'use Z3 version 2.x (x <= 16) pretty printer'), ('compact', BOOL, True, 'try to compact function graph (i.e., function interpretations that are lookup tables)'), ('inline_def', BOOL, False, 'inline local function definitions ignoring possible expansion'), ('completion', BOOL, False, 'enable/disable model completion'), )) z3-z3-4.8.7/src/model/model_pp.cpp000066400000000000000000000055501356505360400166260ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: model_pp.cpp Abstract: Pretty printer for models for debugging purposes. Author: Leonardo de Moura (leonardo) Revision History: --*/ #include "model/model_pp.h" #include "model/model_core.h" #include "ast/ast_pp.h" #include "ast/ast_smt2_pp.h" #include "ast/used_symbols.h" #include "ast/pp.h" static void display_uninterp_sorts(std::ostream & out, model_core const & md) { ast_manager & m = md.get_manager(); unsigned sz = md.get_num_uninterpreted_sorts(); for (unsigned i = 0; i < sz; i++) { sort * s = md.get_uninterpreted_sort(i); out << "(define-sort " << mk_pp(s, m); for (expr* e : md.get_universe(s)) { out << " " << mk_ismt2_pp(e, m); } out << ")\n"; } } static void display_constants(std::ostream & out, model_core const & md) { ast_manager & m = md.get_manager(); unsigned sz = md.get_num_constants(); for (unsigned i = 0; i < sz; i++) { func_decl * c = md.get_constant(i); char const * d = "(define "; std::string n = c->get_name().str(); unsigned indent = static_cast(n.length() + strlen(d) + 1); out << d << n << " " << mk_ismt2_pp(md.get_const_interp(c), m, indent) << ")\n"; } } static void display_functions(std::ostream & out, model_core const & md) { ast_manager & m = md.get_manager(); unsigned sz = md.get_num_functions(); for (unsigned i = 0; i < sz; i++) { func_decl * f = md.get_function(i); out << "(define (" << f->get_name(); unsigned arity = f->get_arity(); func_interp * fi = md.get_func_interp(f); for (unsigned j = 0; j < arity; j++) { out << " " << "x!" << j; } out << ")\n"; unsigned num_entries = fi->num_entries(); for (unsigned j = 0; j < num_entries; j++) { func_entry const * curr = fi->get_entry(j); out << " (if "; if (arity > 1) out << "(and "; for (unsigned j = 0; j < arity; j++) { out << "(= x!" << j << " " << mk_ismt2_pp(curr->get_arg(j), m) << ")"; if (j + 1 < arity) out << " "; } if (arity > 1) out << ")"; out << " " << mk_ismt2_pp(curr->get_result(), m) << "\n"; } if (num_entries > 0) out << " "; if (fi->is_partial()) out << " #unspecified"; else { out << " " << mk_ismt2_pp(fi->get_else(), m, params_ref(), 5, arity, "x"); } for (unsigned j = 0; j < num_entries; j++) out << ")"; out << ")\n"; } } void model_pp(std::ostream & out, model_core const & md) { display_uninterp_sorts(out, md); display_constants(out, md); display_functions(out, md); } z3-z3-4.8.7/src/model/model_pp.h000066400000000000000000000005431356505360400162700ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: model_pp.h Abstract: Pretty printer for models for debugging purposes. Author: Leonardo de Moura (leonardo) Revision History: --*/ #ifndef MODEL_PP_H_ #define MODEL_PP_H_ #include class model_core; void model_pp(std::ostream & out, model_core const & m); #endif z3-z3-4.8.7/src/model/model_smt2_pp.cpp000066400000000000000000000275451356505360400176030ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: model_smt2_pp.cpp Abstract: Pretty printer for models using SMT2 format. Author: Leonardo de Moura (leonardo) Revision History: --*/ #include #include "model/model_smt2_pp.h" #include "ast/ast_smt2_pp.h" #include "ast/func_decl_dependencies.h" #include "ast/recfun_decl_plugin.h" #include "ast/pp.h" using namespace format_ns; static void pp_indent(std::ostream & out, unsigned indent) { for (unsigned i = 0; i < indent; i++) out << " "; } static unsigned pp_symbol(std::ostream & out, symbol const & s) { if (is_smt2_quoted_symbol(s)) { std::string str = mk_smt2_quoted_symbol(s); out << str; return static_cast(str.length()); } else if (s.is_numerical()) { std::string str = s.str(); out << str; return static_cast(str.length()); } else { out << s.bare_str(); return static_cast(strlen(s.bare_str())); } } #define TAB_SZ 2 static void pp_uninterp_sorts(std::ostream & out, ast_printer_context & ctx, model_core const & md, unsigned indent) { ast_manager & m = ctx.get_ast_manager(); ptr_buffer f_conds; unsigned num = md.get_num_uninterpreted_sorts(); for (unsigned i = 0; i < num; i++) { sort * s = md.get_uninterpreted_sort(i); ptr_vector const & u = md.get_universe(s); std::ostringstream buffer; buffer << "universe for "; ctx.display(buffer, s, 13); buffer << ":\n"; pp_indent(buffer, TAB_SZ); for (expr* e : u) { SASSERT(is_app(e)); app * c = to_app(e); pp_symbol(buffer, c->get_decl()->get_name()); buffer << " "; } buffer << "\n-----------"; std::string buffer_str = buffer.str(); unsigned len = static_cast(buffer_str.length()); pp_indent(out, indent); out << ";; "; for (unsigned i = 0; i < len; i++) { char c = buffer_str[i]; if (c == '\n') { out << "\n"; pp_indent(out, indent); out << ";; "; } else { out << c; } } out << "\n"; pp_indent(out, indent); out << ";; definitions for universe elements:\n"; for (expr * e : u) { app * c = to_app(e); pp_indent(out, indent); out << "(declare-fun "; unsigned len = pp_symbol(out, c->get_decl()->get_name()); out << " () "; ctx.display(out, c->get_decl()->get_range(), indent + len + 16); out << ")\n"; } pp_indent(out, indent); out << ";; cardinality constraint:\n"; f_conds.reset(); format * var = mk_string(m, "x"); for (expr* e : u) { app * c = to_app(e); symbol csym = c->get_decl()->get_name(); std::string cname; if (is_smt2_quoted_symbol(csym)) cname = mk_smt2_quoted_symbol(csym); else cname = csym.str(); format * c_args[2] = { var, mk_string(m, cname.c_str()) }; f_conds.push_back(mk_seq1(m, c_args, c_args+2, f2f(), "=")); } SASSERT(!f_conds.empty()); format * f_cond; if (f_conds.size() > 1) f_cond = mk_seq1(m, f_conds.begin(), f_conds.end(), f2f(), "or"); else f_cond = f_conds[0]; format_ref f_s(fm(m)); ctx.pp(s, f_s); format * f_args[2] = { mk_compose(m, mk_string(m, "((x "), mk_indent(m, 4, mk_compose(m, f_s.get(), mk_string(m, "))")))), f_cond }; format_ref f_card(fm(m)); f_card = mk_indent(m, indent, mk_seq1(m, f_args, f_args+2, f2f(), "forall")); pp_indent(out, indent); pp(out, f_card, m); out << "\n"; pp_indent(out, indent); out << ";; -----------\n"; } } static void pp_consts(std::ostream & out, ast_printer_context & ctx, model_core const & md, unsigned indent) { unsigned num = md.get_num_constants(); for (unsigned i = 0; i < num; i++) { func_decl * c = md.get_constant(i); expr * c_i = md.get_const_interp(c); pp_indent(out, indent); out << "(define-fun "; unsigned len = pp_symbol(out, c->get_name()); out << " () "; ctx.display(out, c->get_range(), indent + len + 16); out << "\n"; pp_indent(out, indent + TAB_SZ); ctx.display(out, c_i); out << ")\n"; } } void sort_fun_decls(ast_manager & m, model_core const & md, ptr_buffer & result) { func_decl_set visited; ptr_vector todo; unsigned sz = md.get_num_functions(); for (unsigned i = 0; i < sz; i++) { func_decl * f = md.get_function(i); if (visited.contains(f)) continue; visited.insert(f); todo.push_back(f); while (!todo.empty()) { func_decl * curr = todo.back(); func_interp * curr_i = md.get_func_interp(curr); SASSERT(curr_i != 0); if (!curr_i->is_partial()) { func_decl_set deps; bool all_visited = true; collect_func_decls(m, curr_i->get_else(), deps); for (func_decl * d : deps) { if (d->get_arity() > 0 && md.has_interpretation(d) && !visited.contains(d)) { todo.push_back(d); visited.insert(d); all_visited = false; } } if (!all_visited) continue; } todo.pop_back(); result.push_back(curr); } } } static void pp_funs(std::ostream & out, ast_printer_context & ctx, model_core const & md, unsigned indent) { ast_manager & m = ctx.get_ast_manager(); recfun::util recfun_util(m); sbuffer var_names; ptr_buffer f_var_names; ptr_buffer f_arg_decls; ptr_buffer f_entries; ptr_buffer f_entry_conds; ptr_buffer func_decls; sort_fun_decls(m, md, func_decls); for (func_decl * f : func_decls) { if (recfun_util.is_defined(f) && !recfun_util.is_generated(f)) { continue; } if (!m.is_considered_uninterpreted(f)) { continue; } func_interp * f_i = md.get_func_interp(f); SASSERT(f->get_arity() == f_i->get_arity()); format_ref body(fm(m)); var_names.reset(); if (f_i->is_partial()) { body = mk_string(m, "#unspecified"); for (unsigned j = 0; j < f->get_arity(); j++) { std::stringstream strm; strm << "x!" << (j+1); var_names.push_back(symbol(strm.str().c_str())); } } else { ctx.pp(f_i->get_else(), f->get_arity(), "x", body, var_names); } TRACE("model_smt2_pp", for (unsigned i = 0; i < var_names.size(); i++) tout << var_names[i] << "\n";); f_var_names.reset(); for (auto const& vn : var_names) f_var_names.push_back(mk_string(m, vn.bare_str())); f_arg_decls.reset(); for (unsigned i = 0; i < f->get_arity(); i++) { format_ref f_domain(fm(m)); ctx.pp(f->get_domain(i), f_domain); format * args[2] = { f_var_names[i], f_domain.get() }; f_arg_decls.push_back(mk_seq5(m, args, args+2, f2f())); } format * f_domain = mk_seq5(m, f_arg_decls.begin(), f_arg_decls.end(), f2f()); format_ref f_range(fm(m)); ctx.pp(f->get_range(), f_range); if (f_i->num_entries() > 0) { f_entries.reset(); for (unsigned i = 0; i < f_i->num_entries(); i++) { func_entry const * e = f_i->get_entry(i); f_entry_conds.reset(); for (unsigned j = 0; j < f->get_arity(); j++) { format_ref f_arg(fm(m)); ctx.pp(e->get_arg(j), f_arg); format * eq_args[2] = { f_var_names[j], f_arg.get() }; f_entry_conds.push_back(mk_seq1(m, eq_args, eq_args+2, f2f(), "=")); } format * f_entry_cond; SASSERT(!f_entry_conds.empty()); if (f_entry_conds.size() > 1) f_entry_cond = mk_seq1(m, f_entry_conds.begin(), f_entry_conds.end(), f2f(), "and"); else f_entry_cond = f_entry_conds[0]; format_ref f_result(fm(m)); ctx.pp(e->get_result(), f_result); if (i > 0) f_entries.push_back(mk_line_break(m)); f_entries.push_back(mk_group(m, mk_compose(m, mk_string(m, "(ite "), mk_indent(m, 5, f_entry_cond), mk_indent(m, TAB_SZ, mk_compose(m, mk_line_break(m), f_result.get()))))); } f_entries.push_back(mk_indent(m, TAB_SZ, mk_compose(m, mk_line_break(m), body.get()))); for (unsigned i = 0; i < f_i->num_entries(); i++) f_entries.push_back(mk_string(m, ")")); body = mk_compose(m, f_entries.size(), f_entries.c_ptr()); } format_ref def(fm(m)); std::string fname; if (is_smt2_quoted_symbol(f->get_name())) fname = mk_smt2_quoted_symbol(f->get_name()); else fname = f->get_name().str(); def = mk_indent(m, indent, mk_compose(m, mk_compose(m, mk_string(m, "(define-fun "), mk_string(m, fname.c_str()), mk_string(m, " "), mk_compose(m, f_domain, mk_string(m, " "), f_range)), mk_indent(m, TAB_SZ, mk_compose(m, mk_line_break(m), body.get(), mk_string(m, ")"))))); pp_indent(out, indent); pp(out, def.get(), m); out << "\n"; } } void model_smt2_pp(std::ostream & out, ast_printer_context & ctx, model_core const & m, unsigned indent) { pp_uninterp_sorts(out, ctx, m, indent); pp_consts(out, ctx, m, indent); pp_funs(out, ctx, m, indent); } void model_smt2_pp(std::ostream & out, ast_manager & m, model_core const & md, unsigned indent) { scoped_ptr ctx; ctx = mk_simple_ast_printer_context(m); pp_uninterp_sorts(out, *(ctx.get()), md, indent); pp_consts(out, *(ctx.get()), md, indent); pp_funs(out, *(ctx.get()), md, indent); } std::ostream& operator<<(std::ostream& out, model_core const& m) { model_smt2_pp(out, m.get_manager(), m, 0); return out; } z3-z3-4.8.7/src/model/model_smt2_pp.h000066400000000000000000000010251356505360400172310ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: model_smt2_pp.h Abstract: Pretty printer for models using SMT2 format. Author: Leonardo de Moura (leonardo) Revision History: --*/ #ifndef MODEL_SMT2_PP_H_ #define MODEL_SMT2_PP_H_ #include "ast/ast_printer.h" #include "model/model_core.h" void model_smt2_pp(std::ostream & out, ast_printer_context & ctx, model_core const & m, unsigned indent); void model_smt2_pp(std::ostream & out, ast_manager & m, model_core const & md, unsigned indent); #endif z3-z3-4.8.7/src/model/model_v2_pp.cpp000066400000000000000000000046631356505360400172410ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: model_v2_pp.cpp Abstract: V2 Pretty printer for models. (backward compatibility) Author: Leonardo de Moura (leonardo) Revision History: --*/ #include "model/model_v2_pp.h" #include "model/model_core.h" #include "ast/ast_pp.h" static void display_function(std::ostream & out, model_core const & md, func_decl * f, bool partial) { ast_manager & m = md.get_manager(); func_interp * g = md.get_func_interp(f); out << f->get_name() << " -> {\n"; unsigned num_entries = g->num_entries(); unsigned arity = g->get_arity(); char const * else_str = num_entries == 0 ? " " : " else -> "; unsigned else_indent = static_cast(strlen(else_str)); for (unsigned i = 0; i < num_entries; i++) { func_entry const * entry = g->get_entry(i); out << " "; for (unsigned j = 0; j < arity; j++) { expr * arg = entry->get_arg(j); out << mk_pp(arg, m); out << " "; } out << "-> "; out << mk_pp(entry->get_result(), m); out << "\n"; } if (partial) { out << else_str << "#unspecified\n"; } else { expr * else_val = g->get_else(); out << else_str; if (else_val) out << mk_pp(else_val, m, else_indent); else out << "#unspecified"; out << "\n"; } out << "}\n"; } static void display_functions(std::ostream & out, model_core const & md, bool partial) { unsigned sz = md.get_num_functions(); for (unsigned i = 0; i < sz; i++) { func_decl * f = md.get_function(i); display_function(out, md, f, partial); } } static void display_constants(std::ostream & out, model_core const & md) { ast_manager & m = md.get_manager(); unsigned sz = md.get_num_constants(); for (unsigned i = 0; i < sz; i++) { func_decl * d = md.get_constant(i); std::string name = d->get_name().str(); const char * arrow = " -> "; out << name << arrow; unsigned indent = static_cast(name.length() + strlen(arrow)); out << mk_pp(md.get_const_interp(d), m, indent) << "\n"; } } void model_v2_pp(std::ostream & out, model_core const & md, bool partial) { display_constants(out, md); display_functions(out, md, partial); } // debugging support void pp(model_core const & md) { model_v2_pp(std::cout, md, false); } z3-z3-4.8.7/src/model/model_v2_pp.h000066400000000000000000000006101356505360400166720ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: model_v2_pp.h Abstract: V2 Pretty printer for models. (backward compatibility) Author: Leonardo de Moura (leonardo) Revision History: --*/ #ifndef MODEL_V2_PP_H_ #define MODEL_V2_PP_H_ #include class model_core; void model_v2_pp(std::ostream & out, model_core const & m, bool partial = false); #endif z3-z3-4.8.7/src/model/numeral_factory.cpp000066400000000000000000000020471356505360400202170ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: numeral_factory.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-10-28. Revision History: --*/ #include "ast/ast_pp.h" #include "model/numeral_factory.h" app * arith_factory::mk_value_core(rational const & val, sort * s) { return m_util.mk_numeral(val, s); } arith_factory::arith_factory(ast_manager & m): numeral_factory(m, m.mk_family_id("arith")), m_util(m) { } arith_factory::~arith_factory() { } app * arith_factory::mk_num_value(rational const & val, bool is_int) { return numeral_factory::mk_value(val, is_int ? m_util.mk_int() : m_util.mk_real()); } bv_factory::bv_factory(ast_manager & m): numeral_factory(m, m.mk_family_id("bv")), m_util(m) { } bv_factory::~bv_factory() { } app * bv_factory::mk_value_core(rational const & val, sort * s) { return m_util.mk_numeral(val, s); } app * bv_factory::mk_num_value(rational const & val, unsigned bv_size) { return numeral_factory::mk_value(val, m_util.mk_sort(bv_size)); } z3-z3-4.8.7/src/model/numeral_factory.h000066400000000000000000000021571356505360400176660ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: numeral_factory.h Abstract: Author: Leonardo de Moura (leonardo) 2008-10-28. Revision History: --*/ #ifndef NUMERAL_FACTORY_H_ #define NUMERAL_FACTORY_H_ #include "ast/arith_decl_plugin.h" #include "ast/bv_decl_plugin.h" #include "model/value_factory.h" class numeral_factory : public simple_factory { public: numeral_factory(ast_manager & m, family_id fid):simple_factory(m, fid) {} ~numeral_factory() override {} }; class arith_factory : public numeral_factory { arith_util m_util; app * mk_value_core(rational const & val, sort * s) override; public: arith_factory(ast_manager & m); ~arith_factory() override; app * mk_num_value(rational const & val, bool is_int); }; class bv_factory : public numeral_factory { bv_util m_util; app * mk_value_core(rational const & val, sort * s) override; public: bv_factory(ast_manager & m); ~bv_factory() override; app * mk_num_value(rational const & val, unsigned bv_size); }; #endif /* NUMERAL_FACTORY_H_ */ z3-z3-4.8.7/src/model/seq_factory.h000066400000000000000000000075551356505360400170220ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: theory_seq_empty.h Abstract: Author: Nikolaj Bjorner (nbjorner) 2011-14-11 Revision History: --*/ #ifndef SEQ_FACTORY_H_ #define SEQ_FACTORY_H_ #include "ast/seq_decl_plugin.h" #include "model/model_core.h" class seq_factory : public value_factory { typedef hashtable symbol_set; model_core& m_model; ast_manager& m; seq_util u; symbol_set m_strings; unsigned m_next; std::string m_unique_delim; obj_map m_unique_sequences; expr_ref_vector m_trail; public: seq_factory(ast_manager & m, family_id fid, model_core& md): value_factory(m, fid), m_model(md), m(m), u(m), m_next(0), m_unique_delim("!"), m_trail(m) { m_strings.insert(symbol("")); m_strings.insert(symbol("a")); m_strings.insert(symbol("b")); } void add_trail(expr* e) { m_trail.push_back(e); } void set_prefix(char const* p) { m_unique_delim = p; } // generic method for setting unique sequences void set_prefix(expr* uniq) { m_trail.push_back(uniq); m_unique_sequences.insert(m.get_sort(uniq), uniq); } expr* get_some_value(sort* s) override { if (u.is_seq(s)) { return u.str.mk_empty(s); } sort* seq = nullptr; if (u.is_re(s, seq)) { return u.re.mk_to_re(u.str.mk_empty(seq)); } UNREACHABLE(); return nullptr; } bool get_some_values(sort* s, expr_ref& v1, expr_ref& v2) override { if (u.is_string(s)) { v1 = u.str.mk_string(symbol("a")); v2 = u.str.mk_string(symbol("b")); return true; } sort* ch; if (u.is_seq(s, ch)) { if (m_model.get_some_values(ch, v1, v2)) { v1 = u.str.mk_unit(v1); v2 = u.str.mk_unit(v2); return true; } else { return false; } } NOT_IMPLEMENTED_YET(); return false; } expr* get_fresh_value(sort* s) override { if (u.is_string(s)) { while (true) { std::ostringstream strm; strm << m_unique_delim << std::hex << m_next++ << std::dec << m_unique_delim; symbol sym(strm.str().c_str()); if (m_strings.contains(sym)) continue; m_strings.insert(sym); return u.str.mk_string(sym); } } sort* seq = nullptr, *ch = nullptr; if (u.is_re(s, seq)) { expr* v0 = get_fresh_value(seq); return u.re.mk_to_re(v0); } if (u.is_char(s)) { //char s[2] = { ++m_char, 0 }; //return u.str.mk_char(zstring(s), 0); return u.str.mk_char(zstring("a"), 0); } if (u.is_seq(s, ch)) { expr* v = m_model.get_fresh_value(ch); if (!v) return nullptr; return u.str.mk_unit(v); } UNREACHABLE(); return nullptr; } void register_value(expr* n) override { symbol sym; if (u.str.is_string(n, sym)) { m_strings.insert(sym); if (sym.str().find(m_unique_delim) != std::string::npos) { add_new_delim(); } } } private: void add_new_delim() { bool found = true; while (found) { found = false; m_unique_delim += "!"; symbol_set::iterator it = m_strings.begin(), end = m_strings.end(); for (; it != end && !found; ++it) { found = it->str().find(m_unique_delim) != std::string::npos; } } } }; #endif z3-z3-4.8.7/src/model/struct_factory.cpp000066400000000000000000000031461356505360400201010ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: struct_factory.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-11-06. Revision History: --*/ #include "model/struct_factory.h" #include "model/model_core.h" struct_factory::value_set * struct_factory::get_value_set(sort * s) { value_set * set = nullptr; if (!m_sort2value_set.find(s, set)) { set = alloc(value_set); m_sort2value_set.insert(s, set); m_sorts.push_back(s); m_sets.push_back(set); } SASSERT(set != 0); return set; } struct_factory::struct_factory(ast_manager & m, family_id fid, model_core & md): value_factory(m, fid), m_model(md), m_values(m), m_sorts(m) { } struct_factory::~struct_factory() { std::for_each(m_sets.begin(), m_sets.end(), delete_proc()); } void struct_factory::register_value(expr * new_value) { sort * s = m_manager.get_sort(new_value); value_set * set = get_value_set(s); if (!set->contains(new_value)) { m_values.push_back(new_value); set->insert(new_value); } } bool struct_factory::get_some_values(sort * s, expr_ref & v1, expr_ref & v2) { value_set * set = get_value_set(s); switch (set->size()) { case 0: v1 = get_fresh_value(s); v2 = get_fresh_value(s); return v1 != 0 && v2 != 0; case 1: v1 = get_some_value(s); v2 = get_fresh_value(s); return v2 != 0; default: obj_hashtable::iterator it = set->begin(); v1 = *it; ++it; v2 = *it; return true; } } z3-z3-4.8.7/src/model/struct_factory.h000066400000000000000000000021001356505360400175330ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: struct_factory.h Abstract: Author: Leonardo de Moura (leonardo) 2008-11-06. Revision History: --*/ #ifndef STRUCT_FACTORY_H_ #define STRUCT_FACTORY_H_ #include "model/value_factory.h" #include "util/obj_hashtable.h" class model_core; /** \brief Abstract factory for structured values such as: arrays and algebraic datatypes. */ class struct_factory : public value_factory { protected: typedef obj_hashtable value_set; typedef obj_map sort2value_set; model_core & m_model; sort2value_set m_sort2value_set; expr_ref_vector m_values; sort_ref_vector m_sorts; ptr_vector m_sets; value_set * get_value_set(sort * s); public: struct_factory(ast_manager & m, family_id fid, model_core & md); ~struct_factory() override; bool get_some_values(sort * s, expr_ref & v1, expr_ref & v2) override; void register_value(expr * array_value) override; }; #endif /* STRUCT_FACTORY_H_ */ z3-z3-4.8.7/src/model/value_factory.cpp000066400000000000000000000056271356505360400176770ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: value_factory.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-10-28. Revision History: --*/ #include "model/value_factory.h" value_factory::value_factory(ast_manager & m, family_id fid): m_manager(m), m_fid(fid) { } value_factory::~value_factory() { } basic_factory::basic_factory(ast_manager & m): value_factory(m, m.get_basic_family_id()) { } expr * basic_factory::get_some_value(sort * s) { if (m_manager.is_bool(s)) return m_manager.mk_false(); return nullptr; } bool basic_factory::get_some_values(sort * s, expr_ref & v1, expr_ref & v2) { if (m_manager.is_bool(s)) { v1 = m_manager.mk_false(); v2 = m_manager.mk_true(); return true; } return false; } expr * basic_factory::get_fresh_value(sort * s) { return nullptr; } user_sort_factory::user_sort_factory(ast_manager & m): simple_factory(m, m.mk_family_id("user-sort")) { } void user_sort_factory::freeze_universe(sort * s) { if (!m_finite.contains(s)) { value_set * set = nullptr; m_sort2value_set.find(s, set); if (!m_sort2value_set.find(s, set) || set->m_values.empty()) { // we cannot freeze an empty universe. get_some_value(s); // add one element to the universe... } SASSERT(m_sort2value_set.find(s, set) && set != 0 && !set->m_values.empty()); m_finite.insert(s); } } obj_hashtable const & user_sort_factory::get_known_universe(sort * s) const { value_set * set = nullptr; if (m_sort2value_set.find(s, set)) { return set->m_values; } return m_empty_universe; } expr * user_sort_factory::get_some_value(sort * s) { if (is_finite(s)) { value_set * set = nullptr; m_sort2value_set.find(s, set); SASSERT(set != 0); SASSERT(!set->m_values.empty()); return *(set->m_values.begin()); } return simple_factory::get_some_value(s); } bool user_sort_factory::get_some_values(sort * s, expr_ref & v1, expr_ref & v2) { if (is_finite(s)) { value_set * set = nullptr; if (m_sort2value_set.find(s, set) && set->m_values.size() >= 2) { obj_hashtable::iterator it = set->m_values.begin(); v1 = *it; ++it; v2 = *it; return true; } return false; } return simple_factory::get_some_values(s, v1, v2); } expr * user_sort_factory::get_fresh_value(sort * s) { if (is_finite(s)) return nullptr; return simple_factory::get_fresh_value(s); } void user_sort_factory::register_value(expr * n) { SASSERT(!is_finite(m_manager.get_sort(n))); simple_factory::register_value(n); } app * user_sort_factory::mk_value_core(unsigned const & val, sort * s) { return m_manager.mk_model_value(val, s); } z3-z3-4.8.7/src/model/value_factory.h000066400000000000000000000167531356505360400173460ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: value_factory.h Abstract: Author: Leonardo de Moura (leonardo) 2008-10-28. Revision History: --*/ #ifndef VALUE_FACTORY_H_ #define VALUE_FACTORY_H_ #include "ast/ast.h" #include "util/obj_hashtable.h" /** \brief Auxiliary object used during model construction. */ class value_factory { protected: ast_manager & m_manager; family_id m_fid; public: value_factory(ast_manager & m, family_id fid); virtual ~value_factory(); /** \brief Return some value of the given sort. The result is always different from zero. */ virtual expr * get_some_value(sort * s) = 0; /** \brief Return two distinct values of the given sort. The results are stored in v1 and v2. Return false if the intended interpretation of the given sort has only one element. */ virtual bool get_some_values(sort * s, expr_ref & v1, expr_ref & v2) = 0; /** \brief Return a fresh value of the given sort. Return 0 if it is not possible to do that (e.g., the sort is finite). */ virtual expr * get_fresh_value(sort * s) = 0; /** \brief Used to register that the given value was used in model construction. So, get_fresh_value cannot return this value anymore. */ virtual void register_value(expr * n) = 0; family_id get_family_id() const { return m_fid; } }; class basic_factory : public value_factory { public: basic_factory(ast_manager & m); expr * get_some_value(sort * s) override; bool get_some_values(sort * s, expr_ref & v1, expr_ref & v2) override; expr * get_fresh_value(sort * s) override; void register_value(expr * n) override { } }; /** \brief Template for value factories for numeric (and enumeration-like) sorts */ template class simple_factory : public value_factory { protected: struct value_set { obj_hashtable m_values; Number m_next; value_set(): m_next(0) { } }; typedef obj_map sort2value_set; sort2value_set m_sort2value_set; expr_ref_vector m_values; sort_ref_vector m_sorts; ptr_vector m_sets; value_set * get_value_set(sort * s) { value_set * set = nullptr; if (!m_sort2value_set.find(s, set)) { set = alloc(value_set); m_sort2value_set.insert(s, set); m_sorts.push_back(s); m_sets.push_back(set); } SASSERT(set != 0); DEBUG_CODE({ value_set * set2 = 0; SASSERT(m_sort2value_set.find(s, set2)); SASSERT(set == set2); }); return set; } virtual app * mk_value_core(Number const & val, sort * s) = 0; app * mk_value(Number const & val, sort * s, bool & is_new) { value_set * set = get_value_set(s); app * new_val = mk_value_core(val, s); is_new = false; if (!set->m_values.contains(new_val)) { m_values.push_back(new_val); set->m_values.insert(new_val); is_new = true; } SASSERT(set->m_values.contains(new_val)); return new_val; } public: simple_factory(ast_manager & m, family_id fid): value_factory(m, fid), m_values(m), m_sorts(m) { } ~simple_factory() override { std::for_each(m_sets.begin(), m_sets.end(), delete_proc()); } expr * get_some_value(sort * s) override { value_set * set = nullptr; expr * result = nullptr; if (m_sort2value_set.find(s, set) && !set->m_values.empty()) result = *(set->m_values.begin()); else result = mk_value(Number(0), s); return result; } bool get_some_values(sort * s, expr_ref & v1, expr_ref & v2) override { value_set * set = nullptr; if (m_sort2value_set.find(s, set)) { switch (set->m_values.size()) { case 0: v1 = mk_value(Number(0), s); v2 = mk_value(Number(1), s); break; case 1: v1 = *(set->m_values.begin()); v2 = mk_value(Number(0), s); if (v1 == v2) v2 = mk_value(Number(1), s); break; default: obj_hashtable::iterator it = set->m_values.begin(); v1 = *it; ++it; v2 = *it; break; } SASSERT(v1 != v2); return true; } v1 = mk_value(Number(0), s); v2 = mk_value(Number(1), s); return true; } expr * get_fresh_value(sort * s) override { value_set * set = get_value_set(s); bool is_new = false; expr * result = nullptr; sort_info* s_info = s->get_info(); sort_size const* sz = s_info?&s_info->get_num_elements():nullptr; bool has_max = false; Number max_size(0); if (sz && sz->is_finite() && sz->size() < UINT_MAX) { unsigned usz = static_cast(sz->size()); max_size = Number(usz); has_max = true; } Number start = set->m_next; Number & next = set->m_next; while (!is_new) { result = mk_value(next, s, is_new); next++; if (has_max && next > max_size + start) { return nullptr; } } SASSERT(result != 0); return result; } void register_value(expr * n) override { sort * s = this->m_manager.get_sort(n); value_set * set = get_value_set(s); if (!set->m_values.contains(n)) { m_values.push_back(n); set->m_values.insert(n); } } virtual app * mk_value(Number const & val, sort * s) { bool is_new; return mk_value(val, s, is_new); } unsigned get_num_sorts() const { return m_sorts.size(); } sort * get_sort(unsigned idx) const { return m_sorts.get(idx); } }; /** \brief Factory for creating values for uninterpreted sorts and user declared (uninterpreted) sorts. */ class user_sort_factory : public simple_factory { obj_hashtable m_finite; //!< set of sorts that are marked as finite. obj_hashtable m_empty_universe; app * mk_value_core(unsigned const & val, sort * s) override; public: user_sort_factory(ast_manager & m); ~user_sort_factory() override {} /** \brief Make the universe of \c s finite, by preventing new elements to be added to its universe. */ void freeze_universe(sort * s); /** \brief Return true if the universe of \c s is frozen and finite. */ bool is_finite(sort * s) const { return m_finite.contains(s); } /** \brief Return the "known" universe of \c s. It doesn't matter whether s is finite or not. If \c s is finite, then it is the whole universe. */ obj_hashtable const & get_known_universe(sort * s) const; /** \brief Return sorts with finite interpretations. */ obj_hashtable const & get_finite_sorts() const { return m_finite; } expr * get_some_value(sort * s) override; bool get_some_values(sort * s, expr_ref & v1, expr_ref & v2) override; expr * get_fresh_value(sort * s) override; void register_value(expr * n) override; }; #endif /* VALUE_FACTORY_H_ */ z3-z3-4.8.7/src/muz/000077500000000000000000000000001356505360400140315ustar00rootroot00000000000000z3-z3-4.8.7/src/muz/README000066400000000000000000000007311356505360400147120ustar00rootroot00000000000000muZ: routines related to solving satisfiability of Horn clauses and solving Datalog programs. - base - contains base routines and the main context for maintaining fixedpoint solvers - transforms - common rule transformations - rel - relational algebra based Datalog engine - pdr - PDR based Horn clause solver - clp - Dart/Symbolic execution-based solver - tab - Tabulation based solver - bmc - Bounded model checking based solver - fp - main exported routines z3-z3-4.8.7/src/muz/base/000077500000000000000000000000001356505360400147435ustar00rootroot00000000000000z3-z3-4.8.7/src/muz/base/CMakeLists.txt000066400000000000000000000005661356505360400175120ustar00rootroot00000000000000z3_add_component(muz SOURCES bind_variables.cpp dl_boogie_proof.cpp dl_context.cpp dl_costs.cpp dl_rule.cpp dl_rule_set.cpp dl_rule_subsumption_index.cpp dl_rule_transformer.cpp dl_util.cpp hnf.cpp rule_properties.cpp COMPONENT_DEPENDENCIES aig_tactic qe sat smt smt2parser PYG_FILES fp_params.pyg ) z3-z3-4.8.7/src/muz/base/bind_variables.cpp000066400000000000000000000107551356505360400204230ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: bind_variables.cpp Abstract: Utility to find constants that are declared as variables. Author: Nikolaj Bjorner (nbjorner) 9-24-2014 Notes: --*/ #include "muz/base/bind_variables.h" bind_variables::bind_variables(ast_manager & m): m(m), m_vars(m), m_pinned(m) {} bind_variables::~bind_variables() { } expr_ref bind_variables::operator()(expr* fml, bool is_forall) { if (m_vars.empty()) { return expr_ref(fml, m); } SASSERT(m_pinned.empty()); expr_ref result = abstract(fml, m_cache, 0); if (!m_names.empty()) { m_bound.reverse(); m_names.reverse(); result = m.mk_quantifier(is_forall ? forall_k : exists_k, m_bound.size(), m_bound.c_ptr(), m_names.c_ptr(), result); } m_pinned.reset(); m_cache.reset(); m_names.reset(); m_bound.reset(); for (var2bound::iterator it = m_var2bound.begin(); it != m_var2bound.end(); ++it) { it->m_value = 0; } return result; } expr_ref bind_variables::abstract(expr* term, cache_t& cache, unsigned scope) { unsigned sz = m_todo.size(); m_todo.push_back(term); m_args.reset(); expr* b, *arg; while (m_todo.size() > sz) { expr* e = m_todo.back(); if (cache.contains(e)) { m_todo.pop_back(); continue; } switch(e->get_kind()) { case AST_VAR: { // SASSERT(to_var(e)->get_idx() >= scope); // mixing bound variables and free is possible for the caller, // but not proper use. // So we assert here even though we don't check for it. cache.insert(e, e); m_todo.pop_back(); break; } case AST_APP: { app* a = to_app(e); var2bound::obj_map_entry* w = m_var2bound.find_core(a); if (w) { var* v = w->get_data().m_value; if (!v) { // allocate a bound index. v = m.mk_var(m_names.size(), m.get_sort(a)); m_names.push_back(a->get_decl()->get_name()); m_bound.push_back(m.get_sort(a)); w->get_data().m_value = v; m_pinned.push_back(v); } if (scope == 0) { cache.insert(e, v); } else { var* v1 = m.mk_var(scope + v->get_idx(), m.get_sort(v)); m_pinned.push_back(v1); cache.insert(e, v1); } m_todo.pop_back(); break; } bool all_visited = true; bool some_diff = false; m_args.reset(); for (unsigned i = 0; i < a->get_num_args(); ++i) { arg = a->get_arg(i); if (!cache.find(arg, b)) { m_todo.push_back(arg); all_visited = false; } else if (all_visited) { m_args.push_back(b); if (b != arg) { some_diff = true; } } } if (all_visited) { if (some_diff) { b = m.mk_app(a->get_decl(), m_args.size(), m_args.c_ptr()); m_pinned.push_back(b); } else { b = a; } cache.insert(e, b); m_todo.pop_back(); } break; } case AST_QUANTIFIER: { quantifier* q = to_quantifier(e); expr_ref_buffer patterns(m); expr_ref result1(m); unsigned new_scope = scope + q->get_num_decls(); cache_t new_cache; for (unsigned i = 0; i < q->get_num_patterns(); ++i) { patterns.push_back(abstract(q->get_pattern(i), new_cache, new_scope)); } result1 = abstract(q->get_expr(), new_cache, new_scope); b = m.update_quantifier(q, patterns.size(), patterns.c_ptr(), result1.get()); m_pinned.push_back(b); cache.insert(e, b); m_todo.pop_back(); break; } default: UNREACHABLE(); } } return expr_ref(cache.find(term), m); } void bind_variables::add_var(app* v) { m_vars.push_back(v); m_var2bound.insert(v, 0); } z3-z3-4.8.7/src/muz/base/bind_variables.h000066400000000000000000000017341356505360400200650ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: bind_variables.h Abstract: Utility to find constants that are declard as variables. Author: Nikolaj Bjorner (nbjorner) 9-24-2014 Notes: --*/ #ifndef BIND_VARIABLES_H_ #define BIND_VARIABLES_H_ #include "ast/ast.h" class bind_variables { typedef obj_map var2bound; typedef obj_map cache_t; ast_manager& m; app_ref_vector m_vars; obj_map m_cache; var2bound m_var2bound; expr_ref_vector m_pinned; ptr_vector m_bound; svector m_names; ptr_vector m_todo; ptr_vector m_args; expr_ref abstract(expr* fml, cache_t& cache, unsigned scope); public: bind_variables(ast_manager & m); ~bind_variables(); expr_ref operator()(expr* fml, bool is_forall); void add_var(app* v); }; #endif /* BIND_VARIABLES_H_ */ z3-z3-4.8.7/src/muz/base/dl_boogie_proof.cpp000066400000000000000000000230721356505360400206030ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ /** Example from Boogie: (derivation (step s!4 (main_loop_LoopHead true true) rule!3 (subst (= assertsPassed@@1 true) (= assertsPassed@2@@0 true) (= main_loop_LoopHead_assertsPassed true) ) (labels @950 +895 +668 +670 +893 +899 ) (ref true )) (step s!3 (main true false) rule!1 (subst (= assertsPassed true) (= assertsPassed@0 true) (= assertsPassed@2 false) (= main_assertsPassed false) ) (labels @839 +763 +547 +546 +761 +544 +545 +si_fcall_805 +681 +768 ) (ref s!4 )) (step s!2 (main_SeqInstr true false) rule!2 (subst (= assertsPassed@@0 true) (= assertsPassed@0@@0 false) (= main_SeqInstr_assertsPassed false) ) (labels @890 +851 +572 +si_fcall_883 +853 ) (ref s!3 )) (step s!1 (@Fail!0) rule!4 (subst (= assertsPassed@@2 true) (= main_SeqInstr_assertsPassed@@0 false) ) (labels ) (ref s!2 )) ) (model "tickleBool -> { true -> true false -> true else -> true } ") */ #include "muz/base/dl_boogie_proof.h" #include "model/model_pp.h" #include "ast/proofs/proof_utils.h" #include "ast/ast_pp.h" #include "ast/ast_util.h" namespace datalog { /** \brief push hyper-resolution steps upwards such that every use of hyper-resolution uses a premise that is not derived from hyper-resolution. perform the following rewrite: hr(hr(p1,p2,p3,..),p4,p5) => hr(p1,hr(p2,p4),p3,..,p5) */ void mk_input_resolution(proof_ref& pr) { ast_manager& m = pr.get_manager(); proof_ref pr1(m); proof_ref_vector premises1(m), premises2(m), premises(m); expr_ref conclusion1(m), conclusion2(m), conclusion(m); svector > positions1, positions2, positions; vector substs1, substs2, substs; if (m.is_hyper_resolve(pr, premises1, conclusion1, positions1, substs1) && m.is_hyper_resolve(premises1[0].get(), premises, conclusion2, positions, substs2)) { for (unsigned i = 1; i < premises1.size(); ++i) { pr1 = premises1[i].get(); mk_input_resolution(pr1); premises1[i] = pr1; } for (unsigned i = 0; i < premises.size(); ++i) { pr1 = premises[i].get(); mk_input_resolution(pr1); premises[i] = pr1; } unsigned sz = premises.size(); for (unsigned i = 1; i < sz; ++i) { proof* premise = premises[i].get(); expr_ref_vector literals(m); expr* l1, *l2; if (!m.is_implies(premise, l1, l2)) { continue; } flatten_and(l1, literals); positions2.reset(); premises2.reset(); premises2.push_back(premise); substs2.reset(); for (unsigned j = 0; j < literals.size(); ++j) { expr* lit = literals[j].get(); for (unsigned k = 1; k < premises1.size(); ++k) { if (m.get_fact(premises1[k].get()) == lit) { premises2.push_back(premises1[k].get()); positions2.push_back(std::make_pair(j+1,0)); substs2.push_back(expr_ref_vector(m)); break; } } } premises[i] = m.mk_hyper_resolve(premises2.size(), premises2.c_ptr(), l2, positions2, substs2); } conclusion = conclusion1; pr = m.mk_hyper_resolve(premises.size(), premises.c_ptr(), conclusion, positions, substs); } } void boogie_proof::set_proof(proof* p) { std::cout << "set proof\n"; m_proof = p; proof_utils::push_instantiations_up(m_proof); mk_input_resolution(m_proof); std::cout << "proof set\n"; } void boogie_proof::set_model(model* m) { m_model = m; } void boogie_proof::pp(std::ostream& out) { if (m_proof) { pp_proof(out); } if (m_model) { model_pp(out, *m_model); } } void boogie_proof::pp_proof(std::ostream& out) { vector steps; ptr_vector rules; rules.push_back(m_proof); steps.push_back(step()); obj_map index; index.insert(m_proof, 0); for (unsigned j = 0; j < rules.size(); ++j) { proof* p = rules[j]; proof_ref_vector premises(m); expr_ref conclusion(m); svector > positions; vector substs; expr* tmp; steps[j].m_fact = m.get_fact(p); m.is_implies(steps[j].m_fact, tmp, steps[j].m_fact); get_subst(p, steps[j].m_subst); get_labels(p, steps[j].m_labels); if (m.is_hyper_resolve(p, premises, conclusion, positions, substs)) { for (unsigned i = 1; i < premises.size(); ++i) { proof* premise = premises[i].get(); unsigned position = 0; if (!index.find(premise, position)) { position = rules.size(); rules.push_back(premise); steps.push_back(step()); index.insert(premise, position); } steps[j].m_refs.push_back(position); } get_rule_name(premises[0].get(), steps[j].m_rule_name); } } for (unsigned j = steps.size(); j > 0; ) { --j; step &s = steps[j]; // TBD // s.m_labels; // set references, compensate for reverse ordering. for (unsigned i = 0; i < s.m_refs.size(); ++i) { s.m_refs[i] = rules.size()-1-s.m_refs[i]; } } steps.reverse(); pp_steps(out, steps); } /** \brief extract the instantiation by searching for the first occurrence of a hyper-resolution rule that produces an instance. */ void boogie_proof::get_subst(proof* p, subst& s) { ptr_vector todo; todo.push_back(p); ast_mark visited; std::cout << "get_subst\n" << mk_pp(p, m) << "\n"; while (!todo.empty()) { proof* p = todo.back(); todo.pop_back(); if (visited.is_marked(p)) { continue; } visited.mark(p, true); proof_ref_vector premises(m); expr_ref conclusion(m); svector > positions; vector substs; if (m.is_hyper_resolve(p, premises, conclusion, positions, substs)) { expr_ref_vector const& sub = substs[0]; if (!sub.empty()) { quantifier* q = to_quantifier(m.get_fact(premises[0].get())); unsigned sz = sub.size(); SASSERT(sz == q->get_num_decls()); for (unsigned i = 0; i < sz; ++i) { s.push_back(std::make_pair(q->get_decl_name(sz-1-i), sub[i])); } return; } } unsigned sz = m.get_num_parents(p); for (unsigned i = 0; i < sz; ++i) { todo.push_back(m.get_parent(p, i)); } } } void boogie_proof::get_rule_name(proof* p, symbol& n) { } void boogie_proof::get_labels(proof* p, labels& lbls) { } void boogie_proof::pp_steps(std::ostream& out, vector& steps) { out << "(derivation\n"; for (unsigned i = 0; i < steps.size(); ++i) { pp_step(out, i, steps[i]); } out << ")\n"; } // step :: "(" "step" step-name fact rule-name subst labels premises ")" void boogie_proof::pp_step(std::ostream& out, unsigned id, step& s) { out << "(step\n"; out << " s!" << id << " "; pp_fact(out, s.m_fact); out << " " << s.m_rule_name << "\n"; pp_subst(out << " ", s.m_subst); pp_labels(out << " ", s.m_labels); pp_premises(out << " ", s.m_refs); out << ")\n"; } // fact :: "(" predicate theory-term* ")" void boogie_proof::pp_fact(std::ostream& out, expr* fact) { out << mk_pp(fact, m) << "\n"; } // subst :: "(" "subst" assignment* ")" void boogie_proof::pp_subst(std::ostream& out, subst& s) { out << "(subst"; for (unsigned i = 0; i < s.size(); ++i) { pp_assignment(out, s[i].first, s[i].second); } out << ")\n"; } // assignment :: "(" "=" variable theory-term ")" void boogie_proof::pp_assignment(std::ostream& out, symbol const& v, expr* t) { out << "\n (= " << v << " " << mk_pp(t, m) << ")"; } // labels :: "(" "labels" label* ")" void boogie_proof::pp_labels(std::ostream& out, labels& lbls) { out << "(labels"; for (unsigned i = 0; i < lbls.size(); ++i) { out << " " << lbls[i]; } out << ")\n"; } // premises "(" "ref" step-name* ")" void boogie_proof::pp_premises(std::ostream& out, refs& refs) { out << "(ref"; for (unsigned i = 0; i < refs.size(); ++i) { out << " s!" << refs[i]; } out << ")\n"; } } z3-z3-4.8.7/src/muz/base/dl_boogie_proof.h000066400000000000000000000053721356505360400202530ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ /** output :: derivation model derivation :: "(" "derivation" step* ")" step :: "(" "step" step-name fact rule-name subst labels premises ")" step-name :: identifier rule-name :: identifier fact :: "(" predicate theory-term* ")" subst :: "(" "subst" assignment* ")" assignment :: "(" "=" variable theory-term ")" labels :: "(" "labels" label* ")" premises :: "(" "ref" step-name* ")" model :: "(" "model" smtlib2-model ")" In each step the "fact" is derivable by hyper-resolution from the named premises and the named rule, under the given substitution for the universally quantified variables in the rule. The premises of each step must have occurred previously in the step sequence. The last fact is a nullary placeholder predicate representing satisfaction of the query (its name is arbitrary). The labels list consists of all the positively labeled sub-formulas whose truth is used in the proof, and all the negatively labeled formulas whose negation is used. A theory-term is a ground term using only interpreted constants of the background theories. The smtlib2-model gives an interpretation of the uninterpreted constants in the background theory under which the derivation is valid. Currently it is a quoted string in the old z3 model format, for compatibility with Boogie, however, this should be changed to the new model format (using define-fun) when Boogie supports this. */ #include "ast/ast.h" #include "model/model.h" namespace datalog { class boogie_proof { typedef vector > subst; typedef svector labels; typedef unsigned_vector refs; struct step { symbol m_rule_name; expr* m_fact; subst m_subst; labels m_labels; refs m_refs; }; ast_manager& m; proof_ref m_proof; model_ref m_model; void pp_proof(std::ostream& out); void pp_steps(std::ostream& out, vector& steps); void pp_step(std::ostream& out, unsigned i, step& s); void pp_fact(std::ostream& out, expr* fact); void pp_subst(std::ostream& out, subst& s); void pp_assignment(std::ostream& out, symbol const& v, expr* t); void pp_labels(std::ostream& out, labels& lbls); void pp_premises(std::ostream& out, refs& refs); void get_subst(proof* p, subst& sub); void get_rule_name(proof* p, symbol&); void get_labels(proof* p, labels&); public: boogie_proof(ast_manager& m): m(m), m_proof(m), m_model(nullptr) {} void set_proof(proof* p); void set_model(model* m); void pp(std::ostream& out); }; } z3-z3-4.8.7/src/muz/base/dl_context.cpp000066400000000000000000001305361356505360400176220ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_context.cpp Abstract: Author: Leonardo de Moura (leonardo) 2010-05-18. Revision History: --*/ #include #include #include "ast/arith_decl_plugin.h" #include "ast/bv_decl_plugin.h" #include "muz/base/dl_context.h" #include "ast/for_each_expr.h" #include "ast/ast_smt_pp.h" #include "ast/ast_smt2_pp.h" #include "ast/datatype_decl_plugin.h" #include "ast/scoped_proof.h" #include "muz/base/fp_params.hpp" #include "ast/ast_pp_util.h" namespace datalog { // ----------------------------------- // // context::sort_domain // // ----------------------------------- class context::sort_domain { private: sort_kind m_kind; protected: sort_ref m_sort; bool m_limited_size; uint64_t m_size; sort_domain(sort_kind k, context & ctx, sort * s) : m_kind(k), m_sort(s, ctx.get_manager()) { m_limited_size = ctx.get_decl_util().try_get_size(s, m_size); } public: virtual ~sort_domain() {} sort_kind get_kind() const { return m_kind; } virtual unsigned get_constant_count() const = 0; virtual void print_element(finite_element el_num, std::ostream & out) = 0; }; class context::symbol_sort_domain : public sort_domain { typedef map sym2num; typedef svector num2sym; sym2num m_el_numbers; num2sym m_el_names; public: symbol_sort_domain(context & ctx, sort * s) : sort_domain(SK_SYMBOL, ctx, s) {} finite_element get_number(symbol sym) { //we number symbols starting from zero, so table->size() is equal to the //index of the symbol to be added next unsigned newIdx = m_el_numbers.size(); sym2num::entry* sym_e = m_el_numbers.insert_if_not_there2(sym, newIdx); unsigned idx=sym_e->get_data().m_value; if (idx==newIdx) { m_el_names.push_back(sym); SASSERT(m_el_names.size()==m_el_numbers.size()); } if (m_limited_size && idx>=m_size) { std::stringstream sstm; sstm << "sort " << m_sort->get_name() << " contains more constants than its declared size " << m_size; throw default_exception(sstm.str()); } return idx; } unsigned get_constant_count() const override { return m_el_names.size(); } void print_element(finite_element el_num, std::ostream & out) override { if (el_num>=m_el_names.size()) { out << el_num; return; } out << m_el_names[el_num]; } }; class context::uint64_sort_domain : public sort_domain { typedef map > el2num; typedef svector num2el; el2num m_el_numbers; num2el m_el_names; public: uint64_sort_domain(context & ctx, sort * s) : sort_domain(SK_UINT64, ctx, s) {} finite_element get_number(uint64_t el) { //we number symbols starting from zero, so table->size() is equal to the //index of the symbol to be added next unsigned newIdx = m_el_numbers.size(); el2num::entry* sym_e = m_el_numbers.insert_if_not_there2(el, newIdx); unsigned idx=sym_e->get_data().m_value; if (idx==newIdx) { m_el_names.push_back(el); SASSERT(m_el_names.size()==m_el_numbers.size()); } if (m_limited_size && idx>=m_size) { std::stringstream sstm; sstm << "sort " << m_sort->get_name() << " contains more constants than its declared size " << m_size; throw default_exception(sstm.str()); } return idx; } unsigned get_constant_count() const override { return m_el_names.size(); } void print_element(finite_element el_num, std::ostream & out) override { if (el_num >= m_el_names.size()) { out << "get_name() << ":" << el_num << '>'; return; } out << m_el_names[el_num]; } }; // ----------------------------------- // // trail stack for restoring rules // // ----------------------------------- class context::restore_rules : public trail { rule_set* m_old_rules; void reset() { dealloc(m_old_rules); m_old_rules = nullptr; } public: restore_rules(rule_set& r): m_old_rules(alloc(rule_set, r)) {} ~restore_rules() override {} void undo(context& ctx) override { ctx.replace_rules(*m_old_rules); reset(); } }; template class restore_vec_size_trail : public trail { Vec& m_vector; unsigned m_old_size; public: restore_vec_size_trail(Vec& v): m_vector(v), m_old_size(v.size()) {} ~restore_vec_size_trail() override {} void undo(Ctx& ctx) override { m_vector.shrink(m_old_size); } }; void context::push() { m_trail.push_scope(); m_trail.push(restore_rules(m_rule_set)); m_trail.push(restore_vec_size_trail(m_rule_fmls)); m_trail.push(restore_vec_size_trail(m_background)); } void context::pop() { if (m_trail.get_num_scopes() == 0) { throw default_exception("there are no backtracking points to pop to"); } throw default_exception("pop operation is not supported"); m_trail.pop_scope(1); } // ----------------------------------- // // context // // ----------------------------------- context::context(ast_manager & m, register_engine_base& re, smt_params& fp, params_ref const& pa): m(m), m_register_engine(re), m_fparams(fp), m_params_ref(pa), m_params(alloc(fp_params, m_params_ref)), m_decl_util(m), m_rewriter(m), m_var_subst(m), m_rule_manager(*this), m_contains_p(*this), m_rule_properties(m, m_rule_manager, *this, m_contains_p), m_transf(*this), m_trail(*this), m_pinned(m), m_bind_variables(m), m_rule_set(*this), m_transformed_rule_set(*this), m_rule_fmls_head(0), m_rule_fmls(m), m_background(m), m_mc(nullptr), m_rel(nullptr), m_engine(nullptr), m_closed(false), m_saturation_was_run(false), m_enable_bind_variables(true), m_last_status(OK), m_last_answer(m), m_last_ground_answer(m), m_engine_type(LAST_ENGINE) { re.set_context(this); updt_params(pa); } context::~context() { reset(); dealloc(m_params); } void context::reset() { m_trail.reset(); m_rule_set.reset(); m_rule_fmls_head = 0; m_rule_fmls.reset(); m_rule_names.reset(); m_rule_bounds.reset(); m_argument_var_names.reset(); m_preds.reset(); m_preds_by_name.reset(); reset_dealloc_values(m_sorts); m_engine = nullptr; m_rel = nullptr; } bool context::is_fact(app * head) const { return m_rule_manager.is_fact(head); } bool context::has_sort_domain(relation_sort s) const { return m_sorts.contains(s); } context::sort_domain & context::get_sort_domain(relation_sort s) { return *m_sorts.find(s); } const context::sort_domain & context::get_sort_domain(relation_sort s) const { return *m_sorts.find(s); } bool context::generate_proof_trace() const { return m_generate_proof_trace; } bool context::output_profile() const { return m_params->datalog_output_profile(); } bool context::output_tuples() const { return m_params->datalog_print_tuples(); } bool context::use_map_names() const { return m_params->datalog_use_map_names(); } bool context::fix_unbound_vars() const { return m_params->xform_fix_unbound_vars(); } symbol context::default_table() const { return m_params->datalog_default_table(); } symbol context::default_relation() const { return m_default_relation; } void context::set_default_relation(symbol const& s) { m_default_relation = s; } symbol context::print_aig() const { return m_params->print_aig(); } symbol context::check_relation() const { return m_params->datalog_check_relation(); } symbol context::default_table_checker() const { return m_params->datalog_default_table_checker(); } bool context::default_table_checked() const { return m_params->datalog_default_table_checked(); } bool context::dbg_fpr_nonempty_relation_signature() const { return m_params->datalog_dbg_fpr_nonempty_relation_signature(); } unsigned context::dl_profile_milliseconds_threshold() const { return m_params->datalog_profile_timeout_milliseconds(); } bool context::all_or_nothing_deltas() const { return m_params->datalog_all_or_nothing_deltas(); } bool context::compile_with_widening() const { return m_params->datalog_compile_with_widening(); } bool context::unbound_compressor() const { return m_unbound_compressor; } void context::set_unbound_compressor(bool f) { m_unbound_compressor = f; } unsigned context::soft_timeout() const { return m_params->datalog_timeout(); } bool context::similarity_compressor() const { return m_params->datalog_similarity_compressor(); } unsigned context::similarity_compressor_threshold() const { return m_params->datalog_similarity_compressor_threshold(); } unsigned context::initial_restart_timeout() const { return m_params->datalog_initial_restart_timeout(); } bool context::generate_explanations() const { return m_params->datalog_generate_explanations(); } bool context::explanations_on_relation_level() const { return m_params->datalog_explanations_on_relation_level(); } bool context::magic_sets_for_queries() const { return m_params->datalog_magic_sets_for_queries(); } symbol context::tab_selection() const { return m_params->tab_selection(); } bool context::xform_coi() const { return m_params->xform_coi(); } bool context::xform_slice() const { return m_params->xform_slice(); } bool context::xform_bit_blast() const { return m_params->xform_bit_blast(); } bool context::karr() const { return m_params->xform_karr(); } bool context::scale() const { return m_params->xform_scale(); } bool context::magic() const { return m_params->xform_magic(); } bool context::compress_unbound() const { return m_params->xform_compress_unbound(); } bool context::quantify_arrays() const { return m_params->xform_quantify_arrays(); } bool context::instantiate_quantifiers() const { return m_params->xform_instantiate_quantifiers(); } bool context::array_blast() const { return m_params->xform_array_blast(); } bool context::array_blast_full() const { return m_params->xform_array_blast_full(); } bool context::elim_term_ite() const {return m_params->xform_elim_term_ite();} unsigned context::blast_term_ite_inflation() const { return m_params->xform_elim_term_ite_inflation(); } void context::register_finite_sort(sort * s, sort_kind k) { m_pinned.push_back(s); SASSERT(!m_sorts.contains(s)); sort_domain * dom; switch (k) { case SK_SYMBOL: dom = alloc(symbol_sort_domain, *this, s); break; case SK_UINT64: dom = alloc(uint64_sort_domain, *this, s); break; default: UNREACHABLE(); } m_sorts.insert(s, dom); } void context::register_variable(func_decl* var) { m_bind_variables.add_var(m.mk_const(var)); } expr_ref context::bind_vars(expr* fml, bool is_forall) { if (m_enable_bind_variables) { return m_bind_variables(fml, is_forall); } else { return expr_ref(fml, m); } } void context::register_predicate(func_decl * decl, bool named) { if (!is_predicate(decl)) { m_pinned.push_back(decl); m_preds.insert(decl); TRACE("dl", tout << mk_pp(decl, m) << "\n";); if (named) { m_preds_by_name.insert(decl->get_name(), decl); } } } void context::restrict_predicates(func_decl_set const& preds) { m_preds.reset(); func_decl_set::iterator it = preds.begin(), end = preds.end(); for (; it != end; ++it) { TRACE("dl", tout << mk_pp(*it, m) << "\n";); m_preds.insert(*it); } } context::finite_element context::get_constant_number(relation_sort srt, symbol sym) { sort_domain & dom0 = get_sort_domain(srt); SASSERT(dom0.get_kind() == SK_SYMBOL); symbol_sort_domain & dom = static_cast(dom0); return dom.get_number(sym); } context::finite_element context::get_constant_number(relation_sort srt, uint64_t el) { sort_domain & dom0 = get_sort_domain(srt); SASSERT(dom0.get_kind()==SK_UINT64); uint64_sort_domain & dom = static_cast(dom0); return dom.get_number(el); } void context::print_constant_name(relation_sort srt, uint64_t num, std::ostream & out) { if (has_sort_domain(srt)) { SASSERT(num<=UINT_MAX); get_sort_domain(srt).print_element(static_cast(num), out); } else { out << num; } } bool context::try_get_sort_constant_count(relation_sort srt, uint64_t & constant_count) { if (!has_sort_domain(srt)) { return false; } constant_count = get_sort_domain(srt).get_constant_count(); return true; } uint64_t context::get_sort_size_estimate(relation_sort srt) { if (get_decl_util().is_rule_sort(srt)) { return 1; } uint64_t res; if (!try_get_sort_constant_count(srt, res)) { const sort_size & sz = srt->get_num_elements(); if (sz.is_finite()) { res = sz.size(); } else { res = std::numeric_limits::max(); } } return res; } void context::set_argument_names(const func_decl * pred, const svector & var_names) { SASSERT(!m_argument_var_names.contains(pred)); m_argument_var_names.insert(pred, var_names); } symbol context::get_argument_name(const func_decl * pred, unsigned arg_index) { pred2syms::obj_map_entry * e = m_argument_var_names.find_core(pred); if (!e) { std::stringstream name_stm; name_stm << '#' << arg_index; return symbol(name_stm.str().c_str()); } SASSERT(arg_index < e->get_data().m_value.size()); return e->get_data().m_value[arg_index]; } void context::set_predicate_representation(func_decl * pred, unsigned relation_name_cnt, symbol const * relation_names) { if (relation_name_cnt > 0) { ensure_engine(); } if (relation_name_cnt > 0 && m_rel) { m_rel->set_predicate_representation(pred, relation_name_cnt, relation_names); } } func_decl * context::mk_fresh_head_predicate(symbol const & prefix, symbol const & suffix, unsigned arity, sort * const * domain, func_decl* orig_pred) { func_decl* new_pred = m.mk_fresh_func_decl(prefix, suffix, arity, domain, m.mk_bool_sort()); register_predicate(new_pred, true); if (m_rel) { m_rel->inherit_predicate_kind(new_pred, orig_pred); } return new_pred; } void context::add_rule(expr* rl, symbol const& name, unsigned bound) { SASSERT(rl); m_rule_fmls.push_back(rl); m_rule_names.push_back(name); m_rule_bounds.push_back(bound); } void context::flush_add_rules() { datalog::rule_manager& rm = get_rule_manager(); scoped_proof_mode _scp(m, generate_proof_trace()?PGM_ENABLED:PGM_DISABLED); while (m_rule_fmls_head < m_rule_fmls.size()) { expr* fml = m_rule_fmls[m_rule_fmls_head].get(); proof* p = generate_proof_trace()?m.mk_asserted(fml):nullptr; rm.mk_rule(fml, p, m_rule_set, m_rule_names[m_rule_fmls_head]); ++m_rule_fmls_head; } check_rules(m_rule_set); } // // Update a rule with a new. // It requires basic subsumption. // void context::update_rule(expr* rl, symbol const& name) { datalog::rule_manager& rm = get_rule_manager(); proof* p = nullptr; if (generate_proof_trace()) { p = m.mk_asserted(rl); } unsigned size_before = m_rule_set.get_num_rules(); rm.mk_rule(rl, p, m_rule_set, name); unsigned size_after = m_rule_set.get_num_rules(); if (size_before + 1 != size_after) { std::stringstream strm; strm << "Rule " << name << " has a non-trivial body. It cannot be modified"; throw default_exception(strm.str()); } // The new rule is inserted last: rule_ref r(m_rule_set.get_rule(size_before), rm); rule_ref_vector const& rls = m_rule_set.get_rules(); rule* old_rule = nullptr; for (unsigned i = 0; i < size_before; ++i) { if (rls[i]->name() == name) { if (old_rule) { std::stringstream strm; strm << "Rule " << name << " occurs twice. It cannot be modified"; m_rule_set.del_rule(r); throw default_exception(strm.str()); } old_rule = rls[i]; } } if (old_rule) { if (!check_subsumes(*old_rule, *r)) { std::stringstream strm; strm << "Old rule "; old_rule->display(*this, strm); strm << "does not subsume new rule "; r->display(*this, strm); m_rule_set.del_rule(r); throw default_exception(strm.str()); } m_rule_set.del_rule(old_rule); } } bool context::check_subsumes(rule const& stronger_rule, rule const& weaker_rule) { if (stronger_rule.get_head() != weaker_rule.get_head()) { return false; } for (unsigned i = 0; i < stronger_rule.get_tail_size(); ++i) { app* t = stronger_rule.get_tail(i); bool found = false; for (unsigned j = 0; j < weaker_rule.get_tail_size(); ++j) { app* s = weaker_rule.get_tail(j); if (s == t) { found = true; break; } } if (!found) { return false; } } return true; } unsigned context::get_num_levels(func_decl* pred) { ensure_engine(); return m_engine->get_num_levels(pred); } expr_ref context::get_cover_delta(int level, func_decl* pred) { ensure_engine(); return m_engine->get_cover_delta(level, pred); } expr_ref context::get_reachable(func_decl *pred) { ensure_engine(); return m_engine->get_reachable(pred); } void context::add_cover(int level, func_decl* pred, expr* property) { ensure_engine(); m_engine->add_cover(level, pred, property); } void context::add_invariant(func_decl* pred, expr *property) { ensure_engine(); m_engine->add_invariant(pred, property); } void context::check_rules(rule_set& r) { m_rule_properties.set_generate_proof(generate_proof_trace()); switch(get_engine()) { case DATALOG_ENGINE: m_rule_properties.collect(r); m_rule_properties.check_quantifier_free(); m_rule_properties.check_uninterpreted_free(); m_rule_properties.check_nested_free(); m_rule_properties.check_infinite_sorts(); break; case SPACER_ENGINE: m_rule_properties.collect(r); m_rule_properties.check_existential_tail(); m_rule_properties.check_for_negated_predicates(); m_rule_properties.check_uninterpreted_free(); break; case BMC_ENGINE: m_rule_properties.collect(r); m_rule_properties.check_for_negated_predicates(); break; case QBMC_ENGINE: m_rule_properties.collect(r); m_rule_properties.check_existential_tail(); m_rule_properties.check_for_negated_predicates(); break; case TAB_ENGINE: m_rule_properties.collect(r); m_rule_properties.check_existential_tail(); m_rule_properties.check_for_negated_predicates(); break; case CLP_ENGINE: m_rule_properties.collect(r); m_rule_properties.check_existential_tail(); m_rule_properties.check_for_negated_predicates(); break; case DDNF_ENGINE: break; case LAST_ENGINE: default: UNREACHABLE(); break; } } void context::add_rule(rule_ref& r) { m_rule_set.add_rule(r); } void context::add_fact(func_decl * pred, const relation_fact & fact) { if (get_engine() == DATALOG_ENGINE) { ensure_engine(); m_rel->add_fact(pred, fact); } else { expr_ref rule(m.mk_app(pred, fact.size(), (expr*const*)fact.c_ptr()), m); add_rule(rule, symbol::null); } } void context::add_fact(app * head) { SASSERT(is_fact(head)); relation_fact fact(get_manager()); unsigned n = head->get_num_args(); for (unsigned i = 0; i < n; i++) { fact.push_back(to_app(head->get_arg(i))); } add_fact(head->get_decl(), fact); } bool context::has_facts(func_decl * pred) const { return m_rel && m_rel->has_facts(pred); } void context::add_table_fact(func_decl * pred, const table_fact & fact) { if (get_engine() == DATALOG_ENGINE) { ensure_engine(); m_rel->add_fact(pred, fact); } else { relation_fact rfact(m); for (unsigned i = 0; i < fact.size(); ++i) { rfact.push_back(m_decl_util.mk_numeral(fact[i], pred->get_domain()[i])); } add_fact(pred, rfact); } } void context::add_table_fact(func_decl * pred, unsigned num_args, unsigned args[]) { if (pred->get_arity() != num_args) { std::ostringstream out; out << "mismatched number of arguments passed to " << mk_ismt2_pp(pred, m) << " " << num_args << " passed"; throw default_exception(out.str()); } table_fact fact; for (unsigned i = 0; i < num_args; ++i) { fact.push_back(args[i]); } add_table_fact(pred, fact); } void context::close() { SASSERT(!m_closed); if (!m_rule_set.close()) { throw default_exception("Negation is not stratified!"); } m_closed = true; } void context::ensure_closed() { if (!m_closed) { close(); } } void context::ensure_opened() { if (m_closed) { reopen(); } } void context::reopen() { SASSERT(m_closed); m_rule_set.reopen(); m_closed = false; } void context::transform_rules(rule_transformer::plugin* plugin) { flet _enable_bv(m_enable_bind_variables, false); rule_transformer transformer(*this); transformer.register_plugin(plugin); transform_rules(transformer); } void context::transform_rules(rule_transformer& transf) { SASSERT(m_closed); //we must finish adding rules before we start transforming them TRACE("dl", display_rules(tout);); if (transf(m_rule_set)) { //we have already ensured the negation is stratified and transformations //should not break the stratification m_rule_set.ensure_closed(); TRACE("dl", display_rules(tout);); TRACE("dl_verbose", display(tout);); } } void context::replace_rules(rule_set const & rs) { SASSERT(!m_closed); m_rule_set.replace_rules(rs); if (m_rel) { m_rel->restrict_predicates(get_predicates()); } } void context::record_transformed_rules() { m_transformed_rule_set.replace_rules(m_rule_set); } void context::apply_default_transformation() { } void context::collect_params(param_descrs& p) { fp_params::collect_param_descrs(p); insert_timeout(p); insert_ctrl_c(p); } void context::updt_params(params_ref const& p) { m_params_ref.copy(p); if (m_engine.get()) m_engine->updt_params(); m_generate_proof_trace = m_params->generate_proof_trace(); m_unbound_compressor = m_params->datalog_unbound_compressor(); m_default_relation = m_params->datalog_default_relation(); } expr_ref context::get_background_assertion() { expr_ref result(m); switch (m_background.size()) { case 0: result = m.mk_true(); break; case 1: result = m_background[0].get(); break; default: result = m.mk_and(m_background.size(), m_background.c_ptr()); break; } return result; } void context::assert_expr(expr* e) { TRACE("dl", tout << mk_ismt2_pp(e, m) << "\n";); m_background.push_back(e); } void context::cleanup() { m_last_status = OK; if (m_engine) m_engine->cleanup(); } class context::engine_type_proc { ast_manager& m; arith_util a; datatype_util dt; bv_util bv; DL_ENGINE m_engine_type; bool is_large_bv(sort* s) { return false; } public: engine_type_proc(ast_manager& m): m(m), a(m), dt(m), bv(m), m_engine_type(DATALOG_ENGINE) {} DL_ENGINE get_engine() const { return m_engine_type; } void operator()(expr* e) { if (a.is_int_real(e)) { m_engine_type = SPACER_ENGINE; } else if (is_var(e) && m.is_bool(e)) { m_engine_type = SPACER_ENGINE; } else if (dt.is_datatype(m.get_sort(e))) { m_engine_type = SPACER_ENGINE; } else if (is_large_bv(m.get_sort(e))) { m_engine_type = SPACER_ENGINE; } } }; void context::configure_engine(expr* q) { if (m_engine_type != LAST_ENGINE) { return; } symbol e = m_params->engine(); if (e == symbol("datalog")) { m_engine_type = DATALOG_ENGINE; } else if (e == symbol("spacer")) { m_engine_type = SPACER_ENGINE; } else if (e == symbol("bmc")) { m_engine_type = BMC_ENGINE; } else if (e == symbol("qbmc")) { m_engine_type = QBMC_ENGINE; } else if (e == symbol("tab")) { m_engine_type = TAB_ENGINE; } else if (e == symbol("clp")) { m_engine_type = CLP_ENGINE; } else if (e == symbol("ddnf")) { m_engine_type = DDNF_ENGINE; } if (m_engine_type == LAST_ENGINE) { expr_fast_mark1 mark; engine_type_proc proc(m); m_engine_type = DATALOG_ENGINE; if (q) { quick_for_each_expr(proc, mark, q); m_engine_type = proc.get_engine(); } for (unsigned i = 0; m_engine_type == DATALOG_ENGINE && i < m_rule_set.get_num_rules(); ++i) { rule * r = m_rule_set.get_rule(i); quick_for_each_expr(proc, mark, r->get_head()); for (unsigned j = 0; j < r->get_tail_size(); ++j) { quick_for_each_expr(proc, mark, r->get_tail(j)); } m_engine_type = proc.get_engine(); } for (unsigned i = m_rule_fmls_head; m_engine_type == DATALOG_ENGINE && i < m_rule_fmls.size(); ++i) { expr* fml = m_rule_fmls[i].get(); while (is_quantifier(fml)) { fml = to_quantifier(fml)->get_expr(); } quick_for_each_expr(proc, mark, fml); m_engine_type = proc.get_engine(); } } } lbool context::query(expr* query) { expr_ref _query(query, m); m_mc = mk_skip_model_converter(); m_last_status = OK; m_last_answer = nullptr; m_last_ground_answer = nullptr; switch (get_engine(query)) { case DATALOG_ENGINE: case SPACER_ENGINE: case BMC_ENGINE: case QBMC_ENGINE: case TAB_ENGINE: case CLP_ENGINE: case DDNF_ENGINE: flush_add_rules(); break; default: UNREACHABLE(); } ensure_engine(query); lbool r = m_engine->query(query); if (r != l_undef && get_params().print_certificate()) { display_certificate(std::cout) << "\n"; } return r; } lbool context::query_from_lvl (expr* query, unsigned lvl) { m_mc = mk_skip_model_converter(); m_last_status = OK; m_last_answer = nullptr; m_last_ground_answer = nullptr; switch (get_engine()) { case DATALOG_ENGINE: case SPACER_ENGINE: case BMC_ENGINE: case QBMC_ENGINE: case TAB_ENGINE: case CLP_ENGINE: flush_add_rules(); break; default: UNREACHABLE(); } ensure_engine(); return m_engine->query_from_lvl (query, lvl); } model_ref context::get_model() { ensure_engine(); return m_engine->get_model(); } proof_ref context::get_proof() { ensure_engine(); return m_engine->get_proof(); } void context::ensure_engine(expr* e) { if (!m_engine.get()) { m_engine = m_register_engine.mk_engine(get_engine(e)); m_engine->updt_params(); // break abstraction. if (get_engine() == DATALOG_ENGINE) { m_rel = dynamic_cast(m_engine.get()); } } } lbool context::rel_query(unsigned num_rels, func_decl * const* rels) { m_last_answer = nullptr; ensure_engine(); return m_engine->query(num_rels, rels); } expr* context::get_answer_as_formula() { if (m_last_answer) { return m_last_answer.get(); } ensure_engine(); m_last_answer = m_engine->get_answer(); return m_last_answer.get(); } expr* context::get_ground_sat_answer () { if (m_last_ground_answer) { return m_last_ground_answer; } ensure_engine (); m_last_ground_answer = m_engine->get_ground_sat_answer (); return m_last_ground_answer; } void context::get_rules_along_trace (rule_ref_vector& rules) { ensure_engine (); m_engine->get_rules_along_trace (rules); } void context::get_rules_along_trace_as_formulas (expr_ref_vector& rules, svector& names) { rule_manager& rm = get_rule_manager (); rule_ref_vector rv (rm); get_rules_along_trace (rv); expr_ref fml (m); rule_ref_vector::iterator it = rv.begin (), end = rv.end (); for (; it != end; it++) { m_rule_manager.to_formula (**it, fml); rules.push_back (fml); // The concatenated names are already stored last-first, so do not need to be reversed here const symbol& rule_name = (*it)->name(); names.push_back (rule_name); TRACE ("dl", if (rule_name == symbol::null) { tout << "Encountered unnamed rule: "; (*it)->display(*this, tout); tout << "\n"; }); } } std::ostream& context::display_certificate(std::ostream& out) { ensure_engine(); m_engine->display_certificate(out); return out; } void context::display(std::ostream & out) const { display_rules(out); if (m_rel) m_rel->display_facts(out); } void context::display_profile(std::ostream& out) const { out << "\n---------------\n"; out << "Original rules\n"; display_rules(out); out << "\n---------------\n"; out << "Transformed rules\n"; m_transformed_rule_set.display(out); if (m_rel) { m_rel->display_profile(out); } } void context::reset_statistics() { if (m_engine) { m_engine->reset_statistics(); } } void context::collect_statistics(statistics& st) const { if (m_engine) { m_engine->collect_statistics(st); } get_memory_statistics(st); get_rlimit_statistics(m.limit(), st); } execution_result context::get_status() { return m_last_status; } bool context::result_contains_fact(relation_fact const& f) { return m_rel && m_rel->result_contains_fact(f); } // NB: algebraic data-types declarations will not be printed. static void collect_free_funcs(unsigned sz, expr* const* exprs, ast_pp_util& v, mk_fresh_name& fresh_names) { v.collect(sz, exprs); for (unsigned i = 0; i < sz; ++i) { expr* e = exprs[i]; while (is_quantifier(e)) { e = to_quantifier(e)->get_expr(); } fresh_names.add(e); } } void context::get_raw_rule_formulas(expr_ref_vector& rules, svector& names, unsigned_vector &bounds) { for (unsigned i = 0; i < m_rule_fmls.size(); ++i) { expr_ref r = bind_vars(m_rule_fmls[i].get(), true); rules.push_back(r.get()); names.push_back(m_rule_names[i]); bounds.push_back(m_rule_bounds[i]); } } void context::get_rules_as_formulas(expr_ref_vector& rules, expr_ref_vector& queries, svector& names) { expr_ref fml(m); rule_manager& rm = get_rule_manager(); // ensure that rules are all using bound variables. for (unsigned i = m_rule_fmls_head; i < m_rule_fmls.size(); ++i) { m_free_vars(m_rule_fmls[i].get()); if (!m_free_vars.empty()) { rm.mk_rule(m_rule_fmls[i].get(), nullptr, m_rule_set, m_rule_names[i]); m_rule_fmls[i] = m_rule_fmls.back(); m_rule_names[i] = m_rule_names.back(); m_rule_fmls.pop_back(); m_rule_names.pop_back(); m_rule_bounds.pop_back(); --i; } } rule_set::iterator it = m_rule_set.begin(), end = m_rule_set.end(); for (; it != end; ++it) { rule* r = *it; rm.to_formula(*r, fml); func_decl* h = r->get_decl(); if (m_rule_set.is_output_predicate(h)) { expr* body = fml; expr* e2; if (is_quantifier(body)) { quantifier* q = to_quantifier(body); expr* e = q->get_expr(); if (m.is_implies(e, body, e2)) { fml = m.mk_quantifier(exists_k, q->get_num_decls(), q->get_decl_sorts(), q->get_decl_names(), body); } else { fml = body; } } else { fml = body; if (m.is_implies(body, body, e2)) { fml = body; } } queries.push_back(fml); } else { rules.push_back(fml); names.push_back(r->name()); } } for (unsigned i = m_rule_fmls_head; i < m_rule_fmls.size(); ++i) { rules.push_back(m_rule_fmls[i].get()); names.push_back(m_rule_names[i]); } } static std::ostream& display_symbol(std::ostream& out, symbol const& nm) { if (is_smt2_quoted_symbol(nm)) { out << mk_smt2_quoted_symbol(nm); } else { out << nm; } return out; } void context::display_smt2(unsigned num_queries, expr* const* qs, std::ostream& out) { ast_manager& m = get_manager(); ast_pp_util visitor(m); func_decl_set rels; unsigned num_axioms = m_background.size(); expr* const* axioms = m_background.c_ptr(); expr_ref fml(m); expr_ref_vector rules(m), queries(m); svector names; bool use_fixedpoint_extensions = m_params->print_fixedpoint_extensions(); bool print_low_level = m_params->print_low_level_smt2(); bool do_declare_vars = m_params->print_with_variable_declarations(); #define PP(_e_) if (print_low_level) out << mk_smt_pp(_e_, m); else ast_smt2_pp(out, _e_, env); get_rules_as_formulas(rules, queries, names); queries.append(num_queries, qs); smt2_pp_environment_dbg env(m); mk_fresh_name fresh_names; collect_free_funcs(num_axioms, axioms, visitor, fresh_names); collect_free_funcs(rules.size(), rules.c_ptr(), visitor, fresh_names); collect_free_funcs(queries.size(), queries.c_ptr(), visitor, fresh_names); func_decl_set funcs; unsigned sz = visitor.coll.get_num_decls(); for (unsigned i = 0; i < sz; ++i) { func_decl* f = visitor.coll.get_func_decls()[i]; if (f->get_family_id() != null_family_id) { // } else if (is_predicate(f) && use_fixedpoint_extensions) { rels.insert(f); } else { funcs.insert(f); } } if (!use_fixedpoint_extensions) { out << "(set-logic HORN)\n"; } for (func_decl * f : rels) visitor.remove_decl(f); visitor.display_decls(out); for (func_decl * f : rels) display_rel_decl(out, f); if (use_fixedpoint_extensions && do_declare_vars) { declare_vars(rules, fresh_names, out); } if (num_axioms > 0 && !use_fixedpoint_extensions) { throw default_exception("Background axioms cannot be used with SMT-LIB2 HORN format"); } for (unsigned i = 0; i < num_axioms; ++i) { out << "(assert "; PP(axioms[i]); out << ")\n"; } for (unsigned i = 0; i < rules.size(); ++i) { out << (use_fixedpoint_extensions?"(rule ":"(assert "); expr* r = rules[i].get(); symbol nm = names[i]; if (symbol::null != nm) { out << "(! "; } PP(r); if (symbol::null != nm) { out << " :named "; while (fresh_names.contains(nm)) { std::ostringstream s; s << nm << "!"; nm = symbol(s.str().c_str()); } fresh_names.add(nm); display_symbol(out, nm) << ")"; } out << ")\n"; } if (use_fixedpoint_extensions) { for (unsigned i = 0; i < queries.size(); ++i) { expr* q = queries[i].get(); func_decl_ref fn(m); if (is_query(q)) { fn = to_app(q)->get_decl(); } else { m_free_vars(q); m_free_vars.set_default_sort(m.mk_bool_sort()); sort* const* domain = m_free_vars.c_ptr(); expr_ref qfn(m); expr_ref_vector args(m); fn = m.mk_fresh_func_decl(symbol("q"), symbol(""), m_free_vars.size(), domain, m.mk_bool_sort()); display_rel_decl(out, fn); for (unsigned j = 0; j < m_free_vars.size(); ++j) { args.push_back(m.mk_var(j, m_free_vars[j])); } qfn = m.mk_implies(q, m.mk_app(fn, args.size(), args.c_ptr())); out << "(assert "; PP(qfn); out << ")\n"; } out << "(query "; display_symbol(out, fn->get_name()) << ")\n"; } } else { for (unsigned i = 0; i < queries.size(); ++i) { if (queries.size() > 1) out << "(push)\n"; out << "(assert "; expr_ref q(m); q = m.mk_not(queries[i].get()); PP(q); out << ")\n"; out << "(check-sat)\n"; if (queries.size() > 1) out << "(pop)\n"; } } } void context::display_rel_decl(std::ostream& out, func_decl* f) { smt2_pp_environment_dbg env(m); out << "(declare-rel "; display_symbol(out, f->get_name()) << " ("; for (unsigned i = 0; i < f->get_arity(); ++i) { ast_smt2_pp(out, f->get_domain(i), env); if (i + 1 < f->get_arity()) { out << " "; } } out << "))\n"; } bool context::is_query(expr* q) { if (!is_app(q) || !is_predicate(to_app(q)->get_decl())) { return false; } app* a = to_app(q); for (unsigned i = 0; i < a->get_num_args(); ++i) { if (!is_var(a->get_arg(i))) { return false; } var* v = to_var(a->get_arg(i)); if (v->get_idx() != i) { return false; } } return true; } 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 quantifiers, then replace bound variables // by fresh constants. // smt2_pp_environment_dbg env(m); var_subst vsubst(m, false); expr_ref_vector fresh_vars(m), subst(m); expr_ref res(m); obj_map var_idxs; obj_map max_vars; for (unsigned i = 0; i < rules.size(); ++i) { expr* r = rules[i].get(); if (!is_forall(r)) { continue; } quantifier* q = to_quantifier(r); if (has_quantifiers(q->get_expr())) { continue; } max_vars.reset(); subst.reset(); unsigned max_var = 0; unsigned num_vars = q->get_num_decls(); for (unsigned j = 0; j < num_vars; ++j) { sort* s = q->get_decl_sort(num_vars-1-j); // maximal var for the given sort. if (!max_vars.find(s, max_var)) { max_var = 0; } else { ++max_var; } max_vars.insert(s, max_var); // index into fresh variable array. // unsigned fresh_var_idx = 0; obj_map::obj_map_entry* e = var_idxs.insert_if_not_there2(s, unsigned_vector()); unsigned_vector& vars = e->get_data().m_value; if (max_var >= vars.size()) { SASSERT(vars.size() == max_var); vars.push_back(fresh_vars.size()); symbol name = fresh_names.next(); fresh_vars.push_back(m.mk_const(name, s)); out << "(declare-var " << name << " "; ast_smt2_pp(out, s, env); out << ")\n"; } subst.push_back(fresh_vars[vars[max_var]].get()); } res = vsubst(q->get_expr(), subst.size(), subst.c_ptr()); rules[i] = res.get(); } } }; z3-z3-4.8.7/src/muz/base/dl_context.h000066400000000000000000000530751356505360400172710ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_context.h Abstract: Author: Leonardo de Moura (leonardo) 2010-05-18. Revision History: --*/ #ifndef DL_CONTEXT_H_ #define DL_CONTEXT_H_ #include "ast/arith_decl_plugin.h" #include "util/map.h" #include "ast/rewriter/th_rewriter.h" #include "util/str_hashtable.h" #include "ast/rewriter/var_subst.h" #include "muz/base/dl_costs.h" #include "ast/dl_decl_plugin.h" #include "muz/base/dl_rule_set.h" #include "util/lbool.h" #include "util/statistics.h" #include "util/params.h" #include "util/trail.h" #include "tactic/model_converter.h" #include "model/model2expr.h" #include "smt/params/smt_params.h" #include "muz/base/dl_rule_transformer.h" #include "ast/expr_functors.h" #include "muz/base/dl_engine_base.h" #include "muz/base/bind_variables.h" #include "muz/base/rule_properties.h" struct fp_params; namespace datalog { enum execution_result { OK, TIMEOUT, MEMOUT, INPUT_ERROR, APPROX, BOUNDED, CANCELED }; class relation_manager; typedef sort * relation_sort; typedef uint64_t table_element; typedef svector table_fact; typedef app * relation_element; typedef app_ref relation_element_ref; class relation_fact : public app_ref_vector { public: class el_proxy { friend class relation_fact; relation_fact & m_parent; unsigned m_idx; el_proxy(relation_fact & parent, unsigned idx) : m_parent(parent), m_idx(idx) {} public: operator relation_element() const { return m_parent.get(m_idx); } relation_element operator->() const { return m_parent.get(m_idx); } relation_element operator=(const relation_element & val) const { m_parent.set(m_idx, val); return m_parent.get(m_idx); } relation_element operator=(const el_proxy & val) { m_parent.set(m_idx, val); return m_parent.get(m_idx); } }; typedef const relation_element * iterator; relation_fact(ast_manager & m) : app_ref_vector(m) {} relation_fact(ast_manager & m, unsigned sz) : app_ref_vector(m) { resize(sz); } relation_fact(context & ctx); iterator begin() const { return c_ptr(); } iterator end() const { return c_ptr()+size(); } relation_element operator[](unsigned i) const { return get(i); } el_proxy operator[](unsigned i) { return el_proxy(*this, i); } }; // attempt to modularize context code. class rel_context_base : public engine_base { public: rel_context_base(ast_manager& m, char const* name): engine_base(m, name) {} ~rel_context_base() override {} virtual relation_manager & get_rmanager() = 0; virtual const relation_manager & get_rmanager() const = 0; virtual relation_base & get_relation(func_decl * pred) = 0; virtual relation_base * try_get_relation(func_decl * pred) const = 0; virtual bool is_empty_relation(func_decl* pred) const = 0; virtual expr_ref try_get_formula(func_decl * pred) const = 0; virtual void display_output_facts(rule_set const& rules, std::ostream & out) const = 0; virtual void display_facts(std::ostream & out) const = 0; virtual void restrict_predicates(func_decl_set const& predicates) = 0; virtual bool result_contains_fact(relation_fact const& f) = 0; virtual void add_fact(func_decl* pred, relation_fact const& fact) = 0; virtual void add_fact(func_decl* pred, table_fact const& fact) = 0; virtual bool has_facts(func_decl * pred) const = 0; virtual void store_relation(func_decl * pred, relation_base * rel) = 0; virtual void inherit_predicate_kind(func_decl* new_pred, func_decl* orig_pred) = 0; virtual void set_predicate_representation(func_decl * pred, unsigned relation_name_cnt, symbol const * relation_names) = 0; virtual bool output_profile() const = 0; virtual void collect_non_empty_predicates(func_decl_set& preds) = 0; virtual void transform_rules() = 0; virtual bool try_get_size(func_decl* pred, unsigned& rel_sz) const = 0; virtual lbool saturate() = 0; }; class context { public: typedef unsigned finite_element; enum sort_kind { SK_UINT64, SK_SYMBOL }; class contains_pred : public i_expr_pred { context const& ctx; public: contains_pred(context& ctx): ctx(ctx) {} ~contains_pred() override {} bool operator()(expr* e) override { return ctx.is_predicate(e); } }; private: class sort_domain; class symbol_sort_domain; class uint64_sort_domain; class restore_rules; typedef hashtable symbol_set; typedef map sym2decl; typedef obj_map > pred2syms; typedef obj_map sort_domain_map; ast_manager & m; register_engine_base& m_register_engine; smt_params & m_fparams; params_ref m_params_ref; fp_params* m_params; bool m_generate_proof_trace; // cached configuration parameter bool m_unbound_compressor; // cached configuration parameter symbol m_default_relation; // cached configuration parameter dl_decl_util m_decl_util; th_rewriter m_rewriter; var_subst m_var_subst; rule_manager m_rule_manager; contains_pred m_contains_p; rule_properties m_rule_properties; rule_transformer m_transf; trail_stack m_trail; ast_ref_vector m_pinned; bind_variables m_bind_variables; sort_domain_map m_sorts; func_decl_set m_preds; sym2decl m_preds_by_name; pred2syms m_argument_var_names; rule_set m_rule_set; rule_set m_transformed_rule_set; expr_free_vars m_free_vars; unsigned m_rule_fmls_head; expr_ref_vector m_rule_fmls; svector m_rule_names; vector m_rule_bounds; expr_ref_vector m_background; model_converter_ref m_mc; proof_converter_ref m_pc; rel_context_base* m_rel; scoped_ptr m_engine; bool m_closed; bool m_saturation_was_run; bool m_enable_bind_variables; execution_result m_last_status; expr_ref m_last_answer; expr_ref m_last_ground_answer; DL_ENGINE m_engine_type; bool is_fact(app * head) const; bool has_sort_domain(relation_sort s) const; sort_domain & get_sort_domain(relation_sort s); const sort_domain & get_sort_domain(relation_sort s) const; class engine_type_proc; public: context(ast_manager & m, register_engine_base& re, smt_params& fp, params_ref const& p = params_ref()); ~context(); void reset(); void push(); void pop(); bool saturation_was_run() const { return m_saturation_was_run; } void notify_saturation_was_run() { m_saturation_was_run = true; } void configure_engine(expr* e); ast_manager & get_manager() const { return m; } rule_manager & get_rule_manager() { return m_rule_manager; } smt_params & get_fparams() const { return m_fparams; } fp_params const& get_params() const { return *m_params; } DL_ENGINE get_engine(expr* e = nullptr) { configure_engine(e); return m_engine_type; } register_engine_base& get_register_engine() { return m_register_engine; } th_rewriter& get_rewriter() { return m_rewriter; } var_subst & get_var_subst() { return m_var_subst; } dl_decl_util & get_decl_util() { return m_decl_util; } bool generate_proof_trace() const; bool output_profile() const; bool output_tuples() const; bool use_map_names() const; bool fix_unbound_vars() const; symbol default_table() const; symbol default_relation() const; void set_default_relation(symbol const& s); symbol default_table_checker() const; symbol check_relation() const; bool default_table_checked() const; bool dbg_fpr_nonempty_relation_signature() const; unsigned dl_profile_milliseconds_threshold() const; bool all_or_nothing_deltas() const; bool compile_with_widening() const; bool unbound_compressor() const; void set_unbound_compressor(bool f); bool similarity_compressor() const; symbol print_aig() const; symbol tab_selection() const; unsigned similarity_compressor_threshold() const; unsigned soft_timeout() const; unsigned initial_restart_timeout() const; bool generate_explanations() const; bool explanations_on_relation_level() const; bool magic_sets_for_queries() const; bool karr() const; bool scale() const; bool magic() const; bool compress_unbound() const; bool quantify_arrays() const; bool instantiate_quantifiers() const; bool xform_bit_blast() const; bool xform_slice() const; bool xform_coi() const; bool array_blast() const; bool array_blast_full() const; bool elim_term_ite() const; unsigned blast_term_ite_inflation() const; void register_finite_sort(sort * s, sort_kind k); /** Register uninterpreted constant to be used as an implicitly quantified variable. The variable gets quantified in the formula passed to rule::mk_rule_from_horn. */ void register_variable(func_decl* var); /* Replace constants that have been registered as variables by de-Bruijn indices and corresponding universal (if is_forall is true) or existential quantifier. */ expr_ref bind_vars(expr* fml, bool is_forall); bool& bind_vars_enabled() { return m_enable_bind_variables; } /** Register datalog relation. If named is true, we associate the predicate with its name, so that it can be retrieved by the try_get_predicate_decl() function. Auxiliary predicates introduced e.g. by rule transformations do not need to be named. */ void register_predicate(func_decl * pred, bool named); /** Restrict reltaions to set of predicates. */ void restrict_predicates(func_decl_set const& preds); /** \brief Retrieve predicates */ func_decl_set const& get_predicates() const { return m_preds; } ast_ref_vector const &get_pinned() const {return m_pinned; } bool is_predicate(func_decl* pred) const { return m_preds.contains(pred); } bool is_predicate(expr * e) const { return is_app(e) && is_predicate(to_app(e)->get_decl()); } /** \brief If a predicate name has a \c func_decl object assigned, return pointer to it; otherwise return 0. Not all \c func_decl object used as relation identifiers need to be assigned to their names. Generally, the names coming from the parses are registered here. */ func_decl * try_get_predicate_decl(symbol const& pred_name) const { func_decl * res = nullptr; m_preds_by_name.find(pred_name, res); return res; } /** \brief Create a fresh head predicate declaration. */ func_decl * mk_fresh_head_predicate(symbol const & prefix, symbol const & suffix, unsigned arity, sort * const * domain, func_decl* orig_pred=nullptr); /** \brief Return number of a symbol in a DK_SYMBOL kind sort (\see register_sort() ) */ finite_element get_constant_number(relation_sort sort, symbol s); /** \brief Return number of a symbol in a DK_UINT64 kind sort (\see register_sort() ) */ finite_element get_constant_number(relation_sort srt, uint64_t el); /** \brief Output name of constant with number \c num in sort \c sort. */ void print_constant_name(relation_sort sort, uint64_t num, std::ostream & out); bool try_get_sort_constant_count(relation_sort srt, uint64_t & constant_count); uint64_t get_sort_size_estimate(relation_sort srt); /** \brief Assign names of variables used in the declaration of a predicate. These names are used when printing out the relations to make the output conform to the one of bddbddb. */ void set_argument_names(const func_decl * pred, const svector & var_names); symbol get_argument_name(const func_decl * pred, unsigned arg_index); void set_predicate_representation(func_decl * pred, unsigned relation_name_cnt, symbol const * relation_names); void set_output_predicate(func_decl * pred) { m_rule_set.set_output_predicate(pred); } rule_set & get_rules() { flush_add_rules(); return m_rule_set; } void get_rules_as_formulas(expr_ref_vector& fmls, expr_ref_vector& qs, svector& names); void get_raw_rule_formulas(expr_ref_vector& fmls, svector& names, unsigned_vector &bounds); void add_fact(app * head); void add_fact(func_decl * pred, const relation_fact & fact); bool has_facts(func_decl * pred) const; void add_rule(rule_ref& r); void assert_expr(expr* e); expr_ref get_background_assertion(); unsigned get_num_assertions() { return m_background.size(); } expr* get_assertion(unsigned i) const { return m_background[i]; } /** Method exposed from API for adding rules. */ void add_rule(expr* rl, symbol const& name, unsigned bound = UINT_MAX); /** Update a named rule. */ void update_rule(expr* rl, symbol const& name); /** Retrieve the maximal number of relevant unfoldings of 'pred' with respect to the current state. */ unsigned get_num_levels(func_decl* pred); /** Retrieve reachable facts of 'pred'. */ expr_ref get_reachable(func_decl *pred); /** Retrieve the current cover of 'pred' up to 'level' unfoldings. Return just the delta that is known at 'level'. To obtain the full set of properties of 'pred' one should query at 'level+1', 'level+2' etc, and include level=-1. */ expr_ref get_cover_delta(int level, func_decl* pred); /** Add a property of predicate 'pred' at 'level'. It gets pushed forward when possible. */ void add_cover(int level, func_decl* pred, expr* property); /** Add an invariant of predicate 'pred'. */ void add_invariant (func_decl *pred, expr *property); /** \brief Check rule subsumption. */ bool check_subsumes(rule const& stronger_rule, rule const& weaker_rule); /** \brief Check if rule is well-formed according to engine. */ void check_rules(rule_set& r); /** \brief Return true if facts to \c pred can be added using the \c add_table_fact() function. This function should return true if \c pred is represented by a table_relation and there is no transformation of relation values before they are put into the table. */ void add_table_fact(func_decl * pred, const table_fact & fact); void add_table_fact(func_decl * pred, unsigned num_args, unsigned args[]); /** \brief To be called after all rules are added. */ void close(); void ensure_closed(); bool is_closed() { return m_closed; } /** \brief Undo the effect of the \c close operation. */ void reopen(); void ensure_opened(); model_converter_ref& get_model_converter() { return m_mc; } void add_model_converter(model_converter* mc) { m_mc = concat(m_mc.get(), mc); } proof_converter_ref& get_proof_converter() { return m_pc; } void add_proof_converter(proof_converter* pc) { m_pc = concat(m_pc.get(), pc); } void transform_rules(rule_transformer& transf); void transform_rules(rule_transformer::plugin* plugin); void replace_rules(rule_set const& rs); void record_transformed_rules(); void apply_default_transformation(); void collect_params(param_descrs& r); void updt_params(params_ref const& p); void display_rules(std::ostream & out) const { m_rule_set.display(out); } void display(std::ostream & out) const; void display_smt2(unsigned num_queries, expr* const* qs, std::ostream& out); void display_profile(std::ostream& out) const; // ----------------------------------- // // basic usage methods // // ----------------------------------- bool canceled() { return m.canceled() && (m_last_status = CANCELED, true); } void cleanup(); /** \brief check if query 'q' is satisfied under asserted rules and background. If successful, return OK and into \c result assign a relation with all tuples matching the query. Otherwise return reason for failure and do not modify \c result. The numbers of variables in the query body must form a contiguous sequence starting from zero. The caller becomes an owner of the relation object returned in \c result. The relation object, however, should not outlive the datalog context since it is linked to a relation plugin in the context. */ lbool query(expr* q); lbool query_from_lvl (expr* q, unsigned lvl); /** \brief retrieve model from inductive invariant that shows query is unsat. \pre engine == 'pdr' || engine == 'duality' - this option is only supported for PDR mode and Duality mode. */ model_ref get_model(); /** \brief retrieve proof from derivation of the query. \pre engine == 'pdr' || engine == 'duality'- this option is only supported for PDR mode and Duality mode. */ proof_ref get_proof(); /** Query multiple output relations. */ lbool rel_query(unsigned num_rels, func_decl * const* rels); /** \brief retrieve last proof status. */ execution_result get_status(); void set_status(execution_result r) { m_last_status = r; } /** \brief retrieve formula corresponding to query that returns l_true. The formula describes one or more instances of the existential variables in the query that are derivable. */ expr* get_answer_as_formula(); /** * get bottom-up (from query) sequence of ground predicate instances * (for e.g. P(0,1,0,0,3)) that together form a ground derivation to query */ expr* get_ground_sat_answer (); /** * \brief obtain the sequence of rules along the counterexample trace */ void get_rules_along_trace (rule_ref_vector& rules); void get_rules_along_trace_as_formulas (expr_ref_vector& rules, svector& names); void collect_statistics(statistics& st) const; void reset_statistics(); /** \brief Display a certificate for reachability and/or unreachability. */ std::ostream& display_certificate(std::ostream& out); /** \brief query result if it contains fact. */ bool result_contains_fact(relation_fact const& f); rel_context_base* get_rel_context() { ensure_engine(); return m_rel; } void add_callback(void *state, const datalog::t_new_lemma_eh new_lemma_eh, const datalog::t_predecessor_eh predecessor_eh, const datalog::t_unfold_eh unfold_eh) { ensure_engine(); m_engine->add_callback(state, new_lemma_eh, predecessor_eh, unfold_eh); } void add_constraint (expr *c, unsigned lvl){ ensure_engine(); m_engine->add_constraint(c, lvl); } private: /** Just reset all tables. */ void reset_tables(); void flush_add_rules(); void ensure_engine(expr* e = nullptr); // auxiliary functions for SMT2 pretty-printer. void declare_vars(expr_ref_vector& rules, mk_fresh_name& mk_fresh, std::ostream& out); //undefined and private copy constructor and operator= context(const context&); context& operator=(const context&); bool is_query(expr* e); void display_rel_decl(std::ostream& out, func_decl* f); }; }; #endif /* DL_CONTEXT_H_ */ z3-z3-4.8.7/src/muz/base/dl_costs.cpp000066400000000000000000000075051356505360400172700ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_costs.cpp Abstract: Author: Krystof Hoder (t-khoder) 2010-09-20. Revision History: --*/ #include "util/debug.h" #include "util/stopwatch.h" #include "muz/base/dl_context.h" #include "muz/base/dl_rule.h" #include "muz/base/dl_costs.h" namespace datalog { // ----------------------------------- // // costs // // ----------------------------------- costs::costs() : milliseconds(0), instructions(0) {} bool costs::empty() const { return !milliseconds && !instructions; } void costs::reset() { milliseconds = 0; instructions = 0; } costs costs::operator-(const costs & o) const { costs res(*this); SASSERT(milliseconds>o.milliseconds); res.milliseconds-=o.milliseconds; SASSERT(instructions>o.instructions); res.instructions-=o.instructions; return res; } void costs::operator+=(const costs & o) { milliseconds+=o.milliseconds; instructions+=o.instructions; } bool costs::passes_thresholds(context & ctx) const { return milliseconds >= ctx.dl_profile_milliseconds_threshold(); } void costs::output(std::ostream & out) const { out << "instr: " << instructions << " time: " << milliseconds << "ms"; } // ----------------------------------- // // accounted_object // // ----------------------------------- accounted_object::~accounted_object() { if(m_parent_object) { SASSERT(m_context); m_context->get_rule_manager().dec_ref(m_parent_object); } } void accounted_object::set_accounting_parent_object(context & ctx, rule * parent) { if(m_parent_object) { SASSERT(m_context); SASSERT(m_context==&ctx); m_context->get_rule_manager().dec_ref(m_parent_object); } m_context = &ctx; m_parent_object = parent; m_context->get_rule_manager().inc_ref(m_parent_object); } void accounted_object::process_costs() { costs delta = get_current_costs(); if(delta.empty()) { return; } get_current_costs().reset(); accounted_object * obj = this; do { obj->m_processed_cost+=delta; obj=obj->m_parent_object; } while(obj); } void accounted_object::get_total_cost(costs & result) const { result.reset(); result+=m_current_cost; result+=m_processed_cost; } bool accounted_object::passes_output_thresholds(context & ctx) const { costs c; get_total_cost(c); return c.passes_thresholds(ctx); } void accounted_object::output_profile(std::ostream & out) const { costs c; get_total_cost(c); c.output(out); } // ----------------------------------- // // cost_recorder // // ----------------------------------- cost_recorder::cost_recorder() : m_obj(nullptr) { m_stopwatch = alloc(stopwatch); m_stopwatch->start(); } cost_recorder::~cost_recorder() { if(m_obj) { finish(); } dealloc(m_stopwatch); } void cost_recorder::start(accounted_object * obj) { uint64_t curr_time = static_cast(m_stopwatch->get_current_seconds()*1000); if(m_obj) { costs::time_type time_delta = static_cast(curr_time-m_last_time); costs & c = m_obj->get_current_costs(); c.instructions++; c.milliseconds+=time_delta; m_obj->m_being_recorded = false; } m_running = obj!=nullptr; m_obj = obj; m_last_time = curr_time; if(obj) { m_obj->m_being_recorded = true; } } }; z3-z3-4.8.7/src/muz/base/dl_costs.h000066400000000000000000000052751356505360400167370ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_costs.h Abstract: Author: Krystof Hoder (t-khoder) 2010-09-20. Revision History: --*/ #ifndef DL_COSTS_H_ #define DL_COSTS_H_ #include #include "ast/ast.h" class stopwatch; namespace datalog { class context; class rule; class cost_recorder; struct costs { typedef unsigned time_type; time_type milliseconds; unsigned instructions; costs(); bool empty() const; void reset(); costs operator-(const costs & o) const; void operator+=(const costs & o); bool passes_thresholds(context & ctx) const; void output(std::ostream & out) const; }; class accounted_object { friend class cost_recorder; context * m_context; rule * m_parent_object; costs m_current_cost; costs m_processed_cost; bool m_being_recorded; protected: accounted_object() : m_context(nullptr), m_parent_object(nullptr), m_being_recorded(false) {} ~accounted_object(); public: void set_accounting_parent_object(context & ctx, rule * parent); rule * get_parent_object() const { return m_parent_object; } costs & get_current_costs() { return m_current_cost; } const costs & get_current_costs() const { return m_current_cost; } const costs & get_processed_costs() const { return m_processed_cost; } void get_total_cost(costs & result) const; bool being_recorded() const { return m_being_recorded; } void process_costs(); bool passes_output_thresholds(context & ctx) const; void output_profile(std::ostream & out) const; private: //private and undefined copy constructor and operator= to avoid the default ones accounted_object(const accounted_object &); accounted_object& operator=(const accounted_object &); }; class cost_recorder { accounted_object * m_obj; //it's a pointer to avoid everything depending on the stopwatch.h //(and transitively then on windows.h) header file stopwatch * m_stopwatch; bool m_running; uint64_t m_last_time; public: cost_recorder(); ~cost_recorder(); /** \brief Start recording costs for the next object. If the recording of the previous object did not finish, it will be finished here. Also, it will be done more efficiently than if the \c finish() function was called before separately. */ void start(accounted_object *); void finish() { start(nullptr); } }; }; #endif /* DL_COSTS_H_ */ z3-z3-4.8.7/src/muz/base/dl_engine_base.h000066400000000000000000000107511356505360400200360ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: dl_engine_base.h Abstract: Base class for Datalog engines. Author: Nikolaj Bjorner (nbjorner) 2013-08-28 Revision History: --*/ #ifndef DL_ENGINE_BASE_H_ #define DL_ENGINE_BASE_H_ #include "model/model.h" #include "muz/base/dl_util.h" namespace datalog { enum DL_ENGINE { DATALOG_ENGINE, SPACER_ENGINE, BMC_ENGINE, QBMC_ENGINE, TAB_ENGINE, CLP_ENGINE, DDNF_ENGINE, LAST_ENGINE }; typedef void (*t_new_lemma_eh)(void *state, expr *lemma, unsigned level); typedef void (*t_predecessor_eh)(void *state); typedef void (*t_unfold_eh)(void *state); class engine_base { ast_manager& m; std::string m_name; public: engine_base(ast_manager& m, char const* name): m(m), m_name(name) {} virtual ~engine_base() {} virtual expr_ref get_answer() = 0; virtual expr_ref get_ground_sat_answer () { throw default_exception(std::string("operation is not supported for ") + m_name); } virtual lbool query(expr* q) = 0; virtual lbool query(unsigned num_rels, func_decl*const* rels) { if (num_rels != 1) return l_undef; expr_ref q(m); expr_ref_vector args(m); sort_ref_vector sorts(m); svector names; func_decl* r = rels[0]; for (unsigned i = 0; i < r->get_arity(); ++i) { args.push_back(m.mk_var(i, r->get_domain(i))); sorts.push_back(r->get_domain(i)); names.push_back(symbol(i)); } sorts.reverse(); names.reverse(); q = m.mk_app(r, args.size(), args.c_ptr()); if (!args.empty()) { q = m.mk_exists(sorts.size(), sorts.c_ptr(), names.c_ptr(), q); } return query(q); } virtual lbool query_from_lvl (expr* q, unsigned lvl) { throw default_exception(std::string("operation is not supported for ") + m_name); } virtual void reset_statistics() {} virtual void display_profile(std::ostream& out) {} virtual void collect_statistics(statistics& st) const {} virtual unsigned get_num_levels(func_decl* pred) { throw default_exception(std::string("get_num_levels is not supported for ") + m_name); } virtual expr_ref get_reachable(func_decl* pred) { throw default_exception(std::string("operation is not supported for ") + m_name); } virtual expr_ref get_cover_delta(int level, func_decl* pred) { throw default_exception(std::string("operation is not supported for ") + m_name); } virtual void add_cover(int level, func_decl* pred, expr* property) { throw default_exception(std::string("operation is not supported for ") + m_name); } virtual void add_invariant (func_decl *pred, expr *property) { throw default_exception(std::string("operation is not supported for ") + m_name); } virtual void display_certificate(std::ostream& out) const { throw default_exception(std::string("certificates are not supported for ") + m_name); } virtual model_ref get_model() { return model_ref(alloc(model, m)); } virtual void get_rules_along_trace (rule_ref_vector& rules) { throw default_exception(std::string("get_rules_along_trace is not supported for ") + m_name); } virtual proof_ref get_proof() { return proof_ref(m.mk_asserted(m.mk_true()), m); } virtual void add_callback(void *state, const t_new_lemma_eh new_lemma_eh, const t_predecessor_eh predecessor_eh, const t_unfold_eh unfold_eh) { throw default_exception(std::string("add_lemma_exchange_callbacks is not supported for ") + m_name); } virtual void add_constraint (expr *c, unsigned lvl){ throw default_exception(std::string("add_constraint is not supported for ") + m_name); } virtual void updt_params() {} virtual void cancel() {} virtual void cleanup() {} }; class context; class register_engine_base { public: virtual engine_base* mk_engine(DL_ENGINE engine_type) = 0; virtual void set_context(context* ctx) = 0; }; } #endif z3-z3-4.8.7/src/muz/base/dl_rule.cpp000066400000000000000000001030411356505360400170740ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_rule.cpp Abstract: Author: Krystof Hoder (t-khoder) 2011-10-19. Revision History: Nikolaj Bjorner (nbjorner) 2012-10-31. Check for enabledness of fix_unbound_vars inside call. This function gets called from many rule tansformers. --*/ #include #include #include "ast/ast_pp.h" #include "muz/base/dl_context.h" #include "util/map.h" #include "ast/recurse_expr_def.h" #include "muz/base/dl_rule.h" #include "qe/qe.h" #include "ast/for_each_expr.h" #include "ast/used_vars.h" #include "ast/rewriter/var_subst.h" #include "ast/rewriter/rewriter_def.h" #include "ast/rewriter/th_rewriter.h" #include "ast/ast_smt2_pp.h" #include "ast/used_symbols.h" #include "ast/rewriter/quant_hoist.h" #include "ast/rewriter/expr_replacer.h" #include "ast/rewriter/bool_rewriter.h" #include "ast/rewriter/expr_safe_replace.h" #include "tactic/generic_model_converter.h" #include "ast/scoped_proof.h" #include "ast/datatype_decl_plugin.h" #include "ast/ast_util.h" namespace datalog { rule_manager::rule_manager(context& ctx) : m(ctx.get_manager()), m_ctx(ctx), m_body(m), m_head(m), m_args(m), m_hnf(m), m_qe(m, params_ref(), false), m_rwr(m), m_ufproc(m), m_fd_proc(m) {} void rule_manager::inc_ref(rule * r) { if (r) { SASSERT(r->m_ref_cnt != UINT_MAX); r->m_ref_cnt++; } } void rule_manager::dec_ref(rule * r) { if (r) { SASSERT(r->m_ref_cnt > 0); r->m_ref_cnt--; if (r->m_ref_cnt == 0) { r->deallocate(m); } } } void rule_manager::remove_labels(expr_ref& fml, proof_ref& pr) { m_rwr.remove_labels(fml, pr); } var_idx_set& rule_manager::collect_vars(expr* e) { return collect_vars(e, nullptr); } var_idx_set& rule_manager::collect_vars(expr* e1, expr* e2) { reset_collect_vars(); if (e1) accumulate_vars(e1); if (e2) accumulate_vars(e2); return finalize_collect_vars(); } void rule_manager::reset_collect_vars() { m_var_idx.reset(); m_free_vars.reset(); } var_idx_set& rule_manager::finalize_collect_vars() { unsigned sz = m_free_vars.size(); for (unsigned i = 0; i < sz; ++i) { if (m_free_vars[i]) m_var_idx.insert(i); } return m_var_idx; } var_idx_set& rule_manager::collect_tail_vars(rule * r) { reset_collect_vars(); unsigned n = r->get_tail_size(); for (unsigned i=0;iget_tail(i)); } return finalize_collect_vars(); } var_idx_set& rule_manager::collect_rule_vars_ex(rule * r, app* t) { reset_collect_vars(); unsigned n = r->get_tail_size(); accumulate_vars(r->get_head()); for (unsigned i=0;iget_tail(i) != t) { accumulate_vars(r->get_tail(i)); } } return finalize_collect_vars(); } var_idx_set& rule_manager::collect_rule_vars(rule * r) { reset_collect_vars(); unsigned n = r->get_tail_size(); accumulate_vars(r->get_head()); for (unsigned i=0;iget_tail(i)); } return finalize_collect_vars(); } void rule_manager::accumulate_vars(expr* e) { m_free_vars.accumulate(e); } void rule_manager::mk_rule(expr* fml, proof* p, rule_set& rules, symbol const& name) { scoped_proof_mode _sc(m, m_ctx.generate_proof_trace()?PGM_ENABLED:PGM_DISABLED); proof_ref pr(p, m); expr_ref fml1(m); bind_variables(fml, true, fml1); if (fml1 != fml && pr) { pr = m.mk_asserted(fml1); } remove_labels(fml1, pr); mk_rule_core(fml1, pr, rules, name); } void rule_manager::mk_negations(app_ref_vector& body, svector& is_negated) { for (unsigned i = 0; i < body.size(); ++i) { expr* e = body[i].get(), *e1; if (m.is_not(e, e1) && m_ctx.is_predicate(e1)) { check_app(e1); body[i] = to_app(e1); is_negated.push_back(true); } else { is_negated.push_back(false); } } } void rule_manager::mk_rule_core(expr* fml, proof* p, rule_set& rules, symbol const& name) { expr_ref_vector fmls(m); proof_ref_vector prs(m); m_hnf.reset(); m_hnf.set_name(name); m_hnf(fml, p, fmls, prs); for (unsigned i = 0; i < m_hnf.get_fresh_predicates().size(); ++i) { m_ctx.register_predicate(m_hnf.get_fresh_predicates()[i], false); } for (unsigned i = 0; i < fmls.size(); ++i) { mk_horn_rule(fmls[i].get(), prs[i].get(), rules, name); } } void rule_manager::mk_horn_rule(expr* fml, proof* p, rule_set& rules, symbol const& name) { m_body.reset(); m_neg.reset(); unsigned index = extract_horn(fml, m_body, m_head); hoist_compound_predicates(index, m_head, m_body); TRACE("dl_rule", tout << mk_pp(m_head, m) << " :- "; for (unsigned i = 0; i < m_body.size(); ++i) { tout << mk_pp(m_body[i].get(), m) << " "; } tout << "\n";); mk_negations(m_body, m_neg); check_valid_rule(m_head, m_body.size(), m_body.c_ptr()); rule_ref r(*this); r = mk(m_head.get(), m_body.size(), m_body.c_ptr(), m_neg.c_ptr(), name); expr_ref fml1(m); if (p) { to_formula(*r, fml1); if (fml1 == fml) { // no-op. } else if (is_quantifier(fml1)) { p = m.mk_modus_ponens(p, m.mk_symmetry(m.mk_der(to_quantifier(fml1), fml))); } else { p = m.mk_modus_ponens(p, m.mk_rewrite(fml, fml1)); } } if (m_ctx.fix_unbound_vars()) { fix_unbound_vars(r, true); } if (p) { expr_ref fml2(m); to_formula(*r, fml2); if (fml1 != fml2) { p = m.mk_modus_ponens(p, m.mk_rewrite(fml1, fml2)); } r->set_proof(m, p); } rules.add_rule(r); } unsigned rule_manager::extract_horn(expr* fml, app_ref_vector& body, app_ref& head) { expr* e1, *e2; if (::is_forall(fml)) { fml = to_quantifier(fml)->get_expr(); } unsigned index = m_counter.get_next_var(fml); if (m.is_implies(fml, e1, e2)) { m_args.reset(); head = ensure_app(e2); flatten_and(e1, m_args); for (unsigned i = 0; i < m_args.size(); ++i) { body.push_back(ensure_app(m_args[i].get())); } } else { head = ensure_app(fml); } return index; } void rule_manager::hoist_compound_predicates(unsigned index, app_ref& head, app_ref_vector& body) { unsigned sz = body.size(); hoist_compound(index, head, body); for (unsigned i = 0; i < sz; ++i) { app_ref b(body[i].get(), m); hoist_compound(index, b, body); body[i] = b; } } func_decl* rule_manager::mk_query(expr* query, rule_set& rules) { TRACE("dl", tout << mk_pp(query, m) << "\n";); ptr_vector vars; svector names; app_ref_vector body(m); expr_ref q(m); // Add implicit variables. // Remove existential prefix. bind_variables(query, false, q); quantifier_hoister qh(m); qh.pull_quantifier(false, q, nullptr, &names); // retrieve free variables. m_free_vars(q); vars.append(m_free_vars.size(), m_free_vars.c_ptr()); if (vars.contains(static_cast(nullptr))) { var_subst sub(m, false); expr_ref_vector args(m); // [s0, 0, s2, ..] // [0 -> 0, 1 -> x, 2 -> 1, ..] for (unsigned i = 0, j = 0; i < vars.size(); ++i) { if (vars[i]) { args.push_back(m.mk_var(j, vars[i])); ++j; } else { args.push_back(m.mk_var(0, m.mk_bool_sort())); } } q = sub(q, args.size(), args.c_ptr()); vars.reset(); m_free_vars(q); vars.append(m_free_vars.size(), m_free_vars.c_ptr()); } SASSERT(!vars.contains(static_cast(0)) && "Unused variables have been eliminated"); // flatten body and extract query predicate. if (!is_app(q)) { throw default_exception("Query body is not well-formed"); } body.push_back(to_app(q)); flatten_body(body); func_decl* body_pred = nullptr; for (unsigned i = 0; i < body.size(); i++) { if (is_uninterp(body[i].get())) { body_pred = body[i]->get_decl(); break; } } // we want outermost declared variable first to // follow order of quantified variables so we reverse vars. while (vars.size() > names.size()) { names.push_back(symbol(names.size())); } vars.reverse(); names.reverse(); func_decl* qpred = m_ctx.mk_fresh_head_predicate(symbol("query"), symbol(), vars.size(), vars.c_ptr(), body_pred); m_ctx.register_predicate(qpred, false); rules.set_output_predicate(qpred); if (m_ctx.get_model_converter()) { generic_model_converter* mc = alloc(generic_model_converter, m, "dl_rule"); mc->hide(qpred); m_ctx.add_model_converter(mc); } expr_ref_vector qhead_args(m); for (unsigned i = 0; i < vars.size(); i++) { qhead_args.push_back(m.mk_var(vars.size()-i-1, vars[i])); } app_ref qhead(m.mk_app(qpred, qhead_args.c_ptr()), m); app_ref impl(m.mk_implies(q, qhead), m); expr_ref rule_expr(impl.get(), m); if (!vars.empty()) { rule_expr = m.mk_forall(vars.size(), vars.c_ptr(), names.c_ptr(), impl); } TRACE("dl", tout << rule_expr << "\n";); scoped_proof_mode _sc(m, m_ctx.generate_proof_trace()?PGM_ENABLED:PGM_DISABLED); proof_ref pr(m); if (m_ctx.generate_proof_trace()) { pr = m.mk_asserted(rule_expr); } mk_rule(rule_expr, pr, rules); return qpred; } void rule_manager::bind_variables(expr* fml, bool is_forall, expr_ref& result) { result = m_ctx.bind_vars(fml, is_forall); } void rule_manager::flatten_body(app_ref_vector& body) { expr_ref_vector r(m); for (unsigned i = 0; i < body.size(); ++i) { r.push_back(body[i].get()); } flatten_and(r); body.reset(); for (unsigned i = 0; i < r.size(); ++i) { body.push_back(ensure_app(r[i].get())); } } void rule_manager::hoist_compound(unsigned& num_bound, app_ref& fml, app_ref_vector& body) { expr_ref e(m); expr* not_fml; if (m.is_not(fml, not_fml)) { fml = ensure_app(not_fml); hoist_compound(num_bound, fml, body); fml = m.mk_not(fml); return; } if (!m_ctx.is_predicate(fml)) { return; } m_args.reset(); for (unsigned i = 0; i < fml->get_num_args(); ++i) { e = fml->get_arg(i); if (!is_app(e)) { m_args.push_back(e); continue; } app* b = to_app(e); if (m.is_value(b)) { m_args.push_back(e); } else { var* v = m.mk_var(num_bound++, m.get_sort(b)); m_args.push_back(v); body.push_back(m.mk_eq(v, b)); } } fml = m.mk_app(fml->get_decl(), m_args.size(), m_args.c_ptr()); TRACE("dl_rule", tout << mk_pp(fml.get(), m) << "\n";); } class contains_predicate_proc { context const& ctx; public: struct found {}; contains_predicate_proc(context const& ctx): ctx(ctx) {} void operator()(var * n) {} void operator()(quantifier * n) {} void operator()(app* n) { if (ctx.is_predicate(n)) throw found(); } }; bool rule_manager::contains_predicate(expr* fml) const { contains_predicate_proc proc(m_ctx); try { quick_for_each_expr(proc, fml); } catch (const contains_predicate_proc::found &) { return true; } return false; } bool rule_manager::is_forall(ast_manager& m, expr* e, quantifier*& q) { expr* e1, *e2; if (m.is_iff(e, e1, e2)) { if (m.is_true(e2)) { e = e1; } else if (m.is_true(e1)) { e = e2; } } return ::is_forall(e); } app_ref rule_manager::ensure_app(expr* e) { SASSERT(m.is_bool(e)); if (is_app(e)) { return app_ref(to_app(e), m); } else { return app_ref(m.mk_eq(e, m.mk_true()), m); } } void rule_manager::check_app(expr* e) { if (!is_app(e)) { std::ostringstream out; out << "expected application, got " << mk_pp(e, m); throw default_exception(out.str()); } } rule * rule_manager::mk(app * head, unsigned n, app * const * tail, bool const * is_negated, symbol const& name, bool normalize) { DEBUG_CODE(check_valid_rule(head, n, tail);); unsigned sz = rule::get_obj_size(n); void * mem = m.get_allocator().allocate(sz); rule * r = new (mem) rule(); r->m_head = head; r->m_name = name; r->m_tail_size = n; r->m_proof = nullptr; m.inc_ref(r->m_head); app * * uninterp_tail = r->m_tail; //grows upwards app * * interp_tail = r->m_tail+n; //grows downwards bool has_neg = false; for (unsigned i = 0; i < n; i++) { bool is_neg = (is_negated != nullptr && is_negated[i]); app * curr = tail[i]; if (is_neg && !m_ctx.is_predicate(curr)) { curr = m.mk_not(curr); is_neg = false; } if (is_neg) { has_neg = true; } app * tail_entry = TAG(app *, curr, is_neg); if (m_ctx.is_predicate(curr)) { *uninterp_tail=tail_entry; uninterp_tail++; } else { interp_tail--; *interp_tail=tail_entry; } m.inc_ref(curr); } SASSERT(uninterp_tail==interp_tail); r->m_uninterp_cnt = static_cast(uninterp_tail - r->m_tail); if (has_neg) { //put negative predicates between positive and interpreted app * * it = r->m_tail; app * * end = r->m_tail + r->m_uninterp_cnt; while(it!=end) { bool is_neg = GET_TAG(*it)!=0; if (is_neg) { --end; std::swap(*it, *end); } else { ++it; } } r->m_positive_cnt = static_cast(it - r->m_tail); SASSERT(r->m_positive_cnt < r->m_uninterp_cnt); } else { r->m_positive_cnt = r->m_uninterp_cnt; } if (normalize) { r->norm_vars(*this); } return r; } rule * rule_manager::mk(rule const * source, symbol const& name) { return mk(source, source->get_head(), name); } rule * rule_manager::mk(rule const * source, app * new_head, symbol const& name) { unsigned n = source->get_tail_size(); unsigned sz = rule::get_obj_size(n); void * mem = m.get_allocator().allocate(sz); rule * r = new (mem) rule(); r->m_head = new_head; r->m_name = name; r->m_tail_size = n; r->m_positive_cnt = source->m_positive_cnt; r->m_uninterp_cnt = source->m_uninterp_cnt; r->m_proof = nullptr; m.inc_ref(r->m_head); for (unsigned i = 0; i < n; i++) { r->m_tail[i] = source->m_tail[i]; m.inc_ref(r->get_tail(i)); } return r; } void rule_manager::to_formula(rule const& r, expr_ref& fml) { ast_manager & m = fml.get_manager(); expr_ref_vector body(m); for (unsigned i = 0; i < r.get_tail_size(); i++) { body.push_back(r.get_tail(i)); if (r.is_neg_tail(i)) { body[body.size()-1] = m.mk_not(body.back()); } } fml = r.get_head(); switch (body.size()) { case 0: break; case 1: fml = m.mk_implies(body[0].get(), fml); break; default: fml = m.mk_implies(m.mk_and(body.size(), body.c_ptr()), fml); break; } m_free_vars(fml); if (m_free_vars.empty()) { return; } svector names; used_symbols<> us; m_free_vars.set_default_sort(m.mk_bool_sort()); us(fml); m_free_vars.reverse(); for (unsigned j = 0, i = 0; i < m_free_vars.size(); ++j) { for (char c = 'A'; i < m_free_vars.size() && c <= 'Z'; ++c) { func_decl_ref f(m); std::stringstream _name; _name << c; if (j > 0) _name << j; symbol name(_name.str().c_str()); if (!us.contains(name)) { names.push_back(name); ++i; } } } fml = m.mk_forall(m_free_vars.size(), m_free_vars.c_ptr(), names.c_ptr(), fml); } std::ostream& rule_manager::display_smt2(rule const& r, std::ostream & out) { expr_ref fml(m); to_formula(r, fml); return out << mk_ismt2_pp(fml, m); } void rule_manager::reduce_unbound_vars(rule_ref& r) { unsigned ut_len = r->get_uninterpreted_tail_size(); unsigned t_len = r->get_tail_size(); expr_ref_vector conjs(m); if (ut_len == t_len) { return; } reset_collect_vars(); accumulate_vars(r->get_head()); for (unsigned i = 0; i < ut_len; ++i) { accumulate_vars(r->get_tail(i)); } var_idx_set& index_set = finalize_collect_vars(); for (unsigned i = ut_len; i < t_len; ++i) { conjs.push_back(r->get_tail(i)); } m_qe(index_set, false, conjs); bool change = conjs.size() != t_len - ut_len; for (unsigned i = 0; !change && i < conjs.size(); ++i) { change = r->get_tail(ut_len+i) != conjs[i].get(); } if (change) { app_ref_vector tail(m); svector tail_neg; for (unsigned i = 0; i < ut_len; ++i) { tail.push_back(r->get_tail(i)); tail_neg.push_back(r->is_neg_tail(i)); } for (unsigned i = 0; i < conjs.size(); ++i) { tail.push_back(ensure_app(conjs[i].get())); } tail_neg.resize(tail.size(), false); r = mk(r->get_head(), tail.size(), tail.c_ptr(), tail_neg.c_ptr(), r->name()); TRACE("dl", r->display(m_ctx, tout << "reduced rule\n");); } } void rule_manager::fix_unbound_vars(rule_ref& r, bool try_quantifier_elimination) { reduce_unbound_vars(r); if (!m_ctx.fix_unbound_vars()) { return; } unsigned ut_len = r->get_uninterpreted_tail_size(); unsigned t_len = r->get_tail_size(); if (ut_len == t_len) { // no interpreted tail to fix return; } var_counter vctr; app_ref_vector tail(m); svector tail_neg; app_ref head(r->get_head(), m); vctr.count_vars(head); for (unsigned i = 0; i < ut_len; i++) { app * t = r->get_tail(i); vctr.count_vars(t); tail.push_back(t); tail_neg.push_back(r->is_neg_tail(i)); } var_idx_set unbound_vars; expr_ref_vector tails_with_unbound(m); for (unsigned i = ut_len; i < t_len; i++) { app * t = r->get_tail(i); m_free_vars(t); bool has_unbound = false; unsigned iv_size = m_free_vars.size(); for (unsigned i=0; i qsorts; qsorts.resize(q_var_cnt); unsigned q_idx = 0; for (unsigned v = 0; v < m_free_vars.size(); ++v) { sort * v_sort = m_free_vars[v]; if (!v_sort) { //this variable index is not used continue; } unsigned new_idx; if (unbound_vars.contains(v)) { new_idx = q_idx++; qsorts.push_back(v_sort); } else { new_idx = v + q_var_cnt; } subst.push_back(m.mk_var(new_idx, v_sort)); } SASSERT(q_idx == q_var_cnt); svector qnames; for (unsigned i = 0; i < q_var_cnt; i++) { qnames.push_back(symbol(i)); } //quantifiers take this reversed qsorts.reverse(); qnames.reverse(); expr_ref unbound_tail_pre_quant(m), fixed_tail(m), quant_tail(m); var_subst vs(m, false); unbound_tail_pre_quant = vs(unbound_tail, subst.size(), subst.c_ptr()); quant_tail = m.mk_exists(q_var_cnt, qsorts.c_ptr(), qnames.c_ptr(), unbound_tail_pre_quant); if (try_quantifier_elimination) { TRACE("dl_rule_unbound_fix_pre_qe", tout<<"rule: "; r->display(m_ctx, tout); tout<<"tail with unbound vars: "<display(m_ctx, tout); tout<<"tail with unbound vars: "<name()); r->set_accounting_parent_object(m_ctx, old_r); } void rule_manager::mk_rule_rewrite_proof(rule& old_rule, rule& new_rule) { if (&old_rule != &new_rule && !new_rule.get_proof() && old_rule.get_proof()) { expr_ref fml(m); to_formula(new_rule, fml); scoped_proof _sc(m); proof* p = m.mk_rewrite(m.get_fact(old_rule.get_proof()), fml); new_rule.set_proof(m, m.mk_modus_ponens(old_rule.get_proof(), p)); } } void rule_manager::mk_rule_asserted_proof(rule& r) { if (m_ctx.generate_proof_trace()) { scoped_proof _scp(m); expr_ref fml(m); to_formula(r, fml); r.set_proof(m, m.mk_asserted(fml)); } } void rule_manager::substitute(rule_ref& r, unsigned sz, expr*const* es) { expr_ref tmp(m); app_ref new_head(m); app_ref_vector new_tail(m); svector tail_neg; var_subst vs(m, false); tmp = vs(r->get_head(), sz, es); new_head = to_app(tmp); for (unsigned i = 0; i < r->get_tail_size(); ++i) { tmp = vs(r->get_tail(i), sz, es); new_tail.push_back(to_app(tmp)); tail_neg.push_back(r->is_neg_tail(i)); } r = mk(new_head.get(), new_tail.size(), new_tail.c_ptr(), tail_neg.c_ptr(), r->name(), false); // keep old variable indices around so we can compose with substitutions. // r->norm_vars(*this); } void rule_manager::check_valid_rule(app * head, unsigned n, app * const * tail) const { check_valid_head(head); } void rule_manager::check_valid_head(expr * head) const { SASSERT(head); if (!m_ctx.is_predicate(head)) { std::ostringstream out; out << "Illegal head. The head predicate needs to be uninterpreted and registered (as recursive) " << mk_pp(head, m); throw default_exception(out.str()); } unsigned num_args = to_app(head)->get_num_args(); for (unsigned i = 0; i < num_args; i++) { expr * arg = to_app(head)->get_arg(i); if (!is_var(arg) && !m.is_value(arg)) { std::ostringstream out; out << "Illegal argument to predicate in head " << mk_pp(arg, m); throw default_exception(out.str()); } } } bool rule_manager::is_fact(app * head) const { unsigned num_args = head->get_num_args(); for (unsigned i = 0; i < num_args; i++) { if (!m.is_value(head->get_arg(i))) return false; } return true; } void rule::deallocate(ast_manager & m) { m.dec_ref(m_head); unsigned n = get_tail_size(); for (unsigned i = 0; i < n; i++) { m.dec_ref(get_tail(i)); } if (m_proof) { m.dec_ref(m_proof); } this->~rule(); m.get_allocator().deallocate(get_obj_size(n), this); } void rule::set_proof(ast_manager& m, proof* p) { if (p) { m.inc_ref(p); } if (m_proof) { m.dec_ref(m_proof); } m_proof = p; } bool rule::is_in_tail(const func_decl * p, bool only_positive) const { unsigned len = only_positive ? get_positive_tail_size() : get_uninterpreted_tail_size(); for (unsigned i = 0; i < len; i++) { if (get_tail(i)->get_decl()==p) { return true; } } return false; } // // non-predicates may appear only in the interpreted tail, it is therefore // sufficient only to check the tail. // bool rule_manager::has_uninterpreted_non_predicates(rule const& r, func_decl*& f) const { unsigned sz = r.get_tail_size(); m_ufproc.reset(); m_visited.reset(); for (unsigned i = r.get_uninterpreted_tail_size(); i < sz && !m_ufproc.found(f); ++i) { for_each_expr_core(m_ufproc, m_visited, r.get_tail(i)); } return m_ufproc.found(f); } // // Quantifiers may appear only in the interpreted tail, it is therefore // sufficient only to check the interpreted tail. // void rule_manager::has_quantifiers(rule const& r, bool& existential, bool& universal) const { unsigned sz = r.get_tail_size(); m_qproc.reset(); m_visited.reset(); for (unsigned i = r.get_uninterpreted_tail_size(); i < sz; ++i) { for_each_expr_core(m_qproc, m_visited, r.get_tail(i)); } existential = m_qproc.m_exist; universal = m_qproc.m_univ; } bool rule_manager::has_quantifiers(rule const& r) const { bool exist, univ; has_quantifiers(r, exist, univ); return exist || univ; } bool rule_manager::is_finite_domain(rule const& r) const { m_visited.reset(); m_fd_proc.reset(); for (unsigned i = r.get_uninterpreted_tail_size(); i < r.get_tail_size(); ++i) { for_each_expr_core(m_fd_proc, m_visited, r.get_tail(i)); } for (unsigned i = 0; i < r.get_uninterpreted_tail_size(); ++i) { for (expr* arg : *r.get_tail(i)) { for_each_expr_core(m_fd_proc, m_visited, arg); } } for (expr* arg : *r.get_head()) { for_each_expr_core(m_fd_proc, m_visited, arg); } return m_fd_proc.is_fd(); } bool rule::has_negation() const { for (unsigned i = 0; i < get_uninterpreted_tail_size(); ++i) { if (is_neg_tail(i)) { return true; } } return false; } void rule::get_used_vars(used_vars& used) const { used.process(get_head()); unsigned sz = get_tail_size(); for (unsigned i = 0; i < sz; ++i) { used.process(get_tail(i)); } } void rule::get_vars(ast_manager& m, ptr_vector& sorts) const { sorts.reset(); used_vars used; get_used_vars(used); unsigned sz = used.get_max_found_var_idx_plus_1(); for (unsigned i = 0; i < sz; ++i) { sort* s = used.get(i); sorts.push_back(s?s:m.mk_bool_sort()); } } void rule::norm_vars(rule_manager & rm) { used_vars& used = rm.reset_used(); get_used_vars(used); unsigned first_unsused = used.get_max_found_var_idx_plus_1(); if (used.uses_all_vars(first_unsused)) { return; } ast_manager& m = rm.get_manager(); unsigned next_fresh_var = 0; expr_ref_vector subst_vals(m); for (unsigned i=0; i 0) out << ","; out << "\n "; if (is_neg_tail(i)) out << "not "; app * t = get_tail(i); if (ctx.is_predicate(t)) { output_predicate(ctx, t, out); } else { out << mk_pp(t, m); } } out << '.'; if (ctx.output_profile()) { out << " {"; output_profile(out); out << '}'; } out << '\n'; if (m_proof) { out << mk_pp(m_proof, m) << '\n'; } } bool rule_eq_proc::operator()(const rule * r1, const rule * r2) const { if (r1->get_head()!=r2->get_head()) { return false; } unsigned tail_len = r1->get_tail_size(); if (r2->get_tail_size()!=tail_len) { return false; } for (unsigned i=0; iget_tail(i)!=r2->get_tail(i)) { return false; } if (r1->is_neg_tail(i)!=r2->is_neg_tail(i)) { return false; } } return true; } unsigned rule::hash() const { unsigned res = get_head()->hash(); unsigned tail_len = get_tail_size(); for (unsigned i=0; ihash(), is_neg_tail(i))); } return res; } unsigned rule_hash_proc::operator()(const rule * r) const { return r->hash(); } }; z3-z3-4.8.7/src/muz/base/dl_rule.h000066400000000000000000000300161356505360400165420ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_rule.h Abstract: Author: Leonardo de Moura (leonardo) 2010-05-17. Revision History: --*/ #ifndef DL_RULE_H_ #define DL_RULE_H_ #include "ast/ast.h" #include "muz/base/dl_costs.h" #include "muz/base/dl_util.h" #include "ast/used_vars.h" #include "tactic/proof_converter.h" #include "tactic/model_converter.h" #include "ast/rewriter/ast_counter.h" #include "ast/rewriter/rewriter.h" #include "muz/base/hnf.h" #include "qe/qe_lite.h" #include "ast/rewriter/var_subst.h" #include "ast/datatype_decl_plugin.h" #include "ast/rewriter/label_rewriter.h" namespace datalog { class rule; class rule_manager; class rule_set; class table; class context; typedef obj_ref rule_ref; typedef ref_vector rule_ref_vector; typedef ptr_vector rule_vector; struct uninterpreted_function_finder_proc { ast_manager& m; datatype_util m_dt; dl_decl_util m_dl; bool m_found; func_decl* m_func; uninterpreted_function_finder_proc(ast_manager& m): m(m), m_dt(m), m_dl(m), m_found(false), m_func(nullptr) {} void operator()(var * n) { } void operator()(quantifier * n) { } void operator()(app * n) { if (is_uninterp(n) && !m_dl.is_rule_sort(n->get_decl()->get_range())) { m_found = true; m_func = n->get_decl(); } else if (m_dt.is_accessor(n)) { sort* s = m.get_sort(n->get_arg(0)); SASSERT(m_dt.is_datatype(s)); if (m_dt.get_datatype_constructors(s)->size() > 1) { m_found = true; m_func = n->get_decl(); } } } void reset() { m_found = false; m_func = nullptr; } bool found(func_decl*& f) const { f = m_func; return m_found; } }; struct quantifier_finder_proc { bool m_exist; bool m_univ; quantifier_finder_proc() : m_exist(false), m_univ(false) {} void operator()(var * n) { } void operator()(quantifier * n) { switch (n->get_kind()) { case forall_k: m_univ = true; break; case exists_k: m_exist = true; break; case lambda_k: UNREACHABLE(); } } void operator()(app * n) { } void reset() { m_exist = m_univ = false; } }; struct fd_finder_proc { ast_manager& m; bv_util m_bv; bool m_is_fd; fd_finder_proc(ast_manager& m): m(m), m_bv(m), m_is_fd(true) {} bool is_fd() const { return m_is_fd; } bool is_fd(sort* s) { return m.is_bool(s) || m_bv.is_bv_sort(s); } void operator()(var* n) { m_is_fd &= is_fd(n->get_sort()); } void operator()(quantifier* ) { m_is_fd = false; } void operator()(app* n) { m_is_fd &= is_fd(n->get_decl()->get_range()); } void reset() { m_is_fd = true; } }; /** \brief Manager for the \c rule class \remark \c rule_manager objects are interchangeable as long as they contain the same \c ast_manager object. */ class rule_manager { ast_manager& m; context& m_ctx; rule_counter m_counter; used_vars m_used; var_idx_set m_var_idx; expr_free_vars m_free_vars; app_ref_vector m_body; app_ref m_head; expr_ref_vector m_args; svector m_neg; hnf m_hnf; qe_lite m_qe; label_rewriter m_rwr; mutable uninterpreted_function_finder_proc m_ufproc; mutable quantifier_finder_proc m_qproc; mutable expr_sparse_mark m_visited; mutable fd_finder_proc m_fd_proc; // only the context can create a rule_manager friend class context; explicit rule_manager(context& ctx); /** \brief Move functions from predicate tails into the interpreted tail by introducing new variables. */ void hoist_compound_predicates(unsigned num_bound, app_ref& head, app_ref_vector& body); void hoist_compound(unsigned& num_bound, app_ref& fml, app_ref_vector& body); void flatten_body(app_ref_vector& body); void remove_labels(expr_ref& fml, proof_ref& pr); app_ref ensure_app(expr* e); void check_app(expr* e); bool contains_predicate(expr* fml) const; void bind_variables(expr* fml, bool is_forall, expr_ref& result); void mk_negations(app_ref_vector& body, svector& is_negated); void mk_rule_core(expr* fml, proof* p, rule_set& rules, symbol const& name); void mk_horn_rule(expr* fml, proof* p, rule_set& rules, symbol const& name); static expr_ref mk_implies(app_ref_vector const& body, expr* head); unsigned extract_horn(expr* fml, app_ref_vector& body, app_ref& head); /** \brief Perform cheap quantifier elimination to reduce the number of variables in the interpreted tail. */ void reduce_unbound_vars(rule_ref& r); void reset_collect_vars(); var_idx_set& finalize_collect_vars(); public: ast_manager& get_manager() const { return m; } void inc_ref(rule * r); void dec_ref(rule * r); used_vars& reset_used() { m_used.reset(); return m_used; } var_idx_set& collect_vars(expr * pred); var_idx_set& collect_vars(expr * e1, expr* e2); var_idx_set& collect_rule_vars(rule * r); var_idx_set& collect_rule_vars_ex(rule * r, app* t); var_idx_set& collect_tail_vars(rule * r); void accumulate_vars(expr* pred); // ptr_vector& get_var_sorts() { return m_vars; } var_idx_set& get_var_idx() { return m_var_idx; } /** \brief Create a Datalog rule from a Horn formula. The formula is of the form (forall (...) (forall (...) (=> (and ...) head))) */ void mk_rule(expr* fml, proof* p, rule_set& rules, symbol const& name = symbol::null); /** \brief Create a Datalog query from an expression. The formula is of the form (exists (...) (exists (...) (and ...)) */ func_decl* mk_query(expr* query, rule_set& rules); /** \brief Create a Datalog rule head :- tail[0], ..., tail[n-1]. Return 0 if it is not a valid rule. \remark A tail may contain negation. tail[i] is assumed to be negated if is_neg != 0 && is_neg[i] == true */ rule * mk(app * head, unsigned n, app * const * tail, bool const * is_neg = nullptr, symbol const& name = symbol::null, bool normalize = true); /** \brief Create a rule with the same tail as \c source and with a specified head. */ rule * mk(rule const * source, app * new_head, symbol const& name = symbol::null); /** \brief Create a copy of the given rule. */ rule * mk(rule const * source, symbol const& name = symbol::null); /** make sure there are not non-quantified variables that occur only in interpreted predicates */ void fix_unbound_vars(rule_ref& r, bool try_quantifier_elimination); /** \brief add proof that new rule is obtained by rewriting old rule. */ void mk_rule_rewrite_proof(rule& old_rule, rule& new_rule); /** \brief tag rule as asserted. */ void mk_rule_asserted_proof(rule& r); /** \brief apply substitution to variables of rule. */ void substitute(rule_ref& r, unsigned sz, expr*const* es); /** \brief Check that head :- tail[0], ..., tail[n-1] is a valid Datalog rule. */ void check_valid_rule(app * head, unsigned n, app * const * tail) const; /** \brief Check that \c head may occur as a Datalog rule head. */ void check_valid_head(expr * head) const; /** \brief Return true if \c head may occur as a fact. */ bool is_fact(app * head) const; static bool is_forall(ast_manager& m, expr* e, quantifier*& q); rule_counter& get_counter() { return m_counter; } void to_formula(rule const& r, expr_ref& result); std::ostream& display_smt2(rule const& r, std::ostream & out); bool has_uninterpreted_non_predicates(rule const& r, func_decl*& f) const; void has_quantifiers(rule const& r, bool& existential, bool& universal) const; bool has_quantifiers(rule const& r) const; bool is_finite_domain(rule const& r) const; }; class rule : public accounted_object { friend class rule_manager; app * m_head; proof* m_proof; unsigned m_tail_size:20; // unsigned m_reserve:12; unsigned m_ref_cnt; unsigned m_positive_cnt; unsigned m_uninterp_cnt; symbol m_name; /** The following field is an array of tagged pointers. - Tag 0: the atom is not negated - Tag 1: the atom is negated. The order of tail formulas is the following: uninterpreted positive, uninterpreted negative, interpreted. The negated flag is never set for interpreted tails. */ app * m_tail[0]; static unsigned get_obj_size(unsigned n) { return sizeof(rule) + n * sizeof(app *); } rule() : m_ref_cnt(0), m_name(symbol::null) {} ~rule() {} void deallocate(ast_manager & m); void get_used_vars(used_vars& uv) const; public: proof * get_proof() const { return m_proof; } void set_proof(ast_manager& m, proof* p); app * get_head() const { return m_head; } func_decl* get_decl() const { return get_head()->get_decl(); } unsigned get_tail_size() const { return m_tail_size; } /** \brief Return number of positive uninterpreted predicates in the tail. These predicates are the first in the tail. */ unsigned get_positive_tail_size() const { return m_positive_cnt; } unsigned get_uninterpreted_tail_size() const { return m_uninterp_cnt; } /** \brief Return i-th tail atom. The first \c get_uninterpreted_tail_size() atoms are uninterpreted and the first \c get_positive_tail_size() are uninterpreted and non-negated. */ app * get_tail(unsigned i) const { SASSERT(i < m_tail_size); return UNTAG(app *, m_tail[i]); } func_decl* get_decl(unsigned i) const { SASSERT(i < get_uninterpreted_tail_size()); return get_tail(i)->get_decl(); } bool is_neg_tail(unsigned i) const { SASSERT(i < m_tail_size); return GET_TAG(m_tail[i]) == 1; } /** Check whether predicate p is in the interpreted tail. If only_positive is true, only the positive predicate tail atoms are checked. */ bool is_in_tail(const func_decl * p, bool only_positive=false) const; bool has_negation() const; /** \brief Store in d the (direct) dependencies of the given rule. */ void norm_vars(rule_manager & rm); void get_vars(ast_manager& m, ptr_vector& sorts) const; void display(context & ctx, std::ostream & out) const; /** \brief Return the name(s) associated with this rule. Plural for preprocessed (e.g. obtained by inlining) rules. This possibly returns a ";"-separated list of names. */ symbol const& name() const { return m_name; } ; unsigned hash() const; }; struct rule_eq_proc { bool operator()(const rule * r1, const rule * r2) const; }; struct rule_hash_proc { unsigned operator()(const rule * r) const; }; }; #endif /* DL_RULE_H_ */ z3-z3-4.8.7/src/muz/base/dl_rule_set.cpp000066400000000000000000000563401356505360400177600ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_rule_set.cpp Abstract: Author: Leonardo de Moura (leonardo) 2010-05-17. Revision History: --*/ #include #include #include "muz/base/dl_context.h" #include "muz/base/dl_rule_set.h" #include "ast/ast_pp.h" namespace datalog { rule_dependencies::rule_dependencies(context& ctx): m_context(ctx) { } rule_dependencies::rule_dependencies(const rule_dependencies & o, bool reversed): m_context(o.m_context) { if (reversed) { for (auto & kv : o) { func_decl * pred = kv.m_key; item_set & orig_items = *kv.get_value(); ensure_key(pred); for (func_decl * master_pred : orig_items) { insert(master_pred, pred); } } } else { for (auto & kv : o) { func_decl * pred = kv.m_key; item_set & orig_items = *kv.get_value(); m_data.insert(pred, alloc(item_set, orig_items)); } } } rule_dependencies::~rule_dependencies() { reset(); } void rule_dependencies::reset() { reset_dealloc_values(m_data); } void rule_dependencies::remove_m_data_entry(func_decl * key) { item_set * itm_set = m_data.find(key); dealloc(itm_set); m_data.remove(key); } rule_dependencies::item_set & rule_dependencies::ensure_key(func_decl * pred) { deps_type::obj_map_entry * e = m_data.insert_if_not_there2(pred, 0); if (!e->get_data().m_value) { e->get_data().m_value = alloc(item_set); } return *e->get_data().m_value; } void rule_dependencies::insert(func_decl * depending, func_decl * master) { SASSERT(m_data.contains(master)); //see m_data documentation item_set & s = ensure_key(depending); s.insert(master); } void rule_dependencies::populate(const rule_set & rules) { SASSERT(m_data.empty()); for (auto & kv : rules.m_head2rules) { ptr_vector * rules = kv.m_value; for (rule* r : *rules) { populate(r); } } } void rule_dependencies::populate(unsigned n, rule * const * rules) { SASSERT(m_data.empty()); for (unsigned i=0; iget_decl()->get_name() << "\n";); m_visited.reset(); func_decl * d = r->get_decl(); func_decl_set & s = ensure_key(d); for (unsigned i = 0; i < r->get_tail_size(); ++i) { m_todo.push_back(r->get_tail(i)); } while (!m_todo.empty()) { expr* e = m_todo.back(); m_todo.pop_back(); if (m_visited.is_marked(e)) { continue; } m_visited.mark(e, true); if (is_app(e)) { app* a = to_app(e); d = a->get_decl(); if (m_context.is_predicate(d)) { // insert d and ensure the invariant // that every predicate is present as // a key in m_data s.insert(d); ensure_key(d); } m_todo.append(a->get_num_args(), a->get_args()); } else if (is_quantifier(e)) { m_todo.push_back(to_quantifier(e)->get_expr()); } } } const rule_dependencies::item_set & rule_dependencies::get_deps(func_decl * f) const { deps_type::obj_map_entry * e = m_data.find_core(f); if (!e) { return m_empty_item_set; } SASSERT(e->get_data().get_value()); return *e->get_data().get_value(); } void rule_dependencies::restrict(const item_set & allowed) { ptr_vector to_remove; for (auto const& kv : *this) { func_decl * pred = kv.m_key; if (!allowed.contains(pred)) { to_remove.insert(pred); continue; } item_set& itms = *kv.get_value(); set_intersection(itms, allowed); } for (func_decl* f : to_remove) remove_m_data_entry(f); } void rule_dependencies::remove(func_decl * itm) { remove_m_data_entry(itm); for (auto const& kv : *this) { item_set & itms = *kv.get_value(); itms.remove(itm); } } void rule_dependencies::remove(const item_set & to_remove) { for (auto * item : to_remove) { remove_m_data_entry(item); } for (auto & kv : *this) { item_set * itms = kv.get_value(); set_difference(*itms, to_remove); } } unsigned rule_dependencies::out_degree(func_decl * f) const { unsigned res = 0; for (auto & kv : *this) { item_set & itms = *kv.get_value(); if (itms.contains(f)) { res++; } } return res; } bool rule_dependencies::sort_deps(ptr_vector & res) { typedef obj_map deg_map; unsigned init_len = res.size(); deg_map degs; unsigned curr_index = init_len; rule_dependencies reversed(*this, true); iterator pit = begin(); iterator pend = end(); for (; pit!=pend; ++pit) { func_decl * pred = pit->m_key; unsigned deg = in_degree(pred); if (deg==0) { res.push_back(pred); } else { degs.insert(pred, deg); } } while (curr_indexget_data().m_value; SASSERT(child_deg>0); child_deg--; if (child_deg==0) { res.push_back(child); } } curr_index++; } if (res.size() < init_len + m_data.size()) { res.shrink(init_len); return false; } SASSERT(res.size()==init_len+m_data.size()); return true; } void rule_dependencies::display(std::ostream & out ) const { iterator pit = begin(); iterator pend = end(); for (; pit != pend; ++pit) { func_decl * pred = pit->m_key; const item_set & deps = *pit->m_value; item_set::iterator dit=deps.begin(); item_set::iterator dend=deps.end(); if (dit == dend) { out<get_name()<<" - \n"; } for (; dit != dend; ++dit) { func_decl * dep = *dit; out << pred->get_name() << " -> " << dep->get_name() << "\n"; } } } // ----------------------------------- // // rule_set // // ----------------------------------- rule_set::rule_set(context & ctx) : m_context(ctx), m_rule_manager(ctx.get_rule_manager()), m_rules(m_rule_manager), m_deps(ctx), m_stratifier(nullptr), m_refs(ctx.get_manager()) { } rule_set::rule_set(const rule_set & other) : m_context(other.m_context), m_rule_manager(other.m_rule_manager), m_rules(m_rule_manager), m_deps(other.m_context), m_stratifier(nullptr), m_refs(m_context.get_manager()) { add_rules(other); if (other.m_stratifier) { VERIFY(close()); } } rule_set::~rule_set() { reset(); } void rule_set::reset() { m_rules.reset(); reset_dealloc_values(m_head2rules); m_deps.reset(); m_stratifier = nullptr; m_output_preds.reset(); m_orig2pred.reset(); m_pred2orig.reset(); m_refs.reset(); } ast_manager & rule_set::get_manager() const { return m_context.get_manager(); } func_decl* rule_set::get_orig(func_decl* pred) const { func_decl* orig = pred; m_pred2orig.find(pred, orig); return orig; } func_decl* rule_set::get_pred(func_decl* orig) const { func_decl* pred = orig; m_orig2pred.find(orig, pred); return pred; } void rule_set::inherit_predicates(rule_set const& other) { m_refs.append(other.m_refs); set_union(m_output_preds, other.m_output_preds); for (auto & kv : other.m_orig2pred) { m_orig2pred.insert(kv.m_key, kv.m_value); } for (auto & kv : other.m_pred2orig) { m_pred2orig.insert(kv.m_key, kv.m_value); } } void rule_set::inherit_predicate(rule_set const& other, func_decl* orig, func_decl* pred) { if (other.is_output_predicate(orig)) { set_output_predicate(pred); } orig = other.get_orig(orig); m_refs.push_back(pred); m_refs.push_back(orig); m_orig2pred.insert(orig, pred); m_pred2orig.insert(pred, orig); } void rule_set::add_rule(rule * r) { TRACE("dl_verbose", r->display(m_context, tout << "add:");); SASSERT(!is_closed()); m_rules.push_back(r); app * head = r->get_head(); SASSERT(head != 0); func_decl * d = head->get_decl(); decl2rules::obj_map_entry* e = m_head2rules.insert_if_not_there2(d, 0); if (!e->get_data().m_value) e->get_data().m_value = alloc(ptr_vector); e->get_data().m_value->push_back(r); } void rule_set::del_rule(rule * r) { TRACE("dl", r->display(m_context, tout << "del:");); func_decl* d = r->get_decl(); rule_vector* rules = m_head2rules.find(d); #define DEL_VECTOR(_v) \ for (unsigned i = (_v).size(); i > 0; ) { \ --i; \ if ((_v)[i] == r) { \ (_v)[i] = (_v).back(); \ (_v).pop_back(); \ break; \ } \ } \ DEL_VECTOR(*rules); DEL_VECTOR(m_rules); } void rule_set::replace_rule(rule * r, rule * other) { TRACE("dl", r->display(m_context, tout << "replace:");); func_decl* d = r->get_decl(); rule_vector* rules = m_head2rules.find(d); #define REPLACE_VECTOR(_v) \ for (unsigned i = (_v).size(); i > 0; ) { \ --i; \ if ((_v)[i] == r) { \ (_v)[i] = other; \ break; \ } \ } \ REPLACE_VECTOR(*rules); REPLACE_VECTOR(m_rules); } void rule_set::ensure_closed() { if (!is_closed()) { VERIFY(close()); } } bool rule_set::close() { SASSERT(!is_closed()); //the rule_set is not already closed m_deps.populate(*this); m_stratifier = alloc(rule_stratifier, m_deps); if (!stratified_negation()) { m_stratifier = nullptr; m_deps.reset(); return false; } return true; } void rule_set::reopen() { if (is_closed()) { m_stratifier = nullptr; m_deps.reset(); } } /** \brief Return true if the negation is indeed stratified. */ bool rule_set::stratified_negation() { ptr_vector::const_iterator it = m_rules.c_ptr(); ptr_vector::const_iterator end = m_rules.c_ptr()+m_rules.size(); for (; it != end; it++) { rule * r = *it; app * head = r->get_head(); func_decl * head_decl = head->get_decl(); unsigned n = r->get_uninterpreted_tail_size(); for (unsigned i = r->get_positive_tail_size(); i < n; i++) { SASSERT(r->is_neg_tail(i)); func_decl * tail_decl = r->get_tail(i)->get_decl(); unsigned neg_strat = get_predicate_strat(tail_decl); unsigned head_strat = get_predicate_strat(head_decl); SASSERT(head_strat>=neg_strat); //head strat can never be lower than that of a tail if (head_strat == neg_strat) { return false; } } } return true; } void rule_set::replace_rules(const rule_set & src) { if (this != &src) { reset(); add_rules(src); } } void rule_set::add_rules(const rule_set & src) { SASSERT(!is_closed()); unsigned n = src.get_num_rules(); for (unsigned i = 0; i < n; i++) { add_rule(src.get_rule(i)); } inherit_predicates(src); } const rule_vector & rule_set::get_predicate_rules(func_decl * pred) const { decl2rules::obj_map_entry * e = m_head2rules.find_core(pred); if (!e) { return m_empty_rule_vector; } return *e->get_data().m_value; } const rule_set::pred_set_vector & rule_set::get_strats() const { SASSERT(m_stratifier); return m_stratifier->get_strats(); } unsigned rule_set::get_predicate_strat(func_decl * pred) const { SASSERT(m_stratifier); return m_stratifier->get_predicate_strat(pred); } void rule_set::split_founded_rules(func_decl_set& founded, func_decl_set& non_founded) { founded.reset(); non_founded.reset(); { decl2rules::iterator it = begin_grouped_rules(), end = end_grouped_rules(); for (; it != end; ++it) { non_founded.insert(it->m_key); } } bool change = true; while (change) { change = false; func_decl_set::iterator it = non_founded.begin(), end = non_founded.end(); for (; it != end; ++it) { rule_vector const& rv = get_predicate_rules(*it); bool found = false; for (unsigned i = 0; !found && i < rv.size(); ++i) { rule const& r = *rv[i]; bool is_founded = true; for (unsigned j = 0; is_founded && j < r.get_uninterpreted_tail_size(); ++j) { is_founded = founded.contains(r.get_decl(j)); } if (is_founded) { founded.insert(*it); non_founded.remove(*it); change = true; found = true; } } } } } void rule_set::display(std::ostream & out) const { out << "; rule count: " << get_num_rules() << "\n"; out << "; predicate count: " << m_head2rules.size() << "\n"; func_decl_set::iterator pit = m_output_preds.begin(); func_decl_set::iterator pend = m_output_preds.end(); for (; pit != pend; ++pit) { out << "; output: " << (*pit)->get_name() << '\n'; } decl2rules::iterator it = m_head2rules.begin(); decl2rules::iterator end = m_head2rules.end(); for (; it != end; ++it) { ptr_vector * rules = it->m_value; ptr_vector::iterator it2 = rules->begin(); ptr_vector::iterator end2 = rules->end(); for (; it2 != end2; ++it2) { rule * r = *it2; if (!r->passes_output_thresholds(m_context)) { continue; } r->display(m_context, out); } } } bool rule_set::is_finite_domain() const { for (rule * r : *this) { if (!get_rule_manager().is_finite_domain(*r)) return false; } return true; } void rule_set::display_deps( std::ostream & out ) const { const pred_set_vector & strats = get_strats(); bool non_empty = false; for (func_decl_set* strat : strats) { if (non_empty) { out << "\n"; non_empty = false; } for (func_decl * first : *strat) { const func_decl_set & deps = m_deps.get_deps(first); for (func_decl * dep : deps) { non_empty = true; out<get_name()<<" -> " <get_name()<<"\n"; } } } } // ----------------------------------- // // rule_stratifier // // ----------------------------------- rule_stratifier::~rule_stratifier() { for (auto * t : m_strats) { dealloc(t); } } unsigned rule_stratifier::get_predicate_strat(func_decl * pred) const { unsigned num; if (!m_pred_strat_nums.find(pred, num)) { //the number of the predicate is not stored, therefore it did not appear //in the algorithm and therefore it does not depend on anything and nothing //depends on it. So it is safe to assign zero strate to it, although it is //not strictly true. num = 0; } return num; } void rule_stratifier::traverse(T* el) { unsigned p_num; if (m_preorder_nums.find(el, p_num)) { if (p_num < m_first_preorder) { //traversed in a previous sweep return; } if (m_component_nums.contains(el)) { //we already assigned a component for el return; } while (!m_stack_P.empty()) { unsigned on_stack_num = 0; VERIFY( m_preorder_nums.find(m_stack_P.back(), on_stack_num) ); if (on_stack_num <= p_num) { break; } m_stack_P.pop_back(); } } else { p_num=m_next_preorder++; m_preorder_nums.insert(el, p_num); m_stack_S.push_back(el); m_stack_P.push_back(el); const item_set & children = m_deps.get_deps(el); for (T* ch : children) { traverse(ch); } if (el == m_stack_P.back()) { unsigned comp_num = m_components.size(); item_set * new_comp = alloc(item_set); m_components.push_back(new_comp); T* s_el; do { s_el=m_stack_S.back(); m_stack_S.pop_back(); new_comp->insert(s_el); m_component_nums.insert(s_el, comp_num); } while (s_el!=el); m_stack_P.pop_back(); } } } void rule_stratifier::process() { if (m_deps.empty()) { return; } //detect strong components for (auto const& kv : m_deps) { T * el = kv.m_key; //we take a note of the preorder number with which this sweep started m_first_preorder = m_next_preorder; traverse(el); } //do topological sorting //degres of components (number of inter-component edges ending up in the component) svector in_degrees; in_degrees.resize(m_components.size()); //init in_degrees for (auto const& kv : m_deps) { T * el = kv.m_key; item_set * out_edges = kv.m_value; unsigned el_comp = m_component_nums[el]; for (T * tgt : *out_edges) { unsigned tgt_comp = m_component_nums.find(tgt); if (el_comp != tgt_comp) { in_degrees[tgt_comp]++; } } } // We put components whose indegree is zero to m_strats and assign its // m_components entry to zero. unsigned comp_cnt = m_components.size(); for (unsigned i = 0; i < comp_cnt; i++) { if (in_degrees[i] == 0) { m_strats.push_back(m_components[i]); m_components[i] = 0; } } SASSERT(!m_strats.empty()); //the component graph is acyclic and non-empty //we remove edges from components with zero indegre building the topological ordering unsigned strats_index = 0; while (strats_index < m_strats.size()) { //m_strats.size() changes inside the loop! item_set * comp = m_strats[strats_index]; for (T * el : *comp) { const item_set & deps = m_deps.get_deps(el); for (T * tgt : deps) { unsigned tgt_comp = 0; VERIFY( m_component_nums.find(tgt, tgt_comp) ); //m_components[tgt_comp]==0 means the edge is intra-component. //Otherwise it would go to another component, but it is not possible, since //as m_components[tgt_comp]==0, its indegree has already reached zero. if (m_components[tgt_comp]) { SASSERT(in_degrees[tgt_comp]>0); in_degrees[tgt_comp]--; if (in_degrees[tgt_comp]==0) { m_strats.push_back(m_components[tgt_comp]); m_components[tgt_comp] = 0; } } traverse(el); } } strats_index++; } //we have managed to topologicaly order all the components //reverse the strats array, so that the only the later components would depend on earlier ones std::reverse(m_strats.begin(), m_strats.end()); SASSERT(m_pred_strat_nums.empty()); unsigned strat_cnt = m_strats.size(); for (unsigned strat_index=0; strat_index < strat_cnt; strat_index++) { item_set * comp = m_strats[strat_index]; for (T * el : *comp) { m_pred_strat_nums.insert(el, strat_index); } } //finalize structures that are not needed anymore m_preorder_nums.finalize(); m_stack_S.finalize(); m_stack_P.finalize(); m_component_nums.finalize(); m_components.finalize(); } void rule_stratifier::display(std::ostream& out) const { m_deps.display(out << "dependencies\n"); out << "strata\n"; for (unsigned i = 0; i < m_strats.size(); ++i) { for (auto * item : *m_strats[i]) { out << item->get_name() << " "; } out << "\n"; } } }; z3-z3-4.8.7/src/muz/base/dl_rule_set.h000066400000000000000000000221551356505360400174220ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_rule_set.h Abstract: Author: Leonardo de Moura (leonardo) 2010-05-17. Revision History: --*/ #ifndef DL_RULE_SET_H_ #define DL_RULE_SET_H_ #include "util/obj_hashtable.h" #include "muz/base/dl_rule.h" namespace datalog { class rule_set; class rule_dependencies { public: typedef obj_hashtable item_set; typedef obj_map deps_type; typedef deps_type::iterator iterator; private: /** Map (dependent -> set of master objects) Each master object is also present as a key of the map, even if its master set is empty. */ deps_type m_data; context & m_context; ptr_vector m_todo; expr_sparse_mark m_visited; //we need to take care with removing to avoid memory leaks void remove_m_data_entry(func_decl * key); //sometimes we need to return reference to an empty set, //so we return reference to this one. item_set m_empty_item_set; item_set & ensure_key(func_decl * pred); void insert(func_decl * depending, func_decl * master); void populate(rule const* r); public: rule_dependencies(context& ctx); rule_dependencies(const rule_dependencies & o, bool reversed = false); ~rule_dependencies(); void reset(); void populate(const rule_set & rules); void populate(unsigned n, rule * const * rules); void restrict(const item_set & allowed); void remove(func_decl * itm); void remove(const item_set & to_remove); bool empty() const { return m_data.empty(); } const item_set & get_deps(func_decl * f) const; /** \brief Number of predicates \c f depends on. */ unsigned in_degree(func_decl * f) const { return get_deps(f).size(); } /** \brief Number of predicates that depend on \c f. */ unsigned out_degree(func_decl * f) const; /** \brief If the rependency graph is acyclic, put all elements into \c res ordered so that elements can depend only on elements that are before them. If the graph is not acyclic, return false. */ bool sort_deps(ptr_vector & res); iterator begin() const { return m_data.begin(); } iterator end() const { return m_data.end(); } void display( std::ostream & out ) const; }; class rule_stratifier { public: typedef func_decl T; typedef obj_hashtable item_set; typedef ptr_vector comp_vector; typedef obj_map deps_type; private: const rule_dependencies & m_deps; comp_vector m_strats; obj_map m_preorder_nums; ptr_vector m_stack_S; ptr_vector m_stack_P; obj_map m_component_nums; comp_vector m_components; obj_map m_pred_strat_nums; unsigned m_next_preorder; unsigned m_first_preorder; /** Finds strongly connected components using the Gabow's algorithm. */ void traverse(T* el); /** Calls \c traverse to identify strognly connected components and then orders them using topological sorting. */ void process(); public: /** \remark The \c stratifier object keeps a reference to the \c deps object, so it must exist for the whole lifetime of the \c stratifier object. */ rule_stratifier(const rule_dependencies & deps) : m_deps(deps), m_next_preorder(0) { process(); } ~rule_stratifier(); /** Return vector of components ordered so that the only dependencies are from later components to earlier. */ const comp_vector & get_strats() const { return m_strats; } unsigned get_predicate_strat(func_decl * pred) const; void display( std::ostream & out ) const; }; /** \brief Datalog "Program" (aka rule set). */ class rule_set { friend class rule_dependencies; public: typedef ptr_vector pred_set_vector; typedef obj_map decl2rules; private: typedef obj_map decl2deps; context & m_context; rule_manager & m_rule_manager; rule_ref_vector m_rules; //!< all rules decl2rules m_head2rules; //!< mapping from head symbol to rules. rule_dependencies m_deps; //!< dependencies scoped_ptr m_stratifier; //!< contains stratifier object iff the rule_set is closed func_decl_set m_output_preds; //!< output predicates obj_map m_orig2pred; obj_map m_pred2orig; func_decl_ref_vector m_refs; //sometimes we need to return reference to an empty rule_vector, //so we return reference to this one. rule_vector m_empty_rule_vector; void compute_deps(); void compute_tc_deps(); bool stratified_negation(); public: rule_set(context & ctx); rule_set(const rule_set & rs); ~rule_set(); ast_manager & get_manager() const; rule_manager & get_rule_manager() const { return const_cast(m_rule_manager); } context& get_context() const { return m_context; } void inherit_predicates(rule_set const& other); void inherit_predicate(rule_set const& other, func_decl* orig, func_decl* pred); func_decl* get_orig(func_decl* pred) const; func_decl* get_pred(func_decl* orig) const; /** \brief Add rule \c r to the rule set. */ void add_rule(rule * r); /** \brief Remove rule \c r from the rule set. */ void del_rule(rule * r); /** \brief Replace a rule \c r with the rule \c other */ void replace_rule(rule * r, rule * other); /** \brief Add all rules from a different rule_set. */ void add_rules(const rule_set& src); void replace_rules(const rule_set& other); /** \brief This method should be invoked after all rules are added to the rule set. It will check if the negation is indeed stratified. Return true if succeeded. \remark If new rules are added, the rule_set will be "reopen". */ bool close(); void ensure_closed(); /** \brief Undo the effect of the \c close() operation. */ void reopen(); bool is_closed() const { return m_stratifier != 0; } unsigned get_num_rules() const { return m_rules.size(); } 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]; } rule_ref_vector const& get_rules() const { return m_rules; } const rule_vector & get_predicate_rules(func_decl * pred) const; bool contains(func_decl* pred) const { return m_head2rules.contains(pred); } const rule_stratifier & get_stratifier() const { SASSERT(m_stratifier); return *m_stratifier; } const pred_set_vector & get_strats() const; unsigned get_predicate_strat(func_decl * pred) const; const rule_dependencies & get_dependencies() const { SASSERT(is_closed()); return m_deps; } // split predicats into founded and non-founded. void split_founded_rules(func_decl_set& founded, func_decl_set& non_founded); void reset(); void set_output_predicate(func_decl * pred) { m_refs.push_back(pred); m_output_preds.insert(pred); } bool is_output_predicate(func_decl * pred) const { return m_output_preds.contains(pred); } const func_decl_set & get_output_predicates() const { return m_output_preds; } func_decl* get_output_predicate() const { SASSERT(m_output_preds.size() == 1); return *m_output_preds.begin(); } bool is_finite_domain() const; void display(std::ostream & out) const; /** \brief Output rule dependencies. The rule set must be closed before calling this function. */ void display_deps(std::ostream & out) const; typedef rule * const * iterator; iterator begin() const { return m_rules.c_ptr(); } iterator end() const { return m_rules.c_ptr()+m_rules.size(); } decl2rules::iterator begin_grouped_rules() const { return m_head2rules.begin(); } decl2rules::iterator end_grouped_rules() const { return m_head2rules.end(); } }; inline std::ostream& operator<<(std::ostream& out, rule_set const& r) { r.display(out); return out; } }; #endif /* DL_RULE_SET_H_ */ z3-z3-4.8.7/src/muz/base/dl_rule_subsumption_index.cpp000066400000000000000000000033511356505360400227360ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_rule_subsumption_index.cpp Abstract: Subsumption index for rules. Currently an underapproximation (fails to identify some subsumptions). Author: Krystof Hoder (t-khoder) 2011-10-10. Revision History: --*/ #include #include "ast/ast_pp.h" #include "muz/base/dl_rule_subsumption_index.h" namespace datalog { // ----------------------------------- // // rule_subsumption_index // // ----------------------------------- void rule_subsumption_index::handle_unconditioned_rule(rule * r) { SASSERT(r->get_tail_size()==0); app * head = r->get_head(); func_decl * pred = head->get_decl(); app_set * head_set; if(!m_unconditioned_heads.find(pred, head_set)) { head_set = alloc(app_set); m_unconditioned_heads.insert(pred, head_set); } head_set->insert(head); } void rule_subsumption_index::add(rule * r) { m_ref_holder.push_back(r); if(r->get_tail_size()==0) { handle_unconditioned_rule(r); } m_rule_set.insert(r); } bool rule_subsumption_index::is_subsumed(app * query) { func_decl * pred = query->get_decl(); app_set * head_set; if(m_unconditioned_heads.find(pred, head_set)) { if(head_set->contains(query)) { return true; } } return false; } bool rule_subsumption_index::is_subsumed(rule * r) { app * head = r->get_head(); if(is_subsumed(head)) { return true; } if(m_rule_set.contains(r)) { return true; } return false; } }; z3-z3-4.8.7/src/muz/base/dl_rule_subsumption_index.h000066400000000000000000000026421356505360400224050ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_rule_subsumption_index.h Abstract: Subsumption index for rules. Currently an underapproximation (fails to identify some subsumptions). Author: Krystof Hoder (t-khoder) 2011-10-10. Revision History: --*/ #ifndef DL_RULE_SUBSUMPTION_INDEX_H_ #define DL_RULE_SUBSUMPTION_INDEX_H_ #include "muz/base/dl_context.h" namespace datalog { class rule_subsumption_index { //private and undefined copy constroctor rule_subsumption_index(rule_subsumption_index const&); //private and undefined operator= rule_subsumption_index& operator=(rule_subsumption_index const&); typedef obj_hashtable app_set; ast_manager & m; context & m_context; rule_ref_vector m_ref_holder; obj_map m_unconditioned_heads; hashtable m_rule_set; void handle_unconditioned_rule(rule * r); public: rule_subsumption_index(context & ctx) : m(ctx.get_manager()), m_context(ctx), m_ref_holder(ctx.get_rule_manager()) {} ~rule_subsumption_index() { reset_dealloc_values(m_unconditioned_heads); } void add(rule * r); bool is_subsumed(rule * r); bool is_subsumed(app * query); }; }; #endif /* DL_RULE_SUBSUMPTION_INDEX_H_ */ z3-z3-4.8.7/src/muz/base/dl_rule_transformer.cpp000066400000000000000000000104361356505360400215230ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_rule_transformer.cpp Abstract: Author: Krystof Hoder (t-khoder) 2010-09-20. Revision History: --*/ #include #include #include "muz/base/dl_context.h" #include "muz/base/dl_rule_transformer.h" #include "util/stopwatch.h" namespace datalog { rule_transformer::rule_transformer(context & ctx) : m_context(ctx), m_rule_manager(m_context.get_rule_manager()), m_dirty(false) { } rule_transformer::~rule_transformer() { reset(); } void rule_transformer::reset() { plugin_vector::iterator it = m_plugins.begin(); plugin_vector::iterator end = m_plugins.end(); for(; it!=end; ++it) { dealloc(*it); } m_plugins.reset(); m_dirty = false; } void rule_transformer::cancel() { plugin_vector::iterator it = m_plugins.begin(); plugin_vector::iterator end = m_plugins.end(); for(; it!=end; ++it) { (*it)->cancel(); } } struct rule_transformer::plugin_comparator { bool operator()(rule_transformer::plugin * p1, rule_transformer::plugin * p2) { return p1->get_priority() > p2->get_priority(); } }; void rule_transformer::ensure_ordered() { if (m_dirty) { std::sort(m_plugins.begin(), m_plugins.end(), plugin_comparator()); m_dirty = false; } } void rule_transformer::register_plugin(plugin * p) { m_plugins.push_back(p); p->attach(*this); m_dirty=true; } bool rule_transformer::operator()(rule_set & rules) { ensure_ordered(); bool modified = false; TRACE("dl_rule_transf", tout<<"init:\n"; rules.display(tout); ); rule_set* new_rules = alloc(rule_set, rules); plugin_vector::iterator it = m_plugins.begin(); plugin_vector::iterator end = m_plugins.end(); for(; it!=end && !m_context.canceled(); ++it) { plugin & p = **it; IF_VERBOSE(1, verbose_stream() << "(transform " << typeid(p).name() << "...";); stopwatch sw; sw.start(); rule_set * new_rules1 = p(*new_rules); sw.stop(); double sec = sw.get_seconds(); if (sec < 0.001) sec = 0.0; if (!new_rules1) { IF_VERBOSE(1, verbose_stream() << "no-op " << sec << "s)\n";); continue; } if (p.can_destratify_negation() && !new_rules1->is_closed() && !new_rules1->close()) { warning_msg("a rule transformation skipped " "because it destratified negation"); dealloc(new_rules1); IF_VERBOSE(1, verbose_stream() << "no-op " << sec << "s)\n";); continue; } modified = true; dealloc(new_rules); new_rules = new_rules1; new_rules->ensure_closed(); IF_VERBOSE(1, verbose_stream() << new_rules->get_num_rules() << " rules " << sec << "s)\n";); TRACE("dl_rule_transf", tout << typeid(p).name()<<":\n"; new_rules->display(tout); ); } if (modified) { rules.replace_rules(*new_rules); } dealloc(new_rules); return modified; } //------------------------------ // //rule_transformer::plugin // //------------------------------ void rule_transformer::plugin::remove_duplicate_tails(app_ref_vector& tail, svector& tail_neg) { //one set for positive and one for negative obj_hashtable tail_apps[2]; unsigned i=0; while(i Author: Krystof Hoder (t-khoder) 2010-09-20. Revision History: --*/ #ifndef DL_RULE_TRANSFORMER_H_ #define DL_RULE_TRANSFORMER_H_ #include "util/map.h" #include "util/vector.h" #include "muz/base/dl_rule.h" #include "muz/base/dl_rule_set.h" namespace datalog { class context; class rule_transformer { public: class plugin; private: friend class plugin; typedef svector plugin_vector; struct plugin_comparator; context & m_context; rule_manager & m_rule_manager; bool m_dirty; svector m_plugins; void ensure_ordered(); public: rule_transformer(context & ctx); ~rule_transformer(); /** \brief Reset all registered transformers. */ void reset(); void cancel(); /** \brief Add a plugin for rule transformation. The rule_transformer object takes ownership of the plugin object. */ void register_plugin(plugin * p); /** \brief Transform the rule set using the registered transformation plugins. If the rule set has changed, return true; otherwise return false. */ bool operator()(rule_set & rules); }; class rule_transformer::plugin { friend class rule_transformer; unsigned m_priority; bool m_can_destratify_negation; rule_transformer * m_transformer; void attach(rule_transformer & transformer) { m_transformer = &transformer; } protected: /** \brief Create a plugin object for rule_transformer. The priority argument determines in what order will rules be applied to the rules (higher priority plugins will be applied first). */ plugin(unsigned priority, bool can_destratify_negation = false) : m_priority(priority), m_can_destratify_negation(can_destratify_negation), m_transformer(nullptr) {} public: virtual ~plugin() {} unsigned get_priority() { return m_priority; } bool can_destratify_negation() const { return m_can_destratify_negation; } virtual void cancel() {} /** \brief Return \c rule_set object with containing transformed rules or 0 if no transformation was done. The caller takes ownership of the returned \c rule_set object. */ virtual rule_set * operator()(rule_set const & source) = 0; /** Removes duplicate tails. */ static void remove_duplicate_tails(app_ref_vector& tail, svector& tail_neg); }; }; #endif /* DL_RULE_TRANSFORMER_H_ */ z3-z3-4.8.7/src/muz/base/dl_util.cpp000066400000000000000000000513541356505360400171130ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_util.cpp Abstract: Author: Leonardo de Moura (leonardo) 2010-05-20. Revision History: --*/ #include #include #include #ifdef _WINDOWS #include #endif #include "ast/ast_pp.h" #include "ast/rewriter/bool_rewriter.h" #include "ast/for_each_expr.h" #include "ast/scoped_proof.h" #include "muz/base/dl_context.h" #include "muz/base/dl_rule.h" #include "muz/base/dl_util.h" #include "util/stopwatch.h" #ifndef __STDC_FORMAT_MACROS #define __STDC_FORMAT_MACROS #endif #include namespace datalog { verbose_action::verbose_action(char const* msg, unsigned lvl): m_lvl(lvl), m_sw(nullptr) { IF_VERBOSE(m_lvl, (verbose_stream() << msg << "...").flush(); m_sw = alloc(stopwatch); m_sw->start();); } verbose_action::~verbose_action() { double sec = 0.0; if (m_sw) m_sw->stop(); sec = m_sw?m_sw->get_seconds():0.0; if (sec < 0.001) sec = 0.0; IF_VERBOSE(m_lvl, (verbose_stream() << sec << "s\n").flush(); ); dealloc(m_sw); } bool contains_var(expr * trm, unsigned var_idx) { expr_free_vars fv; fv(trm); return fv.contains(var_idx); } unsigned count_variable_arguments(app * pred) { SASSERT(is_uninterp(pred)); unsigned res = 0; unsigned n = pred->get_num_args(); for (unsigned i = 0; i < n; i++) { expr * arg = pred->get_arg(i); if (is_var(arg)) { res++; } } return res; } void mk_new_rule_tail(ast_manager & m, app * pred, var_idx_set const & non_local_vars, unsigned & next_idx, varidx2var_map & varidx2var, sort_ref_buffer & new_rule_domain, expr_ref_buffer & new_rule_args, app_ref & new_pred) { expr_ref_buffer new_args(m); unsigned n = pred->get_num_args(); for (unsigned i = 0; i < n; i++) { expr * arg = pred->get_arg(i); if (m.is_value(arg)) { new_args.push_back(arg); } else { SASSERT(is_var(arg)); int vidx = to_var(arg)->get_idx(); var * new_var = nullptr; if (!varidx2var.find(vidx, new_var)) { new_var = m.mk_var(next_idx, to_var(arg)->get_sort()); next_idx++; varidx2var.insert(vidx, new_var); if (non_local_vars.contains(vidx)) { // other predicates used this variable... so it should be in the domain of the filter new_rule_domain.push_back(to_var(arg)->get_sort()); new_rule_args.push_back(new_var); } } SASSERT(new_var != 0); new_args.push_back(new_var); } } new_pred = m.mk_app(pred->get_decl(), new_args.size(), new_args.c_ptr()); } void apply_subst(expr_ref_vector& tgt, expr_ref_vector const& sub) { ast_manager& m = tgt.get_manager(); var_subst vs(m, false); expr_ref tmp(m); for (unsigned i = 0; i < tgt.size(); ++i) { if (tgt[i].get()) { tgt[i] = vs(tgt[i].get(), sub.size(), sub.c_ptr()); } else { tgt[i] = sub[i]; } } for (unsigned i = tgt.size(); i < sub.size(); ++i) { tgt.push_back(sub[i]); } } void output_predicate(context & ctx, app * f, std::ostream & out) { func_decl * pred_decl = f->get_decl(); unsigned arity = f->get_num_args(); out << pred_decl->get_name() << '('; for (unsigned i = 0; i < arity; i++) { expr * arg = f->get_arg(i); if (i != 0) { out << ','; } if (is_var(arg)) { out << "#" << to_var(arg)->get_idx(); } else { out << mk_pp(arg, ctx.get_manager()); } } out << ")"; } void display_predicate(context & ctx, app * f, std::ostream & out) { output_predicate(ctx, f, out); out << "\n"; } void display_fact(context & ctx, app * f, std::ostream & out) { func_decl * pred_decl = f->get_decl(); unsigned arity = f->get_num_args(); out << "\t("; for(unsigned i = 0; i < arity; i++) { if (i != 0) { out << ','; } expr * arg = f->get_arg(i); uint64_t sym_num; SASSERT(is_app(arg)); VERIFY( ctx.get_decl_util().is_numeral_ext(to_app(arg), sym_num) ); relation_sort sort = pred_decl->get_domain(i); out << ctx.get_argument_name(pred_decl, i) << '='; ctx.print_constant_name(sort, sym_num, out); out << '(' << sym_num << ')'; } out << ")\n"; } void idx_set_union(idx_set & tgt, const idx_set & src) { idx_set::iterator vit = src.begin(); idx_set::iterator vend = src.end(); for(;vit!=vend;++vit) { tgt.insert(*vit); } } bool variable_intersection::values_match(const expr * v1, const expr * v2) { //return !m_manager.are_distinct(v1, v2); return v1==v2; } bool variable_intersection::args_match(const app * f1, const app * f2) { unsigned n=size(); for (unsigned i = 0; i < n; i++) { unsigned f1_index, f2_index; get(i, f1_index, f2_index); if (!values_match(f1->get_arg(f1_index),f2->get_arg(f2_index))) { return false; } } return true; } bool variable_intersection::args_self_match(const app * f) { if(!args_match(f,f)) { return false; } unsigned n = m_const_indexes.size(); for(unsigned i=0; iget_arg(f_index), m_consts[i].get())) { return false; } } return true; } void variable_intersection::populate_self(const app * a) { SASSERT(is_uninterp(a)); //TODO: optimize quadratic complexity //TODO: optimize number of checks when variable occurs multiple times unsigned arity = a->get_num_args(); for(unsigned i1=0; i1get_arg(i1); if(is_var(e1)) { var* v1=to_var(e1); for(unsigned i2=i1+1; i2get_arg(i2); if(!is_var(e2)) { continue; } var* v2=to_var(e2); if(v1->get_idx()==v2->get_idx()) { add_pair(i1, i2); } } } else { SASSERT(is_app(e1)); app * c1 = to_app(e1); SASSERT(c1->get_num_args()==0); //c1 must be a constant m_const_indexes.push_back(i1); m_consts.push_back(c1); SASSERT(m_const_indexes.size()==m_consts.size()); } } } void rule_counter::count_rule_vars(const rule * r, int coef) { reset(); count_vars(r->get_head(), 1); unsigned n = r->get_tail_size(); for (unsigned i = 0; i < n; i++) { count_vars(r->get_tail(i), coef); } } unsigned rule_counter::get_max_rule_var(const rule & r) { m_todo.push_back(r.get_head()); m_scopes.push_back(0); unsigned n = r.get_tail_size(); bool has_var = false; for (unsigned i = 0; i < n; i++) { m_todo.push_back(r.get_tail(i)); m_scopes.push_back(0); } return get_max_var(has_var); } void del_rule(horn_subsume_model_converter* mc, rule& r, bool unreachable) { if (mc) { ast_manager& m = mc->get_manager(); expr_ref_vector body(m); if (unreachable) { body.push_back(m.mk_false()); } else { for (unsigned i = 0; i < r.get_tail_size(); ++i) { if (r.is_neg_tail(i)) { body.push_back(m.mk_not(r.get_tail(i))); } else { body.push_back(r.get_tail(i)); } } } TRACE("dl_dr", tout << mk_pp(r.get_head(), m) << " :- \n"; for (unsigned i = 0; i < body.size(); ++i) { tout << mk_pp(body[i].get(), m) << "\n"; }); mc->insert(r.get_head(), body.size(), body.c_ptr()); } } void resolve_rule(rule_manager& rm, replace_proof_converter* pc, rule const& r1, rule const& r2, unsigned idx, expr_ref_vector const& s1, expr_ref_vector const& s2, rule const& res) { if (!pc) return; ast_manager& m = s1.get_manager(); expr_ref fml1(m), fml2(m), fml3(m); rm.to_formula(r1, fml1); rm.to_formula(r2, fml2); rm.to_formula(res, fml3); vector substs; svector > positions; substs.push_back(s1); substs.push_back(s2); scoped_proof _sc(m); proof_ref pr(m); proof_ref_vector premises(m); premises.push_back(m.mk_asserted(fml1)); premises.push_back(m.mk_asserted(fml2)); positions.push_back(std::make_pair(idx+1, 0)); TRACE("dl", tout << premises[0]->get_id() << " " << mk_pp(premises[0].get(), m) << "\n"; for (unsigned i = 0; i < s1.size(); ++i) { tout << mk_pp(s1[i], m) << " "; } tout << "\n"; tout << premises[1]->get_id() << " " << mk_pp(premises[1].get(), m) << "\n"; for (unsigned i = 0; i < s2.size(); ++i) { tout << mk_pp(s2[i], m) << " "; } tout << "\n"; ); pr = m.mk_hyper_resolve(2, premises.c_ptr(), fml3, positions, substs); pc->insert(pr); } void resolve_rule(rule_manager& rm, rule const& r1, rule const& r2, unsigned idx, expr_ref_vector const& s1, expr_ref_vector const& s2, rule& res) { if (!r1.get_proof()) { return; } SASSERT(r2.get_proof()); ast_manager& m = s1.get_manager(); expr_ref fml(m); rm.to_formula(res, fml); vector substs; svector > positions; substs.push_back(s1); substs.push_back(s2); scoped_proof _sc(m); proof_ref pr(m); proof_ref_vector premises(m); premises.push_back(r1.get_proof()); premises.push_back(r2.get_proof()); positions.push_back(std::make_pair(idx+1, 0)); TRACE("dl", tout << premises[0]->get_id() << " " << mk_pp(premises[0].get(), m) << "\n"; for (unsigned i = 0; i < s1.size(); ++i) { tout << mk_pp(s1[i], m) << " "; } tout << "\n"; tout << premises[1]->get_id() << " " << mk_pp(premises[1].get(), m) << "\n"; for (unsigned i = 0; i < s2.size(); ++i) { tout << mk_pp(s2[i], m) << " "; } tout << "\n"; ); pr = m.mk_hyper_resolve(2, premises.c_ptr(), fml, positions, substs); res.set_proof(m, pr); } class skip_model_converter : public model_converter { public: skip_model_converter() {} model_converter * translate(ast_translation & translator) override { return alloc(skip_model_converter); } void operator()(model_ref&) override {} void display(std::ostream & out) override { } void get_units(obj_map& units) override {} }; model_converter* mk_skip_model_converter() { return alloc(skip_model_converter); } class skip_proof_converter : public proof_converter { proof_ref operator()(ast_manager & m, unsigned num_source, proof * const * source) override { SASSERT(num_source == 1); return proof_ref(source[0], m); } proof_converter * translate(ast_translation & translator) override { return alloc(skip_proof_converter); } void display(std::ostream & out) override { out << "(skip-proof-converter)\n"; } }; proof_converter* mk_skip_proof_converter() { return alloc(skip_proof_converter); } void reverse_renaming(ast_manager & m, const expr_ref_vector & src, expr_ref_vector & tgt) { SASSERT(tgt.empty()); unsigned src_sz = src.size(); unsigned src_ofs = src_sz-1; unsigned max_var_idx = 0; for(unsigned i=0; iget_idx(); if(var_idx>max_var_idx) { max_var_idx=var_idx; } } unsigned tgt_sz = max_var_idx+1; unsigned tgt_ofs = tgt_sz-1; tgt.resize(tgt_sz, nullptr); for(unsigned i=0; iget_idx(); tgt[tgt_ofs-var_idx] = m.mk_var(i, v->get_sort()); } } void print_renaming(const expr_ref_vector & cont, std::ostream & out) { unsigned len = cont.size(); out << "("; for(int i=len-1; i>=0; i--) { out << (len-1-i) <<"->"; if(cont.get(i)==nullptr) { out << "{none}"; } else { out << to_var(cont.get(i))->get_idx(); } if(i!=0) { out << ","; } } out << ")\n"; } void cycle_from_permutation(unsigned_vector & permutation, unsigned_vector & cycle) { try_remove_cycle_from_permutation(permutation, cycle); DEBUG_CODE( //here we assert that there is at most one cycle in the permutation unsigned_vector aux; SASSERT(!try_remove_cycle_from_permutation(permutation, aux)); ); } bool try_remove_cycle_from_permutation(unsigned_vector & permutation, unsigned_vector & cycle) { SASSERT(cycle.empty()); DEBUG_CODE( counter ctr; ctr.count(permutation); SASSERT(permutation.empty() || ctr.get_max_positive()==permutation.size()-1); SASSERT(permutation.empty() || ctr.get_positive_count()==permutation.size()); ); unsigned sz = permutation.size(); for(unsigned i=0; i extension.size() && extension == std::string(name+len-extension.size())) { res.push_back(directory+std::string(name)); } } while(FindNextFileA(hFind, &findFileData)); FindClose(hFind); } if(traverse_subdirs) { std::string subdirPattern = directory+"*.*"; hFind = FindFirstFileA(subdirPattern.c_str(), &findFileData); if (hFind != INVALID_HANDLE_VALUE) { do { if(findFileData.cFileName[0]=='.') { continue; } get_file_names(directory+findFileData.cFileName, extension, traverse_subdirs, res); } while(FindNextFileA(hFind, &findFileData)); FindClose(hFind); } } #else NOT_IMPLEMENTED_YET(); #endif } bool file_exists(const std::string & name) { struct stat st; if(stat(name.c_str(),&st) == 0) { return true; } return false; } bool is_directory(const std::string & name) { if(!file_exists(name)) { return false; } struct stat status; stat(name.c_str(), &status); return (status.st_mode&S_IFDIR)!=0; } std::string get_file_name_without_extension(const std::string & name) { size_t slash_index = name.find_last_of("\\/"); size_t dot_index = name.rfind('.'); size_t ofs = (slash_index==std::string::npos) ? 0 : slash_index+1; size_t count = (dot_index!=std::string::npos && dot_index>ofs) ? (dot_index-ofs) : std::string::npos; return name.substr(ofs, count); } bool string_to_uint64(const char * s, uint64_t & res) { #if _WINDOWS int converted = sscanf_s(s, "%" SCNu64, &res); #else int converted = sscanf(s, "%" SCNu64, &res); #endif if(converted==0) { return false; } SASSERT(converted==1); return true; } bool read_uint64(const char * & s, uint64_t & res) { static const uint64_t max_but_one_digit = ULLONG_MAX/10; static const uint64_t max_but_one_digit_safe = (ULLONG_MAX-9)/10; if(*s<'0' || *s>'9') { return false; } res=*s-'0'; s++; while(*s>='0' && *s<='9') { if(res>max_but_one_digit_safe) { if(res>max_but_one_digit) { return false; //overflow } res*=10; char digit = *s-'0'; if(static_cast(ULLONG_MAX-res)-digit<0) { return false; //overflow } res+=digit; } else { res*=10; res+=*s-'0'; s++; } } return true; } std::string to_string(uint64_t num) { std::stringstream stm; stm< #include "ast/ast.h" #include "util/hashtable.h" #include "util/obj_hashtable.h" #include "util/uint_set.h" #include "tactic/horn_subsume_model_converter.h" #include "tactic/replace_proof_converter.h" #include "ast/substitution/substitution.h" #include "ast/rewriter/ast_counter.h" #include "util/statistics.h" #include "util/stopwatch.h" #include "util/lbool.h" #include "util/container_util.h" namespace datalog { class context; class rule; class relation_base; class relation_manager; class table_base; class pentagon_relation; class relation_fact; class relation_signature; class rule_manager; class verbose_action { unsigned m_lvl; class stopwatch* m_sw; public: verbose_action(char const* msg, unsigned lvl = 11); ~verbose_action(); }; typedef ref_vector rule_ref_vector; enum PDR_CACHE_MODE { NO_CACHE, HASH_CACHE, CONSTRAINT_CACHE, LAST_CACHE_MODE }; struct std_string_hash_proc { unsigned operator()(const std::string & s) const { return string_hash(s.c_str(), static_cast(s.length()), 17); } }; // typedef int_hashtable > idx_set; typedef uint_set idx_set; typedef idx_set var_idx_set; typedef u_map varidx2var_map; typedef obj_hashtable func_decl_set; //!< Rule dependencies. typedef std::vector string_vector; bool contains_var(expr * trm, unsigned var_idx); /** \brief Return number of arguments of \c pred that are variables */ unsigned count_variable_arguments(app * pred); template void copy_nonvariables(app * src, T& tgt) { unsigned n = src->get_num_args(); for (unsigned i = 0; i < n; i++) { expr * arg = src->get_arg(i); if (!is_var(arg)) { tgt[i]=arg; } } } /** \brief Auxiliary function used to create a tail based on \c pred for a new rule. The variables in \c pred are re-assigned using \c next_idx and \c varidx2var. A variable is considered non-local to the rule if it is in the set \c non_local_vars. Non-local variables are copied to new_rule_args, and their sorts to \c new_rule_domain. The new predicate is stored in \c new_pred. */ void mk_new_rule_tail(ast_manager & m, app * pred, var_idx_set const & non_local_vars, unsigned & next_idx, varidx2var_map & varidx2var, sort_ref_buffer & new_rule_domain, expr_ref_buffer & new_rule_args, app_ref & new_pred); /** \brief Simpler version of the previous function. Initializes next_idx with 0, and an empty varid2var */ inline void mk_new_rule_tail(ast_manager & m, app * pred, var_idx_set const & non_local_vars, sort_ref_buffer & new_rule_domain, expr_ref_buffer & new_rule_args, app_ref & new_pred) { unsigned next_idx = 0; varidx2var_map varidx2var; mk_new_rule_tail(m, pred, non_local_vars, next_idx, varidx2var, new_rule_domain, new_rule_args, new_pred); } /** \brief Print a predicate \c f to the stream \c out. */ void display_predicate(context & ctx, app * f, std::ostream & out); /** \brief Like \c display_predicate, just without the final '\n' character. */ void output_predicate(context & ctx, app * f, std::ostream & out); /** \brief Print a fact \c f to the stream \c out in a format conforming to Bddbddb. */ void display_fact(context & ctx, app * f, std::ostream & out); class variable_intersection { bool values_match(const expr * v1, const expr * v2); unsigned_vector m_args1; unsigned_vector m_args2; unsigned_vector m_const_indexes; app_ref_vector m_consts; static unsigned expr_cont_get_size(app * a) { return a->get_num_args(); } static expr * expr_cont_get(app * a, unsigned i) { return a->get_arg(i); } static unsigned expr_cont_get_size(const ptr_vector & v) { return v.size(); } static unsigned expr_cont_get_size(const expr_ref_vector & v) { return v.size(); } static expr * expr_cont_get(const ptr_vector & v, unsigned i) { return v[i]; } static expr * expr_cont_get(const expr_ref_vector & v, unsigned i) { return v[i]; } public: variable_intersection(ast_manager & m) : m_consts(m) {} unsigned size() const { return m_args1.size(); } const unsigned * get_cols1() const { return m_args1.c_ptr(); } const unsigned * get_cols2() const { return m_args2.c_ptr(); } bool empty() const { return size()==0; } void get(unsigned i, unsigned & index1, unsigned & index2) const { index1=m_args1[i]; index2=m_args2[i]; } void reset() { m_args1.reset(); m_args2.reset(); m_const_indexes.reset(); m_consts.reset(); } bool args_match(const app * f1, const app * f2); bool args_self_match(const app * f); /** \brief Fill arguments of \c f1 into corresponding positions in \c tgt using its \c operator[]. */ template void fill_into_second(const app * f1, T & tgt) const { unsigned n=size(); for(unsigned i=0; iget_arg(f1_index); } } void add_pair(unsigned idx1, unsigned idx2) { m_args1.push_back(idx1); m_args2.push_back(idx2); } /** Find pairs of indexes of arguments of \c a1 and \c a2 that correspond to the same variable. Here we do not detect the constant arguments in \c a1 and \c a2. */ template void populate(const T1 & a1, const T2 & a2) { //TODO: optimize quadratic complexity //TODO: optimize number of checks when variable occurs multiple times unsigned a1num = expr_cont_get_size(a1); unsigned a2num = expr_cont_get_size(a2); for(unsigned i1=0; i1get_idx()==v2->get_idx()) { add_pair(i1, i2); } } } } /** Find pairs of indexes of arguments of \c a that correspond to the same variable and indexes that correspond to a constant. */ void populate_self(const app * a); }; template void project_out_vector_columns(T & container, unsigned removed_col_cnt, const unsigned * removed_cols) { if(removed_col_cnt==0) { return; } unsigned n = container.size(); unsigned ofs = 1; unsigned r_i = 1; for(unsigned i=removed_cols[0]+1; i void project_out_vector_columns(ref_vector & container, unsigned removed_col_cnt, const unsigned * removed_cols) { if(removed_col_cnt==0) { return; } unsigned n = container.size(); unsigned ofs = 1; unsigned r_i = 1; for(unsigned i=removed_cols[0]+1; i void project_out_vector_columns(T & container, const unsigned_vector & removed_cols) { project_out_vector_columns(container, removed_cols.size(), removed_cols.c_ptr()); } /** \brief Take a single cycle permutation and store it in the form of a cycle. The function modifies the \c permutation vector */ void cycle_from_permutation(unsigned_vector & permutation, unsigned_vector & cycle); /** \brief If \c permutation is an identity, return false. Otherwise remove one cycle from the permutation, store it in the form of a cycle in \c cycle and return true. Using this function one can retrieve all cycles in a permutation. \c cycle must be empty before calling the function. */ bool try_remove_cycle_from_permutation(unsigned_vector & permutation, unsigned_vector & cycle); void collect_sub_permutation(const unsigned_vector & permutation, const unsigned_vector & translation, unsigned_vector & res, bool & identity); template void permutate_by_cycle(T & container, unsigned cycle_len, const unsigned * permutation_cycle) { if(cycle_len<2) { return; } typename T::data aux = container[permutation_cycle[0]]; for(unsigned i=1; i void permutate_by_cycle(ref_vector & container, unsigned cycle_len, const unsigned * permutation_cycle) { if(cycle_len<2) { return; } T * aux = container.get(permutation_cycle[0]); for(unsigned i=1; i void permutate_by_cycle(T & container, const unsigned_vector & permutation_cycle) { permutate_by_cycle(container, permutation_cycle.size(), permutation_cycle.c_ptr()); } class rule_counter : public var_counter { public: rule_counter(){} void count_rule_vars(const rule * r, int coef = 1); unsigned get_max_rule_var(const rule& r); }; void del_rule(horn_subsume_model_converter* mc, rule& r, bool unreachable); void resolve_rule(rule_manager& rm, replace_proof_converter* pc, rule const& r1, rule const& r2, unsigned idx, expr_ref_vector const& s1, expr_ref_vector const& s2, rule const& res); void resolve_rule(rule_manager& rm, rule const& r1, rule const& r2, unsigned idx, expr_ref_vector const& s1, expr_ref_vector const& s2, rule& res); model_converter* mk_skip_model_converter(); proof_converter* mk_skip_proof_converter(); void reverse_renaming(ast_manager & m, const expr_ref_vector & src, expr_ref_vector & tgt); void print_renaming(const expr_ref_vector & cont, std::ostream & out); /** \brief Update tgt with effect of applying substitution from 'sub' to it. tgt is extended by variables that are substituted by 'sub'. We use the convention that the entry at index 'i' corresponds to variable with de-Bruijn index 'i'. */ void apply_subst(expr_ref_vector& tgt, expr_ref_vector const& sub); template bool vectors_equal(const T & c1, const U & c2) { if(c1.size()!=c2.size()) { return false; } typename T::data * it1 = c1.c_ptr(); typename T::data * end1 = c1.c_ptr()+c1.size(); typename U::data * it2 = c2.c_ptr(); for(; it1!=end1; ++it1, ++it2) { if(*it1!=*it2) { return false; } } return true; } void idx_set_union(idx_set & tgt, const idx_set & src); template struct default_obj_chash { unsigned operator()(T const& cont, unsigned i) const { return cont[i]->hash(); } }; template unsigned obj_vector_hash(const T & cont) { return get_composite_hash(cont, cont.size(),default_kind_hash_proc(), default_obj_chash()); } template struct obj_vector_hash_proc { unsigned operator()(const T & cont) const { return obj_vector_hash(cont); } }; template struct svector_hash_proc { unsigned operator()(const svector & cont) const { return svector_hash()(cont); } }; template struct vector_eq_proc { bool operator()(const T & c1, const T & c2) const { return vectors_equal(c1, c2); } }; template void dealloc_ptr_vector_content(ptr_vector & v) { typename ptr_vector::iterator it = v.begin(); typename ptr_vector::iterator end = v.end(); for(; it!=end; ++it) { dealloc(*it); } } /** \brief Add elements from an iterable object \c src into the vector \c vector. */ template void push_into_vector(VectType & vector, const U & src) { typename U::iterator it = src.begin(); typename U::iterator end = src.end(); for(; it!=end; ++it) { vector.push_back(*it); } } template void push_into_vector(VectType & vector, const ref_vector & src) { U * const * it = src.begin(); U * const * end = src.end(); for(; it!=end; ++it) { vector.push_back(*it); } } template void insert_into_set(SetType & tgt, const U & src) { typename U::const_iterator it = src.begin(); typename U::const_iterator end = src.end(); for(; it!=end; ++it) { tgt.insert(*it); } } /** \brief Remove the first occurrence of \c el from \c v and return \c true. If \c el is not present in \c v, return \c false. The order of elements in \c v is not preserved. */ template bool remove_from_vector(T & v, const typename T::data & el) { unsigned sz = v.size(); for(unsigned i=0; i */ template void reset_dealloc_values(map & m) { typename map::iterator it = m.begin(); typename map::iterator end = m.end(); for (; it != end; ++it) { dealloc(it->m_value); } m.reset(); } template struct aux__index_comparator { T* m_keys; aux__index_comparator(T* keys) : m_keys(keys) {} bool operator()(unsigned a, unsigned b) { return m_keys[a] void sort_two_arrays(unsigned len, T* keys, U* vals) { if(len<2) { return; } if(len==2) { if(keys[0]>keys[1]) { std::swap(keys[0], keys[1]); std::swap(vals[0], vals[1]); } return; } unsigned_vector numbers; for(unsigned i=0; i cmp(keys); std::sort(numbers.begin(), numbers.end(), cmp); for(unsigned i=0; i void add_sequence_without_set(unsigned start, unsigned count, const Container & complement, unsigned_vector & v) { unsigned after_last = start+count; for(unsigned i=start; i void universal_delete(T* ptr) { dealloc(ptr); } /** \brief If it is possible to convert the beginning of \c s to uint64, store the result of conversion and return true; otherwise return false. */ bool string_to_uint64(const char * s, uint64_t & res); std::string to_string(uint64_t num); /** \brief Read the sequence of decimal digits starting at \c s and interpret it as uint64. If successful, \c res will contain the read number and \c s will point to the first non-digit character, and true is returned. If the first character is not a digit, no parameter is modified and false is returned. If the uint64 overflows, \c points to the character which caused the overflow and false is returned. */ bool read_uint64(const char * & s, uint64_t & res); }; #endif /* DL_UTIL_H_ */ z3-z3-4.8.7/src/muz/base/fp_params.pyg000066400000000000000000000372601356505360400174440ustar00rootroot00000000000000def_module_params('fp', description='fixedpoint parameters', export=True, params=(('engine', SYMBOL, 'auto-config', 'Select: auto-config, datalog, bmc, spacer'), ('datalog.default_table', SYMBOL, 'sparse', 'default table implementation: sparse, hashtable, bitvector, interval'), ('datalog.default_relation', SYMBOL, 'pentagon', 'default relation implementation: external_relation, pentagon'), ('datalog.generate_explanations', BOOL, False, 'produce explanations for produced facts when using the datalog engine'), ('datalog.use_map_names', BOOL, True, "use names from map files when displaying tuples"), ('datalog.magic_sets_for_queries', BOOL, False, "magic set transformation will be used for queries"), ('datalog.explanations_on_relation_level', BOOL, False, 'if true, explanations are generated as history of each relation, ' + 'rather than per fact (generate_explanations must be set to true for ' + 'this option to have any effect)'), ('datalog.unbound_compressor', BOOL, True, "auxiliary relations will be introduced to avoid unbound variables " + "in rule heads"), ('datalog.similarity_compressor', BOOL, True, "rules that differ only in values of constants will be merged into " + "a single rule"), ('datalog.similarity_compressor_threshold', UINT, 11, "if similarity_compressor is on, this value determines how many " + "similar rules there must be in order for them to be merged"), ('datalog.all_or_nothing_deltas', BOOL, False, "compile rules so that it is enough for the delta relation in " + "union and widening operations to determine only whether the " + "updated relation was modified or not"), ('datalog.compile_with_widening', BOOL, False, "widening will be used to compile recursive rules"), ('datalog.default_table_checked', BOOL, False, "if true, the default " + 'table will be default_table inside a wrapper that checks that its results ' + 'are the same as of default_table_checker table'), ('datalog.default_table_checker', SYMBOL, 'null', "see default_table_checked"), ('datalog.check_relation',SYMBOL,'null', "name of default relation to check. " + "operations on the default relation will be verified using SMT solving"), ('datalog.initial_restart_timeout', UINT, 0, "length of saturation run before the first restart (in ms), " + "zero means no restarts"), ('datalog.timeout', UINT, 0, "Time limit used for saturation"), ('datalog.output_profile', BOOL, False, "determines whether profile information should be " + "output when outputting Datalog rules or instructions"), ('datalog.print.tuples', BOOL, True, "determines whether tuples for output predicates should be output"), ('datalog.profile_timeout_milliseconds', UINT, 0, "instructions and rules that took less than the threshold " + "will not be printed when printed the instruction/rule list"), ('datalog.dbg_fpr_nonempty_relation_signature', BOOL, False, "if true, finite_product_relation will attempt to avoid creating " + "inner relation with empty signature by putting in half of the " + "table columns, if it would have been empty otherwise"), ('datalog.subsumption', BOOL, True, "if true, removes/filters predicates with total transitions"), ('generate_proof_trace', BOOL, False, "trace for 'sat' answer as proof object"), ('spacer.push_pob', BOOL, False, "push blocked pobs to higher level"), ('spacer.push_pob_max_depth', UINT, UINT_MAX, 'Maximum depth at which push_pob is enabled'), ('validate', BOOL, False, "validate result (by proof checking or model checking)"), ('spacer.simplify_lemmas_pre', BOOL, False, "simplify derived lemmas before inductive propagation"), ('spacer.simplify_lemmas_post', BOOL, False, "simplify derived lemmas after inductive propagation"), ('spacer.use_inductive_generalizer', BOOL, True, "generalize lemmas using induction strengthening"), ('spacer.max_num_contexts', UINT, 500, "maximal number of contexts to create"), ('print_fixedpoint_extensions', BOOL, True, "use SMT-LIB2 fixedpoint extensions, instead of pure SMT2, " + "when printing rules"), ('print_low_level_smt2', BOOL, False, "use (faster) low-level SMT2 printer (the printer is scalable " + "but the result may not be as readable)"), ('print_with_variable_declarations', BOOL, True, "use variable declarations when displaying rules " + "(instead of attempting to use original names)"), ('print_answer', BOOL, False, 'print answer instance(s) to query'), ('print_certificate', BOOL, False, 'print certificate for reachability or non-reachability'), ('print_boogie_certificate', BOOL, False, 'print certificate for reachability or non-reachability using a ' + 'format understood by Boogie'), ('print_aig', SYMBOL, '', 'Dump clauses in AIG text format (AAG) to the given file name'), ('print_statistics', BOOL, False, 'print statistics'), ('tab.selection', SYMBOL, 'weight', 'selection method for tabular strategy: weight (default), first, var-use'), ('xform.bit_blast', BOOL, False, 'bit-blast bit-vectors'), ('xform.magic', BOOL, False, "perform symbolic magic set transformation"), ('xform.scale', BOOL, False, "add scaling variable to linear real arithmetic clauses"), ('xform.inline_linear', BOOL, True, "try linear inlining method"), ('xform.inline_eager', BOOL, True, "try eager inlining of rules"), ('xform.inline_linear_branch', BOOL, False, "try linear inlining method with potential expansion"), ('xform.compress_unbound', BOOL, True, "compress tails with unbound variables"), ('xform.fix_unbound_vars', BOOL, False, "fix unbound variables in tail"), ('xform.unfold_rules', UINT, 0, "unfold rules statically using iterative squaring"), ('xform.slice', BOOL, True, "simplify clause set using slicing"), ('xform.karr', BOOL, False, "Add linear invariants to clauses using Karr's method"), ('spacer.use_euf_gen', BOOL, False, 'Generalize lemmas and pobs using implied equalities'), ('xform.transform_arrays', BOOL, False, "Rewrites arrays equalities and applies select over store"), ('xform.instantiate_arrays', BOOL, False, "Transforms P(a) into P(i, a[i] a)"), ('xform.instantiate_arrays.enforce', BOOL, False, "Transforms P(a) into P(i, a[i]), discards a from predicate"), ('xform.instantiate_arrays.nb_quantifier', UINT, 1, "Gives the number of quantifiers per array"), ('xform.instantiate_arrays.slice_technique', SYMBOL, "no-slicing", "=> GetId(i) = i, => GetId(i) = true"), ('xform.quantify_arrays', BOOL, False, "create quantified Horn clauses from clauses with arrays"), ('xform.instantiate_quantifiers', BOOL, False, "instantiate quantified Horn clauses using E-matching heuristic"), ('xform.coalesce_rules', BOOL, False, "coalesce rules"), ('xform.tail_simplifier_pve', BOOL, True, "propagate_variable_equivalences"), ('xform.subsumption_checker', BOOL, True, "Enable subsumption checker (no support for model conversion)"), ('xform.coi', BOOL, True, "use cone of influence simplification"), ('spacer.order_children', UINT, 0, 'SPACER: order of enqueuing children in non-linear rules : 0 (original), 1 (reverse), 2 (random)'), ('spacer.use_lemma_as_cti', BOOL, False, 'SPACER: use a lemma instead of a CTI in flexible_trace'), ('spacer.reset_pob_queue', BOOL, True, 'SPACER: reset pob obligation queue when entering a new level'), ('spacer.use_array_eq_generalizer', BOOL, True, 'SPACER: attempt to generalize lemmas with array equalities'), ('spacer.use_derivations', BOOL, True, 'SPACER: using derivation mechanism to cache intermediate results for non-linear rules'), ('xform.array_blast', BOOL, False, "try to eliminate local array terms using Ackermannization -- some array terms may remain"), ('xform.array_blast_full', BOOL, False, "eliminate all local array variables by QE"), ('xform.elim_term_ite', BOOL, False, 'Eliminate term-ite expressions'), ('xform.elim_term_ite.inflation', UINT, 3, 'Maximum inflation for non-Boolean ite-terms blasting: 0 (none), k (multiplicative)'), ('spacer.propagate', BOOL, True, 'Enable propagate/pushing phase'), ('spacer.max_level', UINT, UINT_MAX, "Maximum level to explore"), ('spacer.elim_aux', BOOL, True, "Eliminate auxiliary variables in reachability facts"), ('spacer.blast_term_ite_inflation', UINT, 3, 'Maximum inflation for non-Boolean ite-terms expansion: 0 (none), k (multiplicative)'), ('spacer.reach_dnf', BOOL, True, "Restrict reachability facts to DNF"), ('bmc.linear_unrolling_depth', UINT, UINT_MAX, "Maximal level to explore"), ('spacer.iuc.split_farkas_literals', BOOL, False, "Split Farkas literals"), ('spacer.native_mbp', BOOL, True, "Use native mbp of Z3"), ('spacer.eq_prop', BOOL, True, "Enable equality and bound propagation in arithmetic"), ('spacer.weak_abs', BOOL, True, "Weak abstraction"), ('spacer.restarts', BOOL, False, "Enable resetting obligation queue"), ('spacer.restart_initial_threshold', UINT, 10, "Initial threshold for restarts"), ('spacer.random_seed', UINT, 0, "Random seed to be used by SMT solver"), ('spacer.mbqi', BOOL, True, 'Enable mbqi'), ('spacer.keep_proxy', BOOL, True, 'keep proxy variables (internal parameter)'), ('spacer.q3', BOOL, True, 'Allow quantified lemmas in frames'), ('spacer.q3.instantiate', BOOL, True, 'Instantiate quantified lemmas'), ('spacer.q3.use_qgen', BOOL, False, 'use quantified lemma generalizer'), ('spacer.q3.qgen.normalize', BOOL, True, 'normalize cube before quantified generalization'), ('spacer.iuc', UINT, 1, '0 = use old implementation of unsat-core-generation, ' + '1 = use new implementation of IUC generation, ' + '2 = use new implementation of IUC + min-cut optimization'), ('spacer.iuc.arith', UINT, 1, '0 = use simple Farkas plugin, ' + '1 = use simple Farkas plugin with constant from other partition (like old unsat-core-generation),' + '2 = use Gaussian elimination optimization (broken), 3 = use additive IUC plugin'), ('spacer.iuc.old_hyp_reducer', BOOL, False, 'use old hyp reducer instead of new implementation, for debugging only'), ('spacer.validate_lemmas', BOOL, False, 'Validate each lemma after generalization'), ('spacer.ground_pobs', BOOL, True, 'Ground pobs by using values from a model'), ('spacer.iuc.print_farkas_stats', BOOL, False, 'prints for each proof how many Farkas lemmas it contains and how many of these participate in the cut (for debugging)'), ('spacer.iuc.debug_proof', BOOL, False, 'prints proof used by unsat-core-learner for debugging purposes (debugging)'), ('spacer.simplify_pob', BOOL, False, 'simplify pobs by removing redundant constraints'), ('spacer.p3.share_lemmas', BOOL, False, 'Share frame lemmas'), ('spacer.p3.share_invariants', BOOL, False, "Share invariants lemmas"), ('spacer.min_level', UINT, 0, 'Minimal level to explore'), ('spacer.print_json', SYMBOL, '', 'Print pobs tree in JSON format to a given file'), ('spacer.ctp', BOOL, True, 'Enable counterexample-to-pushing'), ('spacer.use_inc_clause', BOOL, True, 'Use incremental clause to represent trans'), ('spacer.dump_benchmarks', BOOL, False, 'Dump SMT queries as benchmarks'), ('spacer.dump_threshold', DOUBLE, 5.0, 'Threshold in seconds on dumping benchmarks'), ('spacer.gpdr', BOOL, False, 'Use GPDR solving strategy for non-linear CHC'), ('spacer.gpdr.bfs', BOOL, True, 'Use BFS exploration strategy for expanding model search'), ('spacer.use_bg_invs', BOOL, False, 'Enable external background invariants'), ('spacer.use_lim_num_gen', BOOL, False, 'Enable limit numbers generalizer to get smaller numbers'), )) z3-z3-4.8.7/src/muz/base/hnf.cpp000066400000000000000000000374101356505360400162270ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: hnf.cpp Abstract: Horn normal form conversion. Author: Nikolaj Bjorner (nbjorner) 3-20-2013 Notes: Convert formula (forall x f(x)) into conjunction (f1 xy) (f2 xy) (f3 xy) such that (forall x f(x)) ~ /\ (forall xy (f_i xy)) modulo definitions that are introduced. Convert proof with asserted (forall xy (f' xy)) To: (forall xy (f' xy)) by mp~ 1, 2 1. asserted/def-intro (forall xy (f xy)) 2. (forall xy (f xy)) ~ (forall xy (f' xy)) by trans, 3, 4 3. (forall xy (f xy)) ~ (forall xy (f1 xy)) by pull quantifiers (rewrite) 4. (forall xy (f1 xy)) ~ (forall xy (f' xy)) by oeq_quant_intro 5 5. f1 xy ~ f' xy by sub-proof. --*/ #include "muz/base/hnf.h" #include "util/warning.h" #include "ast/used_vars.h" #include "ast/well_sorted.h" #include "ast/rewriter/var_subst.h" #include "ast/normal_forms/name_exprs.h" #include "ast/act_cache.h" #include "ast/ast_pp.h" #include "ast/rewriter/quant_hoist.h" #include "ast/ast_util.h" #include "muz/base/dl_util.h" #include "ast/for_each_ast.h" #include "ast/for_each_expr.h" class hnf::imp { class contains_predicate_proc { imp const& m; public: struct found {}; contains_predicate_proc(imp const& m): m(m) {} void operator()(var * n) {} void operator()(quantifier * n) {} void operator()(app* n) { if (m.is_predicate(n)) throw found(); } }; ast_manager& m; bool m_produce_proofs; expr_ref_vector m_todo; proof_ref_vector m_proofs; expr_ref_vector m_refs; symbol m_name; svector m_names; ptr_vector m_sorts; quantifier_hoister m_qh; obj_map m_memoize_disj; obj_map m_memoize_proof; func_decl_ref_vector m_fresh_predicates; expr_ref_vector m_body; proof_ref_vector m_defs; contains_predicate_proc m_proc; expr_free_vars m_free_vars; ast_fast_mark1 m_mark1; public: imp(ast_manager & m): m(m), m_produce_proofs(false), m_todo(m), m_proofs(m), m_refs(m), m_name("P"), m_qh(m), m_fresh_predicates(m), m_body(m), m_defs(m), m_proc(*this) { } bool is_horn(expr* n) { expr* n1, *n2; while (is_forall(n)) n = to_quantifier(n)->get_expr(); if (m.is_implies(n, n1, n2) && is_predicate(n2)) { if (is_var(n1)) { return true; } if (is_quantifier(n1)) { return false; } app* a1 = to_app(n1); if (m.is_and(a1)) { for (unsigned i = 0; i < a1->get_num_args(); ++i) { if (!is_predicate(a1->get_arg(i)) && contains_predicate(a1->get_arg(i))) { return false; } } } else if (!is_predicate(a1) && contains_predicate(a1)) { return false; } return true; } return false; } void operator()(expr * n, proof* p, expr_ref_vector& result, proof_ref_vector& ps) { if (is_horn(n)) { result.push_back(n); ps.push_back(p); return; } expr_ref fml(m); proof_ref pr(m); m_todo.reset(); m_proofs.reset(); m_refs.reset(); m_memoize_disj.reset(); m_memoize_proof.reset(); m_fresh_predicates.reset(); m_todo.push_back(n); m_proofs.push_back(p); m_produce_proofs = p != nullptr; while (!m_todo.empty() && checkpoint()) { fml = m_todo.back(); pr = m_proofs.back(); m_todo.pop_back(); m_proofs.pop_back(); mk_horn(fml, pr); if (fml) { result.push_back(fml); ps.push_back(pr); } } TRACE("hnf", tout << mk_pp(n, m) << "\n==>\n"; for (unsigned i = 0; i < result.size(); ++i) { tout << mk_pp(result[i].get(), m) << "\n"; }); } bool checkpoint() { return !m.canceled(); } void set_name(symbol const& n) { if (n == symbol::null) { m_name = symbol("P"); } else { m_name = n; } } func_decl_ref_vector const& get_fresh_predicates() { return m_fresh_predicates; } void reset() { m_todo.reset(); m_proofs.reset(); m_refs.reset(); m_memoize_disj.reset(); m_memoize_proof.reset(); m_fresh_predicates.reset(); } ast_manager& get_manager() { return m; } private: bool produce_proofs() const { return m_produce_proofs; } bool is_predicate(expr* p) const { return is_app(p) && is_predicate(to_app(p)->get_decl()); } bool is_predicate(func_decl* f) const { return m.is_bool(f->get_range()) && f->get_family_id() == null_family_id; } bool contains_predicate(expr* fml) { try { quick_for_each_expr(m_proc, m_mark1, fml); m_mark1.reset(); } catch (const contains_predicate_proc::found &) { m_mark1.reset(); return true; } return false; } void mk_horn(expr_ref& fml, proof_ref& premise) { SASSERT(!premise || fml == m.get_fact(premise)); expr* e1, *e2; expr_ref fml0(m), fml1(m), fml2(m), head(m); proof_ref p(m); fml0 = fml; m_names.reset(); m_sorts.reset(); m_body.reset(); m_defs.reset(); m_qh.pull_quantifier(true, fml0, &m_sorts, &m_names); if (premise){ fml1 = bind_variables(fml0); if (!m_sorts.empty()) { proof* p1 = m.mk_pull_quant(fml, to_quantifier(fml1)); premise = mk_modus_ponens(premise, p1); fml = fml1; } } head = fml0; while (m.is_implies(head, e1, e2)) { m_body.push_back(e1); head = e2; } flatten_and(m_body); if (premise) { p = m.mk_rewrite(fml0, mk_implies(m_body, head)); } // // Case: // A \/ B -> C // => // A -> C // B -> C // if (m_body.size() == 1 && m.is_or(m_body[0].get()) && contains_predicate(m_body[0].get())) { app* _or = to_app(m_body[0].get()); unsigned sz = _or->get_num_args(); expr* const* args = _or->get_args(); for (unsigned i = 0; i < sz; ++i) { m_todo.push_back(bind_variables(m.mk_implies(args[i], head))); m_proofs.push_back(nullptr); } if (premise) { expr_ref f1 = bind_variables(mk_implies(m_body, head)); expr* f2 = m.mk_and(sz, m_todo.c_ptr()+m_todo.size()-sz); proof_ref p2(m), p3(m); p2 = m.mk_def_axiom(m.mk_iff(f1, f2)); p3 = mk_quant_intro(fml, f1, p); p2 = mk_transitivity(p3, p2); p2 = mk_modus_ponens(premise, p2); for (unsigned i = 0; i < sz; ++i) { m_proofs[m_proofs.size()-sz+i] = m.mk_and_elim(p2, i); } } fml = nullptr; return; } eliminate_disjunctions(m_body, m_defs); p = mk_congruence(p, m_body, head, m_defs); eliminate_quantifier_body(m_body, m_defs); p = mk_congruence(p, m_body, head, m_defs); fml2 = mk_implies(m_body, head); fml = bind_variables(fml2); if (premise) { SASSERT(p); p = mk_quant_intro(fml1, fml, p); premise = mk_modus_ponens(premise, p); } } proof* mk_quant_intro(expr* e1, expr* e2, proof* p) { if (m_sorts.empty()) { return p; } quantifier* q1 = to_quantifier(e1); quantifier* q2 = to_quantifier(e2); if (m.is_iff(m.get_fact(p))) { return m.mk_quant_intro(q1, q2, p); } if (m.is_oeq(m.get_fact(p))) { return m.mk_oeq_quant_intro(q1, q2, p); } UNREACHABLE(); return p; } void eliminate_disjunctions(expr_ref_vector::element_ref& body, proof_ref_vector& proofs) { expr* b = body.get(); expr* e1, *e2; bool negate_args = false; bool is_disj = false; expr_ref_vector _body(m); unsigned num_disj = 0; expr* const* disjs = nullptr; if (!contains_predicate(b)) { return; } TRACE("hnf", tout << mk_pp(b, m) << "\n";); if (m.is_or(b)) { is_disj = true; negate_args = false; num_disj = to_app(b)->get_num_args(); disjs = to_app(b)->get_args(); } if (m.is_not(b, e1) && m.is_and(e1)) { is_disj = true; negate_args = true; num_disj = to_app(e1)->get_num_args(); disjs = to_app(e1)->get_args(); } if (m.is_implies(b, e1, e2)) { is_disj = true; _body.push_back(mk_not(m, e1)); _body.push_back(e2); disjs = _body.c_ptr(); num_disj = 2; negate_args = false; } if (is_disj) { app* old_head = nullptr; if (m_memoize_disj.find(b, old_head)) { body = old_head; } else { app_ref head = mk_fresh_head(b); proof_ref_vector defs(m); for (unsigned i = 0; i < num_disj; ++i) { expr* e = disjs[i]; if (negate_args) { e = m.mk_not(e); } m_todo.push_back(bind_variables(m.mk_implies(e, head))); m_proofs.push_back(nullptr); if (produce_proofs()) { defs.push_back(m.mk_def_intro(m_todo.back())); m_proofs[m_proofs.size()-1] = defs.back(); } } if (produce_proofs()) { proof* p = m.mk_apply_defs(body.get(), head, defs.size(), defs.c_ptr()); m_refs.push_back(p); m_memoize_proof.insert(b, p); } m_memoize_disj.insert(b, head); m_refs.push_back(b); m_refs.push_back(head); // update the body to be the newly introduced head relation body = head; } if (produce_proofs()) { proofs.push_back(m_memoize_proof.find(b)); } } } app_ref mk_fresh_head(expr* e) { ptr_vector sorts1; m_free_vars(e); expr_ref_vector args(m); for (unsigned i = 0; i < m_free_vars.size(); ++i) { if (m_free_vars[i]) { args.push_back(m.mk_var(i, m_free_vars[i])); sorts1.push_back(m_free_vars[i]); } } func_decl_ref f(m); f = m.mk_fresh_func_decl(m_name.str().c_str(), "", sorts1.size(), sorts1.c_ptr(), m.mk_bool_sort()); m_fresh_predicates.push_back(f); return app_ref(m.mk_app(f, args.size(), args.c_ptr()), m); } void eliminate_disjunctions(expr_ref_vector& body, proof_ref_vector& proofs) { for (unsigned i = 0; i < body.size(); ++i) { expr_ref_vector::element_ref r = body[i]; eliminate_disjunctions(r, proofs); } } void eliminate_quantifier_body(expr_ref_vector::element_ref& body, proof_ref_vector& proofs) { if (is_forall(body.get()) && contains_predicate(body.get())) { quantifier* q = to_quantifier(body.get()); expr* e = q->get_expr(); if (!is_predicate(e)) { app_ref head = mk_fresh_head(e); m_todo.push_back(bind_variables(m.mk_implies(e, head))); m_proofs.push_back(nullptr); body = m.update_quantifier(q, head); if (produce_proofs()) { proof* def_intro = m.mk_def_intro(m_todo.back()); proof* def_proof = m.mk_apply_def(e, head, def_intro); proofs.push_back(m.mk_nnf_neg(q, body.get(), 1, &def_proof)); m_proofs[m_proofs.size()-1] = def_intro; } } } } void eliminate_quantifier_body(expr_ref_vector& body, proof_ref_vector& proofs) { for (unsigned i = 0; i < body.size(); ++i) { expr_ref_vector::element_ref r = body[i]; eliminate_quantifier_body(r, proofs); } } app_ref mk_implies(expr_ref_vector const& body, expr* head) { switch (body.size()) { case 0: return app_ref(to_app(head), m); case 1: return app_ref(m.mk_implies(body[0], head), m); default: return app_ref(m.mk_implies(m.mk_and(body.size(), body.c_ptr()), head), m); } } proof_ref mk_congruence(proof* p1, expr_ref_vector const& body, expr* head, proof_ref_vector& defs) { if (defs.empty()) { return proof_ref(p1, m); } else { SASSERT(p1); proof_ref p2(m), p3(m); app_ref fml = mk_implies(body, head); expr* fact = m.get_fact(p1); if (m.is_iff(fact)) { p1 = m.mk_iff_oeq(p1); fact = m.get_fact(p1); } VERIFY (m.is_oeq(fact) || m.is_eq(fact)); app* e2 = to_app(to_app(fact)->get_arg(1)); p2 = m.mk_oeq_congruence(e2, fml, defs.size(), defs.c_ptr()); p3 = mk_transitivity(p1, p2); defs.reset(); return proof_ref(p3, m); } } proof_ref mk_modus_ponens(proof* premise, proof* eq) { proof_ref result(m); result = m.mk_modus_ponens(premise, eq); if (m.get_fact(premise) == m.get_fact(result)) { result = premise; } return result; } proof* mk_transitivity(proof* p1, proof* p2) { if (p1) { app* f = to_app(m.get_fact(p1)); if (f->get_arg(0) == f->get_arg(1)) { return p2; } } if (p2) { app* f = to_app(m.get_fact(p2)); if (f->get_arg(0) == f->get_arg(1)) { return p1; } } return m.mk_transitivity(p1, p2); } expr_ref bind_variables(expr* e) { SASSERT(m_sorts.size() == m_names.size()); if (m_sorts.empty()) { return expr_ref(e, m); } return expr_ref(m.mk_forall(m_sorts.size(), m_sorts.c_ptr(), m_names.c_ptr(), e), m); } }; hnf::hnf(ast_manager & m) { m_imp = alloc(imp, m); } hnf::~hnf() { dealloc(m_imp); } void hnf::operator()(expr * n, proof* p, expr_ref_vector & rs, proof_ref_vector& ps) { m_imp->operator()(n, p, rs, ps); TRACE("hnf", ast_manager& m = rs.get_manager(); tout << mk_ismt2_pp(n, m) << "\nHNF result:\n"; for (unsigned i = 0; i < rs.size(); ++i) { tout << mk_pp(rs[i].get(), m) << "\n"; } ); } void hnf::set_name(symbol const& n) { m_imp->set_name(n); } void hnf::reset() { m_imp->reset(); } func_decl_ref_vector const& hnf::get_fresh_predicates() { return m_imp->get_fresh_predicates(); } z3-z3-4.8.7/src/muz/base/hnf.h000066400000000000000000000017221356505360400156710ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ /*-- Module Name: hnf.h Abstract: Horn normal form conversion. Author: Notes: Very similar to NNF. --*/ #ifndef HNF_H_ #define HNF_H_ #include "ast/ast.h" #include "util/params.h" #include "ast/normal_forms/defined_names.h" #include "tactic/proof_converter.h" class hnf { class imp; imp * m_imp; public: hnf(ast_manager & m); ~hnf(); void operator()(expr * n, // [IN] expression that should be put into Horn NF proof* p, // [IN] proof of n expr_ref_vector & rs, // [OUT] resultant (conjunction) of expressions proof_ref_vector& ps // [OUT] proofs of rs ); void set_name(symbol const& name); void reset(); func_decl_ref_vector const& get_fresh_predicates(); }; #endif /* HNF_H_ */ z3-z3-4.8.7/src/muz/base/rule_properties.cpp000066400000000000000000000147671356505360400207110ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: rule_properties.cpp Abstract: Collect properties of rules. Author: Nikolaj Bjorner (nbjorner) 9-25-2014 Notes: --*/ #include "ast/expr_functors.h" #include "muz/base/rule_properties.h" #include "muz/base/dl_rule_set.h" #include "ast/for_each_expr.h" #include "muz/base/dl_context.h" using namespace datalog; rule_properties::rule_properties(ast_manager & m, rule_manager& rm, context& ctx, i_expr_pred& p): m(m), rm(rm), m_ctx(ctx), m_is_predicate(p), m_dt(m), m_dl(m), m_bv(m), m_generate_proof(false) {} rule_properties::~rule_properties() {} void rule_properties::collect(rule_set const& rules) { reset(); rule_set::iterator it = rules.begin(), end = rules.end(); expr_sparse_mark visited; for (; it != end; ++it) { rule* r = *it; m_rule = r; unsigned ut_size = r->get_uninterpreted_tail_size(); unsigned t_size = r->get_tail_size(); if (r->has_negation()) { m_negative_rules.push_back(r); } for (unsigned i = ut_size; i < t_size; ++i) { for_each_expr_core(*this, visited, r->get_tail(i)); } if (m_generate_proof && !r->get_proof()) { rm.mk_rule_asserted_proof(*r); } for (unsigned i = 0; m_inf_sort.empty() && i < r->get_decl()->get_arity(); ++i) { sort* d = r->get_decl()->get_domain(i); sort_size sz = d->get_num_elements(); if (!sz.is_finite() && !m_dl.is_rule_sort(d)) { TRACE("dl", tout << "sort " << mk_pp(d, m) << " is not finite " << sz << "\n";); m_inf_sort.push_back(m_rule); } } } } void rule_properties::check_quantifier_free() { if (!m_quantifiers.empty()) { rule* r = m_quantifiers.begin()->m_value; std::stringstream stm; stm << "cannot process quantifier in rule "; r->display(m_ctx, stm); throw default_exception(stm.str()); } } void rule_properties::check_for_negated_predicates() { if (!m_negative_rules.empty()) { rule* r = m_negative_rules[0]; std::stringstream stm; stm << "Rule contains negative predicate "; r->display(m_ctx, stm); throw default_exception(stm.str()); } } void rule_properties::check_uninterpreted_free() { if (!m_uninterp_funs.empty()) { func_decl* f = m_uninterp_funs.begin()->m_key; rule* r = m_uninterp_funs.begin()->m_value; std::stringstream stm; stm << "Uninterpreted '" << f->get_name() << "' in "; r->display(m_ctx, stm); throw default_exception(stm.str()); } } void rule_properties::check_infinite_sorts() { if (!m_inf_sort.empty()) { std::stringstream stm; rule* r = m_inf_sort.back(); stm << "Rule contains infinite sorts in rule "; r->display(m_ctx, stm); throw default_exception(stm.str()); } } void rule_properties::check_nested_free() { if (!m_interp_pred.empty()) { std::stringstream stm; rule* r = m_interp_pred[0]; stm << "Rule contains nested predicates "; r->display(m_ctx, stm); throw default_exception(stm.str()); } } void rule_properties::check_existential_tail() { ast_mark visited; ptr_vector todo, tocheck; for (unsigned i = 0; i < m_interp_pred.size(); ++i) { rule& r = *m_interp_pred[i]; unsigned ut_size = r.get_uninterpreted_tail_size(); unsigned t_size = r.get_tail_size(); for (unsigned i = ut_size; i < t_size; ++i) { todo.push_back(r.get_tail(i)); } } context::contains_pred contains_p(m_ctx); check_pred check_pred(contains_p, m); while (!todo.empty()) { expr* e = todo.back(), *e1, *e2; todo.pop_back(); if (visited.is_marked(e)) { continue; } visited.mark(e, true); if (m_is_predicate(e)) { } else if (m.is_and(e) || m.is_or(e)) { todo.append(to_app(e)->get_num_args(), to_app(e)->get_args()); } else if (m.is_implies(e, e1, e2)) { tocheck.push_back(e1); todo.push_back(e2); } else if (is_quantifier(e)) { tocheck.push_back(to_quantifier(e)->get_expr()); } else if (m.is_eq(e, e1, e2) && m.is_true(e1)) { todo.push_back(e2); } else if (m.is_eq(e, e1, e2) && m.is_true(e2)) { todo.push_back(e1); } else { tocheck.push_back(e); } } for (unsigned i = 0; i < tocheck.size(); ++i) { expr* e = tocheck[i]; if (check_pred(e)) { std::ostringstream out; out << "recursive predicate " << mk_ismt2_pp(e, m) << " occurs nested in the body of a rule"; throw default_exception(out.str()); } } } void rule_properties::insert(ptr_vector& rules, rule* r) { if (rules.empty() || rules.back() != r) { rules.push_back(r); } } void rule_properties::operator()(var* n) { } void rule_properties::operator()(quantifier* n) { m_quantifiers.insert(n, m_rule); } void rule_properties::operator()(app* n) { if (m_is_predicate(n)) { insert(m_interp_pred, m_rule); } else if (is_uninterp(n) && !m_dl.is_rule_sort(n->get_decl()->get_range())) { m_uninterp_funs.insert(n->get_decl(), m_rule); } else if (m_dt.is_accessor(n)) { sort* s = m.get_sort(n->get_arg(0)); SASSERT(m_dt.is_datatype(s)); if (m_dt.get_datatype_constructors(s)->size() > 1) { bool found = false; func_decl * c = m_dt.get_accessor_constructor(n->get_decl()); unsigned ut_size = m_rule->get_uninterpreted_tail_size(); unsigned t_size = m_rule->get_tail_size(); for (unsigned i = ut_size; !found && i < t_size; ++i) { app* t = m_rule->get_tail(i); if (m_dt.is_recognizer(t) && t->get_arg(0) == n->get_arg(0) && m_dt.get_recognizer_constructor(t->get_decl()) == c) { found = true; } } if (!found) { m_uninterp_funs.insert(n->get_decl(), m_rule); } } } } void rule_properties::reset() { m_quantifiers.reset(); m_uninterp_funs.reset(); m_interp_pred.reset(); m_negative_rules.reset(); m_inf_sort.reset(); } z3-z3-4.8.7/src/muz/base/rule_properties.h000066400000000000000000000032271356505360400203430ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: rule_properties.h Abstract: Collect properties of rules. Author: Nikolaj Bjorner (nbjorner) 9-25-2014 Notes: --*/ #ifndef RULE_PROPERTIES_H_ #define RULE_PROPERTIES_H_ #include "ast/ast.h" #include "ast/datatype_decl_plugin.h" #include "ast/bv_decl_plugin.h" #include "muz/base/dl_rule.h" namespace datalog { class rule_properties { ast_manager& m; rule_manager& rm; context& m_ctx; i_expr_pred& m_is_predicate; datatype_util m_dt; dl_decl_util m_dl; bv_util m_bv; bool m_generate_proof; rule* m_rule; obj_map m_quantifiers; obj_map m_uninterp_funs; ptr_vector m_interp_pred; ptr_vector m_negative_rules; ptr_vector m_inf_sort; void insert(ptr_vector& rules, rule* r); public: rule_properties(ast_manager & m, rule_manager& rm, context& ctx, i_expr_pred& is_predicate); ~rule_properties(); void set_generate_proof(bool generate_proof) { m_generate_proof = generate_proof; } void collect(rule_set const& r); void check_quantifier_free(); void check_uninterpreted_free(); void check_existential_tail(); void check_for_negated_predicates(); void check_nested_free(); void check_infinite_sorts(); void operator()(var* n); void operator()(quantifier* n); void operator()(app* n); void reset(); }; } #endif /* RULE_PROPERTIES_H_ */ z3-z3-4.8.7/src/muz/bmc/000077500000000000000000000000001356505360400145725ustar00rootroot00000000000000z3-z3-4.8.7/src/muz/bmc/CMakeLists.txt000066400000000000000000000001471356505360400173340ustar00rootroot00000000000000z3_add_component(bmc SOURCES dl_bmc_engine.cpp COMPONENT_DEPENDENCIES muz transforms ) z3-z3-4.8.7/src/muz/bmc/dl_bmc_engine.cpp000066400000000000000000001726151356505360400200570ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: dl_bmc_engine.cpp Abstract: BMC engine for fixedpoint solver. Author: Nikolaj Bjorner (nbjorner) 2012-9-20 Revision History: --*/ #include "ast/datatype_decl_plugin.h" #include "ast/dl_decl_plugin.h" #include "ast/ast_smt_pp.h" #include "ast/well_sorted.h" #include "ast/rewriter/bool_rewriter.h" #include "ast/rewriter/rewriter_def.h" #include "ast/scoped_proof.h" #include "smt/smt_solver.h" #include "tactic/fd_solver/fd_solver.h" #include "muz/base/dl_context.h" #include "muz/base/dl_rule_transformer.h" #include "muz/bmc/dl_bmc_engine.h" #include "muz/transforms/dl_mk_slice.h" #include "model/model_smt2_pp.h" #include "muz/transforms/dl_transforms.h" #include "muz/transforms/dl_mk_rule_inliner.h" #include "muz/base/fp_params.hpp" namespace datalog { // --------------------------------------------------------------------------- // Basic linear BMC based on indexed variables using quantified bit-vector domains. class bmc::qlinear { bmc& b; ast_manager& m; bv_util m_bv; unsigned m_bit_width; public: qlinear(bmc& b): b(b), m(b.m), m_bv(m), m_bit_width(1) {} lbool check() { setup(); m_bit_width = 4; lbool res = l_false; while (res == l_false) { b.m_solver->push(); IF_VERBOSE(1, verbose_stream() << "bit_width: " << m_bit_width << "\n";); compile(); b.checkpoint(); func_decl_ref q = mk_q_func_decl(b.m_query_pred); expr* T = m.mk_const(symbol("T"), mk_index_sort()); expr_ref fml(m.mk_app(q, T), m); b.assert_expr(fml); res = b.m_solver->check_sat(0, nullptr); if (res == l_true) { res = get_model(); } b.m_solver->pop(1); ++m_bit_width; } return res; } private: sort_ref mk_index_sort() { return sort_ref(m_bv.mk_sort(m_bit_width), m); } var_ref mk_index_var() { return var_ref(m.mk_var(0, mk_index_sort()), m); } void compile() { sort_ref index_sort = mk_index_sort(); var_ref var = mk_index_var(); sort* index_sorts[1] = { index_sort }; symbol tick("T"); rule_set::decl2rules::iterator it = b.m_rules.begin_grouped_rules(); rule_set::decl2rules::iterator end = b.m_rules.end_grouped_rules(); for (; it != end; ++it) { func_decl* p = it->m_key; rule_vector const& rls = *it->m_value; // Assert: forall level . p(T) => body of rule i + equalities for head of rule i func_decl_ref pr = mk_q_func_decl(p); expr_ref pred = expr_ref(m.mk_app(pr, var.get()), m); expr_ref_vector rules(m), sub(m), conjs(m); expr_ref trm(m), rule_body(m), rule_i(m); for (unsigned i = 0; i < rls.size(); ++i) { sub.reset(); conjs.reset(); rule& r = *rls[i]; rule_i = m.mk_app(mk_q_rule(p, i), var.get()); rules.push_back(rule_i); mk_qrule_vars(r, i, sub); // apply substitution to body. var_subst vs(m, false); for (unsigned k = 0; k < p->get_arity(); ++k) { trm = vs(r.get_head()->get_arg(k), sub.size(), sub.c_ptr()); conjs.push_back(m.mk_eq(trm, mk_q_arg(p, k, true))); } for (unsigned j = 0; j < r.get_uninterpreted_tail_size(); ++j) { func_decl* q = r.get_decl(j); for (unsigned k = 0; k < q->get_arity(); ++k) { trm = vs(r.get_tail(j)->get_arg(k), sub.size(), sub.c_ptr()); conjs.push_back(m.mk_eq(trm, mk_q_arg(q, k, false))); } func_decl_ref qr = mk_q_func_decl(q); conjs.push_back(m.mk_app(qr, m_bv.mk_bv_sub(var, mk_q_one()))); } for (unsigned j = r.get_uninterpreted_tail_size(); j < r.get_tail_size(); ++j) { trm = vs(r.get_tail(j), sub.size(), sub.c_ptr()); conjs.push_back(trm); } if (r.get_uninterpreted_tail_size() > 0) { conjs.push_back(m_bv.mk_ule(mk_q_one(), var)); } bool_rewriter(m).mk_and(conjs.size(), conjs.c_ptr(), rule_body); trm = m.mk_implies(rule_i, rule_body); trm = m.mk_forall(1, index_sorts, &tick, trm, 1); b.assert_expr(trm); } bool_rewriter(m).mk_or(rules.size(), rules.c_ptr(), trm); trm = m.mk_implies(pred, trm); trm = m.mk_forall(1, index_sorts, &tick, trm, 1); SASSERT(is_well_sorted(m, trm)); b.assert_expr(trm); } } void setup() { params_ref p; p.set_uint("smt.relevancy", 2ul); p.set_bool("smt.mbqi", true); b.m_solver->updt_params(p); b.m_rule_trace.reset(); } void mk_qrule_vars(datalog::rule const& r, unsigned rule_id, expr_ref_vector& sub) { ptr_vector sorts; r.get_vars(m, sorts); // populate substitution of bound variables. sub.reset(); sub.resize(sorts.size()); for (unsigned k = 0; k < r.get_decl()->get_arity(); ++k) { expr* arg = r.get_head()->get_arg(k); if (is_var(arg)) { unsigned idx = to_var(arg)->get_idx(); if (!sub[idx].get()) { sub[idx] = mk_q_arg(r.get_decl(), k, true); } } } for (unsigned j = 0; j < r.get_uninterpreted_tail_size(); ++j) { func_decl* q = r.get_decl(j); for (unsigned k = 0; k < q->get_arity(); ++k) { expr* arg = r.get_tail(j)->get_arg(k); if (is_var(arg)) { unsigned idx = to_var(arg)->get_idx(); if (!sub[idx].get()) { sub[idx] = mk_q_arg(q, k, false); } } } } for (unsigned j = 0, idx = 0; j < sorts.size(); ++j) { if (sorts[j] && !sub[j].get()) { sub[j] = mk_q_var(r.get_decl(), sorts[j], rule_id, idx++); } } } expr_ref mk_q_var(func_decl* pred, sort* s, unsigned rule_id, unsigned idx) { std::stringstream _name; _name << pred->get_name() << "#" << rule_id << "_" << idx; symbol nm(_name.str().c_str()); var_ref var = mk_index_var(); return expr_ref(m.mk_app(m.mk_func_decl(nm, mk_index_sort(), s), var), m); } expr_ref mk_q_arg(func_decl* pred, unsigned idx, bool is_current) { SASSERT(idx < pred->get_arity()); std::stringstream _name; _name << pred->get_name() << "#" << idx; symbol nm(_name.str().c_str()); expr_ref var(mk_index_var(), m); if (!is_current) { var = m_bv.mk_bv_sub(var, mk_q_one()); } return expr_ref(m.mk_app(m.mk_func_decl(nm, mk_index_sort(), pred->get_domain(idx)), var), m); } expr_ref mk_q_one() { return mk_q_num(1); } expr_ref mk_q_num(unsigned i) { return expr_ref(m_bv.mk_numeral(i, m_bit_width), m); } func_decl_ref mk_q_func_decl(func_decl* f) { std::stringstream _name; _name << f->get_name() << "#"; symbol nm(_name.str().c_str()); return func_decl_ref(m.mk_func_decl(nm, mk_index_sort(), f->get_range()), m); } func_decl_ref mk_q_rule(func_decl* f, unsigned rule_id) { std::stringstream _name; _name << f->get_name() << "#" << rule_id; symbol nm(_name.str().c_str()); return func_decl_ref(m.mk_func_decl(nm, mk_index_sort(), m.mk_bool_sort()), m); } expr_ref eval_q(model_ref& model, func_decl* f, unsigned i) { func_decl_ref fn = mk_q_func_decl(f); expr_ref t(m); t = m.mk_app(mk_q_func_decl(f).get(), mk_q_num(i)); return (*model)(t); } expr_ref eval_q(model_ref& model, expr* t, unsigned i) { expr_ref tmp(m), result(m), num(m); var_subst vs(m, false); num = mk_q_num(i); expr* nums[1] = { num }; tmp = vs(t, 1, nums); return (*model)(tmp); } lbool get_model() { rule_manager& rm = b.m_ctx.get_rule_manager(); func_decl_ref q = mk_q_func_decl(b.m_query_pred); expr_ref T(m), rule_i(m), vl(m); model_ref md; proof_ref pr(m); rule_unifier unifier(b.m_ctx); rational num; unsigned level, bv_size; b.m_solver->get_model(md); func_decl* pred = b.m_query_pred; dl_decl_util util(m); T = m.mk_const(symbol("T"), mk_index_sort()); vl = (*md)(T); VERIFY (m_bv.is_numeral(vl, num, bv_size)); SASSERT(num.is_unsigned()); level = num.get_unsigned(); SASSERT(m.is_true(eval_q(md, b.m_query_pred, level))); TRACE("bmc", model_smt2_pp(tout, m, *md, 0);); rule_ref r0(rm), r1(rm), r2(rm); while (true) { TRACE("bmc", tout << "Predicate: " << pred->get_name() << "\n";); expr_ref_vector sub(m); rule_vector const& rls = b.m_rules.get_predicate_rules(pred); rule* r = nullptr; unsigned i = 0; for (; i < rls.size(); ++i) { rule_i = m.mk_app(mk_q_rule(pred, i), mk_q_num(level).get()); TRACE("bmc", rls[i]->display(b.m_ctx, tout << "Checking rule " << mk_pp(rule_i, m) << " ");); if (m.is_true(eval_q(md, rule_i, level))) { r = rls[i]; break; } } SASSERT(r); mk_qrule_vars(*r, i, sub); b.m_rule_trace.push_back(r); // we have rule, we have variable names of rule. // extract values for the variables in the rule. for (unsigned j = 0; j < sub.size(); ++j) { expr_ref vl = eval_q(md, sub[j].get(), i); if (vl) { // vl can be 0 if the interpretation does not assign a value to it. sub[j] = vl; } else { sub[j] = m.mk_var(j, m.get_sort(sub[j].get())); } } svector > positions; vector substs; expr_ref fml(m), concl(m); rm.to_formula(*r, fml); r2 = r; rm.substitute(r2, sub.size(), sub.c_ptr()); proof_ref p(m); if (r0) { VERIFY(unifier.unify_rules(*r0.get(), 0, *r2.get())); expr_ref_vector sub1 = unifier.get_rule_subst(*r0.get(), true); expr_ref_vector sub2 = unifier.get_rule_subst(*r2.get(), false); apply_subst(sub, sub2); unifier.apply(*r0.get(), 0, *r2.get(), r1); rm.to_formula(*r1.get(), concl); scoped_proof _sp(m); p = r->get_proof(); if (!p) { p = m.mk_asserted(fml); } proof* premises[2] = { pr, p }; positions.push_back(std::make_pair(0, 1)); substs.push_back(sub1); substs.push_back(sub); pr = m.mk_hyper_resolve(2, premises, concl, positions, substs); r0 = r1; } else { rm.to_formula(*r, concl); scoped_proof _sp(m); p = r->get_proof(); if (!p) { p = m.mk_asserted(fml); } if (sub.empty()) { pr = p; } else { substs.push_back(sub); proof* ps[1] = { p }; pr = m.mk_hyper_resolve(1, ps, concl, positions, substs); } r0 = r2; } if (level == 0) { SASSERT(r->get_uninterpreted_tail_size() == 0); break; } --level; SASSERT(r->get_uninterpreted_tail_size() == 1); pred = r->get_decl(0); } scoped_proof _sp(m); apply(m, b.m_ctx.get_proof_converter().get(), pr); b.m_answer = pr; return l_true; } }; // -------------------------------------------------------------------------- // Basic non-linear BMC based on compiling into quantifiers. class bmc::nonlinear { bmc& b; ast_manager& m; public: nonlinear(bmc& b): b(b), m(b.m) {} lbool check() { setup(); for (unsigned i = 0; ; ++i) { IF_VERBOSE(1, verbose_stream() << "level: " << i << "\n";); b.checkpoint(); expr_ref_vector fmls(m); compile(b.m_rules, fmls, i); assert_fmls(fmls); lbool res = check(i); if (res == l_undef) { return res; } if (res == l_true) { get_model(i); return res; } } } expr_ref compile_query(func_decl* query_pred, unsigned level) { expr_ref_vector vars(m); func_decl_ref level_p = mk_level_predicate(query_pred, level); for (unsigned i = 0; i < level_p->get_arity(); ++i) { std::stringstream _name; _name << query_pred->get_name() << "#" << level << "_" << i; symbol nm(_name.str().c_str()); vars.push_back(m.mk_const(nm, level_p->get_domain(i))); } return expr_ref(m.mk_app(level_p, vars.size(), vars.c_ptr()), m); } void compile(rule_set const& rules, expr_ref_vector& result, unsigned level) { bool_rewriter br(m); rule_set::decl2rules::iterator it = rules.begin_grouped_rules(); rule_set::decl2rules::iterator end = rules.end_grouped_rules(); for (; it != end; ++it) { func_decl* p = it->m_key; rule_vector const& rls = *it->m_value; // Assert: p_level(vars) => r1_level(vars) \/ r2_level(vars) \/ r3_level(vars) \/ ... // Assert: r_i_level(vars) => exists aux_vars . body of rule i for level func_decl_ref level_pred = mk_level_predicate(p, level); expr_ref_vector rules(m); expr_ref body(m), head(m); for (unsigned i = 0; i < rls.size(); ++i) { rule& r = *rls[i]; func_decl_ref rule_i = mk_level_rule(p, i, level); rules.push_back(apply_vars(rule_i)); ptr_vector rule_vars; expr_ref_vector args(m), conjs(m); r.get_vars(m, rule_vars); obj_hashtable used_vars; unsigned num_vars = 0; for (unsigned i = 0; i < r.get_decl()->get_arity(); ++i) { expr* arg = r.get_head()->get_arg(i); if (is_var(arg) && !used_vars.contains(arg)) { used_vars.insert(arg); args.push_back(arg); rule_vars[to_var(arg)->get_idx()] = 0; } else { sort* srt = m.get_sort(arg); args.push_back(m.mk_var(rule_vars.size()+num_vars, srt)); conjs.push_back(m.mk_eq(args.back(), arg)); ++num_vars; } } head = m.mk_app(rule_i, args.size(), args.c_ptr()); for (unsigned i = 0; i < r.get_tail_size(); ++i) { conjs.push_back(r.get_tail(i)); } br.mk_and(conjs.size(), conjs.c_ptr(), body); replace_by_level_predicates(level, body); body = skolemize_vars(r, args, rule_vars, body); body = m.mk_implies(head, body); body = bind_vars(body, head); result.push_back(body); } br.mk_or(rules.size(), rules.c_ptr(), body); head = apply_vars(level_pred); body = m.mk_implies(head, body); body = bind_vars(body, head); result.push_back(body); } } private: void assert_fmls(expr_ref_vector const& fmls) { for (unsigned i = 0; i < fmls.size(); ++i) { b.assert_expr(fmls[i]); } } void setup() { params_ref p; p.set_uint("smt.relevancy", 2ul); b.m_solver->updt_params(p); b.m_rule_trace.reset(); } lbool check(unsigned level) { expr_ref p = compile_query(b.m_query_pred, level); expr_ref q(m), q_at_level(m); q = m.mk_fresh_const("q",m.mk_bool_sort()); q_at_level = m.mk_implies(q, p); b.assert_expr(q_at_level); expr* qr = q.get(); return b.m_solver->check_sat(1, &qr); } proof_ref get_proof(model_ref& md, func_decl* pred, app* prop, unsigned level) { if (m.canceled()) { return proof_ref(nullptr, m); } TRACE("bmc", tout << "Predicate: " << pred->get_name() << "\n";); rule_manager& rm = b.m_ctx.get_rule_manager(); expr_ref prop_r(m), prop_v(m), fml(m), prop_body(m), tmp(m), body(m); expr_ref_vector args(m); proof_ref_vector prs(m); proof_ref pr(m); // find the rule that was triggered by evaluating the arguments to prop. rule_vector const& rls = b.m_rules.get_predicate_rules(pred); rule* r = nullptr; for (unsigned i = 0; i < rls.size(); ++i) { func_decl_ref rule_i = mk_level_rule(pred, i, level); TRACE("bmc", rls[i]->display(b.m_ctx, tout << "Checking rule " << mk_pp(rule_i, m) << " ");); prop_r = m.mk_app(rule_i, prop->get_num_args(), prop->get_args()); if (md->is_true(prop_r)) { r = rls[i]; break; } } SASSERT(r); b.m_rule_trace.push_back(r); rm.to_formula(*r, fml); IF_VERBOSE(1, verbose_stream() << mk_pp(fml, m) << "\n";); prs.push_back(r->get_proof()); unsigned sz = r->get_uninterpreted_tail_size(); ptr_vector rule_vars; r->get_vars(m, rule_vars); args.append(prop->get_num_args(), prop->get_args()); expr_ref_vector sub = mk_skolem_binding(*r, rule_vars, args); if (sub.empty() && sz == 0) { pr = prs[0].get(); return pr; } for (unsigned j = 0; j < sub.size(); ++j) { sub[j] = (*md)(sub[j].get()); } svector > positions; vector substs; var_subst vs(m, false); substs.push_back(sub); for (unsigned j = 0; j < sz; ++j) { func_decl* head_j = r->get_decl(j); app* body_j = r->get_tail(j); prop_body = vs(body_j, sub.size(), sub.c_ptr()); prs.push_back(get_proof(md, head_j, to_app(prop_body), level-1)); positions.push_back(std::make_pair(j+1,0)); substs.push_back(expr_ref_vector(m)); } pr = m.mk_hyper_resolve(sz+1, prs.c_ptr(), prop, positions, substs); return pr; } void get_model(unsigned level) { scoped_proof _sp(m); expr_ref level_query = compile_query(b.m_query_pred, level); model_ref md; b.m_solver->get_model(md); IF_VERBOSE(2, model_smt2_pp(verbose_stream(), m, *md, 0);); proof_ref pr(m); pr = get_proof(md, b.m_query_pred, to_app(level_query), level); apply(m, b.m_ctx.get_proof_converter().get(), pr); b.m_answer = pr; } func_decl_ref mk_level_predicate(func_decl* p, unsigned level) { std::stringstream _name; _name << p->get_name() << "#" << level; symbol nm(_name.str().c_str()); return func_decl_ref(m.mk_func_decl(nm, p->get_arity(), p->get_domain(), m.mk_bool_sort()), m); } func_decl_ref mk_level_rule(func_decl* p, unsigned rule_idx, unsigned level) { std::stringstream _name; _name << "rule:" << p->get_name() << "#" << level << "_" << rule_idx; symbol nm(_name.str().c_str()); return func_decl_ref(m.mk_func_decl(nm, p->get_arity(), p->get_domain(), m.mk_bool_sort()), m); } expr_ref apply_vars(func_decl* p) { expr_ref_vector vars(m); for (unsigned i = 0; i < p->get_arity(); ++i) { vars.push_back(m.mk_var(i, p->get_domain(i))); } return expr_ref(m.mk_app(p, vars.size(), vars.c_ptr()), m); } // remove variables from dst that are in src. void subtract_vars(ptr_vector& dst, ptr_vector const& src) { for (unsigned i = 0; i < dst.size(); ++i) { if (i >= src.size()) { break; } if (src[i]) { dst[i] = 0; } } } expr_ref_vector mk_skolem_binding(rule& r, ptr_vector const& vars, expr_ref_vector const& args) { expr_ref_vector binding(m); ptr_vector arg_sorts; for (unsigned i = 0; i < args.size(); ++i) { arg_sorts.push_back(m.get_sort(args[i])); } for (unsigned i = 0; i < vars.size(); ++i) { if (vars[i]) { func_decl_ref f = mk_body_func(r, arg_sorts, i, vars[i]); binding.push_back(m.mk_app(f, args.size(), args.c_ptr())); } else { binding.push_back(nullptr); } } return binding; } expr_ref skolemize_vars(rule& r, expr_ref_vector const& args, ptr_vector const& vars, expr* e) { expr_ref_vector binding = mk_skolem_binding(r, vars, args); var_subst vs(m, false); return vs(e, binding.size(), binding.c_ptr()); } func_decl_ref mk_body_func(rule& r, ptr_vector const& args, unsigned index, sort* s) { std::stringstream _name; _name << r.get_decl()->get_name() << "@" << index; symbol name(_name.str().c_str()); func_decl* f = m.mk_func_decl(name, args.size(), args.c_ptr(), s); return func_decl_ref(f, m); } expr_ref bind_vars(expr* e, expr* pat) { ptr_vector sorts; svector names; expr_ref_vector binding(m), patterns(m); expr_ref tmp(m), head(m); expr_free_vars vars; vars(e); for (unsigned i = 0; i < vars.size(); ++i) { if (vars[i]) { binding.push_back(m.mk_var(sorts.size(), vars[i])); sorts.push_back(vars[i]); names.push_back(symbol(i)); } else { binding.push_back(nullptr); } } sorts.reverse(); if (sorts.empty()) { return expr_ref(e, m); } var_subst vs(m, false); tmp = vs(e, binding.size(), binding.c_ptr()); head = vs(pat, binding.size(), binding.c_ptr()); patterns.push_back(m.mk_pattern(to_app(head))); symbol qid, skid; return expr_ref(m.mk_forall(sorts.size(), sorts.c_ptr(), names.c_ptr(), tmp, 1, qid, skid, 1, patterns.c_ptr()), m); } public: class level_replacer { nonlinear& n; unsigned m_level; bool is_predicate(func_decl* f) { return n.b.m_ctx.is_predicate(f); } public: level_replacer(nonlinear& n, unsigned level): n(n), m_level(level) {} br_status mk_app_core(func_decl* f, unsigned num_args, expr* const* args, expr_ref& result) { if (is_predicate(f)) { if (m_level > 0) { result = n.m.mk_app(n.mk_level_predicate(f, m_level-1), num_args, args); } else { result = n.m.mk_false(); } return BR_DONE; } return BR_FAILED; } bool reduce_quantifier(quantifier* old_q, expr* new_body, expr_ref& result) { if (is_ground(new_body)) { result = new_body; } else { expr * const * no_pats = &new_body; result = n.m.update_quantifier(old_q, 0, nullptr, 1, no_pats, new_body); } return true; } }; struct level_replacer_cfg : public default_rewriter_cfg { level_replacer m_r; level_replacer_cfg(nonlinear& nl, unsigned level): m_r(nl, level) {} 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) { return m_r.mk_app_core(f, num, args, result); } 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) { return m_r.reduce_quantifier(old_q, new_body, result); } }; class level_replacer_star : public rewriter_tpl { level_replacer_cfg m_cfg; public: level_replacer_star(nonlinear& nl, unsigned level): rewriter_tpl(nl.m, false, m_cfg), m_cfg(nl, level) {} }; private: void replace_by_level_predicates(unsigned level, expr_ref& fml) { level_replacer_star rep(*this, level); expr_ref tmp(m); rep(fml, tmp); fml = tmp; } }; // -------------------------------------------------------------------------- // Basic non-linear BMC based on compiling into data-types (it is inefficient) class bmc::nonlinear_dt { bmc& b; ast_manager& m; ast_ref_vector m_pinned; sort_ref m_path_sort; obj_map m_pred2sort; obj_map m_sort2pred; public: nonlinear_dt(bmc& b): b(b), m(b.m), m_pinned(m), m_path_sort(m) {} lbool check() { setup(); declare_datatypes(); compile(); return check_query(); } private: void setup() { m_pred2sort.reset(); m_pinned.reset(); m_sort2pred.reset(); params_ref p; p.set_uint("smt.relevancy", 2ul); p.set_bool("smt.mbqi", false); b.m_solver->updt_params(p); b.m_rule_trace.reset(); } func_decl_ref mk_predicate(func_decl* pred) { std::stringstream _name; _name << pred->get_name() << "#"; symbol nm(_name.str().c_str()); sort* pred_trace_sort = m_pred2sort.find(pred); return func_decl_ref(m.mk_func_decl(nm, pred_trace_sort, m_path_sort, m.mk_bool_sort()), m); } func_decl_ref mk_rule(func_decl* p, unsigned rule_idx) { std::stringstream _name; _name << "rule:" << p->get_name() << "#" << rule_idx; symbol nm(_name.str().c_str()); sort* pred_trace_sort = m_pred2sort.find(p); return func_decl_ref(m.mk_func_decl(nm, pred_trace_sort, m_path_sort, m.mk_bool_sort()), m); } expr_ref mk_var(func_decl* pred, sort*s, unsigned idx, expr* path_arg, expr* trace_arg) { std::stringstream _name; _name << pred->get_name() << "#V_" << idx; symbol nm(_name.str().c_str()); func_decl_ref fn(m); fn = m.mk_func_decl(nm, m_pred2sort.find(pred), m_path_sort, s); return expr_ref(m.mk_app(fn, trace_arg, path_arg), m); } expr_ref mk_arg(func_decl* pred, unsigned idx, expr* path_arg, expr* trace_arg) { SASSERT(idx < pred->get_arity()); std::stringstream _name; _name << pred->get_name() << "#X_" << idx; symbol nm(_name.str().c_str()); func_decl_ref fn(m); fn = m.mk_func_decl(nm, m_pred2sort.find(pred), m_path_sort, pred->get_domain(idx)); return expr_ref(m.mk_app(fn, trace_arg, path_arg), m); } void mk_subst(datalog::rule& r, expr* path, app* trace, expr_ref_vector& sub) { datatype_util dtu(m); ptr_vector sorts; func_decl* p = r.get_decl(); ptr_vector const& succs = *dtu.get_datatype_constructors(m.get_sort(path)); // populate substitution of bound variables. r.get_vars(m, sorts); sub.reset(); sub.resize(sorts.size()); for (unsigned k = 0; k < r.get_decl()->get_arity(); ++k) { expr* arg = r.get_head()->get_arg(k); if (is_var(arg)) { unsigned idx = to_var(arg)->get_idx(); if (!sub[idx].get()) { sub[idx] = mk_arg(p, k, path, trace); } } } for (unsigned j = 0; j < r.get_uninterpreted_tail_size(); ++j) { func_decl* q = r.get_decl(j); expr_ref path_arg(m); if (j == 0) { path_arg = path; } else { path_arg = m.mk_app(succs[j], path); } for (unsigned k = 0; k < q->get_arity(); ++k) { expr* arg = r.get_tail(j)->get_arg(k); if (is_var(arg)) { unsigned idx = to_var(arg)->get_idx(); if (!sub[idx].get()) { sub[idx] = mk_arg(q, k, path_arg, trace->get_arg(j)); } } } } for (unsigned j = 0, idx = 0; j < sorts.size(); ++j) { if (sorts[j] && !sub[j].get()) { sub[j] = mk_var(r.get_decl(), sorts[j], idx++, path, trace); } } } /** \brief compile Horn rule into co-Horn implication. forall args . R(path_var, rule_i(trace_vars)) => Body[X(path_var, rule_i(trace_vars)), Y(S_j(path_var), trace_vars_j)] */ void compile() { datatype_util dtu(m); rule_set::decl2rules::iterator it = b.m_rules.begin_grouped_rules(); rule_set::decl2rules::iterator end = b.m_rules.end_grouped_rules(); for (; it != end; ++it) { func_decl* p = it->m_key; rule_vector const& rls = *it->m_value; // Assert: p_level => r1_level \/ r2_level \/ r3_level \/ ... // where: r_i_level = body of rule i for level + equalities for head of rule i expr_ref rule_body(m), tmp(m), pred(m), trace_arg(m), fml(m); var_ref path_var(m), trace_var(m); expr_ref_vector rules(m), sub(m), conjs(m), vars(m), patterns(m); sort* pred_sort = m_pred2sort.find(p); path_var = m.mk_var(0, m_path_sort); trace_var = m.mk_var(1, pred_sort); // sort* sorts[2] = { pred_sort, m_path_sort }; ptr_vector const& cnstrs = *dtu.get_datatype_constructors(pred_sort); ptr_vector const& succs = *dtu.get_datatype_constructors(m_path_sort); SASSERT(cnstrs.size() == rls.size()); pred = m.mk_app(mk_predicate(p), trace_var.get(), path_var.get()); for (unsigned i = 0; i < rls.size(); ++i) { sub.reset(); conjs.reset(); vars.reset(); rule& r = *rls[i]; func_decl_ref rule_pred_i = mk_rule(p, i); // Create cnstr_rule_i(Vars) func_decl* cnstr = cnstrs[i]; rules.push_back(m.mk_app(rule_pred_i, trace_var.get(), path_var.get())); unsigned arity = cnstr->get_arity(); for (unsigned j = 0; j < arity; ++j) { vars.push_back(m.mk_var(arity-j,cnstr->get_domain(j))); } trace_arg = m.mk_app(cnstr, vars.size(), vars.c_ptr()); mk_subst(r, path_var, to_app(trace_arg), sub); // apply substitution to body. var_subst vs(m, false); for (unsigned k = 0; k < p->get_arity(); ++k) { tmp = vs(r.get_head()->get_arg(k), sub.size(), sub.c_ptr()); expr_ref arg = mk_arg(p, k, path_var, trace_arg); conjs.push_back(m.mk_eq(tmp, arg)); } for (unsigned j = 0; j < r.get_uninterpreted_tail_size(); ++j) { expr_ref path_arg(m); if (j == 0) { path_arg = path_var.get(); } else { path_arg = m.mk_app(succs[j], path_var.get()); } func_decl* q = r.get_decl(j); for (unsigned k = 0; k < q->get_arity(); ++k) { tmp = vs(r.get_tail(j)->get_arg(k), sub.size(), sub.c_ptr()); expr_ref arg = mk_arg(q, k, path_arg, vars[j].get()); conjs.push_back(m.mk_eq(tmp, arg)); } func_decl_ref q_pred = mk_predicate(q); conjs.push_back(m.mk_app(q_pred, vars[j].get(), path_arg)); } for (unsigned j = r.get_uninterpreted_tail_size(); j < r.get_tail_size(); ++j) { tmp = vs(r.get_tail(j), sub.size(), sub.c_ptr()); conjs.push_back(tmp); } bool_rewriter(m).mk_and(conjs.size(), conjs.c_ptr(), rule_body); ptr_vector q_sorts; vector names; for (unsigned i = 0; i < vars.size(); ++i) { q_sorts.push_back(m.get_sort(vars[i].get())); names.push_back(symbol(i+1)); } vars.push_back(path_var); q_sorts.push_back(m.get_sort(path_var)); names.push_back(symbol("path")); SASSERT(names.size() == q_sorts.size()); SASSERT(vars.size() == names.size()); symbol qid = r.name(), skid; tmp = m.mk_app(mk_predicate(p), trace_arg.get(), path_var.get()); patterns.reset(); patterns.push_back(m.mk_pattern(to_app(tmp))); fml = m.mk_implies(tmp, rule_body); fml = m.mk_forall(vars.size(), q_sorts.c_ptr(), names.c_ptr(), fml, 1, qid, skid, 1, patterns.c_ptr()); b.assert_expr(fml); } } } void declare_datatypes() { rule_set::decl2rules::iterator it = b.m_rules.begin_grouped_rules(); rule_set::decl2rules::iterator end = b.m_rules.end_grouped_rules(); datatype_util dtu(m); ptr_vector dts; obj_map pred_idx; for (unsigned i = 0; it != end; ++it, ++i) { pred_idx.insert(it->m_key, i); } it = b.m_rules.begin_grouped_rules(); for (; it != end; ++it) { rule_vector const& rls = *it->m_value; func_decl* pred = it->m_key; ptr_vector cnstrs; for (unsigned i = 0; i < rls.size(); ++i) { rule* r = rls[i]; ptr_vector accs; for (unsigned j = 0; j < r->get_uninterpreted_tail_size(); ++j) { func_decl* q = r->get_decl(j); unsigned idx = pred_idx.find(q); std::stringstream _name; _name << pred->get_name() << "_" << q->get_name() << j; symbol name(_name.str().c_str()); type_ref tr(idx); accs.push_back(mk_accessor_decl(m, name, tr)); } std::stringstream _name; _name << pred->get_name() << "_" << i; symbol name(_name.str().c_str()); _name << "?"; symbol is_name(_name.str().c_str()); cnstrs.push_back(mk_constructor_decl(name, is_name, accs.size(), accs.c_ptr())); } dts.push_back(mk_datatype_decl(dtu, pred->get_name(), 0, nullptr, cnstrs.size(), cnstrs.c_ptr())); } sort_ref_vector new_sorts(m); family_id dfid = m.mk_family_id("datatype"); datatype_decl_plugin* dtp = static_cast(m.get_plugin(dfid)); VERIFY (dtp->mk_datatypes(dts.size(), dts.c_ptr(), 0, nullptr, new_sorts)); it = b.m_rules.begin_grouped_rules(); for (unsigned i = 0; it != end; ++it, ++i) { m_pred2sort.insert(it->m_key, new_sorts[i].get()); m_sort2pred.insert(new_sorts[i].get(), it->m_key); m_pinned.push_back(new_sorts[i].get()); } if (!new_sorts.empty()) { TRACE("bmc", dtu.display_datatype(new_sorts[0].get(), tout);); } del_datatype_decls(dts.size(), dts.c_ptr()); // declare path data-type. { new_sorts.reset(); dts.reset(); ptr_vector cnstrs; unsigned max_arity = 0; rule_set::iterator it = b.m_rules.begin(); rule_set::iterator end = b.m_rules.end(); for (; it != end; ++it) { rule* r = *it; unsigned sz = r->get_uninterpreted_tail_size(); max_arity = std::max(sz, max_arity); } cnstrs.push_back(mk_constructor_decl(symbol("Z#"), symbol("Z#?"), 0, nullptr)); for (unsigned i = 0; i + 1 < max_arity; ++i) { std::stringstream _name; _name << "succ#" << i; symbol name(_name.str().c_str()); _name << "?"; symbol is_name(_name.str().c_str()); std::stringstream _name2; _name2 << "get_succ#" << i; ptr_vector accs; type_ref tr(0); accs.push_back(mk_accessor_decl(m, name, tr)); cnstrs.push_back(mk_constructor_decl(name, is_name, accs.size(), accs.c_ptr())); } dts.push_back(mk_datatype_decl(dtu, symbol("Path"), 0, nullptr, cnstrs.size(), cnstrs.c_ptr())); VERIFY (dtp->mk_datatypes(dts.size(), dts.c_ptr(), 0, nullptr, new_sorts)); m_path_sort = new_sorts[0].get(); } } proof_ref get_proof(model_ref& md, app* trace, app* path) { datatype_util dtu(m); rule_manager& rm = b.m_ctx.get_rule_manager(); sort* trace_sort = m.get_sort(trace); func_decl* p = m_sort2pred.find(trace_sort); datalog::rule_vector const& rules = b.m_rules.get_predicate_rules(p); ptr_vector const& cnstrs = *dtu.get_datatype_constructors(trace_sort); ptr_vector const& succs = *dtu.get_datatype_constructors(m_path_sort); for (unsigned i = 0; i < cnstrs.size(); ++i) { if (trace->get_decl() == cnstrs[i]) { svector > positions; scoped_proof _sc(m); proof_ref_vector prs(m); expr_ref_vector sub(m); vector substs; proof_ref pr(m); expr_ref fml(m), head(m), tmp(m); app_ref path1(m); // var_subst vs(m, false); mk_subst(*rules[i], path, trace, sub); rm.to_formula(*rules[i], fml); prs.push_back(rules[i]->get_proof()); unsigned sz = trace->get_num_args(); if (sub.empty() && sz == 0) { pr = prs[0].get(); return pr; } for (unsigned j = 0; j < sub.size(); ++j) { sub[j] = (*md)(sub.get(j)); } rule_ref rl(b.m_ctx.get_rule_manager()); rl = rules[i]; b.m_ctx.get_rule_manager().substitute(rl, sub.size(), sub.c_ptr()); substs.push_back(sub); for (unsigned j = 0; j < sz; ++j) { if (j == 0) { path1 = path; } else { path1 = m.mk_app(succs[j], path); } prs.push_back(get_proof(md, to_app(trace->get_arg(j)), path1)); positions.push_back(std::make_pair(j+1,0)); substs.push_back(expr_ref_vector(m)); } head = rl->get_head(); pr = m.mk_hyper_resolve(sz+1, prs.c_ptr(), head, positions, substs); b.m_rule_trace.push_back(rl.get()); return pr; } } UNREACHABLE(); return proof_ref(nullptr, m); } // instantiation of algebraic data-types takes care of the rest. lbool check_query() { sort* trace_sort = m_pred2sort.find(b.m_query_pred); func_decl_ref q = mk_predicate(b.m_query_pred); expr_ref trace(m), path(m), fml(m); trace = m.mk_const(symbol("trace"), trace_sort); path = m.mk_const(symbol("path"), m_path_sort); fml = m.mk_app(q, trace.get(), path.get()); b.assert_expr(fml); while (true) { lbool is_sat = b.m_solver->check_sat(0, nullptr); model_ref md; if (is_sat == l_false) { return is_sat; } b.m_solver->get_model(md); mk_answer(md, trace, path); return l_true; } } bool check_model(model_ref& md, expr* trace) { expr_ref trace_val(m), eq(m); trace_val = (*md)(trace); eq = m.mk_eq(trace, trace_val); b.m_solver->push(); b.m_solver->assert_expr(eq); lbool is_sat = b.m_solver->check_sat(0, nullptr); if (is_sat != l_false) { b.m_solver->get_model(md); } b.m_solver->pop(1); if (is_sat == l_false) { IF_VERBOSE(1, verbose_stream() << "infeasible trace " << mk_pp(trace_val, m) << "\n";); eq = m.mk_not(eq); b.assert_expr(eq); } return is_sat != l_false; } void mk_answer(model_ref& md, expr_ref& trace, expr_ref& path) { IF_VERBOSE(2, model_smt2_pp(verbose_stream(), m, *md, 0);); trace = (*md)(trace); path = (*md)(path); IF_VERBOSE(2, verbose_stream() << mk_pp(trace, m) << "\n"; for (unsigned i = 0; i < b.m_solver->get_num_assertions(); ++i) { verbose_stream() << mk_pp(b.m_solver->get_assertion(i), m) << "\n"; }); scoped_proof _sp(m); proof_ref pr(m); pr = get_proof(md, to_app(trace), to_app(path)); apply(m, b.m_ctx.get_proof_converter().get(), pr); b.m_answer = pr; } }; // -------------------------------------------------------------------------- // Basic linear BMC based on incrementally unfolding the transition relation. class bmc::linear { bmc& b; ast_manager& m; public: linear(bmc& b): b(b), m(b.m) {} lbool check() { setup(); unsigned max_depth = b.m_ctx.get_params().bmc_linear_unrolling_depth(); for (unsigned i = 0; i < max_depth; ++i) { IF_VERBOSE(1, verbose_stream() << "level: " << i << "\n";); b.checkpoint(); compile(i); lbool res = check(i); if (res == l_undef) { return res; } if (res == l_true) { get_model(i); return res; } } return l_undef; } private: void get_model(unsigned level) { if (m.canceled()) { return; } rule_manager& rm = b.m_ctx.get_rule_manager(); expr_ref level_query = mk_level_predicate(b.m_query_pred, level); model_ref md; proof_ref pr(m); rule_unifier unifier(b.m_ctx); b.m_solver->get_model(md); func_decl* pred = b.m_query_pred; SASSERT(m.is_true(md->get_const_interp(to_app(level_query)->get_decl()))); TRACE("bmc", model_smt2_pp(tout, m, *md, 0);); rule_ref r0(rm), r1(rm), r2(rm); while (true) { TRACE("bmc", tout << "Predicate: " << pred->get_name() << "\n";); expr_ref_vector sub(m); rule_vector const& rls = b.m_rules.get_predicate_rules(pred); rule* r = nullptr; unsigned i = 0; for (; i < rls.size(); ++i) { expr_ref rule_i = mk_level_rule(pred, i, level); TRACE("bmc", rls[i]->display(b.m_ctx, tout << "Checking rule " << mk_pp(rule_i, m) << " ");); if (m.is_true(md->get_const_interp(to_app(rule_i)->get_decl()))) { r = rls[i]; break; } } SASSERT(r); b.m_rule_trace.push_back(r); mk_rule_vars(*r, level, i, sub); // we have rule, we have variable names of rule. // extract values for the variables in the rule. for (unsigned j = 0; j < sub.size(); ++j) { expr* vl = md->get_const_interp(to_app(sub[j].get())->get_decl()); if (vl) { // vl can be 0 if the interpretation does not assign a value to it. sub[j] = vl; } else { sub[j] = m.mk_var(j, m.get_sort(sub[j].get())); } } svector > positions; vector substs; expr_ref fml(m), concl(m); rm.to_formula(*r, fml); r2 = r; rm.substitute(r2, sub.size(), sub.c_ptr()); proof_ref p(m); { scoped_proof _sp(m); p = r->get_proof(); if (!p) { p = m.mk_asserted(fml); } } if (r0) { VERIFY(unifier.unify_rules(*r0.get(), 0, *r2.get())); expr_ref_vector sub1 = unifier.get_rule_subst(*r0.get(), true); expr_ref_vector sub2 = unifier.get_rule_subst(*r2.get(), false); apply_subst(sub, sub2); unifier.apply(*r0.get(), 0, *r2.get(), r1); rm.to_formula(*r1.get(), concl); scoped_proof _sp(m); proof* premises[2] = { pr, p }; positions.push_back(std::make_pair(0, 1)); substs.push_back(sub1); substs.push_back(sub); pr = m.mk_hyper_resolve(2, premises, concl, positions, substs); r0 = r1; } else { rm.to_formula(*r2.get(), concl); scoped_proof _sp(m); if (sub.empty()) { pr = p; } else { substs.push_back(sub); proof * ps[1] = { p }; pr = m.mk_hyper_resolve(1, ps, concl, positions, substs); } r0 = r2; } if (level == 0) { SASSERT(r->get_uninterpreted_tail_size() == 0); break; } --level; SASSERT(r->get_uninterpreted_tail_size() == 1); pred = r->get_decl(0); } scoped_proof _sp(m); apply(m, b.m_ctx.get_proof_converter().get(), pr); b.m_answer = pr; } void setup() { params_ref p; p.set_uint("smt.relevancy", 0ul); p.set_bool("smt.mbqi", false); b.m_solver->updt_params(p); b.m_rule_trace.reset(); } lbool check(unsigned level) { expr_ref level_query = mk_level_predicate(b.m_query_pred, level); expr* q = level_query.get(); return b.m_solver->check_sat(1, &q); } expr_ref mk_level_predicate(func_decl* p, unsigned level) { return mk_level_predicate(p->get_name(), level); } expr_ref mk_level_predicate(symbol const& name, unsigned level) { std::stringstream _name; _name << name << "#" << level; symbol nm(_name.str().c_str()); return expr_ref(m.mk_const(nm, m.mk_bool_sort()), m); } expr_ref mk_level_arg(func_decl* pred, unsigned idx, unsigned level) { SASSERT(idx < pred->get_arity()); std::stringstream _name; _name << pred->get_name() << "#" << level << "_" << idx; symbol nm(_name.str().c_str()); return expr_ref(m.mk_const(nm, pred->get_domain(idx)), m); } expr_ref mk_level_var(func_decl* pred, sort* s, unsigned rule_id, unsigned idx, unsigned level) { std::stringstream _name; _name << pred->get_name() << "#" << level << "_" << rule_id << "_" << idx; symbol nm(_name.str().c_str()); return expr_ref(m.mk_const(nm, s), m); } expr_ref mk_level_rule(func_decl* p, unsigned rule_idx, unsigned level) { std::stringstream _name; _name << "rule:" << p->get_name() << "#" << level << "_" << rule_idx; symbol nm(_name.str().c_str()); return expr_ref(m.mk_const(nm, m.mk_bool_sort()), m); } void mk_rule_vars(rule& r, unsigned level, unsigned rule_id, expr_ref_vector& sub) { ptr_vector sorts; r.get_vars(m, sorts); // populate substitution of bound variables. sub.reset(); sub.resize(sorts.size()); for (unsigned k = 0; k < r.get_decl()->get_arity(); ++k) { expr* arg = r.get_head()->get_arg(k); if (is_var(arg)) { unsigned idx = to_var(arg)->get_idx(); if (!sub[idx].get()) { sub[idx] = mk_level_arg(r.get_decl(), k, level); } } } for (unsigned j = 0; j < r.get_uninterpreted_tail_size(); ++j) { SASSERT(level > 0); func_decl* q = r.get_decl(j); for (unsigned k = 0; k < q->get_arity(); ++k) { expr* arg = r.get_tail(j)->get_arg(k); if (is_var(arg)) { unsigned idx = to_var(arg)->get_idx(); if (!sub[idx].get()) { sub[idx] = mk_level_arg(q, k, level-1); } } } } for (unsigned j = 0, idx = 0; j < sorts.size(); ++j) { if (sorts[j] && !sub[j].get()) { sub[j] = mk_level_var(r.get_decl(), sorts[j], rule_id, idx++, level); } } } void compile(unsigned level) { rule_set::decl2rules::iterator it = b.m_rules.begin_grouped_rules(); rule_set::decl2rules::iterator end = b.m_rules.end_grouped_rules(); for (; it != end; ++it) { func_decl* p = it->m_key; rule_vector const& rls = *it->m_value; // Assert: p_level => r1_level \/ r2_level \/ r3_level \/ ... // Assert: r_i_level => body of rule i for level + equalities for head of rule i expr_ref level_pred = mk_level_predicate(p, level); expr_ref_vector rules(m), sub(m), conjs(m); expr_ref rule_body(m), tmp(m); for (unsigned i = 0; i < rls.size(); ++i) { sub.reset(); conjs.reset(); rule& r = *rls[i]; expr_ref rule_i = mk_level_rule(p, i, level); rules.push_back(rule_i); if (level == 0 && r.get_uninterpreted_tail_size() > 0) { tmp = m.mk_not(rule_i); b.assert_expr(tmp); continue; } mk_rule_vars(r, level, i, sub); // apply substitution to body. var_subst vs(m, false); for (unsigned k = 0; k < p->get_arity(); ++k) { tmp = vs(r.get_head()->get_arg(k), sub.size(), sub.c_ptr()); conjs.push_back(m.mk_eq(tmp, mk_level_arg(p, k, level))); } for (unsigned j = 0; j < r.get_uninterpreted_tail_size(); ++j) { SASSERT(level > 0); func_decl* q = r.get_decl(j); for (unsigned k = 0; k < q->get_arity(); ++k) { tmp = vs(r.get_tail(j)->get_arg(k), sub.size(), sub.c_ptr()); conjs.push_back(m.mk_eq(tmp, mk_level_arg(q, k, level-1))); } conjs.push_back(mk_level_predicate(q, level-1)); } for (unsigned j = r.get_uninterpreted_tail_size(); j < r.get_tail_size(); ++j) { tmp = vs(r.get_tail(j), sub.size(), sub.c_ptr()); conjs.push_back(tmp); } bool_rewriter(m).mk_and(conjs.size(), conjs.c_ptr(), rule_body); rule_body = m.mk_implies(rule_i, rule_body); b.assert_expr(rule_body); } bool_rewriter(m).mk_or(rules.size(), rules.c_ptr(), tmp); tmp = m.mk_implies(level_pred, tmp); b.assert_expr(tmp); } } }; bmc::bmc(context& ctx): engine_base(ctx.get_manager(), "bmc"), m_ctx(ctx), m(ctx.get_manager()), m_solver(nullptr), m_rules(ctx), m_query_pred(m), m_answer(m), m_rule_trace(ctx.get_rule_manager()) { } bmc::~bmc() {} lbool bmc::query(expr* query) { m_solver = nullptr; m_answer = nullptr; m_ctx.ensure_opened(); m_rules.reset(); datalog::rule_manager& rule_manager = m_ctx.get_rule_manager(); rule_set& rules0 = m_ctx.get_rules(); datalog::rule_set old_rules(rules0); rule_manager.mk_query(query, rules0); expr_ref bg_assertion = m_ctx.get_background_assertion(); apply_default_transformation(m_ctx); if (m_ctx.xform_slice()) { datalog::rule_transformer transformer(m_ctx); datalog::mk_slice* slice = alloc(datalog::mk_slice, m_ctx); transformer.register_plugin(slice); m_ctx.transform_rules(transformer); } const rule_set& rules = m_ctx.get_rules(); if (rules.get_output_predicates().empty()) { return l_false; } m_query_pred = rules.get_output_predicate(); m_rules.replace_rules(rules); m_rules.close(); m_ctx.reopen(); m_ctx.replace_rules(old_rules); checkpoint(); IF_VERBOSE(2, m_ctx.display_rules(verbose_stream());); params_ref p; if (m_rules.get_num_rules() == 0) { return l_false; } if (m_rules.get_predicate_rules(m_query_pred).empty()) { return l_false; } if (is_linear()) { if (m_ctx.get_engine() == QBMC_ENGINE) { m_solver = mk_smt_solver(m, p, symbol::null); qlinear ql(*this); return ql.check(); } else { if (m_rules.is_finite_domain()) { m_solver = mk_fd_solver(m, p); } else { m_solver = mk_smt_solver(m, p, symbol::null); } linear lin(*this); return lin.check(); } } else { m_solver = mk_smt_solver(m, p, symbol::null); IF_VERBOSE(0, verbose_stream() << "WARNING: non-linear BMC is highly inefficient\n";); nonlinear nl(*this); return nl.check(); } } void bmc::assert_expr(expr* e) { TRACE("bmc", tout << mk_pp(e, m) << "\n";); m_solver->assert_expr(e); } bool bmc::is_linear() const { unsigned sz = m_rules.get_num_rules(); for (unsigned i = 0; i < sz; ++i) { if (m_rules.get_rule(i)->get_uninterpreted_tail_size() > 1) { return false; } if (m_rules.get_rule_manager().has_quantifiers(*m_rules.get_rule(i))) { return false; } } return true; } void bmc::checkpoint() { if (m.canceled()) { throw default_exception(Z3_CANCELED_MSG); } } void bmc::display_certificate(std::ostream& out) const { out << mk_pp(m_answer, m) << "\n"; } void bmc::collect_statistics(statistics& st) const { m_solver->collect_statistics(st); } void bmc::reset_statistics() { // m_solver->reset_statistics(); } expr_ref bmc::get_answer() { return m_answer; } proof_ref bmc::get_proof() { return proof_ref(to_app(m_answer), m); } void bmc::get_rules_along_trace(datalog::rule_ref_vector& rules) { rules.append(m_rule_trace); } void bmc::compile(rule_set const& rules, expr_ref_vector& fmls, unsigned level) { nonlinear nl(*this); nl.compile(rules, fmls, level); } expr_ref bmc::compile_query(func_decl* query_pred, unsigned level) { nonlinear nl(*this); return nl.compile_query(query_pred, level); } }; template class rewriter_tpl; z3-z3-4.8.7/src/muz/bmc/dl_bmc_engine.h000066400000000000000000000030501356505360400175060ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: dl_bmc_engine.h Abstract: BMC engine for fixedpoint solver. Author: Nikolaj Bjorner (nbjorner) 2012-9-20 Revision History: --*/ #ifndef DL_BMC_ENGINE_H_ #define DL_BMC_ENGINE_H_ #include "util/params.h" #include "util/statistics.h" #include "ast/bv_decl_plugin.h" #include "solver/solver.h" namespace datalog { class context; class bmc : public engine_base { context& m_ctx; ast_manager& m; solver_ref m_solver; rule_set m_rules; func_decl_ref m_query_pred; expr_ref m_answer; rule_ref_vector m_rule_trace; void checkpoint(); class nonlinear_dt; class nonlinear; class qlinear; class linear; bool is_linear() const; void assert_expr(expr* e); public: bmc(context& ctx); ~bmc() override; lbool query(expr* query) override; void display_certificate(std::ostream& out) const override; void collect_statistics(statistics& st) const override; void reset_statistics() override; void get_rules_along_trace(datalog::rule_ref_vector& rules) override; expr_ref get_answer() override; proof_ref get_proof() override; // direct access to (new) non-linear compiler. void compile(rule_set const& rules, expr_ref_vector& fmls, unsigned level); expr_ref compile_query(func_decl* query_pred, unsigned level); }; }; #endif z3-z3-4.8.7/src/muz/clp/000077500000000000000000000000001356505360400146075ustar00rootroot00000000000000z3-z3-4.8.7/src/muz/clp/CMakeLists.txt000066400000000000000000000001451356505360400173470ustar00rootroot00000000000000z3_add_component(clp SOURCES clp_context.cpp COMPONENT_DEPENDENCIES muz transforms ) z3-z3-4.8.7/src/muz/clp/clp_context.cpp000066400000000000000000000156651356505360400176520ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: clp_context.cpp Abstract: Bounded CLP (symbolic simulation using Z3) context. Author: Nikolaj Bjorner (nbjorner) 2013-04-26 Revision History: --*/ #include "muz/clp/clp_context.h" #include "muz/base/dl_context.h" #include "ast/substitution/unifier.h" #include "ast/rewriter/var_subst.h" #include "ast/substitution/substitution.h" #include "smt/smt_kernel.h" #include "muz/transforms/dl_transforms.h" namespace datalog { class clp::imp { struct stats { stats() { reset(); } void reset() { memset(this, 0, sizeof(*this)); } unsigned m_num_unfold; unsigned m_num_no_unfold; unsigned m_num_subsumed; }; context& m_ctx; ast_manager& m; rule_manager& rm; smt_params m_fparams; smt::kernel m_solver; var_subst m_var_subst; expr_ref_vector m_ground; app_ref_vector m_goals; stats m_stats; public: imp(context& ctx): m_ctx(ctx), m(ctx.get_manager()), rm(ctx.get_rule_manager()), m_solver(m, m_fparams), // TBD: can be replaced by efficient BV solver. m_var_subst(m, false), m_ground(m), m_goals(m) { // m_fparams.m_relevancy_lvl = 0; m_fparams.m_mbqi = false; } ~imp() {} lbool query(expr* query) { m_ctx.ensure_opened(); m_solver.reset(); m_goals.reset(); rm.mk_query(query, m_ctx.get_rules()); apply_default_transformation(m_ctx); const rule_set& rules = m_ctx.get_rules(); if (rules.get_output_predicates().empty()) { return l_false; } func_decl *head_decl = rules.get_output_predicate(); rule_vector const& rv = rules.get_predicate_rules(head_decl); if (rv.empty()) { return l_false; } expr_ref head(rv[0]->get_head(), m); ground(head); m_goals.push_back(to_app(head)); return search(20, 0); } void reset_statistics() { m_stats.reset(); } void collect_statistics(statistics& st) const { //st.update("tab.num_unfold", m_stats.m_num_unfold); //st.update("tab.num_unfold_fail", m_stats.m_num_no_unfold); //st.update("tab.num_subsumed", m_stats.m_num_subsumed); } void display_certificate(std::ostream& out) const { expr_ref ans = get_answer(); out << mk_pp(ans, m) << "\n"; } expr_ref get_answer() const { return expr_ref(m.mk_true(), m); } private: void reset_ground() { m_ground.reset(); } void ground(expr_ref& e) { expr_free_vars fv; fv(e); if (m_ground.size() < fv.size()) { m_ground.resize(fv.size()); } for (unsigned i = 0; i < fv.size(); ++i) { if (fv[i] && !m_ground[i].get()) { m_ground[i] = m.mk_fresh_const("c", fv[i]); } } e = m_var_subst(e, m_ground.size(), m_ground.c_ptr()); } static bool rule_sort_fn(const rule *r1, const rule *r2) { return r1->get_uninterpreted_tail_size() < r2->get_uninterpreted_tail_size(); } lbool search(unsigned depth, unsigned index) { if (index == m_goals.size()) { return l_true; } if (depth == 0) { return l_undef; } IF_VERBOSE(1, verbose_stream() << "search " << depth << " " << index << "\n";); unsigned num_goals = m_goals.size(); app* head = m_goals[index].get(); rule_vector rules(m_ctx.get_rules().get_predicate_rules(head->get_decl())); std::stable_sort(rules.begin(), rules.end(), rule_sort_fn); lbool status = l_false; for (unsigned i = 0; i < rules.size(); ++i) { rule* r = rules[i]; m_solver.push(); reset_ground(); expr_ref tmp(m); tmp = r->get_head(); IF_VERBOSE(2, verbose_stream() << index << " " << mk_pp(tmp, m) << "\n";); ground(tmp); for (unsigned j = 0; j < head->get_num_args(); ++j) { expr_ref eq(m); eq = m.mk_eq(head->get_arg(j), to_app(tmp)->get_arg(j)); m_solver.assert_expr(eq); } for (unsigned j = r->get_uninterpreted_tail_size(); j < r->get_tail_size(); ++j) { tmp = r->get_tail(j); ground(tmp); m_solver.assert_expr(tmp); } lbool is_sat = m_solver.check(); switch (is_sat) { case l_false: break; case l_true: if (depth == 1 && (index+1 > m_goals.size() || r->get_uninterpreted_tail_size() > 0)) { status = l_undef; break; } for (unsigned j = 0; j < r->get_uninterpreted_tail_size(); ++j) { tmp = r->get_tail(j); ground(tmp); m_goals.push_back(to_app(tmp)); } switch(search(depth-1, index+1)) { case l_undef: status = l_undef; // fallthrough case l_false: m_goals.resize(num_goals); break; case l_true: return l_true; } break; case l_undef: status = l_undef; throw default_exception("undef"); } m_solver.pop(1); } return status; } proof_ref get_proof() const { return proof_ref(nullptr, m); } }; clp::clp(context& ctx): engine_base(ctx.get_manager(), "clp"), m_imp(alloc(imp, ctx)) { } clp::~clp() { dealloc(m_imp); } lbool clp::query(expr* query) { return m_imp->query(query); } void clp::reset_statistics() { m_imp->reset_statistics(); } void clp::collect_statistics(statistics& st) const { m_imp->collect_statistics(st); } void clp::display_certificate(std::ostream& out) const { m_imp->display_certificate(out); } expr_ref clp::get_answer() { return m_imp->get_answer(); } }; z3-z3-4.8.7/src/muz/clp/clp_context.h000066400000000000000000000015171356505360400173060ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: clp_context.h Abstract: Bounded CLP (symbolic simulation using Z3) context. Author: Nikolaj Bjorner (nbjorner) 2013-04-26 Revision History: --*/ #ifndef CLP_CONTEXT_H_ #define CLP_CONTEXT_H_ #include "ast/ast.h" #include "util/lbool.h" #include "util/statistics.h" #include "muz/base/dl_engine_base.h" namespace datalog { class context; class clp : public datalog::engine_base { class imp; imp* m_imp; public: clp(context& ctx); ~clp() override; lbool query(expr* query) override; void reset_statistics() override; void collect_statistics(statistics& st) const override; void display_certificate(std::ostream& out) const override; expr_ref get_answer() override; }; }; #endif z3-z3-4.8.7/src/muz/dataflow/000077500000000000000000000000001356505360400156325ustar00rootroot00000000000000z3-z3-4.8.7/src/muz/dataflow/CMakeLists.txt000066400000000000000000000001301356505360400203640ustar00rootroot00000000000000z3_add_component(dataflow SOURCES dataflow.cpp COMPONENT_DEPENDENCIES muz ) z3-z3-4.8.7/src/muz/dataflow/dataflow.cpp000066400000000000000000000005711356505360400201420ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: dataflow.cpp Abstract: Generic bottom-up and top-down data-flow engine for analysis of rule sets. Author: Henning Guenther (t-hennig) --*/ #include "muz/dataflow/dataflow.h" #include "muz/dataflow/reachability.h" namespace datalog { const reachability_info reachability_info::null_fact; } z3-z3-4.8.7/src/muz/dataflow/dataflow.h000066400000000000000000000216711356505360400176130ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: dataflow.h Abstract: Generic bottom-up and top-down data-flow engine for analysis of rule sets. Author: Henning Guenther (t-hennig) --*/ #ifndef DATAFLOW_H_ #define DATAFLOW_H_ #include "muz/base/dl_rule.h" #include "muz/base/dl_rule_set.h" #include "util/hashtable.h" #include "util/vector.h" namespace datalog { template class fact_reader; template class fact_writer; /* The structure of fact classes: class fact { public: typedef ... ctx_t; // Empty fact static fact null_fact; fact(); -- bottom // Init (Top down) void init_down(ctx_t& ctx, const rule* r); // Init (Bottom up) bool init_up(ctx_t& ctx, const rule* r); // Step (Bottom up) bool propagate_up(ctx_t& ctx, const rule* r, const fact_reader& tail_facts); // Step (Top down) void propagate_down(ctx_t& ctx, const rule* r, fact_writer& tail_facts) const; // Debugging void dump(ctx_t& ctx, std::ostream& outp) const; // Union void join(ctx_t& ctx, const Fact& oth); // Intersection void intersect(ctx_t& ctx, const Fact& oth); }; */ template class dataflow_engine { public: typedef map, ptr_eq > fact_db; typedef hashtable, ptr_eq > todo_set; typedef typename fact_db::iterator iterator; private: const rule_set& m_rules; fact_db m_facts; todo_set m_todo[2]; unsigned m_todo_idx; typename Fact::ctx_t& m_context; rule_set::decl2rules m_body2rules; void init_bottom_up() { for (rule* cur : m_rules) { for (unsigned i = 0; i < cur->get_uninterpreted_tail_size(); ++i) { func_decl *d = cur->get_decl(i); rule_set::decl2rules::obj_map_entry *e = m_body2rules.insert_if_not_there2(d, 0); if (!e->get_data().m_value) { e->get_data().m_value = alloc(ptr_vector); } e->get_data().m_value->push_back(cur); } if (cur->get_positive_tail_size() == 0) { func_decl *sym = cur->get_head()->get_decl(); bool new_info = m_facts.insert_if_not_there2(sym, Fact())->get_data().m_value.init_up(m_context, cur); if (new_info) { m_todo[m_todo_idx].insert(sym); } } } } void init_top_down() { for (func_decl* sym : m_rules.get_output_predicates()) { TRACE("dl", tout << sym->get_name() << "\n";); const rule_vector& output_rules = m_rules.get_predicate_rules(sym); for (rule* r : output_rules) { m_facts.insert_if_not_there2(sym, Fact())->get_data().m_value.init_down(m_context, r); m_todo[m_todo_idx].insert(sym); } } } void step_bottom_up() { for (func_decl* f : m_todo[m_todo_idx]) { ptr_vector * rules; if (!m_body2rules.find(f, rules)) continue; for (rule* r : *rules) { func_decl* head_sym = r->get_head()->get_decl(); fact_reader tail_facts(m_facts, r); bool new_info = m_facts.insert_if_not_there2(head_sym, Fact())->get_data().m_value.propagate_up(m_context, r, tail_facts); if (new_info) { m_todo[!m_todo_idx].insert(head_sym); } } } // Update todo list m_todo[m_todo_idx].reset(); m_todo_idx = !m_todo_idx; } void step_top_down() { for (func_decl* head_sym : m_todo[m_todo_idx]) { // We can't use a reference here because we are changing the map while using the reference const Fact head_fact = m_facts.get(head_sym, Fact::null_fact); const rule_vector& deps = m_rules.get_predicate_rules(head_sym); for (rule* trg_rule : deps) { fact_writer writer(m_facts, trg_rule, m_todo[!m_todo_idx]); // Generate new facts head_fact.propagate_down(m_context, trg_rule, writer); } } // Update todo list m_todo[m_todo_idx].reset(); m_todo_idx = !m_todo_idx; } bool done() const { return m_todo[m_todo_idx].empty(); } public: dataflow_engine(typename Fact::ctx_t& ctx, const rule_set& rules) : m_rules(rules), m_todo_idx(0), m_context(ctx) {} ~dataflow_engine() { for (auto & kv : m_body2rules) dealloc(kv.m_value); } void dump(std::ostream& outp) { obj_hashtable visited; for (rule const* r : m_rules) { func_decl* head_decl = r->get_decl(); obj_hashtable::entry *dummy; if (visited.insert_if_not_there_core(head_decl, dummy)) { const Fact& fact = m_facts.get(head_decl, Fact::null_fact); outp << head_decl->get_name() << " -> "; fact.dump(m_context, outp); outp << "\n"; } for (unsigned i = 0; i < r->get_uninterpreted_tail_size(); ++i) { func_decl *tail_decl = r->get_decl(i); if (visited.insert_if_not_there_core(tail_decl, dummy)) { const Fact& fact = m_facts.get(tail_decl, Fact::null_fact); outp << tail_decl->get_name() << " -> "; fact.dump(m_context, outp); outp << "\n"; } } } } void run_bottom_up() { init_bottom_up(); while (!done()) step_bottom_up(); } void run_top_down() { init_top_down(); while (!done()) step_top_down(); } const Fact& get_fact(func_decl* decl) const { return m_facts.get(decl, Fact::null_fact); } iterator begin() const { return m_facts.begin(); } iterator end() const { return m_facts.end(); } void join(const dataflow_engine& oth) { for (auto const& kv : oth.m_facts) { typename fact_db::entry* entry; if (m_facts.insert_if_not_there_core(kv.m_key, entry)) { entry->get_data().m_value = kv.m_value; } else { entry->get_data().m_value.join(m_context, kv.m_value); } } } void intersect(const dataflow_engine& oth) { ptr_vector to_delete; for (auto const& kv : m_facts) { if (typename fact_db::entry *entry = oth.m_facts.find_core(kv.m_key)) { kv.m_value.intersect(m_context, entry->get_data().m_value); } else { to_delete.push_back(kv.m_key); } } for (func_decl* f : to_delete) { m_facts.erase(f); } } }; // This helper-class is used to look up facts for rule tails template class fact_reader { typedef typename dataflow_engine::fact_db fact_db; const fact_db& m_facts; const rule* m_rule; public: fact_reader(const fact_db& facts, const rule* r) : m_facts(facts), m_rule(r) { } const Fact& get(unsigned idx) const { return m_facts.get(m_rule->get_decl(idx), Fact::null_fact); } unsigned size() const { return m_rule->get_positive_tail_size(); } }; template class fact_writer { friend class dataflow_engine; typedef typename dataflow_engine::fact_db fact_db; fact_db& m_facts; const rule* m_rule; typename dataflow_engine::todo_set& m_todo; public: fact_writer(fact_db& facts, const rule* r, typename dataflow_engine::todo_set& todo) : m_facts(facts), m_rule(r), m_todo(todo) {} Fact& get(unsigned idx) { func_decl *sym = m_rule->get_decl(idx); return m_facts.insert_if_not_there2(sym, Fact())->get_data().m_value; } void set_changed(unsigned idx) { m_todo.insert(m_rule->get_decl(idx)); } unsigned size() const { return m_rule->get_uninterpreted_tail_size(); } }; } #endif z3-z3-4.8.7/src/muz/dataflow/reachability.h000066400000000000000000000042341356505360400204460ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: reachability.h Abstract: Abstract domain for tracking rule reachability. Author: Henning Guenther (t-hennig) --*/ #ifndef REACHABILITY_H_ #define REACHABILITY_H_ #include "muz/dataflow/dataflow.h" namespace datalog { class reachability_info { bool m_reachable; reachability_info(bool r) : m_reachable(r) {} public: typedef ast_manager ctx_t; static const reachability_info null_fact; reachability_info() : m_reachable(false) {} void init_down(const ctx_t& m, const rule* r) { m_reachable = true; } bool init_up(const ctx_t& m, const rule* r) { if (m_reachable) return false; else { m_reachable = true; return true; } } void propagate_down(const ctx_t& manager, const rule* r, fact_writer& tail_facts) const { SASSERT(m_reachable); for (unsigned i = 0; i < r->get_uninterpreted_tail_size(); ++i) { reachability_info& tail_fact = tail_facts.get(i); if (!tail_fact.m_reachable) { tail_fact.m_reachable = true; tail_facts.set_changed(i); } } } bool propagate_up(const ctx_t& manager, const rule* r, const fact_reader& tail_facts) { if (m_reachable) return false; for (unsigned i = 0; i < r->get_positive_tail_size(); ++i) { if (!tail_facts.get(i).m_reachable) { return false; } } m_reachable = true; return true; } void join(const ctx_t& manager, const reachability_info& oth) { m_reachable |= oth.m_reachable; } void dump(const ctx_t& manager, std::ostream& outp) const { outp << (m_reachable ? "reachable" : "unreachable"); } bool is_reachable() const { return m_reachable; } }; typedef dataflow_engine reachability; } #endif z3-z3-4.8.7/src/muz/ddnf/000077500000000000000000000000001356505360400147445ustar00rootroot00000000000000z3-z3-4.8.7/src/muz/ddnf/CMakeLists.txt000066400000000000000000000001471356505360400175060ustar00rootroot00000000000000z3_add_component(ddnf SOURCES ddnf.cpp COMPONENT_DEPENDENCIES muz rel transforms ) z3-z3-4.8.7/src/muz/ddnf/ddnf.cpp000066400000000000000000000721551356505360400163750ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: ddnf.cpp Abstract: DDNF based engine. Author: Nikolaj Bjorner (nbjorner) 2014-08-21 Revision History: - inherits from Nuno Lopes Hassel utilities and Garvit Juniwal's DDNF engine. --*/ #include "muz/ddnf/ddnf.h" #include "muz/base/dl_rule_set.h" #include "muz/base/dl_context.h" #include "ast/scoped_proof.h" #include "ast/bv_decl_plugin.h" #include "muz/rel/tbv.h" namespace datalog { class ddnf_mgr; class ddnf_node; typedef ref_vector ddnf_node_vector; class ddnf_node { public: struct eq { tbv_manager& m; eq(tbv_manager& m):m(m) {} bool operator()(ddnf_node* n1, ddnf_node* n2) const { return m.equals(n1->get_tbv(), n2->get_tbv()); } }; struct hash { tbv_manager& m; hash(tbv_manager& m):m(m) {} unsigned operator()(ddnf_node* n) const { return m.hash(n->get_tbv()); } }; typedef ptr_hashtable ddnf_nodes; private: tbv_manager& tbvm; tbv const& m_tbv; ddnf_node_vector m_children; unsigned m_refs; unsigned m_id; ddnf_node::hash m_hash; ddnf_node::eq m_eq; ddnf_nodes m_descendants; friend class ddnf_mgr; public: ddnf_node(ddnf_mgr& m, tbv_manager& tbvm, tbv const& tbv, unsigned id): tbvm(tbvm), m_tbv(tbv), m_children(m), m_refs(0), m_id(id), m_hash(tbvm), m_eq(tbvm), m_descendants(DEFAULT_HASHTABLE_INITIAL_CAPACITY, m_hash, m_eq) { } ~ddnf_node() {} unsigned inc_ref() { return ++m_refs; } void dec_ref() { SASSERT(m_refs > 0); --m_refs; if (m_refs == 0) { dealloc(this); } } ddnf_nodes& descendants() { return m_descendants; } unsigned get_id() const { return m_id; } unsigned num_children() const { return m_children.size(); } ddnf_node* operator[](unsigned index) { return m_children[index].get(); } tbv const& get_tbv() const { return m_tbv; } void add_child(ddnf_node* n); void remove_child(ddnf_node* n); bool contains_child(ddnf_node* n) const; void display(std::ostream& out) const { out << "node[" << get_id() << ": "; tbvm.display(out, m_tbv); for (unsigned i = 0; i < m_children.size(); ++i) { out << " " << m_children[i]->get_id(); } out << "]"; } }; typedef ddnf_node::ddnf_nodes ddnf_nodes; class ddnf_mgr { struct stats { unsigned m_num_inserts; unsigned m_num_comparisons; stats() { reset(); } void reset() { memset(this, 0, sizeof(*this)); } }; ddnf_node* m_root; ddnf_node_vector m_noderefs; bool m_internalized; tbv_manager m_tbv; ddnf_node::hash m_hash; ddnf_node::eq m_eq; ddnf_nodes m_nodes; svector m_marked; stats m_stats; public: ddnf_mgr(unsigned n): m_noderefs(*this), m_internalized(false), m_tbv(n), m_hash(m_tbv), m_eq(m_tbv), m_nodes(DEFAULT_HASHTABLE_INITIAL_CAPACITY, m_hash, m_eq) { tbv* bX = m_tbv.allocateX(); m_root = alloc(ddnf_node, *this, m_tbv, *bX, m_nodes.size()); m_noderefs.push_back(m_root); m_nodes.insert(m_root); } ~ddnf_mgr() { m_noderefs.reset(); m_tbv.reset(); } void inc_ref(ddnf_node* n) { n->inc_ref(); } void dec_ref(ddnf_node* n) { n->dec_ref(); } void reset_accumulate() { m_marked.resize(m_nodes.size()); for (unsigned i = 0; i < m_marked.size(); ++i) { m_marked[i] = false; } } void accumulate(tbv const& t, unsigned_vector& acc) { ddnf_node* n = find(t); ptr_vector todo; todo.push_back(n); while (!todo.empty()) { n = todo.back(); todo.pop_back(); unsigned id = n->get_id(); if (m_marked[id]) continue; acc.push_back(id); m_marked[id] = true; unsigned sz = n->num_children(); for (unsigned i = 0; i < sz; ++i) { todo.push_back((*n)[i]); } } } ddnf_node* insert(tbv const& t) { SASSERT(!m_internalized); ptr_vector new_tbvs; new_tbvs.push_back(&t); for (unsigned i = 0; i < new_tbvs.size(); ++i) { tbv const& nt = *new_tbvs[i]; IF_VERBOSE(10, m_tbv.display(verbose_stream() << "insert: ", nt); verbose_stream() << "\n";); ddnf_node* n; if (contains(nt)) { n = find(nt); } else { n = alloc(ddnf_node, *this, m_tbv, nt, m_noderefs.size()); m_noderefs.push_back(n); m_nodes.insert(n); } insert(*m_root, n, new_tbvs); } return find(t); } tbv* allocate(uint64_t v, unsigned hi, unsigned lo) { return m_tbv.allocate(v, hi, lo); } tbv_manager& get_tbv_manager() { return m_tbv; } unsigned size() const { return m_noderefs.size(); } ddnf_nodes const& lookup(tbv const& t) { internalize(); return find(t)->descendants(); } void display_statistics(std::ostream& out) const { std::cout << "Number of insertions: " << m_stats.m_num_inserts << "\n"; std::cout << "Number of comparisons: " << m_stats.m_num_comparisons << "\n"; std::cout << "Number of nodes: " << size() << "\n"; } void display(std::ostream& out) const { for (unsigned i = 0; i < m_noderefs.size(); ++i) { m_noderefs[i]->display(out); out << "\n"; } } bool contains(tbv const& t) { ddnf_node dummy(*this, m_tbv, t, 0); return m_nodes.contains(&dummy); } bool well_formed() { ptr_vector todo; todo.push_back(m_root); reset_accumulate(); while (!todo.empty()) { ddnf_node* n = todo.back(); todo.pop_back(); if (m_marked[n->get_id()]) continue; m_marked[n->get_id()] = true; unsigned sz = n->num_children(); for (unsigned i = 0; i < sz; ++i) { ddnf_node* child = (*n)[i]; if (!m_tbv.contains(n->get_tbv(), child->get_tbv())) { IF_VERBOSE(0, m_tbv.display(verbose_stream() << "parent ", n->get_tbv()); m_tbv.display(verbose_stream() << " does not contains child: ", child->get_tbv()); display(verbose_stream()); ); return false; } todo.push_back(child); } } return true; } private: ddnf_node* find(tbv const& t) { ddnf_node dummy(*this, m_tbv, t, 0); return *(m_nodes.find(&dummy)); } void insert(ddnf_node& root, ddnf_node* new_n, ptr_vector& new_intersections) { tbv const& new_tbv = new_n->get_tbv(); IF_VERBOSE(10, m_tbv.display(verbose_stream() << "root: ", root.get_tbv()); m_tbv.display(verbose_stream() << " new node ", new_tbv); verbose_stream() << "\n";); SASSERT(m_tbv.contains(root.get_tbv(), new_tbv)); if (m_eq(&root, new_n)) return; ++m_stats.m_num_inserts; bool inserted = false; for (unsigned i = 0; i < root.num_children(); ++i) { ddnf_node& child = *(root[i]); ++m_stats.m_num_comparisons; IF_VERBOSE(10, m_tbv.display(verbose_stream() << "child ", child.get_tbv()); verbose_stream() << " contains: " << m_tbv.contains(child.get_tbv(), new_tbv) << "\n";); if (m_tbv.contains(child.get_tbv(), new_tbv)) { inserted = true; insert(child, new_n, new_intersections); } } if (inserted) { return; } ddnf_node_vector subset_children(*this); tbv* intr = m_tbv.allocate(); for (unsigned i = 0; i < root.num_children(); ++i) { ddnf_node& child = *(root[i]); // cannot be superset SASSERT(!m_tbv.contains(child.get_tbv(),new_tbv)); // checking for subset if (m_tbv.contains(new_tbv, child.get_tbv())) { subset_children.push_back(&child); IF_VERBOSE(10, m_tbv.display(verbose_stream() << "contains child", child.get_tbv()); verbose_stream() << "\n";); ++m_stats.m_num_comparisons; } else if (m_tbv.intersect(child.get_tbv(), new_tbv, *intr)) { // this means there is a non-full intersection new_intersections.push_back(intr); IF_VERBOSE(10, m_tbv.display(verbose_stream() << "intersect child ", child.get_tbv()); verbose_stream() << "\n";); intr = m_tbv.allocate(); m_stats.m_num_comparisons += 2; } else { m_stats.m_num_comparisons += 2; } } m_tbv.deallocate(intr); for (unsigned i = 0; i < subset_children.size(); ++i) { root.remove_child(subset_children[i].get()); new_n->add_child(subset_children[i].get()); } root.add_child(new_n); } void internalize() { // populate maps (should be bit-sets) of descendants. if (m_internalized) { return; } ptr_vector todo; todo.push_back(m_root); svector done(m_noderefs.size(), false); while (!todo.empty()) { ddnf_node& n = *todo.back(); if (done[n.get_id()]) { todo.pop_back(); continue; } unsigned sz = n.num_children(); bool all_done = true; for (unsigned i = 0; i < sz; ++i) { ddnf_node* child = n[i]; if (!done[child->get_id()]) { all_done = false; todo.push_back(child); } } if (all_done) { n.descendants().insert(&n); for (unsigned i = 0; i < sz; ++i) { ddnf_node* child = n[i]; add_table(n.descendants(), child->descendants()); } done[n.get_id()] = true; todo.pop_back(); } } m_internalized = true; } void add_table(ddnf_nodes& dst, ddnf_nodes const& src) { ddnf_nodes::iterator it = src.begin(), end = src.end(); for (; it != end; ++it) { dst.insert(*it); } } }; ddnf_core::ddnf_core(unsigned n) { m_imp = alloc(ddnf_mgr, n); } ddnf_core::~ddnf_core() { dealloc(m_imp); } ddnf_node* ddnf_core::insert(tbv const& t) { return m_imp->insert(t); } tbv_manager& ddnf_core::get_tbv_manager() { return m_imp->get_tbv_manager(); } unsigned ddnf_core::size() const { return m_imp->size(); } bool ddnf_core::contains(tbv const& t) { return m_imp->contains(t); } bool ddnf_core::well_formed() { return m_imp->well_formed(); } void ddnf_core::reset_accumulate() { return m_imp->reset_accumulate(); } void ddnf_core::accumulate(tbv const& t, unsigned_vector& acc) { return m_imp->accumulate(t, acc); } void ddnf_core::display(std::ostream& out) const { m_imp->display(out); } void ddnf_core::display_statistics(std::ostream& out) const { m_imp->display_statistics(out); } void ddnf_node::add_child(ddnf_node* n) { //SASSERT(!m_tbv.is_subset(n->m_tbv)); m_children.push_back(n); } void ddnf_node::remove_child(ddnf_node* n) { m_children.erase(n); } bool ddnf_node::contains_child(ddnf_node* n) const { return m_children.contains(n); } class ddnfs { u_map m_mgrs; public: ddnfs() {} ~ddnfs() { u_map::iterator it = m_mgrs.begin(), end = m_mgrs.end(); for (; it != end; ++it) { dealloc(it->m_value); } } tbv* allocate(unsigned num_bits, uint64_t v, unsigned hi, unsigned lo) { return get(num_bits).allocate(v, hi, lo); } void insert(unsigned num_bits, tbv const& t) { get(num_bits).insert(t); } ddnf_mgr& get(unsigned num_bits) { return *insert(num_bits); } ddnf_nodes const& lookup(unsigned n, tbv const& t) const { return m_mgrs.find(n)->lookup(t); } void display(std::ostream& out) const { u_map::iterator it = m_mgrs.begin(), end = m_mgrs.end(); for (; it != end; ++it) { it->m_value->display(out); } } private: ddnf_mgr* insert(unsigned n) { ddnf_mgr* m = nullptr; if (!m_mgrs.find(n, m)) { m = alloc(ddnf_mgr, n); m_mgrs.insert(n, m); } return m; } }; class ddnf::imp { struct stats { stats() { reset(); } void reset() { memset(this, 0, sizeof(*this)); } }; context& m_ctx; ast_manager& m; rule_manager& rm; bv_util bv; ptr_vector m_todo; ast_mark m_visited1, m_visited2; ddnfs m_ddnfs; stats m_stats; obj_map m_expr2tbv; obj_map m_cache; expr_ref_vector m_trail; context m_inner_ctx; public: imp(context& ctx): m_ctx(ctx), m(ctx.get_manager()), rm(ctx.get_rule_manager()), bv(m), m_trail(m), m_inner_ctx(m, m_ctx.get_register_engine(), m_ctx.get_fparams()) { params_ref params; params.set_sym("engine", symbol("datalog")); m_inner_ctx.updt_params(params); } ~imp() {} lbool query(expr* query) { m_ctx.ensure_opened(); rule_set& old_rules = m_ctx.get_rules(); rm.mk_query(query, old_rules); rule_set new_rules(m_ctx); IF_VERBOSE(10, verbose_stream() << "(ddnf.preprocess)\n";); if (!pre_process_rules(old_rules)) { return l_undef; } IF_VERBOSE(10, verbose_stream() << "(ddnf.compile)\n";); if (!compile_rules1(old_rules, new_rules)) { return l_undef; } IF_VERBOSE(15, m_ddnfs.display(verbose_stream());); dump_rules(new_rules); return l_undef; // return execute_rules(new_rules); } void reset_statistics() { m_stats.reset(); } void collect_statistics(statistics& st) const { } void display_certificate(std::ostream& out) const { expr_ref ans = get_answer(); out << mk_pp(ans, m) << "\n"; } expr_ref get_answer() const { UNREACHABLE(); return expr_ref(m.mk_true(), m); } private: proof_ref get_proof() const { scoped_proof sp(m); proof_ref pr(m); return pr; } bool pre_process_rules(rule_set const& rules) { m_visited1.reset(); m_todo.reset(); m_cache.reset(); m_expr2tbv.reset(); datalog::rule_set::iterator it = rules.begin(); datalog::rule_set::iterator end = rules.end(); for (; it != end; ++it) { if (!pre_process_rule(**it)) { return false; } } return true; } bool pre_process_rule(rule const& r) { // all predicates are monadic. unsigned utsz = r.get_uninterpreted_tail_size(); unsigned sz = r.get_tail_size(); for (unsigned i = utsz; i < sz; ++i) { m_todo.push_back(r.get_tail(i)); } if (process_todo()) { return true; } else { r.display(m_ctx, std::cout); return false; } } bool process_todo() { while (!m_todo.empty()) { expr* e = m_todo.back(); m_todo.pop_back(); if (m_visited1.is_marked(e)) { continue; } m_visited1.mark(e, true); if (is_var(e)) { continue; } if (is_quantifier(e)) { return false; } if (m.is_and(e) || m.is_or(e) || m.is_iff(e) || m.is_not(e) || m.is_implies(e)) { m_todo.append(to_app(e)->get_num_args(), to_app(e)->get_args()); continue; } if (is_ground(e)) { continue; } if (process_atomic(e)) { continue; } IF_VERBOSE(0, verbose_stream() << "Could not handle: " << mk_pp(e, m) << "\n";); return false; } return true; } bool process_atomic(expr* e) { expr* e1, *e2, *e3; unsigned lo, hi; if (m.is_eq(e, e1, e2) && bv.is_bv(e1)) { if (is_var(e1) && is_ground(e2)) { return process_eq(e, to_var(e1), bv.get_bv_size(e1)-1, 0, e2); } if (is_var(e2) && is_ground(e1)) { return process_eq(e, to_var(e2), bv.get_bv_size(e2)-1, 0, e1); } if (bv.is_extract(e1, lo, hi, e3) && is_var(e3) && is_ground(e2)) { return process_eq(e, to_var(e3), hi, lo, e2); } if (bv.is_extract(e2, lo, hi, e3) && is_var(e3) && is_ground(e1)) { return process_eq(e, to_var(e3), hi, lo, e1); } if (is_var(e1) && is_var(e2)) { return true; } } return false; } bool process_eq(expr* e, var* v, unsigned hi, unsigned lo, expr* c) { rational val; unsigned sz_c; unsigned sz_v = bv.get_bv_size(v); if (!bv.is_numeral(c, val, sz_c)) { return false; } if (!val.is_uint64()) { return false; } // v[hi:lo] = val tbv* tv = m_ddnfs.allocate(sz_v, val.get_uint64(), hi, lo); m_ddnfs.insert(sz_v, *tv); m_expr2tbv.insert(e, tv); // std::cout << mk_pp(v, m) << " " << lo << " " << hi << " " << v << " " << tbv << "\n"; return true; } void init_ctx(rule_set& rules) { m_inner_ctx.reset(); func_decl_set const& predicates = m_ctx.get_predicates(); for (func_decl_set::iterator fit = predicates.begin(); fit != predicates.end(); ++fit) { m_inner_ctx.register_predicate(*fit, false); } m_inner_ctx.ensure_opened(); m_inner_ctx.replace_rules(rules); m_inner_ctx.close(); } void dump_rules(rule_set& rules) { init_ctx(rules); m_inner_ctx.display_smt2(0, nullptr, std::cout); } lbool execute_rules(rule_set& rules) { init_ctx(rules); ptr_vector heads; rule_set::decl2rules::iterator dit = rules.begin_grouped_rules(); rule_set::decl2rules::iterator dend = rules.end_grouped_rules(); for (; dit != dend; ++dit) { heads.push_back(dit->m_key); } return m_inner_ctx.rel_query(heads.size(), heads.c_ptr()); } bool compile_rules1(rule_set const& rules, rule_set& new_rules) { datalog::rule_set::iterator it = rules.begin(); datalog::rule_set::iterator end = rules.end(); unsigned idx = 0; for (; it != end; ++idx, ++it) { if (!compile_rule1(**it, rules, new_rules)) { return false; } } return true; } bool compile_rule1(rule& r, rule_set const& old_rules, rule_set& new_rules) { app_ref head(m), pred(m); app_ref_vector body(m); expr_ref tmp(m); compile_predicate(r.get_head(), head); unsigned utsz = r.get_uninterpreted_tail_size(); unsigned sz = r.get_tail_size(); for (unsigned i = 0; i < utsz; ++i) { compile_predicate(r.get_tail(i), pred); body.push_back(pred); } for (unsigned i = utsz; i < sz; ++i) { compile_expr(r.get_tail(i), tmp); body.push_back(to_app(tmp)); } rule* r_new = rm.mk(head, body.size(), body.c_ptr(), nullptr, r.name(), false); new_rules.add_rule(r_new); IF_VERBOSE(20, r_new->display(m_ctx, verbose_stream());); if (old_rules.is_output_predicate(r.get_decl())) { new_rules.set_output_predicate(r_new->get_decl()); } return true; } void compile_predicate(app* p, app_ref& result) { sort_ref_vector domain(m); func_decl* d = p->get_decl(); SASSERT(d->get_family_id() == null_family_id); for (unsigned i = 0; i < p->get_num_args(); ++i) { domain.push_back(compile_sort(m.get_sort(p->get_arg(i)))); } func_decl_ref fn(m); fn = m.mk_func_decl(d->get_name(), domain.size(), domain.c_ptr(), m.mk_bool_sort()); m_ctx.register_predicate(fn, false); expr_ref_vector args(m); expr_ref tmp(m); for (unsigned i = 0; i < p->get_num_args(); ++i) { compile_expr(p->get_arg(i), tmp); args.push_back(tmp); } result = m.mk_app(fn, args.size(), args.c_ptr()); } void insert_cache(expr* e, expr* r) { m_trail.push_back(r); m_cache.insert(e, r); } void compile_var(var* v, var_ref& result) { expr* r; if (m_cache.find(v, r)) { result = to_var(r); } else { unsigned idx = v->get_idx(); result = m.mk_var(idx, compile_sort(v->get_sort())); insert_cache(v, result); } } sort* compile_sort(sort* s) { if (m.is_bool(s)) { return s; } if (bv.is_bv_sort(s)) { unsigned sz = bv.get_bv_size(s); ddnf_mgr const& mgr = m_ddnfs.get(sz); unsigned num_elems = mgr.size(); unsigned nb = 1; while ((1UL << nb) <= num_elems) { ++nb; } return bv.mk_sort(nb); } UNREACHABLE(); return nullptr; } void compile_expr(expr* e, expr_ref& result) { expr* r = nullptr; if (m_cache.find(e, r)) { result = r; return; } if (is_ground(e)) { result = e; m_cache.insert(e, result); return; } if (is_var(e)) { var_ref w(m); compile_var(to_var(e), w); result = w; return; } if (m.is_and(e) || m.is_or(e) || m.is_iff(e) || m.is_not(e) || m.is_implies(e)) { app* a = to_app(e); expr_ref tmp(m); expr_ref_vector args(m); for (unsigned i = 0; i < a->get_num_args(); ++i) { compile_expr(a->get_arg(i), tmp); args.push_back(tmp); } result = m.mk_app(a->get_decl(), args.size(), args.c_ptr()); insert_cache(e, result); return; } expr* e1, *e2, *e3; unsigned lo, hi; if (m.is_eq(e, e1, e2) && bv.is_bv(e1)) { if (is_var(e1) && is_ground(e2)) { compile_eq(e, result, to_var(e1), bv.get_bv_size(e1)-1, 0, e2); } else if (is_var(e2) && is_ground(e1)) { compile_eq(e, result, to_var(e2), bv.get_bv_size(e2)-1, 0, e1); } else if (bv.is_extract(e1, lo, hi, e3) && is_var(e3) && is_ground(e2)) { compile_eq(e, result, to_var(e3), hi, lo, e2); } else if (bv.is_extract(e2, lo, hi, e3) && is_var(e3) && is_ground(e1)) { compile_eq(e, result, to_var(e3), hi, lo, e1); } else if (is_var(e1) && is_var(e2)) { var_ref v1(m), v2(m); compile_var(to_var(e1), v1); compile_var(to_var(e2), v2); result = m.mk_eq(v1, v2); } else { UNREACHABLE(); } insert_cache(e, result); return; } std::cout << mk_pp(e, m) << "\n"; UNREACHABLE(); } void compile_eq(expr* e, expr_ref& result, var* v, unsigned hi, unsigned lo, expr* c) { tbv* t = nullptr; // TBD: hi, lo are ignored. VERIFY(m_expr2tbv.find(e, t)); var_ref w(m); compile_var(v, w); unsigned num_bits = bv.get_bv_size(c); ddnf_nodes const& ns = m_ddnfs.lookup(num_bits, *t); ddnf_nodes::iterator it = ns.begin(), end = ns.end(); expr_ref_vector eqs(m); sort* s = m.get_sort(w); for (; it != end; ++it) { eqs.push_back(m.mk_eq(w, bv.mk_numeral(rational((*it)->get_id()), s))); } switch (eqs.size()) { case 0: UNREACHABLE(); result = m.mk_false(); break; case 1: result = eqs[0].get(); break; default: result = m.mk_or(eqs.size(), eqs.c_ptr()); break; } } }; ddnf::ddnf(context& ctx): datalog::engine_base(ctx.get_manager(),"tabulation"), m_imp(alloc(imp, ctx)) { } ddnf::~ddnf() { dealloc(m_imp); } lbool ddnf::query(expr* query) { return m_imp->query(query); } void ddnf::reset_statistics() { m_imp->reset_statistics(); } void ddnf::collect_statistics(statistics& st) const { m_imp->collect_statistics(st); } void ddnf::display_certificate(std::ostream& out) const { m_imp->display_certificate(out); } expr_ref ddnf::get_answer() { return m_imp->get_answer(); } }; z3-z3-4.8.7/src/muz/ddnf/ddnf.h000066400000000000000000000030271356505360400160320ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: ddnf.h Abstract: DDNF based engine. Author: Nikolaj Bjorner (nbjorner) 2014-08-21 Revision History: --*/ #ifndef DDNF_H_ #define DDNF_H_ #include "ast/ast.h" #include "util/lbool.h" #include "util/statistics.h" #include "muz/base/dl_engine_base.h" class tbv; class tbv_manager; namespace datalog { class context; class ddnf : public engine_base { class imp; imp* m_imp; public: ddnf(context& ctx); ~ddnf() override; lbool query(expr* query) override; void reset_statistics() override; void collect_statistics(statistics& st) const override; void display_certificate(std::ostream& out) const override; expr_ref get_answer() override; }; class ddnf_node; class ddnf_mgr; class ddnf_core { ddnf_mgr* m_imp; public: ddnf_core(unsigned n); ~ddnf_core(); ddnf_node* insert(tbv const& t); tbv_manager& get_tbv_manager(); unsigned size() const; bool contains(tbv const& t); bool well_formed(); // // accumulate labels covered by the stream of tbvs, // such that labels that are covered by the current // tbv but not the previous tbvs are included. // void reset_accumulate(); void accumulate(tbv const& t, unsigned_vector& labels); void display(std::ostream& out) const; void display_statistics(std::ostream& out) const; }; }; #endif z3-z3-4.8.7/src/muz/fp/000077500000000000000000000000001356505360400144365ustar00rootroot00000000000000z3-z3-4.8.7/src/muz/fp/CMakeLists.txt000066400000000000000000000003561356505360400172020ustar00rootroot00000000000000z3_add_component(fp SOURCES datalog_parser.cpp dl_cmds.cpp dl_register_engine.cpp horn_tactic.cpp COMPONENT_DEPENDENCIES bmc clp ddnf muz rel spacer tab TACTIC_HEADERS horn_tactic.h ) z3-z3-4.8.7/src/muz/fp/datalog_parser.cpp000066400000000000000000001353431356505360400201420ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "muz/fp/datalog_parser.h" #include "util/string_buffer.h" #include "util/str_hashtable.h" #include "ast/ast_pp.h" #include "ast/arith_decl_plugin.h" #include "util/region.h" #include "util/warning.h" #include #include #include using namespace datalog; enum dtoken { TK_LP, TK_RP, TK_STRING, TK_ID, TK_NUM, TK_PERIOD, TK_INCLUDE, TK_COMMA, TK_COLON, TK_WILD, TK_LEFT_ARROW, TK_EOS, TK_NEWLINE, TK_ERROR, TK_NEQ, TK_LT, TK_GT, TK_EQ, TK_NEG }; static char const* dtoken_strings[] = { "(", ")", "", "", "", ".", ".include", ",", ":", "_", ":-", "", "\\n", "", "!=", "<", ">", "=", "!" }; class line_reader { static const char s_delimiter = '\n'; static const unsigned s_expansion_step = 1024; FILE * m_file; svector m_data; bool m_eof; bool m_eof_behind_buffer; unsigned m_next_index; bool m_ok; //actually by one larger than the actual size of m_data, //to fit in the terminating delimiter unsigned m_data_size; void resize_data(unsigned sz) { m_data_size = sz; m_data.resize(m_data_size+1); m_data[m_data_size] = s_delimiter; } #if 0 void refill_buffer(unsigned start) { unsigned should_read = m_data_size-start; m_stm.read(m_data.begin()+start, should_read); unsigned actually_read = static_cast(m_stm.gcount()); SASSERT(should_read==actually_read || m_stm.eof()); if(m_stm.eof()) { m_eof_behind_buffer = true; resize_data(start+actually_read); } } #else void refill_buffer(unsigned start) { unsigned should_read = m_data_size-start; size_t actually_read = fread(m_data.begin()+start, 1, should_read, m_file); if(actually_read==should_read) { return; } SASSERT(actually_read < should_read); SASSERT(feof(m_file)); m_eof_behind_buffer = true; resize_data(start+static_cast(actually_read)); } #endif public: line_reader(const char * fname) :m_eof(false), m_eof_behind_buffer(false), m_next_index(0), m_ok(true), m_data_size(0) { m_data.resize(2*s_expansion_step); resize_data(0); #if _WINDOWS errno_t err = fopen_s(&m_file, fname, "rb"); m_ok = (m_file != nullptr) && (err == 0); #else m_file = fopen(fname, "rb"); m_ok = (m_file != nullptr); #endif } ~line_reader() { if (m_file != nullptr){ fclose(m_file); } } bool operator()() { return m_ok; } /** \brief Retrieve next line from the stream. This operation invalidates the line previously retrieved. This operation can be called only if we are not at the end of file. User is free to modify the content of the returned array until the terminating NULL character. */ char * get_line() { SASSERT(!m_eof); unsigned start = m_next_index; unsigned curr = start; for(;;) { SASSERT(curr<=m_data_size); SASSERT(m_data[m_data_size]==s_delimiter); { const char * data_ptr = m_data.begin(); const char * ptr = data_ptr+curr; while(*ptr!=s_delimiter) { ptr++; } curr = static_cast(ptr-data_ptr); } SASSERT(m_data[curr]==s_delimiter); if(curr str2token; str2token m_str2token; public: reserved_symbols() { m_str2token.insert(":-", TK_LEFT_ARROW); m_str2token.insert("_", TK_WILD); m_str2token.insert(".", TK_PERIOD); m_str2token.insert("!=", TK_NEQ); m_str2token.insert("=", TK_EQ); m_str2token.insert("<", TK_LT); m_str2token.insert(">", TK_GT); m_str2token.insert(":", TK_COLON); m_str2token.insert(".include", TK_INCLUDE); m_str2token.insert("!", TK_NEG); } dtoken string2dtoken(char const * str) { str2token::entry * e = m_str2token.find_core(str); if (e) return e->get_data().m_value; else return TK_ID; } }; class dlexer { std::istream* m_input; char_reader* m_reader; char m_prev_char; char m_curr_char; int m_line; int m_pos; int m_tok_pos; string_buffer<> m_buffer; reserved_symbols m_reserved_symbols; public: //when parsing domains, we want '.' character to be allowed in IDs, but elsewhere //we don't (because of the "y." in rules like "P(x,y):-x=y.") bool m_parsing_domains; bool eos() const { return m_curr_char == EOF; } void next() { m_prev_char = m_curr_char; if (m_reader) { m_curr_char = m_reader->get(); } else { m_curr_char = m_input->get(); } m_pos++; } void save_char(char c) { m_buffer << c; } void save_and_next() { m_buffer << m_curr_char; next(); } dlexer(): m_input(nullptr), m_reader(nullptr), m_prev_char(0), m_curr_char(0), m_line(1), m_pos(0), m_tok_pos(0), m_parsing_domains(false) { } void set_stream(std::istream* s, char_reader* r) { m_input = s; m_reader = r; next(); } dtoken read_num() { while(isdigit(m_curr_char)) { save_and_next(); } return TK_NUM; } dtoken read_id() { while (!eos() && m_curr_char != '(' && m_curr_char != ')' && m_curr_char != '#' && m_curr_char != ',' && (m_parsing_domains || m_curr_char != '.') && m_curr_char != ':' && m_curr_char != '=' && !iswspace(m_curr_char) ) { save_and_next(); } return m_reserved_symbols.string2dtoken(m_buffer.c_str()); } // read an id of the form '|'.*'|' dtoken read_bid() { while (!eos() && m_curr_char != '|') { save_and_next(); } if (m_curr_char == '|') { next(); } return m_reserved_symbols.string2dtoken(m_buffer.c_str()); } dtoken read_string() { m_tok_pos = m_pos; next(); while (m_curr_char != '"') { if (m_input && m_input->eof()) { return TK_ERROR; } if (m_reader && m_reader->eof()) { return TK_ERROR; } if (m_curr_char == '\n') { return TK_ERROR; } save_and_next(); } next(); return TK_STRING; } void read_comment() { bool line_comment = m_prev_char=='\n' || m_prev_char == 0; while (m_curr_char != '\n' && !eos()) { next(); } if (line_comment && m_curr_char == '\n') { m_line++; next(); } } bool lookahead_newline() { while (m_curr_char == ' ') { save_and_next(); } if (m_curr_char == '\n') { next(); m_line++; m_buffer.reset(); return true; } if (m_curr_char == '#') { m_buffer.reset(); m_prev_char = 0; read_comment(); return true; } return false; } dtoken next_token() { for(;;) { if (eos()) { return TK_EOS; } m_buffer.reset(); switch (m_curr_char) { case '#': // comment read_comment(); break; case '\n': next(); m_line++; return TK_NEWLINE; case '\\': // here we ignore a newline if it is preceded by a backslash. // We need to take care, since anywhere else backshlash is used // as a regular character next(); save_char('\\'); if (lookahead_newline()) { break; } return read_id(); case '(': m_tok_pos = m_pos; next(); return TK_LP; case ')': m_tok_pos = m_pos; next(); return TK_RP; case ',': m_tok_pos = m_pos; next(); return TK_COMMA; case '=': m_tok_pos = m_pos; next(); return TK_EQ; case '!': m_tok_pos = m_pos; next(); if(m_curr_char == '=') { next(); return TK_NEQ; } return TK_NEG; case ':': m_tok_pos = m_pos; next(); if (m_curr_char == '-') { next(); return TK_LEFT_ARROW; } return TK_COLON; case '\"': return read_string(); case '|': next(); return read_bid(); default: if (iswspace(m_curr_char)) { next(); break; } else if (iswdigit(m_curr_char)) { m_tok_pos = m_pos; save_and_next(); return read_num(); } else { char old = m_curr_char; m_tok_pos = m_pos; save_and_next(); if (old == '-' && iswdigit(m_curr_char)) { return read_num(); } else { return read_id(); } } } } } const char * get_token_data() const { return m_buffer.c_str(); } unsigned get_token_pos() const { return m_tok_pos; } unsigned get_line() const { return m_line; } }; class dparser : public parser { protected: typedef map > str2var; typedef map > str2sort; context& m_context; ast_manager& m_manager; dlexer* m_lexer; region m_region; dl_decl_util & m_decl_util; arith_util m_arith; unsigned m_num_vars; str2var m_vars; unsigned m_sym_idx; std::string m_path; str2sort m_sort_dict; // true if an error occurred during the current call to the parse_stream // function bool m_error; public: dparser(context& ctx, ast_manager& m): m_context(ctx), m_manager(m), m_decl_util(ctx.get_decl_util()), m_arith(m), m_num_vars(0), m_sym_idx(0) { } bool parse_file(char const * filename) override { reset(); if (filename != nullptr) { set_path(filename); char_reader reader(filename); if (!reader()) { get_err() << "ERROR: could not open file '" << filename << "'.\n"; return false; } return parse_stream(nullptr, &reader); } else { return parse_stream(&std::cin, nullptr); } } bool parse_string(char const * string) override { reset(); std::string s(string); std::istringstream is(s); return parse_stream(&is, nullptr); } protected: void reset() { m_num_vars = 0; m_sym_idx = 0; m_vars.reset(); m_region.reset(); m_path.clear(); m_sort_dict.reset(); } void set_path(char const* filename) { char const* div = strrchr(filename, '/'); if (!div) { div = strrchr(filename,'\\'); } if (div) { m_path.assign(filename, div - filename + 1); } } std::ostream& get_err() { return std::cerr; } bool parse_stream(std::istream* is, char_reader* r) { bool result = false; try { m_error=false; dlexer lexer; m_lexer = &lexer; m_lexer->set_stream(is, r); dtoken tok = m_lexer->next_token(); tok = parse_domains(tok); tok = parse_decls(tok); result = tok == TK_EOS && m_error == false; } catch (z3_exception& ex) { std::cerr << ex.msg() << std::endl; result = false; } return result; } dtoken parse_domains(dtoken tok) { flet flet_parsing_domains(m_lexer->m_parsing_domains, true); while (tok != TK_EOS && tok != TK_ERROR) { switch(tok) { case TK_ID: tok = parse_domain(); break; case TK_NEWLINE: return m_lexer->next_token(); case TK_INCLUDE: tok = m_lexer->next_token(); if (tok != TK_STRING) { tok = unexpected(tok, "a string"); break; } tok = parse_include(m_lexer->get_token_data(), true); if(tok!=TK_NEWLINE) { tok = unexpected(tok, "newline expected after include statement"); } else { tok = m_lexer->next_token(); } break; default: tok = unexpected(tok, "identifier, newline or include"); break; } } return tok; } bool extract_domain_name(const char* s0, std::string & result) { std::string str(s0); size_t last_non_digit = str.find_last_not_of("0123456789"); if(last_non_digit==std::string::npos) { //the domain name consists only of digits, which should not happen result=str; return false; } str.erase(last_non_digit+1); result=str; return true; } dtoken parse_domain() { std::string domain_name; if(!extract_domain_name(m_lexer->get_token_data(), domain_name)) { return unexpected(TK_ID, "domain name"); } dtoken tok = m_lexer->next_token(); if (tok == TK_ID && strcmp(m_lexer->get_token_data(), "int")==0) { register_int_sort(symbol(domain_name.c_str())); tok = m_lexer->next_token(); if(tok != TK_NEWLINE) { return unexpected(tok, "end of line"); } return tok; } if (tok != TK_NUM) { return unexpected(tok, "numeral or 'int'"); } unsigned num = atoi(m_lexer->get_token_data()); sort * s = register_finite_sort(symbol(domain_name.c_str()), num, context::SK_SYMBOL); tok = m_lexer->next_token(); if (tok == TK_ID) { tok = parse_mapfile(tok, s, m_lexer->get_token_data()); } if (tok == TK_NEWLINE) { tok = m_lexer->next_token(); } return tok; } dtoken parse_decls(dtoken tok) { while (tok != TK_EOS && tok != TK_ERROR) { switch(tok) { case TK_ID: tok = parse_rule(tok); break; case TK_NEWLINE: tok = m_lexer->next_token(); break; case TK_INCLUDE: tok = m_lexer->next_token(); if (tok != TK_STRING) { tok = unexpected(tok, "a string"); break; } tok = parse_include(m_lexer->get_token_data(), false); break; default: tok = unexpected(tok, "identifier"); break; } } return tok; } dtoken unexpected(dtoken tok, char const* msg) { #if 1 throw default_exception(default_exception::fmt(), "%s at line %u '%s' found '%s'\n", msg, m_lexer->get_line(), m_lexer->get_token_data(), dtoken_strings[tok]); SASSERT(false); return tok; #else m_error = true; get_err() << msg << " expected at line " << m_lexer->get_line() << "\n"; get_err() << "'" << m_lexer->get_token_data() << "' found\n"; get_err() << "'" << dtoken_strings[tok] << "'\n"; if (tok == TK_ERROR || tok == TK_EOS) { return tok; } return m_lexer->next_token(); #endif } dtoken parse_rule(dtoken tok) { m_num_vars = 0; m_vars.reset(); switch(tok) { case TK_EOS: return tok; case TK_ID: { app_ref pred(m_manager); symbol s(m_lexer->get_token_data()); tok = m_lexer->next_token(); bool is_predicate_declaration; tok = parse_pred(tok, s, pred, is_predicate_declaration); switch (tok) { case TK_PERIOD: if(is_predicate_declaration) { return unexpected(tok, "predicate declaration should not end with '.'"); } add_rule(pred, 0, nullptr, nullptr); return m_lexer->next_token(); case TK_LEFT_ARROW: return parse_body(pred); case TK_NEWLINE: case TK_EOS: if(!is_predicate_declaration) { return unexpected(tok, "'.' expected at the end of rule"); } return tok; default: return unexpected(tok, "unexpected token"); } } default: return unexpected(tok, "rule expected"); } } dtoken parse_body(app* head) { app_ref_vector body(m_manager); svector polarity_vect; dtoken tok = m_lexer->next_token(); while (tok != TK_ERROR && tok != TK_EOS) { if (tok == TK_PERIOD) { SASSERT(body.size()==polarity_vect.size()); add_rule(head, body.size(), body.c_ptr(), polarity_vect.c_ptr()); return m_lexer->next_token(); } char const* td = m_lexer->get_token_data(); app_ref pred(m_manager); bool is_neg = false; if (tok == TK_NEG) { tok = m_lexer->next_token(); is_neg = true; } if (tok == TK_STRING || tok == TK_NUM || (tok == TK_ID && m_vars.contains(td))) { tok = parse_infix(tok, td, pred); } else if (tok == TK_ID) { symbol s(td); tok = m_lexer->next_token(); bool is_declaration; tok = parse_pred(tok, s, pred, is_declaration); SASSERT(!is_declaration); } else { tok = unexpected(tok, "expected predicate or relation"); return tok; } body.push_back(pred); polarity_vect.push_back(is_neg); if (tok == TK_COMMA) { tok = m_lexer->next_token(); } else if (tok == TK_PERIOD) { continue; } else { tok = unexpected(tok, "expected comma or period"); return tok; } } return tok; } // // infix: // Sym REL Sym // Sym ::= String | NUM | Var // dtoken parse_infix(dtoken tok1, char const* td, app_ref& pred) { symbol td1(td); expr_ref v1(m_manager), v2(m_manager); sort* s = nullptr; dtoken tok2 = m_lexer->next_token(); if (tok2 != TK_NEQ && tok2 != TK_GT && tok2 != TK_LT && tok2 != TK_EQ) { return unexpected(tok2, "built-in infix operator"); } dtoken tok3 = m_lexer->next_token(); td = m_lexer->get_token_data(); if (tok3 != TK_STRING && tok3 != TK_NUM && !(tok3 == TK_ID && m_vars.contains(td))) { return unexpected(tok3, "identifier"); } symbol td2(td); if (tok1 == TK_ID) { expr* _v1 = nullptr; m_vars.find(td1.bare_str(), _v1); v1 = _v1; } if (tok3 == TK_ID) { expr* _v2 = nullptr; m_vars.find(td2.bare_str(), _v2); v2 = _v2; } if (!v1 && !v2) { return unexpected(tok3, "at least one argument should be a variable"); } if (v1) { s = m_manager.get_sort(v1); } else { s = m_manager.get_sort(v2); } if (!v1) { v1 = mk_const(td1, s); } if (!v2) { v2 = mk_const(td2, s); } switch(tok2) { case TK_EQ: pred = m_manager.mk_eq(v1,v2); break; case TK_NEQ: pred = m_manager.mk_not(m_manager.mk_eq(v1,v2)); break; case TK_LT: pred = m_decl_util.mk_lt(v1, v2); break; case TK_GT: pred = m_decl_util.mk_lt(v2, v1); break; default: UNREACHABLE(); } return m_lexer->next_token(); } dtoken parse_pred(dtoken tok, symbol const& s, app_ref& pred, bool & is_predicate_declaration) { expr_ref_vector args(m_manager); svector arg_names; func_decl* f = m_context.try_get_predicate_decl(s); tok = parse_args(tok, f, args, arg_names); is_predicate_declaration = f==nullptr; if (f==nullptr) { //we're in a declaration unsigned arity = args.size(); ptr_vector domain; for (unsigned i = 0; i < arity; ++i) { domain.push_back(m_manager.get_sort(args[i].get())); } f = m_manager.mk_func_decl(s, domain.size(), domain.c_ptr(), m_manager.mk_bool_sort()); m_context.register_predicate(f, true); while (tok == TK_ID) { char const* pred_pragma = m_lexer->get_token_data(); if(strcmp(pred_pragma, "printtuples")==0 || strcmp(pred_pragma, "outputtuples")==0) { m_context.set_output_predicate(f); } tok = m_lexer->next_token(); } m_context.set_argument_names(f, arg_names); } if(args.size() < f->get_arity()) { return unexpected(tok, "too few arguments passed to predicate"); } SASSERT(args.size()==f->get_arity()); //TODO: we do not need to do the mk_app if we're in a declaration pred = m_manager.mk_app(f, args.size(), args.c_ptr()); return tok; } /** \brief Parse predicate arguments. If \c f==0, they are arguments of a predicate declaration. If parsing a declaration, argument names are pushed to the \c arg_names vector. */ dtoken parse_args(dtoken tok, func_decl* f, expr_ref_vector& args, svector & arg_names) { if (tok != TK_LP) { return tok; } unsigned arg_idx = 0; tok = m_lexer->next_token(); while (tok != TK_EOS && tok != TK_ERROR) { symbol alias; sort* s = nullptr; if(!f) { //we're in a predicate declaration if(tok != TK_ID) { tok = unexpected(tok, "Expecting variable in declaration"); return tok; } symbol var_symbol(m_lexer->get_token_data()); tok = m_lexer->next_token(); if (tok != TK_COLON) { tok = unexpected(tok, "Expecting colon in declaration (first occurrence of a predicate must be a declaration)"); return tok; } tok = m_lexer->next_token(); if (tok != TK_ID) { tok = unexpected(tok, "Expecting sort after colon in declaration"); return tok; } std::string sort_name; if(!extract_domain_name(m_lexer->get_token_data(), sort_name)) { return unexpected(TK_ID, "sort name"); } sort* s = get_sort(sort_name.c_str()); args.push_back(m_manager.mk_var(m_num_vars, s)); arg_names.push_back(var_symbol); tok = m_lexer->next_token(); } else { if(arg_idx>=f->get_arity()) { return unexpected(tok, "too many arguments passed to predicate"); } s = f->get_domain(arg_idx); symbol var_symbol; tok = parse_arg(tok, s, args); } ++arg_idx; if (tok == TK_RP) { return m_lexer->next_token(); } if (tok == TK_COMMA) { tok = m_lexer->next_token(); } } return tok; } /** \remark \c var_symbol argument is assigned name of the variable. If the argument is not a variable, is remains unchanged. */ dtoken parse_arg(dtoken tok, sort* s, expr_ref_vector& args) { switch(tok) { case TK_WILD: { args.push_back(m_manager.mk_var(m_num_vars++, s)); break; } case TK_ID: { symbol data (m_lexer->get_token_data()); if (is_var(data.bare_str())) { unsigned idx = 0; expr* v = nullptr; if (!m_vars.find(data.bare_str(), v)) { idx = m_num_vars++; v = m_manager.mk_var(idx, s); m_vars.insert(data.bare_str(), v); } else if (s != m_manager.get_sort(v)) { throw default_exception(default_exception::fmt(), "sort: %s expected, but got: %s\n", s->get_name().bare_str(), m_manager.get_sort(v)->get_name().bare_str()); } args.push_back(v); } else { args.push_back(mk_const(data, s)); } break; } case TK_STRING: { char const* data = m_lexer->get_token_data(); args.push_back(mk_const(symbol(data), s)); break; } case TK_NUM: { char const* data = m_lexer->get_token_data(); rational num(data); if(!num.is_uint64()) { return unexpected(tok, "integer expected"); } uint64_t int_num = num.get_uint64(); app * numeral = mk_symbol_const(int_num, s); args.push_back(numeral); break; } default: break; } return m_lexer->next_token(); } // all IDs are variables. bool is_var(char const* data) { return true; } dtoken parse_decl(dtoken tok) { return tok; } dtoken parse_include(char const* filename, bool parsing_domain) { IF_VERBOSE(2, verbose_stream() << "include: " << filename << "\n";); std::string path(m_path); path += filename; char_reader stream(path.c_str()); if (!stream()) { get_err() << "ERROR: could not open file '" << path << "'.\n"; return TK_ERROR; } dtoken tok; dlexer lexer; { flet lexer_let(m_lexer, &lexer); m_lexer->set_stream(nullptr, &stream); tok = m_lexer->next_token(); if(parsing_domain) { tok = parse_domains(tok); } tok = parse_decls(tok); } if (tok == TK_EOS) { tok = m_lexer->next_token(); } return tok; } dtoken parse_mapfile(dtoken tok, sort * s, char const* filename) { std::string path(m_path); path += filename; line_reader reader(path.c_str()); if (!reader()) { get_err() << "Warning: could not open file '" << path << "'.\n"; return m_lexer->next_token(); } std::string line; while(!reader.eof()) { symbol sym=symbol(reader.get_line()); m_context.get_constant_number(s, sym); } return m_lexer->next_token(); } bool read_line(std::istream& strm, std::string& line) { line.clear(); char ch = strm.get(); while (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r') { ch = strm.get(); } while (ch != '\n' && ch != '\r' && ch != EOF) { line.push_back(ch); ch = strm.get(); } return !line.empty(); } void add_rule(app* head, unsigned sz, app* const* body, const bool * is_neg) { rule_manager& m = m_context.get_rule_manager(); if(sz==0 && m.is_fact(head)) { m_context.add_fact(head); } else { rule * r = m.mk(head, sz, body, is_neg); rule_ref rule(r, m); m_context.add_rule(rule); } } sort * register_finite_sort(symbol name, uint64_t domain_size, context::sort_kind k) { if(m_sort_dict.contains(name.bare_str())) { throw default_exception(default_exception::fmt(), "sort %s already declared", name.bare_str()); } sort * s = m_decl_util.mk_sort(name, domain_size); m_context.register_finite_sort(s, k); m_sort_dict.insert(name.bare_str(), s); return s; } sort * register_int_sort(symbol name) { if(m_sort_dict.contains(name.bare_str())) { throw default_exception(default_exception::fmt(), "sort %s already declared", name.bare_str()); } sort * s = m_arith.mk_int(); m_sort_dict.insert(name.bare_str(), s); return s; } sort * get_sort(const char* str) { sort * res; if(!m_sort_dict.find(str, res)) { throw default_exception(default_exception::fmt(), "unknown sort \"%s\"", str); } return res; } app* mk_const(symbol const& name, sort* s) { app * res; if(m_arith.is_int(s)) { uint64_t val; if(!string_to_uint64(name.bare_str(), val)) { throw default_exception(default_exception::fmt(), "Invalid integer: \"%s\"", name.bare_str()); } res = m_arith.mk_numeral(rational(val, rational::ui64()), s); } else { unsigned idx = m_context.get_constant_number(s, name); res = m_decl_util.mk_numeral(idx, s); } return res; } /** \brief Make a constant for DK_SYMBOL sort out of an integer */ app* mk_symbol_const(uint64_t el, sort* s) { app * res; if(m_arith.is_int(s)) { res = m_arith.mk_numeral(rational(el, rational::ui64()), s); } else { unsigned idx = m_context.get_constant_number(s, symbol(to_string(el).c_str())); res = m_decl_util.mk_numeral(idx, s); } return res; } app* mk_const(uint64_t el, sort* s) { unsigned idx = m_context.get_constant_number(s, el); app * res = m_decl_util.mk_numeral(idx, s); return res; } table_element mk_table_const(symbol const& name, sort* s) { return m_context.get_constant_number(s, name); } table_element mk_table_const(uint64_t el, sort* s) { return m_context.get_constant_number(s, el); } }; /* Program ::== Sort* (Rule | Include | Decl)* Comment ::== '#...' Rule ::== Fact | InfRule Fact ::== Identifier(Element*). InfRule ::== Identifier(Element*) :- (Identifier(Element*))+. Element ::== '_' | 'string' | integer | Identifier Sort ::== Identifier (Number [map-file]| 'int') Decl ::== Identifier(SortDecl) [Pragma] \n SortDecl ::== Identifier ':' Identifier Pragma ::== 'input' | 'printtuples' | If sort name ends with a sequence of digits, they are ignored (so V and V1234 stand for the same sort) This is how BDDBDDB behaves. */ // ----------------------------------- // // wpa_parser // // ----------------------------------- class wpa_parser_impl : public wpa_parser, dparser { typedef svector uint64_vector; typedef hashtable > uint64_set; typedef map > num2sym; typedef map sym2nums; num2sym m_number_names; sym2nums m_sort_contents; sort_ref m_bool_sort; sort_ref m_short_sort; std::string m_current_file; unsigned m_current_line; bool m_use_map_names; uint64_set& ensure_sort_content(symbol sort_name) { sym2nums::entry * e = m_sort_contents.insert_if_not_there2(sort_name, nullptr); if(!e->get_data().m_value) { e->get_data().m_value = alloc(uint64_set); } return *e->get_data().m_value; } public: wpa_parser_impl(context & ctx) : dparser(ctx, ctx.get_manager()), m_bool_sort(ctx.get_manager()), m_short_sort(ctx.get_manager()), m_use_map_names(ctx.use_map_names()) { } ~wpa_parser_impl() override { reset_dealloc_values(m_sort_contents); } void reset() { } bool parse_directory(char const * path) override { bool result = false; try { result = parse_directory_core(path); } catch (z3_exception& ex) { std::cerr << ex.msg() << std::endl; return false; } return result; } private: bool parse_directory_core(char const* path) { IF_VERBOSE(10, verbose_stream() << "Start parsing directory " << path << "\n";); reset(); string_vector map_files; get_file_names(path, "map", true, map_files); string_vector::iterator mit = map_files.begin(); string_vector::iterator mend = map_files.end(); for(; mit!=mend; ++mit) { std::string map_file_name = *mit; parse_map_file(map_file_name); } finish_map_files(); string_vector rule_files; get_file_names(path, "rules", true, rule_files); string_vector::iterator rlit = rule_files.begin(); string_vector::iterator rlend = rule_files.end(); for(; rlit!=rlend; ++rlit) { parse_rules_file(*rlit); } string_vector rel_files; get_file_names(path, "rel", true, rel_files); string_vector::iterator rit = rel_files.begin(); string_vector::iterator rend = rel_files.end(); for(; rit!=rend; ++rit) { std::string rel_file_name = *rit; //skip relations which we do not support yet if(rel_file_name.find("DirectCall")!=std::string::npos || rel_file_name.find("FunctionFormals")!=std::string::npos || rel_file_name.find("IndirectCall")!=std::string::npos) { continue; } parse_rel_file(rel_file_name); } IF_VERBOSE(10, verbose_stream() << "Done parsing directory " << path << "\n";); return true; } bool inp_num_to_element(sort * s, uint64_t num, table_element & res) { if(s==m_bool_sort.get() || s==m_short_sort.get()) { res = mk_table_const(num, s); return true; } if(num==0) { if(!m_use_map_names) { res = mk_table_const(0, s); } else { res = mk_table_const(symbol(""), s); } return true; } sym2nums::entry * e = m_sort_contents.find_core(s->get_name()); SASSERT(e); SASSERT(e->get_data().m_value); uint64_set & sort_content = *e->get_data().m_value; if(!sort_content.contains(num)) { warning_msg("symbol number %I64u on line %d in file %s does not belong to sort %s", num, m_current_line, m_current_file.c_str(), s->get_name().bare_str()); return false; } if(!m_use_map_names) { res = mk_table_const(num, s); return true; } else { symbol const_name; if(num==0) { const_name = symbol(""); } else if(!m_number_names.find(num, const_name)) { throw default_exception(default_exception::fmt(), "unknown symbol number %I64u on line %d in file %s", num, m_current_line, m_current_file.c_str()); } res = mk_table_const(const_name, s); return true; } } void parse_rules_file(const std::string & fname) { SASSERT(file_exists(fname)); flet flet_cur_file(m_current_file, fname); std::ifstream stm(fname.c_str()); SASSERT(!stm.fail()); dlexer lexer; m_lexer = &lexer; m_lexer->set_stream(&stm, nullptr); dtoken tok = m_lexer->next_token(); tok = parse_decls(tok); m_lexer = nullptr; } bool parse_rel_line(char * full_line, uint64_vector & args) { SASSERT(args.empty()); cut_off_comment(full_line); if(full_line[0]==0) { return false; } const char * ptr = full_line; bool last = false; do { while(*ptr==' ') { ptr++; } if(*ptr==0) { break; } uint64_t num; if(!read_uint64(ptr, num)) { throw default_exception(default_exception::fmt(), "number expected on line %d in file %s", m_current_line, m_current_file.c_str()); } if(*ptr!=' ' && *ptr!=0) { throw default_exception(default_exception::fmt(), "' ' expected to separate numbers on line %d in file %s, got '%s'", m_current_line, m_current_file.c_str(), ptr); } args.push_back(num); } while(!last); return true; } void parse_rel_file(const std::string & fname) { SASSERT(file_exists(fname)); IF_VERBOSE(10, verbose_stream() << "Parsing relation file " << fname << "\n";); flet flet_cur_file(m_current_file, fname); flet flet_cur_line(m_current_line, 0); std::string predicate_name_str = get_file_name_without_extension(fname); symbol predicate_name(predicate_name_str.c_str()); func_decl * pred = m_context.try_get_predicate_decl(predicate_name); if(!pred) { throw default_exception(default_exception::fmt(), "tuple file %s for undeclared predicate %s", m_current_file.c_str(), predicate_name.bare_str()); } unsigned pred_arity = pred->get_arity(); sort * const * arg_sorts = pred->get_domain(); uint64_vector args; table_fact fact; //std::ifstream stm(fname.c_str(), std::ios_base::binary); //SASSERT(!stm.fail()); //line_reader rdr(stm); line_reader rdr(fname.c_str()); while(!rdr.eof()) { m_current_line++; char * full_line = rdr.get_line(); args.reset(); if(!parse_rel_line(full_line, args)) { continue; } if(args.size()!=pred_arity) { throw default_exception(default_exception::fmt(), "invalid number of arguments on line %d in file %s", m_current_line, m_current_file.c_str()); } bool fact_fail = false; fact.reset(); for(unsigned i=0;im_key; uint64_set & sort_content = *sit->m_value; //the +1 is for a zero element which happens to appear in the problem files uint64_t domain_size = sort_content.size()+1; // sort * s; if(!m_use_map_names) { /* s = */ register_finite_sort(sort_name, domain_size, context::SK_UINT64); } else { /* s = */ register_finite_sort(sort_name, domain_size, context::SK_SYMBOL); } /* uint64_set::iterator cit = sort_content.begin(); uint64_set::iterator cend = sort_content.end(); for(; cit!=cend; ++cit) { uint64_t const_num = *cit; inp_num_to_element(s, const_num); } */ } } void cut_off_comment(char * line) { char * ptr = line; while(*ptr && *ptr!='#' && *ptr!='\n' && *ptr!='\r') { ptr++; } *ptr=0; } bool parse_map_line(char * full_line, uint64_t & num, symbol & name) { cut_off_comment(full_line); if(full_line[0]==0) { return false; } const char * ptr = full_line; if(!read_uint64(ptr, num)) { throw default_exception(default_exception::fmt(), "number expected at line %d in file %s", m_current_line, m_current_file.c_str()); } if(*ptr!=' ') { throw default_exception(default_exception::fmt(), "' ' expected after the number at line %d in file %s", m_current_line, m_current_file.c_str()); } ptr++; if(!m_use_map_names) { static symbol no_name(""); name=no_name; } else { std::string rest_of_line(ptr); const char * cut_off_word = " SC_EXTERN "; size_t cut_off_pos = rest_of_line.find(cut_off_word); if(cut_off_pos!=std::string::npos) { rest_of_line = rest_of_line.substr(0, cut_off_pos); } cut_off_word = " _ZONE_"; cut_off_pos = rest_of_line.find(cut_off_word); if(cut_off_pos!=std::string::npos) { rest_of_line = rest_of_line.substr(0, cut_off_pos); } const char * const ignored_suffix = "Constant "; const size_t ignored_suffix_len = 9; if(rest_of_line.size()>ignored_suffix_len && rest_of_line.substr(rest_of_line.size()-ignored_suffix_len)==ignored_suffix) { rest_of_line = rest_of_line.substr(0, rest_of_line.size()-ignored_suffix_len); } if(rest_of_line[rest_of_line.size()-1]==' ') { rest_of_line = rest_of_line.substr(0, rest_of_line.size()-1); } name = symbol(rest_of_line.c_str()); } return true; } void parse_map_file(const std::string & fname) { SASSERT(file_exists(fname)); IF_VERBOSE(10, verbose_stream() << "Parsing map file " << fname << "\n";); flet flet_cur_file(m_current_file, fname); flet flet_cur_line(m_current_line, 0); std::string sort_name_str = get_file_name_without_extension(fname); symbol sort_name(sort_name_str.c_str()); uint64_set & sort_elements = ensure_sort_content(sort_name); //std::ifstream stm(fname.c_str(), std::ios_base::binary); //SASSERT(!stm.fail()); //line_reader rdr(stm); line_reader rdr(fname.c_str()); while(!rdr.eof()) { m_current_line++; char * full_line = rdr.get_line(); uint64_t num; symbol el_name; if(!parse_map_line(full_line, num, el_name)) { continue; } sort_elements.insert(num); if(m_use_map_names) { num2sym::entry * e = m_number_names.insert_if_not_there2(num, el_name); if(e->get_data().m_value!=el_name) { warning_msg("mismatch of number names on line %d in file %s. old: \"%s\" new: \"%s\"", m_current_line, fname.c_str(), e->get_data().m_value.bare_str(), el_name.bare_str()); } } } } }; parser* parser::create(context& ctx, ast_manager& m) { return alloc(dparser, ctx, m); } wpa_parser * wpa_parser::create(context& ctx, ast_manager & ast_manager) { return alloc(wpa_parser_impl, ctx); } z3-z3-4.8.7/src/muz/fp/datalog_parser.h000066400000000000000000000015011356505360400175730ustar00rootroot00000000000000/*++ Copyright (c) 2010 Microsoft Corporation Module Name: datalog_parser.h Abstract: Parser for Datalogish files Author: Nikolaj Bjorner (nbjorner) 2010-5-17 Revision History: --*/ #ifndef DATALOG_PARSER_H_ #define DATALOG_PARSER_H_ #include "ast/ast.h" #include "muz/base/dl_context.h" namespace datalog { class parser { public: static parser * create(context& ctx, ast_manager & ast_manager); virtual ~parser() {} virtual bool parse_file(char const * path) = 0; virtual bool parse_string(char const * string) = 0; }; class wpa_parser { public: static wpa_parser * create(context& ctx, ast_manager & ast_manager); virtual ~wpa_parser() {} virtual bool parse_directory(char const * path) = 0; }; }; #endif z3-z3-4.8.7/src/muz/fp/dl_cmds.cpp000066400000000000000000000363411356505360400165560ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: dl_cmds.cpp Abstract: Datalog commands for SMT2 front-end. Author: Leonardo (leonardo) 2011-03-28 Notes: --*/ #include "cmd_context/cmd_context.h" #include "muz/fp/dl_cmds.h" #include "muz/rel/dl_external_relation.h" #include "muz/base/dl_context.h" #include "muz/fp/dl_register_engine.h" #include "ast/dl_decl_plugin.h" #include "muz/rel/dl_instruction.h" #include "muz/rel/dl_compiler.h" #include "muz/base/dl_rule.h" #include "ast/ast_pp.h" #include "cmd_context/parametric_cmd.h" #include "util/cancel_eh.h" #include "util/scoped_ctrl_c.h" #include "util/scoped_timer.h" #include "util/trail.h" #include "muz/base/fp_params.hpp" #include struct dl_context { smt_params m_fparams; params_ref m_params_ref; fp_params m_params; cmd_context & m_cmd; datalog::register_engine m_register_engine; dl_collected_cmds* m_collected_cmds; unsigned m_ref_count; datalog::dl_decl_plugin* m_decl_plugin; scoped_ptr m_context; trail_stack m_trail; fp_params const& get_params() { init(); return m_context->get_params(); } dl_context(cmd_context & ctx, dl_collected_cmds* collected_cmds): m_params(m_params_ref), m_cmd(ctx), m_collected_cmds(collected_cmds), m_ref_count(0), m_decl_plugin(nullptr), m_trail(*this) {} void inc_ref() { ++m_ref_count; } void dec_ref() { --m_ref_count; if (0 == m_ref_count) { dealloc(this); } } void init() { ast_manager& m = m_cmd.m(); if (!m_context) { m_context = alloc(datalog::context, m, m_register_engine, m_fparams, m_params_ref); } if (!m_decl_plugin) { symbol name("datalog_relation"); if (m.has_plugin(name)) { m_decl_plugin = static_cast(m_cmd.m().get_plugin(m.mk_family_id(name))); } else { m_decl_plugin = alloc(datalog::dl_decl_plugin); m.register_plugin(symbol("datalog_relation"), m_decl_plugin); } } } void reset() { m_context = nullptr; } void register_predicate(func_decl* pred, unsigned num_kinds, symbol const* kinds) { if (m_collected_cmds) { m_collected_cmds->m_rels.push_back(pred); m_trail.push(push_back_vector(m_collected_cmds->m_rels)); } dlctx().register_predicate(pred, false); dlctx().set_predicate_representation(pred, num_kinds, kinds); } void add_rule(expr * rule, symbol const& name, unsigned bound) { init(); if (m_collected_cmds) { expr_ref rl = m_context->bind_vars(rule, true); m_collected_cmds->m_rules.push_back(rl); m_collected_cmds->m_names.push_back(name); m_trail.push(push_back_vector(m_collected_cmds->m_rules)); m_trail.push(push_back_vector >(m_collected_cmds->m_names)); } else { m_context->add_rule(rule, name, bound); } } bool collect_query(func_decl* q) { if (m_collected_cmds) { ast_manager& m = m_cmd.m(); expr_ref qr(m); expr_ref_vector args(m); for (unsigned i = 0; i < q->get_arity(); ++i) { args.push_back(m.mk_var(i, q->get_domain(i))); } qr = m.mk_app(q, args.size(), args.c_ptr()); qr = m_context->bind_vars(qr, false); m_collected_cmds->m_queries.push_back(qr); m_trail.push(push_back_vector(m_collected_cmds->m_queries)); return true; } else { return false; } } void push() { m_trail.push_scope(); dlctx().push(); } void pop() { m_trail.pop_scope(1); dlctx().pop(); } datalog::context & dlctx() { init(); return *m_context; } }; /** \brief rule command. It is also the owner of dl_context object. */ class dl_rule_cmd : public cmd { ref m_dl_ctx; mutable unsigned m_arg_idx; expr* m_t; symbol m_name; unsigned m_bound; public: dl_rule_cmd(dl_context * dl_ctx): cmd("rule"), m_dl_ctx(dl_ctx), m_arg_idx(0), m_t(nullptr), m_bound(UINT_MAX) {} char const * get_usage() const override { return "(forall (q) (=> (and body) head)) :optional-name :optional-recursion-bound"; } char const * get_descr(cmd_context & ctx) const override { return "add a Horn rule."; } unsigned get_arity() const override { return VAR_ARITY; } cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { switch(m_arg_idx) { case 0: return CPK_EXPR; case 1: return CPK_SYMBOL; case 2: return CPK_UINT; default: return CPK_SYMBOL; } } void set_next_arg(cmd_context & ctx, expr * t) override { m_t = t; m_arg_idx++; } void set_next_arg(cmd_context & ctx, symbol const & s) override { m_name = s; m_arg_idx++; } void set_next_arg(cmd_context & ctx, unsigned bound) override { m_bound = bound; m_arg_idx++; } void reset(cmd_context & ctx) override { m_dl_ctx->reset(); prepare(ctx); m_t = nullptr; } void prepare(cmd_context& ctx) override { m_arg_idx = 0; m_name = symbol::null; m_bound = UINT_MAX; } void finalize(cmd_context & ctx) override { } void execute(cmd_context & ctx) override { if (!m_t) throw cmd_exception("invalid rule, expected formula"); m_dl_ctx->add_rule(m_t, m_name, m_bound); } }; class dl_query_cmd : public parametric_cmd { ref m_dl_ctx; func_decl* m_target; public: dl_query_cmd(dl_context * dl_ctx): parametric_cmd("query"), m_dl_ctx(dl_ctx), m_target(nullptr) { } char const * get_usage() const override { return "predicate"; } char const * get_main_descr() const override { return "pose a query to a predicate based on the Horn rules."; } cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { if (m_target == nullptr) return CPK_FUNC_DECL; return parametric_cmd::next_arg_kind(ctx); } void set_next_arg(cmd_context & ctx, func_decl* t) override { m_target = t; if (t->get_family_id() != null_family_id) { throw cmd_exception("Invalid query argument, expected uinterpreted function name, but argument is interpreted"); } datalog::context& dlctx = m_dl_ctx->dlctx(); if (!dlctx.get_predicates().contains(t)) { throw cmd_exception("Invalid query argument, expected a predicate registered as a relation"); } } void prepare(cmd_context & ctx) override { ctx.m(); // ensure manager is initialized. parametric_cmd::prepare(ctx); m_target = nullptr; } void execute(cmd_context& ctx) override { if (m_target == nullptr) { throw cmd_exception("invalid query command, argument expected"); } if (m_dl_ctx->collect_query(m_target)) { return; } datalog::context& dlctx = m_dl_ctx->dlctx(); set_background(ctx); dlctx.updt_params(m_params); unsigned timeout = ctx.params().m_timeout; unsigned rlimit = ctx.params().rlimit(); cancel_eh eh(ctx.m().limit()); bool query_exn = false; lbool status = l_undef; { IF_VERBOSE(10, verbose_stream() << "(query)\n";); scoped_ctrl_c ctrlc(eh); scoped_timer timer(timeout, &eh); scoped_rlimit _rlimit(ctx.m().limit(), rlimit); cmd_context::scoped_watch sw(ctx); try { status = dlctx.rel_query(1, &m_target); } catch (z3_error & ex) { ctx.regular_stream() << "(error \"query failed: " << ex.msg() << "\")" << std::endl; print_statistics(ctx); throw ex; } catch (z3_exception& ex) { ctx.regular_stream() << "(error \"query failed: " << ex.msg() << "\")" << std::endl; query_exn = true; } } switch (status) { case l_false: ctx.regular_stream() << "unsat\n"; print_certificate(ctx); break; case l_true: ctx.regular_stream() << "sat\n"; print_answer(ctx); print_certificate(ctx); break; case l_undef: if (dlctx.get_status() == datalog::BOUNDED){ ctx.regular_stream() << "bounded\n"; print_certificate(ctx); break; } ctx.regular_stream() << "unknown\n"; switch(dlctx.get_status()) { case datalog::INPUT_ERROR: ctx.regular_stream() << "input error\n"; break; case datalog::MEMOUT: ctx.regular_stream() << "memory bounds exceeded\n"; break; case datalog::TIMEOUT: ctx.regular_stream() << "timeout\n"; break; case datalog::APPROX: ctx.regular_stream() << "approximated relations\n"; break; case datalog::OK: (void)query_exn; break; case datalog::CANCELED: ctx.regular_stream() << "canceled\n"; dlctx.display_profile(ctx.regular_stream()); break; default: UNREACHABLE(); break; } break; } dlctx.cleanup(); print_statistics(ctx); m_target = nullptr; } void init_pdescrs(cmd_context & ctx, param_descrs & p) override { m_dl_ctx->dlctx().collect_params(p); } private: void set_background(cmd_context& ctx) { datalog::context& dlctx = m_dl_ctx->dlctx(); for (expr * e : ctx.assertions()) { dlctx.assert_expr(e); } } void print_answer(cmd_context& ctx) { if (m_dl_ctx->get_params().print_answer()) { datalog::context& dlctx = m_dl_ctx->dlctx(); ast_manager& m = ctx.m(); expr_ref query_result(dlctx.get_answer_as_formula(), m); sbuffer var_names; unsigned num_decls = 0; ctx.display(ctx.regular_stream(), query_result, 0, num_decls, "X", var_names); ctx.regular_stream() << std::endl; } } void print_statistics(cmd_context& ctx) { if (ctx.params().m_statistics) { statistics st; datalog::context& dlctx = m_dl_ctx->dlctx(); dlctx.collect_statistics(st); st.update("time", ctx.get_seconds()); st.display_smt2(ctx.regular_stream()); } } void print_certificate(cmd_context& ctx) { if (m_dl_ctx->get_params().print_certificate()) { datalog::context& dlctx = m_dl_ctx->dlctx(); dlctx.display_certificate(ctx.regular_stream()); ctx.regular_stream() << "\n"; } } }; class dl_declare_rel_cmd : public cmd { ref m_dl_ctx; unsigned m_arg_idx; mutable unsigned m_query_arg_idx; symbol m_rel_name; ptr_vector m_domain; svector m_kinds; public: dl_declare_rel_cmd(dl_context * dl_ctx): cmd("declare-rel"), m_dl_ctx(dl_ctx), m_domain(0) {} char const * get_usage() const override { return " ( ...) *"; } char const * get_descr(cmd_context & ctx) const override { return "declare new relation"; } unsigned get_arity() const override { return VAR_ARITY; } void prepare(cmd_context & ctx) override { ctx.m(); // ensure manager is initialized. m_arg_idx = 0; m_query_arg_idx = 0; m_domain.reset(); m_kinds.reset(); } cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { switch(m_query_arg_idx++) { case 0: return CPK_SYMBOL; // relation name case 1: return CPK_SORT_LIST; // arguments default: return CPK_SYMBOL; // optional representation specification } } void set_next_arg(cmd_context & ctx, unsigned num, sort * const * slist) override { m_domain.reset(); m_domain.append(num, slist); m_arg_idx++; } void set_next_arg(cmd_context & ctx, symbol const & s) override { if(m_arg_idx==0) { m_rel_name = s; } else { SASSERT(m_arg_idx>1); m_kinds.push_back(s); } m_arg_idx++; } void execute(cmd_context & ctx) override { if(m_arg_idx<2) { throw cmd_exception("at least 2 arguments expected"); } ast_manager& m = ctx.m(); func_decl_ref pred( m.mk_func_decl(m_rel_name, m_domain.size(), m_domain.c_ptr(), m.mk_bool_sort()), m); ctx.insert(pred); m_dl_ctx->register_predicate(pred, m_kinds.size(), m_kinds.c_ptr()); } }; class dl_declare_var_cmd : public cmd { unsigned m_arg_idx; symbol m_var_name; sort* m_var_sort; ref m_dl_ctx; public: dl_declare_var_cmd(dl_context* dl_ctx): cmd("declare-var"), m_arg_idx(0), m_dl_ctx(dl_ctx) {} char const * get_usage() const override { return " "; } char const * get_descr(cmd_context & ctx) const override { return "declare constant as variable"; } unsigned get_arity() const override { return 2; } void prepare(cmd_context & ctx) override { ctx.m(); // ensure manager is initialized. m_arg_idx = 0; } cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { SASSERT(m_arg_idx <= 1); if (m_arg_idx == 0) { return CPK_SYMBOL; } return CPK_SORT; } void set_next_arg(cmd_context & ctx, sort* s) override { m_var_sort = s; ++m_arg_idx; } void set_next_arg(cmd_context & ctx, symbol const & s) override { m_var_name = s; ++m_arg_idx; } void execute(cmd_context & ctx) override { ast_manager& m = ctx.m(); func_decl_ref var(m.mk_func_decl(m_var_name, 0, static_cast(nullptr), m_var_sort), m); ctx.insert(var); m_dl_ctx->dlctx().register_variable(var); } }; static void install_dl_cmds_aux(cmd_context& ctx, dl_collected_cmds* collected_cmds) { dl_context * dl_ctx = alloc(dl_context, ctx, collected_cmds); ctx.insert(alloc(dl_rule_cmd, dl_ctx)); ctx.insert(alloc(dl_query_cmd, dl_ctx)); ctx.insert(alloc(dl_declare_rel_cmd, dl_ctx)); ctx.insert(alloc(dl_declare_var_cmd, dl_ctx)); } void install_dl_cmds(cmd_context & ctx) { install_dl_cmds_aux(ctx, nullptr); } void install_dl_collect_cmds(dl_collected_cmds& collected_cmds, cmd_context & ctx) { install_dl_cmds_aux(ctx, &collected_cmds); } z3-z3-4.8.7/src/muz/fp/dl_cmds.h000066400000000000000000000012031356505360400162100ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: dl_cmds.h Abstract: Datalog commands for SMT2 front-end. Author: Nikolaj Bjorner (nbjorner) 2012-11-17 Notes: --*/ #ifndef DL_CMDS_H_ #define DL_CMDS_H_ #include "ast/ast.h" class cmd_context; struct dl_collected_cmds { expr_ref_vector m_rules; svector m_names; expr_ref_vector m_queries; func_decl_ref_vector m_rels; dl_collected_cmds(ast_manager& m) : m_rules(m), m_queries(m), m_rels(m) {} }; void install_dl_cmds(cmd_context & ctx); void install_dl_collect_cmds(dl_collected_cmds& collected_cmds, cmd_context& ctx); #endif z3-z3-4.8.7/src/muz/fp/dl_register_engine.cpp000066400000000000000000000023231356505360400207720ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: dl_register_engine.cpp Abstract: Class for creating Datalog engines. Author: Nikolaj Bjorner (nbjorner) 2013-08-28 Revision History: --*/ #include "muz/fp/dl_register_engine.h" #include "muz/bmc/dl_bmc_engine.h" #include "muz/clp/clp_context.h" #include "muz/tab/tab_context.h" #include "muz/rel/rel_context.h" #include "muz/ddnf/ddnf.h" #include "muz/spacer/spacer_dl_interface.h" namespace datalog { register_engine::register_engine(): m_ctx(nullptr) {} engine_base* register_engine::mk_engine(DL_ENGINE engine_type) { switch(engine_type) { case SPACER_ENGINE: return alloc(spacer::dl_interface, *m_ctx); case DATALOG_ENGINE: return alloc(rel_context, *m_ctx); case BMC_ENGINE: case QBMC_ENGINE: return alloc(bmc, *m_ctx); case TAB_ENGINE: return alloc(tab, *m_ctx); case CLP_ENGINE: return alloc(clp, *m_ctx); case DDNF_ENGINE: return alloc(ddnf, *m_ctx); case LAST_ENGINE: UNREACHABLE(); return nullptr; } UNREACHABLE(); return nullptr; } } z3-z3-4.8.7/src/muz/fp/dl_register_engine.h000066400000000000000000000011271356505360400204400ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: dl_register_engine.h Abstract: Class for creating Datalog engines. Author: Nikolaj Bjorner (nbjorner) 2013-08-28 Revision History: --*/ #ifndef DL_REGISTER_ENGINE_H_ #define DL_REGISTER_ENGINE_H_ #include "muz/base/dl_context.h" namespace datalog { class register_engine : public register_engine_base { context* m_ctx; public: register_engine(); engine_base* mk_engine(DL_ENGINE engine_type) override; void set_context(context* ctx) override { m_ctx = ctx; } }; } #endif z3-z3-4.8.7/src/muz/fp/horn_tactic.cpp000066400000000000000000000312521356505360400174420ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: horn_tactic.h Abstract: HORN as a tactic to solve Horn clauses. Author: Nikolaj Bjorner (nbjorner) 2012-11-16. Revision History: --*/ #include "tactic/tactical.h" #include "tactic/model_converter.h" #include "tactic/proof_converter.h" #include "muz/fp/horn_tactic.h" #include "muz/base/dl_context.h" #include "muz/fp/dl_register_engine.h" #include "ast/rewriter/expr_replacer.h" #include "muz/base/dl_rule_transformer.h" #include "muz/transforms/dl_mk_slice.h" #include "tactic/generic_model_converter.h" #include "muz/transforms/dl_transforms.h" #include "muz/base/fp_params.hpp" #include "ast/ast_util.h" #include "ast/rewriter/var_subst.h" class horn_tactic : public tactic { struct imp { ast_manager& m; bool m_is_simplify; datalog::register_engine m_register_engine; datalog::context m_ctx; smt_params m_fparams; expr_free_vars m_free_vars; imp(bool t, ast_manager & m, params_ref const & p): m(m), m_is_simplify(t), m_ctx(m, m_register_engine, m_fparams) { updt_params(p); } void updt_params(params_ref const & p) { m_ctx.updt_params(p); } void collect_param_descrs(param_descrs & r) { m_ctx.collect_params(r); } void reset_statistics() { m_ctx.reset_statistics(); } void collect_statistics(statistics & st) const { m_ctx.collect_statistics(st); } void normalize(expr_ref& f) { bool is_positive = true; expr* e = nullptr; while (true) { if (is_forall(f) && is_positive) { f = to_quantifier(f)->get_expr(); } else if (is_exists(f) && !is_positive) { f = to_quantifier(f)->get_expr(); } else if (m.is_not(f, e)) { is_positive = !is_positive; f = e; } else { break; } } if (!is_positive) { f = m.mk_not(f); } } bool is_predicate(expr* a) { SASSERT(m.is_bool(a)); return is_app(a) && to_app(a)->get_decl()->get_family_id() == null_family_id; } void register_predicate(expr* a) { SASSERT(is_predicate(a)); m_ctx.register_predicate(to_app(a)->get_decl(), false); } void check_predicate(ast_mark& mark, expr* a) { ptr_vector todo; todo.push_back(a); while (!todo.empty()) { a = todo.back(); todo.pop_back(); if (mark.is_marked(a)) { continue; } mark.mark(a, true); if (is_quantifier(a)) { a = to_quantifier(a)->get_expr(); todo.push_back(a); } else if (m.is_not(a) || m.is_and(a) || m.is_or(a) || m.is_implies(a)) { todo.append(to_app(a)->get_num_args(), to_app(a)->get_args()); } else if (m.is_ite(a)) { todo.push_back(to_app(a)->get_arg(1)); todo.push_back(to_app(a)->get_arg(2)); } else if (is_predicate(a)) { register_predicate(a); } } } enum formula_kind { IS_RULE, IS_QUERY, IS_NONE }; bool is_implication(expr* f) { expr* e1; while (is_forall(f)) { f = to_quantifier(f)->get_expr(); } while (m.is_implies(f, e1, f)) ; return is_predicate(f); } formula_kind get_formula_kind(expr_ref& f) { expr_ref tmp(f); normalize(tmp); ast_mark mark; expr_ref_vector args(m), body(m); expr_ref head(m); expr* a = nullptr, *a1 = nullptr; flatten_or(tmp, args); for (unsigned i = 0; i < args.size(); ++i) { a = args[i].get(); check_predicate(mark, a); if (m.is_not(a, a1)) { body.push_back(a1); } else if (is_predicate(a)) { if (head) { return IS_NONE; } head = a; } else { body.push_back(m.mk_not(a)); } } if (head) { if (!is_implication(f)) { f = m.mk_and(body.size(), body.c_ptr()); f = m.mk_implies(f, head); } return IS_RULE; } else { f = m.mk_and(body.size(), body.c_ptr()); return IS_QUERY; } } expr_ref mk_rule(expr* body, expr* head) { return expr_ref(m.mk_implies(body, head), m); } void operator()(goal_ref const & g, goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); tactic_report report("horn", *g); bool produce_proofs = g->proofs_enabled(); if (produce_proofs) { if (!m_ctx.generate_proof_trace()) { params_ref params = m_ctx.get_params().p; params.set_bool("generate_proof_trace", true); updt_params(params); } } unsigned sz = g->size(); expr_ref q(m), f(m); expr_ref_vector queries(m); std::stringstream msg; m_ctx.reset(); m_ctx.ensure_opened(); for (unsigned i = 0; i < sz; i++) { f = g->form(i); formula_kind k = get_formula_kind(f); switch(k) { case IS_RULE: m_ctx.add_rule(f, symbol::null); break; case IS_QUERY: queries.push_back(f); break; default: msg << "formula is not in Horn fragment: " << mk_pp(g->form(i), m) << "\n"; TRACE("horn", tout << msg.str();); throw tactic_exception(msg.str()); } } if (queries.size() != 1 || m_is_simplify) { q = m.mk_fresh_const("query", m.mk_bool_sort()); register_predicate(q); for (unsigned i = 0; i < queries.size(); ++i) { f = mk_rule(queries[i].get(), q); bind_variables(f); m_ctx.add_rule(f, symbol::null); } queries.reset(); queries.push_back(q); generic_model_converter* mc1 = alloc(generic_model_converter, m, "horn"); mc1->hide(q); g->add(mc1); } SASSERT(queries.size() == 1); q = queries[0].get(); proof_converter_ref pc = g->pc(); model_converter_ref mc; if (m_is_simplify) { simplify(q, g, result, mc, pc); } else { verify(q, g, result, mc, pc); } g->set(pc.get()); g->set(mc.get()); } void verify(expr* q, goal_ref const& g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc) { lbool is_reachable = l_undef; try { is_reachable = m_ctx.query(q); } catch (default_exception& ex) { IF_VERBOSE(1, verbose_stream() << ex.msg() << "\n";); throw ex; } g->inc_depth(); bool produce_models = g->models_enabled(); bool produce_proofs = g->proofs_enabled(); result.push_back(g.get()); switch (is_reachable) { case l_true: { // goal is unsat if (produce_proofs) { proof_ref proof = m_ctx.get_proof(); pc = proof2proof_converter(m, proof); g->assert_expr(m.mk_false(), proof, nullptr); } else { g->assert_expr(m.mk_false()); } break; } case l_false: { // goal is sat g->reset(); if (produce_models) { model_ref md = m_ctx.get_model(); model_converter_ref mc2 = model2model_converter(&*md); if (mc) { mc = concat(mc.get(), mc2.get()); } else { mc = mc2; } } break; } case l_undef: // subgoal is unchanged. break; } TRACE("horn", g->display(tout);); SASSERT(g->is_well_sorted()); } void bind_variables(expr_ref& f) { m_free_vars.reset(); m_free_vars(f); m_free_vars.set_default_sort(m.mk_bool_sort()); if (!m_free_vars.empty()) { m_free_vars.reverse(); svector names; for (unsigned i = 0; i < m_free_vars.size(); ++i) { names.push_back(symbol(m_free_vars.size() - i - 1)); } f = m.mk_forall(m_free_vars.size(), m_free_vars.c_ptr(), names.c_ptr(), f); } } void simplify(expr* q, goal_ref const& g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc) { expr_ref fml(m); func_decl* query_pred = to_app(q)->get_decl(); m_ctx.set_output_predicate(query_pred); m_ctx.get_rules(); // flush adding rules. apply_default_transformation(m_ctx); if (m_ctx.xform_slice()) { datalog::rule_transformer transformer(m_ctx); datalog::mk_slice* slice = alloc(datalog::mk_slice, m_ctx); transformer.register_plugin(slice); m_ctx.transform_rules(transformer); } expr_substitution sub(m); sub.insert(q, m.mk_false()); scoped_ptr rep = mk_default_expr_replacer(m); rep->set_substitution(&sub); g->inc_depth(); g->reset(); result.push_back(g.get()); datalog::rule_set const& rules = m_ctx.get_rules(); datalog::rule_set::iterator it = rules.begin(), end = rules.end(); for (; it != end; ++it) { datalog::rule* r = *it; m_ctx.get_rule_manager().to_formula(*r, fml); (*rep)(fml); g->assert_expr(fml); } } }; bool m_is_simplify; params_ref m_params; statistics m_stats; imp * m_imp; public: horn_tactic(bool t, ast_manager & m, params_ref const & p): m_is_simplify(t), m_params(p) { m_imp = alloc(imp, t, m, p); } tactic * translate(ast_manager & m) override { return alloc(horn_tactic, m_is_simplify, m, m_params); } ~horn_tactic() override { dealloc(m_imp); } void updt_params(params_ref const & p) override { m_params = p; m_imp->updt_params(p); } void collect_param_descrs(param_descrs & r) override { m_imp->collect_param_descrs(r); } void operator()(goal_ref const & in, goal_ref_buffer & result) override { (*m_imp)(in, result); } void collect_statistics(statistics & st) const override { m_imp->collect_statistics(st); st.copy(m_stats); } void reset_statistics() override { m_stats.reset(); m_imp->reset_statistics(); } void cleanup() override { ast_manager & m = m_imp->m; m_imp->collect_statistics(m_stats); dealloc(m_imp); m_imp = alloc(imp, m_is_simplify, m, m_params); } }; tactic * mk_horn_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(horn_tactic, false, m, p)); } tactic * mk_horn_simplify_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(horn_tactic, true, m, p)); } z3-z3-4.8.7/src/muz/fp/horn_tactic.h000066400000000000000000000012411356505360400171020ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: horn_tactic.h Abstract: PDR as a tactic to solve Horn clauses. Author: Nikolaj Bjorner (nbjorner) 2012-11-16. Revision History: --*/ #ifndef HORN_TACTIC_H_ #define HORN_TACTIC_H_ #include "util/params.h" class ast_manager; class tactic; tactic * mk_horn_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("horn", "apply tactic for horn clauses.", "mk_horn_tactic(m, p)") */ tactic * mk_horn_simplify_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("horn-simplify", "simplify horn clauses.", "mk_horn_simplify_tactic(m, p)") */ #endif z3-z3-4.8.7/src/muz/rel/000077500000000000000000000000001356505360400146135ustar00rootroot00000000000000z3-z3-4.8.7/src/muz/rel/CMakeLists.txt000066400000000000000000000012431356505360400173530ustar00rootroot00000000000000z3_add_component(rel SOURCES aig_exporter.cpp check_relation.cpp dl_base.cpp dl_bound_relation.cpp dl_check_table.cpp dl_compiler.cpp dl_external_relation.cpp dl_finite_product_relation.cpp dl_instruction.cpp dl_interval_relation.cpp dl_lazy_table.cpp dl_mk_explanations.cpp dl_mk_similarity_compressor.cpp dl_mk_simple_joins.cpp dl_product_relation.cpp dl_relation_manager.cpp dl_sieve_relation.cpp dl_sparse_table.cpp dl_table.cpp dl_table_relation.cpp doc.cpp karr_relation.cpp rel_context.cpp tbv.cpp udoc_relation.cpp COMPONENT_DEPENDENCIES muz transforms ) z3-z3-4.8.7/src/muz/rel/aig_exporter.cpp000066400000000000000000000255341356505360400200200ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: aig_exporter.cpp Abstract: Export AIG files from horn clauses --*/ #include "muz/rel/aig_exporter.h" #include "muz/base/dl_context.h" #include namespace datalog { aig_exporter::aig_exporter(const rule_set& rules, context& ctx, const fact_vector *facts) : m_rules(rules), m_facts(facts), m(ctx.get_manager()), m_rm(ctx.get_rule_manager()), m_aigm(m), m_next_decl_id(1), m_next_aig_expr_id(2), m_num_and_gates(0), m_latch_vars(m), m_latch_varsp(m), m_ruleid_var_set(m), m_ruleid_varp_set(m) { std::set predicates; for (rule_set::decl2rules::iterator I = m_rules.begin_grouped_rules(), E = m_rules.end_grouped_rules(); I != E; ++I) { predicates.insert(I->m_key); } for (fact_vector::const_iterator I = facts->begin(), E = facts->end(); I != E; ++I) { predicates.insert(I->first); } // reserve pred id = 0 for initialization purposes unsigned num_preds = (unsigned)predicates.size() + 1; // poor's man round-up log2 unsigned preds_bitsize = log2(num_preds); if ((1U << preds_bitsize) < num_preds) ++preds_bitsize; SASSERT((1U << preds_bitsize) >= num_preds); for (unsigned i = 0; i < preds_bitsize; ++i) { m_ruleid_var_set.push_back(m.mk_fresh_const("rule_id", m.mk_bool_sort())); m_ruleid_varp_set.push_back(m.mk_fresh_const("rule_id_p", m.mk_bool_sort())); } } void aig_exporter::mk_latch_vars(unsigned n) { for (unsigned i = m_latch_vars.size(); i <= n; ++i) { m_latch_vars.push_back(m.mk_fresh_const("latch_var", m.mk_bool_sort())); m_latch_varsp.push_back(m.mk_fresh_const("latch_varp", m.mk_bool_sort())); } SASSERT(m_latch_vars.size() > n); } expr* aig_exporter::get_latch_var(unsigned i, const expr_ref_vector& vars) { mk_latch_vars(i); return vars.get(i); } void aig_exporter::assert_pred_id(func_decl *decl, const expr_ref_vector& vars, expr_ref_vector& exprs) { unsigned id = 0; if (decl && !m_decl_id_map.find(decl, id)) { id = m_next_decl_id++; SASSERT(id < (1U << vars.size())); m_decl_id_map.insert(decl, id); } for (unsigned i = 0; i < vars.size(); ++i) { exprs.push_back((id & (1U << i)) ? vars[i] : m.mk_not(vars[i])); } } void aig_exporter::collect_var_substs(substitution& subst, const app *h, const expr_ref_vector& vars, expr_ref_vector& eqs) { for (unsigned i = 0; i < h->get_num_args(); ++i) { expr *arg = h->get_arg(i); expr *latchvar = get_latch_var(i, vars); if (is_var(arg)) { var *v = to_var(arg); expr_offset othervar; if (subst.find(v, 0, othervar)) { eqs.push_back(m.mk_eq(latchvar, othervar.get_expr())); } else { subst.insert(v, 0, expr_offset(latchvar, 0)); } } else { eqs.push_back(m.mk_eq(latchvar, arg)); } } } void aig_exporter::operator()(std::ostream& out) { expr_ref_vector transition_function(m), output_preds(m); var_ref_vector input_vars(m); rule_counter& vc = m_rm.get_counter(); expr_ref_vector exprs(m); substitution subst(m); for (rule_set::decl2rules::iterator I = m_rules.begin_grouped_rules(), E = m_rules.end_grouped_rules(); I != E; ++I) { for (rule_vector::iterator II = I->get_value()->begin(), EE = I->get_value()->end(); II != EE; ++II) { rule *r = *II; unsigned numqs = r->get_positive_tail_size(); if (numqs > 1) { std::cerr << "non-linear clauses not supported\n"; exit(-1); } if (numqs != r->get_uninterpreted_tail_size()) { std::cerr << "negation of queries not supported\n"; exit(-1); } exprs.reset(); assert_pred_id(numqs ? r->get_tail(0)->get_decl() : nullptr, m_ruleid_var_set, exprs); assert_pred_id(r->get_head()->get_decl(), m_ruleid_varp_set, exprs); subst.reset(); subst.reserve(1, vc.get_max_rule_var(*r)+1); if (numqs) collect_var_substs(subst, r->get_tail(0), m_latch_vars, exprs); collect_var_substs(subst, r->get_head(), m_latch_varsp, exprs); for (unsigned i = numqs; i < r->get_tail_size(); ++i) { expr_ref e(m); subst.apply(r->get_tail(i), e); exprs.push_back(e); } transition_function.push_back(m.mk_and(exprs.size(), exprs.c_ptr())); } } // collect table facts if (m_facts) { for (fact_vector::const_iterator I = m_facts->begin(), E = m_facts->end(); I != E; ++I) { exprs.reset(); assert_pred_id(nullptr, m_ruleid_var_set, exprs); assert_pred_id(I->first, m_ruleid_varp_set, exprs); for (unsigned i = 0; i < I->second.size(); ++i) { exprs.push_back(m.mk_eq(get_latch_var(i, m_latch_varsp), I->second[i])); } transition_function.push_back(m.mk_and(exprs.size(), exprs.c_ptr())); } } expr *tr = m.mk_or(transition_function.size(), transition_function.c_ptr()); aig_ref aig = m_aigm.mk_aig(tr); expr_ref aig_expr(m); m_aigm.to_formula(aig, aig_expr); #if 0 std::cout << mk_pp(tr, m) << "\n\n"; std::cout << mk_pp(aig_expr, m) << "\n\n"; #endif // make rule_id vars latches for (unsigned i = 0; i < m_ruleid_var_set.size(); ++i) { m_latch_vars.push_back(m_ruleid_var_set.get(i)); m_latch_varsp.push_back(m_ruleid_varp_set.get(i)); } // create vars for latches for (unsigned i = 0; i < m_latch_vars.size(); ++i) { mk_var(m_latch_vars.get(i)); mk_input_var(m_latch_varsp.get(i)); } unsigned tr_id = expr_to_aig(aig_expr); // create latch next state variables: (ite tr varp var) unsigned_vector latch_varp_ids; for (unsigned i = 0; i < m_latch_vars.size(); ++i) { unsigned in_val = mk_and(tr_id, get_var(m_latch_varsp.get(i))); unsigned latch_val = mk_and(neg(tr_id), get_var(m_latch_vars.get(i))); latch_varp_ids.push_back(mk_or(in_val, latch_val)); } m_latch_varsp.reset(); // create output variable (true iff an output predicate is derivable) unsigned output_id = 0; { expr_ref_vector output(m); const func_decl_set& preds = m_rules.get_output_predicates(); for (func_decl_set::iterator I = preds.begin(), E = preds.end(); I != E; ++I) { exprs.reset(); assert_pred_id(*I, m_ruleid_var_set, exprs); output.push_back(m.mk_and(exprs.size(), exprs.c_ptr())); } expr *out = m.mk_or(output.size(), output.c_ptr()); aig = m_aigm.mk_aig(out); m_aigm.to_formula(aig, aig_expr); output_id = expr_to_aig(aig_expr); #if 0 std::cout << "output formula\n"; std::cout << mk_pp(out, m) << "\n\n"; std::cout << mk_pp(aig_expr, m) << "\n\n"; #endif } // 1) print header // aag var_index inputs latches outputs andgates out << "aag " << (m_next_aig_expr_id-1)/2 << ' ' << m_input_vars.size() << ' ' << m_latch_vars.size() << " 1 " << m_num_and_gates << '\n'; // 2) print inputs for (unsigned i = 0; i < m_input_vars.size(); ++i) { out << m_input_vars[i] << '\n'; } // 3) print latches for (unsigned i = 0; i < m_latch_vars.size(); ++i) { out << get_var(m_latch_vars.get(i)) << ' ' << latch_varp_ids[i] << '\n'; } // 4) print outputs (just one for now) out << output_id << '\n'; // 5) print formula out << m_buffer.str(); } unsigned aig_exporter::expr_to_aig(const expr *e) { unsigned id; if (m_aig_expr_id_map.find(e, id)) return id; if (is_uninterp_const(e)) return get_var(e); switch (e->get_kind()) { case AST_APP: { const app *a = to_app(e); switch (a->get_decl_kind()) { case OP_OR: SASSERT(a->get_num_args() > 0); id = expr_to_aig(a->get_arg(0)); for (unsigned i = 1; i < a->get_num_args(); ++i) { id = mk_or(id, expr_to_aig(a->get_arg(i))); } m_aig_expr_id_map.insert(e, id); return id; case OP_NOT: return neg(expr_to_aig(a->get_arg(0))); case OP_FALSE: return 0; case OP_TRUE: return 1; } break;} case AST_VAR: return get_var(e); default: UNREACHABLE(); } UNREACHABLE(); return 0; } unsigned aig_exporter::neg(unsigned id) const { return (id % 2) ? (id-1) : (id+1); } unsigned aig_exporter::mk_and(unsigned id1, unsigned id2) { if (id1 > id2) std::swap(id1, id2); std::pair key(id1, id2); and_gates_map::const_iterator I = m_and_gates_map.find(key); if (I != m_and_gates_map.end()) return I->second; unsigned id = mk_expr_id(); m_buffer << id << ' ' << id1 << ' ' << id2 << '\n'; m_and_gates_map[key] = id; ++m_num_and_gates; return id; } unsigned aig_exporter::mk_or(unsigned id1, unsigned id2) { return neg(mk_and(neg(id1), neg(id2))); } unsigned aig_exporter::get_var(const expr *e) { unsigned id; if (m_aig_expr_id_map.find(e, id)) return id; return mk_input_var(e); } unsigned aig_exporter::mk_var(const expr *e) { SASSERT(!m_aig_expr_id_map.contains(e)); unsigned id = mk_expr_id(); m_aig_expr_id_map.insert(e, id); return id; } unsigned aig_exporter::mk_input_var(const expr *e) { SASSERT(!m_aig_expr_id_map.contains(e)); unsigned id = mk_expr_id(); m_input_vars.push_back(id); if (e) m_aig_expr_id_map.insert(e, id); return id; } unsigned aig_exporter::mk_expr_id() { unsigned id = m_next_aig_expr_id; m_next_aig_expr_id += 2; return id; } } z3-z3-4.8.7/src/muz/rel/aig_exporter.h000066400000000000000000000040301356505360400174510ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: aig_exporter.h Abstract: Export AIG files from horn clauses --*/ #ifndef AIG_EXPORTER_H_ #define AIG_EXPORTER_H_ #include "tactic/aig/aig.h" #include "muz/base/dl_rule_set.h" #include #include #include "muz/rel/rel_context.h" namespace datalog { class aig_exporter { public: aig_exporter(const rule_set& rules, context& ctx, const fact_vector *facts = nullptr); void operator()(std::ostream& out); private: typedef obj_map decl_id_map; typedef obj_map aig_expr_id_map; typedef std::map, unsigned> and_gates_map; const rule_set& m_rules; const fact_vector *m_facts; ast_manager& m; rule_manager& m_rm; aig_manager m_aigm; decl_id_map m_decl_id_map; unsigned m_next_decl_id; aig_expr_id_map m_aig_expr_id_map; unsigned m_next_aig_expr_id; and_gates_map m_and_gates_map; unsigned m_num_and_gates; expr_ref_vector m_latch_vars, m_latch_varsp; expr_ref_vector m_ruleid_var_set, m_ruleid_varp_set; unsigned_vector m_input_vars; std::stringstream m_buffer; void mk_latch_vars(unsigned n); expr* get_latch_var(unsigned i, const expr_ref_vector& vars); void assert_pred_id(func_decl *decl, const expr_ref_vector& vars, expr_ref_vector& exprs); void collect_var_substs(substitution& subst, const app *h, const expr_ref_vector& vars, expr_ref_vector& eqs); unsigned expr_to_aig(const expr *e); unsigned neg(unsigned id) const; unsigned mk_and(unsigned id1, unsigned id2); unsigned mk_or(unsigned id1, unsigned id2); unsigned get_var(const expr *e); unsigned mk_var(const expr *e); unsigned mk_input_var(const expr *e = nullptr); unsigned mk_expr_id(); }; } #endif z3-z3-4.8.7/src/muz/rel/check_relation.cpp000066400000000000000000000767221356505360400203070ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "muz/rel/check_relation.h" #include "muz/rel/dl_relation_manager.h" #include "ast/ast_util.h" #include "smt/smt_kernel.h" #include namespace datalog { check_relation::check_relation(check_relation_plugin& p, relation_signature const& sig, relation_base* r): relation_base(p, sig), m(p.get_ast_manager()), m_relation(r), m_fml(m) { m_relation->to_formula(m_fml); } check_relation::~check_relation() { m_relation->deallocate(); } void check_relation::check_equiv(char const* objective, expr* f1, expr* f2) const { get_plugin().check_equiv(objective, f1, f2); } void check_relation::consistent_formula() { expr_ref fml(m); m_relation->to_formula(fml); if (m_fml != fml) { IF_VERBOSE(0, display(verbose_stream() << "relation does not have a consistent formula");); } } expr_ref check_relation::mk_eq(relation_fact const& f) const { relation_signature const& sig = get_signature(); expr_ref_vector conjs(m); for (unsigned i = 0; i < sig.size(); ++i) { conjs.push_back(m.mk_eq(m.mk_var(i, sig[i]), f[i])); } return expr_ref(mk_and(m, conjs.size(), conjs.c_ptr()), m); } expr_ref check_relation::ground(expr* fml) const { return get_plugin().ground(*this, fml); } expr_ref check_relation_plugin::ground(relation_base const& dst) const { expr_ref fml(m); dst.to_formula(fml); return ground(dst, fml); } expr_ref check_relation_plugin::ground(relation_base const& dst, expr* fml) const { relation_signature const& sig = dst.get_signature(); var_subst sub(m, false); expr_ref_vector vars(m); for (unsigned i = 0; i < sig.size(); ++i) { vars.push_back(m.mk_const(symbol(i), sig[i])); } return sub(fml, vars.size(), vars.c_ptr()); } void check_relation::add_fact(const relation_fact & f) { expr_ref fml1(m); m_relation->add_fact(f); m_relation->to_formula(fml1); m_fml = m.mk_or(m_fml, mk_eq(f)); check_equiv("add_fact", ground(m_fml), ground(fml1)); m_fml = fml1; } void check_relation::add_new_fact(const relation_fact & f) { expr_ref fml1(m); m_relation->add_new_fact(f); m_relation->to_formula(fml1); m_fml = m.mk_or(m_fml, mk_eq(f)); check_equiv("add_fact", ground(m_fml), ground(fml1)); m_fml = fml1; } bool check_relation::empty() const { bool result = m_relation->empty(); if (result && !m.is_false(m_fml)) { check_equiv("empty", m.mk_false(), ground(m_fml)); } return result; } bool check_relation::fast_empty() const { bool result = m_relation->fast_empty(); if (result && !m.is_false(m_fml)) { check_equiv("fast_empty", m.mk_false(), ground(m_fml)); } return result; } void check_relation::reset() { m_relation->reset(); m_fml = m.mk_false(); } bool check_relation::contains_fact(const relation_fact & f) const { bool result = m_relation->contains_fact(f); expr_ref fml1(m), fml2(m); fml1 = mk_eq(f); fml2 = m.mk_and(m_fml, fml1); if (result) { check_equiv("contains fact", ground(fml1), ground(fml2)); } else if (!m.is_false(m_fml)) { check_equiv("contains fact", ground(fml2), m.mk_false()); } return result; } check_relation * check_relation::clone() const { check_relation* result = check_relation_plugin::get(get_plugin().mk_empty(get_signature())); result->m_relation->deallocate(); result->m_relation = m_relation->clone(); result->m_relation->to_formula(result->m_fml); if (m_fml != result->m_fml) { check_equiv("clone", ground(m_fml), ground(result->m_fml)); } return result; } check_relation * check_relation::complement(func_decl* f) const { check_relation* result = check_relation_plugin::get(get_plugin().mk_empty(get_signature())); result->m_relation->deallocate(); result->m_relation = m_relation->complement(f); result->m_relation->to_formula(result->m_fml); expr_ref fml(m); fml = m.mk_not(m_fml); check_equiv("complement", ground(fml), ground(result->m_fml)); return result; } void check_relation::to_formula(expr_ref& fml) const { fml = m_fml; } check_relation_plugin& check_relation::get_plugin() const { return static_cast(relation_base::get_plugin()); } void check_relation::display(std::ostream& out) const { m_relation->display(out); out << m_fml << "\n"; } // ------------- check_relation_plugin::check_relation_plugin(relation_manager& rm): relation_plugin(check_relation_plugin::get_name(), rm), m(rm.get_context().get_manager()), m_base(nullptr) { } check_relation_plugin::~check_relation_plugin() { } check_relation& check_relation_plugin::get(relation_base& r) { return dynamic_cast(r); } check_relation* check_relation_plugin::get(relation_base* r) { return r?dynamic_cast(r):nullptr; } check_relation const & check_relation_plugin::get(relation_base const& r) { return dynamic_cast(r); } bool check_relation_plugin::can_handle_signature(const relation_signature & sig) { return m_base && m_base->can_handle_signature(sig); } relation_base * check_relation_plugin::mk_empty(const relation_signature & sig) { relation_base* r = m_base->mk_empty(sig); check_relation* result = alloc(check_relation, *this, sig, r); if (result->m_fml != m.mk_false()) { check_equiv("mk_empty", result->ground(result->m_fml), m.mk_false()); } return result; } relation_base * check_relation_plugin::mk_full(func_decl* p, const relation_signature & s) { relation_base* r = m_base->mk_full(p, s); check_relation* result = alloc(check_relation, *this, s, r); if (result->m_fml != m.mk_true()) { check_equiv("mk_full", result->ground(result->m_fml), m.mk_true()); } return result; } class check_relation_plugin::join_fn : public convenient_relation_join_fn { scoped_ptr m_join; public: join_fn(relation_join_fn* j, const relation_signature & o1_sig, const relation_signature & o2_sig, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) : convenient_join_fn(o1_sig, o2_sig, col_cnt, cols1, cols2), m_join(j) {} ~join_fn() override {} relation_base * operator()(const relation_base & r1, const relation_base & r2) override { check_relation const& t1 = get(r1); check_relation const& t2 = get(r2); check_relation_plugin& p = t1.get_plugin(); relation_base* r = (*m_join)(t1.rb(), t2.rb()); p.verify_join(r1, r2, *r, m_cols1, m_cols2); return alloc(check_relation, p, r->get_signature(), r); } }; relation_join_fn * check_relation_plugin::mk_join_fn( const relation_base & t1, const relation_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { relation_join_fn* j = m_base->mk_join_fn(get(t1).rb(), get(t2).rb(), col_cnt, cols1, cols2); return j?alloc(join_fn, j, t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2):nullptr; } class check_relation_plugin::join_project_fn : public convenient_relation_join_project_fn { scoped_ptr m_join; public: join_project_fn( relation_join_fn* j, const relation_signature & o1_sig, const relation_signature & o2_sig, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned* removed_cols) : convenient_join_project_fn(o1_sig, o2_sig, col_cnt, cols1, cols2, removed_col_cnt, removed_cols), m_join(j) {} ~join_project_fn() override {} relation_base * operator()(const relation_base & r1, const relation_base & r2) override { check_relation const& t1 = get(r1); check_relation const& t2 = get(r2); check_relation_plugin& p = t1.get_plugin(); relation_base* r = (*m_join)(t1.rb(), t2.rb()); p.verify_join_project(r1, r2, *r, m_cols1, m_cols2, m_removed_cols); return alloc(check_relation, p, r->get_signature(), r); } }; relation_join_fn * check_relation_plugin::mk_join_project_fn( const relation_base & t1, const relation_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols) { relation_join_fn* j = m_base->mk_join_project_fn(get(t1).rb(), get(t2).rb(), col_cnt, cols1, cols2, removed_col_cnt, removed_cols); return j?alloc(join_project_fn, j, t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2, removed_col_cnt, removed_cols):nullptr; } void check_relation_plugin::verify_filter_project( relation_base const& src, relation_base const& dst, app* cond, unsigned_vector const& removed_cols) { expr_ref fml1(m), fml2(m); src.to_formula(fml1); dst.to_formula(fml2); fml1 = m.mk_and(cond, fml1); verify_project(src, fml1, dst, fml2, removed_cols); } void check_relation_plugin::verify_project( relation_base const& src, relation_base const& dst, unsigned_vector const& removed_cols) { expr_ref fml1(m), fml2(m); src.to_formula(fml1); dst.to_formula(fml2); verify_project(src, fml1, dst, fml2, removed_cols); } void check_relation_plugin::verify_project( relation_base const& src, expr* f1, relation_base const& dst, expr* f2, unsigned_vector const& removed_cols) { expr_ref fml1 = ground(dst, mk_project(src.get_signature(), f1, removed_cols)); expr_ref fml2 = ground(dst, f2); check_equiv("project", fml1, fml2); } expr_ref check_relation_plugin::mk_project( relation_signature const& sig, expr* fml, unsigned_vector const& removed_cols) { expr_ref fml1(m); ptr_vector bound; svector names; expr_ref_vector vars(m); unsigned rm_cnt = removed_cols.size(); for (unsigned i = 0, j = 0, k = 0; i < sig.size(); ++i) { if (j < rm_cnt && removed_cols[j] == i) { std::ostringstream strm; strm << "x" << j; bound.push_back(sig[i]); names.push_back(symbol(strm.str().c_str())); vars.push_back(m.mk_var(j, sig[i])); ++j; } else { vars.push_back(m.mk_var(k + rm_cnt, sig[i])); ++k; } } var_subst sub(m, false); fml1 = sub(fml, vars.size(), vars.c_ptr()); bound.reverse(); fml1 = m.mk_exists(bound.size(), bound.c_ptr(), names.c_ptr(), fml1); return fml1; } void check_relation_plugin::verify_join_project( relation_base const& t1, relation_base const& t2, relation_base const& t, unsigned_vector const& cols1, unsigned_vector const& cols2, unsigned_vector const& rm_cols) { ast_manager& m = get_ast_manager(); relation_signature const& sigA = t1.get_signature(); relation_signature const& sigB = t2.get_signature(); relation_signature sig1; sig1.append(sigA); sig1.append(sigB); expr_ref fml1 = mk_join(t1, t2, cols1, cols2); fml1 = mk_project(sig1, fml1, rm_cols); fml1 = ground(t, fml1); expr_ref fml2(m); t.to_formula(fml2); fml2 = ground(t, fml2); check_equiv("join_project", fml1, fml2); } expr_ref check_relation_plugin::mk_join( relation_base const& t1, relation_base const& t2, unsigned_vector const& cols1, unsigned_vector const& cols2) { ast_manager& m = get_ast_manager(); expr_ref fml1(m), fml2(m), fml3(m); relation_signature const& sig1 = t1.get_signature(); relation_signature const& sig2 = t2.get_signature(); var_ref var1(m), var2(m); t1.to_formula(fml1); t2.to_formula(fml2); var_subst sub(m, false); expr_ref_vector vars(m); for (unsigned i = 0; i < sig2.size(); ++i) { vars.push_back(m.mk_var(i + sig1.size(), sig2[i])); } fml2 = sub(fml2, vars.size(), vars.c_ptr()); fml1 = m.mk_and(fml1, fml2); for (unsigned i = 0; i < cols1.size(); ++i) { unsigned v1 = cols1[i]; unsigned v2 = cols2[i]; var1 = m.mk_var(v1, sig1[v1]); var2 = m.mk_var(v2 + sig1.size(), sig2[v2]); fml1 = m.mk_and(m.mk_eq(var1, var2), fml1); } return fml1; } void check_relation_plugin::verify_permutation( relation_base const& src, relation_base const& dst, unsigned_vector const& cycle) { unsigned_vector perm; relation_signature const& sig1 = src.get_signature(); relation_signature const& sig2 = dst.get_signature(); for (unsigned i = 0; i < sig1.size(); ++i) { perm.push_back(i); } for (unsigned i = 0; i < cycle.size(); ++i) { unsigned j = (i + 1)%cycle.size(); unsigned col1 = cycle[i]; unsigned col2 = cycle[j]; perm[col2] = col1; } for (unsigned i = 0; i < perm.size(); ++i) { SASSERT(sig2[perm[i]] == sig1[i]); } expr_ref_vector sub(m); for (unsigned i = 0; i < perm.size(); ++i) { sub.push_back(m.mk_var(perm[i], sig1[i])); } var_subst subst(m, false); expr_ref fml1(m), fml2(m); src.to_formula(fml1); dst.to_formula(fml2); fml1 = subst(fml1, sub.size(), sub.c_ptr()); expr_ref_vector vars(m); for (unsigned i = 0; i < sig2.size(); ++i) { vars.push_back(m.mk_const(symbol(i), sig2[i])); } fml1 = subst(fml1, vars.size(), vars.c_ptr()); fml2 = subst(fml2, vars.size(), vars.c_ptr()); check_equiv("permutation", fml1, fml2); } void check_relation_plugin::verify_join( relation_base const& t1, relation_base const& t2, relation_base const& t, unsigned_vector const& cols1, unsigned_vector const& cols2) { expr_ref fml1 = ground(t, mk_join(t1, t2, cols1, cols2)); expr_ref fml2 = ground(t); check_equiv("join", fml1, fml2); } void check_relation_plugin::verify_filter(expr* fml0, relation_base const& t, expr* cond) { expr_ref fml1(m), fml2(m); fml1 = m.mk_and(fml0, cond); t.to_formula(fml2); relation_signature const& sig = t.get_signature(); expr_ref_vector vars(m); var_subst sub(m, false); for (unsigned i = 0; i < sig.size(); ++i) { std::stringstream strm; strm << "x" << i; vars.push_back(m.mk_const(symbol(strm.str().c_str()), sig[i])); } fml1 = sub(fml1, vars.size(), vars.c_ptr()); fml2 = sub(fml2, vars.size(), vars.c_ptr()); check_equiv("filter", fml1, fml2); } void check_relation_plugin::check_contains(char const* objective, expr* fml1, expr* fml2) { expr_ref fml0(m); fml0 = m.mk_and(fml1, fml2); check_equiv(objective, fml0, fml2); } void check_relation_plugin::check_equiv(char const* objective, expr* fml1, expr* fml2) { TRACE("doc", tout << mk_pp(fml1, m) << "\n"; tout << mk_pp(fml2, m) << "\n";); smt_params fp; smt::kernel solver(m, fp); expr_ref tmp(m); tmp = m.mk_not(m.mk_eq(fml1, fml2)); solver.assert_expr(tmp); lbool res = solver.check(); if (res == l_false) { IF_VERBOSE(3, verbose_stream() << objective << " verified\n";); } else if (res == l_true) { IF_VERBOSE(0, verbose_stream() << "NOT verified " << res << "\n"; verbose_stream() << mk_pp(fml1, m) << "\n"; verbose_stream() << mk_pp(fml2, m) << "\n"; verbose_stream().flush(); ); throw default_exception("operation was not verified"); } } void check_relation_plugin::verify_union(expr* dst0, relation_base const& src, relation_base const& dst, expr* delta0, relation_base const* delta) { expr_ref fml1(m), fml2(m); src.to_formula(fml1); dst.to_formula(fml2); fml1 = m.mk_or(fml1, dst0); relation_signature const& sig = dst.get_signature(); expr_ref_vector vars(m); var_subst sub(m, false); for (unsigned i = 0; i < sig.size(); ++i) { std::stringstream strm; strm << "x" << i; vars.push_back(m.mk_const(symbol(strm.str().c_str()), sig[i])); } fml1 = sub(fml1, vars.size(), vars.c_ptr()); fml2 = sub(fml2, vars.size(), vars.c_ptr()); check_equiv("union", fml1, fml2); if (delta) { expr_ref d0(m), d(m); delta->to_formula(d); IF_VERBOSE(3, verbose_stream() << "verify delta " << d << "\n";); // delta >= dst \ dst0 // dst \ dst0 == delta & dst & \ dst0 expr_ref fml4(m), fml5(m); fml4 = m.mk_and(fml2, m.mk_not(dst0)); fml4 = sub(fml4, vars.size(), vars.c_ptr()); d = sub(d, vars.size(), vars.c_ptr()); check_contains("union_delta low", d, fml4); // // delta >= delta0 // d0 = sub(delta0, vars.size(), vars.c_ptr()); check_contains("union delta0", d, d0); // // dst u delta0 = delta u dst0 // fml4 = m.mk_or(fml2, delta0); fml5 = m.mk_or(d, dst0); fml4 = sub(fml4, vars.size(), vars.c_ptr()); fml5 = sub(fml5, vars.size(), vars.c_ptr()); check_equiv("union no overflow", fml4, fml5); } } class check_relation_plugin::union_fn : public relation_union_fn { scoped_ptr m_union; public: union_fn(relation_union_fn* m): m_union(m) {} void operator()(relation_base & _r, const relation_base & _src, relation_base * _delta) override { TRACE("doc", _r.display(tout << "dst:\n"); _src.display(tout << "src:\n");); check_relation& r = get(_r); check_relation const& src = get(_src); check_relation* d = get(_delta); expr_ref fml0 = r.m_fml; expr_ref delta0(r.m_fml.get_manager()); if (d) d->to_formula(delta0); (*m_union)(r.rb(), src.rb(), d?(&d->rb()):nullptr); r.get_plugin().verify_union(fml0, src.rb(), r.rb(), delta0, d?(&d->rb()):nullptr); r.rb().to_formula(r.m_fml); if (d) d->rb().to_formula(d->m_fml); } }; relation_union_fn * check_relation_plugin::mk_union_fn( const relation_base & tgt, const relation_base & src, const relation_base * delta) { relation_base const* d1 = delta?(&(get(*delta).rb())):nullptr; relation_union_fn* u = m_base->mk_union_fn(get(tgt).rb(), get(src).rb(), d1); return u?alloc(union_fn, u):nullptr; } relation_union_fn * check_relation_plugin::mk_widen_fn( const relation_base & tgt, const relation_base & src, const relation_base * delta) { relation_base const* d1 = delta?(&(get(*delta).rb())):nullptr; relation_union_fn* u = m_base->mk_widen_fn(get(tgt).rb(), get(src).rb(), d1); return u?alloc(union_fn, u):nullptr; } class check_relation_plugin::filter_identical_fn : public relation_mutator_fn { unsigned_vector m_cols; scoped_ptr m_filter; public: filter_identical_fn(relation_mutator_fn* f, unsigned col_cnt, const unsigned *identical_cols) : m_cols(col_cnt, identical_cols), m_filter(f) { } ~filter_identical_fn() override {} void operator()(relation_base & _r) override { check_relation& r = get(_r); check_relation_plugin& p = r.get_plugin(); ast_manager& m = p.m; expr_ref cond(m); relation_signature const& sig = r.get_signature(); expr_ref_vector conds(m); unsigned c1 = m_cols[0]; for (unsigned i = 1; i < m_cols.size(); ++i) { unsigned c2 = m_cols[i]; conds.push_back(m.mk_eq(m.mk_var(c1, sig[c1]), m.mk_var(c2, sig[c2]))); } cond = mk_and(m, conds.size(), conds.c_ptr()); r.consistent_formula(); (*m_filter)(r.rb()); p.verify_filter(r.m_fml, r.rb(), cond); r.rb().to_formula(r.m_fml); } }; relation_mutator_fn * check_relation_plugin::mk_filter_identical_fn( const relation_base & t, unsigned col_cnt, const unsigned * identical_cols) { relation_mutator_fn* r = m_base->mk_filter_identical_fn(get(t).rb(), col_cnt, identical_cols); return r?alloc(filter_identical_fn, r, col_cnt, identical_cols):nullptr; } class check_relation_plugin::filter_interpreted_fn : public relation_mutator_fn { scoped_ptr m_mutator; app_ref m_condition; public: filter_interpreted_fn(relation_mutator_fn* r, app_ref& condition) : m_mutator(r), m_condition(condition) { } ~filter_interpreted_fn() override {} void operator()(relation_base & tb) override { check_relation& r = get(tb); check_relation_plugin& p = r.get_plugin(); expr_ref fml = r.m_fml; (*m_mutator)(r.rb()); p.verify_filter(fml, r.rb(), m_condition); r.rb().to_formula(r.m_fml); } }; relation_mutator_fn * check_relation_plugin::mk_filter_interpreted_fn(const relation_base & t, app * condition) { relation_mutator_fn* r = m_base->mk_filter_interpreted_fn(get(t).rb(), condition); app_ref cond(condition, m); return r?alloc(filter_interpreted_fn, r, cond):nullptr; } class check_relation_plugin::project_fn : public convenient_relation_project_fn { scoped_ptr m_project; public: project_fn(relation_transformer_fn* p, relation_base const & t, unsigned removed_col_cnt, const unsigned * removed_cols) : convenient_relation_project_fn(t.get_signature(), removed_col_cnt, removed_cols), m_project(p) { } ~project_fn() override {} relation_base * operator()(const relation_base & tb) override { check_relation const& t = get(tb); check_relation_plugin& p = t.get_plugin(); relation_base* r = (*m_project)(t.rb()); p.verify_project(tb, *r, m_removed_cols); return alloc(check_relation, p, r->get_signature(), r); } }; relation_transformer_fn * check_relation_plugin::mk_project_fn( const relation_base & t, unsigned col_cnt, const unsigned * removed_cols) { relation_transformer_fn* p = m_base->mk_project_fn(get(t).rb(), col_cnt, removed_cols); return p?alloc(project_fn, p, t, col_cnt, removed_cols):nullptr; } class check_relation_plugin::rename_fn : public convenient_relation_rename_fn { scoped_ptr m_permute; public: rename_fn(relation_transformer_fn* permute, relation_base const& t, unsigned cycle_len, const unsigned * cycle) : convenient_relation_rename_fn(t.get_signature(), cycle_len, cycle), m_permute(permute) { } ~rename_fn() override {} relation_base * operator()(const relation_base & _t) override { check_relation const& t = get(_t); check_relation_plugin& p = t.get_plugin(); relation_signature const& sig = get_result_signature(); relation_base* r = (*m_permute)(t.rb()); p.verify_permutation(t.rb(), *r, m_cycle); return alloc(check_relation, p, sig, r); } }; relation_transformer_fn * check_relation_plugin::mk_rename_fn( const relation_base & r, unsigned cycle_len, const unsigned * permutation_cycle) { relation_transformer_fn* p = m_base->mk_rename_fn(get(r).rb(), cycle_len, permutation_cycle); return p?alloc(rename_fn, p, r, cycle_len, permutation_cycle):nullptr; } class check_relation_plugin::filter_equal_fn : public relation_mutator_fn { scoped_ptr m_filter; relation_element m_val; unsigned m_col; public: filter_equal_fn(relation_mutator_fn* filter, relation_base const& t, const relation_element val, unsigned col): m_filter(filter), m_val(val), m_col(col) {} ~filter_equal_fn() override { } void operator()(relation_base & tb) override { check_relation & t = get(tb); check_relation_plugin& p = t.get_plugin(); (*m_filter)(t.rb()); expr_ref fml = t.m_fml; t.rb().to_formula(t.m_fml); fml = p.m.mk_and(fml, p.m.mk_eq(p.m.mk_var(m_col, t.get_signature()[m_col]), m_val)); p.check_equiv("filter_equal", t.ground(fml), t.ground(t.m_fml)); } }; relation_mutator_fn * check_relation_plugin::mk_filter_equal_fn( const relation_base & t, const relation_element & value, unsigned col) { relation_mutator_fn* r = m_base->mk_filter_equal_fn(get(t).rb(), value, col); return r?alloc(filter_equal_fn, r, t, value, col):nullptr; } class check_relation_plugin::negation_filter_fn : public relation_intersection_filter_fn { scoped_ptr m_filter; const unsigned_vector m_t_cols; const unsigned_vector m_neg_cols; public: negation_filter_fn( relation_intersection_filter_fn* filter, unsigned joined_col_cnt, const unsigned *t_cols, const unsigned *neg_cols) : m_filter(filter), m_t_cols(joined_col_cnt, t_cols), m_neg_cols(joined_col_cnt, neg_cols) { SASSERT(joined_col_cnt > 0); } void operator()(relation_base& tb, const relation_base& negb) override { check_relation& t = get(tb); check_relation const& n = get(negb); check_relation_plugin& p = t.get_plugin(); ast_manager& m = p.get_ast_manager(); expr_ref dst0(m); t.to_formula(dst0); (*m_filter)(t.rb(), n.rb()); t.rb().to_formula(t.m_fml); p.verify_filter_by_negation(dst0, t.rb(), n.rb(), m_t_cols, m_neg_cols); } }; relation_intersection_filter_fn * check_relation_plugin::mk_filter_by_negation_fn( const relation_base& t, const relation_base& neg, unsigned joined_col_cnt, const unsigned *t_cols, const unsigned *negated_cols) { relation_intersection_filter_fn* f = m_base->mk_filter_by_negation_fn(get(t).rb(), get(neg).rb(), joined_col_cnt, t_cols, negated_cols); return f?alloc(negation_filter_fn, f, joined_col_cnt, t_cols, negated_cols):nullptr; } /* The filter_by_negation postcondition: filter_by_negation(tgt, neg, columns in tgt: c1,...,cN, corresponding columns in neg: d1,...,dN): tgt_1:={x: x\in tgt_0 && ! \exists y: ( y \in neg & pi_c1(x)= pi_d1(y) & ... & pi_cN(x)= pi_dN(y) ) } */ void check_relation_plugin::verify_filter_by_negation( expr* dst0, relation_base const& dst, relation_base const& neg, unsigned_vector const& cols1, unsigned_vector const& cols2) { relation_signature const& sig1 = dst.get_signature(); relation_signature const& sig2 = neg.get_signature(); expr_ref dstf(m), negf(m); //std::cout << mk_pp(dst0, m) << "\n"; expr_ref_vector eqs(m); dst.to_formula(dstf); //std::cout << mk_pp(dstf, m) << "\n"; neg.to_formula(negf); //std::cout << mk_pp(negf, m) << "\n"; eqs.push_back(negf); for (unsigned i = 0; i < cols1.size(); ++i) { var_ref v1(m), v2(m); unsigned c1 = cols1[i]; unsigned c2 = cols2[i]; SASSERT(sig1[c1] == sig2[c2]); v1 = m.mk_var(sig2.size() + c1, sig1[c1]); v2 = m.mk_var(c2, sig2[c2]); eqs.push_back(m.mk_eq(v1, v2)); } negf = mk_and(m, eqs.size(), eqs.c_ptr()); ptr_vector rev_sig2(sig2.size(), sig2.c_ptr()); rev_sig2.reverse(); svector names; for (unsigned i = 0; i < sig2.size(); ++i) { names.push_back(symbol(i)); } negf = m.mk_exists(rev_sig2.size(), rev_sig2.c_ptr(), names.c_ptr(), negf); negf = m.mk_and(dst0, m.mk_not(negf)); negf = ground(dst, negf); dstf = ground(dst, dstf); //std::cout << negf << "\n"; //std::cout << dstf << "\n"; check_equiv("filter by negation", dstf, negf); } class check_relation_plugin::filter_proj_fn : public convenient_relation_project_fn { app_ref m_cond; scoped_ptr m_xform; public: filter_proj_fn(relation_transformer_fn* xform, relation_base const& t, app_ref& cond, unsigned col_cnt, const unsigned * removed_cols) : convenient_relation_project_fn(t.get_signature(), col_cnt, removed_cols), m_cond(cond), m_xform(xform) {} ~filter_proj_fn() override {} relation_base* operator()(const relation_base & tb) override { check_relation const & t = get(tb); check_relation_plugin& p = t.get_plugin(); relation_base* r = (*m_xform)(t.rb()); p.verify_filter_project(t.rb(), *r, m_cond, m_removed_cols); relation_signature const& sig = get_result_signature(); return alloc(check_relation, p, sig, r); } }; relation_transformer_fn * check_relation_plugin::mk_filter_interpreted_and_project_fn( const relation_base & t, app * condition, unsigned removed_col_cnt, const unsigned * removed_cols) { relation_transformer_fn* r = m_base->mk_filter_interpreted_and_project_fn(get(t).rb(), condition, removed_col_cnt, removed_cols); app_ref cond(condition, m); return r?alloc(filter_proj_fn, r, t, cond, removed_col_cnt, removed_cols):nullptr; } } z3-z3-4.8.7/src/muz/rel/check_relation.h000066400000000000000000000156661356505360400177540ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: check_relation.h Abstract: Checked relation. Each operation on an underlying relation is checked for correctness. Author: Nikolaj Bjorner (nbjorner) 2014-09-23 Revision History: --*/ #ifndef CHECK_RELATION_H_ #define CHECK_RELATION_H_ #include "muz/rel/doc.h" #include "muz/rel/dl_base.h" namespace datalog { class check_relation_plugin; class check_relation; class check_relation : public relation_base { friend class check_relation_plugin; ast_manager& m; relation_base* m_relation; expr_ref m_fml; void check_equiv(char const* objective, expr* f1, expr* f2) const; expr_ref mk_eq(relation_fact const& f) const; public: check_relation(check_relation_plugin& p, relation_signature const& s, relation_base* r); ~check_relation() override; void reset() override; void add_fact(const relation_fact & f) override; void add_new_fact(const relation_fact & f) override; bool contains_fact(const relation_fact & f) const override; check_relation * clone() const override; check_relation * complement(func_decl*) const override; void to_formula(expr_ref& fml) const override; check_relation_plugin& get_plugin() const; bool fast_empty() const override; bool empty() const override; void display(std::ostream& out) const override; bool is_precise() const override { return m_relation->is_precise(); } unsigned get_size_estimate_rows() const override { return m_relation->get_size_estimate_rows(); } relation_base& rb() { return *m_relation; } relation_base const& rb() const { return *m_relation; } expr_ref ground(expr* fml) const; void consistent_formula(); }; class check_relation_plugin : public relation_plugin { friend class check_relation; class join_fn; class join_project_fn; class project_fn; class union_fn; class rename_fn; class filter_equal_fn; class filter_identical_fn; class filter_interpreted_fn; class filter_by_negation_fn; class filter_by_union_fn; class filter_proj_fn; class negation_filter_fn; ast_manager& m; relation_plugin* m_base; static check_relation& get(relation_base& r); static check_relation* get(relation_base* r); static check_relation const & get(relation_base const& r); expr_ref ground(relation_base const& rb, expr* fml) const; expr_ref ground(relation_base const& rb) const; expr_ref mk_project( relation_signature const& sig, expr* fml, unsigned_vector const& removed_cols); expr_ref mk_join( relation_base const& t1, relation_base const& t2, unsigned_vector const& cols1, unsigned_vector const& cols2); public: check_relation_plugin(relation_manager& rm); ~check_relation_plugin() override; void set_plugin(relation_plugin* p) { m_base = p; } bool can_handle_signature(const relation_signature & s) override; static symbol get_name() { return symbol("check_relation"); } relation_base * mk_empty(const relation_signature & s) override; relation_base * mk_full(func_decl* p, const relation_signature & s) override; relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) override; relation_join_fn * mk_join_project_fn( const relation_base & t1, const relation_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols) override; relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, const unsigned * removed_cols) override; relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, const unsigned * permutation_cycle) override; relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta) override; relation_union_fn * mk_widen_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta) override; relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, const unsigned * identical_cols) override; relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, unsigned col) override; relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition) override; relation_intersection_filter_fn * mk_filter_by_negation_fn( const relation_base& t, const relation_base& neg, unsigned joined_col_cnt, const unsigned *t_cols, const unsigned *negated_cols) override; relation_transformer_fn * mk_filter_interpreted_and_project_fn( const relation_base & t, app * condition, unsigned removed_col_cnt, const unsigned * removed_cols) override; void verify_join(relation_base const& t1, relation_base const& t2, relation_base const& t, unsigned_vector const& cols1, unsigned_vector const& cols2); void verify_filter(expr* fml0, relation_base const& t, expr* cond); void verify_union(expr* fml0, relation_base const& src, relation_base const& dst, expr* delta0, relation_base const* delta); void verify_permutation( relation_base const& src, relation_base const& dst, unsigned_vector const& cycle); void verify_project( relation_base const& src, expr* f1, relation_base const& dst, expr* f2, unsigned_vector const& removed_cols); void verify_project( relation_base const& src, relation_base const& dst, unsigned_vector const& removed_cols); void verify_filter_project( relation_base const& src, relation_base const& dst, app* cond, unsigned_vector const& removed_cols); void verify_join_project( relation_base const& t1, relation_base const& t2, relation_base const& t, unsigned_vector const& cols1, unsigned_vector const& cols2, unsigned_vector const& rm_cols); void check_equiv(char const* objective, expr* f1, expr* f2); void check_contains(char const* objective, expr* f1, expr* f2); void verify_filter_by_negation( expr* dst0, relation_base const& dst, relation_base const& neg, unsigned_vector const& dst_eq, unsigned_vector const& neg_eq); }; }; #endif /* CHECK_RELATION_H_ */ z3-z3-4.8.7/src/muz/rel/dl_base.cpp000066400000000000000000000355341356505360400167220ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_base.cpp Abstract: Author: Krystof Hoder (t-khoder) 2010-09-14. Revision History: --*/ #include "ast/ast_pp.h" #include "util/union_find.h" #include "util/vector.h" #include "muz/base/dl_context.h" #include "muz/rel/dl_base.h" #include "ast/rewriter/bool_rewriter.h" #include "muz/rel/dl_relation_manager.h" #include namespace datalog { void universal_delete(relation_base* ptr) { ptr->deallocate(); } void universal_delete(table_base* ptr) { ptr->deallocate(); } void dealloc_ptr_vector_content(ptr_vector & v) { ptr_vector::iterator it = v.begin(); ptr_vector::iterator end = v.end(); for(; it!=end; ++it) { (*it)->deallocate(); } } void get_renaming_args(const unsigned_vector & map, const relation_signature & orig_sig, expr_ref_vector & renaming_arg) { ast_manager & m = renaming_arg.get_manager(); unsigned sz = map.size(); unsigned ofs = sz-1; renaming_arg.resize(sz, static_cast(nullptr)); for(unsigned i=0; i reset_fn = get_manager().mk_filter_interpreted_fn(*this, bottom_ref); if(!reset_fn) { NOT_IMPLEMENTED_YET(); } (*reset_fn)(*this); } void table_signature::from_join(const table_signature & s1, const table_signature & s2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, table_signature & result) { result.reset(); unsigned s1sz=s1.size(); unsigned s2sz=s2.size(); unsigned s1first_func=s1sz-s1.functional_columns(); unsigned s2first_func=s2sz-s2.functional_columns(); for(unsigned i=0; i=col_cnt); result.set_functional_columns(func_cnt-col_cnt); } } void table_signature::from_project_with_reduce(const table_signature & src, unsigned col_cnt, const unsigned * removed_cols, table_signature & result) { signature_base::from_project(src, col_cnt, removed_cols, result); unsigned remaining_fun = src.functional_columns(); unsigned first_src_fun = src.first_functional(); for(int i=col_cnt-1; i>=0; i--) { if(removed_cols[i] remaining_in_equivalence_class; remaining_in_equivalence_class.resize(join_sig_sz, 0); bool merging_rows_can_happen = false; union_find_default_ctx uf_ctx; union_find<> uf(uf_ctx); //the numbers in uf correspond to column indexes after the join for(unsigned i=0; icols1[i]) ? cols1[i] : (first_func_ofs+cols1[i]-s1_first_func); unsigned idx2 = (s2_first_func>cols2[i]) ? (second_ofs+cols2[i]) : (second_func_ofs+cols2[i]-s2_first_func); uf.merge(idx1, idx2); } for(unsigned i=0; i=first_func_ofs) { //removing functional columns won't make us merge rows continue; } unsigned eq_class_idx = uf.find(rc); if(remaining_in_equivalence_class[eq_class_idx]>1) { remaining_in_equivalence_class[eq_class_idx]--; } else { merging_rows_can_happen = true; break; } } if(merging_rows_can_happen) { //this one marks all columns as non-functional from_project(aux, removed_col_cnt, removed_cols, result); SASSERT(result.functional_columns()==0); } else { //this one preserves columns to be functional from_project_with_reduce(aux, removed_col_cnt, removed_cols, result); } } // ----------------------------------- // // table_base // // ----------------------------------- //here we give generic implementation of table operations using iterators bool table_base::empty() const { return begin()==end(); } void table_base::remove_facts(unsigned fact_cnt, const table_fact * facts) { for(unsigned i=0; i to_remove; table_base::iterator it = begin(); table_base::iterator iend = end(); table_fact row; for(; it!=iend; ++it) { it->get_fact(row); to_remove.push_back(row); } remove_facts(to_remove.size(), to_remove.c_ptr()); } bool table_base::contains_fact(const table_fact & f) const { iterator it = begin(); iterator iend = end(); table_fact row; for(; it!=iend; ++it) { it->get_fact(row); if(vectors_equal(row, f)) { return true; } } return false; } bool table_base::fetch_fact(table_fact & f) const { if(get_signature().functional_columns()==0) { return contains_fact(f); } else { unsigned sig_sz = get_signature().size(); unsigned non_func_cnt = sig_sz-get_signature().functional_columns(); table_base::iterator it = begin(); table_base::iterator iend = end(); table_fact row; for(; it!=iend; ++it) { it->get_fact(row); bool differs = false; for(unsigned i=0; iget_fact(row); res->add_new_fact(row); } return res; } /** \brief Default method for complementation. It assumes that the compiler creates only tables with at most one column (0 or 1 columns). Complementation of tables with more than one columns is transformed into a cross product of complements and/or difference. */ table_base * table_base::complement(func_decl* p, const table_element * func_columns) const { const table_signature & sig = get_signature(); SASSERT(sig.functional_columns()==0 || func_columns!=0); SASSERT(sig.first_functional() <= 1); table_base * res = get_plugin().mk_empty(sig); table_fact fact; fact.resize(sig.first_functional()); fact.append(sig.functional_columns(), func_columns); if (sig.first_functional() == 0) { if (empty()) { res->add_fact(fact); } return res; } VERIFY(sig.first_functional() == 1); uint64_t upper_bound = get_signature()[0]; bool empty_table = empty(); if (upper_bound > (1 << 18)) { std::ostringstream buffer; buffer << "creating large table of size " << upper_bound; if (p) buffer << " for relation " << p->get_name(); warning_msg("%s", buffer.str().c_str()); } for(table_element i = 0; i < upper_bound; i++) { fact[0] = i; if(empty_table || !contains_fact(fact)) { res->add_fact(fact); } } return res; } void table_base::display(std::ostream & out) const { out << "table with signature "; print_container(get_signature(), out); out << ":\n"; iterator it = begin(); iterator iend = end(); for(; it!=iend; ++it) { const row_interface & r = *it; r.display(out); } out << "\n"; } class table_base::row_interface::fact_row_iterator : public table_base::row_iterator_core { const row_interface & m_parent; unsigned m_index; protected: bool is_finished() const override { return m_index==m_parent.size(); } public: fact_row_iterator(const row_interface & row, bool finished) : m_parent(row), m_index(finished ? row.size() : 0) {} table_element operator*() override { SASSERT(!is_finished()); return m_parent[m_index]; } void operator++() override { m_index++; SASSERT(m_index<=m_parent.size()); } }; table_base::row_iterator table_base::row_interface::begin() const { return row_iterator(alloc(fact_row_iterator, *this, false)); } table_base::row_iterator table_base::row_interface::end() const { return row_iterator(alloc(fact_row_iterator, *this, true)); } void table_base::row_interface::get_fact(table_fact & result) const { result.reset(); unsigned n=size(); for(unsigned i=0; i Author: Krystof Hoder (t-khoder) 2010-09-23. Revision History: --*/ #ifndef DL_BASE_H_ #define DL_BASE_H_ #define DL_LEAK_HUNTING 0 #include #include "ast/ast.h" #include "util/map.h" #include "util/vector.h" #include "util/ref.h" #include "muz/base/dl_util.h" #include "muz/base/dl_context.h" namespace datalog { class context; class relation_manager; template class scoped_rel { T* m_t; public: scoped_rel(T* t) : m_t(t) {} ~scoped_rel() { if (m_t) { universal_delete(m_t); } } scoped_rel() : m_t(nullptr) {} scoped_rel& operator=(T* t) { if (m_t && t != m_t) { universal_delete(m_t); } m_t = t; return *this; } T* operator->() { return m_t; } const T* operator->() const { return m_t; } T& operator*() { return *m_t; } const T& operator*() const { return *m_t; } operator bool() const { return m_t!=nullptr; } T* get() const { return m_t; } /** \brief Remove object from \c scoped_rel without deleting it. */ T* release() { T* res = m_t; m_t = nullptr; return res; } }; ast_manager & get_ast_manager_from_rel_manager(const relation_manager & rm); context & get_context_from_rel_manager(const relation_manager & rm); typedef func_decl_set decl_set; #if DL_LEAK_HUNTING void leak_guard_check(const symbol & s); #endif void universal_delete(relation_base* ptr); void universal_delete(table_base* ptr); void dealloc_ptr_vector_content(ptr_vector & v); /** Termplate class containing common infrastructure for relations and tables */ template struct tr_infrastructure { typedef typename Traits::plugin plugin; typedef typename Traits::base_object base_object; typedef typename Traits::element element; typedef typename Traits::fact fact; typedef typename Traits::sort sort; typedef typename Traits::signature_base_base signature_base_base; //this must be a vector-like type typedef typename Traits::signature signature; //this must be a vector-like type /** The client submits an initial class to be used as a base for signature. Then we extend it by the common signature methods into a signature_base class which then the client inherits from to obtain the actual signature class. */ class signature_base : public signature_base_base { public: bool operator==(const signature & o) const { unsigned n=signature_base_base::size(); if(n!=o.size()) { return false; } return memcmp(this->c_ptr(), o.c_ptr(), n*sizeof(sort))==0; /*for(unsigned i=0; i=2); result=src; permutate_by_cycle(result, cycle_len, permutation_cycle); } /** \brief Into \c result assign signature \c src with reordered columns. */ static void from_permutation_rename(const signature & src, const unsigned * permutation, signature & result) { result.reset(); unsigned n = src.size(); for(unsigned i=0; i(nullptr)); } }; /** \brief Mutator for relations that propagate constraints as a consequence of combination. - supports_attachment is used to query the mutator if it allows communicating constraints to relations of the kind of the relation. - attach is used to associate downstream clients. It assumes that the relation kind is supported (supports_kind returns true) */ class mutator_fn : public base_fn { public: ~mutator_fn() override {} virtual void operator()(base_object & t) = 0; virtual bool supports_attachment(base_object& other) { return false; } virtual void attach(base_object& other) { UNREACHABLE(); } }; class intersection_filter_fn : public base_fn { public: virtual void operator()(base_object & t, const base_object & intersected_obj) = 0; }; class intersection_join_filter_fn : public base_fn { public: virtual void operator()(base_object & t, const base_object & inter1, const base_object& inter2) = 0; }; class default_join_project_fn; /** \brief Plugin class providing factory functions for a table and operations on it. The functor factory functions (mk_*_fn) may return 0. It means that the plugin is unable to perform the operation on relations/tables of the particular kind. */ class plugin_object { friend class relation_manager; friend class check_table_plugin; friend class check_relation_plugin; family_id m_kind; symbol m_name; relation_manager & m_manager; protected: plugin_object(symbol const& name, relation_manager & manager) : m_kind(null_family_id), m_name(name), m_manager(manager) {} /** \brief Check \c r is of the same kind as the plugin. */ bool check_kind(base_object const& r) const { return &r.get_plugin()==this; } public: virtual ~plugin_object() {} virtual void initialize(family_id fid) { m_kind = fid; } family_id get_kind() const { return m_kind; } symbol const& get_name() const { return m_name; } relation_manager & get_manager() const { return m_manager; } ast_manager& get_ast_manager() const { return datalog::get_ast_manager_from_rel_manager(m_manager); } context& get_context() const { return datalog::get_context_from_rel_manager(m_manager); } virtual bool can_handle_signature(const signature & s) = 0; virtual bool can_handle_signature(const signature & s, family_id kind) { return can_handle_signature(s); } /** \brief Create empty table/relation with given signature and return pointer to it. Precondition: can_handle_signature(s)==true */ virtual base_object * mk_empty(const signature & s) = 0; /** \brief Create empty table/relation with signature \c s and kind \c kind. Precondition: &orig.get_plugin()==this */ virtual base_object * mk_empty(const signature & s, family_id kind) { SASSERT(kind==get_kind()); //if plugin uses multiple kinds, this function needs to be overridden return mk_empty(s); } /** \brief Create empty table/relation of the same specification as \c orig and return pointer to it. Precondition: &orig.get_plugin()==this */ virtual base_object * mk_empty(const base_object & orig) { return mk_empty(orig.get_signature(), orig.get_kind()); } /** \brief Create full table/relation with given signature and return pointer to it. Precondition: can_handle_signature(s)==true */ virtual base_object * mk_full(func_decl* p, const signature & s) { base_object * aux = mk_empty(s); base_object * res = aux->complement(p); aux->deallocate(); return res; } virtual base_object * mk_full(func_decl* p, const signature & s, family_id kind) { if (kind == get_kind() || kind == null_family_id) { return mk_full(p, s); } base_object * aux = mk_empty(s, kind); base_object * res = aux->complement(p); aux->deallocate(); return res; } protected: //see \c relation_manager for documentation of the operations virtual join_fn * mk_join_fn(const base_object & t1, const base_object & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { return nullptr; } virtual transformer_fn * mk_project_fn(const base_object & t, unsigned col_cnt, const unsigned * removed_cols) { return nullptr; } virtual join_fn * mk_join_project_fn(const base_object & t1, const base_object & t2, unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols) { return nullptr; } virtual transformer_fn * mk_rename_fn(const base_object & t, unsigned permutation_cycle_len, const unsigned * permutation_cycle) { return nullptr; } virtual transformer_fn * mk_permutation_rename_fn(const base_object & t, const unsigned * permutation) { return nullptr; } public: virtual union_fn * mk_union_fn(const base_object & tgt, const base_object & src, const base_object * delta) { return nullptr; } protected: virtual union_fn * mk_widen_fn(const base_object & tgt, const base_object & src, const base_object * delta) { return nullptr; } virtual mutator_fn * mk_filter_identical_fn(const base_object & t, unsigned col_cnt, const unsigned * identical_cols) { return nullptr; } virtual mutator_fn * mk_filter_equal_fn(const base_object & t, const element & value, unsigned col) { return nullptr; } virtual mutator_fn * mk_filter_interpreted_fn(const base_object & t, app * condition) { return nullptr; } virtual transformer_fn * mk_filter_interpreted_and_project_fn(const base_object & t, app * condition, unsigned removed_col_cnt, const unsigned * removed_cols) { return nullptr; } virtual transformer_fn * mk_select_equal_and_project_fn(const base_object & t, const element & value, unsigned col) { return nullptr; } virtual intersection_filter_fn * mk_filter_by_intersection_fn(const base_object & t, const base_object & src, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * src_cols) { return nullptr; } virtual intersection_filter_fn * mk_filter_by_negation_fn(const base_object & t, const base_object & negated_obj, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) { return nullptr; } virtual intersection_join_filter_fn * mk_filter_by_negated_join_fn( const base_object & t, const base_object & src1, const base_object & src2, unsigned_vector const& t_cols, unsigned_vector const& src_cols, unsigned_vector const& src1_cols, unsigned_vector const& src2_cols) { return nullptr; } }; class base_ancestor { plugin & m_plugin; signature m_signature; family_id m_kind; #if DL_LEAK_HUNTING sort_ref m_leak_guard; #endif protected: base_ancestor(plugin & p, const signature & s) : m_plugin(p), m_signature(s), m_kind(p.get_kind()) #if DL_LEAK_HUNTING , m_leak_guard(p.get_ast_manager().mk_fresh_sort(p.get_name().bare_str()), p.get_ast_manager()) #endif { #if DL_LEAK_HUNTING leak_guard_check(m_leak_guard->get_name()); #endif } #if DL_LEAK_HUNTING base_ancestor(const base_ancestor & o) : m_plugin(o.m_plugin), m_signature(o.m_signature), m_kind(o.m_kind), m_leak_guard(m_plugin.get_ast_manager().mk_fresh_sort(m_plugin.get_name().bare_str()), m_plugin.get_ast_manager()) { leak_guard_check(m_leak_guard->get_name()); } #endif virtual ~base_ancestor() {} void set_kind(family_id kind) { SASSERT(kind>=0); m_kind = kind; } /** Since the destructor is protected, we cannot use the \c dealloc macro. */ void destroy() { this->~base_ancestor(); memory::deallocate(this); } public: /** Deallocate the current object. Pointers and references to the current object become invalid after a call to this function. */ virtual void deallocate() { destroy(); } /** It must hold that operations created for particular table/relation are able to operate on tables/relations of the same signature and kind. In most cases it is sufficient to use the kind of the plugin object. However, it there is some parameter that is not reflected in the signature, the plugin may need to allocate different kind numbers to the tables is creates. */ family_id get_kind() const { return m_kind; } const signature & get_signature() const { return m_signature; } plugin & get_plugin() const { return m_plugin; } relation_manager & get_manager() const { return get_plugin().get_manager(); } virtual bool empty() const = 0; /** \brief fast emptiness check. This may be partial. The requirement is that if fast_empty returns true then the table or relation is in fact empty. It is allowed to return false even if the relation is empty. */ virtual bool fast_empty() const { return empty(); } virtual void add_fact(const fact & f) = 0; /** \brief Like \c add_fact, only here the caller guarantees that the fact is not present in the table yet. */ virtual void add_new_fact(const fact & f) { add_fact(f); } virtual bool contains_fact(const fact & f) const = 0; virtual void reset() = 0; /** \brief Return table/relation that contains the same data as the current one. */ virtual base_object * clone() const = 0; virtual bool can_swap(const base_object & o) const { return false; } virtual void swap(base_object & o) { std::swap(m_kind, o.m_kind); #if DL_LEAK_HUNTING m_leak_guard = get_plugin().get_ast_manager().mk_fresh_sort(get_plugin().get_name().bare_str()); o.m_leak_guard = get_plugin().get_ast_manager().mk_fresh_sort(get_plugin().get_name().bare_str()); leak_guard_check(m_leak_guard->get_name()); leak_guard_check(o.m_leak_guard->get_name()); #endif } virtual unsigned get_size_estimate_rows() const { return UINT_MAX; } virtual unsigned get_size_estimate_bytes() const { return UINT_MAX; } virtual bool knows_exact_size() const { return false; } unsigned num_columns() const { return get_signature().size(); } virtual void display(std::ostream & out) const = 0; }; class convenient_join_fn : public join_fn { signature m_result_sig; protected: unsigned_vector m_cols1; unsigned_vector m_cols2; convenient_join_fn(const signature & o1_sig, const signature & o2_sig, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) : m_cols1(col_cnt, cols1), m_cols2(col_cnt, cols2) { signature::from_join(o1_sig, o2_sig, col_cnt, cols1, cols2, m_result_sig); } const signature & get_result_signature() const { return m_result_sig; } }; class convenient_join_project_fn : public join_fn { signature m_result_sig; protected: unsigned_vector m_cols1; unsigned_vector m_cols2; //it is non-const because it needs to be modified in sparse_table version of the join_project operator unsigned_vector m_removed_cols; convenient_join_project_fn(const signature & o1_sig, const signature & o2_sig, unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols) : m_cols1(joined_col_cnt, cols1), m_cols2(joined_col_cnt, cols2), m_removed_cols(removed_col_cnt, removed_cols) { signature::from_join_project(o1_sig, o2_sig, joined_col_cnt, cols1, cols2, removed_col_cnt, removed_cols, m_result_sig); } const signature & get_result_signature() const { return m_result_sig; } }; class convenient_transformer_fn : public transformer_fn { signature m_result_sig; protected: signature & get_result_signature() { return m_result_sig; } const signature & get_result_signature() const { return m_result_sig; } }; class convenient_project_fn : public convenient_transformer_fn { protected: unsigned_vector m_removed_cols; convenient_project_fn(const signature & orig_sig, unsigned col_cnt, const unsigned * removed_cols) : m_removed_cols(col_cnt, removed_cols) { signature::from_project(orig_sig, col_cnt, removed_cols, convenient_transformer_fn::get_result_signature()); } }; class convenient_rename_fn : public convenient_transformer_fn { protected: const unsigned_vector m_cycle; convenient_rename_fn(const signature & orig_sig, unsigned cycle_len, const unsigned * permutation_cycle) : m_cycle(cycle_len, permutation_cycle) { signature::from_rename(orig_sig, cycle_len, permutation_cycle, convenient_transformer_fn::get_result_signature()); } }; class convenient_negation_filter_fn : public intersection_filter_fn { protected: unsigned m_joined_col_cnt; const unsigned_vector m_cols1; const unsigned_vector m_cols2; bool m_all_neg_bound; //all columns are bound at least once bool m_overlap; //one column in negated table is bound multiple times svector m_bound; convenient_negation_filter_fn(const base_object & tgt, const base_object & neg_t, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) : m_joined_col_cnt(joined_col_cnt), m_cols1(joined_col_cnt, t_cols), m_cols2(joined_col_cnt, negated_cols) { unsigned neg_sig_size = neg_t.get_signature().size(); m_overlap = false; m_bound.resize(neg_sig_size, false); for(unsigned i=0; i void make_neg_bindings(T & tgt_neg, const U & src) const { SASSERT(m_all_neg_bound); SASSERT(!m_overlap); for(unsigned i=0; i bool bindings_match(const T & tgt_neg, const U & src) const { for(unsigned i=0; i renamer_vector; unsigned_vector m_permutation; //this is valid only before m_renamers_initialized becomes true bool m_renamers_initialized; renamer_vector m_renamers; public: default_permutation_rename_fn(const base_object & o, const unsigned * permutation) : m_permutation(o.get_signature().size(), permutation), m_renamers_initialized(false) {} ~default_permutation_rename_fn() override { dealloc_ptr_vector_content(m_renamers); } base_object * operator()(const base_object & o) override { const base_object * res = &o; scoped_rel res_scoped; if(m_renamers_initialized) { typename renamer_vector::iterator rit = m_renamers.begin(); typename renamer_vector::iterator rend = m_renamers.end(); for(; rit!=rend; ++rit) { res_scoped = (**rit)(*res); res = res_scoped.get(); } } else { SASSERT(m_renamers.empty()); unsigned_vector cycle; while(try_remove_cycle_from_permutation(m_permutation, cycle)) { transformer_fn * renamer = o.get_manager().mk_rename_fn(*res, cycle); SASSERT(renamer); m_renamers.push_back(renamer); cycle.reset(); res_scoped = (*renamer)(*res); res = res_scoped.get(); } m_renamers_initialized = true; } if(res_scoped) { SASSERT(res==res_scoped.get()); //we don't want to delete the last one since we'll be returning it return res_scoped.release(); } else { SASSERT(res==&o); return res->clone(); } } }; }; // ----------------------------------- // // relation_base // // ----------------------------------- class relation_signature; class relation_plugin; class relation_base; typedef ptr_vector relation_signature_base0; typedef ptr_hash relation_sort_hash; struct relation_traits { typedef relation_plugin plugin; typedef relation_base base_object; typedef relation_element element; typedef relation_fact fact; typedef relation_sort sort; typedef relation_signature_base0 signature_base_base; typedef relation_signature signature; }; typedef tr_infrastructure relation_infrastructure; typedef relation_infrastructure::base_fn base_relation_fn; typedef relation_infrastructure::join_fn relation_join_fn; typedef relation_infrastructure::transformer_fn relation_transformer_fn; typedef relation_infrastructure::union_fn relation_union_fn; typedef relation_infrastructure::mutator_fn relation_mutator_fn; typedef relation_infrastructure::intersection_filter_fn relation_intersection_filter_fn; typedef relation_infrastructure::intersection_join_filter_fn relation_intersection_join_filter_fn; typedef relation_infrastructure::convenient_join_fn convenient_relation_join_fn; typedef relation_infrastructure::convenient_join_project_fn convenient_relation_join_project_fn; typedef relation_infrastructure::convenient_transformer_fn convenient_relation_transformer_fn; typedef relation_infrastructure::convenient_project_fn convenient_relation_project_fn; typedef relation_infrastructure::convenient_rename_fn convenient_relation_rename_fn; typedef relation_infrastructure::convenient_negation_filter_fn convenient_relation_negation_filter_fn; typedef relation_infrastructure::identity_transformer_fn identity_relation_transformer_fn; typedef relation_infrastructure::identity_mutator_fn identity_relation_mutator_fn; typedef relation_infrastructure::identity_intersection_filter_fn identity_relation_intersection_filter_fn; typedef relation_infrastructure::default_permutation_rename_fn default_relation_permutation_rename_fn; class relation_signature : public relation_infrastructure::signature_base { public: bool operator!=(const relation_signature & o) const { return !(*this==o); } void output(ast_manager & m, std::ostream & out) const; struct hash { unsigned operator()(relation_signature const& s) const { return obj_vector_hash(s); } }; struct eq { bool operator()(relation_signature const& s1, relation_signature const& s2) const { return s1 == s2; } }; }; class relation_plugin : public relation_infrastructure::plugin_object { protected: enum special_relation_type { ST_ORDINARY, ST_TABLE_RELATION, ST_FINITE_PRODUCT_RELATION, ST_PRODUCT_RELATION, ST_SIEVE_RELATION }; private: special_relation_type m_special_type; protected: relation_plugin(symbol const& name, relation_manager & manager, special_relation_type special_type = ST_ORDINARY) : plugin_object(name, manager), m_special_type(special_type) {} public: bool from_table() const { return m_special_type==ST_TABLE_RELATION; } bool is_finite_product_relation() const { return m_special_type==ST_FINITE_PRODUCT_RELATION; } bool is_product_relation() const { return m_special_type==ST_PRODUCT_RELATION; } bool is_sieve_relation() const { return m_special_type==ST_SIEVE_RELATION; } /** \brief If true, the relation can contain only one or zero elements. Having this zero allows the finite_product_relation to perform some operations in a simpler way. (KH: I started implementing finite_product_relation::inner_singleton_union_fn that takes advantage of it, but it's not finished.) */ virtual bool is_singleton_relation() const { return false; } }; class relation_base : public relation_infrastructure::base_ancestor { protected: relation_base(relation_plugin & plugin, const relation_signature & s) : base_ancestor(plugin, s) {} ~relation_base() override {} public: virtual relation_base * complement(func_decl* p) const = 0; void reset() override; virtual void display_tuples(func_decl & pred, std::ostream & out) const { out << "Tuples in " << pred.get_name() << ": \n"; display(out); } virtual void to_formula(expr_ref& fml) const = 0; bool from_table() const { return get_plugin().from_table(); } virtual bool is_precise() const { return true; } }; typedef ptr_vector relation_vector; // ----------------------------------- // // table_base // // ----------------------------------- class table_signature; class table_plugin; class table_base; typedef uint64_t table_sort; typedef svector table_signature_base0; typedef uint64_hash table_sort_hash; typedef uint64_hash table_element_hash; struct table_traits { typedef table_plugin plugin; typedef table_base base_object; typedef table_element element; typedef table_fact fact; typedef table_sort sort; typedef table_signature_base0 signature_base_base; typedef table_signature signature; }; typedef tr_infrastructure table_infrastructure; typedef table_infrastructure::base_fn base_table_fn; typedef table_infrastructure::join_fn table_join_fn; typedef table_infrastructure::transformer_fn table_transformer_fn; typedef table_infrastructure::union_fn table_union_fn; typedef table_infrastructure::mutator_fn table_mutator_fn; typedef table_infrastructure::intersection_filter_fn table_intersection_filter_fn; typedef table_infrastructure::intersection_join_filter_fn table_intersection_join_filter_fn; typedef table_infrastructure::convenient_join_fn convenient_table_join_fn; typedef table_infrastructure::convenient_join_project_fn convenient_table_join_project_fn; typedef table_infrastructure::convenient_transformer_fn convenient_table_transformer_fn; typedef table_infrastructure::convenient_project_fn convenient_table_project_fn; typedef table_infrastructure::convenient_rename_fn convenient_table_rename_fn; typedef table_infrastructure::convenient_negation_filter_fn convenient_table_negation_filter_fn; typedef table_infrastructure::identity_transformer_fn identity_table_transformer_fn; typedef table_infrastructure::identity_mutator_fn identity_table_mutator_fn; typedef table_infrastructure::identity_intersection_filter_fn identity_table_intersection_filter_fn; typedef table_infrastructure::default_permutation_rename_fn default_table_permutation_rename_fn; class table_row_mutator_fn { public: virtual ~table_row_mutator_fn() {} /** \brief The function is called for a particular table row. The \c func_columns contains a pointer to an array of functional column values that can be modified. If the function returns true, the modification will appear in the table; otherwise the row will be deleted. It is possible that one call to the function stands for multiple table rows that share the same functional column values. */ virtual bool operator()(table_element * func_columns) = 0; }; class table_row_pair_reduce_fn { public: virtual ~table_row_pair_reduce_fn() {} /** \brief The function is called for pair of table rows that became duplicit due to projection. The values that are in the first array after return from the function will be used for the resulting row. It is assumed that the function is idempotent: when the two functional sub-tuples are equal, the result is assumed to be equal to them as well, so the function may not be evaluated for them. */ virtual void operator()(table_element * func_columns, const table_element * merged_func_columns) = 0; }; class table_signature : public table_infrastructure::signature_base { public: struct hash { unsigned operator()(table_signature const& s) const { return svector_hash()(s); } }; struct eq { bool operator()(table_signature const& s1, table_signature const& s2) const { return s1 == s2; } }; private: unsigned m_functional_columns; public: table_signature() : m_functional_columns(0) {} void swap(table_signature & s) { signature_base::swap(s); std::swap(m_functional_columns, s.m_functional_columns); } /** \brief The returned value is the number of last columns that are functional. The uniqueness is enforced on non-functional columns. When projection causes two facts to have equal non-functional parts, it is not defined which one of them is retained. */ unsigned functional_columns() const { return m_functional_columns; } void set_functional_columns(unsigned val) { SASSERT(size()>=val); m_functional_columns = val; } /** \brief Return index of the first functional column, or the size of the signature if there are no functional columns. */ unsigned first_functional() const { return size()-m_functional_columns; } bool operator==(const table_signature & o) const { return signature_base::operator==(o) && m_functional_columns==o.m_functional_columns; } bool operator!=(const table_signature & o) const { return !(*this==o); } /** \brief return true iof the two signatures are equal when we ignore which columns are functional. */ bool equal_up_to_fn_mark(const table_signature & o) const { return signature_base::operator==(o); } /** \brief Into \c result assign signature of result of join of relations with signatures \c s1 and \c s2. The result is (non-functional of s1)(non-functional of s2)(functional of s1)(functional of s2) */ static void from_join(const table_signature & s1, const table_signature & s2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, table_signature & result); static void from_join_project(const table_signature & s1, const table_signature & s2, unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols, table_signature & result); /** \brief Into \c result assign signature projected from \c src. The array of removed columns must be sorted in ascending order. If we remove at least one non-functional column, all the columns in the result are non-functional. */ static void from_project(const table_signature & src, unsigned col_cnt, const unsigned * removed_cols, table_signature & result); static void from_project_with_reduce(const table_signature & src, unsigned col_cnt, const unsigned * removed_cols, table_signature & result); /** \brief Into \c result assign signature \c src with reordered columns. Permutations between functional and nonfunctional columns are not allowed. */ static void from_rename(const table_signature & src, unsigned cycle_len, const unsigned * permutation_cycle, table_signature & result) { signature_base::from_rename(src, cycle_len, permutation_cycle, result); result.set_functional_columns(src.functional_columns()); #if Z3DEBUG unsigned first_src_fun = src.size()-src.functional_columns(); bool in_func = permutation_cycle[0]>=first_src_fun; for(unsigned i=1;i=first_src_fun)); } #endif } /** \brief Into \c result assign signature \c src with reordered columns. Permutations mixing functional and nonfunctional columns are not allowed. */ static void from_permutation_rename(const table_signature & src, const unsigned * permutation, table_signature & result) { signature_base::from_permutation_rename(src, permutation, result); result.set_functional_columns(src.functional_columns()); #if Z3DEBUG unsigned sz = src.size(); unsigned first_src_fun = sz-src.functional_columns(); for(unsigned i=first_src_fun;i=first_src_fun); } #endif } }; class table_plugin : public table_infrastructure::plugin_object { friend class relation_manager; protected: table_plugin(symbol const& n, relation_manager & manager) : plugin_object(n, manager) {} public: bool can_handle_signature(const table_signature & s) override { return s.functional_columns()==0; } protected: /** If the returned value is non-zero, the returned object must take ownership of \c mapper. Otherwise \c mapper must remain unmodified. */ virtual table_mutator_fn * mk_map_fn(const table_base & t, table_row_mutator_fn * mapper) { return nullptr; } /** If the returned value is non-zero, the returned object must take ownership of \c reducer. Otherwise \c reducer must remain unmodified. */ virtual table_transformer_fn * mk_project_with_reduce_fn(const table_base & t, unsigned col_cnt, const unsigned * removed_cols, table_row_pair_reduce_fn * reducer) { return nullptr; } }; class table_base : public table_infrastructure::base_ancestor { protected: table_base(table_plugin & plugin, const table_signature & s) : base_ancestor(plugin, s) {} ~table_base() override {} public: table_base * clone() const override; virtual table_base * complement(func_decl* p, const table_element * func_columns = nullptr) const; bool empty() const override; /** \brief Return true if table contains fact that corresponds to \c f in all non-functional columns. */ bool contains_fact(const table_fact & f) const override; /** \brief If \c f (i.e. its non-functional part) is not present in the table, add it and return true. Otherwise update \c f, so that the values of functional columns correspond to the ones present in the table. */ virtual bool suggest_fact(table_fact & f); /** \brief If \c f (i.e. its non-functional part) is not present in the table, return false. Otherwise update \c f, so that the values of functional columns correspond to the ones present in the table and return true. */ virtual bool fetch_fact(table_fact & f) const; /** \brief Ensure fact \c f is present in the table (including the values of its functional columns). */ virtual void ensure_fact(const table_fact & f); virtual void remove_fact(const table_fact & fact) { SASSERT(fact.size() == get_signature().size()); remove_fact(fact.c_ptr()); } virtual void remove_fact(table_element const* fact) = 0; virtual void remove_facts(unsigned fact_cnt, const table_fact * facts); virtual void remove_facts(unsigned fact_cnt, const table_element * facts); void reset() override; class row_interface; void display(std::ostream & out) const override; /** \brief Convert table to a formula that encodes the table. The columns correspond to bound variables indexed as 0, .., sig.size()-1 */ virtual void to_formula(relation_signature const& sig, expr_ref& fml) const; protected: class iterator_core { unsigned m_ref_cnt; public: iterator_core() : m_ref_cnt(0) {} virtual ~iterator_core() {} void inc_ref() { m_ref_cnt++; } void dec_ref() { SASSERT(m_ref_cnt>0); m_ref_cnt--; if(m_ref_cnt==0) { dealloc(this); } } virtual bool is_finished() const = 0; virtual row_interface & operator*() = 0; virtual void operator++() = 0; virtual bool operator==(const iterator_core & it) { //we worry about the equality operator only because of checking //the equality with the end() iterator return is_finished() && it.is_finished(); } private: //private and undefined copy constructor and assignment operator iterator_core(const iterator_core &); iterator_core & operator=(const iterator_core &); }; struct row_iterator_core { unsigned m_ref_cnt; public: row_iterator_core() : m_ref_cnt(0) {} virtual ~row_iterator_core() {} void inc_ref() { m_ref_cnt++; } void dec_ref() { SASSERT(m_ref_cnt>0); m_ref_cnt--; if(m_ref_cnt==0) { dealloc(this); } } virtual bool is_finished() const = 0; virtual table_element operator*() = 0; virtual void operator++() = 0; virtual bool operator==(const row_iterator_core & it) { //we worry about the equality operator only because of checking //the equality with the end() iterator return is_finished() && it.is_finished(); } private: //private and undefined copy constructor and assignment operator row_iterator_core(const row_iterator_core &); row_iterator_core & operator=(const row_iterator_core &); }; public: class iterator { friend class table_base; ref m_core; iterator(iterator_core * core) : m_core(core) {} public: /** \brief Return reference to a row_interface object for the current row. The reference is valid only until the \c operator++() is called or until the iterator is invalidated. */ row_interface & operator*() { return *(*m_core); } row_interface * operator->() { return &(*(*m_core)); } iterator & operator++() { ++(*m_core); return *this; } bool operator==(const iterator & it) { return (*m_core)==(*it.m_core); } bool operator!=(const iterator & it) { return !operator==(it); } }; class row_iterator { friend class table_base; friend class row_interface; ref m_core; row_iterator(row_iterator_core * core) : m_core(core) {} public: table_element operator*() { return *(*m_core); } row_iterator & operator++() { ++(*m_core); return *this; } bool operator==(const row_iterator & it) { return (*m_core)==(*it.m_core); } bool operator!=(const row_iterator & it) { return !operator==(it); } }; virtual iterator begin() const = 0; virtual iterator end() const = 0; class row_interface { class fact_row_iterator; const table_base & m_parent_table; public: typedef row_iterator iterator; typedef row_iterator const_iterator; row_interface(const table_base & parent_table) : m_parent_table(parent_table) {} virtual ~row_interface() {} virtual table_element operator[](unsigned col) const = 0; unsigned size() const { return m_parent_table.get_signature().size(); } virtual void get_fact(table_fact & result) const; virtual row_iterator begin() const; virtual row_iterator end() const; virtual void display(std::ostream & out) const; }; protected: class caching_row_interface : public row_interface { mutable table_fact m_current; bool populated() const { return !m_current.empty(); } void ensure_populated() const { if(!populated()) { get_fact(m_current); } } public: caching_row_interface(const table_base & parent) : row_interface(parent) {} void get_fact(table_fact & result) const override = 0; table_element operator[](unsigned col) const override { ensure_populated(); return m_current[col]; } /** \brief Resets the cache of the row object. Must be called when the row object begins to represent a different row in the table. */ void reset() { m_current.reset(); } }; //This function is here to create iterator instances in classes that derive from table_base. //We do not want to make the constructor of the iterator class public, and being private, the //inheritor classes cannot see it directly. static iterator mk_iterator(iterator_core * core) { return iterator(core); } }; /** \brief Populate vector \c renaming_args so that it can be used as an argument to \c var_subst. The renaming we want is one that transforms variables with numbers of indexes of \c map into the values of at those indexes. If a value if \c UINT_MAX, it means we do not transform the index corresponding to it. */ void get_renaming_args(const unsigned_vector & map, const relation_signature & orig_sig, expr_ref_vector & renaming_arg); }; #endif /* DL_BASE_H_ */ z3-z3-4.8.7/src/muz/rel/dl_bound_relation.cpp000066400000000000000000000575221356505360400210150ustar00rootroot00000000000000/*++ Copyright (c) 2010 Microsoft Corporation Module Name: dl_bound_relation.cpp Abstract: Basic (strict upper) bound relation. Author: Nikolaj Bjorner (nbjorner) 2010-2-11 Revision History: --*/ #include "muz/rel/dl_bound_relation.h" #include "util/debug.h" #include "ast/ast_pp.h" namespace datalog { bound_relation_plugin::bound_relation_plugin(relation_manager& m): relation_plugin(bound_relation_plugin::get_name(), m), m_arith(get_ast_manager()), m_bsimp(get_ast_manager()) { } bool bound_relation_plugin::can_handle_signature(const relation_signature & sig) { for (unsigned i = 0; i < sig.size(); ++i) { if (!m_arith.is_int(sig[i]) && !m_arith.is_real(sig[i])) { return false; } } return true; } bound_relation& bound_relation_plugin::get(relation_base& r) { return dynamic_cast(r); } bound_relation const & bound_relation_plugin::get(relation_base const& r) { return dynamic_cast(r); } bound_relation* bound_relation_plugin::get(relation_base* r) { return dynamic_cast(r); } bool bound_relation_plugin::is_interval_relation(relation_base const& r) { return symbol("interval_relation") == r.get_plugin().get_name(); } interval_relation& bound_relation_plugin::get_interval_relation(relation_base& r) { SASSERT(is_interval_relation(r)); return dynamic_cast(r); } interval_relation const& bound_relation_plugin::get_interval_relation(relation_base const& r) { SASSERT(is_interval_relation(r)); return dynamic_cast(r); } relation_base * bound_relation_plugin::mk_empty(const relation_signature & s) { return alloc(bound_relation, *this, s, true); } relation_base * bound_relation_plugin::mk_full(func_decl* p, const relation_signature & s) { return alloc(bound_relation, *this, s, false); } class bound_relation_plugin::join_fn : public convenient_relation_join_fn { public: join_fn(const relation_signature & o1_sig, const relation_signature & o2_sig, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) : convenient_relation_join_fn(o1_sig, o2_sig, col_cnt, cols1, cols2) { } relation_base * operator()(const relation_base & _r1, const relation_base & _r2) override { bound_relation const& r1 = get(_r1); bound_relation const& r2 = get(_r2); bound_relation_plugin& p = r1.get_plugin(); bound_relation* result = dynamic_cast(p.mk_full(nullptr, get_result_signature())); result->mk_join(r1, r2, m_cols1.size(), m_cols1.c_ptr(), m_cols2.c_ptr()); return result; } }; relation_join_fn * bound_relation_plugin::mk_join_fn(const relation_base & r1, const relation_base & r2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { if (!check_kind(r1) || !check_kind(r2)) { return nullptr; } return alloc(join_fn, r1.get_signature(), r2.get_signature(), col_cnt, cols1, cols2); } class bound_relation_plugin::project_fn : public convenient_relation_project_fn { public: project_fn(const relation_signature & orig_sig, unsigned removed_col_cnt, const unsigned * removed_cols) : convenient_relation_project_fn(orig_sig, removed_col_cnt, removed_cols) { } relation_base * operator()(const relation_base & _r) override { bound_relation const& r = get(_r); bound_relation_plugin& p = r.get_plugin(); bound_relation* result = get(p.mk_full(nullptr, get_result_signature())); result->mk_project(r, m_removed_cols.size(), m_removed_cols.c_ptr()); return result; } }; relation_transformer_fn * bound_relation_plugin::mk_project_fn(const relation_base & r, unsigned col_cnt, const unsigned * removed_cols) { return alloc(project_fn, r.get_signature(), col_cnt, removed_cols); } class bound_relation_plugin::rename_fn : public convenient_relation_rename_fn { public: rename_fn(const relation_signature & orig_sig, unsigned cycle_len, const unsigned * cycle) : convenient_relation_rename_fn(orig_sig, cycle_len, cycle) { } relation_base * operator()(const relation_base & _r) override { bound_relation const& r = get(_r); bound_relation_plugin& p = r.get_plugin(); bound_relation* result = get(p.mk_full(nullptr, get_result_signature())); result->mk_rename(r, m_cycle.size(), m_cycle.c_ptr()); return result; } }; relation_transformer_fn * bound_relation_plugin::mk_rename_fn(const relation_base & r, unsigned cycle_len, const unsigned * permutation_cycle) { if(check_kind(r)) { return alloc(rename_fn, r.get_signature(), cycle_len, permutation_cycle); } return nullptr; } class bound_relation_plugin::union_fn : public relation_union_fn { bool m_is_widen; public: union_fn(bool is_widen) : m_is_widen(is_widen) { } void operator()(relation_base & _r, const relation_base & _src, relation_base * _delta) override { TRACE("bound_relation", _r.display(tout << "dst:\n"); _src.display(tout << "src:\n");); get(_r).mk_union(get(_src), get(_delta), m_is_widen); } }; class bound_relation_plugin::union_fn_i : public relation_union_fn { bool m_is_widen; public: union_fn_i(bool is_widen) : m_is_widen(is_widen) { } void operator()(relation_base & _r, const relation_base & _src, relation_base * _delta) override { TRACE("bound_relation", _r.display(tout << "dst:\n"); _src.display(tout << "src:\n");); get(_r).mk_union_i(get_interval_relation(_src), get(_delta), m_is_widen); TRACE("bound_relation", _r.display(tout << "dst':\n");); } }; relation_union_fn * bound_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta) { if (check_kind(tgt) && is_interval_relation(src) && (!delta || check_kind(*delta))) { return alloc(union_fn_i, false); } if (check_kind(tgt) && check_kind(src) && (!delta || check_kind(*delta))) { return alloc(union_fn, false); } return nullptr; } relation_union_fn * bound_relation_plugin::mk_widen_fn( const relation_base & tgt, const relation_base & src, const relation_base * delta) { if (check_kind(tgt) && is_interval_relation(src) && (!delta || check_kind(*delta))) { return alloc(union_fn_i, true); } if (check_kind(tgt) && check_kind(src) && (!delta || check_kind(*delta))) { return alloc(union_fn, true); } return nullptr; } class bound_relation_plugin::filter_identical_fn : public relation_mutator_fn { unsigned_vector m_cols; public: filter_identical_fn(unsigned col_cnt, const unsigned * identical_cols) : m_cols(col_cnt, identical_cols) {} void operator()(relation_base & r) override { for (unsigned i = 1; i < m_cols.size(); ++i) { get(r).equate(m_cols[0], m_cols[i]); } } }; relation_mutator_fn * bound_relation_plugin::mk_filter_identical_fn( const relation_base & t, unsigned col_cnt, const unsigned * identical_cols) { if(check_kind(t)) { return alloc(filter_identical_fn, col_cnt, identical_cols); } return nullptr; } class bound_relation_plugin::filter_equal_fn : public relation_mutator_fn { public: filter_equal_fn(relation_element const& value, unsigned col) {} void operator()(relation_base & r) override { } }; relation_mutator_fn * bound_relation_plugin::mk_filter_equal_fn(const relation_base & r, const relation_element & value, unsigned col) { if (check_kind(r)) { return alloc(filter_equal_fn, value, col); } return nullptr; } class bound_relation_plugin::filter_interpreted_fn : public relation_mutator_fn { enum kind_t { NOT_APPLICABLE, EQ_VAR, EQ_SUB, LT_VAR, LE_VAR, K_FALSE }; app_ref m_cond; app_ref m_lt; arith_util m_arith; interval_relation* m_interval; unsigned_vector m_vars; kind_t m_kind; unsigned get_var(expr* a) { SASSERT(is_var(a)); return to_var(a)->get_idx(); } // x = z - y void mk_sub_eq(expr* x, expr* z, expr* y) { SASSERT(is_var(x)); SASSERT(is_var(z)); SASSERT(is_var(y)); m_vars.push_back(get_var(x)); m_vars.push_back(get_var(z)); m_vars.push_back(get_var(y)); m_kind = EQ_SUB; } void mk_lt(expr* l, expr* r) { SASSERT(is_var(l)); SASSERT(is_var(r)); m_vars.push_back(get_var(l)); m_vars.push_back(get_var(r)); m_lt = m_arith.mk_lt(l, r); m_kind = LT_VAR; } void mk_le(expr* l, expr* r) { SASSERT(is_var(l)); SASSERT(is_var(r)); m_vars.push_back(get_var(l)); m_vars.push_back(get_var(r)); m_kind = LE_VAR; } void mk_eq(expr* l, expr* r) { m_vars.push_back(get_var(l)); m_vars.push_back(get_var(r)); m_kind = EQ_VAR; } public: filter_interpreted_fn(ast_manager& m, app* cond) : m_cond(cond, m), m_lt(m), m_arith(m), m_interval(nullptr), m_kind(NOT_APPLICABLE) { expr* l, *r, *r1, *r2, *c2; rational n1; if ((m_arith.is_lt(cond, l, r) || m_arith.is_gt(cond, r, l)) && is_var(l) && is_var(r)) { mk_lt(l, r); } else if (m.is_not(cond, c2) && (m_arith.is_ge(c2, l, r) || m_arith.is_le(c2, r, l)) && is_var(l) && is_var(r)) { mk_lt(l, r); } else if ((m_arith.is_le(cond, l, r) || m_arith.is_ge(cond, r, l)) && is_var(l) && is_var(r)) { mk_le(l, r); } else if (m.is_not(cond, c2) && (m_arith.is_gt(c2, l, r) || m_arith.is_lt(c2, r, l)) && is_var(l) && is_var(r)) { mk_le(l, r); } else if (m.is_false(cond)) { m_kind = K_FALSE; } else if (m.is_eq(cond, l, r) && is_var(l) && is_var(r)) { mk_eq(l, r); } else if (m.is_eq(cond, l, r) && m_arith.is_sub(r, r1, r2) && is_var(l) && is_var(r1) && is_var(r2)) { mk_sub_eq(l, r1, r2); } else if (m.is_eq(cond, l, r) && m_arith.is_sub(l, r1, r2) && is_var(r) && is_var(r1) && is_var(r2)) { mk_sub_eq(r, r1, r2); } else if (m.is_eq(cond, l, r) && m_arith.is_add(r, r1, r2) && m_arith.is_numeral(r1, n1) && n1.is_pos() && is_var(l) && is_var(r2)) { mk_lt(r2, l); } else if (m.is_eq(cond, l, r) && m_arith.is_add(r, r1, r2) && m_arith.is_numeral(r2, n1) && n1.is_pos() && is_var(l) && is_var(r1)) { mk_lt(r1, l); } else { } } // // x = z - y // x = y // x < y // x <= y // x < y + z // void operator()(relation_base& t) override { TRACE("dl", tout << mk_pp(m_cond, m_cond.get_manager()) << "\n"; t.display(tout);); bound_relation& r = get(t); switch(m_kind) { case K_FALSE: r.set_empty(); break; case NOT_APPLICABLE: break; case EQ_VAR: r.equate(m_vars[0], m_vars[1]); break; case EQ_SUB: // TBD break; case LT_VAR: r.mk_lt(m_vars[0], m_vars[1]); break; case LE_VAR: r.mk_le(m_vars[0], m_vars[1]); break; default: UNREACHABLE(); break; } TRACE("dl", t.display(tout << "result\n");); } bool supports_attachment(relation_base& t) override { return is_interval_relation(t); } void attach(relation_base& t) override { SASSERT(is_interval_relation(t)); interval_relation& r = get_interval_relation(t); m_interval = &r; } }; relation_mutator_fn * bound_relation_plugin::mk_filter_interpreted_fn(const relation_base & t, app * condition) { return alloc(filter_interpreted_fn, t.get_plugin().get_ast_manager(), condition); } // ----------------------------- // bound_relation void bound_relation_helper::mk_project_t(uint_set2& t, unsigned_vector const& renaming) { if (t.lt.empty() && t.le.empty()) { return; } uint_set::iterator it = t.lt.begin(), end = t.lt.end(); unsigned_vector ltv, lev; for (; it != end; ++it) { ltv.push_back(renaming[*it]); } it = t.le.begin(), end = t.le.end(); for (; it != end; ++it) { lev.push_back(renaming[*it]); } TRACE("dl", tout << "project: "; for (unsigned i = 0; i < renaming.size(); ++i) if (renaming[i] == UINT_MAX) tout << i << " "; tout << ": "; it = t.lt.begin(); end = t.lt.end(); for (; it != end; ++it) tout << *it << " "; tout << " le "; it = t.le.begin(); end = t.le.end(); for (; it != end; ++it) tout << *it << " "; tout << " => "; for (unsigned i = 0; i < ltv.size(); ++i) tout << ltv[i] << " "; tout << " le "; for (unsigned i = 0; i < lev.size(); ++i) tout << lev[i] << " "; tout << "\n";); t.lt.reset(); for (unsigned i = 0; i < ltv.size(); ++i) { t.lt.insert(ltv[i]); } t.le.reset(); for (unsigned i = 0; i < lev.size(); ++i) { t.le.insert(lev[i]); } } bound_relation::bound_relation(bound_relation_plugin& p, relation_signature const& s, bool is_empty): vector_relation(p, s, is_empty, uint_set2()) { } uint_set2 bound_relation::mk_intersect(uint_set2 const& t1, uint_set2 const& t2, bool& is_empty) const { is_empty = false; uint_set2 r(t1); r.lt |= t2.lt; r.le |= t2.le; return r; } uint_set2 bound_relation::mk_widen(uint_set2 const& t1, uint_set2 const& t2) const { return mk_unite(t1, t2); } uint_set2 bound_relation::mk_unite(uint_set2 const& t1, uint_set2 const& t2) const { uint_set2 s1(t1); s1.lt &= t2.lt; s1.le &= t2.le; return s1; } uint_set2 bound_relation::mk_eq(union_find<> const& old_eqs, union_find<> const& new_eqs, uint_set2 const& t) const { unsigned sz = old_eqs.get_num_vars(); SASSERT(sz == new_eqs.get_num_vars()); uint_set2 result; for (unsigned i = 0; i < sz; ++i) { if (t.lt.contains(i)) { unsigned j = i; do { result.lt.insert(new_eqs.find(j)); j = old_eqs.next(j); } while (j != i); } if (t.le.contains(i)) { unsigned j = i; do { result.le.insert(new_eqs.find(j)); j = old_eqs.next(j); } while (j != i); } } return result; } bool bound_relation::is_subset_of(uint_set2 const& t1, uint_set2 const& t2) const { uint_set2 s1, s2; normalize(t1, s1); normalize(t2, s2); return s1.lt.subset_of(s2.lt) && s1.le.subset_of(s2.le); } void bound_relation::mk_rename_elem(uint_set2& t, unsigned col_cnt, unsigned const* cycle) { // [ 0 -> 2 -> 3 -> 0] if (col_cnt == 0) return; unsigned col1, col2; col1 = find(cycle[0]); col2 = find(cycle[col_cnt-1]); bool has_col2_lt = t.lt.contains(col2); t.lt.remove(col2); bool has_col2_le = t.le.contains(col2); t.le.remove(col2); for (unsigned i = 0; i + 1 < col_cnt; ++i) { col1 = find(cycle[i]); col2 = find(cycle[i+1]); if (t.lt.contains(col1)) { t.lt.remove(col1); t.lt.insert(col2); } if (t.le.contains(col1)) { t.le.remove(col1); t.le.insert(col2); } } if (has_col2_lt) { col1 = find(cycle[0]); t.lt.insert(col1); } if (has_col2_le) { col1 = find(cycle[0]); t.le.insert(col1); } } bool bound_relation::is_full(uint_set2 const& t) const { return t.lt.empty() && t.le.empty(); } bool bound_relation::is_empty(unsigned index, uint_set2 const& t) const { return t.lt.contains(find(index)) || t.le.contains(find(index)); } void bound_relation::normalize(uint_set const& src, uint_set& dst) const { uint_set::iterator it = src.begin(), end = src.end(); for (; it != end; ++it) { dst.insert(find(*it)); } } void bound_relation::normalize(uint_set2 const& src, uint_set2& dst) const { normalize(src.lt, dst.lt); normalize(src.le, dst.le); } void bound_relation::mk_lt(unsigned i) { uint_set2& dst = (*this)[i]; while (!m_todo.empty()) { unsigned j = m_todo.back().first; bool strict = m_todo.back().second; if (i == j && strict) { m_todo.reset(); m_empty = true; return; } m_todo.pop_back(); if (i == j) { continue; } uint_set2& src = (*m_elems)[j]; uint_set::iterator it = src.lt.begin(), end = src.lt.end(); for(; it != end; ++it) { m_todo.push_back(std::make_pair(*it, true)); } it = src.le.begin(), end = src.le.end(); for(; it != end; ++it) { m_todo.push_back(std::make_pair(*it, strict)); } if (strict) { dst.lt.insert(j); } else { dst.le.insert(j); } } } void bound_relation::mk_lt(unsigned i, unsigned j) { m_todo.reset(); i = find(i); m_todo.push_back(std::make_pair(find(j), true)); mk_lt(i); } void bound_relation::mk_le(unsigned i, unsigned j) { m_todo.reset(); i = find(i); m_todo.push_back(std::make_pair(find(j), false)); mk_lt(i); } bool bound_relation::is_lt(unsigned i, unsigned j) const { return (*this)[i].lt.contains(find(j)); } void bound_relation::add_fact(const relation_fact & f) { bound_relation r(get_plugin(), get_signature(), false); for (unsigned i = 0; i < f.size(); ++i) { scoped_ptr fe = get_plugin().mk_filter_equal_fn(r, f[i], i); (*fe)(r); } mk_union(r, nullptr, false); } bool bound_relation::contains_fact(const relation_fact & f) const { if (empty()) { return false; } // this is a very rough approximation. return true; } bound_relation * bound_relation::clone() const { bound_relation* result = nullptr; if (empty()) { result = bound_relation_plugin::get(get_plugin().mk_empty(get_signature())); } else { result = bound_relation_plugin::get(get_plugin().mk_full(nullptr, get_signature())); result->copy(*this); } return result; } void bound_relation::mk_union_i(interval_relation const& src, bound_relation* delta, bool is_widen) { unsigned size = get_signature().size(); for (unsigned i = 0; i < size; ++i) { if (find(i) != i) { continue; } uint_set2& s = (*this)[i]; ext_numeral const& lo = src[i].sup(); if (lo.is_infinite()) { s.lt.reset(); s.le.reset(); continue; } uint_set::iterator it = s.lt.begin(), end = s.lt.end(); for(; it != end; ++it) { ext_numeral const& hi = src[*it].inf(); if (hi.is_infinite() || lo.to_rational() >= hi.to_rational()) { s.lt.remove(*it); } } it = s.le.begin(), end = s.le.end(); for(; it != end; ++it) { ext_numeral const& hi = src[*it].inf(); if (hi.is_infinite() || lo.to_rational() > hi.to_rational()) { s.le.remove(*it); } } } } bound_relation * bound_relation::complement(func_decl* p) const { UNREACHABLE(); return nullptr; } void bound_relation::to_formula(expr_ref& fml) const { ast_manager& m = get_plugin().get_ast_manager(); arith_util& arith = get_plugin().m_arith; bool_rewriter& bsimp = get_plugin().m_bsimp; expr_ref_vector conjs(m); relation_signature const& sig = get_signature(); for (unsigned i = 0; i < sig.size(); ++i) { if (i != find(i)) { conjs.push_back(m.mk_eq(m.mk_var(i, sig[i]), m.mk_var(find(i), sig[find(i)]))); continue; } uint_set2 const& upper = (*this)[i]; uint_set::iterator it = upper.lt.begin(), end = upper.lt.end(); for (; it != end; ++it) { conjs.push_back(arith.mk_lt(m.mk_var(i, sig[i]), m.mk_var(*it, sig[*it]))); } it = upper.le.begin(), end = upper.le.end(); for (; it != end; ++it) { conjs.push_back(arith.mk_le(m.mk_var(i, sig[i]), m.mk_var(*it, sig[*it]))); } } bsimp.mk_and(conjs.size(), conjs.c_ptr(), fml); } void bound_relation::display_index(unsigned i, uint_set2 const& src, std::ostream & out) const { uint_set::iterator it = src.lt.begin(), end = src.lt.end(); out << "#" << i; if (!src.lt.empty()) { out << " < "; for(; it != end; ++it) { out << *it << " "; } } if (!src.le.empty()) { it = src.le.begin(), end = src.le.end(); out << " <= "; for(; it != end; ++it) { out << *it << " "; } } if (src.lt.empty() && src.le.empty()) { out << " < oo"; } out << "\n"; } bound_relation_plugin& bound_relation::get_plugin() const { return dynamic_cast(relation_base::get_plugin()); } }; z3-z3-4.8.7/src/muz/rel/dl_bound_relation.h000066400000000000000000000141301356505360400204460ustar00rootroot00000000000000/*++ Copyright (c) 2010 Microsoft Corporation Module Name: dl_bound_relation.h Abstract: Basic (strict upper) bound relation. Author: Nikolaj Bjorner (nbjorner) 2010-2-11 Revision History: --*/ #ifndef DL_BOUND_RELATION_H_ #define DL_BOUND_RELATION_H_ #include "muz/base/dl_context.h" #include "muz/rel/dl_relation_manager.h" #include "muz/rel/dl_base.h" #include "util/uint_set.h" #include "muz/rel/dl_vector_relation.h" #include "muz/rel/dl_interval_relation.h" #include "ast/arith_decl_plugin.h" #include "ast/rewriter/bool_rewriter.h" namespace datalog { class bound_relation; class bound_relation_plugin : public relation_plugin { friend class bound_relation; class join_fn; class project_fn; class rename_fn; class union_fn; class union_fn_i; class filter_equal_fn; class filter_identical_fn; class filter_interpreted_fn; class filter_intersection_fn; arith_util m_arith; bool_rewriter m_bsimp; public: bound_relation_plugin(relation_manager& m); bool can_handle_signature(const relation_signature & s) override; static symbol get_name() { return symbol("bound_relation"); } relation_base * mk_empty(const relation_signature & s) override; relation_base * mk_full(func_decl* p, const relation_signature & s) override; relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) override; relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, const unsigned * removed_cols) override; relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, const unsigned * permutation_cycle) override; relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta) override; relation_union_fn * mk_widen_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta) override; relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, const unsigned * identical_cols) override; relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, unsigned col) override; relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition) override; relation_join_fn * mk_join_project_fn(const relation_base & t1, const relation_base & t2, unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols) override { return nullptr; } #if 0 virtual intersection_filter_fn * mk_filter_by_intersection_fn( const relation_base & t, const relation_base & src, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * src_cols) { return 0; } #endif static bound_relation* get(relation_base* r); private: static bound_relation& get(relation_base& r); static bound_relation const & get(relation_base const& r); static bool is_interval_relation(relation_base const& r); static interval_relation& get_interval_relation(relation_base& r); static interval_relation const& get_interval_relation(relation_base const& r); }; struct uint_set2 { uint_set lt; uint_set le; uint_set2(uint_set2 const& other):lt(other.lt), le(other.le) {} uint_set2() {} bool operator==(const uint_set2& other) const { return other.lt == lt && other.le == le; } bool operator!=(const uint_set2& other) const { return other.lt != lt || other.le != le; } }; inline std::ostream & operator<<(std::ostream & target, const uint_set2 & s) { return target << s.lt << " " << s.le; } class bound_relation_helper { public: static void mk_project_t(uint_set2& t, unsigned_vector const& renaming); }; class bound_relation : public vector_relation { friend class bound_relation_plugin; svector > m_todo; public: bound_relation(bound_relation_plugin& p, relation_signature const& s, bool is_empty); bound_relation& operator=(bound_relation const& other); bool empty() const override { return m_empty; } void add_fact(const relation_fact & f) override; bool contains_fact(const relation_fact & f) const override; bound_relation * clone() const override; bound_relation * complement(func_decl* p) const override; void to_formula(expr_ref& fml) const override; bound_relation_plugin& get_plugin() const; void mk_union_i(interval_relation const& src, bound_relation* delta, bool is_widen); void mk_lt(unsigned i, unsigned j); void mk_lt(unsigned i); void mk_le(unsigned i, unsigned j); bool is_lt(unsigned i, unsigned j) const; bool is_precise() const override { return false; } private: typedef uint_set2 T; T mk_intersect(T const& t1, T const& t2, bool& is_empty) const override; T mk_widen(T const& t1, T const& t2) const override; T mk_unite(T const& t1, T const& t2) const override; T mk_eq(union_find<> const& old_eqs, union_find<> const& new_eqs, T const& t) const override; void mk_rename_elem(T& i, unsigned col_cnt, unsigned const* cycle) override; bool is_subset_of(T const& t1, T const& t2) const override; bool is_full(T const& t) const override; bool is_empty(unsigned idx, T const& t) const override; void display_index(unsigned idx, T const& t, std::ostream& out) const override; void normalize(T const& src, T& dst) const; void normalize(uint_set const& src, uint_set& dst) const; }; }; #endif z3-z3-4.8.7/src/muz/rel/dl_check_table.cpp000066400000000000000000000441521356505360400202300ustar00rootroot00000000000000/*++ Copyright (c) 2010 Microsoft Corporation Module Name: dl_check_table.cpp Abstract: Author: Nikolaj Bjorner (nbjorner) 2010-11-15 Revision History: --*/ #include "muz/rel/dl_check_table.h" #include "muz/rel/dl_table.h" namespace datalog { bool check_table_plugin::can_handle_signature(table_signature const& s) { return m_tocheck.can_handle_signature(s) && m_checker.can_handle_signature(s); } check_table & check_table_plugin::get(table_base& r) { return static_cast(r); } check_table const & check_table_plugin::get(table_base const& r) { return static_cast(r); } table_base& check_table_plugin::checker(table_base& r) { return *get(r).m_checker; } table_base const& check_table_plugin::checker(table_base const& r) { return *get(r).m_checker; } table_base* check_table_plugin::checker(table_base* r) { return r?(get(*r).m_checker):nullptr; } table_base const* check_table_plugin::checker(table_base const* r) { return r?(get(*r).m_checker):nullptr; } table_base& check_table_plugin::tocheck(table_base& r) { return *get(r).m_tocheck; } table_base const& check_table_plugin::tocheck(table_base const& r) { return *get(r).m_tocheck; } table_base* check_table_plugin::tocheck(table_base* r) { return r?(get(*r).m_tocheck):nullptr; } table_base const* check_table_plugin::tocheck(table_base const* r) { return r?(get(*r).m_tocheck):nullptr; } table_base * check_table_plugin::mk_empty(const table_signature & s) { IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";); table_base* checker = m_checker.mk_empty(s); table_base* tocheck = m_tocheck.mk_empty(s); return alloc(check_table, *this, s, tocheck, checker); } class check_table_plugin::join_fn : public table_join_fn { scoped_ptr m_tocheck; scoped_ptr m_checker; public: join_fn(check_table_plugin& p, const table_base & t1, const table_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { m_tocheck = p.get_manager().mk_join_fn(tocheck(t1), tocheck(t2), col_cnt, cols1, cols2); m_checker = p.get_manager().mk_join_fn(checker(t1), checker(t2), col_cnt, cols1, cols2); } table_base* operator()(const table_base & t1, const table_base & t2) override { IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";); table_base* ttocheck = (*m_tocheck)(tocheck(t1), tocheck(t2)); table_base* tchecker = (*m_checker)(checker(t1), checker(t2)); check_table* result = alloc(check_table, get(t1).get_plugin(), ttocheck->get_signature(), ttocheck, tchecker); return result; } }; table_join_fn * check_table_plugin::mk_join_fn(const table_base & t1, const table_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { if (!check_kind(t1) || !check_kind(t2)) { return nullptr; } return alloc(join_fn, *this, t1, t2, col_cnt, cols1, cols2); } class check_table_plugin::join_project_fn : public table_join_fn { scoped_ptr m_tocheck; scoped_ptr m_checker; public: join_project_fn(check_table_plugin& p, const table_base & t1, const table_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols) { m_tocheck = p.get_manager().mk_join_project_fn(tocheck(t1), tocheck(t2), col_cnt, cols1, cols2, removed_col_cnt, removed_cols); m_checker = p.get_manager().mk_join_project_fn(checker(t1), checker(t2), col_cnt, cols1, cols2, removed_col_cnt, removed_cols); } table_base* operator()(const table_base & t1, const table_base & t2) override { table_base* ttocheck = (*m_tocheck)(tocheck(t1), tocheck(t2)); table_base* tchecker = (*m_checker)(checker(t1), checker(t2)); check_table* result = alloc(check_table, get(t1).get_plugin(), ttocheck->get_signature(), ttocheck, tchecker); return result; } }; table_join_fn * check_table_plugin::mk_join_project_fn(const table_base & t1, const table_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols) { if (!check_kind(t1) || !check_kind(t2)) { return nullptr; } return alloc(join_project_fn, *this, t1, t2, col_cnt, cols1, cols2, removed_col_cnt, removed_cols); } class check_table_plugin::union_fn : public table_union_fn { scoped_ptr m_tocheck; scoped_ptr m_checker; public: union_fn(check_table_plugin& p, table_base const& tgt, const table_base& src, table_base const* delta) { m_tocheck = p.get_manager().mk_union_fn(tocheck(tgt), tocheck(src), tocheck(delta)); m_checker = p.get_manager().mk_union_fn(checker(tgt), checker(src), checker(delta)); } void operator()(table_base& tgt, const table_base& src, table_base* delta) override { IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";); (*m_tocheck)(tocheck(tgt), tocheck(src), tocheck(delta)); (*m_checker)(checker(tgt), checker(src), checker(delta)); get(tgt).well_formed(); if (delta) { get(*delta).well_formed(); } } }; table_union_fn * check_table_plugin::mk_union_fn(const table_base & tgt, const table_base & src, const table_base * delta) { if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) { return nullptr; } return alloc(union_fn, *this, tgt, src, delta); } class check_table_plugin::project_fn : public table_transformer_fn { scoped_ptr m_checker; scoped_ptr m_tocheck; public: project_fn(check_table_plugin& p, const table_base & t, unsigned col_cnt, const unsigned * removed_cols) { m_checker = p.get_manager().mk_project_fn(checker(t), col_cnt, removed_cols); m_tocheck = p.get_manager().mk_project_fn(tocheck(t), col_cnt, removed_cols); } table_base* operator()(table_base const& src) override { table_base* tchecker = (*m_checker)(checker(src)); table_base* ttocheck = (*m_tocheck)(tocheck(src)); check_table* result = alloc(check_table, get(src).get_plugin(), tchecker->get_signature(), ttocheck, tchecker); return result; } }; table_transformer_fn * check_table_plugin::mk_project_fn(const table_base & t, unsigned col_cnt, const unsigned * removed_cols) { if (!check_kind(t)) { return nullptr; } return alloc(project_fn, *this, t, col_cnt, removed_cols); } class check_table_plugin::select_equal_and_project_fn : public table_transformer_fn { scoped_ptr m_checker; scoped_ptr m_tocheck; public: select_equal_and_project_fn(check_table_plugin& p, const table_base & t, const table_element & value, unsigned col) { m_checker = p.get_manager().mk_select_equal_and_project_fn(checker(t), value, col); m_tocheck = p.get_manager().mk_select_equal_and_project_fn(tocheck(t), value, col); } table_base* operator()(table_base const& src) override { table_base* tchecker = (*m_checker)(checker(src)); table_base* ttocheck = (*m_tocheck)(tocheck(src)); check_table* result = alloc(check_table, get(src).get_plugin(), tchecker->get_signature(), ttocheck, tchecker); return result; } }; table_transformer_fn * check_table_plugin::mk_select_equal_and_project_fn(const table_base & t, const table_element & value, unsigned col) { if (!check_kind(t)) { return nullptr; } return alloc(select_equal_and_project_fn, *this, t, value, col); } class check_table_plugin::rename_fn : public table_transformer_fn { scoped_ptr m_checker; scoped_ptr m_tocheck; public: rename_fn(check_table_plugin& p, const table_base & t, unsigned cycle_len, unsigned const* cycle) { m_checker = p.get_manager().mk_rename_fn(checker(t), cycle_len, cycle); m_tocheck = p.get_manager().mk_rename_fn(tocheck(t), cycle_len, cycle); } table_base* operator()(table_base const& src) override { IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";); table_base* tchecker = (*m_checker)(checker(src)); table_base* ttocheck = (*m_tocheck)(tocheck(src)); check_table* result = alloc(check_table, get(src).get_plugin(), ttocheck->get_signature(), ttocheck, tchecker); return result; } }; table_transformer_fn * check_table_plugin::mk_rename_fn(const table_base & t, unsigned len, const unsigned * cycle) { if (!check_kind(t)) { return nullptr; } return alloc(rename_fn, *this, t, len, cycle); } class check_table_plugin::filter_identical_fn : public table_mutator_fn { scoped_ptr m_checker; scoped_ptr m_tocheck; public: filter_identical_fn(check_table_plugin& p, const table_base & t,unsigned cnt, unsigned const* cols) { m_checker = p.get_manager().mk_filter_identical_fn(checker(t), cnt, cols); m_tocheck = p.get_manager().mk_filter_identical_fn(tocheck(t), cnt, cols); } void operator()(table_base & t) override { (*m_checker)(checker(t)); (*m_tocheck)(tocheck(t)); get(t).well_formed(); } }; table_mutator_fn * check_table_plugin::mk_filter_identical_fn(const table_base & t, unsigned col_cnt, const unsigned * identical_cols) { if (check_kind(t)) { return alloc(filter_identical_fn, *this, t, col_cnt, identical_cols); } return nullptr; } class check_table_plugin::filter_equal_fn : public table_mutator_fn { scoped_ptr m_checker; scoped_ptr m_tocheck; public: filter_equal_fn(check_table_plugin& p, const table_base & t, const table_element & v, unsigned col) { m_checker = p.get_manager().mk_filter_equal_fn(checker(t), v, col); m_tocheck = p.get_manager().mk_filter_equal_fn(tocheck(t), v, col); } void operator()(table_base& src) override { (*m_checker)(checker(src)); (*m_tocheck)(tocheck(src)); get(src).well_formed(); } }; table_mutator_fn * check_table_plugin::mk_filter_equal_fn(const table_base & t, const table_element & value, unsigned col) { if (check_kind(t)) { return alloc(filter_equal_fn, *this, t, value, col); } return nullptr; } class check_table_plugin::filter_interpreted_fn : public table_mutator_fn { scoped_ptr m_checker; scoped_ptr m_tocheck; public: filter_interpreted_fn(check_table_plugin& p, const table_base & t, app * condition) { m_checker = p.get_manager().mk_filter_interpreted_fn(checker(t), condition); m_tocheck = p.get_manager().mk_filter_interpreted_fn(tocheck(t), condition); } void operator()(table_base& src) override { (*m_checker)(checker(src)); (*m_tocheck)(tocheck(src)); get(src).well_formed(); } }; table_mutator_fn * check_table_plugin::mk_filter_interpreted_fn(const table_base & t, app * condition) { if (check_kind(t)) { return alloc(filter_interpreted_fn, *this, t, condition); } return nullptr; } class check_table_plugin::filter_interpreted_and_project_fn : public table_transformer_fn { scoped_ptr m_checker; scoped_ptr m_tocheck; public: filter_interpreted_and_project_fn(check_table_plugin& p, const table_base & t, app * condition, unsigned removed_col_cnt, const unsigned * removed_cols) { m_checker = p.get_manager().mk_filter_interpreted_and_project_fn(checker(t), condition, removed_col_cnt, removed_cols); m_tocheck = p.get_manager().mk_filter_interpreted_and_project_fn(tocheck(t), condition, removed_col_cnt, removed_cols); } table_base* operator()(table_base const& src) override { table_base* tchecker = (*m_checker)(checker(src)); table_base* ttocheck = (*m_tocheck)(tocheck(src)); check_table* result = alloc(check_table, get(src).get_plugin(), ttocheck->get_signature(), ttocheck, tchecker); return result; } }; table_transformer_fn * check_table_plugin::mk_filter_interpreted_and_project_fn(const table_base & t, app * condition, unsigned removed_col_cnt, const unsigned * removed_cols) { if (check_kind(t)) { return alloc(filter_interpreted_and_project_fn, *this, t, condition, removed_col_cnt, removed_cols); } return nullptr; } class check_table_plugin::filter_by_negation_fn : public table_intersection_filter_fn { scoped_ptr m_checker; scoped_ptr m_tocheck; public: filter_by_negation_fn( check_table_plugin& p, const table_base & t, const table_base & negated_obj, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) { m_checker = p.get_manager().mk_filter_by_negation_fn(checker(t), checker(negated_obj), joined_col_cnt, t_cols, negated_cols); m_tocheck = p.get_manager().mk_filter_by_negation_fn(tocheck(t), tocheck(negated_obj), joined_col_cnt, t_cols, negated_cols); } void operator()(table_base& src, table_base const& negated_obj) override { IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";); (*m_checker)(checker(src), checker(negated_obj)); (*m_tocheck)(tocheck(src), tocheck(negated_obj)); get(src).well_formed(); } }; table_intersection_filter_fn * check_table_plugin::mk_filter_by_negation_fn(const table_base & t, const table_base & negated_obj, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) { if (check_kind(t) && check_kind(negated_obj)) { return alloc(filter_by_negation_fn, *this, t, negated_obj, joined_col_cnt, t_cols, negated_cols); } return nullptr; } // ------------------ // check_table check_table::check_table(check_table_plugin & p, const table_signature & sig): table_base(p, sig) { (well_formed()); } check_table::check_table(check_table_plugin & p, const table_signature & sig, table_base* tocheck, table_base* checker): table_base(p, sig), m_checker(checker), m_tocheck(tocheck) { well_formed(); } check_table::~check_table() { m_tocheck->deallocate(); m_checker->deallocate(); } bool check_table::well_formed() const { get_plugin().m_count++; iterator it = m_tocheck->begin(), end = m_tocheck->end(); for (; it != end; ++it) { table_fact fact; it->get_fact(fact); if (!m_checker->contains_fact(fact)) { m_tocheck->display(verbose_stream()); m_checker->display(verbose_stream()); verbose_stream() << get_plugin().m_count << "\n"; UNREACHABLE(); fatal_error(0); return false; } } iterator it2 = m_checker->begin(), end2 = m_checker->end(); for (; it2 != end2; ++it2) { table_fact fact; it2->get_fact(fact); if (!m_tocheck->contains_fact(fact)) { m_tocheck->display(verbose_stream()); m_checker->display(verbose_stream()); verbose_stream() << get_plugin().m_count << "\n"; UNREACHABLE(); fatal_error(0); return false; } } return true; } bool check_table::empty() const { if (m_tocheck->empty() != m_checker->empty()) { m_tocheck->display(verbose_stream()); m_checker->display(verbose_stream()); verbose_stream() << get_plugin().m_count << "\n"; fatal_error(0); } return m_tocheck->empty(); } void check_table::add_fact(const table_fact & f) { IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";); m_tocheck->add_fact(f); m_checker->add_fact(f); well_formed(); } void check_table::remove_fact(const table_element* f) { IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";); m_tocheck->remove_fact(f); m_checker->remove_fact(f); well_formed(); } bool check_table::contains_fact(const table_fact & f) const { return m_checker->contains_fact(f); } table_base * check_table::clone() const { IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";); check_table* result = alloc(check_table, get_plugin(), get_signature(), m_tocheck->clone(), m_checker->clone()); return result; } table_base * check_table::complement(func_decl* p, const table_element * func_columns) const { check_table* result = alloc(check_table, get_plugin(), get_signature(), m_tocheck->complement(p, func_columns), m_checker->complement(p, func_columns)); return result; } }; z3-z3-4.8.7/src/muz/rel/dl_check_table.h000066400000000000000000000120741356505360400176730ustar00rootroot00000000000000/*++ Copyright (c) 2010 Microsoft Corporation Module Name: dl_check_table.h Abstract: Author: Nikolaj Bjorner (nbjorner) 2010-11-15 Revision History: --*/ #ifndef DL_CHECK_TABLE_H_ #define DL_CHECK_TABLE_H_ #include "muz/rel/dl_base.h" #include "ast/dl_decl_plugin.h" #include "muz/rel/dl_relation_manager.h" namespace datalog { class check_table; class check_table_plugin : public table_plugin { friend class check_table; table_plugin& m_checker; table_plugin& m_tocheck; unsigned m_count; protected: class join_fn; class join_project_fn; class union_fn; class transformer_fn; class rename_fn; class project_fn; class select_equal_and_project_fn; class filter_equal_fn; class filter_identical_fn; class filter_interpreted_fn; class filter_interpreted_and_project_fn; class filter_by_negation_fn; public: check_table_plugin(relation_manager & manager, symbol const& checker, symbol const& tocheck) : table_plugin(symbol("check"), manager), m_checker(*manager.get_table_plugin(checker)), m_tocheck(*manager.get_table_plugin(tocheck)), m_count(0) {} table_base * mk_empty(const table_signature & s) override; table_join_fn * mk_join_fn(const table_base & t1, const table_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) override; table_join_fn * mk_join_project_fn(const table_base & t1, const table_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols) override; table_union_fn * mk_union_fn(const table_base & tgt, const table_base & src, const table_base * delta) override; table_transformer_fn * mk_project_fn(const table_base & t, unsigned col_cnt, const unsigned * removed_cols) override; table_transformer_fn * mk_select_equal_and_project_fn(const table_base & t, const table_element & value, unsigned col) override; table_transformer_fn * mk_rename_fn(const table_base & t, unsigned permutation_cycle_len, const unsigned * permutation_cycle) override; table_mutator_fn * mk_filter_identical_fn(const table_base & t, unsigned col_cnt, const unsigned * identical_cols) override; table_mutator_fn * mk_filter_equal_fn(const table_base & t, const table_element & value, unsigned col) override; table_mutator_fn * mk_filter_interpreted_fn(const table_base & t, app * condition) override; table_transformer_fn * mk_filter_interpreted_and_project_fn(const table_base & t, app * condition, unsigned removed_col_cnt, const unsigned * removed_cols) override; table_intersection_filter_fn * mk_filter_by_negation_fn( const table_base & t, const table_base & negated_obj, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) override; bool can_handle_signature(table_signature const& s) override; private: static check_table& get(table_base& r); static check_table const & get(table_base const& r); static table_base& checker(table_base& r); static table_base const& checker(table_base const& r); static table_base* checker(table_base* r); static table_base const* checker(table_base const* r); static table_base& tocheck(table_base& r); static table_base const& tocheck(table_base const& r); static table_base* tocheck(table_base* r); static table_base const* tocheck(table_base const* r); }; class check_table : public table_base { friend class check_table_plugin; table_base* m_checker; table_base* m_tocheck; check_table(check_table_plugin & p, const table_signature & sig); check_table(check_table_plugin & p, const table_signature & sig, table_base* tocheck, table_base* checker); ~check_table() override; bool well_formed() const; public: check_table_plugin & get_plugin() const { return static_cast(table_base::get_plugin()); } bool empty() const override; void add_fact(const table_fact & f) override; void remove_fact(const table_element* fact) override; bool contains_fact(const table_fact & f) const override; table_base * complement(func_decl* p, const table_element * func_columns = nullptr) const override; table_base * clone() const override; iterator begin() const override { SASSERT(well_formed()); return m_tocheck->begin(); } iterator end() const override { return m_tocheck->end(); } unsigned get_size_estimate_rows() const override { return m_tocheck->get_size_estimate_rows(); } unsigned get_size_estimate_bytes() const override { return m_tocheck->get_size_estimate_bytes(); } }; }; #endif /* DL_CHECK_TABLE_H_ */ z3-z3-4.8.7/src/muz/rel/dl_compiler.cpp000066400000000000000000001534001356505360400176130ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_compiler.cpp Abstract: Author: Krystof Hoder (t-khoder) 2010-09-14. Revision History: --*/ #include #include "util/ref_vector.h" #include "muz/base/dl_context.h" #include "muz/rel/rel_context.h" #include "muz/base/dl_rule.h" #include "muz/base/dl_util.h" #include "muz/rel/dl_compiler.h" #include "ast/ast_pp.h" // include"ast_smt2_pp.h" #include "ast/ast_util.h" namespace datalog { void compiler::reset() { m_pred_regs.reset(); } void compiler::ensure_predicate_loaded(func_decl * pred, instruction_block & acc) { pred2idx::obj_map_entry * e = m_pred_regs.insert_if_not_there2(pred, UINT_MAX); if(e->get_data().m_value!=UINT_MAX) { //predicate is already loaded return; } relation_signature sig; m_context.get_rel_context()->get_rmanager().from_predicate(pred, sig); reg_idx reg = get_fresh_register(sig); e->get_data().m_value=reg; acc.push_back(instruction::mk_load(m_context.get_manager(), pred, reg)); } void compiler::make_join(reg_idx t1, reg_idx t2, const variable_intersection & vars, reg_idx & result, bool reuse_t1, instruction_block & acc) { relation_signature res_sig; relation_signature::from_join(m_reg_signatures[t1], m_reg_signatures[t2], vars.size(), vars.get_cols1(), vars.get_cols2(), res_sig); result = get_register(res_sig, reuse_t1, t1); acc.push_back(instruction::mk_join(t1, t2, vars.size(), vars.get_cols1(), vars.get_cols2(), result)); } void compiler::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) { relation_signature aux_sig; relation_signature sig1 = m_reg_signatures[t1]; relation_signature sig2 = m_reg_signatures[t2]; relation_signature::from_join(sig1, sig2, vars.size(), vars.get_cols1(), vars.get_cols2(), aux_sig); relation_signature res_sig; relation_signature::from_project(aux_sig, removed_cols.size(), removed_cols.c_ptr(), res_sig); result = get_register(res_sig, reuse_t1, t1); acc.push_back(instruction::mk_join_project(t1, t2, vars.size(), vars.get_cols1(), vars.get_cols2(), removed_cols.size(), removed_cols.c_ptr(), result)); } void compiler::make_filter_interpreted_and_project(reg_idx src, app_ref & cond, const unsigned_vector & removed_cols, reg_idx & result, bool reuse, instruction_block & acc) { SASSERT(!removed_cols.empty()); relation_signature res_sig; relation_signature::from_project(m_reg_signatures[src], removed_cols.size(), removed_cols.c_ptr(), res_sig); result = get_register(res_sig, reuse, src); acc.push_back(instruction::mk_filter_interpreted_and_project(src, cond, removed_cols.size(), removed_cols.c_ptr(), result)); } void compiler::make_select_equal_and_project(reg_idx src, const relation_element val, unsigned col, reg_idx & result, bool reuse, instruction_block & acc) { relation_signature res_sig; relation_signature::from_project(m_reg_signatures[src], 1, &col, res_sig); result = get_register(res_sig, reuse, src); acc.push_back(instruction::mk_select_equal_and_project(m_context.get_manager(), src, val, col, result)); } void compiler::make_clone(reg_idx src, reg_idx & result, instruction_block & acc) { relation_signature sig = m_reg_signatures[src]; result = get_fresh_register(sig); acc.push_back(instruction::mk_clone(src, result)); } void compiler::make_union(reg_idx src, reg_idx tgt, reg_idx delta, bool use_widening, instruction_block & acc) { SASSERT(m_reg_signatures[src]==m_reg_signatures[tgt]); SASSERT(delta==execution_context::void_register || m_reg_signatures[src]==m_reg_signatures[delta]); if (use_widening) { acc.push_back(instruction::mk_widen(src, tgt, delta)); } else { acc.push_back(instruction::mk_union(src, tgt, delta)); } } void compiler::make_projection(reg_idx src, unsigned col_cnt, const unsigned * removed_cols, reg_idx & result, bool reuse, instruction_block & acc) { SASSERT(col_cnt>0); relation_signature res_sig; relation_signature::from_project(m_reg_signatures[src], col_cnt, removed_cols, res_sig); result = get_register(res_sig, reuse, src); acc.push_back(instruction::mk_projection(src, col_cnt, removed_cols, result)); } compiler::reg_idx compiler::get_fresh_register(const relation_signature & sig) { //since we might be resizing the m_reg_signatures vector, the argument must not point inside it SASSERT((&sig>=m_reg_signatures.end()) || (&sigget_rmanager().to_nice_string(s) << "\n";); IF_VERBOSE(3, { expr_ref e(m_context.get_manager()); m_context.get_rule_manager().to_formula(*compiled_rule, e); verbose_stream() << "Compiling unsafe rule column " << col_idx << "\n" << mk_ismt2_pp(e, m_context.get_manager()) << "\n"; }); reg_idx total_table; if (!m_total_registers.find(s, pred, total_table)) { total_table = get_single_column_register(s); relation_signature sig; sig.push_back(s); m_top_level_code.push_back(instruction::mk_total(sig, pred, total_table)); m_total_registers.insert(s, pred, total_table); } if(src == execution_context::void_register) { result = total_table; SASSERT(dealloc == false); } else { variable_intersection empty_vars(m_context.get_manager()); make_join(src, total_table, empty_vars, result, dealloc, acc); dealloc = true; } } void compiler::make_full_relation(func_decl* pred, const relation_signature & sig, reg_idx & result, instruction_block & acc) { SASSERT(sig.empty()); TRACE("dl", tout << "Adding unbound column " << mk_pp(pred, m_context.get_manager()) << "\n";); if (m_empty_tables_registers.find(pred, result)) return; result = get_fresh_register(sig); m_top_level_code.push_back(instruction::mk_total(sig, pred, result)); m_empty_tables_registers.insert(pred, result); } void compiler::make_duplicate_column(reg_idx src, unsigned col, reg_idx & result, bool reuse, instruction_block & acc) { relation_signature & src_sig = m_reg_signatures[src]; reg_idx single_col_reg; unsigned src_col_cnt = src_sig.size(); if(src_col_cnt==1) { single_col_reg = src; } else { unsigned_vector removed_cols; for(unsigned i=0; i & acis0, reg_idx & result, bool & dealloc, instruction_block & acc) { TRACE("dl", tout << mk_pp(head_pred, m_context.get_manager()) << "\n";); unsigned col_cnt = acis0.size(); reg_idx curr = src; relation_signature empty_signature; relation_signature * curr_sig; if(curr!=execution_context::void_register) { curr_sig = & m_reg_signatures[curr]; } else { curr_sig = & empty_signature; } unsigned src_col_cnt=curr_sig->size(); svector acis(acis0); int2int handled_unbound; //first remove unused source columns int_set referenced_src_cols; for(unsigned i=0; isize(); if(acis[i].kind==ACK_CONSTANT) { make_add_constant_column(head_pred, curr, acis[i].domain, acis[i].constant, curr, dealloc, acc); } else { SASSERT(acis[i].kind==ACK_UNBOUND_VAR); TRACE("dl", tout << head_pred->get_name() << " index: " << i << " " << m_context.get_rel_context()->get_rmanager().to_nice_string(acis[i].domain) << "\n";); make_add_unbound_column(compiled_rule, i, head_pred, curr, acis[i].domain, curr, dealloc, acc); handled_unbound.insert(acis[i].var_index,bound_column_index); } curr_sig = & m_reg_signatures[curr]; SASSERT(bound_column_index==curr_sig->size()-1); } SASSERT((*curr_sig)[bound_column_index]==acis[i].domain); acis[i].kind=ACK_BOUND_VAR; acis[i].source_column=bound_column_index; } //duplicate needed source columns int_set used_cols; for(unsigned i=0; isize()-1; SASSERT((*curr_sig)[bound_column_index]==acis[i].domain); acis[i].source_column=bound_column_index; } //reorder source columns to match target SASSERT(curr_sig->size()==col_cnt); //now the intermediate table is a permutation for(unsigned i=0; i=i); //columns below i are already reordered SASSERT(nextget_num_args(); for(unsigned i = 0; iget_arg(i); if (is_var(e) && globals.get(to_var(e)->get_idx()) > 0) { globals.update(to_var(e)->get_idx(), -1); res.push_back(i + ofs); } } } void compiler::get_local_indexes_for_projection(rule * r, unsigned_vector & res) { SASSERT(r->get_positive_tail_size()==2); rule_counter counter; // leave one column copy per var in the head (avoids later duplication) counter.count_vars(r->get_head(), -1); // take interp & neg preds into account (at least 1 column copy if referenced) unsigned n = r->get_tail_size(); if (n > 2) { rule_counter counter_tail; for (unsigned i = 2; i < n; ++i) { counter_tail.count_vars(r->get_tail(i)); } rule_counter::iterator I = counter_tail.begin(), E = counter_tail.end(); for (; I != E; ++I) { int& n = counter.get(I->m_key); if (n == 0) n = -1; } } app * t1 = r->get_tail(0); app * t2 = r->get_tail(1); counter.count_vars(t1); counter.count_vars(t2); get_local_indexes_for_projection(t1, counter, 0, res); get_local_indexes_for_projection(t2, counter, t1->get_num_args(), res); } void compiler::compile_rule_evaluation_run(rule * r, reg_idx head_reg, const reg_idx * tail_regs, reg_idx delta_reg, bool use_widening, instruction_block & acc) { ast_manager & m = m_context.get_manager(); m_instruction_observer.start_rule(r); const app * h = r->get_head(); unsigned head_len = h->get_num_args(); func_decl * head_pred = h->get_decl(); TRACE("dl", r->display(m_context, tout); ); unsigned pt_len = r->get_positive_tail_size(); SASSERT(pt_len<=2); //we require rules to be processed by the mk_simple_joins rule transformer plugin reg_idx single_res; expr_ref_vector single_res_expr(m); //used to save on filter_identical instructions where the check is already done //by the join operation unsigned second_tail_arg_ofs = 0; // whether to dealloc the previous result bool dealloc = true; if(pt_len == 2) { reg_idx t1_reg=tail_regs[0]; reg_idx t2_reg=tail_regs[1]; app * a1 = r->get_tail(0); app * a2 = r->get_tail(1); SASSERT(m_reg_signatures[t1_reg].size()==a1->get_num_args()); SASSERT(m_reg_signatures[t2_reg].size()==a2->get_num_args()); variable_intersection a1a2(m_context.get_manager()); a1a2.populate(a1,a2); unsigned_vector removed_cols; get_local_indexes_for_projection(r, removed_cols); if(removed_cols.empty()) { make_join(t1_reg, t2_reg, a1a2, single_res, false, acc); } else { make_join_project(t1_reg, t2_reg, a1a2, removed_cols, single_res, false, acc); } unsigned rem_index = 0; unsigned rem_sz = removed_cols.size(); unsigned a1len=a1->get_num_args(); for(unsigned i=0; i=i); if(rem_indexget_arg(i)); } second_tail_arg_ofs = single_res_expr.size(); unsigned a2len=a2->get_num_args(); for(unsigned i=0; i=i+a1len); if(rem_indexget_arg(i)); } SASSERT(rem_index==rem_sz); } else if(pt_len==1) { app * a = r->get_tail(0); single_res = tail_regs[0]; dealloc = false; SASSERT(m_reg_signatures[single_res].size() == a->get_num_args()); unsigned n=a->get_num_args(); for(unsigned i=0; iget_arg(i); if(is_app(arg)) { app * c = to_app(arg); //argument is a constant SASSERT(m.is_value(c)); make_select_equal_and_project(single_res, c, single_res_expr.size(), single_res, dealloc, acc); dealloc = true; } else { SASSERT(is_var(arg)); single_res_expr.push_back(arg); } } } else { SASSERT(pt_len==0); //single_res register should never be used in this case single_res=execution_context::void_register; dealloc = false; } add_unbound_columns_for_negation(r, head_pred, single_res, single_res_expr, dealloc, acc); int2ints var_indexes; reg_idx filtered_res = single_res; { //enforce equality to constants unsigned srlen=single_res_expr.size(); SASSERT((single_res==execution_context::void_register) ? (srlen==0) : (srlen==m_reg_signatures[single_res].size())); for(unsigned i=0; iget_idx(); int2ints::entry * e = var_indexes.insert_if_not_there2(var_num, unsigned_vector()); e->get_data().m_value.push_back(i); } } } //enforce equality of columns int2ints::iterator vit=var_indexes.begin(); int2ints::iterator vend=var_indexes.end(); for(; vit!=vend; ++vit) { int2ints::key_data & k = *vit; unsigned_vector & indexes = k.m_value; if(indexes.size()==1) { continue; } SASSERT(indexes.size()>1); if(pt_len==2 && indexes[0]=second_tail_arg_ofs) { //If variable appears in multiple tails, the identicity will already be enforced by join. //(If behavior the join changes so that it is not enforced anymore, remove this //condition!) continue; } if (!dealloc) make_clone(filtered_res, filtered_res, acc); acc.push_back(instruction::mk_filter_identical(filtered_res, indexes.size(), indexes.c_ptr())); dealloc = true; } // add unbounded columns for interpreted filter unsigned ut_len = r->get_uninterpreted_tail_size(); unsigned ft_len = r->get_tail_size(); // full tail expr_ref_vector tail(m); for (unsigned tail_index = ut_len; tail_index < ft_len; ++tail_index) { tail.push_back(r->get_tail(tail_index)); } expr_ref_vector binding(m); if (!tail.empty()) { expr_ref filter_cond = mk_and(tail); m_free_vars.reset(); m_free_vars(filter_cond); // create binding binding.resize(m_free_vars.size()+1); for (unsigned v = 0; v < m_free_vars.size(); ++v) { if (!m_free_vars[v]) continue; int2ints::entry * entry = var_indexes.find_core(v); unsigned src_col; if (entry) { src_col = entry->get_data().m_value.back(); } else { // we have an unbound variable, so we add an unbound column for it relation_sort unbound_sort = m_free_vars[v]; TRACE("dl", tout << "unbound: " << v << "\n" << filter_cond << " " << mk_pp(unbound_sort, m) << "\n";); make_add_unbound_column(r, 0, head_pred, filtered_res, unbound_sort, filtered_res, dealloc, acc); src_col = single_res_expr.size(); single_res_expr.push_back(m.mk_var(v, unbound_sort)); entry = var_indexes.insert_if_not_there2(v, unsigned_vector()); entry->get_data().m_value.push_back(src_col); } relation_sort var_sort = m_reg_signatures[filtered_res][src_col]; binding[m_free_vars.size()-v] = m.mk_var(src_col, var_sort); } } // add at least one column for the negative filter if (pt_len != ut_len && filtered_res == execution_context::void_register) { relation_signature empty_signature; make_full_relation(head_pred, empty_signature, filtered_res, acc); } //enforce negative predicates for (unsigned i = pt_len; iget_tail(i); func_decl * neg_pred = neg_tail->get_decl(); variable_intersection neg_intersection(m_context.get_manager()); neg_intersection.populate(single_res_expr, neg_tail); unsigned_vector t_cols(neg_intersection.size(), neg_intersection.get_cols1()); unsigned_vector neg_cols(neg_intersection.size(), neg_intersection.get_cols2()); unsigned neg_len = neg_tail->get_num_args(); for (unsigned i = 0; iget_arg(i); if (is_var(e)) { continue; } SASSERT(is_app(e)); relation_sort arg_sort; m_context.get_rel_context()->get_rmanager().from_predicate(neg_pred, i, arg_sort); make_add_constant_column(head_pred, filtered_res, arg_sort, to_app(e), filtered_res, dealloc, acc); t_cols.push_back(single_res_expr.size()); neg_cols.push_back(i); single_res_expr.push_back(e); } SASSERT(t_cols.size() == neg_cols.size()); reg_idx neg_reg = m_pred_regs.find(neg_pred); if (!dealloc) make_clone(filtered_res, filtered_res, acc); acc.push_back(instruction::mk_filter_by_negation(filtered_res, neg_reg, t_cols.size(), t_cols.c_ptr(), neg_cols.c_ptr())); dealloc = true; } // enforce interpreted tail predicates if (!tail.empty()) { app_ref filter_cond(tail.size() == 1 ? to_app(tail.back()) : m.mk_and(tail.size(), tail.c_ptr()), m); // check if there are any columns to remove unsigned_vector remove_columns; { unsigned_vector var_idx_to_remove; m_free_vars(r->get_head()); for (int2ints::iterator I = var_indexes.begin(), E = var_indexes.end(); I != E; ++I) { unsigned var_idx = I->m_key; if (!m_free_vars.contains(var_idx)) { unsigned_vector & cols = I->m_value; for (unsigned i = 0; i < cols.size(); ++i) { remove_columns.push_back(cols[i]); } var_idx_to_remove.push_back(var_idx); } } for (unsigned i = 0; i < var_idx_to_remove.size(); ++i) { var_indexes.remove(var_idx_to_remove[i]); } // update column idx for after projection state if (!remove_columns.empty()) { unsigned_vector offsets; offsets.resize(single_res_expr.size(), 0); for (unsigned i = 0; i < remove_columns.size(); ++i) { for (unsigned col = remove_columns[i]; col < offsets.size(); ++col) { ++offsets[col]; } } for (int2ints::iterator I = var_indexes.begin(), E = var_indexes.end(); I != E; ++I) { unsigned_vector & cols = I->m_value; for (unsigned i = 0; i < cols.size(); ++i) { cols[i] -= offsets[cols[i]]; } } } } expr_ref renamed = m_context.get_var_subst()(filter_cond, binding.size(), binding.c_ptr()); app_ref app_renamed(to_app(renamed), m); if (remove_columns.empty()) { if (!dealloc) make_clone(filtered_res, filtered_res, acc); acc.push_back(instruction::mk_filter_interpreted(filtered_res, app_renamed)); } else { std::sort(remove_columns.begin(), remove_columns.end()); make_filter_interpreted_and_project(filtered_res, app_renamed, remove_columns, filtered_res, dealloc, acc); } dealloc = true; } #if 0 // this version is potentially better for non-symbolic tables, // since it constraints each unbound column at a time (reducing the // size of intermediate results). unsigned ft_len=r->get_tail_size(); //full tail for(unsigned tail_index=ut_len; tail_indexget_tail(tail_index); m_free_vars(t); if (m_free_vars.empty()) { expr_ref simplified(m); m_context.get_rewriter()(t, simplified); if(m.is_true(simplified)) { //this tail element is always true continue; } //the tail of this rule is never satisfied SASSERT(m.is_false(simplified)); goto finish; } //determine binding size unsigned max_var = m_free_vars.size(); while (max_var > 0 && !m_free_vars[max_var-1]) --max_var; //create binding expr_ref_vector binding(m); binding.resize(max_var); for(unsigned v = 0; v < max_var; ++v) { if (!m_free_vars[v]) { continue; } int2ints::entry * e = var_indexes.find_core(v); if(!e) { //we have an unbound variable, so we add an unbound column for it relation_sort unbound_sort = m_free_vars[v]; reg_idx new_reg; TRACE("dl", tout << mk_pp(head_pred, m_context.get_manager()) << "\n";); bool new_dealloc; make_add_unbound_column(r, 0, head_pred, filtered_res, unbound_sort, new_reg, new_dealloc, acc); if (dealloc) make_dealloc_non_void(filtered_res, acc); dealloc = new_dealloc; filtered_res = new_reg; // here filtered_res value gets changed !! unsigned unbound_column_index = single_res_expr.size(); single_res_expr.push_back(m.mk_var(v, unbound_sort)); e = var_indexes.insert_if_not_there2(v, unsigned_vector()); e->get_data().m_value.push_back(unbound_column_index); } unsigned src_col=e->get_data().m_value.back(); relation_sort var_sort = m_reg_signatures[filtered_res][src_col]; binding[max_var-v]=m.mk_var(src_col, var_sort); } expr_ref renamed(m); m_context.get_var_subst()(t, binding.size(), binding.c_ptr(), renamed); app_ref app_renamed(to_app(renamed), m); if (!dealloc) make_clone(filtered_res, filtered_res, acc); acc.push_back(instruction::mk_filter_interpreted(filtered_res, app_renamed)); dealloc = true; } #endif { //put together the columns of head relation relation_signature & head_sig = m_reg_signatures[head_reg]; svector head_acis; unsigned_vector head_src_cols; for(unsigned i=0; iget_arg(i); if(is_var(exp)) { unsigned var_num=to_var(exp)->get_idx(); int2ints::entry * e = var_indexes.find_core(var_num); if(e) { unsigned_vector & binding_indexes = e->get_data().m_value; aci.kind=ACK_BOUND_VAR; aci.source_column=binding_indexes.back(); SASSERT(aci.source_column1) { //if possible, we do not want multiple head columns //point to a single column in the intermediate table, //since then we would have to duplicate the column //(and remove columns we did not point to at all) binding_indexes.pop_back(); } } else { aci.kind=ACK_UNBOUND_VAR; aci.var_index=var_num; } } else { SASSERT(is_app(exp)); SASSERT(m_context.get_decl_util().is_numeral_ext(exp)); aci.kind=ACK_CONSTANT; aci.constant=to_app(exp); } head_acis.push_back(aci); } SASSERT(head_acis.size()==head_len); reg_idx new_head_reg; make_assembling_code(r, head_pred, filtered_res, head_acis, new_head_reg, dealloc, acc); //update the head relation make_union(new_head_reg, head_reg, delta_reg, use_widening, acc); if (dealloc) make_dealloc_non_void(new_head_reg, acc); } // finish: m_instruction_observer.finish_rule(); } void compiler::add_unbound_columns_for_negation(rule* r, func_decl* pred, reg_idx& single_res, expr_ref_vector& single_res_expr, bool & dealloc, instruction_block & acc) { uint_set pos_vars; u_map neg_vars; ast_manager& m = m_context.get_manager(); unsigned pt_len = r->get_positive_tail_size(); unsigned ut_len = r->get_uninterpreted_tail_size(); // no negated predicates if (pt_len == ut_len) return; // populate negative variables: for (unsigned i = pt_len; i < ut_len; ++i) { app * neg_tail = r->get_tail(i); unsigned neg_len = neg_tail->get_num_args(); for (unsigned j = 0; j < neg_len; ++j) { expr * e = neg_tail->get_arg(j); if (is_var(e)) { unsigned idx = to_var(e)->get_idx(); neg_vars.insert(idx, e); } } } // populate positive variables: for (unsigned i = 0; i < single_res_expr.size(); ++i) { expr* e = single_res_expr[i].get(); if (is_var(e)) { pos_vars.insert(to_var(e)->get_idx()); } } // add negative variables that are not in positive u_map::iterator it = neg_vars.begin(), end = neg_vars.end(); for (; it != end; ++it) { unsigned v = it->m_key; expr* e = it->m_value; if (!pos_vars.contains(v)) { single_res_expr.push_back(e); make_add_unbound_column(r, v, pred, single_res, m.get_sort(e), single_res, dealloc, acc); TRACE("dl", tout << "Adding unbound column: " << mk_pp(e, m) << "\n";); } } } void compiler::compile_rule_evaluation(rule * r, const pred2idx * input_deltas, reg_idx output_delta, bool use_widening, instruction_block & acc) { typedef std::pair tail_delta_info; //(delta register, tail index) typedef svector tail_delta_infos; unsigned rule_len = r->get_uninterpreted_tail_size(); reg_idx head_reg = m_pred_regs.find(r->get_decl()); svector tail_regs; tail_delta_infos tail_deltas; for(unsigned j=0;jget_tail(j)->get_decl(); reg_idx tail_reg = m_pred_regs.find(tail_pred); tail_regs.push_back(tail_reg); if(input_deltas && !all_or_nothing_deltas()) { reg_idx tail_delta_idx; if(input_deltas->find(tail_pred, tail_delta_idx)) { tail_deltas.push_back(tail_delta_info(tail_delta_idx, j)); } } } if(!input_deltas || all_or_nothing_deltas()) { compile_rule_evaluation_run(r, head_reg, tail_regs.c_ptr(), output_delta, use_widening, acc); } else { tail_delta_infos::iterator tdit = tail_deltas.begin(); tail_delta_infos::iterator tdend = tail_deltas.end(); for(; tdit!=tdend; ++tdit) { tail_delta_info tdinfo = *tdit; flet flet_tail_reg(tail_regs[tdinfo.second], tdinfo.first); compile_rule_evaluation_run(r, head_reg, tail_regs.c_ptr(), output_delta, use_widening, acc); } } } class cycle_breaker { typedef func_decl * T; typedef rule_dependencies::item_set item_set; //set of T rule_dependencies & m_deps; item_set & m_removed; svector m_stack; ast_mark m_stack_content; ast_mark m_visited; void traverse(T v) { SASSERT(!m_stack_content.is_marked(v)); if(m_visited.is_marked(v) || m_removed.contains(v)) { return; } m_stack.push_back(v); m_stack_content.mark(v, true); m_visited.mark(v, true); const item_set & deps = m_deps.get_deps(v); item_set::iterator it = deps.begin(); item_set::iterator end = deps.end(); for(; it!=end; ++it) { T d = *it; if(m_stack_content.is_marked(d)) { //TODO: find the best vertex to remove in the cycle m_removed.insert(v); break; } traverse(d); } SASSERT(m_stack.back()==v); m_stack.pop_back(); m_stack_content.mark(v, false); } public: cycle_breaker(rule_dependencies & deps, item_set & removed) : m_deps(deps), m_removed(removed) { SASSERT(removed.empty()); } void operator()() { rule_dependencies::iterator it = m_deps.begin(); rule_dependencies::iterator end = m_deps.end(); for(; it!=end; ++it) { T v = it->m_key; traverse(v); } m_deps.remove(m_removed); } }; void compiler::detect_chains(const func_decl_set & preds, func_decl_vector & ordered_preds, func_decl_set & global_deltas) { SASSERT(ordered_preds.empty()); SASSERT(global_deltas.empty()); rule_dependencies deps(m_rule_set.get_dependencies()); deps.restrict(preds); cycle_breaker(deps, global_deltas)(); VERIFY( deps.sort_deps(ordered_preds) ); //the predicates that were removed to get acyclic induced subgraph are put last //so that all their local input deltas are already populated func_decl_set::iterator gdit = global_deltas.begin(); func_decl_set::iterator gend = global_deltas.end(); for(; gdit!=gend; ++gdit) { ordered_preds.push_back(*gdit); } } void compiler::compile_preds(const func_decl_vector & head_preds, const func_decl_set & widened_preds, const pred2idx * input_deltas, const pred2idx & output_deltas, instruction_block & acc) { func_decl_vector::const_iterator hpit = head_preds.begin(); func_decl_vector::const_iterator hpend = head_preds.end(); for(; hpit!=hpend; ++hpit) { func_decl * head_pred = *hpit; bool widen_predicate_in_loop = widened_preds.contains(head_pred); reg_idx d_head_reg; //output delta for the initial rule execution if(!output_deltas.find(head_pred, d_head_reg)) { d_head_reg = execution_context::void_register; } const rule_vector & pred_rules = m_rule_set.get_predicate_rules(head_pred); rule_vector::const_iterator rit = pred_rules.begin(); rule_vector::const_iterator rend = pred_rules.end(); for(; rit!=rend; ++rit) { rule * r = *rit; SASSERT(head_pred==r->get_decl()); compile_rule_evaluation(r, input_deltas, d_head_reg, widen_predicate_in_loop, acc); } } } void compiler::compile_preds_init(const func_decl_vector & head_preds, const func_decl_set & widened_preds, const pred2idx * input_deltas, const pred2idx & output_deltas, instruction_block & acc) { func_decl_vector::const_iterator hpit = head_preds.begin(); func_decl_vector::const_iterator hpend = head_preds.end(); reg_idx void_reg = execution_context::void_register; for(; hpit!=hpend; ++hpit) { func_decl * head_pred = *hpit; const rule_vector & pred_rules = m_rule_set.get_predicate_rules(head_pred); rule_vector::const_iterator rit = pred_rules.begin(); rule_vector::const_iterator rend = pred_rules.end(); unsigned stratum = m_rule_set.get_predicate_strat(head_pred); for(; rit != rend; ++rit) { rule * r = *rit; SASSERT(head_pred==r->get_decl()); for (unsigned i = 0; i < r->get_uninterpreted_tail_size(); ++i) { unsigned stratum1 = m_rule_set.get_predicate_strat(r->get_decl(i)); if (stratum1 >= stratum) { goto next_loop; } } compile_rule_evaluation(r, input_deltas, void_reg, false, acc); next_loop: ; } reg_idx d_head_reg; if (output_deltas.find(head_pred, d_head_reg)) { acc.push_back(instruction::mk_clone(m_pred_regs.find(head_pred), d_head_reg)); } } } void compiler::make_inloop_delta_transition(const pred2idx & global_head_deltas, const pred2idx & global_tail_deltas, const pred2idx & local_deltas, instruction_block & acc) { //move global head deltas into tail ones pred2idx::iterator gdit = global_head_deltas.begin(); pred2idx::iterator gend = global_head_deltas.end(); for(; gdit!=gend; ++gdit) { func_decl * pred = gdit->m_key; reg_idx head_reg = gdit->m_value; reg_idx tail_reg = global_tail_deltas.find(pred); acc.push_back(instruction::mk_move(head_reg, tail_reg)); } //empty local deltas pred2idx::iterator lit = local_deltas.begin(); pred2idx::iterator lend = local_deltas.end(); for(; lit!=lend; ++lit) { acc.push_back(instruction::mk_dealloc(lit->m_value)); } } void compiler::compile_loop(const func_decl_vector & head_preds, const func_decl_set & widened_preds, const pred2idx & global_head_deltas, const pred2idx & global_tail_deltas, const pred2idx & local_deltas, instruction_block & acc) { instruction_block * loop_body = alloc(instruction_block); loop_body->set_observer(&m_instruction_observer); pred2idx all_head_deltas(global_head_deltas); unite_disjoint_maps(all_head_deltas, local_deltas); pred2idx all_tail_deltas(global_tail_deltas); unite_disjoint_maps(all_tail_deltas, local_deltas); //generate code for the iterative fixpoint search //The order in which we iterate the preds_vector matters, since rules can depend on //deltas generated earlier in the same iteration. compile_preds(head_preds, widened_preds, &all_tail_deltas, all_head_deltas, *loop_body); svector loop_control_regs; //loop is controlled by global src regs collect_map_range(loop_control_regs, global_tail_deltas); //move target deltas into source deltas at the end of the loop //and clear local deltas make_inloop_delta_transition(global_head_deltas, global_tail_deltas, local_deltas, *loop_body); loop_body->set_observer(nullptr); acc.push_back(instruction::mk_while_loop(loop_control_regs.size(), loop_control_regs.c_ptr(), loop_body)); } void compiler::compile_dependent_rules(const func_decl_set & head_preds, const pred2idx * input_deltas, const pred2idx & output_deltas, bool add_saturation_marks, instruction_block & acc) { if (!output_deltas.empty()) { func_decl_set::iterator hpit = head_preds.begin(); func_decl_set::iterator hpend = head_preds.end(); for(; hpit!=hpend; ++hpit) { if(output_deltas.contains(*hpit)) { //we do not support retrieving deltas for rules that are inside a recursive //stratum, since we would have to maintain this 'global' delta through the loop //iterations NOT_IMPLEMENTED_YET(); } } } func_decl_vector preds_vector; func_decl_set global_deltas_dummy; detect_chains(head_preds, preds_vector, global_deltas_dummy); /* FIXME: right now we use all preds as global deltas for correctness purposes func_decl_set local_deltas(head_preds); set_difference(local_deltas, global_deltas); */ func_decl_set local_deltas; func_decl_set global_deltas(head_preds); pred2idx d_global_src; //these deltas serve as sources of tuples for rule evaluation inside the loop get_fresh_registers(global_deltas, d_global_src); pred2idx d_global_tgt; //these deltas are targets for new tuples in rule evaluation inside the loop get_fresh_registers(global_deltas, d_global_tgt); pred2idx d_local; get_fresh_registers(local_deltas, d_local); pred2idx d_all_src(d_global_src); //src together with local deltas unite_disjoint_maps(d_all_src, d_local); pred2idx d_all_tgt(d_global_tgt); //tgt together with local deltas unite_disjoint_maps(d_all_tgt, d_local); func_decl_set empty_func_decl_set; //generate code for the initial run // compile_preds(preds_vector, empty_func_decl_set, input_deltas, d_global_src, acc); compile_preds_init(preds_vector, empty_func_decl_set, input_deltas, d_global_src, acc); if (compile_with_widening()) { compile_loop(preds_vector, global_deltas, d_global_tgt, d_global_src, d_local, acc); } else { compile_loop(preds_vector, empty_func_decl_set, d_global_tgt, d_global_src, d_local, acc); } if(add_saturation_marks) { //after the loop finishes, all predicates in the group are saturated, //so we may mark them as such func_decl_set::iterator fdit = head_preds.begin(); func_decl_set::iterator fdend = head_preds.end(); for(; fdit!=fdend; ++fdit) { acc.push_back(instruction::mk_mark_saturated(m_context.get_manager(), *fdit)); } } } bool compiler::is_nonrecursive_stratum(const func_decl_set & preds) const { SASSERT(preds.size()>0); if(preds.size()>1) { return false; } func_decl * head_pred = *preds.begin(); const rule_vector & rules = m_rule_set.get_predicate_rules(head_pred); rule_vector::const_iterator it = rules.begin(); rule_vector::const_iterator end = rules.end(); for(; it!=end; ++it) { //it is sufficient to check just for presence of the first head predicate, //since if the rules are recursive and their heads are strongly connected by dependence, //this predicate must appear in some tail if((*it)->is_in_tail(head_pred)) { return false; } } return true; } void compiler::compile_nonrecursive_stratum(const func_decl_set & preds, const pred2idx * input_deltas, const pred2idx & output_deltas, bool add_saturation_marks, instruction_block & acc) { //non-recursive stratum always has just one head predicate SASSERT(preds.size()==1); SASSERT(is_nonrecursive_stratum(preds)); func_decl * head_pred = *preds.begin(); const rule_vector & rules = m_rule_set.get_predicate_rules(head_pred); reg_idx output_delta; if (!output_deltas.find(head_pred, output_delta)) { output_delta = execution_context::void_register; } rule_vector::const_iterator it = rules.begin(); rule_vector::const_iterator end = rules.end(); for (; it != end; ++it) { rule * r = *it; SASSERT(r->get_decl()==head_pred); compile_rule_evaluation(r, input_deltas, output_delta, false, acc); } if (add_saturation_marks) { //now the predicate is saturated, so we may mark it as such acc.push_back(instruction::mk_mark_saturated(m_context.get_manager(), head_pred)); } } bool compiler::all_saturated(const func_decl_set & preds) const { func_decl_set::iterator fdit = preds.begin(); func_decl_set::iterator fdend = preds.end(); for(; fdit!=fdend; ++fdit) { if(!m_context.get_rel_context()->get_rmanager().is_saturated(*fdit)) { return false; } } return true; } void compiler::compile_strats(const rule_stratifier & stratifier, const pred2idx * input_deltas, const pred2idx & output_deltas, bool add_saturation_marks, instruction_block & acc) { rule_set::pred_set_vector strats = stratifier.get_strats(); rule_set::pred_set_vector::const_iterator sit = strats.begin(); rule_set::pred_set_vector::const_iterator send = strats.end(); for(; sit!=send; ++sit) { func_decl_set & strat_preds = **sit; if (all_saturated(strat_preds)) { //all predicates in stratum are saturated, so no need to compile rules for them continue; } TRACE("dl", tout << "Stratum: "; func_decl_set::iterator pit = strat_preds.begin(); func_decl_set::iterator pend = strat_preds.end(); for(; pit!=pend; ++pit) { func_decl * pred = *pit; tout << pred->get_name() << " "; } tout << "\n"; ); if (is_nonrecursive_stratum(strat_preds)) { //this stratum contains just a single non-recursive rule compile_nonrecursive_stratum(strat_preds, input_deltas, output_deltas, add_saturation_marks, acc); } else { compile_dependent_rules(strat_preds, input_deltas, output_deltas, add_saturation_marks, acc); } } } void compiler::do_compilation(instruction_block & execution_code, instruction_block & termination_code) { unsigned rule_cnt=m_rule_set.get_num_rules(); if(rule_cnt==0) { return; } instruction_block & acc = execution_code; acc.set_observer(&m_instruction_observer); //load predicate data for(unsigned i=0;iget_decl(), acc); unsigned rule_len = r->get_uninterpreted_tail_size(); for(unsigned j=0;jget_tail(j)->get_decl(), acc); } } pred2idx empty_pred2idx_map; compile_strats(m_rule_set.get_stratifier(), static_cast(nullptr), empty_pred2idx_map, true, execution_code); //store predicate data pred2idx::iterator pit = m_pred_regs.begin(); pred2idx::iterator pend = m_pred_regs.end(); for(; pit!=pend; ++pit) { pred2idx::key_data & e = *pit; func_decl * pred = e.m_key; reg_idx reg = e.m_value; termination_code.push_back(instruction::mk_store(m_context.get_manager(), pred, reg)); } acc.set_observer(nullptr); TRACE("dl", execution_code.display(execution_context(m_context), tout);); } } z3-z3-4.8.7/src/muz/rel/dl_compiler.h000066400000000000000000000304321356505360400172570ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_compiler.h Abstract: Author: Krystof Hoder (t-khoder) 2010-09-14. Revision History: --*/ #ifndef DL_COMPILER_H_ #define DL_COMPILER_H_ #include #include #include #include "ast/ast.h" #include "util/hashtable.h" #include "util/map.h" #include "util/obj_pair_hashtable.h" #include "util/ref_vector.h" #include "util/vector.h" #include "muz/rel/dl_base.h" #include "muz/base/dl_context.h" #include "muz/rel/dl_instruction.h" namespace datalog { class compiler { typedef instruction::reg_idx reg_idx; typedef hashtable int_set; typedef u_map int2int; typedef u_map int2ints; typedef obj_map pred2idx; typedef unsigned_vector var_vector; typedef ptr_vector func_decl_vector; enum assembling_column_kind { ACK_BOUND_VAR, ACK_UNBOUND_VAR, ACK_CONSTANT }; /** \brief instruction for assembling head relation from a joint relation built from rule evaluation. ACK_BOUND_VAR(source_column) - encodes that the column contains a variable bound in the body. ACK_UNBOUND_VAR(var_index) - encodes that the column contains a variable that is unbound (by the corresponding rule body), var_index is the de-Bruijn index (var->get_idx()) of the variable associated with the column. ACK_CONSTANT(constant) - encodes that the column contains the constant. Examples: P(x) :- Q(x,y), Q(y,z) The variables in the body relation are [x, y, y, z] indexed as 0, 1, 2, 3. The variable x gets the instruction ACK_BOUND_VAR(0) P(u,x) :- Q(x,y), Q(y,z) The variable u gets the instruction ACK_UNBOUND_VAR(#0) P(1, x) :- Q(x,y), Q(y,z) The instruction for column 0 is ACK_CONSTANT(1) */ struct assembling_column_info { relation_sort domain; // domain of the column assembling_column_kind kind; // "instruction" tag union { unsigned source_column; // for ACK_BOUND_VAR unsigned var_index; // for ACK_UNBOUND_VAR relation_element constant; // for ACK_CONSTANT }; }; class instruction_observer : public instruction_block::instruction_observer { compiler & m_parent; rule * m_current; public: instruction_observer(compiler & parent) : m_parent(parent), m_current(nullptr) {} void start_rule(rule * r) { SASSERT(!m_current); m_current=r; } void finish_rule() { m_current = nullptr; } void notify(instruction * i) override { if(m_current) { i->set_accounting_parent_object(m_parent.m_context, m_current); } } }; context & m_context; rule_set const & m_rule_set; /** Invariant: the \c m_top_level_code never contains the loop that is being constructed, so instruction that need to be executed before the loop can be pushed into it. */ instruction_block & m_top_level_code; pred2idx m_pred_regs; vector m_reg_signatures; obj_pair_map m_constant_registers; obj_pair_map m_total_registers; obj_map m_empty_tables_registers; instruction_observer m_instruction_observer; expr_free_vars m_free_vars; /** If true, the union operation on the underlying structure only provides the information whether the updated relation has changed or not. In this case we do not get anything from using delta relations at position of input relations in the saturation loop, so we would not do it. */ bool all_or_nothing_deltas() const { return m_context.all_or_nothing_deltas(); } /** If true, we compile the saturation loops in a way that allows us to use widening. */ bool compile_with_widening() const { return m_context.compile_with_widening(); } reg_idx get_fresh_register(const relation_signature & sig); reg_idx get_register(const relation_signature & sig, bool reuse, reg_idx r); reg_idx get_single_column_register(const relation_sort s); /** \brief Allocate registers for predicates in \c pred and add them into the \c regs map. \c regs must not already contain any predicate from \c preds. */ void get_fresh_registers(const func_decl_set & preds, pred2idx & regs); 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, 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, const unsigned_vector & removed_cols, reg_idx & result, bool reuse, instruction_block & acc); void make_select_equal_and_project(reg_idx src, const relation_element val, unsigned col, reg_idx & result, bool reuse, instruction_block & acc); /** \brief Create add an union or widen operation and put it into \c acc. */ void make_union(reg_idx src, reg_idx tgt, reg_idx delta, bool widening, instruction_block & acc); void make_projection(reg_idx src, unsigned col_cnt, const unsigned * removed_cols, reg_idx & result, bool reuse, instruction_block & acc); void make_rename(reg_idx src, unsigned cycle_len, const unsigned * permutation_cycle, reg_idx & result, bool reuse, instruction_block & acc); void make_clone(reg_idx src, reg_idx & result, instruction_block & acc); /** \brief Into \c acc add code that will assemble columns of a relation according to description in \c acis0. The source for bound variables is the table in register \c src. If \c src is \c execution_context::void_register, it is assumed to be a full relation with empty signature. */ void make_assembling_code(rule* compiled_rule, func_decl* head_pred, reg_idx src, const svector & acis0, reg_idx & result, bool & dealloc, instruction_block & acc); void make_dealloc_non_void(reg_idx r, instruction_block & acc); void make_add_constant_column(func_decl* pred, reg_idx src, const relation_sort s, const relation_element val, reg_idx & result, bool & dealloc, instruction_block & acc); void make_add_unbound_column(rule* compiled_rule, unsigned col_idx, func_decl* pred, reg_idx src, const relation_sort& s, reg_idx & result, bool & dealloc, instruction_block & acc); void make_full_relation(func_decl* pred, const relation_signature & sig, reg_idx & result, instruction_block & acc); void add_unbound_columns_for_negation(rule* compiled_rule, func_decl* pred, reg_idx& single_res, expr_ref_vector& single_res_expr, bool & dealloc, instruction_block& acc); void make_duplicate_column(reg_idx src, unsigned col, reg_idx & result, bool reuse, instruction_block & acc); void ensure_predicate_loaded(func_decl * pred, instruction_block & acc); /** \brief For rule \c r with two positive uninterpreted predicates put into \c res indexes of local variables in a table that results from join of the two positive predicates. Used to get input for the "project" part of join-project. */ void get_local_indexes_for_projection(rule * r, unsigned_vector & res); void get_local_indexes_for_projection(app * t, var_counter & globals, unsigned ofs, unsigned_vector & res); /** \brief Into \c acc add instructions that will add new facts following from the rule into \c head_reg, and add the facts that were not in \c head_reg before into \c delta_reg. */ void compile_rule_evaluation_run(rule * r, reg_idx head_reg, const reg_idx * tail_regs, reg_idx delta_reg, bool use_widening, instruction_block & acc); void compile_rule_evaluation(rule * r, const pred2idx * input_deltas, reg_idx output_delta, bool use_widening, instruction_block & acc); /** \brief Generate code to evaluate rules corresponding to predicates in \c head_preds. The rules are evaluated in the order their heads appear in the \c head_preds vector. */ void compile_preds(const func_decl_vector & head_preds, const func_decl_set & widened_preds, const pred2idx * input_deltas, const pred2idx & output_deltas, instruction_block & acc); /** \brief Generate code to evaluate predicates in a stratum based on their non-recursive rules. */ void compile_preds_init(const func_decl_vector & head_preds, const func_decl_set & widened_preds, const pred2idx * input_deltas, const pred2idx & output_deltas, instruction_block & acc); void make_inloop_delta_transition(const pred2idx & global_head_deltas, const pred2idx & global_tail_deltas, const pred2idx & local_deltas, instruction_block & acc); void compile_loop(const func_decl_vector & head_preds, const func_decl_set & widened_preds, const pred2idx & global_head_deltas, const pred2idx & global_tail_deltas, const pred2idx & local_deltas, instruction_block & acc); void compile_dependent_rules(const func_decl_set & head_preds, const pred2idx * input_deltas, const pred2idx & output_deltas, bool add_saturation_marks, instruction_block & acc); void detect_chains(const func_decl_set & preds, func_decl_vector & ordered_preds, func_decl_set & global_deltas); /** Return true if there is no dependency inside the \c rules stratum. The head predicates in stratum must be strongly connected by dependency. */ bool is_nonrecursive_stratum(const func_decl_set & preds) const; /** input_deltas==0 --> we use the actual content of relations instead of deltas */ void compile_nonrecursive_stratum(const func_decl_set & preds, const pred2idx * input_deltas, const pred2idx & output_deltas, bool add_saturation_marks, instruction_block & acc); void compile_strats(const rule_stratifier & stratifier, const pred2idx * input_deltas, const pred2idx & output_deltas, bool add_saturation_marks, instruction_block & acc); bool all_saturated(const func_decl_set & preds) const; void reset(); explicit compiler(context & ctx, rule_set const & rules, instruction_block & top_level_code) : m_context(ctx), m_rule_set(rules), m_top_level_code(top_level_code), m_instruction_observer(*this) {} /** \brief Compile \c rules in to pseudocode. Instructions to load data and perform computations put into \c execution_code */ void do_compilation(instruction_block & execution_code, instruction_block & termination_code); public: static void compile(context & ctx, rule_set const & rules, instruction_block & execution_code, instruction_block & termination_code) { compiler(ctx, rules, execution_code) .do_compilation(execution_code, termination_code); } }; }; #endif /* DL_COMPILER_H_ */ z3-z3-4.8.7/src/muz/rel/dl_external_relation.cpp000066400000000000000000000443121356505360400215210ustar00rootroot00000000000000/*++ Copyright (c) 2010 Microsoft Corporation Module Name: dl_external_relation.cpp Abstract: Author: Nikolaj Bjorner (nbjorner) 2010-05-10 Revision History: --*/ #include "util/debug.h" #include "ast/ast_pp.h" #include "muz/base/dl_context.h" #include "muz/rel/dl_external_relation.h" #include "ast/dl_decl_plugin.h" namespace datalog { external_relation::external_relation(external_relation_plugin & p, const relation_signature & s, expr* r) : relation_base(p, s), m_rel(r, p.get_ast_manager()), m_select_fn(p.get_ast_manager()), m_store_fn(p.get_ast_manager()), m_is_empty_fn(p.get_ast_manager()) { } external_relation::~external_relation() { } void external_relation::mk_accessor(decl_kind k, func_decl_ref& fn, const relation_fact& f, bool destructive, expr_ref& res) const { ast_manager& m = m_rel.get_manager(); family_id fid = get_plugin().get_family_id(); ptr_vector args; args.push_back(m_rel); for (unsigned i = 0; i < f.size(); ++i) { args.push_back(f[i]); } if (!fn.get()) { fn = m.mk_func_decl(fid, k, 0, nullptr, args.size(), args.c_ptr()); } if (destructive) { get_plugin().reduce_assign(fn, args.size(), args.c_ptr(), 1, args.c_ptr()); res = m_rel; } else { get_plugin().reduce(fn, args.size(), args.c_ptr(), res); } } bool external_relation::empty() const { ast_manager& m = m_rel.get_manager(); expr* r = m_rel.get(); expr_ref res(m); if (!m_is_empty_fn.get()) { family_id fid = get_plugin().get_family_id(); const_cast(m_is_empty_fn) = m.mk_func_decl(fid, OP_RA_IS_EMPTY, 0, nullptr, 1, &r); } get_plugin().reduce(m_is_empty_fn, 1, &r, res); return m.is_true(res); } void external_relation::add_fact(const relation_fact & f) { mk_accessor(OP_RA_STORE, m_store_fn, f, true, m_rel); } bool external_relation::contains_fact(const relation_fact & f) const { ast_manager& m = get_plugin().get_ast_manager(); expr_ref res(m); mk_accessor(OP_RA_SELECT, const_cast(m_select_fn), f, false, res); return !m.is_false(res); } external_relation * external_relation::clone() const { ast_manager& m = m_rel.get_manager(); family_id fid = get_plugin().get_family_id(); expr* rel = m_rel.get(); expr_ref res(m.mk_fresh_const("T", m.get_sort(rel)), m); expr* rel_out = res.get(); func_decl_ref fn(m.mk_func_decl(fid, OP_RA_CLONE,0,nullptr, 1, &rel), m); get_plugin().reduce_assign(fn, 1, &rel, 1, &rel_out); return alloc(external_relation, get_plugin(), get_signature(), res); } external_relation * external_relation::complement(func_decl* p) const { ast_manager& m = m_rel.get_manager(); family_id fid = get_plugin().get_family_id(); expr_ref res(m); expr* rel = m_rel; func_decl_ref fn(m.mk_func_decl(fid, OP_RA_COMPLEMENT,0,nullptr, 1, &rel), m); get_plugin().reduce(fn, 1, &rel, res); return alloc(external_relation, get_plugin(), get_signature(), res); } void external_relation::display(std::ostream & out) const { out << mk_pp(m_rel, m_rel.get_manager()) << "\n"; } void external_relation::display_tuples(func_decl & pred, std::ostream & out) const { display(out); } external_relation_plugin & external_relation::get_plugin() const { return static_cast(relation_base::get_plugin()); } // ----------------------------------- // // external_relation_plugin // // ----------------------------------- external_relation_plugin::external_relation_plugin(external_relation_context& ctx, relation_manager & m) : relation_plugin(external_relation_plugin::get_name(), m), m_ext(ctx) {} external_relation const & external_relation_plugin::get(relation_base const& r) { return dynamic_cast(r); } external_relation & external_relation_plugin::get(relation_base & r) { return dynamic_cast(r); } relation_base * external_relation_plugin::mk_empty(const relation_signature & s) { ast_manager& m = get_ast_manager(); sort* r_sort = get_relation_sort(s); parameter param(r_sort); family_id fid = get_family_id(); expr_ref e(m.mk_fresh_const("T", r_sort), m); expr* args[1] = { e.get() }; func_decl_ref empty_decl(m.mk_func_decl(fid, OP_RA_EMPTY, 1, ¶m, 0, (sort*const*)nullptr), m); reduce_assign(empty_decl, 0, nullptr, 1, args); return alloc(external_relation, *this, s, e); } sort* external_relation_plugin::get_relation_sort(relation_signature const& sig) { vector sorts; ast_manager& m = get_ast_manager(); family_id fid = get_family_id(); for (unsigned i = 0; i < sig.size(); ++i) { sorts.push_back(parameter(sig[i])); } return m.mk_sort(fid, DL_RELATION_SORT, sorts.size(), sorts.c_ptr()); } sort* external_relation_plugin::get_column_sort(unsigned col, sort* s) { SASSERT(s->get_num_parameters() > col); SASSERT(s->get_parameter(col).is_ast()); SASSERT(is_sort(s->get_parameter(col).get_ast())); return to_sort(s->get_parameter(col).get_ast()); } family_id external_relation_plugin::get_family_id() { return m_ext.get_family_id(); } void external_relation_plugin::mk_filter_fn(sort* s, app* condition, func_decl_ref& f) { ast_manager& m = get_ast_manager(); family_id fid = get_family_id(); parameter param(condition); f = m.mk_func_decl(fid, OP_RA_FILTER, 1, ¶m, 1, &s); } class external_relation_plugin::join_fn : public convenient_relation_join_fn { external_relation_plugin& m_plugin; func_decl_ref m_join_fn; expr* m_args[2]; public: join_fn(external_relation_plugin& p, const relation_signature & o1_sig, const relation_signature & o2_sig, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) : convenient_relation_join_fn(o1_sig, o2_sig, col_cnt, cols1, cols2), m_plugin(p), m_join_fn(p.get_ast_manager()) { ast_manager& m = p.get_ast_manager(); family_id fid = p.get_family_id(); vector params; for (unsigned i = 0; i < col_cnt; ++i) { params.push_back(parameter(cols1[i])); params.push_back(parameter(cols2[i])); } sort* domain[2] = { p.get_relation_sort(o1_sig), p.get_relation_sort(o2_sig) }; m_join_fn = m.mk_func_decl(fid, OP_RA_JOIN, params.size(), params.c_ptr(), 2, domain); } relation_base * operator()(const relation_base & r1, const relation_base & r2) override { expr_ref res(m_plugin.get_ast_manager()); m_args[0] = get(r1).get_relation(); m_args[1] = get(r2).get_relation(); m_plugin.reduce(m_join_fn, 2, m_args, res); return alloc(external_relation, m_plugin, get_result_signature(), res); } }; relation_join_fn * external_relation_plugin::mk_join_fn(const relation_base & r1, const relation_base & r2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { if (!check_kind(r1) || !check_kind(r2)) { return nullptr; } return alloc(join_fn, *this, r1.get_signature(), r2.get_signature() , col_cnt, cols1, cols2); } class external_relation_plugin::project_fn : public convenient_relation_project_fn { external_relation_plugin& m_plugin; func_decl_ref m_project_fn; public: project_fn(external_relation_plugin& p, sort* relation_sort, const relation_signature & orig_sig, unsigned removed_col_cnt, const unsigned * removed_cols) : convenient_relation_project_fn(orig_sig, removed_col_cnt, removed_cols), m_plugin(p), m_project_fn(p.get_ast_manager()) { vector params; ast_manager& m = p.get_ast_manager(); family_id fid = p.get_family_id(); for (unsigned i = 0; i < removed_col_cnt; ++i) { params.push_back(parameter(removed_cols[i])); } m_project_fn = m.mk_func_decl(fid, OP_RA_PROJECT, params.size(), params.c_ptr(), 1, &relation_sort); } relation_base * operator()(const relation_base & r) override { expr_ref res(m_plugin.get_ast_manager()); expr* rel = get(r).get_relation(); m_plugin.reduce(m_project_fn, 1, &rel, res); return alloc(external_relation, m_plugin, get_result_signature(), to_app(res)); } }; relation_transformer_fn * external_relation_plugin::mk_project_fn(const relation_base & r, unsigned col_cnt, const unsigned * removed_cols) { return alloc(project_fn, *this, get(r).get_sort(), r.get_signature(), col_cnt, removed_cols); } class external_relation_plugin::rename_fn : public convenient_relation_rename_fn { external_relation_plugin& m_plugin; func_decl_ref m_rename_fn; expr* m_args[2]; public: rename_fn(external_relation_plugin& p, sort* relation_sort, const relation_signature & orig_sig, unsigned cycle_len, const unsigned * cycle) : convenient_relation_rename_fn(orig_sig, cycle_len, cycle), m_plugin(p), m_rename_fn(p.get_ast_manager()) { ast_manager& m = p.get_ast_manager(); family_id fid = p.get_family_id(); vector params; for (unsigned i = 0; i < cycle_len; ++i) { SASSERT(cycle[i] < orig_sig.size()); params.push_back(parameter(cycle[i])); } m_rename_fn = m.mk_func_decl(fid, OP_RA_RENAME, params.size(), params.c_ptr(), 1, &relation_sort); } relation_base * operator()(const relation_base & r) override { expr* rel = get(r).get_relation(); expr_ref res(m_plugin.get_ast_manager()); m_args[0] = rel; m_plugin.reduce(m_rename_fn, 1, &rel, res); return alloc(external_relation, m_plugin, get_result_signature(), res); } }; relation_transformer_fn * external_relation_plugin::mk_rename_fn(const relation_base & r, unsigned cycle_len, const unsigned * permutation_cycle) { if(!check_kind(r)) { return nullptr; } return alloc(rename_fn, *this, get(r).get_sort(), r.get_signature(), cycle_len, permutation_cycle); } class external_relation_plugin::union_fn : public relation_union_fn { external_relation_plugin& m_plugin; func_decl_ref m_union_fn; expr* m_args[2]; expr* m_outs[2]; public: union_fn(external_relation_plugin& p, decl_kind k, sort* relation_sort): m_plugin(p), m_union_fn(p.get_ast_manager()) { ast_manager& m = p.get_ast_manager(); sort* domain[2] = { relation_sort, relation_sort }; m_union_fn = m.mk_func_decl(p.get_family_id(), k, 0, nullptr, 2, domain); } void operator()(relation_base & r, const relation_base & src, relation_base * delta) override { ast_manager& m = m_plugin.get_ast_manager(); expr_ref_vector res(m); m_args[0] = get(r).get_relation(); m_args[1] = get(src).get_relation(); m_outs[0] = m_args[0]; unsigned num_out = 1; if (delta) { m_outs[1] = get(*delta).get_relation(); ++num_out; } m_plugin.reduce_assign(m_union_fn, 2, m_args, num_out, m_outs); } }; relation_union_fn * external_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta) { if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) { return nullptr; } return alloc(union_fn, *this, OP_RA_UNION, get(src).get_sort()); } relation_union_fn * external_relation_plugin::mk_widen_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta) { if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) { return nullptr; } return alloc(union_fn, *this, OP_RA_WIDEN, get(src).get_sort()); } class external_relation_plugin::filter_interpreted_fn : public relation_mutator_fn { external_relation_plugin& m_plugin; app_ref m_condition; func_decl_ref m_filter_fn; public: filter_interpreted_fn(external_relation_plugin& p, sort* relation_sort, app * condition) : m_plugin(p), m_condition(condition, p.get_ast_manager()), m_filter_fn(p.get_ast_manager()) { p.mk_filter_fn(relation_sort, condition, m_filter_fn); SASSERT(p.get_ast_manager().is_bool(condition)); } void operator()(relation_base & r) override { SASSERT(m_plugin.check_kind(r)); expr* arg = get(r).get_relation(); m_plugin.reduce_assign(m_filter_fn, 1, &arg, 1, &arg); } }; relation_mutator_fn * external_relation_plugin::mk_filter_interpreted_fn(const relation_base & r, app * condition) { if(!check_kind(r)) { return nullptr; } return alloc(filter_interpreted_fn, *this, get(r).get_sort(), condition); } relation_mutator_fn * external_relation_plugin::mk_filter_equal_fn(const relation_base & r, const relation_element & value, unsigned col) { if(!check_kind(r)) { return nullptr; } ast_manager& m = get_ast_manager(); app_ref condition(m); expr_ref var(m); sort* relation_sort = get(r).get_sort(); sort* column_sort = get_column_sort(col, relation_sort); var = m.mk_var(col, column_sort); condition = m.mk_eq(var, value); return mk_filter_interpreted_fn(r, condition); } class external_relation_plugin::filter_identical_fn : public relation_mutator_fn { external_relation_plugin& m_plugin; func_decl_ref_vector m_filter_fn; public: filter_identical_fn(external_relation_plugin& p, sort* relation_sort, unsigned col_cnt, const unsigned * identical_cols) : m_plugin(p), m_filter_fn(p.get_ast_manager()) { ast_manager& m = p.get_ast_manager(); func_decl_ref fn(m); app_ref eq(m); if (col_cnt <= 1) { return; } unsigned col = identical_cols[0]; sort* s = p.get_column_sort(col, relation_sort); var* v0 = m.mk_var(col, s); for (unsigned i = 1; i < col_cnt; ++i) { col = identical_cols[i]; s = p.get_column_sort(col, relation_sort); eq = m.mk_eq(v0, m.mk_var(col, s)); p.mk_filter_fn(relation_sort, eq.get(), fn); m_filter_fn.push_back(fn); } } void operator()(relation_base & r) override { expr* r0 = get(r).get_relation(); for (unsigned i = 0; i < m_filter_fn.size(); ++i) { m_plugin.reduce_assign(m_filter_fn[i].get(), 1, &r0, 1, &r0); } } }; relation_mutator_fn * external_relation_plugin::mk_filter_identical_fn(const relation_base & r, unsigned col_cnt, const unsigned * identical_cols) { if (!check_kind(r)) { return nullptr; } return alloc(filter_identical_fn, *this, get(r).get_sort(), col_cnt, identical_cols); } class external_relation_plugin::negation_filter_fn : public convenient_relation_negation_filter_fn { external_relation_plugin& m_plugin; func_decl_ref m_negated_filter_fn; expr* m_args[2]; public: negation_filter_fn(external_relation_plugin& p, const relation_base & tgt, const relation_base & neg_t, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) : convenient_negation_filter_fn(tgt, neg_t, joined_col_cnt, t_cols, negated_cols), m_plugin(p), m_negated_filter_fn(p.get_ast_manager()) { ast_manager& m = p.get_ast_manager(); family_id fid = p.get_family_id(); vector params; for (unsigned i = 0; i < joined_col_cnt; ++i) { params.push_back(parameter(t_cols[i])); params.push_back(parameter(negated_cols[i])); } sort* domain[2] = { get(tgt).get_sort(), get(neg_t).get_sort() }; m_negated_filter_fn = m.mk_func_decl(fid, OP_RA_NEGATION_FILTER, params.size(), params.c_ptr(), 2, domain); } void operator()(relation_base & t, const relation_base & negated_obj) override { m_args[0] = get(t).get_relation(); m_args[1] = get(negated_obj).get_relation(); m_plugin.reduce_assign(m_negated_filter_fn.get(), 2, m_args, 1, m_args); } }; relation_intersection_filter_fn * external_relation_plugin::mk_filter_by_negation_fn(const relation_base & t, const relation_base & negated_obj, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) { if (!check_kind(t) || !check_kind(negated_obj)) { return nullptr; } return alloc(negation_filter_fn, *this, t, negated_obj, joined_col_cnt, t_cols, negated_cols); } }; z3-z3-4.8.7/src/muz/rel/dl_external_relation.h000066400000000000000000000125671356505360400211750ustar00rootroot00000000000000/*++ Copyright (c) 2010 Microsoft Corporation Module Name: dl_external_relation.h Abstract: Author: Nikolaj Bjorner (nbjorner) 2010-05-10 Revision History: --*/ #ifndef DL_EXTERNAL_RELATION_H_ #define DL_EXTERNAL_RELATION_H_ #include "muz/rel/dl_base.h" namespace datalog { class external_relation; class external_relation_context { public: virtual ~external_relation_context() {} virtual family_id get_family_id() const = 0; // reduce arguments. virtual void reduce(func_decl* f, unsigned num_args, expr * const* args, expr_ref& result) = 0; // overwrite terms passed in outs vector with values computed by function. virtual void reduce_assign(func_decl* f, unsigned num_args, expr * const* args, unsigned num_out, expr* const* outs) = 0; }; class external_relation_plugin : public relation_plugin { friend class external_relation; class join_fn; class project_fn; class rename_fn; class union_fn; class filter_identical_fn; class filter_interpreted_fn; class negation_filter_fn; external_relation_context& m_ext; public: external_relation_plugin(external_relation_context& ctx, relation_manager & m); bool can_handle_signature(const relation_signature & s) override { return true; } static symbol get_name() { return symbol("external_relation"); } relation_base * mk_empty(const relation_signature & s) override; relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) override; relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, const unsigned * removed_cols) override; relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, const unsigned * permutation_cycle) override; relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta) override; relation_union_fn * mk_widen_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta) override; relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, const unsigned * identical_cols) override; relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, unsigned col) override; relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition) override; relation_intersection_filter_fn * mk_filter_by_negation_fn(const relation_base & t, const relation_base & negated_obj, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) override; private: static external_relation& get(relation_base& r); static external_relation const & get(relation_base const& r); void reduce(func_decl* f, unsigned num_args, expr * const* args, expr_ref& result) { m_ext.reduce(f, num_args, args, result); } void reduce_assign(func_decl* f, unsigned num_args, expr * const* args, unsigned num_out, expr* const* outs) { m_ext.reduce_assign(f, num_args, args, num_out, outs); } sort* get_relation_sort(relation_signature const& sig); sort* get_column_sort(unsigned col, sort* relation_sort); void mk_filter_fn(sort* s, app* condition, func_decl_ref& f); family_id get_family_id(); }; class external_relation : public relation_base { friend class external_relation_plugin; friend class external_relation_plugin::join_fn; friend class external_relation_plugin::project_fn; friend class external_relation_plugin::rename_fn; friend class external_relation_plugin::union_fn; friend class external_relation_plugin::filter_identical_fn; friend class external_relation_plugin::filter_interpreted_fn; friend class external_relation_plugin::negation_filter_fn; expr_ref m_rel; func_decl_ref m_select_fn; func_decl_ref m_store_fn; func_decl_ref m_is_empty_fn; unsigned size() const { return get_signature().size(); } sort* get_sort() const { return m_rel.get_manager().get_sort(m_rel); } void mk_accessor(decl_kind k, func_decl_ref& fn, const relation_fact& f, bool destructive, expr_ref& res) const; external_relation(external_relation_plugin & p, const relation_signature & s, expr* r); ~external_relation() override; public: external_relation_plugin & get_plugin() const; bool empty() const override; void add_fact(const relation_fact & f) override; bool contains_fact(const relation_fact & f) const override; external_relation * clone() const override; external_relation * complement(func_decl*) const override; void display(std::ostream & out) const override; void display_tuples(func_decl & pred, std::ostream & out) const override; expr* get_relation() const { return m_rel.get(); } void to_formula(expr_ref& fml) const override { fml = get_relation(); } }; }; #endif z3-z3-4.8.7/src/muz/rel/dl_finite_product_relation.cpp000066400000000000000000003034431356505360400227200ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_finite_product_relation.cpp Abstract: Author: Krystof Hoder (t-khoder) 2010-09-14. Revision History: --*/ #include #include "muz/base/dl_context.h" #include "muz/rel/dl_relation_manager.h" #include "muz/rel/dl_table_relation.h" #include "muz/rel/dl_finite_product_relation.h" #include "ast/rewriter/bool_rewriter.h" #include "ast/rewriter/th_rewriter.h" namespace datalog { //static variables const table_sort finite_product_relation::s_rel_idx_sort = INT_MAX; void universal_delete(finite_product_relation* ptr) { ptr->deallocate(); } // ----------------------------------- // // finite_product_relation_plugin // // ----------------------------------- finite_product_relation & finite_product_relation_plugin::get(relation_base & r) { SASSERT(r.get_plugin().is_finite_product_relation()); return static_cast(r); } const finite_product_relation & finite_product_relation_plugin::get(const relation_base & r) { SASSERT(r.get_plugin().is_finite_product_relation()); return static_cast(r); } finite_product_relation * finite_product_relation_plugin::get(relation_base * r) { SASSERT(!r || r->get_plugin().is_finite_product_relation()); return static_cast(r); } const finite_product_relation * finite_product_relation_plugin::get(const relation_base * r) { SASSERT(!r || r->get_plugin().is_finite_product_relation()); return static_cast(r); } symbol finite_product_relation_plugin::get_name(relation_plugin & inner_plugin) { std::string str = std::string("fpr_")+inner_plugin.get_name().bare_str(); return symbol(str.c_str()); } finite_product_relation_plugin & finite_product_relation_plugin::get_plugin(relation_manager & rmgr, relation_plugin & inner) { finite_product_relation_plugin * res; if(!rmgr.try_get_finite_product_relation_plugin(inner, res)) { res = alloc(finite_product_relation_plugin, inner, rmgr); rmgr.register_plugin(res); } return *res; } finite_product_relation_plugin::finite_product_relation_plugin(relation_plugin & inner_plugin, relation_manager & manager) : relation_plugin(get_name(inner_plugin), manager, ST_FINITE_PRODUCT_RELATION), m_inner_plugin(inner_plugin), m_spec_store(*this) { } void finite_product_relation_plugin::initialize(family_id fid) { relation_plugin::initialize(fid); m_spec_store.add_available_kind(get_kind()); } family_id finite_product_relation_plugin::get_relation_kind(finite_product_relation & r, const bool * table_columns) { const relation_signature & sig = r.get_signature(); svector table_cols_vect(sig.size(), table_columns); return m_spec_store.get_relation_kind(sig, rel_spec(table_cols_vect)); } void finite_product_relation_plugin::get_all_possible_table_columns(relation_manager & rmgr, const relation_signature & s, svector & table_columns) { SASSERT(table_columns.empty()); unsigned s_sz = s.size(); for(unsigned i=0; i table_columns; get_all_possible_table_columns(s, table_columns); #ifndef _EXTERNAL_RELEASE unsigned s_sz = s.size(); unsigned rel_col_cnt = 0; for(unsigned i=0; icomplement_self(p); return res; } bool finite_product_relation_plugin::can_convert_to_table_relation(const finite_product_relation & r) { return r.m_other_sig.empty(); } table_relation * finite_product_relation_plugin::to_table_relation(const finite_product_relation & r) { SASSERT(can_convert_to_table_relation(r)); r.garbage_collect(true); //now all rows in the table will correspond to rows in the resulting table_relation const table_base & t = r.get_table(); unsigned removed_col = t.get_signature().size()-1; scoped_ptr project_fun = get_manager().mk_project_fn(r.get_table(), 1, &removed_col); table_base * res_table = (*project_fun)(t); SASSERT(res_table->get_signature().functional_columns()==0); return static_cast(get_manager().mk_table_relation(r.get_signature(), res_table)); } bool finite_product_relation_plugin::can_be_converted(const relation_base & r) { if(&r.get_plugin()==&get_inner_plugin()) { //can be converted by mk_from_inner_relation return true; } if(r.from_table()) { //We can convert directly from table plugin only if the inner plugin can handle empty signatures. //TODO: If the inner plugin cannot handle empty signatures, we may try to move some of the //table columns into the inner relation signature. return get_inner_plugin().can_handle_signature(relation_signature()); } return false; } finite_product_relation * finite_product_relation_plugin::mk_from_table_relation(const table_relation & r) { func_decl* pred = nullptr; const relation_signature & sig = r.get_signature(); const table_base & t = r.get_table(); table_plugin & tplugin = r.get_table().get_plugin(); relation_signature inner_sig; //empty signature for the inner relation if(!get_inner_plugin().can_handle_signature(inner_sig)) { return nullptr; } table_signature idx_singleton_sig; idx_singleton_sig.push_back(finite_product_relation::s_rel_idx_sort); idx_singleton_sig.set_functional_columns(1); scoped_rel idx_singleton; if(tplugin.can_handle_signature(idx_singleton_sig)) { idx_singleton = tplugin.mk_empty(idx_singleton_sig); } else { idx_singleton = get_manager().mk_empty_table(idx_singleton_sig); } table_fact idx_singleton_fact; idx_singleton_fact.push_back(0); idx_singleton->add_fact(idx_singleton_fact); scoped_ptr join_fun = get_manager().mk_join_fn(t, *idx_singleton, 0, nullptr, nullptr); SASSERT(join_fun); scoped_rel res_table = (*join_fun)(t, *idx_singleton); svector table_cols(sig.size(), true); finite_product_relation * res = mk_empty(sig, table_cols.c_ptr()); //this one does not need to be deleted -- it will be taken over by \c res in the \c init function relation_base * inner_rel = get_inner_plugin().mk_full(pred, inner_sig, get_inner_plugin().get_kind()); relation_vector rels; rels.push_back(inner_rel); res->init(*res_table, rels, true); return res; } finite_product_relation * finite_product_relation_plugin::mk_from_inner_relation(const relation_base & r) { SASSERT(&r.get_plugin()==&get_inner_plugin()); const relation_signature & sig = r.get_signature(); table_signature idx_singleton_sig; idx_singleton_sig.push_back(finite_product_relation::s_rel_idx_sort); idx_singleton_sig.set_functional_columns(1); scoped_rel idx_singleton = get_manager().mk_empty_table(idx_singleton_sig); table_fact idx_singleton_fact; idx_singleton_fact.push_back(0); idx_singleton->add_fact(idx_singleton_fact); svector table_cols(sig.size(), false); finite_product_relation * res = mk_empty(sig, table_cols.c_ptr()); relation_vector rels; rels.push_back(r.clone()); res->init(*idx_singleton, rels, true); return res; } class finite_product_relation_plugin::converting_join_fn : public convenient_relation_join_fn { finite_product_relation_plugin & m_plugin; scoped_ptr m_native_join; finite_product_relation * convert(const relation_base & r) { SASSERT(&r.get_plugin()!=&m_plugin); if(&r.get_plugin()==&m_plugin.get_inner_plugin()) { return m_plugin.mk_from_inner_relation(r); } SASSERT(r.from_table()); const table_relation & tr = static_cast(r); finite_product_relation * res = m_plugin.mk_from_table_relation(tr); SASSERT(res); return res; } public: converting_join_fn(finite_product_relation_plugin & plugin, const relation_signature & sig1, const relation_signature & sig2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) : convenient_relation_join_fn(sig1, sig2, col_cnt, cols1, cols2), m_plugin(plugin) {} relation_base * operator()(const relation_base & r1, const relation_base & r2) override { scoped_rel r1_conv; if(&r1.get_plugin()!=&m_plugin) { r1_conv = convert(r1); } scoped_rel r2_conv; if(&r2.get_plugin()!=&m_plugin) { r2_conv = convert(r2); } const finite_product_relation & fpr1 = r1_conv ? *r1_conv : get(r1); const finite_product_relation & fpr2 = r2_conv ? *r2_conv : get(r2); SASSERT(&fpr1.get_plugin()==&m_plugin); SASSERT(&fpr2.get_plugin()==&m_plugin); if(!m_native_join) { m_native_join = m_plugin.get_manager().mk_join_fn(fpr1, fpr2, m_cols1, m_cols2, false); } return (*m_native_join)(fpr1, fpr2); } }; class finite_product_relation_plugin::join_fn : public convenient_relation_join_fn { scoped_ptr m_tjoin_fn; scoped_ptr m_rjoin_fn; unsigned_vector m_t_joined_cols1; unsigned_vector m_t_joined_cols2; unsigned_vector m_r_joined_cols1; unsigned_vector m_r_joined_cols2; //Column equalities betweet table and inner relations. //The columns numbers correspont to the columns of the table/inner relation //in the result of the join operation unsigned_vector m_tr_table_joined_cols; unsigned_vector m_tr_rel_joined_cols; scoped_ptr m_filter_tr_identities; scoped_ptr m_tjoined_second_rel_remover; //determines which columns of the result are table columns and which are in the inner relation svector m_res_table_columns; public: class join_maker : public table_row_mutator_fn { join_fn & m_parent; const finite_product_relation & m_r1; const finite_product_relation & m_r2; relation_vector & m_rjoins; public: join_maker(join_fn & parent, const finite_product_relation & r1, const finite_product_relation & r2, relation_vector & rjoins) : m_parent(parent), m_r1(r1), m_r2(r2), m_rjoins(rjoins) {} bool operator()(table_element * func_columns) override { const relation_base & or1 = m_r1.get_inner_rel(func_columns[0]); const relation_base & or2 = m_r2.get_inner_rel(func_columns[1]); unsigned new_rel_num = m_rjoins.size(); m_rjoins.push_back(m_parent.do_rjoin(or1, or2)); func_columns[0]=new_rel_num; return true; } }; join_fn(const finite_product_relation & r1, const finite_product_relation & r2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) : convenient_relation_join_fn(r1.get_signature(), r2.get_signature(), col_cnt, cols1, cols2) { unsigned second_table_after_join_ofs = r1.m_table2sig.size(); unsigned second_inner_rel_after_join_ofs = r1.m_other2sig.size(); for(unsigned i=0;i tjoined = (*m_tjoin_fn)(r1.get_table(), r2.get_table()); relation_vector joined_orelations; { join_maker * mutator = alloc(join_maker, *this, r1, r2, joined_orelations); //dealocated in inner_join_mapper scoped_ptr inner_join_mapper = rmgr.mk_map_fn(*tjoined, mutator); (*inner_join_mapper)(*tjoined); } if(!m_tjoined_second_rel_remover) { unsigned removed_col = tjoined->get_signature().size()-1; m_tjoined_second_rel_remover = rmgr.mk_project_fn(*tjoined, 1, &removed_col); } //remove the second functional column from tjoined to get a table that corresponds //to the table signature of the resulting relation scoped_rel res_table = (*m_tjoined_second_rel_remover)(*tjoined); relation_plugin & res_oplugin = joined_orelations.empty() ? r1.m_other_plugin : joined_orelations.back()->get_plugin(); //TODO: Maybe we might want to specify a particular relation kind, instead of just null_family_id. //It would however need to be somehow inferred for the new signature. finite_product_relation * res = alloc(finite_product_relation, r1.get_plugin(), get_result_signature(), m_res_table_columns.c_ptr(), res_table->get_plugin(), res_oplugin, null_family_id); res->init(*res_table, joined_orelations, true); 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) { m_filter_tr_identities = plugin.mk_filter_identical_pairs(*res, m_tr_table_joined_cols.size(), m_tr_table_joined_cols.c_ptr(), m_tr_rel_joined_cols.c_ptr()); SASSERT(m_filter_tr_identities); } (*m_filter_tr_identities)(*res); } return res; } }; relation_join_fn * finite_product_relation_plugin::mk_join_fn(const relation_base & rb1, const relation_base & rb2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { if(!check_kind(rb1) || !check_kind(rb2)) { bool r1foreign = &rb1.get_plugin()!=this; bool r2foreign = &rb2.get_plugin()!=this; if( (!r1foreign || can_be_converted(rb1)) && (!r2foreign || can_be_converted(rb2))) { return alloc(converting_join_fn, *this, rb1.get_signature(), rb2.get_signature(), col_cnt, cols1, cols2); } return nullptr; } const finite_product_relation & r1 = get(rb1); const finite_product_relation & r2 = get(rb2); return alloc(join_fn, r1, r2, col_cnt, cols1, cols2); } class finite_product_relation_plugin::project_fn : public convenient_relation_project_fn { unsigned_vector m_removed_table_cols; unsigned_vector m_removed_rel_cols; scoped_ptr m_rel_projector; scoped_ptr m_inner_rel_union; //determines which columns of the result are table columns and which are in the inner relation svector m_res_table_columns; public: project_fn(const finite_product_relation & r, unsigned col_cnt, const unsigned * removed_cols) : convenient_relation_project_fn(r.get_signature(), col_cnt, removed_cols) { SASSERT(col_cnt>0); for(unsigned i=0; ii); m_res_table_columns.push_back(r.is_table_column(i)); } } class project_reducer : public table_row_pair_reduce_fn { project_fn & m_parent; relation_vector & m_relations; public: project_reducer(project_fn & parent, relation_vector & relations) : m_parent(parent), m_relations(relations) {} void operator()(table_element * func_columns, const table_element * merged_func_columns) override { relation_base * tgt = m_relations[static_cast(func_columns[0])]->clone(); relation_base & src = *m_relations[static_cast(merged_func_columns[0])]; if(!m_parent.m_inner_rel_union) { m_parent.m_inner_rel_union = tgt->get_manager().mk_union_fn(*tgt, src); } (*m_parent.m_inner_rel_union)(*tgt, src); unsigned new_idx = m_relations.size(); m_relations.push_back(tgt); func_columns[0] = new_idx; } }; relation_base * operator()(const relation_base & rb) override { const finite_product_relation & r = get(rb); finite_product_relation_plugin & plugin = r.get_plugin(); const table_base & rtable = r.get_table(); relation_manager & rmgr = plugin.get_manager(); r.garbage_collect(false); relation_vector res_relations; unsigned orig_rel_cnt = r.m_others.size(); for(unsigned i=0; iclone() : nullptr); } SASSERT(res_relations.size()==orig_rel_cnt); bool shared_res_table = false; const table_base * res_table; if(m_removed_table_cols.empty()) { shared_res_table = true; res_table = &rtable; } else { project_reducer * preducer = alloc(project_reducer, *this, res_relations); scoped_ptr tproject = rmgr.mk_project_with_reduce_fn(rtable, m_removed_table_cols.size(), m_removed_table_cols.c_ptr(), preducer); res_table = (*tproject)(rtable); } relation_plugin * res_oplugin = nullptr; if(!m_removed_rel_cols.empty()) { unsigned res_rel_cnt = res_relations.size(); for(unsigned i=0; ideallocate(); if(!res_oplugin) { res_oplugin = &res_relations[i]->get_plugin(); } } } if(!res_oplugin) { res_oplugin = &r.m_other_plugin; } //TODO: Maybe we might want to specify a particular relation kind, instead of just null_family_id. //It would however need to be somehow inferred for the new signature. finite_product_relation * res = alloc(finite_product_relation, r.get_plugin(), get_result_signature(), m_res_table_columns.c_ptr(), res_table->get_plugin(), *res_oplugin, null_family_id); res->init(*res_table, res_relations, false); if(!shared_res_table) { const_cast(res_table)->deallocate(); } return res; } }; relation_transformer_fn * finite_product_relation_plugin::mk_project_fn(const relation_base & rb, unsigned col_cnt, const unsigned * removed_cols) { if(&rb.get_plugin()!=this) { return nullptr; } return alloc(project_fn, get(rb), col_cnt, removed_cols); } class finite_product_relation_plugin::rename_fn : public convenient_relation_rename_fn { scoped_ptr m_table_renamer; scoped_ptr m_rel_renamer; bool m_rel_identity; unsigned_vector m_rel_permutation; //determines which columns of the result are table columns and which are in the inner relation svector m_res_table_columns; public: rename_fn(const finite_product_relation & r, unsigned cycle_len, const unsigned * permutation_cycle) : convenient_relation_rename_fn(r.get_signature(), cycle_len, permutation_cycle) { SASSERT(cycle_len>1); unsigned sig_sz = r.get_signature().size(); unsigned_vector permutation; add_sequence(0, sig_sz, permutation); permutate_by_cycle(permutation, cycle_len, permutation_cycle); unsigned_vector table_permutation; bool table_identity = true; m_rel_identity = true; for(unsigned new_i=0; new_iclone() : nullptr); } if(!m_rel_identity) { unsigned res_rel_cnt = res_relations.size(); for(unsigned i=0; i inner_rel = res_relations[i]; if(!m_rel_renamer) { m_rel_renamer = r.get_manager().mk_permutation_rename_fn(*inner_rel, m_rel_permutation); } res_relations[i] = (*m_rel_renamer)(*inner_rel); } } scoped_rel res_table_scoped; const table_base * res_table = &rtable; if(m_table_renamer) { res_table_scoped = (*m_table_renamer)(*res_table); res_table = res_table_scoped.get(); } //TODO: Maybe we might want to specify a particular relation kind, instead of just null_family_id. //It would however need to be somehow inferred for the new signature. finite_product_relation * res = alloc(finite_product_relation, r.get_plugin(), get_result_signature(), m_res_table_columns.c_ptr(), res_table->get_plugin(), r.m_other_plugin, null_family_id); res->init(*res_table, res_relations, false); return res; } }; relation_transformer_fn * finite_product_relation_plugin::mk_rename_fn(const relation_base & rb, unsigned permutation_cycle_len, const unsigned * permutation_cycle) { if(&rb.get_plugin()!=this) { return nullptr; } const finite_product_relation & r = get(rb); return alloc(rename_fn, r, permutation_cycle_len, permutation_cycle); } class finite_product_relation_plugin::union_fn : public relation_union_fn { bool m_use_delta; unsigned_vector m_data_cols;//non-functional columns in the product-relation table (useful for creating operations) scoped_ptr m_common_join; //result of the join contains (data columns), tgt_rel_idx, src_rel_idx scoped_ptr m_rel_union; scoped_ptr m_table_union; scoped_ptr m_remove_overlaps; scoped_ptr m_remove_src_column_from_overlap; //this one is populated only if we're doing union with delta scoped_ptr m_delta_merging_union; scoped_ptr m_overlap_delta_table_builder; public: union_fn(const finite_product_relation & tgt, bool use_delta) : m_use_delta(use_delta) {} relation_union_fn & get_inner_rel_union_op(relation_base & r) { if(!m_rel_union) { m_rel_union = r.get_manager().mk_union_fn(r, r, m_use_delta ? &r : nullptr); } return *m_rel_union; } class union_mapper : public table_row_mutator_fn { union_fn & m_parent; finite_product_relation & m_tgt; const finite_product_relation & m_src; table_base * m_delta_indexes; //table with signature (updated tgt rel index, delta_index in m_delta_rels) relation_vector * m_delta_rels; table_fact m_di_fact; //auxiliary fact for inserting into \c m_delta_indexes public: /** If \c delta_indexes is 0, it means we are not collecting indexes. */ union_mapper(union_fn & parent, finite_product_relation & tgt, const finite_product_relation & src, table_base * delta_indexes, relation_vector * delta_rels) : m_parent(parent), m_tgt(tgt), m_src(src), m_delta_indexes(delta_indexes), m_delta_rels(delta_rels) {} ~union_mapper() override {} bool operator()(table_element * func_columns) override { relation_base & otgt_orig = m_tgt.get_inner_rel(func_columns[0]); const relation_base & osrc = m_src.get_inner_rel(func_columns[1]); relation_base * otgt = otgt_orig.clone(); unsigned new_tgt_idx = m_tgt.get_next_rel_idx(); m_tgt.set_inner_rel(new_tgt_idx, otgt); if(m_delta_indexes) { relation_base * odelta = otgt->get_plugin().mk_empty(otgt->get_signature()); m_parent.get_inner_rel_union_op(*otgt)(*otgt, osrc, odelta); unsigned delta_idx = m_delta_rels->size(); m_delta_rels->push_back(odelta); m_di_fact.reset(); m_di_fact.push_back(new_tgt_idx); m_di_fact.push_back(delta_idx); m_delta_indexes->add_fact(m_di_fact); } else { m_parent.get_inner_rel_union_op(*otgt)(*otgt, osrc); } func_columns[0]=new_tgt_idx; return true; } }; /** Makes a table whose last column has indexes to relations in \c src into a table with indexes to relation \c tgt. */ class src_copying_mapper : public table_row_mutator_fn { finite_product_relation & m_tgt; const finite_product_relation & m_src; public: /** If \c delta_indexes is 0, it means we are not collecting indexes. */ src_copying_mapper(finite_product_relation & tgt, const finite_product_relation & src) : m_tgt(tgt), m_src(src) {} bool operator()(table_element * func_columns) override { const relation_base & osrc = m_src.get_inner_rel(func_columns[0]); unsigned new_tgt_idx = m_tgt.get_next_rel_idx(); m_tgt.set_inner_rel(new_tgt_idx, osrc.clone()); func_columns[0]=new_tgt_idx; return true; } }; void operator()(relation_base & tgtb, const relation_base & srcb, relation_base * deltab) override { finite_product_relation & tgt = get(tgtb); const finite_product_relation & src0 = get(srcb); finite_product_relation * delta = get(deltab); relation_manager & rmgr = tgt.get_manager(); scoped_rel src_aux_copy; //copy of src in case its specification needs to be modified if(!vectors_equal(tgt.m_table2sig, src0.m_table2sig) || (delta && !vectors_equal(tgt.m_table2sig, delta->m_table2sig)) ) { src_aux_copy = src0.clone(); ptr_vector orig_rels; orig_rels.push_back(src_aux_copy.get()); orig_rels.push_back(&tgt); if(delta) { orig_rels.push_back(delta); } if(!finite_product_relation::try_unify_specifications(orig_rels)) { throw default_exception("finite_product_relation union: cannot convert relations to common specification"); } } const finite_product_relation & src = src_aux_copy ? *src_aux_copy : src0; table_plugin & tplugin = tgt.get_table_plugin(); if(!m_common_join) { unsigned data_cols_cnt = tgt.m_table_sig.size()-1; for(unsigned i=0; i table_overlap = (*m_common_join)(tgt.get_table(), src.get_table()); scoped_rel delta_indexes; relation_vector delta_rels; if(m_use_delta) { table_signature di_sig; di_sig.push_back(finite_product_relation::s_rel_idx_sort); di_sig.push_back(finite_product_relation::s_rel_idx_sort); di_sig.set_functional_columns(1); delta_indexes = tplugin.mk_empty(di_sig); } { union_mapper * umapper = alloc(union_mapper, *this, tgt, src, delta_indexes.get(), &delta_rels); scoped_ptr mapping_fn = rmgr.mk_map_fn(*table_overlap, umapper); (*mapping_fn)(*table_overlap); } if(!m_remove_src_column_from_overlap) { unsigned removed_cols[] = { table_overlap->get_signature().size()-1 }; m_remove_src_column_from_overlap = rmgr.mk_project_fn(*table_overlap, 1, removed_cols); } //transform table_overlap into the signature of tgt.get_table(), so that the functional //column contains indexes of the united relations scoped_rel regular_overlap = (*m_remove_src_column_from_overlap)(*table_overlap); if(!m_remove_overlaps) { m_remove_overlaps = rmgr.mk_filter_by_negation_fn(tgt.get_table(), *regular_overlap, m_data_cols, m_data_cols); } //in tgt keep only the rows that are in tgt only (*m_remove_overlaps)(tgt.get_table(), *regular_overlap); //add rows in which tgt and src overlapped if(!m_table_union) { m_table_union = rmgr.mk_union_fn(tgt.get_table(), tgt.get_table()); } (*m_table_union)(tgt.get_table(), *regular_overlap); scoped_rel src_only = src.get_table().clone(); (*m_remove_overlaps)(*src_only, *regular_overlap); scoped_rel src_only2; //a copy of src_only for use in building the delta if(m_use_delta) { src_only2 = src_only->clone(); } { src_copying_mapper * cpmapper = alloc(src_copying_mapper, tgt, src); scoped_ptr mapping_fn = rmgr.mk_map_fn(*src_only, cpmapper); (*mapping_fn)(*src_only); } //add rows that were only in src (*m_table_union)(tgt.get_table(), *src_only); if(m_use_delta) { bool extending_delta = !delta->empty(); //current delta, we will add it to the deltab argument later if it was not given to us empty finite_product_relation * cdelta; if(extending_delta) { cdelta = delta->get_plugin().mk_empty(*delta); } else { cdelta = delta; } if(!m_overlap_delta_table_builder) { unsigned table_fn_col = regular_overlap->get_signature().size()-1; unsigned first_col = 0; unsigned removed_cols[] = { table_fn_col, table_fn_col+1 }; m_overlap_delta_table_builder = rmgr.mk_join_project_fn(*regular_overlap, *delta_indexes, 1, &table_fn_col, &first_col, 2, removed_cols); } scoped_rel overlap_delta_table = (*m_overlap_delta_table_builder)(*regular_overlap, *delta_indexes); cdelta->init(*overlap_delta_table, delta_rels, true); { src_copying_mapper * cpmapper = alloc(src_copying_mapper, *cdelta, src); scoped_ptr mapping_fn = rmgr.mk_map_fn(*src_only2, cpmapper); (*mapping_fn)(*src_only2); } //add rows that were only in src (*m_table_union)(cdelta->get_table(), *src_only2); if(extending_delta) { if(!m_delta_merging_union) { m_delta_merging_union = rmgr.mk_union_fn(*delta, *cdelta); } (*m_delta_merging_union)(*delta, *cdelta); cdelta->deallocate(); } } } }; #if 0 /** Union operation taking advantage of the fact that the inner relation of all the arguments is a singleton relation. */ class finite_product_relation_plugin::inner_singleton_union_fn : public relation_union_fn { class offset_row_mapper : public table_row_mutator_fn { public: unsigned m_ofs; virtual bool operator()(table_element * func_columns) { func_columns[0] += m_ofs; return true; } }; // [Leo]: gcc complained about the following class. // It does not have a constructor and uses a reference class inner_relation_copier : public table_row_mutator_fn { finite_product_relation & m_tgt; const finite_product_relation & m_src; finite_product_relation * m_delta; unsigned m_tgt_ofs; unsigned m_delta_ofs; public: virtual bool operator()(table_element * func_columns) { unsigned src_idx = static_cast(func_columns[0]); unsigned tgt_idx = src_idx + m_tgt_ofs; unsigned delta_idx = m_delta ? (src_idx + m_delta_ofs) : 0; SASSERT(!m_delta || m_tgt.m_others[tgt_idx]==m_delta->m_others[delta_idx]); SASSERT(m_tgt.m_others[tgt_idx]==0 || m_tgt.m_others[tgt_idx]==m_src.m_others[src_idx]); if(m_tgt.m_others[tgt_idx]==0) { m_tgt.m_others[tgt_idx] = m_src.m_others[src_idx]->clone(); if(m_delta) { m_delta->m_others[delta_idx] = m_src.m_others[src_idx]->clone(); } } return true; } }; scoped_ptr m_t_union_fun; offset_row_mapper * m_offset_mapper_obj; //initialized together with m_offset_mapper_fun, and deallocated by it scoped_ptr m_offset_mapper_fun; public: virtual void operator()(relation_base & tgtb, const relation_base & srcb, relation_base * deltab) { finite_product_relation & tgt = get(tgtb); const finite_product_relation & src = get(srcb); finite_product_relation * delta = get(deltab); finite_product_relation_plugin & plugin = tgt.get_plugin(); relation_manager & rmgr = plugin.get_manager(); //we want only non-empty inner relations to remain tgt.garbage_collect(true); src.garbage_collect(true); table_base & tgt_table = tgt.get_table(); const table_base & src_table = src.get_table(); scoped_rel offsetted_src = src_table.clone(); if(!m_offset_mapper_fun) { m_offset_mapper_obj = alloc(offset_row_mapper); m_offset_mapper_fun = rmgr.mk_map_fn(*offsetted_src, m_offset_mapper_obj); } unsigned src_rel_offset = tgt.m_others.size(); m_offset_mapper_obj->m_ofs = src_rel_offset; (*m_offset_mapper_fun)(*offsetted_src); //if we need to generate a delta, we get collect it into an empty relation and then union //it with the delta passed in as argument. scoped_rel loc_delta = delta ? get(plugin.mk_empty(*delta)) : 0; //even if we don't need to generate the delta for the caller, we still want to have a delta //table to know which relations to copy. scoped_rel loc_delta_table_scoped; if(!loc_delta) { loc_delta_table_scoped = tgt_table.get_plugin().mk_empty(tgt_table); } table_base * loc_delta_table = loc_delta ? &loc_delta->get_table() : loc_delta_table_scoped.get(); if(!m_t_union_fun) { m_t_union_fun = rmgr.mk_union_fn(tgt_table, *offsetted_src, loc_delta_table); } (*m_t_union_fun)(tgt_table, *offsetted_src, loc_delta_table); //TODO: copy the relations into tgt and (possibly) delta using inner_relation_copier //TODO: unite the local delta with the delta passed in as an argument NOT_IMPLEMENTED_YET(); } }; #endif class finite_product_relation_plugin::converting_union_fn : public relation_union_fn { scoped_ptr m_tr_union_fun; public: void operator()(relation_base & tgtb, const relation_base & srcb, relation_base * deltab) override { SASSERT(srcb.get_plugin().is_finite_product_relation()); const finite_product_relation & src = get(srcb); finite_product_relation_plugin & plugin = src.get_plugin(); scoped_rel tr_src = plugin.to_table_relation(src); if(!m_tr_union_fun) { m_tr_union_fun = plugin.get_manager().mk_union_fn(tgtb, *tr_src, deltab); SASSERT(m_tr_union_fun); } (*m_tr_union_fun)(tgtb, *tr_src, deltab); } }; relation_union_fn * finite_product_relation_plugin::mk_union_fn(const relation_base & tgtb, const relation_base & srcb, const relation_base * deltab) { if(&srcb.get_plugin()!=this) { return nullptr; } const finite_product_relation & src = get(srcb); if(&tgtb.get_plugin()!=this || (deltab && &deltab->get_plugin()!=this) ) { if(can_convert_to_table_relation(src)) { return alloc(converting_union_fn); } return nullptr; } const finite_product_relation * delta = get(deltab); return alloc(union_fn, get(tgtb), delta!=nullptr); } class finite_product_relation_plugin::filter_identical_fn : public relation_mutator_fn { //the table and relation columns that should be identical //the column numbering is local to the table or inner relation unsigned_vector m_table_cols; unsigned_vector m_rel_cols; scoped_ptr m_table_filter; scoped_ptr m_rel_filter; scoped_ptr m_tr_filter; public: filter_identical_fn(const finite_product_relation & r, unsigned col_cnt, const unsigned * identical_cols) : m_table_filter(nullptr), m_rel_filter(nullptr), m_tr_filter(nullptr) { finite_product_relation_plugin & plugin = r.get_plugin(); for(unsigned i=0; i1) { m_table_filter = r.get_manager().mk_filter_identical_fn(r.get_table(), m_table_cols.size(), m_table_cols.c_ptr()); SASSERT(m_table_filter); } if(!m_table_cols.empty() && !m_rel_cols.empty()) { unsigned tr_filter_table_cols[] = { m_table_cols[0] }; unsigned tr_filter_rel_cols[] = { m_rel_cols[0] }; m_tr_filter = plugin.mk_filter_identical_pairs(r, 1, tr_filter_table_cols, tr_filter_rel_cols); SASSERT(m_tr_filter); } } void ensure_rel_filter(const relation_base & orel) { SASSERT(m_rel_cols.size()>1); if(m_rel_filter) { return; } m_rel_filter = orel.get_manager().mk_filter_identical_fn(orel, m_rel_cols.size(), m_rel_cols.c_ptr()); SASSERT(m_rel_filter); } void operator()(relation_base & rb) override { finite_product_relation & r = get(rb); if(m_table_cols.size()>1) { (*m_table_filter)(r.get_table()); } if(m_rel_cols.size()>1) { r.garbage_collect(true); unsigned rel_cnt = r.m_others.size(); for(unsigned rel_idx=0; rel_idx m_table_filter; scoped_ptr m_rel_filter; unsigned m_col; app_ref m_value; public: filter_equal_fn(const finite_product_relation & r, const relation_element & value, unsigned col) : m_col(col), m_value(value, r.get_context().get_manager()) { if(r.is_table_column(col)) { table_element tval; r.get_manager().relation_to_table(r.get_signature()[col], value, tval); m_table_filter = r.get_manager().mk_filter_equal_fn(r.get_table(), tval, r.m_sig2table[col]); } } void operator()(relation_base & rb) override { finite_product_relation & r = get(rb); if(m_table_filter) { (*m_table_filter)(r.get_table()); return; } r.garbage_collect(false); relation_vector & inner_rels = r.m_others; unsigned rel_cnt = inner_rels.size(); for(unsigned i=0; i m_table_filter; scoped_ptr m_rel_filter; app_ref m_cond; idx_set m_table_cond_columns; idx_set m_rel_cond_columns; //like m_table_cond_columns and m_rel_cond_columns, only the indexes are local to the //table/relation, not to the signature of the whole relation idx_set m_table_local_cond_columns; idx_set m_rel_local_cond_columns; /** If equal to 0, it means the interpreted condition uses all table columns. Then the original table is used instead of the result of the projection. */ scoped_ptr m_table_cond_columns_project; /** \brief Column indexes of the global relations to which correspond the data columns in the table that is result of applying the \c m_table_cond_columns_project functor. */ unsigned_vector m_global_origins_of_projected_columns; scoped_ptr m_assembling_join_project; /** \brief Renaming that transforms the variable numbers pointing to the global relation into variables that point to the inner relation variables. The elements that do not correspond to columns of the inner relation (but rather to the table columns) is modified in \c operator() when evaluating the condition for all the relevant combinations of table values. */ expr_ref_vector m_renaming_for_inner_rel; public: filter_interpreted_fn(const finite_product_relation & r, app * condition) : m_manager(r.get_context().get_manager()), m_subst(r.get_context().get_var_subst()), m_cond(condition, m_manager), m_renaming_for_inner_rel(m_manager) { relation_manager & rmgr = r.get_manager(); rule_manager& rm = r.get_context().get_rule_manager(); idx_set& cond_columns = rm.collect_vars(m_cond); unsigned sig_sz = r.get_signature().size(); for(unsigned i=0; i tproj_scope; const table_base * tproj; if(m_table_cond_columns_project) { tproj_scope = (*m_table_cond_columns_project)(rtable); tproj = tproj_scope.get(); } else { tproj = &rtable; } unsigned projected_data_cols = tproj->get_signature().size()-1; SASSERT(m_table_cond_columns.num_elems()==projected_data_cols); table_signature filtered_sig = tproj->get_signature(); filtered_sig.push_back(finite_product_relation::s_rel_idx_sort); filtered_sig.set_functional_columns(1); relation_vector new_rels; scoped_rel filtered_table = tplugin.mk_empty(filtered_sig); table_fact f; unsigned renaming_ofs = m_renaming_for_inner_rel.size()-1; table_base::iterator pit = tproj->begin(); table_base::iterator pend = tproj->end(); for(; pit!=pend; ++pit) { pit->get_fact(f); unsigned old_rel_idx = static_cast(f.back()); const relation_base & old_rel = r.get_inner_rel(old_rel_idx); //put the table values into the substitution for(unsigned i=0; i filter = rmgr.mk_filter_interpreted_fn(*new_rel, to_app(inner_cond)); (*filter)(*new_rel); if(new_rel->empty()) { new_rel->deallocate(); continue; } unsigned new_rel_idx = new_rels.size(); new_rels.push_back(new_rel); f.push_back(new_rel_idx); filtered_table->add_fact(f); } if(!m_assembling_join_project) { unsigned_vector table_cond_columns_vect; for(unsigned i=0; i new_table = (*m_assembling_join_project)(rtable, *filtered_table); r.reset(); r.init(*new_table, new_rels, true); } }; relation_mutator_fn * finite_product_relation_plugin::mk_filter_interpreted_fn(const relation_base & rb, app * condition) { if(&rb.get_plugin()!=this) { return nullptr; } return alloc(filter_interpreted_fn, get(rb), condition); } class finite_product_relation_plugin::negation_filter_fn : public relation_intersection_filter_fn { unsigned_vector m_r_cols; unsigned_vector m_neg_cols; scoped_ptr m_table_neg_filter; scoped_ptr m_table_neg_complement_selector; scoped_ptr m_neg_intersection_join; scoped_ptr m_table_intersection_join; scoped_ptr m_table_overlap_union; scoped_ptr m_table_subtract; scoped_ptr m_inner_subtract; scoped_ptr m_overlap_table_last_column_remover; scoped_ptr m_r_table_union; bool m_table_overlaps_only; unsigned_vector m_r_shared_table_cols; unsigned_vector m_neg_shared_table_cols; unsigned_vector m_r_shared_rel_cols; unsigned_vector m_neg_shared_rel_cols; public: negation_filter_fn(const finite_product_relation & r, const finite_product_relation & neg, unsigned joined_col_cnt, const unsigned * r_cols, const unsigned * neg_cols) : m_r_cols(joined_col_cnt, r_cols), m_neg_cols(joined_col_cnt, neg_cols), m_table_overlaps_only(true) { const table_signature & tsig = r.m_table_sig; const table_base & rtable = r.get_table(); relation_manager & rmgr = r.get_manager(); for(unsigned i=0; iget_signature().size() , all_rel_cols); m_parent.m_inner_subtract = m_r.get_manager().mk_filter_by_negation_fn(*r_inner, inters_inner, all_rel_cols, all_rel_cols); } (*m_parent.m_inner_subtract)(*r_inner, inters_inner); unsigned new_rel_num = m_r.get_next_rel_idx(); m_r.set_inner_rel(new_rel_num, r_inner); func_columns[0]=new_rel_num; return true; } }; void operator()(relation_base & rb, const relation_base & negb) override { finite_product_relation & r = get(rb); const finite_product_relation & neg = get(negb); if(m_table_overlaps_only) { handle_only_tables_overlap_case(r, neg); return; } scoped_rel intersection = get((*m_neg_intersection_join)(r, neg)); DEBUG_CODE( finite_product_relation_plugin & plugin = r.get_plugin(); SASSERT(&intersection->get_plugin()==&plugin); //the result of join is also finite product SASSERT(r.get_signature()==intersection->get_signature());); table_base & r_table = r.get_table(); table_plugin & tplugin = r_table.get_plugin(); relation_manager & rmgr = r.get_manager(); //we need to do this before we perform the \c m_table_subtract //the sigrature of the \c table_overlap0 relation is //(data_columns)(original rel idx)(subtracted rel idx) scoped_rel table_overlap0 = (*m_table_intersection_join)(r_table, intersection->get_table()); //the rows that don't appear in the table of the intersection are safe to stay (*m_table_subtract)(r_table, intersection->get_table()); //now we will examine the rows that do appear in the intersection //There are no functional columns in the \c table_overlap0 relation (because of //the project we did). We need to make both rel idx columns functional. //We do not lose any rows, since the data columns by themselves are unique. table_signature table_overlap_sig(table_overlap0->get_signature()); table_overlap_sig.set_functional_columns(2); scoped_rel table_overlap = tplugin.mk_empty(table_overlap_sig); if(!m_table_overlap_union) { m_table_overlap_union = rmgr.mk_union_fn(*table_overlap, *table_overlap0); SASSERT(m_table_overlap_union); } (*m_table_overlap_union)(*table_overlap, *table_overlap0); { rel_subtractor * mutator = alloc(rel_subtractor, *this, r, *intersection); scoped_ptr mapper = rmgr.mk_map_fn(*table_overlap, mutator); (*mapper)(*table_overlap); } if(!m_overlap_table_last_column_remover) { unsigned removed_col = table_overlap->get_signature().size()-1; m_overlap_table_last_column_remover = rmgr.mk_project_fn(*table_overlap, 1, &removed_col); } scoped_rel final_overlapping_rows_table = (*m_overlap_table_last_column_remover)(*table_overlap); if(!m_r_table_union) { m_r_table_union = rmgr.mk_union_fn(r_table, *final_overlapping_rows_table); } (*m_r_table_union)(r_table, *final_overlapping_rows_table); } }; relation_intersection_filter_fn * finite_product_relation_plugin::mk_filter_by_negation_fn(const relation_base & rb, const relation_base & negb, unsigned joined_col_cnt, const unsigned * r_cols, const unsigned * negated_cols) { if(&rb.get_plugin()!=this || &negb.get_plugin()!=this) { return nullptr; } return alloc(negation_filter_fn, get(rb), get(negb), joined_col_cnt, r_cols, negated_cols); } class finite_product_relation_plugin::filter_identical_pairs_fn : public relation_mutator_fn { scoped_ptr m_tproject_fn; //if zero, no columns need to be projected away unsigned m_col_cnt; unsigned_vector m_table_cols; unsigned_vector m_rel_cols; scoped_ptr m_assembling_join_project; scoped_ptr m_updating_union; public: filter_identical_pairs_fn(const finite_product_relation & r, unsigned col_cnt, const unsigned * table_cols, const unsigned * rel_cols) : m_col_cnt(col_cnt), m_table_cols(col_cnt, table_cols), m_rel_cols(col_cnt, rel_cols) { SASSERT(col_cnt>0); const table_signature & tsig = r.m_table_sig; unsigned t_sz = tsig.size(); sort_two_arrays(col_cnt, m_table_cols.begin(), m_rel_cols.begin()); SASSERT(m_table_cols.back() tproj; if(m_tproject_fn) { tproj = (*m_tproject_fn)(r.get_table()); } else { tproj = r.get_table().clone(); } SASSERT(m_col_cnt+1==tproj->get_signature().size()); table_signature filtered_sig = tproj->get_signature(); filtered_sig.push_back(finite_product_relation::s_rel_idx_sort); filtered_sig.set_functional_columns(1); relation_vector new_rels; scoped_rel filtered_table = tplugin.mk_empty(filtered_sig); table_fact f; table_base::iterator pit = tproj->begin(); table_base::iterator pend = tproj->end(); for(; pit!=pend; ++pit) { pit->get_fact(f); unsigned old_rel_idx = static_cast(f.back()); const relation_base & old_rel = r.get_inner_rel(old_rel_idx); relation_base * new_rel = old_rel.clone(); for(unsigned i=0; i filter = rmgr.mk_filter_equal_fn(*new_rel, r_el, m_rel_cols[i]); (*filter)(*new_rel); } if(new_rel->empty()) { new_rel->deallocate(); continue; } unsigned new_rel_idx = new_rels.size(); new_rels.push_back(new_rel); f.push_back(new_rel_idx); filtered_table->add_fact(f); } if(!m_assembling_join_project) { m_assembling_join_project = mk_assembler_of_filter_result(rtable, *filtered_table, m_table_cols); } scoped_rel new_table = (*m_assembling_join_project)(r.get_table(), *filtered_table); r.reset(); r.init(*new_table, new_rels, true); } }; relation_mutator_fn * finite_product_relation_plugin::mk_filter_identical_pairs(const finite_product_relation & r, unsigned col_cnt, const unsigned * table_cols, const unsigned * rel_cols) { return alloc(filter_identical_pairs_fn, r, col_cnt, table_cols, rel_cols); } table_join_fn * finite_product_relation_plugin::mk_assembler_of_filter_result(const table_base & relation_table, const table_base & filtered_table, const unsigned_vector & selected_columns) { table_plugin & tplugin = relation_table.get_plugin(); const table_signature & rtable_sig = relation_table.get_signature(); unsigned rtable_sig_sz = rtable_sig.size(); unsigned selected_col_cnt = selected_columns.size(); SASSERT(selected_col_cnt+2==filtered_table.get_signature().size()); SASSERT(rtable_sig.functional_columns()==1); SASSERT(filtered_table.get_signature().functional_columns()==1); unsigned_vector rtable_joined_cols; rtable_joined_cols.append(selected_col_cnt, selected_columns.c_ptr()); //filtered table cols rtable_joined_cols.push_back(rtable_sig_sz-1); //unfiltered relation indexes unsigned_vector filtered_joined_cols; add_sequence(0, selected_col_cnt, filtered_joined_cols); //filtered table cols filtered_joined_cols.push_back(selected_col_cnt); //unfiltered relation indexes SASSERT(rtable_joined_cols.size()==filtered_joined_cols.size()); //the signature after join: //(all relation table columns)(all filtered relation table columns)(unfiltered rel idx non-func from 'filtered') // (unfiltered rel idx func from 'rtable')(filtered rel idx) unsigned_vector removed_cols; unsigned filtered_nonfunc_ofs = rtable_sig_sz-1; add_sequence(filtered_nonfunc_ofs, selected_col_cnt, removed_cols); //data columns from 'filtered' unsigned idx_ofs = filtered_nonfunc_ofs+selected_col_cnt; removed_cols.push_back(idx_ofs); //unfiltered relation indexes from 'filtered' removed_cols.push_back(idx_ofs+1); //unfiltered relation indexes from rtable table_join_fn * res = tplugin.get_manager().mk_join_project_fn(relation_table, filtered_table, rtable_joined_cols, filtered_joined_cols, removed_cols); SASSERT(res); return res; } // ----------------------------------- // // finite_product_relation // // ----------------------------------- finite_product_relation::finite_product_relation(finite_product_relation_plugin & p, const relation_signature & s, const bool * table_columns, table_plugin & tplugin, relation_plugin & oplugin, family_id other_kind) : relation_base(p, s), m_other_plugin(oplugin), m_other_kind(other_kind), m_full_rel_idx(UINT_MAX) { const relation_signature & rel_sig = get_signature(); unsigned sz = rel_sig.size(); m_sig2table.resize(sz, UINT_MAX); m_sig2other.resize(sz, UINT_MAX); for(unsigned i=0; iclone()), m_others(r.m_others), m_available_rel_indexes(r.m_available_rel_indexes), m_full_rel_idx(r.m_full_rel_idx), m_live_rel_collection_project(), m_empty_rel_removal_filter() { //m_others is now just a shallow copy, we need use clone of the relations that in it now unsigned other_sz = m_others.size(); for(unsigned i=0; ideallocate(); relation_vector::iterator it = m_others.begin(); relation_vector::iterator end = m_others.end(); for(; it!=end; ++it) { relation_base * rel= *it; if(rel) { rel->deallocate(); } } } context & finite_product_relation::get_context() const { return get_manager().get_context(); } unsigned finite_product_relation::get_next_rel_idx() const { unsigned res; if(!m_available_rel_indexes.empty()) { res = m_available_rel_indexes.back(); m_available_rel_indexes.pop_back(); } else { res = m_others.size(); m_others.push_back(0); } SASSERT(m_others[res]==0); return res; } void finite_product_relation::recycle_rel_idx(unsigned idx) const { SASSERT(m_others[idx]==0); m_available_rel_indexes.push_back(idx); } unsigned finite_product_relation::get_full_rel_idx() { if(m_full_rel_idx==UINT_MAX) { m_full_rel_idx = get_next_rel_idx(); relation_base * full_other = mk_full_inner(nullptr); m_others[m_full_rel_idx] = full_other; } return m_full_rel_idx; } void finite_product_relation::init(const table_base & table_vals, const relation_vector & others, bool contiguous) { SASSERT(m_table_sig.equal_up_to_fn_mark(table_vals.get_signature())); SASSERT(empty()); if(!m_others.empty()) { garbage_collect(false); } SASSERT(m_others.empty()); m_others = others; scoped_ptr table_union = get_manager().mk_union_fn(get_table(), table_vals); (*table_union)(get_table(), table_vals); if(!contiguous) { unsigned rel_cnt = m_others.size(); for(unsigned i=0; isuggest_fact(t_f)) { SASSERT(t_f.back()==new_rel_idx); new_rel = mk_empty_inner(); } else { new_rel = get_inner_rel(t_f.back()).clone(); t_f[t_f.size()-1]=new_rel_idx; m_table->ensure_fact(t_f); } new_rel->add_fact(o_f); set_inner_rel(new_rel_idx, new_rel); } bool finite_product_relation::contains_fact(const relation_fact & f) const { table_fact t_f; extract_table_fact(f, t_f); if(!m_table->fetch_fact(t_f)) { return false; } relation_fact o_f(get_context()); extract_other_fact(f, o_f); const relation_base & other = get_inner_rel(t_f.back()); return other.contains_fact(o_f); } bool finite_product_relation::empty() const { garbage_collect(true); return m_table->empty(); } finite_product_relation * finite_product_relation::clone() const { return alloc(finite_product_relation, *this); } void finite_product_relation::complement_self(func_decl* p) { unsigned other_sz = m_others.size(); for(unsigned i=0; icomplement(p); std::swap(m_others[i],r); r->deallocate(); } table_element full_rel_idx = get_full_rel_idx(); scoped_rel complement_table = m_table->complement(p, &full_rel_idx); scoped_ptr u_fn = get_manager().mk_union_fn(*m_table, *complement_table, nullptr); SASSERT(u_fn); (*u_fn)(*m_table, *complement_table, nullptr); } finite_product_relation * finite_product_relation::complement(func_decl* p) const { finite_product_relation * res = clone(); res->complement_self(p); return res; } class finite_product_relation::live_rel_collection_reducer : public table_row_pair_reduce_fn { idx_set & m_accumulator; public: live_rel_collection_reducer(idx_set & accumulator) : m_accumulator(accumulator) {} void operator()(table_element * func_columns, const table_element * merged_func_columns) override { m_accumulator.insert(static_cast(merged_func_columns[0])); } }; void finite_product_relation::collect_live_relation_indexes(idx_set & res) const { SASSERT(res.empty()); unsigned table_data_col_cnt = m_table_sig.size()-1; if(table_data_col_cnt==0) { if(!get_table().empty()) { table_base::iterator iit = get_table().begin(); table_base::iterator iend = get_table().end(); SASSERT(iit!=iend); res.insert(static_cast((*iit)[0])); SASSERT((++iit)==iend); } return; } if(!m_live_rel_collection_project) { buffer removed_cols; removed_cols.resize(table_data_col_cnt); for(unsigned i=0; i live_indexes_table = (*m_live_rel_collection_project)(get_table()); res.swap(m_live_rel_collection_acc); SASSERT(live_indexes_table->get_signature().size()==1); SASSERT(live_indexes_table->get_signature().functional_columns()==1); if(!live_indexes_table->empty()) { table_base::iterator iit = live_indexes_table->begin(); table_base::iterator iend = live_indexes_table->end(); SASSERT(iit!=iend); res.insert(static_cast((*iit)[0])); SASSERT((++iit)==iend); } } void finite_product_relation::garbage_collect(bool remove_empty) const { idx_set live_indexes; collect_live_relation_indexes(live_indexes); scoped_rel empty_rel_indexes; //populated only if \c remove_empty==true table_fact empty_rel_fact; unsigned rel_cnt = m_others.size(); #if Z3DEBUG unsigned encountered_live_indexes = 0; #endif for(unsigned rel_idx=0; rel_idxempty()) { continue; } if(empty_rel_indexes==0) { table_signature empty_rel_indexes_sig; empty_rel_indexes_sig.push_back(s_rel_idx_sort); empty_rel_indexes = get_table_plugin().mk_empty(empty_rel_indexes_sig); } empty_rel_fact.reset(); empty_rel_fact.push_back(rel_idx); empty_rel_indexes->add_fact(empty_rel_fact); } m_others[rel_idx]->deallocate(); m_others[rel_idx] = 0; if(rel_idx==m_full_rel_idx) { m_full_rel_idx = UINT_MAX; } recycle_rel_idx(rel_idx); } SASSERT(encountered_live_indexes==live_indexes.num_elems()); if(m_available_rel_indexes.size()==m_others.size()) { m_available_rel_indexes.reset(); m_others.reset(); } if(empty_rel_indexes) { SASSERT(remove_empty); if(!m_empty_rel_removal_filter) { unsigned t_joined_cols[] = { m_table_sig.size()-1 }; unsigned ei_joined_cols[] = { 0 }; m_empty_rel_removal_filter = get_manager().mk_filter_by_negation_fn(get_table(), *empty_rel_indexes, 1, t_joined_cols, ei_joined_cols); } (*m_empty_rel_removal_filter)(*m_table, *empty_rel_indexes); } } bool finite_product_relation::try_unify_specifications(ptr_vector & rels) { if(rels.empty()) { return true; } unsigned sig_sz = rels.back()->get_signature().size(); svector table_cols(sig_sz, true); ptr_vector::iterator it = rels.begin(); ptr_vector::iterator end = rels.end(); for(; it!=end; ++it) { finite_product_relation & rel = **it; for(unsigned i=0; i pr_fun = get_manager().mk_project_fn(get_table(), to_project_away); table_base * moved_cols_table = (*pr_fun)(get_table()); //gets destroyed inside moved_cols_trel scoped_rel moved_cols_trel = rmgr.get_table_relation_plugin(moved_cols_table->get_plugin()).mk_from_table(moved_cols_sig, moved_cols_table); svector moved_cols_table_flags(moved_cols_sig.size(), false); scoped_rel moved_cols_rel = get_plugin().mk_empty(moved_cols_sig, moved_cols_table_flags.c_ptr()); scoped_ptr union_fun = get_manager().mk_union_fn(*moved_cols_rel, *moved_cols_trel); SASSERT(union_fun); //the table_relation should be able to be 'unioned into' any relation (*union_fun)(*moved_cols_rel, *moved_cols_trel); unsigned_vector all_moved_cols_indexes; add_sequence(0, moved_cols_sig.size(), all_moved_cols_indexes); scoped_ptr join_fun = get_manager().mk_join_project_fn(*this, *moved_cols_rel, new_rel_columns, all_moved_cols_indexes, new_rel_columns, false); scoped_rel unordered_rel = (*join_fun)(*this, *moved_cols_rel); SASSERT(unordered_rel->get_signature().size()==sig_sz); //the signature size should be the same as original //now we need to reorder the columns in the \c new_rel to match the original table unsigned_vector permutation; unsigned moved_cols_cnt = new_rel_columns.size(); unsigned next_replaced_idx = 0; unsigned next_orig_idx = 0; for(unsigned i=0; i perm_fun = get_manager().mk_rename_fn(*unordered_rel, cycle); //the scoped_rel wrapper does the destruction of the old object unordered_rel = (*perm_fun)(*unordered_rel); cycle.reset(); } finite_product_relation & new_rel = finite_product_relation_plugin::get(*unordered_rel); //Swap the content of the current object and new_rel. On exitting the function new_rel will be destroyed //since it points to the content of scoped_rel unordered_rel. swap(new_rel); return true; } void finite_product_relation::display(std::ostream & out) const { garbage_collect(true); out << "finite_product_relation:\n"; out << " table:\n"; get_table().display(out); unsigned other_sz = m_others.size(); for(unsigned i=0; iget_fact(tfact); const table_relation & orel = static_cast(get_inner_rel(tfact[rel_idx_col])); const table_base & otable = orel.get_table(); table_base::iterator oit = otable.begin(); table_base::iterator oend = otable.end(); for(; oit!=oend; ++oit) { oit->get_fact(ofact); out << "\t("; for(unsigned i=0; iget_fact(fact); conjs.reset(); SASSERT(fact.size() == fact_sz); unsigned rel_idx = static_cast(fact[fact_sz-1]); m_others[rel_idx]->to_formula(tmp); for (unsigned i = 0; i + 1 < fact_sz; ++i) { conjs.push_back(m.mk_eq(m.mk_var(i, sig[i]), util.mk_numeral(fact[i], sig[i]))); } sh(tmp, fact_sz-1, tmp); conjs.push_back(tmp); disjs.push_back(m.mk_and(conjs.size(), conjs.c_ptr())); } bool_rewriter(m).mk_or(disjs.size(), disjs.c_ptr(), fml); } }; z3-z3-4.8.7/src/muz/rel/dl_finite_product_relation.h000066400000000000000000000354061356505360400223660ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_finite_product_relation.h Abstract: Author: Krystof Hoder (t-khoder) 2010-09-24. Revision History: --*/ #ifndef DL_FINITE_PRODUCT_RELATION_H_ #define DL_FINITE_PRODUCT_RELATION_H_ #include "muz/rel/dl_base.h" #include "muz/rel/dl_relation_manager.h" #include "muz/rel/dl_table_relation.h" namespace datalog { class finite_product_relation; void universal_delete(finite_product_relation* ptr); class finite_product_relation_plugin : public relation_plugin { friend class finite_product_relation; public: struct rel_spec { family_id m_inner_kind; //null_family_id means we don't care about the kind svector m_table_cols; rel_spec() : m_inner_kind(null_family_id) {} rel_spec(const svector& table_cols) : m_inner_kind(null_family_id), m_table_cols(table_cols) {} bool operator==(const rel_spec & o) const { return m_inner_kind==o.m_inner_kind && vectors_equal(m_table_cols, o.m_table_cols); } struct hash { unsigned operator()(const rel_spec & o) const { return o.m_inner_kind^svector_hash()(o.m_table_cols); } }; }; private: class join_fn; class converting_join_fn; class project_fn; class rename_fn; class union_fn; class inner_singleton_union_fn; class converting_union_fn; class filter_identical_fn; class filter_equal_fn; class filter_interpreted_fn; class negation_filter_fn; class filter_identical_pairs_fn; relation_plugin & m_inner_plugin; rel_spec_store > m_spec_store; static symbol get_name(relation_plugin & inner_plugin); family_id get_relation_kind(finite_product_relation & r, const bool * table_columns); static void get_all_possible_table_columns(relation_manager & rmgr, const relation_signature & s, svector & table_columns); void get_all_possible_table_columns(const relation_signature & s, svector & table_columns) { get_all_possible_table_columns(get_manager(), s, table_columns); } void split_signatures(const relation_signature & s, table_signature & table_sig, relation_signature & remaining_sig); void split_signatures(const relation_signature & s, const bool * table_columns, table_signature & table_sig, relation_signature & remaining_sig); public: static finite_product_relation & get(relation_base & r); static const finite_product_relation & get(const relation_base & r); static finite_product_relation * get(relation_base * r); static const finite_product_relation * get(const relation_base * r); static finite_product_relation_plugin & get_plugin(relation_manager & rmgr, relation_plugin & inner); finite_product_relation_plugin(relation_plugin & inner_plugin, relation_manager & manager); void initialize(family_id fid) override; relation_plugin & get_inner_plugin() const { return m_inner_plugin; } bool can_handle_signature(const relation_signature & s) override; relation_base * mk_empty(const relation_signature & s) override; /** \c inner_kind==null_family_id means we don't care about the kind of the inner relation */ finite_product_relation * mk_empty(const relation_signature & s, const bool * table_columns, family_id inner_kind=null_family_id); finite_product_relation * mk_empty(const finite_product_relation & original); relation_base * mk_empty(const relation_base & original) override; relation_base * mk_empty(const relation_signature & s, family_id kind) override; relation_base * mk_full(func_decl* p, const relation_signature & s) override; /** \brief Return true if \c r can be converted to \c finite_product_relation_plugin either by \c mk_from_table_relation or by \c mk_from_inner_relation. */ bool can_be_converted(const relation_base & r); /** If the conversion cannot be performed, 0 is returned. */ finite_product_relation * mk_from_table_relation(const table_relation & r); finite_product_relation * mk_from_inner_relation(const relation_base & r); bool can_convert_to_table_relation(const finite_product_relation & r); table_relation * to_table_relation(const finite_product_relation & r); protected: relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) override; relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, const unsigned * removed_cols) override; relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, const unsigned * permutation_cycle) override; relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta) override; relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, const unsigned * identical_cols) override; relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, unsigned col) override; relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition) override; relation_intersection_filter_fn * mk_filter_by_negation_fn(const relation_base & t, const relation_base & negated_obj, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) override; private: /** \brief Create a filter that enforces equality between pairs of table and relation columns The column numbers in arrays \c table_cols and \c rel_cols must be local to the table/inner relation. */ relation_mutator_fn * mk_filter_identical_pairs(const finite_product_relation & r, unsigned col_cnt, const unsigned * table_cols, const unsigned * rel_cols); /** \brief Create a join-project operation that creates a table according to \c relation_table but with references to relations updated and removed according to the content of \c filtered_table. \c selected_columns contains sorted indexes of data columns in \c relation_table that are also in the \c filtered_table (so that the first column in \c filtered_table corresponds to \c selected_columns[0] -th column in \c relation_table etc...) Signature of \c relation_table: (data columns)(functional column with indexes of relation objects) Signature of \c filtered_table: (selected data columns)(non-functional column with original relation object indexes) (functional column with indexes of filtered relation objects) */ static table_join_fn * mk_assembler_of_filter_result(const table_base & relation_table, const table_base & filtered_table, const unsigned_vector & selected_columns); }; class finite_product_relation : public relation_base { friend class finite_product_relation_plugin; friend class finite_product_relation_plugin::join_fn; friend class finite_product_relation_plugin::project_fn; friend class finite_product_relation_plugin::union_fn; friend class finite_product_relation_plugin::rename_fn; friend class finite_product_relation_plugin::inner_singleton_union_fn; friend class finite_product_relation_plugin::filter_equal_fn; friend class finite_product_relation_plugin::filter_identical_pairs_fn; class live_rel_collection_reducer; public: /** Size of this sort determines how many different relation objects can we refer to. */ static const table_sort s_rel_idx_sort; /** \brief The last column in the signature is a functional column with index of the associated inner relation. The other columns correspond to the relation signature according to \c m_table2sig. It holds that \c m_table_sig.size()-1==m_table2sig.size() */ table_signature m_table_sig; unsigned_vector m_table2sig; // (ordered list) unsigned_vector m_sig2table; //index of corresponding table column or UINT_MAX private: relation_signature m_other_sig; unsigned_vector m_other2sig; // (ordered list) public: unsigned_vector m_sig2other; //index of corresponding other relation column or UINT_MAX private: relation_plugin & m_other_plugin; family_id m_other_kind; mutable table_base * m_table; public: mutable relation_vector m_others; private: mutable unsigned_vector m_available_rel_indexes; /** \c UINT_MAX means uninitialized. If we can get away with it, we want to have a single full relation to refer to. */ mutable unsigned m_full_rel_idx; mutable idx_set m_live_rel_collection_acc; mutable scoped_ptr m_live_rel_collection_project; mutable scoped_ptr m_empty_rel_removal_filter; void recycle_rel_idx(unsigned idx) const; // creates a full relation if it does not exist. unsigned get_full_rel_idx(); public: relation_base & get_inner_rel(table_element idx) { SASSERT(idx(idx)); } relation_base & get_inner_rel(unsigned idx) { SASSERT(m_others[idx]); return *m_others[idx]; } const relation_base & get_inner_rel(unsigned idx) const { return const_cast(*this).get_inner_rel(idx); } unsigned get_next_rel_idx() const; /** The relation takes ownership of the \c inner object. */ void set_inner_rel(table_element idx, relation_base * inner) { SASSERT(idx(idx), inner); } /** The relation takes ownership of the \c inner object. */ void set_inner_rel(unsigned idx, relation_base * inner) { SASSERT(!m_others[idx]); SASSERT(inner); m_others[idx] = inner; } table_base & get_table() { return *m_table; } table_plugin & get_table_plugin() const { return get_table().get_plugin(); } void garbage_collect(bool remove_empty) const; /** \brief Initialize an empty relation with table \c table_vals and relations in \c others. The relation object takes ownership of relations inside the \c others vector. If \c contiguous is true, it can be assumed that there are no zero elements in the \c others array. */ void init(const table_base & table_vals, const relation_vector & others, bool contiguous); private: /** \brief Extract the values of table non-functional columns from the relation fact. The value of the functional column which determines index of the inner relation is undefined. */ void extract_table_fact(const relation_fact & rf, table_fact & tf) const; /** \brief Extract the values of the inner relation columns from the relation fact. */ void extract_other_fact(const relation_fact & rf, relation_fact & of) const; relation_base * mk_empty_inner(); relation_base * mk_full_inner(func_decl* pred); void complement_self(func_decl* pred); void collect_live_relation_indexes(idx_set & res) const; /** \brief Try to modify relations in \c rels so that they have the same columns corresponding to the table and the inner relation (so that the union can be perofrmed on theim in a straightforward way). Relations in \c rels must all have equal signature. Even if the function fails and false is returned, some relations may already be modified. They are in a valid state, but with different specification. */ static bool try_unify_specifications(ptr_vector & rels); bool try_modify_specification(const bool * table_cols); bool can_swap(const relation_base & r) const override { return &get_plugin()==&r.get_plugin(); } /** \brief Swap content of the current relation with the content of \c r. Both relations must come from the same plugin and be of the same signature. */ void swap(relation_base & r) override; /** \brief Create a \c finite_product_relation object. */ finite_product_relation(finite_product_relation_plugin & p, const relation_signature & s, const bool * table_columns, table_plugin & tplugin, relation_plugin & oplugin, family_id other_kind); finite_product_relation(const finite_product_relation & r); ~finite_product_relation() override; public: context & get_context() const; finite_product_relation_plugin & get_plugin() const { return static_cast(relation_base::get_plugin()); } bool is_table_column(unsigned col_idx) const { return m_sig2table[col_idx]!=UINT_MAX; } const table_base & get_table() const { return *m_table; } const relation_base & get_inner_rel(table_element idx) const { SASSERT(idx(idx)); } /** The function calls garbage_collect, so the internal state may change when it is called. */ bool empty() const override; void reset() override { m_table->reset(); garbage_collect(false); } void add_fact(const relation_fact & f) override; bool contains_fact(const relation_fact & f) const override; finite_product_relation * clone() const override; finite_product_relation * complement(func_decl* p) const override; void display(std::ostream & out) const override; void display_tuples(func_decl & pred, std::ostream & out) const override; unsigned get_size_estimate_rows() const override { return m_table->get_size_estimate_rows(); } unsigned get_size_estimate_bytes() const override { return m_table->get_size_estimate_bytes(); } void to_formula(expr_ref& fml) const override; }; }; #endif /* DL_FINITE_PRODUCT_RELATION_H_ */ z3-z3-4.8.7/src/muz/rel/dl_instruction.cpp000066400000000000000000001326411356505360400203660ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_instruction.cpp Abstract: Author: Krystof Hoder (t-khoder) 2010-09-14. Revision History: --*/ #include "ast/ast_pp.h" #include "util/stopwatch.h" #include "muz/base/dl_context.h" #include "muz/base/dl_util.h" #include "muz/rel/dl_instruction.h" #include "muz/rel/rel_context.h" #include "util/debug.h" #include "util/warning.h" namespace datalog { // ----------------------------------- // // execution_context // // ----------------------------------- execution_context::execution_context(context & context) : m_context(context), m_stopwatch(nullptr), m_timelimit_ms(0) {} execution_context::~execution_context() { reset(); } void execution_context::reset() { for (relation_base * rel : m_registers) { if (rel) { rel->deallocate(); } } m_registers.reset(); m_reg_annotation.reset(); reset_timelimit(); } rel_context& execution_context::get_rel_context() { return dynamic_cast(*m_context.get_rel_context()); } rel_context const& execution_context::get_rel_context() const { return dynamic_cast(*m_context.get_rel_context()); } struct compare_size_proc { typedef std::pair pr; bool operator()(pr const& a, pr const& b) const { return a.second > b.second; } }; void execution_context::report_big_relations(unsigned threshold, std::ostream & out) const { unsigned n = register_count(); svector > sizes; size_t total_bytes = 0; for(unsigned i = 0; i < n; i++) { unsigned sz = reg(i) ? reg(i)->get_size_estimate_bytes() : 0; total_bytes += sz; sizes.push_back(std::make_pair(i, sz)); } std::sort(sizes.begin(), sizes.end(), compare_size_proc()); out << "bytes " << total_bytes << "\n"; out << "bytes\trows\tannotation\n"; for(unsigned i = 0; i < n; i++) { unsigned sz = sizes[i].second; unsigned rg = sizes[i].first; unsigned rows = reg(rg) ? reg(rg)->get_size_estimate_rows() : 0; if (sz < threshold) { continue; } std::string annotation; get_register_annotation(i, annotation); out << sz << "\t" << rows << "\t" << annotation << "\n"; } } void execution_context::set_timelimit(unsigned time_in_ms) { SASSERT(time_in_ms > 0); m_timelimit_ms = time_in_ms; if (!m_stopwatch) { m_stopwatch = alloc(stopwatch); } else { m_stopwatch->stop(); m_stopwatch->reset(); } m_stopwatch->start(); } void execution_context::reset_timelimit() { dealloc(m_stopwatch); m_stopwatch = nullptr; m_timelimit_ms = 0; } bool execution_context::should_terminate() { return m_context.canceled() || memory::above_high_watermark() || (m_stopwatch && m_timelimit_ms != 0 && m_timelimit_ms < static_cast(1000*m_stopwatch->get_current_seconds())); } void execution_context::collect_statistics(statistics& st) const { st.update("dl.joins", m_stats.m_join); st.update("dl.project", m_stats.m_project); st.update("dl.filter", m_stats.m_filter); st.update("dl.total", m_stats.m_total); st.update("dl.unary_singleton", m_stats.m_unary_singleton); st.update("dl.filter_by_negation", m_stats.m_filter_by_negation); st.update("dl.select_equal_project", m_stats.m_select_equal_project); st.update("dl.join_project", m_stats.m_join_project); st.update("dl.project_rename", m_stats.m_project_rename); st.update("dl.union", m_stats.m_union); st.update("dl.filter_interpreted_project", m_stats.m_filter_interp_project); st.update("dl.filter_id", m_stats.m_filter_id); st.update("dl.filter_eq", m_stats.m_filter_eq); } // ----------------------------------- // // instruction // // ----------------------------------- instruction::~instruction() { for (auto& p : m_fn_cache) { dealloc(p.m_value); } } void instruction::process_all_costs() { process_costs(); } void instruction::collect_statistics(statistics& st) const { costs c; get_total_cost(c); st.update("instruction", c.instructions); st.update("instruction-time", c.milliseconds); } void instruction::display_indented(execution_context const & _ctx, std::ostream & out, const std::string & indentation) const { out << indentation; rel_context const& ctx = _ctx.get_rel_context(); display_head_impl(_ctx, out); if (ctx.output_profile()) { out << " {"; output_profile(out); out << '}'; } out << "\n"; display_body_impl(_ctx, out, indentation); } void instruction::log_verbose(execution_context& ctx) { IF_VERBOSE(2, display(ctx, verbose_stream());); } class instr_io : public instruction { bool m_store; func_decl_ref m_pred; reg_idx m_reg; public: instr_io(bool store, func_decl_ref const& pred, reg_idx reg) : m_store(store), m_pred(pred), m_reg(reg) {} bool perform(execution_context & ctx) override { log_verbose(ctx); if (m_store) { if (ctx.reg(m_reg)) { ctx.get_rel_context().store_relation(m_pred, ctx.release_reg(m_reg)); } else { rel_context & dctx = ctx.get_rel_context(); relation_base * empty_rel; //the object referenced by sig is valid only until we call dctx.store_relation() const relation_signature & sig = dctx.get_relation(m_pred).get_signature(); empty_rel = dctx.get_rmanager().mk_empty_relation(sig, m_pred.get()); dctx.store_relation(m_pred, empty_rel); } } else { relation_base& rel = ctx.get_rel_context().get_relation(m_pred); if (!rel.fast_empty()) { ctx.set_reg(m_reg, rel.clone()); } else { ctx.make_empty(m_reg); } } return true; } void make_annotations(execution_context & ctx) override { ctx.set_register_annotation(m_reg, m_pred->get_name().bare_str()); } std::ostream& display_head_impl(execution_context const& ctx, std::ostream & out) const override { const char * rel_name = m_pred->get_name().bare_str(); if (m_store) { return out << "store " << m_reg << " into " << rel_name; } else { return out << "load " << rel_name << " into " << m_reg; } } }; instruction * instruction::mk_load(ast_manager & m, func_decl * pred, reg_idx tgt) { return alloc(instr_io, false, func_decl_ref(pred, m), tgt); } instruction * instruction::mk_store(ast_manager & m, func_decl * pred, reg_idx src) { return alloc(instr_io, true, func_decl_ref(pred, m), src); } class instr_dealloc : public instruction { reg_idx m_reg; public: instr_dealloc(reg_idx reg) : m_reg(reg) {} bool perform(execution_context & ctx) override { ctx.make_empty(m_reg); return true; } void make_annotations(execution_context & ctx) override { ctx.set_register_annotation(m_reg, "alloc"); } std::ostream& display_head_impl(execution_context const& ctx, std::ostream & out) const override { return out << "dealloc " << m_reg; } }; instruction * instruction::mk_dealloc(reg_idx reg) { return alloc(instr_dealloc, reg); } class instr_clone_move : public instruction { bool m_clone; reg_idx m_src; reg_idx m_tgt; public: instr_clone_move(bool clone, reg_idx src, reg_idx tgt) : m_clone(clone), m_src(src), m_tgt(tgt) {} bool perform(execution_context & ctx) override { if (ctx.reg(m_src)) log_verbose(ctx); if (m_clone) { ctx.set_reg(m_tgt, ctx.reg(m_src) ? ctx.reg(m_src)->clone() : nullptr); } else { ctx.set_reg(m_tgt, ctx.reg(m_src) ? ctx.release_reg(m_src) : nullptr); } return true; } void make_annotations(execution_context & ctx) override { std::string str; if (ctx.get_register_annotation(m_src, str)) { ctx.set_register_annotation(m_tgt, str); } else if (ctx.get_register_annotation(m_tgt, str)) { ctx.set_register_annotation(m_src, str); } } std::ostream& display_head_impl(execution_context const& ctx, std::ostream & out) const override { return out << (m_clone ? "clone " : "move ") << m_src << " into " << m_tgt; } }; instruction * instruction::mk_clone(reg_idx from, reg_idx to) { return alloc(instr_clone_move, true, from, to); } instruction * instruction::mk_move(reg_idx from, reg_idx to) { return alloc(instr_clone_move, false, from, to); } class instr_while_loop : public instruction { typedef const vector idx_vector; idx_vector m_controls; instruction_block * m_body; bool control_is_empty(execution_context & ctx) { for (reg_idx r : m_controls) { if (ctx.reg(r) && !ctx.reg(r)->fast_empty()) { return false; } } return true; } protected: void process_all_costs() override { instruction::process_all_costs(); m_body->process_all_costs(); } public: instr_while_loop(unsigned control_reg_cnt, const reg_idx * control_regs, instruction_block * body) : m_controls(control_reg_cnt, control_regs), m_body(body) {} ~instr_while_loop() override { dealloc(m_body); } bool perform(execution_context & ctx) override { log_verbose(ctx); TRACE("dl", tout << "loop entered\n";); unsigned count = 0; while (!control_is_empty(ctx)) { IF_VERBOSE(10, verbose_stream() << "looping ... " << count++ << "\n";); if (!m_body->perform(ctx)) { TRACE("dl", tout << "while loop terminated before completion\n";); return false; } } TRACE("dl", tout << "while loop exited\n";); return true; } void make_annotations(execution_context & ctx) override { m_body->make_annotations(ctx); } std::ostream& display_head_impl(execution_context const & ctx, std::ostream & out) const override { out << "while"; print_container(m_controls, out); return out; } void display_body_impl(execution_context const & ctx, std::ostream & out, const std::string & indentation) const override { m_body->display_indented(ctx, out, indentation+" "); } }; instruction * instruction::mk_while_loop(unsigned control_reg_cnt, const reg_idx * control_regs, instruction_block * body) { return alloc(instr_while_loop, control_reg_cnt, control_regs, body); } class instr_join : public instruction { typedef unsigned_vector column_vector; reg_idx m_rel1; reg_idx m_rel2; column_vector m_cols1; column_vector m_cols2; reg_idx m_res; public: instr_join(reg_idx rel1, reg_idx rel2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, reg_idx result) : m_rel1(rel1), m_rel2(rel2), m_cols1(col_cnt, cols1), m_cols2(col_cnt, cols2), m_res(result) {} bool perform(execution_context & ctx) override { log_verbose(ctx); ++ctx.m_stats.m_join; if (!ctx.reg(m_rel1) || !ctx.reg(m_rel2)) { ctx.make_empty(m_res); return true; } relation_join_fn * fn; const relation_base & r1 = *ctx.reg(m_rel1); const relation_base & r2 = *ctx.reg(m_rel2); if (!find_fn(r1, r2, fn)) { fn = r1.get_manager().mk_join_fn(r1, r2, m_cols1, m_cols2); if (!fn) { throw default_exception(default_exception::fmt(), "trying to perform unsupported join operation on relations of kinds %s and %s", r1.get_plugin().get_name().bare_str(), r2.get_plugin().get_name().bare_str()); } store_fn(r1, r2, fn); } TRACE("dl", r1.get_signature().output(ctx.get_rel_context().get_manager(), tout); tout<<":"<\n";); ctx.set_reg(m_res, (*fn)(r1, r2)); TRACE("dl", ctx.reg(m_res)->get_signature().output(ctx.get_rel_context().get_manager(), tout); tout<<":"<get_size_estimate_rows()<<"\n";); if (ctx.reg(m_res)->fast_empty()) { ctx.make_empty(m_res); } return true; } void make_annotations(execution_context & ctx) override { std::string a1 = "rel1", a2 = "rel2"; ctx.get_register_annotation(m_rel1, a1); ctx.get_register_annotation(m_rel1, a1); ctx.set_register_annotation(m_res, "join " + a1 + " " + a2); } std::ostream& display_head_impl(execution_context const & ctx, std::ostream & out) const override { out << "join " << m_rel1; print_container(m_cols1, out); out << " and " << m_rel2; print_container(m_cols2, out); return out << " into " << m_res; } }; instruction * instruction::mk_join(reg_idx rel1, reg_idx rel2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, reg_idx result) { return alloc(instr_join, rel1, rel2, col_cnt, cols1, cols2, result); } class instr_filter_equal : public instruction { reg_idx m_reg; app_ref m_value; unsigned m_col; public: instr_filter_equal(ast_manager & m, reg_idx reg, const relation_element & value, unsigned col) : m_reg(reg), m_value(value, m), m_col(col) {} bool perform(execution_context & ctx) override { log_verbose(ctx); ++ctx.m_stats.m_filter_eq; if (!ctx.reg(m_reg)) { return true; } relation_mutator_fn * fn; relation_base & r = *ctx.reg(m_reg); if (!find_fn(r, fn)) { fn = r.get_manager().mk_filter_equal_fn(r, m_value, m_col); if (!fn) { throw default_exception(default_exception::fmt(), "trying to perform unsupported filter_equal operation on a relation of kind %s", r.get_plugin().get_name().bare_str()); } store_fn(r, fn); } (*fn)(r); if (r.fast_empty()) { ctx.make_empty(m_reg); } return true; } void make_annotations(execution_context & ctx) override { std::stringstream a; a << "filter_equal " << m_col << " val: " << ctx.get_rel_context().get_rmanager().to_nice_string(m_value); ctx.set_register_annotation(m_reg, a.str()); } std::ostream& display_head_impl(execution_context const& ctx, std::ostream & out) const override { return out << "filter_equal " << m_reg << " col: " << m_col << " val: " << ctx.get_rel_context().get_rmanager().to_nice_string(m_value); } }; instruction * instruction::mk_filter_equal(ast_manager & m, reg_idx reg, const relation_element & value, unsigned col) { return alloc(instr_filter_equal, m, reg, value, col); } class instr_filter_identical : public instruction { typedef unsigned_vector column_vector; reg_idx m_reg; column_vector m_cols; public: instr_filter_identical(reg_idx reg, unsigned col_cnt, const unsigned * identical_cols) : m_reg(reg), m_cols(col_cnt, identical_cols) {} bool perform(execution_context & ctx) override { log_verbose(ctx); ++ctx.m_stats.m_filter_id; if (!ctx.reg(m_reg)) { return true; } relation_mutator_fn * fn; relation_base & r = *ctx.reg(m_reg); if (!find_fn(r, fn)) { fn = r.get_manager().mk_filter_identical_fn(r, m_cols.size(), m_cols.c_ptr()); if (!fn) { throw default_exception(default_exception::fmt(), "trying to perform unsupported filter_identical operation on a relation of kind %s", r.get_plugin().get_name().bare_str()); } store_fn(r, fn); } (*fn)(r); if (r.fast_empty()) { ctx.make_empty(m_reg); } return true; } std::ostream& display_head_impl(execution_context const& ctx, std::ostream & out) const override { out << "filter_identical " << m_reg << " "; print_container(m_cols, out); return out; } void make_annotations(execution_context & ctx) override { ctx.set_register_annotation(m_reg, "filter_identical"); } }; instruction * instruction::mk_filter_identical(reg_idx reg, unsigned col_cnt, const unsigned * identical_cols) { return alloc(instr_filter_identical, reg, col_cnt, identical_cols); } class instr_filter_interpreted : public instruction { reg_idx m_reg; app_ref m_cond; public: instr_filter_interpreted(reg_idx reg, app_ref & condition) : m_reg(reg), m_cond(condition) {} bool perform(execution_context & ctx) override { if (!ctx.reg(m_reg)) { return true; } log_verbose(ctx); ++ctx.m_stats.m_filter; relation_mutator_fn * fn; relation_base & r = *ctx.reg(m_reg); TRACE("dl_verbose", r.display(tout <<"pre-filter-interpreted:\n");); if (!find_fn(r, fn)) { fn = r.get_manager().mk_filter_interpreted_fn(r, m_cond); if (!fn) { throw default_exception(default_exception::fmt(), "trying to perform unsupported filter_interpreted operation on a relation of kind %s", r.get_plugin().get_name().bare_str()); } store_fn(r, fn); } (*fn)(r); if (r.fast_empty()) { ctx.make_empty(m_reg); } //TRACE("dl_verbose", r.display(tout <<"post-filter-interpreted:\n");); return true; } std::ostream& display_head_impl(execution_context const& ctx, std::ostream & out) const override { return out << "filter_interpreted " << m_reg << " using " << mk_pp(m_cond, m_cond.get_manager()); } void make_annotations(execution_context & ctx) override { std::stringstream a; a << "filter_interpreted " << mk_pp(m_cond, m_cond.get_manager()); ctx.set_register_annotation(m_reg, a.str()); } }; instruction * instruction::mk_filter_interpreted(reg_idx reg, app_ref & condition) { return alloc(instr_filter_interpreted, reg, condition); } class instr_filter_interpreted_and_project : public instruction { reg_idx m_src; app_ref m_cond; unsigned_vector m_cols; reg_idx m_res; public: instr_filter_interpreted_and_project(reg_idx src, app_ref & condition, unsigned col_cnt, const unsigned * removed_cols, reg_idx result) : m_src(src), m_cond(condition), m_cols(col_cnt, removed_cols), m_res(result) {} bool perform(execution_context & ctx) override { log_verbose(ctx); if (!ctx.reg(m_src)) { ctx.make_empty(m_res); return true; } ++ctx.m_stats.m_filter_interp_project; relation_transformer_fn * fn; relation_base & reg = *ctx.reg(m_src); TRACE("dl_verbose", reg.display(tout <<"pre-filter-interpreted-and-project:\n");); if (!find_fn(reg, fn)) { fn = reg.get_manager().mk_filter_interpreted_and_project_fn(reg, m_cond, m_cols.size(), m_cols.c_ptr()); if (!fn) { throw default_exception(default_exception::fmt(), "trying to perform unsupported filter_interpreted_and_project operation on a relation of kind %s", reg.get_plugin().get_name().bare_str()); } store_fn(reg, fn); } ctx.set_reg(m_res, (*fn)(reg)); if (ctx.reg(m_res)->fast_empty()) { ctx.make_empty(m_res); } // TRACE("dl_verbose", reg.display(tout << "post-filter-interpreted-and-project:\n");); return true; } std::ostream& display_head_impl(execution_context const& ctx, std::ostream & out) const override { out << "filter_interpreted_and_project " << m_src << " into " << m_res; out << " using " << mk_pp(m_cond, m_cond.get_manager()); out << " deleting columns "; print_container(m_cols, out); return out; } void make_annotations(execution_context & ctx) override { std::stringstream s; std::string a = "rel_src"; ctx.get_register_annotation(m_src, a); s << "filter_interpreted_and_project " << mk_pp(m_cond, m_cond.get_manager()); ctx.set_register_annotation(m_res, s.str()); } }; instruction * instruction::mk_filter_interpreted_and_project(reg_idx reg, app_ref & condition, unsigned col_cnt, const unsigned * removed_cols, reg_idx result) { return alloc(instr_filter_interpreted_and_project, reg, condition, col_cnt, removed_cols, result); } class instr_union : public instruction { reg_idx m_src; reg_idx m_tgt; reg_idx m_delta; bool m_widen; //if true, widening is performed instead of an union public: instr_union(reg_idx src, reg_idx tgt, reg_idx delta, bool widen) : m_src(src), m_tgt(tgt), m_delta(delta), m_widen(widen) {} bool perform(execution_context & ctx) override { TRACE("dl", tout << "union " << m_src << " into " << m_tgt << " " << ctx.reg(m_src) << " " << ctx.reg(m_tgt) << "\n";); if (!ctx.reg(m_src)) { return true; } log_verbose(ctx); ++ctx.m_stats.m_union; relation_base & r_src = *ctx.reg(m_src); if (!ctx.reg(m_tgt)) { relation_base * new_tgt = r_src.get_plugin().mk_empty(r_src); ctx.set_reg(m_tgt, new_tgt); } relation_base & r_tgt = *ctx.reg(m_tgt); if (m_delta!=execution_context::void_register && !ctx.reg(m_delta)) { relation_base * new_delta = r_tgt.get_plugin().mk_empty(r_tgt); ctx.set_reg(m_delta, new_delta); } relation_base * r_delta = (m_delta!=execution_context::void_register) ? ctx.reg(m_delta) : nullptr; relation_union_fn * fn; if (r_delta) { if (!find_fn(r_tgt, r_src, *r_delta, fn)) { if (m_widen) { fn = r_src.get_manager().mk_widen_fn(r_tgt, r_src, r_delta); } else { fn = r_src.get_manager().mk_union_fn(r_tgt, r_src, r_delta); } if (!fn) { std::stringstream sstm; sstm << "trying to perform unsupported union operation on relations of kinds "; sstm << r_tgt.get_plugin().get_name() << ", " << r_src.get_plugin().get_name() << " and "; sstm << r_delta->get_plugin().get_name(); throw default_exception(sstm.str()); } store_fn(r_tgt, r_src, *r_delta, fn); } } else { if (!find_fn(r_tgt, r_src, fn)) { if (m_widen) { fn = r_src.get_manager().mk_widen_fn(r_tgt, r_src, nullptr); } else { fn = r_src.get_manager().mk_union_fn(r_tgt, r_src, nullptr); } if (!fn) { std::stringstream sstm; sstm << "trying to perform unsupported union operation on relations of kinds " << r_tgt.get_plugin().get_name() << " and " << r_src.get_plugin().get_name(); throw default_exception(sstm.str()); } store_fn(r_tgt, r_src, fn); } } SASSERT(r_src.get_signature().size() == r_tgt.get_signature().size()); TRACE("dl_verbose", r_tgt.display(tout <<"pre-union:");); (*fn)(r_tgt, r_src, r_delta); TRACE("dl_verbose", r_src.display(tout <<"src:"); r_tgt.display(tout <<"post-union:"); if (r_delta) { r_delta->display(tout <<"delta:"); }); if (r_delta && r_delta->fast_empty()) { ctx.make_empty(m_delta); } return true; } void make_annotations(execution_context & ctx) override { std::string str = "union"; if (!ctx.get_register_annotation(m_tgt, str)) { ctx.set_register_annotation(m_tgt, "union"); } if (m_delta != execution_context::void_register) { str = "delta of " + str; } ctx.set_register_annotation(m_delta, str); } std::ostream& display_head_impl(execution_context const& ctx, std::ostream & out) const override { out << (m_widen ? "widen " : "union ") << m_src << " into " << m_tgt; if (m_delta!=execution_context::void_register) { out << " with delta " << m_delta; } return out; } }; instruction * instruction::mk_union(reg_idx src, reg_idx tgt, reg_idx delta) { return alloc(instr_union, src, tgt, delta, false); } instruction * instruction::mk_widen(reg_idx src, reg_idx tgt, reg_idx delta) { return alloc(instr_union, src, tgt, delta, true); } class instr_project_rename : public instruction { typedef unsigned_vector column_vector; bool m_projection; reg_idx m_src; column_vector m_cols; reg_idx m_tgt; public: instr_project_rename(bool projection, reg_idx src, unsigned col_cnt, const unsigned * cols, reg_idx tgt) : m_projection(projection), m_src(src), m_cols(col_cnt, cols), m_tgt(tgt) {} bool perform(execution_context & ctx) override { if (!ctx.reg(m_src)) { ctx.make_empty(m_tgt); return true; } log_verbose(ctx); ++ctx.m_stats.m_project_rename; relation_transformer_fn * fn; relation_base & r_src = *ctx.reg(m_src); if (!find_fn(r_src, fn)) { if (m_projection) { fn = r_src.get_manager().mk_project_fn(r_src, m_cols.size(), m_cols.c_ptr()); } else { fn = r_src.get_manager().mk_rename_fn(r_src, m_cols.size(), m_cols.c_ptr()); } if (!fn) { std::stringstream sstm; sstm << "trying to perform unsupported " << (m_projection ? "project" : "rename"); sstm << " operation on a relation of kind " << r_src.get_plugin().get_name(); throw default_exception(sstm.str()); } store_fn(r_src, fn); } ctx.set_reg(m_tgt, (*fn)(r_src)); return true; } std::ostream& display_head_impl(execution_context const& ctx, std::ostream & out) const override { out << (m_projection ? "project " : "rename ") << m_src << " into " << m_tgt; out << (m_projection ? " deleting columns " : " with cycle "); print_container(m_cols, out); return out; } void make_annotations(execution_context & ctx) override { std::stringstream s; std::string a = "rel_src"; ctx.get_register_annotation(m_src, a); s << (m_projection ? "project " : "rename ") << a; ctx.set_register_annotation(m_tgt, s.str()); } }; instruction * instruction::mk_projection(reg_idx src, unsigned col_cnt, const unsigned * removed_cols, reg_idx tgt) { return alloc(instr_project_rename, true, src, col_cnt, removed_cols, tgt); } instruction * instruction::mk_rename(reg_idx src, unsigned cycle_len, const unsigned * permutation_cycle, reg_idx tgt) { return alloc(instr_project_rename, false, src, cycle_len, permutation_cycle, tgt); } class instr_join_project : public instruction { typedef unsigned_vector column_vector; reg_idx m_rel1; reg_idx m_rel2; column_vector m_cols1; column_vector m_cols2; column_vector m_removed_cols; reg_idx m_res; public: instr_join_project(reg_idx rel1, reg_idx rel2, unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols, reg_idx result) : m_rel1(rel1), m_rel2(rel2), m_cols1(joined_col_cnt, cols1), m_cols2(joined_col_cnt, cols2), m_removed_cols(removed_col_cnt, removed_cols), m_res(result) { } bool perform(execution_context & ctx) override { log_verbose(ctx); if (!ctx.reg(m_rel1) || !ctx.reg(m_rel2)) { ctx.make_empty(m_res); return true; } ++ctx.m_stats.m_join_project; relation_join_fn * fn; const relation_base & r1 = *ctx.reg(m_rel1); const relation_base & r2 = *ctx.reg(m_rel2); if (!find_fn(r1, r2, fn)) { fn = r1.get_manager().mk_join_project_fn(r1, r2, m_cols1, m_cols2, m_removed_cols); if (!fn) { throw default_exception(default_exception::fmt(), "trying to perform unsupported join-project operation on relations of kinds %s and %s", r1.get_plugin().get_name().bare_str(), r2.get_plugin().get_name().bare_str()); } store_fn(r1, r2, fn); } TRACE("dl", tout<\n";); ctx.set_reg(m_res, (*fn)(r1, r2)); TRACE("dl", tout<get_size_estimate_rows()<<"\n";); if (ctx.reg(m_res)->fast_empty()) { ctx.make_empty(m_res); } return true; } std::ostream& display_head_impl(execution_context const& ctx, std::ostream & out) const override { relation_base const* r1 = ctx.reg(m_rel1); relation_base const* r2 = ctx.reg(m_rel2); out << "join_project " << m_rel1; if (r1) { out << ":" << r1->num_columns(); out << "-" << r1->get_size_estimate_rows(); } print_container(m_cols1, out); out << " and " << m_rel2; if (r2) { out << ":" << r2->num_columns(); out << "-" << r2->get_size_estimate_rows(); } print_container(m_cols2, out); out << " into " << m_res << " removing columns "; print_container(m_removed_cols, out); return out; } void make_annotations(execution_context & ctx) override { std::string s1 = "rel1", s2 = "rel2"; ctx.get_register_annotation(m_rel1, s1); ctx.get_register_annotation(m_rel2, s2); ctx.set_register_annotation(m_res, "join project " + s1 + " " + s2); } }; instruction * instruction::mk_join_project(reg_idx rel1, reg_idx rel2, unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols, reg_idx result) { return alloc(instr_join_project, rel1, rel2, joined_col_cnt, cols1, cols2, removed_col_cnt, removed_cols, result); } class instr_select_equal_and_project : public instruction { reg_idx m_src; reg_idx m_result; app_ref m_value; unsigned m_col; public: instr_select_equal_and_project(ast_manager & m, reg_idx src, const relation_element & value, unsigned col, reg_idx result) : m_src(src), m_result(result), m_value(value, m), m_col(col) { // [Leo]: does not compile on gcc // TRACE("dl", tout << "src:" << m_src << " result: " << m_result << " value:" << m_value << " column:" << m_col << "\n";); } bool perform(execution_context & ctx) override { if (!ctx.reg(m_src)) { ctx.make_empty(m_result); return true; } log_verbose(ctx); ++ctx.m_stats.m_select_equal_project; relation_transformer_fn * fn; relation_base & r = *ctx.reg(m_src); if (!find_fn(r, fn)) { fn = r.get_manager().mk_select_equal_and_project_fn(r, m_value, m_col); if (!fn) { throw default_exception(default_exception::fmt(), "trying to perform unsupported select_equal_and_project operation on a relation of kind %s", r.get_plugin().get_name().bare_str()); } store_fn(r, fn); } ctx.set_reg(m_result, (*fn)(r)); if (ctx.reg(m_result)->fast_empty()) { ctx.make_empty(m_result); } return true; } std::ostream& display_head_impl(execution_context const& ctx, std::ostream & out) const override { return out << "select_equal_and_project " << m_src <<" into " << m_result << " col: " << m_col << " val: " << ctx.get_rel_context().get_rmanager().to_nice_string(m_value); } void make_annotations(execution_context & ctx) override { std::stringstream s; std::string s1 = "src"; ctx.get_register_annotation(m_src, s1); s << "select equal project col " << m_col << " val: " << ctx.get_rel_context().get_rmanager().to_nice_string(m_value) << " " << s1; ctx.set_register_annotation(m_result, s.str()); } }; instruction * instruction::mk_select_equal_and_project(ast_manager & m, reg_idx src, const relation_element & value, unsigned col, reg_idx result) { return alloc(instr_select_equal_and_project, m, src, value, col, result); } class instr_filter_by_negation : public instruction { typedef unsigned_vector column_vector; reg_idx m_tgt; reg_idx m_neg_rel; column_vector m_cols1; column_vector m_cols2; public: instr_filter_by_negation(reg_idx tgt, reg_idx neg_rel, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) : m_tgt(tgt), m_neg_rel(neg_rel), m_cols1(col_cnt, cols1), m_cols2(col_cnt, cols2) {} bool perform(execution_context & ctx) override { log_verbose(ctx); if (!ctx.reg(m_tgt) || !ctx.reg(m_neg_rel)) { return true; } ++ctx.m_stats.m_filter_by_negation; relation_intersection_filter_fn * fn; relation_base & r1 = *ctx.reg(m_tgt); const relation_base & r2 = *ctx.reg(m_neg_rel); if (!find_fn(r1, r2, fn)) { fn = r1.get_manager().mk_filter_by_negation_fn(r1, r2, m_cols1.size(), m_cols1.c_ptr(), m_cols2.c_ptr()); if (!fn) { std::stringstream sstm; sstm << "trying to perform unsupported filter_by_negation on relations of kinds "; sstm << r1.get_plugin().get_name() << " and " << r2.get_plugin().get_name(); throw default_exception(sstm.str()); } store_fn(r1, r2, fn); } (*fn)(r1, r2); if (r1.fast_empty()) { ctx.make_empty(m_tgt); } return true; } std::ostream& display_head_impl(execution_context const& ctx, std::ostream & out) const override { out << "filter_by_negation on " << m_tgt; print_container(m_cols1, out); out << " with " << m_neg_rel; print_container(m_cols2, out); return out << " as the negated table"; } void make_annotations(execution_context & ctx) override { std::string s = "negated relation"; ctx.get_register_annotation(m_neg_rel, s); ctx.set_register_annotation(m_tgt, "filter by negation " + s); } }; instruction * instruction::mk_filter_by_negation(reg_idx tgt, reg_idx neg_rel, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { return alloc(instr_filter_by_negation, tgt, neg_rel, col_cnt, cols1, cols2); } class instr_mk_unary_singleton : public instruction { relation_signature m_sig; func_decl* m_pred; reg_idx m_tgt; relation_fact m_fact; public: instr_mk_unary_singleton(ast_manager & m, func_decl* head_pred, const relation_sort & s, const relation_element & val, reg_idx tgt) : m_pred(head_pred), m_tgt(tgt), m_fact(m) { m_sig.push_back(s); m_fact.push_back(val); } bool perform(execution_context & ctx) override { log_verbose(ctx); ++ctx.m_stats.m_unary_singleton; relation_base * rel = ctx.get_rel_context().get_rmanager().mk_empty_relation(m_sig, m_pred); rel->add_fact(m_fact); ctx.set_reg(m_tgt, rel); return true; } std::ostream& display_head_impl(execution_context const& ctx, std::ostream & out) const override { return out << "mk_unary_singleton into " << m_tgt << " sort:" << ctx.get_rel_context().get_rmanager().to_nice_string(m_sig[0]) << " val:" << ctx.get_rel_context().get_rmanager().to_nice_string(m_sig[0], m_fact[0]); } void make_annotations(execution_context & ctx) override { std::string s; if (!ctx.get_register_annotation(m_tgt, s)) { ctx.set_register_annotation(m_tgt, "mk unary singleton"); } } }; instruction * instruction::mk_unary_singleton(ast_manager & m, func_decl* head_pred, const relation_sort & s, const relation_element & val, reg_idx tgt) { return alloc(instr_mk_unary_singleton, m, head_pred, s, val, tgt); } class instr_mk_total : public instruction { relation_signature m_sig; func_decl* m_pred; reg_idx m_tgt; public: instr_mk_total(const relation_signature & sig, func_decl* p, reg_idx tgt) : m_sig(sig), m_pred(p), m_tgt(tgt) {} bool perform(execution_context & ctx) override { log_verbose(ctx); ++ctx.m_stats.m_total; ctx.set_reg(m_tgt, ctx.get_rel_context().get_rmanager().mk_full_relation(m_sig, m_pred)); return true; } std::ostream& display_head_impl(execution_context const& ctx, std::ostream & out) const override { return out << "mk_total into " << m_tgt << " sort:" << ctx.get_rel_context().get_rmanager().to_nice_string(m_sig) << " " << m_pred->get_name(); } void make_annotations(execution_context & ctx) override { std::string s; if (!ctx.get_register_annotation(m_tgt, s)) { ctx.set_register_annotation(m_tgt, "mk_total"); } } }; instruction * instruction::mk_total(const relation_signature & sig, func_decl* pred, reg_idx tgt) { return alloc(instr_mk_total, sig, pred, tgt); } class instr_mark_saturated : public instruction { func_decl_ref m_pred; public: instr_mark_saturated(ast_manager & m, func_decl * pred) : m_pred(pred, m) {} bool perform(execution_context & ctx) override { log_verbose(ctx); ctx.get_rel_context().get_rmanager().mark_saturated(m_pred); return true; } std::ostream& display_head_impl(execution_context const& ctx, std::ostream & out) const override { return out << "mark_saturated " << m_pred->get_name().bare_str(); } void make_annotations(execution_context & ctx) override { } }; instruction * instruction::mk_mark_saturated(ast_manager & m, func_decl * pred) { return alloc(instr_mark_saturated, m, pred); } class instr_assert_signature : public instruction { relation_signature m_sig; reg_idx m_tgt; public: instr_assert_signature(const relation_signature & s, reg_idx tgt) : m_sig(s), m_tgt(tgt) {} bool perform(execution_context & ctx) override { log_verbose(ctx); if (ctx.reg(m_tgt)) { SASSERT(ctx.reg(m_tgt)->get_signature()==m_sig); } return true; } std::ostream& display_head_impl(execution_context const& ctx, std::ostream & out) const override { out << "instr_assert_signature of " << m_tgt << " signature:"; print_container(m_sig, out); return out; } void make_annotations(execution_context & ctx) override { std::string s; if (!ctx.get_register_annotation(m_tgt, s)) { ctx.set_register_annotation(m_tgt, "assert signature"); } } }; instruction * instruction::mk_assert_signature(const relation_signature & s, reg_idx tgt) { return alloc(instr_assert_signature, s, tgt); } // ----------------------------------- // // instruction_block // // ----------------------------------- instruction_block::~instruction_block() { reset(); } void instruction_block::reset() { for (auto* t : m_data) { dealloc(t); } m_data.reset(); m_observer = nullptr; } bool instruction_block::perform(execution_context & ctx) const { cost_recorder crec; for (instruction * instr : m_data) { crec.start(instr); //finish is performed by the next start() or by the destructor of crec TRACE("dl", instr->display_head_impl(ctx, tout << "% ") << "\n";); if (ctx.should_terminate() || !instr->perform(ctx)) { return false; } } return true; } void instruction_block::process_all_costs() { for (auto* t : m_data) { t->process_all_costs(); } } void instruction_block::collect_statistics(statistics& st) const { for (auto* t : m_data) { t->collect_statistics(st); } } void instruction_block::make_annotations(execution_context & ctx) { for (auto* t : m_data) { t->make_annotations(ctx); } } void instruction_block::display_indented(execution_context const& _ctx, std::ostream & out, const std::string & indentation) const { rel_context const& ctx = _ctx.get_rel_context(); for (auto* i : m_data) { if (i->passes_output_thresholds(ctx.get_context()) || i->being_recorded()) { i->display_indented(_ctx, out, indentation); } } } } z3-z3-4.8.7/src/muz/rel/dl_instruction.h000066400000000000000000000321361356505360400200310ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_instruction.h Abstract: Author: Krystof Hoder (t-khoder) 2010-09-14. Revision History: --*/ #ifndef DL_INSTRUCTION_H_ #define DL_INSTRUCTION_H_ #include #include #include #include "ast/ast.h" #include "util/vector.h" #include "muz/rel/dl_base.h" #include "muz/base/dl_costs.h" #include "muz/base/dl_context.h" namespace datalog { class instruction_block; class rel_context; inline void check_overflow(unsigned i) { if (i == UINT_MAX) { throw out_of_memory_error(); } } // ----------------------------------- // // execution_context // // ----------------------------------- class execution_context { public: typedef relation_base * reg_type; typedef vector reg_vector; typedef unsigned reg_idx; /** \brief A register number that should never be referenced to. Can stand e.g. for a tail table in a rule with no tail. */ static const reg_idx void_register = UINT_MAX; private: typedef u_map reg_annotations; context & m_context; reg_vector m_registers; reg_annotations m_reg_annotation; stopwatch * m_stopwatch; unsigned m_timelimit_ms; //zero means no limit public: execution_context(context & context); ~execution_context(); void reset(); rel_context & get_rel_context(); rel_context const & get_rel_context() const; void set_timelimit(unsigned time_in_ms); void reset_timelimit(); bool should_terminate(); struct stats { unsigned m_join; unsigned m_project; unsigned m_filter; unsigned m_total; unsigned m_unary_singleton; unsigned m_filter_by_negation; unsigned m_select_equal_project; unsigned m_join_project; unsigned m_project_rename; unsigned m_union; unsigned m_filter_interp_project; unsigned m_filter_id; unsigned m_filter_eq; unsigned m_min; stats() { reset(); } void reset() { memset(this, 0, sizeof(*this)); } }; stats m_stats; void collect_statistics(statistics& st) const; /** \brief Return reference to \c i -th register that contains pointer to a relation. If register contains zero, it should be treated as if it contains an empty relation. */ reg_type reg(reg_idx i) const { if (i >= m_registers.size()) { return nullptr; } return m_registers[i]; } /** \brief Return value of the register and assign zero into it place. */ reg_type release_reg(reg_idx i) { SASSERT(i < m_registers.size()); SASSERT(m_registers[i]); reg_type res = m_registers[i]; m_registers[i] = 0; return res; } /** \brief Assign value to a register. If it was non-empty, deallocate its content first. */ void set_reg(reg_idx i, reg_type val) { if (i >= m_registers.size()) { check_overflow(i); m_registers.resize(i+1); } if (m_registers[i]) { m_registers[i]->deallocate(); } m_registers[i] = val; } void make_empty(reg_idx i) { if (reg(i)) { set_reg(i, nullptr); } } unsigned register_count() const { return m_registers.size(); } bool get_register_annotation(reg_idx reg, std::string & res) const { return m_reg_annotation.find(reg, res); } void set_register_annotation(reg_idx reg, const std::string & str) { m_reg_annotation.insert(reg, str); } void report_big_relations(unsigned threshold, std::ostream & out) const; }; // ----------------------------------- // // instruction // // ----------------------------------- /** \brief Base class for instructions used in datalog saturation. A relation in a register is owned by that register and is not referenced from anywhere else. Instructions that move context of one register to another leave the source register empty and deallocate the previous content of the target register. */ class instruction : public accounted_object { typedef u_map fn_cache; fn_cache m_fn_cache; static const int rk_encode_base = 1024; inline static unsigned encode_kind(family_id k) { SASSERT(k bool find_fn(const relation_base & r, T* & result) const { return m_fn_cache.find(encode_kind(r.get_kind()), reinterpret_cast(result)); } template bool find_fn(const relation_base & r1, const relation_base & r2, T*& result) const { return m_fn_cache.find(encode_kinds(r1.get_kind(), r2.get_kind()), reinterpret_cast(result)); } template bool find_fn(const relation_base & r1, const relation_base & r2, const relation_base & r3, T * & result) const { return m_fn_cache.find(encode_kinds(r1.get_kind(), r2.get_kind(), r3.get_kind()), reinterpret_cast(result)); } void store_fn(const relation_base & r, base_relation_fn * fn) { m_fn_cache.insert(encode_kind(r.get_kind()), fn); } void store_fn(const relation_base & r1, const relation_base & r2, base_relation_fn * fn) { m_fn_cache.insert(encode_kinds(r1.get_kind(), r2.get_kind()), fn); } void store_fn(const relation_base & r1, const relation_base & r2, const relation_base & r3, base_relation_fn * fn) { m_fn_cache.insert(encode_kinds(r1.get_kind(), r2.get_kind(), r3.get_kind()), fn); } /** Process not only costs associated with the current instruction, but in case of block instructions, process also costs associated with its child instructions. */ virtual void process_all_costs(); /** \brief Output one line header of the current instruction. The newline character at the end should not be printed. */ virtual std::ostream& display_head_impl(execution_context const & ctx, std::ostream & out) const { return out << ""; } /** \brief If relevant, output the body of the current instruction. Each line must be prepended by \c indentation and ended by a newline character. */ virtual void display_body_impl(execution_context const & ctx, std::ostream & out, const std::string & indentation) const {} void log_verbose(execution_context& ctx); public: typedef execution_context::reg_type reg_type; typedef execution_context::reg_idx reg_idx; virtual ~instruction(); virtual bool perform(execution_context & ctx) = 0; virtual void make_annotations(execution_context & ctx) = 0; void display(execution_context const& ctx, std::ostream & out) const { display_indented(ctx, out, ""); } void display_indented(execution_context const & ctx, std::ostream & out, const std::string & indentation) const; static instruction * mk_load(ast_manager & m, func_decl * pred, reg_idx tgt); /** \brief The store operation moves the relation from a register into the context. The register is set to zero after the operation. */ static instruction * mk_store(ast_manager & m, func_decl * pred, reg_idx src); static instruction * mk_dealloc(reg_idx reg); //maybe not necessary static instruction * mk_clone(reg_idx from, reg_idx to); static instruction * mk_move(reg_idx from, reg_idx to); /** \brief Return instruction that performs \c body as long as at least one register in \c control_regs contains non-empty relation. The instruction object takes over the ownership of the \c body object. */ static instruction * mk_while_loop(unsigned control_reg_cnt, const reg_idx * control_regs, instruction_block * body); static instruction * mk_join(reg_idx rel1, reg_idx rel2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, reg_idx result); static instruction * mk_filter_equal(ast_manager & m, reg_idx reg, const relation_element & value, unsigned col); static instruction * mk_filter_identical(reg_idx reg, unsigned col_cnt, const unsigned * identical_cols); static instruction * mk_filter_interpreted(reg_idx reg, app_ref & condition); static instruction * mk_filter_interpreted_and_project(reg_idx src, app_ref & condition, unsigned col_cnt, const unsigned * removed_cols, reg_idx result); static instruction * mk_union(reg_idx src, reg_idx tgt, reg_idx delta); static instruction * mk_widen(reg_idx src, reg_idx tgt, reg_idx delta); static instruction * mk_projection(reg_idx src, unsigned col_cnt, const unsigned * removed_cols, reg_idx tgt); static instruction * mk_join_project(reg_idx rel1, reg_idx rel2, unsigned joined_col_cnt, 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, 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, const unsigned * cols1, const unsigned * cols2); static instruction * mk_select_equal_and_project(ast_manager & m, reg_idx src, const relation_element & value, unsigned col, reg_idx result); static instruction * mk_unary_singleton(ast_manager & m, func_decl* pred, const relation_sort & s, const relation_element & val, reg_idx tgt); static instruction * mk_total(const relation_signature & sig, func_decl* pred, reg_idx tgt); /** \brief The mark_saturated instruction marks a relation as saturated, so that after next restart it does not have to be part of the saturation again. */ static instruction * mk_mark_saturated(ast_manager & m, func_decl * pred); static instruction * mk_assert_signature(const relation_signature & s, reg_idx tgt); void collect_statistics(statistics& st) const; }; // ----------------------------------- // // instruction_block // // ----------------------------------- class instruction_block { public: struct instruction_observer { virtual ~instruction_observer() {} virtual void notify(instruction * i) {} }; private: typedef ptr_vector instr_seq_type; instr_seq_type m_data; instruction_observer* m_observer; public: instruction_block() : m_observer(nullptr) {} ~instruction_block(); void reset(); void push_back(instruction * i) { m_data.push_back(i); if (m_observer) { m_observer->notify(i); } } void set_observer(instruction_observer * o) { SASSERT(o==0 || m_observer==0); m_observer = o; } void collect_statistics(statistics& st) const; /** \brief Perform instructions in the block. If the run was interrupted before completion, return false; otherwise return true. The execution can terminate before completion if the function \c execution_context::should_terminate() returns true. */ bool perform(execution_context & ctx) const; void process_all_costs(); void make_annotations(execution_context & ctx); void display(execution_context const & ctx, std::ostream & out) const { display_indented(ctx, out, ""); } void display_indented(execution_context const & ctx, std::ostream & out, const std::string & indentation) const; unsigned num_instructions() const { return m_data.size(); } }; }; #endif /* DL_INSTRUCTION_H_ */ z3-z3-4.8.7/src/muz/rel/dl_interval_relation.cpp000066400000000000000000000564501356505360400215310ustar00rootroot00000000000000/*++ Copyright (c) 2010 Microsoft Corporation Module Name: dl_interval_relation.cpp Abstract: Basic interval reatlion. Author: Nikolaj Bjorner (nbjorner) 2010-2-11 Revision History: --*/ #include "util/debug.h" #include "ast/ast_pp.h" #include "muz/rel/dl_interval_relation.h" #include "muz/rel/dl_relation_manager.h" #include "ast/rewriter/bool_rewriter.h" namespace datalog { // ------------------------- // interval_relation_plugin interval_relation_plugin::interval_relation_plugin(relation_manager& m): relation_plugin(interval_relation_plugin::get_name(), m), m_empty(m_dep), m_arith(get_ast_manager()) { } bool interval_relation_plugin::can_handle_signature(const relation_signature & sig) { for (unsigned i = 0; i < sig.size(); ++i) { if (!m_arith.is_int(sig[i]) && !m_arith.is_real(sig[i])) { return false; } } return true; } relation_base * interval_relation_plugin::mk_empty(const relation_signature & s) { return alloc(interval_relation, *this, s, true); } relation_base * interval_relation_plugin::mk_full(func_decl* p, const relation_signature & s) { return alloc(interval_relation, *this, s, false); } class interval_relation_plugin::join_fn : public convenient_relation_join_fn { public: join_fn(const relation_signature & o1_sig, const relation_signature & o2_sig, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) : convenient_relation_join_fn(o1_sig, o2_sig, col_cnt, cols1, cols2){ } relation_base * operator()(const relation_base & _r1, const relation_base & _r2) override { interval_relation const& r1 = get(_r1); interval_relation const& r2 = get(_r2); interval_relation_plugin& p = r1.get_plugin(); interval_relation* result = dynamic_cast(p.mk_full(nullptr, get_result_signature())); result->mk_join(r1, r2, m_cols1.size(), m_cols1.c_ptr(), m_cols2.c_ptr()); return result; } }; relation_join_fn * interval_relation_plugin::mk_join_fn(const relation_base & r1, const relation_base & r2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { if (!check_kind(r1) || !check_kind(r2)) { return nullptr; } return alloc(join_fn, r1.get_signature(), r2.get_signature(), col_cnt, cols1, cols2); } class interval_relation_plugin::project_fn : public convenient_relation_project_fn { public: project_fn(const relation_signature & orig_sig, unsigned removed_col_cnt, const unsigned * removed_cols) : convenient_relation_project_fn(orig_sig, removed_col_cnt, removed_cols) { } relation_base * operator()(const relation_base & _r) override { interval_relation const& r = get(_r); interval_relation_plugin& p = r.get_plugin(); interval_relation* result = dynamic_cast(p.mk_full(nullptr, get_result_signature())); result->mk_project(r, m_removed_cols.size(), m_removed_cols.c_ptr()); return result; } }; relation_transformer_fn * interval_relation_plugin::mk_project_fn(const relation_base & r, unsigned col_cnt, const unsigned * removed_cols) { return alloc(project_fn, r.get_signature(), col_cnt, removed_cols); } class interval_relation_plugin::rename_fn : public convenient_relation_rename_fn { public: rename_fn(const relation_signature & orig_sig, unsigned cycle_len, const unsigned * cycle) : convenient_relation_rename_fn(orig_sig, cycle_len, cycle) { } relation_base * operator()(const relation_base & _r) override { interval_relation const& r = get(_r); interval_relation_plugin& p = r.get_plugin(); interval_relation* result = dynamic_cast(p.mk_full(nullptr, get_result_signature())); result->mk_rename(r, m_cycle.size(), m_cycle.c_ptr()); return result; } }; relation_transformer_fn * interval_relation_plugin::mk_rename_fn(const relation_base & r, unsigned cycle_len, const unsigned * permutation_cycle) { if(!check_kind(r)) { return nullptr; } return alloc(rename_fn, r.get_signature(), cycle_len, permutation_cycle); } interval interval_relation_plugin::unite(interval const& src1, interval const& src2) { bool l_open = src1.is_lower_open(); bool r_open = src1.is_upper_open(); ext_numeral low = src1.inf(); ext_numeral high = src1.sup(); if (src2.inf() < low || (src2.inf() == low && l_open)) { low = src2.inf(); l_open = src2.is_lower_open(); } if (src2.sup() > high || (src2.sup() == high && r_open)) { high = src2.sup(); r_open = src2.is_upper_open(); } return interval(dep(), low, l_open, nullptr, high, r_open, nullptr); } interval interval_relation_plugin::widen(interval const& src1, interval const& src2) { bool l_open = src1.is_lower_open(); bool r_open = src1.is_upper_open(); ext_numeral low = src1.inf(); ext_numeral high = src1.sup(); if (src2.inf() < low || (low == src2.inf() && l_open && !src2.is_lower_open())) { low = ext_numeral(false); l_open = true; } if (high < src2.sup() || (src2.sup() == high && !r_open && src2.is_upper_open())) { high = ext_numeral(true); r_open = true; } return interval(dep(), low, l_open, nullptr, high, r_open, nullptr); } interval interval_relation_plugin::meet(interval const& src1, interval const& src2, bool& isempty) { isempty = false; if (is_empty(0, src1) || is_infinite(src2)) { return src1; } if (is_empty(0, src2) || is_infinite(src1)) { return src2; } bool l_open = src1.is_lower_open(); bool r_open = src1.is_upper_open(); ext_numeral low = src1.inf(); ext_numeral high = src1.sup(); if (src2.inf() > low || (src2.inf() == low && !l_open)) { low = src2.inf(); l_open = src2.is_lower_open(); } if (src2.sup() < high || (src2.sup() == high && !r_open)) { high = src2.sup(); r_open = src2.is_upper_open(); } if (low > high || (low == high && (l_open || r_open))) { isempty = true; return interval(dep()); } else { return interval(dep(), low, l_open, nullptr, high, r_open, nullptr); } } bool interval_relation_plugin::is_infinite(interval const& i) { return i.plus_infinity() && i.minus_infinity(); } bool interval_relation_plugin::is_empty(unsigned, interval const& i) { return i.sup() < i.inf(); } class interval_relation_plugin::union_fn : public relation_union_fn { bool m_is_widen; public: union_fn(bool is_widen) : m_is_widen(is_widen) { } void operator()(relation_base & _r, const relation_base & _src, relation_base * _delta) override { TRACE("interval_relation", _r.display(tout << "dst:\n"); _src.display(tout << "src:\n");); interval_relation& r = get(_r); interval_relation const& src = get(_src); if (_delta) { interval_relation& d = get(*_delta); r.mk_union(src, &d, m_is_widen); } else { r.mk_union(src, nullptr, m_is_widen); } } }; relation_union_fn * interval_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta) { if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) { return nullptr; } return alloc(union_fn, false); } relation_union_fn * interval_relation_plugin::mk_widen_fn( const relation_base & tgt, const relation_base & src, const relation_base * delta) { if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) { return nullptr; } return alloc(union_fn, true); } class interval_relation_plugin::filter_identical_fn : public relation_mutator_fn { unsigned_vector m_identical_cols; public: filter_identical_fn(unsigned col_cnt, const unsigned * identical_cols) : m_identical_cols(col_cnt, identical_cols) {} void operator()(relation_base & r) override { interval_relation & pr = get(r); for (unsigned i = 1; i < m_identical_cols.size(); ++i) { unsigned c1 = m_identical_cols[0]; unsigned c2 = m_identical_cols[i]; pr.equate(c1, c2); } } }; relation_mutator_fn * interval_relation_plugin::mk_filter_identical_fn( const relation_base & t, unsigned col_cnt, const unsigned * identical_cols) { if(!check_kind(t)) { return nullptr; } return alloc(filter_identical_fn, col_cnt, identical_cols); } class interval_relation_plugin::filter_equal_fn : public relation_mutator_fn { unsigned m_col; rational m_value; public: filter_equal_fn(relation_manager & m, const relation_element & value, unsigned col) : m_col(col) { arith_util arith(m.get_context().get_manager()); VERIFY(arith.is_numeral(value, m_value)); } void operator()(relation_base & _r) override { interval_relation & r = get(_r); interval_relation_plugin & p = r.get_plugin(); r.mk_intersect(m_col, interval(p.dep(), m_value)); TRACE("interval_relation", tout << m_value << "\n"; r.display(tout);); } }; relation_mutator_fn * interval_relation_plugin::mk_filter_equal_fn(const relation_base & r, const relation_element & value, unsigned col) { if(check_kind(r)) { return alloc(filter_equal_fn, get_manager(), value, col); } return nullptr; } class interval_relation_plugin::filter_interpreted_fn : public relation_mutator_fn { app_ref m_cond; public: filter_interpreted_fn(interval_relation const& t, app* cond): m_cond(cond, t.get_plugin().get_ast_manager()) { } void operator()(relation_base& t) override { get(t).filter_interpreted(m_cond); TRACE("interval_relation", tout << mk_pp(m_cond, m_cond.get_manager()) << "\n"; t.display(tout);); } }; relation_mutator_fn * interval_relation_plugin::mk_filter_interpreted_fn(const relation_base & t, app * condition) { if (check_kind(t)) { return alloc(filter_interpreted_fn, get(t), condition); } return nullptr; } interval_relation& interval_relation_plugin::get(relation_base& r) { return dynamic_cast(r); } interval_relation const & interval_relation_plugin::get(relation_base const& r) { return dynamic_cast(r); } // ----------------------- // interval_relation interval_relation::interval_relation(interval_relation_plugin& p, relation_signature const& s, bool is_empty): vector_relation(p, s, is_empty, interval(p.dep())) { TRACE("interval_relation", tout << s.size() << "\n";); } void interval_relation::add_fact(const relation_fact & f) { interval_relation r(get_plugin(), get_signature(), false); ast_manager& m = get_plugin().get_ast_manager(); for (unsigned i = 0; i < f.size(); ++i) { app_ref eq(m); expr* e = f[i]; eq = m.mk_eq(m.mk_var(i, m.get_sort(e)), e); r.filter_interpreted(eq.get()); } mk_union(r, nullptr, false); } bool interval_relation::contains_fact(const relation_fact & f) const { SASSERT(f.size() == get_signature().size()); interval_relation_plugin& p = get_plugin(); for (unsigned i = 0; i < f.size(); ++i) { if (f[i] != f[find(i)]) { return false; } interval const& iv = (*this)[i]; if (p.is_infinite(iv)) { continue; } rational v; if (p.m_arith.is_numeral(f[i], v)) { if (!iv.contains(v)) { return false; } } else { // TBD: may or must? } } return true; } interval_relation * interval_relation::clone() const { interval_relation* result = alloc(interval_relation, get_plugin(), get_signature(), empty()); result->copy(*this); return result; } interval_relation * interval_relation::complement(func_decl*) const { UNREACHABLE(); return nullptr; } void interval_relation::to_formula(expr_ref& fml) const { ast_manager& m = get_plugin().get_ast_manager(); arith_util& arith = get_plugin().m_arith; expr_ref_vector conjs(m); relation_signature const& sig = get_signature(); for (unsigned i = 0; i < sig.size(); ++i) { if (i != find(i)) { conjs.push_back(m.mk_eq(m.mk_var(i, sig[i]), m.mk_var(find(i), sig[find(i)]))); continue; } interval const& iv = (*this)[i]; sort* ty = sig[i]; expr_ref var(m.mk_var(i, ty), m); if (!iv.minus_infinity()) { expr* lo = arith.mk_numeral(iv.get_lower_value(), ty); if (iv.is_lower_open()) { conjs.push_back(arith.mk_lt(lo, var)); } else { conjs.push_back(arith.mk_le(lo, var)); } } if (!iv.plus_infinity()) { expr* hi = arith.mk_numeral(iv.get_upper_value(), ty); if (iv.is_upper_open()) { conjs.push_back(arith.mk_lt(var, hi)); } else { conjs.push_back(arith.mk_le(var, hi)); } } } bool_rewriter br(m); br.mk_and(conjs.size(), conjs.c_ptr(), fml); } void interval_relation::display_index(unsigned i, interval const& j, std::ostream & out) const { out << i << " in " << j << "\n"; } interval_relation_plugin& interval_relation::get_plugin() const { return static_cast(relation_base::get_plugin()); } void interval_relation::mk_intersect(unsigned idx, interval const& i) { bool isempty; (*this)[idx] = mk_intersect((*this)[idx], i, isempty); if (isempty || is_empty(idx, (*this)[idx])) { set_empty(); } } void interval_relation::mk_rename_elem(interval& i, unsigned, unsigned const* ) { } void interval_relation::filter_interpreted(app* cond) { interval_relation_plugin& p = get_plugin(); rational k; unsigned x, y; if (p.is_lt(cond, x, k, y)) { // 0 < x - y + k if (x == UINT_MAX) { // y < k mk_intersect(y, interval(p.dep(), k, true, false, nullptr)); return; } if (y == UINT_MAX) { // -k < x mk_intersect(x, interval(p.dep(), -k, true, true, nullptr)); return; } // y < x + k ext_numeral x_hi = (*this)[x].sup(); ext_numeral y_lo = (*this)[y].inf(); if (!x_hi.is_infinite()) { mk_intersect(y, interval(p.dep(), k + x_hi.to_rational(), true, false, nullptr)); } if (!y_lo.is_infinite()) { mk_intersect(x, interval(p.dep(), y_lo.to_rational() - k, true, true, nullptr)); } return; } bool is_int = false; if (p.is_le(cond, x, k, y, is_int)) { // 0 <= x - y + k if (x == UINT_MAX) { // y <= k mk_intersect(y, interval(p.dep(), k, false, false, nullptr)); return; } if (y == UINT_MAX) { // -k <= x mk_intersect(x, interval(p.dep(), -k, false, true, nullptr)); return; } ext_numeral x_hi = (*this)[x].sup(); ext_numeral y_lo = (*this)[y].inf(); if (!x_hi.is_infinite()) { mk_intersect(y, interval(p.dep(), k + x_hi.to_rational(), false, false, nullptr)); } if (!y_lo.is_infinite()) { mk_intersect(x, interval(p.dep(), y_lo.to_rational() - k, false, true, nullptr)); } return; } if (p.is_eq(cond, x, k, y)) { // y = x + k if (x == UINT_MAX) { SASSERT(y != UINT_MAX); mk_intersect(y, interval(p.dep(), k)); return; } if (y == UINT_MAX) { // x = - k SASSERT(x != UINT_MAX); mk_intersect(x, interval(p.dep(), -k)); return; } interval x_i = (*this)[x]; interval y_i = (*this)[y]; x_i += interval(p.dep(), k); y_i -= interval(p.dep(), k); mk_intersect(x, y_i); mk_intersect(y, x_i); } if (get_plugin().get_ast_manager().is_false(cond)) { set_empty(); } } bool interval_relation_plugin::is_linear(expr* e, unsigned& neg, unsigned& pos, rational& k, bool is_pos) const { #define SET_VAR(_idx_) \ if (is_pos &&pos == UINT_MAX) { \ pos = _idx_; \ return true; \ } \ if (!is_pos && neg == UINT_MAX) { \ neg = _idx_; \ return true; \ } \ else { \ return false; \ } if (is_var(e)) { SET_VAR(to_var(e)->get_idx()); } if (!is_app(e)) { return false; } app* a = to_app(e); if (m_arith.is_add(e)) { for (unsigned i = 0; i < a->get_num_args(); ++i) { if (!is_linear(a->get_arg(i), neg, pos, k, is_pos)) return false; } return true; } if (m_arith.is_sub(e)) { SASSERT(a->get_num_args() == 2); return is_linear(a->get_arg(0), neg, pos, k, is_pos) && is_linear(a->get_arg(1), neg, pos, k, !is_pos); } rational k1; SASSERT(!m_arith.is_mul(e) || a->get_num_args() == 2); if (m_arith.is_mul(e) && m_arith.is_numeral(a->get_arg(0), k1) && k1.is_minus_one() && is_var(a->get_arg(1))) { SET_VAR(to_var(a->get_arg(1))->get_idx()); } if (m_arith.is_numeral(e, k1)) { if (is_pos) { k += k1; } else { k -= k1; } return true; } return false; } // 0 <= x - y + k bool interval_relation_plugin::is_le(app* cond, unsigned& x, rational& k, unsigned& y, bool& is_int) const { ast_manager& m = get_ast_manager(); k.reset(); x = UINT_MAX; y = UINT_MAX; if (m_arith.is_le(cond)) { is_int = m_arith.is_int(cond->get_arg(0)); if (!is_linear(cond->get_arg(0), y, x, k, false)) return false; if (!is_linear(cond->get_arg(1), y, x, k, true)) return false; return (x != UINT_MAX || y != UINT_MAX); } if (m_arith.is_ge(cond)) { is_int = m_arith.is_int(cond->get_arg(0)); if (!is_linear(cond->get_arg(0), y, x, k, true)) return false; if (!is_linear(cond->get_arg(1), y, x, k, false)) return false; return (x != UINT_MAX || y != UINT_MAX); } if (m_arith.is_lt(cond) && m_arith.is_int(cond->get_arg(0))) { is_int = true; if (!is_linear(cond->get_arg(0), y, x, k, false)) return false; if (!is_linear(cond->get_arg(1), y, x, k, true)) return false; k -= rational::one(); return (x != UINT_MAX || y != UINT_MAX); } if (m_arith.is_gt(cond) && m_arith.is_int(cond->get_arg(0))) { is_int = true; if (!is_linear(cond->get_arg(0), y, x, k, true)) return false; if (!is_linear(cond->get_arg(1), y, x, k, false)) return false; k += rational::one(); return (x != UINT_MAX || y != UINT_MAX); } if (m.is_not(cond) && is_app(cond->get_arg(0))) { // not (0 <= x - y + k) // <=> // 0 > x - y + k // <=> // 0 <= y - x - k - 1 if (is_le(to_app(cond->get_arg(0)), x, k, y, is_int) && is_int) { k.neg(); k -= rational::one(); std::swap(x, y); return true; } // not (0 < x - y + k) // <=> // 0 >= x - y + k // <=> // 0 <= y - x - k if (is_lt(to_app(cond->get_arg(0)), x, k, y)) { is_int = false; k.neg(); std::swap(x, y); return true; } } return false; } // 0 < x - y + k bool interval_relation_plugin::is_lt(app* cond, unsigned& x, rational& k, unsigned& y) const { k.reset(); x = UINT_MAX; y = UINT_MAX; if (m_arith.is_lt(cond) && m_arith.is_real(cond->get_arg(0))) { if (!is_linear(cond->get_arg(0), y, x, k, false)) return false; if (!is_linear(cond->get_arg(1), y, x, k, true)) return false; return (x != UINT_MAX || y != UINT_MAX); } if (m_arith.is_gt(cond) && m_arith.is_real(cond->get_arg(0))) { if (!is_linear(cond->get_arg(0), y, x, k, true)) return false; if (!is_linear(cond->get_arg(1), y, x, k, false)) return false; return (x != UINT_MAX || y != UINT_MAX); } return false; } // 0 = x - y + k bool interval_relation_plugin::is_eq(app* cond, unsigned& x, rational& k, unsigned& y) const { ast_manager& m = get_ast_manager(); k.reset(); x = UINT_MAX; y = UINT_MAX; if (m.is_eq(cond)) { if (!is_linear(cond->get_arg(0), y, x, k, false)) return false; if (!is_linear(cond->get_arg(1), y, x, k, true)) return false; return (x != UINT_MAX || y != UINT_MAX); } return false; } }; z3-z3-4.8.7/src/muz/rel/dl_interval_relation.h000066400000000000000000000123601356505360400211660ustar00rootroot00000000000000/*++ Copyright (c) 2010 Microsoft Corporation Module Name: dl_interval_relation.h Abstract: Basic interval reatlion. Author: Nikolaj Bjorner (nbjorner) 2010-2-11 Revision History: --*/ #ifndef DL_INTERVAL_RELATION_H_ #define DL_INTERVAL_RELATION_H_ #include "ast/arith_decl_plugin.h" #include "smt/old_interval.h" #include "muz/base/dl_context.h" #include "muz/rel/dl_relation_manager.h" #include "muz/rel/dl_base.h" #include "muz/rel/dl_vector_relation.h" namespace datalog { class interval_relation; class interval_relation_plugin : public relation_plugin { v_dependency_manager m_dep; interval m_empty; arith_util m_arith; class join_fn; class project_fn; class rename_fn; class union_fn; class filter_equal_fn; class filter_identical_fn; class filter_interpreted_fn; friend class interval_relation; interval unite(interval const& src1, interval const& src2); interval widen(interval const& src1, interval const& src2); interval meet(interval const& src1, interval const& src2, bool& is_empty); v_dependency_manager & dep() const { return const_cast(m_dep); } public: interval_relation_plugin(relation_manager& m); bool can_handle_signature(const relation_signature & s) override; static symbol get_name() { return symbol("interval_relation"); } relation_base * mk_empty(const relation_signature & s) override; relation_base * mk_full(func_decl* p, const relation_signature & s) override; relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) override; relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, const unsigned * removed_cols) override; relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, const unsigned * permutation_cycle) override; relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta) override; relation_union_fn * mk_widen_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta) override; relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, const unsigned * identical_cols) override; relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, unsigned col) override; relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition) override; static bool is_empty(unsigned idx, interval const& i); static bool is_infinite(interval const& i); private: static interval_relation& get(relation_base& r); static interval_relation const & get(relation_base const& r); bool is_linear(expr* e, unsigned& pos, unsigned& neg, rational& k, bool is_pos) const; // x + k <= y bool is_le(app* cond, unsigned& x, rational& k, unsigned& y, bool& is_int) const; // x + k < y bool is_lt(app* cond, unsigned& x, rational& k, unsigned& y) const; // x + k = y bool is_eq(app* cond, unsigned& x, rational& k, unsigned& y) const; }; class interval_relation : public vector_relation { friend class interval_relation_plugin; friend class interval_relation_plugin::filter_equal_fn; public: interval_relation(interval_relation_plugin& p, relation_signature const& s, bool is_empty); void add_fact(const relation_fact & f) override; bool contains_fact(const relation_fact & f) const override; interval_relation * clone() const override; interval_relation * complement(func_decl*) const override; void to_formula(expr_ref& fml) const override; interval_relation_plugin& get_plugin() const; void filter_interpreted(app* cond); bool is_precise() const override { return false; } private: interval mk_intersect(interval const& t1, interval const& t2, bool& is_empty) const override { return get_plugin().meet(t1, t2, is_empty); } interval mk_unite(interval const& t1, interval const& t2) const override { return get_plugin().unite(t1,t2); } interval mk_widen(interval const& t1, interval const& t2) const override { return get_plugin().widen(t1,t2); } bool is_subset_of(interval const& t1, interval const& t2) const override { NOT_IMPLEMENTED_YET(); return false; } bool is_full(interval const& t) const override { return interval_relation_plugin::is_infinite(t); } bool is_empty(unsigned idx, interval const& t) const override { return interval_relation_plugin::is_empty(idx, t); } void mk_rename_elem(interval& i, unsigned col_cnt, unsigned const* cycle) override; void display_index(unsigned idx, interval const & i, std::ostream& out) const override; void mk_intersect(unsigned idx, interval const& i); }; }; #endif z3-z3-4.8.7/src/muz/rel/dl_lazy_table.cpp000066400000000000000000000372251356505360400201350ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: dl_lazy_table.cpp Abstract: Author: Nikolaj Bjorner (nbjorner) 2013-09-04 Revision History: --*/ #include "muz/rel/dl_lazy_table.h" #include "muz/rel/dl_relation_manager.h" #include namespace datalog { // ------------------ // lazy_table_plugin: symbol lazy_table_plugin::mk_name(table_plugin& p) { std::ostringstream strm; strm << "lazy_" << p.get_name(); return symbol(strm.str().c_str()); } table_base * lazy_table_plugin::mk_empty(const table_signature & s) { return alloc(lazy_table, alloc(lazy_table_base, *this, m_plugin.mk_empty(s))); } lazy_table const& lazy_table_plugin::get(table_base const& tb) { return dynamic_cast(tb); } lazy_table& lazy_table_plugin::get(table_base& tb) { return dynamic_cast(tb); } lazy_table const* lazy_table_plugin::get(table_base const* tb) { return dynamic_cast(tb); } lazy_table* lazy_table_plugin::get(table_base* tb) { return dynamic_cast(tb); } // -------------------------- // lazy_table_plugin::join_fn class lazy_table_plugin::join_fn : public convenient_table_join_fn { public: join_fn(table_signature const& s1, table_signature const& s2, unsigned col_cnt, unsigned const* cols1, unsigned const* cols2): convenient_table_join_fn(s1, s2, col_cnt, cols1, cols2) {} table_base* operator()(const table_base& _t1, const table_base& _t2) override { lazy_table const& t1 = get(_t1); lazy_table const& t2 = get(_t2); lazy_table_ref* tr = alloc(lazy_table_join, m_cols1.size(), m_cols1.c_ptr(), m_cols2.c_ptr(), t1, t2, get_result_signature()); return alloc(lazy_table, tr); } }; table_join_fn * lazy_table_plugin::mk_join_fn( const table_base & t1, const table_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { if (check_kind(t1) && check_kind(t2)) { return alloc(join_fn, t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2); } else { return nullptr; } } // ------------------------ // lazy_table_plugin::union class lazy_table_plugin::union_fn : public table_union_fn { public: void operator()(table_base & _tgt, const table_base & _src, table_base * _delta) override { lazy_table& tgt = get(_tgt); lazy_table const& src = get(_src); lazy_table* delta = get(_delta); table_base const* t_src = src.eval(); table_base * t_tgt = tgt.eval(); table_base * t_delta = delta?delta->eval():nullptr; verbose_action _t("union"); table_union_fn* m = tgt.get_lplugin().get_manager().mk_union_fn(*t_tgt, *t_src, t_delta); SASSERT(m); (*m)(*t_tgt, *t_src, t_delta); dealloc(m); } }; table_union_fn* lazy_table_plugin::mk_union_fn( const table_base & tgt, const table_base & src, const table_base * delta) { if (check_kind(tgt) && check_kind(src) && (!delta || check_kind(*delta))) { return alloc(union_fn); } else { return nullptr; } } // -------------------------- // lazy_table_plugin::project class lazy_table_plugin::project_fn : public convenient_table_project_fn { public: project_fn(table_signature const& orig_sig, unsigned cnt, unsigned const* cols): convenient_table_project_fn(orig_sig, cnt, cols) {} table_base* operator()(table_base const& _t) override { lazy_table const& t = get(_t); return alloc(lazy_table, alloc(lazy_table_project, m_removed_cols.size(), m_removed_cols.c_ptr(), t, get_result_signature())); } }; table_transformer_fn * lazy_table_plugin::mk_project_fn( const table_base & t, unsigned col_cnt, const unsigned * removed_cols) { if (check_kind(t)) { return alloc(project_fn, t.get_signature(), col_cnt, removed_cols); } else { return nullptr; } } // ------------------------- // lazy_table_plugin::rename class lazy_table_plugin::rename_fn : public convenient_table_rename_fn { public: rename_fn(table_signature const& orig_sig, unsigned cnt, unsigned const* cols): convenient_table_rename_fn(orig_sig, cnt, cols) {} table_base* operator()(table_base const& _t) override { lazy_table const& t = get(_t); return alloc(lazy_table, alloc(lazy_table_rename, m_cycle.size(), m_cycle.c_ptr(), t, get_result_signature())); } }; table_transformer_fn * lazy_table_plugin::mk_rename_fn( const table_base & t, unsigned col_cnt, const unsigned * removed_cols) { if (check_kind(t)) { return alloc(rename_fn, t.get_signature(), col_cnt, removed_cols); } else { return nullptr; } } // ----------------------------------- // lazy_table_plugin::filter_identical class lazy_table_plugin::filter_identical_fn : public table_mutator_fn { unsigned_vector m_cols; public: filter_identical_fn(unsigned cnt, unsigned const* cols): m_cols(cnt, cols) {} void operator()(table_base& _t) override { lazy_table& t = get(_t); t.set(alloc(lazy_table_filter_identical, m_cols.size(), m_cols.c_ptr(), t)); } }; table_mutator_fn * lazy_table_plugin::mk_filter_identical_fn( const table_base & t, unsigned col_cnt, const unsigned * identical_cols) { if (check_kind(t)) { return alloc(filter_identical_fn, col_cnt, identical_cols); } else { return nullptr; } } // ------------------------------------- // lazy_table_plugin::filter_interpreted class lazy_table_plugin::filter_interpreted_fn : public table_mutator_fn { app_ref m_condition; public: filter_interpreted_fn(app_ref& p): m_condition(p) {} void operator()(table_base& _t) override { lazy_table& t = get(_t); t.set(alloc(lazy_table_filter_interpreted, t, m_condition)); } }; table_mutator_fn * lazy_table_plugin::mk_filter_interpreted_fn( const table_base & t, app* condition) { if (check_kind(t)) { app_ref cond(condition, get_ast_manager()); return alloc(filter_interpreted_fn, cond); } else { return nullptr; } } // ------------------------------------- // lazy_table_plugin::filter_by_negation class lazy_table_plugin::filter_by_negation_fn : public table_intersection_filter_fn { unsigned_vector m_cols1; unsigned_vector m_cols2; public: filter_by_negation_fn(unsigned cnt, unsigned const* cols1, unsigned const* cols2): m_cols1(cnt, cols1), m_cols2(cnt, cols2) {} void operator()(table_base & _t, const table_base & _intersected_obj) override { lazy_table& t = get(_t); lazy_table const& it = get(_intersected_obj); t.set(alloc(lazy_table_filter_by_negation, t, it, m_cols1, m_cols2)); } }; table_intersection_filter_fn * lazy_table_plugin::mk_filter_by_negation_fn( const table_base & t, const table_base & negated_obj, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) { if (check_kind(t) && check_kind(negated_obj)) { return alloc(filter_by_negation_fn, joined_col_cnt, t_cols, negated_cols); } else { return nullptr; } } // ------------------------------- // lazy_table_plugin::filter_equal class lazy_table_plugin::filter_equal_fn : public table_mutator_fn { table_element m_value; unsigned m_col; public: filter_equal_fn(const table_element & value, unsigned col): m_value(value), m_col(col) { } void operator()(table_base& _t) override { lazy_table& t = get(_t); t.set(alloc(lazy_table_filter_equal, m_col, m_value, t)); } }; table_mutator_fn * lazy_table_plugin::mk_filter_equal_fn( const table_base & t, const table_element & value, unsigned col) { if (check_kind(t)) { return alloc(filter_equal_fn, value, col); } else { return nullptr; } } table_plugin* lazy_table_plugin::mk_sparse(relation_manager& rm) { table_plugin* sp = rm.get_table_plugin(symbol("sparse")); SASSERT(sp); if (sp) { return alloc(lazy_table_plugin, *sp); } else { return nullptr; } } // ---------- // lazy_table table_base * lazy_table::clone() const { table_base* t = eval(); verbose_action _t("clone"); return alloc(lazy_table, alloc(lazy_table_base, get_lplugin(), t->clone())); } table_base * lazy_table::complement(func_decl* p, const table_element * func_columns) const { table_base* t = eval()->complement(p, func_columns); return alloc(lazy_table, alloc(lazy_table_base, get_lplugin(), t)); } bool lazy_table::empty() const { return m_ref->eval()->empty(); } bool lazy_table::contains_fact(const table_fact & f) const { return m_ref->eval()->contains_fact(f); } void lazy_table::remove_fact(table_element const* fact) { m_ref->eval()->remove_fact(fact); } void lazy_table::remove_facts(unsigned fact_cnt, const table_fact * facts) { m_ref->eval()->remove_facts(fact_cnt, facts); } void lazy_table::remove_facts(unsigned fact_cnt, const table_element * facts) { m_ref->eval()->remove_facts(fact_cnt, facts); } void lazy_table::reset() { m_ref = alloc(lazy_table_base, get_lplugin(), get_lplugin().m_plugin.mk_empty(get_signature())); } void lazy_table::add_fact(table_fact const& f) { m_ref->eval()->add_fact(f); } table_base::iterator lazy_table::begin() const { return eval()->begin(); } table_base::iterator lazy_table::end() const { return eval()->end(); } table_base* lazy_table::eval() const { return m_ref->eval(); } // ------------------------- // eval table_base* lazy_table_join::force() { SASSERT(!m_table); table_base* t1 = m_t1->eval(); table_base* t2 = m_t2->eval(); verbose_action _t("join"); table_join_fn* join = rm().mk_join_fn(*t1, *t2, m_cols1.size(), m_cols1.c_ptr(), m_cols2.c_ptr()); m_table = (*join)(*t1, *t2); dealloc(join); return m_table.get(); } table_base* lazy_table_project::force() { SASSERT(!m_table); switch(m_src->kind()) { case LAZY_TABLE_JOIN: { lazy_table_join& src = dynamic_cast(*m_src); table_base* t1 = src.t1()->eval(); table_base* t2 = src.t2()->eval(); table_join_fn* j_fn = rm().mk_join_project_fn(*t1, *t2, src.cols1(), src.cols2(), m_cols); if (j_fn) { verbose_action _t("join_project"); m_table = (*j_fn)(*t1, *t2); dealloc(j_fn); } break; } case LAZY_TABLE_FILTER_INTERPRETED: { lazy_table_filter_interpreted& src = dynamic_cast(*m_src); table_transformer_fn* tr = rm().mk_filter_interpreted_and_project_fn(*src.eval(), src.condition(), m_cols.size(), m_cols.c_ptr()); if (tr) { verbose_action _t("filter_interpreted_project"); m_table = (*tr)(*src.eval()); dealloc(tr); } break; } case LAZY_TABLE_FILTER_EQUAL: { lazy_table_filter_equal& src = dynamic_cast(*m_src); table_base* t = src.eval(); table_transformer_fn* tr = rm().mk_select_equal_and_project_fn(*t, src.value(), src.col()); if (tr) { verbose_action _t("select_equal_project"); m_table = (*tr)(*t); dealloc(tr); } break; } default: break; } if (m_table) { return m_table.get(); } table_base* src = m_src->eval(); verbose_action _t("project"); table_transformer_fn* project = rm().mk_project_fn(*src, m_cols.size(), m_cols.c_ptr()); SASSERT(project); m_table = (*project)(*src); dealloc(project); return m_table.get(); } table_base* lazy_table_rename::force() { SASSERT(!m_table); table_base* src = m_src->eval(); verbose_action _t("rename"); table_transformer_fn* rename = rm().mk_rename_fn(*src, m_cols.size(), m_cols.c_ptr()); m_table = (*rename)(*src); dealloc(rename); return m_table.get(); } table_base* lazy_table_filter_identical::force() { SASSERT(!m_table); m_table = m_src->eval(); m_src->release_table(); m_src = nullptr; verbose_action _t("filter_identical"); table_mutator_fn* m = rm().mk_filter_identical_fn(*m_table, m_cols.size(), m_cols.c_ptr()); SASSERT(m); (*m)(*m_table); dealloc(m); return m_table.get(); } table_base* lazy_table_filter_equal::force() { SASSERT(!m_table); m_table = m_src->eval(); m_src->release_table(); m_src = nullptr; verbose_action _t("filter_equal"); table_mutator_fn* m = rm().mk_filter_equal_fn(*m_table, m_value, m_col); SASSERT(m); (*m)(*m_table); dealloc(m); return m_table.get(); } table_base* lazy_table_filter_interpreted::force() { SASSERT(!m_table); m_table = m_src->eval(); m_src->release_table(); m_src = nullptr; verbose_action _t("filter_interpreted"); table_mutator_fn* m = rm().mk_filter_interpreted_fn(*m_table, m_condition); SASSERT(m); (*m)(*m_table); dealloc(m); return m_table.get(); } table_base* lazy_table_filter_by_negation::force() { SASSERT(!m_table); m_table = m_tgt->eval(); m_tgt->release_table(); m_tgt = nullptr; switch(m_src->kind()) { case LAZY_TABLE_JOIN: { lazy_table_join& src = dynamic_cast(*m_src); table_base* t1 = src.t1()->eval(); table_base* t2 = src.t2()->eval(); verbose_action _t("filter_by_negation_join"); table_intersection_join_filter_fn* jn = rm().mk_filter_by_negated_join_fn(*m_table, *t1, *t2, cols1(), cols2(), src.cols1(), src.cols2()); if (jn) { (*jn)(*m_table, *t1, *t2); dealloc(jn); return m_table.get(); } break; } default: break; } table_base* src = m_src->eval(); verbose_action _t("filter_by_negation"); table_intersection_filter_fn* m = rm().mk_filter_by_negation_fn(*m_table, *src, m_cols1, m_cols2); SASSERT(m); (*m)(*m_table, *src); dealloc(m); return m_table.get(); } } z3-z3-4.8.7/src/muz/rel/dl_lazy_table.h000066400000000000000000000266421356505360400176030ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: dl_lazy_table.h Abstract: Structure for delaying table operations. Author: Nikolaj Bjorner (nbjorner) 2013-09-04 Revision History: --*/ #ifndef DL_LAZY_TABLE_H_ #define DL_LAZY_TABLE_H_ #include "muz/rel/dl_base.h" #include "util/ref.h" namespace datalog { class lazy_table; class lazy_table_plugin : public table_plugin { friend class lazy_table; class join_fn; class project_fn; class union_fn; class rename_fn; class filter_equal_fn; class filter_identical_fn; class filter_interpreted_fn; class filter_by_negation_fn; table_plugin& m_plugin; static symbol mk_name(table_plugin& p); public: lazy_table_plugin(table_plugin& p): table_plugin(mk_name(p), p.get_manager()), m_plugin(p) {} bool can_handle_signature(const table_signature & s) override { return m_plugin.can_handle_signature(s); } table_base * mk_empty(const table_signature & s) override; static table_plugin* mk_sparse(relation_manager& rm); protected: table_join_fn * mk_join_fn( const table_base & t1, const table_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) override; table_union_fn * mk_union_fn( const table_base & tgt, const table_base & src, const table_base * delta) override; table_transformer_fn * mk_project_fn( const table_base & t, unsigned col_cnt, const unsigned * removed_cols) override; table_transformer_fn * mk_rename_fn( const table_base & t, unsigned permutation_cycle_len, const unsigned * permutation_cycle) override; table_mutator_fn * mk_filter_identical_fn( const table_base & t, unsigned col_cnt, const unsigned * identical_cols) override; table_mutator_fn * mk_filter_equal_fn( const table_base & t, const table_element & value, unsigned col) override; table_mutator_fn * mk_filter_interpreted_fn( const table_base & t, app * condition) override; table_intersection_filter_fn * mk_filter_by_negation_fn( const table_base & t, const table_base & negated_obj, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) override; static lazy_table const& get(table_base const& tb); static lazy_table& get(table_base& tb); static lazy_table const* get(table_base const* tb); static lazy_table* get(table_base* tb); }; enum lazy_table_kind { LAZY_TABLE_BASE, LAZY_TABLE_JOIN, LAZY_TABLE_PROJECT, LAZY_TABLE_RENAME, LAZY_TABLE_FILTER_IDENTICAL, LAZY_TABLE_FILTER_EQUAL, LAZY_TABLE_FILTER_INTERPRETED, LAZY_TABLE_FILTER_BY_NEGATION }; class lazy_table_ref { protected: lazy_table_plugin& m_plugin; table_signature m_signature; unsigned m_ref; scoped_rel m_table; relation_manager& rm() { return m_plugin.get_manager(); } virtual table_base* force() = 0; public: lazy_table_ref(lazy_table_plugin& p, table_signature const& sig): m_plugin(p), m_signature(sig), m_ref(0) {} virtual ~lazy_table_ref() {} void inc_ref() { ++m_ref; } void dec_ref() { --m_ref; if (0 == m_ref) dealloc(this); } void release_table() { m_table.release(); } virtual lazy_table_kind kind() const = 0; table_signature const& get_signature() const { return m_signature; } lazy_table_plugin & get_lplugin() const { return m_plugin; } table_base* eval() { if (!m_table) { m_table = force(); } SASSERT(m_table); return m_table.get(); } }; class lazy_table : public table_base { protected: mutable ref m_ref; public: lazy_table(lazy_table_ref* t): table_base(t->get_lplugin(), t->get_signature()), m_ref(t) {} ~lazy_table() override {} lazy_table_plugin& get_lplugin() const { return dynamic_cast(table_base::get_plugin()); } table_base * clone() const override; table_base * complement(func_decl* p, const table_element * func_columns = nullptr) const override; bool empty() const override; bool contains_fact(const table_fact & f) const override; void remove_fact(table_element const* fact) override; void remove_facts(unsigned fact_cnt, const table_fact * facts) override; void remove_facts(unsigned fact_cnt, const table_element * facts) override; void reset() override; void add_fact(table_fact const& f) override; unsigned get_size_estimate_rows() const override { return 1; } unsigned get_size_estimate_bytes() const override { return 1; } bool knows_exact_size() const override { return false; } table_base* eval() const; table_base::iterator begin() const override; table_base::iterator end() const override; lazy_table_ref* get_ref() const { return m_ref.get(); } void set(lazy_table_ref* r) { m_ref = r; } }; class lazy_table_base : public lazy_table_ref { public: lazy_table_base(lazy_table_plugin & p, table_base* table) : lazy_table_ref(p, table->get_signature()) { m_table = table; // SASSERT(&p.m_plugin == &table->get_lplugin()); } ~lazy_table_base() override {} lazy_table_kind kind() const override { return LAZY_TABLE_BASE; } table_base* force() override { return m_table.get(); } }; class lazy_table_join : public lazy_table_ref { unsigned_vector m_cols1; unsigned_vector m_cols2; ref m_t1; ref m_t2; public: lazy_table_join(unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, lazy_table const& t1, lazy_table const& t2, table_signature const& sig) : lazy_table_ref(t1.get_lplugin(), sig), m_cols1(col_cnt, cols1), m_cols2(col_cnt, cols2), m_t1(t1.get_ref()), m_t2(t2.get_ref()) { } ~lazy_table_join() override {} lazy_table_kind kind() const override { return LAZY_TABLE_JOIN; } unsigned_vector const& cols1() const { return m_cols1; } unsigned_vector const& cols2() const { return m_cols2; } lazy_table_ref* t1() const { return m_t1.get(); } lazy_table_ref* t2() const { return m_t2.get(); } table_base* force() override; }; class lazy_table_project : public lazy_table_ref { unsigned_vector m_cols; ref m_src; public: lazy_table_project(unsigned col_cnt, const unsigned * cols, lazy_table const& src, table_signature const& sig) : lazy_table_ref(src.get_lplugin(), sig), m_cols(col_cnt, cols), m_src(src.get_ref()) {} ~lazy_table_project() override {} lazy_table_kind kind() const override { return LAZY_TABLE_PROJECT; } unsigned_vector const& cols() const { return m_cols; } lazy_table_ref* src() const { return m_src.get(); } table_base* force() override; }; class lazy_table_rename : public lazy_table_ref { unsigned_vector m_cols; ref m_src; public: lazy_table_rename(unsigned col_cnt, const unsigned * cols, lazy_table const& src, table_signature const& sig) : lazy_table_ref(src.get_lplugin(), sig), m_cols(col_cnt, cols), m_src(src.get_ref()) {} ~lazy_table_rename() override {} lazy_table_kind kind() const override { return LAZY_TABLE_RENAME; } unsigned_vector const& cols() const { return m_cols; } lazy_table_ref* src() const { return m_src.get(); } table_base* force() override; }; class lazy_table_filter_identical : public lazy_table_ref { unsigned_vector m_cols; ref m_src; public: lazy_table_filter_identical(unsigned col_cnt, const unsigned * cols, lazy_table const& src) : lazy_table_ref(src.get_lplugin(), src.get_signature()), m_cols(col_cnt, cols), m_src(src.get_ref()) {} ~lazy_table_filter_identical() override {} lazy_table_kind kind() const override { return LAZY_TABLE_FILTER_IDENTICAL; } unsigned_vector const& cols() const { return m_cols; } lazy_table_ref* src() const { return m_src.get(); } table_base* force() override; }; class lazy_table_filter_equal : public lazy_table_ref { unsigned m_col; table_element m_value; ref m_src; public: lazy_table_filter_equal(unsigned col, table_element value, lazy_table const& src) : lazy_table_ref(src.get_lplugin(), src.get_signature()), m_col(col), m_value(value), m_src(src.get_ref()) {} ~lazy_table_filter_equal() override {} lazy_table_kind kind() const override { return LAZY_TABLE_FILTER_EQUAL; } unsigned col() const { return m_col; } table_element value() const { return m_value; } lazy_table_ref* src() const { return m_src.get(); } table_base* force() override; }; class lazy_table_filter_interpreted : public lazy_table_ref { app_ref m_condition; ref m_src; public: lazy_table_filter_interpreted(lazy_table const& src, app* condition) : lazy_table_ref(src.get_lplugin(), src.get_signature()), m_condition(condition, src.get_lplugin().get_ast_manager()), m_src(src.get_ref()) {} ~lazy_table_filter_interpreted() override {} lazy_table_kind kind() const override { return LAZY_TABLE_FILTER_INTERPRETED; } app* condition() const { return m_condition; } lazy_table_ref* src() const { return m_src.get(); } table_base* force() override; }; class lazy_table_filter_by_negation : public lazy_table_ref { ref m_tgt; ref m_src; unsigned_vector m_cols1; unsigned_vector m_cols2; public: lazy_table_filter_by_negation(lazy_table const& tgt, lazy_table const& src, unsigned_vector const& c1, unsigned_vector const& c2) : lazy_table_ref(tgt.get_lplugin(), tgt.get_signature()), m_tgt(tgt.get_ref()), m_src(src.get_ref()), m_cols1(c1), m_cols2(c2) {} ~lazy_table_filter_by_negation() override {} lazy_table_kind kind() const override { return LAZY_TABLE_FILTER_BY_NEGATION; } lazy_table_ref* tgt() const { return m_tgt.get(); } lazy_table_ref* src() const { return m_src.get(); } unsigned_vector const& cols1() const { return m_cols1; } unsigned_vector const& cols2() const { return m_cols2; } table_base* force() override; }; } #endif z3-z3-4.8.7/src/muz/rel/dl_mk_explanations.cpp000066400000000000000000001023101356505360400211670ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_mk_explanations.cpp Abstract: Author: Krystof Hoder (t-khoder) 2010-11-08. Revision History: --*/ #include #include "ast/ast_pp.h" #include "ast/ast_smt_pp.h" #include "muz/rel/dl_finite_product_relation.h" #include "muz/rel/dl_product_relation.h" #include "muz/rel/dl_sieve_relation.h" #include "muz/rel/dl_mk_explanations.h" namespace datalog { // ----------------------------------- // // explanation_relation_plugin declaration // // ----------------------------------- class explanation_relation; class explanation_relation_plugin : public relation_plugin { friend class explanation_relation; class join_fn; class project_fn; class rename_fn; class union_fn; class foreign_union_fn; class assignment_filter_fn; class negation_filter_fn; class intersection_filter_fn; bool m_relation_level_explanations; func_decl_ref m_union_decl; vector > m_pool; app * mk_union(app * a1, app * a2) { return get_ast_manager().mk_app(m_union_decl, a1, a2); } public: static symbol get_name(bool relation_level) { return symbol(relation_level ? "relation_explanation" : "fact_explanation"); } explanation_relation_plugin(bool relation_level, relation_manager & manager) : relation_plugin(get_name(relation_level), manager), m_relation_level_explanations(relation_level), m_union_decl(mk_explanations::get_union_decl(get_context()), get_ast_manager()) {} ~explanation_relation_plugin() override { for (unsigned i = 0; i < m_pool.size(); ++i) { for (unsigned j = 0; j < m_pool[i].size(); ++j) { dealloc(m_pool[i][j]); } } } bool can_handle_signature(const relation_signature & s) override { unsigned n=s.size(); for (unsigned i=0; i(relation_base::get_plugin()); } void to_formula(expr_ref& fml) const override { ast_manager& m = fml.get_manager(); fml = m.mk_eq(m.mk_var(0, m.get_sort(m_data[0])), m_data[0]); } bool is_undefined(unsigned col_idx) const { return m_data[col_idx]==nullptr; } bool no_undefined() const { if (empty()) { return true; } unsigned n = get_signature().size(); for (unsigned i=0; i(get_plugin().mk_empty(get_signature())); res->m_empty = m_empty; SASSERT(res->m_data.empty()); res->m_data.append(m_data); return res; } relation_base * complement(func_decl* pred) const override { explanation_relation * res = static_cast(get_plugin().mk_empty(get_signature())); if (empty()) { res->set_undefined(); } return res; } void display_explanation(app * expl, std::ostream & out) const { if (expl) { //TODO: some nice explanation output ast_smt_pp pp(get_plugin().get_ast_manager()); pp.display_expr_smt2(out, expl); } else { out << ""; } } void display(std::ostream & out) const override { if (empty()) { out << "\n"; return; } unsigned sz = get_signature().size(); for (unsigned i=0; i s.size() && !m_pool[s.size()].empty()) { explanation_relation* r = m_pool[s.size()].back(); m_pool[s.size()].pop_back(); r->m_empty = true; r->m_data.reset(); return r; } return alloc(explanation_relation, *this, s); } void explanation_relation_plugin::recycle(explanation_relation* r) { relation_signature const& sig = r->get_signature(); if (m_pool.size() <= sig.size()) { m_pool.resize(sig.size()+1); } m_pool[sig.size()].push_back(r); } class explanation_relation_plugin::join_fn : public convenient_relation_join_fn { public: join_fn(const relation_signature & sig1, const relation_signature & sig2) : convenient_relation_join_fn(sig1, sig2, 0, nullptr, nullptr) {} relation_base * operator()(const relation_base & r1_0, const relation_base & r2_0) override { const explanation_relation & r1 = static_cast(r1_0); const explanation_relation & r2 = static_cast(r2_0); explanation_relation_plugin & plugin = r1.get_plugin(); explanation_relation * res = static_cast(plugin.mk_empty(get_result_signature())); if (!r1.empty() && !r2.empty()) { res->m_empty = false; SASSERT(res->m_data.empty()); res->m_data.append(r1.m_data); res->m_data.append(r2.m_data); } return res; } }; relation_join_fn * explanation_relation_plugin::mk_join_fn(const relation_base & r1, const relation_base & r2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { if (&r1.get_plugin()!=this || &r2.get_plugin()!=this) { return nullptr; } if (col_cnt!=0) { return nullptr; } return alloc(join_fn, r1.get_signature(), r2.get_signature()); } class explanation_relation_plugin::project_fn : public convenient_relation_project_fn { public: project_fn(const relation_signature & sig, unsigned col_cnt, const unsigned * removed_cols) : convenient_relation_project_fn(sig, col_cnt, removed_cols) {} relation_base * operator()(const relation_base & r0) override { const explanation_relation & r = static_cast(r0); explanation_relation_plugin & plugin = r.get_plugin(); explanation_relation * res = static_cast(plugin.mk_empty(get_result_signature())); if (!r.empty()) { relation_fact proj_data = r.m_data; project_out_vector_columns(proj_data, m_removed_cols); res->assign_data(proj_data); } return res; } }; relation_transformer_fn * explanation_relation_plugin::mk_project_fn(const relation_base & r, unsigned col_cnt, const unsigned * removed_cols) { if (&r.get_plugin()!=this) { return nullptr; } return alloc(project_fn, r.get_signature(), col_cnt, removed_cols); } class explanation_relation_plugin::rename_fn : public convenient_relation_rename_fn { public: rename_fn(const relation_signature & sig, unsigned permutation_cycle_len, const unsigned * permutation_cycle) : convenient_relation_rename_fn(sig, permutation_cycle_len, permutation_cycle) {} relation_base * operator()(const relation_base & r0) override { const explanation_relation & r = static_cast(r0); explanation_relation_plugin & plugin = r.get_plugin(); explanation_relation * res = static_cast(plugin.mk_empty(get_result_signature())); if (!r.empty()) { relation_fact permutated_data = r.m_data; permutate_by_cycle(permutated_data, m_cycle); res->assign_data(permutated_data); } return res; } }; relation_transformer_fn * explanation_relation_plugin::mk_rename_fn(const relation_base & r, unsigned permutation_cycle_len, const unsigned * permutation_cycle) { return alloc(rename_fn, r.get_signature(), permutation_cycle_len, permutation_cycle); } class explanation_relation_plugin::union_fn : public relation_union_fn { scoped_ptr m_delta_union_fun; public: void operator()(relation_base & tgt0, const relation_base & src0, relation_base * delta0) override { explanation_relation & tgt = static_cast(tgt0); const explanation_relation & src = static_cast(src0); explanation_relation * delta = delta0 ? static_cast(delta0) : nullptr; explanation_relation_plugin & plugin = tgt.get_plugin(); if (!src.no_undefined() || !tgt.no_undefined() || (delta && !delta->no_undefined())) { UNREACHABLE(); } if (src.empty()) { return; } if (plugin.m_relation_level_explanations) { tgt.unite_with_data(src.m_data); if (delta) { if (!m_delta_union_fun) { m_delta_union_fun = plugin.get_manager().mk_union_fn(*delta, src); SASSERT(m_delta_union_fun); } (*m_delta_union_fun)(*delta, src); } } else { if (tgt.empty()) { tgt.assign_data(src.m_data); if (delta && delta->empty()) { delta->assign_data(src.m_data); } } } } }; class explanation_relation_plugin::foreign_union_fn : public relation_union_fn { scoped_ptr m_delta_union_fun; public: void operator()(relation_base & tgt0, const relation_base & src, relation_base * delta0) override { explanation_relation & tgt = static_cast(tgt0); explanation_relation * delta = delta0 ? static_cast(delta0) : nullptr; if (src.empty()) { return; } tgt.set_undefined(); if (delta) { delta->set_undefined(); } } }; relation_union_fn * explanation_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta) { if (!check_kind(tgt) || (delta && !check_kind(*delta))) { return nullptr; } if (!check_kind(src)) { //this is to handle the product relation return alloc(foreign_union_fn); } return alloc(union_fn); } class explanation_relation_plugin::assignment_filter_fn : public relation_mutator_fn { ast_manager & m_manager; var_subst & m_subst; unsigned m_col_idx; app_ref m_new_rule; public: assignment_filter_fn(context & ctx, unsigned col_idx, app_ref new_rule) : m_manager(ctx.get_manager()), m_subst(ctx.get_var_subst()), m_col_idx(col_idx), m_new_rule(std::move(new_rule)) {} void operator()(relation_base & r0) override { explanation_relation & r = static_cast(r0); if (!r.is_undefined(m_col_idx)) { UNREACHABLE(); } unsigned sz = r.get_signature().size(); ptr_vector subst_arg; subst_arg.resize(sz); unsigned ofs = sz-1; for (unsigned i=0; iget_arg(0); expr * arg2 = cond->get_arg(1); if (is_var(arg2)) { std::swap(arg1, arg2); } if (!is_var(arg1) || !is_app(arg2)) { TRACE("dl", tout << "not variable assignemnt\n";); return nullptr; } var * col_var = to_var(arg1); app * new_rule = to_app(arg2); if (!get_context().get_decl_util().is_rule_sort(col_var->get_sort())) { TRACE("dl", tout << "not rule sort\n";); return nullptr; } unsigned col_idx = col_var->get_idx(); return alloc(assignment_filter_fn, get_context(), col_idx, app_ref(new_rule, get_ast_manager())); } class explanation_relation_plugin::negation_filter_fn : public relation_intersection_filter_fn { public: void operator()(relation_base & r, const relation_base & neg) override { if (!neg.empty()) { r.reset(); } } }; relation_intersection_filter_fn * explanation_relation_plugin::mk_filter_by_negation_fn(const relation_base & r, const relation_base & neg, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) { if (&r.get_plugin()!=this || &neg.get_plugin()!=this) { return nullptr; } return alloc(negation_filter_fn); } class explanation_relation_plugin::intersection_filter_fn : public relation_intersection_filter_fn { func_decl_ref m_union_decl; public: intersection_filter_fn(explanation_relation_plugin & plugin) : m_union_decl(plugin.m_union_decl) {} void operator()(relation_base & tgt0, const relation_base & src0) override { explanation_relation & tgt = static_cast(tgt0); const explanation_relation & src = static_cast(src0); if (src.empty()) { tgt.reset(); return; } if (tgt.empty()) { return; } unsigned sz = tgt.get_signature().size(); for (unsigned i=0; iget_decl()==m_union_decl.get()) { if (curr_tgt->get_arg(0)==curr_src || curr_tgt->get_arg(1)==curr_src) { tgt.m_data.set(i, curr_src); continue; } } //the intersection is imprecise because we do nothing here, but it is good enough for //the purpose of explanations } } }; relation_intersection_filter_fn * explanation_relation_plugin::mk_filter_by_intersection_fn( const relation_base & tgt, const relation_base & src, unsigned joined_col_cnt, const unsigned * tgt_cols, const unsigned * src_cols) { if (&tgt.get_plugin()!=this || &src.get_plugin()!=this) { return nullptr; } //this checks the join is one to one on all columns if (tgt.get_signature()!=src.get_signature() || joined_col_cnt!=tgt.get_signature().size() || !containers_equal(tgt_cols, tgt_cols+joined_col_cnt, src_cols, src_cols+joined_col_cnt)) { return nullptr; } counter ctr; ctr.count(joined_col_cnt, tgt_cols); if (ctr.get_max_counter_value()>1 || (joined_col_cnt && ctr.get_max_positive()!=joined_col_cnt-1)) { return nullptr; } return alloc(intersection_filter_fn, *this); } // ----------------------------------- // // mk_explanations // // ----------------------------------- mk_explanations::mk_explanations(context & ctx) : plugin(50000), m_manager(ctx.get_manager()), m_context(ctx), m_decl_util(ctx.get_decl_util()), m_relation_level(ctx.explanations_on_relation_level()), m_pinned(m_manager) { m_e_sort = m_decl_util.mk_rule_sort(); m_pinned.push_back(m_e_sort); relation_manager & rmgr = ctx.get_rel_context()->get_rmanager(); symbol er_symbol = explanation_relation_plugin::get_name(m_relation_level); m_er_plugin = static_cast(rmgr.get_relation_plugin(er_symbol)); if (!m_er_plugin) { m_er_plugin = alloc(explanation_relation_plugin, m_relation_level, rmgr); rmgr.register_plugin(m_er_plugin); if (!m_relation_level) { DEBUG_CODE( finite_product_relation_plugin * dummy; SASSERT(!rmgr.try_get_finite_product_relation_plugin(*m_er_plugin, dummy)); ); rmgr.register_plugin(alloc(finite_product_relation_plugin, *m_er_plugin, rmgr)); } } DEBUG_CODE( if (!m_relation_level) { finite_product_relation_plugin * dummy; SASSERT(rmgr.try_get_finite_product_relation_plugin(*m_er_plugin, dummy)); } ); } func_decl * mk_explanations::get_union_decl(context & ctx) { ast_manager & m = ctx.get_manager(); sort_ref s(ctx.get_decl_util().mk_rule_sort(), m); //can it happen that the function name would collide with some other symbol? //if functions can be overloaded by their ranges, it should be fine. return m.mk_func_decl(symbol("e_union"), s, s, s); } void mk_explanations::assign_rel_level_kind(func_decl * e_decl, func_decl * orig) { SASSERT(m_relation_level); relation_manager & rmgr = m_context.get_rel_context()->get_rmanager(); unsigned sz = e_decl->get_arity(); relation_signature sig; rmgr.from_predicate(e_decl, sig); svector inner_sieve(sz-1, true); inner_sieve.push_back(false); svector expl_sieve(sz-1, false); expl_sieve.push_back(true); sieve_relation_plugin & sieve_plugin = sieve_relation_plugin::get_plugin(rmgr); family_id inner_kind = rmgr.get_requested_predicate_kind(orig); //may be null_family_id family_id inner_sieve_kind = sieve_plugin.get_relation_kind(sig, inner_sieve, inner_kind); family_id expl_kind = m_er_plugin->get_kind(); family_id expl_sieve_kind = sieve_plugin.get_relation_kind(sig, expl_sieve, expl_kind); rel_spec product_spec; product_spec.push_back(inner_sieve_kind); product_spec.push_back(expl_sieve_kind); family_id pred_kind = product_relation_plugin::get_plugin(rmgr).get_relation_kind(sig, product_spec); rmgr.set_predicate_kind(e_decl, pred_kind); } func_decl * mk_explanations::get_e_decl(func_decl * orig_decl) { decl_map::obj_map_entry * e = m_e_decl_map.insert_if_not_there2(orig_decl, 0); if (e->get_data().m_value==0) { relation_signature e_domain; e_domain.append(orig_decl->get_arity(), orig_decl->get_domain()); e_domain.push_back(m_e_sort); func_decl * new_decl = m_context.mk_fresh_head_predicate(orig_decl->get_name(), symbol("expl"), e_domain.size(), e_domain.c_ptr(), orig_decl); m_pinned.push_back(new_decl); e->get_data().m_value = new_decl; if (m_relation_level) { assign_rel_level_kind(new_decl, orig_decl); } } return e->get_data().m_value; } app * mk_explanations::get_e_lit(app * lit, unsigned e_var_idx) { expr_ref_vector args(m_manager); func_decl * e_decl = get_e_decl(lit->get_decl()); args.append(lit->get_num_args(), lit->get_args()); args.push_back(m_manager.mk_var(e_var_idx, m_e_sort)); return m_manager.mk_app(e_decl, args.c_ptr()); } symbol mk_explanations::get_rule_symbol(rule * r) { if (r->name() == symbol::null) { std::stringstream sstm; r->display(m_context, sstm); std::string res = sstm.str(); res = res.substr(0, res.find_last_not_of('\n')+1); return symbol(res.c_str()); } else { return r->name(); } } rule * mk_explanations::get_e_rule(rule * r) { rule_counter ctr; ctr.count_rule_vars(r); unsigned max_var; unsigned next_var = ctr.get_max_positive(max_var) ? (max_var+1) : 0; unsigned head_var = next_var++; app_ref e_head(get_e_lit(r->get_head(), head_var), m_manager); app_ref_vector e_tail(m_manager); svector neg_flags; unsigned pos_tail_sz = r->get_positive_tail_size(); for (unsigned i=0; iget_tail(i), e_var)); neg_flags.push_back(false); } unsigned tail_sz = r->get_tail_size(); for (unsigned i=pos_tail_sz; iget_tail(i)); neg_flags.push_back(r->is_neg_tail(i)); } symbol rule_repr = get_rule_symbol(r); expr_ref_vector rule_expr_args(m_manager); for (unsigned tail_idx=0; tail_idxget_arg(tail->get_num_args()-1)); } else { //this adds argument values and the explanation term //(values will be substituted for variables at runtime by the finite_product_relation) rule_expr_args.append(tail->get_num_args(), tail->get_args()); } } //rule_expr contains rule function with string representation of the rule as symbol and //for each positive uninterpreted tail it contains its argument values and its explanation term expr * rule_expr = m_decl_util.mk_rule(rule_repr, rule_expr_args.size(), rule_expr_args.c_ptr()); app_ref e_record(m_manager.mk_eq(m_manager.mk_var(head_var, m_e_sort), rule_expr), m_manager); e_tail.push_back(e_record); neg_flags.push_back(false); SASSERT(e_tail.size()==neg_flags.size()); return m_context.get_rule_manager().mk(e_head, e_tail.size(), e_tail.c_ptr(), neg_flags.c_ptr()); } void mk_explanations::transform_rules(const rule_set & src, rule_set & dst) { rule_set::iterator rit = src.begin(); rule_set::iterator rend = src.end(); for (; rit!=rend; ++rit) { rule * e_rule = get_e_rule(*rit); dst.add_rule(e_rule); } //add rules that will (for output predicates) copy facts from explained relations back to //the original ones expr_ref_vector lit_args(m_manager); decl_set::iterator pit = src.get_output_predicates().begin(); decl_set::iterator pend = src.get_output_predicates().end(); for (; pit != pend; ++pit) { func_decl * orig_decl = *pit; lit_args.reset(); unsigned arity = orig_decl->get_arity(); for (unsigned i=0; iget_domain(i))); } app_ref orig_lit(m_manager.mk_app(orig_decl, lit_args.c_ptr()), m_manager); app_ref e_lit(get_e_lit(orig_lit, arity), m_manager); app * tail[] = { e_lit.get() }; dst.add_rule(m_context.get_rule_manager().mk(orig_lit, 1, tail, nullptr)); } } void mk_explanations::translate_rel_level_relation(relation_manager & rmgr, relation_base & orig, relation_base & e_rel) { SASSERT(m_e_fact_relation); SASSERT(e_rel.get_plugin().is_product_relation()); product_relation & prod_rel = static_cast(e_rel); SASSERT(prod_rel.size()==2); SASSERT(prod_rel[0].get_plugin().is_sieve_relation()); SASSERT(prod_rel[1].get_plugin().is_sieve_relation()); sieve_relation * srels[] = { static_cast(&prod_rel[0]), static_cast(&prod_rel[1]) }; if (&srels[0]->get_inner().get_plugin()==m_er_plugin) { std::swap(srels[0], srels[1]); } SASSERT(&srels[0]->get_inner().get_plugin()==&orig.get_plugin()); SASSERT(&srels[1]->get_inner().get_plugin()==m_er_plugin); relation_base & new_orig = srels[0]->get_inner(); explanation_relation & expl_rel = static_cast(srels[1]->get_inner()); { scoped_ptr orig_union_fun = rmgr.mk_union_fn(new_orig, orig); SASSERT(orig_union_fun); (*orig_union_fun)(new_orig, orig); } { scoped_ptr expl_union_fun = rmgr.mk_union_fn(expl_rel, *m_e_fact_relation); SASSERT(expl_union_fun); (*expl_union_fun)(expl_rel, *m_e_fact_relation); } } void mk_explanations::transform_facts(relation_manager & rmgr, rule_set const& src, rule_set& dst) { if (!m_e_fact_relation) { relation_signature expl_singleton_sig; expl_singleton_sig.push_back(m_e_sort); relation_base * expl_singleton = rmgr.mk_empty_relation(expl_singleton_sig, m_er_plugin->get_kind()); relation_fact es_fact(m_manager); es_fact.push_back(m_decl_util.mk_fact(symbol("fact"))); expl_singleton->add_fact(es_fact); SASSERT(&expl_singleton->get_plugin()==m_er_plugin); m_e_fact_relation = static_cast(expl_singleton); } func_decl_set predicates(m_context.get_predicates()); decl_set::iterator it = predicates.begin(); decl_set::iterator end = predicates.end(); for (; it!=end; ++it) { func_decl * orig_decl = *it; TRACE("dl", tout << mk_pp(orig_decl, m_manager) << "\n";); func_decl * e_decl = get_e_decl(orig_decl); if (!rmgr.try_get_relation(orig_decl) && !src.contains(orig_decl)) { // there are no facts or rules for this predicate continue; } dst.inherit_predicate(src, orig_decl, e_decl); relation_base & orig_rel = rmgr.get_relation(orig_decl); relation_base & e_rel = rmgr.get_relation(e_decl); SASSERT(e_rel.empty()); //the e_rel should be a new relation if (m_relation_level) { translate_rel_level_relation(rmgr, orig_rel, e_rel); } else { scoped_ptr product_fun = rmgr.mk_join_fn(orig_rel, *m_e_fact_relation, 0, nullptr, nullptr); SASSERT(product_fun); scoped_rel aux_extended_rel = (*product_fun)(orig_rel, *m_e_fact_relation); TRACE("dl", tout << aux_extended_rel << " " << aux_extended_rel->get_plugin().get_name() << "\n"; tout << e_rel.get_plugin().get_name() << "\n";); scoped_ptr union_fun = rmgr.mk_union_fn(e_rel, *aux_extended_rel); TRACE("dl", tout << union_fun << "\n";); SASSERT(union_fun); (*union_fun)(e_rel, *aux_extended_rel); } } } rule_set * mk_explanations::operator()(rule_set const & source) { if (source.empty()) { return nullptr; } if (!m_context.generate_explanations()) { return nullptr; } rule_set * res = alloc(rule_set, m_context); transform_facts(m_context.get_rel_context()->get_rmanager(), source, *res); transform_rules(source, *res); return res; } }; z3-z3-4.8.7/src/muz/rel/dl_mk_explanations.h000066400000000000000000000044761356505360400206520ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_mk_explanations.h Abstract: Author: Krystof Hoder (t-khoder) 2010-11-08. Revision History: --*/ #ifndef DL_MK_EXPLANATIONS_H_ #define DL_MK_EXPLANATIONS_H_ #include "muz/base/dl_context.h" #include "muz/base/dl_rule_transformer.h" namespace datalog { class explanation_relation; class explanation_relation_plugin; class mk_explanations : public rule_transformer::plugin { typedef obj_map decl_map; ast_manager & m_manager; context & m_context; dl_decl_util & m_decl_util; bool m_relation_level; ast_ref_vector m_pinned; explanation_relation_plugin * m_er_plugin; sort * m_e_sort; scoped_rel m_e_fact_relation; decl_map m_e_decl_map; symbol get_rule_symbol(rule * r); app * get_e_lit(app * lit, unsigned e_var_idx); rule * get_e_rule(rule * r); /** If \c m_relation_level is true, ensure \c e_decl predicate will be represented by the right relation object. \c orig is the predicate corresponding to \c e_decl without the explanation column. */ void assign_rel_level_kind(func_decl * e_decl, func_decl * orig); void translate_rel_level_relation(relation_manager & rmgr, relation_base & orig, relation_base & e_rel); void transform_rules(const rule_set & src, rule_set & dst); void transform_facts(relation_manager & rmgr, rule_set const& src, rule_set& dst); public: /** If relation_level is true, the explanation will not be stored for each fact, but we will rather store history of the whole relation. */ mk_explanations(context & ctx); /** \brief Return explanation predicate that corresponds to \c orig_decl. */ func_decl * get_e_decl(func_decl * orig_decl); static func_decl * get_union_decl(context & ctx); func_decl * get_union_decl() const { return get_union_decl(m_context); } rule_set * operator()(rule_set const & source) override; static expr* get_explanation(relation_base const& r); }; }; #endif /* DL_MK_EXPLANATIONS_H_ */ z3-z3-4.8.7/src/muz/rel/dl_mk_similarity_compressor.cpp000066400000000000000000000460001356505360400231270ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_mk_similarity_compressor.cpp Abstract: Author: Krystof Hoder (t-khoder) 2010-10-22. Revision History: --*/ #include #include #include "muz/rel/dl_mk_similarity_compressor.h" #include "muz/rel/dl_relation_manager.h" namespace datalog { mk_similarity_compressor::mk_similarity_compressor(context & ctx) : plugin(5000), m_context(ctx), m_manager(ctx.get_manager()), m_threshold_count(ctx.similarity_compressor_threshold()), m_result_rules(ctx.get_rule_manager()), m_modified(false), m_pinned(m_manager) { SASSERT(m_threshold_count>1); } void mk_similarity_compressor::reset() { m_rules.reset(); m_result_rules.reset(); m_pinned.reset(); } /** Allows to traverse head and positive tails in a single for loop starting from -1 */ static app * get_by_tail_index(rule * r, int idx) { if (idx < 0) { return r->get_head(); } SASSERT(idx < static_cast(r->get_positive_tail_size())); return r->get_tail(idx); } template static int aux_compare(T a, T b) { return (a>b) ? 1 : ( (a==b) ? 0 : -1); } template static int aux_compare(T* a, T* b); static int compare_var_args(app* t1, app* t2) { SASSERT(t1->get_num_args()==t2->get_num_args()); int res; unsigned n = t1->get_num_args(); for (unsigned i = 0; i < n; i++) { expr * a1 = t1->get_arg(i); expr * a2 = t2->get_arg(i); res = aux_compare(is_var(a1), is_var(a2)); if (res != 0) { return res; } if (is_var(a1)) { res = aux_compare(to_var(a1)->get_idx(), to_var(a2)->get_idx()); if (res != 0) { return res; } } } return 0; } static int compare_args(app* t1, app* t2, int & skip_countdown) { SASSERT(t1->get_num_args()==t2->get_num_args()); int res; unsigned n = t1->get_num_args(); for (unsigned i=0; iget_arg(i))) { SASSERT(t1->get_arg(i) == t2->get_arg(i)); continue; } if ((skip_countdown--) == 0) { continue; } res = aux_compare(t1->get_arg(i)->get_id(), t2->get_arg(i)->get_id()); if (res!=0) { return res; } } return 0; } /** \brief Return 0 if r1 and r2 could be similar. If the rough similarity equaivelance class of r1 is greater than the one of r2, return 1; otherwise return -1. Two rules are in the same rough similarity class if they differ only in constant arguments of positive uninterpreted predicates. */ static int rough_compare(rule * r1, rule * r2) { int res = aux_compare(r1->get_tail_size(), r2->get_tail_size()); if (res!=0) { return res; } res = aux_compare(r1->get_uninterpreted_tail_size(), r2->get_uninterpreted_tail_size()); if (res!=0) { return res; } res = aux_compare(r1->get_positive_tail_size(), r2->get_positive_tail_size()); if (res!=0) { return res; } int pos_tail_sz = r1->get_positive_tail_size(); for (int i=-1; iget_decl()->get_id(), t2->get_decl()->get_id()); if (res!=0) { return res; } res = compare_var_args(t1, t2); if (res!=0) { return res; } } unsigned tail_sz = r1->get_tail_size(); for (unsigned i=pos_tail_sz; iget_tail(i)->get_id(), r2->get_tail(i)->get_id()); if (res!=0) { return res; } } return 0; } /** \c r1 and \c r2 must be equal according to the \c rough_compare function for this function to be called. */ static int total_compare(rule * r1, rule * r2, int skipped_arg_index = INT_MAX) { SASSERT(rough_compare(r1, r2)==0); int pos_tail_sz = r1->get_positive_tail_size(); for (int i=-1; i info_vector; static void collect_const_indexes(app * t, int tail_index, info_vector & res) { unsigned n = t->get_num_args(); for (unsigned i=0; iget_arg(i))) { continue; } res.push_back(const_info(tail_index, i)); } } static void collect_const_indexes(rule * r, info_vector & res) { collect_const_indexes(r->get_head(), -1, res); unsigned pos_tail_sz = r->get_positive_tail_size(); for (unsigned i=0; iget_tail(i), i, res); } } template static void collect_orphan_consts(rule * r, const info_vector & const_infos, T & tgt) { unsigned const_cnt = const_infos.size(); tgt.reset(); for (unsigned i=0; iget_arg(inf.arg_index()))); SASSERT(tgt.back()->get_num_args()==0); } } template static void collect_orphan_sorts(rule * r, const info_vector & const_infos, T & tgt) { unsigned const_cnt = const_infos.size(); tgt.reset(); for (unsigned i=0; iget_decl()->get_domain(inf.arg_index())); } } /** \brief From the \c tail_indexes and \c arg_indexes remove elements corresponding to constants that are the same in rules \c *first ... \c *(after_last-1). */ static void remove_stable_constants(rule_vector::iterator first, rule_vector::iterator after_last, info_vector & const_infos) { SASSERT(after_last-first>1); unsigned const_cnt = const_infos.size(); ptr_vector vals; rule * r = *(first++); collect_orphan_consts(r, const_infos, vals); SASSERT(vals.size()==const_cnt); rule_vector::iterator it = first; for (; it!=after_last; ++it) { for (unsigned i=0; iget_arg(const_infos[i].arg_index())); if (vals[i]!=val) { vals[i] = 0; } } } unsigned removed_cnt = 0; for (unsigned i=0; i vals; ptr_vector sorts; rule * r = *(first++); collect_orphan_consts(r, const_infos, vals); collect_orphan_sorts(r, const_infos, sorts); SASSERT(vals.size()==const_cnt); vector possible_parents(const_cnt); for (unsigned i=1; iget_head()->get_num_args() - count_variable_arguments(r->get_head()); unsigned pos_tail_sz = r->get_positive_tail_size(); for (unsigned i=0; iget_tail(i)->get_num_args() - count_variable_arguments(r->get_tail(i)); } return res; } static bool initial_comparator(rule * r1, rule * r2) { int res = rough_compare(r1, r2); if (res!=0) { return res>0; } return total_compare(r1, r2)>0; } class arg_ignoring_comparator { unsigned m_ignored_index; public: arg_ignoring_comparator(unsigned ignored_index) : m_ignored_index(ignored_index) {} bool operator()(rule * r1, rule * r2) const { return total_compare(r1, r2, m_ignored_index)>0; } bool eq(rule * r1, rule * r2) const { return total_compare(r1, r2, m_ignored_index)==0; } }; void mk_similarity_compressor::merge_class(rule_vector::iterator first, rule_vector::iterator after_last) { SASSERT(after_last-first>1); info_vector const_infos; rule * r = *first; //an arbitrary representative of the class collect_const_indexes(r, const_infos); remove_stable_constants(first, after_last, const_infos); unsigned const_cnt = const_infos.size(); SASSERT(const_cnt>0); detect_equal_constants(first, after_last, const_infos); //The aux relation contains column for each constant which does not have an earlier constant //that it is equal to (i.e. only has no parent) ptr_vector aux_domain; collect_orphan_sorts(r, const_infos, aux_domain); func_decl* head_pred = r->get_decl(); symbol const& name_prefix = head_pred->get_name(); std::string name_suffix = "sc_" + to_string(const_cnt); func_decl * aux_pred = m_context.mk_fresh_head_predicate(name_prefix, symbol(name_suffix.c_str()), aux_domain.size(), aux_domain.c_ptr(), head_pred); m_pinned.push_back(aux_pred); relation_fact val_fact(m_manager, const_cnt); rule_vector::iterator it = first; for (; it!=after_last; ++it) { collect_orphan_consts(*it, const_infos, val_fact); m_context.add_fact(aux_pred, val_fact); } m_context.get_rel_context()->get_rmanager().mark_saturated(aux_pred); app * new_head = r->get_head(); ptr_vector new_tail; svector new_negs; unsigned tail_sz = r->get_tail_size(); for (unsigned i=0; iget_tail(i)); new_negs.push_back(r->is_neg_tail(i)); } rule_counter ctr; ctr.count_rule_vars(r); unsigned max_var_idx, new_var_idx_base; if (ctr.get_max_positive(max_var_idx)) { new_var_idx_base = max_var_idx+1; } else { new_var_idx_base = 0; } ptr_vector const_vars; //variables at indexes of their corresponding constants expr_ref_vector aux_vars(m_manager); //variables as arguments for the auxiliary predicate unsigned aux_column_index = 0; for (unsigned i=0; i mod_args(mod_tail->get_num_args(), mod_tail->get_args()); for (; iget_decl(), mod_args.c_ptr()); m_pinned.push_back(upd_tail); mod_tail = upd_tail; } app_ref aux_tail(m_manager.mk_app(aux_pred, aux_vars.c_ptr()), m_manager); new_tail.push_back(aux_tail); new_negs.push_back(false); rule * new_rule = m_context.get_rule_manager().mk(new_head, new_tail.size(), new_tail.c_ptr(), new_negs.c_ptr(), r->name()); m_result_rules.push_back(new_rule); //TODO: allow for a rule to have multiple parent objects new_rule->set_accounting_parent_object(m_context, r); m_modified = true; } void mk_similarity_compressor::process_class(rule_set const& source, rule_vector::iterator first, rule_vector::iterator after_last) { SASSERT(first!=after_last); //remove duplicates { rule_vector::iterator it = first; rule_vector::iterator prev = it; ++it; while(it!=after_last) { if (it!=after_last && total_compare(*prev, *it)==0) { --after_last; std::swap(*it, *after_last); m_modified = true; } else { prev = it; ++it; } } } SASSERT(first!=after_last); unsigned const_cnt = get_constant_count(*first); #if 0 for (unsigned ignored_index=0; ignored_indexm_threshold_count) { merge_class(grp_begin, it); //group was processed, so we remove it from the class if (it==after_last) { after_last=grp_begin; it=after_last; } else { while(it!=grp_begin) { std::swap(*--it, *--after_last); } } } grp_begin = it; grp_size = 0; } } } #endif //TODO: compress also rules with pairs (or tuples) of equal constants #if 1 if (const_cnt>0 && !source.is_output_predicate((*first)->get_decl())) { unsigned rule_cnt = static_cast(after_last-first); if (rule_cnt>m_threshold_count) { merge_class(first, after_last); return; } } #endif //put rules which weren't merged into result rule_vector::iterator it = first; for (; it!=after_last; ++it) { m_result_rules.push_back(*it); } } rule_set * mk_similarity_compressor::operator()(rule_set const & source) { // TODO mc m_modified = false; unsigned init_rule_cnt = source.get_num_rules(); SASSERT(m_rules.empty()); for (unsigned i=0; i(nullptr); if (m_modified) { result = alloc(rule_set, m_context); unsigned fin_rule_cnt = m_result_rules.size(); for (unsigned i=0; iadd_rule(m_result_rules.get(i)); } result->inherit_predicates(source); } reset(); return result; } }; z3-z3-4.8.7/src/muz/rel/dl_mk_similarity_compressor.h000066400000000000000000000033351356505360400226000ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_mk_similarity_compressor.h Abstract: Author: Krystof Hoder (t-khoder) 2010-10-22. Revision History: --*/ #ifndef DL_MK_SIMILARITY_COMPRESSOR_H_ #define DL_MK_SIMILARITY_COMPRESSOR_H_ #include #include "util/map.h" #include "util/obj_pair_hashtable.h" #include "muz/base/dl_context.h" #include "muz/base/dl_rule_set.h" #include "muz/base/dl_rule_transformer.h" namespace datalog { /** \brief Functor for merging groups of similar rules. A rule sequence P("1",x):-Q(x). ... P("N",x):-Q(x). will be replaced by P(y,x):-Q(x), Aux(y). and a set of facts Aux("1"). ... Aux("N"). Similar transformation is performed when the varying constant appears in the positive tail. */ class mk_similarity_compressor : public rule_transformer::plugin { context & m_context; ast_manager & m_manager; /** number of similar rules necessary for a group to be introduced */ unsigned m_threshold_count; rule_vector m_rules; rule_ref_vector m_result_rules; bool m_modified; ast_ref_vector m_pinned; void merge_class(rule_vector::iterator first, rule_vector::iterator after_last); void process_class(rule_set const& source, rule_vector::iterator first, rule_vector::iterator after_last); void reset(); public: mk_similarity_compressor(context & ctx); rule_set * operator()(rule_set const & source) override; }; }; #endif /* DL_MK_SIMILARITY_COMPRESSOR_H_ */ z3-z3-4.8.7/src/muz/rel/dl_mk_simple_joins.cpp000066400000000000000000000744151356505360400211730ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_mk_simple_joins.cpp Abstract: Author: Krystof Hoder 2010-05-20. Revision History: --*/ #include #include #include #include "muz/rel/dl_mk_simple_joins.h" #include "muz/rel/dl_relation_manager.h" #include "ast/ast_pp.h" #include "util/trace.h" namespace datalog { mk_simple_joins::mk_simple_joins(context & ctx): plugin(1000), m_context(ctx), rm(ctx.get_rule_manager()) { } class join_planner { typedef float cost; class pair_info { cost m_total_cost; /** \brief Number of rules longer than two that contain this pair. This number is being updated by \c add_rule and \remove rule. Even though between adding a rule and removing it, the length of a rule can decrease without this pair being notified about it, it will surely see the decrease from length 3 to 2 which the threshold for rule being counted in this counter. */ unsigned m_consumers; bool m_stratified; unsigned m_src_stratum; public: var_idx_set m_all_nonlocal_vars; rule_vector m_rules; pair_info() : m_consumers(0), m_stratified(true), m_src_stratum(0) {} bool can_be_joined() const { return m_consumers > 0; } cost get_cost() const { SASSERT(m_consumers > 0); cost amortized = m_total_cost/m_consumers; if (m_stratified) { return amortized * ( (amortized > 0) ? (1/16.0f) : 16.0f); } else { return amortized; } } /** \brief Add rule \c r among rules interested in current predicate pair. The \c pl.m_rule_content entry of the rule has to be properly filled in by the time of a call to this function */ void add_rule(join_planner & pl, app * t1, app * t2, rule * r, const var_idx_set & non_local_vars_normalized, const var_idx_set & non_local_vars) { if (m_rules.empty()) { m_total_cost = pl.compute_cost(t1, t2, non_local_vars); m_src_stratum = std::max(pl.get_stratum(t1->get_decl()), pl.get_stratum(t2->get_decl())); } m_rules.push_back(r); if (pl.m_rules_content.find(r).size() > 2) { m_consumers++; } if (m_stratified) { unsigned head_stratum = pl.get_stratum(r->get_decl()); SASSERT(head_stratum >= m_src_stratum); m_stratified = (head_stratum > m_src_stratum); } idx_set_union(m_all_nonlocal_vars, non_local_vars_normalized); TRACE("dl", tout << "all-nonlocal: " << m_all_nonlocal_vars << "\n";); } /** \brief Remove rule from the pair record. Return true if no rules remain in the pair, and so it should be removed. */ bool remove_rule(rule * r, unsigned original_length) { VERIFY( remove_from_vector(m_rules, r) ); if (original_length > 2) { SASSERT(m_consumers > 0); m_consumers--; } SASSERT(!m_rules.empty() || m_consumers == 0); return m_rules.empty(); } private: pair_info & operator=(const pair_info &); //to avoid the implicit one }; typedef std::pair app_pair; typedef pair_hash, obj_ptr_hash > app_pair_hash; typedef map > cost_map; typedef map, ptr_hash, ptr_eq > rule_pred_map; typedef ptr_hashtable > rule_hashtable; context & m_context; ast_manager & m; rule_manager & rm; var_subst & m_var_subst; rule_set & m_rs_aux_copy; //reference to a rule_set that will allow to ask for stratum levels cost_map m_costs; ptr_vector m_interpreted; rule_pred_map m_rules_content; rule_ref_vector m_introduced_rules; bool m_modified_rules; ast_ref_vector m_pinned; mutable ptr_vector m_vars; public: join_planner(context & ctx, rule_set & rs_aux_copy) : m_context(ctx), m(ctx.get_manager()), rm(ctx.get_rule_manager()), m_var_subst(ctx.get_var_subst()), m_rs_aux_copy(rs_aux_copy), m_introduced_rules(ctx.get_rule_manager()), m_modified_rules(false), m_pinned(ctx.get_manager()) { } ~join_planner() { for (auto & kv : m_costs) { dealloc(kv.m_value); } m_costs.reset(); } private: void get_normalizer(app * t, unsigned & next_var, expr_ref_vector & result) const { SASSERT(!result.empty()); unsigned res_ofs = result.size()-1; for (expr* arg : *t) { unsigned var_idx = to_var(arg)->get_idx(); if (!result.get(res_ofs - var_idx)) { result[res_ofs - var_idx] = m.mk_var(next_var++, m.get_sort(arg)); } } } expr_ref_vector get_normalizer(app * t1, app * t2) const { expr_ref_vector result(m); if (t1->get_num_args() == 0 && t2->get_num_args() == 0) { return result; //nothing to normalize } SASSERT(!t1->is_ground() || !t2->is_ground()); unsigned max_var_idx = 0; var_idx_set& orig_var_set = rm.collect_vars(t1, t2); for (unsigned var_idx : orig_var_set) { if (var_idx>max_var_idx) { max_var_idx = var_idx; } } if (t1->get_decl() != t2->get_decl()) { if (t1->get_decl()->get_id() < t2->get_decl()->get_id()) { std::swap(t1, t2); } } else { int_vector norm1(max_var_idx + 1, -1); int_vector norm2(max_var_idx + 1, -1); unsigned n = t1->get_num_args(); SASSERT(n == t2->get_num_args()); for (unsigned i = 0; i < n; ++i) { //We assume that the mk_simple_joins transformer is applied after mk_filter_rules, //so the only literals which appear in pairs are the ones that contain only variables. var * v1 = to_var(t1->get_arg(i)); var * v2 = to_var(t2->get_arg(i)); if (v1->get_sort() != v2->get_sort()) { //different sorts mean we can distinguish the two terms if (v1->get_sort()->get_id() < v2->get_sort()->get_id()) { std::swap(t1, t2); } break; } unsigned v1_idx = v1->get_idx(); unsigned v2_idx = v2->get_idx(); //since the rules already went through the mk_filter_rules transformer, //variables must be linear SASSERT(norm1[v1_idx] == -1); SASSERT(norm2[v2_idx] == -1); if (norm2[v1_idx] != norm1[v2_idx]) { //now we can distinguish the two terms if (norm2[v1_idx] < norm1[v2_idx]) { std::swap(t1, t2); } break; } norm1[v1_idx] = i; norm2[v2_idx] = i; } //if we did not exit the loop prematurely, the two terms are indistinguishable, //so the order should not matter } result.resize(max_var_idx + 1, static_cast(nullptr)); unsigned next_var = 0; get_normalizer(t1, next_var, result); get_normalizer(t2, next_var, result); return result; } app_pair get_key(app * t1, app * t2) { expr_ref_vector norm_subst = get_normalizer(t1, t2); expr_ref t1n_ref(m); expr_ref t2n_ref(m); t1n_ref = m_var_subst(t1, norm_subst.size(), norm_subst.c_ptr()); t2n_ref = m_var_subst(t2, norm_subst.size(), norm_subst.c_ptr()); app * t1n = to_app(t1n_ref); app * t2n = to_app(t2n_ref); if (t1n->get_id() > t2n->get_id()) { std::swap(t1n, t2n); } m_pinned.push_back(t1n); m_pinned.push_back(t2n); TRACE("dl", tout << mk_pp(t1, m) << " " << mk_pp(t2, m) << " |-> " << t1n_ref << " " << t2n_ref << "\n";); return app_pair(t1n, t2n); } /** \brief Add rule \c r among rules interested in predicate pair \c t1, \c t2. The \c m_rule_content entry of the rule \c r has to be properly filled in by the time of a call to this function */ void register_pair(app * t1, app * t2, rule * r, const var_idx_set & non_local_vars) { SASSERT (t1!=t2); cost_map::entry * e = m_costs.insert_if_not_there2(get_key(t1, t2), nullptr); pair_info * & ptr_inf = e->get_data().m_value; if (ptr_inf == nullptr) { ptr_inf = alloc(pair_info); } pair_info & inf = *ptr_inf; expr_ref_vector normalizer = get_normalizer(t1, t2); unsigned norm_ofs = normalizer.size()-1; var_idx_set normalized_vars; for (auto idx : non_local_vars) { unsigned norm_var = to_var(normalizer.get(norm_ofs - idx))->get_idx(); normalized_vars.insert(norm_var); } inf.add_rule(*this, t1, t2, r, normalized_vars, non_local_vars); TRACE("dl", tout << mk_pp(t1, m) << " " << mk_pp(t2, m) << " "; tout << non_local_vars << "\n"; r->display(m_context, tout); if (inf.can_be_joined()) tout << "cost: " << inf.get_cost() << "\n";); } void remove_rule_from_pair(app_pair key, rule * r, unsigned original_len) { pair_info * ptr = nullptr; if (m_costs.find(key, ptr) && ptr && ptr->remove_rule(r, original_len)) { SASSERT(ptr->m_rules.empty()); m_costs.remove(key); dealloc(ptr); } } void register_rule(rule * r) { rule_counter counter; counter.count_rule_vars(r, 1); TRACE("dl", tout << "counter: "; for (auto const& kv: counter) tout << kv.m_key << ": " << kv.m_value << " "; tout << "\n";); ptr_vector & rule_content = m_rules_content.insert_if_not_there2(r, ptr_vector())->get_data().m_value; SASSERT(rule_content.empty()); TRACE("dl", r->display(m_context, tout << "register ");); unsigned pos_tail_size = r->get_positive_tail_size(); for (unsigned i = 0; i < pos_tail_size; i++) { rule_content.push_back(r->get_tail(i)); } for (unsigned i = 0; i+1 < pos_tail_size; i++) { app * t1 = r->get_tail(i); var_idx_set t1_vars = rm.collect_vars(t1); counter.count_vars(t1, -1); //temporarily remove t1 variables from counter for (unsigned j = i+1; j < pos_tail_size; j++) { app * t2 = r->get_tail(j); counter.count_vars(t2, -1); //temporarily remove t2 variables from counter var_idx_set t2_vars = rm.collect_vars(t2); t2_vars |= t1_vars; var_idx_set non_local_vars; counter.collect_positive(non_local_vars); counter.count_vars(t2, 1); //restore t2 variables in counter set_intersection(non_local_vars, t2_vars); TRACE("dl", tout << "non-local vars: " << non_local_vars << "\n";); register_pair(t1, t2, r, non_local_vars); } counter.count_vars(t1, 1); //restore t1 variables in counter } } bool extract_argument_info(unsigned var_idx, app * t, expr_ref_vector & args, ptr_vector & domain) { for (expr* arg : *t) { var * v = to_var(arg); if (v->get_idx() == var_idx) { args.push_back(v); domain.push_back(m.get_sort(v)); return true; } } return false; } void join_pair(app_pair pair_key) { app * t1 = pair_key.first; app * t2 = pair_key.second; pair_info & inf = *m_costs[pair_key]; SASSERT(!inf.m_rules.empty()); var_idx_set const & output_vars = inf.m_all_nonlocal_vars; expr_ref_vector args(m); ptr_vector domain; unsigned arity = output_vars.num_elems(); for (unsigned var_idx : output_vars) { bool found = extract_argument_info(var_idx, t1, args, domain); if (!found) { found = extract_argument_info(var_idx, t2, args, domain); } SASSERT(found); } TRACE("dl", tout << mk_pp(t1, m) << " " << mk_pp(t2, m) << " arity: " << arity << "\n"; tout << "output: " << output_vars << "\n"; tout << "args: " << args << "\n";); SASSERT(args.size() == arity); SASSERT(domain.size() == arity); rule * one_parent = inf.m_rules.back(); func_decl* parent_head = one_parent->get_decl(); const char * one_parent_name = parent_head->get_name().bare_str(); std::string parent_name; if (inf.m_rules.size() > 1) { parent_name = one_parent_name + std::string("_and_") + to_string(inf.m_rules.size()-1); } else { parent_name = one_parent_name; } func_decl * decl = m_context.mk_fresh_head_predicate( symbol(parent_name.c_str()), symbol("split"), arity, domain.c_ptr(), parent_head); app_ref head(m.mk_app(decl, arity, args.c_ptr()), m); app * tail[] = {t1, t2}; rule * new_rule = m_context.get_rule_manager().mk(head, 2, tail, nullptr); //TODO: update accounting so that it can handle multiple parents new_rule->set_accounting_parent_object(m_context, one_parent); m_introduced_rules.push_back(new_rule); //here we copy the inf.m_rules vector because inf.m_rules will get changed //in the iteration. Also we use hashtable instead of vector because we do //not want to process one rule twice. rule_hashtable processed_rules; rule_vector rules(inf.m_rules); for (rule * r : rules) { if (!processed_rules.contains(r)) { apply_binary_rule(r, pair_key, head); processed_rules.insert(r); } } // SASSERT(!m_costs.contains(pair_key)); } void replace_edges(rule * r, const app_ref_vector & removed_tails, const app_ref_vector & added_tails0, const ptr_vector & rule_content) { SASSERT(removed_tails.size()>=added_tails0.size()); unsigned len = rule_content.size(); unsigned original_len = len+removed_tails.size()-added_tails0.size(); app_ref_vector added_tails(added_tails0); //we need a copy since we'll be modifying it TRACE("dl", tout << added_tails << "\n";); unsigned rt_sz = removed_tails.size(); //remove edges between removed tails for (unsigned i = 0; i < rt_sz; i++) { for (unsigned j = i+1; j < rt_sz; j++) { app_pair pair_key = get_key(removed_tails[i], removed_tails[j]); remove_rule_from_pair(pair_key, r, original_len); } } //remove edges between surviving tails and removed tails for (unsigned i = 0; i < len; i++) { if (added_tails.contains(rule_content[i])) { continue; } for (unsigned ri = 0; ri < rt_sz; ri++) { app_pair pair_key = get_key(rule_content[i], removed_tails[ri]); remove_rule_from_pair(pair_key, r, original_len); } } if (len == 1) { return; } app * head = r->get_head(); var_counter counter; counter.count_vars(head, 1); unsigned tail_size = r->get_tail_size(); unsigned pos_tail_size = r->get_positive_tail_size(); for (unsigned i = pos_tail_size; i < tail_size; i++) { counter.count_vars(r->get_tail(i), 1); } for (unsigned i = 0; i < len; i++) { counter.count_vars(rule_content[i], 1); } //add edges that contain added tails while (!added_tails.empty()) { app * a_tail = added_tails.back(); //added tail TRACE("dl", tout << "replace edges " << mk_pp(a_tail, m) << "\n";); var_idx_set a_tail_vars = rm.collect_vars(a_tail); counter.count_vars(a_tail, -1); //temporarily remove a_tail variables from counter for (unsigned i = 0; i < len; i++) { app * o_tail = rule_content[i]; //other tail if (added_tails.contains(o_tail)) { //this avoids adding edges between new tails twice continue; } counter.count_vars(o_tail, -1); //temporarily remove o_tail variables from counter var_idx_set scope_vars = rm.collect_vars(o_tail); scope_vars |= a_tail_vars; var_idx_set non_local_vars; counter.collect_positive(non_local_vars); counter.count_vars(o_tail, 1); //restore o_tail variables in counter set_intersection(non_local_vars, scope_vars); register_pair(o_tail, a_tail, r, non_local_vars); } counter.count_vars(a_tail, 1); //restore t1 variables in counter added_tails.pop_back(); } } void apply_binary_rule(rule * r, app_pair pair_key, app * t_new) { app * t1 = pair_key.first; app * t2 = pair_key.second; ptr_vector & rule_content = m_rules_content.find(r); unsigned len = rule_content.size(); if (len == 1) { return; } TRACE("dl", r->display(m_context, tout << "rule "); tout << "pair: " << mk_pp(t1, m) << " " << mk_pp(t2, m) << "\n"; tout << mk_pp(t_new, m) << "\n"; tout << "all-non-local: " << m_costs[pair_key]->m_all_nonlocal_vars << "\n"; for (app* a : rule_content) tout << mk_pp(a, m) << " "; tout << "\n";); rule_counter counter; counter.count_rule_vars(r, 1); func_decl * t1_pred = t1->get_decl(); func_decl * t2_pred = t2->get_decl(); app_ref_vector removed_tails(m); app_ref_vector added_tails(m); for (unsigned i1 = 0; i1 < len; i1++) { app * rt1 = rule_content[i1]; if (rt1->get_decl() != t1_pred) { continue; } var_idx_set rt1_vars = rm.collect_vars(rt1); counter.count_vars(rt1, -1); var_idx_set t1_vars = rm.collect_vars(t1); unsigned i2start = (t1_pred==t2_pred) ? (i1+1) : 0; for (unsigned i2 = i2start; i2 < len; i2++) { app * rt2 = rule_content[i2]; if (i1 == i2 || rt2->get_decl() != t2_pred) { continue; } if (get_key(rt1, rt2) != pair_key) { continue; } expr_ref_vector denormalizer(m); expr_ref_vector normalizer = get_normalizer(rt1, rt2); reverse_renaming(m, normalizer, denormalizer); expr_ref new_transf(m); new_transf = m_var_subst(t_new, denormalizer.size(), denormalizer.c_ptr()); var_idx_set transf_vars = rm.collect_vars(new_transf); TRACE("dl", tout << mk_pp(rt1, m) << " " << mk_pp(rt2, m) << " -> " << new_transf << "\n";); counter.count_vars(rt2, -1); var_idx_set rt2_vars = rm.collect_vars(rt2); var_idx_set tr_vars = rm.collect_vars(new_transf); rt2_vars |= rt1_vars; var_idx_set non_local_vars; counter.collect_positive(non_local_vars); set_intersection(non_local_vars, rt2_vars); counter.count_vars(rt2, +1); // require that tr_vars contains non_local_vars TRACE("dl", tout << "non-local : " << non_local_vars << " tr_vars " << tr_vars << " rt12_vars " << rt2_vars << "\n";); if (!non_local_vars.subset_of(tr_vars)) { expr_ref_vector normalizer2 = get_normalizer(rt2, rt1); TRACE("dl", tout << normalizer << "\nnorm\n" << normalizer2 << "\n";); denormalizer.reset(); reverse_renaming(m, normalizer2, denormalizer); new_transf = m_var_subst(t_new, denormalizer.size(), denormalizer.c_ptr()); SASSERT(non_local_vars.subset_of(rm.collect_vars(new_transf))); TRACE("dl", tout << mk_pp(rt2, m) << " " << mk_pp(rt1, m) << " -> " << new_transf << "\n";); } app * new_lit = to_app(new_transf); m_pinned.push_back(new_lit); rule_content[i1] = new_lit; rule_content[i2] = rule_content.back(); rule_content.pop_back(); len--; //here the bound of both loops changes!!! removed_tails.push_back(rt1); removed_tails.push_back(rt2); added_tails.push_back(new_lit); // this exits the inner loop, the outer one continues in case there will // be other matches break; } counter.count_vars(rt1, 1); } SASSERT(!removed_tails.empty()); SASSERT(!added_tails.empty()); m_modified_rules = true; TRACE("dl", tout << "replace rule content\n";); replace_edges(r, removed_tails, added_tails, rule_content); } cost get_domain_size(func_decl * pred, unsigned arg_index) const { relation_sort sort = pred->get_domain(arg_index); return static_cast(m_context.get_sort_size_estimate(sort)); } unsigned get_stratum(func_decl * pred) const { return m_rs_aux_copy.get_predicate_strat(pred); } cost estimate_size(app * t) const { func_decl * pred = t->get_decl(); unsigned n = pred->get_arity(); rel_context_base* rel = m_context.get_rel_context(); if (!rel) { return cost(1); } relation_manager& rm = rel->get_rmanager(); if ( (m_context.saturation_was_run() && rm.try_get_relation(pred)) || rm.is_saturated(pred)) { SASSERT(rm.try_get_relation(pred)); //if it is saturated, it should exist unsigned rel_size_int = rel->get_relation(pred).get_size_estimate_rows(); if (rel_size_int != 0) { cost rel_size = static_cast(rel_size_int); cost curr_size = rel_size; for (unsigned i = 0; i < n; i++) { if (!is_var(t->get_arg(i))) { curr_size /= get_domain_size(pred, i); } } return curr_size; } } cost res = 1; for (unsigned i = 0; i < n; i++) { if (is_var(t->get_arg(i))) { res *= get_domain_size(pred, i); } } return res; } cost compute_cost(app * t1, app * t2, const var_idx_set & non_local_vars) const { func_decl * t1_pred = t1->get_decl(); func_decl * t2_pred = t2->get_decl(); cost inters_size = 1; variable_intersection vi(m_context.get_manager()); vi.populate(t1, t2); unsigned n = vi.size(); // remove contributions from joined columns. for (unsigned i = 0; i < n; i++) { unsigned arg_index1, arg_index2; vi.get(i, arg_index1, arg_index2); SASSERT(is_var(t1->get_arg(arg_index1))); if (non_local_vars.contains(to_var(t1->get_arg(arg_index1))->get_idx())) { inters_size *= get_domain_size(t1_pred, arg_index1); } //joined arguments must have the same domain SASSERT(get_domain_size(t1_pred, arg_index1)==get_domain_size(t2_pred, arg_index2)); } // remove contributions from projected columns. for (unsigned i = 0; i < t1->get_num_args(); ++i) { if (is_var(t1->get_arg(i)) && !non_local_vars.contains(to_var(t1->get_arg(i))->get_idx())) { inters_size *= get_domain_size(t1_pred, i); } } for (unsigned i = 0; i < t2->get_num_args(); ++i) { if (is_var(t2->get_arg(i)) && !non_local_vars.contains(to_var(t2->get_arg(i))->get_idx())) { inters_size *= get_domain_size(t2_pred, i); } } cost res = estimate_size(t1)*estimate_size(t2)/ inters_size; // (inters_size*inters_size); //cost res = -inters_size; TRACE("report_costs", display_predicate(m_context, t1, tout); display_predicate(m_context, t2, tout); tout << res << "\n";); return res; } bool pick_best_pair(app_pair & p) { app_pair best; bool found = false; cost best_cost; for (auto const& kv : m_costs) { app_pair key = kv.m_key; pair_info & inf = *kv.m_value; if (!inf.can_be_joined()) { continue; } cost c = inf.get_cost(); if (!found || c < best_cost) { found = true; best_cost = c; best = key; } } if (!found) { return false; } p = best; return true; } public: rule_set * run(rule_set const & source) { for (rule * r : source) { register_rule(r); } app_pair selected; while (pick_best_pair(selected)) { join_pair(selected); } if (!m_modified_rules) { return nullptr; } rule_set * result = alloc(rule_set, m_context); for (auto& kv : m_rules_content) { rule * orig_r = kv.m_key; ptr_vector content = kv.m_value; SASSERT(content.size() <= 2); if (content.size() == orig_r->get_positive_tail_size()) { //rule did not change result->add_rule(orig_r); continue; } ptr_vector tail(content); svector negs(tail.size(), false); unsigned or_len = orig_r->get_tail_size(); for (unsigned i=orig_r->get_positive_tail_size(); i < or_len; i++) { tail.push_back(orig_r->get_tail(i)); negs.push_back(orig_r->is_neg_tail(i)); } rule * new_rule = m_context.get_rule_manager().mk(orig_r->get_head(), tail.size(), tail.c_ptr(), negs.c_ptr(), orig_r->name()); new_rule->set_accounting_parent_object(m_context, orig_r); m_context.get_rule_manager().mk_rule_rewrite_proof(*orig_r, *new_rule); result->add_rule(new_rule); } while (!m_introduced_rules.empty()) { result->add_rule(m_introduced_rules.back()); m_context.get_rule_manager().mk_rule_asserted_proof(*m_introduced_rules.back()); m_introduced_rules.pop_back(); } result->inherit_predicates(source); return result; } }; rule_set * mk_simple_joins::operator()(rule_set const & source) { rule_set rs_aux_copy(m_context); rs_aux_copy.replace_rules(source); if (!rs_aux_copy.is_closed()) { rs_aux_copy.close(); } join_planner planner(m_context, rs_aux_copy); return planner.run(source); } }; z3-z3-4.8.7/src/muz/rel/dl_mk_simple_joins.h000066400000000000000000000026171356505360400206330ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_mk_simple_joins.h Abstract: Author: Leonardo de Moura (leonardo) 2010-05-20. Revision History: --*/ #ifndef DL_MK_SIMPLE_JOINS_H_ #define DL_MK_SIMPLE_JOINS_H_ #include "util/map.h" #include "util/obj_pair_hashtable.h" #include "muz/base/dl_context.h" #include "muz/base/dl_rule_set.h" #include "muz/base/dl_rule_transformer.h" namespace datalog { /** \brief Functor for creating rules that contain simple joins. A simple join is the join of two tables. After applying this transformation, every rule has at most one join. So, the rules will have the form HEAD :- TAIL. HEAD :- TAIL_1, TAIL_2. We also assume a rule may contain interpreted expressions that work as filtering conditions. So, we may also have: HEAD :- TAIL, C_1, ..., C_n. HEAD :- TAIL_1, TAIL_2, C_1, ..., C_n. Where the C_i's are interpreted expressions. We say that a rule containing C_i's is a rule with a "big tail". */ class mk_simple_joins : public rule_transformer::plugin { context & m_context; rule_manager & rm; public: mk_simple_joins(context & ctx); rule_set * operator()(rule_set const & source) override; }; }; #endif /* DL_MK_SIMPLE_JOINS_H_ */ z3-z3-4.8.7/src/muz/rel/dl_product_relation.cpp000066400000000000000000001333401356505360400213570ustar00rootroot00000000000000/*++ Copyright (c) 2010 Microsoft Corporation Module Name: dl_product_relation.cpp Abstract: A Relation combinator. Author: Nikolaj Bjorner (nbjorner) 2010-4-11 Revision History: Notes: join = more refined version lets augment the product relation as a consequence of join. join Q = join = u = more refined version: < (R u R') n (R u S') n (R' u S), (S u S') n (S u R') n (S' u R)> proj = < proj R, proj S> & phi = attach S to [R & phi] whenever R & phi can propagate to S [rename] = --*/ #include "muz/rel/dl_sieve_relation.h" #include "muz/rel/dl_table_relation.h" #include "muz/rel/dl_product_relation.h" #include "ast/rewriter/bool_rewriter.h" #include "ast/ast_pp.h" namespace datalog { // ----------------------------------- // // product_relation_plugin // // ----------------------------------- product_relation_plugin & product_relation_plugin::get_plugin(relation_manager & rmgr) { product_relation_plugin * res = static_cast(rmgr.get_relation_plugin(get_name())); if(!res) { res = alloc(product_relation_plugin, rmgr); rmgr.register_plugin(res); } return *res; } product_relation_plugin::product_relation_plugin(relation_manager& m): relation_plugin(product_relation_plugin::get_name(), m, ST_PRODUCT_RELATION), m_spec_store(*this) { } void product_relation_plugin::initialize(family_id fid) { relation_plugin::initialize(fid); m_spec_store.add_available_kind(get_kind()); } family_id product_relation_plugin::get_relation_kind(const relation_signature & sig, const rel_spec & spec) { SASSERT(spec.well_formed()); return m_spec_store.get_relation_kind(sig, spec); } family_id product_relation_plugin::get_relation_kind(const product_relation & r) { return get_relation_kind(r.get_signature(), r.m_spec); } bool product_relation_plugin::can_handle_signature(const relation_signature & s) { return m_spec_store.contains_signature(s); } bool product_relation_plugin::can_handle_signature(const relation_signature & s, family_id k) { return true; } product_relation& product_relation_plugin::get(relation_base& r) { return dynamic_cast(r); } product_relation const & product_relation_plugin::get(relation_base const& r) { return dynamic_cast(r); } product_relation* product_relation_plugin::get(relation_base* r) { return dynamic_cast(r); } product_relation const* product_relation_plugin::get(relation_base const* r) { return dynamic_cast(r); } bool product_relation_plugin::is_product_relation(relation_base const& r) { return r.get_plugin().get_name() == product_relation_plugin::get_name(); } bool product_relation_plugin::are_aligned(const product_relation& r1, const product_relation& r2) { unsigned sz = r1.size(); if(sz!=r2.size()) { return false; } for(unsigned i=0; i & rels, rel_spec & res) { vector specs; ptr_vector::const_iterator rit = rels.begin(); ptr_vector::const_iterator rend = rels.end(); for(; rit!=rend; ++rit) { specs.push_back((*rit)->m_spec); SASSERT(specs.back().well_formed()); std::sort(specs.back().begin(), specs.back().end()); } vector::iterator sit = specs.begin(), send = specs.end(); res.reset(); for(;;) { family_id next = -1; sit = specs.begin(); for(; sit!=send; ++sit) { rel_spec & s = *sit; if(!s.empty() && s.back()>next) { next = s.back(); } } if(next==-1) { //we're done break; } res.push_back(next); sit = specs.begin(); for(; sit!=send; ++sit) { rel_spec & s = *sit; while (!s.empty() && s.back()==next) { s.pop_back(); } } } } relation_base * product_relation_plugin::mk_empty(const relation_signature & s) { return alloc(product_relation,*this, s); } relation_base * product_relation_plugin::mk_empty(const relation_signature & s, family_id kind) { rel_spec spec; m_spec_store.get_relation_spec(s, kind, spec); relation_vector inner_rels; unsigned rel_cnt = spec.size(); for(unsigned i=0; im_default_empty = false; return result; } rel_spec spec; m_spec_store.get_relation_spec(s, kind, spec); SASSERT(spec.well_formed()); relation_vector inner_rels; unsigned rel_cnt = spec.size(); for(unsigned i=0; i m_joins; ptr_vector m_full; unsigned_vector m_offset1; svector m_kind1; unsigned_vector m_offset2; svector m_kind2; const relation_base & get_nonsieve_relation(const relation_base & r) { relation_plugin & rp = r.get_plugin(); if(rp.is_sieve_relation()) { return static_cast(r).get_inner(); } else { return r; } } relation_plugin & get_nonsieve_plugin(const relation_base & r) { return get_nonsieve_relation(r).get_plugin(); } family_id get_nonsieve_kind(const relation_base & r) { return get_nonsieve_relation(r).get_kind(); } /** A tableish relatio is either a table_relation or a sieve_relation with a table_relation inside. */ bool is_tableish_relation(const relation_base & r) { return get_nonsieve_plugin(r).from_table(); } relation_base * get_full_tableish_relation(const relation_signature & sig, func_decl* p, family_id kind) { relation_manager& rmgr = m_plugin.get_manager(); table_signature tsig; if(rmgr.relation_signature_to_table(sig, tsig)) { return rmgr.mk_table_relation(sig, rmgr.get_appropriate_plugin(tsig).mk_full(p, tsig, kind)); } unsigned sz = sig.size(); tsig.reset(); for(unsigned i=0; i relations; unsigned sz = m_joins.size(); relation_base* result = nullptr; for (unsigned i = 0; i < sz; ++i) { relation_base const& r1 = (m_kind1[i] == T_FULL)?(*m_full[m_offset1[i]]):access(m_offset1[i], _r1); relation_base const& r2 = (m_kind2[i] == T_FULL)?(*m_full[m_offset2[i]]):access(m_offset2[i], _r2); relations.push_back((*m_joins[i])(r1, r2)); } result = alloc(product_relation, m_plugin, get_result_signature(), sz, relations.c_ptr()); TRACE("dl",result->display(tout);); return result; } }; relation_join_fn * product_relation_plugin::mk_join_fn(const relation_base & r1, const relation_base & r2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { if (is_product_relation(r1) && is_product_relation(r2)) { return alloc(join_fn, *this, get(r1), get(r2), col_cnt, cols1, cols2); } if (is_product_relation(r1)) { return alloc(join_fn, *this, get(r1), r2, col_cnt, cols1, cols2); } if (is_product_relation(r2)) { return alloc(join_fn, *this, r1, get(r2), col_cnt, cols1, cols2); } if (r1.get_kind() != r2.get_kind()) { return alloc(join_fn, *this, r1, r2, col_cnt, cols1, cols2); } return nullptr; } class product_relation_plugin::transform_fn : public relation_transformer_fn { relation_signature m_sig; ptr_vector m_transforms; public: transform_fn(relation_signature s, unsigned num_trans, relation_transformer_fn** trans): m_sig(std::move(s)), m_transforms(num_trans, trans) {} ~transform_fn() override { dealloc_ptr_vector_content(m_transforms); } relation_base * operator()(const relation_base & _r) override { product_relation const& r = get(_r); product_relation_plugin& p = r.get_plugin(); SASSERT(m_transforms.size() == r.size()); ptr_vector relations; for (unsigned i = 0; i < r.size(); ++i) { relations.push_back((*m_transforms[i])(r[i])); } relation_base* result = alloc(product_relation, p, m_sig, relations.size(), relations.c_ptr()); TRACE("dl", _r.display(tout); result->display(tout);); return result; } }; relation_transformer_fn * product_relation_plugin::mk_project_fn(const relation_base & _r, unsigned col_cnt, const unsigned * removed_cols) { if (is_product_relation(_r)) { product_relation const& r = get(_r); ptr_vector projs; for (unsigned i = 0; i < r.size(); ++i) { projs.push_back(get_manager().mk_project_fn(r[i], col_cnt, removed_cols)); } relation_signature s; relation_signature::from_project(r.get_signature(), col_cnt, removed_cols, s); return alloc(transform_fn, s, projs.size(), projs.c_ptr()); } return nullptr; } relation_transformer_fn * product_relation_plugin::mk_rename_fn(const relation_base & _r, unsigned cycle_len, const unsigned * permutation_cycle) { if(is_product_relation(_r)) { ptr_vector trans; product_relation const& r = get(_r); for (unsigned i = 0; i < r.size(); ++i) { trans.push_back(get_manager().mk_rename_fn(r[i], cycle_len, permutation_cycle)); } relation_signature s; relation_signature::from_rename(r.get_signature(), cycle_len, permutation_cycle, s); return alloc(transform_fn, s, trans.size(), trans.c_ptr()); } return nullptr; } class product_relation_plugin::aligned_union_fn : public relation_union_fn { relation_manager & m_rmgr; product_relation_plugin& m_plugin; bool m_is_widen; //m_union[i][j] is union between i-th and j-th relation. //It can be zero which means that particular union should be skipped. vector > m_unions; void mk_union_fn(unsigned i, unsigned j, relation_base const& r1, relation_base const& r2, const relation_base* delta) { relation_manager& rmgr = r1.get_manager(); relation_union_fn* u = nullptr; if (m_is_widen) { u = rmgr.mk_widen_fn(r1, r2, delta); } else { u = rmgr.mk_union_fn(r1, r2, delta); } TRACE("dl_verbose", tout << r1.get_plugin().get_name() << " " << r2.get_plugin().get_name() << " " << (u?"found":"not found") << "\n";); m_unions.back().push_back(u); } void init(const relation_vector & tgts, const relation_vector & srcs, const relation_vector * deltas) { SASSERT(tgts.size()==srcs.size()); unsigned num = tgts.size(); for (unsigned i = 0; i < num; ++i) { relation_base& r1 = *tgts[i]; relation_base* delta = deltas ? (*deltas)[i] : 0; m_unions.push_back(ptr_vector()); for (unsigned j = 0; j < num; ++j) { relation_base& r2 = *srcs[j]; mk_union_fn(i, j, r1, r2, delta); } } } bool can_do_inner_union(unsigned tgt_idx, unsigned src_idx) { return m_unions[tgt_idx][src_idx] != 0; } void do_inner_union(unsigned tgt_idx, unsigned src_idx, relation_base& tgt, relation_base& src, relation_base * delta) { SASSERT(m_unions[tgt_idx][src_idx]); (*m_unions[tgt_idx][src_idx])(tgt, src, delta); } /** If tgt is zero, it is assumed to be a full relation. */ void do_destructive_intersection(scoped_rel& tgt, scoped_rel& src) { if(!src) { return; } if(!tgt) { tgt=src.release(); return; } do_intersection(*tgt, *src); src = nullptr; } void do_intersection(relation_base& tgt, relation_base& src) { scoped_ptr intersect_fun = m_rmgr.mk_filter_by_intersection_fn(tgt, src); if (!intersect_fun) { TRACE("dl", tgt.display(tout << "tgt\n"); src.display(tout << "src\n");); warning_msg("intersection does not exist"); return; } (*intersect_fun)(tgt, src); } void do_delta_union(unsigned rel_idx, relation_base& tgt, relation_base& src) { scoped_ptr union_fun = m_rmgr.mk_union_fn(tgt, src); SASSERT(union_fun); (*union_fun)(tgt, src); } public: aligned_union_fn( product_relation const& tgt, product_relation const& src, product_relation const* delta, bool is_widen) : m_rmgr(tgt.get_manager()), m_plugin(tgt.get_plugin()), m_is_widen(is_widen) { SASSERT(vectors_equal(tgt.m_spec, src.m_spec)); SASSERT(!delta || vectors_equal(tgt.m_spec, delta->m_spec)); init(tgt.m_relations, src.m_relations, delta ? &delta->m_relations : nullptr); } ~aligned_union_fn() override { unsigned sz = m_unions.size(); for(unsigned i=0; i side_results; ptr_vector side_deltas; for (unsigned i = 0; i < num; ++i) { relation_base& itgt = tgt[i]; relation_base* idelta = delta ? &(*delta)[i] : nullptr; scoped_rel fresh_delta = idelta ? idelta->get_plugin().mk_empty(*idelta) : nullptr; scoped_rel side_result; scoped_rel side_delta; //compute the side unions with which we will intersect the result of the basic one for (unsigned j = 0; j < num; ++j) { if (i == j) { continue; //this is the basic union which we will perform later } if (can_do_inner_union(i, j) && can_do_inner_union(j, i)) { TRACE("dl", itgt.display(tout << "tgt:\n"); src[j].display(tout << "src:\n");); // union[i][j] scoped_rel one_side_union = itgt.clone(); scoped_rel one_side_delta = fresh_delta ? fresh_delta->clone() : nullptr; TRACE("dl", one_side_union->display(tout << "union 1:\n"); src[j].display(tout);); do_inner_union(i, j, *one_side_union, src[j], one_side_delta.get()); TRACE("dl", one_side_union->display(tout << "union:\n");); do_destructive_intersection(side_result, one_side_union); TRACE("dl", side_result->display(tout << "inner-union: " << i << " " << j << "\n"); itgt.display(tout << "tgt:\n");); if (one_side_delta) { do_destructive_intersection(side_delta, one_side_delta); } // union[j][i] one_side_union = src[i].clone(); one_side_delta = fresh_delta ? fresh_delta->clone() : nullptr; TRACE("dl", one_side_union->display(tout << "union 2:\n"); tgt[j].display(tout);); do_inner_union(i, j, *one_side_union, tgt[j], one_side_delta.get()); TRACE("dl", one_side_union->display(tout << "union:\n");); do_destructive_intersection(side_result, one_side_union); TRACE("dl", side_result->display(tout << "inner-union: " << i << " " << j << "\n"); itgt.display(tout << "tgt:\n");); if (one_side_delta) { do_destructive_intersection(side_delta, one_side_delta); } } } side_results.push_back(side_result.release()); side_deltas.push_back(side_delta.release()); } for (unsigned i = 0; i < num; ++i) { relation_base& itgt = tgt[i]; relation_base* idelta = delta ? &(*delta)[i] : nullptr; scoped_rel fresh_delta = idelta ? idelta->get_plugin().mk_empty(*idelta) : nullptr; scoped_rel side_result(side_results[i]); scoped_rel side_delta(side_deltas[i]); // perform the basic union // assume a relation can always perform union with the relation of the same type VERIFY(can_do_inner_union(i,i)); do_inner_union(i, i, itgt, src[i], fresh_delta.get()); if (side_result) { do_intersection(itgt, *side_result); TRACE("dl", side_result->display(tout << "inner-union-end: " << i << "\n");); } if (fresh_delta) { do_destructive_intersection(fresh_delta,side_delta); SASSERT(idelta); do_delta_union(i, *idelta, *fresh_delta); } } if (num == 0) { //we need to handle product relation of no relations separately if (!src.m_default_empty && tgt.m_default_empty) { tgt.m_default_empty = false; if (delta) { delta->m_default_empty = false; } } } TRACE("dl", _tgt.display(tout << "dst':\n"); if (_delta) _delta->display(tout << "delta:\n"); ;); } }; class product_relation_plugin::unaligned_union_fn : public relation_union_fn { bool m_is_widen; rel_spec m_common_spec; scoped_ptr m_aligned_union_fun; public: unaligned_union_fn(product_relation const& tgt, product_relation const& src, product_relation const* delta, bool is_widen) : m_is_widen(is_widen) { ptr_vector rels; rels.push_back(&tgt); rels.push_back(&src); if(delta) { rels.push_back(delta); } get_common_spec(rels, m_common_spec); } void operator()(relation_base& _tgt, const relation_base& _src, relation_base* _delta) override { TRACE("dl_verbose", _tgt.display(tout << "dst:\n"); _src.display(tout << "src:\n");); product_relation& tgt = get(_tgt); product_relation const& src0 = get(_src); product_relation* delta = _delta ? get(_delta) : nullptr; tgt.convert_spec(m_common_spec); if(delta) { delta->convert_spec(m_common_spec); } scoped_rel src_scoped; if(src0.get_kind()!=tgt.get_kind()) { src_scoped = src0.clone(); src_scoped->convert_spec(m_common_spec); } product_relation const& src = src_scoped ? *src_scoped : src0; if(!m_aligned_union_fun) { m_aligned_union_fun = alloc(aligned_union_fn, tgt, src, delta, m_is_widen); SASSERT(m_aligned_union_fun); } (*m_aligned_union_fun)(tgt, src, delta); TRACE("dl", _tgt.display(tout << "dst':\n"); if (_delta) _delta->display(tout << "delta:\n");); } }; class product_relation_plugin::single_non_transparent_src_union_fn : public relation_union_fn { unsigned m_single_rel_idx; scoped_ptr m_inner_union_fun; public: single_non_transparent_src_union_fn(unsigned single_rel_idx, relation_union_fn* inner_union_fun) : m_single_rel_idx(single_rel_idx), m_inner_union_fun(inner_union_fun) {} void operator()(relation_base& tgt, const relation_base& _src, relation_base* delta) override { TRACE("dl", tgt.display(tout); _src.display(tout); ); product_relation const& src = get(_src); (*m_inner_union_fun)(tgt, src[m_single_rel_idx], delta); } }; relation_union_fn * product_relation_plugin::mk_union_w_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta, bool is_widen) { if (check_kind(tgt) && check_kind(src) && (!delta || check_kind(*delta))) { if(are_aligned(get(tgt), get(src)) && (!delta || are_aligned(get(tgt), *get(delta)))) { return alloc(aligned_union_fn, get(tgt), get(src), get(delta), is_widen); } return alloc(unaligned_union_fn, get(tgt), get(src), get(delta), is_widen); } if (check_kind(src)) { TRACE("dl", tgt.display(tout << "different kinds"); src.display(tout);); const product_relation & p_src = get(src); unsigned single_idx; if(p_src.try_get_single_non_transparent(single_idx)) { relation_union_fn * inner; if(is_widen) { inner = get_manager().mk_widen_fn(tgt, p_src[single_idx], delta); } else { inner = get_manager().mk_union_fn(tgt, p_src[single_idx], delta); } if (inner) { return alloc(single_non_transparent_src_union_fn, single_idx, inner); } } } return nullptr; } relation_union_fn * product_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta) { return mk_union_w_fn(tgt, src, delta, false); } relation_union_fn * product_relation_plugin::mk_widen_fn( const relation_base & tgt, const relation_base & src, const relation_base * delta) { return mk_union_w_fn(tgt, src, delta, true); } class product_relation_plugin::mutator_fn : public relation_mutator_fn { ptr_vector m_mutators; public: mutator_fn(unsigned sz, relation_mutator_fn** muts): m_mutators(sz, muts) {} ~mutator_fn() override { dealloc_ptr_vector_content(m_mutators); } void operator()(relation_base & _r) override { TRACE("dl", _r.display(tout);); product_relation& r = get(_r); SASSERT(m_mutators.size() == r.size()); for (unsigned i = 0; i < r.size(); ++i) { relation_mutator_fn* m = m_mutators[i]; if (m) { (*m)(r[i]); } } TRACE("dl", _r.display(tout);); } }; relation_mutator_fn * product_relation_plugin::mk_filter_identical_fn( const relation_base & _t, unsigned col_cnt, const unsigned * identical_cols) { if(is_product_relation(_t)) { bool found = false; product_relation const& r = get(_t); ptr_vector mutators; for (unsigned i = 0; i < r.size(); ++i) { relation_mutator_fn* m = get_manager().mk_filter_identical_fn(r[i], col_cnt, identical_cols); mutators.push_back(m); if (m) found = true; } if (found) { return alloc(mutator_fn, mutators.size(), mutators.c_ptr()); } } return nullptr; } relation_mutator_fn * product_relation_plugin::mk_filter_equal_fn(const relation_base & _t, const relation_element & value, unsigned col) { if(is_product_relation(_t)) { product_relation const& r = get(_t); ptr_vector mutators; bool found = false; for (unsigned i = 0; i < r.size(); ++i) { relation_mutator_fn* m = get_manager().mk_filter_equal_fn(r[i], value, col); mutators.push_back(m); if (m) found = true; } if (found) { return alloc(mutator_fn, mutators.size(), mutators.c_ptr()); } } return nullptr; } class product_relation_plugin::filter_interpreted_fn : public relation_mutator_fn { ptr_vector m_mutators; svector > m_attach; public: filter_interpreted_fn(product_relation const& r, app* cond) { for (unsigned i = 0; i < r.size(); ++i) { m_mutators.push_back(r.get_manager().mk_filter_interpreted_fn(r[i], cond)); } for (unsigned i = 0; i < r.size(); ++i) { relation_mutator_fn& m1 = *(m_mutators[i]); for (unsigned j = i + 1; j < r.size(); ++j) { relation_mutator_fn& m2 = *(m_mutators[j]); if (m1.supports_attachment(r[j])) { m_attach.push_back(std::make_pair(i,j)); } if (m2.supports_attachment(r[i])) { m_attach.push_back(std::make_pair(j,i)); } } } } ~filter_interpreted_fn() override { dealloc_ptr_vector_content(m_mutators); } void operator()(relation_base& _r) override { TRACE("dl", _r.display(tout);); product_relation const& r = get(_r); for (unsigned i = 0; i < m_attach.size(); ++i) { m_mutators[m_attach[i].first]->attach(r[m_attach[i].second]); } for (unsigned i = 0; i < m_mutators.size(); ++i) { (*m_mutators[i])(r[i]); } TRACE("dl", _r.display(tout);); } }; relation_mutator_fn * product_relation_plugin::mk_filter_interpreted_fn(const relation_base & t, app * condition) { return alloc(filter_interpreted_fn, get(t), condition); } // ----------------------------------- // // product_relation // // ----------------------------------- product_relation::product_relation(product_relation_plugin& p, relation_signature const& s): relation_base(p, s), m_default_empty(true) { ensure_correct_kind(); } product_relation::product_relation(product_relation_plugin& p, relation_signature const& s, unsigned num_relations, relation_base** relations) : relation_base(p, s), m_default_empty(true) { for (unsigned i = 0; i < num_relations; ++i) { SASSERT(relations[i]->get_signature()==s); m_relations.push_back(relations[i]); } ensure_correct_kind(); } product_relation::~product_relation() { unsigned num_relations = m_relations.size(); for (unsigned i = 0; i < num_relations; ++i) { m_relations[i]->deallocate(); } } product_relation_plugin& product_relation::get_plugin() const { return dynamic_cast(relation_base::get_plugin()); } void product_relation::ensure_correct_kind() { unsigned rel_cnt = m_relations.size(); //the rel_cnt==0 part makes us to update the kind also when the relation is newly created bool spec_changed = rel_cnt != m_spec.size() || rel_cnt==0; if (spec_changed) { m_spec.resize(rel_cnt); } for (unsigned i = 0; i < rel_cnt; i++) { family_id rkind = m_relations[i]->get_kind(); spec_changed |= (m_spec[i] != rkind); m_spec[i] = rkind; } if (spec_changed) { set_kind(get_plugin().get_relation_kind(*this)); } } void product_relation::convert_spec(const rel_spec & spec) { func_decl* p = nullptr; const relation_signature & sig = get_signature(); family_id new_kind = get_plugin().get_relation_kind(sig, spec); if (new_kind == get_kind()) { return; } TRACE("dl", { ast_manager& m = get_ast_manager_from_rel_manager(get_manager()); sig.output(m, tout); tout << "\n"; for (unsigned i = 0; i < spec.size(); ++i) { tout << spec[i] << " "; } tout << "\n"; } ); unsigned old_sz = size(); unsigned new_sz = spec.size(); unsigned old_remain = old_sz; relation_vector new_rels; //the loop is quadratic with the number of relations, maybe we want to fix it for(unsigned i=0; iget_kind()==ikind) { irel = m_relations[j]; m_relations[j] = 0; old_remain--; break; } } if(!irel) { if(old_sz == 0 && m_default_empty) { //The relation didn't contain any inner relations but it was empty, //so we make the newly added relations empty as well. irel = get_manager().mk_empty_relation(sig, ikind); } else { irel = get_manager().mk_full_relation(sig, p, ikind); } } new_rels.push_back(irel); } SASSERT(old_remain==0); //the new specification must be a superset of the old one m_relations = new_rels; set_kind(new_kind); m_spec = spec; SASSERT(get_kind() == new_kind); } bool product_relation::try_get_single_non_transparent(unsigned & idx) const { unsigned sz = size(); bool found = false; unsigned candidate; for(unsigned i=0; i relations; for (unsigned i = 0; i < size(); ++i) { relations.push_back((*this)[i].clone()); } product_relation_plugin& p = get_plugin(); return alloc(product_relation, p, get_signature(), relations.size(), relations.c_ptr()); } product_relation * product_relation::complement(func_decl*) const { if(m_relations.empty()) { product_relation * res = clone(); res->m_default_empty = !m_default_empty; return res; } UNREACHABLE(); return nullptr; } bool product_relation::empty() const { if(m_relations.empty()) { return m_default_empty; } for (unsigned i = 0; i < m_relations.size(); ++i) { if (m_relations[i]->empty()) { return true; } } return false; } void product_relation::to_formula(expr_ref& fml) const { ast_manager& m = fml.get_manager(); expr_ref_vector conjs(m); expr_ref tmp(m); for (unsigned i = 0; i < m_relations.size(); ++i) { m_relations[i]->to_formula(tmp); conjs.push_back(tmp); } bool_rewriter(m).mk_and(conjs.size(), conjs.c_ptr(), fml); } void product_relation::display(std::ostream & out) const { if (m_relations.empty()) { out << "{}\n"; return; } out << "Product of the following relations:\n"; for (unsigned i = 0; i < m_relations.size(); ++i) { m_relations[i]->display(out); } } }; z3-z3-4.8.7/src/muz/rel/dl_product_relation.h000066400000000000000000000157371356505360400210350ustar00rootroot00000000000000/*++ Copyright (c) 2010 Microsoft Corporation Module Name: dl_product_relation.h Abstract: A Relation relation combinator. Author: Nikolaj Bjorner (nbjorner) 2010-4-11 Revision History: --*/ #ifndef DL_PRODUCT_RELATION_H_ #define DL_PRODUCT_RELATION_H_ #include "muz/base/dl_context.h" #include "muz/rel/dl_relation_manager.h" namespace datalog { class product_relation; struct rel_spec : public svector { bool well_formed() const { return true; } }; class product_relation_plugin : public relation_plugin { friend class product_relation; private: class join_fn; class transform_fn; class mutator_fn; class aligned_union_fn; class unaligned_union_fn; class single_non_transparent_src_union_fn; class filter_equal_fn; class filter_identical_fn; class filter_interpreted_fn; struct fid_hash { typedef family_id data; unsigned operator()(data x) const { return static_cast(x); } }; rel_spec_store > m_spec_store; family_id get_relation_kind(const product_relation & r); bool is_product_relation(relation_base * r) { return r->get_plugin().is_product_relation(); } public: static product_relation_plugin& get_plugin(relation_manager & rmgr); product_relation_plugin(relation_manager& m); void initialize(family_id fid) override; bool can_handle_signature(const relation_signature & s) override; bool can_handle_signature(const relation_signature & s, family_id kind) override; static symbol get_name() { return symbol("product_relation"); } family_id get_relation_kind(const relation_signature & sig, const rel_spec & spec); relation_base * mk_empty(const relation_signature & s) override; relation_base * mk_empty(const relation_signature & s, family_id kind) override; relation_base * mk_full(func_decl* p, const relation_signature & s) override; relation_base * mk_full(func_decl* p, const relation_signature & s, family_id kind) override; protected: relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) override; relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, const unsigned * removed_cols) override; relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, const unsigned * permutation_cycle) override; relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta) override; relation_union_fn * mk_widen_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta) override; relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, const unsigned * identical_cols) override; relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, unsigned col) override; relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition) override; static bool is_product_relation(relation_base const& r); private: static product_relation& get(relation_base& r); static product_relation const & get(relation_base const& r); static product_relation* get(relation_base* r); static product_relation const* get(relation_base const* r); relation_union_fn * mk_union_w_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta, bool is_widen); bool are_aligned(const product_relation& r1, const product_relation& r2); static void get_common_spec(const ptr_vector & rels, rel_spec & res); }; class product_relation : public relation_base { friend class product_relation_plugin; friend class product_relation_plugin::join_fn; friend class product_relation_plugin::transform_fn; friend class product_relation_plugin::mutator_fn; friend class product_relation_plugin::aligned_union_fn; friend class product_relation_plugin::unaligned_union_fn; friend class product_relation_plugin::single_non_transparent_src_union_fn; friend class product_relation_plugin::filter_equal_fn; friend class product_relation_plugin::filter_identical_fn; friend class product_relation_plugin::filter_interpreted_fn; /** If m_relations is empty, value of this determines whether the relation is empty or full. */ bool m_default_empty; /** There must not be two relations of the same kind */ ptr_vector m_relations; /** Array of kinds of inner relations. If two product relations have equal signature and specification, their m_relations arrays contain corresponding relations at the same indexes. The value returned by get_kind() depends uniquely on the specification. */ rel_spec m_spec; /** \brief Ensure the kind assigned to this relation reflects the types of inner relations. */ void ensure_correct_kind(); /** The current specification must be a subset of the new one. */ void convert_spec(const rel_spec & spec); public: product_relation(product_relation_plugin& p, relation_signature const& s); product_relation(product_relation_plugin& p, relation_signature const& s, unsigned num_relations, relation_base** relations); ~product_relation() override; bool empty() const override; void add_fact(const relation_fact & f) override; bool contains_fact(const relation_fact & f) const override; product_relation * clone() const override; product_relation * complement(func_decl* p) const override; void display(std::ostream & out) const override; void to_formula(expr_ref& fml) const override; product_relation_plugin& get_plugin() const; unsigned size() const { return m_relations.size(); } relation_base& operator[](unsigned i) const { return *m_relations[i]; } /** If all relations except one are sieve_relations with no inner columns, return true and into \c idx assign index of that relation. Otherwise return false. */ bool try_get_single_non_transparent(unsigned & idx) const; bool is_precise() const override { for (unsigned i = 0; i < m_relations.size(); ++i) { if (!m_relations[i]->is_precise()) { return false; } } return true; } }; }; #endif z3-z3-4.8.7/src/muz/rel/dl_relation_manager.cpp000066400000000000000000002037041356505360400213130ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_relation_manager.cpp Abstract: Author: Krystof Hoder (t-khoder) 2010-09-14. Revision History: --*/ #include #include "ast/ast_pp.h" #include "muz/rel/dl_check_table.h" #include "muz/base/dl_context.h" #include "muz/rel/dl_finite_product_relation.h" #include "muz/rel/dl_product_relation.h" #include "muz/rel/dl_sieve_relation.h" #include "muz/rel/dl_table_relation.h" #include "muz/rel/dl_relation_manager.h" namespace datalog { relation_manager::~relation_manager() { reset(); } void relation_manager::reset_relations() { for (auto const& kv : m_relations) { func_decl * pred = kv.m_key; get_context().get_manager().dec_ref(pred); //inc_ref in get_relation kv.m_value->deallocate(); } m_relations.reset(); } void relation_manager::reset() { reset_relations(); m_favourite_table_plugin = static_cast(nullptr); m_favourite_relation_plugin = static_cast(nullptr); dealloc_ptr_vector_content(m_table_plugins); m_table_plugins.reset(); dealloc_ptr_vector_content(m_relation_plugins); m_relation_plugins.reset(); m_next_table_fid = 0; m_next_relation_fid = 0; } dl_decl_util & relation_manager::get_decl_util() const { return get_context().get_decl_util(); } family_id relation_manager::get_next_relation_fid(relation_plugin & claimer) { unsigned res = m_next_relation_fid++; m_kind2plugin.insert(res, &claimer); return res; } void relation_manager::set_predicate_kind(func_decl * pred, family_id kind) { SASSERT(!m_relations.contains(pred)); m_pred_kinds.insert(pred, kind); } family_id relation_manager::get_requested_predicate_kind(func_decl * pred) { family_id res; if(m_pred_kinds.find(pred, res)) { return res; } else { return null_family_id; } } relation_base & relation_manager::get_relation(func_decl * pred) { relation_base * res = try_get_relation(pred); if(!res) { relation_signature sig; from_predicate(pred, sig); family_id rel_kind = get_requested_predicate_kind(pred); res = mk_empty_relation(sig, rel_kind); store_relation(pred, res); } return *res; } relation_base * relation_manager::try_get_relation(func_decl * pred) const { relation_base * res = nullptr; if(!m_relations.find(pred, res)) { return nullptr; } SASSERT(res); return res; } void relation_manager::store_relation(func_decl * pred, relation_base * rel) { SASSERT(rel); relation_map::obj_map_entry * e = m_relations.insert_if_not_there2(pred, 0); if (e->get_data().m_value) { e->get_data().m_value->deallocate(); } else { get_context().get_manager().inc_ref(pred); //dec_ref in reset } e->get_data().m_value = rel; } void relation_manager::collect_non_empty_predicates(decl_set & res) const { for (auto const& kv : m_relations) { if (!kv.m_value->fast_empty()) { res.insert(kv.m_key); } } } void relation_manager::restrict_predicates(const decl_set & preds) { ptr_vector to_remove; for (auto const& kv : m_relations) { func_decl* pred = kv.m_key; if (!preds.contains(pred)) { to_remove.insert(pred); } } for (func_decl* pred : to_remove) { m_relations.find(pred)->deallocate(); m_relations.remove(pred); get_context().get_manager().dec_ref(pred); } set_intersection(m_saturated_rels, preds); } void relation_manager::register_plugin(table_plugin * plugin) { plugin->initialize(get_next_table_fid()); m_table_plugins.push_back(plugin); if(plugin->get_name()==get_context().default_table()) { m_favourite_table_plugin = plugin; } table_relation_plugin * tr_plugin = alloc(table_relation_plugin, *plugin, *this); register_relation_plugin_impl(tr_plugin); m_table_relation_plugins.insert(plugin, tr_plugin); if (plugin->get_name()==get_context().default_table()) { m_favourite_table_plugin = plugin; m_favourite_relation_plugin = tr_plugin; } symbol checker_name = get_context().default_table_checker(); if (get_context().default_table_checked() && get_table_plugin(checker_name)) { if( m_favourite_table_plugin && (plugin==m_favourite_table_plugin || plugin->get_name()==checker_name) ) { symbol checked_name = get_context().default_table(); //the plugins we need to create the checking plugin were just added SASSERT(m_favourite_table_plugin->get_name()==get_context().default_table()); table_plugin * checking_plugin = alloc(check_table_plugin, *this, checker_name, checked_name); register_plugin(checking_plugin); m_favourite_table_plugin = checking_plugin; } if (m_favourite_relation_plugin && m_favourite_relation_plugin->from_table()) { table_relation_plugin * fav_rel_plugin = static_cast(m_favourite_relation_plugin); if(&fav_rel_plugin->get_table_plugin()==plugin || plugin->get_name()==checker_name) { //the plugins we need to create the checking table_relation_plugin were just added SASSERT(m_favourite_relation_plugin->get_name() == get_context().default_relation()); symbol checked_name = fav_rel_plugin->get_table_plugin().get_name(); table_plugin * checking_plugin = alloc(check_table_plugin, *this, checker_name, checked_name); register_plugin(checking_plugin); table_relation_plugin * checking_tr_plugin = alloc(table_relation_plugin, *checking_plugin, *this); register_relation_plugin_impl(checking_tr_plugin); m_table_relation_plugins.insert(checking_plugin, checking_tr_plugin); m_favourite_relation_plugin = checking_tr_plugin; } } } } void relation_manager::register_relation_plugin_impl(relation_plugin * plugin) { TRACE("dl", tout << "register: " << plugin->get_name() << "\n";); m_relation_plugins.push_back(plugin); plugin->initialize(get_next_relation_fid(*plugin)); if (plugin->get_name() == get_context().default_relation()) { m_favourite_relation_plugin = plugin; } if(plugin->is_finite_product_relation()) { finite_product_relation_plugin * fprp = static_cast(plugin); relation_plugin * inner = &fprp->get_inner_plugin(); m_finite_product_relation_plugins.insert(inner, fprp); } } relation_plugin * relation_manager::try_get_appropriate_plugin(const relation_signature & s) { if(m_favourite_relation_plugin && m_favourite_relation_plugin->can_handle_signature(s)) { return m_favourite_relation_plugin; } for (auto * r : m_relation_plugins) { if (r->can_handle_signature(s)) { return r; } } return nullptr; } relation_plugin & relation_manager::get_appropriate_plugin(const relation_signature & s) { relation_plugin * res = try_get_appropriate_plugin(s); if (!res) { throw default_exception("no suitable plugin found for given relation signature"); } return *res; } table_plugin * relation_manager::try_get_appropriate_plugin(const table_signature & t) { if (m_favourite_table_plugin && m_favourite_table_plugin->can_handle_signature(t)) { return m_favourite_table_plugin; } for (auto * a : m_table_plugins) { if (a->can_handle_signature(t)) { return a; } } return nullptr; } table_plugin & relation_manager::get_appropriate_plugin(const table_signature & t) { table_plugin * res = try_get_appropriate_plugin(t); if(!res) { throw default_exception("no suitable plugin found for given table signature"); } return *res; } relation_plugin * relation_manager::get_relation_plugin(symbol const& s) { for (auto* r : m_relation_plugins) { if(r->get_name() == s) { return r; } } return nullptr; } relation_plugin & relation_manager::get_relation_plugin(family_id kind) { SASSERT(kind>=0); SASSERT(kindget_name()==k) { return tp; } } return nullptr; } table_relation_plugin & relation_manager::get_table_relation_plugin(table_plugin & tp) { table_relation_plugin * res = nullptr; VERIFY( m_table_relation_plugins.find(&tp, res) ); return *res; } bool relation_manager::try_get_finite_product_relation_plugin(const relation_plugin & inner, finite_product_relation_plugin * & res) { return m_finite_product_relation_plugins.find(&inner, res); } table_base * relation_manager::mk_empty_table(const table_signature & s) { return get_appropriate_plugin(s).mk_empty(s); } bool relation_manager::is_non_explanation(relation_signature const& s) const { dl_decl_util & decl_util = get_context().get_decl_util(); unsigned n = s.size(); for(unsigned i = 0; i < n; i++) { if(decl_util.is_rule_sort(s[i])) { return false; } } return true; } relation_base * relation_manager::mk_empty_relation(const relation_signature & s, func_decl* pred) { return mk_empty_relation(s, get_requested_predicate_kind(pred)); } relation_base * relation_manager::mk_empty_relation(const relation_signature & s, family_id kind) { if (kind != null_family_id) { relation_plugin & plugin = get_relation_plugin(kind); if (plugin.can_handle_signature(s, kind)) return plugin.mk_empty(s, kind); } relation_base * res; relation_plugin* p = m_favourite_relation_plugin; if (p && p->can_handle_signature(s)) { return p->mk_empty(s); } if (mk_empty_table_relation(s, res)) { return res; } for (relation_plugin* p1 : m_relation_plugins) { if (p1->can_handle_signature(s)) { return p1->mk_empty(s); } } //If there is no plugin to handle the signature, we just create an empty product relation and //stuff will be added to it by later operations. TRACE("dl", s.output(get_context().get_manager(), tout << "empty product relation"); tout << "\n";); return product_relation_plugin::get_plugin(*this).mk_empty(s); } /** The newly created object takes ownership of the \c table object. */ relation_base * relation_manager::mk_table_relation(const relation_signature & s, table_base * table) { SASSERT(s.size()==table->get_signature().size()); return get_table_relation_plugin(table->get_plugin()).mk_from_table(s, table); } bool relation_manager::mk_empty_table_relation(const relation_signature & s, relation_base * & result) { table_signature tsig; if(!relation_signature_to_table(s, tsig)) { return false; } table_base * table = mk_empty_table(tsig); result = mk_table_relation(s, table); return true; } relation_base * relation_manager::mk_full_relation(const relation_signature & s, func_decl* p, family_id kind) { if (kind != null_family_id) { relation_plugin & plugin = get_relation_plugin(kind); if (plugin.can_handle_signature(s, kind)) { return plugin.mk_full(p, s, kind); } } return get_appropriate_plugin(s).mk_full(p, s, null_family_id); } relation_base * relation_manager::mk_full_relation(const relation_signature & s, func_decl* pred) { family_id kind = get_requested_predicate_kind(pred); return mk_full_relation(s, pred, kind); } void relation_manager::relation_to_table(const relation_sort & sort, const relation_element & from, table_element & to) { SASSERT(from->get_num_args()==0); VERIFY(get_context().get_decl_util().is_numeral_ext(from, to)); } void relation_manager::table_to_relation(const relation_sort & sort, const table_element & from, relation_element & to) { to = get_decl_util().mk_numeral(from, sort); } void relation_manager::table_to_relation(const relation_sort & sort, const table_element & from, relation_element_ref & to) { relation_element rel_el; table_to_relation(sort, from, rel_el); to = rel_el; } void relation_manager::table_to_relation(const relation_sort & sort, const table_element & from, const relation_fact::el_proxy & to) { relation_element rel_el; table_to_relation(sort, from, rel_el); to = rel_el; } bool relation_manager::relation_sort_to_table(const relation_sort & from, table_sort & to) { return get_context().get_decl_util().try_get_size(from, to); } void relation_manager::from_predicate(func_decl * pred, unsigned arg_index, relation_sort & result) { result = pred->get_domain(arg_index); } void relation_manager::from_predicate(func_decl * pred, relation_signature & result) { result.reset(); unsigned arg_num=pred->get_arity(); for(unsigned i=0;iget_name() << "\n"; kv.m_value->display(out); } } void relation_manager::display_relation_sizes(std::ostream & out) const { for (auto const& kv : m_relations) { out << "Relation " << kv.m_key->get_name() << " has size " << kv.m_value->get_size_estimate_rows() << "\n"; } } void relation_manager::display_output_tables(rule_set const& rules, std::ostream & out) const { const decl_set & output_preds = rules.get_output_predicates(); for (func_decl * pred : output_preds) { relation_base * rel = try_get_relation(pred); if (!rel) { out << "Tuples in " << pred->get_name() << ": \n"; continue; } rel->display_tuples(*pred, out); } } // ----------------------------------- // // relation operations // // ----------------------------------- class relation_manager::empty_signature_relation_join_fn : public relation_join_fn { public: relation_base * operator()(const relation_base & r1, const relation_base & r2) override { TRACE("dl", tout << r1.get_plugin().get_name() << " " << r2.get_plugin().get_name() << "\n";); if(r1.get_signature().empty()) { if(r1.empty()) { return r2.get_manager().mk_empty_relation(r2.get_signature(), r2.get_kind()); } else { return r2.clone(); } } else { SASSERT(r2.get_signature().empty()); if(r2.empty()) { return r1.get_manager().mk_empty_relation(r1.get_signature(), r1.get_kind()); } else { return r1.clone(); } } } }; relation_join_fn * relation_manager::mk_join_fn(const relation_base & t1, const relation_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, bool allow_product_relation) { relation_plugin * p1 = &t1.get_plugin(); relation_plugin * p2 = &t2.get_plugin(); relation_join_fn * res = p1->mk_join_fn(t1, t2, col_cnt, cols1, cols2); if(!res && p1!=p2) { res = p2->mk_join_fn(t1, t2, col_cnt, cols1, cols2); } if(!res && (t1.get_signature().empty() || t2.get_signature().empty())) { res = alloc(empty_signature_relation_join_fn); } finite_product_relation_plugin * fprp; if(!res && p1->from_table() && try_get_finite_product_relation_plugin(*p2, fprp)) { //we downcast here to relation_plugin so that we don't have to declare //relation_manager as a friend class of finite_product_relation_plugin res = static_cast(fprp)->mk_join_fn(t1, t2, col_cnt, cols1, cols2); } if(!res && p2->from_table() && try_get_finite_product_relation_plugin(*p1, fprp)) { res = static_cast(fprp)->mk_join_fn(t1, t2, col_cnt, cols1, cols2); } if(!res && allow_product_relation) { relation_plugin & product_plugin = product_relation_plugin::get_plugin(*this); res = product_plugin.mk_join_fn(t1, t2, col_cnt, cols1, cols2); } return res; } relation_transformer_fn * relation_manager::mk_project_fn(const relation_base & t, unsigned col_cnt, const unsigned * removed_cols) { return t.get_plugin().mk_project_fn(t, col_cnt, removed_cols); } class relation_manager::default_relation_filter_interpreted_and_project_fn : public relation_transformer_fn { scoped_ptr m_filter; scoped_ptr m_project; unsigned_vector m_removed_cols; public: /** This constructor should be used only if we know that the projection operation exists for the result of the join. */ default_relation_filter_interpreted_and_project_fn( relation_mutator_fn* filter, unsigned removed_col_cnt, const unsigned * removed_cols) : m_filter(filter), m_project(nullptr), m_removed_cols(removed_col_cnt, removed_cols) {} relation_base * operator()(const relation_base & t) override { scoped_rel t1 = t.clone(); (*m_filter)(*t1); if( !m_project) { relation_manager & rmgr = t1->get_plugin().get_manager(); m_project = rmgr.mk_project_fn(*t1, m_removed_cols.size(), m_removed_cols.c_ptr()); if (!m_project) { throw default_exception("projection does not exist"); } } return (*m_project)(*t1); } }; relation_transformer_fn * relation_manager::mk_filter_interpreted_and_project_fn( const relation_base & t, app * condition, unsigned removed_col_cnt, const unsigned * removed_cols) { relation_transformer_fn* res = t.get_plugin().mk_filter_interpreted_and_project_fn( t, condition, removed_col_cnt, removed_cols); if (!res) { relation_mutator_fn* filter_fn = mk_filter_interpreted_fn(t, condition); if (filter_fn) { res = alloc(default_relation_filter_interpreted_and_project_fn, filter_fn, removed_col_cnt, removed_cols); } } return res; } class relation_manager::default_relation_apply_sequential_fn : public relation_mutator_fn { ptr_vector m_mutators; public: default_relation_apply_sequential_fn(unsigned n, relation_mutator_fn ** mutators): m_mutators(n, mutators) { } ~default_relation_apply_sequential_fn() override { std::for_each(m_mutators.begin(), m_mutators.end(), delete_proc()); } void operator()(relation_base& t) override { for (unsigned i = 0; i < m_mutators.size(); ++i) { if (t.empty()) return; (*(m_mutators[i]))(t); } } }; relation_mutator_fn * relation_manager::mk_apply_sequential_fn(unsigned n, relation_mutator_fn ** mutators) { return alloc(default_relation_apply_sequential_fn, n, mutators); } class relation_manager::default_relation_join_project_fn : public relation_join_fn { scoped_ptr m_join; scoped_ptr m_project; unsigned_vector m_removed_cols; public: /** This constructor should be used only if we know that the projection operation exists for the result of the join. */ default_relation_join_project_fn(join_fn * join, unsigned removed_col_cnt, const unsigned * removed_cols) : m_join(join), m_project(nullptr), m_removed_cols(removed_col_cnt, removed_cols) {} relation_base * operator()(const relation_base & t1, const relation_base & t2) override { scoped_rel aux = (*m_join)(t1, t2); if(!m_project) { relation_manager & rmgr = aux->get_plugin().get_manager(); m_project = rmgr.mk_project_fn(*aux, m_removed_cols.size(), m_removed_cols.c_ptr()); if(!m_project) { throw default_exception("projection does not exist"); } } relation_base * res = (*m_project)(*aux); return res; } }; relation_join_fn * relation_manager::mk_join_project_fn(const relation_base & t1, const relation_base & t2, unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols, bool allow_product_relation_join) { relation_join_fn * res = t1.get_plugin().mk_join_project_fn(t1, t2, joined_col_cnt, cols1, cols2, removed_col_cnt, removed_cols); if(!res && &t1.get_plugin()!=&t2.get_plugin()) { res = t2.get_plugin().mk_join_project_fn(t1, t2, joined_col_cnt, cols1, cols2, removed_col_cnt, removed_cols); } if(!res) { relation_join_fn * join = mk_join_fn(t1, t2, joined_col_cnt, cols1, cols2, allow_product_relation_join); if(join) { res = alloc(default_relation_join_project_fn, join, removed_col_cnt, removed_cols); } } return res; } relation_transformer_fn * relation_manager::mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, const unsigned * permutation_cycle) { return t.get_plugin().mk_rename_fn(t, permutation_cycle_len, permutation_cycle); } relation_transformer_fn * relation_manager::mk_permutation_rename_fn(const relation_base & t, const unsigned * permutation) { relation_transformer_fn * res = t.get_plugin().mk_permutation_rename_fn(t, permutation); if(!res) { res = alloc(default_relation_permutation_rename_fn, t, permutation); } return res; } relation_union_fn * relation_manager::mk_union_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta) { relation_union_fn * res = tgt.get_plugin().mk_union_fn(tgt, src, delta); if(!res && &tgt.get_plugin()!=&src.get_plugin()) { res = src.get_plugin().mk_union_fn(tgt, src, delta); } if(!res && delta && &tgt.get_plugin()!=&delta->get_plugin() && &src.get_plugin()!=&delta->get_plugin()) { res = delta->get_plugin().mk_union_fn(tgt, src, delta); } // TRACE("dl", tout << src.get_plugin().get_name() << " " << tgt.get_plugin().get_name() << " " << (res?"created":"not created") << "\n";); return res; } relation_union_fn * relation_manager::mk_widen_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta) { relation_union_fn * res = tgt.get_plugin().mk_widen_fn(tgt, src, delta); if(!res && &tgt.get_plugin()!=&src.get_plugin()) { res = src.get_plugin().mk_widen_fn(tgt, src, delta); } if(!res && delta && &tgt.get_plugin()!=&delta->get_plugin() && &src.get_plugin()!=&delta->get_plugin()) { res = delta->get_plugin().mk_widen_fn(tgt, src, delta); } if(!res) { res = mk_union_fn(tgt, src, delta); } return res; } relation_mutator_fn * relation_manager::mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, const unsigned * identical_cols) { return t.get_plugin().mk_filter_identical_fn(t, col_cnt, identical_cols); } relation_mutator_fn * relation_manager::mk_filter_equal_fn(const relation_base & t, const relation_element & value, unsigned col) { return t.get_plugin().mk_filter_equal_fn(t, value, col); } relation_mutator_fn * relation_manager::mk_filter_interpreted_fn(const relation_base & t, app * condition) { return t.get_plugin().mk_filter_interpreted_fn(t, condition); } class relation_manager::default_relation_select_equal_and_project_fn : public relation_transformer_fn { scoped_ptr m_filter; scoped_ptr m_project; public: default_relation_select_equal_and_project_fn(relation_mutator_fn * filter, relation_transformer_fn * project) : m_filter(filter), m_project(project) {} relation_base * operator()(const relation_base & t1) override { TRACE("dl", tout << t1.get_plugin().get_name() << "\n";); scoped_rel aux = t1.clone(); (*m_filter)(*aux); relation_base * res = (*m_project)(*aux); return res; } }; relation_transformer_fn * relation_manager::mk_select_equal_and_project_fn(const relation_base & t, const relation_element & value, unsigned col) { relation_transformer_fn * res = t.get_plugin().mk_select_equal_and_project_fn(t, value, col); if(!res) { relation_mutator_fn * selector = mk_filter_equal_fn(t, value, col); if(selector) { relation_transformer_fn * projector = mk_project_fn(t, 1, &col); if(projector) { res = alloc(default_relation_select_equal_and_project_fn, selector, projector); } else { dealloc(selector); } } } return res; } class relation_manager::default_relation_intersection_filter_fn : public relation_intersection_filter_fn { scoped_ptr m_join_fun; scoped_ptr m_union_fun; public: default_relation_intersection_filter_fn(relation_join_fn * join_fun, relation_union_fn * union_fun) : m_join_fun(join_fun), m_union_fun(union_fun) {} void operator()(relation_base & tgt, const relation_base & intersected_obj) override { scoped_rel filtered_rel = (*m_join_fun)(tgt, intersected_obj); TRACE("dl", tgt.display(tout << "tgt:\n"); intersected_obj.display(tout << "intersected:\n"); filtered_rel->display(tout << "filtered:\n"); ); if(!m_union_fun) { SASSERT(tgt.can_swap(*filtered_rel)); tgt.swap(*filtered_rel); } tgt.reset(); TRACE("dl", tgt.display(tout << "target reset:\n"); ); (*m_union_fun)(tgt, *filtered_rel); TRACE("dl", tgt.display(tout << "intersected target:\n"); ); } }; relation_intersection_filter_fn * relation_manager::try_mk_default_filter_by_intersection_fn( const relation_base & tgt, const relation_base & src, unsigned joined_col_cnt, const unsigned * tgt_cols, const unsigned * src_cols) { TRACE("dl_verbose", tout << tgt.get_plugin().get_name() << "\n";); unsigned_vector join_removed_cols; add_sequence(tgt.get_signature().size(), src.get_signature().size(), join_removed_cols); scoped_rel join_fun = mk_join_project_fn(tgt, src, joined_col_cnt, tgt_cols, src_cols, join_removed_cols.size(), join_removed_cols.c_ptr(), false); if(!join_fun) { return nullptr; } //we perform the join operation here to see what the result is scoped_rel join_res = (*join_fun)(tgt, src); if(tgt.can_swap(*join_res)) { return alloc(default_relation_intersection_filter_fn, join_fun.release(), nullptr); } if(join_res->get_plugin().is_product_relation()) { //we cannot have the product relation here, since it uses the intersection operation //for unions and therefore we would get into an infinite recursion return nullptr; } scoped_rel union_fun = mk_union_fn(tgt, *join_res); if(!union_fun) { return nullptr; } return alloc(default_relation_intersection_filter_fn, join_fun.release(), union_fun.release()); } relation_intersection_filter_fn * relation_manager::mk_filter_by_intersection_fn(const relation_base & t, const relation_base & src, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * src_cols) { TRACE("dl_verbose", tout << t.get_plugin().get_name() << "\n";); relation_intersection_filter_fn * res = t.get_plugin().mk_filter_by_intersection_fn(t, src, joined_col_cnt, t_cols, src_cols); if(!res && &t.get_plugin()!=&src.get_plugin()) { res = src.get_plugin().mk_filter_by_intersection_fn(t, src, joined_col_cnt, t_cols, src_cols); } if(!res) { res = try_mk_default_filter_by_intersection_fn(t, src, joined_col_cnt, t_cols, src_cols); } return res; } relation_intersection_filter_fn * relation_manager::mk_filter_by_intersection_fn(const relation_base & tgt, const relation_base & src) { TRACE("dl_verbose", tout << tgt.get_plugin().get_name() << "\n";); SASSERT(tgt.get_signature()==src.get_signature()); unsigned sz = tgt.get_signature().size(); unsigned_vector cols; add_sequence(0, sz, cols); return mk_filter_by_intersection_fn(tgt, src, cols, cols); } relation_intersection_filter_fn * relation_manager::mk_filter_by_negation_fn(const relation_base & t, const relation_base & negated_obj, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) { TRACE("dl", tout << t.get_plugin().get_name() << "\n";); relation_intersection_filter_fn * res = t.get_plugin().mk_filter_by_negation_fn(t, negated_obj, joined_col_cnt, t_cols, negated_cols); if(!res && &t.get_plugin()!=&negated_obj.get_plugin()) { res = negated_obj.get_plugin().mk_filter_by_negation_fn(t, negated_obj, joined_col_cnt, t_cols, negated_cols); } return res; } // ----------------------------------- // // table operations // // ----------------------------------- class relation_manager::default_table_join_fn : public convenient_table_join_fn { unsigned m_col_cnt; public: default_table_join_fn(const table_signature & t1_sig, const table_signature & t2_sig, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) : convenient_table_join_fn(t1_sig, t2_sig, col_cnt, cols1, cols2), m_col_cnt(col_cnt) {} table_base * operator()(const table_base & t1, const table_base & t2) override { table_plugin * plugin = &t1.get_plugin(); const table_signature & res_sign = get_result_signature(); if (!plugin->can_handle_signature(res_sign)) { plugin = &t2.get_plugin(); if (!plugin->can_handle_signature(res_sign)) { plugin = &t1.get_manager().get_appropriate_plugin(res_sign); } } SASSERT(plugin->can_handle_signature(res_sign)); table_base * res = plugin->mk_empty(res_sign); unsigned t1cols = t1.get_signature().size(); unsigned t2cols = t2.get_signature().size(); unsigned t1first_func = t1.get_signature().first_functional(); unsigned t2first_func = t2.get_signature().first_functional(); table_base::iterator els1it = t1.begin(); table_base::iterator els1end = t1.end(); table_base::iterator els2end = t2.end(); table_fact acc; for(; els1it!=els1end; ++els1it) { const table_base::row_interface & row1 = *els1it; table_base::iterator els2it = t2.begin(); for(; els2it!=els2end; ++els2it) { const table_base::row_interface & row2 = *els2it; bool match=true; for(unsigned i=0; iadd_fact(acc); } } return res; } }; table_join_fn * relation_manager::mk_join_fn(const table_base & t1, const table_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { table_join_fn * res = t1.get_plugin().mk_join_fn(t1, t2, col_cnt, cols1, cols2); if(!res && &t1.get_plugin()!=&t2.get_plugin()) { res = t2.get_plugin().mk_join_fn(t1, t2, col_cnt, cols1, cols2); } if(!res) { table_signature sig; table_signature::from_join(t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2, sig); res = alloc(default_table_join_fn, t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2); } return res; } class relation_manager::auxiliary_table_transformer_fn { table_fact m_row; public: virtual ~auxiliary_table_transformer_fn() {} virtual const table_signature & get_result_signature() const = 0; virtual void modify_fact(table_fact & f) const = 0; table_base * operator()(const table_base & t) { table_plugin & plugin = t.get_plugin(); const table_signature & res_sign = get_result_signature(); SASSERT(plugin.can_handle_signature(res_sign)); table_base * res = plugin.mk_empty(res_sign); for (table_base::row_interface& a : t) { a.get_fact(m_row); modify_fact(m_row); res->add_fact(m_row); } return res; } }; class relation_manager::default_table_project_fn : public convenient_table_project_fn, auxiliary_table_transformer_fn { public: default_table_project_fn(const table_signature & orig_sig, unsigned removed_col_cnt, const unsigned * removed_cols) : convenient_table_project_fn(orig_sig, removed_col_cnt, removed_cols) { SASSERT(removed_col_cnt>0); } const table_signature & get_result_signature() const override { return convenient_table_project_fn::get_result_signature(); } void modify_fact(table_fact & f) const override { project_out_vector_columns(f, m_removed_cols); } table_base * operator()(const table_base & t) override { return auxiliary_table_transformer_fn::operator()(t); } }; class relation_manager::null_signature_table_project_fn : public table_transformer_fn { const table_signature m_empty_sig; public: null_signature_table_project_fn() : m_empty_sig() {} table_base * operator()(const table_base & t) override { relation_manager & m = t.get_plugin().get_manager(); table_base * res = m.mk_empty_table(m_empty_sig); if(!t.empty()) { table_fact el; res->add_fact(el); } return res; } }; table_transformer_fn * relation_manager::mk_project_fn(const table_base & t, unsigned col_cnt, const unsigned * removed_cols) { table_transformer_fn * res = t.get_plugin().mk_project_fn(t, col_cnt, removed_cols); if(!res && col_cnt==t.get_signature().size()) { //all columns are projected out res = alloc(null_signature_table_project_fn); } if(!res) { res = alloc(default_table_project_fn, t.get_signature(), col_cnt, removed_cols); } return res; } class relation_manager::default_table_join_project_fn : public convenient_table_join_project_fn { scoped_ptr m_join; scoped_ptr m_project; unsigned_vector m_removed_cols; public: default_table_join_project_fn(join_fn * join, const table_base & t1, const table_base & t2, unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols) : convenient_table_join_project_fn(t1.get_signature(), t2.get_signature(), joined_col_cnt, cols1, cols2, removed_col_cnt, removed_cols), m_join(join), m_removed_cols(removed_col_cnt, removed_cols) {} class unreachable_reducer : public table_row_pair_reduce_fn { void operator()(table_element * func_columns, const table_element * merged_func_columns) override { //we do project_with_reduce only if we are sure there will be no reductions //(see code of the table_signature::from_join_project function) UNREACHABLE(); } }; table_base * operator()(const table_base & t1, const table_base & t2) override { table_base * aux = (*m_join)(t1, t2); if(m_project==0) { relation_manager & rmgr = aux->get_plugin().get_manager(); if(get_result_signature().functional_columns()!=0) { //to preserve functional columns we need to do the project_with_reduction unreachable_reducer * reducer = alloc(unreachable_reducer); m_project = rmgr.mk_project_with_reduce_fn(*aux, m_removed_cols.size(), m_removed_cols.c_ptr(), reducer); } else { m_project = rmgr.mk_project_fn(*aux, m_removed_cols); } if(!m_project) { throw default_exception("projection for table does not exist"); } } table_base * res = (*m_project)(*aux); aux->deallocate(); return res; } }; table_join_fn * relation_manager::mk_join_project_fn(const table_base & t1, const table_base & t2, unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols) { table_join_fn * res = t1.get_plugin().mk_join_project_fn(t1, t2, joined_col_cnt, cols1, cols2, removed_col_cnt, removed_cols); if(!res && &t1.get_plugin()!=&t2.get_plugin()) { res = t2.get_plugin().mk_join_project_fn(t1, t2, joined_col_cnt, cols1, cols2, removed_col_cnt, removed_cols); } if(!res) { table_join_fn * join = mk_join_fn(t1, t2, joined_col_cnt, cols1, cols2); if(join) { res = alloc(default_table_join_project_fn, join, t1, t2, joined_col_cnt, cols1, cols2, removed_col_cnt, removed_cols); } } return res; } class relation_manager::default_table_rename_fn : public convenient_table_rename_fn, auxiliary_table_transformer_fn { public: default_table_rename_fn(const table_signature & orig_sig, unsigned permutation_cycle_len, const unsigned * permutation_cycle) : convenient_table_rename_fn(orig_sig, permutation_cycle_len, permutation_cycle) { SASSERT(permutation_cycle_len>=2); } const table_signature & get_result_signature() const override { return convenient_table_rename_fn::get_result_signature(); } void modify_fact(table_fact & f) const override { permutate_by_cycle(f, m_cycle); } table_base * operator()(const table_base & t) override { return auxiliary_table_transformer_fn::operator()(t); } }; table_transformer_fn * relation_manager::mk_rename_fn(const table_base & t, unsigned permutation_cycle_len, const unsigned * permutation_cycle) { table_transformer_fn * res = t.get_plugin().mk_rename_fn(t, permutation_cycle_len, permutation_cycle); if(!res) { res = alloc(default_table_rename_fn, t.get_signature(), permutation_cycle_len, permutation_cycle); } return res; } table_transformer_fn * relation_manager::mk_permutation_rename_fn(const table_base & t, const unsigned * permutation) { table_transformer_fn * res = t.get_plugin().mk_permutation_rename_fn(t, permutation); if(!res) { res = alloc(default_table_permutation_rename_fn, t, permutation); } return res; } class relation_manager::default_table_union_fn : public table_union_fn { table_fact m_row; public: void operator()(table_base & tgt, const table_base & src, table_base * delta) override { for (table_base::row_interface& a : src) { a.get_fact(m_row); if (delta) { if(!tgt.contains_fact(m_row)) { tgt.add_new_fact(m_row); delta->add_fact(m_row); } } else { //if there's no delta, we don't need to know whether we are actually adding a new fact tgt.add_fact(m_row); } } } }; table_union_fn * relation_manager::mk_union_fn(const table_base & tgt, const table_base & src, const table_base * delta) { table_union_fn * res = tgt.get_plugin().mk_union_fn(tgt, src, delta); if(!res && &tgt.get_plugin()!=&src.get_plugin()) { res = src.get_plugin().mk_union_fn(tgt, src, delta); } if(!res && delta && &tgt.get_plugin()!=&delta->get_plugin() && &src.get_plugin()!=&delta->get_plugin()) { res = delta->get_plugin().mk_union_fn(tgt, src, delta); } if(!res) { res = alloc(default_table_union_fn); } return res; } table_union_fn * relation_manager::mk_widen_fn(const table_base & tgt, const table_base & src, const table_base * delta) { table_union_fn * res = tgt.get_plugin().mk_widen_fn(tgt, src, delta); if(!res && &tgt.get_plugin()!=&src.get_plugin()) { res = src.get_plugin().mk_widen_fn(tgt, src, delta); } if(!res && delta && &tgt.get_plugin()!=&delta->get_plugin() && &src.get_plugin()!=&delta->get_plugin()) { res = delta->get_plugin().mk_widen_fn(tgt, src, delta); } if(!res) { res = mk_union_fn(tgt, src, delta); } return res; } /** An auixiliary class for functors that perform filtering. It performs the table traversal and only asks for each individual row whether it should be removed. When using this class in multiple inheritance, this class should not be inherited publicly and should be mentioned as last. This should ensure that deteletion of the object will go well when initiated from a pointer to the first ancestor. */ class relation_manager::auxiliary_table_filter_fn { table_fact m_row; svector m_to_remove; public: virtual ~auxiliary_table_filter_fn() {} virtual bool should_remove(const table_fact & f) const = 0; void operator()(table_base & r) { m_to_remove.reset(); unsigned sz = 0; for (table_base::row_interface& a : r) { a.get_fact(m_row); if (should_remove(m_row)) { m_to_remove.append(m_row.size(), m_row.c_ptr()); ++sz; } } r.remove_facts(sz, m_to_remove.c_ptr()); } }; class relation_manager::default_table_filter_identical_fn : public table_mutator_fn, auxiliary_table_filter_fn { const unsigned m_col_cnt; const unsigned_vector m_identical_cols; public: default_table_filter_identical_fn(unsigned col_cnt, const unsigned * identical_cols) : m_col_cnt(col_cnt), m_identical_cols(col_cnt, identical_cols) { SASSERT(col_cnt>=2); } bool should_remove(const table_fact & f) const override { table_element val=f[m_identical_cols[0]]; for(unsigned i=1; iget_arg(0); if (!m.is_eq(condition)) { return nullptr; } expr* x = to_app(condition)->get_arg(0); expr* y = to_app(condition)->get_arg(1); if (!is_var(x)) { std::swap(x, y); } if (!is_var(x)) { return nullptr; } dl_decl_util decl_util(m); uint64_t value = 0; if (!decl_util.is_numeral_ext(y, value)) { return nullptr; } return alloc(default_table_filter_not_equal_fn, ctx, to_var(x)->get_idx(), value); } }; class relation_manager::default_table_filter_interpreted_fn : public table_mutator_fn, auxiliary_table_filter_fn { ast_manager & m_ast_manager; var_subst & m_vs; dl_decl_util & m_decl_util; th_rewriter & m_simp; app_ref m_condition; expr_free_vars m_free_vars; expr_ref_vector m_args; public: default_table_filter_interpreted_fn(context & ctx, unsigned col_cnt, app* condition) : m_ast_manager(ctx.get_manager()), m_vs(ctx.get_var_subst()), m_decl_util(ctx.get_decl_util()), m_simp(ctx.get_rewriter()), m_condition(condition, ctx.get_manager()), m_args(ctx.get_manager()) { m_free_vars(m_condition); } bool should_remove(const table_fact & f) const override { expr_ref_vector& args = const_cast(m_args); args.reset(); //arguments need to be in reverse order for the substitution unsigned col_cnt = f.size(); for(int i=col_cnt-1;i>=0;i--) { if(!m_free_vars.contains(i)) { args.push_back(nullptr); continue; //this variable does not occur in the condition; } table_element el = f[i]; args.push_back(m_decl_util.mk_numeral(el, m_free_vars[i])); } expr_ref ground = m_vs(m_condition.get(), args.size(), args.c_ptr()); m_simp(ground); return m_ast_manager.is_false(ground); } void operator()(table_base & t) override { auxiliary_table_filter_fn::operator()(t); } }; table_mutator_fn * relation_manager::mk_filter_interpreted_fn(const table_base & t, app * condition) { context & ctx = get_context(); table_mutator_fn * res = t.get_plugin().mk_filter_interpreted_fn(t, condition); if (!res) { res = default_table_filter_not_equal_fn::mk(ctx, condition); } if(!res) { res = alloc(default_table_filter_interpreted_fn, ctx, t.get_signature().size(), condition); } return res; } class relation_manager::default_table_filter_interpreted_and_project_fn : public table_transformer_fn { scoped_ptr m_filter; scoped_ptr m_project; app_ref m_condition; unsigned_vector m_removed_cols; public: default_table_filter_interpreted_and_project_fn(context & ctx, table_mutator_fn * filter, app * condition, unsigned removed_col_cnt, const unsigned * removed_cols) : m_filter(filter), m_condition(condition, ctx.get_manager()), m_removed_cols(removed_col_cnt, removed_cols) {} table_base* operator()(const table_base & tb) override { scoped_rel t2 = tb.clone(); (*m_filter)(*t2); if (!m_project) { relation_manager & rmgr = t2->get_plugin().get_manager(); m_project = rmgr.mk_project_fn(*t2, m_removed_cols.size(), m_removed_cols.c_ptr()); if (!m_project) { throw default_exception("projection does not exist"); } } return (*m_project)(*t2); } }; table_transformer_fn * relation_manager::mk_filter_interpreted_and_project_fn(const table_base & t, app * condition, unsigned removed_col_cnt, const unsigned * removed_cols) { table_transformer_fn * res = t.get_plugin().mk_filter_interpreted_and_project_fn(t, condition, removed_col_cnt, removed_cols); if (res) return res; table_mutator_fn * filter = mk_filter_interpreted_fn(t, condition); SASSERT(filter); res = alloc(default_table_filter_interpreted_and_project_fn, get_context(), filter, condition, removed_col_cnt, removed_cols); return res; } table_intersection_filter_fn * relation_manager::mk_filter_by_intersection_fn(const table_base & t, const table_base & src, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * src_cols) { table_intersection_filter_fn * res = t.get_plugin().mk_filter_by_negation_fn(t, src, joined_col_cnt, t_cols, src_cols); if(!res && &t.get_plugin()!=&src.get_plugin()) { res = src.get_plugin().mk_filter_by_negation_fn(t, src, joined_col_cnt, t_cols, src_cols); } return res; } class relation_manager::default_table_negation_filter_fn : public convenient_table_negation_filter_fn, auxiliary_table_filter_fn { const table_base * m_negated_table; mutable table_fact m_aux_fact; public: default_table_negation_filter_fn(const table_base & tgt, const table_base & neg_t, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) : convenient_table_negation_filter_fn(tgt, neg_t, joined_col_cnt, t_cols, negated_cols), m_negated_table(nullptr) { m_aux_fact.resize(neg_t.get_signature().size()); } bool should_remove(const table_fact & f) const override { if(!m_all_neg_bound || m_overlap) { table_base::iterator nit = m_negated_table->begin(); table_base::iterator nend = m_negated_table->end(); for(; nit!=nend; ++nit) { const table_base::row_interface & nrow = *nit; if(bindings_match(nrow, f)) { return true; } } return false; } else { make_neg_bindings(m_aux_fact, f); return m_negated_table->contains_fact(m_aux_fact); } } void operator()(table_base & tgt, const table_base & negated_table) override { SASSERT(m_negated_table==0); flet flet_neg_table(m_negated_table, &negated_table); auxiliary_table_filter_fn::operator()(tgt); } }; table_intersection_filter_fn * relation_manager::mk_filter_by_negation_fn(const table_base & t, const table_base & negated_obj, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) { table_intersection_filter_fn * res = t.get_plugin().mk_filter_by_negation_fn(t, negated_obj, joined_col_cnt, t_cols, negated_cols); if(!res && &t.get_plugin()!=&negated_obj.get_plugin()) { res = negated_obj.get_plugin().mk_filter_by_negation_fn(t, negated_obj, joined_col_cnt, t_cols, negated_cols); } if(!res) { res = alloc(default_table_negation_filter_fn, t, negated_obj, joined_col_cnt, t_cols, negated_cols); } return res; } table_intersection_join_filter_fn* relation_manager::mk_filter_by_negated_join_fn( const table_base & t, const table_base & src1, const table_base & src2, unsigned_vector const& t_cols, unsigned_vector const& src_cols, unsigned_vector const& src1_cols, unsigned_vector const& src2_cols) { return t.get_plugin().mk_filter_by_negated_join_fn(t, src1, src2, t_cols, src_cols, src1_cols, src2_cols); } class relation_manager::default_table_select_equal_and_project_fn : public table_transformer_fn { scoped_ptr m_filter; scoped_ptr m_project; public: default_table_select_equal_and_project_fn(table_mutator_fn * filter, table_transformer_fn * project) : m_filter(filter), m_project(project) {} table_base * operator()(const table_base & t1) override { TRACE("dl", tout << t1.get_plugin().get_name() << "\n";); scoped_rel aux = t1.clone(); (*m_filter)(*aux); return (*m_project)(*aux); } }; table_transformer_fn * relation_manager::mk_select_equal_and_project_fn(const table_base & t, const table_element & value, unsigned col) { table_transformer_fn * res = t.get_plugin().mk_select_equal_and_project_fn(t, value, col); if(!res) { table_mutator_fn * selector = mk_filter_equal_fn(t, value, col); SASSERT(selector); table_transformer_fn * projector = mk_project_fn(t, 1, &col); SASSERT(projector); res = alloc(default_table_select_equal_and_project_fn, selector, projector); } return res; } class relation_manager::default_table_map_fn : public table_mutator_fn { scoped_ptr m_mapper; unsigned m_first_functional; scoped_rel m_aux_table; scoped_ptr m_union_fn; table_fact m_curr_fact; public: default_table_map_fn(const table_base & t, table_row_mutator_fn * mapper) : m_mapper(mapper), m_first_functional(t.get_signature().first_functional()) { SASSERT(t.get_signature().functional_columns()>0); table_plugin & plugin = t.get_plugin(); m_aux_table = plugin.mk_empty(t.get_signature()); m_union_fn = plugin.mk_union_fn(t, *m_aux_table, static_cast(nullptr)); } ~default_table_map_fn() override {} void operator()(table_base & t) override { SASSERT(t.get_signature()==m_aux_table->get_signature()); if(!m_aux_table->empty()) { m_aux_table->reset(); } for (table_base::row_interface& a : t) { a.get_fact(m_curr_fact); if((*m_mapper)(m_curr_fact.c_ptr()+m_first_functional)) { m_aux_table->add_fact(m_curr_fact); } } t.reset(); (*m_union_fn)(t, *m_aux_table, static_cast(nullptr)); } }; table_mutator_fn * relation_manager::mk_map_fn(const table_base & t, table_row_mutator_fn * mapper) { SASSERT(t.get_signature().functional_columns()>0); table_mutator_fn * res = t.get_plugin().mk_map_fn(t, mapper); if(!res) { res = alloc(default_table_map_fn, t, mapper); } return res; } class relation_manager::default_table_project_with_reduce_fn : public convenient_table_transformer_fn { unsigned_vector m_removed_cols; const unsigned m_inp_col_cnt; const unsigned m_removed_col_cnt; const unsigned m_result_col_cnt; scoped_ptr m_reducer; unsigned m_res_first_functional; table_fact m_row; table_fact m_former_row; public: default_table_project_with_reduce_fn(const table_signature & orig_sig, unsigned removed_col_cnt, const unsigned * removed_cols, table_row_pair_reduce_fn * reducer) : m_removed_cols(removed_col_cnt, removed_cols), m_inp_col_cnt(orig_sig.size()), m_removed_col_cnt(removed_col_cnt), m_result_col_cnt(orig_sig.size()-removed_col_cnt), m_reducer(reducer) { SASSERT(removed_col_cnt>0); table_signature::from_project_with_reduce(orig_sig, removed_col_cnt, removed_cols, get_result_signature()); m_res_first_functional = get_result_signature().first_functional(); m_row.resize(get_result_signature().size()); m_former_row.resize(get_result_signature().size()); } ~default_table_project_with_reduce_fn() override {} virtual void modify_fact(table_fact & f) const { unsigned ofs=1; unsigned r_i=1; for(unsigned i=m_removed_cols[0]+1; isuggest_fact(m_former_row)) { (*m_reducer)(m_former_row.c_ptr()+m_res_first_functional, m_row.c_ptr()+m_res_first_functional); res->ensure_fact(m_former_row); } } return res; } }; table_transformer_fn * relation_manager::mk_project_with_reduce_fn(const table_base & t, unsigned col_cnt, const unsigned * removed_cols, table_row_pair_reduce_fn * reducer) { SASSERT(t.get_signature().functional_columns()>0); table_transformer_fn * res = t.get_plugin().mk_project_with_reduce_fn(t, col_cnt, removed_cols, reducer); if(!res) { res = alloc(default_table_project_with_reduce_fn, t.get_signature(), col_cnt, removed_cols, reducer); } return res; } }; z3-z3-4.8.7/src/muz/rel/dl_relation_manager.h000066400000000000000000000733501356505360400207620ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_relation_manager.h Abstract: Author: Krystof Hoder (t-khoder) 2010-09-24. Revision History: --*/ #ifndef DL_RELATION_MANAGER_H_ #define DL_RELATION_MANAGER_H_ #include "util/map.h" #include "util/vector.h" #include "muz/rel/dl_base.h" namespace datalog { class context; class dl_decl_util; class table_relation; class table_relation_plugin; class finite_product_relation; class finite_product_relation_plugin; class sieve_relation; class sieve_relation_plugin; class rule_set; class relation_manager { class empty_signature_relation_join_fn; class default_relation_join_project_fn; class default_relation_select_equal_and_project_fn; class default_relation_intersection_filter_fn; class default_relation_filter_interpreted_and_project_fn; class default_relation_apply_sequential_fn; class auxiliary_table_transformer_fn; class auxiliary_table_filter_fn; class default_table_join_fn; class default_table_project_fn; class null_signature_table_project_fn; class default_table_join_project_fn; class default_table_rename_fn; class default_table_union_fn; class default_table_filter_equal_fn; class default_table_filter_identical_fn; class default_table_filter_interpreted_fn; class default_table_filter_interpreted_and_project_fn; class default_table_negation_filter_fn; class default_table_filter_not_equal_fn; class default_table_select_equal_and_project_fn; class default_table_map_fn; class default_table_project_with_reduce_fn; typedef obj_map decl2kind_map; typedef u_map kind2plugin_map; typedef map, ptr_eq > tp2trp_map; typedef map, ptr_eq > rp2fprp_map; typedef obj_map relation_map; typedef ptr_vector table_plugin_vector; typedef ptr_vector relation_plugin_vector; context & m_context; table_plugin_vector m_table_plugins; relation_plugin_vector m_relation_plugins; //table_relation_plugins corresponding to table_plugins tp2trp_map m_table_relation_plugins; rp2fprp_map m_finite_product_relation_plugins; kind2plugin_map m_kind2plugin; table_plugin * m_favourite_table_plugin; relation_plugin * m_favourite_relation_plugin; relation_map m_relations; decl_set m_saturated_rels; family_id m_next_table_fid; family_id m_next_relation_fid; /** Map specifying what kind of relation should be used to represent particular predicate. */ decl2kind_map m_pred_kinds; void register_relation_plugin_impl(relation_plugin * plugin); relation_manager(const relation_manager &); //private and undefined copy constructor relation_manager & operator=(const relation_manager &); //private and undefined operator= public: relation_manager(context & ctx) : m_context(ctx), m_favourite_table_plugin(nullptr), m_favourite_relation_plugin(nullptr), m_next_table_fid(0), m_next_relation_fid(0) {} virtual ~relation_manager(); void reset(); void reset_relations(); context & get_context() const { return m_context; } dl_decl_util & get_decl_util() const; family_id get_next_table_fid() { return m_next_table_fid++; } family_id get_next_relation_fid(relation_plugin & claimer); /** Set what kind of relation is going to be used to represent the predicate \c pred. This function can be called only before the relation object for \c pred is created (i.e. before the \c get_relation function is called with \c pred as argument for the first time). */ void set_predicate_kind(func_decl * pred, family_id kind); /** Return the relation kind that was requested to represent the predicate \c pred by \c set_predicate_kind. If there was no such request, return \c null_family_id. */ family_id get_requested_predicate_kind(func_decl * pred); relation_base & get_relation(func_decl * pred); relation_base * try_get_relation(func_decl * pred) const; /** \brief Store the relation \c rel under the predicate \c pred. The \c relation_manager takes over the relation object. */ void store_relation(func_decl * pred, relation_base * rel); bool is_saturated(func_decl * pred) const { return m_saturated_rels.contains(pred); } void mark_saturated(func_decl * pred) { m_saturated_rels.insert(pred); } void reset_saturated_marks() { if(!m_saturated_rels.empty()) { m_saturated_rels.reset(); } } void collect_non_empty_predicates(decl_set & res) const; void restrict_predicates(const decl_set & preds); void register_plugin(table_plugin * plugin); /** table_relation_plugins should not be passed to this function since they are created automatically when registering a table plugin. */ void register_plugin(relation_plugin * plugin) { SASSERT(!plugin->from_table()); register_relation_plugin_impl(plugin); } table_plugin & get_appropriate_plugin(const table_signature & t); relation_plugin & get_appropriate_plugin(const relation_signature & t); table_plugin * try_get_appropriate_plugin(const table_signature & t); relation_plugin * try_get_appropriate_plugin(const relation_signature & t); table_plugin * get_table_plugin(symbol const& s); relation_plugin * get_relation_plugin(symbol const& s); relation_plugin & get_relation_plugin(family_id kind); void set_favourite_plugin(relation_plugin* p) { m_favourite_relation_plugin = p; } table_relation_plugin & get_table_relation_plugin(table_plugin & tp); bool try_get_finite_product_relation_plugin(const relation_plugin & inner, finite_product_relation_plugin * & res); table_base * mk_empty_table(const table_signature & s); relation_base * mk_implicit_relation(const relation_signature & s, app * expr); relation_base * mk_empty_relation(const relation_signature & s, family_id kind); relation_base * mk_empty_relation(const relation_signature & s, func_decl* pred); relation_base * mk_full_relation(const relation_signature & s, func_decl* pred, family_id kind); relation_base * mk_full_relation(const relation_signature & s, func_decl* pred); relation_base * mk_table_relation(const relation_signature & s, table_base * table); bool mk_empty_table_relation(const relation_signature & s, relation_base * & result); bool is_non_explanation(relation_signature const& s) const; /** \brief Convert relation value to table one. This function can be called only for the relation sorts that have a table counterpart. */ void relation_to_table(const relation_sort & sort, const relation_element & from, table_element & to); void table_to_relation(const relation_sort & sort, const table_element & from, relation_element & to); void table_to_relation(const relation_sort & sort, const table_element & from, const relation_fact::el_proxy & to); void table_to_relation(const relation_sort & sort, const table_element & from, relation_element_ref & to); bool relation_sort_to_table(const relation_sort & from, table_sort & to); void from_predicate(func_decl * pred, unsigned arg_index, relation_sort & result); void from_predicate(func_decl * pred, relation_signature & result); /** \brief Convert relation signature to table signature and return true if successful. If false is returned, the value of \c to is undefined. */ bool relation_signature_to_table(const relation_signature & from, table_signature & to); void relation_fact_to_table(const relation_signature & s, const relation_fact & from, table_fact & to); void table_fact_to_relation(const relation_signature & s, const table_fact & from, relation_fact & to); // ----------------------------------- // // relation operations // // ----------------------------------- //TODO: If multiple operation implementations are available, we may want to do something to //select the best one here. /** If \c allow_product_relation is true, we will create a join that builds a product relation, if there is no other way to do the join. If \c allow_product_relation is false, we will return zero in that case. */ relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, bool allow_product_relation=true); relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, const unsigned_vector & cols1, const unsigned_vector & cols2, bool allow_product_relation=true) { SASSERT(cols1.size()==cols2.size()); return mk_join_fn(t1, t2, cols1.size(), cols1.c_ptr(), cols2.c_ptr(), allow_product_relation); } /** \brief Return functor that transforms a table into one that lacks columns listed in \c removed_cols array. The \c removed_cols contains columns of table \c t in strictly ascending order. */ relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, const unsigned * removed_cols); relation_transformer_fn * mk_project_fn(const relation_base & t, const unsigned_vector & removed_cols) { return mk_project_fn(t, removed_cols.size(), removed_cols.c_ptr()); } /** \brief Return an operation that is a composition of a join and a project operation. */ relation_join_fn * mk_join_project_fn(const relation_base & t1, const relation_base & t2, unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols, bool allow_product_relation_join=true); relation_join_fn * mk_join_project_fn(const relation_base & t1, const relation_base & t2, const unsigned_vector & cols1, const unsigned_vector & cols2, const unsigned_vector & removed_cols, bool allow_product_relation_join=true) { return mk_join_project_fn(t1, t2, cols1.size(), cols1.c_ptr(), cols2.c_ptr(), removed_cols.size(), removed_cols.c_ptr(), allow_product_relation_join); } relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, const unsigned * permutation_cycle); relation_transformer_fn * mk_rename_fn(const relation_base & t, const unsigned_vector & permutation_cycle) { return mk_rename_fn(t, permutation_cycle.size(), permutation_cycle.c_ptr()); } /** Like \c mk_rename_fn, only the permutation is not specified by cycle, but by a permutated array of column number. */ relation_transformer_fn * mk_permutation_rename_fn(const relation_base & t, const unsigned * permutation); relation_transformer_fn * mk_permutation_rename_fn(const relation_base & t, const unsigned_vector & permutation) { SASSERT(t.get_signature().size()==permutation.size()); return mk_permutation_rename_fn(t, permutation.c_ptr()); } /** The post-condition for an ideal union operation is be Union(tgt, src, delta): tgt_1==tgt_0 \union src delta_1== delta_0 \union ( tgt_1 \setminus tgt_0 ) A required post-condition is Union(tgt, src, delta): tgt_1==tgt_0 \union src tgt_1==tgt_0 => delta_1==delta_0 delta_0 \subset delta_1 delta_1 \subset (delta_0 \union tgt_1) ( tgt_1 \setminus tgt_0 ) \subset delta_1 So that a sufficient implementation is Union(tgt, src, delta) { oldTgt:=tgt.clone(); tgt:=tgt \union src if(tgt!=oldTgt) { delta:=delta \union src //also ?delta \union tgt? would work } } If rules are compiled with all_or_nothing_deltas parameter set to true, a sufficient post-condition is Union(tgt, src, delta): tgt_1==tgt_0 \union src (tgt_1==tgt_0 || delta_0 is non-empty) <=> delta_1 is non-empty */ relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta); relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src) { return mk_union_fn(tgt, src, static_cast(nullptr)); } /** Similar to union, but this one should be used inside loops to allow for abstract domain convergence. */ relation_union_fn * mk_widen_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta); relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, const unsigned * identical_cols); relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, const unsigned_vector & identical_cols) { return mk_filter_identical_fn(t, identical_cols.size(), identical_cols.c_ptr()); } relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, unsigned col); relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); relation_transformer_fn * mk_filter_interpreted_and_project_fn(const relation_base & t, app * condition, unsigned removed_col_cnt, const unsigned * removed_cols); relation_mutator_fn * mk_apply_sequential_fn(unsigned n, relation_mutator_fn* * mutators); /** \brief Operations that returns all rows of \c t for which is column \c col equal to \c value with the column \c col removed. This operation can often be efficiently implemented and is useful for evaluating rules of the form F(x):-P("c",x). */ relation_transformer_fn * mk_select_equal_and_project_fn(const relation_base & t, const relation_element & value, unsigned col); relation_intersection_filter_fn * mk_filter_by_intersection_fn(const relation_base & tgt, const relation_base & src, unsigned joined_col_cnt, const unsigned * tgt_cols, const unsigned * src_cols); relation_intersection_filter_fn * mk_filter_by_intersection_fn(const relation_base & tgt, const relation_base & src, const unsigned_vector & tgt_cols, const unsigned_vector & src_cols) { SASSERT(tgt_cols.size()==src_cols.size()); return mk_filter_by_intersection_fn(tgt, src, tgt_cols.size(), tgt_cols.c_ptr(), src_cols.c_ptr()); } relation_intersection_filter_fn * mk_filter_by_intersection_fn(const relation_base & tgt, const relation_base & src); /** The filter_by_negation postcondition: filter_by_negation(tgt, neg, columns in tgt: c1,...,cN, corresponding columns in neg: d1,...,dN): tgt_1:={x: x\in tgt_0 && ! \exists y: ( y \in neg & pi_c1(x)= pi_d1(y) & ... & pi_cN(x)= pi_dN(y) ) } */ relation_intersection_filter_fn * mk_filter_by_negation_fn(const relation_base & t, const relation_base & negated_obj, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols); relation_intersection_filter_fn * mk_filter_by_negation_fn(const relation_base & t, const relation_base & negated_obj, const unsigned_vector & t_cols, const unsigned_vector & negated_cols) { SASSERT(t_cols.size()==negated_cols.size()); return mk_filter_by_negation_fn(t, negated_obj, t_cols.size(), t_cols.c_ptr(), negated_cols.c_ptr()); } // ----------------------------------- // // table operations // // ----------------------------------- table_join_fn * mk_join_fn(const table_base & t1, const table_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); table_join_fn * mk_join_fn(const table_base & t1, const table_base & t2, const unsigned_vector & cols1, const unsigned_vector & cols2) { SASSERT(cols1.size()==cols2.size()); return mk_join_fn(t1, t2, cols1.size(), cols1.c_ptr(), cols2.c_ptr()); } /** \brief Return functor that transforms a table into one that lacks columns listed in \c removed_cols array. The \c removed_cols contains columns of table \c t in strictly ascending order. If a project operation removes a non-functional column, all functional columns become non-functional (so that none of the values in functional columns are lost) */ table_transformer_fn * mk_project_fn(const table_base & t, unsigned col_cnt, const unsigned * removed_cols); table_transformer_fn * mk_project_fn(const table_base & t, const unsigned_vector & removed_cols) { return mk_project_fn(t, removed_cols.size(), removed_cols.c_ptr()); } /** \brief Return an operation that is a composition of a join and a project operation. This operation is equivalent to the two operations performed separately, unless functional columns are involved. The ordinary project would make all of the functional columns into non-functional if any non-functional column was removed. In function, however, we group columns into equivalence classes (according to the equalities in \c cols1 and \c cols2) and make everything non-functional only if some equivalence class of non-functional columns would have no non-functional columns remain after the removal. This behavior is implemented in the \c table_signature::from_join_project function. */ table_join_fn * mk_join_project_fn(const table_base & t1, const table_base & t2, unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols); table_join_fn * mk_join_project_fn(const table_base & t1, const table_base & t2, const unsigned_vector & cols1, const unsigned_vector & cols2, const unsigned_vector & removed_cols) { return mk_join_project_fn(t1, t2, cols1.size(), cols1.c_ptr(), cols2.c_ptr(), removed_cols.size(), removed_cols.c_ptr()); } table_transformer_fn * mk_rename_fn(const table_base & t, unsigned permutation_cycle_len, const unsigned * permutation_cycle); table_transformer_fn * mk_rename_fn(const table_base & t, const unsigned_vector & permutation_cycle) { return mk_rename_fn(t, permutation_cycle.size(), permutation_cycle.c_ptr()); } /** Like \c mk_rename_fn, only the permutation is not specified by cycle, but by a permutated array of column number. */ table_transformer_fn * mk_permutation_rename_fn(const table_base & t, const unsigned * permutation); table_transformer_fn * mk_permutation_rename_fn(const table_base & t, const unsigned_vector & permutation) { SASSERT(t.get_signature().size()==permutation.size()); return mk_permutation_rename_fn(t, permutation.c_ptr()); } /** The post-condition for an ideal union operation is be Union(tgt, src, delta): tgt_1==tgt_0 \union src delta_1== delta_0 \union ( tgt_1 \setminus tgt_0 ) A required post-condition is Union(tgt, src, delta): tgt_1==tgt_0 \union src tgt_1==tgt_0 => delta_1==delta_0 delta_0 \subset delta_1 delta_1 \subset (delta_0 \union tgt_1) ( tgt_1 \setminus tgt_0 ) \subset delta_1 So that a sufficient implementation is Union(tgt, src, delta) { oldTgt:=tgt.clone(); tgt:=tgt \union src if(tgt!=oldTgt) { delta:=delta \union src //also ?delta \union tgt? would work } } If rules are compiled with all_or_nothing_deltas parameter set to true, a sufficient post-condition is Union(tgt, src, delta): tgt_1==tgt_0 \union src (tgt_1==tgt_0 || delta_0 is non-empty) <=> delta_1 is non-empty */ table_union_fn * mk_union_fn(const table_base & tgt, const table_base & src, const table_base * delta); table_union_fn * mk_union_fn(const table_base & tgt, const table_base & src) { return mk_union_fn(tgt, src, static_cast(nullptr)); } /** Similar to union, but this one should be used inside loops to allow for abstract domain convergence. */ table_union_fn * mk_widen_fn(const table_base & tgt, const table_base & src, const table_base * delta); table_mutator_fn * mk_filter_identical_fn(const table_base & t, unsigned col_cnt, const unsigned * identical_cols); table_mutator_fn * mk_filter_identical_fn(const table_base & t, const unsigned_vector & identical_cols) { return mk_filter_identical_fn(t, identical_cols.size(), identical_cols.c_ptr()); } table_mutator_fn * mk_filter_equal_fn(const table_base & t, const table_element & value, unsigned col); table_mutator_fn * mk_filter_interpreted_fn(const table_base & t, app * condition); table_transformer_fn * mk_filter_interpreted_and_project_fn(const table_base & t, app * condition, unsigned removed_col_cnt, const unsigned * removed_cols); /** \brief Operations that returns all rows of \c t for which is column \c col equal to \c value with the column \c col removed. This operation can often be efficiently implemented and is useful for evaluating rules of the form F(x):-P("c",x). */ table_transformer_fn * mk_select_equal_and_project_fn(const table_base & t, const table_element & value, unsigned col); table_intersection_filter_fn * mk_filter_by_intersection_fn(const table_base & t, const table_base & src, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * src_cols); table_intersection_filter_fn * mk_filter_by_intersection_fn(const table_base & t, const table_base & src, const unsigned_vector & t_cols, const unsigned_vector & src_cols) { SASSERT(t_cols.size()==src_cols.size()); return mk_filter_by_intersection_fn(t, src, t_cols.size(), t_cols.c_ptr(), src_cols.c_ptr()); } /** The filter_by_negation postcondition: filter_by_negation(tgt, neg, columns in tgt: c1,...,cN, corresponding columns in neg: d1,...,dN): tgt_1:={x: x\in tgt_0 && ! \exists y: ( y \in neg & pi_c1(x)= pi_d1(y) & ... & pi_cN(x)= pi_dN(y) ) } */ table_intersection_filter_fn * mk_filter_by_negation_fn(const table_base & t, const table_base & negated_obj, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols); table_intersection_filter_fn * mk_filter_by_negation_fn(const table_base & t, const table_base & negated_obj, const unsigned_vector & t_cols, const unsigned_vector & negated_cols) { SASSERT(t_cols.size()==negated_cols.size()); return mk_filter_by_negation_fn(t, negated_obj, t_cols.size(), t_cols.c_ptr(), negated_cols.c_ptr()); } /** combined filter by negation with a join. */ table_intersection_join_filter_fn* mk_filter_by_negated_join_fn( const table_base & t, const table_base & src1, const table_base & src2, unsigned_vector const& t_cols, unsigned_vector const& src_cols, unsigned_vector const& src1_cols, unsigned_vector const& src2_cols); /** \c t must contain at least one functional column. Created object takes ownership of the \c mapper object. */ virtual table_mutator_fn * mk_map_fn(const table_base & t, table_row_mutator_fn * mapper); /** \c t must contain at least one functional column. Created object takes ownership of the \c mapper object. */ virtual table_transformer_fn * mk_project_with_reduce_fn(const table_base & t, unsigned col_cnt, const unsigned * removed_cols, table_row_pair_reduce_fn * reducer); // ----------------------------------- // // output functions // // ----------------------------------- std::string to_nice_string(const relation_element & el) const; /** This one may give a nicer representation of \c el than the \c to_nice_string(const relation_element & el) function, by using the information about the sort of the element. */ std::string to_nice_string(const relation_sort & s, const relation_element & el) const; std::string to_nice_string(const relation_sort & s) const; std::string to_nice_string(const relation_signature & s) const; void display(std::ostream & out) const; void display_relation_sizes(std::ostream & out) const; void display_output_tables(rule_set const& rules, std::ostream & out) const; private: relation_intersection_filter_fn * try_mk_default_filter_by_intersection_fn(const relation_base & t, const relation_base & src, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * src_cols); }; /** This is a helper class for relation_plugins whose relations can be of various kinds. */ template > class rel_spec_store { typedef relation_signature::hash r_hash; typedef relation_signature::eq r_eq; typedef map family_id_idx_store; typedef map sig2store; typedef u_map family_id2spec; typedef map sig2spec_store; relation_plugin & m_parent; svector m_allocated_kinds; sig2store m_kind_assignment; sig2spec_store m_kind_specs; relation_manager & get_manager() { return m_parent.get_manager(); } void add_new_kind() { add_available_kind(get_manager().get_next_relation_fid(m_parent)); } public: rel_spec_store(relation_plugin & parent) : m_parent(parent) {} ~rel_spec_store() { reset_dealloc_values(m_kind_assignment); reset_dealloc_values(m_kind_specs); } void add_available_kind(family_id k) { m_allocated_kinds.push_back(k); } bool contains_signature(relation_signature const& sig) const { return m_kind_assignment.contains(sig); } family_id get_relation_kind(const relation_signature & sig, const Spec & spec) { typename sig2store::entry * e = m_kind_assignment.find_core(sig); if(!e) { e = m_kind_assignment.insert_if_not_there2(sig, alloc(family_id_idx_store)); m_kind_specs.insert(sig, alloc(family_id2spec)); } family_id_idx_store & ids = *e->get_data().m_value; unsigned res_idx; if(!ids.find(spec, res_idx)) { res_idx = ids.size(); if(res_idx==m_allocated_kinds.size()) { add_new_kind(); } SASSERT(res_idxinsert(m_allocated_kinds[res_idx], spec); } return m_allocated_kinds[res_idx]; } void get_relation_spec(const relation_signature & sig, family_id kind, Spec & spec) { family_id2spec * idspecs = m_kind_specs.find(sig); spec = idspecs->find(kind); } }; }; #endif /* DL_RELATION_MANAGER_H_ */ z3-z3-4.8.7/src/muz/rel/dl_sieve_relation.cpp000066400000000000000000000671171356505360400210220ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_mk_explanations.cpp Abstract: Author: Krystof Hoder (t-khoder) 2010-11-08. Revision History: --*/ #include #include "ast/ast_pp.h" #include "muz/rel/dl_sieve_relation.h" namespace datalog { // ----------------------------------- // // sieve_relation // // ----------------------------------- sieve_relation::sieve_relation(sieve_relation_plugin & p, const relation_signature & s, const bool * inner_columns, relation_base * inner) : relation_base(p, s), m_inner_cols(s.size(), inner_columns), m_inner(inner) { unsigned n = s.size(); for(unsigned i=0; i 0; ) { --i; unsigned idx = m_inner2sig[i]; s.push_back(m.mk_var(idx, sig[i])); } get_inner().to_formula(tmp); fml = get_plugin().get_context().get_var_subst()(tmp, sz, s.c_ptr()); } void sieve_relation::display(std::ostream & out) const { out << "Sieve relation "; print_container(m_inner_cols, out); out <<"\n"; get_inner().display(out); } // ----------------------------------- // // sieve_relation_plugin // // ----------------------------------- sieve_relation_plugin & sieve_relation_plugin::get_plugin(relation_manager & rmgr) { sieve_relation_plugin * res = static_cast(rmgr.get_relation_plugin(get_name())); if(!res) { res = alloc(sieve_relation_plugin, rmgr); rmgr.register_plugin(res); } return *res; } sieve_relation& sieve_relation_plugin::get(relation_base& r) { return dynamic_cast(r); } sieve_relation const & sieve_relation_plugin::get(relation_base const& r) { return dynamic_cast(r); } sieve_relation* sieve_relation_plugin::get(relation_base* r) { return dynamic_cast(r); } sieve_relation const* sieve_relation_plugin::get(relation_base const* r) { return dynamic_cast(r); } sieve_relation_plugin::sieve_relation_plugin(relation_manager & manager) : relation_plugin(get_name(), manager, ST_SIEVE_RELATION), m_spec_store(*this) {} void sieve_relation_plugin::initialize(family_id fid) { relation_plugin::initialize(fid); m_spec_store.add_available_kind(get_kind()); } family_id sieve_relation_plugin::get_relation_kind(const relation_signature & sig, const bool * inner_columns, family_id inner_kind) { rel_spec spec(sig.size(), inner_columns, inner_kind); return m_spec_store.get_relation_kind(sig, spec); } family_id sieve_relation_plugin::get_relation_kind(sieve_relation & r, const bool * inner_columns) { const relation_signature & sig = r.get_signature(); return get_relation_kind(sig, inner_columns, r.get_inner().get_kind()); } void sieve_relation_plugin::extract_inner_columns(const relation_signature & s, relation_plugin & inner, svector & inner_columns) { SASSERT(inner_columns.size()==s.size()); unsigned n = s.size(); relation_signature inner_sig_singleton; for(unsigned i=0; i & inner_columns, relation_signature & inner_sig) { SASSERT(inner_columns.size()==s.size()); inner_sig.reset(); unsigned n = s.size(); for(unsigned i=0; i inner_cols(s.size()); extract_inner_columns(s, inner_cols.c_ptr()); collect_inner_signature(s, inner_cols, inner_sig); #endif } bool sieve_relation_plugin::can_handle_signature(const relation_signature & s) { //we do not want this plugin to handle anything by default return false; #if 0 relation_signature inner_sig; extract_inner_signature(s, inner_sig); SASSERT(inner_sig.size()<=s.size()); return !inner_sig.empty() && inner_sig.size()!=s.size(); #endif } sieve_relation * sieve_relation_plugin::mk_from_inner(const relation_signature & s, const bool * inner_columns, relation_base * inner_rel) { SASSERT(!inner_rel->get_plugin().is_sieve_relation()); //it does not make sense to make a sieve of a sieve return alloc(sieve_relation, *this, s, inner_columns, inner_rel); } sieve_relation * sieve_relation_plugin::mk_empty(const sieve_relation & original) { return static_cast(mk_empty(original.get_signature(), original.get_kind())); } relation_base * sieve_relation_plugin::mk_empty(const relation_base & original) { return mk_empty(static_cast(original)); } relation_base * sieve_relation_plugin::mk_empty(const relation_signature & s, family_id kind) { rel_spec spec; m_spec_store.get_relation_spec(s, kind, spec); relation_signature inner_sig; collect_inner_signature(s, spec.m_inner_cols, inner_sig); relation_base * inner = get_manager().mk_empty_relation(inner_sig, spec.m_inner_kind); return mk_from_inner(s, spec.m_inner_cols.c_ptr(), inner); } relation_base * sieve_relation_plugin::mk_empty(const relation_signature & s) { UNREACHABLE(); return nullptr; #if 0 svector inner_cols(s.size()); extract_inner_columns(s, inner_cols.c_ptr()); return mk_empty(s, inner_cols.c_ptr()); #endif } sieve_relation * sieve_relation_plugin::mk_empty(const relation_signature & s, relation_plugin & inner_plugin) { SASSERT(!inner_plugin.is_sieve_relation()); //it does not make sense to make a sieve of a sieve svector inner_cols(s.size()); extract_inner_columns(s, inner_plugin, inner_cols); relation_signature inner_sig; collect_inner_signature(s, inner_cols, inner_sig); relation_base * inner_rel = inner_plugin.mk_empty(inner_sig); return mk_from_inner(s, inner_cols, inner_rel); } relation_base * sieve_relation_plugin::mk_full(func_decl* p, const relation_signature & s) { relation_signature empty_sig; relation_plugin& plugin = get_manager().get_appropriate_plugin(s); relation_base * inner = plugin.mk_full(p, empty_sig, null_family_id); svector inner_cols; inner_cols.resize(s.size(), false); return mk_from_inner(s, inner_cols, inner); } sieve_relation * sieve_relation_plugin::full(func_decl* p, const relation_signature & s, relation_plugin & inner_plugin) { SASSERT(!inner_plugin.is_sieve_relation()); //it does not make sense to make a sieve of a sieve svector inner_cols(s.size()); extract_inner_columns(s, inner_plugin, inner_cols); relation_signature inner_sig; collect_inner_signature(s, inner_cols, inner_sig); relation_base * inner_rel = inner_plugin.mk_full(p, inner_sig, null_family_id); return mk_from_inner(s, inner_cols, inner_rel); } class sieve_relation_plugin::join_fn : public convenient_relation_join_fn { sieve_relation_plugin & m_plugin; unsigned_vector m_inner_cols_1; unsigned_vector m_inner_cols_2; svector m_result_inner_cols; scoped_ptr m_inner_join_fun; public: join_fn(sieve_relation_plugin & p, const relation_base & r1, const relation_base & r2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, relation_join_fn * inner_join_fun) : convenient_relation_join_fn(r1.get_signature(), r2.get_signature(), col_cnt, cols1, cols2), m_plugin(p), m_inner_join_fun(inner_join_fun) { bool r1_sieved = r1.get_plugin().is_sieve_relation(); bool r2_sieved = r2.get_plugin().is_sieve_relation(); const sieve_relation * sr1 = r1_sieved ? static_cast(&r1) : nullptr; const sieve_relation * sr2 = r2_sieved ? static_cast(&r2) : nullptr; if(r1_sieved) { m_result_inner_cols.append(sr1->m_inner_cols); } else { m_result_inner_cols.resize(r1.get_signature().size(), true); } if(r2_sieved) { m_result_inner_cols.append(sr2->m_inner_cols); } else { m_result_inner_cols.resize(m_result_inner_cols.size() + r2.get_signature().size(), true); } } relation_base * operator()(const relation_base & r1, const relation_base & r2) override { bool r1_sieved = r1.get_plugin().is_sieve_relation(); bool r2_sieved = r2.get_plugin().is_sieve_relation(); SASSERT(r1_sieved || r2_sieved); const sieve_relation * sr1 = r1_sieved ? static_cast(&r1) : nullptr; const sieve_relation * sr2 = r2_sieved ? static_cast(&r2) : nullptr; const relation_base & inner1 = r1_sieved ? sr1->get_inner() : r1; const relation_base & inner2 = r2_sieved ? sr2->get_inner() : r2; relation_base * inner_res = (*m_inner_join_fun)(inner1, inner2); return m_plugin.mk_from_inner(get_result_signature(), m_result_inner_cols.c_ptr(), inner_res); } }; relation_join_fn * sieve_relation_plugin::mk_join_fn(const relation_base & r1, const relation_base & r2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { if( &r1.get_plugin()!=this && &r2.get_plugin()!=this ) { //we create just operations that involve the current plugin return nullptr; } bool r1_sieved = r1.get_plugin().is_sieve_relation(); bool r2_sieved = r2.get_plugin().is_sieve_relation(); const sieve_relation * sr1 = r1_sieved ? static_cast(&r1) : nullptr; const sieve_relation * sr2 = r2_sieved ? static_cast(&r2) : nullptr; const relation_base & inner1 = r1_sieved ? sr1->get_inner() : r1; const relation_base & inner2 = r2_sieved ? sr2->get_inner() : r2; unsigned_vector inner_cols1; unsigned_vector inner_cols2; for(unsigned i=0; iis_inner_col(cols1[i])) { continue; } if(r2_sieved && !sr2->is_inner_col(cols2[i])) { continue; } inner_cols1.push_back( r1_sieved ? sr1->get_inner_col(cols1[i]) : cols1[i] ); inner_cols2.push_back( r2_sieved ? sr2->get_inner_col(cols2[i]) : cols2[i] ); } relation_join_fn * inner_join_fun = get_manager().mk_join_fn(inner1, inner2, inner_cols1, inner_cols2, false); if(!inner_join_fun) { return nullptr; } return alloc(join_fn, *this, r1, r2, col_cnt, cols1, cols2, inner_join_fun); } class sieve_relation_plugin::transformer_fn : public convenient_relation_transformer_fn { svector m_result_inner_cols; scoped_ptr m_inner_fun; public: transformer_fn(relation_transformer_fn * inner_fun, const relation_signature & result_sig, const bool * result_inner_cols) : m_result_inner_cols(result_sig.size(), result_inner_cols), m_inner_fun(inner_fun) { get_result_signature() = result_sig; } relation_base * operator()(const relation_base & r0) override { SASSERT(r0.get_plugin().is_sieve_relation()); const sieve_relation & r = static_cast(r0); sieve_relation_plugin & plugin = r.get_plugin(); relation_base * inner_res = (*m_inner_fun)(r.get_inner()); return plugin.mk_from_inner(get_result_signature(), m_result_inner_cols.c_ptr(), inner_res); } }; relation_transformer_fn * sieve_relation_plugin::mk_project_fn(const relation_base & r0, unsigned col_cnt, const unsigned * removed_cols) { if(&r0.get_plugin()!=this) { return nullptr; } const sieve_relation & r = static_cast(r0); unsigned_vector inner_removed_cols; for(unsigned i=0; i result_inner_cols = r.m_inner_cols; project_out_vector_columns(result_inner_cols, col_cnt, removed_cols); relation_signature result_sig; relation_signature::from_project(r.get_signature(), col_cnt, removed_cols, result_sig); relation_transformer_fn * inner_fun; if(inner_removed_cols.empty()) { inner_fun = alloc(identity_relation_transformer_fn); } else { inner_fun = get_manager().mk_project_fn(r.get_inner(), inner_removed_cols); } if(!inner_fun) { return nullptr; } return alloc(transformer_fn, inner_fun, result_sig, result_inner_cols.c_ptr()); } relation_transformer_fn * sieve_relation_plugin::mk_rename_fn(const relation_base & r0, unsigned cycle_len, const unsigned * permutation_cycle) { if(&r0.get_plugin()!=this) { return nullptr; } const sieve_relation & r = static_cast(r0); unsigned sig_sz = r.get_signature().size(); unsigned_vector permutation; add_sequence(0, sig_sz, permutation); permutate_by_cycle(permutation, cycle_len, permutation_cycle); bool inner_identity; unsigned_vector inner_permutation; collect_sub_permutation(permutation, r.m_sig2inner, inner_permutation, inner_identity); svector result_inner_cols = r.m_inner_cols; permutate_by_cycle(result_inner_cols, cycle_len, permutation_cycle); relation_signature result_sig; relation_signature::from_rename(r.get_signature(), cycle_len, permutation_cycle, result_sig); relation_transformer_fn * inner_fun = get_manager().mk_permutation_rename_fn(r.get_inner(), inner_permutation); if(!inner_fun) { return nullptr; } return alloc(transformer_fn, inner_fun, result_sig, result_inner_cols.c_ptr()); } class sieve_relation_plugin::union_fn : public relation_union_fn { scoped_ptr m_union_fun; public: union_fn(relation_union_fn * union_fun) : m_union_fun(union_fun) {} void operator()(relation_base & tgt, const relation_base & src, relation_base * delta) override { bool tgt_sieved = tgt.get_plugin().is_sieve_relation(); bool src_sieved = src.get_plugin().is_sieve_relation(); bool delta_sieved = delta && delta->get_plugin().is_sieve_relation(); sieve_relation * stgt = tgt_sieved ? static_cast(&tgt) : nullptr; const sieve_relation * ssrc = src_sieved ? static_cast(&src) : nullptr; sieve_relation * sdelta = delta_sieved ? static_cast(delta) : nullptr; relation_base & itgt = tgt_sieved ? stgt->get_inner() : tgt; const relation_base & isrc = src_sieved ? ssrc->get_inner() : src; relation_base * idelta = delta_sieved ? &sdelta->get_inner() : delta; (*m_union_fun)(itgt, isrc, idelta); } }; relation_union_fn * sieve_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta) { if(&tgt.get_plugin()!=this && &src.get_plugin()!=this && (delta && &delta->get_plugin()!=this)) { //we create the operation only if it involves this plugin return nullptr; } bool tgt_sieved = tgt.get_plugin().is_sieve_relation(); bool src_sieved = src.get_plugin().is_sieve_relation(); bool delta_sieved = delta && delta->get_plugin().is_sieve_relation(); const sieve_relation * stgt = tgt_sieved ? static_cast(&tgt) : nullptr; const sieve_relation * ssrc = src_sieved ? static_cast(&src) : nullptr; const sieve_relation * sdelta = delta_sieved ? static_cast(delta) : nullptr; const relation_base & itgt = tgt_sieved ? stgt->get_inner() : tgt; const relation_base & isrc = src_sieved ? ssrc->get_inner() : src; const relation_base * idelta = delta_sieved ? &sdelta->get_inner() : delta; //Now we require that the sieved and inner columns must match on all relations. //We may want to allow for some cases of misalignment even though it could introcude imprecision if( tgt_sieved && src_sieved && (!delta || delta_sieved) ) { if( !vectors_equal(stgt->m_inner_cols, ssrc->m_inner_cols) || (delta && !vectors_equal(stgt->m_inner_cols, sdelta->m_inner_cols)) ) { return nullptr; } } else { if( (stgt && !stgt->no_sieved_columns()) || (ssrc && !ssrc->no_sieved_columns()) || (sdelta && !sdelta->no_sieved_columns()) ) { //We have an unsieved relation and then some relation with some sieved columns, //which means there is an misalignment. return nullptr; } } relation_union_fn * union_fun = get_manager().mk_union_fn(itgt, isrc, idelta); if(!union_fun) { return nullptr; } return alloc(union_fn, union_fun); } class sieve_relation_plugin::filter_fn : public relation_mutator_fn { scoped_ptr m_inner_fun; public: filter_fn(relation_mutator_fn * inner_fun) : m_inner_fun(inner_fun) {} void operator()(relation_base & r0) override { SASSERT(r0.get_plugin().is_sieve_relation()); sieve_relation & r = static_cast(r0); (*m_inner_fun)(r.get_inner()); } }; relation_mutator_fn * sieve_relation_plugin::mk_filter_identical_fn(const relation_base & r0, unsigned col_cnt, const unsigned * identical_cols) { if(&r0.get_plugin()!=this) { return nullptr; } const sieve_relation & r = static_cast(r0); unsigned_vector inner_icols; //we ignore the columns which do not belong to the inner relation (which introduces imprecision) for(unsigned i=0; i(r0); if(!r.is_inner_col(col)) { //if the column which do not belong to the inner relation, we do nothing (which introduces imprecision) return alloc(identity_relation_mutator_fn); } unsigned inner_col = r.get_inner_col(col); relation_mutator_fn * inner_fun = get_manager().mk_filter_equal_fn(r.get_inner(), value, inner_col); if(!inner_fun) { return nullptr; } return alloc(filter_fn, inner_fun); } relation_mutator_fn * sieve_relation_plugin::mk_filter_interpreted_fn(const relation_base & rb, app * condition) { if(&rb.get_plugin()!=this) { return nullptr; } ast_manager & m = get_ast_manager(); const sieve_relation & r = static_cast(rb); const relation_signature sig = r.get_signature(); unsigned sz = sig.size(); var_idx_set& cond_vars = get_context().get_rule_manager().collect_vars(condition); expr_ref_vector subst_vect(m); subst_vect.resize(sz); unsigned subst_ofs = sz-1; for(unsigned i=0; i m_inner_fun; public: negation_filter_fn(relation_intersection_filter_fn * inner_fun) : m_inner_fun(inner_fun) {} void operator()(relation_base & r, const relation_base & neg) override { bool r_sieved = r.get_plugin().is_sieve_relation(); bool neg_sieved = neg.get_plugin().is_sieve_relation(); SASSERT(r_sieved || neg_sieved); sieve_relation * sr = r_sieved ? static_cast(&r) : nullptr; const sieve_relation * sneg = neg_sieved ? static_cast(&neg) : nullptr; relation_base & inner_r = r_sieved ? sr->get_inner() : r; const relation_base & inner_neg = neg_sieved ? sneg->get_inner() : neg; (*m_inner_fun)(inner_r, inner_neg); } }; relation_intersection_filter_fn * sieve_relation_plugin::mk_filter_by_negation_fn(const relation_base & r, const relation_base & neg, unsigned col_cnt, const unsigned * r_cols, const unsigned * neg_cols) { if(&r.get_plugin()!=this && &neg.get_plugin()!=this) { //we create just operations that involve the current plugin return nullptr; } bool r_sieved = r.get_plugin().is_sieve_relation(); bool neg_sieved = neg.get_plugin().is_sieve_relation(); SASSERT(r_sieved || neg_sieved); const sieve_relation * sr = r_sieved ? static_cast(&r) : nullptr; const sieve_relation * sneg = neg_sieved ? static_cast(&neg) : nullptr; const relation_base & inner_r = r_sieved ? sr->get_inner() : r; const relation_base & inner_neg = neg_sieved ? sneg->get_inner() : neg; unsigned_vector ir_cols; unsigned_vector ineg_cols; for(unsigned i=0; iis_inner_col(r_cols[i]); bool neg_col_inner = neg_sieved && !sneg->is_inner_col(neg_cols[i]); if(r_col_inner && neg_col_inner) { ir_cols.push_back( r_sieved ? sr->get_inner_col(i) : i ); ineg_cols.push_back( neg_sieved ? sneg->get_inner_col(i) : i ); } else if(!r_col_inner && neg_col_inner) { //Sieved (i.e. full) column in r is matched on an inner column in neg. //If we assume the column in neg is not full, no rows from the inner relation of //r would be removed. So in this case we perform no operation at cost of a little //impresicion. return alloc(identity_relation_intersection_filter_fn); } else { //Inner or sieved column in r must match a sieved column in neg. //Since sieved columns are full, this is always true so we can skip the equality. continue; } } relation_intersection_filter_fn * inner_fun = get_manager().mk_filter_by_negation_fn(inner_r, inner_neg, ir_cols, ineg_cols); if(!inner_fun) { return nullptr; } return alloc(negation_filter_fn, inner_fun); } }; z3-z3-4.8.7/src/muz/rel/dl_sieve_relation.h000066400000000000000000000173121356505360400204570ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_sieve_relation.h Abstract: Author: Krystof Hoder (t-khoder) 2010-11-11. Revision History: --*/ #ifndef DL_SIEVE_RELATION_H_ #define DL_SIEVE_RELATION_H_ #include "muz/base/dl_context.h" #include "muz/rel/dl_relation_manager.h" namespace datalog { class sieve_relation; class sieve_relation_plugin : public relation_plugin { friend class sieve_relation; public: struct rel_spec { svector m_inner_cols; family_id m_inner_kind; /** Create uninitialized rel_spec. */ rel_spec() {} /** \c inner_kind==null_family_id means we will not specify a relation kind when requesting the relation object from the relation_manager. \c inner_kind==null_family_id cannot hold in a specification of existing relation object. */ rel_spec(unsigned sig_sz, const bool * inner_cols, family_id inner_kind=null_family_id) : m_inner_cols(sig_sz, inner_cols), m_inner_kind(inner_kind) {} bool operator==(const rel_spec & o) const { return m_inner_kind==o.m_inner_kind && vectors_equal(m_inner_cols, o.m_inner_cols); } struct hash { unsigned operator()(const rel_spec & s) const { return svector_hash()(s.m_inner_cols)^s.m_inner_kind; } }; }; private: class join_fn; class transformer_fn; class union_fn; class filter_fn; class negation_filter_fn; rel_spec_store > m_spec_store; family_id get_relation_kind(sieve_relation & r, const bool * inner_columns); void extract_inner_columns(const relation_signature & s, relation_plugin & inner, svector & inner_columns); void extract_inner_signature(const relation_signature & s, relation_signature & inner_sig); void collect_inner_signature(const relation_signature & s, const svector & inner_columns, relation_signature & inner_sig); public: static symbol get_name() { return symbol("sieve_relation"); } static sieve_relation_plugin& get_plugin(relation_manager & rmgr); static sieve_relation& get(relation_base& r); static sieve_relation const & get(relation_base const& r); static sieve_relation* get(relation_base* r); static sieve_relation const* get(relation_base const* r); sieve_relation_plugin(relation_manager & manager); void initialize(family_id fid) override; family_id get_relation_kind(const relation_signature & sig, const bool * inner_columns, family_id inner_kind); family_id get_relation_kind(const relation_signature & sig, const svector & inner_columns, family_id inner_kind) { SASSERT(sig.size()==inner_columns.size()); return get_relation_kind(sig, inner_columns.c_ptr(), inner_kind); } bool can_handle_signature(const relation_signature & s) override; relation_base * mk_empty(const relation_signature & s) override; sieve_relation * mk_empty(const sieve_relation & original); relation_base * mk_empty(const relation_base & original) override; relation_base * mk_empty(const relation_signature & s, family_id kind) override; sieve_relation * mk_empty(const relation_signature & s, relation_plugin & inner_plugin); relation_base * mk_full(func_decl* p, const relation_signature & s) override; sieve_relation * full(func_decl* p, const relation_signature & s, relation_plugin & inner_plugin); sieve_relation * mk_from_inner(const relation_signature & s, const bool * inner_columns, relation_base * inner_rel); sieve_relation * mk_from_inner(const relation_signature & s, const svector & inner_columns, relation_base * inner_rel) { SASSERT(inner_columns.size()==s.size()); return mk_from_inner(s, inner_columns.c_ptr(), inner_rel); } protected: relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) override; relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, const unsigned * removed_cols) override; relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, const unsigned * permutation_cycle) override; relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta) override; relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, const unsigned * identical_cols) override; relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, unsigned col) override; relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition) override; relation_intersection_filter_fn * mk_filter_by_negation_fn(const relation_base & t, const relation_base & negated_obj, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) override; }; // ----------------------------------- // // sieve_relation // // ----------------------------------- class sieve_relation : public relation_base { friend class sieve_relation_plugin; friend class sieve_relation_plugin::join_fn; friend class sieve_relation_plugin::transformer_fn; friend class sieve_relation_plugin::union_fn; friend class sieve_relation_plugin::filter_fn; svector m_inner_cols; unsigned_vector m_sig2inner; unsigned_vector m_inner2sig; unsigned_vector m_ignored_cols; //in ascending order, so that it can be used in project-like functions scoped_rel m_inner; sieve_relation(sieve_relation_plugin & p, const relation_signature & s, const bool * inner_columns, relation_base * inner); public: sieve_relation_plugin & get_plugin() const { return static_cast(relation_base::get_plugin()); } bool is_inner_col(unsigned idx) const { return m_sig2inner[idx]!=UINT_MAX; } unsigned get_inner_col(unsigned idx) const { SASSERT(is_inner_col(idx)); return m_sig2inner[idx]; } 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; } const relation_base & get_inner() const { return *m_inner; } void add_fact(const relation_fact & f) override; bool contains_fact(const relation_fact & f) const override; sieve_relation * clone() const override; relation_base * complement(func_decl*p) const override; void to_formula(expr_ref& fml) const override; bool empty() const override { return get_inner().empty(); } void reset() override { get_inner().reset(); } unsigned get_size_estimate_rows() const override { return get_inner().get_size_estimate_rows(); } unsigned get_size_estimate_bytes() const override { return get_inner().get_size_estimate_bytes(); } void display(std::ostream & out) const override; }; }; #endif /* DL_SIEVE_RELATION_H_ */ z3-z3-4.8.7/src/muz/rel/dl_sparse_table.cpp000066400000000000000000001534351356505360400204550ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_sparse_table.cpp Abstract: Author: Krystof Hoder (t-khoder) 2010-09-24. Revision History: --*/ #include #include "muz/base/dl_context.h" #include "muz/base/dl_util.h" #include "muz/rel/dl_sparse_table.h" namespace datalog { // ----------------------------------- // // entry_storage // // ----------------------------------- entry_storage::store_offset entry_storage::insert_or_get_reserve_content() { SASSERT(has_reserve()); store_offset entry_ofs = m_data_indexer.insert_if_not_there(m_reserve); if (m_reserve == entry_ofs) { //entry inserted, so reserve is no longer a reserve m_reserve = NO_RESERVE; } return entry_ofs; } bool entry_storage::insert_reserve_content() { SASSERT(has_reserve()); store_offset entry_ofs = m_data_indexer.insert_if_not_there(m_reserve); if (m_reserve == entry_ofs) { //entry inserted, so reserve is no longer a reserve m_reserve = NO_RESERVE; return true; } return false; } bool entry_storage::remove_reserve_content() { SASSERT(has_reserve()); store_offset entry_ofs; if (!find_reserve_content(entry_ofs)) { //the fact was not in the table return false; } remove_offset(entry_ofs); return true; } void entry_storage::remove_offset(store_offset ofs) { m_data_indexer.remove(ofs); store_offset last_ofs = after_last_offset() - m_entry_size; if (ofs!=last_ofs) { SASSERT(ofs + m_entry_size <= last_ofs); //we don't want any holes, so we put the last element at the place //of the removed one m_data_indexer.remove(last_ofs); char * base = &m_data.get(0); memcpy(base+ofs, base+last_ofs, m_entry_size); m_data_indexer.insert(ofs); } if (has_reserve()) { //we already have a reserve, so we need to shrink a little to keep having just one resize_data(m_data_size-m_entry_size); } m_reserve=last_ofs; } unsigned entry_storage::get_size_estimate_bytes() const { size_t sz = m_data.capacity(); sz += m_data_indexer.capacity()*sizeof(storage_indexer::entry); return static_cast(sz); } // ----------------------------------- // // sparse_table::column_layout // // ----------------------------------- unsigned get_domain_length(uint64_t dom_size) { SASSERT(dom_size>0); unsigned length = 0; unsigned dom_size_sm; if (dom_size>UINT_MAX) { dom_size_sm = static_cast(dom_size>>32); length += 32; if ( (dom_size&UINT_MAX)!=0 && dom_size_sm!=UINT_MAX ) { dom_size_sm++; } } else { dom_size_sm=static_cast(dom_size); } if (dom_size_sm == 1) { length += 1; //unary domains } else if (dom_size_sm > 0x80000000u) { length += 32; } else { length += get_num_1bits(next_power_of_two(dom_size_sm)-1); //ceil(log2(dom_size)) } return length; } sparse_table::column_layout::column_layout(const table_signature & sig) : m_functional_col_cnt(sig.functional_columns()) { SASSERT(sig.size() > 0); unsigned ofs = 0; unsigned sig_sz = sig.size(); unsigned first_functional = sig_sz-m_functional_col_cnt; for (unsigned i=0; i0); SASSERT(length<=64); if (size() > 0 && (length > 54 || i == first_functional)) { //large domains must start byte-aligned, as well as functional columns make_byte_aligned_end(size()-1); ofs = back().next_ofs(); } push_back(column_info(ofs, length)); ofs += length; } make_byte_aligned_end(size()-1); SASSERT(back().next_ofs()%8 == 0);//the entries must be aligned to whole bytes m_entry_size = back().next_ofs()/8; if (m_functional_col_cnt) { SASSERT((*this)[first_functional].m_offset%8 == 0); m_functional_part_size = m_entry_size - (*this)[first_functional].m_offset/8; } else { m_functional_part_size = 0; } } void sparse_table::column_layout::make_byte_aligned_end(unsigned col_index0) { unsigned ofs = (*this)[col_index0].next_ofs(); unsigned ofs_bit_part = ofs%8; unsigned rounded_ofs = (ofs_bit_part == 0) ? ofs : (ofs+8-ofs_bit_part); if (rounded_ofs!=ofs) { SASSERT(rounded_ofs>ofs); int diff = rounded_ofs-ofs; unsigned col_idx = col_index0+1; while(diff!=0) { //we should always be able to fix the alignment by the time we reach zero SASSERT(col_idx>0); col_idx--; column_info & ci = (*this)[col_idx]; unsigned new_length = ci.m_length; if (ci.m_length < 64) { unsigned swallowed = std::min(64-static_cast(ci.m_length), diff); diff -= swallowed; new_length += swallowed; } unsigned new_ofs = ci.m_offset+diff; ci = column_info(new_ofs, new_length); } } SASSERT(rounded_ofs%8 == 0); SASSERT((*this)[col_index0].next_ofs()%8 == 0); } // ----------------------------------- // // sparse_table // // ----------------------------------- class sparse_table::our_iterator_core : public iterator_core { class our_row : public row_interface { const our_iterator_core & m_parent; public: our_row(const sparse_table & t, const our_iterator_core & parent) : row_interface(t), m_parent(parent) {} table_element operator[](unsigned col) const override { return m_parent.m_layout.get(m_parent.m_ptr, col); } }; const char * m_end; const char * m_ptr; unsigned m_fact_size; our_row m_row_obj; const column_layout & m_layout; public: our_iterator_core(const sparse_table & t, bool finished) : m_end(t.m_data.after_last()), m_ptr(finished ? m_end : t.m_data.begin()), m_fact_size(t.m_fact_size), m_row_obj(t, *this), m_layout(t.m_column_layout) {} bool is_finished() const override { return m_ptr == m_end; } row_interface & operator*() override { SASSERT(!is_finished()); return m_row_obj; } void operator++() override { SASSERT(!is_finished()); m_ptr+=m_fact_size; } }; class sparse_table::key_indexer { protected: unsigned_vector m_key_cols; public: typedef const store_offset * offset_iterator; /** Iterators returned by \c begin() and \c end() are valid only as long as the \c query_result object that returned them exists. */ struct query_result { private: bool m_singleton; union { store_offset m_single_result; struct { offset_iterator begin; offset_iterator end; } m_many; }; public: /** \brief Empty result. */ query_result() : m_singleton(false) { m_many.begin = nullptr; m_many.end = nullptr; } query_result(offset_iterator begin, offset_iterator end) : m_singleton(false) { m_many.begin = begin; m_many.end = end; } query_result(store_offset single_result) : m_singleton(true), m_single_result(single_result) {} offset_iterator begin() const { return m_singleton ? &m_single_result : m_many.begin; } offset_iterator end() const { return m_singleton ? (&m_single_result+1) : m_many.end; } bool empty() const { return begin() == end(); } }; key_indexer(unsigned key_len, const unsigned * key_cols) : m_key_cols(key_len, key_cols) {} virtual ~key_indexer() {} virtual void update(const sparse_table & t) {} virtual query_result get_matching_offsets(const key_value & key) const = 0; }; class sparse_table::general_key_indexer : public key_indexer { typedef svector offset_vector; typedef size_t_map index_map; index_map m_map; mutable entry_storage m_keys; store_offset m_first_nonindexed; void key_to_reserve(const key_value & key) const { m_keys.ensure_reserve(); m_keys.write_into_reserve((char *)(key.c_ptr())); } offset_vector & get_matching_offset_vector(const key_value & key) { key_to_reserve(key); store_offset ofs = m_keys.insert_or_get_reserve_content(); index_map::entry * e = m_map.find_core(ofs); if (!e) { TRACE("dl_table_relation", tout << "inserting\n";); e = m_map.insert_if_not_there2(ofs, offset_vector()); } return e->get_data().m_value; } public: general_key_indexer(unsigned key_len, const unsigned * key_cols) : key_indexer(key_len, key_cols), m_keys(key_len*sizeof(table_element)), m_first_nonindexed(0) {} void update(const sparse_table & t) override { if (m_first_nonindexed == t.m_data.after_last_offset()) { return; } SASSERT(m_first_nonindexedinsert(ofs); } m_first_nonindexed = t.m_data.after_last_offset(); } query_result get_matching_offsets(const key_value & key) const override { key_to_reserve(key); store_offset ofs; if (!m_keys.find_reserve_content(ofs)) { return query_result(); } index_map::entry * e = m_map.find_core(ofs); if (!e) { return query_result(); } const offset_vector & res = e->get_data().m_value; return query_result(res.begin(), res.end()); } }; /** When doing lookup using this index, the content of the reserve in sparse_table::m_data changes. */ class sparse_table::full_signature_key_indexer : public key_indexer { const sparse_table & m_table; /** Permutation of key columns to make it into table facts. If empty, no permutation is necessary. */ unsigned_vector m_permutation; mutable table_fact m_key_fact; public: static bool can_handle(unsigned key_len, const unsigned * key_cols, const sparse_table & t) { unsigned non_func_cols = t.get_signature().first_functional(); if (key_len!=non_func_cols) { return false; } counter ctr; ctr.count(key_len, key_cols); if (ctr.get_max_counter_value()!=1 || ctr.get_max_positive()!=non_func_cols-1) { return false; } SASSERT(ctr.get_positive_count() == non_func_cols); return true; } full_signature_key_indexer(unsigned key_len, const unsigned * key_cols, const sparse_table & t) : key_indexer(key_len, key_cols), m_table(t) { SASSERT(can_handle(key_len, key_cols, t)); m_permutation.resize(key_len); for (unsigned i=0; i(m_table); t.write_into_reserve(m_key_fact.c_ptr()); store_offset res; if (!t.m_data.find_reserve_content(res)) { return query_result(); } return query_result(res); } }; sparse_table::sparse_table(sparse_table_plugin & p, const table_signature & sig, unsigned init_capacity) : table_base(p, sig), m_column_layout(sig), m_fact_size(m_column_layout.m_entry_size), m_data(m_fact_size, m_column_layout.m_functional_part_size, init_capacity) {} sparse_table::sparse_table(const sparse_table & t) : table_base(t.get_plugin(), t.get_signature()), m_column_layout(t.m_column_layout), m_fact_size(t.m_fact_size), m_data(t.m_data) {} table_base * sparse_table::clone() const { return get_plugin().mk_clone(*this); } sparse_table::~sparse_table() { reset_indexes(); } void sparse_table::reset() { reset_indexes(); m_data.reset(); } table_base::iterator sparse_table::begin() const { return mk_iterator(alloc(our_iterator_core, *this, false)); } table_base::iterator sparse_table::end() const { return mk_iterator(alloc(our_iterator_core, *this, true)); } sparse_table::key_indexer& sparse_table::get_key_indexer(unsigned key_len, const unsigned * key_cols) const { verbose_action _va("get_key_indexer"); #if Z3DEBUG //We allow indexes only on non-functional columns because we want to be able to modify them //without having to worry about updating indexes. //Maybe we might keep a list of indexes that contain functional columns and on an update reset //only those. SASSERT(key_len == 0 || counter().count(key_len, key_cols).get_max_positive()get_data().m_value) { if (full_signature_key_indexer::can_handle(key_len, key_cols, *this)) { key_map_entry->get_data().m_value = alloc(full_signature_key_indexer, key_len, key_cols, *this); } else { key_map_entry->get_data().m_value = alloc(general_key_indexer, key_len, key_cols); } } key_indexer & indexer = *key_map_entry->get_data().m_value; indexer.update(*this); return indexer; } void sparse_table::reset_indexes() { key_index_map::iterator kmit = m_key_indexes.begin(); key_index_map::iterator kmend = m_key_indexes.end(); for (; kmit!=kmend; ++kmit) { dealloc((*kmit).m_value); } m_key_indexes.reset(); } void sparse_table::write_into_reserve(const table_element* f) { TRACE("dl_table_relation", tout << "\n";); m_data.ensure_reserve(); char * reserve = m_data.get_reserve_ptr(); unsigned col_cnt = m_column_layout.size(); for (unsigned i = 0; i < col_cnt; ++i) { SASSERT(f[i] < get_signature()[i]); //the value fits into the table signature m_column_layout.set(reserve, i, f[i]); } } bool sparse_table::add_fact(const char * data) { verbose_action _va("add_fact", 10); m_data.write_into_reserve(data); return add_reserve_content(); } void sparse_table::add_fact(const table_fact & f) { write_into_reserve(f.c_ptr()); add_reserve_content(); } bool sparse_table::add_reserve_content() { return m_data.insert_reserve_content(); } bool sparse_table::contains_fact(const table_fact & f) const { verbose_action _va("contains_fact", 2); sparse_table & t = const_cast(*this); t.write_into_reserve(f.c_ptr()); unsigned func_col_cnt = get_signature().functional_columns(); if (func_col_cnt == 0) { return t.m_data.reserve_content_already_present(); } else { store_offset ofs; if (!t.m_data.find_reserve_content(ofs)) { return false; } unsigned sz = get_signature().size(); for (unsigned i=func_col_cnt; i(*this); t.write_into_reserve(f.c_ptr()); store_offset ofs; if (!t.m_data.find_reserve_content(ofs)) { return false; } unsigned sz = sig.size(); for (unsigned i=sig.first_functional(); ipre_projection_idx); dest_layout.set(dest, dest_idx++, src_layout.get(src, i)); } } void sparse_table::concatenate_rows(const column_layout & layout1, const column_layout & layout2, const column_layout & layout_res, const char * ptr1, const char * ptr2, char * res, const unsigned * removed_cols) { unsigned t1non_func = layout1.size()-layout1.m_functional_col_cnt; unsigned t2non_func = layout2.size()-layout2.m_functional_col_cnt; unsigned t1cols = layout1.size(); unsigned t2cols = layout2.size(); unsigned orig_i = 0; unsigned res_i = 0; const unsigned * next_removed = removed_cols; copy_columns(layout1, layout_res, 0, t1non_func, ptr1, res, res_i, orig_i, next_removed); copy_columns(layout2, layout_res, 0, t2non_func, ptr2, res, res_i, orig_i, next_removed); copy_columns(layout1, layout_res, t1non_func, t1cols, ptr1, res, res_i, orig_i, next_removed); copy_columns(layout2, layout_res, t2non_func, t2cols, ptr2, res, res_i, orig_i, next_removed); } void sparse_table::garbage_collect() { if (memory::above_high_watermark()) { get_plugin().garbage_collect(); } if (memory::above_high_watermark()) { IF_VERBOSE(1, verbose_stream() << "Ran out of memory while filling table of size: " << get_size_estimate_rows() << " rows " << get_size_estimate_bytes() << " bytes\n";); throw out_of_memory_error(); } } void sparse_table::self_agnostic_join_project(const sparse_table & t1, const sparse_table & t2, unsigned joined_col_cnt, const unsigned * t1_joined_cols, const unsigned * t2_joined_cols, const unsigned * removed_cols, bool tables_swapped, sparse_table & result) { verbose_action _va("join_project", 1); unsigned t1_entry_size = t1.m_fact_size; unsigned t2_entry_size = t2.m_fact_size; size_t t1idx = 0; size_t t1end = t1.m_data.after_last_offset(); TRACE("dl_table_relation", tout << "joined_col_cnt: " << joined_col_cnt << "\n"; tout << "t1_entry_size: " << t1_entry_size << "\n"; tout << "t2_entry_size: " << t2_entry_size << "\n"; t1.display(tout); t2.display(tout); tout << (&t1) << " " << (&t2) << " " << (&result) << "\n"; ); if (joined_col_cnt == 0) { size_t t2idx = 0; size_t t2end = t2.m_data.after_last_offset(); for (; t1idx!=t1end; t1idx+=t1_entry_size) { for (t2idx = 0; t2idx != t2end; t2idx += t2_entry_size) { result.m_data.ensure_reserve(); result.garbage_collect(); char * res_reserve = result.m_data.get_reserve_ptr(); char const* t1ptr = t1.get_at_offset(t1idx); char const* t2ptr = t2.get_at_offset(t2idx); if (tables_swapped) { concatenate_rows(t2.m_column_layout, t1.m_column_layout, result.m_column_layout, t2ptr, t1ptr, res_reserve, removed_cols); } else { concatenate_rows(t1.m_column_layout, t2.m_column_layout, result.m_column_layout, t1ptr, t2ptr, res_reserve, removed_cols); } result.add_reserve_content(); } } return; } key_value t1_key; t1_key.resize(joined_col_cnt); key_indexer& t2_indexer = t2.get_key_indexer(joined_col_cnt, t2_joined_cols); bool key_modified = true; key_indexer::query_result t2_offsets; for (; t1idx != t1end; t1idx += t1_entry_size) { for (unsigned i = 0; i < joined_col_cnt; i++) { table_element val = t1.m_column_layout.get(t1.get_at_offset(t1idx), t1_joined_cols[i]); TRACE("dl_table_relation", tout << "val: " << val << " " << t1idx << " " << t1_joined_cols[i] << "\n";); if (t1_key[i] != val) { t1_key[i] = val; key_modified = true; } } if (key_modified) { t2_offsets = t2_indexer.get_matching_offsets(t1_key); key_modified = false; } if (t2_offsets.empty()) { continue; } key_indexer::offset_iterator t2ofs_it = t2_offsets.begin(); key_indexer::offset_iterator t2ofs_end = t2_offsets.end(); for (; t2ofs_it != t2ofs_end; ++t2ofs_it) { store_offset t2ofs = *t2ofs_it; result.m_data.ensure_reserve(); result.garbage_collect(); char * res_reserve = result.m_data.get_reserve_ptr(); char const * t1ptr = t1.get_at_offset(t1idx); char const * t2ptr = t2.get_at_offset(t2ofs); if (tables_swapped) { concatenate_rows(t2.m_column_layout, t1.m_column_layout, result.m_column_layout, t2ptr, t1ptr, res_reserve, removed_cols); } else { concatenate_rows(t1.m_column_layout, t2.m_column_layout, result.m_column_layout, t1ptr, t2ptr, res_reserve, removed_cols); } result.add_reserve_content(); } } } // ----------------------------------- // // sparse_table_plugin // // ----------------------------------- sparse_table_plugin::sparse_table_plugin(relation_manager & manager) : table_plugin(symbol("sparse"), manager) {} sparse_table_plugin::~sparse_table_plugin() { reset(); } sparse_table const& sparse_table_plugin::get(table_base const& t) { return dynamic_cast(t); } sparse_table& sparse_table_plugin::get(table_base& t) { return dynamic_cast(t); } sparse_table const* sparse_table_plugin::get(table_base const* t) { return dynamic_cast(t); } sparse_table* sparse_table_plugin::get(table_base* t) { return dynamic_cast(t); } void sparse_table_plugin::reset() { table_pool::iterator it = m_pool.begin(); table_pool::iterator end = m_pool.end(); for (; it!=end; ++it) { sp_table_vector * vect = it->m_value; sp_table_vector::iterator vit = vect->begin(); sp_table_vector::iterator vend = vect->end(); for (; vit!=vend; ++vit) { (*vit)->destroy(); //calling deallocate() would only put the table back into the pool } dealloc(vect); } m_pool.reset(); } void sparse_table_plugin::garbage_collect() { IF_VERBOSE(2, verbose_stream() << "garbage collecting "<< memory::get_allocation_size() << " bytes down to ";); reset(); IF_VERBOSE(2, verbose_stream() << memory::get_allocation_size() << " bytes\n";); } void sparse_table_plugin::recycle(sparse_table * t) { verbose_action _va("recycle", 2); const table_signature & sig = t->get_signature(); t->reset(); table_pool::entry * e = m_pool.insert_if_not_there2(sig, nullptr); sp_table_vector * & vect = e->get_data().m_value; if (vect == nullptr) { vect = alloc(sp_table_vector); } IF_VERBOSE(12, verbose_stream() << "Recycle: " << t->get_size_estimate_bytes() << "\n";); vect->push_back(t); } table_base * sparse_table_plugin::mk_empty(const table_signature & s) { SASSERT(can_handle_signature(s)); sp_table_vector * vect; if (!m_pool.find(s, vect) || vect->empty()) { return alloc(sparse_table, *this, s); } sparse_table * res = vect->back(); vect->pop_back(); return res; } sparse_table * sparse_table_plugin::mk_clone(const sparse_table & t) { sparse_table * res = get(mk_empty(t.get_signature())); res->m_data = t.m_data; return res; } bool sparse_table_plugin::join_involves_functional(const table_signature & s1, const table_signature & s2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { if (col_cnt == 0) { return false; } return counter().count(col_cnt, cols1).get_max_positive()>=s1.first_functional() || counter().count(col_cnt, cols2).get_max_positive()>=s2.first_functional(); } class sparse_table_plugin::join_project_fn : public convenient_table_join_project_fn { public: join_project_fn(const table_signature & t1_sig, const table_signature & t2_sig, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols) : convenient_table_join_project_fn(t1_sig, t2_sig, col_cnt, cols1, cols2, removed_col_cnt, removed_cols) { m_removed_cols.push_back(UINT_MAX); } table_base * operator()(const table_base & tb1, const table_base & tb2) override { const sparse_table & t1 = get(tb1); const sparse_table & t2 = get(tb2); sparse_table_plugin & plugin = t1.get_plugin(); sparse_table * res = get(plugin.mk_empty(get_result_signature())); //If we join with some intersection, want to iterate over the smaller table and //do indexing into the bigger one. If we simply do a product, we want the bigger //one to be at the outer iteration (then the small one will hopefully fit into //the cache) if ( (t1.row_count() > t2.row_count()) == (!m_cols1.empty()) ) { sparse_table::self_agnostic_join_project(t2, t1, m_cols1.size(), m_cols2.c_ptr(), m_cols1.c_ptr(), m_removed_cols.c_ptr(), true, *res); } else { sparse_table::self_agnostic_join_project(t1, t2, m_cols1.size(), m_cols1.c_ptr(), m_cols2.c_ptr(), m_removed_cols.c_ptr(), false, *res); } TRACE("dl_table_relation", tb1.display(tout); tb2.display(tout); res->display(tout); ); return res; } }; table_join_fn * sparse_table_plugin::mk_join_fn(const table_base & t1, const table_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { const table_signature & sig1 = t1.get_signature(); const table_signature & sig2 = t2.get_signature(); if (t1.get_kind()!=get_kind() || t2.get_kind()!=get_kind() || join_involves_functional(sig1, sig2, col_cnt, cols1, cols2)) { //We also don't allow indexes on functional columns (and they are needed for joins) return nullptr; } return mk_join_project_fn(t1, t2, col_cnt, cols1, cols2, 0, static_cast(nullptr)); } table_join_fn * sparse_table_plugin::mk_join_project_fn(const table_base & t1, const table_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols) { const table_signature & sig1 = t1.get_signature(); const table_signature & sig2 = t2.get_signature(); if (t1.get_kind()!=get_kind() || t2.get_kind()!=get_kind() || removed_col_cnt == t1.get_signature().size()+t2.get_signature().size() || join_involves_functional(sig1, sig2, col_cnt, cols1, cols2)) { //We don't allow sparse tables with zero signatures (and project on all columns leads to such) //We also don't allow indexes on functional columns. return nullptr; } return alloc(join_project_fn, t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2, removed_col_cnt, removed_cols); } class sparse_table_plugin::union_fn : public table_union_fn { public: void operator()(table_base & tgt0, const table_base & src0, table_base * delta0) override { verbose_action _va("union"); sparse_table & tgt = get(tgt0); const sparse_table & src = get(src0); sparse_table * delta = get(delta0); unsigned fact_size = tgt.m_fact_size; const char* ptr = src.m_data.begin(); const char* after_last=src.m_data.after_last(); for (; ptradd_fact(ptr); } } } }; table_union_fn * sparse_table_plugin::mk_union_fn(const table_base & tgt, const table_base & src, const table_base * delta) { if (tgt.get_kind()!=get_kind() || src.get_kind()!=get_kind() || (delta && delta->get_kind()!=get_kind()) || tgt.get_signature()!=src.get_signature() || (delta && delta->get_signature()!=tgt.get_signature())) { return nullptr; } return alloc(union_fn); } class sparse_table_plugin::project_fn : public convenient_table_project_fn { const unsigned m_inp_col_cnt; const unsigned m_removed_col_cnt; const unsigned m_result_col_cnt; public: project_fn(const table_signature & orig_sig, unsigned removed_col_cnt, const unsigned * removed_cols) : convenient_table_project_fn(orig_sig, removed_col_cnt, removed_cols), m_inp_col_cnt(orig_sig.size()), m_removed_col_cnt(removed_col_cnt), m_result_col_cnt(orig_sig.size()-removed_col_cnt) { SASSERT(removed_col_cnt>0); } virtual void transform_row(const char * src, char * tgt, const sparse_table::column_layout & src_layout, const sparse_table::column_layout & tgt_layout) { unsigned r_idx=0; unsigned tgt_i=0; for (unsigned i=0; im_column_layout; const char* t_ptr = t.m_data.begin(); const char* t_end = t.m_data.after_last(); for (; t_ptr!=t_end; t_ptr+=t_fact_size) { SASSERT(t_ptrm_data.ensure_reserve(); char * res_ptr = res->m_data.get_reserve_ptr(); transform_row(t_ptr, res_ptr, src_layout, tgt_layout); res->m_data.insert_reserve_content(); } return res; } }; table_transformer_fn * sparse_table_plugin::mk_project_fn(const table_base & t, unsigned col_cnt, const unsigned * removed_cols) { if (col_cnt == t.get_signature().size()) { return nullptr; } return alloc(project_fn, t.get_signature(), col_cnt, removed_cols); } class sparse_table_plugin::select_equal_and_project_fn : public convenient_table_transformer_fn { const unsigned m_col; sparse_table::key_value m_key; public: select_equal_and_project_fn(const table_signature & orig_sig, table_element val, unsigned col) : m_col(col) { table_signature::from_project(orig_sig, 1, &col, get_result_signature()); m_key.push_back(val); } table_base * operator()(const table_base & tb) override { verbose_action _va("select_equal_and_project"); const sparse_table & t = get(tb); sparse_table_plugin & plugin = t.get_plugin(); sparse_table * res = get(plugin.mk_empty(get_result_signature())); const sparse_table::column_layout & t_layout = t.m_column_layout; const sparse_table::column_layout & res_layout = res->m_column_layout; unsigned t_cols = t_layout.size(); sparse_table::key_indexer & indexer = t.get_key_indexer(1, &m_col); sparse_table::key_indexer::query_result t_offsets = indexer.get_matching_offsets(m_key); if (t_offsets.empty()) { //no matches return res; } sparse_table::key_indexer::offset_iterator ofs_it=t_offsets.begin(); sparse_table::key_indexer::offset_iterator ofs_end=t_offsets.end(); for (; ofs_it!=ofs_end; ++ofs_it) { sparse_table::store_offset t_ofs = *ofs_it; const char * t_ptr = t.get_at_offset(t_ofs); res->m_data.ensure_reserve(); char * res_reserve = res->m_data.get_reserve_ptr(); unsigned res_i = 0; for (unsigned i=0; iadd_reserve_content(); } return res; } }; table_transformer_fn * sparse_table_plugin::mk_select_equal_and_project_fn(const table_base & t, const table_element & value, unsigned col) { if (t.get_kind()!=get_kind() || t.get_signature().size() == 1 || col>=t.get_signature().first_functional()) { //We don't allow sparse tables with zero signatures (and project on a single //column table produces one). //We also don't allow indexes on functional columns. And our implementation of //select_equal_and_project uses index on \c col. return nullptr; } return alloc(select_equal_and_project_fn, t.get_signature(), value, col); } class sparse_table_plugin::rename_fn : public convenient_table_rename_fn { unsigned_vector m_out_of_cycle; public: rename_fn(const table_signature & orig_sig, unsigned permutation_cycle_len, const unsigned * permutation_cycle) : convenient_table_rename_fn(orig_sig, permutation_cycle_len, permutation_cycle) { SASSERT(permutation_cycle_len>=2); idx_set cycle_cols; for (unsigned i=0; i < permutation_cycle_len; ++i) { cycle_cols.insert(permutation_cycle[i]); } for (unsigned i=0; i < orig_sig.size(); ++i) { if (!cycle_cols.contains(i)) { m_out_of_cycle.push_back(i); } } } void transform_row(const char * src, char * tgt, const sparse_table::column_layout & src_layout, const sparse_table::column_layout & tgt_layout) { for (unsigned i=1; i < m_cycle.size(); ++i) { tgt_layout.set(tgt, m_cycle[i-1], src_layout.get(src, m_cycle[i])); } tgt_layout.set(tgt, m_cycle[m_cycle.size()-1], src_layout.get(src, m_cycle[0])); unsigned_vector::const_iterator it = m_out_of_cycle.begin(); unsigned_vector::const_iterator end = m_out_of_cycle.end(); for (; it!=end; ++it) { unsigned col = *it; tgt_layout.set(tgt, col, src_layout.get(src, col)); } } table_base * operator()(const table_base & tb) override { verbose_action _va("rename"); const sparse_table & t = get(tb); unsigned t_fact_size = t.m_fact_size; sparse_table_plugin & plugin = t.get_plugin(); sparse_table * res = get(plugin.mk_empty(get_result_signature())); size_t res_fact_size = res->m_fact_size; size_t res_data_size = res_fact_size*t.row_count(); if (res_fact_size != 0 && (res_data_size / res_fact_size) != t.row_count()) { throw default_exception("multiplication overflow"); } res->m_data.resize_data(res_data_size); //here we can separate data creating and insertion into hashmap, since we know //that no row will become duplicate //create the data const char* t_ptr = t.m_data.begin(); char* res_ptr = res->m_data.begin(); char* res_end = res_ptr+res_data_size; for (; res_ptr!=res_end; t_ptr+=t_fact_size, res_ptr+=res_fact_size) { transform_row(t_ptr, res_ptr, t.m_column_layout, res->m_column_layout); } //and insert them into the hash-map for (size_t i = 0; i != res_data_size; i += res_fact_size) { TRUSTME(res->m_data.insert_offset(i)); } return res; } }; table_transformer_fn * sparse_table_plugin::mk_rename_fn(const table_base & t, unsigned permutation_cycle_len, const unsigned * permutation_cycle) { if (t.get_kind()!=get_kind()) { return nullptr; } return alloc(rename_fn, t.get_signature(), permutation_cycle_len, permutation_cycle); } class sparse_table_plugin::negation_filter_fn : public convenient_table_negation_filter_fn { typedef sparse_table::store_offset store_offset; typedef sparse_table::key_value key_value; typedef sparse_table::key_indexer key_indexer; bool m_joining_neg_non_functional; /** Used by \c collect_intersection_offsets function. If tgt_is_first is false, contains the same items as \c res. */ idx_set m_intersection_content; public: negation_filter_fn(const table_base & tgt, const table_base & neg, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) : convenient_table_negation_filter_fn(tgt, neg, joined_col_cnt, t_cols, negated_cols) { unsigned neg_first_func = neg.get_signature().first_functional(); counter ctr; ctr.count(m_cols2); m_joining_neg_non_functional = ctr.get_max_counter_value() == 1 && ctr.get_positive_count() == neg_first_func && (neg_first_func == 0 || ctr.get_max_positive() == neg_first_func-1); } /** Collect offsets of rows in \c t1 or \c t2 (depends on whether \c tgt_is_first is true or false) that have a match in the other table into \c res. Offsets in \c res are in ascending order. */ void collect_intersection_offsets(const sparse_table & t1, const sparse_table & t2, bool tgt_is_first, svector & res) { SASSERT(res.empty()); m_intersection_content.reset(); unsigned joined_col_cnt = m_cols1.size(); unsigned t1_entry_size = t1.m_data.entry_size(); const unsigned * cols1 = tgt_is_first ? m_cols1.c_ptr() : m_cols2.c_ptr(); const unsigned * cols2 = tgt_is_first ? m_cols2.c_ptr() : m_cols1.c_ptr(); key_value t1_key; t1_key.resize(joined_col_cnt); key_indexer & t2_indexer = t2.get_key_indexer(joined_col_cnt, cols2); bool key_modified=true; key_indexer::query_result t2_offsets; store_offset t1_after_last = t1.m_data.after_last_offset(); for (store_offset t1_ofs=0; t1_ofs(ofs); if (ofs != offs2) { throw default_exception("Z3 cannot perform negation with excessively large tables"); } if (!m_intersection_content.contains(offs2)) { m_intersection_content.insert(offs2); res.push_back(ofs); } } } } if (!tgt_is_first) { //in this case \c res now may be in arbitrary order std::sort(res.begin(), res.end()); } } void operator()(table_base & tgt0, const table_base & neg0) override { sparse_table & tgt = get(tgt0); const sparse_table & neg = get(neg0); verbose_action _va("filter_by_negation"); if (m_cols1.empty()) { if (!neg.empty()) { tgt.reset(); } return; } svector to_remove; //offsets here are in increasing order //We don't do just the simple tgt.row_count()>neg.row_count() because the swapped case is //more expensive. The constant 4 is, however, just my guess what the ratio might be. if (tgt.row_count()/4>neg.row_count()) { collect_intersection_offsets(neg, tgt, false, to_remove); } else { collect_intersection_offsets(tgt, neg, true, to_remove); } //the largest offsets are at the end, so we can remove them one by one while (!to_remove.empty()) { store_offset removed_ofs = to_remove.back(); to_remove.pop_back(); tgt.m_data.remove_offset(removed_ofs); } tgt.reset_indexes(); } }; table_intersection_filter_fn * sparse_table_plugin::mk_filter_by_negation_fn(const table_base & t, const table_base & negated_obj, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) { if (!check_kind(t) || !check_kind(negated_obj) || join_involves_functional(t.get_signature(), negated_obj.get_signature(), joined_col_cnt, t_cols, negated_cols) ) { return nullptr; } return alloc(negation_filter_fn, t, negated_obj, joined_col_cnt, t_cols, negated_cols); } /** T \ (S1 Join S2) t_cols - columns from T s_cols - columns from (S1 Join S2) that are equated src1_cols - columns from S1 equated with columns from S2 src2_cols - columns from S2 equated with columns from S1 t1_cols - columns from T that map into S1 s1_cols - matching columns from s_cols for t1_cols t2s1_cols - columns from T that map into S2, and columns from src1 that join src2 s2_cols - matching columns from t2s1_cols columns from s2 that are equal to a column from s1 that is in s_cols: - ... */ class sparse_table_plugin::negated_join_fn : public table_intersection_join_filter_fn { typedef sparse_table::store_offset store_offset; typedef sparse_table::key_value key_value; typedef sparse_table::key_indexer key_indexer; unsigned_vector m_t1_cols; unsigned_vector m_s1_cols; unsigned_vector m_t2_cols; unsigned_vector m_s2_cols; unsigned_vector m_src1_cols; public: negated_join_fn( table_base const& src1, unsigned_vector const& t_cols, unsigned_vector const& src_cols, unsigned_vector const& src1_cols, unsigned_vector const& src2_cols): m_src1_cols(src1_cols) { // split t_cols and src_cols according to src1, and src2 unsigned src1_size = src1.get_signature().size(); for (unsigned i = 0; i < t_cols.size(); ++i) { if (src_cols[i] < src1_size) { m_t1_cols.push_back(t_cols[i]); m_s1_cols.push_back(src_cols[i]); } else { m_t2_cols.push_back(t_cols[i]); m_s2_cols.push_back(src_cols[i]); } } m_s2_cols.append(src2_cols); } void operator()(table_base & _t, const table_base & _s1, const table_base& _s2) override { verbose_action _va("negated_join"); sparse_table& t = get(_t); svector to_remove; collect_to_remove(t, get(_s1), get(_s2), to_remove); for (unsigned i = 0; i < to_remove.size(); ++i) { t.m_data.remove_offset(to_remove[i]); } t.reset_indexes(); } private: void collect_to_remove(sparse_table& t, sparse_table const& s1, sparse_table const& s2, svector& to_remove) { key_value s1_key, s2_key; SASSERT(&s1 != &s2); SASSERT(m_s1_cols.size() == m_t1_cols.size()); SASSERT(m_s2_cols.size() == m_t2_cols.size() + m_src1_cols.size()); s1_key.resize(m_s1_cols.size()); s2_key.resize(m_s2_cols.size()); key_indexer & s1_indexer = s1.get_key_indexer(m_s1_cols.size(), m_s1_cols.c_ptr()); key_indexer & s2_indexer = s2.get_key_indexer(m_s2_cols.size(), m_s2_cols.c_ptr()); store_offset t_after_last = t.m_data.after_last_offset(); key_indexer::query_result s1_offsets, s2_offsets; unsigned t_entry_size = t.m_data.entry_size(); for (store_offset t_ofs = 0; t_ofs < t_after_last; t_ofs += t_entry_size) { if (update_key(s1_key, 0, t, t_ofs, m_t1_cols)) { s1_offsets = s1_indexer.get_matching_offsets(s1_key); } key_indexer::offset_iterator it = s1_offsets.begin(); key_indexer::offset_iterator end = s1_offsets.end(); for (; it != end; ++it) { store_offset s1_ofs = *it; bool upd1 = update_key(s2_key, 0, t, t_ofs, m_t2_cols); bool upd2 = update_key(s2_key, m_t2_cols.size(), s1, s1_ofs, m_src1_cols); if (upd1 || upd2) { s2_offsets = s2_indexer.get_matching_offsets(s2_key); } if (!s2_offsets.empty()) { to_remove.push_back(t_ofs); break; } } } } inline bool update_key(key_value& key, unsigned key_offset, sparse_table const& t, store_offset ofs, unsigned_vector const& cols) { bool modified = false; unsigned sz = cols.size(); for (unsigned i = 0; i < sz; ++i) { table_element val = t.get_cell(ofs, cols[i]); modified = update_key(key[i+key_offset], val) || modified; } return modified; } inline bool update_key(table_element& tgt, table_element src) { if (tgt == src) { return false; } else { tgt = src; return true; } } }; table_intersection_join_filter_fn* sparse_table_plugin::mk_filter_by_negated_join_fn( const table_base & t, const table_base & src1, const table_base & src2, unsigned_vector const& t_cols, unsigned_vector const& src_cols, unsigned_vector const& src1_cols, unsigned_vector const& src2_cols) { if (check_kind(t) && check_kind(src1) && check_kind(src2)) { return alloc(negated_join_fn, src1, t_cols, src_cols, src1_cols, src2_cols); } else { return nullptr; } } unsigned sparse_table::get_size_estimate_bytes() const { unsigned sz = 0; sz += m_data.get_size_estimate_bytes(); sz += m_key_indexes.capacity()*8; // TBD return sz; } }; z3-z3-4.8.7/src/muz/rel/dl_sparse_table.h000066400000000000000000000453671356505360400201260ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_table.h Abstract: Author: Krystof Hoder (t-khoder) 2010-09-01. Revision History: --*/ #ifndef DL_SPARSE_TABLE_H_ #define DL_SPARSE_TABLE_H_ #include #include #include #include "ast/ast.h" #include "util/bit_vector.h" #include "util/buffer.h" #include "util/hashtable.h" #include "util/map.h" #include "util/ref_vector.h" #include "util/vector.h" #include "muz/rel/dl_base.h" namespace datalog { class sparse_table; class sparse_table_plugin : public table_plugin { friend class sparse_table; protected: class join_project_fn; class union_fn; class transformer_fn; class rename_fn; class project_fn; class negation_filter_fn; class select_equal_and_project_fn; class negated_join_fn; typedef ptr_vector sp_table_vector; typedef map table_pool; table_pool m_pool; void recycle(sparse_table * t); void garbage_collect(); void reset(); static bool join_involves_functional(const table_signature & s1, const table_signature & s2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); public: typedef sparse_table table; sparse_table_plugin(relation_manager & manager); ~sparse_table_plugin() override; bool can_handle_signature(const table_signature & s) override { return !s.empty(); } table_base * mk_empty(const table_signature & s) override; sparse_table * mk_clone(const sparse_table & t); protected: table_join_fn * mk_join_fn(const table_base & t1, const table_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) override; table_join_fn * mk_join_project_fn(const table_base & t1, const table_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols) override; table_union_fn * mk_union_fn(const table_base & tgt, const table_base & src, const table_base * delta) override; table_transformer_fn * mk_project_fn(const table_base & t, unsigned col_cnt, const unsigned * removed_cols) override; table_transformer_fn * mk_rename_fn(const table_base & t, unsigned permutation_cycle_len, const unsigned * permutation_cycle) override; table_transformer_fn * mk_select_equal_and_project_fn(const table_base & t, const table_element & value, unsigned col) override; table_intersection_filter_fn * mk_filter_by_negation_fn(const table_base & t, const table_base & negated_obj, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) override; table_intersection_join_filter_fn* mk_filter_by_negated_join_fn( const table_base & t, const table_base & src1, const table_base & src2, unsigned_vector const& t_cols, unsigned_vector const& src_cols, unsigned_vector const& src1_cols, unsigned_vector const& src2_cols) override; static sparse_table const& get(table_base const&); static sparse_table& get(table_base&); static sparse_table const* get(table_base const*); static sparse_table* get(table_base*); }; class entry_storage { public: typedef size_t store_offset; private: typedef svector storage; class offset_hash_proc { storage & m_storage; unsigned m_unique_entry_size; public: offset_hash_proc(storage & s, unsigned unique_entry_sz) : m_storage(s), m_unique_entry_size(unique_entry_sz) {} unsigned operator()(store_offset ofs) const { return string_hash(m_storage.c_ptr()+ofs, m_unique_entry_size, 0); } }; class offset_eq_proc { storage & m_storage; unsigned m_unique_entry_size; public: offset_eq_proc(storage & s, unsigned unique_entry_sz) : m_storage(s), m_unique_entry_size(unique_entry_sz) {} bool operator()(store_offset o1, store_offset o2) const { const char * base = m_storage.c_ptr(); return memcmp(base+o1, base+o2, m_unique_entry_size)==0; } }; typedef hashtable storage_indexer; static const store_offset NO_RESERVE = UINT_MAX; unsigned m_entry_size; unsigned m_unique_part_size; size_t m_data_size; /** Invariant: Every or all but one blocks of length \c m_entry_size in the \c m_data vector are unique sequences of bytes and have their offset stored in the \c m_data_indexer hashtable. If the offset of the last block is not stored in the hashtable, it is stored in the \c m_reserve variable. Otherwise \c m_reserve==NO_RESERVE. The size of m_data is actually 8 bytes larger than stated in m_data_size, so that we may deref an uint64_t pointer at the end of the array. */ storage m_data; storage_indexer m_data_indexer; store_offset m_reserve; public: entry_storage(unsigned entry_size, unsigned functional_size = 0, unsigned init_size = 0) : m_entry_size(entry_size), m_unique_part_size(entry_size-functional_size), m_data_indexer(next_power_of_two(std::max(8u,init_size)), offset_hash_proc(m_data, m_unique_part_size), offset_eq_proc(m_data, m_unique_part_size)), m_reserve(NO_RESERVE) { SASSERT(entry_size>0); SASSERT(functional_size<=entry_size); resize_data(init_size); resize_data(0); } entry_storage(const entry_storage &s) : m_entry_size(s.m_entry_size), m_unique_part_size(s.m_unique_part_size), m_data_size(s.m_data_size), m_data(s.m_data), m_data_indexer(next_power_of_two(std::max(8u,s.entry_count())), offset_hash_proc(m_data, m_unique_part_size), offset_eq_proc(m_data, m_unique_part_size)), m_reserve(s.m_reserve) { store_offset after_last=after_last_offset(); for(store_offset i=0; i(this)->get(ofs); } unsigned entry_count() const { return m_data_indexer.size(); } store_offset after_last_offset() const { return (m_reserve==NO_RESERVE) ? m_data_size : m_reserve; } char * begin() { return get(0); } const char * begin() const { return get(0); } const char * after_last() const { return get(after_last_offset()); } bool has_reserve() const { return m_reserve!=NO_RESERVE; } store_offset reserve() const { SASSERT(has_reserve()); return m_reserve; } void ensure_reserve() { if(has_reserve()) { SASSERT(m_reserve==m_data_size-m_entry_size); return; } m_reserve = m_data_size; resize_data(m_data_size+m_entry_size); } /** \brief Return pointer to the reserve. The reserve must exist when the function is called. */ char * get_reserve_ptr() { SASSERT(has_reserve()); return &m_data.get(reserve()); } bool reserve_content_already_present() const { SASSERT(has_reserve()); return m_data_indexer.contains(reserve()); } bool find_reserve_content(store_offset & result) const { SASSERT(has_reserve()); storage_indexer::entry * indexer_entry = m_data_indexer.find_core(reserve()); if(!indexer_entry) { return false; } result = indexer_entry->get_data(); return true; } /** \brief Write fact \c f into the reserve at the end of the \c m_data storage. If the reserve does not exist, this function creates it. */ void write_into_reserve(const char * data) { ensure_reserve(); memcpy(get_reserve_ptr(), data, m_entry_size); } /** \brief If the fact in reserve is not in the table, insert it there and return true; otherwise return false. When a fact is inserted into the table, the reserve becomes part of the table and is no longer a reserve. */ bool insert_reserve_content(); store_offset insert_or_get_reserve_content(); bool remove_reserve_content(); /** Remove data at the offset \c ofs. Data with offset lower than \c ofs are not be modified by this function, data with higher offset may be moved. */ void remove_offset(store_offset ofs); //the following two operations allow breaking of the object invariant! void resize_data(size_t sz) { m_data_size = sz; if (sz + sizeof(uint64_t) < sz) { throw default_exception("overflow resizing data section for sparse table"); } m_data.resize(sz + sizeof(uint64_t)); } bool insert_offset(store_offset ofs) { return m_data_indexer.insert_if_not_there(ofs)==ofs; } }; class sparse_table : public table_base { friend class sparse_table_plugin; friend class sparse_table_plugin::join_project_fn; friend class sparse_table_plugin::union_fn; friend class sparse_table_plugin::transformer_fn; friend class sparse_table_plugin::rename_fn; friend class sparse_table_plugin::project_fn; friend class sparse_table_plugin::negation_filter_fn; friend class sparse_table_plugin::select_equal_and_project_fn; class our_iterator_core; class key_indexer; class general_key_indexer; class full_signature_key_indexer; typedef entry_storage::store_offset store_offset; class column_info { unsigned m_big_offset; unsigned m_small_offset; uint64_t m_mask; uint64_t m_write_mask; public: unsigned m_offset; //!< in bits unsigned m_length; //!< in bits column_info(unsigned offset, unsigned length) : m_big_offset(offset / 8), m_small_offset(offset % 8), m_mask( length == 64 ? ULLONG_MAX : (static_cast(1)<(rec + m_big_offset); uint64_t res = *ptr; res >>= m_small_offset; res &= m_mask; return res; } void set(char * rec, table_element val) const { SASSERT( (val&~m_mask)==0 ); //the value fits into the column uint64_t * ptr = reinterpret_cast(rec + m_big_offset); *ptr &= m_write_mask; *ptr |= val << m_small_offset; } unsigned next_ofs() const { return m_offset+m_length; } }; class column_layout : public svector { void make_byte_aligned_end(unsigned col_index); public: unsigned m_entry_size; /** Number of last bytes which correspond to functional columns in the signature. */ unsigned m_functional_part_size; unsigned m_functional_col_cnt; column_layout(const table_signature & sig); table_element get(const char * rec, unsigned col) const { return (*this)[col].get(rec); } void set(char * rec, unsigned col, table_element val) const { return (*this)[col].set(rec, val); } }; typedef svector key_spec; //sequence of columns in a key typedef svector key_value; //values of key columns typedef map, vector_eq_proc > key_index_map; static const store_offset NO_RESERVE = UINT_MAX; column_layout m_column_layout; unsigned m_fact_size; entry_storage m_data; mutable key_index_map m_key_indexes; const char * get_at_offset(store_offset i) const { return m_data.get(i); } table_element get_cell(store_offset ofs, unsigned column) const { return m_column_layout.get(m_data.get(ofs), column); } void set_cell(store_offset ofs, unsigned column, table_element val) { m_column_layout.set(m_data.get(ofs), column, val); } void write_into_reserve(const table_element* f); /** \brief Return reference to an indexer over columns in \c key_cols. An indexer can retrieve a sequence of offsets that with \c key_cols columns equal to the specified key. Indexers are populated lazily -- they remember the position of the last fact they contain, and when an indexer is retrieved by the \c get_key_indexer function, all the new facts are added into the indexer. When a fact is removed from the table, all indexers are destroyed. This is not an extra expense in the current use scenario, because we first perform all fact removals and do the joins only after that (joins are the only operations that lead to index construction). */ key_indexer& get_key_indexer(unsigned key_len, const unsigned * key_cols) const; void reset_indexes(); static void copy_columns(const column_layout & src_layout, const column_layout & dest_layout, unsigned start_index, unsigned after_last, const char * src, char * dest, unsigned & dest_idx, unsigned & pre_projection_idx, const unsigned * & next_removed); /** \c array \c removed_cols contains column indexes to be removed in ascending order and is terminated by a number greater than the highest column index of a join the two tables. This is to simplify the traversal of the array when building facts. */ static void concatenate_rows(const column_layout & layout1, const column_layout & layout2, const column_layout & layout_res, const char * ptr1, const char * ptr2, char * res, const unsigned * removed_cols); /** \brief Perform join-project between t1 and t2 iterating through t1 and retrieving relevant columns from t2 using indexing. \c array \c removed_cols contains column indexes to be removed in ascending order and is terminated by a number greater than the highest column index of a join the two tables. This is to simplify the traversal of the array when building facts. \c tables_swapped value means that the resulting facts should contain facts from t2 first, instead of the default behavior that would concatenate the two facts as \c (t1,t2). \remark The function is called \c self_agnostic_join since, unlike the virtual method \c join, it is static and therefore allows to easily swap the roles of the two joined tables (the indexed and iterated one) in a way that is expected to give better performance. */ static void self_agnostic_join_project(const sparse_table & t1, const sparse_table & t2, unsigned joined_col_cnt, const unsigned * t1_joined_cols, const unsigned * t2_joined_cols, const unsigned * removed_cols, bool tables_swapped, sparse_table & result); /** If the fact at \c data (in table's native representation) is not in the table, add it and return true. Otherwise return false. */ bool add_fact(const char * data); bool add_reserve_content(); void garbage_collect(); sparse_table(sparse_table_plugin & p, const table_signature & sig, unsigned init_capacity=0); sparse_table(const sparse_table & t); ~sparse_table() override; public: void deallocate() override { get_plugin().recycle(this); } unsigned row_count() const { return m_data.entry_count(); } sparse_table_plugin & get_plugin() const { return static_cast(table_base::get_plugin()); } bool empty() const override { return row_count()==0; } void add_fact(const table_fact & f) override; bool contains_fact(const table_fact & f) const override; bool fetch_fact(table_fact & f) const override; void ensure_fact(const table_fact & f) override; void remove_fact(const table_element* fact) override; void reset() override; table_base * clone() const override; table_base::iterator begin() const override; table_base::iterator end() const override; unsigned get_size_estimate_rows() const override { return row_count(); } unsigned get_size_estimate_bytes() const override; bool knows_exact_size() const override { return true; } }; }; #endif /* DL_SPARSE_TABLE_H_ */ z3-z3-4.8.7/src/muz/rel/dl_table.cpp000066400000000000000000000222311356505360400170650ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_table.cpp Abstract: Author: Krystof Hoder (t-khoder) 2010-09-01. Revision History: --*/ #include "muz/base/dl_context.h" #include "muz/base/dl_util.h" #include "muz/rel/dl_table.h" #include "muz/rel/dl_relation_manager.h" namespace datalog { // ----------------------------------- // // hashtable_table // // ----------------------------------- table_base * hashtable_table_plugin::mk_empty(const table_signature & s) { SASSERT(can_handle_signature(s)); return alloc(hashtable_table, *this, s); } class hashtable_table_plugin::join_fn : public convenient_table_join_fn { unsigned m_joined_col_cnt; public: join_fn(const table_signature & t1_sig, const table_signature & t2_sig, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) : convenient_table_join_fn(t1_sig, t2_sig, col_cnt, cols1, cols2), m_joined_col_cnt(col_cnt) {} table_base * operator()(const table_base & t1, const table_base & t2) override { const hashtable_table & ht1 = static_cast(t1); const hashtable_table & ht2 = static_cast(t2); hashtable_table_plugin & plugin = ht1.get_plugin(); hashtable_table * res = static_cast(plugin.mk_empty(get_result_signature())); hashtable_table::storage::iterator els1it = ht1.m_data.begin(); hashtable_table::storage::iterator els1end = ht1.m_data.end(); hashtable_table::storage::iterator els2end = ht2.m_data.end(); table_fact acc; for(; els1it!=els1end; ++els1it) { const table_fact & row1 = *els1it; hashtable_table::storage::iterator els2it = ht2.m_data.begin(); for(; els2it!=els2end; ++els2it) { const table_fact & row2 = *els2it; bool match=true; for(unsigned i=0; im_data.insert(acc); } } return res; } }; table_join_fn * hashtable_table_plugin::mk_join_fn(const table_base & t1, const table_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { if(t1.get_kind()!=get_kind() || t2.get_kind()!=get_kind()) { return nullptr; } return alloc(join_fn, t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2); } class hashtable_table::our_iterator_core : public iterator_core { const hashtable_table & m_parent; storage::iterator m_inner; storage::iterator m_end; class our_row : public row_interface { const our_iterator_core & m_parent; public: our_row(const our_iterator_core & parent) : row_interface(parent.m_parent), m_parent(parent) {} void get_fact(table_fact & result) const override { result = *m_parent.m_inner; } table_element operator[](unsigned col) const override { return (*m_parent.m_inner)[col]; } }; our_row m_row_obj; public: our_iterator_core(const hashtable_table & t, bool finished) : m_parent(t), m_inner(finished ? t.m_data.end() : t.m_data.begin()), m_end(t.m_data.end()), m_row_obj(*this) {} bool is_finished() const override { return m_inner==m_end; } row_interface & operator*() override { SASSERT(!is_finished()); return m_row_obj; } void operator++() override { SASSERT(!is_finished()); ++m_inner; } }; table_base::iterator hashtable_table::begin() const { return mk_iterator(alloc(our_iterator_core, *this, false)); } table_base::iterator hashtable_table::end() const { return mk_iterator(alloc(our_iterator_core, *this, true)); } // ----------------------------------- // // bitvector_table // // ----------------------------------- bool bitvector_table_plugin::can_handle_signature(const table_signature & sig) { if(sig.functional_columns()!=0) { return false; } unsigned cols = sig.size(); unsigned shift = 0; for (unsigned i = 0; i < cols; ++i) { unsigned s = static_cast(sig[i]); if (s != sig[i] || !is_power_of_two(s)) { return false; } unsigned num_bits = 0; unsigned bit_pos = 1; for (num_bits = 1; num_bits < 32; ++num_bits) { if (bit_pos & s) { break; } bit_pos <<= 1; } shift += num_bits; if (shift >= 32) { return false; } } return true; } table_base * bitvector_table_plugin::mk_empty(const table_signature & s) { SASSERT(can_handle_signature(s)); return alloc(bitvector_table, *this, s); } class bitvector_table::bv_iterator : public iterator_core { bitvector_table const& m_bv; unsigned m_offset; class our_row : public caching_row_interface { const bv_iterator& m_parent; public: our_row(const bv_iterator & p) : caching_row_interface(p.m_bv), m_parent(p) {} void get_fact(table_fact& result) const override { if (result.size() < size()) { result.resize(size(), 0); } m_parent.m_bv.offset2fact(m_parent.m_offset, result); } }; our_row m_row_obj; public: bv_iterator(const bitvector_table& bv, bool end): m_bv(bv), m_offset(end?m_bv.m_bv.size():0), m_row_obj(*this) { if (!is_finished() && !m_bv.m_bv.get(m_offset)) { ++(*this); } } bool is_finished() const override { return m_offset == m_bv.m_bv.size(); } row_interface & operator*() override { SASSERT(!is_finished()); return m_row_obj; } void operator++() override { SASSERT(!is_finished()); ++m_offset; while (!is_finished() && !m_bv.m_bv.get(m_offset)) { ++m_offset; } m_row_obj.reset(); } }; bitvector_table::bitvector_table(bitvector_table_plugin & plugin, const table_signature & sig) : table_base(plugin, sig) { SASSERT(plugin.can_handle_signature(sig)); m_num_cols = sig.size(); unsigned shift = 0; for (unsigned i = 0; i < m_num_cols; ++i) { unsigned s = static_cast(sig[i]); if (s != sig[i] || !is_power_of_two(s)) { throw default_exception("bit-vector table is specialized to small domains that are powers of two"); } m_shift.push_back(shift); m_mask.push_back(s - 1); unsigned num_bits = 0; unsigned bit_pos = 1; for (num_bits = 1; num_bits < 32; ++num_bits) { if (bit_pos & s) { break; } bit_pos <<= 1; } shift += num_bits; if (shift >= 32) { throw default_exception("bit-vector table is specialized to small domains that are powers of two"); } m_bv.reserve(1 << shift); } } unsigned bitvector_table::fact2offset(const table_element* f) const { unsigned result = 0; for (unsigned i = 0; i < m_num_cols; ++i) { SASSERT(f[i]> m_shift[i]); } } void bitvector_table::add_fact(const table_fact & f) { m_bv.set(fact2offset(f.c_ptr())); } void bitvector_table::remove_fact(const table_element* fact) { m_bv.unset(fact2offset(fact)); } bool bitvector_table::contains_fact(const table_fact & f) const { return m_bv.get(fact2offset(f.c_ptr())); } table_base::iterator bitvector_table::begin() const { return mk_iterator(alloc(bv_iterator, *this, false)); } table_base::iterator bitvector_table::end() const { return mk_iterator(alloc(bv_iterator, *this, true)); } }; z3-z3-4.8.7/src/muz/rel/dl_table.h000066400000000000000000000076161356505360400165440ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_table.h Abstract: Author: Krystof Hoder (t-khoder) 2010-09-01. Revision History: --*/ #ifndef DL_TABLE_H_ #define DL_TABLE_H_ #include #include #include #include "ast/ast.h" #include "util/bit_vector.h" #include "util/buffer.h" #include "util/hashtable.h" #include "util/map.h" #include "util/ref_vector.h" #include "util/vector.h" #include "util/union_find.h" #include "muz/rel/dl_base.h" #include "muz/base/dl_util.h" #include "util/bit_vector.h" namespace datalog { class context; class variable_intersection; // ----------------------------------- // // hashtable_table // // ----------------------------------- class hashtable_table; class hashtable_table_plugin : public table_plugin { friend class hashtable_table; protected: class join_fn; public: typedef hashtable_table table; hashtable_table_plugin(relation_manager & manager) : table_plugin(symbol("hashtable"), manager) {} table_base * mk_empty(const table_signature & s) override; table_join_fn * mk_join_fn(const table_base & t1, const table_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) override; }; class hashtable_table : public table_base { friend class hashtable_table_plugin; friend class hashtable_table_plugin::join_fn; class our_iterator_core; typedef hashtable, vector_eq_proc > storage; storage m_data; hashtable_table(hashtable_table_plugin & plugin, const table_signature & sig) : table_base(plugin, sig) {} public: hashtable_table_plugin & get_plugin() const { return static_cast(table_base::get_plugin()); } void add_fact(const table_fact & f) override { m_data.insert(f); } void remove_fact(const table_element* fact) override { table_fact f(get_signature().size(), fact); m_data.remove(f); } bool contains_fact(const table_fact & f) const override { return m_data.contains(f); } iterator begin() const override; iterator end() const override; unsigned get_size_estimate_rows() const override { return m_data.size(); } unsigned get_size_estimate_bytes() const override { return m_data.size()*get_signature().size()*8; } bool knows_exact_size() const override { return true; } }; // ----------------------------------- // // bitvector_table // // ----------------------------------- class bitvector_table; class bitvector_table_plugin : public table_plugin { public: typedef bitvector_table table; bitvector_table_plugin(relation_manager & manager) : table_plugin(symbol("bitvector"), manager) {} bool can_handle_signature(const table_signature & s) override; table_base * mk_empty(const table_signature & s) override; }; class bitvector_table : public table_base { friend class bitvector_table_plugin; class bv_iterator; bit_vector m_bv; unsigned m_num_cols; unsigned_vector m_shift; unsigned_vector m_mask; unsigned fact2offset(const table_element* f) const; void offset2fact(unsigned offset, table_fact& f) const; bitvector_table(bitvector_table_plugin & plugin, const table_signature & sig); public: void add_fact(const table_fact & f) override; void remove_fact(const table_element* fact) override; bool contains_fact(const table_fact & f) const override; iterator begin() const override; iterator end() const override; }; }; #endif /* DL_TABLE_H_ */ z3-z3-4.8.7/src/muz/rel/dl_table_plugin.h000066400000000000000000000123441356505360400201140ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_table_plugin.h Abstract: Author: Krystof Hoder (t-khoder) 2010-09-23. Revision History: --*/ #ifndef DL_TABLE_PLUGIN_H_ #define DL_TABLE_PLUGIN_H_ #include "ast/ast.h" #include "util/map.h" #include "util/vector.h" #include"dl_table_ops.h" namespace datalog { /** Termplate class containing common infrastructure for relations and tables */ template struct tr_infrastructure { typedef typename Traits::base_object base_object; typedef typename Traits::signature signature; typedef typename Traits::element element; typedef typename Traits::fact fact; typedef typename Traits::kind kind; class base_fn { public: virtual ~base_fn() {} }; class join_fn : public base_fn { public: virtual base_object * operator()(const base_object & t1, const base_object & t2); }; class transformer_fn : public base_fn { public: virtual base_object * operator()(const base_object & t); }; class union_fn : public base_fn { public: virtual void operator()(base_object & tgt, const base_object & src, base_object * delta); }; class mutator_fn : public base_fn { public: virtual void operator()(base_object & t); }; class negation_filter_fn : public base_fn { public: virtual void operator()(base_object & t, const base_object & negated_obj); }; class plugin_object { const kind m_kind; protected: plugin_object(kind k) : m_kind(k) {} public: kind get_kind(); virtual base_object * mk_empty(const signature & s) = 0; virtual join_fn * mk_join_fn(const table_base & t1, const table_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { NOT_IMPLEMENTED_YET(); } virtual transformer_fn * mk_project_fn(const base_object & t, unsigned col_cnt, const unsigned * removed_cols) = 0 virtual transformer_fn * mk_rename_fn(const base_object & t, unsigned permutation_cycle_len, const unsigned * permutation_cycle) = 0; virtual union_fn * mk_union_fn(base_object & tgt, const base_object & src, base_object * delta) = 0; virtual mutator_fn * mk_filter_identical_fn(base_object & t, unsigned col_cnt, const unsigned * identical_cols) = 0; virtual mutator_fn * mk_filter_equal_fn(base_object & t, const element & value, unsigned col) = 0; virtual mutator_fn * mk_filter_interpreted_fn(base_object & t, app * condition) = 0; virtual negation_filter_fn * mk_filter_interpreted_fn(base_object & t, const base_object & negated_obj, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) = 0; }; class base_ancestor { const kind m_kind; protected: relation_manager & m_manager; signature m_signature; base_ancestor(kind k, relation_manager & m, const signature & s) : m_kind(k), m_manager(m), m_signature(s) {} public: virtual ~base_ancestor() {} kind get_kind() const { return m_kind; } relation_manager & get_manager() const { return m_manager; } const signature & get_signature() const { return m_signature; } virtual bool empty() const = 0; virtual void add_fact(const fact & f) = 0; virtual bool contains_fact(const fact & f) const = 0; /** \brief Return table that contains the same data as the current one. */ virtual base_object * clone() const; }; }; // ----------------------------------- // // relation_base // // ----------------------------------- class relation_base1; enum relation_kind { RK_UNKNOWN, RK_TABLE }; struct relation_traits { typedef relation_base1 base_object; typedef relation_signature signature; typedef app * element; typedef ptr_vector fact; typedef relation_kind kind; }; typedef tr_infrastructure relation_infrastructure; typedef relation_infrastructure::plugin_object relation_plugin_base; class relation_base1 : public relation_infrastructure::base_ancestor { }; // ----------------------------------- // // table_base // // ----------------------------------- class table_base1; struct table_traits { typedef table_base1 base_object; typedef table_signature signature; typedef unsigned element; typedef unsigned_vector fact; typedef table_kind kind; }; typedef tr_infrastructure table_infrastructure; typedef table_infrastructure::plugin_object table_plugin_base; class table_base1 : public table_infrastructure::base_ancestor { }; }; #endif /* DL_TABLE_PLUGIN_H_ */ z3-z3-4.8.7/src/muz/rel/dl_table_relation.cpp000066400000000000000000000471011356505360400207650ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_table_relation.cpp Abstract: Author: Krystof Hoder (t-khoder) 2010-09-14. Revision History: --*/ #include #include "muz/base/dl_context.h" #include "muz/rel/dl_relation_manager.h" #include "muz/rel/dl_table_relation.h" namespace datalog { // ----------------------------------- // // table_relation_plugin // // ----------------------------------- symbol table_relation_plugin::create_plugin_name(const table_plugin &p) { std::string name = std::string("tr_") + p.get_name().bare_str(); return symbol(name.c_str()); } bool table_relation_plugin::can_handle_signature(const relation_signature & s) { table_signature tsig; return get_manager().relation_signature_to_table(s, tsig) && m_table_plugin.can_handle_signature(tsig); } relation_base * table_relation_plugin::mk_empty(const relation_signature & s) { table_signature tsig; if (!get_manager().relation_signature_to_table(s, tsig)) { return nullptr; } table_base * t = m_table_plugin.mk_empty(tsig); return alloc(table_relation, *this, s, t); } relation_base * table_relation_plugin::mk_full_relation(const relation_signature & s, func_decl* p, family_id kind) { table_signature tsig; if(!get_manager().relation_signature_to_table(s, tsig)) { return nullptr; } table_base * t = m_table_plugin.mk_full(p, tsig, kind); return alloc(table_relation, *this, s, t); } /** The newly created object takes ownership of the \c t object. */ relation_base * table_relation_plugin::mk_from_table(const relation_signature & s, table_base * t) { if (&t->get_plugin() == &m_table_plugin) return alloc(table_relation, *this, s, t); table_relation_plugin& other = t->get_manager().get_table_relation_plugin(t->get_plugin()); return alloc(table_relation, other, s, t); } class table_relation_plugin::tr_join_project_fn : public convenient_relation_join_project_fn { scoped_ptr m_tfun; public: tr_join_project_fn(const relation_signature & s1, const relation_signature & s2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols, table_join_fn * tfun) : convenient_relation_join_project_fn(s1, s2, col_cnt, cols1, cols2, removed_col_cnt, removed_cols), m_tfun(tfun) {} relation_base * operator()(const relation_base & t1, const relation_base & t2) override { SASSERT(t1.from_table()); SASSERT(t2.from_table()); table_relation_plugin & plugin = static_cast(t1.get_plugin()); const table_relation & tr1 = static_cast(t1); const table_relation & tr2 = static_cast(t2); table_base * tres = (*m_tfun)(tr1.get_table(), tr2.get_table()); TRACE("dl_table_relation", tout << "# join => "; tres->display(tout);); if(&tres->get_plugin()!=&plugin.m_table_plugin) { IF_VERBOSE(1, verbose_stream() << "new type returned\n";); //Operation returned a table of different type than the one which is associated with //this plugin. We need to get a correct table_relation_plugin and create the relation //using it. return plugin.get_manager().get_table_relation_plugin(tres->get_plugin()) .mk_from_table(get_result_signature(), tres); } return plugin.mk_from_table(get_result_signature(), tres); } }; relation_join_fn * table_relation_plugin::mk_join_fn(const relation_base & r1, const relation_base & r2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { if(!r1.from_table() || !r2.from_table()) { return nullptr; } const table_relation & tr1 = static_cast(r1); const table_relation & tr2 = static_cast(r2); table_join_fn * tfun = get_manager().mk_join_fn(tr1.get_table(), tr2.get_table(), col_cnt, cols1, cols2); if(!tfun) { return nullptr; } return alloc(tr_join_project_fn, r1.get_signature(), r2.get_signature(), col_cnt, cols1, cols2, 0, static_cast(nullptr), tfun); } relation_join_fn * table_relation_plugin::mk_join_project_fn(const relation_base & r1, const relation_base & r2, unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols) { if(!r1.from_table() || !r2.from_table()) { return nullptr; } const table_relation & tr1 = static_cast(r1); const table_relation & tr2 = static_cast(r2); table_join_fn * tfun = get_manager().mk_join_project_fn(tr1.get_table(), tr2.get_table(), joined_col_cnt, cols1, cols2, removed_col_cnt, removed_cols); SASSERT(tfun); return alloc(tr_join_project_fn, r1.get_signature(), r2.get_signature(), joined_col_cnt, cols1, cols2, removed_col_cnt, removed_cols, tfun); } class table_relation_plugin::tr_transformer_fn : public convenient_relation_transformer_fn { scoped_ptr m_tfun; public: tr_transformer_fn(const relation_signature & rsig, table_transformer_fn * tfun) : m_tfun(tfun) { get_result_signature() = rsig; } relation_base * operator()(const relation_base & t) override { SASSERT(t.from_table()); table_relation_plugin & plugin = static_cast(t.get_plugin()); const table_relation & tr = static_cast(t); table_base * tres = (*m_tfun)(tr.get_table()); TRACE("dl_table_relation", tout << "# transform => "; tres->display(tout);); if(&tres->get_plugin()!=&plugin.m_table_plugin) { //Transformation returned a table of different type than the one which is associated with this plugin. //We need to get a correct table_relation_plugin and create the relation using it. return plugin.get_manager().get_table_relation_plugin(tres->get_plugin()) .mk_from_table(get_result_signature(), tres); } return plugin.mk_from_table(get_result_signature(), tres); } }; relation_transformer_fn * table_relation_plugin::mk_project_fn(const relation_base & t, unsigned col_cnt, const unsigned * removed_cols) { if(!t.from_table()) { return nullptr; } const table_relation & tr = static_cast(t); table_transformer_fn * tfun = get_manager().mk_project_fn(tr.get_table(), col_cnt, removed_cols); SASSERT(tfun); relation_signature sig; relation_signature::from_project(t.get_signature(), col_cnt, removed_cols, sig); return alloc(tr_transformer_fn, sig, tfun); } relation_transformer_fn * table_relation_plugin::mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, const unsigned * permutation_cycle) { if(!t.from_table()) { return nullptr; } const table_relation & tr = static_cast(t); table_transformer_fn * tfun = get_manager().mk_rename_fn(tr.get_table(), permutation_cycle_len, permutation_cycle); SASSERT(tfun); relation_signature sig; relation_signature::from_rename(t.get_signature(), permutation_cycle_len, permutation_cycle, sig); return alloc(tr_transformer_fn, sig, tfun); } relation_transformer_fn * table_relation_plugin::mk_permutation_rename_fn(const relation_base & t, const unsigned * permutation) { if(!t.from_table()) { return nullptr; } const table_relation & tr = static_cast(t); table_transformer_fn * tfun = get_manager().mk_permutation_rename_fn(tr.get_table(), permutation); SASSERT(tfun); relation_signature sig; relation_signature::from_permutation_rename(t.get_signature(), permutation, sig); return alloc(tr_transformer_fn, sig, tfun); } relation_transformer_fn * table_relation_plugin::mk_select_equal_and_project_fn(const relation_base & t, const relation_element & value, unsigned col) { if(!t.from_table()) { return nullptr; } const table_relation & tr = static_cast(t); table_element tvalue; get_manager().relation_to_table(tr.get_signature()[col], value, tvalue); table_transformer_fn * tfun = get_manager().mk_select_equal_and_project_fn(tr.get_table(), tvalue, col); SASSERT(tfun); relation_signature res_sig; relation_signature::from_project(t.get_signature(), 1, &col, res_sig); return alloc(tr_transformer_fn, res_sig, tfun); } /** Union functor that can unite table relation into any other relation (using any delta relation) by iterating through the table and calling \c add_fact of the target relation. */ class table_relation_plugin::universal_target_union_fn : public relation_union_fn { void operator()(relation_base & tgt, const relation_base & src, relation_base * delta) override { SASSERT(src.from_table()); const table_relation & tr_src = static_cast(src); relation_manager & rmgr = tr_src.get_manager(); const relation_signature & sig = tr_src.get_signature(); SASSERT(tgt.get_signature()==sig); SASSERT(!delta || delta->get_signature()==sig); table_base::iterator it = tr_src.get_table().begin(); table_base::iterator end = tr_src.get_table().end(); table_fact tfact; relation_fact rfact(rmgr.get_context()); for (; it != end; ++it) { it->get_fact(tfact); rmgr.table_fact_to_relation(sig, tfact, rfact); if(delta) { if(!tgt.contains_fact(rfact)) { tgt.add_new_fact(rfact); delta->add_fact(rfact); } } else { tgt.add_fact(rfact); } } TRACE("dl_table_relation", tout << "# universal union => "; tgt.display(tout);); } }; class table_relation_plugin::tr_union_fn : public relation_union_fn { scoped_ptr m_tfun; public: tr_union_fn(table_union_fn * tfun) : m_tfun(tfun) {} void operator()(relation_base & tgt, const relation_base & src, relation_base * delta) override { SASSERT(tgt.from_table()); SASSERT(src.from_table()); SASSERT(!delta || delta->from_table()); table_relation & tr_tgt = static_cast(tgt); const table_relation & tr_src = static_cast(src); table_relation * tr_delta = static_cast(delta); (*m_tfun)(tr_tgt.get_table(), tr_src.get_table(), tr_delta ? &tr_delta->get_table() : nullptr); TRACE("dl_table_relation", tout << "# union => "; tr_tgt.get_table().display(tout);); } }; relation_union_fn * table_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta) { if(!src.from_table()) { return nullptr; } if(!tgt.from_table() || (delta && !delta->from_table())) { return alloc(universal_target_union_fn); } const table_relation & tr_tgt = static_cast(tgt); const table_relation & tr_src = static_cast(src); const table_relation * tr_delta = static_cast(delta); table_union_fn * tfun = get_manager().mk_union_fn(tr_tgt.get_table(), tr_src.get_table(), tr_delta ? &tr_delta->get_table() : nullptr); SASSERT(tfun); return alloc(tr_union_fn, tfun); } class table_relation_plugin::tr_mutator_fn : public relation_mutator_fn { scoped_ptr m_tfun; public: tr_mutator_fn(table_mutator_fn * tfun) : m_tfun(tfun) {} void operator()(relation_base & r) override { SASSERT(r.from_table()); table_relation & tr = static_cast(r); (*m_tfun)(tr.get_table()); TRACE("dl_table_relation", tout << "# mutator => "; tr.get_table().display(tout);); } }; relation_mutator_fn * table_relation_plugin::mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, const unsigned * identical_cols) { if(!t.from_table()) { return nullptr; } const table_relation & tr = static_cast(t); table_mutator_fn * tfun = get_manager().mk_filter_identical_fn(tr.get_table(), col_cnt, identical_cols); SASSERT(tfun); return alloc(tr_mutator_fn, tfun); } relation_mutator_fn * table_relation_plugin::mk_filter_equal_fn(const relation_base & t, const relation_element & value, unsigned col) { if(!t.from_table()) { return nullptr; } const table_relation & tr = static_cast(t); table_element tvalue; get_manager().relation_to_table(tr.get_signature()[col], value, tvalue); table_mutator_fn * tfun = get_manager().mk_filter_equal_fn(tr.get_table(), tvalue, col); SASSERT(tfun); return alloc(tr_mutator_fn, tfun); } relation_mutator_fn * table_relation_plugin::mk_filter_interpreted_fn(const relation_base & t, app * condition) { bool condition_needs_transforming = false; if(!t.from_table() || condition_needs_transforming) { return nullptr; } const table_relation & tr = static_cast(t); table_mutator_fn * tfun = get_manager().mk_filter_interpreted_fn(tr.get_table(), condition); SASSERT(tfun); return alloc(tr_mutator_fn, tfun); } relation_transformer_fn * table_relation_plugin::mk_filter_interpreted_and_project_fn(const relation_base & t, app * condition, unsigned removed_col_cnt, const unsigned * removed_cols) { if (!t.from_table()) return nullptr; const table_relation & tr = static_cast(t); table_transformer_fn * tfun = get_manager().mk_filter_interpreted_and_project_fn(tr.get_table(), condition, removed_col_cnt, removed_cols); SASSERT(tfun); relation_signature sig; relation_signature::from_project(t.get_signature(), removed_col_cnt, removed_cols, sig); return alloc(tr_transformer_fn, sig, tfun); } class table_relation_plugin::tr_intersection_filter_fn : public relation_intersection_filter_fn { scoped_ptr m_tfun; public: tr_intersection_filter_fn(table_intersection_filter_fn * tfun) : m_tfun(tfun) {} void operator()(relation_base & r, const relation_base & src) override { SASSERT(r.from_table()); SASSERT(src.from_table()); table_relation & tr = static_cast(r); const table_relation & tr_src = static_cast(src); (*m_tfun)(tr.get_table(), tr_src.get_table()); TRACE("dl_table_relation", tout << "# negation_filter => "; tr.get_table().display(tout);); } }; relation_intersection_filter_fn * table_relation_plugin::mk_filter_by_intersection_fn(const relation_base & r, const relation_base & src, unsigned joined_col_cnt, const unsigned * r_cols, const unsigned * src_cols) { if(!r.from_table() || !src.from_table()) { return nullptr; } const table_relation & tr = static_cast(r); const table_relation & tr_neg = static_cast(src); table_intersection_filter_fn * tfun = get_manager().mk_filter_by_intersection_fn(tr.get_table(), tr_neg.get_table(), joined_col_cnt, r_cols, src_cols); if(!tfun) { return nullptr; } return alloc(tr_intersection_filter_fn, tfun); } relation_intersection_filter_fn * table_relation_plugin::mk_filter_by_negation_fn(const relation_base & r, const relation_base & negated_rel, unsigned joined_col_cnt, const unsigned * r_cols, const unsigned * negated_cols) { if(!r.from_table() || !negated_rel.from_table()) { return nullptr; } const table_relation & tr = static_cast(r); const table_relation & tr_neg = static_cast(negated_rel); table_intersection_filter_fn * tfun = get_manager().mk_filter_by_negation_fn(tr.get_table(), tr_neg.get_table(), joined_col_cnt, r_cols, negated_cols); SASSERT(tfun); return alloc(tr_intersection_filter_fn, tfun); } // ----------------------------------- // // table_relation // // ----------------------------------- void table_relation::add_table_fact(const table_fact & f) { get_table().add_fact(f); } void table_relation::add_fact(const relation_fact & f) { SASSERT(f.size()==get_signature().size()); table_fact vals; get_manager().relation_fact_to_table(get_signature(), f, vals); get_table().add_fact(vals); TRACE("dl_table_relation", tout << "# add fact => "; get_table().display(tout);); } bool table_relation::contains_fact(const relation_fact & f) const { table_fact vals; get_manager().relation_fact_to_table(get_signature(), f, vals); return get_table().contains_fact(vals); } relation_base * table_relation::clone() const { table_base * tres = get_table().clone(); return get_plugin().mk_from_table(get_signature(), tres); } relation_base * table_relation::complement(func_decl* p) const { table_base * tres = get_table().complement(p); return get_plugin().mk_from_table(get_signature(), tres); } void table_relation::display_tuples(func_decl & pred, std::ostream & out) const { context & ctx = get_manager().get_context(); unsigned arity = pred.get_arity(); out << "Tuples in " << pred.get_name() << ": \n"; table_base::iterator it = get_table().begin(); table_base::iterator end = get_table().end(); table_fact fact; for (; it != end; ++it) { it->get_fact(fact); out << "\t("; for(unsigned i=0;i Author: Krystof Hoder (t-khoder) 2010-09-24. Revision History: --*/ #ifndef DL_TABLE_RELATION_H_ #define DL_TABLE_RELATION_H_ #include "muz/rel/dl_base.h" #include "muz/base/dl_util.h" namespace datalog { class table_relation; class table_relation_plugin : public relation_plugin { friend class table_relation; class tr_join_project_fn; class tr_transformer_fn; class universal_target_union_fn; class tr_union_fn; class tr_mutator_fn; class tr_intersection_filter_fn; table_plugin & m_table_plugin; static symbol create_plugin_name(const table_plugin & p); public: table_relation_plugin(table_plugin & tp, relation_manager & manager) : relation_plugin(create_plugin_name(tp), manager, ST_TABLE_RELATION), m_table_plugin(tp) {} table_plugin & get_table_plugin() { return m_table_plugin; } bool can_handle_signature(const relation_signature & s) override; relation_base * mk_empty(const relation_signature & s) override; virtual relation_base * mk_full_relation(const relation_signature & s, func_decl* p, family_id kind); relation_base * mk_from_table(const relation_signature & s, table_base * t); protected: relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) override; relation_join_fn * mk_join_project_fn(const relation_base & t1, const relation_base & t2, unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols) override; relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, const unsigned * removed_cols) override; relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, const unsigned * permutation_cycle) override; relation_transformer_fn * mk_permutation_rename_fn(const relation_base & t, const unsigned * permutation) override; relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta) override; relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, const unsigned * identical_cols) override; relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, unsigned col) override; relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition) override; relation_transformer_fn * mk_filter_interpreted_and_project_fn(const relation_base & t, app * condition, unsigned removed_col_cnt, const unsigned * removed_cols) override; relation_intersection_filter_fn * mk_filter_by_intersection_fn(const relation_base & t, const relation_base & src, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * src_cols) override; relation_intersection_filter_fn * mk_filter_by_negation_fn(const relation_base & t, const relation_base & negated_obj, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) override; relation_transformer_fn * mk_select_equal_and_project_fn(const relation_base & t, const relation_element & value, unsigned col) override; }; class table_relation : public relation_base { friend class table_relation_plugin; friend class table_relation_plugin::tr_join_project_fn; friend class table_relation_plugin::tr_transformer_fn; scoped_rel m_table; /** \brief Create a \c table_relation object. The newly created object takes ownership of the \c table object. */ table_relation(table_relation_plugin & p, const relation_signature & s, table_base * table) : relation_base(p, s), m_table(table) { SASSERT(s.size()==table->get_signature().size()); } public: table_relation_plugin & get_plugin() const { return static_cast(relation_base::get_plugin()); } table_base & get_table() { return *m_table; } const table_base & get_table() const { return *m_table; } bool empty() const override { return m_table->empty(); } void add_table_fact(const table_fact & f); void add_fact(const relation_fact & f) override; bool contains_fact(const relation_fact & f) const override; relation_base * clone() const override; relation_base * complement(func_decl* p) const override; void to_formula(expr_ref& fml) const override { get_table().to_formula(get_signature(), fml); } void display(std::ostream & out) const override { get_table().display(out); } void display_tuples(func_decl & pred, std::ostream & out) const override; unsigned get_size_estimate_rows() const override { return m_table->get_size_estimate_rows(); } unsigned get_size_estimate_bytes() const override { return m_table->get_size_estimate_bytes(); } bool knows_exact_size() const override { return m_table->knows_exact_size(); } }; }; #endif /* DL_TABLE_RELATION_H_ */ z3-z3-4.8.7/src/muz/rel/dl_vector_relation.h000066400000000000000000000321661356505360400206520ustar00rootroot00000000000000/*++ Copyright (c) 2010 Microsoft Corporation Module Name: dl_vector_relation.h Abstract: Basic relation with equivalences. Author: Nikolaj Bjorner (nbjorner) 2010-2-11 Revision History: --*/ #ifndef DL_VECTOR_RELATION_H_ #define DL_VECTOR_RELATION_H_ #include "ast/ast_pp.h" #include "muz/base/dl_context.h" #include "util/union_find.h" namespace datalog { typedef std::pair u_pair; template class vector_relation_helper { public: static void mk_project_t(T& t, unsigned_vector const& renaming) {} }; template > class vector_relation : public relation_base { protected: T m_default; vector* m_elems; bool m_empty; union_find_default_ctx m_ctx; union_find<>* m_eqs; public: vector_relation(relation_plugin& p, relation_signature const& s, bool is_empty, T const& t = T()): relation_base(p, s), m_default(t), m_elems(alloc(vector)), m_empty(is_empty), m_eqs(alloc(union_find<>, m_ctx)) { m_elems->resize(s.size(), t); for (unsigned i = 0; i < s.size(); ++i) { m_eqs->mk_var(); } } ~vector_relation() override { dealloc(m_eqs); dealloc(m_elems); } void swap(relation_base& other) override { vector_relation& o = dynamic_cast(other); if (&o == this) return; std::swap(o.m_eqs, m_eqs); std::swap(o.m_empty, m_empty); std::swap(o.m_elems, m_elems); } void copy(vector_relation const& other) { SASSERT(get_signature() == other.get_signature()); if (other.empty()) { set_empty(); return; } m_empty = false; for (unsigned i = 0; i < m_elems->size(); ++i) { (*this)[i] = other[i]; SASSERT(find(i) == i); } for (unsigned i = 0; i < m_elems->size(); ++i) { merge(i, find(i)); } } bool empty() const override { return m_empty; } T& operator[](unsigned i) { return (*m_elems)[find(i)]; } T const& operator[](unsigned i) const { return (*m_elems)[find(i)]; } virtual void display_index(unsigned i, T const& t, std::ostream& out) const = 0; void display(std::ostream & out) const override { if (empty()) { out << "empty\n"; return; } for (unsigned i = 0; i < m_elems->size(); ++i) { if (i == find(i)) { display_index(i, (*m_elems)[i], out); } else { out << i << " = " << find(i) << " "; } } out << "\n"; } bool is_subset_of(vector_relation const& other) const { if (empty()) return true; if (other.empty()) return false; for (unsigned i = 0; i < get_signature().size(); ++i) { if (!is_subset_of((*this)[i], other[i])) { return false; } } return true; } void set_empty() { unsigned sz = m_elems->size(); m_empty = true; m_elems->reset(); m_elems->resize(sz, m_default); dealloc(m_eqs); m_eqs = alloc(union_find<>,m_ctx); for (unsigned i = 0; i < sz; ++i) { m_eqs->mk_var(); } } virtual T mk_intersect(T const& t1, T const& t2, bool& is_empty) const = 0; virtual T mk_widen(T const& t1, T const& t2) const = 0; virtual T mk_unite(T const& t1, T const& t2) const = 0; virtual bool is_subset_of(T const& t1, T const& t2) const = 0; virtual bool is_full(T const& t) const = 0; virtual bool is_empty(unsigned i, T const& t) const = 0; virtual void mk_rename_elem(T& t, unsigned col_cnt, unsigned const* cycle) = 0; virtual T mk_eq(union_find<> const& old_eqs, union_find<> const& neq_eqs, T const& t) const { return t; } void equate(unsigned i, unsigned j) { SASSERT(i < get_signature().size()); SASSERT(j < get_signature().size()); if (!empty() && find(i) != find(j)) { bool isempty; T r = mk_intersect((*this)[i], (*this)[j], isempty); if (isempty || is_empty(find(i),r)) { m_empty = true; } else { merge(i, j); (*this)[i] = r; } } } bool is_full() const { for (unsigned i = 0; i < m_elems->size(); ++i) { if (!is_full((*this)[i])) { return false; } } return true; } void mk_join(vector_relation const& r1, vector_relation const& r2, unsigned num_cols, unsigned const* cols1, unsigned const* cols2) { SASSERT(is_full()); bool is_empty = r1.empty() || r2.empty(); if (is_empty) { m_empty = true; return; } unsigned sz1 = r1.get_signature().size(); unsigned sz2 = r2.get_signature().size(); for (unsigned i = 0; i < sz1; ++i) { (*this)[i] = r1[i]; } for (unsigned i = 0; i < sz2; ++i) { (*this)[sz1+i] = r2[i]; } for (unsigned i = 0; i < num_cols; ++i) { unsigned col1 = cols1[i]; unsigned col2 = cols2[i]; equate(col1, sz1 + col2); } TRACE("dl_relation", r1.display(tout << "r1:\n"); r2.display(tout << "r2:\n"); display(tout << "dst:\n"); ); } void mk_project(vector_relation const& r, unsigned col_cnt, unsigned const* removed_cols) { SASSERT(is_full()); unsigned_vector classRep, repNode; unsigned result_size = get_signature().size(); unsigned input_size = r.get_signature().size(); repNode.resize(input_size, UINT_MAX); // initialize vector entries and set class representatives. for (unsigned i = 0, j = 0, c = 0; i < input_size; ++i) { if (c < col_cnt && removed_cols[c] == i) { ++c; } else { (*this)[j] = r[i]; classRep.push_back(r.find(i)); ++j; } } // merge remaining equivalence classes. for (unsigned i = 0; i < result_size; ++i) { unsigned rep = classRep[i]; if (repNode[rep] == UINT_MAX) { repNode[rep] = i; } else { merge(repNode[rep], i); } } // rename columns in image of vector relation. unsigned_vector renaming; for (unsigned i = 0, j = 0, c = 0; i < input_size; ++i) { if (c < col_cnt && removed_cols[c] == i) { renaming.push_back(UINT_MAX); ++c; } else { renaming.push_back(find(j)); ++j; } } for (unsigned k = 0; k < result_size; ++k) { Helper::mk_project_t((*this)[k], renaming); } TRACE("dl_relation", ast_manager& m = r.get_plugin().get_ast_manager(); tout << "Signature: "; for (unsigned i = 0; i < r.get_signature().size(); ++i) { tout << mk_pp(r.get_signature()[i], m) << " "; } tout << "Remove: "; for (unsigned i = 0; i < col_cnt; ++i) { tout << removed_cols[i] << " "; } tout << "\n"; r.display(tout); tout << " --> \n"; display(tout);); } void mk_rename(vector_relation const& r, unsigned col_cnt, unsigned const* cycle) { unsigned col1, col2; SASSERT(is_full()); // roundabout way of creating permuted relation. unsigned_vector classRep, repNode; for (unsigned i = 0; i < r.m_elems->size(); ++i) { classRep.push_back(r.find(i)); repNode.push_back(UINT_MAX); (*this)[i] = r[i]; } for (unsigned i = 0; i + 1 < col_cnt; ++i) { col1 = cycle[i]; col2 = cycle[i+1]; (*this)[col2] = (*r.m_elems)[col1]; classRep[col2] = r.find(col1); } col1 = cycle[col_cnt-1]; col2 = cycle[0]; (*this)[col2] = (*r.m_elems)[col1]; classRep[col2] = r.find(col1); for (unsigned i = 0; i < r.m_elems->size(); ++i) { unsigned rep = classRep[i]; if (repNode[rep] == UINT_MAX) { repNode[rep] = i; } else { merge(repNode[rep], i); } } for (unsigned i = 0; i < r.m_elems->size(); ++i) { mk_rename_elem((*m_elems)[i], col_cnt, cycle); } TRACE("dl_relation", ast_manager& m = r.get_plugin().get_ast_manager(); tout << "cycle: "; for (unsigned i = 0; i < col_cnt; ++i) { tout << cycle[i] << " "; } tout << "\nold_sig: "; for (unsigned i = 0; i < r.get_signature().size(); ++i) { tout << mk_pp(r.get_signature()[i], m) << " "; } tout << "\nnew_sig: "; for (unsigned i = 0; i < get_signature().size(); ++i) { tout << mk_pp(get_signature()[i], m) << " "; } tout << "\n"; r.display(tout << "src:\n"); ); } void mk_union(vector_relation const& src, vector_relation* delta, bool is_widen) { TRACE("dl_relation", display(tout << "dst:\n"); src.display(tout << "src:\n");); if (src.empty()) { if (delta) { delta->copy(src); } return; } if (empty()) { copy(src); if (delta) { delta->copy(src); } return; } // find coarsest equivalence class containing joint equalities union_find<>* uf = alloc(union_find<>, m_ctx); unsigned size = get_signature().size(); map, default_eq > mp; bool change = false; bit_vector finds; finds.resize(size, false); for (unsigned i = 0; i < size; ++i) { uf->mk_var(); unsigned w; u_pair p(std::make_pair(find(i), src.find(i))); if (mp.find(p, w)) { uf->merge(i, w); } else { mp.insert(p, i); // detect change if (finds.get(find(i))) { change = true; } else { finds.set(find(i), true); } } } vector* elems = alloc(vector); for (unsigned i = 0; i < size; ++i) { T t1 = mk_eq(*m_eqs, *uf, (*this)[i]); T t2 = mk_eq(*src.m_eqs, *uf, src[i]); if (is_widen) { elems->push_back(mk_widen(t1, t2)); } else { elems->push_back(mk_unite(t1, t2)); } TRACE("dl_relation", tout << t1 << " u " << t2 << " = " << elems->back() << "\n";); change = delta && (change || !((*elems)[i] == (*this)[i])); } dealloc(m_eqs); dealloc(m_elems); m_eqs = uf; m_elems = elems; if (delta && change) { delta->copy(*this); } TRACE("dl_relation", display(tout << "dst':\n");); } unsigned find(unsigned i) const { return m_eqs->find(i); } void merge(unsigned i, unsigned j) { m_eqs->merge(i, j); } }; }; #endif z3-z3-4.8.7/src/muz/rel/doc.cpp000066400000000000000000000522711356505360400160730ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: doc.cpp Abstract: difference of cubes. Author: Nuno Lopes (a-nlopes) 2013-03-01 Nikolaj Bjorner (nbjorner) 2014-09-15 Revision History: Based on ternary_diff_bitvector by Nuno Lopes. --*/ #include "muz/rel/doc.h" #include "smt/smt_kernel.h" #include "ast/rewriter/expr_safe_replace.h" #include "smt/params/smt_params.h" #include "ast/ast_util.h" #include "ast/ast_pp.h" doc_manager::doc_manager(unsigned n): m(n), m_alloc("doc") { m_full = m.allocateX(); } doc_manager::~doc_manager() { m.deallocate(m_full); } doc* doc_manager::allocate() { return allocate(m.allocate()); } doc* doc_manager::allocate1() { return allocate(m.allocate1()); } doc* doc_manager::allocate0() { return allocate(m.allocate0()); } doc* doc_manager::allocateX() { return allocate(m.allocateX()); } doc* doc_manager::allocate(doc const& src) { doc* r = allocate(m.allocate(src.pos())); for (unsigned i = 0; i < src.neg().size(); ++i) { r->neg().push_back(m.allocate(src.neg()[i])); } return r; } doc* doc_manager::allocate(tbv* t) { SASSERT(t); void* mm = m_alloc.allocate(sizeof(doc)); return new (mm) doc(t); } doc* doc_manager::allocate(tbv const& src) { return allocate(m.allocate(src)); } doc* doc_manager::allocate(uint64_t n) { return allocate(m.allocate(n)); } doc* doc_manager::allocate(rational const& r) { return allocate(m.allocate(r)); } doc* doc_manager::allocate(uint64_t n, unsigned hi, unsigned lo) { return allocate(m.allocate(n, hi, lo)); } doc* doc_manager::allocate(doc const& src, unsigned const* permutation) { doc* r = allocate(m.allocate(src.pos(), permutation)); for (unsigned i = 0; i < src.neg().size(); ++i) { r->neg().push_back(m.allocate(src.neg()[i], permutation)); } return r; } void doc_manager::deallocate(doc* src) { if (!src) return; m.deallocate(&src->pos()); src->neg().reset(m); src->~doc(); m_alloc.deallocate(sizeof(doc), src); } void doc_manager::copy(doc& dst, doc const& src) { m.copy(dst.pos(), src.pos()); dst.neg().reset(m); for (unsigned i = 0; i < src.neg().size(); ++i) { dst.neg().push_back(m.allocate(src.neg()[i])); } } doc& doc_manager::fill0(doc& src) { src.neg().reset(m); m.fill0(src.pos()); return src; } doc& doc_manager::fill1(doc& src) { src.neg().reset(m); m.fill1(src.pos()); return src; } doc& doc_manager::fillX(doc& src) { src.neg().reset(m); m.fillX(src.pos()); return src; } unsigned doc_manager::get_size_estimate_bytes(const doc& d) const { return m.get_size_estimate_bytes(d.pos()) + d.neg().get_size_estimate_bytes(m) + sizeof(doc); } bool doc_manager::set_and(doc& dst, doc const& src) { // (A \ B) & (C \ D) = (A & C) \ (B u D) if (!m.set_and(dst.pos(), src.pos())) return false; dst.neg().intersect(m, dst.pos()); tbv_ref t(m); for (unsigned i = 0; i < src.neg().size(); ++i) { t = m.allocate(src.neg()[i]); if (m.set_and(*t, dst.pos())) { dst.neg().insert(m, t.detach()); } } return fold_neg(dst); } bool doc_manager::set_and(doc& dst, tbv const& src) { // (A \ B) & C = (A & C) \ (B & C) if (!m.set_and(dst.pos(), src)) return false; dst.neg().intersect(m, src); return fold_neg(dst); } bool doc_manager::well_formed(doc const& d) const { if (!m.is_well_formed(d.pos())) return false; for (unsigned i = 0; i < d.neg().size(); ++i) { if (!m.is_well_formed(d.neg()[i])) return false; if (!m.contains(d.pos(), d.neg()[i])) return false; } return true; } bool doc_manager::fold_neg(doc& dst) { start_over: for (unsigned i = 0; i < dst.neg().size(); ++i) { if (m.contains(dst.neg()[i], dst.pos())) return false; unsigned index; unsigned count = diff_by_012(dst.pos(), dst.neg()[i], index); if (count != 2) { if (count == 0) { return false; } else if (count == 3) { dst.neg().erase(tbvm(), i); --i; } else { // count == 1: m.set(dst.pos(), index, neg(dst.neg()[i][index])); dst.neg().intersect(tbvm(), dst.pos()); goto start_over; } } } SASSERT(well_formed(dst)); return true; } unsigned doc_manager::diff_by_012(tbv const& pos, tbv const& neg, unsigned& index) { unsigned n = num_tbits(); unsigned count = 0; for (unsigned i = 0; i < n; ++i) { tbit b1 = pos[i]; tbit b2 = neg[i]; SASSERT(b1 != BIT_z && b2 != BIT_z); if (b1 != b2) { if (count == 1) return 2; if (b1 == BIT_x) { index = i; count = 1; } else if (b2 != BIT_x) { return 3; } } } return count; } void doc_manager::set(doc& d, unsigned idx, tbit value) { m.set(d.pos(), idx, value); for (unsigned i = 0; i < d.neg().size(); ++i) { tbit b = d.neg()[i][idx]; if (b != BIT_x && value != BIT_x && value != b) { d.neg().erase(tbvm(), i); --i; } else { m.set(d.neg()[i], idx, value); } } } // // merge range from [lo:lo+length-1] with each index in equivalence class. // under assumption of equalities and columns that are discarded. // bool doc_manager::merge( doc& d, unsigned lo, unsigned length, subset_ints const& equalities, bit_vector const& discard_cols) { for (unsigned i = 0; i < length; ++i) { unsigned idx = lo + i; if (!merge(d, idx, equalities, discard_cols)) return false; } return true; } bool doc_manager::merge(doc& d, unsigned idx, subset_ints const& equalities, bit_vector const& discard_cols) { unsigned root = equalities.find(idx); idx = root; unsigned num_x = 0; unsigned root1 = root; tbit value = BIT_x; do { switch (d[idx]) { case BIT_0: if (value == BIT_1) return false; value = BIT_0; break; case BIT_1: if (value == BIT_0) return false; value = BIT_1; break; case BIT_x: ++num_x; if (!discard_cols.get(idx)) { root1 = idx; } break; default: UNREACHABLE(); break; } idx = equalities.next(idx); } while (idx != root); TRACE("doc", tout << "num_x: " << num_x << " value: " << value << "\n";); if (num_x == 0) { // nothing to do. } else if (value != BIT_x) { do { if (d[idx] == BIT_x) { set(d, idx, value); } idx = equalities.next(idx); } while (idx != root); } else { bool all_x = true; if (!d.neg().is_empty()) { idx = root; do { for (unsigned i = 0; all_x && i < d.neg().size(); ++i) { all_x = (BIT_x == d.neg()[i][idx]); } idx = equalities.next(idx); } while (idx != root && all_x); } idx = root; do { if ((!discard_cols.get(idx) || !all_x) && idx != root1) { tbv* t = m.allocate(d.pos()); m.set(*t, idx, BIT_0); m.set(*t, root1, BIT_1); d.neg().insert(tbvm(), t); t = m.allocate(d.pos()); m.set(*t, idx, BIT_1); m.set(*t, root1, BIT_0); d.neg().insert(tbvm(), t); } idx = equalities.next(idx); } while (idx != root); } return true; } bool doc_manager::intersect(doc const& A, doc const& B, doc& result) { copy(result, A); return set_and(result, B); } // // 1. If n = 0,1: can project directly. // 2. If tbv_i uses X in all positions with vars or constant where tbv is constant: can project directly. // 3. Perform resolution on remaining tbv_i // // tbv & ~tbv1 & ~tbv2 & .. & ~tbv_n // Semantics of ~tbv1 is that it is a clause of literals. // indices where BIT_1 is set are negative. // indices where BIT_0 is set are positive. // doc* doc_manager::project(doc_manager& dstm, bit_vector const& to_delete, doc const& src) { tbv_manager& dstt = dstm.m; tbv_ref t(dstt); t = dstt.project(to_delete, src.pos()); doc* r = dstm.allocate(t.detach()); SASSERT(r); if (src.neg().is_empty()) { return r; } // // A negation can be projected directly if it does not constrain // deleted variables. // tbv_vector todo, new_todo; for (unsigned i = 0; i < src.neg().size(); ++i) { todo.push_back(tbvm().allocate(src.neg()[i])); } unsigned idx; bool done = false; while (!todo.empty() && !done) { switch(pick_resolvent(src.pos(), todo, to_delete, idx)) { case project_is_empty: t = dstt.allocate(r->pos()); r->neg().push_back(t.detach()); done = true; break; case project_monolithic: done = true; break; case project_neg: case project_pos: for (unsigned i = 0; i < todo.size(); ++i) { tbv& tx = *todo[i]; if (tx[idx] == BIT_x) { new_todo.push_back(&tx); } else { m.deallocate(&tx); } } std::swap(new_todo, todo); new_todo.reset(); break; case project_resolve: { utbv pos, neg; for (unsigned i = 0; i < todo.size(); ++i) { tbv& tx = *todo[i]; switch(tx[idx]) { case BIT_x: new_todo.push_back(&tx); break; case BIT_0: neg.push_back(&tx); break; case BIT_1: pos.push_back(&tx); break; default: UNREACHABLE(); break; } } TRACE("doc", tout << "pos: "; for (unsigned i = 0; i < pos.size(); ++i) { tbvm().display(tout, pos[i]) << " "; } tout << "\nneg: "; for (unsigned i = 0; i < neg.size(); ++i) { tbvm().display(tout, neg[i]) << " "; } tout << "\n"; ); SASSERT(pos.size() > 0 && neg.size() > 0); tbv_ref t1(m); for (unsigned j = 0; j < pos.size(); ++j) { for (unsigned k = 0; k < neg.size(); ++k) { t1 = m.allocate(pos[j]); m.set(*t1, idx, BIT_x); if (tbvm().set_and(*t1, neg[k])) { m.set(*t1, idx, BIT_x); new_todo.push_back(t1.detach()); } } } pos.reset(m); neg.reset(m); std::swap(todo, new_todo); new_todo.reset(); break; } case project_done: { for (unsigned i = 0; i < todo.size(); ++i) { t = dstt.project(to_delete, *todo[i]); if (dstt.equals(r->pos(), *t)) { r->neg().reset(dstt); r->neg().push_back(t.detach()); break; } if (r->neg().size() > 0 && dstt.equals(r->neg()[0], *t)) { continue; } r->neg().push_back(t.detach()); } done = true; break; } } } for (unsigned i = 0; i < todo.size(); ++i) { m.deallocate(todo[i]); } return r; } doc* doc_manager::join(const doc& d1, const doc& d2, doc_manager& dm1, const unsigned_vector& cols1, const unsigned_vector& cols2) { doc_ref d(*this, allocateX()); tbv_ref t(m); tbv& pos = d->pos(); utbv& neg = d->neg(); unsigned mid = dm1.num_tbits(); unsigned hi = num_tbits(); m.set(pos, d1.pos(), mid - 1, 0); m.set(pos, d2.pos(), hi - 1, mid); SASSERT(well_formed(*d)); // first fix bits for (unsigned i = 0; i < cols1.size(); ++i) { unsigned idx1 = cols1[i]; unsigned idx2 = mid + cols2[i]; tbit v1 = pos[idx1]; tbit v2 = pos[idx2]; if (v1 == BIT_x) { if (v2 != BIT_x) m.set(pos, idx1, v2); } else if (v2 == BIT_x) { m.set(pos, idx2, v1); } else if (v1 != v2) { // columns don't match return nullptr; } SASSERT(well_formed(*d)); } // fix equality of don't care columns for (unsigned i = 0; i < cols1.size(); ++i) { unsigned idx1 = cols1[i]; unsigned idx2 = mid + cols2[i]; unsigned v1 = pos[idx1]; unsigned v2 = pos[idx2]; if (v1 == BIT_x && v2 == BIT_x) { // add to subtracted TBVs: 1xx0 and 0xx1 t = m.allocate(pos); m.set(*t, idx1, BIT_0); m.set(*t, idx2, BIT_1); neg.push_back(t.detach()); t = m.allocate(pos); m.set(*t, idx1, BIT_1); m.set(*t, idx2, BIT_0); neg.push_back(t.detach()); } SASSERT(well_formed(*d)); } // handle subtracted TBVs: 1010 -> 1010xxx for (unsigned i = 0; i < d1.neg().size(); ++i) { t = m.allocateX(); m.set(*t, d1.neg()[i], mid - 1, 0); if (m.set_and(*t, pos)) neg.push_back(t.detach()); SASSERT(well_formed(*d)); } for (unsigned i = 0; i < d2.neg().size(); ++i) { t = m.allocateX(); m.set(*t, d2.neg()[i], hi - 1, mid); if (m.set_and(*t, pos)) neg.push_back(t.detach()); SASSERT(well_formed(*d)); } SASSERT(well_formed(*d)); return d.detach(); } doc_manager::project_action_t doc_manager::pick_resolvent( tbv const& pos, tbv_vector const& neg, bit_vector const& to_delete, unsigned& idx) { if (neg.empty()) return project_done; for (unsigned j = 0; j < neg.size(); ++j) { if (m.equals(pos, *neg[j])) return project_is_empty; } unsigned best_pos = UINT_MAX; unsigned best_neg = UINT_MAX; unsigned best_idx = UINT_MAX; for (unsigned i = 0; i < num_tbits(); ++i) { if (!to_delete.get(i)) continue; if (pos[i] != BIT_x) continue; unsigned num_pos = 0, num_neg = 0; tbit b1 = (*neg[0])[i]; if (b1 == BIT_0) num_neg++; if (b1 == BIT_1) num_pos++; bool monolithic = true; for (unsigned j = 1; j < neg.size(); ++j) { tbit b2 = (*neg[j])[i]; if (b1 != b2) { monolithic = false; } if (b2 == BIT_0) num_neg++; if (b2 == BIT_1) num_pos++; } if (monolithic && b1 != BIT_x) { idx = i; return project_monolithic; } if (monolithic && b1 == BIT_x) { continue; } SASSERT(!monolithic); if (num_pos == 0) { SASSERT(num_neg > 0); idx = i; return project_neg; } if (num_neg == 0) { SASSERT(num_pos > 0); idx = i; return project_pos; } if ((best_pos >= num_pos && best_neg >= num_neg) || num_neg == 1 || num_pos == 1) { best_pos = num_pos; best_neg = num_neg; best_idx = i; } } if (best_idx != UINT_MAX) { idx = best_idx; return project_resolve; } else { return project_done; } } void doc_manager::complement(doc const& src, doc_vector& result) { result.reset(); if (is_full(src)) { return; } doc* r = allocateX(); r->neg().push_back(m.allocate(src.pos())); result.push_back(r); for (unsigned i = 0; i < src.neg().size(); ++i) { result.push_back(allocate(src.neg()[i])); } } // (A \ {A1}) \ (B \ {B1}) // (A & !A1 & & !B) | (A & B1 & !A1) // A \ {A1 u B} u (A & B1) \ {A1} void doc_manager::subtract(doc const& A, doc const& B, doc_vector& result) { doc_ref r(*this); tbv_ref t(m); r = allocate(A); t = m.allocate(B.pos()); if (m.set_and(*t, A.pos())) { r->neg().insert(m, t.detach()); } if (fold_neg(*r)) result.push_back(r.detach()); for (unsigned i = 0; i < B.neg().size(); ++i) { r = allocate(A); if (set_and(*r, B.neg()[i])) { result.push_back(r.detach()); } } } bool doc_manager::equals(doc const& a, doc const& b) const { if (!m.equals(a.pos(), b.pos())) return false; if (a.neg().size() != b.neg().size()) return false; for (unsigned i = 0; i < a.neg().size(); ++i) { if (!m.equals(a.neg()[i], b.neg()[i])) return false; } return true; } bool doc_manager::is_full(doc const& src) const { return src.neg().is_empty() && m.equals(src.pos(), *m_full); } bool doc_manager::is_empty_complete(ast_manager& m, doc const& src) { if (src.neg().size() == 0) return false; smt_params fp; smt::kernel s(m, fp); expr_ref fml = to_formula(m, src); s.assert_expr(fml); lbool res = s.check(); if (res == l_true) { return false; } SASSERT(res == l_false); return true; } unsigned doc_manager::hash(doc const& src) const { unsigned r = 0; for (unsigned i = 0; i < src.neg().size(); ++i) { r = 2*r + m.hash(src.neg()[i]); } return r + m.hash(src.pos()); } // approximation // A \ (A1 u A2) contains B \ (B1 u B2) // if // A contains B // B1 contains A1 or B2 contains A1 // B1 contains A2 or B2 contains A2 bool doc_manager::contains(doc const& a, doc const& b) const { if (!m.contains(a.pos(), b.pos())) return false; for (unsigned i = 0; i < a.neg().size(); ++i) { bool found = false; for (unsigned j = 0; !found && j < b.neg().size(); ++j) { found = m.contains(b.neg()[j],a.neg()[i]); } if (!found) return false; } return true; } bool doc_manager::contains(doc const& a, unsigned_vector const& colsa, doc const& b, unsigned_vector const& colsb) const { if (!m.contains(a.pos(), colsa, b.pos(), colsb)) return false; for (unsigned i = 0; i < a.neg().size(); ++i) { bool found = false; for (unsigned j = 0; !found && j < b.neg().size(); ++j) { found = m.contains(b.neg()[j], colsb, a.neg()[i], colsa); } if (!found) return false; } return true; } std::ostream& doc_manager::display(std::ostream& out, doc const& b) const { if (num_tbits() == 0) return out << "[]"; return display(out, b, num_tbits()-1, 0); } std::ostream& doc_manager::display(std::ostream& out, doc const& b, unsigned hi, unsigned lo) const { m.display(out, b.pos(), hi, lo); if (b.neg().is_empty()) return out; out << " \\ "; b.neg().display(m, out, hi, lo); return out; } void doc_manager::verify_project(ast_manager& m, doc_manager& dstm, bit_vector const& to_delete, doc const& src, doc const& dst) { expr_ref fml1 = to_formula(m, src); expr_ref fml2 = dstm.to_formula(m, dst); project_rename(fml2, to_delete); project_expand(fml1, to_delete); check_equiv(m, fml1, fml2); } void doc_manager::check_equiv(ast_manager& m, expr* fml1, expr* fml2) { smt_params fp; smt::kernel solver(m, fp); expr_ref fml(m); fml = m.mk_not(m.mk_eq(fml1, fml2)); solver.assert_expr(fml); lbool res = solver.check(); if (res != l_false) { TRACE("doc", tout << mk_pp(fml1, m) << "\n"; tout << mk_pp(fml2, m) << "\n"; ); UNREACHABLE(); throw 0; } SASSERT(res == l_false); } expr_ref doc_manager::to_formula(ast_manager& m, doc const& src) { expr_ref result(m); expr_ref_vector conj(m); conj.push_back(tbvm().to_formula(m, src.pos())); for (unsigned i = 0; i < src.neg().size(); ++i) { conj.push_back(m.mk_not(tbvm().to_formula(m, src.neg()[i]))); } result = mk_and(m, conj.size(), conj.c_ptr()); return result; } void doc_manager::project_expand(expr_ref& fml, bit_vector const& to_delete) { ast_manager& m = fml.get_manager(); expr_ref tmp1(m), tmp2(m); for (unsigned i = 0; i < num_tbits(); ++i) { if (to_delete.get(i)) { expr_safe_replace rep1(m), rep2(m); rep1.insert(tbvm().mk_var(m, i), m.mk_true()); rep1(fml, tmp1); rep2.insert(tbvm().mk_var(m, i), m.mk_false()); rep2(fml, tmp2); if (tmp1 == tmp2) { fml = tmp1; } else { fml = m.mk_or(tmp1, tmp2); } } } } void doc_manager::project_rename(expr_ref& fml, bit_vector const& to_delete) { ast_manager& m = fml.get_manager(); expr_safe_replace rep(m); for (unsigned i = 0, j = 0; i < num_tbits(); ++i) { if (!to_delete.get(i)) { rep.insert(tbvm().mk_var(m, j), tbvm().mk_var(m, i)); ++j; } } rep(fml); } z3-z3-4.8.7/src/muz/rel/doc.h000066400000000000000000000277731356505360400155510ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: doc.h Abstract: difference of cubes. Author: Nuno Lopes (a-nlopes) 2013-03-01 Nikolaj Bjorner (nbjorner) 2014-09-15 Revision History: Based on ternary_diff_bitvector by Nuno Lopes. --*/ #ifndef DOC_H_ #define DOC_H_ #include "muz/rel/tbv.h" #include "util/union_find.h" #include "util/buffer.h" class doc; template class union_bvec; typedef union_find<> subset_ints; typedef union_bvec utbv; typedef buffer tbv_vector; typedef buffer doc_vector; class doc_manager { tbv_manager m; tbv* m_full; small_object_allocator m_alloc; enum project_action_t { project_is_empty, project_done, project_monolithic, project_neg, project_pos, project_resolve }; project_action_t pick_resolvent( tbv const& pos, tbv_vector const& neg, bit_vector const& to_delete, unsigned& idx); public: doc_manager(unsigned num_bits); ~doc_manager(); tbv_manager& tbvm() { return m; } tbv_manager const& tbvm() const { return m; } doc* allocate(); doc* allocate1(); doc* allocate0(); doc* allocateX(); doc* allocate(doc const& src); doc* allocate(tbv const& src); doc* allocate(tbv * src); doc* allocate(uint64_t n); doc* allocate(rational const& r); doc* allocate(uint64_t n, unsigned hi, unsigned lo); doc* allocate(doc const& src, unsigned const* permutation); void deallocate(doc* src); void copy(doc& dst, doc const& src); doc& reset(doc& src) { return fill0(src); } doc& fill0(doc& src); doc& fill1(doc& src); doc& fillX(doc& src); bool is_full(doc const& src) const; bool is_empty_complete(ast_manager& m, doc const& src); bool set_and(doc& dst, doc const& src); bool set_and(doc& dst, tbv const& src); bool fold_neg(doc& dst); bool intersect(doc const& A, doc const& B, doc& result); void complement(doc const& src, doc_vector& result); void subtract(doc const& A, doc const& B, doc_vector& result); bool equals(doc const& a, doc const& b) const; unsigned hash(doc const& src) const; bool contains(doc const& a, doc const& b) const; bool contains(doc const& a, unsigned_vector const& colsa, doc const& b, unsigned_vector const& colsb) const; std::ostream& display(std::ostream& out, doc const& b) const; std::ostream& display(std::ostream& out, doc const& b, unsigned hi, unsigned lo) const; unsigned num_tbits() const { return m.num_tbits(); } unsigned get_size_estimate_bytes(const doc& d) const; doc* project(doc_manager& dstm, bit_vector const& to_delete, doc const& src); bool well_formed(doc const& d) const; bool merge(doc& d, unsigned lo, unsigned length, subset_ints const& equalities, bit_vector const& discard_cols); void set(doc& d, unsigned idx, tbit value); doc* join(const doc& a, const doc& b, doc_manager& dm1, const unsigned_vector& cols1, const unsigned_vector& cols2); void verify_project(ast_manager& m, doc_manager& dstm, bit_vector const& to_delete, doc const& src, doc const& dst); private: unsigned diff_by_012(tbv const& pos, tbv const& neg, unsigned& index); bool merge(doc& d, unsigned idx, subset_ints const& equalities, bit_vector const& discard_cols); void project_rename(expr_ref& fml, bit_vector const& to_delete); void project_expand(expr_ref& fml, bit_vector const& to_delete); expr_ref to_formula(ast_manager& m, doc const& src); void check_equiv(ast_manager& m, expr* fml1, expr* fml2); }; // union of tbv*, union of doc* template class union_bvec { buffer m_elems; // TBD: reuse allocator of M enum fix_bit_result_t { e_row_removed, // = 1 e_duplicate_row, // = 2 e_fixed }; public: unsigned size() const { return m_elems.size(); } T& operator[](unsigned idx) const { return *m_elems[idx]; } bool is_empty() const { return m_elems.empty(); } bool is_empty_complete(ast_manager& m, doc_manager& dm) const { for (unsigned i = 0; i < size(); ++i) { if (!dm.is_empty_complete(m, *m_elems[i])) return false; } return true; } bool is_full(M& m) const { return size() == 1 && m.is_full(*m_elems[0]); } bool contains(M& m, T& t) const { for (unsigned i = 0; i < size(); ++i) { if (m.contains(*m_elems[i], t)) return true; } return false; } std::ostream& display(M const& m, std::ostream& out) const { if (m.num_tbits() == 0) return out << "[]"; return display(m, out, m.num_tbits()-1, 0); } std::ostream& display(M const& m, std::ostream& out, unsigned hi, unsigned lo) const { out << "{"; if (size() + m.num_tbits() > 10) out << "\n "; for (unsigned i = 0; i < size(); ++i) { m.display(out, *m_elems[i], hi, lo); if (i + 1 < size()) out << ", "; if (i + 1 < size() && m.num_tbits() > 10) out << "\n "; } return out << "}"; } void push_back(T* t) { SASSERT(t); m_elems.push_back(t); } void erase(M& m, unsigned idx) { m.deallocate(m_elems[idx]); unsigned sz = m_elems.size(); for (unsigned i = idx+1; i < sz; ++i) { m_elems[i-1] = m_elems[i]; } m_elems.resize(sz-1); } void reset(M& m) { for (unsigned i = 0; i < m_elems.size(); ++i) { m.deallocate(m_elems[i]); } m_elems.reset(); } bool insert(M& m, T* t) { SASSERT(t); unsigned sz = size(), j = 0; bool found = false; unsigned i = 0; for ( ; i < sz; ++i, ++j) { if (m.contains(*m_elems[i], *t)) { found = true; } else if (m.contains(*t, *m_elems[i])) { m.deallocate(m_elems[i]); --j; continue; } if (i != j) { m_elems[j] = m_elems[i]; } } if (j != sz) m_elems.resize(j); if (found) { m.deallocate(t); } else { m_elems.push_back(t); } return !found; } void intersect(M& m, T const& t) { unsigned sz = size(); unsigned j = 0; for (unsigned i = 0; i < sz; ++i, ++j) { if (!m.set_and(*m_elems[i], t)) { m.deallocate(m_elems[i]); --j; } else if (i != j) { m_elems[j] = m_elems[i]; } } if (j != sz) m_elems.resize(j); } void insert(M& m, union_bvec const& other) { for (unsigned i = 0; i < other.size(); ++i) { insert(m, &other[i]); } } void intersect(M& m, union_bvec const& other) { unsigned sz = other.size(); union_bvec tmp, result; for (unsigned i = 0; i < sz; ++i) { tmp.copy(m, *this); tmp.intersect(m, other[i]); for (unsigned j = 0; j < tmp.size(); ++j) { result.push_back(tmp.m_elems[j]); } tmp.m_elems.reset(); } std::swap(*this, result); result.reset(m); } void subtract(M& m, union_bvec const& other) { unsigned sz = other.size(); for (unsigned i = 0; !is_empty() && i < sz; ++i) { subtract(m, other[i]); } // TBD compress? } void subtract(M& m, T& t) { unsigned sz = size(); union_bvec result; for (unsigned i = 0; i < sz; ++i) { m.subtract(*m_elems[i], t, result.m_elems); } std::swap(m_elems, result.m_elems); result.reset(m); } void complement(M& m, union_bvec& result) const { union_bvec negated; result.reset(m); result.push_back(m.allocateX()); unsigned sz = size(); for (unsigned i = 0; !is_empty() && i < sz; ++i) { m.complement(*m_elems[i], negated.m_elems); result.intersect(m, negated); negated.reset(m); } } void copy(M& m, union_bvec const& other) { reset(m); for (unsigned i = 0; i < other.size(); ++i) { push_back(m.allocate(other[i])); } } void simplify(M& m) { union_bvec result; for (unsigned i = 0; i < size(); ++i) { if (m.fold_neg(*m_elems[i])) { result.insert(m, m_elems[i]); } else { m.deallocate(m_elems[i]); } } std::swap(*this, result); } bool well_formed(M& m) const { for (unsigned i = 0; i < size(); ++i) { if (!m.well_formed(*m_elems[i])) return false; } return true; } void merge(M& m, unsigned lo, unsigned length, subset_ints const& equalities, bit_vector const& discard_cols) { unsigned j = 0; unsigned sz = size(); for (unsigned i = 0; i < sz; ++i, ++j) { if (!m.merge(*m_elems[i], lo, length, equalities, discard_cols)) { --j; m.deallocate(m_elems[i]); } else if (i != j) { m_elems[j] = m_elems[i]; } } if (j != sz) m_elems.resize(j); } void merge(M& m, unsigned lo1, unsigned lo2, unsigned length, bit_vector const& discard_cols) { union_find_default_ctx union_ctx; subset_ints equalities(union_ctx); for (unsigned i = 0; i < discard_cols.size(); ++i) { equalities.mk_var(); } for (unsigned j = 0; j < length; ++j) { equalities.merge(lo1 + j, lo2 + j); } merge(m, lo1, length, equalities, discard_cols); } void merge(M& m, unsigned_vector const& roots, subset_ints const& equalities, bit_vector const& discard_cols) { for (unsigned i = 0; i < roots.size(); ++i) { merge(m, roots[i], 1, equalities, discard_cols); } } void join(const union_bvec& d1, const union_bvec& d2, M& dm, M& dm1, const unsigned_vector& cols1, const unsigned_vector& cols2) { for (unsigned i = 0; i < d1.size(); ++i) { for (unsigned j = 0; j < d2.size(); ++j) { if (T *d = dm.join(d1[i], d2[j], dm1, cols1, cols2)) insert(dm, d); } } } unsigned get_size_estimate_bytes(const M& m) const { unsigned sz = sizeof(T*) * size(); for (unsigned i = 0; i < size(); ++i) { sz += m.get_size_estimate_bytes(*m_elems[i]); } return sz; } }; class doc { // pos \ (neg_0 \/ ... \/ neg_n) friend class doc_manager; tbv* m_pos; utbv m_neg; public: struct eq { doc_manager& m; eq(doc_manager& m):m(m) {} bool operator()(doc const& d1, doc const& d2) const { return m.equals(d1, d2); } }; struct hash { doc_manager& m; hash(doc_manager& m):m(m) {} unsigned operator()(doc const& d) const { return m.hash(d); } }; doc(tbv* t): m_pos(t) {} tbv& pos() { return *m_pos; } utbv& neg() { return m_neg; } tbv const& pos() const { return *m_pos; } utbv const& neg() const { return m_neg; } tbit operator[](unsigned idx) const { return pos()[idx]; } }; typedef union_bvec udoc; class doc_ref { doc_manager& dm; doc* d; public: doc_ref(doc_manager& dm):dm(dm),d(nullptr) {} doc_ref(doc_manager& dm, doc* d):dm(dm),d(d) {} ~doc_ref() { if (d) dm.deallocate(d); } doc_ref& operator=(doc* d2) { if (d) dm.deallocate(d); d = d2; return *this; } doc& operator*() { return *d; } doc* operator->() { return d; } doc* detach() { doc* r = d; d = nullptr; return r; } operator bool() const { return d != nullptr; } }; #endif /* DOC_H_ */ z3-z3-4.8.7/src/muz/rel/karr_relation.cpp000066400000000000000000000661141356505360400201630ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "muz/rel/karr_relation.h" #include "ast/rewriter/bool_rewriter.h" #include "ast/ast_util.h" namespace datalog { class karr_relation : public relation_base { friend class karr_relation_plugin; friend class karr_relation_plugin::filter_equal_fn; karr_relation_plugin& m_plugin; ast_manager& m; mutable arith_util a; func_decl_ref m_fn; mutable bool m_empty; mutable matrix m_ineqs; mutable bool m_ineqs_valid; mutable matrix m_basis; mutable bool m_basis_valid; public: karr_relation(karr_relation_plugin& p, func_decl* f, relation_signature const& s, bool is_empty): relation_base(p, s), m_plugin(p), m(p.get_ast_manager()), a(m), m_fn(f, m), m_empty(is_empty), m_ineqs_valid(!is_empty), m_basis_valid(false) { } bool empty() const override { return m_empty; } bool is_precise() const override { return false; } void add_fact(const relation_fact & f) override { SASSERT(m_empty); SASSERT(!m_basis_valid); m_empty = false; m_ineqs_valid = true; for (unsigned i = 0; i < f.size(); ++i) { rational n; if (a.is_numeral(f[i], n) && n.is_int()) { vector row; row.resize(f.size()); row[i] = rational(1); m_ineqs.A.push_back(row); m_ineqs.b.push_back(-n); m_ineqs.eq.push_back(true); } } } bool contains_fact(const relation_fact & f) const override { UNREACHABLE(); return false; } void display(std::ostream & out) const override { if (m_fn) { out << m_fn->get_name() << "\n"; } if (empty()) { out << "empty\n"; } else { if (m_ineqs_valid) { m_ineqs.display(out << "ineqs:\n"); } if (m_basis_valid) { m_basis.display(out << "basis:\n"); } } } karr_relation * clone() const override { karr_relation* result = alloc(karr_relation, m_plugin, m_fn, get_signature(), m_empty); result->copy(*this); return result; } karr_relation * complement(func_decl*) const override { UNREACHABLE(); return nullptr; } void to_formula(expr_ref& fml) const override { if (empty()) { fml = m.mk_false(); } else { matrix const& M = get_ineqs(); expr_ref_vector conj(m); for (unsigned i = 0; i < M.size(); ++i) { to_formula(M.A[i], M.b[i], M.eq[i], conj); } bool_rewriter(m).mk_and(conj.size(), conj.c_ptr(), fml); } } karr_relation_plugin& get_plugin() const { return m_plugin; } void filter_interpreted(app* cond) { rational one(1), mone(-1); expr* e1 = nullptr, *e2 = nullptr, *en = nullptr; var* v = nullptr, *w = nullptr; rational n1, n2; expr_ref_vector conjs(m); flatten_and(cond, conjs); matrix& M = get_ineqs(); unsigned num_columns = get_signature().size(); for (unsigned i = 0; i < conjs.size(); ++i) { expr* e = conjs[i].get(); rational b(0); vector row; row.resize(num_columns, rational(0)); bool processed = true; if (m.is_eq(e, e1, e2) && is_linear(e1, row, b, one) && is_linear(e2, row, b, mone)) { M.A.push_back(row); M.b.push_back(b); M.eq.push_back(true); } else if ((a.is_le(e, e1, e2) || a.is_ge(e, e2, e1)) && is_linear(e1, row, b, mone) && is_linear(e2, row, b, one)) { M.A.push_back(row); M.b.push_back(b); M.eq.push_back(false); } else if ((a.is_lt(e, e1, e2) || a.is_gt(e, e2, e1)) && is_linear(e1, row, b, mone) && is_linear(e2, row, b, one)) { M.A.push_back(row); M.b.push_back(b - rational(1)); M.eq.push_back(false); } else if (m.is_not(e, en) && (a.is_lt(en, e2, e1) || a.is_gt(en, e1, e2)) && is_linear(e1, row, b, mone) && is_linear(e2, row, b, one)) { M.A.push_back(row); M.b.push_back(b); M.eq.push_back(false); } else if (m.is_not(e, en) && (a.is_le(en, e2, e1) || a.is_ge(en, e1, e2)) && is_linear(e1, row, b, mone) && is_linear(e2, row, b, one)) { M.A.push_back(row); M.b.push_back(b - rational(1)); M.eq.push_back(false); } else if (m.is_or(e, e1, e2) && is_eq(e1, v, n1) && is_eq(e2, w, n2) && v == w) { if (n1 > n2) { std::swap(n1, n2); } SASSERT(n1 <= n2); row[v->get_idx()] = rational(1); // v - n1 >= 0 M.A.push_back(row); M.b.push_back(-n1); M.eq.push_back(false); // -v + n2 >= 0 row[v->get_idx()] = rational(-1); M.A.push_back(row); M.b.push_back(n2); M.eq.push_back(false); } else { processed = false; } (void)processed; TRACE("dl", tout << (processed?"+ ":"- ") << mk_pp(e, m) << "\n"; if (processed) matrix::display_ineq(tout, row, M.b.back(), M.eq.back()); ); } TRACE("dl", display(tout);); } void mk_join(karr_relation const& r1, karr_relation const& r2, unsigned col_cnt, unsigned const* cols1, unsigned const* cols2) { if (r1.empty() || r2.empty()) { m_empty = true; return; } matrix const& M1 = r1.get_ineqs(); matrix const& M2 = r2.get_ineqs(); unsigned sig1_size = r1.get_signature().size(); unsigned sig_size = get_signature().size(); m_ineqs.reset(); for (unsigned i = 0; i < M1.size(); ++i) { vector row; row.append(M1.A[i]); row.resize(sig_size); m_ineqs.A.push_back(row); m_ineqs.b.push_back(M1.b[i]); m_ineqs.eq.push_back(M1.eq[i]); } for (unsigned i = 0; i < M2.size(); ++i) { vector row; row.resize(sig_size); for (unsigned j = 0; j < M2.A[i].size(); ++j) { row[sig1_size + j] = M2.A[i][j]; } m_ineqs.A.push_back(row); m_ineqs.b.push_back(M2.b[i]); m_ineqs.eq.push_back(M2.eq[i]); } for (unsigned i = 0; i < col_cnt; ++i) { vector row; row.resize(sig_size); row[cols1[i]] = rational(1); row[sig1_size + cols2[i]] = rational(-1); m_ineqs.A.push_back(row); m_ineqs.b.push_back(rational(0)); m_ineqs.eq.push_back(true); } m_ineqs_valid = true; m_basis_valid = false; m_empty = false; if (r1.m_fn) { m_fn = r1.m_fn; } if (r2.m_fn) { m_fn = r2.m_fn; } } void mk_project(karr_relation const& r, unsigned cnt, unsigned const* cols) { if (r.m_empty) { m_empty = true; return; } matrix const& M = r.get_basis(); m_basis.reset(); for (unsigned i = 0; i < M.size(); ++i) { vector row; unsigned k = 0; for (unsigned j = 0; j < M.A[i].size(); ++j) { if (k < cnt && j == cols[k]) { ++k; } else { row.push_back(M.A[i][j]); } } SASSERT(row.size() + cnt == M.A[i].size()); SASSERT(M.eq[i]); m_basis.A.push_back(row); m_basis.b.push_back(M.b[i]); m_basis.eq.push_back(true); } m_basis_valid = true; m_ineqs_valid = false; m_empty = false; m_fn = r.m_fn; TRACE("dl", for (unsigned i = 0; i < cnt; ++i) { tout << cols[i] << " "; } tout << "\n"; r.display(tout); display(tout);); } void mk_rename(const karr_relation & r, unsigned col_cnt, const unsigned * cols) { if (r.empty()) { m_empty = true; return; } m_ineqs.reset(); m_basis.reset(); m_ineqs_valid = r.m_ineqs_valid; m_basis_valid = r.m_basis_valid; if (m_ineqs_valid) { m_ineqs.append(r.m_ineqs); mk_rename(m_ineqs, col_cnt, cols); } if (m_basis_valid) { m_basis.append(r.m_basis); mk_rename(m_basis, col_cnt, cols); } m_fn = r.m_fn; TRACE("dl", r.display(tout); display(tout);); } void mk_union(karr_relation const& src, karr_relation* delta) { if (src.empty()) { if (delta) { delta->m_empty = true; } return; } matrix const& M = src.get_basis(); if (empty()) { m_basis = M; m_basis_valid = true; m_empty = false; m_ineqs_valid = false; if (delta) { delta->copy(*this); } return; } matrix& N = get_basis(); unsigned N_size = N.size(); for (unsigned i = 0; i < M.size(); ++i) { bool found = false; for (unsigned j = 0; !found && j < N_size; ++j) { found = same_row(M.A[i], N.A[j]) && M.b[i] == N.b[j] && M.eq[i] == N.eq[j]; } if (!found) { N.A.push_back(M.A[i]); N.b.push_back(M.b[i]); N.eq.push_back(M.eq[i]); } } m_ineqs_valid = false; if (N_size != N.size()) { if (delta) { delta->copy(*this); } } } matrix const& get_basis() const { init_basis(); return m_basis; } matrix& get_basis() { init_basis(); return m_basis; } matrix const& get_ineqs() const { init_ineqs(); return m_ineqs; } matrix & get_ineqs() { init_ineqs(); return m_ineqs; } private: void copy(karr_relation const& other) { m_ineqs = other.m_ineqs; m_basis = other.m_basis; m_basis_valid = other.m_basis_valid; m_ineqs_valid = other.m_ineqs_valid; m_empty = other.m_empty; } bool same_row(vector const& r1, vector const& r2) const { SASSERT(r1.size() == r2.size()); for (unsigned i = 0; i < r1.size(); ++i) { if (r1[i] != r2[i]) { return false; } } return true; } void mk_rename(matrix& M, unsigned col_cnt, unsigned const* cols) { for (unsigned j = 0; j < M.size(); ++j) { vector & row = M.A[j]; rational tmp = row[cols[0]]; for (unsigned i = 0; i + 1 < col_cnt; ++i) { row[cols[i]] = row[cols[i+1]]; } row[cols[col_cnt-1]] = tmp; } } bool is_eq(expr* e, var*& v, rational& n) { expr* e1, *e2; if (!m.is_eq(e, e1, e2)) { return false; } if (!is_var(e1)) { std::swap(e1, e2); } if (!is_var(e1)) { return false; } v = to_var(e1); if (!a.is_numeral(e2, n)) { return false; } return true; } bool is_linear(expr* e, vector& row, rational& b, rational const& mul) { if (!a.is_int(e)) { return false; } if (is_var(e)) { row[to_var(e)->get_idx()] += mul; return true; } if (!is_app(e)) { return false; } rational n; if (a.is_numeral(e, n)) { b += mul*n; return true; } if (a.is_add(e)) { for (unsigned i = 0; i < to_app(e)->get_num_args(); ++i) { if (!is_linear(to_app(e)->get_arg(i), row, b, mul)) { return false; } } return true; } expr* e1, *e2; if (a.is_sub(e, e1, e2)) { return is_linear(e1, row, b, mul) && is_linear(e2, row, b, -mul); } if (a.is_mul(e, e1, e2) && a.is_numeral(e1, n)) { return is_linear(e2, row, b, mul*n); } if (a.is_mul(e, e1, e2) && a.is_numeral(e2, n)) { return is_linear(e1, row, b, mul*n); } if (a.is_uminus(e, e1)) { return is_linear(e1, row, b, -mul); } return false; } void init_ineqs() const { if (!m_ineqs_valid) { SASSERT(m_basis_valid); m_plugin.dualizeH(m_ineqs, m_basis); m_ineqs_valid = true; } } void init_basis() const { if (!m_basis_valid) { SASSERT(m_ineqs_valid); if (m_plugin.dualizeI(m_basis, m_ineqs)) { m_basis_valid = true; } else { m_empty = true; } } } void to_formula(vector const& row, rational const& b, bool is_eq, expr_ref_vector& conj) const { expr_ref_vector sum(m); expr_ref zero(m), lhs(m); zero = a.mk_numeral(rational(0), true); for (unsigned i = 0; i < row.size(); ++i) { if (row[i].is_zero()) { continue; } var* var = m.mk_var(i, a.mk_int()); if (row[i].is_one()) { sum.push_back(var); } else { sum.push_back(a.mk_mul(a.mk_numeral(row[i], true), var)); } } if (!b.is_zero()) { sum.push_back(a.mk_numeral(b, true)); } lhs = a.mk_add(sum.size(), sum.c_ptr()); if (is_eq) { conj.push_back(m.mk_eq(lhs, zero)); } else { conj.push_back(a.mk_ge(lhs, zero)); } } }; karr_relation& karr_relation_plugin::get(relation_base& r) { return dynamic_cast(r); } karr_relation const & karr_relation_plugin::get(relation_base const& r) { return dynamic_cast(r); } relation_base * karr_relation_plugin::mk_empty(const relation_signature & s) { return alloc(karr_relation, *this, nullptr, s, true); } relation_base * karr_relation_plugin::mk_full(func_decl* p, const relation_signature & s) { return alloc(karr_relation, *this, p, s, false); } class karr_relation_plugin::join_fn : public convenient_relation_join_fn { public: join_fn(const relation_signature & o1_sig, const relation_signature & o2_sig, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) : convenient_relation_join_fn(o1_sig, o2_sig, col_cnt, cols1, cols2){ } relation_base * operator()(const relation_base & _r1, const relation_base & _r2) override { karr_relation const& r1 = get(_r1); karr_relation const& r2 = get(_r2); karr_relation_plugin& p = r1.get_plugin(); karr_relation* result = dynamic_cast(p.mk_full(nullptr, get_result_signature())); result->mk_join(r1, r2, m_cols1.size(), m_cols1.c_ptr(), m_cols2.c_ptr()); return result; } }; relation_join_fn * karr_relation_plugin::mk_join_fn( const relation_base & t1, const relation_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { if (!check_kind(t1) || !check_kind(t2)) { return nullptr; } return alloc(join_fn, t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2); } class karr_relation_plugin::project_fn : public convenient_relation_project_fn { public: project_fn(const relation_signature & orig_sig, unsigned removed_col_cnt, const unsigned * removed_cols) : convenient_relation_project_fn(orig_sig, removed_col_cnt, removed_cols) { } relation_base * operator()(const relation_base & _r) override { karr_relation const& r = get(_r); karr_relation_plugin& p = r.get_plugin(); karr_relation* result = dynamic_cast(p.mk_full(nullptr, get_result_signature())); result->mk_project(r, m_removed_cols.size(), m_removed_cols.c_ptr()); return result; } }; relation_transformer_fn * karr_relation_plugin::mk_project_fn(const relation_base & r, unsigned col_cnt, const unsigned * removed_cols) { return alloc(project_fn, r.get_signature(), col_cnt, removed_cols); } class karr_relation_plugin::rename_fn : public convenient_relation_rename_fn { public: rename_fn(karr_relation_plugin& p, const relation_signature & orig_sig, unsigned cycle_len, const unsigned * cycle) : convenient_relation_rename_fn(orig_sig, cycle_len, cycle) {} relation_base * operator()(const relation_base & _r) override { karr_relation const& r = get(_r); karr_relation_plugin& p = r.get_plugin(); karr_relation* result = dynamic_cast(p.mk_full(nullptr, get_result_signature())); result->mk_rename(r, m_cycle.size(), m_cycle.c_ptr()); return result; } }; relation_transformer_fn * karr_relation_plugin::mk_rename_fn(const relation_base & r, unsigned cycle_len, const unsigned * permutation_cycle) { if (!check_kind(r)) { return nullptr; } return alloc(rename_fn, *this, r.get_signature(), cycle_len, permutation_cycle); } bool karr_relation_plugin::dualizeI(matrix& dst, matrix const& src) { dst.reset(); m_hb.reset(); for (unsigned i = 0; i < src.size(); ++i) { if (src.eq[i]) { m_hb.add_eq(src.A[i], -src.b[i]); } else { m_hb.add_ge(src.A[i], -src.b[i]); } } for (unsigned i = 0; !src.A.empty() && i < src.A[0].size(); ++i) { m_hb.set_is_int(i); } lbool is_sat = l_undef; try { is_sat = m_hb.saturate(); } catch (...) { is_sat = l_undef; } TRACE("dl_verbose", m_hb.display(tout);); if (is_sat == l_false) { return false; } if (is_sat == l_undef) { return true; } unsigned basis_size = m_hb.get_basis_size(); bool first_initial = true; for (unsigned i = 0; i < basis_size; ++i) { bool is_initial; vector soln; m_hb.get_basis_solution(i, soln, is_initial); if (is_initial && first_initial) { dst.A.push_back(soln); dst.b.push_back(rational(1)); dst.eq.push_back(true); first_initial = false; } else if (!is_initial) { dst.A.push_back(soln); dst.b.push_back(rational(0)); dst.eq.push_back(true); } } return true; } void karr_relation_plugin::dualizeH(matrix& dst, matrix const& src) { dst.reset(); if (src.size() == 0) { return; } m_hb.reset(); for (unsigned i = 0; i < src.size(); ++i) { vector v(src.A[i]); v.push_back(src.b[i]); if (src.eq[i]) { m_hb.add_eq(v, rational(0)); } else { m_hb.add_ge(v, rational(0)); } } for (unsigned i = 0; i < 1 + src.A[0].size(); ++i) { m_hb.set_is_int(i); } lbool is_sat = l_undef; try { is_sat = m_hb.saturate(); } catch (...) { is_sat = l_undef; } if (is_sat != l_true) { return; } TRACE("dl_verbose", m_hb.display(tout);); SASSERT(is_sat == l_true); unsigned basis_size = m_hb.get_basis_size(); for (unsigned i = 0; i < basis_size; ++i) { bool is_initial; vector soln; m_hb.get_basis_solution(i, soln, is_initial); if (!is_initial) { dst.b.push_back(soln.back()); dst.eq.push_back(true); soln.pop_back(); dst.A.push_back(soln); } } } class karr_relation_plugin::union_fn : public relation_union_fn { public: union_fn() {} void operator()(relation_base & _r, const relation_base & _src, relation_base * _delta) override { karr_relation& r = get(_r); karr_relation const& src = get(_src); TRACE("dl", r.display(tout << "dst:\n"); src.display(tout << "src:\n");); if (_delta) { karr_relation& d = get(*_delta); r.mk_union(src, &d); } else { r.mk_union(src, nullptr); } TRACE("dl", r.display(tout << "result:\n");); } }; relation_union_fn * karr_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta) { if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) { return nullptr; } return alloc(union_fn); } class karr_relation_plugin::filter_identical_fn : public relation_mutator_fn { unsigned_vector m_identical_cols; public: filter_identical_fn(unsigned col_cnt, const unsigned * identical_cols) : m_identical_cols(col_cnt, identical_cols) {} void operator()(relation_base & _r) override { karr_relation & r = get(_r); TRACE("dl", r.display(tout << "src:\n");); r.get_ineqs(); for (unsigned i = 1; i < m_identical_cols.size(); ++i) { unsigned c1 = m_identical_cols[0]; unsigned c2 = m_identical_cols[i]; vector row; row.resize(r.get_signature().size()); row[c1] = rational(1); row[c2] = rational(-1); r.m_ineqs.A.push_back(row); r.m_ineqs.b.push_back(rational(0)); r.m_ineqs.eq.push_back(true); r.m_basis_valid = false; } TRACE("dl", r.display(tout << "result:\n");); } }; relation_mutator_fn * karr_relation_plugin::mk_filter_identical_fn( const relation_base & t, unsigned col_cnt, const unsigned * identical_cols) { if(!check_kind(t)) { return nullptr; } return alloc(filter_identical_fn, col_cnt, identical_cols); } class karr_relation_plugin::filter_equal_fn : public relation_mutator_fn { unsigned m_col; rational m_value; bool m_valid; public: filter_equal_fn(relation_manager & m, const relation_element & value, unsigned col) : m_col(col) { arith_util arith(m.get_context().get_manager()); m_valid = arith.is_numeral(value, m_value) && m_value.is_int(); } void operator()(relation_base & _r) override { karr_relation & r = get(_r); if (m_valid) { r.get_ineqs(); vector row; row.resize(r.get_signature().size()); row[m_col] = rational(1); r.m_ineqs.A.push_back(row); r.m_ineqs.b.push_back(rational(-1)); r.m_ineqs.eq.push_back(true); r.m_basis_valid = false; } TRACE("dl", tout << m_value << "\n"; r.display(tout);); } }; relation_mutator_fn * karr_relation_plugin::mk_filter_equal_fn(const relation_base & r, const relation_element & value, unsigned col) { if (check_kind(r)) { return alloc(filter_equal_fn, get_manager(), value, col); } return nullptr; } class karr_relation_plugin::filter_interpreted_fn : public relation_mutator_fn { app_ref m_cond; public: filter_interpreted_fn(karr_relation const& t, app* cond): m_cond(cond, t.get_plugin().get_ast_manager()) { } void operator()(relation_base& t) override { get(t).filter_interpreted(m_cond); TRACE("dl", tout << mk_pp(m_cond, m_cond.get_manager()) << "\n"; t.display(tout);); } }; relation_mutator_fn * karr_relation_plugin::mk_filter_interpreted_fn(const relation_base & t, app * condition) { if (check_kind(t)) { return alloc(filter_interpreted_fn, get(t), condition); } return nullptr; } }; z3-z3-4.8.7/src/muz/rel/karr_relation.h000066400000000000000000000052171356505360400176250ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: karr_relation.h Abstract: Extract integer linear invariants. Author: Nikolaj Bjorner (nbjorner) 2013-03-08 Revision History: --*/ #ifndef KARR_RELATION_H_ #define KARR_RELATION_H_ #include "muz/transforms/dl_mk_karr_invariants.h" #include "muz/rel/dl_relation_manager.h" namespace datalog { class karr_relation; class karr_relation_plugin : public relation_plugin { hilbert_basis m_hb; arith_util a; class join_fn; class project_fn; class rename_fn; class union_fn; class filter_equal_fn; class filter_identical_fn; class filter_interpreted_fn; friend class karr_relation; public: karr_relation_plugin(relation_manager& rm): relation_plugin(karr_relation_plugin::get_name(), rm), m_hb(get_ast_manager().limit()), a(get_ast_manager()) {} bool can_handle_signature(const relation_signature & sig) override { return get_manager().get_context().karr(); } static symbol get_name() { return symbol("karr_relation"); } relation_base * mk_empty(const relation_signature & s) override; relation_base * mk_full(func_decl* p, const relation_signature & s) override; static karr_relation& get(relation_base& r); static karr_relation const & get(relation_base const& r); relation_join_fn * mk_join_fn( const relation_base & t1, const relation_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) override; relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, const unsigned * removed_cols) override; relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, const unsigned * permutation_cycle) override; relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta) override; relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, const unsigned * identical_cols) override; relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, unsigned col) override; relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition) override; private: bool dualizeI(matrix& dst, matrix const& src); void dualizeH(matrix& dst, matrix const& src); }; }; #endif /* DL_MK_KARR_INVARIANTS_H_ */ z3-z3-4.8.7/src/muz/rel/rel_context.cpp000066400000000000000000000540201356505360400176460ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: rel_context.cpp Abstract: context for relational datalog engine. Author: Nikolaj Bjorner (nbjorner) 2012-12-3. Revision History: Extracted from dl_context --*/ #include "muz/rel/rel_context.h" #include "util/stopwatch.h" #include "muz/base/dl_context.h" #include "muz/rel/dl_compiler.h" #include "muz/rel/dl_instruction.h" #include "muz/rel/dl_mk_explanations.h" #include "muz/transforms/dl_mk_magic_sets.h" #include "muz/rel/dl_product_relation.h" #include "muz/rel/dl_bound_relation.h" #include "muz/rel/dl_interval_relation.h" #include "muz/rel/karr_relation.h" #include "muz/rel/dl_finite_product_relation.h" #include "muz/rel/udoc_relation.h" #include "muz/rel/check_relation.h" #include "muz/rel/dl_lazy_table.h" #include "muz/rel/dl_sparse_table.h" #include "muz/rel/dl_table.h" #include "muz/rel/dl_table_relation.h" #include "muz/rel/aig_exporter.h" #include "muz/rel/dl_mk_simple_joins.h" #include "muz/rel/dl_mk_similarity_compressor.h" #include "muz/transforms/dl_mk_unbound_compressor.h" #include "muz/transforms/dl_mk_subsumption_checker.h" #include "muz/transforms/dl_mk_coi_filter.h" #include "muz/transforms/dl_mk_filter_rules.h" #include "muz/transforms/dl_mk_rule_inliner.h" #include "muz/transforms/dl_mk_interp_tail_simplifier.h" #include "muz/transforms/dl_mk_bit_blast.h" #include "muz/transforms/dl_mk_separate_negated_tails.h" #include "ast/ast_util.h" namespace datalog { class rel_context::scoped_query { context& m_ctx; rule_set m_rules; decl_set m_preds; bool m_was_closed; public: scoped_query(context& ctx): m_ctx(ctx), m_rules(ctx.get_rules()), m_preds(ctx.get_predicates()), m_was_closed(ctx.is_closed()) { if (m_was_closed) { ctx.reopen(); } } ~scoped_query() { m_ctx.reopen(); m_ctx.restrict_predicates(m_preds); m_ctx.replace_rules(m_rules); if (m_was_closed) { m_ctx.close(); } } void reset() { m_ctx.reopen(); m_ctx.restrict_predicates(m_preds); m_ctx.replace_rules(m_rules); m_ctx.close(); } }; rel_context::rel_context(context& ctx) : rel_context_base(ctx.get_manager(), "datalog"), m_context(ctx), m(ctx.get_manager()), m_rmanager(ctx), m_answer(m), m_last_result_relation(nullptr), m_ectx(ctx), m_sw(0) { // register plugins for builtin tables relation_manager& rm = get_rmanager(); rm.register_plugin(alloc(sparse_table_plugin, rm)); rm.register_plugin(alloc(hashtable_table_plugin, rm)); rm.register_plugin(alloc(bitvector_table_plugin, rm)); rm.register_plugin(lazy_table_plugin::mk_sparse(rm)); // register plugins for builtin relations rm.register_plugin(alloc(bound_relation_plugin, rm)); rm.register_plugin(alloc(interval_relation_plugin, rm)); if (m_context.karr()) rm.register_plugin(alloc(karr_relation_plugin, rm)); rm.register_plugin(alloc(udoc_plugin, rm)); rm.register_plugin(alloc(check_relation_plugin, rm)); } rel_context::~rel_context() { if (m_last_result_relation) { m_last_result_relation->deallocate(); m_last_result_relation = nullptr; } } lbool rel_context::saturate() { scoped_query sq(m_context); return saturate(sq); } lbool rel_context::saturate(scoped_query& sq) { m_context.ensure_closed(); unsigned remaining_time_limit = m_context.soft_timeout(); unsigned restart_time = m_context.initial_restart_timeout(); bool time_limit = remaining_time_limit != 0; instruction_block termination_code; lbool result; TRACE("dl", m_context.display(tout);); while (true) { m_ectx.reset(); m_code.reset(); termination_code.reset(); m_context.ensure_closed(); transform_rules(); if (m_context.canceled()) { TRACE("dl", tout << "canceled\n";); result = l_undef; break; } TRACE("dl", m_context.display(tout);); //IF_VERBOSE(3, m_context.display_smt2(0,0,verbose_stream());); if (m_context.print_aig().size()) { const char *filename = static_cast(m_context.print_aig().c_ptr()); aig_exporter aig(m_context.get_rules(), get_context(), &m_table_facts); std::ofstream strm(filename, std::ios_base::binary); aig(strm); exit(0); } ::stopwatch sw; sw.start(); compiler::compile(m_context, m_context.get_rules(), m_code, termination_code); bool timeout_after_this_round = time_limit && (restart_time==0 || remaining_time_limit<=restart_time); if (time_limit || restart_time!=0) { unsigned timeout = time_limit ? (restart_time!=0) ? std::min(remaining_time_limit, restart_time) : remaining_time_limit : restart_time; m_ectx.set_timelimit(timeout); } bool early_termination = !m_code.perform(m_ectx); m_ectx.reset_timelimit(); VERIFY( termination_code.perform(m_ectx) || m_context.canceled()); m_code.process_all_costs(); sw.stop(); m_sw += sw.get_seconds(); IF_VERBOSE(10, m_ectx.report_big_relations(1000, verbose_stream());); if (m_context.canceled()) { TRACE("dl", tout << "canceled\n";); result = l_undef; break; } if (!early_termination) { m_context.set_status(OK); result = l_true; break; } if (memory::above_high_watermark()) { m_context.set_status(MEMOUT); result = l_undef; break; } if (timeout_after_this_round) { m_context.set_status(TIMEOUT); TRACE("dl", tout << "timeout\n";); result = l_undef; break; } SASSERT(restart_time != 0); if (time_limit) { SASSERT(remaining_time_limit>restart_time); remaining_time_limit -= restart_time; } uint64_t new_restart_time = static_cast(restart_time)*m_context.initial_restart_timeout(); if (new_restart_time > UINT_MAX) { restart_time = UINT_MAX; } else { restart_time = static_cast(new_restart_time); } sq.reset(); } m_context.record_transformed_rules(); TRACE("dl", display_profile(tout);); return result; } lbool rel_context::query(unsigned num_rels, func_decl * const* rels) { setup_default_relation(); get_rmanager().reset_saturated_marks(); scoped_query _scoped_query(m_context); for (unsigned i = 0; i < num_rels; ++i) { m_context.set_output_predicate(rels[i]); } m_context.close(); reset_negated_tables(); lbool res = saturate(_scoped_query); switch(res) { case l_true: { const rule_set& rules = m_context.get_rules(); expr_ref_vector ans(m); expr_ref e(m); bool some_non_empty = num_rels == 0; bool is_approx = false; for (unsigned i = 0; i < num_rels; ++i) { func_decl* q = rules.get_pred(rels[i]); relation_base& rel = get_relation(q); if (!rel.empty()) { some_non_empty = true; } if (!rel.is_precise()) { is_approx = true; } rel.to_formula(e); #if 0 // Alternative format: // List the signature of the relation as // part of the answer. expr_ref_vector args(m); for (unsigned j = 0; j < q->get_arity(); ++j) { args.push_back(m.mk_var(j, q->get_domain(j))); } e = m.mk_implies(m.mk_app(q, args.size(), args.c_ptr()), e); #endif ans.push_back(e); } SASSERT(!m_last_result_relation); if (some_non_empty) { m_answer = mk_and(m, ans.size(), ans.c_ptr()); if (is_approx) { TRACE("dl", tout << "approx\n";); res = l_undef; m_context.set_status(APPROX); } } else { m_answer = m.mk_false(); res = l_false; } break; } case l_false: m_answer = m.mk_false(); break; case l_undef: TRACE("dl", tout << "saturation in undef\n";); break; } return res; } void rel_context::transform_rules() { rule_transformer transf(m_context); transf.register_plugin(alloc(mk_coi_filter, m_context)); transf.register_plugin(alloc(mk_filter_rules, m_context)); transf.register_plugin(alloc(mk_simple_joins, m_context)); if (m_context.unbound_compressor()) { transf.register_plugin(alloc(mk_unbound_compressor, m_context)); } if (m_context.similarity_compressor()) { transf.register_plugin(alloc(mk_similarity_compressor, m_context)); } transf.register_plugin(alloc(mk_rule_inliner, m_context)); transf.register_plugin(alloc(mk_interp_tail_simplifier, m_context)); transf.register_plugin(alloc(mk_separate_negated_tails, m_context)); if (m_context.xform_bit_blast()) { transf.register_plugin(alloc(mk_bit_blast, m_context, 22000)); transf.register_plugin(alloc(mk_interp_tail_simplifier, m_context, 21000)); } m_context.transform_rules(transf); } bool rel_context::try_get_size(func_decl* p, unsigned& rel_size) const { relation_base* rb = try_get_relation(p); if (rb && rb->knows_exact_size()) { rel_size = rb->get_size_estimate_rows(); return true; } else { return false; } } lbool rel_context::query(expr* query) { setup_default_relation(); get_rmanager().reset_saturated_marks(); scoped_query _scoped_query(m_context); rule_manager& rm = m_context.get_rule_manager(); func_decl_ref query_pred(m); try { query_pred = rm.mk_query(query, m_context.get_rules()); } catch (default_exception& exn) { m_context.set_status(INPUT_ERROR); throw exn; } m_context.close(); reset_negated_tables(); if (m_context.generate_explanations()) { m_context.transform_rules(alloc(mk_explanations, m_context)); } query_pred = m_context.get_rules().get_pred(query_pred); if (m_context.magic_sets_for_queries()) { m_context.transform_rules(alloc(mk_magic_sets, m_context, query_pred)); query_pred = m_context.get_rules().get_pred(query_pred); } lbool res = saturate(_scoped_query); query_pred = m_context.get_rules().get_pred(query_pred); if (res != l_undef) { m_last_result_relation = get_relation(query_pred).clone(); if (m_last_result_relation->empty()) { res = l_false; m_answer = m.mk_false(); } else { m_last_result_relation->to_formula(m_answer); if (!m_last_result_relation->is_precise()) { m_context.set_status(APPROX); TRACE("dl", tout << "approx\n";); res = l_undef; } } } return res; } void rel_context::reset_negated_tables() { const rule_set& all_rules = m_context.get_rules(); rule_set::pred_set_vector const & pred_sets = all_rules.get_strats(); bool non_empty = false; for (unsigned i = 1; i < pred_sets.size(); ++i) { func_decl_set::iterator it = pred_sets[i]->begin(), end = pred_sets[i]->end(); for (; it != end; ++it) { func_decl* pred = *it; relation_base & rel = get_relation(pred); if (!rel.fast_empty()) { non_empty = true; break; } } } if (!non_empty) { return; } // collect predicates that depend on negation. func_decl_set depends_on_negation; for (unsigned i = 1; i < pred_sets.size(); ++i) { bool change = true; while (change) { change = false; func_decl_set::iterator it = pred_sets[i]->begin(), end = pred_sets[i]->end(); for (; it != end; ++it) { func_decl* pred = *it; if (depends_on_negation.contains(pred)) { continue; } rule_vector const& rules = all_rules.get_predicate_rules(pred); bool inserted = false; for (unsigned j = 0; !inserted && j < rules.size(); ++j) { rule* r = rules[j]; unsigned psz = r->get_positive_tail_size(); unsigned tsz = r->get_uninterpreted_tail_size(); if (psz < tsz) { depends_on_negation.insert(pred); change = true; inserted = true; } for (unsigned k = 0; !inserted && k < tsz; ++k) { func_decl* tail_decl = r->get_tail(k)->get_decl(); if (depends_on_negation.contains(tail_decl)) { depends_on_negation.insert(pred); change = true; inserted = true; } } } } } } func_decl_set::iterator it = depends_on_negation.begin(), end = depends_on_negation.end(); for (; it != end; ++it) { func_decl* pred = *it; relation_base & rel = get_relation(pred); if (!rel.empty()) { TRACE("dl", tout << "Resetting: " << mk_ismt2_pp(pred, m) << "\n";); rel.reset(); } } } void rel_context::restrict_predicates(func_decl_set const& predicates) { get_rmanager().restrict_predicates(predicates); } relation_base & rel_context::get_relation(func_decl * pred) { return get_rmanager().get_relation(pred); } relation_base * rel_context::try_get_relation(func_decl * pred) const { return get_rmanager().try_get_relation(pred); } expr_ref rel_context::try_get_formula(func_decl* p) const { expr_ref result(m); relation_base* rb = try_get_relation(p); if (rb) { rb->to_formula(result); } return result; } bool rel_context::is_empty_relation(func_decl* pred) const { relation_base* rb = try_get_relation(pred); return !rb || rb->fast_empty(); } relation_manager & rel_context::get_rmanager() { return m_rmanager; } const relation_manager & rel_context::get_rmanager() const { return m_rmanager; } bool rel_context::output_profile() const { return m_context.output_profile(); } void rel_context::set_predicate_representation(func_decl * pred, unsigned relation_name_cnt, symbol const * relation_names) { TRACE("dl", tout << pred->get_name() << ": "; for (unsigned i = 0; i < relation_name_cnt; ++i) { tout << relation_names[i] << " "; } tout << "\n"; ); relation_manager & rmgr = get_rmanager(); family_id target_kind = null_family_id; switch (relation_name_cnt) { case 0: return; case 1: target_kind = get_ordinary_relation_plugin(relation_names[0]).get_kind(); break; default: { rel_spec rel_kinds; // kinds of plugins that are not table plugins family_id rel_kind; // the aggregate kind of non-table plugins for (unsigned i = 0; i < relation_name_cnt; i++) { relation_plugin & p = get_ordinary_relation_plugin(relation_names[i]); rel_kinds.push_back(p.get_kind()); } if (rel_kinds.size() == 1) { rel_kind = rel_kinds[0]; } else { relation_signature rel_sig; rmgr.from_predicate(pred, rel_sig); product_relation_plugin & prod_plugin = product_relation_plugin::get_plugin(rmgr); rel_kind = prod_plugin.get_relation_kind(rel_sig, rel_kinds); } target_kind = rel_kind; break; } } SASSERT(target_kind != null_family_id); get_rmanager().set_predicate_kind(pred, target_kind); } void rel_context::setup_default_relation() { if (m_context.default_relation() == symbol("doc")) { m_context.set_unbound_compressor(false); } } relation_plugin & rel_context::get_ordinary_relation_plugin(symbol relation_name) { relation_plugin * plugin = get_rmanager().get_relation_plugin(relation_name); if (!plugin) { std::stringstream sstm; sstm << "relation plugin " << relation_name << " does not exist"; throw default_exception(sstm.str()); } if (plugin->is_product_relation()) { throw default_exception("cannot request product relation directly"); } if (plugin->is_sieve_relation()) { throw default_exception("cannot request sieve relation directly"); } if (plugin->is_finite_product_relation()) { throw default_exception("cannot request finite product relation directly"); } return *plugin; } bool rel_context::result_contains_fact(relation_fact const& f) { SASSERT(m_last_result_relation); return m_last_result_relation->contains_fact(f); } void rel_context::add_fact(func_decl* pred, relation_fact const& fact) { get_rmanager().reset_saturated_marks(); get_relation(pred).add_fact(fact); if (m_context.print_aig().size()) { m_table_facts.push_back(std::make_pair(pred, fact)); } } void rel_context::add_fact(func_decl* pred, table_fact const& fact) { get_rmanager().reset_saturated_marks(); relation_base & rel0 = get_relation(pred); if (rel0.from_table()) { table_relation & rel = static_cast(rel0); rel.add_table_fact(fact); // TODO: table facts? } else { relation_fact rfact(m); for (unsigned i = 0; i < fact.size(); ++i) { rfact.push_back(m_context.get_decl_util().mk_numeral(fact[i], pred->get_domain()[i])); } add_fact(pred, rfact); } } bool rel_context::has_facts(func_decl * pred) const { relation_base* r = try_get_relation(pred); return r && !r->empty(); } void rel_context::store_relation(func_decl * pred, relation_base * rel) { get_rmanager().store_relation(pred, rel); } void rel_context::collect_statistics(statistics& st) const { st.update("saturation time", m_sw); m_code.collect_statistics(st); m_ectx.collect_statistics(st); } void rel_context::updt_params() { if (m_context.check_relation() != symbol::null && m_context.check_relation() != symbol("null")) { symbol cr("check_relation"); m_context.set_default_relation(cr); relation_plugin* p = get_rmanager().get_relation_plugin(cr); SASSERT(p); check_relation_plugin* p1 = dynamic_cast(p); relation_plugin* p2 = get_rmanager().get_relation_plugin(m_context.check_relation()); SASSERT(p2); SASSERT(p1 != p2); p1->set_plugin(p2); get_rmanager().set_favourite_plugin(p1); if (m_context.check_relation() == symbol("doc")) { m_context.set_unbound_compressor(false); } } } void rel_context::inherit_predicate_kind(func_decl* new_pred, func_decl* orig_pred) { if (orig_pred) { family_id target_kind = get_rmanager().get_requested_predicate_kind(orig_pred); if (target_kind != null_family_id) { get_rmanager().set_predicate_kind(new_pred, target_kind); } } } void rel_context::display_output_facts(rule_set const& rules, std::ostream & out) const { get_rmanager().display_output_tables(rules, out); } void rel_context::display_facts(std::ostream& out) const { get_rmanager().display(out); } void rel_context::display_profile(std::ostream& out) { m_code.make_annotations(m_ectx); m_code.process_all_costs(); out << "Big relations\n"; m_ectx.report_big_relations(1000, out); get_rmanager().display_relation_sizes(out); } }; z3-z3-4.8.7/src/muz/rel/rel_context.h000066400000000000000000000075711356505360400173240ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: rel_context.h Abstract: context for relational datalog engine. Author: Nikolaj Bjorner (nbjorner) 2012-12-3. Revision History: Extracted from dl_context --*/ #ifndef REL_CONTEXT_H_ #define REL_CONTEXT_H_ #include "ast/ast.h" #include "muz/rel/dl_relation_manager.h" #include "muz/rel/dl_instruction.h" #include "muz/base/dl_engine_base.h" #include "muz/base/dl_context.h" #include "util/lbool.h" namespace datalog { class context; typedef vector > fact_vector; class rel_context : public rel_context_base { context& m_context; ast_manager& m; relation_manager m_rmanager; expr_ref m_answer; relation_base * m_last_result_relation; fact_vector m_table_facts; execution_context m_ectx; instruction_block m_code; double m_sw; class scoped_query; void reset_negated_tables(); relation_plugin & get_ordinary_relation_plugin(symbol relation_name); lbool saturate(scoped_query& sq); void setup_default_relation(); public: rel_context(context& ctx); ~rel_context() override; relation_manager & get_rmanager() override; const relation_manager & get_rmanager() const override; ast_manager& get_manager() const { return m; } context& get_context() const { return m_context; } relation_base & get_relation(func_decl * pred) override; relation_base * try_get_relation(func_decl * pred) const override; bool is_empty_relation(func_decl* pred) const override; expr_ref try_get_formula(func_decl * pred) const override; expr_ref get_answer() override { return m_answer; } bool output_profile() const override; lbool query(expr* q) override; lbool query(unsigned num_rels, func_decl * const* rels) override; void set_predicate_representation(func_decl * pred, unsigned relation_name_cnt, symbol const * relation_names) override; void inherit_predicate_kind(func_decl* new_pred, func_decl* orig_pred) override; void collect_statistics(statistics& st) const override; void updt_params() override; /** \brief Restrict the set of used predicates to \c res. The function deallocates unused relations, it does not deal with rules. */ void restrict_predicates(func_decl_set const& predicates) override; void transform_rules() override; bool try_get_size(func_decl* pred, unsigned& rel_size) const override; /** \brief query result if it contains fact. */ bool result_contains_fact(relation_fact const& f) override; void collect_non_empty_predicates(func_decl_set& ps) override { return get_rmanager().collect_non_empty_predicates(ps); } /** \brief add facts to relation */ void add_fact(func_decl* pred, relation_fact const& fact) override; void add_fact(func_decl* pred, table_fact const& fact) override; /** \brief check if facts were added to relation */ bool has_facts(func_decl * pred) const override; /** \brief Store the relation \c rel under the predicate \c pred. The \c context object takes over the ownership of the relation object. */ void store_relation(func_decl * pred, relation_base * rel) override; void display_output_facts(rule_set const& rules, std::ostream & out) const override; void display_facts(std::ostream & out) const override; void display_profile(std::ostream& out) override; lbool saturate() override; }; }; #endif /* REL_CONTEXT_H_ */ z3-z3-4.8.7/src/muz/rel/tbv.cpp000066400000000000000000000203461356505360400161170ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: tbv.cpp Abstract: ternary bit-vector utilities. Author: Nikolaj Bjorner (nbjorner) 2014-09-15 Revision History: --*/ #include "muz/rel/tbv.h" #include "util/hashtable.h" #include "ast/ast_util.h" static bool s_debug_alloc = false; void tbv_manager::debug_alloc() { s_debug_alloc = true; } tbv_manager::~tbv_manager() { DEBUG_CODE( ptr_vector::iterator it = allocated_tbvs.begin(); ptr_vector::iterator end = allocated_tbvs.end(); for (; it != end; ++it) { std::cout << "dangling: " << (*it) << "\n"; TRACE("doc", tout << "dangling: " << (*it) << "\n";); }); } void tbv_manager::reset() { m.reset(); } tbv* tbv_manager::allocate() { tbv* r = static_cast(m.allocate()); DEBUG_CODE( if (s_debug_alloc) { TRACE("doc", tout << allocated_tbvs.size() << " " << r << "\n";); } allocated_tbvs.insert(r); ); return r; } tbv* tbv_manager::allocate1() { tbv* v = allocate(); fill1(*v); return v; } tbv* tbv_manager::allocate0() { tbv* v = allocate(); fill0(*v); return v; } tbv* tbv_manager::allocateX() { tbv* v = allocate(); fillX(*v); return v; } tbv* tbv_manager::allocate(tbv const& bv) { tbv* r = allocate(); copy(*r, bv); return r; } tbv* tbv_manager::allocate(uint64_t val) { tbv* v = allocate0(); for (unsigned bit = std::min(64u, num_tbits()); bit-- > 0;) { if (val & (1ULL << bit)) { set(*v, bit, BIT_1); } else { set(*v, bit, BIT_0); } } return v; } tbv* tbv_manager::allocate(uint64_t val, unsigned hi, unsigned lo) { tbv* v = allocateX(); SASSERT(64 >= num_tbits() && num_tbits() > hi && hi >= lo); set(*v, val, hi, lo); return v; } tbv* tbv_manager::allocate(tbv const& bv, unsigned const* permutation) { tbv* r = allocate(); unsigned sz = num_tbits(); for (unsigned i = 0; i < sz; ++i) { set(*r, permutation[i], bv[i]); } return r; } tbv* tbv_manager::allocate(char const* bv) { tbv* result = allocateX(); unsigned i = 0, sz = num_tbits(); while(*bv && i < sz) { if (*bv == '0') set(*result, i++, BIT_0); else if (*bv == '1') set(*result, i++, BIT_1); else if (*bv == '*') i++; else if (*bv == 'x') i++; else if (i == 0 && (*bv == ' ' || *bv == '\t')) ; else break; ++bv; } return result; } tbv* tbv_manager::project(bit_vector const& to_delete, tbv const& src) { tbv* r = allocate(); unsigned i, j; unsigned n = to_delete.size(); for (i = 0, j = 0; i < n; ++i) { if (!to_delete.get(i)) { set(*r, j, src[i]); ++j; } } SASSERT(num_tbits() == j); return r; } void tbv_manager::set(tbv& dst, unsigned index, tbit value) { SASSERT(index < num_tbits()); m.set(dst, 2*index, (value & 2) != 0); m.set(dst, 2*index+1, (value & 1) != 0); } void tbv_manager::set(tbv& dst, uint64_t val, unsigned hi, unsigned lo) { SASSERT(lo <= hi && hi < num_tbits()); for (unsigned i = 0; i < hi - lo + 1; ++i) { set(dst, lo + i, (val & (1ULL << i))?BIT_1:BIT_0); } } void tbv_manager::set(tbv& dst, rational const& r, unsigned hi, unsigned lo) { SASSERT(lo <= hi && hi < num_tbits()); if (r.is_uint64()) { set(dst, r.get_uint64(), hi, lo); return; } for (unsigned i = 0; i < hi - lo + 1; ++i) { if (bitwise_and(r, rational::power_of_two(i)).is_zero()) set(dst, lo + i, BIT_0); else set(dst, lo + i, BIT_1); } } void tbv_manager::set(tbv& dst, tbv const& other, unsigned hi, unsigned lo) { dst.set(other, 2*hi+1, 2*lo); } tbv* tbv_manager::allocate(rational const& r) { if (r.is_uint64()) { return allocate(r.get_uint64()); } tbv* v = allocate0(); for (unsigned bit = num_tbits(); bit > 0; ) { --bit; if (bitwise_and(r, rational::power_of_two(bit)).is_zero()) { set(*v, bit, BIT_0); } else { set(*v, bit, BIT_1); } } return v; } void tbv_manager::deallocate(tbv* bv) { DEBUG_CODE( if (!allocated_tbvs.contains(bv)) { std::cout << "double deallocate: " << bv << "\n"; UNREACHABLE(); } if (s_debug_alloc) { TRACE("doc", tout << "deallocate: " << bv << "\n";); } allocated_tbvs.erase(bv);); m.deallocate(bv); } void tbv_manager::copy(tbv& dst, tbv const& src) const { m.copy(dst, src); } tbv& tbv_manager::fill0(tbv& bv) const { // 10101010 = 2 + 8 + 32 + 128 memset(bv.m_data, 2 + 8 + 32 + 128, m.num_bytes()); return bv; } tbv& tbv_manager::fill1(tbv& bv) const { // 01010101 = 1 + 4 + 16 + 64 memset(bv.m_data, 1 + 4 + 16 + 64, m.num_bytes()); return bv; } tbv& tbv_manager::fillX(tbv& bv) const { m.fill1(bv); return bv; } tbv& tbv_manager::set_or(tbv& dst, tbv const& src) const { m.set_or(dst, src); return dst; } bool tbv_manager::set_and(tbv& dst, tbv const& src) const { m.set_and(dst, src); return is_well_formed(dst); } bool tbv_manager::is_well_formed(tbv const& dst) const { unsigned nw = m.num_words(); unsigned w; for (unsigned i = 0; i + 1 < nw; ++i) { w = dst.get_word(i); w = w | (w << 1) | 0x55555555; if (w != 0xFFFFFFFF) return false; } if (nw > 0) { w = m.last_word(dst); w = w | (w << 1) | 0x55555555 | ~m.get_mask(); if (w != 0xFFFFFFFF) return false; } return true; } void tbv_manager::complement(tbv const& src, ptr_vector& result) { tbv* r; unsigned n = num_tbits(); for (unsigned i = 0; i < n; ++i) { switch (src.get(i)) { case BIT_0: r = allocate(src); set(*r, i, BIT_1); result.push_back(r); break; case BIT_1: r = allocate(src); set(*r, i, BIT_0); result.push_back(r); break; default: break; } } } bool tbv_manager::equals(tbv const& a, tbv const& b) const { return m.equals(a, b); } unsigned tbv_manager::hash(tbv const& src) const { return m.hash(src); } bool tbv_manager::contains(tbv const& a, tbv const& b) const { return m.contains(a, b); } bool tbv_manager::contains(tbv const& a, unsigned_vector const& colsa, tbv const& b, unsigned_vector const& colsb) const { for (unsigned i = 0; i < colsa.size(); ++i) { tbit bit_a = a[colsa[i]]; if (bit_a == BIT_x) continue; if (bit_a != b[colsb[i]]) return false; } return true; } bool tbv_manager::intersect(tbv const& a, tbv const& b, tbv& result) { copy(result, a); return set_and(result, b); } std::ostream& tbv_manager::display(std::ostream& out, tbv const& b, unsigned hi, unsigned lo) const { SASSERT(lo <= hi && hi < num_tbits()); for (unsigned i = hi+1; i-- > lo; ) { switch (b.get(i)) { case BIT_0: out << '0'; break; case BIT_1: out << '1'; break; case BIT_x: out << 'x'; break; case BIT_z: out << 'z'; break; default: UNREACHABLE(); } } return out; } std::ostream& tbv_manager::display(std::ostream& out, tbv const& b) const { if (num_tbits() == 0) return out << "[]"; return display(out, b, num_tbits()-1, 0); } expr_ref tbv_manager::to_formula(ast_manager& m, tbv const& src) { expr_ref result(m); expr_ref_vector conj(m); for (unsigned i = 0; i < num_tbits(); ++i) { switch (src[i]) { case BIT_0: conj.push_back(m.mk_not(m.mk_const(symbol(i), m.mk_bool_sort()))); break; case BIT_1: conj.push_back(m.mk_const(symbol(i), m.mk_bool_sort())); break; default: break; } } result = mk_and(m, conj.size(), conj.c_ptr()); return result; } expr_ref tbv_manager::mk_var(ast_manager& m, unsigned i) { return expr_ref(m.mk_const(symbol(i), m.mk_bool_sort()), m); } z3-z3-4.8.7/src/muz/rel/tbv.h000066400000000000000000000073751356505360400155730ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: tbv.h Abstract: ternary bit-vector utilities. Author: Nikolaj Bjorner (nbjorner) 2014-09-15 Revision History: --*/ #ifndef TBV_H_ #define TBV_H_ #include "util/fixed_bit_vector.h" #include "util/rational.h" #include "ast/ast.h" class tbv; enum tbit { BIT_z = 0x0, BIT_0 = 0x1, BIT_1 = 0x2, BIT_x = 0x3 }; inline tbit neg(tbit t) { return (tbit)(t ^ 0x3); } class tbv_manager { friend class tbv; fixed_bit_vector_manager m; ptr_vector allocated_tbvs; public: tbv_manager(unsigned n): m(2*n) {} ~tbv_manager(); void reset(); tbv* allocate(); tbv* allocate1(); tbv* allocate0(); tbv* allocateX(); tbv* allocate(tbv const& bv); tbv* allocate(uint64_t n); tbv* allocate(rational const& r); tbv* allocate(uint64_t n, unsigned hi, unsigned lo); tbv* allocate(tbv const& bv, unsigned const* permutation); tbv* allocate(char const* bv); void deallocate(tbv* bv); unsigned get_size_estimate_bytes(const tbv&) const { return m.num_bytes(); } void copy(tbv& dst, tbv const& src) const; unsigned num_tbits() const { return m.num_bits()/2; } tbv& reset(tbv& bv) const { return fill0(bv); } tbv& fill0(tbv& bv) const; tbv& fill1(tbv& bv) const; tbv& fillX(tbv& bv) const; bool set_and(tbv& dst, tbv const& src) const; tbv& set_or(tbv& dst, tbv const& src) const; void complement(tbv const& src, ptr_vector& result); bool equals(tbv const& a, tbv const& b) const; unsigned hash(tbv const& src) const; bool contains(tbv const& a, tbv const& b) const; bool contains(tbv const& a, unsigned_vector const& colsa, tbv const& b, unsigned_vector const& colsb) const; bool intersect(tbv const& a, tbv const& b, tbv& result); std::ostream& display(std::ostream& out, tbv const& b) const; std::ostream& display(std::ostream& out, tbv const& b, unsigned hi, unsigned lo) const; tbv* project(bit_vector const& to_delete, tbv const& src); bool is_well_formed(tbv const& b) const; // - does not contain BIT_z; void set(tbv& dst, uint64_t n, unsigned hi, unsigned lo); void set(tbv& dst, rational const& r, unsigned hi, unsigned lo); void set(tbv& dst, tbv const& other, unsigned hi, unsigned lo); void set(tbv& dst, unsigned index, tbit value); static void debug_alloc(); expr_ref to_formula(ast_manager& m, tbv const& src); expr_ref mk_var(ast_manager& m, unsigned i); }; class tbv: private fixed_bit_vector { friend class fixed_bit_vector_manager; friend class tbv_manager; public: struct eq { tbv_manager& m; eq(tbv_manager& m):m(m) {} bool operator()(tbv const& d1, tbv const& d2) const { return m.equals(d1, d2); } }; struct hash { tbv_manager& m; hash(tbv_manager& m):m(m) {} unsigned operator()(tbv const& d) const { return m.hash(d); } }; tbit operator[](unsigned idx) const { return (tbit)get(idx); } private: unsigned get(unsigned index) const { index *= 2; return (fixed_bit_vector::get(index) << 1) | (unsigned)fixed_bit_vector::get(index+1); } }; class tbv_ref { tbv_manager& mgr; tbv* d; public: tbv_ref(tbv_manager& mgr):mgr(mgr),d(nullptr) {} tbv_ref(tbv_manager& mgr, tbv* d):mgr(mgr),d(d) {} ~tbv_ref() { if (d) mgr.deallocate(d); } tbv_ref& operator=(tbv* d2) { if (d) mgr.deallocate(d); d = d2; return *this; } tbv& operator*() { return *d; } tbv* operator->() { return d; } tbv* get() { return d; } tbv* detach() { tbv* result = d; d = nullptr; return result; } }; #endif /* TBV_H_ */ z3-z3-4.8.7/src/muz/rel/udoc_relation.cpp000066400000000000000000001363471356505360400201640ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: udoc_relation.cpp Abstract: Relation based on union of DOCs. Author: Nuno Lopes (a-nlopes) 2013-03-01 Nikolaj Bjorner (nbjorner) 2014-09-15 Revision History: Revised version of dl_hassel_diff facilities. Notes: --*/ #include "muz/rel/udoc_relation.h" #include "muz/rel/dl_relation_manager.h" #include "ast/ast_util.h" #include "smt/smt_kernel.h" namespace datalog { udoc_relation::udoc_relation(udoc_plugin& p, relation_signature const& sig): relation_base(p, sig), dm(p.dm(p.num_signature_bits(sig))) { unsigned column = 0; for (unsigned i = 0; i < sig.size(); ++i) { m_column_info.push_back(column); column += p.num_sort_bits(sig[i]); } m_column_info.push_back(column); } udoc_relation::~udoc_relation() { reset(); } void udoc_relation::reset() { m_elems.reset(dm); } void udoc_relation::expand_column_vector(unsigned_vector& v, const udoc_relation* other) const { unsigned_vector orig; orig.swap(v); for (unsigned i = 0; i < orig.size(); ++i) { unsigned col, limit; if (orig[i] < get_num_cols()) { col = column_idx(orig[i]); limit = col + column_num_bits(orig[i]); } else { unsigned idx = orig[i] - get_num_cols(); col = get_num_bits() + other->column_idx(idx); limit = col + other->column_num_bits(idx); } for (; col < limit; ++col) { v.push_back(col); } } } doc* udoc_relation::fact2doc(const relation_fact & f) const { doc* d = dm.allocate0(); for (unsigned i = 0; i < f.size(); ++i) { unsigned bv_size; rational val; VERIFY(get_plugin().is_numeral(f[i], val, bv_size)); SASSERT(bv_size == column_num_bits(i)); unsigned lo = column_idx(i); unsigned hi = column_idx(i + 1); dm.tbvm().set(d->pos(),val, hi-1, lo); } return d; } void udoc_relation::add_fact(const relation_fact & f) { doc* d = fact2doc(f); m_elems.insert(dm, d); } void udoc_relation::add_new_fact(const relation_fact & f) { m_elems.push_back(fact2doc(f)); } bool udoc_relation::empty() const { return m_elems.is_empty_complete(get_plugin().m, dm); } bool udoc_relation::contains_fact(const relation_fact & f) const { doc_ref d(dm, fact2doc(f)); return m_elems.contains(dm, *d); } udoc_relation * udoc_relation::clone() const { udoc_relation* result = udoc_plugin::get(get_plugin().mk_empty(get_signature())); for (unsigned i = 0; i < m_elems.size(); ++i) { result->m_elems.push_back(dm.allocate(m_elems[i])); } return result; } udoc_relation * udoc_relation::complement(func_decl* f) const { udoc_relation* result = udoc_plugin::get(get_plugin().mk_empty(get_signature())); m_elems.complement(dm, result->m_elems); return result; } void udoc_relation::to_formula(expr_ref& fml) const { ast_manager& m = fml.get_manager(); expr_ref_vector disj(m); for (unsigned i = 0; i < m_elems.size(); ++i) { disj.push_back(to_formula(m_elems[i])); } fml = mk_or(m, disj.size(), disj.c_ptr()); } expr_ref udoc_relation::to_formula(doc const& d) const { ast_manager& m = get_plugin().get_ast_manager(); expr_ref result(m); expr_ref_vector conjs(m); conjs.push_back(to_formula(d.pos())); for (unsigned i = 0; i < d.neg().size(); ++i) { conjs.push_back(m.mk_not(to_formula(d.neg()[i]))); } result = mk_and(m, conjs.size(), conjs.c_ptr()); return result; } expr_ref udoc_relation::to_formula(tbv const& t) const { udoc_plugin& p = get_plugin(); ast_manager& m = p.get_ast_manager(); expr_ref result(m); expr_ref_vector conjs(m); for (unsigned i = 0; i < get_num_cols(); ++i) { var_ref v(m); v = m.mk_var(i, get_signature()[i]); unsigned lo = column_idx(i); unsigned hi = column_idx(i+1); rational r(0); unsigned lo1 = lo; bool is_x = true; for (unsigned j = lo; j < hi; ++j) { switch(t[j]) { case BIT_0: if (is_x) is_x = false, lo1 = j, r.reset(); break; case BIT_1: if (is_x) is_x = false, lo1 = j, r.reset(); r += rational::power_of_two(j - lo1); break; case BIT_x: if (!is_x) { SASSERT(p.bv.is_bv_sort(get_signature()[i])); conjs.push_back(m.mk_eq(p.bv.mk_extract(j-1-lo,lo1-lo,v), p.bv.mk_numeral(r,j-lo1))); } is_x = true; break; default: UNREACHABLE(); } } if (!is_x) { expr_ref num(m); if (lo1 == lo) { num = p.mk_numeral(r, get_signature()[i]); conjs.push_back(m.mk_eq(v, num)); } else { num = p.bv.mk_numeral(r, hi-lo1); conjs.push_back(m.mk_eq(p.bv.mk_extract(hi-1-lo,lo1-lo,v), num)); } } } result = mk_and(m, conjs.size(), conjs.c_ptr()); return result; } udoc_plugin& udoc_relation::get_plugin() const { return static_cast(relation_base::get_plugin()); } void udoc_relation::display(std::ostream& out) const { m_elems.display(dm, out); out << "\n"; } unsigned udoc_relation::get_size_estimate_bytes() const { return sizeof(*this) + m_elems.get_size_estimate_bytes(dm); } // ------------- udoc_plugin::udoc_plugin(relation_manager& rm): relation_plugin(udoc_plugin::get_name(), rm), m(rm.get_context().get_manager()), bv(m), dl(m), m_disable_fast_pass(false) { } udoc_plugin::~udoc_plugin() { u_map::iterator it = m_dms.begin(), end = m_dms.end(); for (; it != end; ++it) { dealloc(it->m_value); } } udoc_relation& udoc_plugin::get(relation_base& r) { return dynamic_cast(r); } udoc_relation* udoc_plugin::get(relation_base* r) { return r?dynamic_cast(r):nullptr; } udoc_relation const & udoc_plugin::get(relation_base const& r) { return dynamic_cast(r); } doc_manager& udoc_plugin::dm(relation_signature const& sig) { return dm(num_signature_bits(sig)); } doc_manager& udoc_plugin::dm(unsigned n) { doc_manager* r; if (!m_dms.find(n, r)) { r = alloc(doc_manager, n); m_dms.insert(n, r); } return *r; } bool udoc_relation::is_var_range(expr* e, unsigned& hi, unsigned& lo, unsigned& v) const { udoc_plugin& p = get_plugin(); if (is_var(e)) { v = to_var(e)->get_idx(); hi = p.num_sort_bits(e)-1; lo = 0; return true; } expr* e2; if (p.bv.is_extract(e, lo, hi, e2) && is_var(e2)) { v = to_var(e2)->get_idx(); SASSERT(lo <= hi); return true; } return false; } expr* udoc_plugin::mk_numeral(rational const& r, sort* s) { if (bv.is_bv_sort(s)) { return bv.mk_numeral(r, s); } if (m.is_bool(s)) { if (r.is_zero()) return m.mk_false(); return m.mk_true(); } SASSERT(dl.is_finite_sort(s)); return dl.mk_numeral(r.get_uint64(), s); } bool udoc_plugin::is_numeral(expr* e, rational& r, unsigned& num_bits) { if (bv.is_numeral(e, r, num_bits)) return true; if (m.is_true(e)) { r = rational(1); num_bits = 1; return true; } if (m.is_false(e)) { r = rational(0); num_bits = 1; return true; } uint64_t n, sz; ast_manager& m = get_ast_manager(); if (dl.is_numeral(e, n) && dl.try_get_size(m.get_sort(e), sz)) { num_bits = 0; while (sz > 0) ++num_bits, sz = sz/2; r = rational(n, rational::ui64()); return true; } return false; } unsigned udoc_plugin::num_sort_bits(sort* s) const { unsigned num_bits = 0; if (bv.is_bv_sort(s)) return bv.get_bv_size(s); if (m.is_bool(s)) return 1; uint64_t sz; if (dl.try_get_size(s, sz)) { while (sz > 0) ++num_bits, sz /= 2; return num_bits; } UNREACHABLE(); return 0; } unsigned udoc_plugin::num_signature_bits(relation_signature const& sig) { unsigned result = 0; for (unsigned i = 0; i < sig.size(); ++i) { result += num_sort_bits(sig[i]); } return result; } bool udoc_plugin::is_finite_sort(sort* s) const { return bv.is_bv_sort(s) || dl.is_finite_sort(s); } bool udoc_plugin::can_handle_signature(const relation_signature & sig) { for (unsigned i = 0; i < sig.size(); ++i) { if (!is_finite_sort(sig[i])) return false; } return true; } relation_base * udoc_plugin::mk_empty(const relation_signature & sig) { return alloc(udoc_relation, *this, sig); } relation_base * udoc_plugin::mk_full(func_decl* p, const relation_signature & s) { udoc_relation* r = get(mk_empty(s)); r->get_udoc().push_back(dm(s).allocateX()); return r; } class udoc_plugin::join_fn : public convenient_relation_join_fn { doc_manager& dm; doc_manager& dm1; doc_manager& dm2; public: join_fn(udoc_plugin& p, udoc_relation const& t1, udoc_relation const& t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) : convenient_relation_join_fn(t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2), dm(p.dm(get_result_signature())), dm1(t1.get_dm()), dm2(t2.get_dm()) { t1.expand_column_vector(m_cols1); t2.expand_column_vector(m_cols2); } relation_base * operator()(const relation_base & _r1, const relation_base & _r2) override { udoc_relation const& r1 = get(_r1); udoc_relation const& r2 = get(_r2); TRACE("doc", r1.display(tout << "r1:\n"); r2.display(tout << "r2:\n");); udoc_plugin& p = r1.get_plugin(); relation_signature const& sig = get_result_signature(); udoc_relation * result = alloc(udoc_relation, p, sig); udoc const& d1 = r1.get_udoc(); udoc const& d2 = r2.get_udoc(); udoc& r = result->get_udoc(); r.join(d1, d2, dm, dm1, m_cols1, m_cols2); TRACE("doc", result->display(tout << "result:\n");); IF_VERBOSE(3, result->display(verbose_stream() << "join result:\n");); SASSERT(r.well_formed(result->get_dm())); return result; } }; relation_join_fn * udoc_plugin::mk_join_fn( const relation_base & t1, const relation_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { if (!check_kind(t1) || !check_kind(t2)) { return nullptr; } return alloc(join_fn, *this, get(t1), get(t2), col_cnt, cols1, cols2); } class udoc_plugin::project_fn : public convenient_relation_project_fn { bit_vector m_to_delete; public: project_fn(udoc_relation const & t, unsigned removed_col_cnt, const unsigned * removed_cols) : convenient_relation_project_fn(t.get_signature(), removed_col_cnt, removed_cols) { t.expand_column_vector(m_removed_cols); unsigned n = t.get_dm().num_tbits(); m_to_delete.resize(n, false); for (unsigned i = 0; i < m_removed_cols.size(); ++i) { m_to_delete.set(m_removed_cols[i], true); } } relation_base * operator()(const relation_base & tb) override { TRACE("doc", tb.display(tout << "src:\n");); udoc_relation const& t = get(tb); udoc_plugin& p = t.get_plugin(); udoc_relation* r = udoc_plugin::get(p.mk_empty(get_result_signature())); doc_manager& dm1 = t.get_dm(); doc_manager& dm2 = r->get_dm(); doc_ref d2(dm2); udoc const& ud1 = t.get_udoc(); udoc& ud2 = r->get_udoc(); for (unsigned i = 0; i < ud1.size(); ++i) { d2 = dm1.project(dm2, m_to_delete, ud1[i]); ud2.push_back(d2.detach()); } TRACE("doc", tout << "final size: " << r->get_size_estimate_rows() << '\n';); SASSERT(ud2.well_formed(dm2)); return r; } }; relation_transformer_fn * udoc_plugin::mk_project_fn( const relation_base & t, unsigned col_cnt, const unsigned * removed_cols) { if (!check_kind(t)) return nullptr; return alloc(project_fn, get(t), col_cnt, removed_cols); } class udoc_plugin::rename_fn : public convenient_relation_rename_fn { unsigned_vector m_permutation; public: rename_fn(udoc_relation const& t, unsigned cycle_len, const unsigned * cycle) : convenient_relation_rename_fn(t.get_signature(), cycle_len, cycle) { udoc_plugin& p = t.get_plugin(); relation_signature const& sig1 = t.get_signature(); relation_signature const& sig2 = get_result_signature(); unsigned_vector permutation0, column_info; for (unsigned i = 0; i < t.get_num_bits(); ++i) { m_permutation.push_back(i); } for (unsigned i = 0; i < sig1.size(); ++i) { permutation0.push_back(i); } for (unsigned i = 0; i < cycle_len; ++i) { unsigned j = (i + 1)%cycle_len; unsigned col1 = cycle[i]; unsigned col2 = cycle[j]; permutation0[col2] = col1; } unsigned column = 0; for (unsigned i = 0; i < sig2.size(); ++i) { column_info.push_back(column); column += p.num_sort_bits(sig2[i]); } column_info.push_back(column); SASSERT(column == t.get_num_bits()); TRACE("doc", ast_manager& m = p.get_ast_manager(); sig1.output(m, tout << "sig1: "); tout << "\n"; sig2.output(m, tout << "sig2: "); tout << "\n"; tout << "permute: "; for (unsigned i = 0; i < permutation0.size(); ++i) { tout << permutation0[i] << " "; } tout << "\n"; tout << "cycle: "; for (unsigned i = 0; i < cycle_len; ++i) { tout << cycle[i] << " "; } tout << "\n"; ); // 0 -> 2 // [3:2:1] -> [1:2:3] // [3,4,5,1,2,0] for (unsigned i = 0; i < sig1.size(); ++i) { unsigned len = t.column_num_bits(i); unsigned lo1 = t.column_idx(i); unsigned col2 = permutation0[i]; unsigned lo2 = column_info[col2]; SASSERT(lo2 + len <= t.get_num_bits()); SASSERT(lo1 + len <= t.get_num_bits()); for (unsigned k = 0; k < len; ++k) { m_permutation[k + lo1] = k + lo2; } } } relation_base * operator()(const relation_base & _r) override { udoc_relation const& r = get(_r); TRACE("doc", r.display(tout << "r:\n");); udoc_plugin& p = r.get_plugin(); relation_signature const& sig = get_result_signature(); udoc_relation* result = alloc(udoc_relation, p, sig); udoc const& src = r.get_udoc(); udoc& dst = result->get_udoc(); doc_manager& dm = r.get_dm(); SASSERT(&result->get_dm() == &dm); for (unsigned i = 0; i < src.size(); ++i) { dst.push_back(dm.allocate(src[i], m_permutation.c_ptr())); } TRACE("doc", result->display(tout << "result:\n");); SASSERT(dst.well_formed(dm)); return result; } }; relation_transformer_fn * udoc_plugin::mk_rename_fn( const relation_base & r, unsigned cycle_len, const unsigned * permutation_cycle) { if (check_kind(r)) { return alloc(rename_fn, get(r), cycle_len, permutation_cycle); } else { return nullptr; } } class udoc_plugin::union_fn : public relation_union_fn { public: union_fn() {} void operator()(relation_base & _r, const relation_base & _src, relation_base * _delta) override { TRACE("doc", _r.display(tout << "dst:\n"); _src.display(tout << "src:\n");); udoc_relation& r = get(_r); udoc_relation const& src = get(_src); udoc_relation* d = get(_delta); doc_manager& dm = r.get_dm(); udoc* d1 = nullptr; if (d) d1 = &d->get_udoc(); IF_VERBOSE(3, r.display(verbose_stream() << "orig: ");); r.get_plugin().mk_union(dm, r.get_udoc(), src.get_udoc(), d1); SASSERT(r.get_udoc().well_formed(dm)); SASSERT(!d1 || d1->well_formed(dm)); TRACE("doc", _r.display(tout << "dst':\n"); ); IF_VERBOSE(3, r.display(verbose_stream() << "union: ");); IF_VERBOSE(3, if (d) d->display(verbose_stream() << "delta: ");); } }; void udoc_plugin::mk_union(doc_manager& dm, udoc& dst, udoc const& src, udoc* delta) { bool deltaempty = delta ? delta->is_empty() : false; if (dst.is_empty()) { for (unsigned i = 0; i < src.size(); ++i) { dst.push_back(dm.allocate(src[i])); if (delta) { if (deltaempty) delta->push_back(dm.allocate(src[i])); else delta->insert(dm, dm.allocate(src[i])); } } } else { for (unsigned i = 0; i < src.size(); ++i) { if (dst.insert(dm, dm.allocate(src[i])) && delta) { if (deltaempty) delta->push_back(dm.allocate(src[i])); else delta->insert(dm, dm.allocate(src[i])); } } } } relation_union_fn * udoc_plugin::mk_union_fn( const relation_base & tgt, const relation_base & src, const relation_base * delta) { if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) { return nullptr; } return alloc(union_fn); } relation_union_fn * udoc_plugin::mk_widen_fn( const relation_base & tgt, const relation_base & src, const relation_base * delta) { return mk_union_fn(tgt, src, delta); } class udoc_plugin::filter_identical_fn : public relation_mutator_fn { unsigned_vector m_cols; unsigned m_size; bit_vector m_empty_bv; union_find_default_ctx union_ctx; union_find<> m_equalities; public: filter_identical_fn(const relation_base & _r, unsigned col_cnt, const unsigned *identical_cols) : m_cols(col_cnt), m_equalities(union_ctx) { udoc_relation const& r = get(_r); m_size = r.column_num_bits(identical_cols[0]); m_empty_bv.resize(r.get_num_bits(), false); for (unsigned i = 0; i < col_cnt; ++i) { m_cols[i] = r.column_idx(identical_cols[i]); } for (unsigned i = 0, e = m_empty_bv.size(); i < e; ++i) { m_equalities.mk_var(); } for (unsigned i = 1; i < col_cnt; ++i) { for (unsigned j = 0; j < m_size; ++j) { m_equalities.merge(m_cols[0]+j ,m_cols[i]+j); } } } void operator()(relation_base & _r) override { udoc_relation& r = get(_r); udoc& d = r.get_udoc(); doc_manager& dm = r.get_dm(); d.merge(dm, m_cols[0], m_size, m_equalities, m_empty_bv); SASSERT(d.well_formed(dm)); TRACE("doc", tout << "final size: " << r.get_size_estimate_rows() << '\n';); } }; relation_mutator_fn * udoc_plugin::mk_filter_identical_fn( const relation_base & t, unsigned col_cnt, const unsigned * identical_cols) { return check_kind(t)?alloc(filter_identical_fn, t, col_cnt, identical_cols):nullptr; } class udoc_plugin::filter_equal_fn : public relation_mutator_fn { doc_manager& dm; doc* m_filter; public: filter_equal_fn(udoc_plugin& p, const udoc_relation & t, const relation_element val, unsigned col): dm(p.dm(t.get_signature())) { rational r; unsigned num_bits; VERIFY(p.is_numeral(val, r, num_bits)); m_filter = dm.allocateX(); unsigned lo = t.column_idx(col); unsigned hi = t.column_idx(col+1); SASSERT(num_bits == hi - lo); dm.tbvm().set(m_filter->pos(), r, hi-1, lo); } ~filter_equal_fn() override { dm.deallocate(m_filter); } void operator()(relation_base & tb) override { udoc_relation & t = get(tb); t.get_udoc().intersect(dm, *m_filter); SASSERT(t.get_udoc().well_formed(t.get_dm())); } }; relation_mutator_fn * udoc_plugin::mk_filter_equal_fn( const relation_base & t, const relation_element & value, unsigned col) { if (!check_kind(t)) return nullptr; return alloc(filter_equal_fn, *this, get(t), value, col); } bool udoc_relation::is_guard(unsigned n, expr* const* gs) const { for (unsigned i = 0; i < n; ++i) { if (!is_guard(gs[i])) return false; } return true; } bool udoc_relation::is_guard(expr* g) const { udoc_plugin& p = get_plugin(); ast_manager& m = p.get_ast_manager(); bv_util& bv = p.bv; expr* e1, *e2; unsigned hi, lo, v; if (m.is_and(g) || m.is_or(g) || m.is_not(g) || m.is_true(g) || m.is_false(g)) { return is_guard(to_app(g)->get_num_args(), to_app(g)->get_args()); } if (m.is_eq(g, e1, e2) && bv.is_bv(e1)) { if (is_var_range(e1, hi, lo, v) && is_ground(e2)) return true; if (is_var_range(e2, hi, lo, v) && is_ground(e1)) return true; } if (is_var(g)) { return true; } return false; } void udoc_relation::extract_guard(expr* cond, expr_ref& guard, expr_ref& rest) const { rest.reset(); ast_manager& m = get_plugin().get_ast_manager(); expr_ref_vector conds(m), guards(m), rests(m); conds.push_back(cond); flatten_and(conds); for (unsigned i = 0; i < conds.size(); ++i) { expr* g = conds[i].get(); if (is_guard(g)) { guards.push_back(g); } else { rests.push_back(g); } } guard = mk_and(m, guards.size(), guards.c_ptr()); rest = mk_and(m, rests.size(), rests.c_ptr()); } void udoc_relation::extract_equalities(expr* g, expr_ref& rest, subset_ints& equalities, unsigned_vector& roots) const { rest.reset(); ast_manager& m = get_plugin().get_ast_manager(); expr_ref_vector conds(m); conds.push_back(g); flatten_and(conds); expr* e1, *e2; for (unsigned i = 0; i < conds.size(); ++i) { expr* g = conds[i].get(); if (m.is_eq(g, e1, e2)) { extract_equalities(e1, e2, conds, equalities, roots); conds[i] = conds.back(); conds.pop_back(); } } rest = mk_and(m, conds.size(), conds.c_ptr()); } void udoc_relation::extract_equalities( expr* e1, expr* e2, expr_ref_vector& conds, subset_ints& equalities, unsigned_vector& roots) const { udoc_plugin& p = get_plugin(); ast_manager& m = p.get_ast_manager(); bv_util& bv = p.bv; th_rewriter rw(m); unsigned hi, lo1, lo2, hi1, hi2, v1, v2; if (bv.is_concat(e2)) { std::swap(e1, e2); } if (bv.is_concat(e1)) { expr_ref e3(m); app* a1 = to_app(e1); hi = p.num_sort_bits(e1)-1; unsigned n = a1->get_num_args(); for (unsigned i = 0; i < n; ++i) { expr* e = a1->get_arg(i); unsigned sz = p.num_sort_bits(e); e3 = bv.mk_extract(hi, hi-sz+1, e2); rw(e3); extract_equalities(e, e3, conds, equalities, roots); hi -= sz; } return; } if (is_var_range(e1, hi1, lo1, v1) && is_var_range(e2, hi2, lo2, v2)) { unsigned col1 = column_idx(v1); lo1 += col1; hi1 += col1; unsigned col2 = column_idx(v2); lo2 += col2; hi2 += col2; for (unsigned j = 0; j <= hi1-lo1; ++j) { roots.push_back(lo1 + j); equalities.merge(lo1 + j, lo2 + j); } return; } conds.push_back(m.mk_eq(e1, e2)); } void udoc_relation::compile_guard(expr* g, udoc& result, bit_vector const& discard_cols) const { result.push_back(dm.allocateX()); // datastructure to store equalities with columns that will be projected out union_find_default_ctx union_ctx; subset_ints equalities(union_ctx); for (unsigned i = 0, e = discard_cols.size(); i < e; ++i) { equalities.mk_var(); } apply_guard(g, result, equalities, discard_cols); } bool udoc_relation::apply_ground_eq(doc_ref& d, unsigned v, unsigned hi, unsigned lo, expr* c) const { udoc_plugin& p = get_plugin(); unsigned num_bits; rational r; unsigned col = column_idx(v); lo += col; hi += col; if (p.is_numeral(c, r, num_bits)) { d = dm.allocateX(); dm.tbvm().set(d->pos(), r, hi, lo); return true; } // other cases? return false; } bool udoc_relation::apply_bv_eq( expr* e1, expr* e2, bit_vector const& discard_cols, udoc& result) const { udoc_plugin& p = get_plugin(); ast_manager& m = p.get_ast_manager(); bv_util& bv = p.bv; th_rewriter rw(m); doc_ref d(get_dm()); unsigned hi, lo, lo1, lo2, hi1, hi2, v, v1, v2; if (bv.is_concat(e2)) { std::swap(e1, e2); } if (bv.is_concat(e1)) { expr_ref e3(m); app* a1 = to_app(e1); hi = p.num_sort_bits(e1)-1; unsigned n = a1->get_num_args(); for (unsigned i = 0; i < n; ++i) { expr* e = a1->get_arg(i); unsigned sz = p.num_sort_bits(e); e3 = bv.mk_extract(hi, hi-sz+1, e2); rw(e3); if (!apply_bv_eq(e, e3, discard_cols, result)) return false; hi -= sz; } return true; } if (is_ground(e1)) { std::swap(e1, e2); } if (is_var_range(e1, hi, lo, v) && is_ground(e2) && apply_ground_eq(d, v, hi, lo, e2)) { result.intersect(dm, *d); return true; } if (is_var_range(e1, hi1, lo1, v1) && is_var_range(e2, hi2, lo2, v2)) { unsigned idx1 = lo1 + column_idx(v1); unsigned idx2 = lo2 + column_idx(v2); unsigned length = hi1-lo1+1; result.merge(dm, idx1, idx2, length, discard_cols); return true; } return false; } void udoc_relation::apply_guard( expr* g, udoc& result, subset_ints const& equalities, bit_vector const& discard_cols) const { ast_manager& m = get_plugin().get_ast_manager(); bv_util& bv = get_plugin().bv; expr *e0, *e1, *e2; unsigned hi, lo, v; doc_ref d(get_dm()); if (result.is_empty()) { } else if (m.is_true(g)) { } else if (m.is_false(g)) { result.reset(dm); } else if (m.is_and(g)) { for (unsigned i = 0; !result.is_empty() && i < to_app(g)->get_num_args(); ++i) { apply_guard(to_app(g)->get_arg(i), result, equalities, discard_cols); } } else if (m.is_not(g, e0) && m.is_eq(e0, e1, e2) && bv.is_bv(e1) && is_var_range(e1, hi, lo, v) && is_ground(e2) && apply_ground_eq(d, v, hi, lo, e2)) { result.subtract(dm, *d); } else if (m.is_not(g, e0) && m.is_eq(e0, e2, e1) && bv.is_bv(e1) && is_var_range(e1, hi, lo, v) && is_ground(e2) && apply_ground_eq(d, v, hi, lo, e2)) { result.subtract(dm, *d); } else if (m.is_not(g, e1)) { udoc sub; sub.push_back(dm.allocateX()); // TODO: right now we state that no columns are discarded to avoid // silent column merging. This can be optimized if the set of merged // columns is returned so that here we remove different columns. bit_vector empty; empty.resize(discard_cols.size(), false); apply_guard(e1, sub, equalities, empty); result.subtract(dm, sub); result.simplify(dm); TRACE("doc", result.display(dm, tout << "result0:") << "\n"; sub.display(dm, tout << "sub:") << "\n";); sub.reset(dm); TRACE("doc", result.display(dm, tout << "result:") << "\n";); } else if (m.is_or(g)) { udoc sub; sub.push_back(dm.allocateX()); for (unsigned i = 0; !sub.is_empty() && i < to_app(g)->get_num_args(); ++i) { expr_ref arg(m); arg = mk_not(m, to_app(g)->get_arg(i)); apply_guard(arg, sub, equalities, discard_cols); } TRACE("doc", result.display(dm, tout << "result0:") << "\n";); result.subtract(dm, sub); TRACE("doc", sub.display(dm, tout << "sub:") << "\n"; result.display(dm, tout << "result:") << "\n";); sub.reset(dm); } else if (is_var(g)) { SASSERT(m.is_bool(g)); unsigned v = to_var(g)->get_idx(); unsigned idx = column_idx(v); doc_ref d(dm); d = dm.allocateX(); dm.set(*d, idx, BIT_1); result.intersect(dm, *d); } else if (m.is_iff(g, e1, e2)) { udoc diff1, diff2; diff1.push_back(dm.allocateX()); diff2.push_back(dm.allocateX()); expr_ref f1(m), f2(m); f1 = mk_not(m, e1); f2 = mk_not(m, e2); apply_guard(e1, diff1, equalities, discard_cols); apply_guard(f2, diff1, equalities, discard_cols); result.subtract(dm, diff1); diff1.reset(dm); apply_guard(f1, diff2, equalities, discard_cols); apply_guard(e2, diff2, equalities, discard_cols); result.subtract(dm, diff2); diff2.reset(dm); } else if (m.is_eq(g, e1, e2) && bv.is_bv(e1)) { if (apply_bv_eq(e1, e2, discard_cols, result)) { // done } else { goto failure_case; } } else { failure_case: std::ostringstream strm; strm << "Guard expression is not handled" << mk_pp(g, m); throw default_exception(strm.str()); } } class udoc_plugin::filter_interpreted_fn : public relation_mutator_fn { union_find_default_ctx union_ctx; doc_manager& dm; expr_ref m_original_condition; expr_ref m_reduced_condition; udoc m_udoc; bit_vector m_empty_bv; subset_ints m_equalities; public: filter_interpreted_fn(const udoc_relation & t, ast_manager& m, app *condition) : dm(t.get_dm()), m_original_condition(condition, m), m_reduced_condition(m), m_equalities(union_ctx) { unsigned num_bits = t.get_num_bits(); m_empty_bv.resize(num_bits, false); expr_ref guard(m); for (unsigned i = 0; i < num_bits; ++i) { m_equalities.mk_var(); } t.extract_guard(condition, guard, m_reduced_condition); m_udoc.push_back(dm.allocateX()); t.apply_guard(guard, m_udoc, m_equalities, m_empty_bv); TRACE("doc", tout << "original condition: " << mk_pp(condition, m) << "\n"; tout << "remaining condition: " << m_reduced_condition << "\n"; m_udoc.display(dm, tout) << "\n";); } ~filter_interpreted_fn() override { m_udoc.reset(dm); } void operator()(relation_base & tb) override { udoc_relation & t = get(tb); udoc& u = t.get_udoc(); SASSERT(u.well_formed(dm)); u.intersect(dm, m_udoc); SASSERT(u.well_formed(dm)); t.apply_guard(m_reduced_condition, u, m_equalities, m_empty_bv); SASSERT(u.well_formed(dm)); u.simplify(dm); SASSERT(u.well_formed(dm)); TRACE("doc", tout << "final size: " << t.get_size_estimate_rows() << '\n';); IF_VERBOSE(3, t.display(verbose_stream());); } }; relation_mutator_fn * udoc_plugin::mk_filter_interpreted_fn(const relation_base & t, app * condition) { return check_kind(t)?alloc(filter_interpreted_fn, get(t), get_ast_manager(), condition):nullptr; } class udoc_plugin::join_project_fn : public convenient_relation_join_project_fn { #if 0 udoc_plugin::join_fn m_joiner; #endif bit_vector m_to_delete; public: join_project_fn( udoc_relation const& t1, udoc_relation const& t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, unsigned const* rm_cols) : convenient_relation_join_project_fn( t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2, removed_col_cnt, rm_cols) #if 0 , m_joiner(t1.get_plugin(), t1, t2, col_cnt, cols1, cols2) #endif { unsigned num_bits1 = t1.get_num_bits(); unsigned num_bits = num_bits1 + t2.get_num_bits(); unsigned_vector removed_cols(removed_col_cnt, rm_cols); t1.expand_column_vector(removed_cols, &t2); t1.expand_column_vector(m_cols1); t2.expand_column_vector(m_cols2); m_to_delete.resize(num_bits, false); for (unsigned i = 0; i < removed_cols.size(); ++i) { m_to_delete.set(removed_cols[i], true); } } // TBD: replace this by "join" given below. relation_base* operator()(relation_base const& t1, relation_base const& t2) override { #if 1 return join(get(t1), get(t2)); #else udoc_relation *joined = get(m_joiner(t1, t2)); relation_base* result = 0; if (joined->fast_empty()) { result = t1.get_plugin().mk_empty(get_result_signature()); } else { project_fn projector(*joined, m_removed_cols.size(), m_removed_cols.c_ptr()); result = projector(*joined); } joined->deallocate(); return result; #endif } private: udoc_relation* join(udoc_relation const& t1, udoc_relation const& t2) { relation_signature prod_signature; prod_signature.append(t1.get_signature()); prod_signature.append(t2.get_signature()); udoc const& d1 = t1.get_udoc(); udoc const& d2 = t2.get_udoc(); doc_manager& dm1 = t1.get_dm(); udoc_plugin& p = t1.get_plugin(); doc_manager& dm_prod = p.dm(prod_signature); udoc_relation* result = get(p.mk_empty(get_result_signature())); udoc& res = result->get_udoc(); doc_manager& dm_res = result->get_dm(); for (unsigned i = 0; i < d1.size(); ++i) { for (unsigned j = 0; j < d2.size(); ++j) { doc_ref d(dm_prod, dm_prod.join(d1[i], d2[j], dm1, m_cols1, m_cols2)); if (d) { res.insert(dm_res, dm_prod.project(dm_res, m_to_delete, *d)); IF_VERBOSE(2, if (res.size() > 0 && 0 == res.size() % 10000) { verbose_stream() << "result size: " << res.size() << " i:" << i << " j:" << j << " " << 100*i/d1.size() << "% complete\n"; }); } } } TRACE("doc", result->display(tout);); return result; } }; class udoc_plugin::join_project_and_fn : public relation_join_fn { public: join_project_and_fn() {} relation_base* operator()(relation_base const& t1, relation_base const& t2) override { udoc_relation *result = get(t1.clone()); result->get_udoc().intersect(result->get_dm(), get(t2).get_udoc()); return result; } }; relation_join_fn * udoc_plugin::mk_join_project_fn( relation_base const& t1, relation_base const& t2, unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols) { if (!check_kind(t1) || !check_kind(t2)) return nullptr; // special case where we have h(X) :- f(X), g(X). if (joined_col_cnt == removed_col_cnt && t1.get_signature().size() == joined_col_cnt && t2.get_signature().size() == joined_col_cnt) { for (unsigned i = 0; i < removed_col_cnt; ++i) { if (removed_cols[i] != i || cols1[i] != cols2[i]) goto general_fn; } return alloc(join_project_and_fn); } general_fn: return alloc(join_project_fn, get(t1), get(t2), joined_col_cnt, cols1, cols2, removed_col_cnt, removed_cols); } // // Notes: // 1. this code could use some cleanup and simplification. // 2. It is also not very efficient in the copy routines. // They fall back to copying each bit instead of a chunk. // 3. Argument about correctness is needed as comments. // 4. Unit/stress test cases are needed. // class udoc_plugin::negation_filter_fn : public relation_intersection_filter_fn { struct mk_remove_cols { mk_remove_cols(relation_base const& t1, relation_base const& t2, unsigned_vector& remove_cols) { unsigned sz1 = t1.get_signature().size(); unsigned sz2 = t2.get_signature().size(); for (unsigned i = 0; i < sz2; ++i) { remove_cols.push_back(sz1 + i); } } }; unsigned_vector m_t_cols; unsigned_vector m_neg_cols; unsigned_vector m_remove_cols; mk_remove_cols m_mk_remove_cols; join_project_fn m_join_project; bool m_is_subtract; //bool m_is_aliased; public: negation_filter_fn(const udoc_relation & t, const udoc_relation & neg, unsigned joined_col_cnt, const unsigned *t_cols, const unsigned *neg_cols) : m_t_cols(joined_col_cnt, t_cols), m_neg_cols(joined_col_cnt, neg_cols), m_mk_remove_cols(t, neg, m_remove_cols), m_join_project(t, neg, joined_col_cnt, t_cols, neg_cols, m_remove_cols.size(), m_remove_cols.c_ptr()), m_is_subtract(false)//, /*m_is_aliased(true) */{ SASSERT(joined_col_cnt > 0 || neg.get_signature().size() == 0); m_is_subtract = (joined_col_cnt == t.get_signature().size()); m_is_subtract &= (joined_col_cnt == neg.get_signature().size()); svector found(joined_col_cnt, false); for (unsigned i = 0; m_is_subtract && i < joined_col_cnt; ++i) { m_is_subtract = !found[t_cols[i]] && (t_cols[i] == neg_cols[i]); found[t_cols[i]] = true; } t.expand_column_vector(m_t_cols); neg.expand_column_vector(m_neg_cols); } void operator()(relation_base& tb, const relation_base& negb) override { udoc_relation& t = get(tb); udoc_relation const& n = get(negb); IF_VERBOSE(3, t.display(verbose_stream() << "dst:");); IF_VERBOSE(3, n.display(verbose_stream() << "neg:");); if (t.fast_empty() || n.fast_empty()) return; /* TODO: double check if (!m_is_aliased && !p.m_disable_fast_pass) { // fast_pass(t, n); } */ if (n.get_signature().empty()) t.get_udoc().reset(t.get_dm()); else if (m_is_subtract) t.get_udoc().subtract(t.get_dm(), n.get_udoc()); else slow_pass(t, n); } private: /* void fast_pass(udoc_relation& t, const udoc_relation& n) { SASSERT(!m_is_aliased); udoc & dst = t.get_udoc(); udoc const & neg = n.get_udoc(); doc_manager& dmt = t.get_dm(); doc_manager& dmn = n.get_dm(); udoc result; for (unsigned i = 0; i < dst.size(); ++i) { bool subsumed = false; for (unsigned j = 0; j < neg.size(); ++j) { if (dmn.contains(neg[j], m_neg_cols, dst[i], m_t_cols)) { dmt.deallocate(&dst[i]); subsumed = true; break; } } if (!subsumed) result.push_back(&dst[i]); } std::swap(dst, result); } */ void slow_pass(udoc_relation& t, udoc_relation const& n) { doc_manager& dmt = t.get_dm(); udoc_relation* jp = get(m_join_project(t, n)); if (!jp->fast_empty()) { t.get_udoc().subtract(dmt, jp->get_udoc()); } TRACE("doc", t.display(tout); tout << "\n"; jp->display(tout); tout << "\n";); jp->deallocate(); } }; relation_intersection_filter_fn * udoc_plugin::mk_filter_by_negation_fn( const relation_base& t, const relation_base& neg, unsigned joined_col_cnt, const unsigned *t_cols, const unsigned *negated_cols) { if (!check_kind(t) || !check_kind(neg)) return nullptr; return alloc(negation_filter_fn, get(t), get(neg), joined_col_cnt, t_cols, negated_cols); } class udoc_plugin::filter_proj_fn : public convenient_relation_project_fn { union_find_default_ctx union_ctx; doc_manager& dm; expr_ref m_original_condition; expr_ref m_reduced_condition; udoc m_udoc; udoc m_udoc2; bit_vector m_to_delete; // map: col idx -> bool (whether the column is to be removed) subset_ints m_equalities; unsigned_vector m_roots; public: filter_proj_fn(const udoc_relation & t, ast_manager& m, app *condition, unsigned col_cnt, const unsigned * removed_cols) : convenient_relation_project_fn(t.get_signature(), col_cnt, removed_cols), dm(t.get_dm()), m_original_condition(condition, m), m_reduced_condition(m), m_equalities(union_ctx) { unsigned num_bits = t.get_num_bits(); t.expand_column_vector(m_removed_cols); m_to_delete.resize(num_bits, false); for (unsigned i = 0; i < num_bits; ++i) { m_equalities.mk_var(); } for (unsigned i = 0; i < m_removed_cols.size(); ++i) { m_to_delete.set(m_removed_cols[i], true); } expr_ref guard(m), non_eq_cond(condition, m); t.extract_equalities(condition, non_eq_cond, m_equalities, m_roots); t.extract_guard(non_eq_cond, guard, m_reduced_condition); t.compile_guard(guard, m_udoc, m_to_delete); } ~filter_proj_fn() override { m_udoc.reset(dm); } relation_base* operator()(const relation_base & tb) override { udoc_relation const & t = get(tb); udoc const& u1 = t.get_udoc(); doc_manager& dm = t.get_dm(); m_udoc2.copy(dm, u1); m_udoc2.intersect(dm, m_udoc); t.apply_guard(m_reduced_condition, m_udoc2, m_equalities, m_to_delete); m_udoc2.merge(dm, m_roots, m_equalities, m_to_delete); SASSERT(m_udoc2.well_formed(dm)); udoc_relation* r = get(t.get_plugin().mk_empty(get_result_signature())); doc_manager& dm2 = r->get_dm(); for (unsigned i = 0; i < m_udoc2.size(); ++i) { doc* d = dm.project(dm2, m_to_delete, m_udoc2[i]); r->get_udoc().insert(dm2, d); SASSERT(r->get_udoc().well_formed(dm2)); } m_udoc2.reset(dm); IF_VERBOSE(3, r->display(verbose_stream() << "filter project result:\n");); return r; } }; relation_transformer_fn * udoc_plugin::mk_filter_interpreted_and_project_fn( const relation_base & t, app * condition, unsigned removed_col_cnt, const unsigned * removed_cols) { return check_kind(t)?alloc(filter_proj_fn, get(t), get_ast_manager(), condition, removed_col_cnt, removed_cols):nullptr; } } z3-z3-4.8.7/src/muz/rel/udoc_relation.h000066400000000000000000000156331356505360400176230ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: udoc_relation.h Abstract: Relation based on union of DOCs. Author: Nuno Lopes (a-nlopes) 2013-03-01 Nikolaj Bjorner (nbjorner) 2014-09-15 Revision History: --*/ #ifndef UDOC_RELATION_H_ #define UDOC_RELATION_H_ #include "muz/rel/doc.h" #include "muz/rel/dl_base.h" namespace datalog { class udoc_plugin; class udoc_relation; class udoc_relation : public relation_base { friend class udoc_plugin; doc_manager& dm; mutable udoc m_elems; unsigned_vector m_column_info; doc* fact2doc(relation_fact const& f) const; expr_ref to_formula(tbv const& t) const; expr_ref to_formula(doc const& d) const; public: udoc_relation(udoc_plugin& p, relation_signature const& s); ~udoc_relation() override; void reset() override; void add_fact(const relation_fact & f) override; void add_new_fact(const relation_fact & f) override; bool contains_fact(const relation_fact & f) const override; udoc_relation * clone() const override; udoc_relation * complement(func_decl*) const override; void to_formula(expr_ref& fml) const override; udoc_plugin& get_plugin() const; bool fast_empty() const override { return m_elems.is_empty(); } bool empty() const override; void display(std::ostream& out) const override; bool is_precise() const override { return true; } unsigned get_size_estimate_rows() const override { return m_elems.size(); } unsigned get_size_estimate_bytes() const override; doc_manager& get_dm() const { return dm; } udoc const& get_udoc() const { return m_elems; } udoc& get_udoc() { return m_elems; } unsigned get_num_records() const { return m_elems.size(); } unsigned get_num_bits() const { return m_column_info.back(); } unsigned get_num_cols() const { return m_column_info.size()-1; } unsigned column_idx(unsigned col) const { return m_column_info[col]; } unsigned column_num_bits(unsigned col) const { return m_column_info[col+1] - m_column_info[col]; } void expand_column_vector(unsigned_vector& v, const udoc_relation* other = nullptr) const; void extract_guard(expr* condition, expr_ref& guard, expr_ref& rest) const; bool is_guard(expr* g) const; bool is_guard(unsigned n, expr* const *g) const; void compile_guard(expr* g, udoc& result, bit_vector const& discard_cols) const; void extract_equalities(expr* g, expr_ref& rest, subset_ints& equalities, unsigned_vector& roots) const; void extract_equalities( expr* e1, expr* e2, expr_ref_vector& conds, subset_ints& equalities, unsigned_vector& roots) const; void apply_guard(expr* g, udoc& result, subset_ints const& equalities, bit_vector const& discard_cols) const; bool apply_ground_eq(doc_ref& d, unsigned v, unsigned hi, unsigned lo, expr* c) const; bool apply_bv_eq(expr* e1, expr* e2, bit_vector const& discard_cols, udoc& result) const; bool is_var_range(expr* e, unsigned& hi, unsigned& lo, unsigned& v) const; }; class udoc_plugin : public relation_plugin { friend class udoc_relation; class join_fn; class join_project_fn; class join_project_and_fn; class project_fn; class union_fn; class rename_fn; class filter_equal_fn; class filter_identical_fn; class filter_interpreted_fn; class filter_by_negation_fn; class filter_by_union_fn; class filter_proj_fn; class negation_filter_fn; ast_manager& m; bv_util bv; dl_decl_util dl; u_map m_dms; bool m_disable_fast_pass; doc_manager& dm(unsigned sz); doc_manager& dm(relation_signature const& sig); static udoc_relation& get(relation_base& r); static udoc_relation* get(relation_base* r); static udoc_relation const & get(relation_base const& r); void mk_union(doc_manager& dm, udoc& dst, udoc const& src, udoc* delta); bool is_numeral(expr* e, rational& r, unsigned& num_bits); unsigned num_sort_bits(expr* e) const { return num_sort_bits(get_ast_manager().get_sort(e)); } unsigned num_sort_bits(sort* s) const; bool is_finite_sort(sort* s) const; unsigned num_signature_bits(relation_signature const& sig); expr* mk_numeral(rational const& r, sort* s); public: udoc_plugin(relation_manager& rm); ~udoc_plugin() override; bool can_handle_signature(const relation_signature & s) override; static symbol get_name() { return symbol("doc"); } relation_base * mk_empty(const relation_signature & s) override; relation_base * mk_full(func_decl* p, const relation_signature & s) override; relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) override; relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, const unsigned * removed_cols) override; relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, const unsigned * permutation_cycle) override; relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta) override; relation_union_fn * mk_widen_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta) override; relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, const unsigned * identical_cols) override; relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, unsigned col) override; relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition) override; relation_intersection_filter_fn * mk_filter_by_negation_fn( const relation_base& t, const relation_base& neg, unsigned joined_col_cnt, const unsigned *t_cols, const unsigned *negated_cols) override; relation_transformer_fn * mk_filter_interpreted_and_project_fn( const relation_base & t, app * condition, unsigned removed_col_cnt, const unsigned * removed_cols) override; relation_join_fn * mk_join_project_fn( relation_base const& t1, relation_base const& t2, unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols) override; void disable_fast_pass() { m_disable_fast_pass = true; } }; }; #endif /* UDOC_RELATION_H_ */ z3-z3-4.8.7/src/muz/spacer/000077500000000000000000000000001356505360400153065ustar00rootroot00000000000000z3-z3-4.8.7/src/muz/spacer/CMakeLists.txt000066400000000000000000000014431356505360400200500ustar00rootroot00000000000000z3_add_component(spacer SOURCES spacer_legacy_mev.cpp spacer_legacy_frames.cpp spacer_context.cpp spacer_dl_interface.cpp spacer_farkas_learner.cpp spacer_generalizers.cpp spacer_manager.cpp spacer_prop_solver.cpp spacer_sym_mux.cpp spacer_util.cpp spacer_iuc_solver.cpp spacer_legacy_mbp.cpp spacer_proof_utils.cpp spacer_unsat_core_learner.cpp spacer_unsat_core_plugin.cpp spacer_matrix.cpp spacer_antiunify.cpp spacer_mev_array.cpp spacer_qe_project.cpp spacer_sem_matcher.cpp spacer_quant_generalizer.cpp spacer_arith_generalizers.cpp spacer_callback.cpp spacer_json.cpp spacer_iuc_proof.cpp spacer_mbc.cpp spacer_pdr.cpp spacer_sat_answer.cpp COMPONENT_DEPENDENCIES arith_tactics core_tactics muz qe smt_tactic transforms ) z3-z3-4.8.7/src/muz/spacer/spacer_antiunify.cpp000066400000000000000000000275751356505360400213750ustar00rootroot00000000000000/*++ Copyright (c) 2017 Arie Gurfinkel Module Name: spacer_antiunify.cpp Abstract: Antiunification utilities Author: Bernhard Gleiss Arie Gurfinkel Revision History: --*/ #include"muz/spacer/spacer_antiunify.h" #include"ast/ast.h" #include"ast/rewriter/rewriter.h" #include"ast/rewriter/rewriter_def.h" #include"ast/arith_decl_plugin.h" #include"ast/ast_util.h" #include"ast/expr_abstract.h" namespace spacer { // Abstracts numeric values by variables struct var_abs_rewriter : public default_rewriter_cfg { ast_manager &m; arith_util m_util; ast_mark m_seen; ast_mark m_has_num; unsigned m_var_index; expr_ref_vector m_pinned; obj_map& m_substitution; ptr_vector m_stack; var_abs_rewriter (ast_manager &manager, obj_map& substitution, unsigned k = 0) : m(manager), m_util(m), m_var_index(k), m_pinned(m), m_substitution(substitution) {} void reset(unsigned k = 0) { m_pinned.reset(); m_var_index = k; } bool pre_visit(expr * t) { bool r = (!m_seen.is_marked(t) || m_has_num.is_marked(t)); // only unify if convex closure will not contain non-linear multiplication if (m_util.is_mul(t)) { bool contains_const_child = false; app* a = to_app(t); for (expr * arg : *a) { if (m_util.is_numeral(arg)) { contains_const_child = true; } } if (!contains_const_child) {r = false;} } if (r) {m_stack.push_back (t);} return r; } br_status reduce_app (func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { expr *s; s = m_stack.back(); m_stack.pop_back(); if (is_app(s)) { app *a = to_app(s); for (unsigned i=0, sz = a->get_num_args(); i < sz; ++i) { if (m_has_num.is_marked(a->get_arg(i))) { m_has_num.mark(a,true); return BR_FAILED; } } } return BR_FAILED; } bool cache_all_results() const { return false; } bool cache_results() const { return false; } bool get_subst(expr * s, expr * & t, proof * & t_pr) { if (m_util.is_numeral(s)) { t = m.mk_var(m_var_index++, m.get_sort(s)); m_substitution.insert(t, s); m_pinned.push_back(t); m_has_num.mark(s, true); m_seen.mark(t, true); return true; } return false; } }; anti_unifier::anti_unifier(ast_manager &manager) : m(manager), m_pinned(m) {} void anti_unifier::reset() { m_subs.reset(); m_cache.reset(); m_todo.reset(); m_pinned.reset(); } void anti_unifier::operator()(expr *e1, expr *e2, expr_ref &res, substitution &s1, substitution &s2) { reset(); if (e1 == e2) {res = e1; s1.reset(); s2.reset(); return;} m_todo.push_back(expr_pair(e1, e2)); while (!m_todo.empty()) { const expr_pair &p = m_todo.back(); SASSERT(is_app(p.first)); SASSERT(is_app(p.second)); app * n1 = to_app(p.first); app * n2 = to_app(p.second); unsigned num_arg1 = n1->get_num_args(); unsigned num_arg2 = n2->get_num_args(); if (n1->get_decl() != n2->get_decl() || num_arg1 != num_arg2) { expr_ref v(m); v = m.mk_var(m_subs.size(), get_sort(n1)); m_pinned.push_back(v); m_subs.push_back(expr_pair(n1, n2)); m_cache.insert(n1, n2, v); } else { expr *tmp; unsigned todo_sz = m_todo.size(); ptr_buffer kids; for (unsigned i = 0; i < num_arg1; ++i) { expr *arg1 = n1->get_arg(i); expr *arg2 = n2->get_arg(i); if (arg1 == arg2) {kids.push_back(arg1);} else if (m_cache.find(arg1, arg2, tmp)) {kids.push_back(tmp);} else {m_todo.push_back(expr_pair(arg1, arg2));} } if (m_todo.size() > todo_sz) {continue;} expr_ref u(m); u = m.mk_app(n1->get_decl(), kids.size(), kids.c_ptr()); m_pinned.push_back(u); m_cache.insert(n1, n2, u); } } expr *r; VERIFY(m_cache.find(e1, e2, r)); res = r; // create substitutions s1.reserve(2, m_subs.size()); s2.reserve(2, m_subs.size()); for (unsigned i = 0, sz = m_subs.size(); i < sz; ++i) { expr_pair p = m_subs.get(i); s1.insert(i, 0, expr_offset(p.first, 1)); s2.insert(i, 0, expr_offset(p.second, 1)); } } class ncc_less_than_key { public: ncc_less_than_key(arith_util& util) : m_util(util) {} bool operator() (const expr*& e1, const expr*& e2) { rational val1; rational val2; if (m_util.is_numeral(e1, val1) && m_util.is_numeral(e2, val2)) { return val1 < val2; } else { SASSERT(false); return false; } } arith_util m_util; }; /* * if there is a single interval which exactly captures each of the * substitutions, return the corresponding closure, otherwise do * nothing */ bool naive_convex_closure::compute_closure(anti_unifier& au, ast_manager& m, expr_ref& result) { NOT_IMPLEMENTED_YET(); #if 0 arith_util util(m); SASSERT(au.get_num_substitutions() > 0); if (au.get_substitution(0).size() == 0) { result = au.get_generalization(); return true; } // check that all substitutions have the same size for (unsigned i=0, sz = au.get_num_substitutions(); i+1 < sz; ++i) { if (au.get_substitution(i).size() != au.get_substitution(i+1).size()) { return false; } } // for each substitution entry bool is_first_key = true; unsigned lower_bound = 0; unsigned upper_bound = 0; for (const auto& pair : au.get_substitution(0)) { // construct vector expr* key = &pair.get_key(); vector entries; rational val; for (unsigned i=0, sz = au.get_num_substitutions(); i < sz; ++i) { if (util.is_numeral(au.get_substitution(i)[key], val) && val.is_unsigned()) { entries.push_back(val.get_unsigned()); } else { return false; } } // check whether vector represents interval unsigned current_lower_bound = 0; unsigned current_upper_bound = 0; // if vector represents interval if (get_range(entries, current_lower_bound, current_upper_bound)) { // if interval is the same as previous interval if (is_first_key) { is_first_key = false; lower_bound = current_lower_bound; upper_bound = current_upper_bound; } else { if (current_lower_bound != lower_bound || current_upper_bound != upper_bound) { return false; } } } // otherwise we don't do a convex closure else { return false; } } // we finally know that we can express the substitutions using a // single interval, so build the expression 1. construct const expr_ref const_ref(m.mk_const(symbol("scti!0"), util.mk_int()), m); // 2. construct body with const expr_ref lit1(util.mk_le(util.mk_int(lower_bound), const_ref), m); expr_ref lit2(util.mk_le(const_ref, util.mk_int(upper_bound)), m); expr_ref lit3(m); substitute_vars_by_const(m, au.get_generalization(), const_ref, lit3); expr_ref_vector args(m); args.push_back(lit1); args.push_back(lit2); args.push_back(lit3); expr_ref body_with_consts = mk_and(args); // 3. replace const by var ptr_vector vars; vars.push_back(const_ref); expr_ref body(m); expr_abstract(m, 0, vars.size(), (expr*const*)vars.c_ptr(), body_with_consts, body); // 4. introduce quantifier ptr_vector sorts; sorts.push_back(util.mk_int()); svector names; names.push_back(symbol("scti!0")); result = expr_ref(m.mk_exists(vars.size(), sorts.c_ptr(), names.c_ptr(), body),m); return true; #endif } bool naive_convex_closure::get_range(vector& v, unsigned int& lower_bound, unsigned int& upper_bound) { // sort substitutions std::sort(v.begin(), v.end()); // check that numbers are consecutive for (unsigned i=0; i+1 < v.size(); ++i) { if (v[i] + 1 != v[i+1]) { return false; } } SASSERT(v.size() > 0); lower_bound = v[0]; upper_bound = v.back(); return true; } struct subs_rewriter_cfg : public default_rewriter_cfg { ast_manager &m; expr_ref m_c; subs_rewriter_cfg (ast_manager &manager, expr* c) : m(manager), m_c(c, m) {} bool reduce_var(var * t, expr_ref & result, proof_ref & result_pr) { result = m_c; result_pr = nullptr; return true; } }; void naive_convex_closure::substitute_vars_by_const(ast_manager& m, expr* t, expr* c, expr_ref& res) { subs_rewriter_cfg subs_cfg(m, c); rewriter_tpl subs_rw (m, false, subs_cfg); subs_rw (t, res); } /// Construct a pattern by abstracting all numbers by variables struct mk_num_pat_rewriter : public default_rewriter_cfg { ast_manager &m; arith_util m_arith; // -- mark already seen expressions ast_mark m_seen; // -- true if the expression is known to have a number as a sub-expression ast_mark m_has_num; // -- expressions created during the transformation expr_ref_vector m_pinned; // -- map from introduced variables to expressions they replace app_ref_vector &m_subs; // -- stack of expressions being processed to have access to expressions // -- before rewriting ptr_buffer m_stack; mk_num_pat_rewriter (ast_manager &manager, app_ref_vector& subs) : m(manager), m_arith(m), m_pinned(m), m_subs(subs) {} bool pre_visit(expr * t) { // -- don't touch multiplication if (m_arith.is_mul(t)) return false; bool r = (!m_seen.is_marked(t) || m_has_num.is_marked(t)); if (r) {m_stack.push_back (t);} return r; } br_status reduce_app (func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { expr *s; s = m_stack.back(); m_stack.pop_back(); if (is_app(s)) { app *a = to_app(s); for (unsigned i = 0, sz = a->get_num_args(); i < sz; ++i) { if (m_has_num.is_marked(a->get_arg(i))) { m_has_num.mark(a, true); break; } } } return BR_FAILED; } bool cache_all_results() const { return false; } bool cache_results() const { return false; } bool get_subst(expr * s, expr * & t, proof * & t_pr) { if (m_arith.is_numeral(s)) { t = m.mk_var(m_subs.size(), m.get_sort(s)); m_pinned.push_back(t); m_subs.push_back(to_app(s)); m_has_num.mark(t, true); m_seen.mark(t, true); return true; } return false; } }; void mk_num_pat(expr *e, expr_ref &result, app_ref_vector &subs) { SASSERT(subs.empty()); mk_num_pat_rewriter rw_cfg(result.m(), subs); rewriter_tpl rw(result.m(), false, rw_cfg); rw(e, result); } } template class rewriter_tpl; template class rewriter_tpl; template class rewriter_tpl; z3-z3-4.8.7/src/muz/spacer/spacer_antiunify.h000066400000000000000000000032651356505360400210300ustar00rootroot00000000000000/*++ Copyright (c) 2017 Arie Gurfinkel Module Name: spacer_antiunify.h Abstract: Antiunification utilities Author: Bernhard Gleiss Arie Gurfinkel Revision History: --*/ #ifndef _SPACER_ANTIUNIFY_H_ #define _SPACER_ANTIUNIFY_H_ #include "ast/ast.h" #include "ast/substitution/substitution.h" #include "util/obj_pair_hashtable.h" namespace spacer { /** \brief Anti-unifier for ground expressions */ class anti_unifier { typedef std::pair expr_pair; typedef pair_hash, obj_ptr_hash > expr_pair_hash; typedef obj_pair_map cache_ty; ast_manager &m; expr_ref_vector m_pinned; svector m_todo; cache_ty m_cache; svector m_subs; public: anti_unifier(ast_manager& m); void reset(); /** \brief Computes anti-unifier of two ground expressions. Returns the anti-unifier and the corresponding substitutions */ void operator() (expr *e1, expr *e2, expr_ref &res, substitution &s1, substitution &s2); }; class naive_convex_closure { public: static bool compute_closure(anti_unifier& au, ast_manager& m, expr_ref& result); private: static bool get_range(vector& v, unsigned& lower_bound, unsigned& upper_bound); static void substitute_vars_by_const(ast_manager& m, expr* t, expr* c, expr_ref& res); }; /// Abstracts numbers in the given ground expression by variables /// Returns the created pattern and the corresponding substitution. void mk_num_pat(expr *e, expr_ref &result, app_ref_vector &subs); } #endif z3-z3-4.8.7/src/muz/spacer/spacer_arith_generalizers.cpp000066400000000000000000000111271356505360400232320ustar00rootroot00000000000000/*++ Copyright (c) 2019 Microsoft Corporation and Arie Gurfinkel Module Name: spacer_arith_generalizers.cpp Abstract: Arithmetic-related generalizers Author: Arie Gurfinkel Revision History: --*/ #include "ast/rewriter/rewriter.h" #include "ast/rewriter/rewriter_def.h" #include "muz/spacer/spacer_generalizers.h" #include "smt/smt_solver.h" namespace spacer { namespace { /* Rewrite all denominators to be no larger than a given limit */ struct limit_denominator_rewriter_cfg : public default_rewriter_cfg { ast_manager &m; arith_util m_arith; rational m_limit; limit_denominator_rewriter_cfg(ast_manager &manager, rational limit) : m(manager), m_arith(m), m_limit(limit) {} bool is_numeral(func_decl *f, rational &val, bool &is_int) { if (f->get_family_id() == m_arith.get_family_id() && f->get_decl_kind() == OP_NUM) { val = f->get_parameter(0).get_rational(); is_int = f->get_parameter(1).get_int() != 0; return true; } return false; } bool limit_denominator(rational &num) { return rational::limit_denominator(num, m_limit); } br_status reduce_app(func_decl *f, unsigned num, expr *const *args, expr_ref &result, proof_ref &result_pr) { bool is_int; rational val; if (is_numeral(f, val, is_int) && !is_int) { if (limit_denominator(val)) { result = m_arith.mk_numeral(val, false); return BR_DONE; } } return BR_FAILED; } }; } // namespace limit_num_generalizer::limit_num_generalizer(context &ctx, unsigned failure_limit) : lemma_generalizer(ctx), m_failure_limit(failure_limit) {} bool limit_num_generalizer::limit_denominators(expr_ref_vector &lits, rational &limit) { ast_manager &m = m_ctx.get_ast_manager(); limit_denominator_rewriter_cfg rw_cfg(m, limit); rewriter_tpl rw(m, false, rw_cfg); expr_ref lit(m); bool dirty = false; for (unsigned i = 0, sz = lits.size(); i < sz; ++i) { rw(lits.get(i), lit); dirty |= (lits.get(i) != lit.get()); lits[i] = lit; } return dirty; } void limit_num_generalizer::operator()(lemma_ref &lemma) { if (lemma->get_cube().empty()) return; m_st.count++; scoped_watch _w_(m_st.watch); unsigned uses_level; pred_transformer &pt = lemma->get_pob()->pt(); ast_manager &m = pt.get_ast_manager(); expr_ref_vector cube(m); // create a solver to check whether updated cube is in a generalization ref sol = mk_smt_solver(m, params_ref::get_empty(), symbol::null); SASSERT(lemma->has_pob()); sol->assert_expr(lemma->get_pob()->post()); unsigned weakness = lemma->weakness(); rational limit(100); for (unsigned num_failures = 0; num_failures < m_failure_limit; ++num_failures) { cube.reset(); cube.append(lemma->get_cube()); // try to limit denominators if (!limit_denominators(cube, limit)) return; bool failed = false; // check that pob->post() ==> cube for (auto *lit : cube) { solver::scoped_push _p_(*sol); expr_ref nlit(m); nlit = m.mk_not(lit); sol->assert_expr(nlit); lbool res = sol->check_sat(0, nullptr); if (res == l_false) { // good } else { failed = true; TRACE("spacer.limnum", tout << "Failed to generalize: " << lemma->get_cube() << "\ninto\n" << cube << "\n";); break; } } // check that !cube & F & Tr ==> !cube' if (!failed && pt.check_inductive(lemma->level(), cube, uses_level, weakness)) { TRACE("spacer", tout << "Reduced fractions from:\n" << lemma->get_cube() << "\n\nto\n" << cube << "\n";); lemma->update_cube(lemma->get_pob(), cube); lemma->set_level(uses_level); // done return; } ++m_st.num_failures; // increase limit limit = limit * 10; } } void limit_num_generalizer::collect_statistics(statistics &st) const { st.update("time.spacer.solve.reach.gen.lim_num", m_st.watch.get_seconds()); st.update("limitted num gen", m_st.count); st.update("limitted num gen failures", m_st.num_failures); } } // namespace spacer z3-z3-4.8.7/src/muz/spacer/spacer_callback.cpp000066400000000000000000000011021356505360400210750ustar00rootroot00000000000000/**++ Copyright (c) 2017 Microsoft Corporation and Matteo Marescotti Module Name: spacer_callback.cpp Abstract: SPACER plugin for handling events Author: Matteo Marescotti Notes: --*/ #include "spacer_callback.h" #include "muz/spacer/spacer_context.h" namespace spacer { void user_callback::new_lemma_eh(expr *lemma, unsigned level) { m_new_lemma_eh(m_state, lemma, level); } void user_callback::predecessor_eh() { m_predecessor_eh(m_state); } void user_callback::unfold_eh() { m_unfold_eh(m_state); } }z3-z3-4.8.7/src/muz/spacer/spacer_callback.h000066400000000000000000000027661356505360400205630ustar00rootroot00000000000000/**++ Copyright (c) 2017 Microsoft Corporation and Matteo Marescotti Module Name: spacer_callback.h Abstract: SPACER plugin for handling events Author: Matteo Marescotti Notes: --*/ #ifndef _SPACER_CALLBACK_H_ #define _SPACER_CALLBACK_H_ #include "muz/spacer/spacer_context.h" #include "muz/base/dl_engine_base.h" namespace spacer { class user_callback : public spacer_callback { private: void *m_state; const datalog::t_new_lemma_eh m_new_lemma_eh; const datalog::t_predecessor_eh m_predecessor_eh; const datalog::t_unfold_eh m_unfold_eh; public: user_callback(context &context, void *state, const datalog::t_new_lemma_eh new_lemma_eh, const datalog::t_predecessor_eh predecessor_eh, const datalog::t_unfold_eh unfold_eh) : spacer_callback(context), m_state(state), m_new_lemma_eh(new_lemma_eh), m_predecessor_eh(predecessor_eh), m_unfold_eh(unfold_eh) {} inline bool new_lemma() override { return m_new_lemma_eh != nullptr; } void new_lemma_eh(expr *lemma, unsigned level) override; inline bool predecessor() override { return m_predecessor_eh != nullptr; } void predecessor_eh() override; inline bool unfold() override { return m_unfold_eh != nullptr; } void unfold_eh() override; }; } #endif //_SPACER_CALLBACK_H_ z3-z3-4.8.7/src/muz/spacer/spacer_context.cpp000066400000000000000000004050601356505360400210400ustar00rootroot00000000000000/** Copyright (c) 2017 Microsoft Corporation and Arie Gurfinkel Module Name: spacer_context.cpp Abstract: SPACER predicate transformers and search context. Author: Arie Gurfinkel Anvesh Komuravelli Based on muz/pdr/pdr_context.cpp by Nikolaj Bjorner (nbjorner) Notes: --*/ #include #include #include "muz/base/dl_util.h" #include "ast/rewriter/rewriter.h" #include "ast/rewriter/rewriter_def.h" #include "ast/rewriter/var_subst.h" #include "util/util.h" #include "muz/spacer/spacer_prop_solver.h" #include "muz/spacer/spacer_context.h" #include "muz/spacer/spacer_generalizers.h" #include "ast/for_each_expr.h" #include "muz/base/dl_rule_set.h" #include "smt/tactic/unit_subsumption_tactic.h" #include "model/model_smt2_pp.h" #include "muz/transforms/dl_mk_rule_inliner.h" #include "ast/ast_smt2_pp.h" #include "ast/ast_ll_pp.h" #include "ast/ast_util.h" #include "ast/proofs/proof_checker.h" #include "smt/smt_value_sort.h" #include "ast/scoped_proof.h" #include "muz/spacer/spacer_qe_project.h" #include "tactic/core/blast_term_ite_tactic.h" #include "util/timeit.h" #include "util/luby.h" #include "ast/rewriter/expr_safe_replace.h" #include "ast/expr_abstract.h" #include "smt/smt_solver.h" #include "muz/spacer/spacer_sat_answer.h" #define WEAKNESS_MAX 65535 namespace spacer { /// pob -- proof obligation pob::pob (pob* parent, pred_transformer& pt, unsigned level, unsigned depth, bool add_to_parent): m_ref_count (0), m_parent (parent), m_pt (pt), m_post (m_pt.get_ast_manager ()), m_binding(m_pt.get_ast_manager()), m_new_post (m_pt.get_ast_manager ()), m_level (level), m_depth (depth), m_open (true), m_use_farkas (true), m_in_queue(false), m_weakness(0), m_blocked_lvl(0) { if (add_to_parent && m_parent) { m_parent->add_child(*this); } } void pob::set_post(expr* post) { app_ref_vector empty_binding(get_ast_manager()); set_post(post, empty_binding); } void pob::set_post(expr* post, app_ref_vector const &binding) { SASSERT(!is_in_queue()); normalize(post, m_post, m_pt.get_context().simplify_pob(), m_pt.get_context().use_euf_gen()); m_binding.reset(); if (!binding.empty()) {m_binding.append(binding);} } void pob::inherit(pob const &p) { SASSERT(!is_in_queue()); SASSERT(m_parent == p.m_parent); SASSERT(&m_pt == &p.m_pt); SASSERT(m_post == p.m_post); SASSERT(!m_new_post); m_binding.reset(); m_binding.append(p.m_binding); m_level = p.m_level; m_depth = p.m_depth; m_open = p.m_open; m_use_farkas = p.m_use_farkas; m_weakness = p.m_weakness; m_derivation = nullptr; } void pob::close () { if (!m_open) { return; } m_derivation = nullptr; m_open = false; for (unsigned i = 0, sz = m_kids.size (); i < sz; ++i) { m_kids [i]->close(); } } void pob::get_skolems(app_ref_vector &v) { for (unsigned i = 0, sz = m_binding.size(); i < sz; ++i) { expr* e; e = m_binding.get(i); v.push_back (mk_zk_const (get_ast_manager(), i, get_sort(e))); } } std::ostream &pob::display(std::ostream &out, bool full) const { out << pt().head()->get_name () << " level: " << level() << " depth: " << depth() << " post_id: " << post()->get_id() << (is_in_queue() ? " in_queue" : ""); if (full) out << "\n" << m_post; return out; } // ---------------- // pob_queue pob* pob_queue::top () { /// nothing in the queue if (m_data.empty()) { return nullptr; } /// top queue element is above max level if (m_data.top()->level() > m_max_level) { return nullptr; } /// top queue element is at the max level, but at a higher than base depth if (m_data.top ()->level () == m_max_level && m_data.top()->depth() > m_min_depth) { return nullptr; } /// there is something good in the queue return m_data.top (); } void pob_queue::pop() { pob *p = m_data.top(); p->set_in_queue(false); m_data.pop(); } void pob_queue::set_root(pob& root) { m_root = &root; m_max_level = root.level (); m_min_depth = root.depth (); reset(); } void pob_queue::reset() { while (!m_data.empty()) { pob *p = m_data.top(); m_data.pop(); p->set_in_queue(false); } if (m_root) { SASSERT(!m_root->is_in_queue()); m_root->set_in_queue(true); m_data.push(m_root.get()); } } void pob_queue::push(pob &n) { if (!n.is_in_queue()) { TRACE("pob_queue", tout << "pob_queue::push(" << n.post()->get_id() << ")\n";); n.set_in_queue(true); m_data.push (&n); n.get_context().new_pob_eh(&n); } } // ---------------- // derivation derivation::derivation (pob& parent, datalog::rule const& rule, expr *trans, app_ref_vector const &evars) : m_parent (parent), m_rule (rule), m_premises (), m_active (0), m_trans (trans, m_parent.get_ast_manager ()), m_evars (evars) {} void derivation::add_premise (pred_transformer &pt, unsigned oidx, expr* summary, bool must, const ptr_vector *aux_vars) {m_premises.push_back (premise (pt, oidx, summary, must, aux_vars));} pob *derivation::create_first_child (model &mdl) { if (m_premises.empty()) { return nullptr; } m_active = 0; return create_next_child(mdl); } void derivation::exist_skolemize(expr* fml, app_ref_vector &vars, expr_ref &res) { ast_manager &m = get_ast_manager(); if (vars.empty()) {res = fml; return;} if (m.is_true(fml) || m.is_false(fml)) {res = fml; return;} { std::stable_sort (vars.c_ptr(), vars.c_ptr() + vars.size(), sk_lt_proc()); unsigned i, j, end; app_ref v(m); for (i = 1, j = 1, end = vars.size(); i < end; ++i) { if (vars.get(j-1) != vars.get(i)) { v = vars.get(i); // keep ref vars.set(j++, v); } } vars.shrink(j); } TRACE("spacer", tout << "Skolemizing: "; for (auto v : vars) tout << " " << mk_pp(v, m) << " "; tout << "\nfrom " << mk_pp(fml, m) << "\n"; ); app_ref_vector pinned(m); expr_safe_replace sub(m); for (unsigned i = 0, sz = vars.size(); i < sz; ++i) { expr* e; e = vars.get(i); pinned.push_back (mk_zk_const (m, i, get_sort(e))); sub.insert (e, pinned.back()); } sub(fml, res); } pob *derivation::create_next_child(model &mdl) { timeit _timer (is_trace_enabled("spacer_timeit"), "spacer::derivation::create_next_child", verbose_stream ()); ast_manager &m = get_ast_manager (); expr_ref_vector summaries (m); app_ref_vector vars(m); // -- find first may premise while (m_active < m_premises.size() && m_premises[m_active].is_must()) { summaries.push_back (m_premises[m_active].get_summary ()); vars.append (m_premises[m_active].get_ovars ()); ++m_active; } if (m_active >= m_premises.size()) { return nullptr; } // -- update m_trans with the pre-image of m_trans over the must summaries summaries.push_back (m_trans); m_trans = mk_and (summaries); summaries.reset (); if (!vars.empty()) { timeit _timer1 (is_trace_enabled("spacer_timeit"), "create_next_child::qproject1", verbose_stream ()); vars.append(m_evars); m_evars.reset(); pt().mbp(vars, m_trans, mdl, true, pt().get_context().use_ground_pob()); CTRACE("spacer", !vars.empty(), tout << "Failed to eliminate: " << vars << "\n";); m_evars.append (vars); vars.reset(); } if (!mdl.is_true(m_premises[m_active].get_summary())) { IF_VERBOSE(1, verbose_stream() << "Summary unexpectendly not true\n";); return nullptr; } // create the post-condition by computing a post-image over summaries // that precede currently active premise for (unsigned i = m_active + 1; i < m_premises.size(); ++i) { summaries.push_back (m_premises [i].get_summary ()); vars.append (m_premises [i].get_ovars ()); } summaries.push_back (m_trans); expr_ref post(m); post = mk_and(summaries); summaries.reset (); if (!vars.empty()) { timeit _timer2(is_trace_enabled("spacer_timeit"), "create_next_child::qproject2", verbose_stream ()); // include m_evars in case they can eliminated now as well vars.append(m_evars); pt().mbp(vars, post, mdl, true, pt().get_context().use_ground_pob()); CTRACE("spacer", !vars.empty(), tout << "Failed to eliminate: " << vars << "\n";); //qe::reduce_array_selects (*mev.get_model (), post); } else { // if no variables to eliminate, don't forget about m_evars // that occur in m_trans vars.append(m_evars); } if (!vars.empty()) { // existentially quantify out vars from post and skolemize the result exist_skolemize(post.get(), vars, post); } get_manager ().formula_o2n (post.get (), post, m_premises [m_active].get_oidx (), vars.empty()); /* The level and depth are taken from the parent, not the sibling. The reasoning is that the sibling has not been checked before, and lower level is a better starting point. */ pob *n = m_premises[m_active].pt().mk_pob(&m_parent, prev_level (m_parent.level ()), m_parent.depth (), post, vars); IF_VERBOSE (1, verbose_stream () << "\n\tcreate_child: " << n->pt ().head ()->get_name () << " (" << n->level () << ", " << n->depth () << ") " << (n->use_farkas_generalizer () ? "FAR " : "SUB ") << n->post ()->get_id (); verbose_stream().flush ();); return n; } pob *derivation::create_next_child () { if (m_active + 1 >= m_premises.size()) { return nullptr; } // update the summary of the active node to some must summary // construct a new model consistent with the must summary of m_active premise pred_transformer &pt = m_premises[m_active].pt (); ast_manager &m = get_ast_manager (); manager &pm = get_manager (); expr_ref_vector summaries (m); for (unsigned i = m_active + 1; i < m_premises.size (); ++i) { summaries.push_back(m_premises [i].get_summary()); } // -- orient transition relation towards m_active premise expr_ref active_trans (m); pm.formula_o2n (m_trans, active_trans, m_premises[m_active].get_oidx (), false); summaries.push_back (active_trans); // if not true, bail out, the must summary of m_active is not strong enough // this is possible if m_post was weakened for some reason model_ref mdl; if (!pt.is_must_reachable(mk_and(summaries), &mdl)) { return nullptr; } mdl->set_model_completion(false); // find must summary used reach_fact *rf = pt.get_used_rf (*mdl, true); // get an implicant of the summary expr_ref_vector u(m), lits(m); u.push_back (rf->get ()); compute_implicant_literals (*mdl, u, lits); expr_ref v(m); v = mk_and (lits); // XXX The summary is not used by anyone after this point m_premises[m_active].set_summary (v, true, &(rf->aux_vars ())); /** HACK: needs a rewrite * compute post over the new must summary this must be done here * because the must summary is currently described over new * variables. However, we store it over old-variables, but we do * not update the model. So we must get rid of all of the * new-variables at this point. */ { pred_transformer &pt = m_premises[m_active].pt (); app_ref_vector vars (m); summaries.reset (); summaries.push_back (v); summaries.push_back (active_trans); m_trans = mk_and (summaries); // variables to eliminate vars.append (rf->aux_vars ().size (), rf->aux_vars ().c_ptr ()); for (unsigned i = 0, sz = pt.head ()->get_arity (); i < sz; ++i) { vars.push_back(m.mk_const(pm.o2n(pt.sig(i), 0))); } if (!vars.empty ()) { vars.append(m_evars); m_evars.reset(); this->pt().mbp(vars, m_trans, *mdl, true, this->pt().get_context().use_ground_pob()); // keep track of implicitly quantified variables CTRACE("spacer", !vars.empty(), tout << "Failed to eliminate: " << vars << "\n";); m_evars.append (vars); vars.reset(); } } m_active++; return create_next_child (*mdl); } /// derivation::premise derivation::premise::premise (pred_transformer &pt, unsigned oidx, expr *summary, bool must, const ptr_vector *aux_vars) : m_pt (pt), m_oidx (oidx), m_summary (summary, pt.get_ast_manager ()), m_must (must), m_ovars (pt.get_ast_manager ()) { ast_manager &m = m_pt.get_ast_manager (); manager &sm = m_pt.get_manager (); unsigned sig_sz = m_pt.head ()->get_arity (); for (unsigned i = 0; i < sig_sz; ++i) { m_ovars.push_back(m.mk_const(sm.o2o(pt.sig(i), 0, m_oidx))); } if (aux_vars) for (unsigned i = 0, sz = aux_vars->size (); i < sz; ++i) { m_ovars.push_back(m.mk_const(sm.n2o(aux_vars->get(i)->get_decl(), m_oidx))); } } derivation::premise::premise (const derivation::premise &p) : m_pt (p.m_pt), m_oidx (p.m_oidx), m_summary (p.m_summary), m_must (p.m_must), m_ovars (p.m_ovars) {} /// \brief Updated the summary. /// The new summary is over n-variables. void derivation::premise::set_summary (expr * summary, bool must, const ptr_vector *aux_vars) { ast_manager &m = m_pt.get_ast_manager (); manager &sm = m_pt.get_manager (); unsigned sig_sz = m_pt.head ()->get_arity (); m_must = must; sm.formula_n2o (summary, m_summary, m_oidx); m_ovars.reset (); for (unsigned i = 0; i < sig_sz; ++i) { m_ovars.push_back(m.mk_const(sm.o2o(m_pt.sig(i), 0, m_oidx))); } if (aux_vars) for (unsigned i = 0, sz = aux_vars->size (); i < sz; ++i) m_ovars.push_back (m.mk_const (sm.n2o (aux_vars->get (i)->get_decl (), m_oidx))); } /// Lemma lemma::lemma (ast_manager &manager, expr * body, unsigned lvl) : m_ref_count(0), m(manager), m_body(body, m), m_cube(m), m_zks(m), m_bindings(m), m_pob(nullptr), m_ctp(nullptr), m_lvl(lvl), m_init_lvl(m_lvl), m_bumped(0), m_weakness(WEAKNESS_MAX), m_external(false), m_blocked(false), m_background(false) { SASSERT(m_body); normalize(m_body, m_body); } lemma::lemma(pob_ref const &p) : m_ref_count(0), m(p->get_ast_manager()), m_body(m), m_cube(m), m_zks(m), m_bindings(m), m_pob(p), m_ctp(nullptr), m_lvl(p->level()), m_init_lvl(m_lvl), m_bumped(0), m_weakness(p->weakness()), m_external(false), m_blocked(false), m_background(false) { SASSERT(m_pob); m_pob->get_skolems(m_zks); add_binding(m_pob->get_binding()); } lemma::lemma(pob_ref const &p, expr_ref_vector &cube, unsigned lvl) : m_ref_count(0), m(p->get_ast_manager()), m_body(m), m_cube(m), m_zks(m), m_bindings(m), m_pob(p), m_ctp(nullptr), m_lvl(p->level()), m_init_lvl(m_lvl), m_bumped(0), m_weakness(p->weakness()), m_external(false), m_blocked(false), m_background(false) { if (m_pob) { m_pob->get_skolems(m_zks); add_binding(m_pob->get_binding()); } update_cube(p, cube); set_level(lvl); } void lemma::add_skolem(app *zk, app *b) { SASSERT(m_bindings.size() == m_zks.size()); // extend bindings m_bindings.push_back(b); // extend skolems m_zks.push_back(zk); } void lemma::mk_expr_core() { if (m_body) {return;} if (m_pob) {mk_cube_core();} SASSERT(!m_cube.empty()); m_body = ::mk_and(m_cube); // normalize works better with a cube normalize(m_body, m_body, false /* no simplify bounds */, false /* term_graph */); m_body = ::push_not(m_body); if (!m_zks.empty() && has_zk_const(m_body)) { app_ref_vector zks(m); zks.append(m_zks); zks.reverse(); expr_abstract(m, 0, zks.size(), (expr* const*)zks.c_ptr(), m_body, m_body); ptr_buffer sorts; svector names; for (unsigned i=0, sz=zks.size(); i < sz; ++i) { sorts.push_back(get_sort(zks.get(i))); names.push_back(zks.get(i)->get_decl()->get_name()); } m_body = m.mk_quantifier(forall_k, zks.size(), sorts.c_ptr(), names.c_ptr(), m_body, 15, symbol(m_body->get_id())); } SASSERT(m_body); } void lemma::mk_cube_core() { if (!m_cube.empty()) {return;} expr_ref cube(m); if (m_pob || m_body) { if (m_pob) {cube = m_pob->post();} else if (m_body) { // no quantifiers for now SASSERT(!is_quantifier(m_body)); cube = m_body; cube = ::push_not(cube); } flatten_and(cube, m_cube); if (m_cube.empty()) { m_cube.push_back(m.mk_true()); } else { std::sort(m_cube.c_ptr(), m_cube.c_ptr() + m_cube.size(), ast_lt_proc()); } } else { UNREACHABLE(); } } bool lemma::is_false() { // a lemma is false if // 1. it is defined by a cube, and the cube contains a single literal 'true' // 2. it is defined by a body, and the body is a single literal false // 3. it is defined by a pob, and the pob post is false if (m_cube.size() == 1) {return m.is_true(m_cube.get(0));} else if (m_body) {return m.is_false(m_body);} else if (m_pob) {return m.is_true(m_pob->post());} return false; } expr* lemma::get_expr() { mk_expr_core(); return m_body; } expr_ref_vector const &lemma::get_cube() { mk_cube_core(); return m_cube; } void lemma::update_cube (pob_ref const &p, expr_ref_vector &cube) { SASSERT(m_pob); SASSERT(m_pob.get() == p.get()); m_cube.reset(); m_body.reset(); m_cube.append(cube); if (m_cube.empty()) {m_cube.push_back(m.mk_true());} // after the cube is updated, if there are no skolems, // convert the lemma to quantifier-free bool is_quant = false; for (unsigned i = 0, sz = cube.size(); !is_quant && i < sz; ++i) { is_quant = has_zk_const(cube.get(i)); } if (!is_quant) { m_zks.reset(); m_bindings.reset(); } } bool lemma::has_binding(app_ref_vector const &binding) { unsigned num_decls = m_zks.size(); SASSERT(binding.size() == num_decls); if (num_decls == 0) return true; for (unsigned off = 0, sz = m_bindings.size(); off < sz; off += num_decls) { unsigned i = 0; for (; i < num_decls; ++i) { if (m_bindings.get(off + i) != binding.get(i)) { break; } } if (i == num_decls) return true; } return false; } void lemma::add_binding(app_ref_vector const &binding) { if (!has_binding(binding)) { m_bindings.append(binding); TRACE("spacer", tout << "new binding: "; for (unsigned i = 0; i < binding.size(); i++) tout << mk_pp(binding.get(i), m) << " "; tout << "\n";); } } void lemma::instantiate(expr * const * exprs, expr_ref &result, expr *e) { expr *lem = e == nullptr ? get_expr() : e; if (!is_quantifier (lem) || m_bindings.empty()) {return;} expr *body = to_quantifier(lem)->get_expr(); unsigned num_decls = to_quantifier(lem)->get_num_decls(); var_subst vs(m, false); result = vs(body, num_decls, exprs); } void lemma::set_level (unsigned lvl) { if (m_pob){m_pob->blocked_at(lvl);} m_lvl = lvl; } void lemma::mk_insts(expr_ref_vector &out, expr* e) { expr *lem = e == nullptr ? get_expr() : e; if (!is_quantifier (lem) || m_bindings.empty()) {return;} unsigned num_decls = to_quantifier(lem)->get_num_decls(); expr_ref inst(m); for (unsigned off = 0, sz = m_bindings.size(); off < sz; off += num_decls) { instantiate((expr * const *) m_bindings.c_ptr() + off, inst, e); out.push_back(inst); inst.reset(); } } // ---------------- // pred_tansformer pred_transformer::pt_rule &pred_transformer::pt_rules::mk_rule(const pred_transformer::pt_rule &v) { pt_rule *p = nullptr; if (find_by_rule(v.rule(), p)) return *p; p = alloc(pt_rule, v); m_rules.insert(&p->rule(), p); if (p->tag()) m_tags.insert(p->tag(), p); return *p; } pred_transformer::pred_transformer(context& ctx, manager& pm, func_decl* head): pm(pm), m(pm.get_manager()), ctx(ctx), m_head(head, m), m_sig(m), m_reach_solver (ctx.mk_solver2()), m_pobs(*this), m_frames(*this), m_reach_facts(), m_rf_init_sz(0), m_transition_clause(m), m_transition(m), m_init(m), m_extend_lit0(m), m_extend_lit(m), m_all_init(false) { m_solver = alloc(prop_solver, m, ctx.mk_solver0(), ctx.mk_solver1(), ctx.get_params(), head->get_name()); init_sig (); m_extend_lit = mk_extend_lit(); m_extend_lit0 = m_extend_lit; } app_ref pred_transformer::mk_extend_lit() { app_ref v(m); std::stringstream name; name << m_head->get_name () << "_ext0"; v = m.mk_const (symbol(name.str().c_str()), m.mk_bool_sort()); return app_ref(m.mk_not (m.mk_const (pm.get_n_pred (v->get_decl ()))), m); } std::ostream& pred_transformer::display(std::ostream& out) const { if (!rules().empty()) { out << "rules\n"; } datalog::rule_manager& rm = ctx.get_datalog_context().get_rule_manager(); for (unsigned i = 0; i < rules().size(); ++i) { rm.display_smt2(*rules()[i], out) << "\n"; } out << "transition\n" << mk_pp(transition(), m) << "\n"; return out; } void pred_transformer::collect_statistics(statistics& st) const { m_solver->collect_statistics(st); // -- number of times a lemma has been propagated to a higher level // -- during push st.update("SPACER num propagations", m_stats.m_num_propagations); // -- number of lemmas in all current frames st.update("SPACER num active lemmas", m_frames.lemma_size ()); // -- number of lemmas that are inductive invariants st.update("SPACER num invariants", m_stats.m_num_invariants); // -- number of proof obligations (0 if pobs are not reused) st.update("SPACER num pobs", m_pobs.size()); // -- number of reach facts created st.update("SPACER num reach queries", m_stats.m_num_reach_queries); st.update("SPACER num ctp blocked", m_stats.m_num_ctp_blocked); st.update("SPACER num is_invariant", m_stats.m_num_is_invariant); st.update("SPACER num lemma jumped", m_stats.m_num_lemma_level_jump); // -- time in rule initialization st.update ("time.spacer.init_rules.pt.init", m_initialize_watch.get_seconds ()); // -- time is must_reachable() st.update ("time.spacer.solve.pt.must_reachable", m_must_reachable_watch.get_seconds ()); st.update("time.spacer.ctp", m_ctp_watch.get_seconds()); st.update("time.spacer.mbp", m_mbp_watch.get_seconds()); } void pred_transformer::reset_statistics() { m_solver->reset_statistics(); //m_reachable.reset_statistics(); m_stats.reset(); m_initialize_watch.reset (); m_must_reachable_watch.reset (); m_ctp_watch.reset(); m_mbp_watch.reset(); } void pred_transformer::init_sig() { for (unsigned i = 0; i < m_head->get_arity(); ++i) { sort * arg_sort = m_head->get_domain(i); std::stringstream name_stm; name_stm << m_head->get_name() << '_' << i; func_decl_ref stm(m); stm = m.mk_func_decl(symbol(name_stm.str().c_str()), 0, (sort*const*)nullptr, arg_sort); m_sig.push_back(pm.get_o_pred(stm, 0)); } } void pred_transformer::ensure_level(unsigned level) { if (is_infty_level(level)) { return; } while (m_frames.size() <= level) { m_frames.add_frame (); m_solver->add_level (); } } bool pred_transformer::is_must_reachable(expr* state, model_ref* model) { scoped_watch _t_(m_must_reachable_watch); SASSERT (state); // XXX This seems to mis-handle the case when state is // reachable using the init rule of the current transformer if (m_reach_facts.empty()) { return false; } m_reach_solver->push (); m_reach_solver->assert_expr (state); m_reach_solver->assert_expr (m.mk_not (m_reach_facts.back()->tag())); lbool res = m_reach_solver->check_sat (0, nullptr); if (model) { m_reach_solver->get_model(*model); } m_reach_solver->pop (1); return (res == l_true); } reach_fact* pred_transformer::get_used_rf (model& mdl, bool all) { expr_ref v (m); model::scoped_model_completion _sc_(mdl, false); for (auto *rf : m_reach_facts) { if (!all && rf->is_init()) continue; if (mdl.is_false(rf->tag())) return rf; } UNREACHABLE(); return nullptr; } reach_fact *pred_transformer::get_used_origin_rf(model& mdl, unsigned oidx) { expr_ref b(m), v(m); model::scoped_model_completion _sc_(mdl, false); for (auto *rf : m_reach_facts) { pm.formula_n2o (rf->tag(), v, oidx); if (mdl.is_false(v)) return rf; } UNREACHABLE(); return nullptr; } const datalog::rule *pred_transformer::find_rule(model &model) { expr_ref val(m); for (auto &kv : m_pt_rules) { app *tag = kv.m_value->tag(); if (model.is_true_decl(tag->get_decl())) { return &kv.m_value->rule(); } } return nullptr; } const datalog::rule *pred_transformer::find_rule(model &model, bool& is_concrete, vector& reach_pred_used, unsigned& num_reuse_reach) { // find a rule whose tag is true in the model; // prefer a rule where the model intersects with reach facts of all predecessors; // also find how many predecessors' reach facts are true in the model expr_ref vl(m); const datalog::rule *r = ((datalog::rule*)nullptr); //for (auto &entry : m_tag2rule) { for (auto &kv : m_pt_rules) { app* tag = kv.m_value->tag(); if (model.eval(tag->get_decl(), vl) && m.is_true(vl)) { r = &kv.m_value->rule(); is_concrete = true; num_reuse_reach = 0; reach_pred_used.reset(); unsigned tail_sz = r->get_uninterpreted_tail_size(); for (unsigned i = 0; i < tail_sz; i++) { bool used = false; func_decl* d = r->get_tail(i)->get_decl(); const pred_transformer &pt = ctx.get_pred_transformer(d); if (!pt.has_rfs()) {is_concrete = false;} else { expr_ref v(m); pm.formula_n2o(pt.get_last_rf_tag (), v, i); model.eval(to_app (v.get ())->get_decl (), vl); used = m.is_false (vl); is_concrete = is_concrete && used; } reach_pred_used.push_back (used); if (used) {num_reuse_reach++;} } if (is_concrete) {break;} } } // SASSERT (r); // reached by a reachability fact if (!r) { is_concrete = true; } return r; } void pred_transformer::find_predecessors(datalog::rule const& r, ptr_vector& preds) const { preds.reset(); unsigned tail_sz = r.get_uninterpreted_tail_size(); for (unsigned ti = 0; ti < tail_sz; ti++) { preds.push_back(r.get_tail(ti)->get_decl()); } } void pred_transformer::simplify_formulas() {m_frames.simplify_formulas ();} expr_ref pred_transformer::get_formulas(unsigned level, bool bg) const { expr_ref_vector res(m); m_frames.get_frame_geq_lemmas (level, res, bg); return mk_and(res); } bool pred_transformer::propagate_to_next_level (unsigned src_level) {return m_frames.propagate_to_next_level (src_level);} /// \brief adds a lemma to the solver and to child solvers void pred_transformer::add_lemma_core(lemma* lemma, bool ground_only) { SASSERT(!lemma->is_background()); unsigned lvl = lemma->level(); expr* l = lemma->get_expr(); SASSERT(!lemma->is_ground() || is_clause(m, l)); SASSERT(!is_quantifier(l) || is_clause(m, to_quantifier(l)->get_expr())); TRACE("spacer", tout << "add-lemma-core: " << pp_level (lvl) << " " << head ()->get_name () << " " << mk_pp (l, m) << "\n";); TRACE("core_array_eq", tout << "add-lemma-core: " << pp_level (lvl) << " " << head ()->get_name () << " " << mk_pp (l, m) << "\n";); STRACE ("spacer_progress", tout << "** add-lemma: " << pp_level (lvl) << " " << head ()->get_name () << " " << mk_epp (l, m) << "\n"; if (!lemma->is_ground()) { tout << "Bindings: " << lemma->get_bindings() << "\n"; } tout << "\n"; ); if (is_infty_level(lvl)) { m_stats.m_num_invariants++; } if (lemma->is_ground()) { if (is_infty_level(lvl)) { m_solver->assert_expr(l); } else { ensure_level (lvl); m_solver->assert_expr (l, lvl); } } for (unsigned i = 0, sz = m_use.size (); i < sz; ++i) { m_use [i]->add_lemma_from_child(*this, lemma, next_level(lvl), ground_only); } } bool pred_transformer::add_lemma (expr *e, unsigned lvl, bool bg) { lemma_ref lem = alloc(lemma, m, e, lvl); lem->set_background(bg); return m_frames.add_lemma(lem.get()); } void pred_transformer::add_lemma_from_child (pred_transformer& child, lemma* lemma, unsigned lvl, bool ground_only) { ensure_level(lvl); expr_ref_vector fmls(m); mk_assumptions(child.head(), lemma->get_expr(), fmls); for (unsigned i = 0; i < fmls.size(); ++i) { expr_ref_vector inst(m); expr* a = to_app(fmls.get(i))->get_arg(0); expr* l = to_app(fmls.get(i))->get_arg(1); if (!lemma->is_ground() && get_context().use_instantiate()) { expr_ref grnd_lemma(m); app_ref_vector tmp(m); lemma->mk_insts(inst, l); // -- take ground instance of the current lemma ground_expr(to_quantifier(l)->get_expr(), grnd_lemma, tmp); inst.push_back(grnd_lemma); } for (unsigned j=0; j < inst.size(); j++) { inst.set(j, m.mk_implies(a, inst.get(j))); } if (lemma->is_ground() || (get_context().use_qlemmas() && !ground_only)) { inst.push_back(fmls.get(i)); } SASSERT (!inst.empty ()); for (unsigned j = 0; j < inst.size(); ++j) { TRACE("spacer_detail", tout << "child property: " << mk_pp(inst.get (j), m) << "\n";); if (is_infty_level(lvl)) { m_solver->assert_expr(inst.get(j)); } else { m_solver->assert_expr(inst.get(j), lvl); } } } } app_ref pred_transformer::mk_fresh_rf_tag () { std::stringstream name; func_decl_ref decl(m); name << head ()->get_name () << "#reach_tag_" << m_reach_facts.size (); decl = m.mk_func_decl (symbol (name.str ().c_str ()), 0, (sort*const*)nullptr, m.mk_bool_sort ()); return app_ref(m.mk_const (pm.get_n_pred (decl)), m); } void pred_transformer::add_rf (reach_fact *rf) { timeit _timer (is_trace_enabled("spacer_timeit"), "spacer::pred_transformer::add_rf", verbose_stream ()); TRACE ("spacer", tout << "add_rf: " << head()->get_name() << " " << (rf->is_init () ? "INIT " : "") << mk_pp(rf->get (), m) << "\n";); // -- avoid duplicates if (!rf || get_rf(rf->get())) {return;} // all initial facts are grouped together SASSERT (!rf->is_init () || m_reach_facts.empty () || m_reach_facts.back ()->is_init ()); // create tags app_ref last_tag(m); app_ref new_tag(m); expr_ref fml(m); if (!m_reach_facts.empty()) {last_tag = m_reach_facts.back()->tag();} if (rf->is_init ()) new_tag = mk_fresh_rf_tag(); else // side-effect: updates m_solver with rf new_tag = to_app(extend_initial(rf->get())->get_arg(0)); rf->set_tag(new_tag); // add to m_reach_facts m_reach_facts.push_back (rf); if (rf->is_init()) {m_rf_init_sz++;} // update m_reach_solver if (last_tag) {fml = m.mk_or(m.mk_not(last_tag), rf->get(), rf->tag());} else {fml = m.mk_or(rf->get(), rf->tag());} m_reach_solver->assert_expr (fml); TRACE ("spacer", tout << "updating reach ctx: " << fml << "\n";); // update solvers of other pred_transformers // XXX wrap rf into a lemma to fit the API lemma fake_lemma(m, fml, infty_level()); // update users; reach facts are independent of levels for (auto use : m_use) use->add_lemma_from_child (*this, &fake_lemma, infty_level()); } expr_ref pred_transformer::get_reachable() { expr_ref res(m); res = m.mk_false(); if (!m_reach_facts.empty()) { expr_substitution sub(m); expr_ref c(m), v(m); for (unsigned i = 0, sz = sig_size(); i < sz; ++i) { c = m.mk_const(pm.o2n(sig(i), 0)); v = m.mk_var(i, sig(i)->get_range()); sub.insert(c, v); } scoped_ptr rep = mk_expr_simp_replacer(m); rep->set_substitution(&sub); expr_ref_vector args(m); for (unsigned i = 0, sz = m_reach_facts.size (); i < sz; ++i) { reach_fact *f; f = m_reach_facts.get(i); expr_ref r(m); r = f->get(); const ptr_vector &aux = f->aux_vars(); if (!aux.empty()) { // -- existentially quantify auxiliary variables r = mk_exists (m, aux.size(), aux.c_ptr(), r); // XXX not sure how this interacts with variable renaming later on. // XXX For now, simply dissallow existentially quantified auxiliaries NOT_IMPLEMENTED_YET(); } (*rep)(r); args.push_back (r); } res = mk_or(args); } return res; } expr* pred_transformer::get_last_rf_tag () const {return m_reach_facts.empty() ? nullptr : m_reach_facts.back()->tag();} expr_ref pred_transformer::get_cover_delta(func_decl* p_orig, int level) { expr_ref result(m.mk_true(), m), v(m), c(m); expr_ref_vector lemmas (m); m_frames.get_frame_lemmas (level == -1 ? infty_level() : level, lemmas); if (!lemmas.empty()) { result = mk_and(lemmas); } // replace local constants by bound variables. expr_substitution sub(m); for (unsigned i = 0; i < sig_size(); ++i) { c = m.mk_const(pm.o2n(sig(i), 0)); v = m.mk_var(i, sig(i)->get_range()); sub.insert(c, v); } scoped_ptr rep = mk_default_expr_replacer(m); rep->set_substitution(&sub); (*rep)(result); // adjust result according to model converter. unsigned arity = m_head->get_arity(); model_ref md = alloc(model, m); if (arity == 0) { md->register_decl(m_head, result); } else { func_interp* fi = alloc(func_interp, m, arity); fi->set_else(result); md->register_decl(m_head, fi); } model_converter_ref mc = ctx.get_model_converter(); apply(mc, md); if (p_orig->get_arity() == 0) { result = md->get_const_interp(p_orig); } else { result = md->get_func_interp(p_orig)->get_interp(); } return result; } /** * get an origin summary used by this transformer in the given model * level is the level at which may summaries are obtained * oidx is the origin index of this predicate in the model * must indicates whether a must or a may summary is requested * * returns an implicant of the summary */ expr_ref pred_transformer::get_origin_summary (model &mdl, unsigned level, unsigned oidx, bool must, const ptr_vector **aux) { model::scoped_model_completion _sc_(mdl, false); expr_ref_vector summary (m); expr_ref v(m); if (!must) { // use may summary summary.push_back (get_formulas(level)); // -- no auxiliary variables in lemmas *aux = nullptr; } else { // find must summary to use reach_fact *f = get_used_origin_rf(mdl, oidx); summary.push_back (f->get ()); *aux = &f->aux_vars (); } SASSERT (!summary.empty ()); // -- convert to origin for (unsigned i = 0; i < summary.size(); ++i) { pm.formula_n2o (summary.get (i), v, oidx); summary[i] = v; } // bail out of if the model is insufficient // (skip quantified lemmas cause we can't validate them in the model) // TBD: for quantified lemmas use current instances flatten_and(summary); for (auto *s : summary) { if (!is_quantifier(s) && !mdl.is_true(s)) { TRACE("spacer", tout << "Summary not true in the model: " << mk_pp(s, m) << "\n";); // return expr_ref(m); } } // -- pick an implicant expr_ref_vector lits(m); compute_implicant_literals (mdl, summary, lits); return mk_and(lits); } void pred_transformer::add_cover(unsigned level, expr* property, bool bg) { SASSERT(!bg || is_infty_level(level)); // replace bound variables by local constants. expr_ref result(property, m), v(m), c(m); expr_substitution sub(m); proof_ref pr(m); pr = m.mk_asserted(m.mk_true()); for (unsigned i = 0; i < sig_size(); ++i) { c = m.mk_const(pm.o2n(sig(i), 0)); v = m.mk_var(i, sig(i)->get_range()); sub.insert(v, c, pr); } scoped_ptr rep = mk_default_expr_replacer(m); rep->set_substitution(&sub); (*rep)(result); TRACE("spacer", tout << "cover:\n" << mk_pp(result, m) << "\n";); // add the property. expr_ref_vector lemmas(m); flatten_and(result, lemmas); for (unsigned i = 0, sz = lemmas.size(); i < sz; ++i) { add_lemma(lemmas.get(i), level, bg); } } void pred_transformer::propagate_to_infinity (unsigned level) {m_frames.propagate_to_infinity (level);} // compute a conjunction of all background facts void pred_transformer::get_pred_bg_invs(expr_ref_vector& out) { expr_ref inv(m), tmp1(m), tmp2(m); ptr_vector preds; for (auto kv : m_pt_rules) { expr* tag = kv.m_value->tag(); datalog::rule const &r = kv.m_value->rule(); find_predecessors (r, preds); for (unsigned i = 0, preds_sz = preds.size(); i < preds_sz; i++) { func_decl* pre = preds[i]; pred_transformer &pt = ctx.get_pred_transformer(pre); const lemma_ref_vector &invs = pt.get_bg_invs(); CTRACE("spacer", !invs.empty(), tout << "add-bg-invariant: " << mk_pp (pre, m) << "\n";); for (auto inv : invs) { // tag -> inv1 ... tag -> invn tmp1 = m.mk_implies(tag, inv->get_expr()); pm.formula_n2o(tmp1, tmp2, i); out.push_back(tmp2); TRACE("spacer", tout << tmp2 << "\n";); } } } } /// \brief Returns true if the obligation is already blocked by current lemmas bool pred_transformer::is_blocked (pob &n, unsigned &uses_level) { ensure_level (n.level ()); prop_solver::scoped_level _sl (*m_solver, n.level ()); m_solver->set_core (nullptr); m_solver->set_model (nullptr); expr_ref_vector post(m), _aux(m); post.push_back (n.post ()); // this only uses the lemmas at the current level // transition relation is irrelevant // XXX quic3: not all lemmas are asserted at the post-condition lbool res = m_solver->check_assumptions (post, _aux, _aux, 0, nullptr, 0); if (res == l_false) { uses_level = m_solver->uses_level(); } return res == l_false; } bool pred_transformer::is_qblocked (pob &n) { // XXX currently disabled return false; params_ref p; p.set_bool("arith.ignore_int", true); p.set_bool("array.weak", true); p.set_bool("mbqi", false); scoped_ptr s; s = mk_smt_solver(m, p, symbol::null); s->updt_params(p); // XXX force parameters to be set s->push(); s->pop(1); expr_ref_vector frame_lemmas(m); m_frames.get_frame_geq_lemmas (n.level (), frame_lemmas); // assert all lemmas bool has_quant = false; for (unsigned i = 0, sz = frame_lemmas.size (); i < sz; ++i) { has_quant = has_quant || is_quantifier(frame_lemmas.get(i)); s->assert_expr(frame_lemmas.get(i)); } if (!has_quant) return false; // assert cti s->assert_expr(n.post()); lbool res = s->check_sat(0, nullptr); // if (res == l_false) { // expr_ref_vector core(m); // solver->get_itp_core(core); // expr_ref c(m); // c = mk_and(core); // STRACE("spacer_progress", tout << "core: " << mk_epp(c,m) << "\n";); // } return res == l_false; } void pred_transformer::mbp(app_ref_vector &vars, expr_ref &fml, model &mdl, bool reduce_all_selects, bool force) { scoped_watch _t_(m_mbp_watch); qe_project(m, vars, fml, mdl, reduce_all_selects, use_native_mbp(), !force); } // // check if predicate transformer has a satisfiable predecessor state. // returns either a satisfiable predecessor state or // return a property that blocks state and is implied by the // predicate transformer (or some unfolding of it). // lbool pred_transformer::is_reachable(pob& n, expr_ref_vector* core, model_ref* model, unsigned& uses_level, bool& is_concrete, datalog::rule const*& r, vector& reach_pred_used, unsigned& num_reuse_reach) { TRACE("spacer", tout << "is-reachable: " << head()->get_name() << " level: " << n.level() << " depth: " << n.depth () << "\n"; tout << mk_pp(n.post(), m) << "\n";); timeit _timer (is_trace_enabled("spacer_timeit"), "spacer::pred_transformer::is_reachable", verbose_stream ()); ensure_level(n.level()); // prepare the solver prop_solver::scoped_level _sl(*m_solver, n.level()); prop_solver::scoped_subset_core _sc (*m_solver, !n.use_farkas_generalizer ()); prop_solver::scoped_weakness _sw(*m_solver, 0, ctx.weak_abs() ? n.weakness() : UINT_MAX); m_solver->set_core(core); m_solver->set_model(model); expr_ref_vector post (m), reach_assumps (m); post.push_back (n.post ()); flatten_and(post); // if equality propagation is disabled in arithmetic, expand // equality literals into two inequalities to increase the space // for interpolation if (!ctx.use_eq_prop()) { expand_literals(m, post); } // populate reach_assumps if (n.level () > 0 && !m_all_init) { for (auto &kv : m_pt_rules) { datalog::rule const* r = &kv.m_value->rule(); find_predecessors(*r, m_predicates); if (m_predicates.empty()) {continue;} for (unsigned i = 0; i < m_predicates.size(); i++) { const pred_transformer &pt = ctx.get_pred_transformer(m_predicates[i]); if (pt.has_rfs()) { expr_ref a(m); pm.formula_n2o(pt.get_last_rf_tag(), a, i); reach_assumps.push_back(m.mk_not (a)); } else { reach_assumps.push_back(m.mk_not (kv.m_value->tag())); break; } } } } TRACE ("spacer", if (!reach_assumps.empty ()) { tout << "reach assumptions\n"; for (unsigned i = 0; i < reach_assumps.size (); i++) { tout << mk_pp (reach_assumps.get (i), m) << "\n"; } } ); // check local reachability; // result is either sat (with some reach assumps) or // unsat (even with no reach assumps) expr *bg = m_extend_lit.get (); lbool is_sat = m_solver->check_assumptions (post, reach_assumps, m_transition_clause, 1, &bg, 0); TRACE ("spacer", if (!reach_assumps.empty ()) { tout << "reach assumptions used\n"; for (unsigned i = 0; i < reach_assumps.size (); i++) { tout << mk_pp (reach_assumps.get (i), m) << "\n"; } } ); if (is_sat == l_true || is_sat == l_undef) { if (core) { core->reset(); } if (model && model->get()) { r = find_rule(**model, is_concrete, reach_pred_used, num_reuse_reach); TRACE ("spacer", tout << "reachable " << "is_concrete " << is_concrete << " rused: "; for (unsigned i = 0, sz = reach_pred_used.size (); i < sz; ++i) tout << reach_pred_used [i]; tout << "\n";); } return is_sat; } if (is_sat == l_false) { SASSERT (reach_assumps.empty ()); TRACE ("spacer", tout << "unreachable with lemmas\n"; if (core) { tout << "Core:\n"; for (unsigned i = 0; i < core->size (); i++) { tout << mk_pp (core->get(i), m) << "\n"; } } ); uses_level = m_solver->uses_level(); return l_false; } UNREACHABLE(); return l_undef; } /// returns true if lemma is blocked by an existing model bool pred_transformer::is_ctp_blocked(lemma *lem) { if (!ctx.use_ctp()) {return false;} if (!lem->has_ctp()) {return false;} scoped_watch _t_(m_ctp_watch); model_ref &ctp = lem->get_ctp(); // -- find rule of the ctp const datalog::rule *r; r = find_rule(*ctp); if (r == nullptr) { // no rules means lemma is blocked forever because // it does not satisfy some derived facts lem->set_blocked(true); return true; } // -- find predicates along the rule find_predecessors(*r, m_predicates); // check if any lemma blocks the ctp model for (unsigned i = 0, sz = m_predicates.size(); i < sz; ++i) { pred_transformer &pt = ctx.get_pred_transformer(m_predicates[i]); expr_ref lemmas(m), val(m); lemmas = pt.get_formulas(lem->level()); pm.formula_n2o(lemmas.get(), lemmas, i); if (ctp->is_false(lemmas)) return false; } // lem is blocked by ctp since none of the lemmas at the previous // level block ctp return true; } bool pred_transformer::is_invariant(unsigned level, lemma* lem, unsigned& solver_level, expr_ref_vector* core) { if (lem->is_blocked()) return false; m_stats.m_num_is_invariant++; if (is_ctp_blocked(lem)) { m_stats.m_num_ctp_blocked++; return false; } expr_ref lemma_expr(m); lemma_expr = lem->get_expr(); expr_ref_vector cand(m), aux(m), conj(m); expr_ref gnd_lemma(m); if (!get_context().use_qlemmas() && !lem->is_ground()) { app_ref_vector tmp(m); ground_expr(to_quantifier(lemma_expr)->get_expr (), gnd_lemma, tmp); lemma_expr = gnd_lemma.get(); } cand.push_back(mk_not(m, lemma_expr)); flatten_and (cand); prop_solver::scoped_level _sl(*m_solver, level); prop_solver::scoped_subset_core _sc (*m_solver, true); prop_solver::scoped_weakness _sw (*m_solver, 1, ctx.weak_abs() ? lem->weakness() : UINT_MAX); model_ref mdl; model_ref *mdl_ref_ptr = nullptr; if (ctx.use_ctp()) {mdl_ref_ptr = &mdl;} m_solver->set_core(core); m_solver->set_model(mdl_ref_ptr); conj.push_back(m_extend_lit); if (ctx.use_bg_invs()) get_pred_bg_invs(conj); lbool r = m_solver->check_assumptions (cand, aux, m_transition_clause, conj.size(), conj.c_ptr(), 1); if (r == l_false) { solver_level = m_solver->uses_level (); lem->reset_ctp(); if (level < m_solver->uses_level()) {m_stats.m_num_lemma_level_jump++;} SASSERT (level <= solver_level); } else if (r == l_true) { // TBD: optionally remove unused symbols from the model if (mdl_ref_ptr) {lem->set_ctp(*mdl_ref_ptr);} } else {lem->reset_ctp();} return r == l_false; } bool pred_transformer::check_inductive(unsigned level, expr_ref_vector& state, unsigned& uses_level, unsigned weakness) { expr_ref_vector conj(m), core(m); expr_ref states(m); states = mk_and(state); states = m.mk_not(states); mk_assumptions(head(), states, conj); prop_solver::scoped_level _sl(*m_solver, level); prop_solver::scoped_subset_core _sc (*m_solver, true); prop_solver::scoped_weakness _sw (*m_solver, 1, ctx.weak_abs() ? weakness : UINT_MAX); m_solver->set_core(&core); m_solver->set_model (nullptr); expr_ref_vector aux (m); if (ctx.use_bg_invs()) get_pred_bg_invs(conj); conj.push_back (m_extend_lit); lbool res = m_solver->check_assumptions (state, aux, m_transition_clause, conj.size (), conj.c_ptr (), 1); if (res == l_false) { state.reset(); state.append(core); uses_level = m_solver->uses_level(); } TRACE ("core_array_eq", tout << "check_inductive: " << "states: " << mk_pp (states, m) << " is: " << res << "\n" << "with transition: " << mk_pp (m_transition, m) << "\n";); return res == l_false; } void pred_transformer::mk_assumptions(func_decl* head, expr* fml, expr_ref_vector& result) { expr_ref tmp1(m), tmp2(m); for (auto& kv : m_pt_rules) { expr* tag = kv.m_value->tag(); datalog::rule const& r = kv.m_value->rule(); find_predecessors(r, m_predicates); for (unsigned i = 0; i < m_predicates.size(); i++) { func_decl* d = m_predicates[i]; if (d == head) { tmp1 = m.mk_implies(tag, fml); pm.formula_n2o(tmp1, tmp2, i); result.push_back(tmp2); } } } } void pred_transformer::initialize(decl2rel const& pts) { m_init = m.mk_false(); m_transition = m.mk_true(); init_rules(pts); th_rewriter rw(m); rw(m_transition); rw(m_init); m_solver->assert_expr (m_transition); m_solver->assert_expr (m_init, 0); TRACE("spacer", tout << "Initial state: " << mk_pp(m_init, m) << "\n"; tout << "Transition: " << mk_pp(m_transition, m) << "\n";); SASSERT(is_app(m_init)); //m_reachable.add_init(to_app(m_init)); } void pred_transformer::init_rfs () { expr_ref_vector v(m); reach_fact_ref fact; for (auto &kv : m_pt_rules) { pt_rule &ptr = *kv.m_value; const datalog::rule& r = ptr.rule(); if (ptr.is_init()) { fact = alloc(reach_fact, m, r, ptr.trans(), ptr.auxs(), true); add_rf(fact.get()); } } } void pred_transformer::init_rules(decl2rel const& pts) { expr_ref_vector transitions(m), not_inits(m); app_ref tag(m); for (auto r : m_rules) { init_rule(pts, *r); } if (m_pt_rules.empty()) { m_transition = m.mk_false(); m_transition_clause.reset(); } else { unsigned i = 0; expr_ref_vector transitions(m); m_transition_clause.push_back (m_extend_lit->get_arg(0)); for (auto &kv : m_pt_rules) { pt_rule &r = *kv.m_value; std::string name = head()->get_name().str() + "__tr" + std::to_string(i); tag = m.mk_const(symbol(name.c_str()), m.mk_bool_sort()); m_pt_rules.set_tag(tag, r); m_transition_clause.push_back(tag); transitions.push_back(m.mk_implies(r.tag(), r.trans())); if (!r.is_init()) {not_inits.push_back(m.mk_not(tag));} ++i; } if (!ctx.use_inc_clause()) { transitions.push_back(mk_or(m_transition_clause)); m_transition_clause.reset(); } m_transition = mk_and(transitions); } // mk init condition -- disables all non-initial transitions m_init = mk_and(not_inits); // no rule has uninterpreted tail if (not_inits.empty ()) {m_all_init = true;} } #ifdef Z3DEBUG static bool is_all_non_null(app_ref_vector const& apps) { for (auto *a : apps) if (!a) return false; return true; } #endif void pred_transformer::init_rule(decl2rel const& pts, datalog::rule const& rule) { scoped_watch _t_(m_initialize_watch); // Predicates that are variable representatives. Other predicates at // positions the variables occur are made equivalent with these. expr_ref_vector side(m); app_ref_vector var_reprs(m); ptr_vector aux_vars; unsigned ut_size = rule.get_uninterpreted_tail_size(); unsigned t_size = rule.get_tail_size(); SASSERT(ut_size <= t_size); init_atom(pts, rule.get_head(), var_reprs, side, UINT_MAX); for (unsigned i = 0; i < ut_size; ++i) { if (rule.is_neg_tail(i)) { throw default_exception("SPACER does not support " "negated predicates in rule tails"); } init_atom(pts, rule.get_tail(i), var_reprs, side, i); } // -- substitute free variables expr_ref trans(m); { expr_ref_vector tail(m); for (unsigned i = ut_size; i < t_size; ++i) tail.push_back(rule.get_tail(i)); trans= mk_and (tail); ground_free_vars(trans, var_reprs, aux_vars, ut_size == 0); SASSERT(is_all_non_null(var_reprs)); expr_ref tmp = var_subst(m, false)(trans, var_reprs.size (), (expr*const*)var_reprs.c_ptr()); flatten_and (tmp, side); trans = mk_and(side); side.reset (); } // rewrite and simplify th_rewriter rw(m); rw(trans); if (ctx.blast_term_ite_inflation() > 0) { blast_term_ite(trans, ctx.blast_term_ite_inflation()); rw(trans); } TRACE("spacer_init_rule", tout << mk_pp(trans, m) << "\n";); // allow quantifiers in init rule SASSERT(ut_size == 0 || is_ground(trans)); if (!m.is_false(trans)) { pt_rule &ptr = m_pt_rules.mk_rule(m, rule); ptr.set_trans(trans); ptr.set_auxs(aux_vars); ptr.set_reps(var_reprs); } // TRACE("spacer", // tout << rule.get_decl()->get_name() << "\n"; // tout << var_reprs << "\n";); } // create constants for free variables in tail. void pred_transformer::ground_free_vars(expr* e, app_ref_vector& vars, ptr_vector& aux_vars, bool is_init) { expr_free_vars fv; fv(e); while (vars.size() < fv.size()) {vars.push_back(nullptr);} for (unsigned i = 0; i < fv.size(); ++i) { if (fv[i] && !vars[i].get()) { // AG: is it useful to make names unique across rules? app_ref v(m); v = m.mk_fresh_const("aux", fv[i]); v = m.mk_const (pm.get_n_pred(v->get_decl ())); vars[i] = v; aux_vars.push_back(v); } } } // create names for variables used in relations. void pred_transformer::init_atom(decl2rel const &pts, app *atom, app_ref_vector &var_reprs, expr_ref_vector &side, unsigned tail_idx) { unsigned arity = atom->get_num_args(); func_decl* head = atom->get_decl(); pred_transformer& pt = *pts.find(head); for (unsigned i = 0; i < arity; i++) { app_ref rep(m); if (tail_idx == UINT_MAX) { rep = m.mk_const(pm.o2n(pt.sig(i), 0)); } else { rep = m.mk_const(pm.o2o(pt.sig(i), 0, tail_idx)); } expr * arg = atom->get_arg(i); if (is_var(arg)) { var * v = to_var(arg); unsigned var_idx = v->get_idx(); if (var_idx >= var_reprs.size()) { var_reprs.resize(var_idx+1); } expr * repr = var_reprs[var_idx].get(); if (repr) { side.push_back(m.mk_eq(rep, repr)); } else { var_reprs[var_idx] = rep; } } else { SASSERT(is_app(arg)); side.push_back(m.mk_eq(rep, arg)); } } } void pred_transformer::add_premises(decl2rel const& pts, unsigned lvl, expr_ref_vector& r) { if (lvl == 0) {r.push_back(m_init);} else { r.push_back(m_transition); if (!m_transition_clause.empty()) { expr_ref c(m); c = mk_or(m_transition_clause); r.push_back(c); } } for (unsigned i = 0; i < rules().size(); ++i) { add_premises(pts, lvl, *rules()[i], r); } } void pred_transformer::add_premises(decl2rel const& pts, unsigned lvl, datalog::rule& rule, expr_ref_vector& r) { find_predecessors(rule, m_predicates); for (unsigned i = 0; i < m_predicates.size(); ++i) { expr_ref tmp(m); func_decl* head = m_predicates[i]; pred_transformer& pt = *pts.find(head); expr_ref inv = pt.get_formulas(lvl); if (!m.is_true(inv)) { pm.formula_n2o(inv, tmp, i, true); r.push_back(tmp); } } } void pred_transformer::inherit_lemmas(pred_transformer& other) {m_frames.inherit_frames (other.m_frames);} app* pred_transformer::extend_initial (expr *e) { // create fresh extend literal app_ref v(m); std::stringstream name; name << m_head->get_name() << "_ext"; v = m.mk_fresh_const (name.str ().c_str (), m.mk_bool_sort ()); v = m.mk_const (pm.get_n_pred (v->get_decl ())); expr_ref ic(m); // -- extend the initial condition ic = m.mk_or (m_extend_lit, e, v); m_solver->assert_expr (ic); // -- remember the new extend literal m_extend_lit = m.mk_not (v); return m_extend_lit; } /// \brief Update a given solver with all constraints representing /// this pred_transformer void pred_transformer::updt_solver(prop_solver *solver) { solver->assert_expr(m_transition); solver->assert_expr(m_init, 0); // -- facts derivable at the head expr_ref last_tag(m); last_tag = m_extend_lit0; for (auto *rf : m_reach_facts) { if (rf->is_init()) continue; // already in m_init solver->assert_expr(m.mk_or(last_tag, rf->get(), rf->tag())); last_tag = m.mk_not(rf->tag()); } SASSERT(last_tag == m_extend_lit); // -- lemmas app_ref_vector _unused(m); expr_ref_vector fmls(m); // -- assert lemmas for (auto *u : m_frames.lemmas()) { // instances u->mk_insts(fmls); // extra ground instance if (!u->is_ground()) { expr_ref gnd(m); ground_expr(u->get_expr(), gnd, _unused); fmls.push_back(gnd); } // (quantified) lemma if (u->is_ground() || get_context().use_qlemmas()) fmls.push_back(u->get_expr()); // send to solver if (is_infty_level(u->level())) solver->assert_exprs(fmls); else { for (unsigned i = 0; i <= u->level(); ++i) solver->assert_exprs(fmls, i); } fmls.reset(); } // -- lemmas and rfs from other predicates for (auto &kv : m_pt_rules) { const datalog::rule &r = kv.m_value->rule(); find_predecessors(r, m_predicates); if (m_predicates.empty()) continue; for (unsigned i = 0, sz = m_predicates.size(); i < sz; ++i) { const pred_transformer &pt = ctx.get_pred_transformer(m_predicates[i]); // assert lemmas of pt updt_solver_with_lemmas(solver, pt, to_app(kv.m_value->tag()), i); // assert rfs of pt update_solver_with_rfs(solver, pt, to_app(kv.m_value->tag()), i); } } } void pred_transformer::updt_solver_with_lemmas(prop_solver *solver, const pred_transformer &pt, app* rule_tag, unsigned pos) { app_ref_vector _unused(m); expr_ref_vector fmls(m); for (auto *u : pt.m_frames.lemmas()) { expr_ref e(m), gnd(m); e = u->get_expr(); pm.formula_n2o(e, e, pos); u->mk_insts(fmls, e); if (!u->is_ground()) { // special ground instance ground_expr(u->get_expr(), gnd, _unused); pm.formula_n2o(gnd, gnd, pos); fmls.push_back(gnd); } // quantified formula if (u->is_ground() || get_context().use_qlemmas()) fmls.push_back(e); // add tag for (unsigned i = 0, sz = fmls.size(); i < sz; ++i) fmls.set(i, m.mk_implies(rule_tag, fmls.get(i))); // send to solver if (is_infty_level(u->level())) solver->assert_exprs(fmls); else { for (unsigned i = 1, end = next_level(u->level()); i <= end; ++i) solver->assert_exprs(fmls, i); } fmls.reset(); } } void pred_transformer::update_solver_with_rfs(prop_solver *solver, const pred_transformer &pt, app *rule_tag, unsigned pos) { expr_ref not_rule_tag(m); not_rule_tag = m.mk_not(rule_tag); expr_ref last_tag(m); for (auto *rf : pt.m_reach_facts) { expr_ref e(m); if (!last_tag) { e = m.mk_or(m.mk_not(rule_tag), rf->get(), rf->tag()); } else { expr *args[4] = { not_rule_tag, last_tag, rf->get(), rf->tag() }; e = m.mk_or(4, args); } last_tag = m.mk_not(rf->tag()); pm.formula_n2o(e.get(), e, pos); solver->assert_expr(e); } } /// pred_transformer::frames bool pred_transformer::frames::add_lemma(lemma *new_lemma) { TRACE("spacer", tout << "add-lemma: " << pp_level(new_lemma->level()) << " " << m_pt.head()->get_name() << " " << mk_pp(new_lemma->get_expr(), m_pt.get_ast_manager()) << "\n";); if (new_lemma->is_background()) { SASSERT (is_infty_level(new_lemma->level())); for (auto &l : m_bg_invs) { if (l->get_expr() == new_lemma->get_expr()) return false; } TRACE("spacer", tout << "add-external-lemma: " << pp_level(new_lemma->level()) << " " << m_pt.head()->get_name() << " " << mk_pp(new_lemma->get_expr(), m_pt.get_ast_manager()) << "\n";); m_bg_invs.push_back(new_lemma); return true; } unsigned i = 0; for (auto *old_lemma : m_lemmas) { if (old_lemma->get_expr() == new_lemma->get_expr()) { m_pt.get_context().new_lemma_eh(m_pt, new_lemma); // register existing lemma with the pob if (new_lemma->has_pob()) { pob_ref &pob = new_lemma->get_pob(); if (!pob->lemmas().contains(old_lemma)) pob->add_lemma(old_lemma); } // extend bindings if needed if (!new_lemma->get_bindings().empty()) { old_lemma->add_binding(new_lemma->get_bindings()); } // if the lemma is at a higher level, skip it, if (old_lemma->level() >= new_lemma->level()) { TRACE("spacer", tout << "Already at a higher level: " << pp_level(old_lemma->level()) << "\n";); // but, since the instances might be new, assert the // instances that have been copied into m_lemmas[i] if (!new_lemma->get_bindings().empty()) { m_pt.add_lemma_core(old_lemma, true); } if (is_infty_level(old_lemma->level())) { old_lemma->bump(); if (old_lemma->get_bumped() >= 100) { IF_VERBOSE(1, verbose_stream() << "Adding lemma to oo " << old_lemma->get_bumped() << " " << mk_pp(old_lemma->get_expr(), m_pt.get_ast_manager()) << "\n";); throw default_exception("Stuck on a lemma"); } } // no new lemma added return false; } // update level of the existing lemma old_lemma->set_level(new_lemma->level()); // assert lemma in the solver m_pt.add_lemma_core(old_lemma, false); // move the lemma to its new place to maintain sortedness unsigned sz = m_lemmas.size(); for (unsigned j = i; (j + 1) < sz && m_lt(m_lemmas[j + 1], m_lemmas[j]); ++j) { m_lemmas.swap (j, j+1); } return true; } i++; } // new_lemma is really new m_lemmas.push_back(new_lemma); // XXX because m_lemmas is reduced, keep secondary vector of all lemmas // XXX so that pob can refer to its lemmas without creating reference cycles m_pinned_lemmas.push_back(new_lemma); m_sorted = false; m_pt.add_lemma_core(new_lemma); if (new_lemma->has_pob()) {new_lemma->get_pob()->add_lemma(new_lemma);} if (!new_lemma->external()) { m_pt.get_context().new_lemma_eh(m_pt, new_lemma); } return true; } void pred_transformer::frames::propagate_to_infinity (unsigned level) { for (unsigned i = 0, sz = m_lemmas.size (); i < sz; ++i) if (m_lemmas[i]->level() >= level && !is_infty_level(m_lemmas [i]->level())) { m_lemmas [i]->set_level (infty_level ()); m_pt.add_lemma_core (m_lemmas [i]); m_sorted = false; } } void pred_transformer::frames::sort () { if (m_sorted) { return; } m_sorted = true; std::sort(m_lemmas.c_ptr(), m_lemmas.c_ptr() + m_lemmas.size (), m_lt); } bool pred_transformer::frames::propagate_to_next_level (unsigned level) { sort (); bool all = true; if (m_lemmas.empty()) { return all; } unsigned tgt_level = next_level (level); m_pt.ensure_level (tgt_level); for (unsigned i = 0, sz = m_lemmas.size(); i < sz && m_lemmas [i]->level() <= level;) { if (m_lemmas [i]->level () < level) {++i; continue;} unsigned solver_level; if (m_pt.is_invariant(tgt_level, m_lemmas.get(i), solver_level)) { m_lemmas [i]->set_level (solver_level); m_pt.add_lemma_core (m_lemmas.get(i)); // percolate the lemma up to its new place for (unsigned j = i; (j+1) < sz && m_lt (m_lemmas[j+1], m_lemmas[j]); ++j) { m_lemmas.swap(j, j + 1); } ++m_pt.m_stats.m_num_propagations; } else { all = false; ++i; } } return all; } void pred_transformer::frames::simplify_formulas () { // number of subsumed lemmas unsigned num_sumbsumed = 0; // ensure that the lemmas are sorted sort(); ast_manager &m = m_pt.get_ast_manager(); tactic_ref simplifier = mk_unit_subsumption_tactic(m); lemma_ref_vector new_lemmas; unsigned lemmas_size = m_lemmas.size(); goal_ref g(alloc (goal, m, false, false, false)); unsigned j = 0; // for every frame + infinity frame for (unsigned i = 0; i <= m_size; ++i) { g->reset_all (); // normalize level unsigned level = i < m_size ? i : infty_level (); goal_ref_buffer result; // simplify lemmas of the current level // XXX lemmas of higher levels can be assumed in background // XXX decide what to do with non-ground lemmas! unsigned begin = j; for (; j < lemmas_size && m_lemmas[j]->level() <= level; ++j) { if (m_lemmas[j]->level() == level) { g->assert_expr(m_lemmas[j]->get_expr()); } } unsigned end = j; unsigned sz = end - begin; // no lemmas at current level, move to next level if (sz <= 0) {continue;} // exactly one lemma at current level, nothing to // simplify. move to next level if (sz == 1) { new_lemmas.push_back(m_lemmas[begin]); continue; } // more than one lemma at current level. simplify. (*simplifier)(g, result); SASSERT(result.size () == 1); goal *r = result[0]; // no simplification happened, copy all the lemmas if (r->size () == sz) { for (unsigned n = begin; n < end; ++n) { new_lemmas.push_back (m_lemmas[n]); } } // something got simplified, find out which lemmas remain else { num_sumbsumed += (sz - r->size()); // For every expression in the result, copy corresponding // lemma into new_lemmas // XXX linear search. optimize if needed. for (unsigned k = 0; k < r->size(); ++k) { bool found = false; for (unsigned n = begin; n < end; ++n) { if (m_lemmas[n]->get_expr() == r->form(k)) { new_lemmas.push_back(m_lemmas[n]); found = true; break; } } if (!found) { verbose_stream() << "Failed to find a lemma for: " << mk_pp(r->form(k), m) << "\n"; verbose_stream() << "Available lemmas are: "; for (unsigned n = begin; n < end; ++n) { verbose_stream() << n << ": " << mk_pp(m_lemmas[n]->get_expr(), m) << "\n"; } verbose_stream() << "Simplified goal is:\n"; for (unsigned k = 0; k < r->size(); ++k) verbose_stream() << k << ": " << mk_pp(r->form(k), m) << "\n"; } ENSURE(found); SASSERT(found); } } } SASSERT(new_lemmas.size() + num_sumbsumed == m_lemmas.size()); ENSURE(new_lemmas.size() + num_sumbsumed == m_lemmas.size()); if (new_lemmas.size() < m_lemmas.size()) { m_lemmas.reset(); m_lemmas.append(new_lemmas); m_sorted = false; sort(); } } /// pred_transformer::pobs pob* pred_transformer::pob_manager::mk_pob(pob *parent, unsigned level, unsigned depth, expr *post, app_ref_vector const &b) { // create a new pob and set its post to normalize it pob p(parent, m_pt, level, depth, false); p.set_post(post, b); if (m_pobs.contains(p.post())) { for (auto *f : m_pobs[p.post()]) { if (f->parent() == parent && !f->is_in_queue()) { f->inherit(p); return f; } } } pob* n = alloc(pob, parent, m_pt, level, depth); n->set_post(post, b); m_pinned.push_back(n); if (m_pobs.contains(n->post())) { m_pobs[n->post()].push_back(n); } else { pob_buffer buf; buf.push_back(n); m_pobs.insert(n->post(), buf); } return n; } pob* pred_transformer::pob_manager::find_pob(pob* parent, expr *post) { pob p(parent, m_pt, 0, 0, false); p.set_post(post); pob *res = nullptr; if (m_pobs.contains(p.post())) { for (auto *f : m_pobs[p.post()]) { if (f->parent() == parent) { // try to find pob not already in queue if (!f->is_in_queue()) return f; res = f; } } } return res; } // ---------------- // context context::context(fp_params const& params, ast_manager& m) : m_params(params), m(m), m_context(nullptr), m_pm(m), m_query_pred(m), m_query(nullptr), m_pob_queue(), m_last_result(l_undef), m_inductive_lvl(0), m_expanded_lvl(0), m_json_marshaller(this) { ref pool0_base = mk_smt_solver(m, params_ref::get_empty(), symbol::null); ref pool1_base = mk_smt_solver(m, params_ref::get_empty(), symbol::null); ref pool2_base = mk_smt_solver(m, params_ref::get_empty(), symbol::null); unsigned max_num_contexts = params.spacer_max_num_contexts(); m_pool0 = alloc(solver_pool, pool0_base.get(), max_num_contexts); m_pool1 = alloc(solver_pool, pool1_base.get(), max_num_contexts); m_pool2 = alloc(solver_pool, pool2_base.get(), max_num_contexts); updt_params(); } context::~context() { reset_lemma_generalizers(); reset(); } void context::updt_params() { m_random.set_seed(m_params.spacer_random_seed()); m_children_order = static_cast(m_params.spacer_order_children()); m_simplify_pob = m_params.spacer_simplify_pob(); m_use_euf_gen = m_params.spacer_use_euf_gen(); m_use_lim_num_gen = m_params.spacer_use_lim_num_gen(); m_use_ctp = m_params.spacer_ctp(); m_use_inc_clause = m_params.spacer_use_inc_clause(); m_blast_term_ite_inflation = m_params.spacer_blast_term_ite_inflation(); m_use_ind_gen = m_params.spacer_use_inductive_generalizer(); m_use_array_eq_gen = m_params.spacer_use_array_eq_generalizer(); m_validate_lemmas = m_params.spacer_validate_lemmas(); m_max_level = m_params.spacer_max_level (); m_use_propagate = m_params.spacer_propagate (); m_reset_obligation_queue = m_params.spacer_reset_pob_queue(); m_push_pob = m_params.spacer_push_pob(); m_push_pob_max_depth = m_params.spacer_push_pob_max_depth(); m_use_lemma_as_pob = m_params.spacer_use_lemma_as_cti(); m_elim_aux = m_params.spacer_elim_aux(); m_reach_dnf = m_params.spacer_reach_dnf(); m_use_derivations = m_params.spacer_use_derivations(); m_validate_result = m_params.validate(); m_use_eq_prop = m_params.spacer_eq_prop(); m_ground_pob = m_params.spacer_ground_pobs(); m_q3_qgen = m_params.spacer_q3_use_qgen(); m_use_gpdr = m_params.spacer_gpdr(); m_simplify_formulas_pre = m_params.spacer_simplify_lemmas_pre(); m_simplify_formulas_post = m_params.spacer_simplify_lemmas_post(); m_use_native_mbp = m_params.spacer_native_mbp (); m_instantiate = m_params.spacer_q3_instantiate (); m_use_qlemmas = m_params.spacer_q3(); m_weak_abs = m_params.spacer_weak_abs(); m_use_restarts = m_params.spacer_restarts(); m_restart_initial_threshold = m_params.spacer_restart_initial_threshold(); m_pdr_bfs = m_params.spacer_gpdr_bfs(); m_use_bg_invs = m_params.spacer_use_bg_invs(); if (m_use_gpdr) { // set options to be compatible with GPDR m_weak_abs = false; m_push_pob = false; m_use_qlemmas = false; m_ground_pob = true; m_reset_obligation_queue = false; m_use_derivations = false; m_use_lemma_as_pob = false; } } void context::reset() { TRACE("spacer", tout << "\n";); m_pob_queue.reset(); for (auto &entry: m_rels) {dealloc(entry.m_value);} m_rels.reset(); m_query = nullptr; m_last_result = l_undef; m_inductive_lvl = 0; } void context::init_rules(datalog::rule_set& rules, decl2rel& rels) { scoped_watch _t_(m_init_rules_watch); m_context = &rules.get_context(); // Allocate collection of predicate transformers for (auto dit = rules.begin_grouped_rules(), dend = rules.end_grouped_rules(); dit != dend; ++dit) { func_decl* pred = dit->m_key; TRACE("spacer", tout << mk_pp(pred, m) << "\n";); SASSERT(!rels.contains(pred)); auto *e = rels.insert_if_not_there2(pred, alloc(pred_transformer, *this, get_manager(), pred)); datalog::rule_vector const& pred_rules = *dit->m_value; for (auto rule : pred_rules) {e->get_data().m_value->add_rule(rule);} } // Allocate predicate transformers for predicates that are used // but don't have rules for (auto *r : rules) { pred_transformer* pt; unsigned utz = r->get_uninterpreted_tail_size(); for (unsigned i = 0; i < utz; ++i) { func_decl* pred = r->get_decl(i); if (!rels.find(pred, pt)) { pt = alloc(pred_transformer, *this, get_manager(), pred); rels.insert(pred, pt); } } } // Initialize use list dependencies for (auto &entry : rels) { func_decl* pred = entry.m_key; pred_transformer* pt = entry.m_value, *pt_user = nullptr; for (auto dep : rules.get_dependencies().get_deps(pred)) { TRACE("spacer", tout << mk_pp(pred, m) << " " << mk_pp(dep, m) << "\n";); rels.find(dep, pt_user); pt_user->add_use(pt); } } // Initialize the predicate transformers. for (auto &entry : rels) { pred_transformer* rel = entry.m_value; rel->initialize(rels); TRACE("spacer", rel->display(tout); ); } // initialize reach facts for (auto &entry : rels) {entry.m_value->init_rfs();} } void context::inherit_lemmas(const decl2rel &rels) { for (auto &entry : rels) { pred_transformer *pt = nullptr; if (m_rels.find(entry.m_key, pt)) { entry.m_value->inherit_lemmas(*pt); } } } void context::update_rules(datalog::rule_set& rules) { decl2rel rels; // SMT params must be set before any expression is asserted to any // solver init_global_smt_params(); // constructs new pred transformers and asserts trans and init init_rules(rules, rels); // inherits lemmas from m_rels into rels inherit_lemmas(rels); // switch context to new rels init(rels); // re-initialize lemma generalizers init_lemma_generalizers(); } void context::init(const decl2rel &rels) { // reset context. Current state is all stored in rels reset(); // re-initialize context for (auto &entry : rels) {m_rels.insert(entry.m_key, entry.m_value);} } unsigned context::get_num_levels(func_decl* p) { pred_transformer* pt = nullptr; if (m_rels.find(p, pt)) { return pt->get_num_levels(); } else { IF_VERBOSE(10, verbose_stream() << "did not find predicate " << p->get_name() << "\n";); return 0; } } expr_ref context::get_cover_delta(int level, func_decl* p_orig, func_decl* p) { pred_transformer* pt = nullptr; if (m_rels.find(p, pt)) { return pt->get_cover_delta(p_orig, level); } else { IF_VERBOSE(10, verbose_stream() << "did not find predicate " << p->get_name() << "\n";); return expr_ref(m.mk_true(), m); } } void context::add_cover(int level, func_decl* p, expr* property, bool bg) { scoped_proof _pf_(m); pred_transformer* pt = nullptr; if (!m_rels.find(p, pt)) { pt = alloc(pred_transformer, *this, get_manager(), p); m_rels.insert(p, pt); IF_VERBOSE(10, verbose_stream() << "did not find predicate " << p->get_name() << "\n";); } unsigned lvl = (level == -1)?infty_level():((unsigned)level); pt->add_cover(lvl, property, bg); } void context::add_invariant (func_decl *p, expr *property) {add_cover (infty_level(), p, property, use_bg_invs());} expr_ref context::get_reachable(func_decl *p) { pred_transformer* pt = nullptr; if (!m_rels.find(p, pt)) { return expr_ref(m.mk_false(), m); } return pt->get_reachable(); } bool context::validate() { if (!m_validate_result) { return true; } std::stringstream msg; switch(m_last_result) { case l_true: { #if 0 expr_ref cex(m); cex = get_ground_sat_answer(); if (!cex.get()) { IF_VERBOSE(0, verbose_stream() << "Cex validation failed\n";); throw default_exception("Cex validation failed\n"); return false; } #endif proof_ref cex(m); cex = get_ground_refutation(); if (!cex.get()) { IF_VERBOSE(0, verbose_stream() << "Cex validation failed\n";); throw default_exception("Cex validation failed\n"); return false; } break; } case l_false: { expr_ref_vector refs(m); expr_ref tmp(m); model_ref model; vector rs; model_converter_ref mc; get_level_property(m_inductive_lvl, refs, rs, use_bg_invs()); inductive_property ex(m, mc, rs); ex.to_model(model); var_subst vs(m, false); for (auto& kv : m_rels) { ptr_vector const& rules = kv.m_value->rules(); TRACE ("spacer", tout << "PT: " << kv.m_value->head ()->get_name ().str () << "\n";); for (auto* rp : rules) { datalog::rule& r = *rp; TRACE ("spacer", get_datalog_context (). get_rule_manager (). display_smt2(r, tout) << "\n";); tmp = (*model)(r.get_head()); expr_ref_vector fmls(m); fmls.push_back(m.mk_not(tmp)); unsigned utsz = r.get_uninterpreted_tail_size(); unsigned tsz = r.get_tail_size(); for (unsigned j = 0; j < utsz; ++j) { tmp = (*model)(r.get_tail(j)); fmls.push_back(tmp); } for (unsigned j = utsz; j < tsz; ++j) { fmls.push_back(r.get_tail(j)); } tmp = m.mk_and(fmls.size(), fmls.c_ptr()); svector names; expr_free_vars fv; fv (tmp); fv.set_default_sort (m.mk_bool_sort ()); for (unsigned i = 0; i < fv.size(); ++i) { names.push_back(symbol(fv.size () - i - 1)); } if (!fv.empty()) { fv.reverse (); tmp = m.mk_exists(fv.size(), fv.c_ptr(), names.c_ptr(), tmp); } ref sol = mk_smt_solver(m, params_ref::get_empty(), symbol::null); sol->assert_expr(tmp); lbool res = sol->check_sat(0, nullptr); if (res != l_false) { msg << "rule validation failed when checking: " << mk_pp(tmp, m); IF_VERBOSE(0, verbose_stream() << msg.str() << "\n";); throw default_exception(msg.str()); return false; } } } TRACE ("spacer", tout << "Validation Succeeded\n";); break; } default: break; } return true; } void context::reset_lemma_generalizers() { std::for_each(m_lemma_generalizers.begin(), m_lemma_generalizers.end(), delete_proc()); m_lemma_generalizers.reset(); } // initialize global SMT parameters shared by all solvers void context::init_global_smt_params() { m.toggle_proof_mode(PGM_ENABLED); params_ref p; if (!m_use_eq_prop) { p.set_uint("arith.propagation_mode", BP_NONE); p.set_bool("arith.auto_config_simplex", true); p.set_bool("arith.propagate_eqs", false); p.set_bool("arith.eager_eq_axioms", false); } p.set_uint("random_seed", m_params.spacer_random_seed()); p.set_bool("dump_benchmarks", m_params.spacer_dump_benchmarks()); p.set_double("dump_threshold", m_params.spacer_dump_threshold()); // mbqi p.set_bool("mbqi", m_params.spacer_mbqi()); if (!m_ground_pob) { p.set_uint("phase_selection", PS_CACHING_CONSERVATIVE2); p.set_uint("restart_strategy", RS_GEOMETRIC); p.set_double("restart_factor", 1.5); p.set_uint("qi.quick_checker", MC_UNSAT); p.set_double("qi.eager_threshold", 10.0); p.set_double("qi.lazy_threshold", 20.0); // options that we used to set, but are not user visible and // possibly not very useful // fparams.m_ng_lift_ite = LI_FULL; // fparams.m_eliminate_bounds = true; // fparams.m_pi_use_database = true; } m_pool0->updt_params(p); m_pool1->updt_params(p); m_pool2->updt_params(p); } void context::init_lemma_generalizers() { reset_lemma_generalizers(); if (m_q3_qgen) { m_lemma_generalizers.push_back(alloc(lemma_bool_inductive_generalizer, *this, 0, true)); m_lemma_generalizers.push_back(alloc(lemma_quantifier_generalizer, *this, m_params.spacer_q3_qgen_normalize())); } if (m_use_euf_gen) { m_lemma_generalizers.push_back (alloc(lemma_eq_generalizer, *this)); } // -- AG: commented out because it is causing performance issues at the moment //m_lemma_generalizers.push_back (alloc (unsat_core_generalizer, *this)); if (m_use_ind_gen) { m_lemma_generalizers.push_back(alloc(lemma_bool_inductive_generalizer, *this, 0)); } // after the lemma is minimized (maybe should also do before) if (m_use_lim_num_gen) { m_lemma_generalizers.push_back(alloc(limit_num_generalizer, *this, 5)); } if (m_use_array_eq_gen) { m_lemma_generalizers.push_back(alloc(lemma_array_eq_generalizer, *this)); } if (m_validate_lemmas) { m_lemma_generalizers.push_back(alloc(lemma_sanity_checker, *this)); } } void context::get_level_property(unsigned lvl, expr_ref_vector& res, vector& rs, bool with_bg) const { for (auto const& kv : m_rels) { pred_transformer* r = kv.m_value; if (r->head() == m_query_pred) { continue; } expr_ref conj = r->get_formulas(lvl, with_bg); m_pm.formula_n2o(0, false, conj); res.push_back(conj); ptr_vector sig(r->head()->get_arity(), r->sig()); rs.push_back(relation_info(m, r->head(), sig, conj)); } } void context::simplify_formulas() { for (auto& kv : m_rels) { kv.m_value->simplify_formulas(); } } lbool context::solve(unsigned from_lvl) { m_last_result = l_undef; try { if (m_use_gpdr) { SASSERT(from_lvl == 0); m_last_result = gpdr_solve_core(); } else { m_last_result = solve_core (from_lvl); } if (m_last_result == l_false) { simplify_formulas(); m_last_result = l_false; IF_VERBOSE(1, { expr_ref_vector refs(m); vector rs; get_level_property(m_inductive_lvl, refs, rs, use_bg_invs()); model_converter_ref mc; inductive_property ex(m, mc, rs); verbose_stream() << ex.to_string(); }); // upgrade invariants that are known to be inductive. // decl2rel::iterator it = m_rels.begin (), end = m_rels.end (); // for (; m_inductive_lvl > 0 && it != end; ++it) { // if (it->m_value->head() != m_query_pred) { // it->m_value->propagate_to_infinity (m_inductive_lvl); // } // } } VERIFY (validate ()); } catch (const unknown_exception &) {} if (m_last_result == l_true) { m_stats.m_cex_depth = get_cex_depth (); } if (m_params.print_statistics ()) { statistics st; collect_statistics (st); st.display_smt2 (verbose_stream ()); } return m_last_result; } void context::checkpoint() { if (m.canceled ()) { throw default_exception("spacer canceled"); } } unsigned context::get_cex_depth() { if (m_last_result != l_true) { IF_VERBOSE(1, verbose_stream () << "Trace unavailable when result is false\n";); return 0; } // treat the following as queues: read from left to right and insert at right ptr_vector preds; ptr_vector pts; reach_fact_ref_vector facts; // temporary reach_fact* fact; datalog::rule const* r; pred_transformer* pt; // get and discard query rule fact = m_query->get_last_rf (); r = &fact->get_rule (); unsigned cex_depth = 0; // initialize queues // assume that the query is only on a single predicate // (i.e. disallow fancy queries for now) facts.append (fact->get_justifications ()); if (facts.size() != 1) { // XXX AG: Escape if an assertion is about to fail IF_VERBOSE(1, verbose_stream () << "Warning: counterexample is trivial or non-existent\n";); return cex_depth; } SASSERT (facts.size () == 1); m_query->find_predecessors (*r, preds); SASSERT (preds.size () == 1); pts.push_back (&(get_pred_transformer (preds[0]))); pts.push_back (NULL); // cex depth marker // bfs traversal of the query derivation tree for (unsigned curr = 0; curr < pts.size (); curr++) { // get current pt and fact pt = pts.get (curr); // check for depth marker if (pt == nullptr) { ++cex_depth; // insert new marker if there are pts at higher depth if (curr + 1 < pts.size()) { pts.push_back(NULL); } continue; } fact = facts.get (curr - cex_depth); // discount the number of markers // get rule justifying the derivation of fact at pt r = &fact->get_rule (); TRACE ("spacer", tout << "next rule: " << r->name ().str () << "\n"; ); // add child facts and pts facts.append (fact->get_justifications ()); pt->find_predecessors (*r, preds); for (unsigned j = 0; j < preds.size (); j++) { pts.push_back (&(get_pred_transformer (preds[j]))); } } return cex_depth; } /** \brief retrieve answer. */ void context::get_rules_along_trace(datalog::rule_ref_vector& rules) { if (m_last_result != l_true) { IF_VERBOSE(1, verbose_stream () << "Trace unavailable when result is false\n";); return; } // treat the following as queues: read from left to right and insert at right ptr_vector preds; ptr_vector pts; reach_fact_ref_vector facts; // temporary reach_fact* fact; datalog::rule const* r; pred_transformer* pt; // get query rule fact = m_query->get_last_rf (); r = &fact->get_rule (); rules.push_back (const_cast (r)); TRACE ("spacer", tout << "Initial rule: " << r->name().str() << "\n"; ); // initialize queues // assume that the query is only on a single predicate // (i.e. disallow fancy queries for now) facts.append (fact->get_justifications ()); if (facts.size() != 1) { // XXX AG: Escape if an assertion is about to fail IF_VERBOSE(1, verbose_stream () << "Warning: counterexample is trivial or non-existent\n";); return; } SASSERT (facts.size () == 1); m_query->find_predecessors (*r, preds); SASSERT (preds.size () == 1); pts.push_back (&(get_pred_transformer (preds[0]))); // populate rules according to a preorder traversal of the query derivation tree for (unsigned curr = 0; curr < pts.size (); curr++) { // get current pt and fact pt = pts.get (curr); fact = facts.get (curr); // get rule justifying the derivation of fact at pt r = &fact->get_rule (); rules.push_back (const_cast (r)); TRACE ("spacer", tout << "next rule: " << r->name ().str () << "\n"; ); // add child facts and pts facts.append (fact->get_justifications ()); pt->find_predecessors (*r, preds); for (unsigned j = 0; j < preds.size (); j++) { pts.push_back (&(get_pred_transformer (preds[j]))); } } } model_ref context::get_model() { model_ref model; expr_ref_vector refs(m); vector rs; get_level_property(m_inductive_lvl, refs, rs, use_bg_invs()); inductive_property ex(m, const_cast(m_mc), rs); ex.to_model (model); return model; } expr_ref context::get_answer() { switch(m_last_result) { case l_true: return mk_sat_answer(); case l_false: return mk_unsat_answer(); default: return expr_ref(m.mk_true(), m); } } expr_ref context::mk_unsat_answer() const { expr_ref_vector refs(m); vector rs; get_level_property(m_inductive_lvl, refs, rs, use_bg_invs()); inductive_property ex(m, const_cast(m_mc), rs); return ex.to_expr(); } proof_ref context::get_ground_refutation() const { if (m_last_result != l_true) { IF_VERBOSE(0, verbose_stream() << "Sat answer unavailable when result is false\n";); return proof_ref(m); } ground_sat_answer_op op(*this); return op(*m_query); } expr_ref context::get_ground_sat_answer() const { if (m_last_result != l_true) { IF_VERBOSE(0, verbose_stream() << "Sat answer unavailable when result is false\n";); return expr_ref(m); } // convert a ground refutation into a linear counterexample // only works for linear CHC systems expr_ref_vector cex(m); proof_ref pf = get_ground_refutation(); proof_ref_vector premises(m); expr_ref conclusion(m); svector> positions; vector substs; unsigned count = 0; while (m.is_hyper_resolve(pf, premises, conclusion, positions, substs)) { // skip the first fact since it is query!X introduced by the encoding // and not a user-visible predicate if (count++ > 0) cex.push_back(m.get_fact(pf)); if (premises.size() > 1) { SASSERT(premises.size() == 2 && "Non linear CHC detected"); pf = premises.get(1); } else { pf.reset(); break; } premises.reset(); conclusion.reset(); positions.reset(); substs.reset(); } SASSERT(!pf || !m.is_hyper_resolve(pf)); if (pf) { cex.push_back(m.get_fact(pf)); } TRACE ("spacer", tout << "ground cex\n" << cex << "\n";); return mk_and(cex); } void context::display_certificate(std::ostream &out) const { switch(m_last_result) { case l_false: out << mk_pp(mk_unsat_answer(), m); break; case l_true: out << mk_pp(mk_sat_answer(), m); break; case l_undef: out << "unknown"; break; } } ///this is where everything starts lbool context::solve_core (unsigned from_lvl) { scoped_watch _w_(m_solve_watch); //if there is no query predicate, abort if (!m_rels.find(m_query_pred, m_query)) { return l_false; } unsigned lvl = from_lvl; pob *root = m_query->mk_pob(nullptr,from_lvl,0,m.mk_true()); m_pob_queue.set_root (*root); unsigned max_level = m_max_level; for (unsigned i = from_lvl; i < max_level; ++i) { checkpoint(); m_expanded_lvl = infty_level (); m_stats.m_max_query_lvl = lvl; if (check_reachability()) { return l_true; } if (lvl > 0 && m_use_propagate) if (propagate(m_expanded_lvl, lvl, UINT_MAX)) { dump_json(); return l_false; } dump_json(); if (is_inductive()){ return l_false; } for (unsigned i = 0; i < m_callbacks.size(); i++){ if (m_callbacks[i]->unfold()) m_callbacks[i]->unfold_eh(); } m_pob_queue.inc_level (); lvl = m_pob_queue.max_level (); m_stats.m_max_depth = std::max(m_stats.m_max_depth, lvl); IF_VERBOSE(1,verbose_stream() << "Entering level "<< lvl << "\n";); STRACE("spacer_progress", tout << "\n* LEVEL " << lvl << "\n";); IF_VERBOSE(1, if (m_params.print_statistics()) { statistics st; collect_statistics(st); st.display_smt2(verbose_stream()); }; ); } // communicate failure to datalog::context if (m_context) { m_context->set_status(datalog::BOUNDED); } return l_undef; } // bool context::check_reachability () { scoped_watch _w_(m_reach_watch); timeit _timer (get_verbosity_level () >= 1, "spacer::context::check_reachability", verbose_stream ()); pob_ref last_reachable; pob_ref_buffer new_pobs; if (m_reset_obligation_queue) { m_pob_queue.reset(); } unsigned initial_size = m_stats.m_num_lemmas; unsigned threshold = m_restart_initial_threshold; unsigned luby_idx = 1; while (m_pob_queue.top()) { pob_ref node; checkpoint (); while (last_reachable) { checkpoint (); node = last_reachable; last_reachable = nullptr; if (m_pob_queue.is_root(*node)) { return true; } if (is_reachable (*node->parent())) { last_reachable = node->parent (); SASSERT(last_reachable->is_closed()); last_reachable->close (); } else if (!node->parent()->is_closed()) { /* bump node->parent */ node->parent ()->bump_weakness(); } } SASSERT (m_pob_queue.top ()); // -- remove all closed nodes // -- this is necessary because there is no easy way to // -- remove nodes from the priority queue. while (m_pob_queue.top ()->is_closed ()) { pob_ref n = m_pob_queue.top(); m_pob_queue.pop(); IF_VERBOSE (1, verbose_stream () << "Deleting closed node: " << n->pt ().head ()->get_name () << "(" << n->level () << ", " << n->depth () << ")" << " " << n->post ()->get_id () << "\n";); if (m_pob_queue.is_root(*n)) {return true;} SASSERT (m_pob_queue.top ()); } SASSERT (m_pob_queue.top ()); if (m_use_restarts && m_stats.m_num_lemmas - initial_size > threshold) { luby_idx++; m_stats.m_num_restarts++; threshold = static_cast(get_luby(luby_idx)) * m_restart_initial_threshold; IF_VERBOSE (1, verbose_stream () << "(restarting :lemmas " << m_stats.m_num_lemmas << " :restart_threshold " << threshold << ")\n";); // -- clear obligation queue up to the root while (!m_pob_queue.is_root(*m_pob_queue.top())) { m_pob_queue.pop(); } initial_size = m_stats.m_num_lemmas; } node = m_pob_queue.top (); m_pob_queue.pop(); size_t old_sz = m_pob_queue.size(); (void)old_sz; SASSERT (node->level () <= m_pob_queue.max_level ()); switch (expand_pob(*node, new_pobs)) { case l_true: SASSERT(m_pob_queue.size() == old_sz); SASSERT(new_pobs.empty()); last_reachable = node; last_reachable->close (); if (m_pob_queue.is_root(*node)) {return true;} break; case l_false: SASSERT(m_pob_queue.size() == old_sz); for (auto pob : new_pobs) { if (is_requeue(*pob)) {m_pob_queue.push(*pob);} } if (m_pob_queue.is_root(*node)) {return false;} break; case l_undef: SASSERT(m_pob_queue.size() == old_sz); for (auto pob : new_pobs) {m_pob_queue.push(*pob);} break; } new_pobs.reset(); } UNREACHABLE(); return false; } /// returns true if the given pob can be re-scheduled bool context::is_requeue(pob &n) { if (!m_push_pob) {return false;} unsigned max_depth = m_push_pob_max_depth; return (n.level() >= m_pob_queue.max_level() || m_pob_queue.max_level() - n.level() <= max_depth); } /// check whether node n is concretely reachable bool context::is_reachable(pob &n) { scoped_watch _w_(m_is_reach_watch); // XXX hold a reference for n during this call. // XXX Should convert is_reachable() to accept pob_ref as argument pob_ref nref(&n); TRACE ("spacer", tout << "is-reachable: " << n.pt().head()->get_name() << " level: " << n.level() << " depth: " << (n.depth () - m_pob_queue.min_depth ()) << "\n" << mk_pp(n.post(), m) << "\n";); stopwatch watch; IF_VERBOSE (1, verbose_stream () << "is-reachable: " << n.pt ().head ()->get_name () << " (" << n.level () << ", " << (n.depth () - m_pob_queue.min_depth ()) << ") " << (n.use_farkas_generalizer () ? "FAR " : "SUB ") << n.post ()->get_id (); verbose_stream().flush (); watch.start ();); // used in case n is unreachable unsigned uses_level = infty_level (); model_ref mdl; // used in case n is reachable bool is_concrete; const datalog::rule * r = nullptr; // denotes which predecessor's (along r) reach facts are used vector reach_pred_used; unsigned num_reuse_reach = 0; unsigned saved = n.level (); // TBD: don't expose private field n.m_level = infty_level (); lbool res = n.pt().is_reachable(n, nullptr, &mdl, uses_level, is_concrete, r, reach_pred_used, num_reuse_reach); n.m_level = saved; if (res != l_true || !is_concrete) { IF_VERBOSE(1, verbose_stream () << " F " << std::fixed << std::setprecision(2) << watch.get_seconds () << "\n";); return false; } SASSERT(res == l_true); SASSERT(is_concrete); // -- update must summary if (r && r->get_uninterpreted_tail_size () > 0) { reach_fact_ref rf = n.pt().mk_rf (n, *mdl, *r); n.pt ().add_rf (rf.get ()); } // if n has a derivation, create a new child and report l_undef // otherwise if n has no derivation or no new children, report l_true pob *next = nullptr; scoped_ptr deriv; if (n.has_derivation()) {deriv = n.detach_derivation();} // -- close n, it is reachable // -- don't worry about removing n from the obligation queue n.close (); if (deriv) { next = deriv->create_next_child (); if (next) { SASSERT(!next->is_closed()); // move derivation over to the next obligation next->set_derivation(deriv.detach()); // remove the current node from the queue if it is at the top if (m_pob_queue.top() == &n) { m_pob_queue.pop(); } m_pob_queue.push(*next); } } // either deriv was a nullptr or it was moved into next SASSERT(!next || !deriv); IF_VERBOSE(1, verbose_stream () << (next ? " X " : " T ") << std::fixed << std::setprecision(2) << watch.get_seconds () << "\n";); // recurse on the new proof obligation return next ? is_reachable(*next) : true; } void context::dump_json() { if (m_params.spacer_print_json().size()) { std::ofstream of; of.open(m_params.spacer_print_json().bare_str()); m_json_marshaller.marshal(of); of.close(); } } void context::predecessor_eh() { for (unsigned i = 0; i < m_callbacks.size(); i++) { if (m_callbacks[i]->predecessor()) m_callbacks[i]->predecessor_eh(); } } /// Checks whether the given pob is reachable /// returns l_true if reachable, l_false if unreachable /// returns l_undef if reachability cannot be decided /// out contains new pobs to add to the queue in case the result is l_undef lbool context::expand_pob(pob& n, pob_ref_buffer &out) { SASSERT(out.empty()); pob::on_expand_event _evt(n); TRACE ("spacer", tout << "expand-pob: " << n.pt().head()->get_name() << " level: " << n.level() << " depth: " << (n.depth () - m_pob_queue.min_depth ()) << " fvsz: " << n.get_free_vars_size() << "\n" << mk_pp(n.post(), m) << "\n";); STRACE ("spacer_progress", tout << "** expand-pob: " << n.pt().head()->get_name() << " level: " << n.level() << " depth: " << (n.depth () - m_pob_queue.min_depth ()) << "\n" << mk_epp(n.post(), m) << "\n\n";); TRACE ("core_array_eq", tout << "expand-pob: " << n.pt().head()->get_name() << " level: " << n.level() << " depth: " << (n.depth () - m_pob_queue.min_depth ()) << "\n" << mk_pp(n.post(), m) << "\n";); stopwatch watch; IF_VERBOSE (1, verbose_stream () << "expand: " << n.pt ().head ()->get_name () << " (" << n.level () << ", " << (n.depth () - m_pob_queue.min_depth ()) << ") " << (n.use_farkas_generalizer () ? "FAR " : "SUB ") << " w(" << n.weakness() << ") " << n.post ()->get_id (); verbose_stream().flush (); watch.start ();); // used in case n is unreachable unsigned uses_level = infty_level (); expr_ref_vector cube(m); model_ref model; // used in case n is reachable bool is_concrete; const datalog::rule * r = nullptr; // denotes which predecessor's (along r) reach facts are used vector reach_pred_used; unsigned num_reuse_reach = 0; if (m_push_pob && n.pt().is_blocked(n, uses_level)) { // if (!m_pob_queue.is_root (n)) n.close (); IF_VERBOSE (1, verbose_stream () << " K " << std::fixed << std::setprecision(2) << watch.get_seconds () << "\n";); n.inc_level(); out.push_back(&n); return l_false; } if (/* XXX noop */ n.pt().is_qblocked(n)) { STRACE("spacer_progress", tout << "This pob can be blocked by instantiation\n";); } predecessor_eh(); lbool res = n.pt ().is_reachable (n, &cube, &model, uses_level, is_concrete, r, reach_pred_used, num_reuse_reach); if (model) model->set_model_completion(false); checkpoint (); IF_VERBOSE (1, verbose_stream () << "." << std::flush;); switch (res) { //reachable but don't know if this is purely using UA case l_true: { // update stats m_stats.m_num_reuse_reach += num_reuse_reach; // must-reachable if (is_concrete) { // -- update must summary if (r && r->get_uninterpreted_tail_size() > 0) { reach_fact_ref rf = n.pt().mk_rf (n, *model, *r); checkpoint (); n.pt ().add_rf (rf.get ()); checkpoint (); } // if n has a derivation, create a new child and report l_undef // otherwise if n has no derivation or no new children, report l_true pob *next = nullptr; scoped_ptr deriv; if (n.has_derivation()) {deriv = n.detach_derivation();} // -- close n, it is reachable // -- don't worry about removing n from the obligation queue n.close (); if (deriv) { next = deriv->create_next_child (); checkpoint (); if (next) { // move derivation over to the next obligation next->set_derivation (deriv.detach()); // this is the new node to add out.push_back (next); } } IF_VERBOSE(1, verbose_stream () << (next ? " X " : " T ") << std::fixed << std::setprecision(2) << watch.get_seconds () << "\n";); return next ? l_undef : l_true; } // create a child of n out.push_back(&n); VERIFY(create_children (n, *r, *model, reach_pred_used, out)); IF_VERBOSE(1, verbose_stream () << " U " << std::fixed << std::setprecision(2) << watch.get_seconds () << "\n";); return l_undef; } case l_false: { // n is unreachable, create new summary facts timeit _timer (is_trace_enabled("spacer_timeit"), "spacer::expand_pob::false", verbose_stream ()); // -- only update expanded level when new lemmas are generated at it. if (n.level() < m_expanded_lvl) { m_expanded_lvl = n.level(); } TRACE("spacer", tout << "cube:\n"; for (unsigned j = 0; j < cube.size(); ++j) tout << mk_pp(cube[j].get(), m) << "\n";); pob_ref nref(&n); // -- create lemma from a pob and last unsat core lemma_ref lemma = alloc(class lemma, pob_ref(&n), cube, uses_level); // -- run all lemma generalizers for (unsigned i = 0; // -- only generalize if lemma was constructed using farkas n.use_farkas_generalizer () && !lemma->is_false() && i < m_lemma_generalizers.size(); ++i) { checkpoint (); (*m_lemma_generalizers[i])(lemma); } DEBUG_CODE( lemma_sanity_checker sanity_checker(*this); sanity_checker(lemma); ); TRACE("spacer", tout << "invariant state: " << (is_infty_level(lemma->level())?"(inductive)":"") << mk_pp(lemma->get_expr(), m) << "\n";); bool v = n.pt().add_lemma (lemma.get()); if (v) { m_stats.m_num_lemmas++; } // Optionally update the node to be the negation of the lemma if (v && m_use_lemma_as_pob) { expr_ref c(m); c = mk_and(lemma->get_cube()); // check that the post condition is different if (c != n.post()) { pob *f = n.pt().find_pob(n.parent(), c); // skip if a similar pob is already in the queue if (f != &n && (!f || !f->is_in_queue())) { f = n.pt().mk_pob(n.parent(), n.level(), n.depth(), c, n.get_binding()); SASSERT(!f->is_in_queue()); f->inc_level(); //f->set_farkas_generalizer(false); out.push_back(f); } } } // schedule the node to be placed back in the queue n.inc_level(); out.push_back(&n); CASSERT("spacer", n.level() == 0 || check_invariant(n.level()-1)); IF_VERBOSE(1, verbose_stream () << " F " << std::fixed << std::setprecision(2) << watch.get_seconds () << "\n";); return l_false; } case l_undef: // something went wrong if (n.weakness() < 10 /* MAX_WEAKENSS */) { bool has_new_child = false; SASSERT(m_weak_abs); m_stats.m_expand_pob_undef++; if (r && r->get_uninterpreted_tail_size() > 0) { // do not trust reach_pred_used for (unsigned i = 0, sz = reach_pred_used.size(); i < sz; ++i) { reach_pred_used[i] = false; } has_new_child = create_children(n, *r, *model, reach_pred_used, out); } IF_VERBOSE(1, verbose_stream() << " UNDEF " << std::fixed << std::setprecision(2) << watch.get_seconds () << "\n";); if (has_new_child) { // ensure that n is placed back in the queue out.push_back(&n); return l_undef; } // -- failed to create a child, bump weakness and repeat // -- the recursion is bounded by the levels of weakness supported SASSERT(out.empty()); n.bump_weakness(); return expand_pob(n, out); } TRACE("spacer", tout << "unknown state: " << mk_and(cube) << "\n";); throw unknown_exception(); } UNREACHABLE(); throw unknown_exception(); } // // check if predicate transformer has a satisfiable predecessor state. // returns either a satisfiable predecessor state or // return a property that blocks state and is implied by the // predicate transformer (or some unfolding of it). // bool context::propagate(unsigned min_prop_lvl, unsigned max_prop_lvl, unsigned full_prop_lvl) { scoped_watch _w_(m_propagate_watch); if (min_prop_lvl == infty_level()) { return false; } timeit _timer (get_verbosity_level() >= 1, "spacer::context::propagate", verbose_stream ()); if (full_prop_lvl < max_prop_lvl) { full_prop_lvl = max_prop_lvl; } if (m_simplify_formulas_pre) { simplify_formulas(); } STRACE ("spacer_progress", tout << "Propagating\n";); IF_VERBOSE (1, verbose_stream () << "Propagating: " << std::flush;); for (unsigned lvl = min_prop_lvl; lvl <= full_prop_lvl; lvl++) { IF_VERBOSE (1, if (lvl > max_prop_lvl && lvl == max_prop_lvl + 1) verbose_stream () << " ! "; verbose_stream () << lvl << " " << std::flush;); checkpoint(); CTRACE ("spacer", lvl > max_prop_lvl && lvl == max_prop_lvl + 1, tout << "In full propagation\n";); bool all_propagated = true; for (auto & kv : m_rels) { checkpoint(); pred_transformer& r = *kv.m_value; all_propagated = r.propagate_to_next_level(lvl) && all_propagated; } //CASSERT("spacer", check_invariant(lvl)); if (all_propagated) { for (auto& kv : m_rels) { checkpoint (); pred_transformer& r = *kv.m_value; r.propagate_to_infinity (lvl); } if (lvl <= max_prop_lvl) { m_inductive_lvl = lvl; IF_VERBOSE(1, verbose_stream () << "\n";); return true; } break; } if (all_propagated && lvl == max_prop_lvl) { m_inductive_lvl = lvl; return true; } else if (all_propagated && lvl > max_prop_lvl) { break; } } if (m_simplify_formulas_post) { simplify_formulas(); } IF_VERBOSE(1, verbose_stream () << "\n";); return false; } reach_fact *pred_transformer::mk_rf(pob& n, model &mdl, const datalog::rule& r) { SASSERT(&n.pt() == this); timeit _timer1 (is_trace_enabled("spacer_timeit"), "mk_rf", verbose_stream ()); expr_ref res(m); reach_fact_ref_vector child_reach_facts; ptr_vector preds; find_predecessors (r, preds); expr_ref_vector path_cons (m); path_cons.push_back (get_transition (r)); app_ref_vector vars (m); for (unsigned i = 0; i < preds.size (); i++) { func_decl* pred = preds[i]; pred_transformer& ch_pt = ctx.get_pred_transformer (pred); // get a reach fact of body preds used in the model expr_ref o_ch_reach (m); reach_fact *kid = ch_pt.get_used_origin_rf(mdl, i); child_reach_facts.push_back (kid); pm.formula_n2o (kid->get (), o_ch_reach, i); path_cons.push_back (o_ch_reach); // collect o-vars to eliminate for (unsigned j = 0; j < pred->get_arity (); j++) { vars.push_back(m.mk_const(pm.o2o(ch_pt.sig(j), 0, i))); } const ptr_vector &v = kid->aux_vars (); for (unsigned j = 0, sz = v.size (); j < sz; ++j) { vars.push_back(m.mk_const(pm.n2o(v [j]->get_decl(), i))); } } // collect aux vars to eliminate ptr_vector& aux_vars = get_aux_vars (r); bool elim_aux = ctx.elim_aux(); if (elim_aux) { vars.append(aux_vars.size(), aux_vars.c_ptr()); } res = mk_and (path_cons); // -- pick an implicant from the path condition if (ctx.reach_dnf()) { expr_ref_vector u(m), lits(m); u.push_back (res); compute_implicant_literals (mdl, u, lits); res = mk_and (lits); } TRACE ("spacer", tout << "Reach fact, before QE:\n"; tout << mk_pp (res, m) << "\n"; tout << "Vars:\n"; for (unsigned i = 0; i < vars.size(); ++i) { tout << mk_pp(vars.get (i), m) << "\n"; } ); { timeit _timer1 (is_trace_enabled("spacer_timeit"), "mk_rf::qe_project", verbose_stream ()); mbp(vars, res, mdl, false, true /* force or skolemize */); } TRACE ("spacer", tout << "Reach fact, after QE project:\n"; tout << mk_pp (res, m) << "\n"; tout << "Vars:\n"; for (unsigned i = 0; i < vars.size(); ++i) { tout << mk_pp(vars.get (i), m) << "\n"; } ); SASSERT (vars.empty ()); m_stats.m_num_reach_queries++; ptr_vector empty; reach_fact *f = alloc(reach_fact, m, r, res, elim_aux ? empty : aux_vars); for (unsigned i = 0, sz = child_reach_facts.size (); i < sz; ++i) { f->add_justification(child_reach_facts.get(i)); } return f; } /** \brief create children states from model cube. */ bool context::create_children(pob& n, datalog::rule const& r, model &mdl, const vector &reach_pred_used, pob_ref_buffer &out) { scoped_watch _w_ (m_create_children_watch); pred_transformer& pt = n.pt(); TRACE("spacer", tout << "Model:\n"; model_smt2_pp(tout, m, mdl, 0); tout << "\n"; tout << "Transition:\n" << mk_pp(pt.get_transition(r), m) << "\n"; tout << "Pob:\n" << mk_pp(n.post(), m) << "\n";); SASSERT (r.get_uninterpreted_tail_size () > 0); ptr_vector preds; pt.find_predecessors(r, preds); // obtain all formulas to consider for model generalization expr_ref_vector forms(m), lits(m); forms.push_back(pt.get_transition(r)); forms.push_back(n.post()); compute_implicant_literals (mdl, forms, lits); expr_ref phi = mk_and (lits); // primed variables of the head app_ref_vector vars(m); for (unsigned i = 0, sz = pt.head()->get_arity(); i < sz; ++i) { vars.push_back(m.mk_const(m_pm.o2n(pt.sig(i), 0))); } // local variables of the rule ptr_vector& aux_vars = pt.get_aux_vars(r); vars.append(aux_vars.size(), aux_vars.c_ptr()); // skolems of the pob n.get_skolems(vars); n.pt().mbp(vars, phi, mdl, true, use_ground_pob()); //qe::reduce_array_selects (*mev.get_model (), phi1); SASSERT (!m_ground_pob || vars.empty ()); TRACE ("spacer", tout << "Implicant:\n"; tout << lits << "\n"; tout << "After MBP:\n" << phi << "\n"; if (!vars.empty()) tout << "Failed to eliminate: " << vars << "\n"; ); if (m_use_gpdr && preds.size() > 1) { SASSERT(vars.empty()); return gpdr_create_split_children(n, r, phi, mdl, out); } derivation *deriv = alloc(derivation, n, r, phi, vars); // pick an order to process children unsigned_vector kid_order; kid_order.resize(preds.size(), 0); for (unsigned i = 0, sz = preds.size(); i < sz; ++i) kid_order[i] = i; if (m_children_order == CO_REV_RULE) { kid_order.reverse(); } else if (m_children_order == CO_RANDOM) { shuffle(kid_order.size(), kid_order.c_ptr(), m_random); } for (unsigned i = 0, sz = preds.size(); i < sz; ++i) { unsigned j = kid_order[i]; pred_transformer &pt = get_pred_transformer(preds.get(j)); const ptr_vector *aux = nullptr; expr_ref sum(m); sum = pt.get_origin_summary (mdl, prev_level(n.level()), j, reach_pred_used[j], &aux); if (!sum) { dealloc(deriv); return false; } deriv->add_premise (pt, j, sum, reach_pred_used[j], aux); } // create post for the first child and add to queue pob* kid = deriv->create_first_child (mdl); // -- failed to create derivation, cleanup and bail out if (!kid) { dealloc(deriv); return false; } SASSERT (kid); kid->set_derivation (deriv); // Optionally disable derivation optimization if (!m_use_derivations) { kid->reset_derivation(); } // -- deriviation is abstract if the current weak model does // -- not satisfy 'T && phi'. It is possible to recover from // -- that more gracefully. For now, we just remove the // -- derivation completely forcing it to be recomputed if (m_weak_abs && (!mdl.is_true(pt.get_transition(r)) || !mdl.is_true(n.post()))) { kid->reset_derivation(); } out.push_back(kid); m_stats.m_num_queries++; return true; } void context::collect_statistics(statistics& st) const { // m_params is not necessarily live when collect_statistics is called. m_pool0->collect_statistics(st); m_pool1->collect_statistics(st); m_pool2->collect_statistics(st); for (auto const& kv : m_rels) { kv.m_value->collect_statistics(st); } // -- number of times a pob for some predicate transformer has // -- been created st.update("SPACER num queries", m_stats.m_num_queries); // -- number of times a reach fact was true in some model st.update("SPACER num reuse reach facts", m_stats.m_num_reuse_reach); // -- maximum level at which any query was asked st.update("SPACER max query lvl", m_stats.m_max_query_lvl); // -- maximum depth st.update("SPACER max depth", m_stats.m_max_depth); // -- level at which safe inductive invariant was found st.update("SPACER inductive level", m_inductive_lvl); // -- length of the counterexample st.update("SPACER cex depth", m_stats.m_cex_depth); // -- number of times expand_pobresulted in undef st.update("SPACER expand pob undef", m_stats.m_expand_pob_undef); // -- number of distinct lemmas constructed st.update("SPACER num lemmas", m_stats.m_num_lemmas); // -- number of restarts taken st.update("SPACER restarts", m_stats.m_num_restarts); // -- time to initialize the rules st.update ("time.spacer.init_rules", m_init_rules_watch.get_seconds ()); // -- time in the main solve loop st.update ("time.spacer.solve", m_solve_watch.get_seconds ()); // -- time in lemma propagation (i.e., pushing) st.update ("time.spacer.solve.propagate", m_propagate_watch.get_seconds ()); // -- time in reachability (i.e., blocking) st.update ("time.spacer.solve.reach", m_reach_watch.get_seconds ()); // -- time in deciding whether a pob is must-reachable st.update ("time.spacer.solve.reach.is-reach", m_is_reach_watch.get_seconds ()); // -- time in creating new predecessors st.update ("time.spacer.solve.reach.children", m_create_children_watch.get_seconds ()); st.update("spacer.lemmas_imported", m_stats.m_num_lemmas_imported); st.update("spacer.lemmas_discarded", m_stats.m_num_lemmas_discarded); for (unsigned i = 0; i < m_lemma_generalizers.size(); ++i) { m_lemma_generalizers[i]->collect_statistics(st); } } void context::reset_statistics() { m_pool0->reset_statistics(); m_pool1->reset_statistics(); m_pool2->reset_statistics(); for (auto & kv : m_rels) { kv.m_value->reset_statistics(); } m_stats.reset(); for (unsigned i = 0; i < m_lemma_generalizers.size(); ++i) { m_lemma_generalizers[i]->reset_statistics(); } m_init_rules_watch.reset (); m_solve_watch.reset (); m_propagate_watch.reset (); m_reach_watch.reset (); m_is_reach_watch.reset (); m_create_children_watch.reset (); } bool context::check_invariant(unsigned lvl) { for (auto &entry : m_rels) { checkpoint(); if (!check_invariant(lvl, entry.m_key)) { return false; } } return true; } bool context::check_invariant(unsigned lvl, func_decl* fn) { ref ctx = mk_smt_solver(m, params_ref::get_empty(), symbol::null); pred_transformer& pt = *m_rels.find(fn); expr_ref_vector conj(m); expr_ref inv = pt.get_formulas(next_level(lvl)); if (m.is_true(inv)) { return true; } pt.add_premises(m_rels, lvl, conj); conj.push_back(m.mk_not(inv)); expr_ref fml(m.mk_and(conj.size(), conj.c_ptr()), m); ctx->assert_expr(fml); lbool result = ctx->check_sat(0, nullptr); TRACE("spacer", tout << "Check invariant level: " << lvl << " " << result << "\n" << mk_pp(fml, m) << "\n";); return result == l_false; } expr_ref context::get_constraints (unsigned level) { expr_ref res(m); expr_ref_vector constraints(m); for (auto & kv : m_rels) { pred_transformer& r = *kv.m_value; expr_ref c = r.get_formulas(level); if (m.is_true(c)) { continue; } // replace local constants by bound variables. expr_ref_vector args(m); for (unsigned i = 0; i < r.sig_size(); ++i) { args.push_back(m.mk_const(m_pm.o2n(r.sig(i), 0))); } expr_ref pred(m); pred = m.mk_app(r.head (), r.sig_size(), args.c_ptr()); constraints.push_back(m.mk_implies(pred, c)); } if (constraints.empty()) { return expr_ref(m.mk_true(), m); } return mk_and (constraints); } void context::add_constraint (expr *c, unsigned level) { if (!c) { return; } if (m.is_true(c)) { return; } expr *e1, *e2; if (m.is_implies(c, e1, e2)) { SASSERT (is_app (e1)); pred_transformer *r = nullptr; if (m_rels.find (to_app (e1)->get_decl (), r)){ lemma_ref lem = alloc(lemma, m, e2, level); lem.get()->set_external(true); if (r->add_lemma(lem.get())) { this->m_stats.m_num_lemmas_imported++; } else{ this->m_stats.m_num_lemmas_discarded++; } } } } void context::new_lemma_eh(pred_transformer &pt, lemma *lem) { if (m_params.spacer_print_json().size()) m_json_marshaller.register_lemma(lem); bool handle=false; for (unsigned i = 0; i < m_callbacks.size(); i++) { handle|=m_callbacks[i]->new_lemma(); } if (!handle) return; if ((is_infty_level(lem->level()) && m_params.spacer_p3_share_invariants()) || (!is_infty_level(lem->level()) && m_params.spacer_p3_share_lemmas())) { expr_ref_vector args(m); for (unsigned i = 0; i < pt.sig_size(); ++i) { args.push_back(m.mk_const(pt.get_manager().o2n(pt.sig(i), 0))); } expr *app = m.mk_app(pt.head(), pt.sig_size(), args.c_ptr()); expr *lemma = m.mk_implies(app, lem->get_expr()); for (unsigned i = 0; i < m_callbacks.size(); i++) { if (m_callbacks[i]->new_lemma()) m_callbacks[i]->new_lemma_eh(lemma, lem->level()); } } } void context::new_pob_eh(pob *p) { if (m_params.spacer_print_json().size()) m_json_marshaller.register_pob(p); } bool context::is_inductive() { // check that inductive level (F infinity) of the query predicate // contains a constant false return false; } /// pob_lt operator inline bool pob_lt_proc::operator() (const pob *pn1, const pob *pn2) const { SASSERT (pn1); SASSERT (pn2); const pob& n1 = *pn1; const pob& n2 = *pn2; if (n1.level() != n2.level()) { return n1.level() < n2.level(); } if (n1.depth() != n2.depth()) { return n1.depth() < n2.depth(); } // -- a more deterministic order of proof obligations in a queue // if (!n1.get_context ().get_params ().spacer_nondet_tie_break ()) { const expr* p1 = n1.post (); const expr* p2 = n2.post (); ast_manager &m = n1.get_ast_manager (); // -- order by size. Less conjunctions is a proxy for // -- generality. Currently, this takes precedence over // -- predicates which might not be the best choice unsigned sz1 = 1; unsigned sz2 = 1; if (m.is_and(p1)) { sz1 = to_app(p1)->get_num_args(); } if (m.is_and(p2)) { sz2 = to_app(p2)->get_num_args(); } if (sz1 != sz2) { return sz1 < sz2; } // -- when all else fails, order by identifiers of the // -- expressions. This roughly means that expressions created // -- earlier are preferred. Note that variables in post are // -- based on names of the predicates. Hence this guarantees an // -- order over predicates as well. That is, two expressions // -- are equal if and only if they correspond to the same proof // -- obligation of the same predicate. if (p1->get_id() != p2->get_id()) { return p1->get_id() < p2->get_id(); } if (n1.pt().head()->get_id() == n2.pt().head()->get_id()) { IF_VERBOSE (1, verbose_stream () << "dup: " << n1.pt ().head ()->get_name () << "(" << n1.level () << ", " << n1.depth () << ") " << p1->get_id () << "\n"; //<< " p1: " << mk_pp (const_cast(p1), m) << "\n" ); } // XXX see comment below on identical nodes // SASSERT (n1.pt ().head ()->get_id () != n2.pt ().head ()->get_id ()); // -- if expression comparison fails, compare by predicate id if (n1.pt().head ()->get_id () != n2.pt ().head ()->get_id ()) { return n1.pt().head()->get_id() < n2.pt().head()->get_id(); } /** XXX Identical nodes. This should not happen. However, * currently, when propagating reachability, we might call * expand_pob() twice on the same node, causing it to generate * the same proof obligation multiple times */ return &n1 < &n2; } // else // return &n1 < &n2; } } z3-z3-4.8.7/src/muz/spacer/spacer_context.h000066400000000000000000001167271356505360400205160ustar00rootroot00000000000000/**++ Copyright (c) 2017 Microsoft Corporation and Arie Gurfinkel Module Name: spacer_context.h Abstract: SPACER predicate transformers and search context. Author: Arie Gurfinkel Anvesh Komuravelli Based on muz/pdr/pdr_context.h by Nikolaj Bjorner (nbjorner) Notes: --*/ #ifndef _SPACER_CONTEXT_H_ #define _SPACER_CONTEXT_H_ #include #include "util/scoped_ptr_vector.h" #include "muz/spacer/spacer_manager.h" #include "muz/spacer/spacer_prop_solver.h" #include "muz/spacer/spacer_json.h" #include "muz/base/fp_params.hpp" namespace datalog { class rule_set; class context; }; namespace spacer { class model_search; class pred_transformer; class derivation; class pob_queue; class context; typedef obj_map rule2inst; typedef obj_map decl2rel; class pob; typedef ref pob_ref; typedef sref_vector pob_ref_vector; typedef sref_buffer pob_ref_buffer; class reach_fact; typedef ref reach_fact_ref; typedef sref_vector reach_fact_ref_vector; class reach_fact { unsigned m_ref_count; expr_ref m_fact; ptr_vector m_aux_vars; const datalog::rule &m_rule; reach_fact_ref_vector m_justification; // variable used to tag this reach fact in an incremental disjunction app_ref m_tag; bool m_init; public: reach_fact (ast_manager &m, const datalog::rule &rule, expr* fact, const ptr_vector &aux_vars, bool init = false) : m_ref_count (0), m_fact (fact, m), m_aux_vars (aux_vars), m_rule(rule), m_tag(m), m_init (init) {} reach_fact (ast_manager &m, const datalog::rule &rule, expr* fact, bool init = false) : m_ref_count (0), m_fact (fact, m), m_rule(rule), m_tag(m), m_init (init) {} bool is_init () {return m_init;} const datalog::rule& get_rule () {return m_rule;} void add_justification (reach_fact *f) {m_justification.push_back (f);} const reach_fact_ref_vector& get_justifications () {return m_justification;} expr *get () {return m_fact.get ();} const ptr_vector &aux_vars () {return m_aux_vars;} app* tag() const {SASSERT(m_tag); return m_tag;} void set_tag(app* tag) {m_tag = tag;} void inc_ref () {++m_ref_count;} void dec_ref () { SASSERT (m_ref_count > 0); --m_ref_count; if(m_ref_count == 0) { dealloc(this); } } }; class lemma; typedef ref lemma_ref; typedef sref_vector lemma_ref_vector; typedef pob pob; // a lemma class lemma { unsigned m_ref_count; ast_manager &m; expr_ref m_body; expr_ref_vector m_cube; app_ref_vector m_zks; app_ref_vector m_bindings; pob_ref m_pob; model_ref m_ctp; // counter-example to pushing unsigned m_lvl; // current level of the lemma unsigned m_init_lvl; // level at which lemma was created unsigned m_bumped:16; unsigned m_weakness:16; unsigned m_external:1; // external lemma from another solver unsigned m_blocked:1; // blocked by CTP unsigned m_background:1; // background assumed fact void mk_expr_core(); void mk_cube_core(); public: lemma(ast_manager &manager, expr * fml, unsigned lvl); lemma(pob_ref const &p); lemma(pob_ref const &p, expr_ref_vector &cube, unsigned lvl); // lemma(const lemma &other) = delete; ast_manager &get_ast_manager() {return m;} model_ref& get_ctp() {return m_ctp;} bool has_ctp() {return !is_inductive() && m_ctp;} void set_ctp(model_ref &v) {m_ctp = v;} void reset_ctp() {m_ctp.reset();} void bump() {m_bumped++;} unsigned get_bumped() {return m_bumped;} expr *get_expr(); bool is_false(); expr_ref_vector const &get_cube(); void update_cube(pob_ref const &p, expr_ref_vector &cube); bool has_pob() {return m_pob;} pob_ref &get_pob() {return m_pob;} unsigned weakness() {return m_weakness;} void add_skolem(app *zk, app* b); void set_external(bool ext){m_external = ext;} bool external() { return m_external;} void set_background(bool v) {m_background = v;} bool is_background() {return m_background;} bool is_blocked() {return m_blocked;} void set_blocked(bool v) {m_blocked=v;} bool is_inductive() const {return is_infty_level(m_lvl);} unsigned level () const {return m_lvl;} unsigned init_level() const {return m_init_lvl;} void set_level (unsigned lvl); app_ref_vector& get_bindings() {return m_bindings;} bool has_binding(app_ref_vector const &binding); void add_binding(app_ref_vector const &binding); void instantiate(expr * const * exprs, expr_ref &result, expr *e = nullptr); void mk_insts(expr_ref_vector& inst, expr* e = nullptr); bool is_ground () {return !is_quantifier (get_expr());} void inc_ref () {++m_ref_count;} void dec_ref () { SASSERT (m_ref_count > 0); --m_ref_count; if (m_ref_count == 0) {dealloc(this);} } }; struct lemma_lt_proc : public std::binary_function { bool operator() (lemma *a, lemma *b) { return (a->level () < b->level ()) || (a->level () == b->level () && ast_lt_proc() (a->get_expr (), b->get_expr ())); } }; // // Predicate transformer state. // A predicate transformer corresponds to the // set of rules that have the same head predicates. // class pred_transformer { struct stats { unsigned m_num_propagations; // num of times lemma is pushed higher unsigned m_num_invariants; // num of infty lemmas found unsigned m_num_ctp_blocked; // num of time ctp blocked lemma pushing unsigned m_num_is_invariant; // num of times lemmas are pushed unsigned m_num_lemma_level_jump; // lemma learned at higher level than expected unsigned m_num_reach_queries; stats() { reset(); } void reset() { memset(this, 0, sizeof(*this)); } }; /// manager of the lemmas in all the frames #include "muz/spacer/spacer_legacy_frames.h" class frames { private: pred_transformer &m_pt; // parent pred_transformer lemma_ref_vector m_pinned_lemmas; // all created lemmas lemma_ref_vector m_lemmas; // active lemmas lemma_ref_vector m_bg_invs; // background (assumed) invariants unsigned m_size; // num of frames bool m_sorted; // true if m_lemmas is sorted by m_lt lemma_lt_proc m_lt; // sort order for m_lemmas void sort (); public: frames (pred_transformer &pt) : m_pt (pt), m_size(0), m_sorted (true) {} ~frames() {} void simplify_formulas (); pred_transformer& pt() const {return m_pt;} const lemma_ref_vector &lemmas() const {return m_lemmas;} void get_frame_lemmas (unsigned level, expr_ref_vector &out) const { for (auto &lemma : m_lemmas) { if (lemma->level() == level) { out.push_back(lemma->get_expr()); } } } void get_frame_geq_lemmas (unsigned level, expr_ref_vector &out, bool with_bg = false) const { for (auto &lemma : m_lemmas) { if (lemma->level() >= level) { out.push_back(lemma->get_expr()); } } if (with_bg) { for (auto &lemma : m_bg_invs) out.push_back(lemma->get_expr()); } } const lemma_ref_vector& get_bg_invs() const {return m_bg_invs;} unsigned size() const {return m_size;} unsigned lemma_size() const {return m_lemmas.size ();} unsigned bg_invs_size() const {return m_bg_invs.size();} void add_frame() {m_size++;} void inherit_frames (frames &other) { for (auto &other_lemma : other.m_lemmas) { lemma_ref new_lemma = alloc(lemma, m_pt.get_ast_manager(), other_lemma->get_expr(), other_lemma->level()); new_lemma->add_binding(other_lemma->get_bindings()); add_lemma(new_lemma.get()); } m_sorted = false; m_bg_invs.append(other.m_bg_invs); } bool add_lemma (lemma *new_lemma); void propagate_to_infinity (unsigned level); bool propagate_to_next_level (unsigned level); }; /** manager of proof-obligations (pob_manager) Pobs are determined uniquely by their post-condition and a parent pob. They are managed by pob_manager and remain live through the life of the manager */ class pob_manager { // a buffer that contains space for one pob and allocates more // space if needed typedef ptr_buffer pob_buffer; // Type for the map from post-conditions to pobs. The common // case is that each post-condition corresponds to a single // pob. Other cases are handled by expanding the buffer typedef obj_map expr2pob_buffer; // parent predicate transformer pred_transformer &m_pt; // map from post-conditions to pobs expr2pob_buffer m_pobs; // a store pob_ref_vector m_pinned; public: pob_manager(pred_transformer &pt) : m_pt(pt) {} pob* mk_pob(pob *parent, unsigned level, unsigned depth, expr *post, app_ref_vector const &b); pob* mk_pob(pob *parent, unsigned level, unsigned depth, expr *post) { app_ref_vector b(m_pt.get_ast_manager()); return mk_pob (parent, level, depth, post, b); } unsigned size() const {return m_pinned.size();} pob* find_pob(pob* parent, expr *post); }; class pt_rule { const datalog::rule &m_rule; expr_ref m_trans; // ground version of m_rule ptr_vector m_auxs; // auxiliary variables in m_trans app_ref_vector m_reps; // map from fv in m_rule to ground constants app_ref m_tag; // a unique tag for the rule public: pt_rule(ast_manager &m, const datalog::rule &r) : m_rule(r), m_trans(m), m_reps(m), m_tag(m) {} const datalog::rule &rule() const {return m_rule;} void set_tag(expr *tag) {SASSERT(is_app(tag)); set_tag(to_app(tag));} void set_tag(app* tag) {m_tag = tag;} app* tag() const {return m_tag;} ptr_vector &auxs() {return m_auxs;} void set_auxs(ptr_vector &v) {m_auxs.reset(); m_auxs.append(v);} void set_reps(app_ref_vector &v) {m_reps.reset(); m_reps.append(v);} void set_trans(expr_ref &v) {m_trans=v;} expr* trans() const {return m_trans;} bool is_init() const {return m_rule.get_uninterpreted_tail_size() == 0;} }; class pt_rules { typedef obj_map rule2ptrule; typedef obj_map tag2ptrule; typedef rule2ptrule::iterator iterator; rule2ptrule m_rules; tag2ptrule m_tags; public: pt_rules() {} ~pt_rules() {for (auto &kv : m_rules) {dealloc(kv.m_value);}} bool find_by_rule(const datalog::rule &r, pt_rule* &ptr) { return m_rules.find(&r, ptr); } bool find_by_tag(const expr* tag, pt_rule* &ptr) { return m_tags.find(tag, ptr); } pt_rule &mk_rule(ast_manager &m, const datalog::rule &r) { return mk_rule(pt_rule(m, r)); } pt_rule &mk_rule(const pt_rule &v); void set_tag(expr* tag, pt_rule &v) { pt_rule *p; VERIFY(find_by_rule(v.rule(), p)); p->set_tag(tag); m_tags.insert(tag, p); } bool empty() {return m_rules.empty();} iterator begin() {return m_rules.begin();} iterator end() {return m_rules.end();} }; manager& pm; // spacer::manager ast_manager& m; // ast_manager context& ctx; // spacer::context func_decl_ref m_head; // predicate func_decl_ref_vector m_sig; // signature ptr_vector m_use; // places where 'this' is referenced. pt_rules m_pt_rules; // pt rules used to derive transformer ptr_vector m_rules; // rules used to derive transformer scoped_ptr m_solver; // solver context ref m_reach_solver; // context for reachability facts pob_manager m_pobs; // proof obligations created so far frames m_frames; // frames with lemmas reach_fact_ref_vector m_reach_facts; // reach facts unsigned m_rf_init_sz; // number of reach fact from INIT expr_ref_vector m_transition_clause; // extra clause for trans expr_ref m_transition; // transition relation expr_ref m_init; // initial condition app_ref m_extend_lit0; // first literal used to extend initial state app_ref m_extend_lit; // current literal to extend initial state bool m_all_init; // true if the pt has no uninterpreted body in any rule ptr_vector m_predicates; // temp vector used with find_predecessors() stats m_stats; stopwatch m_initialize_watch; stopwatch m_must_reachable_watch; stopwatch m_ctp_watch; stopwatch m_mbp_watch; void init_sig(); app_ref mk_extend_lit(); void ensure_level(unsigned level); void add_lemma_core (lemma *lemma, bool ground_only = false); void add_lemma_from_child (pred_transformer &child, lemma *lemma, unsigned lvl, bool ground_only = false); void mk_assumptions(func_decl* head, expr* fml, expr_ref_vector& result); // Initialization void init_rules(decl2rel const& pts); void init_rule(decl2rel const& pts, datalog::rule const& rule); void init_atom(decl2rel const& pts, app * atom, app_ref_vector& var_reprs, expr_ref_vector& side, unsigned tail_idx); void simplify_formulas(tactic& tac, expr_ref_vector& fmls); void add_premises(decl2rel const& pts, unsigned lvl, datalog::rule& rule, expr_ref_vector& r); app_ref mk_fresh_rf_tag (); // get tagged formulae of all of the background invariants for all of the // predecessors of the current transformer void get_pred_bg_invs(expr_ref_vector &out); const lemma_ref_vector &get_bg_invs() const {return m_frames.get_bg_invs();} public: pred_transformer(context& ctx, manager& pm, func_decl* head); ~pred_transformer() {} inline bool use_native_mbp (); reach_fact *get_rf (expr *v) { for (auto *rf : m_reach_facts) { if (v == rf->get()) {return rf;} } return nullptr; } void find_predecessors(datalog::rule const& r, ptr_vector& predicates) const; void add_rule(datalog::rule* r) {m_rules.push_back(r);} void add_use(pred_transformer* pt) {if (!m_use.contains(pt)) {m_use.insert(pt);}} void initialize(decl2rel const& pts); func_decl* head() const {return m_head;} ptr_vector const& rules() const {return m_rules;} func_decl* sig(unsigned i) const {return m_sig[i];} // signature func_decl* const* sig() {return m_sig.c_ptr();} unsigned sig_size() const {return m_sig.size();} expr* transition() const {return m_transition;} expr* init() const {return m_init;} expr* rule2tag(datalog::rule const* r) { pt_rule *p; return m_pt_rules.find_by_rule(*r, p) ? p->tag() : nullptr; } unsigned get_num_levels() const {return m_frames.size ();} expr_ref get_cover_delta(func_decl* p_orig, int level); void add_cover(unsigned level, expr* property, bool bg = false); expr_ref get_reachable(); std::ostream& display(std::ostream& strm) const; void collect_statistics(statistics& st) const; void reset_statistics(); bool is_must_reachable(expr* state, model_ref* model = nullptr); /// \brief Returns reachability fact active in the given model /// all determines whether initial reachability facts are included as well reach_fact *get_used_rf(model& mdl, bool all = true); /// \brief Returns reachability fact active in the origin of the given model reach_fact* get_used_origin_rf(model &mdl, unsigned oidx); expr_ref get_origin_summary(model &mdl, unsigned level, unsigned oidx, bool must, const ptr_vector **aux); bool is_ctp_blocked(lemma *lem); const datalog::rule *find_rule(model &mdl); const datalog::rule *find_rule(model &mev, bool& is_concrete, vector& reach_pred_used, unsigned& num_reuse_reach); expr* get_transition(datalog::rule const& r) { pt_rule *p; return m_pt_rules.find_by_rule(r, p) ? p->trans() : nullptr; } ptr_vector& get_aux_vars(datalog::rule const& r) { pt_rule *p = nullptr; VERIFY(m_pt_rules.find_by_rule(r, p)); return p->auxs(); } bool propagate_to_next_level(unsigned level); void propagate_to_infinity(unsigned level); /// \brief Add a lemma to the current context and all users bool add_lemma(expr * e, unsigned lvl, bool bg); bool add_lemma(lemma* lem) {return m_frames.add_lemma(lem);} expr* get_reach_case_var (unsigned idx) const; bool has_rfs () const { return !m_reach_facts.empty () ;} /// initialize reachability facts using initial rules void init_rfs (); reach_fact *mk_rf(pob &n, model &mdl, const datalog::rule &r); void add_rf (reach_fact *fact); // add reachability fact reach_fact* get_last_rf () const { return m_reach_facts.back (); } expr* get_last_rf_tag () const; pob* mk_pob(pob *parent, unsigned level, unsigned depth, expr *post, app_ref_vector const &b){ return m_pobs.mk_pob(parent, level, depth, post, b); } pob* find_pob(pob *parent, expr *post) { return m_pobs.find_pob(parent, post); } pob* mk_pob(pob *parent, unsigned level, unsigned depth, expr *post) { return m_pobs.mk_pob(parent, level, depth, post); } lbool is_reachable(pob& n, expr_ref_vector* core, model_ref *model, unsigned& uses_level, bool& is_concrete, datalog::rule const*& r, vector& reach_pred_used, unsigned& num_reuse_reach); bool is_invariant(unsigned level, lemma* lem, unsigned& solver_level, expr_ref_vector* core = nullptr); bool is_invariant(unsigned level, expr* lem, unsigned& solver_level, expr_ref_vector* core = nullptr) { // XXX only needed for legacy_frames to compile UNREACHABLE(); return false; } bool check_inductive(unsigned level, expr_ref_vector& state, unsigned& assumes_level, unsigned weakness = UINT_MAX); expr_ref get_formulas(unsigned level, bool bg = false) const; void simplify_formulas(); context& get_context () const {return ctx;} manager& get_manager() const {return pm;} ast_manager& get_ast_manager() const {return m;} void add_premises(decl2rel const& pts, unsigned lvl, expr_ref_vector& r); void inherit_lemmas(pred_transformer& other); void ground_free_vars(expr* e, app_ref_vector& vars, ptr_vector& aux_vars, bool is_init); /// \brief Adds a given expression to the set of initial rules app* extend_initial (expr *e); /// \brief Returns true if the obligation is already blocked by current lemmas bool is_blocked (pob &n, unsigned &uses_level); /// \brief Returns true if the obligation is already blocked by current quantified lemmas bool is_qblocked (pob &n); /// \brief interface to Model Based Projection void mbp(app_ref_vector &vars, expr_ref &fml, model &mdl, bool reduce_all_selects, bool force = false); void updt_solver(prop_solver *solver); void updt_solver_with_lemmas(prop_solver *solver, const pred_transformer &pt, app *rule_tag, unsigned pos); void update_solver_with_rfs(prop_solver *solver, const pred_transformer &pt, app *rule_tag, unsigned pos); }; /** * A proof obligation. */ class pob { // TBD: remove this friend class context; unsigned m_ref_count; /// parent node pob_ref m_parent; /// predicate transformer pred_transformer& m_pt; /// post-condition decided by this node expr_ref m_post; // if m_post is not ground, then m_binding is an instantiation for // all quantified variables app_ref_vector m_binding; /// new post to be swapped in for m_post expr_ref m_new_post; /// level at which to decide the post unsigned m_level:16; unsigned m_depth:16; /// whether a concrete answer to the post is found unsigned m_open:1; /// whether to use farkas generalizer to construct a lemma blocking this node unsigned m_use_farkas:1; /// true if this pob is in pob_queue unsigned m_in_queue:1; unsigned m_weakness; /// derivation representing the position of this node in the parent's rule scoped_ptr m_derivation; /// pobs created as children of this pob (at any time, not /// necessarily currently active) ptr_vector m_kids; // lemmas created to block this pob (at any time, not necessarily active) ptr_vector m_lemmas; // depth -> watch std::map m_expand_watches; unsigned m_blocked_lvl; public: pob (pob* parent, pred_transformer& pt, unsigned level, unsigned depth=0, bool add_to_parent=true); ~pob() {if (m_parent) { m_parent->erase_child(*this); }} // TBD: move into constructor and make private void set_post(expr *post, app_ref_vector const &binding); void set_post(expr *post); unsigned weakness() {return m_weakness;} void bump_weakness() {m_weakness++;} void reset_weakness() {m_weakness=0;} void inc_level () { SASSERT(!is_in_queue()); m_level++; m_depth++;reset_weakness(); } void inherit(pob const &p); void set_derivation (derivation *d) {m_derivation = d;} bool has_derivation () const {return (bool)m_derivation;} derivation &get_derivation() const {return *m_derivation.get ();} void reset_derivation () {set_derivation (nullptr);} /// detaches derivation from the node without deallocating derivation* detach_derivation () {return m_derivation.detach ();} pob* parent () const { return m_parent.get (); } pred_transformer& pt () const { return m_pt; } ast_manager& get_ast_manager () const { return m_pt.get_ast_manager (); } manager& get_manager () const { return m_pt.get_manager (); } context& get_context () const {return m_pt.get_context ();} unsigned level () const { return m_level; } unsigned depth () const {return m_depth;} unsigned width () const {return m_kids.size();} unsigned blocked_at(unsigned lvl=0){ return (m_blocked_lvl = std::max(lvl, m_blocked_lvl)); } bool is_in_queue() const {return m_in_queue;} void set_in_queue(bool v) {m_in_queue = v;} bool use_farkas_generalizer () const {return m_use_farkas;} void set_farkas_generalizer (bool v) {m_use_farkas = v;} expr* post() const { return m_post.get (); } bool is_closed () const { return !m_open; } void close(); const ptr_vector &children() const {return m_kids;} void add_child (pob &v) {m_kids.push_back (&v);} void erase_child (pob &v) {m_kids.erase (&v);} const ptr_vector &lemmas() const {return m_lemmas;} void add_lemma(lemma* new_lemma) {m_lemmas.push_back(new_lemma);} bool is_ground () const { return m_binding.empty (); } unsigned get_free_vars_size() const { return m_binding.size(); } app_ref_vector const &get_binding() const {return m_binding;} /* * Returns a map from variable id to skolems that implicitly * represent them in the pob. Note that only some (or none) of the * skolems returned actually appear in the post of the pob. */ void get_skolems(app_ref_vector& v); void on_expand() { m_expand_watches[m_depth].start(); if (m_parent.get()){m_parent.get()->on_expand();} } void off_expand() { m_expand_watches[m_depth].stop(); if (m_parent.get()){m_parent.get()->off_expand();} } double get_expand_time(unsigned depth) { return m_expand_watches[depth].get_seconds();} void inc_ref () {++m_ref_count;} void dec_ref () { --m_ref_count; if (m_ref_count == 0) {dealloc(this);} } std::ostream &display(std::ostream &out, bool full = false) const; class on_expand_event { pob &m_p; public: on_expand_event(pob &p) : m_p(p) {m_p.on_expand();} ~on_expand_event() {m_p.off_expand();} }; }; inline std::ostream &operator<<(std::ostream &out, pob const &p) { return p.display(out); } struct pob_lt_proc : public std::binary_function { bool operator() (const pob *pn1, const pob *pn2) const; }; struct pob_gt_proc : public std::binary_function { bool operator() (const pob *n1, const pob *n2) const { return pob_lt_proc()(n2, n1); } }; /** */ class derivation { /// a single premise of a derivation class premise { pred_transformer &m_pt; /// origin order in the rule unsigned m_oidx; /// summary fact corresponding to the premise expr_ref m_summary; /// whether this is a must or may premise bool m_must; app_ref_vector m_ovars; public: premise (pred_transformer &pt, unsigned oidx, expr *summary, bool must, const ptr_vector *aux_vars = nullptr); premise (const premise &p); bool is_must() {return m_must;} expr * get_summary() {return m_summary.get ();} app_ref_vector &get_ovars() {return m_ovars;} unsigned get_oidx() {return m_oidx;} pred_transformer &pt() {return m_pt;} /// \brief Updated the summary. /// The new summary is over n-variables. void set_summary(expr * summary, bool must, const ptr_vector *aux_vars = nullptr); }; /// parent model node pob& m_parent; /// the rule corresponding to this derivation const datalog::rule &m_rule; /// the premises vector m_premises; /// pointer to the active premise unsigned m_active; // transition relation over origin variables expr_ref m_trans; // implicitly existentially quantified variables in m_trans app_ref_vector m_evars; /// -- create next child using given model as the guide /// -- returns NULL if there is no next child pob* create_next_child (model &mdl); /// existentially quantify vars and skolemize the result void exist_skolemize(expr *fml, app_ref_vector &vars, expr_ref &res); public: derivation (pob& parent, datalog::rule const& rule, expr *trans, app_ref_vector const &evars); void add_premise (pred_transformer &pt, unsigned oidx, expr * summary, bool must, const ptr_vector *aux_vars = nullptr); /// creates the first child. Must be called after all the premises /// are added. The model must be valid for the premises /// Returns NULL if no child exits pob *create_first_child (model &mdl); /// Create the next child. Must summary of the currently active /// premise must be consistent with the transition relation pob *create_next_child (); datalog::rule const& get_rule () const { return m_rule; } pob& get_parent () const { return m_parent; } ast_manager &get_ast_manager () const {return m_parent.get_ast_manager ();} manager &get_manager () const {return m_parent.get_manager ();} context &get_context() const {return m_parent.get_context();} pred_transformer &pt() const {return m_parent.pt();} }; class pob_queue { typedef std::priority_queue, pob_gt_proc> pob_queue_ty; pob_ref m_root; unsigned m_max_level; unsigned m_min_depth; pob_queue_ty m_data; public: pob_queue(): m_root(nullptr), m_max_level(0), m_min_depth(0) {} ~pob_queue() {} void reset(); pob* top(); void pop(); void push (pob &n); void inc_level () { SASSERT (!m_data.empty () || m_root); m_max_level++; m_min_depth++; if (m_root && m_data.empty()) { SASSERT(!m_root->is_in_queue()); m_root->set_in_queue(true); m_data.push(m_root.get()); } } pob& get_root() const {return *m_root.get ();} void set_root(pob& n); bool is_root(pob& n) const {return m_root.get () == &n;} unsigned max_level() const {return m_max_level;} unsigned min_depth() const {return m_min_depth;} size_t size() const {return m_data.size();} }; /** * Generalizers (strengthens) a lemma */ class lemma_generalizer { protected: context& m_ctx; public: lemma_generalizer(context& ctx): m_ctx(ctx) {} virtual ~lemma_generalizer() {} virtual void operator()(lemma_ref &lemma) = 0; virtual void collect_statistics(statistics& st) const {} virtual void reset_statistics() {} }; class spacer_callback { protected: context &m_context; public: spacer_callback(context &context) : m_context(context) {} virtual ~spacer_callback() = default; context &get_context() { return m_context; } virtual inline bool new_lemma() { return false; } virtual void new_lemma_eh(expr *lemma, unsigned level) {} virtual inline bool predecessor() { return false; } virtual void predecessor_eh() {} virtual inline bool unfold() { return false; } virtual void unfold_eh() {} virtual inline bool propagate() { return false; } virtual void propagate_eh() {} }; // order in which children are processed enum spacer_children_order { CO_RULE, // same order as in the rule CO_REV_RULE, // reverse order of the rule CO_RANDOM // random shuffle }; class context { struct stats { unsigned m_num_queries; unsigned m_num_reuse_reach; unsigned m_max_query_lvl; unsigned m_max_depth; unsigned m_cex_depth; unsigned m_expand_pob_undef; unsigned m_num_lemmas; unsigned m_num_restarts; unsigned m_num_lemmas_imported; unsigned m_num_lemmas_discarded; stats() { reset(); } void reset() { memset(this, 0, sizeof(*this)); } }; // stat watches stopwatch m_solve_watch; stopwatch m_propagate_watch; stopwatch m_reach_watch; stopwatch m_is_reach_watch; stopwatch m_create_children_watch; stopwatch m_init_rules_watch; fp_params const& m_params; ast_manager& m; datalog::context* m_context; manager m_pm; // three solver pools for different queries scoped_ptr m_pool0; scoped_ptr m_pool1; scoped_ptr m_pool2; random_gen m_random; spacer_children_order m_children_order; decl2rel m_rels; // Map from relation predicate to fp-operator. func_decl_ref m_query_pred; pred_transformer* m_query; mutable pob_queue m_pob_queue; lbool m_last_result; unsigned m_inductive_lvl; unsigned m_expanded_lvl; ptr_buffer m_lemma_generalizers; stats m_stats; model_converter_ref m_mc; proof_converter_ref m_pc; bool m_use_native_mbp; bool m_instantiate; bool m_use_qlemmas; bool m_weak_abs; bool m_use_restarts; bool m_simplify_pob; bool m_use_euf_gen; bool m_use_lim_num_gen; bool m_use_ctp; bool m_use_inc_clause; bool m_use_ind_gen; bool m_use_array_eq_gen; bool m_validate_lemmas; bool m_use_propagate; bool m_reset_obligation_queue; bool m_push_pob; bool m_use_lemma_as_pob; bool m_elim_aux; bool m_reach_dnf; bool m_use_derivations; bool m_validate_result; bool m_use_eq_prop; bool m_ground_pob; bool m_q3_qgen; bool m_use_gpdr; bool m_simplify_formulas_pre; bool m_simplify_formulas_post; bool m_pdr_bfs; bool m_use_bg_invs; unsigned m_push_pob_max_depth; unsigned m_max_level; unsigned m_restart_initial_threshold; unsigned m_blast_term_ite_inflation; scoped_ptr_vector m_callbacks; json_marshaller m_json_marshaller; // Solve using gpdr strategy lbool gpdr_solve_core(); bool gpdr_check_reachability(unsigned lvl, model_search &ms); bool gpdr_create_split_children(pob &n, const datalog::rule &r, expr *trans, model &mdl, pob_ref_buffer &out); // Functions used by search. lbool solve_core(unsigned from_lvl = 0); bool is_requeue(pob &n); bool check_reachability(); bool propagate(unsigned min_prop_lvl, unsigned max_prop_lvl, unsigned full_prop_lvl); bool is_reachable(pob &n); lbool expand_pob(pob &n, pob_ref_buffer &out); bool create_children(pob& n, const datalog::rule &r, model &mdl, const vector& reach_pred_used, pob_ref_buffer &out); /** \brief Retrieve satisfying assignment with explanation. */ expr_ref mk_sat_answer() const {return get_ground_sat_answer();} expr_ref mk_unsat_answer() const; unsigned get_cex_depth (); // Generate inductive property void get_level_property(unsigned lvl, expr_ref_vector& res, vector & rs, bool with_bg = false) const; // Initialization void init_lemma_generalizers(); void reset_lemma_generalizers(); void inherit_lemmas(const decl2rel& rels); void init_global_smt_params(); void init_rules(datalog::rule_set& rules, decl2rel& transformers); // (re)initialize context with new relations void init(const decl2rel &rels); bool validate(); bool check_invariant(unsigned lvl); bool check_invariant(unsigned lvl, func_decl* fn); void checkpoint(); void simplify_formulas(); void dump_json(); void predecessor_eh(); void updt_params(); public: /** Initial values of predicates are stored in corresponding relations in dctx. We check whether there is some reachable state of the relation checked_relation. */ context(fp_params const& params, ast_manager& m); ~context(); const fp_params &get_params() const { return m_params; } bool use_eq_prop() const {return m_use_eq_prop;} bool use_native_mbp() const {return m_use_native_mbp;} bool use_ground_pob() const {return m_ground_pob;} bool use_instantiate() const {return m_instantiate;} bool weak_abs() const {return m_weak_abs;} bool use_qlemmas() const {return m_use_qlemmas;} bool use_euf_gen() const {return m_use_euf_gen;} bool use_lim_num_gen() const {return m_use_lim_num_gen;} bool simplify_pob() const {return m_simplify_pob;} bool use_ctp() const {return m_use_ctp;} bool use_inc_clause() const {return m_use_inc_clause;} unsigned blast_term_ite_inflation() const {return m_blast_term_ite_inflation;} bool elim_aux() const {return m_elim_aux;} bool reach_dnf() const {return m_reach_dnf;} bool use_bg_invs() const {return m_use_bg_invs;} ast_manager& get_ast_manager() const {return m;} manager& get_manager() {return m_pm;} const manager & get_manager() const {return m_pm;} decl2rel const& get_pred_transformers() const {return m_rels;} pred_transformer& get_pred_transformer(func_decl* p) const {return *m_rels.find(p);} datalog::context& get_datalog_context() const { SASSERT(m_context); return *m_context; } void update_rules(datalog::rule_set& rules); lbool solve(unsigned from_lvl = 0); lbool solve_from_lvl (unsigned from_lvl); expr_ref get_answer(); /** * get bottom-up (from query) sequence of ground predicate instances * (for e.g. P(0,1,0,0,3)) that together form a ground derivation to query */ expr_ref get_ground_sat_answer () const; proof_ref get_ground_refutation() const; void get_rules_along_trace (datalog::rule_ref_vector& rules); void collect_statistics(statistics& st) const; void reset_statistics(); void reset(); std::ostream& display(std::ostream& out) const; void display_certificate(std::ostream& out) const; pob& get_root() const {return m_pob_queue.get_root();} void set_query(func_decl* q) {m_query_pred = q;} void set_unsat() {m_last_result = l_false;} void set_model_converter(model_converter_ref& mc) {m_mc = mc;} model_converter_ref get_model_converter() { return m_mc; } void set_proof_converter(proof_converter_ref& pc) { m_pc = pc; } scoped_ptr_vector &callbacks() {return m_callbacks;} unsigned get_num_levels(func_decl* p); expr_ref get_cover_delta(int level, func_decl* p_orig, func_decl* p); void add_cover(int level, func_decl* pred, expr* property, bool bg = false); expr_ref get_reachable (func_decl* p); void add_invariant (func_decl *pred, expr* property); model_ref get_model(); proof_ref get_proof() const {return get_ground_refutation();} expr_ref get_constraints (unsigned lvl); void add_constraint (expr *c, unsigned lvl); void new_lemma_eh(pred_transformer &pt, lemma *lem); void new_pob_eh(pob *p); bool is_inductive(); // three different solvers with three different sets of parameters // different solvers are used for different types of queries in spacer solver* mk_solver0() {return m_pool0->mk_solver();} solver* mk_solver1() {return m_pool1->mk_solver();} solver* mk_solver2() {return m_pool2->mk_solver();} }; inline bool pred_transformer::use_native_mbp () {return ctx.use_native_mbp ();} } #endif z3-z3-4.8.7/src/muz/spacer/spacer_dl_interface.cpp000066400000000000000000000252041356505360400217710ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation and Arie Gurfinkel Module Name: spacer_dl.cpp Abstract: SMT2 interface for the datalog SPACER Author: Arie Gurfinkel Revision History: --*/ #include "muz/base/dl_context.h" #include "muz/transforms/dl_mk_coi_filter.h" #include "muz/transforms/dl_mk_interp_tail_simplifier.h" #include "muz/transforms/dl_mk_subsumption_checker.h" #include "muz/transforms/dl_mk_rule_inliner.h" #include "muz/base/dl_rule.h" #include "muz/base/dl_rule_transformer.h" #include "parsers/smt2/smt2parser.h" #include "muz/spacer/spacer_context.h" #include "muz/spacer/spacer_dl_interface.h" #include "muz/base/dl_rule_set.h" #include "muz/transforms/dl_mk_slice.h" #include "muz/transforms/dl_mk_unfold.h" #include "muz/transforms/dl_mk_coalesce.h" #include "model/model_smt2_pp.h" #include "ast/scoped_proof.h" #include "muz/transforms/dl_transforms.h" #include "muz/spacer/spacer_callback.h" using namespace spacer; dl_interface::dl_interface(datalog::context& ctx) : engine_base(ctx.get_manager(), "spacer"), m_ctx(ctx), m_spacer_rules(ctx), m_old_rules(ctx), m_context(nullptr), m_refs(ctx.get_manager()) { m_context = alloc(spacer::context, ctx.get_params(), ctx.get_manager()); } dl_interface::~dl_interface() { dealloc(m_context); } // // Check if the new rules are weaker so that we can // re-use existing context. // void dl_interface::check_reset() { datalog::rule_set const& new_rules = m_ctx.get_rules(); datalog::rule_ref_vector const& old_rules = m_old_rules.get_rules(); bool is_subsumed = !old_rules.empty(); for (unsigned i = 0; is_subsumed && i < new_rules.get_num_rules(); ++i) { is_subsumed = false; for (unsigned j = 0; !is_subsumed && j < old_rules.size(); ++j) { if (m_ctx.check_subsumes(*old_rules[j], *new_rules.get_rule(i))) { is_subsumed = true; } } if (!is_subsumed) { TRACE("spacer", new_rules.get_rule(i)->display(m_ctx, tout << "Fresh rule ");); m_context->reset(); } } m_old_rules.replace_rules(new_rules); } lbool dl_interface::query(expr * query) { //we restore the initial state in the datalog context m_ctx.ensure_opened(); m_refs.reset(); m_pred2slice.reset(); ast_manager& m = m_ctx.get_manager(); datalog::rule_manager& rm = m_ctx.get_rule_manager(); datalog::rule_set& rules0 = m_ctx.get_rules(); datalog::rule_set old_rules(rules0); func_decl_ref query_pred(m); rm.mk_query(query, m_ctx.get_rules()); check_reset(); TRACE("spacer", tout << "query: " << mk_pp(query, m) << "\n"; tout << "rules:\n"; m_ctx.display_rules(tout); ); apply_default_transformation(m_ctx); if (m_ctx.get_params().xform_slice()) { datalog::rule_transformer transformer(m_ctx); datalog::mk_slice* slice = alloc(datalog::mk_slice, m_ctx); transformer.register_plugin(slice); m_ctx.transform_rules(transformer); // track sliced predicates. obj_map const& preds = slice->get_predicates(); obj_map::iterator it = preds.begin(); obj_map::iterator end = preds.end(); for (; it != end; ++it) { m_pred2slice.insert(it->m_key, it->m_value); m_refs.push_back(it->m_key); m_refs.push_back(it->m_value); } } if (m_ctx.get_params().xform_unfold_rules() > 0) { unsigned num_unfolds = m_ctx.get_params().xform_unfold_rules(); datalog::rule_transformer transf1(m_ctx), transf2(m_ctx); transf1.register_plugin(alloc(datalog::mk_coalesce, m_ctx)); transf2.register_plugin(alloc(datalog::mk_unfold, m_ctx)); if (m_ctx.get_params().xform_coalesce_rules()) { m_ctx.transform_rules(transf1); } while (num_unfolds > 0) { m_ctx.transform_rules(transf2); --num_unfolds; } } const datalog::rule_set& rules = m_ctx.get_rules(); if (rules.get_output_predicates().empty()) { m_context->set_unsat(); return l_false; } query_pred = rules.get_output_predicate(); IF_VERBOSE(2, m_ctx.display_rules(verbose_stream());); m_spacer_rules.replace_rules(rules); m_spacer_rules.close(); m_ctx.record_transformed_rules(); m_ctx.reopen(); m_ctx.replace_rules(old_rules); scoped_restore_proof _sc(m); // update_rules may overwrite the proof mode. m_context->set_proof_converter(m_ctx.get_proof_converter()); m_context->set_model_converter(m_ctx.get_model_converter()); m_context->set_query(query_pred); m_context->update_rules(m_spacer_rules); if (m_spacer_rules.get_rules().empty()) { m_context->set_unsat(); IF_VERBOSE(2, model_smt2_pp(verbose_stream(), m, *m_context->get_model(), 0);); return l_false; } return m_context->solve(m_ctx.get_params().spacer_min_level()); } lbool dl_interface::query_from_lvl(expr * query, unsigned lvl) { //we restore the initial state in the datalog context m_ctx.ensure_opened(); m_refs.reset(); m_pred2slice.reset(); ast_manager& m = m_ctx.get_manager(); datalog::rule_manager& rm = m_ctx.get_rule_manager(); datalog::rule_set& rules0 = m_ctx.get_rules(); datalog::rule_set old_rules(rules0); func_decl_ref query_pred(m); rm.mk_query(query, m_ctx.get_rules()); expr_ref bg_assertion = m_ctx.get_background_assertion(); check_reset(); TRACE("spacer", if (!m.is_true(bg_assertion)) { tout << "axioms:\n"; tout << mk_pp(bg_assertion, m) << "\n"; } tout << "query: " << mk_pp(query, m) << "\n"; tout << "rules:\n"; m_ctx.display_rules(tout); ); apply_default_transformation(m_ctx); if (m_ctx.get_params().xform_slice()) { datalog::rule_transformer transformer(m_ctx); datalog::mk_slice* slice = alloc(datalog::mk_slice, m_ctx); transformer.register_plugin(slice); m_ctx.transform_rules(transformer); // track sliced predicates. obj_map const& preds = slice->get_predicates(); obj_map::iterator it = preds.begin(); obj_map::iterator end = preds.end(); for (; it != end; ++it) { m_pred2slice.insert(it->m_key, it->m_value); m_refs.push_back(it->m_key); m_refs.push_back(it->m_value); } } if (m_ctx.get_params().xform_unfold_rules() > 0) { unsigned num_unfolds = m_ctx.get_params().xform_unfold_rules(); datalog::rule_transformer transf1(m_ctx), transf2(m_ctx); transf1.register_plugin(alloc(datalog::mk_coalesce, m_ctx)); transf2.register_plugin(alloc(datalog::mk_unfold, m_ctx)); if (m_ctx.get_params().xform_coalesce_rules()) { m_ctx.transform_rules(transf1); } while (num_unfolds > 0) { m_ctx.transform_rules(transf2); --num_unfolds; } } const datalog::rule_set& rules = m_ctx.get_rules(); if (rules.get_output_predicates().empty()) { m_context->set_unsat(); return l_false; } query_pred = rules.get_output_predicate(); IF_VERBOSE(2, m_ctx.display_rules(verbose_stream());); m_spacer_rules.replace_rules(rules); m_spacer_rules.close(); m_ctx.record_transformed_rules(); m_ctx.reopen(); m_ctx.replace_rules(old_rules); scoped_restore_proof _sc(m); // update_rules may overwrite the proof mode. m_context->set_proof_converter(m_ctx.get_proof_converter()); m_context->set_model_converter(m_ctx.get_model_converter()); m_context->set_query(query_pred); m_context->update_rules(m_spacer_rules); if (m_spacer_rules.get_rules().empty()) { m_context->set_unsat(); IF_VERBOSE(1, model_smt2_pp(verbose_stream(), m, *m_context->get_model(), 0);); return l_false; } return m_context->solve(lvl); } expr_ref dl_interface::get_cover_delta(int level, func_decl* pred_orig) { func_decl* pred = pred_orig; m_pred2slice.find(pred_orig, pred); SASSERT(pred); return m_context->get_cover_delta(level, pred_orig, pred); } void dl_interface::add_cover(int level, func_decl* pred, expr* property) { if (m_ctx.get_params().xform_slice()) { throw default_exception("Covers are incompatible with slicing. Disable slicing before using covers"); } m_context->add_cover(level, pred, property); } void dl_interface::add_invariant(func_decl* pred, expr* property) { if (m_ctx.get_params().xform_slice()) { throw default_exception("Invariants are incompatible with slicing. Disable slicing before using invariants"); } m_context->add_invariant(pred, property); } expr_ref dl_interface::get_reachable(func_decl* pred) { if (m_ctx.get_params().xform_slice()) { throw default_exception("Invariants are incompatible with slicing. " "Disable slicing before using invariants"); } return m_context->get_reachable(pred); } unsigned dl_interface::get_num_levels(func_decl* pred) { m_pred2slice.find(pred, pred); SASSERT(pred); return m_context->get_num_levels(pred); } void dl_interface::collect_statistics(statistics& st) const { m_context->collect_statistics(st); } void dl_interface::reset_statistics() { m_context->reset_statistics(); } void dl_interface::display_certificate(std::ostream& out) const { m_context->display_certificate(out); } expr_ref dl_interface::get_answer() { return m_context->get_answer(); } expr_ref dl_interface::get_ground_sat_answer() { return m_context->get_ground_sat_answer(); } void dl_interface::get_rules_along_trace(datalog::rule_ref_vector& rules) { m_context->get_rules_along_trace(rules); } void dl_interface::updt_params() { dealloc(m_context); m_context = alloc(spacer::context, m_ctx.get_params(), m_ctx.get_manager()); } model_ref dl_interface::get_model() { return m_context->get_model(); } proof_ref dl_interface::get_proof() { return m_context->get_proof(); } void dl_interface::add_callback(void *state, const datalog::t_new_lemma_eh new_lemma_eh, const datalog::t_predecessor_eh predecessor_eh, const datalog::t_unfold_eh unfold_eh){ m_context->callbacks().push_back(alloc(user_callback, *m_context, state, new_lemma_eh, predecessor_eh, unfold_eh)); } void dl_interface::add_constraint (expr *c, unsigned lvl){ m_context->add_constraint(c, lvl); } z3-z3-4.8.7/src/muz/spacer/spacer_dl_interface.h000066400000000000000000000037601356505360400214410ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation and Arie Gurfinkel Module Name: spacer_dl_interface.h Abstract: SMT2 interface for the datalog SPACER Author: Revision History: --*/ #ifndef _SPACER_DL_INTERFACE_H_ #define _SPACER_DL_INTERFACE_H_ #include "util/lbool.h" #include "muz/base/dl_rule.h" #include "muz/base/dl_rule_set.h" #include "muz/base/dl_engine_base.h" #include "util/statistics.h" namespace datalog { class context; } namespace spacer { class context; class dl_interface : public datalog::engine_base { datalog::context& m_ctx; datalog::rule_set m_spacer_rules; datalog::rule_set m_old_rules; context* m_context; obj_map m_pred2slice; ast_ref_vector m_refs; void check_reset(); public: dl_interface(datalog::context& ctx); ~dl_interface() override; lbool query(expr* query) override; lbool query_from_lvl(expr* query, unsigned lvl) override; void display_certificate(std::ostream& out) const override; void collect_statistics(statistics& st) const override; void reset_statistics() override; expr_ref get_answer() override; expr_ref get_ground_sat_answer() override; void get_rules_along_trace(datalog::rule_ref_vector& rules) override; unsigned get_num_levels(func_decl* pred) override; expr_ref get_cover_delta(int level, func_decl* pred) override; void add_cover(int level, func_decl* pred, expr* property) override; void add_invariant(func_decl* pred, expr* property) override; expr_ref get_reachable(func_decl *pred) override; void updt_params() override; model_ref get_model() override; proof_ref get_proof() override; void add_callback(void *state, const datalog::t_new_lemma_eh new_lemma_eh, const datalog::t_predecessor_eh predecessor_eh, const datalog::t_unfold_eh unfold_eh) override; void add_constraint (expr *c, unsigned lvl) override; }; } #endif z3-z3-4.8.7/src/muz/spacer/spacer_farkas_learner.cpp000066400000000000000000000334041356505360400223320ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: spacer_farkas_learner.cpp Abstract: Provides abstract interface and some implementations of algorithms for strenghtening lemmas Author: Krystof Hoder (t-khoder) 2011-11-1. Revision History: // TODO: what to write here --*/ //TODO: reorder, delete unnecessary includes #include "ast/ast_smt2_pp.h" #include "ast/array_decl_plugin.h" #include "ast/rewriter/bool_rewriter.h" #include "ast/dl_decl_plugin.h" #include "ast/for_each_expr.h" #include "muz/base/dl_util.h" #include "ast/rewriter/rewriter.h" #include "ast/rewriter/rewriter_def.h" #include "muz/spacer/spacer_util.h" #include "muz/spacer/spacer_farkas_learner.h" #include "ast/rewriter/th_rewriter.h" #include "ast/ast_ll_pp.h" #include "ast/proofs/proof_utils.h" #include "ast/reg_decl_plugins.h" #include "smt/smt_farkas_util.h" namespace spacer { class collect_pure_proc { func_decl_set& m_symbs; public: collect_pure_proc(func_decl_set& s): m_symbs(s) {} void operator()(app* a) { if (a->get_family_id() == null_family_id) { m_symbs.insert(a->get_decl()); } } void operator()(var*) {} void operator()(quantifier*) {} }; void farkas_learner::combine_constraints(unsigned n, app * const * lits, rational const * coeffs, expr_ref& res) { ast_manager& m = res.get_manager(); smt::farkas_util res_c(m); res_c.set_split_literals(m_split_literals); for (unsigned i = 0; i < n; ++i) { res_c.add(coeffs[i], lits[i]); } res = res_c.get(); } // every uninterpreted symbol is in symbs class is_pure_expr_proc { func_decl_set const& m_symbs; array_util m_au; public: struct non_pure {}; is_pure_expr_proc(func_decl_set const& s, ast_manager& m): m_symbs(s), m_au(m) {} void operator()(app* a) { if (a->get_family_id() == null_family_id) { if (!m_symbs.contains(a->get_decl())) { throw non_pure(); } } else if (a->get_family_id() == m_au.get_family_id() && a->is_app_of(a->get_family_id(), OP_ARRAY_EXT)) { throw non_pure(); } } void operator()(var*) {} void operator()(quantifier*) {} }; bool farkas_learner::is_pure_expr(func_decl_set const& symbs, expr* e, ast_manager& m) const { is_pure_expr_proc proc(symbs, m); try { for_each_expr(proc, e); } catch (const is_pure_expr_proc::non_pure &) { return false; } return true; }; /** Revised version of Farkas strengthener. 1. Mark B-pure nodes as derivations that depend only on B. 2. Collect B-influenced nodes 3. (optional) Permute B-pure units over resolution steps to narrow dependencies on B. 4. Weaken B-pure units for resolution with Farkas Clauses. 5. Add B-pure units elsewhere. Rules: - hypothesis h |- h H |- false - lemma ---------- |- not H Th |- L \/ C H |- not L - th-lemma ------------------------- H |- C Note: C is false for theory axioms, C is unit literal for propagation. - rewrite |- t = s H |- t = s - monotonicity ---------------- H |- f(t) = f(s) H |- t = s H' |- s = u - trans ---------------------- H, H' |- t = u H |- C \/ L H' |- not L - unit_resolve ------------------------ H, H' |- C H |- a ~ b H' |- a - mp -------------------- H, H' |- b - def-axiom |- C - asserted |- f Mark nodes by: - Hypotheses - Dependency on bs - Dependency on A A node is unit derivable from bs if: - It has no hypotheses. - It depends on bs. - It does not depend on A. NB: currently unit derivable is not symmetric: A clause can be unit derivable, but a unit literal with hypotheses is not. This is clearly wrong, because hypotheses are just additional literals in a clausal version. NB: the routine is not interpolating, though an interpolating variant would be preferable because then we can also use it for model propagation. We collect the unit derivable nodes from bs. These are the weakenings of bs, besides the units under Farkas. */ #define INSERT(_x_) if (!lemma_set.contains(_x_)) { lemma_set.insert(_x_); lemmas.push_back(_x_); } void farkas_learner::get_lemmas(proof* root, expr_set const& bs, expr_ref_vector& lemmas) { ast_manager& m = lemmas.get_manager(); bool_rewriter brwr(m); func_decl_set Bsymbs; collect_pure_proc collect_proc(Bsymbs); expr_set::iterator it = bs.begin(), end = bs.end(); for (; it != end; ++it) { for_each_expr(collect_proc, *it); } proof_ref pr(root, m); proof_utils::reduce_hypotheses(pr); proof_utils::permute_unit_resolution(pr); IF_VERBOSE(3, verbose_stream() << "Reduced proof:\n" << mk_ismt2_pp(pr, m) << "\n";); ptr_vector hyprefs; obj_map hypmap; obj_hashtable lemma_set; ast_mark b_depend, a_depend, visited, b_closed; expr_set* empty_set = alloc(expr_set); hyprefs.push_back(empty_set); ptr_vector todo; TRACE("spacer_verbose", tout << mk_pp(pr, m) << "\n";); todo.push_back(pr); while (!todo.empty()) { proof* p = todo.back(); SASSERT(m.is_proof(p)); if (visited.is_marked(p)) { todo.pop_back(); continue; } bool all_visit = true; for (unsigned i = 0; i < m.get_num_parents(p); ++i) { expr* arg = p->get_arg(i); SASSERT(m.is_proof(arg)); if (!visited.is_marked(arg)) { all_visit = false; todo.push_back(to_app(arg)); } } if (!all_visit) { continue; } visited.mark(p, true); todo.pop_back(); // retrieve hypotheses and dependencies on A, bs. bool b_dep = false, a_dep = false; expr_set* hyps = empty_set; for (unsigned i = 0; i < m.get_num_parents(p); ++i) { expr* arg = p->get_arg(i); a_dep = a_dep || a_depend.is_marked(arg); b_dep = b_dep || b_depend.is_marked(arg); expr_set* hyps2 = hypmap.find(arg); if (hyps != hyps2 && !hyps2->empty()) { if (hyps->empty()) { hyps = hyps2; } else { expr_set* hyps3 = alloc(expr_set); set_union(*hyps3, *hyps); set_union(*hyps3, *hyps2); hyps = hyps3; hyprefs.push_back(hyps); } } } hypmap.insert(p, hyps); a_depend.mark(p, a_dep); b_depend.mark(p, b_dep); #define IS_B_PURE(_p) (b_depend.is_marked(_p) && !a_depend.is_marked(_p) && hypmap.find(_p)->empty()) // Add lemmas that depend on bs, have no hypotheses, don't depend on A. if ((!hyps->empty() || a_depend.is_marked(p)) && b_depend.is_marked(p) && !is_farkas_lemma(m, p)) { for (unsigned i = 0; i < m.get_num_parents(p); ++i) { app* arg = to_app(p->get_arg(i)); if (IS_B_PURE(arg)) { expr* fact = m.get_fact(arg); if (is_pure_expr(Bsymbs, fact, m)) { TRACE("farkas_learner2", tout << "Add: " << mk_pp(m.get_fact(arg), m) << "\n"; tout << mk_pp(arg, m) << "\n"; ); INSERT(fact); } else { get_asserted(p, bs, b_closed, lemma_set, lemmas); b_closed.mark(p, true); } } } } switch (p->get_decl_kind()) { case PR_ASSERTED: if (bs.contains(m.get_fact(p))) { b_depend.mark(p, true); } else { a_depend.mark(p, true); } break; case PR_HYPOTHESIS: { SASSERT(hyps == empty_set); hyps = alloc(expr_set); hyps->insert(m.get_fact(p)); hyprefs.push_back(hyps); hypmap.insert(p, hyps); break; } case PR_DEF_AXIOM: { if (!is_pure_expr(Bsymbs, m.get_fact(p), m)) { a_depend.mark(p, true); } break; } case PR_LEMMA: { expr_set* hyps2 = alloc(expr_set); hyprefs.push_back(hyps2); set_union(*hyps2, *hyps); hyps = hyps2; expr* fml = m.get_fact(p); hyps->remove(fml); if (m.is_or(fml)) { for (unsigned i = 0; i < to_app(fml)->get_num_args(); ++i) { expr* f = to_app(fml)->get_arg(i); expr_ref hyp(m); brwr.mk_not(f, hyp); hyps->remove(hyp); } } hypmap.insert(p, hyps); break; } case PR_TH_LEMMA: { if (!is_farkas_lemma(m, p)) { break; } SASSERT(m.has_fact(p)); unsigned prem_cnt = m.get_num_parents(p); func_decl * d = p->get_decl(); SASSERT(d->get_num_parameters() >= prem_cnt + 2); SASSERT(d->get_parameter(0).get_symbol() == "arith"); SASSERT(d->get_parameter(1).get_symbol() == "farkas"); parameter const* params = d->get_parameters() + 2; app_ref_vector lits(m); expr_ref tmp(m); unsigned num_b_pures = 0; rational coef; vector coeffs; TRACE("farkas_learner2", for (unsigned i = 0; i < prem_cnt; ++i) { VERIFY(params[i].is_rational(coef)); proof* prem = to_app(p->get_arg(i)); bool b_pure = IS_B_PURE(prem); tout << (b_pure ? "B" : "A") << " " << coef << " " << mk_pp(m.get_fact(prem), m) << "\n"; } tout << mk_pp(m.get_fact(p), m) << "\n"; ); // NB. Taking 'abs' of coefficients is a workaround. // The Farkas coefficient extraction in arith_core must be wrong. // The coefficients would be always positive relative to the theory lemma. for (unsigned i = 0; i < prem_cnt; ++i) { expr * prem_e = p->get_arg(i); SASSERT(is_app(prem_e)); proof * prem = to_app(prem_e); if (IS_B_PURE(prem)) { ++num_b_pures; } else { VERIFY(params[i].is_rational(coef)); lits.push_back(to_app(m.get_fact(prem))); coeffs.push_back(abs(coef)); } } params += prem_cnt; if (prem_cnt + 2 < d->get_num_parameters()) { unsigned num_args = 1; expr* fact = m.get_fact(p); expr* const* args = &fact; if (m.is_or(fact)) { app* _or = to_app(fact); num_args = _or->get_num_args(); args = _or->get_args(); } SASSERT(prem_cnt + 2 + num_args == d->get_num_parameters()); for (unsigned i = 0; i < num_args; ++i) { expr* prem_e = args[i]; brwr.mk_not(prem_e, tmp); VERIFY(params[i].is_rational(coef)); SASSERT(is_app(tmp)); lits.push_back(to_app(tmp)); coeffs.push_back(abs(coef)); } } SASSERT(coeffs.size() == lits.size()); if (num_b_pures > 0) { expr_ref res(m); combine_constraints(coeffs.size(), lits.c_ptr(), coeffs.c_ptr(), res); TRACE("farkas_learner2", tout << "Add: " << mk_pp(res, m) << "\n";); INSERT(res); b_closed.mark(p, true); } } default: break; } } std::for_each(hyprefs.begin(), hyprefs.end(), delete_proc()); simplify_bounds(lemmas); } void farkas_learner::get_asserted(proof* p0, expr_set const& bs, ast_mark& b_closed, obj_hashtable& lemma_set, expr_ref_vector& lemmas) { ast_manager& m = lemmas.get_manager(); ast_mark visited; proof* p = p0; ptr_vector todo; todo.push_back(p); while (!todo.empty()) { p = todo.back(); todo.pop_back(); if (visited.is_marked(p) || b_closed.is_marked(p)) { continue; } visited.mark(p, true); for (unsigned i = 0; i < m.get_num_parents(p); ++i) { expr* arg = p->get_arg(i); SASSERT(m.is_proof(arg)); todo.push_back(to_app(arg)); } if (p->get_decl_kind() == PR_ASSERTED && bs.contains(m.get_fact(p))) { expr* fact = m.get_fact(p); TRACE("farkas_learner2", tout << mk_ll_pp(p0, m) << "\n"; tout << "Add: " << mk_pp(p, m) << "\n";); INSERT(fact); b_closed.mark(p, true); } } } bool farkas_learner::is_farkas_lemma(ast_manager& m, expr* e) { app * a; func_decl* d; symbol sym; return is_app(e) && (a = to_app(e), d = a->get_decl(), true) && PR_TH_LEMMA == a->get_decl_kind() && d->get_num_parameters() >= 2 && d->get_parameter(0).is_symbol(sym) && sym == "arith" && d->get_parameter(1).is_symbol(sym) && sym == "farkas" && d->get_num_parameters() >= m.get_num_parents(to_app(e)) + 2; } } z3-z3-4.8.7/src/muz/spacer/spacer_farkas_learner.h000066400000000000000000000023371356505360400220000ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: spacer_farkas_learner.h Abstract: SMT2 interface for the datalog SPACER Author: Krystof Hoder (t-khoder) 2011-11-1. Revision History: --*/ #ifndef _SPACER_FARKAS_LEARNER_H_ #define _SPACER_FARKAS_LEARNER_H_ #include "ast/ast.h" namespace spacer { class farkas_learner { typedef obj_hashtable expr_set; bool m_split_literals; void combine_constraints(unsigned cnt, app * const * constrs, rational const * coeffs, expr_ref& res); bool is_farkas_lemma(ast_manager& m, expr* e); void get_asserted(proof* p, expr_set const& bs, ast_mark& b_closed, obj_hashtable& lemma_set, expr_ref_vector& lemmas); bool is_pure_expr(func_decl_set const& symbs, expr* e, ast_manager& m) const; public: farkas_learner(): m_split_literals(false) {} /** Traverse a proof and retrieve lemmas using the vocabulary from bs. */ void get_lemmas(proof* root, expr_set const& bs, expr_ref_vector& lemmas); void collect_statistics(statistics& st) const {} void reset_statistics() {} /** \brief see smt::farkas_util::set_split_literals */ void set_split_literals(bool v) {m_split_literals = v;} }; } #endif z3-z3-4.8.7/src/muz/spacer/spacer_generalizers.cpp000066400000000000000000000241641356505360400220500ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation and Arie Gurfinkel Module Name: spacer_generalizers.cpp Abstract: Lemma generalizers. Author: Nikolaj Bjorner (nbjorner) 2011-11-20. Arie Gurfinkel Revision History: --*/ #include "muz/spacer/spacer_context.h" #include "muz/spacer/spacer_generalizers.h" #include "ast/ast_util.h" #include "ast/expr_abstract.h" #include "ast/rewriter/var_subst.h" #include "ast/for_each_expr.h" #include "ast/rewriter/factor_equivs.h" #include "ast/rewriter/expr_safe_replace.h" #include "ast/substitution/matcher.h" #include "ast/expr_functors.h" #include "smt/smt_solver.h" #include "qe/qe_term_graph.h" namespace spacer { void lemma_sanity_checker::operator()(lemma_ref &lemma) { unsigned uses_level; expr_ref_vector cube(lemma->get_ast_manager()); cube.append(lemma->get_cube()); ENSURE(lemma->get_pob()->pt().check_inductive(lemma->level(), cube, uses_level, lemma->weakness())); } namespace{ class contains_array_op_proc : public i_expr_pred { ast_manager &m; family_id m_array_fid; public: contains_array_op_proc(ast_manager &manager) : m(manager), m_array_fid(m.mk_family_id("array")) {} bool operator()(expr *e) override { return is_app(e) && to_app(e)->get_family_id() == m_array_fid; } }; } // ------------------------ // lemma_bool_inductive_generalizer /// Inductive generalization by dropping and expanding literals void lemma_bool_inductive_generalizer::operator()(lemma_ref &lemma) { if (lemma->get_cube().empty()) return; m_st.count++; scoped_watch _w_(m_st.watch); unsigned uses_level; pred_transformer &pt = lemma->get_pob()->pt(); ast_manager &m = pt.get_ast_manager(); contains_array_op_proc has_array_op(m); check_pred has_arrays(has_array_op, m); expr_ref_vector cube(m); cube.append(lemma->get_cube()); bool dirty = false; expr_ref true_expr(m.mk_true(), m); ptr_vector processed; expr_ref_vector extra_lits(m); unsigned weakness = lemma->weakness(); unsigned i = 0, num_failures = 0; while (i < cube.size() && (!m_failure_limit || num_failures < m_failure_limit)) { expr_ref lit(m); lit = cube.get(i); if (m_array_only && !has_arrays(lit)) { processed.push_back(lit); ++i; continue; } cube[i] = true_expr; if (cube.size() > 1 && pt.check_inductive(lemma->level(), cube, uses_level, weakness)) { num_failures = 0; dirty = true; for (i = 0; i < cube.size() && processed.contains(cube.get(i)); ++i); } else { // check if the literal can be expanded and any single // literal in the expansion can replace it extra_lits.reset(); extra_lits.push_back(lit); expand_literals(m, extra_lits); SASSERT(extra_lits.size() > 0); bool found = false; if (extra_lits.get(0) != lit && extra_lits.size() > 1) { for (unsigned j = 0, sz = extra_lits.size(); !found && j < sz; ++j) { cube[i] = extra_lits.get(j); if (pt.check_inductive(lemma->level(), cube, uses_level, weakness)) { num_failures = 0; dirty = true; found = true; processed.push_back(extra_lits.get(j)); for (i = 0; i < cube.size() && processed.contains(cube.get(i)); ++i); } } } if (!found) { cube[i] = lit; processed.push_back(lit); ++num_failures; ++m_st.num_failures; ++i; } } } if (dirty) { TRACE("spacer", tout << "Generalized from:\n" << mk_and(lemma->get_cube()) << "\ninto\n" << mk_and(cube) << "\n";); lemma->update_cube(lemma->get_pob(), cube); SASSERT(uses_level >= lemma->level()); lemma->set_level(uses_level); } } void lemma_bool_inductive_generalizer::collect_statistics(statistics &st) const { st.update("time.spacer.solve.reach.gen.bool_ind", m_st.watch.get_seconds()); st.update("bool inductive gen", m_st.count); st.update("bool inductive gen failures", m_st.num_failures); } void unsat_core_generalizer::operator()(lemma_ref &lemma) { m_st.count++; scoped_watch _w_(m_st.watch); ast_manager &m = lemma->get_ast_manager(); pred_transformer &pt = lemma->get_pob()->pt(); unsigned old_sz = lemma->get_cube().size(); unsigned old_level = lemma->level(); (void)old_level; unsigned uses_level; expr_ref_vector core(m); VERIFY(pt.is_invariant(lemma->level(), lemma.get(), uses_level, &core)); CTRACE("spacer", old_sz > core.size(), tout << "unsat core reduced lemma from: " << old_sz << " to " << core.size() << "\n";); CTRACE("spacer", old_level < uses_level, tout << "unsat core moved lemma up from: " << old_level << " to " << uses_level << "\n";); if (old_sz > core.size()) { lemma->update_cube(lemma->get_pob(), core); lemma->set_level(uses_level); } } void unsat_core_generalizer::collect_statistics(statistics &st) const { st.update("time.spacer.solve.reach.gen.unsat_core", m_st.watch.get_seconds()); st.update("gen.unsat_core.cnt", m_st.count); st.update("gen.unsat_core.fail", m_st.num_failures); } namespace { class collect_array_proc { array_util m_au; func_decl_set &m_symbs; sort *m_sort; public: collect_array_proc(ast_manager &m, func_decl_set& s) : m_au(m), m_symbs(s), m_sort(nullptr) {} void operator()(app* a) { if (a->get_family_id() == null_family_id && m_au.is_array(a)) { if (m_sort && m_sort != get_sort(a)) { return; } if (!m_sort) { m_sort = get_sort(a); } m_symbs.insert(a->get_decl()); } } void operator()(var*) {} void operator()(quantifier*) {} }; } bool lemma_array_eq_generalizer::is_array_eq (ast_manager &m, expr* e) { expr *e1 = nullptr, *e2 = nullptr; if (m.is_eq(e, e1, e2) && is_app(e1) && is_app(e2)) { app *a1 = to_app(e1); app *a2 = to_app(e2); array_util au(m); if (a1->get_family_id() == null_family_id && a2->get_family_id() == null_family_id && au.is_array(a1) && au.is_array(a2)) return true; } return false; } void lemma_array_eq_generalizer::operator() (lemma_ref &lemma) { ast_manager &m = lemma->get_ast_manager(); expr_ref_vector core(m); expr_ref v(m); func_decl_set symb; collect_array_proc cap(m, symb); // -- find array constants core.append (lemma->get_cube()); v = mk_and(core); for_each_expr(cap, v); CTRACE("core_array_eq", symb.size() > 1 && symb.size() <= 8, tout << "found " << symb.size() << " array variables in: \n" << v << "\n";); // too few constants or too many constants if (symb.size() <= 1 || symb.size() > 8) { return; } // -- for every pair of constants (A, B), check whether the // -- equality (A=B) generalizes a literal in the lemma ptr_vector vsymbs; for (auto * fdecl : symb) {vsymbs.push_back(fdecl);} // create all equalities expr_ref_vector eqs(m); for (unsigned i = 0, sz = vsymbs.size(); i < sz; ++i) { for (unsigned j = i + 1; j < sz; ++j) { eqs.push_back(m.mk_eq(m.mk_const(vsymbs.get(i)), m.mk_const(vsymbs.get(j)))); } } // smt-solver to check whether a literal is generalized. using // default params. There has to be a simpler way to approximate // this check ref sol = mk_smt_solver(m, params_ref::get_empty(), symbol::null); // literals of the new lemma expr_ref_vector lits(m); lits.append(core); expr *t = nullptr; bool dirty = false; for (unsigned i = 0, sz = core.size(); i < sz; ++i) { // skip a literal is it is already an array equality if (m.is_not(lits.get(i), t) && is_array_eq(m, t)) continue; solver::scoped_push _pp_(*sol); sol->assert_expr(lits.get(i)); for (auto *e : eqs) { solver::scoped_push _p_(*sol); sol->assert_expr(e); lbool res = sol->check_sat(0, nullptr); if (res == l_false) { TRACE("core_array_eq", tout << "strengthened " << mk_pp(lits.get(i), m) << " with " << mk_pp(mk_not(m, e), m) << "\n";); lits[i] = mk_not(m, e); dirty = true; break; } } } // nothing changed if (!dirty) return; TRACE("core_array_eq", tout << "new possible core " << mk_and(lits) << "\n";); pred_transformer &pt = lemma->get_pob()->pt(); // -- check if the generalized result is consistent with trans unsigned uses_level1; if (pt.check_inductive(lemma->level(), lits, uses_level1, lemma->weakness())) { TRACE("core_array_eq", tout << "Inductive!\n";); lemma->update_cube(lemma->get_pob(), lits); lemma->set_level(uses_level1); } else {TRACE("core_array_eq", tout << "Not-Inductive!\n";);} } void lemma_eq_generalizer::operator() (lemma_ref &lemma) { TRACE("core_eq", tout << "Transforming equivalence classes\n";); if (lemma->get_cube().empty()) return; ast_manager &m = m_ctx.get_ast_manager(); qe::term_graph egraph(m); egraph.add_lits(lemma->get_cube()); // -- expand the cube with all derived equalities expr_ref_vector core(m); egraph.to_lits(core, true); // -- if the core looks different from the original cube if (core.size() != lemma->get_cube().size() || core.get(0) != lemma->get_cube().get(0)) { // -- update the lemma lemma->update_cube(lemma->get_pob(), core); } } }; z3-z3-4.8.7/src/muz/spacer/spacer_generalizers.h000066400000000000000000000115231356505360400215100ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation and Arie Gurfinkel Module Name: spacer_generalizers.h Abstract: Generalizer plugins. Author: Nikolaj Bjorner (nbjorner) 2011-11-22. Arie Gurfinkel Revision History: --*/ #ifndef _SPACER_GENERALIZERS_H_ #define _SPACER_GENERALIZERS_H_ #include "ast/arith_decl_plugin.h" #include "muz/spacer/spacer_context.h" namespace spacer { // can be used to check whether produced core is really implied by // frame and therefore valid TODO: or negation? class lemma_sanity_checker : public lemma_generalizer { public: lemma_sanity_checker(context &ctx) : lemma_generalizer(ctx) {} ~lemma_sanity_checker() override {} void operator()(lemma_ref &lemma) override; }; /** * Boolean inductive generalization by dropping literals */ class lemma_bool_inductive_generalizer : public lemma_generalizer { struct stats { unsigned count; unsigned num_failures; stopwatch watch; stats() { reset(); } void reset() { count = 0; num_failures = 0; watch.reset(); } }; unsigned m_failure_limit; bool m_array_only; stats m_st; public: lemma_bool_inductive_generalizer(context &ctx, unsigned failure_limit, bool array_only = false) : lemma_generalizer(ctx), m_failure_limit(failure_limit), m_array_only(array_only) {} ~lemma_bool_inductive_generalizer() override {} void operator()(lemma_ref &lemma) override; void collect_statistics(statistics &st) const override; void reset_statistics() override { m_st.reset(); } }; class unsat_core_generalizer : public lemma_generalizer { struct stats { unsigned count; unsigned num_failures; stopwatch watch; stats() { reset(); } void reset() { count = 0; num_failures = 0; watch.reset(); } }; stats m_st; public: unsat_core_generalizer(context &ctx) : lemma_generalizer(ctx) {} ~unsat_core_generalizer() override {} void operator()(lemma_ref &lemma) override; void collect_statistics(statistics &st) const override; void reset_statistics() override { m_st.reset(); } }; class lemma_array_eq_generalizer : public lemma_generalizer { private: bool is_array_eq(ast_manager &m, expr *e); public: lemma_array_eq_generalizer(context &ctx) : lemma_generalizer(ctx) {} ~lemma_array_eq_generalizer() override {} void operator()(lemma_ref &lemma) override; }; class lemma_eq_generalizer : public lemma_generalizer { public: lemma_eq_generalizer(context &ctx) : lemma_generalizer(ctx) {} ~lemma_eq_generalizer() override {} void operator()(lemma_ref &lemma) override; }; class lemma_quantifier_generalizer : public lemma_generalizer { struct stats { unsigned count; unsigned num_failures; stopwatch watch; stats() { reset(); } void reset() { count = 0; num_failures = 0; watch.reset(); } }; ast_manager &m; arith_util m_arith; stats m_st; expr_ref_vector m_cube; bool m_normalize_cube; int m_offset; public: lemma_quantifier_generalizer(context &ctx, bool normalize_cube = true); ~lemma_quantifier_generalizer() override {} void operator()(lemma_ref &lemma) override; void collect_statistics(statistics &st) const override; void reset_statistics() override { m_st.reset(); } private: bool generalize(lemma_ref &lemma, app *term); void find_candidates(expr *e, app_ref_vector &candidate); bool is_ub(var *var, expr *e); bool is_lb(var *var, expr *e); void mk_abs_cube(lemma_ref &lemma, app *term, var *var, expr_ref_vector &gnd_cube, expr_ref_vector &abs_cube, expr *&lb, expr *&ub, unsigned &stride); bool match_sk_idx(expr *e, app_ref_vector const &zks, expr *&idx, app *&sk); void cleanup(expr_ref_vector &cube, app_ref_vector const &zks, expr_ref &bind); bool find_stride(expr_ref_vector &c, expr_ref &pattern, unsigned &stride); }; class limit_num_generalizer : public lemma_generalizer { struct stats { unsigned count; unsigned num_failures; stopwatch watch; stats() { reset(); } void reset() { count = 0; num_failures = 0; watch.reset(); } }; unsigned m_failure_limit; stats m_st; bool limit_denominators(expr_ref_vector &lits, rational &limit); public: limit_num_generalizer(context &ctx, unsigned failure_limit); ~limit_num_generalizer() override {} void operator()(lemma_ref &lemma) override; void collect_statistics(statistics &st) const override; void reset_statistics() override { m_st.reset(); } }; } // namespace spacer #endif z3-z3-4.8.7/src/muz/spacer/spacer_iuc_proof.cpp000066400000000000000000000201111356505360400213270ustar00rootroot00000000000000#include #include "ast/ast_pp_dot.h" #include "muz/spacer/spacer_iuc_proof.h" #include "ast/for_each_expr.h" #include "ast/array_decl_plugin.h" #include "ast/proofs/proof_utils.h" #include "muz/spacer/spacer_proof_utils.h" #include "muz/spacer/spacer_util.h" namespace spacer { /* * ==================================== * init * ==================================== */ iuc_proof::iuc_proof(ast_manager& m, proof* pr, const expr_set& core_lits) : m(m), m_pr(pr,m) { for (auto lit : core_lits) m_core_lits.insert(lit); // init A-marks and B-marks collect_core_symbols(); compute_marks(); } iuc_proof::iuc_proof(ast_manager& m, proof* pr, const expr_ref_vector& core_lits) : m(m), m_pr(pr,m) { for (auto lit : core_lits) m_core_lits.insert(lit); // init A-marks and B-marks collect_core_symbols(); compute_marks(); } /* * ==================================== * methods for computing symbol colors * ==================================== */ class collect_pure_proc { func_decl_set& m_symbs; public: collect_pure_proc(func_decl_set& s):m_symbs(s) {} void operator()(app* a) { if (a->get_family_id() == null_family_id) { m_symbs.insert(a->get_decl()); } } void operator()(var*) {} void operator()(quantifier*) {} }; void iuc_proof::collect_core_symbols() { expr_mark visited; collect_pure_proc proc(m_core_symbols); for (auto lit : m_core_lits) for_each_expr(proc, visited, lit); } class is_pure_expr_proc { func_decl_set const& m_symbs; array_util m_au; public: struct non_pure {}; is_pure_expr_proc(func_decl_set const& s, ast_manager& m): m_symbs(s), m_au (m) {} void operator()(app* a) { if (a->get_family_id() == null_family_id) { if (!m_symbs.contains(a->get_decl())) { throw non_pure(); } } else if (a->get_family_id () == m_au.get_family_id () && a->is_app_of (a->get_family_id (), OP_ARRAY_EXT)) { throw non_pure(); } } void operator()(var*) {} void operator()(quantifier*) {} }; bool iuc_proof::is_core_pure(expr* e) const { is_pure_expr_proc proc(m_core_symbols, m); try { for_each_expr(proc, e); } catch (const is_pure_expr_proc::non_pure &) {return false;} return true; } void iuc_proof::compute_marks() { proof_post_order it(m_pr, m); while (it.hasNext()) { proof* cur = it.next(); if (m.get_num_parents(cur) == 0) { switch(cur->get_decl_kind()) { case PR_ASSERTED: if (m_core_lits.contains(m.get_fact(cur))) m_b_mark.mark(cur, true); else m_a_mark.mark(cur, true); break; case PR_HYPOTHESIS: m_h_mark.mark(cur, true); break; default: break; } } else { // collect from parents whether derivation of current node // contains A-axioms, B-axioms and hypothesis bool need_to_mark_a = false; bool need_to_mark_b = false; bool need_to_mark_h = false; for (unsigned i = 0; i < m.get_num_parents(cur); ++i) { SASSERT(m.is_proof(cur->get_arg(i))); proof* premise = to_app(cur->get_arg(i)); need_to_mark_a |= m_a_mark.is_marked(premise); need_to_mark_b |= m_b_mark.is_marked(premise); need_to_mark_h |= m_h_mark.is_marked(premise); } // if current node is application of a lemma, then all // active hypotheses are removed if (cur->get_decl_kind() == PR_LEMMA) need_to_mark_h = false; // save results m_a_mark.mark(cur, need_to_mark_a); m_b_mark.mark(cur, need_to_mark_b); m_h_mark.mark(cur, need_to_mark_h); } } } /* * ==================================== * statistics * ==================================== */ // debug method void iuc_proof::dump_farkas_stats() { unsigned fl_total = 0; unsigned fl_lowcut = 0; proof_post_order it(m_pr, m); while (it.hasNext()) { proof* cur = it.next(); // if node is theory lemma if (is_farkas_lemma(m, cur)) { fl_total++; // check whether farkas lemma is to be interpolated (could // potentially miss farkas lemmas, which are interpolated, // because we potentially don't want to use the lowest // cut) bool has_blue_nonred_parent = false; for (unsigned i = 0; i < m.get_num_parents(cur); ++i) { proof* premise = to_app(cur->get_arg(i)); if (!is_a_marked(premise) && is_b_marked(premise)) { has_blue_nonred_parent = true; break; } } if (has_blue_nonred_parent && is_a_marked(cur)) { SASSERT(is_b_marked(cur)); fl_lowcut++; } } } IF_VERBOSE(1, verbose_stream() << "\n total farkas lemmas " << fl_total << " farkas lemmas in lowest cut " << fl_lowcut << "\n";); } void iuc_proof::display_dot(std::ostream& out) { out << "digraph proof { \n"; std::unordered_map ids; unsigned last_id = 0; proof_post_order it(m_pr, m); while (it.hasNext()) { proof* curr = it.next(); SASSERT(ids.count(curr->get_id()) == 0); ids.insert(std::make_pair(curr->get_id(), last_id)); std::string color = "white"; if (this->is_a_marked(curr) && !this->is_b_marked(curr)) color = "red"; else if (!this->is_a_marked(curr) && this->is_b_marked(curr)) color = "blue"; else if (this->is_a_marked(curr) && this->is_b_marked(curr) ) color = "purple"; // compute node label std::ostringstream label_ostream; label_ostream << mk_epp(m.get_fact(curr), m) << "\n"; std::string label = escape_dot(label_ostream.str()); // compute edge-label std::string edge_label = ""; if (m.get_num_parents(curr) == 0) { switch (curr->get_decl_kind()) { case PR_ASSERTED: edge_label = "asserted:"; break; case PR_HYPOTHESIS: edge_label = "hyp:"; color = "grey"; break; case PR_TH_LEMMA: if (is_farkas_lemma(m, curr)) edge_label = "th_axiom(farkas):"; else if (is_arith_lemma(m, curr)) edge_label = "th_axiom(arith):"; else edge_label = "th_axiom:"; break; default: edge_label = "unknown axiom:"; } } else { if (curr->get_decl_kind() == PR_LEMMA) edge_label = "lemma:"; else if (curr->get_decl_kind() == PR_TH_LEMMA) { if (is_farkas_lemma(m, curr)) edge_label = "th_lemma(farkas):"; else if (is_arith_lemma(m, curr)) edge_label = "th_lemma(arith):"; else edge_label = "th_lemma(other):"; } } // generate entry for node in dot-file out << "node_" << last_id << " " << "[" << "shape=box,style=\"filled\"," << "label=\"" << edge_label << " " << label << "\", " << "fillcolor=\"" << color << "\"" << "]\n"; // add entry for each edge to that node for (unsigned i = m.get_num_parents(curr); i > 0 ; --i) { proof* premise = to_app(curr->get_arg(i-1)); unsigned pid = ids.at(premise->get_id()); out << "node_" << pid << " -> " << "node_" << last_id << ";\n"; } ++last_id; } out << "\n}" << std::endl; } } z3-z3-4.8.7/src/muz/spacer/spacer_iuc_proof.h000066400000000000000000000033561356505360400210100ustar00rootroot00000000000000#ifndef IUC_PROOF_H_ #define IUC_PROOF_H_ #include #include "ast/ast.h" namespace spacer { typedef obj_hashtable expr_set; typedef obj_hashtable func_decl_set; /* * An iuc_proof is a proof together with information of the * coloring of the axioms. */ class iuc_proof { public: // Constructs an iuc_proof given an ast_manager, a proof, and a set of // literals core_lits that might be included in the unsat core iuc_proof(ast_manager& m, proof* pr, const expr_set& core_lits); iuc_proof(ast_manager& m, proof* pr, const expr_ref_vector &core_lits); // returns the proof object proof* get() {return m_pr.get();} // returns true if all uninterpreted symbols of e are from the core literals // requires that m_core_symbols has already been computed bool is_core_pure(expr* e) const; bool is_a_marked(proof* p) {return m_a_mark.is_marked(p);} bool is_b_marked(proof* p) {return m_b_mark.is_marked(p);} bool is_h_marked(proof* p) {return m_h_mark.is_marked(p);} bool is_b_pure (proof *p) { return !is_h_marked (p) && !this->is_a_marked(p) && is_core_pure(m.get_fact (p)); } void display_dot(std::ostream &out); // debug method void dump_farkas_stats(); private: ast_manager& m; proof_ref m_pr; ast_mark m_a_mark; ast_mark m_b_mark; ast_mark m_h_mark; // -- literals that are part of the core expr_set m_core_lits; // symbols that occur in any literals in the core func_decl_set m_core_symbols; // collect symbols occurring in B (the core) void collect_core_symbols(); // compute for each formula of the proof whether it derives // from A or from B void compute_marks(); }; } #endif /* IUC_PROOF_H_ */ z3-z3-4.8.7/src/muz/spacer/spacer_iuc_solver.cpp000066400000000000000000000361451356505360400215320ustar00rootroot00000000000000/** Copyright (c) 2017 Arie Gurfinkel Module Name: spacer_iuc_solver.cpp Abstract: A solver that produces interpolated unsat cores (IUCs) Author: Arie Gurfinkel Notes: --*/ #include"muz/spacer/spacer_iuc_solver.h" #include"ast/ast.h" #include"muz/spacer/spacer_util.h" #include"ast/proofs/proof_utils.h" #include"muz/spacer/spacer_farkas_learner.h" #include"ast/rewriter/expr_replacer.h" #include"muz/spacer/spacer_unsat_core_learner.h" #include"muz/spacer/spacer_unsat_core_plugin.h" #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 (); } } 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); } def_manager &def = !m_defs.empty() ? 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; } 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) { 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)); } 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)); } // 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 { // 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(); } } // -- new hypothesis reducer else { #if 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; { 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";); } 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)); } } } z3-z3-4.8.7/src/muz/spacer/spacer_iuc_solver.h000066400000000000000000000141361356505360400211730ustar00rootroot00000000000000/** Copyright (c) 2017 Arie Gurfinkel Module Name: spacer_iuc_solver.h Abstract: A solver that produces interpolated unsat cores Author: Arie Gurfinkel Notes: --*/ #ifndef SPACER_IUC_SOLVER_H_ #define SPACER_IUC_SOLVER_H_ #include"solver/solver.h" #include"ast/expr_substitution.h" #include"util/stopwatch.h" namespace spacer { class iuc_solver : public solver { private: struct def_manager { iuc_solver & m_parent; expr_ref_vector m_defs; obj_map m_expr2proxy; obj_map m_proxy2def; def_manager(iuc_solver &parent) : m_parent(parent), m_defs(m_parent.m) {} bool is_proxy(app *k, app_ref &v); app* mk_proxy(expr *v); void reset(); bool is_proxy_def(expr *v); }; friend struct def_manager; ast_manager& m; solver& m_solver; app_ref_vector m_proxies; unsigned m_num_proxies; vector m_defs; def_manager m_base_defs; expr_ref_vector m_assumptions; unsigned m_first_assumption; bool m_is_proxied; stopwatch m_iuc_sw; stopwatch m_hyp_reduce1_sw; stopwatch m_hyp_reduce2_sw; stopwatch m_learn_core_sw; expr_substitution m_elim_proxies_sub; bool m_split_literals; unsigned m_iuc; unsigned m_iuc_arith; bool m_print_farkas_stats; bool m_old_hyp_reducer; bool is_proxy(expr *e, app_ref &def); void undo_proxies_in_core(expr_ref_vector &v); app* mk_proxy(expr *v); app* fresh_proxy(); void elim_proxies(expr_ref_vector &v); public: iuc_solver(solver &solver, unsigned iuc, unsigned iuc_arith, bool print_farkas_stats, bool old_hyp_reducer, bool split_literals = false) : m(solver.get_manager()), m_solver(solver), m_proxies(m), m_num_proxies(0), m_base_defs(*this), m_assumptions(m), m_first_assumption(0), m_is_proxied(false), m_elim_proxies_sub(m, false, true), m_split_literals(split_literals), m_iuc(iuc), m_iuc_arith(iuc_arith), m_print_farkas_stats(print_farkas_stats), m_old_hyp_reducer(old_hyp_reducer) {} ~iuc_solver() override {} /* iuc solver specific */ virtual void get_iuc(expr_ref_vector &core); void set_split_literals(bool v) { m_split_literals = v; } bool mk_proxies(expr_ref_vector &v, unsigned from = 0); void undo_proxies(expr_ref_vector &v); void push_bg(expr *e); void pop_bg(unsigned n); unsigned get_num_bg(); void get_full_unsat_core(ptr_vector &core) { expr_ref_vector _core(m); m_solver.get_unsat_core(_core); core.append(_core.size(), _core.c_ptr()); } /* solver interface */ solver* translate(ast_manager &m, params_ref const &p) override { return m_solver.translate(m, p); } void updt_params(params_ref const &p) override { m_solver.updt_params(p); } void reset_params(params_ref const &p) override { m_solver.reset_params(p); } const params_ref &get_params() const override { return m_solver.get_params(); } void push_params() override { m_solver.push_params(); } void pop_params() override { m_solver.pop_params(); } void collect_param_descrs(param_descrs &r) override { m_solver.collect_param_descrs(r); } void set_produce_models(bool f) override { m_solver.set_produce_models(f); } 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 push() override; void pop(unsigned n) override; unsigned get_scope_level() const override { return m_solver.get_scope_level(); } 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); } unsigned get_num_assertions() const override { return m_solver.get_num_assertions(); } expr * get_assertion(unsigned idx) const override { return m_solver.get_assertion(idx); } unsigned get_num_assumptions() const override { return m_solver.get_num_assumptions(); } expr * get_assumption(unsigned idx) const override { return m_solver.get_assumption(idx); } std::ostream &display(std::ostream &out, unsigned n, expr* const* es) const override { return m_solver.display(out, n, es); } /* check_sat_result interface */ void collect_statistics(statistics &st) const override ; virtual void reset_statistics(); void get_unsat_core(expr_ref_vector &r) override; void get_model_core(model_ref &m) override {m_solver.get_model(m);} proof *get_proof() override {return m_solver.get_proof();} std::string reason_unknown() const override { return m_solver.reason_unknown(); } void set_reason_unknown(char const* msg) override { m_solver.set_reason_unknown(msg); } void get_labels(svector &r) override { m_solver.get_labels(r); } ast_manager& get_manager() const override { return m; } virtual void refresh(); class scoped_mk_proxy { iuc_solver &m_s; expr_ref_vector &m_v; public: scoped_mk_proxy(iuc_solver &s, expr_ref_vector &v) : m_s(s), m_v(v) { m_s.mk_proxies(m_v); } ~scoped_mk_proxy() { m_s.undo_proxies(m_v); } }; class scoped_bg { iuc_solver &m_s; unsigned m_bg_sz; public: scoped_bg(iuc_solver &s) : m_s(s), m_bg_sz(m_s.get_num_bg()) {} ~scoped_bg() { if (m_s.get_num_bg() > m_bg_sz) { m_s.pop_bg(m_s.get_num_bg() - m_bg_sz); } } }; }; } #endif z3-z3-4.8.7/src/muz/spacer/spacer_json.cpp000066400000000000000000000124251356505360400203240ustar00rootroot00000000000000/**++ Copyright (c) 2017 Microsoft Corporation and Matteo Marescotti Module Name: spacer_json.cpp Abstract: SPACER json marshalling support Author: Matteo Marescotti Notes: --*/ #include #include "spacer_context.h" #include "spacer_json.h" #include "spacer_util.h" namespace spacer { static std::ostream &json_marshal(std::ostream &out, ast *t, ast_manager &m) { mk_epp pp = mk_epp(t, m); std::ostringstream ss; ss << pp; out << "\""; for (auto &c:ss.str()) { switch (c) { case '"': out << "\\\""; break; case '\\': out << "\\\\"; break; case '\b': out << "\\b"; break; case '\f': out << "\\f"; break; case '\n': out << "\\n"; break; case '\r': out << "\\r"; break; case '\t': out << "\\t"; break; default: if ('\x00' <= c && c <= '\x1f') { out << "\\u" << std::hex << std::setw(4) << std::setfill('0') << (int) c; } else { out << c; } } } out << "\""; return out; } static std::ostream &json_marshal(std::ostream &out, lemma *l) { out << "{" << R"("init_level":")" << l->init_level() << R"(", "level":")" << l->level() << R"(", "expr":)"; json_marshal(out, l->get_expr(), l->get_ast_manager()); out << "}"; return out; } static std::ostream &json_marshal(std::ostream &out, const lemma_ref_vector &lemmas) { std::ostringstream ls; for (auto l:lemmas) { ls << ((unsigned)ls.tellp() == 0 ? "" : ","); json_marshal(ls, l); } out << "[" << ls.str() << "]"; return out; } void json_marshaller::register_lemma(lemma *l) { if (l->has_pob()) { m_relations[&*l->get_pob()][l->get_pob()->depth()].push_back(l); } } void json_marshaller::register_pob(pob *p) { m_relations[p]; } void json_marshaller::marshal_lemmas_old(std::ostream &out) const { unsigned pob_id = 0; for (auto &pob_map:m_relations) { std::ostringstream pob_lemmas; for (auto &depth_lemmas : pob_map.second) { pob_lemmas << ((unsigned)pob_lemmas.tellp() == 0 ? "" : ",") << "\"" << depth_lemmas.first << "\":"; json_marshal(pob_lemmas, depth_lemmas.second); } if (pob_lemmas.tellp()) { out << ((unsigned)out.tellp() == 0 ? "" : ",\n"); out << "\"" << pob_id << "\":{" << pob_lemmas.str() << "}"; } pob_id++; } } void json_marshaller::marshal_lemmas_new(std::ostream &out) const { unsigned pob_id = 0; for (auto &pob_map:m_relations) { std::ostringstream pob_lemmas; pob *n = pob_map.first; unsigned i = 0; for (auto *l : n->lemmas()) { pob_lemmas << ((unsigned)pob_lemmas.tellp() == 0 ? "" : ",") << "\"" << i++ << "\":"; lemma_ref_vector lemmas_vec; lemmas_vec.push_back(l); json_marshal(pob_lemmas, lemmas_vec); } if (pob_lemmas.tellp()) { out << ((unsigned)out.tellp() == 0 ? "" : ",\n"); out << "\"" << pob_id << "\":{" << pob_lemmas.str() << "}"; } pob_id++; } } std::ostream &json_marshaller::marshal(std::ostream &out) const { std::ostringstream nodes; std::ostringstream edges; std::ostringstream lemmas; if (m_old_style) marshal_lemmas_old(lemmas); else marshal_lemmas_new(lemmas); unsigned pob_id = 0; unsigned depth = 0; while (true) { double root_expand_time = m_ctx->get_root().get_expand_time(depth); bool a = false; pob_id = 0; for (auto &pob_map:m_relations) { pob *n = pob_map.first; double expand_time = n->get_expand_time(depth); if (expand_time > 0) { a = true; std::ostringstream pob_expr; json_marshal(pob_expr, n->post(), n->get_ast_manager()); nodes << ((unsigned)nodes.tellp() == 0 ? "" : ",\n") << "{\"id\":\"" << depth << n << "\",\"relative_time\":\"" << expand_time / root_expand_time << "\",\"absolute_time\":\"" << std::setprecision(2) << expand_time << "\",\"predicate\":\"" << n->pt().head()->get_name() << "\",\"expr_id\":\"" << n->post()->get_id() << "\",\"pob_id\":\"" << pob_id << "\",\"depth\":\"" << depth << "\",\"expr\":" << pob_expr.str() << "}"; if (n->parent()) { edges << ((unsigned)edges.tellp() == 0 ? "" : ",\n") << "{\"from\":\"" << depth << n->parent() << "\",\"to\":\"" << depth << n << "\"}"; } } pob_id++; } if (!a) { break; } depth++; } out << "{\n\"nodes\":[\n" << nodes.str() << "\n],\n"; out << "\"edges\":[\n" << edges.str() << "\n],\n"; out << "\"lemmas\":{\n" << lemmas.str() << "\n}\n}\n"; return out; } } z3-z3-4.8.7/src/muz/spacer/spacer_json.h000066400000000000000000000017321356505360400177700ustar00rootroot00000000000000/**++ Copyright (c) 2017 Microsoft Corporation and Matteo Marescotti Module Name: spacer_json.h Abstract: SPACER json marshalling support Author: Matteo Marescotti Notes: --*/ #ifndef Z3_SPACER_JSON_H #define Z3_SPACER_JSON_H #include #include #include "util/ref.h" #include "util/ref_vector.h" class ast; class ast_manager; namespace spacer { class lemma; typedef sref_vector lemma_ref_vector; class context; class pob; class json_marshaller { context *m_ctx; bool m_old_style; std::map> m_relations; void marshal_lemmas_old(std::ostream &out) const; void marshal_lemmas_new(std::ostream &out) const; public: json_marshaller(context *ctx, bool old_style = false) : m_ctx(ctx), m_old_style(old_style) {} void register_lemma(lemma *l); void register_pob(pob *p); std::ostream &marshal(std::ostream &out) const; }; } #endif //Z3_SPACER_JSON_H z3-z3-4.8.7/src/muz/spacer/spacer_legacy_frames.cpp000066400000000000000000000125521356505360400221550ustar00rootroot00000000000000/* Copyright (c) 2017 Arie Gurfinkel Legacy implementations of frames. To be removed. */ #include #include #include "muz/spacer/spacer_context.h" #include "muz/base/dl_util.h" #include "ast/rewriter/rewriter.h" #include "ast/rewriter/rewriter_def.h" #include "ast/rewriter/var_subst.h" #include "util/util.h" #include "muz/spacer/spacer_prop_solver.h" #include "muz/spacer/spacer_context.h" #include "muz/spacer/spacer_generalizers.h" #include "ast/for_each_expr.h" #include "muz/base/dl_rule_set.h" #include "smt/tactic/unit_subsumption_tactic.h" #include "model/model_smt2_pp.h" #include "muz/transforms/dl_mk_rule_inliner.h" #include "ast/ast_smt2_pp.h" #include "ast/ast_ll_pp.h" #include "ast/ast_util.h" #include "ast/proofs/proof_checker.h" #include "smt/smt_value_sort.h" #include "ast/proofs/proof_utils.h" #include "ast/scoped_proof.h" #include "muz/spacer/spacer_qe_project.h" #include "tactic/core/blast_term_ite_tactic.h" #include "util/timeit.h" #include "util/luby.h" #include "ast/rewriter/expr_safe_replace.h" #include "ast/expr_abstract.h" #include "ast/rewriter/factor_equivs.h" namespace spacer { // ------------------ // legacy_frames void pred_transformer::legacy_frames::simplify_formulas(tactic& tac, expr_ref_vector& v) { ast_manager &m = m_pt.get_ast_manager(); goal_ref g(alloc(goal, m, false, false, false)); for (unsigned j = 0; j < v.size(); ++j) { g->assert_expr(v[j].get()); } goal_ref_buffer result; tac(g, result); SASSERT(result.size() == 1); goal* r = result[0]; v.reset(); for (unsigned j = 0; j < r->size(); ++j) { v.push_back(r->form(j)); } } void pred_transformer::legacy_frames::simplify_formulas() { ast_manager &m = m_pt.get_ast_manager(); tactic_ref us = mk_unit_subsumption_tactic(m); simplify_formulas(*us, m_invariants); for (unsigned i = 0; i < m_levels.size(); ++i) { simplify_formulas(*us, m_levels[i]); } } void pred_transformer::legacy_frames::get_frame_geq_lemmas(unsigned lvl, expr_ref_vector &out) { get_frame_lemmas(infty_level(), out); for (unsigned i = lvl, sz = m_levels.size(); i < sz; ++i) { get_frame_lemmas(i, out); } } bool pred_transformer::legacy_frames::propagate_to_next_level(unsigned src_level) { ast_manager &m = m_pt.get_ast_manager(); (void) m; if (m_levels.size() <= src_level) { return true; } if (m_levels [src_level].empty()) { return true; } unsigned tgt_level = next_level(src_level); m_pt.ensure_level(next_level(tgt_level)); TRACE("spacer", tout << "propagating " << src_level << " to " << tgt_level; tout << " for relation " << m_pt.head()->get_name() << "\n";); for (unsigned i = 0; i < m_levels[src_level].size();) { expr_ref_vector &src = m_levels[src_level]; expr * curr = src[i].get(); unsigned stored_lvl; VERIFY(m_prop2level.find(curr, stored_lvl)); SASSERT(stored_lvl >= src_level); unsigned solver_level; if (stored_lvl > src_level) { TRACE("spacer", tout << "at level: " << stored_lvl << " " << mk_pp(curr, m) << "\n";); src[i] = src.back(); src.pop_back(); } else if (m_pt.is_invariant(tgt_level, curr, solver_level)) { // -- might invalidate src reference add_lemma(curr, solver_level); TRACE("spacer", tout << "is invariant: " << pp_level(solver_level) << " " << mk_pp(curr, m) << "\n";); // shadow higher-level src expr_ref_vector &src = m_levels[src_level]; src[i] = src.back(); src.pop_back(); ++m_pt.m_stats.m_num_propagations; } else { TRACE("spacer", tout << "not propagated: " << mk_pp(curr, m) << "\n";); ++i; } } CTRACE("spacer", m_levels[src_level].empty(), tout << "Fully propagated level " << src_level << " of " << m_pt.head()->get_name() << "\n";); return m_levels[src_level].empty(); } bool pred_transformer::legacy_frames::add_lemma(expr * lemma, unsigned lvl) { if (is_infty_level(lvl)) { if (!m_invariants.contains(lemma)) { m_invariants.push_back(lemma); m_prop2level.insert(lemma, lvl); //m_pt.add_lemma_core (lemma, lvl); return true; } return false; } unsigned old_level; if (!m_prop2level.find(lemma, old_level) || old_level < lvl) { m_levels[lvl].push_back(lemma); m_prop2level.insert(lemma, lvl); //m_pt.add_lemma_core (lemma, lvl); return true; } return false; } void pred_transformer::legacy_frames::propagate_to_infinity(unsigned level) { TRACE("spacer", tout << "propagating to oo from lvl " << level << " of " << m_pt.m_head->get_name() << "\n";); if (m_levels.empty()) { return; } for (unsigned i = m_levels.size(); i > level; --i) { expr_ref_vector &lemmas = m_levels [i - 1]; for (unsigned j = 0; j < lemmas.size(); ++j) { add_lemma(lemmas.get(j), infty_level()); } lemmas.reset(); } } void pred_transformer::legacy_frames::inherit_frames(legacy_frames& other) { SASSERT(m_pt.m_head == other.m_pt.m_head); obj_map::iterator it = other.m_prop2level.begin(); obj_map::iterator end = other.m_prop2level.end(); for (; it != end; ++it) { add_lemma(it->m_key, it->m_value); } } } z3-z3-4.8.7/src/muz/spacer/spacer_legacy_frames.h000066400000000000000000000026071356505360400216220ustar00rootroot00000000000000/**++ Copyright (c) 2017 Arie Gurfinkel Legacy implementations of frames. To be removed. Notes: this file is included from the middle of spacer_context.h */ class legacy_frames { pred_transformer &m_pt; /// level formulas vector m_levels; /// map property to level where it occurs. obj_map m_prop2level; /// properties that are invariant. expr_ref_vector m_invariants; void simplify_formulas (tactic& tac, expr_ref_vector& v); public: legacy_frames (pred_transformer &pt) : m_pt(pt), m_invariants (m_pt.get_ast_manager ()) {} pred_transformer& pt () const {return m_pt;} bool add_lemma (expr * lemma, unsigned level); void get_frame_lemmas (unsigned level, expr_ref_vector &out) { if(is_infty_level(level)) { out.append(m_invariants); } else if(level < m_levels.size()) { out.append(m_levels [level]); } } void get_frame_geq_lemmas (unsigned level, expr_ref_vector &out); void add_frame () {m_levels.push_back (expr_ref_vector (m_pt.get_ast_manager ()));} unsigned size () const {return m_levels.size ();} unsigned lemma_size () const {return m_prop2level.size ();} void propagate_to_infinity (unsigned level); bool propagate_to_next_level (unsigned level); void simplify_formulas (); void inherit_frames (legacy_frames& other); }; z3-z3-4.8.7/src/muz/spacer/spacer_legacy_mbp.cpp000066400000000000000000000063221356505360400214540ustar00rootroot00000000000000/** Copyright (c) 2017 Arie Gurfinkel Module Name: spacer_legacy_mbp.cpp Abstract: Legacy Model Based Projection. Used by Grigory Fedyukovich Author: Arie Gurfinkel Anvesh Komuravelli Notes: --*/ #include #include "ast/array_decl_plugin.h" #include "ast/ast_pp.h" #include "ast/rewriter/bool_rewriter.h" #include "muz/base/dl_util.h" #include "ast/for_each_expr.h" #include "smt/params/smt_params.h" #include "model/model.h" #include "util/ref_vector.h" #include "ast/rewriter/rewriter.h" #include "ast/rewriter/rewriter_def.h" #include "util/util.h" #include "muz/spacer/spacer_manager.h" #include "muz/spacer/spacer_util.h" #include "ast/arith_decl_plugin.h" #include "ast/rewriter/expr_replacer.h" #include "model/model_smt2_pp.h" #include "ast/scoped_proof.h" #include "qe/qe_lite.h" #include "muz/spacer/spacer_qe_project.h" #include "model/model_pp.h" #include "ast/rewriter/expr_safe_replace.h" #include "ast/datatype_decl_plugin.h" #include "ast/bv_decl_plugin.h" #include "muz/spacer/spacer_legacy_mev.h" namespace spacer { void qe_project(ast_manager& m, app_ref_vector& vars, expr_ref& fml, model_ref& M, expr_map& map) { th_rewriter rw(m); // qe-lite; TODO: use qe_lite aggressively params_ref p; qe_lite qe(m, p, true); qe(vars, fml); rw(fml); TRACE("spacer", tout << "After qe_lite:\n"; tout << mk_pp(fml, m) << "\n"; tout << "Vars:\n"; for (unsigned i = 0; i < vars.size(); ++i) { tout << mk_pp(vars.get(i), m) << "\n"; } ); // substitute model values for booleans and // use LW projection for arithmetic variables if (!vars.empty()) { app_ref_vector arith_vars(m); expr_substitution sub(m); proof_ref pr(m.mk_asserted(m.mk_true()), m); expr_ref bval(m); model::scoped_model_completion _scm(*M, true); for (unsigned i = 0; i < vars.size(); i++) { if (m.is_bool(vars.get(i))) { // obtain the interpretation of the ith var using model completion bval = (*M)(vars.get(i)); sub.insert(vars.get(i), bval, pr); } else { arith_vars.push_back(vars.get(i)); } } if (!sub.empty()) { scoped_ptr rep = mk_expr_simp_replacer(m); rep->set_substitution(&sub); (*rep)(fml); rw(fml); TRACE("spacer", tout << "Projected Boolean vars:\n" << mk_pp(fml, m) << "\n"; ); } // model based projection if (!arith_vars.empty()) { TRACE("spacer", tout << "Arith vars:\n"; for (unsigned i = 0; i < arith_vars.size(); ++i) { tout << mk_pp(arith_vars.get(i), m) << "\n"; } ); { scoped_no_proof _sp(m); spacer_qe::arith_project(*M, arith_vars, fml, map); } SASSERT(arith_vars.empty()); TRACE("spacer", tout << "Projected arith vars:\n" << mk_pp(fml, m) << "\n"; ); } SASSERT(M->is_true(fml)); vars.reset(); vars.append(arith_vars); } } } z3-z3-4.8.7/src/muz/spacer/spacer_legacy_mev.cpp000066400000000000000000000575501356505360400214760ustar00rootroot00000000000000/** Copyright (c) 2017 Arie Gurfinkel Deprecated implementation of model evaluator. To be removed. */ #include #include "ast/array_decl_plugin.h" #include "ast/ast_pp.h" #include "ast/rewriter/bool_rewriter.h" #include "muz/base/dl_util.h" #include "ast/for_each_expr.h" #include "smt/params/smt_params.h" #include "model/model.h" #include "util/ref_vector.h" #include "ast/rewriter/rewriter.h" #include "ast/rewriter/rewriter_def.h" #include "util/util.h" #include "muz/spacer/spacer_manager.h" #include "muz/spacer/spacer_legacy_mev.h" #include "muz/spacer/spacer_util.h" #include "ast/arith_decl_plugin.h" #include "ast/rewriter/expr_replacer.h" #include "model/model_smt2_pp.h" #include "ast/scoped_proof.h" #include "qe/qe_lite.h" #include "muz/spacer/spacer_qe_project.h" #include "model/model_pp.h" #include "ast/rewriter/expr_safe_replace.h" #include "ast/datatype_decl_plugin.h" #include "ast/bv_decl_plugin.h" namespace old { ///////////////////////// // model_evaluator // void model_evaluator::assign_value(expr* e, expr* val) { rational r; if (m.is_true(val)) { set_true(e); } else if (m.is_false(val)) { set_false(e); } else if (m_arith.is_numeral(val, r)) { set_number(e, r); } else if (m.is_value(val)) { set_value(e, val); } else { IF_VERBOSE(3, verbose_stream() << "Not evaluated " << mk_pp(e, m) << "\n";); TRACE("old_spacer", tout << "Variable is not tracked: " << mk_pp(e, m) << "\n";); set_x(e); } } void model_evaluator::setup_model(const model_ref& model) { m_numbers.reset(); m_values.reset(); m_model = model.get(); rational r; unsigned sz = model->get_num_constants(); for (unsigned i = 0; i < sz; i++) { func_decl * d = model->get_constant(i); expr* val = model->get_const_interp(d); expr* e = m.mk_const(d); m_refs.push_back(e); assign_value(e, val); } } void model_evaluator::reset() { m1.reset(); m2.reset(); m_values.reset(); m_visited.reset(); m_numbers.reset(); m_refs.reset(); m_model = nullptr; } void model_evaluator::minimize_literals(ptr_vector const& formulas, const model_ref& mdl, expr_ref_vector& result) { TRACE("old_spacer", tout << "formulas:\n"; for (unsigned i = 0; i < formulas.size(); ++i) tout << mk_pp(formulas[i], m) << "\n"; ); expr_ref tmp(m); ptr_vector tocollect; setup_model(mdl); collect(formulas, tocollect); for (unsigned i = 0; i < tocollect.size(); ++i) { expr* e = tocollect[i]; expr* e1, *e2; SASSERT(m.is_bool(e)); SASSERT(is_true(e) || is_false(e)); if (is_true(e)) { result.push_back(e); } // hack to break disequalities for arithmetic variables. else if (m.is_eq(e, e1, e2) && m_arith.is_int_real(e1)) { if (get_number(e1) < get_number(e2)) { result.push_back(m_arith.mk_lt(e1, e2)); } else { result.push_back(m_arith.mk_lt(e2, e1)); } } else { result.push_back(m.mk_not(e)); } } reset(); TRACE("old_spacer", tout << "minimized model:\n"; for (unsigned i = 0; i < result.size(); ++i) tout << mk_pp(result[i].get(), m) << "\n"; ); } void model_evaluator::process_formula(app* e, ptr_vector& todo, ptr_vector& tocollect) { SASSERT(m.is_bool(e)); SASSERT(is_true(e) || is_false(e)); unsigned v = is_true(e); unsigned sz = e->get_num_args(); expr* const* args = e->get_args(); if (e->get_family_id() == m.get_basic_family_id()) { switch (e->get_decl_kind()) { case OP_TRUE: break; case OP_FALSE: break; case OP_EQ: if (args[0] == args[1]) { SASSERT(v); // no-op } else if (m.is_bool(args[0])) { todo.append(sz, args); } else { tocollect.push_back(e); } break; case OP_DISTINCT: tocollect.push_back(e); break; case OP_ITE: if (args[1] == args[2]) { tocollect.push_back(args[1]); } else if (is_true(args[1]) && is_true(args[2])) { todo.append(2, args + 1); } else if (is_false(args[1]) && is_false(args[2])) { todo.append(2, args + 1); } else if (is_true(args[0])) { todo.append(2, args); } else { SASSERT(is_false(args[0])); todo.push_back(args[0]); todo.push_back(args[2]); } break; case OP_AND: if (v) { todo.append(sz, args); } else { unsigned i = 0; for (; !is_false(args[i]) && i < sz; ++i); if (i == sz) { fatal_error(1); } VERIFY(i < sz); todo.push_back(args[i]); } break; case OP_OR: if (v) { unsigned i = 0; for (; !is_true(args[i]) && i < sz; ++i); if (i == sz) { fatal_error(1); } VERIFY(i < sz); todo.push_back(args[i]); } else { todo.append(sz, args); } break; case OP_XOR: case OP_NOT: todo.append(sz, args); break; case OP_IMPLIES: if (v) { if (is_true(args[1])) { todo.push_back(args[1]); } else if (is_false(args[0])) { todo.push_back(args[0]); } else { IF_VERBOSE(0, verbose_stream() << "Term not handled " << mk_pp(e, m) << "\n";); UNREACHABLE(); } } else { todo.append(sz, args); } break; default: IF_VERBOSE(0, verbose_stream() << "Term not handled " << mk_pp(e, m) << "\n";); UNREACHABLE(); } } else { tocollect.push_back(e); } } void model_evaluator::collect(ptr_vector const& formulas, ptr_vector& tocollect) { ptr_vector todo; todo.append(formulas); m_visited.reset(); VERIFY(check_model(formulas)); while (!todo.empty()) { app* e = to_app(todo.back()); todo.pop_back(); if (!m_visited.is_marked(e)) { process_formula(e, todo, tocollect); m_visited.mark(e, true); } } m_visited.reset(); } void model_evaluator::eval_arith(app* e) { rational r, r2; #define ARG1 e->get_arg(0) #define ARG2 e->get_arg(1) unsigned arity = e->get_num_args(); for (unsigned i = 0; i < arity; ++i) { expr* arg = e->get_arg(i); if (is_x(arg)) { set_x(e); return; } SASSERT(!is_unknown(arg)); } switch (e->get_decl_kind()) { case OP_NUM: VERIFY(m_arith.is_numeral(e, r)); set_number(e, r); break; case OP_IRRATIONAL_ALGEBRAIC_NUM: set_x(e); break; case OP_LE: set_bool(e, get_number(ARG1) <= get_number(ARG2)); break; case OP_GE: set_bool(e, get_number(ARG1) >= get_number(ARG2)); break; case OP_LT: set_bool(e, get_number(ARG1) < get_number(ARG2)); break; case OP_GT: set_bool(e, get_number(ARG1) > get_number(ARG2)); break; case OP_ADD: r = rational::zero(); for (unsigned i = 0; i < arity; ++i) { r += get_number(e->get_arg(i)); } set_number(e, r); break; case OP_SUB: r = get_number(e->get_arg(0)); for (unsigned i = 1; i < arity; ++i) { r -= get_number(e->get_arg(i)); } set_number(e, r); break; case OP_UMINUS: SASSERT(arity == 1); set_number(e, -get_number(e->get_arg(0))); break; case OP_MUL: r = rational::one(); for (unsigned i = 0; i < arity; ++i) { r *= get_number(e->get_arg(i)); } set_number(e, r); break; case OP_DIV: SASSERT(arity == 2); r = get_number(ARG2); if (r.is_zero()) { set_x(e); } else { set_number(e, get_number(ARG1) / r); } break; case OP_IDIV: SASSERT(arity == 2); r = get_number(ARG2); if (r.is_zero()) { set_x(e); } else { set_number(e, div(get_number(ARG1), r)); } break; case OP_REM: // rem(v1,v2) = if v2 >= 0 then mod(v1,v2) else -mod(v1,v2) SASSERT(arity == 2); r = get_number(ARG2); if (r.is_zero()) { set_x(e); } else { r2 = mod(get_number(ARG1), r); if (r.is_neg()) { r2.neg(); } set_number(e, r2); } break; case OP_MOD: SASSERT(arity == 2); r = get_number(ARG2); if (r.is_zero()) { set_x(e); } else { set_number(e, mod(get_number(ARG1), r)); } break; case OP_TO_REAL: SASSERT(arity == 1); set_number(e, get_number(ARG1)); break; case OP_TO_INT: SASSERT(arity == 1); set_number(e, floor(get_number(ARG1))); break; case OP_IS_INT: SASSERT(arity == 1); set_bool(e, get_number(ARG1).is_int()); break; case OP_POWER: set_x(e); break; default: IF_VERBOSE(0, verbose_stream() << "Term not handled " << mk_pp(e, m) << "\n";); UNREACHABLE(); break; } } void model_evaluator::inherit_value(expr* e, expr* v) { expr* w; SASSERT(!is_unknown(v)); SASSERT(m.get_sort(e) == m.get_sort(v)); if (is_x(v)) { set_x(e); } else if (m.is_bool(e)) { SASSERT(m.is_bool(v)); if (is_true(v)) { set_true(e); } else if (is_false(v)) { set_false(e); } else { TRACE("old_spacer", tout << "not inherited:\n" << mk_pp(e, m) << "\n" << mk_pp(v, m) << "\n";); set_x(e); } } else if (m_arith.is_int_real(e)) { set_number(e, get_number(v)); } else if (m.is_value(v)) { set_value(e, v); } else if (m_values.find(v, w)) { set_value(e, w); } else { TRACE("old_spacer", tout << "not inherited:\n" << mk_pp(e, m) << "\n" << mk_pp(v, m) << "\n";); set_x(e); } } void model_evaluator::eval_exprs(expr_ref_vector& es) { model_ref mr(m_model); for (unsigned j = 0; j < es.size(); ++j) { if (m_array.is_as_array(es[j].get())) { es[j] = eval(mr, es[j].get()); } } } bool model_evaluator::extract_array_func_interp(expr* a, vector& stores, expr_ref& else_case) { SASSERT(m_array.is_array(a)); TRACE("old_spacer", tout << mk_pp(a, m) << "\n";); while (m_array.is_store(a)) { expr_ref_vector store(m); store.append(to_app(a)->get_num_args() - 1, to_app(a)->get_args() + 1); eval_exprs(store); stores.push_back(store); a = to_app(a)->get_arg(0); } if (m_array.is_const(a)) { else_case = to_app(a)->get_arg(0); return true; } while (m_array.is_as_array(a)) { func_decl* f = m_array.get_as_array_func_decl(to_app(a)); func_interp* g = m_model->get_func_interp(f); unsigned sz = g->num_entries(); unsigned arity = f->get_arity(); for (unsigned i = 0; i < sz; ++i) { expr_ref_vector store(m); func_entry const* fe = g->get_entry(i); store.append(arity, fe->get_args()); store.push_back(fe->get_result()); for (unsigned j = 0; j < store.size(); ++j) { if (!is_ground(store[j].get())) { TRACE("old_spacer", tout << "could not extract array interpretation: " << mk_pp(a, m) << "\n" << mk_pp(store[j].get(), m) << "\n";); return false; } } eval_exprs(store); stores.push_back(store); } else_case = g->get_else(); if (!else_case) { TRACE("old_spacer", tout << "no else case " << mk_pp(a, m) << "\n";); return false; } if (!is_ground(else_case)) { TRACE("old_spacer", tout << "non-ground else case " << mk_pp(a, m) << "\n" << mk_pp(else_case, m) << "\n";); return false; } if (m_array.is_as_array(else_case)) { model_ref mr(m_model); else_case = eval(mr, else_case); } TRACE("old_spacer", tout << "else case: " << mk_pp(else_case, m) << "\n";); return true; } TRACE("old_spacer", tout << "no translation: " << mk_pp(a, m) << "\n";); return false; } /** best effort evaluator of extensional array equality. */ void model_evaluator::eval_array_eq(app* e, expr* arg1, expr* arg2) { TRACE("old_spacer", tout << "array equality: " << mk_pp(e, m) << "\n";); expr_ref v1(m), v2(m); v1 = (*m_model)(arg1); v2 = (*m_model)(arg2); if (v1 == v2) { set_true(e); return; } sort* s = m.get_sort(arg1); sort* r = get_array_range(s); // give up evaluating finite domain/range arrays if (!r->is_infinite() && !r->is_very_big() && !s->is_infinite() && !s->is_very_big()) { TRACE("old_spacer", tout << "equality is unknown: " << mk_pp(e, m) << "\n";); set_x(e); return; } vector store; expr_ref else1(m), else2(m); if (!extract_array_func_interp(v1, store, else1) || !extract_array_func_interp(v2, store, else2)) { TRACE("old_spacer", tout << "equality is unknown: " << mk_pp(e, m) << "\n";); set_x(e); return; } if (else1 != else2) { if (m.is_value(else1) && m.is_value(else2)) { TRACE("old_spacer", tout << "defaults are different: " << mk_pp(e, m) << " " << mk_pp(else1, m) << " " << mk_pp(else2, m) << "\n";); set_false(e); } else if (m_array.is_array(else1)) { eval_array_eq(e, else1, else2); } else { TRACE("old_spacer", tout << "equality is unknown: " << mk_pp(e, m) << "\n";); set_x(e); } return; } expr_ref s1(m), s2(m), w1(m), w2(m); expr_ref_vector args1(m), args2(m); args1.push_back(v1); args2.push_back(v2); for (unsigned i = 0; i < store.size(); ++i) { args1.resize(1); args2.resize(1); args1.append(store[i].size() - 1, store[i].c_ptr()); args2.append(store[i].size() - 1, store[i].c_ptr()); s1 = m_array.mk_select(args1.size(), args1.c_ptr()); s2 = m_array.mk_select(args2.size(), args2.c_ptr()); w1 = (*m_model)(s1); w2 = (*m_model)(s2); if (w1 == w2) { continue; } if (m.is_value(w1) && m.is_value(w2)) { TRACE("old_spacer", tout << "Equality evaluation: " << mk_pp(e, m) << "\n"; tout << mk_pp(s1, m) << " |-> " << mk_pp(w1, m) << "\n"; tout << mk_pp(s2, m) << " |-> " << mk_pp(w2, m) << "\n";); set_false(e); } else if (m_array.is_array(w1)) { eval_array_eq(e, w1, w2); if (is_true(e)) { continue; } } else { TRACE("old_spacer", tout << "equality is unknown: " << mk_pp(e, m) << "\n";); set_x(e); } return; } set_true(e); } void model_evaluator::eval_eq(app* e, expr* arg1, expr* arg2) { if (arg1 == arg2) { set_true(e); } else if (m_array.is_array(arg1)) { eval_array_eq(e, arg1, arg2); } else if (is_x(arg1) || is_x(arg2)) { set_x(e); } else if (m.is_bool(arg1)) { bool val = is_true(arg1) == is_true(arg2); SASSERT(val == (is_false(arg1) == is_false(arg2))); if (val) { set_true(e); } else { set_false(e); } } else if (m_arith.is_int_real(arg1)) { set_bool(e, get_number(arg1) == get_number(arg2)); } else { expr* e1 = get_value(arg1); expr* e2 = get_value(arg2); if (m.is_value(e1) && m.is_value(e2)) { set_bool(e, e1 == e2); } else if (e1 == e2) { set_bool(e, true); } else { TRACE("old_spacer", tout << "not value equal:\n" << mk_pp(e1, m) << "\n" << mk_pp(e2, m) << "\n";); set_x(e); } } } void model_evaluator::eval_basic(app* e) { expr* arg1, *arg2; expr *argCond, *argThen, *argElse, *arg; bool has_x = false; unsigned arity = e->get_num_args(); switch (e->get_decl_kind()) { case OP_AND: for (unsigned j = 0; j < arity; ++j) { expr * arg = e->get_arg(j); if (is_false(arg)) { set_false(e); return; } else if (is_x(arg)) { has_x = true; } else { SASSERT(is_true(arg)); } } if (has_x) { set_x(e); } else { set_true(e); } break; case OP_OR: for (unsigned j = 0; j < arity; ++j) { expr * arg = e->get_arg(j); if (is_true(arg)) { set_true(e); return; } else if (is_x(arg)) { has_x = true; } else { SASSERT(is_false(arg)); } } if (has_x) { set_x(e); } else { set_false(e); } break; case OP_NOT: VERIFY(m.is_not(e, arg)); if (is_true(arg)) { set_false(e); } else if (is_false(arg)) { set_true(e); } else { SASSERT(is_x(arg)); set_x(e); } break; case OP_IMPLIES: VERIFY(m.is_implies(e, arg1, arg2)); if (is_false(arg1) || is_true(arg2)) { set_true(e); } else if (arg1 == arg2) { set_true(e); } else if (is_true(arg1) && is_false(arg2)) { set_false(e); } else { SASSERT(is_x(arg1) || is_x(arg2)); set_x(e); } break; case OP_XOR: VERIFY(m.is_xor(e, arg1, arg2)); eval_eq(e, arg1, arg2); if (is_false(e)) { set_true(e); } else if (is_true(e)) { set_false(e); } break; case OP_ITE: VERIFY(m.is_ite(e, argCond, argThen, argElse)); if (is_true(argCond)) { inherit_value(e, argThen); } else if (is_false(argCond)) { inherit_value(e, argElse); } else if (argThen == argElse) { inherit_value(e, argThen); } else if (m.is_bool(e)) { SASSERT(is_x(argCond)); if (is_x(argThen) || is_x(argElse)) { set_x(e); } else if (is_true(argThen) == is_true(argElse)) { inherit_value(e, argThen); } else { set_x(e); } } else { set_x(e); } break; case OP_TRUE: set_true(e); break; case OP_FALSE: set_false(e); break; case OP_EQ: VERIFY(m.is_eq(e, arg1, arg2)); eval_eq(e, arg1, arg2); break; case OP_DISTINCT: { vector values; for (unsigned i = 0; i < arity; ++i) { expr* arg = e->get_arg(i); if (is_x(arg)) { set_x(e); return; } values.push_back(get_number(arg)); } std::sort(values.begin(), values.end()); for (unsigned i = 0; i + 1 < values.size(); ++i) { if (values[i] == values[i + 1]) { set_false(e); return; } } set_true(e); break; } default: IF_VERBOSE(0, verbose_stream() << "Term not handled " << mk_pp(e, m) << "\n";); UNREACHABLE(); } } void model_evaluator::eval_fmls(ptr_vector const& formulas) { ptr_vector todo(formulas); while (!todo.empty()) { expr * curr_e = todo.back(); if (!is_app(curr_e)) { todo.pop_back(); continue; } app * curr = to_app(curr_e); if (!is_unknown(curr)) { todo.pop_back(); continue; } unsigned arity = curr->get_num_args(); for (unsigned i = 0; i < arity; ++i) { if (is_unknown(curr->get_arg(i))) { todo.push_back(curr->get_arg(i)); } } if (todo.back() != curr) { continue; } todo.pop_back(); if (curr->get_family_id() == m_arith.get_family_id()) { eval_arith(curr); } else if (curr->get_family_id() == m.get_basic_family_id()) { eval_basic(curr); } else { expr_ref vl(m); vl = eval(m_model, curr); assign_value(curr, vl); } IF_VERBOSE(35, verbose_stream() << "assigned " << mk_pp(curr_e, m) << (is_true(curr_e) ? "true" : is_false(curr_e) ? "false" : "unknown") << "\n";); SASSERT(!is_unknown(curr)); } } bool model_evaluator::check_model(ptr_vector const& formulas) { eval_fmls(formulas); bool has_x = false; for (unsigned i = 0; i < formulas.size(); ++i) { expr * form = formulas[i]; SASSERT(!is_unknown(form)); TRACE("spacer_verbose", tout << "formula is " << (is_true(form) ? "true" : is_false(form) ? "false" : "unknown") << "\n" << mk_pp(form, m) << "\n";); if (is_false(form)) { IF_VERBOSE(0, verbose_stream() << "formula false in model: " << mk_pp(form, m) << "\n";); UNREACHABLE(); } if (is_x(form)) { IF_VERBOSE(0, verbose_stream() << "formula undetermined in model: " << mk_pp(form, m) << "\n";); TRACE("old_spacer", model_smt2_pp(tout, m, *m_model, 0);); has_x = true; } } return !has_x; } expr_ref model_evaluator::eval_heavy(const model_ref& model, expr* fml) { expr_ref result(model->get_manager()); setup_model(model); ptr_vector fmls; fmls.push_back(fml); eval_fmls(fmls); if (is_false(fml)) { result = m.mk_false(); } else if (is_true(fml) || is_x(fml)) { result = m.mk_true(); } else if (m_arith.is_int_real(fml)) { result = m_arith.mk_numeral(get_number(fml), m_arith.is_int(fml)); } else { result = get_value(fml); } reset(); return result; } expr_ref model_evaluator::eval(const model_ref& model, func_decl* d) { SASSERT(d->get_arity() == 0); expr_ref result(m); if (m_array.is_array(d->get_range())) { expr_ref e(m); e = m.mk_const(d); result = eval(model, e); } else { result = model->get_const_interp(d); } return result; } expr_ref model_evaluator::eval(const model_ref& model, expr* e){ m_model = model.get(); model::scoped_model_completion _scm(m_model, true); expr_ref result = (*m_model)(e); if (m_array.is_array(e)) { vector stores; expr_ref_vector args(m); expr_ref else_case(m); if (extract_array_func_interp(result, stores, else_case)) { result = m_array.mk_const_array(m.get_sort(e), else_case); while (!stores.empty() && stores.back().back() == else_case) { stores.pop_back(); } for (unsigned i = stores.size(); i > 0;) { --i; args.resize(1); args[0] = result; args.append(stores[i]); result = m_array.mk_store(args); } return result; } } return result; } } z3-z3-4.8.7/src/muz/spacer/spacer_legacy_mev.h000066400000000000000000000074511356505360400211360ustar00rootroot00000000000000/** Copyright (c) 2017 Arie Gurfinkel Deprecated implementation of model evaluator. To be removed. */ #ifndef OLD_MEV_H #define OLD_MEV_H #include "ast/ast.h" #include "ast/ast_pp.h" #include "util/obj_hashtable.h" #include "util/ref_vector.h" #include "util/trace.h" #include "util/vector.h" #include "ast/arith_decl_plugin.h" #include "ast/array_decl_plugin.h" #include "ast/bv_decl_plugin.h" namespace old { class model_evaluator { ast_manager& m; arith_util m_arith; array_util m_array; obj_map m_numbers; expr_ref_vector m_refs; obj_map m_values; model_ref m_model; //00 -- non-visited //01 -- X //10 -- false //11 -- true expr_mark m1; expr_mark m2; /// used by collect() expr_mark m_visited; void reset(); /// caches the values of all constants in the given model void setup_model(const model_ref& model); /// caches the value of an expression void assign_value(expr* e, expr* v); /// extracts an implicant of the conjunction of formulas void collect(ptr_vector const& formulas, ptr_vector& tocollect); /// one-round of extracting an implicant of e. The implicant /// literals are stored in tocollect. The worklist is stored in todo void process_formula(app* e, ptr_vector& todo, ptr_vector& tocollect); void eval_arith(app* e); void eval_basic(app* e); void eval_eq(app* e, expr* arg1, expr* arg2); void eval_array_eq(app* e, expr* arg1, expr* arg2); void inherit_value(expr* e, expr* v); bool is_unknown(expr* x) { return !m1.is_marked(x) && !m2.is_marked(x); } void set_unknown(expr* x) { m1.mark(x, false); m2.mark(x, false); } bool is_x(expr* x) { return !m1.is_marked(x) && m2.is_marked(x); } bool is_false(expr* x) { return m1.is_marked(x) && !m2.is_marked(x); } bool is_true(expr* x) { return m1.is_marked(x) && m2.is_marked(x); } void set_x(expr* x) { SASSERT(is_unknown(x)); m2.mark(x); } void set_v(expr* x) { SASSERT(is_unknown(x)); m1.mark(x); } void set_false(expr* x) { SASSERT(is_unknown(x)); m1.mark(x); } void set_true(expr* x) { SASSERT(is_unknown(x)); m1.mark(x); m2.mark(x); } void set_bool(expr* x, bool v) { if(v) { set_true(x); } else { set_false(x); } } rational const& get_number(expr* x) const { return m_numbers.find(x); } void set_number(expr* x, rational const& v) { set_v(x); m_numbers.insert(x, v); TRACE("spacer_verbose", tout << mk_pp(x, m) << " " << v << "\n";); } expr* get_value(expr* x) { return m_values.find(x); } void set_value(expr* x, expr* v) { set_v(x); m_refs.push_back(v); m_values.insert(x, v); } /// evaluates all sub-formulas and terms of the input in the current model. /// Caches the result void eval_fmls(ptr_vector const & formulas); /// calls eval_fmls(). Then checks whether all formulas are /// TRUE. Returns false if at lest one formula is unknown (X) bool check_model(ptr_vector const & formulas); bool extract_array_func_interp(expr* a, vector& stores, expr_ref& else_case); void eval_exprs(expr_ref_vector& es); public: model_evaluator(ast_manager& m) : m(m), m_arith(m), m_array(m), m_refs(m) {} /** \brief extract literals from formulas that satisfy formulas. \pre model satisfies formulas */ void minimize_literals(ptr_vector const & formulas, const model_ref& mdl, expr_ref_vector& result); expr_ref eval_heavy(const model_ref& mdl, expr* fml); expr_ref eval(const model_ref& mdl, expr* e); expr_ref eval(const model_ref& mdl, func_decl* d); }; } #endif /* OLD_MEV_H */ z3-z3-4.8.7/src/muz/spacer/spacer_manager.cpp000066400000000000000000000157521356505360400207730ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: spacer_manager.cpp Abstract: A manager class for SPACER, taking care of creating of AST objects and conversions between them. Author: Krystof Hoder (t-khoder) 2011-8-25. Revision History: --*/ #include #include "muz/spacer/spacer_manager.h" #include "ast/ast_smt2_pp.h" #include "ast/for_each_expr.h" #include "ast/has_free_vars.h" #include "ast/rewriter/expr_replacer.h" #include "ast/expr_abstract.h" #include "model/model2expr.h" #include "model/model_smt2_pp.h" #include "tactic/model_converter.h" #include "smt/smt_solver.h" namespace spacer { class collect_decls_proc { func_decl_set& m_bound_decls; func_decl_set& m_aux_decls; public: collect_decls_proc(func_decl_set& bound_decls, func_decl_set& aux_decls): m_bound_decls(bound_decls), m_aux_decls(aux_decls) { } void operator()(app* a) { if (a->get_family_id() == null_family_id) { func_decl* f = a->get_decl(); if (!m_bound_decls.contains(f)) { m_aux_decls.insert(f); } } } void operator()(var* v) {} void operator()(quantifier* q) {} }; typedef hashtable symbol_set; expr_ref inductive_property::fixup_clause(expr* fml) const { expr_ref_vector disjs(m); flatten_or(fml, disjs); expr_ref result(m); bool_rewriter(m).mk_or(disjs.size(), disjs.c_ptr(), result); return result; } expr_ref inductive_property::fixup_clauses(expr* fml) const { expr_ref_vector conjs(m); expr_ref result(m); flatten_and(fml, conjs); for (unsigned i = 0; i < conjs.size(); ++i) { conjs[i] = fixup_clause(conjs[i].get()); } bool_rewriter(m).mk_and(conjs.size(), conjs.c_ptr(), result); return result; } std::string inductive_property::to_string() const { std::stringstream stm; model_ref md; expr_ref result(m); to_model(md); model_smt2_pp(stm, m, *md.get(), 0); return stm.str(); } void inductive_property::to_model(model_ref& md) const { md = alloc(model, m); vector const& rs = m_relation_info; expr_ref_vector conjs(m); for (unsigned i = 0; i < rs.size(); ++i) { relation_info ri(rs[i]); func_decl * pred = ri.m_pred; expr_ref prop = fixup_clauses(ri.m_body); func_decl_ref_vector const& sig = ri.m_vars; expr_ref q(m); expr_ref_vector sig_vars(m); for (unsigned j = 0; j < sig.size(); ++j) { sig_vars.push_back(m.mk_const(sig[sig.size() - j - 1])); } expr_abstract(m, 0, sig_vars.size(), sig_vars.c_ptr(), prop, q); if (sig.empty()) { md->register_decl(pred, q); } else { func_interp* fi = alloc(func_interp, m, sig.size()); fi->set_else(q); md->register_decl(pred, fi); } } TRACE("spacer", model_smt2_pp(tout, m, *md, 0);); apply(const_cast(m_mc), md); } expr_ref inductive_property::to_expr() const { model_ref md; expr_ref result(m); to_model(md); model2expr(md, result); return result; } void inductive_property::display(datalog::rule_manager& rm, ptr_vector const& rules, std::ostream& out) const { func_decl_set bound_decls, aux_decls; collect_decls_proc collect_decls(bound_decls, aux_decls); for (unsigned i = 0; i < m_relation_info.size(); ++i) { bound_decls.insert(m_relation_info[i].m_pred); func_decl_ref_vector const& sig = m_relation_info[i].m_vars; for (unsigned j = 0; j < sig.size(); ++j) { bound_decls.insert(sig[j]); } for_each_expr(collect_decls, m_relation_info[i].m_body); } for (unsigned i = 0; i < rules.size(); ++i) { bound_decls.insert(rules[i]->get_decl()); } for (unsigned i = 0; i < rules.size(); ++i) { unsigned u_sz = rules[i]->get_uninterpreted_tail_size(); unsigned t_sz = rules[i]->get_tail_size(); for (unsigned j = u_sz; j < t_sz; ++j) { for_each_expr(collect_decls, rules[i]->get_tail(j)); } } smt2_pp_environment_dbg env(m); func_decl_set::iterator it = aux_decls.begin(), end = aux_decls.end(); for (; it != end; ++it) { func_decl* f = *it; ast_smt2_pp(out, f, env); out << "\n"; } out << to_string() << "\n"; for (unsigned i = 0; i < rules.size(); ++i) { out << "(push)\n"; out << "(assert (not\n"; rm.display_smt2(*rules[i], out); out << "))\n"; out << "(check-sat)\n"; out << "(pop)\n"; } } manager::manager(ast_manager& manager) : m(manager), m_mux(m) {} func_decl * manager::get_o_pred(func_decl* s, unsigned idx) { func_decl * res = m_mux.find_by_decl(s, o_index(idx)); if (!res) { m_mux.register_decl(s); res = m_mux.find_by_decl(s, o_index(idx)); } SASSERT(res); return res; } func_decl * manager::get_n_pred(func_decl* s) { func_decl * res = m_mux.find_by_decl(s, n_index()); if (!res) { m_mux.register_decl(s); res = m_mux.find_by_decl(s, n_index()); } SASSERT(res); return res; } /** * Create a new skolem constant */ app* mk_zk_const(ast_manager &m, unsigned idx, sort *s) { std::stringstream name; name << "sk!" << idx; return m.mk_const(symbol(name.str().c_str()), s); } namespace find_zk_const_ns { struct proc { int m_max; app_ref_vector &m_out; proc (app_ref_vector &out) : m_max(-1), m_out(out) {} void operator() (var const * n) const {} void operator() (app *n) { int idx; if (is_zk_const(n, idx)) { m_out.push_back(n); if (idx > m_max) { m_max = idx; } } } void operator() (quantifier const *n) const {} }; } int find_zk_const(expr *e, app_ref_vector &res) { find_zk_const_ns::proc p(res); for_each_expr (p, e); return p.m_max; } namespace has_zk_const_ns { struct found {}; struct proc { void operator() (var const *n) const {} void operator() (app const *n) const { int idx; if (is_zk_const(n, idx)) { throw found(); } } void operator() (quantifier const *n) const {} }; } bool has_zk_const(expr *e){ has_zk_const_ns::proc p; try { for_each_expr(p, e); } catch (const has_zk_const_ns::found &) { return true; } return false; } bool is_zk_const (const app *a, int &n) { if (!is_uninterp_const(a)) return false; const symbol &name = a->get_decl()->get_name(); if (name.str().compare (0, 3, "sk!") != 0) { return false; } n = std::stoi(name.str().substr(3)); return true; } bool sk_lt_proc::operator()(const app *a1, const app *a2) { if (a1 == a2) return false; int n1, n2; bool z1, z2; z1 = is_zk_const(a1, n1); z2 = is_zk_const(a2, n2); if (z1 && z2) return n1 < n2; if (z1 != z2) return z1; return ast_lt_proc()(a1, a2); } } z3-z3-4.8.7/src/muz/spacer/spacer_manager.h000066400000000000000000000101641356505360400204300ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: spacer_manager.h Abstract: A manager class for SPACER, taking care of creating of AST objects and conversions between them. Author: Krystof Hoder (t-khoder) 2011-8-25. Arie Gurfinkel Revision History: --*/ #ifndef _SPACER_MANAGER_H_ #define _SPACER_MANAGER_H_ #include #include #include #include "ast/rewriter/bool_rewriter.h" #include "ast/rewriter/expr_replacer.h" #include "ast/expr_substitution.h" #include "util/map.h" #include "util/ref_vector.h" #include "smt/smt_kernel.h" #include "muz/spacer/spacer_util.h" #include "muz/spacer/spacer_sym_mux.h" #include "muz/spacer/spacer_farkas_learner.h" #include "muz/base/dl_rule.h" #include "solver/solver.h" #include "solver/solver_pool.h" namespace smt {class context;} namespace spacer { struct relation_info { func_decl_ref m_pred; func_decl_ref_vector m_vars; expr_ref m_body; relation_info(ast_manager& m, func_decl* pred, ptr_vector const& vars, expr* b): m_pred(pred, m), m_vars(m, vars.size(), vars.c_ptr()), m_body(b, m) {} relation_info(relation_info const& other): m_pred(other.m_pred), m_vars(other.m_vars), m_body(other.m_body) {} }; class unknown_exception {}; class inductive_property { ast_manager& m; model_converter_ref m_mc; vector m_relation_info; expr_ref fixup_clauses(expr* property) const; expr_ref fixup_clause(expr* clause) const; public: inductive_property(ast_manager& m, model_converter_ref& mc, vector const& relations): m(m), m_mc(mc), m_relation_info(relations) {} std::string to_string() const; expr_ref to_expr() const; void to_model(model_ref& md) const; void display(datalog::rule_manager& rm, ptr_vector const& rules, std::ostream& out) const; }; class manager { ast_manager& m; // manager of multiplexed names sym_mux m_mux; unsigned n_index() const { return 0; } unsigned o_index(unsigned i) const { return i + 1; } public: manager(ast_manager & manager); ast_manager& get_manager() const { return m; } // management of mux names //"o" predicates stand for the old states and "n" for the new states func_decl * get_o_pred(func_decl * s, unsigned idx); func_decl * get_n_pred(func_decl * s); bool is_n_formula(expr * f) const {return m_mux.is_homogenous_formula(f, n_index());} func_decl * o2n(func_decl * p, unsigned o_idx) const {return m_mux.shift_decl(p, o_index(o_idx), n_index());} func_decl * o2o(func_decl * p, unsigned src_idx, unsigned tgt_idx) const {return m_mux.shift_decl(p, o_index(src_idx), o_index(tgt_idx));} func_decl * n2o(func_decl * p, unsigned o_idx) const {return m_mux.shift_decl(p, n_index(), o_index(o_idx));} void formula_o2n(expr * f, expr_ref & result, unsigned o_idx, bool homogenous = true) const {m_mux.shift_expr(f, o_index(o_idx), n_index(), result, homogenous);} void formula_n2o(expr * f, expr_ref & result, unsigned o_idx, bool homogenous = true) const {m_mux.shift_expr(f, n_index(), o_index(o_idx), result, homogenous);} void formula_n2o(unsigned o_idx, bool homogenous, expr_ref & result) const {m_mux.shift_expr(result.get(), n_index(), o_index(o_idx), result, homogenous);} void formula_o2o(expr * src, expr_ref & tgt, unsigned src_idx, unsigned tgt_idx, bool homogenous = true) const {m_mux.shift_expr(src, o_index(src_idx), o_index(tgt_idx), tgt, homogenous);} }; /** Skolem constants for quantified spacer */ app* mk_zk_const (ast_manager &m, unsigned idx, sort *s); int find_zk_const(expr* e, app_ref_vector &out); inline int find_zk_const(expr_ref_vector const &v, app_ref_vector &out) {return find_zk_const (mk_and(v), out);} bool has_zk_const(expr* e); bool is_zk_const (const app *a, int &n); struct sk_lt_proc {bool operator()(const app* a1, const app* a2);}; } #endif z3-z3-4.8.7/src/muz/spacer/spacer_matrix.cpp000066400000000000000000000076141356505360400206630ustar00rootroot00000000000000/*++ Copyright (c) 2017 Arie Gurfinkel Module Name: spacer_matrix.cpp Abstract: a matrix Author: Bernhard Gleiss Revision History: --*/ #include "muz/spacer/spacer_matrix.h" namespace spacer { spacer_matrix::spacer_matrix(unsigned m, unsigned n) : m_num_rows(m), m_num_cols(n) { for (unsigned i=0; i < m; ++i) { vector v; for (unsigned j=0; j < n; ++j) { v.push_back(rational(0)); } m_matrix.push_back(v); } } unsigned spacer_matrix::num_rows() { return m_num_rows; } unsigned spacer_matrix::num_cols() { return m_num_cols; } const rational& spacer_matrix::get(unsigned int i, unsigned int j) { SASSERT(i < m_num_rows); SASSERT(j < m_num_cols); return m_matrix[i][j]; } void spacer_matrix::set(unsigned int i, unsigned int j, const rational& v) { SASSERT(i < m_num_rows); SASSERT(j < m_num_cols); m_matrix[i][j] = v; } unsigned spacer_matrix::perform_gaussian_elimination() { unsigned i=0; unsigned j=0; while(i < m_matrix.size() && j < m_matrix[0].size()) { // find maximal element in column with row index bigger or equal i rational max = m_matrix[i][j]; unsigned max_index = i; for (unsigned k=i+1; k < m_matrix.size(); ++k) { if (max < m_matrix[k][j]) { max = m_matrix[k][j]; max_index = k; } } if (max.is_zero()) // skip this column { ++j; } else { // reorder rows if necessary vector tmp = m_matrix[i]; m_matrix[i] = m_matrix[max_index]; m_matrix[max_index] = m_matrix[i]; // normalize row rational pivot = m_matrix[i][j]; if (!pivot.is_one()) { for (unsigned k=0; k < m_matrix[i].size(); ++k) { m_matrix[i][k] = m_matrix[i][k] / pivot; } } // subtract row from all other rows for (unsigned k=1; k < m_matrix.size(); ++k) { if (k != i) { rational factor = m_matrix[k][j]; for (unsigned l=0; l < m_matrix[k].size(); ++l) { m_matrix[k][l] = m_matrix[k][l] - (factor * m_matrix[i][l]); } } } ++i; ++j; } } if (get_verbosity_level() >= 1) { SASSERT(m_matrix.size() > 0); } return i; //i points to the row after the last row which is non-zero } void spacer_matrix::print_matrix() { verbose_stream() << "\nMatrix\n"; for (const auto& row : m_matrix) { for (const auto& element : row) { verbose_stream() << element << ", "; } verbose_stream() << "\n"; } verbose_stream() << "\n"; } void spacer_matrix::normalize() { rational den = rational::one(); for (unsigned i=0; i < m_num_rows; ++i) { for (unsigned j=0; j < m_num_cols; ++j) { den = lcm(den, denominator(m_matrix[i][j])); } } for (unsigned i=0; i < m_num_rows; ++i) { for (unsigned j=0; j < m_num_cols; ++j) { m_matrix[i][j] = den * m_matrix[i][j]; SASSERT(m_matrix[i][j].is_int()); } } } } z3-z3-4.8.7/src/muz/spacer/spacer_matrix.h000066400000000000000000000014341356505360400203220ustar00rootroot00000000000000/*++ Copyright (c) 2017 Arie Gurfinkel Module Name: spacer_matrix.h Abstract: a matrix Author: Bernhard Gleiss Revision History: --*/ #ifndef _SPACER_MATRIX_H_ #define _SPACER_MATRIX_H_ #include "util/rational.h" #include "util/vector.h" namespace spacer { class spacer_matrix { public: spacer_matrix(unsigned m, unsigned n); // m rows, n columns unsigned num_rows(); unsigned num_cols(); const rational& get(unsigned i, unsigned j); void set(unsigned i, unsigned j, const rational& v); unsigned perform_gaussian_elimination(); void print_matrix(); void normalize(); private: unsigned m_num_rows; unsigned m_num_cols; vector> m_matrix; }; } #endif z3-z3-4.8.7/src/muz/spacer/spacer_mbc.cpp000066400000000000000000000054441356505360400201170ustar00rootroot00000000000000#include #include "muz/spacer/spacer_mbc.h" #include "ast/rewriter/rewriter.h" #include "ast/rewriter/rewriter_def.h" #include "ast/rewriter/th_rewriter.h" #include "ast/scoped_proof.h" #include "model/model_evaluator.h" namespace spacer { mbc::mbc(ast_manager &m) : m(m) {} namespace { class mbc_rewriter_cfg : public default_rewriter_cfg { ast_manager &m; const mbc::partition_map &m_pmap; obj_map &m_subs; model &m_mdl; model_evaluator m_mev; vector &m_parts; unsigned m_current_part; public: mbc_rewriter_cfg(ast_manager &m, const mbc::partition_map &pmap, obj_map &subs, model &mdl, vector &parts) : m(m), m_pmap(pmap), m_subs(subs), m_mdl(mdl), m_mev(m_mdl), m_parts(parts), m_current_part(UINT_MAX) {m_mev.set_model_completion(true);} bool get_subst(expr *s, expr * & t, proof * & t_pr) { if (!is_app(s)) return false; unsigned part = UINT_MAX; // not in partition map if (!m_pmap.find (to_app(s)->get_decl(), part)) return false; // first part element, remember it if (!found_partition()) { set_partition(part); return false; } // already in our substitution map expr *tmp = nullptr; if (m_subs.find(s, tmp)) { t = tmp; return true; } // decide value based on model expr_ref val(m); // eval in the model m_mev.eval(s, val, true); // store decided equality (also keeps ref to s and val) m_parts[part].push_back(m.mk_eq(s, val)); // store substitution m_subs.insert(s, val); t = val; return true; } void reset() {reset_partition();}; void reset_partition() {m_current_part = UINT_MAX;} unsigned partition() {return m_current_part;} bool found_partition() {return m_current_part < UINT_MAX;} void set_partition(unsigned v) {m_current_part = v;} }; } void mbc::operator()(const partition_map &pmap, expr_ref_vector &lits, model &mdl, vector &res) { scoped_no_proof _sp (m); obj_map subs; mbc_rewriter_cfg cfg(m, pmap, subs, mdl, res); rewriter_tpl rw(m, false, cfg); th_rewriter thrw(m); for (auto *lit : lits) { expr_ref new_lit(m); rw.reset(); rw(lit, new_lit); thrw(new_lit); if (cfg.found_partition()) { SASSERT(cfg.partition() < res.size()); res[cfg.partition()].push_back(new_lit); } } TRACE("mbc", tout << "Input: " << lits << "\n" << "Output: \n"; for (auto &vec : res) tout << vec << "\n==================\n";); } } z3-z3-4.8.7/src/muz/spacer/spacer_mbc.h000066400000000000000000000012271356505360400175570ustar00rootroot00000000000000/*++ Copyright (c) 2018 Arie Gurfinkel Module Name: spacer_mbc.h Abstract: Model-Based Cartesian Decomposition Author: Arie Gurfinkel Revision History: --*/ #ifndef _SPACER_MBC_H_ #define _SPACER_MBC_H_ #include "ast/ast.h" #include "util/obj_hashtable.h" #include "model/model.h" namespace spacer { class mbc { ast_manager &m; public: mbc(ast_manager &m); typedef obj_map partition_map; /** \Brief Model Based Cartesian projection of lits */ void operator()(const partition_map &pmap, expr_ref_vector &lits, model &mdl, vector &res); }; } #endif z3-z3-4.8.7/src/muz/spacer/spacer_mev_array.cpp000066400000000000000000000156261356505360400213460ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: model_mev_array.cpp Abstract: Evaluate array expressions in a given model. Author: Revision History: --*/ #include"model/model.h" #include "model/model_evaluator_params.hpp" #include"ast/rewriter/rewriter_types.h" #include"model/model_evaluator.h" #include"muz/spacer/spacer_mev_array.h" #include"ast/rewriter/bool_rewriter.h" #include"ast/rewriter/arith_rewriter.h" #include"ast/rewriter/bv_rewriter.h" #include"ast/rewriter/datatype_rewriter.h" #include"ast/rewriter/array_rewriter.h" #include"ast/rewriter/rewriter_def.h" #include"ast/ast_pp.h" #include"model/func_interp.h" // model_evaluator_array_util void model_evaluator_array_util::eval_exprs(model& mdl, expr_ref_vector& es) { for (unsigned j = 0; j < es.size(); ++j) { if (m_array.is_as_array(es.get (j))) { expr_ref r (m); eval(mdl, es.get (j), r); es.set (j, r); } } } bool model_evaluator_array_util::extract_array_func_interp(model& mdl, expr* a, vector& stores, expr_ref& else_case) { SASSERT(m_array.is_array(a)); TRACE("model_evaluator", tout << mk_pp(a, m) << "\n";); while (m_array.is_store(a)) { expr_ref_vector store(m); store.append(to_app(a)->get_num_args()-1, to_app(a)->get_args()+1); eval_exprs(mdl, store); stores.push_back(store); a = to_app(a)->get_arg(0); } if (m_array.is_const(a)) { else_case = to_app(a)->get_arg(0); return true; } while (m_array.is_as_array(a)) { func_decl* f = m_array.get_as_array_func_decl(to_app(a)); func_interp* g = mdl.get_func_interp(f); unsigned sz = g->num_entries(); unsigned arity = f->get_arity(); for (unsigned i = 0; i < sz; ++i) { expr_ref_vector store(m); func_entry const* fe = g->get_entry(i); store.append(arity, fe->get_args()); store.push_back(fe->get_result()); for (unsigned j = 0; j < store.size(); ++j) { if (!is_ground(store[j].get())) { TRACE("model_evaluator", tout << "could not extract array interpretation: " << mk_pp(a, m) << "\n" << mk_pp(store[j].get(), m) << "\n";); return false; } } eval_exprs(mdl, store); stores.push_back(store); } else_case = g->get_else(); if (!else_case) { TRACE("model_evaluator", tout << "no else case " << mk_pp(a, m) << "\n";); return false; } if (!is_ground(else_case)) { TRACE("model_evaluator", tout << "non-ground else case " << mk_pp(a, m) << "\n" << mk_pp(else_case, m) << "\n";); return false; } if (m_array.is_as_array(else_case)) { expr_ref r (m); eval(mdl, else_case, r); else_case = r; } TRACE("model_evaluator", tout << "else case: " << mk_pp(else_case, m) << "\n";); return true; } TRACE("model_evaluator", tout << "no translation: " << mk_pp(a, m) << "\n";); return false; } void model_evaluator_array_util::eval_array_eq(model& mdl, app* e, expr* arg1, expr* arg2, expr_ref& res) { TRACE("model_evaluator", tout << "array equality: " << mk_pp(e, m) << "\n";); expr_ref v1(m), v2(m); eval (mdl, arg1, v1); eval (mdl, arg2, v2); if (v1 == v2) { res = m.mk_true (); return; } sort* s = m.get_sort(arg1); sort* r = get_array_range(s); // give up evaluating finite domain/range arrays if (!r->is_infinite() && !r->is_very_big() && !s->is_infinite() && !s->is_very_big()) { TRACE("model_evaluator", tout << "equality is unknown: " << mk_pp(e, m) << "\n";); res.reset (); return; } vector store; expr_ref else1(m), else2(m); if (!extract_array_func_interp(mdl, v1, store, else1) || !extract_array_func_interp(mdl, v2, store, else2)) { TRACE("model_evaluator", tout << "equality is unknown: " << mk_pp(e, m) << "\n";); res.reset (); return; } if (else1 != else2) { if (m.is_value(else1) && m.is_value(else2)) { TRACE("model_evaluator", tout << "defaults are different: " << mk_pp(e, m) << " " << mk_pp(else1, m) << " " << mk_pp(else2, m) << "\n";); res = m.mk_false (); } else if (m_array.is_array(else1)) { eval_array_eq(mdl, e, else1, else2, res); } else { TRACE("model_evaluator", tout << "equality is unknown: " << mk_pp(e, m) << "\n";); res.reset (); } return; } expr_ref s1(m), s2(m), w1(m), w2(m); expr_ref_vector args1(m), args2(m); args1.push_back(v1); args2.push_back(v2); for (unsigned i = 0; i < store.size(); ++i) { args1.resize(1); args2.resize(1); args1.append(store[i].size()-1, store[i].c_ptr()); args2.append(store[i].size()-1, store[i].c_ptr()); s1 = m_array.mk_select(args1); s2 = m_array.mk_select(args2); eval (mdl, s1, w1); eval (mdl, s2, w2); if (w1 == w2) { continue; } if (m.is_value(w1) && m.is_value(w2)) { TRACE("model_evaluator", tout << "Equality evaluation: " << mk_pp(e, m) << "\n"; tout << mk_pp(s1, m) << " |-> " << mk_pp(w1, m) << "\n"; tout << mk_pp(s2, m) << " |-> " << mk_pp(w2, m) << "\n";); res = m.mk_false (); } else if (m_array.is_array(w1)) { eval_array_eq(mdl, e, w1, w2, res); if (m.is_true (res)) { continue; } } else { TRACE("model_evaluator", tout << "equality is unknown: " << mk_pp(e, m) << "\n";); res.reset (); } return; } res = m.mk_true (); } void model_evaluator_array_util::eval(model& mdl, expr* e, expr_ref& r, bool model_completion) { model_evaluator mev (mdl); mev.set_model_completion (model_completion); bool eval_result = true; try { mev (e, r); } catch (model_evaluator_exception &) { eval_result = false; } VERIFY(eval_result); if (m_array.is_array(e)) { vector stores; expr_ref_vector args(m); expr_ref else_case(m); if (extract_array_func_interp(mdl, r, stores, else_case)) { r = m_array.mk_const_array(m.get_sort(e), else_case); while (!stores.empty() && stores.back().back() == else_case) { stores.pop_back(); } for (unsigned i = stores.size(); i > 0; ) { --i; args.resize(1); args[0] = r; args.append(stores[i]); r = m_array.mk_store(args); } return; } } return; } z3-z3-4.8.7/src/muz/spacer/spacer_mev_array.h000066400000000000000000000020551356505360400210030ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: spacer_mev_array.h Abstract: Utilities to evaluate arrays in the model. Author: based on model_evaluator in muz/pdr/pdr_util.h Revision History: --*/ #ifndef _SPACER_MEV_ARRAY_H_ #define _SPACER_MEV_ARRAY_H_ #include"ast/ast.h" #include"ast/rewriter/rewriter_types.h" #include"util/params.h" #include"ast/array_decl_plugin.h" /** * based on model_evaluator in muz/pdr/pdr_util.h */ class model_evaluator_array_util { ast_manager& m; array_util m_array; void eval_exprs(model& mdl, expr_ref_vector& es); bool extract_array_func_interp(model& mdl, expr* a, vector& stores, expr_ref& else_case); public: model_evaluator_array_util (ast_manager& m): m (m), m_array (m) {} /** * best effort evaluator of extensional array equality. */ void eval_array_eq(model& mdl, app* e, expr* arg1, expr* arg2, expr_ref& res); void eval(model& mdl, expr* e, expr_ref& r, bool model_completion = true); }; #endif z3-z3-4.8.7/src/muz/spacer/spacer_notes.txt000066400000000000000000000147551356505360400205500ustar00rootroot00000000000000a queue contains a model_node let n = leaves.pop_top () if (!n.has_derivation ()) if n.pt ().must_reach (n.post ()) add parent of n to the leaves return check abstract reachability of n if must reachable then create new reachability fact for n.pt () add parent of n to the leaves else if may reachable then create derivation d for n create model_node kid for the top of d add kid to the leaves else /* unreachable */ create a lemma for n.pt () p = parent of n p.reset_derivation() add p to the leaves else if (n.has_derivation ()) create next model_node kid for n.get_derivation () if (kid != NULL) add kid to leaves else /* done with the derivation, no more kids */ // the derivation is reachable, otherwise it was reset in another branch p = parent of n p.reset_derivation () add p to the leaves ================================================================================= create derivation for the top of d input: model M, transition relation formula trans with auxiliary variables quantified out sequence of pedicates P_i, may and must summaries of P_i ================================================================================= create first derivation child: input: model create next derivation child: create new model update trans by computing pre-image over new reachability facts call create next derivation child private: create next derivation child using a given model, and starting index ========================================================= create a next model for a derivation =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- // an obligation model_node // NULL means root model_node_ref parent model_node_ref_vector kids pred_transformer &predicate expr* condition unsigned level unsigned depth // monotonically increasing unsigned id bool open; model_node::close () open = false for k : kids do k.close () model_search model_node_ref root; // proof obligations priority_queue m_obligations; model_node_ref m_last_reachable; bool model_node::operator< (model_node& other) lexicographic order based on level<, depth<, id> assert (!m_last_reachable); while (!m_obligations.empty ()) { // propagate reachability as much as possible while (m_last_reachable) { obl = m_last_reachable m_last_reachable.reset (); if (is_root (obl)) return true; if (discharge_obligation (obl.get_parent ()) == l_true) m_last_reachable = obl.get_parent (); } // at least one obligation is not closed, ow root was reachable while (m_obligations.top ().is_closed ()) m_obligations.pop (); assert (!m_obligations.empty ()); // process an obligation assert (!m_last_reachable) obl = m_obligations.top (); switch (discharge_obligation (obl)) { case l_true: // if reachable, schedule a reachability round m_last_reachable = m_obligations.top (); m_obligations.pop (); break; case l_false: // if unreachable removed from the queue m_obligations.pop (); /// bump level obl.inc_level (); /// optionally insert back into the queue if (is_active (obl)) m_obligations.push (obl); break; default: assert (m_obligations.top () != obl); } } return false /// with priority queue bool is_active (model_node obl) { return level <= m_search.max_level (); } /// with out priority queue. Discharged obligations are dropped bool is_active (model_node obl) { return false; } discharge_obligation (model_node obl) { assert (!obl.is_closed ()); switch (check_reachability (obl)) { case l_true: obl.close () update reachability facts return l_true; case l_false: update lemmas return l_false case l_unknown: create children populate m_obligations queue return l_unknown } } ============================================================= a node keeps a derivation object if a node is sat, a new node is constructed and inherits the derivation object if a node is sat and the derivation is done, this is reported to the parent expand_node(n): process node ignoring derivation if sat: if concrete: if has derivation and has next child close current node and push new node return l_undef else return l_true else create_child (creates a new node and optionally sets derivation) else if unsat generate lemmas derivation remains unchanged to be used at a higher level return ====================================================================== 1. open disjunction for transition relation - a fresh literal to open the disjunction of the transition relation - expr* expand_init (expr *e) -- add e to initial state and return new disj var - close the disjunction by passing the negation of the literal during various calls - store the literal negated to have access to both positive and negative versions - with this, can do an optional check whether the lemmas alone are strong enough to discharge the counterexample. Easiest is to implement it as a separate pre-check. 2. auxiliary variables in lemmas and reach-facts. - store and expect auxiliary variables - quantify them out when necessary 3. initial rules as reach-facts - add initial rules of a predicate to its reach-facts. Propagate them to uses. - this way, the checks at level 0 will include initial rules of immediate predecessors ====================================================================== reach_fact_ref_vector m_reach_facts app_ref_vector m_reach_case_vars bool is_must_reachable (expr *state, model_ref *model) reach_fact* get_used_reach_fact (model_evaluator &mev) app* mk_fresh_reach_case_var () expr* get_reach () expr* get_last_reach_case_var () app* get_reach_case_var (unsigned idx) get_used_origin_reach_fact(): ====================================================================== 4. track relationship between an obligation and lemmas. Attempt to generalize an obligation into the exact lemma that worked before. Perhaps pick one lemma with highest level? Implement as core-generalizer. Will require reworking how legacy_frames is implemented. z3-z3-4.8.7/src/muz/spacer/spacer_pdr.cpp000066400000000000000000000243001356505360400201330ustar00rootroot00000000000000/**++ Copyright (c) 2018 Arie Gurfinkel Module Name: spacer_pdr.h Abstract: SPACER gPDR strategy implementation Author: Arie Gurfinkel Based on muz/pdr Notes: --*/ #include "muz/spacer/spacer_pdr.h" #include "muz/base/dl_context.h" #include "muz/spacer/spacer_mbc.h" namespace spacer { model_node::model_node(model_node* parent, class pob *pob): m_pob(pob), m_parent(parent), m_next(nullptr), m_prev(nullptr), m_orig_level(m_pob->level()), m_depth(0), m_closed(false) { SASSERT(m_pob); if (m_parent) m_parent->add_child(this); } void model_node::add_child(model_node* kid) { m_children.push_back(kid); SASSERT(level() == kid->level() + 1); SASSERT(level() > 0); kid->m_depth = m_depth + 1; if (is_closed()) set_open(); } unsigned model_node::index_in_parent() const { if (!m_parent) return 0; for (unsigned i = 0, sz = m_parent->children().size(); i < sz; ++i) { if (this == m_parent->children().get(i)) return i; } UNREACHABLE(); return 0; } void model_node::check_pre_closed() { for (auto *kid : m_children) {if (kid->is_open()) return;} set_pre_closed(); model_node* p = m_parent; while (p && p->is_1closed()) { p->set_pre_closed(); p = p->parent(); } } void model_node::set_open() { SASSERT(m_closed); m_closed = false; model_node* p = parent(); while (p && p->is_closed()) { p->m_closed = false; p = p->parent(); } } void model_node::detach(model_node*& qhead) { SASSERT(in_queue()); SASSERT(children().empty()); if (this == m_next) { SASSERT(m_prev == this); SASSERT(this == qhead); qhead = nullptr; } else { m_next->m_prev = m_prev; m_prev->m_next = m_next; if (this == qhead) qhead = m_next; } // detach m_prev = nullptr; m_next = nullptr; } // insert node n after this in the queue // requires: this is in a queue or this == n void model_node::insert_after(model_node* n) { SASSERT(this == n || in_queue()); SASSERT(n); SASSERT(!n->in_queue()); if (this == n) { m_next = n; m_prev = n; } else { n->m_next = m_next; m_next->m_prev = n; m_next = n; n->m_prev = this; } } void model_search::reset() { if (m_root) { erase_children(*m_root, false); remove_node(m_root, false); dealloc(m_root); m_root = nullptr; } m_cache.reset(); } model_node* model_search::pop_front() { model_node *res = m_qhead; if (res) { res->detach(m_qhead); } return res; } void model_search::add_leaf(model_node* _n) { model_node& n = *_n; SASSERT(n.children().empty()); model_nodes ns; model_nodes& nodes = cache(n).insert_if_not_there2(n.post(), ns)->get_data().m_value; if (nodes.contains(&n)) return; nodes.push_back(_n); if (nodes.size() == 1) { SASSERT(n.is_open()); enqueue_leaf(n); } else { n.set_pre_closed(); } } void model_search::enqueue_leaf(model_node& n) { SASSERT(n.is_open()); SASSERT(!n.in_queue()); // queue is empty, initialize it with n if (!m_qhead) { m_qhead = &n; m_qhead->insert_after(m_qhead); } // insert n after m_qhead else if (m_bfs) { m_qhead->insert_after(&n); } // insert n after m_qhead()->next() else { m_qhead->next()->insert_after(&n); } } void model_search::set_root(model_node* root) { reset(); m_root = root; SASSERT(m_root); SASSERT(m_root->children().empty()); add_leaf(root); } void model_search::backtrack_level(bool uses_level, model_node& n) { SASSERT(m_root); if (uses_level) {NOT_IMPLEMENTED_YET();} if (uses_level && m_root->level() > n.level()) { n.increase_level(); enqueue_leaf(n); } else { model_node* p = n.parent(); if (p) { erase_children(*p, true); enqueue_leaf(*p); } } } obj_map >& model_search::cache(model_node const& n) { unsigned l = n.orig_level(); if (l >= m_cache.size()) m_cache.resize(l + 1); return m_cache[l]; } void model_search::erase_children(model_node& n, bool backtrack) { ptr_vector todo, nodes; todo.append(n.children()); // detach n from queue if (n.in_queue()) n.detach(m_qhead); n.reset_children(); while (!todo.empty()) { model_node* m = todo.back(); todo.pop_back(); nodes.push_back(m); todo.append(m->children()); remove_node(m, backtrack); } std::for_each(nodes.begin(), nodes.end(), delete_proc()); } // removes node from the search tree and from the cache void model_search::remove_node(model_node* _n, bool backtrack) { model_node& n = *_n; model_nodes& nodes = cache(n).find(n.post()); nodes.erase(_n); if (n.in_queue()) n.detach(m_qhead); // TBD: siblings would also fail if n is not a goal. if (!nodes.empty() && backtrack && nodes[0]->children().empty() && nodes[0]->is_closed()) { model_node* n1 = nodes[0]; n1->set_open(); enqueue_leaf(*n1); } if (nodes.empty()) cache(n).remove(n.post()); } lbool context::gpdr_solve_core() { scoped_watch _w_(m_solve_watch); //if there is no query predicate, abort if (!m_rels.find(m_query_pred, m_query)) { return l_false; } model_search ms(m_pdr_bfs); unsigned lvl = 0; unsigned max_level = m_max_level; for (lvl = 0; lvl < max_level; ++lvl) { checkpoint(); IF_VERBOSE(1,verbose_stream() << "GPDR Entering level "<< lvl << "\n";); STRACE("spacer_progress", tout << "\n* LEVEL " << lvl << "\n";); m_expanded_lvl = infty_level(); m_stats.m_max_query_lvl = lvl; if (gpdr_check_reachability(lvl, ms)) {return l_true;} if (lvl > 0) { if (propagate(m_expanded_lvl, lvl, UINT_MAX)) {return l_false;} } } // communicate failure to datalog::context if (m_context) { m_context->set_status(datalog::BOUNDED); } return l_undef; } bool context::gpdr_check_reachability(unsigned lvl, model_search &ms) { pob_ref root_pob = m_query->mk_pob(nullptr, lvl, 0, m.mk_true()); model_node *root_node = alloc(model_node, nullptr, root_pob.get()); ms.set_root(root_node); pob_ref_buffer new_pobs; while (model_node *node = ms.pop_front()) { IF_VERBOSE(2, verbose_stream() << "Expand node: " << node->level() << "\n";); new_pobs.reset(); checkpoint(); pred_transformer &pt = node->pt(); // check reachable cache if (pt.is_must_reachable(node->pob()->post(), nullptr)) { TRACE("spacer", tout << "must-reachable: " << pt.head()->get_name() << " level: " << node->level() << " depth: " << node->depth () << "\n"; tout << mk_pp(node->pob()->post(), m) << "\n";); node->set_closed(); if (node == root_node) return true; continue; } switch (expand_pob(*node->pob(), new_pobs)){ case l_true: node->set_closed(); if (node == root_node) return true; break; case l_false: ms.backtrack_level(false, *node); if (node == root_node) return false; break; case l_undef: SASSERT(!new_pobs.empty()); for (auto pob : new_pobs) { TRACE("spacer_pdr", tout << "looking at pob at level " << pob->level() << " " << mk_pp(pob->post(), m) << "\n";); if (pob != node->pob()) ms.add_leaf(alloc(model_node, node, pob)); } node->check_pre_closed(); break; } } return root_node->is_closed(); } bool context::gpdr_create_split_children(pob &n, const datalog::rule &r, expr *trans, model &mdl, pob_ref_buffer &out) { pred_transformer &pt = n.pt(); ptr_vector preds; pt.find_predecessors(r, preds); SASSERT(preds.size() > 1); ptr_vector ppts; for (auto *p : preds) ppts.push_back(&get_pred_transformer(p)); mbc::partition_map pmap; for (unsigned i = 0, sz = preds.size(); i < sz; ++i) { func_decl *p = preds.get(i); pred_transformer &ppt = *ppts.get(i); for (unsigned j = 0, jsz = p->get_arity(); j < jsz; ++j) { pmap.insert(m_pm.o2o(ppt.sig(j), 0, i), i); } } spacer::mbc _mbc(m); expr_ref_vector lits(m); flatten_and(trans, lits); vector res(preds.size(), expr_ref_vector(m)); _mbc(pmap, lits, mdl, res); // pick an order to process children unsigned_vector kid_order; kid_order.resize(preds.size(), 0); for (unsigned i = 0, sz = preds.size(); i < sz; ++i) kid_order[i] = i; if (m_children_order == CO_REV_RULE) { kid_order.reverse(); } else if (m_children_order == CO_RANDOM) { shuffle(kid_order.size(), kid_order.c_ptr(), m_random); } for (unsigned i = 0, sz = res.size(); i < sz; ++i) { unsigned j = kid_order[i]; expr_ref post(m); pred_transformer &ppt = *ppts.get(j); post = mk_and(res.get(j)); m_pm.formula_o2n(post.get(), post, j, true); pob * k = ppt.mk_pob(&n, prev_level(n.level()), n.depth(), post); out.push_back(k); IF_VERBOSE (1, verbose_stream() << "\n\tcreate_child: " << k->pt().head()->get_name() << " (" << k->level() << ", " << k->depth() << ") " << (k->use_farkas_generalizer() ? "FAR " : "SUB ") << k->post()->get_id(); verbose_stream().flush();); TRACE ("spacer", tout << "create-pob: " << k->pt().head()->get_name() << " level: " << k->level() << " depth: " << k->depth () << " fvsz: " << k->get_free_vars_size() << "\n" << mk_pp(k->post(), m) << "\n";); } return true; } } // spacer z3-z3-4.8.7/src/muz/spacer/spacer_pdr.h000066400000000000000000000062551356505360400176110ustar00rootroot00000000000000/**++ Copyright (c) 2018 Arie Gurfinkel Module Name: spacer_pdr.h Abstract: SPACER gPDR strategy implementation Author: Arie Gurfinkel Based on muz/pdr Notes: --*/ #ifndef _SPACER_PDR_H_ #define _SPACER_PDR_H_ #include "muz/spacer/spacer_context.h" namespace spacer { // structure for counter-example search. class model_node { pob_ref m_pob; // proof obligation model_node* m_parent; // parent in the search tree ptr_vector m_children; // children in the search tree model_node* m_next; // next element of an in-place circular queue model_node* m_prev; // prev element of an in-place circular queue unsigned m_orig_level; // level at which this search node was created unsigned m_depth; // bool m_closed; // whether the pob is derivable public: model_node(model_node* parent, pob* pob); void add_child(model_node* kid); expr *post() const { return m_pob->post(); } unsigned level() const { return m_pob->level(); } unsigned orig_level() const { return m_orig_level; } unsigned depth() const { return m_depth; } void increase_level() { m_pob->inc_level(); } pob_ref &pob() { return m_pob; } ptr_vector const& children() { return m_children; } pred_transformer& pt() const { return m_pob->pt(); } model_node* parent() const { return m_parent; } // order in children of the parent unsigned index_in_parent() const; bool is_closed() const { return m_closed; } bool is_open() const { return !is_closed(); } // closed or has children and they are all closed bool is_1closed() { if (is_closed()) return true; if (m_children.empty()) return false; for (auto kid : m_children) if (kid->is_open()) return false; return true; } void check_pre_closed(); void set_pre_closed() { m_closed = true; } void set_closed() { m_closed = true; } void set_open(); void reset_children() { m_children.reset(); } /// queue // remove this node from the given queue void detach(model_node*& qhead); void insert_after(model_node* n); model_node* next() const { return m_next; } bool in_queue() { return m_next && m_prev; } }; class model_search { typedef ptr_vector model_nodes; bool m_bfs; model_node* m_root; model_node* m_qhead; vector > m_cache; obj_map& cache(model_node const& n); void erase_children(model_node& n, bool backtrack); void remove_node(model_node* _n, bool backtrack); public: model_search(bool bfs): m_bfs(bfs), m_root(nullptr), m_qhead(nullptr) {} ~model_search() {reset();} void set_root(model_node* n); void reset(); model_node* pop_front(); void add_leaf(model_node* n); // add fresh node. model_node& get_root() const { return *m_root; } void backtrack_level(bool uses_level, model_node& n); void remove_goal(model_node& n); void enqueue_leaf(model_node &n); }; } #endif z3-z3-4.8.7/src/muz/spacer/spacer_proof_utils.cpp000066400000000000000000000660111356505360400217200ustar00rootroot00000000000000/*++ Copyright (c) 2017 Arie Gurfinkel Module Name: spacer_proof_utils.cpp Abstract: Utilities to traverse and manipulate proofs Author: Bernhard Gleiss Arie Gurfinkel Revision History: --*/ #include "util/params.h" #include "ast/ast_pp.h" #include "ast/ast_util.h" #include "ast/proofs/proof_checker.h" #include "muz/base/dl_util.h" #include "muz/spacer/spacer_iuc_proof.h" #include "ast/proofs/proof_utils.h" #include "muz/spacer/spacer_proof_utils.h" #include "muz/spacer/spacer_util.h" namespace spacer { // arithmetic lemma recognizer bool is_arith_lemma(ast_manager& m, proof* pr) { // arith lemmas: second parameter specifies exact type of lemma, // could be "farkas", "triangle-eq", "eq-propagate", // "assign-bounds", maybe also something else if (pr->get_decl_kind() == PR_TH_LEMMA) { func_decl* d = pr->get_decl(); symbol sym; return d->get_num_parameters() >= 1 && d->get_parameter(0).is_symbol(sym) && sym == "arith"; } return false; } // farkas lemma recognizer bool is_farkas_lemma(ast_manager& m, proof* pr) { if (pr->get_decl_kind() == PR_TH_LEMMA) { func_decl* d = pr->get_decl(); symbol sym; return d->get_num_parameters() >= 2 && d->get_parameter(0).is_symbol(sym) && sym == "arith" && d->get_parameter(1).is_symbol(sym) && sym == "farkas"; } return false; } static bool is_assign_bounds_lemma(ast_manager &m, proof *pr) { if (pr->get_decl_kind() == PR_TH_LEMMA) { func_decl* d = pr->get_decl(); symbol sym; return d->get_num_parameters() >= 2 && d->get_parameter(0).is_symbol(sym) && sym == "arith" && d->get_parameter(1).is_symbol(sym) && sym == "assign-bounds"; } return false; } class linear_combinator { struct scaled_lit { bool is_pos; app *lit; rational coeff; scaled_lit(bool is_pos, app *lit, const rational &coeff) : is_pos(is_pos), lit(lit), coeff(coeff) {} }; ast_manager &m; th_rewriter m_rw; arith_util m_arith; expr_ref m_sum; bool m_is_strict; rational m_lc; vector m_lits; public: linear_combinator(ast_manager &m) : m(m), m_rw(m), m_arith(m), m_sum(m), m_is_strict(false), m_lc(1) {} void add_lit(app* lit, rational const &coeff, bool is_pos = true) { m_lits.push_back(scaled_lit(is_pos, lit, coeff)); } void normalize_coeff() { for (auto &lit : m_lits) m_lc = lcm(m_lc, denominator(lit.coeff)); if (!m_lc.is_one()) { for (auto &lit : m_lits) lit.coeff *= m_lc; } } rational const &lc() const {return m_lc;} bool process_lit(scaled_lit &lit0) { arith_util a(m); app* lit = lit0.lit; rational &coeff = lit0.coeff; bool is_pos = lit0.is_pos; if (m.is_not(lit)) { lit = to_app(lit->get_arg(0)); is_pos = !is_pos; } if (!m_arith.is_le(lit) && !m_arith.is_lt(lit) && !m_arith.is_ge(lit) && !m_arith.is_gt(lit) && !m.is_eq(lit)) { return false; } SASSERT(lit->get_num_args() == 2); sort* s = m.get_sort(lit->get_arg(0)); bool is_int = m_arith.is_int(s); if (!is_int && m_arith.is_int_expr(lit->get_arg(0))) { is_int = true; s = m_arith.mk_int(); } if (!is_int && is_pos && (m_arith.is_gt(lit) || m_arith.is_lt(lit))) { m_is_strict = true; } if (!is_int && !is_pos && (m_arith.is_ge(lit) || m_arith.is_le(lit))) { m_is_strict = true; } SASSERT(m_arith.is_int(s) || m_arith.is_real(s)); expr_ref sign1(m), sign2(m), term(m); sign1 = m_arith.mk_numeral(m.is_eq(lit)?coeff:abs(coeff), s); sign2 = m_arith.mk_numeral(m.is_eq(lit)?-coeff:-abs(coeff), s); if (!m_sum.get()) { m_sum = m_arith.mk_numeral(rational(0), s); } expr* a0 = lit->get_arg(0); expr* a1 = lit->get_arg(1); if (is_pos && (m_arith.is_ge(lit) || m_arith.is_gt(lit))) { std::swap(a0, a1); } if (!is_pos && (m_arith.is_le(lit) || m_arith.is_lt(lit))) { std::swap(a0, a1); } // // Multiplying by coefficients over strict // and non-strict inequalities: // // (a <= b) * 2 // (a - b <= 0) * 2 // (2a - 2b <= 0) // (a < b) * 2 <=> // (a +1 <= b) * 2 <=> // 2a + 2 <= 2b <=> // 2a+2-2b <= 0 bool strict_ineq = is_pos?(m_arith.is_gt(lit) || m_arith.is_lt(lit)):(m_arith.is_ge(lit) || m_arith.is_le(lit)); if (is_int && strict_ineq) { m_sum = m_arith.mk_add(m_sum, sign1); } term = m_arith.mk_mul(sign1, a0); m_sum = m_arith.mk_add(m_sum, term); term = m_arith.mk_mul(sign2, a1); m_sum = m_arith.mk_add(m_sum, term); m_rw(m_sum); return true; } expr_ref operator()(){ if (!m_sum) normalize_coeff(); m_sum.reset(); for (auto &lit : m_lits) { if (!process_lit(lit)) return expr_ref(m); } return m_sum; } }; /* * ==================================== * methods for transforming proofs * ==================================== */ void theory_axiom_reducer::reset() { m_cache.reset(); m_pinned.reset(); } static proof_ref mk_th_lemma(ast_manager &m, ptr_buffer const &parents, unsigned num_params, parameter const *params) { buffer v; for (unsigned i = 1; i < num_params; ++i) v.push_back(params[i]); SASSERT(params[0].is_symbol()); family_id tid = m.mk_family_id(params[0].get_symbol()); SASSERT(tid != null_family_id); proof_ref pf(m); pf = m.mk_th_lemma(tid, m.mk_false(), parents.size(), parents.c_ptr(), v.size(), v.c_ptr()); return pf; } static bool match_mul(expr *e, expr_ref &var, expr_ref &val, arith_util &a) { expr *e1 = nullptr, *e2 = nullptr; if (!a.is_mul(e, e1, e2)) { if (a.is_numeral(e)) return false; if (!var || var == e) { var = e; val = a.mk_numeral(rational(1), get_sort(e)); return true; } return false; } if (!a.is_numeral(e1)) std::swap(e1, e2); if (!a.is_numeral(e1)) return false; // if variable is given, match it as well if (!var || var == e2) { var = e2; val = e1; return true; } return false; } static expr_ref get_coeff(expr *lit0, expr_ref &var) { ast_manager &m = var.m(); arith_util a(m); expr *lit = nullptr; if (!m.is_not(lit0, lit)) lit = lit0; expr *e1 = nullptr, *e2 = nullptr; // assume e2 is numeral and ignore it if ((a.is_le(lit, e1, e2) || a.is_lt(lit, e1, e2) || a.is_ge(lit, e1, e2) || a.is_gt(lit, e1, e2) || m.is_eq(lit, e1, e2))) { if (a.is_numeral(e1)) std::swap(e1, e2); } else { e1 = lit; } expr_ref val(m); if (!a.is_add(e1)) { if (match_mul(e1, var, val, a)) return val; } else { for (auto *arg : *to_app(e1)) { if (match_mul(arg, var, val, a)) return val; } } return expr_ref(m); } // convert assign-bounds lemma to a farkas lemma by adding missing coeff // assume that missing coeff is for premise at position 0 static proof_ref mk_fk_from_ab(ast_manager &m, ptr_buffer const &parents, unsigned num_params, parameter const *params) { SASSERT(num_params == parents.size() + 1 /* one param is missing */); arith_util a(m); th_rewriter rw(m); // compute missing coefficient linear_combinator lcb(m); for (unsigned i = 1, sz = parents.size(); i < sz; ++i) { app *p = to_app(m.get_fact(parents.get(i))); rational const &r = params[i+1].get_rational(); lcb.add_lit(p, r); } expr_ref lit0(m); lit0 = m.get_fact(parents.get(0)); // put lit0 into canonical form rw(lit0); TRACE("spacer.fkab", tout << "lit0 is: " << lit0 << "\n" << "LCB is: " << lcb() << "\n";); expr_ref var(m), val1(m), val2(m); val1 = get_coeff(lit0, var); val2 = get_coeff(lcb(), var); TRACE("spacer.fkab", tout << "var: " << var << " val1: " << val1 << " val2: " << val2 << "\n";); rational rat1, rat2, coeff0; CTRACE("spacer.fkab", !(val1 && val2), tout << "Failed to match variables\n";); if (val1 && val2 && a.is_numeral(val1, rat1) && a.is_numeral(val2, rat2)) { coeff0 = abs(rat2/rat1); coeff0 = coeff0 / lcb.lc(); TRACE("spacer.fkab", tout << "coeff0: " << coeff0 << "\n";); } else { IF_VERBOSE(1, verbose_stream() << "\n\n\nFAILED TO FIND COEFFICIENT\n\n\n";); TRACE("spacer.fkab", tout << "FAILED TO FIND COEFFICIENT\n";); // failed to find a coefficient return proof_ref(m); } buffer v; v.push_back(parameter(symbol("farkas"))); v.push_back(parameter(coeff0)); for (unsigned i = 2; i < num_params; ++i) v.push_back(params[i]); SASSERT(params[0].is_symbol()); family_id tid = m.mk_family_id(params[0].get_symbol()); SASSERT(tid != null_family_id); proof_ref pf(m); pf = m.mk_th_lemma(tid, m.mk_false(), parents.size(), parents.c_ptr(), v.size(), v.c_ptr()); SASSERT(is_arith_lemma(m, pf)); DEBUG_CODE( proof_checker pc(m); expr_ref_vector side(m); ENSURE(pc.check(pf, side));); return pf; } /// -- rewrite theory axioms into theory lemmas proof_ref theory_axiom_reducer::reduce(proof* pr) { proof_post_order pit(pr, m); while (pit.hasNext()) { proof* p = pit.next(); if (m.get_num_parents(p) == 0 && is_arith_lemma(m, p)) { // we have an arith-theory-axiom and want to get rid of it // we need to replace the axiom with // (a) corresponding hypothesis, // (b) a theory lemma, and // (c) a lemma. // Furthermore update data-structures app *fact = to_app(m.get_fact(p)); ptr_buffer cls; if (m.is_or(fact)) { for (unsigned i = 0, sz = fact->get_num_args(); i < sz; ++i) cls.push_back(fact->get_arg(i)); } else cls.push_back(fact); // (a) create hypothesis ptr_buffer hyps; for (unsigned i = 0, sz = cls.size(); i < sz; ++i) { expr *c; expr_ref hyp_fact(m); if (m.is_not(cls[i], c)) hyp_fact = c; else hyp_fact = m.mk_not (cls[i]); proof* hyp = m.mk_hypothesis(hyp_fact); m_pinned.push_back(hyp); hyps.push_back(hyp); } // (b) Create a theory lemma proof_ref th_lemma(m); func_decl *d = p->get_decl(); if (is_assign_bounds_lemma(m, p)) { th_lemma = mk_fk_from_ab(m, hyps, d->get_num_parameters(), d->get_parameters()); } // fall back to th-lemma if (!th_lemma) { th_lemma = mk_th_lemma(m, hyps, d->get_num_parameters(), d->get_parameters()); } m_pinned.push_back(th_lemma); SASSERT(is_arith_lemma(m, th_lemma)); // (c) create lemma proof* res = m.mk_lemma(th_lemma, fact); m_pinned.push_back(res); m_cache.insert(p, res); SASSERT(m.get_fact(res) == m.get_fact(p)); } else { // proof is dirty, if a sub-proof of one of its premises // has been transformed bool dirty = false; ptr_buffer args; for (unsigned i = 0, sz = m.get_num_parents(p); i < sz; ++i) { proof *pp, *tmp; pp = m.get_parent(p, i); VERIFY(m_cache.find(pp, tmp)); args.push_back(tmp); dirty |= (pp != tmp); } // if not dirty just use the old step if (!dirty) m_cache.insert(p, p); // otherwise create new proof with the corresponding proofs // of the premises else { if (m.has_fact(p)) args.push_back(m.get_fact(p)); SASSERT(p->get_decl()->get_arity() == args.size()); proof* res = m.mk_app(p->get_decl(), args.size(), (expr * const*)args.c_ptr()); m_pinned.push_back(res); m_cache.insert(p, res); } } } proof* res; VERIFY(m_cache.find(pr,res)); DEBUG_CODE( proof_checker pc(m); expr_ref_vector side(m); SASSERT(pc.check(res, side)); ); return proof_ref(res, m); } /* ------------------------------------------------------------------------- */ /* hypothesis_reducer */ /* ------------------------------------------------------------------------- */ proof_ref hypothesis_reducer::reduce(proof* pr) { compute_hypsets(pr); collect_units(pr); proof_ref res(reduce_core(pr), m); SASSERT(res); reset(); DEBUG_CODE(proof_checker pc(m); expr_ref_vector side(m); SASSERT(pc.check(res, side));); return res; } void hypothesis_reducer::reset() { m_active_hyps.reset(); m_units.reset(); m_cache.reset(); for (auto t : m_pinned_active_hyps) dealloc(t); m_pinned_active_hyps.reset(); m_pinned.reset(); m_hyp_mark.reset(); m_open_mark.reset(); m_visited.reset(); } void hypothesis_reducer::compute_hypsets(proof *pr) { ptr_buffer todo; todo.push_back(pr); while (!todo.empty()) { proof* p = todo.back(); if (m_visited.is_marked(p)) { todo.pop_back(); continue; } unsigned todo_sz = todo.size(); for (unsigned i = 0, sz = m.get_num_parents(p); i < sz; ++i) { SASSERT(m.is_proof(p->get_arg(i))); proof *parent = to_app(p->get_arg(i)); if (!m_visited.is_marked(parent)) todo.push_back(parent); } if (todo.size() > todo_sz) continue; todo.pop_back(); m_visited.mark(p); proof_ptr_vector* active_hyps = nullptr; // fill both sets if (m.is_hypothesis(p)) { // create active_hyps-set for step p proof_ptr_vector* active_hyps = alloc(proof_ptr_vector); m_pinned_active_hyps.insert(active_hyps); m_active_hyps.insert(p, active_hyps); active_hyps->push_back(p); m_open_mark.mark(p); m_hyp_mark.mark(m.get_fact(p)); continue; } ast_fast_mark1 seen; active_hyps = alloc(proof_ptr_vector); for (unsigned i = 0, sz = m.get_num_parents(p); i < sz; ++i) { proof* parent = m.get_parent(p, i); // lemmas clear all hypotheses above them if (m.is_lemma(p)) continue; for (auto *x : *m_active_hyps.find(parent)) { if (!seen.is_marked(x)) { seen.mark(x); active_hyps->push_back(x); m_open_mark.mark(p); } } } if (active_hyps->empty()) { dealloc(active_hyps); m_active_hyps.insert(p, &m_empty_vector); } else { m_pinned_active_hyps.push_back(active_hyps); m_active_hyps.insert(p, active_hyps); } } } // collect all units that are hyp-free and are used as hypotheses somewhere // requires that m_active_hyps has been computed void hypothesis_reducer::collect_units(proof* pr) { proof_post_order pit(pr, m); while (pit.hasNext()) { proof* p = pit.next(); if (!m.is_hypothesis(p)) { // collect units that are hyp-free and are used as // hypotheses in the proof pr if (!m_open_mark.is_marked(p) && m.has_fact(p) && m_hyp_mark.is_marked(m.get_fact(p))) m_units.insert(m.get_fact(p), p); } } } /** \brief returns true if p is an ancestor of q */ bool hypothesis_reducer::is_ancestor(proof *p, proof *q) { if (p == q) return true; ptr_vector todo; todo.push_back(q); expr_mark visited; while (!todo.empty()) { proof *cur; cur = todo.back(); todo.pop_back(); if (visited.is_marked(cur)) continue; if (cur == p) return true; visited.mark(cur); for (unsigned i = 0, sz = m.get_num_parents(cur); i < sz; ++i) { todo.push_back(m.get_parent(cur, i)); } } return false; } proof* hypothesis_reducer::reduce_core(proof* pf) { SASSERT(m.is_false(m.get_fact(pf))); proof *res = nullptr; ptr_vector todo; todo.push_back(pf); ptr_buffer args; bool dirty = false; while (true) { proof *p, *tmp, *pp; unsigned todo_sz; p = todo.back(); if (m_cache.find(p, tmp)) { todo.pop_back(); continue; } dirty = false; args.reset(); todo_sz = todo.size(); for (unsigned i = 0, sz = m.get_num_parents(p); i < sz; ++i) { pp = m.get_parent(p, i); if (m_cache.find(pp, tmp)) { args.push_back(tmp); dirty |= pp != tmp; } else { todo.push_back(pp); } } if (todo_sz < todo.size()) continue; todo.pop_back(); // transform the current proof node if (m.is_hypothesis(p)) { // if possible, replace a hypothesis by a unit derivation if (m_units.find(m.get_fact(p), tmp)) { // use already transformed proof of the unit if it is available proof* proof_of_unit; if (!m_cache.find(tmp, proof_of_unit)) { proof_of_unit = tmp; } // make sure hypsets for the unit are computed // AG: is this needed? compute_hypsets(proof_of_unit); // if the transformation doesn't create a cycle, perform it if (!is_ancestor(p, proof_of_unit)) { res = proof_of_unit; } else { // -- failed to transform the proof, perhaps bad // -- choice of the proof of unit res = p; } } else { // -- no unit found to replace the hypothesis res = p; } } else if (!dirty) {res = p;} else if (m.is_lemma(p)) { // lemma: reduce the premise; remove reduced consequences // from conclusion SASSERT(args.size() == 1); res = mk_lemma_core(args[0], m.get_fact(p)); // -- re-compute hypsets compute_hypsets(res); } else if (m.is_unit_resolution(p)) { // unit: reduce untis; reduce the first premise; rebuild // unit resolution res = mk_unit_resolution_core(p, args); // -- re-compute hypsets compute_hypsets(res); } else { res = mk_proof_core(p, args); // -- re-compute hypsets compute_hypsets(res); } SASSERT(res); m_cache.insert(p, res); // bail out as soon as found a sub-proof of false if (!m_open_mark.is_marked(res) && m.has_fact(res) && m.is_false(m.get_fact(res))) return res; } UNREACHABLE(); return nullptr; } proof* hypothesis_reducer::mk_lemma_core(proof* premise, expr *fact) { SASSERT(m.is_false(m.get_fact(premise))); SASSERT(m_active_hyps.contains(premise)); proof_ptr_vector* active_hyps = m_active_hyps.find(premise); // if there is no active hypothesis return the premise if (!m_open_mark.is_marked(premise)) { // XXX just in case premise might go away m_pinned.push_back(premise); return premise; } // add some stability std::stable_sort(active_hyps->begin(), active_hyps->end(), ast_lt_proc()); // otherwise, build a disjunction of the negated active hypotheses // and add a lemma proof step expr_ref_buffer args(m); for (auto hyp : *active_hyps) { expr *hyp_fact, *t; hyp_fact = m.get_fact(hyp); if (m.is_not(hyp_fact, t)) args.push_back(t); else args.push_back(m.mk_not(hyp_fact)); } expr_ref lemma(m); lemma = mk_or(m, args.size(), args.c_ptr()); proof* res; res = m.mk_lemma(premise, lemma); m_pinned.push_back(res); return res; } proof* hypothesis_reducer::mk_unit_resolution_core(proof *ures, ptr_buffer& args) { // if any literal is false, we don't need a unit resolution step // This can be the case due to some previous transformations for (unsigned i = 1, sz = args.size(); i < sz; ++i) { if (m.is_false(m.get_fact(args[i]))) { // XXX pin just in case m_pinned.push_back(args[i]); return args[i]; } } proof* arg0 = args[0]; app *fact0 = to_app(m.get_fact(arg0)); ptr_buffer pf_args; ptr_buffer pf_fact; pf_args.push_back(arg0); // compute literals to be resolved ptr_buffer lits; // fact0 is a literal whenever the original resolution was a // binary resolution to an empty clause if (m.get_num_parents(ures) == 2 && m.is_false(m.get_fact(ures))) { lits.push_back(fact0); } // fact0 is a literal unless it is a dijsunction else if (!m.is_or(fact0)) { lits.push_back(fact0); } // fact0 is a literal only if it appears as a literal in the // original resolution else { lits.reset(); app* ures_fact = to_app(m.get_fact(m.get_parent(ures, 0))); for (unsigned i = 0, sz = ures_fact->get_num_args(); i < sz; ++i) { if (ures_fact->get_arg(i) == fact0) { lits.push_back(fact0); break; } } if (lits.empty()) { lits.append(fact0->get_num_args(), fact0->get_args()); } } // -- find all literals that are resolved on for (unsigned i = 0, sz = lits.size(); i < sz; ++i) { bool found = false; for (unsigned j = 1; j < args.size(); ++j) { if (m.is_complement(lits.get(i), m.get_fact(args[j]))) { found = true; pf_args.push_back(args[j]); break; } } if (!found) {pf_fact.push_back(lits.get(i));} } // unit resolution got reduced to noop if (pf_args.size() == 1) { // XXX pin just in case m_pinned.push_back(arg0); return arg0; } // make unit resolution proof step // expr_ref tmp(m); // tmp = mk_or(m, pf_fact.size(), pf_fact.c_ptr()); // proof* res = m.mk_unit_resolution(pf_args.size(), pf_args.c_ptr(), tmp); proof *res = m.mk_unit_resolution(pf_args.size(), pf_args.c_ptr()); m_pinned.push_back(res); return res; } proof* hypothesis_reducer::mk_proof_core(proof* old, ptr_buffer& args) { // if any of the literals are false, we don't need a step for (unsigned i = 0; i < args.size(); ++i) { if (m.is_false(m.get_fact(args[i]))) { // XXX just in case m_pinned.push_back(args[i]); return args[i]; } } // otherwise build step // BUG: I guess this doesn't work with quantifiers (since they are no apps) args.push_back(to_app(m.get_fact(old))); SASSERT(old->get_decl()->get_arity() == args.size()); proof* res = m.mk_app(old->get_decl(), args.size(), (expr * const*)args.c_ptr()); m_pinned.push_back(res); return res; } }; z3-z3-4.8.7/src/muz/spacer/spacer_proof_utils.h000066400000000000000000000047721356505360400213730ustar00rootroot00000000000000/*++ Copyright (c) 2017 Arie Gurfinkel Module Name: spacer_proof_utils.cpp Abstract: Utilities to traverse and manipulate proofs Author: Bernhard Gleiss Arie Gurfinkel Revision History: --*/ #ifndef _SPACER_PROOF_UTILS_H_ #define _SPACER_PROOF_UTILS_H_ #include "ast/ast.h" namespace spacer { bool is_arith_lemma(ast_manager& m, proof* pr); bool is_farkas_lemma(ast_manager& m, proof* pr); /// rewrites theory axioms into theory lemmas class theory_axiom_reducer { public: theory_axiom_reducer(ast_manager& m) : m(m), m_pinned(m) {} // reduce theory axioms and return transformed proof proof_ref reduce(proof* pr); private: ast_manager &m; // tracking all created expressions expr_ref_vector m_pinned; // maps each proof of a clause to the transformed subproof of // that clause obj_map m_cache; void reset(); }; /// reduces the number of hypotheses in a proof class hypothesis_reducer { public: hypothesis_reducer(ast_manager &m) : m(m), m_pinned(m) {} ~hypothesis_reducer() {reset();} // reduce hypothesis and return transformed proof proof_ref reduce(proof* pf); private: typedef ptr_vector proof_ptr_vector; ast_manager &m; proof_ptr_vector m_empty_vector; // created expressions expr_ref_vector m_pinned; // created sets of active hypothesis ptr_vector m_pinned_active_hyps; // maps a proof to the transformed proof obj_map m_cache; // maps a unit literal to its derivation obj_map m_units; // maps a proof node to the set of its active (i.e., in scope) hypotheses obj_map m_active_hyps; /// marks if an expression is ever used as a hypothesis in a proof expr_mark m_hyp_mark; /// marks a proof as open, i.e., has a non-discharged hypothesis as ancestor expr_mark m_open_mark; expr_mark m_visited; void reset(); /// true if p is an ancestor of q bool is_ancestor(proof *p, proof *q); // compute active_hyps and parent_hyps for a given proof node and // all its ancestors void compute_hypsets(proof* pr); // compute m_units void collect_units(proof* pr); // -- rewrite proof to reduce number of hypotheses used proof* reduce_core(proof* pf); proof* mk_lemma_core(proof *pf, expr *fact); proof* mk_unit_resolution_core(proof* ures, ptr_buffer& args); proof* mk_proof_core(proof* old, ptr_buffer& args); }; } #endif z3-z3-4.8.7/src/muz/spacer/spacer_prop_solver.cpp000066400000000000000000000311351356505360400217240ustar00rootroot00000000000000/** Copyright (c) 2017 Arie Gurfinkel Module Name: spacer_prop_solver.cpp Abstract: SAT solver abstraction for SPACER. Author: Arie Gurfinkel Anvesh Komuravelli Revision History: --*/ #include "ast/ast_smt2_pp.h" #include "ast/datatype_decl_plugin.h" #include "ast/bv_decl_plugin.h" #include "ast/rewriter/expr_replacer.h" #include "smt/params/smt_params.h" #include "model/model.h" #include "model/model_pp.h" #include "muz/base/dl_util.h" #include "muz/spacer/spacer_util.h" #include "muz/spacer/spacer_farkas_learner.h" #include "muz/spacer/spacer_prop_solver.h" #include "model/model_evaluator.h" #include "muz/base/fp_params.hpp" namespace spacer { prop_solver::prop_solver(ast_manager &m, solver *solver0, solver *solver1, fp_params const& p, symbol const& name) : m(m), m_name(name), m_ctx(nullptr), m_pos_level_atoms(m), m_neg_level_atoms(m), m_core(nullptr), m_subset_based_core(false), m_uses_level(infty_level()), m_delta_level(false), m_in_level(false), m_use_push_bg(p.spacer_keep_proxy()) { m_random.set_seed(p.spacer_random_seed()); m_solvers[0] = solver0; m_solvers[1] = solver1; m_contexts[0] = alloc(spacer::iuc_solver, *(m_solvers[0]), p.spacer_iuc(), p.spacer_iuc_arith(), p.spacer_iuc_print_farkas_stats(), p.spacer_iuc_old_hyp_reducer(), p.spacer_iuc_split_farkas_literals()); m_contexts[1] = alloc(spacer::iuc_solver, *(m_solvers[1]), p.spacer_iuc(), p.spacer_iuc_arith(), p.spacer_iuc_print_farkas_stats(), p.spacer_iuc_old_hyp_reducer(), p.spacer_iuc_split_farkas_literals()); } void prop_solver::add_level() { unsigned idx = level_cnt(); std::stringstream name; name << m_name << "#level_" << idx; func_decl * lev_pred = m.mk_fresh_func_decl(name.str().c_str(), 0, nullptr, m.mk_bool_sort()); m_level_preds.push_back(lev_pred); app_ref pos_la(m.mk_const(lev_pred), m); app_ref neg_la(m.mk_not(pos_la.get()), m); m_pos_level_atoms.push_back(pos_la); m_neg_level_atoms.push_back(neg_la); m_level_atoms_set.insert(pos_la.get()); m_level_atoms_set.insert(neg_la.get()); } void prop_solver::ensure_level(unsigned lvl) { if (is_infty_level(lvl)) return; while (lvl >= level_cnt()) { add_level(); } } unsigned prop_solver::level_cnt() const { return m_level_preds.size(); } void prop_solver::assert_level_atoms(unsigned level) { unsigned lev_cnt = level_cnt(); for (unsigned i = 0; i < lev_cnt; i++) { bool active = m_delta_level ? i == level : i >= level; app * lev_atom = active ? m_neg_level_atoms.get(i) : m_pos_level_atoms.get(i); m_ctx->push_bg(lev_atom); } } void prop_solver::assert_expr(expr * form) { SASSERT(!m_in_level); m_contexts[0]->assert_expr(form); m_contexts[1]->assert_expr(form); IF_VERBOSE(21, verbose_stream() << "$ asserted " << mk_pp(form, m) << "\n";); TRACE("spacer", tout << "add_formula: " << mk_pp(form, m) << "\n";); } void prop_solver::assert_expr(expr * form, unsigned level) { if (is_infty_level(level)) {assert_expr(form);return;} ensure_level(level); app * lev_atom = m_pos_level_atoms[level].get(); app_ref lform(m.mk_or(form, lev_atom), m); assert_expr(lform); } /// Local model guided maxsmt lbool prop_solver::mss(expr_ref_vector &hard, expr_ref_vector &soft) { // replace expressions by assumption literals iuc_solver::scoped_mk_proxy _p_(*m_ctx, hard); unsigned hard_sz = hard.size(); lbool res = m_ctx->check_sat(hard.size(), hard.c_ptr()); // bail out if hard constraints are not sat, or if there are no // soft constraints if (res != l_true || soft.empty()) {return res;} // the main loop model_ref mdl; m_ctx->get_model(mdl); // don't proxy soft literals. Assume that they are propositional. hard.append(soft); soft.reset(); // hard is divided into 4 regions // x < hard_sz ---> hard constraints // hard_sz <= x < i ---> sat soft constraints // i <= x < j ---> backbones (unsat soft constraints) // j <= x < hard.size() ---> unprocessed soft constraints unsigned i, j; i = hard_sz; j = hard_sz; while (j < hard.size()) { model_evaluator mev(*mdl); // move all true soft constraints to [hard_sz, i) for (unsigned k = j; k < hard.size(); ++k) { expr_ref e(m); e = hard.get(k); if (!mev.is_false(e) /* true or unset */) { expr_ref tmp(m); tmp = hard.get(i); hard[i] = e; if (i < j) { // tmp is a backbone, put it at j if (j == k) {hard[j] = tmp;} else /* j < k */ { e = hard.get(j); hard[j] = tmp; hard[k] = e; } j++; } else { // there are no backbone literals hard[k] = tmp; j++; } i++; } } // done with the model. Reset to avoid confusion in debugging mdl.reset(); // -- grow the set of backbone literals for (;j < hard.size(); ++j) { res = m_ctx->check_sat(j+1, hard.c_ptr()); if (res == l_false) { // -- flip non-true literal to be false hard[j] = mk_not(m, hard.get(j)); } else if (res == l_true) { // -- get the model for the next iteration of the outer loop m_ctx->get_model(mdl); break; } else if (res == l_undef) { // -- conservatively bail out hard.resize(hard_sz); return l_undef; } } } // move sat soft constraints to the output vector for (unsigned k = i; k < j; ++k) { soft.push_back(hard.get(k)); } // cleanup hard constraints hard.resize(hard_sz); return l_true; } /// Poor man's maxsat. No guarantees of maximum solution /// Runs maxsat loop on m_ctx Returns l_false if hard is unsat, /// otherwise reduces soft such that hard & soft is sat. lbool prop_solver::maxsmt(expr_ref_vector &hard, expr_ref_vector &soft, vector const & clauses) { // replace expressions by assumption literals iuc_solver::scoped_mk_proxy _p_(*m_ctx, hard); unsigned hard_sz = hard.size(); // assume soft constraints are propositional literals (no need to proxy) hard.append(soft); lbool res = m_ctx->check_sat_cc(hard, clauses); // if hard constraints alone are unsat or there are no soft // constraints, we are done if (res != l_false || soft.empty()) { return res; } // clear soft constraints, we will recompute them later soft.reset(); expr_ref saved(m); expr_ref_vector core(m); m_ctx->get_unsat_core(core); // while there are soft constraints while (hard.size() > hard_sz) { bool found = false; // look for a soft constraint that is in the unsat core for (unsigned i = hard_sz, sz = hard.size(); i < sz; ++i) if (core.contains(hard.get(i))) { found = true; // AG: not sure why we are saving it saved = hard.get(i); hard[i] = hard.back(); hard.pop_back(); break; } // if no soft constraints in the core, return this should // not happen because it implies that hard alone is unsat // and that is taken care of earlier if (!found) { hard.resize(hard_sz); return l_false; } // check that the NEW constraints became sat res = m_ctx->check_sat_cc(hard, clauses); if (res != l_false) { break; } // still unsat, update the core and repeat core.reset(); m_ctx->get_unsat_core(core); } // update soft with found soft constraints if (res == l_true) { for (unsigned i = hard_sz, sz = hard.size(); i < sz; ++i) { soft.push_back(hard.get(i)); } } // revert hard back to the right size // proxies are undone on exit via scoped_mk_proxy hard.resize(hard_sz); return res; } lbool prop_solver::internal_check_assumptions(expr_ref_vector &hard_atoms, expr_ref_vector &soft_atoms, vector const & clauses) { // XXX Turn model generation if m_model != 0 SASSERT(m_ctx); params_ref p; if (m_model != nullptr) { p.set_bool("produce_models", true); m_ctx->updt_params(p); } if (m_in_level) { assert_level_atoms(m_current_level); } lbool result = maxsmt(hard_atoms, soft_atoms, clauses); if (result != l_false && m_model) { m_ctx->get_model(*m_model); } SASSERT(result != l_false || soft_atoms.empty()); /// compute level used in the core // XXX this is a poor approximation because the core will get minimized further if (result == l_false) { ptr_vector core; m_ctx->get_full_unsat_core(core); unsigned core_size = core.size(); m_uses_level = infty_level(); for (unsigned i = 0; i < core_size; ++i) { if (m_level_atoms_set.contains(core[i])) { unsigned sz = std::min(m_uses_level, m_neg_level_atoms.size()); for (unsigned j = 0; j < sz; ++j) if (m_neg_level_atoms [j].get() == core[i]) { m_uses_level = j; break; } SASSERT(!is_infty_level(m_uses_level)); } } } if (result == l_false && m_core && m.proofs_enabled() && !m_subset_based_core) { TRACE("spacer", tout << "Using IUC core\n";); m_core->reset(); m_ctx->get_iuc(*m_core); } else if (result == l_false && m_core) { m_core->reset(); m_ctx->get_unsat_core(*m_core); // manually undo proxies because maxsmt() call above manually adds proxies // AG: don't think this is needed. maxsmt() undoes the proxies already m_ctx->undo_proxies(*m_core); } if (m_model != nullptr) { p.set_bool("produce_models", false); m_ctx->updt_params(p); } return result; } lbool prop_solver::check_assumptions(const expr_ref_vector & _hard, expr_ref_vector& soft, const expr_ref_vector &clause, unsigned num_bg, expr * const * bg, unsigned solver_id) { expr_ref cls(m); // current clients expect that flattening of HARD is // done implicitly during check_assumptions expr_ref_vector hard(m); hard.append(_hard.size(), _hard.c_ptr()); flatten_and(hard); shuffle(hard.size(), hard.c_ptr(), m_random); m_ctx = m_contexts [solver_id == 0 ? 0 : 0 /* 1 */].get(); // can be disabled if use_push_bg == true // solver::scoped_push _s_(*m_ctx); if (!m_use_push_bg) {m_ctx->push();} iuc_solver::scoped_bg _b_(*m_ctx); for (unsigned i = 0; i < num_bg; ++i) if (m_use_push_bg) { m_ctx->push_bg(bg [i]); } else { m_ctx->assert_expr(bg[i]); } unsigned soft_sz = soft.size(); (void) soft_sz; vector clauses; if (!clause.empty()) clauses.push_back(clause); lbool res = internal_check_assumptions(hard, soft, clauses); if (!m_use_push_bg) { m_ctx->pop(1); } TRACE("psolve_verbose", tout << "sat: " << mk_pp(mk_and(hard), m) << "\n" << mk_pp(mk_and(soft), m) << "\n"; for (unsigned i = 0; i < num_bg; ++i) tout << "bg" << i << ": " << mk_pp(bg[i], m) << "\n"; tout << "res: " << res << "\n";); CTRACE("psolve", m_core, tout << "core is: " << mk_pp(mk_and(*m_core), m) << "\n";); SASSERT(soft_sz >= soft.size()); // -- reset all parameters m_core = nullptr; m_model = nullptr; m_subset_based_core = false; return res; } void prop_solver::collect_statistics(statistics& st) const { m_contexts[0]->collect_statistics(st); m_contexts[1]->collect_statistics(st); } void prop_solver::reset_statistics() { } } z3-z3-4.8.7/src/muz/spacer/spacer_prop_solver.h000066400000000000000000000112651356505360400213730ustar00rootroot00000000000000/** Copyright (c) 2017 Arie Gurfinkel Module Name: spacer_prop_solver.h Abstract: SAT solver abstraction for SPACER. Author: Arie Gurfinkel Revision History: --*/ #ifndef _PROP_SOLVER_H_ #define _PROP_SOLVER_H_ #include #include #include #include "ast/ast.h" #include "util/obj_hashtable.h" #include "smt/smt_kernel.h" #include "util/util.h" #include "util/vector.h" #include "solver/solver.h" #include "muz/spacer/spacer_iuc_solver.h" #include "muz/spacer/spacer_util.h" struct fp_params; namespace spacer { typedef ptr_vector decl_vector; class prop_solver { private: ast_manager& m; symbol m_name; ref m_solvers[2]; scoped_ptr m_contexts[2]; iuc_solver * m_ctx; decl_vector m_level_preds; app_ref_vector m_pos_level_atoms; // atoms used to identify level app_ref_vector m_neg_level_atoms; // obj_hashtable m_level_atoms_set; expr_ref_vector* m_core; model_ref* m_model; bool m_subset_based_core; unsigned m_uses_level; /// if true sets the solver into a delta level, enabling only /// atoms explicitly asserted in m_current_level bool m_delta_level; bool m_in_level; bool m_use_push_bg; unsigned m_current_level; // set when m_in_level random_gen m_random; void assert_level_atoms(unsigned level); void ensure_level(unsigned lvl); lbool internal_check_assumptions(expr_ref_vector &hard, expr_ref_vector &soft, vector const & clause); lbool maxsmt(expr_ref_vector &hard, expr_ref_vector &soft, vector const & clauses); lbool mss(expr_ref_vector &hard, expr_ref_vector &soft); public: prop_solver(ast_manager &m, solver *solver0, solver* solver1, fp_params const& p, symbol const& name); void set_core(expr_ref_vector* core) { m_core = core; } void set_model(model_ref* mdl) { m_model = mdl; } void set_subset_based_core(bool f) { m_subset_based_core = f; } bool assumes_level() const { return !is_infty_level(m_uses_level); } unsigned uses_level() const {return m_uses_level;} void add_level(); unsigned level_cnt() const; void assert_expr(expr * form); void assert_expr(expr * form, unsigned level); void assert_exprs(const expr_ref_vector &fmls) { for (auto *f : fmls) assert_expr(f); } void assert_exprs(const expr_ref_vector &fmls, unsigned level) { for (auto *f : fmls) assert_expr(f, level); } /** * check assumptions with a background formula */ lbool check_assumptions(const expr_ref_vector & hard, expr_ref_vector & soft, const expr_ref_vector &clause, unsigned num_bg = 0, expr * const *bg = nullptr, unsigned solver_id = 0); void collect_statistics(statistics& st) const; void reset_statistics(); class scoped_level { bool& m_lev; public: scoped_level(prop_solver& ps, unsigned lvl): m_lev(ps.m_in_level) { SASSERT(!m_lev); m_lev = true; ps.m_current_level = lvl; } ~scoped_level() { m_lev = false; } }; class scoped_subset_core { prop_solver &m_ps; bool m_subset_based_core; public: scoped_subset_core(prop_solver &ps, bool subset_core) : m_ps(ps), m_subset_based_core(ps.m_subset_based_core) {m_ps.set_subset_based_core(subset_core);} ~scoped_subset_core() {m_ps.set_subset_based_core(m_subset_based_core);} }; class scoped_delta_level : public scoped_level { bool &m_delta; public: scoped_delta_level(prop_solver &ps, unsigned lvl) : scoped_level(ps, lvl), m_delta(ps.m_delta_level) {m_delta = true;} ~scoped_delta_level() {m_delta = false;} }; class scoped_weakness { public: solver *sol; scoped_weakness(prop_solver &ps, unsigned solver_id, unsigned weakness) : sol(nullptr) { sol = ps.m_solvers[solver_id == 0 ? 0 : 0 /* 1 */].get(); if (!sol) return; sol->push_params(); params_ref p; p.set_bool("arith.ignore_int", weakness < 1); p.set_bool("array.weak", weakness < 2); sol->updt_params(p); } ~scoped_weakness() {if (sol) {sol->pop_params();}} }; }; } #endif z3-z3-4.8.7/src/muz/spacer/spacer_qe_project.cpp000066400000000000000000002565271356505360400215230ustar00rootroot00000000000000/*++ Copyright (c) 2010 Microsoft Corporation and Arie Gurfinkel Module Name: spacer_qe_project.cpp Abstract: Simple projection function for real arithmetic based on Loos-W. Projection functions for arrays based on MBP Author: Nikolaj Bjorner (nbjorner) 2013-09-12 Anvesh Komuravelli Arie Gurfinkel Revision History: --*/ #include "ast/arith_decl_plugin.h" #include "ast/ast_pp.h" #include "ast/expr_functors.h" #include "ast/expr_substitution.h" #include "ast/ast_util.h" #include "ast/rewriter/expr_replacer.h" #include "ast/rewriter/expr_safe_replace.h" #include "ast/rewriter/th_rewriter.h" #include "model/model_evaluator.h" #include "model/model_pp.h" #include "qe/qe.h" #include "qe/qe_vartest.h" #include "qe/qe_lite.h" #include "muz/spacer/spacer_mev_array.h" #include "muz/spacer/spacer_qe_project.h" namespace spacer_qe { bool is_partial_eq (app* a); /** * \brief utility class for partial equalities * * A partial equality (a ==I b), for two arrays a,b and a finite set of indices I holds * iff (Forall i. i \not\in I => a[i] == b[i]); in other words, it is a * restricted form of the extensionality axiom * * using this class, we denote (a =I b) as f(a,b,i0,i1,...) * where f is an uninterpreted predicate with name PARTIAL_EQ and * I = {i0,i1,...} */ class peq { ast_manager& m; expr_ref m_lhs; expr_ref m_rhs; unsigned m_num_indices; expr_ref_vector m_diff_indices; func_decl_ref m_decl; // the partial equality declaration app_ref m_peq; // partial equality application app_ref m_eq; // equivalent std equality using def. of partial eq array_util m_arr_u; public: static const char* PARTIAL_EQ; peq (app* p, ast_manager& m); peq (expr* lhs, expr* rhs, unsigned num_indices, expr * const * diff_indices, ast_manager& m); void lhs (expr_ref& result); void rhs (expr_ref& result); void get_diff_indices (expr_ref_vector& result); void mk_peq (app_ref& result); void mk_eq (app_ref_vector& aux_consts, app_ref& result, bool stores_on_rhs = true); }; const char* peq::PARTIAL_EQ = "partial_eq"; peq::peq (app* p, ast_manager& m): m (m), m_lhs (p->get_arg (0), m), m_rhs (p->get_arg (1), m), m_num_indices (p->get_num_args ()-2), m_diff_indices (m), m_decl (p->get_decl (), m), m_peq (p, m), m_eq (m), m_arr_u (m) { VERIFY (is_partial_eq (p)); SASSERT (m_arr_u.is_array (m_lhs) && m_arr_u.is_array (m_rhs) && ast_eq_proc() (m.get_sort (m_lhs), m.get_sort (m_rhs))); for (unsigned i = 2; i < p->get_num_args (); i++) { m_diff_indices.push_back (p->get_arg (i)); } } peq::peq (expr* lhs, expr* rhs, unsigned num_indices, expr * const * diff_indices, ast_manager& m): m (m), m_lhs (lhs, m), m_rhs (rhs, m), m_num_indices (num_indices), m_diff_indices (m), m_decl (m), m_peq (m), m_eq (m), m_arr_u (m) { SASSERT (m_arr_u.is_array (lhs) && m_arr_u.is_array (rhs) && ast_eq_proc() (m.get_sort (lhs), m.get_sort (rhs))); ptr_vector sorts; sorts.push_back (m.get_sort (m_lhs)); sorts.push_back (m.get_sort (m_rhs)); for (unsigned i = 0; i < num_indices; i++) { sorts.push_back (m.get_sort (diff_indices [i])); m_diff_indices.push_back (diff_indices [i]); } m_decl = m.mk_func_decl (symbol (PARTIAL_EQ), sorts.size (), sorts.c_ptr (), m.mk_bool_sort ()); } void peq::lhs (expr_ref& result) { result = m_lhs; } void peq::rhs (expr_ref& result) { result = m_rhs; } void peq::get_diff_indices (expr_ref_vector& result) { for (unsigned i = 0; i < m_diff_indices.size (); i++) { result.push_back (m_diff_indices.get (i)); } } void peq::mk_peq (app_ref& result) { if (!m_peq) { ptr_vector args; args.push_back (m_lhs); args.push_back (m_rhs); for (unsigned i = 0; i < m_num_indices; i++) { args.push_back (m_diff_indices.get (i)); } m_peq = m.mk_app (m_decl, args.size (), args.c_ptr ()); } result = m_peq; } void peq::mk_eq (app_ref_vector& aux_consts, app_ref& result, bool stores_on_rhs) { if (!m_eq) { expr_ref lhs (m_lhs, m), rhs (m_rhs, m); if (!stores_on_rhs) { std::swap (lhs, rhs); } // lhs = (...(store (store rhs i0 v0) i1 v1)...) sort* val_sort = get_array_range (m.get_sort (lhs)); expr_ref_vector::iterator end = m_diff_indices.end (); for (expr_ref_vector::iterator it = m_diff_indices.begin (); it != end; it++) { app* val = m.mk_fresh_const ("diff", val_sort); ptr_vector store_args; store_args.push_back (rhs); store_args.push_back (*it); store_args.push_back (val); rhs = m_arr_u.mk_store (store_args); aux_consts.push_back (val); } m_eq = m.mk_eq (lhs, rhs); } result = m_eq; } bool is_partial_eq (app* a) { return a->get_decl ()->get_name () == peq::PARTIAL_EQ; } } namespace spacer_qe { class is_relevant_default : public i_expr_pred { public: bool operator()(expr* e) override { return true; } }; class mk_atom_default : public qe::i_nnf_atom { public: void operator()(expr* e, bool pol, expr_ref& result) override { if (pol) result = e; else result = result.get_manager().mk_not(e); } }; class arith_project_util { ast_manager& m; arith_util a; th_rewriter m_rw; expr_ref_vector m_lits; expr_ref_vector m_terms; vector m_coeffs; vector m_divs; svector m_strict; svector m_eq; scoped_ptr m_var; bool is_linear(rational const& mul, expr* t, rational& c, expr_ref_vector& ts) { expr* t1, *t2; rational mul1; bool res = true; if (t == m_var->x()) { c += mul; } else if (a.is_mul(t, t1, t2) && a.is_numeral(t1, mul1)) { res = is_linear(mul* mul1, t2, c, ts); } else if (a.is_mul(t, t1, t2) && a.is_numeral(t2, mul1)) { res = is_linear(mul* mul1, t1, c, ts); } else if (a.is_add(t)) { app* ap = to_app(t); for (unsigned i = 0; res && i < ap->get_num_args(); ++i) { res = is_linear(mul, ap->get_arg(i), c, ts); } } else if (a.is_sub(t, t1, t2)) { res = is_linear(mul, t1, c, ts) && is_linear(-mul, t2, c, ts); } else if (a.is_uminus(t, t1)) { res = is_linear(-mul, t1, c, ts); } else if (a.is_numeral(t, mul1)) { ts.push_back(a.mk_numeral(mul*mul1, m.get_sort(t))); } else if ((*m_var)(t)) { IF_VERBOSE(2, verbose_stream() << "can't project:" << mk_pp(t, m) << "\n";); TRACE ("qe", tout << "Failed to project: " << mk_pp (t, m) << "\n";); res = false; } else if (mul.is_one()) { ts.push_back(t); } else { ts.push_back(a.mk_mul(a.mk_numeral(mul, m.get_sort(t)), t)); } return res; } // either an equality (cx + t = 0) or an inequality (cx + t <= 0) or a divisibility literal (d | cx + t) bool is_linear(expr* lit, rational& c, expr_ref& t, rational& d, bool& is_strict, bool& is_eq, bool& is_diseq) { SASSERT ((*m_var)(lit)); expr* e1, *e2; c.reset(); sort* s; expr_ref_vector ts(m); bool is_not = m.is_not(lit, lit); rational mul(1); if (is_not) { mul.neg(); } SASSERT(!m.is_not(lit)); if (a.is_le(lit, e1, e2) || a.is_ge(lit, e2, e1)) { if (!is_linear( mul, e1, c, ts) || !is_linear(-mul, e2, c, ts)) return false; s = m.get_sort(e1); is_strict = is_not; } else if (a.is_lt(lit, e1, e2) || a.is_gt(lit, e2, e1)) { if (!is_linear( mul, e1, c, ts) || !is_linear(-mul, e2, c, ts)) return false; s = m.get_sort(e1); is_strict = !is_not; } else if (m.is_eq(lit, e1, e2) && a.is_int_real (e1)) { expr *t, *num; rational num_val, d_val, z; bool is_int; if (a.is_mod (e1, t, num) && a.is_numeral (num, num_val, is_int) && is_int && a.is_numeral (e2, z) && z.is_zero ()) { // divsibility constraint: t % num == 0 <=> num | t if (num_val.is_zero ()) { IF_VERBOSE(1, verbose_stream() << "div by zero" << mk_pp(lit, m) << "\n";); return false; } d = num_val; if (!is_linear (mul, t, c, ts)) return false; } else if (a.is_mod (e2, t, num) && a.is_numeral (num, num_val, is_int) && is_int && a.is_numeral (e1, z) && z.is_zero ()) { // divsibility constraint: 0 == t % num <=> num | t if (num_val.is_zero ()) { IF_VERBOSE(1, verbose_stream() << "div by zero" << mk_pp(lit, m) << "\n";); return false; } d = num_val; if (!is_linear (mul, t, c, ts)) return false; } else { // equality or disequality if (!is_linear( mul, e1, c, ts) || !is_linear(-mul, e2, c, ts)) return false; if (is_not) is_diseq = true; else is_eq = true; } s = m.get_sort(e1); } else { IF_VERBOSE(2, verbose_stream() << "can't project:" << mk_pp(lit, m) << "\n";); TRACE ("qe", tout << "Failed to project: " << mk_pp (lit, m) << "\n";); return false; } if (ts.empty()) { t = a.mk_numeral(rational(0), s); } else if (ts.size () == 1) { t = ts.get (0); } else { t = a.mk_add(ts.size(), ts.c_ptr()); } return true; } bool project(model& mdl, expr_ref_vector& lits) { unsigned num_pos = 0; unsigned num_neg = 0; bool use_eq = false; expr_ref_vector new_lits(m); expr_ref eq_term (m); m_lits.reset (); m_terms.reset(); m_coeffs.reset(); m_strict.reset(); m_eq.reset (); for (unsigned i = 0; i < lits.size(); ++i) { rational c(0), d(0); expr_ref t(m); bool is_strict = false; bool is_eq = false; bool is_diseq = false; if (!(*m_var)(lits.get (i))) { new_lits.push_back(lits.get (i)); continue; } if (is_linear(lits.get (i), c, t, d, is_strict, is_eq, is_diseq)) { if (c.is_zero()) { m_rw(lits.get (i), t); new_lits.push_back(t); } else if (is_eq) { if (!use_eq) { // c*x + t = 0 <=> x = -t/c eq_term = mk_mul (-(rational::one ()/c), t); use_eq = true; } m_lits.push_back (lits.get (i)); m_coeffs.push_back(c); m_terms.push_back(t); m_strict.push_back(false); m_eq.push_back (true); } else { if (is_diseq) { // c*x + t != 0 // find out whether c*x + t < 0, or c*x + t > 0 expr_ref cx (m), cxt (m), val (m); rational r; cx = mk_mul (c, m_var->x()); cxt = mk_add (cx, t); val = mdl(cxt); VERIFY(a.is_numeral(val, r)); SASSERT (r > rational::zero () || r < rational::zero ()); if (r > rational::zero ()) { c = -c; t = mk_mul (-(rational::one()), t); } is_strict = true; } m_lits.push_back (lits.get (i)); m_coeffs.push_back(c); m_terms.push_back(t); m_strict.push_back(is_strict); m_eq.push_back (false); if (c.is_pos()) { ++num_pos; } else { ++num_neg; } } } else return false; } if (use_eq) { TRACE ("qe", tout << "Using equality term: " << mk_pp (eq_term, m) << "\n"; ); // substitute eq_term for x everywhere for (unsigned i = 0; i < m_lits.size(); ++i) { expr_ref cx (m), cxt (m), z (m), result (m); cx = mk_mul (m_coeffs[i], eq_term); cxt = mk_add (cx, m_terms.get(i)); z = a.mk_numeral(rational(0), m.get_sort(eq_term)); if (m_eq[i]) { // c*x + t = 0 result = a.mk_eq (cxt, z); } else if (m_strict[i]) { // c*x + t < 0 result = a.mk_lt (cxt, z); } else { // c*x + t <= 0 result = a.mk_le (cxt, z); } m_rw (result); new_lits.push_back (result); } } lits.reset(); lits.append(new_lits); if (use_eq || num_pos == 0 || num_neg == 0) { return true; } bool use_pos = num_pos < num_neg; unsigned max_t = find_max(mdl, use_pos); expr_ref new_lit (m); for (unsigned i = 0; i < m_lits.size(); ++i) { if (i != max_t) { if (m_coeffs[i].is_pos() == use_pos) { new_lit = mk_le(i, max_t); } else { new_lit = mk_lt(i, max_t); } lits.push_back(new_lit); TRACE ("qe", tout << "Old literal: " << mk_pp (m_lits.get (i), m) << "\n"; tout << "New literal: " << mk_pp (new_lit, m) << "\n"; ); } } return true; } bool project(model& mdl, app_ref_vector const& lits, expr_map& map, app_ref& div_lit) { unsigned num_pos = 0; // number of positive literals true in the model unsigned num_neg = 0; // number of negative literals true in the model m_lits.reset (); m_terms.reset(); m_coeffs.reset(); m_divs.reset (); m_strict.reset(); m_eq.reset (); expr_ref var_val = mdl(m_var->x()); unsigned eq_idx = lits.size (); for (unsigned i = 0; i < lits.size(); ++i) { rational c(0), d(0); expr_ref t(m); bool is_strict = false; bool is_eq = false; bool is_diseq = false; if (!(*m_var)(lits.get (i))) continue; if (is_linear(lits.get (i), c, t, d, is_strict, is_eq, is_diseq)) { TRACE ("qe", tout << "Literal: " << mk_pp (lits.get (i), m) << "\n"; ); if (c.is_zero()) { TRACE ("qe", tout << "independent of variable\n"; ); continue; } // evaluate c*x + t in the model expr_ref cx (m), cxt (m), val (m); rational r; cx = mk_mul (c, m_var->x()); cxt = mk_add (cx, t); val = mdl(cxt); VERIFY(a.is_numeral(val, r)); if (is_eq) { TRACE ("qe", tout << "equality term\n"; ); // check if the equality is true in the mdl if (eq_idx == lits.size () && r == rational::zero ()) { eq_idx = m_lits.size (); } m_lits.push_back (lits.get (i)); m_coeffs.push_back(c); m_terms.push_back(t); m_strict.push_back(false); m_eq.push_back (true); m_divs.push_back (d); } else { TRACE ("qe", tout << "not an equality term\n"; ); if (is_diseq) { // c*x + t != 0 // find out whether c*x + t < 0, or c*x + t > 0 if (r > rational::zero ()) { c = -c; t = mk_mul (-(rational::one()), t); r = -r; } // note: if the disequality is false in the model, // r==0 and we end up choosing c*x + t < 0 is_strict = true; } m_lits.push_back (lits.get (i)); m_coeffs.push_back(c); m_terms.push_back(t); m_strict.push_back(is_strict); m_eq.push_back (false); m_divs.push_back (d); if (d.is_zero ()) { // not a div term if ((is_strict && r < rational::zero ()) || (!is_strict && r <= rational::zero ())) { // literal true in the model if (c.is_pos()) { ++num_pos; } else { ++num_neg; } } } } TRACE ("qe", tout << "c: " << c << "\n"; tout << "t: " << mk_pp (t, m) << "\n"; tout << "d: " << d << "\n"; ); } else return false; } rational lcm_coeffs (1), lcm_divs (1); if (a.is_int (m_var->x())) { // lcm of (absolute values of) coeffs for (unsigned i = 0; i < m_lits.size (); i++) { lcm_coeffs = lcm (lcm_coeffs, abs (m_coeffs[i])); } // normalize coeffs of x to +/-lcm_coeffs and scale terms and divs appropriately; // find lcm of scaled-up divs for (unsigned i = 0; i < m_lits.size (); i++) { rational factor (lcm_coeffs / abs(m_coeffs[i])); if (!factor.is_one () && !a.is_zero (m_terms.get (i))) m_terms[i] = a.mk_mul (a.mk_numeral (factor, a.mk_int ()), m_terms.get (i)); m_coeffs[i] = (m_coeffs[i].is_pos () ? lcm_coeffs : -lcm_coeffs); if (!m_divs[i].is_zero ()) { m_divs[i] *= factor; lcm_divs = lcm (lcm_divs, m_divs[i]); } TRACE ("qe", tout << "normalized coeff: " << m_coeffs[i] << "\n"; tout << "normalized term: " << mk_pp (m_terms.get (i), m) << "\n"; tout << "normalized div: " << m_divs[i] << "\n"; ); } // consider new divisibility literal (lcm_coeffs | (lcm_coeffs * x)) lcm_divs = lcm (lcm_divs, lcm_coeffs); TRACE ("qe", tout << "lcm of coeffs: " << lcm_coeffs << "\n"; tout << "lcm of divs: " << lcm_divs << "\n"; ); } expr_ref z (a.mk_numeral (rational::zero (), a.mk_int ()), m); expr_ref x_term_val (m); // use equality term if (eq_idx < lits.size ()) { if (a.is_real (m_var->x ())) { // c*x + t = 0 <=> x = -t/c expr_ref eq_term (mk_mul (-(rational::one ()/m_coeffs[eq_idx]), m_terms.get (eq_idx)), m); m_rw (eq_term); map.insert (m_var->x (), eq_term, nullptr); TRACE ("qe", tout << "Using equality term: " << mk_pp (eq_term, m) << "\n"; ); } else { // find substitution term for (lcm_coeffs * x) if (m_coeffs[eq_idx].is_pos ()) { x_term_val = a.mk_uminus (m_terms.get (eq_idx)); } else { x_term_val = m_terms.get (eq_idx); } m_rw (x_term_val); TRACE ("qe", tout << "Using equality literal: " << mk_pp (m_lits.get (eq_idx), m) << "\n"; tout << "substitution for (lcm_coeffs * x): " << mk_pp (x_term_val, m) << "\n"; ); // can't simply substitute for x; need to explicitly substitute the lits mk_lit_substitutes (x_term_val, map, eq_idx); if (!lcm_coeffs.is_one ()) { // new div constraint: lcm_coeffs | x_term_val div_lit = m.mk_eq (a.mk_mod (x_term_val, a.mk_numeral (lcm_coeffs, a.mk_int ())), z); } } return true; } expr_ref new_lit (m); if (num_pos == 0 || num_neg == 0) { TRACE ("qe", if (num_pos == 0) { tout << "virtual substitution with +infinity\n"; } else { tout << "virtual substitution with -infinity\n"; } ); /** * make all equalities false; * if num_pos = 0 (num_neg = 0), make all positive (negative) inequalities false; * make the rest inequalities true; * substitute value of x under given model for the rest (div terms) */ if (a.is_int (m_var->x())) { // to substitute for (lcm_coeffs * x), it suffices to pick // some element in the congruence class of (lcm_coeffs * x) mod lcm_divs; // simply substituting var_val for x in the literals does this job; // but to keep constants small, we use (lcm_coeffs * var_val) % lcm_divs instead rational var_val_num; VERIFY (a.is_numeral (var_val, var_val_num)); x_term_val = a.mk_numeral (mod (lcm_coeffs * var_val_num, lcm_divs), a.mk_int ()); TRACE ("qe", tout << "Substitution for (lcm_coeffs * x): " << mk_pp (x_term_val, m) << "\n"; ); } for (unsigned i = 0; i < m_lits.size (); i++) { if (!m_divs[i].is_zero ()) { // m_divs[i] | (x_term_val + m_terms[i]) // -- x_term_val is the absolute value, negate it if needed if (m_coeffs.get (i).is_pos ()) new_lit = a.mk_add (m_terms.get (i), x_term_val); else new_lit = a.mk_add (m_terms.get (i), a.mk_uminus (x_term_val)); // XXX Our handling of divisibility constraints is very fragile. // XXX Rewrite before applying divisibility to preserve syntactic structure m_rw(new_lit); new_lit = m.mk_eq (a.mk_mod (new_lit, a.mk_numeral (m_divs[i], a.mk_int ())), z); } else if (m_eq[i] || (num_pos == 0 && m_coeffs[i].is_pos ()) || (num_neg == 0 && m_coeffs[i].is_neg ())) { new_lit = m.mk_false (); } else { new_lit = m.mk_true (); } map.insert (m_lits.get (i), new_lit, nullptr); TRACE ("qe", tout << "Old literal: " << mk_pp (m_lits.get (i), m) << "\n"; tout << "New literal: " << mk_pp (new_lit, m) << "\n"; ); } return true; } bool use_pos = num_pos < num_neg; // pick a side; both are sound unsigned max_t = find_max(mdl, use_pos); TRACE ("qe", if (use_pos) { tout << "virtual substitution with upper bound:\n"; } else { tout << "virtual substitution with lower bound:\n"; } tout << "test point: " << mk_pp (m_lits.get (max_t), m) << "\n"; tout << "coeff: " << m_coeffs[max_t] << "\n"; tout << "term: " << mk_pp (m_terms.get (max_t), m) << "\n"; tout << "is_strict: " << m_strict[max_t] << "\n"; ); if (a.is_real (m_var->x ())) { for (unsigned i = 0; i < m_lits.size(); ++i) { if (i != max_t) { if (m_eq[i]) { if (!m_strict[max_t]) { new_lit = mk_eq (i, max_t); } else { new_lit = m.mk_false (); } } else if (m_coeffs[i].is_pos() == use_pos) { new_lit = mk_le (i, max_t); } else { new_lit = mk_lt (i, max_t); } } else { new_lit = m.mk_true (); } map.insert (m_lits.get (i), new_lit, nullptr); TRACE ("qe", tout << "Old literal: " << mk_pp (m_lits.get (i), m) << "\n"; tout << "New literal: " << mk_pp (new_lit, m) << "\n"; ); } } else { SASSERT (a.is_int (m_var->x ())); // mk substitution term for (lcm_coeffs * x) // evaluate c*x + t for the literal at max_t expr_ref cx (m), cxt (m), val (m); rational r; cx = mk_mul (m_coeffs[max_t], m_var->x()); cxt = mk_add (cx, m_terms.get (max_t)); val = mdl(cxt); VERIFY(a.is_numeral(val, r)); // get the offset from the smallest/largest possible value for x // literal smallest/largest val of x // ------- -------------------------- // l < x l+1 // l <= x l // x < u u-1 // x <= u u rational offset; if (m_strict[max_t]) { offset = abs(r) - rational::one (); } else { offset = abs(r); } // obtain the offset modulo lcm_divs offset %= lcm_divs; // for strict negative literal (i.e. strict lower bound), // substitution term is (t+1+offset); for non-strict, it's (t+offset) // // for positive term, subtract from 0 x_term_val = mk_add (m_terms.get (max_t), a.mk_numeral (offset, a.mk_int ())); if (m_strict[max_t]) { x_term_val = a.mk_add (x_term_val, a.mk_numeral (rational::one(), a.mk_int ())); } if (m_coeffs[max_t].is_pos ()) { x_term_val = a.mk_uminus (x_term_val); } m_rw (x_term_val); TRACE ("qe", tout << "substitution for (lcm_coeffs * x): " << mk_pp (x_term_val, m) << "\n"; ); // obtain substitutions for all literals in map mk_lit_substitutes (x_term_val, map, max_t); if (!lcm_coeffs.is_one ()) { // new div constraint: lcm_coeffs | x_term_val div_lit = m.mk_eq (a.mk_mod (x_term_val, a.mk_numeral (lcm_coeffs, a.mk_int ())), z); } } return true; } unsigned find_max(model& mdl, bool do_pos) { unsigned result = UINT_MAX; bool found = false; bool found_strict = false; rational found_val (0), r, r_plus_x, found_c; expr_ref val(m); // evaluate x in mdl rational r_x; val = mdl(m_var->x ()); VERIFY(a.is_numeral (val, r_x)); for (unsigned i = 0; i < m_terms.size(); ++i) { rational const& ac = m_coeffs[i]; if (!m_eq[i] && ac.is_pos() == do_pos) { val = mdl(m_terms.get (i)); VERIFY(a.is_numeral(val, r)); r /= abs(ac); // skip the literal if false in the model if (do_pos) { r_plus_x = r + r_x; } else { r_plus_x = r - r_x; } if (!((m_strict[i] && r_plus_x < rational::zero ()) || (!m_strict[i] && r_plus_x <= rational::zero ()))) { continue; } IF_VERBOSE(2, verbose_stream() << "max: " << mk_pp(m_terms.get (i), m) << " " << r << " " << (!found || r > found_val || (r == found_val && !found_strict && m_strict[i])) << "\n";); if (!found || r > found_val || (r == found_val && !found_strict && m_strict[i])) { result = i; found_val = r; found_c = ac; found = true; found_strict = m_strict[i]; } } } SASSERT(found); return result; } // ax + t <= 0 // bx + s <= 0 // a and b have different signs. // Infer: a|b|x + |b|t + |a|bx + |a|s <= 0 // e.g. |b|t + |a|s <= 0 expr_ref mk_lt(unsigned i, unsigned j) { rational const& ac = m_coeffs[i]; rational const& bc = m_coeffs[j]; SASSERT(ac.is_pos() != bc.is_pos()); SASSERT(ac.is_neg() != bc.is_neg()); expr_ref bt (m), as (m), ts (m), z (m); expr* t = m_terms.get (i); expr* s = m_terms.get (j); bt = mk_mul(abs(bc), t); as = mk_mul(abs(ac), s); ts = mk_add(bt, as); z = a.mk_numeral(rational(0), m.get_sort(t)); expr_ref result1(m), result2(m); if (m_strict[i] || m_strict[j]) { result1 = a.mk_lt(ts, z); } else { result1 = a.mk_le(ts, z); } m_rw(result1, result2); return result2; } // ax + t <= 0 // bx + s <= 0 // a and b have same signs. // encode:// t/|a| <= s/|b| // e.g. |b|t <= |a|s expr_ref mk_le(unsigned i, unsigned j) { rational const& ac = m_coeffs[i]; rational const& bc = m_coeffs[j]; SASSERT(ac.is_pos() == bc.is_pos()); SASSERT(ac.is_neg() == bc.is_neg()); expr_ref bt (m), as (m); expr* t = m_terms.get (i); expr* s = m_terms.get (j); bt = mk_mul(abs(bc), t); as = mk_mul(abs(ac), s); expr_ref result1(m), result2(m); if (!m_strict[j] && m_strict[i]) { result1 = a.mk_lt(bt, as); } else { result1 = a.mk_le(bt, as); } m_rw(result1, result2); return result2; } // ax + t = 0 // bx + s <= 0 // replace equality by (-t/a == -s/b), or, as = bt expr_ref mk_eq (unsigned i, unsigned j) { expr_ref as (m), bt (m); as = mk_mul (m_coeffs[i], m_terms.get (j)); bt = mk_mul (m_coeffs[j], m_terms.get (i)); expr_ref result (m); result = m.mk_eq (as, bt); m_rw (result); return result; } expr* mk_add(expr* t1, expr* t2) { return a.mk_add(t1, t2); } expr* mk_mul(rational const& r, expr* t2) { expr* t1 = a.mk_numeral(r, m.get_sort(t2)); return a.mk_mul(t1, t2); } /** * walk the ast of fml and introduce a fresh variable for every mod term * (updating the mdl accordingly) */ void factor_mod_terms (expr_ref& fml, app_ref_vector& vars, model& mdl) { expr_ref_vector todo (m), eqs (m); expr_map factored_terms (m); ast_mark done; todo.push_back (fml); while (!todo.empty ()) { expr* e = todo.back (); if (!is_app (e) || done.is_marked (e)) { todo.pop_back (); continue; } app* ap = to_app (e); unsigned num_args = ap->get_num_args (); bool all_done = true, changed = false; expr_ref_vector args (m); for (unsigned i = 0; i < num_args; i++) { expr* old_arg = ap->get_arg (i); if (!done.is_marked (old_arg)) { todo.push_back (old_arg); all_done = false; } if (!all_done) continue; // all args so far have been processed // get the correct arg to use proof* pr = nullptr; expr* new_arg = nullptr; factored_terms.get (old_arg, new_arg, pr); if (new_arg) { // changed args.push_back (new_arg); changed = true; } else { // not changed args.push_back (old_arg); } } if (all_done) { // all args processed; make new term func_decl* d = ap->get_decl (); expr_ref new_term (m); new_term = m.mk_app (d, args.size (), args.c_ptr ()); // check for mod and introduce new var if (a.is_mod (ap)) { app_ref new_var (m); new_var = m.mk_fresh_const ("mod_var", d->get_range ()); eqs.push_back (m.mk_eq (new_var, new_term)); // obtain value of new_term in mdl expr_ref val = mdl(new_term); // use the variable from now on new_term = new_var; changed = true; // update vars and mdl vars.push_back (new_var); mdl.register_decl (new_var->get_decl (), val); } if (changed) { factored_terms.insert (e, new_term, nullptr); } done.mark (e, true); todo.pop_back (); } } // mk new fml proof* pr = nullptr; expr* new_fml = nullptr; factored_terms.get (fml, new_fml, pr); if (new_fml) { fml = new_fml; // add in eqs fml = m.mk_and (fml, m.mk_and (eqs.size (), eqs.c_ptr ())); } else { // unchanged SASSERT (eqs.empty ()); } } /** * factor out mod terms by using divisibility terms; * * for now, only handle mod equalities of the form (t1 % num == t2), * replacing it by the equivalent (num | (t1-t2)) /\ (0 <= t2 < abs(num)); * the divisibility atom is a special mod term ((t1-t2) % num == 0) */ void mod2div (expr_ref& fml, expr_map& map) { expr* new_fml = nullptr; proof *pr = nullptr; map.get (fml, new_fml, pr); if (new_fml) { fml = new_fml; return; } expr_ref z (a.mk_numeral (rational::zero (), a.mk_int ()), m); bool is_mod_eq = false; expr *e1, *e2, *num; expr_ref t1 (m), t2 (m); rational num_val; bool is_int; // check if fml is a mod equality (t1 % num) == t2 if (m.is_eq (fml, e1, e2)) { expr* t; if (a.is_mod (e1, t, num) && a.is_numeral (num, num_val, is_int) && is_int) { t1 = t; t2 = e2; is_mod_eq = true; } else if (a.is_mod (e2, t, num) && a.is_numeral (num, num_val, is_int) && is_int) { t1 = t; t2 = e1; is_mod_eq = true; } } if (is_mod_eq) { // recursively mod2div for t1 and t2 mod2div (t1, map); mod2div (t2, map); rational t2_num; if (a.is_numeral (t2, t2_num) && t2_num.is_zero ()) { // already in the desired form; // new_fml is (num_val | t1) new_fml = m.mk_eq (a.mk_mod (t1, a.mk_numeral (num_val, a.mk_int ())), z); } else { expr_ref_vector lits (m); // num_val | (t1 - t2) lits.push_back (m.mk_eq (a.mk_mod (a.mk_sub (t1, t2), a.mk_numeral (num_val, a.mk_int ())), z)); // 0 <= t2 lits.push_back (a.mk_le (z, t2)); // t2 < abs (num_val) lits.push_back (a.mk_lt (t2, a.mk_numeral (abs (num_val), a.mk_int ()))); new_fml = m.mk_and (lits.size (), lits.c_ptr ()); } } else if (!is_app (fml)) { new_fml = fml; } else { app* a = to_app (fml); expr_ref_vector children (m); expr_ref ch (m); for (unsigned i = 0; i < a->get_num_args (); i++) { ch = a->get_arg (i); mod2div (ch, map); children.push_back (ch); } new_fml = m.mk_app (a->get_decl (), children.size (), children.c_ptr ()); } map.insert (fml, new_fml, nullptr); fml = new_fml; } void collect_lits (expr* fml, app_ref_vector& lits) { expr_ref_vector todo (m); ast_mark visited; todo.push_back(fml); while (!todo.empty()) { expr* e = todo.back(); todo.pop_back(); if (visited.is_marked(e)) { continue; } visited.mark(e, true); if (!is_app(e)) { continue; } app* a = to_app(e); if (m.is_and(a) || m.is_or(a)) { for (unsigned i = 0; i < a->get_num_args(); ++i) { todo.push_back(a->get_arg(i)); } } else { lits.push_back (a); } } SASSERT(todo.empty()); visited.reset(); } /** * assume that all coeffs of x are the same, say c * substitute x_term_val for (c*x) in all lits and update map * make the literal at idx true */ void mk_lit_substitutes (expr_ref const& x_term_val, expr_map& map, unsigned idx) { expr_ref z (a.mk_numeral (rational::zero (), a.mk_int ()), m); expr_ref cxt (m), new_lit (m); for (unsigned i = 0; i < m_lits.size(); ++i) { if (i == idx) { new_lit = m.mk_true (); } else { // cxt if (m_coeffs[i].is_neg ()) { cxt = a.mk_sub (m_terms.get (i), x_term_val); } else { cxt = a.mk_add (m_terms.get (i), x_term_val); } if (m_divs[i].is_zero ()) { if (m_eq[i]) { new_lit = m.mk_eq (cxt, z); } else if (m_strict[i]) { new_lit = a.mk_lt (cxt, z); } else { new_lit = a.mk_le (cxt, z); } m_rw(new_lit); } else { // div term // XXX rewrite before applying mod to ensure mod is the top-level operator m_rw(cxt); new_lit = m.mk_eq (a.mk_mod (cxt, a.mk_numeral (m_divs[i], a.mk_int ())), z); } } map.insert (m_lits.get (i), new_lit, nullptr); TRACE ("qe", tout << "Old literal: " << mk_pp (m_lits.get (i), m) << "\n"; tout << "New literal: " << mk_pp (new_lit, m) << "\n"; ); } } void substitute (expr_ref& fml, app_ref_vector& lits, expr_map& map) { expr_substitution sub (m); // literals for (unsigned i = 0; i < lits.size (); i++) { expr* new_lit = nullptr; proof* pr = nullptr; app* old_lit = lits.get (i); map.get (old_lit, new_lit, pr); if (new_lit) { sub.insert (old_lit, new_lit); TRACE ("qe", tout << "old lit " << mk_pp (old_lit, m) << "\n"; tout << "new lit " << mk_pp (new_lit, m) << "\n"; ); } } // substitute for x, if any expr* x_term = nullptr; proof* pr = nullptr; map.get (m_var->x (), x_term, pr); if (x_term) { sub.insert (m_var->x (), x_term); TRACE ("qe", tout << "substituting " << mk_pp (m_var->x (), m) << " by " << mk_pp (x_term, m) << "\n"; ); } scoped_ptr rep = mk_default_expr_replacer (m); rep->set_substitution (&sub); (*rep)(fml); } public: arith_project_util(ast_manager& m): m(m), a(m), m_rw(m), m_lits (m), m_terms (m) {} // OLD AND UNUSED INTERFACE expr_ref operator()(model& mdl, app_ref_vector& vars, expr_ref_vector const& lits) { app_ref_vector new_vars(m); expr_ref_vector result(lits); for (unsigned i = 0; i < vars.size(); ++i) { app* v = vars.get (i); m_var = alloc(contains_app, m, v); bool fail = a.is_int (v) || !project (mdl, result); if (fail) new_vars.push_back (v); IF_VERBOSE(2, if (fail) { verbose_stream() << "can't project:" << mk_pp(v, m) << "\n"; } ); TRACE("qe", if (!fail) { tout << "projected: " << mk_pp(v, m) << "\n"; for (unsigned i = 0; i < result.size(); ++i) { tout << mk_pp(result.get (i), m) << "\n"; } } else { tout << "Failed to project: " << mk_pp (v, m) << "\n"; } ); } vars.reset(); vars.append(new_vars); return mk_and(result); } void operator()(model& mdl, app_ref_vector& vars, expr_ref& fml) { expr_map map (m); operator()(mdl, vars, fml, map); } void operator()(model& mdl, app_ref_vector& vars, expr_ref& fml, expr_map& map) { app_ref_vector new_vars(m); // factor out mod terms by introducing new variables TRACE ("qe", tout << "before factoring out mod terms:" << "\n"; tout << mk_pp (fml, m) << "\n"; tout << "mdl:\n"; model_pp (tout, mdl); tout << "\n"; ); factor_mod_terms (fml, vars, mdl); TRACE ("qe", tout << "after factoring out mod terms:" << "\n"; tout << mk_pp (fml, m) << "\n"; tout << "updated mdl:\n"; model_pp (tout, mdl); tout << "\n"; ); app_ref_vector lits (m); // expr_map map (m); for (unsigned i = 0; i < vars.size(); ++i) { app* v = vars.get (i); TRACE ("qe", tout << "projecting variable: " << mk_pp (v, m) << "\n"; ); m_var = alloc(contains_app, m, v); map.reset (); lits.reset (); if (a.is_int (v)) { // factor out mod terms using div terms expr_map mod_map (m); mod2div (fml, mod_map); TRACE ("qe", tout << "after mod2div:" << "\n"; tout << mk_pp (fml, m) << "\n"; ); } collect_lits (fml, lits); app_ref div_lit (m); if (project (mdl, lits, map, div_lit)) { substitute (fml, lits, map); if (div_lit) { fml = m.mk_and (fml, div_lit); } TRACE("qe", tout << "projected: " << mk_pp(v, m) << " " << mk_pp(fml, m) << "\n"; ); } else { IF_VERBOSE(2, verbose_stream() << "can't project:" << mk_pp(v, m) << "\n";); TRACE ("qe", tout << "Failed to project: " << mk_pp (v, m) << "\n";); new_vars.push_back(v); } } vars.reset(); vars.append(new_vars); m_rw (fml); } }; class array_project_eqs_util { ast_manager& m; array_util m_arr_u; model_ref M; app_ref m_v; // array var to eliminate ast_mark m_has_stores_v; // has stores for m_v expr_ref m_subst_term_v; // subst term for m_v expr_safe_replace m_true_sub_v; // subst for true equalities expr_safe_replace m_false_sub_v; // subst for false equalities expr_ref_vector m_aux_lits_v; expr_ref_vector m_idx_lits_v; app_ref_vector m_aux_vars; model_evaluator_array_util m_mev; void reset_v () { m_v = nullptr; m_has_stores_v.reset (); m_subst_term_v = nullptr; m_true_sub_v.reset (); m_false_sub_v.reset (); m_aux_lits_v.reset (); m_idx_lits_v.reset (); } void reset () { M = nullptr; reset_v (); m_aux_vars.reset (); } /** * find all array equalities on m_v or containing stores on/of m_v * * also mark terms containing stores on/of m_v */ void find_arr_eqs (expr_ref const& fml, expr_ref_vector& eqs) { if (!is_app (fml)) return; ast_mark done; ptr_vector todo; todo.push_back (to_app (fml)); while (!todo.empty ()) { app* a = todo.back (); if (done.is_marked (a)) { todo.pop_back (); continue; } bool all_done = true; bool args_have_stores = false; unsigned num_args = a->get_num_args (); for (unsigned i = 0; i < num_args; i++) { expr* arg = a->get_arg (i); if (!is_app (arg)) continue; if (!done.is_marked (arg)) { all_done = false; todo.push_back (to_app (arg)); } else if (!args_have_stores && m_has_stores_v.is_marked (arg)) { args_have_stores = true; } } if (!all_done) continue; todo.pop_back (); // mark if a has stores if ((!m_arr_u.is_select (a) && args_have_stores) || (m_arr_u.is_store (a) && (a->get_arg (0) == m_v))) { m_has_stores_v.mark (a, true); TRACE ("qe", tout << "has stores:\n"; tout << mk_pp (a, m) << "\n"; ); } // check if a is a relevant array equality if (m.is_eq (a)) { expr* a0 = to_app (a)->get_arg (0); expr* a1 = to_app (a)->get_arg (1); if (a0 == m_v || a1 == m_v || (m_arr_u.is_array (a0) && m_has_stores_v.is_marked (a))) { eqs.push_back (a); } } // else, we can check for disequalities and handle them using extensionality, // but it's not necessary done.mark (a, true); } } /** * factor out select terms on m_v using fresh consts */ void factor_selects (app_ref& fml) { expr_map sel_cache (m); ast_mark done; ptr_vector todo; expr_ref_vector pinned (m); // to ensure a reference todo.push_back (fml); while (!todo.empty ()) { app* a = todo.back (); if (done.is_marked (a)) { todo.pop_back (); continue; } expr_ref_vector args (m); bool all_done = true; for (unsigned i = 0; i < a->get_num_args (); i++) { expr* arg = a->get_arg (i); if (!is_app (arg)) continue; if (!done.is_marked (arg)) { all_done = false; todo.push_back (to_app (arg)); } else if (all_done) { // all done so far.. expr* arg_new = nullptr; proof* pr; sel_cache.get (arg, arg_new, pr); if (!arg_new) { arg_new = arg; } args.push_back (arg_new); } } if (!all_done) continue; todo.pop_back (); expr_ref a_new (m.mk_app (a->get_decl (), args.size (), args.c_ptr ()), m); // if a_new is select on m_v, introduce new constant if (m_arr_u.is_select (a) && (args.get (0) == m_v || m_has_stores_v.is_marked (args.get (0)))) { sort* val_sort = get_array_range (m.get_sort (m_v)); app_ref val_const (m.mk_fresh_const ("sel", val_sort), m); m_aux_vars.push_back (val_const); // extend M to include val_const expr_ref val(m); m_mev.eval (*M, a_new, val); M->register_decl (val_const->get_decl (), val); // add equality m_aux_lits_v.push_back (m.mk_eq (val_const, a_new)); // replace select by const a_new = val_const; } if (a != a_new) { sel_cache.insert (a, a_new, nullptr); pinned.push_back (a_new); } done.mark (a, true); } expr* res = nullptr; proof* pr; sel_cache.get (fml, res, pr); if (res) { fml = to_app (res); } } /** * convert partial equality expression p_exp to an equality by * recursively adding stores on diff indices * * add stores on lhs or rhs depending on whether stores_on_rhs is false/true */ void convert_peq_to_eq (expr* p_exp, app_ref& eq, bool stores_on_rhs = true) { peq p (to_app (p_exp), m); app_ref_vector diff_val_consts (m); p.mk_eq (diff_val_consts, eq, stores_on_rhs); m_aux_vars.append (diff_val_consts); // extend M to include diff_val_consts expr_ref arr (m); expr_ref_vector I (m); p.lhs (arr); p.get_diff_indices (I); expr_ref val (m); unsigned num_diff = diff_val_consts.size (); SASSERT (num_diff == I.size ()); for (unsigned i = 0; i < num_diff; i++) { // mk val term ptr_vector sel_args; sel_args.push_back (arr); sel_args.push_back (I.get (i)); expr_ref val_term (m_arr_u.mk_select (sel_args.size (), sel_args.c_ptr ()), m); // evaluate and assign to ith diff_val_const m_mev.eval (*M, val_term, val); M->register_decl (diff_val_consts.get (i)->get_decl (), val); } } /** * mk (e0 ==indices e1) * * result has stores if either e0 or e1 or an index term has stores */ void mk_peq (expr* e0, expr* e1, unsigned num_indices, expr* const* indices, app_ref& result) { peq p (e0, e1, num_indices, indices, m); p.mk_peq (result); } void find_subst_term (app* eq) { app_ref p_exp (m); mk_peq (eq->get_arg (0), eq->get_arg (1), 0, nullptr, p_exp); bool subst_eq_found = false; while (true) { TRACE ("qe", tout << "processing peq:\n"; tout << mk_pp (p_exp, m) << "\n"; ); peq p (p_exp, m); expr_ref lhs (m), rhs (m); p.lhs (lhs); p.rhs (rhs); if (!m_has_stores_v.is_marked (lhs)) { std::swap (lhs, rhs); } if (m_has_stores_v.is_marked (lhs)) { /** project using the equivalence: * * (store(arr0,idx,x) ==I arr1) <-> * * (idx \in I => (arr0 ==I arr1)) /\ * (idx \not\in I => (arr0 ==I+idx arr1) /\ (arr1[idx] == x))) */ expr_ref_vector I (m); p.get_diff_indices (I); app* a_lhs = to_app (lhs); expr* arr0 = a_lhs->get_arg (0); expr* idx = a_lhs->get_arg (1); expr* x = a_lhs->get_arg (2); expr* arr1 = rhs; // check if (idx \in I) in M bool idx_in_I = false; expr_ref_vector idx_diseq (m); if (!I.empty ()) { expr_ref val (m); m_mev.eval (*M, idx, val); for (unsigned i = 0; i < I.size () && !idx_in_I; i++) { if (idx == I.get (i)) { idx_in_I = true; } else { expr_ref val1 (m); expr* idx1 = I.get (i); expr_ref idx_eq (m.mk_eq (idx, idx1), m); m_mev.eval (*M, idx1, val1); if (val == val1) { idx_in_I = true; m_idx_lits_v.push_back (idx_eq); } else { idx_diseq.push_back (m.mk_not (idx_eq)); } } } } if (idx_in_I) { TRACE ("qe", tout << "store index in diff indices:\n"; tout << mk_pp (m_idx_lits_v.back (), m) << "\n"; ); // arr0 ==I arr1 mk_peq (arr0, arr1, I.size (), I.c_ptr (), p_exp); TRACE ("qe", tout << "new peq:\n"; tout << mk_pp (p_exp, m) << "\n"; ); } else { m_idx_lits_v.append (idx_diseq); // arr0 ==I+idx arr1 I.push_back (idx); mk_peq (arr0, arr1, I.size (), I.c_ptr (), p_exp); TRACE ("qe", tout << "new peq:\n"; tout << mk_pp (p_exp, m) << "\n"; ); // arr1[idx] == x ptr_vector sel_args; sel_args.push_back (arr1); sel_args.push_back (idx); expr_ref arr1_idx (m_arr_u.mk_select (sel_args.size (), sel_args.c_ptr ()), m); expr_ref eq (m.mk_eq (arr1_idx, x), m); m_aux_lits_v.push_back (eq); TRACE ("qe", tout << "new eq:\n"; tout << mk_pp (eq, m) << "\n"; ); } } else if (lhs == rhs) { // trivial peq (a ==I a) break; } else if (lhs == m_v || rhs == m_v) { subst_eq_found = true; TRACE ("qe", tout << "subst eq found!\n"; ); break; } else { UNREACHABLE (); } } // factor out select terms on m_v from p_exp using fresh constants if (subst_eq_found) { factor_selects (p_exp); TRACE ("qe", tout << "after factoring selects:\n"; tout << mk_pp (p_exp, m) << "\n"; for (unsigned i = m_aux_lits_v.size () - m_aux_vars.size (); i < m_aux_lits_v.size (); i++) { tout << mk_pp (m_aux_lits_v.get (i), m) << "\n"; } ); // find subst_term bool stores_on_rhs = true; app* a = to_app (p_exp); if (a->get_arg (1) == m_v) { stores_on_rhs = false; } app_ref eq (m); convert_peq_to_eq (p_exp, eq, stores_on_rhs); m_subst_term_v = eq->get_arg (1); TRACE ("qe", tout << "subst term found:\n"; tout << mk_pp (m_subst_term_v, m) << "\n"; ); } } /** * try to substitute for m_v, using array equalities * * compute substitution term and aux lits */ bool project (expr_ref const& fml) { expr_ref_vector eqs (m); ptr_vector true_eqs; // subset of eqs; eqs ensures references find_arr_eqs (fml, eqs); TRACE ("qe", tout << "array equalities:\n"; for (unsigned i = 0; i < eqs.size (); i++) { tout << mk_pp (eqs.get (i), m) << "\n"; } ); // evaluate eqs in M for (unsigned i = 0; i < eqs.size (); i++) { TRACE ("qe", tout << "array equality:\n"; tout << mk_pp (eqs.get (i), m) << "\n"; ); expr* eq = eqs.get (i); // evaluate eq in M app* a = to_app (eq); expr_ref val (m); m_mev.eval_array_eq (*M, a, a->get_arg (0), a->get_arg (1), val); if (!val) { // XXX HACK: unable to evaluate. set to true? val = m.mk_true (); } SASSERT (m.is_true (val) || m.is_false (val)); if (m.is_false (val)) { m_false_sub_v.insert (eq, m.mk_false ()); } else { true_eqs.push_back (to_app (eq)); } } // compute nesting depths of stores on m_v in true_eqs, as follows: // 0 if m_v appears on both sides of equality // 1 if equality is (m_v=t) // 2 if equality is (store(m_v,i,v)=t) // ... unsigned num_true_eqs = true_eqs.size (); vector nds (num_true_eqs); for (unsigned i = 0; i < num_true_eqs; i++) { app* eq = true_eqs.get (i); expr* lhs = eq->get_arg (0); expr* rhs = eq->get_arg (1); bool lhs_has_v = (lhs == m_v || m_has_stores_v.is_marked (lhs)); bool rhs_has_v = (rhs == m_v || m_has_stores_v.is_marked (rhs)); app* store = nullptr; SASSERT (lhs_has_v || rhs_has_v); if (!lhs_has_v) { store = to_app (rhs); } else if (!rhs_has_v) { store = to_app (lhs); } // else v appears on both sides -- trivial equality // put it in the beginning to simplify it away unsigned nd = 0; // nesting depth if (store) { for (nd = 1; m_arr_u.is_store (store); nd++, store = to_app (store->get_arg (0))) /* empty */ ; SASSERT (store == m_v); } nds[i] = nd; } SASSERT (true_eqs.size () == nds.size ()); // sort true_eqs according to nesting depth // use insertion sort for (unsigned i = 1; i < num_true_eqs; i++) { app_ref eq(m); eq = true_eqs.get (i); unsigned nd = nds.get (i); unsigned j = i; for (; j >= 1 && nds.get (j-1) > nd; j--) { true_eqs.set (j, true_eqs.get (j-1)); nds.set (j, nds.get (j-1)); } if (j < i) { true_eqs.set (j, eq); nds.set (j, nd); TRACE ("qe", tout << "changing eq order!\n"; ); } } // search for subst term for (unsigned i = 0; !m_subst_term_v && i < num_true_eqs; i++) { app* eq = true_eqs.get (i); m_true_sub_v.insert (eq, m.mk_true ()); // try to find subst term find_subst_term (eq); } return true; } void mk_result (expr_ref& fml) { th_rewriter rw(m); rw (fml); // add in aux_lits and idx_lits expr_ref_vector lits (m); // TODO: eliminate possible duplicates, especially in idx_lits // theory rewriting is a possibility, but not sure if it // introduces unwanted terms such as ite's lits.append (m_idx_lits_v); lits.append (m_aux_lits_v); lits.push_back (fml); fml = m.mk_and (lits.size (), lits.c_ptr ()); if (m_subst_term_v) { m_true_sub_v.insert (m_v, m_subst_term_v); m_true_sub_v (fml); } else { m_true_sub_v (fml); m_false_sub_v (fml); } rw(fml); SASSERT (!m.is_false (fml)); } public: array_project_eqs_util (ast_manager& m): m (m), m_arr_u (m), m_v (m), m_subst_term_v (m), m_true_sub_v (m), m_false_sub_v (m), m_aux_lits_v (m), m_idx_lits_v (m), m_aux_vars (m), m_mev (m) {} void operator () (model& mdl, app_ref_vector& arr_vars, expr_ref& fml, app_ref_vector& aux_vars) { reset (); app_ref_vector rem_arr_vars (m); // remaining arr vars M = &mdl; for (unsigned i = 0; i < arr_vars.size (); i++) { reset_v (); m_v = arr_vars.get (i); if (!m_arr_u.is_array (m_v)) { TRACE ("qe", tout << "not an array variable: " << mk_pp (m_v, m) << "\n"; ); aux_vars.push_back (m_v); continue; } TRACE ("qe", tout << "projecting equalities on variable: " << mk_pp (m_v, m) << "\n"; ); if (project (fml)) { mk_result (fml); contains_app contains_v (m, m_v); if (!m_subst_term_v || contains_v (m_subst_term_v)) { rem_arr_vars.push_back (m_v); } TRACE ("qe", tout << "after projection: \n"; tout << mk_pp (fml, m) << "\n"; ); } else { IF_VERBOSE(2, verbose_stream() << "can't project:" << mk_pp(m_v, m) << "\n";); TRACE ("qe", tout << "Failed to project: " << mk_pp (m_v, m) << "\n";); rem_arr_vars.push_back(m_v); } } arr_vars.reset (); arr_vars.append (rem_arr_vars); aux_vars.append (m_aux_vars); } }; class array_select_reducer { ast_manager& m; array_util m_arr_u; obj_map m_cache; expr_ref_vector m_pinned; // to ensure a reference expr_ref_vector m_idx_lits; model_ref M; model_evaluator_array_util m_mev; th_rewriter m_rw; ast_mark m_arr_test; ast_mark m_has_stores; bool m_reduce_all_selects; void reset () { m_cache.reset (); m_pinned.reset (); m_idx_lits.reset (); M = nullptr; m_arr_test.reset (); m_has_stores.reset (); m_reduce_all_selects = false; } bool is_equals (expr *e1, expr *e2) { if (e1 == e2) return true; expr_ref val1 (m), val2 (m); m_mev.eval (*M, e1, val1); m_mev.eval (*M, e2, val2); return (val1 == val2); } void add_idx_cond (expr_ref& cond) { m_rw (cond); if (!m.is_true (cond)) m_idx_lits.push_back (cond); } bool has_stores (expr* e) { if (m_reduce_all_selects) return true; return m_has_stores.is_marked (e); } void mark_stores (app* a, bool args_have_stores) { if (m_reduce_all_selects) return; if (args_have_stores || (m_arr_u.is_store (a) && m_arr_test.is_marked (a->get_arg (0)))) { m_has_stores.mark (a, true); } } bool reduce (expr_ref& e) { if (!is_app (e)) return true; expr *r = nullptr; if (m_cache.find (e, r)) { e = r; return true; } ptr_vector todo; todo.push_back (to_app (e)); while (!todo.empty ()) { app *a = todo.back (); unsigned sz = todo.size (); expr_ref_vector args (m); bool dirty = false; bool args_have_stores = false; for (unsigned i = 0; i < a->get_num_args (); ++i) { expr *arg = a->get_arg (i); expr *narg = nullptr; if (!is_app (arg)) args.push_back (arg); else if (m_cache.find (arg, narg)) { args.push_back (narg); dirty |= (arg != narg); if (!args_have_stores && has_stores (narg)) { args_have_stores = true; } } else { todo.push_back (to_app (arg)); } } if (todo.size () > sz) continue; todo.pop_back (); if (dirty) { r = m.mk_app (a->get_decl (), args.size (), args.c_ptr ()); m_pinned.push_back (r); } else r = a; if (m_arr_u.is_select (r) && has_stores (to_app (r)->get_arg (0))) { r = reduce_core (to_app(r)); } else { mark_stores (to_app (r), args_have_stores); } m_cache.insert (a, r); } SASSERT (r); e = r; return true; } expr* reduce_core (app *a) { if (!m_arr_u.is_store (a->get_arg (0))) return a; SASSERT (a->get_num_args () == 2 && "Multi-dimensional arrays are not supported"); expr* array = a->get_arg (0); expr* j = a->get_arg (1); while (m_arr_u.is_store (array)) { a = to_app (array); expr* idx = a->get_arg (1); expr_ref cond (m); if (is_equals (idx, j)) { cond = m.mk_eq (idx, j); add_idx_cond (cond); return a->get_arg (2); } else { cond = m.mk_not (m.mk_eq (idx, j)); add_idx_cond (cond); array = a->get_arg (0); } } expr* args[2] = {array, j}; expr* r = m_arr_u.mk_select (2, args); m_pinned.push_back (r); return r; } void mk_result (expr_ref& fml) { // conjoin idx lits expr_ref_vector lits (m); lits.append (m_idx_lits); lits.push_back (fml); fml = m.mk_and (lits.size (), lits.c_ptr ()); // simplify all trivial expressions introduced m_rw (fml); TRACE ("qe", tout << "after reducing selects:\n"; tout << mk_pp (fml, m) << "\n"; ); } public: array_select_reducer (ast_manager& m): m (m), m_arr_u (m), m_pinned (m), m_idx_lits (m), m_mev (m), m_rw (m), m_reduce_all_selects (false) {} void operator () (model& mdl, app_ref_vector const& arr_vars, expr_ref& fml, bool reduce_all_selects = false) { if (!reduce_all_selects && arr_vars.empty ()) return; reset (); M = &mdl; m_reduce_all_selects = reduce_all_selects; // mark vars to eliminate for (unsigned i = 0; i < arr_vars.size (); i++) { m_arr_test.mark (arr_vars.get (i), true); } // assume all arr_vars are of array sort // and assume no store equalities on arr_vars if (reduce (fml)) { mk_result (fml); } else { IF_VERBOSE(2, verbose_stream() << "can't project arrays:" << "\n";); TRACE ("qe", tout << "Failed to project arrays\n";); } } }; class array_project_selects_util { typedef obj_map*> sel_map; ast_manager& m; array_util m_arr_u; arith_util m_ari_u; sel_map m_sel_terms; // representative indices for eliminating selects expr_ref_vector m_idx_reprs; expr_ref_vector m_idx_vals; app_ref_vector m_sel_consts; expr_ref_vector m_idx_lits; model_ref M; model_evaluator_array_util m_mev; expr_safe_replace m_sub; ast_mark m_arr_test; void reset () { m_sel_terms.reset (); m_idx_reprs.reset (); m_idx_vals.reset (); m_sel_consts.reset (); m_idx_lits.reset (); M = nullptr; m_sub.reset (); m_arr_test.reset (); } /** * collect sel terms on array vars as given by m_arr_test */ void collect_selects (expr* fml) { if (!is_app (fml)) return; ast_mark done; ptr_vector todo; todo.push_back (to_app (fml)); while (!todo.empty ()) { app* a = todo.back (); if (done.is_marked (a)) { todo.pop_back (); continue; } unsigned num_args = a->get_num_args (); bool all_done = true; for (unsigned i = 0; i < num_args; i++) { expr* arg = a->get_arg (i); if (!done.is_marked (arg) && is_app (arg)) { todo.push_back (to_app (arg)); all_done = false; } } if (!all_done) continue; todo.pop_back (); if (m_arr_u.is_select (a)) { expr* arr = a->get_arg (0); if (m_arr_test.is_marked (arr)) { ptr_vector* lst = m_sel_terms.find (to_app (arr));; lst->push_back (a); } } done.mark (a, true); } } /** * model based ackermannization for sel terms of some array * * update sub with val consts for sel terms */ void ackermann (ptr_vector const& sel_terms) { if (sel_terms.empty ()) return; expr* v = sel_terms.get (0)->get_arg (0); // array variable sort* v_sort = m.get_sort (v); sort* val_sort = get_array_range (v_sort); sort* idx_sort = get_array_domain (v_sort, 0); (void) idx_sort; unsigned start = m_idx_reprs.size (); // append at the end for (unsigned i = 0; i < sel_terms.size (); i++) { app* a = sel_terms.get (i); expr* idx = a->get_arg (1); expr_ref val (m); m_mev.eval (*M, idx, val); bool is_new = true; for (unsigned j = start; j < m_idx_vals.size (); j++) { if (m_idx_vals.get (j) == val) { // idx belongs to the jth equivalence class; // substitute sel term with ith sel const expr* c = m_sel_consts.get (j); m_sub.insert (a, c); // add equality (idx == repr) expr* repr = m_idx_reprs.get (j); m_idx_lits.push_back (m.mk_eq (idx, repr)); is_new = false; break; } } if (is_new) { // new repr, val, and sel const m_idx_reprs.push_back (idx); m_idx_vals.push_back (val); app_ref c (m.mk_fresh_const ("sel", val_sort), m); m_sel_consts.push_back (c); // substitute sel term with new const m_sub.insert (a, c); // extend M to include c m_mev.eval (*M, a, val); M->register_decl (c->get_decl (), val); } } // sort reprs by their value and add a chain of strict inequalities unsigned num_reprs = m_idx_reprs.size () - start; if (num_reprs == 0) return; SASSERT ((m_ari_u.is_real (idx_sort) || m_ari_u.is_int (idx_sort)) && "Unsupported index sort: neither real nor int"); // using insertion sort unsigned end = start + num_reprs; for (unsigned i = start+1; i < end; i++) { expr_ref repr(m), val(m); repr = m_idx_reprs.get (i); val = m_idx_vals.get (i); unsigned j = i; for (; j > start; j--) { rational j_val, jm1_val; VERIFY (m_ari_u.is_numeral (val, j_val)); VERIFY (m_ari_u.is_numeral (m_idx_vals.get (j-1), jm1_val)); if (j_val >= jm1_val) break; m_idx_reprs[j] = m_idx_reprs.get (j-1); m_idx_vals[j] = m_idx_vals.get (j-1); } m_idx_reprs[j] = repr; m_idx_vals[j] = val; } for (unsigned i = start; i < end-1; i++) { m_idx_lits.push_back (m_ari_u.mk_lt (m_idx_reprs.get (i), m_idx_reprs.get (i+1))); } } void mk_result (expr_ref& fml) { // conjoin idx lits expr_ref_vector lits (m); lits.append (m_idx_lits); lits.push_back (fml); fml = m.mk_and (lits.size (), lits.c_ptr ()); // substitute for sel terms m_sub (fml); TRACE ("qe", tout << "after projection of selects:\n"; tout << mk_pp (fml, m) << "\n"; ); } /** * project selects * populates idx lits and obtains substitution for sel terms */ bool project (expr_ref& fml) { // collect sel terms -- populate the map m_sel_terms collect_selects (fml); // model based ackermannization sel_map::iterator begin = m_sel_terms.begin (), end = m_sel_terms.end (); for (sel_map::iterator it = begin; it != end; it++) { TRACE ("qe", tout << "ackermann for var: " << mk_pp (it->m_key, m) << "\n"; ); ackermann (*(it->m_value)); } TRACE ("qe", tout << "idx lits:\n"; for (unsigned i = 0; i < m_idx_lits.size (); i++) { tout << mk_pp (m_idx_lits.get (i), m) << "\n"; } ); return true; } public: array_project_selects_util (ast_manager& m): m (m), m_arr_u (m), m_ari_u (m), m_idx_reprs (m), m_idx_vals (m), m_sel_consts (m), m_idx_lits (m), m_mev (m), m_sub (m) {} void operator () (model& mdl, app_ref_vector& arr_vars, expr_ref& fml, app_ref_vector& aux_vars) { reset (); M = &mdl; // mark vars to eliminate for (unsigned i = 0; i < arr_vars.size (); i++) { m_arr_test.mark (arr_vars.get (i), true); } // alloc empty map from array var to sel terms over it for (unsigned i = 0; i < arr_vars.size (); i++) { ptr_vector* lst = alloc (ptr_vector); m_sel_terms.insert (arr_vars.get (i), lst); } // assume all arr_vars are of array sort // and they only appear in select terms if (project (fml)) { mk_result (fml); aux_vars.append (m_sel_consts); arr_vars.reset (); } else { IF_VERBOSE(2, verbose_stream() << "can't project arrays:" << "\n";); TRACE ("qe", tout << "Failed to project arrays\n";); } // dealloc sel_map::iterator begin = m_sel_terms.begin (), end = m_sel_terms.end (); for (sel_map::iterator it = begin; it != end; it++) { dealloc (it->m_value); } m_sel_terms.reset (); } }; expr_ref arith_project(model& mdl, app_ref_vector& vars, expr_ref_vector const& lits) { ast_manager& m = vars.get_manager(); arith_project_util ap(m); return ap(mdl, vars, lits); } void arith_project(model& mdl, app_ref_vector& vars, expr_ref& fml) { ast_manager& m = vars.get_manager(); arith_project_util ap(m); qe::atom_set pos_lits, neg_lits; is_relevant_default is_relevant; mk_atom_default mk_atom; get_nnf (fml, is_relevant, mk_atom, pos_lits, neg_lits); ap(mdl, vars, fml); } void arith_project(model& mdl, app_ref_vector& vars, expr_ref& fml, expr_map& map) { ast_manager& m = vars.get_manager(); arith_project_util ap(m); qe::atom_set pos_lits, neg_lits; is_relevant_default is_relevant; mk_atom_default mk_atom; get_nnf (fml, is_relevant, mk_atom, pos_lits, neg_lits); ap(mdl, vars, fml, map); } void array_project_eqs (model& mdl, app_ref_vector& arr_vars, expr_ref& fml, app_ref_vector& aux_vars) { ast_manager& m = arr_vars.get_manager (); array_project_eqs_util ap (m); ap (mdl, arr_vars, fml, aux_vars); } void reduce_array_selects (model& mdl, app_ref_vector const& arr_vars, expr_ref& fml, bool reduce_all_selects) { ast_manager& m = arr_vars.get_manager (); array_select_reducer ap (m); ap (mdl, arr_vars, fml, reduce_all_selects); } void reduce_array_selects (model& mdl, expr_ref& fml) { ast_manager& m = fml.get_manager (); app_ref_vector _tmp (m); reduce_array_selects (mdl, _tmp, fml, true); } void array_project_selects (model& mdl, app_ref_vector& arr_vars, expr_ref& fml, app_ref_vector& aux_vars) { ast_manager& m = arr_vars.get_manager (); array_project_selects_util ap (m); ap (mdl, arr_vars, fml, aux_vars); } void array_project (model& mdl, app_ref_vector& arr_vars, expr_ref& fml, app_ref_vector& aux_vars, bool reduce_all_selects) { // 1. project array equalities array_project_eqs (mdl, arr_vars, fml, aux_vars); TRACE ("qe", ast_manager& m = fml.get_manager (); tout << "Projected array eqs:\n" << mk_pp (fml, m) << "\n"; tout << "Remaining array vars:\n"; for (unsigned i = 0; i < arr_vars.size (); i++) { tout << mk_pp (arr_vars.get (i), m) << "\n"; } tout << "Aux vars:\n"; for (unsigned i = 0; i < aux_vars.size (); i++) { tout << mk_pp (aux_vars.get (i), m) << "\n"; } ); // 2. reduce selects if (reduce_all_selects) { reduce_array_selects (mdl, fml); } else { reduce_array_selects (mdl, arr_vars, fml); } TRACE ("qe", ast_manager& m = fml.get_manager (); tout << "Reduced selects:\n" << mk_pp (fml, m) << "\n"; ); // 3. project selects using model based ackermannization array_project_selects (mdl, arr_vars, fml, aux_vars); TRACE ("qe", ast_manager& m = fml.get_manager (); tout << "Projected array selects:\n" << mk_pp (fml, m) << "\n"; tout << "All aux vars:\n"; for (unsigned i = 0; i < aux_vars.size (); i++) { tout << mk_pp (aux_vars.get (i), m) << "\n"; } ); } } z3-z3-4.8.7/src/muz/spacer/spacer_qe_project.h000066400000000000000000000024661356505360400211570ustar00rootroot00000000000000/*++ Copyright (c) 2017 Arie Gurfinkel Module Name: spacer_qe_project.h Abstract: Model-based projection Author: Anvesh Komuravelli Arie Gurfinkel (arie) Notes: --*/ #ifndef SPACER_QE_PROJECT_H_ #define SPACER_QE_PROJECT_H_ #include "model/model.h" #include "ast/expr_map.h" namespace spacer_qe { /** Loos-Weispfenning model-based projection for a basic conjunction. Lits is a vector of literals. return vector of variables that could not be projected. */ expr_ref arith_project(model& model, app_ref_vector& vars, expr_ref_vector const& lits); void arith_project(model& model, app_ref_vector& vars, expr_ref& fml); void arith_project(model& model, app_ref_vector& vars, expr_ref& fml, expr_map& map); void array_project_eqs (model& model, app_ref_vector& arr_vars, expr_ref& fml, app_ref_vector& aux_vars); void reduce_array_selects (model& mdl, app_ref_vector const& arr_vars, expr_ref& fml, bool reduce_all_selects = false); void reduce_array_selects (model& mdl, expr_ref& fml); void array_project_selects (model& model, app_ref_vector& arr_vars, expr_ref& fml, app_ref_vector& aux_vars); void array_project (model& model, app_ref_vector& arr_vars, expr_ref& fml, app_ref_vector& aux_vars, bool reduce_all_selects = false); }; #endif z3-z3-4.8.7/src/muz/spacer/spacer_quant_generalizer.cpp000066400000000000000000000544661356505360400231050ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation and Arie Gurfinkel Module Name: spacer_quant_generalizer.cpp Abstract: Quantified lemma generalizer. Author: Yakir Vizel Arie Gurfinkel Revision History: --*/ #include "muz/spacer/spacer_context.h" #include "muz/spacer/spacer_generalizers.h" #include "muz/spacer/spacer_manager.h" #include "ast/expr_abstract.h" #include "ast/rewriter/var_subst.h" #include "ast/for_each_expr.h" #include "ast/rewriter/factor_equivs.h" #include "ast/rewriter/expr_safe_replace.h" #include "ast/substitution/matcher.h" #include "ast/expr_functors.h" #include "muz/spacer/spacer_sem_matcher.h" using namespace spacer; namespace { struct index_lt_proc : public std::binary_function { arith_util m_arith; index_lt_proc(ast_manager &m) : m_arith(m) {} bool operator() (app *a, app *b) { // XXX This order is a bit strange. // XXX It does the job in our current application, but only because // XXX we assume that we only compare expressions of the form (B + k), // XXX where B is fixed and k is a number. // XXX Might be better to specialize just for that specific use case. rational val1, val2; bool is_num1 = m_arith.is_numeral(a, val1); bool is_num2 = m_arith.is_numeral(b, val2); if (is_num1 && is_num2) { return val1 < val2; } else if (is_num1 != is_num2) { return is_num1; } is_num1 = false; is_num2 = false; // compare the first numeric argument of a to first numeric argument of b // if available for (unsigned i = 0, sz = a->get_num_args(); !is_num1 && i < sz; ++i) { is_num1 = m_arith.is_numeral (a->get_arg(i), val1); } for (unsigned i = 0, sz = b->get_num_args(); !is_num2 && i < sz; ++i) { is_num2 = m_arith.is_numeral(b->get_arg(i), val2); } if (is_num1 && is_num2) { return val1 < val2; } else if (is_num1 != is_num2) { return is_num1; } else { return a->get_id() < b->get_id(); } } }; struct has_nlira_functor { struct found{}; ast_manager &m; arith_util u; has_nlira_functor(ast_manager &_m) : m(_m), u(m) {} void operator()(var *) {} void operator()(quantifier *) {} void operator()(app *n) { family_id fid = n->get_family_id(); if (fid != u.get_family_id()) return; switch(n->get_decl_kind()) { case OP_MUL: if (n->get_num_args() != 2) throw found(); if (!u.is_numeral(n->get_arg(0)) && !u.is_numeral(n->get_arg(1))) throw found(); return; case OP_IDIV: case OP_DIV: case OP_REM: case OP_MOD: if (!u.is_numeral(n->get_arg(1))) throw found(); return; default: return; } return; } }; bool has_nlira(expr_ref_vector &v) { has_nlira_functor fn(v.m()); expr_fast_mark1 visited; try { for (expr *e : v) quick_for_each_expr(fn, visited, e); } catch (const has_nlira_functor::found &) { return true; } return false; } } namespace spacer { lemma_quantifier_generalizer::lemma_quantifier_generalizer(context &ctx, bool normalize_cube) : lemma_generalizer(ctx), m(ctx.get_ast_manager()), m_arith(m), m_cube(m), m_normalize_cube(normalize_cube), m_offset(0) {} void lemma_quantifier_generalizer::collect_statistics(statistics &st) const { st.update("time.spacer.solve.reach.gen.quant", m_st.watch.get_seconds()); st.update("quantifier gen", m_st.count); st.update("quantifier gen failures", m_st.num_failures); } /** Finds candidates terms to be existentially abstracted. A term t is a candidate if (a) t is ground (b) t appears in an expression of the form (select A t) for some array A (c) t appears in an expression of the form (select A (+ t v)) where v is ground The goal is to pick candidates that might result in a lemma in the essentially uninterpreted fragment of FOL or its extensions. */ void lemma_quantifier_generalizer::find_candidates(expr *e, app_ref_vector &candidates) { if (!contains_selects(e, m)) return; app_ref_vector indices(m); get_select_indices(e, indices); app_ref_vector extra(m); expr_sparse_mark marked_args; // Make sure not to try and quantify already-quantified indices for (unsigned idx=0, sz = indices.size(); idx < sz; idx++) { // skip expressions that already contain a quantified variable if (has_zk_const(indices.get(idx))) { continue; } app *index = indices.get(idx); TRACE ("spacer_qgen", tout << "Candidate: "<< mk_pp(index, m) << " in " << mk_pp(e, m) << "\n";); extra.push_back(index); if (m_arith.is_add(index)) { for (expr * arg : *index) { if (!is_app(arg) || marked_args.is_marked(arg)) {continue;} marked_args.mark(arg); candidates.push_back (to_app(arg)); } } } std::sort(candidates.c_ptr(), candidates.c_ptr() + candidates.size(), index_lt_proc(m)); // keep actual select indices in the order found at the back of // candidate list. There is no particular reason for this order candidates.append(extra); } /// returns true if expression e contains a sub-expression of the form (select A idx) where /// idx contains exactly one skolem from zks. Returns idx and the skolem bool lemma_quantifier_generalizer::match_sk_idx(expr *e, app_ref_vector const &zks, expr *&idx, app *&sk) { if (zks.size() != 1) return false; contains_app has_zk(m, zks.get(0)); if (!contains_selects(e, m)) return false; app_ref_vector indices(m); get_select_indices(e, indices); if (indices.size() > 2) return false; unsigned i=0; if (indices.size() == 1) { if (!has_zk(indices.get(0))) return false; } else { if (has_zk(indices.get(0)) && !has_zk(indices.get(1))) i = 0; else if (!has_zk(indices.get(0)) && has_zk(indices.get(1))) i = 1; else if (!has_zk(indices.get(0)) && !has_zk(indices.get(1))) return false; } idx = indices.get(i); sk = zks.get(0); return true; } namespace { expr* times_minus_one(expr *e, arith_util &arith) { expr *r; if (arith.is_times_minus_one (e, r)) { return r; } return arith.mk_mul(arith.mk_numeral(rational(-1), arith.is_int(get_sort(e))), e); } } /** Attempts to rewrite a cube so that quantified variable appears as a top level argument of select-term Find sub-expression of the form (select A (+ sk!0 t)) and replaces (+ sk!0 t) --> sk!0 and sk!0 --> (+ sk!0 (* (- 1) t)) rewrites bind to (+ bsk!0 t) where bsk!0 is the original binding for sk!0 Current implementation is an ugly hack for one special case. Should be rewritten based on an equality solver from qe */ void lemma_quantifier_generalizer::cleanup(expr_ref_vector &cube, app_ref_vector const &zks, expr_ref &bind) { if (zks.size() != 1) return; arith_util arith(m); expr *idx = nullptr; app *sk = nullptr; expr_ref rep(m); for (expr *e : cube) { if (match_sk_idx(e, zks, idx, sk)) { CTRACE("spacer_qgen", idx != sk, tout << "Possible cleanup of " << mk_pp(idx, m) << " in " << mk_pp(e, m) << " on " << mk_pp(sk, m) << "\n";); if (!arith.is_add(idx)) continue; app *a = to_app(idx); bool found = false; expr_ref_vector kids(m); expr_ref_vector kids_bind(m); for (expr* arg : *a) { if (arg == sk) { found = true; kids.push_back(arg); kids_bind.push_back(bind); } else { kids.push_back(times_minus_one(arg, arith)); kids_bind.push_back(arg); } } if (!found) continue; rep = arith.mk_add(kids.size(), kids.c_ptr()); bind = arith.mk_add(kids_bind.size(), kids_bind.c_ptr()); TRACE("spacer_qgen", tout << "replace " << mk_pp(idx, m) << " with " << mk_pp(rep, m) << "\n" << "bind is: " << bind << "\n";); break; } } if (rep) { expr_safe_replace rw(m); rw.insert(sk, rep); rw.insert(idx, sk); rw(cube); TRACE("spacer_qgen", tout << "Cleaned cube to: " << mk_and(cube) << "\n";); } } /** Create an abstract cube by abstracting a given term with a given variable. On return, gnd_cube contains all ground literals from m_cube abs_cube contains all newly quantified literals from m_cube lb contains an expression determining the lower bound on the variable ub contains an expression determining the upper bound on the variable Conjunction of gnd_cube and abs_cube is the new quantified cube lb and ub are null if no bound was found */ void lemma_quantifier_generalizer::mk_abs_cube(lemma_ref &lemma, app *term, var *var, expr_ref_vector &gnd_cube, expr_ref_vector &abs_cube, expr *&lb, expr *&ub, unsigned &stride) { // create an abstraction function that maps candidate term to variables expr_safe_replace sub(m); // term -> var sub.insert(term, var); rational val; if (m_arith.is_numeral(term, val)) { bool is_int = val.is_int(); expr_ref minus_one(m); minus_one = m_arith.mk_numeral(rational(-1), is_int); // term+1 -> var+1 if term is a number sub.insert( m_arith.mk_numeral(val + 1, is_int), m_arith.mk_add(var, m_arith.mk_numeral(rational(1), is_int))); // -term-1 -> -1*var + -1 if term is a number sub.insert( m_arith.mk_numeral(-1*val + -1, is_int), m_arith.mk_add (m_arith.mk_mul (minus_one, var), minus_one)); } lb = nullptr; ub = nullptr; for (expr *lit : m_cube) { expr_ref abs_lit(m); sub(lit, abs_lit); if (lit == abs_lit) {gnd_cube.push_back(lit);} else { expr *e1, *e2; // generalize v=num into v>=num if (m.is_eq(abs_lit, e1, e2) && (e1 == var || e2 == var)) { if (m_arith.is_numeral(e1)) { abs_lit = m_arith.mk_ge(var, e1); } else if (m_arith.is_numeral(e2)) { abs_lit = m_arith.mk_ge(var, e2); } } abs_cube.push_back(abs_lit); if (contains_selects(abs_lit, m)) { expr_ref_vector pob_cube(m); flatten_and(lemma->get_pob()->post(), pob_cube); find_stride(pob_cube, abs_lit, stride); } if (!lb && is_lb(var, abs_lit)) { lb = abs_lit; } else if (!ub && is_ub(var, abs_lit)) { ub = abs_lit; } } } } // -- returns true if e is an upper bound for var bool lemma_quantifier_generalizer::is_ub(var *var, expr *e) { expr *e1, *e2; // var <= e2 if ((m_arith.is_le (e, e1, e2) || m_arith.is_lt(e, e1, e2)) && var == e1) { return true; } // e1 >= var if ((m_arith.is_ge(e, e1, e2) || m_arith.is_gt(e, e1, e2)) && var == e2) { return true; } // t <= -1*var if ((m_arith.is_le (e, e1, e2) || m_arith.is_lt(e, e1, e2)) && m_arith.is_times_minus_one(e2, e2) && e2 == var) { return true; } // -1*var >= t if ((m_arith.is_ge(e, e1, e2) || m_arith.is_gt(e, e1, e2)) && m_arith.is_times_minus_one(e1, e1) && e1 == var) { return true; } // ! (var >= e2) if (m.is_not (e, e1) && is_lb(var, e1)) { return true; } // var + t1 <= t2 if ((m_arith.is_le(e, e1, e2) || m_arith.is_lt(e, e1, e2)) && m_arith.is_add(e1)) { app *a = to_app(e1); for (expr* arg : *a) { if (arg == var) return true; } } // t1 <= t2 + -1*var if ((m_arith.is_le(e, e1, e2) || m_arith.is_lt(e, e1, e2)) && m_arith.is_add(e2)) { app *a = to_app(e2); for (expr* arg : *a) { if (m_arith.is_times_minus_one(arg, arg) && arg == var) return true; } } // t1 >= t2 + var if ((m_arith.is_ge(e, e1, e2) || m_arith.is_gt(e, e1, e2)) && m_arith.is_add(e2)) { app *a = to_app(e2); for (expr * arg : *a) { if (arg == var) return true; } } // -1*var + t1 >= t2 if ((m_arith.is_ge(e, e1, e2) || m_arith.is_gt(e, e1, e2)) && m_arith.is_add(e1)) { app *a = to_app(e1); for (expr * arg : *a) { if (m_arith.is_times_minus_one(arg, arg) && arg == var) return true; } } return false; } // -- returns true if e is a lower bound for var bool lemma_quantifier_generalizer::is_lb(var *var, expr *e) { expr *e1, *e2; // var >= e2 if ((m_arith.is_ge (e, e1, e2) || m_arith.is_gt(e, e1, e2)) && var == e1) { return true; } // e1 <= var if ((m_arith.is_le(e, e1, e2) || m_arith.is_lt(e, e1, e2)) && var == e2) { return true; } // t >= -1*var if ((m_arith.is_ge (e, e1, e2) || m_arith.is_gt(e, e1, e2)) && m_arith.is_times_minus_one(e2, e2) && e2 == var) { return true; } // -1*var <= t if ((m_arith.is_le(e, e1, e2) || m_arith.is_lt(e, e1, e2)) && m_arith.is_times_minus_one(e1, e1) && e1 == var) { return true; } // ! (var <= e2) if (m.is_not (e, e1) && is_ub(var, e1)) { return true; } // var + t1 >= t2 if ((m_arith.is_ge(e, e1, e2) || m_arith.is_gt(e, e1, e2)) && m_arith.is_add(e1)) { app *a = to_app(e1); for (expr * arg : *a) { if (arg == var) return true; } } // t1 >= t2 + -1*var if ((m_arith.is_ge(e, e1, e2) || m_arith.is_gt(e, e1, e2)) && m_arith.is_add(e2)) { app *a = to_app(e2); for (expr * arg : *a) { if (m_arith.is_times_minus_one(arg, arg) && arg == var) return true; } } // t1 <= t2 + var if ((m_arith.is_le(e, e1, e2) || m_arith.is_lt(e, e1, e2)) && m_arith.is_add(e2)) { app *a = to_app(e2); for (expr * arg : *a) { if (arg == var) return true; } } // -1*var + t1 <= t2 if ((m_arith.is_le(e, e1, e2) || m_arith.is_lt(e, e1, e2)) && m_arith.is_add(e1)) { app *a = to_app(e1); for (expr * arg : *a) { if (m_arith.is_times_minus_one(arg, arg) && arg == var) return true; } } return false; } bool lemma_quantifier_generalizer::generalize (lemma_ref &lemma, app *term) { expr *lb = nullptr, *ub = nullptr; unsigned stride = 1; expr_ref_vector gnd_cube(m); expr_ref_vector abs_cube(m); var_ref var(m); var = m.mk_var (m_offset, get_sort(term)); mk_abs_cube(lemma, term, var, gnd_cube, abs_cube, lb, ub, stride); if (abs_cube.empty()) {return false;} if (has_nlira(abs_cube)) { TRACE("spacer_qgen", tout << "non-linear expression: " << abs_cube << "\n";); return false; } TRACE("spacer_qgen", tout << "abs_cube is: " << mk_and(abs_cube) << "\n"; tout << "term: " << mk_pp(term, m) << "\n"; tout << "lb = "; if (lb) tout << mk_pp(lb, m); else tout << "none"; tout << "\n"; tout << "ub = "; if (ub) tout << mk_pp(ub, m); else tout << "none"; tout << "\n";); if (!lb && !ub) return false; // -- guess lower or upper bound if missing if (!lb) { abs_cube.push_back (m_arith.mk_ge (var, term)); lb = abs_cube.back(); } if (!ub) { abs_cube.push_back (m_arith.mk_le(var, term)); ub = abs_cube.back(); } rational init; expr_ref constant(m); if (is_var(to_app(lb)->get_arg(0))) constant = to_app(lb)->get_arg(1); else constant = to_app(lb)->get_arg(0); if (stride > 1 && m_arith.is_numeral(constant, init)) { unsigned mod = init.get_unsigned() % stride; TRACE("spacer_qgen", tout << "mod=" << mod << " init=" << init << " stride=" << stride << "\n"; tout.flush();); abs_cube.push_back (m.mk_eq(m_arith.mk_mod(var, m_arith.mk_numeral(rational(stride), true)), m_arith.mk_numeral(rational(mod), true)));} // skolemize expr_ref gnd(m); app_ref_vector zks(m); ground_expr(mk_and(abs_cube), gnd, zks); flatten_and(gnd, gnd_cube); TRACE("spacer_qgen", tout << "New CUBE is: " << gnd_cube << "\n";); // check if the result is a true lemma unsigned uses_level = 0; pred_transformer &pt = lemma->get_pob()->pt(); if (pt.check_inductive(lemma->level(), gnd_cube, uses_level, lemma->weakness())) { TRACE("spacer_qgen", tout << "Quantifier Generalization Succeeded!\n" << "New CUBE is: " << gnd_cube << "\n";); SASSERT(zks.size() >= static_cast(m_offset)); // lift quantified variables to top of select expr_ref ext_bind(m); ext_bind = term; cleanup(gnd_cube, zks, ext_bind); // XXX better do that check before changing bind in cleanup() // XXX Or not because substitution might introduce _n variable into bind if (m_ctx.get_manager().is_n_formula(ext_bind)) { // XXX this creates an instance, but not necessarily the needed one // XXX This is sound because any instance of // XXX universal quantifier is sound // XXX needs better long term solution. leave // comment here for the future m_ctx.get_manager().formula_n2o(ext_bind, ext_bind, 0); } lemma->update_cube(lemma->get_pob(), gnd_cube); lemma->set_level(uses_level); SASSERT(var->get_idx() < zks.size()); SASSERT(is_app(ext_bind)); lemma->add_skolem(zks.get(var->get_idx()), to_app(ext_bind)); return true; } return false; } bool lemma_quantifier_generalizer::find_stride(expr_ref_vector &cube, expr_ref &pattern, unsigned &stride) { expr_ref tmp(m); tmp = mk_and(cube); normalize(tmp, tmp, false, true); cube.reset(); flatten_and(tmp, cube); app_ref_vector indices(m); get_select_indices(pattern, indices); CTRACE("spacer_qgen", indices.empty(), tout << "Found no select indices in: " << pattern << "\n";); // TBD: handle multi-dimensional arrays and literals with multiple // select terms if (indices.size() != 1) return false; app *p_index = indices.get(0); unsigned_vector instances; for (expr* lit : cube) { if (!contains_selects(lit, m)) continue; indices.reset(); get_select_indices(lit, indices); // TBD: handle multi-dimensional arrays if (indices.size() != 1) continue; app *candidate = indices.get(0); unsigned size = p_index->get_num_args(); unsigned matched = 0; for (unsigned p = 0; p < size; p++) { expr *arg = p_index->get_arg(p); if (is_var(arg)) { rational val; if (p < candidate->get_num_args() && m_arith.is_numeral(candidate->get_arg(p), val) && val.is_unsigned()) { instances.push_back(val.get_unsigned()); } } else { for (expr* cand : *candidate) { if (cand == arg) { matched++; break; } } } } if (matched < size - 1) continue; if (candidate->get_num_args() == matched) instances.push_back(0); TRACE("spacer_qgen", tout << "Match succeeded!\n";); } if (instances.size() <= 1) return false; std::sort(instances.begin(), instances.end()); stride = instances[1]-instances[0]; TRACE("spacer_qgen", tout << "Index Stride is: " << stride << "\n";); return true; } void lemma_quantifier_generalizer::operator()(lemma_ref &lemma) { if (lemma->get_cube().empty()) return; if (!lemma->has_pob()) return; m_st.count++; scoped_watch _w_(m_st.watch); TRACE("spacer_qgen", tout << "initial cube: " << mk_and(lemma->get_cube()) << "\n";); // setup the cube m_cube.reset(); m_cube.append(lemma->get_cube()); if (m_normalize_cube) { // -- re-normalize the cube expr_ref c(m); c = mk_and(m_cube); normalize(c, c, false, true); m_cube.reset(); flatten_and(c, m_cube); TRACE("spacer_qgen", tout << "normalized cube:\n" << mk_and(m_cube) << "\n";); } // first unused free variable m_offset = lemma->get_pob()->get_free_vars_size(); // for every literal, find a candidate term to abstract for (unsigned i=0; i < m_cube.size(); i++) { expr *r = m_cube.get(i); // generate candidates for abstraction app_ref_vector candidates(m); find_candidates(r, candidates); if (candidates.empty()) continue; // for every candidate for (unsigned arg=0, sz = candidates.size(); arg < sz; arg++) { if (generalize (lemma, candidates.get(arg))) { return; } else { ++m_st.num_failures; } } } } } z3-z3-4.8.7/src/muz/spacer/spacer_sat_answer.cpp000066400000000000000000000143271356505360400215240ustar00rootroot00000000000000#include "muz/spacer/spacer_sat_answer.h" #include "muz/base/dl_context.h" #include "muz/base/dl_rule.h" #include "ast/scoped_proof.h" #include "smt/smt_solver.h" namespace spacer { struct ground_sat_answer_op::frame { reach_fact *m_rf; pred_transformer &m_pt; expr_ref_vector m_gnd_subst; expr_ref m_gnd_eq; expr_ref m_fact; unsigned m_visit; expr_ref_vector m_kids; frame(reach_fact *rf, pred_transformer &pt, const expr_ref_vector &gnd_subst) : m_rf(rf), m_pt(pt), m_gnd_subst(gnd_subst), m_gnd_eq(pt.get_ast_manager()), m_fact(pt.get_ast_manager()), m_visit(0), m_kids(pt.get_ast_manager()) { ast_manager &m = pt.get_ast_manager(); spacer::manager &pm = pt.get_manager(); m_fact = m.mk_app(head(), m_gnd_subst.size(), m_gnd_subst.c_ptr()); if (pt.head()->get_arity() == 0) m_gnd_eq = m.mk_true(); else { SASSERT(m_gnd_subst.size() == pt.head()->get_arity()); for (unsigned i = 0, sz = pt.sig_size(); i < sz; ++i) { m_gnd_eq = m.mk_eq(m.mk_const(pm.o2n(pt.sig(i), 0)), m_gnd_subst.get(i)); } } } func_decl* head() {return m_pt.head();} expr* fact() {return m_fact;} const datalog::rule &rule() {return m_rf->get_rule();} pred_transformer &pt() {return m_pt;} }; ground_sat_answer_op::ground_sat_answer_op(const context &ctx) : m_ctx(ctx), m(m_ctx.get_ast_manager()), m_pm(m_ctx.get_manager()), m_pinned(m) { } proof_ref ground_sat_answer_op::operator()(pred_transformer &query) { // -- turn on proof mode so that proof constructing API in ast_manager work correctly scoped_proof _pf(m); scoped_ptr factory(mk_smt_strategic_solver_factory(symbol::null)); m_solver = (*factory)(m, params_ref::get_empty(), m.proofs_enabled() /*proofs*/, true /*models*/, false /*unsat_core*/, symbol::null /*logic*/); // m_solver = mk_smt_solver(m, params_ref::get_empty(), symbol::null); vector todo, new_todo; // -- find substitution for a query if query is not nullary expr_ref_vector qsubst(m); if (query.head()->get_arity() > 0) { solver::scoped_push _s_(*m_solver); m_solver->assert_expr(query.get_last_rf()->get()); lbool res = m_solver->check_sat(0, nullptr); (void)res; SASSERT(res == l_true); model_ref mdl; m_solver->get_model(mdl); model::scoped_model_completion _scm(mdl, true); for (unsigned i = 0, sz = query.sig_size(); i < sz; ++i) { expr_ref arg(m), val(m); arg = m.mk_const(m_pm.o2n(query.sig(i), 0)); val = (*mdl)(arg); qsubst.push_back(val); } } todo.push_back(frame(query.get_last_rf(), query, qsubst)); expr_ref root_fact(m); root_fact = todo.back().fact(); while (!todo.empty()) { frame &curr = todo.back(); if (m_cache.contains(curr.fact())) { todo.pop_back(); continue; } if (curr.m_visit == 0) { new_todo.reset(); mk_children(curr, new_todo); curr.m_visit = 1; // curr becomes invalid todo.append(new_todo); } else { proof* pf = mk_proof_step(curr); m_pinned.push_back(curr.fact()); m_cache.insert(curr.fact(), pf); todo.pop_back(); } } m_solver.reset(); return proof_ref(m_cache.find(root_fact), m); } void ground_sat_answer_op::mk_children(frame &fr, vector &todo) { const datalog::rule &r = fr.rule(); ptr_vector preds; fr.pt().find_predecessors(r, preds); if (preds.empty()) return; const reach_fact_ref_vector &kid_rfs = fr.m_rf->get_justifications(); solver::scoped_push _s_(*m_solver); m_solver->assert_expr(fr.m_gnd_eq); unsigned ut_sz = r.get_uninterpreted_tail_size(); for (unsigned i = 0; i < ut_sz; ++i) { expr_ref f(m); m_pm.formula_n2o(kid_rfs.get(i)->get(), f, i); m_solver->assert_expr(f); } m_solver->assert_expr(fr.pt().transition()); m_solver->assert_expr(fr.pt().rule2tag(&r)); lbool res = m_solver->check_sat(0, nullptr); (void)res; VERIFY(res == l_true); model_ref mdl; m_solver->get_model(mdl); expr_ref_vector subst(m); for (unsigned i = 0, sz = preds.size(); i < sz; ++i) { subst.reset(); mk_child_subst_from_model(preds.get(i), i, mdl, subst); todo.push_back(frame(kid_rfs.get(i), m_ctx.get_pred_transformer(preds.get(i)), subst)); fr.m_kids.push_back(todo.back().fact()); } } void ground_sat_answer_op::mk_child_subst_from_model(func_decl *pred, unsigned j, model_ref &mdl, expr_ref_vector &subst) { model::scoped_model_completion _scm(mdl, true); pred_transformer &pt = m_ctx.get_pred_transformer(pred); for (unsigned i = 0, sz = pt.sig_size(); i < sz; ++i) { expr_ref arg(m), val(m); arg = m.mk_const(m_pm.o2o(pt.sig(i), 0, j)); val = (*mdl)(arg); subst.push_back(val); } } proof *ground_sat_answer_op::mk_proof_step(frame &fr) { svector> positions; vector substs; proof_ref_vector premises(m); datalog::rule_manager &rm = m_ctx.get_datalog_context().get_rule_manager(); expr_ref rule_fml(m); rm.to_formula(fr.rule(), rule_fml); // premises.push_back(fr.rule().get_proof()); premises.push_back(m.mk_asserted(rule_fml)); for (auto &k : fr.m_kids) {premises.push_back(m_cache.find(k));} for (unsigned i = 0; i < premises.size(); i++) { positions.push_back(std::make_pair(0,i)); } for (unsigned i = 0; i <= premises.size(); i++) { substs.push_back(expr_ref_vector(m)); } m_pinned.push_back(m.mk_hyper_resolve(premises.size(), premises.c_ptr(), fr.fact(), positions, substs)); return to_app(m_pinned.back()); } } z3-z3-4.8.7/src/muz/spacer/spacer_sat_answer.h000066400000000000000000000017131356505360400211640ustar00rootroot00000000000000/*++ Copyright (c) 2018 Arie Gurfinkel Module Name: spacer_sat_answer.h Abstract: Compute refutation proof for CHC Author: Arie Gurfinkel Revision History: --*/ #ifndef _SPACER_SAT_ANSWER_H_ #define _SPACER_SAT_ANSWER_H_ #include "muz/spacer/spacer_context.h" #include "ast/ast.h" #include "util/obj_hashtable.h" #include "model/model.h" #include "solver/solver.h" namespace spacer { class ground_sat_answer_op { const context &m_ctx; ast_manager &m; const manager &m_pm; expr_ref_vector m_pinned; obj_map m_cache; ref m_solver; struct frame; proof *mk_proof_step(frame &fr); void mk_children(frame &fr, vector &todo); void mk_child_subst_from_model(func_decl *pred, unsigned i, model_ref &mdl, expr_ref_vector &subst); public: ground_sat_answer_op(const context &ctx); proof_ref operator() (pred_transformer &query); }; } #endif z3-z3-4.8.7/src/muz/spacer/spacer_sem_matcher.cpp000066400000000000000000000073241356505360400216440ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation and Arie Gurfinkel Module Name: sem_matcher.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-02-02. Arie Gurfinkel Revision History: --*/ #include "muz/spacer/spacer_sem_matcher.h" namespace spacer { sem_matcher::sem_matcher(ast_manager &man) : m(man), m_arith(m), m_pinned(m) {} bool sem_matcher::match_var (var *v, expr *e) { expr_offset r; if (m_subst->find(v, 0, r)) { if (!m.are_equal(r.get_expr(), e)) { return false; } } else { m_subst->insert(v, 0, expr_offset(e, 1)); } return true; } bool sem_matcher::operator()(expr * e1, expr * e2, substitution & s, bool &pos) { reset(); m_subst = &s; m_todo.push_back(expr_pair(e1, e2)); // true on the first run through the loop bool top = true; pos = true; while (!m_todo.empty()) { expr_pair const & p = m_todo.back(); if (is_var(p.first)) { if (!match_var(to_var(p.first), p.second)) { return false; } m_todo.pop_back(); top = false; continue; } if (is_var(p.second)) return false; if (!is_app(p.first)) return false; if (!is_app(p.second)) return false; app * n1 = to_app(p.first); app * n2 = to_app(p.second); expr *t = nullptr; // strip negation if (top && n1->get_decl() != n2->get_decl()) { if (m.is_not(n1, t) && !m.is_not(n2) && is_app(t) && to_app(t)->get_decl() == n2->get_decl()) { pos = false; n1 = to_app(e1); } else if (!m.is_not(n1) && m.is_not(n2, t) && is_app(t) && to_app(t)->get_decl() == n1->get_decl()) { pos = false; n2 = to_app(t); } } top = false; if (n1->get_decl() != n2->get_decl()) { expr *e1 = nullptr, *e2 = nullptr; rational val1, val2; // x<=y == !(x>y) if (m_arith.is_le(n1) && m.is_not(n2, t) && m_arith.is_gt(t)) { n2 = to_app(t); } else if (m_arith.is_le(n2) && m.is_not(n1, t) && m_arith.is_gt(t)) { n1 = to_app(t); } // x>=y == !(xget_num_args(); if (num_args1 != n2->get_num_args()) return false; m_todo.pop_back(); if (num_args1 == 0) continue; unsigned j = num_args1; while (j > 0) { --j; m_todo.push_back(expr_pair(n1->get_arg(j), n2->get_arg(j))); } } return true; } void sem_matcher::reset() { m_todo.reset(); m_pinned.reset(); } } z3-z3-4.8.7/src/muz/spacer/spacer_sem_matcher.h000066400000000000000000000032431356505360400213050ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation and Arie Gurfinkel Module Name: sem_matcher.h Abstract: Semantic matcher Author: Leonardo de Moura (leonardo) 2008-02-02. Arie Gurfinkel Revision History: --*/ #ifndef SPACER_SEM_MATCHER_H_ #define SPACER_SEM_MATCHER_H_ #include "ast/substitution/substitution.h" #include "ast/arith_decl_plugin.h" #include "util/hashtable.h" namespace spacer { /** \brief Functor for matching expressions. */ class sem_matcher { typedef std::pair expr_pair; typedef pair_hash, obj_ptr_hash > expr_pair_hash; typedef hashtable > cache; ast_manager &m; arith_util m_arith; expr_ref_vector m_pinned; substitution * m_subst; svector m_todo; void reset(); bool match_var(var *v, expr *e); public: sem_matcher(ast_manager &man); /** \brief Return true if e2 is an instance of e1. In case of success (result is true), it will store the substitution that makes e1 equals to e2 into s. Sets pos to true if the match is positive and to false if it is negative (i.e., e1 equals !e2) For example: 1) e1 = f(g(x), x), e2 = f(g(h(a)), h(a)) The result is true, and s will contain x -> h(a) 2) e1 = f(a, x) e2 = f(x, a) The result is false. 3) e1 = f(x, x) e2 = f(y, a) The result is false 4) e1 = f(x, y) e2 = f(h(z), a) The result is true, and s contains x->h(z) and y->a */ bool operator()(expr * e1, expr * e2, substitution & s, bool &pos); }; } #endif /* SPACER_SEM_MATCHER_H_ */ z3-z3-4.8.7/src/muz/spacer/spacer_sym_mux.cpp000066400000000000000000000113761356505360400210600ustar00rootroot00000000000000/*++ Copyright (c) 2018 Arie Gurfinkel and Microsoft Corporation Module Name: sym_mux.cpp Abstract: A symbol multiplexer that helps with having multiple versions of each of a set of symbols. Author: Arie Gurfinkel Krystof Hoder (t-khoder) 2011-9-8. Revision History: --*/ #include "ast/ast_pp.h" #include "ast/for_each_expr.h" #include "ast/rewriter/rewriter.h" #include "ast/rewriter/rewriter_def.h" #include "muz/spacer/spacer_util.h" #include "muz/spacer/spacer_sym_mux.h" using namespace spacer; sym_mux::sym_mux(ast_manager & m) : m(m) {} sym_mux::~sym_mux() { for (auto &entry : m_entries) { dealloc(entry.m_value); } } func_decl_ref sym_mux::mk_variant(func_decl *fdecl, unsigned i) const { func_decl_ref v(m); std::string name = fdecl->get_name().str(); std::string suffix = "_"; suffix += i == 0 ? "n" : std::to_string(i - 1); name += suffix; v = m.mk_func_decl(symbol(name.c_str()), fdecl->get_arity(), fdecl->get_domain(), fdecl->get_range()); return v; } void sym_mux::register_decl(func_decl *fdecl) { sym_mux_entry *entry = alloc(sym_mux_entry, m); entry->m_main = fdecl; entry->m_variants.push_back(mk_variant(fdecl, 0)); entry->m_variants.push_back(mk_variant(fdecl, 1)); m_entries.insert(fdecl, entry); m_muxes.insert(entry->m_variants.get(0), std::make_pair(entry, 0)); m_muxes.insert(entry->m_variants.get(1), std::make_pair(entry, 1)); } void sym_mux::ensure_capacity(sym_mux_entry &entry, unsigned sz) const { while (entry.m_variants.size() < sz) { unsigned idx = entry.m_variants.size(); entry.m_variants.push_back (mk_variant(entry.m_main, idx)); m_muxes.insert(entry.m_variants.back(), std::make_pair(&entry, idx)); } } bool sym_mux::find_idx(func_decl * sym, unsigned & idx) const { std::pair entry; if (m_muxes.find(sym, entry)) {idx = entry.second; return true;} return false; } func_decl * sym_mux::find_by_decl(func_decl* fdecl, unsigned idx) const { sym_mux_entry *entry = nullptr; if (m_entries.find(fdecl, entry)) { ensure_capacity(*entry, idx+1); return entry->m_variants.get(idx); } return nullptr; } func_decl * sym_mux::shift_decl(func_decl * decl, unsigned src_idx, unsigned tgt_idx) const { std::pair entry; if (m_muxes.find(decl, entry)) { SASSERT(entry.second == src_idx); ensure_capacity(*entry.first, tgt_idx + 1); return entry.first->m_variants.get(tgt_idx); } UNREACHABLE(); return nullptr; } namespace { struct formula_checker { formula_checker(const sym_mux & parent, unsigned idx) : m_parent(parent), m_idx(idx), m_found(false) {} void operator()(expr * e) { if (m_found || !is_app(e)) { return; } func_decl * sym = to_app(e)->get_decl(); unsigned sym_idx; if (!m_parent.find_idx(sym, sym_idx)) { return; } bool have_idx = sym_idx == m_idx; m_found = !have_idx; } bool all_have_idx() const {return !m_found;} private: const sym_mux & m_parent; unsigned m_idx; bool m_found; }; } bool sym_mux::is_homogenous_formula(expr * e, unsigned idx) const { expr_mark visited; formula_checker fck(*this, idx); for_each_expr(fck, visited, e); return fck.all_have_idx(); } namespace { struct conv_rewriter_cfg : public default_rewriter_cfg { private: ast_manager & m; const sym_mux & m_parent; unsigned m_from_idx; unsigned m_to_idx; bool m_homogenous; expr_ref_vector m_pinned; public: conv_rewriter_cfg(const sym_mux & parent, unsigned from_idx, unsigned to_idx, bool homogenous) : m(parent.get_manager()), m_parent(parent), m_from_idx(from_idx), m_to_idx(to_idx), m_homogenous(homogenous), m_pinned(m) {(void) m_homogenous;} bool get_subst(expr * s, expr * & t, proof * & t_pr) { if (!is_app(s)) { return false; } app * a = to_app(s); func_decl * sym = a->get_decl(); if (!m_parent.has_index(sym, m_from_idx)) { SASSERT(!m_homogenous || !m_parent.is_muxed(sym)); return false; } func_decl * tgt = m_parent.shift_decl(sym, m_from_idx, m_to_idx); t = m.mk_app(tgt, a->get_args()); m_pinned.push_back(t); return true; } }; } void sym_mux::shift_expr(expr * f, unsigned src_idx, unsigned tgt_idx, expr_ref & res, bool homogenous) const { if (src_idx == tgt_idx) {res = f;} else { conv_rewriter_cfg r_cfg(*this, src_idx, tgt_idx, homogenous); rewriter_tpl rwr(m, false, r_cfg); rwr(f, res); } } z3-z3-4.8.7/src/muz/spacer/spacer_sym_mux.h000066400000000000000000000044001356505360400205130ustar00rootroot00000000000000/*++ Copyright (c) 2018 Arie Gurfinkel and Microsoft Corporation Module Name: sym_mux.h Abstract: A symbol multiplexer that helps with having multiple versions of each of a set of symbols. Author: Arie Gurfinkel Krystof Hoder (t-khoder) 2011-9-8. Revision History: --*/ #ifndef _SYM_MUX_H_ #define _SYM_MUX_H_ #include #include "ast/ast.h" #include "util/map.h" #include "util/vector.h" namespace spacer { class sym_mux { private: class sym_mux_entry { public: func_decl_ref m_main; func_decl_ref_vector m_variants; sym_mux_entry(ast_manager &m) : m_main(m), m_variants(m) {}; }; typedef obj_map decl2entry_map; typedef obj_map > mux2entry_map; ast_manager &m; mutable decl2entry_map m_entries; mutable mux2entry_map m_muxes; func_decl_ref mk_variant(func_decl *fdecl, unsigned i) const; void ensure_capacity(sym_mux_entry &entry, unsigned sz) const; public: sym_mux(ast_manager & m); ~sym_mux(); ast_manager & get_manager() const { return m; } void register_decl(func_decl *fdecl); bool find_idx(func_decl * sym, unsigned & idx) const; bool has_index(func_decl * sym, unsigned idx) const {unsigned v; return find_idx(sym, v) && idx == v;} bool is_muxed(func_decl *fdecl) const {return m_muxes.contains(fdecl);} /** \brief Return symbol created from prefix, or 0 if the prefix was never used. */ func_decl * find_by_decl(func_decl* fdecl, unsigned idx) const; /** \brief Return true if the only multiplexed symbols which e contains are of index idx. */ bool is_homogenous_formula(expr * e, unsigned idx) const; /** \brief Convert symbol sym which has to be of src_idx variant into variant tgt_idx. */ func_decl * shift_decl(func_decl * sym, unsigned src_idx, unsigned tgt_idx) const; /** \brief Convert src_idx symbols in formula f variant into tgt_idx. If homogenous is true, formula cannot contain symbols of other variants. */ void shift_expr(expr * f, unsigned src_idx, unsigned tgt_idx, expr_ref & res, bool homogenous = true) const; }; } #endif z3-z3-4.8.7/src/muz/spacer/spacer_unsat_core_learner.cpp000066400000000000000000000044101356505360400232200ustar00rootroot00000000000000/*++ Copyright (c) 2017 Arie Gurfinkel Module Name: spacer_unsat_core_learner.cpp Abstract: itp cores Author: Bernhard Gleiss Revision History: --*/ #include #include "ast/for_each_expr.h" #include "ast/proofs/proof_utils.h" #include "muz/spacer/spacer_unsat_core_learner.h" #include "muz/spacer/spacer_unsat_core_plugin.h" #include "muz/spacer/spacer_iuc_proof.h" #include "muz/spacer/spacer_util.h" namespace spacer { unsat_core_learner::~unsat_core_learner() { std::for_each(m_plugins.begin(), m_plugins.end(), delete_proc()); } void unsat_core_learner::register_plugin(unsat_core_plugin* plugin) { m_plugins.push_back(plugin); } void unsat_core_learner::compute_unsat_core(expr_ref_vector& unsat_core) { proof_post_order it(m_pr.get(), m); while (it.hasNext()) { proof* curr = it.next(); bool done = is_closed(curr); if (done) continue; if (m.get_num_parents(curr) > 0) { done = true; for (proof* p : m.get_parents(curr)) done &= !is_b_open(p); set_closed(curr, done); } // we have now collected all necessary information, // so we can visit the node // if the node mixes A-reasoning and B-reasoning // and contains non-closed premises if (!done) { if (is_a(curr) && is_b(curr)) { compute_partial_core(curr); } } } // give plugins chance to finalize their unsat-core-computation finalize(); // TODO: remove duplicates from unsat core? // move all lemmas into vector for (expr* e : m_unsat_core) { unsat_core.push_back(e); } } void unsat_core_learner::compute_partial_core(proof* step) { for (unsat_core_plugin* plugin : m_plugins) { if (is_closed(step)) break; plugin->compute_partial_core(step); } } void unsat_core_learner::finalize() { for (unsat_core_plugin* plugin : m_plugins) { plugin->finalize(); } } bool unsat_core_learner::is_closed(proof* p) { return m_closed.is_marked(p); } void unsat_core_learner::set_closed(proof* p, bool value) { m_closed.mark(p, value); } void unsat_core_learner::add_lemma_to_core(expr* lemma) { m_unsat_core.push_back(lemma); } } z3-z3-4.8.7/src/muz/spacer/spacer_unsat_core_learner.h000066400000000000000000000051061356505360400226700ustar00rootroot00000000000000/*++ Copyright (c) 2017 Arie Gurfinkel Module Name: spacer_unsat_core_learner.h Abstract: itp cores Author: Bernhard Gleiss Revision History: --*/ #ifndef _SPACER_UNSAT_CORE_LEARNER_H_ #define _SPACER_UNSAT_CORE_LEARNER_H_ #include "ast/ast.h" #include "muz/spacer/spacer_util.h" #include "muz/spacer/spacer_proof_utils.h" #include "muz/spacer/spacer_iuc_proof.h" namespace spacer { class unsat_core_plugin; class iuc_proof; class unsat_core_learner { typedef obj_hashtable expr_set; ast_manager& m; iuc_proof& m_pr; ptr_vector m_plugins; ast_mark m_closed; expr_ref_vector m_unsat_core; public: unsat_core_learner(ast_manager& m, iuc_proof& pr) : m(m), m_pr(pr), m_unsat_core(m) {}; virtual ~unsat_core_learner(); ast_manager& get_manager() {return m;} bool is_a(proof *pr) { // AG: treat hypotheses as A // AG: assume that all B-hyp have been eliminated // AG: this is not yet true in case of arithmetic eq_prop return m_pr.is_a_marked(pr) || is_h(pr); } bool is_b(proof *pr) {return m_pr.is_b_marked(pr);} bool is_h(proof *pr) {return m_pr.is_h_marked(pr);} bool is_b_pure(proof *pr) { return m_pr.is_b_pure(pr);} bool is_b_open(proof *p) {return m_pr.is_b_marked(p) && !is_closed (p);} /* * register a plugin for computation of partial unsat cores * currently plugins are called in the order they have been registered */ void register_plugin(unsat_core_plugin* plugin); /* * compute unsat core using the registered unsat-core-plugins */ void compute_unsat_core(expr_ref_vector& unsat_core); /* * getter/setter methods for data structures exposed to plugins * the following invariant can be assumed and need to be maintained by the plugins: * - a node is closed, iff it has already been interpolated, i.e. its contribution is * already covered by the unsat-core. */ bool is_closed(proof* p); void set_closed(proof* p, bool value); /* * adds a lemma to the unsat core */ void add_lemma_to_core(expr* lemma); private: /* * computes partial core for step by delegating computation to plugins */ void compute_partial_core(proof* step); /* * finalize computation of unsat-core */ void finalize(); }; } #endif z3-z3-4.8.7/src/muz/spacer/spacer_unsat_core_plugin.cpp000066400000000000000000000633371356505360400231030ustar00rootroot00000000000000/*++ Copyright (c) 2017 Arie Gurfinkel Module Name: spacer_unsat_core_plugin.cpp Abstract: plugin for itp cores Author: Bernhard Gleiss Revision History: --*/ #include #include #include "ast/rewriter/bool_rewriter.h" #include "ast/arith_decl_plugin.h" #include "ast/proofs/proof_utils.h" #include "solver/solver.h" #include "smt/smt_farkas_util.h" #include "smt/smt_solver.h" #include "muz/spacer/spacer_matrix.h" #include "muz/spacer/spacer_unsat_core_plugin.h" #include "muz/spacer/spacer_unsat_core_learner.h" #include "muz/spacer/spacer_iuc_proof.h" namespace spacer { unsat_core_plugin::unsat_core_plugin(unsat_core_learner& ctx): m(ctx.get_manager()), m_ctx(ctx) {}; void unsat_core_plugin_lemma::compute_partial_core(proof* step) { SASSERT(m_ctx.is_a(step)); SASSERT(m_ctx.is_b(step)); for (auto* p : m.get_parents(step)) { if (m_ctx.is_b_open (p)) { // by IH, premises that are AB marked are already closed SASSERT(!m_ctx.is_a(p)); add_lowest_split_to_core(p); } } m_ctx.set_closed(step, true); } void unsat_core_plugin_lemma::add_lowest_split_to_core(proof* step) const { SASSERT(m_ctx.is_b_open(step)); ptr_buffer todo; todo.push_back(step); while (!todo.empty()) { proof* pf = todo.back(); todo.pop_back(); // if current step hasn't been processed, if (!m_ctx.is_closed(pf)) { m_ctx.set_closed(pf, true); // the step is b-marked and not closed. // by I.H. the step must be already visited // so if it is also a-marked, it must be closed SASSERT(m_ctx.is_b(pf)); SASSERT(!m_ctx.is_a(pf)); // the current step needs to be interpolated: expr* fact = m.get_fact(pf); // if we trust the current step and we are able to use it if (m_ctx.is_b_pure (pf) && (m.is_asserted(pf) || is_literal(m, fact))) { // just add it to the core m_ctx.add_lemma_to_core(fact); } // otherwise recurse on premises else { for (proof* premise : m.get_parents(pf)) if (m_ctx.is_b_open(premise)) todo.push_back(premise); } } } } /*** * FARKAS */ void unsat_core_plugin_farkas_lemma::compute_partial_core(proof* step) { SASSERT(m_ctx.is_a(step)); SASSERT(m_ctx.is_b(step)); // XXX this assertion should be true so there is no need to check for it SASSERT (!m_ctx.is_closed (step)); func_decl* d = step->get_decl(); symbol sym; TRACE("spacer.farkas", tout << "looking at: " << mk_pp(step, m) << "\n";); if (!m_ctx.is_closed(step) && is_farkas_lemma(m, step)) { // weaker check : d->get_num_parameters() >= m.get_num_parents(step) + 2 SASSERT(d->get_num_parameters() == m.get_num_parents(step) + 2); SASSERT(m.has_fact(step)); coeff_lits_t coeff_lits; expr_ref_vector pinned(m); /* The farkas lemma represents a subproof starting from premise(-set)s A, BNP and BP(ure) and * ending in a disjunction D. We need to compute the contribution of BP, i.e. a formula, which * is entailed by BP and together with A and BNP entails D. * * Let Fark(F) be the farkas coefficient for F. We can use the fact that * (A*Fark(A) + BNP*Fark(BNP) + BP*Fark(BP) + (neg D)*Fark(D)) => false. (E1) * We further have that A+B => C implies (A \land B) => C. (E2) * * Alternative 1: * From (E1) immediately get that BP*Fark(BP) is a solution. * * Alternative 2: * We can rewrite (E2) to rewrite (E1) to * (BP*Fark(BP)) => (neg(A*Fark(A) + BNP*Fark(BNP) + (neg D)*Fark(D))) (E3) * and since we can derive (A*Fark(A) + BNP*Fark(BNP) + (neg D)*Fark(D)) from * A, BNP and D, we also know that it is inconsistent. Therefore * neg(A*Fark(A) + BNP*Fark(BNP) + (neg D)*Fark(D)) is a solution. * * Finally we also need the following workaround: * 1) Although we know from theory, that the Farkas coefficients are always nonnegative, * the Farkas coefficients provided by arith_core are sometimes negative (must be a bug) * as workaround we take the absolute value of the provided coefficients. */ parameter const* params = d->get_parameters() + 2; // point to the first Farkas coefficient TRACE("spacer.farkas", tout << "Farkas input: "<< "\n"; for (unsigned i = 0; i < m.get_num_parents(step); ++i) { proof * prem = m.get_parent(step, i); rational coef = params[i].get_rational(); bool b_pure = m_ctx.is_b_pure (prem); tout << (b_pure?"B":"A") << " " << coef << " " << mk_pp(m.get_fact(prem), m) << "\n"; } ); bool done = true; for (unsigned i = 0; i < m.get_num_parents(step); ++i) { proof * premise = m.get_parent(step, i); if (m_ctx.is_b_open (premise)) { SASSERT(!m_ctx.is_a(premise)); if (m_ctx.is_b_pure (premise)) { if (!m_use_constant_from_a) { rational coefficient = params[i].get_rational(); coeff_lits.push_back(std::make_pair(abs(coefficient), (app*)m.get_fact(premise))); } } else { // -- mixed premise, won't be able to close this proof step done = false; if (m_use_constant_from_a) { rational coefficient = params[i].get_rational(); coeff_lits.push_back(std::make_pair(abs(coefficient), (app*)m.get_fact(premise))); } } } else { if (m_use_constant_from_a) { rational coefficient = params[i].get_rational(); coeff_lits.push_back(std::make_pair(abs(coefficient), (app*)m.get_fact(premise))); } } } // TBD: factor into another method if (m_use_constant_from_a) { params += m.get_num_parents(step); // point to the first Farkas coefficient, which corresponds to a formula in the conclusion // the conclusion can either be a single formula or a disjunction of several formulas, we have to deal with both situations if (m.get_num_parents(step) + 2 < d->get_num_parameters()) { unsigned num_args = 1; expr* conclusion = m.get_fact(step); expr* const* args = &conclusion; if (m.is_or(conclusion)) { app* _or = to_app(conclusion); num_args = _or->get_num_args(); args = _or->get_args(); } SASSERT(m.get_num_parents(step) + 2 + num_args == d->get_num_parameters()); bool_rewriter brw(m); for (unsigned i = 0; i < num_args; ++i) { expr* premise = args[i]; expr_ref negatedPremise(m); brw.mk_not(premise, negatedPremise); pinned.push_back(negatedPremise); rational coefficient = params[i].get_rational(); coeff_lits.push_back(std::make_pair(abs(coefficient), to_app(negatedPremise))); } } } // only if all b-premises can be used directly, add the farkas core and close the step // AG: this decision needs to be re-evaluated. If the proof cannot be closed, literals above // AG: it will go into the core. However, it does not mean that this literal should/could not be added. m_ctx.set_closed(step, done); expr_ref res = compute_linear_combination(coeff_lits); TRACE("spacer.farkas", tout << "Farkas core: " << res << "\n";); m_ctx.add_lemma_to_core(res); } } expr_ref unsat_core_plugin_farkas_lemma::compute_linear_combination(const coeff_lits_t& coeff_lits) { smt::farkas_util util(m); if (m_use_constant_from_a) { util.set_split_literals (m_split_literals); // small optimization: if flag m_split_literals is set, then preserve diff constraints } for (auto& p : coeff_lits) { util.add(p.first, p.second); } if (m_use_constant_from_a) { return util.get(); } else { return expr_ref(mk_not(m, util.get()), m); } } void unsat_core_plugin_farkas_lemma_optimized::compute_partial_core(proof* step) { SASSERT(m_ctx.is_a(step)); SASSERT(m_ctx.is_b(step)); func_decl* d = step->get_decl(); symbol sym; if (!m_ctx.is_closed(step) && // if step is not already interpolated is_farkas_lemma(m, step)) { SASSERT(d->get_num_parameters() == m.get_num_parents(step) + 2); SASSERT(m.has_fact(step)); coeff_lits_t linear_combination; // collects all summands of the linear combination parameter const* params = d->get_parameters() + 2; // point to the first Farkas coefficient TRACE("spacer.farkas", tout << "Farkas input: "<< "\n"; for (unsigned i = 0; i < m.get_num_parents(step); ++i) { proof * prem = m.get_parent(step, i); rational coef = params[i].get_rational(); bool b_pure = m_ctx.is_b_pure (prem); tout << (b_pure?"B":"A") << " " << coef << " " << mk_pp(m.get_fact(prem), m) << "\n"; } ); bool can_be_closed = true; for (unsigned i = 0; i < m.get_num_parents(step); ++i) { proof * premise = m.get_parent(step, i); if (m_ctx.is_b(premise) && !m_ctx.is_closed(premise)) { SASSERT(!m_ctx.is_a(premise)); if (m_ctx.is_b_pure(premise)) { rational coefficient = params[i].get_rational(); linear_combination.push_back (std::make_pair(abs(coefficient), to_app(m.get_fact(premise)))); } else { can_be_closed = false; } } } // only if all b-premises can be used directly, close the step and add linear combinations for later processing if (can_be_closed) { m_ctx.set_closed(step, true); if (!linear_combination.empty()) { m_linear_combinations.push_back(linear_combination); } } } } struct farkas_optimized_less_than_pairs { inline bool operator() (const std::pair& pair1, const std::pair& pair2) const { return (pair1.second->get_id() < pair2.second->get_id()); } }; void unsat_core_plugin_farkas_lemma_optimized::finalize() { if (m_linear_combinations.empty()) { return; } DEBUG_CODE( for (auto& linear_combination : m_linear_combinations) { SASSERT(!linear_combination.empty()); }); // 1. construct ordered basis ptr_vector ordered_basis; obj_map map; unsigned counter = 0; for (const auto& linear_combination : m_linear_combinations) { for (const auto& pair : linear_combination) { if (!map.contains(pair.second)) { ordered_basis.push_back(pair.second); map.insert(pair.second, counter++); } } } // 2. populate matrix spacer_matrix matrix(m_linear_combinations.size(), ordered_basis.size()); for (unsigned i = 0; i < m_linear_combinations.size(); ++i) { auto linear_combination = m_linear_combinations[i]; for (const auto& pair : linear_combination) { matrix.set(i, map[pair.second], pair.first); } } // 3. perform gaussian elimination unsigned i = matrix.perform_gaussian_elimination(); // 4. extract linear combinations from matrix and add result to core for (unsigned k = 0; k < i; ++k)// i points to the row after the last row which is non-zero { coeff_lits_t coeff_lits; for (unsigned l = 0; l < matrix.num_cols(); ++l) { if (!matrix.get(k,l).is_zero()) { coeff_lits.push_back(std::make_pair(matrix.get(k, l), ordered_basis[l])); } } SASSERT(!coeff_lits.empty()); expr_ref linear_combination = compute_linear_combination(coeff_lits); m_ctx.add_lemma_to_core(linear_combination); } } expr_ref unsat_core_plugin_farkas_lemma_optimized::compute_linear_combination(const coeff_lits_t& coeff_lits) { smt::farkas_util util(m); for (auto const & p : coeff_lits) { util.add(p.first, p.second); } expr_ref negated_linear_combination = util.get(); SASSERT(m.is_not(negated_linear_combination)); return expr_ref(mk_not(m, negated_linear_combination), m); //TODO: rewrite the get-method to return nonnegated stuff? } void unsat_core_plugin_farkas_lemma_bounded::finalize() { if (m_linear_combinations.empty()) { return; } DEBUG_CODE( for (auto& linear_combination : m_linear_combinations) { SASSERT(!linear_combination.empty()); }); // 1. construct ordered basis ptr_vector ordered_basis; obj_map map; unsigned counter = 0; for (const auto& linear_combination : m_linear_combinations) { for (const auto& pair : linear_combination) { if (!map.contains(pair.second)) { ordered_basis.push_back(pair.second); map.insert(pair.second, counter++); } } } // 2. populate matrix spacer_matrix matrix(m_linear_combinations.size(), ordered_basis.size()); for (unsigned i=0; i < m_linear_combinations.size(); ++i) { auto linear_combination = m_linear_combinations[i]; for (const auto& pair : linear_combination) { matrix.set(i, map[pair.second], pair.first); } } matrix.print_matrix(); // 3. normalize matrix to integer values matrix.normalize(); arith_util util(m); vector coeffs; for (unsigned i = 0; i < matrix.num_rows(); ++i) { coeffs.push_back(expr_ref_vector(m)); } vector bounded_vectors; for (unsigned j = 0; j < matrix.num_cols(); ++j) { bounded_vectors.push_back(expr_ref_vector(m)); } // 4. find smallest n using guess and check algorithm for (unsigned n = 1; true; ++n) { params_ref p; p.set_bool("model", true); solver_ref s = mk_smt_solver(m, p, symbol::null); // TODO: incremental version? // add new variables w_in, for (unsigned i = 0; i < matrix.num_rows(); ++i) { std::string name = "w_" + std::to_string(i) + std::to_string(n); coeffs[i].push_back(m.mk_const(name, util.mk_int())); } // we need s_jn for (unsigned j = 0; j < matrix.num_cols(); ++j) { std::string name = "s_" + std::to_string(j) + std::to_string(n); bounded_vectors[j].push_back(m.mk_const(name, util.mk_int())); } // assert bounds for all s_jn for (unsigned l = 0; l < n; ++l) { for (unsigned j = 0; j < matrix.num_cols(); ++j) { expr* s_jn = bounded_vectors[j][l].get(); expr_ref lb(util.mk_le(util.mk_int(0), s_jn), m); expr_ref ub(util.mk_le(s_jn, util.mk_int(1)), m); s->assert_expr(lb); s->assert_expr(ub); } } // assert: forall i,j: a_ij = sum_k w_ik * s_jk for (unsigned i = 0; i < matrix.num_rows(); ++i) { for (unsigned j = 0; j < matrix.num_cols(); ++j) { SASSERT(matrix.get(i, j).is_int()); app_ref a_ij(util.mk_numeral(matrix.get(i,j), true), m); app_ref sum(util.mk_int(0), m); for (unsigned k = 0; k < n; ++k) { sum = util.mk_add(sum, util.mk_mul(coeffs[i][k].get(), bounded_vectors[j][k].get())); } expr_ref eq(m.mk_eq(a_ij, sum), m); s->assert_expr(eq); } } // check result lbool res = s->check_sat(0, nullptr); // if sat extract model and add corresponding linear combinations to core if (res == lbool::l_true) { model_ref model; s->get_model(model); for (unsigned k = 0; k < n; ++k) { coeff_lits_t coeff_lits; for (unsigned j = 0; j < matrix.num_cols(); ++j) { expr_ref evaluation(m); evaluation = (*model)(bounded_vectors[j][k].get()); if (!util.is_zero(evaluation)) { coeff_lits.push_back(std::make_pair(rational(1), ordered_basis[j])); } } SASSERT(!coeff_lits.empty()); // since then previous outer loop would have found solution already expr_ref linear_combination = compute_linear_combination(coeff_lits); m_ctx.add_lemma_to_core(linear_combination); } return; } } } unsat_core_plugin_min_cut::unsat_core_plugin_min_cut(unsat_core_learner& learner, ast_manager& m) : unsat_core_plugin(learner) {} /* * traverses proof rooted in step and constructs graph, which corresponds to the proof-DAG, with the following differences: * * 1) we want to skip vertices, which have a conclusion, which we don't like (call those steps bad and the other ones good). In other words, we start at a good step r and compute the smallest subproof with root r, which has only good leaves. Then we add an edge from the root to each of the leaves and remember that we already dealt with s. Then we recurse on all leaves. * 2) we want to introduce two vertices for all (unskipped) edges in order to run the min-cut algorithm * 3) we want to introduce a single super-source vertex, which is connected to all source-vertices of the proof-DAG and a * single super-sink vertex, to which all sink-vertices of the proof-DAG are connected * * 1) is dealt with using advance_to_lowest_partial_cut * 2) and 3) are dealt with using add_edge */ void unsat_core_plugin_min_cut::compute_partial_core(proof* step) { ptr_vector todo; SASSERT(m_ctx.is_a(step)); SASSERT(m_ctx.is_b(step)); SASSERT(m.get_num_parents(step) > 0); SASSERT(!m_ctx.is_closed(step)); todo.push_back(step); while (!todo.empty()) { proof* current = todo.back(); todo.pop_back(); // if we need to deal with the node and if we haven't added the corresponding edges so far if (!m_ctx.is_closed(current) && !m_visited.is_marked(current)) { // compute smallest subproof rooted in current, which has only good edges // add an edge from current to each leaf of that subproof // add the leaves to todo advance_to_lowest_partial_cut(current, todo); m_visited.mark(current, true); } } m_ctx.set_closed(step, true); } void unsat_core_plugin_min_cut::advance_to_lowest_partial_cut(proof* step, ptr_vector& todo) { bool is_sink = true; ptr_buffer todo_subproof; for (proof* premise : m.get_parents(step)) { if (m_ctx.is_b(premise)) { todo_subproof.push_back(premise); } } while (!todo_subproof.empty()) { proof* current = todo_subproof.back(); todo_subproof.pop_back(); // if we need to deal with the node if (!m_ctx.is_closed(current)) { SASSERT(!m_ctx.is_a(current)); // by I.H. the step must be already visited // and the current step needs to be interpolated: if (m_ctx.is_b(current)) { // if we trust the current step and we are able to use it if (m_ctx.is_b_pure (current) && (m.is_asserted(current) || is_literal(m, m.get_fact(current)))) { // we found a leaf of the subproof, so // 1) we add corresponding edges if (m_ctx.is_a(step)) { add_edge(nullptr, current); // current is sink } else { add_edge(step, current); // standard edge } // 2) add the leaf to todo todo.push_back(current); is_sink = false; } // otherwise continue search for leaves of subproof else { for (proof* premise : m.get_parents(current)) { todo_subproof.push_back(premise); } } } } } if (is_sink) { add_edge(step, nullptr); } } /* * adds an edge from the out-vertex of i to the in-vertex of j to the graph * if i or j isn't already present, it adds the corresponding in- and out-vertices to the graph * if i is a nullptr, it is treated as source vertex * if j is a nullptr, it is treated as sink vertex */ void unsat_core_plugin_min_cut::add_edge(proof* i, proof* j) { SASSERT(i != nullptr || j != nullptr); unsigned node_i; unsigned node_j; if (i == nullptr) { node_i = 0; } else { unsigned tmp; if (m_proof_to_node_plus.find(i, tmp)) { node_i = tmp; } else { unsigned node_other = m_min_cut.new_node(); node_i = m_min_cut.new_node(); m_proof_to_node_minus.insert(i, node_other); m_proof_to_node_plus.insert(i, node_i); if (node_i >= m_node_to_formula.size()) { m_node_to_formula.resize(node_i + 1); } m_node_to_formula[node_other] = m.get_fact(i); m_node_to_formula[node_i] = m.get_fact(i); m_min_cut.add_edge(node_other, node_i, 1); } } if (j == nullptr) { node_j = 1; } else { unsigned tmp; if (m_proof_to_node_minus.find(j, tmp)) { node_j = tmp; } else { node_j = m_min_cut.new_node(); unsigned node_other = m_min_cut.new_node(); m_proof_to_node_minus.insert(j, node_j); m_proof_to_node_plus.insert(j, node_other); if (node_other >= m_node_to_formula.size()) { m_node_to_formula.resize(node_other + 1); } m_node_to_formula[node_j] = m.get_fact(j); m_node_to_formula[node_other] = m.get_fact(j); m_min_cut.add_edge(node_j, node_other, 1); } } // finally connect nodes (if there is not already a connection i -> j (only relevant if i is the supersource)) if (!(i == nullptr && m_connected_to_s.is_marked(j))) { m_min_cut.add_edge(node_i, node_j, 1); } if (i == nullptr) { m_connected_to_s.mark(j, true); } } /* * computes min-cut on the graph constructed by compute_partial_core-method * and adds the corresponding lemmas to the core */ void unsat_core_plugin_min_cut::finalize() { unsigned_vector cut_nodes; m_min_cut.compute_min_cut(cut_nodes); for (unsigned cut_node : cut_nodes) { m_ctx.add_lemma_to_core(m_node_to_formula[cut_node]); } } } z3-z3-4.8.7/src/muz/spacer/spacer_unsat_core_plugin.h000066400000000000000000000074001356505360400225350ustar00rootroot00000000000000/*++ Copyright (c) 2017 Arie Gurfinkel Module Name: spacer_unsat_core_plugin.h Abstract: plugin for itp cores Author: Bernhard Gleiss Revision History: --*/ #ifndef _SPACER_UNSAT_CORE_PLUGIN_H_ #define _SPACER_UNSAT_CORE_PLUGIN_H_ #include "ast/ast.h" #include "util/min_cut.h" namespace spacer { class unsat_core_learner; class unsat_core_plugin { protected: typedef vector> coeff_lits_t; ast_manager& m; public: unsat_core_plugin(unsat_core_learner& learner); virtual ~unsat_core_plugin() {}; virtual void compute_partial_core(proof* step) = 0; virtual void finalize(){}; unsat_core_learner& m_ctx; }; class unsat_core_plugin_lemma : public unsat_core_plugin { public: unsat_core_plugin_lemma(unsat_core_learner& learner) : unsat_core_plugin(learner){}; void compute_partial_core(proof* step) override; private: void add_lowest_split_to_core(proof* step) const; }; class unsat_core_plugin_farkas_lemma : public unsat_core_plugin { public: unsat_core_plugin_farkas_lemma(unsat_core_learner& learner, bool split_literals, bool use_constant_from_a=true) : unsat_core_plugin(learner), m_split_literals(split_literals), m_use_constant_from_a(use_constant_from_a) {}; void compute_partial_core(proof* step) override; private: bool m_split_literals; bool m_use_constant_from_a; /* * compute linear combination of literals 'literals' having coefficients 'coefficients' and save result in res */ expr_ref compute_linear_combination(const coeff_lits_t& coeff_lits); }; class unsat_core_plugin_farkas_lemma_optimized : public unsat_core_plugin { public: unsat_core_plugin_farkas_lemma_optimized(unsat_core_learner& learner, ast_manager& m) : unsat_core_plugin(learner) {}; void compute_partial_core(proof* step) override; void finalize() override; protected: vector m_linear_combinations; /* * compute linear combination of literals 'literals' having coefficients 'coefficients' and save result in res */ expr_ref compute_linear_combination(const coeff_lits_t& coeff_lits); }; class unsat_core_plugin_farkas_lemma_bounded : public unsat_core_plugin_farkas_lemma_optimized { public: unsat_core_plugin_farkas_lemma_bounded(unsat_core_learner& learner, ast_manager& m) : unsat_core_plugin_farkas_lemma_optimized(learner, m) {}; void finalize() override; }; class unsat_core_plugin_min_cut : public unsat_core_plugin { public: unsat_core_plugin_min_cut(unsat_core_learner& learner, ast_manager& m); void compute_partial_core(proof* step) override; void finalize() override; private: ast_mark m_visited; // saves for each node i whether the subproof with root i has already been added to the min-cut-problem obj_map m_proof_to_node_minus; // maps proof-steps to the corresponding minus-nodes (the ones which are closer to source) obj_map m_proof_to_node_plus; // maps proof-steps to the corresponding plus-nodes (the ones which are closer to sink) void advance_to_lowest_partial_cut(proof* step, ptr_vector& todo); void add_edge(proof* i, proof* j); vector m_node_to_formula; // maps each node to the corresponding formula in the original proof ast_mark m_connected_to_s; // remember which nodes have already been connected to the supersource, in order to avoid multiple edges. min_cut m_min_cut; }; } #endif z3-z3-4.8.7/src/muz/spacer/spacer_util.cpp000066400000000000000000000767151356505360400203440ustar00rootroot00000000000000/** Copyright (c) 2011 Microsoft Corporation Module Name: spacer_util.cpp Abstract: Utility functions for SPACER. Author: Krystof Hoder (t-khoder) 2011-8-19. Arie Gurfinkel Anvesh Komuravelli Revision History: Modified by Anvesh Komuravelli Notes: --*/ #include #include #include "ast/ast.h" #include "ast/occurs.h" #include "ast/ast_pp.h" #include "ast/rewriter/bool_rewriter.h" #include "muz/base/dl_util.h" #include "ast/for_each_expr.h" #include "smt/params/smt_params.h" #include "model/model.h" #include "model/model_evaluator.h" #include "util/ref_vector.h" #include "ast/rewriter/rewriter.h" #include "ast/rewriter/rewriter_def.h" #include "util/util.h" #include "muz/spacer/spacer_manager.h" #include "muz/spacer/spacer_util.h" #include "ast/rewriter/expr_replacer.h" #include "model/model_smt2_pp.h" #include "ast/scoped_proof.h" #include "qe/qe_lite.h" #include "muz/spacer/spacer_qe_project.h" #include "model/model_pp.h" #include "ast/rewriter/expr_safe_replace.h" #include "ast/array_decl_plugin.h" #include "ast/arith_decl_plugin.h" #include "ast/datatype_decl_plugin.h" #include "ast/bv_decl_plugin.h" #include "muz/spacer/spacer_legacy_mev.h" #include "qe/qe_mbp.h" #include "tactic/tactical.h" #include "tactic/core/propagate_values_tactic.h" #include "tactic/arith/propagate_ineqs_tactic.h" #include "tactic/arith/arith_bounds_tactic.h" #include "ast/rewriter/factor_equivs.h" #include "qe/qe_term_graph.h" namespace spacer { void subst_vars(ast_manager& m, app_ref_vector const& vars, model& mdl, expr_ref& fml) { model::scoped_model_completion _sc_(mdl, true); expr_safe_replace sub(m); for (app * v : vars) sub.insert (v, mdl(v)); sub(fml); } void to_mbp_benchmark(std::ostream &out, expr* fml, const app_ref_vector &vars) { ast_manager &m = vars.m(); ast_pp_util pp(m); pp.collect(fml); pp.display_decls(out); out << "(define-fun mbp_benchmark_fml () Bool\n "; out << mk_pp(fml, m) << ")\n\n"; out << "(push)\n" << "(assert mbp_benchmark_fml)\n" << "(check-sat)\n" << "(mbp mbp_benchmark_fml ("; for (auto v : vars) {out << mk_pp(v, m) << " ";} out << "))\n" << "(pop)\n" << "(exit)\n"; } void qe_project_z3 (ast_manager& m, app_ref_vector& vars, expr_ref& fml, model & mdl, bool reduce_all_selects, bool use_native_mbp, bool dont_sub) { params_ref p; p.set_bool("reduce_all_selects", reduce_all_selects); p.set_bool("dont_sub", dont_sub); qe::mbp mbp(m, p); mbp.spacer(vars, mdl, fml); } /* * eliminate simple equalities using qe_lite * then, MBP for Booleans (substitute), reals (based on LW), ints (based on Cooper), and arrays */ void qe_project_spacer (ast_manager& m, app_ref_vector& vars, expr_ref& fml, model& mdl, bool reduce_all_selects, bool use_native_mbp, bool dont_sub) { th_rewriter rw (m); TRACE ("spacer_mbp", tout << "Before projection:\n"; tout << fml << "\n"; tout << "Vars:\n" << vars;); { // Ensure that top-level AND of fml is flat expr_ref_vector flat(m); flatten_and (fml, flat); fml = mk_and(flat); } // uncomment for benchmarks //to_mbp_benchmark(verbose_stream(), fml, vars); app_ref_vector arith_vars (m); app_ref_vector array_vars (m); array_util arr_u (m); arith_util ari_u (m); expr_safe_replace bool_sub (m); expr_ref bval (m); while (true) { params_ref p; qe_lite qe(m, p, false); qe (vars, fml); rw (fml); TRACE ("spacer_mbp", tout << "After qe_lite:\n"; tout << mk_pp (fml, m) << "\n"; tout << "Vars:\n" << vars;); SASSERT (!m.is_false (fml)); // sort out vars into bools, arith (int/real), and arrays for (app* v : vars) { if (m.is_bool (v)) { // obtain the interpretation of the ith var // using model completion model::scoped_model_completion _sc_(mdl, true); bool_sub.insert (v, mdl(v)); } else if (arr_u.is_array(v)) { array_vars.push_back(v); } else { SASSERT (ari_u.is_int(v) || ari_u.is_real(v)); arith_vars.push_back(v); } } // substitute Booleans if (!bool_sub.empty()) { bool_sub(fml); // -- bool_sub is not simplifying rw (fml); SASSERT(!m.is_false (fml)); TRACE("spacer_mbp", tout << "Projected Booleans:\n" << fml << "\n"; ); bool_sub.reset(); } TRACE ("spacer_mbp", tout << "Array vars:\n"; tout << array_vars;); vars.reset (); // project arrays { scoped_no_proof _sp (m); // -- local rewriter that is aware of current proof mode th_rewriter srw(m); spacer_qe::array_project (mdl, array_vars, fml, vars, reduce_all_selects); SASSERT (array_vars.empty ()); srw (fml); SASSERT (!m.is_false (fml)); } TRACE ("spacer_mbp", tout << "extended model:\n"; model_pp (tout, mdl); tout << "Auxiliary variables of index and value sorts:\n"; tout << vars;); if (vars.empty()) { break; } } // project reals and ints if (!arith_vars.empty ()) { TRACE ("spacer_mbp", tout << "Arith vars:\n" << arith_vars;); if (use_native_mbp) { qe::mbp mbp (m); expr_ref_vector fmls(m); flatten_and (fml, fmls); mbp (true, arith_vars, mdl, fmls); fml = mk_and(fmls); SASSERT(arith_vars.empty ()); } else { scoped_no_proof _sp (m); spacer_qe::arith_project (mdl, arith_vars, fml); } TRACE ("spacer_mbp", tout << "Projected arith vars:\n" << fml << "\n"; tout << "Remaining arith vars:\n" << arith_vars << "\n";); SASSERT (!m.is_false (fml)); } if (!arith_vars.empty ()) { mbqi_project (mdl, arith_vars, fml); } // substitute any remaining arith vars if (!dont_sub && !arith_vars.empty ()) { subst_vars (m, arith_vars, mdl, fml); TRACE ("spacer_mbp", tout << "After substituting remaining arith vars:\n"; tout << mk_pp (fml, m) << "\n"; ); // an extra round of simplification because subst_vars is not simplifying rw(fml); } DEBUG_CODE ( model_evaluator mev(mdl); mev.set_model_completion(false); SASSERT(mev.is_true(fml)); ); vars.reset (); if (dont_sub && !arith_vars.empty ()) { vars.append(arith_vars); } } static expr* apply_accessor(ast_manager &m, ptr_vector const& acc, unsigned j, func_decl* f, expr* c) { if (is_app(c) && to_app(c)->get_decl() == f) { return to_app(c)->get_arg(j); } else { return m.mk_app(acc[j], c); } } void qe_project (ast_manager& m, app_ref_vector& vars, expr_ref& fml, model &mdl, bool reduce_all_selects, bool use_native_mbp, bool dont_sub) { if (use_native_mbp) qe_project_z3(m, vars, fml, mdl, reduce_all_selects, use_native_mbp, dont_sub); else qe_project_spacer(m, vars, fml, mdl, reduce_all_selects, use_native_mbp, dont_sub); } void expand_literals(ast_manager &m, expr_ref_vector& conjs) { if (conjs.empty()) { return; } arith_util arith(m); datatype_util dt(m); bv_util bv(m); expr* e1, *e2, *c, *val; rational r; unsigned bv_size; TRACE("spacer_expand", tout << "begin expand\n" << conjs << "\n";); for (unsigned i = 0; i < conjs.size(); ++i) { expr* e = conjs[i].get(); if (m.is_eq(e, e1, e2) && arith.is_int_real(e1)) { conjs[i] = arith.mk_le(e1,e2); if (i+1 == conjs.size()) { conjs.push_back(arith.mk_ge(e1, e2)); } else { conjs.push_back(conjs[i+1].get()); conjs[i+1] = arith.mk_ge(e1, e2); } ++i; } else if ((m.is_eq(e, c, val) && is_app(val) && dt.is_constructor(to_app(val))) || (m.is_eq(e, val, c) && is_app(val) && dt.is_constructor(to_app(val)))) { func_decl* f = to_app(val)->get_decl(); func_decl* r = dt.get_constructor_is(f); conjs[i] = m.mk_app(r, c); ptr_vector const& acc = *dt.get_constructor_accessors(f); for (unsigned j = 0; j < acc.size(); ++j) { conjs.push_back(m.mk_eq(apply_accessor(m, acc, j, f, c), to_app(val)->get_arg(j))); } } else if ((m.is_eq(e, c, val) && bv.is_numeral(val, r, bv_size)) || (m.is_eq(e, val, c) && bv.is_numeral(val, r, bv_size))) { rational two(2); for (unsigned j = 0; j < bv_size; ++j) { parameter p(j); expr* e = m.mk_eq(m.mk_app(bv.get_family_id(), OP_BIT1), bv.mk_extract(j, j, c)); if ((r % two).is_zero()) { e = m.mk_not(e); } r = div(r, two); if (j == 0) { conjs[i] = e; } else { conjs.push_back(e); } } } } TRACE("spacer_expand", tout << "end expand\n" << conjs << "\n";); } namespace { class implicant_picker { model &m_model; ast_manager &m; arith_util m_arith; expr_ref_vector m_todo; expr_mark m_visited; // add literal to the implicant // applies lightweight normalization void add_literal(expr *e, expr_ref_vector &out) { SASSERT(m.is_bool(e)); expr_ref res(m), v(m); v = m_model(e); // the literal must have a value SASSERT(m.is_true(v) || m.is_false(v)); res = m.is_false(v) ? m.mk_not(e) : e; if (m.is_distinct(res)) { // --(distinct a b) == (not (= a b)) if (to_app(res)->get_num_args() == 2) { res = m.mk_eq(to_app(res)->get_arg(0), to_app(res)->get_arg(1)); res = m.mk_not(res); } } expr *nres = nullptr, *f1 = nullptr, *f2 = nullptr; if (m.is_not(res, nres)) { // --(not (xor a b)) == (= a b) if (m.is_xor(nres, f1, f2)) res = m.mk_eq(f1, f2); // -- split arithmetic inequality else if (m.is_eq(nres, f1, f2) && m_arith.is_int_real(f1)) { res = m_arith.mk_lt(f1, f2); if (!m_model.is_true(res)) res = m_arith.mk_lt(f2, f1); } } if (!m_model.is_true(res)) { verbose_stream() << "Bad literal: " << res << "\n"; } SASSERT(m_model.is_true(res)); out.push_back(res); } void process_app(app *a, expr_ref_vector &out) { if (m_visited.is_marked(a)) return; SASSERT(m.is_bool(a)); expr_ref v(m); v = m_model(a); bool is_true = m.is_true(v); if (!is_true && !m.is_false(v)) return; expr *na = nullptr, *f1 = nullptr, *f2 = nullptr, *f3 = nullptr; SASSERT(!m.is_false(a)); if (m.is_true(a)) { // noop } else if (a->get_family_id() != m.get_basic_family_id()) { add_literal(a, out); } else if (is_uninterp_const(a)) { add_literal(a, out); } else if (m.is_not(a, na)) { m_todo.push_back(na); } else if (m.is_distinct(a)) { if (!is_true) { f1 = qe::project_plugin::pick_equality(m, m_model, a); m_todo.push_back(f1); } else if (a->get_num_args() == 2) { add_literal(a, out); } else { m_todo.push_back(m.mk_distinct_expanded(a->get_num_args(), a->get_args())); } } else if (m.is_and(a)) { if (is_true) { m_todo.append(a->get_num_args(), a->get_args()); } else { for (expr* e : *a) { if (m_model.is_false(e)) { m_todo.push_back(e); break; } } } } else if (m.is_or(a)) { if (!is_true) m_todo.append(a->get_num_args(), a->get_args()); else { for (expr * e : *a) { if (m_model.is_true(e)) { m_todo.push_back(e); break; } } } } else if (m.is_eq(a, f1, f2) || (is_true && m.is_not(a, na) && m.is_xor(na, f1, f2))) { if (!m.are_equal(f1, f2) && !m.are_distinct(f1, f2)) { if (m.is_bool(f1) && (!is_uninterp_const(f1) || !is_uninterp_const(f2))) m_todo.append(a->get_num_args(), a->get_args()); else add_literal(a, out); } } else if (m.is_ite(a, f1, f2, f3)) { if (m.are_equal(f2, f3)) { m_todo.push_back(f2); } else if (m_model.is_true(f2) && m_model.is_true(f3)) { m_todo.push_back(f2); m_todo.push_back(f3); } else if (m_model.is_false(f2) && m_model.is_false(f3)) { m_todo.push_back(f2); m_todo.push_back(f3); } else if (m_model.is_true(f1)) { m_todo.push_back(f1); m_todo.push_back(f2); } else if (m_model.is_false(f1)) { m_todo.push_back(f1); m_todo.push_back(f3); } } else if (m.is_xor(a, f1, f2)) { m_todo.append(a->get_num_args(), a->get_args()); } else if (m.is_implies(a, f1, f2)) { if (is_true) { if (m_model.is_true(f2)) m_todo.push_back(f2); else if (m_model.is_false(f1)) m_todo.push_back(f1); } else m_todo.append(a->get_num_args(), a->get_args()); } else { IF_VERBOSE(0, verbose_stream() << "Unexpected expression: " << mk_pp(a, m) << "\n"); UNREACHABLE(); } } void pick_literals(expr *e, expr_ref_vector &out) { SASSERT(m_todo.empty()); if (m_visited.is_marked(e) || !is_app(e)) return; m_todo.push_back(e); do { e = m_todo.back(); if (!is_app(e)) continue; app * a = to_app(e); m_todo.pop_back(); process_app(a, out); m_visited.mark(a, true); } while (!m_todo.empty()); } bool pick_implicant(const expr_ref_vector &in, expr_ref_vector &out) { m_visited.reset(); bool is_true = m_model.is_true(in); for (expr* e : in) { if (is_true || m_model.is_true(e)) { pick_literals(e, out); } } m_visited.reset(); return is_true; } public: implicant_picker(model &mdl) : m_model(mdl), m(m_model.get_manager()), m_arith(m), m_todo(m) {} void operator()(expr_ref_vector &in, expr_ref_vector& out) { model::scoped_model_completion _sc_(m_model, false); pick_implicant(in, out); } }; } void compute_implicant_literals(model &mdl, expr_ref_vector &formula, expr_ref_vector &res) { // flatten the formula and remove all trivial literals // TBD: not clear why there is a dependence on it(other than // not handling of Boolean constants by implicant_picker), however, // it was a source of a problem on a benchmark flatten_and(formula); if (formula.empty()) {return;} implicant_picker ipick(mdl); ipick(formula, res); } void simplify_bounds_old(expr_ref_vector& cube) { ast_manager& m = cube.m(); scoped_no_proof _no_pf_(m); goal_ref g(alloc(goal, m, false, false, false)); for (expr* c : cube) g->assert_expr(c); goal_ref_buffer result; tactic_ref simplifier = mk_arith_bounds_tactic(m); (*simplifier)(g, result); SASSERT(result.size() == 1); goal* r = result[0]; cube.reset(); for (unsigned i = 0; i < r->size(); ++i) { cube.push_back(r->form(i)); } } void simplify_bounds_new(expr_ref_vector &cube) { ast_manager &m = cube.m(); scoped_no_proof _no_pf_(m); goal_ref g(alloc(goal, m, false, false, false)); for (expr* c : cube) g->assert_expr(c); goal_ref_buffer goals; tactic_ref prop_values = mk_propagate_values_tactic(m); tactic_ref prop_bounds = mk_propagate_ineqs_tactic(m); tactic_ref t = and_then(prop_values.get(), prop_bounds.get()); (*t)(g, goals); SASSERT(goals.size() == 1); g = goals[0]; cube.reset(); for (unsigned i = 0; i < g->size(); ++i) { cube.push_back(g->form(i)); } } void simplify_bounds(expr_ref_vector &cube) { simplify_bounds_new(cube); } /// Adhoc rewriting of arithmetic expressions struct adhoc_rewriter_cfg : public default_rewriter_cfg { ast_manager &m; arith_util m_util; adhoc_rewriter_cfg(ast_manager &manager) : m(manager), m_util(m) {} bool is_le(func_decl const * n) const { return m_util.is_le(n); } bool is_ge(func_decl const * n) const { return m_util.is_ge(n); } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { expr * e; if (is_le(f)) return mk_le_core(args[0], args[1], result); if (is_ge(f)) return mk_ge_core(args[0], args[1], result); if (m.is_not(f) && m.is_not(args[0], e)) { result = e; return BR_DONE; } return BR_FAILED; } br_status mk_le_core(expr *arg1, expr * arg2, expr_ref & result) { // t <= -1 ==> t < 0 ==> !(t >= 0) if (m_util.is_int(arg1) && m_util.is_minus_one(arg2)) { result = m.mk_not(m_util.mk_ge(arg1, mk_zero())); return BR_DONE; } return BR_FAILED; } br_status mk_ge_core(expr * arg1, expr * arg2, expr_ref & result) { // t >= 1 ==> t > 0 ==> !(t <= 0) if (m_util.is_int(arg1) && is_one(arg2)) { result = m.mk_not(m_util.mk_le(arg1, mk_zero())); return BR_DONE; } return BR_FAILED; } expr * mk_zero() {return m_util.mk_numeral(rational(0), true);} bool is_one(expr const * n) const { rational val; return m_util.is_numeral(n, val) && val.is_one(); } }; void normalize(expr *e, expr_ref &out, bool use_simplify_bounds, bool use_factor_eqs) { params_ref params; // arith_rewriter params.set_bool("sort_sums", true); params.set_bool("gcd_rounding", true); params.set_bool("arith_lhs", true); // poly_rewriter params.set_bool("som", true); params.set_bool("flat", true); // apply rewriter th_rewriter rw(out.m(), params); rw(e, out); adhoc_rewriter_cfg adhoc_cfg(out.m()); rewriter_tpl adhoc_rw(out.m(), false, adhoc_cfg); adhoc_rw(out.get(), out); if (out.m().is_and(out)) { expr_ref_vector v(out.m()); flatten_and(out, v); if (v.size() > 1) { if (use_simplify_bounds) { // remove redundant inequalities simplify_bounds(v); } if (use_factor_eqs) { // -- refactor equivalence classes and choose a representative qe::term_graph egraph(out.m()); egraph.add_lits(v); v.reset(); egraph.to_lits(v); } // sort arguments of the top-level and std::stable_sort(v.c_ptr(), v.c_ptr() + v.size(), ast_lt_proc()); TRACE("spacer_normalize", tout << "Normalized:\n" << out << "\n" << "to\n" << mk_and(v) << "\n";); TRACE("spacer_normalize", qe::term_graph egraph(out.m()); for (expr* e : v) egraph.add_lit(to_app(e)); tout << "Reduced app:\n" << mk_pp(egraph.to_expr(), out.m()) << "\n";); out = mk_and(v); } } } // rewrite term such that the pretty printing is easier to read struct adhoc_rewriter_rpp : public default_rewriter_cfg { ast_manager &m; arith_util m_arith; adhoc_rewriter_rpp(ast_manager &manager) : m(manager), m_arith(m) {} bool is_le(func_decl const * n) const { return m_arith.is_le(n); } bool is_ge(func_decl const * n) const { return m_arith.is_ge(n); } bool is_lt(func_decl const * n) const { return m_arith.is_lt(n); } bool is_gt(func_decl const * n) const { return m_arith.is_gt(n); } bool is_zero(expr const * n) const {rational val; return m_arith.is_numeral(n, val) && val.is_zero();} br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { br_status st = BR_FAILED; expr *e1, *e2, *e3, *e4; // rewrites(=(+ A(* -1 B)) 0) into(= A B) if (m.is_eq(f) && is_zero(args [1]) && m_arith.is_add(args[0], e1, e2) && m_arith.is_mul(e2, e3, e4) && m_arith.is_minus_one(e3)) { result = m.mk_eq(e1, e4); return BR_DONE; } // simplify normalized leq, where right side is different from 0 // rewrites(<=(+ A(* -1 B)) C) into(<= A B+C) else if ((is_le(f) || is_lt(f) || is_ge(f) || is_gt(f)) && m_arith.is_add(args[0], e1, e2) && m_arith.is_mul(e2, e3, e4) && m_arith.is_minus_one(e3)) { expr_ref rhs(m); rhs = is_zero(args[1]) ? e4 : m_arith.mk_add(e4, args[1]); if (is_le(f)) { result = m_arith.mk_le(e1, rhs); st = BR_DONE; } else if (is_lt(f)) { result = m_arith.mk_lt(e1, rhs); st = BR_DONE; } else if (is_ge(f)) { result = m_arith.mk_ge(e1, rhs); st = BR_DONE; } else if (is_gt(f)) { result = m_arith.mk_gt(e1, rhs); st = BR_DONE; } else { UNREACHABLE(); } } // simplify negation of ordering predicate else if (m.is_not(f)) { if (m_arith.is_lt(args[0], e1, e2)) { result = m_arith.mk_ge(e1, e2); st = BR_DONE; } else if (m_arith.is_le(args[0], e1, e2)) { result = m_arith.mk_gt(e1, e2); st = BR_DONE; } else if (m_arith.is_gt(args[0], e1, e2)) { result = m_arith.mk_le(e1, e2); st = BR_DONE; } else if (m_arith.is_ge(args[0], e1, e2)) { result = m_arith.mk_lt(e1, e2); st = BR_DONE; } } return st; } }; mk_epp::mk_epp(ast *t, ast_manager &m, unsigned indent, unsigned num_vars, char const * var_prefix) : mk_pp(t, m, m_epp_params, indent, num_vars, var_prefix), m_epp_expr(m) { m_epp_params.set_uint("min_alias_size", UINT_MAX); m_epp_params.set_uint("max_depth", UINT_MAX); if (is_expr(m_ast)) { rw(to_expr(m_ast), m_epp_expr); m_ast = m_epp_expr; } } void mk_epp::rw(expr *e, expr_ref &out) { adhoc_rewriter_rpp cfg(out.m()); rewriter_tpl arw(out.m(), false, cfg); arw(e, out); } void ground_expr(expr *e, expr_ref &out, app_ref_vector &vars) { expr_free_vars fv; ast_manager &m = out.m(); fv(e); if (vars.size() < fv.size()) { vars.resize(fv.size()); } for (unsigned i = 0, sz = fv.size(); i < sz; ++i) { sort *s = fv[i] ? fv[i] : m.mk_bool_sort(); vars[i] = mk_zk_const(m, i, s); var_subst vs(m, false); out = vs(e, vars.size(),(expr * *) vars.c_ptr()); } } struct index_term_finder { ast_manager &m; array_util m_array; app_ref m_var; expr_ref_vector &m_res; index_term_finder(ast_manager &mgr, app* v, expr_ref_vector &res) : m(mgr), m_array(m), m_var(v, m), m_res(res) {} void operator()(var *n) {} void operator()(quantifier *n) {} void operator()(app *n) { if (m_array.is_select(n) || m.is_eq(n)) { unsigned i = 0; for (expr * arg : *n) { if ((m.is_eq(n) || i > 0) && m_var != arg) m_res.push_back(arg); ++i; } } } }; bool mbqi_project_var(model &mdl, app* var, expr_ref &fml) { ast_manager &m = fml.get_manager(); model::scoped_model_completion _sc_(mdl, false); expr_ref val(m); val = mdl(var); TRACE("mbqi_project_verbose", tout << "MBQI: var: " << mk_pp(var, m) << "\n" << "fml: " << fml << "\n";); expr_ref_vector terms(m); index_term_finder finder(m, var, terms); for_each_expr(finder, fml); TRACE("mbqi_project_verbose", tout << "terms:\n" << terms << "\n";); for (expr * term : terms) { expr_ref tval(m); tval = mdl(term); TRACE("mbqi_project_verbose", tout << "term: " << mk_pp(term, m) << " tval: " << tval << " val: " << val << "\n";); // -- if the term does not contain an occurrence of var // -- and is in the same equivalence class in the model if (tval == val && !occurs(var, term)) { TRACE("mbqi_project", tout << "MBQI: replacing " << mk_pp(var, m) << " with " << mk_pp(term, m) << "\n";); expr_safe_replace sub(m); sub.insert(var, term); sub(fml); return true; } } TRACE("mbqi_project", tout << "MBQI: failed to eliminate " << mk_pp(var, m) << " from " << fml << "\n";); return false; } void mbqi_project(model &mdl, app_ref_vector &vars, expr_ref &fml) { ast_manager &m = fml.get_manager(); expr_ref tmp(m); model::scoped_model_completion _sc_(mdl, false); // -- evaluate to initialize mev cache tmp = mdl(fml); tmp.reset(); unsigned j = 0; for (app* v : vars) if (!mbqi_project_var(mdl, v, fml)) vars[j++] = v; vars.shrink(j); } struct found {}; struct check_select { array_util a; check_select(ast_manager& m): a(m) {} void operator()(expr* n) {} void operator()(app* n) { if (a.is_select(n)) throw found(); } }; bool contains_selects(expr* fml, ast_manager& m) { check_select cs(m); try { for_each_expr(cs, fml); return false; } catch(const found &) { return true; } } struct collect_indices { app_ref_vector& m_indices; array_util a; collect_indices(app_ref_vector& indices): m_indices(indices), a(indices.get_manager()) {} void operator()(expr* n) {} void operator()(app* n) { if (a.is_select(n)) { // for all but first argument for (unsigned i = 1; i < n->get_num_args(); ++i) { expr *arg = n->get_arg(i); if (is_app(arg)) m_indices.push_back(to_app(arg)); } } } }; void get_select_indices(expr* fml, app_ref_vector &indices) { collect_indices ci(indices); for_each_expr(ci, fml); } struct collect_decls { app_ref_vector& m_decls; std::string& prefix; collect_decls(app_ref_vector& decls, std::string& p): m_decls(decls), prefix(p) {} void operator()(expr* n) {} void operator()(app* n) { if (n->get_decl()->get_name().str().find(prefix) != std::string::npos) m_decls.push_back(n); } }; void find_decls(expr* fml, app_ref_vector& decls, std::string& prefix) { collect_decls cd(decls, prefix); for_each_expr(cd, fml); } } template class rewriter_tpl; template class rewriter_tpl; z3-z3-4.8.7/src/muz/spacer/spacer_util.h000066400000000000000000000075011356505360400177740ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: spacer_util.h Abstract: Utility functions for SPACER. Author: Krystof Hoder (t-khoder) 2011-8-19. Arie Gurfinkel Anvesh Komuravelli Revision History: --*/ #ifndef _SPACER_UTIL_H_ #define _SPACER_UTIL_H_ #include "ast/ast.h" #include "ast/ast_pp.h" #include "util/obj_hashtable.h" #include "util/ref_vector.h" #include "util/trace.h" #include "util/vector.h" #include "ast/arith_decl_plugin.h" #include "ast/array_decl_plugin.h" #include "ast/bv_decl_plugin.h" #include "ast/ast_util.h" #include "ast/expr_map.h" #include "model/model.h" #include "util/stopwatch.h" #include "muz/spacer/spacer_antiunify.h" class model; class model_core; namespace spacer { inline unsigned infty_level () { return UINT_MAX; } inline bool is_infty_level(unsigned lvl) { // XXX: level is 16 bits in class pob return lvl >= 65535; } inline unsigned next_level(unsigned lvl) { return is_infty_level(lvl)?lvl:(lvl+1); } inline unsigned prev_level (unsigned lvl) { if (is_infty_level(lvl)) return infty_level(); if (lvl == 0) return 0; return lvl - 1; } struct pp_level { unsigned m_level; pp_level(unsigned l): m_level(l) {} }; inline std::ostream& operator<<(std::ostream& out, pp_level const& p) { if (is_infty_level(p.m_level)) { return out << "oo"; } else { return out << p.m_level; } } typedef ptr_vector app_vector; typedef ptr_vector decl_vector; typedef obj_hashtable func_decl_set; /** \brief hoist non-boolean if expressions. */ void to_mbp_benchmark(std::ostream &out, const expr* fml, const app_ref_vector &vars); // TBD: deprecate by qe::mbp /** * do the following in sequence * 1. use qe_lite to cheaply eliminate vars * 2. for remaining boolean vars, substitute using M * 3. use MBP for remaining array and arith variables * 4. for any remaining arith variables, substitute using M */ void qe_project (ast_manager& m, app_ref_vector& vars, expr_ref& fml, model &mdl, bool reduce_all_selects=false, bool native_mbp=false, bool dont_sub=false); // deprecate void qe_project (ast_manager& m, app_ref_vector& vars, expr_ref& fml, model_ref& M, expr_map& map); // TBD: sort out void expand_literals(ast_manager &m, expr_ref_vector& conjs); void compute_implicant_literals(model &mdl, expr_ref_vector &formula, expr_ref_vector &res); void simplify_bounds (expr_ref_vector &lemmas); void normalize(expr *e, expr_ref &out, bool use_simplify_bounds = true, bool factor_eqs = false); /** * Ground expression by replacing all free variables by skolem * constants. On return, out is the resulting expression, and vars is * a map from variable ids to corresponding skolem constants. */ void ground_expr (expr *e, expr_ref &out, app_ref_vector &vars); void mbqi_project(model &mdl, app_ref_vector &vars, expr_ref &fml); bool contains_selects (expr* fml, ast_manager& m); void get_select_indices (expr* fml, app_ref_vector& indices); void find_decls (expr* fml, app_ref_vector& decls, std::string& prefix); /** * extended pretty-printer * used for debugging * disables aliasing of common sub-expressions */ struct mk_epp : public mk_pp { params_ref m_epp_params; expr_ref m_epp_expr; mk_epp(ast *t, ast_manager &m, unsigned indent = 0, unsigned num_vars = 0, char const * var_prefix = nullptr); void rw(expr *e, expr_ref &out); }; } #endif z3-z3-4.8.7/src/muz/tab/000077500000000000000000000000001356505360400145775ustar00rootroot00000000000000z3-z3-4.8.7/src/muz/tab/CMakeLists.txt000066400000000000000000000001451356505360400173370ustar00rootroot00000000000000z3_add_component(tab SOURCES tab_context.cpp COMPONENT_DEPENDENCIES muz transforms ) z3-z3-4.8.7/src/muz/tab/tab_context.cpp000066400000000000000000001616471356505360400176340ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: tab_context.cpp Abstract: Tabulation/subsumption/cyclic proof context. Author: Nikolaj Bjorner (nbjorner) 2013-01-15 Revision History: --*/ #include "muz/tab/tab_context.h" #include "util/trail.h" #include "muz/base/dl_rule_set.h" #include "muz/base/dl_context.h" #include "muz/transforms/dl_mk_rule_inliner.h" #include "smt/smt_kernel.h" #include "qe/qe_lite.h" #include "ast/rewriter/bool_rewriter.h" #include "ast/rewriter/th_rewriter.h" #include "ast/datatype_decl_plugin.h" #include "ast/for_each_expr.h" #include "ast/substitution/matcher.h" #include "ast/scoped_proof.h" #include "muz/base/fp_params.hpp" #include "ast/ast_util.h" namespace tb { // semantic matcher. class matcher { typedef std::pair expr_pair; ast_manager& m; svector m_todo; datatype_util m_dt; lbool is_eq(expr* _s, expr* _t) { if (_s == _t) { return l_true; } if (!is_app(_s) || !is_app(_t)) { return l_undef; } app* s = to_app(_s); app* t = to_app(_t); if (m.is_value(s) && m.is_value(t)) { IF_VERBOSE(2, verbose_stream() << "different:" << mk_pp(s, m) << " " << mk_pp(t, m) << "\n";); return l_false; } if (m_dt.is_constructor(s) && m_dt.is_constructor(t)) { if (s->get_decl() == t->get_decl()) { lbool state = l_true; for (unsigned i = 0; i < s->get_num_args(); ++i) { // move is_eq: decompose arguments to constraints. switch (is_eq(s->get_arg(i), t->get_arg(i))) { case l_undef: state = l_undef; break; case l_false: return l_false; default: break; } } return state; } else { IF_VERBOSE(2, verbose_stream() << "different constructors:" << mk_pp(s, m) << " " << mk_pp(t, m) << "\n";); return l_false; } } return l_undef; } bool match_var(var* v, app* t, substitution& s, expr_ref_vector& conds) { expr_offset r; if (s.find(v, 0, r)) { app* p = to_app(r.get_expr()); switch (is_eq(p, t)) { case l_true: break; case l_false: return false; default: conds.push_back(m.mk_eq(p, t)); break; } } else { s.insert(v, 0, expr_offset(t, 1)); } return true; } bool match_app(app* p, app* t, substitution& s, expr_ref_vector& conds) { switch(is_eq(p, t)) { case l_true: return true; case l_false: return false; default: conds.push_back(m.mk_eq(p, t)); return true; } } public: matcher(ast_manager& m): m(m), m_dt(m) {} bool operator()(app* pat, app* term, substitution& s, expr_ref_vector& conds) { // top-most term to match is a predicate. The predicates should be the same. if (pat->get_decl() != term->get_decl() || pat->get_num_args() != term->get_num_args()) { return false; } m_todo.reset(); for (unsigned i = 0; i < pat->get_num_args(); ++i) { m_todo.push_back(expr_pair(pat->get_arg(i), term->get_arg(i))); } while (!m_todo.empty()) { expr_pair const& pr = m_todo.back(); expr* p = pr.first; expr* t = pr.second; m_todo.pop_back(); if (!is_app(t)) { IF_VERBOSE(2, verbose_stream() << "term is not app\n";); return false; } else if (is_var(p) && match_var(to_var(p), to_app(t), s, conds)) { continue; } else if (!is_app(p)) { IF_VERBOSE(2, verbose_stream() << "pattern is not app\n";); return false; } else if (!match_app(to_app(p), to_app(t), s, conds)) { return false; } } return true; } }; class clause { app_ref m_head; // head predicate app_ref_vector m_predicates; // predicates used in goal expr_ref m_constraint; // side constraint unsigned m_seqno; // sequence number of goal unsigned m_index; // index of goal into set of goals unsigned m_num_vars; // maximal free variable index+1 unsigned m_predicate_index; // selected predicate unsigned m_parent_rule; // rule used to produce goal unsigned m_parent_index; // index of parent goal unsigned m_next_rule; // next rule to expand goal on unsigned m_ref; // reference count public: clause(ast_manager& m): m_head(m), m_predicates(m), m_constraint(m), m_seqno(0), m_index(0), m_num_vars(0), m_predicate_index(0), m_parent_rule(0), m_parent_index(0), m_next_rule(static_cast(-1)), m_ref(0) { } void set_seqno(unsigned seqno) { m_seqno = seqno; } unsigned get_seqno() const { return m_seqno; } unsigned get_next_rule() const { return m_next_rule; } void inc_next_rule() { m_next_rule++; } unsigned get_predicate_index() const { return m_predicate_index; } void set_predicate_index(unsigned i) { m_predicate_index = i; } unsigned get_num_predicates() const { return m_predicates.size(); } app* get_predicate(unsigned i) const { return m_predicates[i]; } expr* get_constraint() const { return m_constraint; } unsigned get_num_vars() const { return m_num_vars; } unsigned get_index() const { return m_index; } void set_index(unsigned index) { m_index = index; } app* get_head() const { return m_head; } func_decl* get_decl() const { return m_head->get_decl(); } void set_head(app* h) { m_head = h; } unsigned get_parent_index() const { return m_parent_index; } unsigned get_parent_rule() const { return m_parent_rule; } void set_parent(ref& parent) { m_parent_index = parent->get_index(); m_parent_rule = parent->get_next_rule(); } expr_ref get_body() const { ast_manager& m = get_manager(); expr_ref_vector fmls(m); expr_ref fml(m); for (unsigned i = 0; i < m_predicates.size(); ++i) { fmls.push_back(m_predicates[i]); } fmls.push_back(m_constraint); flatten_and(fmls); bool_rewriter(m).mk_and(fmls.size(), fmls.c_ptr(), fml); return fml; } void get_free_vars(ptr_vector& vars) const { expr_free_vars fv; fv(m_head); for (unsigned i = 0; i < m_predicates.size(); ++i) { fv.accumulate(m_predicates[i]); } fv.accumulate(m_constraint); vars.append(fv.size(), fv.c_ptr()); } expr_ref to_formula() const { ast_manager& m = get_manager(); expr_ref body = get_body(); if (m.is_true(body)) { body = m_head; } else { body = m.mk_implies(body, m_head); } ptr_vector vars; svector names; get_free_vars(vars); mk_fresh_name fresh; fresh.add(body); vars.reverse(); for (unsigned i = 0; i < vars.size(); ++i) { names.push_back(fresh.next()); if (!vars[i]) vars[i] = m.mk_bool_sort(); } if (!vars.empty()) { body = m.mk_forall(vars.size(), vars.c_ptr(), names.c_ptr(), body); } return body; } void init(app* head, app_ref_vector const& predicates, expr* constraint) { m_index = 0; m_predicate_index = 0; m_next_rule = static_cast(-1); m_head = head; m_predicates.reset(); m_predicates.append(predicates); m_constraint = constraint; ptr_vector sorts; get_free_vars(sorts); m_num_vars = sorts.size(); reduce_equalities(); } void init(datalog::rule_ref& g) { m_index = 0; m_predicate_index = 0; m_next_rule = static_cast(-1); init_from_rule(g); reduce_equalities(); // IF_VERBOSE(1, display(verbose_stream());); } void inc_ref() { m_ref++; } void dec_ref() { --m_ref; if (m_ref == 0) { dealloc(this); } } void display(std::ostream& out) const { ast_manager& m = m_head.get_manager(); expr_ref_vector fmls(m); expr_ref fml(m); for (unsigned i = 0; i < m_predicates.size(); ++i) { fmls.push_back(m_predicates[i]); } fmls.push_back(m_constraint); bool_rewriter(m).mk_and(fmls.size(), fmls.c_ptr(), fml); if (!m.is_false(m_head)) { if (m.is_true(fml)) { fml = m_head; } else { fml = m.mk_implies(fml, m_head); } } out << mk_pp(fml, m) << "\n"; } private: ast_manager& get_manager() const { return m_head.get_manager(); } // Given a rule, initialize fields: // - m_num_vars - number of free variables in rule // - m_head - head predicate // - m_predicates - auxiliary predicates in body. // - m_constraint - side constraint // void init_from_rule(datalog::rule_ref const& r) { ast_manager& m = get_manager(); expr_ref_vector fmls(m); unsigned utsz = r->get_uninterpreted_tail_size(); unsigned tsz = r->get_tail_size(); for (unsigned i = utsz; i < tsz; ++i) { fmls.push_back(r->get_tail(i)); } m_num_vars = 1 + r.get_manager().get_counter().get_max_rule_var(*r); m_head = r->get_head(); m_predicates.reset(); for (unsigned i = 0; i < utsz; ++i) { m_predicates.push_back(r->get_tail(i)); } bool_rewriter(m).mk_and(fmls.size(), fmls.c_ptr(), m_constraint); } // Simplify a clause by applying equalities as substitutions on predicates. // x = t[y], if x does not occur in t[y], then add t[y] to subst. void reduce_equalities() { ast_manager& m = get_manager(); th_rewriter rw(m); unsigned delta[1] = { 0 }; expr_ref_vector fmls(m); expr_ref tmp(m); substitution subst(m); subst.reserve(1, get_num_vars()); flatten_and(m_constraint, fmls); unsigned num_fmls = fmls.size(); for (unsigned i = 0; i < num_fmls; ++i) { if (get_subst(rw, subst, i, fmls)) { fmls[i] = m.mk_true(); } } subst.apply(1, delta, expr_offset(m_head, 0), tmp); m_head = to_app(tmp); for (unsigned i = 0; i < m_predicates.size(); ++i) { subst.apply(1, delta, expr_offset(m_predicates[i].get(), 0), tmp); m_predicates[i] = to_app(tmp); } bool_rewriter(m).mk_and(fmls.size(), fmls.c_ptr(), m_constraint); subst.apply(1, delta, expr_offset(m_constraint, 0), m_constraint); rw(m_constraint); } bool get_subst(th_rewriter& rw, substitution& S, unsigned i, expr_ref_vector& fmls) { ast_manager& m = get_manager(); unsigned delta[1] = { 0 }; expr* f = fmls[i].get(); expr_ref e(m), tr(m); expr* t, *v; S.apply(1, delta, expr_offset(f, 0), e); rw(e); fmls[i] = e; if (!m.is_eq(e, v, t)) { return false; } if (!is_var(v)) { std::swap(v, t); } if (!is_var(v)) { return false; } if (!can_be_substituted(m, t)) { return false; } SASSERT(!S.contains(to_var(v), 0)); S.push_scope(); S.insert(to_var(v)->get_idx(), 0, expr_offset(t, 0)); if (!S.acyclic()) { S.pop_scope(); return false; } fmls[i] = m.mk_true(); return true; } struct non_constructor {}; struct constructor_test { ast_manager& m; datatype_util dt; constructor_test(ast_manager& m): m(m), dt(m) {} void operator()(app* e) { if (!m.is_value(e) && !dt.is_constructor(e->get_decl())) { throw non_constructor(); } } void operator()(var* v) { } void operator()(quantifier* ) { throw non_constructor(); } }; bool can_be_substituted(ast_manager& m, expr* t) { constructor_test p(m); try { quick_for_each_expr(p, t); } catch (const non_constructor &) { return false; } return true; } }; // rules class rules { typedef obj_map map; vector > m_rules; map m_index; public: typedef vector >::const_iterator iterator; iterator begin() const { return m_rules.begin(); } iterator end() const { return m_rules.end(); } void init(datalog::rule_set const& rules) { reset(); datalog::rule_manager& rm = rules.get_rule_manager(); datalog::rule_ref r(rm); datalog::rule_set::iterator it = rules.begin(); datalog::rule_set::iterator end = rules.end(); for (unsigned i = 0; it != end; ++it) { r = *it; ref g = alloc(clause, rm.get_manager()); g->init(r); g->set_index(i++); insert(g); } } void insert(ref& g) { unsigned idx = m_rules.size(); m_rules.push_back(g); func_decl* f = g->get_decl(); map::obj_map_entry* e = m_index.insert_if_not_there2(f, unsigned_vector()); SASSERT(e); e->get_data().m_value.push_back(idx); } unsigned get_num_rules(func_decl* p) const { map::obj_map_entry* e = m_index.find_core(p); if (e) { return e->get_data().get_value().size(); } else { return 0; } } void get_decls(ptr_vector& decls) const { map::iterator it = m_index.begin(); map::iterator end = m_index.end(); for (; it != end; ++it) { decls.push_back(it->m_key); } } ref get_rule(func_decl* p, unsigned idx) const { map::obj_map_entry* e = m_index.find_core(p); SASSERT(p); unsigned rule_id = e->get_data().get_value()[idx]; return m_rules[rule_id]; } private: void reset() { m_rules.reset(); m_index.reset(); } }; // subsumption index structure. class index { ast_manager& m; app_ref_vector m_preds; app_ref m_head; expr_ref m_precond; expr_ref_vector m_sideconds; ref m_clause; vector > m_index; matcher m_matcher; expr_ref_vector m_refs; obj_hashtable m_sat_lits; substitution m_subst; qe_lite m_qe; uint_set m_empty_set; bool_rewriter m_rw; smt_params m_fparams; smt::kernel m_solver; public: index(ast_manager& m): m(m), m_preds(m), m_head(m), m_precond(m), m_sideconds(m), m_matcher(m), m_refs(m), m_subst(m), m_qe(m, params_ref()), m_rw(m), m_solver(m, m_fparams) {} void insert(ref& g) { m_index.push_back(g); } bool is_subsumed(ref& g, unsigned& subsumer) { setup(*g); m_clause = g; m_solver.push(); m_solver.assert_expr(m_precond); bool found = find_match(subsumer); m_solver.pop(1); return found; } void reset() { m_index.reset(); } private: void setup(clause const& g) { m_preds.reset(); m_refs.reset(); m_sat_lits.reset(); expr_ref_vector fmls(m); expr_ref_vector vars(m); expr_ref fml(m); ptr_vector sorts; g.get_free_vars(sorts); var_subst vs(m, false); for (unsigned i = 0; i < sorts.size(); ++i) { if (!sorts[i]) { sorts[i] = m.mk_bool_sort(); } vars.push_back(m.mk_const(symbol(i), sorts[i])); } fml = vs(g.get_head(), vars.size(), vars.c_ptr()); m_head = to_app(fml); for (unsigned i = 0; i < g.get_num_predicates(); ++i) { fml = vs(g.get_predicate(i), vars.size(), vars.c_ptr()); m_preds.push_back(to_app(fml)); } fml = vs(g.get_constraint(), vars.size(), vars.c_ptr()); fmls.push_back(fml); m_precond = m.mk_and(fmls.size(), fmls.c_ptr()); IF_VERBOSE(2, verbose_stream() << "setup-match: "; for (unsigned i = 0; i < m_preds.size(); ++i) { verbose_stream() << mk_pp(m_preds[i].get(), m) << " "; } verbose_stream() << mk_pp(m_precond, m) << "\n";); } // extract pre_cond => post_cond validation obligation from match. bool find_match(unsigned& subsumer) { for (unsigned i = 0; !m.canceled() && i < m_index.size(); ++i) { if (match_rule(i)) { subsumer = m_index[i]->get_seqno(); return true; } } return false; } // // check that each predicate in r is matched by some predicate in premise. // for now: skip multiple matches within the same rule (incomplete). // bool match_rule(unsigned rule_index) { clause const& g = *m_index[rule_index]; m_sideconds.reset(); m_subst.reset(); m_subst.reserve(2, g.get_num_vars()); IF_VERBOSE(2, g.display(verbose_stream() << "try-match\n");); return match_head(g); } bool match_head(clause const& g) { return m_head->get_decl() == g.get_decl() && m_matcher(m_head, g.get_head(), m_subst, m_sideconds) && match_predicates(0, g); } bool match_predicates(unsigned predicate_index, clause const& g) { if (predicate_index == g.get_num_predicates()) { return check_substitution(g); } app* q = g.get_predicate(predicate_index); for (unsigned i = 0; !m.canceled() && i < m_preds.size(); ++i) { app* p = m_preds[i].get(); m_subst.push_scope(); unsigned limit = m_sideconds.size(); IF_VERBOSE(2, for (unsigned j = 0; j < predicate_index; ++j) { verbose_stream() << " "; } verbose_stream() << mk_pp(q, m) << " = " << mk_pp(p, m) << "\n"; ); if (q->get_decl() == p->get_decl() && m_matcher(q, p, m_subst, m_sideconds) && match_predicates(predicate_index + 1, g)) { return true; } m_subst.pop_scope(1); m_sideconds.resize(limit); } return false; } bool check_substitution(clause const& g) { unsigned deltas[2] = {0, 0}; expr_ref q(m), postcond(m); expr_ref_vector fmls(m_sideconds); m_subst.reset_cache(); for (unsigned i = 0; !m.canceled() && i < fmls.size(); ++i) { m_subst.apply(2, deltas, expr_offset(fmls[i].get(), 0), q); fmls[i] = q; } m_subst.apply(2, deltas, expr_offset(g.get_constraint(), 0), q); fmls.push_back(q); m_qe(m_empty_set, false, fmls); flatten_and(fmls); for (unsigned i = 0; i < fmls.size(); ++i) { expr_ref n = normalize(fmls[i].get()); if (m_sat_lits.contains(n)) { return false; } } m_rw.mk_and(fmls.size(), fmls.c_ptr(), postcond); if (m.canceled()) { return false; } if (m.is_false(postcond)) { return false; } if (m.is_true(postcond)) { return true; } IF_VERBOSE(2, for (unsigned i = 0; i < g.get_num_predicates(); ++i) { verbose_stream() << " "; } verbose_stream() << "check: " << mk_pp(postcond, m, 7 + g.get_num_predicates()) << "\n";); if (!is_ground(postcond)) { IF_VERBOSE(1, verbose_stream() << "TBD: non-ground\n" << mk_pp(postcond, m) << "\n"; m_clause->display(verbose_stream()); verbose_stream() << "\n=>\n"; g.display(verbose_stream()); verbose_stream() << "\n";); return false; } postcond = m.mk_not(postcond); m_solver.push(); m_solver.assert_expr(postcond); lbool is_sat = m_solver.check(); if (is_sat == l_true) { expr* n; model_ref mdl; m_solver.get_model(mdl); for (unsigned i = 0; i < fmls.size(); ++i) { n = fmls[i].get(); if (mdl->is_false(n)) { m_refs.push_back(normalize(n)); m_sat_lits.insert(m_refs.back()); } } } m_solver.pop(1); return is_sat == l_false; } expr_ref normalize(expr* e) { expr* x, *y; if (m.is_eq(e, x, y) && x->get_id() > y->get_id()) { return expr_ref(m.mk_eq(y, x), m); } else { return expr_ref(e, m); } } }; // predicate selection strategy. class selection { enum strategy { WEIGHT_SELECT, BASIC_WEIGHT_SELECT, FIRST_SELECT, VAR_USE_SELECT }; typedef svector double_vector; typedef obj_map score_map; typedef obj_map pred_map; ast_manager& m; datatype_util dt; score_map m_score_map; double_vector m_scores; double_vector m_var_scores; strategy m_strategy; pred_map m_pred_map; expr_ref_vector m_refs; double m_weight_multiply; unsigned m_update_frequency; unsigned m_next_update; public: selection(datalog::context& ctx): m(ctx.get_manager()), dt(m), m_refs(m), m_weight_multiply(1.0), m_update_frequency(20), m_next_update(20) { set_strategy(ctx.tab_selection()); } void init(rules const& rs) { reset(); double_vector& scores = m_scores; rules::iterator it = rs.begin(), end = rs.end(); for (; it != end; ++it) { ref g = *it; app* p = g->get_head(); scores.reset(); basic_score_predicate(p, scores); insert_score(p->get_decl(), scores); } normalize_scores(rs); } unsigned select(clause const& g) { switch(m_strategy) { case WEIGHT_SELECT: return weight_select(g); case BASIC_WEIGHT_SELECT: return basic_weight_select(g); case FIRST_SELECT: return trivial_select(g); case VAR_USE_SELECT: return andrei_select(g); default: return weight_select(g); } } void reset() { m_score_map.reset(); m_scores.reset(); m_var_scores.reset(); } private: // determine if constructors in p are matches by rules. bool is_reductive(app* p, double_vector const& p_scores) { func_decl* f = p->get_decl(); score_map::obj_map_entry* e = m_score_map.find_core(f); if (!e) { return false; } double_vector const& scores = e->get_data().m_value; SASSERT(scores.size() == p->get_num_args()); bool has_reductive = false; bool is_red = true; for (unsigned i = 0; is_red && i < scores.size(); ++i) { if (scores[i] >= 1) { has_reductive = true; is_red &= p_scores[i] >= 1; } } return has_reductive && is_red; } void set_strategy(symbol const& str) { if (str == symbol("weight")) { m_strategy = WEIGHT_SELECT; } if (str == symbol("basic-weight")) { m_strategy = BASIC_WEIGHT_SELECT; } else if (str == symbol("first")) { m_strategy = FIRST_SELECT; } else if (str == symbol("var-use")) { m_strategy = VAR_USE_SELECT; } else { m_strategy = WEIGHT_SELECT; } } unsigned trivial_select(clause const& g) { return 0; } unsigned andrei_select(clause const& g) { score_variables(g); double_vector& scores = m_scores; double max_score = 0; unsigned result = 0; for (unsigned i = 0; i < g.get_num_predicates(); ++i) { scores.reset(); double_vector p_scores; double score = 0; app* p = g.get_predicate(i); basic_score_predicate(p, scores); m_score_map.find(p->get_decl(), p_scores); SASSERT(p_scores.empty() || p->get_num_args() == p_scores.size()); p_scores.resize(p->get_num_args()); for (unsigned j = 0; j < p->get_num_args(); ++j) { if (is_var(p->get_arg(j))) { unsigned idx = to_var(p->get_arg(j))->get_idx(); score += m_var_scores[idx]; } else { IF_VERBOSE(2, verbose_stream() << p_scores[j] << " " << scores[j] << "\n";); score += p_scores[j]*scores[j]; } } IF_VERBOSE(2, verbose_stream() << "score: " << mk_pp(p, m) << " " << score << "\n";); if (score > max_score) { max_score = score; result = i; } } IF_VERBOSE(1, verbose_stream() << "select:" << result << "\n";); return result; } unsigned basic_weight_select(clause const& g) { double max_score = 0; unsigned result = 0; for (unsigned i = 0; i < g.get_num_predicates(); ++i) { app* p = g.get_predicate(i); double score = basic_score_predicate(p); IF_VERBOSE(2, verbose_stream() << "score: " << mk_pp(p, m) << " " << score << "\n";); if (score > max_score) { max_score = score; result = i; } } IF_VERBOSE(2, verbose_stream() << "select " << result << "\n";); return result; } unsigned weight_select(clause const& g) { prepare_weight_select(); double max_score = 0; unsigned result = 0; for (unsigned i = 0; i < g.get_num_predicates(); ++i) { app* p = g.get_predicate(i); double score = score_predicate(p); IF_VERBOSE(2, verbose_stream() << "score: " << mk_pp(p, m) << " " << score << "\n";); if (score > max_score) { max_score = score; result = i; } } IF_VERBOSE(2, verbose_stream() << "select " << result << "\n";); return result; } void score_variables(clause const& g) { m_var_scores.reset(); for (unsigned i = 0; i < g.get_num_predicates(); ++i) { app* p = g.get_predicate(i); score_variables(p); } } void score_variables(app* p) { score_map::obj_map_entry* e = m_score_map.find_core(p->get_decl()); if (!e) { return; } double_vector& scores = e->get_data().m_value; for (unsigned i = 0; i < p->get_num_args(); ++i) { if (is_var(p->get_arg(i))) { unsigned idx = to_var(p->get_arg(i))->get_idx(); if (m_var_scores.size() <= idx) { m_var_scores.resize(idx+1); } m_var_scores[idx] += scores[i]; } } } void normalize_scores(rules const& rs) { ptr_vector decls; rs.get_decls(decls); for (unsigned i = 0; i < decls.size(); ++i) { unsigned nr = rs.get_num_rules(decls[i]); score_map::obj_map_entry& e = *m_score_map.find_core(decls[i]); double_vector& scores = e.get_data().m_value; for (unsigned j = 0; j < scores.size(); ++j) { scores[j] = scores[j]/nr; } } } double basic_score_predicate(app* p) { double score = 1; for (unsigned i = 0; i < p->get_num_args(); ++i) { score += score_argument(p->get_arg(i)); } return score; } void basic_score_predicate(app* p, double_vector& scores) { for (unsigned i = 0; i < p->get_num_args(); ++i) { scores.push_back(score_argument(p->get_arg(i))); } } double score_predicate(app* p) { double score = 1; if (find_score(p, score)) { return score; } for (unsigned i = 0; i < p->get_num_args(); ++i) { score += score_argument(p->get_arg(i)); } score = adjust_score(score); insert_score(p, score); return score; } unsigned score_argument(expr* arg) { unsigned score = 0; score_argument(arg, score, 20); return score; } void score_argument(expr* arg, unsigned& score, unsigned max_score) { if (score < max_score && is_app(arg)) { app* a = to_app(arg); if (dt.is_constructor(a->get_decl())) { score += 1; for (unsigned i = 0; i < a->get_num_args(); ++i) { score_argument(a->get_arg(i), score, max_score); } } else if (m.is_value(a)) { ++score; } } } void prepare_weight_select() { SASSERT(m_next_update > 0); --m_next_update; if (m_next_update == 0) { if (m_update_frequency >= (1 << 16)) { m_update_frequency = 20; m_weight_multiply = 1.0; } m_update_frequency *= 11; m_update_frequency /= 10; m_next_update = m_update_frequency; m_weight_multiply *= 1.1; } } bool find_score(app* p, double& score) { return m_pred_map.find(p, score); } double adjust_score(double score) { return score/m_weight_multiply; } void insert_score(app* p, double score) { m_pred_map.insert(p, score); m_refs.push_back(p); } void insert_score(func_decl* f, double_vector const& scores) { score_map::obj_map_entry* e = m_score_map.find_core(f); if (e) { double_vector & old_scores = e->get_data().m_value; SASSERT(scores.size() == old_scores.size()); for (unsigned i = 0; i < scores.size(); ++i) { old_scores[i] += scores[i]; } } else { m_score_map.insert(f, scores); } } }; class unifier { ast_manager& m; ::unifier m_unifier; substitution m_S1; var_subst m_S2; expr_ref_vector m_rename; expr_ref_vector m_sub1; expr_ref_vector m_sub2; public: unifier(ast_manager& m): m(m), m_unifier(m), m_S1(m), m_S2(m, false), m_rename(m), m_sub1(m), m_sub2(m) {} bool operator()(ref& tgt, unsigned idx, ref& src, bool compute_subst, ref& result) { return unify(*tgt, idx, *src, compute_subst, result); } expr_ref_vector get_rule_subst(bool is_tgt) { if (is_tgt) { return m_sub1; } else { return m_sub2; } } bool unify(clause const& tgt, unsigned idx, clause const& src, bool compute_subst, ref& result) { qe_lite qe(m, params_ref()); reset(); SASSERT(tgt.get_predicate(idx)->get_decl() == src.get_decl()); unsigned var_cnt = std::max(tgt.get_num_vars(), src.get_num_vars()); m_S1.reserve(2, var_cnt); if (!m_unifier(tgt.get_predicate(idx), src.get_head(), m_S1)) { return false; } app_ref_vector predicates(m); expr_ref tmp(m), tmp2(m), constraint(m); app_ref head(m); result = alloc(clause, m); unsigned delta[2] = { 0, var_cnt }; m_S1.apply(2, delta, expr_offset(tgt.get_head(), 0), tmp); head = to_app(tmp); for (unsigned i = 0; i < tgt.get_num_predicates(); ++i) { if (i != idx) { m_S1.apply(2, delta, expr_offset(tgt.get_predicate(i), 0), tmp); predicates.push_back(to_app(tmp)); } else { for (unsigned j = 0; j < src.get_num_predicates(); ++j) { m_S1.apply(2, delta, expr_offset(src.get_predicate(j), 1), tmp); predicates.push_back(to_app(tmp)); } } } m_S1.apply(2, delta, expr_offset(tgt.get_constraint(), 0), tmp); m_S1.apply(2, delta, expr_offset(src.get_constraint(), 1), tmp2); constraint = m.mk_and(tmp, tmp2); // perform trivial quantifier-elimination: uint_set index_set; expr_free_vars fv; fv(head); for (unsigned i = 0; i < predicates.size(); ++i) { fv.accumulate(predicates[i].get()); } for (unsigned i = 0; i < fv.size(); ++i) { if (fv[i]) { index_set.insert(i); } } qe(index_set, false, constraint); if (m.is_false(constraint)) { return false; } // initialize rule. result->init(head, predicates, constraint); ptr_vector vars; result->get_free_vars(vars); bool change = false; var_ref w(m); for (unsigned i = 0, j = 0; i < vars.size(); ++i) { if (vars[i]) { w = m.mk_var(j, vars[i]); m_rename.push_back(w); ++j; } else { change = true; m_rename.push_back(nullptr); } } if (change) { constraint = m_S2(result->get_constraint(), m_rename.size(), m_rename.c_ptr()); for (unsigned i = 0; i < result->get_num_predicates(); ++i) { tmp = m_S2(result->get_predicate(i), m_rename.size(), m_rename.c_ptr()); predicates[i] = to_app(tmp); } tmp = m_S2(result->get_head(), m_rename.size(), m_rename.c_ptr()); head = to_app(tmp); result->init(head, predicates, constraint); } if (compute_subst) { extract_subst(delta, tgt, 0); extract_subst(delta, src, 1); } // init result using head, predicates, constraint return true; } private: void reset() { m_S1.reset(); m_S2.reset(); m_rename.reset(); m_sub1.reset(); m_sub2.reset(); } void extract_subst(unsigned const* delta, clause const& g, unsigned offset) { ptr_vector vars; var_ref v(m); expr_ref tmp(m); g.get_free_vars(vars); for (unsigned i = 0; i < vars.size(); ++i) { if (vars[i]) { v = m.mk_var(i, vars[i]); m_S1.apply(2, delta, expr_offset(v, offset), tmp); tmp = m_S2(tmp, m_rename.size(), m_rename.c_ptr()); insert_subst(offset, tmp); } else { insert_subst(offset, m.mk_true()); } } } void insert_subst(unsigned offset, expr* e) { if (offset == 0) { m_sub1.push_back(e); } else { m_sub2.push_back(e); } } }; class extract_delta { ast_manager& m; unifier m_unifier; public: extract_delta(ast_manager& m): m(m), m_unifier(m) {} // // Given a clause // P(s) :- P(t), Phi(x). // Compute the clauses: // acc: P(s) :- Delta(z,t), P(z), Phi(x). // delta1: Delta(z,z). // delta2: Delta(z,s) :- Delta(z,t), Phi(x). // void mk_delta_clauses(clause const& g, ref& acc, ref& delta1, ref& delta2) { SASSERT(g.get_num_predicates() > 0); app* p = g.get_head(); app* q = g.get_predicate(0); SASSERT(p->get_decl() == q->get_decl()); expr_ref_vector zs = mk_fresh_vars(g); expr_ref_vector zszs(m); func_decl_ref delta(m); sort_ref_vector dom(m); for (unsigned j = 0; j < 1; ++j) { for (unsigned i = 0; i < zs.size(); ++i) { dom.push_back(m.get_sort(zs[i].get())); zszs.push_back(zs[i].get()); } } app_ref_vector preds(m); delta = m.mk_fresh_func_decl("Delta", dom.size(), dom.c_ptr(), m.mk_bool_sort()); acc = alloc(clause, m); delta1 = alloc(clause, m); delta2 = alloc(clause, m); delta1->init(m.mk_app(delta, zszs.size(), zszs.c_ptr()), preds, m.mk_true()); for (unsigned i = 0; i < zs.size(); ++i) { zszs[i+zs.size()] = p->get_arg(i); } app_ref head(m), pred(m); head = m.mk_app(delta, zszs.size(), zszs.c_ptr()); for (unsigned i = 0; i < zs.size(); ++i) { zszs[i+zs.size()] = q->get_arg(i); } pred = m.mk_app(delta, zszs.size(), zszs.c_ptr()); preds.push_back(pred); for (unsigned i = 1; i < g.get_num_predicates(); ++i) { preds.push_back(g.get_predicate(i)); } delta2->init(head, preds, g.get_constraint()); preds.push_back(m.mk_app(q->get_decl(), zs.size(), zs.c_ptr())); acc->init(p, preds, g.get_constraint()); IF_VERBOSE(1, delta1->display(verbose_stream() << "delta1:\n"); delta2->display(verbose_stream() << "delta2:\n"); acc->display(verbose_stream() << "acc:\n");); } // // Given a sequence of clauses and inference rules // compute a super-predicate and auxiliary clauses. // // P1(x) :- P2(y), R(z) // P2(y) :- P3(z), T(u) // P3(z) :- P1(x), U(v) // => // P1(x) :- P1(x), R(z), T(u), U(v) // ref resolve_rules(unsigned num_clauses, clause*const* clauses, unsigned const* positions) { ref result = clauses[0]; ref tmp; unsigned offset = 0; for (unsigned i = 0; i + 1 < num_clauses; ++i) { clause const& cl = *clauses[i+1]; offset += positions[i]; VERIFY (m_unifier.unify(*result, offset, cl, false, tmp)); result = tmp; } return result; } private: expr_ref_vector mk_fresh_vars(clause const& g) { expr_ref_vector result(m); app* p = g.get_head(); unsigned num_vars = g.get_num_vars(); for (unsigned i = 0; i < p->get_num_args(); ++i) { result.push_back(m.mk_var(num_vars+i, m.get_sort(p->get_arg(i)))); } return result; } }; enum instruction { SELECT_RULE, SELECT_PREDICATE, BACKTRACK, SATISFIABLE, UNSATISFIABLE, CANCEL }; std::ostream& operator<<(std::ostream& out, instruction i) { switch(i) { case SELECT_RULE: return out << "select-rule"; case SELECT_PREDICATE: return out << "select-predicate"; case BACKTRACK: return out << "backtrack"; case SATISFIABLE: return out << "sat"; case UNSATISFIABLE: return out << "unsat"; case CANCEL: return out << "cancel"; } return out << "unmatched instruction"; } }; namespace datalog { class tab::imp { struct stats { stats() { reset(); } void reset() { memset(this, 0, sizeof(*this)); } unsigned m_num_unfold; unsigned m_num_no_unfold; unsigned m_num_subsumed; }; context& m_ctx; ast_manager& m; rule_manager& rm; tb::index m_index; tb::selection m_selection; smt_params m_fparams; smt::kernel m_solver; mutable tb::unifier m_unifier; tb::rules m_rules; vector > m_clauses; unsigned m_seqno; tb::instruction m_instruction; lbool m_status; stats m_stats; uint_set m_displayed_rules; public: imp(context& ctx): m_ctx(ctx), m(ctx.get_manager()), rm(ctx.get_rule_manager()), m_index(m), m_selection(ctx), m_solver(m, m_fparams), m_unifier(m), m_rules(), m_seqno(0), m_instruction(tb::SELECT_PREDICATE), m_status(l_undef) { // m_fparams.m_relevancy_lvl = 0; m_fparams.m_mbqi = false; } ~imp() {} lbool query(expr* query) { m_ctx.ensure_opened(); m_index.reset(); m_selection.reset(); m_displayed_rules.reset(); m_rules.init(m_ctx.get_rules()); m_selection.init(m_rules); rule_set query_rules(m_ctx); rule_ref clause(rm); rm.mk_query(query, query_rules); clause = query_rules.last(); ref g = alloc(tb::clause, m); g->init(clause); g->set_head(m.mk_false()); init_clause(g); IF_VERBOSE(1, display_clause(*get_clause(), verbose_stream() << "g" << get_clause()->get_seqno() << " ");); return run(); } void cleanup() { m_clauses.reset(); } void reset_statistics() { m_stats.reset(); } void collect_statistics(statistics& st) const { st.update("tab.num_unfold", m_stats.m_num_unfold); st.update("tab.num_unfold_fail", m_stats.m_num_no_unfold); st.update("tab.num_subsumed", m_stats.m_num_subsumed); } void display_certificate(std::ostream& out) const { expr_ref ans = get_answer(); out << mk_pp(ans, m) << "\n"; } expr_ref get_answer() const { switch(m_status) { case l_undef: UNREACHABLE(); return expr_ref(m.mk_false(), m); case l_true: { proof_ref pr = get_proof(); return expr_ref(pr.get(), m); } case l_false: // NOT_IMPLEMENTED_YET(); return expr_ref(m.mk_true(), m); } UNREACHABLE(); return expr_ref(m.mk_true(), m); } private: void select_predicate() { tb::clause & g = *get_clause(); unsigned num_predicates = g.get_num_predicates(); if (num_predicates == 0) { m_instruction = tb::UNSATISFIABLE; IF_VERBOSE(2, g.display(verbose_stream()); ); } else { m_instruction = tb::SELECT_RULE; unsigned pi = m_selection.select(g); g.set_predicate_index(pi); IF_VERBOSE(2, verbose_stream() << mk_pp(g.get_predicate(pi), m) << "\n";); } } void apply_rule(ref& r) { ref clause = get_clause(); ref next_clause; if (m_unifier(clause, clause->get_predicate_index(), r, false, next_clause) && !query_is_tautology(*next_clause)) { init_clause(next_clause); unsigned subsumer = 0; IF_VERBOSE(1, display_rule(*clause, verbose_stream()); display_premise(*clause, verbose_stream() << "g" << next_clause->get_seqno() << " "); display_clause(*next_clause, verbose_stream()); ); if (m_index.is_subsumed(next_clause, subsumer)) { IF_VERBOSE(1, verbose_stream() << "subsumed by g" << subsumer << "\n";); m_stats.m_num_subsumed++; m_clauses.pop_back(); m_instruction = tb::SELECT_RULE; } else { m_stats.m_num_unfold++; next_clause->set_parent(clause); m_index.insert(next_clause); m_instruction = tb::SELECT_PREDICATE; } } else { m_stats.m_num_no_unfold++; m_instruction = tb::SELECT_RULE; } } void select_rule() { tb::clause& g = *get_clause(); g.inc_next_rule(); unsigned pi = g.get_predicate_index(); func_decl* p = g.get_predicate(pi)->get_decl(); unsigned num_rules = m_rules.get_num_rules(p); unsigned index = g.get_next_rule(); if (num_rules <= index) { m_instruction = tb::BACKTRACK; } else { ref rl = m_rules.get_rule(p, index); apply_rule(rl); } } void backtrack() { SASSERT(!m_clauses.empty()); m_clauses.pop_back(); if (m_clauses.empty()) { m_instruction = tb::SATISFIABLE; } else { m_instruction = tb::SELECT_RULE; } } lbool run() { m_instruction = tb::SELECT_PREDICATE; m_status = l_undef; while (true) { IF_VERBOSE(2, verbose_stream() << m_instruction << "\n";); if (m.canceled()) { cleanup(); return l_undef; } switch(m_instruction) { case tb::SELECT_PREDICATE: select_predicate(); break; case tb::SELECT_RULE: select_rule(); break; case tb::BACKTRACK: backtrack(); break; case tb::SATISFIABLE: m_status = l_false; return l_false; case tb::UNSATISFIABLE: m_status = l_true; IF_VERBOSE(1, display_certificate(verbose_stream());); return l_true; case tb::CANCEL: cleanup(); m_status = l_undef; return l_undef; } } } bool query_is_tautology(tb::clause const& g) { expr_ref fml = g.to_formula(); fml = m.mk_not(fml); m_solver.push(); m_solver.assert_expr(fml); lbool is_sat = m_solver.check(); m_solver.pop(1); TRACE("dl", tout << is_sat << ":\n" << mk_pp(fml, m) << "\n";); return l_false == is_sat; } void init_clause(ref& clause) { clause->set_index(m_clauses.size()); clause->set_seqno(m_seqno++); m_clauses.push_back(clause); } ref get_clause() const { return m_clauses.back(); } void display_rule(tb::clause const& p, std::ostream& out) { func_decl* f = p.get_predicate(p.get_predicate_index())->get_decl(); ref rl = m_rules.get_rule(f, p.get_next_rule()); unsigned idx = rl->get_index(); if (!m_displayed_rules.contains(idx)) { m_displayed_rules.insert(idx); rl->display(out << "r" << p.get_next_rule() << ": "); } } void display_premise(tb::clause& p, std::ostream& out) { func_decl* f = p.get_predicate(p.get_predicate_index())->get_decl(); out << "{g" << p.get_seqno() << " " << f->get_name() << " pos: " << p.get_predicate_index() << " rule: " << p.get_next_rule() << "}\n"; } void display_clause(tb::clause& g, std::ostream& out) { g.display(out); } proof_ref get_proof() const { scoped_proof sp(m); proof_ref pr(m); proof_ref_vector prs(m); ref clause = get_clause(); ref replayed_clause; replace_proof_converter pc(m); // clause is a empty clause. // Pretend it is asserted. // It gets replaced by premises. SASSERT(clause->get_num_predicates() == 0); expr_ref root = clause->to_formula(); vector substs; while (0 != clause->get_index()) { SASSERT(clause->get_parent_index() < clause->get_index()); unsigned p_index = clause->get_parent_index(); unsigned p_rule = clause->get_parent_rule(); ref parent = m_clauses[p_index]; unsigned pi = parent->get_predicate_index(); func_decl* pred = parent->get_predicate(pi)->get_decl(); ref rl = m_rules.get_rule(pred, p_rule); VERIFY(m_unifier(parent, parent->get_predicate_index(), rl, true, replayed_clause)); expr_ref_vector s1(m_unifier.get_rule_subst(true)); expr_ref_vector s2(m_unifier.get_rule_subst(false)); resolve_rule(pc, *parent, *rl, s1, s2, *clause); clause = parent; substs.push_back(s1); } IF_VERBOSE(1, display_body_insts(substs, *clause, verbose_stream());); pc.invert(); prs.push_back(m.mk_asserted(root)); pr = pc(m, 1, prs.c_ptr()); return pr; } void display_body_insts(vector const& substs, tb::clause const& clause, std::ostream& out) const { expr_ref_vector subst(m); for (unsigned i = substs.size(); i > 0; ) { --i; apply_subst(subst, substs[i]); } expr_ref body = clause.get_body(); var_subst vs(m, false); body = vs(body, subst.size(), subst.c_ptr()); out << body << "\n"; } void resolve_rule(replace_proof_converter& pc, tb::clause const& r1, tb::clause const& r2, expr_ref_vector const& s1, expr_ref_vector const& s2, tb::clause const& res) const { unsigned idx = r1.get_predicate_index(); expr_ref fml = res.to_formula(); vector substs; svector > positions; substs.push_back(s1); substs.push_back(s2); scoped_proof _sc(m); proof_ref pr(m); proof_ref_vector premises(m); premises.push_back(m.mk_asserted(r1.to_formula())); premises.push_back(m.mk_asserted(r2.to_formula())); positions.push_back(std::make_pair(idx+1, 0)); pr = m.mk_hyper_resolve(2, premises.c_ptr(), fml, positions, substs); pc.insert(pr); } }; tab::tab(context& ctx): datalog::engine_base(ctx.get_manager(),"tabulation"), m_imp(alloc(imp, ctx)) { } tab::~tab() { dealloc(m_imp); } lbool tab::query(expr* query) { return m_imp->query(query); } void tab::cleanup() { m_imp->cleanup(); } void tab::reset_statistics() { m_imp->reset_statistics(); } void tab::collect_statistics(statistics& st) const { m_imp->collect_statistics(st); } void tab::display_certificate(std::ostream& out) const { m_imp->display_certificate(out); } expr_ref tab::get_answer() { return m_imp->get_answer(); } }; z3-z3-4.8.7/src/muz/tab/tab_context.h000066400000000000000000000015401356505360400172620ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: tab_context.h Abstract: Tabulation/subsumption/cyclic proof context. Author: Nikolaj Bjorner (nbjorner) 2013-01-15 Revision History: --*/ #ifndef TAB_CONTEXT_H_ #define TAB_CONTEXT_H_ #include "ast/ast.h" #include "util/lbool.h" #include "util/statistics.h" #include "muz/base/dl_engine_base.h" namespace datalog { class context; class tab : public engine_base { class imp; imp* m_imp; public: tab(context& ctx); ~tab() override; lbool query(expr* query) override; void cleanup() override; void reset_statistics() override; void collect_statistics(statistics& st) const override; void display_certificate(std::ostream& out) const override; expr_ref get_answer() override; }; }; #endif z3-z3-4.8.7/src/muz/transforms/000077500000000000000000000000001356505360400162275ustar00rootroot00000000000000z3-z3-4.8.7/src/muz/transforms/CMakeLists.txt000066400000000000000000000014451356505360400207730ustar00rootroot00000000000000 z3_add_component(transforms SOURCES dl_mk_array_blast.cpp dl_mk_backwards.cpp dl_mk_bit_blast.cpp dl_mk_coalesce.cpp dl_mk_coi_filter.cpp dl_mk_filter_rules.cpp dl_mk_interp_tail_simplifier.cpp dl_mk_karr_invariants.cpp dl_mk_loop_counter.cpp dl_mk_magic_sets.cpp dl_mk_magic_symbolic.cpp dl_mk_quantifier_abstraction.cpp dl_mk_quantifier_instantiation.cpp dl_mk_rule_inliner.cpp dl_mk_scale.cpp dl_mk_separate_negated_tails.cpp dl_mk_slice.cpp dl_mk_subsumption_checker.cpp dl_mk_unbound_compressor.cpp dl_mk_unfold.cpp dl_transforms.cpp dl_mk_array_eq_rewrite.cpp dl_mk_array_instantiation.cpp dl_mk_elim_term_ite.cpp dl_mk_synchronize.cpp COMPONENT_DEPENDENCIES dataflow hilbert muz ) z3-z3-4.8.7/src/muz/transforms/dl_mk_array_blast.cpp000066400000000000000000000245701356505360400224140ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: dl_mk_array_blast.cpp Abstract: Remove array stores from rules. Author: Nikolaj Bjorner (nbjorner) 2012-11-23 Revision History: --*/ #include "muz/transforms/dl_mk_array_blast.h" #include "ast/ast_util.h" #include "ast/scoped_proof.h" namespace datalog { mk_array_blast::mk_array_blast(context & ctx, unsigned priority) : rule_transformer::plugin(priority, false), m_ctx(ctx), m(ctx.get_manager()), a(m), rm(ctx.get_rule_manager()), m_rewriter(m, m_params), m_simplifier(ctx), m_next_var(0) { m_params.set_bool("expand_select_store",true); m_rewriter.updt_params(m_params); } mk_array_blast::~mk_array_blast() { } bool mk_array_blast::is_store_def(expr* e, expr*& x, expr*& y) { if (m.is_eq(e, x, y)) { if (!a.is_store(y)) { std::swap(x,y); } if (is_var(x) && a.is_store(y)) { return true; } } return false; } expr* mk_array_blast::get_select(expr* e) const { while (a.is_select(e)) { e = to_app(e)->get_arg(0); } return e; } void mk_array_blast::get_select_args(expr* e, ptr_vector& args) const { while (a.is_select(e)) { app* ap = to_app(e); for (unsigned i = 1; i < ap->get_num_args(); ++i) { args.push_back(ap->get_arg(i)); } e = ap->get_arg(0); } } bool mk_array_blast::insert_def(rule const& r, app* e, var* v) { // // For the Ackermann reduction we would like the arrays // to be variables, so that variables can be // assumed to represent difference (alias) // classes. Ehm., Soundness of this approach depends on // if the arrays are finite domains... // if (!is_var(get_select(e))) { return false; } if (v) { m_defs.insert(e, to_var(v)); } else { if (m_next_var == 0) { ptr_vector vars; r.get_vars(m, vars); m_next_var = vars.size() + 1; } v = m.mk_var(m_next_var, m.get_sort(e)); m_defs.insert(e, v); ++m_next_var; } return true; } bool mk_array_blast::is_select_eq_var(expr* e, app*& s, var*& v) const { expr* x, *y; if (m.is_eq(e, x, y) || m.is_iff(e, x, y)) { if (a.is_select(y)) { std::swap(x,y); } if (a.is_select(x) && is_var(y)) { s = to_app(x); v = to_var(y); return true; } } return false; } bool mk_array_blast::ackermanize(rule const& r, expr_ref& body, expr_ref& head) { expr_ref_vector conjs(m), trail(m); flatten_and(body, conjs); m_defs.reset(); m_next_var = 0; ptr_vector todo; obj_map cache; ptr_vector args; app_ref e1(m); app* s; var* v; // disable Ackerman reduction if head contains a non-variable or non-constant argument. for (unsigned i = 0; i < to_app(head)->get_num_args(); ++i) { expr* arg = to_app(head)->get_arg(i); if (!is_var(arg) && !m.is_value(arg)) return false; } for (unsigned i = 0; i < conjs.size(); ++i) { expr* e = conjs[i].get(); if (is_select_eq_var(e, s, v)) { todo.append(s->get_num_args(), s->get_args()); } else { todo.push_back(e); } } while (!todo.empty()) { expr* e = todo.back(); if (cache.contains(e)) { todo.pop_back(); continue; } if (is_var(e)) { cache.insert(e, e); todo.pop_back(); continue; } if (!is_app(e)) { return false; } app* ap = to_app(e); bool valid = true; args.reset(); for (unsigned i = 0; i < ap->get_num_args(); ++i) { expr* arg; if (cache.find(ap->get_arg(i), arg)) { args.push_back(arg); } else { todo.push_back(ap->get_arg(i)); valid = false; } } if (valid) { todo.pop_back(); e1 = m.mk_app(ap->get_decl(), args.size(), args.c_ptr()); trail.push_back(e1); if (a.is_select(ap)) { if (m_defs.find(e1, v)) { cache.insert(e, v); } else if (!insert_def(r, e1, nullptr)) { return false; } else { cache.insert(e, m_defs.find(e1)); } } else { cache.insert(e, e1); } } } for (unsigned i = 0; i < conjs.size(); ++i) { expr* e = conjs[i].get(); if (is_select_eq_var(e, s, v)) { args.reset(); for (unsigned j = 0; j < s->get_num_args(); ++j) { args.push_back(cache.find(s->get_arg(j))); } e1 = m.mk_app(s->get_decl(), args.size(), args.c_ptr()); if (!m_defs.contains(e1) && !insert_def(r, e1, v)) { return false; } conjs[i] = m.mk_eq(v, m_defs.find(e1)); } else { conjs[i] = cache.find(e); } } // perform the Ackermann reduction by creating implications // i1 = i2 => val1 = val2 for each equality pair: // (= val1 (select a_i i1)) // (= val2 (select a_i i2)) defs_t::iterator it1 = m_defs.begin(), end = m_defs.end(); for (; it1 != end; ++it1) { app* a1 = it1->m_key; var* v1 = it1->m_value; defs_t::iterator it2 = it1; ++it2; for (; it2 != end; ++it2) { app* a2 = it2->m_key; var* v2 = it2->m_value; TRACE("dl", tout << mk_pp(a1, m) << " " << mk_pp(a2, m) << "\n";); if (get_select(a1) != get_select(a2)) { continue; } expr_ref_vector eqs(m); ptr_vector args1, args2; get_select_args(a1, args1); get_select_args(a2, args2); for (unsigned j = 0; j < args1.size(); ++j) { eqs.push_back(m.mk_eq(args1[j], args2[j])); } conjs.push_back(m.mk_implies(m.mk_and(eqs.size(), eqs.c_ptr()), m.mk_eq(v1, v2))); } } body = m.mk_and(conjs.size(), conjs.c_ptr()); m_rewriter(body); return true; } bool mk_array_blast::blast(rule& r, rule_set& rules) { unsigned utsz = r.get_uninterpreted_tail_size(); unsigned tsz = r.get_tail_size(); expr_ref_vector conjs(m), new_conjs(m); expr_ref tmp(m); expr_safe_replace sub(m); bool change = false; bool inserted = false; for (unsigned i = 0; i < utsz; ++i) { new_conjs.push_back(r.get_tail(i)); } for (unsigned i = utsz; i < tsz; ++i) { conjs.push_back(r.get_tail(i)); } flatten_and(conjs); for (unsigned i = 0; i < conjs.size(); ++i) { expr* x, *y, *e = conjs[i].get(); if (is_store_def(e, x, y)) { // enforce topological order consistency: uint_set lhs = rm.collect_vars(x); uint_set rhs_vars = rm.collect_vars(y); lhs &= rhs_vars; if (!lhs.empty()) { TRACE("dl", tout << "unusable equality " << mk_pp(e, m) << "\n";); new_conjs.push_back(e); } else { sub.insert(x, y); inserted = true; } } else { m_rewriter(e, tmp); new_conjs.push_back(tmp); } } if (!inserted) { rules.add_rule(&r); return false; } expr_ref fml1(m), fml2(m), body(m), head(m); body = m.mk_and(new_conjs.size(), new_conjs.c_ptr()); head = r.get_head(); sub(body); m_rewriter(body); sub(head); m_rewriter(head); TRACE("dl", tout << body << " => " << head << "\n";); change = ackermanize(r, body, head); if (!change) { rules.add_rule(&r); return false; } fml2 = m.mk_implies(body, head); proof_ref p(m); rule_set new_rules(m_ctx); TRACE("dl", tout << fml2 << "\n";); rm.mk_rule(fml2, p, new_rules, r.name()); rule_ref new_rule(rm); if (m_simplifier.transform_rule(new_rules.last(), new_rule)) { if (r.get_proof()) { scoped_proof _sc(m); rm.to_formula(r, fml1); p = m.mk_rewrite(fml1, fml2); p = m.mk_modus_ponens(r.get_proof(), p); new_rule->set_proof(m, p); } rules.add_rule(new_rule.get()); rm.mk_rule_rewrite_proof(r, *new_rule.get()); TRACE("dl", new_rule->display(m_ctx, tout << "new rule\n");); } return true; } rule_set * mk_array_blast::operator()(rule_set const & source) { if (!m_ctx.array_blast ()) { return nullptr; } rule_set* rules = alloc(rule_set, m_ctx); rules->inherit_predicates(source); rule_set::iterator it = source.begin(), end = source.end(); bool change = false; for (; !m_ctx.canceled() && it != end; ++it) { change = blast(**it, *rules) || change; } if (!change) { dealloc(rules); rules = nullptr; } return rules; } }; z3-z3-4.8.7/src/muz/transforms/dl_mk_array_blast.h000066400000000000000000000034421356505360400220540ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: dl_mk_array_blast.h Abstract: Remove array variables from rules. Author: Nikolaj Bjorner (nbjorner) 2012-11-23 Revision History: --*/ #ifndef DL_MK_ARRAY_BLAST_H_ #define DL_MK_ARRAY_BLAST_H_ #include "muz/base/dl_context.h" #include "muz/base/dl_rule_set.h" #include "muz/base/dl_rule_transformer.h" #include "muz/transforms/dl_mk_interp_tail_simplifier.h" #include "tactic/equiv_proof_converter.h" #include "ast/array_decl_plugin.h" #include "ast/rewriter/expr_safe_replace.h" namespace datalog { /** \brief Blast occurrences of arrays in rules */ class mk_array_blast : public rule_transformer::plugin { typedef obj_map defs_t; context& m_ctx; ast_manager& m; array_util a; rule_manager& rm; params_ref m_params; th_rewriter m_rewriter; mk_interp_tail_simplifier m_simplifier; defs_t m_defs; unsigned m_next_var; bool blast(rule& r, rule_set& new_rules); bool is_store_def(expr* e, expr*& x, expr*& y); bool ackermanize(rule const& r, expr_ref& body, expr_ref& head); expr* get_select(expr* e) const; void get_select_args(expr* e, ptr_vector& args) const; bool insert_def(rule const& r, app* e, var* v); bool is_select_eq_var(expr* e, app*& s, var*& v) const; public: /** \brief Create rule transformer that removes array stores and selects by ackermannization. */ mk_array_blast(context & ctx, unsigned priority); ~mk_array_blast() override; rule_set * operator()(rule_set const & source) override; }; }; #endif /* DL_MK_ARRAY_BLAST_H_ */ z3-z3-4.8.7/src/muz/transforms/dl_mk_array_eq_rewrite.cpp000066400000000000000000000067251356505360400234570ustar00rootroot00000000000000/*++ Module Name: dl_mk_array_eq_rewrite.h Abstract: Selects a representative for array equivalence classes. Author: Julien Braine Revision History: --*/ #include "ast/pattern/pattern_inference.h" #include "ast/rewriter/expr_safe_replace.h" #include "ast/expr_abstract.h" #include "muz/base/dl_context.h" #include "muz/base/dl_context.h" #include "muz/base/fp_params.hpp" #include "muz/transforms/dl_mk_array_eq_rewrite.h" #include "ast/rewriter/factor_equivs.h" namespace datalog { mk_array_eq_rewrite::mk_array_eq_rewrite( context & ctx, unsigned priority): plugin(priority), m(ctx.get_manager()), m_ctx(ctx), m_a(m) { } rule_set * mk_array_eq_rewrite::operator()(rule_set const & source) { m_src_set = &source; rule_set * result = alloc(rule_set, m_ctx); result->inherit_predicates(source); m_dst = result; m_src_manager = &source.get_rule_manager(); for (rule * rp : source) { instantiate_rule(*rp, *result); } return result; } void mk_array_eq_rewrite::instantiate_rule(const rule& r, rule_set & dest) { //Reset everything m_cnt = m_src_manager->get_counter().get_max_rule_var(r)+1; expr_ref_vector new_tail(m); unsigned nb_predicates = r.get_uninterpreted_tail_size(); unsigned tail_size = r.get_tail_size(); for (unsigned i = 0; i < nb_predicates; i++) { new_tail.push_back(r.get_tail(i)); } expr_equiv_class array_eq_classes(m); for(unsigned i = nb_predicates; i < tail_size; i++) { expr* cond = r.get_tail(i); expr* e1, *e2; if (m.is_eq(cond, e1, e2) && m_a.is_array(get_sort(e1))) { array_eq_classes.merge(e1, e2); } else { new_tail.push_back(cond); } } for (auto c_eq : array_eq_classes) { expr* representative = *(c_eq.begin()); for (expr * v : c_eq) { if (!is_var(v)) { representative = v; break; } } for (expr * v : c_eq) { for (unsigned i = 0; i < new_tail.size(); i++) new_tail[i] = replace(new_tail[i].get(), representative, v); } for (expr * v : c_eq) { new_tail.push_back(m.mk_eq(v, representative)); } } params_ref select_over_store; select_over_store.set_bool("expand_select_store", true); th_rewriter t(m, select_over_store); expr_ref_vector res_conjs(m); for (expr* e : new_tail) { expr_ref tmp(m); t(e, tmp); res_conjs.push_back(tmp); } proof_ref pr(m); m_src_manager->mk_rule(m.mk_implies(m.mk_and(res_conjs.size(), res_conjs.c_ptr()), r.get_head()), pr, dest, r.name()); } // NSB Code review: use substitution facility, such as expr_safe_replace or expr_replacer. expr* mk_array_eq_rewrite::replace(expr* e, expr* new_val, expr* old_val) { if (e == old_val) return new_val; else if (!is_app(e)) { return e; } app* f = to_app(e); ptr_vector n_args; for (expr * arg : *f) { n_args.push_back(replace(arg, new_val, old_val)); } return m.mk_app(f->get_decl(), n_args.size(), n_args.c_ptr()); } } z3-z3-4.8.7/src/muz/transforms/dl_mk_array_eq_rewrite.h000066400000000000000000000020441356505360400231120ustar00rootroot00000000000000/*++ Module Name: dl_mk_array_eq_rewrite.h Abstract: Selects a representative for array equivalence classes. Author: Julien Braine Revision History: --*/ #ifndef DL_MK_ARRAY_EQ_REWRITE_H_ #define DL_MK_ARRAY_EQ_REWRITE_H_ #include "muz/base/dl_rule_transformer.h" namespace datalog { class context; class mk_array_eq_rewrite : public rule_transformer::plugin { //Context objects ast_manager& m; context& m_ctx; array_util m_a; //Rule set context const rule_set* m_src_set; rule_set* m_dst; rule_manager* m_src_manager; unsigned m_cnt;//Index for new variables expr* replace(expr* e, expr* new_val, expr* old_val); void instantiate_rule(const rule& r, rule_set & dest); public: mk_array_eq_rewrite(context & ctx, unsigned priority); rule_set * operator()(rule_set const & source) override; ~mk_array_eq_rewrite() override{} }; }; #endif /* DL_MK_ARRAY_EQ_REWRITE_H_ */ z3-z3-4.8.7/src/muz/transforms/dl_mk_array_instantiation.cpp000066400000000000000000000222641356505360400241710ustar00rootroot00000000000000/*++ Module Name: dl_mk_array_instantiation.h Abstract: Does array instantiation Author: Julien Braine Revision History: --*/ #include "muz/transforms/dl_mk_array_instantiation.h" #include "muz/base/dl_context.h" #include "ast/pattern/pattern_inference.h" #include "muz/base/dl_context.h" #include "ast/rewriter/expr_safe_replace.h" #include "ast/expr_abstract.h" #include "muz/base/fp_params.hpp" namespace datalog { mk_array_instantiation::mk_array_instantiation( context & ctx, unsigned priority): plugin(priority), m(ctx.get_manager()), m_ctx(ctx), m_a(m), eq_classes(m), ownership(m) { } rule_set * mk_array_instantiation::operator()(rule_set const & source) { std::cout<<"Array Instantiation called with parameters :" <<" enforce="<display(std::cout); return result; } void mk_array_instantiation::instantiate_rule(const rule& r, rule_set & dest) { //Reset everything selects.reset(); eq_classes.reset(); cnt = src_manager->get_counter().get_max_rule_var(r)+1; done_selects.reset(); ownership.reset(); expr_ref_vector phi(m); expr_ref_vector preds(m); expr_ref new_head = create_head(to_app(r.get_head())); unsigned nb_predicates = r.get_uninterpreted_tail_size(); unsigned tail_size = r.get_tail_size(); for(unsigned i=0;i::iterator it = done_selects.begin(); it!=done_selects.end(); ++it) { expr_ref tmp(m); tmp = &it->get_key(); new_tail.push_back(m.mk_eq(it->get_value(), tmp)); } proof_ref pr(m); src_manager->mk_rule(m.mk_implies(m.mk_and(new_tail.size(), new_tail.c_ptr()), new_head), pr, dest, r.name()); } expr_ref mk_array_instantiation::create_head(app* old_head) { expr_ref_vector new_args(m); for(unsigned i=0;iget_num_args();i++) { expr*arg = old_head->get_arg(i); if(m_a.is_array(get_sort(arg))) { for(unsigned k=0; k< m_ctx.get_params().xform_instantiate_arrays_nb_quantifier();k++) { expr_ref_vector dummy_args(m); dummy_args.push_back(arg); for(unsigned i=0;i()); selects[arg].push_back(select); } if(!m_ctx.get_params().xform_instantiate_arrays_enforce()) new_args.push_back(arg); } else new_args.push_back(arg); } return create_pred(old_head, new_args); } void mk_array_instantiation::retrieve_selects(expr* e) { //If the expression is not a function application, we ignore it if (!is_app(e)) { return; } app*f=to_app(e); //Call the function recursively on all arguments unsigned nbargs = f->get_num_args(); for(unsigned i=0;iget_arg(i)); } //If it is a select, then add it to selects if(m_a.is_select(f)) { SASSERT(!m_a.is_array(get_sort(e))); selects.insert_if_not_there(f->get_arg(0), ptr_vector()); selects[f->get_arg(0)].push_back(e); } //If it is a condition between arrays, for example the result of a store, then add it to the equiv_classes if(m_a.is_store(f)) { eq_classes.merge(e, f->get_arg(0)); } else if(m.is_eq(f) && m_a.is_array(get_sort(f->get_arg(0)))) { eq_classes.merge(f->get_arg(0), f->get_arg(1)); } } expr_ref_vector mk_array_instantiation::getId(app*old_pred, const expr_ref_vector& n_args) { expr_ref_vector res(m); for(unsigned i=0;iget_num_args();j++) { res.push_back(select->get_arg(j)); } } } return res; } expr_ref mk_array_instantiation::create_pred(app*old_pred, expr_ref_vector& n_args) { expr_ref_vector new_args(m); new_args.append(n_args); new_args.append(getId(old_pred, n_args)); for(unsigned i=0;iget_decl()->get_name().str()+"!inst").c_str()), new_sorts.size(), new_sorts.c_ptr(), old_pred->get_decl()->get_range()); m_ctx.register_predicate(fun_decl, false); if(src_set->is_output_predicate(old_pred->get_decl())) dst->set_output_predicate(fun_decl); res=m.mk_app(fun_decl,new_args.size(), new_args.c_ptr()); return res; } var * mk_array_instantiation::mk_select_var(expr* select) { var*result; if(!done_selects.find(select, result)) { ownership.push_back(select); result = m.mk_var(cnt, get_sort(select)); cnt++; done_selects.insert(select, result); } return result; } expr_ref mk_array_instantiation::rewrite_select(expr*array, expr*select) { app*s = to_app(select); expr_ref res(m); expr_ref_vector args(m); args.push_back(array); for(unsigned i=1; iget_num_args();i++) { args.push_back(s->get_arg(i)); } res = m_a.mk_select(args.size(), args.c_ptr()); return res; } expr_ref_vector mk_array_instantiation::retrieve_all_selects(expr*array) { expr_ref_vector all_selects(m); for(expr_equiv_class::iterator it = eq_classes.begin(array); it != eq_classes.end(array); ++it) { selects.insert_if_not_there(*it, ptr_vector()); ptr_vector& select_ops = selects[*it]; for(unsigned i=0;iget_num_args(); //Stores, for each old position, the list of a new possible arguments vector arg_correspondance; for(unsigned i=0;iget_arg(i), m); if(m_a.is_array(get_sort(arg))) { vector arg_possibilities(m_ctx.get_params().xform_instantiate_arrays_nb_quantifier(), retrieve_all_selects(arg)); arg_correspondance.append(arg_possibilities); if(!m_ctx.get_params().xform_instantiate_arrays_enforce()) { expr_ref_vector tmp(m); tmp.push_back(arg); arg_correspondance.push_back(tmp); } } else { expr_ref_vector tmp(m); tmp.push_back(arg); arg_correspondance.push_back(tmp); } } //Now, we need to deal with every combination expr_ref_vector res(m); svector chosen(arg_correspondance.size(), 0u); while(true) { expr_ref_vector new_args(m); for(unsigned i=0;i=arg_correspondance[pos].size()); chosen[pos]++; } } } z3-z3-4.8.7/src/muz/transforms/dl_mk_array_instantiation.h000066400000000000000000000131501356505360400236300ustar00rootroot00000000000000/*++ Module Name: dl_mk_array_instantiation.h Abstract: Transforms predicates so that array invariants can be discovered. Motivation : Given a predicate P(a), no quantifier-free solution can express that P(a) <=> forall i, P(a[i]) = 0 Solution : Introduce a fresh variable i, and transform P(a) into P!inst(i, a). Now, (P!inst(i,a) := a[i] = 0) <=> P(a) := forall i, a[i] = 0. Transformation on Horn rules: P(a, args) /\ phi(a, args, args') => P'(args') (for simplicity, assume there are no arrays in args'). Is transformed into: (/\_r in read_indices(phi) P!inst(r, a, args)) /\ phi(a, args, args') => P'(args') Limitations : This technique can only discover invariants on arrays that depend on one quantifier. Related work : Techniques relying on adding quantifiers and eliminating them. See dl_mk_quantifier_abstraction and dl_mk_quantifier_instantiation Implementation: The implementation follows the solution suggested above, with more options. The addition of options implies that in the simple case described above, we in fact have P(a) transformed into P(i, a[i], a). 1) Dealing with multiple quantifiers -> The options fixedpoint.xform.instantiate_arrays.nb_quantifier gives the number of quantifiers per array. 2) Enforcing the instantiation -> We suggest an option (enforce_instantiation) to enforce this abstraction. This transforms P(a) into P(i, a[i]). This enforces the solver to limit the space search at the cost of imprecise results. This option corresponds to fixedpoint.xform.instantiate_arrays.enforce 3) Adding slices in the mix -> We wish to have the possibility to further restrict the search space: we want to smash cells, given a smashing rule. For example, in for loops j=0; j P'(...) is transformed into (/\_r in read_indices(phi) P!inst(id_r, a[r], a) /\ GetId(r) = id_r) /\ phi(a, ...) => P'(...). Note : when no slicing is done, GetId(i) = i. This option corresponds to fixedpoint.xform.instantiate_arrays.slice_technique Although we described GetId as returning integers, there is no reason to restrict the type of ids to integers. A more direct method, for the 0<=i > selects; expr_equiv_class eq_classes; unsigned cnt;//Index for new variables obj_map done_selects; expr_ref_vector ownership; //Helper functions void instantiate_rule(const rule& r, rule_set & dest);//Instantiates the rule void retrieve_selects(expr* e);//Retrieves all selects (fills the selects and eq_classes members) expr_ref rewrite_select(expr*array, expr*select);//Rewrites select(a, args) to select(array, args) expr_ref_vector retrieve_all_selects(expr*array);//Retrieves all selects linked to a given array (using eq classes and selects) expr_ref_vector instantiate_pred(app*old_pred);//Returns all the instantiation of a given predicate expr_ref create_pred(app*old_pred, expr_ref_vector& new_args);//Creates a predicate expr_ref create_head(app* old_head);//Creates the new head var * mk_select_var(expr* select); /*Given the old predicate, and the new arguments for the new predicate, returns the new setId arguments. By default getId(P(x, y, a, b), (x, y, a[i], a[j], a, b[k], b[l], b)) (nb_quantifier=2, enforce=false) returns (i,j,k,l) So that the final created predicate is P!inst(x, y, a[i], a[j], a, b[k], b[l], b, i, j, k, l) */ expr_ref_vector getId(app*old_pred, const expr_ref_vector& new_args); public: mk_array_instantiation(context & ctx, unsigned priority); rule_set * operator()(rule_set const & source) override; ~mk_array_instantiation() override{} }; }; #endif /* DL_MK_ARRAY_INSTANTIATION_H_ */ z3-z3-4.8.7/src/muz/transforms/dl_mk_backwards.cpp000066400000000000000000000042711356505360400220460ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: dl_mk_backwards.cpp Abstract: Create Horn clauses for backwards flow. Author: Nikolaj Bjorner (nbjorner) 2013-04-17 Revision History: --*/ #include "muz/transforms/dl_mk_backwards.h" #include "muz/base/dl_context.h" namespace datalog { mk_backwards::mk_backwards(context & ctx, unsigned priority): plugin(priority), m(ctx.get_manager()), m_ctx(ctx) { } mk_backwards::~mk_backwards() { } rule_set * mk_backwards::operator()(rule_set const & source) { context& ctx = source.get_context(); rule_manager& rm = source.get_rule_manager(); rule_set * result = alloc(rule_set, ctx); unsigned sz = source.get_num_rules(); rule_ref new_rule(rm); app_ref_vector tail(m); app_ref head(m); svector neg; app_ref query(m); query = m.mk_fresh_const("Q", m.mk_bool_sort()); result->set_output_predicate(query->get_decl()); m_ctx.register_predicate(query->get_decl(), false); for (unsigned i = 0; i < sz; ++i) { tail.reset(); neg.reset(); rule & r = *source.get_rule(i); unsigned utsz = r.get_uninterpreted_tail_size(); unsigned tsz = r.get_tail_size(); if (!source.is_output_predicate(r.get_decl())) { tail.push_back(r.get_head()); neg.push_back(false); } for (unsigned j = utsz; j < tsz; ++j) { tail.push_back(r.get_tail(j)); neg.push_back(false); } for (unsigned j = 0; j <= utsz; ++j) { if (j == utsz && j > 0) { break; } if (j == utsz) { head = query; } else { head = r.get_tail(j); } new_rule = rm.mk(head, tail.size(), tail.c_ptr(), neg.c_ptr(), r.name(), true); result->add_rule(new_rule); } } TRACE("dl", result->display(tout);); return result; } }; z3-z3-4.8.7/src/muz/transforms/dl_mk_backwards.h000066400000000000000000000012321356505360400215050ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: dl_mk_backwards.h Abstract: Create Horn clauses for backwards flow. Author: Nikolaj Bjorner (nbjorner) 2013-04-17 Revision History: --*/ #ifndef DL_MK_BACKWARDS_H_ #define DL_MK_BACKWARDS_H_ #include "muz/base/dl_rule_transformer.h" namespace datalog { class mk_backwards : public rule_transformer::plugin { ast_manager& m; context& m_ctx; public: mk_backwards(context & ctx, unsigned priority = 33000); ~mk_backwards() override; rule_set * operator()(rule_set const & source) override; }; }; #endif /* DL_MK_BACKWARDS_H_ */ z3-z3-4.8.7/src/muz/transforms/dl_mk_bit_blast.cpp000066400000000000000000000262541356505360400220550ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: dl_mk_bit_blast.cpp Abstract: Author: Nikolaj Bjorner (nbjorner) 2012-08-30 Revision History: --*/ #include "muz/transforms/dl_mk_bit_blast.h" #include "ast/rewriter/bit_blaster/bit_blaster_rewriter.h" #include "ast/rewriter/rewriter_def.h" #include "ast/ast_pp.h" #include "ast/rewriter/expr_safe_replace.h" #include "tactic/generic_model_converter.h" #include "muz/transforms/dl_mk_interp_tail_simplifier.h" #include "muz/base/fp_params.hpp" #include "ast/scoped_proof.h" #include "model/model_v2_pp.h" namespace datalog { // // P(v) :- Q(extract[1:1]v ++ 0), R(1 ++ extract[0:0]v). // -> // P(bv(x,y)) :- Q(bv(x,0)), R(bv(1,y)) . // // Introduce P_bv: // P_bv(x,y) :- Q_bv(x,0), R_bv(1,y) // P(bv(x,y)) :- P_bv(x,y) // Query // this model converter should be composed with a filter converter // that gets rid of the new functions. class bit_blast_model_converter : public model_converter { ast_manager& m; bv_util m_bv; func_decl_ref_vector m_old_funcs; func_decl_ref_vector m_new_funcs; public: bit_blast_model_converter(ast_manager& m): m(m), m_bv(m), m_old_funcs(m), m_new_funcs(m) {} void insert(func_decl* old_f, func_decl* new_f) { m_old_funcs.push_back(old_f); m_new_funcs.push_back(new_f); } model_converter * translate(ast_translation & translator) override { return alloc(bit_blast_model_converter, m); } void get_units(obj_map& units) override {} void display(std::ostream& out) override { out << "(bit-blast-model-converter)\n"; } void operator()(model_ref & model) override { for (unsigned i = 0; i < m_new_funcs.size(); ++i) { func_decl* p = m_new_funcs[i].get(); func_decl* q = m_old_funcs[i].get(); func_interp* f = model->get_func_interp(p); if (!f) continue; expr_ref body(m); unsigned arity_q = q->get_arity(); TRACE("dl", model_v2_pp(tout, *model); tout << mk_pp(p, m) << "\n"; tout << mk_pp(q, m) << "\n";); SASSERT(0 < p->get_arity()); SASSERT(f); model->register_decl(p, f->copy()); func_interp* g = alloc(func_interp, m, arity_q); if (f) { body = f->get_interp(); SASSERT(!f->is_partial()); SASSERT(body); } else { body = m.mk_false(); } unsigned idx = 0; expr_ref arg(m), proj(m); expr_safe_replace sub(m); for (unsigned j = 0; j < arity_q; ++j) { sort* s = q->get_domain(j); arg = m.mk_var(j, s); expr* t = arg; if (m_bv.is_bv_sort(s)) { unsigned sz = m_bv.get_bv_size(s); for (unsigned k = 0; k < sz; ++k) { parameter p(k); proj = m.mk_app(m_bv.get_family_id(), OP_BIT2BOOL, 1, &p, 1, &t); sub.insert(m.mk_var(idx++, m.mk_bool_sort()), proj); } } else { sub.insert(m.mk_var(idx++, s), arg); } } sub(body); g->set_else(body); model->register_decl(q, g); } } }; class expand_mkbv_cfg : public default_rewriter_cfg { context& m_context; ast_manager& m; bv_util m_util; expr_ref_vector m_args, m_f_vars, m_g_vars; func_decl_ref_vector m_old_funcs; func_decl_ref_vector m_new_funcs; rule_set const* m_src; rule_set* m_dst; obj_map m_pred2blast; public: expand_mkbv_cfg(context& ctx): m_context(ctx), m(ctx.get_manager()), m_util(m), m_args(m), m_f_vars(m), m_g_vars(m), m_old_funcs(m), m_new_funcs(m), m_src(nullptr), m_dst(nullptr) {} ~expand_mkbv_cfg() {} void set_src(rule_set const* src) { m_src = src; } void set_dst(rule_set* dst) { m_dst = dst; } func_decl_ref_vector const& old_funcs() const { return m_old_funcs; } func_decl_ref_vector const& new_funcs() const { return m_new_funcs; } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { if (num == 0) { if (m_src->is_output_predicate(f)) m_dst->set_output_predicate(f); return BR_FAILED; } for (unsigned i = 0; i < num; ++i) { if (!m_util.is_mkbv(args[i])) return BR_FAILED; } // // f(mk_bv(args),...) // m_args.reset(); m_g_vars.reset(); m_f_vars.reset(); expr_ref fml(m); unsigned idx = 0; for (unsigned j = 0; j < num; ++j) { expr* arg = args[j]; if (m_util.is_mkbv(arg)) { app* a = to_app(arg); unsigned sz = a->get_num_args(); for (unsigned i = 0; i < sz; ++i) { m_args.push_back(a->get_arg(i)); m_g_vars.push_back(m.mk_var(idx++,m.mk_bool_sort())); } m_f_vars.push_back(m_util.mk_bv(sz, m_g_vars.c_ptr()+m_g_vars.size()-sz)); } else { m_args.push_back(arg); m_f_vars.push_back(m.mk_var(idx++, m.get_sort(arg))); m_g_vars.push_back(m_f_vars.back()); } } func_decl* g = nullptr; if (!m_pred2blast.find(f, g)) { ptr_vector domain; for (unsigned i = 0; i < m_args.size(); ++i) { domain.push_back(m.get_sort(m_args[i].get())); } g = m_context.mk_fresh_head_predicate(f->get_name(), symbol("bv"), m_args.size(), domain.c_ptr(), f); m_old_funcs.push_back(f); m_new_funcs.push_back(g); m_pred2blast.insert(f, g); m_dst->inherit_predicate(*m_src, f, g); } result = m.mk_app(g, m_args.size(), m_args.c_ptr()); result_pr = nullptr; return BR_DONE; } }; struct expand_mkbv : public rewriter_tpl { expand_mkbv_cfg m_cfg; expand_mkbv(ast_manager& m, context& ctx): rewriter_tpl(m, m.proofs_enabled(), m_cfg), m_cfg(ctx) { } }; class mk_bit_blast::impl { context & m_context; ast_manager & m; params_ref m_params; mk_interp_tail_simplifier m_simplifier; bit_blaster_rewriter m_blaster; expand_mkbv m_rewriter; bool blast(rule *r, expr_ref& fml) { proof_ref pr(m); expr_ref fml1(m), fml2(m), fml3(m); rule_ref r2(m_context.get_rule_manager()); // We need to simplify rule before bit-blasting. if (!m_simplifier.transform_rule(r, r2)) { r2 = r; } m_context.get_rule_manager().to_formula(*r2.get(), fml1); m_blaster(fml1, fml2, pr); m_rewriter(fml2, fml3); TRACE("dl", tout << mk_pp(fml, m) << " -> " << mk_pp(fml2, m) << " -> " << mk_pp(fml3, m) << "\n";); if (fml3 != fml) { fml = fml3; return true; } else { return false; } } public: impl(context& ctx): m_context(ctx), m(ctx.get_manager()), m_params(ctx.get_params().p), m_simplifier(ctx), m_blaster(ctx.get_manager(), m_params), m_rewriter(ctx.get_manager(), ctx) { m_params.set_bool("blast_full", true); m_params.set_bool("blast_quant", true); m_blaster.updt_params(m_params); } rule_set * operator()(rule_set const & source) { // TODO pc if (!m_context.xform_bit_blast()) { return nullptr; } rule_manager& rm = m_context.get_rule_manager(); unsigned sz = source.get_num_rules(); expr_ref fml(m); rule_set * result = alloc(rule_set, m_context); m_rewriter.m_cfg.set_src(&source); m_rewriter.m_cfg.set_dst(result); for (unsigned i = 0; !m_context.canceled() && i < sz; ++i) { rule * r = source.get_rule(i); rm.to_formula(*r, fml); if (blast(r, fml)) { proof_ref pr(m); if (r->get_proof()) { scoped_proof _sc(m); pr = m.mk_asserted(fml); // loses original proof of r. } // TODO add logic for pc: // 1. replace fresh predicates by non-bit-blasted predicates // 2. replace pr by the proof of r. rm.mk_rule(fml, pr, *result, r->name()); } else { result->add_rule(r); } } // copy output predicates without any rule (bit-blasting not really needed) const func_decl_set& decls = source.get_output_predicates(); for (func_decl_set::iterator I = decls.begin(), E = decls.end(); I != E; ++I) { if (!source.contains(*I)) result->set_output_predicate(*I); } if (m_context.get_model_converter()) { generic_model_converter* fmc = alloc(generic_model_converter, m, "dl_mk_bit_blast"); bit_blast_model_converter* bvmc = alloc(bit_blast_model_converter, m); func_decl_ref_vector const& old_funcs = m_rewriter.m_cfg.old_funcs(); func_decl_ref_vector const& new_funcs = m_rewriter.m_cfg.new_funcs(); for (unsigned i = 0; i < old_funcs.size(); ++i) { fmc->hide(new_funcs[i]); bvmc->insert(old_funcs[i], new_funcs[i]); } m_context.add_model_converter(concat(bvmc, fmc)); } return result; } }; mk_bit_blast::mk_bit_blast(context & ctx, unsigned priority) : plugin(priority) { m_impl = alloc(impl, ctx); } mk_bit_blast::~mk_bit_blast() { dealloc(m_impl); } rule_set * mk_bit_blast::operator()(rule_set const & source) { return (*m_impl)(source); } }; z3-z3-4.8.7/src/muz/transforms/dl_mk_bit_blast.h000066400000000000000000000012141356505360400215070ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: dl_mk_bit_blast.h Abstract: Functor for bit-blasting a rule set Author: Nikolaj Bjorner (nbjorner) 2012-08-30 Revision History: --*/ #ifndef DL_MK_BIT_BLAST_H_ #define DL_MK_BIT_BLAST_H_ #include "muz/base/dl_rule_transformer.h" namespace datalog { class mk_bit_blast : public rule_transformer::plugin { class impl; impl* m_impl; public: mk_bit_blast(context & ctx, unsigned priority = 35000); ~mk_bit_blast() override; rule_set * operator()(rule_set const & source) override; }; }; #endif /* DL_MK_BIT_BLAST_H_ */ z3-z3-4.8.7/src/muz/transforms/dl_mk_coalesce.cpp000066400000000000000000000150511356505360400216610ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: dl_mk_coalesce.cpp Abstract: Coalesce rules with shared bodies. Author: Nikolaj Bjorner (nbjorner) 2012-10-15 Revision History: Notes: Implements proof rule of the form: a(x) & q(x) -> p(x), b(y) & q(y) -> p(y) ---------------------------------------------- (a(z) \/ b(z)) & q(z) -> p(z) --*/ #include "muz/transforms/dl_mk_coalesce.h" #include "ast/rewriter/bool_rewriter.h" namespace datalog { mk_coalesce::mk_coalesce(context& ctx): rule_transformer::plugin(50, false), m_ctx(ctx), m(ctx.get_manager()), rm(ctx.get_rule_manager()), m_sub1(m), m_sub2(m), m_idx(0) {} void mk_coalesce::mk_pred(app_ref& pred, app* p1, app* p2) { SASSERT(p1->get_decl() == p2->get_decl()); unsigned sz = p1->get_num_args(); expr_ref_vector args(m); for (unsigned i = 0; i < sz; ++i) { expr* a = p1->get_arg(i); expr* b = p2->get_arg(i); SASSERT(m.get_sort(a) == m.get_sort(b)); m_sub1.push_back(a); m_sub2.push_back(b); args.push_back(m.mk_var(m_idx++, m.get_sort(a))); } pred = m.mk_app(p1->get_decl(), args.size(), args.c_ptr()); } void mk_coalesce::extract_conjs(expr_ref_vector const& sub, rule const& rl, expr_ref& result) { obj_map indices; bool_rewriter bwr(m); rule_ref r(const_cast(&rl), rm); ptr_vector sorts; expr_ref_vector revsub(m), conjs(m); rl.get_vars(m, sorts); revsub.resize(sorts.size()); svector valid(sorts.size(), true); for (unsigned i = 0; i < sub.size(); ++i) { expr* e = sub[i]; sort* s = m.get_sort(e); expr_ref w(m.mk_var(i, s), m); if (is_var(e)) { unsigned v = to_var(e)->get_idx(); SASSERT(v < valid.size()); if (sorts[v]) { SASSERT(s == sorts[v]); if (valid[v]) { revsub[v] = w; valid[v] = false; } else { SASSERT(revsub[v].get()); SASSERT(m.get_sort(revsub[v].get()) == s); conjs.push_back(m.mk_eq(revsub[v].get(), w)); } } } else { SASSERT(m.is_value(e)); SASSERT(m.get_sort(e) == m.get_sort(w)); conjs.push_back(m.mk_eq(e, w)); } } for (unsigned i = 0; i < sorts.size(); ++i) { if (valid[i] && sorts[i] && !revsub[i].get()) { revsub[i] = m.mk_var(m_idx++, sorts[i]); } } var_subst vs(m, false); for (unsigned i = r->get_uninterpreted_tail_size(); i < r->get_tail_size(); ++i) { result = vs(r->get_tail(i), revsub.size(), revsub.c_ptr()); conjs.push_back(result); } bwr.mk_and(conjs.size(), conjs.c_ptr(), result); } void mk_coalesce::merge_rules(rule_ref& tgt, rule const& src) { SASSERT(same_body(*tgt.get(), src)); m_sub1.reset(); m_sub2.reset(); m_idx = 0; app_ref pred(m), head(m); expr_ref fml1(m), fml2(m), fml(m); app_ref_vector tail(m); ptr_vector sorts1, sorts2; expr_ref_vector conjs1(m), conjs(m); rule_ref res(rm); bool_rewriter bwr(m); svector is_neg; tgt->get_vars(m, sorts1); src.get_vars(m, sorts2); mk_pred(head, src.get_head(), tgt->get_head()); for (unsigned i = 0; i < src.get_uninterpreted_tail_size(); ++i) { mk_pred(pred, src.get_tail(i), tgt->get_tail(i)); tail.push_back(pred); is_neg.push_back(src.is_neg_tail(i)); } extract_conjs(m_sub1, src, fml1); extract_conjs(m_sub2, *tgt.get(), fml2); bwr.mk_or(fml1, fml2, fml); SASSERT(is_app(fml)); tail.push_back(to_app(fml)); is_neg.push_back(false); res = rm.mk(head, tail.size(), tail.c_ptr(), is_neg.c_ptr(), tgt->name()); if (m_ctx.generate_proof_trace()) { rm.to_formula(src, fml1); rm.to_formula(*tgt.get(),fml2); rm.to_formula(*res.get(),fml); #if 0 sort* ps = m.mk_proof_sort(); sort* domain[3] = { ps, ps, m.mk_bool_sort() }; func_decl* merge = m.mk_func_decl(symbol("merge-clauses"), 3, domain, ps); // TBD: ad-hoc proof rule expr* args[3] = { m.mk_asserted(fml1), m.mk_asserted(fml2), fml }; // ...m_pc->insert(m.mk_app(merge, 3, args)); #else svector > pos; vector substs; proof* p = src.get_proof(); p = m.mk_hyper_resolve(1, &p, fml, pos, substs); res->set_proof(m, p); #endif } tgt = res; } bool mk_coalesce::same_body(rule const& r1, rule const& r2) const { SASSERT(r1.get_decl() == r2.get_decl()); unsigned sz = r1.get_uninterpreted_tail_size(); if (sz != r2.get_uninterpreted_tail_size()) { return false; } for (unsigned i = 0; i < sz; ++i) { if (r1.get_decl(i) != r2.get_decl(i)) { return false; } if (r1.is_neg_tail(i) != r2.is_neg_tail(i)) { return false; } } return true; } rule_set * mk_coalesce::operator()(rule_set const & source) { rule_set* rules = alloc(rule_set, m_ctx); rules->inherit_predicates(source); rule_set::decl2rules::iterator it = source.begin_grouped_rules(), end = source.end_grouped_rules(); for (; it != end; ++it) { rule_ref_vector d_rules(rm); d_rules.append(it->m_value->size(), it->m_value->c_ptr()); for (unsigned i = 0; i < d_rules.size(); ++i) { rule_ref r1(d_rules[i].get(), rm); for (unsigned j = i + 1; j < d_rules.size(); ++j) { if (same_body(*r1.get(), *d_rules[j].get())) { merge_rules(r1, *d_rules[j].get()); d_rules[j] = d_rules.back(); d_rules.pop_back(); --j; } } rules->add_rule(r1.get()); } } return rules; } }; z3-z3-4.8.7/src/muz/transforms/dl_mk_coalesce.h000066400000000000000000000023471356505360400213320ustar00rootroot00000000000000 /*++ Copyright (c) 2012 Microsoft Corporation Module Name: dl_mk_coalesce.h Abstract: Coalesce rules with shared bodies. Author: Nikolaj Bjorner (nbjorner) 2012-10-15 Revision History: --*/ #ifndef DL_MK_COALESCE_H_ #define DL_MK_COALESCE_H_ #include "muz/base/dl_context.h" #include "muz/base/dl_rule_set.h" #include "util/uint_set.h" #include "muz/base/dl_rule_transformer.h" #include "muz/transforms/dl_mk_rule_inliner.h" namespace datalog { /** \brief Implements an unfolding transformation. */ class mk_coalesce : public rule_transformer::plugin { context& m_ctx; ast_manager& m; rule_manager& rm; expr_ref_vector m_sub1, m_sub2; unsigned m_idx; void mk_pred(app_ref& pred, app* p1, app* p2); void extract_conjs(expr_ref_vector const& sub, rule const& rl, expr_ref& result); bool same_body(rule const& r1, rule const& r2) const; void merge_rules(rule_ref& tgt, rule const& src); public: /** \brief Create coalesced rules. */ mk_coalesce(context & ctx); rule_set * operator()(rule_set const & source) override; }; }; #endif /* DL_MK_COALESCE_H_ */ z3-z3-4.8.7/src/muz/transforms/dl_mk_coi_filter.cpp000066400000000000000000000135441356505360400222270ustar00rootroot00000000000000/*++ Copyright (c) 2006-2015 Microsoft Corporation Module Name: dl_mk_coi_filter.cpp Abstract: Rule transformer which removes relations which are out of the cone of influence of output relations Author: Krystof Hoder (t-khoder) Andrey Rybalchenko (rybal) Henning Guenther (t-hennig) --*/ #include "muz/transforms/dl_mk_coi_filter.h" #include "muz/dataflow/dataflow.h" #include "muz/dataflow/reachability.h" #include "ast/ast_pp.h" #include "tactic/generic_model_converter.h" #include "ast/ast_util.h" namespace datalog { rule_set * mk_coi_filter::operator()(rule_set const & source) { scoped_ptr result1 = top_down(source); scoped_ptr result2 = bottom_up(result1 ? *result1 : source); return result2 ? result2.detach() : result1.detach(); } rule_set * mk_coi_filter::bottom_up(rule_set const & source) { dataflow_engine engine(source.get_manager(), source); engine.run_bottom_up(); func_decl_set unreachable; scoped_ptr res = alloc(rule_set, m_context); res->inherit_predicates(source); for (rule* r : source) { bool new_tail = false; bool contained = true; m_new_tail.reset(); m_new_tail_neg.reset(); for (unsigned i = 0; i < r->get_uninterpreted_tail_size(); ++i) { func_decl* decl_i = r->get_decl(i); if (m_context.has_facts(decl_i)) { return nullptr; } bool reachable = engine.get_fact(decl_i).is_reachable(); if (!reachable) { unreachable.insert(decl_i); } if (r->is_neg_tail(i)) { if (!reachable) { if (!new_tail) { for (unsigned j = 0; j < i; ++j) { m_new_tail.push_back(r->get_tail(j)); m_new_tail_neg.push_back(r->is_neg_tail(j)); } new_tail = true; } } else if (new_tail) { m_new_tail.push_back(r->get_tail(i)); m_new_tail_neg.push_back(true); } } else { SASSERT(!new_tail); if (!reachable) { contained = false; break; } } } if (contained) { if (new_tail) { for (unsigned i = r->get_uninterpreted_tail_size(); i < r->get_tail_size(); ++i) { m_new_tail.push_back(r->get_tail(i)); m_new_tail_neg.push_back(false); } rule* new_r = m_context.get_rule_manager().mk(r->get_head(), m_new_tail.size(), m_new_tail.c_ptr(), m_new_tail_neg.c_ptr(), symbol::null, false); res->add_rule(new_r); } else { res->add_rule(r); } } } if (res->get_num_rules() == source.get_num_rules()) { TRACE("dl", tout << "No transformation\n";); res = nullptr; } else { res->close(); } // set to false each unreached predicate if (res && m_context.get_model_converter()) { generic_model_converter* mc0 = alloc(generic_model_converter, m, "dl_coi"); for (auto const& kv : engine) { if (!kv.m_value.is_reachable()) { mc0->add(kv.m_key, m.mk_false()); } } for (func_decl* f : unreachable) { mc0->add(f, m.mk_false()); } m_context.add_model_converter(mc0); } CTRACE("dl", 0 != res, res->display(tout);); return res.detach(); } rule_set * mk_coi_filter::top_down(rule_set const & source) { func_decl_set pruned_preds; dataflow_engine engine(source.get_manager(), source); engine.run_top_down(); scoped_ptr res = alloc(rule_set, m_context); res->inherit_predicates(source); for (rule * r : source) { func_decl * pred = r->get_decl(); if (engine.get_fact(pred).is_reachable()) { res->add_rule(r); } else if (m_context.get_model_converter()) { pruned_preds.insert(pred); } } if (res->get_num_rules() == source.get_num_rules()) { TRACE("dl", tout << "No transformation\n";); res = nullptr; } if (res && m_context.get_model_converter()) { generic_model_converter* mc0 = alloc(generic_model_converter, m, "dl_coi"); for (func_decl* f : pruned_preds) { const rule_vector& rules = source.get_predicate_rules(f); expr_ref_vector fmls(m); for (rule * r : rules) { app* head = r->get_head(); expr_ref_vector conj(m); for (unsigned j = 0; j < head->get_num_args(); ++j) { expr* arg = head->get_arg(j); if (!is_var(arg)) { conj.push_back(m.mk_eq(m.mk_var(j, m.get_sort(arg)), arg)); } } fmls.push_back(mk_and(conj)); } expr_ref fml(m); fml = m.mk_or(fmls.size(), fmls.c_ptr()); mc0->add(f, fml); } m_context.add_model_converter(mc0); } CTRACE("dl", 0 != res, res->display(tout);); return res.detach(); } } z3-z3-4.8.7/src/muz/transforms/dl_mk_coi_filter.h000066400000000000000000000020711356505360400216650ustar00rootroot00000000000000/*++ Copyright (c) 2006-2015 Microsoft Corporation Module Name: dl_mk_coi_filter.h Abstract: Rule transformer which removes relations which are out of the cone of influence of output relations Author: Krystof Hoder (t-khoder) Andrey Rybalchenko (rybal) Henning Guenther (t-hennig) --*/ #ifndef DL_MK_COI_FILTER_H_ #define DL_MK_COI_FILTER_H_ #include "muz/base/dl_rule_transformer.h" #include "muz/base/dl_context.h" namespace datalog { class mk_coi_filter : public rule_transformer::plugin { typedef obj_map decl_map; ast_manager & m; context & m_context; vector m_new_tail; svector m_new_tail_neg; rule_set * bottom_up(rule_set const & source); rule_set * top_down(rule_set const & source); public: mk_coi_filter(context & ctx, unsigned priority = 45000) : plugin(priority), m(ctx.get_manager()), m_context(ctx) {} rule_set * operator()(rule_set const & source) override; }; } #endif z3-z3-4.8.7/src/muz/transforms/dl_mk_different.h000066400000000000000000000013421356505360400215140ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: dl_mk_different_symbolic.h Abstract: Create Horn clauses for different symbolic transformation. Author: Nikolaj Bjorner (nbjorner) 2013-06-19 Revision History: --*/ #ifndef DL_MK_DIFFERENT_SYMBOLIC_H_ #define DL_MK_DIFFERENT_SYMBOLIC_H_ #include "muz/base/dl_rule_transformer.h" namespace datalog { class mk_different_symbolic : public rule_transformer::plugin { ast_manager& m; context& m_ctx; public: mk_different_symbolic(context & ctx, unsigned priority = 33037); ~mk_different_symbolic(); rule_set * operator()(rule_set const & source); }; }; #endif /* DL_MK_DIFFERENT_SYMBOLIC_H_ */ z3-z3-4.8.7/src/muz/transforms/dl_mk_elim_term_ite.cpp000066400000000000000000000131311356505360400227160ustar00rootroot00000000000000/*++ Copyright (c) 2018 Arie Gurfinkel Module Name: dl_mk_elim_term_ite.h Abstract: Remove term-ite expressions from the rules Author: Arie Gurfinkel (agurfinkel) Revision History: --*/ #include "muz/transforms/dl_mk_elim_term_ite.h" #include "tactic/core/blast_term_ite_tactic.h" #include "ast/ast_util.h" #include "ast/expr_abstract.h" #include "tactic/tactic.h" #include "tactic/core/elim_term_ite_tactic.h" namespace { struct uninterp_const_collector { app_ref_vector &m_consts; uninterp_const_collector(app_ref_vector &v) : m_consts(v) {} void operator()(var *v) {} void operator()(quantifier *n) {} void operator()(expr *a){} void operator()(app *a){ if (is_uninterp_const(a)) {m_consts.push_back(a);}; } }; void collect_uninterp_consts(expr *e, app_ref_vector &v) { uninterp_const_collector c(v); quick_for_each_expr(c, e); } struct term_ite_proc { class found{}; ast_manager &m; term_ite_proc(ast_manager &m) : m(m) {} void operator()(var *v) {} void operator()(quantifier *n) {} void operator()(expr *a){} void operator()(app *a){ if (m.is_term_ite(a)) throw found(); } }; bool has_term_ite(expr *e, ast_manager& m) { term_ite_proc f(m); try { quick_for_each_expr(f, e); } catch (const term_ite_proc::found &) { return true; } return false; } bool has_term_ite(expr_ref &e) { return has_term_ite(e, e.m());} } namespace datalog { mk_elim_term_ite::mk_elim_term_ite(context & ctx, unsigned priority) : rule_transformer::plugin(priority, false), m_ctx(ctx), m(ctx.get_manager()), rm(ctx.get_rule_manager()), m_ground(m) {} mk_elim_term_ite::~mk_elim_term_ite() {} expr_ref mk_elim_term_ite::ground(expr_ref &e) { expr_free_vars fv; fv(e); if (m_ground.size() < fv.size()) m_ground.resize(fv.size()); for (unsigned i = 0, sz = fv.size(); i < sz; ++i) { if (fv[i] && !m_ground.get(i)) m_ground[i] = m.mk_fresh_const("c", fv[i]); } var_subst vsub(m, false); return vsub(e, m_ground.size(), m_ground.c_ptr()); } bool mk_elim_term_ite::elim(rule &r, rule_set &new_rules){ m_ground.reset(); th_rewriter rw(m); unsigned utsz = r.get_uninterpreted_tail_size(); unsigned tsz = r.get_tail_size(); expr_ref_vector new_conjs(m); expr_ref tmp(m); for (unsigned i = utsz; i < tsz; ++i) new_conjs.push_back(r.get_tail(i)); flatten_and(new_conjs); expr_ref fml1(m), fml2(m), body(m), head(m); // blast ite body = m.mk_and(new_conjs.size(), new_conjs.c_ptr()); if (!has_term_ite(body)) { TRACE("dl_term_ite", tout << "No term-ite, skipping a rule\n";); new_rules.add_rule(&r); return false; } new_conjs.reset(); blast_term_ite(body, m_ctx.blast_term_ite_inflation()); // simplify body rw(body); if (!has_term_ite(body)) { head = r.get_head(); fml2 = m.mk_implies(body, head); proof_ref p(m); rm.mk_rule(fml2, p, new_rules, r.name()); rm.mk_rule_rewrite_proof(r, *new_rules.last()); TRACE("dl_term_ite", tout << "No term-ite after blast_term_ite\n";); return true; } TRACE("dl_term_ite", tout << "Rule has term-ite after blasting, starting elimination\n";); body = ground(body); // elim ite tactic_ref elim_term_ite = mk_elim_term_ite_tactic(m); goal_ref goal = alloc(class goal, m); goal_ref_buffer result; flatten_and(body, new_conjs); for (auto *e : new_conjs) { goal->assert_expr(e); } unsigned sz = goal->num_exprs(); (*elim_term_ite)(goal, result); if (result.size() == 1) { goal_ref new_goal = result[0]; if (new_goal->num_exprs() != sz) { new_conjs.reset(); new_goal->get_formulas(new_conjs); flatten_and(new_conjs); } } expr_ref_vector conjs(m); for (unsigned i = 0; i < utsz; ++i) { tmp = r.get_tail(i); tmp = ground(tmp); conjs.push_back(tmp); } conjs.append(new_conjs); body = mk_and(conjs); rw(body); head = r.get_head(); head = ground(head); fml2 = m.mk_implies(body, head); SASSERT(!has_term_ite(fml2)); app_ref_vector consts(m); collect_uninterp_consts(fml2, consts); fml2 = mk_forall(m, consts.size(), consts.c_ptr(), fml2); proof_ref p(m); rm.mk_rule(fml2, p, new_rules, r.name()); rm.mk_rule_rewrite_proof(r, *new_rules.last()); TRACE("dl_term_ite", tout << "New rule: " << fml2 << "\n";); return true; } rule_set * mk_elim_term_ite::operator()(rule_set const & source) { if (!m_ctx.elim_term_ite ()) {return nullptr;} rule_set* rules = alloc(rule_set, m_ctx); rules->inherit_predicates(source); bool change = false; for (auto *rule : source) { if (m_ctx.canceled()) { change = false; break; } change |= elim(*rule, *rules); } if (!change) { dealloc(rules); rules = nullptr; } return rules; } } z3-z3-4.8.7/src/muz/transforms/dl_mk_elim_term_ite.h000066400000000000000000000015011356505360400223610ustar00rootroot00000000000000/*++ Copyright (c) 2018 Arie Gurfinkel Module Name: dl_mk_elim_term_ite.h Abstract: Remove term-ite expressions from the rules Author: Arie Gurfinkel (agurfinkel) Revision History: --*/ #pragma once #include "muz/base/dl_context.h" #include "muz/base/dl_rule_set.h" #include "muz/base/dl_rule_transformer.h" #include "tactic/equiv_proof_converter.h" namespace datalog { class mk_elim_term_ite : public rule_transformer::plugin { context &m_ctx; ast_manager &m; rule_manager &rm; expr_ref_vector m_ground; bool elim(rule &r, rule_set &new_rules); expr_ref ground(expr_ref &e); public: mk_elim_term_ite(context &ctx, unsigned priority); ~mk_elim_term_ite() override; rule_set * operator()(const rule_set &source) override; }; } z3-z3-4.8.7/src/muz/transforms/dl_mk_filter_rules.cpp000066400000000000000000000141151356505360400226020ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_mk_filter_rules.cpp Abstract: Author: Leonardo de Moura (leonardo) 2010-05-18. Revision History: --*/ #include "muz/transforms/dl_mk_filter_rules.h" #include "muz/base/dl_context.h" #include "ast/for_each_expr.h" #include "ast/ast_pp.h" namespace datalog { mk_filter_rules::mk_filter_rules(context & ctx): plugin(2000), m_context(ctx), m(ctx.get_manager()), rm(ctx.get_rule_manager()), m_result(nullptr), m_pinned(m) { } mk_filter_rules::~mk_filter_rules() { ptr_vector to_dealloc; filter_cache::iterator it = m_tail2filter.begin(); filter_cache::iterator end = m_tail2filter.end(); for(; it!=end; ++it) { to_dealloc.push_back(it->m_key); } m_tail2filter.reset(); ptr_vector::iterator dit = to_dealloc.begin(); ptr_vector::iterator dend = to_dealloc.end(); for(; dit!=dend; ++dit) { dealloc(*dit); } } /** \brief Return true if \c pred is a cadidate for a "filter" rule. */ bool mk_filter_rules::is_candidate(app * pred) { if (!m_context.is_predicate(pred)) { TRACE("mk_filter_rules", tout << mk_pp(pred, m) << "\nis not a candidate because it is interpreted.\n";); return false; } var_idx_set used_vars; unsigned n = pred->get_num_args(); for (unsigned i = 0; i < n; i++) { expr * arg = pred->get_arg(i); if (m.is_value(arg)) return true; SASSERT(is_var(arg)); unsigned vidx = to_var(arg)->get_idx(); if (used_vars.contains(vidx)) return true; used_vars.insert(vidx); } return false; } /** \brief Create a "filter" (if it doesn't exist already) for the given predicate. */ func_decl * mk_filter_rules::mk_filter_decl(app * pred, var_idx_set const & non_local_vars) { sort_ref_buffer filter_domain(m); filter_key * key = alloc(filter_key, m); mk_new_rule_tail(m, pred, non_local_vars, filter_domain, key->filter_args, key->new_pred); filter_cache::obj_map_entry *entry = m_tail2filter.insert_if_not_there2(key, 0); func_decl*& filter_decl = entry->get_data().m_value; if (!filter_decl) { filter_decl = m_context.mk_fresh_head_predicate(pred->get_decl()->get_name(), symbol("filter"), filter_domain.size(), filter_domain.c_ptr(), pred->get_decl()); m_pinned.push_back(filter_decl); app_ref filter_head(m); filter_head = m.mk_app(filter_decl, key->filter_args.size(), key->filter_args.c_ptr()); app * filter_tail = key->new_pred; rule * filter_rule = m_context.get_rule_manager().mk(filter_head, 1, &filter_tail, (const bool *)nullptr); filter_rule->set_accounting_parent_object(m_context, m_current); m_result->add_rule(filter_rule); m_context.get_rule_manager().mk_rule_asserted_proof(*filter_rule); } else { dealloc(key); } SASSERT(filter_decl != 0); SASSERT(filter_decl->get_arity()==filter_domain.size()); return filter_decl; } void mk_filter_rules::process(rule * r) { m_current = r; app * new_head = r->get_head(); app_ref_vector new_tail(m); svector new_is_negated; unsigned sz = r->get_tail_size(); bool rule_modified = false; for (unsigned i = 0; i < sz; i++) { app * tail = r->get_tail(i); if (is_candidate(tail) && !r->is_neg_tail(i)) { TRACE("mk_filter_rules", tout << "is_candidate: " << mk_pp(tail, m) << "\n";); var_idx_set non_local_vars = rm.collect_rule_vars_ex(r, tail); func_decl * filter_decl = mk_filter_decl(tail, non_local_vars); ptr_buffer new_args; var_idx_set used_vars; unsigned num_args = tail->get_num_args(); for (unsigned i = 0; i < num_args; i++) { expr * arg = tail->get_arg(i); if (is_var(arg)) { unsigned vidx = to_var(arg)->get_idx(); if (non_local_vars.contains(vidx) && !used_vars.contains(vidx)) { new_args.push_back(arg); used_vars.insert(vidx); } } } SASSERT(new_args.size() == filter_decl->get_arity()); new_tail.push_back(m.mk_app(filter_decl, new_args.size(), new_args.c_ptr())); rule_modified = true; } else { new_tail.push_back(tail); } new_is_negated.push_back(r->is_neg_tail(i)); } if (rule_modified) { remove_duplicate_tails(new_tail, new_is_negated); SASSERT(new_tail.size() == new_is_negated.size()); rule * new_rule = m_context.get_rule_manager().mk(new_head, new_tail.size(), new_tail.c_ptr(), new_is_negated.c_ptr(), r->name()); new_rule->set_accounting_parent_object(m_context, m_current); m_result->add_rule(new_rule); m_context.get_rule_manager().mk_rule_rewrite_proof(*r, *new_rule); m_modified = true; } else { m_result->add_rule(r); } } rule_set * mk_filter_rules::operator()(rule_set const & source) { m_tail2filter.reset(); m_result = alloc(rule_set, m_context); m_modified = false; unsigned num_rules = source.get_num_rules(); for (unsigned i = 0; i < num_rules; i++) { process(source.get_rule(i)); } if(!m_modified) { dealloc(m_result); return static_cast(nullptr); } m_result->inherit_predicates(source); return m_result; } }; z3-z3-4.8.7/src/muz/transforms/dl_mk_filter_rules.h000066400000000000000000000046631356505360400222560ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_mk_filter_rules.h Abstract: Author: Leonardo de Moura (leonardo) 2010-05-18. Revision History: --*/ #ifndef DL_MK_FILTER_RULES_H_ #define DL_MK_FILTER_RULES_H_ #include "util/map.h" #include "muz/base/dl_rule_set.h" #include "muz/base/dl_rule_transformer.h" namespace datalog { /** \brief Functor for applying a rule set transformation that creates "filters". A "filter" is a rule of the form: Head(X_1, ..., X_n) :- Tail(...) where X_1,...,X_n are distinct, and Tail contain repeated variables and/or values. After applying this functor only "filter" rules will contain atoms with repeated variables and/or values. */ class mk_filter_rules : public rule_transformer::plugin { struct filter_key { app_ref new_pred; expr_ref_buffer filter_args; filter_key(ast_manager & m) : new_pred(m), filter_args(m) {} unsigned hash() const { unsigned r = new_pred->hash(); for (unsigned i = 0; i < filter_args.size(); ++i) { r ^= filter_args[i]->hash(); } return r; } bool operator==(const filter_key & o) const { return o.new_pred==new_pred && vectors_equal(o.filter_args, filter_args); } }; typedef obj_map filter_cache; context & m_context; ast_manager & m; rule_manager & rm; filter_cache m_tail2filter; rule_set * m_result; rule * m_current; bool m_modified; ast_ref_vector m_pinned; bool is_candidate(app * pred); func_decl * mk_filter_decl(app * pred, var_idx_set const & non_local_vars); void process(rule * r); public: mk_filter_rules(context & ctx); ~mk_filter_rules() override; /** \brief Return a new rule set where only filter rules contain atoms with repeated variables and/or values. */ rule_set * operator()(rule_set const & source) override; }; }; #endif /* DL_MK_FILTER_RULES_H_ */ z3-z3-4.8.7/src/muz/transforms/dl_mk_interp_tail_simplifier.cpp000066400000000000000000000466301356505360400246470ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_mk_interp_tail_simplifier.cpp Abstract: Rule transformer which simplifies interpreted tails Author: Krystof Hoder (t-khoder) 2011-10-01. Revision History: --*/ #include #include "ast/ast_pp.h" #include "ast/rewriter/bool_rewriter.h" #include "ast/rewriter/rewriter.h" #include "ast/rewriter/rewriter_def.h" #include "muz/transforms/dl_mk_rule_inliner.h" #include "muz/transforms/dl_mk_interp_tail_simplifier.h" #include "ast/ast_util.h" #include "muz/base/fp_params.hpp" namespace datalog { // ----------------------------------- // // mk_interp_tail_simplifier::rule_substitution // // ----------------------------------- void mk_interp_tail_simplifier::rule_substitution::reset(rule * r) { unsigned var_cnt = m_context.get_rule_manager().get_counter().get_max_rule_var(*r)+1; m_subst.reset(); m_subst.reserve(1, var_cnt); m_rule = r; } bool mk_interp_tail_simplifier::rule_substitution::unify(expr * e1, expr * e2) { SASSERT(m_rule); //we need to apply the current substitution in order to ensure the unifier //works in an incremental way expr_ref e1_s(m); expr_ref e2_s(m); m_subst.apply(e1,e1_s); m_subst.apply(e2,e2_s); //and we need to reset the cache as we're going to modify the substitution m_subst.reset_cache(); return m_unif (e1_s, e2_s, m_subst, false); } void mk_interp_tail_simplifier::rule_substitution::apply(app * a, app_ref& res) { SASSERT(m_rule); expr_ref res_e(m); m_subst.apply(a, res_e); SASSERT(is_app(res_e.get())); res = to_app(res_e.get()); } void mk_interp_tail_simplifier::rule_substitution::get_result(rule_ref & res) { SASSERT(m_rule); apply(m_rule->get_head(), m_head); m_tail.reset(); m_neg.reset(); unsigned tail_len = m_rule->get_tail_size(); for (unsigned i=0; iget_tail(i), new_tail_el); m_tail.push_back(new_tail_el); m_neg.push_back(m_rule->is_neg_tail(i)); } mk_rule_inliner::remove_duplicate_tails(m_tail, m_neg); SASSERT(m_tail.size() == m_neg.size()); res = m_context.get_rule_manager().mk(m_head, m_tail.size(), m_tail.c_ptr(), m_neg.c_ptr(),m_rule->name()); res->set_accounting_parent_object(m_context, m_rule); res->norm_vars(res.get_manager()); } // ----------------------------------- // // mk_interp_tail_simplifier // // ----------------------------------- class mk_interp_tail_simplifier::normalizer_cfg : public default_rewriter_cfg { struct expr_cmp { ast_manager& m; expr_cmp(ast_manager& m) : m(m) {} bool operator()(expr * ae, expr * be) { return cmp_expr(ae, be, 4) == -1; } template static int cmp(T a, T b) { return (a>b) ? 1 : ((a == b) ? 0 : -1); } int cmp_expr(expr * ae, expr * be, int depth) { if (ae == be) { return 0; } //remove negations bool a_neg = m.is_not(ae, ae); bool b_neg = m.is_not(be, be); if (ae==be) { return cmp(a_neg, b_neg); } if (!is_app(ae) && !is_app(be)) { return cmp(ae->get_id(), be->get_id()); } if (!is_app(ae)) { return -1; } if (!is_app(be)) { return 1; } app * a = to_app(ae); app * b = to_app(be); if (a->get_decl()!=b->get_decl()) { return cmp(a->get_decl()->get_id(), b->get_decl()->get_id()); } if (a->get_num_args()!=b->get_num_args()) { return cmp(a->get_num_args(), b->get_num_args()); } if (depth==0) { return cmp(a->get_id(),b->get_id()); } unsigned arg_cnt = a->get_num_args(); unsigned neg_comparison = 0; for (unsigned i=0; iget_arg(i); expr * arg_b = b->get_arg(i); //we normalize away negations bool a_is_neg = m.is_not(arg_a, arg_a); bool b_is_neg = m.is_not(arg_b, arg_b); if (neg_comparison==0 && a_is_neg!=b_is_neg) { neg_comparison = a_is_neg ? -1 : 1; } int res = cmp_expr(arg_a, arg_b, depth-1); if (res!=0) { return res; } } if (neg_comparison!=0) { return neg_comparison; } //by normalizing away negation we may have put non-equal terms to be equal, so here we check return cmp(a->get_id(),b->get_id()); } }; ast_manager& m; bool_rewriter m_brwr; //instead of a local variable expr_ref_vector m_app_args; expr_cmp m_expr_cmp; public: normalizer_cfg(ast_manager& m) : m(m), m_brwr(m), m_app_args(m), m_expr_cmp(m) { } static void remove_duplicates(expr_ref_vector& v) { expr * a = v[0].get(); unsigned read_idx = 1; unsigned write_idx = 1; for (;;) { while(read_idx arg_pair; bool match_arg_pair(expr * e, arg_pair& pair, bool seek_conjunction) { if (seek_conjunction) { return m.is_and(e, pair.first, pair.second); } else { return m.is_or(e, pair.first, pair.second); } } /** If inside_disjunction is false, we're inside a conjunction (and arg pairs represent disjunctions). */ app * detect_equivalence(const arg_pair& p1, const arg_pair& p2, bool inside_disjunction) { if (m.is_not(p1.first)==m.is_not(p2.first)) { return nullptr; } if (m.is_not(p1.second)==m.is_not(p2.second)) { return nullptr; } expr * first_bare = nullptr; if (m.is_not(p1.first, first_bare) && p2.first!=first_bare) { return nullptr; } if (m.is_not(p2.first, first_bare) && p1.first!=first_bare) { return nullptr; } SASSERT(first_bare); expr * second_bare = nullptr; if (m.is_not(p1.second, second_bare) && p2.second!=second_bare) { return nullptr; } if (m.is_not(p2.second, second_bare) && p1.second!=second_bare) { return nullptr; } SASSERT(second_bare); if (!m.is_bool(first_bare) || !m.is_bool(second_bare)) { return nullptr; } //both negations are in the same pair bool negs_together = m.is_not(p1.first)==m.is_not(p1.second); if (negs_together==inside_disjunction) { return m.mk_eq(first_bare, second_bare); } else { return m.mk_eq(first_bare, m.mk_not(second_bare)); } } bool detect_equivalences(expr_ref_vector& v, bool inside_disjunction) { bool have_pair = false; unsigned prev_pair_idx = 0; arg_pair ap; unsigned read_idx = 0; unsigned write_idx = 0; while(read_idx { public: normalizer_rw(ast_manager& m, normalizer_cfg& cfg): rewriter_tpl(m, false, cfg) {} }; mk_interp_tail_simplifier::mk_interp_tail_simplifier(context & ctx, unsigned priority) : plugin(priority), m(ctx.get_manager()), m_context(ctx), m_simp(ctx.get_rewriter()), a(m), m_rule_subst(ctx), m_tail(m), m_itail_members(m), m_conj(m) { m_cfg = alloc(normalizer_cfg, m); m_rw = alloc(normalizer_rw, m, *m_cfg); } mk_interp_tail_simplifier::~mk_interp_tail_simplifier() { dealloc(m_rw); dealloc(m_cfg); } void mk_interp_tail_simplifier::simplify_expr(app * a, expr_ref& res) { expr_ref simp1_res(m); m_simp(a, simp1_res); (*m_rw)(simp1_res.get(), res); m_simp(res.get(), res); } bool mk_interp_tail_simplifier::propagate_variable_equivalences(rule * r, rule_ref& res) { if (!m_context.get_params ().xform_tail_simplifier_pve ()) return false; unsigned u_len = r->get_uninterpreted_tail_size(); unsigned len = r->get_tail_size(); if (u_len == len) { return false; } m_todo.reset(); m_leqs.reset(); for (unsigned i = u_len; i < len; i++) { m_todo.push_back(r->get_tail(i)); SASSERT(!r->is_neg_tail(i)); } m_rule_subst.reset(r); expr_ref_vector trail(m); expr_ref tmp1(m), tmp2(m); bool found_something = false; #define TRY_UNIFY(_x,_y) if (m_rule_subst.unify(_x,_y)) { found_something = true; } #define IS_FLEX(_x) (is_var(_x) || m.is_value(_x)) while (!m_todo.empty()) { expr * arg1, *arg2; expr * t0 = m_todo.back(); m_todo.pop_back(); expr* t = t0; bool neg = m.is_not(t, t); if (is_var(t)) { TRY_UNIFY(t, neg ? m.mk_false() : m.mk_true()); } else if (!neg && m.is_and(t)) { app* a = to_app(t); m_todo.append(a->get_num_args(), a->get_args()); } else if (!neg && m.is_eq(t, arg1, arg2) && IS_FLEX(arg1) && IS_FLEX(arg2)) { TRY_UNIFY(arg1, arg2); } else if (m.is_iff(t, arg1, arg2)) { //determine the polarity of the equivalence and remove the negations while (m.is_not(arg1, arg1)) neg = !neg; while (m.is_not(arg2, arg2)) neg = !neg; if (!is_var(arg1)) { std::swap(arg1, arg2); } if (!IS_FLEX(arg1) || !IS_FLEX(arg2)) { // no-op } else if (is_var(arg1) && !neg) { TRY_UNIFY(arg1, arg2); } else if (is_var(arg1) && neg && m.is_true(arg2)) { TRY_UNIFY(arg1, m.mk_false()); } else if (is_var(arg1) && neg && m.is_false(arg2)) { TRY_UNIFY(arg1, m.mk_true()); } } else if (!neg && (a.is_le(t, arg1, arg2) || a.is_ge(t, arg2, arg1))) { tmp1 = a.mk_sub(arg1, arg2); tmp2 = a.mk_sub(arg2, arg1); if (false && m_leqs.contains(tmp2) && IS_FLEX(arg1) && IS_FLEX(arg2)) { TRY_UNIFY(arg1, arg2); } else { trail.push_back(tmp1); m_leqs.insert(tmp1); } } } if (!found_something) { return false; } TRACE("dl_interp_tail_simplifier_propagation_pre", tout << "will propagate rule:\n"; r->display(m_context, tout); ); m_rule_subst.get_result(res); TRACE("dl_interp_tail_simplifier_propagation", tout << "propagated equivalences of:\n"; r->display(m_context, tout); tout << "into:\n"; res->display(m_context, tout); ); return true; } bool mk_interp_tail_simplifier::transform_rule(rule * r0, rule_ref & res) { rule_manager& rm = m_context.get_rule_manager(); rule_ref r(r0, rm); if (rm.has_quantifiers(*r)) { res = r; return true; } start: unsigned u_len = r->get_uninterpreted_tail_size(); unsigned len = r->get_tail_size(); if (u_len == len) { res = r; return true; } app_ref head(r->get_head(), m); m_tail.reset(); m_tail_neg.reset(); for (unsigned i=0; iget_tail(i)); m_tail_neg.push_back(r->is_neg_tail(i)); } bool modified = false; app_ref itail(m); if (u_len+1==len) { //we have only one interpreted tail itail = r->get_tail(u_len); SASSERT(!r->is_neg_tail(u_len)); } else { m_itail_members.reset(); for (unsigned i=u_len; iget_tail(i)); SASSERT(!r->is_neg_tail(i)); } itail = m.mk_and(m_itail_members.size(), m_itail_members.c_ptr()); modified = true; } expr_ref simp_res(m); simplify_expr(itail.get(), simp_res); modified |= itail.get() != simp_res.get(); if (m.is_false(simp_res)) { TRACE("dl", r->display(m_context, tout << "rule is infeasible\n");); return false; } SASSERT(m.is_bool(simp_res)); if (modified) { m_conj.reset(); flatten_and(simp_res, m_conj); for (unsigned i = 0; i < m_conj.size(); ++i) { expr* e = m_conj[i].get(); if (is_app(e)) { m_tail.push_back(to_app(e)); } else { m_tail.push_back(m.mk_eq(e, m.mk_true())); } m_tail_neg.push_back(false); } SASSERT(m_tail.size() == m_tail_neg.size()); res = m_context.get_rule_manager().mk(head, m_tail.size(), m_tail.c_ptr(), m_tail_neg.c_ptr(), r->name()); res->set_accounting_parent_object(m_context, r); } else { res = r; } rule_ref pro_var_eq_result(m_context.get_rule_manager()); if (propagate_variable_equivalences(res, pro_var_eq_result)) { SASSERT(rule_counter().get_max_rule_var(*r.get())==0 || rule_counter().get_max_rule_var(*r.get()) > rule_counter().get_max_rule_var(*pro_var_eq_result.get())); r = pro_var_eq_result; goto start; } CTRACE("dl", (res != r0), r0->display(m_context, tout << "old:\n"); res->display(m_context, tout << "new:\n");); return true; } bool mk_interp_tail_simplifier::transform_rules(const rule_set & orig, rule_set & tgt) { bool modified = false; rule_manager& rm = m_context.get_rule_manager(); rule_set::iterator rit = orig.begin(); rule_set::iterator rend = orig.end(); for (; rit!=rend; ++rit) { rule_ref new_rule(rm); if (transform_rule(*rit, new_rule)) { rm.mk_rule_rewrite_proof(**rit, *new_rule.get()); bool is_modified = *rit != new_rule; modified |= is_modified; tgt.add_rule(new_rule); } else { modified = true; } } return modified; } rule_set * mk_interp_tail_simplifier::operator()(rule_set const & source) { if (source.get_num_rules() == 0) { return nullptr; } rule_set * res = alloc(rule_set, m_context); if (transform_rules(source, *res)) { res->inherit_predicates(source); TRACE("dl", source.display(tout); res->display(tout);); } else { dealloc(res); res = nullptr; } return res; } }; z3-z3-4.8.7/src/muz/transforms/dl_mk_interp_tail_simplifier.h000066400000000000000000000060511356505360400243050ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_mk_interp_tail_simplifier.h Abstract: Rule transformer which simplifies interpreted tails Author: Krystof Hoder (t-khoder) 2011-10-01. Revision History: --*/ #ifndef DL_MK_INTERP_TAIL_SIMPLIFIER_H_ #define DL_MK_INTERP_TAIL_SIMPLIFIER_H_ #include "muz/base/dl_context.h" #include "muz/base/dl_rule_transformer.h" #include "ast/substitution/unifier.h" #include "ast/substitution/substitution.h" #include "ast/arith_decl_plugin.h" namespace datalog { class mk_interp_tail_simplifier : public rule_transformer::plugin { class rule_substitution { ast_manager& m; context& m_context; substitution m_subst; unifier m_unif; app_ref m_head; app_ref_vector m_tail; svector m_neg; rule * m_rule; void apply(app * a, app_ref& res); public: rule_substitution(context & ctx) : m(ctx.get_manager()), m_context(ctx), m_subst(m), m_unif(m), m_head(m), m_tail(m), m_rule(nullptr) {} /** Reset substitution and get it ready for working with rule r. As long as this object is used without a reset, the rule r must exist. */ void reset(rule * r); /** Reset substitution and unify tail tgt_idx of the target rule and the head of the src rule */ bool unify(expr * e1, expr * e2); void get_result(rule_ref & res); void display(std::ostream& stm) { m_subst.display(stm); } }; class normalizer_cfg; class normalizer_rw; ast_manager & m; context & m_context; th_rewriter & m_simp; arith_util a; rule_substitution m_rule_subst; ptr_vector m_todo; obj_hashtable m_leqs; app_ref_vector m_tail; expr_ref_vector m_itail_members; expr_ref_vector m_conj; svector m_tail_neg; normalizer_cfg* m_cfg; normalizer_rw* m_rw; void simplify_expr(app * a, expr_ref& res); /** return true if some propagation was done */ bool propagate_variable_equivalences(rule * r, rule_ref& res); /** Return true if something was modified */ bool transform_rules(const rule_set & orig, rule_set & tgt); public: mk_interp_tail_simplifier(context & ctx, unsigned priority=40000); ~mk_interp_tail_simplifier() override; /**If rule should be retained, assign transformed version to res and return true; if rule can be deleted, return false. This method is kind of useful, so it's public to allow other rules to use it, e.g. on their intermediate results. */ bool transform_rule(rule * r, rule_ref& res); rule_set * operator()(rule_set const & source) override; }; }; #endif /* DL_MK_INTERP_TAIL_SIMPLIFIER_H_ */ z3-z3-4.8.7/src/muz/transforms/dl_mk_karr_invariants.cpp000066400000000000000000000253711356505360400233060ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: dl_mk_karr_invariants.cpp Abstract: Extract integer linear invariants. The linear invariants are extracted according to Karr's method. A short description is in Nikolaj Bjorner, Anca Browne and Zohar Manna. Automatic Generation of Invariants and Intermediate Assertions, in CP 95. The algorithm is here adapted to Horn clauses. The idea is to maintain two data-structures for each recursive relation. We call them R and RD - R - set of linear congruences that are true of R. - RD - the dual basis of of solutions for R. RD is updated by accumulating basis vectors for solutions to R (the homogeneous dual of R) R is updated from the inhomogeneous dual of RD. Author: Nikolaj Bjorner (nbjorner) 2013-03-09 Revision History: --*/ #include "ast/rewriter/expr_safe_replace.h" #include "ast/rewriter/bool_rewriter.h" #include "ast/for_each_expr.h" #include "muz/transforms/dl_mk_karr_invariants.h" #include "muz/transforms/dl_mk_backwards.h" #include "muz/transforms/dl_mk_loop_counter.h" namespace datalog { mk_karr_invariants::mk_karr_invariants(context & ctx, unsigned priority): rule_transformer::plugin(priority, false), m_ctx(ctx), m(ctx.get_manager()), rm(ctx.get_rule_manager()), m_inner_ctx(m, ctx.get_register_engine(), ctx.get_fparams()), a(m), m_pinned(m) { params_ref params; params.set_sym("default_relation", symbol("karr_relation")); params.set_sym("engine", symbol("datalog")); params.set_bool("karr", false); m_inner_ctx.updt_params(params); } mk_karr_invariants::~mk_karr_invariants() { } matrix& matrix::operator=(matrix const& other) { reset(); append(other); return *this; } void matrix::display_row( std::ostream& out, vector const& row, rational const& b, bool is_eq) { for (unsigned j = 0; j < row.size(); ++j) { out << row[j] << " "; } out << (is_eq?" = ":" >= ") << -b << "\n"; } void matrix::display_ineq( std::ostream& out, vector const& row, rational const& b, bool is_eq) { bool first = true; for (unsigned j = 0; j < row.size(); ++j) { if (!row[j].is_zero()) { if (!first && row[j].is_pos()) { out << "+ "; } if (row[j].is_minus_one()) { out << "- "; } if (row[j] > rational(1) || row[j] < rational(-1)) { out << row[j] << "*"; } out << "x" << j << " "; first = false; } } out << (is_eq?"= ":">= ") << -b << "\n"; } void matrix::display(std::ostream& out) const { for (unsigned i = 0; i < A.size(); ++i) { display_row(out, A[i], b[i], eq[i]); } } class mk_karr_invariants::add_invariant_model_converter : public model_converter { ast_manager& m; arith_util a; func_decl_ref_vector m_funcs; expr_ref_vector m_invs; public: add_invariant_model_converter(ast_manager& m): m(m), a(m), m_funcs(m), m_invs(m) {} ~add_invariant_model_converter() override { } void add(func_decl* p, expr* inv) { if (!m.is_true(inv)) { m_funcs.push_back(p); m_invs.push_back(inv); } } void get_units(obj_map& units) override {} void operator()(model_ref & mr) override { for (unsigned i = 0; i < m_funcs.size(); ++i) { func_decl* p = m_funcs[i].get(); func_interp* f = mr->get_func_interp(p); expr_ref body(m); unsigned arity = p->get_arity(); SASSERT(0 < arity); if (f) { SASSERT(f->num_entries() == 0); if (!f->is_partial()) { bool_rewriter(m).mk_and(f->get_else(), m_invs[i].get(), body); } } else { f = alloc(func_interp, m, arity); mr->register_decl(p, f); body = m.mk_false(); // fragile: assume that relation was pruned by being infeasible. } f->set_else(body); } } model_converter * translate(ast_translation & translator) override { add_invariant_model_converter* mc = alloc(add_invariant_model_converter, m); for (unsigned i = 0; i < m_funcs.size(); ++i) { mc->add(translator(m_funcs[i].get()), m_invs[i].get()); } return mc; } void display(std::ostream& out) override { out << "(add-invariant-model-converter)\n"; } private: void mk_body(matrix const& M, expr_ref& body) { expr_ref_vector conj(m); for (unsigned i = 0; i < M.size(); ++i) { mk_body(M.A[i], M.b[i], M.eq[i], conj); } bool_rewriter(m).mk_and(conj.size(), conj.c_ptr(), body); } void mk_body(vector const& row, rational const& b, bool is_eq, expr_ref_vector& conj) { expr_ref_vector sum(m); expr_ref zero(m), lhs(m); zero = a.mk_numeral(rational(0), true); for (unsigned i = 0; i < row.size(); ++i) { if (row[i].is_zero()) { continue; } var* var = m.mk_var(i, a.mk_int()); if (row[i].is_one()) { sum.push_back(var); } else { sum.push_back(a.mk_mul(a.mk_numeral(row[i], true), var)); } } if (!b.is_zero()) { sum.push_back(a.mk_numeral(b, true)); } lhs = a.mk_add(sum.size(), sum.c_ptr()); if (is_eq) { conj.push_back(m.mk_eq(lhs, zero)); } else { conj.push_back(a.mk_ge(lhs, zero)); } } }; rule_set * mk_karr_invariants::operator()(rule_set const & source) { if (!m_ctx.karr()) { return nullptr; } rule_set::iterator it = source.begin(), end = source.end(); for (; it != end; ++it) { rule const& r = **it; if (r.has_negation()) { return nullptr; } } mk_loop_counter lc(m_ctx); mk_backwards bwd(m_ctx); scoped_ptr src_loop = lc(source); TRACE("dl", src_loop->display(tout << "source loop\n");); get_invariants(*src_loop); if (m.canceled()) { return nullptr; } // figure out whether to update same rules as used for saturation. scoped_ptr rev_source = bwd(*src_loop); get_invariants(*rev_source); scoped_ptr src_annot = update_rules(*src_loop); rule_set* rules = lc.revert(*src_annot); rules->inherit_predicates(source); TRACE("dl", rules->display(tout);); m_pinned.reset(); m_fun2inv.reset(); return rules; } void mk_karr_invariants::get_invariants(rule_set const& src) { m_inner_ctx.reset(); rel_context_base& rctx = *m_inner_ctx.get_rel_context(); ptr_vector heads; func_decl_set const& predicates = m_ctx.get_predicates(); for (func_decl_set::iterator fit = predicates.begin(); fit != predicates.end(); ++fit) { m_inner_ctx.register_predicate(*fit, false); } m_inner_ctx.ensure_opened(); m_inner_ctx.replace_rules(src); m_inner_ctx.close(); rule_set::decl2rules::iterator dit = src.begin_grouped_rules(); rule_set::decl2rules::iterator dend = src.end_grouped_rules(); for (; dit != dend; ++dit) { heads.push_back(dit->m_key); } m_inner_ctx.rel_query(heads.size(), heads.c_ptr()); // retrieve invariants. dit = src.begin_grouped_rules(); for (; dit != dend; ++dit) { func_decl* p = dit->m_key; expr_ref fml = rctx.try_get_formula(p); if (fml && !m.is_true(fml)) { expr* inv = nullptr; if (m_fun2inv.find(p, inv)) { fml = m.mk_and(inv, fml); } m_pinned.push_back(fml); m_fun2inv.insert(p, fml); } } } rule_set* mk_karr_invariants::update_rules(rule_set const& src) { scoped_ptr dst = alloc(rule_set, m_ctx); rule_set::iterator it = src.begin(), end = src.end(); for (; it != end; ++it) { update_body(*dst, **it); } if (m_ctx.get_model_converter()) { add_invariant_model_converter* kmc = alloc(add_invariant_model_converter, m); rule_set::decl2rules::iterator git = src.begin_grouped_rules(); rule_set::decl2rules::iterator gend = src.end_grouped_rules(); for (; git != gend; ++git) { func_decl* p = git->m_key; expr* fml = nullptr; if (m_fun2inv.find(p, fml)) { kmc->add(p, fml); } } m_ctx.add_model_converter(kmc); } dst->inherit_predicates(src); return dst.detach(); } void mk_karr_invariants::update_body(rule_set& rules, rule& r) { unsigned utsz = r.get_uninterpreted_tail_size(); unsigned tsz = r.get_tail_size(); app_ref_vector tail(m); expr_ref fml(m); for (unsigned i = 0; i < tsz; ++i) { tail.push_back(r.get_tail(i)); } for (unsigned i = 0; i < utsz; ++i) { func_decl* q = r.get_decl(i); expr* fml = nullptr; if (m_fun2inv.find(q, fml)) { expr_safe_replace rep(m); for (unsigned j = 0; j < q->get_arity(); ++j) { rep.insert(m.mk_var(j, q->get_domain(j)), r.get_tail(i)->get_arg(j)); } expr_ref tmp(fml, m); rep(tmp); tail.push_back(to_app(tmp)); } } rule* new_rule = &r; if (tail.size() != tsz) { new_rule = rm.mk(r.get_head(), tail.size(), tail.c_ptr(), nullptr, r.name()); } rules.add_rule(new_rule); rm.mk_rule_rewrite_proof(r, *new_rule); // should be weakening rule. } }; z3-z3-4.8.7/src/muz/transforms/dl_mk_karr_invariants.h000066400000000000000000000037571356505360400227570ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: dl_mk_karr_invariants.h Abstract: Extract integer linear invariants. Author: Nikolaj Bjorner (nbjorner) 2013-03-08 Revision History: --*/ #ifndef DL_MK_KARR_INVARIANTS_H_ #define DL_MK_KARR_INVARIANTS_H_ #include "muz/base/dl_context.h" #include "muz/base/dl_rule_set.h" #include "muz/base/dl_rule_transformer.h" #include "ast/arith_decl_plugin.h" #include "math/hilbert/hilbert_basis.h" namespace datalog { /** \brief Rule transformer that strengthens bodies with invariants. */ struct matrix { vector > A; vector b; svector eq; unsigned size() const { return A.size(); } void reset() { A.reset(); b.reset(); eq.reset(); } matrix& operator=(matrix const& other); void append(matrix const& other) { A.append(other.A); b.append(other.b); eq.append(other.eq); } void display(std::ostream& out) const; static void display_row( std::ostream& out, vector const& row, rational const& b, bool is_eq); static void display_ineq( std::ostream& out, vector const& row, rational const& b, bool is_eq); }; class mk_karr_invariants : public rule_transformer::plugin { class add_invariant_model_converter; context& m_ctx; ast_manager& m; rule_manager& rm; context m_inner_ctx; arith_util a; obj_map m_fun2inv; ast_ref_vector m_pinned; void get_invariants(rule_set const& src); void update_body(rule_set& result, rule& r); rule_set* update_rules(rule_set const& src); public: mk_karr_invariants(context & ctx, unsigned priority); ~mk_karr_invariants() override; rule_set * operator()(rule_set const & source) override; }; }; #endif /* DL_MK_KARR_INVARIANTS_H_ */ z3-z3-4.8.7/src/muz/transforms/dl_mk_loop_counter.cpp000066400000000000000000000130711356505360400226130ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: dl_mk_loop_counter.cpp Abstract: Add loop counter argument to relations. Author: Nikolaj Bjorner (nbjorner) 2013-03-31 Revision History: --*/ #include "muz/transforms/dl_mk_loop_counter.h" #include "muz/base/dl_context.h" namespace datalog { mk_loop_counter::mk_loop_counter(context & ctx, unsigned priority): plugin(priority), m(ctx.get_manager()), m_ctx(ctx), a(m), m_refs(m) { } mk_loop_counter::~mk_loop_counter() { } app_ref mk_loop_counter::add_arg(rule_set const& src, rule_set& dst, app* fn, unsigned idx) { expr_ref_vector args(m); func_decl* new_fn, *old_fn = fn->get_decl(); args.append(fn->get_num_args(), fn->get_args()); args.push_back(m.mk_var(idx, a.mk_int())); if (!m_old2new.find(old_fn, new_fn)) { ptr_vector domain; domain.append(fn->get_num_args(), old_fn->get_domain()); domain.push_back(a.mk_int()); new_fn = m.mk_func_decl(old_fn->get_name(), domain.size(), domain.c_ptr(), old_fn->get_range()); m_old2new.insert(old_fn, new_fn); m_new2old.insert(new_fn, old_fn); m_refs.push_back(new_fn); m_ctx.register_predicate(new_fn, false); if (src.is_output_predicate(old_fn)) { dst.set_output_predicate(new_fn); } } return app_ref(m.mk_app(new_fn, args.size(), args.c_ptr()), m); } app_ref mk_loop_counter::del_arg(app* fn) { expr_ref_vector args(m); func_decl* old_fn = nullptr, *new_fn = fn->get_decl(); SASSERT(fn->get_num_args() > 0); args.append(fn->get_num_args()-1, fn->get_args()); VERIFY (m_new2old.find(new_fn, old_fn)); return app_ref(m.mk_app(old_fn, args.size(), args.c_ptr()), m); } rule_set * mk_loop_counter::operator()(rule_set const & source) { m_refs.reset(); m_old2new.reset(); m_new2old.reset(); rule_manager& rm = source.get_rule_manager(); rule_set * result = alloc(rule_set, m_ctx); unsigned sz = source.get_num_rules(); rule_ref new_rule(rm); app_ref_vector tail(m); app_ref head(m); svector neg; rule_counter& vc = rm.get_counter(); for (unsigned i = 0; i < sz; ++i) { tail.reset(); neg.reset(); rule & r = *source.get_rule(i); unsigned cnt = vc.get_max_rule_var(r)+1; unsigned utsz = r.get_uninterpreted_tail_size(); unsigned tsz = r.get_tail_size(); for (unsigned j = 0; j < utsz; ++j, ++cnt) { tail.push_back(add_arg(source, *result, r.get_tail(j), cnt)); neg.push_back(r.is_neg_tail(j)); } for (unsigned j = utsz; j < tsz; ++j) { tail.push_back(r.get_tail(j)); neg.push_back(false); } head = add_arg(source, *result, r.get_head(), cnt); // set the loop counter to be an increment of the previous bool found = false; unsigned last = head->get_num_args()-1; for (unsigned j = 0; !found && j < utsz; ++j) { if (head->get_decl() == tail[j]->get_decl()) { tail.push_back(m.mk_eq(head->get_arg(last), a.mk_add(tail[j]->get_arg(last), a.mk_numeral(rational(1), true)))); neg.push_back(false); found = true; } } // initialize loop counter to 0 if none was found. if (!found) { expr_ref_vector args(m); args.append(head->get_num_args(), head->get_args()); args[last] = a.mk_numeral(rational(0), true); head = m.mk_app(head->get_decl(), args.size(), args.c_ptr()); } new_rule = rm.mk(head, tail.size(), tail.c_ptr(), neg.c_ptr(), r.name(), true); result->add_rule(new_rule); } // model converter: remove references to extra argument. // proof converter: remove references to extra argument as well. return result; } rule_set * mk_loop_counter::revert(rule_set const & source) { context& ctx = source.get_context(); rule_manager& rm = source.get_rule_manager(); rule_set * result = alloc(rule_set, ctx); unsigned sz = source.get_num_rules(); rule_ref new_rule(rm); app_ref_vector tail(m); app_ref head(m); svector neg; for (unsigned i = 0; i < sz; ++i) { tail.reset(); neg.reset(); rule & r = *source.get_rule(i); unsigned utsz = r.get_uninterpreted_tail_size(); unsigned tsz = r.get_tail_size(); for (unsigned j = 0; j < utsz; ++j) { tail.push_back(del_arg(r.get_tail(j))); neg.push_back(r.is_neg_tail(j)); } for (unsigned j = utsz; j < tsz; ++j) { tail.push_back(r.get_tail(j)); neg.push_back(false); } head = del_arg(r.get_head()); new_rule = rm.mk(head, tail.size(), tail.c_ptr(), neg.c_ptr(), r.name(), true); result->add_rule(new_rule); } // model converter: ... // proof converter: ... return result; } }; z3-z3-4.8.7/src/muz/transforms/dl_mk_loop_counter.h000066400000000000000000000022051356505360400222550ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: dl_mk_loop_counter.h Abstract: Add loop counter argument to relations. Author: Nikolaj Bjorner (nbjorner) 2013-03-31 Revision History: --*/ #ifndef DL_MK_LOOP_COUNTER_H_ #define DL_MK_LOOP_COUNTER_H_ #include "muz/base/dl_rule_transformer.h" #include "ast/arith_decl_plugin.h" namespace datalog { class mk_loop_counter : public rule_transformer::plugin { ast_manager& m; context& m_ctx; arith_util a; func_decl_ref_vector m_refs; obj_map m_new2old; obj_map m_old2new; app_ref add_arg(rule_set const& src, rule_set& dst, app* fn, unsigned idx); app_ref del_arg(app* fn); public: mk_loop_counter(context & ctx, unsigned priority = 33000); ~mk_loop_counter() override; rule_set * operator()(rule_set const & source) override; func_decl* get_old(func_decl* f) const { return m_new2old.find(f); } rule_set * revert(rule_set const& source); }; }; #endif /* DL_MK_LOOP_COUNTER_H_ */ z3-z3-4.8.7/src/muz/transforms/dl_mk_magic_sets.cpp000066400000000000000000000332451356505360400222260ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_mk_magic_sets.cpp Abstract: Author: Krystof Hoder (t-khoder) 2010-10-04. Revision History: --*/ #include #include #include "ast/ast_pp.h" #include "muz/transforms/dl_mk_magic_sets.h" namespace datalog { mk_magic_sets::mk_magic_sets(context & ctx, func_decl* goal) : plugin(10000, true), m_context(ctx), m(ctx.get_manager()), rm(ctx.get_rule_manager()), m_pinned(m), m_goal(goal, m) { } void mk_magic_sets::reset() { m_extentional.reset(); m_todo.reset(); m_adorned_preds.reset(); m_adornments.reset(); m_magic_preds.reset(); m_pinned.reset(); } void mk_magic_sets::adornment::populate(app * lit, const var_idx_set & bound_vars) { SASSERT(empty()); unsigned arity = lit->get_num_args(); for (unsigned i = 0; i < arity; i++) { const expr * arg = lit->get_arg(i); bool bound = !is_var(arg) || bound_vars.contains(to_var(arg)->get_idx()); push_back(bound ? AD_BOUND : AD_FREE); } } std::string mk_magic_sets::adornment::to_string() const { std::string res; const_iterator eit = begin(); const_iterator eend = end(); for (; eit != eend; ++eit) { res += (*eit == AD_BOUND)?'b':'f'; } return res; } unsigned get_bound_arg_count(app * lit, const var_idx_set & bound_vars) { unsigned res = 0; unsigned n = lit->get_num_args(); for (unsigned i = 0; i < n; i++) { const expr * arg = lit->get_arg(i); if (!is_var(arg) || bound_vars.contains(to_var(arg)->get_idx())) { SASSERT(is_var(arg) || is_app(arg)); SASSERT(!is_app(arg) || to_app(arg)->get_num_args()==0); res++; } } return res; } float mk_magic_sets::get_unbound_cost(app * lit, const var_idx_set & bound_vars) { func_decl * pred = lit->get_decl(); float res = 1; unsigned n = lit->get_num_args(); for (unsigned i = 0; i < n; i++) { const expr * arg = lit->get_arg(i); if (is_var(arg) && !bound_vars.contains(to_var(arg)->get_idx())) { res *= m_context.get_sort_size_estimate(pred->get_domain(i)); } //res-=1; } return res; } /** \brief From \c cont which is list of indexes of tail literals of rule \c r, select the index pointing to a literal with at least one bound variable that will be the next bound literal in the process of creating an adorned rule. If all literals are unbound, return -1. */ int mk_magic_sets::pop_bound(unsigned_vector & cont, rule * r, const var_idx_set & bound_vars) { float best_cost; int candidate_index = -1; unsigned n = cont.size(); for (unsigned i=0; iget_tail(cont[i]); unsigned bound_cnt = get_bound_arg_count(lit, bound_vars); if (bound_cnt==0) { continue; } float cost = get_unbound_cost(lit, bound_vars); if (candidate_index==-1 || cost(n-1)) { std::swap(cont[candidate_index], cont[n-1]); } unsigned res = cont.back(); cont.pop_back(); return res; } app * mk_magic_sets::adorn_literal(app * lit, const var_idx_set & bound_vars) { SASSERT(!m_extentional.contains(lit->get_decl())); func_decl * old_pred = lit->get_decl(); SASSERT(m.is_bool(old_pred->get_range())); adornment_desc adn(old_pred); adn.m_adornment.populate(lit, bound_vars); adornment_map::entry * e = m_adorned_preds.insert_if_not_there2(adn, nullptr); func_decl * new_pred = e->get_data().m_value; if (new_pred==nullptr) { std::string suffix = "ad_"+adn.m_adornment.to_string(); new_pred = m_context.mk_fresh_head_predicate( old_pred->get_name(), symbol(suffix.c_str()), old_pred->get_arity(), old_pred->get_domain(), old_pred); m_pinned.push_back(new_pred); e->get_data().m_value = new_pred; m_todo.push_back(adn); m_adornments.insert(new_pred, adn.m_adornment); } app * res = m.mk_app(new_pred, lit->get_args()); m_pinned.push_back(res); return res; } app * mk_magic_sets::create_magic_literal(app * l) { func_decl * l_pred = l->get_decl(); SASSERT(m.is_bool(l_pred->get_range())); pred_adornment_map::obj_map_entry * ae = m_adornments.find_core(l_pred); SASSERT(ae); const adornment & adn = ae->get_data().m_value; unsigned l_arity = l->get_num_args(); ptr_vector bound_args; for (unsigned i=0; iget_arg(i)); } } pred2pred::obj_map_entry * e = m_magic_preds.insert_if_not_there2(l_pred, 0); func_decl * mag_pred = e->get_data().m_value; if (mag_pred==nullptr) { unsigned mag_arity = bound_args.size(); ptr_vector mag_domain; for (unsigned i=0; iget_domain(i)); } } mag_pred = m_context.mk_fresh_head_predicate(l_pred->get_name(), symbol("ms"), mag_arity, mag_domain.c_ptr(), l_pred); m_pinned.push_back(mag_pred); e->get_data().m_value = mag_pred; } app * res = m.mk_app(mag_pred, bound_args.c_ptr()); m_pinned.push_back(res); return res; } void mk_magic_sets::create_magic_rules(app * head, unsigned tail_cnt, app * const * tail, bool const* negated, rule_set& result) { //TODO: maybe include relevant interpreted predicates from the original rule ptr_vector new_tail; svector negations; new_tail.push_back(create_magic_literal(head)); new_tail.append(tail_cnt, tail); negations.push_back(false); negations.append(tail_cnt, negated); for (unsigned i=0; iget_decl())) { continue; } app * mag_head = create_magic_literal(tail[i]); rule * r = m_context.get_rule_manager().mk(mag_head, i+1, new_tail.c_ptr(), negations.c_ptr()); TRACE("dl", r->display(m_context,tout); ); result.add_rule(r); } } void mk_magic_sets::transform_rule(const adornment & head_adornment, rule * r, rule_set& result) { app * head = r->get_head(); unsigned head_len = head->get_num_args(); SASSERT(head_len==head_adornment.size()); var_idx_set bound_vars; for (unsigned i=0; iget_arg(i); if (head_adornment[i]==AD_BOUND && is_var(arg)) { bound_vars.insert(to_var(arg)->get_idx()); } } unsigned processed_tail_len = r->get_uninterpreted_tail_size(); unsigned_vector exten_tails; unsigned_vector inten_tails; for (unsigned i=0; iget_tail(i); if (m_extentional.contains(t->get_decl())) { exten_tails.push_back(i); } else { inten_tails.push_back(i); } } ptr_vector new_tail; svector negations; while (new_tail.size()!=processed_tail_len) { bool intentional = false; int curr_index = pop_bound(exten_tails, r, bound_vars); if (curr_index==-1) { curr_index = pop_bound(inten_tails, r,bound_vars); if (curr_index!=-1) { intentional = true; } } if (curr_index==-1) { if (!exten_tails.empty()) { curr_index = exten_tails.back(); exten_tails.pop_back(); } else { SASSERT(!inten_tails.empty()); curr_index = inten_tails.back(); inten_tails.pop_back(); intentional = true; } } SASSERT(curr_index!=-1); app * curr = r->get_tail(curr_index); if (intentional) { curr = adorn_literal(curr, bound_vars); } new_tail.push_back(curr); negations.push_back(r->is_neg_tail(curr_index)); bound_vars |= rm.collect_vars(curr); } func_decl * new_head_pred = nullptr; VERIFY( m_adorned_preds.find(adornment_desc(head->get_decl(), head_adornment), new_head_pred) ); app * new_head = m.mk_app(new_head_pred, head->get_args()); SASSERT(new_tail.size()==r->get_uninterpreted_tail_size()); create_magic_rules(new_head, new_tail.size(), new_tail.c_ptr(), negations.c_ptr(), result); unsigned tail_len = r->get_tail_size(); for (unsigned i=processed_tail_len; iget_tail(i)); negations.push_back(r->is_neg_tail(i)); } new_tail.push_back(create_magic_literal(new_head)); negations.push_back(false); rule * nr = m_context.get_rule_manager().mk(new_head, new_tail.size(), new_tail.c_ptr(), negations.c_ptr(), r->name()); result.add_rule(nr); nr->set_accounting_parent_object(m_context, r); } void mk_magic_sets::create_transfer_rule(const adornment_desc & d, rule_set& result) { func_decl * adn_pred = m_adorned_preds.find(d); unsigned arity = adn_pred->get_arity(); SASSERT(arity == d.m_pred->get_arity()); ptr_vector args; for (unsigned i=0; iget_domain(i))); } app * lit = m.mk_app(d.m_pred, args.c_ptr()); app * adn_lit = m.mk_app(adn_pred, args.c_ptr()); app * mag_lit = create_magic_literal(adn_lit); app * tail[] = {lit, mag_lit}; rule * r = m_context.get_rule_manager().mk(adn_lit, 2, tail, nullptr); result.add_rule(r); } rule_set * mk_magic_sets::operator()(rule_set const & source) { if (!m_context.magic_sets_for_queries()) { return nullptr; } SASSERT(source.contains(m_goal)); SASSERT(source.get_predicate_rules(m_goal).size() == 1); app * goal_head = source.get_predicate_rules(m_goal)[0]->get_head(); unsigned init_rule_cnt = source.get_num_rules(); { func_decl_set intentional; for (unsigned i=0; iget_decl(); intentional.insert(pred); } //now we iterate through all predicates and collect the set of extentional ones const rule_dependencies * deps; rule_dependencies computed_deps(m_context); if (source.is_closed()) { deps = &source.get_dependencies(); } else { computed_deps.populate(source); deps = &computed_deps; } rule_dependencies::iterator it = deps->begin(); rule_dependencies::iterator end = deps->end(); for (; it!=end; ++it) { func_decl * pred = it->m_key; if (intentional.contains(pred)) { continue; } SASSERT(it->m_value->empty());//extentional predicates have no dependency m_extentional.insert(pred); } } //adornment goal_adn; //goal_adn.populate(goal_head, ); var_idx_set empty_var_idx_set; adorn_literal(goal_head, empty_var_idx_set); rule_set * result = alloc(rule_set, m_context); result->inherit_predicates(source); while (!m_todo.empty()) { adornment_desc task = m_todo.back(); m_todo.pop_back(); const rule_vector & pred_rules = source.get_predicate_rules(task.m_pred); rule_vector::const_iterator it = pred_rules.begin(); rule_vector::const_iterator end = pred_rules.end(); for (; it != end; ++it) { rule * r = *it; transform_rule(task.m_adornment, r, *result); } if (!m_context.get_rel_context()->is_empty_relation(task.m_pred)) { //we need a rule to copy facts that are already in a relation into the adorned //relation (since out intentional predicates can have facts, not only rules) create_transfer_rule(task, *result); } } app * adn_goal_head = adorn_literal(goal_head, empty_var_idx_set); app * mag_goal_head = create_magic_literal(adn_goal_head); SASSERT(mag_goal_head->is_ground()); rule * mag_goal_rule = m_context.get_rule_manager().mk(mag_goal_head, 0, nullptr, nullptr); result->add_rule(mag_goal_rule); rule * back_to_goal_rule = m_context.get_rule_manager().mk(goal_head, 1, &adn_goal_head, nullptr); result->add_rule(back_to_goal_rule); return result; } }; z3-z3-4.8.7/src/muz/transforms/dl_mk_magic_sets.h000066400000000000000000000103031356505360400216610ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_mk_magic_sets.h Abstract: Author: Krystof Hoder (t-khoder) 2010-10-4. Revision History: --*/ #ifndef DL_MK_MAGIC_SETS_H_ #define DL_MK_MAGIC_SETS_H_ #include #include "util/map.h" #include "util/obj_pair_hashtable.h" #include "muz/base/dl_context.h" #include "muz/base/dl_rule_set.h" #include "muz/base/dl_rule_transformer.h" namespace datalog { /** \brief Implements magic sets rule transformation. According to A. Voronkov. Foundations of Deductive Databases. The stratified negation is not in the book addressed wrt. magic sets, but it seems that, for the purpose of magic sets, the negated literals should be treated just as if they were non-negated (we are interested only in values of arguments, not in the actual content of relations, at that point). */ class mk_magic_sets : public rule_transformer::plugin { enum a_flag { AD_FREE, AD_BOUND }; struct a_flag_hash { typedef a_flag data; unsigned operator()(a_flag x) const { return x; } }; struct adornment : public svector { void populate(app * lit, const var_idx_set & bound_vars); bool operator==(const adornment & o) const { return vectors_equal(*this, o); } std::string to_string() const; }; struct adornment_desc { func_decl * m_pred; adornment m_adornment; adornment_desc() {} adornment_desc(func_decl * pred) : m_pred(pred) {} adornment_desc(func_decl * pred, const adornment & a) : m_pred(pred), m_adornment(a) {} bool operator==(const adornment_desc & o) const { //m_tail_adornment value is implied by the rule and the head adornment return m_pred==o.m_pred && m_adornment==o.m_adornment; } unsigned hash() const { return m_pred->hash()^svector_hash()(m_adornment); } }; struct adorned_rule { app * m_head; adornment m_head_adornment; ptr_vector m_tail; }; typedef hashtable, default_eq > adornment_set; typedef map, default_eq > adornment_map; typedef obj_map pred_adornment_map; typedef obj_map pred2pred; context & m_context; ast_manager & m; rule_manager& rm; ast_ref_vector m_pinned; /** \brief Predicates from the original set that appear in a head of a rule */ func_decl_set m_extentional; //adornment_set m_processed; vector m_todo; adornment_map m_adorned_preds; pred_adornment_map m_adornments; pred2pred m_magic_preds; func_decl_ref m_goal; void reset(); float get_unbound_cost(app * lit, const var_idx_set & bound_vars); int pop_bound(unsigned_vector & cont, rule * r, const var_idx_set & bound_vars); app * create_magic_literal(app * l); void create_magic_rules(app * head, unsigned tail_cnt, app * const * tail, bool const* negated, rule_set& result); app * adorn_literal(app * lit, const var_idx_set & bound_vars); void transform_rule(const adornment & head_adornment, rule * r, rule_set& result); void create_transfer_rule(const adornment_desc & d, rule_set& result); public: /** \brief Create magic sets rule transformer for \c goal_rule. When applying the transformer, the \c goal_rule must be present in the \c rule_set that is being transformed. */ mk_magic_sets(context & ctx, func_decl* goal); rule_set * operator()(rule_set const & source) override; }; }; #endif /* DL_MK_MAGIC_SETS_H_ */ z3-z3-4.8.7/src/muz/transforms/dl_mk_magic_symbolic.cpp000066400000000000000000000104331356505360400230630ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: dl_mk_magic_symbolic.cpp Abstract: Create Horn clauses for magic symbolic flow. Q(x) :- A(y), B(z), phi1(x,y,z). Q(x) :- C(y), phi2(x,y). A(x) :- C(y), phi3(x,y). A(x) :- A(y), phi3(x,y). B(x) :- C(y), A(z), phi4(x,y,z). C(x) :- phi5(x). Transformed clauses: Q_ans(x) :- Q_query(x), A_ans(y), B_ans(z), phi1(x,y,z). Q_ans(x) :- Q_query(x), C_ans(y), phi2(x,y). Q_query(x) :- true. A_ans(x) :- A_query(x), C_ans(y), phi2(x,y) A_ans(x) :- A_query(x), A_ans(y), phi3(x,y). A_query(y) :- Q_query(x), phi1(x,y,z). A_query(y) :- A_query(x), phi3(x,y). A_query(z) :- B_query(x), C_ans(y), phi4(x,y,z). B_ans(x) :- B_query(x), C_ans(y), A_ans(z), phi4(x,y,z). B_query(z) :- Q_query(x), A_ans(y), phi1(x,y,z). C_ans(x) :- C_query(x), phi5(x). C_query(y) :- Q_query(x), phi2(x,y). C_query(y) :- Q_query(x), phi3(x,y). C_query(y) :- B_query(x), phi4(x,y,z). General scheme: A(x) :- P1(x_1), ..., Pn(x_n), phi(x,x1,..,x_n). P(x) :- Prefix(x,y,z), A(z) ... A_ans(x) :- A_query(x), P_i_ans(x_i), phi(x,..). A_query(z) :- P_query(x), Prefix_ans(x,y,z). Author: Nikolaj Bjorner (nbjorner) 2013-06-19 Revision History: --*/ #include "muz/transforms/dl_mk_magic_symbolic.h" #include "muz/base/dl_context.h" namespace datalog { mk_magic_symbolic::mk_magic_symbolic(context & ctx, unsigned priority): plugin(priority), m(ctx.get_manager()), m_ctx(ctx) { } mk_magic_symbolic::~mk_magic_symbolic() { } rule_set * mk_magic_symbolic::operator()(rule_set const & source) { if (!m_ctx.magic()) { return nullptr; } context& ctx = source.get_context(); rule_manager& rm = source.get_rule_manager(); rule_set * result = alloc(rule_set, ctx); unsigned sz = source.get_num_rules(); rule_ref new_rule(rm); app_ref_vector tail(m); app_ref head(m); svector neg; for (unsigned i = 0; i < sz; ++i) { rule & r = *source.get_rule(i); unsigned utsz = r.get_uninterpreted_tail_size(); unsigned tsz = r.get_tail_size(); tail.reset(); neg.reset(); for (unsigned j = utsz; j < tsz; ++j) { tail.push_back(r.get_tail(j)); neg.push_back(false); } tail.push_back(mk_query(r.get_head())); neg.push_back(false); for (unsigned j = 0; j < utsz; ++j) { tail.push_back(mk_ans(r.get_tail(j))); neg.push_back(false); } new_rule = rm.mk(mk_ans(r.get_head()), tail.size(), tail.c_ptr(), neg.c_ptr(), r.name(), true); result->add_rule(new_rule); if (source.is_output_predicate(r.get_decl())) { result->set_output_predicate(new_rule->get_decl()); new_rule = rm.mk(mk_query(r.get_head()), 0, nullptr, nullptr, r.name(), true); result->add_rule(new_rule); } for (unsigned j = 0; j < utsz; ++j) { new_rule = rm.mk(mk_query(r.get_tail(j)), tail.size()-utsz+j, tail.c_ptr(), neg.c_ptr(), r.name(), true); result->add_rule(new_rule); } } TRACE("dl", result->display(tout);); return result; } app_ref mk_magic_symbolic::mk_query(app* q) { string_buffer<64> name; func_decl* f = q->get_decl(); name << f->get_name() << "!query"; func_decl_ref g(m); g = m.mk_func_decl(symbol(name.c_str()), f->get_arity(), f->get_domain(), f->get_range()); m_ctx.register_predicate(g, false); return app_ref(m.mk_app(g, q->get_num_args(), q->get_args()), m); } app_ref mk_magic_symbolic::mk_ans(app* q) { string_buffer<64> name; func_decl* f = q->get_decl(); func_decl_ref g(m); name << f->get_name() << "!ans"; g = m.mk_func_decl(symbol(name.c_str()), f->get_arity(), f->get_domain(), f->get_range()); m_ctx.register_predicate(g, false); return app_ref(m.mk_app(g, q->get_num_args(), q->get_args()), m); } }; z3-z3-4.8.7/src/muz/transforms/dl_mk_magic_symbolic.h000066400000000000000000000014301356505360400225250ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: dl_mk_magic_symbolic.h Abstract: Create Horn clauses for magic symbolic transformation. Author: Nikolaj Bjorner (nbjorner) 2013-06-19 Revision History: --*/ #ifndef DL_MK_MAGIC_SYMBOLIC_H_ #define DL_MK_MAGIC_SYMBOLIC_H_ #include "muz/base/dl_rule_transformer.h" namespace datalog { class mk_magic_symbolic : public rule_transformer::plugin { ast_manager& m; context& m_ctx; app_ref mk_ans(app* q); app_ref mk_query(app* q); public: mk_magic_symbolic(context & ctx, unsigned priority = 33037); ~mk_magic_symbolic() override; rule_set * operator()(rule_set const & source) override; }; }; #endif /* DL_MK_MAGIC_SYMBOLIC_H_ */ z3-z3-4.8.7/src/muz/transforms/dl_mk_quantifier_abstraction.cpp000066400000000000000000000320761356505360400246510ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: dl_mk_quantifier_abstraction.cpp Abstract: Create quantified Horn clauses from benchmarks with arrays. Author: Ken McMillan Andrey Rybalchenko Nikolaj Bjorner (nbjorner) 2013-04-02 Revision History: --*/ #include "muz/transforms/dl_mk_quantifier_abstraction.h" #include "muz/base/dl_context.h" #include "ast/rewriter/expr_safe_replace.h" #include "ast/expr_abstract.h" namespace datalog { // model converter: // Given model for P^(x, y, i, a[i]) // create model: P(x,y,a) == forall i . P^(x,y,i,a[i]) // requires substitution and list of bound variables. class mk_quantifier_abstraction::qa_model_converter : public model_converter { ast_manager& m; func_decl_ref_vector m_old_funcs; func_decl_ref_vector m_new_funcs; vector m_subst; vector m_sorts; vector > m_bound; public: qa_model_converter(ast_manager& m): m(m), m_old_funcs(m), m_new_funcs(m) {} ~qa_model_converter() override {} model_converter * translate(ast_translation & translator) override { return alloc(qa_model_converter, m); } void display(std::ostream& out) override { display_add(out, m); } void get_units(obj_map& units) override { units.reset(); } void insert(func_decl* old_p, func_decl* new_p, expr_ref_vector& sub, sort_ref_vector& sorts, svector const& bound) { m_old_funcs.push_back(old_p); m_new_funcs.push_back(new_p); m_subst.push_back(sub); m_bound.push_back(bound); m_sorts.push_back(sorts); } void operator()(model_ref & old_model) override { model_ref new_model = alloc(model, m); for (unsigned i = 0; i < m_new_funcs.size(); ++i) { func_decl* p = m_new_funcs[i].get(); func_decl* q = m_old_funcs[i].get(); expr_ref_vector const& sub = m_subst[i]; sort_ref_vector const& sorts = m_sorts[i]; svector const& is_bound = m_bound[i]; func_interp* f = old_model->get_func_interp(p); expr_ref body(m); unsigned arity_q = q->get_arity(); SASSERT(0 < p->get_arity()); func_interp* g = alloc(func_interp, m, arity_q); if (f) { body = f->get_interp(); SASSERT(!f->is_partial()); SASSERT(body); } else { expr_ref_vector args(m); for (unsigned i = 0; i < p->get_arity(); ++i) { args.push_back(m.mk_var(i, p->get_domain(i))); } body = m.mk_app(p, args.size(), args.c_ptr()); } // Create quantifier wrapper around body. TRACE("dl", tout << mk_pp(body, m) << "\n";); // 1. replace variables by the compound terms from // the original predicate. expr_safe_replace rep(m); for (unsigned i = 0; i < sub.size(); ++i) { rep.insert(m.mk_var(i, m.get_sort(sub[i])), sub[i]); } rep(body); rep.reset(); TRACE("dl", tout << mk_pp(body, m) << "\n";); // 2. replace bound variables by constants. expr_ref_vector consts(m), bound(m), _free(m); svector names; ptr_vector bound_sorts; for (unsigned i = 0; i < sorts.size(); ++i) { sort* s = sorts[i]; consts.push_back(m.mk_fresh_const("C", s)); rep.insert(m.mk_var(i, s), consts.back()); if (is_bound[i]) { bound.push_back(consts.back()); names.push_back(symbol(i)); bound_sorts.push_back(s); } else { _free.push_back(consts.back()); } } rep(body); rep.reset(); TRACE("dl", tout << mk_pp(body, m) << "\n";); // 3. abstract and quantify those variables that should be bound. expr_abstract(m, 0, bound.size(), bound.c_ptr(), body, body); body = m.mk_forall(names.size(), bound_sorts.c_ptr(), names.c_ptr(), body); TRACE("dl", tout << mk_pp(body, m) << "\n";); // 4. replace remaining constants by variables. for (unsigned i = 0; i < _free.size(); ++i) { rep.insert(_free[i].get(), m.mk_var(i, m.get_sort(_free[i].get()))); } rep(body); g->set_else(body); TRACE("dl", tout << mk_pp(body, m) << "\n";); new_model->register_decl(q, g); } old_model = new_model; } }; mk_quantifier_abstraction::mk_quantifier_abstraction( context & ctx, unsigned priority): plugin(priority), m(ctx.get_manager()), m_ctx(ctx), a(m), m_refs(m), m_mc(nullptr) { } mk_quantifier_abstraction::~mk_quantifier_abstraction() { } func_decl* mk_quantifier_abstraction::declare_pred(rule_set const& rules, rule_set& dst, func_decl* old_p) { if (rules.is_output_predicate(old_p)) { dst.inherit_predicate(rules, old_p, old_p); return nullptr; } unsigned sz = old_p->get_arity(); unsigned num_arrays = 0; for (unsigned i = 0; i < sz; ++i) { if (a.is_array(old_p->get_domain(i))) { num_arrays++; } } if (num_arrays == 0) { return nullptr; } func_decl* new_p = nullptr; if (!m_old2new.find(old_p, new_p)) { expr_ref_vector sub(m), vars(m); svector bound; sort_ref_vector domain(m), sorts(m); expr_ref arg(m); for (unsigned i = 0; i < sz; ++i) { sort* s0 = old_p->get_domain(i); unsigned lookahead = 0; sort* s = s0; while (a.is_array(s)) { lookahead += get_array_arity(s); s = get_array_range(s); } arg = m.mk_var(bound.size() + lookahead, s0); s = s0; while (a.is_array(s)) { unsigned arity = get_array_arity(s); expr_ref_vector args(m); for (unsigned j = 0; j < arity; ++j) { sort* s1 = get_array_domain(s, j); domain.push_back(s1); args.push_back(m.mk_var(bound.size(), s1)); bound.push_back(true); sorts.push_back(s1); } arg = mk_select(arg, args.size(), args.c_ptr()); s = get_array_range(s); } domain.push_back(s); bound.push_back(false); sub.push_back(arg); sorts.push_back(s0); } SASSERT(old_p->get_range() == m.mk_bool_sort()); new_p = m.mk_func_decl(old_p->get_name(), domain.size(), domain.c_ptr(), old_p->get_range()); m_refs.push_back(new_p); m_ctx.register_predicate(new_p, false); if (m_mc) { m_mc->insert(old_p, new_p, sub, sorts, bound); } m_old2new.insert(old_p, new_p); } return new_p; } app_ref mk_quantifier_abstraction::mk_head(rule_set const& rules, rule_set& dst, app* p, unsigned idx) { func_decl* new_p = declare_pred(rules, dst, p->get_decl()); if (!new_p) { return app_ref(p, m); } expr_ref_vector args(m); expr_ref arg(m); unsigned sz = p->get_num_args(); for (unsigned i = 0; i < sz; ++i) { arg = p->get_arg(i); sort* s = m.get_sort(arg); while (a.is_array(s)) { unsigned arity = get_array_arity(s); for (unsigned j = 0; j < arity; ++j) { args.push_back(m.mk_var(idx++, get_array_domain(s, j))); } arg = mk_select(arg, arity, args.c_ptr()+args.size()-arity); s = get_array_range(s); } args.push_back(arg); } TRACE("dl", tout << mk_pp(new_p, m) << "\n"; for (unsigned i = 0; i < args.size(); ++i) { tout << mk_pp(args[i].get(), m) << "\n"; }); return app_ref(m.mk_app(new_p, args.size(), args.c_ptr()), m); } app_ref mk_quantifier_abstraction::mk_tail(rule_set const& rules, rule_set& dst, app* p) { func_decl* old_p = p->get_decl(); func_decl* new_p = declare_pred(rules, dst, old_p); if (!new_p) { return app_ref(p, m); } SASSERT(new_p->get_arity() > old_p->get_arity()); unsigned num_extra_args = new_p->get_arity() - old_p->get_arity(); var_shifter shift(m); expr_ref p_shifted(m); shift(p, num_extra_args, p_shifted); app* ps = to_app(p_shifted); expr_ref_vector args(m); app_ref_vector pats(m); sort_ref_vector vars(m); svector names; expr_ref arg(m); unsigned idx = 0; unsigned sz = p->get_num_args(); for (unsigned i = 0; i < sz; ++i) { arg = ps->get_arg(i); sort* s = m.get_sort(arg); bool is_pattern = false; while (a.is_array(s)) { is_pattern = true; unsigned arity = get_array_arity(s); for (unsigned j = 0; j < arity; ++j) { vars.push_back(get_array_domain(s, j)); names.push_back(symbol(idx)); args.push_back(m.mk_var(idx++, vars.back())); } arg = mk_select(arg, arity, args.c_ptr()+args.size()-arity); s = get_array_range(s); } if (is_pattern) { pats.push_back(to_app(arg)); } args.push_back(arg); } expr* pat = nullptr; expr_ref pattern(m); pattern = m.mk_pattern(pats.size(), pats.c_ptr()); pat = pattern.get(); app_ref result(m); symbol qid, skid; result = m.mk_app(new_p, args.size(), args.c_ptr()); result = m.mk_eq(m.mk_forall(vars.size(), vars.c_ptr(), names.c_ptr(), result, 1, qid, skid, 1, &pat), m.mk_true()); return result; } expr * mk_quantifier_abstraction::mk_select(expr* arg, unsigned num_args, expr* const* args) { ptr_vector args2; args2.push_back(arg); args2.append(num_args, args); return a.mk_select(args2.size(), args2.c_ptr()); } rule_set * mk_quantifier_abstraction::operator()(rule_set const & source) { if (!m_ctx.quantify_arrays()) { return nullptr; } unsigned sz = source.get_num_rules(); for (unsigned i = 0; i < sz; ++i) { rule& r = *source.get_rule(i); if (r.has_negation()) { return nullptr; } } m_refs.reset(); m_old2new.reset(); m_new2old.reset(); rule_manager& rm = source.get_rule_manager(); rule_ref new_rule(rm); expr_ref_vector tail(m); app_ref head(m); expr_ref fml(m); rule_counter& vc = rm.get_counter(); if (m_ctx.get_model_converter()) { m_mc = alloc(qa_model_converter, m); } rule_set * result = alloc(rule_set, m_ctx); for (unsigned i = 0; i < sz; ++i) { tail.reset(); rule & r = *source.get_rule(i); TRACE("dl", r.display(m_ctx, tout); ); unsigned cnt = vc.get_max_rule_var(r)+1; unsigned utsz = r.get_uninterpreted_tail_size(); unsigned tsz = r.get_tail_size(); for (unsigned j = 0; j < utsz; ++j) { tail.push_back(mk_tail(source, *result, r.get_tail(j))); } for (unsigned j = utsz; j < tsz; ++j) { tail.push_back(r.get_tail(j)); } head = mk_head(source, *result, r.get_head(), cnt); fml = m.mk_implies(m.mk_and(tail.size(), tail.c_ptr()), head); proof_ref pr(m); rm.mk_rule(fml, pr, *result, r.name()); TRACE("dl", result->last()->display(m_ctx, tout);); } // proof converter: proofs are not necessarily preserved using this transformation. if (m_old2new.empty()) { dealloc(result); dealloc(m_mc); result = nullptr; } else { m_ctx.add_model_converter(m_mc); } m_mc = nullptr; return result; } }; z3-z3-4.8.7/src/muz/transforms/dl_mk_quantifier_abstraction.h000066400000000000000000000030601356505360400243050ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: dl_mk_quantifier_abstraction.h Abstract: Convert clauses with array arguments to predicates into Quantified Horn clauses. Author: Ken McMillan Andrey Rybalchenko Nikolaj Bjorner (nbjorner) 2013-04-02 Revision History: Based on approach suggested in SAS 2013 paper "On Solving Universally Quantified Horn Clauses" --*/ #ifndef DL_MK_QUANTIFIER_ABSTRACTION_H_ #define DL_MK_QUANTIFIER_ABSTRACTION_H_ #include "muz/base/dl_rule_transformer.h" #include "ast/array_decl_plugin.h" namespace datalog { class context; class mk_quantifier_abstraction : public rule_transformer::plugin { class qa_model_converter; ast_manager& m; context& m_ctx; array_util a; func_decl_ref_vector m_refs; obj_map m_new2old; obj_map m_old2new; qa_model_converter* m_mc; func_decl* declare_pred(rule_set const& rules, rule_set& dst, func_decl* old_p); app_ref mk_head(rule_set const& rules, rule_set& dst, app* p, unsigned idx); app_ref mk_tail(rule_set const& rules, rule_set& dst, app* p); expr* mk_select(expr* a, unsigned num_args, expr* const* args); public: mk_quantifier_abstraction(context & ctx, unsigned priority); ~mk_quantifier_abstraction() override; rule_set * operator()(rule_set const & source) override; }; }; #endif /* DL_MK_QUANTIFIER_ABSTRACTION_H_ */ z3-z3-4.8.7/src/muz/transforms/dl_mk_quantifier_instantiation.cpp000066400000000000000000000225671356505360400252300ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: dl_mk_quantifier_instantiation.cpp Abstract: Convert Quantified Horn clauses into non-quantified clauses using instantiation. Author: Ken McMillan Andrey Rybalchenko Nikolaj Bjorner (nbjorner) 2013-04-02 Revision History: Based on approach suggested in the SAS 2013 paper "On Solving Universally Quantified Horn Clauses" --*/ #include "muz/transforms/dl_mk_quantifier_instantiation.h" #include "muz/base/dl_context.h" #include "ast/pattern/pattern_inference.h" #include "ast/rewriter/rewriter_def.h" #include "ast/ast_util.h" namespace datalog { mk_quantifier_instantiation::mk_quantifier_instantiation( context & ctx, unsigned priority): plugin(priority), m(ctx.get_manager()), m_ctx(ctx), m_var2cnst(m), m_cnst2var(m) { } mk_quantifier_instantiation::~mk_quantifier_instantiation() { } void mk_quantifier_instantiation::extract_quantifiers(rule& r, expr_ref_vector& conjs, quantifier_ref_vector& qs) { conjs.reset(); qs.reset(); unsigned tsz = r.get_tail_size(); for (unsigned j = 0; j < tsz; ++j) { conjs.push_back(r.get_tail(j)); } flatten_and(conjs); for (unsigned j = 0; j < conjs.size(); ++j) { expr* e = conjs[j].get(); quantifier* q; if (rule_manager::is_forall(m, e, q)) { qs.push_back(q); conjs[j] = conjs.back(); conjs.pop_back(); --j; } } } void mk_quantifier_instantiation::instantiate_quantifier(quantifier* q, expr_ref_vector & conjs) { expr_ref qe(m); qe = q; m_var2cnst(qe); q = to_quantifier(qe); if (q->get_num_patterns() == 0) { proof_ref new_pr(m); pattern_inference_params params; pattern_inference_rw infer(m, params); infer(q, qe, new_pr); q = to_quantifier(qe); } unsigned num_patterns = q->get_num_patterns(); for (unsigned i = 0; i < num_patterns; ++i) { expr * pat = q->get_pattern(i); SASSERT(m.is_pattern(pat)); instantiate_quantifier(q, to_app(pat), conjs); } } void mk_quantifier_instantiation::instantiate_quantifier(quantifier* q, app* pat, expr_ref_vector & conjs) { m_binding.reset(); m_binding.resize(q->get_num_decls()); term_pairs todo; match(0, pat, 0, todo, q, conjs); } void mk_quantifier_instantiation::match(unsigned i, app* pat, unsigned j, term_pairs& todo, quantifier* q, expr_ref_vector& conjs) { TRACE("dl", tout << "match" << mk_pp(pat, m) << "\n";); while (j < todo.size()) { expr* p = todo[j].first; expr* t = todo[j].second; if (is_var(p)) { unsigned idx = to_var(p)->get_idx(); if (!m_binding[idx]) { m_binding[idx] = t; match(i, pat, j + 1, todo, q, conjs); m_binding[idx] = 0; return; } ++j; continue; } if (!is_app(p)) { return; } app* a1 = to_app(p); unsigned id = t->get_id(); unsigned next_id = id; unsigned sz = todo.size(); do { expr* t2 = m_terms[next_id]; if (is_app(t2)) { app* a2 = to_app(t2); if (a1->get_decl() == a2->get_decl() && a1->get_num_args() == a2->get_num_args()) { for (unsigned k = 0; k < a1->get_num_args(); ++k) { todo.push_back(std::make_pair(a1->get_arg(k), a2->get_arg(k))); } match(i, pat, j + 1, todo, q, conjs); todo.resize(sz); } } next_id = m_uf.next(next_id); } while (next_id != id); return; } if (i == pat->get_num_args()) { yield_binding(q, conjs); return; } expr* arg = pat->get_arg(i); ptr_vector* terms = nullptr; if (m_funs.find(to_app(arg)->get_decl(), terms)) { for (unsigned k = 0; k < terms->size(); ++k) { todo.push_back(std::make_pair(arg, (*terms)[k])); match(i + 1, pat, j, todo, q, conjs); todo.pop_back(); } } } void mk_quantifier_instantiation::yield_binding(quantifier* q, expr_ref_vector& conjs) { DEBUG_CODE( for (unsigned i = 0; i < m_binding.size(); ++i) { SASSERT(m_binding[i]); }); m_binding.reverse(); expr_ref res = instantiate(m, q, m_binding.c_ptr()); m_binding.reverse(); m_cnst2var(res); conjs.push_back(res); TRACE("dl", tout << mk_pp(q, m) << "\n==>\n" << mk_pp(res, m) << "\n";); } void mk_quantifier_instantiation::collect_egraph(expr* e) { expr* e1, *e2; m_todo.push_back(e); expr_fast_mark1 visited; while (!m_todo.empty()) { e = m_todo.back(); m_todo.pop_back(); if (visited.is_marked(e)) { continue; } unsigned n = e->get_id(); if (n >= m_terms.size()) { m_terms.resize(n+1); } m_terms[n] = e; visited.mark(e); if (m.is_eq(e, e1, e2)) { m_uf.merge(e1->get_id(), e2->get_id()); } if (is_app(e)) { app* ap = to_app(e); ptr_vector* terms = nullptr; if (!m_funs.find(ap->get_decl(), terms)) { terms = alloc(ptr_vector); m_funs.insert(ap->get_decl(), terms); } terms->push_back(e); m_todo.append(ap->get_num_args(), ap->get_args()); } } } void mk_quantifier_instantiation::instantiate_rule(rule& r, expr_ref_vector& conjs, quantifier_ref_vector& qs, rule_set& rules) { rule_manager& rm = m_ctx.get_rule_manager(); expr_ref fml(m), cnst(m); var_ref var(m); ptr_vector sorts; r.get_vars(m, sorts); m_uf.reset(); m_terms.reset(); m_var2cnst.reset(); m_cnst2var.reset(); fml = m.mk_and(conjs.size(), conjs.c_ptr()); for (unsigned i = 0; i < sorts.size(); ++i) { var = m.mk_var(i, sorts[i]); cnst = m.mk_fresh_const("C", sorts[i]); m_var2cnst.insert(var, cnst); m_cnst2var.insert(cnst, var); } fml = m.mk_and(conjs.size(), conjs.c_ptr()); m_var2cnst(fml); collect_egraph(fml); for (unsigned i = 0; i < qs.size(); ++i) { instantiate_quantifier(qs[i].get(), conjs); } for (auto & kv : m_funs) dealloc(kv.m_value); m_funs.reset(); fml = m.mk_and(conjs.size(), conjs.c_ptr()); fml = m.mk_implies(fml, r.get_head()); TRACE("dl", r.display(m_ctx, tout); tout << mk_pp(fml, m) << "\n";); rule_set added_rules(m_ctx); proof_ref pr(m); rm.mk_rule(fml, pr, added_rules, r.name()); if (r.get_proof()) { // use def-axiom to encode that new rule is a weakening of the original. proof* p1 = r.get_proof(); for (unsigned i = 0; i < added_rules.get_num_rules(); ++i) { rule* r2 = added_rules.get_rule(i); rm.to_formula(*r2, fml); pr = m.mk_modus_ponens(m.mk_def_axiom(m.mk_implies(m.get_fact(p1), fml)), p1); r2->set_proof(m, pr); } } rules.add_rules(added_rules); } rule_set * mk_quantifier_instantiation::operator()(rule_set const & source) { if (!m_ctx.instantiate_quantifiers()) { return nullptr; } bool has_quantifiers = false; unsigned sz = source.get_num_rules(); rule_manager& rm = m_ctx.get_rule_manager(); for (unsigned i = 0; !has_quantifiers && i < sz; ++i) { rule& r = *source.get_rule(i); has_quantifiers = has_quantifiers || rm.has_quantifiers(r); if (r.has_negation()) { return nullptr; } } if (!has_quantifiers) { return nullptr; } expr_ref_vector conjs(m); quantifier_ref_vector qs(m); rule_set * result = alloc(rule_set, m_ctx); bool instantiated = false; for (unsigned i = 0; i < sz; ++i) { rule * r = source.get_rule(i); extract_quantifiers(*r, conjs, qs); if (qs.empty()) { result->add_rule(r); } else { instantiate_rule(*r, conjs, qs, *result); instantiated = true; } } // model conversion: identity function. if (instantiated) { result->inherit_predicates(source); } else { dealloc(result); result = nullptr; } return result; } }; z3-z3-4.8.7/src/muz/transforms/dl_mk_quantifier_instantiation.h000066400000000000000000000037141356505360400246660ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: dl_mk_quantifier_instantiation.h Abstract: Convert Quantified Horn clauses into non-quantified clauses using instantiation. Author: Ken McMillan Andrey Rybalchenko Nikolaj Bjorner (nbjorner) 2013-04-02 Revision History: Based on approach suggested in the SAS 2013 paper "On Solving Universally Quantified Horn Clauses" --*/ #ifndef DL_MK_QUANTIFIER_INSTANTIATION_H_ #define DL_MK_QUANTIFIER_INSTANTIATION_H_ #include "muz/base/dl_rule_transformer.h" #include "ast/rewriter/expr_safe_replace.h" #include "util/union_find.h" namespace datalog { class context; class mk_quantifier_instantiation : public rule_transformer::plugin { typedef svector > term_pairs; ast_manager& m; context& m_ctx; expr_safe_replace m_var2cnst; expr_safe_replace m_cnst2var; basic_union_find m_uf; ptr_vector m_todo; ptr_vector m_terms; ptr_vector m_binding; obj_map*> m_funs; void extract_quantifiers(rule& r, expr_ref_vector& conjs, quantifier_ref_vector& qs); void collect_egraph(expr* e); void instantiate_rule(rule& r, expr_ref_vector& conjs, quantifier_ref_vector& qs, rule_set& rules); void instantiate_quantifier(quantifier* q, expr_ref_vector & conjs); void instantiate_quantifier(quantifier* q, app* pat, expr_ref_vector & conjs); void match(unsigned i, app* pat, unsigned j, term_pairs& todo, quantifier* q, expr_ref_vector& conjs); void yield_binding(quantifier* q, expr_ref_vector& conjs); public: mk_quantifier_instantiation(context & ctx, unsigned priority); ~mk_quantifier_instantiation() override; rule_set * operator()(rule_set const & source) override; }; }; #endif /* DL_MK_QUANTIFIER_INSTANTIATION_H_ */ z3-z3-4.8.7/src/muz/transforms/dl_mk_rule_inliner.cpp000066400000000000000000000735701356505360400226040ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_mk_rule_inliner.cpp Abstract: Rule transformer which simplifies interpreted tails Author: Krystof Hoder (t-khoder) 2011-10-01. Revision History: Added linear_inline 2012-9-10 (nbjorner) Disable inliner for quantified rules 2012-10-31 (nbjorner) Notes: Resolution transformation (resolve): P(x) :- Q(y), phi(x,y) Q(y) :- R(z), psi(y,z) -------------------------------------------------- P(x) :- R(z), phi(x,y), psi(y,z) Proof converter: replace assumption (*) by rule and upper assumptions. Subsumption transformation (remove rule): P(x) :- Q(y), phi(x,y) Rules --------------------------------- Rules Model converter: P(x) := P(x) or (exists y . Q(y) & phi(x,y)) --*/ #include #include "ast/ast_pp.h" #include "ast/rewriter/rewriter.h" #include "ast/rewriter/rewriter_def.h" #include "muz/transforms/dl_mk_rule_inliner.h" #include "muz/base/fp_params.hpp" namespace datalog { // ----------------------------------- // // mk_rule_inliner::rule_unifier // // ----------------------------------- bool rule_unifier::unify_rules(const rule& tgt, unsigned tgt_idx, const rule& src) { rule_counter& vc = m_rm.get_counter(); unsigned var_cnt = std::max(vc.get_max_rule_var(tgt), vc.get_max_rule_var(src))+1; m_subst.reset(); m_subst.reserve(2, var_cnt); m_ready = m_unif(tgt.get_tail(tgt_idx), src.get_head(), m_subst); if (m_ready) { m_deltas[0] = 0; m_deltas[1] = var_cnt; TRACE("dl", output_predicate(m_context, src.get_head(), tout << "unify rules "); output_predicate(m_context, tgt.get_head(), tout << "\n"); tout << "\n";); } return m_ready; } void rule_unifier::apply(app * a, bool is_tgt, app_ref& res) { expr_ref res_e(m); TRACE("dl", output_predicate(m_context, a, tout); tout << "\n";); m_subst.apply(2, m_deltas, expr_offset(a, is_tgt ? 0 : 1), res_e); SASSERT(is_app(res_e.get())); res = to_app(res_e.get()); } void rule_unifier::apply( rule const& r, bool is_tgt, unsigned skipped_index, app_ref_vector& res, svector& res_neg) { unsigned rule_len = r.get_tail_size(); for (unsigned i = 0; i < rule_len; i++) { if (i != skipped_index) { //i can never be UINT_MAX, so we'll never skip if we're not supposed to app_ref new_tail_el(m); apply(r.get_tail(i), is_tgt, new_tail_el); res.push_back(new_tail_el); res_neg.push_back(r.is_neg_tail(i)); } } } bool rule_unifier::apply(rule const& tgt, unsigned tail_index, rule const& src, rule_ref& res) { SASSERT(m_ready); app_ref new_head(m); app_ref_vector tail(m); svector tail_neg; rule_ref simpl_rule(m_rm); apply(tgt.get_head(), true, new_head); apply(tgt, true, tail_index, tail, tail_neg); apply(src, false, UINT_MAX, tail, tail_neg); mk_rule_inliner::remove_duplicate_tails(tail, tail_neg); SASSERT(tail.size()==tail_neg.size()); std::ostringstream comb_name; comb_name << tgt.name().str() << ";" << src.name().str(); symbol combined_rule_name = symbol(comb_name.str().c_str()); res = m_rm.mk(new_head, tail.size(), tail.c_ptr(), tail_neg.c_ptr(), combined_rule_name, m_normalize); res->set_accounting_parent_object(m_context, const_cast(&tgt)); TRACE("dl", tgt.display(m_context, tout << "tgt (" << tail_index << "): \n"); src.display(m_context, tout << "src:\n"); res->display(m_context, tout << "res\n"); // m_unif.display(tout << "subst:\n"); ); if (m_normalize) { m_rm.fix_unbound_vars(res, true); if (m_interp_simplifier.transform_rule(res.get(), simpl_rule)) { res = simpl_rule; return true; } else { return false; } } else { return true; } } expr_ref_vector rule_unifier::get_rule_subst(const rule& r, bool is_tgt) { SASSERT(m_ready); expr_ref_vector result(m); ptr_vector sorts; expr_ref v(m), w(m); r.get_vars(m, sorts); for (unsigned i = 0; i < sorts.size(); ++i) { v = m.mk_var(i, sorts[i]); m_subst.apply(2, m_deltas, expr_offset(v, is_tgt?0:1), w); result.push_back(w); } return result; } // ----------------------------------- // // mk_rule_inliner // // ----------------------------------- /** Inline occurrences of rule src at tail_index in tgt and return the result in res. */ bool mk_rule_inliner::try_to_inline_rule(rule& tgt, rule& src, unsigned tail_index, rule_ref& res) { SASSERT(tail_indexdisplay(m_context, tout << "interpreted tail is unsat\n");); //the interpreted part is unsatisfiable return false; } } // TBD: replace by r.has_quantifiers() and test bool mk_rule_inliner::has_quantifier(rule const& r) const { unsigned utsz = r.get_uninterpreted_tail_size(); for (unsigned i = utsz; i < r.get_tail_size(); ++i) { if (r.get_tail(i)->has_quantifiers()) return true; } return false; } void mk_rule_inliner::count_pred_occurrences(rule_set const & orig) { rel_context_base* rel = m_context.get_rel_context(); if (rel) { rel->collect_non_empty_predicates(m_preds_with_facts); } for (rule * r : orig) { func_decl * head_pred = r->get_decl(); m_head_pred_ctr.inc(head_pred); if (r->get_tail_size()>0) { m_head_pred_non_empty_tails_ctr.inc(head_pred); } unsigned ut_len = r->get_uninterpreted_tail_size(); for (unsigned i=0; iget_decl(i); m_tail_pred_ctr.inc(pred); if (r->is_neg_tail(i)) { m_preds_with_neg_occurrence.insert(pred); } } } } bool mk_rule_inliner::inlining_allowed(rule_set const& source, func_decl * pred) { if (//these three conditions are important for soundness source.is_output_predicate(pred) || m_preds_with_facts.contains(pred) || m_preds_with_neg_occurrence.contains(pred) || //this condition is used for breaking of cycles among inlined rules m_forbidden_preds.contains(pred)) { return false; } // // these conditions are optional, they avoid possible exponential increase // in the size of the problem // return //m_head_pred_non_empty_tails_ctr.get(pred)<=1 m_head_pred_ctr.get(pred) <= 1 || (m_tail_pred_ctr.get(pred) <= 1 && m_head_pred_ctr.get(pred) <= 4) ; } /** Caller has to dealloc the returned object */ rule_set * mk_rule_inliner::create_allowed_rule_set(rule_set const & orig) { rule_set * res = alloc(rule_set, m_context); for (rule * r : orig) { if (inlining_allowed(orig, r->get_decl())) { res->add_rule(r); } } //the rule set should be stratified, since orig (which is its superset) is as well VERIFY(res->close()); return res; } /** Try to make the set of inlined predicates acyclic by forbidding inlining of one predicate from each strongly connected component. Return true if we did forbide some predicate, and false if the set of rules is already acyclic. */ bool mk_rule_inliner::forbid_preds_from_cycles(rule_set const & r) { SASSERT(r.is_closed()); bool something_forbidden = false; const rule_stratifier::comp_vector& comps = r.get_stratifier().get_strats(); for (rule_stratifier::item_set * stratum : comps) { if (stratum->size() == 1) { continue; } SASSERT(stratum->size() > 1); func_decl * first_stratum_pred = *stratum->begin(); //we're trying to break cycles by removing one predicate from each of them m_forbidden_preds.insert(first_stratum_pred); something_forbidden = true; } return something_forbidden; } bool mk_rule_inliner::forbid_multiple_multipliers(const rule_set & orig, rule_set const & proposed_inlined_rules) { bool something_forbidden = false; const rule_stratifier::comp_vector& comps = proposed_inlined_rules.get_stratifier().get_strats(); for (rule_stratifier::item_set * stratum : comps) { SASSERT(stratum->size()==1); func_decl * head_pred = *stratum->begin(); bool is_multi_head_pred = m_head_pred_ctr.get(head_pred)>1; bool is_multi_occurrence_pred = m_tail_pred_ctr.get(head_pred)>1; const rule_vector& pred_rules = proposed_inlined_rules.get_predicate_rules(head_pred); for (rule * r : pred_rules) { unsigned pt_len = r->get_positive_tail_size(); for (unsigned ti = 0; tiget_decl(ti); if (!inlining_allowed(orig, tail_pred)) { continue; } unsigned tail_pred_head_cnt = m_head_pred_ctr.get(tail_pred); if (tail_pred_head_cnt<=1) { continue; } if (is_multi_head_pred) { m_forbidden_preds.insert(head_pred); something_forbidden = true; goto process_next_pred; } if (is_multi_occurrence_pred) { m_forbidden_preds.insert(tail_pred); something_forbidden = true; } else { is_multi_head_pred = true; m_head_pred_ctr.get(head_pred) = m_head_pred_ctr.get(head_pred)*tail_pred_head_cnt; } } } process_next_pred:; } unsigned rule_cnt = orig.get_num_rules(); for (unsigned ri=0; riget_decl(); if (inlining_allowed(orig, head_pred)) { //we have already processed inlined rules continue; } bool has_multi_head_pred = false; unsigned pt_len = r->get_positive_tail_size(); for (unsigned ti = 0; tiget_decl(ti); if (!inlining_allowed(orig, pred)) { continue; } if (m_head_pred_ctr.get(pred)<=1) { continue; } if (has_multi_head_pred) { m_forbidden_preds.insert(pred); something_forbidden = true; } else { has_multi_head_pred = true; } } } return something_forbidden; } void mk_rule_inliner::plan_inlining(rule_set const & orig) { count_pred_occurrences(orig); scoped_ptr candidate_inlined_set = create_allowed_rule_set(orig); while (forbid_preds_from_cycles(*candidate_inlined_set)) { candidate_inlined_set = create_allowed_rule_set(orig); } if (forbid_multiple_multipliers(orig, *candidate_inlined_set)) { candidate_inlined_set = create_allowed_rule_set(orig); } TRACE("dl", tout<<"rules to be inlined:\n" << (*candidate_inlined_set); ); // now we start filling in the set of the inlined rules in a topological order, // so that we inline rules into other rules SASSERT(m_inlined_rules.get_num_rules() == 0); const rule_stratifier::comp_vector& comps = candidate_inlined_set->get_stratifier().get_strats(); for (rule_stratifier::item_set * stratum : comps) { SASSERT(stratum->size() == 1); func_decl * pred = *stratum->begin(); for (rule * r : candidate_inlined_set->get_predicate_rules(pred)) { transform_rule(orig, r, m_inlined_rules); } } TRACE("dl", tout << "inlined rules after mutual inlining:\n" << m_inlined_rules; ); for (rule * r : m_inlined_rules) { datalog::del_rule(m_mc, *r, false); } } bool mk_rule_inliner::transform_rule(rule_set const& orig, rule * r0, rule_set& tgt) { bool modified = false; rule_ref_vector todo(m_rm); todo.push_back(r0); while (!todo.empty()) { rule_ref r(todo.back(), m_rm); todo.pop_back(); unsigned pt_len = r->get_positive_tail_size(); unsigned i = 0; for (; i < pt_len && !inlining_allowed(orig, r->get_decl(i)); ++i) {}; SASSERT(!has_quantifier(*r.get())); if (i == pt_len) { //there's nothing we can inline in this rule tgt.add_rule(r); continue; } modified = true; func_decl * pred = r->get_decl(i); const rule_vector& pred_rules = m_inlined_rules.get_predicate_rules(pred); for (rule * inl_rule : pred_rules) { rule_ref inl_result(m_rm); if (try_to_inline_rule(*r.get(), *inl_rule, i, inl_result)) { todo.push_back(inl_result); } } } if (modified) { datalog::del_rule(m_mc, *r0, true); } return modified; } bool mk_rule_inliner::transform_rules(const rule_set & orig, rule_set & tgt) { bool something_done = false; for (rule* rl : orig) { rule_ref r(rl, m_rm); func_decl * pred = r->get_decl(); // if inlining is allowed, then we are eliminating // this relation through inlining, // so we don't add its rules to the result something_done |= !inlining_allowed(orig, pred) && transform_rule(orig, r, tgt); } if (something_done && m_mc) { for (rule* r : orig) { if (inlining_allowed(orig, r->get_decl())) { datalog::del_rule(m_mc, *r, true); } } } return something_done; } /** Check whether rule r is oriented in a particular ordering. This is to avoid infinite cycle of inlining in the eager inliner. Out ordering is lexicographic, comparing atoms first on stratum they are in, then on arity and then on ast ID of their func_decl. */ bool mk_rule_inliner::is_oriented_rewriter(rule * r, rule_stratifier const& strat) { func_decl * head_pred = r->get_decl(); unsigned head_strat = strat.get_predicate_strat(head_pred); unsigned head_arity = head_pred->get_arity(); unsigned pt_len = r->get_positive_tail_size(); for (unsigned ti=0; ti < pt_len; ++ti) { func_decl * pred = r->get_decl(ti); unsigned pred_strat = strat.get_predicate_strat(pred); SASSERT(pred_strat <= head_strat); if (pred_strat == head_strat) { if (pred->get_arity()>head_arity || (pred->get_arity()==head_arity && pred->get_id()>=head_pred->get_id()) ) { return false; } } } return true; } bool mk_rule_inliner::do_eager_inlining(rule * r, rule_set const& rules, rule_ref& res) { if (r->has_negation()) { return false; } SASSERT(rules.is_closed()); const rule_stratifier& strat = rules.get_stratifier(); func_decl * head_pred = r->get_decl(); unsigned pt_len = r->get_positive_tail_size(); for (unsigned ti = 0; ti < pt_len; ++ti) { func_decl * pred = r->get_decl(ti); if (pred == head_pred || m_preds_with_facts.contains(pred)) { continue; } const rule_vector& pred_rules = rules.get_predicate_rules(pred); rule * inlining_candidate = nullptr; unsigned rule_cnt = pred_rules.size(); if (rule_cnt == 0) { inlining_candidate = nullptr; } else if (rule_cnt == 1) { inlining_candidate = pred_rules[0]; } else { inlining_candidate = nullptr; for (unsigned ri = 0; ri < rule_cnt; ++ri) { rule * pred_rule = pred_rules[ri]; if (!m_unifier.unify_rules(*r, ti, *pred_rule)) { //we skip rules which don't unify with the tail atom continue; } if (inlining_candidate != nullptr) { // We have two rules that can be inlined into the current // tail predicate. In this situation we don't do inlinning // on this tail atom, as we don't want the overall number // of rules to increase. goto process_next_tail; } inlining_candidate = pred_rule; } } if (inlining_candidate == nullptr) { // nothing unifies with the tail atom, therefore the rule is unsatisfiable // (we can say this because relation pred doesn't have any ground facts either) res = nullptr; datalog::del_rule(m_mc, *r, false); return true; } if (!is_oriented_rewriter(inlining_candidate, strat)) { // The rule which should be used for inlining isn't oriented // in a simplifying direction. Inlining with such rule might lead to // infinite loops, so we don't do it. goto process_next_tail; } if (!try_to_inline_rule(*r, *inlining_candidate, ti, res)) { datalog::del_rule(m_mc, *r, false); res = nullptr; } return true; process_next_tail:; } return false; } bool mk_rule_inliner::do_eager_inlining(scoped_ptr & rules) { scoped_ptr res = alloc(rule_set, m_context); bool done_something = false; rule_set::iterator rend = rules->end(); for (rule_set::iterator rit = rules->begin(); rit!=rend; ++rit) { rule_ref r(*rit, m_rm); rule_ref replacement(m_rm); while (r && do_eager_inlining(r, *rules, replacement)) { r = replacement; done_something = true; } if (!r) { continue; } res->add_rule(r); } if (done_something) { rules = res.detach(); } return done_something; } /** Inline predicates that are known to not be join-points. P(1,x) :- P(0,y), phi(x,y) P(0,x) :- P(1,z), psi(x,z) -> P(1,x) :- P(1,z), phi(x,y), psi(y,z) whenever P(0,x) is not unifiable with the body of the rule where it appears (P(1,z)) and P(0,x) is unifiable with at most one (?) other rule (and it does not occur negatively). */ bool mk_rule_inliner::visitor::operator()(expr* e) { m_unifiers.append(m_positions.find(e)); TRACE("dl", tout << "unifier: " << (m_unifiers.empty()?0:m_unifiers.back()); tout << " num unifiers: " << m_unifiers.size(); tout << " num positions: " << m_positions.find(e).size() << "\n"; output_predicate(m_context, to_app(e), tout); tout << "\n";); // stop visitor when we have more than 1 unifier, since that's all we want. return m_unifiers.size() <= 1; } void mk_rule_inliner::visitor::reset(unsigned sz) { m_unifiers.reset(); m_can_remove.reset(); m_can_remove.resize(sz, true); m_can_expand.reset(); m_can_expand.resize(sz, true); m_positions.reset(); } unsigned_vector const& mk_rule_inliner::visitor::add_position(expr* e, unsigned j) { obj_map::obj_map_entry * et = m_positions.insert_if_not_there2(e, unsigned_vector()); et->get_data().m_value.push_back(j); return et->get_data().m_value; } unsigned_vector const& mk_rule_inliner::visitor::del_position(expr* e, unsigned j) { obj_map::obj_map_entry * et = m_positions.find_core(e); SASSERT(et && et->get_data().m_value.contains(j)); et->get_data().m_value.erase(j); return et->get_data().m_value; } void mk_rule_inliner::add_rule(rule_set const& source, rule* r, unsigned i) { svector& can_remove = m_head_visitor.can_remove(); svector& can_expand = m_head_visitor.can_expand(); app* head = r->get_head(); func_decl* headd = head->get_decl(); m_head_visitor.add_position(head, i); m_head_index.insert(head); m_pinned.push_back(r); if (source.is_output_predicate(headd) || m_preds_with_facts.contains(headd)) { can_remove.set(i, false); TRACE("dl", output_predicate(m_context, head, tout << "cannot remove: " << i << " "); tout << "\n";); } unsigned tl_sz = r->get_uninterpreted_tail_size(); for (unsigned j = 0; j < tl_sz; ++j) { app* tail = r->get_tail(j); m_tail_visitor.add_position(tail, i); m_tail_index.insert(tail); } bool can_exp = tl_sz == 1 && r->get_positive_tail_size() == 1 && !m_preds_with_facts.contains(r->get_decl(0)) && !source.is_output_predicate(r->get_decl(0)); can_expand.set(i, can_exp); } void mk_rule_inliner::del_rule(rule* r, unsigned i) { app* head = r->get_head(); m_head_visitor.del_position(head, i); unsigned tl_sz = r->get_uninterpreted_tail_size(); for (unsigned j = 0; j < tl_sz; ++j) { app* tail = r->get_tail(j); m_tail_visitor.del_position(tail, i); } } #define PRT(_x_) ((_x_)?"T":"F") bool mk_rule_inliner::inline_linear(scoped_ptr& rules) { bool done_something = false; unsigned sz = rules->get_num_rules(); m_head_visitor.reset(sz); m_tail_visitor.reset(sz); m_head_index.reset(); m_tail_index.reset(); TRACE("dl", rules->display(tout);); rule_ref_vector acc(m_rm); for (unsigned i = 0; i < sz; ++i) { acc.push_back(rules->get_rule(i)); } // set up unification index. svector& can_remove = m_head_visitor.can_remove(); svector& can_expand = m_head_visitor.can_expand(); for (unsigned i = 0; i < sz; ++i) { add_rule(*rules, acc[i].get(), i); } // initialize substitution. rule_counter& vc = m_rm.get_counter(); unsigned max_var = 0; for (unsigned i = 0; i < sz; ++i) { rule* r = acc[i].get(); max_var = std::max(max_var, vc.get_max_var(r->get_head())); unsigned tl_sz = r->get_uninterpreted_tail_size(); for (unsigned j = 0; j < tl_sz; ++j) { max_var = std::max(max_var, vc.get_max_var(r->get_tail(j))); } } m_subst.reset(); m_subst.reserve_vars(max_var+1); m_subst.reserve_offsets(std::max(m_tail_index.get_approx_num_regs(), 2+m_head_index.get_approx_num_regs())); svector valid; valid.reset(); valid.resize(sz, true); bool allow_branching = m_context.get_params().xform_inline_linear_branch(); for (unsigned i = 0; i < sz; ++i) { while (true) { rule_ref r(acc[i].get(), m_rm); TRACE("dl", r->display(m_context, tout << "processing: " << i << "\n");); if (!valid.get(i)) { TRACE("dl", tout << "invalid: " << i << "\n";); break; } if (!can_expand.get(i)) { TRACE("dl", tout << "cannot expand: " << i << "\n";); break; } m_head_visitor.reset(); m_head_index.unify(r->get_tail(0), m_head_visitor); unsigned num_head_unifiers = m_head_visitor.get_unifiers().size(); if (num_head_unifiers != 1) { TRACE("dl", tout << "no unique unifier " << num_head_unifiers << "\n";); break; } unsigned j = m_head_visitor.get_unifiers()[0]; if (!can_remove.get(j) || !valid.get(j) || i == j) { TRACE("dl", tout << PRT(can_remove.get(j)) << " " << PRT(valid.get(j)) << " " << PRT(i != j) << "\n";); break; } rule* r2 = acc[j].get(); // check that the head of r2 only unifies with this single body position. TRACE("dl", output_predicate(m_context, r2->get_head(), tout << "unify head: "); tout << "\n";); m_tail_visitor.reset(); m_tail_index.unify(r2->get_head(), m_tail_visitor); unsigned_vector const& tail_unifiers = m_tail_visitor.get_unifiers(); unsigned num_tail_unifiers = tail_unifiers.size(); SASSERT(!tail_unifiers.empty()); if (!allow_branching && num_tail_unifiers != 1) { TRACE("dl", tout << "too many tails " << num_tail_unifiers << "\n";); break; } rule_ref rl_res(m_rm); if (!try_to_inline_rule(*r.get(), *r2, 0, rl_res)) { TRACE("dl", r->display(m_context, tout << "inlining failed\n"); r2->display(m_context, tout); ); break; } done_something = true; TRACE("dl", r->display(m_context, tout); r2->display(m_context, tout); rl_res->display(m_context, tout); ); del_rule(r, i); add_rule(*rules, rl_res.get(), i); r = rl_res; acc[i] = r.get(); can_expand.set(i, can_expand.get(j)); if (num_tail_unifiers == 1) { TRACE("dl", tout << "setting invalid: " << j << "\n";); valid.set(j, false); datalog::del_rule(m_mc, *r2, true); del_rule(r2, j); } max_var = std::max(max_var, vc.get_max_rule_var(*r.get())); m_subst.reserve_vars(max_var+1); } } if (done_something) { scoped_ptr res = alloc(rule_set, m_context); for (unsigned i = 0; i < sz; ++i) { if (valid.get(i)) { res->add_rule(acc[i].get()); } } res->inherit_predicates(*rules); TRACE("dl", res->display(tout);); rules = res.detach(); } return done_something; } rule_set * mk_rule_inliner::operator()(rule_set const & source) { bool something_done = false; ref hsmc; if (source.get_num_rules() == 0) { return nullptr; } for (rule const* r : source) if (has_quantifier(*r)) return nullptr; if (m_context.get_model_converter()) { hsmc = alloc(horn_subsume_model_converter, m); } m_mc = hsmc.get(); scoped_ptr res = alloc(rule_set, m_context); if (m_context.get_params().xform_inline_eager()) { TRACE("dl", source.display(tout << "before eager inlining\n");); plan_inlining(source); something_done = transform_rules(source, *res); VERIFY(res->close()); //this transformation doesn't break the negation stratification // try eager inlining if (do_eager_inlining(res)) { something_done = true; } TRACE("dl", res->display(tout << "after eager inlining\n");); } if (something_done) { res->inherit_predicates(source); } else { res = alloc(rule_set, source); } if (m_context.get_params().xform_inline_linear() && inline_linear(res)) { something_done = true; } if (!something_done) { res = nullptr; } else { m_context.add_model_converter(hsmc.get()); } return res.detach(); } }; z3-z3-4.8.7/src/muz/transforms/dl_mk_rule_inliner.h000066400000000000000000000157571356505360400222540ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_mk_interp_tail_simplifier.h Abstract: Rule transformer which inlines some of the rules Author: Krystof Hoder (t-khoder) 2011-10-02. Revision History: --*/ #ifndef DL_MK_RULE_INLINER_H_ #define DL_MK_RULE_INLINER_H_ #include "muz/base/dl_context.h" #include "muz/base/dl_rule_transformer.h" #include "muz/transforms/dl_mk_interp_tail_simplifier.h" #include "ast/substitution/unifier.h" #include "ast/substitution/substitution.h" #include "ast/substitution/substitution_tree.h" namespace datalog { class rule_unifier { ast_manager& m; rule_manager& m_rm; context& m_context; /** We use this simplifier after inlining to get nicer intermediate rules */ mk_interp_tail_simplifier m_interp_simplifier; substitution m_subst; unifier m_unif; bool m_ready; bool m_normalize; unsigned m_deltas[2]; public: rule_unifier(context& ctx) : m(ctx.get_manager()), m_rm(ctx.get_rule_manager()), m_context(ctx), m_interp_simplifier(ctx), m_subst(m), m_unif(m), m_ready(false), m_normalize(true) {} /** Reset substitution and unify tail tgt_idx of the target rule and the head of the src rule */ bool unify_rules(rule const& tgt, unsigned tgt_idx, rule const& src); /** \brief Apply unifier to rules. Return false if the resulting rule is a tautology (the interpreted tail is unsat). */ bool apply(rule const& tgt, unsigned tgt_idx, rule const& src, rule_ref& result); void display(std::ostream& stm) { m_subst.display(stm, 2, m_deltas); } /** Retrieve substitutions for src/tgt. (second argument of unify_rules). */ expr_ref_vector get_rule_subst(rule const& r, bool is_tgt); /** Control if bound variables are normalized after unification. The default is 'true': bound variables are re-mapped to an initial segment of de-Bruijn indices. */ void set_normalize(bool n) { m_normalize = n; } private: void apply(app * a, bool is_tgt, app_ref& res); /** Apply substitution to a rule tail. Tail with skipped_index is skipped, unless skipped_index is equal to UINT_MAX */ void apply(rule const& r, bool is_tgt, unsigned skipped_index, app_ref_vector& res, svector& res_neg); }; class mk_rule_inliner : public rule_transformer::plugin { class visitor : public st_visitor { context& m_context; unsigned_vector m_unifiers; svector m_can_remove, m_can_expand; obj_map m_positions; public: visitor(context& c, substitution & s): st_visitor(s), m_context(c) { (void) m_context; } bool operator()(expr* e) override; void reset() { m_unifiers.reset(); } void reset(unsigned sz); svector& can_remove() { return m_can_remove; } svector& can_expand() { return m_can_expand; } unsigned_vector const& add_position(expr* e, unsigned j); unsigned_vector const& del_position(expr* e, unsigned j); unsigned_vector const& get_unifiers() { return m_unifiers; } }; typedef obj_map decl_map; ast_manager & m; rule_manager & m_rm; context & m_context; th_rewriter& m_simp; rule_ref_vector m_pinned; func_decl_set m_forbidden_preds; func_decl_set m_preds_with_facts; func_decl_set m_preds_with_neg_occurrence; ast_counter m_head_pred_ctr; ast_counter m_head_pred_non_empty_tails_ctr; ast_counter m_tail_pred_ctr; rule_set m_inlined_rules; horn_subsume_model_converter* m_mc; //used in try_to_inline_rule and do_eager_inlining rule_unifier m_unifier; substitution_tree m_head_index; // for straight-line relation inlining. substitution_tree m_tail_index; substitution m_subst; visitor m_head_visitor; visitor m_tail_visitor; bool tail_matches_head(app * tail, app* head); bool try_to_inline_rule(rule& tgt, rule& src, unsigned tail_index, rule_ref& res); bool inlining_allowed(rule_set const& orig, func_decl * pred); void count_pred_occurrences(rule_set const & orig); void plan_inlining(rule_set const & orig); rule_set * create_allowed_rule_set(rule_set const & orig); bool forbid_preds_from_cycles(rule_set const & r); /** Ensure we don't inline two multi-head rules that would appear together in some tail */ bool forbid_multiple_multipliers(const rule_set & orig, rule_set const & proposed_inlined_rules); /** Return true if the rule was modified */ bool transform_rule(rule_set const& orig, rule * r, rule_set& tgt); /** Return true if some transformation was performed */ bool transform_rules(const rule_set & orig, rule_set & tgt); bool is_oriented_rewriter(rule * r, rule_stratifier const& strat); /** Return false if nothing was done with the rule. res may be set to zero if we managed to prove the rule unsatisfiable. */ bool do_eager_inlining(rule * r, rule_set const& rules, rule_ref& res); /** Inline rules even if it doesn't lead to elimination of the whole predicate. The inlining is done as long as it doesn't increase the number of rules (i.e. when only one rule defining a predicate can replace tail atom). The original rule-set must be closed before passing t this function */ bool do_eager_inlining(scoped_ptr & rules); bool has_quantifier(rule const& r) const; /** Inline predicates that are known to not be join-points. */ bool inline_linear(scoped_ptr& rules); void add_rule(rule_set const& rule_set, rule* r, unsigned i); void del_rule(rule* r, unsigned i); public: mk_rule_inliner(context & ctx, unsigned priority=35000) : plugin(priority), m(ctx.get_manager()), m_rm(ctx.get_rule_manager()), m_context(ctx), m_simp(m_context.get_rewriter()), m_pinned(m_rm), m_inlined_rules(m_context), m_mc(nullptr), m_unifier(ctx), m_head_index(m), m_tail_index(m), m_subst(m), m_head_visitor(ctx, m_subst), m_tail_visitor(ctx, m_subst) {} ~mk_rule_inliner() override { } rule_set * operator()(rule_set const & source) override; }; }; #endif /* DL_MK_INTERP_TAIL_SIMPLIFIER_H_ */ z3-z3-4.8.7/src/muz/transforms/dl_mk_scale.cpp000066400000000000000000000202201356505360400211640ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: dl_mk_scale.cpp Abstract: Author: Nikolaj Bjorner (nbjorner) 2013-08-19 Revision History: --*/ #include "muz/transforms/dl_mk_scale.h" #include "muz/base/dl_context.h" #include "muz/base/fp_params.hpp" namespace datalog { class mk_scale::scale_model_converter : public model_converter { ast_manager& m; func_decl_ref_vector m_trail; arith_util a; obj_map m_new2old; public: scale_model_converter(ast_manager& m): m(m), m_trail(m), a(m) {} ~scale_model_converter() override {} void add_new2old(func_decl* new_f, func_decl* old_f) { m_trail.push_back(old_f); m_trail.push_back(new_f); m_new2old.insert(new_f, old_f); } void get_units(obj_map& units) override { units.reset(); } void operator()(model_ref& md) override { model_ref old_model = alloc(model, m); for (auto const& kv : m_new2old) { func_decl* old_p = kv.m_value; func_decl* new_p = kv.m_key; func_interp* old_fi = alloc(func_interp, m, old_p->get_arity()); if (new_p->get_arity() == 0) { old_fi->set_else(md->get_const_interp(new_p)); } else { func_interp* new_fi = md->get_func_interp(new_p); expr_ref_vector subst(m); var_subst vs(m, false); expr_ref tmp(m); if (!new_fi) { TRACE("dl", tout << new_p->get_name() << " has no value in the current model\n";); dealloc(old_fi); continue; } for (unsigned i = 0; i < old_p->get_arity(); ++i) { subst.push_back(m.mk_var(i, old_p->get_domain(i))); } subst.push_back(a.mk_numeral(rational(1), a.mk_real())); // Hedge that we don't have to handle the general case for models produced // by Horn clause solvers. SASSERT(!new_fi->is_partial() && new_fi->num_entries() == 0); tmp = vs(new_fi->get_else(), subst.size(), subst.c_ptr()); old_fi->set_else(tmp); old_model->register_decl(old_p, old_fi); } } // register values that have not been scaled. unsigned sz = md->get_num_constants(); for (unsigned i = 0; i < sz; ++i) { func_decl* c = md->get_constant(i); if (!m_new2old.contains(c)) { old_model->register_decl(c, md->get_const_interp(c)); } } sz = md->get_num_functions(); for (unsigned i = 0; i < sz; ++i) { func_decl* f = md->get_function(i); if (!m_new2old.contains(f)) { func_interp* fi = md->get_func_interp(f); old_model->register_decl(f, fi->copy()); } } md = old_model; //TRACE("dl", model_smt2_pp(tout, m, *md, 0); ); } model_converter * translate(ast_translation & translator) override { UNREACHABLE(); return nullptr; } void display(std::ostream& out) override { out << "(scale-model-converter)\n"; } }; mk_scale::mk_scale(context & ctx, unsigned priority): plugin(priority), m(ctx.get_manager()), m_ctx(ctx), a(m), m_trail(m), m_eqs(m) { } mk_scale::~mk_scale() { } rule_set * mk_scale::operator()(rule_set const & source) { if (!m_ctx.scale()) { return nullptr; } rule_manager& rm = source.get_rule_manager(); rule_set * result = alloc(rule_set, m_ctx); unsigned sz = source.get_num_rules(); rule_ref new_rule(rm); app_ref_vector tail(m); app_ref head(m); svector neg; ptr_vector vars; ref smc; if (m_ctx.get_model_converter()) { smc = alloc(scale_model_converter, m); } m_mc = smc.get(); for (unsigned i = 0; i < sz; ++i) { rule & r = *source.get_rule(i); unsigned utsz = r.get_uninterpreted_tail_size(); unsigned tsz = r.get_tail_size(); tail.reset(); vars.reset(); m_cache.reset(); m_trail.reset(); m_eqs.reset(); r.get_vars(m, vars); unsigned num_vars = vars.size(); for (unsigned j = 0; j < utsz; ++j) { tail.push_back(mk_pred(num_vars, r.get_tail(j))); } for (unsigned j = utsz; j < tsz; ++j) { tail.push_back(mk_constraint(num_vars, r.get_tail(j))); } app_ref new_pred = mk_pred(num_vars, r.get_head()); tail.append(m_eqs); tail.push_back(a.mk_gt(m.mk_var(num_vars, a.mk_real()), a.mk_numeral(rational(0), false))); neg.resize(tail.size(), false); new_rule = rm.mk(new_pred, tail.size(), tail.c_ptr(), neg.c_ptr(), r.name(), true); result->add_rule(new_rule); if (source.is_output_predicate(r.get_decl())) { result->set_output_predicate(new_rule->get_decl()); } } TRACE("dl", result->display(tout);); if (m_mc) { m_ctx.add_model_converter(m_mc); } m_trail.reset(); m_cache.reset(); return result; } app_ref mk_scale::mk_pred(unsigned sigma_idx, app* q) { func_decl* f = q->get_decl(); ptr_vector domain(f->get_arity(), f->get_domain()); domain.push_back(a.mk_real()); func_decl_ref g(m); g = m.mk_func_decl(f->get_name(), f->get_arity() + 1, domain.c_ptr(), f->get_range()); expr_ref_vector args(m); for (unsigned i = 0; i < q->get_num_args(); ++i) { expr* arg = q->get_arg(i); rational val; if (a.is_numeral(arg, val)) { if (val.is_zero()) { // arg is unchanged. } else if (val.is_one()) { arg = m.mk_var(sigma_idx, a.mk_real()); } else { // create a fresh variable 'v', add 'v == sigma*arg' expr* v = m.mk_var(sigma_idx + 1 + m_eqs.size(), a.mk_real()); m_eqs.push_back(m.mk_eq(v, a.mk_mul(arg, m.mk_var(sigma_idx, a.mk_real())))); arg = v; } } args.push_back(arg); } args.push_back(m.mk_var(sigma_idx, a.mk_real())); m_ctx.register_predicate(g, false); if (m_mc) { m_mc->add_new2old(g, f); } return app_ref(m.mk_app(g, q->get_num_args() + 1, args.c_ptr()), m); } app_ref mk_scale::mk_constraint(unsigned sigma_idx, app* q) { expr* r = linearize(sigma_idx, q); SASSERT(is_app(r)); return app_ref(to_app(r), m); } expr* mk_scale::linearize(unsigned sigma_idx, expr* e) { expr* r; if (m_cache.find(e, r)) { return r; } if (!is_app(e)) { return e; } expr_ref result(m); app* ap = to_app(e); if (ap->get_family_id() == m.get_basic_family_id() || a.is_add(e) || a.is_sub(e) || a.is_le(e) || a.is_ge(e) || a.is_lt(e) || a.is_gt(e)) { expr_ref_vector args(m); for (unsigned i = 0; i < ap->get_num_args(); ++i) { args.push_back(linearize(sigma_idx, ap->get_arg(i))); } result = m.mk_app(ap->get_decl(), args.size(), args.c_ptr()); } else if (a.is_numeral(e)) { result = a.mk_mul(m.mk_var(sigma_idx, a.mk_real()), e); } else { result = e; } m_trail.push_back(result); m_cache.insert(e, result); return result; } }; z3-z3-4.8.7/src/muz/transforms/dl_mk_scale.h000066400000000000000000000022221356505360400206330ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: dl_mk_scale.h Abstract: Add scale factor to linear (Real) arithmetic Horn clauses. The transformation replaces occurrences of isolated constants by a scale multiplied to each constant. Author: Nikolaj Bjorner (nbjorner) 2013-08-19 Revision History: --*/ #ifndef DL_MK_SCALE_H_ #define DL_MK_SCALE_H_ #include "muz/base/dl_rule_transformer.h" #include "ast/arith_decl_plugin.h" namespace datalog { class mk_scale : public rule_transformer::plugin { class scale_model_converter; ast_manager& m; context& m_ctx; arith_util a; expr_ref_vector m_trail; app_ref_vector m_eqs; obj_map m_cache; scale_model_converter* m_mc; expr* linearize(unsigned num_vars, expr* e); app_ref mk_pred(unsigned num_vars, app* q); app_ref mk_constraint(unsigned num_vars, app* q); public: mk_scale(context & ctx, unsigned priority = 33039); ~mk_scale() override; rule_set * operator()(rule_set const & source) override; }; }; #endif /* DL_MK_SCALE_H_ */ z3-z3-4.8.7/src/muz/transforms/dl_mk_separate_negated_tails.cpp000066400000000000000000000102021356505360400245630ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: mk_separate_negated_tails.cpp Abstract: Author: Nikolaj Bjorner (nbjorner) 2013-09-09 Revision History: --*/ #include "muz/transforms/dl_mk_separate_negated_tails.h" #include "muz/base/dl_context.h" namespace datalog { mk_separate_negated_tails::mk_separate_negated_tails(context& ctx, unsigned priority): plugin(priority), m(ctx.get_manager()), rm(ctx.get_rule_manager()), m_ctx(ctx) {} bool mk_separate_negated_tails::has_private_vars(rule const& r, unsigned j) { get_private_vars(r, j); return !m_vars.empty(); } void mk_separate_negated_tails::get_private_vars(rule const& r, unsigned j) { m_vars.reset(); m_fv.reset(); m_fv(r.get_head()); for (unsigned i = 0; i < r.get_tail_size(); ++i) { if (i != j) { m_fv.accumulate(r.get_tail(i)); } } app* p = r.get_tail(j); for (unsigned i = 0; i < p->get_num_args(); ++i) { expr* v = p->get_arg(i); if (is_var(v)) { unsigned idx = to_var(v)->get_idx(); if (!m_fv.contains(idx)) { m_vars.push_back(v); } } } } void mk_separate_negated_tails::abstract_predicate(app* p, app_ref& q, rule_set& rules) { expr_ref_vector args(m); sort_ref_vector sorts(m); func_decl_ref fn(m); for (unsigned i = 0; i < p->get_num_args(); ++i) { expr* arg = p->get_arg(i); if (!m_vars.contains(arg)) { args.push_back(arg); sorts.push_back(m.get_sort(arg)); } } fn = m.mk_fresh_func_decl(p->get_decl()->get_name(), symbol("N"), sorts.size(), sorts.c_ptr(), m.mk_bool_sort()); m_ctx.register_predicate(fn, false); q = m.mk_app(fn, args.size(), args.c_ptr()); bool is_neg = true; rules.add_rule(rm.mk(q, 1, & p, &is_neg)); } void mk_separate_negated_tails::create_rule(rule const&r, rule_set& rules) { unsigned utsz = r.get_uninterpreted_tail_size(); unsigned ptsz = r.get_positive_tail_size(); unsigned tsz = r.get_tail_size(); app_ref_vector tail(m); app_ref p(m); svector neg; for (unsigned i = 0; i < ptsz; ++i) { tail.push_back(r.get_tail(i)); neg.push_back(false); } for (unsigned i = ptsz; i < utsz; ++i) { get_private_vars(r, i); if (!m_vars.empty()) { abstract_predicate(r.get_tail(i), p, rules); tail.push_back(p); neg.push_back(false); } else { neg.push_back(true); tail.push_back(r.get_tail(i)); } } for (unsigned i = utsz; i < tsz; ++i) { tail.push_back(r.get_tail(i)); neg.push_back(false); } rules.add_rule(rm.mk(r.get_head(), tail.size(), tail.c_ptr(), neg.c_ptr(), r.name())); } rule_set * mk_separate_negated_tails::operator()(rule_set const& src) { scoped_ptr result = alloc(rule_set, m_ctx); bool has_new_rule = false; unsigned sz = src.get_num_rules(); for (unsigned i = 0; i < sz; ++i) { bool change = false; rule & r = *src.get_rule(i); unsigned utsz = r.get_uninterpreted_tail_size(); unsigned ptsz = r.get_positive_tail_size(); for (unsigned j = ptsz; j < utsz; ++j) { SASSERT(r.is_neg_tail(j)); if (has_private_vars(r, j)) { create_rule(r, *result); has_new_rule = true; change = true; break; } } if (!change) { result->add_rule(&r); } } if (!has_new_rule) { return nullptr; } else { result->inherit_predicates(src); return result.detach(); } } } z3-z3-4.8.7/src/muz/transforms/dl_mk_separate_negated_tails.h000066400000000000000000000027261356505360400242440ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: mk_separate_negated_tails.h Abstract: Rule transformer which creates new rules for predicates in negated tails that use free variables not used elsewhere. These free variables incur an overhead on the instructions compiled using dl_compiler. Consider the following transformations: P(x) :- Exists y, z, u . Q(x,y), !R(y,z), !T(z,u). => P(x) :- Exists y, z . Q(x,y), !R(y,z), Exists u . ! T(z,u). => P(x) :- Exists y, z . Q(x,y), !R(y,z), TN(z). TN(z) :- !T(z,u). Author: Nikolaj Bjorner (nbjorner) 2013-09-09 Revision History: --*/ #ifndef DL_MK_SEPARAT_NEGATED_TAILS_H_ #define DL_MK_SEPARAT_NEGATED_TAILS_H_ #include "muz/base/dl_rule_transformer.h" #include "muz/base/dl_context.h" namespace datalog { class mk_separate_negated_tails : public rule_transformer::plugin { ast_manager & m; rule_manager& rm; context & m_ctx; ptr_vector m_vars; expr_free_vars m_fv; bool has_private_vars(rule const& r, unsigned j); void get_private_vars(rule const& r, unsigned j); void abstract_predicate(app* p, app_ref& q, rule_set& rules); void create_rule(rule const&r, rule_set& rules); public: mk_separate_negated_tails(context& ctx, unsigned priority = 21000); rule_set * operator()(rule_set const & source) override; }; } #endif z3-z3-4.8.7/src/muz/transforms/dl_mk_slice.cpp000066400000000000000000000751511356505360400212110ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_mk_slice.cpp Abstract: Author: Nikolaj Bjorner (nbjorner) 2012-9-12. Revision History: Consider a rule: P(x,y) :- R(x,z), phi(x,y,z) input: x, z output: x, y Let x_i, y_i, z_i be indices into the vectors x, y, z. Suppose that positions in P and R are annotated with what is slicable. Sufficient conditions for sliceability: x_i is sliceable if x_i does not appear in phi(x,y,z) and the positions where x_i is used in P and R are sliceable y_i is sliceable if y_i does not occur in phi(x,y,z), or if it occurs in phi(x,y,z) it is only in one conjunct of the form y_i = t[x_j,y_j,z_j] and the positions where y_i is used in P and R are sliceable z_i is sliceable if z_i does not occur in phi(x,y,z), or if it occurs in phi(x,y,z) it is only in one conjunct of the form y_i = t[x_j,y_j,z_i] where y_i is sliceable and the positions where z_i is used in P and R are sliceable A more refined approach may be using Gaussean elimination based on x,z and eliminating variables from x,y (expressing them in terms of a disjoint subeset of x,z). --*/ #include "muz/transforms/dl_mk_slice.h" #include "ast/ast_pp.h" #include "ast/ast_util.h" #include "ast/expr_functors.h" #include "muz/transforms/dl_mk_rule_inliner.h" #include "model/model_smt2_pp.h" namespace datalog { /** Convert from sliced proofs to original proofs. Given sliced rule fml0: forall x y z u. p(x,y) & z = f(x,y) & phi(x,u) => p(u, z) into fml1: forall a b . q(a) & phi(a,b) => q(b) It induces mappings: theta: a |-> x, b |-> u vars: x y z u. predicates: -q(a) |-> p(x,y) +q(b) |-> z = f(x,y) => p(u,z) fml1 |-> fml0 The mapping theta is an injective function from variable indices to variable indices. We can apply it as a substitution on expressions, but we can also apply it as a transformation on substitutions. We write theta[subst] when applying theta on substitution 'subst' such that if [x |-> t] is in subst, then [theta(x) |-> theta(t)] is in the result. Given hyper-resolvent: fml1 subst1 fml2 subst2 |- fml3 where fml1 |-> fml1' with theta1 fml2 |-> fml2' with theta2 Perform the following steps: 1. [Convert fml1' fml2' to datalog rules because we have resolution routines] 2. Create subst1' := theta1[subst1] subst2' := theta2[subst2] 3. Set fml1'' := subst1'(fml1') fml2'' := subst2'(fml2') 4. Resolve fml1'' and fml2'' extract subst1'', subst2'' from resolvents. extract goal fml3' 5. Create subst1''' := subst1'' o subst1' subst2''' := subst2'' o subst2' 6. Return fml1'' subst1''' fml2'' subst2''' |- fml3' 7. Attach to fml3' the transformation ...? */ class mk_slice::slice_proof_converter : public proof_converter { context& m_ctx; ast_manager& m; rule_manager& rm; rule_ref_vector m_pinned_rules; expr_ref_vector m_pinned_exprs; obj_map m_rule2slice; // rule to sliced rule obj_map m_renaming; // rule to renaming obj_map m_sliceform2rule; // sliced formula to rule. ptr_vector m_todo; obj_map m_new_proof; rule_unifier m_unifier; slice_proof_converter(slice_proof_converter const& other); void init_form2rule() { if (!m_sliceform2rule.empty()) { return; } obj_map::iterator it = m_rule2slice.begin(); obj_map::iterator end = m_rule2slice.end(); expr_ref fml(m); for (; it != end; ++it) { rm.to_formula(*it->m_value, fml); m_pinned_exprs.push_back(fml); TRACE("dl", tout << "orig: " << mk_pp(fml, m) << "\n"; it->m_value->display(m_ctx, tout << "new:\n");); m_sliceform2rule.insert(fml, it->m_key); } } void translate_proof(proof_ref& pr) { m_todo.reset(); m_new_proof.reset(); m_todo.push_back(pr); while (!m_todo.empty()) { proof* p = m_todo.back(); if (m_new_proof.contains(p)) { m_todo.pop_back(); } else if (translate_asserted(p)) { // done } else if (translate_hyper_res(p)) { // done } else { m_new_proof.insert(p, p); m_todo.pop_back(); TRACE("dl", tout << "unhandled proof term\n" << mk_pp(p, m) << "\n";); } } pr = m_new_proof.find(pr); } bool translate_asserted(proof* p) { expr* fact = nullptr; rule* r = nullptr; if (!m.is_asserted(p, fact)) { return false; } if (!m_sliceform2rule.find(fact, r)) { TRACE("dl", tout << "does not have fact\n" << mk_pp(fact, m) << "\n";); return false; } proof_ref new_p(m); new_p = r->get_proof(); m_pinned_exprs.push_back(new_p); m_todo.pop_back(); m_new_proof.insert(p, new_p); return true; } bool translate_hyper_res(proof* p) { dl_decl_util util(m); svector > positions; expr_ref concl(m), slice_concl(m); proof_ref_vector premises0(m); vector substs, substs0; if (!m.is_hyper_resolve(p, premises0, slice_concl, positions, substs0)) { return false; } unsigned num_args = p->get_num_args(); SASSERT(num_args >= 2); bool all_found = true; for (unsigned i = 0; i < num_args-1; ++i) { proof* arg = to_app(p->get_arg(i)); SASSERT(m.is_proof(arg)); if (!m_new_proof.contains(arg)) { m_todo.push_back(arg); all_found = false; } } if (!all_found) { return true; } ptr_vector premises; proof* p0 = to_app(p->get_arg(0)); proof* p0_new = m_new_proof.find(p0); expr* fact0 = m.get_fact(p0); TRACE("dl", tout << "fact0: " << mk_pp(fact0, m) << "\n";); rule* orig0; if (!m_sliceform2rule.find(fact0, orig0)) { return false; } premises.push_back(p0_new); rule_ref r1(rm), r2(rm), r3(rm); r1 = orig0; substs.push_back(expr_ref_vector(m)); for (unsigned i = 1; i < num_args-1; ++i) { proof* p1 = to_app(p->get_arg(i)); proof* p1_new = m_new_proof.find(p1); expr* fact1 = m.get_fact(p1); TRACE("dl", tout << "fact1: " << mk_pp(fact1, m) << "\n";); rule* orig1 = nullptr; if (!m_sliceform2rule.find(fact1, orig1)) { return false; } premises.push_back(p1_new); // TODO: work with substitutions. r2 = orig1; unsigned idx = 0; // brittle. TBD get index from positions. VERIFY(m_unifier.unify_rules(*r1, idx, *r2)); m_unifier.apply(*r1.get(), idx, *r2.get(), r3); expr_ref_vector const sub1 = m_unifier.get_rule_subst(*r1.get(), true); for (unsigned j = 0; j < substs.size(); ++j) { apply_subst(substs[j], sub1); // size of substitutions may have grown...substs[j].resize(num_args[j]); } substs.push_back(m_unifier.get_rule_subst(*r2.get(), false)); TRACE("dl", r1->display(m_ctx, tout << "rule1:"); r2->display(m_ctx, tout << "rule2:"); r3->display(m_ctx, tout << "res:");); r1 = r3; } rm.to_formula(*r1.get(), concl); proof* new_p = m.mk_hyper_resolve(premises.size(), premises.c_ptr(), concl, positions, substs); m_pinned_exprs.push_back(new_p); m_pinned_rules.push_back(r1.get()); TRACE("dl", tout << "orig: " << mk_pp(slice_concl, m) << "\n"; r1->display(m_ctx, tout << "new:");); m_sliceform2rule.insert(slice_concl, r1.get()); m_rule2slice.insert(r1.get(), 0); m_renaming.insert(r1.get(), unsigned_vector()); m_new_proof.insert(p, new_p); m_todo.pop_back(); TRACE("dl", tout << "translated:\n" << mk_pp(p, m) << "\nto\n" << mk_pp(new_p, m) << "\n";); return true; } public: slice_proof_converter(context& ctx): m_ctx(ctx), m(ctx.get_manager()), rm(ctx.get_rule_manager()), m_pinned_rules(rm), m_pinned_exprs(m), m_unifier(ctx) {} void insert(rule* orig_rule, rule* slice_rule, unsigned sz, unsigned const* renaming) { m_rule2slice.insert(orig_rule, slice_rule); m_pinned_rules.push_back(orig_rule); m_pinned_rules.push_back(slice_rule); m_renaming.insert(orig_rule, unsigned_vector(sz, renaming)); } proof_ref operator()(ast_manager& m, unsigned num_source, proof * const * source) override { SASSERT(num_source == 1); proof_ref result(source[0], m); init_form2rule(); translate_proof(result); return result; } proof_converter * translate(ast_translation & translator) override { UNREACHABLE(); // this would require implementing translation for the dl_context. return nullptr; } void display(std::ostream& out) override { out << "(slice-proof-converter)\n"; } }; class mk_slice::slice_model_converter : public model_converter { ast_manager& m; obj_map m_slice2old; obj_map m_sliceable; ast_ref_vector m_pinned; public: slice_model_converter(mk_slice& parent, ast_manager& m): m(m), m_pinned(m) {} void add_predicate(func_decl* old_f, func_decl* slice_f) { m_pinned.push_back(old_f); m_pinned.push_back(slice_f); m_slice2old.insert(slice_f, old_f); } void add_sliceable(func_decl* f, bit_vector const& bv) { m_pinned.push_back(f); m_sliceable.insert(f, bv); } void get_units(obj_map& units) override {} void operator()(model_ref & md) override { if (m_slice2old.empty()) { return; } TRACE("dl", model_smt2_pp(tout, m, *md, 0); ); model_ref old_model = alloc(model, m); obj_map::iterator it = m_slice2old.begin(); obj_map::iterator end = m_slice2old.end(); for (; it != end; ++it) { func_decl* old_p = it->m_value; func_decl* new_p = it->m_key; bit_vector const& is_sliced = m_sliceable.find(old_p); SASSERT(is_sliced.size() == old_p->get_arity()); SASSERT(is_sliced.size() > new_p->get_arity()); func_interp* old_fi = alloc(func_interp, m, is_sliced.size()); TRACE("dl", tout << mk_pp(old_p, m) << " " << mk_pp(new_p, m) << "\n"; for (unsigned j = 0; j < is_sliced.size(); ++j) { tout << (is_sliced.get(j)?"1":"0"); } tout << "\n";); if (new_p->get_arity() == 0) { old_fi->set_else(md->get_const_interp(new_p)); } else { expr_ref_vector subst(m); expr_ref tmp(m); var_subst vs(m, false); for (unsigned i = 0; i < is_sliced.size(); ++i) { if (!is_sliced.get(i)) { subst.push_back(m.mk_var(i, old_p->get_domain(i))); } } func_interp* new_fi = md->get_func_interp(new_p); if (!new_fi) { TRACE("dl", tout << new_p->get_name() << " has no value in the current model\n";); dealloc(old_fi); continue; } if (!new_fi->is_partial()) { TRACE("dl", tout << mk_pp(new_fi->get_else(), m) << "\n";); tmp = vs(new_fi->get_else(), subst.size(), subst.c_ptr()); old_fi->set_else(tmp); } unsigned num_entries = new_fi->num_entries(); for (unsigned j = 0; j < num_entries; ++j) { expr_ref res(m); expr_ref_vector args(m); func_entry const* e = new_fi->get_entry(j); for (unsigned k = 0, l = 0; k < old_p->get_arity(); ++k) { if (!is_sliced.get(k)) { tmp = vs(e->get_arg(l++), subst.size(), subst.c_ptr()); args.push_back(tmp); } else { args.push_back(m.mk_var(k, old_p->get_domain(k))); } SASSERT(l <= new_p->get_arity()); } res = vs(e->get_result(), subst.size(), subst.c_ptr()); old_fi->insert_entry(args.c_ptr(), res.get()); } old_model->register_decl(old_p, old_fi); } } // register values that have not been sliced. unsigned sz = md->get_num_constants(); for (unsigned i = 0; i < sz; ++i) { func_decl* c = md->get_constant(i); if (!m_slice2old.contains(c)) { old_model->register_decl(c, md->get_const_interp(c)); } } sz = md->get_num_functions(); for (unsigned i = 0; i < sz; ++i) { func_decl* f = md->get_function(i); if (!m_slice2old.contains(f)) { func_interp* fi = md->get_func_interp(f); old_model->register_decl(f, fi->copy()); } } md = old_model; TRACE("dl", model_smt2_pp(tout, m, *md, 0); ); } model_converter * translate(ast_translation & translator) override { UNREACHABLE(); return nullptr; } void display(std::ostream& out) override { out << "(slice-model-converter)\n"; } }; mk_slice::mk_slice(context & ctx): plugin(1), m_ctx(ctx), m(ctx.get_manager()), rm(ctx.get_rule_manager()), m_solved_vars(m), m_pinned(m), m_pc(nullptr), m_mc(nullptr) {} bit_vector& mk_slice::get_predicate_slice(func_decl* h) { if (!m_sliceable.contains(h)) { bit_vector bv; bv.resize(h->get_arity(), true); m_sliceable.insert(h, bv); } return m_sliceable.find(h); } /** \brief Saturate set of rules with respect to slicing criteria. */ void mk_slice::saturate(rule_set const& src) { bool change = true; while (change) { change = false; for (rule* r : src) { change = prune_rule(*r) || change; } } } void mk_slice::filter_unique_vars(rule& r) { // // Variables that occur in multiple uinterpreted predicates are not sliceable. // uint_set used_vars; for (unsigned j = 0; j < r.get_uninterpreted_tail_size(); ++j) { app* p = r.get_tail(j); for (unsigned i = 0; i < p->get_num_args(); ++i) { expr* v = p->get_arg(i); if (is_var(v)) { unsigned vi = to_var(v)->get_idx(); add_var(vi); if (used_vars.contains(vi)) { m_var_is_sliceable[vi] = false; } else { used_vars.insert(vi); } } } } } void mk_slice::solve_vars(rule& r, uint_set& used_vars, uint_set& parameter_vars) { expr_ref_vector conjs = get_tail_conjs(r); for (expr * e : conjs) { expr_ref r(m); unsigned v; if (is_eq(e, v, r) && is_output(v) && m_var_is_sliceable[v]) { TRACE("dl", tout << "is_eq: " << mk_pp(e, m) << " " << (m_solved_vars[v].get()?"solved":"new") << "\n";); add_var(v); if (!m_solved_vars[v].get()) { TRACE("dl", tout << v << " is solved\n";); add_free_vars(parameter_vars, r); m_solved_vars[v] = r; } else { TRACE("dl", tout << v << " is used\n";); // variables can only be solved once. add_free_vars(used_vars, e); add_free_vars(used_vars, m_solved_vars[v].get()); used_vars.insert(v); } } else { add_free_vars(used_vars, e); } } } bool mk_slice::prune_rule(rule& r) { TRACE("dl", r.display(m_ctx, tout << "prune:\n"); ); bool change = false; init_vars(r); // // if a predicate in the body takes a constant as argument, // the corresponding position is not sliceable. // for (unsigned j = 0; j < r.get_uninterpreted_tail_size(); ++j) { app* p = r.get_tail(j); bit_vector& bv = get_predicate_slice(p); for (unsigned i = 0; i < p->get_num_args(); ++i) { if (!is_var(p->get_arg(i)) && bv.get(i)) { bv.unset(i); change = true; TRACE("dl", tout << "argument " << i << " is not a variable " << p->get_decl()->get_name() << "\n";); } } } filter_unique_vars(r); // // Collect the set of variables that are solved. // Collect the occurrence count of the variables per conjunct. // uint_set used_vars, parameter_vars; solve_vars(r, used_vars, parameter_vars); for (unsigned uv : used_vars) { if (uv < m_var_is_sliceable.size()) { m_var_is_sliceable[uv] = false; } } // // Check if sliceable variables are either solved // or are used to solve output sliceable variables, or // don't occur in interpreted tail. // for (unsigned i = 0; i < num_vars(); ++i) { if (!m_var_is_sliceable[i]) { continue; } if (used_vars.contains(i)) { m_var_is_sliceable[i] = false; continue; } bool is_input = m_input[i]; bool is_output = m_output[i]; if (is_input && is_output) { if (m_solved_vars[i].get()) { m_var_is_sliceable[i] = false; } if (parameter_vars.contains(i)) { m_var_is_sliceable[i] = false; } } else if (is_output) { if (parameter_vars.contains(i)) { m_var_is_sliceable[i] = false; } } else if (is_input) { // I can be a parameter var, but not in used_vars. } else { // variable does not correspond to // any position in predicates. } } // // Update sliceable predicates based on slicing information of variables. // change = finalize_vars(r.get_head()) || change; for (unsigned j = 0; j < r.get_uninterpreted_tail_size(); ++j) { change = finalize_vars(r.get_tail(j)) || change; } return change; } bool mk_slice::is_eq(expr* e, unsigned& v, expr_ref& r) { expr* c, *th, *el, *e1, *e2; unsigned v1, v2; expr_ref r1(m), r2(m); if (m.is_ite(e, c, th, el)) { if (is_eq(th, v1, r1) && is_eq(el, v2, r2) && v1 == v2) { v = v1; r = m.mk_ite(c, r1, r2); return true; } } if (is_var(e)) { v = to_var(e)->get_idx(); r = m.mk_true(); return true; } if (m.is_not(e,e) && is_var(e)) { v = to_var(e)->get_idx(); r = m.mk_false(); return true; } if (m.is_eq(e, e1, e2) && is_var(e1)) { v = to_var(e1)->get_idx(); r = e2; return true; } if (m.is_eq(e, e1, e2) && is_var(e2)) { v = to_var(e2)->get_idx(); r = e1; return true; } return false; } bool mk_slice::is_output(unsigned idx) { return idx < m_output.size() && m_output[idx] && !m_input[idx]; } bool mk_slice::is_output(expr* e) { if (is_var(e)) { return is_output(to_var(e)->get_idx()); } else { return false; } } void mk_slice::init_vars(rule& r) { m_input.reset(); m_output.reset(); m_var_is_sliceable.reset(); m_solved_vars.reset(); init_vars(r.get_head(), true, false); for (unsigned j = 0; j < r.get_uninterpreted_tail_size(); ++j) { init_vars(r.get_tail(j), false, r.is_neg_tail(j)); } } expr_ref_vector mk_slice::get_tail_conjs(rule const& r) { expr_ref_vector conjs(m); for (unsigned j = r.get_uninterpreted_tail_size(); j < r.get_tail_size(); ++j) { conjs.push_back(r.get_tail(j)); } flatten_and(conjs); return conjs; } void mk_slice::add_var(unsigned idx) { if (idx >= m_input.size()) { m_input.resize(idx+1, false); m_output.resize(idx+1, false); m_var_is_sliceable.resize(idx+1, true); m_solved_vars.resize(idx+1); } } void mk_slice::init_vars(app* p, bool is_output, bool is_neg_tail) { bit_vector& bv = get_predicate_slice(p); for (unsigned i = 0; i < p->get_num_args(); ++i) { if (is_neg_tail) { TRACE("dl", tout << "negated " << i << " in " << p->get_decl()->get_name() << "\n";); bv.unset(i); } expr* arg = p->get_arg(i); if (is_var(arg)) { unsigned idx = to_var(arg)->get_idx(); add_var(idx); if (is_output) { m_output[idx] = true; } else { m_input[idx] = true; } m_var_is_sliceable[idx] &= bv.get(i); } else { SASSERT(m.is_value(arg)); if (!is_output) { TRACE("dl", tout << "input " << i << " in " << p->get_decl()->get_name() << "\n";); bv.unset(i); } } } } bool mk_slice::finalize_vars(app* p) { bool change = false; bit_vector& bv = get_predicate_slice(p); for (unsigned i = 0; i < p->get_num_args(); ++i) { expr* arg = p->get_arg(i); if (is_var(arg) && !m_var_is_sliceable[to_var(arg)->get_idx()] && bv.get(i)) { bv.unset(i); change = true; TRACE("dl", tout << "variable is unslicable " << mk_pp(arg, m) << " for index " << i << " in " << p->get_decl()->get_name() << "\n";); } } return change; } void mk_slice::add_free_vars(uint_set& result, expr* e) { expr_free_vars fv; fv(e); for (unsigned i = 0; i < fv.size(); ++i) { if (fv[i]) { result.insert(i); } } } void mk_slice::display(std::ostream& out) { for (auto const& kv : m_sliceable) { out << kv.m_key->get_name() << " "; bit_vector const& bv = kv.m_value; for (unsigned i = 0; i < bv.size(); ++i) { out << (bv.get(i)?"1":"0"); } out << "\n"; } } void mk_slice::reset() { m_input.reset(); m_output.reset(); m_var_is_sliceable.reset(); m_solved_vars.reset(); m_predicates.reset(); m_pinned.reset(); } void mk_slice::declare_predicates(rule_set const& src, rule_set& dst) { obj_map::iterator it = m_sliceable.begin(), end = m_sliceable.end(); ptr_vector domain; bool has_output = false; func_decl* f; for (; it != end; ++it) { domain.reset(); func_decl* p = it->m_key; bit_vector const& bv = it->m_value; for (unsigned i = 0; i < bv.size(); ++i) { if (!bv.get(i)) { domain.push_back(p->get_domain(i)); } } if (domain.size() < bv.size()) { f = m_ctx.mk_fresh_head_predicate(p->get_name(), symbol("slice"), domain.size(), domain.c_ptr(), p); m_pinned.push_back(f); m_predicates.insert(p, f); dst.inherit_predicate(src, p, f); if (m_mc) { m_mc->add_predicate(p, f); } } else if (src.is_output_predicate(p)) { dst.set_output_predicate(p); has_output = true; } } // disable slicing if the output predicates don't occur in rules. if (!has_output) { m_predicates.reset(); } } bool mk_slice::rule_updated(rule const& r) { if (m_predicates.contains(r.get_decl())) return true; for (unsigned i = 0; i < r.get_uninterpreted_tail_size(); ++i) { if (m_predicates.contains(r.get_decl(i))) return true; } return false; } void mk_slice::update_predicate(app* p, app_ref& q) { func_decl* qd; if (m_predicates.find(p->get_decl(), qd)) { bit_vector const& bv = get_predicate_slice(p->get_decl()); ptr_vector args; for (unsigned i = 0; i < bv.size(); ++i) { if (!bv.get(i)) { args.push_back(p->get_arg(i)); } } q = m.mk_app(qd, args.size(), args.c_ptr()); } else { q = p; } } void mk_slice::update_rule(rule& r, rule_set& dst) { rule_ref new_rule(rm); if (rule_updated(r)) { init_vars(r); app_ref_vector tail(m); app_ref head(m); update_predicate(r.get_head(), head); for (unsigned i = 0; i < r.get_uninterpreted_tail_size(); ++i) { app_ref t(m); update_predicate(r.get_tail(i), t); tail.push_back(t); } expr_ref_vector conjs = get_tail_conjs(r); m_solved_vars.reset(); for (unsigned i = 0; i < conjs.size(); ++i) { expr* e = conjs[i].get(); tail.push_back(to_app(e)); } new_rule = rm.mk(head.get(), tail.size(), tail.c_ptr(), (const bool*) nullptr, r.name()); rm.fix_unbound_vars(new_rule, false); TRACE("dl", r.display(m_ctx, tout << "replacing:\n"); new_rule->display(m_ctx, tout << "by:\n");); if (m_ctx.generate_proof_trace()) { rm.mk_rule_asserted_proof(*new_rule.get()); } } else { new_rule = &r; } dst.add_rule(new_rule.get()); if (m_pc) { m_pc->insert(&r, new_rule.get(), 0, nullptr); } } void mk_slice::update_rules(rule_set const& src, rule_set& dst) { for (unsigned i = 0; i < src.get_num_rules(); ++i) { update_rule(*src.get_rule(i), dst); } } rule_set * mk_slice::operator()(rule_set const & src) { rule_manager& rm = m_ctx.get_rule_manager(); for (unsigned i = 0; i < src.get_num_rules(); ++i) { if (rm.has_quantifiers(*src.get_rule(i))) { return nullptr; } } ref spc; ref smc; if (m_ctx.generate_proof_trace()) { spc = alloc(slice_proof_converter, m_ctx); } if (m_ctx.get_model_converter()) { smc = alloc(slice_model_converter, *this, m); } m_pc = spc.get(); m_mc = smc.get(); reset(); saturate(src); rule_set* result = alloc(rule_set, m_ctx); declare_predicates(src, *result); if (m_predicates.empty()) { // nothing could be sliced. dealloc(result); return nullptr; } TRACE("dl", display(tout);); update_rules(src, *result); TRACE("dl", result->display(tout);); if (m_mc) { obj_map::iterator it = m_sliceable.begin(), end = m_sliceable.end(); for (; it != end; ++it) { m_mc->add_sliceable(it->m_key, it->m_value); } } m_ctx.add_proof_converter(spc.get()); m_ctx.add_model_converter(smc.get()); return result; } }; z3-z3-4.8.7/src/muz/transforms/dl_mk_slice.h000066400000000000000000000055271356505360400206560ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_mk_slice.h Abstract: Author: Krystof Hoder (t-khoder) 2010-10-4. Revision History: --*/ #ifndef DL_MK_SLICE_H_ #define DL_MK_SLICE_H_ #include "muz/base/dl_context.h" #include "muz/base/dl_rule_set.h" #include "util/uint_set.h" #include "muz/base/dl_rule_transformer.h" namespace datalog { /** \brief Implements a slicing rule transformation. */ class mk_slice : public rule_transformer::plugin { class slice_proof_converter; class slice_model_converter; context& m_ctx; ast_manager& m; rule_manager& rm; svector m_input; svector m_output; expr_ref_vector m_solved_vars; svector m_var_is_sliceable; obj_map m_predicates; obj_map m_sliceable; ast_ref_vector m_pinned; slice_proof_converter* m_pc; slice_model_converter* m_mc; void reset(); void init(rule_set const& source); void saturate(rule_set const& source); void display(std::ostream& out); bool prune_rule(rule& r); void init_vars(rule& r); void init_vars(app* p, bool is_output, bool is_neg_tail); bool finalize_vars(app* p); unsigned num_vars() const { return m_input.size(); } bit_vector& get_predicate_slice(func_decl* p); bit_vector& get_predicate_slice(app* p) { return get_predicate_slice(p->get_decl()); } bool is_eq(expr* e, unsigned& v, expr_ref& r); void add_free_vars(uint_set& s, expr* e); void add_var(unsigned idx); bool is_output(expr* e); bool is_output(unsigned idx); void update_rules(rule_set const& src, rule_set& dst); void update_rule(rule& r, rule_set& dst); expr_ref_vector get_tail_conjs(rule const& r); void declare_predicates(rule_set const& src, rule_set& dst); bool rule_updated(rule const& r); void update_predicate(app* p, app_ref& q); void filter_unique_vars(rule& r); void solve_vars(rule& r, uint_set& used_vars, uint_set& parameter_vars); public: /** \brief Create slice rule transformer for \c goal predicate. When applying the transformer, the \c goal must be present in the \c rule_set that is being transformed. */ mk_slice(context & ctx); ~mk_slice() override { } rule_set * operator()(rule_set const & source) override; func_decl* get_predicate(func_decl* p) { func_decl* q = p; m_predicates.find(p, q); return q; } obj_map const& get_predicates() { return m_predicates; } }; }; #endif /* DL_MK_SLICE_H_ */ z3-z3-4.8.7/src/muz/transforms/dl_mk_subsumption_checker.cpp000066400000000000000000000311771356505360400241660ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_mk_subsumption_checker.cpp Abstract: Rule transformer which checks for subsumption (currently just for subsumption with total relations) Author: Krystof Hoder (t-khoder) 2011-10-01. Revision History: --*/ #include #include "ast/ast_pp.h" #include "ast/rewriter/rewriter.h" #include "ast/rewriter/rewriter_def.h" #include "muz/transforms/dl_mk_subsumption_checker.h" #include "muz/base/fp_params.hpp" #include "tactic/generic_model_converter.h" namespace datalog { // ----------------------------------- // // mk_subsumption_checker // // ----------------------------------- bool mk_subsumption_checker::is_total_rule(const rule * r) { if (r->get_tail_size() != 0) { return false; } unsigned pt_len = r->get_positive_tail_size(); if(pt_len != r->get_uninterpreted_tail_size()) { // we don't expect rules with negative tails to be total return false; } for (unsigned i = 0; i < pt_len; i++) { func_decl * tail_pred = r->get_tail(i)->get_decl(); if (!m_total_relations.contains(tail_pred)) { // this rule has a non-total predicate in the tail return false; } } unsigned t_len = r->get_positive_tail_size(); for(unsigned i = pt_len; i < t_len; i++) { SASSERT(!r->is_neg_tail(i)); //we assume interpreted tail not to be negated if (!m.is_true(r->get_tail(i))) { //this rule has an interpreted tail which is not constant true return false; } } var_idx_set head_vars; app * head = r->get_head(); unsigned arity = head->get_num_args(); for(unsigned i=0; iget_arg(i); if(!is_var(arg)) { return false; } unsigned idx = to_var(arg)->get_idx(); if(head_vars.contains(idx)) { return false; } head_vars.insert(idx); } SASSERT(head_vars.num_elems()==arity); return true; } void mk_subsumption_checker::on_discovered_total_relation(func_decl * pred, rule * r) { //this should be rule marking a new relation as total SASSERT(!m_total_relations.contains(pred)); SASSERT(!r || pred==r->get_decl()); SASSERT(!r || is_total_rule(r)); m_total_relations.insert(pred); m_total_relation_defining_rules.insert(pred, r); m_have_new_total_rule = true; if(r) { m_ref_holder.push_back(r); } } void mk_subsumption_checker::scan_for_total_rules(const rule_set & rules) { bool new_discovered; //we cycle through the rules until we keep discovering new total relations //(discovering a total relation might reveal other total relations) do { new_discovered = false; rule_set::iterator rend = rules.end(); for(rule_set::iterator rit = rules.begin(); rit!=rend; ++rit) { rule * r = *rit; func_decl * head_pred = r->get_decl(); if(is_total_rule(r) && !m_total_relations.contains(head_pred)) { on_discovered_total_relation(head_pred, r); new_discovered = true; } } } while(new_discovered); } bool mk_subsumption_checker::transform_rule(rule * r, rule_subsumption_index& subs_index, rule_ref & res) { unsigned u_len = r->get_uninterpreted_tail_size(); unsigned len = r->get_tail_size(); if(u_len==0) { res = r; return true; } app_ref head(r->get_head(), m); app_ref_vector tail(m); svector tail_neg; for(unsigned i=0; iget_tail(i); bool neg = r->is_neg_tail(i); if(m_total_relations.contains(tail_atom->get_decl()) || subs_index.is_subsumed(tail_atom)) { if(neg) { //rule contains negated total relation, this means that it is unsatisfiable //and can be removed return false; } else { //we remove total relations from the tail continue; } } if(!neg && head.get()==tail_atom) { //rule contains its head positively in the tail, therefore //it will never add any new facts to the relation, so it //can be removed return false; } tail.push_back(tail_atom); tail_neg.push_back(neg); } if(tail.size()==u_len) { res = r; return true; } //we just copy the interpreted part of the tail for(unsigned i=u_len; iget_tail(i)); tail_neg.push_back(r->is_neg_tail(i)); } SASSERT(tail.size()==tail_neg.size()); res = m_context.get_rule_manager().mk(head, tail.size(), tail.c_ptr(), tail_neg.c_ptr(), r->name()); res->set_accounting_parent_object(m_context, r); m_context.get_rule_manager().fix_unbound_vars(res, true); m_context.get_rule_manager().mk_rule_rewrite_proof(*r, *res.get()); return true; } bool rule_size_comparator(rule * r1, rule * r2) { return r1->get_tail_size() < r2->get_tail_size(); } bool mk_subsumption_checker::transform_rules(const rule_set & orig, rule_set & tgt) { bool modified = false; func_decl_set total_relations_with_included_rules; rule_subsumption_index subs_index(m_context); rule_ref_vector orig_rules(m_context.get_rule_manager()); orig_rules.append(orig.get_num_rules(), orig.begin()); //before traversing we sort rules so that the shortest are in the beginning. //this will help make subsumption checks more efficient std::sort(orig_rules.c_ptr(), orig_rules.c_ptr() + orig_rules.size(), rule_size_comparator); for (rule * r : orig_rules) { func_decl * head_pred = r->get_decl(); if (m_total_relations.contains(head_pred)) { if (!orig.is_output_predicate(head_pred) || total_relations_with_included_rules.contains(head_pred)) { //We just skip definitions of total non-output relations as //we'll eliminate them from the problem. //We also skip rules of total output relations for which we have //already output the rule which implies their totality. modified = true; continue; } rule * defining_rule = m_total_relation_defining_rules.find(head_pred); if (defining_rule) { rule_ref totality_rule(m_context.get_rule_manager()); VERIFY(transform_rule(defining_rule, subs_index, totality_rule)); if(defining_rule!=totality_rule) { modified = true; } tgt.add_rule(totality_rule); SASSERT(totality_rule->get_decl()==head_pred); } else { modified = true; } total_relations_with_included_rules.insert(head_pred); continue; } rule_ref new_rule(m_context.get_rule_manager()); if (!transform_rule(r, subs_index, new_rule)) { modified = true; continue; } if (m_new_total_relation_discovery_during_transformation && is_total_rule(new_rule)) { on_discovered_total_relation(head_pred, new_rule.get()); } if (subs_index.is_subsumed(new_rule)) { modified = true; continue; } if (new_rule.get()!=r) { modified = true; } tgt.add_rule(new_rule); subs_index.add(new_rule); } tgt.inherit_predicates(orig); if (!m_total_relations.empty() && m_context.get_model_converter()) { generic_model_converter* mc0 = alloc(generic_model_converter, m, "dl-subsumption"); for (func_decl* p : m_total_relations) { mc0->add(p, m.mk_true()); } m_context.add_model_converter(mc0); } TRACE("dl", tout << "original set size: "<try_get_size(pred, rel_sz)) { continue; } unsigned arity = pred->get_arity(); if (arity > 30) { continue; } //for now we only check booleans domains for(unsigned i=0; iget_domain(i))) { goto next_pred; } } { unsigned total_size = 1< * head_store; if(m_ground_unconditional_rule_heads.find(pred, head_store)) { //Some relations may receive facts by ground unconditioned rules. //We scanned for those earlier, so now we check whether we cannot get a //better estimate of relation size from these. unsigned gnd_rule_cnt = head_store->size(); if(gnd_rule_cnt>rel_sz) { rel_sz = gnd_rule_cnt; } } SASSERT(total_size>=rel_sz); if(total_size==rel_sz) { on_discovered_total_relation(pred, nullptr); } } next_pred:; } } void mk_subsumption_checker::collect_ground_unconditional_rule_heads(const rule_set & rules) { rule_set::iterator rend = rules.end(); for(rule_set::iterator rit = rules.begin(); rit!=rend; ++rit) { rule * r = *rit; func_decl * pred = r->get_decl(); if(r->get_tail_size()!=0) { continue; } app * head = r->get_head(); unsigned arity = pred->get_arity(); for(unsigned i=0; iget_arg(i); if(!is_app(arg)) { goto next_rule; } } if(!m_ground_unconditional_rule_heads.contains(pred)) { m_ground_unconditional_rule_heads.insert(pred, alloc(obj_hashtable)); } m_ground_unconditional_rule_heads.find(pred)->insert(head); next_rule:; } } rule_set * mk_subsumption_checker::operator()(rule_set const & source) { // TODO mc if (!m_context.get_params ().xform_subsumption_checker()) return nullptr; m_have_new_total_rule = false; collect_ground_unconditional_rule_heads(source); scan_for_relations_total_due_to_facts(source); scan_for_total_rules(source); m_have_new_total_rule = false; rule_set * res = alloc(rule_set, m_context); bool modified = transform_rules(source, *res); if (!m_have_new_total_rule && !modified) { dealloc(res); return nullptr; } //During the construction of the new set we may discover new total relations //(by quantifier elimination on the uninterpreted tails). SASSERT(m_new_total_relation_discovery_during_transformation || !m_have_new_total_rule); while (m_have_new_total_rule) { m_have_new_total_rule = false; rule_set * old = res; res = alloc(rule_set, m_context); transform_rules(*old, *res); dealloc(old); } return res; } }; z3-z3-4.8.7/src/muz/transforms/dl_mk_subsumption_checker.h000066400000000000000000000051201356505360400236200ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: mk_subsumption_checker.h Abstract: Rule transformer which checks for subsumption (currently just for subsumption with total relations) Author: Krystof Hoder (t-khoder) 2011-10-01. Revision History: --*/ #ifndef DL_MK_SUBSUMPTION_CHECKER_H_ #define DL_MK_SUBSUMPTION_CHECKER_H_ #include "muz/base/dl_context.h" #include "muz/base/dl_rule_transformer.h" #include "muz/base/dl_rule_subsumption_index.h" namespace datalog { class mk_subsumption_checker : public rule_transformer::plugin { ast_manager & m; context & m_context; rule_ref_vector m_ref_holder; func_decl_set m_total_relations; /** Map that for each relation contains the rule which implies its totality. If the totality is due to the relation containing all facts, the rule stored here is zero*/ obj_map m_total_relation_defining_rules; /** Contains heads of rules of shape R(c1,c2,...cN). grouped by their predicate. This information helps to improve the results of the scan_for_relations_total_due_to_facts() function. */ obj_map *> m_ground_unconditional_rule_heads; bool m_have_new_total_rule; bool m_new_total_relation_discovery_during_transformation; bool is_total_rule(const rule * r); /** Function to be called when a new total relation is discovered */ void on_discovered_total_relation(func_decl * pred, rule * r); void scan_for_total_rules(rule_set const& rules); void scan_for_relations_total_due_to_facts(rule_set const& rules); void collect_ground_unconditional_rule_heads(const rule_set & rules); /** Return false if rule is unsatisfiable */ bool transform_rule(rule * r, rule_subsumption_index& subs_index, rule_ref & res); /** Return false if the rule set hasn't changed */ bool transform_rules(const rule_set & orig, rule_set & tgt); public: mk_subsumption_checker(context & ctx, unsigned priority=31000) : plugin(priority), m(ctx.get_manager()), m_context(ctx), m_ref_holder(ctx.get_rule_manager()), m_new_total_relation_discovery_during_transformation(true) {} ~mk_subsumption_checker() override { reset_dealloc_values(m_ground_unconditional_rule_heads); } rule_set * operator()(rule_set const & source) override; }; }; #endif /* DL_MK_SUBSUMPTION_CHECKER_H_ */ z3-z3-4.8.7/src/muz/transforms/dl_mk_synchronize.cpp000066400000000000000000000325071356505360400224630ustar00rootroot00000000000000/*++ Copyright (c) 2017-2018 Saint-Petersburg State University Module Name: dl_mk_synchronize.h Abstract: Rule transformer that attempts to merge recursive iterations relaxing the shape of the inductive invariant. Author: Dmitry Mordvinov (dvvrd) 2017-05-24 Lidiia Chernigovskaia (LChernigovskaya) 2017-10-20 Revision History: --*/ #include "muz/transforms/dl_mk_synchronize.h" #include namespace datalog { typedef mk_synchronize::item_set_vector item_set_vector; mk_synchronize::mk_synchronize(context& ctx, unsigned priority): rule_transformer::plugin(priority, false), m_ctx(ctx), m(ctx.get_manager()), rm(ctx.get_rule_manager()) {} bool mk_synchronize::is_recursive(rule &r, func_decl &decl) const { func_decl *hdecl = r.get_head()->get_decl(); // AG: shouldn't decl appear in the body? if (hdecl == &decl) return true; auto & strata = m_stratifier->get_strats(); unsigned num_of_stratum = m_stratifier->get_predicate_strat(hdecl); return strata[num_of_stratum]->contains(&decl); } bool mk_synchronize::has_recursive_premise(app * app) const { func_decl* app_decl = app->get_decl(); if (m_deps->get_deps(app_decl).contains(app_decl)) { return true; } rule_stratifier::comp_vector const & strata = m_stratifier->get_strats(); unsigned num_of_stratum = m_stratifier->get_predicate_strat(app_decl); return strata[num_of_stratum]->size() > 1; } item_set_vector mk_synchronize::add_merged_decls(ptr_vector & apps) { unsigned sz = apps.size(); item_set_vector merged_decls; merged_decls.resize(sz); auto & strata = m_stratifier->get_strats(); for (unsigned j = 0; j < sz; ++j) { unsigned nos; nos = m_stratifier->get_predicate_strat(apps[j]->get_decl()); merged_decls[j] = strata[nos]; } return merged_decls; } void mk_synchronize::add_new_rel_symbols(unsigned idx, item_set_vector const & decls, ptr_vector & decls_buf, bool & was_added) { if (idx >= decls.size()) { string_buffer<> buffer; ptr_vector domain; for (auto &d : decls_buf) { buffer << d->get_name() << "!!"; domain.append(d->get_arity(), d->get_domain()); } symbol new_name = symbol(buffer.c_str()); if (!m_cache.contains(new_name)) { was_added = true; func_decl* orig = decls_buf[0]; func_decl* product_pred = m_ctx.mk_fresh_head_predicate(new_name, symbol::null, domain.size(), domain.c_ptr(), orig); m_cache.insert(new_name, product_pred); } return; } // -- compute Cartesian product of decls, and create a new // -- predicate for each element of the product for (auto &p : *decls[idx]) { decls_buf[idx] = p; add_new_rel_symbols(idx + 1, decls, decls_buf, was_added); } } void mk_synchronize::replace_applications(rule & r, rule_set & rules, ptr_vector & apps) { app_ref replacing = product_application(apps); ptr_vector new_tail; svector new_tail_neg; unsigned n = r.get_tail_size() - apps.size() + 1; unsigned tail_idx = 0; new_tail.resize(n); new_tail_neg.resize(n); new_tail[0] = replacing; new_tail_neg[0] = false; for (unsigned i = 0; i < r.get_positive_tail_size(); ++i) { app* tail = r.get_tail(i); if (!apps.contains(tail)) { ++tail_idx; new_tail[tail_idx] = tail; new_tail_neg[tail_idx] = false; } } for (unsigned i = r.get_positive_tail_size(); i < r.get_uninterpreted_tail_size(); ++i) { ++tail_idx; new_tail[tail_idx] = r.get_tail(i); new_tail_neg[tail_idx] = true; } for (unsigned i = r.get_uninterpreted_tail_size(); i < r.get_tail_size(); ++i) { ++tail_idx; new_tail[tail_idx] = r.get_tail(i); new_tail_neg[tail_idx] = false; } rule_ref new_rule(rm); new_rule = rm.mk(r.get_head(), tail_idx + 1, new_tail.c_ptr(), new_tail_neg.c_ptr(), symbol::null, false); rules.replace_rule(&r, new_rule.get()); } rule_ref mk_synchronize::rename_bound_vars_in_rule(rule * r, unsigned & var_idx) { // AG: shift all variables in a rule so that lowest var index is var_idx? // AG: update var_idx in the process? ptr_vector sorts; r->get_vars(m, sorts); expr_ref_vector revsub(m); revsub.resize(sorts.size()); for (unsigned i = 0; i < sorts.size(); ++i) { if (sorts[i]) { revsub[i] = m.mk_var(var_idx++, sorts[i]); } } rule_ref new_rule(rm); new_rule = rm.mk(r); rm.substitute(new_rule, revsub.size(), revsub.c_ptr()); return new_rule; } vector mk_synchronize::rename_bound_vars(item_set_vector const & heads, rule_set & rules) { // AG: is every item_set in heads corresponds to rules that are merged? // AG: why are bound variables renamed in the first place? // AG: the data structure seems too complex vector result; unsigned var_idx = 0; for (auto item : heads) { rule_ref_vector dst_vector(rm); for (auto *head : *item) { for (auto *r : rules.get_predicate_rules(head)) { rule_ref new_rule = rename_bound_vars_in_rule(r, var_idx); dst_vector.push_back(new_rule.get()); } } result.push_back(dst_vector); } return result; } void mk_synchronize::add_rec_tail(vector< ptr_vector > & recursive_calls, app_ref_vector & new_tail, svector & new_tail_neg, unsigned & tail_idx) { unsigned max_sz = 0; for (auto &rc : recursive_calls) max_sz= std::max(rc.size(), max_sz); unsigned n = recursive_calls.size(); ptr_vector merged_recursive_calls; for (unsigned j = 0; j < max_sz; ++j) { merged_recursive_calls.reset(); merged_recursive_calls.resize(n); for (unsigned i = 0; i < n; ++i) { unsigned sz = recursive_calls[i].size(); merged_recursive_calls[i] = j < sz ? recursive_calls[i][j] : recursive_calls[i][sz - 1]; } ++tail_idx; new_tail[tail_idx] = product_application(merged_recursive_calls); new_tail_neg[tail_idx] = false; } } void mk_synchronize::add_non_rec_tail(rule & r, app_ref_vector & new_tail, svector & new_tail_neg, unsigned & tail_idx) { for (unsigned i = 0, sz = r.get_positive_tail_size(); i < sz; ++i) { app* tail = r.get_tail(i); if (!is_recursive(r, *tail)) { ++tail_idx; new_tail[tail_idx] = tail; new_tail_neg[tail_idx] = false; } } for (unsigned i = r.get_positive_tail_size(), sz = r.get_uninterpreted_tail_size() ; i < sz; ++i) { ++tail_idx; new_tail[tail_idx] = r.get_tail(i); new_tail_neg[tail_idx] = true; } for (unsigned i = r.get_uninterpreted_tail_size(), sz = r.get_tail_size(); i < sz; ++i) { ++tail_idx; new_tail[tail_idx] = r.get_tail(i); new_tail_neg[tail_idx] = r.is_neg_tail(i); } } app_ref mk_synchronize::product_application(ptr_vector const &apps) { unsigned args_num = 0; string_buffer<> buffer; // AG: factor out into mk_name for (auto *app : apps) { buffer << app->get_decl()->get_name() << "!!"; args_num += app->get_num_args(); } symbol name = symbol(buffer.c_str()); SASSERT(m_cache.contains(name)); func_decl * pred = m_cache[name]; ptr_vector args; args.resize(args_num); unsigned idx = 0; for (auto *a : apps) { for (unsigned i = 0, sz = a->get_num_args(); i < sz; ++i, ++idx) args[idx] = a->get_arg(i); } return app_ref(m.mk_app(pred, args_num, args.c_ptr()), m); } rule_ref mk_synchronize::product_rule(rule_ref_vector const & rules) { unsigned n = rules.size(); string_buffer<> buffer; bool first_rule = true; for (auto it = rules.begin(); it != rules.end(); ++it, first_rule = false) { if (!first_rule) { buffer << "+"; } buffer << (*it)->name(); } ptr_vector heads; heads.resize(n); for (unsigned i = 0; i < n; ++i) { heads[i] = rules[i]->get_head(); } app_ref product_head = product_application(heads); unsigned product_tail_length = 0; bool has_recursion = false; vector< ptr_vector > recursive_calls; recursive_calls.resize(n); for (unsigned i = 0; i < n; ++i) { rule& rule = *rules[i]; product_tail_length += rule.get_tail_size(); for (unsigned j = 0; j < rule.get_positive_tail_size(); ++j) { app* tail = rule.get_tail(j); if (is_recursive(rule, *tail)) { has_recursion = true; recursive_calls[i].push_back(tail); } } if (recursive_calls[i].empty()) { recursive_calls[i].push_back(rule.get_head()); } } app_ref_vector new_tail(m); svector new_tail_neg; new_tail.resize(product_tail_length); new_tail_neg.resize(product_tail_length); unsigned tail_idx = -1; if (has_recursion) { add_rec_tail(recursive_calls, new_tail, new_tail_neg, tail_idx); } for (rule_vector::const_iterator it = rules.begin(); it != rules.end(); ++it) { rule& rule = **it; add_non_rec_tail(rule, new_tail, new_tail_neg, tail_idx); } rule_ref new_rule(rm); new_rule = rm.mk(product_head, tail_idx + 1, new_tail.c_ptr(), new_tail_neg.c_ptr(), symbol(buffer.c_str()), false); rm.fix_unbound_vars(new_rule, false); return new_rule; } void mk_synchronize::merge_rules(unsigned idx, rule_ref_vector & buf, vector const & merged_rules, rule_set & all_rules) { if (idx >= merged_rules.size()) { rule_ref product = product_rule(buf); all_rules.add_rule(product.get()); return; } for (auto *r : merged_rules[idx]) { buf[idx] = r; merge_rules(idx + 1, buf, merged_rules, all_rules); } } void mk_synchronize::merge_applications(rule & r, rule_set & rules) { ptr_vector non_recursive_preds; obj_hashtable apps; for (unsigned i = 0; i < r.get_positive_tail_size(); ++i) { app* t = r.get_tail(i); if (!is_recursive(r, *t) && has_recursive_premise(t)) { apps.insert(t); } } if (apps.size() < 2) return; for (auto *a : apps) non_recursive_preds.push_back(a); item_set_vector merged_decls = add_merged_decls(non_recursive_preds); unsigned n = non_recursive_preds.size(); ptr_vector decls_buf; decls_buf.resize(n); bool was_added = false; add_new_rel_symbols(0, merged_decls, decls_buf, was_added); if (was_added){ rule_ref_vector rules_buf(rm); rules_buf.resize(n); vector renamed_rules = rename_bound_vars(merged_decls, rules); merge_rules(0, rules_buf, renamed_rules, rules); } replace_applications(r, rules, non_recursive_preds); m_deps->populate(rules); m_stratifier = alloc(rule_stratifier, *m_deps); } rule_set * mk_synchronize::operator()(rule_set const & source) { rule_set* rules = alloc(rule_set, m_ctx); rules->inherit_predicates(source); for (auto *r : source) { rules->add_rule(r); } m_deps = alloc(rule_dependencies, m_ctx); m_deps->populate(*rules); m_stratifier = alloc(rule_stratifier, *m_deps); unsigned current_rule = 0; while (current_rule < rules->get_num_rules()) { rule *r = rules->get_rule(current_rule); merge_applications(*r, *rules); ++current_rule; } return rules; } }; z3-z3-4.8.7/src/muz/transforms/dl_mk_synchronize.h000066400000000000000000000103471356505360400221260ustar00rootroot00000000000000/*++ Copyright (c) 2017-2018 Saint-Petersburg State University Module Name: dl_mk_synchronize.h Abstract: Rule transformer that attempts to merge recursive iterations relaxing the shape of the inductive invariant. Example: Q(z) :- A(x), B(y), phi1(x,y,z). A(x) :- A(x'), phi2(x,x'). A(x) :- phi3(x). B(y) :- C(y'), phi4(y,y'). C(y) :- B(y'), phi5(y,y'). B(y) :- phi6(y). Transformed clauses: Q(z) :- AB(x,y), phi1(x,y,z). AB(x,y) :- AC(x',y'), phi2(x,x'), phi4(y,y'). AC(x,y) :- AB(x',y'), phi2(x,x'), phi5(y,y'). AB(x,y) :- AC(x, y'), phi3(x), phi4(y,y'). AC(x,y) :- AB(x, y'), phi3(x), phi5(y,y'). AB(x,y) :- AB(x',y), phi2(x,x'), phi6(y). AB(x,y) :- phi3(x), phi6(y). Author: Dmitry Mordvinov (dvvrd) 2017-05-24 Lidiia Chernigovskaia (LChernigovskaya) 2017-10-20 Revision History: --*/ #ifndef DL_MK_SYNCHRONIZE_H_ #define DL_MK_SYNCHRONIZE_H_ #include"muz/base/dl_context.h" #include"muz/base/dl_rule_set.h" #include"util/uint_set.h" #include"muz/base/dl_rule_transformer.h" #include"muz/transforms/dl_mk_rule_inliner.h" namespace datalog { /** \brief Implements a synchronous product transformation. */ class mk_synchronize : public rule_transformer::plugin { public: typedef ptr_vector item_set_vector; private: context& m_ctx; ast_manager& m; rule_manager& rm; scoped_ptr m_deps; scoped_ptr m_stratifier; // symbol table that maps new predicate names to corresponding // func_decl map m_cache; /// returns true if decl is recursive in the given rule /// requires that decl appears in the tail of r bool is_recursive(rule &r, func_decl &decl) const; bool is_recursive(rule &r, expr &e) const { SASSERT(is_app(&e)); return is_app(&e) && is_recursive(r, *to_app(&e)->get_decl()); } /// returns true if the top-level predicate of app is recursive bool has_recursive_premise(app * app) const; item_set_vector add_merged_decls(ptr_vector & apps); /** Compute Cartesian product of decls and create a new predicate for each element. For example, if decls is ( (a, b), (c, d) ) ) create new predicates: a!!c, a!!d, b!!c, and b!!d */ void add_new_rel_symbols(unsigned idx, item_set_vector const & decls, ptr_vector & buf, bool & was_added); /** Convert vector of predicate apps into a single app. For example, (Foo a) (Bar b) becomes (Foo!!Bar a b) */ app_ref product_application(ptr_vector const & apps); /** Replace a given rule by a rule in which conjunction of predicates is replaced by a single product predicate */ void replace_applications(rule & r, rule_set & rules, ptr_vector & apps); rule_ref rename_bound_vars_in_rule(rule * r, unsigned & var_idx); vector rename_bound_vars(item_set_vector const & heads, rule_set & rules); void add_rec_tail(vector< ptr_vector > & recursive_calls, app_ref_vector & new_tail, svector & new_tail_neg, unsigned & tail_idx); void add_non_rec_tail(rule & r, app_ref_vector & new_tail, svector & new_tail_neg, unsigned & tail_idx); rule_ref product_rule(rule_ref_vector const & rules); void merge_rules(unsigned idx, rule_ref_vector & buf, vector const & merged_rules, rule_set & all_rules); void merge_applications(rule & r, rule_set & rules); public: /** \brief Create synchronous product transformer. */ mk_synchronize(context & ctx, unsigned priority = 22500); rule_set * operator()(rule_set const & source) override; }; }; #endif /* DL_MK_SYNCHRONIZE_H_ */ z3-z3-4.8.7/src/muz/transforms/dl_mk_unbound_compressor.cpp000066400000000000000000000347321356505360400240400ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_mk_unbound_compressor.cpp Abstract: Author: Krystof Hoder (t-khoder) 2010-10-04. Revision History: --*/ #include #include #include "muz/transforms/dl_mk_unbound_compressor.h" namespace datalog { mk_unbound_compressor::mk_unbound_compressor(context & ctx) : plugin(500), m_context(ctx), m(ctx.get_manager()), rm(ctx.get_rule_manager()), m_rules(rm), m_pinned(m) { } void mk_unbound_compressor::reset() { m_rules.reset(); m_todo.reset(); m_in_progress.reset(); m_map.reset(); m_pinned.reset(); } bool mk_unbound_compressor::is_unbound_argument(rule * r, unsigned head_index) { app * head = r->get_head(); expr * head_arg = head->get_arg(head_index); unsigned var_idx; return is_var(head_arg, var_idx) && rm.collect_tail_vars(r).contains(var_idx); } void mk_unbound_compressor::add_task(func_decl * pred, unsigned arg_index) { SASSERT(pred->get_arity() > 0); c_info ci(pred, arg_index); if (m_map.contains(ci)) { return; //this task was already added } unsigned parent_arity = pred->get_arity(); sort * const * parent_domain = pred->get_domain(); symbol const& parent_name = pred->get_name(); unsigned arity = parent_arity-1; ptr_vector domain; for (unsigned i = 0; i < parent_arity; i++) { if (i != arg_index) { domain.push_back(parent_domain[i]); } } std::stringstream name_suffix; name_suffix << "compr_arg_" << arg_index; func_decl * cpred = m_context.mk_fresh_head_predicate(parent_name, symbol(name_suffix.str().c_str()), arity, domain.c_ptr(), pred); m_pinned.push_back(cpred); m_pinned.push_back(pred); m_todo.push_back(ci); m_map.insert(ci, cpred); TRACE("dl", tout << "inserting: " << pred->get_name() << " " << arg_index << " for " << cpred->get_name() << "\n";); } void mk_unbound_compressor::detect_tasks(rule_set const& source, unsigned rule_index) { rule * r = m_rules.get(rule_index); var_idx_set& tail_vars = rm.collect_tail_vars(r); app * head = r->get_head(); func_decl * head_pred = head->get_decl(); if (source.is_output_predicate(head_pred)) { //we don't compress output predicates return; } unsigned n = head_pred->get_arity(); rm.get_counter().reset(); rm.get_counter().count_vars(head, 1); for (unsigned i = 0; i < n; i++) { expr * arg = head->get_arg(i); unsigned var_idx; if (is_var(arg, var_idx) && !tail_vars.contains(var_idx) && (1 == rm.get_counter().get(var_idx))) { TRACE("dl", r->display(m_context, tout << "Compress: ");); add_task(head_pred, i); break; //we compress out the unbound arguments one by one } } } // // l_undef if nothing to compress. // l_true if compressed. // l_false if removed. // lbool mk_unbound_compressor::try_compress(rule_set const& source, unsigned rule_index) { rule * r = m_rules.get(rule_index); var_idx_set& tail_vars = rm.collect_tail_vars(r); app * head = r->get_head(); func_decl * head_pred = head->get_decl(); unsigned head_arity = head_pred->get_arity(); rm.get_counter().reset(); rm.get_counter().count_vars(head); unsigned arg_index; for (arg_index = 0; arg_index < head_arity; arg_index++) { expr * arg = head->get_arg(arg_index); unsigned var_idx; if (is_var(arg, var_idx) && !tail_vars.contains(var_idx) && (rm.get_counter().get(var_idx) == 1) && m_in_progress.contains(c_info(head_pred, arg_index))) { break; } } if (arg_index == head_arity) { //we didn't find anything to compress return l_undef; } c_info ci(head_pred, arg_index); SASSERT(arg_index < head_arity); SASSERT(m_in_progress.contains(ci)); func_decl * cpred = m_map.find(ci); ptr_vector cargs; for (unsigned i=0; i < head_arity; i++) { if (i != arg_index) { cargs.push_back(head->get_arg(i)); } } app_ref chead(m.mk_app(cpred, head_arity-1, cargs.c_ptr()), m); m_modified = true; if (r->get_tail_size()==0 && m_context.get_rule_manager().is_fact(chead)) { m_non_empty_rels.insert(cpred); m_context.add_fact(chead); //remove the rule that became fact by placing the last rule on its place m_head_occurrence_ctr.dec(m_rules.get(rule_index)->get_decl()); unsigned new_size = m_rules.size() - 1; rule* last_rule = m_rules.get(new_size); TRACE("dl", tout << "remove\n"; r->display(m_context, tout); tout << "shift\n"; last_rule->display(m_context, tout);); if (rule_index < new_size) { m_rules.set(rule_index, last_rule); } m_rules.shrink(new_size); return l_false; } else { rule_ref new_rule(m_context.get_rule_manager().mk(r, chead, r->name()), m_context.get_rule_manager()); new_rule->set_accounting_parent_object(m_context, r); m_head_occurrence_ctr.dec(m_rules.get(rule_index)->get_decl()); TRACE("dl", tout << "remove\n"; r->display(m_context, tout); tout << "set\n"; new_rule->display(m_context, tout);); m_rules.set(rule_index, new_rule); m_head_occurrence_ctr.inc(m_rules.get(rule_index)->get_decl()); detect_tasks(source, rule_index); return l_true; } } rule_ref mk_unbound_compressor::mk_decompression_rule(rule * r, unsigned tail_index, unsigned arg_index) { rule_ref res(m_context.get_rule_manager()); app * orig_dtail = r->get_tail(tail_index); //dtail ~ decompressed tail c_info ci(orig_dtail->get_decl(), arg_index); TRACE("dl", tout << "retrieving: " << ci.first->get_name() << " " << ci.second << "\n";); func_decl * dtail_pred = m_map.find(ci); ptr_vector dtail_args; unsigned orig_dtail_arity = orig_dtail->get_num_args(); for (unsigned i = 0; i < orig_dtail_arity; i++) { if (i != arg_index) { dtail_args.push_back(orig_dtail->get_arg(i)); } } SASSERT(dtail_args.size()==dtail_pred->get_arity()); app_ref dtail(m.mk_app(dtail_pred, dtail_args.size(), dtail_args.c_ptr()), m); svector tails_negated; app_ref_vector tails(m); unsigned tail_len = r->get_tail_size(); for (unsigned i = 0; i < tail_len; i++) { tails_negated.push_back(r->is_neg_tail(i)); if (i == tail_index && !r->is_neg_tail(i)) { tails.push_back(dtail); } else { tails.push_back(r->get_tail(i)); } } // Accumulate negated filtered rule instead // of replacing the original predicate. if (r->is_neg_tail(tail_index)) { tails_negated.push_back(true); tails.push_back(dtail); } res = m_context.get_rule_manager().mk( r->get_head(), tails.size(), tails.c_ptr(), tails_negated.c_ptr()); res->set_accounting_parent_object(m_context, r); m_context.get_rule_manager().fix_unbound_vars(res, true); return res; } //TODO: avoid rule duplicity //If two predicates are compressed in a rule, applying decompression //to the results can cause a rule being added multiple times: //P:- R(x,y), S(x,y) //is decompressed into rules //P:- R1(x), S(x,y) //P:- R(x,y), S1(x) //and each of these rules is again decompressed giving the same rule //P:- R1(x), S1(x) //P:- R1(x), S1(x) void mk_unbound_compressor::add_decompression_rule(rule_set const& source, rule * r, unsigned tail_index, unsigned arg_index) { rule_ref new_rule = mk_decompression_rule(r, tail_index, arg_index); unsigned new_rule_index = m_rules.size(); m_rules.push_back(new_rule); TRACE("dl", r->display(m_context, tout); new_rule->display(m_context, tout); ); m_context.get_rule_manager().mk_rule_rewrite_proof(*r, *new_rule.get()); m_head_occurrence_ctr.inc(new_rule->get_decl()); detect_tasks(source, new_rule_index); m_modified = true; } void mk_unbound_compressor::replace_by_decompression_rule(rule_set const& source, unsigned rule_index, unsigned tail_index, unsigned arg_index) { rule * r = m_rules.get(rule_index); rule_ref new_rule = mk_decompression_rule(r, tail_index, arg_index); TRACE("dl", tout << "remove\n"; r->display(m_context, tout); tout << "set\n"; new_rule->display(m_context, tout);); m_rules.set(rule_index, new_rule); // we don't update the m_head_occurrence_ctr because the head predicate doesn't change detect_tasks(source, rule_index); m_modified = true; } void mk_unbound_compressor::add_in_progress_indices(unsigned_vector& arg_indices, app* p) { arg_indices.reset(); for (unsigned i = 0; i < p->get_num_args(); ++i) { if (m_in_progress.contains(c_info(p->get_decl(), i))) { SASSERT(m_map.contains(c_info(p->get_decl(), i))); arg_indices.push_back(i); } } } bool mk_unbound_compressor::decompress_rule(rule_set const& source, rule* r, unsigned_vector const& arg_indices, unsigned rule_index, unsigned tail_index) { app * t = r->get_tail(tail_index); func_decl * t_pred = t->get_decl(); bool is_negated_predicate = r->is_neg_tail(tail_index); bool replace_original_rule = false; for (unsigned i = 0; i < arg_indices.size(); ++i) { unsigned arg_index = arg_indices[i]; SASSERT(m_in_progress.contains(c_info(t_pred, arg_index))); SASSERT(m_map.contains(c_info(t_pred, arg_index))); bool can_remove_orig_rule = arg_indices.empty() && !m_non_empty_rels.contains(t_pred) && m_head_occurrence_ctr.get(t_pred) == 0; if (can_remove_orig_rule || is_negated_predicate) { replace_original_rule = true; replace_by_decompression_rule(source, rule_index, tail_index, arg_index); // NB. arg_indices becomes stale after original rule is replaced. if (is_negated_predicate && !can_remove_orig_rule) { break; } } else { add_decompression_rule(source, r, tail_index, arg_index); } } return replace_original_rule; } void mk_unbound_compressor::add_decompression_rules(rule_set const& source, unsigned rule_index) { unsigned_vector arg_indices; // this value is updated inside the loop if replace_by_decompression_rule is called rule_ref r(m_rules.get(rule_index), m_context.get_rule_manager()); unsigned utail_len = r->get_uninterpreted_tail_size(); unsigned tail_index = 0; while (tail_index < utail_len) { app * t = r->get_tail(tail_index); add_in_progress_indices(arg_indices, t); bool replace_original_rule = decompress_rule(source, r, arg_indices, rule_index, tail_index); if (replace_original_rule) { // update r with the new rule rule * new_rule = m_rules.get(rule_index); SASSERT(new_rule->get_uninterpreted_tail_size() >= utail_len); // here we check that the rule replacement didn't affect other uninterpreted literals // in the tail (aside of variable renaming) SASSERT(tail_index==0 || new_rule->get_decl(tail_index-1) == r->get_decl(tail_index-1)); r = new_rule; //we have replaced the original rule, with one that has different //content of the tail_index -th tail. we will therefore not do //tail_index++, so that we examine the new tail literal as well } else { tail_index++; } } } rule_set * mk_unbound_compressor::operator()(rule_set const & source) { // TODO mc if (!m_context.compress_unbound()) { return nullptr; } m_modified = false; SASSERT(m_rules.empty()); rel_context_base* rel = m_context.get_rel_context(); if (rel) { rel->collect_non_empty_predicates(m_non_empty_rels); } unsigned init_rule_cnt = source.get_num_rules(); for (unsigned i = 0; i < init_rule_cnt; i++) { rule * r = source.get_rule(i); m_rules.push_back(r); m_head_occurrence_ctr.inc(r->get_decl()); } for (unsigned i = 0; i < init_rule_cnt; i++) { detect_tasks(source, i); } while (!m_todo.empty()) { m_in_progress.reset(); while (!m_todo.empty()) { m_in_progress.insert(m_todo.back()); m_todo.pop_back(); } for (unsigned rule_index = 0; rule_index < m_rules.size(); ) { switch (try_compress(source, rule_index)) { case l_true: case l_undef: add_decompression_rules(source, rule_index); //m_rules.size() can be increased here ++rule_index; break; case l_false: break; } } } rule_set * result = static_cast(nullptr); if (m_modified) { result = alloc(rule_set, m_context); unsigned fin_rule_cnt = m_rules.size(); for (unsigned i=0; iadd_rule(m_rules.get(i)); } result->inherit_predicates(source); } reset(); return result; } }; z3-z3-4.8.7/src/muz/transforms/dl_mk_unbound_compressor.h000066400000000000000000000056541356505360400235060ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_mk_unbound_compressor.h Abstract: Author: Krystof Hoder (t-khoder) 2010-10-4. Revision History: --*/ #ifndef DL_MK_UNBOUND_COMPRESSOR_H_ #define DL_MK_UNBOUND_COMPRESSOR_H_ #include #include "util/map.h" #include "util/obj_pair_hashtable.h" #include "muz/base/dl_context.h" #include "muz/base/dl_rule_set.h" #include "muz/base/dl_rule_transformer.h" namespace datalog { /** \brief Functor for introducing auxiliary predicates to avoid unbound variables in rule heads. A rule P(x,_) :- T(x). is replaced by P1(x) :- T(x). and for each occurrence of P in a tail of a rule, a new rule is added with P1 in its place. */ class mk_unbound_compressor : public rule_transformer::plugin { /** predicate and index of compressed argument */ typedef std::pair c_info; typedef pair_hash,unsigned_hash> c_info_hash; /** predicates that are results of compression */ typedef map > c_map; typedef hashtable > in_progress_table; typedef svector todo_stack; context & m_context; ast_manager & m; rule_manager & rm; rule_ref_vector m_rules; bool m_modified; todo_stack m_todo; in_progress_table m_in_progress; c_map m_map; /** Relations that contain facts */ func_decl_set m_non_empty_rels; ast_counter m_head_occurrence_ctr; ast_ref_vector m_pinned; bool is_unbound_argument(rule * r, unsigned head_index); bool has_unbound_head_var(rule * r); void detect_tasks(rule_set const& source, unsigned rule_index); void add_task(func_decl * pred, unsigned arg_index); lbool try_compress(rule_set const& source, unsigned rule_index); void add_decompression_rules(rule_set const& source, unsigned rule_index); rule_ref mk_decompression_rule(rule * r, unsigned tail_index, unsigned arg_index); void add_decompression_rule(rule_set const& source, rule * r, unsigned tail_index, unsigned arg_index); void replace_by_decompression_rule(rule_set const& source, unsigned rule_index, unsigned tail_index, unsigned arg_index); void add_in_progress_indices(unsigned_vector& arg_indices, app* p); bool decompress_rule(rule_set const& source, rule* r, unsigned_vector const& cmpressed_tail_pred_arg_indexes, unsigned rule_index, unsigned tail_index); void reset(); public: mk_unbound_compressor(context & ctx); rule_set * operator()(rule_set const & source) override; }; }; #endif /* DL_MK_UNBOUND_COMPRESSOR_H_ */ z3-z3-4.8.7/src/muz/transforms/dl_mk_unfold.cpp000066400000000000000000000035671356505360400214030ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: dl_mk_unfold.cpp Abstract: Unfold rules once, return the unfolded set of rules. Author: Nikolaj Bjorner (nbjorner) 2012-10-15 Revision History: --*/ #include "muz/transforms/dl_mk_unfold.h" namespace datalog { mk_unfold::mk_unfold(context& ctx): rule_transformer::plugin(100, false), m_ctx(ctx), m(ctx.get_manager()), rm(ctx.get_rule_manager()), m_unify(ctx) {} void mk_unfold::expand_tail(rule& r, unsigned tail_idx, rule_set const& src, rule_set& dst) { SASSERT(tail_idx <= r.get_uninterpreted_tail_size()); if (tail_idx == r.get_uninterpreted_tail_size()) { dst.add_rule(&r); } else { func_decl* p = r.get_decl(tail_idx); rule_vector const& p_rules = src.get_predicate_rules(p); rule_ref new_rule(rm); for (unsigned i = 0; i < p_rules.size(); ++i) { rule const& r2 = *p_rules[i]; if (m_unify.unify_rules(r, tail_idx, r2) && m_unify.apply(r, tail_idx, r2, new_rule)) { expr_ref_vector s1 = m_unify.get_rule_subst(r, true); expr_ref_vector s2 = m_unify.get_rule_subst(r2, false); resolve_rule(rm, r, r2, tail_idx, s1, s2, *new_rule.get()); expand_tail(*new_rule.get(), tail_idx+r2.get_uninterpreted_tail_size(), src, dst); } } } } rule_set * mk_unfold::operator()(rule_set const & source) { rule_set* rules = alloc(rule_set, m_ctx); rule_set::iterator it = source.begin(), end = source.end(); for (; it != end; ++it) { expand_tail(**it, 0, source, *rules); } rules->inherit_predicates(source); return rules; } }; z3-z3-4.8.7/src/muz/transforms/dl_mk_unfold.h000066400000000000000000000020341356505360400210340ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: dl_mk_unfold.h Abstract: Unfold rules once, return the unfolded set of rules. Author: Nikolaj Bjorner (nbjorner) 2012-10-15 Revision History: --*/ #ifndef DL_MK_UNFOLD_H_ #define DL_MK_UNFOLD_H_ #include "muz/base/dl_context.h" #include "muz/base/dl_rule_set.h" #include "util/uint_set.h" #include "muz/base/dl_rule_transformer.h" #include "muz/transforms/dl_mk_rule_inliner.h" namespace datalog { /** \brief Implements an unfolding transformation. */ class mk_unfold : public rule_transformer::plugin { context& m_ctx; ast_manager& m; rule_manager& rm; rule_unifier m_unify; void expand_tail(rule& r, unsigned tail_idx, rule_set const& src, rule_set& dst); public: /** \brief Create unfold rule transformer. */ mk_unfold(context & ctx); rule_set * operator()(rule_set const & source) override; }; }; #endif /* DL_MK_UNFOLD_H_ */ z3-z3-4.8.7/src/muz/transforms/dl_transforms.cpp000066400000000000000000000102271356505360400216120ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: dl_transforms.cpp Abstract: Default transformations. Author: Nikolaj Bjorner (nbjorner) 2013-08-28. Revision History: Extracted from dl_context --*/ #include "muz/transforms/dl_transforms.h" #include "muz/base/dl_rule_transformer.h" #include "muz/transforms/dl_mk_coi_filter.h" #include "muz/transforms/dl_mk_filter_rules.h" #include "muz/transforms/dl_mk_interp_tail_simplifier.h" #include "muz/transforms/dl_mk_rule_inliner.h" #include "muz/transforms/dl_mk_bit_blast.h" #include "muz/transforms/dl_mk_array_blast.h" #include "muz/transforms/dl_mk_karr_invariants.h" #include "muz/transforms/dl_mk_magic_symbolic.h" #include "muz/transforms/dl_mk_quantifier_abstraction.h" #include "muz/transforms/dl_mk_quantifier_instantiation.h" #include "muz/transforms/dl_mk_subsumption_checker.h" #include "muz/transforms/dl_mk_scale.h" #include "muz/transforms/dl_mk_array_eq_rewrite.h" #include "muz/transforms/dl_mk_array_instantiation.h" #include "muz/transforms/dl_mk_elim_term_ite.h" #include "muz/base/fp_params.hpp" namespace datalog { void apply_default_transformation(context& ctx) { flet _enable_bv(ctx.bind_vars_enabled(), false); rule_transformer transf(ctx); ctx.ensure_closed(); transf.reset(); transf.register_plugin(alloc(datalog::mk_coi_filter, ctx)); transf.register_plugin(alloc(datalog::mk_interp_tail_simplifier, ctx)); if (ctx.get_params().xform_instantiate_arrays()) { transf.register_plugin(alloc(datalog::mk_array_instantiation, ctx, 34999)); } if(ctx.get_params().xform_transform_arrays()) transf.register_plugin(alloc(datalog::mk_array_eq_rewrite, ctx, 34998)); if (ctx.get_params().xform_quantify_arrays()) { transf.register_plugin(alloc(datalog::mk_quantifier_abstraction, ctx, 38000)); } transf.register_plugin(alloc(datalog::mk_quantifier_instantiation, ctx, 37000)); if (ctx.get_params().datalog_subsumption()) { transf.register_plugin(alloc(datalog::mk_subsumption_checker, ctx, 35005)); } transf.register_plugin(alloc(datalog::mk_rule_inliner, ctx, 35000)); transf.register_plugin(alloc(datalog::mk_coi_filter, ctx, 34990)); transf.register_plugin(alloc(datalog::mk_interp_tail_simplifier, ctx, 34980)); //and another round of inlining if (ctx.get_params().datalog_subsumption()) { transf.register_plugin(alloc(datalog::mk_subsumption_checker, ctx, 34975)); } transf.register_plugin(alloc(datalog::mk_rule_inliner, ctx, 34970)); transf.register_plugin(alloc(datalog::mk_coi_filter, ctx, 34960)); transf.register_plugin(alloc(datalog::mk_interp_tail_simplifier, ctx, 34950)); if (ctx.get_params().datalog_subsumption()) { transf.register_plugin(alloc(datalog::mk_subsumption_checker, ctx, 34940)); transf.register_plugin(alloc(datalog::mk_rule_inliner, ctx, 34930)); transf.register_plugin(alloc(datalog::mk_subsumption_checker, ctx, 34920)); transf.register_plugin(alloc(datalog::mk_rule_inliner, ctx, 34910)); transf.register_plugin(alloc(datalog::mk_subsumption_checker, ctx, 34900)); transf.register_plugin(alloc(datalog::mk_rule_inliner, ctx, 34890)); transf.register_plugin(alloc(datalog::mk_subsumption_checker, ctx, 34880)); } else { transf.register_plugin(alloc(datalog::mk_rule_inliner, ctx, 34930)); } transf.register_plugin(alloc(datalog::mk_bit_blast, ctx, 35000)); transf.register_plugin(alloc(datalog::mk_karr_invariants, ctx, 36010)); transf.register_plugin(alloc(datalog::mk_scale, ctx, 36030)); if (!ctx.get_params().xform_quantify_arrays()) { transf.register_plugin(alloc(datalog::mk_array_blast, ctx, 35999)); } if (ctx.get_params().xform_magic()) { transf.register_plugin(alloc(datalog::mk_magic_symbolic, ctx, 36020)); } transf.register_plugin(alloc(datalog::mk_elim_term_ite, ctx, 35010)); ctx.transform_rules(transf); } } z3-z3-4.8.7/src/muz/transforms/dl_transforms.h000066400000000000000000000006171356505360400212610ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: dl_transforms.h Abstract: Default transformations. Author: Nikolaj Bjorner (nbjorner) 2013-08-28. Revision History: Extracted from dl_context --*/ #ifndef DL_TRANSFORMS_H_ #define DL_TRANSFORMS_H_ #include "muz/base/dl_context.h" namespace datalog { void apply_default_transformation(context& ctx); } #endif z3-z3-4.8.7/src/nlsat/000077500000000000000000000000001356505360400143375ustar00rootroot00000000000000z3-z3-4.8.7/src/nlsat/CMakeLists.txt000066400000000000000000000003731356505360400171020ustar00rootroot00000000000000z3_add_component(nlsat SOURCES nlsat_clause.cpp nlsat_evaluator.cpp nlsat_explain.cpp nlsat_interval_set.cpp nlsat_solver.cpp nlsat_types.cpp COMPONENT_DEPENDENCIES polynomial sat PYG_FILES nlsat_params.pyg ) z3-z3-4.8.7/src/nlsat/nlsat_assignment.h000066400000000000000000000067021356505360400200660ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: nlsat_assignment.h Abstract: Assignment: Var -> Algebraic Number Author: Leonardo de Moura (leonardo) 2012-01-08. Revision History: --*/ #ifndef NLSAT_ASSIGNMENT_H_ #define NLSAT_ASSIGNMENT_H_ #include "nlsat/nlsat_types.h" #include "math/polynomial/algebraic_numbers.h" namespace nlsat { /** \brief A mapping from variables to values. This mapping is used to encode the current partial interpretation in nlsat. */ class assignment : public polynomial::var2anum { scoped_anum_vector m_values; svector m_assigned; public: assignment(anum_manager & _m):m_values(_m) {} virtual ~assignment() {} anum_manager & am() const { return m_values.m(); } void swap(assignment & other) { m_values.swap(other.m_values); m_assigned.swap(other.m_assigned); } void copy(assignment const& other) { m_assigned.reset(); m_assigned.append(other.m_assigned); m_values.reserve(m_assigned.size(), anum()); for (unsigned i = 0; i < m_assigned.size(); ++i) { if (is_assigned(i)) { am().set(m_values[i], other.value(i)); } } } void set_core(var x, anum & v) { m_values.reserve(x+1, anum()); m_assigned.reserve(x+1, false); m_assigned[x] = true; am().swap(m_values[x], v); } void set(var x, anum const & v) { m_values.reserve(x+1, anum()); m_assigned.reserve(x+1, false); m_assigned[x] = true; am().set(m_values[x], v); } void reset(var x) { if (x < m_assigned.size()) m_assigned[x] = false; } void reset() { m_assigned.reset(); } bool is_assigned(var x) const { return m_assigned.get(x, false); } anum const & value(var x) const { return m_values[x]; } anum_manager & m() const override { return am(); } bool contains(var x) const override { return is_assigned(x); } anum const & operator()(var x) const override { SASSERT(is_assigned(x)); return value(x); } void swap(var x, var y) { SASSERT(x < m_values.size() && y < m_values.size()); std::swap(m_assigned[x], m_assigned[y]); std::swap(m_values[x], m_values[y]); } void display(std::ostream& out) const { for (unsigned i = 0; i < m_assigned.size(); ++i) { if (m_assigned[i]) { out << "x" << i << " := "; m_values.m().display(out, m_values[i]); out << "\n"; } } } }; /** \brief Wrapper for temporarily unassigning a given variable y. That is, given an assignment M, M' = undef_var_assignment(M, y) is identical to M, but y is unassigned in M' */ class undef_var_assignment : public polynomial::var2anum { assignment const & m_assignment; var m_y; public: undef_var_assignment(assignment const & a, var y):m_assignment(a), m_y(y) {} anum_manager & m() const override { return m_assignment.am(); } bool contains(var x) const override { return x != m_y && m_assignment.is_assigned(x); } anum const & operator()(var x) const override { return m_assignment.value(x); } }; }; #endif z3-z3-4.8.7/src/nlsat/nlsat_clause.cpp000066400000000000000000000017561356505360400175310ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: nlsat_clause.cpp Abstract: Clauses used in the Nonlinear arithmetic satisfiability procedure. Author: Leonardo de Moura (leonardo) 2012-01-02. Revision History: --*/ #include "nlsat/nlsat_clause.h" namespace nlsat { clause::clause(unsigned id, unsigned sz, literal const * lits, bool learned, assumption_set as): m_id(id), m_size(sz), m_capacity(sz), m_learned(learned), m_activity(0), m_assumptions(as) { for (unsigned i = 0; i < sz; i++) { m_lits[i] = lits[i]; } } bool clause::contains(literal l) const { for (unsigned i = 0; i < m_size; i++) if (m_lits[i] == l) return true; return false; } bool clause::contains(bool_var v) const { for (unsigned i = 0; i < m_size; i++) if (m_lits[i].var() == v) return true; return false; } }; z3-z3-4.8.7/src/nlsat/nlsat_clause.h000066400000000000000000000040321356505360400171640ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: nlsat_clause.h Abstract: Clauses used in the Nonlinear arithmetic satisfiability procedure. Author: Leonardo de Moura (leonardo) 2012-01-02. Revision History: --*/ #ifndef NLSAT_CLAUSE_H_ #define NLSAT_CLAUSE_H_ #include "nlsat/nlsat_types.h" #include "util/vector.h" namespace nlsat { class clause { friend class solver; unsigned m_id; unsigned m_size; unsigned m_capacity:31; unsigned m_learned:1; unsigned m_activity; assumption_set m_assumptions; literal m_lits[0]; static size_t get_obj_size(unsigned num_lits) { return sizeof(clause) + num_lits * sizeof(literal); } size_t get_size() const { return get_obj_size(m_capacity); } clause(unsigned id, unsigned sz, literal const * lits, bool learned, assumption_set as); public: unsigned size() const { return m_size; } unsigned id() const { return m_id; } literal & operator[](unsigned idx) { SASSERT(idx < m_size); return m_lits[idx]; } literal const & operator[](unsigned idx) const { SASSERT(idx < m_size); return m_lits[idx]; } bool is_learned() const { return m_learned; } literal * begin() { return m_lits; } literal * end() { return m_lits + m_size; } literal const * begin() const { return m_lits; } literal const * end() const { return m_lits + m_size; } literal const * c_ptr() const { return m_lits; } void inc_activity() { m_activity++; } void set_activity(unsigned v) { m_activity = v; } unsigned get_activity() const { return m_activity; } bool contains(literal l) const; bool contains(bool_var v) const; void shrink(unsigned num_lits) { SASSERT(num_lits <= m_size); if (num_lits < m_size) { m_size = num_lits; } } assumption_set assumptions() const { return m_assumptions; } }; typedef ptr_vector clause_vector; }; #endif z3-z3-4.8.7/src/nlsat/nlsat_evaluator.cpp000066400000000000000000000714501356505360400202550ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: nlsat_evaluator.cpp Abstract: Helper class for computing the infeasible intervals of an arithmetic literal. Author: Leonardo de Moura (leonardo) 2012-01-12. Revision History: --*/ #include "nlsat/nlsat_evaluator.h" #include "nlsat/nlsat_solver.h" namespace nlsat { struct evaluator::imp { solver& m_solver; assignment const & m_assignment; pmanager & m_pm; small_object_allocator & m_allocator; anum_manager & m_am; interval_set_manager m_ism; scoped_anum_vector m_tmp_values; scoped_anum_vector m_add_roots_tmp; scoped_anum_vector m_inf_tmp; // sign tables: light version struct sign_table { anum_manager & m_am; struct section { anum m_root; unsigned m_pos; }; svector
m_sections; unsigned_vector m_sorted_sections; // refs to m_sections unsigned_vector m_poly_sections; // refs to m_sections svector m_poly_signs; struct poly_info { unsigned m_num_roots; unsigned m_first_section; // idx in m_poly_sections; unsigned m_first_sign; // idx in m_poly_signs; poly_info(unsigned num, unsigned first_section, unsigned first_sign): m_num_roots(num), m_first_section(first_section), m_first_sign(first_sign) { } }; svector m_info; sign_table(anum_manager & am):m_am(am) {} ~sign_table() { reset(); } void reset() { unsigned sz = m_sections.size(); for (unsigned i = 0; i < sz; i++) m_am.del(m_sections[i].m_root); m_sections.reset(); m_sorted_sections.reset(); m_poly_sections.reset(); m_poly_signs.reset(); m_info.reset(); } unsigned mk_section(anum & v, unsigned pos) { unsigned new_id = m_sections.size(); m_sections.push_back(section()); section & new_s = m_sections.back(); m_am.set(new_s.m_root, v); new_s.m_pos = pos; return new_id; } // Merge the new roots of a polynomial p into m_sections & m_sorted_sections. // Store the section ids for the new roots in p_section_ids unsigned_vector new_sorted_sections; void merge(anum_vector & roots, unsigned_vector & p_section_ids) { new_sorted_sections.reset(); // new m_sorted_sections unsigned i1 = 0; unsigned sz1 = m_sorted_sections.size(); unsigned i2 = 0; unsigned sz2 = roots.size(); unsigned j = 0; while (i1 < sz1 && i2 < sz2) { unsigned s1_id = m_sorted_sections[i1]; section & s1 = m_sections[s1_id]; anum & r2 = roots[i2]; int c = m_am.compare(s1.m_root, r2); if (c == 0) { new_sorted_sections.push_back(s1_id); p_section_ids.push_back(s1_id); s1.m_pos = j; i1++; i2++; } else if (c < 0) { new_sorted_sections.push_back(s1_id); s1.m_pos = j; i1++; } else { // create new section unsigned new_id = mk_section(r2, j); // new_sorted_sections.push_back(new_id); p_section_ids.push_back(new_id); i2++; } j++; } SASSERT(i1 == sz1 || i2 == sz2); while (i1 < sz1) { unsigned s1_id = m_sorted_sections[i1]; section & s1 = m_sections[s1_id]; new_sorted_sections.push_back(s1_id); s1.m_pos = j; i1++; j++; } while (i2 < sz2) { anum & r2 = roots[i2]; // create new section unsigned new_id = mk_section(r2, j); // new_sorted_sections.push_back(new_id); p_section_ids.push_back(new_id); i2++; j++; } m_sorted_sections.swap(new_sorted_sections); } /** \brief Add polynomial with the given roots and signs. */ unsigned_vector p_section_ids; void add(anum_vector & roots, svector & signs) { p_section_ids.reset(); if (!roots.empty()) merge(roots, p_section_ids); unsigned first_sign = m_poly_signs.size(); unsigned first_section = m_poly_sections.size(); unsigned num_poly_signs = signs.size(); // Must normalize signs since we use arithmetic operations such as * // during evaluation. // Without normalization, overflows may happen, and wrong results may be produced. for (unsigned i = 0; i < num_poly_signs; i++) m_poly_signs.push_back(signs[i]); m_poly_sections.append(p_section_ids); m_info.push_back(poly_info(roots.size(), first_section, first_sign)); SASSERT(check_invariant()); } /** \brief Add constant polynomial */ void add_const(polynomial::sign sign) { unsigned first_sign = m_poly_signs.size(); unsigned first_section = m_poly_sections.size(); m_poly_signs.push_back(sign); m_info.push_back(poly_info(0, first_section, first_sign)); } unsigned num_cells() const { return m_sections.size() * 2 + 1; } /** \brief Return true if the given cell is a section (i.e., root) */ bool is_section(unsigned c) const { SASSERT(c < num_cells()); return c % 2 == 1; } /** \brief Return true if the given cell is a sector (i.e., an interval between roots, or (-oo, min-root), or (max-root, +oo)). */ bool is_sector(unsigned c) const { SASSERT(c < num_cells()); return c % 2 == 0; } /** \brief Return the root id associated with the given section. \pre is_section(c) */ unsigned get_root_id(unsigned c) const { SASSERT(is_section(c)); return m_sorted_sections[c/2]; } // Return value of root idx. // If idx == UINT_MAX, it return zero (this is a hack to simplify the infeasible_intervals method anum const & get_root(unsigned idx) const { static anum zero; if (idx == UINT_MAX) return zero; SASSERT(idx < m_sections.size()); return m_sections[idx].m_root; } static unsigned section_id_to_cell_id(unsigned section_id) { return section_id*2 + 1; } // Return the cell_id of the root i of pinfo unsigned cell_id(poly_info const & pinfo, unsigned i) const { return section_id_to_cell_id(m_sections[m_poly_sections[pinfo.m_first_section + i]].m_pos); } // Return the sign idx of pinfo polynomial::sign sign(poly_info const & pinfo, unsigned i) const { return m_poly_signs[pinfo.m_first_sign + i]; } #define LINEAR_SEARCH_THRESHOLD 8 polynomial::sign sign_at(unsigned info_id, unsigned c) const { poly_info const & pinfo = m_info[info_id]; unsigned num_roots = pinfo.m_num_roots; if (num_roots < LINEAR_SEARCH_THRESHOLD) { unsigned i = 0; for (; i < num_roots; i++) { unsigned section_cell_id = cell_id(pinfo, i); if (section_cell_id == c) return polynomial::sign_zero; else if (section_cell_id > c) break; } return sign(pinfo, i); } else { if (num_roots == 0) return sign(pinfo, 0); unsigned root_1_cell_id = cell_id(pinfo, 0); unsigned root_n_cell_id = cell_id(pinfo, num_roots - 1); if (c < root_1_cell_id) return sign(pinfo, 0); else if (c == root_1_cell_id || c == root_n_cell_id) return polynomial::sign_zero; else if (c > root_n_cell_id) return sign(pinfo, num_roots); int low = 0; int high = num_roots-1; while (true) { SASSERT(0 <= low && high < static_cast(num_roots)); SASSERT(cell_id(pinfo, low) < c); SASSERT(c < cell_id(pinfo, high)); if (high == low + 1) { SASSERT(cell_id(pinfo, low) < c); SASSERT(c < cell_id(pinfo, low+1)); return sign(pinfo, low+1); } SASSERT(high > low + 1); int mid = low + ((high - low)/2); SASSERT(low < mid && mid < high); unsigned mid_cell_id = cell_id(pinfo, mid); if (mid_cell_id == c) { return polynomial::sign_zero; } if (c < mid_cell_id) { high = mid; } else { SASSERT(mid_cell_id < c); low = mid; } } } } bool check_invariant() const { SASSERT(m_sections.size() == m_sorted_sections.size()); for (unsigned i = 0; i < m_sorted_sections.size(); i++) { SASSERT(m_sorted_sections[i] < m_sections.size()); SASSERT(m_sections[m_sorted_sections[i]].m_pos == i); } unsigned total_num_sections = 0; unsigned total_num_signs = 0; for (unsigned i = 0; i < m_info.size(); i++) { SASSERT(m_info[i].m_first_section <= m_poly_sections.size()); SASSERT(m_info[i].m_num_roots == 0 || m_info[i].m_first_section < m_poly_sections.size()); SASSERT(m_info[i].m_first_sign < m_poly_signs.size()); total_num_sections += m_info[i].m_num_roots; total_num_signs += m_info[i].m_num_roots + 1; } SASSERT(total_num_sections == m_poly_sections.size()); SASSERT(total_num_signs == m_poly_signs.size()); return true; } // Display sign table for the given variable void display(std::ostream & out) const { out << "sections:\n "; for (unsigned i = 0; i < m_sections.size(); i++) { if (i > 0) out << " < "; m_am.display_decimal(out, m_sections[m_sorted_sections[i]].m_root); } out << "\n"; out << "sign variations:\n"; for (unsigned i = 0; i < m_info.size(); i++) { out << " "; for (unsigned j = 0; j < num_cells(); j++) { if (j > 0) out << " "; auto s = sign_at(i, j); if (s < 0) out << "-"; else if (s == 0) out << "0"; else out << "+"; } out << "\n"; } } // Display sign table for the given variable void display_raw(std::ostream & out) const { out << "sections:\n "; for (unsigned i = 0; i < m_sections.size(); i++) { if (i > 0) out << " < "; m_am.display_decimal(out, m_sections[m_sorted_sections[i]].m_root); } out << "\n"; out << "poly_info:\n"; for (unsigned i = 0; i < m_info.size(); i++) { out << " roots:"; poly_info const & info = m_info[i]; for (unsigned j = 0; j < info.m_num_roots; j++) { out << " "; out << m_poly_sections[info.m_first_section+j]; } out << ", signs:"; for (unsigned j = 0; j < info.m_num_roots+1; j++) { out << " "; int s = m_poly_signs[info.m_first_sign+j]; if (s < 0) out << "-"; else if (s == 0) out << "0"; else out << "+"; } out << "\n"; } } }; sign_table m_sign_table_tmp; imp(solver& s, assignment const & x2v, pmanager & pm, small_object_allocator & allocator): m_solver(s), m_assignment(x2v), m_pm(pm), m_allocator(allocator), m_am(m_assignment.am()), m_ism(m_am, allocator), m_tmp_values(m_am), m_add_roots_tmp(m_am), m_inf_tmp(m_am), m_sign_table_tmp(m_am) { } var max_var(poly const * p) const { return m_pm.max_var(p); } /** \brief Return the sign of the polynomial in the current interpretation. \pre All variables of p are assigned in the current interpretation. */ polynomial::sign eval_sign(poly * p) { // TODO: check if it is useful to cache results SASSERT(m_assignment.is_assigned(max_var(p))); return m_am.eval_sign_at(polynomial_ref(p, m_pm), m_assignment); } bool satisfied(int sign, atom::kind k) { return (sign == 0 && (k == atom::EQ || k == atom::ROOT_EQ || k == atom::ROOT_LE || k == atom::ROOT_GE)) || (sign < 0 && (k == atom::LT || k == atom::ROOT_LT || k == atom::ROOT_LE)) || (sign > 0 && (k == atom::GT || k == atom::ROOT_GT || k == atom::ROOT_GE)); } bool satisfied(int sign, atom::kind k, bool neg) { bool r = satisfied(sign, k); return neg ? !r : r; } bool eval_ineq(ineq_atom * a, bool neg) { SASSERT(m_assignment.is_assigned(a->max_var())); // all variables of a were already assigned... atom::kind k = a->get_kind(); unsigned sz = a->size(); int sign = 1; for (unsigned i = 0; i < sz; i++) { int curr_sign = eval_sign(a->p(i)); if (a->is_even(i) && curr_sign < 0) curr_sign = 1; sign = sign * curr_sign; if (sign == 0) break; } return satisfied(sign, k, neg); } bool eval_root(root_atom * a, bool neg) { SASSERT(m_assignment.is_assigned(a->max_var())); // all variables of a were already assigned... atom::kind k = a->get_kind(); scoped_anum_vector & roots = m_tmp_values; roots.reset(); m_am.isolate_roots(polynomial_ref(a->p(), m_pm), undef_var_assignment(m_assignment, a->x()), roots); TRACE("nlsat_evaluator", m_solver.display(tout << (neg?"!":""), *a); tout << "\n"; if (roots.empty()) { tout << "No roots\n"; } else { tout << "Roots for "; for (unsigned i = 0; i < roots.size(); ++i) { m_am.display_interval(tout, roots[i]); tout << " "; } tout << "\n"; } m_assignment.display(tout); ); SASSERT(a->i() > 0); if (a->i() > roots.size()) { return neg; } int sign = m_am.compare(m_assignment.value(a->x()), roots[a->i() - 1]); return satisfied(sign, k, neg); } bool eval(atom * a, bool neg) { return a->is_ineq_atom() ? eval_ineq(to_ineq_atom(a), neg) : eval_root(to_root_atom(a), neg); } svector m_add_signs_tmp; void add(poly * p, var x, sign_table & t) { SASSERT(m_pm.max_var(p) <= x); if (m_pm.max_var(p) < x) { t.add_const(eval_sign(p)); } else { // isolate roots of p scoped_anum_vector & roots = m_add_roots_tmp; svector & signs = m_add_signs_tmp; roots.reset(); signs.reset(); TRACE("nlsat_evaluator", tout << "x: " << x << " max_var(p): " << m_pm.max_var(p) << "\n";); // Note: I added undef_var_assignment in the following statement, to allow us to obtain the infeasible interval sets // even when the maximal variable is assigned. I need this feature to minimize conflict cores. m_am.isolate_roots(polynomial_ref(p, m_pm), undef_var_assignment(m_assignment, x), roots, signs); t.add(roots, signs); } } // Evaluate the sign of p1^e1*...*pn^en (of atom a) in cell c of table t. polynomial::sign sign_at(ineq_atom * a, sign_table const & t, unsigned c) const { auto sign = polynomial::sign_pos; unsigned num_ps = a->size(); for (unsigned i = 0; i < num_ps; i++) { polynomial::sign curr_sign = t.sign_at(i, c); TRACE("nlsat_evaluator_bug", tout << "sign of i: " << i << " at cell " << c << "\n"; m_pm.display(tout, a->p(i)); tout << "\nsign: " << curr_sign << "\n";); if (a->is_even(i) && curr_sign < 0) curr_sign = polynomial::sign_pos; sign = sign * curr_sign; if (sign == polynomial::sign_zero) break; } return sign; } interval_set_ref infeasible_intervals(ineq_atom * a, bool neg, clause const* cls) { sign_table & table = m_sign_table_tmp; table.reset(); unsigned num_ps = a->size(); var x = a->max_var(); for (unsigned i = 0; i < num_ps; i++) { add(a->p(i), x, table); TRACE("nlsat_evaluator_bug", tout << "table after:\n"; m_pm.display(tout, a->p(i)); tout << "\n"; table.display_raw(tout);); } TRACE("nlsat_evaluator", tout << "sign table for:\n"; for (unsigned i = 0; i < num_ps; i++) { m_pm.display(tout, a->p(i)); tout << "\n"; } table.display(tout);); interval_set_ref result(m_ism); interval_set_ref set(m_ism); literal jst(a->bvar(), neg); atom::kind k = a->get_kind(); anum dummy; bool prev_sat = true; bool prev_inf = true; bool prev_open = true; unsigned prev_root_id = UINT_MAX; unsigned num_cells = table.num_cells(); for (unsigned c = 0; c < num_cells; c++) { TRACE("nlsat_evaluator", tout << "cell: " << c << "\n"; tout << "prev_sat: " << prev_sat << "\n"; tout << "prev_inf: " << prev_inf << "\n"; tout << "prev_open: " << prev_open << "\n"; tout << "prev_root_id: " << prev_root_id << "\n"; tout << "processing cell: " << c << "\n"; tout << "interval_set so far:\n" << result << "\n";); int sign = sign_at(a, table, c); TRACE("nlsat_evaluator", tout << "sign: " << sign << "\n";); if (satisfied(sign, k, neg)) { // current cell is satisfied if (!prev_sat) { SASSERT(c > 0); // add interval bool curr_open; unsigned curr_root_id; if (table.is_section(c)) { curr_open = true; curr_root_id = table.get_root_id(c); } else { SASSERT(table.is_section(c-1)); curr_open = false; curr_root_id = table.get_root_id(c-1); } set = m_ism.mk(prev_open, prev_inf, table.get_root(prev_root_id), curr_open, false, table.get_root(curr_root_id), jst, cls); result = m_ism.mk_union(result, set); prev_sat = true; } } else { // current cell is not satisfied if (prev_sat) { if (c == 0) { if (num_cells == 1) { // (-oo, oo) result = m_ism.mk(true, true, dummy, true, true, dummy, jst, cls); } else { // save -oo as beginning of infeasible interval prev_open = true; prev_inf = true; prev_root_id = UINT_MAX; } } else { SASSERT(c > 0); prev_inf = false; if (table.is_section(c)) { prev_open = false; prev_root_id = table.get_root_id(c); TRACE("nlsat_evaluator", tout << "updated prev_root_id: " << prev_root_id << " using cell: " << c << "\n";); } else { SASSERT(table.is_section(c-1)); prev_open = true; prev_root_id = table.get_root_id(c-1); TRACE("nlsat_evaluator", tout << "updated prev_root_id: " << prev_root_id << " using cell: " << (c - 1) << "\n";); } } prev_sat = false; } if (c == num_cells - 1) { // last cell add interval with (prev, oo) set = m_ism.mk(prev_open, prev_inf, table.get_root(prev_root_id), true, true, dummy, jst, cls); result = m_ism.mk_union(result, set); } } } TRACE("nlsat_evaluator", tout << "interval_set: " << result << "\n";); return result; } interval_set_ref infeasible_intervals(root_atom * a, bool neg, clause const* cls) { atom::kind k = a->get_kind(); unsigned i = a->i(); SASSERT(i > 0); literal jst(a->bvar(), neg); anum dummy; scoped_anum_vector & roots = m_tmp_values; roots.reset(); var x = a->max_var(); // Note: I added undef_var_assignment in the following statement, to allow us to obtain the infeasible interval sets // even when the maximal variable is assigned. I need this feature to minimize conflict cores. m_am.isolate_roots(polynomial_ref(a->p(), m_pm), undef_var_assignment(m_assignment, x), roots); interval_set_ref result(m_ism); if (i > roots.size()) { // p does have sufficient roots // atom is false by definition if (neg) { result = m_ism.mk_empty(); } else { result = m_ism.mk(true, true, dummy, true, true, dummy, jst, cls); // (-oo, oo) } } else { anum const & r_i = roots[i-1]; switch (k) { case atom::ROOT_EQ: if (neg) { result = m_ism.mk(false, false, r_i, false, false, r_i, jst, cls); // [r_i, r_i] } else { interval_set_ref s1(m_ism), s2(m_ism); s1 = m_ism.mk(true, true, dummy, true, false, r_i, jst, cls); // (-oo, r_i) s2 = m_ism.mk(true, false, r_i, true, true, dummy, jst, cls); // (r_i, oo) result = m_ism.mk_union(s1, s2); } break; case atom::ROOT_LT: if (neg) result = m_ism.mk(true, true, dummy, true, false, r_i, jst, cls); // (-oo, r_i) else result = m_ism.mk(false, false, r_i, true, true, dummy, jst, cls); // [r_i, oo) break; case atom::ROOT_GT: if (neg) result = m_ism.mk(true, false, r_i, true, true, dummy, jst, cls); // (r_i, oo) else result = m_ism.mk(true, true, dummy, false, false, r_i, jst, cls); // (-oo, r_i] break; case atom::ROOT_LE: if (neg) result = m_ism.mk(true, true, dummy, false, false, r_i, jst, cls); // (-oo, r_i] else result = m_ism.mk(true, false, r_i, true, true, dummy, jst, cls); // (r_i, oo) break; case atom::ROOT_GE: if (neg) result = m_ism.mk(false, false, r_i, true, true, dummy, jst, cls); // [r_i, oo) else result = m_ism.mk(true, true, dummy, true, false, r_i, jst, cls); // (-oo, r_i) break; default: UNREACHABLE(); break; } } TRACE("nlsat_evaluator", tout << "interval_set: " << result << "\n";); return result; } interval_set_ref infeasible_intervals(atom * a, bool neg, clause const* cls) { return a->is_ineq_atom() ? infeasible_intervals(to_ineq_atom(a), neg, cls) : infeasible_intervals(to_root_atom(a), neg, cls); } }; evaluator::evaluator(solver& s, assignment const & x2v, pmanager & pm, small_object_allocator & allocator) { m_imp = alloc(imp, s, x2v, pm, allocator); } evaluator::~evaluator() { dealloc(m_imp); } interval_set_manager & evaluator::ism() const { return m_imp->m_ism; } bool evaluator::eval(atom * a, bool neg) { return m_imp->eval(a, neg); } interval_set_ref evaluator::infeasible_intervals(atom * a, bool neg, clause const* cls) { return m_imp->infeasible_intervals(a, neg, cls); } void evaluator::push() { // do nothing } void evaluator::pop(unsigned num_scopes) { // do nothing } }; z3-z3-4.8.7/src/nlsat/nlsat_evaluator.h000066400000000000000000000026671356505360400177260ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: nlsat_evaluator.h Abstract: Helper class for computing the infeasible intervals of an arithmetic literal. Author: Leonardo de Moura (leonardo) 2012-01-12. Revision History: --*/ #ifndef NLSAT_EVALUATOR_H_ #define NLSAT_EVALUATOR_H_ #include "nlsat/nlsat_types.h" #include "nlsat/nlsat_assignment.h" #include "nlsat/nlsat_interval_set.h" namespace nlsat { class solver; class evaluator { struct imp; imp * m_imp; public: evaluator(solver& s, assignment const & x2v, pmanager & pm, small_object_allocator & allocator); ~evaluator(); interval_set_manager & ism() const; /** \brief Evaluate the given literal in the current model. All variables in the atom must be assigned. The result is true if the literal is satisfied, and false otherwise. */ bool eval(atom * a, bool neg); /** \brief Return the infeasible interval set for the given literal. All but the a->max_var() must be assigned in the current model. Let x be a->max_var(). Then, the resultant set specifies which values of x falsify the given literal. */ interval_set_ref infeasible_intervals(atom * a, bool neg, clause const* cls); void push(); void pop(unsigned num_scopes); }; }; #endif z3-z3-4.8.7/src/nlsat/nlsat_explain.cpp000066400000000000000000002225521356505360400177140ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: nlsat_explain.cpp Abstract: Functor that implements the "explain" procedure defined in Dejan and Leo's paper. Author: Leonardo de Moura (leonardo) 2012-01-13. Revision History: --*/ #include "nlsat/nlsat_explain.h" #include "nlsat/nlsat_assignment.h" #include "nlsat/nlsat_evaluator.h" #include "math/polynomial/algebraic_numbers.h" #include "util/ref_buffer.h" namespace nlsat { typedef polynomial::polynomial_ref_vector polynomial_ref_vector; typedef ref_buffer polynomial_ref_buffer; struct explain::imp { solver & m_solver; assignment const & m_assignment; atom_vector const & m_atoms; atom_vector const & m_x2eq; anum_manager & m_am; polynomial::cache & m_cache; pmanager & m_pm; polynomial_ref_vector m_ps; polynomial_ref_vector m_ps2; polynomial_ref_vector m_psc_tmp; polynomial_ref_vector m_factors; scoped_anum_vector m_roots_tmp; bool m_simplify_cores; bool m_full_dimensional; bool m_minimize_cores; bool m_factor; bool m_signed_project; struct todo_set { polynomial::cache & m_cache; polynomial_ref_vector m_set; svector m_in_set; todo_set(polynomial::cache & u):m_cache(u), m_set(u.pm()) {} void reset() { pmanager & pm = m_set.m(); unsigned sz = m_set.size(); for (unsigned i = 0; i < sz; i++) { m_in_set[pm.id(m_set.get(i))] = false; } m_set.reset(); } void insert(poly * p) { pmanager & pm = m_set.m(); p = m_cache.mk_unique(p); unsigned pid = pm.id(p); if (m_in_set.get(pid, false)) return; m_in_set.setx(pid, true, false); m_set.push_back(p); } bool empty() const { return m_set.empty(); } // Return max variable in todo_set var max_var() const { pmanager & pm = m_set.m(); var max = null_var; unsigned sz = m_set.size(); for (unsigned i = 0; i < sz; i++) { var x = pm.max_var(m_set.get(i)); SASSERT(x != null_var); if (max == null_var || x > max) max = x; } return max; } /** \brief Remove the maximal polynomials from the set and store them in max_polys. Return the maximal variable */ var remove_max_polys(polynomial_ref_vector & max_polys) { max_polys.reset(); var x = max_var(); pmanager & pm = m_set.m(); unsigned sz = m_set.size(); unsigned j = 0; for (unsigned i = 0; i < sz; i++) { poly * p = m_set.get(i); var y = pm.max_var(p); SASSERT(y <= x); if (y == x) { max_polys.push_back(p); m_in_set[pm.id(p)] = false; } else { m_set.set(j, p); j++; } } m_set.shrink(j); return x; } }; // temporary field for store todo set of polynomials todo_set m_todo; // temporary fields for preprocessing core scoped_literal_vector m_core1; scoped_literal_vector m_core2; // temporary fields for storing the result scoped_literal_vector * m_result; svector m_already_added_literal; evaluator & m_evaluator; imp(solver & s, assignment const & x2v, polynomial::cache & u, atom_vector const & atoms, atom_vector const & x2eq, evaluator & ev): m_solver(s), m_assignment(x2v), m_atoms(atoms), m_x2eq(x2eq), m_am(x2v.am()), m_cache(u), m_pm(u.pm()), m_ps(m_pm), m_ps2(m_pm), m_psc_tmp(m_pm), m_factors(m_pm), m_roots_tmp(m_am), m_todo(u), m_core1(s), m_core2(s), m_result(nullptr), m_evaluator(ev) { m_simplify_cores = false; m_full_dimensional = false; m_minimize_cores = false; m_signed_project = false; } ~imp() { } std::ostream& display(std::ostream & out, polynomial_ref const & p) const { m_pm.display(out, p, m_solver.display_proc()); return out; } std::ostream& display(std::ostream & out, polynomial_ref_vector const & ps, char const * delim = "\n") const { for (unsigned i = 0; i < ps.size(); i++) { if (i > 0) out << delim; m_pm.display(out, ps.get(i), m_solver.display_proc()); } return out; } std::ostream& display(std::ostream & out, literal l) const { return m_solver.display(out, l); } std::ostream& display_var(std::ostream & out, var x) const { return m_solver.display(out, x); } std::ostream& display(std::ostream & out, unsigned sz, literal const * ls) const { return m_solver.display(out, sz, ls); } std::ostream& display(std::ostream & out, literal_vector const & ls) const { return display(out, ls.size(), ls.c_ptr()); } std::ostream& display(std::ostream & out, scoped_literal_vector const & ls) const { return display(out, ls.size(), ls.c_ptr()); } /** \brief Add literal to the result vector. */ void add_literal(literal l) { SASSERT(m_result != 0); SASSERT(l != true_literal); if (l == false_literal) return; unsigned lidx = l.index(); if (m_already_added_literal.get(lidx, false)) return; TRACE("nlsat_explain", tout << "adding literal: " << lidx << "\n"; m_solver.display(tout, l) << "\n";); m_already_added_literal.setx(lidx, true, false); m_result->push_back(l); } /** \brief Reset m_already_added vector using m_result */ void reset_already_added() { SASSERT(m_result != nullptr); for (literal lit : *m_result) m_already_added_literal[lit.index()] = false; SASSERT(check_already_added()); } /** \brief evaluate the given polynomial in the current interpretation. max_var(p) must be assigned in the current interpretation. */ polynomial::sign sign(polynomial_ref const & p) { SASSERT(max_var(p) == null_var || m_assignment.is_assigned(max_var(p))); auto s = m_am.eval_sign_at(p, m_assignment); TRACE("nlsat_explain", tout << "p: " << p << " var: " << max_var(p) << " sign: " << s << "\n";); return s; } /** \brief Wrapper for factorization */ void factor(polynomial_ref & p, polynomial_ref_vector & fs) { // TODO: add params, caching TRACE("nlsat_factor", tout << "factor\n" << p << "\n";); fs.reset(); m_cache.factor(p.get(), fs); } /** \brief Wrapper for psc chain computation */ void psc_chain(polynomial_ref & p, polynomial_ref & q, unsigned x, polynomial_ref_vector & result) { // TODO caching SASSERT(max_var(p) == max_var(q)); SASSERT(max_var(p) == x); m_cache.psc_chain(p, q, x, result); } /** \brief Store in ps the polynomials occurring in the given literals. */ void collect_polys(unsigned num, literal const * ls, polynomial_ref_vector & ps) { ps.reset(); for (unsigned i = 0; i < num; i++) { atom * a = m_atoms[ls[i].var()]; SASSERT(a != 0); if (a->is_ineq_atom()) { unsigned sz = to_ineq_atom(a)->size(); for (unsigned j = 0; j < sz; j++) ps.push_back(to_ineq_atom(a)->p(j)); } else { ps.push_back(to_root_atom(a)->p()); } } } /** \brief Add literal p != 0 into m_result. */ ptr_vector m_zero_fs; svector m_is_even; void add_zero_assumption(polynomial_ref & p) { // If p is of the form p1^n1 * ... * pk^nk, // then only the factors that are zero in the current interpretation needed to be considered. // I don't want to create a nested conjunction in the clause. // Then, I assert p_i1 * ... * p_im != 0 factor(p, m_factors); unsigned num_factors = m_factors.size(); m_zero_fs.reset(); m_is_even.reset(); polynomial_ref f(m_pm); for (unsigned i = 0; i < num_factors; i++) { f = m_factors.get(i); if (sign(f) == polynomial::sign_zero) { m_zero_fs.push_back(m_factors.get(i)); m_is_even.push_back(false); } } SASSERT(!m_zero_fs.empty()); // one of the factors must be zero in the current interpretation, since p is zero in it. literal l = m_solver.mk_ineq_literal(atom::EQ, m_zero_fs.size(), m_zero_fs.c_ptr(), m_is_even.c_ptr()); l.neg(); TRACE("nlsat_explain", tout << "adding (zero assumption) literal:\n"; display(tout, l); tout << "\n";); add_literal(l); } void add_simple_assumption(atom::kind k, poly * p, bool sign = false) { SASSERT(k == atom::EQ || k == atom::LT || k == atom::GT); bool is_even = false; bool_var b = m_solver.mk_ineq_atom(k, 1, &p, &is_even); literal l(b, !sign); add_literal(l); } void add_assumption(atom::kind k, poly * p, bool sign = false) { // TODO: factor add_simple_assumption(k, p, sign); } /** \brief Eliminate "vanishing leading coefficients" of p. That is, coefficients that vanish in the current interpretation. The resultant p is a reduct of p s.t. its leading coefficient does not vanish in the current interpretation. If all coefficients of p vanish, then the resultant p is the zero polynomial. */ void elim_vanishing(polynomial_ref & p) { SASSERT(!is_const(p)); var x = max_var(p); unsigned k = degree(p, x); SASSERT(k > 0); polynomial_ref lc(m_pm); polynomial_ref reduct(m_pm); while (true) { TRACE("nlsat_explain", tout << "elim vanishing x" << x << " k:" << k << " " << p << "\n";); if (is_const(p)) return; if (k == 0) { // x vanished from p, peek next maximal variable x = max_var(p); SASSERT(x != null_var); k = degree(p, x); } #if 0 anum const & x_val = m_assignment.value(x); if (m_am.is_zero(x_val)) { // add_zero_assumption(lc); lc = m_pm.coeff(p, x, k, reduct); k--; p = reduct; continue; } #endif if (m_pm.nonzero_const_coeff(p, x, k)) { TRACE("nlsat_explain", tout << "nonzero const x" << x << "\n";); return; // lc is a nonzero constant } lc = m_pm.coeff(p, x, k, reduct); TRACE("nlsat_explain", tout << "lc: " << lc << " reduct: " << reduct << "\n";); if (!is_zero(lc)) { if (sign(lc) != polynomial::sign_zero) return; // lc is not the zero polynomial, but it vanished in the current interpretation. // so we keep searching... add_zero_assumption(lc); } if (k == 0) { // all coefficients of p vanished in the current interpretation, // and were added as assumptions. p = m_pm.mk_zero(); return; } k--; p = reduct; } } /** Eliminate vanishing coefficients of polynomials in ps. The coefficients that are zero (i.e., vanished) are added as assumptions into m_result. */ void elim_vanishing(polynomial_ref_vector & ps) { unsigned j = 0; unsigned sz = ps.size(); polynomial_ref p(m_pm); for (unsigned i = 0; i < sz; i++) { p = ps.get(i); elim_vanishing(p); if (!is_const(p)) { ps.set(j, p); j++; } } ps.shrink(j); } /** Normalize literal with respect to given maximal variable. The basic idea is to eliminate vanishing (leading) coefficients from a (arithmetic) literal, and factors from lower stages. The vanishing coefficients and factors from lower stages are added as assumptions to the lemma being generated. Example 1) Assume - l is of the form (y^2 - 2)*x^3 + y*x + 1 > 0 - x is the maximal variable - y is assigned to sqrt(2) Thus, (y^2 - 2) the coefficient of x^3 vanished. This method returns y*x + 1 > 0 and adds the assumption (y^2 - 2) = 0 to the lemma Example 2) Assume - l is of the form (x + 2)*(y - 1) > 0 - x is the maximal variable - y is assigned to 0 (x + 2) < 0 is returned and assumption (y - 1) < 0 is added as an assumption. Remark: root atoms are not normalized */ literal normalize(literal l, var max) { bool_var b = l.var(); if (b == true_bool_var) return l; SASSERT(m_atoms[b] != 0); if (m_atoms[b]->is_ineq_atom()) { polynomial_ref_buffer ps(m_pm); sbuffer is_even; polynomial_ref p(m_pm); ineq_atom * a = to_ineq_atom(m_atoms[b]); int atom_sign = 1; unsigned sz = a->size(); bool normalized = false; // true if the literal needs to be normalized for (unsigned i = 0; i < sz; i++) { p = a->p(i); if (max_var(p) == max) elim_vanishing(p); // eliminate vanishing coefficients of max if (is_const(p) || max_var(p) < max) { int s = sign(p); if (!is_const(p)) { SASSERT(max_var(p) != null_var); SASSERT(max_var(p) < max); // factor p is a lower stage polynomial, so we should add assumption to justify p being eliminated if (s == 0) add_simple_assumption(atom::EQ, p); // add assumption p = 0 else if (a->is_even(i)) add_simple_assumption(atom::EQ, p, true); // add assumption p != 0 else if (s < 0) add_simple_assumption(atom::LT, p); // add assumption p < 0 else add_simple_assumption(atom::GT, p); // add assumption p > 0 } if (s == 0) { bool atom_val = a->get_kind() == atom::EQ; bool lit_val = l.sign() ? !atom_val : atom_val; return lit_val ? true_literal : false_literal; } else if (s < 0 && a->is_odd(i)) { atom_sign = -atom_sign; } normalized = true; } else { if (p != a->p(i)) { SASSERT(!m_pm.eq(p, a->p(i))); normalized = true; } is_even.push_back(a->is_even(i)); ps.push_back(p); } } if (ps.empty()) { SASSERT(atom_sign != 0); // LHS is positive or negative. It is positive if atom_sign > 0 and negative if atom_sign < 0 bool atom_val; if (a->get_kind() == atom::EQ) atom_val = false; else if (a->get_kind() == atom::LT) atom_val = atom_sign < 0; else atom_val = atom_sign > 0; bool lit_val = l.sign() ? !atom_val : atom_val; return lit_val ? true_literal : false_literal; } else if (normalized) { atom::kind new_k = a->get_kind(); if (atom_sign < 0) new_k = atom::flip(new_k); literal new_l = m_solver.mk_ineq_literal(new_k, ps.size(), ps.c_ptr(), is_even.c_ptr()); if (l.sign()) new_l.neg(); return new_l; } else { SASSERT(atom_sign > 0); return l; } } else { return l; } } /** Normalize literals (in the conflicting core) with respect to given maximal variable. The basic idea is to eliminate vanishing (leading) coefficients (and factors from lower stages) from (arithmetic) literals, */ void normalize(scoped_literal_vector & C, var max) { unsigned sz = C.size(); unsigned j = 0; for (unsigned i = 0; i < sz; i++) { literal new_l = normalize(C[i], max); if (new_l == true_literal) continue; if (new_l == false_literal) { // false literal was created. The assumptions added are sufficient for implying the conflict. C.reset(); return; } C.set(j, new_l); j++; } C.shrink(j); } var max_var(poly const * p) { return m_pm.max_var(p); } /** \brief Return the maximal variable in a set of nonconstant polynomials. */ var max_var(polynomial_ref_vector const & ps) { if (ps.empty()) return null_var; var max = max_var(ps.get(0)); SASSERT(max != null_var); // there are no constant polynomials in ps unsigned sz = ps.size(); for (unsigned i = 1; i < sz; i++) { var curr = m_pm.max_var(ps.get(i)); SASSERT(curr != null_var); if (curr > max) max = curr; } return max; } polynomial::var max_var(literal l) { atom * a = m_atoms[l.var()]; if (a != nullptr) return a->max_var(); else return null_var; } /** \brief Return the maximal variable in the given set of literals */ var max_var(unsigned sz, literal const * ls) { var max = null_var; for (unsigned i = 0; i < sz; i++) { literal l = ls[i]; atom * a = m_atoms[l.var()]; if (a != nullptr) { var x = a->max_var(); SASSERT(x != null_var); if (max == null_var || x > max) max = x; } } return max; } /** \brief Move the polynomials in q in ps that do not contain x to qs. */ void keep_p_x(polynomial_ref_vector & ps, var x, polynomial_ref_vector & qs) { unsigned sz = ps.size(); unsigned j = 0; for (unsigned i = 0; i < sz; i++) { poly * q = ps.get(i); if (max_var(q) != x) { qs.push_back(q); } else { ps.set(j, q); j++; } } ps.shrink(j); } /** \brief Add factors of p to todo */ void add_factors(polynomial_ref & p) { if (is_const(p)) return; elim_vanishing(p); if (is_const(p)) return; if (m_factor) { TRACE("nlsat_explain", display(tout << "adding factors of\n", p); tout << "\n";); factor(p, m_factors); polynomial_ref f(m_pm); for (unsigned i = 0; i < m_factors.size(); i++) { f = m_factors.get(i); elim_vanishing(f); if (!is_const(f)) { TRACE("nlsat_explain", tout << "adding factor:\n"; display(tout, f); tout << "\n";); m_todo.insert(f); } } } else { m_todo.insert(p); } } /** \brief Add leading coefficients of the polynomials in ps. \pre all polynomials in ps contain x Remark: the leading coefficients do not vanish in the current model, since all polynomials in ps were pre-processed using elim_vanishing. */ void add_lc(polynomial_ref_vector & ps, var x) { polynomial_ref p(m_pm); polynomial_ref lc(m_pm); unsigned sz = ps.size(); for (unsigned i = 0; i < sz; i++) { p = ps.get(i); unsigned k = degree(p, x); SASSERT(k > 0); TRACE("nlsat_explain", tout << "add_lc, x: "; display_var(tout, x); tout << "\nk: " << k << "\n"; display(tout, p); tout << "\n";); if (m_pm.nonzero_const_coeff(p, x, k)) { TRACE("nlsat_explain", tout << "constant coefficient, skipping...\n";); continue; } lc = m_pm.coeff(p, x, k); SASSERT(sign(lc) != 0); SASSERT(!is_const(lc)); add_factors(lc); } } /** \brief Add v-psc(p, q, x) into m_todo */ void psc(polynomial_ref & p, polynomial_ref & q, var x) { polynomial_ref_vector & S = m_psc_tmp; polynomial_ref s(m_pm); psc_chain(p, q, x, S); unsigned sz = S.size(); TRACE("nlsat_explain", tout << "computing psc of\n"; display(tout, p); tout << "\n"; display(tout, q); tout << "\n"; for (unsigned i = 0; i < sz; ++i) { s = S.get(i); tout << "psc: " << s << "\n"; }); for (unsigned i = 0; i < sz; i++) { s = S.get(i); TRACE("nlsat_explain", display(tout << "processing psc(" << i << ")\n", s) << "\n";); if (is_zero(s)) { TRACE("nlsat_explain", tout << "skipping psc is the zero polynomial\n";); continue; } if (is_const(s)) { TRACE("nlsat_explain", tout << "done, psc is a constant\n";); return; } if (sign(s) == polynomial::sign_zero) { TRACE("nlsat_explain", tout << "psc vanished, adding zero assumption\n";); add_zero_assumption(s); continue; } TRACE("nlsat_explain", tout << "adding v-psc of\n"; display(tout, p); tout << "\n"; display(tout, q); tout << "\n---->\n"; display(tout, s); tout << "\n";); // s did not vanish completely, but its leading coefficient may have vanished add_factors(s); return; } } /** \brief For each p in ps, add v-psc(x, p, p') into m_todo \pre all polynomials in ps contain x Remark: the leading coefficients do not vanish in the current model, since all polynomials in ps were pre-processed using elim_vanishing. */ void psc_discriminant(polynomial_ref_vector & ps, var x) { polynomial_ref p(m_pm); polynomial_ref p_prime(m_pm); unsigned sz = ps.size(); for (unsigned i = 0; i < sz; i++) { p = ps.get(i); if (degree(p, x) < 2) continue; p_prime = derivative(p, x); psc(p, p_prime, x); } } /** \brief For each p and q in ps, p != q, add v-psc(x, p, q) into m_todo \pre all polynomials in ps contain x Remark: the leading coefficients do not vanish in the current model, since all polynomials in ps were pre-processed using elim_vanishing. */ void psc_resultant(polynomial_ref_vector & ps, var x) { polynomial_ref p(m_pm); polynomial_ref q(m_pm); unsigned sz = ps.size(); for (unsigned i = 0; i < sz - 1; i++) { p = ps.get(i); for (unsigned j = i + 1; j < sz; j++) { q = ps.get(j); psc(p, q, x); } } } void test_root_literal(atom::kind k, var y, unsigned i, poly * p, scoped_literal_vector& result) { m_result = &result; add_root_literal(k, y, i, p); reset_already_added(); m_result = nullptr; } void add_root_literal(atom::kind k, var y, unsigned i, poly * p) { polynomial_ref pr(p, m_pm); TRACE("nlsat_explain", display(tout << "x" << y << " " << k << "[" << i << "](", pr); tout << ")\n";); if (!mk_linear_root(k, y, i, p) && !mk_quadratic_root(k, y, i, p)) { bool_var b = m_solver.mk_root_atom(k, y, i, p); literal l(b, true); TRACE("nlsat_explain", tout << "adding literal\n"; display(tout, l); tout << "\n";); add_literal(l); } } /** * literal can be expressed using a linear ineq_atom */ bool mk_linear_root(atom::kind k, var y, unsigned i, poly * p) { scoped_mpz c(m_pm.m()); if (m_pm.degree(p, y) == 1 && m_pm.const_coeff(p, y, 1, c)) { SASSERT(!m_pm.m().is_zero(c)); mk_linear_root(k, y, i, p, m_pm.m().is_neg(c)); return true; } return false; } /** Create pseudo-linear root depending on the sign of the coefficient to y. */ bool mk_plinear_root(atom::kind k, var y, unsigned i, poly * p) { if (m_pm.degree(p, y) != 1) { return false; } polynomial_ref c(m_pm); c = m_pm.coeff(p, y, 1); int s = sign(c); if (s == 0) { return false; } ensure_sign(c); mk_linear_root(k, y, i, p, s < 0); return true; } /** Encode root conditions for quadratic polynomials. Basically implements Thom's theorem. The roots are characterized by the sign of polynomials and their derivatives. b^2 - 4ac = 0: - there is only one root, which is -b/2a. - relation to root is a function of the sign of - 2ax + b b^2 - 4ac > 0: - assert i == 1 or i == 2 - relation to root is a function of the signs of: - 2ax + b - ax^2 + bx + c */ bool mk_quadratic_root(atom::kind k, var y, unsigned i, poly * p) { if (m_pm.degree(p, y) != 2) { return false; } if (i != 1 && i != 2) { return false; } SASSERT(m_assignment.is_assigned(y)); polynomial_ref A(m_pm), B(m_pm), C(m_pm), q(m_pm), p_diff(m_pm), yy(m_pm); A = m_pm.coeff(p, y, 2); B = m_pm.coeff(p, y, 1); C = m_pm.coeff(p, y, 0); // TBD throttle based on degree of q? q = (B*B) - (4*A*C); yy = m_pm.mk_polynomial(y); p_diff = 2*A*yy + B; p_diff = m_pm.normalize(p_diff); int sq = ensure_sign(q); if (sq < 0) { return false; } int sa = ensure_sign(A); if (sa == 0) { q = B*yy + C; return mk_plinear_root(k, y, i, q); } ensure_sign(p_diff); if (sq > 0) { polynomial_ref pr(p, m_pm); ensure_sign(pr); } return true; } int ensure_sign(polynomial_ref & p) { #if 0 polynomial_ref f(m_pm); factor(p, m_factors); m_is_even.reset(); unsigned num_factors = m_factors.size(); int s = 1; for (unsigned i = 0; i < num_factors; i++) { f = m_factors.get(i); s *= sign(f); m_is_even.push_back(false); } if (num_factors > 0) { atom::kind k = atom::EQ; if (s == 0) k = atom::EQ; if (s < 0) k = atom::LT; if (s > 0) k = atom::GT; bool_var b = m_solver.mk_ineq_atom(k, num_factors, m_factors.c_ptr(), m_is_even.c_ptr()); add_literal(literal(b, true)); } return s; #else int s = sign(p); if (!is_const(p)) { TRACE("nlsat_explain", tout << p << "\n";); add_simple_assumption(s == 0 ? atom::EQ : (s < 0 ? atom::LT : atom::GT), p); } return s; #endif } /** Auxiliary function to linear roots. y > root[1](-2*y - z) y > -z/2 y + z/2 > 0 2y + z > 0 */ void mk_linear_root(atom::kind k, var y, unsigned i, poly * p, bool mk_neg) { polynomial_ref p_prime(m_pm); p_prime = p; bool lsign = false; if (mk_neg) p_prime = neg(p_prime); p = p_prime.get(); switch (k) { case atom::ROOT_EQ: k = atom::EQ; lsign = false; break; case atom::ROOT_LT: k = atom::LT; lsign = false; break; case atom::ROOT_GT: k = atom::GT; lsign = false; break; case atom::ROOT_LE: k = atom::GT; lsign = true; break; case atom::ROOT_GE: k = atom::LT; lsign = true; break; default: UNREACHABLE(); break; } add_simple_assumption(k, p, lsign); } /** Add one or two literals that specify in which cell of variable y the current interpretation is. One literal is added for the cases: - y in (-oo, min) where min is the minimal root of the polynomials p2 in ps We add literal ! (y < root_1(p2)) - y in (max, oo) where max is the maximal root of the polynomials p1 in ps We add literal ! (y > root_k(p1)) where k is the number of real roots of p - y = r where r is the k-th root of a polynomial p in ps We add literal ! (y = root_k(p)) Two literals are added when - y in (l, u) where (l, u) does not contain any root of polynomials p in ps, and l is the i-th root of a polynomial p1 in ps, and u is the j-th root of a polynomial p2 in ps. We add literals ! (y > root_i(p1)) or !(y < root_j(p2)) */ void add_cell_lits(polynomial_ref_vector & ps, var y) { SASSERT(m_assignment.is_assigned(y)); bool lower_inf = true; bool upper_inf = true; scoped_anum_vector & roots = m_roots_tmp; scoped_anum lower(m_am); scoped_anum upper(m_am); anum const & y_val = m_assignment.value(y); TRACE("nlsat_explain", tout << "adding literals for "; display_var(tout, y); tout << " -> "; m_am.display_decimal(tout, y_val); tout << "\n";); polynomial_ref p_lower(m_pm); unsigned i_lower; polynomial_ref p_upper(m_pm); unsigned i_upper; polynomial_ref p(m_pm); unsigned sz = ps.size(); for (unsigned k = 0; k < sz; k++) { p = ps.get(k); if (max_var(p) != y) continue; roots.reset(); // Variable y is assigned in m_assignment. We must temporarily unassign it. // Otherwise, the isolate_roots procedure will assume p is a constant polynomial. m_am.isolate_roots(p, undef_var_assignment(m_assignment, y), roots); unsigned num_roots = roots.size(); for (unsigned i = 0; i < num_roots; i++) { int s = m_am.compare(y_val, roots[i]); TRACE("nlsat_explain", m_am.display_decimal(tout << "comparing root: ", roots[i]); tout << "\n"; m_am.display_decimal(tout << "with y_val:", y_val); tout << "\nsign " << s << "\n"; tout << "poly: " << p << "\n"; ); if (s == 0) { // y_val == roots[i] // add literal // ! (y = root_i(p)) add_root_literal(atom::ROOT_EQ, y, i+1, p); return; } else if (s < 0) { // y_val < roots[i] // check if roots[i] is a better upper bound if (upper_inf || m_am.lt(roots[i], upper)) { upper_inf = false; m_am.set(upper, roots[i]); p_upper = p; i_upper = i+1; } } else if (s > 0) { // roots[i] < y_val // check if roots[i] is a better lower bound if (lower_inf || m_am.lt(lower, roots[i])) { lower_inf = false; m_am.set(lower, roots[i]); p_lower = p; i_lower = i+1; } } } } if (!lower_inf) { add_root_literal(m_full_dimensional ? atom::ROOT_GE : atom::ROOT_GT, y, i_lower, p_lower); } if (!upper_inf) { add_root_literal(m_full_dimensional ? atom::ROOT_LE : atom::ROOT_LT, y, i_upper, p_upper); } } /** \brief Return true if all polynomials in ps are univariate in x. */ bool all_univ(polynomial_ref_vector const & ps, var x) { unsigned sz = ps.size(); for (unsigned i = 0; i < sz; i++) { poly * p = ps.get(i); if (max_var(p) != x) return false; if (!m_pm.is_univariate(p)) return false; } return true; } /** \brief Apply model-based projection operation defined in our paper. */ void project(polynomial_ref_vector & ps, var max_x) { if (ps.empty()) return; m_todo.reset(); for (poly* p : ps) { m_todo.insert(p); } var x = m_todo.remove_max_polys(ps); // Remark: after vanishing coefficients are eliminated, ps may not contain max_x anymore if (x < max_x) add_cell_lits(ps, x); while (true) { if (all_univ(ps, x) && m_todo.empty()) { m_todo.reset(); break; } TRACE("nlsat_explain", tout << "project loop, processing var "; display_var(tout, x); tout << "\npolynomials\n"; display(tout, ps); tout << "\n";); add_lc(ps, x); psc_discriminant(ps, x); psc_resultant(ps, x); if (m_todo.empty()) break; x = m_todo.remove_max_polys(ps); add_cell_lits(ps, x); } } bool check_already_added() const { for (bool b : m_already_added_literal) { (void)b; SASSERT(!b); } return true; } /* Conflicting core simplification using equations. The idea is to use equations to reduce the complexity of the conflicting core. Basic idea: Let l be of the form h > 0 and eq of the form p = 0 Using pseudo-division we have that: lc(p)^d h = q p + r where q and r are the pseudo-quotient and pseudo-remainder d is the integer returned by the pseudo-division algorithm. lc(p) is the leading coefficient of p If d is even or sign(lc(p)) > 0, we have that sign(h) = sign(r) Otherwise sign(h) = -sign(r) flipped the sign We have the following rules If (C and h > 0) implies false Then 1. (C and p = 0 and lc(p) != 0 and r > 0) implies false if d is even 2. (C and p = 0 and lc(p) > 0 and r > 0) implies false if lc(p) > 0 and d is odd 3. (C and p = 0 and lc(p) < 0 and r < 0) implies false if lc(p) < 0 and d is odd If (C and h = 0) implies false Then (C and p = 0 and lc(p) != 0 and r = 0) implies false If (C and h < 0) implies false Then 1. (C and p = 0 and lc(p) != 0 and r < 0) implies false if d is even 2. (C and p = 0 and lc(p) > 0 and r < 0) implies false if lc(p) > 0 and d is odd 3. (C and p = 0 and lc(p) < 0 and r > 0) implies false if lc(p) < 0 and d is odd Good cases: - lc(p) is a constant - p = 0 is already in the conflicting core - p = 0 is linear We only use equations from the conflicting core and lower stages. Equations from lower stages are automatically added to the lemma. */ struct eq_info { poly const * m_eq; polynomial::var m_x; unsigned m_k; poly * m_lc; int m_lc_sign; bool m_lc_const; bool m_lc_add; bool m_lc_add_ineq; void add_lc_ineq() { m_lc_add = true; m_lc_add_ineq = true; } void add_lc_diseq() { if (!m_lc_add) { m_lc_add = true; m_lc_add_ineq = false; } } }; void simplify(literal l, eq_info & info, var max, scoped_literal & new_lit) { bool_var b = l.var(); atom * a = m_atoms[b]; SASSERT(a); if (a->is_root_atom()) { new_lit = l; return; } ineq_atom * _a = to_ineq_atom(a); unsigned num_factors = _a->size(); if (num_factors == 1 && _a->p(0) == info.m_eq) { new_lit = l; return; } TRACE("nlsat_simplify_core", display(tout << "trying to simplify literal\n", l) << "\nusing equation\n"; m_pm.display(tout, info.m_eq, m_solver.display_proc()); tout << "\n";); int atom_sign = 1; bool modified_lit = false; polynomial_ref_buffer new_factors(m_pm); sbuffer new_factors_even; polynomial_ref new_factor(m_pm); for (unsigned s = 0; s < num_factors; s++) { poly * f = _a->p(s); bool is_even = _a->is_even(s); if (m_pm.degree(f, info.m_x) < info.m_k) { new_factors.push_back(f); new_factors_even.push_back(is_even); continue; } modified_lit = true; unsigned d; m_pm.pseudo_remainder(f, info.m_eq, info.m_x, d, new_factor); polynomial_ref fact(f, m_pm), eq(const_cast(info.m_eq), m_pm); TRACE("nlsat_simplify_core", tout << "d: " << d << " factor " << fact << " eq " << eq << " new factor " << new_factor << "\n";); // adjust sign based on sign of lc of eq if (d % 2 == 1 && // d is odd !is_even && // degree of the factor is odd info.m_lc_sign < 0) { // lc of the eq is negative atom_sign = -atom_sign; // flipped the sign of the current literal TRACE("nlsat_simplify_core", tout << "odd degree\n";); } if (is_const(new_factor)) { TRACE("nlsat_simplify_core", tout << "new factor is const\n";); polynomial::sign s = sign(new_factor); if (s == polynomial::sign_zero) { bool atom_val = a->get_kind() == atom::EQ; bool lit_val = l.sign() ? !atom_val : atom_val; new_lit = lit_val ? true_literal : false_literal; TRACE("nlsat_simplify_core", tout << "zero sign: " << info.m_lc_const << "\n";); if (!info.m_lc_const) { // We have essentially shown the current factor must be zero If the leading coefficient is not zero. // Note that, if the current factor is zero, then the whole polynomial is zero. // The atom is true if it is an equality, and false otherwise. // The sign of the leading coefficient (info.m_lc) of info.m_eq doesn't matter. // However, we have to store the fact it is not zero. info.add_lc_diseq(); } return; } else { TRACE("nlsat_simplify_core", tout << "non-zero sign: " << info.m_lc_const << "\n";); // We have shown the current factor is a constant MODULO the sign of the leading coefficient (of the equation used to rewrite the factor). if (!info.m_lc_const) { // If the leading coefficient is not a constant, we must store this information as an extra assumption. if (d % 2 == 0 || // d is even is_even || // rewriting a factor of even degree, sign flip doesn't matter _a->get_kind() == atom::EQ) { // rewriting an equation, sign flip doesn't matter info.add_lc_diseq(); } else { info.add_lc_ineq(); } } if (s < 0 && !is_even) { atom_sign = -atom_sign; } } } else { new_factors.push_back(new_factor); new_factors_even.push_back(is_even); if (!info.m_lc_const) { if (d % 2 == 0 || // d is even is_even || // rewriting a factor of even degree, sign flip doesn't matter _a->get_kind() == atom::EQ) { // rewriting an equation, sign flip doesn't matter info.add_lc_diseq(); } else { info.add_lc_ineq(); } } } } if (modified_lit) { atom::kind new_k = _a->get_kind(); if (atom_sign < 0) new_k = atom::flip(new_k); new_lit = m_solver.mk_ineq_literal(new_k, new_factors.size(), new_factors.c_ptr(), new_factors_even.c_ptr()); if (l.sign()) new_lit.neg(); TRACE("nlsat_simplify_core", tout << "simplified literal:\n"; display(tout, new_lit) << " " << m_solver.value(new_lit) << "\n";); if (max_var(new_lit) < max) { if (m_solver.value(new_lit) == l_true) { new_lit = l; } else { add_literal(new_lit); new_lit = true_literal; } } else { new_lit = normalize(new_lit, max); TRACE("nlsat_simplify_core", tout << "simplified literal after normalization:\n"; display(tout, new_lit); tout << " " << m_solver.value(new_lit) << "\n";); } } else { new_lit = l; } } bool simplify(scoped_literal_vector & C, poly const * eq, var max) { bool modified_core = false; eq_info info; info.m_eq = eq; info.m_x = m_pm.max_var(info.m_eq); info.m_k = m_pm.degree(eq, info.m_x); polynomial_ref lc_eq(m_pm); lc_eq = m_pm.coeff(eq, info.m_x, info.m_k); info.m_lc = lc_eq.get(); info.m_lc_sign = sign(lc_eq); info.m_lc_add = false; info.m_lc_add_ineq = false; info.m_lc_const = m_pm.is_const(lc_eq); SASSERT(info.m_lc != 0); scoped_literal new_lit(m_solver); unsigned sz = C.size(); unsigned j = 0; for (unsigned i = 0; i < sz; i++) { literal l = C[i]; new_lit = null_literal; simplify(l, info, max, new_lit); SASSERT(new_lit != null_literal); if (l == new_lit) { C.set(j, l); j++; continue; } modified_core = true; if (new_lit == true_literal) continue; if (new_lit == false_literal) { // false literal was created. The assumptions added are sufficient for implying the conflict. j = 0; // force core to be reset break; } C.set(j, new_lit); j++; } C.shrink(j); if (info.m_lc_add) { if (info.m_lc_add_ineq) add_assumption(info.m_lc_sign < 0 ? atom::LT : atom::GT, info.m_lc); else add_assumption(atom::EQ, info.m_lc, true); } return modified_core; } /** \brief (try to) Select an equation from C. Returns 0 if C does not contain any equality. This method selects the equation of minimal degree in max. */ poly * select_eq(scoped_literal_vector & C, var max) { poly * r = nullptr; unsigned min_d = UINT_MAX; unsigned sz = C.size(); for (unsigned i = 0; i < sz; i++) { literal l = C[i]; if (l.sign()) continue; bool_var b = l.var(); atom * a = m_atoms[b]; SASSERT(a != 0); if (a->get_kind() != atom::EQ) continue; ineq_atom * _a = to_ineq_atom(a); if (_a->size() > 1) continue; if (_a->is_even(0)) continue; unsigned d = m_pm.degree(_a->p(0), max); SASSERT(d > 0); if (d < min_d) { r = _a->p(0); min_d = d; if (min_d == 1) break; } } return r; } /** \brief Select an equation eq s.t. max_var(eq) < max, and it can be used to rewrite a literal in C. Return 0, if such equation was not found. */ var_vector m_select_tmp; ineq_atom * select_lower_stage_eq(scoped_literal_vector & C, var max) { var_vector & xs = m_select_tmp; for (literal l : C) { bool_var b = l.var(); atom * a = m_atoms[b]; if (a->is_root_atom()) continue; // we don't rewrite root atoms ineq_atom * _a = to_ineq_atom(a); unsigned num_factors = _a->size(); for (unsigned j = 0; j < num_factors; j++) { poly * p = _a->p(j); xs.reset(); m_pm.vars(p, xs); for (var y : xs) { if (y >= max) continue; atom * eq = m_x2eq[y]; if (eq == nullptr) continue; SASSERT(eq->is_ineq_atom()); SASSERT(to_ineq_atom(eq)->size() == 1); SASSERT(!to_ineq_atom(eq)->is_even(0)); poly * eq_p = to_ineq_atom(eq)->p(0); SASSERT(m_pm.degree(eq_p, y) > 0); // TODO: create a parameter // In the current experiments, using equations with non constant coefficients produces a blowup if (!m_pm.nonzero_const_coeff(eq_p, y, m_pm.degree(eq_p, y))) continue; if (m_pm.degree(p, y) >= m_pm.degree(eq_p, y)) return to_ineq_atom(eq); } } } return nullptr; } /** \brief Simplify the core using equalities. */ void simplify(scoped_literal_vector & C, var max) { // Simplify using equations in the core while (!C.empty()) { poly * eq = select_eq(C, max); if (eq == nullptr) break; TRACE("nlsat_simplify_core", tout << "using equality for simplifying core\n"; m_pm.display(tout, eq, m_solver.display_proc()); tout << "\n";); if (!simplify(C, eq, max)) break; } // Simplify using equations using variables from lower stages. while (!C.empty()) { ineq_atom * eq = select_lower_stage_eq(C, max); if (eq == nullptr) break; SASSERT(eq->size() == 1); SASSERT(!eq->is_even(0)); poly * eq_p = eq->p(0); VERIFY(simplify(C, eq_p, max)); // add equation as an assumption TRACE("nlsat_simpilfy_core", display(tout << "adding equality as assumption ", literal(eq->bvar(), true)); tout << "\n";); add_literal(literal(eq->bvar(), true)); } } /** \brief Main procedure. The explain the given unsat core, and store the result in m_result */ void main(unsigned num, literal const * ls) { if (num == 0) return; collect_polys(num, ls, m_ps); var max_x = max_var(m_ps); TRACE("nlsat_explain", tout << "polynomials in the conflict:\n"; display(tout, m_ps); tout << "\n";); elim_vanishing(m_ps); TRACE("nlsat_explain", tout << "elim vanishing\n"; display(tout, m_ps); tout << "\n";); project(m_ps, max_x); TRACE("nlsat_explain", tout << "after projection\n"; display(tout, m_ps); tout << "\n";); } void process2(unsigned num, literal const * ls) { if (m_simplify_cores) { m_core2.reset(); m_core2.append(num, ls); var max = max_var(num, ls); SASSERT(max != null_var); normalize(m_core2, max); TRACE("nlsat_explain", display(tout << "core after normalization\n", m_core2) << "\n";); simplify(m_core2, max); TRACE("nlsat_explain", display(tout << "core after simplify\n", m_core2) << "\n";); main(m_core2.size(), m_core2.c_ptr()); m_core2.reset(); } else { main(num, ls); } } // Auxiliary method for core minimization. literal_vector m_min_newtodo; bool minimize_core(literal_vector & todo, literal_vector & core) { SASSERT(!todo.empty()); literal_vector & new_todo = m_min_newtodo; new_todo.reset(); interval_set_manager & ism = m_evaluator.ism(); interval_set_ref r(ism); // Copy the union of the infeasible intervals of core into r. unsigned sz = core.size(); for (unsigned i = 0; i < sz; i++) { literal l = core[i]; atom * a = m_atoms[l.var()]; SASSERT(a != 0); interval_set_ref inf = m_evaluator.infeasible_intervals(a, l.sign(), nullptr); r = ism.mk_union(inf, r); if (ism.is_full(r)) { // Done return false; } } TRACE("nlsat_minimize", tout << "interval set after adding partial core:\n" << r << "\n";); if (todo.size() == 1) { // Done core.push_back(todo[0]); return false; } // Copy the union of the infeasible intervals of todo into r until r becomes full. sz = todo.size(); for (unsigned i = 0; i < sz; i++) { literal l = todo[i]; atom * a = m_atoms[l.var()]; SASSERT(a != 0); interval_set_ref inf = m_evaluator.infeasible_intervals(a, l.sign(), nullptr); r = ism.mk_union(inf, r); if (ism.is_full(r)) { // literal l must be in the core core.push_back(l); new_todo.swap(todo); return true; } else { new_todo.push_back(l); } } UNREACHABLE(); return true; } literal_vector m_min_todo; literal_vector m_min_core; void minimize(unsigned num, literal const * ls, scoped_literal_vector & r) { literal_vector & todo = m_min_todo; literal_vector & core = m_min_core; todo.reset(); core.reset(); todo.append(num, ls); while (true) { TRACE("nlsat_minimize", tout << "core minimization:\n"; display(tout, todo); tout << "\nCORE:\n"; display(tout, core);); if (!minimize_core(todo, core)) break; std::reverse(todo.begin(), todo.end()); TRACE("nlsat_minimize", tout << "core minimization:\n"; display(tout, todo); tout << "\nCORE:\n"; display(tout, core);); if (!minimize_core(todo, core)) break; } TRACE("nlsat_minimize", tout << "core:\n"; display(tout, core);); r.append(core.size(), core.c_ptr()); } void process(unsigned num, literal const * ls) { if (m_minimize_cores && num > 1) { m_core1.reset(); minimize(num, ls, m_core1); process2(m_core1.size(), m_core1.c_ptr()); m_core1.reset(); } else { process2(num, ls); } } void operator()(unsigned num, literal const * ls, scoped_literal_vector & result) { SASSERT(check_already_added()); SASSERT(num > 0); TRACE("nlsat_explain", tout << "[explain] set of literals is infeasible in the current interpretation\n"; display(tout, num, ls) << "\n"; m_assignment.display(tout); ); m_result = &result; process(num, ls); reset_already_added(); m_result = nullptr; TRACE("nlsat_explain", display(tout << "[explain] result\n", result) << "\n";); CASSERT("nlsat", check_already_added()); } void project(var x, unsigned num, literal const * ls, scoped_literal_vector & result) { m_result = &result; svector lits; TRACE("nlsat", tout << "project x" << x << "\n"; m_solver.display(tout, num, ls); m_solver.display(tout);); DEBUG_CODE( for (unsigned i = 0; i < num; ++i) { SASSERT(m_solver.value(ls[i]) == l_true); atom* a = m_atoms[ls[i].var()]; SASSERT(!a || m_evaluator.eval(a, ls[i].sign())); }); split_literals(x, num, ls, lits); collect_polys(lits.size(), lits.c_ptr(), m_ps); var mx_var = max_var(m_ps); if (!m_ps.empty()) { svector renaming; if (x != mx_var) { for (var i = 0; i < m_solver.num_vars(); ++i) { renaming.push_back(i); } std::swap(renaming[x], renaming[mx_var]); m_solver.reorder(renaming.size(), renaming.c_ptr()); TRACE("qe", tout << "x: " << x << " max: " << mx_var << " num_vars: " << m_solver.num_vars() << "\n"; m_solver.display(tout);); } elim_vanishing(m_ps); if (m_signed_project) { signed_project(m_ps, mx_var); } else { project(m_ps, mx_var); } reset_already_added(); m_result = nullptr; if (x != mx_var) { m_solver.restore_order(); } } else { reset_already_added(); m_result = nullptr; } for (unsigned i = 0; i < result.size(); ++i) { result.set(i, ~result[i]); } DEBUG_CODE( TRACE("nlsat", m_solver.display(tout, result.size(), result.c_ptr()) << "\n"; ); for (literal l : result) { CTRACE("nlsat", l_true != m_solver.value(l), m_solver.display(tout, l) << " " << m_solver.value(l) << "\n";); SASSERT(l_true == m_solver.value(l)); }); } void split_literals(var x, unsigned n, literal const* ls, svector& lits) { var_vector vs; for (unsigned i = 0; i < n; ++i) { vs.reset(); m_solver.vars(ls[i], vs); if (vs.contains(x)) { lits.push_back(ls[i]); } else { add_literal(~ls[i]); } } } /** Signed projection. Assumptions: - any variable in ps is at most x. - root expressions are satisfied (positive literals) Effect: - if x not in p, then - if sign(p) < 0: p < 0 - if sign(p) = 0: p = 0 - if sign(p) > 0: p > 0 else: - let roots_j be the roots of p_j or roots_j[i] - let L = { roots_j[i] | M(roots_j[i]) < M(x) } - let U = { roots_j[i] | M(roots_j[i]) > M(x) } - let E = { roots_j[i] | M(roots_j[i]) = M(x) } - let glb in L, s.t. forall l in L . M(glb) >= M(l) - let lub in U, s.t. forall u in U . M(lub) <= M(u) - if root in E, then - add E x . x = root & x > lb for lb in L - add E x . x = root & x < ub for ub in U - add E x . x = root & x = root2 for root2 in E \ { root } - else - assume |L| <= |U| (other case is symmetric) - add E x . lb <= x & x <= glb for lb in L - add E x . x = glb & x < ub for ub in U */ void signed_project(polynomial_ref_vector& ps, var x) { TRACE("nlsat_explain", tout << "Signed projection\n";); polynomial_ref p(m_pm); unsigned eq_index = 0; bool eq_valid = false; unsigned eq_degree = 0; for (unsigned i = 0; i < ps.size(); ++i) { p = ps.get(i); int s = sign(p); if (max_var(p) != x) { atom::kind k = (s == 0)?(atom::EQ):((s < 0)?(atom::LT):(atom::GT)); add_simple_assumption(k, p, false); ps[i] = ps.back(); ps.pop_back(); --i; } else if (s == 0) { if (!eq_valid || degree(p, x) < eq_degree) { eq_index = i; eq_valid = true; eq_degree = degree(p, x); } } } if (ps.empty()) { return; } if (ps.size() == 1) { project_single(x, ps.get(0)); return; } // ax + b = 0, p(x) > 0 -> if (eq_valid) { p = ps.get(eq_index); if (degree(p, x) == 1) { // ax + b = 0 // let d be maximal degree of x in p. // p(x) -> a^d*p(-b/a), a // perform virtual substitution with equality. solve_eq(x, eq_index, ps); } else { project_pairs(x, eq_index, ps); } return; } unsigned num_lub = 0, num_glb = 0; unsigned glb_index = 0, lub_index = 0; scoped_anum lub(m_am), glb(m_am), x_val(m_am); x_val = m_assignment.value(x); bool glb_valid = false, lub_valid = false; for (unsigned i = 0; i < ps.size(); ++i) { p = ps.get(i); scoped_anum_vector & roots = m_roots_tmp; roots.reset(); m_am.isolate_roots(p, undef_var_assignment(m_assignment, x), roots); for (auto const& r : roots) { int s = m_am.compare(x_val, r); SASSERT(s != 0); if (s < 0 && (!lub_valid || m_am.lt(r, lub))) { lub_index = i; m_am.set(lub, r); lub_valid = true; } if (s > 0 && (!glb_valid || m_am.lt(glb, r))) { glb_index = i; m_am.set(glb, r); glb_valid = true; } if (s < 0) ++num_lub; if (s > 0) ++num_glb; } } TRACE("nlsat_explain", tout << "glb: " << num_glb << " lub: " << num_lub << "\n" << lub_index << "\n" << glb_index << "\n" << ps << "\n";); if (num_lub == 0) { project_plus_infinity(x, ps); return; } if (num_glb == 0) { project_minus_infinity(x, ps); return; } if (num_lub <= num_glb) { glb_index = lub_index; } project_pairs(x, glb_index, ps); } void project_plus_infinity(var x, polynomial_ref_vector const& ps) { polynomial_ref p(m_pm), lc(m_pm); for (unsigned i = 0; i < ps.size(); ++i) { p = ps.get(i); unsigned d = degree(p, x); lc = m_pm.coeff(p, x, d); if (!is_const(lc)) { int s = sign(p); SASSERT(s != 0); atom::kind k = (s > 0)?(atom::GT):(atom::LT); add_simple_assumption(k, lc); } } } void project_minus_infinity(var x, polynomial_ref_vector const& ps) { polynomial_ref p(m_pm), lc(m_pm); for (unsigned i = 0; i < ps.size(); ++i) { p = ps.get(i); unsigned d = degree(p, x); lc = m_pm.coeff(p, x, d); if (!is_const(lc)) { int s = sign(p); TRACE("nlsat_explain", tout << "degree: " << d << " " << lc << " sign: " << s << "\n";); SASSERT(s != 0); atom::kind k; if (s > 0) { k = (d % 2 == 0)?(atom::GT):(atom::LT); } else { k = (d % 2 == 0)?(atom::LT):(atom::GT); } add_simple_assumption(k, lc); } } } void project_pairs(var x, unsigned idx, polynomial_ref_vector const& ps) { TRACE("nlsat_explain", tout << "project pairs\n";); polynomial_ref p(m_pm); p = ps.get(idx); for (unsigned i = 0; i < ps.size(); ++i) { if (i != idx) { project_pair(x, ps.get(i), p); } } } void project_pair(var x, polynomial::polynomial* p1, polynomial::polynomial* p2) { m_ps2.reset(); m_ps2.push_back(p1); m_ps2.push_back(p2); project(m_ps2, x); } void project_single(var x, polynomial::polynomial* p) { m_ps2.reset(); m_ps2.push_back(p); project(m_ps2, x); } void solve_eq(var x, unsigned idx, polynomial_ref_vector const& ps) { polynomial_ref p(m_pm), A(m_pm), B(m_pm), C(m_pm), D(m_pm), E(m_pm), q(m_pm), r(m_pm); polynomial_ref_vector As(m_pm), Bs(m_pm); p = ps.get(idx); SASSERT(degree(p, x) == 1); A = m_pm.coeff(p, x, 1); B = m_pm.coeff(p, x, 0); As.push_back(m_pm.mk_const(rational(1))); Bs.push_back(m_pm.mk_const(rational(1))); B = neg(B); TRACE("nlsat_explain", tout << "p: " << p << " A: " << A << " B: " << B << "\n";); // x = B/A for (unsigned i = 0; i < ps.size(); ++i) { if (i != idx) { q = ps.get(i); unsigned d = degree(q, x); D = m_pm.mk_const(rational(1)); E = D; r = m_pm.mk_zero(); for (unsigned j = As.size(); j <= d; ++j) { D = As.back(); As.push_back(A * D); D = Bs.back(); Bs.push_back(B * D); } for (unsigned j = 0; j <= d; ++j) { // A^d*p0 + A^{d-1}*B*p1 + ... + B^j*A^{d-j}*pj + ... + B^d*p_d C = m_pm.coeff(q, x, j); TRACE("nlsat_explain", tout << "coeff: q" << j << ": " << C << "\n";); if (!is_zero(C)) { D = As.get(d - j); E = Bs.get(j); r = r + D*E*C; } } TRACE("nlsat_explain", tout << "p: " << p << " q: " << q << " r: " << r << "\n";); ensure_sign(r); } else { ensure_sign(A); } } } void maximize(var x, unsigned num, literal const * ls, scoped_anum& val, bool& unbounded) { svector lits; polynomial_ref p(m_pm); split_literals(x, num, ls, lits); collect_polys(lits.size(), lits.c_ptr(), m_ps); unbounded = true; scoped_anum x_val(m_am); x_val = m_assignment.value(x); for (unsigned i = 0; i < m_ps.size(); ++i) { p = m_ps.get(i); scoped_anum_vector & roots = m_roots_tmp; roots.reset(); m_am.isolate_roots(p, undef_var_assignment(m_assignment, x), roots); for (unsigned j = 0; j < roots.size(); ++j) { int s = m_am.compare(x_val, roots[j]); if (s <= 0 && (unbounded || m_am.compare(roots[j], val) <= 0)) { unbounded = false; val = roots[j]; } } } } }; explain::explain(solver & s, assignment const & x2v, polynomial::cache & u, atom_vector const& atoms, atom_vector const& x2eq, evaluator & ev) { m_imp = alloc(imp, s, x2v, u, atoms, x2eq, ev); } explain::~explain() { dealloc(m_imp); } void explain::reset() { m_imp->m_core1.reset(); m_imp->m_core2.reset(); } void explain::set_simplify_cores(bool f) { m_imp->m_simplify_cores = f; } void explain::set_full_dimensional(bool f) { m_imp->m_full_dimensional = f; } void explain::set_minimize_cores(bool f) { m_imp->m_minimize_cores = f; } void explain::set_factor(bool f) { m_imp->m_factor = f; } void explain::set_signed_project(bool f) { m_imp->m_signed_project = f; } void explain::operator()(unsigned n, literal const * ls, scoped_literal_vector & result) { (*m_imp)(n, ls, result); } void explain::project(var x, unsigned n, literal const * ls, scoped_literal_vector & result) { m_imp->project(x, n, ls, result); } void explain::maximize(var x, unsigned n, literal const * ls, scoped_anum& val, bool& unbounded) { m_imp->maximize(x, n, ls, val, unbounded); } void explain::test_root_literal(atom::kind k, var y, unsigned i, poly* p, scoped_literal_vector & result) { m_imp->test_root_literal(k, y, i, p, result); } }; #ifdef Z3DEBUG void pp(nlsat::explain::imp & ex, unsigned num, nlsat::literal const * ls) { ex.display(std::cout, num, ls); } void pp(nlsat::explain::imp & ex, nlsat::scoped_literal_vector & ls) { ex.display(std::cout, ls); } void pp(nlsat::explain::imp & ex, polynomial_ref const & p) { ex.display(std::cout, p); std::cout << std::endl; } void pp(nlsat::explain::imp & ex, polynomial::polynomial * p) { polynomial_ref _p(p, ex.m_pm); ex.display(std::cout, _p); std::cout << std::endl; } void pp(nlsat::explain::imp & ex, polynomial_ref_vector const & ps) { ex.display(std::cout, ps); } void pp_var(nlsat::explain::imp & ex, nlsat::var x) { ex.display(std::cout, x); std::cout << std::endl; } void pp_lit(nlsat::explain::imp & ex, nlsat::literal l) { ex.display(std::cout, l); std::cout << std::endl; } #endif z3-z3-4.8.7/src/nlsat/nlsat_explain.h000066400000000000000000000067401356505360400173600ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: nlsat_explain.h Abstract: Functor that implements the "explain" procedure defined in Dejan and Leo's paper. Author: Leonardo de Moura (leonardo) 2012-01-13. Revision History: --*/ #ifndef NLSAT_EXPLAIN_H_ #define NLSAT_EXPLAIN_H_ #include "nlsat/nlsat_solver.h" #include "nlsat/nlsat_scoped_literal_vector.h" #include "math/polynomial/polynomial_cache.h" #include "math/polynomial/algebraic_numbers.h" namespace nlsat { class evaluator; class explain { public: struct imp; private: imp * m_imp; public: explain(solver & s, assignment const & x2v, polynomial::cache & u, atom_vector const& atoms, atom_vector const& x2eq, evaluator & ev); ~explain(); void reset(); void set_simplify_cores(bool f); void set_full_dimensional(bool f); void set_minimize_cores(bool f); void set_factor(bool f); void set_signed_project(bool f); /** \brief Given a set of literals ls[0], ... ls[n-1] s.t. - n > 0 - all of them are arithmetic literals. - all of them have the same maximal variable. - (ls[0] and ... and ls[n-1]) is infeasible in the current interpretation. Let x be the maximal variable in {ls[0], ..., ls[n-1]}. Remark: the current interpretation assigns all variables in ls[0], ..., ls[n-1] but x. This procedure stores in result a set of literals: s_1, ..., s_m s.t. - (s_1 or ... or s_m or ~ls[0] or ... or ~ls[n-1]) is a valid clause - s_1, ..., s_m do not contain variable x. - s_1, ..., s_m are false in the current interpretation */ void operator()(unsigned n, literal const * ls, scoped_literal_vector & result); /** \brief projection for a given variable. Given: M |= l1[x] /\ ... /\ ln[x] Find: M |= s1, ..., sm Such that: |= ~s1 \/ ... \/ ~sm \/ E x. l1[x] /\ ... /\ ln[x] Contrast this with with the core-based projection above: Given: M |= A x . l1[x] \/ ... \/ ln[x] Find: M |= ~s1, ..., ~sm. Such that: |= s1 \/ ... \/ sm \/ A x . l1[x] \/ ... \/ ln[x] Claim: the two compute the same solutions if the projection operators are independent of the value of x. Claim: A complete, convergent projection operator can be obtained from M in a way that is independent of x. */ void project(var x, unsigned n, literal const * ls, scoped_literal_vector & result); /** Maximize the value of x (locally) under the current assignment to other variables and while maintaining the assignment to the literals ls. Set unbounded to 'true' if the value of x is unbounded. Precondition: the set of literals are true in the current model. By local optimization we understand that x is increased to the largest value within the signs delineated by the roots of the polynomials in ls. */ void maximize(var x, unsigned n, literal const * ls, scoped_anum& val, bool& unbounded); /** Unit test routine. */ void test_root_literal(atom::kind k, var y, unsigned i, poly* p, scoped_literal_vector & result); }; }; #endif z3-z3-4.8.7/src/nlsat/nlsat_interval_set.cpp000066400000000000000000000727551356505360400207630ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: nlsat_interval_set.cpp Abstract: Sets of disjoint infeasible intervals. Author: Leonardo de Moura (leonardo) 2012-01-11. Revision History: --*/ #include "nlsat/nlsat_interval_set.h" #include "math/polynomial/algebraic_numbers.h" #include "util/buffer.h" namespace nlsat { struct interval { unsigned m_lower_open:1; unsigned m_upper_open:1; unsigned m_lower_inf:1; unsigned m_upper_inf:1; literal m_justification; clause const* m_clause; anum m_lower; anum m_upper; }; class interval_set { public: static unsigned get_obj_size(unsigned num) { return sizeof(interval_set) + num*sizeof(interval); } unsigned m_num_intervals; unsigned m_ref_count:31; unsigned m_full:1; interval m_intervals[0]; }; void display(std::ostream & out, anum_manager & am, interval const & curr) { if (curr.m_lower_inf) { out << "(-oo, "; } else { if (curr.m_lower_open) out << "("; else out << "["; am.display_decimal(out, curr.m_lower); out << ", "; } if (curr.m_justification.sign()) out << "~"; out << "p"; out << curr.m_justification.var() << ", "; if (curr.m_upper_inf) { out << "oo)"; } else { am.display_decimal(out, curr.m_upper); if (curr.m_upper_open) out << ")"; else out << "]"; } } bool check_interval(anum_manager & am, interval const & i) { if (i.m_lower_inf) { SASSERT(i.m_lower_open); } if (i.m_upper_inf) { SASSERT(i.m_upper_open); } if (!i.m_lower_inf && !i.m_upper_inf) { int s = am.compare(i.m_lower, i.m_upper); TRACE("nlsat_interval", tout << "lower: "; am.display_decimal(tout, i.m_lower); tout << ", upper: "; am.display_decimal(tout, i.m_upper); tout << "\ns: " << s << "\n";); SASSERT(s <= 0); if (s == 0) { SASSERT(!i.m_lower_open && !i.m_upper_open); } } return true; } bool check_no_overlap(anum_manager & am, interval const & curr, interval const & next) { SASSERT(!curr.m_upper_inf); SASSERT(!next.m_lower_inf); int sign = am.compare(curr.m_upper, next.m_lower); CTRACE("nlsat", sign > 0, display(tout, am, curr); tout << " "; display(tout, am, next); tout << "\n";); SASSERT(sign <= 0); if (sign == 0) { SASSERT(curr.m_upper_open || next.m_lower_open); } return true; } // Check if the intervals are valid, ordered, and are disjoint. bool check_interval_set(anum_manager & am, unsigned sz, interval const * ints) { DEBUG_CODE( for (unsigned i = 0; i < sz; i++) { interval const & curr = ints[i]; SASSERT(check_interval(am, curr)); if (i < sz - 1) { SASSERT(check_no_overlap(am, curr, ints[i+1])); } }); return true; } interval_set_manager::interval_set_manager(anum_manager & m, small_object_allocator & a): m_am(m), m_allocator(a) { } interval_set_manager::~interval_set_manager() { } void interval_set_manager::del(interval_set * s) { if (s == nullptr) return; unsigned num = s->m_num_intervals; unsigned obj_sz = interval_set::get_obj_size(num); for (unsigned i = 0; i < num; i++) { m_am.del(s->m_intervals[i].m_lower); m_am.del(s->m_intervals[i].m_upper); } s->~interval_set(); m_allocator.deallocate(obj_sz, s); } void interval_set_manager::dec_ref(interval_set * s) { SASSERT(s->m_ref_count > 0); s->m_ref_count--; if (s->m_ref_count == 0) del(s); } void interval_set_manager::inc_ref(interval_set * s) { s->m_ref_count++; } interval_set * interval_set_manager::mk(bool lower_open, bool lower_inf, anum const & lower, bool upper_open, bool upper_inf, anum const & upper, literal justification, clause const* cls) { void * mem = m_allocator.allocate(interval_set::get_obj_size(1)); interval_set * new_set = new (mem) interval_set(); new_set->m_num_intervals = 1; new_set->m_ref_count = 0; new_set->m_full = lower_inf && upper_inf; interval * i = new (new_set->m_intervals) interval(); i->m_lower_open = lower_open; i->m_lower_inf = lower_inf; i->m_upper_open = upper_open; i->m_upper_inf = upper_inf; i->m_justification = justification; i->m_clause = cls; if (!lower_inf) m_am.set(i->m_lower, lower); if (!upper_inf) m_am.set(i->m_upper, upper); SASSERT(check_interval_set(m_am, 1, new_set->m_intervals)); return new_set; } inline int compare_lower_lower(anum_manager & am, interval const & i1, interval const & i2) { if (i1.m_lower_inf && i2.m_lower_inf) return 0; if (i1.m_lower_inf) return -1; if (i2.m_lower_inf) return 1; SASSERT(!i1.m_lower_inf && !i2.m_lower_inf); int s = am.compare(i1.m_lower, i2.m_lower); if (s != 0) return s; if (i1.m_lower_open == i2.m_lower_open) return 0; if (i1.m_lower_open) return 1; else return -1; } inline int compare_upper_upper(anum_manager & am, interval const & i1, interval const & i2) { if (i1.m_upper_inf && i2.m_upper_inf) return 0; if (i1.m_upper_inf) return 1; if (i2.m_upper_inf) return -1; SASSERT(!i1.m_upper_inf && !i2.m_upper_inf); int s = am.compare(i1.m_upper, i2.m_upper); if (s != 0) return s; if (i1.m_upper_open == i2.m_upper_open) return 0; if (i1.m_upper_open) return -1; else return 1; } inline int compare_upper_lower(anum_manager & am, interval const & i1, interval const & i2) { if (i1.m_upper_inf || i2.m_lower_inf) return 1; SASSERT(!i1.m_upper_inf && !i2.m_lower_inf); int s = am.compare(i1.m_upper, i2.m_lower); if (s != 0) return s; if (!i1.m_upper_open && !i2.m_lower_open) return 0; return -1; } typedef sbuffer interval_buffer; // Given two interval in an interval set s.t. curr occurs before next. // We say curr and next are "adjacent" iff // there is no "space" between them. bool adjacent(anum_manager & am, interval const & curr, interval const & next) { SASSERT(!curr.m_upper_inf); SASSERT(!next.m_lower_inf); int sign = am.compare(curr.m_upper, next.m_lower); SASSERT(sign <= 0); if (sign == 0) { SASSERT(curr.m_upper_open || next.m_lower_open); return !curr.m_upper_open || !next.m_lower_open; } return false; } inline void push_back(anum_manager & am, interval_buffer & buf, bool lower_open, bool lower_inf, anum const & lower, bool upper_open, bool upper_inf, anum const & upper, literal justification) { buf.push_back(interval()); interval & i = buf.back(); i.m_lower_open = lower_open; i.m_lower_inf = lower_inf; am.set(i.m_lower, lower); i.m_upper_open = upper_open; i.m_upper_inf = upper_inf; am.set(i.m_upper, upper); i.m_justification = justification; SASSERT(check_interval(am, i)); } inline void push_back(anum_manager & am, interval_buffer & buf, interval const & i) { push_back(am, buf, i.m_lower_open, i.m_lower_inf, i.m_lower, i.m_upper_open, i.m_upper_inf, i.m_upper, i.m_justification); } inline interval_set * mk_interval(small_object_allocator & allocator, interval_buffer & buf, bool full) { unsigned sz = buf.size(); void * mem = allocator.allocate(interval_set::get_obj_size(sz)); interval_set * new_set = new (mem) interval_set(); new_set->m_full = full; new_set->m_ref_count = 0; new_set->m_num_intervals = sz; memcpy(new_set->m_intervals, buf.c_ptr(), sizeof(interval)*sz); return new_set; } interval_set * interval_set_manager::mk_union(interval_set const * s1, interval_set const * s2) { TRACE("nlsat_interval", tout << "mk_union\ns1: "; display(tout, s1); tout << "\ns2: "; display(tout, s2); tout << "\n";); if (s1 == nullptr || s1 == s2) return const_cast(s2); if (s2 == nullptr) return const_cast(s1); if (s1->m_full) return const_cast(s1); if (s2->m_full) return const_cast(s2); interval_buffer result; unsigned sz1 = s1->m_num_intervals; unsigned sz2 = s2->m_num_intervals; unsigned i1 = 0; unsigned i2 = 0; while (true) { if (i1 >= sz1) { while (i2 < sz2) { TRACE("nlsat_interval", tout << "adding remaining intervals from s2: "; nlsat::display(tout, m_am, s2->m_intervals[i2]); tout << "\n";); push_back(m_am, result, s2->m_intervals[i2]); i2++; } break; } if (i2 >= sz2) { while (i1 < sz1) { TRACE("nlsat_interval", tout << "adding remaining intervals from s1: "; nlsat::display(tout, m_am, s1->m_intervals[i1]); tout << "\n";); push_back(m_am, result, s1->m_intervals[i1]); i1++; } break; } interval const & int1 = s1->m_intervals[i1]; interval const & int2 = s2->m_intervals[i2]; int l1_l2_sign = compare_lower_lower(m_am, int1, int2); int u1_u2_sign = compare_upper_upper(m_am, int1, int2); TRACE("nlsat_interval", tout << "i1: " << i1 << ", i2: " << i2 << "\n"; tout << "int1: "; nlsat::display(tout, m_am, int1); tout << "\n"; tout << "int2: "; nlsat::display(tout, m_am, int2); tout << "\n";); if (l1_l2_sign <= 0) { if (u1_u2_sign == 0) { // Cases: // 1) [ ] // [ ] // // 2) [ ] // [ ] // TRACE("nlsat_interval", tout << "l1_l2_sign <= 0, u1_u2_sign == 0\n";); push_back(m_am, result, int1); i1++; i2++; } else if (u1_u2_sign > 0) { // Cases: // // 1) [ ] // [ ] // // 2) [ ] // [ ] i2++; TRACE("nlsat_interval", tout << "l1_l2_sign <= 0, u1_u2_sign > 0\n";); // i1 may consume other intervals of s2 } else { SASSERT(u1_u2_sign < 0); int u1_l2_sign = compare_upper_lower(m_am, int1, int2); if (u1_l2_sign < 0) { SASSERT(l1_l2_sign < 0); // Cases: // 1) [ ] // [ ] TRACE("nlsat_interval", tout << "l1_l2_sign <= 0, u1_u2_sign < 0, u1_l2_sign < 0\n";); push_back(m_am, result, int1); i1++; } else if (u1_l2_sign == 0) { SASSERT(l1_l2_sign <= 0); SASSERT(!int1.m_upper_open && !int2.m_lower_open); SASSERT(!int2.m_lower_inf); TRACE("nlsat_interval", tout << "l1_l2_sign <= 0, u1_u2_sign < 0, u1_l2_sign == 0\n";); // Cases: if (l1_l2_sign != 0) { SASSERT(l1_l2_sign < 0); // 1) [ ] // [ ] SASSERT(!int2.m_lower_open); push_back(m_am, result, int1.m_lower_open, int1.m_lower_inf, int1.m_lower, true /* open */, false /* not +oo */, int1.m_upper, int1.m_justification); i1++; } else { SASSERT(l1_l2_sign == 0); // 2) u <<< int1 is a singleton // [ ] // just consume int1 i1++; } } else { SASSERT(l1_l2_sign <= 0); SASSERT(u1_u2_sign < 0); SASSERT(u1_l2_sign > 0); TRACE("nlsat_interval", tout << "l1_l2_sign <= 0, u1_u2_sign < 0, u1_l2_sign > 0\n";); if (l1_l2_sign == 0) { // Case: // 1) [ ] // [ ] // just consume int1 i1++; } else { SASSERT(l1_l2_sign < 0); SASSERT(u1_u2_sign < 0); SASSERT(u1_l2_sign > 0); // 2) [ ] // [ ] push_back(m_am, result, int1.m_lower_open, int1.m_lower_inf, int1.m_lower, !int2.m_lower_open, false /* not +oo */, int2.m_lower, int1.m_justification); i1++; } } } } else { SASSERT(l1_l2_sign > 0); if (u1_u2_sign == 0) { TRACE("nlsat_interval", tout << "l1_l2_sign > 0, u1_u2_sign == 0\n";); // Case: // 1) [ ] // [ ] // push_back(m_am, result, int2); i1++; i2++; } else if (u1_u2_sign < 0) { TRACE("nlsat_interval", tout << "l1_l2_sign > 0, u1_u2_sign > 0\n";); // Case: // 1) [ ] // [ ] i1++; // i2 may consume other intervals of s1 } else { int u2_l1_sign = compare_upper_lower(m_am, int2, int1); if (u2_l1_sign < 0) { TRACE("nlsat_interval", tout << "l1_l2_sign > 0, u1_u2_sign > 0, u2_l1_sign < 0\n";); // Case: // 1) [ ] // [ ] push_back(m_am, result, int2); i2++; } else if (u2_l1_sign == 0) { TRACE("nlsat_interval", tout << "l1_l2_sign > 0, u1_u2_sign > 0, u2_l1_sign == 0\n";); SASSERT(!int1.m_lower_open && !int2.m_upper_open); SASSERT(!int1.m_lower_inf); // Case: // [ ] // [ ] push_back(m_am, result, int2.m_lower_open, int2.m_lower_inf, int2.m_lower, true /* open */, false /* not +oo */, int2.m_upper, int2.m_justification); i2++; } else { TRACE("nlsat_interval", tout << "l1_l2_sign > 0, u1_u2_sign > 0, u2_l1_sign > 0\n";); SASSERT(l1_l2_sign > 0); SASSERT(u1_u2_sign > 0); SASSERT(u2_l1_sign > 0); // Case: // [ ] // [ ] push_back(m_am, result, int2.m_lower_open, int2.m_lower_inf, int2.m_lower, !int1.m_lower_open, false /* not +oo */, int1.m_lower, int2.m_justification); i2++; } } } SASSERT(result.size() <= 1 || check_no_overlap(m_am, result[result.size() - 2], result[result.size() - 1])); } SASSERT(!result.empty()); SASSERT(check_interval_set(m_am, result.size(), result.c_ptr())); // Compress // Remark: we only combine adjacent intervals when they have the same justification unsigned j = 0; unsigned sz = result.size(); for (unsigned i = 1; i < sz; i++) { interval & curr = result[j]; interval & next = result[i]; if (curr.m_justification == next.m_justification && adjacent(m_am, curr, next)) { // merge them curr.m_upper_inf = next.m_upper_inf; curr.m_upper_open = next.m_upper_open; m_am.swap(curr.m_upper, next.m_upper); } else { j++; if (i != j) { interval & next_curr = result[j]; next_curr.m_lower_inf = next.m_lower_inf; next_curr.m_lower_open = next.m_lower_open; m_am.swap(next_curr.m_lower, next.m_lower); next_curr.m_upper_inf = next.m_upper_inf; next_curr.m_upper_open = next.m_upper_open; m_am.swap(next_curr.m_upper, next.m_upper); next_curr.m_justification = next.m_justification; } } } j++; for (unsigned i = j; i < sz; i++) { interval & curr = result[i]; m_am.del(curr.m_lower); m_am.del(curr.m_upper); } result.shrink(j); SASSERT(check_interval_set(m_am, result.size(), result.c_ptr())); sz = j; SASSERT(sz >= 1); bool found_slack = !result[0].m_lower_inf || !result[sz-1].m_upper_inf; // Check if full for (unsigned i = 0; i < sz - 1 && !found_slack; i++) { if (!adjacent(m_am, result[i], result[i+1])) found_slack = true; } // Create new interval set interval_set * new_set = mk_interval(m_allocator, result, !found_slack); SASSERT(check_interval_set(m_am, sz, new_set->m_intervals)); return new_set; } bool interval_set_manager::is_full(interval_set const * s) { if (s == nullptr) return false; return s->m_full == 1; } unsigned interval_set_manager::num_intervals(interval_set const * s) const { if (s == nullptr) return 0; return s->m_num_intervals; } bool interval_set_manager::subset(interval_set const * s1, interval_set const * s2) { if (s1 == s2) return true; if (s1 == nullptr) return true; if (s2 == nullptr) return false; if (s2->m_full) return true; if (s1->m_full) return false; unsigned sz1 = s1->m_num_intervals; unsigned sz2 = s2->m_num_intervals; SASSERT(sz1 > 0 && sz2 > 0); unsigned i1 = 0; unsigned i2 = 0; while (i1 < sz1 && i2 < sz2) { interval const & int1 = s1->m_intervals[i1]; interval const & int2 = s2->m_intervals[i2]; TRACE("nlsat_interval", tout << "subset main loop, i1: " << i1 << ", i2: " << i2 << "\n"; tout << "int1: "; nlsat::display(tout, m_am, int1); tout << "\n"; tout << "int2: "; nlsat::display(tout, m_am, int2); tout << "\n";); if (compare_lower_lower(m_am, int1, int2) < 0) { TRACE("nlsat_interval", tout << "done\n";); // interval [int1.lower1, int2.lower2] is not in s2 // s1: [ ... // s2: [ ... return false; } while (i2 < sz2) { interval const & int2 = s2->m_intervals[i2]; TRACE("nlsat_interval", tout << "inner loop, i2: " << i2 << "\n"; tout << "int2: "; nlsat::display(tout, m_am, int2); tout << "\n";); int u1_u2_sign = compare_upper_upper(m_am, int1, int2); if (u1_u2_sign == 0) { TRACE("nlsat_interval", tout << "case 1, break\n";); // consume both // s1: ... ] // s2: ... ] i1++; i2++; break; } else if (u1_u2_sign < 0) { TRACE("nlsat_interval", tout << "case 2, break\n";); // consume only int1, int2 may cover other intervals of s1 // s1: ... ] // s2: ... ] i1++; break; } else { SASSERT(u1_u2_sign > 0); int u2_l1_sign = compare_upper_lower(m_am, int2, int1); TRACE("nlsat_interval", tout << "subset, u2_l1_sign: " << u2_l1_sign << "\n";); if (u2_l1_sign < 0) { TRACE("nlsat_interval", tout << "case 3, break\n";); // s1: [ ... // s2: [ ... ] ... i2++; break; } SASSERT(u2_l1_sign >= 0); // s1: [ ... ] // s2: [ ... ] if (i2 == sz2 - 1) { TRACE("nlsat_interval", tout << "case 4, done\n";); // s1: ... ] // s2: ...] // the interval [int2.upper, int1.upper] is not in s2 return false; // last interval of s2 } interval const & next2 = s2->m_intervals[i2+1]; if (!adjacent(m_am, int2, next2)) { TRACE("nlsat_interval", tout << "not adjacent, done\n";); // s1: ... ] // s2: ... ] [ // the interval [int2.upper, min(int1.upper, next2.lower)] is not in s2 return false; } TRACE("nlsat_interval", tout << "continue..\n";); // continue with adjacent interval of s2 // s1: ... ] // s2: ..][ ... i2++; } } } return i1 == sz1; } bool interval_set_manager::set_eq(interval_set const * s1, interval_set const * s2) { if (s1 == nullptr || s2 == nullptr) return s1 == s2; if (s1->m_full || s2->m_full) return s1->m_full == s2->m_full; // TODO: check if bottleneck, then replace simple implementation return subset(s1, s2) && subset(s2, s1); } bool interval_set_manager::eq(interval_set const * s1, interval_set const * s2) { if (s1 == nullptr || s2 == nullptr) return s1 == s2; if (s1->m_num_intervals != s2->m_num_intervals) return false; for (unsigned i = 0; i < s1->m_num_intervals; i++) { interval const & int1 = s1->m_intervals[i]; interval const & int2 = s2->m_intervals[i]; if (int1.m_lower_inf != int2.m_lower_inf || int1.m_lower_open != int2.m_lower_open || int1.m_upper_inf != int2.m_upper_inf || int1.m_upper_open != int2.m_upper_open || int1.m_justification != int2.m_justification || !m_am.eq(int1.m_lower, int2.m_lower) || !m_am.eq(int1.m_upper, int2.m_upper)) return false; } return true; } void interval_set_manager::get_justifications(interval_set const * s, literal_vector & js, ptr_vector& clauses) { js.reset(); clauses.reset(); unsigned num = num_intervals(s); for (unsigned i = 0; i < num; i++) { literal l = s->m_intervals[i].m_justification; unsigned lidx = l.index(); if (m_already_visited.get(lidx, false)) continue; m_already_visited.setx(lidx, true, false); js.push_back(l); if (s->m_intervals[i].m_clause) { clauses.push_back(const_cast(s->m_intervals[i].m_clause)); } } for (unsigned i = 0; i < num; i++) { literal l = s->m_intervals[i].m_justification; unsigned lidx = l.index(); m_already_visited[lidx] = false; } } interval_set * interval_set_manager::get_interval(interval_set const * s, unsigned idx) const { SASSERT(idx < num_intervals(s)); interval_buffer result; push_back(m_am, result, s->m_intervals[idx]); bool found_slack = !result[0].m_lower_inf || !result[0].m_upper_inf; interval_set * new_set = mk_interval(m_allocator, result, !found_slack); SASSERT(check_interval_set(m_am, result.size(), new_set->m_intervals)); return new_set; } void interval_set_manager::peek_in_complement(interval_set const * s, bool is_int, anum & w, bool randomize) { SASSERT(!is_full(s)); if (s == nullptr) { if (randomize) { int num = m_rand() % 2 == 0 ? 1 : -1; #define MAX_RANDOM_DEN_K 4 int den_k = (m_rand() % MAX_RANDOM_DEN_K); int den = is_int ? 1 : (1 << den_k); scoped_mpq _w(m_am.qm()); m_am.qm().set(_w, num, den); m_am.set(w, _w); return; } else { m_am.set(w, 0); return; } } unsigned n = 0; unsigned num = num_intervals(s); if (!s->m_intervals[0].m_lower_inf) { // lower is not -oo n++; m_am.int_lt(s->m_intervals[0].m_lower, w); if (!randomize) return; } if (!s->m_intervals[num-1].m_upper_inf) { // upper is not oo n++; if (n == 1 || m_rand()%n == 0) m_am.int_gt(s->m_intervals[num-1].m_upper, w); if (!randomize) return; } // Try to find a gap that is not an unit. for (unsigned i = 1; i < num; i++) { if (m_am.lt(s->m_intervals[i-1].m_upper, s->m_intervals[i].m_lower)) { n++; if (n == 1 || m_rand()%n == 0) m_am.select(s->m_intervals[i-1].m_upper, s->m_intervals[i].m_lower, w); if (!randomize) return; } } if (n > 0) return; // Try to find a rational unsigned irrational_i = UINT_MAX; for (unsigned i = 1; i < num; i++) { if (s->m_intervals[i-1].m_upper_open && s->m_intervals[i].m_lower_open) { SASSERT(m_am.eq(s->m_intervals[i-1].m_upper, s->m_intervals[i].m_lower)); // otherwise we would have found it in the previous step if (m_am.is_rational(s->m_intervals[i-1].m_upper)) { m_am.set(w, s->m_intervals[i-1].m_upper); return; } if (irrational_i == UINT_MAX) irrational_i = i-1; } } SASSERT(irrational_i != UINT_MAX); // Last option: peek irrational witness :-( SASSERT(s->m_intervals[irrational_i].m_upper_open && s->m_intervals[irrational_i+1].m_lower_open); m_am.set(w, s->m_intervals[irrational_i].m_upper); } std::ostream& interval_set_manager::display(std::ostream & out, interval_set const * s) const { if (s == nullptr) { out << "{}"; return out; } out << "{"; for (unsigned i = 0; i < s->m_num_intervals; i++) { if (i > 0) out << ", "; nlsat::display(out, m_am, s->m_intervals[i]); } out << "}"; if (s->m_full) out << "*"; return out; } }; z3-z3-4.8.7/src/nlsat/nlsat_interval_set.h000066400000000000000000000066261356505360400204220ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: nlsat_interval_set.h Abstract: Sets of disjoint infeasible intervals. Author: Leonardo de Moura (leonardo) 2012-01-11. Revision History: --*/ #ifndef NLSAT_INTERVAL_SET_H_ #define NLSAT_INTERVAL_SET_H_ #include "nlsat/nlsat_types.h" namespace nlsat { class interval_set; class interval_set_manager { anum_manager & m_am; small_object_allocator & m_allocator; svector m_already_visited; random_gen m_rand; void del(interval_set * s); public: interval_set_manager(anum_manager & m, small_object_allocator & a); ~interval_set_manager(); void set_seed(unsigned s) { m_rand.set_seed(s); } /** \brief Return the empty set. */ interval_set * mk_empty() { return nullptr; } /** \brief Return a set of composed of a single interval. */ interval_set * mk(bool lower_open, bool lower_inf, anum const & lower, bool upper_open, bool upper_inf, anum const & upper, literal justification, clause const* cls); /** \brief Return the union of two sets. */ interval_set * mk_union(interval_set const * s1, interval_set const * s2); /** \brief Reference counting */ void dec_ref(interval_set * s); void inc_ref(interval_set * s); /** \brief Return true if s is the empty set. */ bool is_empty(interval_set const * s) { return s == nullptr; } /** \brief Return true if the set contains all real numbers. */ bool is_full(interval_set const * s); /** `\brief Return true if s1 is a subset of s2. */ bool subset(interval_set const * s1, interval_set const * s2); /** \brief Return true if s1 and s2 cover the same subset of R. The justifications are ignored */ bool set_eq(interval_set const * s1, interval_set const * s2); /** \brief Return true if s1 and s2 are the same (the justifications are taking into account). */ bool eq(interval_set const * s1, interval_set const * s2); /** \brief Return a set of literals that justify s. */ void get_justifications(interval_set const * s, literal_vector & js, ptr_vector& clauses ); std::ostream& display(std::ostream & out, interval_set const * s) const; unsigned num_intervals(interval_set const * s) const; /** \brief (For debugging purposes) Return one of the intervals in s. \pre idx < num_intervals() */ interval_set * get_interval(interval_set const * s, unsigned idx) const; /** \brief Select a witness w in the complement of s. \pre !is_full(s) */ void peek_in_complement(interval_set const * s, bool is_int, anum & w, bool randomize); }; typedef obj_ref interval_set_ref; inline std::ostream & operator<<(std::ostream & out, interval_set_ref const & s) { s.m().display(out, s); return out; } }; #endif z3-z3-4.8.7/src/nlsat/nlsat_justification.h000066400000000000000000000104701356505360400205660ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: nlsat_justification.h Abstract: An explanation for a (Boolean) assignment in the nlsat procedure Author: Leonardo de Moura (leonardo) 2012-01-10. Revision History: --*/ #ifndef NLSAT_JUSTIFICATION_H_ #define NLSAT_JUSTIFICATION_H_ #include "nlsat/nlsat_types.h" #include "util/tptr.h" namespace nlsat { // There are two kinds of justifications in nlsat: // // - clause // // - lazy_justification: it is a set of arithmetic literals s.t. // the maximal variable in each literal is the same. // The set is inconsistent in the current model. // Thus, our nonlinear procedure may be applied to it // to produce a clause. // class lazy_justification { unsigned m_num_literals; unsigned m_num_clauses; char m_data[0]; nlsat::clause* const* clauses() const { return (nlsat::clause *const*)(m_data); } public: static unsigned get_obj_size(unsigned nl, unsigned nc) { return sizeof(lazy_justification) + sizeof(literal)*nl + sizeof(nlsat::clause*)*nc; } lazy_justification(unsigned nl, literal const * lits, unsigned nc, nlsat::clause * const* clss): m_num_literals(nl), m_num_clauses(nc) { if (nc > 0) { memcpy(m_data + 0, clss, sizeof(nlsat::clause*)*nc); } if (nl > 0) { memcpy(m_data + sizeof(nlsat::clause*)*nc, lits, sizeof(literal)*nl); } } unsigned num_lits() const { return m_num_literals; } literal lit(unsigned i) const { SASSERT(i < num_lits()); return lits()[i]; } literal const * lits() const { return (literal const*)(m_data + m_num_clauses*sizeof(nlsat::clause*)); } unsigned num_clauses() const { return m_num_clauses; } nlsat::clause const& clause(unsigned i) const { SASSERT(i < num_clauses()); return *(clauses()[i]); } }; class justification { void * m_data; public: enum kind { NULL_JST = 0, DECISION, CLAUSE, LAZY }; justification():m_data(TAG(void *, nullptr, NULL_JST)) { SASSERT(is_null()); } justification(bool):m_data(TAG(void *, nullptr, DECISION)) { SASSERT(is_decision()); } justification(clause * c):m_data(TAG(void *, c, CLAUSE)) { SASSERT(is_clause()); } justification(lazy_justification * j):m_data(TAG(void *, j, LAZY)) { SASSERT(is_lazy()); } kind get_kind() const { return static_cast(GET_TAG(m_data)); } bool is_null() const { return get_kind() == NULL_JST; } bool is_decision() const { return get_kind() == DECISION; } bool is_clause() const { return get_kind() == CLAUSE; } bool is_lazy() const { return get_kind() == LAZY; } clause * get_clause() const { return UNTAG(clause*, m_data); } lazy_justification * get_lazy() const { return UNTAG(lazy_justification*, m_data); } bool operator==(justification other) const { return m_data == other.m_data; } bool operator!=(justification other) const { return m_data != other.m_data; } }; inline std::ostream& operator<<(std::ostream& out, justification::kind k) { switch (k) { case justification::NULL_JST: return out << "null"; case justification::DECISION: return out << "decision"; case justification::CLAUSE: return out << "clause"; case justification::LAZY: return out << "lazy"; default: return out << "??"; } } const justification null_justification; const justification decided_justification(true); inline justification mk_clause_jst(clause const * c) { return justification(const_cast(c)); } inline justification mk_lazy_jst(small_object_allocator & a, unsigned nl, literal const * lits, unsigned nc, clause *const* clauses) { void * mem = a.allocate(lazy_justification::get_obj_size(nl, nc)); return justification(new (mem) lazy_justification(nl, lits, nc, clauses)); } inline void del_jst(small_object_allocator & a, justification jst) { if (jst.is_lazy()) { lazy_justification * ptr = jst.get_lazy(); unsigned obj_sz = lazy_justification::get_obj_size(ptr->num_lits(), ptr->num_clauses()); a.deallocate(obj_sz, ptr); } } }; #endif z3-z3-4.8.7/src/nlsat/nlsat_params.pyg000066400000000000000000000025321356505360400175460ustar00rootroot00000000000000 def_module_params('nlsat', description='nonlinear solver', export=True, params=(max_memory_param(), ('lazy', UINT, 0, "how lazy the solver is."), ('reorder', BOOL, True, "reorder variables."), ('log_lemmas', BOOL, False, "display lemmas as self-contained SMT formulas"), ('check_lemmas', BOOL, False, "check lemmas on the fly using an independent nlsat solver"), ('simplify_conflicts', BOOL, True, "simplify conflicts using equalities before resolving them in nlsat solver."), ('minimize_conflicts', BOOL, False, "minimize conflicts"), ('randomize', BOOL, True, "randomize selection of a witness in nlsat."), ('max_conflicts', UINT, UINT_MAX, "maximum number of conflicts."), ('shuffle_vars', BOOL, False, "use a random variable order."), ('inline_vars', BOOL, False, "inline variables that can be isolated from equations"), ('seed', UINT, 0, "random seed."), ('factor', BOOL, True, "factor polynomials produced during conflict resolution.") )) z3-z3-4.8.7/src/nlsat/nlsat_scoped_literal_vector.h000066400000000000000000000053331356505360400222700ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: nlsat_scoped_literal_vector.h Abstract: Scoped vector for nlsat literals. Just to be "cancel" safe. Author: Leonardo de Moura (leonardo) 2012-01-13. Revision History: --*/ #ifndef NLSAT_SCOPED_LITERAL_VECTOR_H_ #define NLSAT_SCOPED_LITERAL_VECTOR_H_ #include "nlsat/nlsat_solver.h" namespace nlsat { class scoped_literal_vector { solver & m_solver; literal_vector m_lits; public: scoped_literal_vector(solver & s):m_solver(s) {} ~scoped_literal_vector() { reset(); } unsigned size() const { return m_lits.size(); } bool empty() const { return m_lits.empty(); } literal operator[](unsigned i) const { return m_lits[i]; } void reset() { for (literal l : m_lits) { m_solver.dec_ref(l); } m_lits.reset(); } void push_back(literal l) { m_solver.inc_ref(l); m_lits.push_back(l); } void set(unsigned i, literal l) { m_solver.inc_ref(l); m_solver.dec_ref(m_lits[i]); m_lits[i] = l; } literal const * c_ptr() const { return m_lits.c_ptr(); } literal const * begin() const { return m_lits.begin(); } literal const * end() const { return m_lits.end(); } void shrink(unsigned new_sz) { SASSERT(new_sz <= m_lits.size()); unsigned sz = m_lits.size(); if (new_sz == sz) return; for (unsigned i = new_sz; i < sz; i++) { m_solver.dec_ref(m_lits[i]); } m_lits.shrink(new_sz); } void append(unsigned sz, literal const * ls) { for (unsigned i = 0; i < sz; i++) push_back(ls[i]); } void append(scoped_literal_vector const& ls) { append(ls.size(), ls.c_ptr()); } void swap(scoped_literal_vector& other) { SASSERT(&m_solver == &other.m_solver); m_lits.swap(other.m_lits); } }; class scoped_literal { solver & m_solver; literal m_lit; public: scoped_literal(solver & s):m_solver(s), m_lit(null_literal) {} ~scoped_literal() { m_solver.dec_ref(m_lit); } scoped_literal & operator=(literal l) { m_solver.inc_ref(l); m_solver.dec_ref(m_lit); m_lit = l; return *this; } scoped_literal & operator=(scoped_literal const & l) { return operator=(l.m_lit); } operator literal&() { return m_lit; } operator literal const &() const { return m_lit; } void neg() { m_lit.neg(); } }; }; #endif z3-z3-4.8.7/src/nlsat/nlsat_solver.cpp000066400000000000000000004117141356505360400175660ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: nlsat_solver.cpp Abstract: Nonlinear arithmetic satisfiability procedure. The procedure is complete for nonlinear real arithmetic, but it also has limited support for integers. Author: Leonardo de Moura (leonardo) 2012-01-02. Revision History: --*/ #include "util/z3_exception.h" #include "util/chashtable.h" #include "util/id_gen.h" #include "util/map.h" #include "util/dependency.h" #include "util/permutation.h" #include "math/polynomial/algebraic_numbers.h" #include "math/polynomial/polynomial_cache.h" #include "nlsat/nlsat_solver.h" #include "nlsat/nlsat_clause.h" #include "nlsat/nlsat_assignment.h" #include "nlsat/nlsat_justification.h" #include "nlsat/nlsat_evaluator.h" #include "nlsat/nlsat_explain.h" #include "nlsat/nlsat_params.hpp" #define NLSAT_EXTRA_VERBOSE #ifdef NLSAT_EXTRA_VERBOSE #define NLSAT_VERBOSE(CODE) IF_VERBOSE(10, CODE) #else #define NLSAT_VERBOSE(CODE) ((void)0) #endif namespace nlsat { typedef chashtable ineq_atom_table; typedef chashtable root_atom_table; // for apply_permutation procedure void swap(clause * & c1, clause * & c2) { std::swap(c1, c2); } struct solver::ctx { params_ref m_params; reslimit& m_rlimit; small_object_allocator m_allocator; unsynch_mpq_manager m_qm; pmanager m_pm; anum_manager m_am; ctx(reslimit& rlim, params_ref const & p): m_params(p), m_rlimit(rlim), m_allocator("nlsat"), m_pm(rlim, m_qm, &m_allocator), m_am(rlim, m_qm, p, &m_allocator) {} }; struct solver::imp { struct dconfig { typedef imp value_manager; typedef small_object_allocator allocator; typedef void * value; static const bool ref_count = false; }; typedef dependency_manager assumption_manager; typedef assumption_manager::dependency * _assumption_set; typedef obj_ref assumption_set_ref; typedef polynomial::cache cache; typedef ptr_vector interval_set_vector; ctx& m_ctx; solver& m_solver; reslimit& m_rlimit; small_object_allocator& m_allocator; bool m_incremental; unsynch_mpq_manager& m_qm; pmanager& m_pm; cache m_cache; anum_manager& m_am; mutable assumption_manager m_asm; assignment m_assignment; // partial interpretation evaluator m_evaluator; interval_set_manager & m_ism; ineq_atom_table m_ineq_atoms; root_atom_table m_root_atoms; svector m_patch_var; polynomial_ref_vector m_patch_num, m_patch_denom; id_gen m_cid_gen; clause_vector m_clauses; // set of clauses clause_vector m_learned; // set of learned clauses clause_vector m_valids; unsigned m_num_bool_vars; atom_vector m_atoms; // bool_var -> atom svector m_bvalues; // boolean assignment unsigned_vector m_levels; // bool_var -> level svector m_justifications; vector m_bwatches; // bool_var (that are not attached to atoms) -> clauses where it is maximal svector m_dead; // mark dead boolean variables id_gen m_bid_gen; svector m_is_int; // m_is_int[x] is true if variable is integer vector m_watches; // var -> clauses where variable is maximal interval_set_vector m_infeasible; // var -> to a set of interval where the variable cannot be assigned to. atom_vector m_var2eq; // var -> to asserted equality var_vector m_perm; // var -> var permutation of the variables var_vector m_inv_perm; // m_perm: internal -> external // m_inv_perm: external -> internal struct perm_display_var_proc : public display_var_proc { var_vector & m_perm; display_var_proc m_default_display_var; display_var_proc const * m_proc; // display external var ids perm_display_var_proc(var_vector & perm): m_perm(perm), m_proc(nullptr) { } std::ostream& operator()(std::ostream & out, var x) const override { if (m_proc == nullptr) m_default_display_var(out, x); else (*m_proc)(out, m_perm[x]); return out; } }; perm_display_var_proc m_display_var; display_assumption_proc const* m_display_assumption; struct display_literal_assumption : public display_assumption_proc { imp& i; literal_vector const& lits; display_literal_assumption(imp& i, literal_vector const& lits): i(i), lits(lits) {} std::ostream& operator()(std::ostream& out, assumption a) const override { if (lits.begin() <= a && a < lits.end()) { out << *((literal const*)a); } else if (i.m_display_assumption) { (*i.m_display_assumption)(out, a); } return out; } }; struct scoped_display_assumptions { imp& i; display_assumption_proc const* m_save; scoped_display_assumptions(imp& i, display_assumption_proc const& p): i(i), m_save(i.m_display_assumption) { i.m_display_assumption = &p; } ~scoped_display_assumptions() { i.m_display_assumption = m_save; } }; explain m_explain; bool_var m_bk; // current Boolean variable we are processing var m_xk; // current arith variable we are processing unsigned m_scope_lvl; struct bvar_assignment {}; struct stage {}; struct trail { enum kind { BVAR_ASSIGNMENT, INFEASIBLE_UPDT, NEW_LEVEL, NEW_STAGE, UPDT_EQ }; kind m_kind; union { bool_var m_b; interval_set * m_old_set; atom * m_old_eq; }; trail(bool_var b, bvar_assignment):m_kind(BVAR_ASSIGNMENT), m_b(b) {} trail(interval_set * old_set):m_kind(INFEASIBLE_UPDT), m_old_set(old_set) {} trail(bool s, stage):m_kind(s ? NEW_STAGE : NEW_LEVEL) {} trail(atom * a):m_kind(UPDT_EQ), m_old_eq(a) {} }; svector m_trail; anum m_zero; // configuration unsigned long long m_max_memory; unsigned m_lazy; // how lazy the solver is: 0 - satisfy all learned clauses, 1 - process only unit and empty learned clauses, 2 - use only conflict clauses for resolving conflicts bool m_simplify_cores; bool m_reorder; bool m_randomize; bool m_random_order; unsigned m_random_seed; bool m_inline_vars; bool m_log_lemmas; bool m_check_lemmas; unsigned m_max_conflicts; unsigned m_lemma_count; // statistics unsigned m_conflicts; unsigned m_propagations; unsigned m_decisions; unsigned m_stages; unsigned m_irrational_assignments; // number of irrational witnesses imp(solver& s, ctx& c, bool incremental): m_ctx(c), m_solver(s), m_rlimit(c.m_rlimit), m_allocator(c.m_allocator), m_incremental(incremental), m_qm(c.m_qm), m_pm(c.m_pm), m_cache(m_pm), m_am(c.m_am), m_asm(*this, m_allocator), m_assignment(m_am), m_evaluator(s, m_assignment, m_pm, m_allocator), m_ism(m_evaluator.ism()), m_patch_num(m_pm), m_patch_denom(m_pm), m_num_bool_vars(0), m_display_var(m_perm), m_display_assumption(nullptr), m_explain(s, m_assignment, m_cache, m_atoms, m_var2eq, m_evaluator), m_scope_lvl(0), m_lemma(s), m_lazy_clause(s), m_lemma_assumptions(m_asm) { updt_params(c.m_params); reset_statistics(); mk_true_bvar(); m_lemma_count = 0; } ~imp() { clear(); } void mk_true_bvar() { bool_var b = mk_bool_var(); SASSERT(b == true_bool_var); literal true_lit(b, false); mk_clause(1, &true_lit, false, nullptr); } void updt_params(params_ref const & _p) { nlsat_params p(_p); m_max_memory = p.max_memory(); m_lazy = p.lazy(); m_simplify_cores = p.simplify_conflicts(); bool min_cores = p.minimize_conflicts(); m_reorder = p.reorder(); m_randomize = p.randomize(); m_max_conflicts = p.max_conflicts(); m_random_order = p.shuffle_vars(); m_random_seed = p.seed(); m_inline_vars = p.inline_vars(); m_log_lemmas = p.log_lemmas(); m_check_lemmas = p.check_lemmas(); m_ism.set_seed(m_random_seed); m_explain.set_simplify_cores(m_simplify_cores); m_explain.set_minimize_cores(min_cores); m_explain.set_factor(p.factor()); m_am.updt_params(p.p); } void reset() { m_explain.reset(); m_lemma.reset(); m_lazy_clause.reset(); undo_until_size(0); del_clauses(); del_unref_atoms(); m_cache.reset(); 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()); if (memory::get_allocation_size() > m_max_memory) throw solver_exception(Z3_MAX_MEMORY_MSG); } // ----------------------- // // Basic // // ----------------------- unsigned num_bool_vars() const { return m_num_bool_vars; } unsigned num_vars() const { return m_is_int.size(); } bool is_int(var x) const { return m_is_int[x]; } void inc_ref(assumption) {} void dec_ref(assumption) {} void inc_ref(_assumption_set a) { if (a != nullptr) m_asm.inc_ref(a); } void dec_ref(_assumption_set a) { if (a != nullptr) m_asm.dec_ref(a); } void inc_ref(bool_var b) { TRACE("ref", tout << "inc: " << b << "\n";); if (b == null_bool_var) return; if (m_atoms[b] == nullptr) return; m_atoms[b]->inc_ref(); } void inc_ref(literal l) { inc_ref(l.var()); } void dec_ref(bool_var b) { TRACE("ref", tout << "dec: " << b << "\n";); if (b == null_bool_var) return; atom * a = m_atoms[b]; if (a == nullptr) return; SASSERT(a->ref_count() > 0); a->dec_ref(); if (a->ref_count() == 0) del(a); } void dec_ref(literal l) { dec_ref(l.var()); } bool is_arith_atom(bool_var b) const { return m_atoms[b] != nullptr; } bool is_arith_literal(literal l) const { return is_arith_atom(l.var()); } var max_var(poly const * p) const { return m_pm.max_var(p); } var max_var(bool_var b) const { if (!is_arith_atom(b)) return null_var; else return m_atoms[b]->max_var(); } var max_var(literal l) const { return max_var(l.var()); } /** \brief Return the maximum variable occurring in cls. */ var max_var(unsigned sz, literal const * cls) const { var x = null_var; for (unsigned i = 0; i < sz; i++) { literal l = cls[i]; if (is_arith_literal(l)) { var y = max_var(l); if (x == null_var || y > x) x = y; } } return x; } var max_var(clause const & cls) const { return max_var(cls.size(), cls.c_ptr()); } /** \brief Return the maximum Boolean variable occurring in cls. */ bool_var max_bvar(clause const & cls) const { bool_var b = null_bool_var; for (literal l : cls) { if (b == null_bool_var || l.var() > b) b = l.var(); } return b; } /** \brief Return the degree of the maximal variable of the given atom */ unsigned degree(atom const * a) const { if (a->is_ineq_atom()) { unsigned max = 0; unsigned sz = to_ineq_atom(a)->size(); var x = a->max_var(); for (unsigned i = 0; i < sz; i++) { unsigned d = m_pm.degree(to_ineq_atom(a)->p(i), x); if (d > max) max = d; } return max; } else { return m_pm.degree(to_root_atom(a)->p(), a->max_var()); } } /** \brief Return the degree of the maximal variable in c */ unsigned degree(clause const & c) const { var x = max_var(c); if (x == null_var) return 0; unsigned max = 0; for (literal l : c) { atom const * a = m_atoms[l.var()]; if (a == nullptr) continue; unsigned d = degree(a); if (d > max) max = d; } return max; } // ----------------------- // // Variable, Atoms, Clauses & Assumption creation // // ----------------------- bool_var mk_bool_var_core() { bool_var b = m_bid_gen.mk(); m_num_bool_vars++; m_atoms .setx(b, nullptr, nullptr); m_bvalues .setx(b, l_undef, l_undef); m_levels .setx(b, UINT_MAX, UINT_MAX); m_justifications.setx(b, null_justification, null_justification); m_bwatches .setx(b, clause_vector(), clause_vector()); m_dead .setx(b, false, true); return b; } bool_var mk_bool_var() { return mk_bool_var_core(); } var mk_var(bool is_int) { var x = m_pm.mk_var(); register_var(x, is_int); return x; } void register_var(var x, bool is_int) { SASSERT(x == num_vars()); m_is_int. push_back(is_int); m_watches. push_back(clause_vector()); m_infeasible.push_back(0); m_var2eq. push_back(nullptr); m_perm. push_back(x); m_inv_perm. push_back(x); SASSERT(m_is_int.size() == m_watches.size()); SASSERT(m_is_int.size() == m_infeasible.size()); SASSERT(m_is_int.size() == m_var2eq.size()); SASSERT(m_is_int.size() == m_perm.size()); SASSERT(m_is_int.size() == m_inv_perm.size()); } svector m_found_vars; void vars(literal l, var_vector& vs) { vs.reset(); atom * a = m_atoms[l.var()]; if (a == nullptr) { } else if (a->is_ineq_atom()) { unsigned sz = to_ineq_atom(a)->size(); var_vector new_vs; for (unsigned j = 0; j < sz; j++) { m_found_vars.reset(); m_pm.vars(to_ineq_atom(a)->p(j), new_vs); for (unsigned i = 0; i < new_vs.size(); ++i) { if (!m_found_vars.get(new_vs[i], false)) { m_found_vars.setx(new_vs[i], true, false); vs.push_back(new_vs[i]); } } } } else { m_pm.vars(to_root_atom(a)->p(), vs); //vs.erase(max_var(to_root_atom(a)->p())); vs.push_back(to_root_atom(a)->x()); } } void deallocate(ineq_atom * a) { unsigned obj_sz = ineq_atom::get_obj_size(a->size()); a->~ineq_atom(); m_allocator.deallocate(obj_sz, a); } void deallocate(root_atom * a) { a->~root_atom(); m_allocator.deallocate(sizeof(root_atom), a); } void del(bool_var b) { SASSERT(m_bwatches[b].empty()); //SASSERT(m_bvalues[b] == l_undef); m_num_bool_vars--; m_dead[b] = true; m_atoms[b] = nullptr; m_bvalues[b] = l_undef; m_bid_gen.recycle(b); } void del(ineq_atom * a) { SASSERT(a->ref_count() == 0); m_ineq_atoms.erase(a); del(a->bvar()); unsigned sz = a->size(); for (unsigned i = 0; i < sz; i++) m_pm.dec_ref(a->p(i)); deallocate(a); } void del(root_atom * a) { SASSERT(a->ref_count() == 0); m_root_atoms.erase(a); del(a->bvar()); m_pm.dec_ref(a->p()); deallocate(a); } void del(atom * a) { if (a == nullptr) return; TRACE("nlsat_verbose", display(tout << "del: b" << a->m_bool_var << " ", *a) << "\n";); if (a->is_ineq_atom()) del(to_ineq_atom(a)); else del(to_root_atom(a)); } // Delete atoms with ref_count == 0 void del_unref_atoms() { for (auto* a : m_atoms) { del(a); } } ineq_atom* mk_ineq_atom(atom::kind k, unsigned sz, poly * const * ps, bool const * is_even, bool& is_new) { SASSERT(sz >= 1); SASSERT(k == atom::LT || k == atom::GT || k == atom::EQ); int sign = 1; polynomial_ref p(m_pm); ptr_buffer uniq_ps; var max = null_var; for (unsigned i = 0; i < sz; i++) { p = m_pm.flip_sign_if_lm_neg(ps[i]); if (p.get() != ps[i]) sign = -sign; var curr_max = max_var(p.get()); if (curr_max > max || max == null_var) max = curr_max; uniq_ps.push_back(m_cache.mk_unique(p)); TRACE("nlsat_table_bug", tout << "p: " << p << ", uniq: " << uniq_ps.back() << "\n";); } void * mem = m_allocator.allocate(ineq_atom::get_obj_size(sz)); if (sign < 0) k = atom::flip(k); ineq_atom * tmp_atom = new (mem) ineq_atom(k, sz, uniq_ps.c_ptr(), is_even, max); TRACE("nlsat_table_bug", ineq_atom::hash_proc h; tout << "mk_ineq_atom hash: " << h(tmp_atom) << "\n"; display(tout, *tmp_atom, m_display_var); tout << "\n";); ineq_atom * atom = m_ineq_atoms.insert_if_not_there(tmp_atom); CTRACE("nlsat_table_bug", atom->max_var() != max, display(tout, *atom, m_display_var); tout << "\n";); SASSERT(atom->max_var() == max); is_new = (atom == tmp_atom); if (is_new) { for (unsigned i = 0; i < sz; i++) { m_pm.inc_ref(atom->p(i)); } } else { deallocate(tmp_atom); } return atom; } bool_var mk_ineq_atom(atom::kind k, unsigned sz, poly * const * ps, bool const * is_even) { bool is_new = false; ineq_atom* atom = mk_ineq_atom(k, sz, ps, is_even, is_new); if (!is_new) { return atom->bvar(); } else { bool_var b = mk_bool_var_core(); m_atoms[b] = atom; atom->m_bool_var = b; TRACE("nlsat_verbose", display(tout << "create: b" << atom->m_bool_var << " ", *atom) << "\n";); return b; } } literal mk_ineq_literal(atom::kind k, unsigned sz, poly * const * ps, bool const * is_even) { SASSERT(k == atom::LT || k == atom::GT || k == atom::EQ); if (sz == 0) { switch (k) { case atom::LT: return false_literal; // 1 < 0 case atom::EQ: return false_literal; // 1 == 0 case atom::GT: return true_literal; // 1 > 0 default: UNREACHABLE(); return null_literal; } } else { return literal(mk_ineq_atom(k, sz, ps, is_even), false); } } bool_var mk_root_atom(atom::kind k, var x, unsigned i, poly * p) { polynomial_ref p1(m_pm), uniq_p(m_pm); p1 = m_pm.flip_sign_if_lm_neg(p); // flipping the sign of the polynomial will not change its roots. uniq_p = m_cache.mk_unique(p1); TRACE("nlsat_solver", tout << x << " " << p1 << " " << uniq_p << "\n";); SASSERT(i > 0); SASSERT(x >= max_var(p)); SASSERT(k == atom::ROOT_LT || k == atom::ROOT_GT || k == atom::ROOT_EQ || k == atom::ROOT_LE || k == atom::ROOT_GE); void * mem = m_allocator.allocate(sizeof(root_atom)); root_atom * new_atom = new (mem) root_atom(k, x, i, uniq_p); root_atom * old_atom = m_root_atoms.insert_if_not_there(new_atom); SASSERT(old_atom->max_var() == x); if (old_atom != new_atom) { deallocate(new_atom); return old_atom->bvar(); } bool_var b = mk_bool_var_core(); m_atoms[b] = new_atom; new_atom->m_bool_var = b; m_pm.inc_ref(new_atom->p()); return b; } void attach_clause(clause & cls) { var x = max_var(cls); if (x != null_var) { m_watches[x].push_back(&cls); } else { bool_var b = max_bvar(cls); m_bwatches[b].push_back(&cls); } } void deattach_clause(clause & cls) { var x = max_var(cls); if (x != null_var) { m_watches[x].erase(&cls); } else { bool_var b = max_bvar(cls); m_bwatches[b].erase(&cls); } } void deallocate(clause * cls) { size_t obj_sz = clause::get_obj_size(cls->size()); cls->~clause(); m_allocator.deallocate(obj_sz, cls); } void del_clause(clause * cls) { deattach_clause(*cls); m_cid_gen.recycle(cls->id()); unsigned sz = cls->size(); for (unsigned i = 0; i < sz; i++) dec_ref((*cls)[i]); _assumption_set a = static_cast<_assumption_set>(cls->assumptions()); dec_ref(a); deallocate(cls); } void del_clause(clause * cls, clause_vector& clauses) { clauses.erase(cls); del_clause(cls); } void del_clauses(ptr_vector & cs) { for (clause* cp : cs) del_clause(cp); cs.reset(); } void del_clauses() { del_clauses(m_clauses); del_clauses(m_learned); del_clauses(m_valids); } // We use a simple heuristic to sort literals // - bool literals < arith literals // - sort literals based on max_var // - sort literal with the same max_var using degree // break ties using the fact that ineqs are usually cheaper to process than eqs. struct lit_lt { imp & m; lit_lt(imp & _m):m(_m) {} bool operator()(literal l1, literal l2) const { atom * a1 = m.m_atoms[l1.var()]; atom * a2 = m.m_atoms[l2.var()]; if (a1 == nullptr && a2 == nullptr) return l1.index() < l2.index(); if (a1 == nullptr) return true; if (a2 == nullptr) return false; var x1 = a1->max_var(); var x2 = a2->max_var(); if (x1 < x2) return true; if (x1 > x2) return false; SASSERT(x1 == x2); unsigned d1 = m.degree(a1); unsigned d2 = m.degree(a2); if (d1 < d2) return true; if (d1 > d2) return false; if (!a1->is_eq() && a2->is_eq()) return true; if (a1->is_eq() && !a2->is_eq()) return false; return l1.index() < l2.index(); } }; void check_lemma(unsigned n, literal const* cls, bool is_valid, assumption_set a) { TRACE("nlsat", display(tout << "check lemma: ", n, cls) << "\n"; display(tout);); IF_VERBOSE(0, display(verbose_stream() << "check lemma: ", n, cls) << "\n"); for (clause* c : m_learned) IF_VERBOSE(1, display(verbose_stream() << "lemma: ", *c) << "\n"); solver solver2(m_ctx); imp& checker = *(solver2.m_imp); checker.m_check_lemmas = false; checker.m_log_lemmas = false; // need to translate Boolean variables and literals svector tr; for (var x = 0; x < m_is_int.size(); ++x) { checker.register_var(x, m_is_int[x]); } bool_var bv = 0; tr.push_back(bv); checker.inc_ref(bv); for (bool_var b = 1; b < m_atoms.size(); ++b) { atom* a = m_atoms[b]; if (a == nullptr) { bv = checker.mk_bool_var(); } else if (a->is_ineq_atom()) { ineq_atom& ia = *to_ineq_atom(a); unsigned sz = ia.size(); ptr_vector ps; svector is_even; for (unsigned i = 0; i < sz; ++i) { ps.push_back(ia.p(i)); is_even.push_back(ia.is_even(i)); } bv = checker.mk_ineq_atom(ia.get_kind(), sz, ps.c_ptr(), is_even.c_ptr()); } else if (a->is_root_atom()) { root_atom& r = *to_root_atom(a); if (r.x() >= max_var(r.p())) { // permutation may be reverted after check completes, // but then root atoms are not used in lemmas. bv = checker.mk_root_atom(r.get_kind(), r.x(), r.i(), r.p()); } } else { UNREACHABLE(); } checker.inc_ref(bv); tr.push_back(bv); } if (!is_valid) { for (clause* c : m_clauses) { if (!a && c->assumptions()) { continue; } literal_vector lits; for (literal lit : *c) { lits.push_back(literal(tr[lit.var()], lit.sign())); } checker.mk_clause(lits.size(), lits.c_ptr(), nullptr); } } for (unsigned i = 0; i < n; ++i) { literal lit = cls[i]; literal nlit(tr[lit.var()], !lit.sign()); checker.mk_clause(1, &nlit, nullptr); } IF_VERBOSE(0, verbose_stream() << "check\n";); lbool r = checker.check(); if (r == l_true) { for (bool_var b : tr) { literal lit(b, false); IF_VERBOSE(0, checker.display(verbose_stream(), lit) << " := " << checker.value(lit) << "\n"); TRACE("nlsat", checker.display(tout, lit) << " := " << checker.value(lit) << "\n";); } for (clause* c : m_learned) { bool found = false; for (literal lit: *c) { literal tlit(tr[lit.var()], lit.sign()); found |= checker.value(tlit) == l_true; } if (!found) { IF_VERBOSE(0, display(verbose_stream() << "violdated clause: ", *c) << "\n"); TRACE("nlsat", display(tout << "violdated clause: ", *c) << "\n";); } } for (clause* c : m_valids) { bool found = false; for (literal lit: *c) { literal tlit(tr[lit.var()], lit.sign()); found |= checker.value(tlit) == l_true; } if (!found) { IF_VERBOSE(0, display(verbose_stream() << "violdated tautology clause: ", *c) << "\n"); TRACE("nlsat", display(tout << "violdated tautology clause: ", *c) << "\n";); } } UNREACHABLE(); } for (bool_var b : tr) { checker.dec_ref(b); } } void log_lemma(std::ostream& out, clause const& cls) { display_smt2(out); out << "(assert (not "; display_smt2(out, cls) << "))\n"; display(out << "(echo \"#" << m_lemma_count << " ", cls) << "\")\n"; out << "(check-sat)\n(reset)\n"; } clause * mk_clause_core(unsigned num_lits, literal const * lits, bool learned, _assumption_set a) { SASSERT(num_lits > 0); unsigned cid = m_cid_gen.mk(); void * mem = m_allocator.allocate(clause::get_obj_size(num_lits)); clause * cls = new (mem) clause(cid, num_lits, lits, learned, a); for (unsigned i = 0; i < num_lits; i++) inc_ref(lits[i]); inc_ref(a); return cls; } clause * mk_clause(unsigned num_lits, literal const * lits, bool learned, _assumption_set a) { SASSERT(num_lits > 0); clause * cls = mk_clause_core(num_lits, lits, learned, a); ++m_lemma_count; TRACE("nlsat_sort", display(tout << "mk_clause:\n", *cls) << "\n";); std::sort(cls->begin(), cls->end(), lit_lt(*this)); TRACE("nlsat_sort", display(tout << "#" << m_lemma_count << " after sort:\n", *cls) << "\n";); if (learned && m_log_lemmas) { log_lemma(verbose_stream(), *cls); } if (learned && m_check_lemmas) { check_lemma(cls->size(), cls->c_ptr(), false, cls->assumptions()); } if (learned) m_learned.push_back(cls); else m_clauses.push_back(cls); attach_clause(*cls); return cls; } void mk_clause(unsigned num_lits, literal const * lits, assumption a) { SASSERT(num_lits > 0); _assumption_set as = nullptr; if (a != nullptr) as = m_asm.mk_leaf(a); mk_clause(num_lits, lits, false, as); } // ----------------------- // // Search // // ----------------------- void save_assign_trail(bool_var b) { m_trail.push_back(trail(b, bvar_assignment())); } void save_set_updt_trail(interval_set * old_set) { m_trail.push_back(trail(old_set)); } void save_updt_eq_trail(atom * old_eq) { m_trail.push_back(trail(old_eq)); } void save_new_stage_trail() { m_trail.push_back(trail(true, stage())); } void save_new_level_trail() { m_trail.push_back(trail(false, stage())); } void undo_bvar_assignment(bool_var b) { m_bvalues[b] = l_undef; m_levels[b] = UINT_MAX; del_jst(m_allocator, m_justifications[b]); m_justifications[b] = null_justification; if (m_atoms[b] == nullptr && b < m_bk) m_bk = b; } void undo_set_updt(interval_set * old_set) { if (m_xk == null_var) return; var x = m_xk; if (x < m_infeasible.size()) { m_ism.dec_ref(m_infeasible[x]); m_infeasible[x] = old_set; } } void undo_new_stage() { if (m_xk == 0) { m_xk = null_var; } else if (m_xk != null_var) { m_xk--; m_assignment.reset(m_xk); } } void undo_new_level() { SASSERT(m_scope_lvl > 0); m_scope_lvl--; m_evaluator.pop(1); } void undo_updt_eq(atom * a) { if (m_var2eq.size() > m_xk) m_var2eq[m_xk] = a; } template void undo_until(Predicate const & pred) { while (pred() && !m_trail.empty()) { trail & t = m_trail.back(); switch (t.m_kind) { case trail::BVAR_ASSIGNMENT: undo_bvar_assignment(t.m_b); break; case trail::INFEASIBLE_UPDT: undo_set_updt(t.m_old_set); break; case trail::NEW_STAGE: undo_new_stage(); break; case trail::NEW_LEVEL: undo_new_level(); break; case trail::UPDT_EQ: undo_updt_eq(t.m_old_eq); break; default: break; } m_trail.pop_back(); } } struct size_pred { svector & m_trail; unsigned m_old_size; size_pred(svector & trail, unsigned old_size):m_trail(trail), m_old_size(old_size) {} bool operator()() const { return m_trail.size() > m_old_size; } }; // Keep undoing until trail has the given size void undo_until_size(unsigned old_size) { SASSERT(m_trail.size() >= old_size); undo_until(size_pred(m_trail, old_size)); } struct stage_pred { var const & m_xk; var m_target; stage_pred(var const & xk, var target):m_xk(xk), m_target(target) {} bool operator()() const { return m_xk != m_target; } }; // Keep undoing until stage is new_xk void undo_until_stage(var new_xk) { undo_until(stage_pred(m_xk, new_xk)); } struct level_pred { unsigned const & m_scope_lvl; unsigned m_new_lvl; level_pred(unsigned const & scope_lvl, unsigned new_lvl):m_scope_lvl(scope_lvl), m_new_lvl(new_lvl) {} bool operator()() const { return m_scope_lvl > m_new_lvl; } }; // Keep undoing until level is new_lvl void undo_until_level(unsigned new_lvl) { undo_until(level_pred(m_scope_lvl, new_lvl)); } struct unassigned_pred { bool_var m_b; svector const & m_bvalues; unassigned_pred(svector const & bvalues, bool_var b): m_b(b), m_bvalues(bvalues) {} bool operator()() const { return m_bvalues[m_b] != l_undef; } }; // Keep undoing until b is unassigned void undo_until_unassigned(bool_var b) { undo_until(unassigned_pred(m_bvalues, b)); SASSERT(m_bvalues[b] == l_undef); } struct true_pred { bool operator()() const { return true; } }; void undo_until_empty() { undo_until(true_pred()); } /** \brief Create a new scope level */ void new_level() { m_evaluator.push(); m_scope_lvl++; save_new_level_trail(); } /** \brief Return the value of the given literal that was assigned by the search engine. */ lbool assigned_value(literal l) const { bool_var b = l.var(); if (l.sign()) return ~m_bvalues[b]; else return m_bvalues[b]; } /** \brief Assign literal using the given justification */ void assign(literal l, justification j) { TRACE("nlsat", display(tout << "assigning literal: ", l); display(tout << " <- ", j);); SASSERT(assigned_value(l) == l_undef); SASSERT(j != null_justification); SASSERT(!j.is_null()); if (j.is_decision()) m_decisions++; else m_propagations++; bool_var b = l.var(); m_bvalues[b] = to_lbool(!l.sign()); m_levels[b] = m_scope_lvl; m_justifications[b] = j; save_assign_trail(b); updt_eq(b, j); TRACE("nlsat_assign", tout << "b" << b << " -> " << m_bvalues[b] << "\n";); } /** \brief Create a "case-split" */ void decide(literal l) { new_level(); assign(l, decided_justification); } /** \brief Return the value of a literal as defined in Dejan and Leo's paper. */ lbool value(literal l) { lbool val = assigned_value(l); if (val != l_undef) { TRACE("nlsat_verbose", display(tout << " assigned value " << val << " for ", l) << "\n";); return val; } bool_var b = l.var(); atom * a = m_atoms[b]; if (a == nullptr) { TRACE("nlsat_verbose", display(tout << " no atom for ", l) << "\n";); return l_undef; } var max = a->max_var(); if (!m_assignment.is_assigned(max)) { TRACE("nlsat_verbose", display(tout << " maximal variable not assigned ", l) << "\n";); return l_undef; } val = to_lbool(m_evaluator.eval(a, l.sign())); TRACE("nlsat_verbose", display(tout << " evaluated value " << val << " for ", l) << "\n";); TRACE("value_bug", tout << "value of: "; display(tout, l); tout << " := " << val << "\n"; tout << "xk: " << m_xk << ", a->max_var(): " << a->max_var() << "\n"; display_assignment(tout);); return val; } /** \brief Return true if the given clause is already satisfied in the current partial interpretation. */ bool is_satisfied(clause const & cls) const { for (literal l : cls) { if (const_cast(this)->value(l) == l_true) { TRACE("value_bug:", tout << l << " := true\n";); return true; } } return false; } /** \brief Return true if the given clause is false in the current partial interpretation. */ bool is_inconsistent(unsigned sz, literal const * cls) { for (unsigned i = 0; i < sz; i++) { if (value(cls[i]) != l_false) { TRACE("is_inconsistent", tout << "literal is not false:\n"; display(tout, cls[i]); tout << "\n";); return false; } } return true; } /** \brief Process a clauses that contains only Boolean literals. */ bool process_boolean_clause(clause const & cls) { SASSERT(m_xk == null_var); unsigned num_undef = 0; unsigned first_undef = UINT_MAX; unsigned sz = cls.size(); for (unsigned i = 0; i < sz; i++) { literal l = cls[i]; SASSERT(m_atoms[l.var()] == nullptr); SASSERT(value(l) != l_true); if (value(l) == l_false) continue; SASSERT(value(l) == l_undef); num_undef++; if (first_undef == UINT_MAX) first_undef = i; } if (num_undef == 0) return false; SASSERT(first_undef != UINT_MAX); if (num_undef == 1) assign(cls[first_undef], mk_clause_jst(&cls)); // unit clause else decide(cls[first_undef]); return true; } /** \brief assign l to true, because l + (justification of) s is infeasible in RCF in the current interpretation. */ literal_vector core; ptr_vector clauses; void R_propagate(literal l, interval_set const * s, bool include_l = true) { m_ism.get_justifications(s, core, clauses); if (include_l) core.push_back(~l); assign(l, mk_lazy_jst(m_allocator, core.size(), core.c_ptr(), clauses.size(), clauses.c_ptr())); SASSERT(value(l) == l_true); } /** \brief m_infeasible[m_xk] <- m_infeasible[m_xk] Union s */ void updt_infeasible(interval_set const * s) { SASSERT(m_xk != null_var); interval_set * xk_set = m_infeasible[m_xk]; save_set_updt_trail(xk_set); interval_set_ref new_set(m_ism); TRACE("nlsat_inf_set", tout << "updating infeasible set\n"; m_ism.display(tout, xk_set) << "\n"; m_ism.display(tout, s) << "\n";); new_set = m_ism.mk_union(s, xk_set); TRACE("nlsat_inf_set", tout << "new infeasible set:\n"; m_ism.display(tout, new_set) << "\n";); SASSERT(!m_ism.is_full(new_set)); m_ism.inc_ref(new_set); m_infeasible[m_xk] = new_set; } /** \brief Update m_var2eq mapping. */ void updt_eq(bool_var b, justification j) { if (!m_simplify_cores) return; if (m_bvalues[b] != l_true) return; atom * a = m_atoms[b]; if (a == nullptr || a->get_kind() != atom::EQ || to_ineq_atom(a)->size() > 1 || to_ineq_atom(a)->is_even(0)) return; switch (j.get_kind()) { case justification::CLAUSE: if (j.get_clause()->assumptions() != nullptr) return; break; case justification::LAZY: if (j.get_lazy()->num_clauses() > 0) return; if (j.get_lazy()->num_lits() > 0) return; break; default: break; } var x = m_xk; SASSERT(a->max_var() == x); SASSERT(x != null_var); if (m_var2eq[x] != 0 && degree(m_var2eq[x]) <= degree(a)) return; // we only update m_var2eq if the new equality has smaller degree TRACE("nlsat_simplify_core", tout << "Saving equality for "; m_display_var(tout, x) << " (x" << x << ") "; tout << "scope-lvl: " << scope_lvl() << "\n"; display(tout, literal(b, false)) << "\n"; display(tout, j); ); save_updt_eq_trail(m_var2eq[x]); m_var2eq[x] = a; } /** \brief Process a clause that contains nonlinear arithmetic literals If satisfy_learned is true, then learned clauses are satisfied even if m_lazy > 0 */ bool process_arith_clause(clause const & cls, bool satisfy_learned) { if (!satisfy_learned && m_lazy >= 2 && cls.is_learned()) { TRACE("nlsat", tout << "skip learned\n";); return true; // ignore lemmas in super lazy mode } SASSERT(m_xk == max_var(cls)); unsigned num_undef = 0; // number of undefined literals unsigned first_undef = UINT_MAX; // position of the first undefined literal interval_set_ref first_undef_set(m_ism); // infeasible region of the first undefined literal interval_set * xk_set = m_infeasible[m_xk]; // current set of infeasible interval for current variable SASSERT(!m_ism.is_full(xk_set)); for (unsigned idx = 0; idx < cls.size(); ++idx) { literal l = cls[idx]; checkpoint(); if (value(l) == l_false) continue; if (value(l) == l_true) return true; // could happen if clause is a tautology CTRACE("nlsat", max_var(l) != m_xk || value(l) != l_undef, display(tout); tout << "xk: " << m_xk << ", max_var(l): " << max_var(l) << ", l: "; display(tout, l) << "\n"; display(tout, cls) << "\n";); SASSERT(value(l) == l_undef); SASSERT(max_var(l) == m_xk); bool_var b = l.var(); atom * a = m_atoms[b]; SASSERT(a != nullptr); interval_set_ref curr_set(m_ism); curr_set = m_evaluator.infeasible_intervals(a, l.sign(), &cls); TRACE("nlsat_inf_set", tout << "infeasible set for literal: "; display(tout, l); tout << "\n"; m_ism.display(tout, curr_set); tout << "\n"; display(tout, cls) << "\n";); if (m_ism.is_empty(curr_set)) { TRACE("nlsat_inf_set", tout << "infeasible set is empty, found literal\n";); R_propagate(l, nullptr); SASSERT(is_satisfied(cls)); return true; } if (m_ism.is_full(curr_set)) { TRACE("nlsat_inf_set", tout << "infeasible set is R, skip literal\n";); R_propagate(~l, nullptr); continue; } if (m_ism.subset(curr_set, xk_set)) { TRACE("nlsat_inf_set", tout << "infeasible set is a subset of current set, found literal\n";); R_propagate(l, xk_set); return true; } interval_set_ref tmp(m_ism); tmp = m_ism.mk_union(curr_set, xk_set); if (m_ism.is_full(tmp)) { TRACE("nlsat_inf_set", tout << "infeasible set + current set = R, skip literal\n"; display(tout, cls) << "\n";); R_propagate(~l, tmp, false); continue; } num_undef++; if (first_undef == UINT_MAX) { first_undef = idx; first_undef_set = curr_set; } } TRACE("nlsat_inf_set", tout << "num_undef: " << num_undef << "\n";); if (num_undef == 0) return false; SASSERT(first_undef != UINT_MAX); if (num_undef == 1) { // unit clause assign(cls[first_undef], mk_clause_jst(&cls)); updt_infeasible(first_undef_set); } else if ( satisfy_learned || !cls.is_learned() /* must always satisfy input clauses */ || m_lazy == 0 /* if not in lazy mode, we also satiffy lemmas */) { decide(cls[first_undef]); updt_infeasible(first_undef_set); } else { TRACE("nlsat_lazy", tout << "skipping clause, satisfy_learned: " << satisfy_learned << ", cls.is_learned(): " << cls.is_learned() << ", lazy: " << m_lazy << "\n";); } return true; } /** \brief Try to satisfy the given clause. Return true if succeeded. If satisfy_learned is true, then (arithmetic) learned clauses are satisfied even if m_lazy > 0 */ bool process_clause(clause const & cls, bool satisfy_learned) { if (is_satisfied(cls)) return true; if (m_xk == null_var) return process_boolean_clause(cls); else return process_arith_clause(cls, satisfy_learned); } /** \brief Try to satisfy the given "set" of clauses. Return 0, if the set was satisfied, or the violating clause otherwise */ clause * process_clauses(clause_vector const & cs) { for (clause* c : cs) { if (!process_clause(*c, false)) return c; } return nullptr; // succeeded } /** \brief Make sure m_bk is the first unassigned pure Boolean variable. Set m_bk == null_bool_var if there is no unassigned pure Boolean variable. */ void peek_next_bool_var() { while (m_bk < m_atoms.size()) { if (!m_dead[m_bk] && m_atoms[m_bk] == nullptr && m_bvalues[m_bk] == l_undef) { return; } m_bk++; } m_bk = null_bool_var; } /** \brief Create a new stage. See Dejan and Leo's paper. */ void new_stage() { m_stages++; save_new_stage_trail(); if (m_xk == null_var) m_xk = 0; else m_xk++; } /** \brief Assign m_xk */ void select_witness() { scoped_anum w(m_am); SASSERT(!m_ism.is_full(m_infeasible[m_xk])); m_ism.peek_in_complement(m_infeasible[m_xk], m_is_int[m_xk], w, m_randomize); TRACE("nlsat", tout << "infeasible intervals: "; m_ism.display(tout, m_infeasible[m_xk]); tout << "\n"; tout << "assigning "; m_display_var(tout, m_xk) << "(x" << m_xk << ") -> " << w << "\n";); TRACE("nlsat_root", tout << "value as root object: "; m_am.display_root(tout, w); tout << "\n";); if (!m_am.is_rational(w)) m_irrational_assignments++; m_assignment.set_core(m_xk, w); } bool is_satisfied() { if (m_bk == null_bool_var && m_xk >= num_vars()) { TRACE("nlsat", tout << "found model\n"; display_assignment(tout);); fix_patch(); SASSERT(check_satisfied(m_clauses)); return true; // all variables were assigned, and all clauses were satisfied. } else { return false; } } /** \brief main procedure */ lbool search() { TRACE("nlsat", tout << "starting search...\n"; display(tout); tout << "\nvar order:\n"; display_vars(tout);); TRACE("nlsat_proof", tout << "ASSERTED\n"; display(tout);); TRACE("nlsat_proof_sk", tout << "ASSERTED\n"; display_abst(tout);); TRACE("nlsat_mathematica", display_mathematica(tout);); TRACE("nlsat", display_smt2(tout);); m_bk = 0; m_xk = null_var; m_conflicts = 0; while (true) { CASSERT("nlsat", check_satisfied()); if (m_xk == null_var) { peek_next_bool_var(); if (m_bk == null_bool_var) new_stage(); // move to arith vars } else { new_stage(); // peek next arith var } TRACE("nlsat_bug", tout << "xk: x" << m_xk << " bk: b" << m_bk << "\n";); if (is_satisfied()) { return l_true; } while (true) { TRACE("nlsat_verbose", tout << "processing variable "; if (m_xk != null_var) { m_display_var(tout, m_xk); tout << " " << m_watches[m_xk].size(); } else { tout << m_bwatches[m_bk].size() << " boolean b" << m_bk; } tout << "\n";); checkpoint(); clause * conflict_clause; if (m_xk == null_var) conflict_clause = process_clauses(m_bwatches[m_bk]); else conflict_clause = process_clauses(m_watches[m_xk]); if (conflict_clause == nullptr) break; if (!resolve(*conflict_clause)) return l_false; if (m_conflicts >= m_max_conflicts) return l_undef; } if (m_xk == null_var) { if (m_bvalues[m_bk] == l_undef) { decide(literal(m_bk, true)); m_bk++; } } else { select_witness(); } } } lbool search_check() { lbool r = l_undef; while (true) { r = search(); if (r != l_true) break; vector lows; vector vars; for (var x = 0; x < num_vars(); x++) { if (m_is_int[x] && m_assignment.is_assigned(x) && !m_am.is_int(m_assignment.value(x))) { scoped_anum v(m_am), vlo(m_am); v = m_assignment.value(x); rational lo; m_am.int_lt(v, vlo); if (!m_am.is_int(vlo)) continue; m_am.to_rational(vlo, lo); // derive tight bounds. while (true) { lo++; if (!m_am.gt(v, lo.to_mpq())) { lo--; break; } } lows.push_back(lo); vars.push_back(x); } } if (lows.empty()) break; init_search(); for (unsigned i = 0; i < lows.size(); ++i) { rational lo = lows[i]; rational hi = lo + rational::one(); var x = vars[i]; bool is_even = false; polynomial_ref p(m_pm); rational one(1); m_lemma.reset(); p = m_pm.mk_linear(1, &one, &x, -lo); poly* p1 = p.get(); m_lemma.push_back(~mk_ineq_literal(atom::GT, 1, &p1, &is_even)); p = m_pm.mk_linear(1, &one, &x, -hi); poly* p2 = p.get(); m_lemma.push_back(~mk_ineq_literal(atom::LT, 1, &p2, &is_even)); // perform branch and bound clause * cls = mk_clause(m_lemma.size(), m_lemma.c_ptr(), false, nullptr); if (cls) { TRACE("nlsat", display(tout << "conflict " << lo << " " << hi, *cls); tout << "\n";); } } } return r; } lbool check() { TRACE("nlsat_smt2", display_smt2(tout);); TRACE("nlsat_fd", tout << "is_full_dimensional: " << is_full_dimensional() << "\n";); init_search(); m_explain.set_full_dimensional(is_full_dimensional()); bool reordered = false; if (!m_incremental && m_inline_vars) simplify(); if (!can_reorder()) { } else if (m_random_order) { shuffle_vars(); reordered = true; } else if (m_reorder) { heuristic_reorder(); reordered = true; } sort_watched_clauses(); lbool r = search_check(); CTRACE("nlsat_model", r == l_true, tout << "model before restore order\n"; display_assignment(tout);); if (reordered) { restore_order(); } CTRACE("nlsat_model", r == l_true, tout << "model\n"; display_assignment(tout);); CTRACE("nlsat", r == l_false, display(tout);); SASSERT(r != l_true || check_satisfied(m_clauses)); return r; } void init_search() { undo_until_empty(); while (m_scope_lvl > 0) { undo_new_level(); } m_xk = null_var; for (unsigned i = 0; i < m_bvalues.size(); ++i) { m_bvalues[i] = l_undef; } m_assignment.reset(); } lbool check(literal_vector& assumptions) { literal_vector result; unsigned sz = assumptions.size(); literal const* ptr = assumptions.c_ptr(); for (unsigned i = 0; i < sz; ++i) { mk_clause(1, ptr+i, (assumption)(ptr+i)); } display_literal_assumption dla(*this, assumptions); scoped_display_assumptions _scoped_display(*this, dla); lbool r = check(); if (r == l_false) { // collect used literals from m_lemma_assumptions vector deps; get_core(deps); for (unsigned i = 0; i < deps.size(); ++i) { literal const* lp = (literal const*)(deps[i]); if (ptr <= lp && lp < ptr + sz) { result.push_back(*lp); } } } collect(assumptions, m_clauses); collect(assumptions, m_learned); del_clauses(m_valids); if (m_check_lemmas) { for (clause* c : m_learned) { check_lemma(c->size(), c->c_ptr(), false, nullptr); } } #if 0 for (clause* c : m_learned) { IF_VERBOSE(0, display(verbose_stream() << "KEEP: ", c->size(), c->c_ptr()) << "\n"); } #endif assumptions.reset(); assumptions.append(result); return r; } void get_core(vector& deps) { m_asm.linearize(m_lemma_assumptions.get(), deps); } void collect(literal_vector const& assumptions, clause_vector& clauses) { unsigned j = 0; for (clause * c : clauses) { if (collect(assumptions, *c)) { del_clause(c); } else { clauses[j++] = c; } } clauses.shrink(j); } bool collect(literal_vector const& assumptions, clause const& c) { unsigned sz = assumptions.size(); literal const* ptr = assumptions.c_ptr(); _assumption_set asms = static_cast<_assumption_set>(c.assumptions()); if (asms == nullptr) { return false; } vector deps; m_asm.linearize(asms, deps); for (auto dep : deps) { if (ptr <= dep && dep < ptr + sz) { return true; } } return false; } // ----------------------- // // Conflict Resolution // // ----------------------- svector m_marks; // bool_var -> bool temp mark used during conflict resolution unsigned m_num_marks; scoped_literal_vector m_lemma; scoped_literal_vector m_lazy_clause; assumption_set_ref m_lemma_assumptions; // assumption tracking // Conflict resolution invariant: a marked literal is in m_lemma or on the trail stack. bool check_marks() { for (unsigned m : m_marks) { (void)m; SASSERT(m == 0); } return true; } unsigned scope_lvl() const { return m_scope_lvl; } bool is_marked(bool_var b) const { return m_marks.get(b, 0) == 1; } void mark(bool_var b) { m_marks.setx(b, 1, 0); } void reset_mark(bool_var b) { m_marks[b] = 0; } void reset_marks() { for (auto const& l : m_lemma) { reset_mark(l.var()); } } void process_antecedent(literal antecedent) { bool_var b = antecedent.var(); TRACE("nlsat_resolve", display(tout << "resolving antecedent: ", antecedent) << "\n";); if (assigned_value(antecedent) == l_undef) { // antecedent must be false in the current arith interpretation SASSERT(value(antecedent) == l_false); if (!is_marked(b)) { SASSERT(is_arith_atom(b) && max_var(b) < m_xk); // must be in a previous stage TRACE("nlsat_resolve", tout << "literal is unassigned, but it is false in arithmetic interpretation, adding it to lemma\n";); mark(b); m_lemma.push_back(antecedent); } return; } unsigned b_lvl = m_levels[b]; TRACE("nlsat_resolve", tout << "b_lvl: " << b_lvl << ", is_marked(b): " << is_marked(b) << ", m_num_marks: " << m_num_marks << "\n";); if (!is_marked(b)) { mark(b); if (b_lvl == scope_lvl() /* same level */ && max_var(b) == m_xk /* same stage */) { TRACE("nlsat_resolve", tout << "literal is in the same level and stage, increasing marks\n";); m_num_marks++; } else { TRACE("nlsat_resolve", tout << "previous level or stage, adding literal to lemma\n"; tout << "max_var(b): " << max_var(b) << ", m_xk: " << m_xk << ", lvl: " << b_lvl << ", scope_lvl: " << scope_lvl() << "\n";); m_lemma.push_back(antecedent); } } } void resolve_clause(bool_var b, unsigned sz, literal const * c) { TRACE("nlsat_proof", tout << "resolving "; if (b != null_bool_var) display_atom(tout, b) << "\n"; display(tout, sz, c); tout << "\n";); TRACE("nlsat_proof_sk", tout << "resolving "; if (b != null_bool_var) tout << "b" << b; tout << "\n"; display_abst(tout, sz, c); tout << "\n";); for (unsigned i = 0; i < sz; i++) { if (c[i].var() != b) process_antecedent(c[i]); } } void resolve_clause(bool_var b, clause const & c) { TRACE("nlsat_resolve", tout << "resolving clause for b: " << b << "\n";); resolve_clause(b, c.size(), c.c_ptr()); m_lemma_assumptions = m_asm.mk_join(static_cast<_assumption_set>(c.assumptions()), m_lemma_assumptions); } void resolve_lazy_justification(bool_var b, lazy_justification const & jst) { TRACE("nlsat_resolve", tout << "resolving lazy_justification for b" << b << "\n";); unsigned sz = jst.num_lits(); // Dump lemma as Mathematica formula that must be true, // if the current interpretation (really) makes the core in jst infeasible. TRACE("nlsat_mathematica", tout << "assignment lemma\n"; literal_vector core; for (unsigned i = 0; i < sz; i++) { core.push_back(~jst.lit(i)); } display_mathematica_lemma(tout, core.size(), core.c_ptr(), true);); m_lazy_clause.reset(); m_explain(jst.num_lits(), jst.lits(), m_lazy_clause); for (unsigned i = 0; i < sz; i++) m_lazy_clause.push_back(~jst.lit(i)); // lazy clause is a valid clause TRACE("nlsat_mathematica", display_mathematica_lemma(tout, m_lazy_clause.size(), m_lazy_clause.c_ptr());); TRACE("nlsat_proof_sk", tout << "theory lemma\n"; display_abst(tout, m_lazy_clause.size(), m_lazy_clause.c_ptr()); tout << "\n";); TRACE("nlsat_resolve", tout << "m_xk: " << m_xk << ", "; m_display_var(tout, m_xk) << "\n"; tout << "new valid clause:\n"; display(tout, m_lazy_clause.size(), m_lazy_clause.c_ptr()) << "\n";); if (m_check_lemmas) { m_valids.push_back(mk_clause_core(m_lazy_clause.size(), m_lazy_clause.c_ptr(), false, nullptr)); } DEBUG_CODE({ unsigned sz = m_lazy_clause.size(); for (unsigned i = 0; i < sz; i++) { literal l = m_lazy_clause[i]; if (l.var() != b) { SASSERT(value(l) == l_false); } else { SASSERT(value(l) == l_true); SASSERT(!l.sign() || m_bvalues[b] == l_false); SASSERT(l.sign() || m_bvalues[b] == l_true); } } }); resolve_clause(b, m_lazy_clause.size(), m_lazy_clause.c_ptr()); for (unsigned i = 0; i < jst.num_clauses(); ++i) { clause const& c = jst.clause(i); TRACE("nlsat", display(tout << "adding clause assumptions ", c) << "\n";); m_lemma_assumptions = m_asm.mk_join(static_cast<_assumption_set>(c.assumptions()), m_lemma_assumptions); } } /** \brief Return true if all literals in ls are from previous stages. */ bool only_literals_from_previous_stages(unsigned num, literal const * ls) const { for (unsigned i = 0; i < num; i++) { if (max_var(ls[i]) == m_xk) return false; } return true; } /** \brief Return the maximum scope level in ls. \pre This method assumes value(ls[i]) is l_false for i in [0, num) */ unsigned max_scope_lvl(unsigned num, literal const * ls) { unsigned max = 0; for (unsigned i = 0; i < num; i++) { literal l = ls[i]; bool_var b = l.var(); SASSERT(value(ls[i]) == l_false); if (assigned_value(l) == l_false) { unsigned lvl = m_levels[b]; if (lvl > max) max = lvl; } else { // l must be a literal from a previous stage that is false in the current interpretation SASSERT(assigned_value(l) == l_undef); SASSERT(max_var(b) != null_var); SASSERT(m_xk != null_var); SASSERT(max_var(b) < m_xk); } } return max; } /** \brief Remove literals of the given lvl (that are in the current stage) from lemma. \pre This method assumes value(ls[i]) is l_false for i in [0, num) */ void remove_literals_from_lvl(scoped_literal_vector & lemma, unsigned lvl) { TRACE("nlsat_resolve", tout << "removing literals from lvl: " << lvl << " and stage " << m_xk << "\n";); unsigned sz = lemma.size(); unsigned j = 0; for (unsigned i = 0; i < sz; i++) { literal l = lemma[i]; bool_var b = l.var(); SASSERT(is_marked(b)); SASSERT(value(lemma[i]) == l_false); if (assigned_value(l) == l_false && m_levels[b] == lvl && max_var(b) == m_xk) { m_num_marks++; continue; } lemma.set(j, l); j++; } lemma.shrink(j); } /** \brief Return true if it is a Boolean lemma. */ bool is_bool_lemma(unsigned sz, literal const * ls) const { for (unsigned i = 0; i < sz; i++) { if (m_atoms[ls[i].var()] != nullptr) return false; } return true; } /** Return the maximal decision level in lemma for literals in the first sz-1 positions that are at the same stage. If all these literals are from previous stages, we just backtrack the current level. */ unsigned find_new_level_arith_lemma(unsigned sz, literal const * lemma) { SASSERT(!is_bool_lemma(sz, lemma)); unsigned new_lvl = 0; bool found_lvl = false; for (unsigned i = 0; i < sz - 1; i++) { literal l = lemma[i]; if (max_var(l) == m_xk) { bool_var b = l.var(); if (!found_lvl) { found_lvl = true; new_lvl = m_levels[b]; } else { if (m_levels[b] > new_lvl) new_lvl = m_levels[b]; } } } SASSERT(!found_lvl || new_lvl < scope_lvl()); if (!found_lvl) { TRACE("nlsat_resolve", tout << "fail to find new lvl, using previous one\n";); new_lvl = scope_lvl() - 1; } return new_lvl; } /** \brief Return true if the conflict was solved. */ bool resolve(clause const & conflict) { clause const * conflict_clause = &conflict; m_lemma_assumptions = nullptr; start: SASSERT(check_marks()); TRACE("nlsat_proof", tout << "STARTING RESOLUTION\n";); TRACE("nlsat_proof_sk", tout << "STARTING RESOLUTION\n";); m_conflicts++; TRACE("nlsat", tout << "resolve, conflicting clause:\n"; display(tout, *conflict_clause) << "\n"; tout << "xk: "; if (m_xk != null_var) m_display_var(tout, m_xk); else tout << ""; tout << "\n"; tout << "scope_lvl: " << scope_lvl() << "\n"; tout << "current assignment\n"; display_assignment(tout);); m_num_marks = 0; m_lemma.reset(); m_lemma_assumptions = nullptr; resolve_clause(null_bool_var, *conflict_clause); unsigned top = m_trail.size(); bool found_decision; while (true) { found_decision = false; while (m_num_marks > 0) { SASSERT(top > 0); trail & t = m_trail[top-1]; SASSERT(t.m_kind != trail::NEW_STAGE); // we only mark literals that are in the same stage if (t.m_kind == trail::BVAR_ASSIGNMENT) { bool_var b = t.m_b; if (is_marked(b)) { TRACE("nlsat_resolve", tout << "found marked: b" << b << "\n"; display_atom(tout, b) << "\n";); m_num_marks--; reset_mark(b); justification jst = m_justifications[b]; switch (jst.get_kind()) { case justification::CLAUSE: resolve_clause(b, *(jst.get_clause())); break; case justification::LAZY: resolve_lazy_justification(b, *(jst.get_lazy())); break; case justification::DECISION: SASSERT(m_num_marks == 0); found_decision = true; TRACE("nlsat_resolve", tout << "found decision\n";); m_lemma.push_back(literal(b, m_bvalues[b] == l_true)); break; default: UNREACHABLE(); break; } } } top--; } // m_lemma is an implicating clause after backtracking current scope level. if (found_decision) break; // If lemma only contains literals from previous stages, then we can stop. // We make progress by returning to a previous stage with additional information (new lemma) // that forces us to select a new partial interpretation if (only_literals_from_previous_stages(m_lemma.size(), m_lemma.c_ptr())) break; // Conflict does not depend on the current decision, and it is still in the current stage. // We should find // - the maximal scope level in the lemma // - remove literal assigned in the scope level from m_lemma // - backtrack to this level // - and continue conflict resolution from there // - we must bump m_num_marks for literals removed from m_lemma unsigned max_lvl = max_scope_lvl(m_lemma.size(), m_lemma.c_ptr()); TRACE("nlsat_resolve", tout << "conflict does not depend on current decision, backtracking to level: " << max_lvl << "\n";); SASSERT(max_lvl < scope_lvl()); remove_literals_from_lvl(m_lemma, max_lvl); undo_until_level(max_lvl); top = m_trail.size(); TRACE("nlsat_resolve", tout << "scope_lvl: " << scope_lvl() << " num marks: " << m_num_marks << "\n";); SASSERT(scope_lvl() == max_lvl); } TRACE("nlsat_proof", tout << "New lemma\n"; display(tout, m_lemma); tout << "\n=========================\n";); TRACE("nlsat_proof_sk", tout << "New lemma\n"; display_abst(tout, m_lemma); tout << "\n=========================\n";); if (m_lemma.empty()) { TRACE("nlsat", tout << "empty clause generated\n";); return false; // problem is unsat, empty clause was generated } reset_marks(); // remove marks from the literals in m_lemmas. TRACE("nlsat", tout << "new lemma:\n"; display(tout, m_lemma.size(), m_lemma.c_ptr()); tout << "\n"; tout << "found_decision: " << found_decision << "\n";); if (false && m_check_lemmas) { check_lemma(m_lemma.size(), m_lemma.c_ptr(), false, m_lemma_assumptions.get()); } // There are two possibilities: // 1) m_lemma contains only literals from previous stages, and they // are false in the current interpretation. We make progress // by returning to a previous stage with additional information (new clause) // that forces us to select a new partial interpretation // >>> Return to some previous stage (we may also backjump many decisions and stages). // // 2) m_lemma contains at most one literal from the current level (the last literal). // Moreover, this literal was a decision, but the new lemma forces it to // be assigned to a different value. // >>> In this case, we remain in the same stage but, we add a new asserted literal // in a previous scope level. We may backjump many decisions. // unsigned sz = m_lemma.size(); clause * new_cls = nullptr; if (!found_decision) { // Case 1) // We just have to find the maximal variable in m_lemma, and return to that stage // Remark: the lemma may contain only boolean literals, in this case new_max_var == null_var; var new_max_var = max_var(sz, m_lemma.c_ptr()); TRACE("nlsat_resolve", tout << "backtracking to stage: " << new_max_var << ", curr: " << m_xk << "\n";); undo_until_stage(new_max_var); SASSERT(m_xk == new_max_var); new_cls = mk_clause(sz, m_lemma.c_ptr(), true, m_lemma_assumptions.get()); TRACE("nlsat", tout << "new_level: " << scope_lvl() << "\nnew_stage: " << new_max_var << "\n"; if (new_max_var != null_var) m_display_var(tout, new_max_var) << "\n";); } else { SASSERT(scope_lvl() >= 1); // Case 2) if (is_bool_lemma(m_lemma.size(), m_lemma.c_ptr())) { // boolean lemma, we just backtrack until the last literal is unassigned. bool_var max_bool_var = m_lemma[m_lemma.size()-1].var(); undo_until_unassigned(max_bool_var); } else { // We must find the maximal decision level in literals in the first sz-1 positions that // are at the same stage. If all these literals are from previous stages, // we just backtrack the current level. unsigned new_lvl = find_new_level_arith_lemma(m_lemma.size(), m_lemma.c_ptr()); TRACE("nlsat", tout << "backtracking to new level: " << new_lvl << ", curr: " << m_scope_lvl << "\n";); undo_until_level(new_lvl); } if (lemma_is_clause(*conflict_clause)) { TRACE("nlsat", tout << "found decision literal in conflict clause\n";); VERIFY(process_clause(*conflict_clause, true)); return true; } new_cls = mk_clause(sz, m_lemma.c_ptr(), true, m_lemma_assumptions.get()); } NLSAT_VERBOSE(display(verbose_stream(), *new_cls) << "\n";); if (!process_clause(*new_cls, true)) { TRACE("nlsat", tout << "new clause triggered another conflict, restarting conflict resolution...\n"; display(tout, *new_cls) << "\n"; ); // we are still in conflict conflict_clause = new_cls; goto start; } TRACE("nlsat_resolve_done", display_assignment(tout);); return true; } bool lemma_is_clause(clause const& cls) const { bool same = (m_lemma.size() == cls.size()); for (unsigned i = 0; same && i < m_lemma.size(); ++i) { same = m_lemma[i] == cls[i]; } return same; } // ----------------------- // // Debugging // // ----------------------- bool check_watches() const { DEBUG_CODE( for (var x = 0; x < num_vars(); x++) { clause_vector const & cs = m_watches[x]; unsigned sz = cs.size(); for (unsigned i = 0; i < sz; i++) { SASSERT(max_var(*(cs[i])) == x); } }); return true; } bool check_bwatches() const { DEBUG_CODE( for (bool_var b = 0; b < m_bwatches.size(); b++) { clause_vector const & cs = m_bwatches[b]; unsigned sz = cs.size(); for (unsigned i = 0; i < sz; i++) { clause const & c = *(cs[i]); SASSERT(max_var(c) == null_var); SASSERT(max_bvar(c) == b); } }); return true; } bool check_invariant() const { SASSERT(check_watches()); SASSERT(check_bwatches()); return true; } bool check_satisfied(clause_vector const & cs) const { unsigned sz = cs.size(); for (unsigned i = 0; i < sz; i++) { clause const & c = *(cs[i]); if (!is_satisfied(c)) { TRACE("nlsat", tout << "not satisfied\n"; display(tout, c); tout << "\n";); return false; } } return true; } bool check_satisfied() const { TRACE("nlsat", tout << "bk: b" << m_bk << ", xk: x" << m_xk << "\n"; if (m_xk != null_var) { m_display_var(tout, m_xk); tout << "\n"; }); unsigned num = m_atoms.size(); if (m_bk != null_bool_var) num = m_bk; for (bool_var b = 0; b < num; b++) { if (!check_satisfied(m_bwatches[b])) { UNREACHABLE(); return false; } } if (m_xk != null_var) { for (var x = 0; x < m_xk; x++) { if (!check_satisfied(m_watches[x])) { UNREACHABLE(); return false; } } } return true; } // ----------------------- // // Statistics // // ----------------------- void collect_statistics(statistics & st) { st.update("nlsat conflicts", m_conflicts); st.update("nlsat propagations", m_propagations); st.update("nlsat decisions", m_decisions); st.update("nlsat stages", m_stages); st.update("nlsat irrational assignments", m_irrational_assignments); } void reset_statistics() { m_conflicts = 0; m_propagations = 0; m_decisions = 0; m_stages = 0; m_irrational_assignments = 0; } // ----------------------- // // Variable reordering // // ----------------------- struct var_info_collector { pmanager & pm; atom_vector const & m_atoms; unsigned_vector m_max_degree; unsigned_vector m_num_occs; var_info_collector(pmanager & _pm, atom_vector const & atoms, unsigned num_vars): pm(_pm), m_atoms(atoms) { m_max_degree.resize(num_vars, 0); m_num_occs.resize(num_vars, 0); } var_vector m_vars; void collect(poly * p) { m_vars.reset(); pm.vars(p, m_vars); unsigned sz = m_vars.size(); for (unsigned i = 0; i < sz; i++) { var x = m_vars[i]; unsigned k = pm.degree(p, x); m_num_occs[x]++; if (k > m_max_degree[x]) m_max_degree[x] = k; } } void collect(literal l) { bool_var b = l.var(); atom * a = m_atoms[b]; if (a == nullptr) return; if (a->is_ineq_atom()) { unsigned sz = to_ineq_atom(a)->size(); for (unsigned i = 0; i < sz; i++) { collect(to_ineq_atom(a)->p(i)); } } else { collect(to_root_atom(a)->p()); } } void collect(clause const & c) { unsigned sz = c.size(); for (unsigned i = 0; i < sz; i++) collect(c[i]); } void collect(clause_vector const & cs) { unsigned sz = cs.size(); for (unsigned i = 0; i < sz; i++) collect(*(cs[i])); } std::ostream& display(std::ostream & out, display_var_proc const & proc) { unsigned sz = m_num_occs.size(); for (unsigned i = 0; i < sz; i++) { proc(out, i); out << " -> " << m_max_degree[i] << " : " << m_num_occs[i] << "\n"; } return out; } }; struct reorder_lt { var_info_collector const & m_info; reorder_lt(var_info_collector const & info):m_info(info) {} bool operator()(var x, var y) const { // high degree first if (m_info.m_max_degree[x] < m_info.m_max_degree[y]) return false; if (m_info.m_max_degree[x] > m_info.m_max_degree[y]) return true; // more constrained first if (m_info.m_num_occs[x] < m_info.m_num_occs[y]) return false; if (m_info.m_num_occs[x] > m_info.m_num_occs[y]) return true; return x < y; } }; // Order variables by degree and number of occurrences void heuristic_reorder() { unsigned num = num_vars(); var_info_collector collector(m_pm, m_atoms, num); collector.collect(m_clauses); collector.collect(m_learned); TRACE("nlsat_reorder", collector.display(tout, m_display_var);); var_vector new_order; for (var x = 0; x < num; x++) { new_order.push_back(x); } std::sort(new_order.begin(), new_order.end(), reorder_lt(collector)); TRACE("nlsat_reorder", tout << "new order: "; for (unsigned i = 0; i < num; i++) tout << new_order[i] << " "; tout << "\n";); var_vector perm; perm.resize(num, 0); for (var x = 0; x < num; x++) { perm[new_order[x]] = x; } reorder(perm.size(), perm.c_ptr()); SASSERT(check_invariant()); } void shuffle_vars() { var_vector p; unsigned num = num_vars(); for (var x = 0; x < num; x++) { p.push_back(x); } random_gen r(++m_random_seed); shuffle(p.size(), p.c_ptr(), r); reorder(p.size(), p.c_ptr()); } bool can_reorder() const { for (clause* c : m_learned) { if (has_root_atom(*c)) return false; } for (clause* c : m_clauses) { if (has_root_atom(*c)) return false; } return true; } /** \brief Reorder variables using the giving permutation. p maps internal variables to their new positions */ void reorder(unsigned sz, var const * p) { remove_learned_roots(); SASSERT(can_reorder()); TRACE("nlsat_reorder", tout << "solver before variable reorder\n"; display(tout); display_vars(tout); tout << "\npermutation:\n"; for (unsigned i = 0; i < sz; i++) tout << p[i] << " "; tout << "\n"; ); SASSERT(num_vars() == sz); TRACE("nlsat_bool_assignment_bug", tout << "before reset watches\n"; display_bool_assignment(tout);); reset_watches(); assignment new_assignment(m_am); for (var x = 0; x < num_vars(); x++) { if (m_assignment.is_assigned(x)) new_assignment.set(p[x], m_assignment.value(x)); } var_vector new_inv_perm; new_inv_perm.resize(sz); // the undo_until_size(0) statement erases the Boolean assignment. // undo_until_size(0) undo_until_stage(null_var); m_cache.reset(); DEBUG_CODE({ for (var x = 0; x < num_vars(); x++) { SASSERT(m_watches[x].empty()); } }); // update m_perm mapping for (unsigned ext_x = 0; ext_x < sz; ext_x++) { // p: internal -> new pos // m_perm: internal -> external // m_inv_perm: external -> internal new_inv_perm[ext_x] = p[m_inv_perm[ext_x]]; m_perm.set(new_inv_perm[ext_x], ext_x); } svector is_int; is_int.swap(m_is_int); for (var x = 0; x < sz; x++) { m_is_int.setx(p[x], is_int[x], false); SASSERT(m_infeasible[x] == 0); } m_inv_perm.swap(new_inv_perm); DEBUG_CODE({ for (var x = 0; x < num_vars(); x++) { SASSERT(x == m_inv_perm[m_perm[x]]); SASSERT(m_watches[x].empty()); } }); m_pm.rename(sz, p); TRACE("nlsat_bool_assignment_bug", tout << "before reinit cache\n"; display_bool_assignment(tout);); reinit_cache(); m_assignment.swap(new_assignment); reattach_arith_clauses(m_clauses); reattach_arith_clauses(m_learned); TRACE("nlsat_reorder", tout << "solver after variable reorder\n"; display(tout); display_vars(tout);); } /** \brief Restore variable order. */ void restore_order() { // m_perm: internal -> external // m_inv_perm: external -> internal var_vector p; p.append(m_perm); reorder(p.size(), p.c_ptr()); DEBUG_CODE({ for (var x = 0; x < num_vars(); x++) { SASSERT(m_perm[x] == x); SASSERT(m_inv_perm[x] == x); } }); } /** \brief After variable reordering some lemmas containing root atoms may be ill-formed. */ void remove_learned_roots() { unsigned j = 0; for (clause* c : m_learned) { if (has_root_atom(*c)) { del_clause(c); } else { m_learned[j++] = c; } } m_learned.shrink(j); } /** \brief Return true if the clause contains an ill formed root atom */ bool has_root_atom(clause const & c) const { for (literal lit : c) { bool_var b = lit.var(); atom * a = m_atoms[b]; if (a && a->is_root_atom()) return true; } return false; } /** \brief reinsert all polynomials in the unique cache */ void reinit_cache() { reinit_cache(m_clauses); reinit_cache(m_learned); for (atom* a : m_atoms) reinit_cache(a); } void reinit_cache(clause_vector const & cs) { for (clause* c : cs) reinit_cache(*c); } void reinit_cache(clause const & c) { for (literal l : c) reinit_cache(l); } void reinit_cache(literal l) { bool_var b = l.var(); reinit_cache(m_atoms[b]); } void reinit_cache(atom* a) { if (a == nullptr) { } else if (a->is_ineq_atom()) { var max = 0; unsigned sz = to_ineq_atom(a)->size(); for (unsigned i = 0; i < sz; i++) { poly * p = to_ineq_atom(a)->p(i); VERIFY(m_cache.mk_unique(p) == p); var x = m_pm.max_var(p); if (x > max) max = x; } a->m_max_var = max; } else { poly * p = to_root_atom(a)->p(); VERIFY(m_cache.mk_unique(p) == p); a->m_max_var = m_pm.max_var(p); } } void reset_watches() { unsigned num = num_vars(); for (var x = 0; x < num; x++) { m_watches[x].reset(); } } void reattach_arith_clauses(clause_vector const & cs) { for (clause* cp : cs) { var x = max_var(*cp); if (x != null_var) m_watches[x].push_back(cp); } } // ----------------------- // // Solver initialization // // ----------------------- struct degree_lt { unsigned_vector & m_degrees; degree_lt(unsigned_vector & ds):m_degrees(ds) {} bool operator()(unsigned i1, unsigned i2) const { if (m_degrees[i1] < m_degrees[i2]) return true; if (m_degrees[i1] > m_degrees[i2]) return false; return i1 < i2; } }; unsigned_vector m_cs_degrees; unsigned_vector m_cs_p; void sort_clauses_by_degree(unsigned sz, clause ** cs) { if (sz <= 1) return; TRACE("nlsat_reorder_clauses", tout << "before:\n"; for (unsigned i = 0; i < sz; i++) { display(tout, *(cs[i])); tout << "\n"; }); m_cs_degrees.reset(); m_cs_p.reset(); for (unsigned i = 0; i < sz; i++) { m_cs_p.push_back(i); m_cs_degrees.push_back(degree(*(cs[i]))); } std::sort(m_cs_p.begin(), m_cs_p.end(), degree_lt(m_cs_degrees)); TRACE("nlsat_reorder_clauses", tout << "permutation: "; ::display(tout, m_cs_p.begin(), m_cs_p.end()); tout << "\n";); apply_permutation(sz, cs, m_cs_p.c_ptr()); TRACE("nlsat_reorder_clauses", tout << "after:\n"; for (unsigned i = 0; i < sz; i++) { display(tout, *(cs[i])); tout << "\n"; }); } void sort_watched_clauses() { unsigned num = num_vars(); for (unsigned i = 0; i < num; i++) { clause_vector & ws = m_watches[i]; sort_clauses_by_degree(ws.size(), ws.c_ptr()); } } // ----------------------- // // Full dimensional // // A problem is in the full dimensional fragment if it does // not contain equalities or non-strict inequalities. // // ----------------------- bool is_full_dimensional(literal l) const { atom * a = m_atoms[l.var()]; if (a == nullptr) return true; switch (a->get_kind()) { case atom::EQ: return l.sign(); case atom::LT: return !l.sign(); case atom::GT: return !l.sign(); case atom::ROOT_EQ: return l.sign(); case atom::ROOT_LT: return !l.sign(); case atom::ROOT_GT: return !l.sign(); case atom::ROOT_LE: return l.sign(); case atom::ROOT_GE: return l.sign(); default: UNREACHABLE(); return false; } } bool is_full_dimensional(clause const & c) const { for (literal l : c) { if (!is_full_dimensional(l)) return false; } return true; } bool is_full_dimensional(clause_vector const & cs) const { for (clause* c : cs) { if (!is_full_dimensional(*c)) return false; } return true; } bool is_full_dimensional() const { return is_full_dimensional(m_clauses); } // ----------------------- // // Simplification // // ----------------------- // solve simple equalities // TBD WU-Reit decomposition? /** \brief isolate variables in unit equalities. Assume a clause is c == v*p + q and the context implies p > 0 replace v by -q/p remove clause c, The for other occurrences of v, replace v*r + v*v*r' > 0 by by p*p*v*r + p*p*v*v*r' > 0 by p*q*r + q*q*r' > 0 The method ignores lemmas and assumes constraints don't use roots. */ void simplify() { polynomial_ref p(m_pm), q(m_pm); var v; init_var_signs(); SASSERT(m_learned.empty()); bool change = true; while (change) { change = false; for (clause* c : m_clauses) { if (solve_var(*c, v, p, q)) { q = -q; m_patch_var.push_back(v); m_patch_num.push_back(q); m_patch_denom.push_back(p); del_clause(c, m_clauses); substitute_var(v, p, q); change = true; break; } } } } void fix_patch() { for (unsigned i = m_patch_var.size(); i-- > 0; ) { var v = m_patch_var[i]; poly* q = m_patch_num.get(i); poly* p = m_patch_denom.get(i); scoped_anum pv(m_am), qv(m_am), val(m_am); m_pm.eval(p, m_assignment, pv); m_pm.eval(q, m_assignment, qv); val = qv / pv; TRACE("nlsat", m_display_var(tout << "patch ", v) << "\n"; if (m_assignment.is_assigned(v)) m_am.display(tout << "previous value: ", m_assignment.value(v)); tout << "\n"; m_am.display(tout << "updated value: ", val); tout << "\n"; ); m_assignment.set_core(v, val); } } void substitute_var(var x, poly* p, poly* q) { polynomial_ref pr(m_pm); polynomial_ref_vector ps(m_pm); u_map b2l; svector even; unsigned num_atoms = m_atoms.size(); for (unsigned j = 0; j < num_atoms; ++j) { atom* a = m_atoms[j]; if (a && a->is_ineq_atom()) { ineq_atom const& a1 = *to_ineq_atom(a); unsigned sz = a1.size(); ps.reset(); even.reset(); bool change = false; for (unsigned i = 0; i < sz; ++i) { poly * po = a1.p(i); m_pm.substitute(po, x, q, p, pr); ps.push_back(pr); even.push_back(a1.is_even(i)); change |= pr != po; if (m_pm.is_zero(pr)) { ps.reset(); even.reset(); change = true; break; } } if (!change) continue; literal l = mk_ineq_literal(a1.get_kind(), ps.size(), ps.c_ptr(), even.c_ptr()); if (a1.m_bool_var != l.var()) { b2l.insert(a1.m_bool_var, l); inc_ref(l); } } } update_clauses(b2l); for (auto const& kv : b2l) { dec_ref(kv.m_value); } } void update_clauses(u_map const& b2l) { literal_vector lits; clause_vector to_delete; unsigned n = m_clauses.size(); for (unsigned i = 0; i < n; ++i) { clause* c = m_clauses[i]; lits.reset(); bool changed = false; bool is_tautology = false; for (literal l : *c) { literal lit = null_literal; if (b2l.find(l.var(), lit)) { lit = l.sign() ? ~lit : lit; if (lit == true_literal) { is_tautology = true; } else if (lit != false_literal) { lits.push_back(lit); } changed = true; } else { lits.push_back(l); } } if (changed) { to_delete.push_back(c); if (!is_tautology) { mk_clause(lits.size(), lits.c_ptr(), c->is_learned(), static_cast<_assumption_set>(c->assumptions())); } } } for (clause* c : to_delete) { del_clause(c, m_clauses); } } bool is_unit_ineq(clause const& c) const { return c.size() == 1 && m_atoms[c[0].var()] && m_atoms[c[0].var()]->is_ineq_atom(); } bool is_unit_eq(clause const& c) const { return is_unit_ineq(c) && !c[0].sign() && m_atoms[c[0].var()]->is_eq(); } /** \brief determine whether the clause is a comparison v > k or v < k', where k >= 0 or k' <= 0. */ lbool is_cmp0(clause const& c, var& v) { if (!is_unit_ineq(c)) return l_undef; literal lit = c[0]; ineq_atom const& a = *to_ineq_atom(m_atoms[lit.var()]); bool sign = lit.sign(); poly * p0; if (!is_single_poly(a, p0)) return l_undef; if (m_pm.is_var(p0, v)) { if (!sign && a.get_kind() == atom::GT) { return l_true; } if (!sign && a.get_kind() == atom::LT) { return l_false; } return l_undef; } polynomial::scoped_numeral n(m_pm.m()); if (m_pm.is_var_num(p0, v, n)) { // x - k > 0 if (!sign && a.get_kind() == atom::GT && m_pm.m().is_nonneg(n)) { return l_true; } // x + k < 0 if (!sign && a.get_kind() == atom::LT && m_pm.m().is_nonpos(n)) { return l_false; } // !(x + k > 0) if (sign && a.get_kind() == atom::GT && m_pm.m().is_pos(n)) { return l_false; } // !(x - k < 0) if (sign && a.get_kind() == atom::LT && m_pm.m().is_neg(n)) { return l_true; } } return l_undef; } bool is_single_poly(ineq_atom const& a, poly*& p) { unsigned sz = a.size(); return sz == 1 && a.is_odd(0) && (p = a.p(0), true); } svector m_var_signs; void init_var_signs() { m_var_signs.reset(); for (clause* cp : m_clauses) { clause& c = *cp; var x = 0; lbool cmp = is_cmp0(c, x); switch (cmp) { case l_true: m_var_signs.setx(x, l_true, l_undef); break; case l_false: m_var_signs.setx(x, l_false, l_undef); break; default: break; } } } /** \brief returns true then c is an equality that is equivalent to v*p + q, and p > 0, v does not occur in p, q. */ bool solve_var(clause& c, var& v, polynomial_ref& p, polynomial_ref& q) { poly* p0; if (!is_unit_eq(c)) return false; ineq_atom & a = *to_ineq_atom(m_atoms[c[0].var()]); if (!is_single_poly(a, p0)) return false; var mx = max_var(p0); if (mx >= m_is_int.size()) return false; for (var x = 0; x <= mx; ++x) { if (m_is_int[x]) continue; if (1 == m_pm.degree(p0, x)) { p = m_pm.coeff(p0, x, 1, q); switch (m_pm.sign(p, m_var_signs)) { case l_true: v = x; return true; case l_false: v = x; p = -p; q = -q; return true; default: break; } } } return false; } // ----------------------- // // Pretty printing // // ----------------------- std::ostream& display_num_assignment(std::ostream & out, display_var_proc const & proc) const { for (var x = 0; x < num_vars(); x++) { if (m_assignment.is_assigned(x)) { proc(out, x); out << " -> "; m_am.display_decimal(out, m_assignment.value(x)); out << "\n"; } } return out; } std::ostream& display_bool_assignment(std::ostream & out) const { unsigned sz = m_atoms.size(); for (bool_var b = 0; b < sz; b++) { if (m_atoms[b] == nullptr && m_bvalues[b] != l_undef) { out << "b" << b << " -> " << (m_bvalues[b] == l_true ? "true" : "false") << "\n"; } else if (m_atoms[b] != nullptr && m_bvalues[b] != l_undef) { display(out << "b" << b << " ", *m_atoms[b]) << " -> " << (m_bvalues[b] == l_true ? "true" : "false") << "\n"; } } TRACE("nlsat_bool_assignment", for (bool_var b = 0; b < sz; b++) { out << "b" << b << " -> " << m_bvalues[b] << " "; if (m_atoms[b]) display(out, *m_atoms[b]); out << "\n"; }); return out; } bool display_mathematica_assignment(std::ostream & out) const { bool first = true; for (var x = 0; x < num_vars(); x++) { if (m_assignment.is_assigned(x)) { if (first) first = false; else out << " && "; out << "x" << x << " == "; m_am.display_mathematica(out, m_assignment.value(x)); } } return !first; } std::ostream& display_num_assignment(std::ostream & out) const { return display_num_assignment(out, m_display_var); } std::ostream& display_assignment(std::ostream& out) const { display_bool_assignment(out); display_num_assignment(out); return out; } std::ostream& display(std::ostream& out, justification j) const { switch (j.get_kind()) { case justification::CLAUSE: display(out, *j.get_clause()) << "\n"; break; case justification::LAZY: { lazy_justification const& lz = *j.get_lazy(); display_not(out, lz.num_lits(), lz.lits()) << "\n"; for (unsigned i = 0; i < lz.num_clauses(); ++i) { display(out, lz.clause(i)) << "\n"; } break; } default: out << j.get_kind() << "\n"; break; } return out; } std::ostream& display(std::ostream & out, ineq_atom const & a, display_var_proc const & proc, bool use_star = false) const { unsigned sz = a.size(); for (unsigned i = 0; i < sz; i++) { if (use_star && i > 0) out << "*"; bool is_even = a.is_even(i); if (is_even || sz > 1) out << "("; m_pm.display(out, a.p(i), proc, use_star); if (is_even || sz > 1) out << ")"; if (is_even) out << "^2"; } switch (a.get_kind()) { case atom::LT: out << " < 0"; break; case atom::GT: out << " > 0"; break; case atom::EQ: out << " = 0"; break; default: UNREACHABLE(); break; } return out; } std::ostream& display_mathematica(std::ostream & out, ineq_atom const & a) const { unsigned sz = a.size(); for (unsigned i = 0; i < sz; i++) { if (i > 0) out << "*"; bool is_even = a.is_even(i); if (sz > 1) out << "("; if (is_even) out << "("; m_pm.display(out, a.p(i), display_var_proc(), true); if (is_even) out << "^2)"; if (sz > 1) out << ")"; } switch (a.get_kind()) { case atom::LT: out << " < 0"; break; case atom::GT: out << " > 0"; break; case atom::EQ: out << " == 0"; break; default: UNREACHABLE(); break; } return out; } std::ostream& display_smt2(std::ostream & out, ineq_atom const & a, display_var_proc const & proc) const { switch (a.get_kind()) { case atom::LT: out << "(< "; break; case atom::GT: out << "(> "; break; case atom::EQ: out << "(= "; break; default: UNREACHABLE(); break; } unsigned sz = a.size(); if (sz > 1) out << "(* "; for (unsigned i = 0; i < sz; i++) { if (i > 0) out << " "; if (a.is_even(i)) { out << "(* "; m_pm.display_smt2(out, a.p(i), proc); out << " "; m_pm.display_smt2(out, a.p(i), proc); out << ")"; } else { m_pm.display_smt2(out, a.p(i), proc); } } if (sz > 1) out << ")"; out << " 0)"; return out; } std::ostream& display_smt2(std::ostream & out, root_atom const & a, display_var_proc const & proc) const { return display(out, a, proc); } std::ostream& display(std::ostream & out, root_atom const & a, display_var_proc const & proc) const { proc(out, a.x()); switch (a.get_kind()) { case atom::ROOT_LT: out << " < "; break; case atom::ROOT_GT: out << " > "; break; case atom::ROOT_LE: out << " <= "; break; case atom::ROOT_GE: out << " >= "; break; case atom::ROOT_EQ: out << " = "; break; default: UNREACHABLE(); break; } out << "root[" << a.i() << "]("; m_pm.display(out, a.p(), proc); out << ")"; return out; } struct mathematica_var_proc : public display_var_proc { var m_x; public: mathematica_var_proc(var x):m_x(x) {} std::ostream& operator()(std::ostream & out, var x) const override { if (m_x == x) return out << "#1"; else return out << "x" << x; } }; std::ostream& display_mathematica(std::ostream & out, root_atom const & a) const { out << "x" << a.x(); switch (a.get_kind()) { case atom::ROOT_LT: out << " < "; break; case atom::ROOT_GT: out << " > "; break; case atom::ROOT_LE: out << " <= "; break; case atom::ROOT_GE: out << " >= "; break; case atom::ROOT_EQ: out << " == "; break; default: UNREACHABLE(); break; } out << "Root["; m_pm.display(out, a.p(), mathematica_var_proc(a.x()), true); out << " &, " << a.i() << "]"; return out; } std::ostream& display_smt2(std::ostream & out, root_atom const & a) const { NOT_IMPLEMENTED_YET(); return out; } std::ostream& display(std::ostream & out, atom const & a, display_var_proc const & proc) const { if (a.is_ineq_atom()) return display(out, static_cast(a), proc); else return display(out, static_cast(a), proc); } std::ostream& display(std::ostream & out, atom const & a) const { return display(out, a, m_display_var); } std::ostream& display_mathematica(std::ostream & out, atom const & a) const { if (a.is_ineq_atom()) return display_mathematica(out, static_cast(a)); else return display_mathematica(out, static_cast(a)); } std::ostream& display_smt2(std::ostream & out, atom const & a, display_var_proc const & proc) const { if (a.is_ineq_atom()) return display_smt2(out, static_cast(a), proc); else return display_smt2(out, static_cast(a), proc); } std::ostream& display_atom(std::ostream & out, bool_var b, display_var_proc const & proc) const { if (b == 0) out << "true"; else if (m_atoms[b] == 0) out << "b" << b; else display(out, *(m_atoms[b]), proc); return out; } std::ostream& display_atom(std::ostream & out, bool_var b) const { return display_atom(out, b, m_display_var); } std::ostream& display_mathematica_atom(std::ostream & out, bool_var b) const { if (b == 0) out << "(0 < 1)"; else if (m_atoms[b] == 0) out << "b" << b; else display_mathematica(out, *(m_atoms[b])); return out; } std::ostream& display_smt2_atom(std::ostream & out, bool_var b, display_var_proc const & proc) const { if (b == 0) out << "true"; else if (m_atoms[b] == 0) out << "b" << b; else display_smt2(out, *(m_atoms[b]), proc); return out; } std::ostream& display(std::ostream & out, literal l, display_var_proc const & proc) const { if (l.sign()) { bool_var b = l.var(); out << "!"; if (m_atoms[b] != 0) out << "("; display_atom(out, b, proc); if (m_atoms[b] != 0) out << ")"; } else { display_atom(out, l.var(), proc); } return out; } std::ostream& display(std::ostream & out, literal l) const { return display(out, l, m_display_var); } std::ostream& display_smt2(std::ostream & out, literal l) const { return display_smt2(out, l, m_display_var); } std::ostream& display_mathematica(std::ostream & out, literal l) const { if (l.sign()) { bool_var b = l.var(); out << "!"; if (m_atoms[b] != 0) out << "("; display_mathematica_atom(out, b); if (m_atoms[b] != 0) out << ")"; } else { display_mathematica_atom(out, l.var()); } return out; } std::ostream& display_smt2(std::ostream & out, literal l, display_var_proc const & proc) const { if (l.sign()) { bool_var b = l.var(); out << "(not "; display_smt2_atom(out, b, proc); out << ")"; } else { display_smt2_atom(out, l.var(), proc); } return out; } std::ostream& display_assumptions(std::ostream & out, _assumption_set s) const { vector deps; m_asm.linearize(s, deps); bool first = true; for (auto dep : deps) { if (first) first = false; else out << " "; if (m_display_assumption) (*m_display_assumption)(out, dep); } return out; } std::ostream& display(std::ostream & out, unsigned num, literal const * ls, display_var_proc const & proc) const { for (unsigned i = 0; i < num; i++) { if (i > 0) out << " or "; display(out, ls[i], proc); } return out; } std::ostream& display(std::ostream & out, unsigned num, literal const * ls) const { return display(out, num, ls, m_display_var); } std::ostream& display_not(std::ostream & out, unsigned num, literal const * ls, display_var_proc const & proc) const { for (unsigned i = 0; i < num; i++) { if (i > 0) out << " or "; display(out, ~ls[i], proc); } return out; } std::ostream& display_not(std::ostream & out, unsigned num, literal const * ls) const { return display_not(out, num, ls, m_display_var); } std::ostream& display(std::ostream & out, scoped_literal_vector const & cs) { return display(out, cs.size(), cs.c_ptr(), m_display_var); } std::ostream& display(std::ostream & out, clause const & c, display_var_proc const & proc) const { if (c.assumptions() != nullptr) { display_assumptions(out, static_cast<_assumption_set>(c.assumptions())); out << " |- "; } return display(out, c.size(), c.c_ptr(), proc); } std::ostream& display(std::ostream & out, clause const & c) const { return display(out, c, m_display_var); } std::ostream& display_smt2(std::ostream & out, unsigned num, literal const * ls, display_var_proc const & proc) const { if (num == 0) { out << "false"; } else if (num == 1) { display_smt2(out, ls[0], proc); } else { out << "(or"; for (unsigned i = 0; i < num; i++) { out << " "; display_smt2(out, ls[i], proc); } out << ")"; } return out; } std::ostream& display_smt2(std::ostream & out, clause const & c, display_var_proc const & proc = display_var_proc()) const { return display_smt2(out, c.size(), c.c_ptr(), proc); } std::ostream& display_abst(std::ostream & out, literal l) const { if (l.sign()) { bool_var b = l.var(); out << "!"; if (b == true_bool_var) out << "true"; else out << "b" << b; } else { out << "b" << l.var(); } return out; } std::ostream& display_abst(std::ostream & out, unsigned num, literal const * ls) const { for (unsigned i = 0; i < num; i++) { if (i > 0) out << " or "; display_abst(out, ls[i]); } return out; } std::ostream& display_abst(std::ostream & out, scoped_literal_vector const & cs) const { return display_abst(out, cs.size(), cs.c_ptr()); } std::ostream& display_abst(std::ostream & out, clause const & c) const { return display_abst(out, c.size(), c.c_ptr()); } std::ostream& display_mathematica(std::ostream & out, clause const & c) const { out << "("; unsigned sz = c.size(); for (unsigned i = 0; i < sz; i++) { if (i > 0) out << " || "; display_mathematica(out, c[i]); } out << ")"; return out; } // Debugging support: // Display generated lemma in Mathematica format. // Mathematica must reduce lemma to True (modulo resource constraints). std::ostream& display_mathematica_lemma(std::ostream & out, unsigned num, literal const * ls, bool include_assignment = false) const { out << "Resolve[ForAll[{"; // var definition for (unsigned i = 0; i < num_vars(); i++) { if (i > 0) out << ", "; out << "x" << i; } out << "}, "; if (include_assignment) { out << "!("; if (!display_mathematica_assignment(out)) out << "0 < 1"; // nothing was printed out << ") || "; } for (unsigned i = 0; i < num; i++) { if (i > 0) out << " || "; display_mathematica(out, ls[i]); } out << "], Reals]\n"; // end of exists return out; } std::ostream& display(std::ostream & out, clause_vector const & cs, display_var_proc const & proc) const { for (clause* c : cs) { display(out, *c, proc) << "\n"; } return out; } std::ostream& display(std::ostream & out, clause_vector const & cs) const { return display(out, cs, m_display_var); } std::ostream& display_mathematica(std::ostream & out, clause_vector const & cs) const { unsigned sz = cs.size(); for (unsigned i = 0; i < sz; i++) { if (i > 0) out << ",\n"; display_mathematica(out << " ", *(cs[i])); } return out; } std::ostream& display_abst(std::ostream & out, clause_vector const & cs) const { for (clause* c : cs) { display_abst(out, *c) << "\n"; } return out; } std::ostream& display(std::ostream & out, display_var_proc const & proc) const { display(out, m_clauses, proc); if (!m_learned.empty()) { display(out << "Lemmas:\n", m_learned, proc); } return out; } std::ostream& display_mathematica(std::ostream & out) const { return display_mathematica(out << "{\n", m_clauses) << "}\n"; } std::ostream& display_abst(std::ostream & out) const { display_abst(out, m_clauses); if (!m_learned.empty()) { display_abst(out << "Lemmas:\n", m_learned); } return out; } std::ostream& display(std::ostream & out) const { display(out, m_display_var); display_assignment(out << "assignment:\n"); return out << "---\n"; } std::ostream& display_vars(std::ostream & out) const { for (unsigned i = 0; i < num_vars(); i++) { out << i << " -> "; m_display_var(out, i); out << "\n"; } return out; } std::ostream& display_smt2_arith_decls(std::ostream & out) const { unsigned sz = m_is_int.size(); for (unsigned i = 0; i < sz; i++) { if (m_is_int[i]) out << "(declare-fun x" << i << " () Int)\n"; else out << "(declare-fun x" << i << " () Real)\n"; } return out; } std::ostream& display_smt2_bool_decls(std::ostream & out) const { unsigned sz = m_atoms.size(); for (unsigned i = 0; i < sz; i++) { if (m_atoms[i] == nullptr) out << "(declare-fun b" << i << " () Bool)\n"; } return out; } std::ostream& display_smt2(std::ostream & out) const { display_smt2_bool_decls(out); display_smt2_arith_decls(out); out << "(assert (and true\n"; for (clause* c : m_clauses) { display_smt2(out, *c) << "\n"; } out << "))\n" << std::endl; return out; } }; solver::solver(reslimit& rlim, params_ref const & p, bool incremental) { m_ctx = alloc(ctx, rlim, p); m_imp = alloc(imp, *this, *m_ctx, incremental); } solver::solver(ctx& ctx) { m_ctx = nullptr; m_imp = alloc(imp, *this, ctx, false); } solver::~solver() { dealloc(m_imp); dealloc(m_ctx); } lbool solver::check() { return m_imp->check(); } lbool solver::check(literal_vector& assumptions) { return m_imp->check(assumptions); } void solver::get_core(vector& assumptions) { return m_imp->get_core(assumptions); } void solver::reset() { m_imp->reset(); } void solver::updt_params(params_ref const & p) { m_imp->updt_params(p); } void solver::collect_param_descrs(param_descrs & d) { algebraic_numbers::manager::collect_param_descrs(d); nlsat_params::collect_param_descrs(d); } unsynch_mpq_manager & solver::qm() { return m_imp->m_qm; } anum_manager & solver::am() { return m_imp->m_am; } pmanager & solver::pm() { return m_imp->m_pm; } void solver::set_display_var(display_var_proc const & proc) { m_imp->m_display_var.m_proc = &proc; } void solver::set_display_assumption(display_assumption_proc const& proc) { m_imp->m_display_assumption = &proc; } unsigned solver::num_vars() const { return m_imp->num_vars(); } bool solver::is_int(var x) const { return m_imp->is_int(x); } bool_var solver::mk_bool_var() { return m_imp->mk_bool_var(); } literal solver::mk_true() { return literal(0, false); } atom * solver::bool_var2atom(bool_var b) { return m_imp->m_atoms[b]; } void solver::vars(literal l, var_vector& vs) { m_imp->vars(l, vs); } atom_vector const& solver::get_atoms() { return m_imp->m_atoms; } atom_vector const& solver::get_var2eq() { return m_imp->m_var2eq; } evaluator& solver::get_evaluator() { return m_imp->m_evaluator; } explain& solver::get_explain() { return m_imp->m_explain; } void solver::reorder(unsigned sz, var const* p) { m_imp->reorder(sz, p); } void solver::restore_order() { m_imp->restore_order(); } void solver::set_rvalues(assignment const& as) { m_imp->m_assignment.copy(as); } void solver::get_rvalues(assignment& as) { as.copy(m_imp->m_assignment); } void solver::get_bvalues(svector const& bvars, svector& vs) { vs.reset(); for (bool_var b : bvars) { vs.reserve(b + 1, l_undef); if (!m_imp->m_atoms[b]) { vs[b] = m_imp->m_bvalues[b]; } } TRACE("nlsat", display(tout);); } void solver::set_bvalues(svector const& vs) { TRACE("nlsat", display(tout);); for (bool_var b = 0; b < vs.size(); ++b) { if (vs[b] != l_undef) { m_imp->m_bvalues[b] = vs[b]; SASSERT(!m_imp->m_atoms[b]); } } #if 0 m_imp->m_bvalues.reset(); m_imp->m_bvalues.append(vs); m_imp->m_bvalues.resize(m_imp->m_atoms.size(), l_undef); for (unsigned i = 0; i < m_imp->m_atoms.size(); ++i) { atom* a = m_imp->m_atoms[i]; SASSERT(!a); if (a) { m_imp->m_bvalues[i] = to_lbool(m_imp->m_evaluator.eval(a, false)); } } #endif TRACE("nlsat", display(tout);); } var solver::mk_var(bool is_int) { return m_imp->mk_var(is_int); } bool_var solver::mk_ineq_atom(atom::kind k, unsigned sz, poly * const * ps, bool const * is_even) { return m_imp->mk_ineq_atom(k, sz, ps, is_even); } literal solver::mk_ineq_literal(atom::kind k, unsigned sz, poly * const * ps, bool const * is_even) { return m_imp->mk_ineq_literal(k, sz, ps, is_even); } bool_var solver::mk_root_atom(atom::kind k, var x, unsigned i, poly * p) { return m_imp->mk_root_atom(k, x, i, p); } void solver::inc_ref(bool_var b) { m_imp->inc_ref(b); } void solver::dec_ref(bool_var b) { m_imp->dec_ref(b); } void solver::mk_clause(unsigned num_lits, literal * lits, assumption a) { return m_imp->mk_clause(num_lits, lits, a); } std::ostream& solver::display(std::ostream & out) const { return m_imp->display(out); } std::ostream& solver::display(std::ostream & out, literal l) const { return m_imp->display(out, l); } std::ostream& solver::display(std::ostream & out, unsigned n, literal const* ls) const { for (unsigned i = 0; i < n; ++i) { display(out, ls[i]); out << "; "; } return out; } std::ostream& solver::display(std::ostream & out, literal_vector const& ls) const { return display(out, ls.size(), ls.c_ptr()); } std::ostream& solver::display_smt2(std::ostream & out, literal l) const { return m_imp->display_smt2(out, l); } std::ostream& solver::display_smt2(std::ostream & out, unsigned n, literal const* ls) const { for (unsigned i = 0; i < n; ++i) { display_smt2(out, ls[i]); out << " "; } return out; } std::ostream& solver::display_smt2(std::ostream & out, literal_vector const& ls) const { return display_smt2(out, ls.size(), ls.c_ptr()); } std::ostream& solver::display(std::ostream & out, var x) const { return m_imp->m_display_var(out, x); } std::ostream& solver::display(std::ostream & out, atom const& a) const { return m_imp->display(out, a, m_imp->m_display_var); } display_var_proc const & solver::display_proc() const { return m_imp->m_display_var; } anum const & solver::value(var x) const { if (m_imp->m_assignment.is_assigned(x)) return m_imp->m_assignment.value(x); return m_imp->m_zero; } lbool solver::bvalue(bool_var b) const { return m_imp->m_bvalues[b]; } lbool solver::value(literal l) const { return m_imp->value(l); } bool solver::is_interpreted(bool_var b) const { return m_imp->m_atoms[b] != 0; } void solver::reset_statistics() { return m_imp->reset_statistics(); } void solver::collect_statistics(statistics & st) { return m_imp->collect_statistics(st); } }; z3-z3-4.8.7/src/nlsat/nlsat_solver.h000066400000000000000000000152721356505360400172320ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: nlsat_solver.h Abstract: Nonlinear arithmetic satisfiability procedure. The procedure is complete for nonlinear real arithmetic, but it also has limited support for integers. Author: Leonardo de Moura (leonardo) 2012-01-02. Revision History: --*/ #ifndef NLSAT_SOLVER_H_ #define NLSAT_SOLVER_H_ #include "nlsat/nlsat_types.h" #include "util/params.h" #include "util/statistics.h" #include "util/rlimit.h" namespace nlsat { class evaluator; class explain; class display_assumption_proc { public: virtual std::ostream& operator()(std::ostream& out, assumption a) const = 0; }; class solver { struct imp; struct ctx; imp * m_imp; ctx * m_ctx; solver(ctx& c); public: solver(reslimit& rlim, params_ref const & p, bool incremental); ~solver(); /** \brief Return reference to rational manager. */ unsynch_mpq_manager & qm(); /** \brief Return reference to algebraic number manager. */ anum_manager & am(); /** \brief Return a reference to the polynomial manager used by the solver. */ pmanager & pm(); void set_display_var(display_var_proc const & proc); void set_display_assumption(display_assumption_proc const& proc); // ----------------------- // // Variable, Atoms, Clauses & Assumption creation // // ----------------------- /** \brief Create a fresh boolean variable that is not associated with any nonlinear arithmetic atom. */ bool_var mk_bool_var(); literal mk_true(); /** \brief Create a real/integer variable. */ var mk_var(bool is_int); /** \brief Create an atom of the form: p=0, p<0, p>0 Where p = ps[0]^e[0]*...*ps[sz-1]^e[sz-1] e[i] = 1 if is_even[i] is false e[i] = 2 if is_even[i] is true \pre sz > 0 */ bool_var mk_ineq_atom(atom::kind k, unsigned sz, poly * const * ps, bool const * is_even); /** \brief Create a literal for the p=0, p<0, p>0. Where p = ps[0]^e[0]*...*ps[sz-1]^e[sz-1] for sz > 0 p = 1 for sz = 0 e[i] = 1 if is_even[i] is false e[i] = 2 if is_even[i] is true */ literal mk_ineq_literal(atom::kind k, unsigned sz, poly * const * ps, bool const * is_even); /** \brief Create an atom of the form: x=root[i](p), xroot[i](p) */ bool_var mk_root_atom(atom::kind k, var x, unsigned i, poly * p); void inc_ref(bool_var b); void inc_ref(literal l) { inc_ref(l.var()); } void dec_ref(bool_var b); void dec_ref(literal l) { dec_ref(l.var()); } /** \brief Create a new clause. */ void mk_clause(unsigned num_lits, literal * lits, assumption a = nullptr); // ----------------------- // // Basic // // ----------------------- /** \brief Return the number of Boolean variables. */ unsigned num_bool_vars() const; /** \brief Get atom associated with Boolean variable. Return 0 if there is none. */ atom * bool_var2atom(bool_var b); /** \brief extract free variables from literal. */ void vars(literal l, var_vector& vs); /** \brief provide access to atoms. Used by explain. */ atom_vector const& get_atoms(); /** \brief Access var -> asserted equality. */ atom_vector const& get_var2eq(); /** \brief expose evaluator. */ evaluator& get_evaluator(); /** \brief Access explanation module. */ explain& get_explain(); /** \brief Access assignments to variables. */ void get_rvalues(assignment& as); void set_rvalues(assignment const& as); void get_bvalues(svector const& bvars, svector& vs); void set_bvalues(svector const& vs); /** \brief reorder variables. */ void reorder(unsigned sz, var const* permutation); void restore_order(); /** \brief Return number of integer/real variables */ unsigned num_vars() const; bool is_int(var x) const; // ----------------------- // // Search // // ----------------------- lbool check(); lbool check(literal_vector& assumptions); // ----------------------- // // Model // // ----------------------- anum const & value(var x) const; lbool bvalue(bool_var b) const; bool is_interpreted(bool_var b) const; lbool value(literal l) const; // ----------------------- // // Core // // ----------------------- void get_core(vector& deps); // ----------------------- // // Misc // // ----------------------- void updt_params(params_ref const & p); static void collect_param_descrs(param_descrs & d); void reset(); void collect_statistics(statistics & st); void reset_statistics(); void display_status(std::ostream & out) const; // ----------------------- // // Pretty printing // // ----------------------- /** \brief Display solver's state. */ std::ostream& display(std::ostream & out) const; /** \brief Display literal */ std::ostream& display(std::ostream & out, literal l) const; std::ostream& display(std::ostream & out, unsigned n, literal const* ls) const; std::ostream& display(std::ostream & out, literal_vector const& ls) const; std::ostream& display(std::ostream & out, atom const& a) const; std::ostream& display_smt2(std::ostream & out, literal l) const; std::ostream& display_smt2(std::ostream & out, unsigned n, literal const* ls) const; std::ostream& display_smt2(std::ostream & out, literal_vector const& ls) const; /** \brief Display variable */ std::ostream& display(std::ostream & out, var x) const; display_var_proc const & display_proc() const; }; }; #endif z3-z3-4.8.7/src/nlsat/nlsat_types.cpp000066400000000000000000000035601356505360400174140ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: nlsat_types.cpp Abstract: Basic types used in the nonlinear arithmetic satisfiability procedure. Author: Leonardo de Moura (leonardo) 2012-01-02. Revision History: --*/ #include "nlsat/nlsat_types.h" #include "util/debug.h" #include "util/hash.h" #include "math/polynomial/polynomial.h" namespace nlsat { ineq_atom::ineq_atom(kind k, unsigned sz, poly * const * ps, bool const * is_even, var max_var): atom(k, max_var), m_size(sz) { for (unsigned i = 0; i < m_size; i++) { m_ps[i] = TAG(poly *, ps[i], is_even[i] ? 1 : 0); } SASSERT(is_ineq_atom()); } unsigned ineq_atom::hash_proc::operator()(ineq_atom const * a) const { return get_composite_hash(a, a->m_size); } bool ineq_atom::eq_proc::operator()(ineq_atom const * a1, ineq_atom const * a2) const { if (a1->m_size != a2->m_size || a1->m_kind != a2->m_kind) return false; unsigned sz = a1->m_size; for (unsigned i = 0; i < sz; i++) { if (a1->m_ps[i] != a2->m_ps[i]) return false; } return true; } root_atom::root_atom(kind k, var x, unsigned i, poly * p): atom(k, x), m_x(x), m_i(i), m_p(p) { SASSERT(is_root_atom()); } unsigned root_atom::hash_proc::operator()(root_atom const * a) const { unsigned _a = a->m_x; unsigned _b = ((a->m_i << 2) | (a->m_kind)); unsigned _c = polynomial::manager::id(a->m_p); mix(_a, _b, _c); return _c; } bool root_atom::eq_proc::operator()(root_atom const * a1, root_atom const * a2) const { return a1->m_kind == a2->m_kind && a1->m_x == a2->m_x && a1->m_i == a2->m_i && a1->m_p == a2->m_p; } }; z3-z3-4.8.7/src/nlsat/nlsat_types.h000066400000000000000000000134771356505360400170710ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: nlsat_types.h Abstract: Basic types used in the nonlinear arithmetic satisfiability procedure. Author: Leonardo de Moura (leonardo) 2012-01-02. Revision History: --*/ #ifndef NLSAT_TYPES_H_ #define NLSAT_TYPES_H_ #include "math/polynomial/polynomial.h" #include "util/buffer.h" #include "sat/sat_types.h" #include "util/z3_exception.h" namespace algebraic_numbers { class anum; class manager; }; namespace nlsat { #define NLSAT_VB_LVL 10 typedef void * assumption; typedef void * assumption_set; typedef sat::bool_var bool_var; typedef sat::bool_var_vector bool_var_vector; const bool_var null_bool_var = sat::null_bool_var; typedef sat::literal literal; const literal null_literal = sat::null_literal; typedef sat::literal_vector literal_vector; inline literal to_literal(unsigned x) { return sat::to_literal(x); } typedef polynomial::var var; typedef polynomial::var_vector var_vector; typedef polynomial::manager pmanager; typedef polynomial::polynomial poly; typedef polynomial::monomial monomial; typedef polynomial::numeral numeral; const var null_var = polynomial::null_var; const var true_bool_var = 0; const literal true_literal(true_bool_var, false); const literal false_literal(true_bool_var, true); typedef polynomial::display_var_proc display_var_proc; class atom; class ineq_atom; // atoms of the form: p=0, p>0, p<0, where p is a product of polynomials class root_atom; // atoms of the form: x=root[i](p), xroot[i](p), where x is a variable and p a polynomial. class clause; class solver; class atom { public: enum kind { EQ=0, LT, GT, ROOT_EQ=10, ROOT_LT, ROOT_GT, ROOT_LE, ROOT_GE }; static kind flip(kind k) { SASSERT(k == EQ || k == LT || k == GT); if (k == LT) return GT; if (k == GT) return LT; return EQ; } protected: friend class solver; kind m_kind; unsigned m_ref_count; bool_var m_bool_var; var m_max_var; public: atom(kind k, var max_var):m_kind(k), m_ref_count(0), m_bool_var(null_bool_var), m_max_var(max_var) {} bool is_eq() const { return m_kind == EQ || m_kind == ROOT_EQ; } bool is_ineq_atom() const { return m_kind <= GT; } bool is_root_atom() const { return m_kind >= ROOT_EQ; } kind get_kind() const { return m_kind; } bool_var bvar() const { return m_bool_var; } var max_var() const { return m_max_var; } unsigned ref_count() const { return m_ref_count; } void inc_ref() { m_ref_count++; } void dec_ref() { SASSERT(m_ref_count > 0); m_ref_count--; } }; typedef ptr_vector atom_vector; class ineq_atom : public atom { friend class solver; unsigned m_size; poly * m_ps[0]; ineq_atom(kind k, unsigned sz, poly * const * ps, bool const * is_even, var max_var); static unsigned get_obj_size(unsigned sz) { return sizeof(ineq_atom) + sizeof(poly*)*sz; } public: unsigned size() const { return m_size; } poly * p(unsigned i) const { SASSERT(i < size()); return UNTAG(poly*, m_ps[i]); } // Return true if i-th factor has odd degree bool is_odd(unsigned i) const { SASSERT(i < size()); return GET_TAG(m_ps[i]) == 0; } bool is_even(unsigned i) const { return !is_odd(i); } struct khasher { unsigned operator()(ineq_atom const * a) const { return a->m_kind; } }; struct chasher { unsigned operator()(ineq_atom const * a, unsigned idx) const { return polynomial::manager::id(a->p(idx)); } }; struct hash_proc { unsigned operator()(ineq_atom const * a) const; }; struct eq_proc { bool operator()(ineq_atom const * a1, ineq_atom const * a2) const; }; }; inline std::ostream& operator<<(std::ostream& out, atom::kind k) { switch (k) { case atom::EQ: return out << "="; case atom::LT: return out << "<"; case atom::GT: return out << ">"; case atom::ROOT_EQ: return out << "= root"; case atom::ROOT_LT: return out << "< root"; case atom::ROOT_LE: return out << "<= root"; case atom::ROOT_GT: return out << "> root"; case atom::ROOT_GE: return out << ">= root"; default: return out << (int)k; } return out; } class root_atom : public atom { friend class solver; var m_x; unsigned m_i; poly * m_p; root_atom(kind k, var x, unsigned i, poly * p); public: var x() const { return m_x; } unsigned i() const { return m_i; } poly * p() const { return m_p; } struct hash_proc { unsigned operator()(root_atom const * a) const; }; struct eq_proc { bool operator()(root_atom const * a1, root_atom const * a2) const; }; }; inline ineq_atom * to_ineq_atom(atom * a) { SASSERT(a->is_ineq_atom()); return static_cast(a); } inline root_atom * to_root_atom(atom * a) { SASSERT(a->is_root_atom()); return static_cast(a); } inline ineq_atom const * to_ineq_atom(atom const * a) { SASSERT(a->is_ineq_atom()); return static_cast(a); } inline root_atom const * to_root_atom(atom const * a) { SASSERT(a->is_root_atom()); return static_cast(a); } typedef algebraic_numbers::anum anum; typedef algebraic_numbers::manager anum_manager; typedef default_exception solver_exception; class assignment; inline int normalize_sign(int s) { if (s < 0) return -1; if (s == 0) return 0; return 1; } }; #endif z3-z3-4.8.7/src/nlsat/tactic/000077500000000000000000000000001356505360400156065ustar00rootroot00000000000000z3-z3-4.8.7/src/nlsat/tactic/CMakeLists.txt000066400000000000000000000003561356505360400203520ustar00rootroot00000000000000z3_add_component(nlsat_tactic SOURCES goal2nlsat.cpp nlsat_tactic.cpp qfnra_nlsat_tactic.cpp COMPONENT_DEPENDENCIES arith_tactics nlsat sat_tactic TACTIC_HEADERS nlsat_tactic.h qfnra_nlsat_tactic.h ) z3-z3-4.8.7/src/nlsat/tactic/goal2nlsat.cpp000066400000000000000000000337431356505360400203720ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: goal2nlsat.cpp Abstract: "Compile" a goal into the nonlinear arithmetic engine. Non-arithmetic atoms are "abstracted" into boolean variables. Non-supported terms are "abstracted" into variables. The mappings can be used to convert back the state of the engine into a goal. Author: Leonardo (leonardo) 2012-01-02 Notes: --*/ #include "nlsat/tactic/goal2nlsat.h" #include "tactic/goal.h" #include "tactic/goal_util.h" #include "nlsat/nlsat_solver.h" #include "ast/expr2polynomial.h" #include "ast/expr2var.h" #include "ast/arith_decl_plugin.h" #include "tactic/tactic.h" #include "ast/ast_pp.h" #include "math/polynomial/polynomial.h" #include "math/polynomial/algebraic_numbers.h" struct goal2nlsat::imp { struct nlsat_expr2polynomial : public expr2polynomial { nlsat::solver & m_solver; nlsat_expr2polynomial(nlsat::solver & s, ast_manager & m, polynomial::manager & pm, expr2var * e2v): expr2polynomial(m, pm, e2v), m_solver(s) { } bool is_int(polynomial::var x) const override { return m_solver.is_int(x); } polynomial::var mk_var(bool is_int) override { return m_solver.mk_var(is_int); } }; ast_manager & m; nlsat::solver & m_solver; polynomial::manager & m_pm; unsynch_mpq_manager & m_qm; arith_util m_util; expr2var & m_a2b; expr2var & m_t2x; nlsat_expr2polynomial m_expr2poly; polynomial::factor_params m_fparams; unsigned long long m_max_memory; bool m_factor; imp(ast_manager & _m, params_ref const & p, nlsat::solver & s, expr2var & a2b, expr2var & t2x): m(_m), m_solver(s), m_pm(s.pm()), m_qm(s.qm()), m_util(m), m_a2b(a2b), m_t2x(t2x), m_expr2poly(m_solver, m, m_solver.pm(), &m_t2x) { updt_params(p); } void updt_params(params_ref const & p) { m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); m_factor = p.get_bool("factor", true); m_fparams.updt_params(p); } nlsat::atom::kind flip(nlsat::atom::kind k) { switch (k) { case nlsat::atom::EQ: return k; case nlsat::atom::LT: return nlsat::atom::GT; case nlsat::atom::GT: return nlsat::atom::LT; default: UNREACHABLE(); return k; } } nlsat::bool_var factor_atom(polynomial::polynomial * p, nlsat::atom::kind k) { sbuffer is_even; ptr_buffer ps; polynomial::factors fs(m_pm); m_pm.factor(p, fs, m_fparams); TRACE("goal2nlsat_bug", tout << "factors:\n" << fs << "\n";); SASSERT(fs.distinct_factors() > 0); for (unsigned i = 0; i < fs.distinct_factors(); i++) { ps.push_back(fs[i]); is_even.push_back(fs.get_degree(i) % 2 == 0); } if (m_qm.is_neg(fs.get_constant())) k = flip(k); return m_solver.mk_ineq_atom(k, ps.size(), ps.c_ptr(), is_even.c_ptr()); } nlsat::literal process_atom(app * f, nlsat::atom::kind k) { SASSERT(f->get_num_args() == 2); expr * lhs = f->get_arg(0); expr * rhs = f->get_arg(1); polynomial_ref p1(m_pm); polynomial_ref p2(m_pm); scoped_mpz d1(m_qm); scoped_mpz d2(m_qm); m_expr2poly.to_polynomial(lhs, p1, d1); m_expr2poly.to_polynomial(rhs, p2, d2); scoped_mpz lcm(m_qm); m_qm.lcm(d1, d2, lcm); m_qm.div(lcm, d1, d1); m_qm.div(lcm, d2, d2); m_qm.neg(d2); polynomial_ref p(m_pm); p = m_pm.addmul(d1, m_pm.mk_unit(), p1, d2, m_pm.mk_unit(), p2); TRACE("goal2nlsat_bug", tout << mk_pp(f, m) << " p: " << p << "\nk: " << k << "\n";); if (is_const(p)) { int sign; if (is_zero(p)) sign = 0; else sign = m_qm.is_pos(m_pm.coeff(p, 0)) ? 1 : -1; switch (k) { case nlsat::atom::EQ: return sign == 0 ? nlsat::true_literal : nlsat::false_literal; case nlsat::atom::LT: return sign < 0 ? nlsat::true_literal : nlsat::false_literal; case nlsat::atom::GT: return sign > 0 ? nlsat::true_literal : nlsat::false_literal; default: UNREACHABLE(); return nlsat::true_literal; } } if (m_factor) { return nlsat::literal(factor_atom(p, k), false); } else { bool is_even = false; polynomial::polynomial * _p = p.get(); return nlsat::literal(m_solver.mk_ineq_atom(k, 1, &_p, &is_even), false); } } nlsat::literal process_eq(app * f) { return process_atom(f, nlsat::atom::EQ); } nlsat::literal process_le(app * f) { return ~process_atom(f, nlsat::atom::GT); } nlsat::literal process_ge(app * f) { return ~process_atom(f, nlsat::atom::LT); } // everything else is compiled as a boolean variable nlsat::bool_var process_bvar(expr * f) { if (m_a2b.is_var(f)) { return static_cast(m_a2b.to_var(f)); } else { nlsat::bool_var b = m_solver.mk_bool_var(); m_a2b.insert(f, b); return b; } } nlsat::literal process_atom(expr * f) { if (m.is_eq(f)) { if (m_util.is_int_real(to_app(f)->get_arg(0))) return process_eq(to_app(f)); else return nlsat::literal(process_bvar(f), false); } else if (m_util.is_le(f)) { return process_le(to_app(f)); } else if (m_util.is_ge(f)) { return process_ge(to_app(f)); } else if (is_app(f)) { if (to_app(f)->get_family_id() == m.get_basic_family_id()) { switch (to_app(f)->get_decl_kind()) { case OP_TRUE: case OP_FALSE: TRACE("goal2nlsat", tout << "f: " << mk_pp(f, m) << "\n";); throw tactic_exception("apply simplify before applying nlsat"); case OP_AND: case OP_OR: case OP_XOR: case OP_NOT: case OP_IMPLIES: throw tactic_exception("convert goal into cnf before applying nlsat"); case OP_DISTINCT: throw tactic_exception("eliminate distinct operator (use tactic '(using-params simplify :blast-distinct true)') before applying nlsat"); default: UNREACHABLE(); return nlsat::literal(nlsat::null_bool_var, false); } } else if (to_app(f)->get_family_id() == m_util.get_family_id()) { throw tactic_exception("apply purify-arith before applying nlsat"); } else { return nlsat::literal(process_bvar(f), false); } } else { SASSERT(is_quantifier(f)); return nlsat::literal(process_bvar(f), false); } } nlsat::literal process_literal(expr * f) { bool neg = false; while (m.is_not(f, f)) neg = !neg; nlsat::literal l = process_atom(f); if (neg) l.neg(); return l; } void process(expr * f, expr_dependency * dep) { 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; } sbuffer ls; for (unsigned i = 0; i < num_lits; i++) { ls.push_back(process_literal(lits[i])); } m_solver.mk_clause(ls.size(), ls.c_ptr(), dep); } void operator()(goal const & g) { TRACE("goal2nlsat", g.display(tout);); if (has_term_ite(g)) throw tactic_exception("eliminate term-ite before applying nlsat"); unsigned sz = g.size(); for (unsigned i = 0; i < sz; i++) { process(g.form(i), g.dep(i)); } } }; struct goal2nlsat::scoped_set_imp { goal2nlsat & m_owner; scoped_set_imp(goal2nlsat & o, imp & i):m_owner(o) { m_owner.m_imp = &i; } ~scoped_set_imp() { m_owner.m_imp = nullptr; } }; goal2nlsat::goal2nlsat() { m_imp = nullptr; } goal2nlsat::~goal2nlsat() { SASSERT(m_imp == 0); } void goal2nlsat::collect_param_descrs(param_descrs & r) { insert_max_memory(r); r.insert("factor", CPK_BOOL, "(default: true) factor polynomials."); polynomial::factor_params::get_param_descrs(r); } void goal2nlsat::operator()(goal const & g, params_ref const & p, nlsat::solver & s, expr2var & a2b, expr2var & t2x) { imp local_imp(g.m(), p, s, a2b, t2x); scoped_set_imp setter(*this, local_imp); local_imp(g); } struct nlsat2goal::imp { ast_manager& m; arith_util a; u_map const* m_x2t; public: imp(ast_manager& m):m(m),a(m) {} expr_ref operator()(nlsat::solver& s, u_map const& b2a, u_map const& x2t, nlsat::literal l) { m_x2t = &x2t; expr_ref result(m); expr* t; if (b2a.find(l.var(), t)) { result = t; } else { nlsat::atom const* at = s.bool_var2atom(l.var()); SASSERT(at != 0); if (at->is_ineq_atom()) { nlsat::ineq_atom const* ia = to_ineq_atom(at); unsigned sz = ia->size(); expr_ref_vector ps(m); bool is_int = true; for (unsigned i = 0; is_int && i < sz; ++i) { is_int = poly_is_int(ia->p(i)); } for (unsigned i = 0; i < sz; ++i) { polynomial::polynomial* p = ia->p(i); expr_ref t = poly2expr(s, p, is_int); if (ia->is_even(i)) { t = a.mk_power(t, a.mk_numeral(rational(2), a.is_int(t))); } ps.push_back(t); } result = a.mk_mul_simplify(ps); expr_ref zero(m); zero = a.mk_numeral(rational(0), a.is_int(result)); switch (ia->get_kind()) { case nlsat::atom::EQ: result = m.mk_eq(result, zero); break; case nlsat::atom::LT: if (l.sign()) { l.neg(); result = a.mk_ge(result, zero); } else { result = a.mk_lt(result, zero); } break; case nlsat::atom::GT: if (l.sign()) { l.neg(); result = a.mk_le(result, zero); } else { result = a.mk_gt(result, zero); } break; default: UNREACHABLE(); } } else { //nlsat::root_atom const* ra = nlsat::to_root_atom(at); //ra->i(); //expr_ref p = poly2expr(s, ra->p()); //expr* x = m_x2t->find(ra->x()); std::ostringstream strm; s.display(strm, l.sign()?~l:l); result = m.mk_const(symbol(strm.str().c_str()), m.mk_bool_sort()); } } if (l.sign()) { result = m.mk_not(result); } return result; } expr_ref poly2expr(nlsat::solver& s, polynomial::polynomial* p, bool is_int) { expr_ref result(m); unsigned sz = polynomial::manager::size(p); expr_ref_vector args(m); for (unsigned i = 0; i < sz; ++i) { args.push_back(mono2expr(s, polynomial::manager::coeff(p, i), polynomial::manager::get_monomial(p, i), is_int)); } result = a.mk_add_simplify(args); return result; } expr_ref mono2expr(nlsat::solver& s, polynomial::numeral const& c, polynomial::monomial* mon, bool is_int) { expr_ref result(m); expr_ref_vector args(m); unsigned sz = polynomial::manager::size(mon); for (unsigned i = 0; i < sz; ++i) { unsigned d = polynomial::manager::degree(mon, i); expr* t = m_x2t->find(polynomial::manager::get_var(mon, i)); SASSERT(d >= 1); if (d == 1) { args.push_back(t); } else { args.push_back(a.mk_power(t, a.mk_numeral(rational(d), a.is_int(t)))); } } if (!s.pm().m().is_one(c)) { args.push_back(a.mk_numeral(c, is_int)); } result = a.mk_mul_simplify(args); return result; } bool poly_is_int(polynomial::polynomial* p) { bool is_int = true; unsigned sz = polynomial::manager::size(p); for (unsigned i = 0; is_int && i < sz; ++i) { is_int = mono_is_int(polynomial::manager::get_monomial(p, i)); } return is_int; } bool mono_is_int(polynomial::monomial* mon) { bool is_int = true; unsigned sz = polynomial::manager::size(mon); for (unsigned i = 0; is_int && i < sz; ++i) { is_int = a.is_int(m_x2t->find(polynomial::manager::get_var(mon, i))); } return is_int; } }; nlsat2goal::nlsat2goal(ast_manager& m) { m_imp = alloc(imp, m); } nlsat2goal::~nlsat2goal() { dealloc(m_imp); } expr_ref nlsat2goal::operator()(nlsat::solver& s, u_map const& b2a, u_map const& x2t, nlsat::literal l) { return (*m_imp)(s, b2a, x2t, l); } z3-z3-4.8.7/src/nlsat/tactic/goal2nlsat.h000066400000000000000000000030561356505360400200310ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: goal2nlsat.h Abstract: "Compile" a goal into the nonlinear arithmetic engine. Non-arithmetic atoms are "abstracted" into boolean variables. Non-supported terms are "abstracted" into variables. The mappings can be used to convert back the state of the engine into a goal. Author: Leonardo (leonardo) 2012-01-02 Notes: --*/ #ifndef GOAL2NLSAT_H_ #define GOAL2NLSAT_H_ #include "nlsat/nlsat_types.h" #include "tactic/model_converter.h" class goal; class expr2var; class goal2nlsat { struct imp; imp * m_imp; struct scoped_set_imp; public: goal2nlsat(); ~goal2nlsat(); static void collect_param_descrs(param_descrs & r); /** \brief "Compile" the goal into the given nlsat engine. Store a mapping from atoms to boolean variables into a2b. Store a mapping from terms into arithmetic variables into t2x. \remark a2b and t2x m don't need to be empty. The definitions there are reused. The input is expected to be in CNF */ void operator()(goal const & g, params_ref const & p, nlsat::solver & s, expr2var & a2b, expr2var & t2x); }; class nlsat2goal { struct imp; imp * m_imp; public: nlsat2goal(ast_manager& m); ~nlsat2goal(); static void collect_param_descrs(param_descrs & r); /** \brief Translate a literal into a formula. */ expr_ref operator()(nlsat::solver& s, u_map const& b2a, u_map const& x2t, nlsat::literal l); }; #endif z3-z3-4.8.7/src/nlsat/tactic/nlsat_tactic.cpp000066400000000000000000000211521356505360400207630ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: nlsat_tactic.cpp Abstract: Tactic for using nonlinear procedure. Author: Leonardo (leonardo) 2012-01-02 Notes: --*/ #include "tactic/tactical.h" #include "nlsat/tactic/goal2nlsat.h" #include "nlsat/nlsat_solver.h" #include "model/model.h" #include "ast/expr2var.h" #include "ast/arith_decl_plugin.h" #include "ast/ast_smt2_pp.h" #include "util/z3_exception.h" #include "math/polynomial/algebraic_numbers.h" #include "ast/ast_pp.h" class nlsat_tactic : public tactic { struct expr_display_var_proc : public nlsat::display_var_proc { ast_manager & m; expr_ref_vector m_var2expr; expr_display_var_proc(ast_manager & _m):m(_m), m_var2expr(_m) {} std::ostream& operator()(std::ostream & out, nlsat::var x) const override { if (x < m_var2expr.size()) return out << mk_ismt2_pp(m_var2expr.get(x), m); else return out << "x!" << x; } }; struct imp { ast_manager & m; params_ref m_params; expr_display_var_proc m_display_var; nlsat::solver m_solver; goal2nlsat m_g2nl; imp(ast_manager & _m, params_ref const & p): m(_m), m_params(p), m_display_var(_m), m_solver(m.limit(), p, false) { } void updt_params(params_ref const & p) { m_params = p; m_solver.updt_params(p); } bool contains_unsupported(expr_ref_vector & b2a, expr_ref_vector & x2t) { for (unsigned x = 0; x < x2t.size(); x++) { if (!is_uninterp_const(x2t.get(x))) { TRACE("unsupported", tout << "unsupported atom:\n" << mk_ismt2_pp(x2t.get(x), m) << "\n";); return true; } } for (unsigned b = 0; b < b2a.size(); b++) { expr * a = b2a.get(b); if (a == nullptr) continue; if (is_uninterp_const(a)) continue; if (m_solver.is_interpreted(b)) continue; // arithmetic atom TRACE("unsupported", tout << "unsupported atom:\n" << mk_ismt2_pp(a, m) << "\n";); return true; // unsupported } return false; } bool eval_model(model& model, goal& g) { unsigned sz = g.size(); for (unsigned i = 0; i < sz; i++) { if (!model.is_true(g.form(i))) { TRACE("nlsat", tout << mk_pp(g.form(i), m) << " -> " << model(g.form(i)) << "\n";); IF_VERBOSE(0, verbose_stream() << mk_pp(g.form(i), m) << " -> " << model(g.form(i)) << "\n";); IF_VERBOSE(1, verbose_stream() << model << "\n"); IF_VERBOSE(1, m_solver.display(verbose_stream())); return false; } } return true; } // Return false if nlsat assigned noninteger value to an integer variable. bool mk_model(goal & g, expr_ref_vector & b2a, expr_ref_vector & x2t, model_converter_ref & mc) { bool ok = true; model_ref md = alloc(model, m); arith_util util(m); for (unsigned x = 0; x < x2t.size(); x++) { expr * t = x2t.get(x); if (!is_uninterp_const(t)) continue; expr * v; try { v = util.mk_numeral(m_solver.value(x), util.is_int(t)); } catch (z3_error & ex) { throw ex; } catch (z3_exception &) { v = util.mk_to_int(util.mk_numeral(m_solver.value(x), false)); ok = false; } md->register_decl(to_app(t)->get_decl(), v); } for (unsigned b = 0; b < b2a.size(); b++) { expr * a = b2a.get(b); if (a == nullptr || !is_uninterp_const(a)) continue; lbool val = m_solver.bvalue(b); if (val == l_undef) continue; // don't care md->register_decl(to_app(a)->get_decl(), val == l_true ? m.mk_true() : m.mk_false()); } DEBUG_CODE(eval_model(*md.get(), g);); // VERIFY(eval_model(*md.get(), g)); mc = model2model_converter(md.get()); return ok; } void operator()(goal_ref const & g, goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); tactic_report report("nlsat", *g); if (g->is_decided()) { result.push_back(g.get()); return; } fail_if_proof_generation("nlsat", g); TRACE("nlsat", g->display(tout);); expr2var a2b(m); expr2var t2x(m); m_g2nl(*g, m_params, m_solver, a2b, t2x); m_display_var.m_var2expr.reset(); t2x.mk_inv(m_display_var.m_var2expr); m_solver.set_display_var(m_display_var); IF_VERBOSE(10000, m_solver.display(verbose_stream())); IF_VERBOSE(10000, g->display(verbose_stream())); lbool st = m_solver.check(); if (st == l_undef) { } else if (st == l_true) { expr_ref_vector x2t(m); expr_ref_vector b2a(m); a2b.mk_inv(b2a); t2x.mk_inv(x2t); if (!contains_unsupported(b2a, x2t)) { // If mk_model is false it means that the model produced by nlsat // assigns noninteger values to integer variables model_converter_ref mc; if (mk_model(*g.get(), b2a, x2t, mc)) { // result goal is trivially SAT g->reset(); g->add(mc.get()); } } } else { expr_dependency* lcore = nullptr; if (g->unsat_core_enabled()) { vector assumptions; m_solver.get_core(assumptions); for (nlsat::assumption a : assumptions) { expr_dependency* d = static_cast(a); lcore = m.mk_join(lcore, d); } } g->assert_expr(m.mk_false(), nullptr, lcore); } g->inc_depth(); result.push_back(g.get()); TRACE("nlsat", g->display(tout);); SASSERT(g->is_well_sorted()); } }; imp * m_imp; params_ref m_params; statistics m_stats; struct scoped_set_imp { nlsat_tactic & m_owner; scoped_set_imp(nlsat_tactic & o, imp & i):m_owner(o) { m_owner.m_imp = &i; } ~scoped_set_imp() { m_owner.m_imp->m_solver.collect_statistics(m_owner.m_stats); m_owner.m_imp = nullptr; } }; public: nlsat_tactic(params_ref const & p): m_params(p) { m_imp = nullptr; } tactic * translate(ast_manager & m) override { return alloc(nlsat_tactic, m_params); } ~nlsat_tactic() override { SASSERT(m_imp == 0); } void updt_params(params_ref const & p) override { m_params = p; } void collect_param_descrs(param_descrs & r) override { goal2nlsat::collect_param_descrs(r); nlsat::solver::collect_param_descrs(r); algebraic_numbers::manager::collect_param_descrs(r); } void operator()(goal_ref const & in, goal_ref_buffer & result) override { try { imp local_imp(in->m(), m_params); scoped_set_imp setter(*this, local_imp); local_imp(in, result); } catch (z3_error & ex) { throw ex; } catch (z3_exception & ex) { // convert all Z3 exceptions into tactic exceptions. throw tactic_exception(ex.msg()); } } void cleanup() override {} void collect_statistics(statistics & st) const override { st.copy(m_stats); } void reset_statistics() override { m_stats.reset(); } }; tactic * mk_nlsat_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(nlsat_tactic, p)); } z3-z3-4.8.7/src/nlsat/tactic/nlsat_tactic.h000066400000000000000000000007671356505360400204410ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: nlsat_tactic.h Abstract: Tactic for using nonlinear procedure. Author: Leonardo (leonardo) 2012-01-02 Notes: --*/ #ifndef NLSAT_TACTIC_H_ #define NLSAT_TACTIC_H_ #include "util/params.h" class ast_manager; class tactic; tactic * mk_nlsat_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC('nlsat', '(try to) solve goal using a nonlinear arithmetic solver.', 'mk_nlsat_tactic(m, p)') */ #endif z3-z3-4.8.7/src/nlsat/tactic/qfnra_nlsat_tactic.cpp000066400000000000000000000044411356505360400221540ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qfnra_nlsat_tactic.h Abstract: Tactic based on nlsat for solving QF_NRA problems Author: Leonardo (leonardo) 2012-01-23 Notes: --*/ #include "tactic/tactical.h" #include "tactic/core/tseitin_cnf_tactic.h" #include "tactic/arith/degree_shift_tactic.h" #include "tactic/arith/purify_arith_tactic.h" #include "nlsat/tactic/nlsat_tactic.h" #include "tactic/arith/factor_tactic.h" #include "tactic/core/simplify_tactic.h" #include "tactic/core/elim_uncnstr_tactic.h" #include "tactic/core/propagate_values_tactic.h" #include "tactic/core/solve_eqs_tactic.h" #include "tactic/core/elim_term_ite_tactic.h" tactic * mk_qfnra_nlsat_tactic(ast_manager & m, params_ref const & p) { params_ref main_p = p; main_p.set_bool("elim_and", true); main_p.set_bool("blast_distinct", true); params_ref purify_p = p; purify_p.set_bool("complete", false); // temporary hack, solver does not support uninterpreted functions for encoding (div0 x) applications. So, we replace it application of this kind with an uninterpreted function symbol. tactic * factor; if (p.get_bool("factor", true)) factor = mk_factor_tactic(m, p); else factor = mk_skip_tactic(); return and_then( mk_report_verbose_tactic("(qfnra-nlsat-tactic)", 10), and_then(using_params(mk_simplify_tactic(m, p), main_p), using_params(mk_purify_arith_tactic(m, p), purify_p), mk_propagate_values_tactic(m, p), mk_solve_eqs_tactic(m, p), mk_elim_uncnstr_tactic(m, p), mk_elim_term_ite_tactic(m, p), using_params(mk_purify_arith_tactic(m, p), purify_p)), and_then(/* mk_degree_shift_tactic(m, p), */ // may affect full dimensionality detection factor, mk_solve_eqs_tactic(m, p), using_params(mk_purify_arith_tactic(m, p), purify_p), using_params(mk_simplify_tactic(m, p), main_p), mk_tseitin_cnf_core_tactic(m, p), using_params(mk_simplify_tactic(m, p), main_p), mk_nlsat_tactic(m, p))); } z3-z3-4.8.7/src/nlsat/tactic/qfnra_nlsat_tactic.h000066400000000000000000000010551356505360400216170ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qfnra_nlsat_tactic.h Abstract: Tactic based on nlsat for solving QF_NRA problems Author: Leonardo (leonardo) 2012-01-23 Notes: --*/ #ifndef QFNRA_NLSAT_TACTIC_H_ #define QFNRA_NLSAT_TACTIC_H_ #include "util/params.h" class ast_manager; class tactic; tactic * mk_qfnra_nlsat_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("qfnra-nlsat", "builtin strategy for solving QF_NRA problems using only nlsat.", "mk_qfnra_nlsat_tactic(m, p)") */ #endif z3-z3-4.8.7/src/opt/000077500000000000000000000000001356505360400140205ustar00rootroot00000000000000z3-z3-4.8.7/src/opt/CMakeLists.txt000066400000000000000000000005311356505360400165570ustar00rootroot00000000000000z3_add_component(opt SOURCES maxlex.cpp maxres.cpp maxsmt.cpp opt_cmds.cpp opt_context.cpp opt_pareto.cpp opt_parse.cpp optsmt.cpp opt_solver.cpp pb_sls.cpp sortmax.cpp wmax.cpp COMPONENT_DEPENDENCIES sat_solver sls_tactic smt smtlogic_tactics PYG_FILES opt_params.pyg ) z3-z3-4.8.7/src/opt/maxlex.cpp000066400000000000000000000132051356505360400160230ustar00rootroot00000000000000/*++ 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); } } z3-z3-4.8.7/src/opt/maxlex.h000066400000000000000000000007111356505360400154660ustar00rootroot00000000000000/*++ 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 z3-z3-4.8.7/src/opt/maxres.cpp000066400000000000000000000720011356505360400160230ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: maxsres.cpp Abstract: MaxRes (weighted) max-sat algorithms: - mus: max-sat algorithm by Nina and Bacchus, AAAI 2014. - mus-mss: based on dual refinement of bounds. MaxRes is a core-guided approach to maxsat. MusMssMaxRes extends the core-guided approach by leveraging both cores and satisfying assignments to make progress towards a maximal satisfying assignment. Given a (minimal) unsatisfiable core for the soft constraints the approach works like max-res. Given a (maximal) satisfying subset of the soft constraints the approach updates the upper bound if the current assignment improves the current best assignmet. Furthermore, take the soft constraints that are complements to the current satisfying subset. E.g, if F are the hard constraints and s1,...,sn, t1,..., tm are the soft clauses and F & s1 & ... & sn is satisfiable, then the complement of of the current satisfying subset is t1, .., tm. Update the hard constraint: F := F & (t1 or ... or tm) Replace t1, .., tm by m-1 new soft clauses: t1 & t2, t3 & (t1 or t2), t4 & (t1 or t2 or t3), ..., tn & (t1 or ... t_{n-1}) Claim: If k of these soft clauses are satisfied, then k+1 of the previous soft clauses are satisfied. If k of these soft clauses are false in the satisfying assignment for the updated F, then k of the original soft clauses are also false under the assignment. In summary: any assignment to the new clauses that satsfies F has the same cost. Claim: If there are no satisfying assignments to F, then the current best assignment is the optimum. Author: Nikolaj Bjorner (nbjorner) 2014-20-7 Notes: --*/ #include "ast/ast_pp.h" #include "ast/pb_decl_plugin.h" #include "ast/ast_util.h" #include "model/model_smt2_pp.h" #include "solver/solver.h" #include "solver/mus.h" #include "sat/sat_solver/inc_sat_solver.h" #include "smt/smt_solver.h" #include "opt/opt_context.h" #include "opt/opt_params.hpp" #include "opt/maxsmt.h" #include "opt/maxres.h" using namespace opt; class maxres : public maxsmt_solver_base { public: enum strategy_t { s_primal, s_primal_dual }; private: struct stats { unsigned m_num_cores; unsigned m_num_cs; stats() { reset(); } void reset() { memset(this, 0, sizeof(*this)); } }; unsigned m_index; stats m_stats; expr_ref_vector m_B; expr_ref_vector m_asms; expr_ref_vector m_defs; obj_map m_asm2weight; expr_ref_vector m_new_core; mus m_mus; expr_ref_vector m_trail; strategy_t m_st; rational m_max_upper; model_ref m_csmodel; unsigned m_correction_set_size; bool m_found_feasible_optimum; bool m_hill_climb; // prefer large weight soft clauses for cores unsigned m_last_index; // last index used during hill-climbing bool m_add_upper_bound_block; // restrict upper bound with constraint unsigned m_max_num_cores; // max number of cores per round. unsigned m_max_core_size; // max core size per round. bool m_maximize_assignment; // maximize assignment to find MCS unsigned m_max_correction_set_size;// maximal set of correction set that is tolerated. bool m_wmax; // Block upper bound using wmax // this option is disabled if SAT core is used. bool m_pivot_on_cs; // prefer smaller correction set to core. bool m_dump_benchmarks; // display benchmarks (into wcnf format) std::string m_trace_id; typedef ptr_vector exprs; public: maxres(maxsat_context& c, unsigned index, weights_t& ws, expr_ref_vector const& soft, strategy_t st): maxsmt_solver_base(c, ws, soft), m_index(index), m_B(m), m_asms(m), m_defs(m), m_new_core(m), m_mus(c.get_solver()), m_trail(m), m_st(st), m_correction_set_size(0), m_found_feasible_optimum(false), m_hill_climb(true), m_last_index(0), m_add_upper_bound_block(false), m_max_num_cores(UINT_MAX), m_max_core_size(3), m_maximize_assignment(false), m_max_correction_set_size(3), m_pivot_on_cs(true) { switch(st) { case s_primal: m_trace_id = "maxres"; break; case s_primal_dual: m_trace_id = "pd-maxres"; break; } } ~maxres() override {} bool is_literal(expr* l) { return is_uninterp_const(l) || (m.is_not(l, l) && is_uninterp_const(l)); } void add(expr_ref_vector const& es) { for (expr* e : es) add(e); } void add(expr* e) { s().assert_expr(e); } void add_soft(expr* e, rational const& w) { TRACE("opt", tout << mk_pp(e, m) << " |-> " << w << "\n";); expr_ref asum(m), fml(m); app_ref cls(m); rational weight(0); if (m_asm2weight.find(e, weight)) { weight += w; m_asm2weight.insert(e, weight); return; } if (is_literal(e)) { asum = e; } else { asum = mk_fresh_bool("soft"); fml = m.mk_iff(asum, e); m_defs.push_back(fml); add(fml); } new_assumption(asum, w); } void new_assumption(expr* e, rational const& w) { IF_VERBOSE(13, verbose_stream() << "new assumption " << mk_pp(e, m) << " " << w << "\n";); m_asm2weight.insert(e, w); m_asms.push_back(e); m_trail.push_back(e); TRACE("opt", tout << "insert: " << mk_pp(e, m) << " : " << w << "\n"; tout << m_asms << " " << "\n"; ); } void trace() { trace_bounds(m_trace_id.c_str()); } lbool mus_solver() { lbool is_sat = l_true; if (!init()) return l_undef; is_sat = init_local(); trace(); if (is_sat != l_true) return is_sat; while (m_lower < m_upper) { TRACE("opt_verbose", s().display(tout << m_asms << "\n") << "\n"; display(tout);); is_sat = check_sat_hill_climb(m_asms); if (m.canceled()) { return l_undef; } switch (is_sat) { case l_true: CTRACE("opt", !m_model->is_true(m_asms), tout << *m_model << "assumptions: "; for (expr* a : m_asms) tout << mk_pp(a, m) << " -> " << (*m_model)(a) << " "; tout << "\n";); SASSERT(m_model->is_true(m_asms)); found_optimum(); return l_true; case l_false: is_sat = process_unsat(); if (is_sat == l_false) { m_lower = m_upper; } if (is_sat == l_undef) { return is_sat; } break; case l_undef: return l_undef; default: break; } } found_optimum(); trace(); return l_true; } lbool primal_dual_solver() { if (!init()) return l_undef; lbool is_sat = init_local(); trace(); exprs cs; if (is_sat != l_true) return is_sat; while (m_lower < m_upper) { is_sat = check_sat_hill_climb(m_asms); if (m.canceled()) { return l_undef; } switch (is_sat) { case l_true: get_current_correction_set(cs); if (cs.empty()) { m_found_feasible_optimum = m_model.get() != nullptr; m_lower = m_upper; } else { process_sat(cs); } break; case l_false: is_sat = process_unsat(); if (is_sat == l_false) { m_lower = m_upper; } if (is_sat == l_undef) { return is_sat; } break; case l_undef: return l_undef; default: break; } } m_lower = m_upper; trace(); return l_true; } lbool check_sat_hill_climb(expr_ref_vector& asms1) { expr_ref_vector asms(asms1); lbool is_sat = l_true; if (m_hill_climb) { /** Give preference to cores that have large minmal values. */ sort_assumptions(asms); m_last_index = std::min(m_last_index, asms.size()-1); m_last_index = 0; unsigned index = m_last_index>0?m_last_index-1:0; m_last_index = 0; bool first = index > 0; SASSERT(index < asms.size() || asms.empty()); IF_VERBOSE(10, verbose_stream() << "start hill climb " << index << " asms: " << asms.size() << "\n";); while (index < asms.size() && is_sat == l_true) { while (!first && asms.size() > 20*(index - m_last_index) && index < asms.size()) { index = next_index(asms, index); } first = false; m_last_index = index; is_sat = check_sat(index, asms.c_ptr()); } } else { is_sat = check_sat(asms.size(), asms.c_ptr()); } return is_sat; } lbool check_sat(unsigned sz, expr* const* asms) { lbool r = s().check_sat(sz, asms); if (r == l_true) { model_ref mdl; s().get_model(mdl); TRACE("opt", tout << *mdl;); if (mdl.get()) { update_assignment(mdl); } } return r; } void found_optimum() { IF_VERBOSE(1, verbose_stream() << "found optimum\n";); verify_assumptions(); m_lower.reset(); for (soft& s : m_soft) { s.set_value(m_model->is_true(s.s)); if (!s.is_true()) { m_lower += s.weight; } } m_upper = m_lower; m_found_feasible_optimum = true; } lbool operator()() override { m_defs.reset(); switch(m_st) { case s_primal: return mus_solver(); case s_primal_dual: return primal_dual_solver(); } return l_undef; } void collect_statistics(statistics& st) const override { st.update("maxres-cores", m_stats.m_num_cores); st.update("maxres-correction-sets", m_stats.m_num_cs); } struct weighted_core { exprs m_core; rational m_weight; weighted_core(exprs const& c, rational const& w): m_core(c), m_weight(w) {} }; lbool get_cores(vector& cores) { // assume m_s is unsat. lbool is_sat = l_false; cores.reset(); exprs core; while (is_sat == l_false) { core.reset(); expr_ref_vector _core(m); s().get_unsat_core(_core); model_ref mdl; get_mus_model(mdl); is_sat = minimize_core(_core); core.append(_core.size(), _core.c_ptr()); 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";); break; } 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; } // 1. remove all core literals from m_asms // 2. re-add literals of higher weight than min-weight. // 3. 'core' stores the core literals that are // re-encoded as assumptions, afterwards cores.push_back(weighted_core(core, core_weight(core))); remove_soft(core, m_asms); split_core(core); if (core.size() >= m_max_core_size) break; if (cores.size() >= m_max_num_cores) break; is_sat = check_sat_hill_climb(m_asms); } TRACE("opt", tout << "sat: " << is_sat << " num cores: " << cores.size() << "\n"; for (auto const& c : cores) display_vec(tout, c.m_core); tout << "num assumptions: " << m_asms.size() << "\n";); return is_sat; } void get_current_correction_set(exprs& cs) { model_ref mdl; s().get_model(mdl); update_assignment(mdl); get_current_correction_set(mdl.get(), cs); } void get_current_correction_set(model* mdl, exprs& cs) { cs.reset(); if (!mdl) return; for (expr* a : m_asms) { if (mdl->is_false(a)) { cs.push_back(a); } } TRACE("opt", display_vec(tout << "new correction set: ", cs);); } struct compare_asm { maxres& mr; compare_asm(maxres& mr):mr(mr) {} bool operator()(expr* a, expr* b) const { rational w1 = mr.get_weight(a); rational w2 = mr.get_weight(b); return w1 > w2 || (w1 == w2 && a->get_id() > b->get_id()); } }; void sort_assumptions(expr_ref_vector& _asms) { compare_asm comp(*this); exprs asms(_asms.size(), _asms.c_ptr()); expr_ref_vector trail(_asms); std::sort(asms.begin(), asms.end(), comp); _asms.reset(); _asms.append(asms.size(), asms.c_ptr()); DEBUG_CODE( for (unsigned i = 0; i + 1 < asms.size(); ++i) { SASSERT(get_weight(asms[i]) >= get_weight(asms[i+1])); }); } unsigned next_index(expr_ref_vector const& asms, unsigned index) { if (index < asms.size()) { rational w = get_weight(asms[index]); ++index; for (; index < asms.size() && w == get_weight(asms[index]); ++index); } return index; } void process_sat(exprs const& corr_set) { ++m_stats.m_num_cs; expr_ref fml(m), tmp(m); TRACE("opt", display_vec(tout << "corr_set: ", corr_set);); remove_soft(corr_set, m_asms); rational w = split_core(corr_set); cs_max_resolve(corr_set, w); IF_VERBOSE(2, verbose_stream() << "(opt.maxres.correction-set " << corr_set.size() << ")\n";); m_csmodel = nullptr; m_correction_set_size = 0; } lbool process_unsat() { vector cores; lbool is_sat = get_cores(cores); if (is_sat != l_true) { return is_sat; } if (cores.empty()) { return l_false; } else { process_unsat(cores); return l_true; } } unsigned max_core_size(vector const& cores) { unsigned result = 0; for (auto const& c : cores) { result = std::max(c.size(), result); } return result; } void process_unsat(vector const& cores) { for (auto const & c : cores) { process_unsat(c.m_core, c.m_weight); } } void update_model(expr* def, expr* value) { SASSERT(is_uninterp_const(def)); if (m_csmodel) { m_csmodel->register_decl(to_app(def)->get_decl(), (*m_csmodel)(value)); } } void process_unsat(exprs const& core, rational w) { IF_VERBOSE(3, verbose_stream() << "(maxres cs model valid: " << (m_csmodel.get() != nullptr) << " cs size:" << m_correction_set_size << " core: " << core.size() << ")\n";); expr_ref fml(m); SASSERT(!core.empty()); TRACE("opt", display_vec(tout << "minimized core: ", core);); IF_VERBOSE(10, display_vec(verbose_stream() << "core: ", core);); 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); } if (m_csmodel.get() && m_correction_set_size > 0) { // this estimate can overshoot for weighted soft constraints. --m_correction_set_size; } trace(); if (m_c.num_objectives() == 1 && m_pivot_on_cs && m_csmodel.get() && m_correction_set_size < core.size()) { exprs cs; TRACE("opt", tout << "cs " << m_correction_set_size << " " << core.size() << "\n";); get_current_correction_set(m_csmodel.get(), cs); m_correction_set_size = cs.size(); if (m_correction_set_size < core.size()) { process_sat(cs); return; } } } bool get_mus_model(model_ref& mdl) { rational w(0); if (m_c.sat_enabled()) { // SAT solver core extracts some model // during unsat core computation. mdl = nullptr; s().get_model(mdl); } else { w = m_mus.get_best_model(mdl); } if (mdl.get() && w < m_upper) { update_assignment(mdl); } return nullptr != mdl.get(); } lbool minimize_core(expr_ref_vector& core) { if (core.empty()) { return l_true; } if (m_c.sat_enabled()) { return l_true; } m_mus.reset(); m_mus.add_soft(core.size(), core.c_ptr()); lbool is_sat = m_mus.get_mus(m_new_core); if (is_sat != l_true) { return is_sat; } core.reset(); core.append(m_new_core); return l_true; } rational get_weight(expr* e) const { return m_asm2weight.find(e); } rational core_weight(exprs const& core) { if (core.empty()) return rational(0); // find the minimal weight: rational w = get_weight(core[0]); for (unsigned i = 1; i < core.size(); ++i) { w = std::min(w, get_weight(core[i])); } return w; } rational split_core(exprs const& core) { rational w = core_weight(core); // add fresh soft clauses for weights that are above w. for (expr* e : core) { rational w2 = get_weight(e); if (w2 > w) { rational w3 = w2 - w; new_assumption(e, w3); } } return w; } void display_vec(std::ostream& out, exprs const& exprs) { display_vec(out, exprs.size(), exprs.c_ptr()); } void display_vec(std::ostream& out, expr_ref_vector const& exprs) { display_vec(out, exprs.size(), exprs.c_ptr()); } void display_vec(std::ostream& out, unsigned sz, expr* const* args) const { for (unsigned i = 0; i < sz; ++i) { out << mk_pp(args[i], m) << " : " << get_weight(args[i]) << " "; } out << "\n"; } void display(std::ostream& out) { for (expr * a : m_asms) { out << mk_pp(a, m) << " : " << get_weight(a) << "\n"; } } void max_resolve(exprs const& core, rational const& w) { SASSERT(!core.empty()); expr_ref fml(m), asum(m); app_ref cls(m), d(m), dd(m); m_B.reset(); m_B.append(core.size(), core.c_ptr()); // // d_0 := true // d_i := b_{i-1} and d_{i-1} for i = 1...sz-1 // soft (b_i or !d_i) // == (b_i or !(!b_{i-1} or d_{i-1})) // == (b_i or b_0 & b_1 & ... & b_{i-1}) // // Soft constraint is satisfied if previous soft constraint // holds or if it is the first soft constraint to fail. // // Soundness of this rule can be established using MaxRes // for (unsigned i = 1; i < core.size(); ++i) { expr* b_i = core[i-1]; expr* b_i1 = core[i]; if (i == 1) { d = to_app(b_i); } else if (i == 2) { d = m.mk_and(b_i, d); m_trail.push_back(d); } else { dd = mk_fresh_bool("d"); fml = m.mk_implies(dd, d); add(fml); m_defs.push_back(fml); fml = m.mk_implies(dd, b_i); add(fml); m_defs.push_back(fml); fml = m.mk_and(d, b_i); update_model(dd, fml); d = dd; } asum = mk_fresh_bool("a"); cls = m.mk_or(b_i1, d); fml = m.mk_implies(asum, cls); update_model(asum, cls); new_assumption(asum, w); add(fml); m_defs.push_back(fml); } } // cs is a correction set (a complement of a (maximal) satisfying assignment). void cs_max_resolve(exprs const& cs, rational const& w) { if (cs.empty()) return; TRACE("opt", display_vec(tout << "correction set: ", cs);); expr_ref fml(m), asum(m); app_ref cls(m), d(m), dd(m); m_B.reset(); m_B.append(cs.size(), cs.c_ptr()); d = m.mk_false(); // // d_0 := false // d_i := b_{i-1} or d_{i-1} for i = 1...sz-1 // soft (b_i and d_i) // == (b_i and (b_0 or b_1 or ... or b_{i-1})) // // asm => b_i // asm => d_{i-1} or b_{i-1} // d_i => d_{i-1} or b_{i-1} // for (unsigned i = 1; i < cs.size(); ++i) { expr* b_i = cs[i - 1]; expr* b_i1 = cs[i]; cls = m.mk_or(b_i, d); if (i > 2) { d = mk_fresh_bool("d"); fml = m.mk_implies(d, cls); update_model(d, cls); add(fml); m_defs.push_back(fml); } else { d = cls; } asum = mk_fresh_bool("a"); fml = m.mk_implies(asum, b_i1); add(fml); m_defs.push_back(fml); fml = m.mk_implies(asum, cls); add(fml); m_defs.push_back(fml); new_assumption(asum, w); fml = m.mk_and(b_i1, cls); update_model(asum, fml); } fml = m.mk_or(cs.size(), cs.c_ptr()); add(fml); } void update_assignment(model_ref & mdl) { mdl->set_model_completion(true); unsigned correction_set_size = 0; for (expr* a : m_asms) { if (mdl->is_false(a)) { ++correction_set_size; } } if (!m_csmodel.get() || correction_set_size < m_correction_set_size) { m_csmodel = mdl; m_correction_set_size = correction_set_size; } TRACE("opt_verbose", tout << *mdl;); rational upper(0); for (soft& s : m_soft) { TRACE("opt", tout << s.s << ": " << (*mdl)(s.s) << " " << s.weight << "\n";); if (!mdl->is_true(s.s)) { upper += s.weight; } } if (upper > m_upper) { TRACE("opt", tout << "new upper: " << upper << " vs existing upper: " << m_upper << "\n";); return; } if (!m_c.verify_model(m_index, mdl.get(), upper)) { return; } m_model = mdl; m_c.model_updated(mdl.get()); TRACE("opt", tout << "updated upper: " << upper << "\n";); for (soft& s : m_soft) { s.set_value(m_model->is_true(s.s)); } verify_assignment(); m_upper = upper; trace(); add_upper_bound_block(); } void add_upper_bound_block() { if (!m_add_upper_bound_block) return; pb_util u(m); expr_ref_vector nsoft(m); vector weights; expr_ref fml(m); for (soft& s : m_soft) { nsoft.push_back(mk_not(m, s.s)); weights.push_back(s.weight); } fml = u.mk_lt(nsoft.size(), weights.c_ptr(), nsoft.c_ptr(), m_upper); TRACE("opt", tout << "block upper bound " << fml << "\n";);; add(fml); } void remove_soft(exprs const& core, expr_ref_vector& asms) { TRACE("opt", tout << "before remove: " << asms << "\n";); unsigned j = 0; for (expr* a : asms) if (!core.contains(a)) asms[j++] = a; asms.shrink(j); TRACE("opt", tout << "after remove: " << asms << "\n";); } void updt_params(params_ref& _p) override { maxsmt_solver_base::updt_params(_p); opt_params p(_p); m_hill_climb = p.maxres_hill_climb(); m_add_upper_bound_block = p.maxres_add_upper_bound_block(); m_max_num_cores = p.maxres_max_num_cores(); m_max_core_size = p.maxres_max_core_size(); m_maximize_assignment = p.maxres_maximize_assignment(); m_max_correction_set_size = p.maxres_max_correction_set_size(); m_pivot_on_cs = p.maxres_pivot_on_correction_set(); m_wmax = p.maxres_wmax(); m_dump_benchmarks = p.dump_benchmarks(); } lbool init_local() { m_lower.reset(); m_trail.reset(); lbool is_sat = l_true; obj_map new_soft; is_sat = find_mutexes(new_soft); if (is_sat != l_true) { return is_sat; } for (auto const& kv : new_soft) { add_soft(kv.m_key, kv.m_value); } m_max_upper = m_upper; m_found_feasible_optimum = false; m_last_index = 0; add_upper_bound_block(); m_csmodel = nullptr; m_correction_set_size = 0; return l_true; } void commit_assignment() override { if (m_found_feasible_optimum) { 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(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 << " 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() { return; IF_VERBOSE(1, verbose_stream() << "verify assumptions\n";); ref _solver = mk_smt_solver(m, m_params, symbol()); _solver->assert_expr(s().get_assertions()); _solver->assert_expr(m_asms); lbool is_sat = _solver->check_sat(0, nullptr); IF_VERBOSE(1, verbose_stream() << "assignment status (l_true) " << is_sat << "\n";); VERIFY(is_sat == l_true); } void verify_assignment() { return; IF_VERBOSE(1, verbose_stream() << "verify assignment\n";); ref _solver = mk_smt_solver(m, m_params, symbol()); _solver->assert_expr(s().get_assertions()); expr_ref n(m); for (soft& s : m_soft) { n = s.s; if (!s.is_true()) { n = mk_not(m, n); } _solver->assert_expr(n); } lbool is_sat = _solver->check_sat(0, nullptr); IF_VERBOSE(1, verbose_stream() << "assignment status (l_true) " << is_sat << "\n"); VERIFY(is_sat == l_true); } }; opt::maxsmt_solver_base* opt::mk_maxres( maxsat_context& c, unsigned id, weights_t& ws, expr_ref_vector const& soft) { return alloc(maxres, c, id, ws, soft, maxres::s_primal); } opt::maxsmt_solver_base* opt::mk_primal_dual_maxres( maxsat_context& c, unsigned id, weights_t& ws, expr_ref_vector const& soft) { return alloc(maxres, c, id, ws, soft, maxres::s_primal_dual); } z3-z3-4.8.7/src/opt/maxres.h000066400000000000000000000010231356505360400154640ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: maxsres.h Abstract: MaxRes (weighted) max-sat algorithm by Nina and Bacchus, AAAI 2014. Author: Nikolaj Bjorner (nbjorner) 2014-20-7 Notes: --*/ #ifndef MAXRES_H_ #define MAXRES_H_ namespace opt { maxsmt_solver_base* mk_maxres(maxsat_context& c, unsigned id, weights_t & ws, expr_ref_vector const& soft); maxsmt_solver_base* mk_primal_dual_maxres(maxsat_context& c, unsigned id, weights_t & ws, expr_ref_vector const& soft); }; #endif z3-z3-4.8.7/src/opt/maxsmt.cpp000066400000000000000000000351671356505360400160510ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: maxsmt.cpp Abstract: MaxSMT optimization context. Author: Nikolaj Bjorner (nbjorner) 2013-11-7 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 "opt/opt_context.h" #include "smt/theory_wmaxsat.h" #include "smt/theory_pb.h" namespace opt { maxsmt_solver_base::maxsmt_solver_base( maxsat_context& c, vector const& ws, expr_ref_vector const& softs): m(c.get_manager()), m_c(c), m_assertions(m), m_trail(m) { c.get_base_model(m_model); SASSERT(m_model); updt_params(c.params()); for (unsigned i = 0; i < ws.size(); ++i) { m_soft.push_back(soft(expr_ref(softs.get(i), m), ws[i], false)); } } void maxsmt_solver_base::updt_params(params_ref& p) { m_params.copy(p); } solver& maxsmt_solver_base::s() { return m_c.get_solver(); } void maxsmt_solver_base::commit_assignment() { expr_ref tmp(m); expr_ref_vector fmls(m); rational k(0), cost(0); vector weights; for (soft const& s : m_soft) { if (s.is_true()) { k += s.weight; } else { cost += s.weight; } weights.push_back(s.weight); fmls.push_back(s.s); } pb_util pb(m); tmp = pb.mk_ge(weights.size(), weights.c_ptr(), fmls.c_ptr(), k); TRACE("opt", tout << "cost: " << cost << "\n" << tmp << "\n";); s().assert_expr(tmp); } bool maxsmt_solver_base::init() { m_lower.reset(); m_upper.reset(); for (soft& s : m_soft) { 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"); tout << "\n";); return true; } void maxsmt_solver_base::set_mus(bool f) { params_ref p; p.set_bool("minimize_core", f); // p.set_bool("minimize_core_partial", f); s().updt_params(p); } void maxsmt_solver_base::enable_sls(bool force) { m_c.enable_sls(force); } app* maxsmt_solver_base::mk_fresh_bool(char const* name) { app* result = m.mk_fresh_const(name, m.mk_bool_sort()); m_c.fm().hide(result); return result; } smt::theory_wmaxsat* maxsmt_solver_base::get_wmax_theory() const { smt::theory_id th_id = m.get_family_id("weighted_maxsat"); smt::theory* th = m_c.smt_context().get_theory(th_id); if (th) { return dynamic_cast(th); } else { return nullptr; } } smt::theory_wmaxsat* maxsmt_solver_base::ensure_wmax_theory() { smt::theory_wmaxsat* wth = get_wmax_theory(); if (wth) { wth->reset_local(); } else { wth = alloc(smt::theory_wmaxsat, m, m_c.fm()); m_c.smt_context().register_plugin(wth); } smt::theory_id th_pb = m.get_family_id("pb"); smt::theory_pb* pb = dynamic_cast(m_c.smt_context().get_theory(th_pb)); if (!pb) { theory_pb_params params; pb = alloc(smt::theory_pb, m, params); m_c.smt_context().register_plugin(pb); } return wth; } maxsmt_solver_base::scoped_ensure_theory::scoped_ensure_theory(maxsmt_solver_base& s) { m_wth = s.ensure_wmax_theory(); } maxsmt_solver_base::scoped_ensure_theory::~scoped_ensure_theory() { if (m_wth) { m_wth->reset_local(); } } smt::theory_wmaxsat& maxsmt_solver_base::scoped_ensure_theory::operator()() { return *m_wth; } void maxsmt_solver_base::trace_bounds(char const * solver) { IF_VERBOSE(1, rational l = m_adjust_value(m_lower); rational u = m_adjust_value(m_upper); if (l > u) std::swap(l, u); verbose_stream() << "(opt." << solver << " [" << l << ":" << u << "])\n";); } lbool maxsmt_solver_base::find_mutexes(obj_map& new_soft) { m_lower.reset(); expr_ref_vector fmls(m); for (soft& s : m_soft) { new_soft.insert(s.s, s.weight); fmls.push_back(s.s); } vector mutexes; lbool is_sat = s().find_mutexes(fmls, mutexes); if (is_sat != l_true) { return is_sat; } for (auto& mux : mutexes) { process_mutex(mux, new_soft); } return l_true; } struct maxsmt_compare_soft { obj_map const& m_soft; maxsmt_compare_soft(obj_map const& soft): m_soft(soft) {} bool operator()(expr* a, expr* b) const { return m_soft.find(a) > m_soft.find(b); } }; void maxsmt_solver_base::process_mutex(expr_ref_vector& mutex, obj_map& new_soft) { TRACE("opt", for (expr* e : mutex) { tout << mk_pp(e, m) << " |-> " << new_soft.find(e) << "\n"; }); if (mutex.size() <= 1) { return; } maxsmt_compare_soft cmp(new_soft); ptr_vector _mutex(mutex.size(), mutex.c_ptr()); std::sort(_mutex.begin(), _mutex.end(), cmp); mutex.reset(); mutex.append(_mutex.size(), _mutex.c_ptr()); rational weight(0), sum1(0), sum2(0); vector weights; for (expr* e : mutex) { rational w = new_soft.find(e); weights.push_back(w); sum1 += w; new_soft.remove(e); } for (unsigned i = mutex.size(); i-- > 0; ) { expr_ref soft(m.mk_or(i+1, mutex.c_ptr()), m); m_trail.push_back(soft); rational w = weights[i]; weight = w - weight; m_lower += weight*rational(i); IF_VERBOSE(1, verbose_stream() << "(opt.maxsat mutex size: " << i + 1 << " weight: " << weight << ")\n";); sum2 += weight*rational(i+1); new_soft.insert(soft, weight); for (; i > 0 && weights[i-1] == w; --i) {} weight = w; } SASSERT(sum1 == sum2); } maxsmt::maxsmt(maxsat_context& c, unsigned index): m(c.get_manager()), m_c(c), m_index(index), m_soft_constraints(m), m_answer(m) {} 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 (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")) { m_msolver = mk_primal_dual_maxres(m_c, m_index, m_weights, m_soft_constraints); } else if (maxsat_engine == symbol("wmax")) { m_msolver = mk_wmax(m_c, m_weights, m_soft_constraints); } else if (maxsat_engine == symbol("sortmax")) { m_msolver = mk_sortmax(m_c, m_weights, m_soft_constraints); } else { warning_msg("solver %s is not recognized, using default 'maxres'", maxsat_engine.str().c_str()); m_msolver = mk_maxres(m_c, m_index, m_weights, m_soft_constraints); } if (m_msolver) { m_msolver->updt_params(m_params); m_msolver->set_adjust_value(m_adjust_value); is_sat = l_undef; try { is_sat = (*m_msolver)(); } catch (z3_exception&) { is_sat = l_undef; } if (is_sat != l_false) { m_msolver->get_model(m_model, m_labels); } } IF_VERBOSE(5, verbose_stream() << "is-sat: " << is_sat << "\n"; if (is_sat == l_true) { verbose_stream() << "Satisfying soft constraints\n"; display_answer(verbose_stream()); }); DEBUG_CODE(if (is_sat == l_true) verify_assignment();); return is_sat; } void maxsmt::set_adjust_value(adjust_value& adj) { m_adjust_value = adj; if (m_msolver) { m_msolver->set_adjust_value(m_adjust_value); } } void maxsmt::verify_assignment() { // TBD: have to use a different solver // because we don't push local scope any longer. return; } bool maxsmt::get_assignment(unsigned idx) const { if (m_msolver) { return m_msolver->get_assignment(idx); } else { return true; } } rational maxsmt::get_lower() const { rational r = m_lower; if (m_msolver) { rational q = m_msolver->get_lower(); if (q > r) r = q; } return m_adjust_value(r); } rational maxsmt::get_upper() const { rational r = m_upper; if (m_msolver) { rational q = m_msolver->get_upper(); if (q < r) r = q; } return m_adjust_value(r); } void maxsmt::update_lower(rational const& r) { m_lower = r; } void maxsmt::update_upper(rational const& r) { m_upper = r; } void maxsmt::get_model(model_ref& mdl, svector& labels) { mdl = m_model.get(); labels = m_labels; } void maxsmt::commit_assignment() { if (m_msolver) { m_msolver->commit_assignment(); } } void maxsmt::add(expr* f, rational const& w) { TRACE("opt", tout << mk_pp(f, m) << " weight: " << w << "\n";); SASSERT(m.is_bool(f)); SASSERT(w.is_pos()); unsigned index = 0; if (m_soft_constraint_index.find(f, index)) { m_weights[index] += w; } else { m_soft_constraint_index.insert(f, m_weights.size()); m_soft_constraints.push_back(f); m_weights.push_back(w); } m_upper += w; } struct cmp_first { bool operator()(std::pair const& x, std::pair const& y) const { return x.first < y.first; } }; void maxsmt::display_answer(std::ostream& out) const { vector> sorted_weights; unsigned n = m_weights.size(); for (unsigned i = 0; i < n; ++i) { sorted_weights.push_back(std::make_pair(i, m_weights[i])); } std::sort(sorted_weights.begin(), sorted_weights.end(), cmp_first()); sorted_weights.reverse(); for (unsigned i = 0; i < n; ++i) { unsigned idx = sorted_weights[i].first; expr* e = m_soft_constraints[idx]; bool is_not = m.is_not(e, e); out << m_weights[idx] << ": " << mk_pp(e, m) << ((is_not != get_assignment(idx))?" |-> true ":" |-> false ") << "\n"; } } bool maxsmt::is_maxsat_problem(vector const& ws) const { for (unsigned i = 0; i < ws.size(); ++i) { if (!ws[i].is_one()) { return false; } } return true; } void maxsmt::updt_params(params_ref& p) { m_params.append(p); if (m_msolver) { m_msolver->updt_params(p); } } void maxsmt::collect_statistics(statistics& st) const { if (m_msolver) { m_msolver->collect_statistics(st); } } solver& maxsmt::s() { return m_c.get_solver(); } void maxsmt::model_updated(model* mdl) { m_c.model_updated(mdl); } class solver_maxsat_context : public maxsat_context { params_ref m_params; solver_ref m_solver; model_ref m_model; ref m_fm; symbol m_maxsat_engine; public: solver_maxsat_context(params_ref& p, solver* s, model * m): m_params(p), m_solver(s), m_model(m), m_fm(alloc(generic_model_converter, s->get_manager(), "maxsmt")) { opt_params _p(p); m_maxsat_engine = _p.maxsat_engine(); } generic_model_converter& fm() override { return *m_fm.get(); } bool sat_enabled() const override { return false; } solver& get_solver() override { return *m_solver.get(); } ast_manager& get_manager() const override { return m_solver->get_manager(); } params_ref& params() override { return m_params; } void enable_sls(bool force) override { } // no op symbol const& maxsat_engine() const override { return m_maxsat_engine; } void get_base_model(model_ref& _m) override { _m = m_model; }; smt::context& smt_context() override { throw default_exception("stand-alone maxsat context does not support wmax"); } unsigned num_objectives() override { return 1; } bool verify_model(unsigned id, model* mdl, rational const& v) override { return true; }; void set_model(model_ref& _m) override { m_model = _m; } void model_updated(model* mdl) override { } // no-op }; lbool maxsmt_wrapper::operator()(vector>& soft) { solver_maxsat_context ctx(m_params, m_solver.get(), m_model.get()); maxsmt maxsmt(ctx, 0); for (auto const& p : soft) { maxsmt.add(p.first, p.second); } lbool r = maxsmt(); if (r == l_true) { svector labels; maxsmt.get_model(m_model, labels); // TBD: is m_fm applied or not? unsigned j = 0; for (auto const& p : soft) { if (m_model->is_true(p.first)) { soft[j++] = p; } } soft.shrink(j); } return r; } }; z3-z3-4.8.7/src/opt/maxsmt.h000066400000000000000000000162151356505360400155070ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: maxsmt.h Abstract: MaxSMT optimization context. Author: Nikolaj Bjorner (nbjorner) 2013-11-7 Notes: --*/ #ifndef OPT_MAXSMT_H_ #define OPT_MAXSMT_H_ #include "ast/ast.h" #include "util/params.h" #include "solver/solver.h" #include "util/statistics.h" #include "smt/smt_context.h" #include "smt/smt_theory.h" #include "smt/theory_wmaxsat.h" #include "opt/opt_solver.h" namespace opt { typedef vector const weights_t; class maxsat_context; class maxsmt_solver { protected: adjust_value m_adjust_value; public: virtual ~maxsmt_solver() {} virtual lbool operator()() = 0; virtual rational get_lower() const = 0; virtual rational get_upper() const = 0; virtual bool get_assignment(unsigned index) const = 0; virtual void collect_statistics(statistics& st) const = 0; virtual void get_model(model_ref& mdl, svector& labels) = 0; virtual void updt_params(params_ref& p) = 0; void set_adjust_value(adjust_value& adj) { m_adjust_value = adj; } }; // --------------------------------------------- // base class with common utilities used // by maxsmt solvers // class maxsmt_solver_base : public maxsmt_solver { protected: struct soft { expr_ref s; rational weight; 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; vector m_soft; expr_ref_vector m_assertions; expr_ref_vector m_trail; rational m_lower; rational m_upper; model_ref m_model; svector m_labels; //const expr_ref_vector m_soft; //vector m_weights; //svector m_assignment; // truth assignment to soft constraints params_ref m_params; // config public: maxsmt_solver_base(maxsat_context& c, weights_t& ws, expr_ref_vector const& soft); ~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(); } 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(); void set_model() { s().get_model(m_model); s().get_labels(m_labels); } void updt_params(params_ref& p) override; solver& s(); bool init(); void set_mus(bool f); app* mk_fresh_bool(char const* name); class smt::theory_wmaxsat* get_wmax_theory() const; smt::theory_wmaxsat* ensure_wmax_theory(); class scoped_ensure_theory { smt::theory_wmaxsat* m_wth; public: scoped_ensure_theory(maxsmt_solver_base& s); ~scoped_ensure_theory(); smt::theory_wmaxsat& operator()(); }; lbool find_mutexes(obj_map& new_soft); protected: void enable_sls(bool force); void trace_bounds(char const* solver); void process_mutex(expr_ref_vector& mutex, obj_map& new_soft); }; /** Takes solver with hard constraints added. Returns modified soft constraints that are maximal assignments. */ class maxsmt { ast_manager& m; maxsat_context& m_c; unsigned m_index; scoped_ptr m_msolver; expr_ref_vector m_soft_constraints; obj_map m_soft_constraint_index; expr_ref_vector m_answer; vector m_weights; rational m_lower; rational m_upper; adjust_value m_adjust_value; model_ref m_model; svector m_labels; params_ref m_params; public: maxsmt(maxsat_context& c, unsigned id); lbool operator()(); void updt_params(params_ref& p); void add(expr* f, rational const& w); void set_adjust_value(adjust_value& adj); unsigned size() const { return m_soft_constraints.size(); } expr* operator[](unsigned idx) const { return m_soft_constraints[idx]; } rational weight(unsigned idx) const { return m_weights[idx]; } void commit_assignment(); rational get_lower() const; rational get_upper() const; void update_lower(rational const& r); void update_upper(rational const& r); void get_model(model_ref& mdl, svector& labels); bool get_assignment(unsigned index) const; void display_answer(std::ostream& out) const; void collect_statistics(statistics& st) const; void model_updated(model* mdl); private: bool is_maxsat_problem(weights_t& ws) const; void verify_assignment(); solver& s(); }; /** \brief Standalone MaxSMT solver. It takes as input a solver object and provides a MaxSAT solver routine. It assumes the solver state is satisfiable and therefore there is a model associated with the constraints asserted to the solver. A model of the solver state must be supplied as a last argument. It assumes that the caller manages scope on the solver such that the solver can be left in a stronger or inconsistent state upon return. Callers should therefore use this feature under a push/pop. */ class maxsmt_wrapper { params_ref m_params; ref m_solver; model_ref m_model; public: maxsmt_wrapper(params_ref & p, solver* s, model* m): m_params(p), m_solver(s), m_model(m) {} lbool operator()(expr_ref_vector& soft) { vector> _soft; for (expr* e : soft) _soft.push_back(std::make_pair(e, rational::one())); lbool r = (*this)(_soft); soft.reset(); for (auto const& p : _soft) soft.push_back(p.first); return r; } /** \brief takes a vector of weighted soft constraints. Returns a modified list of soft constraints that are satisfied in the maximal satisfying assignment. */ lbool operator()(vector> & soft); model_ref get_model() { return m_model; } }; }; #endif z3-z3-4.8.7/src/opt/opt_cmds.cpp000066400000000000000000000117541356505360400163440ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: opt_cmds.cpp Abstract: Commands for optimization benchmarks Author: Anh-Dung Phan (t-anphan) 2013-10-14 Notes: TODO: - Add appropriate statistics tracking to opt::context - Deal with push/pop (later) --*/ #include "opt/opt_cmds.h" #include "cmd_context/cmd_context.h" #include "ast/ast_pp.h" #include "opt/opt_context.h" #include "util/cancel_eh.h" #include "util/scoped_ctrl_c.h" #include "util/scoped_timer.h" #include "cmd_context/parametric_cmd.h" #include "opt/opt_params.hpp" #include "model/model_smt2_pp.h" static opt::context& get_opt(cmd_context& cmd, opt::context* opt) { if (opt) { return *opt; } if (!cmd.get_opt()) { cmd.set_opt(alloc(opt::context, cmd.m())); } return dynamic_cast(*cmd.get_opt()); } class assert_soft_cmd : public parametric_cmd { unsigned m_idx; expr* m_formula; opt::context* m_opt; public: assert_soft_cmd(opt::context* opt): parametric_cmd("assert-soft"), m_idx(0), m_formula(nullptr), m_opt(opt) {} ~assert_soft_cmd() override { } void reset(cmd_context & ctx) override { m_idx = 0; m_formula = nullptr; } char const * get_usage() const override { return " [:weight ] [:id ]"; } char const * get_main_descr() const override { return "assert soft constraint with optional weight and identifier"; } // command invocation void prepare(cmd_context & ctx) override { reset(ctx); } cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { if (m_idx == 0) return CPK_EXPR; return parametric_cmd::next_arg_kind(ctx); } void init_pdescrs(cmd_context & ctx, param_descrs & p) override { p.insert("weight", CPK_NUMERAL, "(default: 1) penalty of not satisfying constraint."); p.insert("id", CPK_SYMBOL, "(default: null) partition identifier for soft constraints."); } void set_next_arg(cmd_context & ctx, expr * t) override { SASSERT(m_idx == 0); if (!ctx.m().is_bool(t)) { throw cmd_exception("Invalid type for expression. Expected Boolean type."); } m_formula = t; ++m_idx; } void failure_cleanup(cmd_context & ctx) override { reset(ctx); } void execute(cmd_context & ctx) override { if (!m_formula) { throw cmd_exception("assert-soft requires a formulas as argument."); } symbol w("weight"); rational weight = ps().get_rat(symbol("weight"), rational::one()); symbol id = ps().get_sym(symbol("id"), symbol::null); get_opt(ctx, m_opt).add_soft_constraint(m_formula, weight, id); ctx.print_success(); reset(ctx); } void finalize(cmd_context & ctx) override { } }; class min_maximize_cmd : public cmd { bool m_is_max; opt::context* m_opt; public: min_maximize_cmd(bool is_max, opt::context* opt): cmd(is_max?"maximize":"minimize"), m_is_max(is_max), m_opt(opt) {} void reset(cmd_context & ctx) override { } char const * get_usage() const override { return ""; } char const * get_descr(cmd_context & ctx) const override { return "check sat modulo objective function";} unsigned get_arity() const override { return 1; } void prepare(cmd_context & ctx) override {} cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { return CPK_EXPR; } void set_next_arg(cmd_context & ctx, expr * t) override { if (!is_app(t)) { throw cmd_exception("malformed objective term: it cannot be a quantifier or bound variable"); } get_opt(ctx, m_opt).add_objective(to_app(t), m_is_max); ctx.print_success(); } void failure_cleanup(cmd_context & ctx) override { reset(ctx); } void execute(cmd_context & ctx) override { } }; class get_objectives_cmd : public cmd { opt::context* m_opt; public: get_objectives_cmd(opt::context* opt): cmd("get-objectives"), m_opt(opt) {} void reset(cmd_context & ctx) override { } char const * get_usage() const override { return "(get-objectives)"; } char const * get_descr(cmd_context & ctx) const override { return "retrieve the objective values (after optimization)"; } unsigned get_arity() const override { return 0; } void prepare(cmd_context & ctx) override {} void failure_cleanup(cmd_context & ctx) override { reset(ctx); } void execute(cmd_context & ctx) override { if (!ctx.ignore_check()) { get_opt(ctx, m_opt).display_assignment(ctx.regular_stream()); } } }; void install_opt_cmds(cmd_context & ctx, opt::context* opt) { ctx.insert(alloc(assert_soft_cmd, opt)); ctx.insert(alloc(min_maximize_cmd, true, opt)); ctx.insert(alloc(min_maximize_cmd, false, opt)); ctx.insert(alloc(get_objectives_cmd, opt)); } z3-z3-4.8.7/src/opt/opt_cmds.h000066400000000000000000000006001356505360400157750ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: opt_cmds.h Abstract: Commands for optimization benchmarks Author: Anh-Dung Phan (t-anphan) 2013-10-14 Notes: --*/ #ifndef OPT_CMDS_H_ #define OPT_CMDS_H_ #include "ast/ast.h" #include "opt/opt_context.h" class cmd_context; void install_opt_cmds(cmd_context & ctx, opt::context* opt = nullptr); #endif z3-z3-4.8.7/src/opt/opt_context.cpp000066400000000000000000001621371356505360400171040ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: opt_context.cpp Abstract: Facility for running optimization problem. Author: Anh-Dung Phan (t-anphan) 2013-10-16 Notes: --*/ #include "util/gparams.h" #include "ast/for_each_expr.h" #include "ast/ast_pp.h" #include "ast/bv_decl_plugin.h" #include "ast/pb_decl_plugin.h" #include "ast/ast_smt_pp.h" #include "ast/ast_pp_util.h" #include "model/model_smt2_pp.h" #include "tactic/goal.h" #include "tactic/tactic.h" #include "tactic/arith/lia2card_tactic.h" #include "tactic/core/solve_eqs_tactic.h" #include "tactic/core/simplify_tactic.h" #include "tactic/core/propagate_values_tactic.h" #include "tactic/core/solve_eqs_tactic.h" #include "tactic/core/elim_uncnstr_tactic.h" #include "tactic/tactical.h" #include "tactic/arith/card2bv_tactic.h" #include "tactic/arith/eq2bv_tactic.h" #include "tactic/bv/dt2bv_tactic.h" #include "tactic/generic_model_converter.h" #include "ackermannization/ackermannize_bv_tactic.h" #include "sat/sat_solver/inc_sat_solver.h" #include "qe/qsat.h" #include "opt/opt_context.h" #include "opt/opt_solver.h" #include "opt/opt_params.hpp" 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()); } 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(); m_objectives[idx].m_terms.pop_back(); m_objectives[idx].m_weights.pop_back(); m_objectives_term_trail.pop_back(); } m_objectives_term_trail_lim.pop_back(); k = m_objectives_lim.back(); while (m_objectives.size() > k) { objective& obj = m_objectives.back(); if (obj.m_type == O_MAXSMT) { m_indices.erase(obj.m_id); } m_objectives.pop_back(); } m_objectives_lim.pop_back(); m_hard_lim.pop_back(); m_asms_lim.pop_back(); } void context::scoped_state::add(expr* hard) { m_hard.push_back(hard); } bool context::scoped_state::set(expr_ref_vector const & hard) { bool eq = hard.size() == m_hard.size(); for (unsigned i = 0; eq && i < hard.size(); ++i) { eq = hard.get(i) == m_hard.get(i); } m_hard.reset(); m_hard.append(hard); return !eq; } unsigned context::scoped_state::add(expr* f, rational const& w, symbol const& id) { if (!m.is_bool(f)) { throw default_exception("Soft constraint should be Boolean"); } if (!m_indices.contains(id)) { m_objectives.push_back(objective(m, id)); m_indices.insert(id, m_objectives.size() - 1); } SASSERT(m_indices.contains(id)); unsigned idx = m_indices[id]; if (!w.is_zero()) { m_objectives[idx].m_terms.push_back(f); m_objectives[idx].m_weights.push_back(w); m_objectives_term_trail.push_back(idx); } return idx; } unsigned context::scoped_state::add(app* t, bool is_max) { app_ref tr(t, m); if (!m_bv.is_bv(t) && !m_arith.is_int_real(t)) { throw default_exception("Objective must be bit-vector, integer or real"); } unsigned index = m_objectives.size(); m_objectives.push_back(objective(is_max, tr, index)); return index; } context::context(ast_manager& m): m(m), m_arith(m), m_bv(m), m_hard_constraints(m), m_solver(nullptr), m_pareto1(false), m_box_index(UINT_MAX), m_optsmt(m, *this), m_scoped_state(m), m_fm(alloc(generic_model_converter, m, "opt")), m_model_fixed(), m_objective_refs(m), m_core(m), m_enable_sat(false), m_is_clausal(false), m_pp_neat(false), m_unknown("unknown") { params_ref p; p.set_bool("model", true); p.set_bool("unsat_core", true); p.set_bool("elim_to_real", true); updt_params(p); m_model_counter = 0; } context::~context() { reset_maxsmts(); } void context::reset_maxsmts() { for (auto& kv : m_maxsmts) { dealloc(kv.m_value); } m_maxsmts.reset(); } void context::push() { m_scoped_state.push(); } void context::pop(unsigned n) { for (unsigned i = 0; i < n; ++i) { m_scoped_state.pop(); } clear_state(); reset_maxsmts(); m_optsmt.reset(); m_hard_constraints.reset(); } void context::get_labels(svector & r) { r.append(m_labels); } void context::get_unsat_core(expr_ref_vector & r) { r.append(m_core); } void context::set_hard_constraints(expr_ref_vector const& fmls) { if (m_scoped_state.set(fmls)) { clear_state(); } } void context::add_hard_constraint(expr* f) { m_scoped_state.add(f); 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); } expr_ref context::get_objective(unsigned i) { SASSERT(i < num_objectives()); objective const& o = m_scoped_state.m_objectives[i]; expr_ref result(m), zero(m); expr_ref_vector args(m); switch (o.m_type) { case O_MAXSMT: zero = m_arith.mk_numeral(rational(0), false); for (unsigned i = 0; i < o.m_terms.size(); ++i) { args.push_back(m.mk_ite(o.m_terms[i], zero, m_arith.mk_numeral(o.m_weights[i], false))); } result = m_arith.mk_add(args.size(), args.c_ptr()); break; case O_MAXIMIZE: result = o.m_term; if (m_arith.is_arith_expr(result)) { result = m_arith.mk_uminus(result); } else if (m_bv.is_bv(result)) { result = m_bv.mk_bv_neg(result); } else { UNREACHABLE(); } break; case O_MINIMIZE: result = o.m_term; break; } return result; } unsigned context::add_soft_constraint(expr* f, rational const& w, symbol const& id) { clear_state(); return m_scoped_state.add(f, w, id); } unsigned context::add_objective(app* t, bool is_max) { clear_state(); return m_scoped_state.add(t, is_max); } void context::import_scoped_state() { m_optsmt.reset(); reset_maxsmts(); m_objectives.reset(); m_hard_constraints.reset(); scoped_state& s = m_scoped_state; for (unsigned i = 0; i < s.m_objectives.size(); ++i) { objective& obj = s.m_objectives[i]; m_objectives.push_back(obj); if (obj.m_type == O_MAXSMT) { add_maxsmt(obj.m_id, i); } } m_hard_constraints.append(s.m_hard); } lbool context::optimize(expr_ref_vector const& _asms) { if (m_pareto) { return execute_pareto(); } if (m_box_index != UINT_MAX) { return execute_box(); } 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; } internalize(); update_solver(); if (contains_quantifiers()) { warning_msg("optimization with quantified constraints is not supported"); } #if 0 if (is_qsat_opt()) { return run_qsat_opt(); } #endif solver& s = get_solver(); s.assert_expr(m_hard_constraints); opt_params optp(m_params); symbol pri = optp.priority(); IF_VERBOSE(1, verbose_stream() << "(optimize:check-sat)\n"); lbool is_sat = s.check_sat(asms.size(),asms.c_ptr()); TRACE("opt", s.display(tout << "initial search result: " << is_sat << "\n");); if (is_sat != l_false) { s.get_model(m_model); s.get_labels(m_labels); model_updated(m_model.get()); } if (is_sat != l_true) { TRACE("opt", tout << m_hard_constraints << " " << asms << "\n";); if (!asms.empty()) { s.get_unsat_core(m_core); } 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()); update_lower(); switch (m_objectives.size()) { case 0: break; case 1: if (m_pareto1) { is_sat = l_false; m_pareto1 = false; } else { m_pareto1 = (pri == symbol("pareto")); is_sat = execute(m_objectives[0], true, false); } break; default: { opt_params optp(m_params); symbol pri = optp.priority(); if (pri == symbol("pareto")) { is_sat = execute_pareto(); } else if (pri == symbol("box")) { is_sat = execute_box(); } else { is_sat = execute_lex(); } } } if (is_sat == l_true) validate_model(); return adjust_unknown(is_sat); } lbool context::adjust_unknown(lbool r) { if (r == l_true && m_opt_solver.get() && m_opt_solver->was_unknown()) { r = l_undef; } return r; } void context::get_base_model(model_ref& mdl) { mdl = m_model; } void context::fix_model(model_ref& mdl) { if (mdl && !m_model_fixed.contains(mdl.get())) { TRACE("opt", m_fm->display(tout << "fix-model\n"); if (m_model_converter) m_model_converter->display(tout);); (*m_fm)(mdl); apply(m_model_converter, mdl); m_model_fixed.push_back(mdl.get()); } } 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); CTRACE("opt", mdl, tout << *mdl;); } void context::get_box_model(model_ref& mdl, unsigned index) { if (index >= m_box_models.size()) { throw default_exception("index into models is out of bounds"); } mdl = m_box_models[index]; fix_model(mdl); } bool context::contains_quantifiers() const { for (expr* f : m_hard_constraints) { if (has_quantifiers(f)) return true; } return false; } 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); 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()) { throw default_exception("unbounded objectives on quantified constraints is not supported"); } return result; } lbool context::execute_maxsat(symbol const& id, bool committed, bool scoped) { model_ref tmp; maxsmt& ms = *m_maxsmts.find(id); if (scoped) get_solver().push(); lbool result = ms(); if (result != l_false && (ms.get_model(tmp, m_labels), tmp.get())) { ms.get_model(m_model, m_labels); } if (scoped) get_solver().pop(1); if (result == l_true && committed) ms.commit_assignment(); DEBUG_CODE(if (result == l_true) validate_maxsat(id);); return result; } lbool context::execute(objective const& obj, bool committed, bool scoped) { switch(obj.m_type) { case O_MAXIMIZE: return execute_min_max(obj.m_index, committed, scoped, true); case O_MINIMIZE: return execute_min_max(obj.m_index, committed, scoped, false); case O_MAXSMT: return execute_maxsat(obj.m_id, committed, scoped); default: UNREACHABLE(); return l_undef; } } /** \brief there is no need to use push/pop when all objectives are maxsat and engine is maxres. */ bool context::scoped_lex() { if (m_maxsat_engine == symbol("maxres")) { for (auto const& o : m_objectives) { if (o.m_type != O_MAXSMT) return true; } return false; } return true; } lbool context::execute_lex() { lbool r = l_true; bool sc = scoped_lex(); IF_VERBOSE(1, verbose_stream() << "(opt :lex)\n";); unsigned sz = m_objectives.size(); 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); if (r == l_true && o.m_type == O_MINIMIZE && !get_lower_as_num(i).is_finite()) { return r; } if (r == l_true && o.m_type == O_MAXIMIZE && !get_upper_as_num(i).is_finite()) { return r; } if (r == l_true && i + 1 < sz) { update_lower(); } } DEBUG_CODE(if (r == l_true) validate_lex();); return r; } lbool context::execute_box() { if (m_box_index < m_box_models.size()) { m_model = m_box_models[m_box_index]; ++m_box_index; return l_true; } if (m_box_index < m_objectives.size()) { m_model = nullptr; ++m_box_index; return l_undef; } if (m_box_index != UINT_MAX && m_box_index >= m_objectives.size()) { m_box_index = UINT_MAX; return l_false; } m_box_index = 1; m_box_models.reset(); lbool r = m_optsmt.box(); for (unsigned i = 0, j = 0; r == l_true && i < m_objectives.size(); ++i) { objective const& obj = m_objectives[i]; if (obj.m_type == O_MAXSMT) { solver::scoped_push _sp(get_solver()); r = execute(obj, false, false); m_box_models.push_back(m_model.get()); } else { m_box_models.push_back(m_optsmt.get_model(j)); ++j; } } if (r == l_true && !m_box_models.empty()) { m_model = m_box_models[0]; } return r; } expr_ref context::mk_le(unsigned i, model_ref& mdl) { objective const& obj = m_objectives[i]; return mk_cmp(false, mdl, obj); } expr_ref context::mk_ge(unsigned i, model_ref& mdl) { objective const& obj = m_objectives[i]; return mk_cmp(true, mdl, obj); } expr_ref context::mk_gt(unsigned i, model_ref& mdl) { expr_ref result = mk_le(i, mdl); result = mk_not(m, result); return result; } expr_ref context::mk_cmp(bool is_ge, model_ref& mdl, objective const& obj) { rational k(0); expr_ref val(m), result(m); switch (obj.m_type) { case O_MINIMIZE: is_ge = !is_ge; case O_MAXIMIZE: val = (*mdl)(obj.m_term); if (is_numeral(val, k)) { if (is_ge) { result = mk_ge(obj.m_term, val); } else { result = mk_ge(val, obj.m_term); } } else { result = m.mk_true(); } break; case O_MAXSMT: { pb_util pb(m); unsigned sz = obj.m_terms.size(); ptr_vector terms; vector coeffs; for (unsigned i = 0; i < sz; ++i) { terms.push_back(obj.m_terms[i]); coeffs.push_back(obj.m_weights[i]); if (mdl->is_true(obj.m_terms[i])) { k += obj.m_weights[i]; } else { TRACE("opt", tout << (*mdl)(obj.m_terms[i]) << "\n";); } } if (is_ge) { result = pb.mk_ge(sz, coeffs.c_ptr(), terms.c_ptr(), k); } else { result = pb.mk_le(sz, coeffs.c_ptr(), terms.c_ptr(), k); } break; } } TRACE("opt", tout << (is_ge?">= ":"<= ") << k << "\n"; display_objective(tout, obj); tout << "\n"; tout << result << "\n";); return result; } expr_ref context::mk_ge(expr* t, expr* s) { expr_ref result(m); if (m_bv.is_bv(t)) { result = m_bv.mk_ule(s, t); } else { result = m_arith.mk_ge(t, s); } return result; } void context::yield() { SASSERT (m_pareto); m_pareto->get_model(m_model, m_labels); update_bound(true); update_bound(false); TRACE("opt", model_smt2_pp(tout, m, *m_model.get(), 0);); } lbool context::execute_pareto() { if (!m_pareto) { set_pareto(alloc(gia_pareto, m, *this, m_solver.get(), m_params)); } lbool is_sat = (*(m_pareto.get()))(); if (is_sat != l_true) { set_pareto(nullptr); } if (is_sat == l_true) { yield(); } return is_sat; } std::string context::reason_unknown() const { if (m.canceled()) { return Z3_CANCELED_MSG; } if (m_solver.get()) { return m_solver->reason_unknown(); } return m_unknown; } void context::display_bounds(std::ostream& out, bounds_t const& b) const { for (unsigned i = 0; i < m_objectives.size(); ++i) { objective const& obj = m_objectives[i]; display_objective(out, obj); if (obj.m_type == O_MAXIMIZE) { out << " |-> [" << b[i].first << ":" << b[i].second << "]\n"; } else { out << " |-> [" << -b[i].second << ":" << -b[i].first << "]\n"; } } } solver& context::get_solver() { return *m_solver.get(); } void context::init_solver() { setup_arith_solver(); m_opt_solver = alloc(opt_solver, m, m_params, *m_fm); m_opt_solver->set_logic(m_logic); m_solver = m_opt_solver.get(); m_opt_solver->ensure_pb(); //if (opt_params(m_params).priority() == symbol("pareto") || // (opt_params(m_params).priority() == symbol("lex") && m_objectives.size() > 1)) { //} } void context::setup_arith_solver() { opt_params p(m_params); if (p.optsmt_engine() == symbol("symba") || p.optsmt_engine() == symbol("farkas")) { std::stringstream strm; strm << AS_OPTINF; gparams::set("smt.arith.solver", strm.str().c_str()); } } void context::update_solver() { if (!m_enable_sat || !probe_bv()) { return; } if (m_maxsat_engine != symbol("maxres") && m_maxsat_engine != symbol("pd-maxres") && m_maxsat_engine != symbol("bcd2") && m_maxsat_engine != symbol("sls")) { return; } if (opt_params(m_params).priority() == symbol("pareto")) { return; } if (m.proofs_enabled()) { return; } m_params.set_bool("minimize_core_partial", true); m_params.set_bool("minimize_core", true); m_sat_solver = mk_inc_sat_solver(m, m_params); expr_ref_vector fmls(m); get_solver().get_assertions(fmls); m_sat_solver->assert_expr(fmls); m_solver = m_sat_solver.get(); } void context::enable_sls(bool force) { if ((force || m_enable_sls) && m_sat_solver.get()) { m_params.set_bool("optimize_model", true); m_sat_solver->updt_params(m_params); } } struct context::is_bv { struct found {}; ast_manager& m; pb_util pb; bv_util bv; is_bv(ast_manager& m): m(m), pb(m), bv(m) {} void operator()(var *) { throw found(); } void operator()(quantifier *) { throw found(); } void operator()(app *n) { family_id fid = n->get_family_id(); if (fid != m.get_basic_family_id() && fid != pb.get_family_id() && fid != bv.get_family_id() && !is_uninterp_const(n)) { throw found(); } } }; bool context::probe_bv() { expr_fast_mark1 visited; is_bv proc(m); try { for (objective& obj : m_objectives) { if (obj.m_type != O_MAXSMT) return false; maxsmt& ms = *m_maxsmts.find(obj.m_id); for (unsigned j = 0; j < ms.size(); ++j) { quick_for_each_expr(proc, visited, ms[j]); } } unsigned sz = get_solver().get_num_assertions(); for (unsigned i = 0; i < sz; i++) { quick_for_each_expr(proc, visited, get_solver().get_assertion(i)); } for (expr* f : m_hard_constraints) { quick_for_each_expr(proc, visited, f); } } catch (const is_bv::found &) { return false; } return true; } struct context::is_propositional_fn { struct found {}; ast_manager& m; is_propositional_fn(ast_manager& m): m(m) {} void operator()(var *) { throw found(); } void operator()(quantifier *) { throw found(); } void operator()(app *n) { family_id fid = n->get_family_id(); if (fid != m.get_basic_family_id() && !is_uninterp_const(n)) { throw found(); } } }; bool context::is_propositional(expr* p) { expr* np; if (is_uninterp_const(p) || (m.is_not(p, np) && is_uninterp_const(np))) { return true; } is_propositional_fn proc(m); expr_fast_mark1 visited; try { quick_for_each_expr(proc, visited, p); } catch (const is_propositional_fn::found &) { return false; } return true; } void context::add_maxsmt(symbol const& id, unsigned index) { maxsmt* ms = alloc(maxsmt, *this, index); ms->updt_params(m_params); m_maxsmts.insert(id, ms); } bool context::is_numeral(expr* e, rational & n) const { unsigned sz; return m_arith.is_numeral(e, n) || m_bv.is_numeral(e, n, sz); } void context::normalize(expr_ref_vector const& asms) { expr_ref_vector fmls(m); to_fmls(fmls); simplify_fmls(fmls, asms); from_fmls(fmls); } void context::simplify_fmls(expr_ref_vector& fmls, expr_ref_vector const& asms) { if (m_is_clausal) { return; } goal_ref g(alloc(goal, m, true, !asms.empty())); for (expr* fml : fmls) { g->assert_expr(fml); } for (expr * a : asms) { g->assert_expr(a, a); } tactic_ref tac0 = and_then(mk_simplify_tactic(m, m_params), mk_propagate_values_tactic(m), mk_solve_eqs_tactic(m), // NB: cannot ackermannize because max/min objectives would disappear // mk_ackermannize_bv_tactic(m, m_params), // NB: mk_elim_uncstr_tactic(m) is not sound with soft constraints mk_simplify_tactic(m)); opt_params optp(m_params); tactic_ref tac1, tac2, tac3, tac4; if (optp.elim_01()) { tac1 = mk_dt2bv_tactic(m); tac2 = mk_lia2card_tactic(m); tac3 = mk_eq2bv_tactic(m); params_ref lia_p; lia_p.set_bool("compile_equality", optp.pb_compile_equality()); tac2->updt_params(lia_p); set_simplify(and_then(tac0.get(), tac1.get(), tac2.get(), tac3.get(), mk_simplify_tactic(m))); } else { set_simplify(tac0.get()); } goal_ref_buffer result; TRACE("opt", g->display(tout);); (*m_simplify)(g, result); SASSERT(result.size() == 1); goal* r = result[0]; m_model_converter = r->mc(); fmls.reset(); expr_ref tmp(m); for (unsigned i = 0; i < r->size(); ++i) { if (asms.empty()) { fmls.push_back(r->form(i)); continue; } ptr_vector deps; expr_dependency_ref core(r->dep(i), m); m.linearize(core, deps); if (!deps.empty()) { fmls.push_back(m.mk_implies(m.mk_and(deps.size(), deps.c_ptr()), r->form(i))); } else { fmls.push_back(r->form(i)); } } if (r->inconsistent()) { ptr_vector core_elems; expr_dependency_ref core(r->dep(0), m); m.linearize(core, core_elems); m_core.append(core_elems.size(), core_elems.c_ptr()); } } bool context::is_maximize(expr* fml, app_ref& term, expr_ref& orig_term, unsigned& index) { if (is_app(fml) && m_objective_fns.find(to_app(fml)->get_decl(), index) && m_objectives[index].m_type == O_MAXIMIZE) { term = to_app(to_app(fml)->get_arg(0)); orig_term = m_objective_orig.find(to_app(fml)->get_decl()); return true; } return false; } bool context::is_minimize(expr* fml, app_ref& term, expr_ref& orig_term, unsigned& index) { if (is_app(fml) && m_objective_fns.find(to_app(fml)->get_decl(), index) && m_objectives[index].m_type == O_MINIMIZE) { term = to_app(to_app(fml)->get_arg(0)); orig_term = m_objective_orig.find(to_app(fml)->get_decl()); return true; } return false; } bool context::is_maxsat(expr* fml, expr_ref_vector& terms, vector& weights, rational& offset, 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); rational weight = m_objectives[index].m_weights[i]; if (weight.is_neg()) { weight.neg(); arg = mk_not(m, arg); offset -= weight; } if (m.is_true(arg)) { IF_VERBOSE(5, verbose_stream() << weight << ": " << mk_pp(m_objectives[index].m_terms[i].get(), m) << " |-> true\n";); } else if (weight.is_zero()) { // skip } else if (m.is_false(arg)) { IF_VERBOSE(5, verbose_stream() << weight << ": " << mk_pp(m_objectives[index].m_terms[i].get(), m) << " |-> false\n";); offset += weight; } else { terms.push_back(arg); weights.push_back(weight); } } id = m_objectives[index].m_id; return true; } app_ref term(m); offset = rational::zero(); bool is_max = is_maximize(fml, term, orig_term, index); bool is_min = !is_max && is_minimize(fml, term, orig_term, index); if (is_min && get_pb_sum(term, terms, weights, offset)) { TRACE("opt", tout << "try to convert minimization\n" << mk_pp(term, m) << "\n";); // minimize 2*x + 3*y // <=> // (assert-soft (not x) 2) // (assert-soft (not y) 3) // for (unsigned i = 0; i < weights.size(); ++i) { if (weights[i].is_neg()) { offset += weights[i]; weights[i].neg(); } else { terms[i] = mk_not(m, terms[i].get()); } } TRACE("opt", tout << "Convert minimization " << orig_term << "\n"; tout << "to maxsat: " << term << "\n"; for (unsigned i = 0; i < weights.size(); ++i) { tout << mk_pp(terms[i].get(), m) << ": " << weights[i] << "\n"; } tout << "offset: " << offset << "\n"; ); std::ostringstream out; out << orig_term << ":" << index; id = symbol(out.str().c_str()); return true; } if (is_max && get_pb_sum(term, terms, weights, offset)) { TRACE("opt", tout << "try to convert maximization " << mk_pp(term, m) << "\n";); // maximize 2*x + 3*y - z // <=> // (assert-soft x 2) // (assert-soft y 3) // (assert-soft (not z) 1) // offset := 6 // maximize = offset - penalty // for (unsigned i = 0; i < weights.size(); ++i) { if (weights[i].is_neg()) { weights[i].neg(); terms[i] = mk_not(m, terms[i].get()); } offset += weights[i]; } neg = true; std::ostringstream out; out << orig_term << ":" << index; id = symbol(out.str().c_str()); return true; } if ((is_max || is_min) && m_bv.is_bv(term)) { offset.reset(); unsigned bv_size = m_bv.get_bv_size(term); expr_ref val(m); val = m_bv.mk_numeral(is_max, 1); for (unsigned i = 0; i < bv_size; ++i) { rational w = power(rational(2),i); weights.push_back(w); terms.push_back(m.mk_eq(val, m_bv.mk_extract(i, i, term))); if (is_max) { offset += w; } } neg = is_max; std::ostringstream out; out << orig_term << ":" << index; id = symbol(out.str().c_str()); return true; } return false; } expr* context::mk_objective_fn(unsigned index, objective_t ty, unsigned sz, expr*const* args) { ptr_vector domain; for (unsigned i = 0; i < sz; ++i) { domain.push_back(m.get_sort(args[i])); } char const* name = ""; switch(ty) { case O_MAXIMIZE: name = "maximize"; break; case O_MINIMIZE: name = "minimize"; break; case O_MAXSMT: name = "maxsat"; break; default: break; } func_decl* f = m.mk_fresh_func_decl(name,"", domain.size(), domain.c_ptr(), m.mk_bool_sort()); m_objective_fns.insert(f, index); m_objective_refs.push_back(f); m_objective_orig.insert(f, sz > 0 ? args[0] : nullptr); return m.mk_app(f, sz, args); } expr* context::mk_maximize(unsigned index, app* t) { expr* t_ = t; return mk_objective_fn(index, O_MAXIMIZE, 1, &t_); } expr* context::mk_minimize(unsigned index, app* t) { expr* t_ = t; return mk_objective_fn(index, O_MINIMIZE, 1, &t_); } expr* context::mk_maxsat(unsigned index, unsigned num_fmls, expr* const* fmls) { return mk_objective_fn(index, O_MAXSMT, num_fmls, fmls); } void context::from_fmls(expr_ref_vector const& fmls) { TRACE("opt", tout << fmls << "\n";); m_hard_constraints.reset(); 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 = 0; symbol id; bool neg; if (is_maxsat(fml, terms, weights, offset, neg, id, orig_term, index)) { objective& obj = m_objectives[index]; if (obj.m_type != O_MAXSMT) { // change from maximize/minimize. obj.m_id = id; obj.m_type = O_MAXSMT; SASSERT(!m_maxsmts.contains(id)); add_maxsmt(id, index); } mk_atomic(terms); SASSERT(obj.m_id == id); obj.m_term = orig_term?to_app(orig_term):nullptr; obj.m_terms.reset(); obj.m_terms.append(terms); obj.m_weights.reset(); obj.m_weights.append(weights); obj.m_adjust_value.set_offset(offset); obj.m_adjust_value.set_negate(neg); m_maxsmts.find(id)->set_adjust_value(obj.m_adjust_value); TRACE("opt", tout << "maxsat: " << id << " offset:" << offset << "\n"; tout << terms << "\n";); } else if (is_maximize(fml, tr, orig_term, index)) { purify(tr); m_objectives[index].m_term = tr; } else if (is_minimize(fml, tr, orig_term, index)) { purify(tr); m_objectives[index].m_term = tr; m_objectives[index].m_adjust_value.set_negate(true); } else { m_hard_constraints.push_back(fml); } } // fix types of objectives: for (objective & obj : m_objectives) { expr* t = obj.m_term; switch(obj.m_type) { case O_MINIMIZE: case O_MAXIMIZE: if (!m_arith.is_int(t) && !m_arith.is_real(t)) { obj.m_term = m_arith.mk_numeral(rational(0), true); } break; default: break; } } } 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; model_ref mdl = md->copy(); fix_model(mdl); std::ostringstream buffer; buffer << prefix << (m_model_counter++) << ".smt2"; std::ofstream out(buffer.str()); if (out) { out << *mdl; out.close(); } #endif } bool context::verify_model(unsigned index, model* md, rational const& _v) { rational r; app_ref term = m_objectives[index].m_term; if (!term) { return true; } rational v = m_objectives[index].m_adjust_value(_v); expr_ref val(m); model_ref mdl = md->copy(); fix_model(mdl); val = (*mdl)(term); unsigned bvsz; if (!m_arith.is_numeral(val, r) && !m_bv.is_numeral(val, r, bvsz)) { TRACE("opt", tout << "model does not evaluate objective to a value but instead " << val << "\n"; tout << *mdl << "\n"; ); return false; } if (r != v) { TRACE("opt", tout << "Out of bounds: " << term << " " << val << " != " << v << "\n";); return false; } else { TRACE("opt", tout << "validated: " << term << " = " << val << "\n";); } return true; } void context::purify(app_ref& term) { generic_model_converter_ref fm; if (m_arith.is_add(term)) { expr_ref_vector args(m); for (expr* arg : *term) { if (is_mul_const(arg)) { args.push_back(arg); } else { args.push_back(purify(fm, arg)); } } term = m_arith.mk_add(args.size(), args.c_ptr()); } else if (m.is_ite(term) || !is_mul_const(term)) { TRACE("opt", tout << "Purifying " << term << "\n";); term = purify(fm, term); } if (fm) { m_model_converter = concat(m_model_converter.get(), fm.get()); } } bool context::is_mul_const(expr* e) { expr* e1, *e2; return is_uninterp_const(e) || m_arith.is_numeral(e) || (m_arith.is_mul(e, e1, e2) && m_arith.is_numeral(e1) && is_uninterp_const(e2)) || (m_arith.is_mul(e, e2, e1) && m_arith.is_numeral(e1) && is_uninterp_const(e2)); } app* context::purify(generic_model_converter_ref& fm, expr* term) { std::ostringstream out; out << mk_pp(term, m); app* q = m.mk_fresh_const(out.str(), m.get_sort(term)); if (!fm) fm = alloc(generic_model_converter, m, "opt"); if (m_arith.is_int_real(term)) { m_hard_constraints.push_back(m_arith.mk_ge(q, term)); m_hard_constraints.push_back(m_arith.mk_le(q, term)); } else { m_hard_constraints.push_back(m.mk_eq(q, term)); } fm->hide(q); return q; } /** To select the proper theory solver we have to ensure that all theory symbols from soft constraints are reflected in the hard constraints. - filter "obj" from generated model. */ void context::mk_atomic(expr_ref_vector& terms) { generic_model_converter_ref fm; for (unsigned i = 0; i < terms.size(); ++i) { expr_ref p(terms[i].get(), m); app_ref q(m); if (is_propositional(p)) { terms[i] = p; } else { terms[i] = purify(fm, p); } } if (fm) { m_model_converter = concat(m_model_converter.get(), fm.get()); } } void context::to_fmls(expr_ref_vector& fmls) { m_objective_fns.reset(); fmls.append(m_hard_constraints); for (unsigned i = 0; i < m_objectives.size(); ++i) { objective const& obj = m_objectives[i]; switch(obj.m_type) { case O_MINIMIZE: fmls.push_back(mk_minimize(i, obj.m_term)); break; case O_MAXIMIZE: fmls.push_back(mk_maximize(i, obj.m_term)); break; case O_MAXSMT: fmls.push_back(mk_maxsat(i, obj.m_terms.size(), obj.m_terms.c_ptr())); break; } } TRACE("opt", tout << fmls << "\n";); } void context::internalize() { for (objective & obj : m_objectives) { switch(obj.m_type) { case O_MINIMIZE: { app_ref tmp(m); tmp = obj.m_term; if (m_arith.is_int(tmp) || m_arith.is_real(tmp)) { tmp = m_arith.mk_uminus(obj.m_term); } obj.m_index = m_optsmt.add(tmp); break; } case O_MAXIMIZE: obj.m_index = m_optsmt.add(obj.m_term); break; case O_MAXSMT: { maxsmt& ms = *m_maxsmts.find(obj.m_id); for (unsigned j = 0; j < obj.m_terms.size(); ++j) { ms.add(obj.m_terms[j].get(), obj.m_weights[j]); } break; } } } } void context::update_bound(bool is_lower) { expr_ref val(m); if (!m_model.get()) return; for (objective const& obj : m_objectives) { rational r; switch(obj.m_type) { case O_MINIMIZE: { val = (*m_model)(obj.m_term); TRACE("opt", tout << obj.m_term << " " << val << "\n";); if (is_numeral(val, r)) { inf_eps val = inf_eps(obj.m_adjust_value(r)); TRACE("opt", tout << "adjusted value: " << val << "\n";); if (is_lower) { m_optsmt.update_lower(obj.m_index, val); } else { m_optsmt.update_upper(obj.m_index, val); } } break; } case O_MAXIMIZE: { val = (*m_model)(obj.m_term); TRACE("opt", tout << obj.m_term << " " << val << "\n";); if (is_numeral(val, r)) { inf_eps val = inf_eps(obj.m_adjust_value(r)); TRACE("opt", tout << "adjusted value: " << val << "\n";); if (is_lower) { m_optsmt.update_lower(obj.m_index, val); } else { m_optsmt.update_upper(obj.m_index, val); } } break; } case O_MAXSMT: { bool ok = true; for (unsigned j = 0; ok && j < obj.m_terms.size(); ++j) { val = (*m_model)(obj.m_terms[j]); TRACE("opt", tout << mk_pp(obj.m_terms[j], m) << " " << val << "\n";); if (!m.is_true(val)) { r += obj.m_weights[j]; } } if (ok) { maxsmt& ms = *m_maxsmts.find(obj.m_id); if (is_lower) { ms.update_upper(r); TRACE("opt", tout << "update upper from " << r << " to " << ms.get_upper() << "\n";); } else { ms.update_lower(r); TRACE("opt", tout << "update lower from " << r << " to " << ms.get_lower() << "\n";); } } break; } } } } void context::display_benchmark() { display(verbose_stream()); return; if (opt_params(m_params).dump_benchmarks() && sat_enabled() && m_objectives.size() == 1 && m_objectives[0].m_type == O_MAXSMT ) { objective& o = m_objectives[0]; unsigned sz = o.m_terms.size(); inc_sat_display(verbose_stream(), get_solver(), sz, o.m_terms.c_ptr(), o.m_weights.c_ptr()); } } void context::display(std::ostream& out) { display_assignment(out); } void context::display_assignment(std::ostream& out) { if (m_scoped_state.m_objectives.size() != m_objectives.size()) { throw default_exception("check-sat has not been called with latest objectives"); } out << "(objectives\n"; for (unsigned i = 0; i < m_scoped_state.m_objectives.size(); ++i) { objective const& obj = m_scoped_state.m_objectives[i]; out << " ("; display_objective(out, obj); if (get_lower_as_num(i) != get_upper_as_num(i)) { out << " (interval " << get_lower(i) << " " << get_upper(i) << ")"; } else { out << " " << get_lower(i); } out << ")\n"; } out << ")\n"; } void context::display_objective(std::ostream& out, objective const& obj) const { switch(obj.m_type) { case O_MAXSMT: { symbol s = obj.m_id; if (s != symbol::null) { out << s; } break; } default: out << obj.m_term; break; } } inf_eps context::get_lower_as_num(unsigned idx) { if (idx >= m_objectives.size()) { throw default_exception("index out of bounds"); } objective const& obj = m_objectives[idx]; switch(obj.m_type) { case O_MAXSMT: return inf_eps(m_maxsmts.find(obj.m_id)->get_lower()); case O_MINIMIZE: return obj.m_adjust_value(m_optsmt.get_upper(obj.m_index)); case O_MAXIMIZE: return obj.m_adjust_value(m_optsmt.get_lower(obj.m_index)); default: UNREACHABLE(); return inf_eps(); } } inf_eps context::get_upper_as_num(unsigned idx) { if (idx >= m_objectives.size()) { throw default_exception("index out of bounds"); } objective const& obj = m_objectives[idx]; switch(obj.m_type) { case O_MAXSMT: return inf_eps(m_maxsmts.find(obj.m_id)->get_upper()); case O_MINIMIZE: return obj.m_adjust_value(m_optsmt.get_lower(obj.m_index)); case O_MAXIMIZE: return obj.m_adjust_value(m_optsmt.get_upper(obj.m_index)); default: UNREACHABLE(); return inf_eps(); } } expr_ref context::get_lower(unsigned idx) { return to_expr(get_lower_as_num(idx)); } expr_ref context::get_upper(unsigned idx) { return to_expr(get_upper_as_num(idx)); } void context::to_exprs(inf_eps const& n, expr_ref_vector& es) { rational inf = n.get_infinity(); rational r = n.get_rational(); rational eps = n.get_infinitesimal(); es.push_back(m_arith.mk_numeral(inf, inf.is_int())); es.push_back(m_arith.mk_numeral(r, r.is_int())); es.push_back(m_arith.mk_numeral(eps, eps.is_int())); } expr_ref context::to_expr(inf_eps const& n) { rational inf = n.get_infinity(); rational r = n.get_rational(); rational eps = n.get_infinitesimal(); expr_ref_vector args(m); bool is_int = eps.is_zero() && r.is_int(); if (!inf.is_zero()) { expr* oo = m.mk_const(symbol("oo"), is_int ? m_arith.mk_int() : m_arith.mk_real()); if (inf.is_one()) { args.push_back(oo); } else { args.push_back(m_arith.mk_mul(m_arith.mk_numeral(inf, is_int), oo)); } } if (!r.is_zero()) { args.push_back(m_arith.mk_numeral(r, is_int)); } if (!eps.is_zero()) { expr* ep = m.mk_const(symbol("epsilon"), m_arith.mk_real()); if (eps.is_one()) { args.push_back(ep); } else { args.push_back(m_arith.mk_mul(m_arith.mk_numeral(eps, is_int), ep)); } } switch(args.size()) { case 0: return expr_ref(m_arith.mk_numeral(rational(0), true), m); case 1: return expr_ref(args[0].get(), m); default: return expr_ref(m_arith.mk_add(args.size(), args.c_ptr()), m); } } void context::set_simplify(tactic* tac) { m_simplify = tac; } void context::clear_state() { m_pareto = nullptr; m_box_index = UINT_MAX; m_model.reset(); m_model_fixed.reset(); m_core.reset(); } void context::set_pareto(pareto_base* p) { m_pareto = p; m_pareto1 = p != nullptr; } void context::collect_statistics(statistics& stats) const { if (m_solver) { m_solver->collect_statistics(stats); } if (m_simplify) { m_simplify->collect_statistics(stats); } for (auto const& kv : m_maxsmts) { kv.m_value->collect_statistics(stats); } get_memory_statistics(stats); get_rlimit_statistics(m.limit(), stats); if (m_qmax) { m_qmax->collect_statistics(stats); } } void context::collect_param_descrs(param_descrs & r) { opt_params::collect_param_descrs(r); insert_timeout(r); insert_ctrl_c(r); } void context::updt_params(params_ref const& p) { m_params.append(p); if (m_solver) { m_solver->updt_params(m_params); } if (m_sat_solver) { m_sat_solver->updt_params(m_params); } m_optsmt.updt_params(m_params); for (auto & kv : m_maxsmts) { kv.m_value->updt_params(m_params); } opt_params _p(p); m_enable_sat = _p.enable_sat(); m_enable_sls = _p.enable_sls(); m_maxsat_engine = _p.maxsat_engine(); m_pp_neat = _p.pp_neat(); } std::string context::to_string() const { return to_string(false, m_scoped_state.m_hard, m_scoped_state.m_objectives); } std::string context::to_string_internal() const { return to_string(true, m_hard_constraints, m_objectives); } std::string context::to_string(bool is_internal, expr_ref_vector const& hard, vector const& objectives) const { smt2_pp_environment_dbg env(m); ast_pp_util visitor(m); std::ostringstream out; visitor.collect(hard); model_converter_ref mc = concat(m_model_converter.get(), m_fm.get()); for (objective const& obj : objectives) { switch(obj.m_type) { case O_MAXIMIZE: case O_MINIMIZE: visitor.collect(obj.m_term); break; case O_MAXSMT: visitor.collect(obj.m_terms); break; default: UNREACHABLE(); break; } } if (is_internal && mc) { mc->set_env(&visitor); } param_descrs descrs; collect_param_descrs(descrs); m_params.display_smt2(out, "opt", descrs); visitor.display_decls(out); visitor.display_asserts(out, hard, m_pp_neat); for (objective const& obj : objectives) { switch(obj.m_type) { case O_MAXIMIZE: out << "(maximize "; ast_smt2_pp(out, obj.m_term, env); out << ")\n"; break; case O_MINIMIZE: out << "(minimize "; ast_smt2_pp(out, obj.m_term, env); out << ")\n"; break; case O_MAXSMT: for (unsigned j = 0; j < obj.m_terms.size(); ++j) { out << "(assert-soft "; ast_smt2_pp(out, obj.m_terms[j], env); rational w = obj.m_weights[j]; w.display_decimal(out << " :weight ", 3, true); if (obj.m_id != symbol::null) { if (is_smt2_quoted_symbol(obj.m_id)) { out << " :id " << mk_smt2_quoted_symbol(obj.m_id); } else { out << " :id " << obj.m_id; } } out << ")\n"; } break; default: UNREACHABLE(); break; } } if (is_internal && mc) { mc->display(out); } if (is_internal && mc) { mc->set_env(nullptr); } out << "(check-sat)\n"; return out.str(); } void context::validate_model() { return; if (!gparams::get_ref().get_bool("model_validate", false)) return; expr_ref_vector fmls(m); get_hard_constraints(fmls); expr_ref tmp(m); model_ref mdl; get_model(mdl); mdl->set_model_completion(true); for (expr * f : fmls) { if (!mdl->is_true(f)) { IF_VERBOSE(0, verbose_stream() << "Failed to validate " << mk_pp(f, m) << "\n" << tmp << "\n"; m_fm->display(verbose_stream() << "fm\n"); m_model_converter->display(verbose_stream() << "mc\n"); model_smt2_pp(verbose_stream(), m, *mdl, 0); verbose_stream() << to_string_internal() << "\n"); } } } void context::validate_maxsat(symbol const& id) { maxsmt& ms = *m_maxsmts.find(id); TRACE("opt", tout << "Validate: " << id << "\n";); for (objective const& obj : m_objectives) { if (obj.m_id == id && obj.m_type == O_MAXSMT) { SASSERT(obj.m_type == O_MAXSMT); rational value(0); expr_ref val(m); for (unsigned i = 0; i < obj.m_terms.size(); ++i) { auto const& t = obj.m_terms[i]; if (!m_model->is_true(t)) { value += obj.m_weights[i]; } // TBD: check that optimal was not changed. } value = obj.m_adjust_value(value); rational value0 = ms.get_lower(); TRACE("opt", tout << "value " << value << " " << value0 << "\n";); // TBD is this correct? SASSERT(value == value0); } } } 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) { case O_MINIMIZE: case O_MAXIMIZE: { inf_eps n = m_optsmt.get_lower(obj.m_index); if (m_optsmt.objective_is_model_valid(obj.m_index) && n.get_infinity().is_zero() && n.get_infinitesimal().is_zero() && is_numeral((*m_model)(obj.m_term), r1)) { rational r2 = n.get_rational(); if (obj.m_type == O_MINIMIZE) { r1.neg(); } CTRACE("opt", r1 != r2, tout << obj.m_term << " evaluates to " << r1 << " but has objective " << r2 << "\n";); CTRACE("opt", r1 != r2, tout << *m_model;); SASSERT(r1 == r2); } break; } case O_MAXSMT: { rational value(0); for (unsigned i = 0; i < obj.m_terms.size(); ++i) { if (!m_model->is_true(obj.m_terms[i])) { value += obj.m_weights[i]; } // TBD: check that optimal was not changed. } 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; } } } } bool context::is_qsat_opt() { if (m_objectives.size() != 1) { return false; } if (m_objectives[0].m_type != O_MAXIMIZE && m_objectives[0].m_type != O_MINIMIZE) { return false; } if (!m_arith.is_real(m_objectives[0].m_term)) { return false; } for (expr* fml : m_hard_constraints) { if (has_quantifiers(fml)) { return true; } } return false; } lbool context::run_qsat_opt() { SASSERT(is_qsat_opt()); objective const& obj = m_objectives[0]; app_ref term(obj.m_term); if (obj.m_type == O_MINIMIZE) { term = m_arith.mk_uminus(term); } inf_eps value; m_qmax = alloc(qe::qmax, m, m_params); lbool result = (*m_qmax)(m_hard_constraints, term, value, m_model); if (result != l_undef && obj.m_type == O_MINIMIZE) { value.neg(); } m_optsmt.setup(*m_opt_solver.get()); if (result == l_undef) { if (obj.m_type == O_MINIMIZE) { m_optsmt.update_upper(obj.m_index, value); } else { m_optsmt.update_lower(obj.m_index, value); } } else { m_optsmt.update_lower(obj.m_index, value); m_optsmt.update_upper(obj.m_index, value); } return result; } } z3-z3-4.8.7/src/opt/opt_context.h000066400000000000000000000307621356505360400165470ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: opt_context.h Abstract: Facility for running optimization problem. Author: Anh-Dung Phan (t-anphan) 2013-10-16 Notes: --*/ #ifndef OPT_CONTEXT_H_ #define OPT_CONTEXT_H_ #include "ast/ast.h" #include "ast/arith_decl_plugin.h" #include "ast/bv_decl_plugin.h" #include "tactic/model_converter.h" #include "tactic/tactic.h" #include "qe/qsat.h" #include "opt/opt_solver.h" #include "opt/opt_pareto.h" #include "opt/optsmt.h" #include "opt/maxsmt.h" #include "cmd_context/cmd_context.h" namespace opt { class opt_solver; /** \brief base class required by MaxSMT solvers. By implementing a base class, you can invoke the MaxSMT solvers independent of the overall optimization infrastructure. The caller has to supply a solver object that encapsulates an incremental SAT or SMT solver. The MaxSMT solvers may assume that the solver object should be in a satisfiable state and contain an initial model. */ class maxsat_context { public: virtual generic_model_converter& fm() = 0; // converter that removes fresh names introduced by simplification. virtual bool sat_enabled() const = 0; // is using th SAT solver core enabled? virtual solver& get_solver() = 0; // retrieve solver object (SAT or SMT solver) virtual ast_manager& get_manager() const = 0; virtual params_ref& params() = 0; virtual void enable_sls(bool force) = 0; // stochastic local search virtual symbol const& maxsat_engine() const = 0; // retrieve maxsat engine configuration parameter. virtual void get_base_model(model_ref& _m) = 0; // retrieve model from initial satisfiability call. virtual smt::context& smt_context() = 0; // access SMT context for SMT based MaxSMT solver (wmax requires SMT core) virtual unsigned num_objectives() = 0; virtual bool verify_model(unsigned id, model* mdl, rational const& v) = 0; virtual void set_model(model_ref& _m) = 0; virtual void model_updated(model* mdl) = 0; }; /** \brief main context object for optimization. Hard and soft assertions, and objectives are registered with this context. It handles combinations of objectives. */ class context : public opt_wrapper, public pareto_callback, public maxsat_context { typedef map map_t; typedef map map_id; typedef vector > bounds_t; enum objective_t { O_MAXIMIZE, O_MINIMIZE, O_MAXSMT }; struct objective { objective_t m_type; app_ref m_term; // for maximize, minimize term expr_ref_vector m_terms; // for maxsmt vector m_weights; // for maxsmt adjust_value m_adjust_value; symbol m_id; // for maxsmt unsigned m_index; // for maximize/minimize index objective(bool is_max, app_ref& t, unsigned idx): m_type(is_max?O_MAXIMIZE:O_MINIMIZE), m_term(t), m_terms(t.get_manager()), m_id(), m_index(idx) { if (!is_max) { m_adjust_value.set_negate(true); } } objective(ast_manager& m, symbol id): m_type(O_MAXSMT), m_term(m), m_terms(m), m_id(id), m_index(0) {} }; class scoped_state { ast_manager& m; 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_asms; vector m_objectives; scoped_state(ast_manager& m): m(m), m_arith(m), m_bv(m), m_hard(m), m_asms(m) {} void push(); void pop(); void add(expr* hard); bool set(expr_ref_vector const& hard); unsigned add(expr* soft, rational const& weight, symbol const& id); unsigned add(app* obj, bool is_max); unsigned get_index(symbol const& id) { return m_indices[id]; } }; ast_manager& m; arith_util m_arith; bv_util m_bv; expr_ref_vector m_hard_constraints; ref m_opt_solver; ref m_solver; ref m_sat_solver; scoped_ptr m_pareto; bool m_pareto1; scoped_ptr m_qmax; sref_vector m_box_models; unsigned m_box_index; params_ref m_params; optsmt m_optsmt; map_t m_maxsmts; scoped_state m_scoped_state; vector m_objectives; model_ref m_model; model_converter_ref m_model_converter; generic_model_converter_ref m_fm; sref_vector m_model_fixed; unsigned m_model_counter; obj_map m_objective_fns; obj_map m_objective_orig; func_decl_ref_vector m_objective_refs; expr_ref_vector m_core; tactic_ref m_simplify; bool m_enable_sat; bool m_enable_sls; bool m_is_clausal; bool m_pp_neat; symbol m_maxsat_engine; symbol m_logic; svector m_labels; std::string m_unknown; public: context(ast_manager& m); ~context() override; 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_vector get_hard_constraints() { expr_ref_vector hard(m); get_hard_constraints(hard); return hard; } expr_ref get_objective(unsigned i); void push() override; void pop(unsigned n) override; 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; 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; void collect_statistics(statistics& stats) const override; proof* get_proof() override { return nullptr; } void get_labels(svector & r) override; void get_unsat_core(expr_ref_vector & r) override; std::string reason_unknown() const override; void set_reason_unknown(char const* msg) override { m_unknown = msg; } void display_assignment(std::ostream& out) override; bool is_pareto() override { return m_pareto.get() != nullptr; } void set_logic(symbol const& s) override { m_logic = s; } void set_clausal(bool f) { m_is_clausal = f; } void display(std::ostream& out); static void collect_param_descrs(param_descrs & r); void updt_params(params_ref const& p) override; params_ref& get_params() { return m_params; } expr_ref get_lower(unsigned idx); expr_ref get_upper(unsigned idx); void get_lower(unsigned idx, expr_ref_vector& es) { to_exprs(get_lower_as_num(idx), es); } void get_upper(unsigned idx, expr_ref_vector& es) { to_exprs(get_upper_as_num(idx), es); } std::string to_string() const; unsigned num_objectives() override { return m_scoped_state.m_objectives.size(); } expr_ref mk_gt(unsigned i, model_ref& model) override; expr_ref mk_ge(unsigned i, model_ref& model) override; expr_ref mk_le(unsigned i, model_ref& model) override; generic_model_converter& fm() override { return *m_fm; } smt::context& smt_context() override { return m_opt_solver->get_context(); } bool sat_enabled() const override { return nullptr != m_sat_solver.get(); } solver& get_solver() override; ast_manager& get_manager() const override { return this->m; } params_ref& params() override { return m_params; } void enable_sls(bool force) override; symbol const& maxsat_engine() const override { return m_maxsat_engine; } void get_base_model(model_ref& _m) override; bool verify_model(unsigned id, model* mdl, rational const& v) override; void model_updated(model* mdl) override; private: lbool execute(objective const& obj, bool committed, bool scoped); lbool execute_min_max(unsigned index, bool committed, bool scoped, bool is_max); lbool execute_maxsat(symbol const& s, bool committed, bool scoped); lbool execute_lex(); lbool execute_box(); lbool execute_pareto(); lbool adjust_unknown(lbool r); bool scoped_lex(); bool contains_quantifiers() const; expr_ref to_expr(inf_eps const& n); void to_exprs(inf_eps const& n, expr_ref_vector& es); void reset_maxsmts(); void import_scoped_state(); void normalize(expr_ref_vector const& asms); void internalize(); bool is_maximize(expr* fml, app_ref& term, expr_ref& orig_term, unsigned& index); bool is_minimize(expr* fml, app_ref& term, expr_ref& orig_term, unsigned& index); bool is_maxsat(expr* fml, expr_ref_vector& terms, vector& weights, rational& offset, bool& neg, symbol& id, expr_ref& orig_term, unsigned& index); void purify(app_ref& term); app* purify(generic_model_converter_ref& fm, expr* e); bool is_mul_const(expr* e); expr* mk_maximize(unsigned index, app* t); expr* mk_minimize(unsigned index, app* t); expr* mk_maxsat(unsigned index, unsigned num_fmls, expr* const* fmls); expr* mk_objective_fn(unsigned index, objective_t ty, unsigned sz, expr*const* args); void to_fmls(expr_ref_vector& fmls); void from_fmls(expr_ref_vector const& fmls); void simplify_fmls(expr_ref_vector& fmls, expr_ref_vector const& asms); void mk_atomic(expr_ref_vector& terms); void update_lower() { update_bound(true); } void update_bound(bool is_lower); inf_eps get_lower_as_num(unsigned idx); inf_eps get_upper_as_num(unsigned idx); struct is_bv; bool probe_bv(); struct is_propositional_fn; bool is_propositional(expr* e); void init_solver(); void update_solver(); void setup_arith_solver(); void add_maxsmt(symbol const& id, unsigned index); void set_simplify(tactic *simplify); void set_pareto(pareto_base* p); void clear_state(); bool is_numeral(expr* e, rational& n) const; void display_objective(std::ostream& out, objective const& obj) const; void display_bounds(std::ostream& out, bounds_t const& b) const; std::string to_string(bool is_internal, expr_ref_vector const& hard, vector const& objectives) const; std::string to_string_internal() const; void validate_lex(); void validate_maxsat(symbol const& id); void validate_model(); void display_benchmark(); // pareto void yield(); expr_ref mk_ge(expr* t, expr* s); expr_ref mk_cmp(bool is_ge, model_ref& mdl, objective const& obj); // quantifiers bool is_qsat_opt(); lbool run_qsat_opt(); }; } #endif z3-z3-4.8.7/src/opt/opt_params.pyg000066400000000000000000000052541356505360400167140ustar00rootroot00000000000000def_module_params('opt', description='optimization parameters', export=True, params=(('optsmt_engine', SYMBOL, 'basic', "select optimization engine: 'basic', 'farkas', 'symba'"), ('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)'), ('enable_sls', BOOL, False, 'enable SLS tuning during weighted maxsast'), ('enable_sat', BOOL, True, 'enable the new SAT core for propositional constraints'), ('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'), ('maxres.max_core_size', UINT, 3, 'break batch of generated cores if size reaches this number'), ('maxres.maximize_assignment', BOOL, False, 'find an MSS/MCS to improve current assignment'), ('maxres.max_correction_set_size', UINT, 3, 'allow generating correction set constraints up to maximal size'), ('maxres.wmax', BOOL, False, 'use weighted theory solver to constrain upper bounds'), ('maxres.pivot_on_correction_set', BOOL, True, 'reduce soft constraints if the current correction set is smaller than current core') )) z3-z3-4.8.7/src/opt/opt_pareto.cpp000066400000000000000000000061151356505360400167030ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: opt_pareto.cpp Abstract: Pareto front utilities Author: Nikolaj Bjorner (nbjorner) 2014-4-24 Notes: --*/ #include "opt/opt_pareto.h" #include "ast/ast_pp.h" #include "ast/ast_util.h" #include "model/model_smt2_pp.h" namespace opt { // --------------------- // GIA pareto algorithm lbool gia_pareto::operator()() { expr_ref fml(m); lbool is_sat = m_solver->check_sat(0, nullptr); if (is_sat == l_true) { { solver::scoped_push _s(*m_solver.get()); while (is_sat == l_true) { if (m.canceled()) { return l_undef; } m_solver->get_model(m_model); m_solver->get_labels(m_labels); m_model->set_model_completion(true); IF_VERBOSE(1, model_ref mdl(m_model); cb.fix_model(mdl); model_smt2_pp(verbose_stream() << "new model:\n", m, *mdl, 0);); // TBD: we can also use local search to tune solution coordinate-wise. mk_dominates(); is_sat = m_solver->check_sat(0, nullptr); } } if (is_sat == l_undef) { return l_undef; } SASSERT(is_sat == l_false); is_sat = l_true; mk_not_dominated_by(); } return is_sat; } void pareto_base::mk_dominates() { unsigned sz = cb.num_objectives(); expr_ref fml(m); expr_ref_vector gt(m), fmls(m); for (unsigned i = 0; i < sz; ++i) { fmls.push_back(cb.mk_ge(i, m_model)); gt.push_back(cb.mk_gt(i, m_model)); } fmls.push_back(mk_or(gt)); fml = mk_and(fmls); IF_VERBOSE(10, verbose_stream() << "dominates: " << fml << "\n";); TRACE("opt", model_smt2_pp(tout << fml << "\n", m, *m_model, 0);); m_solver->assert_expr(fml); } void pareto_base::mk_not_dominated_by() { unsigned sz = cb.num_objectives(); expr_ref fml(m); expr_ref_vector le(m); for (unsigned i = 0; i < sz; ++i) { le.push_back(cb.mk_le(i, m_model)); } fml = m.mk_not(mk_and(le)); IF_VERBOSE(10, verbose_stream() << "not dominated by: " << fml << "\n";); TRACE("opt", tout << fml << "\n";); m_solver->assert_expr(fml); } // --------------------------------- // OIA algorithm (without filtering) lbool oia_pareto::operator()() { solver::scoped_push _s(*m_solver.get()); lbool is_sat = m_solver->check_sat(0, nullptr); if (m.canceled()) { is_sat = l_undef; } if (is_sat == l_true) { m_solver->get_model(m_model); m_solver->get_labels(m_labels); m_model->set_model_completion(true); mk_not_dominated_by(); } return is_sat; } } z3-z3-4.8.7/src/opt/opt_pareto.h000066400000000000000000000051561356505360400163540ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: opt_pareto.h Abstract: Pareto front utilities Author: Nikolaj Bjorner (nbjorner) 2014-4-24 Notes: --*/ #ifndef OPT_PARETO_H_ #define OPT_PARETO_H_ #include "solver/solver.h" #include "model/model.h" namespace opt { class pareto_callback { public: virtual unsigned num_objectives() = 0; virtual expr_ref mk_gt(unsigned i, model_ref& model) = 0; virtual expr_ref mk_ge(unsigned i, model_ref& model) = 0; virtual expr_ref mk_le(unsigned i, model_ref& model) = 0; virtual void fix_model(model_ref& m) = 0; }; class pareto_base { protected: ast_manager& m; pareto_callback& cb; ref m_solver; params_ref m_params; model_ref m_model; svector m_labels; public: pareto_base( ast_manager & m, pareto_callback& cb, solver* s, params_ref & p): m(m), cb(cb), m_solver(s), m_params(p) { } virtual ~pareto_base() {} virtual void updt_params(params_ref & p) { m_solver->updt_params(p); m_params.copy(p); } virtual void collect_param_descrs(param_descrs & r) { m_solver->collect_param_descrs(r); } virtual void collect_statistics(statistics & st) const { m_solver->collect_statistics(st); } virtual void display(std::ostream & out) const { m_solver->display(out); } virtual lbool operator()() = 0; virtual void get_model(model_ref& mdl, svector& labels) { mdl = m_model; labels = m_labels; } protected: void mk_dominates(); void mk_not_dominated_by(); }; class gia_pareto : public pareto_base { public: gia_pareto(ast_manager & m, pareto_callback& cb, solver* s, params_ref & p): pareto_base(m, cb, s, p) { } ~gia_pareto() override {} lbool operator()() override; }; // opportunistic improvement algorithm. class oia_pareto : public pareto_base { public: oia_pareto(ast_manager & m, pareto_callback& cb, solver* s, params_ref & p): pareto_base(m, cb, s, p) { } ~oia_pareto() override {} lbool operator()() override; }; } #endif z3-z3-4.8.7/src/opt/opt_parse.cpp000066400000000000000000000604601356505360400165260ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: opt_parse.cpp Abstract: Parse utilities for optimization. Author: Nikolaj Bjorner (nbjorner) 2017-11-19 Revision History: --*/ #include "opt/opt_context.h" #include "opt/opt_parse.h" class opt_stream_buffer { std::istream & m_stream; int m_val; unsigned m_line; public: opt_stream_buffer(std::istream & s): m_stream(s), m_line(0) { m_val = m_stream.get(); } int operator *() const { return m_val;} void operator ++() { m_val = m_stream.get(); } int ch() const { return m_val; } void next() { m_val = m_stream.get(); } bool eof() const { return ch() == EOF; } unsigned line() const { return m_line; } void skip_whitespace() { while ((ch() >= 9 && ch() <= 13) || ch() == 32) { if (ch() == 10) ++m_line; next(); } } void skip_space() { while (ch() != 10 && ((ch() >= 9 && ch() <= 13) || ch() == 32)) next(); } void skip_line() { while(true) { if (eof()) { return; } if (ch() == '\n') { ++m_line; next(); return; } next(); } } bool parse_token(char const* token); int parse_int(); unsigned parse_unsigned(); }; bool opt_stream_buffer::parse_token(char const* token) { skip_whitespace(); char const* t = token; while (ch() == *t) { next(); ++t; } return 0 == *t; } unsigned opt_stream_buffer::parse_unsigned() { skip_space(); if (ch() == '\n') { return UINT_MAX; } unsigned val = 0; while (ch() >= '0' && ch() <= '9') { val = val*10 + (ch() - '0'); next(); } return val; } int opt_stream_buffer::parse_int() { int val = 0; bool neg = false; skip_whitespace(); if (ch() == '-') { neg = true; next(); } else if (ch() == '+') { next(); } if (ch() < '0' || ch() > '9') { std::cerr << "(error line " << line() << " \"unexpected char: " << ((char)ch()) << "\" )\n"; exit(3); } while (ch() >= '0' && ch() <= '9') { val = val*10 + (ch() - '0'); next(); } return neg ? -val : val; } class wcnf { opt::context& opt; ast_manager& m; opt_stream_buffer& in; unsigned_vector& m_handles; app_ref read_clause(unsigned& weight) { int parsed_lit; int var; weight = in.parse_unsigned(); app_ref result(m), p(m); expr_ref_vector ors(m); while (true) { parsed_lit = in.parse_int(); if (parsed_lit == 0) break; var = abs(parsed_lit); p = m.mk_const(symbol(var), m.mk_bool_sort()); if (parsed_lit < 0) p = m.mk_not(p); ors.push_back(p); } result = to_app(mk_or(m, ors.size(), ors.c_ptr())); return result; } void parse_spec(unsigned& num_vars, unsigned& num_clauses, unsigned& max_weight) { in.parse_token("wcnf"); num_vars = in.parse_unsigned(); num_clauses = in.parse_unsigned(); max_weight = in.parse_unsigned(); } public: wcnf(opt::context& opt, opt_stream_buffer& in, unsigned_vector& h): opt(opt), m(opt.get_manager()), in(in), m_handles(h) { opt.set_clausal(true); } void parse() { unsigned num_vars = 0, num_clauses = 0, max_weight = 0; while (true) { in.skip_whitespace(); if (in.eof()) { break; } else if (*in == 'c') { in.skip_line(); } else if (*in == 'p') { ++in; parse_spec(num_vars, num_clauses, max_weight); } else { unsigned weight = 0; app_ref cls = read_clause(weight); if (weight >= max_weight) { opt.add_hard_constraint(cls); } else { unsigned id = opt.add_soft_constraint(cls, rational(weight), symbol::null); if (m_handles.empty()) { m_handles.push_back(id); } } } } } }; class opb { opt::context& opt; ast_manager& m; opt_stream_buffer& in; unsigned_vector& m_handles; arith_util arith; app_ref parse_id() { bool negated = in.parse_token("~"); if (!in.parse_token("x")) { std::cerr << "(error line " << in.line() << " \"unexpected char: " << ((char)in.ch()) << "\" expected \"x\")\n"; exit(3); } app_ref p(m); int id = in.parse_int(); p = m.mk_const(symbol(id), m.mk_bool_sort()); if (negated) p = m.mk_not(p); in.skip_whitespace(); return p; } app_ref parse_ids() { app_ref result = parse_id(); while (*in == '~' || *in == 'x') { result = m.mk_and(result, parse_id()); } return result; } rational parse_coeff_r() { in.skip_whitespace(); svector num; bool pos = true; if (*in == '-') pos = false, ++in; if (*in == '+') ++in; if (!pos) num.push_back('-'); in.skip_whitespace(); while ('0' <= *in && *in <='9') num.push_back(*in), ++in; num.push_back(0); return rational(num.c_ptr()); } app_ref parse_coeff() { return app_ref(arith.mk_numeral(parse_coeff_r(), true), m); } app_ref parse_term() { app_ref c = parse_coeff(); app_ref e = parse_ids(); return app_ref(m.mk_ite(e, c, arith.mk_numeral(rational(0), true)), m); } void parse_objective(bool is_min) { app_ref t = parse_term(); while (!in.parse_token(";") && !in.eof()) { if (is_min) { t = arith.mk_add(t, parse_term()); } else { t = arith.mk_sub(t, parse_term()); } } m_handles.push_back(opt.add_objective(t, false)); } void parse_constraint() { app_ref t = parse_term(); while (!in.eof()) { if (in.parse_token(">=")) { t = arith.mk_ge(t, parse_coeff()); in.parse_token(";"); break; } if (in.parse_token("=")) { t = m.mk_eq(t, parse_coeff()); in.parse_token(";"); break; } if (in.parse_token("<=")) { t = arith.mk_le(t, parse_coeff()); in.parse_token(";"); break; } t = arith.mk_add(t, parse_term()); } opt.add_hard_constraint(t); } public: opb(opt::context& opt, opt_stream_buffer& in, unsigned_vector& h): opt(opt), m(opt.get_manager()), in(in), m_handles(h), arith(m) {} void parse() { while (true) { in.skip_whitespace(); if (in.eof()) { break; } else if (*in == '*') { in.skip_line(); } else if (in.parse_token("min:")) { parse_objective(true); } else if (in.parse_token("max:")) { parse_objective(false); } else { parse_constraint(); } } } }; void parse_wcnf(opt::context& opt, std::istream& is, unsigned_vector& h) { opt_stream_buffer _is(is); wcnf w(opt, _is, h); w.parse(); } void parse_opb(opt::context& opt, std::istream& is, unsigned_vector& h) { opt_stream_buffer _is(is); opb opb(opt, _is, h); opb.parse(); } /** * \brief Parser for a modest subset of the CPLEX LP format. * Reference: http://eaton.math.rpi.edu/cplex90html/reffileformatscplex/reffileformatscplex5.html */ struct asymbol { bool m_is_num; symbol m_sym; rational m_num; unsigned m_line; asymbol(symbol const& s, unsigned l): m_is_num(false), m_sym(s), m_line(l) {} asymbol(rational const& r, unsigned l): m_is_num(true), m_num(r), m_line(l) {} }; std::ostream& operator<<(std::ostream& out, asymbol const& c) { if (c.m_is_num) { return out << c.m_num; } else { return out << c.m_sym; } } class lp_tokenizer { vector m_tokens; unsigned m_pos; svector m_buffer; public: lp_tokenizer(opt_stream_buffer& in): m_pos(0) { parse_all(in); } symbol const& peek(unsigned i) { if (i + m_pos >= m_tokens.size()) { return symbol::null; } return m_tokens[i + m_pos].m_sym; } bool peek_num(unsigned i) { if (i + m_pos >= m_tokens.size()) { return false; } return m_tokens[i + m_pos].m_is_num; } rational const& get_num(unsigned i) { return m_tokens[i + m_pos].m_num; } void next(unsigned delta = 1) { m_pos += delta; } bool eof() const { return m_pos == m_tokens.size(); } unsigned line() const { if (m_pos < m_tokens.size()) return m_tokens[m_pos].m_line; return 0; } private: bool is_separator(char c) { return c == '\n' || c == '\\' || c == '*' || c == '+'; } char lower(char c) { if ('A' <= c && c <= 'Z') return c - ('A' - 'a'); return c; } void parse_all(opt_stream_buffer& in) { while (!in.eof()) { in.skip_whitespace(); char c = in.ch(); if (c == '\\') { in.skip_line(); continue; } bool neg = false; if (c == '-') { in.next(); c = in.ch(); m_buffer.reset(); m_buffer.push_back('-'); if (is_num(c)) { neg = true; } else { while (!is_ws(c) && !in.eof()) { m_buffer.push_back(c); in.next(); c = in.ch(); } m_buffer.push_back(0); m_tokens.push_back(asymbol(symbol(m_buffer.c_ptr()), in.line())); IF_VERBOSE(10, verbose_stream() << "tok: " << m_tokens.back() << "\n"); continue; } } if (is_num(c)) { rational n(0); rational div(1); while (is_num(c) && !in.eof()) { n = n*rational(10) + rational(c - '0'); in.next(); c = in.ch(); } if (c == '.') { in.next(); c = in.ch(); while (is_num(c) && !in.eof()) { n = n*rational(10) + rational(c - '0'); in.next(); div *= rational(10); c = in.ch(); } } if (div > rational(1)) n = n / div; if (neg) n.neg(); m_tokens.push_back(asymbol(n, in.line())); IF_VERBOSE(10, verbose_stream() << "num: " << m_tokens.back() << "\n"); continue; } m_buffer.reset(); if (is_alpha(c)) { while (is_sym(c) && !in.eof()) { m_buffer.push_back(lower(c)); in.next(); c = in.ch(); } } else { while (!is_ws(c) && !in.eof()) { m_buffer.push_back(c); in.next(); c = in.ch(); } } m_buffer.push_back(0); m_tokens.push_back(asymbol(symbol(m_buffer.c_ptr()), in.line())); IF_VERBOSE(10, verbose_stream() << "tok: " << m_tokens.back() << "\n"); } } bool is_alpha(char c) const { return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'); } bool is_num(char c) const { return '0' <= c && c <= '9'; } bool is_ws(char c) const { return c == ' ' || c == '\n' || c == '\t'; } bool is_sym(char c) const { return is_alpha(c) || is_num(c) || c == '!' || c == '"' || c == '-' || c == '#' || c == '$' || c == '%' || c == '&' || c == '{' || c == '}' || c == ',' || c == '_' || c == '.' || c == ';' || c == '?' || c == '@' || c == '`' || c == '\'' || c == '(' || c == ')' || c == '~'; } }; class lp_parse { typedef vector > lin_term; struct objective { bool m_is_max; symbol m_name; lin_term m_expr; }; enum rel_op { le, ge, eq }; struct constraint { symbol m_name; symbol m_bvar; rational m_bval; lin_term m_expr; rel_op m_rel; rational m_bound; constraint(symbol const& name, symbol const& v, rational const& val, lin_term& terms, rel_op r, rational const& bound): m_name(name), m_bvar(v), m_bval(val), m_expr(terms), m_rel(r), m_bound(bound) {} }; struct bound { optional m_lo, m_hi; bool m_int; bound() : m_int(false) {} }; opt::context& opt; unsigned_vector& m_h; lp_tokenizer tok; objective m_objective; vector m_constraints; map m_bounds; public: lp_parse(opt::context& opt, opt_stream_buffer& in, unsigned_vector& h) : opt(opt), m_h(h), tok(in) {} void parse() { parse_objective(); if (!try_subject_to()) { error("subject to section expected"); return; } while (!is_section()) { parse_constraint(); } while (true) { if (is_bounds()) { tok.next(); while (!is_section()) { parse_bound(); } } else if (is_binary()) { tok.next(); while (!is_section()) { parse_binary(); } } else if (is_general()) { tok.next(); while (!is_section()) { parse_general(); } } else { break; } } post_process(); } private: void error(char const* msg) { std::ostringstream ous; ous << tok.line() << ": " << msg << " got: " << peek(0) << "\n"; throw default_exception(ous.str()); } symbol const& peek(unsigned i) { return tok.peek(i); } bool try_accept(char const * token) { if (peek(0) == token) { tok.next(); return true; } return false; } void parse_objective() { m_objective.m_is_max = minmax(); if (peek(1) == ":") { m_objective.m_name = peek(0); tok.next(2); } parse_expr(m_objective.m_expr); } bool minmax() { if (try_accept("minimize")) return false; if (try_accept("min")) return false; if (try_accept("maximize")) return true; if (try_accept("max")) return true; error("expected min or max objective"); return false; } void parse_constraint() { symbol name; if (peek(1) == ":") { name = peek(0); tok.next(2); } IF_VERBOSE(10, verbose_stream() << name << "\n"); rational val(0); symbol var; parse_indicator(var, val); lin_term terms; parse_expr(terms); rel_op op = parse_relation(); rational rhs = tok.get_num(0); tok.next(); m_constraints.push_back(constraint(name, var, val, terms, op, rhs)); } void parse_expr(lin_term& terms) { if (is_relation()) { return; } bool pos = true; if (peek(0) == "-") { pos = false; tok.next(); } while (peek(0) == "+") { tok.next(); } terms.push_back(parse_term()); if (!pos) terms.back().first = -terms.back().first; while (peek(0) == "+" || peek(0) == "-") { bool pos = peek(0) == "+"; tok.next(); terms.push_back(parse_term()); if (!pos) terms.back().first = -terms.back().first; } } std::pair parse_term() { std::pair r(rational::one(), peek(0)); if (tok.peek_num(0)) { r.first = tok.get_num(0); r.second = peek(1); tok.next(2); } else { tok.next(1); } return r; } rel_op parse_relation() { if (try_accept("<=")) return le; if (try_accept("=<")) return le; if (try_accept(">=")) return ge; if (try_accept("=>")) return ge; if (try_accept("=")) return eq; error("expected relation"); return eq; } bool peek_le(unsigned pos) { return peek(pos) == "<=" || peek(pos) == "=<"; } bool peek_minus_infty_long(unsigned pos) { return peek(pos) == "-" && (peek(pos+1) == "inf" || peek(pos+1) == "infinity"); } bool peek_minus_infty_short(unsigned pos) { return peek(pos) == "-inf" || peek(pos) == "-infinity"; } bool peek_plus_infty_long(unsigned pos) { return peek(pos) == "+" && (peek(pos+1) == "inf" || peek(pos+1) == "infinity"); } bool peek_plus_infty_short(unsigned pos) { return peek(pos) == "+inf" || peek(pos) == "+infinity"; } void parse_indicator(symbol& var, rational& val) { if (peek(1) == "=" && tok.peek_num(2) && peek(3) == "->") { var = peek(0); val = tok.get_num(2); tok.next(4); } } bool try_subject_to() { if (try_accept("subject") && try_accept("to")) return true; if (try_accept("such") && try_accept("that")) return true; if (try_accept("st")) return true; if (try_accept("s.t.")) return true; return false; } bool is_relation() { return peek(0) == "=" || peek(0) == "=<" || peek(0) == ">=" || peek(0) == "=>" || peek(0) == "<="; } bool is_section() { return is_general() || is_binary() || is_bounds() || is_end();} bool is_bounds() { return peek(0) == "bounds"; } bool is_general() { return peek(0) == "general" || peek(0) == "gen" || peek(0) == "generals"; } bool is_binary() { return peek(0) == "binary" || peek(0) == "binaries" || peek(0) == "bin"; } bool is_end() { return peek(0) == "end" || tok.eof(); } // lo <= x // x <= hi // lo <= x <= hi // void parse_bound() { symbol v; if (peek_le(1) && tok.peek_num(0)) { rational lhs = tok.get_num(0); v = peek(2); update_lower(lhs, v); tok.next(3); parse_upper(v); } else if (peek_minus_infty_long(0) && peek_le(2)) { v = peek(3); tok.next(4); parse_upper(v); } else if (peek_minus_infty_short(0) && peek_le(1)) { v = peek(2); tok.next(3); parse_upper(v); } else if (peek_plus_infty_long(2) && peek_le(1)) { tok.next(4); } else if (peek_plus_infty_short(2) && peek_le(1)) { tok.next(3); } else if (peek_le(1) && tok.peek_num(2)) { v = peek(0); tok.next(2); rational rhs = tok.get_num(0); update_upper(v, rhs); tok.next(1); } else { error("bound expected"); } } void parse_upper(symbol const& v) { if (peek_le(0) && tok.peek_num(1)) { rational rhs = tok.get_num(1); update_upper(v, rhs); tok.next(2); } else if (peek_le(0) && peek_plus_infty_long(1)) { tok.next(3); } else if (peek_le(0) && peek_plus_infty_short(1)) { tok.next(2); } } void update_lower(rational const& r, symbol const& v) { bound b; m_bounds.find(v, b); b.m_lo = r; m_bounds.insert(v, b); } void update_upper(symbol const& v, rational const& r) { bound b; if (!m_bounds.find(v, b)) { // set the lower bound to default 0 b.m_lo = rational::zero(); } b.m_hi = r; m_bounds.insert(v, b); } void parse_binary() { symbol const& v = peek(0); update_lower(rational::zero(), v); update_upper(v, rational::one()); m_bounds[v].m_int = true; tok.next(); } void parse_general() { if (peek(1) == ":" && peek(3) == "=") { symbol const& v = peek(2); std::cout << "TBD: " << v << "\n"; return; } symbol const& v = peek(0); bound b; m_bounds.find(v, b); b.m_int = true; m_bounds.insert(v, b); tok.next(); } void post_process() { ast_manager& m = opt.get_manager(); arith_util a(m); for (constraint const& c : m_constraints) { expr_ref fml(m); expr_ref term = process_terms(c.m_expr); bool is_int = a.is_int(term) && c.m_bound.is_int(); switch (c.m_rel) { case le: fml = a.mk_le(term, a.mk_numeral(c.m_bound, is_int)); break; case ge: fml = a.mk_ge(term, a.mk_numeral(c.m_bound, is_int)); break; case eq: fml = m.mk_eq(term, a.mk_numeral(c.m_bound, is_int)); break; } if (c.m_bvar != symbol::null) { term = mk_var(c.m_bvar); bool is_int = c.m_bval.is_int() && a.is_int(term); term = m.mk_eq(mk_var(c.m_bvar), a.mk_numeral(c.m_bval, is_int)); fml = m.mk_implies(term, fml); } opt.add_hard_constraint(fml); } for (auto const& kv : m_bounds) { bound const& b = kv.m_value; expr_ref term = mk_var(kv.m_key); if (b.m_lo) { bool is_int = b.m_lo->is_int() && a.is_int(term); opt.add_hard_constraint(a.mk_le(a.mk_numeral(*b.m_lo, is_int), term)); } if (b.m_hi) { bool is_int = b.m_hi->is_int() && a.is_int(term); opt.add_hard_constraint(a.mk_le(term, a.mk_numeral(*b.m_hi, is_int))); } } expr_ref term = process_terms(m_objective.m_expr); m_h.push_back(opt.add_objective(to_app(term), m_objective.m_is_max)); } expr_ref process_terms(lin_term const& terms) { ast_manager& m = opt.get_manager(); arith_util a(m); expr_ref_vector result(m); for (auto const& kv : terms) { expr_ref term = mk_var(kv.second); if (!kv.first.is_one()) { bool is_int = kv.first.is_int() && a.is_int(term); term = a.mk_mul(a.mk_numeral(kv.first, is_int), term); } result.push_back(term); } return expr_ref(a.mk_add(result.size(), result.c_ptr()), m); } expr_ref mk_var(symbol const& v) { ast_manager& m = opt.get_manager(); arith_util a(m); bound b; if (!m_bounds.find(v, b)) { b.m_lo = rational::zero(); m_bounds.insert(v, b); } return expr_ref(m.mk_const(v, b.m_int ? a.mk_int() : a.mk_real()), m); } }; void parse_lp(opt::context& opt, std::istream& is, unsigned_vector& h) { opt_stream_buffer _is(is); lp_parse lp(opt, _is, h); lp.parse(); } z3-z3-4.8.7/src/opt/opt_parse.h000066400000000000000000000007611356505360400161710ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: opt_parse.h Abstract: Parse utilities for optimization. Author: Nikolaj Bjorner (nbjorner) 2017-11-19 Revision History: --*/ #ifndef OPT_PARSE_H_ #define OPT_PARSE_H_ void parse_wcnf(opt::context& opt, std::istream& is, unsigned_vector& h); void parse_opb(opt::context& opt, std::istream& is, unsigned_vector& h); void parse_lp(opt::context& opt, std::istream& is, unsigned_vector& h); #endif /* OPT_PARSE_H_ */ z3-z3-4.8.7/src/opt/opt_sls_solver.h000066400000000000000000000164661356505360400172630ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: opt_sls_solver.h Abstract: Wraps a solver with SLS for improving a solution using an objective function. Author: Nikolaj Bjorner (nbjorner) 2014-4-18 Notes: --*/ #ifndef OPT_SLS_SOLVER_H_ #define OPT_SLS_SOLVER_H_ #include "solver/solver_na2as.h" #include "tactic/arith/card2bv_tactic.h" #include "tactic/core/nnf_tactic.h" #include "opt/pb_sls.h" #include "tactic/sls/bvsls_opt_engine.h" namespace opt { class sls_solver : public solver_na2as { ast_manager& m; ref m_solver; scoped_ptr m_bvsls; scoped_ptr m_pbsls; pb::card_pb_rewriter m_pb2bv; vector m_weights; expr_ref_vector m_soft; model_ref m_model; params_ref m_params; symbol m_engine; public: sls_solver(ast_manager & m, solver* s, expr_ref_vector const& soft, vector const& weights, params_ref & p): solver_na2as(m), m(m), m_solver(s), m_bvsls(0), m_pbsls(0), m_pb2bv(m), m_weights(weights), m_soft(soft) { updt_params(p); } virtual ~sls_solver() {} virtual void updt_params(params_ref & p) { m_solver->updt_params(p); m_params.copy(p); opt_params _p(p); m_engine = _p.sls_engine(); } virtual void collect_param_descrs(param_descrs & r) { m_solver->collect_param_descrs(r); } virtual void collect_statistics(statistics & st) const { m_solver->collect_statistics(st); if (m_bvsls) m_bvsls->collect_statistics(st); if (m_pbsls) m_pbsls->collect_statistics(st); } virtual void assert_expr(expr * t) { m_solver->assert_expr(t); } virtual void get_unsat_core(ptr_vector & r) { m_solver->get_unsat_core(r); } virtual void get_model(model_ref & m) { m = m_model; } virtual proof * get_proof() { return m_solver->get_proof(); } virtual std::string reason_unknown() const { return m_solver->reason_unknown(); } virtual void get_labels(svector & r) { m_solver->get_labels(r); } virtual void set_progress_callback(progress_callback * callback) { m_solver->set_progress_callback(callback); } virtual unsigned get_num_assertions() const { return m_solver->get_num_assertions(); } virtual expr * get_assertion(unsigned idx) const { return m_solver->get_assertion(idx); } virtual void display(std::ostream & out) const { m_solver->display(out); // if (m_bvsls) m_bvsls->display(out); } void opt(model_ref& mdl) { if (m_engine == symbol("pb")) { pbsls_opt(mdl); } else { bvsls_opt(mdl); } } static expr_ref soft2bv(expr_ref_vector const& soft, vector const& weights) { ast_manager& m = soft.get_manager(); pb::card_pb_rewriter pb2bv(m); rational upper(1); expr_ref objective(m); for (unsigned i = 0; i < weights.size(); ++i) { upper += weights[i]; } expr_ref zero(m), tmp(m); bv_util bv(m); expr_ref_vector es(m); rational num = numerator(upper); rational den = denominator(upper); rational maxval = num*den; unsigned bv_size = maxval.get_num_bits(); zero = bv.mk_numeral(rational(0), bv_size); for (unsigned i = 0; i < soft.size(); ++i) { pb2bv(soft[i], tmp); es.push_back(m.mk_ite(tmp, bv.mk_numeral(den*weights[i], bv_size), zero)); } if (es.empty()) { objective = bv.mk_numeral(0, bv_size); } else { objective = es[0].get(); for (unsigned i = 1; i < es.size(); ++i) { objective = bv.mk_bv_add(objective, es[i].get()); } } return objective; } protected: typedef bvsls_opt_engine::optimization_result opt_result; virtual lbool check_sat_core(unsigned num_assumptions, expr * const * assumptions) { lbool r = m_solver->check_sat(num_assumptions, assumptions); if (r == l_true) { m_solver->get_model(m_model); opt(m_model); } return r; } virtual void push_core() { m_solver->push(); } virtual void pop_core(unsigned n) { m_solver->pop(n); } private: // convert soft constraints to bit-vector objective. void assertions2sls() { expr_ref tmp(m); goal_ref g(alloc(goal, m, true, false)); for (unsigned i = 0; i < m_solver->get_num_assertions(); ++i) { m_pb2bv(m_solver->get_assertion(i), tmp); g->assert_expr(tmp); } TRACE("opt", g->display(tout);); tactic_ref simplify = mk_nnf_tactic(m); proof_converter_ref pc; expr_dependency_ref core(m); goal_ref_buffer result; model_converter_ref model_converter; (*simplify)(g, result, model_converter, pc, core); SASSERT(result.size() == 1); goal* r = result[0]; for (unsigned i = 0; i < r->size(); ++i) { m_bvsls->assert_expr(r->form(i)); } TRACE("opt", m_bvsls->display(tout);); } void pbsls_opt(model_ref& mdl) { if (m_pbsls) { m_pbsls->reset(); } else { m_pbsls = alloc(smt::pb_sls, m); } m_pbsls->set_model(mdl); m_pbsls->updt_params(m_params); for (unsigned i = 0; i < m_solver->get_num_assertions(); ++i) { m_pbsls->add(m_solver->get_assertion(i)); } for (unsigned i = 0; i < m_soft.size(); ++i) { m_pbsls->add(m_soft[i].get(), m_weights[i]); } (*m_pbsls.get())(); m_pbsls->get_model(m_model); mdl = m_model.get(); } void bvsls_opt(model_ref& mdl) { m_bvsls = alloc(bvsls_opt_engine, m, m_params); assertions2sls(); expr_ref objective = soft2bv(m_soft, m_weights); TRACE("opt", tout << objective << "\n";); opt_result res(m); res.is_sat = l_undef; try { res = m_bvsls->optimize(objective, mdl, true); } catch (...) { } SASSERT(res.is_sat == l_true || res.is_sat == l_undef); if (res.is_sat == l_true) { m_bvsls->get_model(m_model); mdl = m_model.get(); } } }; } #endif z3-z3-4.8.7/src/opt/opt_solver.cpp000066400000000000000000000401611356505360400167220ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: opt_solver.cpp Abstract: Wraps smt::kernel as a solver for optimization Author: Anh-Dung Phan (t-anphan) 2013-10-16 Notes: Based directly on smt_solver. --*/ #include #include "ast/reg_decl_plugins.h" #include "opt/opt_solver.h" #include "smt/smt_context.h" #include "smt/theory_arith.h" #include "smt/theory_diff_logic.h" #include "smt/theory_dense_diff_logic.h" #include "smt/theory_pb.h" #include "smt/theory_lra.h" #include "ast/ast_pp.h" #include "ast/ast_smt_pp.h" #include "ast/pp_params.hpp" #include "opt/opt_params.hpp" #include "model/model_smt2_pp.h" #include "util/stopwatch.h" namespace opt { opt_solver::opt_solver(ast_manager & mgr, params_ref const & p, generic_model_converter& fm): solver_na2as(mgr), m_params(p), m_context(mgr, m_params), m(mgr), m_fm(fm), m_objective_terms(m), m_dump_benchmarks(false), m_first(true), m_was_unknown(false) { solver::updt_params(p); m_params.updt_params(p); if (m_params.m_case_split_strategy == CS_ACTIVITY_DELAY_NEW) { m_params.m_relevancy_lvl = 0; } // m_params.m_auto_config = false; } unsigned opt_solver::m_dump_count = 0; opt_solver::~opt_solver() { } void opt_solver::updt_params(params_ref const & _p) { opt_params p(_p); m_dump_benchmarks = p.dump_benchmarks(); m_params.updt_params(_p); m_context.updt_params(_p); } solver* opt_solver::translate(ast_manager& m, params_ref const& p) { UNREACHABLE(); return nullptr; } void opt_solver::collect_param_descrs(param_descrs & r) { m_context.collect_param_descrs(r); } void opt_solver::collect_statistics(statistics & st) const { m_context.collect_statistics(st); } void opt_solver::assert_expr_core(expr * t) { if (has_quantifiers(t)) { m_params.m_relevancy_lvl = 2; } m_context.assert_expr(t); } void opt_solver::push_core() { m_context.push(); } void opt_solver::pop_core(unsigned n) { m_context.pop(n); } void opt_solver::set_logic(symbol const& logic) { m_logic = logic; m_context.set_logic(logic); } void opt_solver::ensure_pb() { smt::theory_id th_id = m.get_family_id("pb"); smt::theory* th = get_context().get_theory(th_id); if (!th) { get_context().register_plugin(alloc(smt::theory_pb, m, m_params)); } } smt::theory_opt& opt_solver::get_optimizer() { smt::context& ctx = m_context.get_context(); smt::theory_id arith_id = m_context.m().get_family_id("arith"); smt::theory* arith_theory = ctx.get_theory(arith_id); if (!arith_theory) { ctx.register_plugin(alloc(smt::theory_mi_arith, m, m_params)); arith_theory = ctx.get_theory(arith_id); SASSERT(arith_theory); } if (typeid(smt::theory_mi_arith) == typeid(*arith_theory)) { return dynamic_cast(*arith_theory); } else if (typeid(smt::theory_i_arith) == typeid(*arith_theory)) { return dynamic_cast(*arith_theory); } else if (typeid(smt::theory_inf_arith) == typeid(*arith_theory)) { return dynamic_cast(*arith_theory); } else if (typeid(smt::theory_rdl&) == typeid(*arith_theory)) { return dynamic_cast(*arith_theory); } else if (typeid(smt::theory_idl&) == typeid(*arith_theory)) { return dynamic_cast(*arith_theory); } else if (typeid(smt::theory_dense_mi&) == typeid(*arith_theory)) { return dynamic_cast(*arith_theory); } else if (typeid(smt::theory_dense_i&) == typeid(*arith_theory)) { return dynamic_cast(*arith_theory); } else if (typeid(smt::theory_dense_smi&) == typeid(*arith_theory)) { return dynamic_cast(*arith_theory); } else if (typeid(smt::theory_dense_si&) == typeid(*arith_theory)) { return dynamic_cast(*arith_theory); } else if (typeid(smt::theory_lra&) == typeid(*arith_theory)) { return dynamic_cast(*arith_theory); } else { UNREACHABLE(); return dynamic_cast(*arith_theory); } } bool opt_solver::dump_benchmarks() { return m_dump_benchmarks; } 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) { tout << mk_pp(m_context.get_formula(i), m_context.m()) << "\n"; } }); stopwatch w; if (dump_benchmarks()) { w.start(); std::stringstream file_name; file_name << "opt_solver" << ++m_dump_count << ".smt2"; std::ofstream buffer(file_name.str().c_str()); to_smt2_benchmark(buffer, num_assumptions, assumptions, "opt_solver"); buffer.close(); IF_VERBOSE(1, verbose_stream() << "(created benchmark: " << file_name.str() << "..."; verbose_stream().flush();); } lbool r; if (m_first && num_assumptions == 0 && m_context.get_scope_level() == 0) { r = m_context.setup_and_check(); } else { r = m_context.check(num_assumptions, assumptions); } r = adjust_result(r); m_first = false; if (dump_benchmarks()) { w.stop(); IF_VERBOSE(1, verbose_stream() << ".. " << r << " " << std::fixed << w.get_seconds() << ")\n";); } return r; } void opt_solver::maximize_objectives(expr_ref_vector& blockers) { expr_ref blocker(m); for (unsigned i = 0; i < m_objective_vars.size(); ++i) { maximize_objective(i, blocker); blockers.push_back(blocker); } } lbool opt_solver::find_mutexes(expr_ref_vector const& vars, vector& mutexes) { return m_context.find_mutexes(vars, mutexes); } lbool opt_solver::preferred_sat(expr_ref_vector const& asms, vector& cores) { 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); } /** \brief maximize the value of objective i in the current state. Return a predicate that blocks the current maximal value. The result of 'maximize' is post-processed. When maximization involves shared symbols the model produced by local optimization does not necessarily satisfy combination constraints (it may not be a real model). In this case, the model is post-processed (update_model causes an additional call to final_check to propagate theory equalities when 'has_shared' is true). */ void opt_solver::maximize_objective(unsigned i, expr_ref& blocker) { smt::theory_var v = m_objective_vars[i]; bool has_shared = false; inf_eps val = get_optimizer().maximize(v, blocker, has_shared); get_model(m_model); inf_eps val2; m_valid_objectives[i] = true; has_shared = true; TRACE("opt", tout << (has_shared?"has shared":"non-shared") << " " << val << " " << blocker << "\n";); if (!m_models[i]) { set_model(i); } if (!val.is_finite()) { // skip model updates } else if (m_context.get_context().update_model(has_shared)) { if (has_shared && val != current_objective_value(i)) { decrement_value(i, val); } else { set_model(i); } } else { SASSERT(has_shared); decrement_value(i, val); } m_objective_values[i] = val; TRACE("opt", { tout << "objective: " << mk_pp(m_objective_terms[i].get(), m) << "\n"; tout << "maximal value: " << val << "\n"; tout << "new condition: " << blocker << "\n"; if (m_models[i]) model_smt2_pp(tout << "update model:\n", m, *m_models[i], 0); }); } void opt_solver::set_model(unsigned i) { model_ref mdl; get_model(mdl); m_models.set(i, mdl.get()); } lbool opt_solver::decrement_value(unsigned i, inf_eps& val) { push_core(); expr_ref ge = mk_ge(i, val); TRACE("opt", tout << ge << "\n";); assert_expr(ge); lbool is_sat = m_context.check(0, nullptr); is_sat = adjust_result(is_sat); if (is_sat == l_true) { set_model(i); } pop_core(1); TRACE("opt", tout << is_sat << "\n";); if (is_sat != l_true) { // cop-out approximation if (arith_util(m).is_real(m_objective_terms[i].get())) { val -= inf_eps(inf_rational(rational(0), true)); } else { val -= inf_eps(inf_rational(rational(1))); } m_valid_objectives[i] = false; } return is_sat; } lbool opt_solver::adjust_result(lbool r) { if (r == l_undef && m_context.last_failure() == smt::QUANTIFIERS) { r = l_true; m_was_unknown = true; } return r; } void opt_solver::get_unsat_core(expr_ref_vector & r) { r.reset(); unsigned sz = m_context.get_unsat_core_size(); for (unsigned i = 0; i < sz; i++) { r.push_back(m_context.get_unsat_core_expr(i)); } } void opt_solver::get_model_core(model_ref & m) { m_context.get_model(m); if (!m) m = m_model; else m_model = m; } proof * opt_solver::get_proof() { return m_context.get_proof(); } std::string opt_solver::reason_unknown() const { return m_context.last_failure_as_string(); } void opt_solver::set_reason_unknown(char const* msg) { m_context.set_reason_unknown(msg); } void opt_solver::get_labels(svector & r) { r.reset(); buffer tmp; m_context.get_relevant_labels(nullptr, tmp); r.append(tmp.size(), tmp.c_ptr()); } void opt_solver::set_progress_callback(progress_callback * callback) { m_callback = callback; m_context.set_progress_callback(callback); } unsigned opt_solver::get_num_assertions() const { return m_context.size(); } expr * opt_solver::get_assertion(unsigned idx) const { SASSERT(idx < get_num_assertions()); return m_context.get_formula(idx); } smt::theory_var opt_solver::add_objective(app* term) { smt::theory_var v = get_optimizer().add_objective(term); m_objective_vars.push_back(v); m_objective_values.push_back(inf_eps(rational(-1), inf_rational())); m_objective_terms.push_back(term); m_valid_objectives.push_back(true); m_models.push_back(nullptr); return v; } vector const& opt_solver::get_objective_values() { return m_objective_values; } inf_eps const& opt_solver::saved_objective_value(unsigned i) { return m_objective_values[i]; } inf_eps opt_solver::current_objective_value(unsigned i) { smt::theory_var v = m_objective_vars[i]; return get_optimizer().value(v); } expr_ref opt_solver::mk_ge(unsigned var, inf_eps const& val) { if (!val.is_finite()) { return expr_ref(val.is_pos() ? m.mk_false() : m.mk_true(), m); } smt::theory_opt& opt = get_optimizer(); smt::theory_var v = m_objective_vars[var]; TRACE("opt", tout << "v" << var << " " << val << "\n";); if (typeid(smt::theory_inf_arith) == typeid(opt)) { smt::theory_inf_arith& th = dynamic_cast(opt); return th.mk_ge(m_fm, v, val); } if (typeid(smt::theory_mi_arith) == typeid(opt)) { smt::theory_mi_arith& th = dynamic_cast(opt); SASSERT(val.is_finite()); return th.mk_ge(m_fm, v, val.get_numeral()); } if (typeid(smt::theory_i_arith) == typeid(opt)) { SASSERT(val.is_finite()); SASSERT(val.get_infinitesimal().is_zero()); smt::theory_i_arith& th = dynamic_cast(opt); return th.mk_ge(m_fm, v, val.get_rational()); } if (typeid(smt::theory_idl) == typeid(opt)) { smt::theory_idl& th = dynamic_cast(opt); return th.mk_ge(m_fm, v, val); } if (typeid(smt::theory_rdl) == typeid(opt)) { smt::theory_rdl& th = dynamic_cast(opt); return th.mk_ge(m_fm, v, val); } if (typeid(smt::theory_dense_i) == typeid(opt) && val.get_infinitesimal().is_zero()) { smt::theory_dense_i& th = dynamic_cast(opt); return th.mk_ge(m_fm, v, val); } if (typeid(smt::theory_dense_mi) == typeid(opt) && val.get_infinitesimal().is_zero()) { smt::theory_dense_mi& th = dynamic_cast(opt); return th.mk_ge(m_fm, v, val); } if (typeid(smt::theory_lra) == typeid(opt)) { smt::theory_lra& th = dynamic_cast(opt); SASSERT(val.is_finite()); return th.mk_ge(m_fm, v, val.get_numeral()); } // difference logic? if (typeid(smt::theory_dense_si) == typeid(opt) && val.get_infinitesimal().is_zero()) { smt::theory_dense_si& th = dynamic_cast(opt); return th.mk_ge(m_fm, v, val); } if (typeid(smt::theory_dense_smi) == typeid(opt) && val.get_infinitesimal().is_zero()) { smt::theory_dense_smi& th = dynamic_cast(opt); return th.mk_ge(m_fm, v, val); } IF_VERBOSE(0, verbose_stream() << "WARNING: unhandled theory " << typeid(opt).name() << "\n";); return expr_ref(m.mk_true(), m); } void opt_solver::reset_objectives() { m_objective_vars.reset(); m_objective_values.reset(); m_objective_terms.reset(); m_valid_objectives.reset(); } opt_solver& opt_solver::to_opt(solver& s) { if (typeid(opt_solver) != typeid(s)) { throw default_exception("BUG: optimization context has not been initialized correctly"); } return dynamic_cast(s); } void opt_solver::to_smt2_benchmark( std::ofstream & buffer, unsigned num_assumptions, expr * const * assumptions, char const * name, symbol const& logic, char const * status, char const * attributes) { ast_smt_pp pp(m); pp.set_benchmark_name(name); pp.set_logic(logic); pp.set_status(status); pp.add_attributes(attributes); pp_params params; pp.set_simplify_implies(params.simplify_implies()); for (unsigned i = 0; i < num_assumptions; ++i) { pp.add_assumption(assumptions[i]); } for (unsigned i = 0; i < get_num_assertions(); ++i) { pp.add_assumption(get_assertion(i)); } pp.display_smt2(buffer, m.mk_true()); } } z3-z3-4.8.7/src/opt/opt_solver.h000066400000000000000000000126651356505360400163770ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: opt_solver.h Abstract: Wraps smt::kernel as a solver for optimization Author: Anh-Dung Phan (t-anphan) 2013-10-16 Notes: Based directly on smt_solver. --*/ #ifndef OPT_SOLVER_H_ #define OPT_SOLVER_H_ #include "util/inf_rational.h" #include "util/inf_eps_rational.h" #include "ast/ast.h" #include "util/params.h" #include "solver/solver_na2as.h" #include "smt/smt_kernel.h" #include "smt/params/smt_params.h" #include "smt/smt_types.h" #include "smt/theory_opt.h" #include "tactic/generic_model_converter.h" namespace opt { typedef inf_eps_rational inf_eps; // Adjust bound bound |-> m_offset + (m_negate?-1:1)*bound class adjust_value { rational m_offset; bool m_negate; public: adjust_value(rational const& offset, bool neg): m_offset(offset), m_negate(neg) {} adjust_value(): m_offset(0), m_negate(false) {} void set_offset(rational const& o) { m_offset = o; } void set_negate(bool neg) { m_negate = neg; } rational const& get_offset() const { return m_offset; } bool get_negate() { return m_negate; } inf_eps operator()(inf_eps const& r) const { inf_eps result = r; if (m_negate) result.neg(); result += m_offset; return result; } rational operator()(rational const& r) const { rational result = r; if (m_negate) result.neg(); result += m_offset; return result; } }; class opt_solver : public solver_na2as { private: smt_params m_params; smt::kernel m_context; ast_manager& m; generic_model_converter& m_fm; progress_callback * m_callback; symbol m_logic; model_ref m_model; svector m_objective_vars; vector m_objective_values; sref_vector m_models; expr_ref_vector m_objective_terms; svector m_valid_objectives; bool m_dump_benchmarks; static unsigned m_dump_count; statistics m_stats; bool m_first; bool m_was_unknown; public: opt_solver(ast_manager & m, params_ref const & p, generic_model_converter& fm); ~opt_solver() override; solver* translate(ast_manager& m, params_ref const& p) override; void updt_params(params_ref const& p) override; void collect_param_descrs(param_descrs & r) override; void collect_statistics(statistics & st) const override; void assert_expr_core(expr * t) override; void push_core() override; void pop_core(unsigned n) 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; std::string reason_unknown() const override; void set_reason_unknown(char const* msg) override; void get_labels(svector & r) override; void set_progress_callback(progress_callback * callback) override; unsigned get_num_assertions() const override; expr * get_assertion(unsigned idx) const override; 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(); } expr_ref_vector cube(expr_ref_vector&, unsigned) override { return expr_ref_vector(m); } void set_logic(symbol const& logic); smt::theory_var add_objective(app* term); void reset_objectives(); void maximize_objective(unsigned i, expr_ref& blocker); void maximize_objectives(expr_ref_vector& blockers); inf_eps const & saved_objective_value(unsigned obj_index); inf_eps current_objective_value(unsigned obj_index); model* get_model_idx(unsigned obj_index) { return m_models[obj_index]; } bool objective_is_model_valid(unsigned obj_index) const { return m_valid_objectives[obj_index]; } bool was_unknown() const { return m_was_unknown; } vector const& get_objective_values(); expr_ref mk_ge(unsigned obj_index, inf_eps const& val); static opt_solver& to_opt(solver& s); bool dump_benchmarks(); smt::context& get_context() { return m_context.get_context(); } // used by weighted maxsat. void ensure_pb(); smt::theory_opt& get_optimizer(); void to_smt2_benchmark(std::ofstream & buffer, unsigned num_assumptions, expr * const * assumptions, char const * name = "benchmarks", symbol const& logic = symbol::null, char const * status = "unknown", char const * attributes = ""); private: lbool decrement_value(unsigned i, inf_eps& val); void set_model(unsigned i); lbool adjust_result(lbool r); }; } #endif z3-z3-4.8.7/src/opt/optsmt.cpp000066400000000000000000000505611356505360400160610ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: optsmt.cpp Abstract: Objective optimization method. Author: Anh-Dung Phan (t-anphan) 2013-10-16 Notes: Suppose we obtain solution t1 = k1, ..., tn = kn-epsilon Assert: t1 > k1 \/ t2 > k2 \/ ... \/ tn >= kn If this solution is satisfiable, then for each t_i, maximize the assignment and assert the new frontier. Claim: we don't necessarily have to freeze assignments of t_i when optimizing assignment for t_j because the state will always satisfy the disjunction. If one of the k_i is unbounded, then omit a disjunction for it. --*/ #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" #include "ast/ast_util.h" #include "model/model_pp.h" #include "ast/rewriter/th_rewriter.h" #include "opt/opt_params.hpp" namespace opt { void optsmt::set_max(vector& dst, vector const& src, expr_ref_vector& fmls) { for (unsigned i = 0; i < src.size(); ++i) { if (src[i] >= dst[i]) { dst[i] = src[i]; m_models.set(i, m_s->get_model_idx(i)); m_s->get_labels(m_labels); m_lower_fmls[i] = fmls[i].get(); if (dst[i].is_pos() && !dst[i].is_finite()) { // review: likely done already. m_lower_fmls[i] = m.mk_false(); fmls[i] = m.mk_false(); } } else if (src[i] < dst[i] && !m.is_true(m_lower_fmls[i].get())) { fmls[i] = m_lower_fmls[i].get(); } } } /* Enumerate locally optimal assignments until fixedpoint. */ lbool optsmt::basic_opt() { lbool is_sat = l_true; expr_ref bound(m.mk_true(), m), tmp(m); expr* vars[1]; solver::scoped_push _push(*m_s); while (is_sat == l_true && !m.canceled()) { tmp = m.mk_fresh_const("b", m.mk_bool_sort()); vars[0] = tmp; bound = m.mk_implies(tmp, bound); m_s->assert_expr(bound); is_sat = m_s->check_sat(1, vars); if (is_sat == l_true) { bound = update_lower(); } } if (m.canceled() || is_sat == l_undef) { return l_undef; } // set the solution tight. for (unsigned i = 0; i < m_lower.size(); ++i) { m_upper[i] = m_lower[i]; } return l_true; } /* Enumerate locally optimal assignments until fixedpoint. */ lbool optsmt::geometric_opt() { lbool is_sat = l_true; expr_ref bound(m); vector lower(m_lower); unsigned steps = 0; unsigned step_incs = 0; rational delta_per_step(1); unsigned num_scopes = 0; unsigned delta_index = 0; // index of objective to speed up. while (!m.canceled()) { SASSERT(delta_per_step.is_int()); SASSERT(delta_per_step.is_pos()); is_sat = m_s->check_sat(0, nullptr); if (is_sat == l_true) { bound = update_lower(); if (!get_max_delta(lower, delta_index)) { delta_per_step = rational::one(); } else if (steps > step_incs) { delta_per_step *= rational(2); ++step_incs; steps = 0; } else { ++steps; } if (delta_per_step > rational::one()) { m_s->push(); ++num_scopes; // only try to improve delta_index. bound = m_s->mk_ge(delta_index, m_lower[delta_index] + inf_eps(delta_per_step)); } TRACE("opt", tout << delta_per_step << " " << bound << "\n";); m_s->assert_expr(bound); } else if (is_sat == l_false && delta_per_step > rational::one()) { steps = 0; step_incs = 0; delta_per_step = rational::one(); SASSERT(num_scopes > 0); --num_scopes; m_s->pop(1); } else { break; } } m_s->pop(num_scopes); if (m.canceled() || is_sat == l_undef) { return l_undef; } // set the solution tight. for (unsigned i = 0; i < m_lower.size(); ++i) { m_upper[i] = m_lower[i]; } return l_true; } bool optsmt::is_unbounded(unsigned obj_index, bool is_maximize) { if (is_maximize) { return !m_upper[obj_index].is_finite(); } else { return !m_lower[obj_index].is_finite(); } } 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; expr_ref bound(m); for (unsigned i = 0; i < obj_index; ++i) { commit_assignment(i); } m_s->get_model(m_model); unsigned steps = 0; unsigned step_incs = 0; rational delta_per_step(1); unsigned num_scopes = 0; while (!m.canceled()) { 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"; tout << "lower: " << m_lower[obj_index] << "\n"; tout << "upper: " << m_upper[obj_index] << "\n"; ); if (is_sat == l_true) { m_s->maximize_objective(obj_index, bound); m_s->get_model(m_model); SASSERT(m_model); inf_eps obj = m_s->saved_objective_value(obj_index); update_lower_lex(obj_index, obj, is_maximize); if (!is_int || !m_lower[obj_index].is_finite()) { delta_per_step = rational(1); } else if (steps > step_incs) { delta_per_step *= rational(2); ++step_incs; steps = 0; } else { ++steps; } if (delta_per_step > rational::one()) { m_s->push(); ++num_scopes; bound = m_s->mk_ge(obj_index, obj + inf_eps(delta_per_step)); } TRACE("opt", tout << "delta: " << delta_per_step << " " << bound << "\n";); m_s->assert_expr(bound); } else if (is_sat == l_false && delta_per_step > rational::one()) { steps = 0; step_incs = 0; delta_per_step = rational::one(); SASSERT(num_scopes > 0); --num_scopes; m_s->pop(1); } else { break; } } m_s->pop(num_scopes); TRACE("opt", tout << is_sat << " " << num_scopes << "\n";); if (is_sat == l_false && !m_model) { return l_false; } if (m.canceled() || is_sat == l_undef) { return l_undef; } // set the solution tight. m_upper[obj_index] = m_lower[obj_index]; for (unsigned i = obj_index+1; i < m_lower.size(); ++i) { m_lower[i] = inf_eps(rational(-1), inf_rational(0)); } return l_true; } bool optsmt::get_max_delta(vector const& lower, unsigned& idx) { arith_util arith(m); inf_eps max_delta; for (unsigned i = 0; i < m_lower.size(); ++i) { if (arith.is_int(m_objs[i].get())) { inf_eps delta = m_lower[i] - lower[i]; if (m_lower[i].is_finite() && delta > max_delta) { max_delta = delta; } } } return max_delta.is_pos(); } /* Enumerate locally optimal assignments until fixedpoint. */ lbool optsmt::farkas_opt() { smt::theory_opt& opt = m_s->get_optimizer(); if (typeid(smt::theory_inf_arith) != typeid(opt)) { return l_undef; } lbool is_sat = l_true; while (is_sat == l_true && !m.canceled()) { is_sat = update_upper(); } if (m.canceled() || is_sat == l_undef) { return l_undef; } // set the solution tight. for (unsigned i = 0; i < m_lower.size(); ++i) { m_upper[i] = m_lower[i]; } return l_true; } lbool optsmt::symba_opt() { smt::theory_opt& opt = m_s->get_optimizer(); if (typeid(smt::theory_inf_arith) != typeid(opt)) { return l_undef; } expr_ref_vector ors(m), disj(m); expr_ref fml(m), bound(m.mk_true(), m), tmp(m); expr* vars[1]; { for (unsigned i = 0; i < m_upper.size(); ++i) { ors.push_back(m_s->mk_ge(i, m_upper[i])); } fml = mk_or(ors); tmp = m.mk_fresh_const("b", m.mk_bool_sort()); fml = m.mk_implies(tmp, fml); vars[0] = tmp; lbool is_sat = l_true; solver::scoped_push _push(*m_s); while (!m.canceled()) { m_s->assert_expr(fml); TRACE("opt", tout << fml << "\n";); is_sat = m_s->check_sat(1,vars); if (is_sat == l_true) { disj.reset(); m_s->maximize_objectives(disj); m_s->get_model(m_model); m_s->get_labels(m_labels); for (unsigned i = 0; i < ors.size(); ++i) { expr_ref tmp(m); if (m_model->is_true(ors[i].get())) { m_lower[i] = m_upper[i]; ors[i] = m.mk_false(); disj[i] = m.mk_false(); } } set_max(m_lower, m_s->get_objective_values(), disj); fml = mk_or(ors); tmp = m.mk_fresh_const("b", m.mk_bool_sort()); fml = m.mk_implies(tmp, fml); vars[0] = tmp; } else if (is_sat == l_undef) { return l_undef; } else { break; } } } bound = mk_or(m_lower_fmls); m_s->assert_expr(bound); if (m.canceled()) { return l_undef; } return geometric_opt(); } void optsmt::update_lower_lex(unsigned idx, inf_eps const& v, bool is_maximize) { if (v > m_lower[idx]) { m_lower[idx] = v; IF_VERBOSE(1, if (is_maximize) 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_best_model = m_model; m_s->get_labels(m_labels); m_context.set_model(m_model); } } void optsmt::update_lower(unsigned idx, inf_eps const& v) { TRACE("opt", tout << "v" << idx << " >= " << v << "\n";); m_lower_fmls[idx] = m_s->mk_ge(idx, v); m_lower[idx] = v; } void optsmt::update_upper(unsigned idx, inf_eps const& v) { TRACE("opt", tout << "v" << idx << " <= " << v << "\n";); m_upper[idx] = v; } std::ostream& operator<<(std::ostream& out, vector const& vs) { for (unsigned i = 0; i < vs.size(); ++i) { out << vs[i] << " "; } return out; } expr_ref optsmt::update_lower() { expr_ref_vector disj(m); m_s->get_model(m_model); m_s->get_labels(m_labels); m_s->maximize_objectives(disj); set_max(m_lower, m_s->get_objective_values(), disj); TRACE("opt", model_pp(tout << m_lower << "\n", *m_model);); IF_VERBOSE(2, verbose_stream() << "(optsmt.lower " << m_lower << ")\n";); return mk_or(disj); } lbool optsmt::update_upper() { smt::theory_opt& opt = m_s->get_optimizer(); SASSERT(typeid(smt::theory_inf_arith) == typeid(opt)); smt::theory_inf_arith& th = dynamic_cast(opt); expr_ref bound(m); expr_ref_vector bounds(m); solver::scoped_push _push(*m_s); // // NB: we have to create all bound expressions before calling check_sat // because the state after check_sat is not at base level. // vector mid; for (unsigned i = 0; i < m_lower.size() && !m.canceled(); ++i) { if (m_lower[i] < m_upper[i]) { mid.push_back((m_upper[i]+m_lower[i])/rational(2)); bound = m_s->mk_ge(i, mid[i]); bounds.push_back(bound); } else { bounds.push_back(nullptr); mid.push_back(inf_eps()); } } bool progress = false; for (unsigned i = 0; i < m_lower.size() && !m.canceled(); ++i) { if (m_lower[i] <= mid[i] && mid[i] <= m_upper[i] && m_lower[i] < m_upper[i]) { th.enable_record_conflict(bounds[i].get()); lbool is_sat = m_s->check_sat(1, bounds.c_ptr() + i); switch(is_sat) { case l_true: IF_VERBOSE(2, verbose_stream() << "(optsmt lower bound for v" << m_vars[i] << " := " << m_upper[i] << ")\n";); m_lower[i] = mid[i]; th.enable_record_conflict(nullptr); m_s->assert_expr(update_lower()); break; case l_false: IF_VERBOSE(2, verbose_stream() << "(optsmt conflict: " << th.conflict_minimize() << ") \n";); if (!th.conflict_minimize().is_finite()) { // bounds is not in the core. The context is unsat. m_upper[i] = m_lower[i]; return l_false; } else { m_upper[i] = std::min(m_upper[i], th.conflict_minimize()); } break; default: th.enable_record_conflict(nullptr); return l_undef; } th.enable_record_conflict(nullptr); progress = true; } } if (m.canceled()) { return l_undef; } if (!progress) { return l_false; } return l_true; } void optsmt::setup(opt_solver& solver) { m_s = &solver; solver.reset_objectives(); m_vars.reset(); // force base level { solver::scoped_push _push(solver); } for (unsigned i = 0; i < m_objs.size(); ++i) { smt::theory_var v = solver.add_objective(m_objs[i].get()); if (v == smt::null_theory_var) { std::ostringstream out; out << "Objective function '" << mk_pp(m_objs[i].get(), m) << "' is not supported"; throw default_exception(out.str()); } m_vars.push_back(v); } } lbool optsmt::lex(unsigned obj_index, bool is_maximize) { TRACE("opt", tout << "optsmt:lex\n";); m_context.get_base_model(m_best_model); solver::scoped_push _push(*m_s); SASSERT(obj_index < m_vars.size()); if (is_maximize && m_optsmt_engine == symbol("farkas")) { return farkas_opt(); } else if (is_maximize && m_optsmt_engine == symbol("symba")) { return symba_opt(); } else { return geometric_lex(obj_index, is_maximize); } } // deprecated lbool optsmt::basic_lex(unsigned obj_index, bool is_maximize) { lbool is_sat = l_true; expr_ref bound(m); for (unsigned i = 0; i < obj_index; ++i) { commit_assignment(i); } while (is_sat == l_true && !m.canceled()) { is_sat = m_s->check_sat(0, nullptr); if (is_sat != l_true) break; m_s->maximize_objective(obj_index, bound); m_s->get_model(m_model); inf_eps obj = m_s->saved_objective_value(obj_index); update_lower_lex(obj_index, obj, is_maximize); TRACE("opt", tout << "strengthen bound: " << bound << "\n";); m_s->assert_expr(bound); // TBD: only works for simplex // blocking formula should be extracted based // on current state. } if (m.canceled() || is_sat == l_undef) { TRACE("opt", tout << "undef: " << m.canceled() << " " << is_sat << "\n";); return l_undef; } // set the solution tight. m_upper[obj_index] = m_lower[obj_index]; for (unsigned i = obj_index+1; i < m_lower.size(); ++i) { m_lower[i] = inf_eps(rational(-1), inf_rational(0)); } return l_true; } /** Takes solver with hard constraints added. Returns an optimal assignment to objective functions. */ lbool optsmt::box() { lbool is_sat = l_true; if (m_vars.empty()) { return is_sat; } // assertions added during search are temporary. solver::scoped_push _push(*m_s); if (m_optsmt_engine == symbol("farkas")) { is_sat = farkas_opt(); } else if (m_optsmt_engine == symbol("symba")) { is_sat = symba_opt(); } else { is_sat = geometric_opt(); } return is_sat; } inf_eps optsmt::get_lower(unsigned i) const { if (i >= m_lower.size()) return inf_eps(); return m_lower[i]; } bool optsmt::objective_is_model_valid(unsigned index) const { return m_s->objective_is_model_valid(index); } inf_eps optsmt::get_upper(unsigned i) const { if (i >= m_upper.size()) return inf_eps(); return m_upper[i]; } void optsmt::get_model(model_ref& mdl, svector & labels) { mdl = m_best_model.get(); labels = m_labels; } // force lower_bound(i) <= objective_value(i) void optsmt::commit_assignment(unsigned i) { inf_eps lo = m_lower[i]; TRACE("opt", tout << "set lower bound of " << mk_pp(m_objs[i].get(), m) << " to: " << lo << "\n"; tout << get_lower(i) << ":" << get_upper(i) << "\n";); // Only assert bounds for bounded objectives if (lo.is_finite()) { m_s->assert_expr(m_s->mk_ge(i, lo)); } } unsigned optsmt::add(app* t) { expr_ref t1(t, m), t2(m); th_rewriter rw(m); rw(t1, t2); SASSERT(is_app(t2)); m_objs.push_back(to_app(t2)); m_lower.push_back(inf_eps(rational(-1),inf_rational(0))); m_upper.push_back(inf_eps(rational(1), inf_rational(0))); m_lower_fmls.push_back(m.mk_true()); m_models.push_back(nullptr); return m_objs.size()-1; } void optsmt::updt_params(params_ref& p) { opt_params _p(p); m_optsmt_engine = _p.optsmt_engine(); } void optsmt::reset() { m_lower.reset(); m_upper.reset(); m_objs.reset(); m_vars.reset(); m_model.reset(); m_lower_fmls.reset(); m_s = nullptr; } } z3-z3-4.8.7/src/opt/optsmt.h000066400000000000000000000046111356505360400155210ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: optsmt.h Abstract: Objective optimization method. Author: Anh-Dung Phan (t-anphan) 2013-10-16 Notes: --*/ #ifndef OPTSMT_H_ #define OPTSMT_H_ #include "opt/opt_solver.h" namespace opt { /** Takes solver with hard constraints added. 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; app_ref_vector m_objs; expr_ref_vector m_lower_fmls; svector m_vars; symbol m_optsmt_engine; model_ref m_model, m_best_model; svector m_labels; sref_vector m_models; public: 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); lbool box(); lbool lex(unsigned obj_index, bool is_maximize); bool is_unbounded(unsigned obj_index, bool is_maximize); unsigned add(app* t); void updt_params(params_ref& p); unsigned get_num_objectives() const { return m_objs.size(); } void commit_assignment(unsigned index); inf_eps get_lower(unsigned index) const; inf_eps get_upper(unsigned index) const; bool objective_is_model_valid(unsigned index) const; void get_model(model_ref& mdl, svector& labels); model* get_model(unsigned index) const { return m_models[index]; } void update_lower(unsigned idx, inf_eps const& r); void update_upper(unsigned idx, inf_eps const& r); void reset(); private: bool get_max_delta(vector const& lower, unsigned& idx); lbool basic_opt(); lbool geometric_opt(); lbool symba_opt(); lbool basic_lex(unsigned idx, bool is_maximize); lbool geometric_lex(unsigned idx, bool is_maximize); lbool farkas_opt(); void set_max(vector& dst, vector const& src, expr_ref_vector& fmls); expr_ref update_lower(); void update_lower_lex(unsigned idx, inf_eps const& r, bool is_maximize); lbool update_upper(); }; }; #endif z3-z3-4.8.7/src/opt/pb_sls.cpp000066400000000000000000000660431356505360400160170ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: pb_sls.cpp Abstract: SLS for PB optimization. Author: Nikolaj Bjorner (nbjorner) 2014-03-18 Notes: --*/ #include "opt/pb_sls.h" #include "smt/smt_literal.h" #include "ast/ast_pp.h" #include "ast/rewriter/th_rewriter.h" #include "sat/sat_types.h" namespace smt { class index_set { unsigned_vector m_elems; unsigned_vector m_index; public: unsigned num_elems() const { return m_elems.size(); } unsigned operator[](unsigned idx) const { return m_elems[idx]; } void reset() { m_elems.reset(); m_index.reset(); } bool empty() const { return m_elems.empty(); } bool contains(unsigned idx) const { return (idx < m_index.size()) && (m_index[idx] < m_elems.size()) && (m_elems[m_index[idx]] == idx); } void insert(unsigned idx) { m_index.reserve(idx+1); if (!contains(idx)) { m_index[idx] = m_elems.size(); m_elems.push_back(idx); } } void remove(unsigned idx) { if (!contains(idx)) return; unsigned pos = m_index[idx]; m_elems[pos] = m_elems.back(); m_index[m_elems[pos]] = pos; m_elems.pop_back(); } unsigned choose(random_gen& rnd) const { SASSERT(!empty()); return m_elems[rnd(num_elems())]; } }; struct pb_sls::imp { struct clause { literal_vector m_lits; scoped_mpz_vector m_weights; scoped_mpz m_k; scoped_mpz m_value; bool m_eq; clause(unsynch_mpz_manager& m): m_weights(m), m_k(m), m_value(m), m_eq(true) {} clause(clause const& cls): m_lits(cls.m_lits), m_weights(cls.m_weights.m()), m_k(cls.m_k), m_value(cls.m_value), m_eq(cls.m_eq) { for (unsigned i = 0; i < cls.m_weights.size(); ++i) { m_weights.push_back(cls.m_weights[i]); } } }; struct stats { stats() { reset(); } void reset() { memset(this, 0, sizeof(*this)); } unsigned m_num_flips; unsigned m_num_improvements; }; ast_manager& m; pb_util pb; unsynch_mpz_manager mgr; th_rewriter m_rewrite; vector m_clauses; // clauses to be satisfied expr_ref_vector m_orig_clauses; // for debugging model_ref m_orig_model; // for debugging vector m_soft; // soft constraints vector m_weights; // weights of soft constraints rational m_penalty; // current penalty of soft constraints rational m_best_penalty; vector m_hard_occ, m_soft_occ; // variable occurrence svector m_assignment; // current assignment. svector m_best_assignment; expr_ref_vector m_trail; obj_map m_decl2var; // map declarations to Boolean variables. ptr_vector m_var2decl; // reverse map index_set m_hard_false; // list of hard clauses that are false. index_set m_soft_false; // list of soft clauses that are false. unsigned m_max_flips; // maximal number of flips unsigned m_non_greedy_percent; // percent of moves to do non-greedy style random_gen m_rng; scoped_mpz one; stats m_stats; imp(ast_manager& m): m(m), pb(m), m_rewrite(m), m_orig_clauses(m), m_trail(m), one(mgr) { reset(); one = mpz(1); } ~imp() { } void reset() { init_max_flips(); m_non_greedy_percent = 30; m_decl2var.reset(); m_var2decl.reset(); m_assignment.reset(); m_hard_occ.reset(); m_soft_occ.reset(); m_clauses.reset(); m_orig_clauses.reset(); m_soft.reset(); m_weights.reset(); m_trail.reset(); m_decl2var.insert(m.mk_true(), 0); m_var2decl.push_back(m.mk_true()); m_assignment.push_back(true); m_hard_occ.push_back(unsigned_vector()); m_soft_occ.push_back(unsigned_vector()); } void init_max_flips() { m_max_flips = 200; } void add(expr* f) { clause cls(mgr); if (compile_clause(f, cls)) { m_clauses.push_back(cls); m_orig_clauses.push_back(f); } } void add(expr* f, rational const& w) { clause cls(mgr); if (compile_clause(f, cls)) { m_soft.push_back(cls); m_weights.push_back(w); } } void set_model(model_ref & mdl) { m_orig_model = mdl; for (unsigned i = 0; i < m_var2decl.size(); ++i) { expr_ref tmp(m); m_assignment[i] = mdl->is_true(m_var2decl[i]); } } lbool operator()() { init(); IF_VERBOSE(1, verbose_stream() << "(pb.sls initial penalty: " << m_best_penalty << ")\n"; verbose_stream() << "(pb.sls violated: " << m_hard_false.num_elems() << " penalty: " << m_penalty << ")\n";); svector assignment(m_assignment); for (unsigned round = 0; round < 40; ++round) { init_max_flips(); while (m_max_flips > 0) { --m_max_flips; literal lit = flip(); if (m.canceled()) { return l_undef; } IF_VERBOSE(3, verbose_stream() << "(pb.sls violated: " << m_hard_false.num_elems() << " penalty: " << m_penalty << " " << lit << ")\n";); if (m_hard_false.empty() && m_best_penalty.is_zero()) { break; } } if (m_hard_false.empty() && m_best_penalty.is_zero()) { break; } IF_VERBOSE(1, verbose_stream() << "(pb.sls best penalty " << m_best_penalty << ")\n";); if (!m_best_assignment.empty()) { assignment.reset(); assignment.append(m_best_assignment); round = 0; } m_assignment.reset(); m_assignment.append(assignment); m_best_assignment.reset(); m_soft_false.reset(); m_hard_false.reset(); m_penalty.reset(); for (unsigned i = 0; i < m_soft.size(); ++i) { if (!eval(m_soft[i])) { m_soft_false.insert(i); m_penalty += m_weights[i]; } } for (unsigned i = 0; i < m_clauses.size(); ++i) { if (!eval(m_clauses[i])) { m_hard_false.insert(i); } } } m_assignment.reset(); m_assignment.append(assignment); m_penalty = m_best_penalty; return l_true; } bool get_value(literal l) { return l.sign() ^ m_assignment[l.var()]; } void get_model(model_ref& mdl) { mdl = alloc(model, m); for (unsigned i = 1; i < m_var2decl.size(); ++i) { expr* d = m_var2decl[i]; if (is_uninterp_const(d)) { mdl->register_decl(to_app(d)->get_decl(), m_assignment[i] ? m.mk_true() : m.mk_false()); } } } void collect_statistics(::statistics& st) const { st.update("sls.num_flips", m_stats.m_num_flips); st.update("sls.num_improvements", m_stats.m_num_improvements); } void updt_params(params_ref& p) { } bool soft_holds(unsigned index) { return eval(m_soft[index]); } void display(std::ostream& out, clause const& cls) { scoped_mpz w(mgr); for (unsigned i = 0; i < cls.m_lits.size(); ++i) { w = cls.m_weights[i]; out << w << "*" << cls.m_lits[i] << " "; out << "(" << mk_pp(m_var2decl[cls.m_lits[i].var()], m) << ") "; if (i + 1 < cls.m_lits.size()) { out << "+ "; } } out << "(" << cls.m_value << ") "; if (cls.m_eq) { out << "= "; } else { out << ">= "; } out << cls.m_k << "\n"; } void display(std::ostream& out) { for (unsigned i = 0; i < m_clauses.size(); ++i) { display(out, m_clauses[i]); } out << "soft:\n"; for (unsigned i = 0; i < m_soft.size(); ++i) { display(out << m_weights[i] << ": ", m_soft[i]); } for (unsigned i = 0; i < m_assignment.size(); ++i) { out << literal(i) << ": " << mk_pp(m_var2decl[i], m) << " |-> " << (m_assignment[i]?"true":"false") << "\n"; } } bool eval(clause& cls) { unsigned sz = cls.m_lits.size(); cls.m_value.reset(); for (unsigned i = 0; i < sz; ++i) { if (get_value(cls.m_lits[i])) { cls.m_value += cls.m_weights[i]; } } if (cls.m_eq) { return cls.m_value == cls.m_k; } else { return cls.m_value >= cls.m_k; } } void init_occ(vector const& clauses, vector & occ) { for (unsigned i = 0; i < clauses.size(); ++i) { clause const& cls = clauses[i]; for (unsigned j = 0; j < cls.m_lits.size(); ++j) { literal lit = cls.m_lits[j]; if (occ.size() <= static_cast(lit.var())) occ.resize(lit.var() + 1); occ[lit.var()].push_back(i); } } } void init() { m_best_assignment.reset(); m_best_penalty.reset(); m_hard_false.reset(); m_hard_occ.reset(); m_soft_false.reset(); m_soft_occ.reset(); m_penalty.reset(); for (unsigned i = 0; i <= m_var2decl.size(); ++i) { m_soft_occ.push_back(unsigned_vector()); m_hard_occ.push_back(unsigned_vector()); } // initialize the occurs vectors. init_occ(m_clauses, m_hard_occ); init_occ(m_soft, m_soft_occ); // add clauses that are false. for (unsigned i = 0; i < m_clauses.size(); ++i) { if (!eval(m_clauses[i])) { m_hard_false.insert(i); expr_ref tmp = (*m_orig_model)(m_orig_clauses[i].get()); IF_VERBOSE(0, verbose_stream() << "original evaluation: " << tmp << "\n"; verbose_stream() << mk_pp(m_orig_clauses[i].get(), m) << "\n"; display(verbose_stream(), m_clauses[i]);); } } for (unsigned i = 0; i < m_soft.size(); ++i) { if (!eval(m_soft[i])) { m_soft_false.insert(i); m_penalty += m_weights[i]; } } m_best_penalty = m_penalty; TRACE("opt", display(tout);); } literal flip() { m_stats.m_num_flips++; literal result; if (m_hard_false.empty()) { result = flip_soft(); } else { result = flip_hard(); } if (m_hard_false.empty() && m_best_penalty > m_penalty) { IF_VERBOSE(1, verbose_stream() << "(pb.sls improved bound " << m_penalty << ")\n";); m_best_assignment.reset(); m_best_assignment.append(m_assignment); m_best_penalty = m_penalty; m_stats.m_num_improvements++; init_max_flips(); } if (!m_assignment[result.var()]) { result.neg(); } return result; } literal flip_hard() { SASSERT(!m_hard_false.empty()); literal lit; clause const& cls = pick_hard_clause(); // IF_VERBOSE(1, display(verbose_stream(), cls);); int break_count; int min_bc = INT_MAX; unsigned min_bc_index = 0; for (unsigned i = 0; i < cls.m_lits.size(); ++i) { lit = cls.m_lits[i]; break_count = flip(lit); if (break_count < min_bc) { min_bc = break_count; min_bc_index = i; } else if (break_count == min_bc && m_rng(5) == 1) { min_bc_index = i; } int new_break_count = flip(~lit); if (-break_count != new_break_count) { IF_VERBOSE(0, display(verbose_stream() << lit << "\n", cls); display(verbose_stream())); } // VERIFY(-break_count == flip(~lit)); } if (m_rng(100) <= m_non_greedy_percent) { lit = cls.m_lits[m_rng(cls.m_lits.size())]; } else { lit = cls.m_lits[min_bc_index]; } flip(lit); return lit; } literal flip_soft() { literal lit; clause const& cls = pick_soft_clause(); int break_count; int min_bc = INT_MAX; unsigned min_bc_index = 0; rational penalty = m_penalty; rational min_penalty = penalty; for (unsigned i = 0; i < cls.m_lits.size(); ++i) { lit = cls.m_lits[i]; break_count = flip(lit); SASSERT(break_count >= 0); if (break_count == 0 && penalty > m_penalty) { return lit; } if ((break_count < min_bc) || (break_count == min_bc && m_penalty < min_penalty)) { min_bc = break_count; min_bc_index = i; min_penalty = m_penalty; } VERIFY(-break_count == flip(~lit)); } if (m_rng(100) <= m_non_greedy_percent) { lit = cls.m_lits[m_rng(cls.m_lits.size())]; } else { // just do a greedy move: lit = cls.m_lits[min_bc_index]; } flip(lit); return lit; } // // TODO: alternate version: loop over soft clauses and see if there is a flip that // reduces the penalty while preserving the hard constraints. // // crude selection strategy. clause const& pick_hard_clause() { SASSERT(!m_hard_false.empty()); return m_clauses[m_hard_false.choose(m_rng)]; } clause const& pick_soft_clause() { SASSERT(!m_soft_false.empty()); return m_soft[m_soft_false.choose(m_rng)]; } int flip(literal l) { m_assignment[l.var()] = !m_assignment[l.var()]; int break_count = 0; unsigned_vector const& occh = m_hard_occ[l.var()]; scoped_mpz value(mgr); for (unsigned i = 0; i < occh.size(); ++i) { unsigned j = occh[i]; clause& cls = m_clauses[j]; value = cls.m_value; if (eval(cls)) { if (m_hard_false.contains(j)) { break_count--; m_hard_false.remove(j); } } else { if (!m_hard_false.contains(j)) { break_count++; m_hard_false.insert(j); } else if (value < m_clauses[j].m_value) { } } } unsigned_vector const& occs = m_soft_occ[l.var()]; for (unsigned i = 0; i < occs.size(); ++i) { unsigned j = occs[i]; if (eval(m_soft[j])) { if (m_soft_false.contains(j)) { m_penalty -= m_weights[j]; m_soft_false.remove(j); } } else { if (!m_soft_false.contains(j)) { m_penalty += m_weights[j]; m_soft_false.insert(j); } } } TRACE("opt", tout << "flip: " << l << " num false: " << m_hard_false.num_elems() << " penalty: " << m_penalty << " break count: " << break_count << "\n";); return break_count; } literal mk_aux_literal(expr* f) { unsigned var; if (!m_decl2var.find(f, var)) { var = m_hard_occ.size(); SASSERT(m_var2decl.size() == var); SASSERT(m_soft_occ.size() == var); m_hard_occ.push_back(unsigned_vector()); m_soft_occ.push_back(unsigned_vector()); m_assignment.push_back(m_orig_model->is_true(f)); m_decl2var.insert(f, var); m_var2decl.push_back(f); } return literal(var); } void pad(scoped_mpz_vector& vec, unsigned sz, mpz& val) { for (unsigned i = 0; i < sz; ++i) { vec.push_back(val); } } literal mk_literal(expr* f) { if (m.is_not(f, f)) { literal result = mk_literal(f); if (result != null_literal) { result.neg(); } return result; } if (is_uninterp_const(f)) return mk_aux_literal(to_app(f)); if (m.is_true(f)) return true_literal; if (m.is_false(f)) return false_literal; if (m.is_and(f)) { literal_vector lits; app* g = to_app(f); for (unsigned i = 0; i < g->get_num_args(); ++i) { lits.push_back(mk_literal(g->get_arg(i))); } literal result = mk_aux_literal(f); for (unsigned i = 0; i < lits.size(); ++i) { clause cls(mgr); cls.m_lits.push_back(~result); cls.m_weights.push_back(one); cls.m_lits.push_back(lits[i]); cls.m_weights.push_back(one); cls.m_k = one; cls.m_eq = false; m_clauses.push_back(cls); m_orig_clauses.push_back(f); lits[i].neg(); } lits.push_back(result); clause cls(mgr); cls.m_lits.append(lits); pad(cls.m_weights, lits.size(), one); cls.m_k = one; cls.m_eq = false; m_clauses.push_back(cls); m_orig_clauses.push_back(f); return result; } if (m.is_or(f)) { literal_vector lits; app* g = to_app(f); for (unsigned i = 0; i < g->get_num_args(); ++i) { lits.push_back(mk_literal(g->get_arg(i))); } literal result = mk_aux_literal(f); for (unsigned i = 0; i < lits.size(); ++i) { clause cls(mgr); cls.m_lits.push_back(result); cls.m_weights.push_back(one); cls.m_lits.push_back(~lits[i]); cls.m_weights.push_back(one); cls.m_k = one; cls.m_eq = false; m_clauses.push_back(cls); m_orig_clauses.push_back(f); } lits.push_back(~result); clause cls(mgr); cls.m_lits.append(lits); pad(cls.m_weights, lits.size(), one); cls.m_k = one; cls.m_eq = false; m_clauses.push_back(cls); m_orig_clauses.push_back(f); return result; } expr* x, *y; if ((m.is_eq(f, x, y) && m.is_bool(x)) || m.is_iff(f, x, y)) { literal a = mk_literal(x); literal b = mk_literal(y); literal result = mk_aux_literal(f); clause cls(mgr); cls.m_lits.push_back(~result); cls.m_lits.push_back(~a); cls.m_lits.push_back(b); pad(cls.m_weights, 3, one); cls.m_k = one; cls.m_eq = false; m_clauses.push_back(cls); m_orig_clauses.push_back(f); // actually, the clause that defines f cls.m_lits[0] = ~result; cls.m_lits[1] = a; cls.m_lits[2] = ~b; m_clauses.push_back(cls); m_orig_clauses.push_back(f); cls.m_lits[0] = result; cls.m_lits[1] = a; cls.m_lits[2] = b; m_clauses.push_back(cls); m_orig_clauses.push_back(f); cls.m_lits[0] = result; cls.m_lits[1] = ~a; cls.m_lits[2] = ~b; m_clauses.push_back(cls); m_orig_clauses.push_back(f); return result; } if (pb.is_ge(f)) { } IF_VERBOSE(0, verbose_stream() << "not handled: " << mk_pp(f, m) << "\n";); return mk_aux_literal(f); } bool compile_clause(expr* _f, clause& cls) { expr_ref tmp(m); m_rewrite(_f, tmp); if (!is_app(tmp)) return false; app* f = to_app(tmp); expr* f2; unsigned sz = f->get_num_args(); expr* const* args = f->get_args(); literal lit; rational coeff, k; if (m.is_not(f, f2) && pb.is_ge(f2)) { // ~(ax+by >= k) // <=> // ax + by < k // <=> // -ax - by >= -k + 1 // <=> // a(1-x) + b(1-y) >= -k + a + b + 1 sz = to_app(f2)->get_num_args(); args = to_app(f2)->get_args(); k = pb.get_k(f2); SASSERT(k.is_int()); k.neg(); k += rational::one(); expr_ref_vector args(m); vector coeffs; for (unsigned i = 0; i < sz; ++i) { args.push_back(m.mk_not(to_app(f2)->get_arg(i))); coeffs.push_back(pb.get_coeff(f2, i)); k += pb.get_coeff(f2, i); } tmp = pb.mk_ge(coeffs.size(), coeffs.c_ptr(), args.c_ptr(), k); return compile_clause(tmp, cls); } else if (pb.is_ge(f) || pb.is_eq(f)) { k = pb.get_k(f); SASSERT(k.is_int()); cls.m_k = k.to_mpq().numerator(); for (unsigned i = 0; i < sz; ++i) { coeff = pb.get_coeff(f, i); SASSERT(coeff.is_int()); lit = mk_literal(args[i]); if (lit == null_literal) return false; if (lit == false_literal) continue; if (lit == true_literal) { cls.m_k -= coeff.to_mpq().numerator(); continue; } cls.m_lits.push_back(lit); cls.m_weights.push_back(coeff.to_mpq().numerator()); if (get_value(lit)) { cls.m_value += coeff.to_mpq().numerator(); } } cls.m_eq = pb.is_eq(f); } else if (m.is_or(f)) { for (unsigned i = 0; i < sz; ++i) { lit = mk_literal(args[i]); if (lit == null_literal) return false; if (lit == false_literal) continue; if (lit == true_literal) return false; cls.m_lits.push_back(lit); cls.m_weights.push_back(mpz(1)); if (get_value(lit)) { cls.m_value += mpz(1); } } cls.m_eq = false; cls.m_k = mpz(1); } else if (m.is_true(f)) { return false; } else { lit = mk_literal(f); if (lit == null_literal) return false; SASSERT(lit != false_literal && lit != true_literal); cls.m_lits.push_back(lit); cls.m_weights.push_back(mpz(1)); cls.m_eq = true; cls.m_k = mpz(1); } return true; } }; pb_sls::pb_sls(ast_manager& m) { m_imp = alloc(imp, m); } pb_sls::~pb_sls() { dealloc(m_imp); } void pb_sls::add(expr* f) { m_imp->add(f); } void pb_sls::add(expr* f, rational const& w) { m_imp->add(f, w); } void pb_sls::set_model(model_ref& mdl) { m_imp->set_model(mdl); } lbool pb_sls::operator()() { return (*m_imp)(); } void pb_sls::collect_statistics(statistics& st) const { m_imp->collect_statistics(st); } void pb_sls::get_model(model_ref& mdl) { m_imp->get_model(mdl); } void pb_sls::reset() { m_imp->reset(); } bool pb_sls::soft_holds(unsigned index) { return m_imp->soft_holds(index); } void pb_sls::updt_params(params_ref& p) { m_imp->updt_params(p); } } z3-z3-4.8.7/src/opt/pb_sls.h000066400000000000000000000015521356505360400154560ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: pb_sls.h Abstract: SLS for PB optimization. Author: Nikolaj Bjorner (nbjorner) 2014-03-18 Notes: --*/ #ifndef PB_SLS_H_ #define PB_SLS_H_ #include "ast/pb_decl_plugin.h" #include "model/model.h" #include "util/lbool.h" #include "util/params.h" #include "util/statistics.h" namespace smt { class pb_sls { struct imp; imp* m_imp; public: pb_sls(ast_manager& m); ~pb_sls(); void add(expr* f); void add(expr* f, rational const& w); bool soft_holds(unsigned index); void set_model(model_ref& mdl); lbool operator()(); void collect_statistics(::statistics& st) const; void get_model(model_ref& mdl); void updt_params(params_ref& p); void reset(); }; }; #endif z3-z3-4.8.7/src/opt/sortmax.cpp000066400000000000000000000121101356505360400162140ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: sortmax.cpp Abstract: Theory based MaxSAT. Author: Nikolaj Bjorner (nbjorner) 2016-11-18 Notes: --*/ #include "opt/maxsmt.h" #include "util/uint_set.h" #include "ast/ast_pp.h" #include "model/model_smt2_pp.h" #include "smt/smt_theory.h" #include "smt/smt_context.h" #include "opt/opt_context.h" #include "util/sorting_network.h" #include "tactic/generic_model_converter.h" namespace opt { class sortmax : public maxsmt_solver_base { public: typedef expr* pliteral; typedef ptr_vector pliteral_vector; psort_nw m_sort; expr_ref_vector m_trail; func_decl_ref_vector m_fresh; ref m_filter; sortmax(maxsat_context& c, weights_t& ws, expr_ref_vector const& soft): maxsmt_solver_base(c, ws, soft), m_sort(*this), m_trail(m), m_fresh(m) {} ~sortmax() override {} lbool operator()() override { obj_map soft; if (!init()) { return l_false; } lbool is_sat = find_mutexes(soft); if (is_sat != l_true) { return is_sat; } m_filter = alloc(generic_model_converter, m, "sortmax"); rational offset = m_lower; m_upper = offset; expr_ref_vector in(m); expr_ref tmp(m); ptr_vector out; obj_map::iterator it = soft.begin(), end = soft.end(); for (; it != end; ++it) { if (!it->m_value.is_unsigned()) { throw default_exception("sortmax can only handle unsigned weights. Use a different heuristic."); } unsigned n = it->m_value.get_unsigned(); while (n > 0) { in.push_back(it->m_key); --n; } } m_sort.sorting(in.size(), in.c_ptr(), out); // initialize sorting network outputs using the initial assignment. unsigned first = 0; it = soft.begin(); for (; it != end; ++it) { if (m_model->is_true(it->m_key)) { unsigned n = it->m_value.get_unsigned(); while (n > 0) { s().assert_expr(out[first]); ++first; --n; } } else { m_upper += it->m_value; } } while (l_true == is_sat && first < out.size() && m_lower < m_upper) { trace_bounds("sortmax"); s().assert_expr(out[first]); is_sat = s().check_sat(0, nullptr); TRACE("opt", tout << is_sat << "\n"; s().display(tout); tout << "\n";); if (m.canceled()) { is_sat = l_undef; } if (is_sat == l_true) { ++first; s().get_model(m_model); update_assignment(); for (; first < out.size() && is_true(out[first]); ++first) { s().assert_expr(out[first]); } TRACE("opt", model_smt2_pp(tout, m, *m_model.get(), 0);); m_upper = m_lower + rational(out.size() - first); (*m_filter)(m_model); } } if (is_sat == l_false) { is_sat = l_true; m_lower = m_upper; } TRACE("opt", tout << "min cost: " << m_upper << "\n";); return is_sat; } void update_assignment() { for (soft& s : m_soft) s.set_value(is_true(s.s)); } bool is_true(expr* e) { return m_model->is_true(e); } // definitions used for sorting network pliteral mk_false() { return m.mk_false(); } pliteral mk_true() { return m.mk_true(); } pliteral mk_max(unsigned n, pliteral const* as) { return trail(m.mk_or(n, as)); } pliteral mk_min(unsigned n, pliteral const* as) { return trail(m.mk_and(n, as)); } pliteral mk_not(pliteral a) { if (m.is_not(a,a)) return a; return trail(m.mk_not(a)); } std::ostream& pp(std::ostream& out, pliteral lit) { return out << mk_pp(lit, m); } pliteral trail(pliteral l) { m_trail.push_back(l); return l; } pliteral fresh(char const* n) { expr_ref fr(m.mk_fresh_const(n, m.mk_bool_sort()), m); func_decl* f = to_app(fr)->get_decl(); m_fresh.push_back(f); m_filter->hide(f); return trail(fr); } void mk_clause(unsigned n, pliteral const* lits) { s().assert_expr(mk_or(m, n, lits)); } }; maxsmt_solver_base* mk_sortmax(maxsat_context& c, weights_t& ws, expr_ref_vector const& soft) { return alloc(sortmax, c, ws, soft); } } z3-z3-4.8.7/src/opt/wmax.cpp000066400000000000000000000250511356505360400155030ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: wmax.cpp Abstract: Theory based MaxSAT. Author: Nikolaj Bjorner (nbjorner) 2014-4-17 Notes: --*/ #include "opt/wmax.h" #include "util/uint_set.h" #include "ast/ast_pp.h" #include "model/model_smt2_pp.h" #include "smt/smt_theory.h" #include "smt/smt_context.h" #include "smt/theory_wmaxsat.h" #include "opt/opt_context.h" namespace opt { // ---------------------------------------------------------- // weighted max-sat using a custom theory solver for max-sat. // NB. it is quite similar to pseudo-Boolean propagation. class wmax : public maxsmt_solver_base { obj_map m_weights; obj_map m_keys; expr_ref_vector m_trail, m_defs; void reset() { m_weights.reset(); m_keys.reset(); m_trail.reset(); m_defs.reset(); } public: wmax(maxsat_context& c, weights_t& ws, expr_ref_vector const& soft): maxsmt_solver_base(c, ws, soft), m_trail(m), m_defs(m) {} ~wmax() override {} lbool operator()() override { TRACE("opt", tout << "weighted maxsat\n";); scoped_ensure_theory wth(*this); obj_map soft; reset(); lbool is_sat = find_mutexes(soft); if (is_sat != l_true) { return is_sat; } m_upper = m_lower; expr_ref_vector asms(m); vector cores; obj_map::iterator it = soft.begin(), end = soft.end(); for (; it != end; ++it) { assert_weighted(wth(), it->m_key, it->m_value); if (!is_true(it->m_key)) { m_upper += it->m_value; } } wth().init_min_cost(m_upper - m_lower); trace_bounds("wmax"); TRACE("opt", s().display(tout)<< "\n"; tout << "lower: " << m_lower << " upper: " << m_upper << "\n";); while (!m.canceled() && m_lower < m_upper) { is_sat = s().check_sat(0, nullptr); if (m.canceled()) { is_sat = l_undef; } if (is_sat == l_undef) { break; } if (is_sat == l_false) { TRACE("opt", tout << "Unsat\n";); break; } if (is_sat == l_true) { if (wth().is_optimal()) { m_upper = m_lower + wth().get_cost(); s().get_model(m_model); } expr_ref fml = wth().mk_block(); //DEBUG_CODE(verify_cores(cores);); s().assert_expr(fml); } update_cores(wth(), cores); wth().init_min_cost(m_upper - m_lower); trace_bounds("wmax"); SASSERT(m_lower <= m_upper); } update_assignment(); if (!m.canceled() && is_sat == l_undef && m_lower == m_upper) { is_sat = l_true; } if (is_sat == l_false) { is_sat = l_true; m_lower = m_upper; } TRACE("opt", tout << "min cost: " << m_upper << "\n";); return is_sat; } bool is_true(expr* e) { return m_model->is_true(e); } void update_assignment() { for (soft& s : m_soft) s.set_value(is_true(s.s)); } struct compare_asm { wmax& max; compare_asm(wmax& max):max(max) {} bool operator()(expr* a, expr* b) const { return max.m_weights[a] > max.m_weights[b]; } }; void mk_assumptions(expr_ref_vector& asms) { ptr_vector _asms; obj_map::iterator it = m_weights.begin(), end = m_weights.end(); for (; it != end; ++it) { _asms.push_back(it->m_key); } compare_asm comp(*this); std::sort(_asms.begin(),_asms.end(), comp); asms.reset(); for (unsigned i = 0; i < _asms.size(); ++i) { asms.push_back(m.mk_not(_asms[i])); } } void verify_cores(vector const& cores) { for (unsigned i = 0; i < cores.size(); ++i) { verify_core(cores[i]); } } void verify_core(expr_ref_vector const& core) { s().push(); s().assert_expr(core); VERIFY(l_false == s().check_sat(0, nullptr)); s().pop(1); } void update_cores(smt::theory_wmaxsat& th, vector const& cores) { obj_hashtable seen; bool updated = false; unsigned min_core_size = UINT_MAX; for (unsigned i = 0; i < cores.size(); ++i) { expr_ref_vector const& core = cores[i]; if (core.size() <= 20) { s().assert_expr(m.mk_not(mk_and(core))); } min_core_size = std::min(core.size(), min_core_size); if (core.size() >= 11) { continue; } bool found = false; for (unsigned j = 0; !found && j < core.size(); ++j) { found = seen.contains(core[j]); } if (found) { continue; } for (unsigned j = 0; j < core.size(); ++j) { seen.insert(core[j]); } update_core(th, core); updated = true; } // if no core was selected, then take the smallest cores. for (unsigned i = 0; !updated && i < cores.size(); ++i) { expr_ref_vector const& core = cores[i]; if (core.size() > min_core_size + 2) { continue; } bool found = false; for (unsigned j = 0; !found && j < core.size(); ++j) { found = seen.contains(core[j]); } if (found) { continue; } for (unsigned j = 0; j < core.size(); ++j) { seen.insert(core[j]); } update_core(th, core); } } rational remove_negations(smt::theory_wmaxsat& th, expr_ref_vector const& core, ptr_vector& keys, vector& weights) { rational min_weight(-1); for (unsigned i = 0; i < core.size(); ++i) { expr* e = nullptr; VERIFY(m.is_not(core[i], e)); keys.push_back(m_keys[e]); rational weight = m_weights[e]; if (i == 0 || weight < min_weight) { min_weight = weight; } weights.push_back(weight); m_weights.erase(e); m_keys.erase(e); th.disable_var(e); } for (unsigned i = 0; i < core.size(); ++i) { rational weight = weights[i]; if (weight > min_weight) { weight -= min_weight; assert_weighted(th, keys[i], weight); } } return min_weight; } // assert maxres clauses // assert new core members with value of current model. // update lower bound // bounds get re-normalized when solver is invoked. // each element of core is negated literal from theory_wmaxsat // disable those literals from th void update_core(smt::theory_wmaxsat& th, expr_ref_vector const& core) { ptr_vector keys; vector weights; rational min_weight = remove_negations(th, core, keys, weights); max_resolve(th, keys, min_weight); m_lower += min_weight; // std::cout << core << " " << min_weight << "\n"; } void max_resolve(smt::theory_wmaxsat& th, ptr_vector const& core, rational const& w) { SASSERT(!core.empty()); expr_ref fml(m), asum(m); app_ref cls(m), d(m), dd(m); // // d_0 := true // d_i := b_{i-1} and d_{i-1} for i = 1...sz-1 // soft (b_i or !d_i) // == (b_i or !(!b_{i-1} or d_{i-1})) // == (b_i or b_0 & b_1 & ... & b_{i-1}) // // Soft constraint is satisfied if previous soft constraint // holds or if it is the first soft constraint to fail. // // Soundness of this rule can be established using MaxRes // for (unsigned i = 1; i < core.size(); ++i) { expr* b_i = core[i-1]; expr* b_i1 = core[i]; if (i == 1) { d = to_app(b_i); } else if (i == 2) { d = m.mk_and(b_i, d); m_trail.push_back(d); } else { dd = mk_fresh_bool("d"); fml = m.mk_implies(dd, d); s().assert_expr(fml); m_defs.push_back(fml); fml = m.mk_implies(dd, b_i); s().assert_expr(fml); m_defs.push_back(fml); fml = m.mk_and(d, b_i); update_model(dd, fml); d = dd; } cls = m.mk_or(b_i1, d); m_trail.push_back(cls); assert_weighted(th, cls, w); } } expr* assert_weighted(smt::theory_wmaxsat& th, expr* key, rational const& w) { expr* c = th.assert_weighted(key, w); m_weights.insert(c, w); m_keys.insert(c, key); m_trail.push_back(c); return c; } void update_model(expr* def, expr* value) { if (m_model) { m_model->register_decl(to_app(def)->get_decl(), (*m_model)(value)); } } }; maxsmt_solver_base* mk_wmax(maxsat_context& c, weights_t& ws, expr_ref_vector const& soft) { return alloc(wmax, c, ws, soft); } } z3-z3-4.8.7/src/opt/wmax.h000066400000000000000000000007201356505360400151440ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: wmax.h Abstract: Theory Solver based MaxSAT. Author: Nikolaj Bjorner (nbjorner) 2014-4-17 Notes: --*/ #ifndef WMAX_H_ #define WMAX_H_ #include "opt/maxsmt.h" namespace opt { maxsmt_solver_base* mk_wmax(maxsat_context& c, weights_t & ws, expr_ref_vector const& soft); maxsmt_solver_base* mk_sortmax(maxsat_context& c, weights_t & ws, expr_ref_vector const& soft); } #endif z3-z3-4.8.7/src/parsers/000077500000000000000000000000001356505360400146755ustar00rootroot00000000000000z3-z3-4.8.7/src/parsers/smt2/000077500000000000000000000000001356505360400155625ustar00rootroot00000000000000z3-z3-4.8.7/src/parsers/smt2/CMakeLists.txt000066400000000000000000000002301356505360400203150ustar00rootroot00000000000000z3_add_component(smt2parser SOURCES marshal.cpp smt2parser.cpp smt2scanner.cpp COMPONENT_DEPENDENCIES cmd_context parser_util ) z3-z3-4.8.7/src/parsers/smt2/marshal.cpp000066400000000000000000000021271356505360400177170ustar00rootroot00000000000000/*++ Copyright (c) 2017 Arie Gurfinkel Module Name: marshal.cpp Abstract: marshaling and unmarshaling of expressions --*/ #include "parsers/smt2/marshal.h" #include #include "cmd_context/cmd_context.h" #include "parsers/smt2/smt2parser.h" #include "util/vector.h" #include "ast/ast_smt_pp.h" #include "ast/ast_pp.h" #include "ast/ast_util.h" std::ostream &marshal(std::ostream &os, expr_ref e, ast_manager &m) { ast_smt_pp pp(m); pp.display_smt2(os, e); return os; } std::string marshal(expr_ref e, ast_manager &m) { std::stringstream ss; marshal(ss, e, m); return ss.str(); } expr_ref unmarshal(std::istream &is, ast_manager &m) { cmd_context ctx(false, &m); ctx.set_ignore_check(true); if (!parse_smt2_commands(ctx, is)) { return expr_ref(nullptr, m); } ptr_vector::const_iterator it = ctx.assertions().begin(); unsigned size = ctx.assertions().size(); return expr_ref(mk_and(m, size, it), m); } expr_ref unmarshal(std::string s, ast_manager &m) { std::istringstream is(s); return unmarshal(is, m); } z3-z3-4.8.7/src/parsers/smt2/marshal.h000066400000000000000000000007421356505360400173650ustar00rootroot00000000000000/*++ Copyright (c) 2017 Arie Gurfinkel Module Name: marshal.h Abstract: marshaling and unmarshaling of expressions --*/ #ifndef _SPACER_MARSHAL_H_ #define _SPACER_MARSHAL_H_ #include #include #include "ast/ast.h" std::ostream &marshal(std::ostream &os, expr_ref e, ast_manager &m); std::string marshal(expr_ref e, ast_manager &m); expr_ref unmarshal(std::string s, ast_manager &m); expr_ref unmarshal(std::istream &is, ast_manager &m); #endif z3-z3-4.8.7/src/parsers/smt2/smt2parser.cpp000066400000000000000000003743231356505360400204040ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: smt2parser.h Abstract: SMT 2.0 parser Author: Leonardo de Moura (leonardo) 2011-03-01 Revision History: --*/ #include "util/stack.h" #include "ast/datatype_decl_plugin.h" #include "ast/bv_decl_plugin.h" #include "ast/arith_decl_plugin.h" #include "ast/seq_decl_plugin.h" #include "ast/ast_pp.h" #include "ast/well_sorted.h" #include "ast/rewriter/rewriter.h" #include "ast/rewriter/var_subst.h" #include "ast/has_free_vars.h" #include "ast/ast_smt2_pp.h" #include "parsers/smt2/smt2parser.h" #include "parsers/smt2/smt2scanner.h" #include "parsers/util/pattern_validation.h" #include "parsers/util/parser_params.hpp" #include namespace smt2 { typedef cmd_exception parser_exception; class parser { cmd_context & m_ctx; params_ref m_params; scanner m_scanner; scanner::token m_curr; cmd * m_curr_cmd; stack m_stack; struct local { expr * m_term; unsigned m_level; local():m_term(nullptr), m_level(0) {} local(expr * t, unsigned l):m_term(t), m_level(l) {} }; symbol_table m_env; unsigned m_num_bindings; dictionary m_sort_id2param_idx; dictionary m_dt_name2idx; dictionary m_dt_name2arity; svector m_dt_names; scoped_ptr m_psort_stack; scoped_ptr m_sort_stack; scoped_ptr m_expr_stack; unsigned m_num_expr_frames; scoped_ptr m_pattern_stack; scoped_ptr m_nopattern_stack; svector m_symbol_stack; vector m_param_stack; scoped_ptr m_sexpr_stack; scoped_ptr m_bv_util; scoped_ptr m_arith_util; scoped_ptr m_datatype_util; scoped_ptr m_seq_util; scoped_ptr m_pattern_validator; scoped_ptr m_var_shifter; symbol m_let; symbol m_bang; symbol m_forall; symbol m_exists; symbol m_lambda; symbol m_as; symbol m_not; symbol m_root_obj; symbol m_named; symbol m_weight; symbol m_qid; symbol m_skid; symbol m_ex_act; symbol m_pattern; symbol m_nopattern; symbol m_lblneg; symbol m_lblpos; symbol m_assert; symbol m_check_sat; symbol m_define_fun; symbol m_define_const; symbol m_model_add; symbol m_model_del; symbol m_declare_fun; symbol m_declare_const; symbol m_define_sort; symbol m_declare_sort; symbol m_declare_datatypes; symbol m_declare_datatype; symbol m_par; symbol m_push; symbol m_pop; symbol m_get_value; symbol m_reset; symbol m_check_sat_assuming; symbol m_define_fun_rec; symbol m_define_funs_rec; symbol m_match; symbol m_case; symbol m_underscore; typedef std::pair named_expr; named_expr m_last_named_expr; ast_manager & m() const { return m_ctx.m(); } pdecl_manager & pm() const { return m_ctx.pm(); } sexpr_manager & sm() const { return m_ctx.sm(); } bool m_ignore_user_patterns; bool m_ignore_bad_patterns; bool m_display_error_for_vs; bool ignore_user_patterns() const { return m_ignore_user_patterns; } bool ignore_bad_patterns() const { return m_ignore_bad_patterns; } bool use_vs_format() const { return m_display_error_for_vs; } struct psort_frame { psort_decl * m_decl; unsigned m_spos; // position of m_psort_stack psort_frame(parser & p, psort_decl * d, unsigned spos): m_decl(d), m_spos(spos) { } }; typedef psort_frame sort_frame; enum expr_frame_kind { EF_APP, EF_LET, EF_LET_DECL, EF_MATCH, EF_QUANT, EF_ATTR_EXPR, EF_PATTERN }; struct expr_frame { expr_frame_kind m_kind; expr_frame(expr_frame_kind k):m_kind(k) {} }; struct app_frame : public expr_frame { symbol m_f; unsigned m_expr_spos; unsigned m_param_spos; bool m_as_sort; app_frame(symbol const & f, unsigned expr_spos, unsigned param_spos, bool as_sort): expr_frame(EF_APP), m_f(f), m_expr_spos(expr_spos), m_param_spos(param_spos), m_as_sort(as_sort) {} }; struct quant_frame : public expr_frame { quantifier_kind m_kind; symbol m_qid; symbol m_skid; unsigned m_weight; unsigned m_pat_spos; unsigned m_nopat_spos; unsigned m_sym_spos; unsigned m_sort_spos; unsigned m_expr_spos; quant_frame(quantifier_kind k, unsigned pat_spos, unsigned nopat_spos, unsigned sym_spos, unsigned sort_spos, unsigned expr_spos): expr_frame(EF_QUANT), m_kind(k), m_weight(1), m_pat_spos(pat_spos), m_nopat_spos(nopat_spos), m_sym_spos(sym_spos), m_sort_spos(sort_spos), m_expr_spos(expr_spos) {} }; struct match_frame : public expr_frame { match_frame():expr_frame(EF_MATCH) {} }; struct let_frame : public expr_frame { bool m_in_decls; unsigned m_sym_spos; unsigned m_expr_spos; let_frame(unsigned sym_spos, unsigned expr_spos):expr_frame(EF_LET), m_in_decls(true), m_sym_spos(sym_spos), m_expr_spos(expr_spos) {} }; struct let_decl_frame : public expr_frame { let_decl_frame():expr_frame(EF_LET_DECL) {} }; struct attr_expr_frame : public expr_frame { expr_frame * m_prev; unsigned m_sym_spos; unsigned m_expr_spos; symbol m_last_symbol; attr_expr_frame(expr_frame * prev, unsigned sym_spos, unsigned expr_spos): expr_frame(EF_ATTR_EXPR), m_prev(prev), m_sym_spos(sym_spos), m_expr_spos(expr_spos) {} }; struct pattern_frame : public expr_frame { unsigned m_expr_spos; pattern_frame(unsigned expr_spos): expr_frame(EF_PATTERN), m_expr_spos(expr_spos) { } }; struct sexpr_frame { unsigned m_spos; // position of m_sexpr_stack sexpr_frame(unsigned spos): m_spos(spos) { } }; void reset_stack() { m_stack.reset(); } psort_ref_vector & psort_stack() { if (m_psort_stack.get() == nullptr) m_psort_stack = alloc(psort_ref_vector, pm()); return *(m_psort_stack.get()); } sort_ref_vector & sort_stack() { if (m_sort_stack.get() == nullptr) m_sort_stack = alloc(sort_ref_vector, m()); return *(m_sort_stack.get()); } expr_ref_vector & expr_stack() { if (m_expr_stack.get() == nullptr) m_expr_stack = alloc(expr_ref_vector, m()); return *(m_expr_stack.get()); } template static unsigned size(scoped_ptr & v) { return v.get() == nullptr ? 0 : v->size(); } template static void shrink(scoped_ptr & v, unsigned old_sz) { if (v.get() == nullptr) { SASSERT(old_sz == 0); } else { v->shrink(old_sz); } } expr_ref_vector & pattern_stack() { if (m_pattern_stack.get() == nullptr) m_pattern_stack = alloc(expr_ref_vector, m()); return *(m_pattern_stack.get()); } expr_ref_vector & nopattern_stack() { if (m_nopattern_stack.get() == nullptr) m_nopattern_stack = alloc(expr_ref_vector, m()); return *(m_nopattern_stack.get()); } svector & symbol_stack() { return m_symbol_stack; } sexpr_ref_vector & sexpr_stack() { if (m_sexpr_stack.get() == nullptr) m_sexpr_stack = alloc(sexpr_ref_vector, sm()); return *(m_sexpr_stack.get()); } arith_util & autil() { if (m_arith_util.get() == nullptr) m_arith_util = alloc(arith_util, m()); return *(m_arith_util.get()); } datatype_util & dtutil() { if (m_datatype_util.get() == nullptr) m_datatype_util = alloc(datatype_util, m()); return *(m_datatype_util.get()); } seq_util & sutil() { if (m_seq_util.get() == nullptr) m_seq_util = alloc(seq_util, m()); return *(m_seq_util.get()); } bv_util & butil() { if (m_bv_util.get() == nullptr) m_bv_util = alloc(bv_util, m()); return *(m_bv_util.get()); } pattern_validator & pat_validator() { if (m_pattern_validator.get() == nullptr) { m_pattern_validator = alloc(pattern_validator, m()); } return *(m_pattern_validator.get()); } var_shifter & shifter() { if (m_var_shifter.get() == nullptr) m_var_shifter = alloc(var_shifter, m()); return *(m_var_shifter.get()); } unsigned m_cache_end; std::vector m_cached_strings; int m_num_open_paren; void scan_core() { m_cache_end = m_scanner.cache_size(); m_curr = m_scanner.scan(); } void scan() { switch (m_curr) { case scanner::LEFT_PAREN: m_num_open_paren++; break; case scanner::RIGHT_PAREN: m_num_open_paren--; break; default: break; } scan_core(); } void next() { if (m_curr != scanner::EOF_TOKEN) scan(); } scanner::token curr() const { return m_curr; } // consume garbage // return true if managed to recover from the error... bool sync_after_error() { unsigned num_errors = 0; while (num_errors < 100) { try { while (curr_is_rparen()) next(); if (m_num_open_paren < 0) m_num_open_paren = 0; if (curr() == scanner::EOF_TOKEN && m_num_open_paren == 0) return true; SASSERT(m_num_open_paren >= 0); while (m_num_open_paren > 0 || !curr_is_lparen()) { TRACE("sync", tout << "sync(): curr: " << curr() << "\n"; tout << "m_num_open_paren: " << m_num_open_paren << ", line: " << m_scanner.get_line() << ", pos: " << m_scanner.get_pos() << "\n";); if (curr() == scanner::EOF_TOKEN) { return false; } SASSERT(m_num_open_paren >= 0); next(); SASSERT(m_num_open_paren >= -1); if (m_num_open_paren < 0) m_num_open_paren = 0; SASSERT(m_num_open_paren >= 0); } return true; } catch (scanner_exception & ex) { SASSERT(ex.has_pos()); error(ex.line(), ex.pos(), ex.msg()); ++num_errors; } } return false; } void check_next(scanner::token t, char const * msg) { if (curr() == t) { next(); return; } std::ostringstream str; str << msg << " got " << curr_id(); throw parser_exception(str.str()); } symbol const & curr_id() const { return m_scanner.get_id(); } rational curr_numeral() const { return m_scanner.get_number(); } unsigned curr_unsigned() { rational n = curr_numeral(); if (!n.is_unsigned()) throw parser_exception("invalid indexed identifier, index is too big to fit in an unsigned machine integer"); return n.get_unsigned(); } bool curr_is_identifier() const { return curr() == scanner::SYMBOL_TOKEN; } bool curr_is_keyword() const { return curr() == scanner::KEYWORD_TOKEN; } bool curr_is_string() const { return curr() == scanner::STRING_TOKEN; } bool curr_is_lparen() const { return curr() == scanner::LEFT_PAREN; } bool curr_is_rparen() const { return curr() == scanner::RIGHT_PAREN; } bool curr_is_int() const { return curr() == scanner::INT_TOKEN; } bool curr_is_float() const { return curr() == scanner::FLOAT_TOKEN; } bool curr_id_is_underscore() const { SASSERT(curr_is_identifier()); return curr_id() == m_underscore; } bool curr_id_is_as() const { SASSERT(curr_is_identifier()); return curr_id() == m_as; } bool curr_id_is_reserved() const { return curr_id_is_underscore() || curr_id_is_as(); } bool curr_id_is_match() const { SASSERT(curr_is_identifier()); return curr_id() == m_match; } bool curr_id_is_case() const { return curr_id() == m_case; } bool curr_id_is_forall() const { SASSERT(curr_is_identifier()); return curr_id() == m_forall; } bool curr_id_is_exists() const { SASSERT(curr_is_identifier()); return curr_id() == m_exists; } bool curr_id_is_lambda() const { SASSERT(curr_is_identifier()); return curr_id() == m_lambda; } bool curr_id_is_bang() const { SASSERT(curr_is_identifier()); return curr_id() == m_bang; } bool curr_id_is_let() const { SASSERT(curr_is_identifier()); return curr_id() == m_let; } bool curr_id_is_root_obj() const { SASSERT(curr_is_identifier()); return curr_id() == m_root_obj; } void check_lparen(char const * msg) { if (!curr_is_lparen()) throw parser_exception(msg); } void check_lparen_next(char const * msg) { check_next(scanner::LEFT_PAREN, msg); } void check_rparen_next(char const * msg) { check_next(scanner::RIGHT_PAREN, msg); } void check_rparen(char const * msg) { if (!curr_is_rparen()) throw parser_exception(msg); } void check_id_next(symbol const & id, char const * msg) { if (!curr_is_identifier() || curr_id() != id) throw parser_exception(msg); next(); } void check_underscore_next(char const * msg) { check_id_next(m_underscore, msg); } void check_as_next(char const * msg) { check_id_next(m_as, msg); } void check_identifier(char const * msg) { if (!curr_is_identifier()) throw parser_exception(msg); } void check_nonreserved_identifier(char const * msg) { if (!curr_is_identifier() || curr_id_is_reserved()) throw parser_exception(msg); } void check_keyword(char const * msg) { if (!curr_is_keyword()) throw parser_exception(msg); } void check_string(char const * msg) { if (!curr_is_string()) throw parser_exception(msg); } void check_int(char const * msg) { if (!curr_is_int()) throw parser_exception(msg); } void check_int_or_float(char const * msg) { if (!curr_is_int() && !curr_is_float()) throw parser_exception(msg); } void check_float(char const * msg) { if (!curr_is_float()) throw parser_exception(msg); } symbol check_identifier_next(char const * msg) { check_identifier(msg); symbol s = curr_id(); next(); return s; } char const * m_current_file; void set_current_file(char const * s) { m_current_file = s; } void error(unsigned line, unsigned pos, char const * msg) { m_ctx.set_cancel(false); if (use_vs_format()) { m_ctx.diagnostic_stream() << "Z3(" << line << ", " << pos << "): ERROR: " << msg; if (msg[strlen(msg)-1] != '\n') m_ctx.diagnostic_stream() << std::endl; } else { m_ctx.regular_stream() << "(error \""; if (m_current_file) m_ctx.regular_stream() << m_current_file << ": "; m_ctx.regular_stream()<< "line " << line << " column " << pos << ": " << escaped(msg, true) << "\")" << std::endl; } if (m_ctx.exit_on_error()) { // WORKAROUND: ASan's LeakSanitizer reports many false positives when // calling `exit()` so call `_Exit()` instead which avoids invoking leak // checking. _Exit(1); } } void error(char const * msg) { error(m_scanner.get_line(), m_scanner.get_pos(), msg); } void error_wo_pos(char const * msg) { if (use_vs_format()) { m_ctx.diagnostic_stream() << "Z3: ERROR: " << msg; if (msg[strlen(msg)-1] != '\n') m_ctx.diagnostic_stream() << std::endl; } else { m_ctx.regular_stream() << "(error : " << escaped(msg, true) << "\")" << std::endl; } } void unknown_sort(symbol id, char const* context = "") { std::string msg = context; if (context[0]) msg += ": "; msg += "unknown sort '"; msg += id.str() + "'"; throw parser_exception(std::move(msg)); } void consume_sexpr() { unsigned num_parens = 0; do { switch (curr()) { case scanner::LEFT_PAREN: num_parens++; break; case scanner::RIGHT_PAREN: if (num_parens == 0) throw parser_exception("invalid s-expression, unexpected ')'"); num_parens--; break; case scanner::SYMBOL_TOKEN: case scanner::KEYWORD_TOKEN: case scanner::STRING_TOKEN: case scanner::INT_TOKEN: case scanner::FLOAT_TOKEN: case scanner::BV_TOKEN: break; case scanner::EOF_TOKEN: throw parser_exception("invalid s-expression, unexpected end of file"); break; default: throw parser_exception("invalid s-expression, unexpected input"); break; } next(); } while (num_parens > 0); } void parse_sexpr() { unsigned stack_pos = sexpr_stack().size(); (void)stack_pos; unsigned num_frames = 0; do { unsigned line = m_scanner.get_line(); unsigned pos = m_scanner.get_pos(); switch (curr()) { case scanner::LEFT_PAREN: { void * mem = m_stack.allocate(sizeof(sexpr_frame)); new (mem) sexpr_frame(sexpr_stack().size()); num_frames++; break; } case scanner::RIGHT_PAREN: { if (num_frames == 0) throw parser_exception("invalid s-expression, unexpected ')'"); num_frames--; sexpr_frame * fr = static_cast(m_stack.top()); unsigned spos = fr->m_spos; unsigned epos = sexpr_stack().size(); SASSERT(epos >= spos); unsigned num = epos - spos; if (num == 0) throw parser_exception("invalid empty s-expression"); sexpr * r = sm().mk_composite(num, sexpr_stack().c_ptr() + spos, line, pos); sexpr_stack().shrink(spos); sexpr_stack().push_back(r); m_stack.deallocate(fr); break; } case scanner::SYMBOL_TOKEN: sexpr_stack().push_back(sm().mk_symbol(curr_id(), line, pos)); break; case scanner::KEYWORD_TOKEN: sexpr_stack().push_back(sm().mk_keyword(curr_id(), line, pos)); break; case scanner::STRING_TOKEN: sexpr_stack().push_back(sm().mk_string(m_scanner.get_string(), line, pos)); break; case scanner::INT_TOKEN: case scanner::FLOAT_TOKEN: sexpr_stack().push_back(sm().mk_numeral(curr_numeral(), line, pos)); break; case scanner::BV_TOKEN: sexpr_stack().push_back(sm().mk_bv_numeral(curr_numeral(), m_scanner.get_bv_size(), line, pos)); break; case scanner::EOF_TOKEN: throw parser_exception("invalid s-expression, unexpected end of file"); break; default: throw parser_exception("invalid s-expression, unexpected input"); break; } next(); } while (num_frames > 0); SASSERT(sexpr_stack().size() == stack_pos + 1); } sort * parse_sort_name(char const* context = "") { SASSERT(curr_is_identifier()); symbol id = curr_id(); psort_decl * d = m_ctx.find_psort_decl(id); if (d == nullptr) unknown_sort(id, context); if (!d->has_var_params() && d->get_num_params() != 0) throw parser_exception("sort constructor expects parameters"); sort * r = d->instantiate(pm()); if (r == nullptr) throw parser_exception("invalid sort application"); next(); return r; } psort * parse_psort_name(bool ignore_unknown_sort = false) { SASSERT(curr_is_identifier()); symbol id = curr_id(); psort_decl * d = m_ctx.find_psort_decl(id); if (d != nullptr) { if (!d->has_var_params() && d->get_num_params() != 0) throw parser_exception("sort constructor expects parameters"); next(); return pm().mk_psort_app(d); } else { int idx = 0; if (m_sort_id2param_idx.find(id, idx)) { next(); return pm().mk_psort_var(m_sort_id2param_idx.size(), idx); } else { if (!ignore_unknown_sort) { unknown_sort(id); UNREACHABLE(); } return nullptr; } } } sort * parse_indexed_sort() { SASSERT(curr_is_identifier()); SASSERT(curr_id_is_underscore()); next(); symbol id = check_identifier_next("invalid indexed sort, symbol expected"); psort_decl * d = m_ctx.find_psort_decl(id); if (d == nullptr) unknown_sort(id); sbuffer args; while (!curr_is_rparen()) { check_int("invalid indexed sort, integer or ')' expected"); unsigned u = curr_unsigned(); args.push_back(u); next(); } sort * r = d->instantiate(pm(), args.size(), args.c_ptr()); if (r == nullptr) throw parser_exception("invalid sort application"); next(); return r; } void push_psort_app_frame() { SASSERT(curr_is_identifier()); symbol id = curr_id(); psort_decl * d = m_ctx.find_psort_decl(id); if (d == nullptr) { unknown_sort(id); } next(); void * mem = m_stack.allocate(sizeof(psort_frame)); new (mem) psort_frame(*this, d, psort_stack().size()); } void pop_psort_app_frame() { SASSERT(curr_is_rparen()); psort_frame * fr = static_cast(m_stack.top()); psort_decl * d = fr->m_decl; unsigned spos = fr->m_spos; unsigned epos = psort_stack().size(); SASSERT(epos >= spos); unsigned num = epos - spos; if (!d->has_var_params() && d->get_num_params() != num) { TRACE("smt2parser", tout << "num: " << num << ", d->get_num_params(): " << d->get_num_params() << "\n";); throw parser_exception("invalid number of parameters to sort constructor"); } psort * r = pm().mk_psort_app(m_sort_id2param_idx.size(), d, num, psort_stack().c_ptr() + spos); psort_stack().shrink(spos); psort_stack().push_back(r); m_stack.deallocate(fr); next(); } void parse_psort(bool ignore_unknown_sort = false) { unsigned stack_pos = psort_stack().size(); (void)stack_pos; unsigned num_frames = 0; do { if (curr_is_identifier()) { psort_stack().push_back(parse_psort_name(false)); } else if (curr_is_rparen()) { if (num_frames == 0) throw parser_exception("invalid sort, unexpected ')'"); pop_psort_app_frame(); num_frames--; } else { check_lparen_next("invalid sort, symbol, '_' or '(' expected"); if (!curr_is_identifier()) throw parser_exception("invalid sort, symbol or '_' expected"); if (curr_id_is_underscore()) { psort_stack().push_back(pm().mk_psort_cnst(parse_indexed_sort())); } else { push_psort_app_frame(); num_frames++; } } } while (num_frames > 0); SASSERT(psort_stack().size() == stack_pos + 1); } void push_sort_app_frame() { SASSERT(curr_is_identifier()); symbol id = curr_id(); psort_decl * d = m_ctx.find_psort_decl(id); if (d == nullptr) unknown_sort(id); next(); void * mem = m_stack.allocate(sizeof(sort_frame)); new (mem) sort_frame(*this, d, sort_stack().size()); } void pop_sort_app_frame() { SASSERT(curr_is_rparen()); sort_frame * fr = static_cast(m_stack.top()); psort_decl * d = fr->m_decl; unsigned spos = fr->m_spos; unsigned epos = sort_stack().size(); SASSERT(epos >= spos); unsigned num = epos - spos; if (!d->has_var_params() && d->get_num_params() != num) { TRACE("smt2parser", tout << "num: " << num << ", d->get_num_params(): " << d->get_num_params() << "\n";); throw parser_exception("invalid number of parameters to sort constructor"); } sort * r = d->instantiate(pm(), num, sort_stack().c_ptr() + spos); if (r == nullptr) throw parser_exception("invalid sort application"); sort_stack().shrink(spos); sort_stack().push_back(r); m_stack.deallocate(fr); next(); } void parse_sort(char const* context) { unsigned stack_pos = sort_stack().size(); (void)stack_pos; unsigned num_frames = 0; do { if (curr_is_identifier()) { sort_stack().push_back(parse_sort_name(context)); } else if (curr_is_rparen()) { if (num_frames == 0) { throw parser_exception(std::string(context) + " invalid sort, unexpected ')'"); } pop_sort_app_frame(); num_frames--; } else { check_lparen_next("invalid sort, symbol, '_' or '(' expected"); if (!curr_is_identifier()) throw parser_exception(std::string(context) + " invalid sort, symbol or '_' expected"); if (curr_id_is_underscore()) { sort_stack().push_back(parse_indexed_sort()); } else { push_sort_app_frame(); num_frames++; } } } while (num_frames > 0); SASSERT(sort_stack().size() == stack_pos + 1); } unsigned parse_sorts(char const* context) { unsigned sz = 0; check_lparen_next(context); while (!curr_is_rparen()) { parse_sort(context); sz++; } next(); return sz; } unsigned parse_symbols() { unsigned sz = 0; check_lparen_next("invalid list of symbols, '(' expected"); while (!curr_is_rparen()) { m_symbol_stack.push_back(check_identifier_next("invalid list of symbols, symbol or ')' expected")); sz++; } next(); return sz; } ptype parse_ptype() { SASSERT(curr_is_identifier()); psort * p = parse_psort_name(true); ptype result; if (p != nullptr) { result = ptype(p); } else { // parse_psort_name failed, identifier was not consumed. int idx; if (m_dt_name2idx.find(curr_id(), idx)) { result = ptype(idx); } else { result = ptype(curr_id()); } SASSERT(curr_is_identifier()); next(); } return result; } // [ '(' identifier sort ')' ]+ void parse_accessor_decls(paccessor_decl_ref_buffer & a_decls) { while (!curr_is_rparen()) { check_lparen_next("invalid datatype declaration, '(' or ')' expected"); symbol a_name = check_identifier_next("invalid accessor declaration, symbol (accessor name) expected"); if (curr_is_identifier()) { a_decls.push_back(pm().mk_paccessor_decl(m_sort_id2param_idx.size(), a_name, parse_ptype())); } else { parse_psort(true); a_decls.push_back(pm().mk_paccessor_decl(m_sort_id2param_idx.size(), a_name, ptype(psort_stack().back()))); psort_stack().pop_back(); } check_rparen_next("invalid accessor declaration, ')' expected"); } } // [ '(' identifier accessors ')' ]+ void parse_constructor_decls(pconstructor_decl_ref_buffer & ct_decls) { while (!curr_is_rparen()) { if (curr_is_identifier()) { symbol ct_name = curr_id(); std::string r_str = "is-"; r_str += curr_id().str(); symbol r_name(r_str.c_str()); next(); TRACE("datatype_parser_bug", tout << ct_name << " " << r_name << "\n";); ct_decls.push_back(pm().mk_pconstructor_decl(m_sort_id2param_idx.size(), ct_name, r_name, 0, nullptr)); } else { check_lparen_next("invalid datatype declaration, '(' or ')' expected"); check_identifier("invalid constructor declaration, symbol (constructor name) expected"); symbol ct_name = curr_id(); std::string r_str = "is-"; r_str += curr_id().str(); symbol r_name(r_str.c_str()); next(); paccessor_decl_ref_buffer new_a_decls(pm()); parse_accessor_decls(new_a_decls); ct_decls.push_back(pm().mk_pconstructor_decl(m_sort_id2param_idx.size(), ct_name, r_name, new_a_decls.size(), new_a_decls.c_ptr())); check_rparen_next("invalid constructor declaration, ')' expected"); } } if (ct_decls.empty()) throw parser_exception("invalid datatype declaration, datatype does not have any constructors"); } void parse_declare_datatypes() { SASSERT(curr_is_identifier()); SASSERT(curr_id() == m_declare_datatypes); next(); unsigned line = m_scanner.get_line(); unsigned pos = m_scanner.get_pos(); m_dt_name2idx.reset(); bool is_smt2_6 = parse_sort_decl_or_params(); unsigned i = 0; pdatatype_decl_ref_buffer new_dt_decls(pm()); check_lparen_next("invalid datatype declaration, '(' expected"); pdatatype_decl_ref d(pm()); while (!curr_is_rparen()) { pconstructor_decl_ref_buffer new_ct_decls(pm()); if (is_smt2_6) { if (i >= m_dt_names.size()) { throw parser_exception("invalid datatype declaration, too many data-type bodies defined"); } symbol dt_name = m_dt_names[i]; parse_datatype_dec(nullptr, new_ct_decls); d = pm().mk_pdatatype_decl(m_dt_name2arity.find(dt_name), dt_name, new_ct_decls.size(), new_ct_decls.c_ptr()); } else { check_lparen_next("invalid datatype declaration, '(' or ')' expected"); symbol dt_name = check_identifier_next("invalid datatype declaration, symbol (datatype name) expected"); m_dt_name2idx.insert(dt_name, i); parse_constructor_decls(new_ct_decls); d = pm().mk_pdatatype_decl(m_sort_id2param_idx.size(), dt_name, new_ct_decls.size(), new_ct_decls.c_ptr()); check_rparen_next("invalid datatype declaration, ')' expected"); } new_dt_decls.push_back(d); i++; } if (i < m_dt_names.size()) { throw parser_exception("invalid datatype declaration, too few datatype definitions compared to declared sorts"); } next(); check_rparen("invalid datatype declaration"); unsigned sz = new_dt_decls.size(); if (sz == 0) { m_ctx.print_success(); next(); return; } else if (sz == 1) { check_missing(new_dt_decls[0], line, pos); new_dt_decls[0]->commit(pm()); } else { SASSERT(sz > 1); pdatatypes_decl_ref dts(pm()); dts = pm().mk_pdatatypes_decl(m_sort_id2param_idx.size(), sz, new_dt_decls.c_ptr()); symbol missing; if (!pm().fix_missing_refs(dts, missing)) { std::string err_msg = "invalid datatype declaration, unknown sort '"; err_msg += missing.str(); err_msg += "'"; throw parser_exception(std::move(err_msg), line, pos); } dts->commit(pm()); m_ctx.insert_aux_pdecl(dts.get()); } for (unsigned i = 0; i < sz; i++) { pdatatype_decl * d = new_dt_decls[i]; symbol duplicated; check_duplicate(d, line, pos); if (!is_smt2_6) { // datatypes are inserted up front in SMT2.6 mode, so no need to re-insert them. m_ctx.insert(d); } } TRACE("declare_datatypes", tout << "i: " << i << " new_dt_decls.size(): " << sz << "\n"; for (unsigned j = 0; j < new_dt_decls.size(); ++j) tout << new_dt_decls[j]->get_name() << "\n";); m_ctx.print_success(); next(); } // ( declare-datatype symbol datatype_dec) void parse_declare_datatype() { SASSERT(curr_is_identifier()); SASSERT(curr_id() == m_declare_datatype); next(); unsigned line = m_scanner.get_line(); unsigned pos = m_scanner.get_pos(); symbol dt_name = curr_id(); next(); m_dt_name2idx.reset(); m_dt_name2idx.insert(dt_name, 0); m_sort_id2param_idx.reset(); pdatatype_decl_ref d(pm()); pconstructor_decl_ref_buffer new_ct_decls(pm()); parse_datatype_dec(&dt_name, new_ct_decls); d = pm().mk_pdatatype_decl(m_sort_id2param_idx.size(), dt_name, new_ct_decls.size(), new_ct_decls.c_ptr()); check_missing(d, line, pos); check_duplicate(d, line, pos); d->commit(pm()); check_rparen("invalid end of datatype declaration, ')' expected"); m_ctx.print_success(); next(); } // datatype_dec ::= ( constructor_dec+ ) | ( par ( symbol+ ) ( constructor_dec+ ) ) void parse_datatype_dec(symbol* dt_name, pconstructor_decl_ref_buffer & ct_decls) { check_lparen_next("invalid datatype declaration, '(' expected"); if (curr_id() == m_par) { next(); parse_sort_decl_params(); check_lparen_next("invalid constructor declaration after par, '(' expected"); unsigned sz = m_sort_id2param_idx.size(); if (sz > 0 && dt_name) { m_ctx.insert(pm().mk_psort_dt_decl(sz, *dt_name)); } parse_constructor_decls(ct_decls); check_rparen_next("invalid datatype declaration, ')' expected"); } else { if (dt_name) { m_ctx.insert(pm().mk_psort_dt_decl(0, *dt_name)); } parse_constructor_decls(ct_decls); } check_rparen_next("invalid datatype declaration, ')' expected"); } void check_missing(pdatatype_decl* d, unsigned line, unsigned pos) { symbol missing; if (d->has_missing_refs(missing)) { std::string err_msg = "invalid datatype declaration, unknown sort '"; err_msg += missing.str(); err_msg += "'"; throw parser_exception(std::move(err_msg), line, pos); } } void check_duplicate(pdatatype_decl* d, unsigned line, unsigned pos) { symbol duplicated; if (d->has_duplicate_accessors(duplicated)) { std::string err_msg = "invalid datatype declaration, repeated accessor identifier '"; err_msg += duplicated.str(); err_msg += "'"; throw parser_exception(std::move(err_msg), line, pos); } } void name_expr(expr * n, symbol const & s) { TRACE("name_expr", tout << "naming: " << s << " ->\n" << mk_pp(n, m()) << "\n";); if (!is_ground(n) && has_free_vars(n)) throw parser_exception("invalid named expression, expression contains free variables"); m_ctx.insert(s, 0, nullptr, n); m_last_named_expr.first = s; m_last_named_expr.second = n; } bool in_quant_ctx(attr_expr_frame * fr) { return fr != nullptr && fr->m_prev != nullptr && fr->m_prev->m_kind == EF_QUANT; } void check_in_quant_ctx(attr_expr_frame * fr) { if (!in_quant_ctx(fr)) throw parser_exception("invalid attribute, not in the scope of a quantifier"); } void process_last_symbol(attr_expr_frame * fr) { if (fr->m_last_symbol == symbol::null) return; if (fr->m_last_symbol == m_pattern) { expr * pat = expr_stack().back(); if (pat == nullptr) { if (!ignore_bad_patterns()) throw parser_exception("invalid empty pattern"); } else { if (!m().is_pattern(pat)) pat = m().mk_pattern(to_app(pat)); // unary pattern SASSERT(m().is_pattern(pat)); pattern_stack().push_back(pat); } expr_stack().pop_back(); } else if (fr->m_last_symbol == m_nopattern) { nopattern_stack().push_back(expr_stack().back()); expr_stack().pop_back(); } else { UNREACHABLE(); } } void store_qid(attr_expr_frame * fr, symbol const & qid) { SASSERT(in_quant_ctx(fr)); static_cast(fr->m_prev)->m_qid = qid; } void store_skid(attr_expr_frame * fr, symbol const & skid) { SASSERT(in_quant_ctx(fr)); static_cast(fr->m_prev)->m_skid = skid; } void store_weight(attr_expr_frame * fr, unsigned w) { SASSERT(in_quant_ctx(fr)); static_cast(fr->m_prev)->m_weight = w; } // parse expression state enum pe_state { PES_EXPR, // expecting PES_DECL, // expecting ( ) PES_PATTERN, PES_CONTINUE }; pe_state consume_attributes(attr_expr_frame * fr) { if (fr->m_expr_spos == expr_stack().size()) return PES_EXPR; // didn't parse the expression yet. process_last_symbol(fr); while (true) { check_keyword("invalid attributed expression, keyword expected"); symbol id = curr_id(); fr->m_last_symbol = symbol::null; TRACE("consume_attributes", tout << "id: " << id << ", expr_stack().size(): " << expr_stack().size() << "\n";); if (id == m_named) { next(); name_expr(expr_stack().back(), check_identifier_next("invalid attribute value, symbol expected")); } else if (id == m_lblpos || id == m_lblneg) { next(); check_identifier("invalid attribute value, symbol expected"); if (!m().is_bool(expr_stack().back())) throw parser_exception("invalid labeled expression, expression must have Bool sort"); expr * new_expr = m().mk_label(id == m_lblpos, curr_id(), expr_stack().back()); expr_stack().pop_back(); expr_stack().push_back(new_expr); next(); } else if (id == m_weight) { check_in_quant_ctx(fr); next(); check_int("invalid weight attribute, integer expected"); store_weight(fr, curr_unsigned()); next(); } else if (id == m_skid) { check_in_quant_ctx(fr); next(); store_skid(fr, check_identifier_next("invalid attribute value, symbol expected")); } else if (id == m_qid) { check_in_quant_ctx(fr); next(); check_identifier("invalid attribute value, symbol expected"); store_qid(fr, curr_id()); next(); } else if (id == m_pattern) { if (!ignore_user_patterns()) { check_in_quant_ctx(fr); next(); fr->m_last_symbol = id; return PES_PATTERN; } else { // just consume pattern next(); consume_sexpr(); } } else if (id == m_nopattern) { if (!ignore_user_patterns()) { check_in_quant_ctx(fr); next(); fr->m_last_symbol = id; return PES_EXPR; } else { // just consume pattern next(); consume_sexpr(); } } else { std::ostringstream str; str << "unknown attribute " << id; warning_msg("%s", str.str().c_str()); next(); // just consume the consume_sexpr(); } if (curr_is_rparen()) return PES_CONTINUE; } } pe_state parse_expr_state() { if (m_num_expr_frames == 0) return PES_EXPR; expr_frame * fr = static_cast(m_stack.top()); switch (fr->m_kind) { case EF_LET: return static_cast(fr)->m_in_decls ? PES_DECL : PES_EXPR; case EF_ATTR_EXPR: return consume_attributes(static_cast(fr)); default: return PES_EXPR; } } void parse_numeral(bool is_int) { SASSERT(!is_int || curr_is_int()); SASSERT(is_int || curr_is_float()); TRACE("parse_numeral", tout << "curr(): " << curr() << ", curr_numeral(): " << curr_numeral() << ", is_int: " << is_int << "\n";); expr_stack().push_back(autil().mk_numeral(curr_numeral(), is_int && !m_ctx.numeral_as_real())); next(); } void parse_bv_numeral() { SASSERT(curr() == scanner::BV_TOKEN); expr_stack().push_back(butil().mk_numeral(curr_numeral(), m_scanner.get_bv_size())); TRACE("parse_bv_numeral", tout << "new numeral: " << mk_pp(expr_stack().back(), m()) << "\n";); next(); } void parse_string_const() { SASSERT(curr() == scanner::STRING_TOKEN); expr_stack().push_back(sutil().str.mk_string(symbol(m_scanner.get_string()))); TRACE("smt2parser", tout << "new string: " << mk_pp(expr_stack().back(), m()) << "\n";); next(); } void push_pattern_frame() { // TODO: It seems the only reliable way to parse patterns is: // Parse as an S-Expr, then try to convert it to an useful pattern. // If it is not possible, then discard pattern. // After this modification, the (PROMOTE) hack below can be removed. if (curr_is_lparen()) { next(); } else { if (!ignore_bad_patterns()) throw parser_exception("invalid pattern, '(' expected"); consume_sexpr(); expr_stack().push_back(nullptr); // empty pattern return; } if (curr_is_lparen()) { // multi-pattern void * mem = m_stack.allocate(sizeof(pattern_frame)); new (mem) pattern_frame(expr_stack().size()); m_num_expr_frames++; } else if (curr_is_rparen()) { next(); expr_stack().push_back(nullptr); // empty pattern } else { // unary pattern // HACK: to consume & discard (PROMOTE)-like patterns that were incorrectly introduced in SMT-LIB 2.0 // when Simplify benchmarks were converted into SMT2 ones. if (curr_is_identifier()) { symbol id = curr_id(); func_decl * f = nullptr; try { f = m_ctx.find_func_decl(id); } catch (cmd_exception &) { } if (f && f->get_arity() == 0) { if (!ignore_bad_patterns()) throw parser_exception("invalid constant pattern"); while (!curr_is_rparen()) consume_sexpr(); next(); expr_stack().push_back(nullptr); // empty pattern return; // no frame is created } } if (!curr_is_lparen() && !curr_is_identifier()) throw parser_exception("invalid pattern, '(' or identifier expected"); push_app_frame(); } } void push_let_decl_frame() { check_lparen_next("invalid let declaration, '(' expected"); check_identifier("invalid let declaration, symbol expected"); symbol_stack().push_back(curr_id()); next(); void * mem = m_stack.allocate(sizeof(let_decl_frame)); new (mem) let_decl_frame(); m_num_expr_frames++; } unsigned parse_sorted_vars() { unsigned num = 0; unsigned sym_spos = symbol_stack().size(); unsigned sort_spos = sort_stack().size(); TRACE("parse_sorted_vars", tout << "[before] symbol_stack().size(): " << symbol_stack().size() << "\n";); check_lparen_next("invalid list of sorted variables, '(' expected"); m_env.begin_scope(); while (!curr_is_rparen()) { check_lparen_next("invalid sorted variable, '(' expected"); check_identifier("invalid sorted variable, symbol expected"); symbol_stack().push_back(curr_id()); TRACE("parse_sorted_vars", tout << "push_back curr_id(): " << curr_id() << "\n";); next(); parse_sort("invalid sorted variables"); check_rparen_next("invalid sorted variable, ')' expected"); num++; } next(); TRACE("parse_sorted_vars", tout << "[after] symbol_stack().size(): " << symbol_stack().size() << "\n";); symbol const * sym_it = symbol_stack().c_ptr() + sym_spos; sort * const * sort_it = sort_stack().c_ptr() + sort_spos; m_num_bindings += num; unsigned i = num; while (i > 0) { --i; var * v = m().mk_var(i, *sort_it); expr_stack().push_back(v); // prevent v from being deleted TRACE("parse_sorted_vars", tout << "registering " << *sym_it << " -> " << mk_pp(v, m()) << ", num: " << num << ", i: " << i << "\n";); m_env.insert(*sym_it, local(v, m_num_bindings)); SASSERT(m_env.contains(*sym_it)); ++sort_it; ++sym_it; } return num; } void push_let_frame() { next(); check_lparen_next("invalid let declaration, '(' expected"); void * mem = m_stack.allocate(sizeof(let_frame)); new (mem) let_frame(symbol_stack().size(), expr_stack().size()); m_num_expr_frames++; } void push_bang_frame(expr_frame * curr) { TRACE("consume_attributes", tout << "begin bang, expr_stack.size(): " << expr_stack().size() << "\n";); next(); void * mem = m_stack.allocate(sizeof(attr_expr_frame)); new (mem) attr_expr_frame(curr, symbol_stack().size(), expr_stack().size()); m_num_expr_frames++; } void push_quant_frame(quantifier_kind k) { SASSERT(curr_is_identifier()); SASSERT(curr_id_is_forall() || curr_id_is_exists() || curr_id_is_lambda()); SASSERT((k == forall_k) == curr_id_is_forall()); SASSERT((k == exists_k) == curr_id_is_exists()); SASSERT((k == lambda_k) == curr_id_is_lambda()); next(); void * mem = m_stack.allocate(sizeof(quant_frame)); new (mem) quant_frame(k, pattern_stack().size(), nopattern_stack().size(), symbol_stack().size(), sort_stack().size(), expr_stack().size()); m_num_expr_frames++; unsigned num_vars = parse_sorted_vars(); if (num_vars == 0) throw parser_exception("invalid quantifier, list of sorted variables is empty"); } /** * SMT-LIB 2.6 pattern matches are of the form * * (match t ((p1 t1) ... (pm+1 tm+1))) * * precursor form is * * (match t (case p1 t1) (case p2 t2) ... ) * */ void push_match_frame() { SASSERT(curr_is_identifier()); SASSERT(curr_id() == m_match); next(); void * mem = m_stack.allocate(sizeof(match_frame)); new (mem) match_frame(); unsigned num_frames = m_num_expr_frames; parse_expr(); expr_ref t(expr_stack().back(), m()); expr_stack().pop_back(); expr_ref_vector patterns(m()), cases(m()); sort* srt = m().get_sort(t); check_lparen_next("pattern bindings should be enclosed in a parenthesis"); if (curr_id_is_case()) { while (curr_id_is_case()) { next(); m_env.begin_scope(); unsigned num_bindings = m_num_bindings; parse_match_pattern(srt); patterns.push_back(expr_stack().back()); expr_stack().pop_back(); parse_expr(); cases.push_back(expr_stack().back()); expr_stack().pop_back(); m_num_bindings = num_bindings; m_env.end_scope(); check_rparen_next("invalid pattern binding, ')' expected"); if (curr_is_lparen()) { next(); } } } 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(); parse_expr(); cases.push_back(expr_stack().back()); expr_stack().pop_back(); m_num_bindings = num_bindings; m_env.end_scope(); check_rparen_next("invalid pattern binding, ')' expected"); } next(); } m_num_expr_frames = num_frames + 1; expr_stack().push_back(compile_patterns(t, patterns, cases)); } void pop_match_frame(match_frame* fr) { m_stack.deallocate(fr); m_num_expr_frames--; } expr_ref compile_patterns(expr* t, expr_ref_vector const& patterns, expr_ref_vector const& cases) { expr_ref result(m()); var_subst sub(m(), false); TRACE("parse_expr", tout << "term\n" << expr_ref(t, m()) << "\npatterns\n" << patterns << "\ncases\n" << cases << "\n";); check_patterns(patterns, m().get_sort(t)); for (unsigned i = patterns.size(); i > 0; ) { --i; expr_ref_vector subst(m()); expr_ref cond = bind_match(t, patterns[i], subst); expr_ref new_case(m()); if (subst.empty()) { new_case = cases[i]; } else { new_case = sub(cases[i], subst.size(), subst.c_ptr()); inv_var_shifter inv(m()); inv(new_case, subst.size(), new_case); } if (result) { result = m().mk_ite(cond, new_case, result); } else { // pattern match binding is ignored. result = new_case; } } TRACE("parse_expr", tout << result << "\n";); return result; } void check_patterns(expr_ref_vector const& patterns, sort* s) { if (!dtutil().is_datatype(s)) throw parser_exception("pattern matching is only supported for algebraic datatypes"); ptr_vector const& cons = *dtutil().get_datatype_constructors(s); for (expr * arg : patterns) if (is_var(arg)) return; if (patterns.size() < cons.size()) throw parser_exception("non-exhaustive pattern match"); ast_fast_mark1 marked; for (expr * arg : patterns) marked.mark(to_app(arg)->get_decl(), true); for (func_decl * f : cons) if (!marked.is_marked(f)) throw parser_exception("a constructor is missing from pattern match"); } // compute match condition and substitution // t is shifted by size of subst. expr_ref bind_match(expr* t, expr* pattern, expr_ref_vector& subst) { if (m().get_sort(t) != m().get_sort(pattern)) { std::ostringstream str; str << "sorts of pattern " << expr_ref(pattern, m()) << " and term " << expr_ref(t, m()) << " are not aligned"; throw parser_exception(str.str()); } expr_ref tsh(m()); if (is_var(pattern)) { shifter()(t, 1, tsh); subst.push_back(tsh); return expr_ref(m().mk_true(), m()); } else { SASSERT(is_app(pattern)); func_decl * f = to_app(pattern)->get_decl(); func_decl * r = dtutil().get_constructor_is(f); ptr_vector const * acc = dtutil().get_constructor_accessors(f); shifter()(t, acc->size(), tsh); for (func_decl* a : *acc) { subst.push_back(m().mk_app(a, tsh)); } return expr_ref(m().mk_app(r, t), m()); } } /** * parse a match pattern * (C x1 .... xn) * C * _ * x */ void parse_match_pattern(sort * srt) { symbol C; svector vars; expr_ref_vector args(m()); 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 // look up constructor C, // create bound variables based on constructor type. // store expression in expr_stack(). // ensure that bound variables are adjusted to vars 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"); } if (f->get_arity() != vars.size()) { throw parser_exception("mismatching number of variables supplied to constructor"); } m_num_bindings += vars.size(); for (unsigned i = 0; i < vars.size(); ++i) { var * v = m().mk_var(i, f->get_domain(i)); args.push_back(v); if (vars[i] != m_underscore) { m_env.insert(vars[i], local(v, m_num_bindings)); } } expr_stack().push_back(m().mk_app(f, args.size(), args.c_ptr())); } symbol parse_indexed_identifier_core() { check_underscore_next("invalid indexed identifier, '_' expected"); check_identifier("invalid indexed identifier, symbol expected"); symbol r = curr_id(); next(); while (!curr_is_rparen()) { if (curr_is_int()) { if (!curr_numeral().is_unsigned()) { m_param_stack.push_back(parameter(curr_numeral())); } else { m_param_stack.push_back(parameter(curr_unsigned())); } next(); } else if (curr_is_float()) { m_param_stack.push_back(parameter(curr_numeral())); next(); } else if (curr_is_keyword()) { m_param_stack.push_back(parameter(curr_id())); next(); } else if (curr_is_identifier() || curr_is_lparen()) { m_param_stack.push_back(parameter(parse_func_decl_ref())); } else { throw parser_exception("invalid indexed identifier, integer, identifier or '(' expected"); } } next(); return r; } symbol parse_indexed_identifier() { if (curr_is_identifier()) { symbol r = curr_id(); next(); return r; } check_lparen_next("invalid (indexed) identifier, '(_' or symbol expected"); return parse_indexed_identifier_core(); } // parse: // 'as' ')' // '_' + ')' // 'as' '(' '_' (|)+ ')' ')' symbol parse_qualified_identifier_core(bool & has_as) { SASSERT(curr_is_identifier()); SASSERT(curr_id_is_underscore() || curr_id_is_as()); if (curr_id_is_underscore()) { has_as = false; return parse_indexed_identifier_core(); } else { SASSERT(curr_id_is_as()); has_as = true; next(); symbol r = parse_indexed_identifier(); parse_sort("Invalid qualified identifier"); check_rparen_next("invalid qualified identifier, ')' expected"); return r; } } // parse: // // '(' 'as' ')' // '(' '_' + ')' // '(' 'as' (|)+ ')' ')' symbol parse_qualified_identifier(bool & has_as) { SASSERT(curr_is_lparen() || curr_is_identifier()); if (curr_is_identifier()) { has_as = false; symbol r = curr_id(); next(); return r; } SASSERT(curr_is_lparen()); next(); if (!curr_is_identifier() || (!curr_id_is_underscore() && !curr_id_is_as())) throw parser_exception("invalid qualified/indexed identifier, '_' or 'as' expected"); return parse_qualified_identifier_core(has_as); } void unknown_var_const_name(symbol id) { std::string msg = "unknown constant/variable '"; msg += id.str() + "'"; throw parser_exception(std::move(msg)); } rational m_last_bv_numeral; // for bv, bvbin, bvhex // return true if *s == [0-9]+ bool is_bv_decimal(char const * s) { TRACE("is_bv_num", tout << "is_bv_decimal: " << s << "\n";); SASSERT('0' <= *s && *s <= '9'); rational & n = m_last_bv_numeral; n = rational(*s - '0'); ++s; while ('0' <= *s && *s <= '9') { n *= rational(10); n += rational(*s - '0'); ++s; } if (*s != 0) return false; return true; } // return true if *s == bin[0-1]+ bool is_bv_binary(char const * s) { SASSERT(*s == 'b'); ++s; if (*s != 'i') return false; ++s; if (*s != 'n') return false; ++s; rational & n = m_last_bv_numeral; unsigned i = 0; n = rational(0); while (*s == '0' || *s == '1') { n *= rational(2); n += rational(*s - '0'); ++s; ++i; } if (*s != 0 || i == 0) return false; return true; } // return true if *s == hex[0-9,a-f,A-F]+ bool is_bv_hex(char const * s) { SASSERT(*s == 'h'); ++s; if (*s != 'e') return false; ++s; if (*s != 'x') return false; ++s; rational & n = m_last_bv_numeral; unsigned i = 0; n = rational(0); while (true) { if ('0' <= *s && *s <= '9') { n *= rational(16); n += rational(*s - '0'); } else if ('a' <= *s && *s <= 'f') { n *= rational(16); n += rational(10 + (*s - 'a')); } else if ('A' <= *s && *s <= 'F') { n *= rational(16); n += rational(10 + (*s - 'A')); } else if (*s == 0) { return i > 0; } else { return false; } ++s; ++i; } } // Return true if // n == bv[0-9]+ OR // n == bvhex[0-9,a-f,A-F]+ OR // n == bvbin[0-1]+ // It store the bit-vector value in m_last_bv_numeral bool is_bv_num(symbol const & n) { char const * s = n.bare_str(); if (*s != 'b') return false; s++; if (*s != 'v') return false; s++; if ('0' <= *s && *s <= '9') return is_bv_decimal(s); else if (*s == 'b') return is_bv_binary(s); else if (*s == 'h') return is_bv_hex(s); else return false; } void push_local(local const & l) { if (is_ground(l.m_term) || l.m_level == m_num_bindings) { expr_stack().push_back(l.m_term); } else { SASSERT(l.m_level <= m_num_bindings); expr_ref new_term(m()); shifter()(l.m_term, m_num_bindings - l.m_level, new_term); expr_stack().push_back(new_term); } } // parse as expression void parse_expr_name() { SASSERT(curr_is_identifier()); symbol n = curr_id(); local l; if (m_env.find(n, l)) { push_local(l); } else { expr_ref t_ref(m()); m_ctx.mk_const(n, t_ref); expr_stack().push_back(t_ref.get()); } next(); } // if has_as == true, then the sort of t must be equal to sort_stack().pop_back() // if that is the case, pop the top of sort_stack() void check_qualifier(expr * t, bool has_as) { if (has_as) { sort * s = sort_stack().back(); if (s != m().get_sort(t)) throw parser_exception("invalid qualified identifier, sort mismatch"); sort_stack().pop_back(); } } // parse // 'as' ')' // '_' + ')' // 'as' '(' (|)+ ')' ')' void parse_qualified_name() { SASSERT(curr_is_identifier()); SASSERT(curr_id_is_as() || curr_id_is_underscore()); TRACE("parse_qualified_name", tout << "parse_qualified_name() curr_id: " << curr_id() << "\n";); unsigned param_spos = m_param_stack.size(); bool has_as; symbol r = parse_qualified_identifier_core(has_as); TRACE("parse_qualified_name", tout << "parse_qualified_name() r: " << r << "\n";); expr * t; local l; if (m_env.find(r, l)) { push_local(l); t = expr_stack().back(); check_qualifier(t, has_as); if (m_param_stack.size() != param_spos) throw parser_exception("invalid indexed identifier, symbol is a local declaration"); return; } unsigned num_indices = m_param_stack.size() - param_spos; if (is_bv_num(r)) { if (num_indices != 1 || !m_param_stack.back().is_int()) throw parser_exception("invalid bit-vector constant, index expected"); unsigned bv_size = m_param_stack.back().get_int(); m_param_stack.pop_back(); t = butil().mk_numeral(m_last_bv_numeral, bv_size); expr_stack().push_back(t); check_qualifier(t, has_as); return; } expr_ref t_ref(m()); m_ctx.mk_app(r, 0, nullptr, num_indices, m_param_stack.c_ptr() + param_spos, has_as ? sort_stack().back() : nullptr, t_ref); m_param_stack.shrink(param_spos); expr_stack().push_back(t_ref.get()); if (has_as) { check_qualifier(t_ref.get(), has_as); } } void parse_root_obj() { SASSERT(curr_is_identifier()); SASSERT(curr_id_is_root_obj()); next(); parse_sexpr(); sexpr * p = sexpr_stack().back(); check_int("invalid root-obj, (unsigned) integer expected"); rational idx = curr_numeral(); if (!idx.is_unsigned()) throw parser_exception("invalid root-obj, index must fit in an unsigned machine integer"); unsigned u_idx = idx.get_unsigned(); if (u_idx == 0) throw parser_exception("invalid root-obj, index must be >= 1"); next(); check_rparen_next("invalid root-obj, ')' expected"); expr_stack().push_back(autil().mk_numeral(p, u_idx)); sexpr_stack().pop_back(); } void push_app_frame() { SASSERT(curr_is_lparen() || curr_is_identifier()); unsigned param_spos = m_param_stack.size(); unsigned expr_spos = expr_stack().size(); bool has_as; symbol f = parse_qualified_identifier(has_as); void * mem = m_stack.allocate(sizeof(quant_frame)); new (mem) app_frame(f, expr_spos, param_spos, has_as); m_num_expr_frames++; } void push_expr_frame(expr_frame * curr) { SASSERT(curr_is_lparen()); next(); TRACE("push_expr_frame", tout << "push_expr_frame(), curr(): " << m_curr << "\n";); if (curr_is_identifier()) { TRACE("push_expr_frame", tout << "push_expr_frame(), curr_id(): " << curr_id() << "\n";); if (curr_id_is_let()) { push_let_frame(); } else if (curr_id_is_forall()) { push_quant_frame(forall_k); } else if (curr_id_is_exists()) { push_quant_frame(exists_k); } else if (curr_id_is_lambda()) { push_quant_frame(lambda_k); } else if (curr_id_is_bang()) { push_bang_frame(curr); } else if (curr_id_is_as() || curr_id_is_underscore()) { parse_qualified_name(); } else if (curr_id_is_root_obj()) { parse_root_obj(); } else if (curr_id_is_match()) { push_match_frame(); } else { push_app_frame(); } } else if (curr_is_lparen()) { push_app_frame(); } else { throw parser_exception("invalid expression, '(' or symbol expected"); } } void pop_app_frame(app_frame * fr) { SASSERT(expr_stack().size() >= fr->m_expr_spos); SASSERT(m_param_stack.size() >= fr->m_param_spos); if (expr_stack().size() == fr->m_expr_spos) throw parser_exception("invalid function application, arguments missing"); unsigned num_args = expr_stack().size() - fr->m_expr_spos; unsigned num_indices = m_param_stack.size() - fr->m_param_spos; expr_ref t_ref(m()); local l; if (m_env.find(fr->m_f, l)) { push_local(l); t_ref = expr_stack().back(); for (unsigned i = 0; i < num_args; ++i) { expr* arg = expr_stack().get(fr->m_expr_spos + i); expr* args[2] = { t_ref.get(), arg }; m_ctx.mk_app(symbol("select"), 2, args, 0, nullptr, nullptr, t_ref); } } else { m_ctx.mk_app(fr->m_f, num_args, expr_stack().c_ptr() + fr->m_expr_spos, num_indices, m_param_stack.c_ptr() + fr->m_param_spos, fr->m_as_sort ? sort_stack().back() : nullptr, t_ref); } expr_stack().shrink(fr->m_expr_spos); m_param_stack.shrink(fr->m_param_spos); if (fr->m_as_sort) sort_stack().pop_back(); TRACE("pop_app_frame", tout << "new term: " << mk_pp(t_ref, m()) << "\n";); expr_stack().push_back(t_ref.get()); m_stack.deallocate(fr); m_num_expr_frames--; } void pop_let_frame(let_frame * fr) { if (fr->m_in_decls) { m_env.begin_scope(); fr->m_in_decls = false; SASSERT(symbol_stack().size() >= fr->m_sym_spos); SASSERT(expr_stack().size() >= fr->m_expr_spos); if (symbol_stack().size() - fr->m_sym_spos != expr_stack().size() - fr->m_expr_spos) { throw parser_exception("malformed let expression"); } unsigned num_decls = expr_stack().size() - fr->m_expr_spos; symbol * sym_it = symbol_stack().c_ptr() + fr->m_sym_spos; expr ** expr_it = expr_stack().c_ptr() + fr->m_expr_spos; expr ** expr_end = expr_it + num_decls; for (; expr_it != expr_end; ++expr_it, ++sym_it) { TRACE("let_frame", tout << "declaring: " << *sym_it << " " << mk_pp(*expr_it, m()) << "\n";); m_env.insert(*sym_it, local(*expr_it, m_num_bindings)); } } else { // the resultant expression is on the top of the stack TRACE("let_frame", tout << "let result expr: " << mk_pp(expr_stack().back(), m()) << "\n";); expr_ref r(m()); if (expr_stack().size() < fr->m_expr_spos + 1) throw parser_exception("invalid let expression"); r = expr_stack().back(); expr_stack().pop_back(); // remove local declarations from the stack symbol_stack().shrink(fr->m_sym_spos); expr_stack().shrink(fr->m_expr_spos); m_env.end_scope(); // put result back on the stack expr_stack().push_back(r.get()); m_stack.deallocate(fr); m_num_expr_frames--; } } void pop_quant_frame(quant_frame * fr) { SASSERT(pattern_stack().size() >= fr->m_pat_spos); SASSERT(nopattern_stack().size() >= fr->m_nopat_spos); SASSERT(symbol_stack().size() >= fr->m_sym_spos); SASSERT(sort_stack().size() >= fr->m_sort_spos); SASSERT(symbol_stack().size() - fr->m_sym_spos == sort_stack().size() - fr->m_sort_spos); SASSERT(expr_stack().size() >= fr->m_expr_spos); unsigned num_decls = sort_stack().size() - fr->m_sort_spos; if (expr_stack().size() - fr->m_expr_spos != num_decls /* variables */ + 1 /* result */) //throw parser_exception("invalid quantified expression, syntax error: (forall|exists|lambda (( )*) ) expected"); throw parser_exception("invalid quantified expression, syntax error: (forall|exists (( )*) ) expected"); unsigned begin_pats = fr->m_pat_spos; unsigned end_pats = pattern_stack().size(); unsigned j = begin_pats; for (unsigned i = begin_pats; i < end_pats; i++) { expr * pat = pattern_stack().get(i); if (!pat_validator()(num_decls, pat, m_scanner.get_line(), m_scanner.get_pos())) { if (!ignore_bad_patterns()) throw parser_exception("invalid pattern"); continue; } pattern_stack().set(j, pat); j++; } end_pats = j; pattern_stack().shrink(end_pats); unsigned num_pats = end_pats - begin_pats; unsigned num_nopats = nopattern_stack().size() - fr->m_nopat_spos; TRACE("parse_quantifier", tout << "weight: " << fr->m_weight << "\n";); TRACE("skid", tout << "fr->m_skid: " << fr->m_skid << "\n";); TRACE("parse_quantifier", tout << "body:\n" << mk_pp(expr_stack().back(), m()) << "\n";); if (fr->m_qid == symbol::null) fr->m_qid = symbol(m_scanner.get_line()); if (fr->m_kind != lambda_k && !m().is_bool(expr_stack().back())) throw parser_exception("quantifier body must be a Boolean expression"); quantifier* new_q = m().mk_quantifier(fr->m_kind, num_decls, sort_stack().c_ptr() + fr->m_sort_spos, symbol_stack().c_ptr() + fr->m_sym_spos, expr_stack().back(), fr->m_weight, fr->m_qid, fr->m_skid, num_pats, pattern_stack().c_ptr() + fr->m_pat_spos, num_nopats, nopattern_stack().c_ptr() + fr->m_nopat_spos ); TRACE("mk_quantifier", tout << "id: " << new_q->get_id() << "\n" << mk_ismt2_pp(new_q, m()) << "\n";); TRACE("skid", tout << "new_q->skid: " << new_q->get_skid() << "\n";); expr_stack().shrink(fr->m_expr_spos); pattern_stack().shrink(fr->m_pat_spos); nopattern_stack().shrink(fr->m_nopat_spos); symbol_stack().shrink(fr->m_sym_spos); sort_stack().shrink(fr->m_sort_spos); m_env.end_scope(); SASSERT(num_decls <= m_num_bindings); m_num_bindings -= num_decls; expr_stack().push_back(new_q); m_stack.deallocate(fr); m_num_expr_frames--; } void pop_attr_expr_frame(attr_expr_frame * fr) { process_last_symbol(fr); TRACE("consume_attributes", tout << "pop_attr_expr_frame, expr_stack.size(): " << expr_stack().size() << "\n";); // the resultant expression is already on the top of the stack. SASSERT(expr_stack().size() == fr->m_expr_spos + 1); m_stack.deallocate(fr); m_num_expr_frames--; } void pop_pattern_frame(pattern_frame * fr) { SASSERT(expr_stack().size() >= fr->m_expr_spos); if (expr_stack().size() == fr->m_expr_spos) { if (!ignore_bad_patterns()) throw parser_exception("invalid empty pattern"); // ignoring empty pattern expr_stack().shrink(fr->m_expr_spos); } else { unsigned num = expr_stack().size() - fr->m_expr_spos; expr * new_pat = m().mk_pattern(num, reinterpret_cast(expr_stack().c_ptr() + fr->m_expr_spos)); expr_stack().shrink(fr->m_expr_spos); expr_stack().push_back(new_pat); } m_stack.deallocate(fr); m_num_expr_frames--; } void pop_expr_frame() { SASSERT(curr_is_rparen()); expr_frame * fr = static_cast(m_stack.top()); switch (fr->m_kind) { case EF_APP: pop_app_frame(static_cast(fr)); break; case EF_LET: pop_let_frame(static_cast(fr)); break; case EF_LET_DECL: m_stack.deallocate(static_cast(fr)); m_num_expr_frames--; break; case EF_MATCH: pop_match_frame(static_cast(fr)); break; case EF_QUANT: pop_quant_frame(static_cast(fr)); break; case EF_ATTR_EXPR: pop_attr_expr_frame(static_cast(fr)); break; case EF_PATTERN: pop_pattern_frame(static_cast(fr)); break; default: UNREACHABLE(); } SASSERT(curr_is_rparen()); next(); // consume ')' } void parse_expr() { m_num_expr_frames = 0; do { TRACE("parse_expr", tout << "curr(): " << curr() << ", m_num_expr_frames: " << m_num_expr_frames << ", expr_stack().size(): " << expr_stack().size() << "\n";); if (curr_is_rparen()) { if (m_num_expr_frames == 0) throw parser_exception("invalid expression, unexpected ')'"); pop_expr_frame(); } else { pe_state st = parse_expr_state(); TRACE("consume_attributes", tout << "parse_expr_state: " << st << ", expr_stack.size(): " << expr_stack().size() << "\n";); switch (st) { case PES_EXPR: switch (curr()) { case scanner::SYMBOL_TOKEN: parse_expr_name(); break; case scanner::INT_TOKEN: parse_numeral(true); break; case scanner::FLOAT_TOKEN: parse_numeral(false); break; case scanner::BV_TOKEN: parse_bv_numeral(); break; case scanner::LEFT_PAREN: push_expr_frame(m_num_expr_frames == 0 ? nullptr : static_cast(m_stack.top())); break; case scanner::KEYWORD_TOKEN: throw parser_exception("invalid expression, unexpected keyword"); case scanner::STRING_TOKEN: parse_string_const(); break; default: throw parser_exception("invalid expression, unexpected input"); } break; case PES_DECL: push_let_decl_frame(); break; case PES_PATTERN: push_pattern_frame(); break; case PES_CONTINUE: // do nothing break; default: UNREACHABLE(); break; } } } while (m_num_expr_frames > 0 ); SASSERT(!expr_stack().empty()); } unsigned parse_exprs() { unsigned sz = 0; check_lparen_next("invalid list of terms, '(' expected"); while (!curr_is_rparen()) { parse_expr(); sz++; } next(); return sz; } void parse_sort_decl_params() { m_sort_id2param_idx.reset(); check_lparen_next("invalid sort declaration, parameters missing"); unsigned i = 0; while (!curr_is_rparen()) { check_nonreserved_identifier("invalid sort parameter, symbol or ')' expected"); m_sort_id2param_idx.insert(curr_id(), i); i++; next(); } next(); } bool parse_sort_decl_or_params() { m_sort_id2param_idx.reset(); m_dt_name2arity.reset(); m_dt_name2idx.reset(); m_dt_names.reset(); check_lparen_next("invalid sort declaration, parameters missing"); unsigned i = 0; bool first = true; bool is_decl = false; while (!curr_is_rparen()) { if (first) { is_decl = curr_is_lparen(); first = false; } if (is_decl) { check_lparen_next("invalid sort declaration, '(' expected"); symbol dt_name = check_identifier_next("invalid sort name, identified expected"); check_int("invalid sort declaration, arity expected"); unsigned u = curr_unsigned(); next(); m_dt_name2idx.insert(dt_name, i); m_dt_name2arity.insert(dt_name, u); m_dt_names.push_back(dt_name); psort_decl * decl = pm().mk_psort_dt_decl(u, dt_name); m_ctx.insert(decl); check_rparen("invalid sort declaration, ')' expected"); } else { check_identifier("invalid sort parameter, symbol or ')' expected"); m_sort_id2param_idx.insert(curr_id(), i); } i++; next(); } next(); return is_decl; } void parse_declare_sort() { SASSERT(curr_is_identifier()); SASSERT(curr_id() == m_declare_sort); next(); check_nonreserved_identifier("invalid sort declaration, symbol expected"); symbol id = curr_id(); if (m_ctx.find_psort_decl(id) != nullptr) throw parser_exception("invalid sort declaration, sort already declared/defined"); next(); if (curr_is_rparen()) { psort_decl * decl = pm().mk_psort_user_decl(0, id, nullptr); m_ctx.insert(decl); } else { check_int("invalid sort declaration, arity () or ')' expected"); unsigned u = curr_unsigned(); psort_decl * decl = pm().mk_psort_user_decl(u, id, nullptr); m_ctx.insert(decl); next(); check_rparen("invalid sort declaration, ')' expected"); } m_ctx.print_success(); next(); } void parse_define_sort() { SASSERT(curr_is_identifier()); SASSERT(curr_id() == m_define_sort); next(); check_nonreserved_identifier("invalid sort definition, symbol expected"); symbol id = curr_id(); if (m_ctx.find_psort_decl(id) != nullptr) throw parser_exception("invalid sort definition, sort already declared/defined"); next(); parse_sort_decl_params(); parse_psort(); psort_decl * decl = pm().mk_psort_user_decl(m_sort_id2param_idx.size(), id, psort_stack().back()); psort_stack().pop_back(); m_ctx.insert(decl); check_rparen("invalid sort definition, ')' expected"); m_ctx.print_success(); next(); } void parse_define(bool is_fun) { SASSERT(curr_is_identifier()); SASSERT(curr_id() == (is_fun ? m_define_fun : m_model_add)); SASSERT(m_num_bindings == 0); next(); check_nonreserved_identifier("invalid function/constant definition, symbol expected"); symbol id = curr_id(); next(); unsigned sym_spos = symbol_stack().size(); unsigned sort_spos = sort_stack().size(); unsigned expr_spos = expr_stack().size(); unsigned num_vars = parse_sorted_vars(); parse_sort("Invalid function definition"); parse_expr(); if (m().get_sort(expr_stack().back()) != sort_stack().back()) throw parser_exception("invalid function/constant definition, sort mismatch"); sort* const* sorts = sort_stack().c_ptr() + sort_spos; expr* t = expr_stack().back(); if (is_fun) { m_ctx.insert(id, num_vars, sorts, t); } else { m_ctx.model_add(id, num_vars, sorts, t); } check_rparen("invalid function/constant definition, ')' expected"); // restore stacks & env symbol_stack().shrink(sym_spos); sort_stack().shrink(sort_spos); expr_stack().shrink(expr_spos); m_env.end_scope(); SASSERT(num_vars == m_num_bindings); m_num_bindings = 0; m_ctx.print_success(); next(); } void parse_define_fun() { parse_define(true); } void parse_model_add() { parse_define(false); } void parse_model_del() { next(); symbol id = curr_id(); func_decl * f = m_ctx.find_func_decl(id); m_ctx.model_del(f); next(); check_rparen_next("invalid model-del, ')' expected"); m_ctx.print_success(); } void parse_define_fun_rec() { // ( define-fun-rec hfun_defi ) SASSERT(curr_is_identifier()); SASSERT(curr_id() == m_define_fun_rec); SASSERT(m_num_bindings == 0); next(); expr_ref_vector binding(m()); svector ids; func_decl_ref f(m()); parse_rec_fun_decl(f, binding, ids); m_ctx.insert(f); parse_rec_fun_body(f, binding, ids); check_rparen("invalid function/constant definition, ')' expected"); m_ctx.print_success(); next(); } void parse_define_funs_rec() { // ( define-funs-rec ( hfun_decin+1 ) ( htermin+1 ) ) SASSERT(curr_is_identifier()); SASSERT(curr_id() == m_define_funs_rec); SASSERT(m_num_bindings == 0); next(); func_decl_ref_vector decls(m()); vector bindings; vector > ids; parse_rec_fun_decls(decls, bindings, ids); for (func_decl* d : decls) { m_ctx.insert(d); } parse_rec_fun_bodies(decls, bindings, ids); check_rparen("invalid function/constant definition, ')' expected"); m_ctx.print_success(); next(); } void parse_rec_fun_decls(func_decl_ref_vector& decls, vector& bindings, vector >& ids) { check_lparen("invalid recursive function definition, '(' expected"); next(); while (!curr_is_rparen()) { expr_ref_vector binding(m()); svector id; func_decl_ref f(m()); check_lparen("invalid recursive function definition, '(' expected"); next(); parse_rec_fun_decl(f, binding, id); decls.push_back(f); bindings.push_back(binding); ids.push_back(id); check_rparen("invalid recursive function definition, ')' expected"); next(); } next(); } recfun::promise_def parse_rec_fun_decl(func_decl_ref& f, expr_ref_vector& bindings, svector& ids) { SASSERT(m_num_bindings == 0); check_identifier("invalid function/constant definition, symbol expected"); symbol id = curr_id(); next(); unsigned sym_spos = symbol_stack().size(); unsigned sort_spos = sort_stack().size(); unsigned expr_spos = expr_stack().size(); unsigned num_vars = parse_sorted_vars(); SASSERT(num_vars == m_num_bindings); parse_sort("Invalid recursive function definition"); recfun::promise_def pdef = m_ctx.decl_rec_fun(id, num_vars, sort_stack().c_ptr() + sort_spos, sort_stack().back()); f = pdef.get_def()->get_decl(); bindings.append(num_vars, expr_stack().c_ptr() + expr_spos); ids.append(num_vars, symbol_stack().c_ptr() + sym_spos); symbol_stack().shrink(sym_spos); sort_stack().shrink(sort_spos); expr_stack().shrink(expr_spos); m_env.end_scope(); m_num_bindings = 0; return pdef; } void parse_rec_fun_bodies(func_decl_ref_vector const& decls, vector const& bindings, vector >const & ids) { unsigned i = 0; check_lparen("invalid recursive function definition, '(' expected"); next(); while (!curr_is_rparen() && i < decls.size()) { parse_rec_fun_body(decls[i], bindings[i], ids[i]); ++i; } if (i != decls.size()) { throw parser_exception("the number of declarations does not match number of supplied definitions"); } check_rparen("invalid recursive function definition, ')' expected"); next(); } void parse_rec_fun_body(func_decl* f, expr_ref_vector const& bindings, svector const& ids) { SASSERT(m_num_bindings == 0); expr_ref body(m()); unsigned sym_spos = symbol_stack().size(); unsigned num_vars = bindings.size(); m_env.begin_scope(); m_symbol_stack.append(ids.size(), ids.c_ptr()); m_num_bindings = num_vars; for (unsigned i = 0; i < num_vars; ++i) { m_env.insert(ids[i], local(bindings[i], num_vars)); } parse_expr(); body = expr_stack().back(); expr_stack().pop_back(); symbol_stack().shrink(sym_spos); m_env.end_scope(); m_num_bindings = 0; if (m().get_sort(body) != f->get_range()) { std::ostringstream buffer; buffer << "invalid function definition, sort mismatch. Expcected " << mk_pp(f->get_range(), m()) << " but function body has sort " << mk_pp(m().get_sort(body), m()); throw parser_exception(buffer.str()); } m_ctx.insert_rec_fun(f, bindings, ids, body); } void parse_define_const() { SASSERT(curr_is_identifier()); SASSERT(curr_id() == m_define_const); SASSERT(m_num_bindings == 0); next(); check_identifier("invalid constant definition, symbol expected"); symbol id = curr_id(); next(); parse_sort("Invalid constant definition"); parse_expr(); if (m().get_sort(expr_stack().back()) != sort_stack().back()) throw parser_exception("invalid constant definition, sort mismatch"); m_ctx.insert(id, 0, nullptr, expr_stack().back()); check_rparen("invalid constant definition, ')' expected"); expr_stack().pop_back(); sort_stack().pop_back(); m_ctx.print_success(); next(); } void parse_declare_fun() { SASSERT(curr_is_identifier()); SASSERT(curr_id() == m_declare_fun); next(); check_nonreserved_identifier("invalid function declaration, symbol expected"); symbol id = curr_id(); next(); unsigned spos = sort_stack().size(); unsigned num_params = parse_sorts("Parsing function declaration. Expecting sort list '('"); parse_sort("Invalid function declaration"); func_decl_ref f(m()); f = m().mk_func_decl(id, num_params, sort_stack().c_ptr() + spos, sort_stack().back()); sort_stack().shrink(spos); m_ctx.insert(f); check_rparen("invalid function declaration, ')' expected"); m_ctx.print_success(); next(); } void parse_declare_const() { SASSERT(curr_is_identifier()); SASSERT(curr_id() == m_declare_const); next(); check_nonreserved_identifier("invalid constant declaration, symbol expected"); symbol id = curr_id(); next(); parse_sort("Invalid constant declaration"); SASSERT(!sort_stack().empty()); func_decl_ref c(m()); c = m().mk_const_decl(id, sort_stack().back()); TRACE("declare_const", tout << "declaring " << id << " "; pm().display(tout, sort_stack().back()); tout << "\n";); SASSERT(c.get() != 0); sort_stack().pop_back(); m_ctx.insert(c); check_rparen("invalid constant declaration, ')' expected"); m_ctx.print_success(); next(); } unsigned parse_opt_unsigned(unsigned def) { unsigned num; if (!curr_is_rparen()) { check_int("invalid push command, integer expected"); rational n = curr_numeral(); if (n.is_neg()) throw parser_exception("invalid push command, value is negative."); if (!n.is_unsigned()) throw parser_exception("invalid push command, value is too big to fit in an unsigned machine integer"); num = n.get_unsigned(); next(); } else { num = def; } return num; } void parse_push() { SASSERT(curr_is_identifier()); SASSERT(curr_id() == m_push); next(); unsigned num = parse_opt_unsigned(1); m_ctx.push(num); check_rparen("invalid push command, ')' expected"); m_ctx.print_success(); next(); } void parse_pop() { SASSERT(curr_is_identifier()); SASSERT(curr_id() == m_pop); next(); unsigned num = parse_opt_unsigned(1); m_ctx.pop(num); check_rparen("invalid pop command, ')' expected"); m_ctx.print_success(); next(); TRACE("after_pop", tout << "expr_stack.size: " << expr_stack().size() << "\n"; m_ctx.dump_assertions(tout);); } std::string m_assert_expr; void parse_assert() { SASSERT(curr_is_identifier()); SASSERT(curr_id() == m_assert); m_last_named_expr.first = symbol::null; m_last_named_expr.second = 0; if (m_ctx.interactive_mode()) { m_scanner.start_caching(); m_cache_end = 0; } next(); parse_expr(); if (m_ctx.interactive_mode()) { m_assert_expr = m_scanner.cached_str(0, m_cache_end); m_scanner.stop_caching(); } if (expr_stack().empty()) { throw cmd_exception("invalid assert command, expression required as argument"); } expr * f = expr_stack().back(); if (!m().is_bool(f)) { TRACE("smt2parser", tout << expr_ref(f, m()) << "\n";); throw cmd_exception("invalid assert command, term is not Boolean"); } if (f == m_last_named_expr.second) { m_ctx.assert_expr(m_last_named_expr.first, f); } else { m_ctx.assert_expr(f); } if (m_ctx.interactive_mode()) { m_ctx.push_assert_string(m_assert_expr); } expr_stack().pop_back(); check_rparen("invalid assert command, ')' expected"); m_ctx.print_success(); next(); } void parse_assumptions() { while (!curr_is_rparen()) { parse_expr(); if (!m().is_bool(expr_stack().back())) throw parser_exception("invalid check-sat command, argument must be a Boolean literal"); } } void parse_check_sat() { SASSERT(curr_is_identifier()); SASSERT(curr_id() == m_check_sat); next(); unsigned spos = expr_stack().size(); parse_assumptions(); m_ctx.check_sat(expr_stack().size() - spos, expr_stack().c_ptr() + spos); next(); expr_stack().shrink(spos); } void parse_check_sat_assuming() { SASSERT(curr_is_identifier()); SASSERT(curr_id() == m_check_sat_assuming); next(); unsigned spos = expr_stack().size(); check_lparen_next("invalid check-sat-assuming command, '(', expected"); parse_assumptions(); check_rparen_next("invalid check-sat-assuming command, ')', expected"); m_ctx.check_sat(expr_stack().size() - spos, expr_stack().c_ptr() + spos); next(); expr_stack().shrink(spos); } void parse_get_value() { SASSERT(curr_is_identifier()); SASSERT(curr_id() == m_get_value); next(); unsigned spos = expr_stack().size(); unsigned cache_it = 0; m_scanner.start_caching(); m_cache_end = 0; m_cached_strings.resize(0); check_lparen_next("invalid get-value command, '(' expected"); while (!curr_is_rparen()) { parse_expr(); if (!is_ground(expr_stack().back())) throw cmd_exception("invalid get-value term, term must be ground and must not contain quantifiers"); m_cached_strings.push_back(m_scanner.cached_str(cache_it, m_cache_end)); cache_it = m_cache_end; } m_scanner.stop_caching(); if (m_cached_strings.empty()) throw cmd_exception("invalid get-value command, empty list of terms"); next(); unsigned index = 0; if (curr_is_keyword() && (curr_id() == ":model-index" || curr_id() == ":model_index")) { next(); check_int("integer index expected to indexed model evaluation"); index = curr_unsigned(); next(); } check_rparen("invalid get-value command, ')' expected"); model_ref md; if (m_ctx.ignore_check()) { expr_stack().shrink(spos); next(); return; } if (!m_ctx.is_model_available(md) || m_ctx.get_check_sat_result() == nullptr) throw cmd_exception("model is not available"); if (index != 0) { m_ctx.get_opt()->get_box_model(md, index); } m_ctx.regular_stream() << "("; expr ** expr_it = expr_stack().c_ptr() + spos; expr ** expr_end = expr_it + m_cached_strings.size(); md->compress(); for (unsigned i = 0; expr_it < expr_end; expr_it++, i++) { model::scoped_model_completion _scm(md, true); expr_ref v = (*md)(*expr_it); if (i > 0) m_ctx.regular_stream() << "\n "; m_ctx.regular_stream() << "(" << m_cached_strings[i] << " "; m_ctx.display(m_ctx.regular_stream(), v); m_ctx.regular_stream() << ")"; } m_ctx.regular_stream() << ")" << std::endl; expr_stack().shrink(spos); next(); } void parse_reset() { SASSERT(curr_is_identifier()); SASSERT(curr_id() == m_reset); next(); check_rparen("invalid reset command, ')' expected"); m_ctx.reset(); reset(); m_ctx.print_success(); next(); } void parse_option_value() { switch (curr()) { case scanner::BV_TOKEN: case scanner::INT_TOKEN: case scanner::FLOAT_TOKEN: m_curr_cmd->set_next_arg(m_ctx, m_scanner.get_number()); next(); break; case scanner::SYMBOL_TOKEN: m_curr_cmd->set_next_arg(m_ctx, m_scanner.get_id()); next(); break; case scanner::STRING_TOKEN: m_curr_cmd->set_next_arg(m_ctx, m_scanner.get_string()); next(); break; default: throw parser_exception("invalid option value"); } } // A func_decl reference is of the form: // // | ( (+) sort) // | ((_ +) (+) sort) func_decl * parse_func_decl_ref() { if (curr_is_identifier()) { symbol id = curr_id(); func_decl * d = m_ctx.find_func_decl(id); next(); return d; } else { check_lparen_next("invalid function declaration reference, symbol or '(' expected"); symbol id; sbuffer indices; if (curr_is_identifier()) { id = curr_id(); next(); } else { check_lparen_next("invalid function declaration reference, symbol or '(' expected"); check_underscore_next("invalid indexed function declaration reference, '_' expected"); check_identifier("invalid indexed function declaration reference, symbol expected"); id = curr_id(); next(); while (!curr_is_rparen()) { check_int("invalid indexed function declaration reference, integer or ')' expected"); unsigned u = curr_unsigned(); indices.push_back(u); next(); } if (indices.empty()) throw parser_exception("invalid indexed function declaration reference, index expected"); next(); } unsigned spos = sort_stack().size(); parse_sorts("Invalid function name. Expecting sort list starting with '(' to disambiguate function name"); unsigned domain_size = sort_stack().size() - spos; parse_sort("Invalid function name"); func_decl * d = m_ctx.find_func_decl(id, indices.size(), indices.c_ptr(), domain_size, sort_stack().c_ptr() + spos, sort_stack().back()); sort_stack().shrink(spos); check_rparen_next("invalid function declaration reference, ')' expected"); return d; } } void parse_func_decl_refs(ptr_buffer & flist) { check_lparen_next("invalid list of function declaration references, '(' expected"); while (!curr_is_rparen()) { flist.push_back(parse_func_decl_ref()); } next(); } void parse_next_cmd_arg() { SASSERT(m_curr_cmd != 0); cmd_arg_kind k = m_curr_cmd->next_arg_kind(m_ctx); switch (k) { case CPK_UINT: { check_int("invalid command argument, unsigned integer expected"); unsigned u = curr_unsigned(); m_curr_cmd->set_next_arg(m_ctx, u); next(); break; } case CPK_BOOL: { check_identifier("invalid command argument, true/false expected"); symbol val = curr_id(); if (val != "true" && val != "false") throw parser_exception("invalid command argument, true/false expected"); m_curr_cmd->set_next_arg(m_ctx, val == "true"); next(); break; } case CPK_NUMERAL: check_int_or_float("invalid command argument, numeral expected"); m_curr_cmd->set_next_arg(m_ctx, curr_numeral()); next(); break; case CPK_DECIMAL: check_float("invalid command argument, decimal expected"); m_curr_cmd->set_next_arg(m_ctx, curr_numeral()); next(); break; case CPK_STRING: check_string("invalid command argument, string expected"); m_curr_cmd->set_next_arg(m_ctx, m_scanner.get_string()); next(); break; case CPK_KEYWORD: check_keyword("invalid command argument, keyword expected"); m_curr_cmd->set_next_arg(m_ctx, curr_id()); next(); break; case CPK_OPTION_VALUE: parse_option_value(); break; case CPK_SYMBOL: check_identifier("invalid command argument, symbol expected"); m_curr_cmd->set_next_arg(m_ctx, curr_id()); next(); return; case CPK_SYMBOL_LIST: { unsigned spos = m_symbol_stack.size(); unsigned num = parse_symbols(); m_curr_cmd->set_next_arg(m_ctx, num, m_symbol_stack.c_ptr() + spos); break; } case CPK_SORT: parse_sort("invalid command argument, sort expected"); m_curr_cmd->set_next_arg(m_ctx, sort_stack().back()); return; case CPK_SORT_LIST: { unsigned spos = sort_stack().size(); unsigned num = parse_sorts("expecting sort list starting with '('"); m_curr_cmd->set_next_arg(m_ctx, num, sort_stack().c_ptr() + spos); break; } case CPK_EXPR: parse_expr(); m_curr_cmd->set_next_arg(m_ctx, expr_stack().back()); return; case CPK_EXPR_LIST: { unsigned spos = expr_stack().size(); unsigned num = parse_exprs(); m_curr_cmd->set_next_arg(m_ctx, num, expr_stack().c_ptr() + spos); break; } case CPK_FUNC_DECL: { func_decl * f = parse_func_decl_ref(); m_curr_cmd->set_next_arg(m_ctx, f); return; } case CPK_FUNC_DECL_LIST: { ptr_buffer flist; parse_func_decl_refs(flist); m_curr_cmd->set_next_arg(m_ctx, flist.size(), flist.c_ptr()); return; } case CPK_SORTED_VAR: NOT_IMPLEMENTED_YET(); break; case CPK_SORTED_VAR_LIST: NOT_IMPLEMENTED_YET(); break; case CPK_SEXPR: parse_sexpr(); m_curr_cmd->set_next_arg(m_ctx, sexpr_stack().back()); break; case CPK_INVALID: throw parser_exception("invalid/unexpected argument"); default: throw parser_exception("unexpected argument"); } } void parse_unknown_cmd() { SASSERT(curr_is_identifier()); symbol s = curr_id(); next(); while (!curr_is_rparen()) { consume_sexpr(); } m_ctx.print_unsupported(s, m_scanner.get_line(), m_scanner.get_pos()); next(); return; } void parse_ext_cmd(int line, int pos) { symbol s = curr_id(); m_curr_cmd = m_ctx.find_cmd(s); if (m_curr_cmd == nullptr) { parse_unknown_cmd(); return; } next(); unsigned arity = m_curr_cmd->get_arity(); unsigned i = 0; unsigned sort_spos = size(m_sort_stack); unsigned expr_spos = size(m_expr_stack); unsigned sexpr_spos = size(m_sexpr_stack); unsigned sym_spos = m_symbol_stack.size(); m_curr_cmd->set_line_pos(line, pos); m_curr_cmd->prepare(m_ctx); while (true) { if (curr_is_rparen()) { if (arity != VAR_ARITY && i < arity) throw parser_exception("invalid command, argument(s) missing"); m_curr_cmd->execute(m_ctx); next(); m_curr_cmd = nullptr; shrink(m_sort_stack, sort_spos); shrink(m_expr_stack, expr_spos); shrink(m_sexpr_stack, sexpr_spos); m_symbol_stack.shrink(sym_spos); m_num_bindings = 0; // HACK for propagating the update of parser parameters if (norm_param_name(s) == "set_option") { updt_params(); } return; } else { if (arity != VAR_ARITY && i == arity) throw parser_exception("invalid command, too many arguments"); parse_next_cmd_arg(); } i++; } } void parse_cmd() { SASSERT(curr_is_lparen()); int line = m_scanner.get_line(); int pos = m_scanner.get_pos(); next(); check_identifier("invalid command, symbol expected"); symbol s = curr_id(); if (s == m_assert) { parse_assert(); return; } if (s == m_declare_fun) { parse_declare_fun(); return; } if (s == m_declare_const) { parse_declare_const(); return; } if (s == m_check_sat) { parse_check_sat(); return; } if (s == m_push) { parse_push(); return; } if (s == m_pop) { parse_pop(); return; } if (s == m_define_fun) { parse_define_fun(); return; } if (s == m_define_const) { parse_define_const(); return; } if (s == m_define_sort) { parse_define_sort(); return; } if (s == m_declare_sort) { parse_declare_sort(); return; } if (s == m_declare_datatypes) { parse_declare_datatypes(); return; } if (s == m_declare_datatype) { parse_declare_datatype(); return; } if (s == m_get_value) { parse_get_value(); return; } if (s == m_reset) { parse_reset(); return; } if (s == m_check_sat_assuming) { parse_check_sat_assuming(); return; } if (s == m_define_fun_rec) { parse_define_fun_rec(); return; } if (s == m_define_funs_rec) { parse_define_funs_rec(); return; } if (s == m_model_add) { parse_model_add(); return; } if (s == m_model_del) { parse_model_del(); return; } parse_ext_cmd(line, pos); } public: parser(cmd_context & ctx, std::istream & is, bool interactive, params_ref const & p, char const * filename=nullptr): m_ctx(ctx), m_params(p), m_scanner(ctx, is, interactive), m_curr(scanner::NULL_TOKEN), m_curr_cmd(nullptr), m_num_bindings(0), m_let("let"), m_bang("!"), m_forall("forall"), m_exists("exists"), m_lambda("lambda"), m_as("as"), m_not("not"), m_root_obj("root-obj"), m_named(":named"), m_weight(":weight"), m_qid(":qid"), m_skid(":skolemid"), m_ex_act(":ex-act"), m_pattern(":pattern"), m_nopattern(":no-pattern"), m_lblneg(":lblneg"), m_lblpos(":lblpos"), m_assert("assert"), m_check_sat("check-sat"), m_define_fun("define-fun"), m_define_const("define-const"), m_model_add("model-add"), m_model_del("model-del"), m_declare_fun("declare-fun"), m_declare_const("declare-const"), m_define_sort("define-sort"), m_declare_sort("declare-sort"), m_declare_datatypes("declare-datatypes"), m_declare_datatype("declare-datatype"), m_par("par"), m_push("push"), m_pop("pop"), m_get_value("get-value"), m_reset("reset"), m_check_sat_assuming("check-sat-assuming"), m_define_fun_rec("define-fun-rec"), m_define_funs_rec("define-funs-rec"), m_match("match"), m_case("case"), m_underscore("_"), m_num_open_paren(0), m_current_file(filename) { // the following assertion does not hold if ctx was already attached to an AST manager before the parser object is created. // SASSERT(!m_ctx.has_manager()); updt_params(); } ~parser() { reset_stack(); } void updt_params() { parser_params p(m_params); m_ignore_user_patterns = p.ignore_user_patterns(); m_ignore_bad_patterns = p.ignore_bad_patterns(); m_display_error_for_vs = p.error_for_visual_studio(); } void reset() { reset_stack(); m_num_bindings = 0; m_psort_stack = nullptr; m_sort_stack = nullptr; m_expr_stack = nullptr; m_pattern_stack = nullptr; m_nopattern_stack = nullptr; m_sexpr_stack = nullptr; m_symbol_stack .reset(); m_param_stack .reset(); m_env .reset(); m_sort_id2param_idx .reset(); m_dt_name2idx .reset(); m_bv_util = nullptr; m_arith_util = nullptr; m_seq_util = nullptr; m_pattern_validator = nullptr; m_var_shifter = nullptr; } sexpr_ref parse_sexpr_ref() { m_num_bindings = 0; m_num_open_paren = 0; try { scan_core(); parse_sexpr(); if (!sexpr_stack().empty()) { return sexpr_ref(sexpr_stack().back(), sm()); } } catch (z3_exception & ex) { error(ex.msg()); } return sexpr_ref(nullptr, sm()); } bool operator()() { m_num_bindings = 0; unsigned found_errors = 0; try { scan_core(); } catch (scanner_exception & ex) { error(ex.msg()); if (!sync_after_error()) return false; found_errors++; } while (true) { try { m_num_open_paren = 0; while (true) { switch (curr()) { case scanner::LEFT_PAREN: parse_cmd(); break; case scanner::EOF_TOKEN: return found_errors == 0; default: throw parser_exception("invalid command, '(' expected"); break; } } } catch (z3_error & ex) { // Can't invoke error(...) when out of memory. // Reason: escaped() string builder needs memory m_ctx.regular_stream() << "(error \"line " << m_scanner.get_line() << " column " << m_scanner.get_pos() << ": " << ex.msg() << "\")" << std::endl; exit(ex.error_code()); } catch (const stop_parser_exception &) { m_scanner.stop_caching(); return !found_errors; } catch (parser_exception & ex) { if (ex.has_pos()) error(ex.line(), ex.pos(), ex.msg()); else error(ex.msg()); } catch (ast_exception & ex) { error(ex.msg()); } catch (z3_exception & ex) { error(ex.msg()); } m_scanner.stop_caching(); if (m_curr_cmd) m_curr_cmd->failure_cleanup(m_ctx); reset(); found_errors = true; if (!sync_after_error()) return false; TRACE("parser_error", tout << "after sync: " << curr() << "\n";); SASSERT(m_num_open_paren == 0); } } }; }; bool parse_smt2_commands(cmd_context & ctx, std::istream & is, bool interactive, params_ref const & ps, char const * filename) { smt2::parser p(ctx, is, interactive, ps, filename); return p(); } sexpr_ref parse_sexpr(cmd_context& ctx, std::istream& is, params_ref const& ps, char const* filename) { smt2::parser p(ctx, is, false, ps, filename); return p.parse_sexpr_ref(); } z3-z3-4.8.7/src/parsers/smt2/smt2parser.h000066400000000000000000000010411356505360400200310ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: smt2parser.h Abstract: SMT 2.0 parser Author: Leonardo de Moura (leonardo) 2011-03-01 Revision History: --*/ #ifndef SMT2_PARSER_H_ #define SMT2_PARSER_H_ #include "cmd_context/cmd_context.h" bool parse_smt2_commands(cmd_context & ctx, std::istream & is, bool interactive = false, params_ref const & p = params_ref(), char const * filename = nullptr); sexpr_ref parse_sexpr(cmd_context& ctx, std::istream& is, params_ref const& ps, char const* filename); #endif z3-z3-4.8.7/src/parsers/smt2/smt2scanner.cpp000066400000000000000000000262721356505360400205360ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt2scanner.cpp Abstract: Author: Leonardo de Moura (leonardo) 2011-03-09. Revision History: --*/ #include "parsers/smt2/smt2scanner.h" #include "parsers/util/parser_params.hpp" namespace smt2 { void scanner::next() { if (m_cache_input) m_cache.push_back(m_curr); SASSERT(!m_at_eof); if (m_interactive) { m_curr = m_stream.get(); if (m_stream.eof()) m_at_eof = true; } else if (m_bpos < m_bend) { m_curr = m_buffer[m_bpos]; m_bpos++; } else { m_stream.read(m_buffer, SCANNER_BUFFER_SIZE); m_bend = static_cast(m_stream.gcount()); m_bpos = 0; if (m_bpos == m_bend) { m_at_eof = true; } else { m_curr = m_buffer[m_bpos]; m_bpos++; } } m_spos++; } void scanner::read_comment() { SASSERT(curr() == ';'); next(); while (true) { char c = curr(); if (m_at_eof) return; if (c == '\n') { new_line(); next(); return; } next(); } } void scanner::read_multiline_comment() { SASSERT(curr() == '|'); next(); while (true) { char c = curr(); if (m_at_eof) return; if (c == '\n') { new_line(); next(); continue; } next(); if (c == '|' && curr() == '#') { next(); return; } } } scanner::token scanner::read_quoted_symbol() { SASSERT(curr() == '|'); bool escape = false; m_string.reset(); next(); while (true) { char c = curr(); if (m_at_eof) { throw scanner_exception("unexpected end of quoted symbol", m_line, m_spos); } else if (c == '\n') { new_line(); } else if (c == '|' && !escape) { next(); m_string.push_back(0); m_id = m_string.begin(); TRACE("scanner", tout << "new quoted symbol: " << m_id << "\n";); return SYMBOL_TOKEN; } escape = (c == '\\'); m_string.push_back(c); next(); } } scanner::token scanner::read_symbol_core() { while (!m_at_eof) { char c = curr(); signed char n = m_normalized[static_cast(c)]; if (n == 'a' || n == '0' || n == '-') { m_string.push_back(c); next(); } else { m_string.push_back(0); m_id = m_string.begin(); TRACE("scanner", tout << "new symbol: " << m_id << "\n";); return SYMBOL_TOKEN; } } if (!m_string.empty()) { m_string.push_back(0); m_id = m_string.begin(); return SYMBOL_TOKEN; } return EOF_TOKEN; } scanner::token scanner::read_symbol() { SASSERT(m_normalized[static_cast(curr())] == 'a' || curr() == ':' || curr() == '-'); m_string.reset(); m_string.push_back(curr()); next(); return read_symbol_core(); } scanner::token scanner::read_number() { SASSERT('0' <= curr() && curr() <= '9'); rational q(1); m_number = rational(curr() - '0'); next(); bool is_float = false; while (!m_at_eof) { char c = curr(); if ('0' <= c && c <= '9') { m_number = rational(10)*m_number + rational(c - '0'); if (is_float) q *= rational(10); next(); } else if (c == '.') { if (is_float) break; is_float = true; next(); } else { break; } } if (is_float) m_number /= q; TRACE("scanner", tout << "new number: " << m_number << "\n";); return is_float ? FLOAT_TOKEN : INT_TOKEN; } scanner::token scanner::read_signed_number() { SASSERT(curr() == '-'); next(); if ('0' <= curr() && curr() <= '9') { scanner::token r = read_number(); m_number.neg(); return r; } else { // it is a symbol. m_string.reset(); m_string.push_back('-'); return read_symbol_core(); } } scanner::token scanner::read_string() { SASSERT(curr() == '\"'); next(); m_string.reset(); while (true) { char c = curr(); if (m_at_eof) throw scanner_exception("unexpected end of string", m_line, m_spos); if (c == '\n') { new_line(); } else if (c == '\"') { next(); if (curr() != '\"') { m_string.push_back(0); return STRING_TOKEN; } } m_string.push_back(c); next(); } } scanner::token scanner::read_bv_literal() { SASSERT(curr() == '#'); next(); char c = curr(); if (c == 'x') { next(); c = curr(); m_number = rational(0); m_bv_size = 0; while (true) { if ('0' <= c && c <= '9') { m_number *= rational(16); m_number += rational(c - '0'); } else if ('a' <= c && c <= 'f') { m_number *= rational(16); m_number += rational(10 + (c - 'a')); } else if ('A' <= c && c <= 'F') { m_number *= rational(16); m_number += rational(10 + (c - 'A')); } else { if (m_bv_size == 0) throw scanner_exception("invalid empty bit-vector literal", m_line, m_spos); return BV_TOKEN; } m_bv_size += 4; next(); c = curr(); } } else if (c == 'b') { next(); c = curr(); m_number = rational(0); m_bv_size = 0; while (c == '0' || c == '1') { m_number *= rational(2); m_number += rational(c - '0'); m_bv_size++; next(); c = curr(); } if (m_bv_size == 0) throw scanner_exception("invalid empty bit-vector literal", m_line, m_spos); return BV_TOKEN; } else if (c == '|') { read_multiline_comment(); return NULL_TOKEN; } else { throw scanner_exception("invalid bit-vector literal, expecting 'x' or 'b'", m_line, m_spos); } } scanner::scanner(cmd_context & ctx, std::istream& stream, bool interactive) : m_interactive(interactive), m_spos(0), m_curr(0), // avoid Valgrind warning m_at_eof(false), m_line(1), m_pos(0), m_bv_size(UINT_MAX), m_bpos(0), m_bend(0), m_stream(stream), m_cache_input(false) { m_smtlib2_compliant = ctx.params().m_smtlib2_compliant; for (int i = 0; i < 256; ++i) { m_normalized[i] = (signed char) i; } m_normalized[static_cast('\t')] = ' '; m_normalized[static_cast('\r')] = ' '; // assert ('a' < 'z'); for (char ch = 'b'; ch <= 'z'; ++ch) { m_normalized[static_cast(ch)] = 'a'; } for (char ch = 'A'; ch <= 'Z'; ++ch) { m_normalized[static_cast(ch)] = 'a'; } // assert ('0' < '9', '9' - '0' == 9); for (char ch = '1'; ch <= '9'; ++ch) { m_normalized[static_cast(ch)] = '0'; } // SMT2 "Symbols": ~ ! @ $ % ^ & * _ - + = < > . ? / m_normalized[static_cast('~')] = 'a'; m_normalized[static_cast('!')] = 'a'; m_normalized[static_cast('@')] = 'a'; m_normalized[static_cast('$')] = 'a'; m_normalized[static_cast('%')] = 'a'; m_normalized[static_cast('^')] = 'a'; m_normalized[static_cast('&')] = 'a'; m_normalized[static_cast('*')] = 'a'; m_normalized[static_cast('_')] = 'a'; m_normalized[static_cast('-')] = '-'; m_normalized[static_cast('+')] = 'a'; m_normalized[static_cast('=')] = 'a'; m_normalized[static_cast('<')] = 'a'; m_normalized[static_cast('>')] = 'a'; m_normalized[static_cast('.')] = 'a'; m_normalized[static_cast('?')] = 'a'; m_normalized[static_cast('/')] = 'a'; m_normalized[static_cast(',')] = 'a'; next(); } scanner::token scanner::scan() { while (true) { signed char c = curr(); token t; m_pos = m_spos; if (m_at_eof) return EOF_TOKEN; switch (m_normalized[(unsigned char) c]) { case ' ': next(); break; case '\n': next(); new_line(); break; case ';': read_comment(); break; case ':': read_symbol(); return KEYWORD_TOKEN; case '(': next(); return LEFT_PAREN; case ')': next(); return RIGHT_PAREN; case '|': return read_quoted_symbol(); case 'a': return read_symbol(); case '"': return read_string(); case '0': return read_number(); case '#': t = read_bv_literal(); if (t == NULL_TOKEN) break; return t; case '-': if (m_smtlib2_compliant) return read_symbol(); else return read_signed_number(); default: { scanner_exception ex("unexpected character", m_line, m_spos); next(); throw ex; }} } } char const * scanner::cached_str(unsigned begin, unsigned end) { m_cache_result.reset(); while (isspace(m_cache[begin]) && begin < end) begin++; while (begin < end && isspace(m_cache[end-1])) end--; for (unsigned i = begin; i < end; i++) m_cache_result.push_back(m_cache[i]); m_cache_result.push_back(0); return m_cache_result.begin(); } }; z3-z3-4.8.7/src/parsers/smt2/smt2scanner.h000066400000000000000000000056561356505360400202060ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt2scanner.h Abstract: Author: Leonardo de Moura (leonardo) 2011-03-09. Revision History: --*/ #ifndef SMT2SCANNER_H_ #define SMT2SCANNER_H_ #include #include "util/symbol.h" #include "util/vector.h" #include "util/rational.h" #include "cmd_context/cmd_context.h" namespace smt2 { typedef cmd_exception scanner_exception; class scanner { private: bool m_interactive; int m_spos; // position in the current line of the stream char m_curr; // current char; bool m_at_eof; int m_line; // line int m_pos; // start position of the token // data symbol m_id; rational m_number; unsigned m_bv_size; // end of data signed char m_normalized[256]; #define SCANNER_BUFFER_SIZE 1024 char m_buffer[SCANNER_BUFFER_SIZE]; unsigned m_bpos; unsigned m_bend; svector m_string; std::istream& m_stream; bool m_cache_input; svector m_cache; svector m_cache_result; bool m_smtlib2_compliant; char curr() const { return m_curr; } void new_line() { m_line++; m_spos = 0; } void next(); public: enum token { NULL_TOKEN = 0, LEFT_PAREN = 1, RIGHT_PAREN, KEYWORD_TOKEN, SYMBOL_TOKEN, STRING_TOKEN, INT_TOKEN, BV_TOKEN, FLOAT_TOKEN, EOF_TOKEN }; scanner(cmd_context & ctx, std::istream& stream, bool interactive = false); ~scanner() {} int get_line() const { return m_line; } int get_pos() const { return m_pos; } symbol const & get_id() const { return m_id; } rational get_number() const { return m_number; } unsigned get_bv_size() const { return m_bv_size; } char const * get_string() const { return m_string.begin(); } token scan(); token read_symbol_core(); token read_symbol(); token read_quoted_symbol(); void read_comment(); void read_multiline_comment(); token read_number(); token read_signed_number(); token read_string(); token read_bv_literal(); void start_caching() { m_cache_input = true; m_cache.reset(); } void stop_caching() { m_cache_input = false; } unsigned cache_size() const { return m_cache.size(); } void reset_cache() { m_cache.reset(); } char const * cached_str(unsigned begin, unsigned end); }; }; #endif /* SCANNER_H_ */ z3-z3-4.8.7/src/parsers/util/000077500000000000000000000000001356505360400156525ustar00rootroot00000000000000z3-z3-4.8.7/src/parsers/util/CMakeLists.txt000066400000000000000000000003011356505360400204040ustar00rootroot00000000000000z3_add_component(parser_util SOURCES cost_parser.cpp pattern_validation.cpp scanner.cpp simple_parser.cpp COMPONENT_DEPENDENCIES ast PYG_FILES parser_params.pyg ) z3-z3-4.8.7/src/parsers/util/cost_parser.cpp000066400000000000000000000032311356505360400207010ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: cost_parser.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-06-14. Revision History: --*/ #include "parsers/util/cost_parser.h" cost_parser::cost_parser(ast_manager & m): simple_parser(m), m_util(m), m_vars(m) { family_id fid; fid = m.get_basic_family_id(); add_builtin_op("true", fid, OP_TRUE); add_builtin_op("false", fid, OP_FALSE); add_builtin_op("not", fid, OP_NOT); add_builtin_op("and", fid, OP_AND); add_builtin_op("implies", fid, OP_IMPLIES); add_builtin_op("or", fid, OP_OR); add_builtin_op("ite", fid, OP_ITE); add_builtin_op("=", fid, OP_EQ); add_builtin_op("iff", fid, OP_EQ); add_builtin_op("xor", fid, OP_XOR); fid = m_util.get_family_id(); add_builtin_op("+", fid, OP_ADD); add_builtin_op("*", fid, OP_MUL); add_builtin_op("-", fid, OP_SUB); add_builtin_op("/", fid, OP_DIV); add_builtin_op("<=", fid, OP_LE); add_builtin_op(">=", fid, OP_GE); add_builtin_op("<", fid, OP_LT); add_builtin_op(">", fid, OP_GT); } expr * cost_parser::parse_int(rational const & r) { return m_util.mk_numeral(r, false); } expr * cost_parser::parse_float(rational const & r) { return m_util.mk_numeral(r, false); } unsigned cost_parser::add_var(symbol name) { sort * real = m_util.mk_real(); unsigned r = m_vars.size(); var * v = m_manager.mk_var(r, real); simple_parser::add_var(name, v); m_vars.push_back(v); return r; } void cost_parser::reset_vars() { simple_parser::reset_vars(); m_vars.reset(); } z3-z3-4.8.7/src/parsers/util/cost_parser.h000066400000000000000000000013541356505360400203520ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: cost_parser.h Abstract: Author: Leonardo de Moura (leonardo) 2008-06-14. Revision History: --*/ #ifndef COST_PARSER_H_ #define COST_PARSER_H_ #include "parsers/util/simple_parser.h" #include "ast/arith_decl_plugin.h" class cost_parser : public simple_parser { arith_util m_util; var_ref_vector m_vars; public: cost_parser(ast_manager & m); ~cost_parser() override {} expr * parse_int(rational const & r) override; expr * parse_float(rational const & r) override; unsigned add_var(symbol name); unsigned add_var(char const * name) { return add_var(symbol(name)); } void reset_vars(); }; #endif /* COST_PARSER_H_ */ z3-z3-4.8.7/src/parsers/util/parser_params.pyg000066400000000000000000000006251356505360400212350ustar00rootroot00000000000000def_module_params('parser', export=True, params=(('ignore_user_patterns', BOOL, False, 'ignore patterns provided by the user'), ('ignore_bad_patterns', BOOL, True, 'ignore malformed patterns'), ('error_for_visual_studio', BOOL, False, 'display error messages in Visual Studio format'), )) z3-z3-4.8.7/src/parsers/util/pattern_validation.cpp000066400000000000000000000064051356505360400222520ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: pattern_validation.cpp Abstract: Code for checking whether a pattern is valid or not. Author: Leonardo de Moura (leonardo) 2006-12-08. Revision History: --*/ #include "parsers/util/pattern_validation.h" #include "ast/for_each_expr.h" #include "util/warning.h" #include "ast/ast_pp.h" struct pattern_validation_functor { uint_set & m_found_vars; unsigned m_num_bindings; unsigned m_num_new_bindings; bool m_result; bool m_found_a_var; family_id m_bfid; family_id m_lfid; unsigned m_line, m_pos; pattern_validation_functor(uint_set & found_vars, unsigned num_bindings, unsigned num_new_bindings, family_id bfid, family_id lfid, unsigned line, unsigned pos): m_found_vars(found_vars), m_num_bindings(num_bindings), m_num_new_bindings(num_new_bindings), m_result(true), m_found_a_var(false), m_bfid(bfid), m_lfid(lfid), m_line(line), m_pos(pos) { } bool is_forbidden(func_decl const * decl) { family_id fid = decl->get_family_id(); if (fid == m_bfid && decl->get_decl_kind() != OP_EQ && decl->get_decl_kind() != OP_TRUE && decl->get_decl_kind() != OP_FALSE) return true; if (fid == m_lfid) return true; return false; } void operator()(app * n) { func_decl * decl = to_app(n)->get_decl(); if (is_forbidden(decl)) { warning_msg("(%d,%d): '%s' cannot be used in patterns.", m_line, m_pos, decl->get_name().str().c_str()); m_result = false; } } void operator()(var * v) { unsigned idx = to_var(v)->get_idx(); if (idx >= m_num_bindings) { warning_msg("(%d,%d): free variables cannot be used in patterns.", m_line, m_pos); m_result = false; return; } if (idx < m_num_new_bindings) { m_found_a_var = true; m_found_vars.insert(idx); } } void operator()(quantifier * q) { m_result = false; } }; bool pattern_validator::process(uint_set & found_vars, unsigned num_bindings, unsigned num_new_bindings, expr * n, unsigned line, unsigned pos) { // I'm traversing the DAG as a tree, this is not a problem since pattern are supposed to be small ASTs. if (n->get_kind() == AST_VAR) { warning_msg("(%d,%d): invalid pattern: variable.", line, pos); return false; } pattern_validation_functor f(found_vars, num_bindings, num_new_bindings, m_bfid, m_lfid, line, pos); for_each_expr(f, n); if (!f.m_result) return false; if (!f.m_found_a_var) { warning_msg("(%d,%d): pattern does not contain any variable.", line, pos); return false; } return true; } bool pattern_validator::operator()(unsigned num_bindings, unsigned num_new_bindings, expr * n, unsigned line, unsigned pos) { uint_set found_vars; if (!process(found_vars, num_bindings, num_new_bindings, n, line, pos)) return false; bool r = found_vars.num_elems() == num_new_bindings; if (!r) warning_msg("(%d,%d): pattern does not contain all quantified variables.", line, pos); return r; } z3-z3-4.8.7/src/parsers/util/pattern_validation.h000066400000000000000000000020131356505360400217060ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: pattern_validation.h Abstract: Code for checking whether a pattern is valid or not. Author: Leonardo de Moura (leonardo) 2006-12-08. Revision History: --*/ #ifndef PATTERN_VALIDATION_H_ #define PATTERN_VALIDATION_H_ #include "ast/ast.h" #include "util/uint_set.h" #include "util/vector.h" class pattern_validator { family_id m_bfid; family_id m_lfid; bool process(uint_set & found_vars, unsigned num_bindings, unsigned num_new_bindings, expr * n, unsigned line, unsigned pos); public: pattern_validator(ast_manager const & m): m_bfid(m.get_basic_family_id()), m_lfid(m.get_label_family_id()) { } bool operator()(unsigned num_bindings, unsigned num_new_bindings, expr * n, unsigned line, unsigned pos); bool operator()(unsigned num_new_bindings, expr * n, unsigned line, unsigned pos) { return operator()(UINT_MAX, num_new_bindings, n, line, pos); } }; #endif /* PATTERN_VALIDATION_H_ */ z3-z3-4.8.7/src/parsers/util/scanner.cpp000066400000000000000000000330701356505360400200120ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: scanner.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-03-31. Revision History: --*/ #include "parsers/util/scanner.h" inline int scanner::read_char() { if (m_is_interactive) { ++m_pos; return m_stream.get(); } if (m_bpos >= m_bend) { m_buffer[0] = m_last_char; m_stream.read(m_buffer.c_ptr()+1, m_buffer.size()-1); m_bend = 1 + static_cast(m_stream.gcount()); m_bpos = 1; m_last_char = m_buffer[m_bend-1]; } ++m_pos; if (m_bpos < m_bend) { return m_buffer[m_bpos++]; } else { // increment m_bpos, so unread_char() will work properly ++m_bpos; return -1; } } inline void scanner::unread_char() { --m_pos; if (m_is_interactive) { m_stream.unget(); } else { // at most one character can be unread. SASSERT(m_bpos > 0); --m_bpos; } } inline bool scanner::state_ok() { return m_state != ERROR_TOKEN && m_state != EOF_TOKEN; } void scanner::comment(char delimiter) { while(state_ok()) { int ch = read_char(); if ('\n' == ch) { ++m_line; } if (delimiter == ch || -1 == ch) { return; } } } scanner::token scanner::read_symbol(int ch) { bool escape = false; if (m_smt2) m_string.pop_back(); // remove leading '|' while (ch != '|' || escape) { if (ch == EOF) { // TODO: use error reporting m_err << "ERROR: unexpected end of file.\n"; return EOF_TOKEN; } if (ch == '\n') { ++m_line; } escape = (ch == '\\'); m_string.push_back(ch); ch = read_char(); } if (!m_smt2) m_string.push_back(ch); // don't add trailing '|' m_string.push_back(0); m_id = m_string.begin(); return ID_TOKEN; } scanner::token scanner::read_id(char first_char) { int ch; m_string.reset(); m_params.reset(); m_string.push_back(first_char); bool is_arith = (m_normalized[(unsigned char) first_char] == '+'); bool is_alpha = (m_normalized[(unsigned char) first_char] == 'a'); ch = read_char(); // In SMT2 "-20" is an identifier. if (!m_smt2 && state_ok() && first_char == '-' && m_normalized[(unsigned char) ch] == '0') { return read_number(ch, false); } if (state_ok() && first_char == '|') { return read_symbol(ch); } while (state_ok()) { switch(m_normalized[(unsigned char) ch]) { case '+': if (is_arith) { m_string.push_back(ch); break; } // strings can have hyphens. if (!is_alpha || ch != '-') { goto bail_out; } case 'a': case ':': case '.': case '0': if (is_arith) { goto bail_out; } m_string.push_back(ch); break; case '[': m_string.push_back(0); m_id = m_string.begin(); if (read_params()) { return ID_TOKEN; } else { return m_state; } default: goto bail_out; } ch = read_char(); } return m_state; bail_out: m_string.push_back(0); m_id = m_string.begin(); unread_char(); return ID_TOKEN; } bool scanner::read_params() { unsigned param_num = 0; while (state_ok()) { int ch = read_char(); switch (m_normalized[(unsigned char) ch]) { case '0': param_num = 10*param_num + (ch - '0'); break; case ']': m_params.push_back(parameter(param_num)); return true; case ':': m_params.push_back(parameter(param_num)); param_num = 0; break; default: m_string.reset(); m_string.push_back(ch); while (true) { ch = read_char(); if (ch == ':' || ch == ']') { m_string.push_back(0); m_params.push_back(parameter(symbol(m_string.c_ptr()))); param_num = 0; if (ch == ':') { unread_char(); } else { return true; } break; } if (ch == EOF) { // TODO: use error reporting m_err << "ERROR: unexpected character: '" << ((int)ch) << " " << ch << "'.\n"; m_state = ERROR_TOKEN; break; } m_string.push_back(ch); } break; } } return false; } scanner::token scanner::read_number(char first_char, bool is_pos) { unsigned divide_by = 0; m_number = rational(first_char - '0'); m_state = INT_TOKEN; while (true) { int ch = read_char(); if (m_normalized[(unsigned char) ch] == '0') { m_number = rational(10)*m_number + rational(ch - '0'); if (m_state == FLOAT_TOKEN) { ++divide_by; } } else if (ch == '.') { m_state = FLOAT_TOKEN; } else { unread_char(); break; } } if (!is_pos) { m_number.neg(); } if (m_state == FLOAT_TOKEN) { m_number /= power(rational(10), divide_by); } return m_state; } scanner::token scanner::read_string(char delimiter, token result) { m_string.reset(); m_params.reset(); while (true) { int ch = read_char(); if (!state_ok()) { return m_state; } if (ch == '\n') { ++m_line; } if (ch == delimiter || ch == EOF) { m_string.push_back(0); m_id = m_string.begin(); return result; } if (ch == '\\') { m_string.push_back('\\'); ch = read_char(); } m_string.push_back(ch); } return m_state; } scanner::token scanner::read_bv_literal() { TRACE("scanner", tout << "read_bv_literal\n";); if (m_bv_token) { int ch = read_char(); if (ch == 'x') { ch = read_char(); m_number = rational(0); m_bv_size = 0; while (true) { if ('0' <= ch && ch <= '9') { m_number *= rational(16); m_number += rational(ch - '0'); } else if ('a' <= ch && ch <= 'f') { m_number *= rational(16); m_number += rational(10 + (ch - 'a')); } else if ('A' <= ch && ch <= 'F') { m_number *= rational(16); m_number += rational(10 + (ch - 'A')); } else { unread_char(); m_state = m_bv_size == 0 ? ERROR_TOKEN : BV_TOKEN; TRACE("scanner", tout << m_state << ", bv-size: " << m_bv_size << ", INT_TOKEN: " << INT_TOKEN << ", BV_TOKEN: " << BV_TOKEN << "\n";); return m_state; } m_bv_size += 4; ch = read_char(); } } else if (ch == 'b') { ch = read_char(); m_number = rational(0); m_bv_size = 0; while (ch == '0' || ch == '1') { m_number *= rational(2); m_number += rational(ch - '0'); m_bv_size++; ch = read_char(); } unread_char(); m_state = m_bv_size == 0 ? ERROR_TOKEN : BV_TOKEN; return m_state; } else { m_state = ERROR_TOKEN; return m_state; } } else { // hack for the old parser int ch = read_char(); bool is_hex = false; m_state = ID_TOKEN; m_string.reset(); m_params.reset(); // convert to SMT1 format m_string.push_back('b'); m_string.push_back('v'); if (ch == 'x') { m_string.push_back('h'); m_string.push_back('e'); m_string.push_back('x'); is_hex = true; } else if (ch == 'b') { m_string.push_back('b'); m_string.push_back('i'); m_string.push_back('n'); } else { // TODO: use error reporting m_err << "ERROR: unexpected character after '#': '" << ((int)ch) << " " << ch << "'.\n"; m_state = ERROR_TOKEN; return m_state; } while (true) { signed ch = read_char(); if (ch == '0' || ch == '1' || (is_hex && (('0' <= ch && ch <= '9') || ('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F')))) { m_string.push_back(ch); } else { unread_char(); break; } } m_string.push_back(0); m_id = m_string.begin(); return m_state; } } scanner::scanner(std::istream& stream, std::ostream& err, bool smt2, bool bv_token): m_line(1), m_pos(0), m_id(""), m_bv_size(UINT_MAX), m_state(ID_TOKEN), m_stream(stream), m_err(err), m_bpos(1 << 10), m_bend(1 << 10), m_last_char(0), m_smt2(smt2), m_bv_token(bv_token) { char ch; m_is_interactive = &stream == &std::cin; m_buffer.resize(m_bpos); for (int i = 0; i < 256; ++i) { m_normalized[i] = (char) i; } m_normalized[static_cast('\t')] = ' '; m_normalized[static_cast('\r')] = ' '; // assert ('a' < 'z'); for (ch = 'b'; ch <= 'z'; ++ch) { m_normalized[static_cast(ch)] = 'a'; } for (ch = 'A'; ch <= 'Z'; ++ch) { m_normalized[static_cast(ch)] = 'a'; } // assert ('0' < '9', '9' - '0' == 9); for (ch = '1'; ch <= '9'; ++ch) { m_normalized[static_cast(ch)] = '0'; } if (m_smt2) { // SMT2 3.1, "Symbols": ~ ! @ $ % ^ & * _ - + = < > . ? / m_normalized[static_cast('~')] = 'a'; m_normalized[static_cast('!')] = 'a'; m_normalized[static_cast('@')] = 'a'; m_normalized[static_cast('$')] = 'a'; m_normalized[static_cast('%')] = 'a'; m_normalized[static_cast('^')] = 'a'; m_normalized[static_cast('&')] = 'a'; m_normalized[static_cast('*')] = 'a'; m_normalized[static_cast('_')] = 'a'; m_normalized[static_cast('-')] = 'a'; m_normalized[static_cast('+')] = 'a'; m_normalized[static_cast('=')] = 'a'; m_normalized[static_cast('<')] = 'a'; m_normalized[static_cast('>')] = 'a'; m_normalized[static_cast('.')] = 'a'; m_normalized[static_cast('?')] = 'a'; m_normalized[static_cast('/')] = 'a'; // SMT2 3.1, "Hexadecimals", "Binaries" m_normalized[static_cast('#')] = '#'; m_normalized[static_cast('|')] = '+'; } else { m_normalized[static_cast('=')] = '+'; m_normalized[static_cast('<')] = '+'; m_normalized[static_cast('>')] = '+'; m_normalized[static_cast('+')] = '+'; m_normalized[static_cast('-')] = '+'; m_normalized[static_cast('*')] = '+'; m_normalized[static_cast('/')] = '+'; m_normalized[static_cast('%')] = '+'; m_normalized[static_cast('~')] = '+'; m_normalized[static_cast('&')] = '+'; m_normalized[static_cast('@')] = '+'; m_normalized[static_cast('#')] = '+'; m_normalized[static_cast('|')] = '+'; m_normalized[static_cast('\\')] = '+'; m_normalized[static_cast('.')] = '.'; m_normalized[static_cast('_')] = 'a'; m_normalized[static_cast('\'')] = 'a'; m_normalized[static_cast('!')] = 'a'; m_normalized[static_cast('?')] = 'a'; } } scanner::token scanner::scan() { while (state_ok()) { int ch = read_char(); switch (m_normalized[(unsigned char) ch]) { case ' ': break; case '\n': m_pos = 0; ++m_line; break; case ';': comment('\n'); break; case ':': return COLON; case '(': return LEFT_PAREN; case ')': return RIGHT_PAREN; case '?': case '$': case 'a': case '+': case '.': return read_id(ch); case '{': return read_string('}',COMMENT_TOKEN); case '"': return read_string('"',STRING_TOKEN); case '0': return read_number(ch, true); case '#': return read_bv_literal(); case -1: m_state = EOF_TOKEN; break; default: // TODO: use error reporting m_err << "ERROR: unexpected character: '" << ((int)ch) << " " << ch << "'.\n"; m_state = ERROR_TOKEN; break; } } return m_state; } z3-z3-4.8.7/src/parsers/util/scanner.h000066400000000000000000000036201356505360400174550ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: scanner.h Abstract: Author: Leonardo de Moura (leonardo) 2008-03-31. Revision History: --*/ #ifndef SCANNER_H_ #define SCANNER_H_ #include "ast/ast.h" class scanner { public: enum token { LEFT_PAREN = 1, RIGHT_PAREN, COLON, ID_TOKEN, STRING_TOKEN, COMMENT_TOKEN, INT_TOKEN, BV_TOKEN, FLOAT_TOKEN, EOF_TOKEN, ERROR_TOKEN }; scanner(std::istream& stream, std::ostream& err, bool smt2, bool bv_token=false); ~scanner() {} int get_line() const { return m_line; } int get_pos() const { return m_pos; } symbol const & get_id() const { return m_id; } rational get_number() const { return m_number; } unsigned get_bv_size() const { return m_bv_size; } vector const & get_params() const { return m_params; } token scan(); private: int m_line; int m_pos; symbol m_id; rational m_number; unsigned m_bv_size; token m_state; char m_normalized[256]; vector m_string; std::istream& m_stream; std::ostream& m_err; vector m_params; buffer m_buffer; unsigned m_bpos; unsigned m_bend; char m_last_char; bool m_is_interactive; bool m_smt2; bool m_bv_token; int read_char(); token read_symbol(int ch); void unread_char(); void comment(char delimiter); token read_id(char first_char); bool read_params(); token read_number(char first_char, bool is_pos); token read_string(char delimiter, token result); token read_bv_literal(); bool state_ok(); }; #endif /* SCANNER_H_ */ z3-z3-4.8.7/src/parsers/util/simple_parser.cpp000066400000000000000000000067331356505360400212340ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: simple_parser.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-03-31. Revision History: --*/ #include #include #include "parsers/util/simple_parser.h" #include "util/warning.h" #include "parsers/util/scanner.h" simple_parser::simple_parser(ast_manager & m): m_manager(m), m_exprs(m) { } simple_parser::~simple_parser() { } void simple_parser::add_builtin_op(symbol const & s, family_id fid, decl_kind kind) { SASSERT(!m_builtin.contains(s)); SASSERT(!m_vars.contains(s)); m_builtin.insert(s, builtin_op(fid, kind)); } void simple_parser::add_builtin_op(char const * str, family_id fid, decl_kind kind) { add_builtin_op(symbol(str), fid, kind); } void simple_parser::add_var(symbol const & s, var * v) { SASSERT(!m_builtin.contains(s)); SASSERT(!m_vars.contains(s)); m_vars.insert(s, v); } void simple_parser::add_var(char const * str, var * v) { add_var(symbol(str), v); } void simple_parser::reset() { m_builtin.reset(); m_vars.reset(); m_exprs.reset(); } void simple_parser::reset_vars() { m_vars.reset(); } expr * simple_parser::parse_expr(scanner & s) { builtin_op op; var * v; expr * r; scanner::token token; token = s.scan(); switch (token) { case scanner::LEFT_PAREN: token = s.scan(); if (token != scanner::ID_TOKEN) throw parser_error(); if (m_builtin.find(s.get_id(), op)) { ptr_vector args; while (true) { expr * arg = parse_expr(s); if (arg) { args.push_back(arg); } else { expr * r = m_manager.mk_app(op.m_family_id, op.m_kind, args.size(), args.c_ptr()); m_exprs.push_back(r); return r; } } } throw parser_error(); case scanner::RIGHT_PAREN: return nullptr; case scanner::ID_TOKEN: if (m_builtin.find(s.get_id(), op)) { expr * r = m_manager.mk_const(op.m_family_id, op.m_kind); m_exprs.push_back(r); return r; } else if (m_vars.find(s.get_id(), v)) { return v; } throw parser_error(); case scanner::INT_TOKEN: r = parse_int(s.get_number()); m_exprs.push_back(r); return r; case scanner::FLOAT_TOKEN: r = parse_float(s.get_number()); m_exprs.push_back(r); return r; default: throw parser_error(); } } bool simple_parser::parse(std::istream & in, expr_ref & result) { scanner s(in, std::cerr, false); try { result = parse_expr(s); if (!result) throw parser_error(); } catch (const parser_error &) { warning_msg("parser error"); return false; } m_exprs.reset(); return result.get() != nullptr; } bool simple_parser::parse_string(char const * str, expr_ref & result) { std::string s = str; std::istringstream is(s); return parse(is, result); } bool simple_parser::parse_file(char const * file, expr_ref & result) { if (file != nullptr) { std::ifstream stream(file); if (!stream) { warning_msg("ERROR: could not open file '%s'.", file); return false; } return parse(stream, result); } else { return parse(std::cin, result); } } z3-z3-4.8.7/src/parsers/util/simple_parser.h000066400000000000000000000033141356505360400206710ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: simple_parser.h Abstract: Simple sexpr parser Author: Leonardo de Moura (leonardo) 2008-03-31. Revision History: --*/ #ifndef SIMPLE_PARSER_H_ #define SIMPLE_PARSER_H_ #include "ast/ast.h" #include "util/map.h" class scanner; /** \brief Simple sexpr parser. This class is used to parse small expressions used for configuring Z3. */ class simple_parser { protected: struct parser_error {}; struct builtin_op { family_id m_family_id; decl_kind m_kind; builtin_op() : m_family_id(null_family_id), m_kind(0) {} builtin_op(family_id fid, decl_kind k) : m_family_id(fid), m_kind(k) {} }; typedef map op_map; typedef map var_map; ast_manager & m_manager; op_map m_builtin; var_map m_vars; expr_ref_vector m_exprs; expr * parse_expr(scanner & s); public: simple_parser(ast_manager & m); virtual ~simple_parser(); void add_builtin_op(symbol const & s, family_id fid, decl_kind kind); void add_builtin_op(char const * str, family_id fid, decl_kind kind); void add_var(symbol const & s, var * v); void add_var(char const * str, var * v); void reset(); void reset_vars(); bool parse(std::istream & in, expr_ref & result); bool parse_string(char const * str, expr_ref & result); bool parse_file(char const * file, expr_ref & result); virtual expr * parse_int(rational const & r) { throw parser_error(); } virtual expr * parse_float(rational const & r) { throw parser_error(); } }; #endif /* SIMPLE_PARSER_H_ */ z3-z3-4.8.7/src/qe/000077500000000000000000000000001356505360400136235ustar00rootroot00000000000000z3-z3-4.8.7/src/qe/CMakeLists.txt000066400000000000000000000011351356505360400163630ustar00rootroot00000000000000z3_add_component(qe SOURCES nlarith_util.cpp nlqsat.cpp qe_arith.cpp qe_arith_plugin.cpp qe_array_plugin.cpp qe_arrays.cpp qe_bool_plugin.cpp qe_bv_plugin.cpp qe_cmd.cpp qe.cpp qe_datatype_plugin.cpp qe_datatypes.cpp qe_dl_plugin.cpp qe_lite.cpp qe_mbp.cpp qe_mbi.cpp qe_sat_tactic.cpp qe_solve_plugin.cpp qe_tactic.cpp qe_term_graph.cpp qsat.cpp COMPONENT_DEPENDENCIES nlsat_tactic nlsat sat smt tactic TACTIC_HEADERS nlqsat.h qe_lite.h qe_sat_tactic.h qe_tactic.h qsat.h ) z3-z3-4.8.7/src/qe/nlarith_util.cpp000066400000000000000000002201371356505360400170320ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "ast/ast.h" #include "qe/nlarith_util.h" #include "ast/arith_decl_plugin.h" #include "ast/ast_pp.h" #include "qe/qe.h" #include "ast/rewriter/expr_replacer.h" #include "ast/rewriter/arith_rewriter.h" #include "ast/rewriter/bool_rewriter.h" #include "ast/rewriter/th_rewriter.h" #include "ast/expr_functors.h" namespace nlarith { typedef app_ref_vector poly; enum comp { LE, LT, EQ, NE}; typedef vector polys; typedef svector comps; class util::literal_set { app_ref m_inf; app_ref m_sup; app* m_x; app_ref_vector m_lits; vector m_polys; svector m_comps; public: literal_set(ast_manager& m) : m_inf(m), m_sup(m), m_x(nullptr), m_lits(m) {} unsigned size() const { return m_lits.size(); } app_ref_vector& lits() { return m_lits; } vector& polys() { return m_polys; } svector& comps() { return m_comps; } poly const& get_poly(unsigned i) const { return m_polys[i]; } // Note: comp comp(unsigned i) is not valid C++. // it works on VC++, but it is rejected by gcc. comp compare(unsigned i) const { return m_comps[i]; } app* literal(unsigned i) const { return m_lits[i]; } app* x() const { return m_x; } void set_x(app* x) { SASSERT(!m_x); m_x = x; } app* x_inf() { if (!m_inf) { mk_const("inf", m_inf); } return m_inf; } app* x_sup() { if (!m_sup) { mk_const("sup", m_sup); } return m_sup; } private: void mk_const(char const* suffix, app_ref& v) { ast_manager& m = m_lits.get_manager(); std::string name = m_x->get_decl()->get_name().str(); name += suffix; sort* r = m.get_sort(m_x); v= m.mk_const(symbol(name.c_str()), r); } }; class util::imp { ast_manager& m_manager; arith_util m_arith; bool m_enable_linear; app_ref m_zero; app_ref m_one; smt_params m_params; bool_rewriter m_bs; arith_rewriter m_rw; expr_ref_vector m_trail; ast_manager& m() const { return m_manager; } arith_util& a() { return m_arith; } app* z() { return m_zero.get();} app* one() { return m_one.get(); } std::ostream& display(std::ostream& out, comp c) { switch(c) { case LE: out << "<="; return out; case LT: out << "<"; return out; case EQ: out << "="; return out; case NE: out << "!="; return out; } return out; } public: imp(ast_manager& m) : m_manager(m), m_arith(m), m_enable_linear(false), m_zero(num(0),m), m_one(num(1),m), m_bs(m), m_rw(m), m_trail(m) { } // // create branches and substitutions according to case analysis. // bool create_branches(app* x, unsigned num_lits, expr* const* lits, branch_conditions& branch_conds) { polys polys; comps comps; contains_app contains_x(m(), x); branch_conds.reset(); m_trail.reset(); // use scope? if (!a().is_real(x)) { return false; } if (!get_polys(contains_x, num_lits, lits, polys, comps, &branch_conds, nullptr)) { TRACE("nlarith", tout << "could not extract polynomials " << mk_pp(x, m()) << "\n"; for (unsigned i = 0; i < num_lits; ++i) { tout << mk_pp(lits[i], m()) << " "; } tout << "\n"; ); return false; } if (is_degree_two_plus(polys)) { return false; } if (!m_enable_linear && is_linear(polys)) { TRACE("nlarith", tout << "this is a linear problem " << mk_pp(x,m()) << "\n"; display(tout, polys);); return false; } unsigned idx; if (has_single_degree2(polys, comps, idx)) { for (unsigned i = 0; i < polys.size(); ++i) { create_branch_l(idx, i, polys, comps, branch_conds); } } else { for (unsigned i = 0; i < polys.size(); ++i) { create_branch(i, polys, comps, branch_conds); } } inf_branch(polys, comps, branch_conds); TRACE("nlarith", for (unsigned i = 0; i < num_lits; ++i) { tout << mk_pp(lits[i], m()) << " "; } tout << "\n"; display_branching(tout, x, branch_conds); ); return true; } void set_enable_linear(bool enable_linear) { m_enable_linear = enable_linear; } void extract_non_linear(unsigned sz, app* const* es, ptr_vector& nl_vars) { ast_mark visit; for (unsigned i = 0; i < sz; ++i) { extract_non_linear(es[i], visit, nl_vars); } } void extract_non_linear(expr* e, ptr_vector& nl_vars) { ast_mark visit; extract_non_linear(e, visit, nl_vars); } void extract_non_linear(expr* e, ast_mark& visit, ptr_vector& nl_vars) { if (visit.is_marked(e)) { return; } ast_mark nonlin; ptr_vector todo; todo.push_back(e); while(!todo.empty()) { e = todo.back(); todo.pop_back(); if (is_var(e)) { continue; } if (is_quantifier(e)) { e = to_quantifier(e)->get_expr(); if (!visit.is_marked(e)) { todo.push_back(e); } } SASSERT(is_app(e)); app* a = to_app(e); bool nl = m_enable_linear || nonlin.is_marked(e) || is_nonlinear(a); if (is_arithmetical(a)) { // TBD: overshoots in the case of 'ite' expressions. for (unsigned i = 0; i < a->get_num_args(); ++i) { expr* arg = a->get_arg(i); bool nl2 = nonlin.is_marked(arg); if (!visit.is_marked(arg) || (nl && !nl2)) { todo.push_back(to_app(arg)); visit.mark(arg, true); if (nl) { nonlin.mark(arg, true); } } } } else if (is_variable(a)) { if (nl) { nl_vars.push_back(a); } } else { for (unsigned i = 0; i < a->get_num_args(); ++i) { expr* arg = a->get_arg(i); if (!visit.is_marked(arg) || !nonlin.is_marked(arg)) { todo.push_back(to_app(arg)); visit.mark(arg, true); nonlin.mark(arg, true); } } } } TRACE("nlarith", tout << "Non-linear variables: "; for (unsigned i = 0; i < nl_vars.size(); ++i) { tout << mk_pp(nl_vars[i], m()) << " "; } tout << "\n";); } private: void track(expr* e) { m_trail.push_back(e); } app* mk_lt(expr* p) { expr_ref r(m()); m_rw.mk_lt(p, z(), r); track(r); return to_app(r); } app* mk_le(expr* p) { expr_ref r(m()); m_rw.mk_le(p, z(), r); track(r); return to_app(r); } app* mk_gt(expr* p) { return mk_lt(mk_uminus(p)); } app* mk_ge(expr* p) { return mk_le(mk_uminus(p)); } app* mk_eq(expr* p) { expr_ref r(m()); m_bs.mk_eq(p, z(), r); track(r); return to_app(r); } app* mk_ne(expr* p) { expr_ref r(m()); m_bs.mk_eq(p, z(), r); m_bs.mk_not(r, r); track(r); return to_app(r); } app* num(int i) { return a().mk_numeral(rational(i), false); } // // TBD: perhaps merge with arith_rewriter using a app_ref buffer? // app* mk_uminus(expr* e) { expr_ref r(m()); m_rw.mk_uminus(e, r); track(r); return to_app(r); } app* mk_add(unsigned sz, expr* const* args) { expr_ref r(m()); m_rw.mk_add(sz, args, r); track(r); return to_app(r); } app* mk_add(expr* t, expr* s) { expr_ref r(m()); expr* args[2] = { t, s}; m_rw.mk_add(2, args, r); track(r); return to_app(r); } app* mk_add(expr* t, expr* s, expr* u) { return mk_add(t, mk_add(s, u)); } app* mk_mul(expr* t, expr* s) { expr_ref r(m()); expr* args[2] = { t, s}; m_rw.mk_mul(2, args, r); track(r); return to_app(r); } app* mk_sub(expr* t, expr* s) { expr_ref r(m()); expr* args[2] = { t, s}; m_rw.mk_sub(2, args, r); track(r); return to_app(r); } app* mk_mul(expr* t, expr* s, expr* u) { return mk_mul(t, mk_mul(s, u)); } app* mk_and(unsigned num_args, expr* const* args) { expr_ref r(m()); m_bs.mk_and(num_args, args, r); track(r); return to_app(r); } app* mk_and(expr* a, expr* b) { expr* args[2] = { a, b }; return mk_and(2, args); } app* mk_or(unsigned num_args, expr* const* args) { expr_ref r(m()); m_bs.mk_or(num_args, args, r); track(r); return to_app(r); } app* mk_or(expr* a, expr* b) { expr* args[2] = { a, b }; return mk_or(2, args); } void display_branching( std::ostream& out, app* x, branch_conditions const& bc) const { out << mk_pp(x, m()) << ":\n"; for (unsigned i = 0; i < bc.preds().size(); ++i) { out << "Pred: " << mk_pp(bc.preds()[i], m()) << "\n"; } for (unsigned i = 0; i < bc.branches().size(); ++i) { out << "Branch:\n" << mk_pp(bc.branches()[i], m()) << "\n"; for (unsigned j = 0; j < bc.subst()[i].size(); ++j) { out << mk_pp(bc.preds()[j], m()) << " |-> " << mk_pp(bc.subst(i)[j], m()) << "\n"; } out << "Def: " << mk_pp(bc.def(i), m()) << "\n"; } } struct abc_poly { app_ref m_a; app_ref m_b; app_ref m_c; abc_poly(imp& I, app* a, app* b, app* c): m_a(a, I.m()), m_b(b, I.m()), m_c(c, I.m()) {} }; struct sqrt_form { app_ref m_a; int m_b; app_ref m_c; app_ref m_d; sqrt_form(imp& I, app* a, int b, app* c, app* d) : m_a(a, I.m()), m_b(b), m_c(c, I.m()), m_d(d, I.m()) { SASSERT(d != I.z()); } void display(std::ostream& out) const { ast_manager& m = m_a.get_manager(); out << "(/ (+ " << mk_pp(m_a, m) << " (* " << m_b << " (sqrt " << mk_pp(m_c, m) << "))) " << mk_pp(m_d, m) << ")"; } }; expr* mk_abs(expr* e) { return m().mk_ite(mk_lt(e), mk_uminus(e), e); } // // result = (a + b*sqrt(c))/d // expr* to_expr(sqrt_form const& s) { arith_util& A = a(); expr* result; // result = (a + b*sqrt(c))/d if (s.m_c == z() || s.m_b == 0) { result = A.mk_div(s.m_a, s.m_d); } else { expr* half = A.mk_numeral(rational(1,2), false); result = A.mk_div(mk_add(s.m_a, mk_mul(num(s.m_b), A.mk_power(mk_abs(s.m_c), half))), s.m_d); } return result; } // // // Given p(x): ax^2 + bx + c < 0 // then the derivative is d p(x)/dx = 2ax + b // cases: // 1. a != 0, b != 0: // zero: (- b +- sqrt(b^2 - 4ac))/ 2a // then slope of x at zero is: // 2a*zero + b = +- sqrt(..), // so the slope is given by the sign of the solution. // // return zero + epsilon * (if sign > 0 then -1 else 1) // // 2. a = 0, b != 0: // zero : -c/b // slope is b. // return -c/b + epsilon * (if b > 0 then -1 else 1) // // Given p(x): ax^2 + bx + c <= 0, ax^2 + bx + c = 0, // use epsilon = 0. // Given p(x): ax^2 + bx + c <= 0, ax^2 + bx + c = 0, // use epsilon as in < case. // expr* mk_def(comp cmp, abc_poly const& p, sqrt_form const& s) { expr* result = to_expr(s); if (is_strict(cmp)) { if (p.m_a == z()) { result = mk_add(result, mk_mul(mk_epsilon(), m().mk_ite(mk_lt(p.m_b),num(1),num(-1)))); } else { if (s.m_b > 0) { result = mk_add(result, mk_mul(num(-1),mk_epsilon())); } else { result = mk_add(result, mk_epsilon()); } } } return result; } // // TBD: Compute an espilon based on the terms // used in the constraints. // expr* mk_epsilon() { return a().mk_numeral(rational(1,10000), false); } // // TBD: Compute an inf based on the terms // used in the constraints. Eg., use a symbolic // constant for epsilon and inf and then solve for // it postiori. // expr* mk_inf() { return a().mk_numeral(rational(-10000), false); } // lower bounds for each case: // a*x^2 + b*x + c + eps = 0 & a = 0 & b = 0 => x < 0 // a*x^2 + b*x + c + eps = 0 & a = 0 & b != 0 => x < - (c / b) < - (c^2 +1) * (1 + 1/b^2) // a*x^2 + b*x + c + eps = 0 & a != 0 => x < (-|b| - sqrt(b^2 - 4ac))/2|a| < - (b^2*(1 + 1/a^2) + (c^2+1)) app* sq(expr* e) { return mk_mul(e,e); } app* sq1(expr * e) { return mk_add(num(1), sq(e)); } app* inv(expr * e) { return a().mk_div(num(1), e); } expr* mk_inf(branch_conditions const& bc) { return mk_inf(); #if 0 if (bc.size() > 0) { // pick a number lower than the symbolic lower bounds. for(unsigned i = 0; i < bc.size(); ++i) { expr * a = bc.a(i); expr * b = bc.b(i); expr * c = bc.c(i); expr * e = m().mk_ite( mk_eq(a), m().mk_ite( mk_eq(b), num(0), mk_mul(mk_add(sq(c),num(1)), sq1(inv(b)))), mk_add(mk_mul(sq(b),sq1(inv(a))), sq1(c))); r = mk_add(e, r); } return mk_uminus(r); } #endif } void inf_branch( polys const& polys, comps const& comps, branch_conditions& bc) { // /\_j p_j -> p_j[-oo / x] app_ref t1(m()); expr_ref_vector es(m()), subst(m()); for (unsigned j = 0; j < polys.size(); ++j) { minus_inf_subst sub(*this); apply_subst(sub, comps[j], polys[j], t1); es.push_back(m().mk_implies(bc.preds(j), t1)); subst.push_back(t1); TRACE("nlarith_verbose", display(tout << "inf", polys[j]); display(tout, comps[j]); tout << " 0 [-oo] --> " << mk_pp(t1.get(), m()) << "\n";); } TRACE("nlarith", tout << "inf-branch\n";); bc.add_branch(mk_and(es.size(), es.c_ptr()), m().mk_true(), subst, mk_inf(bc), z(), z(), z()); } void create_branch_l(unsigned j, unsigned i, polys const& polys, comps const& comps, branch_conditions& bc) { comp cmp = comps[i]; poly const& p = polys[i]; if (i == j) cmp = LE; // non-strict to avoid epsilon substitution mode. app* a, *b, *c; get_coefficients(p, a, b, c); app_ref t1(m()); expr_ref a2(m()), d(m()), t2(m()), cond(m()); expr_ref_vector es(m()), subst(m()); if (b != z()) { sqrt_form e0(*this, mk_uminus(c), 0, z(), b); // a_i = 0 /\ b_i != 0 /\ phi[e_i/x] TRACE("nlarith", display(tout << "a_i != 0 & b_i != 0 & hi[e_i / x]", p);tout<<"\n";); scoped_ptr rp = mk_default_expr_replacer(m()); expr_substitution sub(m()); sub.insert(a, z()); rp->set_substitution(&sub); if (a != z()) es.push_back(mk_eq(a)); es.push_back(mk_ne(b)); cond = mk_and(es.size(), es.c_ptr()); es.push_back(bc.preds(i)); for (unsigned k = 0; k < polys.size(); ++k) { mk_subst(cmp, polys[k], comps[k], e0, t1); (*rp)(t1, t2); es.push_back(m().mk_implies(bc.preds(k), t2)); subst.push_back(t1); } bc.add_branch(mk_and(es.size(), es.c_ptr()), cond, subst, mk_def(cmp, abc_poly(*this, z(), b, c), e0), a, b, c); } if (i == j && a != z()) { // a != 0 & phi[-b/(2a)/x] TRACE("nlarith", display(tout << "a != 0 & phi[-b/2a / x]", p);tout<<"\n";); app* a2 = mk_mul(num(2), a); sqrt_form e1(*this, mk_uminus(b), 0, z(), a2); es.reset(); subst.reset(); cond = mk_ne(a); es.push_back(cond); es.push_back(bc.preds(i)); for (unsigned k = 0; k < polys.size(); ++k) { mk_subst(cmp, polys[k], comps[k], e1, t1); es.push_back(m().mk_implies(bc.preds(k), t1)); subst.push_back(t1); } bc.add_branch(mk_and(es.size(), es.c_ptr()), cond, subst, mk_def(cmp, abc_poly(*this, a2, b, z()),e1), a, b, c); } } void create_branch(unsigned i, polys const& polys, comps const& comps, branch_conditions& bc) { comp cmp = comps[i]; poly const& p = polys[i]; app* a, *b, *c; get_coefficients(p, a, b, c); app_ref t1(m()), a2(m()), d(m()); expr_ref cond(m()), t2(m()), branch(m()); expr_ref_vector es(m()), subst(m()); d = mk_sub(mk_mul(b,b), mk_mul(num(4), a, c)); a2 = mk_mul(a, num(2)); TRACE("nlarith", display(tout, p); tout << "\n"; tout << "a:" << mk_pp(a, m()) << "\n"; tout << "b:" << mk_pp(b,m()) << "\n"; tout << "c:" << mk_pp(c,m()) << "\n"; tout << "d:" << mk_pp(d, m()) << "\n";); // p & a = 0 & b != 0 & /\_j p_j -> p_j[e0/x] // p & a != 0 & 0 <= d & /\_j p_j -> (p_j[e1/x] \/ p_j[e2/x]) // or: // p & a = 0 & b != 0 /\_j p_j -> p[e0+eps/x] // p & a != 0 & 0 <= d /\_j p_j -> p[e1+eps/x] \/ p[e2+eps/x] if (b != z()) { sqrt_form e0(*this, mk_uminus(c), 0, z(), b); es.reset(); subst.reset(); scoped_ptr rp = mk_default_expr_replacer(m()); expr_substitution sub(m()); sub.insert(a, z()); rp->set_substitution(&sub); if (a != z()) es.push_back(mk_eq(a)); es.push_back(mk_ne(b)); cond = mk_and(es.size(), es.c_ptr()); es.push_back(bc.preds(i)); for (unsigned j = 0; j < polys.size(); ++j) { mk_subst(cmp, polys[j], comps[j], e0, t1); (*rp)(t1, t2); es.push_back(m().mk_implies(bc.preds(j), t2)); subst.push_back(t2); } branch = mk_and(es.size(), es.c_ptr()); bc.add_branch(branch, cond, subst, mk_def(cmp, abc_poly(*this, z(), b, c), e0), a, b, c); } if (a != z()) { sqrt_form e1(*this, mk_uminus(b), 1, d, a2); sqrt_form e2(*this, mk_uminus(b), -1, d, a2); es.reset(); subst.reset(); es.push_back(mk_ne(a)); es.push_back(mk_ge(d)); cond = mk_and(es.size(), es.c_ptr()); es.push_back(bc.preds(i)); for (unsigned j = 0; j < polys.size(); ++j) { mk_subst(cmp, polys[j], comps[j], e1, t1); es.push_back(m().mk_implies(bc.preds(j), t1)); subst.push_back(t1); } branch = mk_and(es.size(), es.c_ptr()); bc.add_branch(branch, cond, subst, mk_def(cmp, abc_poly(*this, a, b, c), e1), a, b, c); TRACE("nlarith", tout << mk_pp(branch,m()) << "\n";); TRACE("nlarith", tout << "0 <= " << mk_pp(d,m()) << "\n"; tout << mk_pp(mk_ge(d), m()) << "\n";); es.resize(3); subst.reset(); for (unsigned j = 0; j < polys.size(); ++j) { mk_subst(cmp, polys[j], comps[j], e2, t1); es.push_back(m().mk_implies(bc.preds(j), t1)); subst.push_back(t1); } branch = mk_and(es.size(), es.c_ptr()); bc.add_branch(branch, cond, subst, mk_def(cmp, abc_poly(*this, a, b, c), e2), a, b, c); TRACE("nlarith", tout << mk_pp(branch,m()) << "\n";); } } bool is_strict(comp c) const { return c == LT || c == NE; } void mk_subst(comp c1, poly const& p, comp c, sqrt_form const& e, app_ref& r) { sqrt_subst sub(*this, e); if (is_strict(c1)) { plus_eps_subst sub2(*this, sub); apply_subst(sub2, c, p, r); } else { apply_subst(sub, c, p, r); } TRACE("nlarith_verbose", display(tout, p); display(tout, c); e.display(tout << " 0 "); tout << " --> " << mk_pp(r.get(), m()) << "\n";); } void get_coefficients(poly const& p, app*& a, app*& b, app*& c) { a = b = c = z(); if (!p.empty()) c = p[0]; if (p.size() > 1) b = p[1]; if (p.size() > 2) a = p[2]; SASSERT(p.size() <= 3); } bool is_le(expr* e, expr*& s, expr*& t) { return a().is_ge(e, t, s) || a().is_le(e, s, t); } bool is_lt(expr* e, expr*& s, expr*& t) { return a().is_gt(e, t, s) || a().is_lt(e, s, t); } bool is_degree_two_plus(polys const& ps) { for (unsigned i = 0; i < ps.size(); ++i) { if (ps[i].size() > 3) { TRACE("nlarith", tout << "not second-degree: "; display(tout, ps[i]); tout <<"\n"; ); return true; } } return false; } bool is_linear(polys& ps) const { rational n; for (unsigned i = 0; i < ps.size(); ++i) { if (ps[i].size() > 2) return false; if (ps[i].size() == 2) { if (is_numeral(ps[i][1].get(), n)) { ps[i][1] = m_arith.mk_numeral(n, false); } else { return false; } } } return true; } bool has_single_degree2(polys const& ps, comps const& comps, unsigned& idx) const { unsigned n = 0; for (unsigned i = 0; i < ps.size(); ++i) { if (ps[i].size() == 3) { ++n; idx = i; if (comps[i] == EQ) { return false; } } } return n == 1; } /** \brief Create branch conditions for each atomic formulI. */ bool get_polys(contains_app& contains_x, unsigned num_lits, expr* const* lits, polys& polys, comps& comps, branch_conditions* bc, app_ref_vector* literals) { ast_manager& M = m(); expr* e1, *e2, *e3; app_ref t(M); poly p(M); comp c; for (unsigned i = 0; i < num_lits; ++i) { if (!contains_x(lits[i])) { continue; } // e1 <= e2 if (is_le(lits[i], e1, e2)) { t = mk_sub(e1, e2); c = LE; } // ! (e2 <= e3) <=> e3 < e2 else if (M.is_not(lits[i], e1) && is_le(e1, e2, e3)) { t = mk_sub(e3, e2); c = LT; } // e1 < e2 else if (is_lt(lits[i], e1, e2)) { t = mk_sub(e1, e2); c = LT; } // ! (e2 < e3) <=> e3 <= e2 else if (M.is_not(lits[i], e1) && is_lt(e1, e2, e3)) { t = mk_sub(e3, e2); c = LE; } else if (M.is_eq(lits[i], e1, e2)) { t = mk_sub(e1, e2); c = EQ; } else if (M.is_not(lits[i], e1) && M.is_eq(e1, e2, e3)) { t = mk_sub(e2, e3); c = NE; } else { return false; } if (!get_decomposition(t, contains_x, p)) { return false; } polys.push_back(p); comps.push_back(c); if (bc) { bc->add_pred(lits[i]); } if (literals) { literals->push_back(to_app(lits[i])); } TRACE("nlarith_verbose", tout << mk_pp(lits[i], m()) << " -> "; display(tout, p); tout << "\n"; ); } return true; } void display(std::ostream& out, poly const& p) { out << "("; for (unsigned i = 0; i < p.size(); ++i) { out << i << ": " << mk_pp(p[i], m()); if (i + 1 < p.size()) out << ", "; } out << ")"; } void display(std::ostream& out, polys const& ps) { for (unsigned i = 0; i < ps.size(); ++i) { display(out, ps[i]); out << " "; } } bool is_numeral(expr* t, rational& n) const { if (!is_app(t)) return false; app* e = to_app(t); func_decl* f = e->get_decl(); if (f->get_family_id() != m_arith.get_family_id()) { return false; } rational m; switch(f->get_decl_kind()) { case OP_ADD: #define MK_AOP(_mk_op_) \ SASSERT(e->get_num_args() > 0); \ if (!is_numeral(e->get_arg(0), n)) return false; \ for (unsigned i = 1; i < e->get_num_args(); ++i) { \ if (!is_numeral(e->get_arg(i), m)) return false; \ n = n _mk_op_ m; \ } MK_AOP(+); return true; case OP_MUL: MK_AOP(*); return true; case OP_SUB: MK_AOP(-); return true; case OP_UMINUS: if (!is_numeral(e->get_arg(0), n)) return false; n.neg(); return true; case OP_NUM: return m_arith.is_numeral(e, n); default: return false; } } /** \brief Decompose polynomial into sum of powers of 'x'. p = result[0] + x*result[1] + x*x*result[2] + ... */ bool get_decomposition(expr* t, contains_app& contains_x, poly& result) { result.reset(); if (!is_app(t)) { return false; } app* e = to_app(t); if (!contains_x(e)) { result.push_back(e); return true; } if (contains_x.x() == e) { result.push_back(z()); result.push_back(one()); return true; } func_decl* f = e->get_decl(); if (f->get_family_id() != a().get_family_id()) { return false; } poly r(m()); switch(f->get_decl_kind()) { case OP_ADD: #define MK_OP(_mk_op_) \ SASSERT(e->get_num_args() > 0); \ if (!get_decomposition(e->get_arg(0), contains_x, result)) return false;\ for (unsigned i = 1; i < e->get_num_args(); ++i) { \ if (!get_decomposition(e->get_arg(i), contains_x, r)) return false; \ _mk_op_(result, r); \ } MK_OP(mk_add); return true; case OP_MUL: MK_OP(mk_mul); return true; case OP_SUB: MK_OP(mk_sub); return true; case OP_UMINUS: if(!get_decomposition(e->get_arg(0), contains_x, result)) return false; mk_uminus(result); return true; default: TRACE("nlarith", tout << "Cannot decompose " << mk_pp(f, m()) << "\n" << mk_pp(e, m()) << "\n";); return false; } } void mk_uminus(poly& p) { for (unsigned i = 0; i < p.size(); ++i) { p[i] = mk_uminus(p[i].get()); } } void mk_sub(poly& r, poly const& other) { for (unsigned i = 0; i < r.size() && i < other.size(); ++i) { r[i] = mk_sub(r[i].get(), other[i]); } for (unsigned i = r.size(); i < other.size(); ++i) { r.push_back(mk_uminus(other[i])); } } void mk_add(poly& r, poly const& other) { for (unsigned i = 0; i < r.size() && i < other.size(); ++i) { r[i] = mk_add(r[i].get(), other[i]); } for (unsigned i = r.size(); i < other.size(); ++i) { r.push_back(other[i]); } } // r[0]*o[0] // r[0]*o[1] + r[1]*o[0] // r[0]*o[2] + r[1]*o[1] + r[2]*o[0] // // r[sz-1]*o[sz-2] + r[sz-2]*o[sz-1] // r[sz-1]*o[sz-1] void mk_mul(poly& r, poly const& other) { poly result(m()); for (unsigned i = 0; i + 1 < r.size() + other.size(); ++i) { app_ref t(z(), m()); for (unsigned j = 0; j <= i && j < r.size(); ++j) { unsigned k = i - j; if (k < other.size()) { t = mk_add(t, mk_mul(r[j].get(),other[k])); } } result.push_back(t); } TRACE("nlarith_verbose", display(tout, r); display(tout <<" * ", other); display(tout << " = ", result); tout <<"\n";); r.reset(); r.append(result.size(), result.c_ptr()); } void mk_mul(poly& p, expr* e) { for (unsigned i = 0; i < p.size(); ++i) { p[i] = mk_mul(p[i].get(), e); } } void mk_add(poly& p, unsigned shift, expr* e) { while (p.size() <= shift) { p.push_back(z()); } p[shift] = mk_add(p[shift].get(), e); } /** \brief Symbolic differentiation with respect to 'x'. result = [p[1], 2*p[2], 3*p[3],..,(num_terms-1)*p[num_terms-1]] */ void mk_differentiate(poly const& p, app_ref_vector& result) { for (unsigned i = 1; i < p.size(); ++i) { result.push_back(mk_mul(num(i), p[i])); } } class isubst { protected: imp& m_imp; public: isubst(imp& i) : m_imp(i) {} virtual void mk_lt(poly const& p, app_ref& r) = 0; virtual void mk_eq(poly const& p, app_ref& r) = 0; virtual void mk_le(poly const& p, app_ref& r) { imp& I = m_imp; app_ref r1(I.m()), r2(I.m()); mk_lt(p, r1); mk_eq(p, r2); r = I.mk_or(r1, r2); } virtual void mk_ne(poly const& p, app_ref& r) { imp& I = m_imp; mk_eq(p, r); r = I.m().mk_not(r); } }; void apply_subst(isubst& sub, comp c, poly const& p, app_ref& r) { switch(c) { case EQ: sub.mk_eq(p, r); return; case LE: sub.mk_le(p, r); return; case LT: sub.mk_lt(p, r); return; case NE: sub.mk_ne(p, r); return; } } class basic_subst : public isubst { app* m_x; public: basic_subst(imp& i, app* x) : isubst(i), m_x(x) {} void mk_lt(poly const& p, app_ref& r) override { imp& I = m_imp; app_ref result(I.m()); I.mk_polynomial(m_x, p, result); r = I.mk_lt(result); } void mk_eq(poly const& p, app_ref& r) override { imp& I = m_imp; app_ref result(I.m()); I.mk_polynomial(m_x, p, result); r = I.mk_eq(result); } }; class sqrt_subst : public isubst { sqrt_form const& m_s; public: sqrt_subst(imp& i, sqrt_form const& s): isubst(i), m_s(s) {} // p[e/x] < 0: (a*parity(d) < 0 /\ 0 < a*a - b*b*c) \/ // (b*parity(d) <= 0 /\ (a*parity(d) < 0 \/ a*a - b*b*c < 0)) void mk_lt(poly const& p, app_ref& r) override { imp& I = m_imp; ast_manager& m = I.m(); app_ref a(m), b(m), c(m_s.m_c), d(m); I.mk_instantiate(p, m_s, a, b, d); app_ref ad(a, m), bd(b, m), aabbc(m); if (is_even(p.size())) { ad = I.mk_mul(a, d); bd = I.mk_mul(b, d); } if (m_s.m_b == 0) { r = I.mk_lt(ad); } else { aabbc = I.mk_sub(I.mk_mul(a,a), I.mk_mul(b,b,c)); r = I.mk_or(I.mk_and(I.mk_lt(ad), I.mk_gt(aabbc)), I.mk_and(I.mk_le(bd), I.mk_or(I.mk_lt(ad), I.mk_lt(aabbc)))); } } // p[e/x] = 0: a*b <= 0 & a*a - b*b*c = 0 void mk_eq(poly const& p, app_ref& r) override { imp& I = m_imp; ast_manager& m = I.m(); app_ref a(m), b(m), c(m_s.m_c),d(m), aabbc(m); I.mk_instantiate(p, m_s, a, b, d); if (m_s.m_b == 0) { r = I.mk_eq(a); } else { aabbc = I.mk_sub(I.mk_mul(a, a), I.mk_mul(b, b, c)); r = I.mk_and(I.mk_le(I.mk_mul(a, b)), I.mk_eq(aabbc)); } } // p[e/x] <= 0: a*parity(d) <= 0 /\ 0 <= a*a - b*b*c \/ b*parity(d) <= 0 /\ a*a - b*b*c <= 0 void mk_le(poly const& p, app_ref& r) override { imp& I = m_imp; ast_manager& m = I.m(); app_ref a(m), b(m), c(m_s.m_c), d(m); I.mk_instantiate(p, m_s, a, b, d); app_ref ad(a, m), bd(b, m), aabbc(m); if (is_even(p.size())) { ad = I.mk_mul(a, d); bd = I.mk_mul(b, d); } if (m_s.m_b == 0) { r = I.mk_le(ad); } else { aabbc = I.mk_sub(I.mk_mul(a, a), I.mk_mul(b, b, c)); r = I.mk_or(I.mk_and(I.mk_le(ad), I.mk_ge(aabbc)), I.mk_and(I.mk_le(bd), I.mk_le(aabbc))); } } }; class plus_eps_subst : public isubst { isubst& m_s; /** \brief compute nu(p): nu(p) = p < 0 if degree(x) = 0 nu(p) = p < 0 \/ (p = 0 /\ nu(p')) Then p(x+epsilon) < 0 iff nu(p(x)) */ void mk_nu(poly const& p, app_ref& r) { imp& I = m_imp; ast_manager& m = I.m(); app_ref_vector t1(m); app_ref t3(m), t4(m); m_s.mk_lt(p, r); if (p.size() > 1) { m_s.mk_eq(p, t3); I.mk_differentiate(p, t1); mk_nu(t1, t4); // p < 0 \/ (p = 0 /\ nu(p')) r = I.mk_or(r, I.mk_and(t3, t4)); } } public: plus_eps_subst(imp& i, isubst& s) : isubst(i), m_s(s) {} void mk_lt(poly const& p, app_ref& r) override { mk_nu(p, r); } // /\ p[i] = 0 void mk_eq(poly const& p, app_ref& r) override { r = m_imp.mk_zero(p); } }; class minus_eps_subst : public isubst { isubst& m_s; /** \brief compute nu(p): nu(p, t) = p < 0 if degree(x) = 0 nu(p, f) = p > 0 if degree(x) = 0 nu(p, t) = p < 0 \/ (p = 0 /\ nu(p', f)) nu(p, f) = p > 0 \/ (p = 0 /\ nu(p', t)) Then p(x-epsilon) < 0 iff nu(p(x), t) */ void mk_lt(poly const& p, bool even, app_ref& r) { imp& I = m_imp; if (even) { m_s.mk_lt(p, r); } else { poly p1(p); I.mk_uminus(p1); m_s.mk_lt(p1, r); } } void mk_nu(poly const& p, bool even, app_ref& r) { imp& I = m_imp; ast_manager& m = I.m(); app_ref_vector t1(m); app_ref t3(m), t4(m); mk_lt(p, even, r); if (p.size() > 1) { m_s.mk_eq(p, t3); I.mk_differentiate(p, t1); mk_nu(t1, !even, t4); // p < 0 \/ (p = 0 /\ nu(p')) r = I.mk_or(r, I.mk_and(t3, t4)); } } public: minus_eps_subst(imp& i, isubst& s) : isubst(i), m_s(s) {} void mk_lt(poly const& p, app_ref& r) override { mk_nu(p, true, r); } // /\ p[i] = 0 void mk_eq(poly const& p, app_ref& r) override { r = m_imp.mk_zero(p); } }; class minus_inf_subst : public isubst { /** \brief compute mu(p) given by. p = p[0] + x*p[1] + x*x*p[2] + ... mu(p) = p[num_terms-1]*(-1)^(parity num_terms-1) < 0 \/ p[num_terms-1] = 0 /\ mu(num_terms-1, terms) */ app* mk_lt(poly const& p, unsigned i) { imp& I = m_imp; ast_manager& m = I.m(); if (i == 0) { return m.mk_false(); } --i; expr* t = p[i]; app* e = is_even(i)?I.mk_lt(t):I.mk_gt(t); if (i == 0) { return e; } else { return I.mk_or(e, I.mk_and(I.mk_eq(t), mk_lt(p, i))); } } public: minus_inf_subst(imp& i) : isubst(i) {} void mk_lt(poly const& p, app_ref& r) override { r = mk_lt(p, p.size()); } // /\ p[i] = 0 void mk_eq(poly const& p, app_ref& r) override { r = m_imp.mk_zero(p); } }; class plus_inf_subst : public isubst { app* mk_lt(poly const& p, unsigned i) { imp& I = m_imp; ast_manager& m = I.m(); if (i == 0) { return m.mk_false(); } --i; expr* t = p[i]; app* e = I.mk_lt(t); if (i == 0) { return e; } else { return I.mk_or(e, I.mk_and(I.mk_eq(t), mk_lt(p, i))); } } public: plus_inf_subst(imp& i) : isubst(i) {} void mk_lt(poly const& p, app_ref& r) override { r = mk_lt(p, p.size()); } // /\ p[i] = 0 void mk_eq(poly const& p, app_ref& r) override { r = m_imp.mk_zero(p); } }; /** \brief create polynomail expression. result = p[0] + x*p[1] + x*x*p[2] + ... */ void mk_polynomial(app* x, poly const& p, app_ref& result) { if (p.empty()) { result = z(); return; } app_ref xx(x, m()); expr_ref_vector tmp(m()); tmp.push_back(p[0]); for (unsigned i = 1; i < p.size(); ++i) { tmp.push_back(mk_mul(xx.get(), p[i])); xx = mk_mul(x, xx.get()); } result = mk_add(tmp.size(), tmp.c_ptr()); } app* mk_zero(poly const& p) { app_ref_vector tmp(m()); mk_zero(p, tmp); return mk_and(tmp.size(), reinterpret_cast(tmp.c_ptr())); } void mk_zero(poly const& p, app_ref_vector& zeros) { for (unsigned i = 0; i < p.size(); ++i) { zeros.push_back(mk_eq(p[i])); } } /** \brief Formal replacement of x by (a + b*sqrt(c))/d in p. where: p = p[0] + x*p[1] + x*x*p[2] + ... The result is an expression (a' + b'*sqrt(c))/d' */ void mk_instantiate(poly const& p, sqrt_form const& s, app_ref& ar, app_ref& br, app_ref& dr) { app* a = s.m_a, *c = s.m_c, *d = s.m_d; app_ref b(num(s.m_b), m()); br = z(); dr = one(); if (p.empty()) { ar = z(); return; } unsigned i = p.size() - 1; ar = p[i]; while (i > 0) { --i; // compute // p[i] + x * (ar + br*sqrt(c))/dr // = // p[i] + (a + b*sqrt(c))/d * (ar + br*sqrt(c))/dr // = // p[i] + (a*ar + b*br*c + (a*br + ar*b)*sqrt(c))/d*dr // = // (d*dr*p[i] + a*ar + b*br*c + (a*br + ar*b)*sqrt(c))/d*dr // app_ref tmp1(mk_add(mk_mul(d, dr, p[i]), mk_mul(a, ar), mk_mul(b, br, c)), m()); br = mk_add(mk_mul(a, br), mk_mul(ar, b)); dr = mk_mul(d, dr); ar = tmp1; } TRACE("nlarith_verbose", display(tout, p); s.display(tout << " "); tout << " " << mk_pp(ar, m()) << " " << mk_pp(br, m()) << " " << mk_pp(dr, m()) << "\n";); } static bool is_even(unsigned n) { return 0 == (n&0x1); } bool is_variable(app* e) { return a().is_real(e) && e->get_family_id() == null_family_id && e->get_num_args() == 0; } bool is_arithmetical(app* e) { if (e->get_family_id() == m().get_basic_family_id()) { return true; } if (e->get_family_id() == a().get_family_id()) { return true; } return false; } bool is_nonlinear(app* e) { if (a().is_mul(e)) { unsigned n = 0; for (unsigned i = 0; n < 2 && i < e->get_num_args(); ++i) { if (!a().is_numeral(e->get_arg(i))) { ++n; } } return n == 2; } return false; } private: // u = v*q + r 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.empty()) { q.reset(); r.reset(); r.append(u); return; } SASSERT(u.size() >= v.size() && v.size() > 0); unsigned n = v.size()-1; if (a().is_numeral(v[n])) { numeric_quot_rem(u, v, q, r); } else { pseudo_quot_rem(u, v, q, r, power); } } // // Compute q and r such that // u = v*q + r, // assuming the leading coefficient of v is a numeral. // void numeric_quot_rem(poly const& u, poly const& v, poly& q, poly& r) { SASSERT(u.size() > 0 && v.size() > 0); unsigned m = u.size()-1, n = v.size()-1; q.reset(); r.reset(); r.append(u); rational v_n; VERIFY(a().is_numeral(v[n], v_n)); app_ref v_inv(a().mk_numeral(rational(1)/v_n, false), m_manager); bool is_one = v_n.is_one(); for (int k = m-n+1; k > 0; ) { --k; if (is_one) { q[k] = u[n+k]; } else { q[k] = mk_mul(u[n+k], v_inv.get()); } for (int j = n + k - 1; j >= k; --j) { r[j] = mk_sub(r[j].get(), mk_mul(q[k].get(), v[j-k])); } } SASSERT(test_quot_rem(u, v, q, r)); } // // Compute q and r such that // lc(v)^{m-n+1}*u = v*q + r, // where lc(v) is the leading coefficient of v // of degree 'n' and the most significant coefficient // in u has degree 'm'. // void pseudo_quot_rem(poly const& u, poly const& v, poly& q, poly& r, unsigned& power) { SASSERT(u.size() > 0 && v.size() > 0); unsigned m = u.size()-1, n = v.size()-1; app* v_n = v[n]; power = m- n + 1; q.reset(); r.reset(); r.append(u); q.resize(m-n+1); poly powers_v(m_manager); powers_v.push_back(num(1)); for (unsigned i = 1; i < m-n+2; ++i) { powers_v[i] = mk_mul(powers_v[i-1].get(), v_n); } for (int k = m-n+1; k > 0; ) { --k; q[k] = mk_mul(u[n+k], powers_v[k].get()); for (int j = n + k; j > 0; ) { --j; r[j] = mk_mul(v_n, r[j].get()); // n + k != j if (j >= k) { r[j] = mk_sub(r[j].get(), mk_mul(r[n+k].get(), v[j-k])); } } } DEBUG_CODE( poly u1(u); mk_mul(u1, powers_v[m-n+1].get()); SASSERT(test_quot_rem(u1, v, q, r)); ); } // validate: u = q*v + r bool test_quot_rem(poly const& u, poly const& v, poly const& q, poly const& r) { poly u1(u), q1(q); mk_mul(q1, v); mk_add(q1, r); mk_sub(q1, u); for (unsigned i = 0; i < q1.size(); ++i) { if (z() != q1[i].get()) { TRACE("nlarith", display(tout, q1);); return false; } } return true; } /** \brief create case split predicates for polynomial elimination. */ void mk_derivative(poly& p) { if(p.empty()) { return; } if (p.size() > 1) { p[0] = p[1].get(); for (unsigned i = 1; i + 1 < p.size(); ++i) { p[i] = mk_mul(num(i), p[i+1].get()); } } p.resize(p.size()-1); } void mk_derivative(unsigned k, poly& p) { for (unsigned i = 0; i < k; ++i) { mk_derivative(p); } } void mk_inf_sign(isubst& sub, util::literal_set const& literals, app_ref& fml, app_ref_vector& new_atoms) { new_atoms.reset(); expr_ref_vector equivs(m()); app_ref tmp(m()); for (unsigned i = 0; i < literals.size(); ++i) { if (literals.compare(i) == EQ) { continue; } apply_subst(sub, literals.compare(i), literals.get_poly(i), tmp); equivs.push_back(m().mk_implies(literals.literal(i), tmp)); new_atoms.push_back(tmp); } fml = mk_and(equivs.size(), equivs.c_ptr()); } void mk_plus_inf_sign(util::literal_set const& literals, app_ref& fml, app_ref_vector& new_atoms) { plus_inf_subst sub(*this); mk_inf_sign(sub, literals, fml, new_atoms); } void mk_minus_inf_sign(util::literal_set const& literals, app_ref& fml, app_ref_vector& new_atoms) { minus_inf_subst sub(*this); mk_inf_sign(sub, literals, fml, new_atoms); } // one of the literals is 0 at x_sup and x_inf, other // literals have their derivative close. void mk_bound(util::literal_set& literals, app_ref& fml, app_ref_vector& new_atoms) { new_atoms.reset(); app_ref tmp(m()); expr_ref_vector conjs(m()); mk_exists_zero(literals, true, nullptr, conjs, new_atoms); mk_same_sign (literals, true, conjs, new_atoms); mk_exists_zero(literals, false, nullptr, conjs, new_atoms); mk_same_sign (literals, false, conjs, new_atoms); mk_lt(literals.x(), literals.x_inf(), conjs, new_atoms); mk_lt(literals.x_sup(), literals.x(), conjs, new_atoms); fml = mk_and(conjs.size(), conjs.c_ptr()); } void mk_lt(app* x, app* y, expr_ref_vector& conjs, app_ref_vector& new_atoms) { app* atm = mk_lt(mk_sub(x,y)); new_atoms.push_back(atm); conjs.push_back(atm); } void mk_exists_zero(util::literal_set& literals, bool is_sup, poly const* p1, expr_ref_vector& conjs, app_ref_vector& new_atoms) { app* x = is_sup?literals.x_sup():literals.x_inf(); expr_ref_vector ors(m()); app_ref fml(m()); basic_subst sub(*this, x); for (unsigned i = 0; i < literals.size(); ++i) { if (literals.compare(i) == EQ) { continue; } apply_subst(sub, EQ, literals.get_poly(i), fml); new_atoms.push_back(fml); ors.push_back(fml); } if (p1) { apply_subst(sub, EQ, *p1, fml); new_atoms.push_back(fml); ors.push_back(fml); } conjs.push_back(mk_or(ors.size(), ors.c_ptr())); } /* z < x < y: z is sup, y is inf /\_j (p_j(x) < 0 -> p_j(z) < 0 \/ p_j(z) = 0 /\ p'_j(z) < 0) /\ /\_j (p_j(x) < 0 -> p_j(y) < 0 \/ p'_j(y) = 0 /\ -p'_j(y) < 0) */ void mk_same_sign(util::literal_set& literals, bool is_sup, expr_ref_vector& conjs, app_ref_vector& new_atoms) { app* x = is_sup?literals.x_sup():literals.x_inf(); app_ref fml(m()); for (unsigned i = 0; i < literals.size(); ++i) { switch(literals.compare(i)) { case EQ: break; case LT: mk_same_sign( x, is_sup, literals.get_poly(i), literals.literal(i), fml, new_atoms); conjs.push_back(fml); break; default: UNREACHABLE(); break; } } } void mk_same_sign(app* x, bool is_sup, poly const& p, app* l, app_ref& fml, app_ref_vector& new_atoms) { basic_subst sub0(*this, x); if (is_sup) { plus_eps_subst sub(*this, sub0); apply_subst(sub, LT, p, fml); } else { minus_eps_subst sub(*this, sub0); apply_subst(sub, LT, p, fml); } collect_atoms(fml, new_atoms); fml = m().mk_implies(l, fml); } void collect_atoms(app* fml, app_ref_vector& atoms) { ptr_vector todo; todo.push_back(fml); while (!todo.empty()) { fml = todo.back(); todo.pop_back(); if (m().is_and(fml) || m().is_or(fml)) { unsigned sz = fml->get_num_args(); for (unsigned i = 0; i < sz; ++i) { todo.push_back(to_app(fml->get_arg(i))); } } else { atoms.push_back(fml); } } } class simple_branch : public util::branch { app_ref m_cnstr; app_ref_vector m_atoms; svector m_updates; public: simple_branch(ast_manager& m, app* cnstr): m_cnstr(cnstr, m), m_atoms(m) {} ~simple_branch() override {} app* get_constraint() override { return m_cnstr.get(); } void get_updates(ptr_vector& atoms, svector& updates) override { for (unsigned i = 0; i < m_atoms.size(); ++i) { atoms.push_back(m_atoms[i].get()); updates.push_back(m_updates[i]); } } void update(app* a, util::atom_update u) { m_atoms.push_back(a); m_updates.push_back(u); } void insert(app* a) { update(a, util::INSERT); } void remove(app* a) { update(a, util::REMOVE); } }; class ins_rem_branch : public simple_branch { public: ins_rem_branch(ast_manager& m, app* a, app* r, app* cnstr): simple_branch(m, cnstr) { insert(a); remove(r); } ~ins_rem_branch() override {} }; /** \brief Compute branches given u = 0 & v = 0. u has degree m, v has degree n. m >= n 1. u = 0 & lc(v) = 0 & v' = 0 remove v = 0, add v' = 0 2. let q, r be such that, m >= n lc(v)^{m-n+1}*u = v*q + r then v = 0 & r = 0 remove u = 0, add r = 0 */ void get_sign_branches_eq(util::literal_set& lits, unsigned i, unsigned j, ptr_vector& branches) { SASSERT(lits.compare(i) == EQ); SASSERT(lits.compare(j) == EQ); poly const* u = &lits.get_poly(i); poly const* v = &lits.get_poly(j); app* l0 = lits.literal(i); app* l1 = lits.literal(j); if (u->size() < v->size()) { std::swap(u, v); std::swap(l0, l1); } app_ref lc_v0(m()), v2_eq(m()), r_eq(m()), lc(m()); poly v2(m()), q(m()), r(m()); unsigned n = v->size()-1; basic_subst sub(*this, lits.x()); unsigned power; v2.set(*v); v2.resize(n); quot_rem(*u, *v, q, r, lc_v0, power); lc_v0 = mk_eq(lc); sub.mk_eq(v2, v2_eq); sub.mk_eq(r, r_eq); branches.push_back(alloc(ins_rem_branch, m(), v2_eq, l1, mk_and(lc_v0, v2_eq))); branches.push_back(alloc(ins_rem_branch, m(), r_eq, l0, r_eq)); // TBD: add constraints that coefficients to l0 are non-zero? branches.push_back(alloc(simple_branch, m(), m().mk_not(l0))); branches.push_back(alloc(simple_branch, m(), m().mk_not(l1))); } /** \brief Compute branch where all predicates are non-zero. p_infty \/ p_minus_infty \/ mk_bound where mk_bound = z < x < y /\ (\/_j p_j(z) = 0) /\_j (p_j(x) < 0 -> p_j(z) < 0 \/ p_j(z) = 0 /\ p'_j(z) < 0) /\ (\/_j p_j(y) = 0) /\_j (p_j(x) < 0 -> p_j(y) < 0 \/ p'_j(y) = 0 /\ -p'_j(z) < 0) p_j ranges over predicates 'p_j(x) < 0' */ void get_sign_branches_neq(util::literal_set& lits, ptr_vector& branches) { app_ref_vector new_atoms(m()); app_ref fml(m()); branches.push_back(mk_inf_branch(lits, true)); branches.push_back(mk_inf_branch(lits, false)); mk_bound(lits, fml, new_atoms); simple_branch* br = alloc(simple_branch, m(), fml); swap_atoms(br, lits.lits(), new_atoms); branches.push_back(br); } util::branch* mk_inf_branch(util::literal_set& literals, bool is_pos) { app_ref fml(m()); app_ref_vector new_atoms(m()); if (is_pos) { mk_plus_inf_sign(literals, fml, new_atoms); } else { mk_minus_inf_sign(literals, fml, new_atoms); } simple_branch* br = alloc(simple_branch, m(), fml); swap_atoms(br, literals.lits(), new_atoms); return br; } void swap_atoms(simple_branch* br, app_ref_vector const& old_atoms, app_ref_vector const& new_atoms) { for (unsigned i = 0; i < old_atoms.size(); ++i) { br->remove(old_atoms[i]); } for (unsigned i = 0; i < new_atoms.size(); ++i) { br->insert(new_atoms[i]); } } /** \brief Compute branches where one equality holds. p != 0 \/ lc(p) = 0 \/ p' = 0 \/ p_j(x) < 0 -> p_j(infty) < 0 \/ p_j(x) < 0 -> p_j(-infty) < 0 \/ p(z) < 0 < p(y) /\ p'(x) > 0 /\ m_bound(-p') \/ p(y) < 0 < p(z) /\ p'(x) < 0 /\ m_bound(p') where mk_bound(q) = z < x < y /\ /\_j p_j(x) < 0 -> r_j(x) < 0 (\/_j r_j(z) = 0) /\_j (p_j(x) < 0 -> r_j(x) < 0 /\ r_j(z-epsilon) < 0 (\/_j r_j(y) = 0) /\_j (p_j(x) < 0 -> r_j(x) < 0 /\ r_j(y+epsilon) < 0 p_j ranges over predicates 'p_j(x) < 0', q(x) < 0 r_j is obtained by quot_rem(p, p_j, q_j, r_j) z < x < y: z is sup, y is inf */ void get_sign_branches_eq_neq(util::literal_set& lits, unsigned i, ptr_vector& branches) { SASSERT(lits.size() > i); SASSERT(lits.compare(i) == EQ); poly const& p = lits.get_poly(i); poly p1(m()); mk_differentiate(p, p1); app_ref eq(m()), lc_p0(m()), l1(m()); basic_subst sub_x(*this, lits.x()); apply_subst(sub_x, EQ, p1, eq); lc_p0 = mk_eq(p[p.size()-1]); poly p2(p); p2.resize(p.size()-1); apply_subst(sub_x, EQ, p2, l1); branches.push_back(alloc(simple_branch, m(), m().mk_not(lits.literal(i)))); branches.push_back(alloc(simple_branch, m(), eq)); branches.push_back(alloc(ins_rem_branch, m(), l1, lits.literal(i), lc_p0)); branches.push_back(mk_inf_branch(lits, true)); branches.push_back(mk_inf_branch(lits, false)); branches.push_back(mk_bound_ext(lits, p, p1, lits.x())); } simple_branch* mk_bound_ext(util::literal_set& lits, poly const& p, poly const& p1, app* x) { // // Assuming p(x) = 0, p'(x) != 0, lc(p) != 0 // x < y < z // (p'(x) < 0 & p(y) < 0 < p(z) | p'(x) > 0 & p(y) > 0 > p(z)) // \/ p'(y) = 0 \/_j p_j(y) = 0 // \/ p'(z) = 0 \/_j p_j(z) = 0 // /\_j p_j(x) < 0 -> sign_adjust(lc, parity, r_j(y+epsilon)) // /\_j p_j(x) < 0 -> sign_adjust(lc, parity, r_j(z-epsilon)) // p'(x) < 0 -> r(y+epsilon) < 0 & r(z-epsilon) < 0 // p'(x) > 0 -> r(y+epsilon) > 0 & r(z-epsilon) > 0 // sign_adjust(lc, even, r) = r < 0 // sign_adjust(lc, odd, r) = (lc > 0 -> r < 0) & (lc < 0 -> r > 0) // app_ref eq(m()), fml(m()), l1(m()), l2(m()), l3(m()); app_ref p1_lt0(m()), p1_gt0(m()); app_ref_vector new_atoms(m()); expr_ref_vector conjs(m()); poly p_m(p), p1_m(p1); mk_uminus(p_m); mk_uminus(p1_m); mk_lt(lits.x(), lits.x_inf(), conjs, new_atoms); // y < x < z mk_lt(lits.x_sup(), lits.x(), conjs, new_atoms); basic_subst sub_x(*this, x); basic_subst sub_y(*this, lits.x_sup()); basic_subst sub_z(*this, lits.x_inf()); apply_subst(sub_y, LT, p, l1); // p(y) < 0 apply_subst(sub_z, LT, p_m,l2); // 0 < p(z) apply_subst(sub_x, LT, p1_m, p1_gt0); // p1(x) > 0 new_atoms.push_back(l1); new_atoms.push_back(l2); new_atoms.push_back(p1_gt0); conjs.push_back(m().mk_implies(p1_gt0, mk_and(l1, l2))); // p'(x) > 0 -> p(y) < 0 < p(z) apply_subst(sub_y, LT, p_m, l1); // p(y) > 0 apply_subst(sub_z, LT, p, l2); // 0 > p(z) apply_subst(sub_x, LT, p1, p1_lt0); // p1(x) < 0 new_atoms.push_back(l1); new_atoms.push_back(l2); new_atoms.push_back(p1_lt0); conjs.push_back(m().mk_implies(p1_lt0, mk_and(l1, l2))); // p'(x) < 0 -> p(y) > 0 > p(z) conjs.push_back(fml); mk_exists_zero(lits, true, &p1, conjs, new_atoms); // p'(z) = 0 \/_j p_j(z) = 0 mk_exists_zero(lits, false, &p1, conjs, new_atoms); // p'(y) = 0 \/_j p_j(y) = 0 for (unsigned i = 0; i < lits.size(); ++i) { if (lits.compare(i) == LT) { mk_bound_ext(lits.literal(i), lits.get_poly(i), p, lits.x_sup(), lits.x_inf(), conjs, new_atoms); } } // p'(x) < 0 -> r(y+epsilon) < 0 & r(z-epsilon) < 0 // p'(x) > 0 -> r(y+epsilon) > 0 & r(z-epsilon) > 0 mk_bound_ext(p1_lt0, p1, p, lits.x_sup(), lits.x_inf(), conjs, new_atoms); mk_bound_ext(p1_gt0, p1_m, p, lits.x_sup(), lits.x_inf(), conjs, new_atoms); fml = mk_and(conjs.size(), conjs.c_ptr()); simple_branch* br = alloc(simple_branch, m(), fml); swap_atoms(br, lits.lits(), new_atoms); return br; } void mk_bound_ext(app* l_j, poly const& p_j, poly const& p, app* y, app* z, expr_ref_vector& conjs, app_ref_vector& new_atoms) { poly q(m()), r(m()); app_ref eq(m()), fml(m()), l1(m()), l2(m()), l3(m()), l4(m()); // lc(p)^{m-n+1}*p_i = p*q + r app_ref lc(m()), lc_m(m()); basic_subst sub_y(*this, y); basic_subst sub_z(*this, z); unsigned power; quot_rem(p_j, p, q, r, lc, power); poly r_m(r); mk_uminus(r_m); lc_m = mk_uminus(lc); plus_eps_subst sub_ye(*this, sub_y); minus_eps_subst sub_ze(*this, sub_z); // p_j(x) < 0 -> sign_adjust(lc, parity, r_j(y+epsilon)) // p_j(x) < 0 -> sign_adjust(lc, parity, r_j(z-epsilon)) if ((power % 2) == 0) { apply_subst(sub_ye, LT, r, l1); apply_subst(sub_ze, LT, r, l2); fml = mk_and(l1, l2); } else { apply_subst(sub_ye, LT, r, l1); apply_subst(sub_ye, LT, r_m, l2); l1 = m().mk_implies(mk_lt(lc_m), l1); l2 = m().mk_implies(mk_lt(lc), l2); apply_subst(sub_ze, LT, r_m, l3); apply_subst(sub_ze, LT, r_m, l4); l3 = m().mk_implies(mk_lt(lc_m), l3); l4 = m().mk_implies(mk_lt(lc), l4); expr* args[4] = { l1, l2, l3, l4 }; fml = mk_and(4, args); } collect_atoms(fml, new_atoms); fml = m().mk_implies(l_j, fml); conjs.push_back(fml); } public: /** \brief Generate branch formulas depending on the current evaluation of literals. There are 3 cases: 1. Two or more equalities are true 2. Precisely one equality is true 3. No equality is true */ void get_sign_branches(util::literal_set& lits, util::eval& eval, ptr_vector& branches) { m_trail.reset(); unsigned z1 = UINT_MAX, z2 = UINT_MAX; for (unsigned i = 0; i < lits.size(); ++i) { if (lits.compare(i) == EQ && l_true == eval(lits.literal(i))) { if (z1 == UINT_MAX) { z1 = i; } else { SASSERT(z2 == UINT_MAX); z2 = i; break; } } } if (z1 == UINT_MAX) { get_sign_branches_neq(lits, branches); } else if (z2 == UINT_MAX) { get_sign_branches_eq_neq(lits, z1, branches); } else { get_sign_branches_eq(lits, z1, z2, branches); } } bool get_sign_literals(util::atoms const& atoms, util::eval& eval, util::literal_set*& lits) { // TBD: use 'eval' to select non-linear literals that are relevant. m_trail.reset(); ptr_vector nlvars, atms; util::atoms::iterator it = atoms.begin(), end = atoms.end(); for (; it != end; ++it) { atms.push_back(*it); } extract_non_linear(atms.size(), atms.begin(), nlvars); if (nlvars.empty()) { lits = nullptr; return true; } app* x = nlvars.back(); contains_app contains_x(m(), x); expr* const* _atoms = (expr*const*)atms.begin(); lits = alloc(util::literal_set, m()); lits->set_x(x); if (get_polys(contains_x, atms.size(), _atoms, lits->polys(), lits->comps(), nullptr, &lits->lits())) { return true; } dealloc(lits); lits = nullptr; return false; } // Sign matrix algorithm (Cohen-Hormander) public: enum sign { Negative, Zero, NonZero, Positive, Unknown }; typedef svector sign_vector; typedef vector sign_matrix; void mk_sign_matrix(vector const& polys, sign_matrix& result) { } private: // remove points that don't contain Zero void condense(sign_matrix& mat) { unsigned i = 0, j = 0; SASSERT(mat.size() % 2 == 0); for (; i + 1 < mat.size(); i += 2) { if (mat[i+1].contains(Zero)) { if (i != j) { mat[j] = mat[i]; mat[j+1] = mat[i+1]; } j += 2; } } mat.resize(j); } // set sign of p(x) to sign of q_i(x) where p_i(x) = 0 void infer_psign(sign_vector& pqs) { unsigned n = pqs.size()/2; for (unsigned i = 0; i < n; ++i) { if (Zero == pqs[i]) { sign s = pqs[i+n]; pqs.resize(n); cons(s, pqs); return; } } pqs.resize(n); cons(Unknown, pqs); } void cons(sign s, sign_vector& v) { for (unsigned i = 0; i < v.size(); ++i) { std::swap(s, v[i]); } v.push_back(s); } // Deduce matrix for p, p1, .., pn from p', p1, .., pn, q0, .., qn void deduce_matrix(sign_matrix& m) { for (unsigned i = 0; i < m.size(); ++i) { infer_psign(m[i]); } condense(m); } }; util::util(ast_manager& m) { m_imp = alloc(imp, m); } util::~util() { dealloc(m_imp); } bool util::create_branches(app* x, unsigned num_lits, expr* const* lits, branch_conditions& bc) { return m_imp->create_branches(x, num_lits, lits, bc); } void util::set_enable_linear(bool enable_linear) { m_imp->set_enable_linear(enable_linear); } void util::extract_non_linear(expr* e, ptr_vector& nl_vars) { m_imp->extract_non_linear(e, nl_vars); } void util::deallocate(literal_set* lits) { dealloc(lits); } bool util::get_sign_literals(atoms const& atoms, eval& ev, literal_set*& lits) { return m_imp->get_sign_literals(atoms, ev, lits); } void util::get_sign_branches(literal_set& lits, eval& ev, ptr_vector& branches) { m_imp->get_sign_branches(lits, ev, branches); } }; z3-z3-4.8.7/src/qe/nlarith_util.h000066400000000000000000000113551356505360400164770ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: nlarith_util.h Abstract: Utilities for nln-linear arithmetic quantifier elimination and solving. Author: Nikolaj (nbjorner) 2011-05-13 Notes: --*/ #ifndef NLARITH_UTIL_H_ #define NLARITH_UTIL_H_ #include "ast/ast.h" #include "util/lbool.h" namespace nlarith { /** \brief A summary for branch side conditions and substitutions. Each branch in a split comprises of: - preds - a sequence of predicates used for the branching. - branches - a sequence of branch side conditions - subst - a sequence of substitutions that replace 'preds' by formulas not containing the eliminated variable - constraints - a sequence of side constraints to add to the main formula. */ class branch_conditions { expr_ref_vector m_branches; expr_ref_vector m_preds; vector m_subst; expr_ref_vector m_constraints; expr_ref_vector m_defs; expr_ref_vector m_a; expr_ref_vector m_b; expr_ref_vector m_c; public: branch_conditions(ast_manager& m) : m_branches(m), m_preds(m), m_constraints(m), m_defs(m), m_a(m), m_b(m), m_c(m) {} void add_pred(expr* p) { m_preds.push_back(p); } void add_branch(expr* branch, expr* cond, expr_ref_vector const& subst, expr* def, expr* a, expr* b, expr* c) { m_branches.push_back(branch); m_constraints.push_back(cond); m_subst.push_back(subst); m_defs.push_back(def); m_a.push_back(a); m_b.push_back(b); m_c.push_back(c); } expr* preds(unsigned i) const { return m_preds[i]; } expr* branches(unsigned i) const { return m_branches[i]; } expr* constraints(unsigned i) const { return m_constraints[i]; } expr* def(unsigned i) const { return m_defs[i]; } expr* a(unsigned i) const { return m_a[i]; } expr* b(unsigned i) const { return m_b[i]; } expr* c(unsigned i) const { return m_c[i]; } expr_ref_vector const& subst(unsigned i) const { return m_subst[i]; } expr_ref_vector const& branches() const { return m_branches; } expr_ref_vector const& preds() const { return m_preds; } vector const& subst() const { return m_subst; } expr_ref_vector const& constraints() const { return m_constraints; } void reset() { m_branches.reset(); m_preds.reset(); m_subst.reset(); m_constraints.reset(); m_defs.reset(); m_a.reset(); m_b.reset(); m_c.reset(); } unsigned size() const { return branches().size(); } unsigned num_preds() const { return preds().size(); } }; class util { class imp; imp* m_imp; public: util(ast_manager& m); ~util(); /** \brief Enable handling of linear variables. */ void set_enable_linear(bool enable_linear); /** \brief Create branches for non-linear variable x. */ bool create_branches(app* x, unsigned nl, expr* const* lits, branch_conditions& bc); /** \brief Extract non-linear variables from ground formula. \requires a ground formula. */ void extract_non_linear(expr* e, ptr_vector& nl_vars); /** \brief literal sets. Opaque state. */ class literal_set; static void deallocate(literal_set* lits); /** \brief Sign-based branching. v2. */ typedef obj_hashtable atoms; class eval { public: virtual ~eval() {} virtual lbool operator()(app* a) = 0; }; enum atom_update { INSERT, REMOVE }; class branch { public: virtual ~branch() {} virtual app* get_constraint() = 0; virtual void get_updates(ptr_vector& atoms, svector& updates) = 0; }; /** \brief select literals that contain non-linear variables. */ bool get_sign_literals(atoms const& atoms, eval& eval, literal_set*& lits); /** \brief given selected literals, generate branch conditions. */ void get_sign_branches(literal_set& lits, eval& eval, ptr_vector& branches); }; }; #endif z3-z3-4.8.7/src/qe/nlqsat.cpp000066400000000000000000001000731356505360400156320ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: nlqsat.cpp Abstract: Quantifier Satisfiability Solver for nlsat Author: Nikolaj Bjorner (nbjorner) 2015-10-17 Revision History: --*/ #include "util/uint_set.h" #include "util/scoped_ptr_vector.h" #include "ast/expr2var.h" #include "ast/ast_util.h" #include "ast/rewriter/expr_safe_replace.h" #include "ast/ast_pp.h" #include "ast/for_each_expr.h" #include "ast/rewriter/rewriter.h" #include "ast/rewriter/th_rewriter.h" #include "ast/rewriter/rewriter_def.h" #include "ast/rewriter/quant_hoist.h" #include "qe/nlqsat.h" #include "qe/qsat.h" #include "nlsat/nlsat_solver.h" #include "nlsat/nlsat_explain.h" #include "nlsat/nlsat_assignment.h" #include "nlsat/tactic/goal2nlsat.h" #include "tactic/core/tseitin_cnf_tactic.h" namespace qe { enum qsat_mode_t { qsat_t, elim_t }; class nlqsat : public tactic { typedef unsigned_vector assumption_vector; typedef nlsat::scoped_literal_vector clause; struct stats { unsigned m_num_rounds; stats() { reset(); } void reset() { memset(this, 0, sizeof(*this)); } }; struct solver_state { ast_manager& m; params_ref m_params; nlsat::solver m_solver; nlsat::literal m_is_true; nlsat::assignment m_rmodel; svector m_bmodel; nlsat::assignment m_rmodel0; svector m_bmodel0; bool m_valid_model; vector m_bound_rvars; vector > m_bound_bvars; scoped_ptr_vector m_preds; svector m_rvar2level; u_map m_bvar2level; expr2var m_a2b, m_t2x; u_map m_b2a, m_x2t; nlsat::literal_vector m_assumptions; nlsat::literal_vector m_asms; nlsat::literal_vector m_cached_asms; unsigned_vector m_cached_asms_lim; u_map m_asm2fml; solver_state(ast_manager& m, params_ref const& p): m(m), m_params(p), m_solver(m.limit(), p, true), m_rmodel(m_solver.am()), m_rmodel0(m_solver.am()), m_valid_model(false), m_a2b(m), m_t2x(m) {} void g2s(goal const& g) { goal2nlsat gs; gs(g, m_params, m_solver, m_a2b, m_t2x); } void init_expr2var(vector const& qvars) { for (app_ref_vector const& qvs : qvars) { init_expr2var(qvs); } } void init_expr2var(app_ref_vector const& qvars) { for (app* v : qvars) { if (m.is_bool(v)) { nlsat::bool_var b = m_solver.mk_bool_var(); m_solver.inc_ref(b); m_a2b.insert(v, b); } else { // TODO: assert it is of type Real. m_t2x.insert(v, m_solver.mk_var(false)); } } } void init_var2expr() { for (auto const& kv : m_t2x) { m_x2t.insert(kv.m_value, kv.m_key); } for (auto const& kv : m_a2b) { m_b2a.insert(kv.m_value, kv.m_key); } } void save_model(bool is_exists) { svector bvars; for (auto const& kv : m_bvar2level) { bvars.push_back(kv.m_key); } m_solver.get_rvalues(m_rmodel); m_solver.get_bvalues(bvars, m_bmodel); m_valid_model = true; if (is_exists) { m_rmodel0.copy(m_rmodel); m_bmodel0.reset(); m_bmodel0.append(m_bmodel); } } void unsave_model() { SASSERT(m_valid_model); m_solver.set_rvalues(m_rmodel); m_solver.set_bvalues(m_bmodel); } void clear_model() { m_valid_model = false; m_rmodel.reset(); m_bmodel.reset(); m_solver.set_rvalues(m_rmodel); } void add_assumption_literal(clause& clause, expr* fml) { nlsat::bool_var b = m_solver.mk_bool_var(); clause.push_back(nlsat::literal(b, true)); m_assumptions.push_back(nlsat::literal(b, false)); m_solver.inc_ref(b); m_asm2fml.insert(b, fml); m_bvar2level.insert(b, max_level()); } expr_ref clause2fml(nlsat::scoped_literal_vector const& clause) { expr_ref_vector fmls(m); expr_ref fml(m); expr* t; nlsat2goal n2g(m); for (nlsat::literal l : clause) { if (m_asm2fml.find(l.var(), t)) { fml = t; if (l.sign()) { fml = push_not(fml); } SASSERT(l.sign()); fmls.push_back(fml); } else { fmls.push_back(n2g(m_solver, m_b2a, m_x2t, l)); } } fml = mk_or(fmls); return fml; } void add_literal(nlsat::literal_vector& lits, nlsat::literal l) { lbool r = m_solver.value(l); switch (r) { case l_true: lits.push_back(l); break; case l_false: lits.push_back(~l); break; default: lits.push_back(l); break; } } void display(std::ostream& out) { out << "level " << level() << "\n"; display_preds(out); display_assumptions(out); m_solver.display(out << "solver:\n"); } void display_assumptions(std::ostream& out) { m_solver.display(out << "assumptions: ", m_asms.size(), m_asms.c_ptr()); out << "\n"; } void display_preds(std::ostream& out) { for (unsigned i = 0; i < m_preds.size(); ++i) { m_solver.display(out << i << ": ", m_preds[i]->size(), m_preds[i]->c_ptr()); out << "\n"; } } unsigned level() const { return m_cached_asms_lim.size(); } void reset() { m_asms.reset(); m_cached_asms.reset(); m_cached_asms_lim.reset(); m_is_true = nlsat::null_literal; m_rmodel.reset(); m_valid_model = false; m_bound_rvars.reset(); m_bound_bvars.reset(); m_preds.reset(); for (auto const& kv : m_bvar2level) { m_solver.dec_ref(kv.m_key); } m_rvar2level.reset(); m_bvar2level.reset(); m_t2x.reset(); m_a2b.reset(); m_b2a.reset(); m_x2t.reset(); m_assumptions.reset(); m_asm2fml.reset(); } }; ast_manager& m; solver_state s; qsat_mode_t m_mode; params_ref m_params; tactic_ref m_nftactic; stats m_stats; statistics m_st; obj_hashtable m_free_vars; obj_hashtable m_aux_vars; expr_ref_vector m_answer; expr_safe_replace m_answer_simplify; expr_ref_vector m_trail; lbool check_sat() { while (true) { ++m_stats.m_num_rounds; check_cancel(); init_assumptions(); lbool res = s.m_solver.check(s.m_asms); TRACE("qe", s.display(tout << res << "\n"); ); switch (res) { case l_true: s.save_model(is_exists(level())); push(); break; case l_false: if (0 == level()) return l_false; if (1 == level() && m_mode == qsat_t) return l_true; project(); break; case l_undef: return res; } } return l_undef; } void init_assumptions() { unsigned lvl = level(); s.m_asms.reset(); s.m_asms.push_back(is_exists()?s.m_is_true:~s.m_is_true); s.m_asms.append(s.m_assumptions); TRACE("qe", tout << "model valid: " << s.m_valid_model << " level: " << lvl << " "; s.display_assumptions(tout); s.m_solver.display(tout);); if (!s.m_valid_model) { s.m_asms.append(s.m_cached_asms); return; } s.unsave_model(); if (lvl == 0) { SASSERT(s.m_cached_asms.empty()); return; } if (lvl <= s.m_preds.size()) { for (unsigned j = 0; j < s.m_preds[lvl - 1]->size(); ++j) { s.add_literal(s.m_cached_asms, (*s.m_preds[lvl - 1])[j]); } } s.m_asms.append(s.m_cached_asms); for (unsigned i = lvl + 1; i < s.m_preds.size(); i += 2) { for (unsigned j = 0; j < s.m_preds[i]->size(); ++j) { nlsat::literal l = (*s.m_preds[i])[j]; max_level lv = s.m_bvar2level.find(l.var()); bool use = (lv.m_fa == i && (lv.m_ex == UINT_MAX || lv.m_ex < lvl)) || (lv.m_ex == i && (lv.m_fa == UINT_MAX || lv.m_fa < lvl)); if (use) { s.add_literal(s.m_asms, l); } } } TRACE("qe", s.display(tout); tout << "assumptions\n"; for (nlsat::literal a : s.m_asms) { s.m_solver.display(tout, a) << "\n"; }); s.save_model(is_exists(level())); } template void insert_set(S& set, T const& vec) { for (auto const& v : vec) { set.insert(v); } } void mbp(unsigned level, nlsat::scoped_literal_vector& result) { nlsat::var_vector vars; uint_set fvars; extract_vars(level, vars, fvars); mbp(vars, fvars, result); } void extract_vars(unsigned level, nlsat::var_vector& vars, uint_set& fvars) { for (unsigned i = 0; i < s.m_bound_rvars.size(); ++i) { if (i < level) { insert_set(fvars, s.m_bound_bvars[i]); } else { vars.append(s.m_bound_rvars[i]); } } } void display_project(std::ostream& out, nlsat::var v, nlsat::scoped_literal_vector const& r1, nlsat::scoped_literal_vector const& r2) { for (auto const& kv : s.m_x2t) { out << "(declare-const x" << kv.m_key << " Real)\n"; } s.m_solver.display(out << "(assert (not (exists ((", v) << " Real)) \n"; s.m_solver.display_smt2(out << "(and ", r1.size(), r1.c_ptr()) << "))))\n"; s.m_solver.display_smt2(out << "(assert (and ", r2.size(), r2.c_ptr()); out << "))\n"; out << "(check-sat)\n(reset)\n"; } void mbp(nlsat::var_vector const& vars, uint_set const& fvars, clause& result) { // // Also project auxiliary variables from clausification. // s.unsave_model(); nlsat::explain& ex = s.m_solver.get_explain(); nlsat::scoped_literal_vector new_result(s.m_solver); result.reset(); // project quantified Boolean variables. for (nlsat::literal lit : s.m_asms) { if (!s.m_b2a.contains(lit.var()) || fvars.contains(lit.var())) { result.push_back(lit); } } TRACE("qe", s.m_solver.display(tout, result.size(), result.c_ptr()); tout << "\n";); // project quantified real variables. // They are sorted by size, so we project the largest variables first to avoid // renaming variables. for (unsigned i = vars.size(); i-- > 0;) { new_result.reset(); ex.project(vars[i], result.size(), result.c_ptr(), new_result); TRACE("qe", display_project(tout, vars[i], result, new_result);); TRACE("qe", display_project(std::cout, vars[i], result, new_result);); result.swap(new_result); } negate_clause(result); } void negate_clause(clause& result) { for (unsigned i = 0; i < result.size(); ++i) { result.set(i, ~result[i]); } } unsigned level() const { return s.level(); } void enforce_parity(clause& cl) { cl.push_back(is_exists()?~s.m_is_true:s.m_is_true); } void add_clause(clause& cl) { if (cl.empty()) { cl.push_back(~s.m_solver.mk_true()); } SASSERT(!cl.empty()); nlsat::literal_vector lits(cl.size(), cl.c_ptr()); s.m_solver.mk_clause(lits.size(), lits.c_ptr()); } max_level get_level(clause const& cl) { return get_level(cl.size(), cl.c_ptr()); } max_level get_level(unsigned n, nlsat::literal const* ls) { max_level level; for (unsigned i = 0; i < n; ++i) { level.merge(get_level(ls[i])); } return level; } max_level get_level(nlsat::literal l) { max_level level; if (s.m_bvar2level.find(l.var(), level)) { return level; } nlsat::var_vector vs; s.m_solver.vars(l, vs); TRACE("qe", s.m_solver.display(tout << vs << " ", l) << "\n";); for (unsigned v : vs) { level.merge(s.m_rvar2level[v]); } set_level(l.var(), level); return level; } void set_level(nlsat::bool_var v, max_level const& level) { unsigned k = level.max(); while (s.m_preds.size() <= k) { s.m_preds.push_back(alloc(nlsat::scoped_literal_vector, s.m_solver)); } nlsat::literal l(v, false); s.m_preds[k]->push_back(l); s.m_solver.inc_ref(v); s.m_bvar2level.insert(v, level); TRACE("qe", s.m_solver.display(tout, l); tout << ": " << level << "\n";); } void project() { TRACE("qe", s.display_assumptions(tout);); if (!s.m_valid_model) { pop(1); return; } if (m_mode == elim_t) { project_qe(); return; } SASSERT(level() >= 2); unsigned num_scopes; clause cl(s.m_solver); mbp(level()-1, cl); max_level clevel = get_level(cl); enforce_parity(cl); add_clause(cl); if (clevel.max() == UINT_MAX) { num_scopes = 2*(level()/2); } else { SASSERT(clevel.max() + 2 <= level()); num_scopes = level() - clevel.max(); SASSERT(num_scopes >= 2); } TRACE("qe", tout << "backtrack: " << num_scopes << "\n";); pop(num_scopes); } void project_qe() { SASSERT(level() >= 1 && m_mode == elim_t && s.m_valid_model); clause cl(s.m_solver); mbp(std::max(1u, level()-1), cl); expr_ref fml = s.clause2fml(cl); TRACE("qe", tout << level() << ": " << fml << "\n";); max_level clevel = get_level(cl); if (level() == 1 || clevel.max() == 0) { add_assumption_literal(cl, fml); } else { enforce_parity(cl); } add_clause(cl); if (level() == 1) { // is_forall() && clevel.max() == 0 add_to_answer(fml); } if (level() == 1) { pop(1); } else { pop(2); } } void add_to_answer(expr_ref& fml) { m_answer_simplify(fml); expr* e; if (m.is_not(fml, e)) { m_answer_simplify.insert(e, m.mk_false()); } else { m_answer_simplify.insert(fml, m.mk_true()); } m_answer.push_back(fml); } void add_assumption_literal(clause& clause, expr* fml) { s.add_assumption_literal(clause, fml); m_trail.push_back(fml); } bool is_exists() const { return is_exists(level()); } bool is_forall() const { return is_forall(level()); } bool is_exists(unsigned level) const { return (level % 2) == 0; } bool is_forall(unsigned level) const { return is_exists(level+1); } void check_cancel() { } struct div { expr_ref num, den; app_ref name; div(ast_manager& m, expr* n, expr* d, app* nm): num(n, m), den(d, m), name(nm, m) {} }; class div_rewriter_cfg : public default_rewriter_cfg { ast_manager& m; arith_util a; expr_ref m_zero; vector
m_divs; public: div_rewriter_cfg(nlqsat& s): m(s.m), a(s.m), m_zero(a.mk_real(0), m) {} ~div_rewriter_cfg() {} br_status reduce_app(func_decl* f, unsigned sz, expr* const* args, expr_ref& result, proof_ref& pr) { rational r(1); if (is_decl_of(f, a.get_family_id(), OP_DIV) && sz == 2 && (!a.is_numeral(args[1], r) || r.is_zero()) && is_ground(args[0]) && is_ground(args[1])) { result = m.mk_fresh_const("div", a.mk_real()); m_divs.push_back(div(m, args[0], args[1], to_app(result))); return BR_DONE; } return BR_FAILED; } vector
const& divs() const { return m_divs; } }; //template class rewriter_tpl; class div_rewriter_star : public rewriter_tpl { div_rewriter_cfg m_cfg; public: div_rewriter_star(nlqsat& s): rewriter_tpl(s.m, false, m_cfg), m_cfg(s) {} vector
const& divs() const { return m_cfg.divs(); } }; class is_pure_proc { nlqsat& s; arith_util a; bool m_has_divs; public: is_pure_proc(nlqsat& s): s(s), a(s.m), m_has_divs(false) {} void operator()(::var * n) { if (!a.is_real(n) && !s.m.is_bool(n)) { throw tactic_exception("not NRA"); } } void operator()(app * n) { if (n->get_family_id() == s.m.get_basic_family_id()) { return; } if (is_uninterp_const(n) && (a.is_real(n) || s.m.is_bool(n))) { return; } if (a.is_mul(n) || a.is_add(n) || a.is_sub(n) || a.is_uminus(n) || a.is_numeral(n) || a.is_le(n) || a.is_ge(n) || a.is_lt(n) || a.is_gt(n)) { return; } expr* n1, *n2; rational r; if (a.is_div(n, n1, n2) && a.is_numeral(n2, r) && !r.is_zero()) { return; } if (a.is_power(n, n1, n2) && a.is_numeral(n2, r) && r.is_unsigned()) { return; } if (a.is_div(n) && s.m_mode == qsat_t && is_ground(n)) { m_has_divs = true; return; } TRACE("qe", tout << "not NRA: " << mk_pp(n, s.m) << "\n";); throw tactic_exception("not NRA"); } void operator()(quantifier * n) {} bool has_divs() const { return m_has_divs; } }; /* Ackermanize division For each p/q: q != 0 => div_pq*q = p For each p/q, p'/q' p = p', q = q' => div_pq = div_pq' */ void ackermanize_div(expr_ref& fml, expr_ref_vector& paxioms) { is_pure_proc is_pure(*this); { expr_fast_mark1 visited; quick_for_each_expr(is_pure, visited, fml); } if (is_pure.has_divs()) { arith_util arith(m); proof_ref pr(m); div_rewriter_star rw(*this); rw(fml, fml, pr); vector
const& divs = rw.divs(); for (unsigned i = 0; i < divs.size(); ++i) { expr_ref den_is0(m.mk_eq(divs[i].den, arith.mk_real(0)), m); paxioms.push_back(m.mk_or(den_is0, m.mk_eq(divs[i].num, arith.mk_mul(divs[i].den, divs[i].name)))); for (unsigned j = i + 1; j < divs.size(); ++j) { paxioms.push_back(m.mk_or(m.mk_not(m.mk_eq(divs[i].den, divs[j].den)), m.mk_not(m.mk_eq(divs[i].num, divs[j].num)), m.mk_eq(divs[i].name, divs[j].name))); } } } } void reset() override { s.reset(); m_st.reset(); s.m_solver.collect_statistics(m_st); m_free_vars.reset(); m_aux_vars.reset(); m_answer.reset(); m_answer_simplify.reset(); m_trail.reset(); } void push() { s.m_cached_asms_lim.push_back(s.m_cached_asms.size()); } void pop(unsigned num_scopes) { s.clear_model(); unsigned new_level = level() - num_scopes; s.m_cached_asms.shrink(s.m_cached_asms_lim[new_level]); s.m_cached_asms_lim.shrink(new_level); } // expr -> nlsat::solver bool hoist(expr_ref& fml) { expr_ref_vector paxioms(m); ackermanize_div(fml, paxioms); quantifier_hoister hoist(m); vector qvars; app_ref_vector vars(m); bool is_forall = false; pred_abs abs(m); expr_ref fml_a(m.mk_and(fml, mk_and(paxioms)), m); abs.get_free_vars(fml_a, vars); insert_set(m_free_vars, vars); qvars.push_back(vars); vars.reset(); if (m_mode == elim_t) { is_forall = true; hoist.pull_quantifier(is_forall, fml, vars); qvars.push_back(vars); } else { hoist.pull_quantifier(is_forall, fml, vars); qvars.back().append(vars); } do { is_forall = !is_forall; vars.reset(); hoist.pull_quantifier(is_forall, fml, vars); qvars.push_back(vars); } while (!vars.empty()); SASSERT(qvars.size() >= 2); SASSERT(qvars.back().empty()); s.init_expr2var(qvars); expr_ref is_true(m), fml1(m), fml2(m); is_true = m.mk_fresh_const("is_true", m.mk_bool_sort()); fml = m.mk_iff(is_true, fml); goal_ref g = alloc(goal, m); g->assert_expr(fml); for (expr* f : paxioms) { g->assert_expr(f); } expr_dependency_ref core(m); goal_ref_buffer result; (*m_nftactic)(g, result); SASSERT(result.size() == 1); TRACE("qe", result[0]->display(tout);); s.g2s(*result[0]); // insert variables and their levels. for (unsigned i = 0; i < qvars.size(); ++i) { s.m_bound_bvars.push_back(svector()); s.m_bound_rvars.push_back(nlsat::var_vector()); max_level lvl; if (is_exists(i)) lvl.m_ex = i; else lvl.m_fa = i; for (app* v : qvars[i]) { if (s.m_a2b.is_var(v)) { SASSERT(m.is_bool(v)); nlsat::bool_var b = s.m_a2b.to_var(v); TRACE("qe", tout << mk_pp(v, m) << " |-> b" << b << "\n";); s.m_bound_bvars.back().push_back(b); set_level(b, lvl); } else if (s.m_t2x.is_var(v)) { nlsat::var w = s.m_t2x.to_var(v); TRACE("qe", tout << mk_pp(v, m) << " |-> x" << w << "\n";); s.m_bound_rvars.back().push_back(w); s.m_rvar2level.setx(w, lvl, max_level()); } else { TRACE("qe", tout << mk_pp(v, m) << " not found\n";); } } } s.init_var2expr(); s.m_is_true = nlsat::literal(s.m_a2b.to_var(is_true), false); // insert literals from arithmetical sub-formulas nlsat::atom_vector const& atoms = s.m_solver.get_atoms(); TRACE("qe", s.m_solver.display(tout);); for (unsigned i = 0; i < atoms.size(); ++i) { if (atoms[i]) { get_level(nlsat::literal(i, false)); } } TRACE("qe", tout << fml << "\n";); return true; } // Return false if nlsat assigned noninteger value to an integer variable. // [copied from nlsat_tactic.cpp] bool mk_model(model_converter_ref & mc) { bool ok = true; model_ref md = alloc(model, m); arith_util util(m); for (auto const& kv : s.m_t2x) { nlsat::var x = kv.m_value; expr * t = kv.m_key; if (!is_uninterp_const(t) || !m_free_vars.contains(t) || m_aux_vars.contains(t)) continue; expr * v; try { v = util.mk_numeral(s.m_rmodel0.value(x), util.is_int(t)); } catch (z3_error & ex) { throw ex; } catch (z3_exception &) { v = util.mk_to_int(util.mk_numeral(s.m_rmodel0.value(x), false)); ok = false; } md->register_decl(to_app(t)->get_decl(), v); } for (auto const& kv : s.m_a2b) { expr * a = kv.m_key; nlsat::bool_var b = kv.m_value; if (a == nullptr || !is_uninterp_const(a) || b == s.m_is_true.var() || !m_free_vars.contains(a) || m_aux_vars.contains(a)) continue; lbool val = s.m_bmodel0.get(b, l_undef); if (val == l_undef) continue; // don't care md->register_decl(to_app(a)->get_decl(), val == l_true ? m.mk_true() : m.mk_false()); } mc = model2model_converter(md.get()); return ok; } public: nlqsat(ast_manager& m, qsat_mode_t mode, params_ref const& p): m(m), s(m, p), m_mode(mode), m_params(p), m_nftactic(nullptr), m_answer(m), m_answer_simplify(m), m_trail(m) { s.m_solver.get_explain().set_signed_project(true); m_nftactic = mk_tseitin_cnf_tactic(m); } ~nlqsat() override { } void updt_params(params_ref const & p) override { params_ref p2(p); p2.set_bool("factor", false); s.m_solver.updt_params(p2); } void collect_param_descrs(param_descrs & r) override { } void operator()(/* in */ goal_ref const & in, /* out */ goal_ref_buffer & result) override { tactic_report report("nlqsat-tactic", *in); ptr_vector fmls; expr_ref fml(m); in->get_formulas(fmls); fml = mk_and(m, fmls.size(), fmls.c_ptr()); if (m_mode == elim_t) { fml = m.mk_not(fml); } reset(); TRACE("qe", tout << fml << "\n";); if (!hoist(fml)) { result.push_back(in.get()); return; } TRACE("qe", tout << "ex: " << fml << "\n";); lbool is_sat = check_sat(); switch (is_sat) { case l_false: in->reset(); in->inc_depth(); if (m_mode == elim_t) { fml = mk_and(m_answer); } else { fml = m.mk_false(); } in->assert_expr(fml); result.push_back(in.get()); break; case l_true: SASSERT(m_mode == qsat_t); in->reset(); in->inc_depth(); result.push_back(in.get()); if (in->models_enabled()) { model_converter_ref mc; VERIFY(mk_model(mc)); in->add(mc.get()); } break; case l_undef: result.push_back(in.get()); throw tactic_exception("search failed"); } } void collect_statistics(statistics & st) const override { st.copy(m_st); st.update("qsat num rounds", m_stats.m_num_rounds); } void reset_statistics() override { m_stats.reset(); s.m_solver.reset_statistics(); } void cleanup() override { reset(); } void set_logic(symbol const & l) override { } void set_progress_callback(progress_callback * callback) override { } tactic * translate(ast_manager & m) override { return alloc(nlqsat, m, m_mode, m_params); } }; }; tactic * mk_nlqsat_tactic(ast_manager & m, params_ref const& p) { return alloc(qe::nlqsat, m, qe::qsat_t, p); } tactic * mk_nlqe_tactic(ast_manager & m, params_ref const& p) { return alloc(qe::nlqsat, m, qe::elim_t, p); } z3-z3-4.8.7/src/qe/nlqsat.h000066400000000000000000000011441356505360400152760ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: nlqsat.h Abstract: Quantifier Satisfiability Solver for nlsat Author: Nikolaj Bjorner (nbjorner) 2015-10-17 Revision History: --*/ #ifndef QE_NLQSAT_H__ #define QE_NLQSAT_H__ #include "tactic/tactic.h" tactic * mk_nlqsat_tactic(ast_manager & m, params_ref const& p = params_ref()); tactic * mk_nlqe_tactic(ast_manager & m, params_ref const& p = params_ref()); /* ADD_TACTIC("nlqsat", "apply a NL-QSAT solver.", "mk_nlqsat_tactic(m, p)") */ // TBD_TACTIC("nlqe", "apply a NL-QE solver.", "mk_nlqe_tactic(m, p)") #endif z3-z3-4.8.7/src/qe/qe.cpp000066400000000000000000002637011356505360400147450ustar00rootroot00000000000000/*++ Copyright (c) 2010 Microsoft Corporation Module Name: qe.cpp Abstract: Quantifier elimination procedures Author: Nikolaj Bjorner (nbjorner) 2010-02-19 Revision History: --*/ #include "qe/qe.h" #include "smt/smt_theory.h" #include "ast/bv_decl_plugin.h" #include "smt/smt_context.h" #include "smt/theory_bv.h" #include "ast/ast_ll_pp.h" #include "ast/ast_pp.h" #include "ast/ast_smt_pp.h" #include "ast/expr_abstract.h" #include "ast/rewriter/var_subst.h" #include "ast/for_each_expr.h" #include "ast/dl_decl_plugin.h" #include "qe/nlarith_util.h" #include "ast/rewriter/expr_replacer.h" #include "ast/rewriter/factor_rewriter.h" #include "ast/expr_functors.h" #include "ast/rewriter/quant_hoist.h" #include "ast/rewriter/bool_rewriter.h" #include "ast/rewriter/th_rewriter.h" #include "smt/smt_kernel.h" #include "model/model_evaluator.h" #include "ast/has_free_vars.h" #include "ast/rewriter/rewriter_def.h" #include "tactic/tactical.h" #include "model/model_v2_pp.h" #include "util/obj_hashtable.h" namespace qe { class conjunctions { ast_manager& m; ptr_vector m_plugins; // family_id -> plugin public: conjunctions(ast_manager& m) : m(m) {} void add_plugin(qe_solver_plugin* p) { family_id fid = p->get_family_id(); if (static_cast(m_plugins.size()) <= fid) { m_plugins.resize(fid + 1); } m_plugins[fid] = p; } void get_partition( expr* fml, unsigned num_vars, app* const* vars, expr_ref& fml_closed, // conjuncts that don't contain any variables from vars. expr_ref& fml_mixed, // conjuncts that contain terms from vars and non-vars. expr_ref& fml_open // conjuncts that contain vars (mixed or pure). ) { expr_ref_vector conjs(m); ast_mark visited; ast_mark contains_var; ast_mark contains_uf; ptr_vector todo; ptr_vector conjs_closed, conjs_mixed, conjs_open; flatten_and(fml, conjs); for (unsigned i = 0; i < conjs.size(); ++i) { todo.push_back(conjs[i].get()); } while (!todo.empty()) { expr* e = todo.back(); if (visited.is_marked(e)) { todo.pop_back(); continue; } if (is_var(to_app(e), num_vars, vars)) { contains_var.mark(e, true); visited.mark(e, true); todo.pop_back(); continue; } if (!is_app(e)) { visited.mark(e, true); todo.pop_back(); continue; } bool all_visited = true; app* a = to_app(e); if (is_uninterpreted(a)) { contains_uf.mark(e, true); } for (unsigned i = 0; i < a->get_num_args(); ++i) { expr* arg = a->get_arg(i); if (!visited.is_marked(arg)) { all_visited = false; todo.push_back(arg); } else { if (contains_var.is_marked(arg)) { contains_var.mark(e, true); } if (contains_uf.is_marked(arg)) { contains_uf.mark(e, true); } } } if (all_visited) { todo.pop_back(); visited.mark(e, true); } } for (expr* e : conjs) { bool cv = contains_var.is_marked(e); bool cu = contains_uf.is_marked(e); if (cv && cu) { conjs_mixed.push_back(e); conjs_open.push_back(e); } else if (cv) { conjs_open.push_back(e); } else { conjs_closed.push_back(e); } } bool_rewriter rewriter(m); rewriter.mk_and(conjs_closed.size(), conjs_closed.c_ptr(), fml_closed); rewriter.mk_and(conjs_mixed.size(), conjs_mixed.c_ptr(), fml_mixed); rewriter.mk_and(conjs_open.size(), conjs_open.c_ptr(), fml_open); TRACE("qe", tout << "closed\n" << mk_ismt2_pp(fml_closed, m) << "\n"; tout << "open\n" << mk_ismt2_pp(fml_open, m) << "\n"; tout << "mixed\n" << mk_ismt2_pp(fml_mixed, m) << "\n"; ); } // // Partition variables into buckets. // The var_paritions buckets covering disjoint subsets of // the conjuncts. The remaining variables in vars are non-partioned. // bool partition_vars( unsigned num_vars, contains_app** vars, unsigned num_args, expr* const* args, vector& partition ) { unsigned_vector contains_index; unsigned_vector non_shared; unsigned_vector non_shared_vars; union_find_default_ctx df; union_find uf(df); partition.reset(); for (unsigned v = 0; v < num_vars; ++v) { contains_app& contains_x = *vars[v]; contains_index.reset(); for (unsigned i = 0; i < num_args; ++i) { if (contains_x(args[i])) { contains_index.push_back(i); TRACE("qe_verbose", tout << "var " << v << " in " << i << "\n";); } } // // x occurs in more than half of conjuncts. // Mark it as shared. // if (2*contains_index.size() > num_args) { if (partition.empty()) { partition.push_back(unsigned_vector()); } partition.back().push_back(v); TRACE("qe_verbose", tout << "majority " << v << "\n";); continue; } // // Create partition of variables that share conjuncts. // unsigned var_x = uf.mk_var(); SASSERT(var_x == non_shared_vars.size()); non_shared_vars.push_back(v); for (unsigned i = 0; i < contains_index.size(); ++i) { unsigned idx = contains_index[i]; if (non_shared.size() <= idx) { non_shared.resize(idx+1,UINT_MAX); } unsigned var_y = non_shared[idx]; if (var_y != UINT_MAX) { uf.merge(var_x, var_y); } else { non_shared[idx] = var_x; } } } if (non_shared_vars.empty()) { return false; } unsigned root0 = uf.find(0); bool is_partitioned = false; for (unsigned idx = 1; !is_partitioned && idx < non_shared_vars.size(); ++idx) { is_partitioned = (uf.find(idx) != root0); } if (!is_partitioned) { return false; } // // variables are partitioned into more than one // class. // unsigned_vector roots; if (!partition.empty()) { roots.push_back(UINT_MAX); } for (unsigned idx = 0; idx < non_shared_vars.size(); ++idx) { unsigned x = non_shared_vars[idx]; unsigned r = non_shared_vars[uf.find(idx)]; TRACE("qe_verbose", tout << "x: " << x << " r: " << r << "\n";); bool found = false; for (unsigned i = 0; !found && i < roots.size(); ++i) { if (roots[i] == r) { found = true; partition[i].push_back(x); } } if (!found) { roots.push_back(r); partition.push_back(unsigned_vector()); partition.back().push_back(x); } } TRACE("qe_verbose", for (unsigned i = 0; i < partition.size(); ++i) { for (unsigned j = 0; j < partition[i].size(); ++j) { tout << " " << mk_ismt2_pp(vars[partition[i][j]]->x(), m);; } tout << "\n"; }); SASSERT(partition.size() != 1); SASSERT(!partition.empty() || roots.size() > 1); return true; } private: bool is_var(app* x, unsigned num_vars, app* const* vars) { for (unsigned i = 0; i < num_vars; ++i) { if (vars[i] == x) { return true; } } return false; } bool is_uninterpreted(app* a) { family_id fid = a->get_family_id(); if (null_family_id == fid) { return true; } if (static_cast(fid) >= m_plugins.size()) { return true; } qe_solver_plugin* p = m_plugins[fid]; if (!p) { return true; } return p->is_uninterpreted(a); } }; // --------------- // conj_enum conj_enum::conj_enum(ast_manager& m, expr* e): m(m), m_conjs(m) { flatten_and(e, m_conjs); } void conj_enum::extract_equalities(expr_ref_vector& eqs) { arith_util arith(m); obj_hashtable leqs; expr_ref_vector trail(m); expr_ref tmp1(m), tmp2(m); expr *a0, *a1; eqs.reset(); conj_enum::iterator it = begin(); for (; it != end(); ++it) { expr* e = *it; if (m.is_eq(e, a0, a1) && (arith.is_int(a0) || arith.is_real(a0))) { tmp1 = arith.mk_sub(a0, a1); eqs.push_back(tmp1); } else if (arith.is_le(e, a0, a1) || arith.is_ge(e, a1, a0)) { tmp1 = arith.mk_sub(a0, a1); tmp2 = arith.mk_sub(a1, a0); if (leqs.contains(tmp2)) { eqs.push_back(tmp1); TRACE("qe", tout << "found: " << mk_ismt2_pp(tmp1, m) << "\n";); } else { trail.push_back(tmp1); leqs.insert(tmp1); TRACE("qe_verbose", tout << "insert: " << mk_ismt2_pp(tmp1, m) << "\n";); } } else { // drop equality. } } } // ----------------- // Lift ite from sub-formulas. // class lift_ite { ast_manager& m; i_expr_pred& m_is_relevant; th_rewriter m_rewriter; scoped_ptr m_replace; public: lift_ite(ast_manager& m, i_expr_pred& is_relevant) : m(m), m_is_relevant(is_relevant), m_rewriter(m), m_replace(mk_default_expr_replacer(m)) {} bool operator()(expr* fml, expr_ref& result) { if (m.is_ite(fml)) { result = fml; return true; } app* ite; if (find_ite(fml, ite)) { expr* cond = nullptr, *th = nullptr, *el = nullptr; VERIFY(m.is_ite(ite, cond, th, el)); expr_ref tmp1(fml, m), tmp2(fml, m); m_replace->apply_substitution(ite, th, tmp1); m_replace->apply_substitution(ite, el, tmp2); result = m.mk_ite(cond, tmp1, tmp2); m_rewriter(result); return true; } else { return false; } } private: bool find_ite(expr* e, app*& ite) { ptr_vector todo; ast_mark visited; todo.push_back(e); while(!todo.empty()) { e = todo.back(); todo.pop_back(); if (visited.is_marked(e)) { continue; } visited.mark(e, true); if (!m_is_relevant(e)) { continue; } if (m.is_ite(e)) { ite = to_app(e); return true; } if (is_app(e)) { app* a = to_app(e); unsigned num_args = a->get_num_args(); for (unsigned i = 0; i < num_args; ++i) { todo.push_back(a->get_arg(i)); } } } return false; } }; // convert formula to NNF. class nnf { ast_manager& m; i_expr_pred& m_is_relevant; lift_ite m_lift_ite; obj_map m_pos, m_neg; // memoize positive/negative sub-formulas expr_ref_vector m_trail; // trail for generated terms expr_ref_vector m_args; ptr_vector m_todo; // stack of formulas to visit svector m_pols; // stack of polarities bool_rewriter m_rewriter; public: nnf(ast_manager& m, i_expr_pred& is_relevant): m(m), m_is_relevant(is_relevant), m_lift_ite(m, is_relevant), m_trail(m), m_args(m), m_rewriter(m) {} void operator()(expr_ref& fml) { reset(); get_nnf(fml, true); } void reset() { m_todo.reset(); m_trail.reset(); m_pols.reset(); m_pos.reset(); m_neg.reset(); } private: bool contains(expr* e, bool p) { return p?m_pos.contains(e):m_neg.contains(e); } expr* lookup(expr* e, bool p) { expr* r = nullptr; if (p && m_pos.find(e, r)) { return r; } if (!p && m_neg.find(e, r)) { return r; } m_todo.push_back(e); m_pols.push_back(p); return nullptr; } void insert(expr* e, bool p, expr* r) { if (p) { m_pos.insert(e, r); } else { m_neg.insert(e, r); } TRACE("nnf", tout << mk_ismt2_pp(e, m) << " " << p << "\n"; tout << mk_ismt2_pp(r, m) << "\n";); m_trail.push_back(r); } void pop() { m_todo.pop_back(); m_pols.pop_back(); } void nnf_iff(app* a, bool p) { SASSERT(m.is_iff(a) || m.is_xor(a) || m.is_eq(a)); expr* a0 = a->get_arg(0); expr* a1 = a->get_arg(1); expr* r1 = lookup(a0, true); expr* r2 = lookup(a0, false); expr* p1 = lookup(a1, true); expr* p2 = lookup(a1, false); if (r1 && r2 && p1 && p2) { expr_ref tmp1(m), tmp2(m), tmp(m); pop(); if (p) { m_rewriter.mk_and(r1, p1, tmp1); m_rewriter.mk_and(r2, p2, tmp2); m_rewriter.mk_or(tmp1, tmp2, tmp); } else { m_rewriter.mk_or(r1, p1, tmp1); m_rewriter.mk_or(r2, p2, tmp2); m_rewriter.mk_and(tmp1, tmp2, tmp); } insert(a, p, tmp); } } void nnf_ite(app* a, bool p) { SASSERT(m.is_ite(a)); expr* r1 = lookup(a->get_arg(0), true); expr* r2 = lookup(a->get_arg(0), false); expr* th = lookup(a->get_arg(1), p); expr* el = lookup(a->get_arg(2), p); if (r1 && r2 && th && el) { expr_ref tmp1(m), tmp2(m), tmp(m); pop(); m_rewriter.mk_and(r1, th, tmp1); m_rewriter.mk_and(r2, el, tmp2); m_rewriter.mk_or(tmp1, tmp2, tmp); TRACE("nnf", tout << mk_ismt2_pp(a, m) << "\n"; tout << mk_ismt2_pp(tmp, m) << "\n";); insert(a, p, tmp); } } void nnf_implies(app* a, bool p) { SASSERT(m.is_implies(a)); expr* r1 = lookup(a->get_arg(0), !p); expr* r2 = lookup(a->get_arg(1), p); if (r1 && r2) { expr_ref tmp(m); if (p) { m_rewriter.mk_or(r1, r2, tmp); } else { m_rewriter.mk_and(r1, r2, tmp); } insert(a, p, tmp); } } void nnf_not(app* a, bool p) { SASSERT(m.is_not(a)); expr* arg = a->get_arg(0); expr* e = lookup(arg, !p); if (e) { insert(a, p, e); } } void nnf_and_or(bool is_and, app* a, bool p) { m_args.reset(); unsigned num_args = a->get_num_args(); expr_ref tmp(m); bool visited = true; for (unsigned i = 0; i < num_args; ++i) { expr* arg = a->get_arg(i); expr* r = lookup(arg, p); if (r) { m_args.push_back(r); } else { visited = false; } } if (visited) { pop(); if ((p && is_and) || (!p && !is_and)) { m_rewriter.mk_and(num_args, m_args.c_ptr(), tmp); } else { m_rewriter.mk_or(num_args, m_args.c_ptr(), tmp); } insert(a, p, tmp); } } bool get_nnf(expr_ref& fml, bool p0) { TRACE("nnf", tout << mk_ismt2_pp(fml.get(), m) << "\n";); bool p = p0; unsigned sz = m_todo.size(); expr_ref tmp(m); expr* e = lookup(fml, p); if (e) { fml = e; return true; } m_trail.push_back(fml); while (sz < m_todo.size()) { e = m_todo.back(); p = m_pols.back(); if (!m_is_relevant(e)) { pop(); insert(e, p, p?e:mk_not(m, e)); continue; } if (!is_app(e)) { return false; } if (contains(e, p)) { pop(); continue; } app* a = to_app(e); if (m.is_and(e) || m.is_or(e)) { nnf_and_or(m.is_and(a), a, p); } else if (m.is_not(a)) { nnf_not(a, p); } else if (m.is_ite(a)) { nnf_ite(a, p); } else if (m.is_iff(a)) { nnf_iff(a, p); } else if (m.is_xor(a)) { nnf_iff(a, !p); } else if (m.is_implies(a)) { nnf_implies(a, p); } else if (m_lift_ite(e, tmp)) { if (!get_nnf(tmp, p)) { return false; } pop(); insert(e, p, tmp); } else { pop(); insert(e, p, p?e:mk_not(m, e)); } } fml = lookup(fml, p0); SASSERT(fml.get()); return true; } }; // ---------------------------------- // Normalize literals in NNF formula. class nnf_normalize_literals { ast_manager& m; i_expr_pred& m_is_relevant; i_nnf_atom& m_mk_atom; obj_map m_cache; ptr_vector m_todo; expr_ref_vector m_trail; ptr_vector m_args; public: nnf_normalize_literals(ast_manager& m, i_expr_pred& is_relevant, i_nnf_atom& mk_atom): m(m), m_is_relevant(is_relevant), m_mk_atom(mk_atom), m_trail(m) {} void operator()(expr_ref& fml) { SASSERT(m_todo.empty()); m_todo.push_back(fml); while (!m_todo.empty()) { expr* e = m_todo.back(); if (m_cache.contains(e)) { m_todo.pop_back(); } else if (!is_app(e)) { m_todo.pop_back(); m_cache.insert(e, e); } else if (visit(to_app(e))) { m_todo.pop_back(); } } fml = m_cache.find(fml); reset(); } void reset() { m_cache.reset(); m_todo.reset(); m_trail.reset(); } private: bool visit(app* e) { bool all_visit = true; expr* f = nullptr; expr_ref tmp(m); if (!m_is_relevant(e)) { m_cache.insert(e, e); } else if (m.is_and(e) || m.is_or(e)) { m_args.reset(); for (unsigned i = 0; i < e->get_num_args(); ++i) { if (m_cache.find(e->get_arg(i), f)) { m_args.push_back(f); } else { all_visit = false; m_todo.push_back(e->get_arg(i)); } } if (all_visit) { m_cache.insert(e, m.mk_app(e->get_decl(), m_args.size(), m_args.c_ptr())); } } else if (m.is_not(e, f)) { SASSERT(!m.is_not(f) && !m.is_and(f) && !m.is_or(f)); m_mk_atom(f, false, tmp); m_cache.insert(e, tmp); m_trail.push_back(tmp); } else { m_mk_atom(e, true, tmp); m_trail.push_back(tmp); m_cache.insert(e, tmp); } return all_visit; } }; // ---------------------------- // def_vector void def_vector::normalize() { // apply nested definitions into place. ast_manager& m = m_vars.get_manager(); expr_substitution sub(m); scoped_ptr rep = mk_expr_simp_replacer(m); if (size() <= 1) { return; } for (unsigned i = size(); i > 0; ) { --i; expr_ref e(m); e = def(i); rep->set_substitution(&sub); (*rep)(e); sub.insert(m.mk_const(var(i)), e); def_ref(i) = e; } } void def_vector::project(unsigned num_vars, app* const* vars) { obj_hashtable fns; for (unsigned i = 0; i < num_vars; ++i) { fns.insert(vars[i]->get_decl()); } for (unsigned i = 0; i < size(); ++i) { if (fns.contains(m_vars[i].get())) { // // retain only first occurrence of eliminated variable. // later occurrences are just recycling the name. // fns.remove(m_vars[i].get()); } else { for (unsigned j = i+1; j < size(); ++j) { m_vars.set(j-1, m_vars.get(j)); m_defs.set(j-1, m_defs.get(j)); } m_vars.pop_back(); m_defs.pop_back(); --i; } } } // ---------------------------- // guarded_defs std::ostream& guarded_defs::display(std::ostream& out) const { ast_manager& m = m_guards.get_manager(); for (unsigned i = 0; i < size(); ++i) { for (unsigned j = 0; j < defs(i).size(); ++j) { out << defs(i).var(j)->get_name() << " := " << mk_pp(defs(i).def(j), m) << "\n"; } out << "if " << mk_pp(guard(i), m) << "\n"; } return out; } bool guarded_defs::inv() { return m_defs.size() == m_guards.size(); } void guarded_defs::add(expr* guard, def_vector const& defs) { SASSERT(inv()); m_defs.push_back(defs); m_guards.push_back(guard); m_defs.back().normalize(); SASSERT(inv()); } void guarded_defs::project(unsigned num_vars, app* const* vars) { for (unsigned i = 0; i < size(); ++i) { m_defs[i].project(num_vars, vars); } } // ---------------------------- // Obtain atoms in NNF formula. class nnf_collect_atoms { ast_manager& m; i_expr_pred& m_is_relevant; ptr_vector m_todo; ast_mark m_visited; public: nnf_collect_atoms(ast_manager& m, i_expr_pred& is_relevant): m(m), m_is_relevant(is_relevant) {} void operator()(expr* fml, atom_set& pos, atom_set& neg) { m_todo.push_back(fml); while (!m_todo.empty()) { expr* e = m_todo.back(); m_todo.pop_back(); if (m_visited.is_marked(e)) { continue; } m_visited.mark(e, true); if (!is_app(e) || !m_is_relevant(e)) { continue; } app* a = to_app(e); if (m.is_and(a) || m.is_or(a)) { for (unsigned i = 0; i < a->get_num_args(); ++i) { m_todo.push_back(a->get_arg(i)); } } else if (m.is_not(a, e) && is_app(e)) { neg.insert(to_app(e)); } else { pos.insert(a); } } SASSERT(m_todo.empty()); m_visited.reset(); } }; // -------------------------------- // Bring formula to NNF, normalize atoms, collect literals. class nnf_normalizer { nnf m_nnf_core; nnf_collect_atoms m_collect_atoms; nnf_normalize_literals m_normalize_literals; public: nnf_normalizer(ast_manager& m, i_expr_pred& is_relevant, i_nnf_atom& mk_atom): m_nnf_core(m, is_relevant), m_collect_atoms(m, is_relevant), m_normalize_literals(m, is_relevant, mk_atom) {} void operator()(expr_ref& fml, atom_set& pos, atom_set& neg) { expr_ref orig(fml); m_nnf_core(fml); m_normalize_literals(fml); m_collect_atoms(fml, pos, neg); TRACE("qe", ast_manager& m = fml.get_manager(); tout << mk_ismt2_pp(orig, m) << "\n-->\n" << mk_ismt2_pp(fml, m) << "\n";); } void reset() { m_nnf_core.reset(); m_normalize_literals.reset(); } }; void get_nnf(expr_ref& fml, i_expr_pred& pred, i_nnf_atom& mk_atom, atom_set& pos, atom_set& neg) { nnf_normalizer nnf(fml.get_manager(), pred, mk_atom); nnf(fml, pos, neg); } // // Theory plugin for quantifier elimination. // class quant_elim { public: virtual ~quant_elim() {} virtual lbool eliminate_exists( unsigned num_vars, app* const* vars, expr_ref& fml, app_ref_vector& free_vars, bool get_first, guarded_defs* defs) = 0; virtual void set_assumption(expr* fml) = 0; virtual void collect_statistics(statistics & st) const = 0; virtual void eliminate(bool is_forall, unsigned num_vars, app* const* vars, expr_ref& fml) = 0; virtual void updt_params(params_ref const& p) {} }; class search_tree { typedef map branch_map; ast_manager& m; app_ref_vector m_vars; // free variables app_ref m_var; // 0 or selected free variable def_vector m_def; // substitution for the variable eliminated relative to the parent. expr_ref m_fml; // formula whose variables are to be eliminated app_ref m_assignment; // assignment that got us here. search_tree* m_parent; // parent pointer rational m_num_branches; // number of possible branches ptr_vector m_children; // list of children branch_map m_branch_index; // branch_id -> child search tree index atom_set m_pos; atom_set m_neg; bool m_pure; // is the node pure (no variables deleted). // The invariant captures that search tree nodes are either // - unit branches (with only one descendant), or // - unassigned variable and formula // - assigned formula, but unassigned variable for branching // - assigned variable and formula with 0 or more branches. // #define CHECK_COND(_cond_) if (!(_cond_)) { TRACE("qe", tout << "violated: " << #_cond_ << "\n";); return false; } bool invariant() const { CHECK_COND(assignment()); CHECK_COND(m_children.empty() || fml()); CHECK_COND(!is_root() || fml()); CHECK_COND(!fml() || has_var() || m_num_branches.is_zero() || is_unit()); branch_map::iterator it = m_branch_index.begin(), end = m_branch_index.end(); for (; it != end; ++it) { CHECK_COND(it->m_value < m_children.size()); CHECK_COND(it->m_key < get_num_branches()); } for (unsigned i = 0; i < m_children.size(); ++i) { CHECK_COND(m_children[i]); CHECK_COND(this == m_children[i]->m_parent); CHECK_COND(m_children[i]->invariant()); } return true; } #undef CHECKC_COND public: search_tree(search_tree* parent, ast_manager& m, app* assignment): m(m), m_vars(m), m_var(m), m_def(m), m_fml(m), m_assignment(assignment, m), m_parent(parent), m_pure(true) {} ~search_tree() { reset(); } expr* fml() const { return m_fml; } expr_ref& fml_ref() { return m_fml; } def_vector const& def() const { return m_def; } app* assignment() const { return m_assignment; } app* var() const { SASSERT(has_var()); return m_var; } bool has_var() const { return nullptr != m_var.get(); } search_tree* parent() const { return m_parent; } bool is_root() const { return !parent(); } rational const& get_num_branches() const { SASSERT(has_var()); return m_num_branches; } // extract disjunctions void get_leaves(expr_ref_vector& result) { SASSERT(is_root()); ptr_vector todo; todo.push_back(this); while (!todo.empty()) { search_tree* st = todo.back(); todo.pop_back(); if (st->m_children.empty() && st->fml() && st->m_vars.empty() && !st->has_var()) { TRACE("qe", st->display(tout << "appending leaf\n");); result.push_back(st->fml()); } for (auto * ch : st->m_children) todo.push_back(ch); } } void get_leaves_rec(def_vector& defs, guarded_defs& gdefs) { expr* f = this->fml(); unsigned sz = defs.size(); defs.append(def()); if (m_children.empty() && f && !m.is_false(f) && m_vars.empty() && !has_var()) { gdefs.add(f, defs); } else { for (unsigned i = 0; i < m_children.size(); ++i) { m_children[i]->get_leaves_rec(defs, gdefs); } } defs.shrink(sz); } void get_leaves(guarded_defs& gdefs) { def_vector defs(m); get_leaves_rec(defs, gdefs); } void reset() { TRACE("qe",tout << "resetting\n";); for (auto* ch : m_children) dealloc(ch); m_pos.reset(); m_neg.reset(); m_children.reset(); m_vars.reset(); m_branch_index.reset(); m_var = nullptr; m_def.reset(); m_num_branches = rational::zero(); m_pure = true; } void init(expr* fml) { m_fml = fml; SASSERT(invariant()); } void add_def(app* v, expr* def) { if (v && def) { m_def.push_back(v->get_decl(), def); } } unsigned num_free_vars() const { return m_vars.size(); } // app* const* free_vars() const { return m_vars.c_ptr(); } app_ref_vector const& free_vars() const { return m_vars; } app* free_var(unsigned i) const { return m_vars[i]; } void reset_free_vars() { TRACE("qe", tout << m_vars << "\n";); m_vars.reset(); } atom_set const& pos_atoms() const { return m_pos; } atom_set const& neg_atoms() const { return m_neg; } atom_set& pos_atoms() { return m_pos; } atom_set& neg_atoms() { return m_neg; } // set the branch variable. void set_var(app* x, rational const& num_branches) { SASSERT(!m_var.get()); SASSERT(m_vars.contains(x)); m_var = x; m_vars.erase(x); m_num_branches = num_branches; SASSERT(invariant()); } // include new variables. void consume_vars(app_ref_vector& vars) { while (!vars.empty()) { m_vars.push_back(vars.back()); vars.pop_back(); } } bool is_pure() const { search_tree const* node = this; while (node) { if (!node->m_pure) return false; node = node->parent(); } return true; } bool is_unit() const { return m_children.size() == 1 && m_branch_index.empty(); } bool has_branch(rational const& branch_id) const { return m_branch_index.contains(branch_id); } search_tree* child(rational const& branch_id) const { unsigned idx = m_branch_index.find(branch_id); return m_children[idx]; } search_tree* child() const { SASSERT(is_unit()); return m_children[0]; } // remove variable from branch. void del_var(app* x) { SASSERT(m_children.empty()); SASSERT(m_vars.contains(x)); m_vars.erase(x); m_pure = false; } // add branch with a given formula search_tree* add_child(expr* fml) { SASSERT(m_branch_index.empty()); SASSERT(m_children.empty()); m_num_branches = rational(1); search_tree* st = alloc(search_tree, this, m, m.mk_true()); m_children.push_back(st); st->init(fml); st->m_vars.append(m_vars.size(), m_vars.c_ptr()); SASSERT(invariant()); TRACE("qe", display_node(tout); st->display_node(tout);); return st; } search_tree* add_child(rational const& branch_id, app* assignment) { SASSERT(!m_branch_index.contains(branch_id)); SASSERT(has_var()); SASSERT(branch_id.is_nonneg() && branch_id < m_num_branches); unsigned index = m_children.size(); search_tree* st = alloc(search_tree, this, m, assignment); m_children.push_back(st); m_branch_index.insert(branch_id, index); st->m_vars.append(m_vars.size(), m_vars.c_ptr()); SASSERT(invariant()); TRACE("qe", display_node(tout); st->display_node(tout);); return st; } void display(std::ostream& out) const { display(out, ""); } void display_node(std::ostream& out, char const* indent = "") const { out << indent << "node " << std::hex << this << std::dec << "\n"; if (m_var) { out << indent << " var: " << m_var << "\n"; } for (app* v : m_vars) { out << indent << " free: " << mk_pp(v, m) << "\n"; } if (m_fml) { out << indent << " fml: " << m_fml << "\n"; } for (unsigned i = 0; i < m_def.size(); ++i) { out << indent << " def: " << m_def.var(i)->get_name() << " = " << mk_ismt2_pp(m_def.def(i), m) << "\n"; } out << indent << " branches: " << m_num_branches << "\n"; } void display(std::ostream& out, char const* indent) const { display_node(out, indent); std::string new_indent(indent); new_indent += " "; for (auto * ch : m_children) { ch->display(out, new_indent.c_str()); } } expr_ref abstract_variable(app* x, expr* fml) const { expr_ref result(m); expr* y = x; expr_abstract(m, 0, 1, &y, fml, result); symbol X(x->get_decl()->get_name()); sort* s = m.get_sort(x); result = m.mk_exists(1, &s, &X, result); return result; } void display_validate(std::ostream& out) const { if (m_children.empty()) { return; } expr_ref q(m); expr* x = m_var; if (x) { q = abstract_variable(m_var, m_fml); expr_ref_vector fmls(m); expr_ref fml(m); for (unsigned i = 0; i < m_children.size(); ++i) { search_tree const& child = *m_children[i]; fml = child.fml(); if (fml) { // abstract free variables in children. ptr_vector child_vars, new_vars; child_vars.append(child.m_vars.size(), child.m_vars.c_ptr()); if (child.m_var) { child_vars.push_back(child.m_var); } for (unsigned j = 0; j < child_vars.size(); ++j) { if (!m_vars.contains(child_vars[j]) && !new_vars.contains(child_vars[j])) { fml = abstract_variable(child_vars[j], fml); new_vars.push_back(child_vars[j]); } } fmls.push_back(fml); } } bool_rewriter(m).mk_or(fmls.size(), fmls.c_ptr(), fml); fml = mk_not(m, m.mk_iff(q, fml)); ast_smt_pp pp(m); out << "; eliminate " << mk_pp(m_var, m) << "\n"; out << "(push)\n"; pp.display_smt2(out, fml); out << "(pop)\n\n"; #if 0 DEBUG_CODE( smt_params params; params.m_simplify_bit2int = true; params.m_arith_enum_const_mod = true; params.m_bv_enable_int2bv2int = true; params.m_relevancy_lvl = 0; smt::kernel ctx(m, params); ctx.assert_expr(fml); lbool is_sat = ctx.check(); if (is_sat == l_true) { std::cout << "; Validation failed:\n"; std::cout << mk_pp(fml, m) << "\n"; } ); #endif } for (unsigned i = 0; i < m_children.size(); ++i) { if (m_children[i]->fml()) { m_children[i]->display_validate(out); } } } }; // ------------------------- // i_solver_context i_solver_context::~i_solver_context() { for (unsigned i = 0; i < m_plugins.size(); ++i) { dealloc(m_plugins[i]); } } bool i_solver_context::is_relevant::operator()(expr* e) { for (unsigned i = 0; i < m_s.get_num_vars(); ++i) { if (m_s.contains(i)(e)) { return true; } } TRACE("qe_verbose", tout << "Not relevant: " << mk_ismt2_pp(e, m_s.get_manager()) << "\n";); return false; } bool i_solver_context::is_var(expr* x, unsigned& idx) const { unsigned nv = get_num_vars(); for (unsigned i = 0; i < nv; ++i) { if (get_var(i) == x) { idx = i; return true; } } return false; } void i_solver_context::add_plugin(qe_solver_plugin* p) { family_id fid = p->get_family_id(); SASSERT(fid != null_family_id); if (static_cast(m_plugins.size()) <= fid) { m_plugins.resize(fid+1); } SASSERT(!m_plugins[fid]); m_plugins[fid] = p; } bool i_solver_context::has_plugin(app* x) { ast_manager& m = get_manager(); family_id fid = m.get_sort(x)->get_family_id(); return 0 <= fid && fid < static_cast(m_plugins.size()) && m_plugins[fid] != 0; } qe_solver_plugin& i_solver_context::plugin(app* x) { ast_manager& m = get_manager(); SASSERT(has_plugin(x)); return *(m_plugins[m.get_sort(x)->get_family_id()]); } void i_solver_context::mk_atom(expr* e, bool p, expr_ref& result) { ast_manager& m = get_manager(); TRACE("qe_verbose", tout << mk_ismt2_pp(e, m) << "\n";); for (unsigned i = 0; i < m_plugins.size(); ++i) { qe_solver_plugin* pl = m_plugins[i]; if (pl && pl->mk_atom(e, p, result)) { return; } } TRACE("qe_verbose", tout << "No plugin for " << mk_ismt2_pp(e, m) << "\n";); result = p?e:mk_not(m, e); } void i_solver_context::mk_atom_fn::operator()(expr* e, bool p, expr_ref& result) { m_s.mk_atom(e, p, result); } void i_solver_context::collect_statistics(statistics& st) const { // tbd } typedef ref_vector_ptr_hash expr_ref_vector_hash; typedef ref_vector_ptr_eq expr_ref_vector_eq; typedef hashtable clause_table; typedef value_trail _value_trail; class quant_elim_plugin : public i_solver_context { ast_manager& m; quant_elim& m_qe; th_rewriter m_rewriter; smt::kernel m_solver; bv_util m_bv; expr_ref_vector m_literals; bool_rewriter m_bool_rewriter; conjunctions m_conjs; // maintain queue for variables. app_ref_vector m_free_vars; // non-quantified variables app_ref_vector m_trail; expr_ref m_fml; expr_ref m_subfml; obj_map m_var2branch; // var -> bv-var, identify explored branch. obj_map m_var2contains; // var -> contains_app obj_map > m_children; // var -> list of dependent children search_tree m_root; search_tree* m_current; // current branch vector m_partition; // cached latest partition of variables. app_ref_vector m_new_vars; // variables added by solvers bool m_get_first; // get first satisfying branch. guarded_defs* m_defs; nnf_normalizer m_nnf; // nnf conversion public: quant_elim_plugin(ast_manager& m, quant_elim& qe, smt_params& p): m(m), m_qe(qe), m_rewriter(m), m_solver(m, p), m_bv(m), m_literals(m), m_bool_rewriter(m), m_conjs(m), m_free_vars(m), m_trail(m), m_fml(m), m_subfml(m), m_root(nullptr, m, m.mk_true()), m_current(nullptr), m_new_vars(m), m_get_first(false), m_defs(nullptr), m_nnf(m, get_is_relevant(), get_mk_atom()) { params_ref params; params.set_bool("gcd_rounding", true); m_rewriter.updt_params(params); } ~quant_elim_plugin() override { reset(); } void reset() { m_free_vars.reset(); m_trail.reset(); obj_map::iterator it = m_var2contains.begin(), end = m_var2contains.end(); for (; it != end; ++it) { dealloc(it->m_value); } m_var2contains.reset(); m_var2branch.reset(); m_root.reset(); m_new_vars.reset(); m_fml = nullptr; m_defs = nullptr; m_nnf.reset(); } void add_plugin(qe_solver_plugin* p) { i_solver_context::add_plugin(p); m_conjs.add_plugin(p); } void check(unsigned num_vars, app* const* vars, expr* assumption, expr_ref& fml, bool get_first, app_ref_vector& free_vars, guarded_defs* defs) { reset(); m_solver.push(); m_get_first = get_first; m_defs = defs; for (unsigned i = 0; i < num_vars; ++i) { if (has_plugin(vars[i])) { add_var(vars[i]); } else { m_free_vars.push_back(vars[i]); } } m_root.consume_vars(m_new_vars); m_current = &m_root; // set sub-formula m_fml = fml; normalize(m_fml, m_root.pos_atoms(), m_root.neg_atoms()); expr_ref f(m_fml); get_max_relevant(get_is_relevant(), f, m_subfml); if (f.get() != m_subfml.get()) { m_fml = f; f = m_subfml; m_solver.assert_expr(f); } m_root.init(f); TRACE("qe", for (unsigned i = 0; i < num_vars; ++i) tout << mk_ismt2_pp(vars[i], m) << "\n"; tout << mk_ismt2_pp(f, m) << "\n";); m_solver.assert_expr(m_fml); if (assumption) m_solver.assert_expr(assumption); bool is_sat = false; lbool res = l_true; while (res == l_true) { res = m_solver.check(); if (res == l_true) { is_sat = true; final_check(); } else { break; } } if (res == l_undef) { free_vars.append(num_vars, vars); reset(); m_solver.pop(1); return; } if (!is_sat) { fml = m.mk_false(); reset(); m_solver.pop(1); return; } if (!get_first) { expr_ref_vector result(m); m_root.get_leaves(result); m_bool_rewriter.mk_or(result.size(), result.c_ptr(), fml); } if (defs) { m_root.get_leaves(*defs); defs->project(num_vars, vars); } TRACE("qe", tout << "result:" << mk_ismt2_pp(fml, m) << "\n"; tout << "input: " << mk_ismt2_pp(m_fml, m) << "\n"; tout << "subformula: " << mk_ismt2_pp(m_subfml, m) << "\n"; m_root.display(tout); m_root.display_validate(tout); tout << "free: " << m_free_vars << "\n";); free_vars.append(m_free_vars); if (!m_free_vars.empty() || m_solver.inconsistent()) { if (m_fml.get() != m_subfml.get()) { scoped_ptr rp = mk_default_expr_replacer(m); rp->apply_substitution(to_app(m_subfml.get()), fml, m_fml); fml = m_fml; } } reset(); m_solver.pop(1); f = nullptr; } void collect_statistics(statistics& st) { m_solver.collect_statistics(st); } private: void final_check() { model_ref model; m_solver.get_model(model); scoped_ptr model_eval = alloc(model_evaluator, *model); while (true) { TRACE("qe", model_v2_pp(tout, *model);); while (can_propagate_assignment(*model_eval)) { propagate_assignment(*model_eval); } VERIFY(CHOOSE_VAR == update_current(*model_eval, true)); SASSERT(m_current->fml()); if (l_true != m_solver.check()) { return; } m_solver.get_model(model); model_eval = alloc(model_evaluator, *model); search_tree* st = m_current; update_current(*model_eval, false); if (st == m_current) { break; } } pop(*model_eval); } ast_manager& get_manager() override { return m; } atom_set const& pos_atoms() const override { return m_current->pos_atoms(); } atom_set const& neg_atoms() const override { return m_current->neg_atoms(); } unsigned get_num_vars() const override { return m_current->num_free_vars(); } app* get_var(unsigned idx) const override { return m_current->free_var(idx); } app_ref_vector const& get_vars() const override { return m_current->free_vars(); } contains_app& contains(unsigned idx) override { return contains(get_var(idx)); } // // The variable at idx is eliminated (without branching). // void elim_var(unsigned idx, expr* _fml, expr* def) override { app* x = get_var(idx); expr_ref fml(_fml, m); TRACE("qe", tout << mk_pp(x,m) << " " << mk_pp(def, m) << "\n";); m_current->set_var(x, rational(1)); m_current = m_current->add_child(fml); m_current->add_def(x, def); m_current->consume_vars(m_new_vars); normalize(*m_current); } void add_var(app* x) override { m_new_vars.push_back(x); if (m_var2branch.contains(x)) { return; } contains_app* ca = alloc(contains_app, m, x); m_var2contains.insert(x, ca); app* bv = nullptr; if (m.is_bool(x) || m_bv.is_bv(x)) { bv = x; } else { bv = m.mk_fresh_const("b", m_bv.mk_sort(20)); m_trail.push_back(bv); } TRACE("qe", tout << "Add branch var: " << mk_ismt2_pp(x, m) << " " << mk_ismt2_pp(bv, m) << "\n";); m_var2branch.insert(x, bv); } void add_constraint(bool use_current_val, expr* l1 = nullptr, expr* l2 = nullptr, expr* l3 = nullptr) override { search_tree* node = m_current; if (!use_current_val) { node = m_current->parent(); } m_literals.reset(); while (node) { m_literals.push_back(mk_not(m, node->assignment())); node = node->parent(); } add_literal(l1); add_literal(l2); add_literal(l3); expr_ref fml(m); fml = m.mk_or(m_literals.size(), m_literals.c_ptr()); TRACE("qe", tout << fml << "\n";); m_solver.assert_expr(fml); } void blast_or(app* var, expr_ref& fml) override { m_qe.eliminate_exists(1, &var, fml, m_free_vars, false, nullptr); } lbool eliminate_exists(unsigned num_vars, app* const* vars, expr_ref& fml, bool get_first, guarded_defs* defs) { return m_qe.eliminate_exists(num_vars, vars, fml, m_free_vars, get_first, defs); } private: void add_literal(expr* l) { if (l != nullptr) { m_literals.push_back(l); } } void get_max_relevant(i_expr_pred& is_relevant, expr_ref& fml, expr_ref& subfml) { if (m.is_and(fml) || m.is_or(fml)) { app* a = to_app(fml); unsigned num_args = a->get_num_args(); ptr_buffer r_args; ptr_buffer i_args; for (unsigned i = 0; i < num_args; ++i) { expr* arg = a->get_arg(i); if (is_relevant(arg)) { r_args.push_back(arg); } else { i_args.push_back(arg); } } if (r_args.empty() || i_args.empty()) { subfml = fml; } else if (r_args.size() == 1) { expr_ref tmp(r_args[0], m); get_max_relevant(is_relevant, tmp, subfml); i_args.push_back(tmp); fml = m.mk_app(a->get_decl(), i_args.size(), i_args.c_ptr()); } else { subfml = m.mk_app(a->get_decl(), r_args.size(), r_args.c_ptr()); i_args.push_back(subfml); fml = m.mk_app(a->get_decl(), i_args.size(), i_args.c_ptr()); } } else { subfml = fml; } } app* get_branch_id(app* x) { return m_var2branch[x]; } bool extract_partition(ptr_vector& vars) { if (m_partition.empty()) { return false; } unsigned_vector& vec = m_partition.back();; for (unsigned i = 0; i < vec.size(); ++i) { vars.push_back(m_current->free_var(vec[i])); } m_partition.pop_back(); return true; } enum update_status { CHOOSE_VAR, NEED_PROPAGATION }; update_status update_current(model_evaluator& model_eval, bool apply) { SASSERT(m_fml); m_current = &m_root; rational branch, nb; while (true) { SASSERT(m_current->fml()); if (m_current->is_unit()) { m_current = m_current->child(); continue; } if (!m_current->has_var()) { return CHOOSE_VAR; } app* x = m_current->var(); app* b = get_branch_id(x); nb = m_current->get_num_branches(); expr_ref fml(m_current->fml(), m); if (!eval(model_eval, b, branch) || branch >= nb) { TRACE("qe", tout << "evaluation failed: setting branch to 0\n";); branch = rational::zero(); } SASSERT(!branch.is_neg()); if (!m_current->has_branch(branch)) { if (apply) { app_ref assignment(mk_eq_value(b, branch), m); m_current = m_current->add_child(branch, assignment); plugin(x).assign(contains(x), fml, branch); m_current->consume_vars(m_new_vars); } return NEED_PROPAGATION; } m_current = m_current->child(branch); if (m_current->fml() == nullptr) { SASSERT(!m_current->has_var()); if (apply) { expr_ref def(m); plugin(x).subst(contains(x), branch, fml, m_defs?&def:nullptr); SASSERT(!contains(x)(fml)); m_current->consume_vars(m_new_vars); m_current->init(fml); m_current->add_def(x, def); normalize(*m_current); } return CHOOSE_VAR; } } } void pop(model_evaluator& model_eval) { // // Eliminate trivial quantifiers by solving // variables that can be eliminated. // solve_vars(); expr* fml = m_current->fml(); // we are done splitting. if (m_current->num_free_vars() == 0) { block_assignment(); return; } expr_ref fml_closed(m), fml_open(m), fml_mixed(m); unsigned num_vars = m_current->num_free_vars(); ptr_vector cont; ptr_vector vars; for (unsigned i = 0; i < num_vars; ++i) { cont.push_back(&contains(i)); vars.push_back(m_current->free_var(i)); } m_conjs.get_partition(fml, num_vars, vars.c_ptr(), fml_closed, fml_mixed, fml_open); if (m.is_and(fml_open) && m_conjs.partition_vars( num_vars, cont.c_ptr(), to_app(fml_open)->get_num_args(), to_app(fml_open)->get_args(), m_partition)) { process_partition(); return; } // // The closed portion of the formula // can be used as the quantifier-free portion, // unless the current state is unsatisfiable. // if (m.is_true(fml_mixed)) { SASSERT(l_true == m_solver.check()); m_current = m_current->add_child(fml_closed); for (unsigned i = 0; m_defs && i < m_current->num_free_vars(); ++i) { expr_ref val(m); app* x = m_current->free_var(i); model_eval(x, val); // hack: assign may add new variables to the branch. if (val == x) { model_ref model; lbool is_sat = m_solver.check(); if (is_sat == l_undef) return; m_solver.get_model(model); SASSERT(is_sat == l_true); model_evaluator model_eval2(*model); model_eval2.set_model_completion(true); model_eval2(x, val); } TRACE("qe", tout << mk_pp(x,m) << " " << mk_pp(val, m) << "\n";); m_current->add_def(x, val); } m_current->reset_free_vars(); block_assignment(); return; } // // one variable is to be processed. // constrain_assignment(); } void normalize(search_tree& st) { normalize(st.fml_ref(), st.pos_atoms(), st.neg_atoms()); } void normalize(expr_ref& result, atom_set& pos, atom_set& neg) { m_rewriter(result); bool simplified = true; while (simplified) { simplified = false; for (unsigned i = 0; !simplified && i < m_plugins.size(); ++i) { qe_solver_plugin* pl = m_plugins[i]; simplified = pl && pl->simplify(result); } } TRACE("qe_verbose", tout << "simp: " << mk_ismt2_pp(result.get(), m) << "\n";); m_nnf(result, pos, neg); TRACE("qe", tout << "nnf: " << mk_ismt2_pp(result.get(), m) << "\n";); } // // variable queuing. // // ------------------------------------------------ // propagate the assignments to branch // literals to implied constraints on the // variable. // bool get_propagate_value(model_evaluator& model_eval, search_tree* node, app*& b, rational& b_val) { return node->has_var() && eval(model_eval, get_branch_id(node->var()), b_val); } bool can_propagate_assignment(model_evaluator& model_eval) { return m_fml && NEED_PROPAGATION == update_current(model_eval, false); } void propagate_assignment(model_evaluator& model_eval) { if (m_fml) { update_current(model_eval, true); } } // // evaluate the Boolean or bit-vector 'b' in // the current model // bool eval(model_evaluator& model_eval, app* b, rational& val) { unsigned bv_size; expr_ref res(m); model_eval(b, res); SASSERT(m.is_bool(b) || m_bv.is_bv(b)); if (m.is_true(res)) { val = rational::one(); return true; } else if (m.is_false(res)) { val = rational::zero(); return true; } else if (m_bv.is_numeral(res, val, bv_size)) { return true; } else { return false; } } // // create literal 'b = r' // app* mk_eq_value(app* b, rational const& vl) { if (m.is_bool(b)) { if (vl.is_zero()) return to_app(mk_not(m, b)); if (vl.is_one()) return b; UNREACHABLE(); } SASSERT(m_bv.is_bv(b)); app_ref value(m_bv.mk_numeral(vl, m_bv.get_bv_size(b)), m); return m.mk_eq(b, value); } bool is_eq_value(app* e, app*& bv, rational& v) { unsigned sz = 0; if (!m.is_eq(e)) return false; expr* e0 = e->get_arg(0), *e1 = e->get_arg(1); if (!m_bv.is_bv(e0)) return false; if (m_bv.is_numeral(e0, v, sz) && is_app(e1)) { bv = to_app(e1); return true; } if (m_bv.is_numeral(e1, v, sz) && is_app(e0)) { bv = to_app(e0); return true; } return false; } // // the current state is satisfiable. // all bound decisions have been made. // void block_assignment() { TRACE("qe_verbose", m_solver.display(tout);); add_constraint(true); } // // The variable v is to be assigned a value in a range. // void constrain_assignment() { SASSERT(m_current->fml()); rational k; app* x; if (!find_min_weight(x, k)) { return; } m_current->set_var(x, k); TRACE("qe", tout << mk_pp(x, m) << " := " << k << "\n";); if (m_bv.is_bv(x)) { return; } app* b = get_branch_id(x); // // Create implication: // // assign_0 /\ ... /\ assign_{v-1} => b(v) <= k-1 // where // - assign_i is the current assignment: i = b(i) // - k is the number of cases for v // if (m.is_bool(b)) { SASSERT(k <= rational(2)); return; } SASSERT(m_bv.is_bv(b)); SASSERT(k.is_pos()); app_ref max_val(m_bv.mk_numeral(k-rational::one(), m_bv.get_bv_size(b)), m); expr_ref ub(m_bv.mk_ule(b, max_val), m); add_constraint(true, ub); } // // Process the partition stored in m_vars. // void process_partition() { expr_ref fml(m_current->fml(), m); ptr_vector vars; bool closed = true; while (extract_partition(vars)) { lbool r = m_qe.eliminate_exists(vars.size(), vars.c_ptr(), fml, m_free_vars, m_get_first, m_defs); vars.reset(); closed = closed && (r != l_undef); } TRACE("qe", tout << fml << " free: " << m_current->free_vars() << "\n";); m_current->add_child(fml)->reset_free_vars(); block_assignment(); } // variable queueing. contains_app& contains(app* x) { return *m_var2contains[x]; } bool find_min_weight(app*& x, rational& num_branches) { SASSERT(!m_current->has_var()); while (m_current->num_free_vars() > 0) { unsigned weight = UINT_MAX; unsigned nv = m_current->num_free_vars(); expr* fml = m_current->fml(); unsigned index = 0; for (unsigned i = 0; i < nv; ++i) { x = get_var(i); if (!has_plugin(x)) { break; } unsigned weight1 = plugin(get_var(i)).get_weight(contains(i), fml); if (weight1 < weight) { index = i; } } x = get_var(index); if (has_plugin(x) && plugin(x).get_num_branches(contains(x), fml, num_branches)) { return true; } TRACE("qe", tout << "setting variable " << mk_pp(x, m) << " free\n";); m_free_vars.push_back(x); m_current->del_var(x); } return false; } // // Solve for variables in fml. // Add a unit branch when variables are solved. // void solve_vars() { bool solved = true; while (solved) { expr_ref fml(m_current->fml(), m); TRACE("qe", tout << fml << "\n";); conj_enum conjs(m, fml); solved = false; for (unsigned i = 0; !solved && i < m_plugins.size(); ++i) { qe_solver_plugin* p = m_plugins[i]; solved = p && p->solve(conjs, fml); SASSERT(m_new_vars.empty()); } } } }; // ------------------------------------------------ // quant_elim class quant_elim_new : public quant_elim { ast_manager& m; smt_params& m_fparams; expr_ref m_assumption; bool m_produce_models; ptr_vector m_plugins; bool m_eliminate_variables_as_block; public: quant_elim_new(ast_manager& m, smt_params& p) : m(m), m_fparams(p), m_assumption(m), m_produce_models(m_fparams.m_model), m_eliminate_variables_as_block(true) { } ~quant_elim_new() override { reset(); } void reset() { for (unsigned i = 0; i < m_plugins.size(); ++i) { dealloc(m_plugins[i]); } } void checkpoint() { if (m.canceled()) throw tactic_exception(m.limit().get_cancel_msg()); } void collect_statistics(statistics & st) const override { for (unsigned i = 0; i < m_plugins.size(); ++i) { m_plugins[i]->collect_statistics(st); } } void updt_params(params_ref const& p) override { m_eliminate_variables_as_block = p.get_bool("eliminate_variables_as_block", m_eliminate_variables_as_block); } void eliminate(bool is_forall, unsigned num_vars, app* const* vars, expr_ref& fml) override { if (is_forall) { eliminate_forall_bind(num_vars, vars, fml); } else { eliminate_exists_bind(num_vars, vars, fml); } } virtual void bind_variables(unsigned num_vars, app* const* vars, expr_ref& fml) { if (num_vars > 0) { ptr_vector sorts; vector names; ptr_vector free_vars; for (unsigned i = 0; i < num_vars; ++i) { contains_app contains_x(m, vars[i]); if (contains_x(fml)) { sorts.push_back(m.get_sort(vars[i])); names.push_back(vars[i]->get_decl()->get_name()); free_vars.push_back(vars[i]); } } if (!free_vars.empty()) { expr_ref tmp(m); expr_abstract(m, 0, free_vars.size(), (expr*const*)free_vars.c_ptr(), fml, tmp); fml = m.mk_exists(free_vars.size(), sorts.c_ptr(), names.c_ptr(), tmp, 1); } } } void set_assumption(expr* fml) override { m_assumption = fml; } lbool eliminate_exists( unsigned num_vars, app* const* vars, expr_ref& fml, app_ref_vector& free_vars, bool get_first, guarded_defs* defs) override { if (get_first) { return eliminate_block(num_vars, vars, fml, free_vars, get_first, defs); } if (m_eliminate_variables_as_block) { return eliminate_block(num_vars, vars, fml, free_vars, get_first, defs); } for (unsigned i = 0; i < num_vars; ++i) { lbool r = eliminate_block(1, vars+i, fml, free_vars, get_first, defs); switch(r) { case l_false: return l_false; case l_undef: free_vars.append(num_vars-i-1,vars+1+i); return l_undef; default: break; } } return l_true; } private: lbool eliminate_block( unsigned num_vars, app* const* vars, expr_ref& fml, app_ref_vector& free_vars, bool get_first, guarded_defs* defs) { checkpoint(); if (has_quantifiers(fml)) { free_vars.append(num_vars, vars); return l_undef; } flet fl1(m_fparams.m_model, true); flet fl2(m_fparams.m_simplify_bit2int, true); flet fl3(m_fparams.m_arith_enum_const_mod, true); flet fl4(m_fparams.m_bv_enable_int2bv2int, true); flet fl5(m_fparams.m_array_canonize_simplify, true); flet fl6(m_fparams.m_relevancy_lvl, 0); TRACE("qe", for (unsigned i = 0; i < num_vars; ++i) { tout << mk_ismt2_pp(vars[i], m) << " "; } tout << "\n"; tout << mk_ismt2_pp(fml, m) << "\n"; ); expr_ref fml0(fml, m); quant_elim_plugin* th; pop_context(th); th->check(num_vars, vars, m_assumption, fml, get_first, free_vars, defs); push_context(th); TRACE("qe", for (unsigned i = 0; i < num_vars; ++i) { tout << mk_ismt2_pp(vars[i], m) << " "; } tout << "\n"; tout << "Input:\n" << mk_ismt2_pp(fml0, m) << "\n"; tout << "Result:\n" << mk_ismt2_pp(fml, m) << "\n";); if (m.is_false(fml)) { return l_false; } if (free_vars.empty()) { return l_true; } return l_undef; } void pop_context(quant_elim_plugin*& th) { if (m_plugins.empty()) { th = alloc(quant_elim_plugin, m, *this, m_fparams); th->add_plugin(mk_bool_plugin(*th)); th->add_plugin(mk_bv_plugin(*th)); th->add_plugin(mk_arith_plugin(*th, m_produce_models, m_fparams)); th->add_plugin(mk_array_plugin(*th)); th->add_plugin(mk_datatype_plugin(*th)); th->add_plugin(mk_dl_plugin(*th)); } else { th = m_plugins.back(); m_plugins.pop_back(); } } void push_context(quant_elim_plugin* th) { m_plugins.push_back(th); th->reset(); } void eliminate_exists_bind(unsigned num_vars, app* const* vars, expr_ref& fml) { checkpoint(); app_ref_vector free_vars(m); eliminate_exists(num_vars, vars, fml, free_vars, false, nullptr); bind_variables(free_vars.size(), free_vars.c_ptr(), fml); } void eliminate_forall_bind(unsigned num_vars, app* const* vars, expr_ref& fml) { expr_ref tmp(m); bool_rewriter rw(m); rw.mk_not(fml, tmp); eliminate_exists_bind(num_vars, vars, tmp); rw.mk_not(tmp, fml); } }; // ------------------------------------------------ // expr_quant_elim expr_quant_elim::expr_quant_elim(ast_manager& m, smt_params const& fp, params_ref const& p): m(m), m_fparams(fp), m_params(p), m_trail(m), m_qe(nullptr), m_assumption(m.mk_true()) { } expr_quant_elim::~expr_quant_elim() { dealloc(m_qe); } void expr_quant_elim::operator()(expr* assumption, expr* fml, expr_ref& result) { TRACE("qe", tout << "elim input\n" << mk_ismt2_pp(fml, m) << "\n";); expr_ref_vector bound(m); result = fml; m_assumption = assumption; instantiate_expr(bound, result); elim(result); m_trail.reset(); m_visited.reset(); abstract_expr(bound.size(), bound.c_ptr(), result); TRACE("qe", tout << "elim result\n" << mk_ismt2_pp(result, m) << "\n";); } void expr_quant_elim::updt_params(params_ref const& p) { init_qe(); m_qe->updt_params(p); } void expr_quant_elim::collect_param_descrs(param_descrs& r) { r.insert("eliminate_variables_as_block", CPK_BOOL, "(default: true) eliminate variables as a block (true) or one at a time (false)"); } void expr_quant_elim::init_qe() { if (!m_qe) { m_qe = alloc(quant_elim_new, m, const_cast(m_fparams)); } } void expr_quant_elim::instantiate_expr(expr_ref_vector& bound, expr_ref& fml) { expr_free_vars fv; fv(fml); fv.set_default_sort(m.mk_bool_sort()); if (!fv.empty()) { expr_ref tmp(m); for (unsigned i = fv.size(); i > 0;) { --i; bound.push_back(m.mk_fresh_const("bound", fv[i])); } var_subst subst(m); fml = subst(fml, bound.size(), bound.c_ptr()); } } void expr_quant_elim::abstract_expr(unsigned sz, expr* const* bound, expr_ref& fml) { if (sz > 0) { expr_ref tmp(m); expr_abstract(m, 0, sz, bound, fml, tmp); fml = tmp; } } void extract_vars(quantifier* q, expr_ref& new_body, app_ref_vector& vars) { ast_manager& m = new_body.get_manager(); expr_ref tmp(m); unsigned nd = q->get_num_decls(); for (unsigned i = 0; i < nd; ++i) { vars.push_back(m.mk_fresh_const("x",q->get_decl_sort(i))); } expr* const* exprs = (expr* const*)(vars.c_ptr()); var_subst subst(m); tmp = subst(new_body, vars.size(), exprs); inv_var_shifter shift(m); shift(tmp, vars.size(), new_body); } void expr_quant_elim::elim(expr_ref& result) { expr_ref tmp(m); ptr_vector todo; m_trail.push_back(result); todo.push_back(result); expr* e = nullptr, *r = nullptr; while (!todo.empty()) { e = todo.back(); if (m_visited.contains(e)) { todo.pop_back(); continue; } switch(e->get_kind()) { case AST_APP: { app* a = to_app(e); expr_ref_vector args(m); unsigned num_args = a->get_num_args(); bool all_visited = true; for (unsigned i = 0; i < num_args; ++i) { if (m_visited.find(a->get_arg(i), r)) { args.push_back(r); } else { todo.push_back(a->get_arg(i)); all_visited = false; } } if (all_visited) { r = m.mk_app(a->get_decl(), args.size(), args.c_ptr()); todo.pop_back(); m_trail.push_back(r); m_visited.insert(e, r); } break; } case AST_QUANTIFIER: { app_ref_vector vars(m); quantifier* q = to_quantifier(e); if (is_lambda(q)) { tmp = e; } else { bool is_fa = is_forall(q); tmp = q->get_expr(); extract_vars(q, tmp, vars); elim(tmp); init_qe(); m_qe->set_assumption(m_assumption); m_qe->eliminate(is_fa, vars.size(), vars.c_ptr(), tmp); } m_trail.push_back(tmp); m_visited.insert(e, tmp); todo.pop_back(); break; } default: UNREACHABLE(); break; } } VERIFY (m_visited.find(result, e)); result = e; } void expr_quant_elim::collect_statistics(statistics & st) const { if (m_qe) { m_qe->collect_statistics(st); } } lbool expr_quant_elim::first_elim(unsigned num_vars, app* const* vars, expr_ref& fml, def_vector& defs) { app_ref_vector fvs(m); init_qe(); guarded_defs gdefs(m); lbool res = m_qe->eliminate_exists(num_vars, vars, fml, fvs, true, &gdefs); if (gdefs.size() > 0) { defs.reset(); defs.append(gdefs.defs(0)); fml = gdefs.guard(0); } return res; } bool expr_quant_elim::solve_for_var(app* var, expr* fml, guarded_defs& defs) { return solve_for_vars(1,&var, fml, defs); } bool expr_quant_elim::solve_for_vars(unsigned num_vars, app* const* vars, expr* _fml, guarded_defs& defs) { app_ref_vector fvs(m); expr_ref fml(_fml, m); TRACE("qe", tout << mk_pp(fml, m) << "\n";); init_qe(); lbool is_sat = m_qe->eliminate_exists(num_vars, vars, fml, fvs, false, &defs); return is_sat != l_undef; } #if 0 // ------------------------------------------------ // expr_quant_elim_star1 bool expr_quant_elim_star1::visit_quantifier(quantifier * q) { if (!is_target(q)) { return true; } bool visited = true; visit(q->get_expr(), visited); return visited; } void expr_quant_elim_star1::reduce1_quantifier(quantifier * q) { if (!is_target(q)) { cache_result(q, q, 0); return; } quantifier_ref new_q(m); expr * new_body = 0; proof * new_pr; get_cached(q->get_expr(), new_body, new_pr); new_q = m.update_quantifier(q, new_body); expr_ref r(m); m_quant_elim(m_assumption, new_q, r); if (q == r.get()) { cache_result(q, q, 0); return; } proof_ref pr(m); if (m.proofs_enabled()) { pr = m.mk_rewrite(q, r); // TODO: improve justification } cache_result(q, r, pr); } expr_quant_elim_star1::expr_quant_elim_star1(ast_manager& m, smt_params const& p): simplifier(m), m_quant_elim(m, p), m_assumption(m.mk_true()) { } #endif void hoist_exists(expr_ref& fml, app_ref_vector& vars) { quantifier_hoister hoister(fml.get_manager()); hoister.pull_exists(fml, vars, fml); } void mk_exists(unsigned num_bound, app* const* vars, expr_ref& fml) { ast_manager& m = fml.get_manager(); expr_ref tmp(m); expr_abstract(m, 0, num_bound, (expr*const*)vars, fml, tmp); ptr_vector sorts; svector names; for (unsigned i = 0; i < num_bound; ++i) { sorts.push_back(vars[i]->get_decl()->get_range()); names.push_back(vars[i]->get_decl()->get_name()); } if (num_bound > 0) { tmp = m.mk_exists(num_bound, sorts.c_ptr(), names.c_ptr(), tmp, 1); } fml = tmp; } class simplify_solver_context : public i_solver_context { ast_manager& m; smt_params m_fparams; app_ref_vector* m_vars; expr_ref* m_fml; ptr_vector m_contains; atom_set m_pos; atom_set m_neg; public: simplify_solver_context(ast_manager& m): m(m), m_vars(nullptr), m_fml(nullptr) { add_plugin(mk_bool_plugin(*this)); add_plugin(mk_arith_plugin(*this, false, m_fparams)); } void updt_params(params_ref const& p) { m_fparams.updt_params(p); } ~simplify_solver_context() override { reset(); } void solve(expr_ref& fml, app_ref_vector& vars) { init(fml, vars); bool solved = true; do { conj_enum conjs(m, fml); solved = false; for (unsigned i = 0; !solved && i < m_plugins.size(); ++i) { qe_solver_plugin* p = m_plugins[i]; solved = p && p->solve(conjs, fml); } TRACE("qe", tout << (solved?"solved":"not solved") << "\n"; if (solved) tout << mk_ismt2_pp(fml, m) << "\n";); } while (solved); } ast_manager& get_manager() override { return m; } atom_set const& pos_atoms() const override { return m_pos; } atom_set const& neg_atoms() const override { return m_neg; } // Access current set of variables to solve unsigned get_num_vars() const override { return m_vars->size(); } app* get_var(unsigned idx) const override { return (*m_vars)[idx].get(); } app_ref_vector const& get_vars() const override { return *m_vars; } bool is_var(expr* e, unsigned& idx) const override { for (unsigned i = 0; i < m_vars->size(); ++i) { if ((*m_vars)[i].get() == e) { idx = i; return true; } } return false; } contains_app& contains(unsigned idx) override { return *m_contains[idx]; } // callback to replace variable at index 'idx' with definition 'def' and updated formula 'fml' void elim_var(unsigned idx, expr* fml, expr* def) override { TRACE("qe", tout << mk_pp(m_vars->get(idx), m) << " " << mk_pp(fml, m) << "\n";); *m_fml = fml; m_vars->set(idx, m_vars->get(m_vars->size()-1)); m_vars->pop_back(); dealloc(m_contains[idx]); m_contains[idx] = m_contains[m_contains.size()-1]; m_contains.pop_back(); } // callback to add new variable to branch. void add_var(app* x) override { TRACE("qe", tout << "add var: " << mk_pp(x, m) << "\n";); m_vars->push_back(x); } // callback to add constraints in branch. void add_constraint(bool use_var, expr* l1 = nullptr, expr* l2 = nullptr, expr* l3 = nullptr) override { UNREACHABLE(); } // eliminate finite domain variable 'var' from fml. void blast_or(app* var, expr_ref& fml) override { UNREACHABLE(); } private: void reset() { for (unsigned i = 0; i < m_contains.size(); ++i) { dealloc (m_contains[i]); } m_contains.reset(); } void init(expr_ref& fml, app_ref_vector& vars) { reset(); m_fml = &fml; m_vars = &vars; for (unsigned i = 0; i < vars.size(); ++i) { m_contains.push_back(alloc(contains_app, m, vars[i].get())); } } }; class simplify_rewriter_cfg::impl { ast_manager& m; simplify_solver_context m_ctx; public: impl(ast_manager& m) : m(m), m_ctx(m) {} void updt_params(params_ref const& p) { m_ctx.updt_params(p); } void collect_statistics(statistics & st) const { m_ctx.collect_statistics(st); } 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 ) { if (is_lambda(old_q)) { return false; } // bool is_forall = old_q->is_forall(); app_ref_vector vars(m); TRACE("qe", tout << "simplifying" << mk_pp(new_body, m) << "\n";); result = new_body; extract_vars(old_q, result, vars); TRACE("qe", tout << "variables extracted" << mk_pp(result, m) << "\n";); if (is_forall(old_q)) { result = mk_not(m, result); } m_ctx.solve(result, vars); if (is_forall(old_q)) { expr* e = nullptr; result = m.is_not(result, e)?e:mk_not(m, result); } var_shifter shift(m); shift(result, vars.size(), result); expr_abstract(m, 0, vars.size(), (expr*const*)vars.c_ptr(), result, result); TRACE("qe", tout << "abstracted" << mk_pp(result, m) << "\n";); ptr_vector sorts; svector names; for (unsigned i = 0; i < vars.size(); ++i) { sorts.push_back(vars[i]->get_decl()->get_range()); names.push_back(vars[i]->get_decl()->get_name()); } if (!vars.empty()) { result = m.mk_quantifier(old_q->get_kind(), vars.size(), sorts.c_ptr(), names.c_ptr(), result, 1); } result_pr = nullptr; return true; } }; simplify_rewriter_cfg::simplify_rewriter_cfg(ast_manager& m) { imp = alloc(simplify_rewriter_cfg::impl, m); } simplify_rewriter_cfg::~simplify_rewriter_cfg() { dealloc(imp); } bool simplify_rewriter_cfg::reduce_quantifier( quantifier * old_q, expr * new_body, expr * const * new_patterns, expr * const * new_no_patterns, expr_ref & result, proof_ref & result_pr ) { return imp->reduce_quantifier(old_q, new_body, new_patterns, new_no_patterns, result, result_pr); } void simplify_rewriter_cfg::updt_params(params_ref const& p) { imp->updt_params(p); } void simplify_rewriter_cfg::collect_statistics(statistics & st) const { imp->collect_statistics(st); } bool simplify_rewriter_cfg::pre_visit(expr* e) { if (!is_quantifier(e)) return true; quantifier * q = to_quantifier(e); return (q->get_num_patterns() == 0 && q->get_num_no_patterns() == 0); } void simplify_exists(app_ref_vector& vars, expr_ref& fml) { ast_manager& m = fml.get_manager(); simplify_solver_context ctx(m); ctx.solve(fml, vars); } } template class rewriter_tpl; z3-z3-4.8.7/src/qe/qe.h000066400000000000000000000275331356505360400144130ustar00rootroot00000000000000/*++ Copyright (c) 2010 Microsoft Corporation Module Name: qe.h Abstract: Quantifier-elimination procedures Author: Nikolaj Bjorner (nbjorner) 2010-02-19 Revision History: --*/ #ifndef QE_H_ #define QE_H_ #include "ast/ast.h" #include "smt/params/smt_params.h" #include "util/statistics.h" #include "util/lbool.h" #include "ast/expr_functors.h" #include "ast/rewriter/rewriter.h" #include "model/model.h" #include "util/params.h" namespace qe { class i_nnf_atom { public: virtual void operator()(expr* e, bool pol, expr_ref& result) = 0; }; typedef obj_hashtable atom_set; class qe_solver_plugin; class i_solver_context { protected: class is_relevant : public i_expr_pred { i_solver_context& m_s; public: is_relevant(i_solver_context& s):m_s(s) {} bool operator()(expr* e) override; }; class mk_atom_fn : public i_nnf_atom { i_solver_context& m_s; public: mk_atom_fn(i_solver_context& s) : m_s(s) {} void operator()(expr* e, bool p, expr_ref& result) override; }; is_relevant m_is_relevant; mk_atom_fn m_mk_atom; ptr_vector m_plugins; // fid -> plugin public: i_solver_context():m_is_relevant(*this), m_mk_atom(*this) {} virtual ~i_solver_context(); void add_plugin(qe_solver_plugin* p); bool has_plugin(app* x); qe_solver_plugin& plugin(app* x); qe_solver_plugin& plugin(unsigned fid) { return *m_plugins[fid]; } void mk_atom(expr* e, bool p, expr_ref& result); virtual ast_manager& get_manager() = 0; // set of atoms in current formula. virtual atom_set const& pos_atoms() const = 0; virtual atom_set const& neg_atoms() const = 0; // Access current set of variables to solve virtual unsigned get_num_vars() const = 0; virtual app* get_var(unsigned idx) const = 0; virtual app_ref_vector const& get_vars() const = 0; virtual bool is_var(expr* e, unsigned& idx) const; virtual contains_app& contains(unsigned idx) = 0; // callback to replace variable at index 'idx' with definition 'def' and updated formula 'fml' virtual void elim_var(unsigned idx, expr* fml, expr* def) = 0; // callback to add new variable to branch. virtual void add_var(app* x) = 0; // callback to add constraints in branch. virtual void add_constraint(bool use_var, expr* l1 = nullptr, expr* l2 = nullptr, expr* l3 = nullptr) = 0; // eliminate finite domain variable 'var' from fml. virtual void blast_or(app* var, expr_ref& fml) = 0; i_expr_pred& get_is_relevant() { return m_is_relevant; } i_nnf_atom& get_mk_atom() { return m_mk_atom; } void collect_statistics(statistics & st) const; }; class conj_enum { ast_manager& m; expr_ref_vector m_conjs; public: conj_enum(ast_manager& m, expr* e); class iterator { conj_enum* m_super; unsigned m_index; public: iterator(conj_enum& c, bool first) : m_super(&c), m_index(first?0:c.m_conjs.size()) {} expr* operator*() { return m_super->m_conjs[m_index].get(); } iterator& operator++() { m_index++; return *this; } bool operator==(iterator const& it) const { return m_index == it.m_index; } bool operator!=(iterator const& it) const { return m_index != it.m_index; } }; void extract_equalities(expr_ref_vector& eqs); iterator begin() { return iterator(*this, true); } iterator end() { return iterator(*this, false); } }; // // interface for plugins to eliminate quantified variables. // class qe_solver_plugin { protected: ast_manager& m; family_id m_fid; i_solver_context& m_ctx; public: qe_solver_plugin(ast_manager& m, family_id fid, i_solver_context& ctx) : m(m), m_fid(fid), m_ctx(ctx) {} virtual ~qe_solver_plugin() {} family_id get_family_id() { return m_fid; } /** \brief Return number of case splits for variable x in fml. */ virtual bool get_num_branches(contains_app& x, expr* fml, rational& num_branches) = 0; /** \brief Given a case split index 'vl' assert the constraints associated with it. */ virtual void assign(contains_app& x, expr* fml, rational const& vl) = 0; /** \brief The case splits associated with 'vl' are satisfiable. Apply the elimination for fml corresponding to case split. If def is non-null, then retrieve the realizer corresponding to the case split. */ virtual void subst(contains_app& x, rational const& vl, expr_ref& fml, expr_ref* def) = 0; /** \brief solve quantified variable. */ virtual bool solve(conj_enum& conjs, expr* fml) = 0; /** \brief project variable x for fml given model. Assumption: model |= fml[x] Projection updates fml to fml', such that: - fml' -> fml - model |= fml' - fml' does not contain x return false if the method is not implemented. */ virtual bool project(contains_app& x, model_ref& model, expr_ref& fml) { return false; } /** \brief assign branching penalty to variable x for 'fml'. */ virtual unsigned get_weight(contains_app& x, expr* fml) { return UINT_MAX; } /** \brief simplify formula. */ virtual bool simplify(expr_ref& fml) { return false; } /** \brief Normalize literal during NNF conversion. */ virtual bool mk_atom(expr* e, bool p, expr_ref& result) { return false; } /** \brief Determine whether sub-term is uninterpreted with respect to quantifier elimination. */ virtual bool is_uninterpreted(app* f) { return true; } }; qe_solver_plugin* mk_datatype_plugin(i_solver_context& ctx); qe_solver_plugin* mk_bool_plugin(i_solver_context& ctx); qe_solver_plugin* mk_bv_plugin(i_solver_context& ctx); qe_solver_plugin* mk_dl_plugin(i_solver_context& ctx); qe_solver_plugin* mk_array_plugin(i_solver_context& ctx); qe_solver_plugin* mk_arith_plugin(i_solver_context& ctx, bool produce_models, smt_params& p); void extract_vars(quantifier* q, expr_ref& new_body, app_ref_vector& vars); class def_vector { func_decl_ref_vector m_vars; expr_ref_vector m_defs; def_vector& operator=(def_vector const& other); public: def_vector(ast_manager& m): m_vars(m), m_defs(m) {} def_vector(def_vector const& other): m_vars(other.m_vars), m_defs(other.m_defs) {} void push_back(func_decl* v, expr* e) { m_vars.push_back(v); m_defs.push_back(e); } void reset() { m_vars.reset(); m_defs.reset(); } void append(def_vector const& o) { m_vars.append(o.m_vars); m_defs.append(o.m_defs); } unsigned size() const { return m_defs.size(); } void shrink(unsigned sz) { m_vars.shrink(sz); m_defs.shrink(sz); } bool empty() const { return m_defs.empty(); } func_decl* var(unsigned i) const { return m_vars[i]; } expr* def(unsigned i) const { return m_defs[i]; } expr_ref_vector::element_ref def_ref(unsigned i) { return m_defs[i]; } void normalize(); void project(unsigned num_vars, app* const* vars); }; /** \brief Guarded definitions. 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. 2. Each guard is equivalent to fml where 'vars' are replaced by the substitution associated with the guard. */ class guarded_defs { expr_ref_vector m_guards; vector m_defs; bool inv(); public: guarded_defs(ast_manager& m): m_guards(m) { SASSERT(inv()); } void add(expr* guard, def_vector const& defs); unsigned size() const { return m_guards.size(); } def_vector const& defs(unsigned i) const { return m_defs[i]; } expr* guard(unsigned i) const { return m_guards[i]; } std::ostream& display(std::ostream& out) const; void project(unsigned num_vars, app* const* vars); }; class quant_elim; class expr_quant_elim { ast_manager& m; smt_params const& m_fparams; params_ref m_params; expr_ref_vector m_trail; obj_map m_visited; quant_elim* m_qe; expr* m_assumption; public: expr_quant_elim(ast_manager& m, smt_params const& fp, params_ref const& p = params_ref()); ~expr_quant_elim(); void operator()(expr* assumption, expr* fml, expr_ref& result); void collect_statistics(statistics & st) const; void updt_params(params_ref const& p); void collect_param_descrs(param_descrs& r); /** \brief try to eliminate 'vars' and find first satisfying assignment. return l_true if satisfiable, l_false if unsatisfiable, l_undef if the formula could not be satisfied modulo eliminating the quantified variables. */ lbool first_elim(unsigned num_vars, app* const* vars, expr_ref& fml, def_vector& defs); /** \brief solve for (exists (var) fml). Return false if operation failed. Return true and list of pairs (t_i, fml_i) in such that fml_1 \/ ... \/ fml_n == (exists (var) fml) and fml_i => fml[t_i] */ bool solve_for_var(app* var, expr* fml, guarded_defs& defs); bool solve_for_vars(unsigned num_vars, app* const* vars, expr* fml, guarded_defs& defs); private: void instantiate_expr(expr_ref_vector& bound, expr_ref& fml); void abstract_expr(unsigned sz, expr* const* bound, expr_ref& fml); void elim(expr_ref& result); void init_qe(); }; void hoist_exists(expr_ref& fml, app_ref_vector& vars); void mk_exists(unsigned num_vars, app* const* vars, expr_ref& fml); void get_nnf(expr_ref& fml, i_expr_pred& pred, i_nnf_atom& mk_atom, atom_set& pos, atom_set& neg); class simplify_rewriter_cfg : public default_rewriter_cfg { class impl; impl* imp; public: simplify_rewriter_cfg(ast_manager& m); ~simplify_rewriter_cfg(); 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); bool pre_visit(expr* e); void updt_params(params_ref const& p); void collect_statistics(statistics & st) const; }; class simplify_rewriter_star : public rewriter_tpl { simplify_rewriter_cfg m_cfg; public: simplify_rewriter_star(ast_manager& m): rewriter_tpl(m, false, m_cfg), m_cfg(m) {} void updt_params(params_ref const& p) { m_cfg.updt_params(p); } void collect_statistics(statistics & st) const { m_cfg.collect_statistics(st); } }; }; #endif z3-z3-4.8.7/src/qe/qe_arith.cpp000066400000000000000000000562451356505360400161370ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: qe_arith.cpp Abstract: Simple projection function for real arithmetic based on Loos-W. Author: Nikolaj Bjorner (nbjorner) 2013-09-12 Revision History: Moved projection functionality to model_based_opt module. 2016-06-26 --*/ #include "qe/qe_arith.h" #include "qe/qe_mbp.h" #include "ast/ast_util.h" #include "ast/arith_decl_plugin.h" #include "ast/ast_pp.h" #include "model/model_v2_pp.h" #include "ast/rewriter/th_rewriter.h" #include "ast/expr_functors.h" #include "ast/rewriter/expr_safe_replace.h" #include "math/simplex/model_based_opt.h" #include "model/model_evaluator.h" #include "model/model_smt2_pp.h" namespace qe { struct arith_project_plugin::imp { ast_manager& m; arith_util a; bool m_check_purified; // check that variables are properly pure void insert_mul(expr* x, rational const& v, obj_map& ts) { // TRACE("qe", tout << "Adding variable " << mk_pp(x, m) << " " << v << "\n";); rational w; if (ts.find(x, w)) { ts.insert(x, w + v); } else { ts.insert(x, v); } } // // extract linear inequalities from literal 'lit' into the model-based optimization manager 'mbo'. // It uses the current model to choose values for conditionals and it primes mbo with the current // interpretation of sub-expressions that are treated as variables for mbo. // bool linearize(opt::model_based_opt& mbo, model_evaluator& eval, expr* lit, expr_ref_vector& fmls, obj_map& tids) { obj_map ts; rational c(0), mul(1); expr_ref t(m); opt::ineq_type ty = opt::t_le; expr* e1, *e2; DEBUG_CODE(expr_ref val(m); eval(lit, val); CTRACE("qe", !m.is_true(val), tout << mk_pp(lit, m) << " := " << val << "\n";); SASSERT(m.is_true(val));); bool is_not = m.is_not(lit, lit); if (is_not) { mul.neg(); } SASSERT(!m.is_not(lit)); if ((a.is_le(lit, e1, e2) || a.is_ge(lit, e2, e1))) { linearize(mbo, eval, mul, e1, c, fmls, ts, tids); linearize(mbo, eval, -mul, e2, c, fmls, ts, tids); ty = is_not ? opt::t_lt : opt::t_le; } else if ((a.is_lt(lit, e1, e2) || a.is_gt(lit, e2, e1))) { linearize(mbo, eval, mul, e1, c, fmls, ts, tids); linearize(mbo, eval, -mul, e2, c, fmls, ts, tids); ty = is_not ? opt::t_le: opt::t_lt; } else if (m.is_eq(lit, e1, e2) && !is_not && is_arith(e1)) { linearize(mbo, eval, mul, e1, c, fmls, ts, tids); linearize(mbo, eval, -mul, e2, c, fmls, ts, tids); ty = opt::t_eq; } else if (m.is_eq(lit, e1, e2) && is_not && is_arith(e1)) { rational r1, r2; expr_ref val1 = eval(e1); expr_ref val2 = eval(e2); //TRACE("qe", tout << mk_pp(e1, m) << " " << val1 << "\n";); //TRACE("qe", tout << mk_pp(e2, m) << " " << val2 << "\n";); if (!a.is_numeral(val1, r1)) return false; if (!a.is_numeral(val2, r2)) return false; SASSERT(r1 != r2); if (r1 < r2) { std::swap(e1, e2); } ty = opt::t_lt; linearize(mbo, eval, mul, e1, c, fmls, ts, tids); linearize(mbo, eval, -mul, e2, c, fmls, ts, tids); } else if (m.is_distinct(lit) && !is_not && is_arith(to_app(lit)->get_arg(0))) { expr_ref val(m); rational r; app* alit = to_app(lit); vector > nums; for (expr* arg : *alit) { val = eval(arg); TRACE("qe", tout << mk_pp(arg, m) << " " << val << "\n";); if (!a.is_numeral(val, r)) return false; nums.push_back(std::make_pair(arg, r)); } std::sort(nums.begin(), nums.end(), compare_second()); for (unsigned i = 0; i + 1 < nums.size(); ++i) { SASSERT(nums[i].second < nums[i+1].second); expr_ref fml(a.mk_lt(nums[i].first, nums[i+1].first), m); if (!linearize(mbo, eval, fml, fmls, tids)) { return false; } } return true; } else if (m.is_distinct(lit) && is_not && is_arith(to_app(lit)->get_arg(0))) { // find the two arguments that are equal. // linearize these. map values; bool found_eq = false; for (unsigned i = 0; !found_eq && i < to_app(lit)->get_num_args(); ++i) { expr* arg1 = to_app(lit)->get_arg(i), *arg2 = nullptr; rational r; expr_ref val = eval(arg1); TRACE("qe", tout << mk_pp(arg1, m) << " " << val << "\n";); if (!a.is_numeral(val, r)) return false; if (values.find(r, arg2)) { ty = opt::t_eq; linearize(mbo, eval, mul, arg1, c, fmls, ts, tids); linearize(mbo, eval, -mul, arg2, c, fmls, ts, tids); found_eq = true; } else { values.insert(r, arg1); } } SASSERT(found_eq); } else { TRACE("qe", tout << "Skipping " << mk_pp(lit, m) << "\n";); return false; } vars coeffs; extract_coefficients(mbo, eval, ts, tids, coeffs); mbo.add_constraint(coeffs, c, ty); return true; } // // convert linear arithmetic term into an inequality for mbo. // void linearize(opt::model_based_opt& mbo, model_evaluator& eval, rational const& mul, expr* t, rational& c, expr_ref_vector& fmls, obj_map& ts, obj_map& tids) { expr* t1, *t2, *t3; rational mul1; expr_ref val(m); if (a.is_mul(t, t1, t2) && is_numeral(t1, mul1)) { linearize(mbo, eval, mul* mul1, t2, c, fmls, ts, tids); } else if (a.is_mul(t, t1, t2) && is_numeral(t2, mul1)) { linearize(mbo, eval, mul* mul1, t1, c, fmls, ts, tids); } else if (a.is_add(t)) { app* ap = to_app(t); for (expr* arg : *ap) { linearize(mbo, eval, mul, arg, c, fmls, ts, tids); } } else if (a.is_sub(t, t1, t2)) { linearize(mbo, eval, mul, t1, c, fmls, ts, tids); linearize(mbo, eval, -mul, t2, c, fmls, ts, tids); } else if (a.is_uminus(t, t1)) { linearize(mbo, eval, -mul, t1, c, fmls, ts, tids); } else if (a.is_numeral(t, mul1)) { c += mul*mul1; } else if (m.is_ite(t, t1, t2, t3)) { val = eval(t1); SASSERT(m.is_true(val) || m.is_false(val)); TRACE("qe", tout << mk_pp(t1, m) << " := " << val << "\n";); if (m.is_true(val)) { linearize(mbo, eval, mul, t2, c, fmls, ts, tids); fmls.push_back(t1); } else { expr_ref not_t1(mk_not(m, t1), m); fmls.push_back(not_t1); linearize(mbo, eval, mul, t3, c, fmls, ts, tids); } } else if (a.is_mod(t, t1, t2) && is_numeral(t2, mul1) && !mul1.is_zero()) { rational r; val = eval(t); VERIFY(a.is_numeral(val, r)); c += mul*r; // t1 mod mul1 == r rational c0(-r), mul0(1); obj_map ts0; linearize(mbo, eval, mul0, t1, c0, fmls, ts0, tids); vars coeffs; extract_coefficients(mbo, eval, ts0, tids, coeffs); mbo.add_divides(coeffs, c0, mul1); } else { insert_mul(t, mul, ts); } } bool is_numeral(expr* t, rational& r) { expr* t1, *t2; rational r1, r2; if (a.is_numeral(t, r)) { // no-op } else if (a.is_uminus(t, t1) && is_numeral(t1, r)) { r.neg(); } else if (a.is_mul(t)) { app* ap = to_app(t); r = rational(1); for (expr * arg : *ap) { if (!is_numeral(arg, r1)) return false; r *= r1; } } else if (a.is_add(t)) { app* ap = to_app(t); r = rational(0); for (expr * arg : *ap) { if (!is_numeral(arg, r1)) return false; r += r1; } } else if (a.is_sub(t, t1, t2) && is_numeral(t1, r1) && is_numeral(t2, r2)) { r = r1 - r2; } else { return false; } return true; } struct compare_second { bool operator()(std::pair const& a, std::pair const& b) const { return a.second < b.second; } }; bool is_arith(expr* e) { return a.is_int_real(e); } rational n_sign(rational const& b) { return rational(b.is_pos()?-1:1); } imp(ast_manager& m): m(m), a(m), m_check_purified(true) {} ~imp() {} bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) { return false; } bool operator()(model& model, app* v, app_ref_vector& vars, expr_ref_vector& lits) { app_ref_vector vs(m); vs.push_back(v); project(model, vs, lits, false); return vs.empty(); } typedef opt::model_based_opt::var var; typedef opt::model_based_opt::row row; typedef vector vars; expr_ref var2expr(ptr_vector const& index2expr, var const& v) { expr_ref t(index2expr[v.m_id], m); if (!v.m_coeff.is_one()) { t = a.mk_mul(a.mk_numeral(v.m_coeff, a.is_int(t)), t); } return t; } vector project(model& model, app_ref_vector& vars, expr_ref_vector& fmls, bool compute_def) { bool has_arith = false; for (expr* v : vars) { has_arith |= is_arith(v); } if (!has_arith) { return vector(); } model_evaluator eval(model); TRACE("qe", tout << model;); // eval.set_model_completion(true); opt::model_based_opt mbo; obj_map tids; expr_ref_vector pinned(m); unsigned j = 0; TRACE("qe", tout << "vars: " << vars << "\nfmls: " << fmls << "\n";); for (unsigned i = 0; i < fmls.size(); ++i) { expr * fml = fmls.get(i); if (!linearize(mbo, eval, fml, fmls, tids)) { TRACE("qe", tout << "could not linearize: " << mk_pp(fml, m) << "\n";); fmls[j++] = fml; } else { pinned.push_back(fml); } } fmls.shrink(j); // fmls holds residue, // mbo holds linear inequalities that are in scope // collect variables in residue an in tids. // filter variables that are absent from residue. // project those. // collect result of projection // return those to fmls. expr_mark var_mark, fmls_mark; for (app * v : vars) { var_mark.mark(v); if (is_arith(v) && !tids.contains(v)) { rational r; expr_ref val = eval(v); VERIFY(a.is_numeral(val, r)); TRACE("qe", tout << mk_pp(v, m) << " " << val << "\n";); tids.insert(v, mbo.add_var(r, a.is_int(v))); } } if (m_check_purified) { for (expr* fml : fmls) { mark_rec(fmls_mark, fml); } for (auto& kv : tids) { expr* e = kv.m_key; if (!var_mark.is_marked(e)) { mark_rec(fmls_mark, e); } } } ptr_vector index2expr; for (auto& kv : tids) { index2expr.setx(kv.m_value, kv.m_key, nullptr); } j = 0; unsigned_vector real_vars; for (app* v : vars) { if (is_arith(v) && !fmls_mark.is_marked(v)) { real_vars.push_back(tids.find(v)); } else { vars[j++] = v; } } vars.shrink(j); TRACE("qe", tout << "remaining vars: " << vars << "\n"; for (unsigned v : real_vars) { tout << "v" << v << " " << mk_pp(index2expr[v], m) << "\n"; } mbo.display(tout);); vector defs = mbo.project(real_vars.size(), real_vars.c_ptr(), compute_def); TRACE("qe", mbo.display(tout);); vector rows; mbo.get_live_rows(rows); for (row const& r : rows) { expr_ref_vector ts(m); expr_ref t(m), s(m), val(m); 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) { var const& v = r.m_vars[0]; t = index2expr[v.m_id]; if (!v.m_coeff.is_minus_one()) { t = a.mk_mul(a.mk_numeral(-v.m_coeff, a.is_int(t)), t); } s = a.mk_numeral(r.m_coeff, a.is_int(t)); switch (r.m_type) { case opt::t_lt: t = a.mk_gt(t, s); break; case opt::t_le: t = a.mk_ge(t, s); break; case opt::t_eq: t = a.mk_eq(t, s); break; default: UNREACHABLE(); } fmls.push_back(t); val = eval(t); CTRACE("qe", !m.is_true(val), tout << "Evaluated unit " << t << " to " << val << "\n";); continue; } for (var const& v : r.m_vars) { t = index2expr[v.m_id]; if (!v.m_coeff.is_one()) { t = a.mk_mul(a.mk_numeral(v.m_coeff, a.is_int(t)), t); } ts.push_back(t); } t = mk_add(ts); s = a.mk_numeral(-r.m_coeff, r.m_coeff.is_int() && a.is_int(t)); switch (r.m_type) { case opt::t_lt: t = a.mk_lt(t, s); break; case opt::t_le: t = a.mk_le(t, s); break; case opt::t_eq: t = a.mk_eq(t, s); break; case opt::t_mod: if (!r.m_coeff.is_zero()) { t = a.mk_sub(t, s); } t = a.mk_eq(a.mk_mod(t, a.mk_numeral(r.m_mod, true)), a.mk_int(0)); break; } fmls.push_back(t); val = eval(t); CTRACE("qe", !m.is_true(val), tout << "Evaluated " << t << " to " << val << "\n";); } vector result; if (compute_def) { SASSERT(defs.size() == real_vars.size()); for (unsigned i = 0; i < defs.size(); ++i) { auto const& d = defs[i]; expr* x = index2expr[real_vars[i]]; bool is_int = a.is_int(x); expr_ref_vector ts(m); expr_ref t(m); for (var const& v : d.m_vars) { ts.push_back(var2expr(index2expr, v)); } if (!d.m_coeff.is_zero()) ts.push_back(a.mk_numeral(d.m_coeff, is_int)); t = mk_add(ts); if (!d.m_div.is_one() && is_int) { t = a.mk_idiv(t, a.mk_numeral(d.m_div, is_int)); } else if (!d.m_div.is_one() && !is_int) { t = a.mk_div(t, a.mk_numeral(d.m_div, is_int)); } result.push_back(def(expr_ref(x, m), t)); } } return result; } expr_ref mk_add(expr_ref_vector const& ts) { switch (ts.size()) { case 0: return expr_ref(a.mk_int(0), m); case 1: return expr_ref(ts.get(0), m); default: return expr_ref(a.mk_add(ts.size(), ts.c_ptr()), m); } } opt::inf_eps maximize(expr_ref_vector const& fmls0, model& mdl, app* t, expr_ref& ge, expr_ref& gt) { SASSERT(a.is_real(t)); expr_ref_vector fmls(fmls0); opt::model_based_opt mbo; opt::inf_eps value; obj_map ts; obj_map tids; model_evaluator eval(mdl); // extract objective function. vars coeffs; rational c(0), mul(1); linearize(mbo, eval, mul, t, c, fmls, ts, tids); extract_coefficients(mbo, eval, ts, tids, coeffs); mbo.set_objective(coeffs, c); SASSERT(validate_model(eval, fmls0)); // extract linear constraints for (expr * fml : fmls) { linearize(mbo, eval, fml, fmls, tids); } // find optimal value value = mbo.maximize(); // update model to use new values that satisfy optimality ptr_vector vars; for (auto& kv : tids) { expr* e = kv.m_key; if (is_uninterp_const(e)) { unsigned id = kv.m_value; func_decl* f = to_app(e)->get_decl(); expr_ref val(a.mk_numeral(mbo.get_value(id), false), m); mdl.register_decl(f, val); } else { TRACE("qe", tout << "omitting model update for non-uninterpreted constant " << mk_pp(e, m) << "\n";); } } expr_ref val(a.mk_numeral(value.get_rational(), false), m); expr_ref tval = eval(t); // update the predicate 'bound' which forces larger values when 'strict' is true. // strict: bound := valuue < t // !strict: bound := value <= t if (!value.is_finite()) { ge = a.mk_ge(t, tval); gt = m.mk_false(); } else if (value.get_infinitesimal().is_neg()) { ge = a.mk_ge(t, tval); gt = a.mk_ge(t, val); } else { ge = a.mk_ge(t, val); gt = a.mk_gt(t, val); } SASSERT(validate_model(eval, fmls0)); return value; } bool validate_model(model_evaluator& eval, expr_ref_vector const& fmls) { bool valid = true; for (unsigned i = 0; i < fmls.size(); ++i) { expr_ref val = eval(fmls[i]); if (!m.is_true(val)) { valid = false; TRACE("qe", tout << mk_pp(fmls[i], m) << " := " << val << "\n";); } } return valid; } void extract_coefficients(opt::model_based_opt& mbo, model_evaluator& eval, obj_map const& ts, obj_map& tids, vars& coeffs) { coeffs.reset(); eval.set_model_completion(true); for (auto& kv : ts) { unsigned id; expr* v = kv.m_key; if (!tids.find(v, id)) { rational r; expr_ref val = eval(v); a.is_numeral(val, r); id = mbo.add_var(r, a.is_int(v)); tids.insert(v, id); } CTRACE("qe", kv.m_value.is_zero(), tout << mk_pp(v, m) << " has coefficeint 0\n";); if (!kv.m_value.is_zero()) { coeffs.push_back(var(id, kv.m_value)); } } } }; arith_project_plugin::arith_project_plugin(ast_manager& m) { m_imp = alloc(imp, m); } arith_project_plugin::~arith_project_plugin() { dealloc(m_imp); } bool arith_project_plugin::operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) { return (*m_imp)(model, var, vars, lits); } void arith_project_plugin::operator()(model& model, app_ref_vector& vars, expr_ref_vector& lits) { m_imp->project(model, vars, lits, false); } vector arith_project_plugin::project(model& model, app_ref_vector& vars, expr_ref_vector& lits) { return m_imp->project(model, vars, lits, true); } void arith_project_plugin::set_check_purified(bool check_purified) { m_imp->m_check_purified = check_purified; } bool arith_project_plugin::solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) { return m_imp->solve(model, vars, lits); } family_id arith_project_plugin::get_family_id() { return m_imp->a.get_family_id(); } opt::inf_eps arith_project_plugin::maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& ge, expr_ref& gt) { return m_imp->maximize(fmls, mdl, t, ge, gt); } void arith_project_plugin::saturate(model& model, func_decl_ref_vector const& shared, expr_ref_vector& lits) { UNREACHABLE(); } bool arith_project(model& model, app* var, expr_ref_vector& lits) { ast_manager& m = lits.get_manager(); arith_project_plugin ap(m); app_ref_vector vars(m); return ap(model, var, vars, lits); } } z3-z3-4.8.7/src/qe/qe_arith.h000066400000000000000000000030451356505360400155720ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #ifndef QE_ARITH_H_ #define QE_ARITH_H_ #include "model/model.h" #include "ast/arith_decl_plugin.h" #include "qe/qe_mbp.h" namespace qe { /** Loos-Weispfenning model-based projection for a basic conjunction. Lits is a vector of literals. return vector of variables that could not be projected. */ class arith_project_plugin : public project_plugin { struct imp; imp* m_imp; public: arith_project_plugin(ast_manager& m); ~arith_project_plugin() override; bool operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) override; bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) override; family_id get_family_id() override; void operator()(model& model, app_ref_vector& vars, expr_ref_vector& lits) override; vector project(model& model, app_ref_vector& vars, expr_ref_vector& lits) override; void saturate(model& model, func_decl_ref_vector const& shared, expr_ref_vector& lits) override; opt::inf_eps maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& ge, expr_ref& gt); /** * \brief check if formulas are purified, or leave it to caller to ensure that * arithmetic variables nested under foreign functions are handled properly. */ void set_check_purified(bool check_purified); }; bool arith_project(model& model, app* var, expr_ref_vector& lits); }; #endif z3-z3-4.8.7/src/qe/qe_arith_plugin.cpp000066400000000000000000002742561356505360400175210ustar00rootroot00000000000000/*++ Copyright (c) 2010 Microsoft Corporation Module Name: arith_plugin.cpp Abstract: Eliminate Arithmetical variable from formula Author: Nikolaj Bjorner (nbjorner) 2010-02-19 Revision History: --*/ #include "qe/qe.h" #include "ast/ast_pp.h" #include "ast/rewriter/expr_safe_replace.h" #include "ast/rewriter/bool_rewriter.h" #include "ast/bv_decl_plugin.h" #include "ast/arith_decl_plugin.h" #include "smt/arith_eq_solver.h" #include "ast/rewriter/arith_rewriter.h" #include "ast/rewriter/th_rewriter.h" #include "ast/rewriter/factor_rewriter.h" #include "util/obj_pair_hashtable.h" #include "qe/nlarith_util.h" #include "model/model_evaluator.h" #include "smt/smt_kernel.h" #include "qe/qe_arith.h" namespace qe { static bool is_divides(arith_util& a, expr* e1, expr* e2, rational& k, expr_ref& p) { expr* t1, *t2; if (a.is_mod(e2, t1, t2) && a.is_numeral(e1, k) && k.is_zero() && a.is_numeral(t2, k)) { p = t1; return true; } return false; } static bool is_divides(arith_util& a, expr* e, rational& k, expr_ref& t) { expr* e1, *e2; if (!a.get_manager().is_eq(e, e1, e2)) { return false; } return is_divides(a, e1, e2, k, t) || is_divides(a, e2, e1, k, t); } class bound { rational m_coeff; expr_ref m_term; bool m_is_strict; public: bound(ast_manager& m, rational const& n, expr* t, bool is_strict) : m_coeff(n), m_term(t, m), m_is_strict(is_strict) { } bool is_strict() const { return m_is_strict; } expr* term() const { return m_term.get(); } rational const& coeff() const { return m_coeff; } void update(rational const& k, expr* t) { m_coeff = k; m_term = t; } void pp(std::ostream& out, app* x) { ast_manager& m = m_term.get_manager(); out << "(<= (+ (* " << coeff() << " " << mk_pp(x, m) << ") " << mk_pp(term(), m) << ") 0)"; } }; typedef rational numeral; // m_k | (m_a * x + m_term) class div_constraint { numeral m_k; numeral m_a; expr* m_term; public: div_constraint(numeral const& k, numeral const& a, expr* t): m_k(k), m_a(a), m_term(t) {} numeral const& a() const { return m_a; } numeral const& k() const { return m_k; } expr* t() const { return m_term; } numeral& a_ref() { return m_a; } numeral& k_ref() { return m_k; } expr*& t_ref() { return m_term; } }; typedef vector div_constraints; class arith_qe_util { ast_manager& m; i_solver_context& m_ctx; public: arith_util m_arith; // initialize before m_zero_i, etc. th_rewriter simplify; app_ref_vector m_vars_added; private: arith_eq_solver m_arith_solver; bv_util m_bv; expr_ref m_zero_i; expr_ref m_one_i; expr_ref m_minus_one_i; expr_ref m_zero_r; expr_ref m_one_r; expr_ref m_tmp; public: expr_safe_replace m_replace; bool_rewriter m_bool_rewriter; arith_rewriter m_arith_rewriter; arith_qe_util(ast_manager& m, smt_params& p, i_solver_context& ctx) : m(m), m_ctx(ctx), m_arith(m), simplify(m), m_vars_added(m), m_arith_solver(m), m_bv(m), m_zero_i(m_arith.mk_numeral(numeral(0), true), m), m_one_i(m_arith.mk_numeral(numeral(1), true), m), m_minus_one_i(m_arith.mk_numeral(numeral(-1), true), m), m_zero_r(m_arith.mk_numeral(numeral(0), false), m), m_one_r(m_arith.mk_numeral(numeral(1), false), m), m_tmp(m), m_replace(m), m_bool_rewriter(m), m_arith_rewriter(m) { } ast_manager& get_manager() { return m; } // // match e := k*x + rest, where k != 0. // bool get_coeff(contains_app& contains_x, expr* p, rational& k, expr_ref& rest) { app* x = contains_x.x(); ptr_vector restl, todo; todo.push_back(p); bool found = false; expr* e1, *e2; while (!todo.empty()) { expr* e = todo.back(); todo.pop_back(); if (m_arith.is_add(e)) { for (unsigned i = 0; i < to_app(e)->get_num_args(); ++i) { todo.push_back(to_app(e)->get_arg(i)); } } else if (e == x) { k = numeral(1); found = true; break; } else if (m_arith.is_mul(e, e1, e2) && e1 == x && m_arith.is_numeral(e2, k)) { found = true; break; } else if (m_arith.is_mul(e, e1, e2) && e2 == x && m_arith.is_numeral(e1, k)) { found = true; break; } else { restl.push_back(e); } } if (!found) { TRACE("qe_verbose", tout << "Did not find: " << mk_pp(x, m) << " in " << mk_pp(p, m) << "\n"; ); return false; } while (!todo.empty()) { restl.push_back(todo.back()); todo.pop_back(); } if (restl.empty()) { rest = mk_zero(x); } else { rest = m_arith.mk_add(restl.size(), restl.c_ptr()); } if (contains_x(rest)) { return false; } TRACE("qe_verbose", tout << mk_pp(p, m) << " = " << "(+ (* " << k << " " << mk_pp(x, m) << ") " << mk_pp(rest, m) << ")\n"; ); return true; } // // match p := k + rest // where k is a numeral and rest does not contain numerals. // void get_const(expr* p, rational& k, expr_ref& rest) { ptr_vector todo, restl; todo.push_back(p); k = numeral(0); while(!todo.empty()) { p = todo.back(); todo.pop_back(); if (m_arith.is_add(p)) { for (unsigned i = 0; i < to_app(p)->get_num_args(); ++i) { todo.push_back(to_app(p)->get_arg(i)); } } else if (m_arith.is_numeral(p, k)) { break; } else { restl.push_back(p); } } while (!todo.empty()) { restl.push_back(todo.back()); todo.pop_back(); } if (restl.empty()) { rest = mk_zero(p); } else { rest = m_arith.mk_add(restl.size(), restl.c_ptr()); } } // // match (not ne) bool is_neg(app* e, expr_ref& ne) { if (m.is_not(e)) { ne = to_app(e)->get_arg(0); return true; } return false; } bool is_le(app* e, expr_ref& p) { return is_le_ge_core<1>(e, p); } bool is_ge(app* e, expr_ref& p) { return is_le_ge_core<0>(e, p); } // match e = p < 0 or p > 0 bool is_lt(app* e, expr_ref& p) { numeral k; expr* a1, *a2; if (m_arith.is_lt(e, a1, a2) || m_arith.is_gt(e, a2, a1)) { p = a1; if (m_arith.is_numeral(a2, k) && k.is_zero()) { return true; } } else { return false; } p = mk_sub(p, a2); simplify(p); return true; } // // match 0 == p mod k, p mod k == 0 // bool is_divides(expr* e, numeral& k, expr_ref& p) { return qe::is_divides(m_arith, e, k, p); } bool is_not_divides(app* e, app_ref& n, numeral& k, expr_ref& p) { if (!m.is_not(e)) { return false; } if (!is_app(to_app(e)->get_arg(0))) { return false; } n = to_app(to_app(e)->get_arg(0)); return is_divides(n, k, p); } bool is_real(app* x) const { return m_arith.is_real(x); } // // b*t <= a*s // template void mk_bound_aux(rational const& a, expr* t, rational const& b, expr* s, expr_ref& result) { SASSERT(a.is_neg() == b.is_neg()); expr_ref tt(t, m), ss(s, m), e(m); // hack to fix weird gcc compilation error rational abs_a(a); rational abs_b(b); if (abs_a.is_neg()) abs_a.neg(); if (abs_b.is_neg()) abs_b.neg(); ss = mk_mul(abs_a, ss); tt = mk_mul(abs_b, tt); if(a.is_neg()) { e = mk_sub(tt, ss); if (is_strict) { if (m_arith.is_int(e)) { e = mk_add(e, m_one_i); mk_le(e, result); } else { mk_lt(e, result); } } else { mk_le(e, result); } } else { e = mk_sub(ss, tt); if (is_strict) { if (m_arith.is_int(e)) { e = mk_add(e, m_one_i); mk_le(e, result); } else { mk_lt(e, result); } } else { mk_le(e, result); } } } void mk_bound(rational const& a, expr* t, rational const& b, expr* s, expr_ref& result) { mk_bound_aux(a, t, b, s, result); } void mk_strict_bound(rational const& a, expr* t, rational const& b, expr* s, expr_ref& result) { mk_bound_aux(a, t, b, s, result); } void mk_divides(numeral n, expr* e, expr_ref& result) { SASSERT(n.is_int()); expr_ref tmp1(e, m), tmp2(m); simplify(tmp1); m_arith_rewriter.mk_mod(tmp1, mk_numeral(n), tmp2); m_bool_rewriter.mk_eq(m_zero_i, tmp2, result); } void mk_div(expr* a, numeral const & k, expr_ref& result) { result = m_arith.mk_div(a, m_arith.mk_numeral(k, false)); simplify(result); } expr* mk_numeral(numeral const& k, bool is_int = true) { return m_arith.mk_numeral(k, is_int); } expr* mk_numeral(int k, bool is_int) { return mk_numeral(numeral(k),is_int); } expr* mk_uminus(expr* e) { return m_arith.mk_uminus(e); } expr* mk_abs(expr* e) { rational val; if (m_arith.is_numeral(e, val)) { if (val.is_neg()) { return m_arith.mk_uminus(e); } else { return e; } } else { return m.mk_ite(m_arith.mk_le(mk_zero(e), e), e, m_arith.mk_uminus(e)); } } template expr_ref mk_min_max(unsigned num_args, expr* const* args) { SASSERT(num_args > 0); if (num_args == 1) { return expr_ref(args[0], m); } else { expr_ref e2 = mk_min_max(num_args-1,args+1); expr* e1 = args[0]; expr* cmp = is_max?m_arith.mk_le(e1, e2):m_arith.mk_le(e2, e1); return expr_ref(m.mk_ite(cmp, e2, e1), m); } } expr_ref mk_max(unsigned num_args, expr* const* args) { return mk_min_max(num_args, args); } expr_ref mk_min(unsigned num_args, expr* const* args) { return mk_min_max(num_args, args); } expr* mk_mul(expr* a, expr* b) { return m_arith.mk_mul(a,b); } expr* mk_add(expr* a, expr* b) { return m_arith.mk_add(a,b); } expr* mk_sub(expr* a, expr* b) { return m_arith.mk_sub(a,b); } expr* mk_mul(numeral const& a, expr* b) { if (a.is_one()) return b; return m_arith.mk_mul(mk_numeral(a, m_arith.is_int(b)),b); } expr* mk_zero(sort* s) { return m_arith.is_int(s)?m_zero_i:m_zero_r; } expr* mk_zero(expr* e) { return m_arith.is_int(e)?m_zero_i:m_zero_r; } expr* mk_one(sort* s) { return m_arith.is_int(s)?m_one_i:m_one_r; } expr* mk_one(expr* e) { return m_arith.is_int(e)?m_one_i:m_one_r; } void mk_le(expr* e, expr_ref& result) { expr_ref tmp(e, m); simplify(tmp); m_arith_rewriter.mk_le(tmp, mk_zero(e), result); } void mk_lt(expr* e, expr_ref& result) { rational r; if (m_arith.is_numeral(e, r)) { if (r.is_neg()) { result = m.mk_true(); } else { result = m.mk_false(); } } else if (m_arith.is_int(e)) { result = m_arith.mk_le(e, m_minus_one_i); } else { result = m.mk_not(m_arith.mk_le(mk_zero(e), e)); } simplify(result); TRACE("qe_verbose", tout << "mk_lt " << mk_pp(result, m) << "\n";); } // ax + t = 0 void mk_eq(rational const& a, app* x, expr* t, expr_ref& result) { result = m_arith.mk_eq(mk_add(mk_mul(a, x), t), mk_zero(x)); } void mk_and(unsigned sz, expr*const* args, expr_ref& result) { m_bool_rewriter.mk_and(sz, args, result); } void mk_and(expr* e1, expr* e2, expr_ref& result) { m_bool_rewriter.mk_and(e1, e2, result); } void add_and(expr* e, ptr_vector& conjs) { if (m.is_and(e)) { conjs.append(to_app(e)->get_num_args(), to_app(e)->get_args()); } else { conjs.push_back(e); } } void mk_flat_and(expr* e1, expr* e2, expr_ref& result) { ptr_vector conjs; add_and(e1, conjs); add_and(e2, conjs); m_bool_rewriter.mk_and(conjs.size(), conjs.c_ptr(), result); } void mk_or(unsigned sz, expr*const* args, expr_ref& result) { m_bool_rewriter.mk_or(sz, args, result); } void mk_or(expr* e1, expr* e2, expr_ref& result) { m_bool_rewriter.mk_or(e1, e2, result); } // // b*t <= a*s // void mk_resolve(app* x, bool is_strict, rational const& a, expr* t, rational const& b, expr* s, expr_ref& result) { rational abs_a(abs(a)), abs_b(abs(b)); SASSERT(a.is_neg() == b.is_pos()); SASSERT(!is_strict || (abs_a.is_one() && abs_b.is_one())); expr_ref bt(mk_mul(abs_b, t), m); expr_ref as(mk_mul(abs_a, s), m); expr_ref as_bt(mk_add(as, bt), m); if(is_strict) { mk_lt(as_bt, result); } else { mk_le(as_bt, result); } if (!abs_a.is_one() && !abs_b.is_one()) { // integer resolution case. SASSERT(!is_strict); SASSERT(abs_a > rational::one() && abs_b > rational::one()); expr_ref slack(mk_numeral((abs_a-numeral(1))*(abs_b-numeral(1)), true), m); expr_ref result1(m), result2(m); // a*s + b*t <= 0 expr_ref as_bt_le_0(result, m), tmp2(m), asz_bt_le_0(m), tmp3(m), tmp4(m); expr_ref b_divides_sz(m); // a*s + b*t + (a-1)(b-1) <= 0 tmp2 = m_arith.mk_add(as_bt, slack); mk_le(tmp2, result1); rational a1 = a, b1 = b; if (abs_a < abs_b) { std::swap(abs_a, abs_b); std::swap(a1, b1); std::swap(s, t); std::swap(as, bt); } SASSERT(abs_a >= abs_b); // create finite disjunction for |b|. // exists x, z in [0 .. |b|-2] . b*x + s + z = 0 && ax + t <= 0 && bx + s <= 0 // <=> // exists x, z in [0 .. |b|-2] . b*x = -z - s && ax + t <= 0 && bx + s <= 0 // <=> // exists x, z in [0 .. |b|-2] . b*x = -z - s && a|b|x + |b|t <= 0 && bx + s <= 0 // <=> // exists x, z in [0 .. |b|-2] . b*x = -z - s && a|b|x + |b|t <= 0 && -z - s + s <= 0 // <=> // exists x, z in [0 .. |b|-2] . b*x = -z - s && a|b|x + |b|t <= 0 && -z <= 0 // <=> // exists x, z in [0 .. |b|-2] . b*x = -z - s && a|b|x + |b|t <= 0 // <=> // exists x, z in [0 .. |b|-2] . b*x = -z - s && a*n_sign(b)(s + z) + |b|t <= 0 // <=> // exists z in [0 .. |b|-2] . |b| | (z + s) && a*n_sign(b)(s + z) + |b|t <= 0 // expr_ref sz(mk_add(s, x), m); if (b1.is_pos()) { sz = m_arith.mk_uminus(sz); } tmp4 = mk_add(mk_mul(a1, sz), bt); mk_le(tmp4, asz_bt_le_0); if (to_app(asz_bt_le_0)->get_arg(0) == x && m_arith.is_zero(to_app(asz_bt_le_0)->get_arg(1))) { // exists z in [0 .. |b|-2] . |b| | (z + s) && z <= 0 // <=> // |b| | s mk_divides(abs_b, s, tmp2); } else { mk_divides(abs_b, sz, b_divides_sz); mk_and(b_divides_sz, asz_bt_le_0, tmp4); mk_big_or(abs_b - numeral(2), x, tmp4, tmp2); TRACE("qe", tout << "b | s + z: " << mk_pp(b_divides_sz, m) << "\n"; tout << "a(s+z) + bt <= 0: " << mk_pp(asz_bt_le_0, m) << "\n"; ); } mk_flat_and(as_bt_le_0, tmp2, result2); mk_or(result1, result2, result); simplify(result); // a*s + b*t + (a-1)(b-1) <= 0 // or exists z in [0 .. |b|-2] . |b| | (z + s) && a*n_sign(b)(s + z) + |b|t <= 0 } TRACE("qe", { tout << (is_strict?"strict":"non-strict") << "\n"; bound(m, a, t, false).pp(tout, x); tout << "\n"; bound(m, b, s, false).pp(tout, x); tout << "\n"; tout << mk_pp(result, m) << "\n"; }); } struct mul_lt { arith_util& u; mul_lt(arith_qe_util& u): u(u.m_arith) {} bool operator()(expr* n1, expr* n2) const { expr* x, *y; if (u.is_mul(n1, x, y) && u.is_numeral(x)) { n1 = y; } if (u.is_mul(n2, x, y) && u.is_numeral(x)) { n2 = y; } return n1->get_id() < n2->get_id(); } }; void normalize_sum(expr_ref& p) { simplify(p); if (!m_arith.is_add(p)) { return; } unsigned sz = to_app(p)->get_num_args(); ptr_buffer args; for (unsigned i = 0; i < sz; ++i) { args.push_back(to_app(p)->get_arg(i)); } std::sort(args.begin(), args.end(), mul_lt(*this)); p = m_arith.mk_add(args.size(), args.c_ptr()); } void pp_div(std::ostream& out, app* x, div_constraint const& div) { out << div.k() << " | (" << div.a() << "*" << mk_pp(x, m) << " + " << mk_pp(div.t(), m) << ") "; } void pp_divs(std::ostream& out, app* x, div_constraints const& divs) { for (unsigned i = 0; i < divs.size(); ++i) { pp_div(out, x, divs[i]); out << " "; } } bool mk_atom(expr* e, bool p, expr_ref& result) { // retain equalities. if (!is_app(e)) { return false; } app* a = to_app(e); expr_ref t1(m), t2(m); expr_ref tmp1(m), tmp2(m); rational k; expr* a0, *a1; if (p && is_divides(a, k, tmp1)) { result = e; } else if (!p && is_divides(a, k, tmp1)) { m_bool_rewriter.mk_not(e, result); } else if (p && m.is_eq(e, a0, a1) && is_arith(a0)) { t1 = mk_sub(a0, a1); simplify(t1); t2 = mk_sub(a1, a0); simplify(t2); mk_le(t1, tmp1); mk_le(t2, tmp2); mk_and(tmp1, tmp2, result); } else if (!p && m.is_eq(e, a0, a1) && m_arith.is_int(a0)) { tmp1 = mk_sub(a0, a1); t1 = mk_add(mk_one(a0), tmp1); simplify(t1); t2 = mk_sub(mk_one(a0), tmp1); simplify(t2); mk_le(t1, tmp1); // a0 < a1 <=> 1 + a0 - a1 <= 0 mk_le(t2, tmp2); // a0 > a1 <=> 1 - a0 + a1 <= 0 mk_or(tmp1, tmp2, result); } else if (!p && m.is_eq(e, a0, a1) && m_arith.is_real(a0)) { t1 = mk_sub(a0, a1); simplify(t1); t2 = mk_sub(a1, a0); simplify(t2); mk_lt(t1, tmp1); mk_lt(t2, tmp2); mk_or(tmp1, tmp2, result); } else if (!p && (m_arith.is_le(e, a0, a1) || m_arith.is_ge(e, a1, a0))) { tmp1 = mk_sub(a1, a0); mk_lt(tmp1, result); } else if (p && (m_arith.is_le(e) || m_arith.is_ge(e))) { result = e; } else if (p && (m_arith.is_lt(e, a0, a1) || m_arith.is_gt(e, a1, a0))) { tmp1 = mk_sub(a0, a1); mk_lt(tmp1, result); } else if (!p && (m_arith.is_lt(e, a0, a1) || m_arith.is_gt(e, a1, a0))) { tmp1 = mk_sub(a1, a0); mk_le(tmp1, result); } else { return false; } TRACE("qe_verbose", tout << "Atom: " << mk_pp(result, m) << "\n";); return true; } void mk_bounded_var(rational const& n, app_ref& z_bv, app_ref& z) { rational two(2), b(n); unsigned sz = 0; do { ++sz; b = div(b, two); } while (b.is_pos()); sort* s = m_bv.mk_sort(sz); z_bv = m.mk_fresh_const("z", s); expr_ref tmp(m); z = m_bv.mk_bv2int(z_bv); } bool solve(conj_enum& conjs, expr* fml) { expr_ref_vector eqs(m); extract_equalities(conjs, eqs); return reduce_equations(eqs.size(), eqs.c_ptr(), fml); } // ---------------------------------------------------------------------- // // Equation solving features. // // Extract equalities from current goal. // void extract_equalities(conj_enum& conjs, expr_ref_vector& eqs) { obj_hashtable leqs; expr_ref_vector trail(m); expr_ref tmp1(m), tmp2(m); expr *a0, *a1; eqs.reset(); conj_enum::iterator it = conjs.begin(), end = conjs.end(); for (; it != end; ++it) { expr* e = *it; bool is_leq = false; if (m.is_eq(e, a0, a1) && is_arith(a0)) { m_arith_rewriter.mk_sub(a0, a1, tmp1); simplify(tmp1); eqs.push_back(tmp1); } else if (m_arith.is_le(e, a0, a1) || m_arith.is_ge(e, a1, a0)) { m_arith_rewriter.mk_sub(a0, a1, tmp1); is_leq = true; } else { // drop equality. } if (is_leq) { normalize_sum(tmp1); tmp2 = m_arith.mk_uminus(tmp1); normalize_sum(tmp2); if (leqs.contains(tmp2)) { eqs.push_back(tmp1); TRACE("qe", tout << "found: " << mk_pp(tmp1, m) << "\n";); } else { trail.push_back(tmp1); leqs.insert(tmp1); TRACE("qe_verbose", tout << "insert: " << mk_pp(tmp1, m) << "\n";); } } } } void add_var(app* v, bool track = true) { m_ctx.add_var(v); if (track) m_vars_added.push_back(v); } private: // // match p <= 0 or p >= 0 // template bool is_le_ge_core(app* e, expr_ref& p) { numeral k; expr_ref tmp(m); expr* a2; if (m_arith.is_le(e)) { p = e->get_arg(1-IS_LE); a2 = e->get_arg(IS_LE); if (m_arith.is_numeral(a2, k) && k.is_zero()) { return true; } } else if (m_arith.is_ge(e)) { p = e->get_arg(IS_LE); a2 = e->get_arg(1-IS_LE); if (m_arith.is_numeral(a2, k) && k.is_zero()) { return true; } } else { return false; } p = mk_sub(p, a2); simplify(p); return true; } bool is_arith(expr* e) { return m_arith.is_int(e) || m_arith.is_real(e); } void mk_big_or(numeral up, app* x, expr* body, expr_ref& result) { TRACE("qe", tout << mk_pp(x, m) << " " << mk_pp(body, m) << "\n";); if (numeral(1) >= up) { mk_big_or_blast(up, x, body, result); } else { mk_big_or_symbolic_blast(up, x, body, result); } } void mk_big_or_blast(numeral up, app* x, expr* body, expr_ref& result) { expr_ref_vector ors(m); numeral index(0); while (index <= up) { expr* n = mk_numeral(index); result = body; m_replace.apply_substitution(x, n, result); ors.push_back(result); ++index; } mk_or(ors.size(), ors.c_ptr(), result); TRACE("qe", tout << "[0 " << up << "] " << mk_pp(x, m) << "\n" << mk_pp(body, m) << "\n" << mk_pp(result, m) << "\n";); } void mk_big_or_symbolic(numeral up, app* x, expr* body, expr_ref& result) { app_ref z_bv(m); mk_big_or_symbolic(up, x, body, z_bv, result); add_var(z_bv); } void mk_big_or_symbolic_blast(numeral up, app* x, expr* body, expr_ref& result) { app_ref z_bv(m); mk_big_or_symbolic(up, x, body, z_bv, result); m_ctx.blast_or(z_bv, result); } void mk_big_or_symbolic(numeral up, app* x, expr* body, app_ref& z_bv, expr_ref& result) { expr* e1 = m_arith.mk_le(x, m_arith.mk_numeral(up, true)); mk_flat_and(e1, body, result); app_ref z(m); mk_bounded_var(up, z_bv, z); m_replace.apply_substitution(x, z, result); } // // Determine if 'x' can be isolated. // Return the coefficient if found. // bool isolate_x(expr* p, app* x, contains_app& contains_x, numeral& coeff) { numeral k; while (m_arith.is_add(p)) { bool found_x = false; expr* next_p = nullptr; for (unsigned i = 0; i < to_app(p)->get_num_args(); ++i) { expr* arg = to_app(p)->get_arg(i); if (contains_x(arg)) { if (found_x) { return false; } found_x = true; next_p = arg; } } if (!next_p) { return false; } p = next_p; } expr *e1, *e2; if (p == x) { coeff = numeral(1); return true; } else if (m_arith.is_mul(p, e1, e2) && m_arith.is_numeral(e1, k) && e2 == x) { coeff = k; return true; } else if (m_arith.is_mul(p, e1, e2) && m_arith.is_numeral(e2, k) && e1 == x) { coeff = k; return true; } return false; } // // Reduce equations. // Singular equations eliminate variables directly. // Linear equations eliminate original variables and introduce auxiliary variables. // bool reduce_equations(unsigned num_eqs, expr * const* eqs, expr* fml) { for (unsigned i = 0; i < num_eqs; ++i) { if (reduce_equation(eqs[i], fml)) { return true; } } return false; } bool solve_singular(unsigned var_idx, expr* p, expr* fml) { rational k; expr_ref e(m), tmp(m); app* x = m_ctx.get_var(var_idx); if (!isolate_x(p, x, m_ctx.contains(var_idx), k)) { return false; } if (m_arith.is_int(x) && !(abs(k).is_one())) { return false; } if (abs(k).is_one()) { if (k.is_neg()) { e = m_arith.mk_add(p, x); } else { e = m_arith.mk_sub(x, p); } } else { SASSERT(!m_arith.is_int(x)); // p = p' + k*x = 0 // <=> // -k*x = p' = p - k*x // => // x = (p - k*x)/ -k expr* ke = m_arith.mk_numeral(-k, false); tmp = m_arith.mk_mul(ke, x); tmp = m_arith.mk_add(p, tmp); e = m_arith.mk_div(tmp, ke); } TRACE("qe", tout << "is singular:\n" << mk_pp(p, m) << "\n" << mk_pp(fml, m) << "\n" << mk_pp(x, m) << " = " << mk_pp(e, m) << "\n"; ); expr_ref result(fml, m); m_replace.apply_substitution(x, e, result); simplify(result); TRACE("qe", tout << "singular solved:\n" << mk_pp(result, m) << "\n"; ); m_ctx.elim_var(var_idx, result, e); return true; } bool solve_singular(expr* p, expr* fml) { unsigned num_vars = m_ctx.get_num_vars(); for (unsigned i = 0; i < num_vars; ++i) { if (solve_singular(i, p, fml)) { return true; } } return false; } bool solve_linear(expr* p, expr* fml) { vector values; unsigned num_vars = m_ctx.get_num_vars(); app_ref_vector const& vars = m_ctx.get_vars(); if (!is_linear(p, num_vars, vars.c_ptr(), values)) { return false; } TRACE("qe", tout << "is linear: " << mk_pp(p, m) << "\n";); SASSERT(values.size() == num_vars + 1); SASSERT(num_vars > 0); unsigned index; bool is_aux; // // The first entry in values is the constant. // VERIFY(m_arith_solver.solve_integer_equation(values, index, is_aux)); SASSERT(1 <= index && index <= num_vars); app_ref x(m_ctx.get_var(index-1), m); app_ref z(m); expr_ref p1(m); if (is_aux) { // An auxiliary variable was introduced in lieu of 'x'. // it has coefficient 'm' = values[index]. SASSERT(values[index] >= rational(3)); z = m.mk_fresh_const("x", m_arith.mk_int()); add_var(z); p1 = m_arith.mk_mul(m_arith.mk_numeral(values[index], true), z); } else { // the coefficient to 'x' is -1. p1 = m_arith.mk_numeral(numeral(0), true); } for (unsigned i = 1; i <= num_vars; ++i) { numeral k = values[i]; if (!k.is_zero() && i != index) { p1 = m_arith.mk_add(p1, m_arith.mk_mul(m_arith.mk_numeral(k, true), m_ctx.get_var(i-1))); } } p1 = m_arith.mk_add(p1, m_arith.mk_numeral(values[0], true)); TRACE("qe", tout << "is linear:\n" << mk_pp(fml, m) << "\n" << mk_pp(p, m) << "\n" << mk_pp(x, m) << " = " << mk_pp(p1, m) << "\n"; tout << values[0] << " + "; for (unsigned i = 0; i < num_vars; ++i) { tout << " + " << values[i+1] << " * " << mk_pp(m_ctx.get_var(i), m) << " "; } tout << " = 0\n"; ); expr_ref result(fml, m); m_replace.apply_substitution(x, p1, result); simplify(result); m_ctx.elim_var(index-1, result, p1); TRACE("qe", tout << "Reduced: " << mk_pp(result, m) << "\n";); return true; } bool reduce_equation(expr* p, expr* fml) { numeral k; if (m_arith.is_numeral(p, k) && k.is_zero()) { return false; } return solve_singular(p, fml) || solve_linear(p, fml); } bool find_variable(expr* p, unsigned num_vars, app* const* vars, numeral* values, numeral const& k) { if (!is_app(p) || to_app(p)->get_num_args() > 0) { return false; } for (unsigned i = 0; i < num_vars; ++i) { if (p == vars[i]) { values[i] += k; return true; } } return false; } bool is_linear(expr* p, unsigned num_vars, app* const* vars, vector& values) { if (num_vars == 0) { return false; } values.reset(); for (unsigned i = 0; i <= num_vars; ++i) { values.push_back(numeral(0)); } numeral* vars_ptr = values.c_ptr() + 1; ptr_vector todo; numeral k; expr* e1, *e2; todo.push_back(p); while (!todo.empty()) { p = todo.back(); todo.pop_back(); if (m_arith.is_add(p)) { for (unsigned i = 0; i < to_app(p)->get_num_args(); ++i) { todo.push_back(to_app(p)->get_arg(i)); } } else if (m_arith.is_mul(p, e1, e2) && m_arith.is_numeral(e1, k) && find_variable(e2, num_vars, vars, vars_ptr, k)) { // ok } else if (m_arith.is_mul(p, e1, e2) && m_arith.is_numeral(e2, k) && find_variable(e1, num_vars, vars, vars_ptr, k)) { // ok } else if (find_variable(p, num_vars, vars, vars_ptr, k)) { // ok } else if (m_arith.is_numeral(p, k)) { values[0] += k; } else { TRACE("qe_verbose", tout << "non-linear " << mk_pp(p, m) << "\n";); return false; } } return true; } }; class bounds_proc { arith_qe_util& m_util; ast_mark m_mark; expr_ref_vector m_le_terms, m_ge_terms, m_lt_terms, m_gt_terms; vector m_le_coeffs, m_ge_coeffs, m_lt_coeffs, m_gt_coeffs; app_ref_vector m_le_atoms, m_ge_atoms, m_lt_atoms, m_gt_atoms; expr_ref_vector m_div_terms; vector m_div_coeffs, m_div_divisors; app_ref_vector m_div_atoms; app_ref m_div_z; expr_ref_vector m_nested_div_terms; vector m_nested_div_coeffs, m_nested_div_divisors; app_ref_vector m_nested_div_atoms; app_ref_vector m_nested_div_z; rational m_d; public: bounds_proc(arith_qe_util& u): m_util(u), m_le_terms(u.get_manager()), m_ge_terms(u.get_manager()), m_lt_terms(u.get_manager()), m_gt_terms(u.get_manager()), m_le_atoms(u.get_manager()), m_ge_atoms(u.get_manager()), m_lt_atoms(u.get_manager()), m_gt_atoms(u.get_manager()), m_div_terms(u.get_manager()), m_div_atoms(u.get_manager()), m_div_z(u.get_manager()), m_nested_div_terms(u.get_manager()), m_nested_div_atoms(u.get_manager()), m_nested_div_z(u.get_manager()) { reset(); } bool get_bound(contains_app& contains_x, app* a) { bool has_bound = m_mark.is_marked(a) || get_le_bound(contains_x, a) || get_lt_bound(contains_x, a) || get_divides(contains_x, a) || get_nested_divs(contains_x, a); if (has_bound) { m_mark.mark(a, true); } TRACE("qe_verbose", ast_manager& m = m_util.get_manager(); app* x = contains_x.x(); tout << has_bound << " bound for " << mk_pp(x, m) << " within " << mk_pp(a, m) << "\n";); return has_bound; } unsigned lt_size() { return m_lt_terms.size(); } unsigned le_size() { return m_le_terms.size(); } unsigned gt_size() { return m_gt_terms.size(); } unsigned ge_size() { return m_ge_terms.size(); } unsigned t_size(bool is_l) { return is_l?lt_size():gt_size(); } unsigned e_size(bool is_l) { return is_l?le_size():ge_size(); } unsigned size(bool is_strict, bool is_l) { return is_strict?t_size(is_l):e_size(is_l); } expr* const* lt() { return m_lt_terms.c_ptr(); } expr* const* le() { return m_le_terms.c_ptr(); } expr* const* gt() { return m_gt_terms.c_ptr(); } expr* const* ge() { return m_ge_terms.c_ptr(); } expr* const* t(bool is_l) { return is_l?lt():gt(); } expr* const* e(bool is_l) { return is_l?le():ge(); } expr* const* exprs(bool is_strict, bool is_l) { return is_strict?t(is_l):e(is_l);} rational const* lt_coeffs() { return m_lt_coeffs.c_ptr(); } rational const* le_coeffs() { return m_le_coeffs.c_ptr(); } rational const* gt_coeffs() { return m_gt_coeffs.c_ptr(); } rational const* ge_coeffs() { return m_ge_coeffs.c_ptr(); } rational const* t_coeffs(bool is_l) { return is_l?lt_coeffs():gt_coeffs(); } rational const* e_coeffs(bool is_l) { return is_l?le_coeffs():ge_coeffs(); } rational const* coeffs(bool is_strict, bool is_l) { return is_strict?t_coeffs(is_l):e_coeffs(is_l); } app* const* lt_atoms() { return m_lt_atoms.c_ptr(); } app* const* le_atoms() { return m_le_atoms.c_ptr(); } app* const* gt_atoms() { return m_gt_atoms.c_ptr(); } app* const* ge_atoms() { return m_ge_atoms.c_ptr(); } app* const* t_atoms(bool is_l) { return is_l?lt_atoms():gt_atoms(); } app* const* e_atoms(bool is_l) { return is_l?le_atoms():ge_atoms(); } app* const* atoms(bool is_strict, bool is_l) { return is_strict?t_atoms(is_l):e_atoms(is_l); } unsigned div_size() const { return m_div_terms.size(); } app* const* div_atoms() { return m_div_atoms.c_ptr(); } rational const* div_coeffs() { return m_div_coeffs.c_ptr(); } expr* const* div_terms() { return m_div_terms.c_ptr(); } rational const* divisors() { return m_div_divisors.c_ptr(); } bool div_z(rational & d, app_ref& z_bv, app_ref& z) { if (m_div_z.get()) { z = m_div_z; z_bv = to_app(z->get_arg(0)); d = m_d; return true; } if (m_div_terms.empty() && m_nested_div_terms.empty()) { return false; } m_d = rational(1); for (unsigned i = 0; i < m_div_divisors.size(); ++i) { m_d = lcm(m_div_divisors[i], m_d); } for (unsigned i = 0; i < m_nested_div_divisors.size(); ++i) { m_d = lcm(m_nested_div_divisors[i], m_d); } if (abs(m_d).is_one()) { return false; } m_util.mk_bounded_var(m_d, z_bv, m_div_z); z = m_div_z; d = m_d; return true; } unsigned nested_div_size() const { return m_nested_div_terms.size(); } app* nested_div_atom(unsigned idx) { return m_nested_div_atoms[idx].get(); } rational const& nested_div_coeff(unsigned idx) { return m_nested_div_coeffs[idx]; } expr* nested_div_term(unsigned idx) { return m_nested_div_terms[idx].get(); } rational const& nested_divisor(unsigned idx) { return m_nested_div_divisors[idx]; } app* nested_div_z(unsigned idx) { return m_nested_div_z[idx].get(); } app* nested_div_z_bv(unsigned idx) { return to_app(m_nested_div_z[idx]->get_arg(0)); } void reset() { m_lt_terms.reset(); m_gt_terms.reset(); m_ge_terms.reset(); m_le_terms.reset(); m_gt_coeffs.reset(); m_lt_coeffs.reset(); m_ge_coeffs.reset(); m_le_coeffs.reset(); m_lt_atoms.reset(); m_gt_atoms.reset(); m_le_atoms.reset(); m_ge_atoms.reset(); m_div_terms.reset(); m_div_coeffs.reset(); m_div_divisors.reset(); m_div_atoms.reset(); m_div_z = nullptr; m_nested_div_terms.reset(); m_nested_div_coeffs.reset(); m_nested_div_divisors.reset(); m_nested_div_atoms.reset(); m_nested_div_z.reset(); } private: bool get_nested_divs(contains_app& contains_x, app* a) { ast_manager& m = m_util.get_manager(); ptr_vector todo; todo.push_back(a); rational k1, k2; expr* e1 = nullptr, *e2 = nullptr; expr_ref rest(m); while (!todo.empty()) { expr* e = todo.back(); todo.pop_back(); if (m_mark.is_marked(e)) { continue; } m_mark.mark(e, true); if (!contains_x(e)) { continue; } if (contains_x.x() == e) { return false; } if (!is_app(e)) { return false; } a = to_app(e); if (m_util.m_arith.is_mod(e, e1, e2) && m_util.m_arith.is_numeral(e2, k1) && m_util.get_coeff(contains_x, e1, k2, rest)) { app_ref z(m), z_bv(m); m_util.mk_bounded_var(k1, z_bv, z); m_nested_div_terms.push_back(rest); m_nested_div_divisors.push_back(k1); m_nested_div_coeffs.push_back(k2); m_nested_div_atoms.push_back(a); m_nested_div_z.push_back(z); continue; } for (expr* arg : *a) { todo.push_back(arg); } } return true; } bool get_le_bound(contains_app& contains_x, app* a) { ast_manager& m = m_util.get_manager(); expr_ref p(m), rest(m); rational k; if (m_util.is_le(a, p) && m_util.get_coeff(contains_x, p, k, rest)) { // k*x + rest <= 0 if (m_util.is_real(contains_x.x())) { m_util.mk_div(rest, abs(k), rest); k = k.is_pos()?rational::one():rational::minus_one(); } if (k.is_neg()) { m_le_terms.push_back(rest); m_le_coeffs.push_back(k); m_le_atoms.push_back(a); } else { m_ge_terms.push_back(rest); m_ge_coeffs.push_back(k); m_ge_atoms.push_back(a); } return true; } return false; } bool get_lt_bound(contains_app& contains_x, app* a) { ast_manager& m = m_util.get_manager(); expr_ref p(m), rest(m), na(m); rational k; if (m_util.is_lt(a, p) && m_util.get_coeff(contains_x, p, k, rest)) { // k*x + rest < 0 } else if (m_util.is_neg(a, na) && is_app(na) && m_util.is_ge(to_app(na), p) && m_util.get_coeff(contains_x, p, k, rest)) { // // not (k*x + rest >= 0) // <=> // k*x + rest < 0 // } else { return false; } SASSERT(m_util.is_real(contains_x.x())); m_util.mk_div(rest, abs(k), rest); if (k.is_neg()) { m_lt_terms.push_back(rest); m_lt_coeffs.push_back(rational::minus_one()); m_lt_atoms.push_back(a); } else { m_gt_terms.push_back(rest); m_gt_coeffs.push_back(rational::one()); m_gt_atoms.push_back(a); } return true; } bool get_divides(contains_app& contains_x, app* a) { ast_manager& m = m_util.get_manager(); expr_ref p(m), rest(m); app_ref a2(m); numeral k, k2; if (m_util.is_divides(a, k, p) && m_util.get_coeff(contains_x, p, k2, rest)) { m_div_terms.push_back(rest); m_div_divisors.push_back(k); m_div_coeffs.push_back(k2); m_div_atoms.push_back(a); return true; } if (m_util.is_not_divides(a, a2, k, p) && m_util.get_coeff(contains_x, p, k2, rest)) { m_div_terms.push_back(rest); m_div_divisors.push_back(k); m_div_coeffs.push_back(k2); m_div_atoms.push_back(a2); return true; } return false; } public: void display(std::ostream& out) { ast_manager& m = m_util.get_manager(); for (unsigned i = 0; i < lt_size(); ++i) { out << mk_pp(lt()[i], m) << " < 0\n"; } for (unsigned i = 0; i < le_size(); ++i) { out << mk_pp(le()[i], m) << " < 0\n"; } for (unsigned i = 0; i < gt_size(); ++i) { out << mk_pp(gt()[i], m) << " < 0\n"; } for (unsigned i = 0; i < ge_size(); ++i) { out << mk_pp(ge()[i], m) << " < 0\n"; } } }; class x_subst { arith_qe_util& m_super; expr_ref m_t; rational m_coeff; public: x_subst(arith_qe_util& s): m_super(s), m_t(s.get_manager()), m_coeff(rational::one()) {} void set_term(expr* t) { m_t = t; } void set_coeff(rational const& k) { m_coeff = k; } expr* get_term() const { return m_t; } rational get_coeff() const { return m_coeff; } expr_ref mk_term(rational const& c, expr* t) { // return t + c*m_t ast_manager& m = m_super.get_manager(); if (m_t.get()) { return expr_ref(m_super.mk_add(m_super.mk_mul(c, m_t), t), m); } else { return expr_ref(t, m); } } rational mk_coeff(rational const& k) { return k * m_coeff; } }; struct branch_formula { expr* m_fml; app* m_var; unsigned m_branch; expr* m_result; rational m_coeff; expr* m_term; ptr_vector m_vars; branch_formula(): m_fml(nullptr), m_var(nullptr), m_branch(0), m_result(nullptr), m_term(nullptr) {} branch_formula(expr* fml, app* var, unsigned b, expr* r, rational coeff, expr* term, app_ref_vector const& vars): m_fml(fml), m_var(var), m_branch(b), m_result(r), m_coeff(coeff), m_term(term), m_vars(vars.size(), vars.c_ptr()) {} unsigned mk_hash() const { return mk_mix(m_fml?m_fml->hash():0, m_var?m_var->hash():0, m_branch); } bool mk_eq(branch_formula const& other) const { return m_fml == other.m_fml && m_var == other.m_var && m_branch == other.m_branch; } struct hash { typedef branch_formula data; unsigned operator()(data const& d) const { return d.mk_hash(); } }; struct eq { typedef branch_formula data; bool operator()(data const& x, data const& y) const { return x.mk_eq(y); } }; }; class arith_plugin : public qe_solver_plugin { typedef obj_pair_map bounds_cache; typedef obj_pair_map resolve_cache; typedef hashtable subst_cache; arith_qe_util m_util; expr_ref_vector m_trail; bounds_cache m_bounds_cache; subst_cache m_subst; public: arith_plugin(i_solver_context& ctx, ast_manager& m, smt_params& p): qe_solver_plugin(m, m.mk_family_id("arith"), ctx), m_util(m, p, ctx), m_trail(m) {} ~arith_plugin() override { bounds_cache::iterator it = m_bounds_cache.begin(), end = m_bounds_cache.end(); for (; it != end; ++it) { dealloc(it->get_value()); } } void assign(contains_app& contains_x, expr* fml, rational const& vl) override { SASSERT(vl.is_unsigned()); app* x = contains_x.x(); unsigned v = vl.get_unsigned(); expr_ref result(fml, m); unsigned t_size, e_size; x_subst x_t(m_util); if (get_cache(x, fml, v, result)) { return; } m_util.m_vars_added.reset(); bounds_proc& bounds = get_bounds(x, fml); bool is_lower = get_bound_sizes(bounds, x, t_size, e_size); assign_nested_divs(contains_x, bounds, result); assign_divs(contains_x, bounds, x_t, result); //assign_all(contains_x, fml); if (v == 0) { // // index is for the infinity case. // assert v => ~(x <= t) each t // assert v => (x >= s) each s // mk_non_bounds(bounds, true, is_lower, result); mk_non_bounds(bounds, false, is_lower, result); mk_non_resolve(bounds, true, is_lower, result); mk_non_resolve(bounds, false, is_lower, result); m_util.simplify(result); add_cache(x, fml, v, result, x_t.get_coeff(), x_t.get_term()); TRACE("qe", tout << vl << " " << mk_pp(x, m) << " infinite case\n"; tout << mk_pp(fml, m) << "\n"; tout << mk_pp(result, m) << "\n";); return; } unsigned index = v-1; bool is_strict = e_size <= index; bool is_eq = false; SASSERT(index < t_size + e_size); if (is_strict) { index -= e_size; TRACE("qe_verbose", bounds.display(tout); ); } else if (m_util.is_real(x)) { SASSERT(0 == (e_size & 0x1)); is_eq = (0 == (index & 0x1)); index /= 2; e_size /= 2; } SASSERT(is_strict || index < e_size); SASSERT(!is_strict || index < t_size); // // index is for the upper/lower-bound case. // assert v => (x <= t_i) // assert v => (x <= t_j => t_i <= t_j), add new atom to stack. // assert v => (x >= s => s <= t_i), add new atom to stack. // // assert v => (x < t_j => t_i < t_j) // SASSERT(index < bounds.size(is_strict, is_lower)); expr_ref t(bounds.exprs(is_strict, is_lower)[index], m); rational a = bounds.coeffs(is_strict, is_lower)[index]; mk_bounds(bounds, x, true, is_eq, is_strict, is_lower, index, a, t, result); mk_bounds(bounds, x, false, is_eq, is_strict, is_lower, index, a, t, result); t = x_t.mk_term(a, t); a = x_t.mk_coeff(a); mk_resolve(bounds, x, x_t, true, is_eq, is_strict, is_lower, index, a, t, result); mk_resolve(bounds, x, x_t, false, is_eq, is_strict, is_lower, index, a, t, result); m_util.simplify(result); add_cache(x, fml, v, result, x_t.get_coeff(), x_t.get_term()); TRACE("qe", { tout << vl << " " << mk_pp(bounds.atoms(is_strict, is_lower)[index],m) << "\n"; tout << mk_pp(fml, m) << "\n"; tout << result << "\n"; } ); } bool get_num_branches(contains_app& contains_x, expr* fml, rational& nb) override { app* x = contains_x.x(); if (!update_bounds(contains_x, fml)) { return false; } bounds_proc& bounds = get_bounds(x, fml); unsigned t_size, e_size; get_bound_sizes(bounds, x, t_size, e_size); nb = rational(t_size + e_size + 1); return true; } void subst(contains_app& contains_x, rational const& vl, expr_ref& fml, expr_ref* def) override { SASSERT(vl.is_unsigned()); if (def) { get_def(contains_x, vl.get_unsigned(), fml, *def); } VERIFY(get_cache(contains_x.x(), fml, vl.get_unsigned(), fml)); TRACE("qe", tout << mk_pp(contains_x.x(), m) << " " << vl << "\n" << mk_pp(fml, m) << "\n";); } bool project(contains_app& x, model_ref& model, expr_ref& fml) override { if (!update_bounds(x, fml)) { TRACE("qe", tout << mk_pp(x.x(), m) << " failed to update bounds\n";); return false; } if (m_util.m_arith.is_real(x.x())) { return project_real(x, model, fml); } else { return project_int(x, model, fml); } } unsigned get_weight(contains_app& contains_x, expr* fml) override { return 2; } bool solve(conj_enum& conjs, expr* fml) override { return m_util.solve(conjs, fml); } bool mk_atom(expr* e, bool p, expr_ref& result) override { return m_util.mk_atom(e, p, result); } bool is_uninterpreted(app* f) override { switch(f->get_decl_kind()) { case OP_NUM: case OP_LE: case OP_LT: case OP_GE: case OP_GT: case OP_ADD: case OP_SUB: case OP_UMINUS: return false; case OP_MOD: if(m_util.m_arith.is_numeral(f->get_arg(1))) { return false; } return true; case OP_MUL: { arith_util& a = m_util.m_arith; expr* m, *n; if (a.is_mul(f, m, n) && (a.is_numeral(m) || a.is_numeral(n))) { return false; } return true; } default: return true; } } private: /** \brief Compute least upper/greatest lower bounds for x. Assume: (not (= k 0)) (<= 0 (mod m k)) (< (mod m k) (abs k)) (= m (+ (* k (div m k)) (mod m k))) i.e. k * (e div k) + (e mod k) = e When k is positive, least upper bound for x such that: k*x <= e is e div k When k is negative, greatest lower bound for x such that k*x <= e is e div k k * (e div k) + (e mod k) = e */ expr_ref mk_idiv(expr* e, numeral k) { SASSERT(!k.is_zero()); arith_util& a = m_util.m_arith; if (k.is_one()) { return expr_ref(e, m); } if (k.is_minus_one()) { return expr_ref(a.mk_uminus(e), m); } SASSERT(a.is_int(e)); return expr_ref(a.mk_idiv(e, a.mk_numeral(k, true)), m); } void get_def(contains_app& contains_x, unsigned v, expr* fml, expr_ref& def) { app* x = contains_x.x(); x_subst x_t(m_util); bounds_proc& bounds = get_bounds(x, fml); branch_formula bf; VERIFY (m_subst.find(branch_formula(fml, x, v, nullptr, rational::zero(), nullptr, m_util.m_vars_added), bf)); x_t.set_term(bf.m_term); x_t.set_coeff(bf.m_coeff); // x is of the form: x_t.get_coeff()*x' + x_t.get_term() CTRACE("qe", x_t.get_term(), tout << x_t.get_coeff() << " " << mk_pp(x_t.get_term(), m) << "\n";); // // a*x + t <= 0 // a*(c*x' + s) + t <= 0 // a*c*x' + a*s + t <= 0 // unsigned t_size, e_size, sz; bool is_lower = get_bound_sizes(bounds, x, t_size, e_size); bool is_strict; if (v == 0) { is_strict = false; sz = bounds.size(is_strict, !is_lower); expr_ref_vector terms(m); if (sz == 0) { terms.push_back(m_util.mk_zero(x)); } for (unsigned i = 0; i < sz; ++i) { // a*x + term <= 0 expr_ref term(bounds.exprs(is_strict, !is_lower)[i], m); rational a = bounds.coeffs(is_strict, !is_lower)[i]; if (x_t.get_term()) { // x := coeff * x' + s // solve instead for // a*coeff*x' + term + a*s <= 0 TRACE("qe", tout << x_t.get_coeff() << "* " << mk_pp(x,m) << " + " << mk_pp(x_t.get_term(), m) << "\n";); SASSERT(x_t.get_coeff().is_pos()); term = m_util.mk_add(term, m_util.mk_mul(a, x_t.get_term())); a = a * x_t.get_coeff(); } TRACE("qe", tout << a << "* " << mk_pp(x,m) << " + " << mk_pp(term, m) << " <= 0\n";); SASSERT(a.is_int()); SASSERT(is_lower == a.is_pos()); // a*x + t <= 0 // <= // x <= -t div a + 1 term = m_util.mk_uminus(term); term = mk_idiv(term, a); terms.push_back(term); TRACE("qe", tout << "a: " << a << " term: " << mk_pp(term, m) << "\n";); } is_strict = true; sz = bounds.size(is_strict, !is_lower); for (unsigned i = 0; i < sz; ++i) { expr_ref term(bounds.exprs(is_strict, !is_lower)[i], m); SASSERT(abs(bounds.coeffs(is_strict, !is_lower)[i]).is_one()); // if (is_lower) { // x + t < 0 // <= // x <= -t -1 term = m_util.mk_uminus(m_util.mk_add(term, m_util.mk_one(x))); } else { // -x + t < 0 // <= // t + 1 <= x term = m_util.mk_add(term, m_util.mk_one(x)); } terms.push_back(term); } if (is_lower) { def = m_util.mk_min(terms.size(), terms.c_ptr()); } else { def = m_util.mk_max(terms.size(), terms.c_ptr()); } if (x_t.get_term()) { // x := coeff * x + s TRACE("qe", tout << x_t.get_coeff() << "* " << mk_pp(x,m) << " + " << mk_pp(x_t.get_term(), m) << "\n";); def = m_util.mk_add(m_util.mk_mul(x_t.get_coeff(), def), x_t.get_term()); } m_util.simplify(def); return; } --v; is_strict = e_size <= v; SASSERT(v < t_size + e_size); if (is_strict) { v -= e_size; TRACE("qe_verbose", bounds.display(tout); ); } else if (m_util.is_real(x)) { SASSERT(0 == (e_size & 0x1)); v /= 2; e_size /= 2; } SASSERT(is_strict || v < e_size); SASSERT(!is_strict || v < t_size); // // index is for the upper/lower-bound case. // assert v => (x <= t_i) // SASSERT(v < bounds.size(is_strict, is_lower)); def = bounds.exprs(is_strict, is_lower)[v]; rational a = bounds.coeffs(is_strict, is_lower)[v]; if (x_t.get_term()) { // x := coeff * x' + s // solve instead for // a*coeff*x' + term + a*s <= 0 TRACE("qe", tout << x_t.get_coeff() << "* " << mk_pp(x,m) << " + " << mk_pp(x_t.get_term(), m) << "\n";); SASSERT(x_t.get_coeff().is_pos()); def = m_util.mk_add(def, m_util.mk_mul(a, x_t.get_term())); a = a * x_t.get_coeff(); } SASSERT(a.is_int()); SASSERT(is_lower != a.is_pos()); // a*x + t <= 0 // <= // x <= -t div a def = m_util.mk_uminus(def); def = mk_idiv(def, a); if (x_t.get_term()) { // x := coeff * x + s def = m_util.mk_add(m_util.mk_mul(x_t.get_coeff(), def), x_t.get_term()); } if (is_strict) { SASSERT(m_util.m_arith.is_real(x)); // We actually want a supremum, such that dual inequalities are satisfied. // i.e. for every dual inequality , if the dual bound is feasible, make sure to // choose a value in the feasible range. def = m_util.mk_sub(def, m_util.mk_one(x)); } m_util.simplify(def); TRACE("qe", tout << "TBD (for Real): " << a << " " << mk_pp(def, m) << "\n";); } expr_ref mk_not(expr* e) { expr* r; if (m.is_not(e,r)) { return expr_ref(r, m); } return expr_ref(m.mk_not(e), m); } // // Projection function for x of type real. // TBD: model-based selection soundness/completeness? // when model selects bound different from what is the smaller half, what then? // shouldn't we find candidate among either lt or gt, and then if both can be found // only then select which one to go with. Then assign has to be context-aware. // Perhaps not really: the model is used as a hint. // bool project_real(contains_app& x, model_ref& model, expr_ref& fml) { SASSERT(m_util.m_arith.is_real(x.x())); model_evaluator model_eval(*model); bounds_proc& bounds = get_bounds(x.x(), fml); bool is_lower = bounds.le_size() + bounds.lt_size() < bounds.ge_size() + bounds.gt_size(); unsigned e_size = bounds.e_size(is_lower); numeral bound1, bound2, vl, x_val; unsigned idx1, idx2; bool found1 = find_min_max(is_lower, false, bounds, model_eval, bound1, idx1); bool found2 = find_min_max(is_lower, true, bounds, model_eval, bound2, idx2); if (!found1 && !found2) { vl = numeral(0); } else if (found2 && (!found1 || bound2 <= bound1)) { // strict indices come after non-strict indices. There // is a pair of index values for non-strict inequalities // corresponding to the disjunction (x < t || x = t) vl = numeral(1 + 2*e_size + idx2); } else if (found1 && (!found2 || bound1 < bound2)) { expr_ref val_x(m); model_eval(x.x(), val_x); VERIFY(m_util.m_arith.is_numeral(val_x, x_val)); if (x_val == bound1) { vl = numeral(1 + 2*idx1); // even indicates equality between x and bound. } else { vl = numeral(1 + 2*idx1 + 1); // odd indicates strict bound. } } assign(x, fml, vl); subst(x, vl, fml, nullptr); TRACE("qe", tout << mk_pp(fml, m) << "\n";); return true; } bool project_int(contains_app& x, model_ref& model, expr_ref& fml) { model_evaluator model_eval(*model); bounds_proc& bounds = get_bounds(x.x(), fml); SASSERT(m_util.m_arith.is_int(x.x())); SASSERT(bounds.lt_size() == 0 && bounds.gt_size() == 0); bool is_lower = bounds.le_size() < bounds.ge_size(); numeral bound, vl, x_val; unsigned idx = bounds.le_size() + bounds.ge_size(); bool found = find_min_max(is_lower, false, bounds, model_eval, bound, idx); if (found) { SASSERT(idx < bounds.size(false, is_lower)); vl = numeral(1 + idx); } else { vl = numeral(0); } assign(x, fml, vl); subst(x, vl, fml, nullptr); TRACE("qe", tout << mk_pp(fml, m) << "\n";); return true; } bool find_min_max(bool is_lower, bool is_strict, bounds_proc& bounds, model_evaluator& eval, rational& bound, unsigned& idx) { bool found = false; unsigned num_bounds = bounds.size(is_strict, is_lower); rational num; for (unsigned i = 0; i < num_bounds; ++i) { expr_ref vl(m); eval(bounds.atoms(is_strict, is_lower)[i], vl); if (!m.is_true(vl)) { continue; } eval(bounds.exprs(is_strict, is_lower)[i], vl); VERIFY(m_util.m_arith.is_numeral(vl, num)); num /= abs(bounds.coeffs(is_strict,is_lower)[i]); if (found) { if (is_lower?(num < bound):(num > bound)) { bound = num; idx = i; } } else { found = true; idx = i; bound = num; } } return found; } bool get_bound_sizes(bounds_proc& bounds, app* x, unsigned& t_size, unsigned& e_size) { unsigned le_size = bounds.le_size(); unsigned ge_size = bounds.ge_size(); if (m_util.is_real(x)) { le_size *= 2; ge_size *= 2; } if (le_size + bounds.lt_size() < ge_size + bounds.gt_size()) { e_size = le_size; t_size = bounds.lt_size(); return true; } else { e_size = ge_size; t_size = bounds.gt_size(); return false; } } void add_cache(app* x, expr* fml, unsigned v, expr* result, rational coeff, expr* term) { m_trail.push_back(x); m_trail.push_back(fml); m_trail.push_back(result); if (term) m_trail.push_back(term); m_subst.insert(branch_formula(fml, x, v, result, coeff, term, m_util.m_vars_added)); } bool get_cache(app* x, expr* fml, unsigned v, expr_ref& result) { branch_formula bf; if (!m_subst.find(branch_formula(fml, x, v, nullptr, rational::zero(), nullptr, m_util.m_vars_added), bf)) { return false; } SASSERT(bf.m_result); result = bf.m_result; for (app* v : bf.m_vars) { m_util.add_var(v, false); } return true; } void assign_divs(contains_app& contains_x, bounds_proc& bounds, x_subst& x_t, expr_ref& result) { app* x = contains_x.x(); app_ref z(m), z_bv(m); rational d; if (!bounds.div_z(d, z_bv, z)) { return; } m_util.add_var(z_bv); // // assert // z < d // d | (x - z) // (c | ax + t <-> c | az + t) for each divisor. // // z < d expr* z_lt_d = m_util.m_arith.mk_le(z, m_util.m_arith.mk_numeral(d-rational(1), true)); m_ctx.add_constraint(false, z_lt_d); TRACE("qe", tout << mk_pp(z_lt_d, m) << "\n";); // result <- result & z <= d - 1 SASSERT(!abs(d).is_one()); rational d1 = d - rational(1); expr_ref tmp(m); m_util.m_arith_rewriter.mk_le(z, m_util.m_arith.mk_numeral(d1, true), tmp); m_util.m_bool_rewriter.mk_and(result, tmp, result); // d | (x - z) expr_ref t1(m), new_atom(m); t1 = m_util.mk_sub(x, z); m_util.mk_divides(d, t1, new_atom); m_ctx.add_constraint(false, new_atom); TRACE("qe", tout << mk_pp(new_atom, m) << "\n";); // (c | ax + t <-> c | az + t) for each divisor. mk_div_equivs(bounds, z, result); TRACE("qe", tout << mk_pp(result, m) << "\n";); // update x_t to map x |-> dx + z x_t.set_term(z); x_t.set_coeff(d); } // // (c | ax + t <-> c | az + t) for each divisor. // void mk_div_equivs(bounds_proc& bounds, expr* z, expr_ref& result) { unsigned sz = bounds.div_size(); app* const* atoms = bounds.div_atoms(); rational const* coeffs = bounds.div_coeffs(); expr* const* terms = bounds.div_terms(); rational const* divisors = bounds.divisors(); expr_ref new_atom(m), t1(m); for (unsigned i = 0; i < sz; ++i) { app* atm = atoms[i]; t1 = m_util.mk_add(m_util.mk_mul(coeffs[i], z), terms[i]); m_util.mk_divides(divisors[i], t1, new_atom); m_util.m_replace.apply_substitution(atm, new_atom.get(), result); m_ctx.add_constraint(false, mk_not(atm), new_atom); m_ctx.add_constraint(false, mk_not(new_atom), atm); } } void assign_nested_divs(contains_app& contains_x, bounds_proc& bounds, expr_ref& result) { unsigned num_nested_divs = bounds.nested_div_size(); if (num_nested_divs == 0) { return; } app_ref z(m), z_bv(m); rational d; VERIFY (bounds.div_z(d, z_bv, z)); for (unsigned i = 0; i < num_nested_divs; ++i) { // // mod_term = arg_0 mod k // app* atm = bounds.nested_div_atom(i); rational const& k = bounds.nested_divisor(i); app* z1_bv = bounds.nested_div_z_bv(i); app* z1 = bounds.nested_div_z(i); m_util.add_var(z1_bv); // // assert // z < k // (coeff*x + rest - z) mod k == 0 // expr* z_lt_k = m_util.m_arith.mk_le(z1, m_util.m_arith.mk_numeral(k-rational(1), true)); m_ctx.add_constraint(false, z_lt_k); expr* e1 = m_util.m_arith.mk_sub(atm->get_arg(0), z1); expr* e2 = atm->get_arg(1); expr_ref mod_term2(m_util.m_arith.mk_mod(e1, e2), m); m_util.simplify(mod_term2); m_ctx.add_constraint(false, m.mk_eq(mod_term2, m_util.mk_zero(mod_term2))); m_util.m_replace.apply_substitution(atm, z1, result); // // conjoin (coeff*z + rest - z1) mod k == 0 to result // expr_ref mod_eq(m), tmp1(m), tmp2(m); tmp2 = m_util.mk_numeral(bounds.nested_div_coeff(i), true); tmp1 = m_util.m_arith.mk_mul(tmp2, z1); tmp2 = m_util.m_arith.mk_sub(bounds.nested_div_term(i), z); tmp2 = m_util.m_arith.mk_add(tmp1, tmp2); tmp1 = m_util.m_arith.mk_mod(tmp2, bounds.nested_div_atom(i)->get_arg(1)); mod_eq = m.mk_eq(tmp1, m_util.mk_zero(z)); m_util.simplify(mod_eq); result = m.mk_and(result, mod_eq); TRACE("qe", tout << mk_pp(mod_eq, m) << "\n";); } } bounds_proc& get_bounds(app* x, expr* fml) { bounds_proc* result = nullptr; VERIFY (m_bounds_cache.find(x, fml, result)); return *result; } void mk_non_bounds(bounds_proc& bounds, bool is_strict, bool is_lower, expr_ref& result) { unsigned sz = bounds.size(is_strict, is_lower); for (unsigned i = 0; i < sz; ++i) { app* e = bounds.atoms(is_strict, is_lower)[i]; m_ctx.add_constraint(true, mk_not(e)); m_util.m_replace.apply_substitution(e, m.mk_false(), result); } } void mk_non_resolve(bounds_proc& bounds, bool is_strict, bool is_lower, expr_ref& result) { unsigned sz = bounds.size(is_strict, !is_lower); for (unsigned i = 0; i < sz; ++i) { app* e = bounds.atoms(is_strict, !is_lower)[i]; m_ctx.add_constraint(true, e); m_util.m_replace.apply_substitution(e, m.mk_true(), result); } } // // phi[x < t, x <= s, x >= u, x > v] // // x = +oo: phi[false, false, true, true] // x < t: phi[true, t-e < s, t - e >= u, t - e > v] == phi[true, t <= s, t > u, t > v] // x < s: phi[s-e < t, true, s - e >= u, s - e > v] == phi[s <= t, true, s > u, s > v] // x = s: phi[s < t, true, s >= u, s > v] // // assert // path1 => x < t // bounds: // path1 => x < t' => t < t' when index(t') < index(t) // path1 => x < t' => t <= t' when index(t') >= index(t) // path1 => x <= s => t <= s // resolve: // path1 => x >= u => t > u // path1 => x > v => t > v // symmetry reduction: // // // path2 => x <= s // bounds: // path2 => x < s => x < t => s <= t // path2 => x = s => x < t => s < t // path2 => x <= s => x <= s' => s < s' when index(s') < index(s) // path2 => x <= s => x <= s' => s <= s' when index(s') >= index(s) // resolve: // path2 => x < s => x >= u => s > u // path2 => x = s => x >= u => s >= u // path2 => x <= s => x > v => s > v // void mk_bound(bool is_strict, bool is_lower, rational const& a, expr* t, rational const& b, expr* s, expr_ref& result) { if (is_strict) { if (is_lower) { // b*t > a*s m_util.mk_strict_bound(b, s, a, t, result); } else { // b*t < a*s m_util.mk_strict_bound(a, t, b, s, result); } } else { if (is_lower) { // b*t >= a*s m_util.mk_bound(b, s, a, t, result); } else { // b*t <= a*s m_util.mk_bound(a, t, b, s, result); } } m_util.simplify(result); TRACE("qe", tout << (is_strict?"strict":"non-strict") << "\n"; tout << (is_lower?"is-lower":"is-upper") << "\n"; tout << "a: " << a << " " << mk_pp(t, m) << "\n"; tout << "b: " << b << " " << mk_pp(s, m) << "\n"; tout << mk_pp(result, m) << "\n";); } // // a*x <= t, a*x < t // /* - bounds - add_assertion - flag whether to add side-effect to state - x - the variable to be eliminated - is_strict - whether to loop over strict inequalities - is_eq_ctx - whether non-strict inequality is to be treated as equality case. - is_strict_ctx - whether 'atm' is a strict inequality - is_lower - whether 'x' is given a lower-bound in 'atm' - index - index of 'atm' in 'bounds' 'atm = bounds[index]' - a - coefficient to 'x' in 'atm' - t - upper/lower bound to 'x' in 'atm' */ void mk_bounds(bounds_proc& bounds, app* x, bool is_strict, bool is_eq_ctx, bool is_strict_ctx, bool is_lower, unsigned index, rational const& a, expr* t, expr_ref& result) { TRACE("qe", tout << mk_pp(t, m) << "\n";); SASSERT(!is_eq_ctx || !is_strict_ctx); unsigned sz = bounds.size(is_strict, is_lower); expr_ref tmp(m), eq(m); bool same_strict = (is_strict == is_strict_ctx); bool non_strict_real = m_util.is_real(x) && !is_strict_ctx; app* atm = bounds.atoms(is_strict_ctx, is_lower)[index]; for (unsigned i = 0; i < sz; ++i) { app* e = bounds.atoms(is_strict, is_lower)[i]; expr_ref s(bounds.exprs(is_strict, is_lower)[i], m); rational b = bounds.coeffs(is_strict, is_lower)[i]; if (same_strict && i == index) { if (non_strict_real) { m_util.mk_eq(a, x, t, eq); TRACE("qe", tout << "a:" << a << " x: " << mk_pp(x, m) << "t: " << mk_pp(t, m) << " eq: " << mk_pp(eq, m) << "\n";); if (is_eq_ctx) { m_ctx.add_constraint(true, eq); } else { m_ctx.add_constraint(true, mk_not(eq)); m_ctx.add_constraint(true, e); } } else { m_ctx.add_constraint(true, e); } m_util.m_replace.apply_substitution(atm, m.mk_true(), result); continue; } // // Break symmetries by using index: // bounds before me are strictly larger. // Cases: // ax <= t & ax != t & bx < s => bt <= as // ax <= t & ax = t & bx < s => bt < as // bx <= s => bt < as or bt <= as depending on symmetry // bool result_is_strict = (non_strict_real && is_eq_ctx && is_strict) || (same_strict && i < index); mk_bound(result_is_strict, is_lower, a, t, b, s, tmp); m_util.m_replace.apply_substitution(e, tmp.get(), result); TRACE("qe", tout << (result_is_strict?"strict result":"non-strict result") << "\n"; tout << (is_strict?"strict":"non-strict") << "\n"; tout << mk_pp(atm, m) << " & "; tout << mk_pp(e, m) << " --> "; tout << mk_pp(tmp.get(), m) << "\n";); m_ctx.add_constraint(true, mk_not(e), tmp); } } // x <= t // x != t => x >= u => t > u // x = t => x >= u => t >= u void mk_resolve(bounds_proc& bounds, app* x, x_subst& x_t, bool is_strict, bool is_eq_ctx, bool is_strict_ctx, bool is_lower, unsigned index, rational const& a, expr* t, expr_ref& result) { expr_ref tmp(m); unsigned sz = bounds.size(is_strict, !is_lower); bool is_strict_real = !is_eq_ctx && m_util.is_real(x) && !is_strict_ctx; bool strict_resolve = is_strict || is_strict_ctx || is_strict_real; for (unsigned i = 0; i < sz; ++i) { app* e = bounds.atoms(is_strict, !is_lower)[i]; expr_ref s(bounds.exprs(is_strict, !is_lower)[i], m); rational b = bounds.coeffs(is_strict, !is_lower)[i]; SASSERT(!b.is_zero()); SASSERT(b.is_pos() != a.is_pos()); s = x_t.mk_term(b, s); b = x_t.mk_coeff(b); m_util.mk_resolve(x, strict_resolve, a, t, b, s, tmp); expr_ref save_result(result); m_util.m_replace.apply_substitution(e, tmp.get(), result); m_ctx.add_constraint(true, mk_not(e), tmp); TRACE("qe_verbose", app* atm = bounds.atoms(is_strict_ctx, is_lower)[index]; tout << mk_pp(atm, m) << " "; tout << mk_pp(e, m) << " ==>\n"; tout << mk_pp(tmp, m) << "\n"; tout << "old fml: " << mk_pp(save_result, m) << "\n"; tout << "new fml: " << mk_pp(result, m) << "\n"; ); } } bool update_bounds(bounds_proc& bounds, contains_app& contains_x, expr* fml, atom_set const& tbl, bool is_pos) { app_ref tmp(m); atom_set::iterator it = tbl.begin(), end = tbl.end(); for (; it != end; ++it) { app* e = *it; if (!contains_x(e)) { continue; } if (!is_pos) { SASSERT(!m.is_not(e)); tmp = m.mk_not(e); e = tmp; } if (!bounds.get_bound(contains_x, e)) { return false; } } return true; } bool update_bounds(contains_app& contains_x, expr* fml) { bounds_proc* bounds = nullptr; if (m_bounds_cache.find(contains_x.x(), fml, bounds)) { return true; } bounds = alloc(bounds_proc, m_util); if (!update_bounds(*bounds, contains_x, fml, m_ctx.pos_atoms(), true)) { dealloc(bounds); return false; } if (!update_bounds(*bounds, contains_x, fml, m_ctx.neg_atoms(), false)) { dealloc(bounds); return false; } m_trail.push_back(contains_x.x()); m_trail.push_back(fml); m_bounds_cache.insert(contains_x.x(), fml, bounds); return true; } }; // --------------------- // non-linear arithmetic class nlarith_plugin : public qe_solver_plugin { typedef obj_map weight_m; typedef obj_pair_map bcs_t; typedef obj_map weights_t; bcs_t m_cache; weights_t m_weights; th_rewriter m_rewriter; nlarith::util m_util; expr_safe_replace m_replace; expr_ref_vector m_trail; factor_rewriter_star m_factor_rw; bool m_produce_models; public: nlarith_plugin(i_solver_context& ctx, ast_manager& m, bool produce_models) : qe_solver_plugin(m, m.mk_family_id("arith"), ctx), m_rewriter(m), m_util(m), m_replace(m), m_trail(m), m_factor_rw(m), m_produce_models(produce_models) { TRACE("qe", tout << "produce models: " << produce_models << "\n";); m_util.set_enable_linear(true); // (produce_models); } ~nlarith_plugin() override { bcs_t::iterator it = m_cache.begin(), end = m_cache.end(); for (; it != end; ++it) { dealloc(it->get_value()); } weights_t::iterator it2 = m_weights.begin(), e2 = m_weights.end(); for (; it2 != e2; ++it2) { dealloc(it2->get_value()); } } bool simplify(expr_ref& fml) override { expr_ref tmp(m), tmp2(m); m_factor_rw(fml, tmp); m_rewriter(tmp, tmp2); if (fml.get() != tmp2.get()) { fml = tmp2; return true; } return false; } void assign(contains_app& x, expr* fml, rational const& vl) override { nlarith::branch_conditions *brs = nullptr; VERIFY (m_cache.find(x.x(), fml, brs)); SASSERT(vl.is_unsigned()); SASSERT(vl.get_unsigned() < brs->size()); expr* branch_fml = brs->branches(vl.get_unsigned()); expr_ref result(m), tmp(m); m_factor_rw(branch_fml, tmp); m_rewriter(tmp, result); TRACE("qe", tout << vl << " " << mk_pp(result.get(), m) << "\n";); m_ctx.add_constraint(true, result); } bool get_num_branches(contains_app& x, expr* fml, rational& num_branches) override { nlarith::branch_conditions *brs; if (m_cache.find(x.x(), fml, brs)) { num_branches = rational(brs->size()); return true; } expr_ref_vector lits(m); update_bounds(lits, m_ctx.pos_atoms(), true); update_bounds(lits, m_ctx.neg_atoms(), false); brs = alloc(nlarith::branch_conditions, m); TRACE("nlarith", tout << mk_pp(fml, m) << "\n";); if (!m_util.create_branches(x.x(), lits.size(), lits.c_ptr(), *brs)) { TRACE("nlarith", tout << "no branches for " << mk_pp(x.x(), m) << "\n";); dealloc(brs); return false; } num_branches = rational(brs->size()); insert_cache(x.x(), fml, brs); return true; } void subst(contains_app& x, rational const& vl, expr_ref& fml, expr_ref* def) override { nlarith::branch_conditions *brs = nullptr; VERIFY (m_cache.find(x.x(), fml, brs)); SASSERT(vl.is_unsigned()); SASSERT(vl.get_unsigned() < brs->size()); unsigned j = vl.get_unsigned(); m_replace.reset(); for (unsigned i = 0; i < brs->preds().size(); ++i) { m_replace.insert(brs->preds(i), brs->subst(j)[i]); } m_replace(fml); expr_ref tmp(m.mk_and(brs->constraints(j), fml), m); m_factor_rw(tmp, fml); if (def) { m_factor_rw(brs->def(j), *def); } } unsigned get_weight(contains_app& x, expr* fml) override { obj_map* weights = nullptr; unsigned weight = 0; if (!m_weights.find(fml, weights)) { weights = alloc(weight_m); m_weights.insert(fml, weights); m_trail.push_back(fml); ptr_vector nl_vars; m_util.extract_non_linear(to_app(fml), nl_vars); for (unsigned i = 0; i < nl_vars.size(); ++i) { weights->insert(nl_vars[i], 100); } } if (weights->find(x.x(), weight)) { return weight; } return UINT_MAX; } bool solve(conj_enum& conjs, expr* fml) override { return false; } // we don't need to modify the atom. bool mk_atom(expr* e, bool p, expr_ref& result) override { return false; } bool is_uninterpreted(app* f) override { if (m_produce_models) { return true; } switch(f->get_decl_kind()) { case OP_NUM: case OP_LE: case OP_LT: case OP_GE: case OP_GT: case OP_ADD: case OP_SUB: case OP_UMINUS: return false; case OP_MUL: { arith_util a(m); expr* m, *n; if (a.is_mul(f, m, n) && (a.is_numeral(m) || a.is_numeral(n))) { return false; } return true; } default: return true; } return true; } private: void insert_cache(app* x, expr* e, nlarith::branch_conditions* brs) { m_trail.push_back(x); m_trail.push_back(e); m_cache.insert(x, e, brs); } void update_bounds(expr_ref_vector& lits, atom_set const& tbl, bool is_pos) { atom_set::iterator it = tbl.begin(), end = tbl.end(); for (; it != end; ++it) { app* e = *it; lits.push_back(is_pos?e:m.mk_not(e)); } } }; qe_solver_plugin* mk_arith_plugin(i_solver_context& ctx, bool produce_models, smt_params& p) { if (p.m_nlquant_elim) { return alloc(nlarith_plugin, ctx, ctx.get_manager(), produce_models); } else { return alloc(arith_plugin, ctx, ctx.get_manager(), p); } } } z3-z3-4.8.7/src/qe/qe_array_plugin.cpp000066400000000000000000000243331356505360400175150ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "qe/qe.h" #include "ast/array_decl_plugin.h" #include "ast/rewriter/expr_safe_replace.h" #include "ast/ast_pp.h" #include "ast/arith_decl_plugin.h" namespace qe { // --------------------- // arrays class array_plugin : public qe_solver_plugin { expr_safe_replace m_replace; public: array_plugin(i_solver_context& ctx, ast_manager& m) : qe_solver_plugin(m, m.mk_family_id("array"), ctx), m_replace(m) { } ~array_plugin() override {} void assign(contains_app& x, expr* fml, rational const& vl) override { UNREACHABLE(); } bool get_num_branches( contains_app& x, expr* fml, rational& num_branches) override { return false; } void subst(contains_app& x, rational const& vl, expr_ref& fml, expr_ref* def) override { UNREACHABLE(); } bool solve(conj_enum& conjs, expr* fml) override { conj_enum::iterator it = conjs.begin(), end = conjs.end(); for (; it != end; ++it) { expr* e = *it; if (m.is_eq(e) && solve_eq(to_app(e), fml)) { return true; } } expr_ref_vector eqs(m); conjs.extract_equalities(eqs); for (unsigned i = 0; i < eqs.size(); ++i) { TRACE("qe_verbose", tout << mk_pp(eqs[i].get(), m) << "\n";); expr* e = eqs[i].get(); if (solve_eq_zero(e, fml)) { return true; } } return false; } bool is_uninterpreted(app* f) override { return true; } private: bool solve_eq(app* eq, expr* fml) { SASSERT(m.is_eq(eq)); expr* arg1 = eq->get_arg(0); expr* arg2 = eq->get_arg(1); return solve_eq(arg1, arg2, fml) || solve_eq(arg2, arg1, fml); } bool solve_eq_zero(expr* e, expr* fml) { arith_util arith(m); if (arith.is_add(e)) { app* a = to_app(e); expr* e1, *e2; unsigned sz = a->get_num_args(); expr_ref_vector args(m); expr_ref lhs(m), rhs(m); rational r; args.append(sz, a->get_args()); for (unsigned i = 0; i < sz; ++i) { expr_ref save(m); save = lhs = args[i].get(); args[i] = arith.mk_numeral(rational(0), m.get_sort(lhs)); rhs = arith.mk_uminus(arith.mk_add(args.size(), args.c_ptr())); if (arith.is_mul(lhs, e1, e2) && arith.is_numeral(e1, r) && r.is_minus_one()) { lhs = to_app(e2); rhs = arith.mk_uminus(rhs); } if (solve_eq(lhs, rhs, fml)) { return true; } args[i] = save; } } return false; } bool solve_eq(expr* lhs, expr* rhs, expr* fml) { if (!is_app(lhs)) { return false; } TRACE("qe_verbose", tout << mk_pp(lhs, m) << " == " << mk_pp(rhs, m) << "\n";); expr_ref tmp(m); app* a = to_app(lhs); // // A = t, A not in t. // unsigned idx = 0; if (m_ctx.is_var(a, idx) && !m_ctx.contains(idx)(rhs)) { expr_ref result(fml, m); m_replace.apply_substitution(a, rhs, result); m_ctx.elim_var(idx, result, rhs); return true; } if (solve_store(a, rhs, fml)) { return true; } if (solve_select(a, rhs, fml)) { return true; } return false; } bool solve_select2(app* lhs, expr* rhs, expr* fml) { // // (select (select A i) j) = t, A not in i, j, t // A |-> (store B' j (store B i t)), where B, B' are fresh. // // TBD return false; } bool solve_select(app* lhs, expr* rhs, expr* fml) { // // (select A i) = t, A not in i, v, t // A |-> (store B i t), where B is fresh. // unsigned idx = 0; vector > args; if (is_select(lhs, idx, rhs, args) && args.size() == 1) { contains_app& contains_A = m_ctx.contains(idx); app* A = contains_A.x(); app_ref B(m); expr_ref store_B_i_t(m); unsigned num_args = args[0].size(); B = m.mk_fresh_const("B", m.get_sort(A)); ptr_buffer args2; args2.push_back(B); for (unsigned i = 0; i < num_args; ++i) { args2.push_back(args[0][i]); } args2.push_back(rhs); store_B_i_t = m.mk_app(m_fid, OP_STORE, args2.size(), args2.c_ptr()); TRACE("qe", tout << "fml: " << mk_pp(fml, m) << "\n"; tout << "solved form: " << mk_pp(store_B_i_t, m) << "\n"; tout << "eq: " << mk_pp(lhs, m) << " == " << mk_pp(rhs, m) << "\n"; ); expr_ref result(fml, m); m_replace.apply_substitution(A, store_B_i_t, result); m_ctx.add_var(B); m_ctx.elim_var(idx, result, store_B_i_t); return true; } return false; } bool is_array_app_of(app* a, unsigned& idx, expr* t, decl_kind k) { if (!is_app_of(a, m_fid, k)) { return false; } expr* v = a->get_arg(0); if (!m_ctx.is_var(v, idx)) { return false; } contains_app& contains_v = m_ctx.contains(idx); for (unsigned i = 1; i < a->get_num_args(); ++i) { if (contains_v(a->get_arg(i))) { return false; } } if (contains_v(t)) { return false; } return true; } bool solve_store(app* lhs, expr* rhs, expr* fml) { // // store(store(A, j, u), i, v) = t, A not in i, j, u, v, t // -> // A |-> store(store(t, i, w), j, w') where w, w' are fresh. // t[i] = v // store(t, i, v)[j] = u // unsigned idx = 0; vector > args; if (is_store_update(lhs, idx, rhs, args)) { contains_app& contains_A = m_ctx.contains(idx); app* A = contains_A.x(); app_ref w(m); expr_ref store_t(rhs, m), store_T(rhs, m), select_t(m); ptr_vector args2; for (unsigned i = args.size(); i > 0; ) { --i; args2.reset(); w = m.mk_fresh_const("w", m.get_sort(args[i].back())); args2.push_back(store_T); args2.append(args[i]); select_t = m.mk_app(m_fid, OP_SELECT, args2.size()-1, args2.c_ptr()); fml = m.mk_and(fml, m.mk_eq(select_t, args2.back())); store_T = m.mk_app(m_fid, OP_STORE, args2.size(), args2.c_ptr()); args2[0] = store_t; args2.back() = w; store_t = m.mk_app(m_fid, OP_STORE, args2.size(), args2.c_ptr()); m_ctx.add_var(w); } TRACE("qe", tout << "Variable: " << mk_pp(A, m) << "\n"; tout << "fml: " << mk_pp(fml, m) << "\n"; tout << "solved form: " << mk_pp(store_t, m) << "\n"; tout << "eq: " << mk_pp(lhs, m) << " == " << mk_pp(rhs, m) << "\n"; ); expr_ref result(fml, m); m_replace.apply_substitution(A, store_t, result); m_ctx.elim_var(idx, result, store_t); return true; } return false; } bool is_array_app_of(app* a, unsigned& idx, expr* t, decl_kind k, vector >& args) { if (m_ctx.is_var(a, idx)) { contains_app& contains_v = m_ctx.contains(idx); if (args.empty() || contains_v(t)) { return false; } for (unsigned i = 0; i < args.size(); ++i) { for (unsigned j = 0; j < args[i].size(); ++j) { if (contains_v(args[i][j])) { return false; } } } return true; } if (!is_app_of(a, m_fid, k)) { return false; } args.push_back(ptr_vector()); for (unsigned i = 1; i < a->get_num_args(); ++i) { args.back().push_back(a->get_arg(i)); } if (!is_app(a->get_arg(0))) { return false; } return is_array_app_of(to_app(a->get_arg(0)), idx, t, k, args); } bool is_store_update(app* a, unsigned& idx, expr* t, vector >& args) { return is_array_app_of(a, idx, t, OP_STORE, args); } bool is_select(app* a, unsigned& idx, expr* t, vector >& args) { return is_array_app_of(a, idx, t, OP_SELECT, args); } }; qe_solver_plugin* mk_array_plugin(i_solver_context& ctx) { return alloc(array_plugin, ctx, ctx.get_manager()); } } z3-z3-4.8.7/src/qe/qe_arrays.cpp000066400000000000000000001666311356505360400163320ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: qe_arrays.cpp Abstract: Model based projection for arrays Author: Nikolaj Bjorner (nbjorner) 2015-06-13 Revision History: --*/ #include "util/lbool.h" #include "ast/rewriter/rewriter_def.h" #include "ast/expr_functors.h" #include "ast/for_each_expr.h" #include "ast/rewriter/expr_safe_replace.h" #include "ast/rewriter/th_rewriter.h" #include "ast/ast_util.h" #include "ast/ast_pp.h" #include "model/model_evaluator.h" #include "qe/qe_arrays.h" #include "qe/qe_term_graph.h" namespace { bool is_partial_eq (app* a); /** * \brief utility class for partial equalities * * A partial equality (a ==I b), for two arrays a,b and a finite set of indices I holds * iff (Forall i. i \not\in I => a[i] == b[i]); in other words, it is a * restricted form of the extensionality axiom * * using this class, we denote (a =I b) as f(a,b,i0,i1,...) * where f is an uninterpreted predicate with name PARTIAL_EQ and * I = {i0,i1,...} */ // TBD: make work for arrays with multiple arguments. class peq { ast_manager& m; expr_ref m_lhs; expr_ref m_rhs; vector m_diff_indices; func_decl_ref m_decl; // the partial equality declaration app_ref m_peq; // partial equality application app_ref m_eq; // equivalent std equality using def. of partial eq array_util m_arr_u; public: static const char* PARTIAL_EQ; peq (app* p, ast_manager& m): m (m), m_lhs (p->get_arg (0), m), m_rhs (p->get_arg (1), m), m_decl (p->get_decl (), m), m_peq (p, m), m_eq (m), m_arr_u (m) { VERIFY (is_partial_eq (p)); SASSERT (m_arr_u.is_array (m_lhs) && m_arr_u.is_array (m_rhs) && m.get_sort(m_lhs) == m.get_sort(m_rhs)); unsigned arity = get_array_arity(m.get_sort(m_lhs)); for (unsigned i = 2; i < p->get_num_args (); i += arity) { SASSERT(arity + i <= p->get_num_args()); expr_ref_vector vec(m); vec.append(arity, p->get_args() + i); m_diff_indices.push_back (vec); } } peq (expr* lhs, expr* rhs, vector const& diff_indices, ast_manager& m): m (m), m_lhs (lhs, m), m_rhs (rhs, m), m_diff_indices (diff_indices), m_decl (m), m_peq (m), m_eq (m), m_arr_u (m) { SASSERT (m_arr_u.is_array (lhs) && m_arr_u.is_array (rhs) && m.get_sort(lhs) == m.get_sort(rhs)); ptr_vector sorts; sorts.push_back (m.get_sort (m_lhs)); sorts.push_back (m.get_sort (m_rhs)); for (auto const& v : diff_indices) { SASSERT(v.size() == get_array_arity(m.get_sort(m_lhs))); for (expr* e : v) sorts.push_back (m.get_sort(e)); } m_decl = m.mk_func_decl (symbol (PARTIAL_EQ), sorts.size (), sorts.c_ptr (), m.mk_bool_sort ()); } expr_ref lhs () { return m_lhs; } expr_ref rhs () { return m_rhs; } void get_diff_indices (vector& result) { result.append(m_diff_indices); } app_ref mk_peq () { if (!m_peq) { ptr_vector args; args.push_back (m_lhs); args.push_back (m_rhs); for (auto const& v : m_diff_indices) { args.append (v.size(), v.c_ptr()); } m_peq = m.mk_app (m_decl, args.size (), args.c_ptr ()); } return m_peq; } app_ref mk_eq (app_ref_vector& aux_consts, bool stores_on_rhs = true) { if (!m_eq) { expr_ref lhs (m_lhs, m), rhs (m_rhs, m); if (!stores_on_rhs) { std::swap (lhs, rhs); } // lhs = (...(store (store rhs i0 v0) i1 v1)...) sort* val_sort = get_array_range (m.get_sort (lhs)); for (expr_ref_vector const& diff : m_diff_indices) { ptr_vector store_args; store_args.push_back (rhs); store_args.append (diff.size(), diff.c_ptr()); app_ref val(m.mk_fresh_const ("diff", val_sort), m); store_args.push_back (val); aux_consts.push_back (val); rhs = m_arr_u.mk_store (store_args); } m_eq = m.mk_eq (lhs, rhs); } return m_eq; } }; const char* peq::PARTIAL_EQ = "!partial_eq"; bool is_partial_eq (app* a) { return a->get_decl ()->get_name () == peq::PARTIAL_EQ; } } namespace qe { static bool is_eq(expr_ref_vector const& xs, expr_ref_vector const& ys) { for (unsigned i = 0; i < xs.size(); ++i) if (xs[i] != ys[i]) return false; return true; } static expr_ref mk_eq(expr_ref_vector const& xs, expr_ref_vector const& ys) { ast_manager& m = xs.get_manager(); expr_ref_vector eqs(m); for (unsigned i = 0; i < xs.size(); ++i) eqs.push_back(m.mk_eq(xs[i], ys[i])); return mk_and(eqs); } /** * 1. Extract all equalities (store (store .. (store x i1 v1) i2 v2) .. ) = ... * where x appears and the equalities do not evaluate to false in the current model. * 2. Track them as partial equivalence relations. * 3. Sort them according to nesting depth. * 4. Solve for x by potentially introducing fresh variables. * Thus, (store x i v) = y, then * x = (store y i w), (select y i) = v, where w is fresh and evaluates to (select x i). * Generally, equalities are tracked as x =_idxs y, where x, y are equal up to idxs. */ class array_project_eqs_util { ast_manager& m; array_util m_arr_u; model_ref M; model_evaluator* m_mev; app_ref m_v; // array var to eliminate ast_mark m_has_stores_v; // has stores for m_v expr_ref m_subst_term_v; // subst term for m_v expr_safe_replace m_true_sub_v; // subst for true equalities expr_safe_replace m_false_sub_v; // subst for false equalities expr_ref_vector m_aux_lits_v; expr_ref_vector m_idx_lits_v; app_ref_vector m_aux_vars; void reset_v () { m_v = nullptr; m_has_stores_v.reset (); m_subst_term_v = nullptr; m_true_sub_v.reset (); m_false_sub_v.reset (); m_aux_lits_v.reset (); m_idx_lits_v.reset (); } void reset () { M = nullptr; m_mev = nullptr; reset_v (); m_aux_vars.reset (); } /** * find all array equalities on m_v or containing stores on/of m_v * * also mark terms containing stores on/of m_v */ void find_arr_eqs (expr_ref const& fml, app_ref_vector& eqs) { if (!is_app (fml)) return; ast_mark done; ptr_vector todo; todo.push_back (to_app (fml)); while (!todo.empty ()) { app* a = todo.back (); if (done.is_marked (a)) { todo.pop_back (); continue; } bool all_done = true; bool args_have_stores = false; for (expr * arg : *a) { if (!is_app (arg)) continue; if (!done.is_marked (arg)) { all_done = false; todo.push_back (to_app (arg)); } else if (!args_have_stores && m_has_stores_v.is_marked (arg)) { args_have_stores = true; } } if (!all_done) continue; todo.pop_back (); // mark if a has stores if ((!m_arr_u.is_select (a) && args_have_stores) || (m_arr_u.is_store (a) && (a->get_arg (0) == m_v))) { m_has_stores_v.mark (a, true); TRACE ("qe", tout << "has stores:\n"; tout << mk_pp (a, m) << "\n"; ); } // check if a is a relevant array equality expr * a0 = nullptr, *a1 = nullptr; if (m.is_eq (a, a0, a1)) { if (a0 == m_v || a1 == m_v || (m_arr_u.is_array (a0) && m_has_stores_v.is_marked (a))) { eqs.push_back (a); } } // else, we can check for disequalities and handle them using extensionality, // but it's not necessary done.mark (a, true); } } /** * factor out select terms on m_v using fresh consts */ void factor_selects (app_ref& fml) { expr_map sel_cache (m); ast_mark done; ptr_vector todo; expr_ref_vector pinned (m); // to ensure a reference todo.push_back (fml); while (!todo.empty ()) { app* a = todo.back (); if (done.is_marked (a)) { todo.pop_back (); continue; } expr_ref_vector args (m); bool all_done = true; for (expr * arg : *a) { if (!is_app (arg)) { args.push_back(arg); } else if (!done.is_marked (arg)) { all_done = false; todo.push_back (to_app (arg)); } else if (all_done) { // all done so far.. expr* arg_new = nullptr; proof* pr; sel_cache.get (arg, arg_new, pr); if (!arg_new) { arg_new = arg; } args.push_back (arg_new); } } if (!all_done) continue; todo.pop_back (); expr_ref a_new (m.mk_app (a->get_decl (), args.size (), args.c_ptr ()), m); // if a_new is select on m_v, introduce new constant if (m_arr_u.is_select (a) && (args.get (0) == m_v || m_has_stores_v.is_marked (args.get (0)))) { sort* val_sort = get_array_range (m.get_sort (m_v)); app_ref val_const (m.mk_fresh_const ("sel", val_sort), m); m_aux_vars.push_back (val_const); // extend M to include val_const expr_ref val = (*m_mev)(a_new); M->register_decl (val_const->get_decl (), val); // add equality m_aux_lits_v.push_back (m.mk_eq (val_const, a_new)); // replace select by const a_new = val_const; } if (a != a_new) { sel_cache.insert (a, a_new, nullptr); pinned.push_back (a_new); } done.mark (a, true); } expr* res = nullptr; proof* pr; sel_cache.get (fml, res, pr); if (res) { fml = to_app (res); } } /** * convert partial equality expression p_exp to an equality by * recursively adding stores on diff indices * * add stores on lhs or rhs depending on whether stores_on_rhs is false/true */ void convert_peq_to_eq (expr* p_exp, app_ref& eq, bool stores_on_rhs = true) { peq p (to_app (p_exp), m); app_ref_vector diff_val_consts (m); eq = p.mk_eq (diff_val_consts, stores_on_rhs); m_aux_vars.append (diff_val_consts); // extend M to include diff_val_consts vector I; expr_ref arr = p.lhs (); p.get_diff_indices (I); expr_ref val (m); unsigned num_diff = diff_val_consts.size (); SASSERT (num_diff == I.size ()); for (unsigned i = 0; i < num_diff; i++) { // mk val term ptr_vector sel_args; sel_args.push_back (arr); sel_args.append(I[i].size(), I[i].c_ptr()); expr_ref val_term (m_arr_u.mk_select (sel_args), m); // evaluate and assign to ith diff_val_const val = (*m_mev)(val_term); M->register_decl (diff_val_consts.get (i)->get_decl (), val); } } /** * mk (e0 ==indices e1) * * result has stores if either e0 or e1 or an index term has stores */ app_ref mk_peq (expr* e0, expr* e1, vector const& indices) { peq p (e0, e1, indices, m); return p.mk_peq (); } void find_subst_term (app* eq) { SASSERT(m.is_eq(eq)); vector empty; app_ref p_exp = mk_peq (eq->get_arg (0), eq->get_arg (1), empty); bool subst_eq_found = false; while (true) { TRACE ("qe", tout << "processing peq:\n" << p_exp << "\n";); peq p (p_exp, m); expr_ref lhs = p.lhs(), rhs = p.rhs(); if (!m_has_stores_v.is_marked (lhs)) { std::swap (lhs, rhs); } if (m_has_stores_v.is_marked (lhs) && m_arr_u.is_store(lhs)) { /** project using the equivalence: * * (store(arr0,idx,x) ==I arr1) <-> * * (idx \in I => (arr0 ==I arr1)) /\ * (idx \not\in I => (arr0 ==I+idx arr1) /\ (arr1[idx] == x))) */ vector I; expr_ref_vector idxs (m); p.get_diff_indices (I); app* a_lhs = to_app (lhs); expr* arr0 = a_lhs->get_arg (0); idxs.append(a_lhs->get_num_args() - 2, a_lhs->get_args() + 1); expr* x = a_lhs->get_arg (2); expr* arr1 = rhs; // check if (idx \in I) in M bool idx_in_I = false; expr_ref_vector idx_diseq (m); if (!I.empty ()) { expr_ref_vector vals = (*m_mev)(idxs); for (unsigned i = 0; i < I.size () && !idx_in_I; i++) { if (is_eq(idxs, I.get(i))) { idx_in_I = true; } else { expr_ref idx_eq = mk_eq(idxs, I[i]); expr_ref_vector vals1 = (*m_mev)(I[i]); if (is_eq(vals, vals1)) { idx_in_I = true; m_idx_lits_v.push_back (idx_eq); } else { idx_diseq.push_back (m.mk_not (idx_eq)); } } } } if (idx_in_I) { TRACE ("qe", tout << "store index in diff indices:\n"; tout << mk_pp (m_idx_lits_v.back (), m) << "\n"; ); // arr0 ==I arr1 p_exp = mk_peq (arr0, arr1, I); TRACE ("qe", tout << "new peq:\n"; tout << mk_pp (p_exp, m) << "\n"; ); } else { m_idx_lits_v.append (idx_diseq); // arr0 ==I+idx arr1 I.push_back (idxs); p_exp = mk_peq (arr0, arr1, I); TRACE ("qe", tout << "new peq:\n" << p_exp << "\n"; ); // arr1[idx] == x ptr_vector sel_args; sel_args.push_back (arr1); sel_args.append(idxs.size(), idxs.c_ptr()); expr_ref arr1_idx (m_arr_u.mk_select (sel_args), m); expr_ref eq (m.mk_eq (arr1_idx, x), m); m_aux_lits_v.push_back (eq); TRACE ("qe", tout << "new eq:\n"; tout << mk_pp (eq, m) << "\n"; ); } } else if (lhs == rhs) { // trivial peq (a ==I a) break; } else if (lhs == m_v || rhs == m_v) { subst_eq_found = true; TRACE ("qe", tout << "subst eq found!\n"; ); break; } else { UNREACHABLE (); } } // factor out select terms on m_v from p_exp using fresh constants if (subst_eq_found) { factor_selects (p_exp); TRACE ("qe", tout << "after factoring selects:\n"; tout << mk_pp (p_exp, m) << "\n"; for (unsigned i = m_aux_lits_v.size () - m_aux_vars.size (); i < m_aux_lits_v.size (); i++) { tout << mk_pp (m_aux_lits_v.get (i), m) << "\n"; } ); // find subst_term bool stores_on_rhs = true; app* a = to_app (p_exp); if (a->get_arg (1) == m_v) { stores_on_rhs = false; } app_ref eq (m); convert_peq_to_eq (p_exp, eq, stores_on_rhs); m_subst_term_v = eq->get_arg (1); TRACE ("qe", tout << "subst term found:\n"; tout << mk_pp (m_subst_term_v, m) << "\n"; ); } } /** * compute nesting depths of stores on m_v in true_eqs, as follows: * 0 if m_v appears on both sides of equality * 1 if equality is (m_v=t) * 2 if equality is (store(m_v,i,v)=t) * ... */ unsigned get_nesting_depth(app* eq) { expr* lhs = nullptr, *rhs = nullptr; VERIFY(m.is_eq(eq, lhs, rhs)); bool lhs_has_v = (lhs == m_v || m_has_stores_v.is_marked (lhs)); bool rhs_has_v = (rhs == m_v || m_has_stores_v.is_marked (rhs)); app* store = nullptr; SASSERT (lhs_has_v || rhs_has_v); if (!lhs_has_v && is_app(rhs)) { store = to_app (rhs); } else if (!rhs_has_v && is_app(lhs)) { store = to_app (lhs); } else { // v appears on both sides -- trivial equality // put it in the beginning to simplify it away return 0; } unsigned nd = 0; // nesting depth for (nd = 1; m_arr_u.is_store (store); nd++, store = to_app (store->get_arg (0))) { /* empty */ ; } if (store != m_v) { TRACE("qe", tout << "not a store " << mk_pp(eq, m) << " " << lhs_has_v << " " << rhs_has_v << " " << mk_pp(m_v, m) << "\n";); return UINT_MAX; } return nd; } struct compare_nd { bool operator()(std::pair const& x, std::pair const& y) const { return x < y; } }; /** * try to substitute for m_v, using array equalities * * compute substitution term and aux lits */ bool project (expr_ref const& fml) { app_ref_vector eqs (m); svector > true_eqs; find_arr_eqs (fml, eqs); TRACE ("qe", tout << "array equalities:\n" << eqs << "\n";); // evaluate eqs in M for (app * eq : eqs) { TRACE ("qe", tout << "array equality:\n" << mk_pp (eq, m) << "\n"; ); if (m_mev->is_false(eq)) { m_false_sub_v.insert (eq, m.mk_false()); } else { true_eqs.push_back (std::make_pair(get_nesting_depth(eq), eq)); } } std::sort(true_eqs.begin(), true_eqs.end(), compare_nd()); DEBUG_CODE(for (unsigned i = 0; i + 1 < true_eqs.size(); ++i) SASSERT(true_eqs[i].first <= true_eqs[i+1].first);); // search for subst term for (unsigned i = 0; !m_subst_term_v && i < true_eqs.size(); i++) { app* eq = true_eqs[i].second; m_true_sub_v.insert (eq, m.mk_true ()); // try to find subst term find_subst_term (eq); } return true; } void mk_result (expr_ref& fml) { th_rewriter rw(m); rw (fml); // add in aux_lits and idx_lits expr_ref_vector lits (m); // TODO: eliminate possible duplicates, especially in idx_lits // theory rewriting is a possibility, but not sure if it // introduces unwanted terms such as ite's lits.append (m_idx_lits_v); lits.append (m_aux_lits_v); lits.push_back (fml); fml = mk_and(lits); if (m_subst_term_v) { m_true_sub_v.insert (m_v, m_subst_term_v); m_true_sub_v (fml); } else { m_true_sub_v (fml); m_false_sub_v (fml); } rw(fml); SASSERT (!m.is_false (fml)); } public: array_project_eqs_util (ast_manager& m): m (m), m_arr_u (m), m_v (m), m_subst_term_v (m), m_true_sub_v (m), m_false_sub_v (m), m_aux_lits_v (m), m_idx_lits_v (m), m_aux_vars (m) {} void operator () (model& mdl, app_ref_vector& arr_vars, expr_ref& fml, app_ref_vector& aux_vars) { reset (); model_evaluator mev(mdl); mev.set_model_completion(true); M = &mdl; m_mev = &mev; unsigned j = 0; for (unsigned i = 0; i < arr_vars.size (); i++) { reset_v (); m_v = arr_vars.get (i); if (!m_arr_u.is_array (m_v)) { TRACE ("qe", tout << "not an array variable: " << m_v << "\n"; ); aux_vars.push_back (m_v); continue; } TRACE ("qe", tout << "projecting equalities on variable: " << m_v << "\n"; ); if (project (fml)) { mk_result (fml); contains_app contains_v (m, m_v); if (!m_subst_term_v || contains_v (m_subst_term_v)) { arr_vars[j++] = m_v; } TRACE ("qe", tout << "after projection: \n" << fml << "\n";); } else { IF_VERBOSE(2, verbose_stream() << "can't project:" << m_v << "\n";); TRACE ("qe", tout << "Failed to project: " << m_v << "\n";); arr_vars[j++] = m_v; } } arr_vars.shrink(j); aux_vars.append (m_aux_vars); } }; /** * Eliminate (select (store ..) ..) redexes by evaluating indices under model M. * This does not eliminate variables, but introduces additional constraints on index equalities. */ class array_select_reducer { ast_manager& m; array_util m_arr_u; obj_map m_cache; expr_ref_vector m_pinned; // to ensure a reference expr_ref_vector m_idx_lits; model_ref M; model_evaluator* m_mev; th_rewriter m_rw; ast_mark m_arr_test; ast_mark m_has_stores; bool m_reduce_all_selects; void reset () { m_cache.reset (); m_pinned.reset (); m_idx_lits.reset (); M = nullptr; m_mev = nullptr; m_arr_test.reset (); m_has_stores.reset (); m_reduce_all_selects = false; } bool is_equals (expr *e1, expr *e2) { return e1 == e2 || (*m_mev)(e1) == (*m_mev)(e2); } bool is_equals (unsigned arity, expr * const* xs, expr * const * ys) { for (unsigned i = 0; i < arity; ++i) { if (!is_equals(xs[i], ys[i])) return false; } return true; } expr_ref mk_eq(unsigned arity, expr * const* xs, expr * const * ys) { expr_ref_vector r(m); for (unsigned i = 0; i < arity; ++i) { r.push_back(m.mk_eq(xs[i], ys[i])); } return mk_and(r); } void add_idx_cond (expr_ref& cond) { m_rw (cond); if (!m.is_true (cond)) m_idx_lits.push_back (cond); } bool has_stores (expr* e) { if (m_reduce_all_selects) return true; return m_has_stores.is_marked (e); } void mark_stores (app* a, bool args_have_stores) { if (m_reduce_all_selects) return; if (args_have_stores || (m_arr_u.is_store (a) && m_arr_test.is_marked (a->get_arg (0)))) { m_has_stores.mark (a, true); } } bool reduce (expr_ref& e) { if (!is_app (e)) return true; expr *r = nullptr; if (m_cache.find (e, r)) { e = r; return true; } ptr_vector todo; todo.push_back (to_app (e)); expr_ref_vector args (m); while (!todo.empty ()) { app *a = todo.back (); unsigned sz = todo.size (); bool dirty = false; bool args_have_stores = false; args.reset(); for (expr * arg : *a) { expr *narg = nullptr; if (!is_app (arg)) { args.push_back (arg); } else if (m_cache.find (arg, narg)) { args.push_back (narg); dirty |= (arg != narg); if (!args_have_stores && has_stores (narg)) { args_have_stores = true; } } else { todo.push_back (to_app (arg)); } } if (todo.size () > sz) continue; todo.pop_back (); if (dirty) { r = m.mk_app (a->get_decl (), args.size (), args.c_ptr ()); m_pinned.push_back (r); } else { r = a; } if (m_arr_u.is_select (r) && has_stores (to_app (r)->get_arg (0))) { r = reduce_core (to_app(r)); } else { mark_stores (to_app (r), args_have_stores); } m_cache.insert (a, r); } SASSERT (r); e = r; return true; } /** * \brief reduce (select (store (store x i1 v1) i2 v2) idx) under model M * such that the result is v2 if idx = i2 under M, it is v1 if idx = i1, idx != i2 under M, * and it is (select x idx) if idx != i1, idx !+ i2 under M. */ expr* reduce_core (app *a) { if (!m_arr_u.is_store (a->get_arg (0))) return a; expr* array = a->get_arg(0); unsigned arity = get_array_arity(m.get_sort(array)); expr* const* js = a->get_args() + 1; while (m_arr_u.is_store (array)) { a = to_app (array); expr* const* idxs = a->get_args() + 1; expr_ref cond = mk_eq(arity, idxs, js); if (is_equals (arity, idxs, js)) { add_idx_cond (cond); return a->get_arg (a->get_num_args() - 1); } else { cond = m.mk_not (cond); add_idx_cond (cond); array = a->get_arg (0); } } ptr_vector args; args.push_back(array); args.append(arity, js); expr* r = m_arr_u.mk_select (args); m_pinned.push_back (r); return r; } void mk_result (expr_ref& fml) { // conjoin idx lits expr_ref_vector lits (m); lits.append (m_idx_lits); lits.push_back (fml); fml = mk_and(lits); // simplify all trivial expressions introduced m_rw (fml); TRACE ("qe", tout << "after reducing selects:\n" << fml << "\n";); } public: array_select_reducer (ast_manager& m): m (m), m_arr_u (m), m_pinned (m), m_idx_lits (m), m_rw (m), m_reduce_all_selects (false) {} void operator () (model& mdl, app_ref_vector const& arr_vars, expr_ref& fml, bool reduce_all_selects = false) { if (!reduce_all_selects && arr_vars.empty ()) return; reset (); model_evaluator mev(mdl); mev.set_model_completion(true); M = &mdl; m_mev = &mev; m_reduce_all_selects = reduce_all_selects; // mark vars to eliminate for (app* v : arr_vars) { m_arr_test.mark (v, true); } // assume all arr_vars are of array sort // and assume no store equalities on arr_vars if (reduce (fml)) { mk_result (fml); } else { IF_VERBOSE(2, verbose_stream() << "can't project arrays:" << "\n";); TRACE ("qe", tout << "Failed to project arrays\n";); } } }; /** * Perform Ackerman reduction on arrays. * for occurrences (select a i1), (select a i2), ... assuming these are all occurrences. * - collect i1, i2, i3, into equivalence classes according to M * - for distinct index classes accumulate constraint i1 < i2 < i3 .. (for arithmetic) * and generally distinct(i1, i2, i3) for arbitrary index sorts. * - for equal indices accumulate constraint i1 = i2, i3 = i5, .. * - introduce variables v1, v2, .., for each equivalence class. * - replace occurrences of select by v1, v2, ... * - update M to evaluate v1, v2, the same as (select a i1) (select a i2) */ class array_project_selects_util { typedef obj_map*> sel_map; struct idx_val { expr_ref_vector idx; expr_ref_vector val; vector rval; idx_val(expr_ref_vector & idx, expr_ref_vector & val, vector const& rval): idx(idx), val(val), rval(rval) {} idx_val& operator=(idx_val const& o) { idx.reset(); val.reset(); rval.reset(); idx.append(o.idx); val.append(o.val); rval.append(o.rval); return *this; } }; ast_manager& m; array_util m_arr_u; arith_util m_ari_u; bv_util m_bv_u; sel_map m_sel_terms; // representative indices for eliminating selects vector m_idxs; app_ref_vector m_sel_consts; expr_ref_vector m_idx_lits; model_ref M; model_evaluator* m_mev; expr_safe_replace m_sub; ast_mark m_arr_test; void reset () { m_sel_terms.reset (); m_idxs.reset(); m_sel_consts.reset (); m_idx_lits.reset (); M = nullptr; m_mev = nullptr; m_sub.reset (); m_arr_test.reset (); } /** * collect sel terms on array vars as given by m_arr_test */ void collect_selects (expr* fml) { if (!is_app (fml)) return; ast_mark done; ptr_vector todo; todo.push_back (to_app (fml)); for (unsigned i = 0; i < todo.size(); ++i) { app* a = todo[i]; if (done.is_marked (a)) continue; done.mark (a, true); for (expr* arg : *a) { if (!done.is_marked (arg) && is_app (arg)) { todo.push_back (to_app (arg)); } } if (m_arr_u.is_select (a)) { expr* arr = a->get_arg (0); if (m_arr_test.is_marked (arr)) { ptr_vector* lst = m_sel_terms.find (to_app (arr));; lst->push_back (a); } } } } vector to_num(expr_ref_vector const& vals) { vector rs; rational r; for (expr* v : vals) { if (m_bv_u.is_bv(v)) { VERIFY (m_bv_u.is_numeral(v, r)); } else if (m_ari_u.is_real(v) || m_ari_u.is_int(v)) { VERIFY (m_ari_u.is_numeral(v, r)); } else { r.reset(); } rs.push_back(r); } return rs; } struct compare_idx { array_project_selects_util& u; compare_idx(array_project_selects_util& u):u(u) {} bool operator()(idx_val const& x, idx_val const& y) { SASSERT(x.rval.size() == y.rval.size()); for (unsigned j = 0; j < x.rval.size(); ++j) { rational const& xv = x.rval[j]; rational const& yv = y.rval[j]; if (xv < yv) return true; if (xv > yv) return false; } return false; } }; expr* mk_lt(expr* x, expr* y) { if (m_bv_u.is_bv(x)) { return m.mk_not(m_bv_u.mk_ule(y, x)); } else { return m_ari_u.mk_lt(x, y); } } expr_ref mk_lex_lt(expr_ref_vector const& xs, expr_ref_vector const& ys) { SASSERT(xs.size() == ys.size() && !xs.empty()); expr_ref result(mk_lt(xs.back(), ys.back()), m); for (unsigned i = xs.size()-1; i-- > 0; ) { result = m.mk_or(mk_lt(xs[i], ys[i]), m.mk_and(m.mk_eq(xs[i], ys[i]), result)); } return result; } /** * model based ackermannization for sel terms of some array * * update sub with val consts for sel terms */ void ackermann (ptr_vector const& sel_terms) { if (sel_terms.empty ()) return; expr* v = sel_terms.get (0)->get_arg (0); // array variable sort* v_sort = m.get_sort (v); sort* val_sort = get_array_range (v_sort); unsigned arity = get_array_arity(v_sort); bool is_numeric = true; for (unsigned i = 0; i < arity && is_numeric; ++i) { sort* srt = get_array_domain(v_sort, i); if (!m_ari_u.is_real(srt) && !m_ari_u.is_int(srt) && !m_bv_u.is_bv_sort(srt)) { TRACE("qe", tout << "non-numeric index sort for Ackerman" << mk_pp(srt, m) << "\n";); is_numeric = false; } } unsigned start = m_idxs.size (); // append at the end for (app * a : sel_terms) { expr_ref_vector idxs(m, arity, a->get_args() + 1); expr_ref_vector vals = (*m_mev)(idxs); bool is_new = true; for (unsigned j = start; j < m_idxs.size (); j++) { if (!is_eq(m_idxs[j].val, vals)) continue; // idx belongs to the jth equivalence class; // substitute sel term with ith sel const expr* c = m_sel_consts.get (j); m_sub.insert (a, c); // add equality (idx == repr) m_idx_lits.push_back (mk_eq (idxs, m_idxs[j].idx)); is_new = false; break; } if (is_new) { // new repr, val, and sel const vector rvals = to_num(vals); m_idxs.push_back(idx_val(idxs, vals, rvals)); app_ref c (m.mk_fresh_const ("sel", val_sort), m); m_sel_consts.push_back (c); // substitute sel term with new const m_sub.insert (a, c); // extend M to include c expr_ref val = (*m_mev)(a); M->register_decl (c->get_decl (), val); } } if (start + 1 == m_idxs.size()) { // nothing to differentiate. } else if (is_numeric) { // sort reprs by their value and add a chain of strict inequalities compare_idx cmp(*this); std::sort(m_idxs.begin() + start, m_idxs.end(), cmp); for (unsigned i = start; i + 1 < m_idxs.size(); ++i) { m_idx_lits.push_back (mk_lex_lt(m_idxs[i].idx, m_idxs[i+1].idx)); } } else if (arity == 1) { // create distinct constraint. expr_ref_vector xs(m); for (unsigned i = start; i < m_idxs.size(); ++i) { xs.append(m_idxs[i].idx); } m_idx_lits.push_back(m.mk_distinct(xs.size(), xs.c_ptr())); } else { datatype::util dt(m); sort_ref_vector srts(m); ptr_vector acc; unsigned i = 0; for (expr * x : m_idxs[0].idx) { std::stringstream name; name << "get" << (i++); acc.push_back(mk_accessor_decl(m, symbol(name.str().c_str()), type_ref(m.get_sort(x)))); } constructor_decl* constrs[1] = { mk_constructor_decl(symbol("tuple"), symbol("is-tuple"), acc.size(), acc.c_ptr()) }; datatype::def* dts = mk_datatype_decl(dt, symbol("tuple"), 0, nullptr, 1, constrs); VERIFY(dt.get_plugin()->mk_datatypes(1, &dts, 0, nullptr, srts)); del_datatype_decl(dts); sort* tuple = srts.get(0); ptr_vector const & decls = *dt.get_datatype_constructors(tuple); expr_ref_vector xs(m); for (unsigned i = start; i < m_idxs.size(); ++i) { xs.push_back(m.mk_app(decls[0], m_idxs[i].idx.size(), m_idxs[i].idx.c_ptr())); } m_idx_lits.push_back(m.mk_distinct(xs.size(), xs.c_ptr())); } } void mk_result (expr_ref& fml) { // conjoin idx lits m_idx_lits.push_back(fml); fml = mk_and (m_idx_lits); // substitute for sel terms m_sub (fml); TRACE ("qe", tout << "after projection of selects:\n" << fml << "\n";); } /** * project selects * populates idx lits and obtains substitution for sel terms */ bool project (expr* fml) { // collect sel terms -- populate the map m_sel_terms collect_selects (fml); // model based ackermannization for (auto & kv : m_sel_terms) { TRACE ("qe",tout << "ackermann for var: " << mk_pp (kv.m_key, m) << "\n";); ackermann (*(kv.m_value)); } TRACE ("qe", tout << "idx lits:\n" << m_idx_lits; ); return true; } public: array_project_selects_util (ast_manager& m): m (m), m_arr_u (m), m_ari_u (m), m_bv_u (m), m_sel_consts (m), m_idx_lits (m), m_sub (m) {} void operator () (model& mdl, app_ref_vector& arr_vars, expr_ref& fml, app_ref_vector& aux_vars) { if (arr_vars.empty()) return; reset (); model_evaluator mev(mdl); mev.set_model_completion(true); M = &mdl; m_mev = &mev; // mark vars to eliminate // alloc empty map from array var to sel terms over it for (app* v : arr_vars) { m_arr_test.mark(v, true); m_sel_terms.insert(v, alloc (ptr_vector)); } // assume all arr_vars are of array sort // and they only appear in select terms if (project (fml)) { mk_result (fml); aux_vars.append (m_sel_consts); arr_vars.reset (); } else { IF_VERBOSE(2, verbose_stream() << "can't project arrays:" << "\n";); TRACE ("qe", tout << "Failed to project arrays\n";); } // dealloc for (auto & kv : m_sel_terms) dealloc(kv.m_value); m_sel_terms.reset (); } }; struct array_project_plugin::imp { struct indices { expr_ref_vector m_values; expr* const* m_vars; indices(ast_manager& m, model& model, unsigned n, expr* const* vars): m_values(m), m_vars(vars) { expr_ref val(m); for (unsigned i = 0; i < n; ++i) { m_values.push_back(model(vars[i])); } } }; ast_manager& m; array_util a; scoped_ptr m_var; imp(ast_manager& m): m(m), a(m), m_stores(m) {} ~imp() {} bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) { return false; } void remove_true(expr_ref_vector& lits) { for (unsigned i = 0; i < lits.size(); ++i) { if (m.is_true(lits[i].get())) { project_plugin::erase(lits, i); } } } bool contains_x(expr* e) { return (*m_var)(e); } void mk_eq(indices const& x, indices const& y, expr_ref_vector& lits) { SASSERT(x.m_values.size() == y.m_values.size()); unsigned n = x.m_values.size(); for (unsigned j = 0; j < n; ++j) { lits.push_back(m.mk_eq(x.m_vars[j], y.m_vars[j])); } } bool solve_eq(model& model, app_ref_vector& vars, expr_ref_vector& lits) { // find an equality to solve for. expr* s, *t; for (unsigned i = 0; i < lits.size(); ++i) { if (m.is_eq(lits[i].get(), s, t)) { vector idxs; expr_ref save(m), back(m); save = lits[i].get(); back = lits.back(); lits[i] = back; lits.pop_back(); unsigned sz = lits.size(); if (contains_x(s) && !contains_x(t) && is_app(s)) { if (solve(model, to_app(s), t, idxs, vars, lits)) { return true; } } else if (contains_x(t) && !contains_x(s) && is_app(t)) { if (solve(model, to_app(t), s, idxs, vars, lits)) { return true; } } // put back the equality literal. lits.resize(sz); lits.push_back(back); lits[i] = save; } // TBD: not distinct? } return false; } bool solve(model& model, app* s, expr* t, vector& idxs, app_ref_vector& vars, expr_ref_vector& lits) { SASSERT(contains_x(s)); SASSERT(!contains_x(t)); if (s == m_var->x()) { expr_ref result(t, m); expr_ref_vector args(m); sort* range = get_array_range(m.get_sort(s)); for (unsigned i = 0; i < idxs.size(); ++i) { app_ref var(m), sel(m); expr_ref val(m); var = m.mk_fresh_const("value", range); vars.push_back(var); args.reset(); args.push_back (s); args.append(idxs[i].m_values.size(), idxs[i].m_vars); sel = a.mk_select (args); val = model(sel); model.register_decl (var->get_decl (), val); args[0] = result; args.push_back(var); result = a.mk_store(args.size(), args.c_ptr()); } expr_safe_replace sub(m); sub.insert(s, result); for (unsigned i = 0; i < lits.size(); ++i) { sub(lits[i].get(), result); lits[i] = result; } return true; } if (a.is_store(s)) { unsigned n = s->get_num_args()-2; indices idx(m, model, n, s->get_args()+1); for (unsigned i = 1; i < n; ++i) { if (contains_x(s->get_arg(i))) { return false; } } unsigned i; expr_ref_vector args(m); switch (contains(idx, idxs, i)) { case l_true: mk_eq(idx, idxs[i], lits); return solve(model, to_app(s->get_arg(0)), t, idxs, vars, lits); case l_false: for (unsigned i = 0; i < idxs.size(); ++i) { expr_ref_vector eqs(m); mk_eq(idx, idxs[i], eqs); lits.push_back(m.mk_not(mk_and(eqs))); // TBD: extract single index of difference based on model. } args.push_back(t); args.append(n, s->get_args()+1); lits.push_back(m.mk_eq(a.mk_select(args), s->get_arg(n+1))); idxs.push_back(idx); return solve(model, to_app(s->get_arg(0)), t, idxs, vars, lits); case l_undef: return false; } } return false; } lbool contains(indices const& idx, vector const& idxs, unsigned& j) { for (unsigned i = 0; i < idxs.size(); ++i) { switch (compare(idx, idxs[i])) { case l_true: j = i; return l_true; case l_false: break; case l_undef: return l_undef; } } return l_false; } lbool compare(indices const& idx1, indices const& idx2) { unsigned n = idx1.m_values.size(); for (unsigned i = 0; i < n; ++i) { switch (compare(idx1.m_values[i], idx2.m_values[i])) { case l_true: break; case l_false: return l_false; case l_undef: return l_undef; } } return l_true; } lbool compare(expr* val1, expr* val2) { if (m.are_equal (val1, val2)) return l_true; if (m.are_distinct (val1, val2)) return l_false; if (is_uninterp(val1) || is_uninterp(val2)) { // TBD chase definition of nested array. return l_undef; } return l_undef; } void saturate(model& model, func_decl_ref_vector const& shared, expr_ref_vector& lits) { term_graph tg(m); tg.set_vars(shared, false); tg.add_model_based_terms(model, lits); // need tg to take term and map it to optional rep over the // shared vocabulary if it exists. // . collect shared store expressions, index sorts // . collect shared index expressions // . assert extensionality (add shared index expressions) // . assert store axioms for collected expressions collect_store_expressions(tg, lits); collect_index_expressions(tg, lits); TRACE("qe", tout << "indices\n"; for (auto& kv : m_indices) { tout << sort_ref(kv.m_key, m) << " |-> " << *kv.m_value << "\n"; } tout << "stores " << m_stores << "\n"; tout << "arrays\n"; for (auto& kv : m_arrays) { tout << sort_ref(kv.m_key, m) << " |-> " << *kv.m_value << "\n"; }); assert_extensionality(model, tg, lits); assert_store_select(model, tg, lits); TRACE("qe", tout << lits << "\n";); for (auto& kv : m_indices) { dealloc(kv.m_value); } for (auto& kv : m_arrays) { dealloc(kv.m_value); } m_stores.reset(); m_indices.reset(); m_arrays.reset(); TRACE("qe", tout << "done: " << lits << "\n";); } app_ref_vector m_stores; obj_map m_indices; obj_map m_arrays; void add_index_sort(expr* n) { sort* s = m.get_sort(n); if (!m_indices.contains(s)) { m_indices.insert(s, alloc(app_ref_vector, m)); } } void add_array(app* n) { sort* s = m.get_sort(n); app_ref_vector* vs = nullptr; if (!m_arrays.find(s, vs)) { vs = alloc(app_ref_vector, m); m_arrays.insert(s, vs); } vs->push_back(n); } app_ref_vector* is_index(expr* n) { app_ref_vector* result = nullptr; m_indices.find(m.get_sort(n), result); return result; } struct for_each_store_proc { imp& m_imp; term_graph& tg; for_each_store_proc(imp& i, term_graph& tg) : m_imp(i), tg(tg) {} void operator()(app* n) { if (m_imp.a.is_array(n) && tg.get_model_based_rep(n)) { m_imp.add_array(n); } if (m_imp.a.is_store(n) && (tg.get_model_based_rep(n->get_arg(0)) || tg.get_model_based_rep(n->get_arg(n->get_num_args() - 1)))) { m_imp.m_stores.push_back(n); for (unsigned i = 1; i + 1 < n->get_num_args(); ++i) { m_imp.add_index_sort(n->get_arg(i)); } } } void operator()(expr* e) {} }; struct for_each_index_proc { imp& m_imp; term_graph& tg; for_each_index_proc(imp& i, term_graph& tg) : m_imp(i), tg(tg) {} void operator()(app* n) { auto* v = m_imp.is_index(n); if (v && tg.get_model_based_rep(n)) { v->push_back(n); } } void operator()(expr* e) {} }; void collect_store_expressions(term_graph& tg, expr_ref_vector const& terms) { for_each_store_proc proc(*this, tg); for_each_expr(proc, terms); } void collect_index_expressions(term_graph& tg, expr_ref_vector const& terms) { for_each_index_proc proc(*this, tg); for_each_expr(proc, terms); } bool are_equal(model& mdl, expr* s, expr* t) { return mdl.are_equal(s, t); } void assert_extensionality(model & mdl, term_graph& tg, expr_ref_vector& lits) { for (auto& kv : m_arrays) { app_ref_vector const& vs = *kv.m_value; if (vs.size() <= 1) continue; func_decl_ref_vector ext(m); sort* s = kv.m_key; unsigned arity = get_array_arity(s); for (unsigned i = 0; i < arity; ++i) { ext.push_back(a.mk_array_ext(s, i)); } expr_ref_vector args(m); args.resize(arity + 1); for (unsigned i = 0; i < vs.size(); ++i) { expr* s = vs[i]; for (unsigned j = i + 1; j < vs.size(); ++j) { expr* t = vs[j]; if (are_equal(mdl, s, t)) { lits.push_back(m.mk_eq(s, t)); } else { for (unsigned k = 0; k < arity; ++k) { args[k+1] = m.mk_app(ext.get(k), s, t); } args[0] = t; expr* t1 = a.mk_select(args); args[0] = s; expr* s1 = a.mk_select(args); lits.push_back(m.mk_not(m.mk_eq(t1, s1))); } } } } } void assert_store_select(model & mdl, term_graph& tg, expr_ref_vector& lits) { for (auto& store : m_stores) { assert_store_select(store, mdl, tg, lits); } } void assert_store_select(app* store, model & mdl, term_graph& tg, expr_ref_vector& lits) { SASSERT(a.is_store(store)); ptr_vector indices; for (unsigned i = 1; i + 1 < store->get_num_args(); ++i) { SASSERT(indices.empty()); assert_store_select(indices, store, mdl, tg, lits); } } void assert_store_select(ptr_vector& indices, app* store, model & mdl, term_graph& tg, expr_ref_vector& lits) { unsigned sz = store->get_num_args(); if (indices.size() + 2 == sz) { ptr_vector args; args.push_back(store); for (expr* idx : indices) args.push_back(idx); for (unsigned i = 1; i + 1 < sz; ++i) { expr* idx1 = store->get_arg(i); expr* idx2 = indices[i - 1]; if (!are_equal(mdl, idx1, idx2)) { lits.push_back(m.mk_not(m.mk_eq(idx1, idx2))); lits.push_back(m.mk_eq(store->get_arg(sz-1), a.mk_select(args))); return; } } for (unsigned i = 1; i + 1 < sz; ++i) { expr* idx1 = store->get_arg(i); expr* idx2 = indices[i - 1]; lits.push_back(m.mk_eq(idx1, idx2)); } expr* a1 = a.mk_select(args); args[0] = store->get_arg(0); expr* a2 = a.mk_select(args); lits.push_back(m.mk_eq(a1, a2)); } else { sort* s = m.get_sort(store->get_arg(indices.size() + 1)); for (app* idx : *m_indices[s]) { indices.push_back(idx); assert_store_select(indices, store, mdl, tg, lits); indices.pop_back(); } } } }; array_project_plugin::array_project_plugin(ast_manager& m) { m_imp = alloc(imp, m); } array_project_plugin::~array_project_plugin() { dealloc(m_imp); } bool array_project_plugin::operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) { ast_manager& m = vars.get_manager(); app_ref_vector vvars(m, 1, &var); expr_ref fml = mk_and(lits); (*this)(model, vvars, fml, vars, false); lits.reset(); flatten_and(fml, lits); return true; } bool array_project_plugin::solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) { return m_imp->solve(model, vars, lits); } family_id array_project_plugin::get_family_id() { return m_imp->a.get_family_id(); } void array_project_plugin::operator()(model& mdl, app_ref_vector& arr_vars, expr_ref& fml, app_ref_vector& aux_vars, bool reduce_all_selects) { // 1. project array equalities ast_manager& m = fml.get_manager(); array_project_eqs_util pe (m); pe (mdl, arr_vars, fml, aux_vars); TRACE ("qe", tout << "Projected array eqs: " << fml << "\n"; tout << "Remaining array vars: " << arr_vars << "\n"; tout << "Aux vars: " << aux_vars << "\n"; ); // 2. reduce selects array_select_reducer rs (m); rs (mdl, arr_vars, fml, reduce_all_selects); TRACE ("qe", tout << "Reduced selects:\n" << fml << "\n"; ); // 3. project selects using model based ackermannization array_project_selects_util ps (m); ps (mdl, arr_vars, fml, aux_vars); TRACE ("qe", tout << "Projected array selects: " << fml << "\n"; tout << "All aux vars: " << aux_vars << "\n"; ); } vector array_project_plugin::project(model& model, app_ref_vector& vars, expr_ref_vector& lits) { return vector(); } void array_project_plugin::saturate(model& model, func_decl_ref_vector const& shared, expr_ref_vector& lits) { m_imp->saturate(model, shared, lits); } }; z3-z3-4.8.7/src/qe/qe_arrays.h000066400000000000000000000021251356505360400157620ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: qe_arrays.h Abstract: Model based projection for arrays Author: Nikolaj Bjorner (nbjorner) 2015-06-13 Revision History: --*/ #ifndef __QE_ARRAYS_H_ #define __QE_ARRAYS_H_ #include "ast/array_decl_plugin.h" #include "qe/qe_mbp.h" namespace qe { class array_project_plugin : public project_plugin { struct imp; imp* m_imp; public: array_project_plugin(ast_manager& m); ~array_project_plugin() override; bool operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) override; bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) override; void operator()(model& model, app_ref_vector& vars, expr_ref& fml, app_ref_vector& aux_vars, bool reduce_all_selects); family_id get_family_id() override; vector project(model& model, app_ref_vector& vars, expr_ref_vector& lits) override; void saturate(model& model, func_decl_ref_vector const& shared, expr_ref_vector& lits) override; }; }; #endif z3-z3-4.8.7/src/qe/qe_bool_plugin.cpp000066400000000000000000000126561356505360400173370ustar00rootroot00000000000000/*++ Copyright (c) 2010 Microsoft Corporation Module Name: qe_bool_plugin.cpp Abstract: Eliminate Boolean variable from formula Author: Nikolaj Bjorner (nbjorner) 2010-02-19 Revision History: Notes: The procedure is a bit naive. Consider a co-factoring approach that eliminates all Boolean variables in scope in one shot, similar to what we do with BDDs. --*/ #include "qe/qe.h" #include "ast/rewriter/expr_safe_replace.h" #include "ast/ast_pp.h" #include "model/model_evaluator.h" namespace qe { class bool_plugin : public qe_solver_plugin { expr_safe_replace m_replace; public: bool_plugin(i_solver_context& ctx, ast_manager& m): qe_solver_plugin(m, m.get_basic_family_id(), ctx), m_replace(m) {} void assign(contains_app& x, expr* fml, rational const& vl) override { SASSERT(vl.is_zero() || vl.is_one()); } bool get_num_branches(contains_app& x, expr* fml, rational& nb) override { nb = rational(2); return true; } void subst(contains_app& x, rational const& vl, expr_ref& fml, expr_ref* def) override { SASSERT(vl.is_one() || vl.is_zero()); expr* tf = (vl.is_one())?m.mk_true():m.mk_false(); m_replace.apply_substitution(x.x(), tf, fml); if (def) { *def = tf; } } bool project(contains_app& x, model_ref& model, expr_ref& fml) override { model_evaluator model_eval(*model); expr_ref val_x(m); rational val; model_eval(x.x(), val_x); CTRACE("qe", (!m.is_true(val_x) && !m.is_false(val_x)), tout << "Boolean is a don't care: " << mk_pp(x.x(), m) << "\n";); val = m.is_true(val_x)?rational::one():rational::zero(); subst(x, val, fml, nullptr); return true; } unsigned get_weight(contains_app& contains_x, expr* fml) override { app* x = contains_x.x(); bool p = m_ctx.pos_atoms().contains(x); bool n = m_ctx.neg_atoms().contains(x); if (p && n) { return 3; } return 0; } bool solve(conj_enum& conjs,expr* fml) override { return solve_units(conjs, fml) || solve_polarized(fml); } bool is_uninterpreted(app* a) override { return false; } private: bool solve_units(conj_enum& conjs, expr* _fml) { expr_ref fml(_fml, m); unsigned idx; for (expr * e : conjs) { if (!is_app(e)) { continue; } app* a = to_app(e); expr* e1; if (m_ctx.is_var(a, idx)) { m_replace.apply_substitution(a, m.mk_true(), fml); m_ctx.elim_var(idx, fml, m.mk_true()); return true; } else if (m.is_not(e, e1) && m_ctx.is_var(e1, idx)) { m_replace.apply_substitution(to_app(e1), m.mk_false(), fml); m_ctx.elim_var(idx, fml, m.mk_false()); return true; } } return false; } bool solve_polarized(expr* _fml) { unsigned num_vars = m_ctx.get_num_vars(); expr_ref fml(_fml, m), def(m); for (unsigned i = 0; i < num_vars; ++i) { if (m.is_bool(m_ctx.get_var(i)) && solve_polarized(m_ctx.contains(i), fml, def)) { m_ctx.elim_var(i, fml, def); return true; } } return false; } bool solve_polarized( contains_app& contains_x, expr_ref& fml, expr_ref& def) { app* x = contains_x.x(); bool p = m_ctx.pos_atoms().contains(x); bool n = m_ctx.neg_atoms().contains(x); TRACE("quant_elim", tout << mk_pp(x, m) << " " << mk_pp(fml, m) << "\n";); if (p && n) { return false; } else if (p && !n) { for (expr* y : m_ctx.pos_atoms()) { if (x != y && contains_x(y)) return false; } for (expr* y : m_ctx.neg_atoms()) { if (contains_x(y)) return false; } // only occurrences of 'x' must be in positive atoms def = m.mk_true(); m_replace.apply_substitution(x, def, fml); return true; } else if (!p && n) { for (expr* y : m_ctx.pos_atoms()) { if (contains_x(y)) return false; } for (expr* y : m_ctx.neg_atoms()) { if (x != y && contains_x(y)) return false; } def = m.mk_false(); m_replace.apply_substitution(x, def, fml); return true; } else if (contains_x(fml)) { return false; } else { def = m.mk_false(); return true; } } }; qe_solver_plugin* mk_bool_plugin(i_solver_context& ctx) { return alloc(bool_plugin, ctx, ctx.get_manager()); } } z3-z3-4.8.7/src/qe/qe_bv_plugin.cpp000066400000000000000000000047471356505360400170150ustar00rootroot00000000000000/*++ Copyright (c) 2010 Microsoft Corporation Module Name: bv_plugin.cpp Abstract: Eliminate Bit-vector variable from formula Author: Nikolaj Bjorner (nbjorner) 2010-02-19 Revision History: Notes: --*/ #include "qe/qe.h" #include "ast/rewriter/expr_safe_replace.h" #include "ast/bv_decl_plugin.h" #include "model/model_evaluator.h" namespace qe { class bv_plugin : public qe_solver_plugin { expr_safe_replace m_replace; bv_util m_bv; public: bv_plugin(i_solver_context& ctx, ast_manager& m): qe_solver_plugin(m, m.mk_family_id("bv"), ctx), m_replace(m), m_bv(m) {} void assign(contains_app& x, expr* fml, rational const& vl) override { } bool get_num_branches(contains_app& x, expr* fml, rational& nb) override { unsigned sz = m_bv.get_bv_size(x.x()); nb = power(rational(2), sz); return true; } void subst(contains_app& x, rational const& vl, expr_ref& fml, expr_ref* def) override { app_ref c(m_bv.mk_numeral(vl, m_bv.get_bv_size(x.x())), m); m_replace.apply_substitution(x.x(), c, fml); if (def) { *def = m_bv.mk_numeral(vl, m_bv.get_bv_size(x.x())); } } bool project(contains_app& x, model_ref& model, expr_ref& fml) override { model_evaluator model_eval(*model); expr_ref val_x(m); rational val(0); unsigned bv_size; model_eval(x.x(), val_x); m_bv.is_numeral(val_x, val, bv_size); subst(x, val, fml, nullptr); return true; } unsigned get_weight(contains_app& contains_x, expr* fml) override { return 2; } bool solve(conj_enum& conjs, expr* fml) override { return false; } bool is_uninterpreted(app* f) override { switch(f->get_decl_kind()) { case OP_BSDIV0: case OP_BUDIV0: case OP_BSREM0: case OP_BUREM0: case OP_BSMOD0: case OP_BSDIV_I: case OP_BUDIV_I: case OP_BSREM_I: case OP_BUREM_I: case OP_BSMOD_I: return true; default: return false; } return false; } }; qe_solver_plugin* mk_bv_plugin(i_solver_context& ctx) { return alloc(bv_plugin, ctx, ctx.get_manager()); } } z3-z3-4.8.7/src/qe/qe_cmd.cpp000066400000000000000000000036321356505360400155630ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "qe/qe_cmd.h" #include "qe/qe.h" #include "cmd_context/cmd_context.h" #include "cmd_context/parametric_cmd.h" class qe_cmd : public parametric_cmd { expr * m_target; public: qe_cmd(char const* name = "elim-quantifiers"):parametric_cmd(name) {} char const * get_usage() const override { return " ( )*"; } char const * get_main_descr() const override { return "apply quantifier elimination to the supplied expression"; } void init_pdescrs(cmd_context & ctx, param_descrs & p) override { insert_timeout(p); p.insert("print", CPK_BOOL, "(default: true) print the simplified term."); p.insert("print_statistics", CPK_BOOL, "(default: false) print statistics."); } ~qe_cmd() override { } void prepare(cmd_context & ctx) override { parametric_cmd::prepare(ctx); m_target = nullptr; } cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { if (m_target == nullptr) return CPK_EXPR; return parametric_cmd::next_arg_kind(ctx); } void set_next_arg(cmd_context & ctx, expr * arg) override { m_target = arg; } void execute(cmd_context & ctx) override { proof_ref pr(ctx.m()); qe::simplify_rewriter_star qe(ctx.m()); expr_ref result(ctx.m()); qe(m_target, result, pr); if (m_params.get_bool("print", true)) { ctx.display(ctx.regular_stream(), result); ctx.regular_stream() << std::endl; } if (m_params.get_bool("print_statistics", false)) { statistics st; qe.collect_statistics(st); st.display(ctx.regular_stream()); } } }; void install_qe_cmd(cmd_context & ctx, char const * cmd_name) { ctx.insert(alloc(qe_cmd, cmd_name)); } z3-z3-4.8.7/src/qe/qe_cmd.h000066400000000000000000000005151356505360400152250ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: qe_cmd.h Abstract: SMT2 front-end 'qe' command. Author: Nikolaj Bjorner (nbjorner) 2011-01-11 Notes: --*/ #ifndef QE_CMD_H_ #define QE_CMD_H_ class cmd_context; void install_qe_cmd(cmd_context & ctx, char const * cmd_name = "elim-quantifiers"); #endif z3-z3-4.8.7/src/qe/qe_datatype_plugin.cpp000066400000000000000000000743251356505360400202200ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ // --------------------- // datatypes // Quantifier elimination routine for recursive data-types. // // // reduce implementation is modeled after Hodges: // subst implementation is using QE "for dummies". // for dummies: // ----------- // // Step 1. ensure x is recognized. // // exists x . phi[x] -> \/_i exists x. R_C(x) & phi[x] // // Step 2. non-recursive data-types // // exists x . R_C(x) & phi[x] -> exists y . phi[C(y)] // // Step 2. recursive data-types, eliminate selectors. // // exists x . R_C(x) & phi[x] -> exists y . phi[C(y)], x occurs with sel^C_i(x) // // Step 3. (recursive data-types) // // Solve equations // . C[t] = C[s] -> t = s // . C[x,t] = y -> x = s1(y) /\ t = s2(y) /\ R_C(y) // . C[x,t] != y -> can remain // // The remaining formula is in NNF where occurrences of 'x' are of the form // x = t_i or t[x] != s_j // // The last normalization step is: // // exists x . R_C(x) & phi[x = t_i, t[x] != s_j] // // -> \/_i R_C(t_i) & phi[t_i/x] \/ phi[false, true] // // Justification: // - We will assume that each of t_i, s_j are constructor terms. // - We can assume that x \notin t_i, x \notin s_j, or otherwise use simplification. // - We can assume that x occurs only in equalities or disequalities, or the recognizer, since // otherwise, we could simplify equalities, or QE does not apply. // - either x = t_i for some positive t_i, or we create // diag = C(t_1, ..., C(t_n, .. C(s_1, .. , C(s_m)))) // and x is different from each t_i, s_j. // // // reduce: // -------- // reduce set of literals containing x. The elimination procedure follows an adaptation of // the proof (with corrections) in Hodges (shorter model theory). // // . x = t - (x \notin t) x is eliminated immediately. // // . x != t1, ..., x != t_n - (x \notin t_i) are collected into distrinct_terms. // // . recognizer(x) - is stored as pos_recognizer. // // . !recognizer(x) - is stored into neg_recognizers. // // . L[constructor(..,x,..)] - // We could assume pre-processing introduces auxiliary // variable y, Is_constructor(y), accessor_j(y) = arg_j. // But we apply the following hack: re-introduce x' into vars, // but also the variable y and the reduced formula. // // . L[accessor_i(x)] - if pos_recognizer(x) matches accessor, // or if complement of neg_recognizers match accessor, then // introduce x_1, .., x_n corresponding to accessor_i(x). // // The only way x is not in the scope of a data-type method is if it is in // an equality or dis-equality of the form: // // . x (!)= acc_1(acc_2(..(acc_i(x)..) - would be false (true) if recognizer(..) // is declared for each sub-term. // // // - otherwise, each x should be in the scope of an accessor. // // Normalized literal elimination: // // . exists x . Lits[accessor_i(x)] & Is_constructor(x) // -> // exists x_1, .., x_n . Lits[x_1, .., x_n] for each accessor_i(x). // // // maintain set of equations and disequations with x. // #include "qe/qe.h" #include "ast/datatype_decl_plugin.h" #include "ast/rewriter/expr_safe_replace.h" #include "util/obj_pair_hashtable.h" #include "ast/for_each_expr.h" #include "ast/ast_pp.h" #include "ast/ast_ll_pp.h" namespace qe { class datatype_atoms { ast_manager& m; app_ref_vector m_recognizers; expr_ref_vector m_eqs; expr_ref_vector m_neqs; app_ref_vector m_eq_atoms; app_ref_vector m_neq_atoms; app_ref_vector m_unsat_atoms; expr_ref_vector m_eq_conds; ast_mark m_mark; datatype_util m_util; public: datatype_atoms(ast_manager& m) : m(m), m_recognizers(m), m_eqs(m), m_neqs(m), m_eq_atoms(m), m_neq_atoms(m), m_unsat_atoms(m), m_eq_conds(m), m_util(m) {} bool add_atom(contains_app& contains_x, bool is_pos, app* a) { app* x = contains_x.x(); SASSERT(contains_x(a)); if (m_mark.is_marked(a)) { return true; } m_mark.mark(a, true); func_decl* f = a->get_decl(); if (m_util.is_recognizer(f) && a->get_arg(0) == x) { m_recognizers.push_back(a); TRACE("qe", tout << "add recognizer:" << mk_pp(a, m) << "\n";); return true; } if (!m.is_eq(a)) { return false; } if (add_eq(contains_x, is_pos, a->get_arg(0), a->get_arg(1))) { add_atom(a, is_pos); return true; } if (add_eq(contains_x, is_pos, a->get_arg(1), a->get_arg(0))) { add_atom(a, is_pos); return true; } if (add_unsat_eq(contains_x, a, a->get_arg(0), a->get_arg(1))) { return true; } return false; } unsigned num_eqs() { return m_eqs.size(); } expr* eq(unsigned i) { return m_eqs[i].get(); } expr* eq_cond(unsigned i) { return m_eq_conds[i].get(); } app* eq_atom(unsigned i) { return m_eq_atoms[i].get(); } unsigned num_neqs() { return m_neq_atoms.size(); } app* neq_atom(unsigned i) { return m_neq_atoms[i].get(); } unsigned num_neq_terms() const { return m_neqs.size(); } expr* neq_term(unsigned i) const { return m_neqs[i]; } expr* const* neq_terms() const { return m_neqs.c_ptr(); } unsigned num_recognizers() { return m_recognizers.size(); } app* recognizer(unsigned i) { return m_recognizers[i].get(); } unsigned num_unsat() { return m_unsat_atoms.size(); } app* unsat_atom(unsigned i) { return m_unsat_atoms[i].get(); } private: // // perform occurs check on equality. // bool add_unsat_eq(contains_app& contains_x, app* atom, expr* a, expr* b) { app* x = contains_x.x(); if (x != a) { std::swap(a, b); } if (x != a) { return false; } if (!contains_x(b)) { return false; } ptr_vector todo; ast_mark mark; todo.push_back(b); while (!todo.empty()) { b = todo.back(); todo.pop_back(); if (mark.is_marked(b)) { continue; } mark.mark(b, true); if (!is_app(b)) { continue; } if (b == x) { m_unsat_atoms.push_back(atom); return true; } app* b_app = to_app(b); if (!m_util.is_constructor(b_app)) { continue; } for (unsigned i = 0; i < b_app->get_num_args(); ++i) { todo.push_back(b_app->get_arg(i)); } } return false; } void add_atom(app* a, bool is_pos) { TRACE("qe", tout << "add atom:" << mk_pp(a, m) << " " << (is_pos?"pos":"neg") << "\n";); if (is_pos) { m_eq_atoms.push_back(a); } else { m_neq_atoms.push_back(a); } } bool add_eq(contains_app& contains_x, bool is_pos, expr* a, expr* b) { if (contains_x(b)) { return false; } if (is_pos) { return solve_eq(contains_x, a, b, m.mk_true()); } else { return solve_diseq(contains_x, a, b); } return false; } bool solve_eq(contains_app& contains_x, expr* _a, expr* b, expr* cond0) { SASSERT(contains_x(_a)); SASSERT(!contains_x(b)); app* x = contains_x.x(); if (!is_app(_a)) { return false; } app* a = to_app(_a); if (x == a) { m_eqs.push_back(b); m_eq_conds.push_back(cond0); return true; } if (!m_util.is_constructor(a)) { return false; } func_decl* c = a->get_decl(); func_decl_ref r(m_util.get_constructor_is(c), m); ptr_vector const & acc = *m_util.get_constructor_accessors(c); SASSERT(acc.size() == a->get_num_args()); // // It suffices to solve just the first available equality. // The others are determined by the first. // expr_ref cond(m.mk_and(m.mk_app(r, b), cond0), m); for (unsigned i = 0; i < a->get_num_args(); ++i) { expr* l = a->get_arg(i); if (contains_x(l)) { expr_ref r(m.mk_app(acc[i], b), m); if (solve_eq(contains_x, l, r, cond)) { return true; } } } return false; } // // check that some occurrence of 'x' is in a constructor sequence. // bool solve_diseq(contains_app& contains_x, expr* a0, expr* b) { SASSERT(!contains_x(b)); SASSERT(contains_x(a0)); app* x = contains_x.x(); ptr_vector todo; ast_mark mark; todo.push_back(a0); while (!todo.empty()) { a0 = todo.back(); todo.pop_back(); if (mark.is_marked(a0)) { continue; } mark.mark(a0, true); if (!is_app(a0)) { continue; } app* a = to_app(a0); if (a == x) { m_neqs.push_back(b); return true; } if (!m_util.is_constructor(a)) { continue; } for (unsigned i = 0; i < a->get_num_args(); ++i) { todo.push_back(a->get_arg(i)); } } return false; } }; // // eliminate foreign variable under datatype functions (constructors). // (= C(x,y) t) -> (R_C(t) && x = s1(t) && x = s2(t)) // class lift_foreign_vars : public map_proc { ast_manager& m; bool m_change; datatype_util& m_util; i_solver_context& m_ctx; public: lift_foreign_vars(ast_manager& m, datatype_util& util, i_solver_context& ctx): map_proc(m), m(m), m_change(false), m_util(util), m_ctx(ctx) {} bool lift(expr_ref& fml) { m_change = false; for_each_expr(*this, fml.get()); if (m_change) { fml = get_expr(fml.get()); TRACE("qe", tout << "lift:\n" << mk_pp(fml.get(), m) << "\n";); } return m_change; } void operator()(var* v) { visit(v); } void operator()(quantifier* q) { visit(q); } void operator()(app* a) { expr* l, *r; if (m.is_eq(a, l, r)) { if (reduce_eq(a, l, r)) { m_change = true; return; } if (reduce_eq(a, r, l)) { m_change = true; return; } } reconstruct(a); } private: bool reduce_eq(app* a, expr* _l, expr* r) { if (!is_app(_l)) { return false; } app* l = to_app(_l); if (!m_util.is_constructor(l)) { return false; } if (!contains_foreign(l)) { return false; } func_decl* c = l->get_decl(); ptr_vector const& acc = *m_util.get_constructor_accessors(c); func_decl* rec = m_util.get_constructor_is(c); expr_ref_vector conj(m); conj.push_back(m.mk_app(rec, r)); for (unsigned i = 0; i < acc.size(); ++i) { expr* r_i = m.mk_app(acc[i], r); expr* l_i = l->get_arg(i); conj.push_back(m.mk_eq(l_i, r_i)); } expr* e = m.mk_and(conj.size(), conj.c_ptr()); m_map.insert(a, e, nullptr); TRACE("qe", tout << "replace: " << mk_pp(a, m) << " ==> \n" << mk_pp(e, m) << "\n";); return true; } bool contains_foreign(app* a) { unsigned num_vars = m_ctx.get_num_vars(); for (unsigned i = 0; i < num_vars; ++i) { contains_app& v = m_ctx.contains(i); sort* s = v.x()->get_decl()->get_range(); // // data-type contains arithmetical term or bit-vector. // if (!m_util.is_datatype(s) && !m.is_bool(s) && v(a)) { return true; } } return false; } }; class datatype_plugin : public qe_solver_plugin { typedef std::pair > subst_clos; typedef obj_pair_map eqs_cache; typedef obj_pair_map subst_map; datatype_util m_datatype_util; expr_safe_replace m_replace; eqs_cache m_eqs_cache; subst_map m_subst_cache; ast_ref_vector m_trail; public: datatype_plugin(i_solver_context& ctx, ast_manager& m) : qe_solver_plugin(m, m.mk_family_id("datatype"), ctx), m_datatype_util(m), m_replace(m), m_trail(m) { } ~datatype_plugin() override { { eqs_cache::iterator it = m_eqs_cache.begin(), end = m_eqs_cache.end(); for (; it != end; ++it) { dealloc(it->get_value()); } } { subst_map::iterator it = m_subst_cache.begin(), end = m_subst_cache.end(); for (; it != end; ++it) { dealloc(it->get_value()); } } } bool get_num_branches( contains_app& x, expr* fml, rational& num_branches) override { sort* s = x.x()->get_decl()->get_range(); SASSERT(m_datatype_util.is_datatype(s)); if (m_datatype_util.is_recursive(s)) { return get_num_branches_rec(x, fml, num_branches); } else { return get_num_branches_nonrec(x, fml, num_branches); } } void assign(contains_app& x, expr* fml, rational const& vl) override { sort* s = x.x()->get_decl()->get_range(); SASSERT(m_datatype_util.is_datatype(s)); TRACE("qe", tout << mk_pp(x.x(), m) << " " << vl << "\n";); if (m_datatype_util.is_recursive(s)) { assign_rec(x, fml, vl); } else { assign_nonrec(x, fml, vl); } } void subst(contains_app& x, rational const& vl, expr_ref& fml, expr_ref* def) override { sort* s = x.x()->get_decl()->get_range(); SASSERT(m_datatype_util.is_datatype(s)); TRACE("qe", tout << mk_pp(x.x(), m) << " " << vl << "\n";); if (m_datatype_util.is_recursive(s)) { subst_rec(x, vl, fml, def); } else { subst_nonrec(x, vl, fml, def); } } unsigned get_weight( contains_app& x, expr* fml) override { return UINT_MAX; } bool solve( conj_enum& conj, expr* fml) override { return false; } bool simplify( expr_ref& fml) override { lift_foreign_vars lift(m, m_datatype_util, m_ctx); return lift.lift(fml); } bool mk_atom(expr* e, bool p, expr_ref& result) override { return false; } virtual rational get_cost(contains_app&, expr* fml) { return rational(0); } private: void add_def(expr* term, expr_ref* def) { if (def) { *def = term; } } // // replace x by C(y1,..,yn) where y1,..,yn are fresh variables. // void subst_constructor(contains_app& x, func_decl* c, expr_ref& fml, expr_ref* def) { subst_clos* sub = nullptr; if (m_subst_cache.find(x.x(), c, sub)) { m_replace.apply_substitution(x.x(), sub->first, fml); add_def(sub->first, def); for (unsigned i = 0; i < sub->second.size(); ++i) { m_ctx.add_var(sub->second[i]); } return; } sub = alloc(subst_clos); unsigned arity = c->get_arity(); expr_ref_vector vars(m); for (unsigned i = 0; i < arity; ++i) { sort* sort_x = c->get_domain()[i]; app_ref fresh_x(m.mk_fresh_const("x", sort_x), m); m_ctx.add_var(fresh_x.get()); vars.push_back(fresh_x.get()); sub->second.push_back(fresh_x.get()); } app_ref t(m.mk_app(c, vars.size(), vars.c_ptr()), m); m_trail.push_back(x.x()); m_trail.push_back(c); m_trail.push_back(t); add_def(t, def); m_replace.apply_substitution(x.x(), t, fml); sub->first = t; m_subst_cache.insert(x.x(), c, sub); } void get_recognizers(expr* fml, ptr_vector& recognizers) { conj_enum conjs(m, fml); conj_enum::iterator it = conjs.begin(), end = conjs.end(); for (; it != end; ++it) { expr* e = *it; if (is_app(e)) { app* a = to_app(e); func_decl* f = a->get_decl(); if (m_datatype_util.is_recognizer(f)) { recognizers.push_back(a); } } } } bool has_recognizer(app* x, expr* fml, func_decl*& r, func_decl*& c) { ptr_vector recognizers; get_recognizers(fml, recognizers); for (unsigned i = 0; i < recognizers.size(); ++i) { app* a = recognizers[i]; if (a->get_arg(0) == x) { r = a->get_decl(); c = m_datatype_util.get_recognizer_constructor(a->get_decl()); return true; } } return false; } bool get_num_branches_rec( contains_app& x, expr* fml, rational& num_branches) { sort* s = x.x()->get_decl()->get_range(); SASSERT(m_datatype_util.is_datatype(s)); SASSERT(m_datatype_util.is_recursive(s)); unsigned sz = m_datatype_util.get_datatype_num_constructors(s); num_branches = rational(sz); func_decl* c = nullptr, *r = nullptr; // // If 'x' does not yet have a recognizer, then branch according to recognizers. // if (!has_recognizer(x.x(), fml, r, c)) { return true; } // // eliminate 'x' by applying constructor to fresh variables. // if (has_selector(x, fml, c)) { num_branches = rational(1); return true; } // // 'x' has a recognizer. Count number of equalities and disequalities. // if (update_eqs(x, fml)) { datatype_atoms& eqs = get_eqs(x.x(), fml); num_branches = rational(eqs.num_eqs() + 1); return true; } TRACE("qe", tout << "could not get number of branches " << mk_pp(x.x(), m) << "\n";); return false; } void assign_rec(contains_app& contains_x, expr* fml, rational const& vl) { app* x = contains_x.x(); sort* s = x->get_decl()->get_range(); func_decl* c = nullptr, *r = nullptr; // // If 'x' does not yet have a recognizer, then branch according to recognizers. // if (!has_recognizer(x, fml, r, c)) { c = m_datatype_util.get_datatype_constructors(s)->get(vl.get_unsigned()); r = m_datatype_util.get_constructor_is(c); app* is_c = m.mk_app(r, x); // assert v => r(x) m_ctx.add_constraint(true, is_c); return; } // // eliminate 'x' by applying constructor to fresh variables. // if (has_selector(contains_x, fml, c)) { return; } // // 'x' has a recognizer. The branch ID id provided by the index of the equality. // datatype_atoms& eqs = get_eqs(x, fml); SASSERT(vl.is_unsigned()); unsigned idx = vl.get_unsigned(); SASSERT(idx <= eqs.num_eqs()); if (idx < eqs.num_eqs()) { expr* t = eqs.eq(idx); m_ctx.add_constraint(true, m.mk_eq(x, t)); } else { for (unsigned i = 0; i < eqs.num_eqs(); ++i) { expr* t = eqs.eq(i); m_ctx.add_constraint(true, m.mk_not(m.mk_eq(x, t))); } } } void subst_rec(contains_app& contains_x, rational const& vl, expr_ref& fml, expr_ref* def) { app* x = contains_x.x(); sort* s = x->get_decl()->get_range(); SASSERT(m_datatype_util.is_datatype(s)); func_decl* c = nullptr, *r = nullptr; TRACE("qe", tout << mk_pp(x, m) << " " << vl << " " << mk_pp(fml, m) << " " << (def != 0) << "\n";); // // Add recognizer to formula. // Introduce auxiliary variable to eliminate. // if (!has_recognizer(x, fml, r, c)) { c = m_datatype_util.get_datatype_constructors(s)->get(vl.get_unsigned()); r = m_datatype_util.get_constructor_is(c); app* is_c = m.mk_app(r, x); fml = m.mk_and(is_c, fml); app_ref fresh_x(m.mk_fresh_const("x", s), m); m_ctx.add_var(fresh_x); m_replace.apply_substitution(x, fresh_x, fml); add_def(fresh_x, def); TRACE("qe", tout << "Add recognizer " << mk_pp(is_c, m) << "\n";); return; } if (has_selector(contains_x, fml, c)) { TRACE("qe", tout << "Eliminate selector " << mk_ll_pp(c, m) << "\n";); subst_constructor(contains_x, c, fml, def); return; } // // 'x' has a recognizer. The branch ID id provided by the index of the equality. // datatype_atoms& eqs = get_eqs(x, fml); SASSERT(vl.is_unsigned()); unsigned idx = vl.get_unsigned(); SASSERT(idx <= eqs.num_eqs()); for (unsigned i = 0; i < eqs.num_recognizers(); ++i) { app* rec = eqs.recognizer(i); if (rec->get_decl() == r) { m_replace.apply_substitution(rec, m.mk_true(), fml); } else { m_replace.apply_substitution(rec, m.mk_false(), fml); } } for (unsigned i = 0; i < eqs.num_unsat(); ++i) { m_replace.apply_substitution(eqs.unsat_atom(i), m.mk_false(), fml); } if (idx < eqs.num_eqs()) { expr* t = eqs.eq(idx); expr* c = eqs.eq_cond(idx); add_def(t, def); m_replace.apply_substitution(x, t, fml); if (!m.is_true(c)) { fml = m.mk_and(c, fml); } } else { for (unsigned i = 0; i < eqs.num_eqs(); ++i) { m_replace.apply_substitution(eqs.eq_atom(i), m.mk_false(), fml); } for (unsigned i = 0; i < eqs.num_neqs(); ++i) { m_replace.apply_substitution(eqs.neq_atom(i), m.mk_false(), fml); } if (def) { sort* s = m.get_sort(x); ptr_vector sorts; sorts.resize(eqs.num_neq_terms(), s); func_decl* diag = m.mk_func_decl(symbol("diag"), sorts.size(), sorts.c_ptr(), s); expr_ref t(m); t = m.mk_app(diag, eqs.num_neq_terms(), eqs.neq_terms()); add_def(t, def); } } TRACE("qe", tout << "reduced " << mk_pp(fml.get(), m) << "\n";); } bool get_num_branches_nonrec( contains_app& x, expr* fml, rational& num_branches) { sort* s = x.x()->get_decl()->get_range(); unsigned sz = m_datatype_util.get_datatype_num_constructors(s); num_branches = rational(sz); func_decl* c = nullptr, *r = nullptr; if (sz != 1 && has_recognizer(x.x(), fml, r, c)) { TRACE("qe", tout << mk_pp(x.x(), m) << " has a recognizer\n";); num_branches = rational(1); } TRACE("qe", tout << mk_pp(x.x(), m) << " branches: " << sz << "\n";); return true; } void assign_nonrec(contains_app& contains_x, expr* fml, rational const& vl) { app* x = contains_x.x(); sort* s = x->get_decl()->get_range(); SASSERT(m_datatype_util.is_datatype(s)); unsigned sz = m_datatype_util.get_datatype_num_constructors(s); SASSERT(vl.is_unsigned()); SASSERT(vl.get_unsigned() < sz); if (sz == 1) { return; } func_decl* c = nullptr, *r = nullptr; if (has_recognizer(x, fml, r, c)) { TRACE("qe", tout << mk_pp(x, m) << " has a recognizer\n";); return; } c = m_datatype_util.get_datatype_constructors(s)->get(vl.get_unsigned()); r = m_datatype_util.get_constructor_is(c); app* is_c = m.mk_app(r, x); // assert v => r(x) m_ctx.add_constraint(true, is_c); } virtual void subst_nonrec(contains_app& x, rational const& vl, expr_ref& fml, expr_ref* def) { sort* s = x.x()->get_decl()->get_range(); SASSERT(m_datatype_util.is_datatype(s)); SASSERT(!m_datatype_util.is_recursive(s)); func_decl* c = nullptr, *r = nullptr; if (has_recognizer(x.x(), fml, r, c)) { TRACE("qe", tout << mk_pp(x.x(), m) << " has a recognizer\n";); } else { SASSERT(vl.is_unsigned()); SASSERT(vl.get_unsigned() < m_datatype_util.get_datatype_num_constructors(s)); c = m_datatype_util.get_datatype_constructors(s)->get(vl.get_unsigned()); } subst_constructor(x, c, fml, def); } class has_select : public i_expr_pred { app* m_x; func_decl* m_c; datatype_util& m_util; public: has_select(app* x, func_decl* c, datatype_util& u): m_x(x), m_c(c), m_util(u) {} bool operator()(expr* e) override { if (!is_app(e)) return false; app* a = to_app(e); if (!m_util.is_accessor(a)) return false; if (a->get_arg(0) != m_x) return false; func_decl* f = a->get_decl(); return m_c == m_util.get_accessor_constructor(f); } }; bool has_selector(contains_app& x, expr* fml, func_decl* c) { has_select hs(x.x(), c, m_datatype_util); check_pred ch(hs, m); return ch(fml); } datatype_atoms& get_eqs(app* x, expr* fml) { datatype_atoms* eqs = nullptr; VERIFY (m_eqs_cache.find(x, fml, eqs)); return *eqs; } bool update_eqs(contains_app& contains_x, expr* fml) { datatype_atoms* eqs = nullptr; if (m_eqs_cache.find(contains_x.x(), fml, eqs)) { return true; } eqs = alloc(datatype_atoms, m); if (!update_eqs(*eqs, contains_x, fml, m_ctx.pos_atoms(), true)) { dealloc(eqs); return false; } if (!update_eqs(*eqs, contains_x, fml, m_ctx.neg_atoms(), false)) { dealloc(eqs); return false; } m_trail.push_back(contains_x.x()); m_trail.push_back(fml); m_eqs_cache.insert(contains_x.x(), fml, eqs); return true; } bool update_eqs(datatype_atoms& eqs, contains_app& contains_x, expr* fml, atom_set const& tbl, bool is_pos) { atom_set::iterator it = tbl.begin(), end = tbl.end(); for (; it != end; ++it) { app* e = *it; if (!contains_x(e)) { continue; } if (!eqs.add_atom(contains_x, is_pos, e)) { return false; } } return true; } }; qe_solver_plugin* mk_datatype_plugin(i_solver_context& ctx) { return alloc(datatype_plugin, ctx, ctx.get_manager()); } } z3-z3-4.8.7/src/qe/qe_datatypes.cpp000066400000000000000000000257561356505360400170310ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: qe_datatypes.cpp Abstract: Simple projection function for algebraic datatypes. Author: Nikolaj Bjorner (nbjorner) 2015-06-13 Revision History: --*/ #include "qe/qe_arith.h" #include "ast/ast_pp.h" #include "ast/rewriter/th_rewriter.h" #include "ast/expr_functors.h" #include "model/model_v2_pp.h" #include "ast/rewriter/expr_safe_replace.h" #include "util/obj_pair_hashtable.h" #include "qe/qe_datatypes.h" namespace qe { struct datatype_project_plugin::imp { ast_manager& m; datatype_util dt; app_ref m_val; scoped_ptr m_var; imp(ast_manager& m): m(m), dt(m), m_val(m) {} bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) { return lift_foreign(vars, lits); } bool operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) { expr_ref val = model(var); SASSERT(is_app(val)); TRACE("qe", tout << mk_pp(var, m) << " := " << val << "\n";); m_val = to_app(val); if (!dt.is_constructor(m_val)) { // assert: var does not occur in lits. return true; } m_var = alloc(contains_app, m, var); try { if (dt.is_recursive(m.get_sort(var))) { project_rec(model, vars, lits); } else { project_nonrec(model, vars, lits); } } catch (const cant_project &) { TRACE("qe", tout << "can't project:" << mk_pp(var, m) << "\n";); return false; } return true; } void project_nonrec(model& model, app_ref_vector& vars, expr_ref_vector& lits) { expr_ref tmp(m), val(m); expr_ref_vector args(m); app_ref arg(m); SASSERT(dt.is_constructor(m_val)); func_decl* f = m_val->get_decl(); ptr_vector const& acc = *dt.get_constructor_accessors(f); for (unsigned i = 0; i < acc.size(); ++i) { arg = m.mk_fresh_const(acc[i]->get_name().str().c_str(), acc[i]->get_range()); vars.push_back(arg); model.register_decl(arg->get_decl(), m_val->get_arg(i)); args.push_back(arg); } val = m.mk_app(f, args.size(), args.c_ptr()); TRACE("qe", tout << mk_pp(m_var->x(), m) << " |-> " << val << "\n";); reduce(val, lits); } void project_rec(model& model, app_ref_vector& vars, expr_ref_vector& lits) { expr_ref rhs(m); expr_ref_vector eqs(m); for (unsigned i = 0; i < lits.size(); ++i) { if (solve(model, vars, lits[i].get(), rhs, eqs)) { project_plugin::erase(lits, i); reduce(rhs, lits); lits.append(eqs); return; } } // otherwise, unfold the constructor associated with m_var // once according to the model. In this way, selector-constructor // redexes are reduced and disequalities are eventually solved // by virtue of the constructors being different. project_nonrec(model, vars, lits); } void reduce(expr* val, expr_ref_vector& lits) { expr_safe_replace sub(m); th_rewriter rw(m); expr_ref tmp(m); sub.insert(m_var->x(), val); TRACE("qe", tout << mk_pp(m_var->x(), m) << " = " << mk_pp(val, m) << "\n"; tout << lits << "\n";); for (unsigned i = 0; i < lits.size(); ++i) { sub(lits[i].get(), tmp); rw(tmp); lits[i] = tmp; } } bool contains_x(expr* e) { return (*m_var)(e); } bool solve(model& model, app_ref_vector& vars, expr* fml, expr_ref& t, expr_ref_vector& eqs) { expr* t1, *t2; if (m.is_eq(fml, t1, t2)) { if (contains_x(t1) && !contains_x(t2) && is_app(t1)) { return solve(model, vars, to_app(t1), t2, t, eqs); } if (contains_x(t2) && !contains_x(t1) && is_app(t2)) { return solve(model, vars, to_app(t2), t1, t, eqs); } } if (m.is_not(fml, t1) && m.is_distinct(t1)) { expr_ref eq = project_plugin::pick_equality(m, model, t1); return solve(model, vars, eq, t, eqs); } return false; } bool solve(model& model, app_ref_vector& vars, app* a, expr* b, expr_ref& t, expr_ref_vector& eqs) { SASSERT(contains_x(a)); SASSERT(!contains_x(b)); if (m_var->x() == a) { t = b; return true; } if (!dt.is_constructor(a)) { return false; } func_decl* c = a->get_decl(); func_decl_ref rec(dt.get_constructor_is(c), m); ptr_vector const & acc = *dt.get_constructor_accessors(c); SASSERT(acc.size() == a->get_num_args()); // // It suffices to solve just the first available equality. // The others are determined by the first. // for (unsigned i = 0; i < a->get_num_args(); ++i) { expr* l = a->get_arg(i); if (is_app(l) && contains_x(l)) { expr_ref r(m); r = access(c, i, acc, b); if (solve(model, vars, to_app(l), r, t, eqs)) { for (unsigned j = 0; j < c->get_arity(); ++j) { if (i != j) { eqs.push_back(m.mk_eq(access(c, j, acc, b), a->get_arg(j))); } } if (!is_app_of(b, c)) { eqs.push_back(m.mk_app(rec, b)); } return true; } } } return false; } expr* access(func_decl* c, unsigned i, ptr_vector const& acc, expr* e) { if (is_app_of(e,c)) { return to_app(e)->get_arg(i); } else { return m.mk_app(acc[i], e); } } bool lift_foreign(app_ref_vector const& vars, expr_ref_vector& lits) { bool reduced = false; expr_mark visited; expr_mark has_var; bool inserted = false; for (unsigned i = 0; i < vars.size(); ++i) { if (m.is_bool(vars[i])) continue; if (dt.is_datatype(m.get_sort(vars[i]))) continue; inserted = true; has_var.mark(vars[i]); visited.mark(vars[i]); } if (inserted) { for (unsigned i = 0; i < lits.size(); ++i) { expr* e = lits[i].get(), *l, *r; if (m.is_eq(e, l, r) && reduce_eq(visited, has_var, l, r, lits)) { project_plugin::erase(lits, i); reduced = true; } } CTRACE("qe", reduced, tout << vars << "\n" << lits << "\n";); } return reduced; } bool reduce_eq(expr_mark& has_var, expr_mark& visited, expr* l, expr* r, expr_ref_vector& lits) { if (!is_app(l) || !is_app(r)) { return false; } bool reduce = false; if (dt.is_constructor(to_app(r)) && contains_foreign(has_var, visited, r)) { std::swap(l, r); reduce = true; } reduce |= dt.is_constructor(to_app(l)) && contains_foreign(has_var, visited, l); if (!reduce) { return false; } func_decl* c = to_app(l)->get_decl(); ptr_vector const& acc = *dt.get_constructor_accessors(c); if (!is_app_of(r, c)) { lits.push_back(m.mk_app(dt.get_constructor_is(c), r)); } for (unsigned i = 0; i < acc.size(); ++i) { lits.push_back(m.mk_eq(to_app(l)->get_arg(i), access(c, i, acc, r))); } return true; } ptr_vector todo; bool contains_foreign(expr_mark& has_var, expr_mark& visited, expr* e) { todo.push_back(e); while (!todo.empty()) { expr* _f = todo.back(); if (visited.is_marked(_f)) { todo.pop_back(); continue; } if (!is_app(_f)) { visited.mark(_f); todo.pop_back(); continue; } app* f = to_app(_f); bool has_new = false, has_v = false; for (unsigned i = 0; i < f->get_num_args(); ++i) { expr* arg = f->get_arg(i); if (!visited.is_marked(arg)) { todo.push_back(arg); has_new = true; } else { has_v |= has_var.is_marked(arg); } } if (has_new) { continue; } todo.pop_back(); if (has_v) { has_var.mark(f); } TRACE("qe", tout << "contains: " << mk_pp(f, m) << " " << has_var.is_marked(f) << "\n";); visited.mark(f); } TRACE("qe", tout << "contains: " << mk_pp(e, m) << " " << has_var.is_marked(e) << "\n";); return has_var.is_marked(e); } }; datatype_project_plugin::datatype_project_plugin(ast_manager& m) { m_imp = alloc(imp, m); } datatype_project_plugin::~datatype_project_plugin() { dealloc(m_imp); } bool datatype_project_plugin::operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) { return (*m_imp)(model, var, vars, lits); } bool datatype_project_plugin::solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) { return m_imp->solve(model, vars, lits); } vector datatype_project_plugin::project(model& model, app_ref_vector& vars, expr_ref_vector& lits) { return vector(); } void datatype_project_plugin::saturate(model& model, func_decl_ref_vector const& shared, expr_ref_vector& lits) { NOT_IMPLEMENTED_YET(); } family_id datatype_project_plugin::get_family_id() { return m_imp->dt.get_family_id(); } } z3-z3-4.8.7/src/qe/qe_datatypes.h000066400000000000000000000017701356505360400164640ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: qe_datatypes.h Abstract: Model based projection for algebraic datatypes Author: Nikolaj Bjorner (nbjorner) 2015-06-13 Revision History: --*/ #ifndef __QE_DATATYPES_H_ #define __QE_DATATYPES_H_ #include "ast/datatype_decl_plugin.h" #include "qe/qe_mbp.h" namespace qe { class datatype_project_plugin : public project_plugin { struct imp; imp* m_imp; public: datatype_project_plugin(ast_manager& m); ~datatype_project_plugin() override; bool operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) override; bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) override; family_id get_family_id() override; vector project(model& model, app_ref_vector& vars, expr_ref_vector& lits) override; void saturate(model& model, func_decl_ref_vector const& shared, expr_ref_vector& lits) override; }; }; #endif z3-z3-4.8.7/src/qe/qe_dl_plugin.cpp000066400000000000000000000175521356505360400170030ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "qe/qe.h" #include "ast/rewriter/expr_safe_replace.h" #include "ast/dl_decl_plugin.h" #include "util/obj_pair_hashtable.h" #include "ast/ast_pp.h" namespace qe { // --------------------- // dl_plugin class eq_atoms { expr_ref_vector m_eqs; expr_ref_vector m_neqs; app_ref_vector m_eq_atoms; app_ref_vector m_neq_atoms; public: eq_atoms(ast_manager& m): m_eqs(m), m_neqs(m), m_eq_atoms(m), m_neq_atoms(m) {} unsigned num_eqs() const { return m_eqs.size(); } expr* eq(unsigned i) const { return m_eqs[i]; } app* eq_atom(unsigned i) const { return m_eq_atoms[i]; } unsigned num_neqs() const { return m_neqs.size(); } app* neq_atom(unsigned i) const { return m_neq_atoms[i]; } expr* neq(unsigned i) const { return m_neqs[i]; } void add_eq(app* atom, expr * e) { m_eq_atoms.push_back(atom); m_eqs.push_back(e); } void add_neq(app* atom, expr* e) { m_neq_atoms.push_back(atom); m_neqs.push_back(e); } }; class dl_plugin : public qe_solver_plugin { typedef obj_pair_map eqs_cache; expr_safe_replace m_replace; datalog::dl_decl_util m_util; expr_ref_vector m_trail; eqs_cache m_eqs_cache; public: dl_plugin(i_solver_context& ctx, ast_manager& m) : qe_solver_plugin(m, m.mk_family_id("datalog_relation"), ctx), m_replace(m), m_util(m), m_trail(m) { } ~dl_plugin() override { eqs_cache::iterator it = m_eqs_cache.begin(), end = m_eqs_cache.end(); for (; it != end; ++it) { dealloc(it->get_value()); } } bool get_num_branches(contains_app & x,expr * fml,rational & num_branches) override { if (!update_eqs(x, fml)) { return false; } eq_atoms& eqs = get_eqs(x.x(), fml); uint64_t domain_size; if (is_small_domain(x, eqs, domain_size)) { num_branches = rational(domain_size, rational::ui64()); } else { num_branches = rational(eqs.num_eqs() + 1); } return true; } void assign(contains_app & x,expr * fml,const rational & v) override { SASSERT(v.is_unsigned()); eq_atoms& eqs = get_eqs(x.x(), fml); unsigned uv = v.get_unsigned(); uint64_t domain_size; if (is_small_domain(x, eqs, domain_size)) { SASSERT(v < rational(domain_size, rational::ui64())); assign_small_domain(x, eqs, uv); } else { assign_large_domain(x, eqs, uv); } } void subst(contains_app & x,const rational & v,expr_ref & fml, expr_ref* def) override { SASSERT(v.is_unsigned()); eq_atoms& eqs = get_eqs(x.x(), fml); unsigned uv = v.get_unsigned(); uint64_t domain_size; if (is_small_domain(x, eqs, domain_size)) { SASSERT(uv < domain_size); subst_small_domain(x, eqs, uv, fml); } else { subst_large_domain(x, eqs, uv, fml); } if (def) { *def = nullptr; // TBD } } bool solve(conj_enum& conjs, expr* fml) override { return false; } private: bool is_small_domain(contains_app& x, eq_atoms& eqs, uint64_t& domain_size) { VERIFY(m_util.try_get_size(m.get_sort(x.x()), domain_size)); return domain_size < eqs.num_eqs() + eqs.num_neqs(); } void assign_small_domain(contains_app & x,eq_atoms& eqs, unsigned value) { expr_ref vl(m_util.mk_numeral(value, m.get_sort(x.x())), m); expr_ref eq(m.mk_eq(x.x(), vl), m); m_ctx.add_constraint(true, eq); } void assign_large_domain(contains_app & x,eq_atoms& eqs, unsigned v) { if (v < eqs.num_eqs()) { m_ctx.add_constraint(true, eqs.eq_atom(v)); } else { SASSERT(v == eqs.num_eqs()); for (unsigned i = 0; i < eqs.num_eqs(); ++i) { expr_ref neq(m.mk_not(eqs.eq_atom(i)), m); m_ctx.add_constraint(true, neq); } for (unsigned i = 0; i < eqs.num_neqs(); ++i) { expr_ref neq(m.mk_not(eqs.neq_atom(i)), m); m_ctx.add_constraint(true, neq); } } } void subst_small_domain(contains_app & x,eq_atoms& eqs, unsigned v,expr_ref & fml) { expr_ref vl(m_util.mk_numeral(v, m.get_sort(x.x())), m); m_replace.apply_substitution(x.x(), vl, fml); } // assumes that all disequalities can be satisfied. void subst_large_domain(contains_app & x,eq_atoms& eqs, unsigned w,expr_ref & fml) { SASSERT(w <= eqs.num_eqs()); if (w < eqs.num_eqs()) { expr* e = eqs.eq(w); m_replace.apply_substitution(x.x(), e, fml); } else { for (unsigned i = 0; i < eqs.num_eqs(); ++i) { m_replace.apply_substitution(eqs.eq_atom(i), m.mk_false(), fml); } for (unsigned i = 0; i < eqs.num_neqs(); ++i) { m_replace.apply_substitution(eqs.neq_atom(i), m.mk_true(), fml); } } } eq_atoms& get_eqs(app* x, expr* fml) { eq_atoms* eqs = nullptr; VERIFY(m_eqs_cache.find(x, fml, eqs)); return *eqs; } bool update_eqs(contains_app& contains_x, expr* fml) { eq_atoms* eqs = nullptr; if (m_eqs_cache.find(contains_x.x(), fml, eqs)) { return true; } eqs = alloc(eq_atoms, m); if (!update_eqs(*eqs, contains_x, fml, m_ctx.pos_atoms(), true)) { dealloc(eqs); return false; } if (!update_eqs(*eqs, contains_x, fml, m_ctx.neg_atoms(), false)) { dealloc(eqs); return false; } m_trail.push_back(contains_x.x()); m_trail.push_back(fml); m_eqs_cache.insert(contains_x.x(), fml, eqs); return true; } bool update_eqs(eq_atoms& eqs, contains_app& contains_x, expr* fml, atom_set const& tbl, bool is_pos) { atom_set::iterator it = tbl.begin(), end = tbl.end(); expr* x = contains_x.x(); for (; it != end; ++it) { app* e = *it; if (!contains_x(e)) { continue; } if (m_util.is_lt(e)) { NOT_IMPLEMENTED_YET(); } expr* e1, *e2; if (!m.is_eq(e, e1, e2)) { TRACE("quant_elim", tout << "Cannot handle: " << mk_pp(e, m) << "\n";); return false; } if (x == e2) { std::swap(e1, e2); } if (contains_x(e2) || x != e1) { TRACE("quant_elim", tout << "Cannot handle: " << mk_pp(e, m) << "\n";); return false; } if (is_pos) { eqs.add_eq(e, e2); } else { eqs.add_neq(e, e2); } } return true; } }; qe_solver_plugin* mk_dl_plugin(i_solver_context& ctx) { return alloc(dl_plugin, ctx, ctx.get_manager()); } } z3-z3-4.8.7/src/qe/qe_lite.cpp000066400000000000000000002563451356505360400157700ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qe_lite.cpp Abstract: Light weight partial quantifier-elimination procedure Author: Nikolaj Bjorner (nbjorner) 2012-10-17 Revision History: --*/ #include "qe/qe_lite.h" #include "ast/expr_abstract.h" #include "ast/used_vars.h" #include "ast/rewriter/rewriter_def.h" #include "ast/ast_pp.h" #include "ast/ast_ll_pp.h" #include "ast/ast_smt2_pp.h" #include "tactic/tactical.h" #include "ast/rewriter/bool_rewriter.h" #include "ast/rewriter/var_subst.h" #include "util/uint_set.h" #include "ast/ast_util.h" #include "ast/rewriter/th_rewriter.h" #include "ast/for_each_expr.h" #include "ast/rewriter/expr_safe_replace.h" #include "ast/datatype_decl_plugin.h" #include "qe/qe_vartest.h" #include "qe/qe_solve_plugin.h" namespace eq { bool occurs_var(unsigned idx, expr* e) { if (is_ground(e)) return false; ptr_buffer todo; todo.push_back(e); ast_mark mark; while (!todo.empty()) { expr* e = todo.back(); todo.pop_back(); if (mark.is_marked(e)) continue; mark.mark(e, true); if (is_ground(e)) continue; if (is_var(e)) { if (to_var(e)->get_idx() == idx) return true; } else if (is_app(e)) { todo.append(to_app(e)->get_num_args(), to_app(e)->get_args()); } else if (is_quantifier(e)) { quantifier* q = to_quantifier(e); if (occurs_var(idx + q->get_num_decls(), q->get_expr())) return true; } } return false; } class der { ast_manager & m; arith_util a; datatype_util dt; bv_util bv; is_variable_proc* m_is_variable; beta_reducer m_subst; expr_ref_vector m_subst_map; expr_ref_vector m_new_exprs; plugin_manager m_solvers; ptr_vector m_map; int_vector m_pos2var; int_vector m_var2pos; ptr_vector m_inx2var; unsigned_vector m_order; expr_ref_buffer m_new_args; th_rewriter m_rewriter; params_ref m_params; bool is_sub_extract(unsigned idx, expr* t) { bool has_ground = false; if (bv.is_concat(t)) { unsigned lo, hi; ptr_buffer args; args.append(to_app(t)->get_num_args(), to_app(t)->get_args()); for (unsigned i = 0; i < args.size(); ++i) { expr* arg = args[i]; if (is_ground(arg)) { has_ground = true; continue; } if (bv.is_extract(arg, lo, hi, arg)) { if (is_var(arg) && to_var(arg)->get_idx() == idx) continue; } if (bv.is_concat(arg)) { args.append(to_app(arg)->get_num_args(), to_app(arg)->get_args()); continue; } return false; } return has_ground; } return false; } bool strict_occurs_var(unsigned idx, expr* t) { return occurs_var(idx, t) && !is_sub_extract(idx, t); } void der_sort_vars(ptr_vector & vars, ptr_vector & definitions, unsigned_vector & order) { order.reset(); // eliminate self loops, and definitions containing quantifiers. bool found = false; for (unsigned i = 0; i < definitions.size(); i++) { var * v = vars[i]; expr * t = definitions[i]; if (t == nullptr || has_quantifiers(t) || strict_occurs_var(v->get_idx(), t)) definitions[i] = nullptr; else found = true; // found at least one candidate } if (!found) return; typedef std::pair frame; svector todo; expr_fast_mark1 visiting; expr_fast_mark2 done; unsigned vidx, num; for (unsigned i = 0; i < definitions.size(); i++) { if (definitions[i] == nullptr) continue; if (is_sub_extract(vars[i]->get_idx(), definitions[i])) { order.push_back(i); done.mark(definitions[i]); continue; } var * v = vars[i]; SASSERT(v->get_idx() == i); SASSERT(todo.empty()); todo.push_back(frame(v, 0)); while (!todo.empty()) { start: frame & fr = todo.back(); expr * t = fr.first; if (done.is_marked(t)) { todo.pop_back(); continue; } switch (t->get_kind()) { case AST_VAR: vidx = to_var(t)->get_idx(); if (fr.second == 0) { CTRACE("der_bug", vidx >= definitions.size(), tout << "vidx: " << vidx << "\n";); // Remark: The size of definitions may be smaller than the number of variables occurring in the quantified formula. if (definitions.get(vidx, nullptr) != nullptr) { if (visiting.is_marked(t)) { // cycle detected: remove t visiting.reset_mark(t); definitions[vidx] = nullptr; } else { visiting.mark(t); fr.second = 1; todo.push_back(frame(definitions[vidx], 0)); goto start; } } } else { SASSERT(fr.second == 1); visiting.reset_mark(t); if (!done.is_marked(t)) { if (definitions.get(vidx, nullptr) != nullptr) order.push_back(vidx); done.mark(t); } } done.mark(t); todo.pop_back(); break; case AST_QUANTIFIER: UNREACHABLE(); todo.pop_back(); break; case AST_APP: num = to_app(t)->get_num_args(); while (fr.second < num) { expr * arg = to_app(t)->get_arg(fr.second); fr.second++; if (done.is_marked(arg)) continue; todo.push_back(frame(arg, 0)); goto start; } done.mark(t); todo.pop_back(); break; default: UNREACHABLE(); todo.pop_back(); break; } } } } bool is_variable(expr * e) const { return (*m_is_variable)(e); } bool is_neg_var(ast_manager & m, expr * e, var*& v) { expr* e1; if (m.is_not(e, e1) && is_variable(e1)) { v = to_var(e1); return true; } else { return false; } } /** \brief Return true if e can be viewed as a variable disequality. Store the variable id in v and the definition in t. For example: if e is (not (= (VAR 1) T)), then v assigned to 1, and t to T. if e is (iff (VAR 2) T), then v is assigned to 2, and t to (not T). (not T) is used because this formula is equivalent to (not (iff (VAR 2) (not T))), and can be viewed as a disequality. */ bool is_var_diseq(expr * e, ptr_vector& vs, expr_ref_vector& ts) { expr* e1; if (m.is_not(e, e1)) { return is_var_eq(e, vs, ts); } else if (is_var_eq(e, vs, ts) && vs.size() == 1 && m.is_bool(vs[0])) { expr_ref tmp(m); bool_rewriter(m).mk_not(ts[0].get(), tmp); ts[0] = tmp; return true; } else { return false; } } bool trivial_solve(expr* lhs, expr* rhs, expr* eq, ptr_vector& vs, expr_ref_vector& ts) { if (!is_variable(lhs)) { std::swap(lhs, rhs); } if (!is_variable(lhs)) { return false; } vs.push_back(to_var(lhs)); ts.push_back(rhs); TRACE("qe_lite", tout << mk_pp(eq, m) << "\n";); return true; } bool same_vars(ptr_vector const& vs1, ptr_vector const& vs2) const { if (vs1.size() != vs2.size()) { return false; } for (unsigned i = 0; i < vs1.size(); ++i) { if (vs1[i] != vs2[i]) { return false; } } return true; } /** \brief Return true if e can be viewed as a variable equality. */ bool is_var_eq(expr * e, ptr_vector& vs, expr_ref_vector & ts) { expr* lhs = nullptr, *rhs = nullptr; TRACE("qe_lite", tout << mk_pp(e, m) << "\n";); // (= VAR t), (iff VAR t), (iff (not VAR) t), (iff t (not VAR)) cases if (m.is_eq(e, lhs, rhs) && trivial_solve(lhs, rhs, e, vs, ts)) { return true; } family_id fid = get_sort(e)->get_family_id(); if (m.is_eq(e, lhs, rhs)) { fid = get_sort(lhs)->get_family_id(); } qe::solve_plugin* p = m_solvers.get_plugin(fid); if (p) { expr_ref res = (*p)(e); if (res != e && m.is_eq(res, lhs, rhs) && is_variable(lhs)) { vs.push_back(to_var(lhs)); ts.push_back(rhs); TRACE("qe_lite", tout << res << "\n";); return true; } } return false; } bool is_var_def(bool check_eq, expr* e, ptr_vector& vs, expr_ref_vector& ts) { if (check_eq) { return is_var_eq(e, vs, ts); } else { return is_var_diseq(e, vs, ts); } } void get_elimination_order() { m_order.reset(); TRACE("top_sort", tout << "DEFINITIONS: " << std::endl; for(unsigned i = 0; i < m_map.size(); i++) if(m_map[i]) tout << "VAR " << i << " = " << mk_pp(m_map[i], m) << std::endl; ); der_sort_vars(m_inx2var, m_map, m_order); TRACE("qe_lite", tout << "Elimination m_order:" << std::endl; tout << m_order << std::endl; ); } void create_substitution(unsigned sz) { m_subst_map.reset(); m_subst_map.resize(sz, nullptr); m_subst.reset(); m_subst.set_inv_bindings(sz, m_subst_map.c_ptr()); for (unsigned idx : m_order) { // do all the previous substitutions before inserting expr* cur = m_map[idx]; expr_ref r(m); if (is_ground(cur)) r = cur; else m_subst(cur, r); unsigned inx = sz - idx - 1; TRACE("qe_lite", tout << idx << " |-> " << r << "\n";); CTRACE("top_sort", m_subst_map.get(inx) != nullptr, tout << "inx is " << inx << "\n" << "idx is " << idx << "\n" << "sz is " << sz << "\n" << "subst_map[inx]: " << mk_pp(m_subst_map.get(inx), m) << "\n";); SASSERT(m_subst_map.get(inx) == nullptr); m_subst.update_inv_binding_at(inx, r); m_subst_map[inx] = std::move(r); } } void flatten_args(quantifier* q, unsigned& num_args, expr*const*& args) { expr * e = q->get_expr(); if ((is_forall(q) && m.is_or(e)) || (is_exists(q) && m.is_and(e))) { num_args = to_app(e)->get_num_args(); args = to_app(e)->get_args(); } } void apply_substitution(quantifier * q, expr_ref & r) { expr * e = q->get_expr(); unsigned num_args = 1; expr* const* args = &e; flatten_args(q, num_args, args); bool_rewriter rw(m); // get a new expression m_new_args.reset(); for(unsigned i = 0; i < num_args; i++) { int x = m_pos2var[i]; if (x == -1 || m_map[x] == 0) { m_new_args.push_back(args[i]); } } if (m_new_args.size() == num_args) { r = q; return; } expr_ref t(m); switch (q->get_kind()) { case forall_k: rw.mk_or(m_new_args.size(), m_new_args.c_ptr(), t); break; case exists_k: rw.mk_and(m_new_args.size(), m_new_args.c_ptr(), t); break; default: t = e; break; } expr_ref new_e = m_subst(t, m_subst_map.size(), m_subst_map.c_ptr()); TRACE("qe_lite", tout << new_e << "\n";); // don't forget to update the quantifier patterns expr_ref_buffer new_patterns(m); expr_ref_buffer new_no_patterns(m); 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); } 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); } r = m.update_quantifier(q, new_patterns.size(), new_patterns.c_ptr(), new_no_patterns.size(), new_no_patterns.c_ptr(), new_e); } void reduce_quantifier1(quantifier * q, expr_ref & r, proof_ref & pr) { expr * e = q->get_expr(); is_variable_test is_v(q->get_num_decls()); set_is_variable_proc(is_v); unsigned num_args = 1; expr* const* args = &e; if (is_lambda(q)) { r = q; pr = nullptr; return; } flatten_args(q, num_args, args); unsigned def_count = 0; unsigned largest_vinx = 0; find_definitions(num_args, args, is_exists(q), def_count, largest_vinx); if (def_count > 0) { get_elimination_order(); SASSERT(m_order.size() <= def_count); // some might be missing because of cycles if (!m_order.empty()) { create_substitution(largest_vinx + 1); apply_substitution(q, r); } else { r = q; } } else { TRACE("der_bug", tout << "Did not find any diseq\n" << mk_pp(q, m) << "\n";); r = q; } if (m.proofs_enabled()) { pr = r == q ? nullptr : m.mk_der(q, r); } } void elim_unused_vars(expr_ref& r, proof_ref &pr) { if (is_quantifier(r)) { quantifier * q = to_quantifier(r); r = ::elim_unused_vars(m, q, m_params); if (m.proofs_enabled()) { proof * p1 = m.mk_elim_unused_vars(q, r); pr = m.mk_transitivity(pr, p1); } } } void find_definitions(unsigned num_args, expr* const* args, bool is_exists, unsigned& def_count, unsigned& largest_vinx) { def_count = 0; largest_vinx = 0; m_map.reset(); m_pos2var.reset(); m_var2pos.reset(); m_inx2var.reset(); m_pos2var.reserve(num_args, -1); // Find all definitions for (unsigned i = 0; i < num_args; i++) { checkpoint(); ptr_vector vs; expr_ref_vector ts(m); expr_ref t(m); if (is_var_def(is_exists, args[i], vs, ts)) { for (unsigned j = 0; j < vs.size(); ++j) { var* v = vs[j]; t = ts.get(j); m_rewriter(t); if (t != ts.get(j)) m_new_exprs.push_back(t); unsigned idx = v->get_idx(); if (m_map.get(idx, nullptr) == nullptr) { m_map.reserve(idx + 1, 0); m_inx2var.reserve(idx + 1, 0); m_map[idx] = t; m_inx2var[idx] = v; m_pos2var[i] = idx; m_var2pos.reserve(idx + 1, -1); m_var2pos[idx] = i; def_count++; largest_vinx = std::max(idx, largest_vinx); m_new_exprs.push_back(std::move(t)); } else if (!m.is_value(m_map[idx])) { // check if the new definition is simpler expr *old_def = m_map[idx]; // -- prefer values if (m.is_value(t)) { m_pos2var[m_var2pos[idx]] = -1; m_pos2var[i] = idx; m_var2pos[idx] = i; m_map[idx] = t; m_new_exprs.push_back(std::move(t)); } // -- prefer ground else if (is_app(t) && to_app(t)->is_ground() && (!is_app(old_def) || !to_app(old_def)->is_ground())) { m_pos2var[m_var2pos[idx]] = -1; m_pos2var[i] = idx; m_var2pos[idx] = i; m_map[idx] = t; m_new_exprs.push_back(std::move(t)); } // -- prefer constants else if (is_uninterp_const(t) /* && !is_uninterp_const(old_def) */){ m_pos2var[m_var2pos[idx]] = -1; m_pos2var[i] = idx; m_var2pos[idx] = i; m_map[idx] = t; m_new_exprs.push_back(std::move(t)); } TRACE ("qe_def", tout << "Replacing definition of VAR " << idx << " from " << mk_pp(old_def, m) << " to " << mk_pp(t, m) << " inferred from: " << mk_pp(args[i], m) << "\n";); } } } } } void flatten_definitions(expr_ref_vector& conjs) { TRACE("qe_lite", expr_ref tmp(m); tmp = m.mk_and(conjs.size(), conjs.c_ptr()); tout << mk_pp(tmp, m) << "\n";); for (unsigned i = 0; i < conjs.size(); ++i) { expr* c = conjs[i].get(); expr* l, *r; if (m.is_false(c)) { conjs[0] = c; conjs.resize(1); break; } if (is_ground(c)) { continue; } if (!m.is_eq(c, l, r)) { continue; } if (!is_app(l) || !is_app(r)) { continue; } if (dt.is_constructor(to_app(l)->get_decl())) { flatten_constructor(to_app(l), to_app(r), conjs); conjs[i] = conjs.back(); conjs.pop_back(); --i; continue; } if (dt.is_constructor(to_app(r)->get_decl())) { flatten_constructor(to_app(r), to_app(l), conjs); conjs[i] = conjs.back(); conjs.pop_back(); --i; continue; } } TRACE("qe_lite", expr_ref tmp(m); tmp = m.mk_and(conjs.size(), conjs.c_ptr()); tout << "after flatten\n" << mk_pp(tmp, m) << "\n";); } void flatten_constructor(app* c, app* r, expr_ref_vector& conjs) { SASSERT(dt.is_constructor(c)); func_decl* d = c->get_decl(); if (dt.is_constructor(r->get_decl())) { app* b = to_app(r); if (d == b->get_decl()) { for (unsigned j = 0; j < c->get_num_args(); ++j) { conjs.push_back(m.mk_eq(c->get_arg(j), b->get_arg(j))); } } else { conjs.push_back(m.mk_false()); } } else { func_decl* rec = dt.get_constructor_is(d); conjs.push_back(m.mk_app(rec, r)); ptr_vector const& acc = *dt.get_constructor_accessors(d); for (unsigned i = 0; i < acc.size(); ++i) { conjs.push_back(m.mk_eq(c->get_arg(i), m.mk_app(acc[i], r))); } } } bool is_unconstrained(var* x, expr* t, unsigned i, expr_ref_vector const& conjs) { bool occ = occurs_var(x->get_idx(), t); for (unsigned j = 0; !occ && j < conjs.size(); ++j) { occ = (i != j) && occurs_var(x->get_idx(), conjs[j]); } return !occ; } bool remove_unconstrained(expr_ref_vector& conjs) { bool reduced = false, change = true; expr* r, *l, *ne; while (change) { change = false; for (unsigned i = 0; i < conjs.size(); ++i) { if (m.is_not(conjs[i].get(), ne) && m.is_eq(ne, l, r)) { TRACE("qe_lite", tout << mk_pp(conjs[i].get(), m) << " " << is_variable(l) << " " << is_variable(r) << "\n";); if (is_variable(l) && ::is_var(l) && is_unconstrained(::to_var(l), r, i, conjs)) { conjs[i] = m.mk_true(); reduced = true; change = true; } else if (is_variable(r) && ::is_var(r) && is_unconstrained(::to_var(r), l, i, conjs)) { conjs[i] = m.mk_true(); reduced = true; change = true; } } } } return reduced; } bool reduce_var_set(expr_ref_vector& conjs) { unsigned def_count = 0; unsigned largest_vinx = 0; bool reduced = false; flatten_definitions(conjs); find_definitions(conjs.size(), conjs.c_ptr(), true, def_count, largest_vinx); if (def_count > 0) { get_elimination_order(); SASSERT(m_order.size() <= def_count); // some might be missing because of cycles if (!m_order.empty()) { expr_ref r(m), new_r(m); r = m.mk_and(conjs.size(), conjs.c_ptr()); create_substitution(largest_vinx + 1); new_r = m_subst(r, m_subst_map.size(), m_subst_map.c_ptr()); m_rewriter(new_r); conjs.reset(); flatten_and(new_r, conjs); reduced = true; } } if (remove_unconstrained(conjs)) { reduced = true; } return reduced; } void checkpoint() { if (m.canceled()) throw tactic_exception(m.limit().get_cancel_msg()); } public: der(ast_manager & m, params_ref const & p): m(m), a(m), dt(m), bv(m), m_is_variable(nullptr), m_subst(m), m_subst_map(m), m_new_exprs(m), m_new_args(m), m_rewriter(m), m_params(p) { } void set_is_variable_proc(is_variable_proc& proc) { m_is_variable = &proc; m_solvers.reset(); m_solvers.register_plugin(qe::mk_arith_solve_plugin(m, proc)); m_solvers.register_plugin(qe::mk_basic_solve_plugin(m, proc)); m_solvers.register_plugin(qe::mk_bv_solve_plugin(m, proc)); } void operator()(quantifier * q, expr_ref & r, proof_ref & pr) { TRACE("qe_lite", tout << mk_pp(q, m) << "\n";); pr = nullptr; r = q; reduce_quantifier(q, r, pr); if (r != q) { elim_unused_vars(r, pr); } } void reduce_quantifier(quantifier * q, expr_ref & r, proof_ref & pr) { r = q; // Keep applying reduce_quantifier1 until r doesn't change anymore do { checkpoint(); proof_ref curr_pr(m); q = to_quantifier(r); reduce_quantifier1(q, r, curr_pr); if (m.proofs_enabled() && r != q) { pr = m.mk_transitivity(pr, curr_pr); } } while (q != r && is_quantifier(r)); m_new_exprs.reset(); } void operator()(expr_ref_vector& r) { while (reduce_var_set(r)) ; m_new_exprs.reset(); } ast_manager& get_manager() const { return m; } }; }; // namespace eq // ------------------------------------------------------------ // basic destructive equality (and disequality) resolution for arrays. namespace ar { class der { ast_manager& m; array_util a; is_variable_proc* m_is_variable; ptr_vector m_todo; expr_mark m_visited; bool is_variable(expr * e) const { return (*m_is_variable)(e); } void mark_all(expr* e) { for_each_expr(*this, m_visited, e); } void mark_all(expr_ref_vector const& fmls, unsigned j) { for (unsigned i = 0; i < fmls.size(); ++i) { if (i != j) { mark_all(fmls[i]); } } } /** Ex A. A[x] = t & Phi[A] where x \not\in A, t. A \not\in t, x => Ex A. Phi[store(A,x,t)] (Not implemented) Perhaps also: Ex A. store(A,y,z)[x] = t & Phi[A] where x \not\in A, t, y, z, A \not\in y z, t => Ex A, v . (x = y => z = t) & Phi[store(store(A,x,t),y,v)] */ bool solve_select(expr_ref_vector& conjs, unsigned i, expr* e1, expr* e2) { if (a.is_select(e1)) { app* a1 = to_app(e1); expr* A = a1->get_arg(0); if (!is_variable(A)) { return false; } m_visited.reset(); for (unsigned j = 1; j < a1->get_num_args(); ++j) { mark_all(a1->get_arg(j)); } mark_all(e2); if (m_visited.is_marked(A)) { return false; } ptr_vector args; args.push_back(A); args.append(a1->get_num_args()-1, a1->get_args()+1); args.push_back(e2); expr* B = a.mk_store(args.size(), args.c_ptr()); expr_safe_replace rep(m); rep.insert(A, B); expr_ref tmp(m); TRACE("qe_lite", tout << mk_pp(e1, m) << " = " << mk_pp(e2, m) << "\n";); for (unsigned j = 0; j < conjs.size(); ++j) { if (i == j) { conjs[j] = m.mk_true(); } else { rep(conjs[j].get(), tmp); conjs[j] = tmp; } } return true; } return false; } bool solve_select(expr_ref_vector& conjs, unsigned i, expr* e) { expr* e1, *e2; return m.is_eq(e, e1, e2) && (solve_select(conjs, i, e1, e2) || solve_select(conjs, i, e2, e1)); } /** Ex x. A[x] != B[x] & Phi where x \not\in A, B, Phi => A != B & Phi */ bool solve_neq_select(expr_ref_vector& conjs, unsigned i, expr* e) { expr* e1, *a1, *a2; if (m.is_not(e, e1) && m.is_eq(e1, a1, a2)) { if (a.is_select(a1) && a.is_select(a2) && to_app(a1)->get_num_args() == to_app(a2)->get_num_args()) { expr* e1 = to_app(a1)->get_arg(0); expr* e2 = to_app(a2)->get_arg(0); m_visited.reset(); mark_all(conjs, i); mark_all(e1); mark_all(e2); for (unsigned j = 1; j < to_app(a1)->get_num_args(); ++j) { expr* x = to_app(a1)->get_arg(j); expr* y = to_app(a2)->get_arg(j); if (!is_variable(x)) { return false; } if (x != y) { return false; } if (m_visited.is_marked(x)) { return false; } } conjs[i] = m.mk_not(m.mk_eq(e1, e2)); return true; } } return false; } void checkpoint() { if (m.canceled()) throw tactic_exception(m.limit().get_cancel_msg()); } public: der(ast_manager& m): m(m), a(m), m_is_variable(nullptr) {} void operator()(expr_ref_vector& fmls) { for (unsigned i = 0; i < fmls.size(); ++i) { checkpoint(); solve_select(fmls, i, fmls[i].get()); solve_neq_select(fmls, i, fmls[i].get()); } } void operator()(expr* e) {} void set_is_variable_proc(is_variable_proc& proc) { m_is_variable = &proc;} }; }; // namespace ar // ------------------------------------------------------------ // fm_tactic adapted to eliminate designated de-Bruijn indices. namespace fm { typedef ptr_vector clauses; typedef unsigned var; typedef int bvar; typedef int literal; typedef svector var_vector; // Encode the constraint // lits \/ ( as[0]*xs[0] + ... + as[num_vars-1]*xs[num_vars-1] <= c // if strict is true, then <= is <. struct constraint { static unsigned get_obj_size(unsigned num_lits, unsigned num_vars) { return sizeof(constraint) + num_lits*sizeof(literal) + num_vars*(sizeof(var) + sizeof(rational)); } unsigned m_id; unsigned m_num_lits:29; unsigned m_strict:1; unsigned m_dead:1; unsigned m_mark:1; unsigned m_num_vars; literal * m_lits; var * m_xs; rational * m_as; rational m_c; expr_dependency * m_dep; ~constraint() { rational * it = m_as; rational * end = it + m_num_vars; for (; it != end; ++it) it->~rational(); } unsigned hash() const { return hash_u(m_id); } }; typedef ptr_vector constraints; class constraint_set { unsigned_vector m_id2pos; constraints m_set; public: typedef constraints::const_iterator iterator; bool contains(constraint const & c) const { if (c.m_id >= m_id2pos.size()) return false; return m_id2pos[c.m_id] != UINT_MAX; } bool empty() const { return m_set.empty(); } unsigned size() const { return m_set.size(); } void insert(constraint & c) { unsigned id = c.m_id; m_id2pos.reserve(id+1, UINT_MAX); if (m_id2pos[id] != UINT_MAX) return; // already in the set unsigned pos = m_set.size(); m_id2pos[id] = pos; m_set.push_back(&c); } void erase(constraint & c) { unsigned id = c.m_id; if (id >= m_id2pos.size()) return; unsigned pos = m_id2pos[id]; if (pos == UINT_MAX) return; m_id2pos[id] = UINT_MAX; unsigned last_pos = m_set.size() - 1; if (pos != last_pos) { constraint * last_c = m_set[last_pos]; m_set[pos] = last_c; m_id2pos[last_c->m_id] = pos; } m_set.pop_back(); } constraint & erase() { SASSERT(!empty()); constraint & c = *m_set.back(); m_id2pos[c.m_id] = UINT_MAX; m_set.pop_back(); return c; } void reset() { m_id2pos.reset(); m_set.reset(); } void finalize() { m_id2pos.finalize(); m_set.finalize(); } iterator begin() const { return m_set.begin(); } iterator end() const { return m_set.end(); } }; class fm { ast_manager & m; is_variable_proc* m_is_variable; small_object_allocator m_allocator; arith_util m_util; constraints m_constraints; expr_ref_vector m_bvar2expr; signed_char_vector m_bvar2sign; obj_map m_expr2bvar; char_vector m_is_int; char_vector m_forbidden; expr_ref_vector m_var2expr; obj_map m_expr2var; unsigned_vector m_var2pos; vector m_lowers; vector m_uppers; uint_set m_forbidden_set; // variables that cannot be eliminated because occur in non OCC ineq part expr_ref_vector m_new_fmls; id_gen m_id_gen; bool m_fm_real_only; unsigned m_fm_limit; unsigned m_fm_cutoff1; unsigned m_fm_cutoff2; unsigned m_fm_extra; bool m_fm_occ; unsigned m_counter; bool m_inconsistent; expr_dependency_ref m_inconsistent_core; constraint_set m_sub_todo; // --------------------------- // // OCC clause recognizer // // --------------------------- bool is_literal(expr * t) const { expr * atom; return is_uninterp_const(t) || (m.is_not(t, atom) && is_uninterp_const(atom)); } bool is_constraint(expr * t) const { return !is_literal(t); } bool is_var(expr * t, expr * & x) const { if ((*m_is_variable)(t)) { x = t; return true; } else if (m_util.is_to_real(t) && (*m_is_variable)(to_app(t)->get_arg(0))) { x = to_app(t)->get_arg(0); return true; } return false; } bool is_var(expr * t) const { expr * x; return is_var(t, x); } bool is_linear_mon_core(expr * t, expr * & x) const { expr * c; if (m_util.is_mul(t, c, x) && m_util.is_numeral(c) && is_var(x, x)) return true; return is_var(t, x); } bool is_linear_mon(expr * t) const { expr * x; return is_linear_mon_core(t, x); } bool is_linear_pol(expr * t) const { unsigned num_mons; expr * const * mons; if (m_util.is_add(t)) { num_mons = to_app(t)->get_num_args(); mons = to_app(t)->get_args(); } else { num_mons = 1; mons = &t; } expr_fast_mark2 visited; bool all_forbidden = true; for (unsigned i = 0; i < num_mons; i++) { expr * x; if (!is_linear_mon_core(mons[i], x)) return false; if (visited.is_marked(x)) return false; // duplicates are not supported... must simplify first visited.mark(x); SASSERT(::is_var(x)); if (!m_forbidden_set.contains(::to_var(x)->get_idx()) && (!m_fm_real_only || !m_util.is_int(x))) all_forbidden = false; } return !all_forbidden; } bool is_linear_ineq(expr * t) const { bool result = false; m.is_not(t, t); expr * lhs, * rhs; if (m_util.is_le(t, lhs, rhs) || m_util.is_ge(t, lhs, rhs)) { result = m_util.is_numeral(rhs) && is_linear_pol(lhs); } TRACE("qe_lite", tout << mk_pp(t, m) << " " << (result?"true":"false") << "\n";); return result; } bool is_occ(expr * t) { if (m_fm_occ && m.is_or(t)) { unsigned num = to_app(t)->get_num_args(); bool found = false; for (unsigned i = 0; i < num; i++) { expr * l = to_app(t)->get_arg(i); if (is_literal(l)) { continue; } else if (is_linear_ineq(l)) { if (found) return false; found = true; } else { return false; } } return found; } return is_linear_ineq(t); } // --------------------------- // // Memory mng // // --------------------------- void del_constraint(constraint * c) { m.dec_ref(c->m_dep); m_sub_todo.erase(*c); m_id_gen.recycle(c->m_id); c->~constraint(); unsigned sz = constraint::get_obj_size(c->m_num_lits, c->m_num_vars); m_allocator.deallocate(sz, c); } void del_constraints(unsigned sz, constraint * const * cs) { for (unsigned i = 0; i < sz; i++) del_constraint(cs[i]); } void reset_constraints() { del_constraints(m_constraints.size(), m_constraints.c_ptr()); m_constraints.reset(); } constraint * mk_constraint(unsigned num_lits, literal * lits, unsigned num_vars, var * xs, rational * as, rational & c, bool strict, expr_dependency * dep) { unsigned sz = constraint::get_obj_size(num_lits, num_vars); char * mem = static_cast(m_allocator.allocate(sz)); char * mem_as = mem + sizeof(constraint); char * mem_lits = mem_as + sizeof(rational)*num_vars; char * mem_xs = mem_lits + sizeof(literal)*num_lits; constraint * cnstr = new (mem) constraint(); cnstr->m_id = m_id_gen.mk(); cnstr->m_num_lits = num_lits; cnstr->m_dead = false; cnstr->m_mark = false; cnstr->m_strict = strict; cnstr->m_num_vars = num_vars; cnstr->m_lits = reinterpret_cast(mem_lits); for (unsigned i = 0; i < num_lits; i++) cnstr->m_lits[i] = lits[i]; cnstr->m_xs = reinterpret_cast(mem_xs); cnstr->m_as = reinterpret_cast(mem_as); for (unsigned i = 0; i < num_vars; i++) { TRACE("qe_lite", tout << "xs[" << i << "]: " << xs[i] << "\n";); cnstr->m_xs[i] = xs[i]; new (cnstr->m_as + i) rational(as[i]); } cnstr->m_c = c; DEBUG_CODE({ for (unsigned i = 0; i < num_vars; i++) { SASSERT(cnstr->m_xs[i] == xs[i]); SASSERT(cnstr->m_as[i] == as[i]); } }); cnstr->m_dep = dep; m.inc_ref(dep); return cnstr; } // --------------------------- // // Util // // --------------------------- unsigned num_vars() const { return m_is_int.size(); } // multiply as and c, by the lcm of their denominators void mk_int(unsigned num, rational * as, rational & c) { rational l = denominator(c); for (unsigned i = 0; i < num; i++) l = lcm(l, denominator(as[i])); if (l.is_one()) return; c *= l; SASSERT(c.is_int()); for (unsigned i = 0; i < num; i++) { as[i] *= l; SASSERT(as[i].is_int()); } } void normalize_coeffs(constraint & c) { if (c.m_num_vars == 0) return; // compute gcd of all coefficients rational g = c.m_c; if (g.is_neg()) g.neg(); for (unsigned i = 0; i < c.m_num_vars; i++) { if (g.is_one()) break; if (c.m_as[i].is_pos()) g = gcd(c.m_as[i], g); else g = gcd(-c.m_as[i], g); } if (g.is_one()) return; c.m_c /= g; for (unsigned i = 0; i < c.m_num_vars; i++) c.m_as[i] /= g; } void display(std::ostream & out, constraint const & c) const { for (unsigned i = 0; i < c.m_num_lits; i++) { literal l = c.m_lits[i]; if (sign(l)) out << "~"; bvar p = lit2bvar(l); out << mk_ismt2_pp(m_bvar2expr[p], m); out << " "; } out << "("; if (c.m_num_vars == 0) out << "0"; for (unsigned i = 0; i < c.m_num_vars; i++) { if (i > 0) out << " + "; if (!c.m_as[i].is_one()) out << c.m_as[i] << "*"; out << mk_ismt2_pp(m_var2expr.get(c.m_xs[i]), m); } if (c.m_strict) out << " < "; else out << " <= "; out << c.m_c; out << ")"; } /** \brief Return true if c1 subsumes c2 c1 subsumes c2 If 1) All literals of c1 are literals of c2 2) polynomial of c1 == polynomial of c2 3) c1.m_c <= c2.m_c */ bool subsumes(constraint const & c1, constraint const & c2) { if (&c1 == &c2) return false; // quick checks first if (c1.m_num_lits > c2.m_num_lits) return false; if (c1.m_num_vars != c2.m_num_vars) return false; if (c1.m_c > c2.m_c) return false; if (!c1.m_strict && c2.m_strict && c1.m_c == c2.m_c) return false; m_counter += c1.m_num_lits + c2.m_num_lits; for (unsigned i = 0; i < c1.m_num_vars; i++) { m_var2pos[c1.m_xs[i]] = i; } bool failed = false; for (unsigned i = 0; i < c2.m_num_vars; i++) { unsigned pos1 = m_var2pos[c2.m_xs[i]]; if (pos1 == UINT_MAX || c1.m_as[pos1] != c2.m_as[i]) { failed = true; break; } } for (unsigned i = 0; i < c1.m_num_vars; i++) { m_var2pos[c1.m_xs[i]] = UINT_MAX; } if (failed) return false; for (unsigned i = 0; i < c2.m_num_lits; i++) { literal l = c2.m_lits[i]; bvar b = lit2bvar(l); SASSERT(m_bvar2sign[b] == 0); m_bvar2sign[b] = sign(l) ? -1 : 1; } for (unsigned i = 0; i < c1.m_num_lits; i++) { literal l = c1.m_lits[i]; bvar b = lit2bvar(l); char s = sign(l) ? -1 : 1; if (m_bvar2sign[b] != s) { failed = true; break; } } for (unsigned i = 0; i < c2.m_num_lits; i++) { literal l = c2.m_lits[i]; bvar b = lit2bvar(l); m_bvar2sign[b] = 0; } if (failed) return false; return true; } void backward_subsumption(constraint const & c) { if (c.m_num_vars == 0) return; var best = UINT_MAX; unsigned best_sz = UINT_MAX; bool best_lower = false; for (unsigned i = 0; i < c.m_num_vars; i++) { var xi = c.m_xs[i]; if (is_forbidden(xi)) continue; // variable is not in the index bool neg_a = c.m_as[i].is_neg(); constraints & cs = neg_a ? m_lowers[xi] : m_uppers[xi]; if (cs.size() < best_sz) { best = xi; best_sz = cs.size(); best_lower = neg_a; } } if (best_sz == 0) return; if (best == UINT_MAX) return; // none of the c variables are in the index. constraints & cs = best_lower ? m_lowers[best] : m_uppers[best]; m_counter += cs.size(); constraints::iterator it = cs.begin(); constraints::iterator it2 = it; constraints::iterator end = cs.end(); for (; it != end; ++it) { constraint * c2 = *it; if (c2->m_dead) continue; if (subsumes(c, *c2)) { TRACE("qe_lite", display(tout, c); tout << "\nsubsumed:\n"; display(tout, *c2); tout << "\n";); c2->m_dead = true; continue; } *it2 = *it; ++it2; } cs.set_end(it2); } void subsume() { while (!m_sub_todo.empty()) { constraint & c = m_sub_todo.erase(); if (c.m_dead) continue; backward_subsumption(c); } } public: // --------------------------- // // Initialization // // --------------------------- fm(ast_manager & _m): m(_m), m_is_variable(nullptr), m_allocator("fm-elim"), m_util(m), m_bvar2expr(m), m_var2expr(m), m_new_fmls(m), m_inconsistent_core(m) { updt_params(); m_counter = 0; m_inconsistent = false; } ~fm() { reset_constraints(); } void updt_params() { m_fm_real_only = false; m_fm_limit = 5000000; m_fm_cutoff1 = 8; m_fm_cutoff2 = 256; m_fm_extra = 0; m_fm_occ = true; } private: struct forbidden_proc { fm & m_owner; forbidden_proc(fm & o):m_owner(o) {} void operator()(::var * n) { if (m_owner.is_var(n) && m_owner.m.get_sort(n)->get_family_id() == m_owner.m_util.get_family_id()) { m_owner.m_forbidden_set.insert(n->get_idx()); } } void operator()(app * n) { } void operator()(quantifier * n) {} }; void init_forbidden_set(expr_ref_vector const & g) { m_forbidden_set.reset(); expr_fast_mark1 visited; forbidden_proc proc(*this); unsigned sz = g.size(); for (unsigned i = 0; i < sz; i++) { expr * f = g[i]; if (is_occ(f)) { TRACE("qe_lite", tout << "OCC: " << mk_ismt2_pp(f, m) << "\n";); continue; } TRACE("qe_lite", tout << "not OCC:\n" << mk_ismt2_pp(f, m) << "\n";); quick_for_each_expr(proc, visited, f); } } void init(expr_ref_vector const & g) { m_sub_todo.reset(); m_id_gen.reset(); reset_constraints(); m_bvar2expr.reset(); m_bvar2sign.reset(); m_bvar2expr.push_back(nullptr); // bvar 0 is not used m_bvar2sign.push_back(0); m_expr2var.reset(); m_is_int.reset(); m_var2pos.reset(); m_forbidden.reset(); m_var2expr.reset(); m_expr2var.reset(); m_lowers.reset(); m_uppers.reset(); m_new_fmls.reset(); m_counter = 0; m_inconsistent = false; m_inconsistent_core = nullptr; init_forbidden_set(g); } // --------------------------- // // Internal data-structures // // --------------------------- static bool sign(literal l) { return l < 0; } static bvar lit2bvar(literal l) { return l < 0 ? -l : l; } bool is_int(var x) const { return m_is_int[x] != 0; } bool is_forbidden(var x) const { return m_forbidden[x] != 0; } bool all_int(constraint const & c) const { for (unsigned i = 0; i < c.m_num_vars; i++) { if (!is_int(c.m_xs[i])) return false; } return true; } app * to_expr(constraint const & c) { expr * ineq; if (c.m_num_vars == 0) { // 0 < k (for k > 0) --> true // 0 <= 0 -- > true if (c.m_c.is_pos() || (!c.m_strict && c.m_c.is_zero())) return m.mk_true(); ineq = nullptr; } else { bool int_cnstr = all_int(c); ptr_buffer ms; for (unsigned i = 0; i < c.m_num_vars; i++) { expr * x = m_var2expr.get(c.m_xs[i]); if (!int_cnstr && is_int(c.m_xs[i])) x = m_util.mk_to_real(x); if (c.m_as[i].is_one()) ms.push_back(x); else ms.push_back(m_util.mk_mul(m_util.mk_numeral(c.m_as[i], int_cnstr), x)); } expr * lhs; if (c.m_num_vars == 1) lhs = ms[0]; else lhs = m_util.mk_add(ms.size(), ms.c_ptr()); expr * rhs = m_util.mk_numeral(c.m_c, int_cnstr); if (c.m_strict) { ineq = m.mk_not(m_util.mk_ge(lhs, rhs)); } else { ineq = m_util.mk_le(lhs, rhs); } } if (c.m_num_lits == 0) { if (ineq) return to_app(ineq); else return m.mk_false(); } ptr_buffer lits; for (unsigned i = 0; i < c.m_num_lits; i++) { literal l = c.m_lits[i]; if (sign(l)) lits.push_back(m.mk_not(m_bvar2expr.get(lit2bvar(l)))); else lits.push_back(m_bvar2expr.get(lit2bvar(l))); } if (ineq) lits.push_back(ineq); if (lits.size() == 1) return to_app(lits[0]); else return m.mk_or(lits.size(), lits.c_ptr()); } var mk_var(expr * t) { SASSERT(::is_var(t)); SASSERT(m_util.is_int(t) || m_util.is_real(t)); var x = m_var2expr.size(); m_var2expr.push_back(t); bool is_int = m_util.is_int(t); m_is_int.push_back(is_int); m_var2pos.push_back(UINT_MAX); m_expr2var.insert(t, x); m_lowers.push_back(constraints()); m_uppers.push_back(constraints()); bool forbidden = m_forbidden_set.contains(::to_var(t)->get_idx()) || (m_fm_real_only && is_int); m_forbidden.push_back(forbidden); SASSERT(m_var2expr.size() == m_is_int.size()); SASSERT(m_lowers.size() == m_is_int.size()); SASSERT(m_uppers.size() == m_is_int.size()); SASSERT(m_forbidden.size() == m_is_int.size()); SASSERT(m_var2pos.size() == m_is_int.size()); TRACE("qe_lite", tout << mk_pp(t,m) << " |-> " << x << " forbidden: " << forbidden << "\n";); return x; } bvar mk_bvar(expr * t) { SASSERT(is_uninterp_const(t)); SASSERT(m.is_bool(t)); bvar p = m_bvar2expr.size(); m_bvar2expr.push_back(t); m_bvar2sign.push_back(0); SASSERT(m_bvar2expr.size() == m_bvar2sign.size()); m_expr2bvar.insert(t, p); SASSERT(p > 0); return p; } var to_var(expr * t) { var x; if (!m_expr2var.find(t, x)) x = mk_var(t); SASSERT(m_expr2var.contains(t)); SASSERT(m_var2expr.get(x) == t); TRACE("qe_lite", tout << mk_ismt2_pp(t, m) << " --> " << x << "\n";); return x; } bvar to_bvar(expr * t) { bvar p; if (m_expr2bvar.find(t, p)) return p; return mk_bvar(t); } literal to_literal(expr * t) { if (m.is_not(t, t)) return -to_bvar(t); else return to_bvar(t); } void add_constraint(expr * f, expr_dependency * dep) { TRACE("qe_lite", tout << mk_pp(f, m) << "\n";); SASSERT(!m.is_or(f) || m_fm_occ); sbuffer lits; sbuffer xs; buffer as; rational c; bool strict = false; unsigned num; expr * const * args; if (m.is_or(f)) { num = to_app(f)->get_num_args(); args = to_app(f)->get_args(); } else { num = 1; args = &f; } #if Z3DEBUG bool found_ineq = false; #endif for (unsigned i = 0; i < num; i++) { expr * l = args[i]; if (is_literal(l)) { lits.push_back(to_literal(l)); } else { // found inequality SASSERT(!found_ineq); DEBUG_CODE(found_ineq = true;); bool neg = m.is_not(l, l); SASSERT(m_util.is_le(l) || m_util.is_ge(l)); strict = neg; if (m_util.is_ge(l)) neg = !neg; expr * lhs = to_app(l)->get_arg(0); expr * rhs = to_app(l)->get_arg(1); VERIFY (m_util.is_numeral(rhs, c)); if (neg) c.neg(); unsigned num_mons; expr * const * mons; if (m_util.is_add(lhs)) { num_mons = to_app(lhs)->get_num_args(); mons = to_app(lhs)->get_args(); } else { num_mons = 1; mons = &lhs; } bool all_int = true; for (unsigned j = 0; j < num_mons; j++) { expr * monomial = mons[j]; expr * a; rational a_val; expr * x; if (m_util.is_mul(monomial, a, x)) { VERIFY(m_util.is_numeral(a, a_val)); } else { x = monomial; a_val = rational(1); } if (neg) a_val.neg(); VERIFY(is_var(x, x)); xs.push_back(to_var(x)); as.push_back(a_val); if (!is_int(xs.back())) all_int = false; } mk_int(as.size(), as.c_ptr(), c); if (all_int && strict) { strict = false; c--; } } } TRACE("qe_lite", tout << "before mk_constraint: "; for (unsigned i = 0; i < xs.size(); i++) tout << " " << xs[i]; tout << "\n";); constraint * new_c = mk_constraint(lits.size(), lits.c_ptr(), xs.size(), xs.c_ptr(), as.c_ptr(), c, strict, dep); TRACE("qe_lite", tout << "add_constraint: "; display(tout, *new_c); tout << "\n";); VERIFY(register_constraint(new_c)); } bool is_false(constraint const & c) const { return c.m_num_lits == 0 && c.m_num_vars == 0 && (c.m_c.is_neg() || (c.m_strict && c.m_c.is_zero())); } bool register_constraint(constraint * c) { normalize_coeffs(*c); if (is_false(*c)) { del_constraint(c); m_inconsistent = true; TRACE("qe_lite", tout << "is false "; display(tout, *c); tout << "\n";); return false; } bool r = false; for (unsigned i = 0; i < c->m_num_vars; i++) { var x = c->m_xs[i]; if (!is_forbidden(x)) { r = true; if (c->m_as[i].is_neg()) m_lowers[x].push_back(c); else m_uppers[x].push_back(c); } } if (r) { m_sub_todo.insert(*c); m_constraints.push_back(c); return true; } else { TRACE("qe_lite", tout << "all variables are forbidden "; display(tout, *c); tout << "\n";); m_new_fmls.push_back(to_expr(*c)); del_constraint(c); return false; } } void init_use_list(expr_ref_vector const & g) { unsigned sz = g.size(); for (unsigned i = 0; !m_inconsistent && i < sz; i++) { expr * f = g[i]; if (is_occ(f)) add_constraint(f, nullptr); else m_new_fmls.push_back(f); } } unsigned get_cost(var x) const { unsigned long long r = static_cast(m_lowers[x].size()) * static_cast(m_uppers[x].size()); if (r > UINT_MAX) return UINT_MAX; return static_cast(r); } typedef std::pair x_cost; struct x_cost_lt { char_vector const m_is_int; x_cost_lt(char_vector & is_int):m_is_int(is_int) {} bool operator()(x_cost const & p1, x_cost const & p2) const { // Integer variables with cost 0 can be eliminated even if they depend on real variables. // Cost 0 == no lower or no upper bound. if (p1.second == 0) { if (p2.second > 0) return true; return p1.first < p2.first; } if (p2.second == 0) return false; bool int1 = m_is_int[p1.first] != 0; bool int2 = m_is_int[p2.first] != 0; return (!int1 && int2) || (int1 == int2 && p1.second < p2.second); } }; void sort_candidates(var_vector & xs) { svector x_cost_vector; unsigned num = num_vars(); for (var x = 0; x < num; x++) { if (!is_forbidden(x)) { x_cost_vector.push_back(x_cost(x, get_cost(x))); } } // x_cost_lt is not a total order on variables std::stable_sort(x_cost_vector.begin(), x_cost_vector.end(), x_cost_lt(m_is_int)); TRACE("qe_lite", for (auto const& kv : x_cost_vector) { tout << "(" << mk_ismt2_pp(m_var2expr.get(kv.first), m) << " " << kv.second << ") "; } tout << "\n";); for (auto const& kv : x_cost_vector) { xs.push_back(kv.first); } } void cleanup_constraints(constraints & cs) { unsigned j = 0; unsigned sz = cs.size(); for (unsigned i = 0; i < sz; i++) { constraint * c = cs[i]; if (c->m_dead) continue; cs[j] = c; j++; } cs.shrink(j); } // Set all_int = true if all variables in c are int. // Set unit_coeff = true if the coefficient of x in c is 1 or -1. // If all_int = false, then unit_coeff may not be set. void analyze(constraint const & c, var x, bool & all_int, bool & unit_coeff) const { all_int = true; unit_coeff = true; for (unsigned i = 0; i < c.m_num_vars; i++) { if (!is_int(c.m_xs[i])) { all_int = false; return; } if (c.m_xs[i] == x) { unit_coeff = (c.m_as[i].is_one() || c.m_as[i].is_minus_one()); } } } void analyze(constraints const & cs, var x, bool & all_int, bool & unit_coeff) const { all_int = true; unit_coeff = true; for (constraint const* c : cs) { bool curr_unit_coeff; analyze(*c, x, all_int, curr_unit_coeff); if (!all_int) return; if (!curr_unit_coeff) unit_coeff = false; } } // An integer variable x may be eliminated, if // 1- All variables in the constraints it occur are integer. // 2- The coefficient of x in all lower bounds (or all upper bounds) is unit. bool can_eliminate(var x) const { if (!is_int(x)) return true; bool all_int; bool l_unit, u_unit; analyze(m_lowers[x], x, all_int, l_unit); if (!all_int) return false; analyze(m_uppers[x], x, all_int, u_unit); return all_int && (l_unit || u_unit); } void copy_constraints(constraints const & s, clauses & t) { for (constraint const* cns : s) { app * c = to_expr(*cns); t.push_back(c); } } clauses tmp_clauses; void save_constraints(var x) { } void mark_constraints_dead(constraints const & cs) { for (constraint* c : cs) c->m_dead = true; } void mark_constraints_dead(var x) { save_constraints(x); mark_constraints_dead(m_lowers[x]); mark_constraints_dead(m_uppers[x]); } void get_coeff(constraint const & c, var x, rational & a) { for (unsigned i = 0; i < c.m_num_vars; i++) { if (c.m_xs[i] == x) { a = c.m_as[i]; return; } } UNREACHABLE(); } var_vector new_xs; vector new_as; svector new_lits; constraint * resolve(constraint const & l, constraint const & u, var x) { m_counter += l.m_num_vars + u.m_num_vars + l.m_num_lits + u.m_num_lits; rational a, b; get_coeff(l, x, a); get_coeff(u, x, b); SASSERT(a.is_neg()); SASSERT(b.is_pos()); a.neg(); SASSERT(!is_int(x) || a.is_one() || b.is_one()); new_xs.reset(); new_as.reset(); rational new_c = l.m_c*b + u.m_c*a; bool new_strict = l.m_strict || u.m_strict; for (unsigned i = 0; i < l.m_num_vars; i++) { var xi = l.m_xs[i]; if (xi == x) continue; unsigned pos = new_xs.size(); new_xs.push_back(xi); SASSERT(m_var2pos[xi] == UINT_MAX); m_var2pos[xi] = pos; new_as.push_back(l.m_as[i] * b); SASSERT(new_xs[m_var2pos[xi]] == xi); SASSERT(new_xs.size() == new_as.size()); } for (unsigned i = 0; i < u.m_num_vars; i++) { var xi = u.m_xs[i]; if (xi == x) continue; unsigned pos = m_var2pos[xi]; if (pos == UINT_MAX) { new_xs.push_back(xi); new_as.push_back(u.m_as[i] * a); } else { new_as[pos] += u.m_as[i] * a; } } // remove zeros and check whether all variables are int bool all_int = true; unsigned sz = new_xs.size(); unsigned j = 0; for (unsigned i = 0; i < sz; i++) { if (new_as[i].is_zero()) continue; if (!is_int(new_xs[i])) all_int = false; if (i != j) { new_xs[j] = new_xs[i]; new_as[j] = new_as[i]; } j++; } new_xs.shrink(j); new_as.shrink(j); if (all_int && new_strict) { new_strict = false; new_c --; } // reset m_var2pos for (unsigned i = 0; i < l.m_num_vars; i++) { m_var2pos[l.m_xs[i]] = UINT_MAX; } if (new_xs.empty() && (new_c.is_pos() || (!new_strict && new_c.is_zero()))) { // literal is true TRACE("qe_lite", tout << "resolution " << x << " consequent literal is always true: \n"; display(tout, l); tout << "\n"; display(tout, u); tout << "\n";); return nullptr; // no constraint needs to be created. } new_lits.reset(); for (unsigned i = 0; i < l.m_num_lits; i++) { literal lit = l.m_lits[i]; bvar p = lit2bvar(lit); m_bvar2sign[p] = sign(lit) ? -1 : 1; new_lits.push_back(lit); } bool tautology = false; for (unsigned i = 0; i < u.m_num_lits && !tautology; i++) { literal lit = u.m_lits[i]; bvar p = lit2bvar(lit); switch (m_bvar2sign[p]) { case 0: new_lits.push_back(lit); break; case -1: if (!sign(lit)) tautology = true; break; case 1: if (sign(lit)) tautology = true; break; default: UNREACHABLE(); } } // reset m_bvar2sign for (unsigned i = 0; i < l.m_num_lits; i++) { literal lit = l.m_lits[i]; bvar p = lit2bvar(lit); m_bvar2sign[p] = 0; } if (tautology) { TRACE("qe_lite", tout << "resolution " << x << " tautology: \n"; display(tout, l); tout << "\n"; display(tout, u); tout << "\n";); return nullptr; } expr_dependency * new_dep = m.mk_join(l.m_dep, u.m_dep); if (new_lits.empty() && new_xs.empty() && (new_c.is_neg() || (new_strict && new_c.is_zero()))) { TRACE("qe_lite", tout << "resolution " << x << " inconsistent: \n"; display(tout, l); tout << "\n"; display(tout, u); tout << "\n";); m_inconsistent = true; m_inconsistent_core = new_dep; return nullptr; } constraint * new_cnstr = mk_constraint(new_lits.size(), new_lits.c_ptr(), new_xs.size(), new_xs.c_ptr(), new_as.c_ptr(), new_c, new_strict, new_dep); TRACE("qe_lite", tout << "resolution " << x << "\n"; display(tout, l); tout << "\n"; display(tout, u); tout << "\n---->\n"; display(tout, *new_cnstr); tout << "\n"; tout << "new_dep: " << new_dep << "\n";); return new_cnstr; } ptr_vector new_constraints; bool try_eliminate(var x) { constraints & l = m_lowers[x]; constraints & u = m_uppers[x]; cleanup_constraints(l); cleanup_constraints(u); if (l.empty() || u.empty()) { // easy case mark_constraints_dead(x); TRACE("qe_lite", tout << "variable was eliminated (trivial case)\n";); return true; } unsigned num_lowers = l.size(); unsigned num_uppers = u.size(); if (num_lowers > m_fm_cutoff1 && num_uppers > m_fm_cutoff1) return false; if (num_lowers * num_uppers > m_fm_cutoff2) return false; if (!can_eliminate(x)) return false; m_counter += num_lowers * num_uppers; TRACE("qe_lite", tout << "eliminating " << mk_ismt2_pp(m_var2expr.get(x), m) << "\nlowers:\n"; display_constraints(tout, l); tout << "uppers:\n"; display_constraints(tout, u);); unsigned num_old_cnstrs = num_uppers + num_lowers; unsigned limit = num_old_cnstrs + m_fm_extra; unsigned num_new_cnstrs = 0; new_constraints.reset(); for (unsigned i = 0; i < num_lowers; i++) { for (unsigned j = 0; j < num_uppers; j++) { if (m_inconsistent || num_new_cnstrs > limit) { TRACE("qe_lite", tout << "too many new constraints: " << num_new_cnstrs << "\n";); del_constraints(new_constraints.size(), new_constraints.c_ptr()); return false; } constraint const & l_c = *(l[i]); constraint const & u_c = *(u[j]); constraint * new_c = resolve(l_c, u_c, x); if (new_c != nullptr) { num_new_cnstrs++; new_constraints.push_back(new_c); } } } mark_constraints_dead(x); unsigned sz = new_constraints.size(); m_counter += sz; for (unsigned i = 0; i < sz; i++) { constraint * c = new_constraints[i]; backward_subsumption(*c); register_constraint(c); } TRACE("qe_lite", tout << "variables was eliminated old: " << num_old_cnstrs << " new_constraints: " << sz << "\n";); return true; } void copy_remaining(vector & v2cs) { for (constraints& cs : v2cs) { for (constraint* c : cs) { if (!c->m_dead) { c->m_dead = true; expr * new_f = to_expr(*c); TRACE("qe_lite", tout << "asserting...\n" << mk_ismt2_pp(new_f, m) << "\nnew_dep: " << c->m_dep << "\n";); m_new_fmls.push_back(new_f); } } } v2cs.finalize(); } // Copy remaining clauses to m_new_fmls void copy_remaining() { copy_remaining(m_uppers); copy_remaining(m_lowers); } void checkpoint() { if (m.canceled()) throw tactic_exception(m.limit().get_cancel_msg()); } public: void set_is_variable_proc(is_variable_proc& proc) { m_is_variable = &proc;} void operator()(expr_ref_vector& fmls) { init(fmls); init_use_list(fmls); for (auto & f : fmls) { if (has_quantifiers(f)) return; } if (m_inconsistent) { m_new_fmls.reset(); m_new_fmls.push_back(m.mk_false()); } else { TRACE("qe_lite", display(tout);); subsume(); var_vector candidates; sort_candidates(candidates); unsigned eliminated = 0; unsigned num = candidates.size(); for (unsigned i = 0; i < num; i++) { checkpoint(); if (m_counter > m_fm_limit) break; m_counter++; if (try_eliminate(candidates[i])) eliminated++; if (m_inconsistent) { m_new_fmls.reset(); m_new_fmls.push_back(m.mk_false()); break; } } if (!m_inconsistent) { copy_remaining(); } } reset_constraints(); fmls.reset(); fmls.append(m_new_fmls); } void display_constraints(std::ostream & out, constraints const & cs) const { for (constraint const* c : cs) { display(out << " ", *c); out << "\n"; } } void display(std::ostream & out) const { unsigned num = num_vars(); for (var x = 0; x < num; x++) { if (is_forbidden(x)) continue; out << mk_ismt2_pp(m_var2expr.get(x), m) << "\n"; display_constraints(out, m_lowers[x]); display_constraints(out, m_uppers[x]); } } }; } // namespace fm class qe_lite::impl { public: struct elim_cfg : public default_rewriter_cfg { impl& m_imp; ast_manager& m; public: elim_cfg(impl& i): m_imp(i), m(i.m) {} bool reduce_quantifier(quantifier * q, expr * new_body, expr * const * new_patterns, expr * const * new_no_patterns, expr_ref & result, proof_ref & result_pr) { result = new_body; if (is_forall(q)) { result = m.mk_not(result); } uint_set indices; for (unsigned i = 0; i < q->get_num_decls(); ++i) { indices.insert(i); } if (q->get_kind() != lambda_k) { m_imp(indices, true, result); } if (is_forall(q)) { result = push_not(result); } result = m.update_quantifier( q, q->get_num_patterns(), new_patterns, q->get_num_no_patterns(), new_no_patterns, result); m_imp.m_rewriter(result); return true; } }; class elim_star : public rewriter_tpl { elim_cfg m_cfg; public: elim_star(impl& i): rewriter_tpl(i.m, false, m_cfg), m_cfg(i) {} }; private: ast_manager& m; eq::der m_der; fm::fm m_fm; ar::der m_array_der; elim_star m_elim_star; th_rewriter m_rewriter; bool m_use_array_der; bool has_unique_non_ground(expr_ref_vector const& fmls, unsigned& index) { index = fmls.size(); if (index <= 1) { return false; } for (unsigned i = 0; i < fmls.size(); ++i) { if (!is_ground(fmls[i])) { if (index != fmls.size()) { return false; } index = i; } } return index < fmls.size(); } public: impl(ast_manager & m, params_ref const & p, bool use_array_der): m(m), m_der(m, p), m_fm(m), m_array_der(m), m_elim_star(*this), m_rewriter(m), m_use_array_der(use_array_der) {} void operator()(app_ref_vector& vars, expr_ref& fml) { if (vars.empty()) { return; } expr_ref tmp(fml); quantifier_ref q(m); proof_ref pr(m); symbol qe_lite("QE"); expr_abstract(m, 0, vars.size(), (expr*const*)vars.c_ptr(), fml, tmp); ptr_vector sorts; svector names; for (unsigned i = 0; i < vars.size(); ++i) { sorts.push_back(m.get_sort(vars[i].get())); names.push_back(vars[i]->get_decl()->get_name()); } q = m.mk_exists(vars.size(), sorts.c_ptr(), names.c_ptr(), tmp, 1, qe_lite); m_der.reduce_quantifier(q, tmp, pr); // assumes m_der just updates the quantifier and does not change things more. if (is_exists(tmp) && to_quantifier(tmp)->get_qid() == qe_lite) { used_vars used; tmp = to_quantifier(tmp)->get_expr(); used.process(tmp); var_subst vs(m, true); fml = vs(tmp, vars.size(), (expr*const*)vars.c_ptr()); // collect set of variables that were used. unsigned j = 0; for (unsigned i = 0; i < vars.size(); ++i) { if (used.contains(vars.size()-i-1)) { vars.set(j, vars.get(i)); ++j; } } vars.resize(j); } else { fml = std::move(tmp); } } void operator()(expr_ref& fml, proof_ref& pr) { expr_ref tmp(m); m_elim_star(fml, tmp, pr); fml = std::move(tmp); } void operator()(uint_set const& index_set, bool index_of_bound, expr_ref& fml) { expr_ref_vector disjs(m); flatten_or(fml, disjs); for (unsigned i = 0; i < disjs.size(); ++i) { expr_ref_vector conjs(m); conjs.push_back(disjs[i].get()); (*this)(index_set, index_of_bound, conjs); bool_rewriter(m).mk_and(conjs.size(), conjs.c_ptr(), fml); disjs[i] = fml; } bool_rewriter(m).mk_or(disjs.size(), disjs.c_ptr(), fml); } void operator()(uint_set const& index_set, bool index_of_bound, expr_ref_vector& fmls) { flatten_and(fmls); unsigned index; if (has_unique_non_ground(fmls, index)) { expr_ref fml(m); fml = fmls[index].get(); (*this)(index_set, index_of_bound, fml); fmls[index] = fml; return; } TRACE("qe_lite", tout << fmls << "\n";); is_variable_test is_var(index_set, index_of_bound); m_der.set_is_variable_proc(is_var); m_fm.set_is_variable_proc(is_var); m_array_der.set_is_variable_proc(is_var); m_der(fmls); m_fm(fmls); // AG: disalble m_array_der() since it interferes with other array handling if (m_use_array_der) m_array_der(fmls); TRACE("qe_lite", for (unsigned i = 0; i < fmls.size(); ++i) tout << mk_pp(fmls[i].get(), m) << "\n";); } }; qe_lite::qe_lite(ast_manager & m, params_ref const & p, bool use_array_der) { m_impl = alloc(impl, m, p, use_array_der); } qe_lite::~qe_lite() { dealloc(m_impl); } void qe_lite::operator()(app_ref_vector& vars, expr_ref& fml) { (*m_impl)(vars, fml); } void qe_lite::operator()(expr_ref& fml, proof_ref& pr) { (*m_impl)(fml, pr); } void qe_lite::operator()(uint_set const& index_set, bool index_of_bound, expr_ref& fml) { (*m_impl)(index_set, index_of_bound, fml); } void qe_lite::operator()(uint_set const& index_set, bool index_of_bound, expr_ref_vector& fmls) { (*m_impl)(index_set, index_of_bound, fmls); } namespace { class qe_lite_tactic : public tactic { struct imp { ast_manager& m; qe_lite m_qe; imp(ast_manager& m, params_ref const & p): m(m), m_qe(m, p, true) {} void checkpoint() { if (m.canceled()) throw tactic_exception(m.limit().get_cancel_msg()); } void debug_diff(expr* a, expr* b) { ptr_vector as, bs; as.push_back(a); bs.push_back(b); expr* a1, *a2, *b1, *b2; while (!as.empty()) { a = as.back(); b = bs.back(); as.pop_back(); bs.pop_back(); if (a == b) { continue; } else if (is_forall(a) && is_forall(b)) { as.push_back(to_quantifier(a)->get_expr()); bs.push_back(to_quantifier(b)->get_expr()); } else if (m.is_and(a, a1, a2) && m.is_and(b, b1, b2)) { as.push_back(a1); as.push_back(a2); bs.push_back(b1); bs.push_back(b2); } else if (m.is_eq(a, a1, a2) && m.is_eq(b, b1, b2)) { as.push_back(a1); as.push_back(a2); bs.push_back(b1); bs.push_back(b2); } else { TRACE("qe", tout << mk_pp(a, m) << " != " << mk_pp(b, m) << "\n";); } } } void operator()(goal_ref const & g, goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); tactic_report report("qe-lite", *g); proof_ref new_pr(m); expr_ref new_f(m); unsigned sz = g->size(); for (unsigned i = 0; i < sz; i++) { checkpoint(); if (g->inconsistent()) break; expr * f = g->form(i); if (!has_quantifiers(f)) continue; new_f = f; m_qe(new_f, new_pr); if (new_pr) { expr* fact = m.get_fact(new_pr); if (to_app(fact)->get_arg(0) != to_app(fact)->get_arg(1)) { new_pr = m.mk_modus_ponens(g->pr(i), new_pr); } else { new_pr = g->pr(i); } } if (f != new_f) { TRACE("qe", tout << mk_pp(f, m) << "\n" << new_f << "\n";); g->update(i, new_f, new_pr, g->dep(i)); } } g->inc_depth(); result.push_back(g.get()); TRACE("qe", g->display(tout);); SASSERT(g->is_well_sorted()); } }; params_ref m_params; imp * m_imp; public: qe_lite_tactic(ast_manager & m, params_ref const & p): m_params(p) { m_imp = alloc(imp, m, p); } ~qe_lite_tactic() override { dealloc(m_imp); } tactic * translate(ast_manager & m) override { return alloc(qe_lite_tactic, m, m_params); } void updt_params(params_ref const & p) override { m_params = p; // m_imp->updt_params(p); } void collect_param_descrs(param_descrs & r) override { // m_imp->collect_param_descrs(r); } void operator()(goal_ref const & in, goal_ref_buffer & result) override { (*m_imp)(in, result); } void collect_statistics(statistics & st) const override { // m_imp->collect_statistics(st); } void reset_statistics() override { // m_imp->reset_statistics(); } void cleanup() override { ast_manager & m = m_imp->m; 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); } template class rewriter_tpl; z3-z3-4.8.7/src/qe/qe_lite.h000066400000000000000000000033431356505360400154210ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qe_lite.h Abstract: Light weight partial quantifier-elimination procedures Author: Nikolaj Bjorner (nbjorner) 2012-10-17 Revision History: --*/ #pragma once #include "ast/ast.h" #include "util/uint_set.h" #include "util/params.h" class tactic; class qe_lite { class impl; impl * m_impl; public: /** use_array_der controls whether equalities over array reads are simplified */ qe_lite(ast_manager& m, params_ref const & p, bool use_array_der = true); ~qe_lite(); /** \brief Apply light-weight quantifier elimination on constants provided as vector of variables. Return the updated formula and updated set of variables that were not eliminated. */ void operator()(app_ref_vector& vars, expr_ref& fml); /** \brief Apply light-weight quantifier elimination to variables present/absent in the index set. If 'index_of_bound' is true, then the index_set is treated as the set of bound variables. if 'index_of_bound' is false, then index_set is treated as the set of variables that are not bound (variables that are not in the index set are bound). */ void operator()(uint_set const& index_set, bool index_of_bound, expr_ref& fml); void operator()(uint_set const& index_set, bool index_of_bound, expr_ref_vector& conjs); /** \brief full rewriting based light-weight quantifier elimination round. */ void operator()(expr_ref& fml, proof_ref& pr); }; 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)") */ z3-z3-4.8.7/src/qe/qe_mbi.cpp000066400000000000000000000504361356505360400155730ustar00rootroot00000000000000/*++ Copyright (c) 2018 Microsoft Corporation Module Name: qe_mbi.cpp Abstract: Model-based interpolation utilities Author: Nikolaj Bjorner (nbjorner), Arie Gurfinkel 2018-6-8 Revision History: Notes: Reduction into: T_EUF T_LIRA Other theories: DT, ARR reduced to EUF BV is EUF/Boolean. --*/ #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" #include "solver/solver.h" #include "qe/qe_mbi.h" #include "qe/qe_term_graph.h" #include "qe/qe_arith.h" #include "qe/qe_arrays.h" namespace qe { lbool mbi_plugin::check(expr_ref_vector& lits, model_ref& mdl) { while (true) { switch ((*this)(lits, mdl)) { case mbi_sat: return l_true; case mbi_unsat: return l_false; case mbi_undef: return l_undef; case mbi_augment: break; } } } // ------------------------------- // prop_mbi prop_mbi_plugin::prop_mbi_plugin(solver* s): mbi_plugin(s->get_manager()), m_solver(s) {} // sketch of propositional mbi_result prop_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); return mbi_unsat; case l_true: m_solver->get_model(mdl); lits.reset(); for (unsigned i = 0, sz = mdl->get_num_constants(); i < sz; ++i) { func_decl* c = mdl->get_constant(i); if (m_shared.contains(c)) { if (m.is_true(mdl->get_const_interp(c))) { lits.push_back(m.mk_const(c)); } else if (m.is_false(mdl->get_const_interp(c))) { lits.push_back(m.mk_not(m.mk_const(c))); } } } return mbi_sat; default: return mbi_undef; } } void prop_mbi_plugin::block(expr_ref_vector const& lits) { m_solver->assert_expr(mk_not(mk_and(lits))); } // ------------------------------- // euf_arith_mbi struct euf_arith_mbi_plugin::is_atom_proc { ast_manager& m; expr_ref_vector& m_atoms; obj_hashtable& m_atom_set; is_atom_proc(expr_ref_vector& atoms, obj_hashtable& atom_set): m(atoms.m()), m_atoms(atoms), m_atom_set(atom_set) {} void operator()(app* a) { if (m_atom_set.contains(a)) { // continue } else if (m.is_eq(a)) { m_atoms.push_back(a); m_atom_set.insert(a); } else if (m.is_bool(a) && a->get_family_id() != m.get_basic_family_id()) { m_atoms.push_back(a); m_atom_set.insert(a); } } void operator()(expr*) {} }; struct euf_arith_mbi_plugin::is_arith_var_proc { ast_manager& m; 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 (is_arith_op(a) || a->get_family_id() == m.get_basic_family_id()) { return; } if (m_arith.is_int_real(a)) { m_avars.push_back(a); if (!m_seen.contains(a)) { m_proxies.push_back(a); m_seen.insert(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), m_fmls(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); m_solver->get_assertions(m_fmls); collect_atoms(m_fmls); } void euf_arith_mbi_plugin::collect_atoms(expr_ref_vector const& fmls) { expr_fast_mark1 marks; is_atom_proc proc(m_atoms, m_atom_set); for (expr* e : fmls) { quick_for_each_expr(proc, marks, e); } } bool euf_arith_mbi_plugin::get_literals(model_ref& mdl, expr_ref_vector& lits) { lits.reset(); for (expr* e : m_atoms) { if (mdl->is_true(e)) { lits.push_back(e); } else if (mdl->is_false(e)) { lits.push_back(m.mk_not(e)); } } TRACE("qe", tout << "atoms from model: " << lits << "\n";); solver_ref dual = m_dual_solver->translate(m, m_dual_solver->get_params()); dual->assert_expr(mk_not(mk_and(m_fmls))); lbool r = dual->check_sat(lits); TRACE("qe", dual->display(tout << "dual result " << r << "\n");); if (l_false == r) { // use the dual solver to find a 'small' implicant lits.reset(); dual->get_unsat_core(lits); return true; } else { return false; } } /** * \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); return avars; } mbi_result euf_arith_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); TRACE("qe", tout << "unsat core: " << lits << "\n";); // optionally minimize core using superposition. return mbi_unsat; case l_true: m_solver->get_model(mdl); if (!get_literals(mdl, lits)) { return mbi_undef; } project(mdl, lits); 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_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";); // 0. saturation array_project_plugin arp(m); arp.saturate(*mdl, m_shared, lits); // . 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";); // . project private non-arithmetical variables from lits project_euf(mdl, lits, avars); // . Minimzie span between smallest and largest proxy variable. minimize_span(mdl, avars, proxies); // . Order arithmetical variables and purified positions order_avars(mdl, lits, avars, proxies); TRACE("qe", tout << "ordered: " << lits << "\n";); // . 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";); // . Substitute solution into lits substitute(defs, lits); TRACE("qe", tout << "substitute: " << lits << "\n";); IF_VERBOSE(1, verbose_stream() << 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 private symbols. * - project with respect to shared symbols only. * retains equalities that are independent of arithmetic * - project with respect to shared + arithmetic basic terms * retains predicates that are projected by arithmetic */ void euf_arith_mbi_plugin::project_euf(model_ref& mdl, expr_ref_vector& lits, app_ref_vector& avars) { term_graph tg1(m), tg2(m); func_decl_ref_vector shared(m_shared); tg1.set_vars(shared, false); for (app* a : avars) shared.push_back(a->get_decl()); tg2.set_vars(shared, false); tg1.add_lits(lits); tg2.add_lits(lits); lits.reset(); lits.append(tg1.project(*mdl.get())); lits.append(tg2.project(*mdl.get())); TRACE("qe", tout << "project: " << lits << "\n";); } vector> euf_arith_mbi_plugin::sort_proxies(model_ref& mdl, 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 offset ordering between proxies compare_first cmp; std::sort(vals.begin(), vals.end(), cmp); return vals; } void euf_arith_mbi_plugin::minimize_span(model_ref& mdl, app_ref_vector& avars, app_ref_vector const& proxies) { #if 0 arith_util a(m); opt::context opt(m); expr_ref_vector fmls(m); m_solver->get_assertions(fmls); for (expr* l : fmls) opt.add_hard_constraint(l); vector> vals = sort_proxies(mdl, proxies); app_ref t(m); for (unsigned i = 1; i < vals.size(); ++i) { rational offset = vals[i].first - vals[i-1].first; expr* t1 = vals[i-1].second; expr* t2 = vals[i].second; if (offset.is_zero()) { t = m.mk_eq(t1, t2); } else { SASSERT(offset.is_pos()); t = a.mk_lt(t1, t2); } opt.add_hard_constraint(t); } t = a.mk_sub(vals[0].second, vals.back().second); opt.add_objective(t, true); expr_ref_vector asms(m); VERIFY(l_true == opt.optimize(asms)); opt.get_model(mdl); model_evaluator mev(*mdl.get()); std::cout << mev(t) << "\n"; #endif } /** * \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 = sort_proxies(mdl, proxies); for (unsigned i = 1; i < vals.size(); ++i) { rational offset = vals[i].first - vals[i-1].first; expr* t1 = vals[i-1].second; expr* t2 = vals[i].second; if (offset.is_zero()) { lits.push_back(m.mk_eq(t1, t2)); } else { expr_ref t(a.mk_add(t1, a.mk_numeral(offset, true)), m); lits.push_back(a.mk_le(t, t2)); } } // 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()) || (x->get_depth() == y->get_depth() && x->get_id() > y->get_id()); } }; 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) { // want to rely only on atoms from original literals: collect_atoms(lits); expr_ref conj(mk_not(mk_and(lits)), m); //m_fmls.push_back(conj); TRACE("qe", tout << "block " << lits << "\n";); m_solver->assert_expr(conj); } /** -------------------------------------------------------------- * ping-pong interpolation of Gurfinkel & Vizel * compute a binary interpolant. */ lbool interpolator::pingpong(mbi_plugin& a, mbi_plugin& b, expr_ref& itp) { model_ref mdl; expr_ref_vector lits(m); bool turn = true; vector itps, blocks; itps.push_back(expr_ref_vector(m)); itps.push_back(expr_ref_vector(m)); blocks.push_back(expr_ref_vector(m)); blocks.push_back(expr_ref_vector(m)); mbi_result last_res = mbi_undef; bool_rewriter rw(m); while (true) { auto* t1 = turn ? &a : &b; auto* t2 = turn ? &b : &a; mbi_result next_res = (*t1)(lits, mdl); switch (next_res) { case mbi_sat: if (last_res == mbi_sat) { itp = nullptr; return l_true; } TRACE("mbi", tout << "new lits " << lits << "\n";); break; // continue case mbi_unsat: { if (lits.empty()) { // TBD, either a => itp and itp => !b // or b => itp and itp => !a itp = mk_and(itps[!turn]); return l_false; } t2->block(lits); expr_ref lemma(mk_not(mk_and(lits))); TRACE("mbi", tout << lemma << "\n";); blocks[turn].push_back(lemma); itp = m.mk_implies(mk_and(blocks[!turn]), lemma); // TBD: compute closure over variables not in vars itps[turn].push_back(itp); lits.reset(); // or find a prefix of lits? break; } case mbi_augment: break; case mbi_undef: return l_undef; } turn = !turn; last_res = next_res; } } /** * One-sided pogo creates clausal interpolants. * It creates a set of consequences of b that are inconsistent with a. */ lbool interpolator::pogo(mbi_plugin& a, mbi_plugin& b, expr_ref& itp) { expr_ref_vector lits(m), itps(m); while (true) { model_ref mdl; lits.reset(); switch (a.check(lits, mdl)) { case l_true: switch (b.check(lits, mdl)) { case l_true: return l_true; case l_false: a.block(lits); itps.push_back(mk_not(mk_and(lits))); break; case l_undef: return l_undef; } break; case l_false: itp = mk_and(itps); return l_false; case l_undef: return l_undef; } } } lbool interpolator::vurtego(mbi_plugin& a, mbi_plugin& b, expr_ref& itp, model_ref &mdl) { /** Assumptions on mbi_plugin() Let local be assertions local to the plugin Let blocked be clauses added by blocked, kept separately from local mbi_plugin::check(lits, mdl, bool force_model): if lits.empty() and mdl == nullptr then if is_sat(local & blocked) then return l_true, mbp of local, mdl of local & blocked else return l_false else if !lits.empty() then if is_sat(local & mdl & blocked) return l_true, lits, extension of mdl to local else if is_sat(local & lits & blocked) if (force_model) then return l_false, core of model, nullptr else return l_true, mbp of local, mdl of local & blocked else if !is_sat(local & lits) then return l_false, mbp of local, nullptr else if is_sat(local & lits) && !is_sat(local & lits & blocked) MISSING CASE MUST PRODUCE AN IMPLICANT OF LOCAL that is inconsistent with lits & blocked in this case !is_sat(local & lits & mdl) and is_sat(mdl, blocked) let mdl_blocked be lits of blocked that are true in mdl return l_false, core of lits & mdl_blocked, nullptr mbi_plugin::block(phi): add phi to blocked probably should use the operator() instead of check. mbi_augment -- means consistent with lits but not with the mdl mbi_sat -- means consistent with lits and mdl */ expr_ref_vector lits(m), itps(m); while (true) { // when lits.empty(), this picks an A-implicant consistent with B // when !lits.empty(), checks whether mdl of shared vocab extends to A bool force_model = !lits.empty(); switch (a.check_ag(lits, mdl, force_model)) { case l_true: if (force_model) // mdl is a model for a && b return l_true; switch (b.check_ag(lits, mdl, false)) { case l_true: /* can return true if know that b did not change the model. For now, cycle back to A. */ SASSERT(!lits.empty()); SASSERT(mdl); break; case l_false: // Force a different A-implicant a.block(lits); lits.reset(); mdl.reset(); break; case l_undef: return l_undef; } case l_false: if (lits.empty()) { // no more A-implicants, terminate itp = mk_and(itps); return l_false; } // force B to pick a different model or a different implicant b.block(lits); itps.push_back(mk_not(mk_and(lits))); lits.reset(); mdl.reset(); break; case l_undef: return l_undef; } } } }; z3-z3-4.8.7/src/qe/qe_mbi.h000066400000000000000000000103071356505360400152310ustar00rootroot00000000000000/*++ Copyright (c) 2018 Microsoft Corporation Module Name: qe_mbi.h Abstract: Model-based interpolation utilities Author: Nikolaj Bjorner (nbjorner), Arie Gurfinkel 2018-6-8 Revision History: --*/ #pragma once #include "qe/qe_arith.h" namespace qe { enum mbi_result { mbi_sat, mbi_unsat, mbi_augment, mbi_undef, }; class mbi_plugin { protected: ast_manager& m; func_decl_ref_vector m_shared; public: mbi_plugin(ast_manager& m): m(m), m_shared(m) {} virtual ~mbi_plugin() {} /** * Set the shared symbols. */ virtual void set_shared(func_decl_ref_vector const& vars) { m_shared.reset(); m_shared.append(vars); } /** * \brief Utility that works modulo a background state. * - vars * variables to preferably project onto (other variables would require quantification to fit interpolation signature) * - lits * set of literals to check satisfiability with respect to. * - mdl * optional model for caller. * on return: * - mbi_sat: * populates mdl with a satisfying state, and lits with implicant for background state. * - mbi_unsat: * populates lits to be inconsistent with background state. * For all practical purposes it is a weakening of lits or even a subset of lits. * - mbi_augment: * populates lits with strengthening of lits (superset) * - mbi_undef: * inconclusive, */ virtual mbi_result operator()(expr_ref_vector& lits, model_ref& mdl) = 0; /** * \brief Block conjunction of lits from future mbi_augment or mbi_sat. */ virtual void block(expr_ref_vector const& lits) = 0; /** * \brief perform a full check, consume internal auguments if necessary. */ lbool check(expr_ref_vector& lits, model_ref& mdl); virtual lbool check_ag(expr_ref_vector& lits, model_ref& mdl, bool force_model) { return l_undef; } }; class prop_mbi_plugin : public mbi_plugin { solver_ref m_solver; public: prop_mbi_plugin(solver* s); ~prop_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; obj_hashtable m_atom_set; expr_ref_vector m_fmls; solver_ref m_solver; solver_ref m_dual_solver; 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& 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); vector> sort_proxies(model_ref& mdl, app_ref_vector const& proxies); void minimize_span(model_ref& mdl, app_ref_vector& avars, app_ref_vector const& proxies); 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; }; /** * use cases for interpolation. */ class interpolator { ast_manager& m; public: interpolator(ast_manager& m):m(m) {} lbool pingpong(mbi_plugin& a, mbi_plugin& b, expr_ref& itp); lbool pogo(mbi_plugin& a, mbi_plugin& b, expr_ref& itp); lbool vurtego(mbi_plugin &a, mbi_plugin &b, expr_ref &itp, model_ref &mdl); }; }; z3-z3-4.8.7/src/qe/qe_mbp.cpp000066400000000000000000000554651356505360400156110ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: qe_mbp.cpp Abstract: Model-based projection utilities Author: Nikolaj Bjorner (nbjorner) 2015-5-29 Revision History: --*/ #include "ast/rewriter/expr_safe_replace.h" #include "ast/ast_pp.h" #include "ast/ast_util.h" #include "ast/occurs.h" #include "ast/rewriter/th_rewriter.h" #include "ast/expr_functors.h" #include "ast/for_each_expr.h" #include "ast/scoped_proof.h" #include "qe/qe_mbp.h" #include "qe/qe_arith.h" #include "qe/qe_arrays.h" #include "qe/qe_datatypes.h" #include "qe/qe_lite.h" #include "model/model_pp.h" #include "model/model_evaluator.h" using namespace qe; struct noop_op_proc { void operator()(expr*) {} }; void project_plugin::mark_rec(expr_mark& visited, expr* e) { for_each_expr_proc fe; for_each_expr(fe, visited, e); } void project_plugin::mark_rec(expr_mark& visited, expr_ref_vector const& es) { for (unsigned i = 0; i < es.size(); ++i) { mark_rec(visited, es[i]); } } /** \brief return two terms that are equal in the model. The distinct term t is false in model, so there are at least two arguments of t that are equal in the model. */ expr_ref project_plugin::pick_equality(ast_manager& m, model& model, expr* t) { SASSERT(m.is_distinct(t)); expr_ref val(m); expr_ref_vector vals(m); obj_map val2expr; app* alit = to_app(t); for (expr * e1 : *alit) { expr *e2; val = model(e1); if (val2expr.find(val, e2)) { return expr_ref(m.mk_eq(e1, e2), m); } val2expr.insert(val, e1); vals.push_back(val); } UNREACHABLE(); return expr_ref(nullptr, m); } void project_plugin::erase(expr_ref_vector& lits, unsigned& i) { lits[i] = lits.back(); lits.pop_back(); --i; } void project_plugin::push_back(expr_ref_vector& lits, expr* e) { if (lits.get_manager().is_true(e)) return; lits.push_back(e); } class mbp::impl { ast_manager& m; params_ref m_params; th_rewriter m_rw; ptr_vector m_plugins; expr_mark m_visited; expr_mark m_bool_visited; // parameters bool m_reduce_all_selects; bool m_dont_sub; void add_plugin(project_plugin* p) { family_id fid = p->get_family_id(); SASSERT(!m_plugins.get(fid, 0)); m_plugins.setx(fid, p, 0); } project_plugin* get_plugin(app* var) { family_id fid = m.get_sort(var)->get_family_id(); return m_plugins.get(fid, 0); } bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) { expr_mark is_var, is_rem; if (vars.empty()) { return false; } bool reduced = false; for (unsigned i = 0; i < vars.size(); ++i) { is_var.mark(vars[i].get()); } expr_ref tmp(m), t(m), v(m); for (unsigned i = 0; i < lits.size(); ++i) { expr* e = lits[i].get(), *l, *r; if (m.is_eq(e, l, r) && reduce_eq(is_var, l, r, v, t)) { reduced = true; project_plugin::erase(lits, i); expr_safe_replace sub(m); sub.insert(v, t); is_rem.mark(v); for (unsigned j = 0; j < lits.size(); ++j) { sub(lits[j].get(), tmp); m_rw(tmp); lits[j] = tmp; } } } if (reduced) { unsigned j = 0; for (app* v : vars) { if (!is_rem.is_marked(v)) { vars[j++] = v; } } vars.shrink(j); } return reduced; } bool reduce_eq(expr_mark& is_var, expr* l, expr* r, expr_ref& v, expr_ref& t) { if (is_var.is_marked(r)) { std::swap(l, r); } if (is_var.is_marked(l)) { contains_app cont(m, to_app(l)); if (!cont(r)) { v = to_app(l); t = r; return true; } } return false; } void filter_variables(model& model, app_ref_vector& vars, expr_ref_vector& lits, expr_ref_vector& unused_lits) { expr_mark lit_visited; project_plugin::mark_rec(lit_visited, lits); unsigned j = 0; for (app* var : vars) { if (lit_visited.is_marked(var)) { vars[j++] = var; } } vars.shrink(j); } bool extract_bools(model_evaluator& eval, expr_ref_vector& fmls, expr* fml) { TRACE("qe", tout << "extract bools: " << mk_pp(fml, m) << "\n";); ptr_vector todo; expr_safe_replace sub(m); m_visited.reset(); bool found_bool = false; if (is_app(fml)) { todo.append(to_app(fml)->get_num_args(), to_app(fml)->get_args()); } while (!todo.empty()) { expr* e = todo.back(); todo.pop_back(); if (m_visited.is_marked(e)) { continue; } m_visited.mark(e); if (m.is_bool(e) && !m.is_true(e) && !m.is_false(e)) { expr_ref val = eval(e); TRACE("qe", tout << "found: " << mk_pp(e, m) << "\n";); SASSERT(m.is_true(val) || m.is_false(val)); if (!m_bool_visited.is_marked(e)) { fmls.push_back(m.is_true(val) ? e : mk_not(m, e)); } sub.insert(e, val); m_bool_visited.mark(e); found_bool = true; } else if (is_app(e)) { todo.append(to_app(e)->get_num_args(), to_app(e)->get_args()); } else { TRACE("qe", tout << "expression not handled " << mk_pp(e, m) << "\n";); } } if (found_bool) { expr_ref tmp(m); sub(fml, tmp); expr_ref val = eval(tmp); SASSERT(m.is_true(val) || m.is_false(val)); fmls.push_back(m.is_true(val) ? tmp : mk_not(m, tmp)); } return found_bool; } void project_bools(model& mdl, app_ref_vector& vars, expr_ref_vector& fmls) { expr_safe_replace sub(m); expr_ref val(m); model_evaluator eval(mdl, m_params); eval.set_model_completion(true); unsigned j = 0; for (unsigned i = 0; i < vars.size(); ++i) { app* var = vars[i].get(); if (m.is_bool(var)) { sub.insert(var, eval(var)); } else { vars[j++] = var; } } if (j == vars.size()) { return; } vars.shrink(j); j = 0; for (unsigned i = 0; i < fmls.size(); ++i) { expr* fml = fmls[i].get(); sub(fml, val); m_rw(val); if (!m.is_true(val)) { TRACE("qe", tout << mk_pp(fml, m) << " -> " << val << "\n";); fmls[j++] = val; } } fmls.shrink(j); } void subst_vars(model_evaluator& eval, app_ref_vector const& vars, expr_ref& fml) { expr_safe_replace sub (m); for (app * v : vars) { sub.insert(v, eval(v)); } sub(fml); } struct index_term_finder { ast_manager& m; array_util m_array; app_ref m_var; expr_ref_vector& m_res; index_term_finder (ast_manager &mgr, app* v, expr_ref_vector &res): m(mgr), m_array (m), m_var (v, m), m_res (res) {} void operator() (var *n) {} void operator() (quantifier *n) {} void operator() (app *n) { expr *e1, *e2; if (m_array.is_select (n)) { for (expr * arg : *n) { if (m.get_sort(arg) == m.get_sort(m_var) && arg != m_var) m_res.push_back (arg); } } else if (m.is_eq(n, e1, e2)) { if (e1 == m_var) m_res.push_back(e2); else if (e2 == m_var) m_res.push_back(e1); } } }; bool project_var (model_evaluator& eval, app* var, expr_ref& fml) { expr_ref val = eval(var); TRACE ("mbqi_project_verbose", tout << "MBQI: var: " << mk_pp (var, m) << "\n" << "fml: " << fml << "\n";); expr_ref_vector terms (m); index_term_finder finder (m, var, terms); for_each_expr (finder, fml); TRACE ("mbqi_project_verbose", tout << "terms:\n" << terms;); for (expr * term : terms) { expr_ref tval = eval(term); TRACE ("mbqi_project_verbose", tout << "term: " << mk_pp (term, m) << " tval: " << tval << " val: " << val << "\n";); // -- if the term does not contain an occurrence of var // -- and is in the same equivalence class in the model if (tval == val && !occurs (var, term)) { TRACE ("mbqi_project", tout << "MBQI: replacing " << mk_pp (var, m) << " with " << mk_pp (term, m) << "\n";); expr_safe_replace sub(m); sub.insert (var, term); sub (fml); return true; } } TRACE ("mbqi_project", tout << "MBQI: failed to eliminate " << mk_pp (var, m) << " from " << fml << "\n";); return false; } void project_vars (model &M, app_ref_vector &vars, expr_ref &fml) { model_evaluator eval(M); eval.set_model_completion(false); // -- evaluate to initialize eval cache (void) eval (fml); unsigned j = 0; for (app * v : vars) { if (!project_var (eval, v, fml)) { vars[j++] = v; } } vars.shrink(j); } public: opt::inf_eps maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& ge, expr_ref& gt) { arith_project_plugin arith(m); return arith.maximize(fmls, mdl, t, ge, gt); } void extract_literals(model& model, expr_ref_vector& fmls) { expr_ref val(m); model_evaluator eval(model); TRACE("qe", tout << fmls << "\n";); for (unsigned i = 0; i < fmls.size(); ++i) { expr* fml = fmls[i].get(), *nfml, *f1, *f2, *f3; SASSERT(m.is_bool(fml)); if (m.is_not(fml, nfml) && m.is_distinct(nfml)) { fmls[i] = project_plugin::pick_equality(m, model, nfml); --i; } else if (m.is_or(fml)) { for (unsigned j = 0; j < to_app(fml)->get_num_args(); ++j) { val = eval(to_app(fml)->get_arg(j)); if (m.is_true(val)) { fmls[i] = to_app(fml)->get_arg(j); --i; break; } } } else if (m.is_and(fml)) { fmls.append(to_app(fml)->get_num_args(), to_app(fml)->get_args()); project_plugin::erase(fmls, i); } else if (m.is_iff(fml, f1, f2) || (m.is_not(fml, nfml) && m.is_xor(nfml, f1, f2))) { val = eval(f1); if (m.is_false(val)) { f1 = mk_not(m, f1); f2 = mk_not(m, f2); } fmls[i] = f1; project_plugin::push_back(fmls, f2); --i; } else if (m.is_implies(fml, f1, f2)) { val = eval(f2); if (m.is_true(val)) { fmls[i] = f2; } else { fmls[i] = mk_not(m, f1); } --i; } else if (m.is_ite(fml, f1, f2, f3)) { val = eval(f1); if (m.is_true(val)) { project_plugin::push_back(fmls, f1); project_plugin::push_back(fmls, f2); } else { project_plugin::push_back(fmls, mk_not(m, f1)); project_plugin::push_back(fmls, f3); } project_plugin::erase(fmls, i); } else if (m.is_not(fml, nfml) && m.is_not(nfml, nfml)) { project_plugin::push_back(fmls, nfml); project_plugin::erase(fmls, i); } else if (m.is_not(fml, nfml) && m.is_and(nfml)) { for (unsigned j = 0; j < to_app(nfml)->get_num_args(); ++j) { val = eval(to_app(nfml)->get_arg(j)); if (m.is_false(val)) { fmls[i] = mk_not(m, to_app(nfml)->get_arg(j)); --i; break; } } } else if (m.is_not(fml, nfml) && m.is_or(nfml)) { for (unsigned j = 0; j < to_app(nfml)->get_num_args(); ++j) { project_plugin::push_back(fmls, mk_not(m, to_app(nfml)->get_arg(j))); } project_plugin::erase(fmls, i); } else if ((m.is_not(fml, nfml) && m.is_iff(nfml, f1, f2)) || m.is_xor(fml, f1, f2)) { val = eval(f1); if (m.is_true(val)) { f2 = mk_not(m, f2); } else { f1 = mk_not(m, f1); } project_plugin::push_back(fmls, f1); project_plugin::push_back(fmls, f2); project_plugin::erase(fmls, i); } else if (m.is_not(fml, nfml) && m.is_implies(nfml, f1, f2)) { project_plugin::push_back(fmls, f1); project_plugin::push_back(fmls, mk_not(m, f2)); project_plugin::erase(fmls, i); } else if (m.is_not(fml, nfml) && m.is_ite(nfml, f1, f2, f3)) { val = eval(f1); if (m.is_true(val)) { project_plugin::push_back(fmls, f1); project_plugin::push_back(fmls, mk_not(m, f2)); } else { project_plugin::push_back(fmls, mk_not(m, f1)); project_plugin::push_back(fmls, mk_not(m, f3)); } project_plugin::erase(fmls, i); } else if (m.is_not(fml, nfml)) { if (extract_bools(eval, fmls, nfml)) { project_plugin::erase(fmls, i); } } else { if (extract_bools(eval, fmls, fml)) { project_plugin::erase(fmls, i); } // TBD other Boolean operations. } } TRACE("qe", tout << fmls << "\n";); m_bool_visited.reset(); } impl(ast_manager& m, params_ref const& p):m(m), m_params(p), m_rw(m) { add_plugin(alloc(arith_project_plugin, m)); add_plugin(alloc(datatype_project_plugin, m)); add_plugin(alloc(array_project_plugin, m)); updt_params(p); } ~impl() { std::for_each(m_plugins.begin(), m_plugins.end(), delete_proc()); } void updt_params(params_ref const& p) { m_params.append(p); m_reduce_all_selects = m_params.get_bool("reduce_all_selects", false); m_dont_sub = m_params.get_bool("dont_sub", false); } void preprocess_solve(model& model, app_ref_vector& vars, expr_ref_vector& fmls) { extract_literals(model, fmls); bool change = true; while (change && !vars.empty()) { change = solve(model, vars, fmls); for (auto* p : m_plugins) { if (p && p->solve(model, vars, fmls)) { change = true; } } } } bool validate_model(model& model, expr_ref_vector const& fmls) { expr_ref val(m); model_evaluator eval(model); for (expr * f : fmls) { VERIFY(!model.is_false(f)); } return true; } void operator()(bool force_elim, app_ref_vector& vars, model& model, expr_ref_vector& fmls) { SASSERT(validate_model(model, fmls)); expr_ref val(m), tmp(m); app_ref var(m); expr_ref_vector unused_fmls(m); bool progress = true; preprocess_solve(model, vars, fmls); filter_variables(model, vars, fmls, unused_fmls); project_bools(model, vars, fmls); while (progress && !vars.empty() && !fmls.empty()) { app_ref_vector new_vars(m); progress = false; for (project_plugin * p : m_plugins) { if (p) { (*p)(model, vars, fmls); } } while (!vars.empty() && !fmls.empty()) { var = vars.back(); vars.pop_back(); project_plugin* p = get_plugin(var); if (p && (*p)(model, var, vars, fmls)) { progress = true; } else { new_vars.push_back(var); } } if (!progress && !new_vars.empty() && !fmls.empty() && force_elim) { var = new_vars.back(); new_vars.pop_back(); expr_safe_replace sub(m); val = model(var); sub.insert(var, val); for (unsigned i = 0; i < fmls.size(); ++i) { sub(fmls[i].get(), tmp); m_rw(tmp); if (m.is_true(tmp)) { project_plugin::erase(fmls, i); } else { fmls[i] = tmp; } } progress = true; } vars.append(new_vars); if (progress) { preprocess_solve(model, vars, fmls); } } if (fmls.empty()) { vars.reset(); } fmls.append(unused_fmls); SASSERT(validate_model(model, fmls)); TRACE("qe", tout << vars << " " << fmls << "\n";); } void do_qe_lite(app_ref_vector& vars, expr_ref& fml) { qe_lite qe(m, m_params, false); qe (vars, fml); m_rw (fml); TRACE ("qe", tout << "After qe_lite:\n" << fml << "\n" << "Vars: " << vars << "\n";); SASSERT (!m.is_false (fml)); } void do_qe_bool(model& mdl, app_ref_vector& vars, expr_ref& fml) { expr_ref_vector fmls(m); fmls.push_back(fml); project_bools(mdl, vars, fmls); fml = mk_and(fmls); } void spacer(app_ref_vector& vars, model& mdl, expr_ref& fml) { TRACE ("qe", tout << "Before projection:\n" << fml << "\n" << "Vars: " << vars << "\n";); model_evaluator eval(mdl, m_params); eval.set_model_completion(true); app_ref_vector other_vars (m); app_ref_vector array_vars (m); array_util arr_u (m); arith_util ari_u (m); flatten_and(fml); while (!vars.empty()) { do_qe_lite(vars, fml); do_qe_bool(mdl, vars, fml); // sort out vars into bools, arith (int/real), and arrays for (app* v : vars) { if (arr_u.is_array(v)) { array_vars.push_back (v); } else { other_vars.push_back (v); } } TRACE ("qe", tout << "Array vars: " << array_vars << "\n";); vars.reset (); // project arrays qe::array_project_plugin ap(m); ap(mdl, array_vars, fml, vars, m_reduce_all_selects); SASSERT (array_vars.empty ()); m_rw (fml); SASSERT (!m.is_false (fml)); TRACE ("qe", tout << "extended model:\n" << mdl; tout << "Vars: " << vars << "\n"; ); } // project reals, ints and other variables. if (!other_vars.empty ()) { TRACE ("qe", tout << "Other vars: " << other_vars << "\n" << mdl;); expr_ref_vector fmls(m); flatten_and (fml, fmls); (*this)(false, other_vars, mdl, fmls); fml = mk_and (fmls); m_rw(fml); TRACE ("qe", tout << "Projected other vars:\n" << fml << "\n"; tout << "Remaining other vars:\n" << other_vars << "\n";); SASSERT (!m.is_false (fml)); } if (!other_vars.empty ()) { project_vars (mdl, other_vars, fml); m_rw(fml); } // substitute any remaining other vars if (!m_dont_sub && !other_vars.empty ()) { subst_vars (eval, other_vars, fml); TRACE ("qe", tout << "After substituting remaining other vars:\n" << fml << "\n";); // an extra round of simplification because subst_vars is not simplifying m_rw(fml); other_vars.reset(); } SASSERT(!eval.is_false(fml)); vars.reset (); vars.append(other_vars); } }; mbp::mbp(ast_manager& m, params_ref const& p) { scoped_no_proof _sp (m); m_impl = alloc(impl, m, p); } mbp::~mbp() { dealloc(m_impl); } void mbp::updt_params(params_ref const& p) { m_impl->updt_params(p); } void mbp::get_param_descrs(param_descrs & r) { r.insert("reduce_all_selects", CPK_BOOL, "(default: false) reduce selects"); r.insert("dont_sub", CPK_BOOL, "(default: false) disable substitution of values for free variables"); } void mbp::operator()(bool force_elim, app_ref_vector& vars, model& mdl, expr_ref_vector& fmls) { scoped_no_proof _sp (fmls.get_manager()); (*m_impl)(force_elim, vars, mdl, fmls); } void mbp::spacer(app_ref_vector& vars, model& mdl, expr_ref& fml) { scoped_no_proof _sp (fml.get_manager()); m_impl->spacer(vars, mdl, fml); } void mbp::solve(model& model, app_ref_vector& vars, expr_ref_vector& fmls) { scoped_no_proof _sp (fmls.get_manager()); m_impl->preprocess_solve(model, vars, fmls); } void mbp::extract_literals(model& model, expr_ref_vector& lits) { scoped_no_proof _sp (lits.get_manager()); m_impl->extract_literals(model, lits); } opt::inf_eps mbp::maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& ge, expr_ref& gt) { scoped_no_proof _sp (fmls.get_manager()); return m_impl->maximize(fmls, mdl, t, ge, gt); } z3-z3-4.8.7/src/qe/qe_mbp.h000066400000000000000000000070121356505360400152370ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: qe_mbp.h Abstract: Model-based projection utilities Author: Nikolaj Bjorner (nbjorner) 2015-5-28 Revision History: --*/ #ifndef __QE_MBP_H__ #define __QE_MBP_H__ #include "ast/ast.h" #include "util/params.h" #include "model/model.h" #include "math/simplex/model_based_opt.h" namespace qe { struct cant_project {}; struct def { expr_ref var, term; def(const expr_ref& v, expr_ref& t): var(v), term(t) {} }; class project_plugin { public: virtual ~project_plugin() {} virtual bool operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) = 0; /** \brief partial solver. */ virtual bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) = 0; virtual family_id get_family_id() = 0; virtual void operator()(model& model, app_ref_vector& vars, expr_ref_vector& lits) { }; /** \brief project vars modulo model, return set of definitions for eliminated variables. - vars in/out: returns variables that were not eliminated - lits in/out: returns projected literals - returns set of definitions (TBD: in triangular form, the last definition can be substituted into definitions that come before) */ virtual vector project(model& model, app_ref_vector& vars, expr_ref_vector& lits) = 0; /** \brief model based saturation. Saturates theory axioms to equi-satisfiable literals over EUF, such that 'shared' are not retained for EUF. */ virtual void saturate(model& model, func_decl_ref_vector const& shared, expr_ref_vector& lits) = 0; static expr_ref pick_equality(ast_manager& m, model& model, expr* t); static void erase(expr_ref_vector& lits, unsigned& i); static void push_back(expr_ref_vector& lits, expr* lit); static void mark_rec(expr_mark& visited, expr* e); static void mark_rec(expr_mark& visited, expr_ref_vector const& es); }; class mbp { class impl; impl * m_impl; public: mbp(ast_manager& m, params_ref const& p = params_ref()); ~mbp(); void updt_params(params_ref const& p); static void get_param_descrs(param_descrs & r); /** \brief Apply model-based qe on constants provided as vector of variables. Return the updated formula and updated set of variables that were not eliminated. */ void operator()(bool force_elim, app_ref_vector& vars, model& mdl, expr_ref_vector& fmls); /** \brief Solve as many variables as possible using "cheap" quantifier elimination" */ void solve(model& model, app_ref_vector& vars, expr_ref_vector& lits); /** \brief Extract literals from formulas based on model. */ void extract_literals(model& model, expr_ref_vector& lits); /** \brief Maximize objective t under current model for constraints in fmls. */ opt::inf_eps maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& ge, expr_ref& gt); /** \brief Apply spacer friendly MBP. Use parameters to control behavior. - reduce_all_selects (false) - dont_sub (false) */ void spacer(app_ref_vector& vars, model& mdl, expr_ref& fml); }; } #endif z3-z3-4.8.7/src/qe/qe_sat_tactic.cpp000066400000000000000000000636631356505360400171500ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qe_sat_tactic.cpp Abstract: Procedure for quantifier satisfiability using quantifier projection. Based on generalizations by Bjorner & Monniaux (see tvm\papers\z3qe\altqe.tex) Author: Nikolaj Bjorner (nbjorner) 2012-02-24 Revision History: --*/ #include "qe/qe_sat_tactic.h" #include "ast/rewriter/quant_hoist.h" #include "ast/ast_pp.h" #include "smt/smt_kernel.h" #include "qe/qe.h" #include "model/model_v2_pp.h" #include "ast/rewriter/expr_replacer.h" #include "ast/rewriter/th_rewriter.h" #include "smt/expr_context_simplifier.h" // plugin registration. // solver specific projection operators. // connect goals to tactic namespace qe { class is_relevant_default : public i_expr_pred { public: bool operator()(expr* e) override { return true; } }; class mk_atom_default : public i_nnf_atom { public: void operator()(expr* e, bool pol, expr_ref& result) override { if (pol) result = e; else result = result.get_manager().mk_not(e); } }; class sat_tactic : public tactic { // forall x . not forall y . not forall z . not forall u . fml. ast_manager& m; expr_ref m_false; smt_params m_fparams; params_ref m_params; unsigned m_extrapolate_strategy_param; bool m_projection_mode_param; bool m_strong_context_simplify_param; bool m_ctx_simplify_local_param; vector m_vars; ptr_vector m_solvers; vector m_fparamv; smt::kernel m_solver; expr_ref m_fml; expr_ref_vector m_Ms; expr_ref_vector m_assignments; is_relevant_default m_is_relevant; mk_atom_default m_mk_atom; th_rewriter m_rewriter; simplify_rewriter_star m_qe_rw; expr_strong_context_simplifier m_ctx_rewriter; class solver_context : public i_solver_context { ast_manager& m; sat_tactic& m_super; smt::kernel& m_solver; atom_set m_pos; atom_set m_neg; app_ref_vector m_vars; expr_ref m_fml; ptr_vector m_contains_app; bool m_projection_mode_param; public: solver_context(sat_tactic& s, unsigned idx): m(s.m), m_super(s), m_solver(*s.m_solvers[idx+1]), m_vars(m), m_fml(m), m_projection_mode_param(true) {} ~solver_context() override { std::for_each(m_contains_app.begin(), m_contains_app.end(), delete_proc()); } void init(expr* fml, unsigned idx) { m_fml = fml; for (unsigned j = 0; j < m_super.vars(idx).size(); ++j) { add_var(m_super.vars(idx)[j]); } get_nnf(m_fml, get_is_relevant(), get_mk_atom(), m_pos, m_neg); } void set_projection_mode(bool p) { m_projection_mode_param = p; } ast_manager& get_manager() override { return m; } expr* fml() { return m_fml; } // set of atoms in current formula. atom_set const& pos_atoms() const override { return m_pos; } atom_set const& neg_atoms() const override { return m_neg; } // Access current set of variables to solve unsigned get_num_vars() const override { return m_vars.size(); } app* get_var(unsigned idx) const override { return m_vars[idx]; } app_ref_vector const& get_vars() const override { return m_vars; } bool is_var(expr* e, unsigned& idx) const override { for (unsigned i = 0; i < m_vars.size(); ++i) { if (e == m_vars[i]) return (idx = i, true); } return false; } contains_app& contains(unsigned idx) override { return *m_contains_app[idx]; } // callback to replace variable at index 'idx' with definition 'def' and updated formula 'fml' void elim_var(unsigned idx, expr* fml, expr* def) override { m_fml = fml; m_pos.reset(); m_neg.reset(); get_nnf(m_fml, get_is_relevant(), get_mk_atom(), m_pos, m_neg); m_vars.erase(idx); dealloc(m_contains_app[idx]); m_contains_app.erase(m_contains_app.c_ptr() + idx); } // callback to add new variable to branch. void add_var(app* x) override { m_vars.push_back(x); m_contains_app.push_back(alloc(contains_app, m, x)); } // callback to add constraints in branch. void add_constraint(bool use_var, expr* l1 = nullptr, expr* l2 = nullptr, expr* l3 = nullptr) override { ptr_buffer args; if (l1) args.push_back(l1); if (l2) args.push_back(l2); if (l3) args.push_back(l3); expr_ref cnstr(m.mk_or(args.size(), args.c_ptr()), m); m_solver.assert_expr(cnstr); TRACE("qe", tout << "add_constraint " << mk_pp(cnstr,m) << "\n";); } // eliminate finite domain variable 'var' from fml. void blast_or(app* var, expr_ref& fml) override { expr_ref result(m); expr_quant_elim qelim(m, m_super.m_fparams); qe::mk_exists(1, &var, fml); qelim(m.mk_true(), fml, result); fml = result; TRACE("qe", tout << mk_pp(var, m) << " " << mk_pp(fml, m) << "\n";); } void project_var_partial(unsigned i) { app* x = get_var(i); m_super.check_success(has_plugin(x)); qe_solver_plugin& p = plugin(m.get_sort(x)->get_family_id()); model_ref model; m_solver.get_model(model); m_super.check_success(p.project(contains(i), model, m_fml)); m_super.m_rewriter(m_fml); TRACE("qe", model_v2_pp(tout, *model); tout << "\n"; tout << mk_pp(m_fml, m) << "\n";); elim_var(i, m_fml, nullptr); } void project_var_full(unsigned i) { expr_ref result(m); app* x = get_var(i); expr_quant_elim qelim(m, m_super.m_fparams); qe::mk_exists(1, &x, m_fml); qelim(m.mk_true(), m_fml, result); m_fml = result; m_super.m_rewriter(m_fml); TRACE("qe", tout << mk_pp(m_fml, m) << "\n";); elim_var(i, m_fml, nullptr); } void project_var(unsigned i) { if (m_projection_mode_param) { project_var_full(i); } else { project_var_partial(i); } } }; public: sat_tactic(ast_manager& m, params_ref const& p = params_ref()): m(m), m_false(m.mk_false(), m), m_params(p), m_extrapolate_strategy_param(0), m_projection_mode_param(true), m_strong_context_simplify_param(true), m_ctx_simplify_local_param(false), m_solver(m, m_fparams), m_fml(m), m_Ms(m), m_assignments(m), m_rewriter(m), m_qe_rw(m), m_ctx_rewriter(m_fparams, m) { m_fparams.m_model = true; } tactic * translate(ast_manager & m) override { return alloc(sat_tactic, m); } ~sat_tactic() override { reset(); } void operator()(goal_ref const& goal, goal_ref_buffer& result) override { try { checkpoint(); reset(); ptr_vector fmls; goal->get_formulas(fmls); m_fml = m.mk_and(fmls.size(), fmls.c_ptr()); TRACE("qe", tout << "input: " << mk_pp(m_fml,m) << "\n";); expr_ref tmp(m); m_qe_rw(m_fml, tmp); m_fml = tmp; TRACE("qe", tout << "reduced: " << mk_pp(m_fml,m) << "\n";); skolemize_existential_prefix(); extract_alt_form(m_fml); model_ref model; expr_ref res = qt(0, m.mk_true(), model); goal->inc_depth(); if (m.is_false(res)) { goal->assert_expr(res); } else { goal->reset(); // equi-satisfiable. What to do with model? goal->add(model2model_converter(&*model)); } result.push_back(goal.get()); } catch (rewriter_exception & ex) { throw tactic_exception(ex.msg()); } } void collect_statistics(statistics & st) const override { for (auto const * s : m_solvers) { s->collect_statistics(st); } m_solver.collect_statistics(st); m_ctx_rewriter.collect_statistics(st); } void reset_statistics() override { for (auto * s : m_solvers) { s->reset_statistics(); } m_solver.reset_statistics(); m_ctx_rewriter.reset_statistics(); } void cleanup() override {} void updt_params(params_ref const & p) override { m_extrapolate_strategy_param = p.get_uint("extrapolate_strategy", m_extrapolate_strategy_param); m_projection_mode_param = p.get_bool("projection_mode", m_projection_mode_param); m_strong_context_simplify_param = p.get_bool("strong_context_simplify", m_strong_context_simplify_param); m_ctx_simplify_local_param = p.get_bool("strong_context_simplify_local", m_ctx_simplify_local_param); m_fparams.updt_params(p); m_qe_rw.updt_params(p); } void collect_param_descrs(param_descrs & r) override { r.insert("extrapolate_strategy",CPK_UINT, "(default: 0 trivial extrapolation) 1 - nnf strengthening 2 - smt-test 3 - nnf_weakening"); r.insert("projection_mode", CPK_BOOL, "(default: true - full) false - partial quantifier instantiation"); r.insert("strong_context_simplify", CPK_BOOL, "(default: true) use strong context simplifier on result of quantifier elimination"); r.insert("strong_context_simplify_local", CPK_BOOL, "(default: false) use strong context simplifier locally on the new formula only"); } private: unsigned num_alternations() const { return m_vars.size(); } void init_Ms() { for (unsigned i = 0; i <= num_alternations(); ++i) { m_fparamv.push_back(m_fparams); } for (unsigned i = 0; i <= num_alternations(); ++i) { m_Ms.push_back(m.mk_true()); m_solvers.push_back(alloc(smt::kernel, m, m_fparamv[i], m_params)); } m_Ms[m_Ms.size()-1] = m_fml; m_solvers.back()->assert_expr(m_fml); } expr* M(unsigned i) { return m_Ms[i].get(); } app_ref_vector const& vars(unsigned i) { return m_vars[i]; } smt::kernel& solver(unsigned i) { return *m_solvers[i]; } void reset() override { for (unsigned i = 0; i < m_solvers.size(); ++i) { dealloc(m_solvers[i]); } m_fml = nullptr; m_Ms.reset(); m_fparamv.reset(); m_solvers.reset(); m_vars.reset(); } void skolemize_existential_prefix() { quantifier_hoister hoist(m); expr_ref result(m); app_ref_vector vars(m); hoist.pull_exists(m_fml, vars, result); m_fml = result; } // // fa x ex y fa z . phi // fa x ! fa y ! fa z ! (!phi) // void extract_alt_form(expr* fml) { quantifier_hoister hoist(m); expr_ref result(m); bool is_fa = false; unsigned parity = 0; m_fml = fml; while (true) { app_ref_vector vars(m); hoist(m_fml, vars, is_fa, result); if (vars.empty()) { break; } SASSERT(((parity & 0x1) == 0) == is_fa); ++parity; TRACE("qe", tout << "Hoist " << (is_fa?"fa":"ex") << "\n"; for (unsigned i = 0; i < vars.size(); ++i) { tout << mk_pp(vars[i].get(), m) << " "; } tout << "\n"; tout << mk_pp(result, m) << "\n";); m_vars.push_back(vars); m_fml = result; } // // negate formula if the last quantifier is universal. // so that normal form fa x ! fa y ! fa z ! psi // is obtained. // if ((parity & 0x1) == 1) { m_fml = m.mk_not(m_fml); } init_Ms(); checkpoint(); } /** \brief Main quantifier test algorithm loop. */ expr_ref qt(unsigned i, expr* ctx, model_ref& model) { model_ref model1; while (true) { IF_VERBOSE(1, verbose_stream() << "(qt " << i << ")\n";); TRACE("qe", tout << i << " " << mk_pp(ctx, m) << "\n"; display(tout);); checkpoint(); if (is_sat(i, ctx, model)) { expr_ref ctxM = extrapolate(i, ctx, M(i)); if (i == num_alternations()) { return ctxM; } expr_ref res = qt(i+1, ctxM, model1); if (m.is_false(res)) { return ctxM; } project(i, res); } else { return m_false; } } UNREACHABLE(); return expr_ref(m); } /** \brief compute extrapolant Assume A & B is sat. Compute C, such that 1. A & C is sat, 2. not B & C is unsat. (C strengthens B and is still satisfiable with A). */ expr_ref extrapolate(unsigned i, expr* A, expr* B) { switch(m_extrapolate_strategy_param) { case 0: return trivial_extrapolate(i, A, B); case 1: return nnf_strengthening_extrapolate(i, A, B); case 2: return smt_test_extrapolate(i, A, B); case 3: return nnf_weakening_extrapolate(i, A, B); default: return trivial_extrapolate(i, A, B); } } expr_ref trivial_extrapolate(unsigned i, expr* A, expr* B) { return expr_ref(B, m); } expr_ref conjunction_extrapolate(unsigned i, expr* A, expr* B) { return expr_ref(m.mk_and(A, B), m); } /** Set C = nnf(B), That is, C is the NNF version of B. For each literal in C in order, replace the literal by False and check the conditions for extrapolation: 1. not B & C is unsat, 2. A & C is sat. The first check holds by construction, so it is redundant. The second is not redundant. Instead of replacing literals in an NNF formula, one simply asserts the negation of that literal. */ expr_ref nnf_strengthening_extrapolate(unsigned i, expr* A, expr* B) { expr_ref Bnnf(B, m); atom_set pos, neg; get_nnf(Bnnf, m_is_relevant, m_mk_atom, pos, neg); expr_substitution sub(m); remove_duplicates(pos, neg); // Assumption: B is already asserted in solver[i]. smt::kernel& solver = *m_solvers[i]; solver.push(); solver.assert_expr(A); nnf_strengthen(solver, pos, m.mk_false(), sub); nnf_strengthen(solver, neg, m.mk_true(), sub); solver.pop(1); scoped_ptr replace = mk_default_expr_replacer(m); replace->set_substitution(&sub); (*replace)(Bnnf); m_rewriter(Bnnf); TRACE("qe", tout << "A: " << mk_pp(A, m) << "\n"; tout << "B: " << mk_pp(B, m) << "\n"; tout << "B': " << mk_pp(Bnnf.get(), m) << "\n"; // solver.display(tout); ); DEBUG_CODE( solver.push(); solver.assert_expr(m.mk_not(B)); solver.assert_expr(Bnnf); lbool is_sat = solver.check(); TRACE("qe", tout << "is sat: " << is_sat << "\n";); SASSERT(is_sat == l_false); solver.pop(1);); DEBUG_CODE( solver.push(); solver.assert_expr(A); solver.assert_expr(Bnnf); lbool is_sat = solver.check(); TRACE("qe", tout << "is sat: " << is_sat << "\n";); SASSERT(is_sat == l_true); solver.pop(1);); return Bnnf; } void nnf_strengthen(smt::kernel& solver, atom_set& atoms, expr* value, expr_substitution& sub) { atom_set::iterator it = atoms.begin(), end = atoms.end(); for (; it != end; ++it) { solver.push(); solver.assert_expr(m.mk_iff(*it, value)); lbool is_sat = solver.check(); solver.pop(1); if (is_sat == l_true) { TRACE("qe", tout << "consistent with: " << mk_pp(*it, m) << " " << mk_pp(value, m) << "\n";); sub.insert(*it, value); solver.assert_expr(m.mk_iff(*it, value)); } checkpoint(); } } void remove_duplicates(atom_set& pos, atom_set& neg) { ptr_vector to_delete; atom_set::iterator it = pos.begin(), end = pos.end(); for (; it != end; ++it) { if (neg.contains(*it)) { to_delete.push_back(*it); } } for (unsigned j = 0; j < to_delete.size(); ++j) { pos.remove(to_delete[j]); neg.remove(to_delete[j]); } } /** Set C = nnf(B), That is, C is the NNF version of B. For each literal in C in order, replace the literal by True and check the conditions for extrapolation 1. not B & C is unsat, 2. A & C is sat. The second holds by construction and is redundant. The first is not redundant. */ expr_ref nnf_weakening_extrapolate(unsigned i, expr* A, expr* B) { expr_ref Bnnf(B, m); atom_set pos, neg; get_nnf(Bnnf, m_is_relevant, m_mk_atom, pos, neg); remove_duplicates(pos, neg); expr_substitution sub(m); m_solver.push(); m_solver.assert_expr(A); m_solver.assert_expr(m.mk_not(B)); nnf_weaken(m_solver, Bnnf, pos, m.mk_true(), sub); nnf_weaken(m_solver, Bnnf, neg, m.mk_false(), sub); scoped_ptr replace = mk_default_expr_replacer(m); replace->set_substitution(&sub); (*replace)(Bnnf); m_rewriter(Bnnf); m_solver.pop(1); return Bnnf; } void nnf_weaken(smt::kernel& solver, expr_ref& B, atom_set& atoms, expr* value, expr_substitution& sub) { atom_set::iterator it = atoms.begin(), end = atoms.end(); for (; it != end; ++it) { solver.push(); expr_ref fml = B; mk_default_expr_replacer(m)->apply_substitution(*it, value, fml); solver.assert_expr(fml); if (solver.check() == l_false) { sub.insert(*it, value); B = fml; } solver.pop(1); checkpoint(); } } /** Use the model for A & B to extrapolate. Initially, C is conjunction of literals from B that are in model of A & B. The model is a conjunction of literals. Let us denote this set $C$. We see that the conditions for extrapolation are satisfied. Furthermore, C can be generalized by removing literals from C as long as !B & A & C is unsatisfiable. */ expr_ref smt_test_extrapolate(unsigned i, expr* A, expr* B) { expr_ref_vector proxies(m), core(m); obj_map proxy_map; checkpoint(); m_solver.push(); m_solver.assert_expr(m.mk_not(B)); for (unsigned i = 0; i < m_assignments.size(); ++i) { proxies.push_back(m.mk_fresh_const("proxy",m.mk_bool_sort())); proxy_map.insert(proxies.back(), m_assignments[i].get()); m_solver.assert_expr(m.mk_iff(proxies.back(), m_assignments[i].get())); TRACE("qe", tout << "assignment: " << mk_pp(m_assignments[i].get(), m) << "\n";); } VERIFY(l_false == m_solver.check(proxies.size(), proxies.c_ptr())); unsigned core_size = m_solver.get_unsat_core_size(); for (unsigned i = 0; i < core_size; ++i) { core.push_back(proxy_map.find(m_solver.get_unsat_core_expr(i))); } expr_ref result(m.mk_and(core.size(), core.c_ptr()), m); TRACE("qe", tout << "core: " << mk_pp(result, m) << "\n"; tout << mk_pp(A, m) << "\n"; tout << mk_pp(B, m) << "\n";); m_solver.pop(1); return result; } /** \brief project vars(idx) from fml relative to M(idx). */ void project(unsigned idx, expr* _fml) { SASSERT(idx < num_alternations()); expr_ref fml(_fml, m); if (m_strong_context_simplify_param && m_ctx_simplify_local_param) { m_ctx_rewriter.push(); m_ctx_rewriter.assert_expr(M(idx)); m_ctx_rewriter(fml); m_ctx_rewriter.pop(); TRACE("qe", tout << mk_pp(_fml, m) << "\n-- context simplify -->\n" << mk_pp(fml, m) << "\n";); } solver_context ctx(*this, idx); smt_params fparams(m_fparams); ctx.add_plugin(mk_arith_plugin(ctx, false, fparams)); ctx.add_plugin(mk_bool_plugin(ctx)); ctx.add_plugin(mk_bv_plugin(ctx)); ctx.init(fml, idx); ctx.set_projection_mode(m_projection_mode_param); m_solvers[idx+1]->push(); while (ctx.get_num_vars() > 0) { VERIFY(l_true == m_solvers[idx+1]->check()); ctx.project_var(ctx.get_num_vars()-1); } m_solvers[idx+1]->pop(1); expr_ref not_fml(m.mk_not(ctx.fml()), m); m_rewriter(not_fml); if (m_strong_context_simplify_param && !m_ctx_simplify_local_param) { m_ctx_rewriter.push(); m_ctx_rewriter.assert_expr(M(idx)); m_ctx_rewriter(not_fml); m_ctx_rewriter.pop(); } expr_ref tmp(m.mk_and(M(idx), not_fml), m); m_rewriter(tmp); m_Ms[idx] = tmp; m_solvers[idx]->assert_expr(not_fml); TRACE("qe", tout << mk_pp(fml, m) << "\n--->\n"; tout << mk_pp(tmp, m) << "\n";); } void checkpoint() { if (m.canceled()) { throw tactic_exception(m.limit().get_cancel_msg()); } } void check_success(bool ok) { if (!ok) { TRACE("qe", tout << "check failed\n";); throw tactic_exception(TACTIC_CANCELED_MSG); } } bool is_sat(unsigned i, expr* ctx, model_ref& model) { smt::kernel& solver = *m_solvers[i]; solver.push(); solver.assert_expr(ctx); lbool r = solver.check(); m_assignments.reset(); solver.get_assignments(m_assignments); solver.pop(1); check_success(r != l_undef); if (r == l_true && i == 0) solver.get_model(model); return r == l_true; } void display(std::ostream& out) { bool is_fa = true; for (unsigned i = 0; i < num_alternations(); ++i) { out << (is_fa?"fa ":"ex "); for (unsigned j = 0; j < vars(i).size(); ++j) { out << mk_pp(m_vars[i][j].get(), m) << " "; } out << "\n"; out << mk_pp(m_Ms[i+1].get(), m) << "\n"; is_fa = !is_fa; } } }; tactic * mk_sat_tactic(ast_manager& m, params_ref const& p) { return alloc(sat_tactic, m, p); } }; z3-z3-4.8.7/src/qe/qe_sat_tactic.h000066400000000000000000000010651356505360400166010ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qe_sat_tactic.h Abstract: Procedure for quantifier satisfiability using quantifier elimination. Author: Nikolaj Bjorner (nbjorner) 2012-02-24 Revision History: --*/ #ifndef QE_SAT_H_ #define QE_SAT_H_ #include "tactic/tactic.h" namespace qe { tactic * mk_sat_tactic(ast_manager& m, params_ref const& p = params_ref()); }; /* ADD_TACTIC("qe-sat", "check satisfiability of quantified formulas using quantifier elimination.", "qe::mk_sat_tactic(m, p)") */ #endif z3-z3-4.8.7/src/qe/qe_solve_plugin.cpp000066400000000000000000000357521356505360400175360ustar00rootroot00000000000000/** Copyright (c) 2018 Microsoft Corporation Module Name: qe_solve.cpp Abstract: Light-weight variable solving plugin model for qe-lite and term_graph. Author: Nikolaj Bjorner (nbjorner), Arie Gurfinkel 2018-6-8 Revision History: --*/ #include "ast/arith_decl_plugin.h" #include "ast/bv_decl_plugin.h" #include "ast/datatype_decl_plugin.h" #include "ast/ast_util.h" #include "ast/ast_pp.h" #include "qe/qe_solve_plugin.h" namespace qe { expr_ref solve_plugin::operator()(expr* lit) { if (m.is_not(lit, lit)) return solve(lit, false); else return solve(lit, true); } class arith_solve_plugin : public solve_plugin { arith_util a; public: arith_solve_plugin(ast_manager& m, is_variable_proc& is_var): solve_plugin(m, m.get_family_id("arith"), is_var), a(m) {} typedef std::pair signed_expr; /** *\brief * return r * (sum_{(sign,e) \in exprs} sign * e) */ expr_ref mk_term(bool is_int, rational const& r, bool sign, svector const& exprs) { expr_ref_vector result(m); for (auto const& kv : exprs) { bool sign2 = kv.first; expr* e = kv.second; rational r2(r); if (sign == sign2) { r2.neg(); } if (!r2.is_one()) { result.push_back(a.mk_mul(a.mk_numeral(r2, is_int), e)); } else { result.push_back(e); } } return a.mk_add_simplify(result); } /** * \brief return true if lhs = rhs can be solved as v = t, where v is a variable. */ bool solve(expr* lhs, expr* rhs, expr_ref& v, expr_ref& t) { if (!a.is_int(lhs) && !a.is_real(rhs)) { return false; } rational a_val; bool is_int = a.is_int(lhs); svector todo, done; todo.push_back(std::make_pair(true, lhs)); todo.push_back(std::make_pair(false, rhs)); while (!todo.empty()) { expr* e = todo.back().second; bool sign = todo.back().first; todo.pop_back(); if (a.is_add(e)) { for (expr* arg : *to_app(e)) { todo.push_back(std::make_pair(sign, arg)); } } else if (a.is_sub(e)) { app* sub = to_app(e); todo.push_back(std::make_pair(sign, sub->get_arg(0))); for (unsigned i = 1; i < sub->get_num_args(); ++i) { todo.push_back(std::make_pair(!sign, sub->get_arg(i))); } } else if (a.is_uminus(e)) { todo.push_back(std::make_pair(!sign, to_app(e)->get_arg(0))); } else if (is_invertible_mul(is_int, e, a_val)) { done.append(todo); v = e; a_val = rational(1)/a_val; t = mk_term(is_int, a_val, sign, done); TRACE("qe", tout << mk_pp(lhs, m) << " " << mk_pp(rhs, m) << " " << mk_pp(e, m) << " := " << t << "\n";); return true; } else { done.push_back(std::make_pair(sign, e)); } } return false; } // is x a constant and can we safely divide by x to solve for a variable? bool is_invertible_const(bool is_int, expr* x, rational& a_val) { expr* y; if (a.is_uminus(x, y) && is_invertible_const(is_int, y, a_val)) { a_val.neg(); return true; } else if (a.is_numeral(x, a_val) && !a_val.is_zero()) { if (!is_int || a_val.is_one() || a_val.is_minus_one()) { return true; } } return false; } // is arg of the form a_val * v, where a_val // is a constant that we can safely divide by. bool is_invertible_mul(bool is_int, expr*& arg, rational& a_val) { if (is_variable(arg)) { a_val = rational(1); return true; } expr* x, *y; if (a.is_mul(arg, x, y)) { if (is_variable(x) && is_invertible_const(is_int, y, a_val)) { arg = x; return true; } if (is_variable(y) && is_invertible_const(is_int, x, a_val)) { arg = y; return true; } } return false; } expr_ref mk_eq_core (expr *e1, expr *e2) { expr_ref v(m), t(m); if (solve(e1, e2, v, t)) { return expr_ref(m.mk_eq(v, t), m); } if (a.is_zero(e1)) { std::swap(e1, e2); } // y + -1*x == 0 --> y = x expr *a0 = nullptr, *a1 = nullptr, *x = nullptr; if (a.is_zero(e2) && a.is_add(e1, a0, a1)) { if (a.is_times_minus_one(a1, x)) { e1 = a0; e2 = x; } else if (a.is_times_minus_one(a0, x)) { e1 = a1; e2 = x; } } return expr_ref(m.mk_eq(e1, e2), m); } app* mk_le_zero(expr *arg) { expr *e1, *e2, *e3; if (a.is_add(arg, e1, e2)) { // e1-e2<=0 --> e1<=e2 if (a.is_times_minus_one(e2, e3)) { return a.mk_le(e1, e3); } // -e1+e2<=0 --> e2<=e1 else if (a.is_times_minus_one(e1, e3)) { return a.mk_le(e2, e3); } } return a.mk_le(arg, mk_zero()); } app* mk_ge_zero(expr *arg) { expr *e1, *e2, *e3; if (a.is_add(arg, e1, e2)) { // e1-e2>=0 --> e1>=e2 if (a.is_times_minus_one(e2, e3)) { return a.mk_ge(e1, e3); } // -e1+e2>=0 --> e2>=e1 else if (a.is_times_minus_one(e1, e3)) { return a.mk_ge(e2, e3); } } return a.mk_ge(arg, mk_zero()); } bool mk_le_core (expr *arg1, expr * arg2, expr_ref &result) { // t <= -1 ==> t < 0 ==> ! (t >= 0) rational n; if (a.is_int (arg1) && a.is_minus_one (arg2)) { result = m.mk_not (mk_ge_zero (arg1)); return true; } else if (a.is_zero(arg2)) { result = mk_le_zero(arg1); return true; } else if (a.is_int(arg1) && a.is_numeral(arg2, n) && n < 0) { // t <= n ==> t < n + 1 ==> ! (t >= n + 1) result = m.mk_not(a.mk_ge(arg1, a.mk_numeral(n+1, true))); return true; } return false; } expr * mk_zero () { return a.mk_numeral (rational (0), true); } bool is_one (expr const * n) const { rational val; return a.is_numeral (n, val) && val.is_one (); } bool mk_ge_core (expr * arg1, expr * arg2, expr_ref &result) { // t >= 1 ==> t > 0 ==> ! (t <= 0) rational n; if (a.is_int (arg1) && is_one (arg2)) { result = m.mk_not (mk_le_zero (arg1)); return true; } else if (a.is_zero(arg2)) { result = mk_ge_zero(arg1); return true; } else if (a.is_int(arg1) && a.is_numeral(arg2, n) && n > 0) { // t >= n ==> t > n - 1 ==> ! (t <= n - 1) result = m.mk_not(a.mk_le(arg1, a.mk_numeral(n-1, true))); return true; } return false; } expr_ref solve(expr* atom, bool is_pos) override { expr *e1, *e2; expr_ref res(atom, m); if (m.is_eq (atom, e1, e2)) { expr_ref v(m), t(m); v = e1; t = e2; // -- attempt to solve using arithmetic solve(e1, e2, v, t); // -- normalize equality res = mk_eq_core(v, t); } else if (a.is_le(atom, e1, e2)) { mk_le_core(e1, e2, res); } else if (a.is_ge(atom, e1, e2)) { mk_ge_core(e1, e2, res); } // restore negation if (!is_pos) { res = mk_not(m, res); } return res; } }; class basic_solve_plugin : public solve_plugin { public: basic_solve_plugin(ast_manager& m, is_variable_proc& is_var): solve_plugin(m, m.get_basic_family_id(), is_var) {} expr_ref solve(expr *atom, bool is_pos) override { expr_ref res(atom, m); expr* lhs = nullptr, *rhs = nullptr, *n = nullptr; if (m.is_eq(atom, lhs, rhs)) { if (m.is_not(lhs, n) && is_variable(n)) { res = m.mk_eq(n, mk_not(m, rhs)); } else if (m.is_not(rhs, n) && is_variable(n)) { res = m.mk_eq(n, mk_not(m, lhs)); } else if (is_variable(rhs) && !is_variable(lhs)) { res = m.mk_eq(rhs, lhs); } } // (ite cond (= VAR t) (= VAR t2)) case expr* cond = nullptr, *th = nullptr, *el = nullptr; if (m.is_ite(atom, cond, th, el)) { expr_ref r1 = solve(th, true); expr_ref r2 = solve(el, true); expr* v1 = nullptr, *t1 = nullptr, *v2 = nullptr, *t2 = nullptr; if (m.is_eq(r1, v1, t1) && m.is_eq(r2, v2, t2) && v1 == v2) { res = m.mk_eq(v1, m.mk_ite(cond, t1, t2)); } } if (is_variable(atom) && m.is_bool(atom)) { res = m.mk_eq(atom, m.mk_bool_val(is_pos)); return res; } return is_pos ? res : mk_not(res); } }; class dt_solve_plugin : public solve_plugin { datatype_util dt; public: dt_solve_plugin(ast_manager& m, is_variable_proc& is_var): solve_plugin(m, m.get_family_id("datatype"), is_var), dt(m) {} expr_ref solve(expr *atom, bool is_pos) override { expr_ref res(atom, m); expr* lhs = nullptr, *rhs = nullptr; if (m.is_eq(atom, lhs, rhs)) { if (dt.is_constructor(rhs)) { std::swap(lhs, rhs); } if (dt.is_constructor(lhs) && dt.is_constructor(rhs)) { app* l = to_app(lhs), *r = to_app(rhs); if (l->get_decl() == r->get_decl()) { expr_ref_vector eqs(m); for (unsigned i = 0, sz = l->get_num_args(); i < sz; ++i) { eqs.push_back(m.mk_eq(l->get_arg(i), r->get_arg(i))); } res = mk_and(eqs); } else { res = m.mk_false(); } } else if (dt.is_constructor(lhs)) { app* l = to_app(lhs); func_decl* d = l->get_decl(); expr_ref_vector conjs(m); conjs.push_back(dt.mk_is(d, rhs)); ptr_vector const& acc = *dt.get_constructor_accessors(d); for (unsigned i = 0; i < acc.size(); ++i) { conjs.push_back(m.mk_eq(l->get_arg(i), m.mk_app(acc[i], rhs))); } res = mk_and(conjs); } } // TBD: can also solve for is_nil(x) by x = nil // return is_pos ? res : mk_not(res); } }; class bv_solve_plugin : public solve_plugin { bv_util m_bv; bool solve_eq(expr*& lhs, expr*& rhs) { unsigned lo, hi; expr* e = nullptr; if (m_bv.is_extract(lhs, lo, hi, e) && is_variable(e)) { lhs = e; unsigned sz = m_bv.get_bv_size(e); if (lo > 0 && hi + 1 < sz) { expr* args[3] = { m_bv.mk_extract(sz-1, hi + 1, e), rhs, m_bv.mk_extract(lo - 1, 0, e)}; rhs = m_bv.mk_concat(3, args); } else if (lo == 0 && hi + 1 < sz) { expr* args[2] = { m_bv.mk_extract(sz-1, hi + 1, e), rhs }; rhs = m_bv.mk_concat(2, args); } else if (lo > 0 && hi + 1 == sz) { expr* args[3] = { rhs, m_bv.mk_extract(lo - 1, 0, e) }; rhs = m_bv.mk_concat(2, args); } else { return false; } return true; } return false; } public: bv_solve_plugin(ast_manager& m, is_variable_proc& is_var): solve_plugin(m, m.get_family_id("bv"), is_var), m_bv(m) {} expr_ref solve(expr *atom, bool is_pos) override { expr_ref res(atom, m); expr* lhs = nullptr, *rhs = nullptr; if (is_pos && m.is_eq(atom, lhs, rhs) && solve_eq(lhs, rhs)) { res = m.mk_eq(lhs, rhs); return res; } if (is_pos && m.is_eq(atom, lhs, rhs) && solve_eq(rhs, lhs)) { res = m.mk_eq(rhs, lhs); return res; } return is_pos ? res : mk_not(res); } }; class array_solve_plugin : public solve_plugin { public: array_solve_plugin(ast_manager& m, is_variable_proc& is_var): solve_plugin(m, m.get_family_id("array"), is_var) {} expr_ref solve(expr *atom, bool is_pos) override { expr_ref res(atom, m); return is_pos ? res : mk_not(res); } }; solve_plugin* mk_basic_solve_plugin(ast_manager& m, is_variable_proc& is_var) { return alloc(basic_solve_plugin, m, is_var); } solve_plugin* mk_arith_solve_plugin(ast_manager& m, is_variable_proc& is_var) { return alloc(arith_solve_plugin, m, is_var); } solve_plugin* mk_dt_solve_plugin(ast_manager& m, is_variable_proc& is_var) { return alloc(dt_solve_plugin, m, is_var); } solve_plugin* mk_bv_solve_plugin(ast_manager& m, is_variable_proc& is_var) { return alloc(bv_solve_plugin, m, is_var); } #if 0 solve_plugin* mk_array_solve_plugin(ast_manager& m, is_variable_proc& is_var) { return alloc(array_solve_plugin, m, is_var); } #endif } z3-z3-4.8.7/src/qe/qe_solve_plugin.h000066400000000000000000000025361356505360400171750ustar00rootroot00000000000000/** Copyright (c) 2018 Microsoft Corporation Module Name: qe_solve.h Abstract: Light-weight variable solving plugin model for qe-lite and term_graph. Author: Nikolaj Bjorner (nbjorner), Arie Gurfinkel 2018-6-8 Revision History: --*/ #pragma once #include "ast/ast.h" #include "util/plugin_manager.h" #include "qe/qe_vartest.h" namespace qe { class solve_plugin { protected: ast_manager& m; family_id m_id; is_variable_proc& m_is_var; virtual expr_ref solve(expr* atom, bool is_pos) = 0; bool is_variable(expr* e) const { return m_is_var(e); } public: solve_plugin(ast_manager& m, family_id fid, is_variable_proc& is_var) : m(m), m_id(fid), m_is_var(is_var) {} virtual ~solve_plugin() {} family_id get_family_id() const { return m_id; } /// Process (and potentially augment) a literal expr_ref operator() (expr *lit); }; solve_plugin* mk_basic_solve_plugin(ast_manager& m, is_variable_proc& is_var); solve_plugin* mk_arith_solve_plugin(ast_manager& m, is_variable_proc& is_var); solve_plugin* mk_dt_solve_plugin(ast_manager& m, is_variable_proc& is_var); solve_plugin* mk_bv_solve_plugin(ast_manager& m, is_variable_proc& is_var); // solve_plugin* mk_array_solve_plugin(ast_manager& m, is_variable_proc& is_var); } z3-z3-4.8.7/src/qe/qe_tactic.cpp000066400000000000000000000071431356505360400162700ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: qe_tactic.cpp Abstract: Quantifier elimination front-end for tactic framework. Author: Leonardo de Moura (leonardo) 2011-12-28. Revision History: --*/ #include "tactic/tactical.h" #include "qe/qe.h" class qe_tactic : public tactic { statistics m_st; struct imp { ast_manager & m; smt_params m_fparams; qe::expr_quant_elim m_qe; imp(ast_manager & _m, params_ref const & p): m(_m), m_qe(m, m_fparams) { updt_params(p); } void updt_params(params_ref const & p) { m_fparams.updt_params(p); m_fparams.m_nlquant_elim = p.get_bool("qe_nonlinear", false); m_qe.updt_params(p); } void collect_param_descrs(param_descrs & r) { m_qe.collect_param_descrs(r); } void checkpoint() { if (m.canceled()) throw tactic_exception(m.limit().get_cancel_msg()); } void operator()(goal_ref const & g, goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); tactic_report report("qe", *g); m_fparams.m_model = g->models_enabled(); proof_ref new_pr(m); expr_ref new_f(m); bool produce_proofs = g->proofs_enabled(); unsigned sz = g->size(); for (unsigned i = 0; i < sz; i++) { checkpoint(); if (g->inconsistent()) break; expr * f = g->form(i); if (!has_quantifiers(f)) continue; m_qe(m.mk_true(), f, new_f); new_pr = nullptr; if (produce_proofs) { new_pr = m.mk_modus_ponens(g->pr(i), new_pr); } g->update(i, new_f, new_pr, g->dep(i)); } g->inc_depth(); g->elim_true(); result.push_back(g.get()); TRACE("qe", g->display(tout);); SASSERT(g->is_well_sorted()); } void collect_statistics(statistics & st) const { m_qe.collect_statistics(st); } void reset_statistics() { } }; imp * m_imp; params_ref m_params; public: qe_tactic(ast_manager & m, params_ref const & p): m_params(p) { m_imp = alloc(imp, m, p); } tactic * translate(ast_manager & m) override { return alloc(qe_tactic, m, m_params); } ~qe_tactic() override { dealloc(m_imp); } void updt_params(params_ref const & p) override { m_params = p; m_imp->updt_params(p); } void collect_param_descrs(param_descrs & r) override { r.insert("qe_nonlinear", CPK_BOOL, "(default: false) enable virtual term substitution."); m_imp->collect_param_descrs(r); } void operator()(goal_ref const & in, goal_ref_buffer & result) override { (*m_imp)(in, result); m_st.reset(); m_imp->collect_statistics(m_st); } void collect_statistics(statistics & st) const override { st.copy(m_st); } void reset_statistics() override { m_st.reset(); } void cleanup() override { ast_manager & m = m_imp->m; dealloc(m_imp); m_imp = alloc(imp, m, m_params); } }; tactic * mk_qe_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(qe_tactic, m, p)); } z3-z3-4.8.7/src/qe/qe_tactic.h000066400000000000000000000007561356505360400157400ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: qe_tactic.h Abstract: Quantifier elimination front-end for tactic framework. Author: Leonardo de Moura (leonardo) 2011-12-28. Revision History: --*/ #ifndef QE_TACTIC_H_ #define QE_TACTIC_H_ #include "util/params.h" class ast_manager; class tactic; tactic * mk_qe_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("qe", "apply quantifier elimination.", "mk_qe_tactic(m, p)") */ #endif z3-z3-4.8.7/src/qe/qe_term_graph.cpp000066400000000000000000001154351356505360400171550ustar00rootroot00000000000000/**++ Copyright (c) Arie Gurfinkel Module Name: qe_term_graph.cpp Abstract: Equivalence graph of terms Author: Arie Gurfinkel Notes: --*/ #include "util/util.h" #include "util/uint_set.h" #include "ast/ast_pp.h" #include "ast/ast_util.h" #include "ast/for_each_expr.h" #include "ast/occurs.h" #include "qe/qe_term_graph.h" #include "model/model_evaluator.h" namespace qe { static expr_ref mk_neq(ast_manager &m, expr *e1, expr *e2) { expr *t = nullptr; // x != !x == true if ((m.is_not(e1, t) && t == e2) || (m.is_not(e2, t) && t == e1)) return expr_ref(m.mk_true(), m); else if (m.are_distinct(e1, e2)) return expr_ref(m.mk_true(), m); return expr_ref(m.mk_not(m.mk_eq(e1, e2)), m); } namespace { struct sort_lt_proc { bool operator()(const expr* a, const expr *b) const { return get_sort(a)->get_id() < get_sort(b)->get_id(); } }; } namespace is_pure_ns { struct found{}; struct proc { is_variable_proc &m_is_var; proc(is_variable_proc &is_var) : m_is_var(is_var) {} void operator()(var *n) const {if (m_is_var(n)) throw found();} void operator()(app const *n) const {if (m_is_var(n)) throw found();} void operator()(quantifier *n) const {} }; } bool is_pure(is_variable_proc &is_var, expr *e) { try { is_pure_ns::proc v(is_var); quick_for_each_expr(v, e); } catch (const is_pure_ns::found &) { return false; } return true; } class term { // -- an app represented by this term expr_ref m_expr; // NSB: to make usable with exprs // -- root of the equivalence class term* m_root; // -- next element in the equivalence class (cyclic linked list) term* m_next; // -- eq class size unsigned m_class_size; // -- general purpose mark unsigned m_mark:1; // -- general purpose second mark unsigned m_mark2:1; // -- is an interpreted constant unsigned m_interpreted:1; // -- terms that contain this term as a child ptr_vector m_parents; // arguments of term. ptr_vector m_children; public: term(expr_ref const& v, u_map& app2term) : m_expr(v), m_root(this), m_next(this), m_class_size(1), m_mark(false), m_mark2(false), m_interpreted(false) { if (!is_app(m_expr)) return; for (expr* e : *to_app(m_expr)) { term* t = app2term[e->get_id()]; t->get_root().m_parents.push_back(this); m_children.push_back(t); } } ~term() {} class parents { term const& t; public: parents(term const& _t):t(_t) {} parents(term const* _t):t(*_t) {} ptr_vector::const_iterator begin() const { return t.m_parents.begin(); } ptr_vector::const_iterator end() const { return t.m_parents.end(); } }; class children { term const& t; public: children(term const& _t):t(_t) {} children(term const* _t):t(*_t) {} ptr_vector::const_iterator begin() const { return t.m_children.begin(); } ptr_vector::const_iterator end() const { return t.m_children.end(); } }; // Congruence table hash function is based on // roots of children and function declaration. unsigned get_hash() const { unsigned a, b, c; a = b = c = get_decl_id(); for (term * ch : children(this)) { a = ch->get_root().get_id(); mix(a, b, c); } return c; } static bool cg_eq(term const * t1, term const * t2) { if (t1->get_decl_id() != t2->get_decl_id()) return false; if (t1->m_children.size() != t2->m_children.size()) return false; for (unsigned i = 0, sz = t1->m_children.size(); i < sz; ++ i) { if (t1->m_children[i]->get_root().get_id() != t2->m_children[i]->get_root().get_id()) return false; } return true; } unsigned get_id() const { return m_expr->get_id();} unsigned get_decl_id() const { return is_app(m_expr) ? to_app(m_expr)->get_decl()->get_id() : m_expr->get_id(); } bool is_marked() const {return m_mark;} void set_mark(bool v){m_mark = v;} bool is_marked2() const {return m_mark2;} // NSB: where is this used? void set_mark2(bool v){m_mark2 = v;} // NSB: where is this used? bool is_interpreted() const {return m_interpreted;} bool is_theory() const { return !is_app(m_expr) || to_app(m_expr)->get_family_id() != null_family_id; } void mark_as_interpreted() {m_interpreted=true;} expr* get_expr() const {return m_expr;} unsigned get_num_args() const { return is_app(m_expr) ? to_app(m_expr)->get_num_args() : 0; } term &get_root() const {return *m_root;} bool is_root() const {return m_root == this;} void set_root(term &r) {m_root = &r;} term &get_next() const {return *m_next;} void add_parent(term* p) { m_parents.push_back(p); } unsigned get_class_size() const {return m_class_size;} void merge_eq_class(term &b) { std::swap(this->m_next, b.m_next); m_class_size += b.get_class_size(); // -- reset (useful for debugging) b.m_class_size = 0; } // -- make this term the root of its equivalence class void mk_root() { if (is_root()) return; term *curr = this; do { if (curr->is_root()) { // found previous root SASSERT(curr != this); m_class_size = curr->get_class_size(); curr->m_class_size = 0; } curr->set_root(*this); curr = &curr->get_next(); } while (curr != this); } std::ostream& display(std::ostream& out) const { out << get_id() << ": " << m_expr << (is_root() ? " R" : "") << " - "; term const* r = &this->get_next(); while (r != this) { out << r->get_id() << " "; r = &r->get_next(); } out << "\n"; return out; } }; static std::ostream& operator<<(std::ostream& out, term const& t) { return t.display(out); } bool term_graph::is_variable_proc::operator()(const expr * e) const { if (!is_app(e)) return false; const app *a = ::to_app(e); TRACE("qe", tout << a->get_family_id() << " " << m_solved.contains(a->get_decl()) << " " << m_decls.contains(a->get_decl()) << "\n";); return a->get_family_id() == null_family_id && !m_solved.contains(a->get_decl()) && m_exclude == m_decls.contains(a->get_decl()); } bool term_graph::is_variable_proc::operator()(const term &t) const { return (*this)(t.get_expr()); } void term_graph::is_variable_proc::set_decls(const func_decl_ref_vector &decls, bool exclude) { reset(); m_exclude = exclude; for (auto *d : decls) m_decls.insert(d); } void term_graph::is_variable_proc::mark_solved(const expr *e) { if ((*this)(e) && is_app(e)) m_solved.insert(::to_app(e)->get_decl()); } unsigned term_graph::term_hash::operator()(term const* t) const { return t->get_hash(); } bool term_graph::term_eq::operator()(term const* a, term const* b) const { return term::cg_eq(a, b); } term_graph::term_graph(ast_manager &man) : m(man), m_lits(m), m_pinned(m), m_projector(nullptr) { m_plugins.register_plugin(mk_basic_solve_plugin(m, m_is_var)); m_plugins.register_plugin(mk_arith_solve_plugin(m, m_is_var)); } term_graph::~term_graph() { dealloc(m_projector); reset(); } bool term_graph::is_pure_def(expr *atom, expr*& v) { expr *e = nullptr; return m.is_eq(atom, v, e) && m_is_var(v) && is_pure(m_is_var, e); } static family_id get_family_id(ast_manager &m, expr *lit) { if (m.is_not(lit, lit)) return get_family_id(m, lit); expr *a = nullptr, *b = nullptr; // deal with equality using sort of range if (m.is_eq (lit, a, b)) { return get_sort (a)->get_family_id(); } // extract family_id of top level app else if (is_app(lit)) { return to_app(lit)->get_decl()->get_family_id(); } else { return null_family_id; } } void term_graph::add_lit(expr *l) { expr_ref lit(m); expr_ref_vector lits(m); lits.push_back(l); for (unsigned i = 0; i < lits.size(); ++i) { l = lits.get(i); family_id fid = get_family_id(m, l); qe::solve_plugin *pin = m_plugins.get_plugin(fid); lit = pin ? (*pin)(l) : l; if (m.is_and(lit)) { lits.append(::to_app(lit)->get_num_args(), ::to_app(lit)->get_args()); } else { m_lits.push_back(lit); internalize_lit(lit); } } } bool term_graph::is_internalized(expr *a) { return m_app2term.contains(a->get_id()); } term* term_graph::get_term(expr *a) { term *res; return m_app2term.find (a->get_id(), res) ? res : nullptr; } term *term_graph::mk_term(expr *a) { expr_ref e(a, m); term * t = alloc(term, e, m_app2term); if (t->get_num_args() == 0 && m.is_unique_value(a)){ t->mark_as_interpreted(); } m_terms.push_back(t); m_app2term.insert(a->get_id(), t); return t; } term* term_graph::internalize_term(expr *t) { term* res = get_term(t); if (res) return res; ptr_buffer todo; todo.push_back(t); while (!todo.empty()) { t = todo.back(); res = get_term(t); if (res) { todo.pop_back(); continue; } unsigned sz = todo.size(); if (is_app(t)) { for (expr * arg : *::to_app(t)) { if (!get_term(arg)) todo.push_back(arg); } } if (sz < todo.size()) continue; todo.pop_back(); res = mk_term(t); } SASSERT(res); return res; } void term_graph::internalize_eq(expr *a1, expr* a2) { SASSERT(m_merge.empty()); merge(*internalize_term(a1), *internalize_term(a2)); merge_flush(); SASSERT(m_merge.empty()); } void term_graph::internalize_lit(expr* lit) { expr *e1 = nullptr, *e2 = nullptr, *v = nullptr; if (m.is_eq (lit, e1, e2)) { internalize_eq (e1, e2); } else { internalize_term(lit); } if (is_pure_def(lit, v)) { m_is_var.mark_solved(v); } } void term_graph::merge_flush() { while (!m_merge.empty()) { term* t1 = m_merge.back().first; term* t2 = m_merge.back().second; m_merge.pop_back(); merge(*t1, *t2); } } void term_graph::merge(term &t1, term &t2) { term *a = &t1.get_root(); term *b = &t2.get_root(); if (a == b) return; // -- merge might invalidate term2app cache m_term2app.reset(); m_pinned.reset(); if (a->get_class_size() > b->get_class_size()) { std::swap(a, b); } // Remove parents of b from the cg table. for (term* p : term::parents(b)) { if (!p->is_marked()) { p->set_mark(true); m_cg_table.erase(p); } } // make 'a' be the root of the equivalence class of 'b' b->set_root(*a); for (term *it = &b->get_next(); it != b; it = &it->get_next()) { it->set_root(*a); } // merge equivalence classes a->merge_eq_class(*b); // Insert parents of b's old equilvalence class into the cg table for (term* p : term::parents(b)) { if (p->is_marked()) { term* p_old = m_cg_table.insert_if_not_there(p); p->set_mark(false); a->add_parent(p); // propagate new equalities. if (p->get_root().get_id() != p_old->get_root().get_id()) { m_merge.push_back(std::make_pair(p, p_old)); } } } SASSERT(marks_are_clear()); } expr* term_graph::mk_app_core (expr *e) { if (is_app(e)) { expr_ref_buffer kids(m); app* a = ::to_app(e); for (expr * arg : *a) { kids.push_back (mk_app(arg)); } app* res = m.mk_app(a->get_decl(), a->get_num_args(), kids.c_ptr()); m_pinned.push_back(res); return res; } else { return e; } } expr_ref term_graph::mk_app(term const &r) { SASSERT(r.is_root()); if (r.get_num_args() == 0) { return expr_ref(r.get_expr(), m); } expr* res = nullptr; if (m_term2app.find(r.get_id(), res)) { return expr_ref(res, m); } res = mk_app_core (r.get_expr()); m_term2app.insert(r.get_id(), res); return expr_ref(res, m); } expr_ref term_graph::mk_app(expr *a) { term *t = get_term(a); if (!t) return expr_ref(a, m); else return mk_app(t->get_root()); } void term_graph::mk_equalities(term const &t, expr_ref_vector &out) { SASSERT(t.is_root()); expr_ref rep(mk_app(t), m); for (term *it = &t.get_next(); it != &t; it = &it->get_next()) { expr* mem = mk_app_core(it->get_expr()); out.push_back (m.mk_eq (rep, mem)); } } void term_graph::mk_all_equalities(term const &t, expr_ref_vector &out) { mk_equalities(t, out); for (term *it = &t.get_next(); it != &t; it = &it->get_next ()) { expr* a1 = mk_app_core (it->get_expr()); for (term *it2 = &it->get_next(); it2 != &t; it2 = &it2->get_next()) { expr* a2 = mk_app_core(it2->get_expr()); out.push_back (m.mk_eq (a1, a2)); } } } void term_graph::reset_marks() { for (term * t : m_terms) { t->set_mark(false); } } bool term_graph::marks_are_clear() { for (term * t : m_terms) { if (t->is_marked()) return false; } return true; } /// Order of preference for roots of equivalence classes /// XXX This should be factored out to let clients control the preference bool term_graph::term_lt(term const &t1, term const &t2) { // prefer constants over applications // prefer uninterpreted constants over values // prefer smaller expressions over larger ones if (t1.get_num_args() == 0 || t2.get_num_args() == 0) { if (t1.get_num_args() == t2.get_num_args()) { // t1.get_num_args() == t2.get_num_args() == 0 if (m.is_value(t1.get_expr()) == m.is_value(t2.get_expr())) return t1.get_id() < t2.get_id(); return m.is_value(t2.get_expr()); } return t1.get_num_args() < t2.get_num_args(); } unsigned sz1 = get_num_exprs(t1.get_expr()); unsigned sz2 = get_num_exprs(t1.get_expr()); return sz1 < sz2; } void term_graph::pick_root (term &t) { term *r = &t; for (term *it = &t.get_next(); it != &t; it = &it->get_next()) { it->set_mark(true); if (term_lt(*it, *r)) { r = it; } } // -- if found something better, make it the new root if (r != &t) { r->mk_root(); } } /// Choose better roots for equivalence classes void term_graph::pick_roots() { SASSERT(marks_are_clear()); for (term* t : m_terms) { if (!t->is_marked() && t->is_root()) pick_root(*t); } reset_marks(); } void term_graph::display(std::ostream &out) { for (term * t : m_terms) { out << *t; } } void term_graph::to_lits (expr_ref_vector &lits, bool all_equalities) { pick_roots(); for (expr * a : m_lits) { if (is_internalized(a)) { lits.push_back (::to_app(mk_app(a))); } } for (term * t : m_terms) { if (!t->is_root()) continue; else if (all_equalities) mk_all_equalities (*t, lits); else mk_equalities(*t, lits); } } expr_ref term_graph::to_expr() { expr_ref_vector lits(m); to_lits(lits); return mk_and(lits); } void term_graph::reset() { m_term2app.reset(); m_pinned.reset(); m_app2term.reset(); std::for_each(m_terms.begin(), m_terms.end(), delete_proc()); m_terms.reset(); m_lits.reset(); m_cg_table.reset(); } class term_graph::projector { term_graph &m_tg; ast_manager &m; u_map m_term2app; u_map m_root2rep; model_ref m_model; expr_ref_vector m_pinned; // tracks expr in the maps expr* mk_pure(term const& t) { TRACE("qe", t.display(tout);); expr* e = nullptr; if (find_term2app(t, e)) return e; e = t.get_expr(); if (!is_app(e)) return nullptr; app* a = ::to_app(e); expr_ref_buffer kids(m); for (term* ch : term::children(t)) { // prefer a node that resembles current child, // otherwise, pick a root representative, if present. if (find_term2app(*ch, e)) { kids.push_back(e); } else if (m_root2rep.find(ch->get_root().get_id(), e)) { kids.push_back(e); } else { return nullptr; } TRACE("qe_verbose", tout << *ch << " -> " << mk_pp(e, m) << "\n";); } expr* pure = m.mk_app(a->get_decl(), kids.size(), kids.c_ptr()); m_pinned.push_back(pure); add_term2app(t, pure); return pure; } bool is_better_rep(expr *t1, expr *t2) { if (!t2) return t1 != nullptr; return m.is_unique_value(t1) && !m.is_unique_value(t2); } struct term_depth { bool operator()(term const* t1, term const* t2) const { return get_depth(t1->get_expr()) < get_depth(t2->get_expr()); } }; void solve_core() { ptr_vector worklist; for (term * t : m_tg.m_terms) { // skip pure terms if (!in_term2app(*t)) { worklist.push_back(t); t->set_mark(true); } } term_depth td; std::sort(worklist.begin(), worklist.end(), td); for (unsigned i = 0; i < worklist.size(); ++i) { term* t = worklist[i]; t->set_mark(false); if (in_term2app(*t)) continue; expr* pure = mk_pure(*t); if (!pure) continue; add_term2app(*t, pure); expr* rep = nullptr; // ensure that the root has a representative m_root2rep.find(t->get_root().get_id(), rep); if (!rep) { m_root2rep.insert(t->get_root().get_id(), pure); for (term * p : term::parents(t->get_root())) { SASSERT(!in_term2app(*p)); if (!p->is_marked()) { p->set_mark(true); worklist.push_back(p); } } } } m_tg.reset_marks(); } bool find_app(term &t, expr *&res) { return find_term2app(t, res) || m_root2rep.find(t.get_root().get_id(), res); } bool find_app(expr *lit, expr *&res) { term const* t = m_tg.get_term(lit); return find_term2app(*t, res) || m_root2rep.find(t->get_root().get_id(), res); } void mk_lits(expr_ref_vector &res) { expr *e = nullptr; for (auto *lit : m_tg.m_lits) { if (!m.is_eq(lit) && find_app(lit, e)) res.push_back(e); } TRACE("qe", tout << "literals: " << res << "\n";); } void lits2pure(expr_ref_vector& res) { expr *e1 = nullptr, *e2 = nullptr, *p1 = nullptr, *p2 = nullptr; for (auto *lit : m_tg.m_lits) { if (m.is_eq(lit, e1, e2)) { if (find_app(e1, p1) && find_app(e2, p2)) { if (p1 != p2) res.push_back(m.mk_eq(p1, p2)); } else { TRACE("qe", tout << "skipping " << mk_pp(lit, m) << "\n";); } } else if (m.is_distinct(lit)) { ptr_buffer diff; for (expr* arg : *to_app(lit)) { if (find_app(arg, p1)) { diff.push_back(p1); } } if (diff.size() > 1) { res.push_back(m.mk_distinct(diff.size(), diff.c_ptr())); } else { TRACE("qe", tout << "skipping " << mk_pp(lit, m) << "\n";); } } else if (find_app(lit, p1)) { res.push_back(p1); } else { TRACE("qe", tout << "skipping " << mk_pp(lit, m) << "\n";); } } TRACE("qe", tout << "literals: " << res << "\n";); } 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; if (!is_projected(*t)) continue; app* a = to_app(e); func_decl* d = a->get_decl(); if (d->get_arity() == 0) continue; unsigned id = d->get_decl_id(); 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 distinct. // for (func_decl* d : m_decls) { unsigned id = d->get_decl_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, 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(); 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";); } void mk_pure_equalities(const term &t, expr_ref_vector &res) { SASSERT(t.is_root()); expr *rep = nullptr; if (!m_root2rep.find(t.get_id(), rep)) return; obj_hashtable members; members.insert(rep); term const * r = &t; do { expr* member = nullptr; if (find_term2app(*r, member) && !members.contains(member)) { res.push_back (m.mk_eq (rep, member)); members.insert(member); } r = &r->get_next(); } while (r != &t); } bool is_projected(const term &t) { return m_tg.m_is_var(t); } void mk_unpure_equalities(const term &t, expr_ref_vector &res) { expr *rep = nullptr; if (!m_root2rep.find(t.get_id(), rep)) return; obj_hashtable members; members.insert(rep); term const * r = &t; do { expr* member = mk_pure(*r); SASSERT(member); if (!members.contains(member) && (!is_projected(*r) || !is_solved_eq(rep, member))) { res.push_back(m.mk_eq(rep, member)); members.insert(member); } r = &r->get_next(); } while (r != &t); } template void mk_equalities(expr_ref_vector &res) { for (term *t : m_tg.m_terms) { if (!t->is_root()) continue; if (!m_root2rep.contains(t->get_id())) continue; if (pure) mk_pure_equalities(*t, res); else mk_unpure_equalities(*t, res); } TRACE("qe", tout << "literals: " << res << "\n";); } void mk_pure_equalities(expr_ref_vector &res) { mk_equalities(res); } void mk_unpure_equalities(expr_ref_vector &res) { mk_equalities(res); } // TBD: generalize for also the case of a (:var n) bool is_solved_eq(expr *lhs, expr* rhs) { return is_uninterp_const(rhs) && !occurs(rhs, lhs); } /// Add equalities and disequalities for all pure representatives /// based on their equivalence in the model void model_complete(expr_ref_vector &res) { if (!m_model) return; obj_map val2rep; model_evaluator mev(*m_model); for (auto &kv : m_root2rep) { expr *rep = kv.m_value; expr_ref val(m); expr *u = nullptr; if (!mev.eval(rep, val)) continue; if (val2rep.find(val, u)) { res.push_back(m.mk_eq(u, rep)); } else { val2rep.insert(val, rep); } } // TBD: optimize further based on implied values (e.g., // some literals are forced to be true/false) and based on // unique_values (e.g., (x=1 & y=1) does not require // (x!=y) to be added ptr_buffer reps; for (auto &kv : val2rep) { expr *rep = kv.m_value; if (!m.is_unique_value(rep)) reps.push_back(kv.m_value); } if (reps.size() <= 1) return; // -- sort representatives, call mk_distinct on any range // -- of the same sort longer than 1 std::sort(reps.c_ptr(), reps.c_ptr() + reps.size(), sort_lt_proc()); unsigned i = 0; unsigned sz = reps.size(); while (i < sz) { sort* last_sort = get_sort(reps.get(i)); unsigned j = i + 1; while (j < sz && last_sort == get_sort(reps.get(j))) {++j;} if (j - i == 2) { expr_ref d(m); d = mk_neq(m, reps.get(i), reps.get(i+1)); if (!m.is_true(d)) res.push_back(d); } else if (j - i > 2) res.push_back(m.mk_distinct(j - i, reps.c_ptr() + i)); i = j; } TRACE("qe", tout << "after distinct: " << res << "\n";); } std::ostream& display(std::ostream& out) const { m_tg.display(out); out << "term2app:\n"; for (auto const& kv : m_term2app) { out << kv.m_key << " |-> " << mk_pp(kv.m_value, m) << "\n"; } out << "root2rep:\n"; for (auto const& kv : m_root2rep) { out << kv.m_key << " |-> " << mk_pp(kv.m_value, m) << "\n"; } return out; } public: projector(term_graph &tg) : m_tg(tg), m(m_tg.m), m_pinned(m) {} void add_term2app(term const& t, expr* a) { m_term2app.insert(t.get_id(), a); } void del_term2app(term const& t) { m_term2app.remove(t.get_id()); } bool find_term2app(term const& t, expr*& r) { return m_term2app.find(t.get_id(), r); } expr* find_term2app(term const& t) { expr* r = nullptr; find_term2app(t, r); return r; } bool in_term2app(term const& t) { return m_term2app.contains(t.get_id()); } void set_model(model &mdl) { m_model = &mdl; } void reset() { m_tg.reset_marks(); m_term2app.reset(); m_root2rep.reset(); m_pinned.reset(); m_model.reset(); } expr_ref_vector project() { expr_ref_vector res(m); purify(); lits2pure(res); mk_distinct(res); reset(); return res; } expr_ref_vector get_ackerman_disequalities() { expr_ref_vector res(m); purify(); lits2pure(res); unsigned sz = res.size(); mk_distinct(res); reset(); unsigned j = 0; for (unsigned i = sz; i < res.size(); ++i) { res[j++] = res.get(i); } res.shrink(j); return res; } expr_ref_vector solve() { expr_ref_vector res(m); purify(); solve_core(); mk_lits(res); mk_unpure_equalities(res); reset(); return res; } vector get_partition(model& mdl, bool include_bool) { vector result; expr_ref_vector pinned(m); obj_map pid; model::scoped_model_completion _smc(mdl, true); for (term *t : m_tg.m_terms) { expr* a = t->get_expr(); if (!is_app(a)) continue; if (m.is_bool(a) && !include_bool) continue; expr_ref val = mdl(a); unsigned p = 0; // NB. works for simple domains Integers, Rationals, // but not for algebraic numerals. if (!pid.find(val, p)) { p = pid.size(); pid.insert(val, p); pinned.push_back(val); result.push_back(expr_ref_vector(m)); } result[p].push_back(a); } return result; } expr_ref_vector shared_occurrences(family_id fid) { expr_ref_vector result(m); for (term *t : m_tg.m_terms) { expr* e = t->get_expr(); if (m.get_sort(e)->get_family_id() != fid) continue; for (term * p : term::parents(t->get_root())) { expr* pe = p->get_expr(); if (!is_app(pe)) continue; if (to_app(pe)->get_family_id() == fid) continue; if (to_app(pe)->get_family_id() == m.get_basic_family_id()) continue; result.push_back(e); break; } } return result; } void purify() { // - propagate representatives up over parents. // use work-list + marking to propagate. // - produce equalities over represented classes. // - produce other literals over represented classes // (walk disequalities in m_lits and represent // lhs/rhs over decls or excluding decls) ptr_vector worklist; for (term * t : m_tg.m_terms) { worklist.push_back(t); t->set_mark(true); } // traverse worklist in order of depth. term_depth td; std::sort(worklist.begin(), worklist.end(), td); for (unsigned i = 0; i < worklist.size(); ++i) { term* t = worklist[i]; t->set_mark(false); if (in_term2app(*t)) continue; if (!t->is_theory() && is_projected(*t)) continue; expr* pure = mk_pure(*t); if (!pure) continue; add_term2app(*t, pure); TRACE("qe_verbose", tout << "purified " << *t << " " << mk_pp(pure, m) << "\n";); expr* rep = nullptr; // ensure that the root has a representative m_root2rep.find(t->get_root().get_id(), rep); // update rep with pure if it is better if (pure != rep && is_better_rep(pure, rep)) { m_root2rep.insert(t->get_root().get_id(), pure); for (term * p : term::parents(t->get_root())) { del_term2app(*p); if (!p->is_marked()) { p->set_mark(true); worklist.push_back(p); } } } } // Here we could also walk equivalence classes that // contain interpreted values by sort and extract // disequalities between non-unique value // representatives. these disequalities are implied // and can be mined using other means, such as theory // aware core minimization m_tg.reset_marks(); TRACE("qe", display(tout << "after purify\n");); } }; void term_graph::set_vars(func_decl_ref_vector const& decls, bool exclude) { m_is_var.set_decls(decls, exclude); } expr_ref_vector term_graph::project() { // reset solved vars so that they are not considered pure by projector m_is_var.reset_solved(); term_graph::projector p(*this); return p.project(); } expr_ref_vector term_graph::project(model &mdl) { m_is_var.reset_solved(); term_graph::projector p(*this); p.set_model(mdl); return p.project(); } expr_ref_vector term_graph::solve() { // reset solved vars so that they are not considered pure by projector m_is_var.reset_solved(); term_graph::projector p(*this); return p.solve(); } expr_ref_vector term_graph::get_ackerman_disequalities() { m_is_var.reset_solved(); dealloc(m_projector); m_projector = alloc(term_graph::projector, *this); return m_projector->get_ackerman_disequalities(); } vector term_graph::get_partition(model& mdl) { dealloc(m_projector); m_projector = alloc(term_graph::projector, *this); return m_projector->get_partition(mdl, false); } expr_ref_vector term_graph::shared_occurrences(family_id fid) { term_graph::projector p(*this); return p.shared_occurrences(fid); } void term_graph::add_model_based_terms(model& mdl, expr_ref_vector const& terms) { for (expr* t : terms) { internalize_term(t); } m_is_var.reset_solved(); SASSERT(!m_projector); m_projector = alloc(term_graph::projector, *this); // retrieve partition of terms vector equivs = m_projector->get_partition(mdl, true); // merge term graph on equal terms. for (auto const& cs : equivs) { term* t0 = get_term(cs[0]); for (unsigned i = 1; i < cs.size(); ++i) { merge(*t0, *get_term(cs[i])); } } TRACE("qe", for (auto & es : equivs) { tout << "equiv: "; for (expr* t : es) tout << expr_ref(t, m) << " "; tout << "\n"; } display(tout);); // create representatives for shared/projected variables. m_projector->set_model(mdl); m_projector->purify(); } expr* term_graph::get_model_based_rep(expr* e) { SASSERT(m_projector); term* t = get_term(e); SASSERT(t && "only get representatives"); return m_projector->find_term2app(*t); } } z3-z3-4.8.7/src/qe/qe_term_graph.h000066400000000000000000000104211356505360400166070ustar00rootroot00000000000000/**++ Copyright (c) Arie Gurfinkel Module Name: qe_term_graph.h Abstract: Equivalence graph of terms Author: Arie Gurfinkel Notes: --*/ #ifndef QE_TERM_GRAPH_H__ #define QE_TERM_GRAPH_H__ #include "ast/ast.h" #include "util/plugin_manager.h" #include "qe/qe_solve_plugin.h" #include "qe/qe_vartest.h" #include "model/model.h" namespace qe { class term; class term_graph { class projector; class is_variable_proc : public ::is_variable_proc { bool m_exclude; obj_hashtable m_decls, m_solved; public: bool operator()(const expr *e) const override; bool operator()(const term &t) const; void set_decls(const func_decl_ref_vector &decls, bool exclude); void mark_solved(const expr *e); void reset_solved() {m_solved.reset();} void reset() {m_decls.reset(); m_solved.reset(); m_exclude = true;} }; struct term_hash { unsigned operator()(term const* t) const; }; struct term_eq { bool operator()(term const* a, term const* b) const; }; ast_manager & m; ptr_vector m_terms; expr_ref_vector m_lits; // NSB: expr_ref_vector? u_map m_app2term; ast_ref_vector m_pinned; projector* m_projector; u_map m_term2app; plugin_manager m_plugins; ptr_hashtable m_cg_table; vector> m_merge; term_graph::is_variable_proc m_is_var; void merge(term &t1, term &t2); void merge_flush(); term *mk_term(expr *t); term *get_term(expr *t); term *internalize_term(expr *t); void internalize_eq(expr *a1, expr *a2); void internalize_lit(expr *lit); bool is_internalized(expr *a); bool term_lt(term const &t1, term const &t2); void pick_root (term &t); void pick_roots(); void reset_marks(); bool marks_are_clear(); expr* mk_app_core(expr* a); expr_ref mk_app(term const &t); expr* mk_pure(term& t); expr_ref mk_app(expr *a); void mk_equalities(term const &t, expr_ref_vector &out); void mk_all_equalities(term const &t, expr_ref_vector &out); void display(std::ostream &out); bool is_pure_def(expr* atom, expr *& v); public: term_graph(ast_manager &m); ~term_graph(); void set_vars(func_decl_ref_vector const& decls, bool exclude); ast_manager& get_ast_manager() const { return m;} void add_lit(expr *lit); void add_lits(expr_ref_vector const &lits) { for (expr* e : lits) add_lit(e); } void add_eq(expr* a, expr* b) { internalize_eq(a, b); } void reset(); // deprecate? void to_lits(expr_ref_vector &lits, bool all_equalities = false); expr_ref to_expr(); /** * Return literals obtained by projecting added literals * onto the vocabulary of decls (if exclude is false) or outside the * vocabulary of decls (if exclude is true). */ expr_ref_vector project(); expr_ref_vector solve(); expr_ref_vector project(model &mdl); /** * Return disequalities to ensure that disequalities between * excluded functions are preserved. * For example if f(a) = b, f(c) = d, and b and d are not * congruent, then produce the disequality a != c. */ expr_ref_vector get_ackerman_disequalities(); /** * Produce a model-based partition. */ vector get_partition(model& mdl); /** * Extract shared occurrences of terms whose sort are * fid, but appear in a context that is not fid. * for example f(x + y) produces the shared occurrence * x + y when f is uninterpreted and x + y has sort Int or Real. */ expr_ref_vector shared_occurrences(family_id fid); /** * Map expression that occurs in added literals into representative if it exists. */ void add_model_based_terms(model& mdl, expr_ref_vector const& terms); expr* get_model_based_rep(expr* e); }; } #endif z3-z3-4.8.7/src/qe/qe_vartest.h000066400000000000000000000026411356505360400161540ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: qe_vartest.h Abstract: Utilities for quantifiers. Author: Nikolaj Bjorner (nbjorner) 2013-08-28 Revision History: --*/ #ifndef QE_VARTEST_H_ #define QE_VARTEST_H_ #include "ast/ast.h" #include "util/uint_set.h" // TBD: move under qe namespace class is_variable_proc : public std::unary_function { public: virtual bool operator()(const expr* e) const = 0; }; class is_variable_test : public is_variable_proc { enum is_var_kind { BY_VAR_SET, BY_VAR_SET_COMPLEMENT, BY_NUM_DECLS }; uint_set m_var_set; unsigned m_num_decls; is_var_kind m_var_kind; public: is_variable_test(uint_set const& vars, bool index_of_bound) : m_var_set(vars), m_num_decls(0), m_var_kind(index_of_bound?BY_VAR_SET:BY_VAR_SET_COMPLEMENT) {} is_variable_test(unsigned num_decls) : m_num_decls(num_decls), m_var_kind(BY_NUM_DECLS) {} bool operator()(const expr* e) const override { if (!is_var(e)) { return false; } unsigned idx = to_var(e)->get_idx(); switch(m_var_kind) { case BY_VAR_SET: return m_var_set.contains(idx); case BY_VAR_SET_COMPLEMENT: return !m_var_set.contains(idx); case BY_NUM_DECLS: return idx < m_num_decls; } UNREACHABLE(); return false; } }; #endif z3-z3-4.8.7/src/qe/qsat.cpp000066400000000000000000001355731356505360400153150ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: qsat.cpp Abstract: Quantifier Satisfiability Solver. Author: Nikolaj Bjorner (nbjorner) 2015-5-28 Revision History: Notes: --*/ #include "ast/expr_abstract.h" #include "ast/ast_util.h" #include "ast/rewriter/quant_hoist.h" #include "ast/ast_pp.h" #include "ast/rewriter/th_rewriter.h" #include "ast/rewriter/expr_replacer.h" #include "model/model_v2_pp.h" #include "model/model_evaluator.h" #include "smt/smt_kernel.h" #include "smt/params/smt_params.h" #include "smt/smt_solver.h" #include "solver/solver.h" #include "solver/mus.h" #include "qe/qsat.h" #include "qe/qe_mbp.h" #include "qe/qe.h" #include "ast/rewriter/label_rewriter.h" namespace qe { pred_abs::pred_abs(ast_manager& m): m(m), m_asms(m), m_trail(m), m_fmc(alloc(generic_model_converter, m, "qsat")) { } generic_model_converter* pred_abs::fmc() { return m_fmc.get(); } void pred_abs::reset() { m_trail.reset(); dec_keys(m_pred2lit); dec_keys(m_lit2pred); dec_keys(m_asm2pred); dec_keys(m_pred2asm); m_lit2pred.reset(); m_pred2lit.reset(); m_asm2pred.reset(); m_pred2asm.reset(); m_elevel.reset(); m_asms.reset(); m_asms_lim.reset(); m_preds.reset(); } max_level pred_abs::compute_level(app* e) { unsigned sz0 = todo.size(); todo.push_back(e); while (sz0 != todo.size()) { app* a = to_app(todo.back()); if (m_elevel.contains(a)) { todo.pop_back(); continue; } max_level lvl, lvl0; bool has_new = false; if (m_flevel.find(a->get_decl(), lvl)) { lvl0.merge(lvl); } for (unsigned i = 0; i < a->get_num_args(); ++i) { app* arg = to_app(a->get_arg(i)); if (m_elevel.find(arg, lvl)) { lvl0.merge(lvl); } else { todo.push_back(arg); has_new = true; } } if (!has_new) { m_elevel.insert(a, lvl0); todo.pop_back(); } } return m_elevel.find(e); } void pred_abs::add_pred(app* p, app* lit) { m.inc_ref(p); m_pred2lit.insert(p, lit); add_lit(p, lit); } void pred_abs::add_lit(app* p, app* lit) { if (!m_lit2pred.contains(lit)) { m.inc_ref(lit); m_lit2pred.insert(lit, p); } } void pred_abs::add_asm(app* p, expr* assum) { SASSERT(!m_asm2pred.contains(assum)); m.inc_ref(p); m.inc_ref(assum); m_asm2pred.insert(assum, p); m_pred2asm.insert(p, assum); } void pred_abs::push() { m_asms_lim.push_back(m_asms.size()); } void pred_abs::pop(unsigned num_scopes) { unsigned l = m_asms_lim.size() - num_scopes; m_asms.resize(m_asms_lim[l]); m_asms_lim.shrink(l); } void pred_abs::insert(app* a, max_level const& lvl) { unsigned l = lvl.max(); if (l == UINT_MAX) l = 0; while (m_preds.size() <= l) { m_preds.push_back(app_ref_vector(m)); } m_preds[l].push_back(a); } bool pred_abs::is_predicate(app* a, unsigned l) { max_level lvl1; return m_flevel.find(a->get_decl(), lvl1) && lvl1.max() < l; } void pred_abs::get_assumptions(model* mdl, expr_ref_vector& asms) { unsigned level = m_asms_lim.size(); if (level > m_preds.size()) { level = m_preds.size(); } if (!mdl) { asms.append(m_asms); return; } if (level == 0) { return; } model_evaluator eval(*mdl); eval.set_model_completion(true); TRACE("qe", model_v2_pp(tout, *mdl);); expr_ref val(m); for (unsigned j = 0; j < m_preds[level - 1].size(); ++j) { app* p = m_preds[level - 1][j].get(); TRACE("qe", tout << "process level: " << level - 1 << ": " << mk_pp(p, m) << "\n";); eval(p, val); if (m.is_false(val)) { m_asms.push_back(m.mk_not(p)); } else { SASSERT(m.is_true(val)); m_asms.push_back(p); } } asms.append(m_asms); for (unsigned i = level + 1; i < m_preds.size(); i += 2) { for (unsigned j = 0; j < m_preds[i].size(); ++j) { app* p = m_preds[i][j].get(); max_level lvl = m_elevel.find(p); bool use = (lvl.m_fa == i && (lvl.m_ex == UINT_MAX || lvl.m_ex < level)) || (lvl.m_ex == i && (lvl.m_fa == UINT_MAX || lvl.m_fa < level)); if (use) { eval(p, val); if (m.is_false(val)) { asms.push_back(m.mk_not(p)); } else { asms.push_back(p); } } } } TRACE("qe", tout << "level: " << level << "\n"; model_v2_pp(tout, *mdl); display(tout, asms);); } void pred_abs::set_expr_level(app* v, max_level const& lvl) { m_elevel.insert(v, lvl); } void pred_abs::set_decl_level(func_decl* f, max_level const& lvl) { m_flevel.insert(f, lvl); } void pred_abs::abstract_atoms(expr* fml, expr_ref_vector& defs) { max_level level; abstract_atoms(fml, level, defs); } /** \brief create propositional abstraction of formula by replacing atomic sub-formulas by fresh propositional variables, and adding definitions for each propositional formula on the side. Assumption is that the formula is quantifier-free. */ void pred_abs::abstract_atoms(expr* fml, max_level& level, expr_ref_vector& defs) { expr_mark mark; ptr_vector args; app_ref r(m), eq(m); app* p; unsigned sz0 = todo.size(); todo.push_back(fml); m_trail.push_back(fml); max_level l; while (sz0 != todo.size()) { app* a = to_app(todo.back()); todo.pop_back(); if (mark.is_marked(a)) { continue; } mark.mark(a); if (m_lit2pred.find(a, p)) { TRACE("qe", tout << mk_pp(a, m) << " " << mk_pp(p, m) << "\n";); level.merge(m_elevel.find(p)); continue; } if (is_uninterp_const(a) && m.is_bool(a)) { l = m_elevel.find(a); level.merge(l); if (!m_pred2lit.contains(a)) { add_pred(a, a); insert(a, l); } continue; } unsigned sz = a->get_num_args(); for (unsigned i = 0; i < sz; ++i) { expr* f = a->get_arg(i); if (!mark.is_marked(f)) { todo.push_back(f); } } bool is_boolop = (a->get_family_id() == m.get_basic_family_id()) && (!m.is_eq(a) || m.is_bool(a->get_arg(0))) && (!m.is_distinct(a) || m.is_bool(a->get_arg(0))); if (!is_boolop && m.is_bool(a)) { TRACE("qe", tout << mk_pp(a, m) << "\n";); r = fresh_bool("p"); max_level l = compute_level(a); add_pred(r, a); m_elevel.insert(r, l); eq = m.mk_eq(r, a); defs.push_back(eq); if (!is_predicate(a, l.max())) { insert(r, l); } level.merge(l); } } } app_ref pred_abs::fresh_bool(char const* name) { app_ref r(m.mk_fresh_const(name, m.mk_bool_sort()), m); m_fmc->hide(r); return r; } // optional pass to replace atoms by predicates // so that SMT core works on propositional // abstraction only. expr_ref pred_abs::mk_abstract(expr* fml) { expr_ref_vector trail(m), args(m); obj_map cache; app* b; expr_ref r(m); unsigned sz0 = todo.size(); todo.push_back(fml); while (sz0 != todo.size()) { app* a = to_app(todo.back()); if (cache.contains(a)) { todo.pop_back(); continue; } if (m_lit2pred.find(a, b)) { cache.insert(a, b); todo.pop_back(); continue; } unsigned sz = a->get_num_args(); bool diff = false; args.reset(); for (unsigned i = 0; i < sz; ++i) { expr* f = a->get_arg(i), *f1; if (cache.find(f, f1)) { args.push_back(f1); diff |= f != f1; } else { todo.push_back(f); } } if (sz == args.size()) { if (diff) { r = m.mk_app(a->get_decl(), sz, args.c_ptr()); trail.push_back(r); } else { r = a; } cache.insert(a, r); todo.pop_back(); } } return expr_ref(cache.find(fml), m); } expr_ref pred_abs::mk_assumption_literal(expr* a, model* mdl, max_level const& lvl, expr_ref_vector& defs) { expr_ref A(m); A = pred2asm(a); a = A; app_ref p(m); expr_ref q(m), fml(m); app *b; expr *c, *d; max_level lvl2; TRACE("qe", tout << mk_pp(a, m) << " " << lvl << "\n";); if (m_asm2pred.find(a, b)) { q = b; } else if (m.is_not(a, c) && m_asm2pred.find(c, b)) { q = m.mk_not(b); } else if (m_pred2asm.find(a, d)) { q = a; } else if (m.is_not(a, c) && m_pred2asm.find(c, d)) { q = a; } else { p = fresh_bool("def"); if (m.is_not(a, a)) { if (mdl) mdl->register_decl(p->get_decl(), m.mk_false()); q = m.mk_not(p); } else { if (mdl) mdl->register_decl(p->get_decl(), m.mk_true()); q = p; } m_elevel.insert(p, lvl); insert(p, lvl); fml = a; abstract_atoms(fml, lvl2, defs); fml = mk_abstract(fml); defs.push_back(m.mk_eq(p, fml)); add_asm(p, a); TRACE("qe", tout << mk_pp(a, m) << " |-> " << p << "\n";); } return q; } void pred_abs::mk_concrete(expr_ref_vector& fmls, obj_map const& map) { obj_map cache; expr_ref_vector trail(m); expr* p; app_ref r(m); ptr_vector args; unsigned sz0 = todo.size(); todo.append(fmls.size(), (expr*const*)fmls.c_ptr()); while (sz0 != todo.size()) { app* a = to_app(todo.back()); if (cache.contains(a)) { todo.pop_back(); continue; } if (map.find(a, p)) { cache.insert(a, p); todo.pop_back(); continue; } unsigned sz = a->get_num_args(); args.reset(); bool diff = false; for (unsigned i = 0; i < sz; ++i) { expr* f = a->get_arg(i), *f1; if (cache.find(f, f1)) { args.push_back(f1); diff |= f != f1; } else { todo.push_back(f); } } if (args.size() == sz) { if (diff) { r = m.mk_app(a->get_decl(), sz, args.c_ptr()); } else { r = to_app(a); } cache.insert(a, r); trail.push_back(r); todo.pop_back(); } } for (unsigned i = 0; i < fmls.size(); ++i) { fmls[i] = to_app(cache.find(fmls[i].get())); } } void pred_abs::pred2lit(expr_ref_vector& fmls) { mk_concrete(fmls, m_pred2lit); } expr_ref pred_abs::pred2asm(expr* fml) { expr_ref_vector fmls(m); fmls.push_back(fml); mk_concrete(fmls, m_pred2asm); return mk_and(fmls); } void pred_abs::collect_statistics(statistics& st) const { st.update("qsat num predicates", m_pred2lit.size()); } void pred_abs::display(std::ostream& out) const { out << "pred2lit:\n"; obj_map::iterator it = m_pred2lit.begin(), end = m_pred2lit.end(); for (; it != end; ++it) { out << mk_pp(it->m_key, m) << " |-> " << mk_pp(it->m_value, m) << "\n"; } for (unsigned i = 0; i < m_preds.size(); ++i) { out << "level " << i << "\n"; for (unsigned j = 0; j < m_preds[i].size(); ++j) { app* p = m_preds[i][j]; expr* e; if (m_pred2lit.find(p, e)) { out << mk_pp(p, m) << " := " << mk_pp(e, m) << "\n"; } else { out << mk_pp(p, m) << "\n"; } } } } void pred_abs::display(std::ostream& out, expr_ref_vector const& asms) const { max_level lvl; for (unsigned i = 0; i < asms.size(); ++i) { expr* e = asms[i]; bool is_not = m.is_not(asms[i], e); out << mk_pp(asms[i], m); if (m_elevel.find(e, lvl)) { lvl.display(out << " - "); } if (m_pred2lit.find(e, e)) { out << " : " << (is_not?"!":"") << mk_pp(e, m); } out << "\n"; } } void pred_abs::get_free_vars(expr* fml, app_ref_vector& vars) { ast_fast_mark1 mark; unsigned sz0 = todo.size(); todo.push_back(fml); while (sz0 != todo.size()) { expr* e = todo.back(); todo.pop_back(); if (mark.is_marked(e) || is_var(e)) { continue; } mark.mark(e); if (is_quantifier(e)) { todo.push_back(to_quantifier(e)->get_expr()); continue; } SASSERT(is_app(e)); app* a = to_app(e); if (is_uninterp_const(a)) { // TBD generalize for uninterpreted functions. vars.push_back(a); } for (expr* arg : *a) { todo.push_back(arg); } } } bool pred_abs::validate_defs(model& model) const { bool valid = true; for (auto& kv : m_pred2lit) { expr_ref val_a(m), val_b(m); expr* a = kv.m_key; expr* b = kv.m_value; val_a = model(a); val_b = model(b); if ((m.is_true(val_a) && m.is_false(val_b)) || (m.is_false(val_a) && m.is_true(val_b))) { TRACE("qe", tout << mk_pp(a, m) << " := " << val_a << "\n"; tout << mk_pp(b, m) << " := " << val_b << "\n"; tout << m_elevel.find(a) << "\n";); valid = false; } } return valid; } class kernel { ast_manager& m; params_ref m_params; ref m_solver; public: kernel(ast_manager& m): m(m), m_solver(mk_smt_solver(m, m_params, symbol::null)) { m_params.set_bool("model", true); m_params.set_uint("relevancy_lvl", 0); m_params.set_uint("case_split_strategy", CS_ACTIVITY_WITH_CACHE); m_solver->updt_params(m_params); } solver& s() { return *m_solver; } solver const& s() const { return *m_solver; } 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); } void get_core(expr_ref_vector& core) { core.reset(); m_solver->get_unsat_core(core); TRACE("qe", m_solver->display(tout << "core: " << core << "\n") << "\n";); } }; enum qsat_mode { qsat_qe, qsat_qe_rec, qsat_sat, qsat_maximize }; class qsat : public tactic { struct stats { unsigned m_num_rounds; stats() { reset(); } void reset() { memset(this, 0, sizeof(*this)); } }; ast_manager& m; params_ref m_params; stats m_stats; statistics m_st; qe::mbp m_mbp; kernel m_fa; kernel m_ex; pred_abs m_pred_abs; expr_ref_vector m_answer; expr_ref_vector m_asms; vector m_vars; // variables from alternating prefixes. unsigned m_level; model_ref m_model; qsat_mode m_mode; app_ref_vector m_avars; // variables to project app_ref_vector m_free_vars; app* m_objective; opt::inf_eps* m_value; bool m_was_sat; model_ref m_model_save; expr_ref m_gt; opt::inf_eps m_value_save; /** \brief check alternating satisfiability. Even levels are existential, odd levels are universal. */ lbool check_sat() { while (true) { ++m_stats.m_num_rounds; IF_VERBOSE(3, verbose_stream() << "(check-qsat level: " << m_level << " round: " << m_stats.m_num_rounds << ")\n";); check_cancel(); expr_ref_vector asms(m_asms); m_pred_abs.get_assumptions(m_model.get(), asms); if (m_model.get()) { validate_assumptions(*m_model.get(), asms); } TRACE("qe", tout << asms << "\n";); solver& s = get_kernel(m_level).s(); lbool res = s.check_sat(asms); switch (res) { case l_true: s.get_model(m_model); SASSERT(validate_defs("check_sat")); SASSERT(validate_assumptions(*m_model.get(), asms)); SASSERT(validate_model(asms)); TRACE("qe", s.display(tout); display(tout << "\n", *m_model.get()); display(tout, asms); ); if (m_level == 0) { m_model_save = m_model; } push(); if (m_level == 1 && m_mode == qsat_maximize) { maximize_model(); } break; case l_false: switch (m_level) { case 0: return l_false; case 1: if (m_mode == qsat_sat) { return l_true; } if (m_model.get()) { SASSERT(validate_assumptions(*m_model.get(), asms)); if (!project_qe(asms)) return l_undef; } else { pop(1); } break; default: if (m_model.get()) { if (!project(asms)) return l_undef; } else { pop(1); } break; } break; case l_undef: return res; } } return l_undef; } kernel& get_kernel(unsigned j) { if (is_exists(j)) { return m_ex; } else { return m_fa; } } bool is_exists(unsigned level) const { return (level % 2) == 0; } bool is_forall(unsigned level) const { return is_exists(level+1); } void push() { m_level++; m_pred_abs.push(); } void pop(unsigned num_scopes) { m_model.reset(); SASSERT(num_scopes <= m_level); m_pred_abs.pop(num_scopes); m_level -= num_scopes; } void clear() { m_st.reset(); m_fa.s().collect_statistics(m_st); m_ex.s().collect_statistics(m_st); m_pred_abs.collect_statistics(m_st); m_level = 0; m_answer.reset(); m_asms.reset(); m_pred_abs.reset(); m_vars.reset(); m_model = nullptr; m_free_vars.reset(); m_fa.clear(); m_ex.clear(); } void reset() override { clear(); m_fa.init(); m_ex.init(); } /** \brief create a quantifier prefix formula. */ void hoist(expr_ref& fml) { proof_ref pr(m); label_rewriter rw(m); rw.remove_labels(fml, pr); quantifier_hoister hoist(m); app_ref_vector vars(m); bool is_forall = false; m_pred_abs.get_free_vars(fml, vars); m_vars.push_back(vars); vars.reset(); if (m_mode != qsat_sat) { is_forall = true; hoist.pull_quantifier(is_forall, fml, vars); m_vars.push_back(vars); filter_vars(vars); } else { hoist.pull_quantifier(is_forall, fml, vars); m_vars.back().append(vars); filter_vars(vars); } do { is_forall = !is_forall; vars.reset(); hoist.pull_quantifier(is_forall, fml, vars); m_vars.push_back(vars); filter_vars(vars); } while (!vars.empty()); SASSERT(m_vars.back().empty()); initialize_levels(); TRACE("qe", tout << fml << "\n";); } void filter_vars(app_ref_vector const& vars) { for (app* v : vars) m_pred_abs.fmc()->hide(v); } void initialize_levels() { // initialize levels. for (unsigned i = 0; i < m_vars.size(); ++i) { max_level lvl; if (is_exists(i)) { lvl.m_ex = i; } else { lvl.m_fa = i; } for (unsigned j = 0; j < m_vars[i].size(); ++j) { m_pred_abs.set_expr_level(m_vars[i][j].get(), lvl); } } } bool validate_defs(char const* msg) { if (m_model.get() && !m_pred_abs.validate_defs(*m_model.get())) { TRACE("qe", tout << msg << "\n"; display(tout); if (m_level > 0) { get_kernel(m_level-1).s().display(tout); } expr_ref_vector asms(m); m_pred_abs.get_assumptions(m_model.get(), asms); tout << asms << "\n"; m_pred_abs.pred2lit(asms); tout << asms << "\n";); return false; } else { return true; } } bool get_core(expr_ref_vector& core, unsigned level) { SASSERT(validate_defs("get_core")); get_kernel(level).get_core(core); m_pred_abs.pred2lit(core); return true; } bool minimize_core(expr_ref_vector& core, unsigned level) { expr_ref_vector core1(m), core2(m), dels(m); TRACE("qe", tout << core.size() << "\n";); mus mus(get_kernel(level).s()); for (unsigned i = 0; i < core.size(); ++i) { app* a = to_app(core[i].get()); max_level lvl = m_pred_abs.compute_level(a); if (lvl.max() + 2 <= level) { VERIFY(core1.size() == mus.add_soft(a)); core1.push_back(a); } else { core2.push_back(a); mus.add_assumption(a); } } TRACE("qe", tout << core1.size() << " " << core2.size() << "\n";); if (core1.size() > 8) { if (l_true != mus.get_mus(core2)) { return false; } TRACE("qe", tout << core1.size() << " -> " << core2.size() << "\n";); core.reset(); core.append(core2); } return true; } void check_cancel() { if (m.canceled()) { throw tactic_exception(m.limit().get_cancel_msg()); } } void display(std::ostream& out) const { out << "level: " << m_level << "\n"; for (unsigned i = 0; i < m_vars.size(); ++i) { out << m_vars[i] << "\n"; } m_pred_abs.display(out); } void display(std::ostream& out, model& model) const { display(out); model_v2_pp(out, model); } void display(std::ostream& out, expr_ref_vector const& asms) const { m_pred_abs.display(out, asms); } void add_assumption(expr* fml) { expr_ref eq(m); app_ref b = m_pred_abs.fresh_bool("b"); m_asms.push_back(b); eq = m.mk_eq(b, fml); m_ex.assert_expr(eq); m_fa.assert_expr(eq); m_pred_abs.add_pred(b, to_app(fml)); max_level lvl; m_pred_abs.set_expr_level(b, lvl); } bool project_qe(expr_ref_vector& core) { SASSERT(m_level == 1); expr_ref fml(m); model& mdl = *m_model.get(); if (!get_core(core, m_level)) { return false; } SASSERT(validate_core(mdl, core)); get_vars(m_level); SASSERT(validate_assumptions(mdl, core)); m_mbp(force_elim(), m_avars, mdl, core); SASSERT(validate_defs("project_qe")); if (m_mode == qsat_maximize) { maximize_core(core, mdl); } else { fml = negate_core(core); add_assumption(fml); m_answer.push_back(fml); m_free_vars.append(m_avars); } pop(1); return true; } bool project(expr_ref_vector& core) { if (!get_core(core, m_level)) return false; TRACE("qe", display(tout); display(tout << "core\n", core);); SASSERT(m_level >= 2); expr_ref fml(m); expr_ref_vector defs(m), core_save(m); max_level level; model& mdl = *m_model.get(); SASSERT(validate_core(mdl, core)); get_vars(m_level-1); SASSERT(validate_project(mdl, core)); m_mbp(force_elim(), m_avars, mdl, core); m_free_vars.append(m_avars); fml = negate_core(core); unsigned num_scopes = 0; m_pred_abs.abstract_atoms(fml, level, defs); m_ex.assert_expr(mk_and(defs)); m_fa.assert_expr(mk_and(defs)); if (level.max() == UINT_MAX) { num_scopes = 2*(m_level/2); } else if (m_mode == qsat_qe_rec) { num_scopes = 2; } else { if (level.max() + 2 > m_level) return false; SASSERT(level.max() + 2 <= m_level); num_scopes = m_level - level.max(); SASSERT(num_scopes >= 2); if ((num_scopes % 2) != 0) { --num_scopes; } } pop(num_scopes); TRACE("qe", tout << "backtrack: " << num_scopes << " new level: " << m_level << "\nproject:\n" << core << "\n|->\n" << fml << "\n";); if (m_level == 0 && m_mode != qsat_sat) { add_assumption(fml); } else { fml = m_pred_abs.mk_abstract(fml); get_kernel(m_level).assert_expr(fml); } SASSERT(!m_model.get()); return true; } void get_vars(unsigned level) { m_avars.reset(); for (unsigned i = level; i < m_vars.size(); ++i) { m_avars.append(m_vars[i]); } } expr_ref negate_core(expr_ref_vector const& core) { return ::push_not(::mk_and(core)); } bool force_elim() const { return m_mode != qsat_qe_rec; } expr_ref elim_rec(expr* fml) { expr_ref tmp(m); expr_ref_vector trail(m); obj_map visited; ptr_vector todo; trail.push_back(fml); todo.push_back(fml); expr* e = nullptr, *r = nullptr; while (!todo.empty()) { check_cancel(); e = todo.back(); if (visited.contains(e)) { todo.pop_back(); continue; } switch(e->get_kind()) { case AST_APP: { app* a = to_app(e); expr_ref_vector args(m); unsigned num_args = a->get_num_args(); bool all_visited = true; for (unsigned i = 0; i < num_args; ++i) { if (visited.find(a->get_arg(i), r)) { args.push_back(r); } else { todo.push_back(a->get_arg(i)); all_visited = false; } } if (all_visited) { r = m.mk_app(a->get_decl(), args.size(), args.c_ptr()); todo.pop_back(); trail.push_back(r); visited.insert(e, r); } break; } case AST_QUANTIFIER: { SASSERT(!is_lambda(e)); app_ref_vector vars(m); quantifier* q = to_quantifier(e); bool is_fa = ::is_forall(q); tmp = q->get_expr(); extract_vars(q, tmp, vars); TRACE("qe", tout << vars << " " << mk_pp(q, m) << " " << tmp << "\n";); tmp = elim_rec(tmp); if (is_fa) { tmp = ::push_not(tmp); } tmp = elim(vars, tmp); if (is_fa) { tmp = ::push_not(tmp); } trail.push_back(tmp); visited.insert(e, tmp); todo.pop_back(); break; } default: UNREACHABLE(); break; } } VERIFY (visited.find(fml, e)); return expr_ref(e, m); } expr_ref elim(app_ref_vector const& vars, expr* _fml) { expr_ref fml(_fml, m); reset(); m_vars.push_back(app_ref_vector(m)); m_vars.push_back(vars); initialize_levels(); fml = push_not(fml); TRACE("qe", tout << vars << " " << fml << "\n";); expr_ref_vector defs(m); m_pred_abs.abstract_atoms(fml, defs); fml = m_pred_abs.mk_abstract(fml); m_ex.assert_expr(mk_and(defs)); m_fa.assert_expr(mk_and(defs)); m_ex.assert_expr(fml); m_fa.assert_expr(m.mk_not(fml)); TRACE("qe", tout << "ex: " << fml << "\n";); lbool is_sat = check_sat(); fml = ::mk_and(m_answer); TRACE("qe", tout << "ans: " << fml << "\n"; tout << "Free vars: " << m_free_vars << "\n";); if (is_sat == l_false) { obj_hashtable vars; for (unsigned i = 0; i < m_free_vars.size(); ++i) { app* v = m_free_vars[i].get(); if (vars.contains(v)) { m_free_vars[i] = m_free_vars.back(); m_free_vars.pop_back(); --i; } else { vars.insert(v); } } fml = mk_exists(m, m_free_vars.size(), m_free_vars.c_ptr(), fml); return fml; } else { return expr_ref(_fml, m); } } bool validate_assumptions(model& mdl, expr_ref_vector const& core) { for (expr* c : core) { if (!mdl.is_true(c)) { TRACE("qe", tout << "component of core is not true: " << mk_pp(c, m) << "\n";); return false; } } return true; } bool validate_core(model& mdl, expr_ref_vector const& core) { return true; #if 0 TRACE("qe", tout << "Validate core\n";); solver& s = get_kernel(m_level).s(); expr_ref_vector fmls(m); fmls.append(core.size(), core.c_ptr()); s.get_assertions(fmls); return check_fmls(fmls) || m.canceled(); #endif } bool check_fmls(expr_ref_vector const& fmls) { smt_params p; smt::kernel solver(m, p); for (unsigned i = 0; i < fmls.size(); ++i) { solver.assert_expr(fmls[i]); } lbool is_sat = solver.check(); CTRACE("qe", is_sat != l_false, tout << fmls << "\nare not unsat\n";); return (is_sat == l_false) || m.canceled(); } bool validate_model(expr_ref_vector const& asms) { return true; #if 0 TRACE("qe", tout << "Validate model\n";); solver& s = get_kernel(m_level).s(); expr_ref_vector fmls(m); s.get_assertions(fmls); return validate_model(*m_model, asms.size(), asms.c_ptr()) && validate_model(*m_model, fmls.size(), fmls.c_ptr()); #endif } bool validate_model(model& mdl, unsigned sz, expr* const* fmls) { expr_ref val(m); for (unsigned i = 0; i < sz; ++i) { if (!m_model->is_true(fmls[i]) && !m.canceled()) { TRACE("qe", tout << "Formula does not evaluate to true in model: " << mk_pp(fmls[i], m) << "\n";); return false; } } return true; } // validate the following: // proj is true in model. // core is true in model. // proj does not contain vars. // proj => exists vars . core // (core[model(vars)/vars] => proj) bool validate_project(model& mdl, expr_ref_vector const& core) { return true; #if 0 TRACE("qe", tout << "Validate projection\n";); if (!validate_model(mdl, core.size(), core.c_ptr())) return false; expr_ref_vector proj(core); app_ref_vector vars(m_avars); m_mbp(false, vars, mdl, proj); if (!vars.empty()) { TRACE("qe", tout << "Not validating partial projection\n";); return true; } if (!validate_model(mdl, proj.size(), proj.c_ptr())) { TRACE("qe", tout << "Projection is false in model\n";); return false; } if (m.canceled()) { return true; } for (unsigned i = 0; i < m_avars.size(); ++i) { contains_app cont(m, m_avars[i].get()); if (cont(proj)) { TRACE("qe", tout << "Projection contains free variable: " << mk_pp(m_avars[i].get(), m) << "\n";); return false; } } // // TBD: proj => exists vars . core, // e.g., extract and use realizer functions from mbp. // // (core[model(vars)/vars] => proj) expr_ref_vector fmls(m); fmls.append(core.size(), core.c_ptr()); for (unsigned i = 0; i < m_avars.size(); ++i) { expr_ref val(m); VERIFY(mdl.eval(m_avars[i].get(), val)); fmls.push_back(m.mk_eq(m_avars[i].get(), val)); } fmls.push_back(m.mk_not(mk_and(proj))); if (!check_fmls(fmls)) { TRACE("qe", tout << "implication check failed, could be due to turning != into >\n";); } return true; #endif } public: qsat(ast_manager& m, params_ref const& p, qsat_mode mode): m(m), m_mbp(m), m_fa(m), m_ex(m), m_pred_abs(m), m_answer(m), m_asms(m), m_level(0), m_mode(mode), m_avars(m), m_free_vars(m), m_objective(nullptr), m_value(nullptr), m_was_sat(false), m_gt(m) { reset(); } ~qsat() override { clear(); } void updt_params(params_ref const & p) override { } void collect_param_descrs(param_descrs & r) override { } void operator()(/* in */ goal_ref const & in, /* out */ goal_ref_buffer & result) override { tactic_report report("qsat-tactic", *in); ptr_vector fmls; expr_ref_vector defs(m); expr_ref fml(m); in->get_formulas(fmls); fml = mk_and(m, fmls.size(), fmls.c_ptr()); // for now: // fail if cores. (TBD) // fail if proofs. (TBD) if (m_mode == qsat_qe_rec) { fml = elim_rec(fml); in->reset(); in->inc_depth(); in->assert_expr(fml); result.push_back(in.get()); return; } reset(); TRACE("qe", tout << fml << "\n";); if (m_mode != qsat_sat) { fml = push_not(fml); } hoist(fml); if (!is_ground(fml)) { throw tactic_exception("formula is not hoistable"); } m_pred_abs.abstract_atoms(fml, defs); fml = m_pred_abs.mk_abstract(fml); m_ex.assert_expr(mk_and(defs)); m_fa.assert_expr(mk_and(defs)); m_ex.assert_expr(fml); m_fa.assert_expr(m.mk_not(fml)); TRACE("qe", tout << "ex: " << fml << "\n";); lbool is_sat = check_sat(); switch (is_sat) { case l_false: in->reset(); in->inc_depth(); if (m_mode == qsat_qe) { fml = ::mk_and(m_answer); in->assert_expr(fml); } else { SASSERT(m_mode == qsat_sat); in->assert_expr(m.mk_false()); } result.push_back(in.get()); break; case l_true: in->reset(); in->inc_depth(); result.push_back(in.get()); if (in->models_enabled()) { model_converter_ref mc; mc = model2model_converter(m_model_save.get()); mc = concat(m_pred_abs.fmc(), mc.get()); in->add(mc.get()); } break; case l_undef: result.push_back(in.get()); std::string s = m_ex.s().reason_unknown(); if (s == "ok" || s == "unknown") { s = m_fa.s().reason_unknown(); } throw tactic_exception(std::move(s)); } } void collect_statistics(statistics & st) const override { st.copy(m_st); m_fa.s().collect_statistics(st); m_ex.s().collect_statistics(st); m_pred_abs.collect_statistics(st); st.update("qsat num rounds", m_stats.m_num_rounds); m_pred_abs.collect_statistics(st); } void reset_statistics() override { m_stats.reset(); m_fa.reset_statistics(); m_ex.reset_statistics(); } void cleanup() override { reset(); } void set_logic(symbol const & l) override { } void set_progress_callback(progress_callback * callback) override { } tactic * translate(ast_manager & m) override { return alloc(qsat, m, m_params, m_mode); } lbool maximize(expr_ref_vector const& fmls, app* t, model_ref& mdl, opt::inf_eps& value) { expr_ref_vector defs(m); expr_ref fml = mk_and(fmls); hoist(fml); m_objective = t; m_value = &value; m_was_sat = false; m_model_save.reset(); m_pred_abs.abstract_atoms(fml, defs); fml = m_pred_abs.mk_abstract(fml); m_ex.assert_expr(mk_and(defs)); m_fa.assert_expr(mk_and(defs)); m_ex.assert_expr(fml); m_fa.assert_expr(m.mk_not(fml)); lbool is_sat = check_sat(); mdl = m_model.get(); switch (is_sat) { case l_false: if (!m_was_sat) { return l_false; } mdl = m_model_save; break; case l_true: UNREACHABLE(); break; case l_undef: std::string s = m_ex.s().reason_unknown(); if (s == "ok") { s = m_fa.s().reason_unknown(); } throw tactic_exception(std::move(s)); } return l_true; } void maximize_core(expr_ref_vector const& core, model& mdl) { SASSERT(m_value); SASSERT(m_objective); TRACE("qe", tout << "maximize: " << core << "\n";); m_was_sat |= !core.empty(); expr_ref bound(m); *m_value = m_value_save; IF_VERBOSE(3, verbose_stream() << "(maximize " << *m_value << ")\n";); m_ex.assert_expr(m_gt); m_fa.assert_expr(m_gt); } void maximize_model() { SASSERT(m_level == 1 && m_mode == qsat_maximize); SASSERT(m_objective); expr_ref ge(m); expr_ref_vector asms(m), defs(m); m_pred_abs.get_assumptions(m_model.get(), asms); m_pred_abs.pred2lit(asms); SASSERT(validate_defs("maximize_model1")); m_value_save = m_mbp.maximize(asms, *m_model.get(), m_objective, ge, m_gt); SASSERT(validate_defs("maximize_model2")); // bound := val <= m_objective IF_VERBOSE(3, verbose_stream() << "(qsat-maximize-bound: " << m_value_save << ")\n";); max_level level; m_pred_abs.abstract_atoms(ge, level, defs); m_ex.assert_expr(mk_and(defs)); m_fa.assert_expr(mk_and(defs)); ge = m_pred_abs.mk_abstract(ge); SASSERT(is_uninterp_const(ge)); // update model with evaluation for bound. if (is_uninterp_const(ge)) { m_model->register_decl(to_app(ge)->get_decl(), m.mk_true()); } SASSERT(validate_defs("maximize_model3")); } }; lbool maximize(expr_ref_vector const& fmls, app* t, opt::inf_eps& value, model_ref& mdl, params_ref const& p) { ast_manager& m = fmls.get_manager(); qsat qs(m, p, qsat_maximize); return qs.maximize(fmls, t, mdl, value); } struct qmax::imp { qsat m_qsat; imp(ast_manager& m, params_ref const& p): m_qsat(m, p, qsat_maximize) {} }; qmax::qmax(ast_manager& m, params_ref const& p) { m_imp = alloc(imp, m, p); } qmax::~qmax() { dealloc(m_imp); } lbool qmax::operator()(expr_ref_vector const& fmls, app* t, opt::inf_eps& value, model_ref& mdl) { return m_imp->m_qsat.maximize(fmls, t, mdl, value); } void qmax::collect_statistics(statistics& st) const { m_imp->m_qsat.collect_statistics(st); } }; tactic * mk_qsat_tactic(ast_manager& m, params_ref const& p) { return alloc(qe::qsat, m, p, qe::qsat_sat); } tactic * mk_qe2_tactic(ast_manager& m, params_ref const& p) { return alloc(qe::qsat, m, p, qe::qsat_qe); } tactic * mk_qe_rec_tactic(ast_manager& m, params_ref const& p) { return alloc(qe::qsat, m, p, qe::qsat_qe_rec); } z3-z3-4.8.7/src/qe/qsat.h000066400000000000000000000113651356505360400147520ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: qsat.h Abstract: Quantifier Satisfiability Solver. Author: Nikolaj Bjorner (nbjorner) 2015-5-28 Revision History: --*/ #ifndef QE_QSAT_H__ #define QE_QSAT_H__ #include "tactic/tactic.h" #include "tactic/generic_model_converter.h" #include "qe/qe_mbp.h" namespace qe { struct max_level { unsigned m_ex, m_fa; max_level(): m_ex(UINT_MAX), m_fa(UINT_MAX) {} void merge(max_level const& other) { merge(m_ex, other.m_ex); merge(m_fa, other.m_fa); } static unsigned max(unsigned a, unsigned b) { if (a == UINT_MAX) return b; if (b == UINT_MAX) return a; return std::max(a, b); } unsigned max() const { return max(m_ex, m_fa); } void merge(unsigned& lvl, unsigned other) { lvl = max(lvl, other); } std::ostream& display(std::ostream& out) const { if (m_ex != UINT_MAX) out << "e" << m_ex << " "; if (m_fa != UINT_MAX) out << "a" << m_fa << " "; return out; } bool operator==(max_level const& other) const { return m_ex == other.m_ex && m_fa == other.m_fa; } }; inline std::ostream& operator<<(std::ostream& out, max_level const& lvl) { return lvl.display(out); } class pred_abs { ast_manager& m; vector m_preds; expr_ref_vector m_asms; unsigned_vector m_asms_lim; obj_map m_pred2lit; // maintain definitions of predicates. obj_map m_lit2pred; // maintain reverse mapping to predicates obj_map m_asm2pred; // maintain map from assumptions to predicates obj_map m_pred2asm; // predicates |-> assumptions expr_ref_vector m_trail; generic_model_converter_ref m_fmc; ptr_vector todo; obj_map m_elevel; obj_map m_flevel; template void dec_keys(obj_map& map) { typename obj_map::iterator it = map.begin(), end = map.end(); for (; it != end; ++it) { m.dec_ref(it->m_key); } } void add_lit(app* p, app* lit); void add_asm(app* p, expr* lit); bool is_predicate(app* a, unsigned l); void mk_concrete(expr_ref_vector& fmls, obj_map const& map); public: pred_abs(ast_manager& m); generic_model_converter* fmc(); void reset(); max_level compute_level(app* e); void push(); void pop(unsigned num_scopes); void insert(app* a, max_level const& lvl); void get_assumptions(model* mdl, expr_ref_vector& asms); void set_expr_level(app* v, max_level const& lvl); void set_decl_level(func_decl* v, max_level const& lvl); void abstract_atoms(expr* fml, max_level& level, expr_ref_vector& defs); void abstract_atoms(expr* fml, expr_ref_vector& defs); expr_ref mk_abstract(expr* fml); void pred2lit(expr_ref_vector& fmls); expr_ref pred2asm(expr* fml); void get_free_vars(expr* fml, app_ref_vector& vars); expr_ref mk_assumption_literal(expr* a, model* mdl, max_level const& lvl, expr_ref_vector& defs); void add_pred(app* p, app* lit); app_ref fresh_bool(char const* name); void display(std::ostream& out) const; void display(std::ostream& out, expr_ref_vector const& asms) const; void collect_statistics(statistics& st) const; bool validate_defs(model& model) const; }; class qmax { struct imp; imp* m_imp; public: qmax(ast_manager& m, params_ref const& p = params_ref()); ~qmax(); lbool operator()(expr_ref_vector const& fmls, app* t, opt::inf_eps& value, model_ref& mdl); void collect_statistics(statistics& st) const; }; lbool maximize(expr_ref_vector const& fmls, app* t, opt::inf_eps& value, model_ref& mdl, params_ref const& p); } tactic * mk_qsat_tactic(ast_manager & m, params_ref const& p = params_ref()); tactic * mk_qe2_tactic(ast_manager & m, params_ref const& p = params_ref()); tactic * mk_qe_rec_tactic(ast_manager & m, params_ref const& p = params_ref()); /* ADD_TACTIC("qsat", "apply a QSAT solver.", "mk_qsat_tactic(m, p)") ADD_TACTIC("qe2", "apply a QSAT based quantifier elimination.", "mk_qe2_tactic(m, p)") ADD_TACTIC("qe_rec", "apply a QSAT based quantifier elimination recursively.", "mk_qe_rec_tactic(m, p)") */ #endif z3-z3-4.8.7/src/sat/000077500000000000000000000000001356505360400140055ustar00rootroot00000000000000z3-z3-4.8.7/src/sat/CMakeLists.txt000066400000000000000000000013721356505360400165500ustar00rootroot00000000000000z3_add_component(sat SOURCES ba_solver.cpp dimacs.cpp sat_asymm_branch.cpp sat_bdd.cpp sat_big.cpp sat_clause.cpp sat_clause_set.cpp sat_clause_use_list.cpp sat_cleaner.cpp sat_config.cpp sat_ddfw.cpp sat_drat.cpp sat_elim_eqs.cpp sat_elim_vars.cpp sat_iff3_finder.cpp sat_integrity_checker.cpp sat_local_search.cpp sat_lookahead.cpp sat_model_converter.cpp sat_mus.cpp sat_parallel.cpp sat_prob.cpp sat_probing.cpp sat_scc.cpp sat_simplifier.cpp sat_solver.cpp sat_unit_walk.cpp sat_watched.cpp COMPONENT_DEPENDENCIES util PYG_FILES sat_asymm_branch_params.pyg sat_params.pyg sat_scc_params.pyg sat_simplifier_params.pyg ) z3-z3-4.8.7/src/sat/ba_solver.cpp000066400000000000000000005400221356505360400164700ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: ba_solver.cpp Abstract: Extension for cardinality and xr reasoning. Author: Nikolaj Bjorner (nbjorner) 2017-01-30 Revision History: --*/ #include #include "sat/ba_solver.h" #include "sat/sat_types.h" #include "util/mpz.h" #include "sat/sat_simplifier_params.hpp" namespace sat { static unsigned _bad_id = 11111111; // 2759; // #define BADLOG(_cmd_) if (p.id() == _bad_id) { _cmd_; } ba_solver::card& ba_solver::constraint::to_card() { SASSERT(is_card()); return static_cast(*this); } ba_solver::card const& ba_solver::constraint::to_card() const{ SASSERT(is_card()); return static_cast(*this); } ba_solver::pb& ba_solver::constraint::to_pb() { SASSERT(is_pb()); return static_cast(*this); } ba_solver::pb const& ba_solver::constraint::to_pb() const{ SASSERT(is_pb()); return static_cast(*this); } ba_solver::pb_base const& ba_solver::constraint::to_pb_base() const{ SASSERT(is_pb() || is_card()); return static_cast(*this); } ba_solver::xr& ba_solver::constraint::to_xr() { SASSERT(is_xr()); return static_cast(*this); } ba_solver::xr const& ba_solver::constraint::to_xr() const{ SASSERT(is_xr()); return static_cast(*this); } unsigned ba_solver::constraint::fold_max_var(unsigned w) const { if (lit() != null_literal) w = std::max(w, lit().var()); for (unsigned i = 0; i < size(); ++i) w = std::max(w, get_lit(i).var()); return w; } std::ostream& operator<<(std::ostream& out, ba_solver::constraint const& cnstr) { if (cnstr.lit() != null_literal) out << cnstr.lit() << " == "; switch (cnstr.tag()) { case ba_solver::card_t: { ba_solver::card const& c = cnstr.to_card(); for (literal l : c) { out << l << " "; } out << " >= " << c.k(); break; } case ba_solver::pb_t: { ba_solver::pb const& p = cnstr.to_pb(); bool first = true; for (ba_solver::wliteral wl : p) { if (!first) out << "+ "; if (wl.first != 1) out << wl.first << " * "; out << wl.second << " "; first = false; } out << " >= " << p.k(); break; } case ba_solver::xr_t: { ba_solver::xr const& x = cnstr.to_xr(); for (unsigned i = 0; i < x.size(); ++i) { out << x[i] << " "; if (i + 1 < x.size()) out << "x "; } break; } default: UNREACHABLE(); } return out; } // ----------------------- // pb_base bool ba_solver::pb_base::well_formed() const { uint_set vars; if (lit() != null_literal) vars.insert(lit().var()); for (unsigned i = 0; i < size(); ++i) { bool_var v = get_lit(i).var(); if (vars.contains(v)) return false; if (get_coeff(i) > k()) return false; vars.insert(v); } return true; } // ---------------------- // card ba_solver::card::card(unsigned id, literal lit, literal_vector const& lits, unsigned k): pb_base(card_t, id, lit, lits.size(), get_obj_size(lits.size()), k) { for (unsigned i = 0; i < size(); ++i) { m_lits[i] = lits[i]; } } void ba_solver::card::negate() { m_lit.neg(); for (unsigned i = 0; i < m_size; ++i) { m_lits[i].neg(); } m_k = m_size - m_k + 1; SASSERT(m_size >= m_k && m_k > 0); } bool ba_solver::card::is_watching(literal l) const { unsigned sz = std::min(k() + 1, size()); for (unsigned i = 0; i < sz; ++i) { if ((*this)[i] == l) return true; } return false; } // ----------------------------------- // pb ba_solver::pb::pb(unsigned id, literal lit, svector const& wlits, unsigned k): pb_base(pb_t, id, lit, wlits.size(), get_obj_size(wlits.size()), k), m_slack(0), m_num_watch(0), m_max_sum(0) { for (unsigned i = 0; i < size(); ++i) { m_wlits[i] = wlits[i]; } update_max_sum(); } void ba_solver::pb::update_max_sum() { m_max_sum = 0; for (unsigned i = 0; i < size(); ++i) { m_wlits[i].first = std::min(k(), m_wlits[i].first); if (m_max_sum + m_wlits[i].first < m_max_sum) { throw default_exception("addition of pb coefficients overflows"); } m_max_sum += m_wlits[i].first; } } void ba_solver::pb::negate() { m_lit.neg(); unsigned w = 0; for (unsigned i = 0; i < m_size; ++i) { m_wlits[i].second.neg(); VERIFY(w + m_wlits[i].first >= w); w += m_wlits[i].first; } m_k = w - m_k + 1; VERIFY(w >= m_k && m_k > 0); } bool ba_solver::pb::is_watching(literal l) const { for (unsigned i = 0; i < m_num_watch; ++i) { if ((*this)[i].second == l) return true; } return false; } bool ba_solver::pb::is_cardinality() const { if (size() == 0) return false; unsigned w = (*this)[0].first; for (wliteral wl : *this) if (w != wl.first) return false; return true; } // ----------------------------------- // xr ba_solver::xr::xr(unsigned id, literal_vector const& lits): constraint(xr_t, id, null_literal, lits.size(), get_obj_size(lits.size())) { for (unsigned i = 0; i < size(); ++i) { m_lits[i] = lits[i]; } } bool ba_solver::xr::is_watching(literal l) const { return l == (*this)[0] || l == (*this)[1] || ~l == (*this)[0] || ~l == (*this)[1]; } bool ba_solver::xr::well_formed() const { uint_set vars; if (lit() != null_literal) vars.insert(lit().var()); for (literal l : *this) { bool_var v = l.var(); if (vars.contains(v)) return false; vars.insert(v); } return true; } // ---------------------------- // card bool ba_solver::init_watch(card& c) { literal root = c.lit(); if (root != null_literal && value(root) == l_false) { clear_watch(c); c.negate(); root.neg(); } if (root != null_literal) { if (!is_watched(root, c)) watch_literal(root, c); if (!c.is_pure() && !is_watched(~root, c)) watch_literal(~root, c); } TRACE("ba", display(tout << "init watch: ", c, true);); SASSERT(root == null_literal || value(root) == l_true); unsigned j = 0, sz = c.size(), bound = c.k(); // put the non-false literals into the head. if (bound == sz) { for (literal l : c) assign(c, l); return false; } for (unsigned i = 0; i < sz; ++i) { if (value(c[i]) != l_false) { if (j != i) { if (c.is_watched() && j <= bound && i > bound) { unwatch_literal(c[j], c); watch_literal(c[i], c); } c.swap(i, j); } ++j; } } DEBUG_CODE( bool is_false = false; for (literal l : c) { SASSERT(!is_false || value(l) == l_false); is_false = value(l) == l_false; }); // j is the number of non-false, sz - j the number of false. if (j < bound) { if (c.is_watched()) clear_watch(c); SASSERT(0 < bound && bound < sz); literal alit = c[j]; // // we need the assignment level of the asserting literal to be maximal. // such that conflict resolution can use the asserting literal as a starting // point. // for (unsigned i = bound; i < sz; ++i) { if (lvl(alit) < lvl(c[i])) { c.swap(i, j); alit = c[j]; } } set_conflict(c, alit); return false; } else if (j == bound) { for (unsigned i = 0; i < bound; ++i) { assign(c, c[i]); } return false; } else { if (c.is_watched()) return true; clear_watch(c); for (unsigned i = 0; i <= bound; ++i) { watch_literal(c[i], c); } c.set_watch(); return true; } } void ba_solver::clear_watch(card& c) { if (c.is_clear()) return; c.clear_watch(); unsigned sz = std::min(c.k() + 1, c.size()); for (unsigned i = 0; i < sz; ++i) { unwatch_literal(c[i], c); } } // ----------------------- // constraint void ba_solver::set_conflict(constraint& c, literal lit) { m_stats.m_num_conflicts++; TRACE("ba", display(tout, c, true); ); if (!validate_conflict(c)) { display(std::cout, c, true); UNREACHABLE(); } SASSERT(validate_conflict(c)); if (c.is_xr() && value(lit) == l_true) lit.neg(); SASSERT(value(lit) == l_false); set_conflict(justification::mk_ext_justification(s().scope_lvl(), c.index()), ~lit); SASSERT(inconsistent()); } void ba_solver::assign(constraint& c, literal lit) { if (inconsistent()) return; switch (value(lit)) { case l_true: break; case l_false: set_conflict(c, lit); break; default: m_stats.m_num_propagations++; m_num_propagations_since_pop++; //TRACE("ba", tout << "#prop: " << m_stats.m_num_propagations << " - " << c.lit() << " => " << lit << "\n";); SASSERT(validate_unit_propagation(c, lit)); if (get_config().m_drat) { svector ps; literal_vector lits; get_antecedents(lit, c, lits); lits.push_back(lit); ps.push_back(drat::premise(drat::s_ext(), c.lit())); // null_literal case. drat_add(lits, ps); } assign(lit, justification::mk_ext_justification(s().scope_lvl(), c.index())); break; } } // ------------------- // pb_base void ba_solver::simplify(pb_base& p) { SASSERT(s().at_base_lvl()); if (p.lit() != null_literal && value(p.lit()) == l_false) { TRACE("ba", tout << "pb: flip sign " << p << "\n";); IF_VERBOSE(1, verbose_stream() << "sign is flipped " << p << "\n";); return; } bool nullify = p.lit() != null_literal && value(p.lit()) == l_true; if (nullify) { IF_VERBOSE(100, display(verbose_stream() << "nullify tracking literal\n", p, true);); SASSERT(lvl(p.lit()) == 0); nullify_tracking_literal(p); init_watch(p); } SASSERT(p.lit() == null_literal || value(p.lit()) != l_false); unsigned true_val = 0, slack = 0, num_false = 0; for (unsigned i = 0; i < p.size(); ++i) { literal l = p.get_lit(i); bool_var v = l.var(); if (s().was_eliminated(v)) { VERIFY(p.learned()); remove_constraint(p, "contains eliminated"); return; } switch (value(l)) { case l_true: true_val += p.get_coeff(i); break; case l_false: ++num_false; break; default: slack += p.get_coeff(i); break; } } if (p.k() == 1 && p.lit() == null_literal) { literal_vector lits(p.literals()); s().mk_clause(lits.size(), lits.c_ptr(), p.learned()); IF_VERBOSE(100, display(verbose_stream() << "add clause: " << lits << "\n", p, true);); remove_constraint(p, "implies clause"); } else if (true_val == 0 && num_false == 0) { if (p.lit() == null_literal || value(p.lit()) == l_true) { init_watch(p); } } else if (true_val >= p.k()) { if (p.lit() != null_literal) { IF_VERBOSE(100, display(verbose_stream() << "assign true literal ", p, true);); s().assign_scoped(p.lit()); } remove_constraint(p, "is true"); } else if (slack + true_val < p.k()) { if (p.lit() != null_literal) { IF_VERBOSE(100, display(verbose_stream() << "assign false literal ", p, true);); s().assign_scoped(~p.lit()); } else { IF_VERBOSE(1, verbose_stream() << "unsat during simplification\n";); s().set_conflict(justification(0)); } remove_constraint(p, "is false"); } else if (slack + true_val == p.k()) { literal_vector lits(p.literals()); assert_unconstrained(p.lit(), lits); remove_constraint(p, "is tight"); } else { unsigned sz = p.size(); clear_watch(p); unsigned j = 0; for (unsigned i = 0; i < sz; ++i) { literal l = p.get_lit(i); if (value(l) == l_undef) { if (i != j) p.swap(i, j); ++j; } } sz = j; // _bad_id = p.id(); BADLOG(display(verbose_stream() << "simplify ", p, true)); unsigned k = p.k() - true_val; if (k == 1 && p.lit() == null_literal) { literal_vector lits(sz, p.literals().c_ptr()); s().mk_clause(sz, lits.c_ptr(), p.learned()); remove_constraint(p, "is clause"); return; } p.set_size(sz); p.set_k(k); if (p.lit() == null_literal || value(p.lit()) == l_true) { init_watch(p); } else { SASSERT(value(p.lit()) == l_undef); } BADLOG(display(verbose_stream() << "simplified ", p, true); verbose_stream() << "\n"); // display(verbose_stream(), c, true); _bad_id = 11111111; SASSERT(p.well_formed()); m_simplify_change = true; } } /* \brief split PB constraint into two because root is reused in arguments. x <=> a*x + B*y >= k x => a*x + By >= k ~x => a*x + By < k k*~x + a*x + By >= k (B+a-k + 1)*x + a*~x + B*~y >= B + a - k + 1 (k - a) * ~x + By >= k - a k' * x + B'y >= k' */ void ba_solver::split_root(pb_base& p) { SASSERT(p.lit() != null_literal); SASSERT(!p.learned()); m_weights.resize(2*s().num_vars(), 0); unsigned k = p.k(); unsigned w, w1, w2; literal root = p.lit(); m_weights[(~root).index()] = k; for (unsigned i = 0; i < p.size(); ++i) { m_weights[p.get_lit(i).index()] += p.get_coeff(i); } literal_vector lits(p.literals()); lits.push_back(~root); for (literal l : lits) { w1 = m_weights[l.index()]; w2 = m_weights[(~l).index()]; if (w1 >= w2) { if (w2 >= k) { for (literal l2 : lits) m_weights[l2.index()] = 0; // constraint is true return; } k -= w2; m_weights[(~l).index()] = 0; m_weights[l.index()] = w1 - w2; } } SASSERT(k > 0); // ~root * (k - a) + p >= k - a m_wlits.reset(); for (literal l : lits) { w = m_weights[l.index()]; if (w != 0) { m_wlits.push_back(wliteral(w, l)); } m_weights[l.index()] = 0; } add_pb_ge(null_literal, m_wlits, k, false); } // ------------------- // pb // watch a prefix of literals, such that the slack of these is >= k bool ba_solver::init_watch(pb& p) { clear_watch(p); if (p.lit() != null_literal && value(p.lit()) == l_false) { p.negate(); } VERIFY(p.lit() == null_literal || value(p.lit()) == l_true); unsigned sz = p.size(), bound = p.k(); // put the non-false literals into the head. unsigned slack = 0, slack1 = 0, num_watch = 0, j = 0; for (unsigned i = 0; i < sz; ++i) { if (value(p[i].second) != l_false) { if (j != i) { p.swap(i, j); } if (slack <= bound) { slack += p[j].first; ++num_watch; } else { slack1 += p[j].first; } ++j; } } BADLOG(verbose_stream() << "watch " << num_watch << " out of " << sz << "\n"); DEBUG_CODE( bool is_false = false; for (unsigned k = 0; k < sz; ++k) { SASSERT(!is_false || value(p[k].second) == l_false); SASSERT((k < j) == (value(p[k].second) != l_false)); is_false = value(p[k].second) == l_false; }); if (slack < bound) { literal lit = p[j].second; VERIFY(value(lit) == l_false); for (unsigned i = j + 1; i < sz; ++i) { if (lvl(lit) < lvl(p[i].second)) { lit = p[i].second; } } set_conflict(p, lit); return false; } else { for (unsigned i = 0; i < num_watch; ++i) { watch_literal(p[i], p); } p.set_slack(slack); p.set_num_watch(num_watch); SASSERT(validate_watch(p, null_literal)); TRACE("ba", display(tout << "init watch: ", p, true);); // slack is tight: if (slack + slack1 == bound) { SASSERT(slack1 == 0); SASSERT(j == num_watch); for (unsigned i = 0; i < j; ++i) { assign(p, p[i].second); } } return true; } } /* Chai Kuhlmann: Lw - set of watched literals Lu - set of unwatched literals that are not false Lw = Lw \ { alit } Sw -= value a_max = max { a | l in Lw u Lu, l = undef } while (Sw < k + a_max & Lu != 0) { a_s = max { a | l in Lu } Sw += a_s Lw = Lw u {l_s} Lu = Lu \ {l_s} } if (Sw < k) return conflict for (li in Lw | Sw < k + ai) assign li return no-conflict a_max index: index of non-false literal with maximal weight. */ void ba_solver::add_index(pb& p, unsigned index, literal lit) { if (value(lit) == l_undef) { m_pb_undef.push_back(index); if (p[index].first > m_a_max) { m_a_max = p[index].first; } } } /* \brief propagate assignment to alit in constraint p. TBD: - consider reordering literals in watch list so that the search for watched literal takes average shorter time. - combine with caching literals that are assigned to 'true' to a cold store where they are not being revisited. Since 'true' literals may be unassigned (unless they are assigned at level 0) the cache has to be backtrack friendly (and the overhead of backtracking has to be taken into account). */ lbool ba_solver::add_assign(pb& p, literal alit) { BADLOG(display(verbose_stream() << "assign: " << alit << " watch: " << p.num_watch() << " size: " << p.size(), p, true)); TRACE("ba", display(tout << "assign: " << alit << "\n", p, true);); SASSERT(!inconsistent()); unsigned sz = p.size(); unsigned bound = p.k(); unsigned num_watch = p.num_watch(); unsigned slack = p.slack(); SASSERT(value(alit) == l_false); SASSERT(p.lit() == null_literal || value(p.lit()) == l_true); SASSERT(num_watch <= sz); SASSERT(num_watch > 0); unsigned index = 0; m_a_max = 0; m_pb_undef.reset(); for (; index < num_watch; ++index) { literal lit = p[index].second; if (lit == alit) { break; } add_index(p, index, lit); } if (index == num_watch || num_watch == 0) { _bad_id = p.id(); BADLOG( verbose_stream() << "BAD: " << p.id() << "\n"; display(verbose_stream(), p, true); verbose_stream() << "alit: " << alit << "\n"; verbose_stream() << "num watch " << num_watch << "\n"); UNREACHABLE(); return l_undef; } SASSERT(validate_watch(p, null_literal)); // SASSERT(validate_watch(p, null_literal)); SASSERT(index < num_watch); unsigned index1 = index + 1; for (; m_a_max == 0 && index1 < num_watch; ++index1) { add_index(p, index1, p[index1].second); } unsigned val = p[index].first; SASSERT(value(p[index].second) == l_false); SASSERT(val <= slack); slack -= val; // find literals to swap with: for (unsigned j = num_watch; j < sz && slack < bound + m_a_max; ++j) { literal lit = p[j].second; if (value(lit) != l_false) { slack += p[j].first; SASSERT(!is_watched(p[j].second, p)); watch_literal(p[j], p); p.swap(num_watch, j); add_index(p, num_watch, lit); BADLOG(verbose_stream() << "add watch: " << lit << " num watch: " << num_watch << " max: " << m_a_max << " slack " << slack << "\n"); ++num_watch; } } SASSERT(!inconsistent()); DEBUG_CODE(for (auto idx : m_pb_undef) { SASSERT(value(p[idx].second) == l_undef); }); if (slack < bound) { // maintain watching the literal slack += val; p.set_slack(slack); p.set_num_watch(num_watch); SASSERT(validate_watch(p, null_literal)); BADLOG(display(verbose_stream() << "conflict: " << alit << " watch: " << p.num_watch() << " size: " << p.size(), p, true)); SASSERT(bound <= slack); TRACE("ba", tout << "conflict " << alit << "\n";); set_conflict(p, alit); return l_false; } if (num_watch == 1) { _bad_id = p.id(); } BADLOG(verbose_stream() << "size: " << p.size() << " index: " << index << " num watch: " << num_watch << "\n"); // swap out the watched literal. --num_watch; SASSERT(num_watch > 0); p.set_slack(slack); p.set_num_watch(num_watch); p.swap(num_watch, index); // // slack >= bound, but slack - w(l) < bound // l must be true. // if (slack < bound + m_a_max) { BADLOG(verbose_stream() << "slack " << slack << " " << bound << " " << m_a_max << "\n";); TRACE("ba", tout << p << "\n"; for(auto j : m_pb_undef) tout << j << " "; tout << "\n";); for (unsigned index1 : m_pb_undef) { if (index1 == num_watch) { index1 = index; } wliteral wl = p[index1]; literal lit = wl.second; SASSERT(value(lit) == l_undef); if (slack < bound + wl.first) { BADLOG(verbose_stream() << "Assign " << lit << " " << wl.first << "\n"); assign(p, lit); } } } SASSERT(validate_watch(p, alit)); // except that alit is still watched. TRACE("ba", display(tout << "assign: " << alit << "\n", p, true);); BADLOG(verbose_stream() << "unwatch " << alit << " watch: " << p.num_watch() << " size: " << p.size() << " slack: " << p.slack() << " " << inconsistent() << "\n"); return l_undef; } void ba_solver::watch_literal(wliteral l, pb& p) { watch_literal(l.second, p); } void ba_solver::clear_watch(pb& p) { p.clear_watch(); for (unsigned i = 0; i < p.num_watch(); ++i) { unwatch_literal(p[i].second, p); } p.set_num_watch(0); DEBUG_CODE(for (wliteral wl : p) VERIFY(!is_watched(wl.second, p));); } void ba_solver::recompile(pb& p) { // IF_VERBOSE(2, verbose_stream() << "re: " << p << "\n";); SASSERT(p.num_watch() == 0); m_weights.resize(2*s().num_vars(), 0); for (wliteral wl : p) { m_weights[wl.second.index()] += wl.first; } unsigned k = p.k(); unsigned sz = p.size(); bool all_units = true; unsigned j = 0; for (unsigned i = 0; i < sz && 0 < k; ++i) { literal l = p[i].second; unsigned w1 = m_weights[l.index()]; unsigned w2 = m_weights[(~l).index()]; if (w1 == 0 || w1 < w2) { continue; } else if (k <= w2) { k = 0; break; } else { SASSERT(w2 <= w1 && w2 < k); k -= w2; w1 -= w2; m_weights[l.index()] = 0; m_weights[(~l).index()] = 0; if (w1 == 0) { continue; } else { p[j] = wliteral(w1, l); all_units &= w1 == 1; ++j; } } } sz = j; // clear weights for (wliteral wl : p) { m_weights[wl.second.index()] = 0; m_weights[(~wl.second).index()] = 0; } if (k == 0) { if (p.lit() != null_literal) { s().assign_scoped(p.lit()); } remove_constraint(p, "recompiled to true"); return; } else if (k == 1 && p.lit() == null_literal) { literal_vector lits(sz, p.literals().c_ptr()); s().mk_clause(sz, lits.c_ptr(), p.learned()); remove_constraint(p, "recompiled to clause"); return; } else if (all_units) { literal_vector lits(sz, p.literals().c_ptr()); add_at_least(p.lit(), lits, k, p.learned()); remove_constraint(p, "recompiled to cardinality"); return; } else { p.set_size(sz); p.update_max_sum(); if (p.max_sum() < k) { if (p.lit() == null_literal) { s().set_conflict(justification(0)); } else { s().assign_scoped(~p.lit()); } remove_constraint(p, "recompiled to false"); return; } p.set_k(k); SASSERT(p.well_formed()); if (clausify(p)) { return; } if (p.lit() == null_literal || value(p.lit()) == l_true) { init_watch(p); } } } void ba_solver::display(std::ostream& out, pb const& p, bool values) const { if (p.lit() != null_literal) out << p.lit() << " == "; if (values) { out << "[watch: " << p.num_watch() << ", slack: " << p.slack() << "]"; } if (p.lit() != null_literal && values) { out << "@(" << value(p.lit()); if (value(p.lit()) != l_undef) { out << ":" << lvl(p.lit()); } out << "): "; } unsigned i = 0; for (wliteral wl : p) { literal l = wl.second; unsigned w = wl.first; if (i > 0) out << "+ "; if (i++ == p.num_watch()) out << " | "; if (w > 1) out << w << " * "; out << l; if (values) { out << "@(" << value(l); if (value(l) != l_undef) { out << ":" << lvl(l); } out << ") "; } else { out << " "; } } out << ">= " << p.k() << "\n"; } // -------------------- // xr: void ba_solver::clear_watch(xr& x) { x.clear_watch(); unwatch_literal(x[0], x); unwatch_literal(x[1], x); unwatch_literal(~x[0], x); unwatch_literal(~x[1], x); } bool ba_solver::parity(xr const& x, unsigned offset) const { bool odd = false; unsigned sz = x.size(); for (unsigned i = offset; i < sz; ++i) { SASSERT(value(x[i]) != l_undef); if (value(x[i]) == l_true) { odd = !odd; } } return odd; } bool ba_solver::init_watch(xr& x) { clear_watch(x); VERIFY(x.lit() == null_literal); TRACE("ba", display(tout, x, true);); unsigned sz = x.size(); unsigned j = 0; for (unsigned i = 0; i < sz && j < 2; ++i) { if (value(x[i]) == l_undef) { x.swap(i, j); ++j; } } switch (j) { case 0: if (!parity(x, 0)) { unsigned l = lvl(x[0]); j = 1; for (unsigned i = 1; i < sz; ++i) { if (lvl(x[i]) > l) { j = i; l = lvl(x[i]); } } set_conflict(x, x[j]); } return false; case 1: SASSERT(x.lit() == null_literal || value(x.lit()) == l_true); assign(x, parity(x, 1) ? ~x[0] : x[0]); return false; default: SASSERT(j == 2); watch_literal(x[0], x); watch_literal(x[1], x); watch_literal(~x[0], x); watch_literal(~x[1], x); return true; } } lbool ba_solver::add_assign(xr& x, literal alit) { // literal is assigned unsigned sz = x.size(); TRACE("ba", tout << "assign: " << ~alit << "@" << lvl(~alit) << " " << x << "\n"; display(tout, x, true); ); VERIFY(x.lit() == null_literal); SASSERT(value(alit) != l_undef); unsigned index = (x[1].var() == alit.var()) ? 1 : 0; VERIFY(x[index].var() == alit.var()); // find a literal to swap with: for (unsigned i = 2; i < sz; ++i) { literal lit = x[i]; if (value(lit) == l_undef) { x.swap(index, i); unwatch_literal(~alit, x); // alit gets unwatched by propagate_core because we return l_undef watch_literal(lit, x); watch_literal(~lit, x); TRACE("ba", tout << "swap in: " << lit << " " << x << "\n";); return l_undef; } } if (index == 0) { x.swap(0, 1); } // alit resides at index 1. VERIFY(x[1].var() == alit.var()); if (value(x[0]) == l_undef) { bool p = parity(x, 1); assign(x, p ? ~x[0] : x[0]); } else if (!parity(x, 0)) { set_conflict(x, ~x[1]); } return inconsistent() ? l_false : l_true; } // --------------------------- // conflict resolution void ba_solver::inc_coeff(literal l, unsigned offset) { SASSERT(offset > 0); bool_var v = l.var(); SASSERT(v != null_bool_var); m_coeffs.reserve(v + 1, 0); TRACE("ba_verbose", tout << l << " " << offset << "\n";); int64_t coeff0 = m_coeffs[v]; if (coeff0 == 0) { m_active_vars.push_back(v); } int64_t loffset = static_cast(offset); int64_t inc = l.sign() ? -loffset : loffset; int64_t coeff1 = inc + coeff0; m_coeffs[v] = coeff1; if (coeff1 > INT_MAX || coeff1 < INT_MIN) { m_overflow = true; return; } if (coeff0 > 0 && inc < 0) { inc_bound(std::max((int64_t)0, coeff1) - coeff0); } else if (coeff0 < 0 && inc > 0) { inc_bound(coeff0 - std::min((int64_t)0, coeff1)); } int64_t lbound = static_cast(m_bound); // reduce coefficient to be no larger than bound. if (coeff1 > lbound) { m_coeffs[v] = lbound; } else if (coeff1 < 0 && -coeff1 > lbound) { m_coeffs[v] = -lbound; } } int64_t ba_solver::get_coeff(bool_var v) const { return m_coeffs.get(v, 0); } uint64_t ba_solver::get_coeff(literal lit) const { int64_t c1 = get_coeff(lit.var()); SASSERT((c1 < 0) == lit.sign()); int64_t c = std::abs(c1); m_overflow |= (c != c1); return static_cast(c); } ba_solver::wliteral ba_solver::get_wliteral(bool_var v) { int64_t c1 = get_coeff(v); literal l = literal(v, c1 < 0); c1 = std::abs(c1); unsigned c = static_cast(c1); // TRACE("ba", tout << l << " " << c << "\n";); m_overflow |= c != c1; return wliteral(c, l); } unsigned ba_solver::get_abs_coeff(bool_var v) const { int64_t c1 = std::abs(get_coeff(v)); unsigned c = static_cast(c1); m_overflow |= c != c1; return c; } int ba_solver::get_int_coeff(bool_var v) const { int64_t c1 = m_coeffs.get(v, 0); int c = static_cast(c1); m_overflow |= c != c1; return c; } void ba_solver::inc_bound(int64_t i) { int64_t new_bound = m_bound; new_bound += i; unsigned nb = static_cast(new_bound); m_overflow |= new_bound < 0 || nb != new_bound; m_bound = nb; } void ba_solver::reset_coeffs() { for (unsigned i = m_active_vars.size(); i-- > 0; ) { m_coeffs[m_active_vars[i]] = 0; } m_active_vars.reset(); } void ba_solver::init_visited() { m_visited_ts++; if (m_visited_ts == 0) { m_visited_ts = 1; m_visited.reset(); } while (m_visited.size() < 2*s().num_vars()) { m_visited.push_back(0); } } static bool _debug_conflict = false; static literal _debug_consequent = null_literal; static unsigned_vector _debug_var2position; // #define DEBUG_CODE(_x_) _x_ void ba_solver::bail_resolve_conflict(unsigned idx) { literal_vector const& lits = s().m_trail; while (m_num_marks > 0) { bool_var v = lits[idx].var(); if (s().is_marked(v)) { s().reset_mark(v); --m_num_marks; } if (idx == 0 && !_debug_conflict) { _debug_conflict = true; _debug_var2position.reserve(s().num_vars()); for (unsigned i = 0; i < lits.size(); ++i) { _debug_var2position[lits[i].var()] = i; } IF_VERBOSE(0, active2pb(m_A); uint64_t c = 0; for (wliteral l : m_A.m_wlits) c += l.first; verbose_stream() << "sum of coefficients: " << c << "\n"; display(verbose_stream(), m_A, true); verbose_stream() << "conflicting literal: " << s().m_not_l << "\n";); for (literal l : lits) { if (s().is_marked(l.var())) { IF_VERBOSE(0, verbose_stream() << "missing mark: " << l << "\n";); s().reset_mark(l.var()); } } m_num_marks = 0; resolve_conflict(); } --idx; } } lbool ba_solver::resolve_conflict() { if (0 == m_num_propagations_since_pop) { return l_undef; } if (s().m_config.m_pb_resolve == PB_ROUNDING) { return resolve_conflict_rs(); } m_overflow = false; reset_coeffs(); m_num_marks = 0; m_bound = 0; literal consequent = s().m_not_l; justification js = s().m_conflict; TRACE("ba", tout << consequent << " " << js << "\n";); bool unique_max; m_conflict_lvl = s().get_max_lvl(consequent, js, unique_max); if (m_conflict_lvl == 0) { return l_undef; } if (consequent != null_literal) { consequent.neg(); process_antecedent(consequent, 1); } literal_vector const& lits = s().m_trail; unsigned idx = lits.size() - 1; unsigned offset = 1; DEBUG_CODE(active2pb(m_A);); do { if (m_overflow || offset > (1 << 12)) { IF_VERBOSE(20, verbose_stream() << "offset: " << offset << "\n"; DEBUG_CODE(active2pb(m_A); display(verbose_stream(), m_A););); goto bail_out; } if (offset == 0) { goto process_next_resolvent; } DEBUG_CODE(TRACE("sat_verbose", display(tout, m_A););); TRACE("ba", tout << "process consequent: " << consequent << " : "; s().display_justification(tout, js) << "\n";); SASSERT(offset > 0); DEBUG_CODE(justification2pb(js, consequent, offset, m_B);); if (_debug_conflict) { IF_VERBOSE(0, verbose_stream() << consequent << "\n"; s().display_justification(verbose_stream(), js); verbose_stream() << "\n";); _debug_consequent = consequent; } switch(js.get_kind()) { case justification::NONE: SASSERT (consequent != null_literal); inc_bound(offset); break; case justification::BINARY: inc_bound(offset); SASSERT (consequent != null_literal); inc_coeff(consequent, offset); process_antecedent(js.get_literal(), offset); break; case justification::TERNARY: inc_bound(offset); SASSERT (consequent != null_literal); inc_coeff(consequent, offset); process_antecedent(js.get_literal1(), offset); process_antecedent(js.get_literal2(), offset); break; case justification::CLAUSE: { inc_bound(offset); clause & c = s().get_clause(js); unsigned i = 0; if (consequent != null_literal) { inc_coeff(consequent, offset); if (c[0] == consequent) { i = 1; } else { SASSERT(c[1] == consequent); process_antecedent(c[0], offset); i = 2; } } unsigned sz = c.size(); for (; i < sz; i++) process_antecedent(c[i], offset); break; } case justification::EXT_JUSTIFICATION: { constraint& cnstr = index2constraint(js.get_ext_justification_idx()); ++m_stats.m_num_resolves; switch (cnstr.tag()) { case card_t: { card& c = cnstr.to_card(); inc_bound(static_cast(offset) * c.k()); process_card(c, offset); break; } case pb_t: { pb& p = cnstr.to_pb(); m_lemma.reset(); inc_bound(offset); inc_coeff(consequent, offset); get_antecedents(consequent, p, m_lemma); TRACE("ba", display(tout, p, true); tout << m_lemma << "\n";); if (_debug_conflict) { verbose_stream() << consequent << " "; verbose_stream() << "antecedents: " << m_lemma << "\n"; } for (literal l : m_lemma) process_antecedent(~l, offset); break; } case xr_t: { // jus.push_back(js); m_lemma.reset(); inc_bound(offset); inc_coeff(consequent, offset); get_xr_antecedents(consequent, idx, js, m_lemma); for (literal l : m_lemma) process_antecedent(~l, offset); break; } default: UNREACHABLE(); break; } break; } default: UNREACHABLE(); break; } SASSERT(validate_lemma()); DEBUG_CODE( active2pb(m_C); VERIFY(validate_resolvent()); m_A = m_C; TRACE("ba", display(tout << "conflict: ", m_A););); cut(); process_next_resolvent: // find the next marked variable in the assignment stack bool_var v; while (true) { consequent = lits[idx]; v = consequent.var(); if (s().is_marked(v)) { if (s().lvl(v) == m_conflict_lvl) { break; } } if (idx == 0) { IF_VERBOSE(2, verbose_stream() << "did not find marked literal\n";); goto bail_out; } SASSERT(idx > 0); --idx; } SASSERT(lvl(v) == m_conflict_lvl); s().reset_mark(v); --idx; TRACE("sat_verbose", tout << "Unmark: v" << v << "\n";); --m_num_marks; js = s().m_justification[v]; offset = get_abs_coeff(v); if (offset > m_bound) { int64_t bound64 = static_cast(m_bound); m_coeffs[v] = (get_coeff(v) < 0) ? -bound64 : bound64; offset = m_bound; DEBUG_CODE(active2pb(m_A);); } SASSERT(value(consequent) == l_true); } while (m_num_marks > 0); DEBUG_CODE(for (bool_var i = 0; i < static_cast(s().num_vars()); ++i) SASSERT(!s().is_marked(i));); SASSERT(validate_lemma()); if (!create_asserting_lemma()) { goto bail_out; } active2lemma(); DEBUG_CODE(VERIFY(validate_conflict(m_lemma, m_A));); return l_true; bail_out: if (m_overflow) { ++m_stats.m_num_overflow; m_overflow = false; } bail_resolve_conflict(idx); return l_undef; } unsigned ba_solver::ineq::bv_coeff(bool_var v) const { for (unsigned i = size(); i-- > 0; ) { if (lit(i).var() == v) return coeff(i); } UNREACHABLE(); return 0; } void ba_solver::ineq::divide(unsigned c) { if (c == 1) return; for (unsigned i = size(); i-- > 0; ) { m_wlits[i].first = (coeff(i) + c - 1) / c; } m_k = (m_k + c - 1) / c; } /** * Remove literal at position i, subtract coefficient from bound. */ void ba_solver::ineq::weaken(unsigned i) { unsigned ci = coeff(i); SASSERT(m_k >= ci); m_k -= ci; m_wlits[i] = m_wlits.back(); m_wlits.pop_back(); } /** * Round coefficient of inequality to 1. */ void ba_solver::round_to_one(ineq& ineq, bool_var v) { unsigned c = ineq.bv_coeff(v); if (c == 1) return; unsigned sz = ineq.size(); for (unsigned i = 0; i < sz; ++i) { unsigned ci = ineq.coeff(i); unsigned q = ci % c; if (q != 0 && !is_false(ineq.lit(i))) { #if 1 // code review by Elffers: ineq.weaken(i); --i; --sz; #else if (q == ci) { ineq.weaken(i); --i; --sz; } else { ineq.m_wlits[i].first -= q; ineq.m_k -= q; } #endif } } ineq.divide(c); TRACE("ba", display(tout << "var: " << v << " " << c << ": ", ineq, true);); } void ba_solver::round_to_one(bool_var w) { unsigned c = get_abs_coeff(w); if (c == 1 || c == 0) return; for (bool_var v : m_active_vars) { wliteral wl = get_wliteral(v); unsigned q = wl.first % c; if (q != 0 && !is_false(wl.second)) { m_coeffs[v] = wl.first - q; m_bound -= q; SASSERT(m_bound > 0); } } SASSERT(validate_lemma()); divide(c); SASSERT(validate_lemma()); TRACE("ba", active2pb(m_B); display(tout, m_B, true);); } void ba_solver::divide(unsigned c) { SASSERT(c != 0); if (c == 1) return; reset_active_var_set(); unsigned j = 0, sz = m_active_vars.size(); for (unsigned i = 0; i < sz; ++i) { bool_var v = m_active_vars[i]; int ci = get_int_coeff(v); if (!test_and_set_active(v) || ci == 0) continue; if (ci > 0) { m_coeffs[v] = (ci + c - 1) / c; } else { m_coeffs[v] = -static_cast((-ci + c - 1) / c); } m_active_vars[j++] = v; } m_active_vars.shrink(j); m_bound = static_cast((m_bound + c - 1) / c); } void ba_solver::resolve_on(literal consequent) { round_to_one(consequent.var()); m_coeffs[consequent.var()] = 0; } void ba_solver::resolve_with(ineq const& ineq) { TRACE("ba", display(tout, ineq, true);); inc_bound(ineq.m_k); TRACE("ba", tout << "bound: " << m_bound << "\n";); for (unsigned i = ineq.size(); i-- > 0; ) { literal l = ineq.lit(i); inc_coeff(l, static_cast(ineq.coeff(i))); TRACE("ba", tout << "bound: " << m_bound << " lit: " << l << " coeff: " << ineq.coeff(i) << "\n";); } } void ba_solver::reset_marks(unsigned idx) { while (m_num_marks > 0) { SASSERT(idx > 0); bool_var v = s().m_trail[idx].var(); if (s().is_marked(v)) { s().reset_mark(v); --m_num_marks; } --idx; } } /** * \brief mark variables that are on the assignment stack but * below the current processing level. */ void ba_solver::mark_variables(ineq const& ineq) { for (wliteral wl : ineq.m_wlits) { literal l = wl.second; if (!is_false(l)) continue; bool_var v = l.var(); unsigned level = lvl(v); if (!s().is_marked(v) && !is_visited(v) && level == m_conflict_lvl) { s().mark(v); ++m_num_marks; } } } lbool ba_solver::resolve_conflict_rs() { if (0 == m_num_propagations_since_pop) { return l_undef; } m_overflow = false; reset_coeffs(); init_visited(); m_num_marks = 0; m_bound = 0; literal consequent = s().m_not_l; justification js = s().m_conflict; bool unique_max; m_conflict_lvl = s().get_max_lvl(consequent, js, unique_max); if (m_conflict_lvl == 0) { return l_undef; } if (consequent != null_literal) { consequent.neg(); process_antecedent(consequent, 1); } TRACE("ba", tout << consequent << " " << js << "\n";); unsigned idx = s().m_trail.size() - 1; do { TRACE("ba", s().display_justification(tout << "process consequent: " << consequent << " : ", js) << "\n"; if (consequent != null_literal) { active2pb(m_A); display(tout, m_A, true); } ); switch (js.get_kind()) { case justification::NONE: SASSERT(consequent != null_literal); round_to_one(consequent.var()); inc_bound(1); inc_coeff(consequent, 1); break; case justification::BINARY: SASSERT(consequent != null_literal); round_to_one(consequent.var()); inc_bound(1); inc_coeff(consequent, 1); process_antecedent(js.get_literal()); break; case justification::TERNARY: SASSERT(consequent != null_literal); round_to_one(consequent.var()); inc_bound(1); inc_coeff(consequent, 1); process_antecedent(js.get_literal1()); process_antecedent(js.get_literal2()); break; case justification::CLAUSE: { clause & c = s().get_clause(js); unsigned i = 0; if (consequent != null_literal) { round_to_one(consequent.var()); inc_coeff(consequent, 1); if (c[0] == consequent) { i = 1; } else { SASSERT(c[1] == consequent); process_antecedent(c[0]); i = 2; } } inc_bound(1); unsigned sz = c.size(); for (; i < sz; i++) process_antecedent(c[i]); break; } case justification::EXT_JUSTIFICATION: { ++m_stats.m_num_resolves; ext_justification_idx index = js.get_ext_justification_idx(); constraint& cnstr = index2constraint(index); SASSERT(!cnstr.was_removed()); switch (cnstr.tag()) { case card_t: case pb_t: { pb_base const& p = cnstr.to_pb_base(); unsigned k = p.k(), sz = p.size(); m_A.reset(0); for (unsigned i = 0; i < sz; ++i) { literal l = p.get_lit(i); unsigned c = p.get_coeff(i); if (l == consequent || !is_visited(l.var())) { m_A.push(l, c); } else { SASSERT(k > c); TRACE("ba", tout << "visited: " << l << "\n";); k -= c; } } SASSERT(k > 0); if (p.lit() != null_literal) m_A.push(~p.lit(), k); m_A.m_k = k; break; } default: constraint2pb(cnstr, consequent, 1, m_A); break; } mark_variables(m_A); if (consequent == null_literal) { SASSERT(validate_ineq(m_A)); m_bound = static_cast(m_A.m_k); for (wliteral wl : m_A.m_wlits) { process_antecedent(wl.second, wl.first); } } else { round_to_one(consequent.var()); if (cnstr.is_pb()) round_to_one(m_A, consequent.var()); SASSERT(validate_ineq(m_A)); resolve_with(m_A); } break; } default: UNREACHABLE(); break; } SASSERT(validate_lemma()); cut(); // find the next marked variable in the assignment stack bool_var v; while (true) { consequent = s().m_trail[idx]; v = consequent.var(); mark_visited(v); if (s().is_marked(v)) { int64_t c = get_coeff(v); if (c == 0 || ((c < 0) == consequent.sign())) { s().reset_mark(v); --m_num_marks; } else { break; } } if (idx == 0) { TRACE("ba", tout << "there is no consequent\n";); goto bail_out; } --idx; } SASSERT(lvl(v) == m_conflict_lvl); s().reset_mark(v); --idx; --m_num_marks; js = s().m_justification[v]; } while (m_num_marks > 0 && !m_overflow); TRACE("ba", active2pb(m_A); display(tout, m_A, true);); // TBD: check if this is useful if (!m_overflow && consequent != null_literal) { round_to_one(consequent.var()); } if (!m_overflow && create_asserting_lemma()) { active2lemma(); return l_true; } bail_out: TRACE("ba", tout << "bail " << m_overflow << "\n";); if (m_overflow) { ++m_stats.m_num_overflow; m_overflow = false; } return l_undef; } bool ba_solver::create_asserting_lemma() { int64_t bound64 = m_bound; int64_t slack = -bound64; reset_active_var_set(); unsigned j = 0, sz = m_active_vars.size(); for (unsigned i = 0; i < sz; ++i) { bool_var v = m_active_vars[i]; unsigned c = get_abs_coeff(v); if (!test_and_set_active(v) || c == 0) continue; slack += c; m_active_vars[j++] = v; } m_active_vars.shrink(j); m_lemma.reset(); m_lemma.push_back(null_literal); unsigned num_skipped = 0; int64_t asserting_coeff = 0; for (unsigned i = 0; 0 <= slack && i < m_active_vars.size(); ++i) { bool_var v = m_active_vars[i]; int64_t coeff = get_coeff(v); lbool val = value(v); bool is_true = val == l_true; bool append = coeff != 0 && val != l_undef && ((coeff < 0) == is_true); if (append) { literal lit(v, !is_true); if (lvl(lit) == m_conflict_lvl) { if (m_lemma[0] == null_literal) { asserting_coeff = std::abs(coeff); slack -= asserting_coeff; m_lemma[0] = ~lit; } else { ++num_skipped; if (asserting_coeff < std::abs(coeff)) { m_lemma[0] = ~lit; slack -= (std::abs(coeff) - asserting_coeff); asserting_coeff = std::abs(coeff); } } } else if (lvl(lit) < m_conflict_lvl) { slack -= std::abs(coeff); m_lemma.push_back(~lit); } } } if (slack >= 0) { TRACE("ba", tout << "slack is non-negative\n";); IF_VERBOSE(20, verbose_stream() << "(sat.card slack: " << slack << " skipped: " << num_skipped << ")\n";); return false; } if (m_overflow) { TRACE("ba", tout << "overflow\n";); return false; } if (m_lemma[0] == null_literal) { if (m_lemma.size() == 1) { s().set_conflict(justification(0)); } TRACE("ba", tout << "no asserting literal\n";); return false; } TRACE("ba", tout << m_lemma << "\n";); if (get_config().m_drat) { svector ps; // TBD fill in drat_add(m_lemma, ps); } s().m_lemma.reset(); s().m_lemma.append(m_lemma); for (unsigned i = 1; i < m_lemma.size(); ++i) { CTRACE("ba", s().is_marked(m_lemma[i].var()), tout << "marked: " << m_lemma[i] << "\n";); s().mark(m_lemma[i].var()); } return true; } /* \brief compute a cut for current resolvent. */ void ba_solver::cut() { // bypass cut if there is a unit coefficient for (bool_var v : m_active_vars) { if (1 == get_abs_coeff(v)) return; } unsigned g = 0; for (unsigned i = 0; g != 1 && i < m_active_vars.size(); ++i) { bool_var v = m_active_vars[i]; unsigned coeff = get_abs_coeff(v); if (coeff == 0) { continue; } if (m_bound < coeff) { int64_t bound64 = m_bound; if (get_coeff(v) > 0) { m_coeffs[v] = bound64; } else { m_coeffs[v] = -bound64; } coeff = m_bound; } SASSERT(0 < coeff && coeff <= m_bound); if (g == 0) { g = coeff; } else { g = u_gcd(g, coeff); } } if (g >= 2) { reset_active_var_set(); unsigned j = 0, sz = m_active_vars.size(); for (unsigned i = 0; i < sz; ++i) { bool_var v = m_active_vars[i]; int64_t c = m_coeffs[v]; if (!test_and_set_active(v) || c == 0) continue; m_coeffs[v] /= static_cast(g); m_active_vars[j++] = v; } m_active_vars.shrink(j); m_bound = (m_bound + g - 1) / g; ++m_stats.m_num_cut; } } void ba_solver::process_card(card& c, unsigned offset) { literal lit = c.lit(); SASSERT(c.k() <= c.size()); SASSERT(lit == null_literal || value(lit) != l_undef); SASSERT(0 < offset); for (unsigned i = c.k(); i < c.size(); ++i) { process_antecedent(c[i], offset); } for (unsigned i = 0; i < c.k(); ++i) { inc_coeff(c[i], offset); } if (lit != null_literal) { uint64_t offset1 = static_cast(offset) * c.k(); if (offset1 > UINT_MAX) { m_overflow = true; } if (value(lit) == l_true) { process_antecedent(~lit, static_cast(offset1)); } else { process_antecedent(lit, static_cast(offset1)); } } } void ba_solver::process_antecedent(literal l, unsigned offset) { SASSERT(value(l) == l_false); bool_var v = l.var(); unsigned level = lvl(v); if (!s().is_marked(v) && level == m_conflict_lvl) { s().mark(v); ++m_num_marks; if (_debug_conflict && _debug_consequent != null_literal && _debug_var2position[_debug_consequent.var()] < _debug_var2position[l.var()]) { IF_VERBOSE(0, verbose_stream() << "antecedent " << l << " is above consequent in stack\n";); } } inc_coeff(l, offset); } literal ba_solver::get_asserting_literal(literal p) { if (get_abs_coeff(p.var()) != 0) { return p; } unsigned level = 0; for (unsigned i = 0; i < m_active_vars.size(); ++i) { bool_var v = m_active_vars[i]; literal lit(v, get_coeff(v) < 0); if (value(lit) == l_false && lvl(lit) > level) { p = lit; level = lvl(lit); } } return p; } ba_solver::ba_solver() : m_solver(nullptr), m_lookahead(nullptr), m_unit_walk(nullptr), m_constraint_id(0), m_ba(*this), m_sort(m_ba) { TRACE("ba", tout << this << "\n";); m_num_propagations_since_pop = 0; m_max_xor_size = 5; } ba_solver::~ba_solver() { m_stats.reset(); for (constraint* c : m_constraints) { m_allocator.deallocate(c->obj_size(), c); } for (constraint* c : m_learned) { m_allocator.deallocate(c->obj_size(), c); } } void ba_solver::add_at_least(bool_var v, literal_vector const& lits, unsigned k) { literal lit = v == null_bool_var ? null_literal : literal(v, false); add_at_least(lit, lits, k, false); } ba_solver::constraint* ba_solver::add_at_least(literal lit, literal_vector const& lits, unsigned k, bool learned) { if (k == 1 && lit == null_literal) { literal_vector _lits(lits); s().mk_clause(_lits.size(), _lits.c_ptr(), learned); return nullptr; } if (!learned && clausify(lit, lits.size(), lits.c_ptr(), k)) { return nullptr; } void * mem = m_allocator.allocate(card::get_obj_size(lits.size())); card* c = new (mem) card(next_id(), lit, lits, k); c->set_learned(learned); add_constraint(c); return c; } void ba_solver::add_constraint(constraint* c) { literal_vector lits(c->literals()); if (c->learned()) { m_learned.push_back(c); } else { SASSERT(!m_solver || s().at_base_lvl()); m_constraints.push_back(c); } literal lit = c->lit(); if (c->learned() && m_solver && !s().at_base_lvl()) { SASSERT(lit == null_literal); // gets initialized after backjump. m_constraint_to_reinit.push_back(c); } else if (lit == null_literal) { init_watch(*c); } else { if (m_solver) m_solver->set_external(lit.var()); watch_literal(lit, *c); watch_literal(~lit, *c); } SASSERT(c->well_formed()); } bool ba_solver::init_watch(constraint& c) { if (inconsistent()) return false; switch (c.tag()) { case card_t: return init_watch(c.to_card()); case pb_t: return init_watch(c.to_pb()); case xr_t: return init_watch(c.to_xr()); } UNREACHABLE(); return false; } lbool ba_solver::add_assign(constraint& c, literal l) { switch (c.tag()) { case card_t: return add_assign(c.to_card(), l); case pb_t: return add_assign(c.to_pb(), l); case xr_t: return add_assign(c.to_xr(), l); } UNREACHABLE(); return l_undef; } ba_solver::constraint* ba_solver::add_pb_ge(literal lit, svector const& wlits, unsigned k, bool learned) { bool units = true; for (wliteral wl : wlits) units &= wl.first == 1; if (k == 0 && lit == null_literal) { return nullptr; } if (!learned) { for (wliteral wl : wlits) s().set_external(wl.second.var()); } if (units || k == 1) { literal_vector lits; for (wliteral wl : wlits) lits.push_back(wl.second); return add_at_least(lit, lits, k, learned); } void * mem = m_allocator.allocate(pb::get_obj_size(wlits.size())); pb* p = new (mem) pb(next_id(), lit, wlits, k); p->set_learned(learned); add_constraint(p); return p; } void ba_solver::add_pb_ge(bool_var v, svector const& wlits, unsigned k) { literal lit = v == null_bool_var ? null_literal : literal(v, false); add_pb_ge(lit, wlits, k, false); } void ba_solver::add_xr(literal_vector const& lits) { add_xr(lits, false); } bool ba_solver::all_distinct(literal_vector const& lits) { init_visited(); for (literal l : lits) { if (is_visited(l.var())) { return false; } mark_visited(l.var()); } return true; } bool ba_solver::all_distinct(xr const& x) { init_visited(); for (literal l : x) { if (is_visited(l.var())) { return false; } mark_visited(l.var()); } return true; } bool ba_solver::all_distinct(clause const& c) { init_visited(); for (literal l : c) { if (is_visited(l.var())) { return false; } mark_visited(l.var()); } return true; } literal ba_solver::add_xor_def(literal_vector& lits, bool learned) { unsigned sz = lits.size(); SASSERT (sz > 1); VERIFY(all_distinct(lits)); init_visited(); bool parity1 = true; for (literal l : lits) { mark_visited(l.var()); parity1 ^= l.sign(); } for (auto const & w : get_wlist(lits[0])) { if (w.get_kind() != watched::EXT_CONSTRAINT) continue; constraint& c = index2constraint(w.get_ext_constraint_idx()); if (!c.is_xr()) continue; xr& x = c.to_xr(); if (sz + 1 != x.size()) continue; bool is_match = true; literal l0 = null_literal; bool parity2 = true; for (literal l : x) { if (!is_visited(l.var())) { if (l0 == null_literal) { l0 = l; } else { is_match = false; break; } } else { parity2 ^= l.sign(); } } if (is_match) { SASSERT(all_distinct(x)); if (parity1 == parity2) l0.neg(); if (!learned && x.learned()) { set_non_learned(x); } return l0; } } bool_var v = s().mk_var(true, true); literal lit(v, false); lits.push_back(~lit); add_xr(lits, learned); return lit; } ba_solver::constraint* ba_solver::add_xr(literal_vector const& lits, bool learned) { void * mem = m_allocator.allocate(xr::get_obj_size(lits.size())); xr* x = new (mem) xr(next_id(), lits); x->set_learned(learned); add_constraint(x); return x; } /* \brief return true to keep watching literal. */ bool ba_solver::propagate(literal l, ext_constraint_idx idx) { SASSERT(value(l) == l_true); constraint& c = index2constraint(idx); if (c.lit() != null_literal && l.var() == c.lit().var()) { init_watch(c); return true; } else if (c.lit() != null_literal && value(c.lit()) != l_true) { // else if (c.lit() != null_literal && value(c.lit()) == l_false) { return true; } else { return l_undef != add_assign(c, ~l); } } double ba_solver::get_reward(card const& c, literal_occs_fun& literal_occs) const { unsigned k = c.k(), slack = 0; bool do_add = get_config().m_lookahead_reward == heule_schur_reward; double to_add = do_add ? 0: 1; for (literal l : c) { switch (value(l)) { case l_true: --k; if (k == 0) return 0; case l_undef: if (do_add) to_add += literal_occs(l); ++slack; break; case l_false: break; } } if (k >= slack) return 1; return pow(0.5, slack - k + 1) * to_add; } double ba_solver::get_reward(pb const& c, literal_occs_fun& occs) const { unsigned k = c.k(), slack = 0; bool do_add = get_config().m_lookahead_reward == heule_schur_reward; double to_add = do_add ? 0 : 1; double undefs = 0; for (wliteral wl : c) { literal l = wl.second; unsigned w = wl.first; switch (value(l)) { case l_true: if (k <= w) return 0; case l_undef: if (do_add) to_add += occs(l); ++undefs; slack += w; break; // TBD multiplier factor on this case l_false: break; } } if (k >= slack || 0 == undefs) return 0; double avg = slack / undefs; return pow(0.5, (slack - k + 1)/avg) * to_add; } double ba_solver::get_reward(literal l, ext_justification_idx idx, literal_occs_fun& occs) const { constraint const& c = index2constraint(idx); switch (c.tag()) { case card_t: return get_reward(c.to_card(), occs); case pb_t: return get_reward(c.to_pb(), occs); case xr_t: return 0; default: UNREACHABLE(); return 0; } } void ba_solver::ensure_parity_size(bool_var v) { if (m_parity_marks.size() <= static_cast(v)) { m_parity_marks.resize(static_cast(v) + 1, 0); } } unsigned ba_solver::get_parity(bool_var v) { return m_parity_marks.get(v, 0); } void ba_solver::inc_parity(bool_var v) { ensure_parity_size(v); m_parity_marks[v]++; } void ba_solver::reset_parity(bool_var v) { ensure_parity_size(v); m_parity_marks[v] = 0; } /** \brief perform parity resolution on xr premises. The idea is to collect premises based on xr resolvents. Variables that are repeated an even number of times cancel out. */ void ba_solver::get_xr_antecedents(literal l, unsigned index, justification js, literal_vector& r) { unsigned level = lvl(l); bool_var v = l.var(); SASSERT(js.get_kind() == justification::EXT_JUSTIFICATION); TRACE("ba", tout << l << ": " << js << "\n"; for (unsigned i = 0; i <= index; ++i) tout << s().m_trail[i] << " "; tout << "\n"; s().display_units(tout); ); unsigned num_marks = 0; while (true) { TRACE("ba", tout << "process: " << l << " " << js << "\n";); if (js.get_kind() == justification::EXT_JUSTIFICATION) { constraint& c = index2constraint(js.get_ext_justification_idx()); TRACE("ba", tout << c << "\n";); if (!c.is_xr()) { r.push_back(l); } else { xr& x = c.to_xr(); if (x[1].var() == l.var()) { x.swap(0, 1); } VERIFY(x[0].var() == l.var()); for (unsigned i = 1; i < x.size(); ++i) { literal lit(value(x[i]) == l_true ? x[i] : ~x[i]); inc_parity(lit.var()); if (lvl(lit) == level) { TRACE("ba", tout << "mark: " << lit << "\n";); ++num_marks; } else { m_parity_trail.push_back(lit); } } } } else { r.push_back(l); } bool found = false; while (num_marks > 0) { l = s().m_trail[index]; v = l.var(); unsigned n = get_parity(v); if (n > 0 && lvl(l) == level) { reset_parity(v); num_marks -= n; if (n % 2 == 1) { found = true; break; } } --index; } if (!found) { break; } --index; js = s().m_justification[v]; } // now walk the defined literals for (literal lit : m_parity_trail) { if (get_parity(lit.var()) % 2 == 1) { r.push_back(lit); } else { // IF_VERBOSE(2, verbose_stream() << "skip even parity: " << lit << "\n";); } reset_parity(lit.var()); } m_parity_trail.reset(); TRACE("ba", tout << r << "\n";); } /** \brief retrieve a sufficient set of literals from p that imply l. Find partition: - Ax + coeff*l + B*y >= k - all literals in x are false. - B < k Then x is an explanation for l */ bool ba_solver::assigned_above(literal above, literal below) { unsigned l = lvl(above); SASSERT(l == lvl(below)); if (l == 0) return false; unsigned start = s().m_scopes[l-1].m_trail_lim; literal_vector const& lits = s().m_trail; #if 0 IF_VERBOSE(10, verbose_stream() << "level " << l << " scope level " << s().scope_lvl() << " tail lim start: " << start << " size of lits: " << lits.size() << " num scopes " << s().m_scopes.size() << "\n";); #endif for (unsigned sz = lits.size(); sz-- > start; ) { if (lits[sz] == above) return true; if (lits[sz] == below) return false; } UNREACHABLE(); return false; } void ba_solver::get_antecedents(literal l, pb const& p, literal_vector& r) { TRACE("ba", display(tout << l << " level: " << s().scope_lvl() << " ", p, true);); SASSERT(p.lit() == null_literal || value(p.lit()) == l_true); if (p.lit() != null_literal) { r.push_back(p.lit()); } unsigned k = p.k(); if (_debug_conflict) { IF_VERBOSE(0, display(verbose_stream(), p, true); verbose_stream() << "literal: " << l << " value: " << value(l) << " num-watch: " << p.num_watch() << " slack: " << p.slack() << "\n";); } if (value(l) == l_false) { // The literal comes from a conflict. // it is forced true, but assigned to false. unsigned slack = 0; for (wliteral wl : p) { if (value(wl.second) != l_false) { slack += wl.first; } } SASSERT(slack < k); for (wliteral wl : p) { literal lit = wl.second; if (lit != l && value(lit) == l_false) { unsigned w = wl.first; if (slack + w < k) { slack += w; } else { r.push_back(~lit); } } } } else { // comes from a unit propagation SASSERT(value(l) == l_true); unsigned coeff = 0, j = 0; for (; j < p.size(); ++j) { if (p[j].second == l) { coeff = p[j].first; break; } } ++j; if (j < p.num_watch()) { j = p.num_watch(); } CTRACE("ba", coeff == 0, display(tout << l << " coeff: " << coeff << "\n", p, true);); if (_debug_conflict) { std::cout << "coeff " << coeff << "\n"; } SASSERT(coeff > 0); unsigned slack = p.max_sum() - coeff; // we need antecedents to be deeper than alit. for (; j < p.size(); ++j) { literal lit = p[j].second; unsigned w = p[j].first; if (l_false != value(lit)) { // skip } else if (lvl(lit) > lvl(l)) { // skip } else if (lvl(lit) == lvl(l) && assigned_above(~lit, l)) { // skip } else if (slack + w < k) { slack += w; } else { r.push_back(~lit); } } } SASSERT(validate_unit_propagation(p, r, l)); } bool ba_solver::is_extended_binary(ext_justification_idx idx, literal_vector & r) { constraint const& c = index2constraint(idx); switch (c.tag()) { case card_t: { card const& ca = c.to_card(); if (ca.size() == ca.k() + 1 && ca.lit() == null_literal) { r.reset(); for (literal l : ca) r.push_back(l); return true; } else { return false; } } default: return false; } } void ba_solver::simplify(xr& x) { if (x.learned()) { x.set_removed(); m_constraint_removed = true; } } void ba_solver::get_antecedents(literal l, card const& c, literal_vector& r) { if (l == ~c.lit()) { for (unsigned i = c.k() - 1; i < c.size(); ++i) { VERIFY(value(c[i]) == l_false); r.push_back(~c[i]); } return; } DEBUG_CODE( bool found = false; for (unsigned i = 0; !found && i < c.k(); ++i) { found = c[i] == l; } CTRACE("ba",!found, s().display(tout << l << ":" << c << "\n");); SASSERT(found);); VERIFY(c.lit() == null_literal || value(c.lit()) != l_false); if (c.lit() != null_literal) r.push_back(value(c.lit()) == l_true ? c.lit() : ~c.lit()); for (unsigned i = c.k(); i < c.size(); ++i) { SASSERT(value(c[i]) == l_false); r.push_back(~c[i]); } } void ba_solver::get_antecedents(literal l, xr const& x, literal_vector& r) { if (x.lit() != null_literal) r.push_back(x.lit()); // TRACE("ba", display(tout << l << " ", x, true);); SASSERT(x.lit() == null_literal || value(x.lit()) == l_true); SASSERT(x[0].var() == l.var() || x[1].var() == l.var()); if (x[0].var() == l.var()) { SASSERT(value(x[1]) != l_undef); r.push_back(value(x[1]) == l_true ? x[1] : ~x[1]); } else { SASSERT(value(x[0]) != l_undef); r.push_back(value(x[0]) == l_true ? x[0] : ~x[0]); } for (unsigned i = 2; i < x.size(); ++i) { SASSERT(value(x[i]) != l_undef); r.push_back(value(x[i]) == l_true ? x[i] : ~x[i]); } } // ---------------------------- // constraint generic methods void ba_solver::get_antecedents(literal l, ext_justification_idx idx, literal_vector & r) { get_antecedents(l, index2constraint(idx), r); } bool ba_solver::is_watched(literal lit, constraint const& c) const { return get_wlist(~lit).contains(watched(c.index())); } void ba_solver::unwatch_literal(literal lit, constraint& c) { watched w(c.index()); get_wlist(~lit).erase(w); SASSERT(!is_watched(lit, c)); } void ba_solver::watch_literal(literal lit, constraint& c) { if (c.is_pure() && lit == ~c.lit()) return; SASSERT(!is_watched(lit, c)); watched w(c.index()); get_wlist(~lit).push_back(w); } void ba_solver::get_antecedents(literal l, constraint const& c, literal_vector& r) { switch (c.tag()) { case card_t: get_antecedents(l, c.to_card(), r); break; case pb_t: get_antecedents(l, c.to_pb(), r); break; case xr_t: get_antecedents(l, c.to_xr(), r); break; default: UNREACHABLE(); break; } } void ba_solver::nullify_tracking_literal(constraint& c) { if (c.lit() != null_literal) { unwatch_literal(c.lit(), c); unwatch_literal(~c.lit(), c); c.nullify_literal(); } } void ba_solver::clear_watch(constraint& c) { switch (c.tag()) { case card_t: clear_watch(c.to_card()); break; case pb_t: clear_watch(c.to_pb()); break; case xr_t: clear_watch(c.to_xr()); break; default: UNREACHABLE(); } } void ba_solver::remove_constraint(constraint& c, char const* reason) { TRACE("ba", display(tout << "remove ", c, true) << " " << reason << "\n";); IF_VERBOSE(21, display(verbose_stream() << "remove " << reason << " ", c, true);); nullify_tracking_literal(c); clear_watch(c); c.set_removed(); m_constraint_removed = true; } // -------------------------------- // validation bool ba_solver::validate_unit_propagation(constraint const& c, literal l) const { return true; switch (c.tag()) { case card_t: return validate_unit_propagation(c.to_card(), l); case pb_t: return validate_unit_propagation(c.to_pb(), l); case xr_t: return true; default: UNREACHABLE(); break; } return false; } bool ba_solver::validate_conflict(constraint const& c) const { return eval(c) == l_false; } lbool ba_solver::eval(constraint const& c) const { lbool v1 = c.lit() == null_literal ? l_true : value(c.lit()); switch (c.tag()) { case card_t: return eval(v1, eval(c.to_card())); case pb_t: return eval(v1, eval(c.to_pb())); case xr_t: return eval(v1, eval(c.to_xr())); default: UNREACHABLE(); break; } return l_undef; } lbool ba_solver::eval(model const& m, constraint const& c) const { lbool v1 = c.lit() == null_literal ? l_true : value(m, c.lit()); switch (c.tag()) { case card_t: return eval(v1, eval(m, c.to_card())); case pb_t: return eval(v1, eval(m, c.to_pb())); case xr_t: return eval(v1, eval(m, c.to_xr())); default: UNREACHABLE(); break; } return l_undef; } lbool ba_solver::eval(lbool a, lbool b) const { if (a == l_undef || b == l_undef) return l_undef; return (a == b) ? l_true : l_false; } lbool ba_solver::eval(card const& c) const { unsigned trues = 0, undefs = 0; for (literal l : c) { switch (value(l)) { case l_true: trues++; break; case l_undef: undefs++; break; default: break; } } if (trues + undefs < c.k()) return l_false; if (trues >= c.k()) return l_true; return l_undef; } lbool ba_solver::eval(model const& m, card const& c) const { unsigned trues = 0, undefs = 0; for (literal l : c) { switch (value(m, l)) { case l_true: trues++; break; case l_undef: undefs++; break; default: break; } } if (trues + undefs < c.k()) return l_false; if (trues >= c.k()) return l_true; return l_undef; } lbool ba_solver::eval(model const& m, pb const& p) const { unsigned trues = 0, undefs = 0; for (wliteral wl : p) { switch (value(m, wl.second)) { case l_true: trues += wl.first; break; case l_undef: undefs += wl.first; break; default: break; } } if (trues + undefs < p.k()) return l_false; if (trues >= p.k()) return l_true; return l_undef; } lbool ba_solver::eval(pb const& p) const { unsigned trues = 0, undefs = 0; for (wliteral wl : p) { switch (value(wl.second)) { case l_true: trues += wl.first; break; case l_undef: undefs += wl.first; break; default: break; } } if (trues + undefs < p.k()) return l_false; if (trues >= p.k()) return l_true; return l_undef; } lbool ba_solver::eval(xr const& x) const { bool odd = false; for (auto l : x) { switch (value(l)) { case l_true: odd = !odd; break; case l_false: break; default: return l_undef; } } return odd ? l_true : l_false; } lbool ba_solver::eval(model const& m, xr const& x) const { bool odd = false; for (auto l : x) { switch (value(m, l)) { case l_true: odd = !odd; break; case l_false: break; default: return l_undef; } } return odd ? l_true : l_false; } bool ba_solver::validate() { if (!validate_watch_literals()) { return false; } for (constraint* c : m_constraints) { if (!validate_watched_constraint(*c)) return false; } for (constraint* c : m_learned) { if (!validate_watched_constraint(*c)) return false; } return true; } bool ba_solver::validate_watch_literals() const { for (unsigned v = 0; v < s().num_vars(); ++v) { literal lit(v, false); if (lvl(lit) == 0) continue; if (!validate_watch_literal(lit)) return false; if (!validate_watch_literal(~lit)) return false; } return true; } bool ba_solver::validate_watch_literal(literal lit) const { if (lvl(lit) == 0) return true; for (auto const & w : get_wlist(lit)) { if (w.get_kind() == watched::EXT_CONSTRAINT) { constraint const& c = index2constraint(w.get_ext_constraint_idx()); if (!c.is_watching(~lit) && lit.var() != c.lit().var()) { IF_VERBOSE(0, display(verbose_stream() << lit << " " << lvl(lit) << " is not watched in " << c << "\n", c, true);); UNREACHABLE(); return false; } } } return true; } bool ba_solver::validate_watched_constraint(constraint const& c) const { if (c.is_pb() && !validate_watch(c.to_pb(), null_literal)) { return false; } if (c.lit() != null_literal && value(c.lit()) != l_true) return true; SASSERT(c.lit() == null_literal || lvl(c.lit()) == 0 || (is_watched(c.lit(), c) && is_watched(~c.lit(), c))); if (eval(c) == l_true) { return true; } literal_vector lits(c.literals()); for (literal l : lits) { if (lvl(l) == 0) continue; bool found = is_watched(l, c); if (found != c.is_watching(l)) { IF_VERBOSE(0, verbose_stream() << "Discrepancy of watched literal: " << l << " id: " << c.id() << " clause: " << c << (found?" is watched, but shouldn't be":" not watched, but should be") << "\n"; s().display_watch_list(verbose_stream() << l << ": ", get_wlist(l)) << "\n"; s().display_watch_list(verbose_stream() << ~l << ": ", get_wlist(~l)) << "\n"; verbose_stream() << "value: " << value(l) << " level: " << lvl(l) << "\n"; display(verbose_stream(), c, true); if (c.lit() != null_literal) verbose_stream() << value(c.lit()) << "\n";); IF_VERBOSE(0, s().display_watches(verbose_stream())); UNREACHABLE(); return false; } } return true; } bool ba_solver::validate_watch(pb const& p, literal alit) const { for (unsigned i = 0; i < p.size(); ++i) { literal l = p[i].second; if (l != alit && lvl(l) != 0 && is_watched(l, p) != (i < p.num_watch())) { IF_VERBOSE(0, display(verbose_stream(), p, true); verbose_stream() << "literal " << l << " at position " << i << " " << is_watched(l, p) << "\n";); UNREACHABLE(); return false; } } unsigned slack = 0; for (unsigned i = 0; i < p.num_watch(); ++i) { slack += p[i].first; } if (slack != p.slack()) { IF_VERBOSE(0, display(verbose_stream(), p, true);); UNREACHABLE(); return false; } return true; } /** \brief Lex on (glue, size) */ struct constraint_glue_psm_lt { bool operator()(ba_solver::constraint const * c1, ba_solver::constraint const * c2) const { return (c1->glue() < c2->glue()) || (c1->glue() == c2->glue() && (c1->psm() < c2->psm() || (c1->psm() == c2->psm() && c1->size() < c2->size()))); } }; void ba_solver::update_psm(constraint& c) const { unsigned r = 0; switch (c.tag()) { case card_t: for (literal l : c.to_card()) { if (s().m_phase[l.var()] == !l.sign()) ++r; } break; case pb_t: for (wliteral l : c.to_pb()) { if (s().m_phase[l.second.var()] == !l.second.sign()) ++r; } break; default: break; } c.set_psm(r); } unsigned ba_solver::max_var(unsigned w) const { for (constraint* cp : m_constraints) { w = cp->fold_max_var(w); } for (constraint* cp : m_learned) { w = cp->fold_max_var(w); } return w; } void ba_solver::gc() { if (m_learned.size() >= 2 * m_constraints.size() && (s().at_search_lvl() || s().at_base_lvl())) { for (auto & c : m_learned) update_psm(*c); std::stable_sort(m_learned.begin(), m_learned.end(), constraint_glue_psm_lt()); gc_half("glue-psm"); cleanup_constraints(m_learned, true); } } void ba_solver::gc_half(char const* st_name) { TRACE("ba", tout << "gc\n";); unsigned sz = m_learned.size(); unsigned new_sz = sz/2; unsigned removed = 0; for (unsigned i = new_sz; i < sz; i++) { constraint* c = m_learned[i]; if (!m_constraint_to_reinit.contains(c)) { remove_constraint(*c, "gc"); m_allocator.deallocate(c->obj_size(), c); ++removed; } else { m_learned[new_sz++] = c; } } m_stats.m_num_gc += removed; m_learned.shrink(new_sz); IF_VERBOSE(2, verbose_stream() << "(sat-gc :strategy " << st_name << " :deleted " << removed << ")\n";); } lbool ba_solver::add_assign(card& c, literal alit) { // literal is assigned to false. unsigned sz = c.size(); unsigned bound = c.k(); TRACE("ba", tout << "assign: " << c.lit() << ": " << ~alit << "@" << lvl(~alit) << " " << c << "\n";); SASSERT(0 < bound && bound <= sz); if (bound == sz) { if (c.lit() != null_literal && value(c.lit()) == l_undef) { assign(c, ~c.lit()); return inconsistent() ? l_false : l_true; } set_conflict(c, alit); return l_false; } SASSERT(value(alit) == l_false); VERIFY(c.lit() == null_literal || value(c.lit()) != l_false); unsigned index = 0; for (index = 0; index <= bound; ++index) { if (c[index] == alit) { break; } } if (index == bound + 1) { // literal is no longer watched. return l_undef; } VERIFY(index <= bound); VERIFY(c[index] == alit); // find a literal to swap with: for (unsigned i = bound + 1; i < sz; ++i) { literal lit2 = c[i]; if (value(lit2) != l_false) { c.swap(index, i); watch_literal(lit2, c); return l_undef; } } // conflict if (bound != index && value(c[bound]) == l_false) { TRACE("ba", tout << "conflict " << c[bound] << " " << alit << "\n";); if (c.lit() != null_literal && value(c.lit()) == l_undef) { if (index + 1 < bound) c.swap(index, bound - 1); assign(c, ~c.lit()); return inconsistent() ? l_false : l_true; } set_conflict(c, alit); return l_false; } if (index != bound) { c.swap(index, bound); } // TRACE("ba", tout << "no swap " << index << " " << alit << "\n";); // there are no literals to swap with, // prepare for unit propagation by swapping the false literal into // position bound. Then literals in positions 0..bound-1 have to be // assigned l_true. if (c.lit() != null_literal && value(c.lit()) == l_undef) { return l_true; } for (unsigned i = 0; i < bound; ++i) { assign(c, c[i]); } if (c.learned() && c.glue() > 2) { unsigned glue; if (s().num_diff_false_levels_below(c.size(), c.begin(), c.glue()-1, glue)) { c.set_glue(glue); } } return inconsistent() ? l_false : l_true; } void ba_solver::asserted(literal l) { } check_result ba_solver::check() { return CR_DONE; } void ba_solver::push() { m_constraint_to_reinit_lim.push_back(m_constraint_to_reinit.size()); } void ba_solver::pop(unsigned n) { TRACE("sat_verbose", tout << "pop:" << n << "\n";); unsigned new_lim = m_constraint_to_reinit_lim.size() - n; m_constraint_to_reinit_last_sz = m_constraint_to_reinit_lim[new_lim]; m_constraint_to_reinit_lim.shrink(new_lim); m_num_propagations_since_pop = 0; } void ba_solver::pop_reinit() { unsigned sz = m_constraint_to_reinit_last_sz; for (unsigned i = sz; i < m_constraint_to_reinit.size(); ++i) { constraint* c = m_constraint_to_reinit[i]; if (!init_watch(*c) && !s().at_base_lvl()) { m_constraint_to_reinit[sz++] = c; } } m_constraint_to_reinit.shrink(sz); } void ba_solver::simplify(constraint& c) { SASSERT(s().at_base_lvl()); switch (c.tag()) { case card_t: simplify(c.to_card()); break; case pb_t: simplify(c.to_pb()); break; case xr_t: simplify(c.to_xr()); break; default: UNREACHABLE(); } } void ba_solver::pre_simplify() { VERIFY(s().at_base_lvl()); barbet_init_parity(); m_constraint_removed = false; for (unsigned sz = m_constraints.size(), i = 0; i < sz; ++i) pre_simplify(*m_constraints[i]); for (unsigned sz = m_learned.size(), i = 0; i < sz; ++i) pre_simplify(*m_learned[i]); bool change = m_constraint_removed; cleanup_constraints(); if (change) { // remove non-used variables. init_use_lists(); remove_unused_defs(); set_non_external(); } } void ba_solver::pre_simplify(constraint& c) { if (c.is_xr() && c.size() <= m_max_xor_size) { unsigned sz = c.size(); literal_vector& lits = m_barbet_clause; bool parity = false; xr const& x = c.to_xr(); for (literal lit : x) { parity ^= lit.sign(); } // IF_VERBOSE(0, verbose_stream() << "blast: " << c << "\n"); for (unsigned i = 0; i < (1ul << sz); ++i) { if (m_barbet_parity[sz][i] == parity) { lits.reset(); for (unsigned j = 0; j < sz; ++j) { lits.push_back(literal(x[j].var(), (0 != (i & (1 << j))))); } // IF_VERBOSE(0, verbose_stream() << lits << "\n"); s().mk_clause(lits); } } c.set_removed(); m_constraint_removed = true; } } void ba_solver::simplify() { if (!s().at_base_lvl()) s().pop_to_base_level(); unsigned trail_sz, count = 0; do { trail_sz = s().init_trail_size(); m_simplify_change = false; m_clause_removed = false; m_constraint_removed = false; for (unsigned sz = m_constraints.size(), i = 0; i < sz; ++i) simplify(*m_constraints[i]); for (unsigned sz = m_learned.size(), i = 0; i < sz; ++i) simplify(*m_learned[i]); init_use_lists(); remove_unused_defs(); set_non_external(); elim_pure(); for (unsigned sz = m_constraints.size(), i = 0; i < sz; ++i) subsumption(*m_constraints[i]); for (unsigned sz = m_learned.size(), i = 0; i < sz; ++i) subsumption(*m_learned[i]); unit_strengthen(); extract_xor(); merge_xor(); cleanup_clauses(); cleanup_constraints(); update_pure(); count++; } while (count < 10 && (m_simplify_change || trail_sz < s().init_trail_size())); // validate_eliminated(); IF_VERBOSE(1, unsigned subs = m_stats.m_num_bin_subsumes + m_stats.m_num_clause_subsumes + m_stats.m_num_pb_subsumes; verbose_stream() << "(ba.simplify" << " :constraints " << m_constraints.size(); if (!m_learned.empty()) verbose_stream() << " :lemmas " << m_learned.size(); if (subs > 0) verbose_stream() << " :subsumes " << subs; if (m_stats.m_num_gc > 0) verbose_stream() << " :gc " << m_stats.m_num_gc; verbose_stream() << ")\n";); // IF_VERBOSE(0, s().display(verbose_stream())); // mutex_reduction(); // if (s().m_clauses.size() < 80000) lp_lookahead_reduction(); } /* * ~lit does not occur in clauses * ~lit is only in one constraint use list * lit == C * -> ignore assignments to ~lit for C * * ~lit does not occur in clauses * lit is only in one constraint use list * lit == C * -> negate: ~lit == ~C */ void ba_solver::update_pure() { //return; for (constraint* cp : m_constraints) { literal lit = cp->lit(); if (lit != null_literal && !cp->is_pure() && value(lit) == l_undef && get_wlist(~lit).size() == 1 && m_clause_use_list.get(lit).empty()) { clear_watch(*cp); cp->negate(); lit.neg(); } if (lit != null_literal && !cp->is_pure() && m_cnstr_use_list[(~lit).index()].size() == 1 && get_wlist(lit).size() == 1 && m_clause_use_list.get(~lit).empty()) { cp->set_pure(); get_wlist(~lit).erase(watched(cp->index())); // just ignore assignments to false } } } void ba_solver::mutex_reduction() { literal_vector lits; for (unsigned v = 0; v < s().num_vars(); ++v) { lits.push_back(literal(v, false)); lits.push_back(literal(v, true)); } vector mutexes; s().find_mutexes(lits, mutexes); for (literal_vector& mux : mutexes) { if (mux.size() > 2) { IF_VERBOSE(1, verbose_stream() << "mux: " << mux << "\n";); for (unsigned i = 0; i < mux.size(); ++i) mux[i].neg(); add_at_least(null_literal, mux, mux.size() - 1, false); } } } // ------------------------- // sorting networks literal ba_solver::ba_sort::mk_false() { return ~mk_true(); } literal ba_solver::ba_sort::mk_true() { if (m_true == null_literal) { bool_var v = s.s().mk_var(false, false); m_true = literal(v, false); s.s().mk_clause(1,&m_true); } VERIFY(m_true != null_literal); return m_true; } literal ba_solver::ba_sort::mk_not(literal l) { return ~l; } literal ba_solver::ba_sort::fresh(char const*) { bool_var v = s.s().mk_var(false, true); return literal(v, false); } literal ba_solver::ba_sort::mk_max(unsigned n, literal const* lits) { m_lits.reset(); for (unsigned i = 0; i < n; ++i) { if (lits[i] == m_true) return m_true; if (lits[i] == ~m_true) continue; m_lits.push_back(lits[i]); } switch (m_lits.size()) { case 0: return ~m_true; case 1: return m_lits[0]; default: { literal max = fresh("max"); for (unsigned i = 0; i < n; ++i) { s.s().mk_clause(~m_lits[i], max); } m_lits.push_back(~max); s.s().mk_clause(m_lits.size(), m_lits.c_ptr()); return max; } } } literal ba_solver::ba_sort::mk_min(unsigned n, literal const* lits) { m_lits.reset(); for (unsigned i = 0; i < n; ++i) { if (lits[i] == ~m_true) return ~m_true; if (lits[i] == m_true) continue; m_lits.push_back(lits[i]); } switch (m_lits.size()) { case 0: return m_true; case 1: return m_lits[0]; default: { literal min = fresh("min"); for (unsigned i = 0; i < n; ++i) { s.s().mk_clause(~min, m_lits[i]); m_lits[i] = ~m_lits[i]; } m_lits.push_back(min); s.s().mk_clause(m_lits.size(), m_lits.c_ptr()); return min; } } } void ba_solver::ba_sort::mk_clause(unsigned n, literal const* lits) { m_lits.reset(); m_lits.append(n, lits); s.s().mk_clause(n, m_lits.c_ptr()); } std::ostream& ba_solver::ba_sort::pp(std::ostream& out, literal l) const { return out << l; } // ------------------------------- // set literals equivalent void ba_solver::reserve_roots() { m_root_vars.reserve(s().num_vars(), false); for (unsigned i = m_roots.size(); i < 2 * s().num_vars(); ++i) { m_roots.push_back(to_literal(i)); } } bool ba_solver::set_root(literal l, literal r) { if (s().is_assumption(l.var())) { return false; } reserve_roots(); m_roots[l.index()] = r; m_roots[(~l).index()] = ~r; m_root_vars[l.var()] = true; return true; } void ba_solver::flush_roots() { if (m_roots.empty()) return; reserve_roots(); // validate(); m_visited.resize(s().num_vars()*2, false); m_constraint_removed = false; for (unsigned sz = m_constraints.size(), i = 0; i < sz; ++i) flush_roots(*m_constraints[i]); for (unsigned sz = m_learned.size(), i = 0; i < sz; ++i) flush_roots(*m_learned[i]); cleanup_constraints(); // validate(); // validate_eliminated(); } void ba_solver::validate_eliminated() { validate_eliminated(m_constraints); validate_eliminated(m_learned); } void ba_solver::validate_eliminated(ptr_vector const& cs) { for (constraint const* c : cs) { if (c->learned()) continue; switch (c->tag()) { case tag_t::card_t: for (literal l : c->to_card()) { VERIFY(!s().was_eliminated(l.var())); } break; case tag_t::pb_t: for (wliteral wl : c->to_pb()) { VERIFY(!s().was_eliminated(wl.second.var())); } break; case tag_t::xr_t: for (literal l : c->to_xr()) { VERIFY(!s().was_eliminated(l.var())); } break; } } } void ba_solver::recompile(constraint& c) { if (c.id() == _bad_id) { IF_VERBOSE(0, display(verbose_stream() << "recompile\n", c, true);); } switch (c.tag()) { case card_t: recompile(c.to_card()); break; case pb_t: recompile(c.to_pb()); break; case xr_t: NOT_IMPLEMENTED_YET(); break; default: UNREACHABLE(); } } void ba_solver::recompile(card& c) { SASSERT(c.lit() == null_literal || is_watched(c.lit(), c)); // pre-condition is that the literals, except c.lit(), in c are unwatched. if (c.id() == _bad_id) std::cout << "recompile: " << c << "\n"; m_weights.resize(2*s().num_vars(), 0); for (literal l : c) { ++m_weights[l.index()]; } unsigned k = c.k(); bool all_units = true; unsigned sz = c.size(); unsigned_vector coeffs; literal_vector lits; unsigned j = 0; for (unsigned i = 0; i < sz && 0 < k; ++i) { literal l = c[i]; unsigned w = m_weights[l.index()]; unsigned w2 = m_weights[(~l).index()]; if (w == 0 || w < w2) { continue; } else if (k <= w2) { k = 0; break; } else { SASSERT(w2 <= w && w2 < k); k -= w2; w -= w2; m_weights[(~l).index()] = 0; m_weights[l.index()] = 0; if (w == 0) { continue; } else { all_units &= (w == 1); coeffs.push_back(w); c[j++] = l; } } } sz = j; // clear weights for (literal l : c) { m_weights[l.index()] = 0; m_weights[(~l).index()] = 0; } if (k == 0 && c.lit() == null_literal) { remove_constraint(c, "recompiled to true"); return; } if (k == 1 && c.lit() == null_literal) { literal_vector lits(sz, c.literals().c_ptr()); s().mk_clause(sz, lits.c_ptr(), c.learned()); remove_constraint(c, "recompiled to clause"); return; } if (sz == 0) { if (c.lit() == null_literal) { if (k > 0) { s().mk_clause(0, nullptr, true); } } else if (k > 0) { literal lit = ~c.lit(); s().mk_clause(1, &lit, c.learned()); } else { literal lit = c.lit(); s().mk_clause(1, &lit, c.learned()); } remove_constraint(c, "recompiled to clause"); return; } if (all_units && sz < k) { if (c.lit() == null_literal) { s().mk_clause(0, nullptr, true); } else { literal lit = ~c.lit(); s().mk_clause(1, &lit, c.learned()); } remove_constraint(c, "recompiled to clause"); return; } VERIFY(!all_units || c.size() - c.k() >= sz - k); c.set_size(sz); c.set_k(k); if (all_units && clausify(c)) { return; } if (!all_units) { TRACE("ba", tout << "replacing by pb: " << c << "\n";); m_wlits.reset(); for (unsigned i = 0; i < sz; ++i) { m_wlits.push_back(wliteral(coeffs[i], c[i])); } literal root = c.lit(); remove_constraint(c, "recompiled to pb"); add_pb_ge(root, m_wlits, k, c.learned()); } else { if (c.lit() == null_literal || value(c.lit()) == l_true) { init_watch(c); } SASSERT(c.lit() == null_literal || is_watched(c.lit(), c)); SASSERT(c.well_formed()); } } bool ba_solver::clausify(literal lit, unsigned n, literal const* lits, unsigned k) { return false; bool is_def = lit != null_literal; if ((!is_def || !s().was_eliminated(lit)) && !std::any_of(lits, lits + n, [&](literal l) { return s().was_eliminated(l); })) { literal def_lit = m_sort.ge(is_def, k, n, lits); if (is_def) { s().mk_clause(~lit, def_lit); s().mk_clause( lit, ~def_lit); } return true; } return false; } bool ba_solver::clausify(xr& x) { return false; } bool ba_solver::clausify(card& c) { return false; if (get_config().m_card_solver) return false; // // TBD: conditions for when to clausify are TBD and // handling of conditional cardinality as well. // if (!c.learned() && clausify(c.lit(), c.size(), c.begin(), c.k())) { IF_VERBOSE(1, verbose_stream() << "clausify " << c << "\n";); // compiled } remove_constraint(c, "recompiled to clauses"); return true; } bool ba_solver::clausify(pb& p) { return false; if (get_config().m_card_solver) return false; bool ok = !p.learned(); bool is_def = p.lit() != null_literal; for (wliteral wl : p) { ok &= !s().was_eliminated(wl.second); } ok &= !is_def || !s().was_eliminated(p.lit()); if (!ok) { remove_constraint(p, "recompiled to clauses"); return true; } if (is_cardinality(p, m_lemma)) { literal lit = m_sort.ge(is_def, p.k(), m_lemma.size(), m_lemma.c_ptr()); if (is_def) { s().mk_clause(p.lit(), ~lit); s().mk_clause(~p.lit(), lit); } remove_constraint(p, "recompiled to clauses"); return true; } return false; } bool ba_solver::is_cardinality(pb const& p, literal_vector& lits) { lits.reset(); p.size(); for (wliteral wl : p) { if (lits.size() > 2*p.size() + wl.first) { return false; } for (unsigned i = 0; i < wl.first; ++i) { lits.push_back(wl.second); } } return true; } void ba_solver::split_root(constraint& c) { switch (c.tag()) { case card_t: split_root(c.to_card()); break; case pb_t: split_root(c.to_pb()); break; case xr_t: NOT_IMPLEMENTED_YET(); break; } } void ba_solver::flush_roots(constraint& c) { if (c.lit() != null_literal && !is_watched(c.lit(), c)) { watch_literal(c.lit(), c); watch_literal(~c.lit(), c); } SASSERT(c.lit() == null_literal || is_watched(c.lit(), c)); bool found = c.lit() != null_literal && m_root_vars[c.lit().var()]; for (unsigned i = 0; !found && i < c.size(); ++i) { found = m_root_vars[c.get_lit(i).var()]; } if (!found) return; clear_watch(c); // this could create duplicate literals for (unsigned i = 0; i < c.size(); ++i) { literal lit = m_roots[c.get_lit(i).index()]; c.set_lit(i, lit); } literal root = c.lit(); if (root != null_literal && m_roots[root.index()] != root) { root = m_roots[root.index()]; nullify_tracking_literal(c); c.update_literal(root); watch_literal(root, c); watch_literal(~root, c); } bool found_dup = false; bool found_root = false; init_visited(); for (unsigned i = 0; i < c.size(); ++i) { literal l = c.get_lit(i); if (is_visited(l)) { found_dup = true; break; } else { mark_visited(l); mark_visited(~l); } } for (unsigned i = 0; i < c.size(); ++i) { found_root |= c.get_lit(i).var() == root.var(); } if (found_root) { split_root(c); c.negate(); split_root(c); remove_constraint(c, "flush roots"); } else if (found_dup) { recompile(c); } else { if (c.lit() == null_literal || value(c.lit()) != l_undef) init_watch(c); SASSERT(c.well_formed()); } } unsigned ba_solver::get_num_unblocked_bin(literal l) { return s().m_simplifier.num_nonlearned_bin(l); } /* \brief garbage collection. This entails - finding pure literals, - setting literals that are not used in the extension to non-external. - subsumption - resolution - blocked literals */ void ba_solver::init_use_lists() { m_visited.resize(s().num_vars()*2, false); m_clause_use_list.init(s().num_vars()); m_cnstr_use_list.reset(); m_cnstr_use_list.resize(2*s().num_vars()); for (clause* c : s().m_clauses) { if (!c->frozen()) m_clause_use_list.insert(*c); } for (constraint* cp : m_constraints) { literal lit = cp->lit(); if (lit != null_literal) { m_cnstr_use_list[lit.index()].push_back(cp); m_cnstr_use_list[(~lit).index()].push_back(cp); } switch (cp->tag()) { case card_t: { card& c = cp->to_card(); for (literal l : c) { m_cnstr_use_list[l.index()].push_back(&c); if (lit != null_literal) m_cnstr_use_list[(~l).index()].push_back(&c); } break; } case pb_t: { pb& p = cp->to_pb(); for (wliteral wl : p) { literal l = wl.second; m_cnstr_use_list[l.index()].push_back(&p); if (lit != null_literal) m_cnstr_use_list[(~l).index()].push_back(&p); } break; } case xr_t: { xr& x = cp->to_xr(); for (literal l : x) { m_cnstr_use_list[l.index()].push_back(&x); m_cnstr_use_list[(~l).index()].push_back(&x); } break; } } } } void ba_solver::remove_unused_defs() { if (incremental_mode()) return; // remove constraints where indicator literal isn't used. for (constraint* cp : m_constraints) { constraint& c = *cp; literal lit = c.lit(); switch (c.tag()) { case card_t: case pb_t: { if (lit != null_literal && value(lit) == l_undef && use_count(lit) == 1 && use_count(~lit) == 1 && get_num_unblocked_bin(lit) == 0 && get_num_unblocked_bin(~lit) == 0) { remove_constraint(c, "unused def"); } break; } default: break; } } } bool ba_solver::incremental_mode() const { sat_simplifier_params p(s().m_params); bool incremental_mode = s().get_config().m_incremental && !p.override_incremental(); incremental_mode |= s().tracking_assumptions(); return incremental_mode; } unsigned ba_solver::set_non_external() { // set variables to be non-external if they are not used in theory constraints. unsigned ext = 0; if (!incremental_mode()) { for (unsigned v = 0; v < s().num_vars(); ++v) { literal lit(v, false); if (s().is_external(v) && m_cnstr_use_list[lit.index()].empty() && m_cnstr_use_list[(~lit).index()].empty()) { s().set_non_external(v); ++ext; } } } // ensure that lemmas use only non-eliminated variables for (constraint* cp : m_learned) { constraint& c = *cp; if (c.was_removed()) continue; SASSERT(c.lit() == null_literal); for (unsigned i = 0; i < c.size(); ++i) { bool_var v = c.get_lit(i).var(); if (s().was_eliminated(v)) { remove_constraint(c, "contains eliminated var"); break; } } } return ext; } bool ba_solver::elim_pure(literal lit) { if (value(lit) == l_undef && !m_cnstr_use_list[lit.index()].empty() && use_count(~lit) == 0 && get_num_unblocked_bin(~lit) == 0) { IF_VERBOSE(100, verbose_stream() << "pure literal: " << lit << "\n";); s().assign_scoped(lit); return true; } return false; } unsigned ba_solver::elim_pure() { if (!get_config().m_elim_vars || incremental_mode()) { return 0; } // eliminate pure literals unsigned pure_literals = 0; for (unsigned v = 0; v < s().num_vars(); ++v) { literal lit(v, false); if (value(v) != l_undef) continue; if (m_cnstr_use_list[lit.index()].empty() && m_cnstr_use_list[(~lit).index()].empty()) continue; if (elim_pure(lit) || elim_pure(~lit)) { ++pure_literals; } } return pure_literals; } /** * Strengthen inequalities using binary implication information. * * x -> ~y, x -> ~z, y + z + u >= 2 * ---------------------------------- * y + z + u + ~x >= 3 * * for c : constraints * for l : c: * slack <- of c under root(~l) * if slack < 0: * add ~root(~l) to c, k <- k + 1 */ void ba_solver::unit_strengthen() { big big(s().m_rand); big.init(s(), true); for (unsigned sz = m_constraints.size(), i = 0; i < sz; ++i) unit_strengthen(big, *m_constraints[i]); for (unsigned sz = m_learned.size(), i = 0; i < sz; ++i) unit_strengthen(big, *m_learned[i]); } void ba_solver::unit_strengthen(big& big, constraint& c) { if (c.was_removed()) return; switch (c.tag()) { case card_t: unit_strengthen(big, c.to_card()); break; case pb_t: unit_strengthen(big, c.to_pb()); break; default: break; } } void ba_solver::unit_strengthen(big& big, pb_base& p) { if (p.lit() != null_literal) return; unsigned sz = p.size(); for (unsigned i = 0; i < sz; ++i) { literal u = p.get_lit(i); literal r = big.get_root(u); if (r == u) continue; unsigned k = p.k(), b = 0; for (unsigned j = 0; j < sz; ++j) { literal v = p.get_lit(j); if (r == big.get_root(v)) { b += p.get_coeff(j); } } if (b > k) { r.neg(); unsigned coeff = b - k; svector wlits; // add coeff * r to p wlits.push_back(wliteral(coeff, r)); for (unsigned j = 0; j < sz; ++j) { u = p.get_lit(j); unsigned c = p.get_coeff(j); if (r == u) { wlits[0].first += c; } else if (~r == u) { if (coeff == c) { wlits[0] = wlits.back(); wlits.pop_back(); b -= c; } else if (coeff < c) { wlits[0].first = c - coeff; wlits[0].second.neg(); b -= coeff; } else { // coeff > c wlits[0].first = coeff - c; b -= c; } } else { wlits.push_back(wliteral(c, u)); } } ++m_stats.m_num_big_strengthenings; p.set_removed(); add_pb_ge(null_literal, wlits, b, p.learned()); return; } } } void ba_solver::subsumption(constraint& cnstr) { if (cnstr.was_removed()) return; switch (cnstr.tag()) { case card_t: { card& c = cnstr.to_card(); if (c.k() > 1) subsumption(c); break; } case pb_t: { pb& p = cnstr.to_pb(); if (p.k() > 1) subsumption(p); break; } default: break; } } // merge xors that contain cut variable void ba_solver::merge_xor() { unsigned sz = s().num_vars(); for (unsigned i = 0; i < sz; ++i) { literal lit(i, false); unsigned index = lit.index(); if (m_cnstr_use_list[index].size() == 2) { constraint& c1 = *m_cnstr_use_list[index][0]; constraint& c2 = *m_cnstr_use_list[index][1]; if (c1.is_xr() && c2.is_xr() && m_clause_use_list.get(lit).empty() && m_clause_use_list.get(~lit).empty()) { bool unique = true; for (watched w : get_wlist(lit)) { if (w.is_binary_clause()) unique = false; } for (watched w : get_wlist(~lit)) { if (w.is_binary_clause()) unique = false; } #if 1 if (!unique) continue; xr const& x1 = c1.to_xr(); xr const& x2 = c2.to_xr(); literal_vector lits, dups; bool parity = false; init_visited(); for (literal l : x1) { mark_visited(l.var()); lits.push_back(l); } for (literal l : x2) { if (is_visited(l.var())) { dups.push_back(l); } else { lits.push_back(l); } } init_visited(); for (literal l : dups) mark_visited(l); unsigned j = 0; for (unsigned i = 0; i < lits.size(); ++i) { literal l = lits[i]; if (is_visited(l)) { // skip } else if (is_visited(~l)) { parity ^= true; } else { lits[j++] = l; } } lits.shrink(j); if (!parity) lits[0].neg(); IF_VERBOSE(1, verbose_stream() << "binary " << lits << " : " << c1 << " " << c2 << "\n"); c1.set_removed(); c2.set_removed(); add_xr(lits, !c1.learned() && !c2.learned()); m_constraint_removed = true; #endif } } } } void ba_solver::extract_xor() { if (!s().get_config().m_xor_solver) { return; } barbet_extract_xor(); return; for (clause* cp : s().m_clauses) { clause& c = *cp; if (c.was_removed() || c.size() <= 3 || !all_distinct(c)) continue; init_visited(); for (literal l : c) mark_visited(l); literal l0 = c[0]; literal l1 = c[1]; if (extract_xor(c, l0) || extract_xor(c, l1) || extract_xor(c, ~l0)) { m_simplify_change = true; } } // extract xor from ternary clauses unsigned sz = s().num_vars(); m_ternary.reset(); m_ternary.reserve(sz); extract_ternary(s().m_clauses); extract_ternary(s().m_learned); for (unsigned v = 0; v < sz; ++v) { ptr_vector& cs = m_ternary[v]; for (unsigned i = 0; i < cs.size() && !cs[i]->is_learned(); ++i) { clause& c = *cs[i]; if (c.was_removed()) continue; init_visited(); for (literal l : c) mark_visited(l); for (unsigned j = i + 1; j < cs.size(); ++j) { if (extract_xor(c, *cs[j])) { m_simplify_change = true; break; } } } } m_ternary.clear(); } void ba_solver::extract_ternary(clause_vector const& clauses) { for (clause* cp : clauses) { clause& c = *cp; if (!c.was_removed() && c.size() == 3 && all_distinct(c)) { bool_var v = std::min(c[0].var(), std::min(c[1].var(), c[2].var())); m_ternary[v].push_back(cp); } } } void ba_solver::barbet_extract_xor() { unsigned max_size = m_max_xor_size; // we better have enough bits in the combination mask to // handle clauses up to max_size. // max_size = 5 -> 32 bits // max_size = 6 -> 64 bits SASSERT(sizeof(m_barbet_combination)*8 <= (1ull << static_cast(max_size))); init_clause_filter(); barbet_init_parity(); m_barbet_var_position.resize(s().num_vars()); for (clause* cp : s().m_clauses) { cp->unmark_used(); } for (; max_size > 2; --max_size) { for (clause* cp : s().m_clauses) { clause& c = *cp; if (c.size() == max_size && !c.was_removed() && !c.is_learned() && !c.was_used()) { barbet_extract_xor(c); } } } m_clause_filters.clear(); } void ba_solver::barbet_extract_xor(clause& c) { SASSERT(c.size() > 2); unsigned filter = get_clause_filter(c); init_visited(); bool parity = false; unsigned mask = 0, i = 0; for (literal l : c) { m_barbet_var_position[l.var()] = i; mark_visited(l.var()); parity ^= l.sign(); mask |= (l.sign() << (i++)); } m_barbet_clauses_to_remove.reset(); m_barbet_clauses_to_remove.push_back(&c); m_barbet_clause.resize(c.size()); m_barbet_combination = 0; barbet_set_combination(mask); c.mark_used(); for (literal l : c) { for (auto const& cf : m_clause_filters[l.var()]) { if ((filter == (filter | cf.m_filter)) && !cf.m_clause->was_used() && barbet_extract_xor(parity, c, *cf.m_clause)) { barbet_add_xor(parity, c); return; } } // loop over binary clauses in watch list for (watched const & w : get_wlist(l)) { if (w.is_binary_clause() && is_visited(w.get_literal().var()) && w.get_literal().index() < l.index()) { if (barbet_extract_xor(parity, c, ~l, w.get_literal())) { barbet_add_xor(parity, c); return; } } } l.neg(); for (watched const & w : get_wlist(l)) { if (w.is_binary_clause() && is_visited(w.get_literal().var()) && w.get_literal().index() < l.index()) { if (barbet_extract_xor(parity, c, ~l, w.get_literal())) { barbet_add_xor(parity, c); return; } } } } } void ba_solver::barbet_add_xor(bool parity, clause& c) { for (clause* cp : m_barbet_clauses_to_remove) { cp->set_removed(true); } m_clause_removed = true; bool learned = false; literal_vector lits; for (literal l : c) { lits.push_back(literal(l.var(), false)); s().set_external(l.var()); } if (parity) lits[0].neg(); add_xr(lits, learned); } bool ba_solver::barbet_extract_xor(bool parity, clause& c, literal l1, literal l2) { SASSERT(is_visited(l1.var())); SASSERT(is_visited(l2.var())); m_barbet_missing.reset(); unsigned mask = 0; for (unsigned i = 0; i < c.size(); ++i) { if (c[i].var() == l1.var()) { mask |= (l1.sign() << i); } else if (c[i].var() == l2.var()) { mask |= (l2.sign() << i); } else { m_barbet_missing.push_back(i); } } return barbet_update_combinations(c, parity, mask); } bool ba_solver::barbet_extract_xor(bool parity, clause& c, clause& c2) { bool parity2 = false; for (literal l : c2) { if (!is_visited(l.var())) return false; parity2 ^= l.sign(); } if (c2.size() == c.size() && parity2 != parity) { return false; } if (c2.size() == c.size()) { m_barbet_clauses_to_remove.push_back(&c2); c2.mark_used(); } // insert missing unsigned mask = 0; m_barbet_missing.reset(); SASSERT(c2.size() <= c.size()); for (unsigned i = 0; i < c.size(); ++i) { m_barbet_clause[i] = null_literal; } for (literal l : c2) { unsigned pos = m_barbet_var_position[l.var()]; m_barbet_clause[pos] = l; } for (unsigned j = 0; j < c.size(); ++j) { literal lit = m_barbet_clause[j]; if (lit == null_literal) { m_barbet_missing.push_back(j); } else { mask |= (m_barbet_clause[j].sign() << j); } } return barbet_update_combinations(c, parity, mask); } bool ba_solver::barbet_update_combinations(clause& c, bool parity, unsigned mask) { unsigned num_missing = m_barbet_missing.size(); for (unsigned k = 0; k < (1ul << num_missing); ++k) { unsigned mask2 = mask; for (unsigned i = 0; i < num_missing; ++i) { if ((k & (1 << i)) != 0) { mask2 |= 1ul << m_barbet_missing[i]; } } barbet_set_combination(mask2); } // return true if xor clause is covered. unsigned sz = c.size(); for (unsigned i = 0; i < (1ul << sz); ++i) { if (parity == m_barbet_parity[sz][i] && !barbet_get_combination(i)) { return false; } } return true; } void ba_solver::barbet_init_parity() { for (unsigned i = m_barbet_parity.size(); i <= m_max_xor_size; ++i) { bool_vector bv; for (unsigned j = 0; j < (1ul << i); ++j) { bool parity = false; for (unsigned k = 0; k < i; ++k) { parity ^= ((j & (1 << k)) != 0); } bv.push_back(parity); } m_barbet_parity.push_back(bv); } } void ba_solver::init_clause_filter() { m_clause_filters.reset(); m_clause_filters.resize(s().num_vars()); init_clause_filter(s().m_clauses); init_clause_filter(s().m_learned); } void ba_solver::init_clause_filter(clause_vector& clauses) { for (clause* cp : clauses) { clause& c = *cp; if (c.size() <= m_max_xor_size && all_distinct(c)) { clause_filter cf(get_clause_filter(c), cp); for (literal l : c) { m_clause_filters[l.var()].push_back(cf); } } } } unsigned ba_solver::get_clause_filter(clause& c) { unsigned filter = 0; for (literal l : c) { filter |= 1 << ((l.var() % 32)); } return filter; } /** * \brief replace (lit0, lit1, lit2), (lit0, ~lit1, ~lit2) * by (lit0, lit), ~lit x lit1 x lit2 */ bool ba_solver::extract_xor(clause& c1, clause& c2) { SASSERT(c1.size() == 3); SASSERT(c2.size() == 3); SASSERT(&c1 != &c2); literal lit0, lit1, lit2; if (is_visited(c2[0]) && is_visited(~c2[1]) && is_visited(~c2[2])) { lit0 = c2[0]; lit1 = c2[1]; lit2 = c2[2]; } else if (is_visited(c2[1]) && is_visited(~c2[0]) && is_visited(~c2[2])) { lit0 = c2[1]; lit1 = c2[0]; lit2 = c2[2]; } else if (is_visited(c2[2]) && is_visited(~c2[0]) && is_visited(~c2[1])) { lit0 = c2[2]; lit1 = c2[0]; lit2 = c2[1]; } else { return false; } c1.set_removed(true); c2.set_removed(true); m_clause_removed = true; literal_vector lits; lits.push_back(lit1); lits.push_back(lit2); literal lit = add_xor_def(lits); lits.reset(); lits.push_back(lit); lits.push_back(lit0); s().mk_clause(lits); TRACE("ba", tout << c1 << " " << c2 << "\n";); return true; } bool ba_solver::extract_xor(clause& c, literal l0) { watch_list & wlist = get_wlist(~l0); unsigned sz = c.size(); SASSERT(sz > 3); for (watched const& w : wlist) { if (!w.is_clause()) continue; clause& c2 = s().get_clause(w); if (c2.size() != sz || c2.was_removed()) continue; bool is_xor = true; literal lit1 = null_literal; literal lit2 = null_literal; for (literal l : c2) { if (is_visited(l)) { // no-op } else if (is_visited(~l) && lit1 == null_literal) { lit1 = l; } else if (is_visited(~l) && lit2 == null_literal) { lit2 = l; } else { is_xor = false; break; } } if (is_xor && lit2 != null_literal && lit1 != lit2) { // ensure all literals in c2 are distinct // this destroys visited, so re-initialize it. bool distinct = all_distinct(c2); init_visited(); for (literal l : c) mark_visited(l); if (!distinct) { continue; } literal_vector lits; lits.push_back(lit1); lits.push_back(lit2); literal lit = add_xor_def(lits); lits.reset(); lits.push_back(lit); for (literal l : c2) { if (l != lit1 && l != lit2) { lits.push_back(l); } } s().mk_clause(lits); c.set_removed(true); c2.set_removed(true); m_clause_removed = true; TRACE("ba", tout << "xor " << lit1 << " " << lit2 << " : " << c << " " << c2 << "\nnew clause: " << lits << "\n";); return true; } } return false; } void ba_solver::cleanup_clauses() { if (m_clause_removed) { cleanup_clauses(s().m_clauses); cleanup_clauses(s().m_learned); } } void ba_solver::cleanup_clauses(clause_vector& clauses) { // version in simplify first clears // all watch literals, then reinserts them. // this ensures linear time cleanup. clause_vector::iterator it = clauses.begin(); clause_vector::iterator end = clauses.end(); clause_vector::iterator it2 = it; for (; it != end; ++it) { clause* c = *it; if (c->was_removed() && s().can_delete(*c)) { s().detach_clause(*c); s().del_clause(*c); } else { if (it2 != it) { *it2 = *it; } ++it2; } } clauses.set_end(it2); } void ba_solver::cleanup_constraints() { if (m_constraint_removed) { cleanup_constraints(m_constraints, false); cleanup_constraints(m_learned, true); m_constraint_removed = false; } } void ba_solver::cleanup_constraints(ptr_vector& cs, bool learned) { ptr_vector::iterator it = cs.begin(); ptr_vector::iterator it2 = it; ptr_vector::iterator end = cs.end(); for (; it != end; ++it) { constraint& c = *(*it); if (c.was_removed()) { clear_watch(c); nullify_tracking_literal(c); m_allocator.deallocate(c.obj_size(), &c); } else if (learned && !c.learned()) { m_constraints.push_back(&c); } else { if (it != it2) { *it2 = *it; } ++it2; } } cs.set_end(it2); } /* \brief subsumption between two cardinality constraints - A >= k subsumes A + B >= k' for k' <= k - A + A' >= k subsumes A + B >= k' for k' + |A'| <= k - A + lit >= k self subsumes A + ~lit + B >= k' into A + B >= k' for k' <= k - version that generalizes self-subsumption to more than one literal A + ~L + B >= k' => A + B >= k' if A + A' + L >= k and k' + |L| + |A'| <= k */ bool ba_solver::subsumes(card& c1, card& c2, literal_vector & comp) { if (c2.lit() != null_literal) return false; unsigned c2_exclusive = 0; unsigned common = 0; comp.reset(); for (literal l : c2) { if (is_visited(l)) { ++common; } else if (is_visited(~l)) { comp.push_back(l); } else { ++c2_exclusive; } } unsigned c1_exclusive = c1.size() - common - comp.size(); return c1_exclusive + c2.k() + comp.size() <= c1.k(); } /* \brief L + A >= k subsumes L + C if |A| < k A + L + B >= k self-subsumes A + ~L + C >= 1 if k + 1 - |B| - |C| - |L| > 0 */ bool ba_solver::subsumes(card& c1, clause& c2, bool& self) { unsigned common = 0, complement = 0, c2_exclusive = 0; self = false; for (literal l : c2) { if (is_visited(l)) { ++common; } else if (is_visited(~l)) { ++complement; } else { ++c2_exclusive; } } unsigned c1_exclusive = c1.size() - common - complement; if (complement > 0 && c1.k() + 1 > c1_exclusive + c2_exclusive + common) { self = true; return true; } return c1.size() - common < c1.k(); } /* \brief Ax >= k subsumes By >= k' if all coefficients in A are <= B and k >= k' */ bool ba_solver::subsumes(pb const& p1, pb_base const& p2) { if (p1.k() < p2.k() || p1.size() > p2.size()) return false; unsigned num_sub = 0; for (unsigned i = 0; i < p2.size(); ++i) { literal l = p2.get_lit(i); if (is_visited(l) && m_weights[l.index()] <= p2.get_coeff(i)) { ++num_sub; } if (p1.size() + i > p2.size() + num_sub) return false; } return num_sub == p1.size(); } void ba_solver::subsumes(pb& p1, literal lit) { for (constraint* c : m_cnstr_use_list[lit.index()]) { if (c == &p1 || c->was_removed()) continue; bool s = false; switch (c->tag()) { case card_t: s = subsumes(p1, c->to_card()); break; case pb_t: s = subsumes(p1, c->to_pb()); break; default: break; } if (s) { ++m_stats.m_num_pb_subsumes; set_non_learned(p1); remove_constraint(*c, "subsumed"); } } } literal ba_solver::get_min_occurrence_literal(card const& c) { unsigned occ_count = UINT_MAX; literal lit = null_literal; for (literal l : c) { unsigned occ_count1 = m_cnstr_use_list[l.index()].size(); if (occ_count1 < occ_count) { lit = l; occ_count = occ_count1; } } return lit; } void ba_solver::card_subsumption(card& c1, literal lit) { literal_vector slit; for (constraint* c : m_cnstr_use_list[lit.index()]) { if (!c->is_card() || c == &c1 || c->was_removed()) { continue; } card& c2 = c->to_card(); SASSERT(c1.index() != c2.index()); if (subsumes(c1, c2, slit)) { if (slit.empty()) { TRACE("ba", tout << "subsume cardinality\n" << c1 << "\n" << c2.index() << ":" << c2 << "\n";); remove_constraint(c2, "subsumed"); ++m_stats.m_num_pb_subsumes; set_non_learned(c1); } else { TRACE("ba", tout << "self subsume cardinality\n";); IF_VERBOSE(11, verbose_stream() << "self-subsume cardinality\n"; verbose_stream() << c1 << "\n"; verbose_stream() << c2 << "\n";); clear_watch(c2); unsigned j = 0; for (unsigned i = 0; i < c2.size(); ++i) { if (!is_visited(~c2[i])) { c2[j++] = c2[i]; } } c2.set_size(j); init_watch(c2); m_simplify_change = true; } } } } void ba_solver::clause_subsumption(card& c1, literal lit, clause_vector& removed_clauses) { SASSERT(!c1.was_removed()); clause_use_list& occurs = m_clause_use_list.get(lit); clause_use_list::iterator it = occurs.mk_iterator(); while (!it.at_end()) { clause& c2 = it.curr(); bool self; if (!c2.was_removed() && subsumes(c1, c2, self)) { if (self) { // self-subsumption is TBD } else { TRACE("ba", tout << "remove\n" << c1 << "\n" << c2 << "\n";); removed_clauses.push_back(&c2); ++m_stats.m_num_clause_subsumes; set_non_learned(c1); } } it.next(); } } void ba_solver::set_non_learned(constraint& c) { literal lit = c.lit(); if (lit != null_literal) { s().set_external(lit.var()); } switch (c.tag()) { case card_t: for (literal lit : c.to_card()) { s().set_external(lit.var()); SASSERT(!s().was_eliminated(lit.var())); } break; case pb_t: for (wliteral wl : c.to_pb()) { s().set_external(wl.second.var()); SASSERT(!s().was_eliminated(wl.second.var())); } break; default: for (literal lit : c.to_xr()) { s().set_external(lit.var()); SASSERT(!s().was_eliminated(lit.var())); } break; } c.set_learned(false); } void ba_solver::binary_subsumption(card& c1, literal lit) { if (c1.k() + 1 != c1.size()) return; SASSERT(is_visited(lit)); SASSERT(!c1.was_removed()); watch_list & wlist = get_wlist(~lit); watch_list::iterator it = wlist.begin(); watch_list::iterator it2 = it; watch_list::iterator end = wlist.end(); for (; it != end; ++it) { watched w = *it; if (w.is_binary_clause() && is_visited(w.get_literal())) { ++m_stats.m_num_bin_subsumes; IF_VERBOSE(20, verbose_stream() << c1 << " subsumes (" << lit << " " << w.get_literal() << ")\n";); if (!w.is_learned()) { set_non_learned(c1); } } else { if (it != it2) { *it2 = *it; } ++it2; } } wlist.set_end(it2); } void ba_solver::subsumption(card& c1) { if (c1.was_removed() || c1.lit() != null_literal) { return; } clause_vector removed_clauses; init_visited(); for (literal l : c1) mark_visited(l); for (unsigned i = 0; i < std::min(c1.size(), c1.k() + 1); ++i) { literal lit = c1[i]; card_subsumption(c1, lit); clause_subsumption(c1, lit, removed_clauses); binary_subsumption(c1, lit); } m_clause_removed |= !removed_clauses.empty(); for (clause *c : removed_clauses) { c->set_removed(true); m_clause_use_list.erase(*c); } } void ba_solver::subsumption(pb& p1) { if (p1.was_removed() || p1.lit() != null_literal) { return; } init_visited(); for (wliteral l : p1) { SASSERT(m_weights.size() <= l.second.index() || m_weights[l.second.index()] == 0); m_weights.setx(l.second.index(), l.first, 0); mark_visited(l.second); } for (unsigned i = 0; i < std::min(10u, p1.num_watch()); ++i) { unsigned j = s().m_rand() % p1.num_watch(); subsumes(p1, p1[j].second); } for (wliteral l : p1) { m_weights[l.second.index()] = 0; } } void ba_solver::clauses_modifed() {} lbool ba_solver::get_phase(bool_var v) { return l_undef; } /* \brief lit <=> conjunction of unconstrained lits */ void ba_solver::assert_unconstrained(literal lit, literal_vector const& lits) { if (lit == null_literal) { for (literal l : lits) { if (value(l) == l_undef) { s().assign_scoped(l); } } } else { // add clauses for: lit <=> conjunction of undef literals SASSERT(value(lit) == l_undef); literal_vector cl; cl.push_back(lit); for (literal l : lits) { if (value(l) == l_undef) { s().mk_clause(~lit, l); cl.push_back(~l); } } s().mk_clause(cl); } } extension* ba_solver::copy(solver* s) { ba_solver* result = alloc(ba_solver); result->set_solver(s); copy_core(result, false); return result; } extension* ba_solver::copy(lookahead* s, bool learned) { ba_solver* result = alloc(ba_solver); result->set_lookahead(s); copy_core(result, learned); return result; } void ba_solver::copy_core(ba_solver* result, bool learned) { copy_constraints(result, m_constraints); if (learned) copy_constraints(result, m_learned); } void ba_solver::copy_constraints(ba_solver* result, ptr_vector const& constraints) { literal_vector lits; svector wlits; for (constraint* cp : constraints) { switch (cp->tag()) { case card_t: { card const& c = cp->to_card(); lits.reset(); for (literal l : c) lits.push_back(l); result->add_at_least(c.lit(), lits, c.k(), c.learned()); break; } case pb_t: { pb const& p = cp->to_pb(); wlits.reset(); for (wliteral w : p) { wlits.push_back(w); } result->add_pb_ge(p.lit(), wlits, p.k(), p.learned()); break; } case xr_t: { xr const& x = cp->to_xr(); lits.reset(); for (literal l : x) lits.push_back(l); result->add_xr(lits, x.learned()); break; } default: UNREACHABLE(); } } } void ba_solver::init_use_list(ext_use_list& ul) { ul.init(s().num_vars()); for (constraint const* cp : m_constraints) { ext_constraint_idx idx = cp->index(); if (cp->lit() != null_literal) { ul.insert(cp->lit(), idx); ul.insert(~cp->lit(), idx); } switch (cp->tag()) { case card_t: { card const& c = cp->to_card(); for (literal l : c) { ul.insert(l, idx); } break; } case pb_t: { pb const& p = cp->to_pb(); for (wliteral w : p) { ul.insert(w.second, idx); } break; } case xr_t: { xr const& x = cp->to_xr(); for (literal l : x) { ul.insert(l, idx); ul.insert(~l, idx); } break; } default: UNREACHABLE(); } } } // // literal is used in a clause (C or l), it // it occurs negatively in constraint c. // all literals in C are marked // bool ba_solver::is_blocked(literal l, ext_constraint_idx idx) { constraint const& c = index2constraint(idx); simplifier& sim = s().m_simplifier; if (c.lit() != null_literal) return false; switch (c.tag()) { case card_t: { card const& ca = c.to_card(); unsigned weight = 0; for (literal l2 : ca) { if (sim.is_marked(~l2)) ++weight; } return weight >= ca.k(); } case pb_t: { pb const& p = c.to_pb(); unsigned weight = 0, offset = 0; for (wliteral l2 : p) { if (~l2.second == l) { offset = l2.first; break; } } SASSERT(offset != 0); for (wliteral l2 : p) { if (sim.is_marked(~l2.second)) { weight += std::min(offset, l2.first); } } return weight >= p.k(); } default: break; } return false; } void ba_solver::find_mutexes(literal_vector& lits, vector & mutexes) { literal_set slits(lits); bool change = false; for (constraint* cp : m_constraints) { if (!cp->is_card()) continue; card const& c = cp->to_card(); if (c.size() == c.k() + 1) { literal_vector mux; for (literal lit : c) { if (slits.contains(~lit)) { mux.push_back(~lit); } } if (mux.size() <= 1) { continue; } for (literal m : mux) { slits.remove(m); } change = true; mutexes.push_back(mux); } } if (!change) return; lits.reset(); for (literal l : slits) { lits.push_back(l); } } void ba_solver::display(std::ostream& out, ineq const& ineq, bool values) const { for (unsigned i = 0; i < ineq.size(); ++i) { if (ineq.coeff(i) != 1) out << ineq.coeff(i) << "*"; out << ineq.lit(i) << " "; if (values) out << value(ineq.lit(i)) << " "; } out << ">= " << ineq.m_k << "\n"; } void ba_solver::display(std::ostream& out, xr const& x, bool values) const { out << "xr: "; for (literal l : x) { out << l; if (values) { out << "@(" << value(l); if (value(l) != l_undef) { out << ":" << lvl(l); } out << ") "; } else { out << " "; } } out << "\n"; } void ba_solver::display_lit(std::ostream& out, literal lit, unsigned sz, bool values) const { if (lit != null_literal) { if (values) { out << lit << "[" << sz << "]"; out << "@(" << value(lit); if (value(lit) != l_undef) { out << ":" << lvl(lit); } out << "): "; } else { out << lit << " == "; } } } void ba_solver::display(std::ostream& out, card const& c, bool values) const { display_lit(out, c.lit(), c.size(), values); for (unsigned i = 0; i < c.size(); ++i) { literal l = c[i]; out << l; if (values) { out << "@(" << value(l); if (value(l) != l_undef) { out << ":" << lvl(l); } out << ") "; } else { out << " "; } } out << ">= " << c.k() << "\n"; } std::ostream& ba_solver::display(std::ostream& out) const { for (constraint const* c : m_constraints) { out << (*c) << "\n"; } if (!m_learned.empty()) { out << "learned:\n"; } for (constraint const* c : m_learned) { out << (*c) << "\n"; } return out; } std::ostream& ba_solver::display_justification(std::ostream& out, ext_justification_idx idx) const { return out << index2constraint(idx); } std::ostream& ba_solver::display_constraint(std::ostream& out, ext_constraint_idx idx) const { return out << index2constraint(idx); } std::ostream& 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; case pb_t: display(out, c.to_pb(), values); break; case xr_t: display(out, c.to_xr(), values); break; default: UNREACHABLE(); break; } return out; } void ba_solver::collect_statistics(statistics& st) const { st.update("ba propagations", m_stats.m_num_propagations); st.update("ba conflicts", m_stats.m_num_conflicts); st.update("ba resolves", m_stats.m_num_resolves); st.update("ba cuts", m_stats.m_num_cut); st.update("ba gc", m_stats.m_num_gc); st.update("ba overflow", m_stats.m_num_overflow); st.update("ba big strengthenings", m_stats.m_num_big_strengthenings); st.update("ba lemmas", m_stats.m_num_lemmas); st.update("ba subsumes", m_stats.m_num_bin_subsumes + m_stats.m_num_clause_subsumes + m_stats.m_num_pb_subsumes); } bool ba_solver::validate_unit_propagation(card const& c, literal alit) const { (void) alit; if (c.lit() != null_literal && value(c.lit()) != l_true) return false; for (unsigned i = c.k(); i < c.size(); ++i) { if (value(c[i]) != l_false) return false; } return true; } bool ba_solver::validate_unit_propagation(pb const& p, literal alit) const { if (p.lit() != null_literal && value(p.lit()) != l_true) { return false; } unsigned sum = 0; TRACE("ba", display(tout << "validate: " << alit << "\n", p, true);); for (wliteral wl : p) { literal lit = wl.second; lbool val = value(lit); if (val != l_false && lit != alit) { sum += wl.first; } } return sum < p.k(); } bool ba_solver::validate_unit_propagation(pb const& p, literal_vector const& r, literal alit) const { // all elements of r are true, for (literal l : r) { if (value(l) != l_true) { IF_VERBOSE(0, verbose_stream() << "value of " << l << " is " << value(l) << "\n"; display(verbose_stream(), p, true);); return false; } if (value(alit) == l_true && lvl(l) > lvl(alit)) { IF_VERBOSE(0, verbose_stream() << "level of premise " << l << " is " << lvl(l) << "\n"; verbose_stream() << "level of asserting literal " << alit << " is " << lvl(alit) << "\n"; display(verbose_stream(), p, true);); return false; } // if (value(alit) == l_true && lvl(l) == lvl(alit)) { // std::cout << "same level " << alit << " " << l << "\n"; // } } // the sum of elements not in r or alit add up to less than k. unsigned sum = 0; // // a*x + b*alit + c*r >= k // sum a < k // val(r) = false // hence alit has to be true. for (wliteral wl : p) { literal lit = wl.second; if (lit != alit && !r.contains(~lit)) { sum += wl.first; } } if (sum >= p.k()) { IF_VERBOSE(0, verbose_stream() << "sum is " << sum << " >= " << p.k() << "\n"; display(verbose_stream(), p, true); verbose_stream() << "id: " << p.id() << "\n"; sum = 0; for (wliteral wl : p) sum += wl.first; verbose_stream() << "overall sum " << sum << "\n"; verbose_stream() << "asserting literal: " << alit << "\n"; verbose_stream() << "reason: " << r << "\n";); return false; } for (wliteral wl : p) { if (alit == wl.second) { return true; } } IF_VERBOSE(0, verbose_stream() << alit << " not found among literals\n";); return false; } bool ba_solver::validate_unit_propagation(xr const& x, literal alit) const { if (value(x.lit()) != l_true) return false; for (unsigned i = 1; i < x.size(); ++i) { if (value(x[i]) == l_undef) return false; } return true; } bool ba_solver::validate_lemma() { int64_t bound64 = m_bound; int64_t val = -bound64; reset_active_var_set(); for (bool_var v : m_active_vars) { if (!test_and_set_active(v)) continue; wliteral wl = get_wliteral(v); if (wl.first == 0) continue; if (!is_false(wl.second)) { val += wl.first; } } CTRACE("ba", val >= 0, active2pb(m_A); display(tout, m_A, true);); return val < 0; } /** * the slack of inequalities on the stack should be non-positive. */ bool ba_solver::validate_ineq(ineq const& ineq) const { int64_t k = -static_cast(ineq.m_k); for (wliteral wl : ineq.m_wlits) { if (!is_false(wl.second)) k += wl.first; } CTRACE("ba", k > 0, display(tout, ineq, true);); return k <= 0; } void ba_solver::reset_active_var_set() { while (!m_active_var_set.empty()) m_active_var_set.erase(); } bool ba_solver::test_and_set_active(bool_var v) { if (m_active_var_set.contains(v)) { return false; } else { m_active_var_set.insert(v); return true; } } void ba_solver::active2pb(ineq& p) { p.reset(m_bound); active2wlits(p.m_wlits); } void ba_solver::active2wlits() { m_wlits.reset(); active2wlits(m_wlits); } void ba_solver::active2wlits(svector& wlits) { uint64_t sum = 0; reset_active_var_set(); for (bool_var v : m_active_vars) { if (!test_and_set_active(v)) continue; wliteral wl = get_wliteral(v); if (wl.first == 0) continue; wlits.push_back(wl); sum += wl.first; } m_overflow |= sum >= UINT_MAX/2; } ba_solver::constraint* ba_solver::active2lemma() { switch (s().m_config.m_pb_lemma_format) { case PB_LEMMA_CARDINALITY: return active2card(); case PB_LEMMA_PB: return active2constraint(); default: UNREACHABLE(); return nullptr; } } ba_solver::constraint* ba_solver::active2constraint() { active2wlits(); if (m_overflow) { return nullptr; } constraint* c = add_pb_ge(null_literal, m_wlits, m_bound, true); TRACE("ba", if (c) display(tout, *c, true);); ++m_stats.m_num_lemmas; return c; } /* Chai Kuhlmann: a1*l1 + ... + a_n*l_n >= k s.t. a1 >= a2 >= .. >= a_n let m be such that sum_{i = 1}^{m-1} a_i < k <= sum_{i = 1}^{m} then l1 + ... + l_n >= m furthermore, for the largest n' <= n, such that sum_{i = n'+1}^n a_i + sum_{i = 1}^{m-1} a_i < k then l1 + ... + l_n' >= m */ struct compare_wlit { bool operator()(ba_solver::wliteral l1, ba_solver::wliteral l2) const { return l1.first > l2.first; } }; ba_solver::constraint* ba_solver::active2card() { active2wlits(); if (m_overflow) { return nullptr; } std::sort(m_wlits.begin(), m_wlits.end(), compare_wlit()); unsigned k = 0; uint64_t sum = 0, sum0 = 0; for (wliteral wl : m_wlits) { if (sum >= m_bound) break; sum0 = sum; sum += wl.first; ++k; } if (k == 1) { return nullptr; } while (!m_wlits.empty()) { wliteral wl = m_wlits.back(); if (wl.first + sum0 >= m_bound) break; m_wlits.pop_back(); sum0 += wl.first; } unsigned slack = 0; unsigned max_level = 0; unsigned num_max_level = 0; for (wliteral wl : m_wlits) { if (value(wl.second) != l_false) ++slack; unsigned level = lvl(wl.second); if (level > max_level) { max_level = level; num_max_level = 1; } else if (max_level == level) { ++num_max_level; } } if (m_overflow) { return nullptr; } if (slack >= k) { #if 0 return active2constraint(); active2pb(m_A); std::cout << "not asserting\n"; display(std::cout, m_A, true); #endif return nullptr; } // produce asserting cardinality constraint literal_vector lits; for (wliteral wl : m_wlits) { lits.push_back(wl.second); } constraint* c = add_at_least(null_literal, lits, k, true); ++m_stats.m_num_lemmas; if (c) { lits.reset(); for (wliteral wl : m_wlits) { if (value(wl.second) == l_false) lits.push_back(wl.second); } unsigned glue = s().num_diff_levels(lits.size(), lits.c_ptr()); c->set_glue(glue); } return c; } void ba_solver::justification2pb(justification const& js, literal lit, unsigned offset, ineq& ineq) { switch (js.get_kind()) { case justification::NONE: SASSERT(lit != null_literal); ineq.reset(offset); ineq.push(lit, offset); break; case justification::BINARY: SASSERT(lit != null_literal); ineq.reset(offset); ineq.push(lit, offset); ineq.push(js.get_literal(), offset); break; case justification::TERNARY: SASSERT(lit != null_literal); ineq.reset(offset); ineq.push(lit, offset); ineq.push(js.get_literal1(), offset); ineq.push(js.get_literal2(), offset); break; case justification::CLAUSE: { ineq.reset(offset); clause & c = s().get_clause(js); for (literal l : c) ineq.push(l, offset); break; } case justification::EXT_JUSTIFICATION: { ext_justification_idx index = js.get_ext_justification_idx(); constraint& cnstr = index2constraint(index); constraint2pb(cnstr, lit, offset, ineq); break; } default: UNREACHABLE(); break; } } void ba_solver::constraint2pb(constraint& cnstr, literal lit, unsigned offset, ineq& ineq) { switch (cnstr.tag()) { case card_t: { card& c = cnstr.to_card(); ineq.reset(offset*c.k()); for (literal l : c) ineq.push(l, offset); if (c.lit() != null_literal) ineq.push(~c.lit(), offset*c.k()); break; } case pb_t: { pb& p = cnstr.to_pb(); ineq.reset(offset * p.k()); for (wliteral wl : p) ineq.push(wl.second, offset * wl.first); if (p.lit() != null_literal) ineq.push(~p.lit(), offset * p.k()); break; } case xr_t: { xr& x = cnstr.to_xr(); literal_vector ls; SASSERT(lit != null_literal); get_antecedents(lit, x, ls); ineq.reset(offset); for (literal l : ls) ineq.push(~l, offset); literal lxr = x.lit(); if (lxr != null_literal) ineq.push(~lxr, offset); break; } default: UNREACHABLE(); break; } } // validate that m_A & m_B implies m_C bool ba_solver::validate_resolvent() { return true; u_map coeffs; uint64_t k = m_A.m_k + m_B.m_k; for (unsigned i = 0; i < m_A.size(); ++i) { uint64_t coeff = m_A.coeff(i); SASSERT(!coeffs.contains(m_A.lit(i).index())); coeffs.insert(m_A.lit(i).index(), coeff); } for (unsigned i = 0; i < m_B.size(); ++i) { uint64_t coeff1 = m_B.coeff(i), coeff2; literal lit = m_B.lit(i); if (coeffs.find((~lit).index(), coeff2)) { if (coeff1 == coeff2) { coeffs.remove((~lit).index()); k += coeff1; } else if (coeff1 < coeff2) { coeffs.insert((~lit).index(), coeff2 - coeff1); k += coeff1; } else { SASSERT(coeff2 < coeff1); coeffs.remove((~lit).index()); coeffs.insert(lit.index(), coeff1 - coeff2); k += coeff2; } } else if (coeffs.find(lit.index(), coeff2)) { coeffs.insert(lit.index(), coeff1 + coeff2); } else { coeffs.insert(lit.index(), coeff1); } } // C is above the sum of A and B for (unsigned i = 0; i < m_C.size(); ++i) { literal lit = m_C.lit(i); uint64_t coeff; if (coeffs.find(lit.index(), coeff)) { if (coeff > m_C.coeff(i) && m_C.coeff(i) < m_C.m_k) { goto violated; } coeffs.remove(lit.index()); } } if (!coeffs.empty()) goto violated; if (m_C.m_k > k) goto violated; SASSERT(coeffs.empty()); SASSERT(m_C.m_k <= k); return true; violated: // last ditch effort by translating to SAT. solver s0(s().m_params, s().rlimit()); u_map translation; literal l1 = translate_to_sat(s0, translation, m_A); if (l1 == null_literal) return true; literal l2 = translate_to_sat(s0, translation, m_B); if (l2 == null_literal) return true; ineq notC = negate(m_B); literal l3 = translate_to_sat(s0, translation, notC); if (l3 == null_literal) return true; s0.assign_scoped(l1); s0.assign_scoped(l2); s0.assign_scoped(l3); lbool is_sat = s0.check(); TRACE("ba", s0.display(tout << "trying sat encoding");); if (is_sat == l_false) return true; IF_VERBOSE(0, display(verbose_stream(), m_A); display(verbose_stream(), m_B); display(verbose_stream(), m_C); for (auto& e : coeffs) { verbose_stream() << to_literal(e.m_key) << ": " << e.m_value << "\n"; }); UNREACHABLE(); return false; } /** \brief translate PB inequality to SAT formula. */ literal ba_solver::translate_to_sat(solver& s, u_map& translation, ineq const& pb) { SASSERT(pb.m_k > 0); if (pb.size() > 1) { ineq a, b; a.reset(pb.m_k); b.reset(pb.m_k); for (unsigned i = 0; i < pb.size()/2; ++i) { a.push(pb.lit(i), pb.coeff(i)); } for (unsigned i = pb.size()/2; i < pb.size(); ++i) { b.push(pb.lit(i), pb.coeff(i)); } bool_var v = s.mk_var(); literal lit(v, false); literal_vector lits; lits.push_back(~lit); push_lit(lits, translate_to_sat(s, translation, a)); push_lit(lits, translate_to_sat(s, translation, b)); push_lit(lits, translate_to_sat(s, translation, a, b)); s.mk_clause(lits); return lit; } if (pb.coeff(0) >= pb.m_k) { return translate_to_sat(s, translation, pb.lit(0)); } else { return null_literal; } } /* \brief encode the case where Sum(a) >= k-1 & Sum(b) >= 1 \/ ... \/ Sum(a) >= 1 & Sum(b) >= k-1 */ literal ba_solver::translate_to_sat(solver& s, u_map& translation, ineq& a, ineq& b) { uint64_t k0 = a.m_k; literal_vector lits; for (unsigned k = 1; k < a.m_k - 1; ++k) { a.m_k = k; b.m_k = k0 - k; literal lit1 = translate_to_sat(s, translation, a); literal lit2 = translate_to_sat(s, translation, b); if (lit1 != null_literal && lit2 != null_literal) { bool_var v = s.mk_var(); literal lit(v, false); s.mk_clause(~lit, lit1); s.mk_clause(~lit, lit2); lits.push_back(lit); } } a.m_k = k0; b.m_k = k0; switch (lits.size()) { case 0: return null_literal; case 1: return lits[0]; default: { bool_var v = s.mk_var(); literal lit(v, false); lits.push_back(~lit); s.mk_clause(lits); return lit; } } } literal ba_solver::translate_to_sat(solver& s, u_map& translation, literal lit) { bool_var v; if (!translation.find(lit.var(), v)) { v = s.mk_var(); translation.insert(lit.var(), v); } return literal(v, lit.sign()); } ba_solver::ineq ba_solver::negate(ineq const& a) const { ineq result; uint64_t sum = 0; for (unsigned i = 0; i < a.size(); ++i) { result.push(~a.lit(i), a.coeff(i)); sum += a.coeff(i); } SASSERT(sum >= a.m_k + 1); result.m_k = sum + 1 - a.m_k; return result; } void ba_solver::push_lit(literal_vector& lits, literal lit) { if (lit != null_literal) { lits.push_back(lit); } } bool ba_solver::validate_conflict(literal_vector const& lits, ineq& p) { for (literal l : lits) { if (value(l) != l_false) { TRACE("ba", tout << "literal " << l << " is not false\n";); return false; } if (!p.contains(l)) { TRACE("ba", tout << "lemma contains literal " << l << " not in inequality\n";); return false; } } uint64_t value = 0; for (unsigned i = 0; i < p.size(); ++i) { uint64_t coeff = p.coeff(i); if (!lits.contains(p.lit(i))) { value += coeff; } } CTRACE("ba", value >= p.m_k, tout << "slack: " << value << " bound " << p.m_k << "\n"; display(tout, p); tout << lits << "\n";); return value < p.m_k; } bool ba_solver::check_model(model const& m) const { bool ok = true; for (constraint const* c : m_constraints) { if (c->was_removed()) { continue; } if (c->is_pure() && c->lit() != null_literal && m[c->lit().var()] == (c->lit().sign() ? l_true : l_false)) { continue; } switch (eval(m, *c)) { case l_false: IF_VERBOSE(0, verbose_stream() << "failed checking " << c->id() << ": " << *c << "\n";); ok = false; break; case l_true: break; case l_undef: IF_VERBOSE(0, verbose_stream() << "undef " << c->id() << ": " << *c << "\n";); break; } } return ok; } }; z3-z3-4.8.7/src/sat/ba_solver.h000066400000000000000000000700701356505360400161360ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: ba_solver.h Abstract: Cardinality extensions, Pseudo Booleans, Xors Author: Nikolaj Bjorner (nbjorner) 2017-01-30 Revision History: --*/ #ifndef BA_SOLVER_H_ #define BA_SOLVER_H_ #include "sat/sat_extension.h" #include "sat/sat_solver.h" #include "sat/sat_lookahead.h" #include "sat/sat_unit_walk.h" #include "sat/sat_big.h" #include "util/small_object_allocator.h" #include "util/scoped_ptr_vector.h" #include "util/sorting_network.h" namespace sat { class ba_solver : public extension { friend class local_search; struct stats { unsigned m_num_propagations; unsigned m_num_conflicts; unsigned m_num_resolves; unsigned m_num_bin_subsumes; unsigned m_num_clause_subsumes; unsigned m_num_pb_subsumes; unsigned m_num_big_strengthenings; unsigned m_num_cut; unsigned m_num_gc; unsigned m_num_overflow; unsigned m_num_lemmas; stats() { reset(); } void reset() { memset(this, 0, sizeof(*this)); } }; public: enum tag_t { card_t, pb_t, xr_t }; class card; class pb; class xr; class pb_base; class constraint { protected: tag_t m_tag; bool m_removed; literal m_lit; literal m_watch; unsigned m_glue; unsigned m_psm; unsigned m_size; size_t m_obj_size; bool m_learned; unsigned m_id; bool m_pure; // is the constraint pure (only positive occurrences) public: constraint(tag_t t, unsigned id, literal l, unsigned sz, size_t osz): m_tag(t), m_removed(false), m_lit(l), m_watch(null_literal), m_glue(0), m_psm(0), m_size(sz), m_obj_size(osz), m_learned(false), m_id(id), m_pure(false) {} ext_constraint_idx index() const { return reinterpret_cast(this); } unsigned id() const { return m_id; } tag_t tag() const { return m_tag; } literal lit() const { return m_lit; } unsigned size() const { return m_size; } void set_size(unsigned sz) { SASSERT(sz <= m_size); m_size = sz; } void update_literal(literal l) { m_lit = l; } bool was_removed() const { return m_removed; } void set_removed() { m_removed = true; } void nullify_literal() { m_lit = null_literal; } unsigned glue() const { return m_glue; } void set_glue(unsigned g) { m_glue = g; } unsigned psm() const { return m_psm; } void set_psm(unsigned p) { m_psm = p; } void set_learned(bool f) { m_learned = f; } bool learned() const { return m_learned; } bool is_watched() const { return m_watch == m_lit && m_lit != null_literal; } void set_watch() { m_watch = m_lit; } void clear_watch() { m_watch = null_literal; } bool is_clear() const { return m_watch == null_literal && m_lit != null_literal; } bool is_pure() const { return m_pure; } void set_pure() { m_pure = true; } unsigned fold_max_var(unsigned w) const; size_t obj_size() const { return m_obj_size; } card& to_card(); pb& to_pb(); xr& to_xr(); card const& to_card() const; pb const& to_pb() const; xr const& to_xr() const; pb_base const& to_pb_base() const; bool is_card() const { return m_tag == card_t; } bool is_pb() const { return m_tag == pb_t; } bool is_xr() const { return m_tag == xr_t; } virtual bool is_watching(literal l) const { UNREACHABLE(); return false; }; virtual literal_vector literals() const { UNREACHABLE(); return literal_vector(); } virtual void swap(unsigned i, unsigned j) { UNREACHABLE(); } virtual literal get_lit(unsigned i) const { UNREACHABLE(); return null_literal; } virtual void set_lit(unsigned i, literal l) { UNREACHABLE(); } virtual bool well_formed() const { return true; } virtual void negate() { UNREACHABLE(); } }; friend std::ostream& operator<<(std::ostream& out, constraint const& c); // base class for pb and cardinality constraints class pb_base : public constraint { protected: unsigned m_k; public: pb_base(tag_t t, unsigned id, literal l, unsigned sz, size_t osz, unsigned k): constraint(t, id, l, sz, osz), m_k(k) { VERIFY(k < 4000000000); } virtual void set_k(unsigned k) { VERIFY(k < 4000000000); m_k = k; } virtual unsigned get_coeff(unsigned i) const { UNREACHABLE(); return 0; } unsigned k() const { return m_k; } bool well_formed() const override; }; class card : public pb_base { literal m_lits[0]; public: static size_t get_obj_size(unsigned num_lits) { return sizeof(card) + num_lits * sizeof(literal); } card(unsigned id, literal lit, literal_vector const& lits, unsigned k); literal operator[](unsigned i) const { return m_lits[i]; } literal& operator[](unsigned i) { return m_lits[i]; } literal const* begin() const { return m_lits; } literal const* end() const { return static_cast(m_lits) + m_size; } void negate() override; void swap(unsigned i, unsigned j) override { std::swap(m_lits[i], m_lits[j]); } literal_vector literals() const override { return literal_vector(m_size, m_lits); } bool is_watching(literal l) const override; literal get_lit(unsigned i) const override { return m_lits[i]; } void set_lit(unsigned i, literal l) override { m_lits[i] = l; } unsigned get_coeff(unsigned i) const override { return 1; } }; typedef std::pair wliteral; class pb : public pb_base { unsigned m_slack; unsigned m_num_watch; unsigned m_max_sum; wliteral m_wlits[0]; public: static size_t get_obj_size(unsigned num_lits) { return sizeof(pb) + num_lits * sizeof(wliteral); } pb(unsigned id, literal lit, svector const& wlits, unsigned k); literal lit() const { return m_lit; } wliteral operator[](unsigned i) const { return m_wlits[i]; } wliteral& operator[](unsigned i) { return m_wlits[i]; } wliteral const* begin() const { return m_wlits; } wliteral const* end() const { return begin() + m_size; } unsigned slack() const { return m_slack; } void set_slack(unsigned s) { m_slack = s; } unsigned num_watch() const { return m_num_watch; } unsigned max_sum() const { return m_max_sum; } void update_max_sum(); void set_num_watch(unsigned s) { m_num_watch = s; } bool is_cardinality() const; void negate() override; void set_k(unsigned k) override { m_k = k; VERIFY(k < 4000000000); update_max_sum(); } void swap(unsigned i, unsigned j) override { std::swap(m_wlits[i], m_wlits[j]); } literal_vector literals() const override { literal_vector lits; for (auto wl : *this) lits.push_back(wl.second); return lits; } bool is_watching(literal l) const override; literal get_lit(unsigned i) const override { return m_wlits[i].second; } void set_lit(unsigned i, literal l) override { m_wlits[i].second = l; } unsigned get_coeff(unsigned i) const override { return m_wlits[i].first; } }; class xr : public constraint { literal m_lits[0]; public: static size_t get_obj_size(unsigned num_lits) { return sizeof(xr) + num_lits * sizeof(literal); } xr(unsigned id, literal_vector const& lits); literal operator[](unsigned i) const { return m_lits[i]; } literal const* begin() const { return m_lits; } literal const* end() const { return begin() + m_size; } void negate() override { m_lits[0].neg(); } void swap(unsigned i, unsigned j) override { std::swap(m_lits[i], m_lits[j]); } bool is_watching(literal l) const override; literal_vector literals() const override { return literal_vector(size(), begin()); } literal get_lit(unsigned i) const override { return m_lits[i]; } void set_lit(unsigned i, literal l) override { m_lits[i] = l; } bool well_formed() const override; }; protected: struct ineq { svector m_wlits; uint64_t m_k; ineq(): m_k(0) {} unsigned size() const { return m_wlits.size(); } literal lit(unsigned i) const { return m_wlits[i].second; } unsigned coeff(unsigned i) const { return m_wlits[i].first; } void reset(uint64_t k) { m_wlits.reset(); m_k = k; } void push(literal l, unsigned c) { m_wlits.push_back(wliteral(c,l)); } unsigned bv_coeff(bool_var v) const; void divide(unsigned c); void weaken(unsigned i); bool contains(literal l) const { for (auto wl : m_wlits) if (wl.second == l) return true; return false; } }; solver* m_solver; lookahead* m_lookahead; unit_walk* m_unit_walk; stats m_stats; small_object_allocator m_allocator; ptr_vector m_constraints; ptr_vector m_learned; ptr_vector m_constraint_to_reinit; unsigned_vector m_constraint_to_reinit_lim; unsigned m_constraint_to_reinit_last_sz; unsigned m_constraint_id; // conflict resolution unsigned m_num_marks; unsigned m_conflict_lvl; svector m_coeffs; svector m_active_vars; unsigned m_bound; tracked_uint_set m_active_var_set; literal_vector m_lemma; literal_vector m_skipped; unsigned m_num_propagations_since_pop; unsigned_vector m_parity_marks; literal_vector m_parity_trail; unsigned_vector m_pb_undef; struct ba_sort { typedef sat::literal pliteral; typedef sat::literal_vector pliteral_vector; ba_solver& s; pliteral m_true; pliteral_vector m_lits; ba_sort(ba_solver& s): s(s), m_true(null_literal) {} pliteral mk_false(); pliteral mk_true(); pliteral mk_not(pliteral l); pliteral fresh(char const*); pliteral mk_min(unsigned, pliteral const* lits); pliteral mk_max(unsigned, pliteral const* lits); void mk_clause(unsigned n, literal const* lits); std::ostream& pp(std::ostream& out, pliteral l) const; }; ba_sort m_ba; psort_nw m_sort; void ensure_parity_size(bool_var v); unsigned get_parity(bool_var v); void inc_parity(bool_var v); void reset_parity(bool_var v); solver& s() const { return *m_solver; } // simplification routines svector m_visited; unsigned m_visited_ts; vector> m_cnstr_use_list; use_list m_clause_use_list; bool m_simplify_change; bool m_clause_removed; bool m_constraint_removed; literal_vector m_roots; svector m_root_vars; unsigned_vector m_weights; svector m_wlits; bool subsumes(card& c1, card& c2, literal_vector& comp); bool subsumes(card& c1, clause& c2, bool& self); bool subsumed(card& c1, literal l1, literal l2); bool subsumes(pb const& p1, pb_base const& p2); void subsumes(pb& p1, literal lit); void subsumption(pb& p1); void binary_subsumption(card& c1, literal lit); void clause_subsumption(card& c1, literal lit, clause_vector& removed_clauses); void card_subsumption(card& c1, literal lit); void init_visited(); void mark_visited(literal l) { m_visited[l.index()] = m_visited_ts; } void mark_visited(bool_var v) { mark_visited(literal(v, false)); } bool is_visited(bool_var v) const { return is_visited(literal(v, false)); } bool is_visited(literal l) const { return m_visited[l.index()] == m_visited_ts; } unsigned get_num_unblocked_bin(literal l); literal get_min_occurrence_literal(card const& c); void init_use_lists(); void remove_unused_defs(); unsigned set_non_external(); unsigned elim_pure(); bool elim_pure(literal lit); void unit_strengthen(); void unit_strengthen(big& big, constraint& cs); void unit_strengthen(big& big, pb_base& p); void subsumption(constraint& c1); void subsumption(card& c1); void gc_half(char const* _method); void update_psm(constraint& c) const; void mutex_reduction(); void update_pure(); void reserve_roots(); unsigned use_count(literal lit) const { return m_cnstr_use_list[lit.index()].size() + m_clause_use_list.get(lit).size(); } void cleanup_clauses(); void cleanup_clauses(clause_vector& clauses); void cleanup_constraints(); void cleanup_constraints(ptr_vector& cs, bool learned); void remove_constraint(constraint& c, char const* reason); // constraints constraint& index2constraint(size_t idx) const { return *reinterpret_cast(idx); } void pop_constraint(); void unwatch_literal(literal w, constraint& c); void watch_literal(literal w, constraint& c); void watch_literal(wliteral w, pb& p); bool is_watched(literal l, constraint const& c) const; void add_constraint(constraint* c); bool init_watch(constraint& c); void init_watch(bool_var v); void clear_watch(constraint& c); lbool add_assign(constraint& c, literal l); bool incremental_mode() const; void simplify(constraint& c); void pre_simplify(constraint& c); void nullify_tracking_literal(constraint& c); void set_conflict(constraint& c, literal lit); void assign(constraint& c, literal lit); bool assigned_above(literal above, literal below); void get_antecedents(literal l, constraint const& c, literal_vector & r); bool validate_conflict(constraint const& c) const; bool validate_unit_propagation(constraint const& c, literal alit) const; void validate_eliminated(); void validate_eliminated(ptr_vector const& cs); void attach_constraint(constraint const& c); void detach_constraint(constraint const& c); lbool eval(constraint const& c) const; lbool eval(model const& m, constraint const& c) const; lbool eval(lbool a, lbool b) const; void assert_unconstrained(literal lit, literal_vector const& lits); void flush_roots(constraint& c); void recompile(constraint& c); void split_root(constraint& c); unsigned next_id() { return m_constraint_id++; } void set_non_learned(constraint& c); // cardinality bool init_watch(card& c); lbool add_assign(card& c, literal lit); void clear_watch(card& c); void reset_coeffs(); void reset_marked_literals(); void get_antecedents(literal l, card const& c, literal_vector & r); void flush_roots(card& c); void recompile(card& c); bool clausify(card& c); bool clausify(literal lit, unsigned n, literal const* lits, unsigned k); lbool eval(card const& c) const; lbool eval(model const& m, card const& c) const; double get_reward(card const& c, literal_occs_fun& occs) const; // xr specific functionality void clear_watch(xr& x); bool init_watch(xr& x); bool parity(xr const& x, unsigned offset) const; lbool add_assign(xr& x, literal alit); void get_xr_antecedents(literal l, unsigned index, justification js, literal_vector& r); void get_antecedents(literal l, xr const& x, literal_vector & r); void simplify(xr& x); void extract_xor(); void merge_xor(); struct clause_filter { unsigned m_filter; clause* m_clause; clause_filter(unsigned f, clause* cp): m_filter(f), m_clause(cp) {} }; typedef svector bool_vector; unsigned m_max_xor_size; vector> m_clause_filters; // index of clauses. unsigned m_barbet_combination; // bit-mask of parities that have been found vector m_barbet_parity; // lookup parity for clauses clause_vector m_barbet_clauses_to_remove; // remove clauses that become xors unsigned_vector m_barbet_var_position; // position of var in main clause literal_vector m_barbet_clause; // reference clause with literals sorted according to main clause unsigned_vector m_barbet_missing; // set of indices not occurring in clause. void init_clause_filter(); void init_clause_filter(clause_vector& clauses); inline void barbet_set_combination(unsigned mask) { m_barbet_combination |= (1 << mask); } inline bool barbet_get_combination(unsigned mask) const { return (m_barbet_combination & (1 << mask)) != 0; } void barbet_extract_xor(); void barbet_init_parity(); void barbet_extract_xor(clause& c); bool barbet_extract_xor(bool parity, clause& c, clause& c2); bool barbet_extract_xor(bool parity, clause& c, literal l1, literal l2); bool barbet_update_combinations(clause& c, bool parity, unsigned mask); void barbet_add_xor(bool parity, clause& c); unsigned get_clause_filter(clause& c); vector> m_ternary; void extract_ternary(clause_vector const& clauses); bool extract_xor(clause& c, literal l); bool extract_xor(clause& c1, clause& c2); bool clausify(xr& x); void flush_roots(xr& x); lbool eval(xr const& x) const; lbool eval(model const& m, xr const& x) const; // pb functionality unsigned m_a_max; bool init_watch(pb& p); lbool add_assign(pb& p, literal alit); void add_index(pb& p, unsigned index, literal lit); void clear_watch(pb& p); void get_antecedents(literal l, pb const& p, literal_vector & r); void split_root(pb_base& p); void simplify(pb_base& p); void simplify2(pb& p); bool is_cardinality(pb const& p); void flush_roots(pb& p); void recompile(pb& p); bool clausify(pb& p); bool is_cardinality(pb const& p, literal_vector& lits); lbool eval(pb const& p) const; lbool eval(model const& m, pb const& p) const; double get_reward(pb const& p, literal_occs_fun& occs) const; // RoundingPb conflict resolution lbool resolve_conflict_rs(); void round_to_one(ineq& ineq, bool_var v); void round_to_one(bool_var v); void divide(unsigned c); void resolve_on(literal lit); void resolve_with(ineq const& ineq); void reset_marks(unsigned idx); void mark_variables(ineq const& ineq); void bail_resolve_conflict(unsigned idx); // access solver inline lbool value(bool_var v) const { return value(literal(v, false)); } inline lbool value(literal lit) const { return m_lookahead ? m_lookahead->value(lit) : m_solver->value(lit); } inline lbool value(model const& m, literal l) const { return l.sign() ? ~m[l.var()] : m[l.var()]; } inline bool is_false(literal lit) const { return l_false == value(lit); } inline unsigned lvl(literal lit) const { return m_lookahead || m_unit_walk ? 0 : m_solver->lvl(lit); } inline unsigned lvl(bool_var v) const { return m_lookahead || m_unit_walk ? 0 : m_solver->lvl(v); } inline bool inconsistent() const { if (m_lookahead) return m_lookahead->inconsistent(); if (m_unit_walk) return m_unit_walk->inconsistent(); return m_solver->inconsistent(); } inline watch_list& get_wlist(literal l) { return m_lookahead ? m_lookahead->get_wlist(l) : m_solver->get_wlist(l); } inline watch_list const& get_wlist(literal l) const { return m_lookahead ? m_lookahead->get_wlist(l) : m_solver->get_wlist(l); } inline void assign(literal l, justification j) { if (m_lookahead) m_lookahead->assign(l); else if (m_unit_walk) m_unit_walk->assign(l); else m_solver->assign(l, j); } inline void set_conflict(justification j, literal l) { if (m_lookahead) m_lookahead->set_conflict(); else if (m_unit_walk) m_unit_walk->set_conflict(); else m_solver->set_conflict(j, l); } inline config const& get_config() const { return m_lookahead ? m_lookahead->get_config() : m_solver->get_config(); } inline void drat_add(literal_vector const& c, svector const& premises) { if (m_solver) m_solver->m_drat.add(c, premises); } mutable bool m_overflow; void reset_active_var_set(); bool test_and_set_active(bool_var v); void inc_coeff(literal l, unsigned offset); int64_t get_coeff(bool_var v) const; uint64_t get_coeff(literal lit) const; wliteral get_wliteral(bool_var v); unsigned get_abs_coeff(bool_var v) const; int get_int_coeff(bool_var v) const; unsigned get_bound() const; void inc_bound(int64_t i); literal get_asserting_literal(literal conseq); void process_antecedent(literal l, unsigned offset); void process_antecedent(literal l) { process_antecedent(l, 1); } void process_card(card& c, unsigned offset); void cut(); bool create_asserting_lemma(); // validation utilities bool validate_conflict(card const& c) const; bool validate_conflict(xr const& x) const; bool validate_conflict(pb const& p) const; bool validate_assign(literal_vector const& lits, literal lit); bool validate_lemma(); bool validate_ineq(ineq const& ineq) const; bool validate_unit_propagation(card const& c, literal alit) const; bool validate_unit_propagation(pb const& p, literal alit) const; bool validate_unit_propagation(pb const& p, literal_vector const& r, literal alit) const; bool validate_unit_propagation(xr const& x, literal alit) const; bool validate_conflict(literal_vector const& lits, ineq& p); bool validate_watch_literals() const; bool validate_watch_literal(literal lit) const; bool validate_watched_constraint(constraint const& c) const; bool validate_watch(pb const& p, literal alit) const; bool is_watching(literal lit, constraint const& c) const; literal translate_to_sat(solver& s, u_map& translation, ineq const& pb); literal translate_to_sat(solver& s, u_map& translation, ineq& a, ineq& b); literal translate_to_sat(solver& s, u_map& translation, literal lit); ineq negate(ineq const& a) const; void push_lit(literal_vector& lits, literal lit); ineq m_A, m_B, m_C; void active2pb(ineq& p); constraint* active2lemma(); constraint* active2constraint(); constraint* active2card(); void active2wlits(); void active2wlits(svector& wlits); void justification2pb(justification const& j, literal lit, unsigned offset, ineq& p); void constraint2pb(constraint& cnstr, literal lit, unsigned offset, ineq& p); bool validate_resolvent(); unsigned get_coeff(ineq const& pb, literal lit); void display(std::ostream& out, ineq const& p, bool values = false) const; void display(std::ostream& out, card const& c, bool values) const; void display(std::ostream& out, pb const& p, bool values) const; void display(std::ostream& out, xr const& c, bool values) const; void display_lit(std::ostream& out, literal l, unsigned sz, bool values) const; constraint* add_at_least(literal l, literal_vector const& lits, unsigned k, bool learned); constraint* add_pb_ge(literal l, svector const& wlits, unsigned k, bool learned); constraint* add_xr(literal_vector const& lits, bool learned); literal add_xor_def(literal_vector& lits, bool learned = false); bool all_distinct(literal_vector const& lits); bool all_distinct(xr const& x); bool all_distinct(clause const& cl); void copy_core(ba_solver* result, bool learned); void copy_constraints(ba_solver* result, ptr_vector const& constraints); public: ba_solver(); ~ba_solver() override; void set_solver(solver* s) override { m_solver = s; } void set_lookahead(lookahead* l) override { m_lookahead = l; } void set_unit_walk(unit_walk* u) override { m_unit_walk = u; } void add_at_least(bool_var v, literal_vector const& lits, unsigned k); void add_pb_ge(bool_var v, svector const& wlits, unsigned k); void add_xr(literal_vector const& lits); bool propagate(literal l, ext_constraint_idx idx) override; lbool resolve_conflict() override; void get_antecedents(literal l, ext_justification_idx idx, literal_vector & r) override; void asserted(literal l) override; check_result check() override; void push() override; void pop(unsigned n) override; void pre_simplify() override; void simplify() override; void clauses_modifed() override; lbool get_phase(bool_var v) override; bool set_root(literal l, literal r) override; 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; void find_mutexes(literal_vector& lits, vector & mutexes) override; void pop_reinit() override; void gc() override; unsigned max_var(unsigned w) const override; double get_reward(literal l, ext_justification_idx idx, literal_occs_fun& occs) const override; bool is_extended_binary(ext_justification_idx idx, literal_vector & r) override; void init_use_list(ext_use_list& ul) override; bool is_blocked(literal l, ext_constraint_idx idx) override; bool check_model(model const& m) const override; ptr_vector const & constraints() const { return m_constraints; } std::ostream& display(std::ostream& out, constraint const& c, bool values) const; bool validate() override; }; }; #endif z3-z3-4.8.7/src/sat/dimacs.cpp000066400000000000000000000055451356505360400157620ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: dimacs.cpp Abstract: Dimacs CNF parser Author: Leonardo de Moura (leonardo) 2011-07-26. Revision History: --*/ #include "sat/dimacs.h" #undef max #undef min #include "sat/sat_solver.h" struct lex_error {}; class stream_buffer { std::istream & m_stream; int m_val; unsigned m_line; public: stream_buffer(std::istream & s): m_stream(s), m_line(0) { m_val = m_stream.get(); } int operator *() const { return m_val; } void operator ++() { m_val = m_stream.get(); if (m_val == '\n') ++m_line; } unsigned line() const { return m_line; } }; template void skip_whitespace(Buffer & in) { while ((*in >= 9 && *in <= 13) || *in == 32) { ++in; } } template void skip_line(Buffer & in) { while(true) { if (*in == EOF) { return; } if (*in == '\n') { ++in; return; } ++in; } } template int parse_int(Buffer & in, std::ostream& err) { int val = 0; bool neg = false; skip_whitespace(in); if (*in == '-') { neg = true; ++in; } else if (*in == '+') { ++in; } if (*in < '0' || *in > '9') { err << "(error, \"unexpected char: " << *in << " line: " << in.line() << "\")\n"; throw lex_error(); } while (*in >= '0' && *in <= '9') { val = val*10 + (*in - '0'); ++in; } return neg ? -val : val; } template void read_clause(Buffer & in, std::ostream& err, sat::solver & solver, sat::literal_vector & lits) { int parsed_lit; int var; lits.reset(); while (true) { parsed_lit = parse_int(in, err); if (parsed_lit == 0) break; var = abs(parsed_lit); SASSERT(var > 0); while (static_cast(var) >= solver.num_vars()) solver.mk_var(); lits.push_back(sat::literal(var, parsed_lit < 0)); } } template bool parse_dimacs_core(Buffer & in, std::ostream& err, sat::solver & solver) { sat::literal_vector lits; try { while (true) { skip_whitespace(in); if (*in == EOF) { break; } else if (*in == 'c' || *in == 'p') { skip_line(in); } else { read_clause(in, err, solver, lits); solver.mk_clause(lits.size(), lits.c_ptr()); } } } catch (lex_error) { return false; } return true; } bool parse_dimacs(std::istream & in, std::ostream& err, sat::solver & solver) { stream_buffer _in(in); return parse_dimacs_core(_in, err, solver); } z3-z3-4.8.7/src/sat/dimacs.h000066400000000000000000000005531356505360400154210ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: dimacs.h Abstract: Dimacs CNF parser Author: Leonardo de Moura (leonardo) 2011-07-26. Revision History: --*/ #ifndef DIMACS_H_ #define DIMACS_H_ #include "sat/sat_types.h" bool parse_dimacs(std::istream & s, std::ostream& err, sat::solver & solver); #endif /* DIMACS_PARSER_H_ */ z3-z3-4.8.7/src/sat/sat_allocator.h000066400000000000000000000060431356505360400170100ustar00rootroot00000000000000/*++ Copyright (c) 2018 Microsoft Corporation Module Name: sat_allocator.h Abstract: Small object allocator suitable for clauses Author: Nikolaj bjorner (nbjorner) 2018-04-26. Revision History: --*/ #ifndef SAT_ALLOCATOR_H_ #define SAT_ALLOCATOR_H_ #include "util/vector.h" #include "util/machine.h" class sat_allocator { static const unsigned CHUNK_SIZE = (1 << 16) - sizeof(char*); static const unsigned SMALL_OBJ_SIZE = 512; static const unsigned MASK = ((1 << PTR_ALIGNMENT) - 1); static const unsigned NUM_FREE = 1 + (SMALL_OBJ_SIZE >> PTR_ALIGNMENT); struct chunk { char * m_curr; char m_data[CHUNK_SIZE]; chunk():m_curr(m_data) {} }; char const * m_id; size_t m_alloc_size; ptr_vector m_chunks; void * m_chunk_ptr; ptr_vector m_free[NUM_FREE]; unsigned align_size(size_t sz) const { return free_slot_id(sz) << PTR_ALIGNMENT; } unsigned free_slot_id(size_t size) const { return (static_cast(size >> PTR_ALIGNMENT) + ((0 != (size & MASK)) ? 1u : 0u)); } public: sat_allocator(char const * id = "unknown"): m_id(id), m_alloc_size(0), m_chunk_ptr(nullptr) {} ~sat_allocator() { reset(); } void reset() { for (chunk * ch : m_chunks) dealloc(ch); m_chunks.reset(); for (unsigned i = 0; i < NUM_FREE; ++i) m_free[i].reset(); m_alloc_size = 0; m_chunk_ptr = nullptr; } void * allocate(size_t size) { m_alloc_size += size; if (size >= SMALL_OBJ_SIZE) { return memory::allocate(size); } unsigned slot_id = free_slot_id(size); if (!m_free[slot_id].empty()) { void* result = m_free[slot_id].back(); m_free[slot_id].pop_back(); return result; } if (m_chunks.empty()) { m_chunks.push_back(alloc(chunk)); m_chunk_ptr = m_chunks.back(); } unsigned sz = align_size(size); if ((char*)m_chunk_ptr + sz > (char*)m_chunks.back() + CHUNK_SIZE) { m_chunks.push_back(alloc(chunk)); m_chunk_ptr = m_chunks.back(); } void * result = m_chunk_ptr; m_chunk_ptr = (char*)m_chunk_ptr + sz; return result; } void deallocate(size_t size, void * p) { m_alloc_size -= size; if (size >= SMALL_OBJ_SIZE) { memory::deallocate(p); } else { m_free[free_slot_id(size)].push_back(p); } } size_t get_allocation_size() const { return m_alloc_size; } char const* id() const { return m_id; } }; inline void * operator new(size_t s, sat_allocator & r) { return r.allocate(s); } inline void * operator new[](size_t s, sat_allocator & r) { return r.allocate(s); } inline void operator delete(void * p, sat_allocator & r) { UNREACHABLE(); } inline void operator delete[](void * p, sat_allocator & r) { UNREACHABLE(); } #endif /* SAT_ALLOCATOR_H_ */ z3-z3-4.8.7/src/sat/sat_asymm_branch.cpp000066400000000000000000000406621356505360400200330ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_asymm_branch.cpp Abstract: SAT solver asymmetric branching Author: Leonardo de Moura (leonardo) 2011-05-30. Revision History: --*/ #include "sat/sat_asymm_branch.h" #include "sat/sat_asymm_branch_params.hpp" #include "sat/sat_solver.h" #include "sat/sat_big.h" #include "util/stopwatch.h" #include "util/trace.h" namespace sat { asymm_branch::asymm_branch(solver & _s, params_ref const & p): s(_s), m_params(p), m_counter(0) { updt_params(p); reset_statistics(); m_calls = 0; m_touch_index = 0; } struct clause_size_lt { bool operator()(clause * c1, clause * c2) const { return c1->size() > c2->size(); } }; struct asymm_branch::report { asymm_branch & m_asymm_branch; stopwatch m_watch; 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_units(a.s.init_trail_size()) { m_watch.start(); } ~report() { m_watch.stop(); 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); 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";); } }; void asymm_branch::process_bin(big& big) { m_tr += big.reduce_tr(s); } bool asymm_branch::process(big& big, bool learned) { unsigned elim0 = m_elim_literals; unsigned eliml0 = m_elim_learned_literals; for (unsigned i = 0; i < m_asymm_branch_rounds; ++i) { unsigned elim = m_elim_literals + m_tr; big.init(s, learned); process(&big, s.m_clauses); process(&big, s.m_learned); process_bin(big); s.propagate(false); if (s.m_inconsistent) break; unsigned num_elim = m_elim_literals + m_tr - elim; IF_VERBOSE(4, verbose_stream() << "(sat-asymm-branch-step :elim " << num_elim << ")\n";); if (num_elim == 0) break; } 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; } bool asymm_branch::process(bool learned) { unsigned eliml0 = m_elim_learned_literals; unsigned elim = m_elim_literals; process(nullptr, s.m_clauses); if (learned) process(nullptr, s.m_learned); s.propagate(false); 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; } void asymm_branch::process(big* big, clause_vector& clauses) { int64_t limit = -m_asymm_branch_limit; std::stable_sort(clauses.begin(), clauses.end(), clause_size_lt()); m_counter -= clauses.size(); clause_vector::iterator it = clauses.begin(); clause_vector::iterator it2 = it; clause_vector::iterator end = clauses.end(); try { for (; it != end; ++it) { if (s.inconsistent()) { for (; it != end; ++it, ++it2) { *it2 = *it; } break; } clause & c = *(*it); if (m_counter < limit || s.inconsistent() || c.was_removed()) { *it2 = *it; ++it2; continue; } s.checkpoint(); if (big ? !process_sampled(*big, c) : !process(c)) { continue; // clause was removed } *it2 = *it; ++it2; } clauses.set_end(it2); } catch (solver_exception & ex) { // put m_clauses in a consistent state... for (; it != end; ++it, ++it2) { *it2 = *it; } clauses.set_end(it2); m_counter = -m_counter; throw ex; } } void asymm_branch::operator()(bool force) { ++m_calls; if (m_calls <= m_asymm_branch_delay) return; if (!m_asymm_branch && !m_asymm_branch_all && !m_asymm_branch_sampled) return; s.propagate(false); // must propagate, since it uses s.push() if (s.m_inconsistent) return; if (!force && m_counter > 0) { m_counter /= 100; return; } CASSERT("asymm_branch", s.check_invariant()); TRACE("asymm_branch_detail", s.display(tout);); report rpt(*this); svector saved_phase(s.m_phase); bool change = true; unsigned counter = 0; while (change && counter < 2) { ++counter; change = false; s.m_touch_index++; if (m_asymm_branch_sampled) { big big(s.m_rand); if (process(big, true)) change = true; } if (m_asymm_branch_sampled) { big big(s.m_rand); if (process(big, false)) change = true; } if (m_asymm_branch) { m_counter = 0; if (process(false)) change = true; m_counter = -m_counter; } m_touch_index = s.m_touch_index; } s.m_phase = saved_phase; m_asymm_branch_limit *= 2; if (m_asymm_branch_limit > UINT_MAX) m_asymm_branch_limit = UINT_MAX; CASSERT("asymm_branch", s.check_invariant()); } /** \brief try asymmetric branching on all literals in clause. */ bool asymm_branch::process_all(clause & c) { scoped_detach scoped_d(s, c); // clause must not be used for propagation unsigned sz = c.size(); SASSERT(sz > 0); unsigned i = 0, new_sz = sz; for (i = sz; i-- > 0; ) { if (flip_literal_at(c, i, new_sz)) return cleanup(scoped_d, c, i, new_sz); } return true; } struct asymm_branch::compare_left { big& s; compare_left(big& s): s(s) {} bool operator()(literal u, literal v) const { return s.get_left(u) < s.get_left(v); } }; bool asymm_branch::is_touched(bool_var v) const { return s.m_touched[v] >= m_touch_index; } void asymm_branch::sort(big& big, clause const& c) { sort(big, c.begin(), c.end()); } void asymm_branch::sort(big& big, literal const* begin, literal const* end) { m_pos.reset(); m_neg.reset(); for (; begin != end; ++begin) { literal l = *begin; m_pos.push_back(l); m_neg.push_back(~l); } compare_left cmp(big); std::sort(m_pos.begin(), m_pos.end(), cmp); std::sort(m_neg.begin(), m_neg.end(), cmp); IF_VERBOSE(100, for (literal l : m_pos) verbose_stream() << big.get_left(l) << " "; verbose_stream() << "\n"; for (literal l : m_neg) verbose_stream() << big.get_left(l) << " "; verbose_stream() << "\n"; ); } bool asymm_branch::uhte(big& big, clause & c) { unsigned pindex = 0, nindex = 0; literal lpos = m_pos[pindex++]; literal lneg = m_neg[nindex++]; while (true) { if (big.get_left(lneg) > big.get_left(lpos)) { if (pindex == m_pos.size()) return false; lpos = m_pos[pindex++]; } else if (big.get_right(lneg) < big.get_right(lpos) || (m_pos.size() == 2 && (lpos == ~lneg || big.get_parent(lpos) == lneg))) { if (nindex == m_neg.size()) return false; lneg = m_neg[nindex++]; } else { return true; } } return false; } void asymm_branch::uhle(big& big) { m_to_delete.reset(); if (m_to_delete.empty()) { int right = big.get_right(m_pos.back()); for (unsigned i = m_pos.size() - 1; i-- > 0; ) { literal lit = m_pos[i]; int right2 = big.get_right(lit); if (right2 > right) { // lit => last, so lit can be deleted m_to_delete.push_back(lit); } else { right = right2; } } } if (m_to_delete.empty()) { int right = big.get_right(m_neg[0]); for (unsigned i = 1; i < m_neg.size(); ++i) { literal lit = m_neg[i]; int right2 = big.get_right(lit); if (right > right2) { // ~first => ~lit m_to_delete.push_back(~lit); } else { right = right2; } } } } bool asymm_branch::uhle(scoped_detach& scoped_d, big& big, clause & c) { uhle(big); if (!m_to_delete.empty()) { unsigned j = 0; for (unsigned i = 0; i < c.size(); ++i) { literal lit = c[i]; switch (s.value(lit)) { case l_true: scoped_d.del_clause(); return false; case l_false: break; default: if (!m_to_delete.contains(lit)) { if (i != j) { std::swap(c[i], c[j]); } j++; } break; } } return re_attach(scoped_d, c, j); } else { return true; } } bool asymm_branch::propagate_literal(clause const& c, literal l) { if (!is_touched(l.var())) { return false; } SASSERT(!s.inconsistent()); TRACE("asymm_branch_detail", tout << "assigning: " << l << "\n";); s.assign_scoped(l); s.propagate_core(false); // must not use propagate(), since check_missed_propagation may fail for c return s.inconsistent(); } bool asymm_branch::flip_literal_at(clause const& c, unsigned flip_index, unsigned& new_sz) { VERIFY(s.m_trail.size() == s.m_qhead); bool found_conflict = false; unsigned i = 0, sz = c.size(); s.push(); for (i = 0; !found_conflict && i < sz; i++) { if (i == flip_index) continue; found_conflict = propagate_literal(c, ~c[i]); } if (!found_conflict) { SASSERT(sz == i); found_conflict = propagate_literal(c, c[flip_index]); } s.pop(1); new_sz = i; return found_conflict; } bool asymm_branch::cleanup(scoped_detach& scoped_d, clause& c, unsigned skip_idx, unsigned new_sz) { unsigned j = 0; for (unsigned i = 0; i < new_sz; i++) { if (skip_idx == i) continue; literal l = c[i]; switch (s.value(l)) { case l_undef: if (i != j) { std::swap(c[i], c[j]); } j++; break; case l_false: break; case l_true: UNREACHABLE(); break; } } new_sz = j; return re_attach(scoped_d, c, new_sz); } bool asymm_branch::re_attach(scoped_detach& scoped_d, clause& c, unsigned new_sz) { VERIFY(s.m_trail.size() == s.m_qhead); unsigned old_sz = c.size(); m_elim_literals += old_sz - new_sz; if (c.is_learned()) { m_elim_learned_literals += old_sz - new_sz; } switch (new_sz) { case 0: s.set_conflict(); return false; case 1: TRACE("asymm_branch", tout << "produced unit clause: " << c[0] << "\n";); 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. case 2: SASSERT(s.value(c[0]) == l_undef && s.value(c[1]) == l_undef); VERIFY(s.value(c[0]) == l_undef && s.value(c[1]) == l_undef); s.mk_bin_clause(c[0], c[1], c.is_learned()); if (s.m_trail.size() > s.m_qhead) s.propagate_core(false); scoped_d.del_clause(); return false; default: s.shrink(c, old_sz, new_sz); return true; } } bool asymm_branch::process_sampled(big& big, clause & c) { scoped_detach scoped_d(s, c); sort(big, c); if (uhte(big, c)) { // don't touch hidden tautologies. // ATE takes care of them. return true; } return uhle(scoped_d, big, c); } bool asymm_branch::process(clause & c) { TRACE("asymm_branch_detail", tout << "processing: " << c << "\n";); SASSERT(s.scope_lvl() == 0); SASSERT(!s.inconsistent()); unsigned sz = c.size(); SASSERT(sz > 0); unsigned i; // check if the clause is already satisfied for (i = 0; i < sz; i++) { if (s.value(c[i]) == l_true) { s.detach_clause(c); s.del_clause(c); return false; } } m_counter -= c.size(); if (m_asymm_branch_all) return process_all(c); // try asymmetric branching // clause must not be used for propagation scoped_detach scoped_d(s, c); unsigned new_sz = c.size(); unsigned flip_position = m_rand(c.size()); bool found_conflict = flip_literal_at(c, flip_position, new_sz); SASSERT(!s.inconsistent()); SASSERT(s.scope_lvl() == 0); if (!found_conflict) { // clause size can't be reduced. return true; } else { // clause can be reduced return cleanup(scoped_d, c, flip_position, new_sz); } } void asymm_branch::updt_params(params_ref const & _p) { sat_asymm_branch_params p(_p); m_asymm_branch = p.asymm_branch(); m_asymm_branch_rounds = p.asymm_branch_rounds(); m_asymm_branch_delay = p.asymm_branch_delay(); m_asymm_branch_sampled = p.asymm_branch_sampled(); m_asymm_branch_limit = p.asymm_branch_limit(); m_asymm_branch_all = p.asymm_branch_all(); if (m_asymm_branch_limit > UINT_MAX) m_asymm_branch_limit = UINT_MAX; } void asymm_branch::collect_param_descrs(param_descrs & d) { sat_asymm_branch_params::collect_param_descrs(d); } void asymm_branch::collect_statistics(statistics & st) const { st.update("sat elim literals", m_elim_literals); st.update("sat tr", m_tr); } void asymm_branch::reset_statistics() { m_elim_literals = 0; m_elim_learned_literals = 0; m_tr = 0; } }; z3-z3-4.8.7/src/sat/sat_asymm_branch.h000066400000000000000000000052471356505360400175000ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_asymm_branch.h Abstract: SAT solver asymmetric branching Author: Leonardo de Moura (leonardo) 2011-05-30. Revision History: --*/ #ifndef SAT_ASYMM_BRANCH_H_ #define SAT_ASYMM_BRANCH_H_ #include "sat/sat_types.h" #include "sat/sat_big.h" #include "util/statistics.h" #include "util/params.h" namespace sat { class solver; class scoped_detach; class asymm_branch { struct report; solver & s; params_ref m_params; int64_t m_counter; random_gen m_rand; unsigned m_calls; unsigned m_touch_index; // config bool m_asymm_branch; unsigned m_asymm_branch_rounds; unsigned m_asymm_branch_delay; bool m_asymm_branch_sampled; bool m_asymm_branch_all; int64_t m_asymm_branch_limit; // stats unsigned m_elim_literals; unsigned m_elim_learned_literals; unsigned m_tr; literal_vector m_pos, m_neg; // literals (complements of literals) in clauses sorted by discovery time (m_left in BIG). svector> m_pos1, m_neg1; literal_vector m_to_delete; literal_vector m_tmp; struct compare_left; bool is_touched(bool_var v) const; void sort(big& big, literal const* begin, literal const* end); void sort(big & big, clause const& c); bool uhle(scoped_detach& scoped_d, big & big, clause & c); void uhle(big & big); bool uhte(big & big, clause & c); bool re_attach(scoped_detach& scoped_d, clause& c, unsigned new_sz); bool process(bool learned); bool process(big& big, bool learned); bool process(clause & c); bool process_sampled(big& big, clause & c); void process(big* big, clause_vector & c); bool process_all(clause & c); void process_bin(big& big); bool flip_literal_at(clause const& c, unsigned flip_index, unsigned& new_sz); bool cleanup(scoped_detach& scoped_d, clause& c, unsigned skip_index, unsigned new_sz); bool propagate_literal(clause const& c, literal l); public: asymm_branch(solver & s, params_ref const & p); void operator()(bool force); void updt_params(params_ref const & p); static void collect_param_descrs(param_descrs & d); void collect_statistics(statistics & st) const; void reset_statistics(); void init_search() { m_calls = 0; } inline void dec(unsigned c) { m_counter -= c; } }; }; #endif z3-z3-4.8.7/src/sat/sat_asymm_branch_params.pyg000066400000000000000000000016031356505360400214030ustar00rootroot00000000000000def_module_params(module_name='sat', class_name='sat_asymm_branch_params', export=True, params=(('asymm_branch', BOOL, True, 'asymmetric branching'), ('asymm_branch.rounds', UINT, 2, 'maximal number of rounds to run asymmetric branch simplifications if progress is made'), ('asymm_branch.delay', UINT, 1, 'number of simplification rounds to wait until invoking asymmetric branch simplification'), ('asymm_branch.sampled', BOOL, True, 'use sampling based asymmetric branching based on binary implication graph'), ('asymm_branch.limit', UINT, 100000000, 'approx. maximum number of literals visited during asymmetric branching'), ('asymm_branch.all', BOOL, False, 'asymmetric branching on all literals per clause'))) z3-z3-4.8.7/src/sat/sat_bdd.cpp000066400000000000000000000701211356505360400161120ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: sat_bdd.cpp Abstract: Simple BDD package modeled after BuDDy, which is modeled after CUDD. Author: Nikolaj Bjorner (nbjorner) 2017-10-13 Revision History: --*/ #include "sat/sat_bdd.h" #include "util/trace.h" #include "util/stopwatch.h" namespace sat { bdd_manager::bdd_manager(unsigned num_vars) { m_cost_metric = bdd_cost; m_cost_bdd = 0; for (BDD a = 0; a < 2; ++a) { for (BDD b = 0; b < 2; ++b) { for (unsigned op = bdd_and_op; op < bdd_not_op; ++op) { unsigned index = a + 2*b + 4*op; m_apply_const.reserve(index+1); m_apply_const[index] = apply_const(a, b, static_cast(op)); } } } // add dummy nodes for operations, and true, false bdds. for (unsigned i = 0; i <= bdd_no_op + 2; ++i) { m_nodes.push_back(bdd_node(0,0,0)); m_nodes.back().m_refcount = max_rc; m_nodes.back().m_index = m_nodes.size()-1; } m_spare_entry = nullptr; m_max_num_bdd_nodes = 1 << 24; // up to 16M nodes m_mark_level = 0; alloc_free_nodes(1024 + num_vars); m_disable_gc = false; m_is_new_node = false; // add variables for (unsigned i = 0; i < num_vars; ++i) { reserve_var(i); } } bdd_manager::~bdd_manager() { if (m_spare_entry) { m_alloc.deallocate(sizeof(*m_spare_entry), m_spare_entry); } for (auto* e : m_op_cache) { SASSERT(e != m_spare_entry); m_alloc.deallocate(sizeof(*e), e); } } bdd_manager::BDD bdd_manager::apply_const(BDD a, BDD b, bdd_op op) { SASSERT(is_const(a) && is_const(b)); switch (op) { case bdd_and_op: return (a == true_bdd && b == true_bdd) ? true_bdd : false_bdd; case bdd_or_op: return (a == true_bdd || b == true_bdd) ? true_bdd : false_bdd; case bdd_xor_op: return (a == b) ? false_bdd : true_bdd; default: return false_bdd; } } bdd_manager::BDD bdd_manager::apply(BDD arg1, BDD arg2, bdd_op op) { bool first = true; SASSERT(well_formed()); while (true) { try { return apply_rec(arg1, arg2, op); } catch (const mem_out &) { try_reorder(); if (!first) throw; first = false; } } SASSERT(well_formed()); } bdd bdd_manager::mk_true() { return bdd(true_bdd, this); } bdd bdd_manager::mk_false() { return bdd(false_bdd, this); } bdd bdd_manager::mk_and(bdd const& a, bdd const& b) { return bdd(apply(a.root, b.root, bdd_and_op), this); } bdd bdd_manager::mk_or(bdd const& a, bdd const& b) { return bdd(apply(a.root, b.root, bdd_or_op), this); } bdd bdd_manager::mk_xor(bdd const& a, bdd const& b) { return bdd(apply(a.root, b.root, bdd_xor_op), this); } bdd bdd_manager::mk_exists(unsigned v, bdd const& b) { return mk_exists(1, &v, b); } bdd bdd_manager::mk_forall(unsigned v, bdd const& b) { return mk_forall(1, &v, b); } bool bdd_manager::check_result(op_entry*& e1, op_entry const* e2, BDD a, BDD b, BDD c) { if (e1 != e2) { SASSERT(e2->m_result != null_bdd); push_entry(e1); e1 = nullptr; return true; } else { e1->m_bdd1 = a; e1->m_bdd2 = b; e1->m_op = c; SASSERT(e1->m_result == null_bdd); return false; } } bdd_manager::BDD bdd_manager::apply_rec(BDD a, BDD b, bdd_op op) { switch (op) { case bdd_and_op: if (a == b) return a; if (is_false(a) || is_false(b)) return false_bdd; if (is_true(a)) return b; if (is_true(b)) return a; break; case bdd_or_op: if (a == b) return a; if (is_false(a)) return b; if (is_false(b)) return a; if (is_true(a) || is_true(b)) return true_bdd; break; case bdd_xor_op: if (a == b) return false_bdd; if (is_false(a)) return b; if (is_false(b)) return a; break; default: UNREACHABLE(); break; } if (is_const(a) && is_const(b)) { return m_apply_const[a + 2*b + 4*op]; } op_entry * e1 = pop_entry(a, b, op); op_entry const* e2 = m_op_cache.insert_if_not_there(e1); if (check_result(e1, e2, a, b, op)) { SASSERT(!m_free_nodes.contains(e2->m_result)); return e2->m_result; } // SASSERT(well_formed()); BDD r; if (level(a) == level(b)) { push(apply_rec(lo(a), lo(b), op)); push(apply_rec(hi(a), hi(b), op)); r = make_node(level(a), read(2), read(1)); } else if (level(a) > level(b)) { push(apply_rec(lo(a), b, op)); push(apply_rec(hi(a), b, op)); r = make_node(level(a), read(2), read(1)); } else { push(apply_rec(a, lo(b), op)); push(apply_rec(a, hi(b), op)); r = make_node(level(b), read(2), read(1)); } pop(2); e1->m_result = r; // SASSERT(well_formed()); SASSERT(!m_free_nodes.contains(r)); return r; } void bdd_manager::push(BDD b) { m_bdd_stack.push_back(b); } void bdd_manager::pop(unsigned num_scopes) { m_bdd_stack.shrink(m_bdd_stack.size() - num_scopes); } bdd_manager::BDD bdd_manager::read(unsigned index) { return m_bdd_stack[m_bdd_stack.size() - index]; } bdd_manager::op_entry* bdd_manager::pop_entry(BDD l, BDD r, BDD op) { op_entry* result = nullptr; if (m_spare_entry) { result = m_spare_entry; m_spare_entry = nullptr; result->m_bdd1 = l; result->m_bdd2 = r; result->m_op = op; } else { void * mem = m_alloc.allocate(sizeof(op_entry)); result = new (mem) op_entry(l, r, op); } result->m_result = null_bdd; return result; } void bdd_manager::push_entry(op_entry* e) { SASSERT(!m_spare_entry); m_spare_entry = e; } bdd_manager::BDD bdd_manager::make_node(unsigned lvl, BDD l, BDD h) { m_is_new_node = false; if (l == h) { return l; } SASSERT(is_const(l) || level(l) < lvl); SASSERT(is_const(h) || level(h) < lvl); bdd_node n(lvl, l, h); node_table::entry* e = m_node_table.insert_if_not_there2(n); if (e->get_data().m_index != 0) { unsigned result = e->get_data().m_index; return result; } e->get_data().m_refcount = 0; bool do_gc = m_free_nodes.empty(); if (do_gc && !m_disable_gc) { gc(); e = m_node_table.insert_if_not_there2(n); e->get_data().m_refcount = 0; } if (do_gc && m_free_nodes.size()*3 < m_nodes.size()) { if (m_nodes.size() > m_max_num_bdd_nodes) { throw mem_out(); } alloc_free_nodes(m_nodes.size()/2); } SASSERT(!m_free_nodes.empty()); unsigned result = m_free_nodes.back(); m_free_nodes.pop_back(); e->get_data().m_index = result; m_nodes[result] = e->get_data(); m_is_new_node = true; SASSERT(!m_free_nodes.contains(result)); SASSERT(m_nodes[result].m_index == result); return result; } void bdd_manager::try_cnf_reorder(bdd const& b) { m_cost_bdd = b.root; m_cost_metric = cnf_cost; try_reorder(); m_cost_metric = bdd_cost; m_cost_bdd = 0; } void bdd_manager::try_reorder() { gc(); for (auto* e : m_op_cache) { m_alloc.deallocate(sizeof(*e), e); } m_op_cache.reset(); init_reorder(); for (unsigned i = 0; i < m_var2level.size(); ++i) { sift_var(i); } SASSERT(m_op_cache.empty()); SASSERT(well_formed()); } double bdd_manager::current_cost() { switch (m_cost_metric) { case bdd_cost: return m_nodes.size() - m_free_nodes.size(); case cnf_cost: return cnf_size(m_cost_bdd); case dnf_cost: return dnf_size(m_cost_bdd); default: UNREACHABLE(); return 0; } } bool bdd_manager::is_bad_cost(double current_cost, double best_cost) const { return current_cost > 1.1 * best_cost; } void bdd_manager::sift_var(unsigned v) { unsigned lvl = m_var2level[v]; unsigned start = lvl; double best_cost = current_cost(); bool first = true; unsigned max_lvl = m_level2nodes.size()-1; if (lvl*2 < max_lvl) { goto go_down; } go_up: TRACE("bdd", tout << "sift up " << lvl << "\n";); while (lvl < max_lvl) { sift_up(lvl++); double cost = current_cost(); if (is_bad_cost(cost, best_cost)) break; best_cost = std::min(cost, best_cost); } if (first) { first = false; while (lvl != start) { sift_up(--lvl); } goto go_down; } else { while (current_cost() != best_cost) { sift_up(--lvl); } return; } go_down: TRACE("bdd", tout << "sift down " << lvl << "\n";); while (lvl > 0) { sift_up(--lvl); double cost = current_cost(); if (is_bad_cost(cost, best_cost)) break; best_cost = std::min(cost, best_cost); } if (first) { first = false; while (lvl != start) { sift_up(lvl++); } goto go_up; } else { while (current_cost() != best_cost) { sift_up(lvl++); } return; } } void bdd_manager::sift_up(unsigned lvl) { if (m_level2nodes[lvl].empty()) return; // SASSERT(well_formed()); // exchange level and level + 1. m_S.reset(); m_T.reset(); m_to_free.reset(); m_disable_gc = true; for (unsigned n : m_level2nodes[lvl + 1]) { BDD l = lo(n); BDD h = hi(n); if (l == 0 && h == 0) continue; if ((is_const(l) || level(l) != lvl) && (is_const(h) || level(h) != lvl)) { m_S.push_back(n); } else { reorder_decref(l); reorder_decref(h); m_T.push_back(n); } TRACE("bdd", tout << "remove " << n << "\n";); m_node_table.remove(m_nodes[n]); } m_level2nodes[lvl + 1].reset(); m_level2nodes[lvl + 1].append(m_T); for (unsigned n : m_level2nodes[lvl]) { bdd_node& node = m_nodes[n]; m_node_table.remove(node); node.m_level = lvl + 1; if (m_reorder_rc[n] == 0) { m_to_free.push_back(n); } else { TRACE("bdd", tout << "set level " << n << " to " << lvl + 1 << "\n";); m_node_table.insert(node); m_level2nodes[lvl + 1].push_back(n); } } m_level2nodes[lvl].reset(); m_level2nodes[lvl].append(m_S); for (unsigned n : m_S) { m_nodes[n].m_level = lvl; m_node_table.insert(m_nodes[n]); } for (unsigned n : m_T) { BDD l = lo(n); BDD h = hi(n); if (l == 0 && h == 0) continue; BDD a, b, c, d; if (level(l) == lvl + 1) { a = lo(l); b = hi(l); } else { a = b = l; } if (level(h) == lvl + 1) { c = lo(h); d = hi(h); } else { c = d = h; } unsigned ac = make_node(lvl, a, c); if (is_new_node()) { m_level2nodes[lvl].push_back(ac); m_reorder_rc.reserve(ac+1); reorder_incref(a); reorder_incref(c); } unsigned bd = make_node(lvl, b, d); if (is_new_node()) { m_level2nodes[lvl].push_back(bd); m_reorder_rc.reserve(bd+1); reorder_incref(b); reorder_incref(d); } m_nodes[n].m_lo = ac; m_nodes[n].m_hi = bd; reorder_incref(ac); reorder_incref(bd); TRACE("bdd", tout << "transform " << n << " " << " " << a << " " << b << " " << c << " " << d << " " << ac << " " << bd << "\n";); m_node_table.insert(m_nodes[n]); } unsigned v = m_level2var[lvl]; unsigned w = m_level2var[lvl+1]; std::swap(m_level2var[lvl], m_level2var[lvl+1]); std::swap(m_var2level[v], m_var2level[w]); m_disable_gc = false; // add orphaned nodes to free-list for (unsigned i = 0; i < m_to_free.size(); ++i) { unsigned n = m_to_free[i]; bdd_node& node = m_nodes[n]; if (!node.is_internal()) { SASSERT(!m_free_nodes.contains(n)); SASSERT(node.m_refcount == 0); m_free_nodes.push_back(n); m_node_table.remove(node); BDD l = lo(n); BDD h = hi(n); node.set_internal(); reorder_decref(l); if (!m_nodes[l].is_internal() && m_reorder_rc[l] == 0) { m_to_free.push_back(l); } reorder_decref(h); if (!m_nodes[h].is_internal() && m_reorder_rc[h] == 0) { m_to_free.push_back(h); } } } TRACE("bdd", tout << "sift " << lvl << "\n"; display(tout); ); DEBUG_CODE( for (unsigned i = 0; i < m_level2nodes.size(); ++i) { for (unsigned n : m_level2nodes[i]) { bdd_node const& node = m_nodes[n]; SASSERT(node.m_level == i); } }); TRACE("bdd", for (unsigned i = 0; i < m_nodes.size(); ++i) { if (m_reorder_rc[i] != 0) { tout << i << " " << m_reorder_rc[i] << "\n"; }}); // SASSERT(well_formed()); } void bdd_manager::init_reorder() { m_level2nodes.reset(); unsigned sz = m_nodes.size(); m_reorder_rc.fill(sz, 0); for (unsigned i = 0; i < sz; ++i) { if (m_nodes[i].m_refcount > 0) m_reorder_rc[i] = UINT_MAX; } for (unsigned i = 0; i < sz; ++i) { bdd_node const& n = m_nodes[i]; if (n.is_internal()) continue; unsigned lvl = n.m_level; SASSERT(i == m_nodes[i].m_index); m_level2nodes.reserve(lvl + 1); m_level2nodes[lvl].push_back(i); reorder_incref(n.m_lo); reorder_incref(n.m_hi); } TRACE("bdd", display(tout); for (unsigned i = 0; i < sz; ++i) { bdd_node const& n = m_nodes[i]; if (n.is_internal()) continue; unsigned lvl = n.m_level; tout << i << " lvl: " << lvl << " rc: " << m_reorder_rc[i] << " lo " << n.m_lo << " hi " << n.m_hi << "\n"; } ); } void bdd_manager::reorder_incref(unsigned n) { if (m_reorder_rc[n] != UINT_MAX) m_reorder_rc[n]++; } void bdd_manager::reorder_decref(unsigned n) { if (m_reorder_rc[n] != UINT_MAX) m_reorder_rc[n]--; } void bdd_manager::reserve_var(unsigned i) { while (m_var2level.size() <= i) { unsigned v = m_var2level.size(); m_var2bdd.push_back(make_node(v, false_bdd, true_bdd)); m_var2bdd.push_back(make_node(v, true_bdd, false_bdd)); m_nodes[m_var2bdd[2*v]].m_refcount = max_rc; m_nodes[m_var2bdd[2*v+1]].m_refcount = max_rc; m_var2level.push_back(v); m_level2var.push_back(v); } } bdd bdd_manager::mk_var(unsigned i) { reserve_var(i); return bdd(m_var2bdd[2*i], this); } bdd bdd_manager::mk_nvar(unsigned i) { reserve_var(i); return bdd(m_var2bdd[2*i+1], this); } bdd bdd_manager::mk_not(bdd b) { bool first = true; while (true) { try { return bdd(mk_not_rec(b.root), this); } catch (const mem_out &) { try_reorder(); if (!first) throw; first = false; } } } bdd_manager::BDD bdd_manager::mk_not_rec(BDD b) { if (is_true(b)) return false_bdd; if (is_false(b)) return true_bdd; op_entry* e1 = pop_entry(b, b, bdd_not_op); op_entry const* e2 = m_op_cache.insert_if_not_there(e1); if (check_result(e1, e2, b, b, bdd_not_op)) return e2->m_result; push(mk_not_rec(lo(b))); push(mk_not_rec(hi(b))); BDD r = make_node(level(b), read(2), read(1)); pop(2); e1->m_result = r; return r; } bdd bdd_manager::mk_ite(bdd const& c, bdd const& t, bdd const& e) { bool first = true; while (true) { try { return bdd(mk_ite_rec(c.root, t.root, e.root), this); } catch (const mem_out &) { try_reorder(); if (!first) throw; first = false; } } } bdd_manager::BDD bdd_manager::mk_ite_rec(BDD a, BDD b, BDD c) { if (is_true(a)) return b; if (is_false(a)) return c; if (b == c) return b; if (is_true(b)) return apply(a, c, bdd_or_op); if (is_false(c)) return apply(a, b, bdd_and_op); if (is_false(b)) return apply(mk_not_rec(a), c, bdd_and_op); if (is_true(c)) return apply(mk_not_rec(a), b, bdd_or_op); SASSERT(!is_const(a) && !is_const(b) && !is_const(c)); op_entry * e1 = pop_entry(a, b, c); op_entry const* e2 = m_op_cache.insert_if_not_there(e1); if (check_result(e1, e2, a, b, c)) return e2->m_result; unsigned la = level(a), lb = level(b), lc = level(c); BDD r; BDD a1, b1, c1, a2, b2, c2; unsigned lvl = la; if (la >= lb && la >= lc) { a1 = lo(a), a2 = hi(a); lvl = la; } else { a1 = a, a2 = a; } if (lb >= la && lb >= lc) { b1 = lo(b), b2 = hi(b); lvl = lb; } else { b1 = b, b2 = b; } if (lc >= la && lc >= lb) { c1 = lo(c), c2 = hi(c); lvl = lc; } else { c1 = c, c2 = c; } push(mk_ite_rec(a1, b1, c1)); push(mk_ite_rec(a2, b2, c2)); r = make_node(lvl, read(2), read(1)); pop(2); e1->m_result = r; return r; } bdd bdd_manager::mk_exists(unsigned n, unsigned const* vars, bdd const& b) { // SASSERT(well_formed()); return bdd(mk_quant(n, vars, b.root, bdd_or_op), this); } bdd bdd_manager::mk_forall(unsigned n, unsigned const* vars, bdd const& b) { return bdd(mk_quant(n, vars, b.root, bdd_and_op), this); } bdd_manager::BDD bdd_manager::mk_quant(unsigned n, unsigned const* vars, BDD b, bdd_op op) { BDD result = b; for (unsigned i = 0; i < n; ++i) { result = mk_quant_rec(m_var2level[vars[i]], result, op); } return result; } bdd_manager::BDD bdd_manager::mk_quant_rec(unsigned l, BDD b, bdd_op op) { unsigned lvl = level(b); BDD r; if (is_const(b)) { r = b; } else if (lvl == l) { r = apply(lo(b), hi(b), op); } else if (lvl < l) { r = b; } else { BDD a = level2bdd(l); bdd_op q_op = op == bdd_and_op ? bdd_and_proj_op : bdd_or_proj_op; op_entry * e1 = pop_entry(a, b, q_op); op_entry const* e2 = m_op_cache.insert_if_not_there(e1); if (check_result(e1, e2, a, b, q_op)) { r = e2->m_result; } else { SASSERT(e1->m_result == null_bdd); push(mk_quant_rec(l, lo(b), op)); push(mk_quant_rec(l, hi(b), op)); r = make_node(lvl, read(2), read(1)); pop(2); e1->m_result = r; } } SASSERT(r != UINT_MAX); return r; } double bdd_manager::count(BDD b, unsigned z) { init_mark(); m_count.resize(m_nodes.size()); m_count[0] = z; m_count[1] = 1-z; set_mark(0); set_mark(1); m_todo.push_back(b); while (!m_todo.empty()) { BDD r = m_todo.back(); if (is_marked(r)) { m_todo.pop_back(); } else if (!is_marked(lo(r))) { SASSERT (is_const(r) || r != lo(r)); m_todo.push_back(lo(r)); } else if (!is_marked(hi(r))) { SASSERT (is_const(r) || r != hi(r)); m_todo.push_back(hi(r)); } else { m_count[r] = m_count[lo(r)] + m_count[hi(r)]; set_mark(r); m_todo.pop_back(); } } return m_count[b]; } unsigned bdd_manager::bdd_size(bdd const& b) { init_mark(); set_mark(0); set_mark(1); unsigned sz = 0; m_todo.push_back(b.root); while (!m_todo.empty()) { BDD r = m_todo.back(); m_todo.pop_back(); if (!is_marked(r)) { ++sz; set_mark(r); if (!is_marked(lo(r))) { m_todo.push_back(lo(r)); } if (!is_marked(hi(r))) { m_todo.push_back(hi(r)); } } } return sz; } void bdd_manager::alloc_free_nodes(unsigned n) { for (unsigned i = 0; i < n; ++i) { m_free_nodes.push_back(m_nodes.size()); m_nodes.push_back(bdd_node()); m_nodes.back().m_index = m_nodes.size() - 1; } m_free_nodes.reverse(); } void bdd_manager::gc() { m_free_nodes.reset(); IF_VERBOSE(13, verbose_stream() << "(bdd :gc " << m_nodes.size() << ")\n";); svector reachable(m_nodes.size(), false); for (unsigned i = m_bdd_stack.size(); i-- > 0; ) { reachable[m_bdd_stack[i]] = true; m_todo.push_back(m_bdd_stack[i]); } for (unsigned i = m_nodes.size(); i-- > 2; ) { if (m_nodes[i].m_refcount > 0) { reachable[i] = true; m_todo.push_back(i); } } while (!m_todo.empty()) { BDD b = m_todo.back(); m_todo.pop_back(); SASSERT(reachable[b]); if (is_const(b)) continue; if (!reachable[lo(b)]) { reachable[lo(b)] = true; m_todo.push_back(lo(b)); } if (!reachable[hi(b)]) { reachable[hi(b)] = true; m_todo.push_back(hi(b)); } } for (unsigned i = m_nodes.size(); i-- > 2; ) { if (!reachable[i]) { m_nodes[i].set_internal(); SASSERT(m_nodes[i].m_refcount == 0); m_free_nodes.push_back(i); } } // sort free nodes so that adjacent nodes are picked in order of use std::sort(m_free_nodes.begin(), m_free_nodes.end()); m_free_nodes.reverse(); ptr_vector to_delete, to_keep; for (auto* e : m_op_cache) { if (e->m_result != null_bdd) { to_delete.push_back(e); } else { to_keep.push_back(e); } } m_op_cache.reset(); for (op_entry* e : to_delete) { m_alloc.deallocate(sizeof(*e), e); } for (op_entry* e : to_keep) { m_op_cache.insert(e); } m_node_table.reset(); // re-populate node cache for (unsigned i = m_nodes.size(); i-- > 2; ) { if (reachable[i]) { SASSERT(m_nodes[i].m_index == i); m_node_table.insert(m_nodes[i]); } } SASSERT(well_formed()); } void bdd_manager::init_mark() { m_mark.resize(m_nodes.size()); ++m_mark_level; if (m_mark_level == 0) { m_mark.fill(0); ++m_mark_level; } } std::ostream& bdd_manager::display(std::ostream& out, bdd const& b) { init_mark(); m_todo.push_back(b.root); m_reorder_rc.reserve(m_nodes.size()); while (!m_todo.empty()) { BDD r = m_todo.back(); if (is_marked(r)) { m_todo.pop_back(); } else if (lo(r) == 0 && hi(r) == 0) { set_mark(r); m_todo.pop_back(); } else if (!is_marked(lo(r))) { m_todo.push_back(lo(r)); } else if (!is_marked(hi(r))) { m_todo.push_back(hi(r)); } else { out << r << " : " << var(r) << " @ " << level(r) << " " << lo(r) << " " << hi(r) << " " << m_reorder_rc[r] << "\n"; set_mark(r); m_todo.pop_back(); } } return out; } bool bdd_manager::well_formed() { bool ok = true; for (unsigned n : m_free_nodes) { ok &= (lo(n) == 0 && hi(n) == 0 && m_nodes[n].m_refcount == 0); if (!ok) { IF_VERBOSE(0, verbose_stream() << "free node is not internal " << n << " " << lo(n) << " " << hi(n) << " " << m_nodes[n].m_refcount << "\n"; display(verbose_stream());); UNREACHABLE(); return false; } } for (bdd_node const& n : m_nodes) { if (n.is_internal()) continue; unsigned lvl = n.m_level; BDD lo = n.m_lo; BDD hi = n.m_hi; ok &= is_const(lo) || level(lo) < lvl; ok &= is_const(hi) || level(hi) < lvl; ok &= is_const(lo) || !m_nodes[lo].is_internal(); ok &= is_const(hi) || !m_nodes[hi].is_internal(); if (!ok) { IF_VERBOSE(0, display(verbose_stream() << n.m_index << " lo " << lo << " hi " << hi << "\n");); UNREACHABLE(); return false; } } return ok; } std::ostream& bdd_manager::display(std::ostream& out) { m_reorder_rc.reserve(m_nodes.size()); for (unsigned i = 0; i < m_nodes.size(); ++i) { bdd_node const& n = m_nodes[i]; if (n.is_internal()) continue; out << i << " : v" << m_level2var[n.m_level] << " " << n.m_lo << " " << n.m_hi << " rc " << m_reorder_rc[i] << "\n"; } for (unsigned i = 0; i < m_level2nodes.size(); ++i) { out << "level: " << i << " : " << m_level2nodes[i] << "\n"; } return out; } bdd& bdd::operator=(bdd const& other) { unsigned r1 = root; root = other.root; m->inc_ref(root); m->dec_ref(r1); return *this; } std::ostream& operator<<(std::ostream& out, bdd const& b) { return b.display(out); } } z3-z3-4.8.7/src/sat/sat_bdd.h000066400000000000000000000214501356505360400155600ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: sat_bdd Abstract: Simple BDD package modeled after BuDDy, which is modeled after CUDD. Author: Nikolaj Bjorner (nbjorner) 2017-10-13 Revision History: --*/ #ifndef SAT_BDD_H_ #define SAT_BDD_H_ #include "util/vector.h" #include "util/map.h" #include "util/small_object_allocator.h" namespace sat { class bdd; class bdd_manager { friend bdd; typedef unsigned BDD; const BDD null_bdd = UINT_MAX; enum bdd_op { bdd_and_op = 2, bdd_or_op = 3, bdd_xor_op = 4, bdd_not_op = 5, bdd_and_proj_op = 6, bdd_or_proj_op = 7, bdd_no_op = 8, }; struct bdd_node { bdd_node(unsigned level, BDD lo, BDD hi): m_refcount(0), m_level(level), m_lo(lo), m_hi(hi), m_index(0) {} bdd_node(): m_refcount(0), m_level(0), m_lo(0), m_hi(0), m_index(0) {} unsigned m_refcount : 10; unsigned m_level : 22; BDD m_lo; BDD m_hi; unsigned m_index; unsigned hash() const { return mk_mix(m_level, m_lo, m_hi); } bool is_internal() const { return m_lo == 0 && m_hi == 0; } void set_internal() { m_lo = 0; m_hi = 0; } }; enum cost_metric { cnf_cost, dnf_cost, bdd_cost }; struct hash_node { unsigned operator()(bdd_node const& n) const { return n.hash(); } }; struct eq_node { bool operator()(bdd_node const& a, bdd_node const& b) const { return a.m_lo == b.m_lo && a.m_hi == b.m_hi && a.m_level == b.m_level; } }; typedef hashtable node_table; struct op_entry { op_entry(BDD l, BDD r, BDD op): m_bdd1(l), m_bdd2(r), m_op(op), m_result(0) {} BDD m_bdd1; BDD m_bdd2; BDD m_op; BDD m_result; unsigned hash() const { return mk_mix(m_bdd1, m_bdd2, m_op); } }; struct hash_entry { unsigned operator()(op_entry* e) const { return e->hash(); } }; struct eq_entry { bool operator()(op_entry * a, op_entry * b) const { return a->m_bdd1 == b->m_bdd2 && a->m_bdd2 == b->m_bdd2 && a->m_op == b->m_op; } }; typedef ptr_hashtable op_table; svector m_nodes; op_table m_op_cache; node_table m_node_table; unsigned_vector m_apply_const; svector m_bdd_stack; op_entry* m_spare_entry; svector m_var2bdd; unsigned_vector m_var2level, m_level2var; unsigned_vector m_free_nodes; small_object_allocator m_alloc; mutable svector m_mark; mutable unsigned m_mark_level; mutable svector m_count; mutable svector m_todo; bool m_disable_gc; bool m_is_new_node; unsigned m_max_num_bdd_nodes; unsigned_vector m_S, m_T, m_to_free; // used for reordering vector m_level2nodes; unsigned_vector m_reorder_rc; cost_metric m_cost_metric; BDD m_cost_bdd; BDD make_node(unsigned level, BDD l, BDD r); bool is_new_node() const { return m_is_new_node; } BDD apply_const(BDD a, BDD b, bdd_op op); BDD apply(BDD arg1, BDD arg2, bdd_op op); BDD mk_quant(unsigned n, unsigned const* vars, BDD b, bdd_op op); BDD apply_rec(BDD arg1, BDD arg2, bdd_op op); BDD mk_not_rec(BDD b); BDD mk_ite_rec(BDD a, BDD b, BDD c); BDD mk_quant_rec(unsigned lvl, BDD b, bdd_op op); void push(BDD b); void pop(unsigned num_scopes); BDD read(unsigned index); op_entry* pop_entry(BDD l, BDD r, BDD op); void push_entry(op_entry* e); bool check_result(op_entry*& e1, op_entry const* e2, BDD a, BDD b, BDD c); double count(BDD b, unsigned z); void alloc_free_nodes(unsigned n); void init_mark(); void set_mark(unsigned i) { m_mark[i] = m_mark_level; } bool is_marked(unsigned i) { return m_mark[i] == m_mark_level; } void init_reorder(); void reorder_incref(unsigned n); void reorder_decref(unsigned n); void sift_up(unsigned level); void sift_var(unsigned v); double current_cost(); bool is_bad_cost(double new_cost, double best_cost) const; static const BDD false_bdd = 0; static const BDD true_bdd = 1; static const unsigned max_rc = (1 << 10) - 1; inline bool is_true(BDD b) const { return b == true_bdd; } inline bool is_false(BDD b) const { return b == false_bdd; } inline bool is_const(BDD b) const { return b <= 1; } inline unsigned level(BDD b) const { return m_nodes[b].m_level; } inline unsigned var(BDD b) const { return m_level2var[level(b)]; } inline BDD lo(BDD b) const { return m_nodes[b].m_lo; } inline BDD hi(BDD b) const { return m_nodes[b].m_hi; } inline void inc_ref(BDD b) { if (m_nodes[b].m_refcount != max_rc) m_nodes[b].m_refcount++; VERIFY(!m_free_nodes.contains(b)); } inline void dec_ref(BDD b) { if (m_nodes[b].m_refcount != max_rc) m_nodes[b].m_refcount--; VERIFY(!m_free_nodes.contains(b)); } inline BDD level2bdd(unsigned l) const { return m_var2bdd[m_level2var[l]]; } double dnf_size(BDD b) { return count(b, 0); } double cnf_size(BDD b) { return count(b, 1); } unsigned bdd_size(bdd const& b); bdd mk_not(bdd b); bdd mk_and(bdd const& a, bdd const& b); bdd mk_or(bdd const& a, bdd const& b); bdd mk_xor(bdd const& a, bdd const& b); void reserve_var(unsigned v); bool well_formed(); public: struct mem_out {}; bdd_manager(unsigned nodes); ~bdd_manager(); void set_max_num_nodes(unsigned n) { m_max_num_bdd_nodes = n; } bdd mk_var(unsigned i); bdd mk_nvar(unsigned i); bdd mk_true(); bdd mk_false(); bdd mk_exists(unsigned n, unsigned const* vars, bdd const & b); bdd mk_forall(unsigned n, unsigned const* vars, bdd const & b); bdd mk_exists(unsigned v, bdd const& b); bdd mk_forall(unsigned v, bdd const& b); bdd mk_ite(bdd const& c, bdd const& t, bdd const& e); std::ostream& display(std::ostream& out); std::ostream& display(std::ostream& out, bdd const& b); void gc(); void try_reorder(); void try_cnf_reorder(bdd const& b); }; class bdd { friend class bdd_manager; unsigned root; bdd_manager* m; bdd(unsigned root, bdd_manager* m): root(root), m(m) { m->inc_ref(root); } public: bdd(bdd & other): root(other.root), m(other.m) { m->inc_ref(root); } bdd(bdd && other): root(0), m(other.m) { std::swap(root, other.root); } bdd& operator=(bdd const& other); ~bdd() { m->dec_ref(root); } bdd lo() const { return bdd(m->lo(root), m); } bdd hi() const { return bdd(m->hi(root), m); } unsigned var() const { return m->var(root); } bool is_true() const { return root == bdd_manager::true_bdd; } bool is_false() const { return root == bdd_manager::false_bdd; } bdd operator!() { return m->mk_not(*this); } bdd operator&&(bdd const& other) { return m->mk_and(*this, other); } bdd operator||(bdd const& other) { return m->mk_or(*this, other); } bdd operator^(bdd const& other) { return m->mk_xor(*this, other); } bdd operator|=(bdd const& other) { return *this = *this || other; } bdd operator&=(bdd const& other) { return *this = *this && other; } std::ostream& display(std::ostream& out) const { return m->display(out, *this); } bool operator==(bdd const& other) const { return root == other.root; } bool operator!=(bdd const& other) const { return root != other.root; } double cnf_size() const { return m->cnf_size(root); } double dnf_size() const { return m->dnf_size(root); } unsigned bdd_size() const { return m->bdd_size(*this); } }; std::ostream& operator<<(std::ostream& out, bdd const& b); } #endif z3-z3-4.8.7/src/sat/sat_big.cpp000066400000000000000000000216171356505360400161300ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_big.cpp Abstract: binary implication graph structure. Author: Nikolaj Bjorner (nbjorner) 2017-12-13. Revision History: --*/ #include "sat/sat_big.h" #include "sat/sat_solver.h" namespace sat { big::big(random_gen& rand): m_rand(rand), m_include_cardinality(false) { } void big::init(solver& s, bool learned) { init_adding_edges(s.num_vars(), learned); unsigned num_lits = m_num_vars * 2; literal_vector lits, r; SASSERT(num_lits == m_dag.size() && num_lits == m_roots.size()); size_t_map seen_idx; for (unsigned l_idx = 0; l_idx < num_lits; l_idx++) { literal u = to_literal(l_idx); if (s.was_eliminated(u.var())) continue; auto& edges = m_dag[l_idx]; for (watched const& w : s.get_wlist(l_idx)) { if (learned ? w.is_binary_clause() : w.is_binary_non_learned_clause()) { literal v = w.get_literal(); m_roots[v.index()] = false; edges.push_back(v); } if (m_include_cardinality && w.is_ext_constraint() && s.m_ext && learned && // cannot (yet) observe if ext constraints are learned !seen_idx.contains(w.get_ext_constraint_idx()) && s.m_ext->is_extended_binary(w.get_ext_constraint_idx(), r)) { seen_idx.insert(w.get_ext_constraint_idx(), true); for (unsigned i = 0; i < std::min(4u, r.size()); ++i) { shuffle(r.size(), r.c_ptr(), m_rand); literal u = r[0]; for (unsigned j = 1; j < r.size(); ++j) { literal v = ~r[j]; // add u -> v m_roots[v.index()] = false; m_dag[u.index()].push_back(v); // add ~v -> ~u v.neg(); u.neg(); m_roots[u.index()] = false; m_dag[v.index()].push_back(u); } } } } } done_adding_edges(); } void big::reinit() { done_adding_edges(); } void big::init_adding_edges(unsigned num_vars, bool learned) { m_learned = learned; m_num_vars = num_vars; unsigned num_lits = m_num_vars * 2; m_dag.reset(); m_roots.reset(); m_dag.resize(num_lits, 0); m_roots.resize(num_lits, true); } void big::add_edge(literal u, literal v) { m_dag[u.index()].push_back(v); } void big::done_adding_edges() { for (auto& edges : m_dag) { shuffle(edges.size(), edges.c_ptr(), m_rand); } init_dfs_num(); } struct big::pframe { literal m_parent; literal m_child; pframe(literal p, literal c): m_parent(p), m_child(c) {} literal child() const { return m_child; } literal parent() const { return m_parent; } }; void big::init_dfs_num() { unsigned num_lits = m_num_vars * 2; m_left.reset(); m_right.reset(); m_root.reset(); m_parent.reset(); m_left.resize(num_lits, 0); m_right.resize(num_lits, -1); m_root.resize(num_lits, null_literal); m_parent.resize(num_lits, null_literal); for (unsigned i = 0; i < num_lits; ++i) { m_root[i] = to_literal(i); m_parent[i] = to_literal(i); } svector todo; // retrieve literals that have no predecessors for (unsigned l_idx = 0; l_idx < num_lits; l_idx++) { literal u(to_literal(l_idx)); if (m_roots[u.index()]) { todo.push_back(pframe(null_literal, u)); } } shuffle(todo.size(), todo.c_ptr(), m_rand); int dfs_num = 0; while (!todo.empty()) { literal u = todo.back().child(); if (m_left[u.index()] > 0) { // already visited if (m_right[u.index()] < 0) { m_right[u.index()] = ++dfs_num; } todo.pop_back(); } else { SASSERT(m_left[u.index()] == 0); m_left[u.index()] = ++dfs_num; literal p = todo.back().parent(); if (p != null_literal) { m_root[u.index()] = m_root[p.index()]; m_parent[u.index()] = p; } for (literal v : m_dag[u.index()]) { if (m_left[v.index()] == 0) { todo.push_back(pframe(u, v)); } } } } for (unsigned i = 0; i < num_lits; ++i) { if (m_right[i] < 0) { VERIFY(m_left[i] == 0); m_left[i] = ++dfs_num; m_right[i] = ++dfs_num; } } DEBUG_CODE(for (unsigned i = 0; i < num_lits; ++i) { VERIFY(m_left[i] < m_right[i]);}); } bool big::in_del(literal u, literal v) const { if (u.index() > v.index()) std::swap(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[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++); watch_list::iterator it = wlist.begin(); watch_list::iterator itprev = it; watch_list::iterator end = wlist.end(); for (; it != end; ++it) { watched& w = *it; if (learned() ? w.is_binary_learned_clause() : w.is_binary_clause()) { literal v = w.get_literal(); if (u != get_parent(v) && safe_reach(u, v)) { ++elim; add_del(~u, v); if (s.get_config().m_drat) s.m_drat.del(~u, v); s.m_mc.stackv().reset(); // TBD: brittle code s.add_ate(~u, v); if (find_binary_watch(wlist, ~v)) { IF_VERBOSE(10, verbose_stream() << "binary: " << ~u << "\n"); s.assign_unit(~u); } // could turn non-learned non-binary tautology into learned binary. s.get_wlist(~v).erase(watched(~u, w.is_learned())); continue; } } *itprev = *it; itprev++; } wlist.set_end(itprev); } s.propagate(false); return elim; } bool big::safe_reach(literal u, literal v) { if (!reaches(u, v)) return false; while (u != v) { literal w = next(u, v); if (in_del(~u, w)) { return false; } u = w; } return true; } literal big::next(literal u, literal v) const { SASSERT(reaches(u, v)); literal result = null_literal; int left = m_right[u.index()]; // identify the path from the reachability graph for (literal w : m_dag[u.index()]) { if (reaches(u, w) && (w == v || reaches(w, v)) && m_left[w.index()] < left) { left = m_left[w.index()]; result = w; } } SASSERT(result != null_literal); return result; } std::ostream& big::display_path(std::ostream& out, literal u, literal v) const { while (u != v) { out << u << " -> "; u = next(u, v); } return out << v; } literal big::get_root(literal l) { literal r = l; do { l = r; r = m_root[l.index()]; } while (r != l); return r; } void big::display(std::ostream& out) const { unsigned idx = 0; for (auto& next : m_dag) { if (!next.empty()) { out << to_literal(idx) << " : " << m_left[idx] << ":" << m_right[idx] << " -> " << next << "\n"; #if 0 for (literal n : next) { out << n << "[" << m_left[n.index()] << ":" << m_right[n.index()] << "] "; } out << "\n"; #endif } ++idx; } } }; z3-z3-4.8.7/src/sat/sat_big.h000066400000000000000000000047611356505360400155760ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: sat_big.h Abstract: binary implication graph structure. Author: Nikolaj Bjorner (nbjorner) 2017-12-13. Revision History: --*/ #ifndef SAT_BIG_H_ #define SAT_BIG_H_ #include "sat/sat_types.h" #include "util/statistics.h" #include "util/params.h" namespace sat { class solver; class big { random_gen& m_rand; unsigned m_num_vars; vector m_dag; svector m_roots; svector m_left, m_right; literal_vector m_root, m_parent; bool m_learned; bool m_include_cardinality; vector > m_del_bin; void init_dfs_num(); struct pframe; bool safe_reach(literal u, literal v); literal next(literal u, literal v) const; std::ostream& display_path(std::ostream& out, literal u, literal v) const; void add_del(literal u, literal v); bool in_del(literal u, literal v) const; public: // static svector> s_del_bin; big(random_gen& rand); void set_include_cardinality(bool f) { m_include_cardinality = f; } /** \brief initialize a BIG from a solver. */ void init(solver& s, bool learned); void reinit(); /** \brief initialize a BIG externally by adding implications. */ void init_adding_edges(unsigned num_vars, bool learned); void add_edge(literal u, literal v); void done_adding_edges(); void ensure_big(solver& s, bool learned) { if (m_left.empty()) init(s, learned); } unsigned reduce_tr(solver& s); // does it include learned binaries? bool learned() const { return m_learned; } int get_left(literal l) const { return m_left[l.index()]; } int get_right(literal l) const { return m_right[l.index()]; } literal get_parent(literal l) const { return m_parent[l.index()]; } literal get_root(literal l); bool is_root(literal l) { return get_root(l) == l; } bool reaches(literal u, literal v) const { return m_left[u.index()] < m_left[v.index()] && m_right[v.index()] < m_right[u.index()]; } bool connected(literal u, literal v) const { return reaches(u, v) || reaches(~v, ~u); } void display(std::ostream& out) const; }; }; #endif z3-z3-4.8.7/src/sat/sat_clause.cpp000066400000000000000000000161761356505360400166470ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_clause.cpp Abstract: Clauses Author: Leonardo de Moura (leonardo) 2011-05-21. Revision History: --*/ #include #include "sat/sat_clause.h" #include "util/z3_exception.h" #include "util/trace.h" namespace sat { clause::clause(unsigned id, unsigned sz, literal const * lits, bool learned): m_id(id), m_size(sz), m_capacity(sz), m_removed(false), m_learned(learned), m_used(false), m_frozen(false), m_reinit_stack(false), m_inact_rounds(0), m_glue(255), m_psm(255) { memcpy(m_lits, lits, sizeof(literal) * sz); mark_strengthened(); SASSERT(check_approx()); } var_approx_set clause::approx(unsigned num, literal const * lits) { var_approx_set r; for (unsigned i = 0; i < num; i++) r.insert(lits[i].var()); return r; } void clause::update_approx() { m_approx = approx(m_size, m_lits); } bool clause::check_approx() const { var_approx_set curr = m_approx; (void)curr; const_cast(this)->update_approx(); SASSERT(may_eq(curr, m_approx)); return true; } bool clause::contains(literal l) const { for (literal l2 : *this) if (l2 == l) return true; return false; } bool clause::contains(bool_var v) const { for (literal l : *this) if (l.var() == v) return true; return false; } void clause::elim(literal l) { unsigned i; for (i = 0; i < m_size; i++) if (m_lits[i] == l) break; SASSERT(i < m_size); i++; for (; i < m_size; i++) m_lits[i-1] = m_lits[i]; m_lits[m_size-1] = l; m_size--; mark_strengthened(); } void clause::shrink(unsigned num_lits) { SASSERT(num_lits <= m_size); if (num_lits < m_size) { m_size = num_lits; 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) { if (l.sign()) { if (m[l.var()] == l_false) return true; } else { if (m[l.var()] == l_true) return true; } } return false; } clause_offset clause::get_new_offset() const { unsigned o1 = m_lits[0].index(); #if defined(__LP64__) || defined(_WIN64) if (sizeof(clause_offset) == 8) { unsigned o2 = m_lits[1].index(); return (clause_offset)o1 + (((clause_offset)o2) << 32); } #endif return (clause_offset)o1; } void clause::set_new_offset(clause_offset offset) { m_lits[0] = to_literal(static_cast(offset)); #if defined(__LP64__) || defined(_WIN64) if (sizeof(offset) == 8) { m_lits[1] = to_literal(static_cast(offset >> 32)); } #endif } void tmp_clause::set(unsigned num_lits, literal const * lits, bool learned) { if (m_clause && m_clause->m_capacity < num_lits) { dealloc_svect(m_clause); m_clause = nullptr; } if (!m_clause) { void * mem = alloc_svect(char, clause::get_obj_size(num_lits)); m_clause = new (mem) clause(UINT_MAX, num_lits, lits, learned); } else { SASSERT(m_clause->m_id == UINT_MAX); m_clause->m_size = num_lits; m_clause->m_learned = learned; memcpy(m_clause->m_lits, lits, sizeof(literal) * num_lits); } SASSERT(m_clause->m_size <= m_clause->m_capacity); for (unsigned i = 0; i < num_lits; i++) { SASSERT((*m_clause)[i] == lits[i]); } } clause_allocator::clause_allocator(): m_allocator("clause-allocator") { } void clause_allocator::finalize() { m_allocator.reset(); } clause * clause_allocator::get_clause(clause_offset cls_off) const { SASSERT(cls_off == reinterpret_cast(reinterpret_cast(cls_off))); return reinterpret_cast(cls_off); } clause_offset clause_allocator::get_offset(clause const * cls) const { SASSERT(cls == reinterpret_cast(reinterpret_cast(cls))); return reinterpret_cast(cls); } clause * clause_allocator::mk_clause(unsigned num_lits, literal const * lits, bool learned) { size_t size = clause::get_obj_size(num_lits); void * mem = m_allocator.allocate(size); clause * cls = new (mem) clause(m_id_gen.mk(), num_lits, lits, learned); TRACE("sat_clause", tout << "alloc: " << cls->id() << " " << *cls << " " << (learned?"l":"a") << "\n";); SASSERT(!learned || cls->is_learned()); return cls; } clause * clause_allocator::copy_clause(clause const& other) { size_t size = clause::get_obj_size(other.size()); void * mem = m_allocator.allocate(size); clause * cls = new (mem) clause(m_id_gen.mk(), other.size(), other.m_lits, other.is_learned()); cls->m_reinit_stack = other.on_reinit_stack(); cls->m_glue = other.glue(); cls->m_psm = other.psm(); cls->m_frozen = other.frozen(); cls->m_approx = other.approx(); return cls; } void clause_allocator::del_clause(clause * cls) { TRACE("sat_clause", tout << "delete: " << cls->id() << " " << *cls << "\n";); m_id_gen.recycle(cls->id()); size_t size = clause::get_obj_size(cls->m_capacity); cls->~clause(); m_allocator.deallocate(size, cls); } std::ostream & operator<<(std::ostream & out, clause const & c) { out << "("; for (unsigned i = 0; i < c.size(); i++) { if (i > 0) out << " "; out << c[i]; } out << ")"; if (c.was_removed()) out << "x"; if (c.strengthened()) out << "+"; if (c.is_learned()) out << "*"; return out; } std::ostream & operator<<(std::ostream & out, clause_vector const & cs) { for (clause *cp : cs) { out << *cp << "\n"; } return out; } bool clause_wrapper::contains(literal l) const { unsigned sz = size(); for (unsigned i = 0; i < sz; i++) if (operator[](i) == l) return true; return false; } bool clause_wrapper::contains(bool_var v) const { unsigned sz = size(); for (unsigned i = 0; i < sz; i++) if (operator[](i).var() == v) return true; return false; } std::ostream & operator<<(std::ostream & out, clause_wrapper const & c) { if (c.is_binary()) { out << "(" << c[0] << " " << c[1] << ")"; } else { out << c.get_clause()->id() << ": " << *c.get_clause(); } return out; } }; z3-z3-4.8.7/src/sat/sat_clause.h000066400000000000000000000167751356505360400163210ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_clause.h Abstract: Clauses Author: Leonardo de Moura (leonardo) 2011-05-21. Revision History: --*/ #ifndef SAT_CLAUSE_H_ #define SAT_CLAUSE_H_ #include "util/small_object_allocator.h" #include "util/id_gen.h" #include "util/map.h" #include "sat/sat_types.h" #include "sat/sat_allocator.h" #ifdef _MSC_VER #pragma warning(disable : 4200) #pragma warning(disable : 4355) #endif namespace sat { class clause_allocator; class clause; std::ostream & operator<<(std::ostream & out, clause const & c); class clause { friend class clause_allocator; friend class tmp_clause; unsigned m_id; unsigned m_size; unsigned m_capacity; var_approx_set m_approx; unsigned m_strengthened:1; unsigned m_removed:1; unsigned m_learned:1; unsigned m_used:1; unsigned m_frozen:1; unsigned m_reinit_stack:1; unsigned m_inact_rounds:8; unsigned m_glue:8; unsigned m_psm:8; // transient field used during gc literal m_lits[0]; static size_t get_obj_size(unsigned num_lits) { return sizeof(clause) + num_lits * sizeof(literal); } size_t get_size() const { return get_obj_size(m_capacity); } clause(unsigned id, unsigned sz, literal const * lits, bool learned); public: unsigned id() const { return m_id; } unsigned size() const { return m_size; } unsigned capacity() const { return m_capacity; } literal & operator[](unsigned idx) { SASSERT(idx < m_size); return m_lits[idx]; } literal const & operator[](unsigned idx) const { SASSERT(idx < m_size); return m_lits[idx]; } 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; } void elim(literal l); bool was_removed() const { return m_removed; } void set_removed(bool f) { m_removed = f; } var_approx_set approx() const { return m_approx; } void update_approx(); bool check_approx() const; // for debugging literal * begin() { return m_lits; } literal * end() { return m_lits + m_size; } literal const * begin() const { return m_lits; } literal const * end() const { return m_lits + m_size; } bool contains(literal l) const; bool contains(bool_var v) const; bool satisfied_by(model const & m) const; void mark_used() { m_used = true; } void unmark_used() { m_used = false; } bool was_used() const { return m_used; } void inc_inact_rounds() { m_inact_rounds++; } void reset_inact_rounds() { m_inact_rounds = 0; } unsigned inact_rounds() const { return m_inact_rounds; } bool frozen() const { return m_frozen; } void freeze() { SASSERT(is_learned()); SASSERT(!frozen()); m_frozen = true; } void unfreeze() { SASSERT(is_learned()); SASSERT(frozen()); m_frozen = false; } static var_approx_set approx(unsigned num, literal const * lits); void set_glue(unsigned glue) { m_glue = glue > 255 ? 255 : glue; } unsigned glue() const { return m_glue; } void set_psm(unsigned psm) { m_psm = psm > 255 ? 255 : psm; } unsigned psm() const { return m_psm; } clause_offset get_new_offset() const; void set_new_offset(clause_offset off); bool on_reinit_stack() const { return m_reinit_stack; } void set_reinit_stack(bool f) { m_reinit_stack = f; } }; std::ostream & operator<<(std::ostream & out, clause_vector const & cs); class bin_clause { unsigned m_val1; unsigned m_val2; public: bin_clause(literal l1, literal l2, bool learned) :m_val1(l1.to_uint()), m_val2((l2.to_uint() << 1) + static_cast(learned)) {} literal get_literal1() const { return to_literal(m_val1); } literal get_literal2() const { return to_literal(m_val2 >> 1); } bool is_learned() const { return (m_val2 & 1) == 1; } bool operator==(const bin_clause & other) const { return (m_val1 == other.m_val1 && m_val2 == other.m_val2) || (m_val1 == other.m_val2 && m_val2 == other.m_val1); } }; class tmp_clause { clause * m_clause; public: tmp_clause():m_clause(nullptr) {} ~tmp_clause() { if (m_clause) dealloc_svect(m_clause); } clause * get() const { return m_clause; } void set(unsigned num_lits, literal const * lits, bool learned); void set(literal l1, literal l2, bool learned) { literal ls[2] = { l1, l2 }; set(2, ls, learned); } void set(bin_clause const & c) { set(c.get_literal1(), c.get_literal2(), c.is_learned()); } }; /** \brief Simple clause allocator that allows uint (32bit integers) to be used to reference clauses (even in 64bit machines). */ class clause_allocator { sat_allocator m_allocator; id_gen m_id_gen; public: clause_allocator(); void finalize(); size_t get_allocation_size() const { return m_allocator.get_allocation_size(); } clause * get_clause(clause_offset cls_off) const; clause_offset get_offset(clause const * ptr) const; clause * mk_clause(unsigned num_lits, literal const * lits, bool learned); clause * copy_clause(clause const& other); void del_clause(clause * cls); }; /** \brief Wrapper for clauses & binary clauses. I do not create clause objects for binary clauses. clause_ref wraps a clause object or a pair of literals (i.e., a binary clause). */ class clause_wrapper { union { clause * m_cls; unsigned m_l1_idx; }; unsigned m_l2_idx; public: explicit clause_wrapper(literal l1, literal l2):m_l1_idx(l1.to_uint()), m_l2_idx(l2.to_uint()) {} explicit clause_wrapper(clause & c):m_cls(&c), m_l2_idx(null_literal.to_uint()) {} clause_wrapper& operator=(clause_wrapper const& other) { if (other.is_binary()) { m_l1_idx = other.m_l1_idx; } else { m_cls = other.m_cls; } m_l2_idx = other.m_l2_idx; return *this; } bool is_binary() const { return m_l2_idx != null_literal.to_uint(); } unsigned size() const { return is_binary() ? 2 : m_cls->size(); } literal operator[](unsigned idx) const { SASSERT(idx < size()); if (is_binary()) return idx == 0 ? to_literal(m_l1_idx) : to_literal(m_l2_idx); else return m_cls->operator[](idx); } bool contains(literal l) const; bool contains(bool_var v) const; clause * get_clause() const { SASSERT(!is_binary()); return m_cls; } bool was_removed() const { return !is_binary() && get_clause()->was_removed(); } }; typedef svector clause_wrapper_vector; std::ostream & operator<<(std::ostream & out, clause_wrapper const & c); }; #endif z3-z3-4.8.7/src/sat/sat_clause_set.cpp000066400000000000000000000045261356505360400175160ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_clause_set.cpp Abstract: Set of clauses Author: Leonardo de Moura (leonardo) 2011-05-25. Revision History: --*/ #include "sat/sat_clause_set.h" namespace sat { void clause_set::insert(clause & c) { unsigned id = c.id(); m_id2pos.reserve(id+1, UINT_MAX); if (m_id2pos[id] != UINT_MAX) return; // already in the set unsigned pos = m_set.size(); m_id2pos[id] = pos; m_set.push_back(&c); CASSERT("clause_set", check_invariant()); } void clause_set::erase(clause & c) { unsigned id = c.id(); if (id >= m_id2pos.size()) return; if (empty()) return; unsigned pos = m_id2pos[id]; if (pos == UINT_MAX) return; m_id2pos[id] = UINT_MAX; unsigned last_pos = m_set.size() - 1; if (pos != last_pos) { clause * last_c = m_set[last_pos]; m_set[pos] = last_c; m_id2pos[last_c->id()] = pos; } m_set.pop_back(); CASSERT("clause_set", check_invariant()); } clause & clause_set::erase() { SASSERT(!empty()); clause & c = *m_set.back(); SASSERT(c.id() < m_id2pos.size()); SASSERT(m_id2pos[c.id()] == m_set.size()-1); if (c.id() < m_id2pos.size()) { m_id2pos[c.id()] = UINT_MAX; } m_set.pop_back(); return c; } bool clause_set::check_invariant() const { DEBUG_CODE( { clause_vector::const_iterator it = m_set.begin(); clause_vector::const_iterator end = m_set.end(); for (unsigned pos = 0; it != end; ++it, ++pos) { clause & c = *(*it); SASSERT(c.id() < m_id2pos.size()); SASSERT(m_id2pos[c.id()] == pos); } } { unsigned_vector::const_iterator it = m_id2pos.begin(); unsigned_vector::const_iterator end = m_id2pos.end(); for (unsigned id = 0; it != end; ++it, ++id) { unsigned pos = *it; if (pos == UINT_MAX) continue; SASSERT(pos < m_set.size()); SASSERT(m_set[pos]->id() == id); } }); return true; } }; z3-z3-4.8.7/src/sat/sat_clause_set.h000066400000000000000000000022351356505360400171560ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_clause_set.h Abstract: Set of clauses Author: Leonardo de Moura (leonardo) 2011-05-25. Revision History: --*/ #ifndef SAT_CLAUSE_SET_H_ #define SAT_CLAUSE_SET_H_ #include "sat/sat_clause.h" namespace sat { class clause_set { unsigned_vector m_id2pos; clause_vector m_set; public: typedef clause_vector::const_iterator iterator; bool contains(clause const & cls) const { if (cls.id() >= m_id2pos.size()) return false; return m_id2pos[cls.id()] != UINT_MAX; } bool empty() const { return m_set.empty(); } unsigned size() const { return m_set.size(); } void insert(clause & c); void erase(clause & c); // erase some clause from the set clause & erase(); void reset() { m_id2pos.reset(); m_set.reset(); } void finalize() { m_id2pos.finalize(); m_set.finalize(); } iterator begin() const { return m_set.begin(); } iterator end() const { return m_set.end(); } bool check_invariant() const; }; }; #endif z3-z3-4.8.7/src/sat/sat_clause_use_list.cpp000066400000000000000000000021471356505360400205470ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_clause_use_list.cpp Abstract: Clause use list Author: Leonardo de Moura (leonardo) 2011-05-31. Revision History: --*/ #include "sat/sat_clause.h" #include "sat/sat_clause_use_list.h" namespace sat { bool clause_use_list::check_invariant() const { unsigned sz = 0; for (clause* c : m_clauses) if (!c->was_removed()) sz++; SASSERT(sz == m_size); unsigned redundant = 0; for (clause* c : m_clauses) if (c->is_learned()) redundant++; SASSERT(redundant == m_num_redundant); return true; } void clause_use_list::iterator::consume() { while (true) { if (m_i == m_size) return; if (!m_clauses[m_i]->was_removed()) { m_clauses[m_j] = m_clauses[m_i]; return; } m_i++; } } clause_use_list::iterator::~iterator() { while (m_i < m_size) next(); m_clauses.shrink(m_j); } }; z3-z3-4.8.7/src/sat/sat_clause_use_list.h000066400000000000000000000072401356505360400202130ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_clause_use_list.h Abstract: Clause use list Author: Leonardo de Moura (leonardo) 2011-05-31. Revision History: --*/ #ifndef SAT_CLAUSE_USE_LIST_H_ #define SAT_CLAUSE_USE_LIST_H_ #include "sat/sat_types.h" #include "util/trace.h" namespace sat { /** \brief Clause use list with delayed deletion. */ class clause_use_list { clause_vector m_clauses; unsigned m_size; unsigned m_num_redundant; public: clause_use_list() { STRACE("clause_use_list_bug", tout << "[cul_created] " << this << "\n";); m_size = 0; m_num_redundant = 0; } unsigned size() const { return m_size; } unsigned num_redundant() const { return m_num_redundant; } unsigned num_irredundant() const { return m_size - m_num_redundant; } bool empty() const { return size() == 0; } void insert(clause & c) { STRACE("clause_use_list_bug", tout << "[cul_insert] " << this << " " << &c << "\n";); SASSERT(!m_clauses.contains(&c)); SASSERT(!c.was_removed()); m_clauses.push_back(&c); m_size++; if (c.is_learned()) ++m_num_redundant; } void erase_not_removed(clause & c) { STRACE("clause_use_list_bug", tout << "[cul_erase_not_removed] " << this << " " << &c << "\n";); SASSERT(m_clauses.contains(&c)); SASSERT(!c.was_removed()); m_clauses.erase(&c); m_size--; if (c.is_learned()) --m_num_redundant; } void erase(clause & c) { STRACE("clause_use_list_bug", tout << "[cul_erase] " << this << " " << &c << "\n";); SASSERT(m_clauses.contains(&c)); SASSERT(c.was_removed()); m_size--; if (c.is_learned()) --m_num_redundant; } void block(clause const& c) { SASSERT(c.is_learned()); ++m_num_redundant; SASSERT(check_invariant()); } void unblock(clause const& c) { SASSERT(!c.is_learned()); --m_num_redundant; SASSERT(check_invariant()); } void reset() { m_clauses.finalize(); m_size = 0; m_num_redundant = 0; } bool check_invariant() const; // iterate & compress class iterator { clause_vector & m_clauses; unsigned m_size; unsigned m_i; unsigned m_j; void consume(); public: iterator(clause_vector & v):m_clauses(v), m_size(v.size()), m_i(0) { m_j = 0; consume(); } ~iterator(); bool at_end() const { return m_i == m_size; } clause & curr() const { SASSERT(!at_end()); return *(m_clauses[m_i]); } void next() { SASSERT(!at_end()); SASSERT(!m_clauses[m_i]->was_removed()); m_i++; m_j++; consume(); } }; iterator mk_iterator() const { return iterator(const_cast(this)->m_clauses); } std::ostream& display(std::ostream& out) const { iterator it = mk_iterator(); while (!it.at_end()) { out << it.curr() << "\n"; it.next(); } return out; } }; }; #endif z3-z3-4.8.7/src/sat/sat_cleaner.cpp000066400000000000000000000202271356505360400167740ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_cleaner.h Abstract: Eliminate satisfied clauses, and literals assigned to false. Author: Leonardo de Moura (leonardo) 2011-05-24. Revision History: --*/ #include "sat/sat_cleaner.h" #include "sat/sat_solver.h" #include "util/trace.h" #include "util/stopwatch.h" namespace sat { cleaner::cleaner(solver & _s): s(_s), m_last_num_units(0), m_cleanup_counter(0) { reset_statistics(); } /** - Delete watch lists of assigned literals. - Delete satisfied binary watched binary clauses - Delete watched clauses (they will be reinserted after they are cleaned). */ void cleaner::cleanup_watches() { vector::iterator it = s.m_watches.begin(); vector::iterator end = s.m_watches.end(); unsigned l_idx = 0; for (; it != end; ++it, ++l_idx) { if (s.value(to_literal(l_idx)) != l_undef) { it->finalize(); SASSERT(it->empty()); continue; } TRACE("cleanup_bug", tout << "processing wlist of " << to_literal(l_idx) << "\n";); watch_list & wlist = *it; watch_list::iterator it2 = wlist.begin(); watch_list::iterator it_prev = it2; watch_list::iterator end2 = wlist.end(); for (; it2 != end2; ++it2) { switch (it2->get_kind()) { case watched::BINARY: TRACE("cleanup_bug", tout << ~to_literal(l_idx) << " " << it2->get_literal() << "\n"; tout << s.value(~to_literal(l_idx)) << " " << s.value(it2->get_literal()) << "\n"; tout << s.was_eliminated(it2->get_literal()) << " " << s.inconsistent() << "\n";); SASSERT(s.value(it2->get_literal()) == l_true || s.value(it2->get_literal()) == l_undef); if (s.value(it2->get_literal()) == l_undef) { *it_prev = *it2; ++it_prev; } TRACE("cleanup_bug", tout << "keeping: " << ~to_literal(l_idx) << " " << it2->get_literal() << "\n";); break; case watched::TERNARY: case watched::CLAUSE: // skip break; case watched::EXT_CONSTRAINT: *it_prev = *it2; ++it_prev; break; default: UNREACHABLE(); break; } } wlist.set_end(it_prev); } } void cleaner::cleanup_clauses(clause_vector & cs) { clause_vector::iterator it = cs.begin(); clause_vector::iterator it2 = it; clause_vector::iterator end = cs.end(); for (; it != end; ++it) { clause & c = *(*it); TRACE("sat_cleaner_bug", tout << "cleaning: " << c << "\n"; for (unsigned i = 0; i < c.size(); i++) tout << c[i] << ": " << s.value(c[i]) << "\n";); CTRACE("sat_cleaner_frozen", c.frozen(), tout << c << "\n";); unsigned sz = c.size(); unsigned i = 0, j = 0; m_cleanup_counter += sz; for (; i < sz; i++) { switch (s.value(c[i])) { case l_true: goto end_loop; case l_false: m_elim_literals++; break; case l_undef: if (i != j) { std::swap(c[j], c[i]); } j++; break; } } end_loop: CTRACE("sat_cleaner_frozen", c.frozen(), tout << "sat: " << (i < sz) << ", new_size: " << j << "\n"; tout << mk_lits_pp(j, c.begin()) << "\n";); if (i < sz) { m_elim_clauses++; s.del_clause(c); } else { unsigned new_sz = j; 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);); switch (new_sz) { case 0: s.set_conflict(); s.del_clause(c); break; case 1: s.assign_unit(c[0]); s.del_clause(c); break; case 2: SASSERT(s.value(c[0]) == l_undef && s.value(c[1]) == l_undef); 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: s.shrink(c, sz, new_sz); *it2 = *it; it2++; if (!c.frozen()) { s.attach_clause(c); } break; } } } cs.set_end(it2); } struct cleaner::report { cleaner & m_cleaner; stopwatch m_watch; unsigned m_elim_clauses; unsigned m_elim_literals; report(cleaner & c): m_cleaner(c), m_elim_clauses(c.m_elim_clauses), m_elim_literals(c.m_elim_literals) { m_watch.start(); } ~report() { m_watch.stop(); 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";); } }; bool cleaner::is_clean() const { for (clause* cp : s.m_clauses) { for (literal lit : *cp) { if (s.value(lit) != l_undef && s.lvl(lit) == 0) return false; } } for (clause* cp : s.m_learned) { for (literal lit : *cp) { if (s.value(lit) != l_undef && s.lvl(lit) == 0) return false; } } unsigned idx = 0; for (auto& wlist : s.m_watches) { literal lit = to_literal(idx); if (s.value(lit) != l_undef && s.lvl(lit) == 0 && !wlist.empty()) return false; ++idx; } return true; } /** \brief Return true if cleaner executed. */ bool cleaner::operator()(bool force) { CASSERT("cleaner_bug", s.check_invariant()); unsigned trail_sz = s.m_trail.size(); s.propagate(false); // make sure that everything was propagated. TRACE("sat_cleaner_bug", s.display(tout); s.display_watches(tout);); if (s.m_inconsistent) return false; if (m_last_num_units == trail_sz) return false; // there are no new assigned literals since last time... nothing to be done if (!force && m_cleanup_counter > 0) return false; // prevent simplifier from being executed over and over again. report rpt(*this); m_last_num_units = trail_sz; m_cleanup_counter = 0; 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() && !s.inconsistent()); CASSERT("cleaner_bug", s.check_invariant()); return true; } void cleaner::reset_statistics() { m_elim_clauses = 0; m_elim_literals = 0; } void cleaner::collect_statistics(statistics & st) const { st.update("sat elim clauses", m_elim_clauses); st.update("sat elim literals", m_elim_literals); } }; z3-z3-4.8.7/src/sat/sat_cleaner.h000066400000000000000000000016441356505360400164430ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_cleaner.h Abstract: Eliminate satisfied clauses, and literals assigned to false. Author: Leonardo de Moura (leonardo) 2011-05-24. Revision History: --*/ #ifndef SAT_CLEANER_H_ #define SAT_CLEANER_H_ #include "sat/sat_types.h" #include "util/statistics.h" namespace sat { class cleaner { struct report; solver & s; unsigned m_last_num_units; int m_cleanup_counter; // stats unsigned m_elim_clauses; unsigned m_elim_literals; void cleanup_watches(); void cleanup_clauses(clause_vector & cs); public: cleaner(solver & s); bool is_clean() const; bool operator()(bool force = false); void collect_statistics(statistics & st) const; void reset_statistics(); void dec() { m_cleanup_counter--; } }; }; #endif z3-z3-4.8.7/src/sat/sat_config.cpp000066400000000000000000000232251356505360400166310ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_config.cpp Abstract: SAT configuration options Author: Leonardo de Moura (leonardo) 2011-05-21. Revision History: --*/ #include "sat/sat_config.h" #include "sat/sat_types.h" #include "sat/sat_params.hpp" #include "sat/sat_simplifier_params.hpp" namespace sat { config::config(params_ref const & p) { m_incremental = false; // ad-hoc parameter updt_params(p); } void config::updt_params(params_ref const & _p) { sat_params p(_p); m_max_memory = megabytes_to_bytes(p.max_memory()); symbol s = p.restart(); if (s == symbol("luby")) m_restart = RS_LUBY; else if (s == symbol("geometric")) m_restart = RS_GEOMETRIC; else if (s == symbol("ema")) m_restart = RS_EMA; else if (s == symbol("static")) m_restart = RS_STATIC; else throw sat_param_exception("invalid restart strategy"); m_fast_glue_avg = p.restart_emafastglue(); m_slow_glue_avg = p.restart_emaslowglue(); m_restart_margin = p.restart_margin(); m_restart_fast = p.restart_fast(); s = p.phase(); if (s == symbol("always_false")) m_phase = PS_ALWAYS_FALSE; else if (s == symbol("always_true")) m_phase = PS_ALWAYS_TRUE; else if (s == symbol("basic_caching")) m_phase = PS_BASIC_CACHING; else if (s == symbol("caching")) m_phase = PS_SAT_CACHING; else if (s == symbol("random")) m_phase = PS_RANDOM; else throw sat_param_exception("invalid phase selection strategy: always_false, always_true, basic_caching, caching, random"); m_rephase_base = p.rephase_base(); m_reorder_base = p.reorder_base(); m_reorder_itau = p.reorder_itau(); m_activity_scale = p.reorder_activity_scale(); m_search_sat_conflicts = p.search_sat_conflicts(); m_search_unsat_conflicts = p.search_unsat_conflicts(); m_phase_sticky = p.phase_sticky(); m_restart_initial = p.restart_initial(); m_restart_factor = p.restart_factor(); m_restart_max = p.restart_max(); m_propagate_prefetch = p.propagate_prefetch(); m_inprocess_max = p.inprocess_max(); m_random_freq = p.random_freq(); m_random_seed = p.random_seed(); if (m_random_seed == 0) { m_random_seed = _p.get_uint("random_seed", 0); } m_burst_search = p.burst_search(); m_max_conflicts = p.max_conflicts(); m_num_threads = p.threads(); m_ddfw_search = p.ddfw_search(); m_ddfw_threads = p.ddfw_threads(); m_prob_search = p.prob_search(); m_local_search = p.local_search(); m_local_search_threads = p.local_search_threads(); if (p.local_search_mode() == symbol("gsat")) m_local_search_mode = local_search_mode::gsat; else m_local_search_mode = local_search_mode::wsat; m_local_search_dbg_flips = p.local_search_dbg_flips(); m_unit_walk = p.unit_walk(); m_unit_walk_threads = p.unit_walk_threads(); m_binspr = false; // unsound :-( p.binspr(); 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; else if (p.lookahead_reward() == symbol("heuleu")) m_lookahead_reward = heule_unit_reward; else if (p.lookahead_reward() == symbol("ternary")) m_lookahead_reward = ternary_reward; else if (p.lookahead_reward() == symbol("unit")) m_lookahead_reward = unit_literal_reward; else if (p.lookahead_reward() == symbol("march_cu")) m_lookahead_reward = march_cu_reward; else throw sat_param_exception("invalid reward type supplied: accepted heuristics are 'ternary', 'heuleu', 'unit' or 'heule_schur'"); if (p.lookahead_cube_cutoff() == symbol("depth")) m_lookahead_cube_cutoff = depth_cutoff; else if (p.lookahead_cube_cutoff() == symbol("freevars")) m_lookahead_cube_cutoff = freevars_cutoff; else if (p.lookahead_cube_cutoff() == symbol("psat")) m_lookahead_cube_cutoff = psat_cutoff; else if (p.lookahead_cube_cutoff() == symbol("adaptive_freevars")) m_lookahead_cube_cutoff = adaptive_freevars_cutoff; else if (p.lookahead_cube_cutoff() == symbol("adaptive_psat")) m_lookahead_cube_cutoff = adaptive_psat_cutoff; else throw sat_param_exception("invalid cutoff type supplied: accepted cutoffs are 'depth', 'freevars', 'psat', 'adaptive_freevars' and 'adaptive_psat'"); m_lookahead_cube_fraction = p.lookahead_cube_fraction(); m_lookahead_cube_depth = p.lookahead_cube_depth(); m_lookahead_cube_freevars = p.lookahead_cube_freevars(); m_lookahead_cube_psat_var_exp = p.lookahead_cube_psat_var_exp(); 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", 90000); m_simplify_mult2 = _p.get_double("simplify_mult2", 1.5); m_simplify_max = _p.get_uint("simplify_max", 1000000); // -------------------------------- m_simplify_delay = p.simplify_delay(); s = p.gc(); if (s == symbol("dyn_psm")) m_gc_strategy = GC_DYN_PSM; else if (s == symbol("glue_psm")) m_gc_strategy = GC_GLUE_PSM; else if (s == symbol("glue")) m_gc_strategy = GC_GLUE; else if (s == symbol("psm")) m_gc_strategy = GC_PSM; else if (s == symbol("psm_glue")) m_gc_strategy = GC_PSM_GLUE; else throw sat_param_exception("invalid gc strategy"); m_gc_initial = p.gc_initial(); m_gc_increment = p.gc_increment(); m_gc_small_lbd = p.gc_small_lbd(); m_gc_k = std::min(255u, p.gc_k()); m_gc_burst = p.gc_burst(); m_gc_defrag = p.gc_defrag(); m_force_cleanup = p.force_cleanup(); m_backtrack_scopes = p.backtrack_scopes(); m_backtrack_init_conflicts = p.backtrack_conflicts(); m_minimize_lemmas = p.minimize_lemmas(); m_core_minimize = p.core_minimize(); m_core_minimize_partial = p.core_minimize_partial(); m_drat_check_unsat = p.drat_check_unsat(); m_drat_check_sat = p.drat_check_sat(); m_drat_file = p.drat_file(); m_drat = (m_drat_check_unsat || m_drat_file != symbol("") || m_drat_check_sat) && p.threads() == 1; m_drat_binary = p.drat_binary(); m_drat_activity = p.drat_activity(); m_dyn_sub_res = p.dyn_sub_res(); // Parameters used in Liang, Ganesh, Poupart, Czarnecki AAAI 2016. m_branching_heuristic = BH_VSIDS; if (p.branching_heuristic() == symbol("vsids")) m_branching_heuristic = BH_VSIDS; else if (p.branching_heuristic() == symbol("chb")) m_branching_heuristic = BH_CHB; else if (p.branching_heuristic() == symbol("lrb")) m_branching_heuristic = BH_LRB; else throw sat_param_exception("invalid branching heuristic: accepted heuristics are 'vsids', 'lrb' or 'chb'"); m_anti_exploration = p.branching_anti_exploration(); m_step_size_init = 0.40; m_step_size_dec = 0.000001; m_step_size_min = 0.06; m_reward_multiplier = 0.9; m_reward_offset = 1000000.0; m_variable_decay = p.variable_decay(); // PB parameters s = p.pb_solver(); if (s != symbol("circuit") && s != symbol("sorting") && s != symbol("totalizer") && s != symbol("solver") && s != symbol("segmented") && s != symbol("binary_merge")) { throw sat_param_exception("invalid PB solver: solver, totalizer, circuit, sorting, segmented, binary_merge"); } s = p.pb_resolve(); if (s == "cardinality") m_pb_resolve = PB_CARDINALITY; else if (s == "rounding") m_pb_resolve = PB_ROUNDING; else throw sat_param_exception("invalid PB resolve: 'cardinality' or 'rounding' expected"); s = p.pb_lemma_format(); if (s == "cardinality") m_pb_lemma_format = PB_LEMMA_CARDINALITY; else if (s == "pb") m_pb_lemma_format = PB_LEMMA_PB; else throw sat_param_exception("invalid PB lemma format: 'cardinality' or 'pb' expected"); m_card_solver = p.cardinality_solver(); m_xor_solver = p.xor_solver(); sat_simplifier_params sp(_p); m_elim_vars = sp.elim_vars(); } void config::collect_param_descrs(param_descrs & r) { sat_params::collect_param_descrs(r); } }; z3-z3-4.8.7/src/sat/sat_config.h000066400000000000000000000131241356505360400162730ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_config.h Abstract: SAT main configuration options. Sub-components have their own options. Author: Leonardo de Moura (leonardo) 2011-05-21. Revision History: --*/ #ifndef SAT_CONFIG_H_ #define SAT_CONFIG_H_ #include "util/params.h" namespace sat { enum phase_selection { PS_ALWAYS_TRUE, PS_ALWAYS_FALSE, PS_BASIC_CACHING, PS_SAT_CACHING, PS_RANDOM }; enum restart_strategy { RS_GEOMETRIC, RS_LUBY, RS_EMA, RS_STATIC }; enum gc_strategy { GC_DYN_PSM, GC_PSM, GC_GLUE, GC_GLUE_PSM, GC_PSM_GLUE }; enum branching_heuristic { BH_VSIDS, BH_CHB, BH_LRB }; enum pb_resolve { PB_CARDINALITY, PB_ROUNDING }; enum pb_lemma_format { PB_LEMMA_CARDINALITY, PB_LEMMA_PB }; enum reward_t { ternary_reward, unit_literal_reward, heule_schur_reward, heule_unit_reward, march_cu_reward }; enum cutoff_t { depth_cutoff, freevars_cutoff, psat_cutoff, adaptive_freevars_cutoff, adaptive_psat_cutoff }; enum local_search_mode { gsat, wsat }; struct config { unsigned long long m_max_memory; phase_selection m_phase; unsigned m_search_sat_conflicts; unsigned m_search_unsat_conflicts; bool m_phase_sticky; unsigned m_rephase_base; unsigned m_reorder_base; double m_reorder_itau; unsigned m_reorder_activity_scale; bool m_propagate_prefetch; restart_strategy m_restart; bool m_restart_fast; unsigned m_restart_initial; double m_restart_factor; // for geometric case double m_restart_margin; // for ema unsigned m_restart_max; unsigned m_activity_scale; double m_fast_glue_avg; double m_slow_glue_avg; unsigned m_inprocess_max; double m_random_freq; unsigned m_random_seed; unsigned m_burst_search; unsigned m_max_conflicts; unsigned m_num_threads; bool m_ddfw_search; unsigned m_ddfw_threads; bool m_prob_search; unsigned m_local_search_threads; bool m_local_search; local_search_mode m_local_search_mode; bool m_local_search_dbg_flips; unsigned m_unit_walk_threads; bool m_unit_walk; bool m_binspr; bool m_lookahead_simplify; bool m_lookahead_simplify_bca; cutoff_t m_lookahead_cube_cutoff; double m_lookahead_cube_fraction; unsigned m_lookahead_cube_depth; double m_lookahead_cube_freevars; double m_lookahead_cube_psat_var_exp; 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; unsigned m_next_simplify1; double m_simplify_mult2; unsigned m_simplify_max; unsigned m_simplify_delay; unsigned m_variable_decay; gc_strategy m_gc_strategy; unsigned m_gc_initial; unsigned m_gc_increment; unsigned m_gc_small_lbd; unsigned m_gc_k; bool m_gc_burst; bool m_gc_defrag; bool m_force_cleanup; // backtracking unsigned m_backtrack_scopes; unsigned m_backtrack_init_conflicts; bool m_minimize_lemmas; bool m_dyn_sub_res; bool m_core_minimize; bool m_core_minimize_partial; // drat proofs bool m_drat; bool m_drat_binary; symbol m_drat_file; bool m_drat_check_unsat; bool m_drat_check_sat; bool m_drat_activity; bool m_card_solver; bool m_xor_solver; pb_resolve m_pb_resolve; pb_lemma_format m_pb_lemma_format; // branching heuristic settings. branching_heuristic m_branching_heuristic; bool m_anti_exploration; double m_step_size_init; double m_step_size_dec; double m_step_size_min; double m_reward_multiplier; double m_reward_offset; // simplifier configurations used outside of sat_simplifier bool m_elim_vars; config(params_ref const & p); void updt_params(params_ref const & p); static void collect_param_descrs(param_descrs & d); }; }; #endif z3-z3-4.8.7/src/sat/sat_ddfw.cpp000066400000000000000000000453101356505360400163070ustar00rootroot00000000000000/*++ Copyright (c) 2019 Microsoft Corporation Module Name: sat_ddfw.cpp Abstract: DDFW Local search module for clauses Author: Nikolaj Bjorner, Marijn Heule 2019-4-23 Notes: http://www.ict.griffith.edu.au/~johnt/publications/CP2006raouf.pdf Todo: - rephase strategy - experiment with backoff schemes for restarts - parallel sync --*/ #include "util/luby.h" #include "sat/sat_ddfw.h" #include "sat/sat_solver.h" #include "sat/sat_params.hpp" namespace sat { ddfw::~ddfw() { for (auto& ci : m_clauses) { m_alloc.del_clause(ci.m_clause); } } lbool ddfw::check(unsigned sz, literal const* assumptions, parallel* p) { init(sz, assumptions); flet _p(m_par, p); while (m_limit.inc() && m_min_sz > 0) { if (should_reinit_weights()) do_reinit_weights(); else if (do_flip()) ; else if (should_restart()) do_restart(); else if (should_parallel_sync()) do_parallel_sync(); else shift_weights(); } return m_min_sz == 0 ? l_true : l_undef; } void ddfw::log() { double sec = m_stopwatch.get_current_seconds(); double kflips_per_sec = (m_flips - m_last_flips) / (1000.0 * sec); if (m_last_flips == 0) { IF_VERBOSE(0, verbose_stream() << "(sat.ddfw :unsat :models :kflips/sec :flips :restarts :reinits :unsat_vars :shifts"; if (m_par) verbose_stream() << " :par"; verbose_stream() << ")\n"); } IF_VERBOSE(0, verbose_stream() << "(sat.ddfw " << std::setw(07) << m_min_sz << std::setw(07) << m_models.size() << std::setw(10) << kflips_per_sec << std::setw(10) << m_flips << std::setw(10) << m_restart_count << std::setw(10) << m_reinit_count << std::setw(10) << m_unsat_vars.size() << std::setw(10) << m_shifts; if (m_par) verbose_stream() << std::setw(10) << m_parsync_count; verbose_stream() << ")\n"); m_stopwatch.start(); m_last_flips = m_flips; } bool ddfw::do_flip() { bool_var v = pick_var(); if (reward(v) > 0 || (reward(v) == 0 && m_rand(100) <= m_config.m_use_reward_zero_pct)) { flip(v); if (m_unsat.size() <= m_min_sz) save_best_values(); return true; } return false; } bool_var ddfw::pick_var() { double sum_pos = 0; unsigned n = 1; bool_var v0 = null_bool_var; for (bool_var v : m_unsat_vars) { int r = reward(v); if (r > 0) { sum_pos += score(r); } else if (r == 0 && sum_pos == 0 && (m_rand() % (n++)) == 0) { v0 = v; } } if (sum_pos > 0) { double lim_pos = ((double) m_rand() / (1.0 + m_rand.max_value())) * sum_pos; for (bool_var v : m_unsat_vars) { int r = reward(v); if (r > 0) { lim_pos -= score(r); if (lim_pos <= 0) { if (m_par) update_reward_avg(v); return v; } } } } if (v0 != null_bool_var) { return v0; } return m_unsat_vars.elem_at(m_rand(m_unsat_vars.size())); } /** * TBD: map reward value to a score, possibly through an exponential function, such as * exp(-tau/r), where tau > 0 */ double ddfw::mk_score(unsigned r) { return r; } void ddfw::add(unsigned n, literal const* c) { clause* cls = m_alloc.mk_clause(n, c, false); unsigned idx = m_clauses.size(); m_clauses.push_back(clause_info(cls, m_config.m_init_clause_weight)); for (literal lit : *cls) { m_use_list.reserve(lit.index()+1); m_vars.reserve(lit.var()+1); m_use_list[lit.index()].push_back(idx); } } void ddfw::add(solver const& s) { for (auto& ci : m_clauses) { m_alloc.del_clause(ci.m_clause); } m_clauses.reset(); m_use_list.reset(); m_num_non_binary_clauses = 0; unsigned trail_sz = s.init_trail_size(); for (unsigned i = 0; i < trail_sz; ++i) { add(1, s.m_trail.c_ptr() + i); } unsigned sz = s.m_watches.size(); for (unsigned l_idx = 0; l_idx < sz; ++l_idx) { literal l1 = ~to_literal(l_idx); watch_list const & wlist = s.m_watches[l_idx]; for (watched const& w : wlist) { if (!w.is_binary_non_learned_clause()) continue; literal l2 = w.get_literal(); if (l1.index() > l2.index()) continue; literal ls[2] = { l1, l2 }; add(2, ls); } } for (clause* c : s.m_clauses) { add(c->size(), c->begin()); } m_num_non_binary_clauses = s.m_clauses.size(); } void ddfw::add_assumptions() { for (unsigned i = 0; i < m_assumptions.size(); ++i) { add(1, m_assumptions.c_ptr() + i); } } void ddfw::init(unsigned sz, literal const* assumptions) { m_assumptions.reset(); m_assumptions.append(sz, assumptions); add_assumptions(); for (unsigned v = 0; v < num_vars(); ++v) { literal lit(v, false), nlit(v, true); value(v) = (m_rand() % 2) == 0; // m_use_list[lit.index()].size() >= m_use_list[nlit.index()].size(); } init_clause_data(); flatten_use_list(); m_reinit_count = 0; m_reinit_next = m_config.m_reinit_base; m_restart_count = 0; m_restart_next = m_config.m_restart_base*2; m_parsync_count = 0; m_parsync_next = m_config.m_parsync_base; m_min_sz = m_unsat.size(); m_flips = 0; m_last_flips = 0; m_shifts = 0; m_stopwatch.start(); } void ddfw::reinit(solver& s) { add(s); add_assumptions(); if (s.m_best_phase_size > 0) { for (unsigned v = 0; v < num_vars(); ++v) { value(v) = s.m_best_phase[v]; reward(v) = 0; make_count(v) = 0; } } init_clause_data(); flatten_use_list(); } void ddfw::flatten_use_list() { m_use_list_index.reset(); m_flat_use_list.reset(); for (auto const& ul : m_use_list) { m_use_list_index.push_back(m_flat_use_list.size()); m_flat_use_list.append(ul); } m_use_list_index.push_back(m_flat_use_list.size()); } void ddfw::flip(bool_var v) { ++m_flips; literal lit = literal(v, !value(v)); literal nlit = ~lit; SASSERT(is_true(lit)); for (unsigned cls_idx : use_list(*this, lit)) { clause_info& ci = m_clauses[cls_idx]; ci.del(lit); unsigned w = ci.m_weight; // cls becomes false: flip any variable in clause to receive reward w switch (ci.m_num_trues) { case 0: { m_unsat.insert(cls_idx); clause const& c = get_clause(cls_idx); for (literal l : c) { inc_reward(l, w); inc_make(l); } inc_reward(lit, w); break; } case 1: dec_reward(to_literal(ci.m_trues), w); break; default: break; } } for (unsigned cls_idx : use_list(*this, nlit)) { clause_info& ci = m_clauses[cls_idx]; unsigned w = ci.m_weight; // the clause used to have a single true (pivot) literal, now it has two. // Then the previous pivot is no longer penalized for flipping. switch (ci.m_num_trues) { case 0: { m_unsat.remove(cls_idx); clause const& c = get_clause(cls_idx); for (literal l : c) { dec_reward(l, w); dec_make(l); } dec_reward(nlit, w); break; } case 1: inc_reward(to_literal(ci.m_trues), w); break; default: break; } ci.add(nlit); } value(v) = !value(v); } bool ddfw::should_reinit_weights() { return m_flips >= m_reinit_next; } void ddfw::do_reinit_weights() { log(); if (m_reinit_count % 2 == 0) { for (auto& ci : m_clauses) { ci.m_weight += 1; } } else { for (auto& ci : m_clauses) { if (ci.is_true()) { ci.m_weight = m_config.m_init_clause_weight; } else { ci.m_weight = m_config.m_init_clause_weight + 1; } } } init_clause_data(); ++m_reinit_count; m_reinit_next += m_reinit_count * m_config.m_reinit_base; } void ddfw::init_clause_data() { for (unsigned v = 0; v < num_vars(); ++v) { make_count(v) = 0; reward(v) = 0; } m_unsat_vars.reset(); m_unsat.reset(); unsigned sz = m_clauses.size(); for (unsigned i = 0; i < sz; ++i) { auto& ci = m_clauses[i]; clause const& c = get_clause(i); ci.m_trues = 0; ci.m_num_trues = 0; for (literal lit : c) { if (is_true(lit)) { ci.add(lit); } } switch (ci.m_num_trues) { case 0: for (literal lit : c) { inc_reward(lit, ci.m_weight); inc_make(lit); } m_unsat.insert(i); break; case 1: dec_reward(to_literal(ci.m_trues), ci.m_weight); break; default: break; } } } bool ddfw::should_restart() { return m_flips >= m_restart_next; } void ddfw::do_restart() { reinit_values(); init_clause_data(); m_restart_next += m_config.m_restart_base*get_luby(++m_restart_count); } /** \brief the higher the bias, the lower the probability to deviate from the value of the bias during a restart. bias = 0 -> flip truth value with 50% |bias| = 1 -> toss coin with 25% probability |bias| = 2 -> toss coin with 12.5% probability etc */ void ddfw::reinit_values() { for (unsigned i = 0; i < num_vars(); ++i) { int b = bias(i); if (0 == (m_rand() % (1 + abs(b)))) { value(i) = (m_rand() % 2) == 0; } else { value(i) = bias(i) > 0; } } } bool ddfw::should_parallel_sync() { return m_par != nullptr && m_flips >= m_parsync_next; } void ddfw::do_parallel_sync() { if (m_par->from_solver(*this)) { // Sum exp(xi) / exp(a) = Sum exp(xi - a) double max_avg = 0; for (unsigned v = 0; v < num_vars(); ++v) { max_avg = std::max(max_avg, (double)m_vars[v].m_reward_avg); } double sum = 0; for (unsigned v = 0; v < num_vars(); ++v) { sum += exp(m_config.m_itau * (m_vars[v].m_reward_avg - max_avg)); } if (sum == 0) { sum = 0.01; } m_probs.reset(); for (unsigned v = 0; v < num_vars(); ++v) { m_probs.push_back(exp(m_config.m_itau * (m_vars[v].m_reward_avg - max_avg)) / sum); } m_par->to_solver(*this); } ++m_parsync_count; m_parsync_next *= 3; m_parsync_next /= 2; } void ddfw::save_best_values() { if (m_unsat.empty()) { m_model.reserve(num_vars()); for (unsigned i = 0; i < num_vars(); ++i) { m_model[i] = to_lbool(value(i)); } } if (m_unsat.size() < m_min_sz) { m_models.reset(); // skip saving the first model. for (unsigned v = 0; v < num_vars(); ++v) { int& b = bias(v); if (abs(b) > 3) { b = b > 0 ? 3 : -3; } } } unsigned h = value_hash(); if (!m_models.contains(h)) { for (unsigned v = 0; v < num_vars(); ++v) { bias(v) += value(v) ? 1 : -1; } m_models.insert(h); if (m_models.size() > m_config.m_max_num_models) { m_models.erase(*m_models.begin()); } } m_min_sz = m_unsat.size(); } unsigned ddfw::value_hash() const { unsigned s0 = 0, s1 = 0; for (auto const& vi : m_vars) { s0 += vi.m_value; s1 += s0; } return s1; } /** \brief Filter on whether to select a satisfied clause 1. with some probability prefer higher weight to lesser weight. 2. take into account number of trues ? 3. select multiple clauses instead of just one per clause in unsat. */ bool ddfw::select_clause(unsigned max_weight, unsigned max_trues, clause_info const& cn, unsigned& n) { if (cn.m_num_trues == 0 || cn.m_weight < max_weight) { return false; } if (cn.m_weight > max_weight) { n = 2; return true; } return (m_rand() % (n++)) == 0; } unsigned ddfw::select_max_same_sign(unsigned cf_idx) { clause const& c = get_clause(cf_idx); unsigned max_weight = 2; unsigned max_trues = 0; unsigned cl = UINT_MAX; // clause pointer to same sign, max weight satisfied clause. unsigned n = 1; for (literal lit : c) { for (unsigned cn_idx : use_list(*this, lit)) { auto& cn = m_clauses[cn_idx]; if (select_clause(max_weight, max_trues, cn, n)) { cl = cn_idx; max_weight = cn.m_weight; max_trues = cn.m_num_trues; } } } return cl; } void ddfw::shift_weights() { ++m_shifts; for (unsigned cf_idx : m_unsat) { auto& cf = m_clauses[cf_idx]; SASSERT(!cf.is_true()); unsigned cn_idx = select_max_same_sign(cf_idx); while (cn_idx == UINT_MAX) { unsigned idx = (m_rand() * m_rand()) % m_clauses.size(); auto & cn = m_clauses[idx]; if (cn.is_true() && cn.m_weight >= 2) { cn_idx = idx; } } auto & cn = m_clauses[cn_idx]; SASSERT(cn.is_true()); unsigned wn = cn.m_weight; SASSERT(wn >= 2); unsigned inc = (wn > 2) ? 2 : 1; SASSERT(wn - inc >= 1); cf.m_weight += inc; cn.m_weight -= inc; for (literal lit : get_clause(cf_idx)) { inc_reward(lit, inc); } if (cn.m_num_trues == 1) { inc_reward(to_literal(cn.m_trues), inc); } } // DEBUG_CODE(invariant();); } std::ostream& ddfw::display(std::ostream& out) const { unsigned num_cls = m_clauses.size(); for (unsigned i = 0; i < num_cls; ++i) { out << get_clause(i) << " "; auto const& ci = m_clauses[i]; out << ci.m_num_trues << " " << ci.m_weight << "\n"; } for (unsigned v = 0; v < num_vars(); ++v) { out << v << ": " << reward(v) << "\n"; } out << "unsat vars: "; for (bool_var v : m_unsat_vars) { out << v << " "; } out << "\n"; return out; } void ddfw::invariant() { // every variable in unsat vars is in a false clause. for (bool_var v : m_unsat_vars) { bool found = false; for (unsigned cl : m_unsat) { for (literal lit : get_clause(cl)) { if (lit.var() == v) { found = true; break; } } if (found) break; } if (!found) IF_VERBOSE(0, verbose_stream() << "unsat var not found: " << v << "\n"; ); VERIFY(found); } for (unsigned v = 0; v < num_vars(); ++v) { int v_reward = 0; literal lit(v, !value(v)); for (unsigned j : m_use_list[lit.index()]) { clause_info const& ci = m_clauses[j]; if (ci.m_num_trues == 1) { SASSERT(lit == to_literal(ci.m_trues)); v_reward -= ci.m_weight; } } for (unsigned j : m_use_list[(~lit).index()]) { clause_info const& ci = m_clauses[j]; if (ci.m_num_trues == 0) { v_reward += ci.m_weight; } } IF_VERBOSE(0, if (v_reward != reward(v)) verbose_stream() << v << " " << v_reward << " " << reward(v) << "\n"); SASSERT(reward(v) == v_reward); } DEBUG_CODE( for (auto const& ci : m_clauses) { SASSERT(ci.m_weight > 0); } for (unsigned i = 0; i < m_clauses.size(); ++i) { bool found = false; for (literal lit : get_clause(i)) { if (is_true(lit)) found = true; } SASSERT(found == !m_unsat.contains(i)); } // every variable in a false clause is in unsat vars for (unsigned cl : m_unsat) { for (literal lit : get_clause(cl)) { SASSERT(m_unsat_vars.contains(lit.var())); } }); } void ddfw::updt_params(params_ref const& _p) { sat_params p(_p); m_config.m_init_clause_weight = p.ddfw_init_clause_weight(); m_config.m_use_reward_zero_pct = p.ddfw_use_reward_pct(); m_config.m_reinit_base = p.ddfw_reinit_base(); m_config.m_restart_base = p.ddfw_restart_base(); } } z3-z3-4.8.7/src/sat/sat_ddfw.h000066400000000000000000000161471356505360400157620ustar00rootroot00000000000000/*++ Copyright (c) 2019 Microsoft Corporation Module Name: sat_ddfw.h Abstract: DDFW Local search module for clauses Author: Nikolaj Bjorner, Marijn Heule 2019-4-23 Notes: http://www.ict.griffith.edu.au/~johnt/publications/CP2006raouf.pdf --*/ #ifndef _SAT_DDFW_ #define _SAT_DDFW_ #include "util/uint_set.h" #include "util/rlimit.h" #include "util/params.h" #include "util/ema.h" #include "sat/sat_clause.h" #include "sat/sat_types.h" namespace sat { class solver; class parallel; class ddfw : public i_local_search { struct clause_info { clause_info(clause* cl, unsigned init_weight): m_weight(init_weight), m_trues(0), m_num_trues(0), m_clause(cl) {} unsigned m_weight; // weight of clause unsigned m_trues; // set of literals that are true unsigned m_num_trues; // size of true set clause* m_clause; bool is_true() const { return m_num_trues > 0; } void add(literal lit) { ++m_num_trues; m_trues += lit.index(); } void del(literal lit) { SASSERT(m_num_trues > 0); --m_num_trues; m_trues -= lit.index(); } }; struct config { config() { reset(); } unsigned m_use_reward_zero_pct; unsigned m_init_clause_weight; unsigned m_max_num_models; unsigned m_restart_base; unsigned m_reinit_base; unsigned m_parsync_base; double m_itau; void reset() { m_init_clause_weight = 8; m_use_reward_zero_pct = 15; m_max_num_models = (1 << 10); m_restart_base = 100333; m_reinit_base = 10000; m_parsync_base = 333333; m_itau = 0.5; } }; struct var_info { var_info(): m_value(false), m_reward(0), m_make_count(0), m_bias(0), m_reward_avg(1e-5) {} bool m_value; int m_reward; unsigned m_make_count; int m_bias; ema m_reward_avg; }; config m_config; reslimit m_limit; clause_allocator m_alloc; svector m_clauses; literal_vector m_assumptions; svector m_vars; // var -> info svector m_probs; // var -> probability of flipping svector m_scores; // reward -> score model m_model; // var -> best assignment vector m_use_list; unsigned_vector m_flat_use_list; unsigned_vector m_use_list_index; indexed_uint_set m_unsat; indexed_uint_set m_unsat_vars; // set of variables that are in unsat clauses random_gen m_rand; unsigned m_num_non_binary_clauses; unsigned m_restart_count, m_reinit_count, m_parsync_count; uint64_t m_restart_next, m_reinit_next, m_parsync_next; uint64_t m_flips, m_last_flips, m_shifts; unsigned m_min_sz; hashtable> m_models; stopwatch m_stopwatch; parallel* m_par; class use_list { ddfw& p; unsigned i; public: use_list(ddfw& p, literal lit): p(p), i(lit.index()) {} unsigned const* begin() { return p.m_flat_use_list.c_ptr() + p.m_use_list_index[i]; } unsigned const* end() { return p.m_flat_use_list.c_ptr() + p.m_use_list_index[i+1]; } }; void flatten_use_list(); double mk_score(unsigned r); inline double score(unsigned r) { return r; } // TBD: { for (unsigned sz = m_scores.size(); sz <= r; ++sz) m_scores.push_back(mk_score(sz)); return m_scores[r]; } inline unsigned num_vars() const { return m_vars.size(); } inline unsigned& make_count(bool_var v) { return m_vars[v].m_make_count; } inline bool& value(bool_var v) { return m_vars[v].m_value; } inline bool value(bool_var v) const { return m_vars[v].m_value; } inline int& reward(bool_var v) { return m_vars[v].m_reward; } inline int reward(bool_var v) const { return m_vars[v].m_reward; } inline int& bias(bool_var v) { return m_vars[v].m_bias; } unsigned value_hash() const; inline bool is_true(literal lit) const { return value(lit.var()) != lit.sign(); } inline clause const& get_clause(unsigned idx) const { return *m_clauses[idx].m_clause; } inline unsigned get_weight(unsigned idx) const { return m_clauses[idx].m_weight; } inline bool is_true(unsigned idx) const { return m_clauses[idx].is_true(); } void update_reward_avg(bool_var v) { m_vars[v].m_reward_avg.update(reward(v)); } unsigned select_max_same_sign(unsigned cf_idx); inline void inc_make(literal lit) { bool_var v = lit.var(); if (make_count(v)++ == 0) m_unsat_vars.insert(v); } inline void dec_make(literal lit) { bool_var v = lit.var(); if (--make_count(v) == 0) m_unsat_vars.remove(v); } inline void inc_reward(literal lit, int inc) { reward(lit.var()) += inc; } inline void dec_reward(literal lit, int inc) { reward(lit.var()) -= inc; } // flip activity bool do_flip(); bool_var pick_var(); void flip(bool_var v); void save_best_values(); // shift activity void shift_weights(); // reinitialize weights activity bool should_reinit_weights(); void do_reinit_weights(); inline bool select_clause(unsigned max_weight, unsigned max_trues, clause_info const& cn, unsigned& n); // restart activity bool should_restart(); void do_restart(); void reinit_values(); // parallel integration bool should_parallel_sync(); void do_parallel_sync(); void log(); void init(unsigned sz, literal const* assumptions); void init_clause_data(); void invariant(); void add(unsigned sz, literal const* c); void add_assumptions(); public: ddfw(): m_par(nullptr) {} ~ddfw() override; lbool check(unsigned sz, literal const* assumptions, parallel* p) override; void updt_params(params_ref const& p) override; model const& get_model() const override { return m_model; } reslimit& rlimit() override { return m_limit; } void set_seed(unsigned n) override { m_rand.set_seed(n); } void add(solver const& s) override; std::ostream& display(std::ostream& out) const; // for parallel integration unsigned num_non_binary_clauses() const override { return m_num_non_binary_clauses; } void reinit(solver& s) override; void collect_statistics(statistics& st) const override {} double get_priority(bool_var v) const override { return m_probs[v]; } }; } #endif z3-z3-4.8.7/src/sat/sat_drat.cpp000066400000000000000000000607231356505360400163220ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: sat_drat.cpp Abstract: Produce DRAT proofs. Check them using a very simple forward checker that interacts with external plugins. Author: Nikolaj Bjorner (nbjorner) 2017-2-3 Notes: --*/ #include "sat_solver.h" #include "sat_drat.h" namespace sat { drat::drat(solver& s): s(s), m_out(nullptr), m_bout(nullptr), m_inconsistent(false), m_num_add(0), m_num_del(0), m_check_unsat(false), m_check_sat(false), m_check(false), m_activity(false) { if (s.get_config().m_drat && s.get_config().m_drat_file != symbol()) { auto mode = s.get_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.get_config().m_drat_file.str().c_str(), mode); if (s.get_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) { m_alloc.del_clause(c); } } m_proof.reset(); m_out = nullptr; m_bout = nullptr; } void drat::updt_config() { m_check_unsat = s.get_config().m_drat_check_unsat; m_check_sat = s.get_config().m_drat_check_sat; m_check = m_check_unsat || m_check_sat; m_activity = s.get_config().m_drat_activity; } std::ostream& operator<<(std::ostream& out, drat::status st) { switch (st) { case drat::status::learned: return out << "l"; case drat::status::asserted: return out << "a"; case drat::status::deleted: return out << "d"; case drat::status::external: return out << "e"; default: return out; } } void drat::dump(unsigned n, literal const* c, status st) { if (st == status::asserted || st == status::external) { return; } if (m_activity && ((m_num_add % 1000) == 0)) { dump_activity(); } char buffer[10000]; char digits[20]; // enough for storing unsigned char* lastd = digits + sizeof(digits); unsigned 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 < sizeof(buffer) + d); 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::dump_activity() { (*m_out) << "c a "; for (unsigned v = 0; v < s.num_vars(); ++v) { (*m_out) << s.m_activity[v] << " "; } (*m_out) << "\n"; } void drat::bdump(unsigned n, literal const* c, status st) { unsigned char ch = 0; switch (st) { case status::asserted: return; case status::external: return; case status::learned: ch = 'a'; break; case status::deleted: ch = 'd'; break; default: UNREACHABLE(); break; } 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 { literal last = null_literal; unsigned n = c.size(); for (unsigned i = 0; i < n; ++i) { if (c[i] == last) return true; last = c[i]; } return false; } void drat::trace(std::ostream& out, unsigned n, literal const* c, status st) { out << st << " "; literal last = null_literal; for (unsigned i = 0; i < n; ++i) { if (c[i] != last) { out << c[i] << " "; last = c[i]; } } out << "\n"; } void drat::append(literal l, status st) { TRACE("sat_drat", tout << st << " " << l << "\n";); declare(l); IF_VERBOSE(20, trace(verbose_stream(), 1, &l, st);); if (st == status::learned) { verify(1, &l); } if (st == status::deleted) { return; } if (m_check_unsat) { assign_propagate(l); } m_units.push_back(l); } void drat::append(literal l1, literal l2, status st) { TRACE("sat_drat", tout << st << " " << l1 << " " << l2 << "\n";); declare(l1); declare(l2); literal lits[2] = { l1, l2 }; IF_VERBOSE(20, trace(verbose_stream(), 2, lits, st);); if (st == status::deleted) { // noop // don't record binary as deleted. } else { if (st == status::learned) { verify(2, lits); } clause* c = m_alloc.mk_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); m_watches[(~l2).index()].push_back(idx); if (value(l1) == l_false && value(l2) == l_false) { m_inconsistent = true; } else if (value(l1) == l_false) { assign_propagate(l2); } else if (value(l2) == l_false) { assign_propagate(l1); } } } #if 0 // debugging code bool drat::is_clause(clause& c, literal l1, literal l2, literal l3, drat::status st1, drat::status st2) { //if (st1 != st2) return false; if (c.size() != 3) return false; if (l1 == c[0]) { if (l2 == c[1] && l3 == c[2]) return true; if (l2 == c[2] && l3 == c[1]) return true; } if (l2 == c[0]) { if (l1 == c[1] && l3 == c[2]) return true; if (l1 == c[2] && l3 == c[1]) return true; } if (l3 == c[0]) { if (l1 == c[1] && l2 == c[2]) return true; if (l1 == c[2] && l2 == c[1]) return true; } return false; } #endif void drat::append(clause& c, status st) { TRACE("sat_drat", tout << st << " " << c << "\n";); for (literal lit : c) declare(lit); unsigned n = c.size(); IF_VERBOSE(20, trace(verbose_stream(), n, c.begin(), st);); if (st == status::learned) { verify(c); } m_status.push_back(st); m_proof.push_back(&c); if (st == status::deleted) { if (n > 0) del_watch(c, c[0]); if (n > 1) del_watch(c, c[1]); return; } unsigned num_watch = 0; literal l1, l2; for (unsigned i = 0; i < n; ++i) { if (value(c[i]) != l_false) { if (num_watch == 0) { l1 = c[i]; ++num_watch; } else { l2 = c[i]; ++num_watch; break; } } } switch (num_watch) { case 0: m_inconsistent = true; break; case 1: assign_propagate(l1); break; default: { SASSERT(num_watch == 2); unsigned idx = m_watched_clauses.size(); m_watched_clauses.push_back(watched_clause(&c, l1, l2)); m_watches[(~l1).index()].push_back(idx); m_watches[(~l2).index()].push_back(idx); break; } } } void drat::del_watch(clause& c, literal l) { watch& w = m_watches[(~l).index()]; for (unsigned i = 0; i < w.size(); ++i) { if (m_watched_clauses[w[i]].m_clause == &c) { w[i] = w.back(); w.pop_back(); break; } } } void drat::declare(literal l) { if (!m_check) return; unsigned n = static_cast(l.var()); while (m_assignment.size() <= n) { m_assignment.push_back(l_undef); m_watches.push_back(watch()); m_watches.push_back(watch()); } } bool drat::is_drup(unsigned n, literal const* c) { if (m_inconsistent || n == 0) return true; unsigned num_units = m_units.size(); for (unsigned i = 0; !m_inconsistent && i < n; ++i) { assign_propagate(~c[i]); } if (!m_inconsistent) { DEBUG_CODE(validate_propagation();); } 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.shrink(num_units); bool ok = m_inconsistent; IF_VERBOSE(9, verbose_stream() << "is-drup " << m_inconsistent << "\n"); m_inconsistent = false; return ok; } bool drat::is_drat(unsigned n, literal const* c) { if (m_inconsistent || n == 0) return true; for (unsigned i = 0; i < n; ++i) { if (is_drat(n, c, i)) return true; } return false; } void drat::validate_propagation() const { for (unsigned i = 0; i < m_proof.size(); ++i) { status st = m_status[i]; 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) { switch (value(c[j])) { case l_false: break; case l_true: num_true++; break; case l_undef: num_undef++; break; } } CTRACE("sat_drat", num_true == 0 && num_undef == 1, display(tout);); SASSERT(num_true != 0 || num_undef != 1); } } } bool drat::is_drat(unsigned n, literal const* c, unsigned pos) { SASSERT(pos < n); literal l = c[pos]; literal_vector lits(n, c); SASSERT(lits.size() == n); for (unsigned i = 0; i < m_proof.size(); ++i) { status st = m_status[i]; 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) {} if (j != c.size()) { lits.append(j, c.begin()); lits.append(c.size() - j - 1, c.begin() + j + 1); if (!is_drup(lits.size(), lits.c_ptr())) return false; lits.resize(n); } } } return true; } void drat::verify(unsigned n, literal const* c) { 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_drat", tout << literal_vector(n, c) << "\n"; display(tout); s.display(tout);); UNREACHABLE(); } } bool drat::contains(literal c, justification const& j) { if (!m_check_sat) { return true; } switch (j.get_kind()) { case justification::NONE: return m_units.contains(c); case justification::BINARY: return contains(c, j.get_literal()); case justification::TERNARY: return contains(c, j.get_literal1(), j.get_literal2()); case justification::CLAUSE: return contains(s.get_clause(j)); default: return true; } } bool drat::contains(unsigned n, literal const* lits) { if (!m_check) return true; unsigned num_add = 0; unsigned num_del = 0; for (unsigned i = m_proof.size(); i-- > 0; ) { clause& c = *m_proof[i]; status st = m_status[i]; if (match(n, lits, c)) { if (st == status::deleted) { num_del++; } else { num_add++; } } } return num_add > num_del; } 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) { lbool v = value(literal(i, false)); if (v != l_undef) out << i << ": " << v << "\n"; } for (unsigned i = 0; i < m_proof.size(); ++i) { clause* c = m_proof[i]; if (m_status[i] != status::deleted && c) { unsigned num_true = 0; unsigned num_undef = 0; for (unsigned j = 0; j < c->size(); ++j) { switch (value((*c)[j])) { case l_true: num_true++; break; case l_undef: num_undef++; break; default: break; } } if (num_true == 0 && num_undef == 0) { out << "False "; } if (num_true == 0 && num_undef == 1) { out << "Unit "; } out << m_status[i] << " " << i << ": " << *c << "\n"; } } for (unsigned i = 0; i < m_assignment.size(); ++i) { watch const& w1 = m_watches[2*i]; watch const& w2 = m_watches[2*i + 1]; if (!w1.empty()) { out << i << " |-> "; for (unsigned i = 0; i < w1.size(); ++i) out << *(m_watched_clauses[w1[i]].m_clause) << " "; out << "\n"; } if (!w2.empty()) { out << "-" << i << " |-> "; for (unsigned i = 0; i < w2.size(); ++i) out << *(m_watched_clauses[w2[i]].m_clause) << " "; out << "\n"; } } } lbool drat::value(literal l) const { lbool val = m_assignment.get(l.var(), l_undef); return val == l_undef || !l.sign() ? val : ~val; } void drat::assign(literal l) { lbool new_value = l.sign() ? l_false : l_true; lbool old_value = value(l); // TRACE("sat_drat", tout << "assign " << l << " := " << new_value << " from " << old_value << "\n";); switch (old_value) { case l_false: m_inconsistent = true; break; case l_true: break; case l_undef: m_assignment.setx(l.var(), new_value, l_undef); m_units.push_back(l); break; } } void drat::assign_propagate(literal l) { unsigned num_units = m_units.size(); assign(l); for (unsigned i = num_units; !m_inconsistent && i < m_units.size(); ++i) { propagate(m_units[i]); } } void drat::propagate(literal l) { watch& clauses = m_watches[l.index()]; watch::iterator it = clauses.begin(); watch::iterator it2 = it; watch::iterator end = clauses.end(); for (; it != end; ++it) { unsigned idx = *it; watched_clause& wc = m_watched_clauses[idx]; clause& c = *wc.m_clause; //TRACE("sat_drat", tout << "Propagate " << l << " " << c << " watch: " << wc.m_l1 << " " << wc.m_l2 << "\n";); if (wc.m_l1 == ~l) { std::swap(wc.m_l1, wc.m_l2); } SASSERT(wc.m_l2 == ~l); if (value(wc.m_l1) == l_true) { *it2 = *it; it2++; } else { bool done = false; for (unsigned i = 0; !done && i < c.size(); ++i) { literal lit = c[i]; if (lit != wc.m_l1 && lit != wc.m_l2 && value(lit) != l_false) { wc.m_l2 = lit; m_watches[(~lit).index()].push_back(idx); done = true; } } if (done) { continue; } else if (value(wc.m_l1) == l_false) { m_inconsistent = true; goto end_process_watch; } else { *it2 = *it; it2++; assign(wc.m_l1); } } } end_process_watch: for (; it != end; ++it, ++it2) *it2 = *it; clauses.set_end(it2); } drat::status drat::get_status(bool learned) const { return learned || s.m_searching ? status::learned : status::asserted; } void drat::add() { ++m_num_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) { ++m_num_add; 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) { ++m_num_add; 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) { ++m_num_add; 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) { clause* cl = m_alloc.mk_clause(c.size(), c.begin(), learned); append(*cl, get_status(learned)); } } void drat::add(literal_vector const& lits, svector const& premises) { ++m_num_add; if (m_check) { switch (lits.size()) { case 0: add(); break; case 1: append(lits[0], status::external); break; default: { clause* c = m_alloc.mk_clause(lits.size(), lits.c_ptr(), true); append(*c, status::external); break; } } } } void drat::add(literal_vector const& c) { ++m_num_add; if (m_out) dump(c.size(), c.begin(), status::learned); if (m_bout) bdump(c.size(), c.begin(), status::learned); if (m_check) { for (literal lit : c) declare(lit); switch (c.size()) { case 0: add(); break; case 1: append(c[0], status::learned); break; default: { verify(c.size(), c.begin()); clause* cl = m_alloc.mk_clause(c.size(), c.c_ptr(), true); append(*cl, status::external); break; } } } } void drat::del(literal l) { ++m_num_del; 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) { ++m_num_del; literal ls[2] = {l1, l2}; SASSERT(!(l1 == literal(13923, false) && l2 == literal(14020, true))); if (m_out) dump(2, ls, 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 ++m_num_del; //SASSERT(!(c.size() == 2 && c[0] == literal(13923, false) && c[1] == literal(14020, true))); 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 = m_alloc.mk_clause(c.size(), c.begin(), c.is_learned()); append(*c1, status::deleted); } } void drat::check_model(model const& m) { } } z3-z3-4.8.7/src/sat/sat_drat.h000066400000000000000000000101231356505360400157540ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: sat_drat.h Abstract: Produce DRAT proofs. Author: Nikolaj Bjorner (nbjorner) 2017-2-3 Notes: --*/ #ifndef SAT_DRAT_H_ #define SAT_DRAT_H_ namespace sat { class drat { public: struct s_ext {}; struct s_unit {}; struct premise { enum { t_clause, t_unit, t_ext } m_type; union { clause* m_clause; unsigned m_literal; }; premise(s_ext, literal l): m_type(t_ext), m_literal(l.index()) {} premise(s_unit, literal l): m_type(t_unit), m_literal(l.index()) {} premise(clause* c): m_type(t_clause), m_clause(c) {} }; private: enum status { asserted, learned, deleted, external }; struct watched_clause { clause* m_clause; literal m_l1, m_l2; watched_clause(clause* c, literal l1, literal l2): m_clause(c), m_l1(l1), m_l2(l2) {} }; svector m_watched_clauses; typedef svector watch; solver& s; clause_allocator m_alloc; std::ostream* m_out; std::ostream* m_bout; ptr_vector m_proof; svector m_status; literal_vector m_units; vector m_watches; svector m_assignment; bool m_inconsistent; unsigned m_num_add, m_num_del; bool m_check_unsat, m_check_sat, m_check, m_activity; void dump_activity(); 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); bool is_clause(clause& c, literal l1, literal l2, literal l3, status st1, status st2); friend std::ostream& operator<<(std::ostream & out, status st); status get_status(bool learned) const; void declare(literal l); void assign(literal l); void propagate(literal l); void assign_propagate(literal l); void del_watch(clause& c, literal l); 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); lbool value(literal l) const; 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); ~drat(); void updt_config(); void add(); void add(literal l, bool learned); void add(literal l1, literal l2, bool learned); void add(clause& c, bool learned); void add(literal_vector const& c, svector const& premises); void add(literal_vector const& c); // add learned clause bool is_cleaned(clause& c) const; void del(literal l); 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); } bool contains(literal c, justification const& j); void check_model(model const& m); }; }; #endif z3-z3-4.8.7/src/sat/sat_elim_eqs.cpp000066400000000000000000000231411356505360400171570ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_elim_eqs.cpp Abstract: Helper class for eliminating eqs. Author: Leonardo de Moura (leonardo) 2011-05-27. Revision History: --*/ #include "sat/sat_elim_eqs.h" #include "sat/sat_solver.h" #include "util/trace.h" namespace sat { elim_eqs::elim_eqs(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()]; else return roots[l.var()]; } void elim_eqs::cleanup_bin_watches(literal_vector const & roots) { unsigned l_idx = 0; m_new_bin.reset(); for (watch_list & wlist : m_solver.m_watches) { literal l1 = ~to_literal(l_idx++); literal r1 = norm(roots, l1); watch_list::iterator it = wlist.begin(); watch_list::iterator itprev = it; watch_list::iterator end = wlist.end(); for (; it != end; ++it) { if (it->is_binary_clause()) { literal l2 = it->get_literal(); literal r2 = norm(roots, l2); if (r1 == r2) { m_solver.assign_unit(r1); if (m_solver.inconsistent()) return; // consume unit continue; } if (r1 == ~r2) { // consume tautology continue; } #if 0 if (l1 != r1) { // add half r1 => r2, the other half ~r2 => ~r1 is added when traversing l2 m_solver.m_watches[(~r1).index()].push_back(watched(r2, it->is_learned())); continue; } it->set_literal(r2); // keep it. #else if (l1 != r1 || l2 != r2) { if (r1.index() < r2.index()) { m_new_bin.push_back(bin(r1, r2, it->is_learned())); } continue; } // keep it #endif } *itprev = *it; itprev++; } wlist.set_end(itprev); } for (auto const& b : m_new_bin) { m_solver.mk_bin_clause(b.l1, b.l2, b.learned); } 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; clause_vector::iterator end = cs.end(); for (; it != end; ++it) { clause & c = *(*it); TRACE("sats", tout << "processing: " << c << "\n";); unsigned sz = c.size(); unsigned i; for (i = 0; i < sz; i++) { literal l = c[i]; literal r = norm(roots, l); if (l != r) break; } if (i == sz) { // clause was not affected *it2 = *it; it2++; continue; } 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]; c[i] = norm(roots, lit); VERIFY(c[i] == norm(roots, c[i])); VERIFY(!m_solver.was_eliminated(c[i].var()) || lit == c[i]); } std::sort(c.begin(), c.end()); for (literal l : c) VERIFY(l == norm(roots, l)); TRACE("sats", tout << "after normalization/sorting: " << c << "\n"; tout.flush();); DEBUG_CODE({ for (literal l : c) { 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 unsigned j = 0; literal l_prev = null_literal; for (i = 0; i < sz; i++) { literal l = c[i]; 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; } if (val == l_false) { continue; // skip } c[j] = l; j++; } TRACE("elim_eqs", tout << "after removing duplicates: " << c << " j: " << j << "\n";); if (i < sz) { drat_delete_clause(); c.set_removed(true); m_solver.del_clause(c); continue; } switch (j) { case 0: m_solver.set_conflict(); for (; it != end; ++it) { *it2 = *it; it2++; } cs.set_end(it2); return; case 1: 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) { c.shrink(j); } 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));); *it2 = *it; it2++; if (!c.frozen()) { m_solver.attach_clause(c); } break; } } cs.set_end(it2); } void elim_eqs::save_elim(literal_vector const & roots, bool_var_vector const & to_elim) { model_converter & mc = m_solver.m_mc; for (bool_var v : to_elim) { literal l(v, false); literal r = roots[v]; SASSERT(v != r.var()); bool set_root = m_solver.set_root(l, r); bool root_ok = !m_solver.is_external(v) || set_root; if (m_solver.is_assumption(v) || (m_solver.is_external(v) && (m_solver.is_incremental() || !root_ok))) { // cannot really eliminate v, since we have to notify extension of future assignments m_solver.mk_bin_clause(~l, r, false); m_solver.mk_bin_clause(l, ~r, false); } else { model_converter::entry & e = mc.mk(model_converter::ELIM_VAR, v); TRACE("save_elim", tout << "marking as deleted: " << v << " l: " << l << " r: " << r << "\n";); m_solver.set_eliminated(v, true); mc.insert(e, ~l, r); mc.insert(e, l, ~r); } } m_solver.flush_roots(); } bool elim_eqs::check_clause(clause const& c, literal_vector const& roots) const { for (literal l : c) { CTRACE("elim_eqs_bug", m_solver.was_eliminated(l.var()), tout << "lit: " << l << " " << norm(roots, l) << "\n"; tout << c << "\n";); if (m_solver.was_eliminated(l.var())) { IF_VERBOSE(0, verbose_stream() << c << " contains eliminated literal " << l << " " << norm(roots, l) << "\n";); UNREACHABLE(); } } return true; } bool elim_eqs::check_clauses(literal_vector const & roots) const { for (clause * cp : m_solver.m_clauses) if (!check_clause(*cp, roots)) return false; for (clause * cp : m_solver.m_learned) if (!check_clause(*cp, roots)) return false; return true; } void elim_eqs::operator()(literal_vector const & roots, bool_var_vector const & to_elim) { TRACE("elim_eqs", tout << "before bin cleanup\n"; m_solver.display(tout);); cleanup_bin_watches(roots); TRACE("elim_eqs", tout << "after bin cleanup\n"; m_solver.display(tout);); cleanup_clauses(roots, m_solver.m_clauses); if (m_solver.inconsistent()) return; cleanup_clauses(roots, m_solver.m_learned); if (m_solver.inconsistent()) return; save_elim(roots, to_elim); m_solver.propagate(false); SASSERT(check_clauses(roots)); TRACE("elim_eqs", tout << "after full cleanup\n"; m_solver.display(tout);); } }; z3-z3-4.8.7/src/sat/sat_elim_eqs.h000066400000000000000000000023011356505360400166170ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_elim_eqs.h Abstract: Helper class for eliminating eqs. Author: Leonardo de Moura (leonardo) 2011-05-27. Revision History: --*/ #ifndef SAT_ELIM_EQS_H_ #define SAT_ELIM_EQS_H_ #include "sat/sat_types.h" namespace sat { class solver; class tmp_clause; class elim_eqs { struct bin { literal l1, l2; bool learned; bin(literal l1, literal l2, bool learned): l1(l1), l2(l2), learned(learned) {} }; 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); bool check_clauses(literal_vector const & roots) const; 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); }; }; #endif z3-z3-4.8.7/src/sat/sat_elim_vars.cpp000066400000000000000000000240211356505360400173400ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: sat_elim_vars.cpp Abstract: Helper class for eliminating variables Author: Nikolaj Bjorner (nbjorner) 2017-10-14 Revision History: --*/ #include "sat/sat_simplifier.h" #include "sat/sat_elim_vars.h" #include "sat/sat_solver.h" namespace sat{ elim_vars::elim_vars(simplifier& s) : simp(s), s(s.s), m(20) { m_mark_lim = 0; m_max_literals = 11; m_miss = 0; m_hit1 = 0; m_hit2 = 0; } bool elim_vars::operator()(bool_var v) { if (s.value(v) != l_undef) return false; literal pos_l(v, false); literal neg_l(v, true); unsigned num_bin_pos = simp.num_nonlearned_bin(pos_l); if (num_bin_pos > m_max_literals) return false; unsigned num_bin_neg = simp.num_nonlearned_bin(neg_l); if (num_bin_neg > m_max_literals) return false; clause_use_list & pos_occs = simp.m_use_list.get(pos_l); clause_use_list & neg_occs = simp.m_use_list.get(neg_l); unsigned clause_size = num_bin_pos + num_bin_neg + pos_occs.num_irredundant() + neg_occs.num_irredundant(); if (clause_size == 0) { return false; } reset_mark(); mark_var(v); if (!mark_literals(pos_occs)) return false; if (!mark_literals(neg_occs)) return false; if (!mark_literals(pos_l)) return false; if (!mark_literals(neg_l)) return false; // associate index with each variable. sort_marked(); bdd b1 = elim_var(v); double sz1 = b1.cnf_size(); if (sz1 > 2*clause_size) { ++m_miss; return false; } if (sz1 <= clause_size) { ++m_hit1; return elim_var(v, b1); } m.try_cnf_reorder(b1); sz1 = b1.cnf_size(); if (sz1 <= clause_size) { ++m_hit2; return elim_var(v, b1); } ++m_miss; return false; } bool elim_vars::elim_var(bool_var v, bdd const& b) { literal pos_l(v, false); literal neg_l(v, true); clause_use_list & pos_occs = simp.m_use_list.get(pos_l); clause_use_list & neg_occs = simp.m_use_list.get(neg_l); // eliminate variable simp.m_pos_cls.reset(); simp.m_neg_cls.reset(); simp.collect_clauses(pos_l, simp.m_pos_cls); simp.collect_clauses(neg_l, simp.m_neg_cls); VERIFY(!simp.is_external(v)); model_converter::entry & mc_entry = s.m_mc.mk(model_converter::ELIM_VAR, v); simp.save_clauses(mc_entry, simp.m_pos_cls); simp.save_clauses(mc_entry, simp.m_neg_cls); s.m_eliminated[v] = true; ++s.m_stats.m_elim_var_bdd; simp.remove_bin_clauses(pos_l); simp.remove_bin_clauses(neg_l); simp.remove_clauses(pos_occs, pos_l); simp.remove_clauses(neg_occs, neg_l); pos_occs.reset(); neg_occs.reset(); literal_vector lits; add_clauses(v, b, lits); return true; } bdd elim_vars::elim_var(bool_var v) { unsigned index = 0; for (bool_var w : m_vars) { m_var2index[w] = index++; } literal pos_l(v, false); literal neg_l(v, true); clause_use_list & pos_occs = simp.m_use_list.get(pos_l); clause_use_list & neg_occs = simp.m_use_list.get(neg_l); bdd b1 = make_clauses(pos_l); bdd b2 = make_clauses(neg_l); bdd b3 = make_clauses(pos_occs); bdd b4 = make_clauses(neg_occs); bdd b0 = b1 && b2 && b3 && b4; bdd b = m.mk_exists(m_var2index[v], b0); TRACE("elim_vars", tout << "eliminate " << v << "\n"; for (watched const& w : simp.get_wlist(~pos_l)) { if (w.is_binary_non_learned_clause()) { tout << pos_l << " " << w.get_literal() << "\n"; } } m.display(tout, b1); for (watched const& w : simp.get_wlist(~neg_l)) { if (w.is_binary_non_learned_clause()) { tout << neg_l << " " << w.get_literal() << "\n"; } } m.display(tout, b2); clause_use_list::iterator itp = pos_occs.mk_iterator(); while (!itp.at_end()) { clause const& c = itp.curr(); tout << c << "\n"; itp.next(); } m.display(tout, b3); clause_use_list::iterator itn = neg_occs.mk_iterator(); while (!itn.at_end()) { clause const& c = itn.curr(); tout << c << "\n"; itn.next(); } m.display(tout, b4); tout << "eliminated:\n"; tout << b << "\n"; tout << b.cnf_size() << "\n"; ); return b; } void elim_vars::add_clauses(bool_var v0, bdd const& b, literal_vector& lits) { if (b.is_true()) { // no-op } else if (b.is_false()) { SASSERT(lits.size() > 0); literal_vector c(lits); if (simp.cleanup_clause(c)) return; switch (c.size()) { case 0: s.set_conflict(); break; case 1: simp.propagate_unit(c[0]); break; case 2: s.m_stats.m_mk_bin_clause++; simp.add_non_learned_binary_clause(c[0], c[1]); simp.back_subsumption1(c[0], c[1], false); break; default: { if (c.size() == 3) s.m_stats.m_mk_ter_clause++; else s.m_stats.m_mk_clause++; clause* cp = s.alloc_clause(c.size(), c.c_ptr(), false); s.m_clauses.push_back(cp); simp.m_use_list.insert(*cp); if (simp.m_sub_counter > 0) simp.back_subsumption1(*cp); else simp.back_subsumption0(*cp); break; } } } else { unsigned v = m_vars[b.var()]; lits.push_back(literal(v, false)); add_clauses(v0, b.lo(), lits); lits.pop_back(); lits.push_back(literal(v, true)); add_clauses(v0, b.hi(), lits); lits.pop_back(); } } void elim_vars::get_clauses(bdd const& b, literal_vector & lits, clause_vector& clauses, literal_vector& units) { if (b.is_true()) { return; } if (b.is_false()) { if (lits.size() > 1) { clause* c = s.alloc_clause(lits.size(), lits.c_ptr(), false); clauses.push_back(c); } else { units.push_back(lits.back()); } return; } // if (v hi lo) // (v | lo) & (!v | hi) // if (v T lo) -> (v | lo) unsigned v = m_vars[b.var()]; lits.push_back(literal(v, false)); get_clauses(b.lo(), lits, clauses, units); lits.pop_back(); lits.push_back(literal(v, true)); get_clauses(b.hi(), lits, clauses, units); lits.pop_back(); } void elim_vars::reset_mark() { m_vars.reset(); m_mark.resize(s.num_vars()); m_var2index.resize(s.num_vars()); m_occ.resize(s.num_vars()); ++m_mark_lim; if (m_mark_lim == 0) { ++m_mark_lim; m_mark.fill(0); } } class elim_vars::compare_occ { elim_vars& ev; public: compare_occ(elim_vars& ev):ev(ev) {} bool operator()(bool_var v1, bool_var v2) const { return ev.m_occ[v1] < ev.m_occ[v2]; } }; void elim_vars::sort_marked() { std::sort(m_vars.begin(), m_vars.end(), compare_occ(*this)); } void elim_vars::shuffle_vars() { unsigned sz = m_vars.size(); for (unsigned i = 0; i < sz; ++i) { unsigned x = m_rand(sz); unsigned y = m_rand(sz); std::swap(m_vars[x], m_vars[y]); } } void elim_vars::mark_var(bool_var v) { if (m_mark[v] != m_mark_lim) { m_mark[v] = m_mark_lim; m_vars.push_back(v); m_occ[v] = 1; } else { ++m_occ[v]; } } bool elim_vars::mark_literals(clause_use_list & occs) { clause_use_list::iterator it = occs.mk_iterator(); while (!it.at_end()) { clause const& c = it.curr(); for (literal l : c) { mark_var(l.var()); } if (num_vars() > m_max_literals) return false; it.next(); } return true; } bool elim_vars::mark_literals(literal lit) { watch_list& wl = simp.get_wlist(lit); for (watched const& w : wl) { if (w.is_binary_non_learned_clause()) { mark_var(w.get_literal().var()); } } return num_vars() <= m_max_literals; } bdd elim_vars::make_clauses(clause_use_list & occs) { bdd result = m.mk_true(); for (auto it = occs.mk_iterator(); !it.at_end(); it.next()) { clause const& c = it.curr(); bdd cl = m.mk_false(); for (literal l : c) { cl |= mk_literal(l); } result &= cl; } return result; } bdd elim_vars::make_clauses(literal lit) { bdd result = m.mk_true(); watch_list& wl = simp.get_wlist(~lit); for (watched const& w : wl) { if (w.is_binary_non_learned_clause()) { result &= (mk_literal(lit) || mk_literal(w.get_literal())); } } return result; } bdd elim_vars::mk_literal(literal l) { return l.sign() ? m.mk_nvar(m_var2index[l.var()]) : m.mk_var(m_var2index[l.var()]); } }; z3-z3-4.8.7/src/sat/sat_elim_vars.h000066400000000000000000000034601356505360400170110ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: sat_elim_vars.h Abstract: Helper class for eliminating variables Author: Nikolaj Bjorner (nbjorner) 2017-10-14 Revision History: --*/ #ifndef SAT_ELIM_VARS_H_ #define SAT_ELIM_VARS_H_ #include "sat/sat_types.h" #include "sat/sat_bdd.h" namespace sat { class solver; class simplifier; class elim_vars { class compare_occ; simplifier& simp; solver& s; bdd_manager m; random_gen m_rand; svector m_vars; unsigned_vector m_mark; unsigned m_mark_lim; unsigned_vector m_var2index; unsigned_vector m_occ; unsigned m_miss; unsigned m_hit1; unsigned m_hit2; unsigned m_max_literals; unsigned num_vars() const { return m_vars.size(); } void reset_mark(); void mark_var(bool_var v); void sort_marked(); void shuffle_vars(); bool mark_literals(clause_use_list & occs); bool mark_literals(literal lit); bdd make_clauses(clause_use_list & occs); bdd make_clauses(literal lit); bdd mk_literal(literal l); void get_clauses(bdd const& b, literal_vector& lits, clause_vector& clauses, literal_vector& units); void add_clauses(bool_var v, bdd const& b, literal_vector& lits); bool elim_var(bool_var v, bdd const& b); bdd elim_var(bool_var v); public: elim_vars(simplifier& s); bool operator()(bool_var v); unsigned hit2() const { return m_hit1; } // first round bdd construction is minimal unsigned hit1() const { return m_hit2; } // minimal after reshufling unsigned miss() const { return m_miss; } // not-minimal }; }; #endif z3-z3-4.8.7/src/sat/sat_extension.h000066400000000000000000000062301356505360400170420ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_extension.h Abstract: An abstract class for SAT extensions. Author: Leonardo de Moura (leonardo) 2011-05-21. Revision History: --*/ #ifndef SAT_EXTENSION_H_ #define SAT_EXTENSION_H_ #include "sat/sat_types.h" #include "util/params.h" #include "util/statistics.h" namespace sat { enum check_result { CR_DONE, CR_CONTINUE, CR_GIVEUP }; class literal_occs_fun { public: virtual double operator()(literal l) = 0; }; typedef svector ext_constraint_list; class ext_use_list { vector m_use_list; public: void init(unsigned num_vars) { m_use_list.reset(); m_use_list.resize(num_vars*2); } void insert(literal l, ext_constraint_idx idx) { get(l).push_back(idx); } ext_constraint_list & get(literal l) { return m_use_list[l.index()]; } ext_constraint_list const & get(literal l) const { return m_use_list[l.index()]; } void finalize() { m_use_list.finalize(); } }; class extension { public: virtual ~extension() {} virtual void set_solver(solver* s) = 0; virtual void set_lookahead(lookahead* s) = 0; virtual void set_unit_walk(unit_walk* u) = 0; virtual bool propagate(literal l, ext_constraint_idx idx) = 0; virtual double get_reward(literal l, ext_constraint_idx idx, literal_occs_fun& occs) const = 0; virtual void get_antecedents(literal l, ext_justification_idx idx, literal_vector & r) = 0; virtual bool is_extended_binary(ext_justification_idx idx, literal_vector & r) = 0; virtual void asserted(literal l) = 0; virtual check_result check() = 0; virtual lbool resolve_conflict() { return l_undef; } // stores result in sat::solver::m_lemma virtual void push() = 0; virtual void pop(unsigned n) = 0; virtual void pre_simplify() = 0; virtual void simplify() = 0; // have a way to replace l by r in all constraints virtual bool set_root(literal l, literal r) { return false; } virtual void flush_roots() {} virtual void clauses_modifed() = 0; 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; virtual void find_mutexes(literal_vector& lits, vector & mutexes) = 0; virtual void gc() = 0; virtual void pop_reinit() = 0; virtual bool validate() = 0; virtual void init_use_list(ext_use_list& ul) = 0; virtual bool is_blocked(literal l, ext_constraint_idx) = 0; virtual bool check_model(model const& m) const = 0; virtual unsigned max_var(unsigned w) const = 0; }; }; #endif z3-z3-4.8.7/src/sat/sat_iff3_finder.cpp000066400000000000000000000167371356505360400175540ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_iff3_finder.cpp Abstract: Find constraints of the form x = l1 = l2 That is, search for clauses of the form ~x \/ l1 \/ ~l2 ~x \/ ~l1 \/ l2 x \/ l1 \/ l2 x \/ ~l1 \/ ~l2 The basic idea is to sort the watch lists. This information can be used to propagate equivalences during probing (and search). The initial experiments were disappointing. Not using it on the solver. Author: Leonardo de Moura (leonardo) 2011-06-04. Revision History: --*/ #include "sat/sat_iff3_finder.h" #include "sat/sat_solver.h" namespace sat { struct iff3_lt { bool operator()(watched const & w1, watched const & w2) const { // keep th binary clauses in the beginning if (w2.is_binary_clause()) return false; if (w1.is_binary_clause()) return true; // if (w2.is_ternary_clause()) { if (w1.is_ternary_clause()) { literal l1_1 = w1.get_literal1(); literal l1_2 = w1.get_literal2(); literal l2_1 = w2.get_literal1(); literal l2_2 = w2.get_literal2(); if (l1_1.index() < l2_1.index()) return true; if (l1_1.index() > l2_1.index()) return false; return l1_2.index() < l2_2.index(); } return false; } if (w1.is_ternary_clause()) return true; return false; } }; static void unmark(svector & marks, literal_vector & to_unmark) { literal_vector::const_iterator it = to_unmark.begin(); literal_vector::const_iterator end = to_unmark.end(); for (; it != end; ++it) { marks[it->index()] = false; } to_unmark.reset(); } #define SMALL_WLIST 16 /** \brief Return true if wlist contains (l1, l2) It assumes wlist have been sorted using iff3_lt */ static bool contains(watch_list const & wlist, literal l1, literal l2) { watched k(l1, l2); if (wlist.size() < SMALL_WLIST) return wlist.contains(k); iff3_lt lt; int low = 0; int high = wlist.size(); while (true) { int mid = low + ((high - low) / 2); watched const & m = wlist[mid]; if (m == k) return true; if (lt(m, k)) { low = mid + 1; } else { SASSERT(lt(k, m)); high = mid - 1; } if (low > high) return false; } } iff3_finder::iff3_finder(solver & _s): s(_s) { } void iff3_finder::sort_watches() { vector::iterator it = s.m_watches.begin(); vector::iterator end = s.m_watches.end(); for (; it != end; ++it) { watch_list & wlist = *it; std::stable_sort(wlist.begin(), wlist.end(), iff3_lt()); } } void iff3_finder::mk_eq(literal l1, literal l2) { s.mk_clause(l1, ~l2); s.mk_clause(~l1, l2); } void iff3_finder::operator()() { TRACE("iff3_finder", tout << "starting iff3_finder\n";); sort_watches(); unsigned counter = 0; svector found; found.resize(s.num_vars()*2, false); literal_vector to_unmark; typedef std::pair lit_pair; svector pairs; for (bool_var x = 0; x < s.num_vars(); x++) { literal pos_x(x, false); literal neg_x(x, true); watch_list & pos_wlist = s.get_wlist(neg_x); watch_list & neg_wlist = s.get_wlist(pos_x); // TRACE("iff3_finder", tout << "visiting: " << x << "\n"; tout << "pos:\n"; s.display_watch_list(tout, pos_wlist); tout << "\nneg:\n"; s.display_watch_list(tout, neg_wlist); tout << "\n--------------\n";); // traverse the ternary clauses x \/ l1 \/ l2 bool_var curr_v1 = null_bool_var; watch_list::iterator it = pos_wlist.begin(); watch_list::iterator end = pos_wlist.end(); for (; it != end; ++it) { if (it->is_binary_clause()) continue; if (it->is_ternary_clause()) { literal l1 = it->get_literal1(); if (l1.index() < pos_x.index()) break; // stop literal l2 = it->get_literal2(); bool_var v1 = l1.var(); if (v1 != curr_v1) { curr_v1 = v1; unmark(found, to_unmark); pairs.reset(); } if (!l1.sign()) { if (!found[l2.index()]) { found[l2.index()] = true; to_unmark.push_back(l2); } } else { l2.neg(); if (found[l2.index()]) { // Found clauses x \/ v1 \/ l2 and x \/ ~v1 \/ ~l2 // So, I have to find the clauses // ~x \/ v1 \/ ~l2 // ~x \/ ~v1 \/ l2 if (contains(neg_wlist, literal(v1, false), ~l2) && contains(neg_wlist, literal(v1, true), l2)) { // found new iff3 // x = v1 = l2 counter++; // verbose_stream() << counter << ": " << x << " = " << v1 << " = " << l2 << "\n"; TRACE("iff3_finder", tout << counter << ": " << x << " = " << v1 << " = " << l2 << "\n";); l1.neg(); svector::iterator it2 = pairs.begin(); svector::iterator end2 = pairs.end(); for (; it2 != end2; ++it2) { if (it2->first == l1) { // l2 == it2->second mk_eq(l2, it2->second); } else if (it2->second == l1) { // l2 == it2->first mk_eq(l2, it2->first); } else if (it2->first == l2) { // l1 == it2->second mk_eq(l1, it2->second); } else if (it2->second == l2) { // l1 == it2->first mk_eq(l1, it2->first); } } pairs.push_back(lit_pair(l1, l2)); } } } } else { break; // stop, no more ternary clauses from this point } } } } }; z3-z3-4.8.7/src/sat/sat_iff3_finder.h000066400000000000000000000014751356505360400172120ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_iff3_finder.h Abstract: Find constraints of the form x = l1 = l2 That is, search for clauses of the form ~x \/ l1 \/ ~l2 ~x \/ ~l1 \/ l2 x \/ l1 \/ l2 x \/ ~l1 \/ ~l2 The basic idea is to sort the watch lists. This information can be used to propagate equivalences during probing (and search). Author: Leonardo de Moura (leonardo) 2011-06-04. Revision History: --*/ #ifndef SAT_IFF3_FINDER_H_ #define SAT_IFF3_FINDER_H_ #include "sat/sat_types.h" namespace sat { class iff3_finder { solver & s; void sort_watches(); void mk_eq(literal l1, literal l2); public: iff3_finder(solver & s); void operator()(); }; }; #endif z3-z3-4.8.7/src/sat/sat_integrity_checker.cpp000066400000000000000000000206741356505360400210730ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_integrity_checker.cpp Abstract: Checker whether the SAT solver internal datastructures are consistent or not. Author: Leonardo de Moura (leonardo) 2011-05-21. Revision History: --*/ #include "sat/sat_integrity_checker.h" #include "sat/sat_solver.h" #include "util/trace.h" namespace sat { integrity_checker::integrity_checker(solver const & _s): s(_s) { } // for ternary clauses static bool contains_watched(watch_list const & wlist, literal l1, literal l2) { return wlist.contains(watched(l1, l2)); } // for nary clauses static bool contains_watched(watch_list const & wlist, clause const & c, clause_offset cls_off) { for (watched const& w : wlist) { if (w.is_clause()) { if (w.get_clause_offset() == cls_off) { // the blocked literal must be in the clause. VERIFY(c.contains(w.get_blocked_literal())); return true; } } } UNREACHABLE(); return false; } bool integrity_checker::check_clause(clause const & c) const { SASSERT(!c.was_removed()); for (unsigned i = 0; i < c.size(); i++) { VERIFY(c[i].var() <= s.num_vars()); CTRACE("sat_bug", s.was_eliminated(c[i].var()), tout << "l: " << c[i].var() << "\n"; tout << "c: " << c << "\n"; s.display(tout);); VERIFY(!s.was_eliminated(c[i].var())); } SASSERT(c.check_approx()); if (c.frozen()) return true; if (c.size() == 3) { CTRACE("sat_ter_watch_bug", !contains_watched(s.get_wlist(~c[0]), c[1], c[2]), tout << c << "\n"; tout << "watch_list:\n"; s.display_watch_list(tout, s.get_wlist(~c[0])); tout << "\n";); VERIFY(contains_watched(s.get_wlist(~c[0]), c[1], c[2])); VERIFY(contains_watched(s.get_wlist(~c[1]), c[0], c[2])); VERIFY(contains_watched(s.get_wlist(~c[2]), c[0], c[1])); } else { if (s.value(c[0]) == l_false || s.value(c[1]) == l_false) { bool on_prop_stack = false; for (unsigned i = s.m_qhead; i < s.m_trail.size(); i++) { if (s.m_trail[i].var() == c[0].var() || s.m_trail[i].var() == c[1].var()) { on_prop_stack = true; break; } } // the clause has been satisfied or all other literals are assigned to false. if (!on_prop_stack && s.status(c) != l_true) { for (unsigned i = 2; i < c.size(); i++) { CTRACE("sat_bug", s.value(c[i]) != l_false, tout << c << " status: " << s.status(c) << "\n"; for (unsigned i = 0; i < c.size(); i++) tout << "val(" << i << "): " << s.value(c[i]) << "\n";); VERIFY(s.value(c[i]) == l_false); } } } // the first two literals must be watched. VERIFY(contains_watched(s.get_wlist(~c[0]), c, s.get_offset(c))); VERIFY(contains_watched(s.get_wlist(~c[1]), c, s.get_offset(c))); } return true; } bool integrity_checker::check_clauses(clause * const * begin, clause * const * end) const { for (clause * const * it = begin; it != end; ++it) { VERIFY(check_clause(*(*it))); } return true; } bool integrity_checker::check_clauses() const { return check_clauses(s.begin_clauses(), s.end_clauses()); } bool integrity_checker::check_learned_clauses() const { unsigned num_frozen = 0; clause * const * end = s.end_clauses(); for (clause * const * it = s.begin_clauses(); it != end; ++it) { clause & c = *(*it); if (c.frozen()) num_frozen++; } SASSERT(num_frozen == s.m_num_frozen); return check_clauses(s.begin_learned(), s.end_learned()); } bool integrity_checker::check_assignment() const { return true; } bool integrity_checker::check_bool_vars() const { VERIFY(s.m_watches.size() == s.num_vars() * 2); VERIFY(s.m_assignment.size() == s.num_vars() * 2); VERIFY(s.m_lit_mark.size() == s.num_vars() * 2); VERIFY(s.m_justification.size() == s.num_vars()); VERIFY(s.m_decision.size() == s.num_vars()); VERIFY(s.m_eliminated.size() == s.num_vars()); VERIFY(s.m_external.size() == s.num_vars()); VERIFY(s.m_mark.size() == s.num_vars()); VERIFY(s.m_activity.size() == s.num_vars()); VERIFY(s.m_phase.size() == s.num_vars()); VERIFY(s.m_prev_phase.size() == s.num_vars()); VERIFY(s.m_assigned_since_gc.size() == s.num_vars()); for (bool_var v = 0; v < s.num_vars(); v++) { if (s.was_eliminated(v)) { VERIFY(s.get_wlist(literal(v, false)).empty()); VERIFY(s.get_wlist(literal(v, true)).empty()); } } return true; } bool integrity_checker::check_watches(literal l) const { return check_watches(l, s.get_wlist(~l)); } bool integrity_checker::check_watches(literal l, watch_list const& wlist) const { for (watched const& w : wlist) { switch (w.get_kind()) { case watched::BINARY: VERIFY(!s.was_eliminated(w.get_literal().var())); CTRACE("sat_watched_bug", !s.get_wlist(~(w.get_literal())).contains(watched(l, w.is_learned())), tout << "l: " << l << " l2: " << w.get_literal() << "\n"; tout << "was_eliminated1: " << s.was_eliminated(l.var()); tout << " was_eliminated2: " << s.was_eliminated(w.get_literal().var()); tout << " learned: " << w.is_learned() << "\n"; s.display_watch_list(tout, wlist); tout << "\n"; s.display_watch_list(tout, s.get_wlist(~(w.get_literal()))); tout << "\n";); VERIFY(find_binary_watch(s.get_wlist(~(w.get_literal())), l)); break; case watched::TERNARY: VERIFY(!s.was_eliminated(w.get_literal1().var())); VERIFY(!s.was_eliminated(w.get_literal2().var())); VERIFY(w.get_literal1().index() < w.get_literal2().index()); break; case watched::CLAUSE: VERIFY(!s.get_clause(w.get_clause_offset()).was_removed()); break; default: break; } } return true; } bool integrity_checker::check_watches() const { unsigned l_idx = 0; for (watch_list const& wlist : s.m_watches) { literal l = ~to_literal(l_idx++); CTRACE("sat_bug", s.was_eliminated(l.var()) && !wlist.empty(), tout << "l: " << l << "\n"; s.display_watches(tout); s.display(tout);); VERIFY(!s.was_eliminated(l.var()) || wlist.empty()); if (!check_watches(l, wlist)) return false; } return true; } bool integrity_checker::check_reinit_stack() const { for (auto const& c : s.m_clauses_to_reinit) { VERIFY(c.is_binary() || c.get_clause()->on_reinit_stack()); } return true; } bool integrity_checker::check_disjoint_clauses() const { uint_set ids; for (clause* cp : s.m_clauses) { ids.insert(cp->id()); } for (clause* cp : s.m_learned) { if (ids.contains(cp->id())) { TRACE("sat", tout << "Repeated clause: " << cp->id() << "\n";); return false; } } return true; } bool integrity_checker::operator()() const { if (s.inconsistent()) return true; VERIFY(check_clauses()); VERIFY(check_learned_clauses()); VERIFY(check_watches()); VERIFY(check_bool_vars()); VERIFY(check_reinit_stack()); VERIFY(check_disjoint_clauses()); return true; } }; z3-z3-4.8.7/src/sat/sat_integrity_checker.h000066400000000000000000000021211356505360400205230ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_integrity_checker.h Abstract: Checker whether the SAT solver internal datastructures are consistent or not. Author: Leonardo de Moura (leonardo) 2011-05-21. Revision History: --*/ #ifndef SAT_INTEGRITY_CHECKER_H_ #define SAT_INTEGRITY_CHECKER_H_ #include "sat/sat_types.h" #include "sat/sat_watched.h" namespace sat { class integrity_checker { solver const & s; public: integrity_checker(solver const & s); bool check_clause(clause const & c) const; bool check_clauses(clause * const * begin, clause * const * end) const; bool check_clauses() const; bool check_learned_clauses() const; bool check_assignment() const; bool check_bool_vars() const; bool check_watches() const; bool check_watches(literal l, watch_list const& wlist) const; bool check_watches(literal l) const; bool check_reinit_stack() const; bool check_disjoint_clauses() const; bool operator()() const; }; }; #endif z3-z3-4.8.7/src/sat/sat_justification.h000066400000000000000000000055601356505360400177060ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_justification.h Abstract: Justifications for SAT assignments Author: Leonardo de Moura (leonardo) 2011-05-21. Revision History: --*/ #ifndef SAT_JUSTIFICATIONS_H_ #define SAT_JUSTIFICATIONS_H_ namespace sat { class justification { public: enum kind { NONE, BINARY, TERNARY, CLAUSE, EXT_JUSTIFICATION }; private: unsigned m_level; size_t m_val1; unsigned m_val2; justification(unsigned lvl, ext_justification_idx idx, kind k):m_level(lvl), m_val1(idx), m_val2(k) {} unsigned val1() const { return static_cast(m_val1); } public: justification(unsigned lvl):m_level(lvl), m_val1(0), m_val2(NONE) {} explicit justification(unsigned lvl, literal l):m_level(lvl), m_val1(l.to_uint()), m_val2(BINARY) {} justification(unsigned lvl, literal l1, literal l2):m_level(lvl), m_val1(l1.to_uint()), m_val2(TERNARY + (l2.to_uint() << 3)) {} explicit justification(unsigned lvl, clause_offset cls_off):m_level(lvl), m_val1(cls_off), m_val2(CLAUSE) {} static justification mk_ext_justification(unsigned lvl, ext_justification_idx idx) { return justification(lvl, idx, EXT_JUSTIFICATION); } unsigned level() const { return m_level; } kind get_kind() const { return static_cast(m_val2 & 7); } bool is_none() const { return m_val2 == NONE; } bool is_binary_clause() const { return m_val2 == BINARY; } literal get_literal() const { SASSERT(is_binary_clause()); return to_literal(val1()); } bool is_ternary_clause() const { return get_kind() == TERNARY; } literal get_literal1() const { SASSERT(is_ternary_clause()); return to_literal(val1()); } literal get_literal2() const { SASSERT(is_ternary_clause()); return to_literal(m_val2 >> 3); } bool is_clause() const { return m_val2 == CLAUSE; } clause_offset get_clause_offset() const { return m_val1; } bool is_ext_justification() const { return m_val2 == EXT_JUSTIFICATION; } ext_justification_idx get_ext_justification_idx() const { return m_val1; } }; inline std::ostream & operator<<(std::ostream & out, justification const & j) { switch (j.get_kind()) { case justification::NONE: out << "none"; break; case justification::BINARY: out << "binary " << j.get_literal(); break; case justification::TERNARY: out << "ternary " << j.get_literal1() << " " << j.get_literal2(); break; case justification::CLAUSE: out << "clause"; break; case justification::EXT_JUSTIFICATION: out << "external"; break; } out << " @" << j.level(); return out; } }; #endif z3-z3-4.8.7/src/sat/sat_local_search.cpp000066400000000000000000000740771356505360400200160ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: sat_local_search.cpp Abstract: Local search module for cardinality clauses. Author: Sixue Liu 2017-2-21 Notes: --*/ #include "sat/sat_local_search.h" #include "sat/sat_solver.h" #include "sat/ba_solver.h" #include "sat/sat_params.hpp" #include "util/timer.h" 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); } // add sentinel variable. m_vars.push_back(var_info()); if (m_config.phase_sticky()) { for (var_info& vi : m_vars) if (!vi.m_unit) vi.m_value = vi.m_bias > 50; } else { for (var_info& vi : m_vars) if (!vi.m_unit) vi.m_value = (0 == (m_rand() % 2)); } m_index_in_unsat_stack.resize(num_constraints(), 0); set_parameters(); } void local_search::init_cur_solution() { for (var_info& vi : m_vars) { if (!vi.m_unit) { if (m_config.phase_sticky()) { vi.m_value = ((unsigned)(m_rand() % 100) < vi.m_bias); } else { vi.m_value = (m_rand() % 2) == 0; } } } } // figure out slack, and init unsat stack void local_search::init_slack() { for (unsigned v = 0; v < num_vars(); ++v) { bool is_true = cur_solution(v); coeff_vector& truep = m_vars[v].m_watch[is_true]; for (auto const& coeff : truep) { unsigned c = coeff.m_constraint_id; constraint& cn = m_constraints[c]; cn.m_slack -= coeff.m_coeff; } } for (unsigned c = 0; c < num_constraints(); ++c) { // violate the at-most-k constraint if (m_constraints[c].m_slack < 0) unsat(c); } } // figure out variables scores and slack_scores void local_search::init_scores() { for (unsigned v = 0; v < num_vars(); ++v) { bool is_true = cur_solution(v); coeff_vector& truep = m_vars[v].m_watch[is_true]; coeff_vector& falsep = m_vars[v].m_watch[!is_true]; for (auto const& coeff : falsep) { constraint& c = m_constraints[coeff.m_constraint_id]; // will --slack if (c.m_slack <= 0) { dec_slack_score(v); if (c.m_slack == 0) dec_score(v); } } for (auto const& coeff : truep) { constraint& c = m_constraints[coeff.m_constraint_id]; // will --true_terms_count[c] // will ++slack if (c.m_slack <= -1) { inc_slack_score(v); if (c.m_slack == -1) inc_score(v); } } } } // init goodvars void local_search::init_goodvars() { m_goodvar_stack.reset(); for (unsigned v = 0; v < num_vars(); ++v) { if (score(v) > 0) { // && conf_change[v] == true m_vars[v].m_in_goodvar_stack = true; m_goodvar_stack.push_back(v); } } } void local_search::reinit() { // // 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) { c.m_slack = c.m_k; } // init unsat stack m_is_unsat = false; m_unsat_stack.reset(); // init solution using the bias init_cur_solution(); // init variable information // the last variable is the virtual variable 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_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_conf_change = true; m_vars[i].m_in_goodvar_stack = false; m_vars[i].m_score = 0; m_vars[i].m_slack_score = 0; } init_slack(); init_scores(); init_goodvars(); set_best_unsat(); for (bool_var v : m_units) { propagate(literal(v, !cur_solution(v))); if (m_is_unsat) break; } if (m_is_unsat) { IF_VERBOSE(0, verbose_stream() << "unsat during reinit\n"); } DEBUG_CODE(verify_slack();); } bool local_search::propagate(literal lit) { bool unit = is_unit(lit); VERIFY(is_true(lit)); m_prop_queue.reset(); add_propagation(lit); for (unsigned i = 0; i < m_prop_queue.size() && i < m_vars.size(); ++i) { literal lit2 = m_prop_queue[i]; if (!is_true(lit2)) { if (is_unit(lit2)) { return false; } flip_walksat(lit2.var()); add_propagation(lit2); } } if (m_prop_queue.size() >= m_vars.size()) { IF_VERBOSE(0, verbose_stream() << "propagation loop\n"); return false; } if (unit) { for (literal lit2 : m_prop_queue) { VERIFY(is_true(lit2)); add_unit(lit2, lit); } } return true; } void local_search::add_propagation(literal l) { VERIFY(is_true(l)); for (literal lit : m_vars[l.var()].m_bin[l.sign()]) { if (!is_true(lit)) { m_prop_queue.push_back(lit); } } } void local_search::set_best_unsat() { m_best_unsat = m_unsat_stack.size(); m_best_phase.reserve(m_vars.size()); for (unsigned i = m_vars.size(); i-- > 0; ) { m_best_phase[i] = m_vars[i].m_value; } } void local_search::verify_solution() const { IF_VERBOSE(0, verbose_stream() << "verifying solution\n"); for (constraint const& c : m_constraints) verify_constraint(c); } 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; } UNREACHABLE(); 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; for (literal t : c) { if (is_true(t)) { value += constraint_coeff(c, t); } } return value; } void local_search::add_clause(unsigned sz, literal const* c) { add_cardinality(sz, c, sz - 1); } // ~c <= k void local_search::add_cardinality(unsigned sz, literal const* c, unsigned k) { if (sz == 1 && k == 0) { add_unit(c[0], null_literal); return; } if (k == 1 && sz == 2) { // IF_VERBOSE(0, verbose_stream() << "bin: " << ~c[0] << " + " << ~c[1] << " <= 1\n"); for (unsigned i = 0; i < 2; ++i) { literal t(c[i]), s(c[1-i]); m_vars.reserve(t.var() + 1); m_vars[t.var()].m_bin[is_pos(t)].push_back(s); } } unsigned id = m_constraints.size(); m_constraints.push_back(constraint(k, id)); for (unsigned i = 0; i < sz; ++i) { m_vars.reserve(c[i].var() + 1); literal t(~c[i]); m_vars[t.var()].m_watch[is_pos(t)].push_back(pbcoeff(id, 1)); m_constraints.back().push(t); } } // 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], null_literal); return; } unsigned id = m_constraints.size(); m_constraints.push_back(constraint(k, id)); for (unsigned i = 0; i < sz; ++i) { m_vars.reserve(c[i].var() + 1); literal t(c[i]); m_vars[t.var()].m_watch[is_pos(t)].push_back(pbcoeff(id, coeffs[i])); m_constraints.back().push(t); } } void local_search::add_unit(literal lit, literal exp) { bool_var v = lit.var(); if (is_unit(lit)) return; 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); DEBUG_CODE(verify_unsat_stack();); } local_search::local_search() : m_is_unsat(false), m_par(nullptr) { } void local_search::reinit(solver& s) { import(s, true); if (s.m_best_phase_size > 0) { for (unsigned i = num_vars(); i-- > 0; ) { set_phase(i, s.m_best_phase[i]); } } } void local_search::import(solver const& 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) { vi.m_bias = s.m_phase[v++] ? 98 : 2; } } // copy units unsigned trail_sz = s.init_trail_size(); for (unsigned i = 0; i < trail_sz; ++i) { add_clause(1, s.m_trail.c_ptr() + i); } // copy binary clauses { unsigned sz = s.m_watches.size(); for (unsigned l_idx = 0; l_idx < sz; ++l_idx) { literal l1 = ~to_literal(l_idx); watch_list const & wlist = s.m_watches[l_idx]; for (watched const& w : wlist) { if (!w.is_binary_non_learned_clause()) continue; literal l2 = w.get_literal(); if (l1.index() > l2.index()) continue; literal ls[2] = { l1, l2 }; add_clause(2, ls); } } } // copy clauses for (clause* c : s.m_clauses) { add_clause(c->size(), c->begin()); } m_num_non_binary_clauses = s.m_clauses.size(); // copy cardinality clauses ba_solver* ext = dynamic_cast(s.get_extension()); if (ext) { unsigned_vector coeffs; literal_vector lits; for (ba_solver::constraint* cp : ext->m_constraints) { switch (cp->tag()) { case ba_solver::card_t: { ba_solver::card const& c = cp->to_card(); unsigned n = c.size(); unsigned k = c.k(); if (c.lit() == null_literal) { // c.lits() >= k // <=> // ~c.lits() <= n - k lits.reset(); for (unsigned j = 0; j < n; ++j) lits.push_back(c[j]); add_cardinality(lits.size(), lits.c_ptr(), n - k); } else { // // c.lit() <=> c.lits() >= k // // (c.lits() < k) or c.lit() // = (c.lits() + (n - k + 1)*~c.lit()) <= n // // ~c.lit() or (c.lits() >= k) // = ~c.lit() or (~c.lits() <= n - k) // = k*c.lit() + ~c.lits() <= n // m_is_pb = true; lits.reset(); coeffs.reset(); for (literal l : c) lits.push_back(l), coeffs.push_back(1); lits.push_back(~c.lit()); coeffs.push_back(n - k + 1); add_pb(lits.size(), lits.c_ptr(), coeffs.c_ptr(), n); lits.reset(); coeffs.reset(); for (literal l : c) lits.push_back(~l), coeffs.push_back(1); lits.push_back(c.lit()); coeffs.push_back(k); add_pb(lits.size(), lits.c_ptr(), coeffs.c_ptr(), n); } break; } case ba_solver::pb_t: { ba_solver::pb const& p = cp->to_pb(); lits.reset(); coeffs.reset(); m_is_pb = true; unsigned sum = 0; for (ba_solver::wliteral wl : p) sum += wl.first; if (p.lit() == null_literal) { // w1 + .. + w_n >= k // <=> // ~wl + ... + ~w_n <= sum_of_weights - k for (ba_solver::wliteral wl : p) lits.push_back(~(wl.second)), coeffs.push_back(wl.first); add_pb(lits.size(), lits.c_ptr(), coeffs.c_ptr(), sum - p.k()); } else { // lit <=> w1 + .. + w_n >= k // <=> // lit or w1 + .. + w_n <= k - 1 // ~lit or w1 + .. + w_n >= k // <=> // (sum - k + 1)*~lit + w1 + .. + w_n <= sum // k*lit + ~wl + ... + ~w_n <= sum lits.push_back(p.lit()), coeffs.push_back(p.k()); for (ba_solver::wliteral wl : p) lits.push_back(~(wl.second)), coeffs.push_back(wl.first); add_pb(lits.size(), lits.c_ptr(), coeffs.c_ptr(), sum); lits.reset(); coeffs.reset(); lits.push_back(~p.lit()), coeffs.push_back(sum + 1 - p.k()); for (ba_solver::wliteral wl : p) lits.push_back(wl.second), coeffs.push_back(wl.first); add_pb(lits.size(), lits.c_ptr(), coeffs.c_ptr(), sum); } break; } case ba_solver::xr_t: NOT_IMPLEMENTED_YET(); break; } } } if (_init) { init(); } } local_search::~local_search() { } lbool local_search::check() { return check(0, nullptr, nullptr); } #define PROGRESS(tries, flips) \ if (tries % 10 == 0 || m_unsat_stack.empty()) { \ IF_VERBOSE(1, verbose_stream() << "(sat.local-search" \ << " :flips " << flips \ << " :noise " << m_noise \ << " :unsat " << /*m_unsat_stack.size()*/ m_best_unsat \ << " :constraints " << m_constraints.size() \ << " :time " << (timer.get_seconds() < 0.001 ? 0.0 : timer.get_seconds()) << ")\n";); \ } void local_search::walksat() { m_best_unsat_rate = 1; m_last_best_unsat_rate = 1; reinit(); DEBUG_CODE(verify_slack();); timer timer; unsigned step = 0, total_flips = 0, tries = 0; for (tries = 1; !m_unsat_stack.empty() && m_limit.inc(); ++tries) { ++m_stats.m_num_restarts; for (step = 0; step < m_max_steps && !m_unsat_stack.empty(); ++step) { pick_flip_walksat(); if (m_unsat_stack.size() < m_best_unsat) { set_best_unsat(); m_last_best_unsat_rate = m_best_unsat_rate; m_best_unsat_rate = (double)m_unsat_stack.size() / num_constraints(); } if (m_is_unsat) return; } total_flips += step; PROGRESS(tries, total_flips); if (m_par) { double max_avg = 0; for (unsigned v = 0; v < num_vars(); ++v) { max_avg = std::max(max_avg, (double)m_vars[v].m_slow_break); } double sum = 0; for (unsigned v = 0; v < num_vars(); ++v) { sum += exp(m_config.itau() * (m_vars[v].m_slow_break - max_avg)); } if (sum == 0) { sum = 0.01; } for (unsigned v = 0; v < num_vars(); ++v) { m_vars[v].m_break_prob = exp(m_config.itau() * (m_vars[v].m_slow_break - max_avg)) / sum; } m_par->to_solver(*this); } if (m_par && m_par->from_solver(*this)) { reinit(); } if (tries % 10 == 0 && !m_unsat_stack.empty()) { reinit(); } } PROGRESS(0, total_flips); } lbool local_search::check(unsigned sz, literal const* assumptions, parallel* p) { flet _p(m_par, p); m_model.reset(); m_assumptions.reset(); m_assumptions.append(sz, assumptions); unsigned num_units = m_units.size(); init(); walksat(); // remove unit clauses for (unsigned i = m_units.size(); i-- > num_units; ) { m_vars[m_units[i]].m_unit = false; } 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; } else if (m_unsat_stack.empty()) { verify_solution(); extract_model(); result = l_true; } else { result = l_undef; } IF_VERBOSE(1, verbose_stream() << "(sat.local-search " << result << ")\n";); IF_VERBOSE(20, display(verbose_stream());); return result; } void local_search::sat(unsigned c) { unsigned last_unsat_constraint = m_unsat_stack.back(); int index = m_index_in_unsat_stack[c]; m_unsat_stack[index] = last_unsat_constraint; m_index_in_unsat_stack[last_unsat_constraint] = index; m_unsat_stack.pop_back(); } // swap the deleted one with the last one and pop void local_search::unsat(unsigned c) { m_index_in_unsat_stack[c] = m_unsat_stack.size(); 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() % num_unsat]]; unsigned reflipped = 0; bool is_core = m_unsat_stack.size() <= 10; if (m_rand() % 10000 <= m_noise) { // take this branch with 98% probability. // find the first one, to fast break the rest unsigned best_bsb = 0; literal_vector::const_iterator cit = c.m_literals.begin(), cend = c.m_literals.end(); literal l; for (; (cit != cend) && (!is_true(*cit) || is_unit(*cit)); ++cit) { } if (cit == cend) { if (c.m_k < constraint_value(c)) { IF_VERBOSE(0, display(verbose_stream() << "unsat clause\n", c)); m_is_unsat = true; return; } goto reflip; } l = *cit; best_var = v = l.var(); bool tt = cur_solution(v); coeff_vector const& falsep = m_vars[v].m_watch[!tt]; for (pbcoeff const& pbc : falsep) { int slack = constraint_slack(pbc.m_constraint_id); if (slack < 0) ++best_bsb; else if (slack < static_cast(pbc.m_coeff)) best_bsb += num_unsat; } ++cit; for (; cit != cend; ++cit) { l = *cit; if (is_true(l) && !is_unit(l)) { v = l.var(); unsigned bsb = 0; coeff_vector const& falsep = m_vars[v].m_watch[!cur_solution(v)]; auto it = falsep.begin(), end = falsep.end(); for (; it != end; ++it) { int slack = constraint_slack(it->m_constraint_id); if (slack < 0) { if (bsb == best_bsb) { break; } else { ++bsb; } } else if (slack < static_cast(it->m_coeff)) { bsb += num_unsat; if (bsb > best_bsb) { break; } } } if (it == end) { if (bsb < best_bsb) { best_bsb = bsb; best_var = v; n = 1; } else {// if (bsb == best_bb) ++n; if (m_rand() % n == 0) { best_var = v; } } } } } } else { for (literal l : c) { if (is_true(l) && !is_unit(l)) { if (m_rand() % n == 0) { best_var = l.var(); } ++n; } } } if (best_var == null_bool_var) { IF_VERBOSE(1, verbose_stream() << "(sat.local_search :unsat)\n"); return; } if (is_unit(best_var)) { goto reflip; } flip_walksat(best_var); literal lit(best_var, !cur_solution(best_var)); if (!propagate(lit)) { if (is_true(lit)) { flip_walksat(best_var); } add_unit(~lit, null_literal); if (!propagate(~lit)) { IF_VERBOSE(0, verbose_stream() << "unsat\n"); m_is_unsat = true; return; } goto reflip; } if (false && is_core && c.m_k < constraint_value(c)) { ++reflipped; goto reflip; } } 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); m_vars[flipvar].m_flips++; m_vars[flipvar].m_slow_break.update(abs(m_vars[flipvar].m_slack_score)); bool flip_is_true = cur_solution(flipvar); coeff_vector const& truep = m_vars[flipvar].m_watch[flip_is_true]; coeff_vector const& falsep = m_vars[flipvar].m_watch[!flip_is_true]; for (auto const& pbc : truep) { unsigned ci = pbc.m_constraint_id; 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); } } for (auto const& pbc : falsep) { unsigned ci = pbc.m_constraint_id; 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); } } 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(); 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'; tout << "max_steps:\t" << m_max_steps << '\n'; ); } void local_search::print_info(std::ostream& out) { for (unsigned v = 0; v < num_vars(); ++v) { out << "v" << v << "\t" << m_vars[v].m_neighbors.size() << '\t' << cur_solution(v) << '\t' << conf_change(v) << '\t' << score(v) << '\t' << slack_score(v) << '\n'; } } void local_search::extract_model() { m_model.reset(); for (unsigned v = 0; v < num_vars(); ++v) { m_model.push_back(cur_solution(v) ? l_true : l_false); } } std::ostream& local_search::display(std::ostream& out) const { for (constraint const& c : m_constraints) { display(out, c); } for (bool_var v = 0; v < num_vars(); ++v) { display(out, v, m_vars[v]); } return out; } std::ostream& local_search::display(std::ostream& out, constraint const& c) const { for (literal l : c) { unsigned coeff = constraint_coeff(c, l); if (coeff > 1) out << coeff << " * "; out << l << " "; } return out << " <= " << c.m_k << " lhs value: " << constraint_value(c) << "\n"; } std::ostream& local_search::display(std::ostream& out, unsigned v, var_info const& vi) const { 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 { if (m_config.dbg_flips()) { unsigned i = 0; for (var_info const& vi : m_vars) { IF_VERBOSE(0, verbose_stream() << "flips: " << i << " " << vi.m_flips << " " << vi.m_slow_break << "\n"); ++i; } } st.update("local-search-flips", m_stats.m_num_flips); st.update("local-search-restarts", m_stats.m_num_restarts); } void local_search::set_phase(bool_var v, bool f) { unsigned& bias = m_vars[v].m_bias; if (f && bias < 100) bias++; if (!f && bias > 0) bias--; } 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; } } } z3-z3-4.8.7/src/sat/sat_local_search.h000066400000000000000000000244271356505360400174550ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: sat_local_search.h Abstract: Local search module for cardinality clauses. Author: Sixue Liu 2017-2-21 Notes: --*/ #ifndef _SAT_LOCAL_SEARCH_H_ #define _SAT_LOCAL_SEARCH_H_ #include "util/vector.h" #include "sat/sat_types.h" #include "sat/sat_config.h" #include "util/rlimit.h" #include "util/ema.h" #include "util/statistics.h" namespace sat { class parallel; class local_search_config { unsigned m_random_seed; int m_best_known_value; local_search_mode m_mode; bool m_phase_sticky; bool m_dbg_flips; double m_itau; 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; m_best_known_value = INT_MAX; m_mode = local_search_mode::wsat; m_phase_sticky = false; m_dbg_flips = false; m_itau = 0.5; } unsigned random_seed() const { return m_random_seed; } unsigned best_known_value() const { return m_best_known_value; } local_search_mode mode() const { return m_mode; } bool phase_sticky() const { return m_phase_sticky; } bool dbg_flips() const { return m_dbg_flips; } double itau() const { return m_itau; } void set_random_seed(unsigned s) { m_random_seed = s; } void set_best_known_value(unsigned v) { m_best_known_value = v; } }; class local_search : public i_local_search { struct pbcoeff { unsigned m_constraint_id; unsigned m_coeff; pbcoeff(unsigned id, unsigned coeff): m_constraint_id(id), m_coeff(coeff) {} }; typedef svector bool_vector; typedef svector coeff_vector; struct stats { unsigned m_num_flips; unsigned m_num_restarts; void reset() { memset(this, 0, sizeof(*this)); } stats() { reset(); } }; struct var_info { bool m_value; // current solution 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 bool_var_vector m_neighbors; // neighborhood variables coeff_vector m_watch[2]; literal_vector m_bin[2]; unsigned m_flips; ema m_slow_break; double m_break_prob; var_info(): m_value(true), m_bias(50), m_unit(false), m_conf_change(true), m_in_goodvar_stack(false), m_score(0), m_slack_score(0), m_flips(0), m_slow_break(1e-5), m_break_prob(0) {} }; struct constraint { unsigned m_id; unsigned m_k; int m_slack; unsigned m_size; literal_vector m_literals; constraint(unsigned k, unsigned id) : m_id(id), m_k(k), m_slack(0), m_size(0) {} void push(literal l) { m_literals.push_back(l); ++m_size; } unsigned size() const { return m_size; } literal const& operator[](unsigned idx) const { return m_literals[idx]; } literal const* begin() const { return m_literals.begin(); } literal const* end() const { return m_literals.end(); } }; stats m_stats; local_search_config m_config; vector m_vars; // variables svector m_best_phase; // best value in round 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; 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++; } inline void dec_score(bool_var v) { m_vars[v].m_score--; } inline int slack_score(bool_var v) const { return m_vars[v].m_slack_score; } inline void inc_slack_score(bool_var v) { m_vars[v].m_slack_score++; } inline void dec_slack_score(bool_var v) { m_vars[v].m_slack_score--; } 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 void set_best_unsat(); /* TBD: other scores */ inline bool is_pos(literal t) const { return !t.sign(); } inline bool is_true(bool_var v) const { return cur_solution(v); } inline bool is_true(literal l) const { return cur_solution(l.var()) != l.sign(); } inline bool is_false(literal l) const { return cur_solution(l.var()) == l.sign(); } inline bool is_unit(bool_var v) const { return m_vars[v].m_unit; } inline bool is_unit(literal l) const { return m_vars[l.var()].m_unit; } 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; } void init(); void reinit(); void reinit_orig(); void init_cur_solution(); void init_slack(); void init_scores(); 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 unsat(unsigned c); void sat(unsigned c); void set_parameters(); 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(); void add_clause(unsigned sz, literal const* c); 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; lbool check(); unsigned num_vars() const { return m_vars.size() - 1; } // var index from 1 to num_vars public: local_search(); ~local_search() override; reslimit& rlimit() override { return m_limit; } lbool check(unsigned sz, literal const* assumptions, parallel* p) override; unsigned num_non_binary_clauses() const override { return m_num_non_binary_clauses; } void add(solver const& s) override { import(s, false); } model const& get_model() const override { return m_model; } void collect_statistics(statistics& st) const override; void updt_params(params_ref const& p) override {} void set_seed(unsigned n) override { config().set_random_seed(n); } void reinit(solver& s) override; // used by unit-walk void set_phase(bool_var v, bool f); void set_bias(bool_var v, lbool f); bool get_best_phase(bool_var v) const { return m_best_phase[v]; } inline bool cur_solution(bool_var v) const { return m_vars[v].m_value; } double get_priority(bool_var v) const override { return m_vars[v].m_break_prob; } void import(solver const& s, bool init); void add_cardinality(unsigned sz, literal const* c, unsigned k); void add_pb(unsigned sz, literal const* c, unsigned const* coeffs, unsigned k); local_search_config& config() { return m_config; } }; } #endif z3-z3-4.8.7/src/sat/sat_lookahead.cpp000066400000000000000000002707341356505360400173240ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: sat_lookahead.h Abstract: Lookahead SAT solver in the style of March. Thanks also to the presentation in sat11.w. Author: Nikolaj Bjorner (nbjorner) 2017-2-11 Notes: --*/ #include #include "sat/sat_solver.h" #include "sat/sat_extension.h" #include "sat/sat_lookahead.h" #include "sat/sat_scc.h" #include "util/union_find.h" namespace sat { lookahead::scoped_ext::scoped_ext(lookahead& p): p(p) { if (p.m_s.m_ext) p.m_s.m_ext->set_lookahead(&p); } lookahead::scoped_ext::~scoped_ext() { if (p.m_s.m_ext) p.m_s.m_ext->set_lookahead(nullptr); } lookahead::scoped_assumptions::scoped_assumptions(lookahead& p, literal_vector const& lits): p(p), lits(lits) { for (auto l : lits) { p.push(l, p.c_fixed_truth); } } lookahead::scoped_assumptions::~scoped_assumptions() { for (auto l : lits) { (void)l; p.pop(); } } void lookahead::flip_prefix() { if (m_trail_lim.size() < 64) { uint64_t mask = (1ull << m_trail_lim.size()); m_prefix = mask | (m_prefix & (mask - 1)); } } void lookahead::prune_prefix() { if (m_trail_lim.size() < 64) { m_prefix &= (1ull << m_trail_lim.size()) - 1; } } void lookahead::update_prefix(literal l) { bool_var x = l.var(); unsigned p = m_vprefix[x].m_prefix; unsigned pl = m_vprefix[x].m_length; unsigned mask = (1 << std::min(31u, pl)) - 1; if (pl >= m_trail_lim.size() || (p & mask) != (m_prefix & mask)) { m_vprefix[x].m_length = m_trail_lim.size(); m_vprefix[x].m_prefix = static_cast(m_prefix); } } bool lookahead::active_prefix(bool_var x) { unsigned lvl = m_trail_lim.size(); unsigned p = m_vprefix[x].m_prefix; unsigned l = m_vprefix[x].m_length; if (l > lvl) return false; if (l == lvl || l >= 31) return m_prefix == p; unsigned mask = ((1 << std::min(l,31u)) - 1); return (m_prefix & mask) == (p & mask); } void lookahead::add_binary(literal l1, literal l2) { TRACE("sat", tout << "binary: " << l1 << " " << l2 << "\n";); SASSERT(l1 != l2); // don't add tautologies and don't add already added binaries if (~l1 == l2) return; if (!m_binary[(~l1).index()].empty() && m_binary[(~l1).index()].back() == l2) return; m_binary[(~l1).index()].push_back(l2); 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 && m_search_mode == lookahead_mode::searching) validate_binary(l1, l2); } void lookahead::del_binary(unsigned idx) { // TRACE("sat", display(tout << "Delete " << to_literal(idx) << "\n");); literal_vector & lits = m_binary[idx]; SASSERT(!lits.empty()); literal l = lits.back(); lits.pop_back(); SASSERT(!m_binary[(~l).index()].empty()); SASSERT(m_binary[(~l).index()].back() == ~to_literal(idx)); m_binary[(~l).index()].pop_back(); ++m_stats.m_del_binary; } void lookahead::validate_binary(literal l1, literal l2) { 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() { ++m_bstamp_id; if (m_bstamp_id == 0) { ++m_bstamp_id; m_bstamp.fill(0); } } void lookahead::inc_istamp() { ++m_istamp_id; if (m_istamp_id == 0) { ++m_istamp_id; for (unsigned i = 0; i < m_lits.size(); ++i) { m_lits[i].m_double_lookahead = 0; } } } void lookahead::set_bstamps(literal l) { inc_bstamp(); set_bstamp(l); literal_vector const& conseq = m_binary[l.index()]; for (literal l : conseq) { set_bstamp(l); } } /** \brief add one-step transitive closure of binary implications return false if we learn a unit literal. \pre all implicants of ~u are stamped. u \/ v is true **/ bool lookahead::add_tc1(literal u, literal v) { unsigned sz = m_binary[v.index()].size(); for (unsigned i = 0; i < sz; ++i) { literal w = m_binary[v.index()][i]; // ~v \/ w if (!is_fixed(w)) { if (is_stamped(~w)) { // u \/ v, ~v \/ w, u \/ ~w => u is unit TRACE("sat", tout << "tc1: " << u << "\n";); propagated(u); return false; } if (m_num_tc1 < m_config.m_tc1_limit) { ++m_num_tc1; IF_VERBOSE(30, verbose_stream() << "tc1: " << u << " " << w << "\n";); add_binary(u, w); } } } return true; } /** \brief main routine for adding a new binary clause dynamically. */ void lookahead::try_add_binary(literal u, literal v) { SASSERT(m_search_mode == lookahead_mode::searching); SASSERT(u.var() != v.var()); if (!is_undef(u) || !is_undef(v)) { IF_VERBOSE(0, verbose_stream() << "adding assigned binary " << v << " " << u << "\n";); } set_bstamps(~u); if (is_stamped(~v)) { TRACE("sat", tout << "try_add_binary: " << u << "\n";); propagated(u); // u \/ ~v, u \/ v => u is a unit literal } else if (!is_stamped(v) && add_tc1(u, v)) { // u \/ v is not in index set_bstamps(~v); if (is_stamped(~u)) { TRACE("sat", tout << "try_add_binary: " << v << "\n";); propagated(v); // v \/ ~u, u \/ v => v is a unit literal } else if (add_tc1(v, u)) { update_prefix(u); update_prefix(v); add_binary(u, v); } } } // ------------------------------------- // pre-selection // see also 91 - 102 sat11.w void lookahead::pre_select() { IF_VERBOSE(10, verbose_stream() << "(sat-lookahead :freevars " << m_freevars.size() << ")\n";); m_lookahead.reset(); for (bool_var x : m_freevars) { // tree lookahead leaves literals fixed in lower truth levels literal l(x, false); set_undef(l); set_undef(~l); } if (select(scope_lvl())) { get_scc(); if (inconsistent()) return; find_heights(); construct_lookahead_table(); } } bool lookahead::select(unsigned level) { init_pre_selection(level); unsigned level_cand = std::max(m_config.m_level_cand, m_freevars.size() / 50); unsigned max_num_cand = (level > 0 && m_config.m_preselect) ? level_cand / level : m_freevars.size(); max_num_cand = std::max(m_config.m_min_cutoff, max_num_cand); double sum = 0; for (bool newbies = false; ; newbies = true) { sum = init_candidates(level, newbies); if (!m_candidates.empty()) break; if (is_sat()) { return false; } } SASSERT(!m_candidates.empty()); // cut number of candidates down to max_num_cand. // step 1. cut it to at most 2*max_num_cand. // step 2. use a heap to sift through the rest. bool progress = true; while (progress && m_candidates.size() >= max_num_cand * 2) { progress = false; double mean = sum / (double)(m_candidates.size() + 0.0001); sum = 0; for (unsigned i = 0; i < m_candidates.size() && m_candidates.size() >= max_num_cand * 2; ++i) { if (m_candidates[i].m_rating >= mean) { sum += m_candidates[i].m_rating; } else { m_candidates[i] = m_candidates.back(); m_candidates.pop_back(); --i; progress = true; } } } TRACE("sat", display_candidates(tout);); SASSERT(!m_candidates.empty()); heap_sort(); while (m_candidates.size() > max_num_cand) { m_candidates.pop_back(); } SASSERT(!m_candidates.empty() && m_candidates.size() <= max_num_cand); TRACE("sat", display_candidates(tout);); return true; } void lookahead::heap_sort() { if (m_candidates.size() > 1) { heapify(); for (unsigned i = m_candidates.size() - 1; i > 0; --i) { candidate c = m_candidates[i]; m_candidates[i] = m_candidates[0]; m_candidates[0] = c; sift_down(0, i); } } SASSERT(validate_heap_sort()); } void lookahead::heapify() { unsigned i = 1 + (m_candidates.size() - 2) / 2; while(i > 0) { sift_down(--i, m_candidates.size()); } } void lookahead::sift_down(unsigned j, unsigned sz) { unsigned i = j; candidate c = m_candidates[j]; for (unsigned k = 2 * j + 1; k < sz; i = k, k = 2 * k + 1) { // pick smallest child if (k + 1 < sz && m_candidates[k].m_rating > m_candidates[k + 1].m_rating) { ++k; } if (c.m_rating <= m_candidates[k].m_rating) break; m_candidates[i] = m_candidates[k]; } if (i > j) m_candidates[i] = c; } /** * \brief validate that the result of heap sort sorts the candidates * in descending order of their rating. */ bool lookahead::validate_heap_sort() { for (unsigned i = 0; i + 1 < m_candidates.size(); ++i) if (m_candidates[i].m_rating < m_candidates[i + 1].m_rating) return false; return true; } double lookahead::init_candidates(unsigned level, bool newbies) { m_candidates.reset(); double sum = 0; unsigned skip_candidates = 0; bool autarky = get_config().m_lookahead_global_autarky; 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])); sum += m_rating[x]; } else { skip_candidates++; } } } } 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) { IF_VERBOSE(1, verbose_stream() << "(sat-lookahead :candidates " << m_candidates.size() << " :skipped " << skip_candidates << ")\n";); } return sum; } std::ostream& lookahead::display_candidates(std::ostream& out) const { for (unsigned i = 0; i < m_candidates.size(); ++i) { out << "var: " << m_candidates[i].m_var << " rating: " << m_candidates[i].m_rating << "\n"; } return out; } bool lookahead::is_unsat() const { for (unsigned idx = 0; idx < m_binary.size(); ++idx) { literal l = to_literal(idx); for (literal lit : m_binary[idx]) { if (is_true(l) && is_false(lit)) return true; } } // check if there is a clause whose literals are false. // every clause is terminated by a null-literal. for (nary* n : m_nary_clauses) { bool all_false = true; for (literal l : *n) { all_false &= is_false(l); } if (all_false) return true; } // check if there is a ternary whose literals are false. for (unsigned idx = 0; idx < m_ternary.size(); ++idx) { literal lit = to_literal(idx); if (is_false(lit)) { unsigned sz = m_ternary_count[lit.index()]; for (binary const& b : m_ternary[lit.index()]) { if (sz-- == 0) break; if (is_false(b.m_u) && is_false(b.m_v)) return true; } } } return false; } bool lookahead::is_sat() const { for (bool_var x : m_freevars) { literal l(x, false); literal_vector const& lits1 = m_binary[l.index()]; for (literal lit1 : lits1) { if (!is_true(lit1)) { TRACE("sat", tout << l << " " << lit1 << "\n";); return false; } } l.neg(); literal_vector const& lits2 = m_binary[l.index()]; for (literal lit2 : lits2) { if (!is_true(lit2)) { TRACE("sat", tout << l << " " << lit2 << "\n";); return false; } } } // check if there is a clause whose literals are false. // every clause is terminated by a null-literal. for (nary * n : m_nary_clauses) { bool no_true = true; for (literal l : *n) { no_true &= !is_true(l); } if (no_true) return false; } // check if there is a ternary whose literals are false. for (unsigned idx = 0; idx < m_ternary.size(); ++idx) { literal lit = to_literal(idx); if (!is_true(lit)) { unsigned sz = m_ternary_count[lit.index()]; for (binary const& b : m_ternary[lit.index()]) { if (sz-- == 0) break; if (!is_true(b.m_u) && !is_true(b.m_v)) return false; } } } return true; } bool lookahead::missed_propagation() const { if (inconsistent()) return false; for (literal l1 : m_trail) { SASSERT(is_true(l1)); for (literal l2 : m_binary[l1.index()]) { VERIFY(is_true(l2)); if (is_undef(l2)) return true; } unsigned sz = m_ternary_count[(~l1).index()]; for (binary b : m_ternary[(~l1).index()]) { if (sz-- == 0) break; if (!(is_true(b.m_u) || is_true(b.m_v) || (is_undef(b.m_v) && is_undef(b.m_u)))) { IF_VERBOSE(0, verbose_stream() << b.m_u << " " << b.m_v << "\n" << get_level(b.m_u) << " " << get_level(b.m_v) << " level: " << m_level << "\n";); UNREACHABLE(); } if ((is_false(b.m_u) && is_undef(b.m_v)) || (is_false(b.m_v) && is_undef(b.m_u))) return true; } } for (nary * n : m_nary_clauses) { if (n->size() == 1 && !is_true(n->get_head())) { for (literal lit : *n) { VERIFY(!is_undef(lit)); if (is_undef(lit)) return true; } } } return false; } bool lookahead::missed_conflict() const { if (inconsistent()) return false; for (literal l1 : m_trail) { for (literal l2 : m_binary[l1.index()]) { if (is_false(l2)) return true; } unsigned sz = m_ternary_count[(~l1).index()]; for (binary b : m_ternary[(~l1).index()]) { if (sz-- == 0) break; if ((is_false(b.m_u) && is_false(b.m_v))) return true; } } for (nary * n : m_nary_clauses) { if (n->size() == 0) return true; } return false; } void lookahead::init_pre_selection(unsigned level) { switch (m_config.m_reward_type) { case ternary_reward: { unsigned max_level = m_config.m_max_hlevel; if (level <= 1) { ensure_H(2); h_scores(m_H[0], m_H[1]); for (unsigned j = 0; j < 2; ++j) { for (unsigned i = 0; i < 2; ++i) { h_scores(m_H[i + 1], m_H[(i + 2) % 3]); } } m_heur = &m_H[1]; } else if (level < max_level) { ensure_H(level); h_scores(m_H[level-1], m_H[level]); m_heur = &m_H[level]; } else { ensure_H(max_level); h_scores(m_H[max_level-1], m_H[max_level]); m_heur = &m_H[max_level]; } break; } case heule_schur_reward: heule_schur_scores(); break; case heule_unit_reward: heule_unit_scores(); break; case march_cu_reward: march_cu_scores(); break; case unit_literal_reward: heule_schur_scores(); break; } } void lookahead::heule_schur_scores() { if (m_rating_throttle++ % 10 != 0) return; for (bool_var x : m_freevars) { literal l(x, false); m_rating[l.var()] = heule_schur_score(l) * heule_schur_score(~l); } } double lookahead::heule_schur_score(literal l) { double sum = 0; for (literal lit : m_binary[l.index()]) { if (is_undef(lit)) sum += literal_occs(lit) / 4.0; } unsigned sz = m_ternary_count[(~l).index()]; for (binary const& b : m_ternary[(~l).index()]) { if (sz-- == 0) break; sum += (literal_occs(b.m_u) + literal_occs(b.m_v)) / 8.0; } sz = m_nary_count[(~l).index()]; for (nary * n : m_nary[(~l).index()]) { if (sz-- == 0) break; double to_add = 0; for (literal lit : *n) { if (!is_fixed(lit) && lit != ~l) { to_add += literal_occs(lit); } } unsigned len = n->size(); sum += pow(0.5, len) * to_add / len; } return sum; } void lookahead::heule_unit_scores() { if (m_rating_throttle++ % 10 != 0) return; for (bool_var x : m_freevars) { literal l(x, false); m_rating[l.var()] = heule_unit_score(l) * heule_unit_score(~l); } } double lookahead::heule_unit_score(literal l) { double sum = 0; for (literal lit : m_binary[l.index()]) { if (is_undef(lit)) sum += 0.5; } sum += 0.25 * m_ternary_count[(~l).index()]; unsigned sz = m_nary_count[(~l).index()]; for (nary * n : m_nary[(~l).index()]) { if (sz-- == 0) break; sum += pow(0.5, n->size()); } return sum; } void lookahead::march_cu_scores() { for (bool_var x : m_freevars) { literal l(x, false); double pos = march_cu_score(l), neg = march_cu_score(~l); m_rating[l.var()] = 1024 * pos * neg + pos + neg + 1; } } double lookahead::march_cu_score(literal l) { double sum = 1.0 + literal_big_occs(~l); for (literal lit : m_binary[l.index()]) { if (is_undef(lit)) sum += literal_big_occs(lit); } return sum; } void lookahead::ensure_H(unsigned level) { while (m_H.size() <= level) { m_H.push_back(svector()); m_H.back().resize(m_num_vars * 2, 0); } } void lookahead::h_scores(svector& h, svector& hp) { double sum = 0; for (bool_var x : m_freevars) { literal l(x, false); sum += h[l.index()] + h[(~l).index()]; } if (sum == 0) sum = 0.0001; double factor = 2 * m_freevars.size() / sum; double sqfactor = factor * factor; double afactor = factor * m_config.m_alpha; for (bool_var x : m_freevars) { literal l(x, false); double pos = l_score(l, h, factor, sqfactor, afactor); double neg = l_score(~l, h, factor, sqfactor, afactor); hp[l.index()] = pos; hp[(~l).index()] = neg; m_rating[l.var()] = pos * neg; } } double lookahead::l_score(literal l, svector const& h, double factor, double sqfactor, double afactor) { double sum = 0, tsum = 0; for (literal lit : m_binary[l.index()]) { if (is_undef(lit)) sum += h[lit.index()]; // if (m_freevars.contains(lit.var())) sum += h[lit.index()]; } unsigned sz = m_ternary_count[(~l).index()]; for (binary const& b : m_ternary[(~l).index()]) { if (sz-- == 0) break; tsum += h[b.m_u.index()] * h[b.m_v.index()]; } // std::cout << "sum: " << sum << " afactor " << afactor << " sqfactor " << sqfactor << " tsum " << tsum << "\n"; sum = (double)(0.1 + afactor*sum + sqfactor*tsum); // std::cout << "sum: " << sum << " max score " << m_config.m_max_score << "\n"; return std::min(m_config.m_max_score, sum); } // ------------------------------------ // Implication graph // Compute implication ordering and strongly connected components. // sat11.w 103 - 114. void lookahead::get_scc() { unsigned num_candidates = m_candidates.size(); init_scc(); for (unsigned i = 0; i < num_candidates && !inconsistent(); ++i) { literal lit(m_candidates[i].m_var, false); if (get_rank(lit) == 0) get_scc(lit); if (get_rank(~lit) == 0) get_scc(~lit); } TRACE("sat", display_scc(tout);); } void lookahead::init_scc() { inc_bstamp(); for (unsigned i = 0; i < m_candidates.size(); ++i) { literal lit(m_candidates[i].m_var, false); init_dfs_info(lit); init_dfs_info(~lit); } for (unsigned i = 0; i < m_candidates.size(); ++i) { literal lit(m_candidates[i].m_var, false); init_arcs(lit); init_arcs(~lit); } m_rank = 0; m_rank_max = UINT_MAX; m_active = null_literal; m_settled = null_literal; TRACE("sat", display_dfs(tout);); } void lookahead::init_dfs_info(literal l) { unsigned idx = l.index(); m_dfs[idx].reset(); set_bstamp(l); } // arcs are added in the opposite direction of implications. // So for implications l => u we add arcs u -> l void lookahead::init_arcs(literal l) { literal_vector lits; literal_vector const& succ = m_binary[l.index()]; for (literal u : succ) { SASSERT(u != l); // l => u // NB. u.index() > l.index() iff u.index() > (~l).index(). // since indices for the same boolean variables occupy // two adjacent numbers. if (u.index() > l.index() && is_stamped(u) && ~l != u) { add_arc(~l, ~u); add_arc( u, l); } } for (auto w : m_watches[l.index()]) { lits.reset(); if (w.is_ext_constraint() && m_s.m_ext->is_extended_binary(w.get_ext_constraint_idx(), lits)) { for (literal u : lits) { // u is positive in lits, l is negative: if (~l != u && u.index() > l.index() && is_stamped(u)) { add_arc(~l, ~u); add_arc( u, l); } } } } } void lookahead::add_arc(literal u, literal v) { auto & lst = m_dfs[u.index()].m_next; if (lst.empty() || lst.back() != v) lst.push_back(v); } void lookahead::get_scc(literal v) { TRACE("scc", tout << v << "\n";); set_parent(v, null_literal); activate_scc(v); do { literal ll = get_min(v); if (has_arc(v)) { literal u = pop_arc(v); unsigned r = get_rank(u); if (r > 0) { // u was processed before ll if (r < get_rank(ll)) set_min(v, u); } else { // process u in dfs order, add v to dfs stack for u set_parent(u, v); v = u; activate_scc(v); } } else { literal u = get_parent(v); if (v == ll) { found_scc(v); } else if (get_rank(ll) < get_rank(get_min(u))) { set_min(u, ll); } // walk back in the dfs stack v = u; } } while (v != null_literal && !inconsistent()); } void lookahead::activate_scc(literal l) { SASSERT(get_rank(l) == 0); set_rank(l, ++m_rank); set_link(l, m_active); set_min(l, l); m_active = l; } // make v root of the scc equivalence class // set vcomp to be the highest rated literal void lookahead::found_scc(literal v) { literal t = m_active; m_active = get_link(v); literal best = v; double best_rating = get_rating(v); set_rank(v, m_rank_max); set_link(v, m_settled); m_settled = t; while (t != v) { if (t == ~v) { TRACE("sat", display_scc(tout << "found contradiction during scc search\n");); set_conflict(); break; } set_rank(t, m_rank_max); set_parent(t, v); double t_rating = get_rating(t); if (t_rating > best_rating) { best = t; best_rating = t_rating; } t = get_link(t); } set_parent(v, v); set_vcomp(v, best); if (maxed_rank(~v)) { set_vcomp(v, ~get_vcomp(get_parent(~v))); } } std::ostream& lookahead::display_dfs(std::ostream& out) const { for (unsigned i = 0; i < m_candidates.size(); ++i) { literal l(m_candidates[i].m_var, false); display_dfs(out, l); display_dfs(out, ~l); } return out; } std::ostream& lookahead::display_dfs(std::ostream& out, literal l) const { arcs const& a1 = get_arcs(l); if (!a1.empty()) { out << l << " -> " << a1 << "\n"; } return out; } std::ostream& lookahead::display_scc(std::ostream& out) const { display_dfs(out); for (unsigned i = 0; i < m_candidates.size(); ++i) { literal l(m_candidates[i].m_var, false); display_scc(out, l); display_scc(out, ~l); } return out; } std::ostream& lookahead::display_scc(std::ostream& out, literal l) const { out << l << " := " << get_parent(l) << " min: " << get_min(l) << " rank: " << get_rank(l) << " height: " << get_height(l) << " link: " << get_link(l) << " child: " << get_child(l) << " vcomp: " << get_vcomp(l) << "\n"; return out; } // ------------------------------------ // lookahead forest // sat11.w 115-121 literal lookahead::get_child(literal u) const { if (u == null_literal) return m_root_child; return m_dfs[u.index()].m_min; } void lookahead::set_child(literal v, literal u) { if (v == null_literal) m_root_child = u; else m_dfs[v.index()].m_min = u; } /* \brief Assign heights to the nodes. Nodes within the same strongly connected component are given the same height. The code assumes that m_settled is topologically sorted such that 1. nodes in the same equivalence class come together 2. the equivalence class representative is last */ void lookahead::find_heights() { m_root_child = null_literal; literal pp = null_literal; unsigned h = 0; literal w, uu; TRACE("sat", for (literal u = m_settled; u != null_literal; u = get_link(u)) { tout << u << " "; } tout << "\n";); for (literal u = m_settled; u != null_literal; u = uu) { TRACE("sat", tout << "process: " << u << "\n";); uu = get_link(u); literal p = get_parent(u); if (p != pp) { // new equivalence class h = 0; w = null_literal; pp = p; } // traverse nodes in order of implication unsigned sz = num_next(~u); for (unsigned j = 0; j < sz; ++j) { literal v = ~get_next(~u, j); TRACE("sat", tout << "child " << v << " link: " << get_link(v) << "\n";); literal pv = get_parent(v); // skip nodes in same equivalence, they will all be processed if (pv == p) continue; unsigned hh = get_height(pv); // update the maximal height descendant if (hh >= h) { h = hh + 1; w = pv; } } if (p == u) { // u is an equivalence class representative // it is processed last literal v = get_child(w); set_height(u, h); set_child(u, null_literal); set_link(u, v); set_child(w, u); TRACE("sat", tout << "child(" << w << ") = " << u << " link(" << u << ") = " << v << "\n";); } } TRACE("sat", display_forest(tout << "forest: ", get_child(null_literal)); tout << "\n"; display_scc(tout); ); } std::ostream& lookahead::display_forest(std::ostream& out, literal l) { for (literal u = l; u != null_literal; u = get_link(u)) { out << u << " "; l = get_child(u); if (l != null_literal) { out << "("; display_forest(out, l); out << ") "; } } return out; } void lookahead::display_search_string() { printf("\r"); uint64_t q = m_prefix; unsigned depth = m_trail_lim.size(); unsigned d = std::min(63u, depth); unsigned new_prefix_length = d; for (unsigned i = 0; i <= d; ++i) { printf((0 != (q & (1ull << i)))? "1" : "0"); } if (d < depth) { printf(" d: %d", depth); new_prefix_length += 10; } for (unsigned i = new_prefix_length; i < m_last_prefix_length; ++i) { printf(" "); } m_last_prefix_length = new_prefix_length; fflush(stdout); } void lookahead::construct_lookahead_table() { literal u = get_child(null_literal), v = null_literal; unsigned offset = 0; SASSERT(m_lookahead.empty()); while (u != null_literal) { set_rank(u, m_lookahead.size()); set_lookahead(get_vcomp(u)); if (null_literal != get_child(u)) { set_parent(u, v); v = u; u = get_child(u); } else { while (true) { set_offset(get_rank(u), offset); offset += 2; set_parent(u, v == null_literal ? v : get_vcomp(v)); u = get_link(u); if (u == null_literal && v != null_literal) { u = v; v = get_parent(u); } else { break; } } } } SASSERT(2*m_lookahead.size() == offset); TRACE("sat", for (unsigned i = 0; i < m_lookahead.size(); ++i) tout << m_lookahead[i].m_lit << " : " << m_lookahead[i].m_offset << "\n";); } // ------------------------------------ // initialization void lookahead::init_var(bool_var v) { m_binary.push_back(literal_vector()); m_binary.push_back(literal_vector()); m_watches.push_back(watch_list()); m_watches.push_back(watch_list()); m_ternary.push_back(svector()); m_ternary.push_back(svector()); m_ternary_count.push_back(0); m_ternary_count.push_back(0); m_nary.push_back(ptr_vector()); m_nary.push_back(ptr_vector()); m_nary_count.push_back(0); m_nary_count.push_back(0); m_bstamp.push_back(0); m_bstamp.push_back(0); m_stamp.push_back(0); m_dfs.push_back(dfs_info()); m_dfs.push_back(dfs_info()); m_lits.push_back(lit_info()); m_lits.push_back(lit_info()); m_rating.push_back(0); m_vprefix.push_back(prefix()); if (!m_s.was_eliminated(v)) m_freevars.insert(v); } 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; m_bstamp_id = 0; for (unsigned i = 0; i < m_num_vars; ++i) { init_var(i); } // copy binary clauses unsigned sz = m_s.m_watches.size(); for (unsigned l_idx = 0; l_idx < sz; ++l_idx) { literal l = ~to_literal(l_idx); if (m_s.was_eliminated(l.var())) continue; watch_list const & wlist = m_s.m_watches[l_idx]; for (auto& w : wlist) { if (!w.is_binary_clause()) continue; if (!learned && w.is_learned()) continue; literal l2 = w.get_literal(); if (l.index() < l2.index() && !m_s.was_eliminated(l2.var())) add_binary(l, l2); } } copy_clauses(m_s.m_clauses, false); if (learned) copy_clauses(m_s.m_learned, true); // copy units unsigned trail_sz = m_s.init_trail_size(); 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_s.m_drat.add(l, false); assign(l); } } if (m_s.m_ext) { // m_ext = m_s.m_ext->copy(this, learned); } propagate(); m_qhead = m_trail.size(); m_init_freevars = m_freevars.size(); TRACE("sat", m_s.display(tout); display(tout);); } void lookahead::copy_clauses(clause_vector const& clauses, bool learned) { // copy clauses for (clause* cp : clauses) { clause& c = *cp; if (c.was_removed()) continue; // enable when there is a non-ternary reward system. bool was_eliminated = false; for (unsigned i = 0; !was_eliminated && i < c.size(); ++i) { was_eliminated = m_s.was_eliminated(c[i].var()); } if (was_eliminated) continue; switch (c.size()) { case 0: set_conflict(); break; case 1: assign(c[0]); break; case 2: add_binary(c[0],c[1]); break; 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_s.m_drat.add(c, false); } } // ------------------------------------ // search void lookahead::push(literal lit, unsigned level) { SASSERT(m_search_mode == lookahead_mode::searching); m_binary_trail_lim.push_back(m_binary_trail.size()); m_trail_lim.push_back(m_trail.size()); m_num_tc1_lim.push_back(m_num_tc1); m_qhead_lim.push_back(m_qhead); scoped_level _sl(*this, level); m_assumptions.push_back(~lit); assign(lit); propagate(); } void lookahead::pop() { SASSERT(!m_assumptions.empty()); m_assumptions.pop_back(); m_inconsistent = false; SASSERT(m_search_mode == lookahead_mode::searching); // m_freevars only for main search // undo assignments unsigned old_sz = m_trail_lim.back(); for (unsigned i = m_trail.size(); i > old_sz; ) { --i; literal l = m_trail[i]; set_undef(l); TRACE("sat", tout << "inserting free var v" << l.var() << "\n";); m_freevars.insert(l.var()); } m_num_tc1 = m_num_tc1_lim.back(); m_num_tc1_lim.pop_back(); for (unsigned i = m_qhead; i > m_qhead_lim.back(); ) { --i; restore_ternary(m_trail[i]); restore_clauses(m_trail[i]); } m_trail.shrink(old_sz); // reset assignment. m_trail_lim.pop_back(); // remove local binary clauses old_sz = m_binary_trail_lim.back(); for (unsigned i = m_binary_trail.size(); i > old_sz; ) { del_binary(m_binary_trail[--i]); } m_binary_trail.shrink(old_sz); m_binary_trail_lim.pop_back(); // reset propagation queue m_qhead = m_qhead_lim.back(); m_qhead_lim.pop_back(); } bool lookahead::push_lookahead2(literal lit, unsigned level) { scoped_level _sl(*this, level); SASSERT(m_search_mode == lookahead_mode::lookahead1); m_search_mode = lookahead_mode::lookahead2; lookahead_backtrack(); assign(lit); propagate(); bool unsat = inconsistent(); SASSERT(m_search_mode == lookahead_mode::lookahead2); m_search_mode = lookahead_mode::lookahead1; m_inconsistent = false; return unsat; } unsigned lookahead::push_lookahead1(literal lit, unsigned level) { SASSERT(m_search_mode == lookahead_mode::searching); m_search_mode = lookahead_mode::lookahead1; scoped_level _sl(*this, level); lookahead_backtrack(); unsigned old_sz = m_trail.size(); assign(lit); propagate(); return m_trail.size() - old_sz; } void lookahead::pop_lookahead1(literal lit, unsigned num_units) { bool unsat = inconsistent(); SASSERT(m_search_mode == lookahead_mode::lookahead1); m_inconsistent = false; m_search_mode = lookahead_mode::searching; // convert windfalls to binary clauses. if (!unsat) { literal nlit = ~lit; for (unsigned i = 0; i < m_wstack.size(); ++i) { literal l2 = m_wstack[i]; //update_prefix(~lit); //update_prefix(m_wstack[i]); TRACE("sat", tout << "windfall: " << nlit << " " << l2 << "\n";); // if we use try_add_binary, then this may produce new assignments // these assignments get put on m_trail, and they are cleared by // lookahead_backtrack. add_binary(nlit, l2); } m_stats.m_windfall_binaries += m_wstack.size(); } switch (m_config.m_reward_type) { case unit_literal_reward: m_lookahead_reward += num_units; break; case heule_unit_reward: case march_cu_reward: case heule_schur_reward: break; default: break; } m_wstack.reset(); } void lookahead::lookahead_backtrack() { literal lit = null_literal; while (!m_trail.empty() && is_undef((lit = m_trail.back()))) { if (m_qhead == m_trail.size()) { unsigned sz = m_nary_count[(~lit).index()]; for (nary* n : m_nary[(~lit).index()]) { if (sz-- == 0) break; n->inc_size(); } --m_qhead; } m_trail.pop_back(); } SASSERT(m_trail_lim.empty() || m_trail.size() >= m_trail_lim.back()); } // // The current version is modeled after CDCL SAT solving data-structures. // It borrows from the watch list data-structure. The cost tradeoffs are somewhat // biased towards CDCL search overheads. // If we walk over the positive occurrences of l, then those clauses can be retired so // that they don't interfere with calculation of H. Instead of removing clauses from the watch // list one can swap them to the "back" and adjust a size indicator of the watch list // Only the size indicator needs to be updated on backtracking. // class lookahead_literal_occs_fun : public literal_occs_fun { lookahead& lh; public: lookahead_literal_occs_fun(lookahead& lh): lh(lh) {} double operator()(literal l) override { return lh.literal_occs(l); } }; // Ternary clause management: void lookahead::add_ternary(literal u, literal v, literal w) { SASSERT(u != w && u != v && v != w && ~u != w && ~u != v && ~w != v); m_ternary[u.index()].push_back(binary(v, w)); m_ternary[v.index()].push_back(binary(w, u)); m_ternary[w.index()].push_back(binary(u, v)); m_ternary_count[u.index()]++; m_ternary_count[v.index()]++; m_ternary_count[w.index()]++; } lbool lookahead::propagate_ternary(literal l1, literal l2) { if (is_fixed(l1)) { if (is_false(l1)) { if (is_false(l2)) { TRACE("sat", tout << l1 << " " << l2 << " " << "\n";); set_conflict(); return l_false; } else if (is_undef(l2)) { propagated(l2); } return l_true; } else { return l_true; } } if (is_fixed(l2)) { if (is_false(l2)) { propagated(l1); return l_false; } else { return l_true; } } return l_undef; } void lookahead::propagate_ternary(literal l) { unsigned sz = m_ternary_count[(~l).index()]; switch (m_search_mode) { case lookahead_mode::searching: { // ternary clauses where l is negative become binary for (binary const& b : m_ternary[(~l).index()]) { if (sz-- == 0) break; // this could create a conflict from propagation, but we complete the transaction. TRACE("sat", display(tout);); literal l1 = b.m_u; literal l2 = b.m_v; switch (propagate_ternary(l1, l2)) { case l_undef: try_add_binary(l1, l2); break; default: // propagated or tautology or conflict break; } remove_ternary(l1, l2, ~l); remove_ternary(l2, ~l, l1); } sz = m_ternary_count[l.index()]; // ternary clauses where l is positive are tautologies for (binary const& b : m_ternary[l.index()]) { if (sz-- == 0) break; remove_ternary(b.m_u, b.m_v, l); remove_ternary(b.m_v, l, b.m_u); } break; } case lookahead_mode::lookahead1: // this could create a conflict from propagation, but we complete the loop. for (binary const& b : m_ternary[(~l).index()]) { if (sz-- == 0) break; literal l1 = b.m_u; literal l2 = b.m_v; switch (propagate_ternary(l1, l2)) { case l_undef: update_binary_clause_reward(l1, l2); break; default: break; } } break; case lookahead2: // this could create a conflict from propagation, but we complete the loop. for (binary const& b : m_ternary[(~l).index()]) { if (sz-- == 0) break; propagate_ternary(b.m_u, b.m_v); } break; } } void lookahead::remove_ternary(literal l, literal u, literal v) { unsigned idx = l.index(); unsigned sz = m_ternary_count[idx]--; auto& tv = m_ternary[idx]; for (unsigned i = sz; i-- > 0; ) { binary const& b = tv[i]; if (b.m_u == u && b.m_v == v) { std::swap(tv[i], tv[sz-1]); return; } } UNREACHABLE(); } void lookahead::restore_ternary(literal l) { unsigned sz = m_ternary_count[(~l).index()]; for (binary const& b : m_ternary[(~l).index()]) { if (sz-- == 0) break; m_ternary_count[b.m_u.index()]++; m_ternary_count[b.m_v.index()]++; } sz = m_ternary_count[l.index()]; for (binary const& b : m_ternary[l.index()]) { if (sz-- == 0) break; m_ternary_count[b.m_u.index()]++; m_ternary_count[b.m_v.index()]++; } } void lookahead::propagate_external(literal l) { SASSERT(is_true(l)); if (!m_s.m_ext || inconsistent()) return; watch_list& wlist = m_watches[l.index()]; watch_list::iterator it = wlist.begin(), it2 = it, end = wlist.end(); for (; it != end && !inconsistent(); ++it) { SASSERT(it->get_kind() == watched::EXT_CONSTRAINT); bool keep = m_s.m_ext->propagate(l, it->get_ext_constraint_idx()); if (m_search_mode == lookahead_mode::lookahead1 && !m_inconsistent) { lookahead_literal_occs_fun literal_occs_fn(*this); m_lookahead_reward += m_s.m_ext->get_reward(l, it->get_ext_constraint_idx(), literal_occs_fn); } if (inconsistent()) { if (!keep) ++it; } else if (keep) { *it2 = *it; it2++; } } for (; it != end; ++it, ++it2) { *it2 = *it; } wlist.set_end(it2); } // new n-ary clause management void lookahead::add_clause(clause const& c) { SASSERT(c.size() > 3); void * mem = m_allocator.allocate(nary::get_obj_size(c.size())); nary * n = new (mem) nary(c.size(), c.begin()); m_nary_clauses.push_back(n); for (literal l : c) { m_nary[l.index()].push_back(n); m_nary_count[l.index()]++; } } void lookahead::propagate_clauses_searching(literal l) { // clauses where l is negative unsigned sz = m_nary_count[(~l).index()]; literal lit; SASSERT(m_search_mode == lookahead_mode::searching); for (nary* n : m_nary[(~l).index()]) { if (sz-- == 0) break; unsigned len = n->dec_size(); if (is_true(n->get_head())) continue; if (inconsistent()) continue; if (len <= 1) continue; // already processed // find the two unassigned literals, if any if (len == 2) { literal l1 = null_literal; literal l2 = null_literal; bool found_true = false; for (literal lit : *n) { if (!is_fixed(lit)) { if (l1 == null_literal) { l1 = lit; } else { SASSERT(l2 == null_literal); l2 = lit; break; } } else if (is_true(lit)) { n->set_head(lit); found_true = true; break; } } if (found_true) { // skip, the clause will be removed when propagating on 'lit' } else if (l1 == null_literal) { set_conflict(); } else if (l2 == null_literal) { // clause may get revisited during propagation, when l2 is true in this clause. // m_removed_clauses.push_back(std::make_pair(~l, idx)); // remove_clause_at(~l, idx); propagated(l1); } else { // extract binary clause. A unary or empty clause may get revisited, // but we skip it then because it is already handled as a binary clause. // m_removed_clauses.push_back(std::make_pair(~l, idx)); // need to restore this clause. // remove_clause_at(~l, idx); try_add_binary(l1, l2); } } } // clauses where l is positive: sz = m_nary_count[l.index()]; for (nary* n : m_nary[l.index()]) { if (sz-- == 0) break; remove_clause_at(l, *n); } } void lookahead::propagate_clauses_lookahead(literal l) { // clauses where l is negative unsigned sz = m_nary_count[(~l).index()]; SASSERT(m_search_mode == lookahead_mode::lookahead1 || m_search_mode == lookahead_mode::lookahead2); for (nary* n : m_nary[(~l).index()]) { if (sz-- == 0) break; unsigned nonfixed = n->dec_size(); // if (is_true(n->get_head())) continue; if (inconsistent()) continue; if (nonfixed <= 1 && !is_true(n->get_head())) { bool found_conflict = true; for (literal lit : *n) { if (!is_fixed(lit)) { propagated(lit); found_conflict = false; break; } else if (is_true(lit)) { n->set_head(lit); found_conflict = false; break; } } if (found_conflict) { set_conflict(); continue; } } if (m_search_mode == lookahead_mode::lookahead1) { //SASSERT(nonfixed >= 2); switch (m_config.m_reward_type) { case heule_schur_reward: { double to_add = 0; for (literal lit : *n) { if (!is_fixed(lit)) { to_add += literal_occs(lit); } } m_lookahead_reward += pow(0.5, nonfixed) * to_add / nonfixed; break; } case heule_unit_reward: m_lookahead_reward += pow(0.5, nonfixed); break; case march_cu_reward: m_lookahead_reward += nonfixed >= 2 ? 3.3 * pow(0.5, nonfixed - 2) : 0.0; break; case ternary_reward: UNREACHABLE(); break; case unit_literal_reward: break; } } } // clauses where l is positive: sz = m_nary_count[l.index()]; for (nary* n : m_nary[l.index()]) { if (sz-- == 0) break; if (get_level(l) > get_level(n->get_head())) { n->set_head(l); } } } void lookahead::remove_clause_at(literal l, nary& n) { for (literal lit : n) { if (lit != l) { remove_clause(lit, n); } } } void lookahead::remove_clause(literal l, nary& n) { ptr_vector& pclauses = m_nary[l.index()]; unsigned sz = m_nary_count[l.index()]--; for (unsigned i = sz; i > 0; ) { --i; if (&n == pclauses[i]) { std::swap(pclauses[i], pclauses[sz-1]); return; } } UNREACHABLE(); } void lookahead::restore_clauses(literal l) { SASSERT(m_search_mode == lookahead_mode::searching); // increase the length of clauses where l is negative unsigned sz = m_nary_count[(~l).index()]; for (nary* n : m_nary[(~l).index()]) { if (sz-- == 0) break; n->inc_size(); } // add idx back to clause list where l is positive // add them back in the same order as they were inserted // in this way we can check that the clauses are the same. sz = m_nary_count[l.index()]; ptr_vector& pclauses = m_nary[l.index()]; for (unsigned i = sz; i-- > 0; ) { for (literal lit : *pclauses[i]) { if (lit != l) { SASSERT(m_nary[lit.index()][m_nary_count[lit.index()]] == pclauses[i]); m_nary_count[lit.index()]++; } } } } void lookahead::propagate_clauses(literal l) { SASSERT(is_true(l)); propagate_ternary(l); switch (m_search_mode) { case lookahead_mode::searching: propagate_clauses_searching(l); break; default: propagate_clauses_lookahead(l); break; } propagate_external(l); } void lookahead::update_binary_clause_reward(literal l1, literal l2) { SASSERT(!is_false(l1)); SASSERT(!is_false(l2)); switch (m_config.m_reward_type) { case ternary_reward: m_lookahead_reward += (*m_heur)[l1.index()] * (*m_heur)[l2.index()]; break; case heule_schur_reward: m_lookahead_reward += (literal_occs(l1) + literal_occs(l2)) / 8.0; break; case heule_unit_reward: m_lookahead_reward += 0.25; break; case march_cu_reward: m_lookahead_reward += 3.3; break; case unit_literal_reward: break; } } void lookahead::update_nary_clause_reward(clause const& c) { if (m_config.m_reward_type == ternary_reward && m_lookahead_reward != 0) { return; } literal const * l_it = c.begin() + 2, *l_end = c.end(); unsigned sz = 0; for (; l_it != l_end; ++l_it) { if (is_true(*l_it)) return; if (!is_false(*l_it)) ++sz; } switch (m_config.m_reward_type) { case heule_schur_reward: { SASSERT(sz > 0); double to_add = 0; for (literal l : c) { if (!is_false(l)) { to_add += literal_occs(l); } } m_lookahead_reward += pow(0.5, sz) * to_add / sz; break; } case heule_unit_reward: m_lookahead_reward += pow(0.5, sz); break; case march_cu_reward: m_lookahead_reward += 3.3 * pow(0.5, sz - 2); break; case ternary_reward: m_lookahead_reward = (double)0.001; break; case unit_literal_reward: break; } } // Sum_{ clause C that contains ~l } 1 // FIXME: counts occurrences of ~l; misleading double lookahead::literal_occs(literal l) { double result = m_binary[l.index()].size(); result += literal_big_occs(l); return result; } // Sum_{ clause C that contains ~l such that |C| > 2} 1 // FIXME: counts occurrences of ~l; misleading double lookahead::literal_big_occs(literal l) { double result = m_nary_count[(~l).index()]; result += m_ternary_count[(~l).index()]; return result; } void lookahead::get_clauses(literal_vector& clauses, unsigned max_clause_size) { // push binary clauses unsigned num_lits = m_s.num_vars() * 2; for (unsigned idx = 0; idx < num_lits; ++idx) { literal u = to_literal(idx); if (m_s.was_eliminated(u.var()) || is_false(u) || is_true(u)) continue; for (literal v : m_binary[idx]) { if (u.index() < v.index() && !m_s.was_eliminated(v.var()) && !is_false(v) && !is_true(v)) { clauses.push_back(~u); clauses.push_back(v); clauses.push_back(null_literal); } } } // push ternary clauses for (unsigned idx = 0; idx < num_lits; ++idx) { literal u = to_literal(idx); if (is_true(u) || is_false(u)) continue; unsigned sz = m_ternary_count[u.index()]; for (binary const& b : m_ternary[u.index()]) { if (sz-- == 0) break; if (u.index() > b.m_v.index() || u.index() > b.m_u.index()) continue; if (is_true(b.m_u) || is_true(b.m_v)) continue; if (is_false(b.m_u) && is_false(b.m_v)) continue; clauses.push_back(u); if (!is_false(b.m_u)) clauses.push_back(b.m_u); if (!is_false(b.m_v)) clauses.push_back(b.m_v); clauses.push_back(null_literal); } } // push n-ary clauses for (unsigned idx = 0; idx < num_lits; ++idx) { literal u = to_literal(idx); unsigned sz = m_nary_count[u.index()]; for (nary* n : m_nary[u.index()]) { if (sz-- == 0) break; unsigned sz0 = clauses.size(); if (n->size() > max_clause_size) continue; for (literal lit : *n) { if (is_true(lit)) { clauses.shrink(sz0); break; } if (!is_false(lit)) { clauses.push_back(lit); } } if (clauses.size() > sz0) { clauses.push_back(null_literal); } } } TRACE("sat", for (literal lit : clauses) { if (lit == null_literal) { tout << "\n"; } else { tout << lit << " "; } } tout << "\n"; m_s.display(tout); ); } void lookahead::propagate_binary(literal l) { literal_vector const& lits = m_binary[l.index()]; TRACE("sat", tout << l << " => " << lits << "\n";); for (literal lit : lits) { if (inconsistent()) break; assign(lit); } } void lookahead::propagate() { unsigned i = m_qhead; for (; i < m_trail.size() && !inconsistent(); ++i) { literal l = m_trail[i]; TRACE("sat", tout << "propagate " << l << " @ " << m_level << "\n";); propagate_binary(l); } while (m_qhead < m_trail.size() && !inconsistent()) { propagate_clauses(m_trail[m_qhead++]); } SASSERT(m_qhead == m_trail.size() || (inconsistent() && m_qhead < m_trail.size())); //SASSERT(!missed_conflict()); //VERIFY(!missed_propagation()); TRACE("sat_verbose", display(tout << scope_lvl() << " " << (inconsistent()?"unsat":"sat") << "\n");); } void lookahead::compute_lookahead_reward() { TRACE("sat", display_lookahead(tout); ); m_delta_decrease = pow(m_config.m_delta_rho, 1.0 / (double)m_lookahead.size()); unsigned base = 2; bool change = true; literal last_changed = null_literal; unsigned ops = 0; m_max_ops = 100000; while (change && !inconsistent() && ops < m_max_ops) { change = false; IF_VERBOSE(10, verbose_stream() << "(sat.lookahead :compute-reward " << m_lookahead.size() << ")\n"); for (unsigned i = 0; !inconsistent() && i < m_lookahead.size() && ops < m_max_ops; ++i) { checkpoint(); ++ops; literal lit = m_lookahead[i].m_lit; if (lit == last_changed) { SASSERT(!change); break; } if (scope_lvl() == 1) { IF_VERBOSE(30, verbose_stream() << scope_lvl() << " " << lit << " binary: " << m_binary_trail.size() << " trail: " << m_trail_lim.back() << "\n";); } unsigned level = base + m_lookahead[i].m_offset; unsigned dl_lvl = level; if (is_fixed_at(lit, c_fixed_truth) || is_true_at(lit, level)) continue; bool unsat = false; if (is_false_at(lit, level)) { unsat = true; } else { TRACE("sat", tout << "lookahead: " << lit << " @ " << m_lookahead[i].m_offset << "\n";); reset_lookahead_reward(lit); unsigned num_units = push_lookahead1(lit, level); update_lookahead_reward(lit, level); num_units += do_double(lit, dl_lvl); if (dl_lvl > level) { base = dl_lvl; SASSERT(get_level(m_trail.back()) == base); } unsat = inconsistent(); pop_lookahead1(lit, num_units); } if (unsat) { TRACE("sat", tout << "backtracking and setting " << ~lit << "\n";); lookahead_backtrack(); assign(~lit); propagate(); change = true; last_changed = lit; continue; } // if l was derived from lit and ~lit -> l, then l is a necessary assignment literal_vector necessary_lits; for (literal l : m_binary[(~lit).index()]) { if (is_true_at(l, dl_lvl) && !is_fixed_at(l, c_fixed_truth)) { necessary_lits.push_back(l); } } if (!necessary_lits.empty()) { change = true; last_changed = lit; lookahead_backtrack(); for (literal l : necessary_lits) { if (inconsistent()) break; assign(l); propagate(); } } SASSERT(inconsistent() || !is_unsat()); } if (c_fixed_truth - 2 * m_lookahead.size() < base) { break; } base += 2 * m_lookahead.size(); } lookahead_backtrack(); TRACE("sat", display_lookahead(tout); ); } literal lookahead::select_literal() { literal l = null_literal; double h = 0; unsigned count = 1; for (unsigned i = 0; i < m_lookahead.size(); ++i) { literal lit = m_lookahead[i].m_lit; if (lit.sign() || !is_undef(lit)) { continue; } double diff1 = get_lookahead_reward(lit), diff2 = get_lookahead_reward(~lit); double mixd = mix_diff(diff1, diff2); if (mixd == h) ++count; if (mixd > h || (mixd == h && m_s.m_rand(count) == 0)) { CTRACE("sat", l != null_literal, tout << lit << " mix diff: " << mixd << "\n";); if (mixd > h) count = 1; h = mixd; l = diff1 < diff2 ? lit : ~lit; } } TRACE("sat", tout << "selected: " << l << "\n";); return l; } double lookahead::mix_diff(double l, double r) const { switch (m_config.m_reward_type) { case ternary_reward: return l + r + (1 << 10) * l * r; case heule_schur_reward: return l * r; case heule_unit_reward: return l * r; case march_cu_reward: return 1024 * (1024 * l * r + l + r); case unit_literal_reward: return l * r; default: UNREACHABLE(); return l * r; } } void lookahead::reset_lookahead_reward(literal l) { m_lookahead_reward = 0; // inherit propagation effect from parent. literal p = get_parent(l); set_lookahead_reward(l, (p == null_literal || is_undef(p) || is_fixed_at(p, c_fixed_truth)) ? 0 : get_lookahead_reward(p)); } void lookahead::update_lookahead_reward(literal l, unsigned level) { if (m_lookahead_reward != 0) { inc_lookahead_reward(l, m_lookahead_reward); } } unsigned lookahead::do_double(literal l, unsigned& base) { unsigned num_units = 0; 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 = m_delta_fraction*get_lookahead_reward(l); dl_disable(l); } } } else { SASSERT(m_delta_decrease > 0.0); m_delta_trigger *= m_delta_decrease; } } return num_units; } unsigned lookahead::double_look(literal l, unsigned& base) { SASSERT(!inconsistent()); SASSERT(dl_no_overflow(base)); SASSERT(is_fixed_at(l, base)); base += m_lookahead.size(); unsigned dl_truth = base + m_lookahead.size() * m_config.m_dl_max_iterations; scoped_level _sl(*this, dl_truth); //SASSERT(get_level(m_trail.back()) == dl_truth); IF_VERBOSE(3, verbose_stream() << "(sat-lookahead :double " << l << " :depth " << m_trail_lim.size() << ")\n";); lookahead_backtrack(); assign(l); propagate(); unsigned old_sz = m_trail.size(); bool change = true; literal last_changed = null_literal; unsigned num_iterations = 0; while (change && num_iterations < m_config.m_dl_max_iterations && !inconsistent()) { num_iterations++; 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 + lh.m_offset; if (level + m_lookahead.size() >= dl_truth) { change = false; break; } bool unsat = false; if (is_false_at(lit, level) && !is_fixed_at(lit, dl_truth)) { unsat = true; } else { if (is_fixed_at(lit, level)) continue; unsat = push_lookahead2(lit, level); } if (unsat) { TRACE("sat", tout << "unit: " << ~lit << "\n";); ++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; last_changed = lit; m_wstack.push_back(~lit); } } base += 2 * m_lookahead.size(); SASSERT(dl_truth >= base); } lookahead_backtrack(); SASSERT(get_level(m_trail.back()) == dl_truth); SASSERT(m_level == dl_truth); base = dl_truth; return m_trail.size() - old_sz; } /** \brief check if literal occurs in a non-tautological reduced clause. */ bool lookahead::in_reduced_clause(bool_var v) { return in_reduced_clause(literal(v, false)) || in_reduced_clause(literal(v, true)); } bool lookahead::in_reduced_clause(literal lit) { if (lit == null_literal) return true; if (m_trail_lim.empty()) return true; unsigned sz = m_nary_count[lit.index()]; for (nary* n : m_nary[lit.index()]) { if (sz-- == 0) break; if (!n->is_reduced()) continue; bool has_true = false; for (literal l : *n) { if (is_true(l)) { has_true = true; break; } } if (!has_true) return true; } auto const& tv = m_ternary[lit.index()]; sz = tv.size(); unsigned i = m_ternary_count[lit.index()]; for (; i < sz; ++i) { binary const& b = tv[i]; if (!is_true(b.m_u) && !is_true(b.m_v)) return true; } return false; } 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_s.m_drat.add(m_assumptions); m_assumptions.pop_back(); } } void lookahead::assign(literal l) { SASSERT(m_level > 0); if (is_undef(l)) { TRACE("sat", tout << "assign: " << l << " @ " << m_level << " " << m_trail_lim.size() << " " << m_search_mode << "\n";); set_true(l); SASSERT(m_trail.empty() || get_level(m_trail.back()) >= get_level(l)); m_trail.push_back(l); if (m_search_mode == lookahead_mode::searching) { m_stats.m_propagations++; TRACE("sat", tout << "removing free var v" << l.var() << "\n";); if (l.var() > m_freevars.max_var()) IF_VERBOSE(0, verbose_stream() << "bigger than max-var: " << l << " " << " " << m_freevars.max_var() << "\n";); if (!m_freevars.contains(l.var())) IF_VERBOSE(0, verbose_stream() << "does not contain: " << l << " eliminated: " << m_s.was_eliminated(l.var()) << "\n";); if (m_freevars.contains(l.var())) { m_freevars.remove(l.var()); } validate_assign(l); } } else if (is_false(l)) { TRACE("sat", tout << "conflict: " << l << " @ " << m_level << " " << m_search_mode << "\n";); SASSERT(!is_true(l)); validate_assign(l); set_conflict(); } } void lookahead::propagated(literal l) { assign(l); for (unsigned i = m_trail.size()-1; i < m_trail.size() && !inconsistent(); ++i) { literal l = m_trail[i]; TRACE("sat", tout << "propagate " << l << " @ " << m_level << "\n";); propagate_binary(l); } if (m_search_mode == lookahead_mode::lookahead1) { m_wstack.push_back(l); } } bool lookahead::backtrack(literal_vector& trail) { while (inconsistent()) { if (trail.empty()) return false; pop(); flip_prefix(); assign(~trail.back()); trail.pop_back(); propagate(); } return true; } lbool lookahead::search() { m_model.reset(); scoped_level _sl(*this, c_fixed_truth); literal_vector trail; m_search_mode = lookahead_mode::searching; while (true) { TRACE("sat", display(tout);); inc_istamp(); checkpoint(); literal l = choose(); if (inconsistent()) { if (!backtrack(trail)) return l_false; continue; } if (l == null_literal) { return l_true; } TRACE("sat", tout << "choose: " << l << " " << trail << "\n";); ++m_stats.m_decisions; IF_VERBOSE(1, display_search_string();); push(l, c_fixed_truth); trail.push_back(l); SASSERT(inconsistent() || !is_unsat()); } } 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()) { pop(); trail.back().neg(); assign(trail.back()); is_decision.back() = false; propagate(); } else { trail.pop_back(); is_decision.pop_back(); } } return true; } 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() { double h = 0.0; for (bool_var x : m_freevars) { literal l(x, false); for (literal lit : m_binary[l.index()]) { h += l.index() > lit.index() ? 1.0 / m_config.m_cube_psat_clause_base : 0.0; } for (literal lit : m_binary[(~l).index()]) { h += l.index() > lit.index() ? 1.0 / m_config.m_cube_psat_clause_base : 0.0; } for (binary b : m_ternary[l.index()]) { h += l.index() > b.m_u.index() && l.index() > b.m_v.index() ? 1.0 / pow(m_config.m_cube_psat_clause_base, 2) : 0.0; } for (binary b : m_ternary[(~l).index()]) { h += l.index() > b.m_u.index() && l.index() > b.m_v.index() ? 1.0 / pow(m_config.m_cube_psat_clause_base, 2) : 0.0; } } for (nary * n : m_nary_clauses) { h += 1.0 / pow(m_config.m_cube_psat_clause_base, n->size() - 1); } h /= pow(m_freevars.size(), m_config.m_cube_psat_var_exp); IF_VERBOSE(10, verbose_stream() << "(sat-cube-psat :val " << h << ")\n";); return h; } bool lookahead::should_cutoff(unsigned depth) { return depth > 0 && ((m_config.m_cube_cutoff == depth_cutoff && depth == m_config.m_cube_depth) || (m_config.m_cube_cutoff == freevars_cutoff && m_freevars.size() <= m_init_freevars * m_config.m_cube_freevars) || (m_config.m_cube_cutoff == psat_cutoff && psat_heur() >= m_config.m_cube_psat_trigger) || (m_config.m_cube_cutoff == adaptive_freevars_cutoff && m_freevars.size() < m_cube_state.m_freevars_threshold) || (m_config.m_cube_cutoff == adaptive_psat_cutoff && psat_heur() >= m_cube_state.m_psat_threshold)); } lbool lookahead::cube(bool_var_vector& vars, literal_vector& lits, unsigned backtrack_level) { scoped_ext _scoped_ext(*this); lits.reset(); bool is_first = m_cube_state.m_first; if (is_first) { m_select_lookahead_vars.reset(); for (auto v : vars) { m_select_lookahead_vars.insert(v); } init_search(); m_model.reset(); m_cube_state.m_first = false; } scoped_level _sl(*this, c_fixed_truth); m_search_mode = lookahead_mode::searching; unsigned depth = 0; // unsigned init_trail = m_trail.size(); m_cube_state.reset_stats(); if (!is_first) { goto pick_up_work; } while (true) { TRACE("sat", display(tout);); checkpoint(); inc_istamp(); if (inconsistent()) { TRACE("sat", tout << "inconsistent: " << m_cube_state.m_cube << "\n";); 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; } continue; } pick_up_work: if (m_cube_state.m_cube.size() >= backtrack_level) { IF_VERBOSE(10, verbose_stream() << "(sat-cube :cube: " << m_cube_state.m_cube.size() << " :backtrack_level " << backtrack_level << ")\n";); while (m_cube_state.m_cube.size() >= backtrack_level) { set_conflict(); backtrack(m_cube_state.m_cube, m_cube_state.m_is_decision); } } backtrack_level = UINT_MAX; depth = m_cube_state.m_cube.size(); if (should_cutoff(depth)) { double dec = (1.0 - pow(m_config.m_cube_fraction, depth)); m_cube_state.m_freevars_threshold *= dec; m_cube_state.m_psat_threshold *= 2.0 - dec; set_conflict(); m_cube_state.inc_cutoff(); lits.append(m_cube_state.m_cube); vars.reset(); for (auto v : m_freevars) if (in_reduced_clause(v)) vars.push_back(v); backtrack(m_cube_state.m_cube, m_cube_state.m_is_decision); return l_undef; } int prev_nfreevars = m_freevars.size(); double prev_psat = m_config.m_cube_cutoff == adaptive_psat_cutoff ? psat_heur() : dbl_max; // MN. only compute PSAT if enabled literal lit = choose(); if (inconsistent()) { TRACE("sat", tout << "inconsistent: " << m_cube_state.m_cube << "\n";); 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; } continue; } if (lit == null_literal) { vars.reset(); for (auto v : m_freevars) if (in_reduced_clause(v)) vars.push_back(v); m_model.reset(); init_model(); return l_true; } TRACE("sat", tout << "choose: " << lit << " cube: " << m_cube_state.m_cube << "\n";); ++m_stats.m_decisions; push(lit, c_fixed_truth); m_cube_state.m_cube.push_back(lit); m_cube_state.m_is_decision.push_back(true); SASSERT(inconsistent() || !is_unsat()); } m_cube_state.reset(); return l_undef; } void lookahead::display_lookahead_scores(std::ostream& out) { scoped_ext _scoped_ext(*this); m_select_lookahead_vars.reset(); init_search(); scoped_level _sl(*this, c_fixed_truth); m_search_mode = lookahead_mode::searching; literal l = choose_base(); if (l == null_literal) { out << "null\n"; return; } for (auto const& l : m_lookahead) { literal lit = l.m_lit; if (!lit.sign() && is_undef(lit)) { double diff1 = get_lookahead_reward(lit); double diff2 = get_lookahead_reward(~lit); out << lit << " " << diff1 << " " << diff2 << "\n"; } } } void lookahead::init_model() { m_model.reset(); for (unsigned i = 0; i < m_num_vars; ++i) { lbool val; literal lit(i, false); if (is_undef(lit)) { val = l_undef; } else if (is_true(lit)) { val = l_true; } else { val = l_false; } m_model.push_back(val); } } std::ostream& lookahead::display_cube(std::ostream& out, literal_vector const& cube) const { out << "c"; for (literal l : cube) { out << " " << ~l; } return out << " 0\n"; } std::ostream& lookahead::display_binary(std::ostream& out) const { for (unsigned i = 0; i < m_binary.size(); ++i) { literal_vector const& lits = m_binary[i]; if (!lits.empty()) { out << to_literal(i) << " -> " << lits << "\n"; } } return out; } std::ostream& lookahead::display_clauses(std::ostream& out) const { for (unsigned idx = 0; idx < m_ternary.size(); ++idx) { literal lit = to_literal(idx); unsigned sz = m_ternary_count[idx]; for (binary const& b : m_ternary[idx]) { if (sz-- == 0) break; if (idx < b.m_u.index() && idx << b.m_v.index()) { out << lit << " " << b.m_u << " " << b.m_v << "\n"; } } } for (nary * n : m_nary_clauses) { for (literal l : *n) out << l << " "; out << "\n"; } return out; } std::ostream& lookahead::display_values(std::ostream& out) const { for (literal l : m_trail) { out << l << "\n"; } return out; } std::ostream& lookahead::display_lookahead(std::ostream& out) const { for (unsigned i = 0; i < m_lookahead.size(); ++i) { literal lit = m_lookahead[i].m_lit; unsigned offset = m_lookahead[i].m_offset; out << lit << "\toffset: " << offset; out << (is_undef(lit)?" undef": (is_true(lit) ? " true": " false")); out << " lookahead_reward: " << get_lookahead_reward(lit); out << "\n"; } return out; } void lookahead::init_search() { m_search_mode = lookahead_mode::searching; scoped_level _sl(*this, c_fixed_truth); init(m_s.m_config.m_lookahead_use_learned); } void lookahead::checkpoint() { if (!m_rlimit.inc()) { throw solver_exception(Z3_CANCELED_MSG); } if (memory::get_allocation_size() > m_s.m_config.m_max_memory) { throw solver_exception(Z3_MAX_MEMORY_MSG); } } literal lookahead::choose() { return choose_base(); } literal lookahead::choose_base() { literal l = null_literal; while (l == null_literal && !inconsistent()) { pre_select(); if (m_lookahead.empty()) { break; } compute_lookahead_reward(); if (inconsistent()) { break; } l = select_literal(); } SASSERT(inconsistent() || !is_unsat()); return l; } /** \brief simplify set of clauses by extracting units from a lookahead at base level. */ void lookahead::simplify(bool learned) { scoped_ext _scoped_ext(*this); SASSERT(m_prefix == 0); SASSERT(m_watches.empty()); m_search_mode = lookahead_mode::searching; scoped_level _sl(*this, c_fixed_truth); init(learned); if (inconsistent()) return; inc_istamp(); choose_base(); if (inconsistent()) return; SASSERT(m_trail_lim.empty()); unsigned num_units = 0; 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_scoped(lit); ++num_units; } } IF_VERBOSE(1, verbose_stream() << "(sat-lookahead :units " << num_units << " :propagations " << m_stats.m_propagations << ")\n";); if (m_s.inconsistent()) return; if (num_units > 0) { m_s.propagate_core(false); m_s.m_simplifier(false); } if (select(0)) { get_scc(); if (!inconsistent()) { normalize_parents(); literal_vector roots; bool_var_vector to_elim; for (unsigned i = 0; i < m_num_vars; ++i) { roots.push_back(literal(i, false)); } for (auto const& c : m_candidates) { bool_var v = c.m_var; literal q(v, false); literal p = get_parent(q); if (p != null_literal && p.var() != v && !m_s.is_external(v) && !m_s.was_eliminated(v) && !m_s.was_eliminated(p.var())) { to_elim.push_back(v); roots[v] = p; VERIFY(get_parent(p) == p); VERIFY(get_parent(~p) == ~p); } } IF_VERBOSE(1, verbose_stream() << "(sat-lookahead :equivalences " << to_elim.size() << ")\n";); elim_eqs elim(m_s); elim(roots, to_elim); if (learned && get_config().m_lookahead_simplify_bca) { add_hyper_binary(); } } } m_lookahead.reset(); } /** \brief reduction based on binary implication graph */ void lookahead::add_hyper_binary() { unsigned num_lits = m_s.num_vars() * 2; unsigned num_bins = 0; typedef std::pair u_pair; hashtable, default_eq > imp; for (unsigned idx = 0; idx < num_lits; ++idx) { literal u = get_parent(to_literal(idx)); if (null_literal != u) { for (watched const& w : m_s.m_watches[idx]) { if (!w.is_binary_clause()) continue; literal v = get_parent(w.get_literal()); if (null_literal != v) { imp.insert(std::make_pair(u.index(), v.index())); } } } } big big(m_s.m_rand); big.init(m_s, true); svector> candidates; unsigned_vector bin_size(num_lits); for (unsigned idx : m_binary_trail) { bin_size[idx]++; } for (unsigned idx = 0; idx < num_lits; ++idx) { literal u = to_literal(idx); if (u != get_parent(u)) continue; if (m_s.was_eliminated(u.var())) continue; literal_vector const& lits = m_binary[idx]; for (unsigned sz = bin_size[idx]; sz > 0; --sz) { literal v = lits[lits.size() - sz]; if (v == get_parent(v) && !m_s.was_eliminated(v.var()) && !imp.contains(std::make_pair(u.index(), v.index())) && !big.connected(u, v)) { candidates.push_back(std::make_pair(u, v)); } } } for (unsigned count = 0; count < 5; ++count) { big.init(m_s, true); unsigned k = 0; union_find_default_ctx ufctx; union_find uf(ufctx); for (unsigned i = 0; i < num_lits; ++i) uf.mk_var(); for (unsigned j = 0; j < candidates.size(); ++j) { literal u = candidates[j].first; literal v = candidates[j].second; if (!big.connected(u, v)) { if (uf.find(u.index()) != uf.find(v.index())) { ++num_bins; uf.merge(u.index(), v.index()); uf.merge((~u).index(), (~v).index()); VERIFY(!m_s.was_eliminated(u.var())); VERIFY(!m_s.was_eliminated(v.var())); m_s.mk_clause(~u, v, true); } else { candidates[k] = candidates[j]; ++k; } } } // std::cout << candidates.size() << " -> " << k << "\n"; if (k == candidates.size()) break; candidates.shrink(k); if (k == 0) break; } IF_VERBOSE(10, verbose_stream() << "(sat-lookahead :bca " << num_bins << ")\n";); m_stats.m_bca += num_bins; } void lookahead::normalize_parents() { literal_vector roots; for (unsigned i = 0; i < m_num_vars; ++i) { literal lit(i, false); roots.push_back(lit); roots.push_back(~lit); SASSERT(roots[lit.index()] == lit); } for (auto const& c : m_candidates) { bool_var v = c.m_var; literal p(v, false); literal q = get_parent(p); literal r = ~get_parent(~p); if (q != r) { if (q.var() < r.var()) { roots[q.index()] = r; } else { roots[r.index()] = q; } } } for (auto const& c : m_candidates) { literal p(c.m_var, false); literal q = roots[get_parent(p).index()]; set_parent(p, q); set_parent(~p, ~q); } } std::ostream& lookahead::display_summary(std::ostream& out) const { out << "Prefix: " << pp_prefix(m_prefix, m_trail_lim.size()) << "\n"; out << "Level: " << m_level << "\n"; out << "Free vars: " << m_freevars.size() << "\n"; return out; } std::ostream& lookahead::display(std::ostream& out) const { display_summary(out); display_values(out); display_binary(out); display_clauses(out); out << "free vars: "; for (bool_var b : m_freevars) out << b << " "; out << "\n"; clause_allocator dummy_allocator; 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, nullptr); out << "\n"; } } return out; } model const& lookahead::get_model() { if (m_model.empty()) { init_model(); } return m_model; } void lookahead::init_config() { m_config.m_reward_type = m_s.m_config.m_lookahead_reward; m_config.m_cube_cutoff = m_s.m_config.m_lookahead_cube_cutoff; m_config.m_cube_fraction = m_s.m_config.m_lookahead_cube_fraction; m_config.m_cube_depth = m_s.m_config.m_lookahead_cube_depth; m_config.m_cube_freevars = m_s.m_config.m_lookahead_cube_freevars; m_config.m_cube_psat_var_exp = m_s.m_config.m_lookahead_cube_psat_var_exp; m_config.m_cube_psat_clause_base = m_s.m_config.m_lookahead_cube_psat_clause_base; m_config.m_cube_psat_trigger = m_s.m_config.m_lookahead_cube_psat_trigger; } void lookahead::collect_statistics(statistics& st) const { st.update("lh bool var", m_vprefix.size()); // TBD: keep count of ternary and >3-ary clauses. st.update("lh bca", m_stats.m_bca); st.update("lh add binary", m_stats.m_add_binary); st.update("lh del binary", m_stats.m_del_binary); st.update("lh propagations", m_stats.m_propagations); st.update("lh decisions", m_stats.m_decisions); st.update("lh windfalls", m_stats.m_windfall_binaries); st.update("lh double lookahead propagations", m_stats.m_double_lookahead_propagations); st.update("lh double lookahead rounds", m_stats.m_double_lookahead_rounds); } } z3-z3-4.8.7/src/sat/sat_lookahead.h000066400000000000000000000610111356505360400167530ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: sat_lookahead.h Abstract: Lookahead SAT solver in the style of March. Thanks also to the presentation in sat11.w. Author: Nikolaj Bjorner (nbjorner) 2017-2-11 Notes: --*/ #ifndef _SAT_LOOKAHEAD_H_ #define _SAT_LOOKAHEAD_H_ #include "util/small_object_allocator.h" #include "sat/sat_elim_eqs.h" namespace sat { struct pp_prefix { uint64_t m_prefix; unsigned m_depth; pp_prefix(uint64_t p, unsigned d) : m_prefix(p), m_depth(d) {} }; inline std::ostream& operator<<(std::ostream& out, pp_prefix const& p) { unsigned d = std::min(63u, p.m_depth); for (unsigned i = 0; i <= d; ++i) { if (0 != (p.m_prefix & (1ull << i))) out << "1"; else out << "0"; } if (d < p.m_depth) { out << " d:" << p.m_depth; } return out; } enum lookahead_mode { searching, // normal search lookahead1, // lookahead mode lookahead2 // double lookahead }; inline std::ostream& operator<<(std::ostream& out, lookahead_mode m) { switch (m) { case lookahead_mode::searching: return out << "searching"; case lookahead_mode::lookahead1: return out << "lookahead1"; case lookahead_mode::lookahead2: return out << "lookahead2"; default: break; } return out; } const double dbl_max = 100000000.0; class lookahead { solver& m_s; unsigned m_num_vars; reslimit m_rlimit; friend class ccc; friend class ba_solver; struct config { double m_dl_success; double m_alpha; double m_max_score; unsigned m_max_hlevel; unsigned m_min_cutoff; bool m_preselect; unsigned m_level_cand; double m_delta_rho; unsigned m_dl_max_iterations; unsigned m_tc1_limit; reward_t m_reward_type; cutoff_t m_cube_cutoff; unsigned m_cube_depth; double m_cube_fraction; double m_cube_freevars; double m_cube_psat_var_exp; double m_cube_psat_clause_base; double m_cube_psat_trigger; config() { memset(this, 0, sizeof(*this)); m_dl_success = 0.8; m_alpha = 3.5; m_max_score = 20.0; m_max_hlevel = 50; m_min_cutoff = 30; m_preselect = false; m_level_cand = 600; m_delta_rho = (double)0.7; m_dl_max_iterations = 2; m_tc1_limit = 10000000; m_reward_type = ternary_reward; m_cube_cutoff = adaptive_freevars_cutoff; m_cube_depth = 10; m_cube_fraction = 0.4; m_cube_freevars = 0.8; m_cube_psat_var_exp = 1.0; m_cube_psat_clause_base = 2.0; m_cube_psat_trigger = 5.0; } }; struct prefix { unsigned m_prefix; unsigned m_length; prefix(): m_prefix(0), m_length(0) {} }; struct lit_info { double m_lookahead_reward; unsigned m_double_lookahead; lit_info(): m_lookahead_reward(0), m_double_lookahead(0) {} }; struct stats { unsigned m_propagations; unsigned m_bca; unsigned m_add_binary; unsigned m_del_binary; unsigned m_decisions; unsigned m_windfall_binaries; unsigned m_double_lookahead_propagations; unsigned m_double_lookahead_rounds; stats() { reset(); } void reset() { memset(this, 0, sizeof(*this)); } }; struct binary { binary(literal u, literal v): m_u(u), m_v(v) {} literal m_u, m_v; }; class nary { unsigned m_size; // number of non-false literals size_t m_obj_size; // object size (counting all literals) literal m_head; // head literal literal m_literals[0]; // list of literals, put any true literal in head. size_t num_lits() const { return (m_obj_size - sizeof(nary)) / sizeof(literal); } public: static size_t get_obj_size(unsigned sz) { return sizeof(nary) + sz * sizeof(literal); } size_t obj_size() const { return m_obj_size; } nary(unsigned sz, literal const* lits): m_size(sz), m_obj_size(get_obj_size(sz)) { for (unsigned i = 0; i < sz; ++i) m_literals[i] = lits[i]; m_head = lits[0]; } unsigned size() const { return m_size; } unsigned dec_size() { SASSERT(m_size > 0); return --m_size; } void inc_size() { SASSERT(is_reduced()); ++m_size; } literal get_head() const { return m_head; } void set_head(literal l) { m_head = l; } bool is_reduced() const { return m_size < num_lits(); } literal operator[](unsigned i) { SASSERT(i < num_lits()); return m_literals[i]; } literal const* begin() const { return m_literals; } literal const* end() const { return m_literals + num_lits(); } // swap the true literal to the head. // void swap(unsigned i, unsigned j) { SASSERT(i < num_lits() && j < num_lits()); std::swap(m_literals[i], m_literals[j]); } }; struct cube_state { bool m_first; svector m_is_decision; literal_vector m_cube; double m_freevars_threshold; double m_psat_threshold; unsigned m_conflicts; unsigned m_cutoffs; unsigned m_backtracks; cube_state() { reset(); } void reset() { m_first = true; m_is_decision.reset(); m_cube.reset(); m_freevars_threshold = 0; m_psat_threshold = dbl_max; reset_stats(); } void reset_stats() { m_conflicts = 0; m_cutoffs = 0; m_backtracks = 0;} void inc_conflict() { ++m_conflicts; } void inc_cutoff() { ++m_cutoffs; } }; config m_config; double m_delta_trigger; double m_delta_decrease; double m_delta_fraction; literal_vector m_assumptions; literal_vector m_trail; // trail of units unsigned_vector m_trail_lim; vector m_binary; // literal: binary clauses unsigned_vector m_binary_trail; // trail of added binary clauses unsigned_vector m_binary_trail_lim; // specialized clause managemet uses ternary clauses and dedicated clause data-structure. // this replaces m_clauses below vector> m_ternary; // lit |-> vector of ternary clauses unsigned_vector m_ternary_count; // lit |-> current number of active ternary clauses for lit small_object_allocator m_allocator; vector> m_nary; // lit |-> vector of nary clauses ptr_vector m_nary_clauses; // vector of all nary clauses unsigned_vector m_nary_count; // lit |-> number of valid clause_id in m_nary[lit] unsigned m_num_tc1; unsigned_vector m_num_tc1_lim; unsigned m_qhead; // propagation queue head unsigned_vector m_qhead_lim; bool m_inconsistent; unsigned_vector m_bstamp; // literal: timestamp for binary implication vector > m_H; // literal: fitness score svector* m_heur; // current fitness svector m_rating; // var: pre-selection rating unsigned m_bstamp_id; // unique id for binary implication. unsigned m_istamp_id; // unique id for managing double lookaheads unsigned_vector m_stamp; // var: timestamp with truth value unsigned m_level; // current level, = 2 * m_trail_lim.size() const unsigned c_fixed_truth = UINT_MAX - 1; vector m_watches; // literal: watch structure svector m_lits; // literal: attributes. double m_lookahead_reward; // metric associated with current lookahead1 literal. literal_vector m_wstack; // windofall stack that is populated in lookahead1 mode unsigned m_last_prefix_length; uint64_t m_prefix; // where we are in search tree svector m_vprefix; // var: prefix where variable participates in propagation unsigned m_rating_throttle; // throttle to recompute rating indexed_uint_set m_freevars; unsigned m_init_freevars; lookahead_mode m_search_mode; // mode of search stats m_stats; model m_model; cube_state m_cube_state; unsigned m_max_ops; // cap number of operations used to compute lookahead reward. //scoped_ptr m_ext; // --------------------------------------- // truth values inline bool is_fixed_at(literal l, unsigned level) const { return m_stamp[l.var()] >= level; } inline bool is_fixed(literal l) const { return is_fixed_at(l, m_level); } inline bool is_undef(literal l) const { return !is_fixed(l); } inline bool is_undef(bool_var v) const { return m_stamp[v] < m_level; } inline bool is_false_at(literal l, unsigned level) const { return is_fixed_at(l, level) && (bool)((m_stamp[l.var()] & 0x1) ^ l.sign()); } // even iff l.sign() inline bool is_false(literal l) const { return is_false_at(l, m_level); } inline bool is_true_at(literal l, unsigned level) const { return is_fixed_at(l, level) && !(bool)((m_stamp[l.var()] & 0x1) ^ l.sign()); } inline bool is_true(literal l) const { return is_true_at(l, m_level); } inline void set_true(literal l) { m_stamp[l.var()] = m_level + l.sign(); } inline void set_undef(literal l) { m_stamp[l.var()] = 0; } inline unsigned get_level(literal l) const { return m_stamp[l.var()] & ~0x1; } void set_level(literal d, literal s) { m_stamp[d.var()] = get_level(s) + d.sign(); } lbool value(literal l) const { return is_undef(l) ? l_undef : is_true(l) ? l_true : l_false; } // set the level within a scope of the search. class scoped_level { lookahead& m_parent; unsigned m_save; public: scoped_level(lookahead& p, unsigned l): m_parent(p), m_save(p.m_level) { p.m_level = l; } ~scoped_level() { m_parent.m_level = m_save; } }; class scoped_ext { lookahead& p; public: scoped_ext(lookahead& p); ~scoped_ext(); }; class scoped_assumptions { lookahead& p; literal_vector lits; public: scoped_assumptions(lookahead& p, literal_vector const& lits); ~scoped_assumptions(); }; // ------------------------------------- // prefix updates. I use low order bits. void flip_prefix(); void prune_prefix(); /** length < trail_lim.size: - mask m_prefix and p wrt length - update if different. */ void update_prefix(literal l); bool active_prefix(bool_var x); // ---------------------------------------- void add_binary(literal l1, literal l2); void del_binary(unsigned idx); void validate_binary(literal l1, literal l2); // ------------------------------------- // track consequences of binary clauses // see also 72 - 79 in sat11.w void inc_bstamp(); void inc_istamp(); void set_bstamp(literal l) { m_bstamp[l.index()] = m_bstamp_id; } void set_bstamps(literal l); bool is_stamped(literal l) const { return m_bstamp[l.index()] == m_bstamp_id; } bool add_tc1(literal u, literal v); /** \brief main routine for adding a new binary clause dynamically. */ void try_add_binary(literal u, literal v); // ------------------------------------- // pre-selection // see also 91 - 102 sat11.w void pre_select(); struct candidate { bool_var m_var; double m_rating; candidate(bool_var v, double r): m_var(v), m_rating(r) {} }; svector m_candidates; uint_set m_select_lookahead_vars; double get_rating(bool_var v) const { return m_rating[v]; } double get_rating(literal l) const { return get_rating(l.var()); } bool select(unsigned level); void heap_sort(); void heapify(); void sift_down(unsigned j, unsigned sz); bool validate_heap_sort(); double init_candidates(unsigned level, bool newbies); std::ostream& display_candidates(std::ostream& out) const; bool is_unsat() const; bool is_sat() const; bool missed_propagation() const; bool missed_conflict() const; void init_pre_selection(unsigned level); void ensure_H(unsigned level); void h_scores(svector& h, svector& hp); void heule_schur_scores(); double heule_schur_score(literal l); void heule_unit_scores(); double heule_unit_score(literal l); void march_cu_scores(); double march_cu_score(literal l); double l_score(literal l, svector const& h, double factor, double sqfactor, double afactor); // ------------------------------------ // Implication graph // Compute implication ordering and strongly connected components. // sat11.w 103 - 114. struct arcs : public literal_vector {}; // Knuth uses a shared pool of fixed size for arcs. // Should it be useful we could use this approach too // by changing the arcs abstraction and associated functions. struct dfs_info { unsigned m_rank; unsigned m_height; literal m_parent; arcs m_next; unsigned m_nextp; literal m_link; literal m_min; literal m_vcomp; dfs_info() { reset(); } void reset() { m_rank = 0; m_height = 0; m_parent = null_literal; m_next.reset(); m_link = null_literal; m_min = null_literal; m_vcomp = null_literal; m_nextp = 0; } }; literal m_active; unsigned m_rank; unsigned m_rank_max; literal m_settled; vector m_dfs; void get_scc(); void init_scc(); void init_dfs_info(literal l); void init_arcs(literal l); void add_arc(literal u, literal v); bool has_arc(literal v) const { return m_dfs[v.index()].m_next.size() > m_dfs[v.index()].m_nextp; } arcs get_arcs(literal v) const { return m_dfs[v.index()].m_next; } literal pop_arc(literal u) { return m_dfs[u.index()].m_next[m_dfs[u.index()].m_nextp++]; } unsigned num_next(literal u) const { return m_dfs[u.index()].m_next.size(); } literal get_next(literal u, unsigned i) const { return m_dfs[u.index()].m_next[i]; } literal get_min(literal v) const { return m_dfs[v.index()].m_min; } unsigned get_rank(literal v) const { return m_dfs[v.index()].m_rank; } bool maxed_rank(literal v) const { return get_rank(v) >= m_rank_max; } unsigned get_height(literal v) const { return m_dfs[v.index()].m_height; } literal get_parent(literal u) const { return m_dfs[u.index()].m_parent; } literal get_link(literal u) const { return m_dfs[u.index()].m_link; } literal get_vcomp(literal u) const { return m_dfs[u.index()].m_vcomp; } void set_link(literal v, literal u) { m_dfs[v.index()].m_link = u; } void set_min(literal v, literal u) { m_dfs[v.index()].m_min = u; } void set_rank(literal v, unsigned r) { m_dfs[v.index()].m_rank = r; } void set_height(literal v, unsigned h) { m_dfs[v.index()].m_height = h; } void set_parent(literal v, literal p) { TRACE("scc", tout << v << " <- " << p << "\n";); m_dfs[v.index()].m_parent = p; } void set_vcomp(literal v, literal u) { m_dfs[v.index()].m_vcomp = u; } void get_scc(literal v); void activate_scc(literal l); void found_scc(literal v); std::ostream& display_dfs(std::ostream& out) const; std::ostream& display_dfs(std::ostream& out, literal l) const; std::ostream& display_scc(std::ostream& out) const; std::ostream& display_scc(std::ostream& out, literal l) const; // ------------------------------------ // lookahead forest // sat11.w 115-121 literal m_root_child; literal get_child(literal u) const; void set_child(literal v, literal u); void find_heights(); std::ostream& display_forest(std::ostream& out, literal l); struct literal_offset { literal m_lit; unsigned m_offset; literal_offset(literal l): m_lit(l), m_offset(0) {} }; svector m_lookahead; void set_offset(unsigned idx, unsigned offset) { m_lookahead[idx].m_offset = offset; } void set_lookahead(literal l) { m_lookahead.push_back(literal_offset(l)); } void construct_lookahead_table(); // ------------------------------------ // clause management watch_list& get_wlist(literal l) { return m_watches[l.index()]; } watch_list const& get_wlist(literal l) const { return m_watches[l.index()]; } // new clause management: void add_ternary(literal u, literal v, literal w); void propagate_ternary(literal l); lbool propagate_ternary(literal l1, literal l2); void remove_ternary(literal l, literal u, literal v); void restore_ternary(literal l); void propagate_external(literal l); void add_clause(clause const& c); void propagate_clauses_searching(literal l); void propagate_clauses_lookahead(literal l); void restore_clauses(literal l); void remove_clause(literal l, nary& n); void remove_clause_at(literal l, nary& n); // ------------------------------------ // initialization void init_var(bool_var v); void init(bool learned); void copy_clauses(clause_vector const& clauses, bool learned); nary * copy_clause(clause const& c); // ------------------------------------ // search void push(literal lit, unsigned level); void pop(); bool push_lookahead2(literal lit, unsigned level); unsigned push_lookahead1(literal lit, unsigned level); void pop_lookahead1(literal lit, unsigned num_units); void lookahead_backtrack(); double mix_diff(double l, double r) const; clause const& get_clause(watch_list::iterator it) const; bool is_nary_propagation(clause const& c, literal l) const; void propagate_clauses(literal l); void propagate_binary(literal l); void propagate(); literal choose(); literal choose_base(); void compute_lookahead_reward(); literal select_literal(); void update_binary_clause_reward(literal l1, literal l2); void update_nary_clause_reward(clause const& c); void set_lookahead_reward(literal l, double f) { m_lits[l.index()].m_lookahead_reward = f; } void inc_lookahead_reward(literal l, double f) { m_lits[l.index()].m_lookahead_reward += f; } double get_lookahead_reward(literal l) const { return m_lits[l.index()].m_lookahead_reward; } void reset_lookahead_reward(literal l); void update_lookahead_reward(literal l, unsigned level); bool dl_enabled(literal l) const { return m_lits[l.index()].m_double_lookahead != m_istamp_id; } void dl_disable(literal l) { m_lits[l.index()].m_double_lookahead = m_istamp_id; } bool dl_no_overflow(unsigned base) const { return base + 2 * m_lookahead.size() * static_cast(m_config.m_dl_max_iterations + 1) < c_fixed_truth; } unsigned do_double(literal l, unsigned& base); unsigned double_look(literal l, unsigned& base); void set_conflict() { TRACE("sat", tout << "conflict\n";); m_inconsistent = true; } bool inconsistent() const { return m_inconsistent; } unsigned scope_lvl() const { return m_trail_lim.size(); } bool in_reduced_clause(literal l); bool in_reduced_clause(bool_var v); void validate_assign(literal l); void assign(literal l); void propagated(literal l); bool backtrack(literal_vector& trail); bool backtrack(literal_vector& trail, svector & is_decision); lbool search(); void init_model(); std::ostream& display_binary(std::ostream& out) const; std::ostream& display_clauses(std::ostream& out) const; std::ostream& display_values(std::ostream& out) const; std::ostream& display_lookahead(std::ostream& out) const; std::ostream& display_cube(std::ostream& out, literal_vector const& cube) const; void display_search_string(); void init_search(); void checkpoint(); void init_config(); void normalize_parents(); void add_hyper_binary(); double psat_heur(); bool should_cutoff(unsigned depth); public: lookahead(solver& s) : m_s(s), m_num_vars(s.num_vars()), m_num_tc1(0), m_level(2), m_last_prefix_length(0), m_prefix(0), m_rating_throttle(0) { m_s.rlimit().push_child(&m_rlimit); init_config(); } ~lookahead() { m_s.rlimit().pop_child(); for (nary* n : m_nary_clauses) { m_allocator.deallocate(n->obj_size(), n); } } lbool check() { init_search(); return search(); } /** \brief create cubes to std-out in DIMACS form. The cubes are controlled using cut-depth and cut-fraction parameters. If cut-depth != 0, then it is used to control thedepth of cuts. Otherwise, cut-fraction gives an adaptive threshold for creating cuts. */ lbool cube(bool_var_vector& vars, literal_vector& lits, unsigned backtrack_level); void update_cube_statistics(statistics& st); /** \brief simplify set of clauses by extracting units from a lookahead at base level. */ void simplify(bool learned); std::ostream& display(std::ostream& out) const; std::ostream& display_summary(std::ostream& out) const; /** \brief display lookahead scores as a sequence of: \n */ void display_lookahead_scores(std::ostream& out); model const& get_model(); void collect_statistics(statistics& st) const; double literal_occs(literal l); double literal_big_occs(literal l); /** \brief retrieve clauses as one vector of literals. clauses are separated by null-literal */ void get_clauses(literal_vector& clauses, unsigned max_clause_size); sat::config const& get_config() const { return m_s.get_config(); } }; } #endif z3-z3-4.8.7/src/sat/sat_model_converter.cpp000066400000000000000000000410171356505360400205520ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_model_converter.cpp Abstract: Low level model converter for SAT solver. Author: Leonardo de Moura (leonardo) 2011-05-26. Revision History: --*/ #include "sat/sat_model_converter.h" #include "sat/sat_clause.h" #include "sat/sat_solver.h" #include "util/trace.h" namespace sat { model_converter::model_converter(): m_exposed_lim(0), m_solver(nullptr) { } model_converter::~model_converter() { } model_converter& model_converter::operator=(model_converter const& other) { copy(other); return *this; } bool model_converter::legal_to_flip(bool_var v) const { if (m_solver && m_solver->is_assumption(v)) { IF_VERBOSE(0, verbose_stream() << "flipping assumption v" << v << "\n";); UNREACHABLE(); throw solver_exception("flipping assumption"); } if (m_solver && m_solver->is_external(v) && m_solver->is_incremental()) { IF_VERBOSE(0, verbose_stream() << "flipping external v" << v << "\n";); UNREACHABLE(); throw solver_exception("flipping external"); } return !m_solver || !m_solver->is_assumption(v); } void model_converter::process_stack(model & m, literal_vector const& c, elim_stackv const& stack) const { SASSERT(!stack.empty()); unsigned sz = stack.size(); for (unsigned i = sz; i-- > 0; ) { unsigned csz = stack[i].first; literal lit = stack[i].second; bool sat = false; for (unsigned j = 0; !sat && j < csz; ++j) { sat = value_at(c[j], m) == l_true; } if (!sat) { VERIFY(legal_to_flip(lit.var())); m[lit.var()] = lit.sign() ? l_false : l_true; } } } void model_converter::operator()(model & m) const { bool first = false; literal_vector clause; for (unsigned i = m_entries.size(); i-- > m_exposed_lim; ) { entry const& e = m_entries[i]; bool_var v0 = e.var(); SASSERT(e.get_kind() != ELIM_VAR || v0 == null_bool_var || m[v0] == l_undef); // if e.get_kind() == BCE, then it might be the case that m[v] != l_undef, // and the following procedure flips its value. bool sat = false; bool var_sign = false; unsigned index = 0; clause.reset(); VERIFY(v0 == null_bool_var || legal_to_flip(v0)); for (literal l : e.m_clauses) { if (l == null_literal) { // end of clause VERIFY (sat || e.get_kind() != ATE); if (!sat && e.get_kind() != ATE && v0 != null_bool_var) { VERIFY(legal_to_flip(v0)); m[v0] = var_sign ? l_false : l_true; } elim_stack* st = e.m_elim_stack[index]; if (st) { process_stack(m, clause, st->stack()); } sat = false; VERIFY(!first || !m_solver || m_solver->check_clauses(m)); ++index; clause.reset(); continue; } clause.push_back(l); if (sat) continue; bool sign = l.sign(); bool_var v = l.var(); VERIFY(v < m.size()); if (v == v0) var_sign = sign; if (value_at(l, m) == l_true) sat = true; else if (!sat && v != v0 && m[v] == l_undef) { VERIFY(legal_to_flip(v)); // clause can be satisfied by assigning v. m[v] = sign ? l_false : l_true; sat = true; VERIFY(!first || !m_solver || m_solver->check_clauses(m)); } } DEBUG_CODE({ // all clauses must be satisfied bool sat = false; bool undef = false; for (literal const& l : e.m_clauses) { if (l == null_literal) { CTRACE("sat", !sat, tout << "exposed: " << m_exposed_lim << "\n"; if (m_solver) m_solver->display(tout); display(tout); for (unsigned v = 0; v < m.size(); ++v) tout << v << ": " << m[v] << "\n"; for (literal const& l2 : e.m_clauses) { if (l2 == null_literal) tout << "\n"; else tout << l2 << " "; if (&l == &l2) break; } ); SASSERT(sat || undef); sat = false; undef = false; continue; } if (sat) continue; switch (value_at(l, m)) { case l_undef: undef = true; break; case l_true: sat = true; break; default: break; } } }); } } /** \brief Test if after applying the model converter, all eliminated clauses are satisfied by m. */ bool model_converter::check_model(model const & m) const { bool ok = true; for (entry const & e : m_entries) { bool sat = false; literal_vector::const_iterator it = e.m_clauses.begin(); literal_vector::const_iterator itbegin = it; literal_vector::const_iterator end = e.m_clauses.end(); for (; it != end; ++it) { literal l = *it; if (l == null_literal) { // end of clause if (!sat) { TRACE("sat_model_bug", tout << "failed eliminated: " << mk_lits_pp(static_cast(it - itbegin), itbegin) << "\n";); ok = false; } sat = false; itbegin = it; itbegin++; continue; } if (sat) continue; if (value_at(l, m) == l_true) sat = true; } } return ok; } model_converter::entry & model_converter::mk(kind k, bool_var v) { m_entries.push_back(entry(k, v)); entry & e = m_entries.back(); SASSERT(e.var() == v); SASSERT(e.get_kind() == k); VERIFY(v == null_bool_var || legal_to_flip(v)); return e; } void model_converter::add_ate(clause const& c) { if (stackv().empty()) return; insert(mk(ATE, null_bool_var), c); } void model_converter::add_ate(literal_vector const& lits) { if (stackv().empty()) return; insert(mk(ATE, null_bool_var), lits); } void model_converter::add_ate(literal l1, literal l2) { if (stackv().empty()) return; insert(mk(ATE, null_bool_var), l1, l2); } void model_converter::add_elim_stack(entry & e) { e.m_elim_stack.push_back(stackv().empty() ? nullptr : alloc(elim_stack, stackv())); // VERIFY(for (auto const& s : stackv()) VERIFY(legal_to_flip(s.second.var()));); stackv().reset(); } void model_converter::set_clause(entry & e, literal l1, literal l2) { e.m_clause.push_back(l1); e.m_clause.push_back(l2); } void model_converter::set_clause(entry & e, clause const & c) { e.m_clause.append(c.size(), c.begin()); } void model_converter::insert(entry & e, clause const & c) { SASSERT(c.contains(e.var())); SASSERT(m_entries.begin() <= &e); SASSERT(&e < m_entries.end()); for (literal l : c) e.m_clauses.push_back(l); e.m_clauses.push_back(null_literal); add_elim_stack(e); TRACE("sat_mc_bug", tout << "adding: " << c << "\n";); } void model_converter::insert(entry & e, literal l1, literal l2) { SASSERT(l1.var() == e.var() || l2.var() == e.var()); SASSERT(m_entries.begin() <= &e); SASSERT(&e < m_entries.end()); e.m_clauses.push_back(l1); e.m_clauses.push_back(l2); e.m_clauses.push_back(null_literal); add_elim_stack(e); TRACE("sat_mc_bug", tout << "adding (binary): " << l1 << " " << l2 << "\n";); } void model_converter::insert(entry & e, clause_wrapper const & c) { SASSERT(c.contains(e.var())); SASSERT(m_entries.begin() <= &e); SASSERT(&e < m_entries.end()); unsigned sz = c.size(); for (unsigned i = 0; i < sz; ++i) e.m_clauses.push_back(c[i]); e.m_clauses.push_back(null_literal); add_elim_stack(e); // TRACE("sat_mc_bug", tout << "adding (wrapper): "; for (literal l : c) tout << l << " "; tout << "\n";); } void model_converter::insert(entry & e, literal_vector const& c) { 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); e.m_clauses.push_back(null_literal); add_elim_stack(e); TRACE("sat_mc_bug", tout << "adding: " << c << "\n";); } bool model_converter::check_invariant(unsigned num_vars) const { // After a variable v occurs in an entry n and the entry has kind ELIM_VAR, // then the variable must not occur in any other entry occurring after it. vector::const_iterator it = m_entries.begin(); vector::const_iterator end = m_entries.end(); for (; it != end; ++it) { SASSERT(it->var() == null_bool_var || it->var() < num_vars); if (it->get_kind() == ELIM_VAR) { svector::const_iterator it2 = it; it2++; for (; it2 != end; ++it2) { SASSERT(it2->var() != it->var()); if (it2->var() == it->var()) return false; for (literal l : it2->m_clauses) { CTRACE("sat_model_converter", l.var() == it->var(), tout << "var: " << it->var() << "\n"; display(tout);); SASSERT(l.var() != it->var()); VERIFY(l == null_literal || l.var() < num_vars); if (it2->var() == it->var()) return false; } } } } return true; } void model_converter::display(std::ostream & out) const { out << "(sat::model-converter\n"; bool first = true; for (auto & entry : m_entries) { if (first) first = false; else out << "\n"; display(out, entry); } out << ")\n"; } std::ostream& model_converter::display(std::ostream& out, entry const& entry) const { out << " (" << entry.get_kind() << " "; if (entry.var() != null_bool_var) out << entry.var(); bool start = true; unsigned index = 0; for (literal l : entry.m_clauses) { if (start) { out << "\n ("; start = false; } else { if (l != null_literal) out << " "; } if (l == null_literal) { out << ")"; start = true; elim_stack* st = entry.m_elim_stack[index]; if (st) { elim_stackv const& stack = st->stack(); unsigned sz = stack.size(); for (unsigned i = sz; i-- > 0; ) { out << "\n " << stack[i].first << " " << stack[i].second; } } ++index; continue; } out << l; } out << ")"; return out; } void model_converter::copy(model_converter const & src) { m_entries.reset(); m_entries.append(src.m_entries); m_exposed_lim = src.m_exposed_lim; } void model_converter::flush(model_converter & src) { VERIFY(this != &src); m_entries.append(src.m_entries); m_exposed_lim = src.m_exposed_lim; src.m_entries.reset(); src.m_exposed_lim = 0; } void model_converter::collect_vars(bool_var_set & s) const { for (entry const & e : m_entries) { s.insert(e.m_var); } } unsigned model_converter::max_var(unsigned min) const { unsigned result = min; for (entry const& e : m_entries) { for (literal l : e.m_clauses) { if (l != null_literal) { if (l.var() != null_bool_var && l.var() > result) result = l.var(); } } } return result; } void model_converter::swap(bool_var v, unsigned sz, literal_vector& clause) { for (unsigned j = 0; j < sz; ++j) { if (v == clause[j].var()) { std::swap(clause[0], clause[j]); return; } } IF_VERBOSE(0, verbose_stream() << "not found: v" << v << " " << clause << "\n";); UNREACHABLE(); } void model_converter::expand(literal_vector& update_stack) { sat::literal_vector clause; for (unsigned i = m_exposed_lim; i < m_entries.size(); ++i) { entry const& e = m_entries[i]; unsigned index = 0; clause.reset(); for (literal l : e.m_clauses) { if (l == null_literal) { elim_stack* st = e.m_elim_stack[index]; if (st) { // clause sizes increase, so we can always swap // the blocked literal to the front from the prefix. for (auto const& p : st->stack()) { unsigned csz = p.first; literal lit = p.second; swap(lit.var(), csz, clause); update_stack.append(csz, clause.c_ptr()); update_stack.push_back(null_literal); } } if (e.var() != null_bool_var) { swap(e.var(), clause.size(), clause); update_stack.append(clause); update_stack.push_back(null_literal); } clause.reset(); } else { clause.push_back(l); } } } m_exposed_lim = m_entries.size(); } void model_converter::init_search(solver& s) { #if 0 unsigned j = 0, k = 0; literal_vector clause; for (unsigned i = 0; i < m_entries.size(); ++i) { entry & e = m_entries[i]; if (!m_mark[e.var()]) { m_entries[j++] = e; if (i < m_exposed_lim) k++; continue; } clause.reset(); // For covered clauses we record the original clause. The role of m_clauses is to record ALA // tautologies and are not part of the clause that is removed. if (!e.m_clause.empty()) { clause.append(e.m_clause); s.mk_clause(clause.size(), clause.c_ptr(), false); continue; } for (literal lit : e.m_clauses) { if (lit == null_literal) { s.mk_clause(clause.size(), clause.c_ptr(), false); clause.reset(); } else { clause.push_back(lit); } } } m_entries.shrink(j); m_exposed_lim = k; for (bool& m : m_mark) { m = false; } #endif } void model_converter::add_clause(unsigned n, literal const* lits) { if (m_entries.empty()) { return; } // TBD: we just mark variables instead of literals because entries don't have directly literal information. for (unsigned i = 0; i < n; ++i) { m_mark.reserve(lits[i].var() + 1); m_mark[lits[i].var()] = true; } } }; z3-z3-4.8.7/src/sat/sat_model_converter.h000066400000000000000000000131441356505360400202170ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_model_converter.h Abstract: Low level model converter for SAT solver. Author: Leonardo de Moura (leonardo) 2011-05-26. Revision History: --*/ #ifndef SAT_MODEL_CONVERTER_H_ #define SAT_MODEL_CONVERTER_H_ #include "sat/sat_types.h" #include "util/ref_vector.h" namespace sat { /** \brief Stores eliminated variables and Blocked clauses. It uses these clauses to extend/patch the model produced for the simplified CNF formula. This information may also be used to support incremental solving. If new clauses are asserted into the SAT engine, then we can restore the state by re-asserting all clauses in the model converter. This is a low-level model_converter. Given a mapping from bool_var to expr, it can be converted into a general Z3 model_converter */ class solver; static unsigned counter = 0; class model_converter { public: typedef svector> elim_stackv; class elim_stack { unsigned m_counter; unsigned m_refcount; elim_stackv m_stack; elim_stack(elim_stack const& ); public: elim_stack(elim_stackv const& stack): m_counter(0), m_refcount(0), m_stack(stack) { m_counter = ++counter; } ~elim_stack() { } void inc_ref() { ++m_refcount; } void dec_ref() { if (0 == --m_refcount) { dealloc(this); } } elim_stackv const& stack() const { return m_stack; } unsigned ref_count() const { return m_refcount; } }; enum kind { ELIM_VAR = 0, BCE, CCE, ACCE, ABCE, ATE }; class entry { friend class model_converter; bool_var m_var; kind m_kind; literal_vector m_clauses; // the different clauses are separated by null_literal literal_vector m_clause; // original clause in case of CCE sref_vector m_elim_stack; entry(kind k, bool_var v): m_var(v), m_kind(k) {} public: entry(entry const & src): m_var(src.m_var), m_kind(src.m_kind), m_clauses(src.m_clauses), m_clause(src.m_clause) { m_elim_stack.append(src.m_elim_stack); } bool_var var() const { return m_var; } kind get_kind() const { return m_kind; } }; private: vector m_entries; // entries accumulated during SAT search unsigned m_exposed_lim; // last entry that was exposed to model converter. svector m_mark; // literals that are used in asserted clauses. solver const* m_solver; elim_stackv m_elim_stack; void process_stack(model & m, literal_vector const& clause, elim_stackv const& stack) const; std::ostream& display(std::ostream & out, entry const& entry) const; bool legal_to_flip(bool_var v) const; void swap(bool_var v, unsigned sz, literal_vector& clause); void add_elim_stack(entry & e); public: model_converter(); ~model_converter(); void set_solver(solver const* s) { m_solver = s; } void operator()(model & m) const; model_converter& operator=(model_converter const& other); elim_stackv& stackv() { return m_elim_stack; } entry & mk(kind k, bool_var v); void insert(entry & e, clause const & c); void insert(entry & e, literal l1, literal l2); void insert(entry & e, clause_wrapper const & c); void insert(entry & c, literal_vector const& covered_clause); void set_clause(entry & e, literal l1, literal l2); void set_clause(entry & e, clause const & c); void add_ate(literal_vector const& lits); void add_ate(literal l1, literal l2); void add_ate(clause const& c); bool empty() const { return m_entries.empty(); } unsigned size() const { return m_entries.size(); } void init_search(solver& s); void add_clause(unsigned n, literal const* lits); bool check_invariant(unsigned num_vars) const; void display(std::ostream & out) const; bool check_model(model const & m) const; void copy(model_converter const & src); /* \brief Append entries from src, then remove entries in src. */ void flush(model_converter& src); void collect_vars(bool_var_set & s) const; unsigned max_var(unsigned min) const; /* * \brief expand entries to a list of clauses, such that * the first literal in each clause is the literal whose * truth value is updated as follows: * * lit0 := lit0 or (~lit1 & ~lit2 & ... & ~lit_k) * */ void expand(literal_vector& update_stack); }; inline std::ostream& operator<<(std::ostream& out, model_converter::kind k) { switch (k) { case model_converter::ELIM_VAR: out << "elim"; break; case model_converter::BCE: out << "bce"; break; case model_converter::CCE: out << "cce"; break; case model_converter::ACCE: out << "acce"; break; case model_converter::ABCE: out << "abce"; break; case model_converter::ATE: out << "ate"; break; } return out; } }; #endif z3-z3-4.8.7/src/sat/sat_mus.cpp000066400000000000000000000163251356505360400161730ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: sat_mus.cpp Abstract: Faster MUS extraction Author: Nikolaj Bjorner (nbjorner) 2014-20-7 Notes: --*/ #include "sat/sat_solver.h" #include "sat/sat_mus.h" namespace sat { mus::mus(solver& s):s(s), m_is_active(false), m_max_num_restarts(UINT_MAX) {} mus::~mus() {} void mus::reset() { m_core.reset(); m_mus.reset(); m_model.reset(); } void mus::set_core() { m_mus.append(m_core); s.m_core.reset(); s.m_core.append(m_mus); TRACE("sat", tout << "new core: " << s.m_core << "\n";); } void mus::update_model() { if (m_model.empty()) { m_model.append(s.m_model); } } lbool mus::operator()() { m_max_num_restarts = s.m_config.m_core_minimize_partial ? s.num_restarts() + 10 : UINT_MAX; flet _disable_min(s.m_config.m_core_minimize, false); flet _is_active(m_is_active, true); IF_VERBOSE(3, verbose_stream() << "(sat.mus size: " << s.get_core().size() << " core: [" << s.get_core() << "])\n";); reset(); lbool r = mus1(); return r; } lbool mus::mus1() { bool minimize_partial = s.m_config.m_core_minimize_partial; TRACE("sat", tout << "old core: " << s.get_core() << "\n";); literal_vector& core = get_core(); literal_vector& mus = m_mus; if (!minimize_partial && core.size() > 64) { return mus2(); } while (!core.empty()) { IF_VERBOSE(1, verbose_stream() << "(sat.mus num-to-process: " << core.size() << " mus: " << mus.size(); if (minimize_partial) verbose_stream() << " max-restarts: " << m_max_num_restarts; verbose_stream() << ")\n";); TRACE("sat", tout << "core: " << core << "\n"; tout << "mus: " << mus << "\n";); if (s.canceled()) { set_core(); return l_undef; } unsigned num_literals = core.size() + mus.size(); if (num_literals <= 2) { // IF_VERBOSE(0, verbose_stream() << "num literals: " << core << " " << mus << "\n";); break; } literal lit = core.back(); core.pop_back(); lbool is_sat; { flet _restart_bound(s.m_config.m_restart_max, m_max_num_restarts); scoped_append _sa(mus, core); mus.push_back(~lit); is_sat = s.check(mus.size(), mus.c_ptr()); TRACE("sat", tout << "mus: " << mus << "\n";); } IF_VERBOSE(1, verbose_stream() << "(sat.mus " << is_sat << ")\n";); switch (is_sat) { case l_undef: if (!s.canceled()) { // treat restart max as sat, so literal is in the mus mus.push_back(lit); } else { core.push_back(lit); set_core(); return l_undef; } break; case l_true: { SASSERT(value_at(lit, s.get_model()) == l_false); mus.push_back(lit); update_model(); break; } case l_false: literal_vector const& new_core = s.get_core(); if (new_core.contains(~lit)) { IF_VERBOSE(3, verbose_stream() << "(sat.mus unit reduction, literal is in both cores " << lit << ")\n";); } else { TRACE("sat", tout << "core: " << new_core << " mus: " << mus << "\n";); core.reset(); for (unsigned i = 0; i < new_core.size(); ++i) { literal lit = new_core[i]; if (!mus.contains(lit)) { core.push_back(lit); } } } break; } } set_core(); IF_VERBOSE(3, verbose_stream() << "(sat.mus.new " << s.m_core << ")\n";); return l_true; } // bisection search. lbool mus::mus2() { literal_set core(get_core()); literal_set support; lbool is_sat = qx(core, support, false); s.m_core.reset(); s.m_core.append(core.to_vector()); IF_VERBOSE(3, verbose_stream() << "(sat.mus.new " << s.m_core << ")\n";); return is_sat; } lbool mus::qx(literal_set& assignment, literal_set& support, bool has_support) { lbool is_sat = l_true; if (has_support) { scoped_append _sa(m_mus, support.to_vector()); is_sat = s.check(m_mus.size(), m_mus.c_ptr()); switch (is_sat) { case l_false: { literal_set core(s.get_core()); support &= core; assignment.reset(); return l_true; } case l_undef: return l_undef; case l_true: update_model(); break; default: break; } } if (assignment.size() == 1) { return l_true; } literal_set assign2; split(assignment, assign2); support |= assignment; is_sat = qx(assign2, support, !assignment.empty()); unsplit(support, assignment); if (is_sat != l_true) return is_sat; support |= assign2; is_sat = qx(assignment, support, !assign2.empty()); assignment |= assign2; unsplit(support, assign2); return is_sat; } void mus::unsplit(literal_set& A, literal_set& B) { literal_set A1, B1; literal_set::iterator it = A.begin(), end = A.end(); for (; it != end; ++it) { if (B.contains(*it)) { B1.insert(*it); } else { A1.insert(*it); } } A = A1; B = B1; } void mus::split(literal_set& lits1, literal_set& lits2) { unsigned half = lits1.size()/2; literal_set lits3; literal_set::iterator it = lits1.begin(), end = lits1.end(); for (unsigned i = 0; it != end; ++it, ++i) { if (i < half) { lits3.insert(*it); } else { lits2.insert(*it); } } lits1 = lits3; } literal_vector& mus::get_core() { m_core.reset(); m_mus.reset(); literal_vector& core = m_core; core.append(s.get_core()); for (unsigned i = 0; i < core.size(); ++i) { if (s.m_user_scope_literals.contains(core[i])) { m_mus.push_back(core[i]); core[i] = core.back(); core.pop_back(); --i; } } return core; } void mus::verify_core(literal_vector const& core) { lbool is_sat = s.check(core.size(), core.c_ptr()); IF_VERBOSE(3, verbose_stream() << "core verification: " << is_sat << " " << core << "\n";); } } z3-z3-4.8.7/src/sat/sat_mus.h000066400000000000000000000031731356505360400156350ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: mus.h Abstract: Faster MUS extraction based on Belov et.al. HYB (Algorithm 3, 4) Author: Nikolaj Bjorner (nbjorner) 2014-20-7 Notes: --*/ #ifndef SAT_MUS_H_ #define SAT_MUS_H_ namespace sat { class mus { solver& s; literal_vector m_core; literal_vector m_mus; bool m_is_active; model m_model; // model obtained during minimal unsat core unsigned m_max_num_restarts; public: mus(solver& s); ~mus(); lbool operator()(); bool is_active() const { return m_is_active; } model const& get_model() const { return m_model; } private: lbool mus1(); lbool mus2(); lbool qx(literal_set& assignment, literal_set& support, bool has_support); void reset(); void set_core(); void update_model(); literal_vector & get_core(); void verify_core(literal_vector const& lits); void split(literal_set& src, literal_set& dst); void intersect(literal_set& dst, literal_set const& src); void unsplit(literal_set& A, literal_set& B); class scoped_append { unsigned m_size; literal_vector& m_lits; public: scoped_append(literal_vector& lits, literal_vector const& other): m_size(lits.size()), m_lits(lits) { m_lits.append(other); } ~scoped_append() { m_lits.resize(m_size); } }; }; }; #endif z3-z3-4.8.7/src/sat/sat_parallel.cpp000066400000000000000000000226611356505360400171630ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: sat_parallel.cpp Abstract: Utilities for parallel SAT solving. Author: Nikolaj Bjorner (nbjorner) 2017-1-29. Revision History: --*/ #include "sat/sat_parallel.h" #include "sat/sat_clause.h" #include "sat/sat_solver.h" namespace sat { void parallel::vector_pool::next(unsigned& index) { SASSERT(index < m_size); unsigned n = index + 2 + get_length(index); if (n >= m_size) { index = 0; } else { index = n; } } void parallel::vector_pool::reserve(unsigned num_threads, unsigned sz) { m_vectors.reset(); m_vectors.resize(sz, 0); m_heads.reset(); m_heads.resize(num_threads, 0); m_at_end.reset(); m_at_end.resize(num_threads, true); m_tail = 0; m_size = sz; } void parallel::vector_pool::begin_add_vector(unsigned owner, unsigned n) { SASSERT(m_tail < m_size); unsigned capacity = n + 2; m_vectors.reserve(m_size + capacity, 0); IF_VERBOSE(3, verbose_stream() << owner << ": begin-add " << n << " tail: " << m_tail << " size: " << m_size << "\n";); for (unsigned i = 0; i < m_heads.size(); ++i) { while (m_tail < m_heads[i] && m_heads[i] < m_tail + capacity) { next(m_heads[i]); } m_at_end[i] = false; } m_vectors[m_tail++] = owner; m_vectors[m_tail++] = n; } void parallel::vector_pool::add_vector_elem(unsigned e) { m_vectors[m_tail++] = e; } void parallel::vector_pool::end_add_vector() { if (m_tail >= m_size) { m_tail = 0; } } bool parallel::vector_pool::get_vector(unsigned owner, unsigned& n, unsigned const*& ptr) { unsigned head = m_heads[owner]; unsigned iterations = 0; while (head != m_tail || !m_at_end[owner]) { ++iterations; SASSERT(head < m_size && m_tail < m_size); bool is_self = owner == get_owner(head); next(m_heads[owner]); IF_VERBOSE(static_cast(iterations > m_size ? 0 : 3), verbose_stream() << owner << ": [" << head << ":" << m_heads[owner] << "] tail: " << m_tail << "\n";); m_at_end[owner] = (m_heads[owner] == m_tail); if (!is_self) { n = get_length(head); ptr = get_ptr(head); return true; } head = m_heads[owner]; } return false; } parallel::parallel(solver& s): m_num_clauses(0), m_consumer_ready(false), m_scoped_rlimit(s.rlimit()) {} parallel::~parallel() { for (unsigned i = 0; i < m_solvers.size(); ++i) { dealloc(m_solvers[i]); } } void parallel::init_solvers(solver& s, unsigned num_extra_solvers) { unsigned num_threads = num_extra_solvers + 1; m_solvers.resize(num_extra_solvers); symbol saved_phase = s.m_params.get_sym("phase", symbol("caching")); for (unsigned i = 0; i < num_extra_solvers; ++i) { m_limits.push_back(reslimit()); } for (unsigned i = 0; i < num_extra_solvers; ++i) { s.m_params.set_uint("random_seed", s.m_rand()); if (i == 1 + num_threads/2) { 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, true); m_solvers[i]->set_par(this, i); push_child(m_solvers[i]->rlimit()); } s.set_par(this, num_extra_solvers); s.m_params.set_sym("phase", saved_phase); } void parallel::push_child(reslimit& rl) { m_scoped_rlimit.push_child(&rl); } void parallel::exchange(solver& s, literal_vector const& in, unsigned& limit, literal_vector& out) { if (s.get_config().m_num_threads == 1 || s.m_par_syncing_clauses) return; flet _disable_sync_clause(s.m_par_syncing_clauses, true); { std::lock_guard lock(m_mux); if (limit < m_units.size()) { // this might repeat some literals. out.append(m_units.size() - limit, m_units.c_ptr() + limit); } for (unsigned i = 0; i < in.size(); ++i) { literal lit = in[i]; if (!m_unit_set.contains(lit.index())) { m_unit_set.insert(lit.index()); m_units.push_back(lit); } } limit = m_units.size(); } } void parallel::share_clause(solver& s, literal l1, literal l2) { if (s.get_config().m_num_threads == 1 || s.m_par_syncing_clauses) return; flet _disable_sync_clause(s.m_par_syncing_clauses, true); IF_VERBOSE(3, verbose_stream() << s.m_par_id << ": share " << l1 << " " << l2 << "\n";); { std::lock_guard lock(m_mux); m_pool.begin_add_vector(s.m_par_id, 2); m_pool.add_vector_elem(l1.index()); m_pool.add_vector_elem(l2.index()); m_pool.end_add_vector(); } } void parallel::share_clause(solver& s, clause const& c) { if (s.get_config().m_num_threads == 1 || !enable_add(c) || s.m_par_syncing_clauses) return; flet _disable_sync_clause(s.m_par_syncing_clauses, true); unsigned n = c.size(); unsigned owner = s.m_par_id; IF_VERBOSE(3, verbose_stream() << owner << ": share " << c << "\n";); std::lock_guard lock(m_mux); m_pool.begin_add_vector(owner, n); for (unsigned i = 0; i < n; ++i) { m_pool.add_vector_elem(c[i].index()); } m_pool.end_add_vector(); } void parallel::get_clauses(solver& s) { if (s.m_par_syncing_clauses) return; flet _disable_sync_clause(s.m_par_syncing_clauses, true); std::lock_guard lock(m_mux); _get_clauses(s); } void parallel::_get_clauses(solver& s) { unsigned n; unsigned const* ptr; unsigned owner = s.m_par_id; while (m_pool.get_vector(owner, n, ptr)) { m_lits.reset(); bool usable_clause = true; for (unsigned i = 0; usable_clause && i < n; ++i) { literal lit(to_literal(ptr[i])); m_lits.push_back(lit); usable_clause = lit.var() <= s.m_par_num_vars && !s.was_eliminated(lit.var()); } IF_VERBOSE(3, verbose_stream() << s.m_par_id << ": retrieve " << m_lits << "\n";); SASSERT(n >= 2); if (usable_clause) { s.mk_clause_core(m_lits.size(), m_lits.c_ptr(), true); } } } bool parallel::enable_add(clause const& c) const { // plingeling, glucose heuristic: return (c.size() <= 40 && c.glue() <= 8) || c.glue() <= 2; } void parallel::_from_solver(solver& s) { if (m_consumer_ready && (m_num_clauses == 0 || (m_num_clauses > s.m_clauses.size()))) { // time to update local search with new clauses. // 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, true); m_num_clauses = s.m_clauses.size(); } } bool parallel::_to_solver(solver& s) { if (m_priorities.empty()) { return false; } for (bool_var v = 0; v < m_priorities.size(); ++v) { s.update_activity(v, m_priorities[v]); } return true; } void parallel::from_solver(solver& s) { std::lock_guard lock(m_mux); _from_solver(s); } bool parallel::to_solver(solver& s) { std::lock_guard lock(m_mux); return _to_solver(s); } void parallel::_to_solver(i_local_search& s) { m_priorities.reset(); for (bool_var v = 0; m_solver_copy && v < m_solver_copy->num_vars(); ++v) { m_priorities.push_back(s.get_priority(v)); } } bool parallel::_from_solver(i_local_search& s) { bool copied = false; m_consumer_ready = true; if (m_solver_copy) { copied = true; s.reinit(*m_solver_copy.get()); } return copied; } bool parallel::from_solver(i_local_search& s) { std::lock_guard lock(m_mux); return _from_solver(s); } void parallel::to_solver(i_local_search& s) { std::lock_guard lock(m_mux); _to_solver(s); } bool parallel::copy_solver(solver& s) { bool copied = false; { std::lock_guard lock(m_mux); m_consumer_ready = true; if (m_solver_copy && s.m_clauses.size() > m_solver_copy->m_clauses.size()) { s.copy(*m_solver_copy, true); copied = true; m_num_clauses = s.m_clauses.size(); } } return copied; } }; z3-z3-4.8.7/src/sat/sat_parallel.h000066400000000000000000000063241356505360400166260ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: sat_parallel.h Abstract: Utilities for parallel SAT solving. Author: Nikolaj Bjorner (nbjorner) 2017-1-29. Revision History: --*/ #ifndef SAT_PARALLEL_H_ #define SAT_PARALLEL_H_ #include "sat/sat_types.h" #include "util/hashtable.h" #include "util/map.h" #include "util/rlimit.h" #include "util/scoped_ptr_vector.h" #include namespace sat { class parallel { // shared pool of learned clauses. class vector_pool { unsigned_vector m_vectors; unsigned m_size; unsigned m_tail; unsigned_vector m_heads; svector m_at_end; void next(unsigned& index); unsigned get_owner(unsigned index) const { return m_vectors[index]; } unsigned get_length(unsigned index) const { return m_vectors[index+1]; } unsigned const* get_ptr(unsigned index) const { return m_vectors.c_ptr() + index + 2; } public: vector_pool() {} void reserve(unsigned num_owners, unsigned sz); void begin_add_vector(unsigned owner, unsigned n); void end_add_vector(); void add_vector_elem(unsigned e); bool get_vector(unsigned owner, unsigned& n, unsigned const*& ptr); }; bool enable_add(clause const& c) const; void _get_clauses(solver& s); void _from_solver(solver& s); bool _to_solver(solver& s); bool _from_solver(i_local_search& s); void _to_solver(i_local_search& s); typedef hashtable index_set; literal_vector m_units; index_set m_unit_set; literal_vector m_lits; vector_pool m_pool; std::mutex m_mux; // for exchange with local search: unsigned m_num_clauses; scoped_ptr m_solver_copy; bool m_consumer_ready; svector m_priorities; scoped_limits m_scoped_rlimit; vector m_limits; ptr_vector m_solvers; public: parallel(solver& s); ~parallel(); void init_solvers(solver& s, unsigned num_extra_solvers); void push_child(reslimit& rl); // reserve space void reserve(unsigned num_owners, unsigned sz) { m_pool.reserve(num_owners, sz); } solver& get_solver(unsigned i) { return *m_solvers[i]; } void cancel_solver(unsigned i) { m_limits[i].cancel(); } // exchange unit literals void exchange(solver& s, literal_vector const& in, unsigned& limit, literal_vector& out); // add clause to shared clause pool void share_clause(solver& s, clause const& c); void share_clause(solver& s, literal l1, literal l2); // receive clauses from shared clause pool void get_clauses(solver& s); // exchange from solver state to local search and back. void from_solver(solver& s); bool to_solver(solver& s); bool from_solver(i_local_search& s); void to_solver(i_local_search& s); bool copy_solver(solver& s); }; }; #endif z3-z3-4.8.7/src/sat/sat_params.pyg000066400000000000000000000276651356505360400167000ustar00rootroot00000000000000def_module_params('sat', export=True, description='propositional SAT solver', params=(max_memory_param(), ('phase', SYMBOL, 'caching', 'phase selection strategy: always_false, always_true, basic_caching, random, caching'), ('phase.sticky', BOOL, True, 'use sticky phase caching'), ('search.unsat.conflicts', UINT, 400, 'period for solving for unsat (in number of conflicts)'), ('search.sat.conflicts', UINT, 400, 'period for solving for sat (in number of conflicts)'), ('rephase.base', UINT, 1000, 'number of conflicts per rephase '), ('reorder.base', UINT, UINT_MAX, 'number of conflicts per random reorder '), ('reorder.itau', DOUBLE, 4.0, 'inverse temperature for softmax'), ('reorder.activity_scale', UINT, 100, 'scaling factor for activity update'), ('propagate.prefetch', BOOL, True, 'prefetch watch lists for assigned literals'), ('restart', SYMBOL, 'ema', 'restart strategy: static, luby, ema or geometric'), ('restart.initial', UINT, 2, 'initial restart (number of conflicts)'), ('restart.max', UINT, UINT_MAX, 'maximal number of restarts.'), ('restart.fast', BOOL, True, 'use fast restart approach only removing less active literals.'), ('restart.factor', DOUBLE, 1.5, 'restart increment factor for geometric strategy'), ('restart.margin', DOUBLE, 1.1, 'margin between fast and slow restart factors. For ema'), ('restart.emafastglue', DOUBLE, 3e-2, 'ema alpha factor for fast moving average'), ('restart.emaslowglue', DOUBLE, 1e-5, 'ema alpha factor for slow moving average'), ('variable_decay', UINT, 110, 'multiplier (divided by 100) for the VSIDS activity increment'), ('inprocess.max', UINT, UINT_MAX, 'maximal number of inprocessing passes'), ('branching.heuristic', SYMBOL, 'vsids', 'branching heuristic vsids, lrb or chb'), ('branching.anti_exploration', BOOL, False, 'apply anti-exploration heuristic for branch selection'), ('random_freq', DOUBLE, 0.01, 'frequency of random case splits'), ('random_seed', UINT, 0, 'random seed'), ('burst_search', UINT, 100, 'number of conflicts before first global simplification'), ('max_conflicts', UINT, UINT_MAX, 'maximum number of conflicts'), ('gc', SYMBOL, 'glue_psm', 'garbage collection strategy: psm, glue, glue_psm, dyn_psm'), ('gc.initial', UINT, 20000, 'learned clauses garbage collection frequency'), ('gc.increment', UINT, 500, 'increment to the garbage collection threshold'), ('gc.small_lbd', UINT, 3, 'learned clauses with small LBD are never deleted (only used in dyn_psm)'), ('gc.k', UINT, 7, 'learned clauses that are inactive for k gc rounds are permanently deleted (only used in dyn_psm)'), ('gc.burst', BOOL, False, 'perform eager garbage collection during initialization'), ('gc.defrag', BOOL, True, 'defragment clauses when garbage collecting'), ('simplify.delay', UINT, 0, 'set initial delay of simplification by a conflict count'), ('force_cleanup', BOOL, False, 'force cleanup to remove tautologies and simplify clauses'), ('minimize_lemmas', BOOL, True, 'minimize learned clauses'), ('dyn_sub_res', BOOL, True, 'dynamic subsumption resolution for minimizing learned clauses'), ('core.minimize', BOOL, False, 'minimize computed core'), ('core.minimize_partial', BOOL, False, 'apply partial (cheap) core minimization'), ('backtrack.scopes', UINT, 100, 'number of scopes to enable chronological backtracking'), ('backtrack.conflicts', UINT, 4000, 'number of conflicts before enabling chronological backtracking'), ('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'), ('drat.activity', BOOL, False, 'dump variable activities'), ('cardinality.solver', BOOL, True, 'use cardinality solver'), ('pb.solver', SYMBOL, 'solver', 'method for handling Pseudo-Boolean constraints: circuit (arithmetical circuit), sorting (sorting circuit), totalizer (use totalizer encoding), binary_merge, segmented, solver (use native solver)'), ('pb.min_arity', UINT, 9, 'minimal arity to compile pb/cardinality constraints to CNF'), ('xor.solver', BOOL, False, 'use xor solver'), ('cardinality.encoding', SYMBOL, 'grouped', 'encoding used for at-most-k constraints: grouped, bimander, ordered, unate, circuit'), ('pb.resolve', SYMBOL, 'cardinality', 'resolution strategy for boolean algebra solver: cardinality, rounding'), ('pb.lemma_format', SYMBOL, 'cardinality', 'generate either cardinality or pb lemmas'), ('ddfw_search', BOOL, False, 'use ddfw local search instead of CDCL'), ('ddfw.init_clause_weight', UINT, 8, 'initial clause weight for DDFW local search'), ('ddfw.use_reward_pct', UINT, 15, 'percentage to pick highest reward variable when it has reward 0'), ('ddfw.restart_base', UINT, 100000, 'number of flips used a starting point for hessitant restart backoff'), ('ddfw.reinit_base', UINT, 10000, 'increment basis for geometric backoff scheme of re-initialization of weights'), ('ddfw.threads', UINT, 0, 'number of ddfw threads to run in parallel with sat solver'), ('prob_search', BOOL, False, 'use probsat local search instead of CDCL'), ('local_search', BOOL, False, 'use local search instead of CDCL'), ('local_search_threads', UINT, 0, 'number of local search threads to find satisfiable solution'), ('local_search_mode', SYMBOL, 'wsat', 'local search algorithm, either default wsat or qsat'), ('local_search_dbg_flips', BOOL, False, 'write debug information for number of flips'), ('unit_walk', BOOL, False, 'use unit-walk search instead of CDCL'), ('unit_walk_threads', UINT, 0, 'number of unit-walk search threads to find satisfiable solution'), ('binspr', BOOL, False, 'enable SPR inferences of binary propagation redundant clauses. This inprocessing step eliminates models'), ('lookahead.cube.cutoff', SYMBOL, 'depth', 'cutoff type used to create lookahead cubes: depth, freevars, psat, adaptive_freevars, adaptive_psat'), # - depth: the maximal cutoff is fixed to the value of lookahead.cube.depth. # So if the value is 10, at most 1024 cubes will be generated of length 10. # - freevars: cutoff based on a variable fraction of lookahead.cube.freevars. # Cut if the number of current unassigned variables drops below a fraction of number of initial variables. # - psat: Let psat_heur := (Sum_{clause C} (psat.clause_base ^ {-|C|+1})) / |freevars|^psat.var_exp # Cut if the value of psat_heur exceeds psat.trigger # - adaptive_freevars: Cut if the number of current unassigned variables drops below a fraction of free variables # at the time of the last conflict. The fraction is increased every time the cutoff is created. # - adative_psat: Cut based on psat_heur in an adaptive way. ('lookahead.cube.fraction', DOUBLE, 0.4, 'adaptive fraction to create lookahead cubes. Used when lookahead.cube.cutoff is adaptive_freevars or adaptive_psat'), ('lookahead.cube.depth', UINT, 1, 'cut-off depth to create cubes. Used when lookahead.cube.cutoff is depth.'), ('lookahead.cube.freevars', DOUBLE, 0.8, 'cube free variable fraction. Used when lookahead.cube.cutoff is freevars'), ('lookahead.cube.psat.var_exp', DOUBLE, 1, 'free variable exponent for PSAT cutoff'), ('lookahead.cube.psat.clause_base', DOUBLE, 2, 'clause base for PSAT cutoff'), ('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_scores', BOOL, False, 'extract lookahead scores. A utility that can only be used from the DIMACS front-end'), ('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. # - heule_schur: reward function based on "Schur Number 5", Heule, AAAI 2018 # The score of a literal lit is: # Sum_{C in Clauses | lit in C} 2 ^ (- |C|+1) # * Sum_{lit' in C | lit' != lit} lit_occs(~lit') # / | C | # where lit_occs(lit) is the number of clauses containing lit. # - heuleu: The score of a literal lit is: Sum_{C in Clauses | lit in C} 2 ^ (-|C| + 1) # - unit: heule_schur + also counts number of unit clauses. # - march_cu: default reward function used in a version of March # Each reward function also comes with its own variant of "mix_diff", which # is the function for combining reward metrics for the positive and negative variant of a literal. ) z3-z3-4.8.7/src/sat/sat_prob.cpp000066400000000000000000000202051356505360400163210ustar00rootroot00000000000000/*++ Copyright (c) 2019 Microsoft Corporation Module Name: sat_prob.cpp Abstract: PROB Local search module for clauses Author: Nikolaj Bjorner 2019-4-23 Notes: --*/ #include "sat/sat_prob.h" #include "sat/sat_solver.h" #include "util/luby.h" namespace sat { prob::~prob() { for (clause* cp : m_clause_db) { m_alloc.del_clause(cp); } } lbool prob::check(unsigned n, literal const* assumptions, parallel* p) { VERIFY(n == 0); init(); while (m_limit.inc() && m_best_min_unsat > 0) { if (should_restart()) do_restart(); else flip(); } if (m_best_min_unsat == 0) { return l_true; } return l_undef; } void prob::flip() { bool_var v = pick_var(); flip(v); if (m_unsat.size() < m_best_min_unsat) { save_best_values(); } } bool_var prob::pick_var() { unsigned cls_idx = m_unsat.elem_at(m_rand() % m_unsat.size()); double sum_prob = 0; unsigned i = 0; clause const& c = get_clause(cls_idx); for (literal lit : c) { double prob = m_prob_break[m_breaks[lit.var()]]; m_probs[i++] = prob; sum_prob += prob; } double lim = sum_prob * ((double)m_rand() / m_rand.max_value()); do { lim -= m_probs[--i]; } while (lim >= 0 && i > 0); return c[i].var(); } void prob::flip(bool_var v) { ++m_flips; literal lit = literal(v, !m_values[v]); literal nlit = ~lit; SASSERT(is_true(lit)); for (unsigned cls_idx : use_list(*this, lit)) { clause_info& ci = m_clauses[cls_idx]; ci.del(lit); switch (ci.m_num_trues) { case 0: m_unsat.insert(cls_idx); dec_break(lit); break; case 1: inc_break(to_literal(ci.m_trues)); break; default: break; } } for (unsigned cls_idx : use_list(*this, nlit)) { clause_info& ci = m_clauses[cls_idx]; switch (ci.m_num_trues) { case 0: m_unsat.remove(cls_idx); inc_break(nlit); break; case 1: dec_break(to_literal(ci.m_trues)); break; default: break; } ci.add(nlit); } m_values[v] = !m_values[v]; } void prob::add(unsigned n, literal const* c) { clause* cls = m_alloc.mk_clause(n, c, false); unsigned idx = m_clause_db.size(); m_clause_db.push_back(cls); m_clauses.push_back(clause_info()); for (literal lit : *cls) { m_values.reserve(lit.var()+1); m_breaks.reserve(lit.var()+1); m_use_list.reserve(lit.index()+1); m_use_list[lit.index()].push_back(idx); } m_probs.reserve(n+1); } void prob::add(solver const& s) { unsigned trail_sz = s.init_trail_size(); for (unsigned i = 0; i < trail_sz; ++i) { add(1, s.m_trail.c_ptr() + i); } unsigned sz = s.m_watches.size(); for (unsigned l_idx = 0; l_idx < sz; ++l_idx) { literal l1 = ~to_literal(l_idx); watch_list const & wlist = s.m_watches[l_idx]; for (watched const& w : wlist) { if (!w.is_binary_non_learned_clause()) continue; literal l2 = w.get_literal(); if (l1.index() > l2.index()) continue; literal ls[2] = { l1, l2 }; add(2, ls); } } for (clause* c : s.m_clauses) { add(c->size(), c->begin()); } } void prob::save_best_values() { m_best_min_unsat = m_unsat.size(); m_best_values.reserve(m_values.size()); m_model.reserve(m_values.size()); for (unsigned i = 0; i < m_values.size(); ++i) { m_best_values[i] = m_values[i]; m_model[i] = to_lbool(m_values[i]); } } void prob::flatten_use_list() { m_use_list_index.reset(); m_flat_use_list.reset(); for (auto const& ul : m_use_list) { m_use_list_index.push_back(m_flat_use_list.size()); m_flat_use_list.append(ul); } m_use_list_index.push_back(m_flat_use_list.size()); } void prob::init_clauses() { for (unsigned& b : m_breaks) { b = 0; } m_unsat.reset(); for (unsigned i = 0; i < m_clauses.size(); ++i) { clause_info& ci = m_clauses[i]; ci.m_num_trues = 0; ci.m_trues = 0; clause const& c = get_clause(i); for (literal lit : c) { if (is_true(lit)) { ci.add(lit); } } switch (ci.m_num_trues) { case 0: m_unsat.insert(i); break; case 1: inc_break(to_literal(ci.m_trues)); break; default: break; } } } void prob::auto_config() { unsigned max_len = 0; for (clause* cp : m_clause_db) { max_len = std::max(max_len, cp->size()); } // ProbSat magic constants switch (max_len) { case 0: case 1: case 2: case 3: m_config.m_cb = 2.5; break; case 4: m_config.m_cb = 2.85; break; case 5: m_config.m_cb = 3.7; break; case 6: m_config.m_cb = 5.1; break; default: m_config.m_cb = 5.4; break; } unsigned max_num_occ = 0; for (auto const& ul : m_use_list) { max_num_occ = std::max(max_num_occ, ul.size()); } // vodoo from prob-sat m_prob_break.reserve(max_num_occ+1); for (int i = 0; i <= static_cast(max_num_occ); ++i) { m_prob_break[i] = pow(m_config.m_cb, -i); } } void prob::log() { double sec = m_stopwatch.get_current_seconds(); double kflips_per_sec = m_flips / (1000.0 * sec); IF_VERBOSE(0, verbose_stream() << sec << " sec. " << (m_flips/1000) << " kflips " << m_best_min_unsat << " unsat " << kflips_per_sec << " kflips/sec " << m_restart_count << " restarts\n"); } void prob::init() { flatten_use_list(); init_random_values(); init_clauses(); auto_config(); save_best_values(); m_restart_count = 1; m_flips = 0; m_next_restart = m_config.m_restart_offset; m_stopwatch.start(); } void prob::init_random_values() { for (unsigned v = 0; v < m_values.size(); ++v) { m_values[v] = (m_rand() % 2 == 0); } } void prob::init_best_values() { for (unsigned v = 0; v < m_values.size(); ++v) { m_values[v] = m_best_values[v]; } } void prob::init_near_best_values() { for (unsigned v = 0; v < m_values.size(); ++v) { if (m_rand(100) < m_config.m_prob_random_init) { m_values[v] = !m_best_values[v]; } else { m_values[v] = m_best_values[v]; } } } void prob::do_restart() { reinit_values(); init_clauses(); m_next_restart += m_config.m_restart_offset*get_luby(m_restart_count++); log(); } bool prob::should_restart() { return m_flips >= m_next_restart; } void prob::reinit_values() { init_near_best_values(); } std::ostream& prob::display(std::ostream& out) const { unsigned num_cls = m_clauses.size(); for (unsigned i = 0; i < num_cls; ++i) { out << get_clause(i) << " "; auto const& ci = m_clauses[i]; out << ci.m_num_trues << "\n"; } return out; } void prob::invariant() { } } z3-z3-4.8.7/src/sat/sat_prob.h000066400000000000000000000101071356505360400157660ustar00rootroot00000000000000/*++ Copyright (c) 2019 Microsoft Corporation Module Name: sat_prob.h Abstract: PROB Local search module for clauses Author: Nikolaj Bjorner 2019-4-23 Notes: http://www.ict.griffith.edu.au/~johnt/publications/CP2006raouf.pdf --*/ #ifndef _SAT_PROB_ #define _SAT_PROB_ #include "util/uint_set.h" #include "util/rlimit.h" #include "sat/sat_clause.h" #include "sat/sat_types.h" namespace sat { class solver; class prob : public i_local_search { struct clause_info { clause_info(): m_trues(0), m_num_trues(0) {} unsigned m_trues; // set of literals that are true unsigned m_num_trues; // size of true set bool is_true() const { return m_num_trues > 0; } void add(literal lit) { ++m_num_trues; m_trues += lit.index(); } void del(literal lit) { SASSERT(m_num_trues > 0); --m_num_trues; m_trues -= lit.index(); } }; struct config { unsigned m_prob_random_init; unsigned m_restart_offset; double m_cb; double m_eps; config() { reset(); } void reset() { m_prob_random_init = 4; m_restart_offset = 1000000; m_cb = 2.85; m_eps = 0.9; } }; config m_config; reslimit m_limit; clause_allocator m_alloc; clause_vector m_clause_db; svector m_clauses; svector m_values, m_best_values; unsigned m_best_min_unsat; vector m_use_list; unsigned_vector m_flat_use_list; unsigned_vector m_use_list_index; svector m_prob_break; svector m_probs; indexed_uint_set m_unsat; random_gen m_rand; unsigned_vector m_breaks; uint64_t m_flips; uint64_t m_next_restart; unsigned m_restart_count; stopwatch m_stopwatch; model m_model; class use_list { prob& p; unsigned i; public: use_list(prob& p, literal lit): p(p), i(lit.index()) {} unsigned const* begin() { return p.m_flat_use_list.c_ptr() + p.m_use_list_index[i]; } unsigned const* end() { return p.m_flat_use_list.c_ptr() + p.m_use_list_index[i+1]; } }; void flatten_use_list(); bool is_true(literal lit) const { return m_values[lit.var()] != lit.sign(); } inline clause const& get_clause(unsigned idx) const { return *m_clause_db[idx]; } inline bool is_true(unsigned idx) const { return m_clauses[idx].is_true(); } inline void inc_break(literal lit) { m_breaks[lit.var()]++; } inline void dec_break(literal lit) { m_breaks[lit.var()]--; } void flip(); bool_var pick_var(); void flip(bool_var v); void reinit_values(); void save_best_values(); void init(); void init_random_values(); void init_best_values(); void init_near_best_values(); void init_clauses(); void auto_config(); bool should_restart(); void do_restart(); void invariant(); void log(); void add(unsigned sz, literal const* c); public: prob() {} ~prob() override; lbool check(unsigned sz, literal const* assumptions, parallel* p) override; void set_seed(unsigned n) override { m_rand.set_seed(n); } reslimit& rlimit() override { return m_limit; } void add(solver const& s) override; model const& get_model() const override { return m_model; } std::ostream& display(std::ostream& out) const; void updt_params(params_ref const& p) override {} unsigned num_non_binary_clauses() const override { return 0; } void collect_statistics(statistics& st) const override {} void reinit(solver& s) override { UNREACHABLE(); } }; } #endif z3-z3-4.8.7/src/sat/sat_probing.cpp000066400000000000000000000214201356505360400170170ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_probing.cpp Abstract: Probing (aka failed literal detection). Author: Leonardo de Moura (leonardo) 2011-06-04. Revision History: --*/ #include "sat/sat_probing.h" #include "sat/sat_solver.h" #include "sat/sat_simplifier_params.hpp" namespace sat { probing::probing(solver & _s, params_ref const & p): s(_s) { updt_params(p); reset_statistics(); m_stopped_at = 0; m_counter = 0; } // reset the cache for the given literal void probing::reset_cache(literal l) { if (l.index() < m_cached_bins.size()) { m_cached_bins[l.index()].m_available = false; m_cached_bins[l.index()].m_lits.finalize(); } } // l implied the literals on the trail stack starting at position old_tr_sz // Thus, ~l \/ l2 is a binary clause for every l2 on this fragment of the trail stack. void probing::cache_bins(literal l, unsigned old_tr_sz) { if (!m_probing_cache) return; if (memory::get_allocation_size() > m_probing_cache_limit) return; // not enough memory to spare m_cached_bins.reserve(l.index() + 1); cache_entry & entry = m_cached_bins[l.index()]; entry.m_available = true; entry.m_lits.reset(); unsigned tr_sz = s.m_trail.size(); for (unsigned i = old_tr_sz; i < tr_sz; i++) { entry.m_lits.push_back(s.m_trail[i]); if (s.m_config.m_drat) { s.m_drat.add(~l, s.m_trail[i], true); } } } // Return true if should keep going. // It will assert literals implied by l that are already marked // as assigned. bool probing::try_lit(literal l, bool updt_cache) { SASSERT(s.m_qhead == s.m_trail.size()); SASSERT(s.value(l.var()) == l_undef); literal_vector * implied_lits = updt_cache ? nullptr : cached_implied_lits(l); if (implied_lits) { for (literal lit : *implied_lits) { if (m_assigned.contains(lit)) { 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++; } } } else { m_to_assert.reset(); s.push(); 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_scoped(~l); s.propagate(false); return false; } // collect literals that were assigned after assigning l unsigned tr_sz = s.m_trail.size(); for (unsigned i = old_tr_sz; i < tr_sz; i++) { if (m_assigned.contains(s.m_trail[i])) { m_to_assert.push_back(s.m_trail[i]); } } if (updt_cache) cache_bins(l, old_tr_sz); s.pop(1); 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++; } } s.propagate(false); return !s.inconsistent(); } void probing::process_core(bool_var v) { TRACE("probing", tout << "processing: " << v << " counter: " << -m_counter << "\n";); SASSERT(s.m_qhead == s.m_trail.size()); SASSERT(s.value(v) == l_undef); m_counter--; s.push(); literal l(v, false); 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_scoped(~l); s.propagate(false); m_num_assigned++; return; } // collect literals that were assigned after assigning l m_assigned.reset(); unsigned tr_sz = s.m_trail.size(); for (unsigned i = old_tr_sz; i < tr_sz; i++) { m_assigned.insert(s.m_trail[i]); } cache_bins(l, old_tr_sz); s.pop(1); if (!try_lit(~l, true)) return; if (m_probing_binary) { watch_list & wlist = s.get_wlist(~l); for (watched & w : wlist) { if (!w.is_binary_clause()) continue; literal l2 = w.get_literal(); if (l.index() > l2.index()) continue; if (s.value(l2) != l_undef) continue; // verbose_stream() << "probing " << l << " " << l2 << " " << m_counter << "\n"; // Note: that try_lit calls propagate, which may update the watch lists. if (!try_lit(l2, false)) return; if (s.inconsistent()) return; } } } void probing::process(bool_var v) { int old_counter = m_counter; unsigned old_num_assigned = m_num_assigned; process_core(v); if (m_num_assigned > old_num_assigned) { // if new variables were assigned when probing x, // then assume the cost is 0. m_counter = old_counter; } } struct probing::report { probing & m_probing; stopwatch m_watch; unsigned m_num_assigned; report(probing & p): m_probing(p), m_num_assigned(p.m_num_assigned) { m_watch.start(); } ~report() { m_watch.stop(); 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() << m_watch << ")\n";); } }; bool probing::operator()(bool force) { if (!m_probing) return true; s.propagate(false); // make sure propagation queue is empty if (s.inconsistent()) return true; SASSERT(s.m_qhead == s.m_trail.size()); CASSERT("probing", s.check_invariant()); if (!force && m_counter > 0) return true; if (m_probing_cache && memory::get_allocation_size() > m_probing_cache_limit) m_cached_bins.finalize(); report rpt(*this); bool r = true; m_counter = 0; int limit = -static_cast(m_probing_limit); unsigned i; unsigned num = s.num_vars(); for (i = 0; i < num; i++) { bool_var v = (m_stopped_at + i) % num; if (m_counter < limit) { m_stopped_at = v; r = false; break; } if (s.inconsistent()) { break; } if (s.value(v) != l_undef || s.was_eliminated(v)) { if (m_probing_cache) { // cache for v literals is not needed anymore. reset_cache(literal(v, false)); reset_cache(literal(v, true)); } continue; } s.checkpoint(); process(v); } if (r) m_stopped_at = 0; m_counter = -m_counter; if (rpt.m_num_assigned == m_num_assigned) { // penalize m_counter *= 2; } CASSERT("probing", s.check_invariant()); finalize(); return r; } void probing::updt_params(params_ref const & _p) { sat_simplifier_params p(_p); m_probing = p.probing(); m_probing_limit = p.probing_limit(); m_probing_cache = p.probing_cache(); m_probing_binary = p.probing_binary(); m_probing_cache_limit = p.probing_cache_limit(); } void probing::collect_param_descrs(param_descrs & d) { // TODO } void probing::finalize() { m_assigned.finalize(); m_to_assert.finalize(); m_cached_bins.finalize(); } void probing::collect_statistics(statistics & st) const { st.update("sat probing assigned", m_num_assigned); } void probing::reset_statistics() { m_num_assigned = 0; } }; z3-z3-4.8.7/src/sat/sat_probing.h000066400000000000000000000046101356505360400164660ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_probing.h Abstract: Probing (aka failed literal detection). Author: Leonardo de Moura (leonardo) 2011-06-04. Revision History: --*/ #ifndef SAT_PROBING_H_ #define SAT_PROBING_H_ #include "sat/sat_types.h" #include "util/params.h" #include "util/statistics.h" namespace sat { class probing { solver & s; unsigned m_stopped_at; // where did it stop literal_set m_assigned; // literals assigned in the first branch literal_vector m_to_assert; // counters int m_counter; // track cost // config bool m_probing; // enabled/disabled unsigned m_probing_limit; // max cost per round bool m_probing_cache; // cache implicit binary clauses bool m_probing_binary; // try l1 and l2 for binary clauses l1 \/ l2 unsigned long long m_probing_cache_limit; // memory limit for enabling caching. // stats unsigned m_num_assigned; struct cache_entry { bool m_available; literal_vector m_lits; cache_entry():m_available(false) {} }; vector m_cached_bins; struct report; void cache_bins(literal l, unsigned old_tr_sz); bool try_lit(literal l, bool updt_cache); void process(bool_var v); void process_core(bool_var v); public: probing(solver & s, params_ref const & p); bool operator()(bool force = false); void reset_cache(literal l); void updt_params(params_ref const & p); static void collect_param_descrs(param_descrs & d); void finalize(); void collect_statistics(statistics & st) const; void reset_statistics(); // return the literals implied by l. // return 0, if the cache is not available literal_vector * cached_implied_lits(literal l) { if (!m_probing_cache) return nullptr; if (l.index() >= m_cached_bins.size()) return nullptr; cache_entry & e = m_cached_bins[l.index()]; if (!e.m_available) return nullptr; return &(e.m_lits); } void dec(unsigned c) { m_counter -= c; } }; }; #endif z3-z3-4.8.7/src/sat/sat_scc.cpp000066400000000000000000000241331356505360400161330ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_scc.cpp Abstract: Use binary clauses to compute strongly connected components. Author: Leonardo de Moura (leonardo) 2011-05-26. Revision History: --*/ #include "sat/sat_scc.h" #include "sat/sat_solver.h" #include "sat/sat_elim_eqs.h" #include "util/stopwatch.h" #include "util/trace.h" #include "sat/sat_scc_params.hpp" namespace sat { scc::scc(solver & s, params_ref const & p): m_solver(s), m_big(s.m_rand) { reset_statistics(); updt_params(p); } struct frame { unsigned m_lidx; unsigned m_succ_idx; bool m_first; watched * m_it; watched * m_end; frame(unsigned lidx, watched * it, watched * end, unsigned sidx = 0):m_lidx(lidx), m_succ_idx(sidx), m_first(true), m_it(it), m_end(end) {} }; typedef svector frames; struct scc::report { scc & m_scc; 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_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; 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; if (num_units > 0) verbose_stream() << " :units " << num_units; verbose_stream() << m_watch << ")\n";); } }; unsigned scc::operator()() { if (m_solver.m_inconsistent) return 0; if (!m_scc) return 0; CASSERT("scc_bug", m_solver.check_invariant()); report rpt(*this); TRACE("scc", m_solver.display(tout);); TRACE("scc_details", m_solver.display_watches(tout);); unsigned_vector index; unsigned_vector lowlink; unsigned_vector s; svector in_s; unsigned num_lits = m_solver.num_vars() * 2; index.resize(num_lits, UINT_MAX); lowlink.resize(num_lits, UINT_MAX); in_s.resize(num_lits, false); literal_vector roots, lits; roots.resize(m_solver.num_vars(), null_literal); unsigned next_index = 0; svector frames; bool_var_vector to_elim; for (unsigned l_idx = 0; l_idx < num_lits; l_idx++) { if (index[l_idx] != UINT_MAX) continue; if (m_solver.was_eliminated(to_literal(l_idx).var())) continue; m_solver.checkpoint(); #define NEW_NODE(LIDX) { \ index[LIDX] = next_index; \ lowlink[LIDX] = next_index; \ next_index++; \ s.push_back(LIDX); \ in_s[LIDX] = true; \ watch_list & wlist = m_solver.get_wlist(LIDX); \ frames.push_back(frame(LIDX, wlist.begin(), wlist.end())); \ } NEW_NODE(l_idx); while (!frames.empty()) { loop: frame & fr = frames.back(); unsigned l_idx = fr.m_lidx; if (!fr.m_first) { SASSERT(fr.m_it->is_binary_clause()); // after visiting child literal l2 = fr.m_it->get_literal(); unsigned l2_idx = l2.index(); SASSERT(index[l2_idx] != UINT_MAX); if (lowlink[l2_idx] < lowlink[l_idx]) lowlink[l_idx] = lowlink[l2_idx]; fr.m_it++; } fr.m_first = false; while (fr.m_it != fr.m_end) { if (!fr.m_it->is_binary_clause()) { fr.m_it++; continue; } literal l2 = fr.m_it->get_literal(); unsigned l2_idx = l2.index(); if (index[l2_idx] == UINT_MAX) { NEW_NODE(l2_idx); goto loop; } else if (in_s[l2_idx]) { if (index[l2_idx] < lowlink[l_idx]) lowlink[l_idx] = index[l2_idx]; } fr.m_it++; } // visited all successors if (lowlink[l_idx] == index[l_idx]) { // found new SCC CTRACE("scc_cycle", s.back() != l_idx, { tout << "cycle: "; unsigned j = s.size() - 1; unsigned l2_idx; do { l2_idx = s[j]; j--; tout << to_literal(l2_idx) << " "; } while (l2_idx != l_idx); tout << "\n"; }); SASSERT(!s.empty()); literal l = to_literal(l_idx); bool_var v = l.var(); if (roots[v] != null_literal) { // variable was already assigned... just consume stack TRACE("scc_detail", tout << "consuming stack...\n";); unsigned l2_idx; do { l2_idx = s.back(); s.pop_back(); in_s[l2_idx] = false; SASSERT(roots[to_literal(l2_idx).var()].var() == roots[v].var()); } while (l2_idx != l_idx); } else { // check if the SCC has an external variable, and check for conflicts TRACE("scc_detail", tout << "assigning roots...\n";); literal r = null_literal; unsigned j = s.size() - 1; unsigned l2_idx; do { l2_idx = s[j]; j--; if (to_literal(l2_idx) == ~l) { m_solver.set_conflict(); return 0; } if (m_solver.is_external(to_literal(l2_idx).var())) { r = to_literal(l2_idx); break; } } while (l2_idx != l_idx); if (r == null_literal) { // SCC does not contain external variable r = to_literal(l_idx); } TRACE("scc_detail", tout << "r: " << r << "\n";); do { l2_idx = s.back(); s.pop_back(); in_s[l2_idx] = false; literal l2 = to_literal(l2_idx); bool_var v2 = l2.var(); if (roots[v2] == null_literal) { if (l2.sign()) { roots[v2] = ~r; } else { roots[v2] = r; } if (v2 != r.var()) to_elim.push_back(v2); } } while (l2_idx != l_idx); } } frames.pop_back(); } } for (unsigned i = 0; i < m_solver.num_vars(); ++i) { if (roots[i] == null_literal) { roots[i] = literal(i, false); } } TRACE("scc", for (unsigned i = 0; i < roots.size(); i++) { tout << i << " -> " << roots[i] << "\n"; } tout << "to_elim: "; for (unsigned v : to_elim) tout << v << " "; tout << "\n";); m_num_elim += to_elim.size(); elim_eqs eliminator(m_solver); eliminator(roots, to_elim); TRACE("scc_detail", m_solver.display(tout);); CASSERT("scc_bug", m_solver.check_invariant()); if (m_scc_tr) { reduce_tr(); } TRACE("scc_detail", m_solver.display(tout);); return to_elim.size(); } unsigned scc::reduce_tr(bool learned) { init_big(learned); unsigned num_elim = m_big.reduce_tr(m_solver); m_num_elim_bin += num_elim; return num_elim; } void scc::reduce_tr() { 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("sat scc elim vars", m_num_elim); st.update("sat scc elim binary", m_num_elim_bin); } void scc::reset_statistics() { m_num_elim = 0; m_num_elim_bin = 0; } void scc::updt_params(params_ref const & _p) { sat_scc_params p(_p); m_scc = p.scc(); m_scc_tr = p.scc_tr(); } void scc::collect_param_descrs(param_descrs & d) { sat_scc_params::collect_param_descrs(d); } }; z3-z3-4.8.7/src/sat/sat_scc.h000066400000000000000000000030061356505360400155740ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_scc.h Abstract: Use binary clauses to compute strongly connected components. Author: Leonardo de Moura (leonardo) 2011-05-26. Revision History: --*/ #ifndef SAT_SCC_H_ #define SAT_SCC_H_ #include "util/statistics.h" #include "util/params.h" #include "sat/sat_types.h" #include "sat/sat_big.h" namespace sat { class solver; class scc { struct report; solver & m_solver; // config bool m_scc; bool m_scc_tr; // stats unsigned m_num_elim; unsigned m_num_elim_bin; big m_big; void reduce_tr(); unsigned reduce_tr(bool learned); public: scc(solver & s, params_ref const & p); unsigned operator()(); void updt_params(params_ref const & p); static void collect_param_descrs(param_descrs & d); void collect_statistics(statistics & st) const; void reset_statistics(); /* \brief create binary implication graph and associated data-structures to check transitivity. */ void init_big(bool learned) { m_big.init(m_solver, learned); } void ensure_big(bool learned) { m_big.ensure_big(m_solver, learned); } int get_left(literal l) const { return m_big.get_left(l); } int get_right(literal l) const { return m_big.get_right(l); } bool connected(literal u, literal v) const { return m_big.connected(u, v); } }; }; #endif z3-z3-4.8.7/src/sat/sat_scc_params.pyg000066400000000000000000000005451356505360400175140ustar00rootroot00000000000000def_module_params(module_name='sat', class_name='sat_scc_params', export=True, params=(('scc', BOOL, True, 'eliminate Boolean variables by computing strongly connected components'), ('scc.tr', BOOL, True, 'apply transitive reduction, eliminate redundant binary clauses'), )) z3-z3-4.8.7/src/sat/sat_simplifier.cpp000066400000000000000000002275241356505360400175370ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_simplifier.cpp Abstract: SAT simplification procedures that use a "full" occurrence list: Subsumption, Blocked Clause Removal, Variable Elimination, ... Author: Leonardo de Moura (leonardo) 2011-05-24. Revision History: --*/ #include "sat/sat_simplifier.h" #include "sat/sat_simplifier_params.hpp" #include "sat/sat_solver.h" #include "sat/sat_elim_vars.h" #include "sat/sat_integrity_checker.h" #include "util/stopwatch.h" #include "util/trace.h" namespace sat { void use_list::init(unsigned num_vars) { m_use_list.reset(); unsigned num_lits = 2 * num_vars; m_use_list.resize(num_lits); } void use_list::insert(clause & c) { for (literal l : c) m_use_list[l.index()].insert(c); } void use_list::erase(clause & c) { for (literal l : c) m_use_list[l.index()].erase(c); } void use_list::erase(clause & c, literal l) { for (literal l2 : c) if (l2 != l) m_use_list[l2.index()].erase(c); } void use_list::block(clause& c) { for (literal l : c) m_use_list[l.index()].block(c); } void use_list::unblock(clause& c) { for (literal l : c) m_use_list[l.index()].unblock(c); } simplifier::simplifier(solver & _s, params_ref const & p): s(_s), m_num_calls(0) { updt_params(p); reset_statistics(); } simplifier::~simplifier() { finalize(); } watch_list & simplifier::get_wlist(literal l) { return s.get_wlist(l); } watch_list const & simplifier::get_wlist(literal l) const { return s.get_wlist(l); } bool simplifier::is_external(bool_var v) const { return s.is_assumption(v) || (s.is_external(v) && s.is_incremental()) || (s.is_external(v) && s.m_ext && (!m_ext_use_list.get(literal(v, false)).empty() || !m_ext_use_list.get(literal(v, true)).empty())); } inline bool simplifier::was_eliminated(bool_var v) const { return s.was_eliminated(v); } lbool simplifier::value(bool_var v) const { return s.value(v); } lbool simplifier::value(literal l) const { return s.value(l); } inline void simplifier::checkpoint() { s.checkpoint(); } bool simplifier::single_threaded() const { return s.m_config.m_num_threads == 1; } bool simplifier::bce_enabled_base() const { return !m_incremental_mode && !s.tracking_assumptions() && !m_learned_in_use_lists && m_num_calls >= m_bce_delay && single_threaded(); } bool simplifier::ate_enabled() const { return m_num_calls >= m_bce_delay && m_ate; } bool simplifier::bce_enabled() const { return bce_enabled_base() && (m_bce || m_bce_at == m_num_calls || m_acce || m_abce || m_cce); } bool simplifier::acce_enabled() const { return bce_enabled_base() && m_acce; } bool simplifier::cce_enabled() const { return bce_enabled_base() && (m_cce || m_acce); } bool simplifier::abce_enabled() const { return bce_enabled_base() && m_abce; } bool simplifier::bca_enabled() const { return bce_enabled_base() && m_bca; } bool simplifier::elim_vars_bdd_enabled() const { return !m_incremental_mode && !s.tracking_assumptions() && m_elim_vars_bdd && m_num_calls >= m_elim_vars_bdd_delay && single_threaded(); } bool simplifier::elim_vars_enabled() const { return !m_incremental_mode && !s.tracking_assumptions() && m_elim_vars && single_threaded(); } void simplifier::register_clauses(clause_vector & cs) { std::stable_sort(cs.begin(), cs.end(), size_lt()); for (clause* c : cs) { if (!c->frozen()) { m_use_list.insert(*c); if (c->strengthened()) m_sub_todo.insert(*c); } } } inline void simplifier::remove_clause(clause & c, bool is_unique) { if (!c.was_removed()) { if (s.m_config.m_drat && is_unique) { 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) { m_need_cleanup = true; s.set_learned(c, true); m_use_list.block(c); } inline void simplifier::set_learned(literal l1, literal l2) { m_sub_bin_todo.erase(bin_clause(l1, l2, false)); m_sub_bin_todo.erase(bin_clause(l2, l1, false)); m_sub_bin_todo.push_back(bin_clause(l1, l2, true)); m_sub_bin_todo.push_back(bin_clause(l2, l1, true)); } void simplifier::init_visited() { m_visited.reset(); m_visited.resize(2*s.num_vars(), false); } void simplifier::finalize() { m_use_list.finalize(); m_sub_todo.finalize(); m_sub_bin_todo.finalize(); m_elim_todo.finalize(); m_visited.finalize(); m_bs_cs.finalize(); m_bs_ls.finalize(); m_ext_use_list.finalize(); } void simplifier::initialize() { m_need_cleanup = false; s.m_cleaner(true); m_last_sub_trail_sz = s.m_trail.size(); m_use_list.init(s.num_vars()); if (s.m_ext) s.m_ext->init_use_list(m_ext_use_list); m_sub_todo.reset(); m_sub_bin_todo.reset(); m_elim_todo.reset(); init_visited(); TRACE("after_cleanup", s.display(tout);); CASSERT("sat_solver", s.check_invariant()); } void simplifier::operator()(bool learned) { if (s.inconsistent()) return; if (!m_subsumption && !bce_enabled() && !bca_enabled() && !elim_vars_enabled()) return; initialize(); CASSERT("sat_solver", s.check_invariant()); TRACE("sat_simplifier", s.display(tout);); s.m_cleaner(true); TRACE("after_cleanup", s.display(tout);); CASSERT("sat_solver", s.check_invariant()); m_need_cleanup = false; m_use_list.init(s.num_vars()); m_learned_in_use_lists = learned; if (learned) { register_clauses(s.m_learned); } register_clauses(s.m_clauses); if (!learned && (bce_enabled() || bca_enabled() || ate_enabled())) { elim_blocked_clauses(); } if (!learned) { m_num_calls++; } m_sub_counter = m_subsumption_limit; m_elim_counter = m_res_limit; m_old_num_elim_vars = m_num_elim_vars; for (bool_var v = 0; v < s.num_vars(); ++v) { if (!s.m_eliminated[v] && !is_external(v)) { insert_elim_todo(v); } } do { if (m_subsumption) subsume(); if (s.inconsistent()) return; if (!learned && elim_vars_enabled()) elim_vars(); if (s.inconsistent()) return; if (!m_subsumption || m_sub_counter < 0) break; } while (!m_sub_todo.empty()); bool vars_eliminated = m_num_elim_vars > m_old_num_elim_vars; if (m_need_cleanup || vars_eliminated) { TRACE("after_simplifier", tout << "cleanning watches...\n";); cleanup_watches(); move_clauses(s.m_learned, true); move_clauses(s.m_clauses, false); cleanup_clauses(s.m_learned, true, vars_eliminated); cleanup_clauses(s.m_clauses, false, vars_eliminated); } CASSERT("sat_solver", s.check_invariant()); TRACE("sat_simplifier", s.display(tout); tout << "model_converter:\n"; s.m_mc.display(tout);); finalize(); } /** \brief Eliminate all ternary and clause watches. */ void simplifier::cleanup_watches() { for (watch_list& wlist : s.m_watches) { watch_list::iterator it2 = wlist.begin(); watch_list::iterator itprev = it2; watch_list::iterator end2 = wlist.end(); for (; it2 != end2; ++it2) { switch (it2->get_kind()) { case watched::TERNARY: case watched::CLAUSE: // consume break; default: *itprev = *it2; itprev++; break; } } wlist.set_end(itprev); } } void simplifier::move_clauses(clause_vector& cs, bool learned) { clause_vector::iterator it = cs.begin(); clause_vector::iterator it2 = it; clause_vector::iterator end = cs.end(); unsigned nm = 0; for (; it != end; ++it) { clause & c = *(*it); if (learned && !c.is_learned()) { s.m_clauses.push_back(&c); ++nm; } else if (!learned && c.is_learned()) { s.m_learned.push_back(&c); ++nm; } else { *it2 = *it; ++it2; } } cs.set_end(it2); } 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; clause_vector::iterator end = cs.end(); for (; it != end; ++it) { clause & c = *(*it); VERIFY(learned == c.is_learned()); if (c.was_removed()) { s.del_clause(c); continue; } if (learned && vars_eliminated) { unsigned sz = c.size(); unsigned i; for (i = 0; i < sz; i++) { if (was_eliminated(c[i].var())) break; } if (i < sz) { s.del_clause(c); continue; } } unsigned sz0 = c.size(); if (cleanup_clause(c)) { s.del_clause(c); continue; } unsigned sz = c.size(); 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; case 2: s.mk_bin_clause(c[0], c[1], c.is_learned()); c.restore(sz0); s.del_clause(c); break; default: s.shrink(c, sz0, sz); *it2 = *it; it2++; if (!c.frozen()) { s.attach_clause(c); } break; } } cs.set_end(it2); } void simplifier::mark_all_but(clause const & c, literal l1) { for (literal l2 : c) if (l2 != l1) mark_visited(l2); } void simplifier::unmark_all(clause const & c) { for (literal l : c) unmark_visited(l); } /** \brief Return the variable in c with the minimal number positive+negative occurrences. */ bool_var simplifier::get_min_occ_var(clause const & c) const { literal l_best = null_literal; unsigned best = UINT_MAX; for (literal l : c) { unsigned num = m_use_list.get(l).size() + m_use_list.get(~l).size(); if (num < best) { l_best = l; best = num; } } return l_best.var(); } /** If c1 subsumes c2, return true If c2 can self subsumed by c1, return true and store the literal that can be removed from c2 in l. Otherwise return false */ bool simplifier::subsumes1(clause const & c1, clause const & c2, literal & l) { for (literal lit : c2) mark_visited(lit); bool r = true; l = null_literal; for (literal lit : c1) { if (!is_marked(lit)) { if (l == null_literal && is_marked(~lit)) { l = ~lit; } else { l = null_literal; r = false; break; } } } for (literal lit : c2) unmark_visited(lit); return r; } /** \brief Return the clauses subsumed by c1 and the clauses that can be subsumed resolved using c1. The collections is populated using the use list of target. */ void simplifier::collect_subsumed1_core(clause const & c1, clause_vector & out, literal_vector & out_lits, literal target) { clause_use_list const & cs = m_use_list.get(target); for (auto it = cs.mk_iterator(); !it.at_end(); it.next()) { clause & c2 = it.curr(); CTRACE("resolution_bug", c2.was_removed(), tout << "clause has been removed:\n" << c2 << "\n";); SASSERT(!c2.was_removed()); if (&c2 != &c1 && c1.size() <= c2.size() && approx_subset(c1.approx(), c2.approx())) { m_sub_counter -= c1.size() + c2.size(); literal l; if (subsumes1(c1, c2, l)) { out.push_back(&c2); out_lits.push_back(l); } } } } /** \brief Return the clauses subsumed by c1 and the clauses that can be subsumed resolved using c1. */ void simplifier::collect_subsumed1(clause const & c1, clause_vector & out, literal_vector & out_lits) { bool_var v = get_min_occ_var(c1); collect_subsumed1_core(c1, out, out_lits, literal(v, false)); collect_subsumed1_core(c1, out, out_lits, literal(v, true)); } /** \brief Perform backward subsumption and self-subsumption resolution using c1. */ void simplifier::back_subsumption1(clause & c1) { m_bs_cs.reset(); m_bs_ls.reset(); collect_subsumed1(c1, m_bs_cs, m_bs_ls); SASSERT(m_bs_cs.size() == m_bs_ls.size()); clause_vector::iterator it = m_bs_cs.begin(); clause_vector::iterator end = m_bs_cs.end(); literal_vector::iterator l_it = m_bs_ls.begin(); for (; it != end; ++it, ++l_it) { clause & c2 = *(*it); if (!c2.was_removed() && *l_it == null_literal) { // c2 was subsumed if (c1.is_learned() && !c2.is_learned()) { s.set_learned(c1, false); } TRACE("subsumption", tout << c1 << " subsumed " << c2 << "\n";); remove_clause(c2, false); m_num_subsumed++; } else if (!c2.was_removed()) { // subsumption resolution TRACE("subsumption_resolution", tout << c1 << " sub-ref(" << *l_it << ") " << c2 << "\n";); elim_lit(c2, *l_it); m_num_sub_res++; TRACE("subsumption_resolution", tout << "result: " << c2 << "\n";); } if (s.inconsistent()) break; } } void simplifier::back_subsumption1(literal l1, literal l2, bool learned) { m_dummy.set(l1, l2, learned); clause & c = *(m_dummy.get()); back_subsumption1(c); } /** \brief Return the literal in c with the minimal number of occurrences. */ literal simplifier::get_min_occ_var0(clause const & c) const { literal l_best = null_literal; unsigned best = UINT_MAX; for (literal l : c) { unsigned num = m_use_list.get(l).size(); if (num < best) { l_best = l; best = num; } } return l_best; } /** If c1 subsumes c2, return true Otherwise return false */ bool simplifier::subsumes0(clause const & c1, clause const & c2) { for (literal l : c2) mark_visited(l); bool r = true; for (literal l : c1) { if (!is_marked(l)) { r = false; break; } } for (literal l : c2) unmark_visited(l); return r; } /** \brief Collect the clauses subsumed by c1 (using the occurrence list of target). */ void simplifier::collect_subsumed0_core(clause const & c1, clause_vector & out, literal target) { clause_use_list const & cs = m_use_list.get(target); clause_use_list::iterator it = cs.mk_iterator(); for (; !it.at_end(); it.next()) { clause & c2 = it.curr(); SASSERT(!c2.was_removed()); if (&c2 != &c1 && c1.size() <= c2.size() && approx_subset(c1.approx(), c2.approx())) { m_sub_counter -= c1.size() + c2.size(); if (subsumes0(c1, c2)) { out.push_back(&c2); } } } } /** \brief Collect the clauses subsumed by c1 */ void simplifier::collect_subsumed0(clause const & c1, clause_vector & out) { literal l = get_min_occ_var0(c1); collect_subsumed0_core(c1, out, l); } /** \brief Perform backward subsumption using c1. */ void simplifier::back_subsumption0(clause & c1) { m_bs_cs.reset(); collect_subsumed0(c1, m_bs_cs); for (clause* cp : m_bs_cs) { clause & c2 = *cp; // c2 was subsumed if (c1.is_learned() && !c2.is_learned()) s.set_learned(c1, false); TRACE("subsumption", tout << c1 << " subsumed " << c2 << "\n";); remove_clause(c2, false); m_num_subsumed++; } } /** \brief Eliminate false literals from c, and update occurrence lists Return true if the clause is satisfied */ bool simplifier::cleanup_clause(clause & c) { bool r = false; unsigned sz = c.size(); unsigned j = 0; for (unsigned i = 0; i < sz; i++) { literal l = c[i]; switch (value(l)) { case l_undef: if (i != j) { std::swap(c[j], c[i]); } j++; break; case l_false: m_need_cleanup = true; break; case l_true: r = true; if (i != j) { std::swap(c[j], c[i]); } j++; break; } } if (j < sz && !r) { if (j > 2) { s.shrink(c, sz, j); } else { c.shrink(j); } } return r; } /** \brief Eliminate false literals from c. Return true if the clause is satisfied */ bool simplifier::cleanup_clause(literal_vector & c) { unsigned sz = c.size(); unsigned j = 0; for (unsigned i = 0; i < sz; i++) { literal l = c[i]; switch (value(l)) { case l_undef: if (i != j) { std::swap(c[j], c[i]); } j++; break; case l_false: break; case l_true: return true; } } c.shrink(j); return false; } inline void simplifier::propagate_unit(literal l) { unsigned old_trail_sz = s.m_trail.size(); 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; unsigned new_trail_sz = s.m_trail.size(); for (unsigned i = old_trail_sz; i < new_trail_sz; i++) { literal l = s.m_trail[i]; // put clauses with literals assigned to false back into todo-list for (auto it = m_use_list.get(~l).mk_iterator(); !it.at_end(); it.next()) { m_sub_todo.insert(it.curr()); } clause_use_list& cs = m_use_list.get(l); for (auto it = cs.mk_iterator(); !it.at_end(); ) { clause & c = it.curr(); it.next(); remove_clause(c, true); } cs.reset(); } } void simplifier::elim_lit(clause & c, literal l) { TRACE("elim_lit", tout << "processing: " << l << " @ " << c << "\n";); m_need_cleanup = true; m_num_elim_lits++; insert_elim_todo(l.var()); if (s.m_config.m_drat && c.contains(l)) { unsigned sz = c.size(); c.elim(l); s.m_drat.add(c, true); c.restore(sz); s.m_drat.del(c); c.shrink(sz-1); } else { c.elim(l); } clause_use_list & occurs = m_use_list.get(l); occurs.erase_not_removed(c); m_sub_counter -= occurs.size()/2; unsigned sz0 = c.size(); if (cleanup_clause(c)) { // clause was satisfied TRACE("elim_lit", tout << "clause was satisfied\n";); remove_clause(c, true); return; } unsigned sz = c.size(); switch (sz) { case 0: TRACE("elim_lit", tout << "clause is empty\n";); s.set_conflict(); break; case 1: TRACE("elim_lit", tout << "clause became unit: " << c[0] << "\n";); c.restore(sz0); propagate_unit(c[0]); // 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())); remove_clause(c, sz0 != sz); break; default: TRACE("elim_lit", tout << "result: " << c << "\n";); m_sub_todo.insert(c); break; } } bool simplifier::subsume_with_binaries() { unsigned init = s.m_rand(); // start in a random place, since subsumption can be aborted unsigned num_lits = s.m_watches.size(); for (unsigned i = 0; i < num_lits; i++) { unsigned l_idx = (i + init) % num_lits; watch_list & wlist = get_wlist(to_literal(l_idx)); literal l = ~to_literal(l_idx); // should not traverse wlist using iterators, since back_subsumption1 may add new binary clauses there for (unsigned j = 0; j < wlist.size(); j++) { watched w = wlist[j]; if (w.is_binary_non_learned_clause()) { literal l2 = w.get_literal(); if (l.index() < l2.index()) { m_dummy.set(l, l2, w.is_learned()); clause & c = *(m_dummy.get()); back_subsumption1(c); if (w.is_learned() && !c.is_learned()) { SASSERT(wlist[j] == w); TRACE("set_not_learned_bug", tout << "marking as not learned: " << l2 << " " << wlist[j].is_learned() << "\n";); wlist[j].set_learned(false); mark_as_not_learned_core(get_wlist(~l2), l); } if (s.inconsistent()) return false; } } } if (m_sub_counter < 0) break; } return true; } void simplifier::mark_as_not_learned_core(watch_list & wlist, literal l2) { for (watched & w : wlist) { if (w.is_binary_clause() && w.get_literal() == l2 && w.is_learned()) { w.set_learned(false); return; } } } void simplifier::mark_as_not_learned(literal l1, literal l2) { mark_as_not_learned_core(get_wlist(~l1), l2); mark_as_not_learned_core(get_wlist(~l2), l1); } struct bin_lt { bool operator()(watched const & w1, watched const & w2) const { if (!w1.is_binary_clause()) return false; if (!w2.is_binary_clause()) return true; unsigned l1_idx = w1.get_literal().index(); unsigned l2_idx = w2.get_literal().index(); if (l1_idx < l2_idx) return true; if (l1_idx == l2_idx && !w1.is_learned() && w2.is_learned()) return true; return false; } }; /** \brief Eliminate duplicated binary clauses. */ void simplifier::elim_dup_bins() { #ifdef _TRACE unsigned l_idx = 0; #endif unsigned elim = 0; for (watch_list & wlist : s.m_watches) { checkpoint(); std::stable_sort(wlist.begin(), wlist.end(), bin_lt()); literal last_lit = null_literal; watch_list::iterator it = wlist.begin(); watch_list::iterator itprev = it; watch_list::iterator end = wlist.end(); for (; it != end; ++it) { if (!it->is_binary_clause()) { *itprev = *it; itprev++; continue; } if (it->get_literal() == last_lit) { TRACE("subsumption", tout << "eliminating: " << ~to_literal(l_idx) << " " << it->get_literal() << "\n";); elim++; } else { last_lit = it->get_literal(); *itprev = *it; itprev++; } } wlist.set_end(itprev); TRACE_CODE(l_idx++;); } m_num_subsumed += elim/2; // each binary clause is "eliminated" twice. } struct simplifier::subsumption_report { simplifier & m_simplifier; stopwatch m_watch; unsigned m_num_subsumed; unsigned m_num_sub_res; subsumption_report(simplifier & s): m_simplifier(s), m_num_subsumed(s.m_num_subsumed), m_num_sub_res(s.m_num_sub_res) { m_watch.start(); } ~subsumption_report() { m_watch.stop(); IF_VERBOSE(SAT_VB_LVL, verbose_stream() << " (sat-subsumer :subsumed " << (m_simplifier.m_num_subsumed - m_num_subsumed) << " :subsumption-resolution " << (m_simplifier.m_num_sub_res - m_num_sub_res) << " :threshold " << m_simplifier.m_sub_counter << mem_stat() << " :time " << std::fixed << std::setprecision(2) << m_watch.get_seconds() << ")\n";); } }; void simplifier::subsume() { subsumption_report rpt(*this); elim_dup_bins(); subsume_with_binaries(); TRACE("subsumption_bug", s.display(tout);); while (true) { TRACE("subsumption", tout << "sub_todo size: " << m_sub_todo.size() << "\n";); m_sub_counter -= m_sub_bin_todo.size(); while (!m_sub_bin_todo.empty()) { checkpoint(); m_dummy.set(m_sub_bin_todo.back()); m_sub_bin_todo.pop_back(); clause & c = *(m_dummy.get()); bool was_learned = c.is_learned(); back_subsumption1(c); if (was_learned && !c.is_learned()) { mark_as_not_learned(c[0], c[1]); } } checkpoint(); TRACE("subsumption_bug", s.display(tout);); if (m_sub_todo.empty()) { m_last_sub_trail_sz = s.m_trail.size(); break; } if (m_sub_counter < 0) break; clause & c = m_sub_todo.erase(); c.unmark_strengthened(); m_sub_counter--; TRACE("subsumption", tout << "next: " << c << "\n";); if (s.m_trail.size() > m_last_sub_trail_sz) { unsigned sz0 = c.size(); if (cleanup_clause(c)) { remove_clause(c, true); continue; } unsigned sz = c.size(); switch (sz) { case 0: s.set_conflict(); return; case 1: c.restore(sz0); propagate_unit(c[0]); // unit propagation removes c continue; 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, sz != sz0); continue; default: break; } } TRACE("subsumption", tout << "using: " << c << "\n";); back_subsumption1(c); } } struct simplifier::blocked_clause_elim { class literal_lt { use_list const & m_use_list; vector const & m_watches; public: literal_lt(use_list const & l, vector const & ws):m_use_list(l), m_watches(ws) {} unsigned weight(unsigned l_idx) const { return 2*m_use_list.get(~to_literal(l_idx)).size() + m_watches[l_idx].size(); } bool operator()(unsigned l_idx1, unsigned l_idx2) const { return weight(l_idx1) < weight(l_idx2); } }; class clause_ante { bool m_from_ri; literal m_lit1; literal m_lit2; clause* m_clause; public: clause_ante(): m_from_ri(false), m_lit1(null_literal), m_lit2(null_literal), m_clause(nullptr) {} clause_ante(literal l1, bool from_ri): m_from_ri(from_ri), m_lit1(l1), m_lit2(null_literal), m_clause(nullptr) {} clause_ante(literal l1, literal l2): m_from_ri(false), m_lit1(l1), m_lit2(l2), m_clause(nullptr) {} clause_ante(clause& c): m_from_ri(false), m_lit1(null_literal), m_lit2(null_literal), m_clause(&c) {} literal lit1() const { return m_lit1; } literal lit2() const { return m_lit2; } clause* cls() const { return m_clause; } bool from_ri() const { return m_from_ri; } bool operator==(clause_ante const& a) const { return a.m_lit1 == m_lit1 && a.m_lit2 == m_lit2 && a.m_clause == m_clause; } std::ostream& display(std::ostream& out, literal lit) const { if (cls()) { out << *cls() << " "; } else { out << "(" << ~lit; } if (lit1() != null_literal) { out << " " << lit1(); } if (lit2() != null_literal) { out << " " << lit2(); } if (!cls()) out << ")"; if (from_ri()) out << "ri"; out << "\n"; return out; } }; class queue { heap m_queue; public: queue(use_list const & l, vector const & ws):m_queue(128, literal_lt(l, ws)) {} void insert(literal l) { unsigned idx = l.index(); m_queue.reserve(idx + 1); SASSERT(!m_queue.contains(idx)); m_queue.insert(idx); } void decreased(literal l) { unsigned idx = l.index(); if (m_queue.contains(idx)) m_queue.decreased(idx); else m_queue.insert(idx); } literal next() { SASSERT(!empty()); return to_literal(m_queue.erase_min()); } bool empty() const { return m_queue.empty(); } void reset() { m_queue.reset(); } }; simplifier & s; int m_counter; model_converter & m_mc; queue m_queue; literal_vector m_covered_clause; // covered clause svector m_covered_antecedent; // explanations for literals in covered clause literal_vector m_intersection; // current resolution intersection literal_vector m_tautology; // literals that are used in blocking tautology literal_vector m_new_intersection; 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): s(_s), m_counter(limit), m_mc(_mc), m_queue(l, wlist), 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) { SASSERT(process_var(l.var())); m_queue.insert(l); } bool process_var(bool_var v) { 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, acce_t, abce_t, ate_t, no_t }; void operator()() { if (s.acce_enabled()) { cce(); } if (s.ate_enabled() && !s.abce_enabled() && !s.acce_enabled()) { cce(); } if (s.cce_enabled() && !s.acce_enabled()) { cce(); } if (s.abce_enabled() && !s.acce_enabled()) { cce(); } if (s.bce_enabled() && !s.cce_enabled() && !s.abce_enabled()) { cce(); } if (s.bca_enabled()) { bca(); } } 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)) { insert(literal(v, false)); insert(literal(v, true)); } } } void reset_intersection() { for (literal l : m_intersection) m_in_intersection[l.index()] = false; m_intersection.reset(); } void add_intersection(literal lit) { m_intersection.push_back(lit); m_in_intersection[lit.index()] = true; } // // Resolve intersection // Find literals that are in the intersection of all resolvents with l. // bool resolution_intersection(literal l, bool adding) { unsigned tsz = m_tautology.size(); reset_intersection(); if (!process_var(l.var())) return false; bool first = true; VERIFY(s.value(l) == l_undef); for (watched & w : s.get_wlist(l)) { // when adding a blocked clause, then all non-learned clauses have to be considered for the // resolution intersection. bool process_bin = adding ? w.is_binary_clause() : w.is_binary_non_learned_clause(); if (process_bin) { literal lit = w.get_literal(); if (s.is_marked(~lit) && lit != ~l) { m_tautology.push_back(~lit); continue; // tautology } if (!first || s.is_marked(lit)) { reset_intersection(); m_tautology.shrink(tsz); return false; // intersection is empty or does not add anything new. } first = false; SASSERT(m_intersection.empty()); add_intersection(lit); } } clause_use_list & neg_occs = s.m_use_list.get(~l); for (auto it = neg_occs.mk_iterator(); !it.at_end(); it.next()) { bool tautology = false; clause & c = it.curr(); if (c.is_learned() && !adding) continue; if (c.was_removed()) continue; for (literal lit : c) { if (s.is_marked(~lit) && lit != ~l) { m_tautology.push_back(~lit); tautology = true; break; } } if (!tautology) { if (first) { for (literal lit : c) if (lit != ~l && !s.is_marked(lit)) add_intersection(lit); first = false; } else { m_new_intersection.reset(); for (literal lit : c) if (m_in_intersection[lit.index()]) m_new_intersection.push_back(lit); reset_intersection(); for (literal lit : m_new_intersection) add_intersection(lit); } if (m_intersection.empty()) { m_tautology.shrink(tsz); return false; } } } // remove tautology literals if literal has no resolution intersection if (m_intersection.empty() && !first) { m_tautology.shrink(tsz); } return first; } bool check_abce_tautology(literal l) { unsigned tsz = m_tautology.size(); if (!process_var(l.var())) return false; for (watched & w : s.get_wlist(l)) { if (w.is_binary_non_learned_clause()) { literal lit = w.get_literal(); VERIFY(lit != ~l); if (!s.is_marked(~lit)) { m_tautology.shrink(tsz); return false; } m_tautology.push_back(~lit); } } clause_use_list & neg_occs = s.m_use_list.get(~l); for (auto it = neg_occs.mk_iterator(); !it.at_end(); it.next()) { clause & c = it.curr(); if (c.is_learned() || c.was_removed()) continue; bool tautology = false; for (literal lit : c) { if (s.is_marked(~lit) && lit != ~l) { tautology = true; m_tautology.push_back(~lit); break; } } if (!tautology) { m_tautology.shrink(tsz); return false; } } return true; } bool revisit_binary(literal l1, literal l2) const { return m_clause.is_binary() && ((m_clause[0] == l1 && m_clause[1] == l2) || (m_clause[0] == l2 && m_clause[1] == l1)); } bool revisit_clause(clause const& c) const { return !m_clause.is_binary() && (m_clause.get_clause() == &c); } /** \brief idx is the index of the blocked literal. m_tautology contains literals that were used to establish that the current members of m_covered_clause is blocked. This routine removes literals that were not relevant to establishing it was blocked. It has a bug: literals that are used to prune tautologies during resolution intersection should be included in the dependencies. They may get used in some RI prunings and then they have to be included to avoid flipping RI literals. */ void minimize_covered_clause(unsigned 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); s.mark_visited(m_covered_clause[idx]); for (unsigned i = 0; i < m_covered_clause.size(); ++i) { literal lit = m_covered_clause[i]; if (m_covered_antecedent[i] == clause_ante()) s.mark_visited(lit); if (s.is_marked(lit)) idx = i; } for (unsigned i = idx; i > 0; --i) { literal lit = m_covered_clause[i]; if (!s.is_marked(lit)) continue; clause_ante const& ante = m_covered_antecedent[i]; if (ante.cls()) { for (literal l : *ante.cls()) { if (l != ~lit) s.mark_visited(l); } } if (ante.lit1() != null_literal) { s.mark_visited(ante.lit1()); } if (ante.lit2() != null_literal) { s.mark_visited(ante.lit2()); } } unsigned j = 0; literal blocked = null_literal; for (unsigned i = 0; i <= idx; ++i) { literal lit = m_covered_clause[i]; if (s.is_marked(lit)) { // // Record that the resolving literal for // resolution intersection can be flipped. // clause_ante const& ante = m_covered_antecedent[i]; if (ante.from_ri() && blocked != ante.lit1()) { blocked = ante.lit1(); VERIFY(s.value(blocked) == l_undef); m_mc.stackv().push_back(std::make_pair(j, blocked)); } m_covered_clause[j++] = lit; s.unmark_visited(lit); } } for (literal l : m_covered_clause) VERIFY(!s.is_marked(l)); for (bool_var v = 0; v < s.s.num_vars(); ++v) VERIFY(!s.is_marked(literal(v, true)) && !s.is_marked(literal(v, false))); // unsigned sz0 = m_covered_clause.size(); m_covered_clause.resize(j); VERIFY(j >= m_clause.size()); } /* * C \/ l l \/ lit * ------------------- * C \/ l \/ ~lit * * C \/ lit \/ l l \/ lit * ------------------------ * l \/ lit C \/ lit \/ l can be removed * * C \/ l1 \/ ... \/ lk l1 \/ ... \/ lk \/ lit * ----------------------------------------------- * C \/ l1 \/ ... \/ lk \/ ~lit * unless C contains lit, and it is a tautology. */ bool add_ala() { 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)) { m_covered_clause.push_back(~lit); m_covered_antecedent.push_back(clause_ante(l, false)); s.mark_visited(~lit); } } } clause_use_list & pos_occs = s.m_use_list.get(l); clause_use_list::iterator it = pos_occs.mk_iterator(); for (; !it.at_end(); it.next()) { clause & c = it.curr(); if (c.is_learned() || c.was_removed()) continue; if (revisit_clause(c)) continue; literal lit1 = null_literal; bool ok = true; for (literal lit : c) { if (lit == l) continue; if (s.is_marked(lit)) continue; if (!s.is_marked(~lit) && lit1 == null_literal) { lit1 = lit; } else { ok = false; break; } } if (!ok) continue; if (lit1 == null_literal) { ++m_ala_benefit; return true; } m_covered_clause.push_back(~lit1); m_covered_antecedent.push_back(clause_ante(c)); s.mark_visited(~lit1); } } return false; } /* * C \/ l ~l \/ lit \/ D_i for i = 1...N all the clauses that have ~l * ------------------------- * C \/ l \/ lit * * */ bool add_cla(literal& blocked) { for (unsigned i = 0; i < m_covered_clause.size(); ++i) { literal lit = m_covered_clause[i]; if (resolution_intersection(lit, false)) { blocked = m_covered_clause[i]; minimize_covered_clause(i); return true; } for (literal l : m_intersection) { if (!s.is_marked(l)) { s.mark_visited(l); m_covered_clause.push_back(l); m_covered_antecedent.push_back(clause_ante(lit, true)); } } } return false; } bool above_threshold(unsigned sz0) const { return sz0 * 400 < m_covered_clause.size(); } void reset_mark() { for (literal l : m_covered_clause) s.unmark_visited(l); } template elim_type cce(literal& blocked, model_converter::kind& k) { bool first = true; unsigned sz = 0, sz0 = m_covered_clause.size(); for (literal l : m_covered_clause) s.mark_visited(l); shuffle(m_covered_clause.size(), m_covered_clause.c_ptr(), s.s.m_rand); m_tautology.reset(); m_mc.stackv().reset(); m_ala_qhead = 0; switch (et) { case cce_t: k = model_converter::CCE; break; case acce_t: k = model_converter::ACCE; break; default: k = model_converter::BCE; break; } /* * For blocked clause elimination with asymmetric literal addition (ABCE) * it suffices to check if one of the original * literals in the clause is blocked modulo the additional literals added to the clause. * So we record sz0, the original set of literals in the clause, mark additional literals, * and then check if any of the first sz0 literals are blocked. */ if (et == ate_t) { bool ala = add_ala(); reset_mark(); m_covered_clause.shrink(sz0); return ala ? ate_t : no_t; } while (m_covered_clause.size() > sz && !above_threshold(sz0)) { if ((et == abce_t || et == acce_t) && add_ala()) { reset_mark(); if (first) { m_covered_clause.shrink(sz0); } else { /* * tautology depends on resolution intersection. * literals used for resolution intersection may have to be flipped. */ for (literal l : m_covered_clause) { m_tautology.push_back(l); s.mark_visited(l); } minimize_covered_clause(m_covered_clause.size()-1); } return ate_t; } if (first) { for (unsigned i = 0; i < sz0; ++i) { if (check_abce_tautology(m_covered_clause[i])) { blocked = m_covered_clause[i]; reset_mark(); m_covered_clause.shrink(sz0); if (et == bce_t) return bce_t; k = model_converter::ABCE; return abce_t; } } } first = false; if (et == abce_t || et == bce_t) { break; } /* * Add resolution intersection while checking if the clause becomes a tautology. */ sz = m_covered_clause.size(); if ((et == cce_t || et == acce_t) && add_cla(blocked)) { reset_mark(); return et; } } reset_mark(); return no_t; } // perform covered clause elimination. // first extract the covered literal addition (CLA). // then check whether the CLA is blocked. template elim_type cce(clause& c, literal& blocked, model_converter::kind& k) { m_clause = clause_wrapper(c); m_covered_clause.reset(); m_covered_antecedent.reset(); for (literal l : c) { m_covered_clause.push_back(l); m_covered_antecedent.push_back(clause_ante()); } return cce(blocked, k); } template elim_type cce(literal l1, literal l2, literal& blocked, model_converter::kind& k) { m_clause = clause_wrapper(l1, l2); m_covered_clause.reset(); m_covered_antecedent.reset(); m_covered_clause.push_back(l1); m_covered_clause.push_back(l2); m_covered_antecedent.push_back(clause_ante()); m_covered_antecedent.push_back(clause_ante()); return cce(blocked, k); } template void cce() { insert_queue(); cce_clauses(); cce_binary(); } template void cce_binary() { 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()); } } template void process_cce_binary(literal l) { literal blocked = null_literal; watch_list & wlist = s.get_wlist(~l); m_counter -= wlist.size(); model_converter::kind k; for (watched& w : wlist) { if (!w.is_binary_non_learned_clause()) continue; if (!select_clause(2)) continue; literal l2 = w.get_literal(); elim_type r = cce(l, l2, blocked, k); inc_bc(r); switch (r) { case ate_t: w.set_learned(true); s.s.set_learned1(l2, l, true); m_mc.add_ate(m_covered_clause); break; case no_t: break; default: w.set_learned(true); s.s.set_learned1(l2, l, true); 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; 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); inc_bc(r); switch (r) { case ate_t: m_mc.add_ate(m_covered_clause); s.set_learned(c); break; case no_t: break; default: block_covered_clause(c, blocked, k); s.set_learned(c); break; } s.checkpoint(); if (reached_max_cost()) { break; } } } void inc_bc(elim_type et) { switch (et) { case cce_t: s.m_num_cce++; break; case acce_t: s.m_num_acce++; break; case abce_t: s.m_num_abce++; break; case ate_t: s.m_num_ate++; break; case bce_t: s.m_num_bce++; break; default: break; } } // select 25% of clauses size 2, 3 // always try to remove larger clauses. template bool select_clause(unsigned sz) { return (sz > 3) || s.s.m_rand(4) == 0; } void block_covered_clause(clause& c, literal l, model_converter::kind k) { 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()); for (literal lit : c) { if (lit != l && process_var(lit.var())) { m_queue.decreased(~lit); } } m_mc.insert(new_entry, m_covered_clause); m_mc.set_clause(new_entry, c); } void block_covered_binary(watched const& w, literal l1, literal blocked, model_converter::kind k) { SASSERT(!s.is_external(blocked)); model_converter::entry& new_entry = m_mc.mk(k, blocked.var()); literal l2 = w.get_literal(); TRACE("blocked_clause", tout << "new blocked clause: " << l2 << " " << l1 << "\n";); s.set_learned(l1, l2); m_mc.insert(new_entry, m_covered_clause); m_mc.set_clause(new_entry, l1, l2); m_queue.decreased(~l2); } void bca() { m_queue.reset(); insert_queue(); while (!m_queue.empty() && m_counter >= 0) { s.checkpoint(); bca(m_queue.next()); } } /* \brief blocked binary clause addition for literal l Let RI be the resolution intersection with l, e.g, RI are the literals that are in all clauses of the form ~l \/ C. If RI is non-empty, then let l' be a literal in RI. Then the following binary clause is blocked: l \/ ~l' */ void bca(literal l) { m_tautology.reset(); if (resolution_intersection(l, true)) { // this literal is pure. return; } for (literal l2 : m_intersection) { watched* w = find_binary_watch(s.get_wlist(~l), ~l2); if (!w) { s.s.mk_bin_clause(l, ~l2, true); ++s.m_num_bca; } } } bool all_tautology(literal l) { watch_list & wlist = s.get_wlist(l); m_counter -= wlist.size(); for (auto const& w : wlist) { if (w.is_binary_non_learned_clause() && !s.is_marked(~w.get_literal())) return false; } clause_use_list & neg_occs = s.m_use_list.get(~l); clause_use_list::iterator it = neg_occs.mk_iterator(); for (; !it.at_end(); it.next()) { clause & c = it.curr(); if (c.is_learned()) continue; if (c.was_removed()) continue; m_counter -= c.size(); unsigned sz = c.size(); unsigned i; for (i = 0; i < sz; i++) { if (s.is_marked(~c[i])) break; } if (i == sz) return false; } if (s.s.m_ext) { ext_constraint_list const& ext_list = s.m_ext_use_list.get(~l); for (ext_constraint_idx idx : ext_list) { if (!s.s.m_ext->is_blocked(l, idx)) { return false; } } } return true; } }; struct simplifier::blocked_cls_report { simplifier & m_simplifier; stopwatch m_watch; unsigned m_num_bce; unsigned m_num_cce; unsigned m_num_acce; unsigned m_num_abce; unsigned m_num_ate; unsigned m_num_bca; blocked_cls_report(simplifier & s): m_simplifier(s), m_num_bce(s.m_num_bce), m_num_cce(s.m_num_cce), m_num_acce(s.m_num_acce), m_num_abce(s.m_num_abce), m_num_ate(s.m_num_ate), m_num_bca(s.m_num_bca) { m_watch.start(); } ~blocked_cls_report() { m_watch.stop(); IF_VERBOSE(SAT_VB_LVL, verbose_stream() << " (sat-blocked-clauses"; report(m_simplifier.m_num_ate, m_num_ate, " :ate "); report(m_simplifier.m_num_bce, m_num_bce, " :bce "); report(m_simplifier.m_num_abce, m_num_abce, " :abce "); report(m_simplifier.m_num_cce, m_num_cce, " :cce "); report(m_simplifier.m_num_bca, m_num_bca, " :bca "); report(m_simplifier.m_num_acce, m_num_acce, " :acce "); verbose_stream() << mem_stat() << " :time " << std::fixed << std::setprecision(2) << m_watch.get_seconds() << ")\n";); } void report(unsigned n, unsigned m, char const* s) { if (n > m) verbose_stream() << s << (n - m); } }; void simplifier::elim_blocked_clauses() { TRACE("blocked_clause_bug", tout << "trail: " << s.m_trail.size() << "\n"; s.display_watches(tout); s.display(tout);); blocked_cls_report rpt(*this); blocked_clause_elim elim(*this, m_blocked_clause_limit, s.m_mc, m_use_list, s.m_watches); elim(); } unsigned simplifier::num_nonlearned_bin(literal l) const { unsigned r = 0; watch_list const & wlist = get_wlist(~l); for (auto & w : wlist) { if (w.is_binary_non_learned_clause()) r++; } return r; } unsigned simplifier::get_to_elim_cost(bool_var v) const { literal pos_l(v, false); literal neg_l(v, true); unsigned num_pos = m_use_list.get(pos_l).size(); unsigned num_neg = m_use_list.get(neg_l).size(); unsigned num_bin_pos = num_nonlearned_bin(pos_l); unsigned num_bin_neg = num_nonlearned_bin(neg_l); unsigned cost = 2 * num_pos * num_neg + num_pos * num_bin_neg + num_neg * num_bin_pos; CTRACE("elim_vars_detail", cost == 0, tout << v << " num_pos: " << num_pos << " num_neg: " << num_neg << " num_bin_pos: " << num_bin_pos << " num_bin_neg: " << num_bin_neg << " cost: " << cost << "\n";); return cost; } typedef std::pair bool_var_and_cost; struct bool_var_and_cost_lt { bool operator()(bool_var_and_cost const & p1, bool_var_and_cost const & p2) const { return p1.second < p2.second; } }; void simplifier::order_vars_for_elim(bool_var_vector & r) { svector tmp; for (bool_var v : m_elim_todo) { if (is_external(v)) continue; if (was_eliminated(v)) continue; if (value(v) != l_undef) continue; unsigned c = get_to_elim_cost(v); tmp.push_back(bool_var_and_cost(v, c)); } m_elim_todo.reset(); std::stable_sort(tmp.begin(), tmp.end(), bool_var_and_cost_lt()); TRACE("elim_vars", for (auto& p : tmp) tout << "(" << p.first << ", " << p.second << ") "; tout << "\n";); for (auto& p : tmp) r.push_back(p.first); } /** \brief Collect clauses and binary clauses containing l. */ void simplifier::collect_clauses(literal l, clause_wrapper_vector & r) { clause_use_list const & cs = m_use_list.get(l); for (auto it = cs.mk_iterator(); !it.at_end(); it.next()) { clause& c = it.curr(); if (!c.is_learned() && !c.was_removed()) { r.push_back(clause_wrapper(c)); SASSERT(r.back().size() == c.size()); } } watch_list & wlist = get_wlist(~l); for (auto & w : wlist) { if (w.is_binary_non_learned_clause()) { r.push_back(clause_wrapper(l, w.get_literal())); SASSERT(r.back().size() == 2); } } } /** \brief Resolve clauses c1 and c2. c1 must contain l. c2 must contain ~l. Store result in r. Return false if the result is a tautology */ bool simplifier::resolve(clause_wrapper const & c1, clause_wrapper const & c2, literal l, literal_vector & r) { CTRACE("resolve_bug", !c1.contains(l), tout << c1 << "\n" << c2 << "\nl: " << l << "\n";); SASSERT(c1.contains(l)); SASSERT(c2.contains(~l)); bool res = true; m_elim_counter -= c1.size() + c2.size(); unsigned sz1 = c1.size(); for (unsigned i = 0; i < sz1; ++i) { literal l1 = c1[i]; if (l == l1) continue; m_visited[l1.index()] = true; r.push_back(l1); } literal not_l = ~l; unsigned sz2 = c2.size(); for (unsigned i = 0; i < sz2; ++i) { literal l2 = c2[i]; if (not_l == l2) continue; if (m_visited[(~l2).index()]) { res = false; break; } if (!m_visited[l2.index()]) r.push_back(l2); } for (unsigned i = 0; i < sz1; ++i) { literal l1 = c1[i]; m_visited[l1.index()] = false; } return res; } void simplifier::save_clauses(model_converter::entry & mc_entry, clause_wrapper_vector const & cs) { for (auto & e : cs) { s.m_mc.insert(mc_entry, e); } } void simplifier::add_non_learned_binary_clause(literal l1, literal l2) { s.mk_bin_clause(l1, l2, false); } /** \brief Eliminate the binary clauses watched by l, when l.var() is being eliminated */ void simplifier::remove_bin_clauses(literal l) { watch_list & wlist = get_wlist(~l); 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) { TRACE("bin_clause_bug", tout << "removing: " << l << " " << it2->get_literal() << "\n";); m_sub_bin_todo.erase(bin_clause(l2, l, it2->is_learned())); continue; } *itprev = *it2; itprev++; } wlist2.set_end(itprev); m_sub_bin_todo.erase(bin_clause(l, l2, w.is_learned())); } } TRACE("bin_clause_bug", tout << "collapsing watch_list of: " << l << "\n";); wlist.finalize(); } /** \brief Eliminate the clauses where the variable being eliminated occur. */ void simplifier::remove_clauses(clause_use_list const & cs, literal l) { for (auto it = cs.mk_iterator(); !it.at_end(); ) { clause & c = it.curr(); it.next(); SASSERT(c.contains(l)); 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; } } } bool simplifier::try_eliminate(bool_var v) { TRACE("resolution_bug", tout << "processing: " << v << "\n";); if (value(v) != l_undef) return false; literal pos_l(v, false); literal neg_l(v, true); unsigned num_bin_pos = num_nonlearned_bin(pos_l); unsigned num_bin_neg = num_nonlearned_bin(neg_l); clause_use_list & pos_occs = m_use_list.get(pos_l); clause_use_list & neg_occs = m_use_list.get(neg_l); unsigned num_pos = pos_occs.num_irredundant() + num_bin_pos; unsigned num_neg = neg_occs.num_irredundant() + num_bin_neg; TRACE("resolution", tout << v << " num_pos: " << num_pos << " neg_pos: " << num_neg << "\n";); if (num_pos >= m_res_occ_cutoff && num_neg >= m_res_occ_cutoff) return false; unsigned before_lits = num_bin_pos*2 + num_bin_neg*2; for (auto it = pos_occs.mk_iterator(); !it.at_end(); it.next()) { if (!it.curr().is_learned()) before_lits += it.curr().size(); } for (auto it = neg_occs.mk_iterator(); !it.at_end(); it.next()) { if (!it.curr().is_learned()) before_lits += it.curr().size(); } TRACE("resolution", tout << v << " num_pos: " << num_pos << " neg_pos: " << num_neg << " before_lits: " << before_lits << "\n";); if (num_pos >= m_res_occ_cutoff3 && num_neg >= m_res_occ_cutoff3 && before_lits > m_res_lit_cutoff3 && s.m_clauses.size() > m_res_cls_cutoff2) return false; if (num_pos >= m_res_occ_cutoff2 && num_neg >= m_res_occ_cutoff2 && before_lits > m_res_lit_cutoff2 && s.m_clauses.size() > m_res_cls_cutoff1 && s.m_clauses.size() <= m_res_cls_cutoff2) return false; if (num_pos >= m_res_occ_cutoff1 && num_neg >= m_res_occ_cutoff1 && before_lits > m_res_lit_cutoff1 && s.m_clauses.size() <= m_res_cls_cutoff1) return false; m_pos_cls.reset(); m_neg_cls.reset(); collect_clauses(pos_l, m_pos_cls); collect_clauses(neg_l, m_neg_cls); TRACE("resolution_detail", tout << "collecting number of after_clauses\n";); unsigned before_clauses = num_pos + num_neg; unsigned after_clauses = 0; for (clause_wrapper& c1 : m_pos_cls) { for (clause_wrapper& c2 : m_neg_cls) { m_new_cls.reset(); if (resolve(c1, c2, pos_l, m_new_cls)) { TRACE("resolution_detail", tout << c1 << "\n" << c2 << "\n-->\n"; for (literal l : m_new_cls) tout << l << " "; tout << "\n";); after_clauses++; if (after_clauses > before_clauses) { TRACE("resolution", tout << "too many after clauses: " << after_clauses << "\n";); return false; } } } } TRACE("resolution", tout << "found var to eliminate, before: " << before_clauses << " after: " << after_clauses << "\n";); m_elim_counter -= num_pos * num_neg + before_lits; m_elim_counter -= num_pos * num_neg + before_lits; // 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.set_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; TRACE("resolution_new_cls", tout << c1 << "\n" << c2 << "\n-->\n" << m_new_cls << "\n";); if (cleanup_clause(m_new_cls)) { continue; // clause is already satisfied. } switch (m_new_cls.size()) { case 0: s.set_conflict(); break; case 1: propagate_unit(m_new_cls[0]); break; case 2: s.m_stats.m_mk_bin_clause++; add_non_learned_binary_clause(m_new_cls[0], m_new_cls[1]); back_subsumption1(m_new_cls[0], m_new_cls[1], false); break; default: if (m_new_cls.size() == 3) s.m_stats.m_mk_ter_clause++; else s.m_stats.m_mk_clause++; clause * new_c = s.alloc_clause(m_new_cls.size(), m_new_cls.c_ptr(), false); if (s.m_config.m_drat) s.m_drat.add(*new_c, true); s.m_clauses.push_back(new_c); m_use_list.insert(*new_c); if (m_sub_counter > 0) back_subsumption1(*new_c); else back_subsumption0(*new_c); break; } if (s.inconsistent()) 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; } struct simplifier::elim_var_report { simplifier & m_simplifier; stopwatch m_watch; unsigned m_num_elim_vars; elim_var_report(simplifier & s): m_simplifier(s), m_num_elim_vars(s.m_num_elim_vars) { m_watch.start(); } ~elim_var_report() { m_watch.stop(); IF_VERBOSE(SAT_VB_LVL, verbose_stream() << " (sat-resolution :elim-vars " << (m_simplifier.m_num_elim_vars - m_num_elim_vars) << " :threshold " << m_simplifier.m_elim_counter << mem_stat() << " :time " << std::fixed << std::setprecision(2) << m_watch.get_seconds() << ")\n";); } }; void simplifier::elim_vars() { if (!elim_vars_enabled()) return; elim_var_report rpt(*this); bool_var_vector vars; order_vars_for_elim(vars); sat::elim_vars elim_bdd(*this); for (bool_var v : vars) { checkpoint(); if (m_elim_counter < 0) break; if (is_external(v)) { // skip } else if (try_eliminate(v)) { m_num_elim_vars++; } else if (elim_vars_bdd_enabled() && elim_bdd(v)) { m_num_elim_vars++; } } m_pos_cls.finalize(); m_neg_cls.finalize(); m_new_cls.finalize(); } void simplifier::updt_params(params_ref const & _p) { sat_simplifier_params p(_p); m_cce = p.cce(); m_acce = p.acce(); m_bca = false && p.bca(); // disabled m_abce = p.abce(); m_ate = p.ate(); m_bce_delay = p.bce_delay(); m_bce = p.bce(); m_bce_at = p.bce_at(); m_retain_blocked_clauses = p.retain_blocked_clauses(); m_blocked_clause_limit = p.blocked_clause_limit(); m_res_limit = p.resolution_limit(); m_res_occ_cutoff = p.resolution_occ_cutoff(); m_res_occ_cutoff1 = p.resolution_occ_cutoff_range1(); m_res_occ_cutoff2 = p.resolution_occ_cutoff_range2(); m_res_occ_cutoff3 = p.resolution_occ_cutoff_range3(); m_res_lit_cutoff1 = p.resolution_lit_cutoff_range1(); m_res_lit_cutoff2 = p.resolution_lit_cutoff_range2(); m_res_lit_cutoff3 = p.resolution_lit_cutoff_range3(); m_res_cls_cutoff1 = p.resolution_cls_cutoff1(); m_res_cls_cutoff2 = p.resolution_cls_cutoff2(); m_subsumption = p.subsumption(); m_subsumption_limit = p.subsumption_limit(); m_elim_vars = p.elim_vars(); m_elim_vars_bdd = false && p.elim_vars_bdd(); // buggy? m_elim_vars_bdd_delay = p.elim_vars_bdd_delay(); m_incremental_mode = s.get_config().m_incremental && !p.override_incremental(); } void simplifier::collect_param_descrs(param_descrs & r) { sat_simplifier_params::collect_param_descrs(r); } void simplifier::collect_statistics(statistics & st) const { 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() { m_num_bce = 0; m_num_cce = 0; m_num_acce = 0; m_num_abce = 0; m_num_subsumed = 0; m_num_sub_res = 0; m_num_elim_lits = 0; m_num_elim_vars = 0; m_num_bca = 0; m_num_ate = 0; } }; z3-z3-4.8.7/src/sat/sat_simplifier.h000066400000000000000000000214111356505360400171670ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_simplifier.h Abstract: SAT simplification procedures that use a "full" occurrence list: Subsumption, Blocked Clause Removal, Variable Elimination, ... Author: Leonardo de Moura (leonardo) 2011-05-24. Revision History: --*/ #ifndef SAT_SIMPLIFIER_H_ #define SAT_SIMPLIFIER_H_ #include "sat/sat_types.h" #include "sat/sat_clause.h" #include "sat/sat_clause_set.h" #include "sat/sat_clause_use_list.h" #include "sat/sat_extension.h" #include "sat/sat_watched.h" #include "sat/sat_model_converter.h" #include "util/heap.h" #include "util/statistics.h" #include "util/params.h" namespace sat { class solver; class use_list { vector m_use_list; public: void init(unsigned num_vars); void insert(clause & c); void block(clause & c); void unblock(clause & c); void erase(clause & c); void erase(clause & c, literal l); clause_use_list & get(literal l) { return m_use_list[l.index()]; } clause_use_list const & get(literal l) const { return m_use_list[l.index()]; } void finalize() { m_use_list.finalize(); } std::ostream& display(std::ostream& out, literal l) const { return m_use_list[l.index()].display(out); } }; class simplifier { friend class ba_solver; friend class elim_vars; solver & s; unsigned m_num_calls; use_list m_use_list; ext_use_list m_ext_use_list; clause_set m_sub_todo; svector m_sub_bin_todo; unsigned m_last_sub_trail_sz; // size of the trail since last cleanup bool_var_set m_elim_todo; bool m_need_cleanup; tmp_clause m_dummy; // simplifier extra variable fields. svector m_visited; // transient // counters int m_sub_counter; int m_elim_counter; // config bool m_abce; // block clauses using asymmetric added literals bool m_cce; // covered clause elimination bool m_acce; // cce with asymmetric literal addition bool m_bca; // blocked (binary) clause addition. unsigned m_bce_delay; bool m_bce; // blocked clause elimination bool m_ate; // asymmetric tautology elimination unsigned m_bce_at; bool m_retain_blocked_clauses; unsigned m_blocked_clause_limit; bool m_incremental_mode; bool m_resolution; unsigned m_res_limit; unsigned m_res_occ_cutoff; unsigned m_res_occ_cutoff1; unsigned m_res_occ_cutoff2; unsigned m_res_occ_cutoff3; unsigned m_res_lit_cutoff1; unsigned m_res_lit_cutoff2; unsigned m_res_lit_cutoff3; unsigned m_res_cls_cutoff1; unsigned m_res_cls_cutoff2; bool m_subsumption; unsigned m_subsumption_limit; bool m_elim_vars; bool m_elim_vars_bdd; unsigned m_elim_vars_bdd_delay; // stats unsigned m_num_bce; unsigned m_num_cce; unsigned m_num_acce; unsigned m_num_abce; unsigned m_num_bca; unsigned m_num_ate; unsigned m_num_subsumed; unsigned m_num_elim_vars; unsigned m_num_sub_res; unsigned m_num_elim_lits; bool m_learned_in_use_lists; unsigned m_old_num_elim_vars; struct size_lt { bool operator()(clause const * c1, clause const * c2) const { return c1->size() > c2->size(); } }; void checkpoint(); void initialize(); void init_visited(); void mark_visited(literal l) { m_visited[l.index()] = true; } void unmark_visited(literal l) { m_visited[l.index()] = false; } bool is_marked(literal l) const { return m_visited[l.index()] != 0; } void mark_all_but(clause const & c, literal l); void unmark_all(clause const & c); void register_clauses(clause_vector & cs); void remove_clause(clause & c, bool is_unique); void set_learned(clause & c); void set_learned(literal l1, literal l2); bool_var get_min_occ_var(clause const & c) const; bool subsumes1(clause const & c1, clause const & c2, literal & l); void collect_subsumed1_core(clause const & c, clause_vector & out, literal_vector & out_lits, literal target); void collect_subsumed1(clause const & c, clause_vector & out, literal_vector & out_lits); clause_vector m_bs_cs; literal_vector m_bs_ls; void back_subsumption1(clause & c); void back_subsumption1(literal l1, literal l2, bool learned); literal get_min_occ_var0(clause const & c) const; bool subsumes0(clause const & c1, clause const & c2); void collect_subsumed0_core(clause const & c1, clause_vector & out, literal target); void collect_subsumed0(clause const & c1, clause_vector & out); void back_subsumption0(clause & c1); bool cleanup_clause(clause & c); bool cleanup_clause(literal_vector & c); void elim_lit(clause & c, literal l); void elim_dup_bins(); bool subsume_with_binaries(); void mark_as_not_learned_core(watch_list & wlist, literal l2); void mark_as_not_learned(literal l1, literal l2); void cleanup_watches(); void move_clauses(clause_vector & cs, bool learned); 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()); } bool was_eliminated(bool_var v) const; lbool value(bool_var v) const; lbool value(literal l) const; watch_list & get_wlist(literal l); watch_list const & get_wlist(literal l) const; struct blocked_clause_elim; void elim_blocked_clauses(); bool single_threaded() const; // { return s.m_config.m_num_threads == 1; } bool bce_enabled_base() const; bool ate_enabled() const; bool bce_enabled() const; bool acce_enabled() const; bool cce_enabled() const; bool abce_enabled() const; bool bca_enabled() const; bool elim_vars_bdd_enabled() const; bool elim_vars_enabled() const; unsigned num_nonlearned_bin(literal l) const; unsigned get_to_elim_cost(bool_var v) const; void order_vars_for_elim(bool_var_vector & r); void collect_clauses(literal l, clause_wrapper_vector & r); clause_wrapper_vector m_pos_cls; clause_wrapper_vector m_neg_cls; literal_vector m_new_cls; bool resolve(clause_wrapper const & c1, clause_wrapper const & c2, literal l, literal_vector & r); void save_clauses(model_converter::entry & mc_entry, clause_wrapper_vector const & cs); void add_non_learned_binary_clause(literal l1, literal l2); void remove_bin_clauses(literal l); void remove_clauses(clause_use_list const & cs, literal l); bool try_eliminate(bool_var v); void elim_vars(); struct blocked_cls_report; struct subsumption_report; struct elim_var_report; class scoped_finalize { simplifier& s; public: scoped_finalize(simplifier& s) : s(s) {} ~scoped_finalize() { s.scoped_finalize_fn(); } }; void scoped_finalize_fn(); public: simplifier(solver & s, params_ref const & p); ~simplifier(); void init_search() { m_num_calls = 0; } void insert_elim_todo(bool_var v) { m_elim_todo.insert(v); } void reset_todos() { m_elim_todo.reset(); m_sub_todo.reset(); m_sub_bin_todo.reset(); } void operator()(bool learned); void updt_params(params_ref const & p); static void collect_param_descrs(param_descrs & d); void finalize(); void collect_statistics(statistics & st) const; void reset_statistics(); void propagate_unit(literal l); void subsume(); }; }; #endif z3-z3-4.8.7/src/sat/sat_simplifier_params.pyg000066400000000000000000000105131356505360400211030ustar00rootroot00000000000000def_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 asymmetric literals'), ('cce', BOOL, False, 'eliminate covered clauses'), ('ate', BOOL, True, 'asymmetric tautology elimination'), ('acce', BOOL, False, 'eliminate covered clauses using asymmetric added literals'), ('bce_at', UINT, 2, 'eliminate blocked clauses only once at the given simplification round'), ('bca', BOOL, False, 'blocked clause addition - add blocked binary clauses'), ('bce_delay', UINT, 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 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'), ('resolution.occ_cutoff_range2', UINT, 5, 'second cutoff (number of positive/negative occurrences) for Boolean variable elimination, for problems containing more than res_cls_cutoff1 and less than res_cls_cutoff2'), ('resolution.occ_cutoff_range3', UINT, 3, 'second cutoff (number of positive/negative occurrences) for Boolean variable elimination, for problems containing more than res_cls_cutoff2'), ('resolution.lit_cutoff_range1', UINT, 700, 'second cutoff (total number of literals) for Boolean variable elimination, for problems containing less than res_cls_cutoff1 clauses'), ('resolution.lit_cutoff_range2', UINT, 400, 'second cutoff (total number of literals) for Boolean variable elimination, for problems containing more than res_cls_cutoff1 and less than res_cls_cutoff2'), ('resolution.lit_cutoff_range3', UINT, 300, 'second cutoff (total number of literals) for Boolean variable elimination, for problems containing more than res_cls_cutoff2'), ('resolution.cls_cutoff1', UINT, 100000000, 'limit1 - total number of problems clauses for the second cutoff of Boolean variable elimination'), ('resolution.cls_cutoff2', UINT, 700000000, 'limit2 - total number of problems clauses for the second cutoff of Boolean variable elimination'), ('elim_vars', BOOL, True, 'enable variable elimination using resolution during simplification'), ('elim_vars_bdd', BOOL, True, 'enable variable elimination using BDD recompilation during simplification'), ('elim_vars_bdd_delay', UINT, 3, 'delay elimination of variables using BDDs until after simplification round'), ('probing', BOOL, True, 'apply failed literal detection during simplification'), ('probing_limit', UINT, 5000000, 'limit to the number of probe calls'), ('probing_cache', BOOL, True, 'add binary literals as lemmas'), ('probing_cache_limit', UINT, 1024, 'cache binaries unless overall memory usage exceeds cache limit'), ('probing_binary', BOOL, True, 'probe binary clauses'), ('subsumption', BOOL, True, 'eliminate subsumed clauses'), ('subsumption.limit', UINT, 100000000, 'approx. maximum number of literals visited during subsumption (and subsumption resolution)'))) z3-z3-4.8.7/src/sat/sat_solver.cpp000066400000000000000000005213261356505360400167030ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_solver.cpp Abstract: SAT solver main class. Author: Leonardo de Moura (leonardo) 2011-05-21. Revision History: --*/ #include #include #include "util/luby.h" #include "util/trace.h" #include "util/max_cliques.h" #include "util/gparams.h" #include "sat/sat_solver.h" #include "sat/sat_integrity_checker.h" #include "sat/sat_lookahead.h" #include "sat/sat_unit_walk.h" #include "sat/sat_ddfw.h" #include "sat/sat_prob.h" #if defined(_MSC_VER) && !defined(_M_ARM) && !defined(_M_ARM64) # include #endif #define ENABLE_TERNARY true namespace sat { solver::solver(params_ref const & p, reslimit& l): solver_core(l), m_checkpoint_enabled(true), m_config(p), m_par(nullptr), m_drat(*this), m_cls_allocator_idx(false), m_cleaner(*this), m_simplifier(*this, p), m_scc(*this, p), m_asymm_branch(*this, p), m_probing(*this, p), m_mus(*this), m_inconsistent(false), m_searching(false), m_conflict(justification(0)), m_num_frozen(0), m_activity_inc(128), m_case_split_queue(m_activity), m_qhead(0), m_scope_lvl(0), m_search_lvl(0), m_fast_glue_avg(), m_slow_glue_avg(), m_fast_glue_backup(), m_slow_glue_backup(), m_trail_avg(), m_params(p), m_par_id(0), m_par_syncing_clauses(false) { init_reason_unknown(); updt_params(p); m_conflicts_since_gc = 0; m_conflicts_since_init = 0; m_next_simplify = 0; m_num_checkpoints = 0; m_simplifications = 0; m_touch_index = 0; m_ext = nullptr; m_cuber = nullptr; m_local_search = nullptr; m_mc.set_solver(this); } solver::~solver() { m_ext = nullptr; SASSERT(check_invariant()); TRACE("sat", tout << "Delete clauses\n";); del_clauses(m_clauses); TRACE("sat", tout << "Delete learned\n";); del_clauses(m_learned); dealloc(m_cuber); m_cuber = nullptr; } void solver::del_clauses(clause_vector& clauses) { for (clause * cp : clauses) dealloc_clause(cp); clauses.reset(); ++m_stats.m_non_learned_generation; } void solver::set_extension(extension* ext) { m_ext = ext; if (ext) ext->set_solver(this); } void solver::copy(solver const & src, bool copy_learned) { pop_to_base_level(); del_clauses(m_clauses); del_clauses(m_learned); m_watches.reset(); m_assignment.reset(); m_justification.reset(); m_decision.reset(); m_eliminated.reset(); m_activity.reset(); m_mark.reset(); m_lit_mark.reset(); m_best_phase.reset(); m_phase.reset(); m_prev_phase.reset(); m_assigned_since_gc.reset(); m_last_conflict.reset(); m_last_propagation.reset(); m_participated.reset(); m_canceled.reset(); m_reasoned.reset(); m_simplifier.reset_todos(); 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++) { bool ext = src.m_external[v]; bool dvar = src.m_decision[v]; VERIFY(v == mk_var(ext, dvar)); if (src.was_eliminated(v)) { set_eliminated(v, true); } m_phase[v] = src.m_phase[v]; m_best_phase[v] = src.m_best_phase[v]; m_prev_phase[v] = src.m_prev_phase[v]; // inherit activity: m_activity[v] = src.m_activity[v]; m_case_split_queue.activity_changed_eh(v, false); } // // register the extension before performing assignments. // the assignments may call back into the extension. // if (src.get_extension()) { m_ext = src.get_extension()->copy(this); } unsigned trail_sz = src.init_trail_size(); for (unsigned i = 0; i < trail_sz; ++i) { assign_unit(src.m_trail[i]); } // copy binary clauses { unsigned sz = src.m_watches.size(); for (unsigned l_idx = 0; l_idx < sz; ++l_idx) { literal l = ~to_literal(l_idx); if (src.was_eliminated(l.var())) continue; watch_list const & wlist = src.m_watches[l_idx]; for (auto & wi : wlist) { if (!wi.is_binary_clause()) continue; literal l2 = wi.get_literal(); if (l.index() > l2.index() || src.was_eliminated(l2.var())) continue; watched w1(l2, wi.is_learned()); watched w2(l, wi.is_learned()); m_watches[(~l).index()].push_back(w1); m_watches[(~l2).index()].push_back(w2); } } } { literal_vector buffer; // copy clauses for (clause* c : src.m_clauses) { buffer.reset(); for (literal l : *c) buffer.push_back(l); mk_clause_core(buffer); } // copy high quality lemmas unsigned num_learned = 0; for (clause* c : src.m_learned) { 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); if (c1) { ++num_learned; c1->set_glue(c->glue()); c1->set_psm(c->psm()); } } } IF_VERBOSE(2, verbose_stream() << "(sat.copy :learned " << num_learned << ")\n";); } m_best_phase_size = src.m_best_phase_size; if (m_best_phase_size > 0) { for (bool_var v = 0; v < num_vars(); ++v) { m_best_phase[v] = src.m_best_phase[v]; } } m_user_scope_literals.reset(); m_user_scope_literals.append(src.m_user_scope_literals); m_mc = src.m_mc; m_stats.m_units = init_trail_size(); } // ----------------------- // // Variable & Clause creation // // ----------------------- bool_var solver::mk_var(bool ext, bool dvar) { m_model_is_current = false; m_stats.m_mk_var++; bool_var v = m_justification.size(); m_watches.push_back(watch_list()); m_watches.push_back(watch_list()); m_assignment.push_back(l_undef); m_assignment.push_back(l_undef); m_justification.push_back(justification(UINT_MAX)); m_decision.push_back(dvar); m_eliminated.push_back(false); m_external.push_back(ext); m_touched.push_back(0); m_activity.push_back(0); m_mark.push_back(false); m_lit_mark.push_back(false); m_lit_mark.push_back(false); m_phase.push_back(false); m_best_phase.push_back(false); m_prev_phase.push_back(false); m_assigned_since_gc.push_back(false); m_last_conflict.push_back(0); m_last_propagation.push_back(0); m_participated.push_back(0); m_canceled.push_back(0); m_reasoned.push_back(0); m_case_split_queue.mk_var_eh(v); m_simplifier.insert_elim_todo(v); SASSERT(!was_eliminated(v)); return v; } void solver::set_non_external(bool_var v) { m_external[v] = false; } void solver::set_external(bool_var v) { if (m_external[v]) return; m_external[v] = true; if (!m_ext) return; lbool val = value(v); switch (val) { case l_true: { m_ext->asserted(literal(v, false)); break; } case l_false: { m_ext->asserted(literal(v, true)); break; } default: break; } } void solver::set_eliminated(bool_var v, bool f) { m_eliminated[v] = f; } 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++) { CTRACE("sat", m_eliminated[lits[i].var()], tout << lits[i] << " was eliminated\n";); SASSERT(m_eliminated[lits[i].var()] == false); } }); if (m_user_scope_literals.empty()) { 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); return mk_clause_core(m_aux_literals.size(), m_aux_literals.c_ptr(), learned); } } clause* solver::mk_clause(literal l1, literal l2, bool learned) { literal ls[2] = { l1, l2 }; return mk_clause(2, ls, learned); } clause* solver::mk_clause(literal l1, literal l2, literal l3, bool learned) { literal ls[3] = { l1, l2, l3 }; return mk_clause(3, ls, learned); } void solver::del_clause(clause& c) { if (!c.is_learned()) { m_stats.m_non_learned_generation++; } if (c.frozen()) { --m_num_frozen; } if (!c.was_removed() && m_config.m_drat && !m_drat.is_cleaned(c)) { m_drat.del(c); } 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; if (!m_searching) { m_mc.add_clause(num_lits, lits); } } switch (num_lits) { case 0: set_conflict(); return nullptr; case 1: assign_unit(lits[0]); return nullptr; case 2: mk_bin_clause(lits[0], lits[1], learned); if (learned && m_par) m_par->share_clause(*this, lits[0], lits[1]); return nullptr; case 3: if (ENABLE_TERNARY) { return mk_ter_clause(lits, learned); } default: return mk_nary_clause(num_lits, lits, learned); } } void solver::mk_bin_clause(literal l1, literal l2, bool learned) { m_touched[l1.var()] = m_touch_index; m_touched[l2.var()] = m_touch_index; if (learned && find_binary_watch(get_wlist(~l1), ~l2) && value(l1) == l_undef) { assign_unit(l1); return; } if (learned && find_binary_watch(get_wlist(~l2), ~l1) && value(l2) == l_undef) { assign_unit(l2); return; } watched* w0 = learned ? find_binary_watch(get_wlist(~l1), l2) : nullptr; if (w0) { TRACE("sat", tout << "found binary " << l1 << " " << l2 << "\n";); if (w0->is_learned() && !learned) { w0->set_learned(false); w0 = find_binary_watch(get_wlist(~l2), l1); VERIFY(w0); w0->set_learned(false); } if (propagate_bin_clause(l1, l2) && !learned && !at_base_lvl() && !at_search_lvl()) { m_clauses_to_reinit.push_back(clause_wrapper(l1, l2)); } return; } if (m_config.m_drat) m_drat.add(l1, l2, learned); if (propagate_bin_clause(l1, l2)) { if (at_base_lvl()) return; if (!learned && !at_search_lvl()) m_clauses_to_reinit.push_back(clause_wrapper(l1, l2)); } m_stats.m_mk_bin_clause++; get_wlist(~l1).push_back(watched(l2, learned)); get_wlist(~l2).push_back(watched(l1, learned)); } bool solver::propagate_bin_clause(literal l1, literal l2) { if (value(l2) == l_false) { if (value(l1) == l_false) { TRACE("sat", tout << "conflict " << l1 << " " << l2 << "\n";); set_conflict(justification(std::max(lvl(l1), lvl(l2)), l1, l2)); } else { m_stats.m_bin_propagate++; //TRACE("sat", tout << "propagate " << l1 << " <- " << ~l2 << "\n";); assign(l1, justification(lvl(l2), l2)); } return true; } else if (value(l1) == l_false) { m_stats.m_bin_propagate++; //TRACE("sat", tout << "propagate " << l2 << " <- " << ~l1 << "\n";); assign(l2, justification(lvl(l1), l1) ); return true; } return false; } void solver::push_reinit_stack(clause & c) { TRACE("sat_reinit", tout << "adding to reinit stack: " << c << "\n";); m_clauses_to_reinit.push_back(clause_wrapper(c)); c.set_reinit_stack(true); } clause * solver::mk_ter_clause(literal * lits, bool learned) { VERIFY(ENABLE_TERNARY); m_stats.m_mk_ter_clause++; clause * r = alloc_clause(3, lits, learned); bool reinit = attach_ter_clause(*r); if (reinit && !learned) push_reinit_stack(*r); if (learned) m_learned.push_back(r); else m_clauses.push_back(r); for (literal l : *r) { m_touched[l.var()] = m_touch_index; } return r; } bool solver::attach_ter_clause(clause & c) { VERIFY(ENABLE_TERNARY); bool reinit = false; if (m_config.m_drat) m_drat.add(c, c.is_learned()); TRACE("sat", tout << c << "\n";); SASSERT(!c.was_removed()); m_watches[(~c[0]).index()].push_back(watched(c[1], c[2])); m_watches[(~c[1]).index()].push_back(watched(c[0], c[2])); m_watches[(~c[2]).index()].push_back(watched(c[0], c[1])); if (!at_base_lvl()) { if (value(c[1]) == l_false && value(c[2]) == l_false) { m_stats.m_ter_propagate++; assign(c[0], justification(std::max(lvl(c[1]), lvl(c[2])), c[1], c[2])); reinit = true; } else if (value(c[0]) == l_false && value(c[2]) == l_false) { m_stats.m_ter_propagate++; assign(c[1], justification(std::max(lvl(c[0]), lvl(c[2])), c[0], c[2])); reinit = true; } else if (value(c[0]) == l_false && value(c[1]) == l_false) { m_stats.m_ter_propagate++; assign(c[2], justification(std::max(lvl(c[0]), lvl(c[1])), c[0], c[1])); reinit = true; } } return reinit; } clause * solver::mk_nary_clause(unsigned num_lits, literal * lits, bool learned) { m_stats.m_mk_clause++; clause * r = alloc_clause(num_lits, lits, learned); SASSERT(!learned || r->is_learned()); bool reinit = attach_nary_clause(*r); if (reinit && !learned) push_reinit_stack(*r); if (learned) { m_learned.push_back(r); } else { m_clauses.push_back(r); } if (m_config.m_drat) { m_drat.add(*r, learned); } for (literal l : *r) { m_touched[l.var()] = m_touch_index; } return r; } bool solver::attach_nary_clause(clause & c) { bool reinit = false; clause_offset cls_off = cls_allocator().get_offset(&c); if (!at_base_lvl()) { if (c.is_learned()) { unsigned w2_idx = select_learned_watch_lit(c); std::swap(c[1], c[w2_idx]); } else { unsigned w1_idx = select_watch_lit(c, 0); std::swap(c[0], c[w1_idx]); unsigned w2_idx = select_watch_lit(c, 1); std::swap(c[1], c[w2_idx]); } if (value(c[0]) == l_false) { m_stats.m_propagate++; unsigned level = lvl(c[0]); for (unsigned i = c.size(); i-- > 2; ) { level = std::max(level, lvl(c[i])); } assign(c[1], justification(level, cls_off)); reinit = true; } else if (value(c[1]) == l_false) { m_stats.m_propagate++; unsigned level = lvl(c[1]); for (unsigned i = c.size(); i-- > 2; ) { level = std::max(level, lvl(c[i])); } assign(c[0], justification(level, cls_off)); reinit = true; } } unsigned some_idx = c.size() >> 1; literal block_lit = c[some_idx]; VERIFY(!c.frozen()); DEBUG_CODE(for (auto const& w : m_watches[(~c[0]).index()]) SASSERT(!w.is_clause() || w.get_clause_offset() != cls_off);); DEBUG_CODE(for (auto const& w : m_watches[(~c[1]).index()]) SASSERT(!w.is_clause() || w.get_clause_offset() != cls_off);); SASSERT(c[0] != c[1]); m_watches[(~c[0]).index()].push_back(watched(block_lit, cls_off)); m_watches[(~c[1]).index()].push_back(watched(block_lit, cls_off)); return reinit; } void solver::attach_clause(clause & c, bool & reinit) { SASSERT(c.size() > 2); reinit = false; if (ENABLE_TERNARY && c.size() == 3) reinit = attach_ter_clause(c); else reinit = attach_nary_clause(c); } void solver::set_learned(clause& c, bool learned) { if (c.is_learned() != learned) c.set_learned(learned); } void solver::set_learned1(literal l1, literal l2, bool learned) { for (watched& w : get_wlist(~l1)) { if (w.is_binary_clause() && l2 == w.get_literal() && !w.is_learned()) { w.set_learned(learned); break; } } } void solver::shrink(clause& c, unsigned old_sz, unsigned new_sz) { SASSERT(new_sz > 2); SASSERT(old_sz >= new_sz); if (old_sz != new_sz) { c.shrink(new_sz); for (literal l : c) { m_touched[l.var()] = m_touch_index; } if (m_config.m_drat) { m_drat.add(c, true); c.restore(old_sz); m_drat.del(c); c.shrink(new_sz); } } } bool solver::memory_pressure() { return 3*cls_allocator().get_allocation_size()/2 + memory::get_allocation_size() > memory::get_max_memory_size(); } struct solver::cmp_activity { solver& s; cmp_activity(solver& s):s(s) {} bool operator()(bool_var v1, bool_var v2) const { return s.m_activity[v1] > s.m_activity[v2]; } }; bool solver::should_defrag() { if (m_defrag_threshold > 0) --m_defrag_threshold; return m_defrag_threshold == 0 && m_config.m_gc_defrag; } void solver::defrag_clauses() { if (memory_pressure()) return; pop(scope_lvl()); 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(); for (clause* c : m_learned) c->unmark_used(); svector vars; for (unsigned i = 0; i < num_vars(); ++i) vars.push_back(i); std::stable_sort(vars.begin(), vars.end(), cmp_activity(*this)); literal_vector lits; for (bool_var v : vars) lits.push_back(literal(v, false)), lits.push_back(literal(v, true)); // walk clauses, reallocate them in an order that defragments memory and creates locality. for (literal lit : lits) { watch_list& wlist = m_watches[lit.index()]; for (watched& w : wlist) { if (w.is_clause()) { clause& c1 = get_clause(w); clause_offset offset; if (c1.was_used()) { offset = c1.get_new_offset(); } else { clause* c2 = alloc.copy_clause(c1); c1.mark_used(); if (c1.is_learned()) { new_learned.push_back(c2); } else { new_clauses.push_back(c2); } offset = get_offset(*c2); c1.set_new_offset(offset); } w = watched(w.get_blocked_literal(), offset); } } } // reallocate ternary clauses. for (clause* c : m_clauses) { if (!c->was_used()) { SASSERT(c->size() == 3); new_clauses.push_back(alloc.copy_clause(*c)); } dealloc_clause(c); } for (clause* c : m_learned) { if (!c->was_used()) { SASSERT(c->size() == 3); new_learned.push_back(alloc.copy_clause(*c)); } dealloc_clause(c); } m_clauses.swap(new_clauses); m_learned.swap(new_learned); cls_allocator().finalize(); m_cls_allocator_idx = !m_cls_allocator_idx; reinit_assumptions(); } void solver::set_learned(literal l1, literal l2, bool learned) { set_learned1(l1, l2, learned); set_learned1(l2, l1, learned); } /** \brief Select a watch literal starting the search at the given position. This method is only used for clauses created during the search. I use the following rules to select a watch literal. 1- select a literal l in idx >= starting_at such that value(l) = l_true, and for all l' in idx' >= starting_at . value(l') = l_true implies lvl(l) <= lvl(l') The purpose of this rule is to make the clause inactive for as long as possible. A clause is inactive when it contains a literal assigned to true. 2- if there isn't a literal assigned to true, then select an unassigned literal l in idx >= starting_at 3- if there isn't a literal l in idx >= starting_at such that value(l) = l_true or value(l) = l_undef (that is, all literals at positions >= starting_at are assigned to false), then peek the literal l such that for all l' starting at starting_at lvl(l) >= lvl(l') Without rule 3, boolean propagation is incomplete, that is, it may miss possible propagations. \remark The method select_lemma_watch_lit is used to select the watch literal for regular learned clauses. */ unsigned solver::select_watch_lit(clause const & cls, unsigned starting_at) const { SASSERT(cls.size() >= 2); unsigned min_true_idx = UINT_MAX; unsigned max_false_idx = UINT_MAX; unsigned unknown_idx = UINT_MAX; unsigned n = cls.size(); for (unsigned i = starting_at; i < n; i++) { literal l = cls[i]; switch(value(l)) { case l_false: if (max_false_idx == UINT_MAX || lvl(l) > lvl(cls[max_false_idx])) max_false_idx = i; break; case l_undef: unknown_idx = i; break; case l_true: if (min_true_idx == UINT_MAX || lvl(l) < lvl(cls[min_true_idx])) min_true_idx = i; break; } } if (min_true_idx != UINT_MAX) return min_true_idx; if (unknown_idx != UINT_MAX) return unknown_idx; SASSERT(max_false_idx != UINT_MAX); return max_false_idx; } /** \brief The learned clauses (lemmas) produced by the SAT solver have the property that the first literal will be implied by it after backtracking. All other literals are assigned to (or implied to be) false when the learned clause is created. The first watch literal will always be the first literal. The second watch literal is computed by this method. It should be the literal with the highest decision level. // TODO: do we really need this? strength the conflict resolution */ unsigned solver::select_learned_watch_lit(clause const & cls) const { SASSERT(cls.size() >= 2); unsigned max_false_idx = UINT_MAX; unsigned num_lits = cls.size(); for (unsigned i = 1; i < num_lits; i++) { literal l = cls[i]; CTRACE("sat", value(l) != l_false, tout << l << ":=" << value(l);); SASSERT(value(l) == l_false); if (max_false_idx == UINT_MAX || lvl(l) > lvl(cls[max_false_idx])) max_false_idx = i; } return max_false_idx; } template bool solver::simplify_clause_core(unsigned & num_lits, literal * lits) const { std::sort(lits, lits+num_lits); literal prev = null_literal; unsigned i = 0; unsigned j = 0; for (; i < num_lits; i++) { literal curr = lits[i]; lbool val = value(curr); if (!lvl0 && lvl(curr) > 0) val = l_undef; switch (val) { case l_false: break; // ignore literal case l_undef: if (curr == ~prev) return false; // clause is equivalent to true if (curr != prev) { prev = curr; if (i != j) std::swap(lits[j], lits[i]); j++; } break; case l_true: return false; // clause is equivalent to true } } num_lits = j; return true; } bool solver::simplify_clause(unsigned & num_lits, literal * lits) const { if (at_base_lvl()) return simplify_clause_core(num_lits, lits); else return simplify_clause_core(num_lits, lits); } void solver::detach_bin_clause(literal l1, literal l2, bool learned) { get_wlist(~l1).erase(watched(l2, learned)); get_wlist(~l2).erase(watched(l1, learned)); if (m_config.m_drat) m_drat.del(l1, l2); } void solver::detach_clause(clause & c) { if (ENABLE_TERNARY && c.size() == 3) detach_ter_clause(c); else detach_nary_clause(c); } void solver::detach_nary_clause(clause & c) { clause_offset cls_off = get_offset(c); erase_clause_watch(get_wlist(~c[0]), cls_off); erase_clause_watch(get_wlist(~c[1]), cls_off); } void solver::detach_ter_clause(clause & c) { erase_ternary_watch(get_wlist(~c[0]), c[1], c[2]); erase_ternary_watch(get_wlist(~c[1]), c[0], c[2]); erase_ternary_watch(get_wlist(~c[2]), c[0], c[1]); } // ----------------------- // // Basic // // ----------------------- void solver::set_conflict(justification c, literal not_l) { if (m_inconsistent) return; m_inconsistent = true; m_conflict = c; m_not_l = not_l; } void solver::assign_core(literal l, justification j) { SASSERT(value(l) == l_undef); TRACE("sat_assign_core", tout << l << " " << j << "\n";); if (j.level() == 0) { if (m_config.m_drat) m_drat.add(l, m_searching); j = justification(0); // erase justification for level 0 } else { VERIFY(!at_base_lvl()); } m_assignment[l.index()] = l_true; m_assignment[(~l).index()] = l_false; bool_var v = l.var(); m_justification[v] = j; m_phase[v] = !l.sign(); m_assigned_since_gc[v] = true; m_trail.push_back(l); if (m_ext && m_external[v]) m_ext->asserted(l); switch (m_config.m_branching_heuristic) { case BH_VSIDS: break; case BH_CHB: m_last_propagation[v] = m_stats.m_conflict; break; case BH_LRB: m_participated[v] = 0; m_reasoned[v] = 0; break; } if (m_config.m_anti_exploration) { uint64_t age = m_stats.m_conflict - m_canceled[v]; if (age > 0) { double decay = pow(0.95, age); set_activity(v, static_cast(m_activity[v] * decay)); // NB. MapleSAT does not update canceled. m_canceled[v] = m_stats.m_conflict; } } if (m_config.m_propagate_prefetch) { #if defined(__GNUC__) || defined(__clang__) __builtin_prefetch((const char*)((m_watches[l.index()].c_ptr()))); #else #if !defined(_M_ARM) && !defined(_M_ARM64) _mm_prefetch((const char*)((m_watches[l.index()].c_ptr())), _MM_HINT_T1); #endif #endif } SASSERT(!l.sign() || !m_phase[v]); SASSERT(l.sign() || m_phase[v]); SASSERT(!l.sign() || value(v) == l_false); SASSERT(l.sign() || value(v) == l_true); SASSERT(value(l) == l_true); SASSERT(value(~l) == l_false); } lbool solver::status(clause const & c) const { bool found_undef = false; for (literal lit : c) { switch (value(lit)) { case l_true: return l_true; case l_undef: found_undef = true; break; default: break; } } return found_undef ? l_undef : l_false; } // ----------------------- // // Propagation // // ----------------------- bool solver::propagate_core(bool update) { if (m_inconsistent) return false; literal l, not_l, l1, l2; lbool val1, val2; bool keep; while (m_qhead < m_trail.size()) { checkpoint(); m_cleaner.dec(); if (m_inconsistent) return false; l = m_trail[m_qhead]; unsigned curr_level = lvl(l); TRACE("sat_propagate", tout << "propagating: " << l << " " << m_justification[l.var()] << "\n"; ); m_qhead++; not_l = ~l; SASSERT(value(l) == l_true); SASSERT(value(not_l) == l_false); watch_list & wlist = m_watches[l.index()]; m_asymm_branch.dec(wlist.size()); m_probing.dec(wlist.size()); watch_list::iterator it = wlist.begin(); watch_list::iterator it2 = it; watch_list::iterator end = wlist.end(); #define CONFLICT_CLEANUP() { \ for (; it != end; ++it, ++it2) \ *it2 = *it; \ wlist.set_end(it2); \ } for (; it != end; ++it) { switch (it->get_kind()) { case watched::BINARY: l1 = it->get_literal(); switch (value(l1)) { case l_false: CONFLICT_CLEANUP(); set_conflict(justification(curr_level, not_l), ~l1); return false; case l_undef: m_stats.m_bin_propagate++; assign_core(l1, justification(curr_level, not_l)); break; case l_true: break; // skip } *it2 = *it; it2++; break; case watched::TERNARY: l1 = it->get_literal1(); l2 = it->get_literal2(); val1 = value(l1); val2 = value(l2); if (val1 == l_false && val2 == l_undef) { m_stats.m_ter_propagate++; assign_core(l2, justification(std::max(curr_level, lvl(l1)), l1, not_l)); } else if (val1 == l_undef && val2 == l_false) { m_stats.m_ter_propagate++; assign_core(l1, justification(std::max(curr_level, lvl(l2)), l2, not_l)); } else if (val1 == l_false && val2 == l_false) { CONFLICT_CLEANUP(); set_conflict(justification(std::max(curr_level, lvl(l1)), l1, not_l), ~l2); return false; } *it2 = *it; it2++; break; case watched::CLAUSE: { if (value(it->get_blocked_literal()) == l_true) { TRACE("propagate_clause_bug", tout << "blocked literal " << it->get_blocked_literal() << "\n"; tout << get_clause(it) << "\n";); *it2 = *it; it2++; break; } clause_offset cls_off = it->get_clause_offset(); clause & c = get_clause(cls_off); TRACE("propagate_clause_bug", tout << "processing... " << c << "\nwas_removed: " << c.was_removed() << "\n";); if (c[0] == not_l) std::swap(c[0], c[1]); CTRACE("propagate_bug", c[1] != not_l, tout << "l: " << l << " " << c << "\n";); if (c.was_removed() || c.size() == 1 || c[1] != not_l) { // Remark: this method may be invoked when the watch lists are not in a consistent state, // and may contain dead/removed clauses, or clauses with removed literals. // See: method propagate_unit at sat_simplifier.cpp // So, we must check whether the clause was marked for deletion, or // c[1] != not_l *it2 = *it; it2++; break; } if (value(c[0]) == l_true) { it2->set_clause(c[0], cls_off); it2++; break; } VERIFY(c[1] == not_l); literal * l_it = c.begin() + 2; literal * l_end = c.end(); unsigned assign_level = curr_level; unsigned max_index = 1; for (; l_it != l_end; ++l_it) { if (value(*l_it) != l_false) { c[1] = *l_it; *l_it = not_l; DEBUG_CODE(for (auto const& w : m_watches[(~c[1]).index()]) VERIFY(!w.is_clause() || w.get_clause_offset() != cls_off);); m_watches[(~c[1]).index()].push_back(watched(c[0], cls_off)); goto end_clause_case; } } SASSERT(value(c[0]) == l_false || value(c[0]) == l_undef); if (assign_level != scope_lvl()) { for (unsigned i = 2; i < c.size(); ++i) { unsigned level = lvl(c[i]); if (level > assign_level) { assign_level = level; max_index = i; } } IF_VERBOSE(20, verbose_stream() << "lower assignment level " << assign_level << " scope: " << scope_lvl() << "\n"); } if (value(c[0]) == l_false) { assign_level = std::max(assign_level, lvl(c[0])); c.mark_used(); CONFLICT_CLEANUP(); set_conflict(justification(assign_level, cls_off)); return false; } else { if (max_index != 1) { IF_VERBOSE(20, verbose_stream() << "swap watch for: " << c[1] << " " << c[max_index] << "\n"); std::swap(c[1], c[max_index]); m_watches[(~c[1]).index()].push_back(watched(c[0], cls_off)); } else { *it2 = *it; it2++; } m_stats.m_propagate++; c.mark_used(); assign_core(c[0], justification(assign_level, cls_off)); if (update && c.is_learned() && c.glue() > 2) { unsigned glue; if (num_diff_levels_below(c.size(), c.begin(), c.glue()-1, glue)) { c.set_glue(glue); } } } end_clause_case: break; } case watched::EXT_CONSTRAINT: SASSERT(m_ext); keep = m_ext->propagate(l, it->get_ext_constraint_idx()); if (m_inconsistent) { if (!keep) { ++it; } CONFLICT_CLEANUP(); return false; } if (keep) { *it2 = *it; it2++; } break; default: UNREACHABLE(); break; } } wlist.set_end(it2); } SASSERT(m_qhead == m_trail.size()); SASSERT(!m_inconsistent); return true; } bool solver::propagate(bool update) { unsigned qhead = m_qhead; bool r = propagate_core(update); if (m_config.m_branching_heuristic == BH_CHB) { update_chb_activity(r, qhead); } CASSERT("sat_propagate", check_invariant()); CASSERT("sat_missed_prop", check_missed_propagation()); return r; } void solver::display_lookahead_scores(std::ostream& out) { lookahead lh(*this); lh.display_lookahead_scores(out); } lbool solver::cube(bool_var_vector& vars, literal_vector& lits, unsigned backtrack_level) { bool is_first = !m_cuber; if (is_first) { m_cuber = alloc(lookahead, *this); } lbool result = m_cuber->cube(vars, lits, backtrack_level); m_cuber->update_cube_statistics(m_aux_stats); switch (result) { case l_false: dealloc(m_cuber); m_cuber = nullptr; if (is_first) { pop_to_base_level(); set_conflict(); } break; case l_true: { lits.reset(); pop_to_base_level(); model const& mdl = m_cuber->get_model(); for (bool_var v = 0; v < mdl.size(); ++v) { if (value(v) != l_undef) { continue; } literal l(v, false); if (mdl[v] != l_true) l.neg(); push(); assign_core(l, justification(scope_lvl())); } mk_model(); break; } default: break; } return result; } // ----------------------- // // Search // // ----------------------- lbool solver::check(unsigned num_lits, literal const* lits) { init_reason_unknown(); pop_to_base_level(); m_stats.m_units = init_trail_size(); IF_VERBOSE(2, verbose_stream() << "(sat.solver)\n";); SASSERT(at_base_lvl()); if (m_config.m_ddfw_search) { m_cleaner(true); return do_ddfw_search(num_lits, lits); } if (m_config.m_prob_search) { m_cleaner(true); return do_prob_search(num_lits, lits); } if (m_config.m_local_search) { m_cleaner(true); return do_local_search(num_lits, lits); } if ((m_config.m_num_threads > 1 || m_config.m_local_search_threads > 0 || m_config.m_ddfw_threads > 0 || m_config.m_unit_walk_threads > 0) && !m_par) { SASSERT(scope_lvl() == 0); return check_par(num_lits, lits); } flet _searching(m_searching, true); if (m_mc.empty() && gparams::get_ref().get_bool("model_validate", false)) { m_clone = alloc(solver, m_params, m_rlimit); m_clone->copy(*this); m_clone->set_extension(nullptr); } try { init_search(); if (inconsistent()) return l_false; propagate(false); if (inconsistent()) return l_false; init_assumptions(num_lits, lits); propagate(false); if (check_inconsistent()) return l_false; do_cleanup(m_config.m_force_cleanup); if (m_config.m_unit_walk) { return do_unit_walk(); } if (m_config.m_gc_burst) { // force gc m_conflicts_since_gc = m_gc_threshold + 1; do_gc(); } if (m_config.m_max_conflicts > 0 && m_config.m_burst_search > 0) { m_restart_threshold = m_config.m_burst_search; lbool r = bounded_search(); if (r != l_undef) return r; pop_reinit(scope_lvl()); m_conflicts_since_restart = 0; m_restart_threshold = m_config.m_restart_initial; } lbool is_sat = l_undef; while (is_sat == l_undef && !should_cancel()) { if (inconsistent()) is_sat = resolve_conflict_core(); else if (should_propagate()) propagate(true); else if (do_cleanup(false)) continue; else if (should_gc()) do_gc(); else if (should_rephase()) do_rephase(); else if (should_reorder()) do_reorder(); else if (should_restart()) do_restart(!m_config.m_restart_fast); else if (should_simplify()) do_simplify(); else if (!decide()) is_sat = final_check(); } return is_sat; } catch (const abort_solver &) { m_reason_unknown = "sat.giveup"; IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "(sat \"abort giveup\")\n";); return l_undef; } } bool solver::should_cancel() { if (limit_reached() || memory_exceeded()) { return true; } if (m_config.m_restart_max <= m_restarts) { m_reason_unknown = "sat.max.restarts"; IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "(sat \"abort: max-restarts\")\n";); return true; } if (m_config.m_inprocess_max <= m_simplifications) { m_reason_unknown = "sat.max.inprocess"; IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "(sat \"abort: max-inprocess\")\n";); return true; } if (reached_max_conflicts()) { return true; } return false; } enum par_exception_kind { DEFAULT_EX, ERROR_EX }; lbool solver::invoke_local_search(unsigned num_lits, literal const* lits) { scoped_limits scoped_rl(rlimit()); SASSERT(m_local_search); i_local_search& srch = *m_local_search; srch.add(*this); srch.updt_params(m_params); scoped_rl.push_child(&srch.rlimit()); lbool r = srch.check(num_lits, lits, nullptr); if (r == l_true) { m_model = srch.get_model(); } m_local_search = nullptr; dealloc(&srch); return r; } lbool solver::do_local_search(unsigned num_lits, literal const* lits) { SASSERT(!m_local_search); m_local_search = alloc(local_search); return invoke_local_search(num_lits, lits); } lbool solver::do_ddfw_search(unsigned num_lits, literal const* lits) { if (m_ext) return l_undef; SASSERT(!m_local_search); m_local_search = alloc(ddfw); return invoke_local_search(num_lits, lits); } lbool solver::do_prob_search(unsigned num_lits, literal const* lits) { if (m_ext) return l_undef; SASSERT(!m_local_search); m_local_search = alloc(prob); return invoke_local_search(num_lits, lits); } lbool solver::do_unit_walk() { unit_walk srch(*this); lbool r = srch(); return r; } lbool solver::check_par(unsigned num_lits, literal const* lits) { scoped_ptr_vector ls; scoped_ptr_vector uw; int num_extra_solvers = m_config.m_num_threads - 1; int num_local_search = static_cast(m_config.m_local_search_threads); int num_unit_walk = static_cast(m_config.m_unit_walk_threads); int num_ddfw = m_ext ? 0 : static_cast(m_config.m_ddfw_threads); int num_threads = num_extra_solvers + 1 + num_local_search + num_unit_walk + num_ddfw; for (int i = 0; i < num_local_search; ++i) { local_search* l = alloc(local_search); l->updt_params(m_params); l->add(*this); l->set_seed(m_config.m_random_seed + i); ls.push_back(l); } // set up ddfw search for (int i = 0; i < num_ddfw; ++i) { ddfw* d = alloc(ddfw); d->updt_params(m_params); d->set_seed(m_config.m_random_seed + i); d->add(*this); ls.push_back(d); } // set up unit walk vector lims(num_unit_walk + num_ddfw); for (int i = 0; i < num_unit_walk; ++i) { solver* s = alloc(solver, m_params, lims[i]); s->copy(*this); s->m_config.m_unit_walk = true; uw.push_back(s); } int local_search_offset = num_extra_solvers; int unit_walk_offset = num_extra_solvers + num_local_search + num_ddfw; int main_solver_offset = unit_walk_offset + num_unit_walk; #define IS_AUX_SOLVER(i) (0 <= i && i < num_extra_solvers) #define IS_LOCAL_SEARCH(i) (local_search_offset <= i && i < unit_walk_offset) #define IS_UNIT_WALK(i) (unit_walk_offset <= i && i < main_solver_offset) #define IS_MAIN_SOLVER(i) (i == main_solver_offset) sat::parallel par(*this); par.reserve(num_threads, 1 << 12); par.init_solvers(*this, num_extra_solvers); for (unsigned i = 0; i < ls.size(); ++i) { par.push_child(ls[i]->rlimit()); } for (reslimit& rl : lims) { par.push_child(rl); } for (unsigned i = 0; i < uw.size(); ++i) { uw[i]->set_par(&par, 0); } int finished_id = -1; std::string ex_msg; par_exception_kind ex_kind = DEFAULT_EX; unsigned error_code = 0; lbool result = l_undef; bool canceled = false; std::mutex mux; auto worker_thread = [&](int i) { try { lbool r = l_undef; if (IS_AUX_SOLVER(i)) { r = par.get_solver(i).check(num_lits, lits); } else if (IS_LOCAL_SEARCH(i)) { r = ls[i-local_search_offset]->check(num_lits, lits, &par); } else if (IS_UNIT_WALK(i)) { r = uw[i-unit_walk_offset]->check(num_lits, lits); } else { r = check(num_lits, lits); } bool first = false; { std::lock_guard lock(mux); if (finished_id == -1) { finished_id = i; first = true; result = r; } } if (first) { for (unsigned j = 0; j < ls.size(); ++j) { ls[j]->rlimit().cancel(); } for (auto& rl : lims) { rl.cancel(); } for (int j = 0; j < num_extra_solvers; ++j) { if (i != j) { par.cancel_solver(j); } } if (!IS_MAIN_SOLVER(i)) { canceled = !rlimit().inc(); if (!canceled) { rlimit().cancel(); } } } } catch (z3_error & err) { error_code = err.error_code(); ex_kind = ERROR_EX; } catch (z3_exception & ex) { ex_msg = ex.msg(); ex_kind = DEFAULT_EX; } }; vector threads(num_threads); for (int i = 0; i < num_threads; ++i) { threads[i] = std::thread([&, i]() { worker_thread(i); }); } for (auto & th : threads) { th.join(); } if (IS_AUX_SOLVER(finished_id)) { m_stats = par.get_solver(finished_id).m_stats; } if (result == l_true && IS_AUX_SOLVER(finished_id)) { set_model(par.get_solver(finished_id).get_model()); } else if (result == l_false && IS_AUX_SOLVER(finished_id)) { m_core.reset(); m_core.append(par.get_solver(finished_id).get_core()); } if (result == l_true && IS_LOCAL_SEARCH(finished_id)) { set_model(ls[finished_id - local_search_offset]->get_model()); } if (result == l_true && IS_UNIT_WALK(finished_id)) { set_model(uw[finished_id - unit_walk_offset]->get_model()); } if (!canceled) { rlimit().reset_cancel(); } set_par(nullptr, 0); ls.reset(); uw.reset(); if (finished_id == -1) { switch (ex_kind) { case ERROR_EX: throw z3_error(error_code); default: throw default_exception(std::move(ex_msg)); } } return result; } /* \brief import lemmas/units from parallel sat solvers. */ void solver::exchange_par() { if (m_par && at_base_lvl() && m_config.m_num_threads > 1) m_par->get_clauses(*this); if (m_par && at_base_lvl() && m_config.m_num_threads > 1) { // SASSERT(scope_lvl() == search_lvl()); // TBD: import also dependencies of assumptions. unsigned sz = init_trail_size(); unsigned num_in = 0, num_out = 0; literal_vector in, out; for (unsigned i = m_par_limit_out; i < sz; ++i) { literal lit = m_trail[i]; if (lit.var() < m_par_num_vars) { ++num_out; out.push_back(lit); } } m_par_limit_out = sz; m_par->exchange(*this, out, m_par_limit_in, in); for (unsigned i = 0; !inconsistent() && i < in.size(); ++i) { literal lit = in[i]; SASSERT(lit.var() < m_par_num_vars); if (lvl(lit.var()) != 0 || value(lit) != l_true) { ++num_in; assign_unit(lit); } } if (num_in > 0 || num_out > 0) { IF_VERBOSE(2, verbose_stream() << "(sat-sync out: " << num_out << " in: " << num_in << ")\n";); } } } void solver::set_par(parallel* p, unsigned id) { m_par = p; m_par_num_vars = num_vars(); m_par_limit_in = 0; m_par_limit_out = 0; m_par_id = id; m_par_syncing_clauses = false; } bool_var solver::next_var() { bool_var next; if (m_rand() < static_cast(m_config.m_random_freq * random_gen::max_value())) { if (num_vars() == 0) return null_bool_var; next = m_rand() % num_vars(); TRACE("random_split", tout << "next: " << next << " value(next): " << value(next) << "\n";); if (value(next) == l_undef && !was_eliminated(next)) return next; } while (!m_case_split_queue.empty()) { if (m_config.m_anti_exploration) { next = m_case_split_queue.min_var(); auto age = m_stats.m_conflict - m_canceled[next]; while (age > 0) { set_activity(next, static_cast(m_activity[next] * pow(0.95, age))); m_canceled[next] = m_stats.m_conflict; next = m_case_split_queue.min_var(); age = m_stats.m_conflict - m_canceled[next]; } } next = m_case_split_queue.next_var(); if (value(next) == l_undef && !was_eliminated(next)) return next; } return null_bool_var; } bool solver::decide() { bool_var next = next_var(); if (next == null_bool_var) return false; push(); m_stats.m_decision++; lbool lphase = m_ext ? m_ext->get_phase(next) : l_undef; bool phase = lphase == l_true; if (lphase == l_undef) { switch (m_config.m_phase) { case PS_ALWAYS_TRUE: phase = true; break; case PS_ALWAYS_FALSE: phase = false; break; case PS_BASIC_CACHING: phase = m_phase[next]; break; case PS_SAT_CACHING: if (m_search_state == s_unsat) { phase = m_phase[next]; } else { phase = m_best_phase[next]; } break; case PS_RANDOM: phase = (m_rand() % 2) == 0; break; default: UNREACHABLE(); phase = false; break; } } literal next_lit(next, !phase); TRACE("sat_decide", tout << scope_lvl() << ": next-case-split: " << next_lit << "\n";); assign_scoped(next_lit); return true; } lbool solver::bounded_search() { while (true) { checkpoint(); bool done = false; while (!done) { lbool is_sat = propagate_and_backjump_step(done); if (is_sat != l_true) return is_sat; } SASSERT(!inconsistent()); do_gc(); if (!decide()) { lbool is_sat = final_check(); if (is_sat != l_undef) { return is_sat; } } } } bool solver::should_propagate() const { return !inconsistent() && m_qhead < m_trail.size(); } lbool solver::propagate_and_backjump_step(bool& done) { done = true; propagate(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_rephase()) do_rephase(); if (at_base_lvl()) { do_cleanup(false); // cleaner may propagate frozen clauses if (inconsistent()) { TRACE("sat", tout << "conflict at level 0\n";); return l_false; } do_gc(); } done = false; return l_true; } lbool solver::final_check() { if (m_ext) { switch (m_ext->check()) { case CR_DONE: mk_model(); return l_true; case CR_CONTINUE: break; case CR_GIVEUP: throw abort_solver(); } return l_undef; } else { mk_model(); return l_true; } } bool solver::check_inconsistent() { if (inconsistent()) { if (tracking_assumptions()) resolve_conflict(); return true; } else { return false; } } struct clause_size_lt { bool operator()(clause const * c1, clause const * c2) const { return c1->size() < c2->size(); } }; void solver::init_assumptions(unsigned num_lits, literal const* lits) { if (num_lits == 0 && m_user_scope_literals.empty()) { return; } SASSERT(at_base_lvl()); reset_assumptions(); push(); propagate(false); if (inconsistent()) { return; } TRACE("sat", tout << literal_vector(num_lits, lits) << "\n"; if (!m_user_scope_literals.empty()) { tout << "user literals: " << m_user_scope_literals << "\n"; } m_mc.display(tout); ); for (unsigned i = 0; !inconsistent() && i < m_user_scope_literals.size(); ++i) { literal nlit = ~m_user_scope_literals[i]; 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_scoped(lit); } m_search_lvl = scope_lvl(); SASSERT(m_search_lvl == 1); } void solver::update_min_core() { if (!m_min_core_valid || m_core.size() < m_min_core.size()) { m_min_core.reset(); m_min_core.append(m_core); m_min_core_valid = true; } } void solver::reset_assumptions() { m_assumptions.reset(); m_assumption_set.reset(); } void solver::add_assumption(literal lit) { m_assumption_set.insert(lit); m_assumptions.push_back(lit); set_external(lit.var()); } void solver::pop_assumption() { VERIFY(m_assumptions.back() == m_assumption_set.pop()); m_assumptions.pop_back(); } void solver::reassert_min_core() { SASSERT(m_min_core_valid); pop_to_base_level(); push(); reset_assumptions(); TRACE("sat", tout << "reassert: " << m_min_core << "\n";); for (literal lit : m_min_core) { SASSERT(is_external(lit.var())); add_assumption(lit); assign_scoped(lit); } propagate(false); SASSERT(inconsistent()); } void solver::reinit_assumptions() { if (tracking_assumptions() && at_base_lvl() && !inconsistent()) { TRACE("sat", tout << "assumptions: " << m_assumptions << " user scopes: " << m_user_scope_literals << "\n";); if (!propagate(false)) return; push(); for (literal lit : m_user_scope_literals) { if (inconsistent()) break; assign_scoped(~lit); } for (literal lit : m_assumptions) { if (inconsistent()) break; assign_scoped(lit); } if (!inconsistent()) propagate(false); TRACE("sat", tout << "consistent: " << !inconsistent() << "\n"; for (literal a : m_assumptions) { index_set s; if (m_antecedents.find(a.var(), s)) { tout << a << ": "; display_index_set(tout, s) << "\n"; } } for (literal lit : m_user_scope_literals) { tout << "user " << lit << "\n"; } ); } } bool solver::tracking_assumptions() const { return !m_assumptions.empty() || !m_user_scope_literals.empty(); } bool solver::is_assumption(literal l) const { return tracking_assumptions() && m_assumption_set.contains(l); } void solver::set_activity(bool_var v, unsigned new_act) { unsigned old_act = m_activity[v]; m_activity[v] = new_act; if (!was_eliminated(v) && value(v) == l_undef && new_act != old_act) { m_case_split_queue.activity_changed_eh(v, new_act > old_act); } } bool solver::is_assumption(bool_var v) const { return is_assumption(literal(v, false)) || is_assumption(literal(v, true)); } void solver::init_search() { m_model_is_current = false; m_phase_counter = 0; m_search_state = s_unsat; m_search_unsat_conflicts = m_config.m_search_unsat_conflicts; m_search_sat_conflicts = m_config.m_search_sat_conflicts; m_search_next_toggle = m_search_unsat_conflicts; m_best_phase_size = 0; m_rephase_lim = 0; m_rephase_inc = 0; m_reorder_lim = m_config.m_reorder_base; m_reorder_inc = 0; m_conflicts_since_restart = 0; m_force_conflict_analysis = false; m_restart_threshold = m_config.m_restart_initial; m_luby_idx = 1; 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; m_min_d_tk = 1.0; m_search_lvl = 0; m_conflicts_since_gc = 0; m_restart_next_out = 0; m_asymm_branch.init_search(); m_stopwatch.reset(); m_stopwatch.start(); m_core.reset(); m_min_core_valid = false; m_min_core.reset(); m_simplifier.init_search(); m_mc.init_search(*this); TRACE("sat", display(tout);); } bool solver::should_simplify() const { return m_conflicts_since_init >= m_next_simplify; } /** \brief Apply all simplifications. */ void solver::do_simplify() { if (!should_simplify()) { return; } log_stats(); m_simplifications++; IF_VERBOSE(2, verbose_stream() << "(sat.simplify :simplifications " << m_simplifications << ")\n";); TRACE("sat", tout << "simplify\n";); pop(scope_lvl()); SASSERT(at_base_lvl()); m_cleaner(m_config.m_force_cleanup); CASSERT("sat_simplify_bug", check_invariant()); m_scc(); CASSERT("sat_simplify_bug", check_invariant()); if (m_ext) { m_ext->pre_simplify(); } m_simplifier(false); CASSERT("sat_simplify_bug", check_invariant()); CASSERT("sat_missed_prop", check_missed_propagation()); if (!m_learned.empty()) { m_simplifier(true); CASSERT("sat_missed_prop", check_missed_propagation()); CASSERT("sat_simplify_bug", check_invariant()); } sort_watch_lits(); CASSERT("sat_simplify_bug", check_invariant()); m_probing(); CASSERT("sat_missed_prop", check_missed_propagation()); CASSERT("sat_simplify_bug", check_invariant()); m_asymm_branch(false); CASSERT("sat_missed_prop", check_missed_propagation()); CASSERT("sat_simplify_bug", check_invariant()); if (m_ext) { m_ext->clauses_modifed(); m_ext->simplify(); } if (m_config.m_lookahead_simplify && !m_ext) { lookahead lh(*this); lh.simplify(true); lh.collect_statistics(m_aux_stats); } reinit_assumptions(); if (inconsistent()) return; if (m_next_simplify == 0) { m_next_simplify = m_config.m_next_simplify1; } else { m_next_simplify = static_cast(m_conflicts_since_init * m_config.m_simplify_mult2); if (m_next_simplify > m_conflicts_since_init + m_config.m_simplify_max) m_next_simplify = m_conflicts_since_init + m_config.m_simplify_max; } if (m_par) { m_par->from_solver(*this); if (m_par->to_solver(*this)) { m_activity_inc = 128; } } #if 0 static unsigned file_no = 0; #pragma omp critical (print_sat) { ++file_no; std::ostringstream ostrm; ostrm << "s" << file_no << ".txt"; std::ofstream ous(ostrm.str()); display(ous); } #endif } bool solver::set_root(literal l, literal r) { return !m_ext || m_ext->set_root(l, r); } void solver::flush_roots() { if (m_ext) m_ext->flush_roots(); } void solver::sort_watch_lits() { for (watch_list & wlist : m_watches) { std::stable_sort(wlist.begin(), wlist.end(), watched_lt()); } } void solver::set_model(model const& mdl) { m_model.reset(); m_model.append(mdl); m_model_is_current = !m_model.empty(); } void solver::mk_model() { m_model.reset(); m_model_is_current = true; unsigned num = num_vars(); m_model.resize(num, l_undef); for (bool_var v = 0; v < num; v++) { if (!was_eliminated(v)) { m_model[v] = value(v); m_phase[v] = value(v) == l_true; } } TRACE("sat_mc_bug", m_mc.display(tout);); #if 0 IF_VERBOSE(2, for (bool_var v = 0; v < num; v++) verbose_stream() << v << ": " << m_model[v] << "\n";); for (auto p : big::s_del_bin) { if (value(p.first) != l_true && value(p.second) != l_true) { IF_VERBOSE(2, verbose_stream() << "binary violation: " << p.first << " " << p.second << "\n"); } } #endif if (m_clone) { 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(1, 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(1, verbose_stream() << "\"checking model (on original set of clauses)\"\n";); if (!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 { bool ok = true; for (clause const* cp : m_clauses) { clause const & c = *cp; if (!c.satisfied_by(m)) { 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"; tout << "model: " << m << "\n"; m_mc.display(tout); ); for (literal l : c) { if (was_eliminated(l.var())) IF_VERBOSE(1, verbose_stream() << "eliminated: " << l << "\n";); } ok = false; } } unsigned l_idx = 0; for (watch_list const& wlist : m_watches) { literal l = ~to_literal(l_idx); if (value_at(l, m) != l_true) { for (watched const& w : wlist) { if (!w.is_binary_non_learned_clause()) continue; literal l2 = w.get_literal(); if (l.index() > l2.index()) continue; if (value_at(l2, m) != l_true) { 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; } } } ++l_idx; } for (literal l : m_assumptions) { if (value_at(l, m) != l_true) { VERIFY(is_external(l.var())); 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"; tout << "model: " << m << "\n"; m_mc.display(tout); ); ok = false; } } if (m_ext && !m_ext->check_model(m)) { ok = false; } return ok; } bool solver::check_model(model const & m) const { bool ok = check_clauses(m); if (ok && !m_mc.check_model(m)) { ok = false; TRACE("sat", tout << "model: " << m << "\n"; m_mc.display(tout);); IF_VERBOSE(0, verbose_stream() << "model check failed\n"); } return ok; } bool solver::should_restart() const { if (m_conflicts_since_restart <= m_restart_threshold) return false; if (scope_lvl() < 2 + search_lvl()) return false; if (m_config.m_restart != RS_EMA) return true; return m_fast_glue_avg + search_lvl() <= scope_lvl() && 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::do_restart(bool to_base) { m_stats.m_restart++; m_restarts++; 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(); } TRACE("sat", tout << "restart " << restart_level(to_base) << "\n";); 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(); } unsigned solver::restart_level(bool to_base) { if (to_base || scope_lvl() == search_lvl()) { return scope_lvl() - search_lvl(); } else { bool_var next = m_case_split_queue.min_var(); // Implementations of Marijn's idea of reusing the // trail when the next decision literal has lower precedence. // pop trail from top #if 0 unsigned n = 0; do { bool_var prev = scope_literal(scope_lvl() - n - 1).var(); if (m_case_split_queue.more_active(prev, next)) break; ++n; } while (n < scope_lvl() - search_lvl()); return n; #endif // pop trail from bottom unsigned n = search_lvl(); for (; n < scope_lvl() && m_case_split_queue.more_active(scope_literal(n).var(), next); ++n) { } return n - search_lvl(); } } void solver::update_activity(bool_var v, double p) { unsigned new_act = (unsigned) (num_vars() * m_config.m_activity_scale * p); set_activity(v, new_act); } void solver::set_next_restart() { m_conflicts_since_restart = 0; switch (m_config.m_restart) { case RS_GEOMETRIC: m_restart_threshold = static_cast(m_restart_threshold * m_config.m_restart_factor); break; case RS_LUBY: m_luby_idx++; m_restart_threshold = m_config.m_restart_initial * get_luby(m_luby_idx); break; case RS_EMA: m_restart_threshold = m_config.m_restart_initial; break; case RS_STATIC: break; default: UNREACHABLE(); break; } CASSERT("sat_restart", check_invariant()); } // ----------------------- // // GC // // ----------------------- bool solver::should_gc() const { return m_conflicts_since_gc > m_gc_threshold && (m_config.m_gc_strategy != GC_DYN_PSM || at_base_lvl()); } void solver::do_gc() { if (!should_gc()) return; TRACE("sat", tout << m_conflicts_since_gc << " " << m_gc_threshold << "\n";); unsigned gc = m_stats.m_gc_clause; m_conflicts_since_gc = 0; m_gc_threshold += m_config.m_gc_increment; IF_VERBOSE(10, verbose_stream() << "(sat.gc)\n";); CASSERT("sat_gc_bug", check_invariant()); switch (m_config.m_gc_strategy) { case GC_GLUE: gc_glue(); break; case GC_PSM: gc_psm(); break; case GC_GLUE_PSM: gc_glue_psm(); break; case GC_PSM_GLUE: gc_psm_glue(); break; case GC_DYN_PSM: if (!m_assumptions.empty()) { gc_glue_psm(); break; } if (!at_base_lvl()) return; gc_dyn_psm(); break; default: UNREACHABLE(); break; } if (m_ext) m_ext->gc(); if (gc > 0 && should_defrag()) { defrag_clauses(); } CASSERT("sat_gc_bug", check_invariant()); } /** \brief Lex on (glue, size) */ struct glue_lt { bool operator()(clause const * c1, clause const * c2) const { if (c1->glue() < c2->glue()) return true; return c1->glue() == c2->glue() && c1->size() < c2->size(); } }; /** \brief Lex on (psm, size) */ struct psm_lt { bool operator()(clause const * c1, clause const * c2) const { if (c1->psm() < c2->psm()) return true; return c1->psm() == c2->psm() && c1->size() < c2->size(); } }; /** \brief Lex on (glue, psm, size) */ struct glue_psm_lt { bool operator()(clause const * c1, clause const * c2) const { if (c1->glue() < c2->glue()) return true; if (c1->glue() > c2->glue()) return false; if (c1->psm() < c2->psm()) return true; if (c1->psm() > c2->psm()) return false; return c1->size() < c2->size(); } }; /** \brief Lex on (psm, glue, size) */ struct psm_glue_lt { bool operator()(clause const * c1, clause const * c2) const { if (c1->psm() < c2->psm()) return true; if (c1->psm() > c2->psm()) return false; if (c1->glue() < c2->glue()) return true; if (c1->glue() > c2->glue()) return false; return c1->size() < c2->size(); } }; void solver::gc_glue() { std::stable_sort(m_learned.begin(), m_learned.end(), glue_lt()); gc_half("glue"); } void solver::gc_psm() { save_psm(); std::stable_sort(m_learned.begin(), m_learned.end(), psm_lt()); gc_half("psm"); } void solver::gc_glue_psm() { save_psm(); std::stable_sort(m_learned.begin(), m_learned.end(), glue_psm_lt()); gc_half("glue-psm"); } void solver::gc_psm_glue() { save_psm(); std::stable_sort(m_learned.begin(), m_learned.end(), psm_glue_lt()); gc_half("psm-glue"); } /** \brief Compute the psm of all learned clauses. */ void solver::save_psm() { for (clause* cp : m_learned) { cp->set_psm(psm(*cp)); } } /** \brief GC (the second) half of the clauses in the database. */ void solver::gc_half(char const * st_name) { TRACE("sat", tout << "gc\n";); unsigned sz = m_learned.size(); unsigned new_sz = sz/2; // std::min(sz/2, m_clauses.size()*2); unsigned j = new_sz; for (unsigned i = new_sz; i < sz; i++) { clause & c = *(m_learned[i]); if (can_delete(c)) { detach_clause(c); del_clause(c); } else { m_learned[j] = &c; j++; } } new_sz = j; m_stats.m_gc_clause += sz - new_sz; m_learned.shrink(new_sz); IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "(sat-gc :strategy " << st_name << " :deleted " << (sz - new_sz) << ")\n";); } bool solver::can_delete3(literal l1, literal l2, literal l3) const { if (value(l1) == l_true && value(l2) == l_false && value(l3) == l_false) { justification const& j = m_justification[l1.var()]; if (j.is_ternary_clause()) { watched w1(l2, l3); watched w2(j.get_literal1(), j.get_literal2()); return w1 != w2; } } return true; } bool solver::can_delete(clause const & c) const { if (c.on_reinit_stack()) return false; if (ENABLE_TERNARY && c.size() == 3) { return can_delete3(c[0],c[1],c[2]) && can_delete3(c[1],c[0],c[2]) && can_delete3(c[2],c[0],c[1]); } literal l0 = c[0]; if (value(l0) != l_true) return true; justification const & jst = m_justification[l0.var()]; return !jst.is_clause() || cls_allocator().get_clause(jst.get_clause_offset()) != &c; } /** \brief Use gc based on dynamic psm. Clauses are initially frozen. */ void solver::gc_dyn_psm() { TRACE("sat", tout << "gc\n";); // To do gc at scope_lvl() > 0, I will need to use the reinitialization stack, or live with the fact // that I may miss some propagations for reactivated clauses. SASSERT(at_base_lvl()); // compute // d_tk unsigned h = 0; unsigned V_tk = 0; for (bool_var v = 0; v < num_vars(); v++) { if (m_assigned_since_gc[v]) { V_tk++; m_assigned_since_gc[v] = false; } if (m_phase[v] != m_prev_phase[v]) { h++; m_prev_phase[v] = m_phase[v]; } } double d_tk = V_tk == 0 ? static_cast(num_vars() + 1) : static_cast(h)/static_cast(V_tk); if (d_tk < m_min_d_tk) m_min_d_tk = d_tk; TRACE("sat_frozen", tout << "m_min_d_tk: " << m_min_d_tk << "\n";); unsigned frozen = 0; unsigned deleted = 0; unsigned activated = 0; clause_vector::iterator it = m_learned.begin(); clause_vector::iterator it2 = it; clause_vector::iterator end = m_learned.end(); for (; it != end; ++it) { clause & c = *(*it); if (!c.frozen()) { // Active clause if (c.glue() > m_config.m_gc_small_lbd) { // I never delete clauses with small lbd if (c.was_used()) { c.reset_inact_rounds(); } else { c.inc_inact_rounds(); if (c.inact_rounds() > m_config.m_gc_k) { detach_clause(c); del_clause(c); m_stats.m_gc_clause++; deleted++; continue; } } c.unmark_used(); if (psm(c) > static_cast(c.size() * m_min_d_tk)) { // move to frozen; TRACE("sat_frozen", tout << "freezing size: " << c.size() << " psm: " << psm(c) << " " << c << "\n";); detach_clause(c); c.reset_inact_rounds(); c.freeze(); m_num_frozen++; frozen++; } } } else { // frozen clause clause & c = *(*it); if (psm(c) <= static_cast(c.size() * m_min_d_tk)) { c.unfreeze(); m_num_frozen--; activated++; if (!activate_frozen_clause(c)) { // clause was satisfied, reduced to a conflict, unit or binary clause. del_clause(c); continue; } } else { c.inc_inact_rounds(); if (c.inact_rounds() > m_config.m_gc_k) { del_clause(c); m_stats.m_gc_clause++; deleted++; continue; } } } *it2 = *it; ++it2; } m_learned.set_end(it2); IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "(sat-gc :d_tk " << d_tk << " :min-d_tk " << m_min_d_tk << " :frozen " << frozen << " :activated " << activated << " :deleted " << deleted << ")\n";); } // return true if should keep the clause, and false if we should delete it. bool solver::activate_frozen_clause(clause & c) { TRACE("sat_gc", tout << "reactivating:\n" << c << "\n";); SASSERT(at_base_lvl()); // do some cleanup unsigned sz = c.size(); unsigned j = 0; for (unsigned i = 0; i < sz; i++) { literal l = c[i]; switch (value(l)) { case l_true: return false; case l_false: break; case l_undef: if (i != j) { std::swap(c[i], c[j]); } j++; break; } } TRACE("sat", tout << "after cleanup:\n" << mk_lits_pp(j, c.begin()) << "\n";); unsigned new_sz = j; switch (new_sz) { case 0: if (m_config.m_drat) m_drat.add(); set_conflict(); return false; case 1: assign_unit(c[0]); return false; case 2: mk_bin_clause(c[0], c[1], true); return false; default: shrink(c, sz, new_sz); attach_clause(c); return true; } } /** \brief Compute phase saving measure for the given clause. */ unsigned solver::psm(clause const & c) const { unsigned r = 0; for (literal l : c) { if (l.sign() ^ m_phase[l.var()]) { r++; } } return r; } // ----------------------- // // Conflict resolution // // ----------------------- bool solver::resolve_conflict() { while (true) { lbool r = resolve_conflict_core(); CASSERT("sat_check_marks", check_marks()); // after pop, clauses are reinitialized, this may trigger another conflict. if (r == l_false) return false; if (!inconsistent()) return true; } } lbool solver::resolve_conflict_core() { m_conflicts_since_init++; m_conflicts_since_restart++; m_conflicts_since_gc++; m_stats.m_conflict++; if (m_step_size > m_config.m_step_size_min) { m_step_size -= m_config.m_step_size_dec; } bool unique_max; m_conflict_lvl = get_max_lvl(m_not_l, m_conflict, unique_max); justification js = m_conflict; if (m_conflict_lvl <= 1 && tracking_assumptions()) { TRACE("sat", tout << "unsat core\n";); resolve_conflict_for_unsat_core(); return l_false; } if (m_conflict_lvl == 0) { TRACE("sat", tout << "conflict level is 0\n";); return l_false; } // force_conflict_analysis is used instead of relying on normal propagation to assign m_not_l // at the backtracking level. This is the case where the external theories miss propagations // that only get triggered after decisions. if (unique_max && !m_force_conflict_analysis) { TRACE("sat", tout << "unique max " << js << " " << m_not_l << "\n";); pop_reinit(m_scope_lvl - m_conflict_lvl + 1); m_force_conflict_analysis = true; ++m_stats.m_backtracks; return l_undef; } m_force_conflict_analysis = false; updt_phase_of_vars(); if (m_ext) { switch (m_ext->resolve_conflict()) { case l_true: learn_lemma_and_backjump(); return l_undef; case l_undef: break; case l_false: // backjumping was taken care of internally. return l_undef; } } m_lemma.reset(); unsigned idx = skip_literals_above_conflict_level(); // save space for first uip m_lemma.push_back(null_literal); TRACE("sat_conflict_detail", tout << "resolve: " << m_not_l << " " << " js: " << js << " idx: " << idx << " trail: " << m_trail.size() << " @" << m_conflict_lvl << "\n";); unsigned num_marks = 0; literal consequent = null_literal; if (m_not_l != null_literal) { TRACE("sat_conflict_detail", tout << "not_l: " << m_not_l << "\n";); process_antecedent(m_not_l, num_marks); consequent = ~m_not_l; } do { TRACE("sat_conflict_detail", tout << "processing consequent: " << consequent << " @" << (consequent==null_literal?m_conflict_lvl:lvl(consequent)) << "\n"; tout << "num_marks: " << num_marks << "\n"; display_justification(tout, js) << "\n";); switch (js.get_kind()) { case justification::NONE: break; case justification::BINARY: process_antecedent(~(js.get_literal()), num_marks); break; case justification::TERNARY: process_antecedent(~(js.get_literal1()), num_marks); process_antecedent(~(js.get_literal2()), num_marks); break; case justification::CLAUSE: { clause & c = get_clause(js); unsigned i = 0; if (consequent != null_literal) { SASSERT(c[0] == consequent || c[1] == consequent); if (c[0] == consequent) { i = 1; } else { process_antecedent(~c[0], num_marks); i = 2; } } unsigned sz = c.size(); for (; i < sz; i++) process_antecedent(~c[i], num_marks); break; } case justification::EXT_JUSTIFICATION: { fill_ext_antecedents(consequent, js); for (literal l : m_ext_antecedents) process_antecedent(l, num_marks); break; } default: UNREACHABLE(); break; } bool_var c_var; while (true) { consequent = m_trail[idx]; c_var = consequent.var(); if (is_marked(c_var)) { if (lvl(c_var) == m_conflict_lvl) { break; } SASSERT(lvl(c_var) < m_conflict_lvl); } SASSERT(idx > 0); idx--; } SASSERT(lvl(consequent) == m_conflict_lvl); js = m_justification[c_var]; idx--; num_marks--; reset_mark(c_var); TRACE("sat", display_justification(tout << consequent << " ", js) << "\n";); } while (num_marks > 0); m_lemma[0] = ~consequent; learn_lemma_and_backjump(); return l_undef; } void solver::learn_lemma_and_backjump() { TRACE("sat_lemma", tout << "new lemma size: " << m_lemma.size() << "\n" << m_lemma << "\n";); if (m_lemma.empty()) { pop_reinit(m_scope_lvl); mk_clause_core(0, nullptr, true); return; } if (m_config.m_minimize_lemmas) { minimize_lemma(); reset_lemma_var_marks(); if (m_config.m_dyn_sub_res) dyn_sub_res(); TRACE("sat_lemma", tout << "new lemma (after minimization) size: " << m_lemma.size() << "\n" << m_lemma << "\n";); } else { reset_lemma_var_marks(); } unsigned backtrack_lvl = lvl(m_lemma[0]); unsigned backjump_lvl = 0; for (unsigned i = m_lemma.size(); i-- > 1;) { unsigned level = lvl(m_lemma[i]); backjump_lvl = std::max(level, backjump_lvl); } // with scope tracking and chronological backtracking, // consequent may not be at highest decision level. if (backtrack_lvl < backjump_lvl) { backtrack_lvl = backjump_lvl; for (unsigned i = m_lemma.size(); i-- > 1;) { if (lvl(m_lemma[i]) == backjump_lvl) { TRACE("sat", tout << "swap " << m_lemma[0] << "@" << lvl(m_lemma[0]) << m_lemma[1] << "@" << backjump_lvl << "\n";); std::swap(m_lemma[i], m_lemma[0]); break; } } } unsigned glue = num_diff_levels(m_lemma.size(), m_lemma.c_ptr()); m_fast_glue_avg.update(glue); m_slow_glue_avg.update(glue); // compute whether to use backtracking or backjumping unsigned num_scopes = m_scope_lvl - backjump_lvl; if (use_backjumping(num_scopes)) { ++m_stats.m_backjumps; pop_reinit(num_scopes); } else { TRACE("sat", tout << "backtrack " << (m_scope_lvl - backtrack_lvl + 1) << " scopes\n";); ++m_stats.m_backtracks; pop_reinit(m_scope_lvl - backtrack_lvl + 1); } clause * lemma = mk_clause_core(m_lemma.size(), m_lemma.c_ptr(), true); if (lemma) { lemma->set_glue(glue); } if (m_par && lemma) { m_par->share_clause(*this, *lemma); } TRACE("sat_conflict_detail", tout << "consistent " << (!m_inconsistent) << " scopes: " << scope_lvl() << " backtrack: " << backtrack_lvl << " backjump: " << backjump_lvl << "\n";); decay_activity(); updt_phase_counters(); } bool solver::use_backjumping(unsigned num_scopes) { return num_scopes > 0 && (num_scopes <= m_config.m_backtrack_scopes || m_conflicts_since_init <= m_config.m_backtrack_init_conflicts); } void solver::process_antecedent_for_unsat_core(literal antecedent) { bool_var var = antecedent.var(); SASSERT(var < num_vars()); TRACE("sat", tout << antecedent << " " << (is_marked(var)?"+":"-") << "\n";); if (!is_marked(var)) { mark(var); m_unmark.push_back(var); if (is_assumption(antecedent)) { m_core.push_back(antecedent); } } } void solver::process_consequent_for_unsat_core(literal consequent, justification const& js) { TRACE("sat", tout << "processing consequent: "; if (consequent == null_literal) tout << "null\n"; else tout << consequent << "\n"; display_justification(tout << "js kind: ", js) << "\n";); switch (js.get_kind()) { case justification::NONE: break; case justification::BINARY: SASSERT(consequent != null_literal); process_antecedent_for_unsat_core(~(js.get_literal())); break; case justification::TERNARY: SASSERT(consequent != null_literal); process_antecedent_for_unsat_core(~(js.get_literal1())); process_antecedent_for_unsat_core(~(js.get_literal2())); break; case justification::CLAUSE: { clause & c = get_clause(js); unsigned i = 0; if (consequent != null_literal) { SASSERT(c[0] == consequent || c[1] == consequent); if (c[0] == consequent) { i = 1; } else { process_antecedent_for_unsat_core(~c[0]); i = 2; } } unsigned sz = c.size(); for (; i < sz; i++) process_antecedent_for_unsat_core(~c[i]); break; } case justification::EXT_JUSTIFICATION: { fill_ext_antecedents(consequent, js); for (literal l : m_ext_antecedents) { process_antecedent_for_unsat_core(l); } break; } default: UNREACHABLE(); break; } } void solver::resolve_conflict_for_unsat_core() { TRACE("sat", display(tout); unsigned level = 0; for (literal l : m_trail) { if (level != lvl(l)) { level = lvl(l); tout << level << ": "; } tout << l; if (m_mark[l.var()]) { tout << "*"; } tout << " "; } tout << "\n"; tout << "conflict level: " << m_conflict_lvl << "\n"; ); m_core.reset(); if (m_conflict_lvl == 0) { return; } SASSERT(m_unmark.empty()); DEBUG_CODE({ for (literal lit : m_trail) { SASSERT(!is_marked(lit.var())); }}); unsigned old_size = m_unmark.size(); int idx = skip_literals_above_conflict_level(); literal consequent = m_not_l; if (m_not_l != null_literal) { justification js = m_justification[m_not_l.var()]; TRACE("sat", tout << "not_l: " << m_not_l << "\n"; display_justification(tout, js) << "\n";); process_antecedent_for_unsat_core(m_not_l); if (is_assumption(~m_not_l)) { m_core.push_back(~m_not_l); } else { process_consequent_for_unsat_core(m_not_l, js); } consequent = ~m_not_l; } justification js = m_conflict; int init_sz = init_trail_size(); while (true) { process_consequent_for_unsat_core(consequent, js); while (idx >= init_sz) { consequent = m_trail[idx]; if (is_marked(consequent.var()) && lvl(consequent) == m_conflict_lvl) break; idx--; } if (idx < init_sz) { break; } SASSERT(lvl(consequent) == m_conflict_lvl); js = m_justification[consequent.var()]; idx--; } reset_unmark(old_size); if (m_core.size() > 1) { unsigned j = 0; for (unsigned i = 0; i < m_core.size(); ++i) { if (lvl(m_core[i]) > 0) m_core[j++] = m_core[i]; } m_core.shrink(j); } if (m_config.m_core_minimize) { if (m_min_core_valid && m_min_core.size() < m_core.size()) { 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); } // TBD: // apply optional clause minimization by detecting subsumed literals. // initial experiment suggests it has no effect. m_mus(); // ignore return value on cancelation. set_model(m_mus.get_model()); IF_VERBOSE(2, verbose_stream() << "(sat.core: " << m_core << ")\n";); } } unsigned solver::get_max_lvl(literal not_l, justification js, bool& unique_max) { unique_max = true; unsigned level = 0; if (not_l != null_literal) { level = lvl(not_l); } switch (js.get_kind()) { case justification::NONE: level = std::max(level, js.level()); return level; case justification::BINARY: level = update_max_level(js.get_literal(), level, unique_max); return level; case justification::TERNARY: level = update_max_level(js.get_literal1(), level, unique_max); level = update_max_level(js.get_literal2(), level, unique_max); return level; case justification::CLAUSE: for (literal l : get_clause(js)) { level = update_max_level(l, level, unique_max); } return level; case justification::EXT_JUSTIFICATION: SASSERT(not_l != null_literal); fill_ext_antecedents(~not_l, js); for (literal l : m_ext_antecedents) { level = update_max_level(l, level, unique_max); } return level; default: UNREACHABLE(); return 0; } } /** \brief Skip literals from levels above m_conflict_lvl. It returns an index idx such that lvl(m_trail[idx]) <= m_conflict_lvl, and for all idx' > idx, lvl(m_trail[idx']) > m_conflict_lvl */ unsigned solver::skip_literals_above_conflict_level() { unsigned idx = m_trail.size(); if (idx == 0) { return idx; } idx--; // skip literals from levels above the conflict level while (lvl(m_trail[idx]) > m_conflict_lvl) { SASSERT(idx > 0); idx--; } return idx; } void solver::process_antecedent(literal antecedent, unsigned & num_marks) { bool_var var = antecedent.var(); unsigned var_lvl = lvl(var); SASSERT(var < num_vars()); if (!is_marked(var) && var_lvl > 0) { mark(var); switch (m_config.m_branching_heuristic) { case BH_VSIDS: inc_activity(var); break; case BH_CHB: m_last_conflict[var] = m_stats.m_conflict; break; default: break; } if (var_lvl == m_conflict_lvl) num_marks++; else m_lemma.push_back(~antecedent); } } /** \brief js is an external justification. Collect its antecedents and store at m_ext_antecedents. */ void solver::fill_ext_antecedents(literal consequent, justification js) { SASSERT(js.is_ext_justification()); SASSERT(m_ext); m_ext_antecedents.reset(); m_ext->get_antecedents(consequent, js.get_ext_justification_idx(), m_ext_antecedents); } bool solver::is_two_phase() const { return m_config.m_phase == PS_SAT_CACHING; } bool solver::is_sat_phase() const { return is_two_phase() && m_search_state == s_sat; } void solver::updt_phase_of_vars() { unsigned from_lvl = m_conflict_lvl; unsigned head = from_lvl == 0 ? 0 : m_scopes[from_lvl - 1].m_trail_lim; unsigned sz = m_trail.size(); for (unsigned i = head; i < sz; i++) { bool_var v = m_trail[i].var(); TRACE("forget_phase", tout << "forgetting phase of v" << v << "\n";); m_phase[v] = m_rand() % 2 == 0; } if (is_sat_phase() && head >= m_best_phase_size) { m_best_phase_size = head; IF_VERBOSE(12, verbose_stream() << "sticky trail: " << head << "\n"); for (unsigned i = 0; i < head; ++i) { bool_var v = m_trail[i].var(); m_best_phase[v] = m_phase[v]; } } } bool solver::should_toggle_search_state() { if (m_search_state == s_unsat) { m_trail_avg.update(m_trail.size()); } return (m_phase_counter >= m_search_next_toggle) && (m_search_state == s_sat || m_trail.size() > 0.50*m_trail_avg); } void solver::do_toggle_search_state() { if (is_two_phase()) { m_best_phase_size = 0; std::swap(m_fast_glue_backup, m_fast_glue_avg); std::swap(m_slow_glue_backup, m_slow_glue_avg); if (m_search_state == s_sat) { m_search_unsat_conflicts += m_config.m_search_unsat_conflicts; } else { m_search_sat_conflicts += m_config.m_search_sat_conflicts; } } if (m_search_state == s_unsat) { m_search_state = s_sat; m_search_next_toggle = m_search_sat_conflicts; } else { m_search_state = s_unsat; m_search_next_toggle = m_search_unsat_conflicts; } m_phase_counter = 0; } bool solver::should_rephase() { return m_conflicts_since_init > m_rephase_lim; } void solver::do_rephase() { switch (m_config.m_phase) { case PS_ALWAYS_TRUE: for (auto& p : m_phase) p = true; break; case PS_ALWAYS_FALSE: for (auto& p : m_phase) p = false; break; case PS_BASIC_CACHING: switch (m_rephase_lim % 4) { case 0: for (auto& p : m_phase) p = (m_rand() % 2) == 0; break; case 1: for (auto& p : m_phase) p = false; break; case 2: for (auto& p : m_phase) p = !p; break; default: break; } break; case PS_SAT_CACHING: if (m_search_state == s_sat) { for (unsigned i = 0; i < m_phase.size(); ++i) { m_phase[i] = m_best_phase[i]; } } break; case PS_RANDOM: for (auto& p : m_phase) p = (m_rand() % 2) == 0; break; default: UNREACHABLE(); break; } m_rephase_inc += m_config.m_rephase_base; m_rephase_lim += m_rephase_inc; } bool solver::should_reorder() { return m_conflicts_since_init > m_reorder_lim; } void solver::do_reorder() { IF_VERBOSE(1, verbose_stream() << "(reorder)\n"); m_activity_inc = 128; svector vars; for (bool_var v = num_vars(); v-- > 0; ) { if (!was_eliminated(v) && value(v) == l_undef) { vars.push_back(v); } } #if 1 // // exp(logits[i]) / sum(exp(logits)) // = // exp(log(exp(logits[i]) / sum(exp(logits)))) // = // exp(log(exp(logits[i])) - log(sum(exp(logits)))) // = // exp(logits[i] - lse) svector logits(vars.size(), 0.0); double itau = m_config.m_reorder_itau; double lse = 0; double mid = m_rand.max_value()/2; double max = 0; for (double& f : logits) { f = itau * (m_rand() - mid)/mid; if (f > max) max = f; } for (double f : logits) { lse += log(f - max); } lse = max + exp(lse); for (unsigned i = 0; i < vars.size(); ++i) { update_activity(vars[i], exp(logits[i] - lse)); } #else shuffle(vars.size(), vars.c_ptr(), m_rand); for (bool_var v : vars) { update_activity(v, m_rand(10)/10.0); } #endif m_reorder_inc += m_config.m_reorder_base; m_reorder_lim += m_reorder_inc; } void solver::updt_phase_counters() { m_phase_counter++; if (should_toggle_search_state()) { do_toggle_search_state(); } } /** \brief Return the number of different levels in lits. All literals in lits must be assigned. */ unsigned solver::num_diff_levels(unsigned num, literal const * lits) { m_diff_levels.reserve(scope_lvl() + 1, false); unsigned r = 0; for (unsigned i = 0; i < num; i++) { SASSERT(value(lits[i]) != l_undef); unsigned lit_lvl = lvl(lits[i]); if (m_diff_levels[lit_lvl] == false) { m_diff_levels[lit_lvl] = true; r++; } } // reset m_diff_levels. for (unsigned i = 0; i < num; i++) m_diff_levels[lvl(lits[i])] = false; return r; } bool solver::num_diff_levels_below(unsigned num, literal const* lits, unsigned max_glue, unsigned& glue) { m_diff_levels.reserve(scope_lvl() + 1, false); glue = 0; unsigned i = 0; for (; i < num && glue < max_glue; i++) { SASSERT(value(lits[i]) != l_undef); unsigned lit_lvl = lvl(lits[i]); if (m_diff_levels[lit_lvl] == false) { m_diff_levels[lit_lvl] = true; glue++; } } num = i; // reset m_diff_levels. for (i = 0; i < num; i++) m_diff_levels[lvl(lits[i])] = false; return glue < max_glue; } bool solver::num_diff_false_levels_below(unsigned num, literal const* lits, unsigned max_glue, unsigned& glue) { m_diff_levels.reserve(scope_lvl() + 1, false); glue = 0; unsigned i = 0; for (; i < num && glue < max_glue; i++) { if (value(lits[i]) == l_false) { unsigned lit_lvl = lvl(lits[i]); if (m_diff_levels[lit_lvl] == false) { m_diff_levels[lit_lvl] = true; glue++; } } } num = i; // reset m_diff_levels. for (i = 0; i < num; i++) { literal lit = lits[i]; if (value(lit) == l_false) { VERIFY(lvl(lit) < m_diff_levels.size()); m_diff_levels[lvl(lit)] = false; } } return glue < max_glue; } /** \brief Process an antecedent for lemma minimization. */ bool solver::process_antecedent_for_minimization(literal antecedent) { bool_var var = antecedent.var(); unsigned var_lvl = lvl(var); if (!is_marked(var) && var_lvl > 0) { if (m_lvl_set.may_contain(var_lvl)) { mark(var); m_unmark.push_back(var); m_lemma_min_stack.push_back(antecedent); } else { return false; } } return true; } /** \brief Return true if lit is implied by other marked literals and/or literals assigned at the base level. The set lvl_set is used as an optimization. The idea is to stop the recursive search with a failure as soon as we find a literal assigned in a level that is not in lvl_set. */ bool solver::implied_by_marked(literal lit) { m_lemma_min_stack.reset(); // avoid recursive function m_lemma_min_stack.push_back(lit); unsigned old_size = m_unmark.size(); while (!m_lemma_min_stack.empty()) { lit = m_lemma_min_stack.back(); bool_var var = lit.var(); m_lemma_min_stack.pop_back(); justification const& js = m_justification[var]; switch(js.get_kind()) { case justification::NONE: // it is a decision variable from a previous scope level if (lvl(var) > 0) { reset_unmark(old_size); return false; } break; case justification::BINARY: if (!process_antecedent_for_minimization(~(js.get_literal()))) { reset_unmark(old_size); return false; } break; case justification::TERNARY: if (!process_antecedent_for_minimization(~(js.get_literal1())) || !process_antecedent_for_minimization(~(js.get_literal2()))) { reset_unmark(old_size); return false; } break; case justification::CLAUSE: { clause & c = get_clause(js); unsigned i = 0; if (c[0].var() == var) { i = 1; } else { SASSERT(c[1].var() == var); if (!process_antecedent_for_minimization(~c[0])) { reset_unmark(old_size); return false; } i = 2; } unsigned sz = c.size(); for (; i < sz; i++) { if (!process_antecedent_for_minimization(~c[i])) { reset_unmark(old_size); return false; } } break; } case justification::EXT_JUSTIFICATION: { literal consequent(var, value(var) == l_false); fill_ext_antecedents(consequent, js); for (literal l : m_ext_antecedents) { if (!process_antecedent_for_minimization(l)) { reset_unmark(old_size); return false; } } break; } default: UNREACHABLE(); break; } TRACE("sat_conflict", display_justification(tout << var << " ",js) << "\n";); } return true; } /** \brief Restore the size of m_unmark to old_size, and unmark variables at positions [old_size, m_unmark.size()). */ void solver::reset_unmark(unsigned old_size) { unsigned curr_size = m_unmark.size(); for(unsigned i = old_size; i < curr_size; i++) reset_mark(m_unmark[i]); m_unmark.shrink(old_size); } /** \brief Store the levels of the literals at m_lemma in the approximated set m_lvl_set. */ void solver::updt_lemma_lvl_set() { m_lvl_set.reset(); for (literal l : m_lemma) m_lvl_set.insert(lvl(l)); } /** \brief Minimize lemma using binary resolution */ bool solver::minimize_lemma_binres() { SASSERT(!m_lemma.empty()); SASSERT(m_unmark.empty()); unsigned sz = m_lemma.size(); unsigned num_reduced = 0; for (unsigned i = 1; i < sz; ++i) { mark_lit(m_lemma[i]); } watch_list const& wlist = get_wlist(m_lemma[0]); for (watched const& w : wlist) { if (w.is_binary_clause() && is_marked_lit(w.get_literal())) { unmark_lit(~w.get_literal()); num_reduced++; } } if (num_reduced > 0) { unsigned j = 1; for (unsigned i = 1; i < sz; ++i) { if (is_marked_lit(m_lemma[i])) { m_lemma[j++] = m_lemma[i]; unmark_lit(m_lemma[i]); } } m_lemma.shrink(j); } return num_reduced > 0; } /** \brief Minimize the number of literals in m_lemma. The main idea is to remove literals that are implied by other literals in m_lemma and/or literals assigned at level 0. */ bool solver::minimize_lemma() { SASSERT(!m_lemma.empty()); SASSERT(m_unmark.empty()); updt_lemma_lvl_set(); unsigned sz = m_lemma.size(); unsigned i = 1; // the first literal is the FUIP unsigned j = 1; for (; i < sz; i++) { literal l = m_lemma[i]; if (implied_by_marked(l)) { m_unmark.push_back(l.var()); } else { m_lemma[j++] = m_lemma[i]; } } reset_unmark(0); m_lemma.shrink(j); m_stats.m_minimized_lits += sz - j; return j < sz; } /** \brief Reset the mark of the variables in the current lemma. */ void solver::reset_lemma_var_marks() { if (m_config.m_branching_heuristic == BH_LRB || m_config.m_branching_heuristic == BH_VSIDS) { update_lrb_reasoned(); } literal_vector::iterator it = m_lemma.begin(); literal_vector::iterator end = m_lemma.end(); SASSERT(!is_marked((*it).var())); ++it; for(; it != end; ++it) { bool_var var = (*it).var(); reset_mark(var); } } void solver::update_lrb_reasoned() { unsigned sz = m_lemma.size(); SASSERT(!is_marked(m_lemma[0].var())); mark(m_lemma[0].var()); for (unsigned i = m_lemma.size(); i-- > 0; ) { justification js = m_justification[m_lemma[i].var()]; switch (js.get_kind()) { case justification::NONE: break; case justification::BINARY: update_lrb_reasoned(js.get_literal()); break; case justification::TERNARY: update_lrb_reasoned(js.get_literal1()); update_lrb_reasoned(js.get_literal2()); break; case justification::CLAUSE: { clause & c = get_clause(js); for (literal l : c) { update_lrb_reasoned(l); } break; } case justification::EXT_JUSTIFICATION: { fill_ext_antecedents(~m_lemma[i], js); for (literal l : m_ext_antecedents) { update_lrb_reasoned(l); } break; } } } reset_mark(m_lemma[0].var()); for (unsigned i = m_lemma.size(); i-- > sz; ) { reset_mark(m_lemma[i].var()); } m_lemma.shrink(sz); } void solver::update_lrb_reasoned(literal lit) { bool_var v = lit.var(); if (!is_marked(v)) { mark(v); m_reasoned[v]++; inc_activity(v); m_lemma.push_back(lit); } } /** \brief Apply dynamic subsumption resolution to new lemma. Only binary and ternary clauses are used. */ bool solver::dyn_sub_res() { unsigned sz = m_lemma.size(); for (unsigned i = 0; i < sz; i++) { mark_lit(m_lemma[i]); } literal l0 = m_lemma[0]; // l0 is the FUIP, and we never remove the FUIP. // // In the following loop, we use unmark_lit(l) to remove a // literal from m_lemma. for (unsigned i = 0; i < sz; i++) { literal l = m_lemma[i]; if (!is_marked_lit(l)) continue; // literal was eliminated // first use watch lists watch_list const & wlist = get_wlist(~l); for (watched const& w : wlist) { // In this for-loop, the conditions l0 != ~l2 and l0 != ~l3 // are not really needed if the solver does not miss unit propagations. // However, we add them anyway because we don't want to rely on this // property of the propagator. // For example, if this property is relaxed in the future, then the code // without the conditions l0 != ~l2 and l0 != ~l3 may remove the FUIP if (w.is_binary_clause()) { literal l2 = w.get_literal(); if (is_marked_lit(~l2) && l0 != ~l2) { // eliminate ~l2 from lemma because we have the clause l \/ l2 unmark_lit(~l2); } } else if (w.is_ternary_clause()) { literal l2 = w.get_literal1(); literal l3 = w.get_literal2(); if (is_marked_lit(l2) && is_marked_lit(~l3) && l0 != ~l3) { // eliminate ~l3 from lemma because we have the clause l \/ l2 \/ l3 unmark_lit(~l3); } else if (is_marked_lit(~l2) && is_marked_lit(l3) && l0 != ~l2) { // eliminate ~l2 from lemma because we have the clause l \/ l2 \/ l3 unmark_lit(~l2); } } else { // May miss some binary/ternary clauses, but that is ok. // I sort the watch lists at every simplification round. break; } } // try to use cached implication if available literal_vector * implied_lits = m_probing.cached_implied_lits(~l); if (implied_lits) { for (literal l2 : *implied_lits) { // Here, we must check l0 != ~l2. // l \/ l2 is an implied binary clause. // However, it may have been deduced using a lemma that has been deleted. // For example, consider the following sequence of events: // // 1. Initial clause database: // // l \/ ~p1 // p1 \/ ~p2 // p2 \/ ~p3 // p3 \/ ~p4 // q1 \/ q2 \/ p1 \/ p2 \/ p3 \/ p4 \/ l2 // q1 \/ ~q2 \/ p1 \/ p2 \/ p3 \/ p4 \/ l2 // ~q1 \/ q2 \/ p1 \/ p2 \/ p3 \/ p4 \/ l2 // ~q1 \/ ~q2 \/ p1 \/ p2 \/ p3 \/ p4 \/ l2 // ... // // 2. Now suppose we learned the lemma // // p1 \/ p2 \/ p3 \/ p4 \/ l2 (*) // // 3. Probing is executed and we notice hat (~l => l2) when we assign l to false. // That is, l \/ l2 is an implied clause. Note that probing does not add // this clause to the clause database (there are too many). // // 4. Lemma (*) is deleted (garbage collected). // // 5. l is decided to be false, p1, p2, p3 and p4 are propagated using BCP, // but l2 is not since the lemma (*) was deleted. // // Probing module still "knows" that l \/ l2 is valid binary clause // // 6. A new lemma is created where ~l2 is the FUIP and the lemma also contains l. // If we remove l0 != ~l2 may try to delete the FUIP. if (is_marked_lit(~l2) && l0 != ~l2) { // eliminate ~l2 from lemma because we have the clause l \/ l2 unmark_lit(~l2); } } } } // can't eliminat FUIP SASSERT(is_marked_lit(m_lemma[0])); unsigned j = 0; for (unsigned i = 0; i < sz; i++) { literal l = m_lemma[i]; if (is_marked_lit(l)) { unmark_lit(l); m_lemma[j] = l; j++; } } m_stats.m_dyn_sub_res += sz - j; SASSERT(j >= 1); m_lemma.shrink(j); return j < sz; } // ----------------------- // // Backtracking // // ----------------------- void solver::push() { SASSERT(!inconsistent()); TRACE("sat_verbose", tout << "q:" << m_qhead << " trail: " << m_trail.size() << "\n";); SASSERT(m_qhead == m_trail.size()); m_scopes.push_back(scope()); scope & s = m_scopes.back(); m_scope_lvl++; s.m_trail_lim = m_trail.size(); s.m_clauses_to_reinit_lim = m_clauses_to_reinit.size(); s.m_inconsistent = m_inconsistent; if (m_ext) m_ext->push(); } void solver::pop_reinit(unsigned num_scopes) { pop(num_scopes); exchange_par(); reinit_assumptions(); m_stats.m_units = init_trail_size(); } void solver::pop(unsigned num_scopes) { if (num_scopes == 0) return; if (m_ext) m_ext->pop(num_scopes); SASSERT(num_scopes <= scope_lvl()); unsigned new_lvl = scope_lvl() - num_scopes; scope & s = m_scopes[new_lvl]; m_inconsistent = false; unassign_vars(s.m_trail_lim, new_lvl); m_scope_lvl -= num_scopes; m_scopes.shrink(new_lvl); reinit_clauses(s.m_clauses_to_reinit_lim); if (m_ext) m_ext->pop_reinit(); } void solver::unassign_vars(unsigned old_sz, unsigned new_lvl) { SASSERT(old_sz <= m_trail.size()); SASSERT(m_replay_assign.empty()); unsigned i = m_trail.size(); while (i != old_sz) { --i; literal l = m_trail[i]; bool_var v = l.var(); if (lvl(v) <= new_lvl) { m_replay_assign.push_back(l); continue; } m_assignment[l.index()] = l_undef; m_assignment[(~l).index()] = l_undef; SASSERT(value(v) == l_undef); m_case_split_queue.unassign_var_eh(v); if (m_config.m_branching_heuristic == BH_LRB) { uint64_t interval = m_stats.m_conflict - m_last_propagation[v]; if (interval > 0) { auto activity = m_activity[v]; auto reward = (m_config.m_reward_offset * (m_participated[v] + m_reasoned[v])) / interval; set_activity(v, static_cast(m_step_size * reward + ((1 - m_step_size) * activity))); } } if (m_config.m_anti_exploration) { m_canceled[v] = m_stats.m_conflict; } } m_trail.shrink(old_sz); m_qhead = m_trail.size(); if (!m_replay_assign.empty()) IF_VERBOSE(20, verbose_stream() << "replay assign: " << m_replay_assign.size() << "\n"); for (unsigned i = m_replay_assign.size(); i-- > 0; ) { m_trail.push_back(m_replay_assign[i]); } m_replay_assign.reset(); } void solver::reinit_clauses(unsigned old_sz) { unsigned sz = m_clauses_to_reinit.size(); SASSERT(old_sz <= sz); unsigned j = old_sz; for (unsigned i = old_sz; i < sz; i++) { clause_wrapper cw = m_clauses_to_reinit[i]; bool reinit = false; if (cw.is_binary()) { if (propagate_bin_clause(cw[0], cw[1])) { if (!at_base_lvl()) { m_clauses_to_reinit[j] = cw; j++; } } } else { clause & c = *(cw.get_clause()); detach_clause(c); attach_clause(c, reinit); if (!at_base_lvl() && reinit) { // clause propagated literal, must keep it in the reinit stack. m_clauses_to_reinit[j] = cw; j++; } else { c.set_reinit_stack(false); } } } m_clauses_to_reinit.shrink(j); } // // All new clauses that are added to the solver // are relative to the user-scope literals. // void solver::user_push() { literal lit; bool_var new_v = mk_var(true, false); lit = literal(new_v, false); m_user_scope_literals.push_back(lit); TRACE("sat", tout << "user_push: " << lit << "\n";); } void solver::gc_lit(clause_vector &clauses, literal lit) { unsigned j = 0; for (unsigned i = 0; i < clauses.size(); ++i) { clause & c = *(clauses[i]); if (c.contains(lit) || c.contains(~lit)) { detach_clause(c); del_clause(c); } else { clauses[j] = &c; ++j; } } clauses.shrink(j); } void solver::gc_bin(literal lit) { bool_var v = lit.var(); for (watch_list& wlist : m_watches) { watch_list::iterator it = wlist.begin(); watch_list::iterator it2 = wlist.begin(); watch_list::iterator end = wlist.end(); for (; it != end; ++it) { if (it->is_binary_clause() && it->get_literal().var() == v) { // skip } else { *it2 = *it; ++it2; } } wlist.set_end(it2); } } bool_var solver::max_var(bool learned, bool_var v) { m_user_bin_clauses.reset(); 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; if (l1.var() > v) v = l1.var(); if (l2.var() > v) v = l2.var(); } return v; } bool_var solver::max_var(clause_vector& clauses, bool_var v) { for (clause* cp : clauses) for (auto it = cp->begin(), end = cp->end(); it != end; ++it) { if (it->var() > v) v = it->var(); } return v; } void solver::gc_var(bool_var v) { bool_var w = max_var(m_learned, v); w = max_var(m_clauses, w); w = max_var(true, w); w = max_var(false, w); v = m_mc.max_var(w); for (literal lit : m_trail) { w = std::max(w, lit.var()); } if (m_ext) { w = m_ext->max_var(w); } v = w + 1; // v is an index of a variable that does not occur in solver state. if (v < m_justification.size()) { for (bool_var i = v; i < m_justification.size(); ++i) { m_case_split_queue.del_var_eh(i); m_probing.reset_cache(literal(i, true)); m_probing.reset_cache(literal(i, false)); } m_watches.shrink(2*v); m_assignment.shrink(2*v); m_justification.shrink(v); m_decision.shrink(v); m_eliminated.shrink(v); m_external.shrink(v); m_touched.shrink(v); m_activity.shrink(v); m_mark.shrink(v); m_lit_mark.shrink(2*v); m_phase.shrink(v); m_best_phase.shrink(v); m_prev_phase.shrink(v); m_assigned_since_gc.shrink(v); m_simplifier.reset_todos(); } } void solver::user_pop(unsigned num_scopes) { pop_to_base_level(); TRACE("sat", display(tout);); while (num_scopes > 0) { literal lit = m_user_scope_literals.back(); m_user_scope_literals.pop_back(); get_wlist(lit).reset(); get_wlist(~lit).reset(); gc_lit(m_learned, lit); gc_lit(m_clauses, lit); gc_bin(lit); TRACE("sat", tout << "gc: " << lit << "\n"; display(tout);); --num_scopes; for (unsigned i = 0; i < m_trail.size(); ++i) { if (m_trail[i] == lit) { TRACE("sat", tout << m_trail << "\n";); unassign_vars(i, 0); break; } } gc_var(lit.var()); } m_qhead = 0; propagate(false); } void solver::pop_to_base_level() { reset_assumptions(); pop(scope_lvl()); } // ----------------------- // // Misc // // ----------------------- void solver::updt_params(params_ref const & p) { m_params.append(p); m_config.updt_params(p); m_simplifier.updt_params(p); m_asymm_branch.updt_params(p); m_probing.updt_params(p); m_scc.updt_params(p); m_rand.set_seed(m_config.m_random_seed); m_step_size = m_config.m_step_size_init; m_drat.updt_config(); m_fast_glue_avg.set_alpha(m_config.m_fast_glue_avg); m_slow_glue_avg.set_alpha(m_config.m_slow_glue_avg); m_fast_glue_backup.set_alpha(m_config.m_fast_glue_avg); m_slow_glue_backup.set_alpha(m_config.m_slow_glue_avg); m_trail_avg.set_alpha(m_config.m_slow_glue_avg); } void solver::collect_param_descrs(param_descrs & d) { config::collect_param_descrs(d); simplifier::collect_param_descrs(d); asymm_branch::collect_param_descrs(d); probing::collect_param_descrs(d); scc::collect_param_descrs(d); } void solver::collect_statistics(statistics & st) const { m_stats.collect_statistics(st); m_cleaner.collect_statistics(st); m_simplifier.collect_statistics(st); m_scc.collect_statistics(st); m_asymm_branch.collect_statistics(st); m_probing.collect_statistics(st); if (m_ext) m_ext->collect_statistics(st); if (m_local_search) m_local_search->collect_statistics(st); st.copy(m_aux_stats); } void solver::reset_statistics() { m_stats.reset(); m_cleaner.reset_statistics(); m_simplifier.reset_statistics(); m_asymm_branch.reset_statistics(); m_probing.reset_statistics(); m_aux_stats.reset(); } // ----------------------- // // Activity related stuff // // ----------------------- void solver::rescale_activity() { SASSERT(m_config.m_branching_heuristic == BH_VSIDS); for (unsigned& act : m_activity) { act >>= 14; } m_activity_inc >>= 14; } void solver::update_chb_activity(bool is_sat, unsigned qhead) { SASSERT(m_config.m_branching_heuristic == BH_CHB); double multiplier = m_config.m_reward_offset * (is_sat ? m_config.m_reward_multiplier : 1.0); for (unsigned i = qhead; i < m_trail.size(); ++i) { auto v = m_trail[i].var(); auto reward = multiplier / (m_stats.m_conflict - m_last_conflict[v] + 1); auto activity = m_activity[v]; set_activity(v, static_cast(m_step_size * reward + ((1.0 - m_step_size) * activity))); } } // ----------------------- // // Iterators // // ----------------------- void solver::collect_bin_clauses(svector & r, bool learned, bool learned_only) const { SASSERT(learned || !learned_only); unsigned sz = m_watches.size(); for (unsigned l_idx = 0; l_idx < sz; l_idx++) { literal l = to_literal(l_idx); l.neg(); for (watched const& w : m_watches[l_idx]) { if (!w.is_binary_clause()) continue; if (!learned && w.is_learned()) continue; else if (learned && learned_only && !w.is_learned()) continue; literal l2 = w.get_literal(); if (l.index() > l2.index()) continue; TRACE("cleanup_bug", tout << "collected: " << l << " " << l2 << "\n";); r.push_back(bin_clause(l, l2)); } } } // ----------------------- // // Debugging // // ----------------------- bool solver::check_invariant() const { if (!m_rlimit.inc()) return true; integrity_checker checker(*this); VERIFY(checker()); VERIFY(!m_ext || m_ext->validate()); return true; } bool solver::check_marks() const { for (bool_var v = 0; v < num_vars(); v++) { SASSERT(!is_marked(v)); } 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++) { literal l = to_literal(l_idx); l.neg(); for (watched const& w : m_watches[l_idx]) { if (!w.is_binary_clause()) continue; literal l2 = w.get_literal(); if (l.index() > l2.index()) continue; out << "(" << l << " " << l2 << ")"; if (w.is_learned()) out << "*"; out << "\n"; } } } void solver::display_units(std::ostream & out) const { unsigned level = 0; for (literal lit : m_trail) { if (lvl(lit) > level) { level = lvl(lit); out << level << ": "; } else { out << " "; } out << lit << " "; if (lvl(lit) < level) { out << "@" << lvl(lit) << " "; } display_justification(out, m_justification[lit.var()]) << "\n"; } } void solver::display(std::ostream & out) const { out << "(sat\n"; display_units(out); display_binary(out); out << m_clauses << m_learned; if (m_ext) { m_ext->display(out); } out << ")\n"; } std::ostream& solver::display_justification(std::ostream & out, justification const& js) const { 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; } 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; for (auto const& wl : m_watches) { literal l = ~to_literal(l_idx++); for (auto const& w : wl) { if (w.is_binary_clause() && l.index() < w.get_literal().index()) num_cls++; } } return num_cls + m_clauses.size() + m_learned.size(); } void solver::num_binary(unsigned& given, unsigned& learned) const { given = learned = 0; unsigned l_idx = 0; for (auto const& wl : m_watches) { literal l = ~to_literal(l_idx++); for (auto const& w : wl) { if (w.is_binary_clause() && l.index() < w.get_literal().index()) { if (w.is_learned()) ++learned; else ++given; } } } } void solver::display_dimacs(std::ostream & out) const { out << "p cnf " << num_vars() << " " << num_clauses() << "\n"; for (literal lit : m_trail) { out << dimacs_lit(lit) << " 0\n"; } unsigned l_idx = 0; for (auto const& wlist : m_watches) { literal l = ~to_literal(l_idx++); for (auto const& w : wlist) { if (w.is_binary_clause() && l.index() < w.get_literal().index()) out << dimacs_lit(l) << " " << dimacs_lit(w.get_literal()) << " 0\n"; } } clause_vector const * vs[2] = { &m_clauses, &m_learned }; for (unsigned i = 0; i < 2; i++) { clause_vector const & cs = *(vs[i]); for (auto cp : cs) { for (literal l : *cp) { out << dimacs_lit(l) << " "; } out << "0\n"; } } } void solver::display_wcnf(std::ostream & out, unsigned sz, literal const* lits, unsigned const* weights) const { unsigned max_weight = 0; for (unsigned i = 0; i < sz; ++i) { max_weight = std::max(max_weight, weights[i]); } ++max_weight; out << "p wcnf " << num_vars() << " " << num_clauses() + sz << " " << max_weight << "\n"; out << "c soft " << sz << "\n"; for (literal lit : m_trail) { out << max_weight << " " << dimacs_lit(lit) << " 0\n"; } unsigned l_idx = 0; for (watch_list const& wlist : m_watches) { literal l = ~to_literal(l_idx); for (watched const& w : wlist) { if (w.is_binary_clause() && l.index() < w.get_literal().index()) out << max_weight << " " << dimacs_lit(l) << " " << dimacs_lit(w.get_literal()) << " 0\n"; } ++l_idx; } clause_vector const * vs[2] = { &m_clauses, &m_learned }; for (unsigned i = 0; i < 2; i++) { clause_vector const & cs = *(vs[i]); for (clause const* cp : cs) { clause const & c = *cp; out << max_weight << " "; for (literal l : c) out << dimacs_lit(l) << " "; out << "0\n"; } } for (unsigned i = 0; i < sz; ++i) { out << weights[i] << " " << lits[i] << " 0\n"; } out.flush(); } void solver::display_watches(std::ostream & out, literal lit) const { display_watch_list(out << lit << ": ", get_wlist(lit)) << "\n"; } void solver::display_watches(std::ostream & out) const { unsigned l_idx = 0; for (watch_list const& wlist : m_watches) { literal l = to_literal(l_idx++); if (!wlist.empty()) display_watch_list(out << l << ": ", wlist) << "\n"; } } std::ostream& solver::display_watch_list(std::ostream& out, watch_list const& wl) const { return sat::display_watch_list(out, cls_allocator(), wl, m_ext.get()); } void solver::display_assignment(std::ostream & out) const { out << m_trail << "\n"; } /** \brief Return true, if c is a clause containing one unassigned literal. */ bool solver::is_unit(clause const & c) const { bool found_undef = false; for (literal l : c) { switch (value(l)) { case l_undef: if (found_undef) return false; found_undef = true; break; case l_true: return false; case l_false: break; } } return found_undef; } /** \brief Return true, if all literals in c are assigned to false. */ bool solver::is_empty(clause const & c) const { for (literal lit : c) if (value(lit) != l_false) return false; return true; } bool solver::check_missed_propagation(clause_vector const & cs) const { for (clause* cp : cs) { clause const & c = *cp; if (c.frozen()) continue; if (is_empty(c) || is_unit(c)) { TRACE("sat_missed_prop", tout << "missed_propagation: " << c << "\n"; for (literal l : c) tout << l << ": " << value(l) << "\n";); UNREACHABLE(); } SASSERT(!is_empty(c)); SASSERT(!is_unit(c)); } return true; } bool solver::check_missed_propagation() const { if (inconsistent()) return true; return check_missed_propagation(m_clauses) && check_missed_propagation(m_learned); } // ----------------------- // // Simplification // // ----------------------- bool solver::do_cleanup(bool force) { if (at_base_lvl() && !inconsistent() && m_cleaner(force)) { if (m_ext) m_ext->clauses_modifed(); return true; } return false; } void solver::simplify(bool learned) { if (!at_base_lvl() || inconsistent()) return; m_simplifier(learned); m_simplifier.finalize(); if (m_ext) m_ext->clauses_modifed(); } unsigned solver::scc_bin() { if (!at_base_lvl() || inconsistent()) return 0; unsigned r = m_scc(); if (r > 0 && m_ext) m_ext->clauses_modifed(); return r; } // ----------------------- // // Extraction of mutexes // // ----------------------- struct neg_literal { unsigned negate(unsigned idx) { return (~to_literal(idx)).index(); } }; lbool solver::find_mutexes(literal_vector const& lits, vector & mutexes) { max_cliques mc; m_user_bin_clauses.reset(); m_binary_clause_graph.reset(); 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; literal l2 = b.second; literal_pair p(l1, l2); if (!seen_bc.contains(p)) { seen_bc.insert(p); mc.add_edge(l1.index(), l2.index()); } } vector _mutexes; literal_vector _lits(lits); if (m_ext) { // m_ext->find_mutexes(_lits, mutexes); } unsigned_vector ps; for (literal lit : _lits) { ps.push_back(lit.index()); } mc.cliques(ps, _mutexes); for (auto const& mux : _mutexes) { literal_vector clique; for (auto const& idx : mux) { clique.push_back(to_literal(idx)); } mutexes.push_back(clique); } return l_true; } // ----------------------- // // Consequence generation. // // ----------------------- static void prune_unfixed(sat::literal_vector& lambda, sat::model const& m) { for (unsigned i = 0; i < lambda.size(); ++i) { if ((m[lambda[i].var()] == l_false) != lambda[i].sign()) { lambda[i] = lambda.back(); lambda.pop_back(); --i; } } } // Algorithm 7: Corebased Algorithm with Chunking static void back_remove(sat::literal_vector& lits, sat::literal l) { for (unsigned i = lits.size(); i > 0; ) { --i; if (lits[i] == l) { lits[i] = lits.back(); lits.pop_back(); return; } } UNREACHABLE(); } static void brute_force_consequences(sat::solver& s, sat::literal_vector const& asms, sat::literal_vector const& gamma, vector& conseq) { for (literal lit : gamma) { sat::literal_vector asms1(asms); asms1.push_back(~lit); lbool r = s.check(asms1.size(), asms1.c_ptr()); if (r == l_false) { conseq.push_back(s.get_core()); } } } static lbool core_chunking(sat::solver& s, model const& m, sat::bool_var_vector const& vars, sat::literal_vector const& asms, vector& conseq, unsigned K) { sat::literal_vector lambda; for (bool_var v : vars) { lambda.push_back(sat::literal(v, m[v] == l_false)); } while (!lambda.empty()) { IF_VERBOSE(1, verbose_stream() << "(sat-backbone-core " << lambda.size() << " " << conseq.size() << ")\n";); unsigned k = std::min(K, lambda.size()); sat::literal_vector gamma, omegaN; for (unsigned i = 0; i < k; ++i) { sat::literal l = lambda[lambda.size() - i - 1]; gamma.push_back(l); omegaN.push_back(~l); } while (true) { sat::literal_vector asms1(asms); asms1.append(omegaN); lbool r = s.check(asms1.size(), asms1.c_ptr()); if (r == l_true) { IF_VERBOSE(1, verbose_stream() << "(sat) " << omegaN << "\n";); prune_unfixed(lambda, s.get_model()); break; } sat::literal_vector const& core = s.get_core(); sat::literal_vector occurs; IF_VERBOSE(1, verbose_stream() << "(core " << core.size() << ")\n";); for (unsigned i = 0; i < omegaN.size(); ++i) { if (core.contains(omegaN[i])) { occurs.push_back(omegaN[i]); } } if (occurs.size() == 1) { sat::literal lit = occurs.back(); sat::literal nlit = ~lit; conseq.push_back(core); back_remove(lambda, ~lit); back_remove(gamma, ~lit); s.mk_clause(1, &nlit); } for (unsigned i = 0; i < omegaN.size(); ++i) { if (occurs.contains(omegaN[i])) { omegaN[i] = omegaN.back(); omegaN.pop_back(); --i; } } if (omegaN.empty() && occurs.size() > 1) { brute_force_consequences(s, asms, gamma, conseq); for (unsigned i = 0; i < gamma.size(); ++i) { back_remove(lambda, gamma[i]); } break; } } } return l_true; } lbool solver::get_consequences(literal_vector const& asms, bool_var_vector const& vars, vector& conseq) { literal_vector lits; lbool is_sat = l_true; if (m_config.m_restart_max != UINT_MAX && !m_model_is_current) { return get_bounded_consequences(asms, vars, conseq); } if (!m_model_is_current) { is_sat = check(asms.size(), asms.c_ptr()); } if (is_sat != l_true) { return is_sat; } model mdl = get_model(); for (unsigned i = 0; i < vars.size(); ++i) { bool_var v = vars[i]; switch (get_model()[v]) { case l_true: lits.push_back(literal(v, false)); break; case l_false: lits.push_back(literal(v, true)); break; default: break; } } if (false && asms.empty()) { is_sat = core_chunking(*this, mdl, vars, asms, conseq, 100); } else { is_sat = get_consequences(asms, lits, conseq); } set_model(mdl); return is_sat; } void solver::fixup_consequence_core() { index_set s; TRACE("sat", tout << m_core << "\n";); for (unsigned i = 0; i < m_core.size(); ++i) { TRACE("sat", tout << m_core[i] << ": "; display_index_set(tout, m_antecedents.find(m_core[i].var())) << "\n";); s |= m_antecedents.find(m_core[i].var()); } m_core.reset(); for (unsigned idx : s) { m_core.push_back(to_literal(idx)); } TRACE("sat", tout << m_core << "\n";); } bool solver::reached_max_conflicts() { if (m_config.m_max_conflicts == 0 || m_conflicts_since_init > m_config.m_max_conflicts) { if (m_reason_unknown != "sat.max.conflicts") { m_reason_unknown = "sat.max.conflicts"; IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "(sat \"abort: max-conflicts = " << m_conflicts_since_init << "\")\n";); } return !inconsistent(); } return false; } lbool solver::get_bounded_consequences(literal_vector const& asms, bool_var_vector const& vars, vector& conseq) { bool_var_set unfixed_vars; unsigned num_units = 0, num_iterations = 0; for (bool_var v : vars) { unfixed_vars.insert(v); } TRACE("sat", tout << asms << "\n";); m_antecedents.reset(); pop_to_base_level(); if (inconsistent()) return l_false; flet _searching(m_searching, true); init_search(); propagate(false); if (inconsistent()) return l_false; if (asms.empty()) { bool_var v = mk_var(true, false); literal lit(v, false); init_assumptions(1, &lit); } else { init_assumptions(asms.size(), asms.c_ptr()); } propagate(false); if (check_inconsistent()) return l_false; extract_fixed_consequences(num_units, asms, unfixed_vars, conseq); do_simplify(); if (check_inconsistent()) { fixup_consequence_core(); return l_false; } while (true) { ++num_iterations; SASSERT(!inconsistent()); lbool r = bounded_search(); if (r != l_undef) { fixup_consequence_core(); return r; } extract_fixed_consequences(num_units, asms, unfixed_vars, conseq); do_restart(true); do_simplify(); if (check_inconsistent()) { fixup_consequence_core(); return l_false; } do_gc(); if (should_cancel()) { return l_undef; } } } lbool solver::get_consequences(literal_vector const& asms, literal_vector const& lits, vector& conseq) { TRACE("sat", tout << asms << "\n";); m_antecedents.reset(); literal_set unfixed_lits(lits), assumptions(asms); bool_var_set unfixed_vars; for (literal lit : lits) { unfixed_vars.insert(lit.var()); } pop_to_base_level(); if (inconsistent()) return l_false; init_search(); propagate(false); if (inconsistent()) return l_false; if (asms.empty()) { bool_var v = mk_var(true, false); literal lit(v, false); init_assumptions(1, &lit); } else { init_assumptions(asms.size(), asms.c_ptr()); } propagate(false); if (check_inconsistent()) return l_false; SASSERT(search_lvl() == 1); unsigned num_iterations = 0; extract_fixed_consequences(unfixed_lits, assumptions, unfixed_vars, conseq); update_unfixed_literals(unfixed_lits, unfixed_vars); while (!unfixed_lits.empty()) { if (scope_lvl() > search_lvl()) { pop(scope_lvl() - search_lvl()); } propagate(false); ++num_iterations; checkpoint(); unsigned num_resolves = 0; unsigned num_fixed = 0; unsigned num_assigned = 0; lbool is_sat = l_true; for (literal lit : unfixed_lits) { if (value(lit) != l_undef) { ++num_fixed; if (lvl(lit) <= 1 && value(lit) == l_true) { extract_fixed_consequences(lit, assumptions, unfixed_vars, conseq); } continue; } push(); ++num_assigned; assign_scoped(~lit); propagate(false); while (inconsistent()) { if (!resolve_conflict()) { TRACE("sat", display(tout << "inconsistent\n");); m_inconsistent = false; is_sat = l_undef; break; } propagate(false); ++num_resolves; } } extract_fixed_consequences(unfixed_lits, assumptions, unfixed_vars, conseq); if (is_sat == l_true) { if (scope_lvl() == search_lvl() && num_resolves > 0) { IF_VERBOSE(1, verbose_stream() << "(sat.get-consequences backjump)\n";); is_sat = l_undef; } else { is_sat = bounded_search(); if (is_sat == l_undef) { do_restart(true); } extract_fixed_consequences(unfixed_lits, assumptions, unfixed_vars, conseq); } } if (is_sat == l_false) { TRACE("sat", tout << "unsat\n";); m_inconsistent = false; } if (is_sat == l_true) { delete_unfixed(unfixed_lits, unfixed_vars); } update_unfixed_literals(unfixed_lits, unfixed_vars); IF_VERBOSE(1, verbose_stream() << "(sat.get-consequences" << " iterations: " << num_iterations << " variables: " << unfixed_lits.size() << " fixed: " << conseq.size() << " status: " << is_sat << " pre-assigned: " << num_fixed << " unfixed: " << lits.size() - conseq.size() - unfixed_lits.size() << ")\n";); if (!unfixed_lits.empty() && m_config.m_restart_max <= num_iterations) { return l_undef; } } return l_true; } void solver::delete_unfixed(literal_set& unfixed_lits, bool_var_set& unfixed_vars) { literal_set to_keep; for (literal lit : unfixed_lits) { if (value(lit) == l_true) { to_keep.insert(lit); } else { unfixed_vars.remove(lit.var()); } } unfixed_lits = to_keep; } void solver::update_unfixed_literals(literal_set& unfixed_lits, bool_var_set& unfixed_vars) { literal_vector to_delete; for (literal lit : unfixed_lits) { if (!unfixed_vars.contains(lit.var())) { to_delete.push_back(lit); } } for (unsigned i = 0; i < to_delete.size(); ++i) { unfixed_lits.remove(to_delete[i]); } } void solver::extract_fixed_consequences(unsigned& start, literal_set const& assumptions, bool_var_set& unfixed, vector& conseq) { SASSERT(!inconsistent()); unsigned sz = m_trail.size(); for (unsigned i = start; i < sz && lvl(m_trail[i]) <= 1; ++i) { extract_fixed_consequences(m_trail[i], assumptions, unfixed, conseq); } start = sz; } void solver::extract_fixed_consequences(literal_set const& unfixed_lits, literal_set const& assumptions, bool_var_set& unfixed_vars, vector& conseq) { for (literal lit: unfixed_lits) { TRACE("sat", tout << "extract: " << lit << " " << value(lit) << " " << lvl(lit) << "\n";); if (lvl(lit) <= 1 && value(lit) == l_true) { extract_fixed_consequences(lit, assumptions, unfixed_vars, conseq); } } } bool solver::check_domain(literal lit, literal lit2) { if (!m_antecedents.contains(lit2.var())) { SASSERT(value(lit2) == l_true); SASSERT(m_todo_antecedents.empty() || m_todo_antecedents.back() != lit2); m_todo_antecedents.push_back(lit2); return false; } else { return true; } } bool solver::extract_assumptions(literal lit, index_set& s) { justification js = m_justification[lit.var()]; TRACE("sat", tout << lit << " " << js << "\n";); bool all_found = true; switch (js.get_kind()) { case justification::NONE: break; case justification::BINARY: if (!check_domain(lit, ~js.get_literal())) return false; s |= m_antecedents.find(js.get_literal().var()); break; case justification::TERNARY: if (!check_domain(lit, ~js.get_literal1()) || !check_domain(lit, ~js.get_literal2())) return false; s |= m_antecedents.find(js.get_literal1().var()); s |= m_antecedents.find(js.get_literal2().var()); break; case justification::CLAUSE: { clause & c = get_clause(js); for (literal l : c) { if (l != lit) { if (check_domain(lit, ~l) && all_found) { s |= m_antecedents.find(l.var()); } else { all_found = false; } } } break; } case justification::EXT_JUSTIFICATION: { fill_ext_antecedents(lit, js); for (literal l : m_ext_antecedents) { if (check_domain(lit, l) && all_found) { s |= m_antecedents.find(l.var()); } else { all_found = false; } } break; } default: UNREACHABLE(); break; } TRACE("sat", display_index_set(tout << lit << ": " , s) << "\n";); return all_found; } std::ostream& solver::display_index_set(std::ostream& out, index_set const& s) const { for (unsigned idx : s) { out << to_literal(idx) << " "; } return out; } bool solver::extract_fixed_consequences1(literal lit, literal_set const& assumptions, bool_var_set& unfixed, vector& conseq) { index_set s; if (m_antecedents.contains(lit.var())) { return true; } if (assumptions.contains(lit)) { s.insert(lit.index()); } else { if (!extract_assumptions(lit, s)) { SASSERT(!m_todo_antecedents.empty()); return false; } add_assumption(lit); } m_antecedents.insert(lit.var(), s); if (unfixed.contains(lit.var())) { literal_vector cons; cons.push_back(lit); for (unsigned idx : s) { cons.push_back(to_literal(idx)); } unfixed.remove(lit.var()); conseq.push_back(cons); } return true; } void solver::extract_fixed_consequences(literal lit, literal_set const& assumptions, bool_var_set& unfixed, vector& conseq) { SASSERT(m_todo_antecedents.empty()); m_todo_antecedents.push_back(lit); while (!m_todo_antecedents.empty()) { if (extract_fixed_consequences1(m_todo_antecedents.back(), assumptions, unfixed, conseq)) { m_todo_antecedents.pop_back(); } } } // ----------------------- // // Statistics // // ----------------------- void solver::display_status(std::ostream & out) const { unsigned num_bin = 0; unsigned num_ext = 0; unsigned num_lits = 0; unsigned l_idx = 0; for (watch_list const& wlist : m_watches) { literal l = ~to_literal(l_idx++); for (watched const& w : wlist) { switch (w.get_kind()) { case watched::BINARY: if (l.index() < w.get_literal().index()) { num_lits += 2; num_bin++; } break; case watched::EXT_CONSTRAINT: num_ext++; break; default: break; } } } unsigned num_elim = 0; for (bool_var v = 0; v < num_vars(); v++) { if (m_eliminated[v]) num_elim++; } unsigned num_ter = 0; unsigned num_cls = 0; clause_vector const * vs[2] = { &m_clauses, &m_learned }; for (unsigned i = 0; i < 2; i++) { clause_vector const & cs = *(vs[i]); for (clause* cp : cs) { clause & c = *cp; if (ENABLE_TERNARY && c.size() == 3) num_ter++; else num_cls++; num_lits += c.size(); } } unsigned total_cls = num_cls + num_ter + num_bin; double mem = static_cast(memory::get_allocation_size())/static_cast(1024*1024); out << "(sat-status\n"; out << " :inconsistent " << (m_inconsistent ? "true" : "false") << "\n"; out << " :vars " << num_vars() << "\n"; out << " :elim-vars " << num_elim << "\n"; out << " :lits " << num_lits << "\n"; out << " :assigned " << m_trail.size() << "\n"; out << " :binary-clauses " << num_bin << "\n"; out << " :ternary-clauses " << num_ter << "\n"; out << " :clauses " << num_cls << "\n"; out << " :del-clause " << m_stats.m_del_clause << "\n"; out << " :avg-clause-size " << (total_cls == 0 ? 0.0 : static_cast(num_lits) / static_cast(total_cls)) << "\n"; out << " :memory " << std::fixed << std::setprecision(2) << mem << ")" << std::endl; } void stats::collect_statistics(statistics & st) const { 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); st.update("sat backjumps", m_backjumps); st.update("sat backtracks", m_backtracks); } void stats::reset() { memset(this, 0, sizeof(*this)); } void mk_stat::display(std::ostream & out) const { unsigned given, learned; m_solver.num_binary(given, learned); 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) { stat.display(out); return out; } }; z3-z3-4.8.7/src/sat/sat_solver.h000066400000000000000000000736711356505360400163550ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_solver.h Abstract: SAT solver main class. Author: Leonardo de Moura (leonardo) 2011-05-21. Revision History: --*/ #ifndef SAT_SOLVER_H_ #define SAT_SOLVER_H_ #include #include "sat/sat_types.h" #include "sat/sat_clause.h" #include "sat/sat_watched.h" #include "sat/sat_justification.h" #include "sat/sat_var_queue.h" #include "sat/sat_extension.h" #include "sat/sat_config.h" #include "sat/sat_cleaner.h" #include "sat/sat_simplifier.h" #include "sat/sat_scc.h" #include "sat/sat_asymm_branch.h" #include "sat/sat_iff3_finder.h" #include "sat/sat_probing.h" #include "sat/sat_mus.h" #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" #include "util/ema.h" #include "util/trace.h" #include "util/rlimit.h" #include "util/scoped_ptr_vector.h" namespace sat { /** \brief Main statistic counters. */ struct stats { unsigned m_mk_var; unsigned m_mk_bin_clause; unsigned m_mk_ter_clause; unsigned m_mk_clause; unsigned m_conflict; unsigned m_propagate; unsigned m_bin_propagate; unsigned m_ter_propagate; unsigned m_decision; unsigned m_restart; unsigned m_gc_clause; unsigned m_del_clause; unsigned m_minimized_lits; unsigned m_dyn_sub_res; unsigned m_non_learned_generation; unsigned m_blocked_corr_sets; unsigned m_elim_var_res; unsigned m_elim_var_bdd; unsigned m_units; unsigned m_backtracks; unsigned m_backjumps; stats() { reset(); } void reset(); void collect_statistics(statistics & st) const; }; class solver : public solver_core { public: struct abort_solver {}; protected: enum search_state { s_sat, s_unsat }; bool m_checkpoint_enabled; config m_config; stats m_stats; scoped_ptr m_ext; parallel* m_par; drat m_drat; // DRAT for generating proofs clause_allocator m_cls_allocator[2]; bool m_cls_allocator_idx; random_gen m_rand; cleaner m_cleaner; model m_model; model_converter m_mc; bool m_model_is_current; simplifier m_simplifier; scc m_scc; asymm_branch m_asymm_branch; probing m_probing; mus m_mus; // MUS for minimal core extraction bool m_inconsistent; bool m_searching; // A conflict is usually a single justification. That is, a justification // for false. If m_not_l is not null_literal, then m_conflict is a // justification for l, and the conflict is union of m_no_l and m_conflict; justification m_conflict; literal m_not_l; clause_vector m_clauses; clause_vector m_learned; unsigned m_num_frozen; vector m_watches; svector m_assignment; svector m_justification; svector m_decision; svector m_mark; svector m_lit_mark; svector m_eliminated; svector m_external; unsigned_vector m_touched; unsigned m_touch_index; literal_vector m_replay_assign; // branch variable selection: svector m_activity; unsigned m_activity_inc; svector m_last_conflict; svector m_last_propagation; svector m_participated; svector m_canceled; svector m_reasoned; int m_action; double m_step_size; // phase svector m_phase; svector m_best_phase; svector m_prev_phase; svector m_assigned_since_gc; search_state m_search_state; unsigned m_search_unsat_conflicts; unsigned m_search_sat_conflicts; unsigned m_search_next_toggle; unsigned m_phase_counter; unsigned m_best_phase_size; unsigned m_rephase_lim; unsigned m_rephase_inc; unsigned m_reorder_lim; unsigned m_reorder_inc; var_queue m_case_split_queue; unsigned m_qhead; unsigned m_scope_lvl; unsigned m_search_lvl; ema m_fast_glue_avg; ema m_slow_glue_avg; ema m_fast_glue_backup; ema m_slow_glue_backup; ema m_trail_avg; literal_vector m_trail; clause_wrapper_vector m_clauses_to_reinit; std::string m_reason_unknown; struct scope { unsigned m_trail_lim; unsigned m_clauses_to_reinit_lim; bool m_inconsistent; }; svector m_scopes; stopwatch m_stopwatch; params_ref m_params; scoped_ptr m_clone; // for debugging purposes literal_vector m_assumptions; // additional assumptions during check literal_set m_assumption_set; // set of enabled assumptions literal_vector m_core; // unsat core unsigned m_par_id; unsigned m_par_limit_in; unsigned m_par_limit_out; unsigned m_par_num_vars; bool m_par_syncing_clauses; class lookahead* m_cuber; class i_local_search* m_local_search; statistics m_aux_stats; void del_clauses(clause_vector& clauses); friend class integrity_checker; friend class cleaner; friend class simplifier; friend class scc; friend class big; friend class binspr; friend class elim_eqs; friend class asymm_branch; friend class probing; friend class iff3_finder; friend class mus; friend class drat; friend class ba_solver; friend class parallel; friend class lookahead; friend class local_search; friend class ddfw; friend class prob; friend class unit_walk; friend struct mk_stat; friend class elim_vars; friend class scoped_detach; public: solver(params_ref const & p, reslimit& l); ~solver() override; // ----------------------- // // Misc // // ----------------------- void updt_params(params_ref const & p) override; static void collect_param_descrs(param_descrs & d); void collect_statistics(statistics & st) const override; void reset_statistics(); void display_status(std::ostream & out) const override; /** \brief Copy (non learned) clauses from src to this solver. Create missing variables if needed. \pre the model converter of src and this must be empty */ 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); 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]; } inline clause_allocator const& cls_allocator() const { return m_cls_allocator[m_cls_allocator_idx]; } inline clause * alloc_clause(unsigned num_lits, literal const * lits, bool learned) { return cls_allocator().mk_clause(num_lits, lits, learned); } inline void dealloc_clause(clause* c) { cls_allocator().del_clause(c); } struct cmp_activity; void defrag_clauses(); bool should_defrag(); bool memory_pressure(); void del_clause(clause & c); clause * mk_clause_core(unsigned num_lits, literal * lits, bool learned); clause * mk_clause_core(literal_vector const& lits) { return mk_clause_core(lits.size(), lits.c_ptr()); } clause * mk_clause_core(unsigned num_lits, literal * lits) { return mk_clause_core(num_lits, lits, false); } void mk_clause_core(literal l1, literal l2) { literal lits[2] = { l1, l2 }; mk_clause_core(2, lits); } void mk_bin_clause(literal l1, literal l2, bool learned); bool propagate_bin_clause(literal l1, literal l2); clause * mk_ter_clause(literal * lits, bool learned); bool attach_ter_clause(clause & c); clause * mk_nary_clause(unsigned num_lits, literal * lits, bool learned); bool attach_nary_clause(clause & c); void attach_clause(clause & c, bool & reinit); void attach_clause(clause & c) { bool reinit; attach_clause(c, reinit); } void set_learned(clause& c, bool learned); void shrink(clause& c, unsigned old_sz, unsigned new_sz); void set_learned(literal l1, literal l2, bool learned); void set_learned1(literal l1, literal l2, bool learned); void add_ate(clause& c) { m_mc.add_ate(c); } void add_ate(literal l1, literal l2) { m_mc.add_ate(l1, l2); } void add_ate(literal_vector const& lits) { m_mc.add_ate(lits); } class scoped_disable_checkpoint { solver& s; public: scoped_disable_checkpoint(solver& s): s(s) { s.m_checkpoint_enabled = false; } ~scoped_disable_checkpoint() { s.m_checkpoint_enabled = true; } }; unsigned select_watch_lit(clause const & cls, unsigned starting_at) const; unsigned select_learned_watch_lit(clause const & cls) const; bool simplify_clause(unsigned & num_lits, literal * lits) const; template bool simplify_clause_core(unsigned & num_lits, literal * lits) const; void detach_bin_clause(literal l1, literal l2, bool learned); void detach_clause(clause & c); void detach_nary_clause(clause & c); void detach_ter_clause(clause & c); void push_reinit_stack(clause & c); // ----------------------- // // Basic // // ----------------------- public: bool inconsistent() const override { return m_inconsistent; } unsigned num_vars() const override { return m_justification.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 override { return m_external[v]; } 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]; } void set_eliminated(bool_var v, bool f) override; 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 override { return m_scope_lvl == 0; } lbool value(literal l) const { return m_assignment[l.index()]; } lbool value(bool_var v) const { return m_assignment[literal(v, false).index()]; } unsigned lvl(bool_var v) const { return m_justification[v].level(); } unsigned lvl(literal l) const { return m_justification[l.var()].level(); } 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) << " j: " << j << "\n";); switch (value(l)) { case l_false: set_conflict(j, ~l); break; case l_undef: assign_core(l, j); break; case l_true: return; } } void assign_unit(literal l) { assign(l, justification(0)); } void assign_scoped(literal l) { assign(l, justification(scope_lvl())); } void assign_core(literal l, justification jst); void set_conflict(justification c, literal not_l); void set_conflict(justification c) { set_conflict(c, null_literal); } void set_conflict() { set_conflict(justification(0)); } lbool status(clause const & c) const; clause_offset get_offset(clause const & c) const { return cls_allocator().get_offset(&c); } bool limit_reached() { if (!m_rlimit.inc()) { m_model_is_current = false; TRACE("sat", tout << "canceled\n";); m_reason_unknown = "sat.canceled"; return true; } return false; } bool memory_exceeded() { ++m_num_checkpoints; if (m_num_checkpoints < 10) return false; m_num_checkpoints = 0; return memory::get_allocation_size() > m_config.m_max_memory; } void checkpoint() { if (!m_checkpoint_enabled) return; if (limit_reached()) { throw solver_exception(Z3_CANCELED_MSG); } if (memory_exceeded()) { throw solver_exception(Z3_MAX_MEMORY_MSG); } } void set_par(parallel* p, unsigned id); bool canceled() { return !m_rlimit.inc(); } 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 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; protected: watch_list & get_wlist(literal l) { return m_watches[l.index()]; } watch_list const & get_wlist(literal l) const { return m_watches[l.index()]; } watch_list & get_wlist(unsigned l_idx) { return m_watches[l_idx]; } bool is_marked(bool_var v) const { return m_mark[v]; } void mark(bool_var v) { SASSERT(!is_marked(v)); m_mark[v] = true; } void reset_mark(bool_var v) { SASSERT(is_marked(v)); m_mark[v] = false; } bool is_marked_lit(literal l) const { return m_lit_mark[l.index()]; } void mark_lit(literal l) { SASSERT(!is_marked_lit(l)); m_lit_mark[l.index()] = true; } void unmark_lit(literal l) { SASSERT(is_marked_lit(l)); m_lit_mark[l.index()] = false; } bool check_inconsistent(); // ----------------------- // // Propagation // // ----------------------- public: // if update == true, then glue of learned clauses is updated. bool propagate(bool update); protected: bool should_propagate() const; bool propagate_core(bool update); // ----------------------- // // Search // // ----------------------- public: lbool check(unsigned num_lits = 0, literal const* lits = nullptr) override; 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 override { return m_core; } model_converter const & get_model_converter() const { return m_mc; } void flush(model_converter& mc) override { mc.flush(m_mc); } void set_model(model const& mdl); 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); void display_lookahead_scores(std::ostream& out); protected: unsigned m_conflicts_since_init; unsigned m_restarts; unsigned m_restart_next_out; unsigned m_conflicts_since_restart; bool m_force_conflict_analysis; unsigned m_simplifications; unsigned m_restart_threshold; unsigned m_luby_idx; unsigned m_conflicts_since_gc; unsigned m_gc_threshold; unsigned m_defrag_threshold; unsigned m_num_checkpoints; double m_min_d_tk; unsigned m_next_simplify; bool decide(); bool_var next_var(); lbool bounded_search(); lbool final_check(); lbool propagate_and_backjump_step(bool& done); void init_search(); literal_vector m_min_core; bool m_min_core_valid; void init_reason_unknown() { m_reason_unknown = "no reason given"; } void init_assumptions(unsigned num_lits, literal const* lits); void reassert_min_core(); void update_min_core(); void resolve_weighted(); void reset_assumptions(); void add_assumption(literal lit); void pop_assumption(); void reinit_assumptions(); bool tracking_assumptions() const; bool is_assumption(literal l) const; bool should_simplify() const; void do_simplify(); void mk_model(); bool check_model(model const & m) const; void do_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_cancel(); bool should_restart() const; void set_next_restart(); void update_activity(bool_var v, double p); bool reached_max_conflicts(); void sort_watch_lits(); void exchange_par(); lbool check_par(unsigned num_lits, literal const* lits); lbool do_local_search(unsigned num_lits, literal const* lits); lbool do_ddfw_search(unsigned num_lits, literal const* lits); lbool do_prob_search(unsigned num_lits, literal const* lits); lbool invoke_local_search(unsigned num_lits, literal const* lits); lbool do_unit_walk(); // ----------------------- // // GC // // ----------------------- protected: bool should_gc() const; void do_gc(); void gc_glue(); void gc_psm(); void gc_glue_psm(); void gc_psm_glue(); void save_psm(); void gc_half(char const * st_name); void gc_dyn_psm(); bool activate_frozen_clause(clause & c); unsigned psm(clause const & c) const; bool can_delete(clause const & c) const; bool can_delete3(literal l1, literal l2, literal l3) const; clause& get_clause(watch_list::iterator it) const { SASSERT(it->get_kind() == watched::CLAUSE); return get_clause(it->get_clause_offset()); } clause& get_clause(watched const& w) const { SASSERT(w.get_kind() == watched::CLAUSE); return get_clause(w.get_clause_offset()); } clause& get_clause(justification const& j) const { SASSERT(j.is_clause()); return get_clause(j.get_clause_offset()); } clause& get_clause(clause_offset cls_off) const { return *(cls_allocator().get_clause(cls_off)); } // ----------------------- // // Conflict resolution // // ----------------------- protected: unsigned m_conflict_lvl; literal_vector m_lemma; literal_vector m_ext_antecedents; bool use_backjumping(unsigned num_scopes); bool resolve_conflict(); lbool resolve_conflict_core(); void learn_lemma_and_backjump(); inline unsigned update_max_level(literal lit, unsigned lvl2, bool& unique_max) { unsigned lvl1 = lvl(lit); if (lvl1 < lvl2) return lvl2; unique_max = lvl1 > lvl2; return lvl1; } unsigned get_max_lvl(literal consequent, justification js, bool& unique_max); void process_antecedent(literal antecedent, unsigned & num_marks); void resolve_conflict_for_unsat_core(); void process_antecedent_for_unsat_core(literal antecedent); void process_consequent_for_unsat_core(literal consequent, justification const& js); void fill_ext_antecedents(literal consequent, justification js); unsigned skip_literals_above_conflict_level(); void updt_phase_of_vars(); void updt_phase_counters(); void do_toggle_search_state(); bool should_toggle_search_state(); bool is_sat_phase() const; bool is_two_phase() const; bool should_rephase(); void do_rephase(); bool should_reorder(); void do_reorder(); svector m_diff_levels; unsigned num_diff_levels(unsigned num, literal const * lits); bool num_diff_levels_below(unsigned num, literal const* lits, unsigned max_glue, unsigned& glue); bool num_diff_false_levels_below(unsigned num, literal const* lits, unsigned max_glue, unsigned& glue); // lemma minimization typedef approx_set_tpl level_approx_set; bool_var_vector m_unmark; level_approx_set m_lvl_set; literal_vector m_lemma_min_stack; bool process_antecedent_for_minimization(literal antecedent); bool implied_by_marked(literal lit); void reset_unmark(unsigned old_size); void updt_lemma_lvl_set(); bool minimize_lemma(); bool minimize_lemma_binres(); void reset_lemma_var_marks(); bool dyn_sub_res(); // ----------------------- // // Backtracking // // ----------------------- void push(); void pop(unsigned num_scopes); void pop_reinit(unsigned num_scopes); void unassign_vars(unsigned old_sz, unsigned new_lvl); void reinit_clauses(unsigned old_sz); literal_vector m_user_scope_literals; literal_vector m_aux_literals; svector m_user_bin_clauses; void gc_lit(clause_vector& clauses, literal lit); void gc_bin(literal lit); void gc_var(bool_var v); bool_var max_var(clause_vector& clauses, bool_var v); bool_var max_var(bool learned, bool_var v); public: 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; } // ----------------------- // // Simplification // // ----------------------- public: bool do_cleanup(bool force); void simplify(bool learned = true); void asymmetric_branching(); unsigned scc_bin(); // ----------------------- // // Auxiliary methods. // // ----------------------- public: lbool find_mutexes(literal_vector const& lits, vector & mutexes); lbool get_consequences(literal_vector const& assms, bool_var_vector const& vars, vector& conseq); // initialize and retrieve local search. // local_search& init_local_search(); private: typedef hashtable index_set; u_map m_antecedents; literal_vector m_todo_antecedents; vector m_binary_clause_graph; bool extract_assumptions(literal lit, index_set& s); bool check_domain(literal lit, literal lit2); std::ostream& display_index_set(std::ostream& out, index_set const& s) const; lbool get_consequences(literal_vector const& assms, literal_vector const& lits, vector& conseq); lbool get_bounded_consequences(literal_vector const& assms, bool_var_vector const& vars, vector& conseq); void delete_unfixed(literal_set& unfixed_lits, bool_var_set& unfixed_vars); void extract_fixed_consequences(unsigned& start, literal_set const& assumptions, bool_var_set& unfixed, vector& conseq); void extract_fixed_consequences(literal_set const& unfixed_lits, literal_set const& assumptions, bool_var_set& unfixed, vector& conseq); void extract_fixed_consequences(literal lit, literal_set const& assumptions, bool_var_set& unfixed, vector& conseq); bool extract_fixed_consequences1(literal lit, literal_set const& assumptions, bool_var_set& unfixed, vector& conseq); void update_unfixed_literals(literal_set& unfixed_lits, bool_var_set& unfixed_vars); void fixup_consequence_core(); // ----------------------- // // Activity related stuff // // ----------------------- public: void inc_activity(bool_var v) { unsigned & act = m_activity[v]; act += m_activity_inc; m_case_split_queue.activity_increased_eh(v); if (act > (1 << 24)) rescale_activity(); } void decay_activity() { m_activity_inc *= m_config.m_variable_decay; m_activity_inc /= 100; } private: void rescale_activity(); void update_chb_activity(bool is_sat, unsigned qhead); void update_lrb_reasoned(); void update_lrb_reasoned(literal lit); // ----------------------- // // Iterators // // ----------------------- public: clause * const * begin_clauses() const { return m_clauses.begin(); } clause * const * end_clauses() const { return m_clauses.end(); } 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 override { return m_clauses; } void collect_bin_clauses(svector & r, bool learned, bool learned_only) const override; // ----------------------- // // Debugging // // ----------------------- public: 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 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; std::ostream& display_watch_list(std::ostream& out, watch_list const& wl) const; protected: void display_binary(std::ostream & out) const; void display_units(std::ostream & out) const; bool is_unit(clause const & c) const; bool is_empty(clause const & c) const; bool check_missed_propagation(clause_vector const & cs) const; bool check_missed_propagation() const; bool check_marks() const; }; struct mk_stat { solver const & m_solver; mk_stat(solver const & s):m_solver(s) {} void display(std::ostream & out) const; }; class scoped_detach { solver& s; clause& c; bool m_deleted; public: scoped_detach(solver& s, clause& c): s(s), c(c), m_deleted(false) { if (!c.frozen()) s.detach_clause(c); } ~scoped_detach() { if (!m_deleted && !c.frozen()) s.attach_clause(c); } void del_clause() { if (!m_deleted) { s.del_clause(c); m_deleted = true; } } }; std::ostream & operator<<(std::ostream & out, mk_stat const & stat); }; #endif z3-z3-4.8.7/src/sat/sat_solver/000077500000000000000000000000001356505360400161665ustar00rootroot00000000000000z3-z3-4.8.7/src/sat/sat_solver/CMakeLists.txt000066400000000000000000000003311356505360400207230ustar00rootroot00000000000000z3_add_component(sat_solver SOURCES inc_sat_solver.cpp COMPONENT_DEPENDENCIES aig_tactic arith_tactics bv_tactics core_tactics sat_tactic solver TACTIC_HEADERS inc_sat_solver.h ) z3-z3-4.8.7/src/sat/sat_solver/inc_sat_solver.cpp000066400000000000000000001027471356505360400217170ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: inc_sat_solver.cpp Abstract: incremental solver based on SAT core. Author: Nikolaj Bjorner (nbjorner) 2014-7-30 Notes: --*/ #include "util/gparams.h" #include "ast/ast_pp.h" #include "ast/ast_translation.h" #include "ast/ast_util.h" #include "solver/solver.h" #include "solver/tactic2solver.h" #include "solver/parallel_params.hpp" #include "solver/parallel_tactic.h" #include "tactic/tactical.h" #include "tactic/aig/aig_tactic.h" #include "tactic/core/propagate_values_tactic.h" #include "tactic/bv/max_bv_sharing_tactic.h" #include "tactic/arith/card2bv_tactic.h" #include "tactic/bv/bit_blaster_tactic.h" #include "tactic/core/simplify_tactic.h" #include "tactic/core/solve_eqs_tactic.h" #include "tactic/bv/bit_blaster_model_converter.h" #include "model/model_smt2_pp.h" #include "model/model_v2_pp.h" #include "model/model_evaluator.h" #include "sat/sat_solver.h" #include "sat/sat_params.hpp" #include "sat/tactic/goal2sat.h" #include "sat/tactic/sat_tactic.h" #include "sat/sat_simplifier_params.hpp" // incremental SAT solver. class inc_sat_solver : public solver { ast_manager& m; mutable sat::solver m_solver; goal2sat m_goal2sat; params_ref m_params; expr_ref_vector m_fmls; expr_ref_vector m_asmsf; unsigned_vector m_fmls_lim; unsigned_vector m_asms_lim; unsigned_vector m_fmls_head_lim; unsigned m_fmls_head; expr_ref_vector m_core; 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; proof_converter_ref m_pc; sref_vector m_mcs; mutable model_converter_ref m_mc0; // TBD: should be saved/retained under push/pop mutable obj_hashtable m_inserted_const2bits; mutable ref m_sat_mc; mutable model_converter_ref m_cached_mc; svector m_weights; std::string m_unknown; // access formulas after they have been pre-processed and handled by the sat solver. // this allows to access the internal state of the SAT solver and carry on partial results. bool m_internalized_converted; // have internalized formulas been converted back expr_ref_vector m_internalized_fmls; // formulas in internalized format typedef obj_map dep2asm_t; bool is_internalized() const { return m_fmls_head == m_fmls.size(); } public: inc_sat_solver(ast_manager& m, params_ref const& p, bool incremental_mode): m(m), m_solver(p, m.limit()), m_fmls(m), m_asmsf(m), 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), m_internalized_fmls(m) { updt_params(p); m_mcs.push_back(nullptr); init_preprocess(); m_solver.set_incremental(incremental_mode && !override_incremental()); } bool override_incremental() const { sat_simplifier_params p(m_params); return p.override_incremental(); } bool is_incremental() const { return m_solver.get_config().m_incremental; } ~inc_sat_solver() override {} solver* translate(ast_manager& dst_m, params_ref const& p) override { if (m_num_scopes > 0) { throw default_exception("Cannot translate sat solver at non-base level"); } ast_translation tr(m, dst_m); m_solver.pop_to_base_level(); inc_sat_solver* result = alloc(inc_sat_solver, dst_m, p, is_incremental()); result->m_solver.copy(m_solver); result->m_fmls_head = m_fmls_head; for (expr* f : m_fmls) result->m_fmls.push_back(tr(f)); for (expr* f : m_asmsf) result->m_asmsf.push_back(tr(f)); for (auto & kv : m_map) result->m_map.insert(tr(kv.m_key), kv.m_value); for (unsigned l : m_fmls_lim) result->m_fmls_lim.push_back(l); for (unsigned a : m_asms_lim) result->m_asms_lim.push_back(a); for (unsigned h : m_fmls_head_lim) result->m_fmls_head_lim.push_back(h); for (expr* f : m_internalized_fmls) result->m_internalized_fmls.push_back(tr(f)); if (m_mcs.back()) result->m_mcs.push_back(m_mcs.back()->translate(tr)); if (m_sat_mc) result->m_sat_mc = dynamic_cast(m_sat_mc->translate(tr)); // copy m_bb_rewriter? result->m_internalized_converted = m_internalized_converted; return result; } void set_progress_callback(progress_callback * callback) override {} void display_weighted(std::ostream& out, unsigned sz, expr * const * assumptions, unsigned const* weights) { if (weights != nullptr) { for (unsigned i = 0; i < sz; ++i) m_weights.push_back(weights[i]); } init_preprocess(); m_solver.pop_to_base_level(); dep2asm_t dep2asm; expr_ref_vector asms(m); for (unsigned i = 0; i < sz; ++i) { expr_ref a(m.mk_fresh_const("s", m.mk_bool_sort()), m); expr_ref fml(m.mk_implies(a, assumptions[i]), m); assert_expr(fml); asms.push_back(a); } VERIFY(l_true == internalize_formulas()); VERIFY(l_true == internalize_assumptions(sz, asms.c_ptr(), dep2asm)); svector nweights; for (unsigned i = 0; i < m_asms.size(); ++i) { nweights.push_back((unsigned) m_weights[i]); } m_weights.reset(); m_solver.display_wcnf(out, m_asms.size(), m_asms.c_ptr(), nweights.c_ptr()); } bool is_literal(expr* e) const { return is_uninterp_const(e) || (m.is_not(e, e) && is_uninterp_const(e)); } 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; expr_ref_vector _assumptions(m); obj_map asm2fml; for (unsigned i = 0; i < sz; ++i) { if (!is_literal(assumptions[i])) { expr_ref a(m.mk_fresh_const("s", m.mk_bool_sort()), m); expr_ref fml(m.mk_eq(a, assumptions[i]), m); assert_expr(fml); _assumptions.push_back(a); asm2fml.insert(a, assumptions[i]); } else { _assumptions.push_back(assumptions[i]); asm2fml.insert(assumptions[i], assumptions[i]); } } TRACE("sat", tout << _assumptions << "\n";); dep2asm_t dep2asm; lbool r = internalize_formulas(); if (r != l_true) return r; r = internalize_assumptions(sz, _assumptions.c_ptr(), dep2asm); if (r != l_true) return r; init_reason_unknown(); m_internalized_converted = false; try { // IF_VERBOSE(0, m_solver.display(verbose_stream())); r = m_solver.check(m_asms.size(), m_asms.c_ptr()); } catch (z3_exception& ex) { IF_VERBOSE(10, verbose_stream() << "exception: " << ex.msg() << "\n";); r = l_undef; } switch (r) { case l_true: if (sz > 0) { check_assumptions(dep2asm); } break; case l_false: // TBD: expr_dependency core is not accounted for. if (!m_asms.empty()) { extract_core(dep2asm, asm2fml); } break; default: set_reason_unknown(m_solver.get_reason_unknown()); break; } return r; } void push() override { internalize_formulas(); m_solver.user_push(); ++m_num_scopes; m_mcs.push_back(m_mcs.back()); m_fmls_lim.push_back(m_fmls.size()); m_asms_lim.push_back(m_asmsf.size()); m_fmls_head_lim.push_back(m_fmls_head); if (m_bb_rewriter) m_bb_rewriter->push(); m_map.push(); } void pop(unsigned n) override { if (n > m_num_scopes) { // allow inc_sat_solver to n = m_num_scopes; // take over for another solver. } if (m_bb_rewriter) m_bb_rewriter->pop(n); m_inserted_const2bits.reset(); m_map.pop(n); SASSERT(n <= m_num_scopes); m_solver.user_pop(n); m_num_scopes -= n; // ? m_internalized_converted = false; while (n > 0) { m_mcs.pop_back(); m_fmls_head = m_fmls_head_lim.back(); m_fmls.resize(m_fmls_lim.back()); m_fmls_lim.pop_back(); m_fmls_head_lim.pop_back(); m_asmsf.resize(m_asms_lim.back()); m_asms_lim.pop_back(); --n; } } unsigned get_scope_level() const override { return m_num_scopes; } void assert_expr_core2(expr * t, expr * a) override { if (a) { m_asmsf.push_back(a); 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); } } 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 {} void collect_param_descrs(param_descrs & r) override { solver::collect_param_descrs(r); goal2sat::collect_param_descrs(r); sat::solver::collect_param_descrs(r); } void updt_params(params_ref const & p) override { m_params.append(p); sat_params p1(p); m_params.set_bool("keep_cardinality_constraints", p1.cardinality_solver()); m_params.set_sym("pb.solver", p1.pb_solver()); m_params.set_bool("xor_solver", p1.xor_solver()); m_solver.updt_params(m_params); m_solver.set_incremental(is_incremental() && !override_incremental()); } void collect_statistics(statistics & st) const override { if (m_preprocess) m_preprocess->collect_statistics(st); m_solver.collect_statistics(st); } void get_unsat_core(expr_ref_vector & r) override { 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; } proof * get_proof() override { return nullptr; } expr_ref_vector last_cube(bool is_sat) { expr_ref_vector result(m); result.push_back(is_sat ? m.mk_true() : m.mk_false()); return result; } expr_ref_vector cube(expr_ref_vector& vs, unsigned backtrack_level) override { if (!is_internalized()) { lbool r = internalize_formulas(); if (r != l_true) { IF_VERBOSE(0, verbose_stream() << "internalize produced " << r << "\n"); return expr_ref_vector(m); } } convert_internalized(); obj_hashtable _vs; for (expr* v : vs) _vs.insert(v); sat::bool_var_vector vars; for (auto& kv : m_map) { if (_vs.empty() || _vs.contains(kv.m_key)) vars.push_back(kv.m_value); } sat::literal_vector lits; lbool result = m_solver.cube(vars, lits, backtrack_level); expr_ref_vector fmls(m); expr_ref_vector lit2expr(m); lit2expr.resize(m_solver.num_vars() * 2); m_map.mk_inv(lit2expr); for (sat::literal l : lits) { fmls.push_back(lit2expr[l.index()].get()); } vs.reset(); for (sat::bool_var v : vars) { expr* x = lit2expr[sat::literal(v, false).index()].get(); if (x) { 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; } lbool get_consequences_core(expr_ref_vector const& assumptions, expr_ref_vector const& vars, expr_ref_vector& conseq) override { init_preprocess(); TRACE("sat", tout << assumptions << "\n" << vars << "\n";); sat::literal_vector asms; sat::bool_var_vector bvars; vector lconseq; dep2asm_t dep2asm; obj_map asm2fml; m_solver.pop_to_base_level(); lbool r = internalize_formulas(); if (r != l_true) return r; r = internalize_vars(vars, bvars); if (r != l_true) return r; r = internalize_assumptions(assumptions.size(), assumptions.c_ptr(), dep2asm); if (r != l_true) return r; r = m_solver.get_consequences(m_asms, bvars, lconseq); if (r == l_false) { if (!m_asms.empty()) { extract_core(dep2asm, asm2fml); } return r; } // build map from bound variables to // the consequences that cover them. u_map bool_var2conseq; for (unsigned i = 0; i < lconseq.size(); ++i) { TRACE("sat", tout << lconseq[i] << "\n";); bool_var2conseq.insert(lconseq[i][0].var(), i); } // extract original fixed variables u_map asm2dep; extract_asm2dep(dep2asm, asm2dep); for (auto v : vars) { expr_ref cons(m); if (extract_fixed_variable(dep2asm, asm2dep, v, bool_var2conseq, lconseq, cons)) { conseq.push_back(cons); } } return r; } lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes) override { sat::literal_vector ls; u_map lit2var; for (unsigned i = 0; i < vars.size(); ++i) { expr* e = vars[i]; bool neg = m.is_not(e, e); sat::bool_var v = m_map.to_bool_var(e); if (v != sat::null_bool_var) { sat::literal lit(v, neg); ls.push_back(lit); lit2var.insert(lit.index(), vars[i]); } } vector ls_mutexes; m_solver.find_mutexes(ls, ls_mutexes); for (sat::literal_vector const& ls_mutex : ls_mutexes) { expr_ref_vector mutex(m); for (sat::literal l : ls_mutex) { mutex.push_back(lit2var.find(l.index())); } mutexes.push_back(mutex); } return l_true; } void init_reason_unknown() { m_unknown = "no reason given"; } std::string reason_unknown() const override { return m_unknown; } void set_reason_unknown(char const* msg) override { m_unknown = msg; } void get_labels(svector & r) override { } unsigned get_num_assertions() const override { const_cast(this)->convert_internalized(); if (is_internalized() && m_internalized_converted) { return m_internalized_fmls.size(); } else { return m_fmls.size(); } } expr * get_assertion(unsigned idx) const override { if (is_internalized() && m_internalized_converted) { return m_internalized_fmls[idx]; } return m_fmls[idx]; } unsigned get_num_assumptions() const override { return m_asmsf.size(); } expr * get_assumption(unsigned idx) const override { return m_asmsf[idx]; } model_converter_ref get_model_converter() const override { const_cast(this)->convert_internalized(); if (m_cached_mc) return m_cached_mc; if (is_internalized() && m_internalized_converted) { m_sat_mc->flush_smc(m_solver, m_map); m_cached_mc = m_mcs.back(); m_cached_mc = concat(solver::get_model_converter().get(), m_cached_mc.get()); m_cached_mc = concat(m_cached_mc.get(), m_sat_mc.get()); return m_cached_mc; } else { return solver::get_model_converter(); } } void convert_internalized() { m_solver.pop_to_base_level(); if (!is_internalized() && m_fmls_head > 0) { internalize_formulas(); } if (!is_internalized() || m_internalized_converted) return; sat2goal s2g; m_cached_mc = nullptr; goal g(m, false, true, false); s2g(m_solver, m_map, m_params, g, m_sat_mc); m_internalized_fmls.reset(); g.get_formulas(m_internalized_fmls); TRACE("sat", m_solver.display(tout); tout << m_internalized_fmls << "\n";); m_internalized_converted = true; } void init_preprocess() { if (m_preprocess) { m_preprocess->reset(); } if (!m_bb_rewriter) { m_bb_rewriter = alloc(bit_blaster_rewriter, m, m_params); } params_ref simp2_p = m_params; simp2_p.set_bool("som", true); simp2_p.set_bool("pull_cheap_ite", true); simp2_p.set_bool("push_ite_bv", false); simp2_p.set_bool("local_ctx", true); simp2_p.set_uint("local_ctx_limit", 10000000); simp2_p.set_bool("flat", true); // required by som simp2_p.set_bool("hoist_mul", false); // required by som simp2_p.set_bool("elim_and", true); simp2_p.set_bool("blast_distinct", true); m_preprocess = and_then(mk_simplify_tactic(m), mk_propagate_values_tactic(m), //time consuming if done in inner loop: mk_solve_eqs_tactic(m, simp2_p), mk_card2bv_tactic(m, m_params), // updates model converter using_params(mk_simplify_tactic(m), simp2_p), mk_max_bv_sharing_tactic(m), mk_bit_blaster_tactic(m, m_bb_rewriter.get()), // updates model converter using_params(mk_simplify_tactic(m), simp2_p)); while (m_bb_rewriter->get_num_scopes() < m_num_scopes) { m_bb_rewriter->push(); } m_preprocess->reset(); } private: lbool internalize_goal(goal_ref& g, dep2asm_t& dep2asm, bool is_lemma) { m_pc.reset(); m_subgoals.reset(); init_preprocess(); SASSERT(g->models_enabled()); if (g->proofs_enabled()) { throw default_exception("generation of proof objects is not supported in this mode"); } SASSERT(!g->proofs_enabled()); TRACE("sat", m_solver.display(tout); g->display(tout);); try { 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";); TRACE("sat", tout << "exception: " << ex.msg() << "\n";); m_preprocess = nullptr; m_bb_rewriter = nullptr; return l_undef; } if (m_subgoals.size() != 1) { IF_VERBOSE(0, verbose_stream() << "size of subgoals is not 1, it is: " << m_subgoals.size() << "\n"); return l_undef; } g = m_subgoals[0]; expr_ref_vector atoms(m); m_pc = g->pc(); m_mcs.set(m_mcs.size()-1, concat(m_mcs.back(), g->mc())); TRACE("sat", g->display_with_dependencies(tout);); // ensure that if goal is already internalized, then import mc from m_solver. m_goal2sat(*g, m_params, m_solver, m_map, dep2asm, is_incremental(), is_lemma); m_goal2sat.get_interpreted_atoms(atoms); if (!m_sat_mc) m_sat_mc = alloc(sat2goal::mc, m); m_sat_mc->flush_smc(m_solver, m_map); if (!atoms.empty()) { std::stringstream strm; strm << "interpreted atoms sent to SAT solver " << atoms; TRACE("sat", tout << strm.str() << "\n";); IF_VERBOSE(1, verbose_stream() << strm.str() << "\n";); set_reason_unknown(strm.str().c_str()); return l_undef; } return l_true; } lbool internalize_assumptions(unsigned sz, expr* const* asms, dep2asm_t& dep2asm) { if (sz == 0 && get_num_assumptions() == 0) { m_asms.shrink(0); return l_true; } goal_ref g = alloc(goal, m, true, true); // models and cores are enabled. for (unsigned i = 0; i < sz; ++i) { g->assert_expr(asms[i], m.mk_leaf(asms[i])); } for (unsigned i = 0; i < get_num_assumptions(); ++i) { g->assert_expr(get_assumption(i), m.mk_leaf(get_assumption(i))); } lbool res = internalize_goal(g, dep2asm, false); if (res == l_true) { extract_assumptions(sz, asms, dep2asm); } return res; } lbool internalize_vars(expr_ref_vector const& vars, sat::bool_var_vector& bvars) { for (expr* v : vars) { internalize_var(v, bvars); } return l_true; } bool internalize_var(expr* v, sat::bool_var_vector& bvars) { obj_map const2bits; ptr_vector newbits; m_bb_rewriter->end_rewrite(const2bits, newbits); expr* bv; bv_util bvutil(m); bool internalized = false; if (is_uninterp_const(v) && m.is_bool(v)) { sat::bool_var b = m_map.to_bool_var(v); if (b != sat::null_bool_var) { bvars.push_back(b); internalized = true; } } else if (is_uninterp_const(v) && const2bits.find(to_app(v)->get_decl(), bv)) { SASSERT(bvutil.is_bv(bv)); app* abv = to_app(bv); internalized = true; for (expr* arg : *abv) { SASSERT(is_uninterp_const(arg)); sat::bool_var b = m_map.to_bool_var(arg); if (b == sat::null_bool_var) { internalized = false; } else { bvars.push_back(b); } } CTRACE("sat", internalized, tout << "var: " << bvars << "\n";); } else if (is_uninterp_const(v) && bvutil.is_bv(v)) { // variable does not occur in assertions, so is unconstrained. } CTRACE("sat", !internalized, tout << "unhandled variable " << mk_pp(v, m) << "\n";); return internalized; } bool extract_fixed_variable(dep2asm_t& dep2asm, u_map& asm2dep, expr* v, u_map const& bool_var2conseq, vector const& lconseq, expr_ref& conseq) { sat::bool_var_vector bvars; if (!internalize_var(v, bvars)) { return false; } sat::literal_vector value; sat::literal_set premises; for (sat::bool_var bv : bvars) { unsigned index; if (bool_var2conseq.find(bv, index)) { value.push_back(lconseq[index][0]); for (unsigned j = 1; j < lconseq[index].size(); ++j) { premises.insert(lconseq[index][j]); } } else { TRACE("sat", tout << "variable is not bound " << mk_pp(v, m) << "\n";); return false; } } expr_ref val(m); expr_ref_vector conj(m); internalize_value(value, v, val); while (!premises.empty()) { expr* e = nullptr; VERIFY(asm2dep.find(premises.pop().index(), e)); conj.push_back(e); } conseq = m.mk_implies(mk_and(conj), val); return true; } vector m_exps; void internalize_value(sat::literal_vector const& value, expr* v, expr_ref& val) { bv_util bvutil(m); if (is_uninterp_const(v) && m.is_bool(v)) { SASSERT(value.size() == 1); val = value[0].sign() ? m.mk_not(v) : v; } else if (is_uninterp_const(v) && bvutil.is_bv_sort(m.get_sort(v))) { SASSERT(value.size() == bvutil.get_bv_size(v)); if (m_exps.empty()) { m_exps.push_back(rational::one()); } while (m_exps.size() < value.size()) { m_exps.push_back(rational(2)*m_exps.back()); } rational r(0); for (unsigned i = 0; i < value.size(); ++i) { if (!value[i].sign()) { r += m_exps[i]; } } val = m.mk_eq(v, bvutil.mk_numeral(r, value.size())); } else { UNREACHABLE(); } } 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; } 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) { expr* fml = m_fmls.get(i); g->assert_expr(fml); } lbool res = internalize_goal(g, dep2asm, false); if (res != l_undef) { m_fmls_head = m_fmls.size(); } m_internalized_converted = false; return res; } void extract_assumptions(unsigned sz, expr* const* asms, dep2asm_t& dep2asm) { m_asms.reset(); unsigned j = 0; sat::literal lit; sat::literal_set seen; for (unsigned i = 0; i < sz; ++i) { if (dep2asm.find(asms[i], lit)) { SASSERT(lit.var() <= m_solver.num_vars()); if (!seen.contains(lit)) { m_asms.push_back(lit); seen.insert(lit); if (i != j && !m_weights.empty()) { m_weights[j] = m_weights[i]; } ++j; } } } for (unsigned i = 0; i < get_num_assumptions(); ++i) { if (dep2asm.find(get_assumption(i), lit)) { SASSERT(lit.var() <= m_solver.num_vars()); if (!seen.contains(lit)) { m_asms.push_back(lit); seen.insert(lit); } } } CTRACE("sat", dep2asm.size() != m_asms.size(), tout << dep2asm.size() << " vs " << m_asms.size() << "\n"; tout << m_asms << "\n"; for (auto const& kv : dep2asm) { tout << mk_pp(kv.m_key, m) << " " << kv.m_value << "\n"; }); SASSERT(dep2asm.size() == m_asms.size()); } void extract_asm2dep(dep2asm_t const& dep2asm, u_map& asm2dep) { for (auto const& kv : dep2asm) { asm2dep.insert(kv.m_value.index(), kv.m_key); } } void extract_core(dep2asm_t& dep2asm, obj_map const& asm2fml) { u_map asm2dep; extract_asm2dep(dep2asm, asm2dep); sat::literal_vector const& core = m_solver.get_core(); TRACE("sat", for (auto kv : dep2asm) { tout << mk_pp(kv.m_key, m) << " |-> " << sat::literal(kv.m_value) << "\n"; } tout << "asm2fml: "; for (auto kv : asm2fml) { tout << mk_pp(kv.m_key, m) << " |-> " << mk_pp(kv.m_value, m) << "\n"; } tout << "core: "; for (auto c : core) tout << c << " "; tout << "\n"; m_solver.display(tout); ); m_core.reset(); for (sat::literal c : core) { expr* e = nullptr; VERIFY(asm2dep.find(c.index(), e)); if (asm2fml.contains(e)) { e = asm2fml.find(e); } m_core.push_back(e); } } void check_assumptions(dep2asm_t& dep2asm) { sat::model const & ll_m = m_solver.get_model(); for (auto const& kv : dep2asm) { sat::literal lit = kv.m_value; if (sat::value_at(lit, ll_m) != l_true) { IF_VERBOSE(0, verbose_stream() << mk_pp(kv.m_key, m) << " does not evaluate to true\n"; verbose_stream() << m_asms << "\n"; m_solver.display_assignment(verbose_stream()); m_solver.display(verbose_stream());); throw default_exception("bad state"); } } } void get_model_core(model_ref & mdl) override { TRACE("sat", tout << "retrieve model " << (m_solver.model_is_current()?"present":"absent") << "\n";); if (!m_solver.model_is_current()) { mdl = nullptr; return; } TRACE("sat", m_solver.display_model(tout);); sat::model ll_m = m_solver.get_model(); if (m_sat_mc) { (*m_sat_mc)(ll_m); } mdl = alloc(model, m); 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; } switch (sat::value_at(v, ll_m)) { case l_true: mdl->register_decl(to_app(n)->get_decl(), m.mk_true()); break; case l_false: mdl->register_decl(to_app(n)->get_decl(), m.mk_false()); break; default: break; } } TRACE("sat", m_solver.display(tout);); if (m_sat_mc) { (*m_sat_mc)(mdl); } if (m_mcs.back()) { (*m_mcs.back())(mdl); } TRACE("sat", model_smt2_pp(tout, m, *mdl, 0);); 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); bool all_true = true; //unsigned i = 0; for (expr * f : m_fmls) { expr_ref tmp(m); eval(f, tmp); CTRACE("sat", !m.is_true(tmp), 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() << "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, if (m_mcs.back()) m_mcs.back()->display(verbose_stream() << "mc0\n")); IF_VERBOSE(0, for (auto const& kv : m_map) verbose_stream() << mk_pp(kv.m_key, m) << " |-> " << kv.m_value << "\n"); } else { IF_VERBOSE(1, verbose_stream() << "solution verified\n"); } } }; solver* mk_inc_sat_solver(ast_manager& m, params_ref const& p, bool incremental_mode) { return alloc(inc_sat_solver, m, p, incremental_mode); } void inc_sat_display(std::ostream& out, solver& _s, unsigned sz, expr*const* soft, rational const* _weights) { inc_sat_solver& s = dynamic_cast(_s); vector weights; for (unsigned i = 0; _weights && i < sz; ++i) { if (!_weights[i].is_unsigned()) { throw default_exception("Cannot display weights that are not integers"); } weights.push_back(_weights[i].get_unsigned()); } s.display_weighted(out, sz, soft, weights.c_ptr()); } tactic * mk_psat_tactic(ast_manager& m, params_ref const& p) { parallel_params pp(p); return pp.enable() ? mk_parallel_tactic(mk_inc_sat_solver(m, p, false), p) : mk_sat_tactic(m); } z3-z3-4.8.7/src/sat/sat_solver/inc_sat_solver.h000066400000000000000000000012531356505360400213520ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: inc_sat_solver.h Abstract: incremental solver based on SAT core. Author: Nikolaj Bjorner (nbjorner) 2014-7-30 Notes: --*/ #ifndef HS_INC_SAT_SOLVER_H_ #define HS_INC_SAT_SOLVER_H_ #include "solver/solver.h" class tactic; solver* mk_inc_sat_solver(ast_manager& m, params_ref const& p, bool incremental_mode = true); tactic* mk_psat_tactic(ast_manager& m, params_ref const& p); /* ADD_TACTIC('psat', '(try to) solve goal using a parallel SAT solver.', 'mk_psat_tactic(m, p)') */ void inc_sat_display(std::ostream& out, solver& s, unsigned sz, expr*const* soft, rational const* _weights); #endif z3-z3-4.8.7/src/sat/sat_solver_core.h000066400000000000000000000075761356505360400173660ustar00rootroot00000000000000/*++ 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 z3-z3-4.8.7/src/sat/sat_types.h000066400000000000000000000210651356505360400161750ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_types.h Abstract: Basic types used in the SAT solver Author: Leonardo de Moura (leonardo) 2011-05-21. Revision History: --*/ #ifndef SAT_TYPES_H_ #define SAT_TYPES_H_ #include "util/debug.h" #include "util/approx_set.h" #include "util/lbool.h" #include "util/z3_exception.h" #include "util/common_msgs.h" #include "util/vector.h" #include "util/uint_set.h" #include "util/stopwatch.h" #include class params_ref; class reslimit; class statistics; namespace sat { #define SAT_VB_LVL 10 // TODO: there is some duplication in the sat and smt namespaces. // The sat namespace should be the base. // I should cleanup the smt namespace later. /** \brief A boolean variable is just an integer. */ typedef unsigned bool_var; typedef svector bool_var_vector; const bool_var null_bool_var = UINT_MAX >> 1; /** \brief The literal b is represented by the value 2*b, and the literal (not b) by the value 2*b + 1 */ class literal { unsigned m_val; explicit literal(unsigned v):m_val(v) {} public: literal():m_val(null_bool_var << 1) { SASSERT(var() == null_bool_var && !sign()); } literal(bool_var v, bool _sign): m_val((v << 1) + static_cast(_sign)) { SASSERT(var() == v); SASSERT(sign() == _sign); } bool_var var() const { return m_val >> 1; } bool sign() const { return m_val & 1; } literal unsign() const { return literal(m_val & ~1); } unsigned index() const { return m_val; } void neg() { m_val = m_val ^ 1; } friend literal operator~(literal l) { return literal(l.m_val ^ 1); } unsigned to_uint() const { return m_val; } unsigned hash() const { return to_uint(); } friend literal to_literal(unsigned x); friend bool operator<(literal const & l1, literal const & l2); friend bool operator==(literal const & l1, literal const & l2); friend bool operator!=(literal const & l1, literal const & l2); }; const literal null_literal; struct literal_hash : obj_hash {}; inline literal to_literal(unsigned x) { return literal(x); } inline bool operator<(literal const & l1, literal const & l2) { return l1.m_val < l2.m_val; } inline bool operator==(literal const & l1, literal const & l2) { return l1.m_val == l2.m_val; } inline bool operator!=(literal const & l1, literal const & l2) { return l1.m_val != l2.m_val; } inline std::ostream & operator<<(std::ostream & out, literal l) { if (l == null_literal) out << "null"; else out << (l.sign() ? "-" : "") << l.var(); return out; } typedef svector literal_vector; typedef std::pair literal_pair; typedef size_t clause_offset; typedef size_t ext_constraint_idx; typedef size_t ext_justification_idx; struct literal2unsigned { unsigned operator()(literal l) const { return l.to_uint(); } }; typedef approx_set_tpl literal_approx_set; typedef approx_set_tpl var_approx_set; class solver; class parallel; class lookahead; class unit_walk; class clause; class clause_wrapper; class integrity_checker; typedef ptr_vector clause_vector; class solver_exception : public default_exception { public: solver_exception(char const * msg):default_exception(msg) {} }; typedef default_exception sat_param_exception; typedef svector model; inline void negate(literal_vector& ls) { for (unsigned i = 0; i < ls.size(); ++i) ls[i].neg(); } inline lbool value_at(bool_var v, model const & m) { return m[v]; } inline lbool value_at(literal l, model const & m) { lbool r = value_at(l.var(), m); return l.sign() ? ~r : r; } inline std::ostream & operator<<(std::ostream & out, model const & m) { bool first = true; for (bool_var v = 0; v < m.size(); v++) { if (m[v] == l_undef) continue; if (first) first = false; else out << " "; if (m[v] == l_true) out << v; else out << "-" << v; } return out; } typedef tracked_uint_set uint_set; typedef uint_set bool_var_set; class literal_set { uint_set m_set; public: literal_set(literal_vector const& v) { for (unsigned i = 0; i < v.size(); ++i) insert(v[i]); } literal_set() {} literal_vector to_vector() const { literal_vector result; iterator it = begin(), e = end(); for (; it != e; ++it) { result.push_back(*it); } return result; } literal_set& operator=(literal_vector const& v) { reset(); for (unsigned i = 0; i < v.size(); ++i) insert(v[i]); return *this; } literal_set& operator=(literal_set const& other) { if (this != &other) { m_set = other.m_set; } return *this; } void insert(literal l) { m_set.insert(l.index()); } void remove(literal l) { m_set.remove(l.index()); } literal pop() { return to_literal(m_set.erase()); } bool contains(literal l) const { return m_set.contains(l.index()); } bool empty() const { return m_set.empty(); } unsigned size() const { return m_set.size(); } void reset() { m_set.reset(); } void finalize() { m_set.finalize(); } class iterator { uint_set::iterator m_it; public: iterator(uint_set::iterator it):m_it(it) {} literal operator*() const { return to_literal(*m_it); } iterator& operator++() { ++m_it; return *this; } iterator operator++(int) { iterator tmp = *this; ++m_it; return tmp; } bool operator==(iterator const& it) const { return m_it == it.m_it; } bool operator!=(iterator const& it) const { return m_it != it.m_it; } }; iterator begin() const { return iterator(m_set.begin()); } iterator end() const { return iterator(m_set.end()); } literal_set& operator&=(literal_set const& other) { m_set &= other.m_set; return *this; } literal_set& operator|=(literal_set const& other) { m_set |= other.m_set; return *this; } }; struct mem_stat { }; inline std::ostream & operator<<(std::ostream & out, mem_stat const & m) { double mem = static_cast(memory::get_allocation_size())/static_cast(1024*1024); 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 { literal m_lit; dimacs_lit(literal l):m_lit(l) {} }; inline std::ostream & operator<<(std::ostream & out, dimacs_lit const & dl) { literal l = dl.m_lit; if (l.sign()) out << "-" << (l.var() + 1); else out << (l.var() + 1); return out; } struct mk_lits_pp { unsigned m_num; literal const * m_lits; mk_lits_pp(unsigned num, literal const * ls):m_num(num), m_lits(ls) {} }; inline std::ostream & operator<<(std::ostream & out, mk_lits_pp const & ls) { for (unsigned i = 0; i < ls.m_num; i++) { if (i > 0) out << " "; out << ls.m_lits[i]; } return out; } inline std::ostream & operator<<(std::ostream & out, literal_vector const & ls) { return out << mk_lits_pp(ls.size(), ls.c_ptr()); } class i_local_search { public: virtual ~i_local_search() {} virtual void add(solver const& s) = 0; virtual void updt_params(params_ref const& p) = 0; virtual void set_seed(unsigned s) = 0; virtual lbool check(unsigned sz, literal const* assumptions, parallel* par) = 0; virtual void reinit(solver& s) = 0; virtual unsigned num_non_binary_clauses() const = 0; virtual reslimit& rlimit() = 0; virtual model const& get_model() const = 0; virtual void collect_statistics(statistics& st) const = 0; virtual double get_priority(bool_var v) const { return 0; } }; }; #endif z3-z3-4.8.7/src/sat/sat_unit_walk.cpp000066400000000000000000000365131356505360400173650ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: sat_unit_walk.cpp 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 pending units as a multi-set. 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 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. Author: Nikolaj Bjorner (nbjorner) 2017-12-15. Revision History: 2018-11-5: change reinitialization to use local search with limited timeouts to find phase and ordering of variables. --*/ #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) { // IF_VERBOSE(0, verbose_stream() << "pop " << v << "\n"); 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() << "#vars: " << m_vars.size() << "\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(); s.pop_to_base_level(); for (unsigned v = 0; v < s.num_vars(); ++v) { if (!s.was_eliminated(v) && s.value(v) == l_undef) { add(v); } } IF_VERBOSE(0, verbose_stream() << "num vars " << m_vars.size() << "\n";); } 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_max_conflicts = 10000; m_flips = 0; } class scoped_set_unit_walk { solver& s; public: scoped_set_unit_walk(unit_walk* u, solver& s): s(s) { if (s.get_extension()) s.get_extension()->set_unit_walk(u); } ~scoped_set_unit_walk() { if (s.get_extension()) s.get_extension()->set_unit_walk(nullptr); } }; lbool unit_walk::operator()() { scoped_set_unit_walk _scoped_set(this, s); init_runs(); 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; } 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_luby_index = 0; m_restart_threshold = 1000; m_max_trail = 0; m_trail.reset(); m_decisions.reset(); m_phase.resize(s.num_vars()); 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); } 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_best_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 { return ls.get_priority(v) > ls.get_priority(w); } }; 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] = l_true == m_ls.cur_solution(v); } pqueue().rewind(); } void unit_walk::init_phase() { for (bool_var v : pqueue()) { m_phase[v] = s.m_phase[v]; } } 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 (s.get_extension()) s.get_extension()->set_unit_walk(this); init_runs(); init_phase(); } for (literal lit : m_trail) { s.m_assignment[lit.index()] = l_undef; s.m_assignment[(~lit).index()] = l_undef; } m_flips = 0; m_trail.reset(); 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(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) { ++s.m_stats.m_propagate; literal not_l = ~l; literal l1, l2; lbool val1, val2; bool keep; watch_list & wlist = s.get_wlist(l); watch_list::iterator it = wlist.begin(); watch_list::iterator it2 = it; watch_list::iterator end = wlist.end(); for (; it != end; ++it) { switch (it->get_kind()) { case watched::BINARY: l1 = it->get_literal(); switch (value(l1)) { case l_false: conflict_cleanup(it, it2, wlist); set_conflict(l,l1); return; case l_undef: assign(l1); break; case l_true: break; // skip } *it2 = *it; it2++; break; case watched::TERNARY: l1 = it->get_literal1(); l2 = it->get_literal2(); val1 = value(l1); val2 = value(l2); if (val1 == l_false && val2 == l_undef) { assign(l2); } else if (val1 == l_undef && val2 == l_false) { assign(l1); } else if (val1 == l_false && val2 == l_false) { conflict_cleanup(it, it2, wlist); set_conflict(l,l1,l2); return; } *it2 = *it; it2++; break; case watched::CLAUSE: { if (value(it->get_blocked_literal()) == l_true) { *it2 = *it; it2++; break; } clause_offset cls_off = it->get_clause_offset(); clause & c = s.get_clause(cls_off); if (c[0] == not_l) std::swap(c[0], c[1]); if (c[1] != not_l) { *it2 = *it; it2++; break; } if (value(c[0]) == l_true) { it2->set_clause(c[0], cls_off); it2++; break; } SASSERT(c[1] == not_l); literal * l_it = c.begin() + 2; literal * l_end = c.end(); for (; l_it != l_end; ++l_it) { if (value(*l_it) != l_false) { c[1] = *l_it; *l_it = not_l; s.get_wlist((~c[1]).index()).push_back(watched(c[0], cls_off)); goto end_clause_case; } } SASSERT(value(c[0]) == l_false || value(c[0]) == l_undef); if (value(c[0]) == l_false) { c.mark_used(); conflict_cleanup(it, it2, wlist); set_conflict(c); return; } else { *it2 = *it; it2++; assign(c[0]); } end_clause_case: break; } case watched::EXT_CONSTRAINT: SASSERT(s.get_extension()); keep = s.get_extension()->propagate(l, it->get_ext_constraint_idx()); if (inconsistent()) { if (!keep) { ++it; } set_conflict(l, l); conflict_cleanup(it, it2, wlist); return; } if (keep) { *it2 = *it; it2++; } break; default: UNREACHABLE(); break; } } wlist.set_end(it2); } void unit_walk::assign(literal lit) { VERIFY(value(lit) == l_undef); s.m_assignment[lit.index()] = l_true; s.m_assignment[(~lit).index()] = l_false; m_trail.push_back(lit); 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]; } void unit_walk::log_status() { 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";); } void unit_walk::set_conflict(literal l1, literal l2) { set_conflict(); } void unit_walk::set_conflict(literal l1, literal l2, literal l3) { set_conflict(); } void unit_walk::set_conflict(clause const& c) { set_conflict(); } void unit_walk::set_conflict() { m_inconsistent = true; } }; z3-z3-4.8.7/src/sat/sat_unit_walk.h000066400000000000000000000061731356505360400170310ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: sat_unit_walk.h Abstract: unit walk local search procedure. Author: Nikolaj Bjorner (nbjorner) 2017-12-15. Revision History: --*/ #ifndef SAT_UNIT_WALK_H_ #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; var_priority m_priorities; unsigned m_luby_index; unsigned m_restart_threshold; // settings unsigned m_max_conflicts; unsigned m_flips; unsigned m_max_trail; unsigned m_qhead; literal_vector m_trail; bool m_inconsistent; literal_vector m_decisions; unsigned m_conflict_offset; 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); void propagate(); void propagate(literal lit); 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); lbool operator()(); std::ostream& display(std::ostream& out) const; bool inconsistent() const { return m_inconsistent; } void set_conflict(); void assign(literal lit); }; }; #endif z3-z3-4.8.7/src/sat/sat_var_queue.h000066400000000000000000000034621356505360400170260ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_var_queue.h Abstract: SAT variable priority queue. Author: Leonardo de Moura (leonardo) 2011-05-21. Revision History: --*/ #ifndef SAT_VAR_QUEUE_H_ #define SAT_VAR_QUEUE_H_ #include "util/heap.h" #include "sat/sat_types.h" namespace sat { class var_queue { struct lt { svector & m_activity; lt(svector & act):m_activity(act) {} bool operator()(bool_var v1, bool_var v2) const { return m_activity[v1] > m_activity[v2]; } }; heap m_queue; public: var_queue(svector & act):m_queue(128, lt(act)) {} void activity_increased_eh(bool_var v) { if (m_queue.contains(v)) m_queue.decreased(v); } void activity_changed_eh(bool_var v, bool up) { if (m_queue.contains(v)) { if (up) m_queue.decreased(v); else m_queue.increased(v); } } void mk_var_eh(bool_var v) { m_queue.reserve(v+1); m_queue.insert(v); } void del_var_eh(bool_var v) { if (m_queue.contains(v)) m_queue.erase(v); } void unassign_var_eh(bool_var v) { if (!m_queue.contains(v)) m_queue.insert(v); } void reset() { m_queue.reset(); } bool empty() const { return m_queue.empty(); } bool_var next_var() { SASSERT(!empty()); return m_queue.erase_min(); } bool_var min_var() { SASSERT(!empty()); return m_queue.min_value(); } bool more_active(bool_var v1, bool_var v2) const { return m_queue.less_than(v1, v2); } }; }; #endif z3-z3-4.8.7/src/sat/sat_watched.cpp000066400000000000000000000105311356505360400167770ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_watched.cpp Abstract: Element of the SAT solver watchlist. Author: Leonardo de Moura (leonardo) 2011-05-24. Revision History: --*/ #include "sat/sat_watched.h" #include "sat/sat_clause.h" #include "sat/sat_extension.h" namespace sat { bool erase_clause_watch(watch_list & wlist, clause_offset c) { watch_list::iterator it = wlist.begin(); watch_list::iterator end = wlist.end(); for (; it != end; ++it) { if (it->is_clause() && it->get_clause_offset() == c) { watch_list::iterator it2 = it; ++it; for (; it != end; ++it, ++it2) { SASSERT(!((it->is_clause() && it->get_clause_offset() == c))); *it2 = *it; } wlist.set_end(it2); return true; } } return false; } watched* find_binary_watch(watch_list & wlist, literal l) { for (watched& w : wlist) { if (w.is_binary_clause() && w.get_literal() == l) return &w; } return nullptr; } watched const* find_binary_watch(watch_list const& wlist, literal l) { for (watched const& w : wlist) { if (w.is_binary_clause() && w.get_literal() == l) return &w; } return nullptr; } void erase_binary_watch(watch_list& wlist, literal l) { watch_list::iterator it = wlist.begin(), end = wlist.end(); watch_list::iterator it2 = it; bool found = false; for (; it != end; ++it) { if (it->is_binary_clause() && it->get_literal() == l && !found) { found = true; } else { *it2 = *it; ++it2; } } wlist.set_end(it2); VERIFY(found); } void erase_ternary_watch(watch_list& wlist, literal l1, literal l2) { watched w(l1, l2); watch_list::iterator it = wlist.begin(), end = wlist.end(); watch_list::iterator it2 = it; bool found = false; for (; it != end; ++it) { if (!found && w == *it) { found = true; } else { *it2 = *it; ++it2; } } wlist.set_end(it2); #if 0 VERIFY(found); for (watched const& w2 : wlist) { if (w2 == w) { std::cout << l1 << " " << l2 << "\n"; } //VERIFY(w2 != w); } #endif } void conflict_cleanup(watch_list::iterator it, watch_list::iterator it2, watch_list& wlist) { watch_list::iterator end = wlist.end(); for (; it != end; ++it, ++it2) *it2 = *it; wlist.set_end(it2); } 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) first = false; else out << " "; switch (w.get_kind()) { case watched::BINARY: out << w.get_literal(); if (w.is_learned()) out << "*"; break; case watched::TERNARY: out << "(" << w.get_literal1() << " " << w.get_literal2() << ")"; break; case watched::CLAUSE: out << "(" << w.get_blocked_literal() << " " << *(ca.get_clause(w.get_clause_offset())) << ")"; break; case watched::EXT_CONSTRAINT: if (ext) { ext->display_constraint(out, w.get_ext_constraint_idx()); } else { out << "ext: " << w.get_ext_constraint_idx(); } break; default: UNREACHABLE(); } } return out; } }; z3-z3-4.8.7/src/sat/sat_watched.h000066400000000000000000000135131356505360400164470ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_watched.h Abstract: Element of the SAT solver watchlist. Author: Leonardo de Moura (leonardo) 2011-05-21. Revision History: --*/ #ifndef SAT_WATCHED_H_ #define SAT_WATCHED_H_ #include "sat/sat_types.h" #include "util/vector.h" namespace sat { /** A watched element can be: 1) A literal: for watched binary clauses 2) A pair of literals: for watched ternary clauses 3) A pair (literal, clause-offset): for watched clauses, where the first element of the pair is a literal of the clause. 4) A external constraint-idx: for external constraints. For binary clauses: we use a bit to store whether the binary clause was learned or not. Remark: there are no clause objects for binary clauses. */ class extension; class watched { public: enum kind { BINARY = 0, TERNARY, CLAUSE, EXT_CONSTRAINT }; private: size_t m_val1; unsigned m_val2; public: watched(literal l, bool learned): m_val1(l.to_uint()), m_val2(static_cast(BINARY) + (static_cast(learned) << 2)) { SASSERT(is_binary_clause()); SASSERT(get_literal() == l); SASSERT(is_learned() == learned); SASSERT(learned || is_binary_non_learned_clause()); } watched(literal l1, literal l2) { SASSERT(l1 != l2); if (l1.index() > l2.index()) std::swap(l1, l2); m_val1 = l1.to_uint(); m_val2 = static_cast(TERNARY) + (l2.to_uint() << 2); SASSERT(is_ternary_clause()); SASSERT(get_literal1() == l1); SASSERT(get_literal2() == l2); } unsigned val2() const { return m_val2; } watched(literal blocked_lit, clause_offset cls_off): m_val1(cls_off), m_val2(static_cast(CLAUSE) + (blocked_lit.to_uint() << 2)) { SASSERT(is_clause()); SASSERT(get_blocked_literal() == blocked_lit); SASSERT(get_clause_offset() == cls_off); } explicit watched(ext_constraint_idx cnstr_idx): m_val1(cnstr_idx), m_val2(static_cast(EXT_CONSTRAINT)) { SASSERT(is_ext_constraint()); SASSERT(get_ext_constraint_idx() == cnstr_idx); } kind get_kind() const { return static_cast(m_val2 & 3); } bool is_binary_clause() const { return get_kind() == BINARY; } literal get_literal() const { SASSERT(is_binary_clause()); return to_literal(static_cast(m_val1)); } void set_literal(literal l) { SASSERT(is_binary_clause()); m_val1 = l.to_uint(); } bool is_learned() const { SASSERT(is_binary_clause()); return ((m_val2 >> 2) & 1) == 1; } bool is_binary_learned_clause() const { return is_binary_clause() && is_learned(); } bool is_binary_non_learned_clause() const { return is_binary_clause() && !is_learned(); } void set_learned(bool l) { if (l) m_val2 |= 4u; else m_val2 &= ~4u; SASSERT(is_learned() == l); } bool is_ternary_clause() const { return get_kind() == TERNARY; } literal get_literal1() const { SASSERT(is_ternary_clause()); return to_literal(static_cast(m_val1)); } literal get_literal2() const { SASSERT(is_ternary_clause()); return to_literal(m_val2 >> 2); } bool is_clause() const { return get_kind() == CLAUSE; } clause_offset get_clause_offset() const { SASSERT(is_clause()); return static_cast(m_val1); } literal get_blocked_literal() const { SASSERT(is_clause()); return to_literal(m_val2 >> 2); } void set_clause_offset(clause_offset c) { SASSERT(is_clause()); m_val1 = c; } void set_blocked_literal(literal l) { SASSERT(is_clause()); m_val2 = static_cast(CLAUSE) + (l.to_uint() << 2); } void set_clause(literal blocked_lit, clause_offset cls_off) { m_val1 = cls_off; m_val2 = static_cast(CLAUSE) + (blocked_lit.to_uint() << 2); } bool is_ext_constraint() const { return get_kind() == EXT_CONSTRAINT; } ext_constraint_idx get_ext_constraint_idx() const { SASSERT(is_ext_constraint()); return m_val1; } bool operator==(watched const & w) const { return m_val1 == w.m_val1 && m_val2 == w.m_val2; } bool operator!=(watched const & w) const { return !operator==(w); } }; static_assert(0 <= watched::BINARY && watched::BINARY <= 3, ""); static_assert(0 <= watched::TERNARY && watched::TERNARY <= 3, ""); static_assert(0 <= watched::CLAUSE && watched::CLAUSE <= 3, ""); static_assert(0 <= watched::EXT_CONSTRAINT && watched::EXT_CONSTRAINT <= 3, ""); struct watched_lt { bool operator()(watched const & w1, watched const & w2) const { if (w2.is_binary_clause()) return false; if (w1.is_binary_clause()) return true; if (w2.is_ternary_clause()) return false; if (w1.is_ternary_clause()) return true; return false; } }; typedef vector watch_list; watched* find_binary_watch(watch_list & wlist, literal l); watched const* find_binary_watch(watch_list const & wlist, literal l); bool erase_clause_watch(watch_list & wlist, clause_offset c); void erase_ternary_watch(watch_list & wlist, literal l1, literal l2); 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, extension* ext); void conflict_cleanup(watch_list::iterator it, watch_list::iterator it2, watch_list& wlist); }; #endif z3-z3-4.8.7/src/sat/tactic/000077500000000000000000000000001356505360400152545ustar00rootroot00000000000000z3-z3-4.8.7/src/sat/tactic/CMakeLists.txt000066400000000000000000000002731356505360400200160ustar00rootroot00000000000000z3_add_component(sat_tactic SOURCES atom2bool_var.cpp goal2sat.cpp sat_tactic.cpp COMPONENT_DEPENDENCIES sat tactic solver TACTIC_HEADERS sat_tactic.h ) z3-z3-4.8.7/src/sat/tactic/atom2bool_var.cpp000066400000000000000000000074721356505360400205400ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: atom2bool_var.cpp Abstract: The mapping between SAT boolean variables and atoms Author: Leonardo (leonardo) 2011-10-25 Notes: --*/ #include "util/ref_util.h" #include "ast/ast_smt2_pp.h" #include "tactic/goal.h" #include "sat/tactic/atom2bool_var.h" void atom2bool_var::mk_inv(expr_ref_vector & lit2expr) const { for (auto const& kv : m_mapping) { sat::literal l(static_cast(kv.m_value), false); lit2expr.set(l.index(), kv.m_key); l.neg(); lit2expr.set(l.index(), m().mk_not(kv.m_key)); } } void atom2bool_var::mk_var_inv(app_ref_vector & var2expr) const { for (auto const& kv : m_mapping) { var2expr.set(kv.m_value, to_app(kv.m_key)); } } sat::bool_var atom2bool_var::to_bool_var(expr * n) const { 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 { struct visitor { obj_hashtable & m_r; visitor(obj_hashtable & r):m_r(r) {} void operator()(var * n) {} void operator()(app * n) { if (is_uninterp_const(n)) m_r.insert(n); } void operator()(quantifier * n) {} }; ast_manager & m; expr_fast_mark2 fvisited; expr_fast_mark1 tvisited; ptr_vector todo; visitor proc; collect_boolean_interface_proc(ast_manager & _m, obj_hashtable & r): m(_m), proc(r) { } void process(expr * f) { if (fvisited.is_marked(f)) return; fvisited.mark(f); todo.push_back(f); while (!todo.empty()) { expr * t = todo.back(); todo.pop_back(); if (is_uninterp_const(t)) continue; if (is_app(t) && to_app(t)->get_family_id() == m.get_basic_family_id() && to_app(t)->get_num_args() > 0) { decl_kind k = to_app(t)->get_decl_kind(); if (k == OP_OR || k == OP_NOT || ((k == OP_EQ || k == OP_ITE) && m.is_bool(to_app(t)->get_arg(1)))) { unsigned num = to_app(t)->get_num_args(); for (unsigned i = 0; i < num; i++) { expr * arg = to_app(t)->get_arg(i); if (fvisited.is_marked(arg)) continue; fvisited.mark(arg); todo.push_back(arg); } } } else { quick_for_each_expr(proc, tvisited, t); } } } template void operator()(T const & g) { unsigned sz = g.size(); ptr_vector deps, all_deps; for (unsigned i = 0; i < sz; i++) { if (g.dep(i)) { deps.reset(); m.linearize(g.dep(i), deps); all_deps.append(deps); } } for (unsigned i = 0; i < all_deps.size(); i++) { quick_for_each_expr(proc, tvisited, all_deps[i]); } for (unsigned i = 0; i < sz; i++) { process(g.form(i)); } } void operator()(unsigned sz, expr * const * fs) { for (unsigned i = 0; i < sz; i++) process(fs[i]); } }; template void collect_boolean_interface_core(T const & s, obj_hashtable & r) { collect_boolean_interface_proc proc(s.m(), r); proc(s); } void collect_boolean_interface(goal const & g, obj_hashtable & r) { collect_boolean_interface_core(g, r); } void collect_boolean_interface(ast_manager & m, unsigned num, expr * const * fs, obj_hashtable & r) { collect_boolean_interface_proc proc(m, r); proc(num, fs); } z3-z3-4.8.7/src/sat/tactic/atom2bool_var.h000066400000000000000000000020231356505360400201700ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: atom2bool_var.h Abstract: The mapping between SAT boolean variables and atoms Author: Leonardo (leonardo) 2011-10-25 Notes: --*/ #ifndef ATOM2BOOL_VAR_H_ #define ATOM2BOOL_VAR_H_ #include "ast/expr2var.h" #include "sat/sat_types.h" /** \brief Mapping from atoms into SAT boolean variables. */ class atom2bool_var : public expr2var { public: atom2bool_var(ast_manager & m):expr2var(m) {} void insert(expr * n, sat::bool_var v) { expr2var::insert(n, v); } sat::bool_var to_bool_var(expr * n) const; void mk_inv(expr_ref_vector & lit2expr) const; void mk_var_inv(app_ref_vector & var2expr) const; // return true if the mapping contains uninterpreted atoms. bool interpreted_atoms() const { return expr2var::interpreted_vars(); } }; class goal; void collect_boolean_interface(goal const & g, obj_hashtable & r); void collect_boolean_interface(ast_manager & m, unsigned num, expr * const * fs, obj_hashtable & r); #endif z3-z3-4.8.7/src/sat/tactic/goal2sat.cpp000066400000000000000000001202771356505360400175050ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: goal2sat.cpp Abstract: "Compile" a goal into the SAT engine. Atoms are "abstracted" into boolean variables. The mapping between boolean variables and atoms can be used to convert back the state of the SAT engine into a goal. The idea is to support scenarios such as: 1) simplify, blast, convert into SAT, and solve 2) convert into SAT, apply SAT for a while, learn new units, and translate back into a goal. 3) convert into SAT, apply SAT preprocessor (failed literal propagation, resolution, etc) and translate back into a goal. 4) Convert boolean structure into SAT, convert atoms into another engine, combine engines using lazy combination, solve. Author: Leonardo (leonardo) 2011-10-26 Notes: --*/ #include "util/ref_util.h" #include "ast/ast_smt2_pp.h" #include "ast/ast_pp.h" #include "ast/ast_ll_pp.h" #include "ast/pb_decl_plugin.h" #include "ast/ast_util.h" #include "ast/for_each_expr.h" #include "sat/tactic/goal2sat.h" #include "sat/ba_solver.h" #include "model/model_evaluator.h" #include "model/model_v2_pp.h" #include "tactic/tactic.h" #include "tactic/generic_model_converter.h" #include struct goal2sat::imp { struct frame { app * m_t; unsigned m_root:1; unsigned m_sign:1; unsigned m_idx; frame(app * t, bool r, bool s, unsigned idx): m_t(t), m_root(r), m_sign(s), m_idx(idx) {} }; ast_manager & m; pb_util pb; sat::ba_solver* m_ext; svector m_frame_stack; svector m_result_stack; obj_map m_cache; obj_hashtable m_interface_vars; sat::solver_core & m_solver; atom2bool_var & m_map; dep2asm_map & m_dep2asm; sat::literal m_true; bool m_ite_extra; unsigned long long m_max_memory; expr_ref_vector m_trail; expr_ref_vector m_interpreted_atoms; bool m_default_external; bool m_xor_solver; bool m_is_lemma; 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), m_solver(s), m_map(map), m_dep2asm(dep2asm), m_trail(m), m_interpreted_atoms(m), m_default_external(default_external), m_is_lemma(false) { updt_params(p); 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) { std::string s0 = "operator " + s + " not supported, apply simplifier before invoking translator"; throw tactic_exception(std::move(s0)); } void mk_clause(sat::literal l) { TRACE("goal2sat", tout << "mk_clause: " << l << "\n";); 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.add_clause(l1, l2, m_is_lemma); } void mk_clause(sat::literal l1, sat::literal l2, sat::literal l3, bool is_lemma = false) { TRACE("goal2sat", tout << "mk_clause: " << l1 << " " << l2 << " " << l3 << "\n";); m_solver.add_clause(l1, l2, l3, m_is_lemma || 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.add_clause(num, lits, m_is_lemma); } sat::literal mk_true() { if (m_true == sat::null_literal) { // create fake variable to represent true; m_true = sat::literal(m_solver.add_var(false), false); mk_clause(m_true); // v is true } return m_true; } void convert_atom(expr * t, bool root, bool sign) { SASSERT(m.is_bool(t)); sat::literal l; sat::bool_var v = m_map.to_bool_var(t); if (v == sat::null_bool_var) { if (m.is_true(t)) { l = sign ? ~mk_true() : mk_true(); } else if (m.is_false(t)) { 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.add_var(ext); m_map.insert(t, v); l = sat::literal(v, sign); TRACE("sat", tout << "new_var: " << v << ": " << mk_bounded_pp(t, m, 2) << "\n";); if (ext && !is_uninterp_const(t)) { m_interpreted_atoms.push_back(t); } } } else { SASSERT(v != sat::null_bool_var); l = sat::literal(v, sign); m_solver.set_eliminated(v, false); } SASSERT(l != sat::null_literal); if (root) mk_clause(l); else m_result_stack.push_back(l); } bool convert_app(app* t, bool root, bool sign) { if (t->get_family_id() == pb.get_family_id()) { ensure_extension(); m_frame_stack.push_back(frame(to_app(t), root, sign, 0)); return false; } else { convert_atom(t, root, sign); return true; } } bool process_cached(app * t, bool root, bool sign) { sat::literal l; if (m_cache.find(t, l)) { if (sign) l.neg(); if (root) mk_clause(l); else m_result_stack.push_back(l); return true; } return false; } bool visit(expr * t, bool root, bool sign) { if (!is_app(t)) { convert_atom(t, root, sign); return true; } if (process_cached(to_app(t), root, sign)) return true; if (to_app(t)->get_family_id() != m.get_basic_family_id()) { return convert_app(to_app(t), root, sign); } switch (to_app(t)->get_decl_kind()) { case OP_NOT: case OP_OR: case OP_AND: m_frame_stack.push_back(frame(to_app(t), root, sign, 0)); return false; case OP_ITE: case OP_EQ: if (m.is_bool(to_app(t)->get_arg(1))) { m_frame_stack.push_back(frame(to_app(t), root, sign, 0)); return false; } else { convert_atom(t, root, sign); return true; } case OP_XOR: case OP_IMPLIES: case OP_DISTINCT: { TRACE("goal2sat_not_handled", tout << mk_ismt2_pp(t, m) << "\n";); std::ostringstream strm; strm << mk_ismt2_pp(t, m); throw_op_not_handled(strm.str()); } default: convert_atom(t, root, sign); return true; } } void convert_or(app * t, bool root, bool sign) { TRACE("goal2sat", tout << "convert_or:\n" << mk_bounded_pp(t, m, 2) << "\n";); unsigned num = t->get_num_args(); if (root) { SASSERT(num == m_result_stack.size()); if (sign) { // this case should not really happen. for (unsigned i = 0; i < num; i++) { sat::literal l = m_result_stack[i]; l.neg(); mk_clause(l); } } else { mk_clause(m_result_stack.size(), m_result_stack.c_ptr()); } m_result_stack.reset(); } else { SASSERT(num <= m_result_stack.size()); 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; for (unsigned i = 0; i < num; i++) { mk_clause(~lits[i], l); } m_result_stack.push_back(~l); lits = m_result_stack.end() - num - 1; // remark: mk_clause may perform destructive updated to lits. // I have to execute it after the binary mk_clause above. mk_clause(num+1, lits); unsigned old_sz = m_result_stack.size() - num - 1; m_result_stack.shrink(old_sz); if (sign) l.neg(); m_result_stack.push_back(l); } } void convert_and(app * t, bool root, bool sign) { TRACE("goal2sat", tout << "convert_and:\n" << mk_ismt2_pp(t, m) << "\n";); unsigned num = t->get_num_args(); if (root) { if (sign) { for (unsigned i = 0; i < num; ++i) { m_result_stack[i].neg(); } mk_clause(m_result_stack.size(), m_result_stack.c_ptr()); } else { for (unsigned i = 0; i < num; ++i) { mk_clause(m_result_stack[i]); } } m_result_stack.reset(); } else { SASSERT(num <= m_result_stack.size()); sat::bool_var k = m_solver.add_var(false); sat::literal l(k, false); m_cache.insert(t, l); // l => /\ lits sat::literal * lits = m_result_stack.end() - num; for (unsigned i = 0; i < num; i++) { mk_clause(~l, lits[i]); } // /\ lits => l for (unsigned i = 0; i < num; ++i) { m_result_stack[m_result_stack.size() - num + i].neg(); } m_result_stack.push_back(l); lits = m_result_stack.end() - num - 1; mk_clause(num+1, lits); unsigned old_sz = m_result_stack.size() - num - 1; m_result_stack.shrink(old_sz); if (sign) l.neg(); m_result_stack.push_back(l); TRACE("goal2sat", tout << m_result_stack << "\n";); } } void convert_ite(app * n, bool root, bool sign) { unsigned sz = m_result_stack.size(); SASSERT(sz >= 3); sat::literal c = m_result_stack[sz-3]; sat::literal t = m_result_stack[sz-2]; sat::literal e = m_result_stack[sz-1]; if (root) { SASSERT(sz == 3); if (sign) { mk_clause(~c, ~t); mk_clause(c, ~e); } else { mk_clause(~c, t); mk_clause(c, e); } m_result_stack.reset(); } else { sat::bool_var k = m_solver.add_var(false); sat::literal l(k, false); m_cache.insert(n, l); mk_clause(~l, ~c, t); mk_clause(~l, c, e); mk_clause(l, ~c, ~t); mk_clause(l, c, ~e); if (m_ite_extra) { mk_clause(~t, ~e, l, false); mk_clause(t, e, ~l, false); } m_result_stack.shrink(sz-3); if (sign) l.neg(); m_result_stack.push_back(l); } } void convert_iff2(app * t, bool root, bool sign) { TRACE("goal2sat", tout << "convert_iff " << root << " " << sign << "\n" << mk_bounded_pp(t, m, 2) << "\n";); unsigned sz = m_result_stack.size(); SASSERT(sz >= 2); sat::literal l1 = m_result_stack[sz-1]; sat::literal l2 = m_result_stack[sz-2]; if (root) { SASSERT(sz == 2); if (sign) { mk_clause(l1, l2); mk_clause(~l1, ~l2); } else { mk_clause(l1, ~l2); mk_clause(~l1, l2); } m_result_stack.reset(); } else { sat::bool_var k = m_solver.add_var(false); sat::literal l(k, false); m_cache.insert(t, l); mk_clause(~l, l1, ~l2); mk_clause(~l, ~l1, l2); mk_clause(l, l1, l2); mk_clause(l, ~l1, ~l2); m_result_stack.shrink(sz-2); if (sign) l.neg(); m_result_stack.push_back(l); } } void convert_iff(app * t, bool root, bool sign) { TRACE("goal2sat", tout << "convert_iff " << root << " " << sign << "\n" << mk_bounded_pp(t, m, 2) << "\n";); unsigned sz = m_result_stack.size(); unsigned num = get_num_args(t); SASSERT(sz >= num && num >= 2); if (num == 2) { convert_iff2(t, root, sign); return; } sat::literal_vector lits; 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 for (unsigned i = 1; i + 1 < lits.size(); ++i) { lits[i].neg(); } ensure_extension(); m_ext->add_xr(lits); sat::literal lit(v, sign); if (root) { m_result_stack.reset(); mk_clause(lit); } else { m_result_stack.shrink(sz - num); m_result_stack.push_back(lit); } } void convert_pb_args(unsigned num_args, sat::literal_vector& lits) { unsigned sz = m_result_stack.size(); for (unsigned i = 0; i < num_args; ++i) { sat::literal lit(m_result_stack[sz - num_args + i]); if (!m_solver.is_external(lit.var())) { m_solver.set_external(lit.var()); } lits.push_back(lit); } } typedef std::pair wliteral; void check_unsigned(rational const& c) { if (!c.is_unsigned()) { throw default_exception("unsigned coefficient expected"); } } void convert_to_wlits(app* t, sat::literal_vector const& lits, svector& wlits) { for (unsigned i = 0; i < lits.size(); ++i) { rational c = pb.get_coeff(t, i); check_unsigned(c); wlits.push_back(std::make_pair(c.get_unsigned(), lits[i])); } } void convert_pb_args(app* t, svector& wlits) { sat::literal_vector lits; convert_pb_args(t->get_num_args(), lits); convert_to_wlits(t, lits, wlits); } void push_result(bool root, sat::literal lit, unsigned num_args) { if (root) { m_result_stack.reset(); mk_clause(lit); } else { m_result_stack.shrink(m_result_stack.size() - num_args); m_result_stack.push_back(lit); } } void convert_pb_ge(app* t, bool root, bool sign) { rational k = pb.get_k(t); check_unsigned(k); svector wlits; convert_pb_args(t, wlits); if (root && m_solver.num_user_scopes() == 0) { m_result_stack.reset(); unsigned k1 = k.get_unsigned(); if (sign) { k1 = 1 - k1; for (wliteral& wl : wlits) { wl.second.neg(); k1 += wl.first; } } m_ext->add_pb_ge(sat::null_bool_var, wlits, k1); } else { 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";); push_result(root, lit, t->get_num_args()); } } void convert_pb_le(app* t, bool root, bool sign) { rational k = pb.get_k(t); k.neg(); svector wlits; convert_pb_args(t, wlits); for (wliteral& wl : wlits) { wl.second.neg(); k += rational(wl.first); } check_unsigned(k); if (root && m_solver.num_user_scopes() == 0) { m_result_stack.reset(); unsigned k1 = k.get_unsigned(); if (sign) { k1 = 1 - k1; for (wliteral& wl : wlits) { wl.second.neg(); k1 += wl.first; } } m_ext->add_pb_ge(sat::null_bool_var, wlits, k1); } else { 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";); push_result(root, lit, t->get_num_args()); } } void convert_pb_eq(app* t, bool root, bool sign) { rational k = pb.get_k(t); SASSERT(k.is_unsigned()); 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.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) { wl.second.neg(); k += rational(wl.first); } check_unsigned(k); m_ext->add_pb_ge(v2, wlits, k.get_unsigned()); if (base_assert) { m_result_stack.reset(); } else { sat::literal l1(v1, false), l2(v2, false); sat::bool_var v = m_solver.add_var(false); sat::literal l(v, false); mk_clause(~l, l1); mk_clause(~l, l2); mk_clause(~l1, ~l2, l); m_cache.insert(t, l); if (sign) l.neg(); push_result(root, l, t->get_num_args()); } } void convert_at_least_k(app* t, rational const& k, bool root, bool sign) { SASSERT(k.is_unsigned()); sat::literal_vector lits; convert_pb_args(t->get_num_args(), lits); unsigned k2 = k.get_unsigned(); if (root && m_solver.num_user_scopes() == 0) { m_result_stack.reset(); if (sign) { for (sat::literal& l : lits) l.neg(); k2 = lits.size() + 1 - k2; } m_ext->add_at_least(sat::null_bool_var, lits, k2); } else { 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); if (sign) lit.neg(); TRACE("goal2sat", tout << "root: " << root << " lit: " << lit << "\n";); push_result(root, lit, t->get_num_args()); } } void convert_at_most_k(app* t, rational const& k, bool root, bool sign) { SASSERT(k.is_unsigned()); sat::literal_vector lits; convert_pb_args(t->get_num_args(), lits); for (sat::literal& l : lits) { l.neg(); } unsigned k2 = lits.size() - k.get_unsigned(); if (root && m_solver.num_user_scopes() == 0) { m_result_stack.reset(); if (sign) { for (sat::literal& l : lits) l.neg(); k2 = lits.size() + 1 - k2; } m_ext->add_at_least(sat::null_bool_var, lits, k2); } else { sat::bool_var v = m_solver.add_var(true); sat::literal lit(v, false); m_ext->add_at_least(v, lits, k2); m_cache.insert(t, lit); if (sign) lit.neg(); push_result(root, lit, t->get_num_args()); } } void convert_eq_k(app* t, rational const& k, bool root, bool sign) { 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.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(); } m_ext->add_at_least(v2, lits, lits.size() - k.get_unsigned()); if (root && !sign) { m_result_stack.reset(); } else { sat::literal l1(v1, false), l2(v2, false); sat::bool_var v = m_solver.add_var(false); sat::literal l(v, false); mk_clause(~l, l1); mk_clause(~l, l2); mk_clause(~l1, ~l2, l); m_cache.insert(t, l); if (sign) l.neg(); push_result(root, l, t->get_num_args()); } } void ensure_extension() { if (!m_ext) { sat::extension* ext = m_solver.get_extension(); if (ext) { m_ext = dynamic_cast(ext); SASSERT(m_ext); } if (!m_ext) { m_ext = alloc(sat::ba_solver); m_solver.set_extension(m_ext); } } } void convert(app * t, bool root, bool sign) { if (t->get_family_id() == m.get_basic_family_id()) { switch (to_app(t)->get_decl_kind()) { case OP_OR: convert_or(t, root, sign); break; case OP_AND: convert_and(t, root, sign); break; case OP_ITE: convert_ite(t, root, sign); break; case OP_EQ: convert_iff(t, root, sign); break; default: UNREACHABLE(); } } else if (t->get_family_id() == pb.get_family_id()) { ensure_extension(); rational k; switch (t->get_decl_kind()) { case OP_AT_MOST_K: k = pb.get_k(t); convert_at_most_k(t, k, root, sign); break; case OP_AT_LEAST_K: k = pb.get_k(t); convert_at_least_k(t, k, root, sign); break; case OP_PB_LE: if (pb.has_unit_coefficients(t)) { k = pb.get_k(t); convert_at_most_k(t, k, root, sign); } else { convert_pb_le(t, root, sign); } break; case OP_PB_GE: if (pb.has_unit_coefficients(t)) { k = pb.get_k(t); convert_at_least_k(t, k, root, sign); } else { convert_pb_ge(t, root, sign); } break; case OP_PB_EQ: if (pb.has_unit_coefficients(t)) { k = pb.get_k(t); convert_eq_k(t, k, root, sign); } else { convert_pb_eq(t, root, sign); } break; default: UNREACHABLE(); } } else { UNREACHABLE(); } } unsigned get_num_args(app* t) { if (m.is_iff(t) && m_xor_solver) { unsigned n = 2; while (m.is_iff(t->get_arg(1))) { ++n; t = to_app(t->get_arg(1)); } return n; } else { return t->get_num_args(); } } expr* get_arg(app* t, unsigned idx) { if (m.is_iff(t) && m_xor_solver) { while (idx >= 1) { SASSERT(m.is_iff(t)); t = to_app(t->get_arg(1)); --idx; } if (m.is_iff(t)) { return t->get_arg(idx); } else { return t; } } else { return t->get_arg(idx); } } void process(expr * n) { //SASSERT(m_result_stack.empty()); TRACE("goal2sat", tout << "converting: " << mk_bounded_pp(n, m, 2) << "\n";); if (visit(n, true, false)) { SASSERT(m_result_stack.empty()); return; } while (!m_frame_stack.empty()) { loop: if (m.canceled()) throw tactic_exception(m.limit().get_cancel_msg()); if (memory::get_allocation_size() > m_max_memory) throw tactic_exception(TACTIC_MAX_MEMORY_MSG); frame & fr = m_frame_stack.back(); app * t = fr.m_t; bool root = fr.m_root; bool sign = fr.m_sign; TRACE("goal2sat_bug", tout << "result stack\n"; tout << mk_ismt2_pp(t, m) << " root: " << root << " sign: " << sign << "\n"; tout << m_result_stack << "\n";); if (fr.m_idx == 0 && process_cached(t, root, sign)) { m_frame_stack.pop_back(); continue; } if (m.is_not(t)) { m_frame_stack.pop_back(); visit(t->get_arg(0), root, !sign); continue; } unsigned num = get_num_args(t); while (fr.m_idx < num) { expr * arg = get_arg(t, fr.m_idx); fr.m_idx++; if (!visit(arg, false, false)) goto loop; } TRACE("goal2sat_bug", tout << "converting\n"; tout << mk_ismt2_pp(t, m) << " root: " << root << " sign: " << sign << "\n"; tout << m_result_stack << "\n";); convert(t, root, sign); m_frame_stack.pop_back(); } CTRACE("goal2sat", !m_result_stack.empty(), tout << m_result_stack << "\n";); SASSERT(m_result_stack.empty()); } void insert_dep(expr* dep0, expr* dep, bool sign) { SASSERT(sign || dep0 == dep); // !sign || (not dep0) == dep. SASSERT(!sign || m.is_not(dep0)); expr_ref new_dep(m), fml(m); if (is_uninterp_const(dep)) { new_dep = dep; } else { new_dep = m.mk_fresh_const("dep", m.mk_bool_sort()); m_trail.push_back(new_dep); m_interface_vars.insert(new_dep); fml = m.mk_iff(new_dep, dep); process(fml); } convert_atom(new_dep, false, false); sat::literal lit = m_result_stack.back(); m_dep2asm.insert(dep0, sign?~lit:lit); m_result_stack.pop_back(); } void operator()(goal const & g) { m_interface_vars.reset(); collect_boolean_interface(g, m_interface_vars); unsigned size = g.size(); expr_ref f(m), d_new(m); ptr_vector deps; expr_ref_vector fmls(m); for (unsigned idx = 0; idx < size; idx++) { f = g.form(idx); // Add assumptions. if (g.dep(idx)) { deps.reset(); fmls.reset(); m.linearize(g.dep(idx), deps); fmls.push_back(f); for (expr * d : deps) { expr * d1 = d; SASSERT(m.is_bool(d)); bool sign = m.is_not(d, d1); insert_dep(d, d1, sign); if (d == f) { goto skip_dep; } if (sign) { d_new = d1; } else { d_new = m.mk_not(d); } fmls.push_back(d_new); } f = m.mk_or(fmls.size(), fmls.c_ptr()); } TRACE("goal2sat", tout << mk_bounded_pp(f, m, 2) << "\n";); process(f); skip_dep: ; } } void operator()(unsigned sz, expr * const * fs) { m_interface_vars.reset(); collect_boolean_interface(m, sz, fs, m_interface_vars); for (unsigned i = 0; i < sz; i++) process(fs[i]); } }; struct unsupported_bool_proc { struct found {}; ast_manager & m; unsupported_bool_proc(ast_manager & _m):m(_m) {} void operator()(var *) {} void operator()(quantifier *) {} void operator()(app * n) { if (n->get_family_id() == m.get_basic_family_id()) { switch (n->get_decl_kind()) { case OP_XOR: case OP_IMPLIES: case OP_DISTINCT: throw found(); default: break; } } } }; /** \brief Return true if s contains an unsupported Boolean operator. goal_rewriter (with the following configuration) can be used to eliminate unsupported operators. :elim-and true :blast-distinct true */ bool goal2sat::has_unsupported_bool(goal const & g) { return test(g); } goal2sat::goal2sat():m_imp(nullptr), m_interpreted_atoms(nullptr) { } void goal2sat::collect_param_descrs(param_descrs & r) { insert_max_memory(r); r.insert("ite_extra", CPK_BOOL, "(default: true) add redundant clauses (that improve unit propagation) when encoding if-then-else formulas"); } struct goal2sat::scoped_set_imp { goal2sat * m_owner; scoped_set_imp(goal2sat * o, goal2sat::imp * i):m_owner(o) { m_owner->m_imp = i; } ~scoped_set_imp() { m_owner->m_imp = nullptr; } }; 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); proc(g); dealloc(m_interpreted_atoms); m_interpreted_atoms = alloc(expr_ref_vector, g.m()); m_interpreted_atoms->append(proc.m_interpreted_atoms); } void goal2sat::get_interpreted_atoms(expr_ref_vector& atoms) { if (m_interpreted_atoms) { atoms.append(*m_interpreted_atoms); } } sat2goal::mc::mc(ast_manager& m): m(m), m_var2expr(m) {} 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); } void sat2goal::mc::flush_gmc() { sat::literal_vector updates; m_smc.expand(updates); if (!m_gmc) m_gmc = alloc(generic_model_converter, m, "sat2goal"); // now gmc owns the model converter sat::literal_vector clause; expr_ref_vector tail(m); expr_ref def(m); for (unsigned i = 0; i < updates.size(); ++i) { sat::literal l = updates[i]; if (l == sat::null_literal) { sat::literal lit0 = clause[0]; for (unsigned i = 1; i < clause.size(); ++i) { tail.push_back(lit2expr(~clause[i])); } def = m.mk_or(lit2expr(lit0), mk_and(tail)); if (lit0.sign()) { lit0.neg(); def = m.mk_not(def); } m_gmc->add(lit2expr(lit0), def); clause.reset(); tail.reset(); } // short circuit for equivalences: else if (clause.empty() && tail.empty() && i + 5 < updates.size() && updates[i] == ~updates[i + 3] && updates[i + 1] == ~updates[i + 4] && updates[i + 2] == sat::null_literal && updates[i + 5] == sat::null_literal) { sat::literal r = ~updates[i+1]; if (l.sign()) { l.neg(); r.neg(); } m_gmc->add(lit2expr(l), lit2expr(r)); i += 5; } else { clause.push_back(l); } } } model_converter* sat2goal::mc::translate(ast_translation& translator) { mc* result = alloc(mc, translator.to()); result->m_smc.copy(m_smc); result->m_gmc = m_gmc ? dynamic_cast(m_gmc->translate(translator)) : nullptr; for (app* e : m_var2expr) { result->m_var2expr.push_back(translator(e)); } return result; } void sat2goal::mc::set_env(ast_pp_util* visitor) { flush_gmc(); if (m_gmc) m_gmc->set_env(visitor); } void sat2goal::mc::display(std::ostream& out) { flush_gmc(); if (m_gmc) m_gmc->display(out); } void sat2goal::mc::get_units(obj_map& units) { flush_gmc(); if (m_gmc) m_gmc->get_units(units); } void sat2goal::mc::operator()(sat::model& md) { m_smc(md); } void sat2goal::mc::operator()(model_ref & md) { // apply externalized model converter if (m_gmc) (*m_gmc)(md); TRACE("sat_mc", tout << "after sat_mc\n"; model_v2_pp(tout, *md);); } void sat2goal::mc::operator()(expr_ref& fml) { flush_gmc(); if (m_gmc) (*m_gmc)(fml); } void sat2goal::mc::insert(sat::bool_var v, app * atom, bool aux) { SASSERT(!m_var2expr.get(v, nullptr)); m_var2expr.reserve(v + 1); m_var2expr.set(v, atom); if (aux) { SASSERT(is_uninterp_const(atom)); SASSERT(m.is_bool(atom)); if (!m_gmc) m_gmc = alloc(generic_model_converter, m, "sat2goal"); m_gmc->hide(atom->get_decl()); } } expr_ref sat2goal::mc::lit2expr(sat::literal l) { if (!m_var2expr.get(l.var())) { app* aux = m.mk_fresh_const(nullptr, m.mk_bool_sort()); m_var2expr.set(l.var(), aux); if (!m_gmc) m_gmc = alloc(generic_model_converter, m, "sat2goal"); m_gmc->hide(aux->get_decl()); } VERIFY(m_var2expr.get(l.var())); expr_ref result(m_var2expr.get(l.var()), m); if (l.sign()) { result = m.mk_not(result); } return result; } struct sat2goal::imp { typedef mc sat_model_converter; ast_manager & m; expr_ref_vector m_lit2expr; unsigned long long m_max_memory; bool m_learned; imp(ast_manager & _m, params_ref const & p):m(_m), m_lit2expr(m) { updt_params(p); } void updt_params(params_ref const & p) { m_learned = p.get_bool("learned", false); m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); } void checkpoint() { if (m.canceled()) throw tactic_exception(m.limit().get_cancel_msg()); if (memory::get_allocation_size() > m_max_memory) throw tactic_exception(TACTIC_MAX_MEMORY_MSG); } expr * lit2expr(ref& mc, sat::literal l) { if (!m_lit2expr.get(l.index())) { SASSERT(m_lit2expr.get((~l).index()) == 0); app* aux = mc ? mc->var2expr(l.var()) : nullptr; if (!aux) { aux = m.mk_fresh_const(nullptr, m.mk_bool_sort()); if (mc) { mc->insert(l.var(), aux, true); } } sat::literal lit(l.var(), false); m_lit2expr.set(lit.index(), aux); m_lit2expr.set((~lit).index(), m.mk_not(aux)); } return m_lit2expr.get(l.index()); } void assert_pb(ref& mc, goal& r, sat::ba_solver::pb const& p) { pb_util pb(m); ptr_buffer lits; vector coeffs; for (auto const& wl : p) { lits.push_back(lit2expr(mc, wl.second)); coeffs.push_back(rational(wl.first)); } rational k(p.k()); expr_ref fml(pb.mk_ge(p.size(), coeffs.c_ptr(), lits.c_ptr(), k), m); if (p.lit() != sat::null_literal) { fml = m.mk_eq(lit2expr(mc, p.lit()), fml); } r.assert_expr(fml); } void assert_card(ref& mc, goal& r, sat::ba_solver::card const& c) { pb_util pb(m); ptr_buffer lits; for (sat::literal l : c) { lits.push_back(lit2expr(mc, l)); } expr_ref fml(pb.mk_at_least_k(c.size(), lits.c_ptr(), c.k()), m); if (c.lit() != sat::null_literal) { fml = m.mk_eq(lit2expr(mc, c.lit()), fml); } r.assert_expr(fml); } void assert_xor(ref& mc, goal & r, sat::ba_solver::xr const& x) { ptr_buffer lits; for (sat::literal l : x) { lits.push_back(lit2expr(mc, l)); } expr_ref fml(m.mk_xor(x.size(), lits.c_ptr()), m); if (x.lit() != sat::null_literal) { fml = m.mk_eq(lit2expr(mc, x.lit()), fml); } r.assert_expr(fml); } 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() <= small_lbd) { for (sat::literal l : c) { lits.push_back(lit2expr(mc, l)); } r.assert_expr(m.mk_or(lits.size(), lits.c_ptr())); } } } sat::ba_solver* get_ba_solver(sat::solver_core const& s) { return dynamic_cast(s.get_extension()); } 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; } if (r.models_enabled() && !mc) { mc = alloc(sat_model_converter, m); } if (mc) mc->flush_smc(s, map); m_lit2expr.resize(s.num_vars() * 2); map.mk_inv(m_lit2expr); // collect units unsigned trail_sz = s.init_trail_size(); for (unsigned i = 0; i < trail_sz; ++i) { checkpoint(); r.assert_expr(lit2expr(mc, s.trail_literal(i))); } // collect binary clauses svector bin_clauses; 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))); } // collect clauses assert_clauses(mc, s, s.clauses(), r, true); sat::ba_solver* ext = get_ba_solver(s); if (ext) { for (auto* c : ext->constraints()) { switch (c->tag()) { case sat::ba_solver::card_t: assert_card(mc, r, c->to_card()); break; case sat::ba_solver::pb_t: assert_pb(mc, r, c->to_pb()); break; case sat::ba_solver::xr_t: assert_xor(mc, r, c->to_xr()); break; } } } } void add_clause(ref& mc, sat::literal_vector const& lits, expr_ref_vector& lemmas) { expr_ref_vector lemma(m); for (sat::literal l : lits) { expr* e = lit2expr(mc, l); if (!e) return; lemma.push_back(e); } lemmas.push_back(mk_or(lemma)); } void add_clause(ref& mc, sat::clause const& c, expr_ref_vector& lemmas) { expr_ref_vector lemma(m); for (sat::literal l : c) { expr* e = lit2expr(mc, l); if (!e) return; lemma.push_back(e); } lemmas.push_back(mk_or(lemma)); } }; sat2goal::sat2goal():m_imp(nullptr) { } void sat2goal::collect_param_descrs(param_descrs & r) { insert_max_memory(r); r.insert("learned", CPK_BOOL, "(default: false) collect also learned clauses."); } struct sat2goal::scoped_set_imp { sat2goal * m_owner; scoped_set_imp(sat2goal * o, sat2goal::imp * i):m_owner(o) { m_owner->m_imp = i; } ~scoped_set_imp() { m_owner->m_imp = nullptr; } }; 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); proc(t, m, g, mc); } z3-z3-4.8.7/src/sat/tactic/goal2sat.h000066400000000000000000000073741356505360400171540ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: goal2sat.h Abstract: "Compile" a goal into the SAT engine. Atoms are "abstracted" into boolean variables. The mapping between boolean variables and atoms can be used to convert back the state of the SAT engine into a goal. The idea is to support scenarios such as: 1) simplify, blast, convert into SAT, and solve 2) convert into SAT, apply SAT for a while, learn new units, and translate back into a goal. 3) convert into SAT, apply SAT preprocessor (failed literal propagation, resolution, etc) and translate back into a goal. 4) Convert boolean structure into SAT, convert atoms into another engine, combine engines using lazy combination, solve. Author: Leonardo (leonardo) 2011-10-26 Notes: --*/ #ifndef GOAL2SAT_H_ #define GOAL2SAT_H_ #include "tactic/goal.h" #include "sat/sat_solver.h" #include "tactic/model_converter.h" #include "tactic/generic_model_converter.h" #include "sat/tactic/atom2bool_var.h" class goal2sat { struct imp; imp * m_imp; struct scoped_set_imp; expr_ref_vector* m_interpreted_atoms; public: goal2sat(); ~goal2sat() { dealloc(m_interpreted_atoms); } typedef obj_map dep2asm_map; static void collect_param_descrs(param_descrs & r); static bool has_unsupported_bool(goal const & s); /** \brief "Compile" the goal into the given sat solver. Store a mapping from atoms to boolean variables into m. \remark m doesn't need to be empty. the definitions there are reused. \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_core & t, atom2bool_var & m, dep2asm_map& dep2asm, bool default_external = false, bool is_lemma = false); void get_interpreted_atoms(expr_ref_vector& atoms); }; class sat2goal { struct imp; imp * m_imp; struct scoped_set_imp; public: class mc : public model_converter { ast_manager& m; sat::model_converter m_smc; generic_model_converter_ref m_gmc; app_ref_vector m_var2expr; // flushes from m_smc to m_gmc; void flush_gmc(); public: mc(ast_manager& m); ~mc() override {} // flush model converter from SAT solver to this structure. void flush_smc(sat::solver_core& s, atom2bool_var const& map); void operator()(sat::model& m); void operator()(model_ref& md) override; void operator()(expr_ref& fml) override; model_converter* translate(ast_translation& translator) override; void set_env(ast_pp_util* visitor) override; void display(std::ostream& out) override; void get_units(obj_map& units) override; app* var2expr(sat::bool_var v) const { return m_var2expr.get(v, nullptr); } expr_ref lit2expr(sat::literal l); void insert(sat::bool_var v, app * atom, bool aux); }; sat2goal(); static void collect_param_descrs(param_descrs & r); /** \brief Translate the state of the SAT engine back into a goal. The SAT solver may use variables that are not in \c m. The translator creates fresh boolean AST variables for them. They are stored in fvars. \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_core & t, atom2bool_var const & m, params_ref const & p, goal & s, ref & mc); }; #endif z3-z3-4.8.7/src/sat/tactic/sat_tactic.cpp000066400000000000000000000172021356505360400201000ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_tactic.cpp Abstract: Tactic for using the SAT solver and its preprocessing capabilities. Author: Leonardo (leonardo) 2011-10-25 Notes: --*/ #include "ast/ast_pp.h" #include "model/model_v2_pp.h" #include "tactic/tactical.h" #include "sat/tactic/goal2sat.h" #include "sat/sat_solver.h" #include "sat/sat_params.hpp" class sat_tactic : public tactic { struct imp { ast_manager & m; goal2sat m_goal2sat; sat2goal m_sat2goal; scoped_ptr m_solver; params_ref m_params; imp(ast_manager & _m, params_ref const & p): m(_m), m_solver(alloc(sat::solver, p, m.limit())), m_params(p) { SASSERT(!m.proofs_enabled()); updt_params(p); } void operator()(goal_ref const & g, goal_ref_buffer & result) { fail_if_proof_generation("sat", g); bool produce_models = g->models_enabled(); bool produce_core = g->unsat_core_enabled(); TRACE("before_sat_solver", g->display(tout);); g->elim_redundancies(); atom2bool_var map(m); obj_map dep2asm; sat::literal_vector assumptions; 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)) tout << mk_ismt2_pp(kv.m_key, m) << "\n"; }); 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);); dep2assumptions(dep2asm, assumptions); 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(); u_map asm2dep; mk_asm2dep(dep2asm, asm2dep); for (unsigned i = 0; i < ucore.size(); ++i) { sat::literal lit = ucore[i]; expr* dep = asm2dep.find(lit.index()); lcore = m.mk_join(lcore, m.mk_leaf(dep)); } } g->assert_expr(m.mk_false(), nullptr, lcore); } else if (r == l_true && !map.interpreted_atoms()) { // register model if (produce_models) { model_ref md = alloc(model, m); 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; sat::bool_var v = kv.m_value; TRACE("sat_tactic", tout << "extracting value of " << mk_ismt2_pp(n, m) << "\nvar: " << v << "\n";); switch (sat::value_at(v, ll_m)) { case l_true: md->register_decl(to_app(n)->get_decl(), m.mk_true()); break; case l_false: md->register_decl(to_app(n)->get_decl(), m.mk_false()); break; default: break; } } TRACE("sat_tactic", model_v2_pp(tout, *md);); g->add(model2model_converter(md.get())); } } else { // get simplified problem. #if 0 IF_VERBOSE(TACTIC_VERBOSITY_LVL, verbose_stream() << "\"formula constrains interpreted atoms, recovering formula from sat solver...\"\n";); #endif m_solver->pop_to_base_level(); ref mc; m_sat2goal(*m_solver, map, m_params, *(g.get()), mc); g->add(mc.get()); } g->inc_depth(); result.push_back(g.get()); } void dep2assumptions(obj_map& dep2asm, sat::literal_vector& assumptions) { for (auto const& kv : dep2asm) { assumptions.push_back(kv.m_value); } } void mk_asm2dep(obj_map& dep2asm, u_map& lit2asm) { for (auto const& kv : dep2asm) { 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->updt_params(m_owner->m_params); } ~scoped_set_imp() { m_owner->m_imp = nullptr; } }; imp * m_imp; params_ref m_params; statistics m_stats; 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 { return alloc(sat_tactic, m, m_params); } ~sat_tactic() override { SASSERT(m_imp == 0); } 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 { goal2sat::collect_param_descrs(r); sat2goal::collect_param_descrs(r); sat::solver::collect_param_descrs(r); } void operator()(goal_ref const & g, goal_ref_buffer & result) override { imp proc(g->m(), m_params); scoped_set_imp set(this, &proc); try { proc(g, result); proc.m_solver->collect_statistics(m_stats); } catch (sat::solver_exception & ex) { proc.m_solver->collect_statistics(m_stats); throw tactic_exception(ex.msg()); } catch (z3_exception& ex) { (void)ex; TRACE("sat", tout << ex.msg() << "\n";); throw; } TRACE("sat_stats", m_stats.display_smt2(tout);); } void cleanup() override { SASSERT(m_imp == 0); } void collect_statistics(statistics & st) const override { st.copy(m_stats); } void reset_statistics() override { m_stats.reset(); } protected: }; tactic * mk_sat_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(sat_tactic, m, p)); } tactic * mk_sat_preprocessor_tactic(ast_manager & m, params_ref const & p) { params_ref p_aux; p_aux.set_uint("max_conflicts", 0); tactic * t = clean(using_params(mk_sat_tactic(m, p), p_aux)); t->updt_params(p); return t; } z3-z3-4.8.7/src/sat/tactic/sat_tactic.h000066400000000000000000000014521356505360400175450ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_tactic.cpp Abstract: Tactic for using the SAT solver and its preprocessing capabilities. Author: Leonardo (leonardo) 2011-10-26 Notes: --*/ #ifndef SAT_TACTIC_H_ #define SAT_TACTIC_H_ #include "util/params.h" class ast_manager; class tactic; tactic * mk_sat_tactic(ast_manager & m, params_ref const & p = params_ref()); tactic * mk_sat_preprocessor_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC('sat', '(try to) solve goal using a SAT solver.', 'mk_sat_tactic(m, p)') ADD_TACTIC('sat-preprocess', 'Apply SAT solver preprocessing procedures (bounded resolution, Boolean constant propagation, 2-SAT, subsumption, subsumption resolution).', 'mk_sat_preprocessor_tactic(m, p)') */ #endif z3-z3-4.8.7/src/shell/000077500000000000000000000000001356505360400143255ustar00rootroot00000000000000z3-z3-4.8.7/src/shell/CMakeLists.txt000066400000000000000000000037711356505360400170750ustar00rootroot00000000000000set (shell_object_files "") # FIXME: z3 should really link against libz3 and not the # individual components. Several things prevent us from # doing this # * The api_dll component in libz3 shouldn't be used the # the z3 executable. # * The z3 executable uses symbols that are hidden in libz3 # We are only using these dependencies to enforce a build # order. We don't use this list for actual linking. set(shell_deps api extra_cmds opt sat) z3_expand_dependencies(shell_expanded_deps ${shell_deps}) get_property(Z3_LIBZ3_COMPONENTS_LIST GLOBAL PROPERTY Z3_LIBZ3_COMPONENTS) foreach (component ${Z3_LIBZ3_COMPONENTS_LIST}) if (NOT ("${component}" STREQUAL "api_dll")) # We don't use the api_dll component in the Z3 executable list(APPEND shell_object_files $) endif() endforeach() add_executable(shell datalog_frontend.cpp dimacs_frontend.cpp "${CMAKE_CURRENT_BINARY_DIR}/gparams_register_modules.cpp" "${CMAKE_CURRENT_BINARY_DIR}/install_tactic.cpp" main.cpp "${CMAKE_CURRENT_BINARY_DIR}/mem_initializer.cpp" opt_frontend.cpp smtlib_frontend.cpp z3_log_frontend.cpp lp_frontend.cpp # FIXME: shell should really link against libz3 but it can't due to requiring # use of some hidden symbols. Also libz3 has the ``api_dll`` component which # we don't want (I think). ${shell_object_files} ) z3_add_install_tactic_rule(${shell_deps}) z3_add_memory_initializer_rule(${shell_deps}) z3_add_gparams_register_modules_rule(${shell_deps}) set_target_properties(shell PROPERTIES OUTPUT_NAME z3) target_compile_definitions(shell PRIVATE ${Z3_COMPONENT_CXX_DEFINES}) target_compile_options(shell PRIVATE ${Z3_COMPONENT_CXX_FLAGS}) target_include_directories(shell PRIVATE ${Z3_COMPONENT_EXTRA_INCLUDE_DIRS}) target_link_libraries(shell PRIVATE ${Z3_DEPENDENT_LIBS}) z3_add_component_dependencies_to_target(shell ${shell_expanded_deps}) z3_append_linker_flag_list_to_target(shell ${Z3_DEPENDENT_EXTRA_CXX_LINK_FLAGS}) install(TARGETS shell RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" ) z3-z3-4.8.7/src/shell/datalog_frontend.cpp000066400000000000000000000164641356505360400203560ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: datalog_frontend.cpp Abstract: Author: Leonardo de Moura (leonardo) 2010-05-18. Revision History: --*/ #include #include #include #include #include "util/stopwatch.h" #include "smt/params/smt_params.h" #include "ast/arith_decl_plugin.h" #include "muz/rel/dl_compiler.h" #include "muz/transforms/dl_mk_filter_rules.h" #include "muz/rel/dl_finite_product_relation.h" #include "muz/base/dl_context.h" #include "muz/rel/rel_context.h" #include "muz/fp/dl_register_engine.h" #include "muz/fp/datalog_parser.h" #include "shell/datalog_frontend.h" #include "util/timeout.h" static stopwatch g_overall_time; static stopwatch g_piece_timer; static unsigned t_parsing = 0; static datalog::context * g_ctx = nullptr; static datalog::rule_set * g_orig_rules; static datalog::instruction_block * g_code; static datalog::execution_context * g_ectx; static std::mutex *display_stats_mux = new std::mutex; static void display_statistics( std::ostream& out, datalog::context& ctx, datalog::rule_set& orig_rules, datalog::instruction_block& code, datalog::execution_context& ex_ctx, bool verbose ) { std::lock_guard lock(*display_stats_mux); g_piece_timer.stop(); unsigned t_other = static_cast(g_piece_timer.get_seconds()*1000); g_overall_time.stop(); code.process_all_costs(); { params_ref p; p.set_bool("output_profile", true); p.set_uint("profile_milliseconds_threshold", 100); ctx.updt_params(p); IF_VERBOSE(2, out << "--------------\n"; out << "original rules\n"; orig_rules.display(out); out << "---------------\n"; out << "generated rules\n"; ctx.display_rules(out); out << "--------------\n"; out << "instructions \n"; code.display(ex_ctx, out); out << "--------------\n"; out << "big relations \n"; ex_ctx.report_big_relations(1000, out);); } IF_VERBOSE(2, out << "--------------\n"; out << "relation sizes\n"; ctx.get_rel_context()->get_rmanager().display_relation_sizes(out);); if (verbose) { out << "--------------\n"; out << "rules\n"; ctx.display_rules(out); } out << "Time: " << static_cast(g_overall_time.get_seconds()*1000) << "ms\n"; out << "Parsing: " << t_parsing << "ms, other: " << t_other << "ms\n"; } static void display_statistics() { if (g_ctx) { display_statistics(std::cout, *g_ctx, *g_orig_rules, *g_code, *g_ectx, true); } } static void on_timeout() { display_statistics(); exit(0); } static void STD_CALL on_ctrl_c(int) { signal (SIGINT, SIG_DFL); display_statistics(); raise(SIGINT); } unsigned read_datalog(char const * file) { IF_VERBOSE(1, verbose_stream() << "Z3 Datalog Engine\n";); smt_params s_params; ast_manager m; datalog::register_engine re; g_overall_time.start(); register_on_timeout_proc(on_timeout); signal(SIGINT, on_ctrl_c); params_ref params; params.set_sym("engine", symbol("datalog")); datalog::context ctx(m, re, s_params, params); datalog::relation_manager & rmgr = ctx.get_rel_context()->get_rmanager(); datalog::relation_plugin & inner_plg = *rmgr.get_relation_plugin(symbol("tr_hashtable")); rmgr.register_plugin(alloc(datalog::finite_product_relation_plugin, inner_plg, rmgr)); g_piece_timer.reset(); g_piece_timer.start(); bool wpa_benchmark = datalog::is_directory(std::string(file)); if (wpa_benchmark) { scoped_ptr parser = datalog::wpa_parser::create(ctx, m); if (!parser->parse_directory(file)) { std::cerr << "ERROR: failed to parse file\n"; return 1; } } else { scoped_ptr parser = datalog::parser::create(ctx, m); if (!parser->parse_file(file)) { std::cerr << "ERROR: failed to parse file\n"; return 1; } } g_piece_timer.stop(); t_parsing = static_cast(g_piece_timer.get_seconds()*1000); IF_VERBOSE(1, verbose_stream() << "parsing finished\n";); IF_VERBOSE(1, verbose_stream() << "running saturation...\n";); g_piece_timer.reset(); g_piece_timer.start(); //all rules were added ctx.close(); TRACE("dl_compiler", ctx.display(tout);); datalog::rule_set original_rules(ctx.get_rules()); datalog::instruction_block rules_code; datalog::instruction_block termination_code; datalog::execution_context ex_ctx(ctx); IF_VERBOSE(10, original_rules.display_deps(verbose_stream());); g_ctx = &ctx; g_orig_rules = &original_rules; g_code = &rules_code; g_ectx = &ex_ctx; try { g_piece_timer.reset(); g_piece_timer.start(); bool early_termination; unsigned timeout = ctx.initial_restart_timeout(); if (timeout == 0) { timeout = UINT_MAX; } do { ctx.get_rel_context()->transform_rules(); datalog::compiler::compile(ctx, ctx.get_rules(), rules_code, termination_code); TRACE("dl_compiler", rules_code.display(ex_ctx, tout);); rules_code.make_annotations(ex_ctx); ex_ctx.set_timelimit(timeout); early_termination = !rules_code.perform(ex_ctx); if(early_termination) { IF_VERBOSE(10, ex_ctx.report_big_relations(1000, verbose_stream());); if (memory::above_high_watermark()) { throw out_of_memory_error(); } } ex_ctx.reset_timelimit(); TRUSTME( termination_code.perform(ex_ctx) ); ctx.saturation_was_run(); if (early_termination) { IF_VERBOSE(1, verbose_stream() << "restarting saturation\n";); uint64_t new_timeout = static_cast(timeout)*ctx.initial_restart_timeout(); if(new_timeout>UINT_MAX) { timeout=UINT_MAX; } else { timeout=static_cast(new_timeout); } rules_code.process_all_costs(); rules_code.reset(); termination_code.reset(); ex_ctx.reset(); ctx.reopen(); ctx.replace_rules(original_rules); ctx.close(); } } while (early_termination); TRACE("dl_compiler", ctx.display(tout); rules_code.display(ex_ctx, tout);); if (ctx.output_tuples()) { ctx.get_rel_context()->display_output_facts(ctx.get_rules(), std::cout); } display_statistics( std::cout, ctx, original_rules, rules_code, ex_ctx, false); } catch (const out_of_memory_error &) { std::cout << "\n\nOUT OF MEMORY!\n\n"; display_statistics( std::cout, ctx, original_rules, rules_code, ex_ctx, true); return ERR_MEMOUT; } return 0; } z3-z3-4.8.7/src/shell/datalog_frontend.h000066400000000000000000000005071356505360400200120ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: datalog_frontend.h Abstract: Author: Leonardo de Moura (leonardo) 2010-05-18. Revision History: --*/ #ifndef DATALOG_FRONTEND_H_ #define DATALOG_FRONTEND_H_ unsigned read_datalog(char const * file); #endif /* DATALOG_FRONTEND_H_ */ z3-z3-4.8.7/src/shell/dimacs_frontend.cpp000066400000000000000000000205121356505360400201700ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: dimacs_frontend.cpp Abstract: Frontend for reading dimacs input files Author: Leonardo de Moura (leonardo) 2011-07-26. Revision History: --*/ #include #include #include #include "util/timeout.h" #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(); 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() { display_statistics(); exit(0); } static void STD_CALL on_ctrl_c(int) { signal (SIGINT, SIG_DFL); display_statistics(); raise(SIGINT); } static void display_model(sat::solver const & s) { sat::model const & m = s.get_model(); for (unsigned i = 1; i < m.size(); i++) { switch (m[i]) { case l_false: std::cout << "-" << i << " "; break; case l_undef: break; case l_true: std::cout << i << " "; break; } } std::cout << "\n"; } static void display_core(sat::solver const& s, vector const& tracking_clauses) { std::cout << "core\n"; sat::literal_vector const& c = s.get_core(); for (unsigned i = 0; i < c.size(); ++i) { sat::literal_vector const& cls = tracking_clauses[c[i].var()]; for (unsigned j = 0; j < cls.size(); ++j) { std::cout << cls[j] << " "; } std::cout << "\n"; } } static void track_clause(sat::solver& dst, sat::literal_vector& lits, sat::literal_vector& assumptions, vector& tracking_clauses) { sat::literal lit = sat::literal(dst.mk_var(true, false), false); tracking_clauses.set(lit.var(), lits); lits.push_back(~lit); dst.mk_clause(lits.size(), lits.c_ptr()); assumptions.push_back(lit); } static void track_clauses(sat::solver const& src, sat::solver& dst, sat::literal_vector& assumptions, vector& tracking_clauses) { for (sat::bool_var v = 0; v < src.num_vars(); ++v) { dst.mk_var(false, true); } sat::literal_vector lits; sat::literal lit; sat::clause * const * it = src.begin_clauses(); sat::clause * const * end = src.end_clauses(); svector bin_clauses; 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) { if (src.value(v) != l_undef) { bool sign = src.value(v) == l_false; lits.reset(); lits.push_back(sat::literal(v, sign)); track_clause(dst, lits, assumptions, tracking_clauses); } } for (; it != end; ++it) { lits.reset(); sat::clause& cls = *(*it); lits.append(static_cast(cls.end()-cls.begin()), cls.begin()); track_clause(dst, lits, assumptions, tracking_clauses); } for (unsigned i = 0; i < bin_clauses.size(); ++i) { lits.reset(); lits.push_back(bin_clauses[i].first); lits.push_back(bin_clauses[i].second); track_clause(dst, lits, assumptions, tracking_clauses); } } void verify_solution(char const * file_name) { params_ref p = gparams::get_module("sat"); p.set_bool("produce_models", true); reslimit limit; sat::solver solver(p, limit); std::ifstream in(file_name); if (in.bad() || in.fail()) { std::cerr << "(error \"failed to open file '" << file_name << "'\")" << std::endl; exit(ERR_OPEN_FILE); } parse_dimacs(in, std::cerr, solver); sat::model const & m = g_solver->get_model(); for (unsigned i = 1; i < m.size(); i++) { sat::literal lit(i, false); switch (m[i]) { case l_false: lit.neg(); break; case l_undef: break; case l_true: break; } solver.mk_clause(1, &lit); } lbool r = solver.check(); switch (r) { case l_false: std::cout << "model checking failed\n"; break; case l_true: std::cout << "model validated\n"; break; default: std::cout << "inconclusive model\n"; break; } } 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) { std::ifstream in(file_name); if (in.bad() || in.fail()) { std::cerr << "(error \"failed to open file '" << file_name << "'\")" << std::endl; exit(ERR_OPEN_FILE); } parse_dimacs(in, std::cerr, solver); } else { parse_dimacs(std::cin, std::cerr, solver); } IF_VERBOSE(20, solver.display_status(verbose_stream());); lbool r; vector tracking_clauses; 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 && gparams::get_ref().get_bool("model_validate", false)) verify_solution(file_name); display_model(*g_solver); break; case l_undef: std::cout << "unknown\n"; break; case l_false: std::cout << "unsat\n"; if (p.get_bool("dimacs.core", false)) { display_core(*g_solver, tracking_clauses); } break; } display_statistics(); return 0; } z3-z3-4.8.7/src/shell/dimacs_frontend.h000066400000000000000000000005121356505360400176330ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: dimacs_frontend.h Abstract: Author: Leonardo de Moura (leonardo) 2011-07-26. Revision History: --*/ #ifndef DIMACS_FRONTEND_H_ #define DIMACS_FRONTEND_H_ unsigned read_dimacs(char const * benchmark_file); #endif /* DIMACS_FRONTEND_H_ */ z3-z3-4.8.7/src/shell/lp_frontend.cpp000066400000000000000000000056031356505360400173470ustar00rootroot00000000000000/*++ Copyright (c) 2016 Microsoft Corporation Author: Lev Nachmanson 2016-10-27 --*/ #include "util/lp/lp_params.hpp" #include "util/lp/lp_settings.h" #include "util/lp/mps_reader.h" #include "util/timeout.h" #include "util/cancel_eh.h" #include "util/scoped_timer.h" #include "util/rlimit.h" #include "util/gparams.h" #include namespace { static std::mutex *display_stats_mux = new std::mutex; static lp::lp_solver* g_solver = nullptr; static void display_statistics() { std::lock_guard lock(*display_stats_mux); if (g_solver && g_solver->settings().print_statistics) { // TBD display relevant information about statistics } } static void STD_CALL on_ctrl_c(int) { signal (SIGINT, SIG_DFL); display_statistics(); raise(SIGINT); } static void on_timeout() { display_statistics(); exit(0); } struct front_end_resource_limit : public lp::lp_resource_limit { reslimit& m_reslim; front_end_resource_limit(reslimit& lim): m_reslim(lim) {} bool get_cancel_flag() override { return !m_reslim.inc(); } }; void run_solver(lp_params & params, char const * mps_file_name) { reslimit rlim; unsigned timeout = gparams::get_ref().get_uint("timeout", 0); unsigned rlimit = gparams::get_ref().get_uint("rlimit", 0); front_end_resource_limit lp_limit(rlim); scoped_rlimit _rlimit(rlim, rlimit); cancel_eh eh(rlim); scoped_timer timer(timeout, &eh); std::string fn(mps_file_name); lp::mps_reader reader(fn); reader.set_message_stream(&std::cout); // can be redirected reader.read(); if (!reader.is_ok()) { std::cerr << "cannot process " << mps_file_name << std::endl; return; } lp::lp_solver * solver = reader.create_solver(false); // false - to create the primal solver solver->settings().set_resource_limit(lp_limit); g_solver = solver; if (params.min()) { solver->flip_costs(); } solver->settings().set_message_ostream(&std::cout); solver->settings().report_frequency = params.rep_freq(); solver->settings().print_statistics = params.print_stats(); solver->settings().simplex_strategy() = lp:: simplex_strategy_enum::lu; solver->find_maximal_solution(); *(solver->settings().get_message_ostream()) << "status is " << lp_status_to_string(solver->get_status()) << std::endl; if (solver->get_status() == lp::lp_status::OPTIMAL) { if (params.min()) { solver->flip_costs(); } solver->print_model(std::cout); } display_statistics(); g_solver = nullptr; delete solver; } } unsigned read_mps_file(char const * mps_file_name) { signal(SIGINT, on_ctrl_c); register_on_timeout_proc(on_timeout); lp_params p; param_descrs r; p.collect_param_descrs(r); run_solver(p, mps_file_name); return 0; } z3-z3-4.8.7/src/shell/lp_frontend.h000066400000000000000000000002421356505360400170060ustar00rootroot00000000000000/* Copyright (c) 2013 Microsoft Corporation. All rights reserved. Author: Lev Nachmanson */ #pragma once unsigned read_mps_file(char const * mps_file_name); z3-z3-4.8.7/src/shell/main.cpp000066400000000000000000000341131356505360400157570ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: main.cpp Abstract: Z3 command line tool. Author: Leonardo de Moura (leonardo) 2006-10-10. Nikolaj Bjorner (nbjorner) Revision History: --*/ #include #include "util/memory_manager.h" #include "util/trace.h" #include "util/debug.h" #include "util/util.h" #include "ast/pp.h" #include "shell/smtlib_frontend.h" #include "shell/z3_log_frontend.h" #include "util/warning.h" #include "util/z3_version.h" #include "shell/dimacs_frontend.h" #include "shell/datalog_frontend.h" #include "shell/opt_frontend.h" #include "util/timeout.h" #include "util/z3_exception.h" #include "util/error_codes.h" #include "util/file_path.h" #include "util/gparams.h" #include "util/env_params.h" #include "util/file_path.h" #include "shell/lp_frontend.h" #if defined( _WINDOWS ) && defined( __MINGW32__ ) && ( defined( __GNUG__ ) || defined( __clang__ ) ) #include #endif typedef enum { IN_UNSPECIFIED, IN_SMTLIB_2, IN_DATALOG, IN_DIMACS, IN_WCNF, IN_OPB, IN_LP, IN_Z3_LOG, IN_MPS } input_kind; static std::string g_aux_input_file; static char const * g_input_file = nullptr; static bool g_standard_input = false; static input_kind g_input_kind = IN_UNSPECIFIED; bool g_display_statistics = false; static bool g_display_istatistics = false; static void error(const char * msg) { std::cerr << "Error: " << msg << "\n"; std::cerr << "For usage information: z3 -h\n"; exit(ERR_CMD_LINE); } #define STRINGIZE(x) #x #define STRINGIZE_VALUE_OF(x) STRINGIZE(x) void display_usage() { std::cout << "Z3 [version " << Z3_MAJOR_VERSION << "." << Z3_MINOR_VERSION << "." << Z3_BUILD_NUMBER; std::cout << " - "; #if defined(__LP64__) || defined(_WIN64) std::cout << "64"; #else std::cout << "32"; #endif std::cout << " bit"; #ifdef Z3GITHASH std::cout << " - build hashcode " << STRINGIZE_VALUE_OF(Z3GITHASH); #endif std::cout << "]. (C) Copyright 2006-2016 Microsoft Corp.\n"; std::cout << "Usage: z3 [options] [-file:]file\n"; std::cout << "\nInput format:\n"; std::cout << " -smt2 use parser for SMT 2 input format.\n"; std::cout << " -dl use parser for Datalog input format.\n"; std::cout << " -dimacs use parser for DIMACS input format.\n"; std::cout << " -wcnf use parser for Weighted CNF DIMACS input format.\n"; std::cout << " -opb use parser for PB optimization input format.\n"; std::cout << " -lp use parser for a modest subset of CPLEX LP input format.\n"; std::cout << " -log use parser for Z3 log input format.\n"; std::cout << " -in read formula from standard input.\n"; std::cout << "\nMiscellaneous:\n"; std::cout << " -h, -? prints this message.\n"; std::cout << " -version prints version number of Z3.\n"; std::cout << " -v:level be verbose, where is the verbosity level.\n"; std::cout << " -nw disable warning messages.\n"; std::cout << " -p display Z3 global (and module) parameters.\n"; std::cout << " -pd display Z3 global (and module) parameter descriptions.\n"; std::cout << " -pm:name display Z3 module ('name') parameters.\n"; std::cout << " -pp:name display Z3 parameter description, if 'name' is not provided, then all module names are listed.\n"; std::cout << " --" << " all remaining arguments are assumed to be part of the input file name. This option allows Z3 to read files with strange names such as: -foo.smt2.\n"; std::cout << "\nResources:\n"; // timeout and memout are now available on Linux and macOS too. std::cout << " -T:timeout set the timeout (in seconds).\n"; std::cout << " -t:timeout set the soft timeout (in milli seconds). It only kills the current query.\n"; std::cout << " -memory:Megabytes set a limit for virtual memory consumption.\n"; // std::cout << "\nOutput:\n"; std::cout << " -st display statistics.\n"; #if defined(Z3DEBUG) || defined(_TRACE) std::cout << "\nDebugging support:\n"; #endif #ifdef _TRACE std::cout << " -tr:tag enable trace messages tagged with .\n"; #endif #ifdef Z3DEBUG std::cout << " -dbg:tag enable assertions tagged with .\n"; #endif std::cout << "\nParameter setting:\n"; std::cout << "Global and module parameters can be set in the command line.\n"; std::cout << " param_name=value for setting global parameters.\n"; std::cout << " module_name.param_name=value for setting module parameters.\n"; std::cout << "Use 'z3 -p' for the complete list of global and module parameters.\n"; } static void parse_cmd_line_args(int argc, char ** argv) { long timeout = 0; int i = 1; char * eq_pos = nullptr; while (i < argc) { char * arg = argv[i]; if (arg[0] == '-' && arg[1] == '-' && arg[2] == 0) { // Little hack used to read files with strange names such as -foo.smt2 // z3 -- -foo.smt2 i++; g_aux_input_file = ""; for (; i < argc; i++) { g_aux_input_file += argv[i]; if (i < argc - 1) g_aux_input_file += " "; } if (g_input_file) { warning_msg("input file was already specified."); } else { g_input_file = g_aux_input_file.c_str(); } break; } if (arg[0] == '-' #ifdef _WINDOWS || arg[0] == '/' #endif ) { char * opt_name = arg + 1; // allow names such as --help if (*opt_name == '-') opt_name++; char * opt_arg = nullptr; char * colon = strchr(arg, ':'); if (colon) { opt_arg = colon + 1; *colon = 0; } if (strcmp(opt_name, "h") == 0 || strcmp(opt_name, "?") == 0 || strcmp(opt_name, "help") == 0) { display_usage(); exit(0); } if (strcmp(opt_name, "version") == 0) { std::cout << "Z3 version " << Z3_MAJOR_VERSION << "." << Z3_MINOR_VERSION << "." << Z3_BUILD_NUMBER; std::cout << " - "; #if defined(__LP64__) || defined(_WIN64) std::cout << "64"; #else std::cout << "32"; #endif std::cout << " bit"; #ifdef Z3GITHASH std::cout << " - build hashcode " << STRINGIZE_VALUE_OF(Z3GITHASH); #endif std::cout << "\n"; exit(0); } else if (strcmp(opt_name, "smt2") == 0) { g_input_kind = IN_SMTLIB_2; } else if (strcmp(opt_name, "dl") == 0) { g_input_kind = IN_DATALOG; } else if (strcmp(opt_name, "in") == 0) { g_standard_input = true; } else if (strcmp(opt_name, "dimacs") == 0) { g_input_kind = IN_DIMACS; } else if (strcmp(opt_name, "wcnf") == 0) { g_input_kind = IN_WCNF; } else if (strcmp(opt_name, "pbo") == 0) { g_input_kind = IN_OPB; } else if (strcmp(opt_name, "lp") == 0) { g_input_kind = IN_LP; } else if (strcmp(opt_name, "log") == 0) { g_input_kind = IN_Z3_LOG; } else if (strcmp(opt_name, "st") == 0) { g_display_statistics = true; gparams::set("stats", "true"); } else if (strcmp(opt_name, "ist") == 0) { g_display_istatistics = true; } else if (strcmp(opt_name, "v") == 0) { if (!opt_arg) error("option argument (-v:level) is missing."); long lvl = strtol(opt_arg, nullptr, 10); set_verbosity_level(lvl); } else if (strcmp(opt_name, "file") == 0) { g_input_file = opt_arg; } else if (strcmp(opt_name, "T") == 0) { if (!opt_arg) error("option argument (-T:timeout) is missing."); timeout = strtol(opt_arg, nullptr, 10); } else if (strcmp(opt_name, "t") == 0) { if (!opt_arg) error("option argument (-t:timeout) is missing."); gparams::set("timeout", opt_arg); } else if (strcmp(opt_name, "nw") == 0) { enable_warning_messages(false); } else if (strcmp(opt_name, "p") == 0) { gparams::display(std::cout, 0, false, false); exit(0); } else if (strcmp(opt_name, "pd") == 0) { gparams::display(std::cout, 0, false, true); exit(0); } else if (strcmp(opt_name, "pm") == 0) { if (opt_arg) { gparams::display_module(std::cout, opt_arg); } else { gparams::display_modules(std::cout); std::cout << "\nUse -pm:name to display all parameters available at module 'name'\n"; } exit(0); } else if (strcmp(opt_name, "pp") == 0) { if (!opt_arg) error("option argument (-pp:name) is missing."); gparams::display_parameter(std::cout, opt_arg); exit(0); } #ifdef _TRACE else if (strcmp(opt_name, "tr") == 0) { if (!opt_arg) error("option argument (-tr:tag) is missing."); enable_trace(opt_arg); } #endif else if (strcmp(opt_name, "dbg") == 0) { if (!opt_arg) error("option argument (-dbg:tag) is missing."); enable_debug(opt_arg); } else if (strcmp(opt_name, "memory") == 0) { if (!opt_arg) error("option argument (-memory:val) is missing."); gparams::set("memory_max_size", opt_arg); } else { std::cerr << "Error: invalid command line option: " << arg << "\n"; std::cerr << "For usage information: z3 -h\n"; exit(ERR_CMD_LINE); } } else if (argv[i][0] != '"' && (eq_pos = strchr(argv[i], '='))) { char * key = argv[i]; *eq_pos = 0; char * value = eq_pos+1; gparams::set(key, value); } else { if (g_input_file) { warning_msg("input file was already specified."); } else { g_input_file = arg; } } i++; } if (timeout) set_timeout(timeout * 1000); } int STD_CALL main(int argc, char ** argv) { #ifdef DUMP_ARGS std::cout << "args are: "; for (int i = 0; i < argc; i++) std::cout << argv[i] <<" "; std::cout << std::endl; #endif try{ unsigned return_value = 0; memory::initialize(0); memory::exit_when_out_of_memory(true, "ERROR: out of memory"); parse_cmd_line_args(argc, argv); env_params::updt_params(); if (g_input_file && g_standard_input) { error("using standard input to read formula."); } if (!g_input_file && !g_standard_input) { error("input file was not specified."); } if (g_input_kind == IN_UNSPECIFIED) { g_input_kind = IN_SMTLIB_2; char const * ext = get_extension(g_input_file); if (ext) { if (strcmp(ext, "datalog") == 0 || strcmp(ext, "dl") == 0) { g_input_kind = IN_DATALOG; } else if (strcmp(ext, "dimacs") == 0 || strcmp(ext, "cnf") == 0) { g_input_kind = IN_DIMACS; } else if (strcmp(ext, "wcnf") == 0) { g_input_kind = IN_WCNF; } else if (strcmp(ext, "opb") == 0) { g_input_kind = IN_OPB; } else if (strcmp(ext, "lp") == 0) { g_input_kind = IN_LP; } else if (strcmp(ext, "log") == 0) { g_input_kind = IN_Z3_LOG; } else if (strcmp(ext, "smt2") == 0) { g_input_kind = IN_SMTLIB_2; } else if (strcmp(ext, "mps") == 0 || strcmp(ext, "sif") == 0 || strcmp(ext, "MPS") == 0 || strcmp(ext, "SIF") == 0) { g_input_kind = IN_MPS; } } } switch (g_input_kind) { case IN_SMTLIB_2: memory::exit_when_out_of_memory(true, "(error \"out of memory\")"); return_value = read_smtlib2_commands(g_input_file); break; case IN_DIMACS: return_value = read_dimacs(g_input_file); break; case IN_WCNF: return_value = parse_opt(g_input_file, wcnf_t); break; case IN_OPB: return_value = parse_opt(g_input_file, opb_t); break; case IN_LP: return_value = parse_opt(g_input_file, lp_t); break; case IN_DATALOG: read_datalog(g_input_file); break; case IN_Z3_LOG: replay_z3_log(g_input_file); break; case IN_MPS: return_value = read_mps_file(g_input_file); break; default: UNREACHABLE(); } memory::finalize(); #ifdef _WINDOWS _CrtDumpMemoryLeaks(); #endif return return_value; } catch (z3_exception & ex) { // unhandled exception std::cerr << "ERROR: " << ex.msg() << "\n"; if (ex.has_error_code()) return ex.error_code(); else return ERR_INTERNAL_FATAL; } } z3-z3-4.8.7/src/shell/opt_frontend.cpp000066400000000000000000000103711356505360400175340ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include #include #include #include "util/gparams.h" #include "util/timeout.h" #include "util/cancel_eh.h" #include "util/scoped_timer.h" #include "ast/ast_util.h" #include "ast/arith_decl_plugin.h" #include "ast/ast_pp.h" #include "ast/reg_decl_plugins.h" #include "model/model_smt2_pp.h" #include "opt/opt_context.h" #include "shell/opt_frontend.h" #include "opt/opt_parse.h" extern bool g_display_statistics; static bool g_first_interrupt = true; static opt::context* g_opt = nullptr; static double g_start_time = 0; static unsigned_vector g_handles; static std::mutex *display_stats_mux = new std::mutex; static void display_results() { IF_VERBOSE(1, if (g_opt) { model_ref mdl; g_opt->get_model(mdl); if (mdl) { model_smt2_pp(verbose_stream(), g_opt->get_manager(), *mdl, 0); } for (unsigned h : g_handles) { expr_ref lo = g_opt->get_lower(h); expr_ref hi = g_opt->get_upper(h); if (lo == hi) { std::cout << " " << lo << "\n"; } else { std::cout << " [" << lo << ":" << hi << "]\n"; } } }); } static void display_statistics() { std::lock_guard lock(*display_stats_mux); if (g_display_statistics && g_opt) { ::statistics stats; g_opt->collect_statistics(stats); stats.display(std::cout); double end_time = static_cast(clock()); std::cout << "time: " << (end_time - g_start_time)/CLOCKS_PER_SEC << " secs\n"; } display_results(); } static void STD_CALL on_ctrl_c(int) { if (g_opt && g_first_interrupt) { g_opt->get_manager().limit().cancel(); g_first_interrupt = false; } else { signal (SIGINT, SIG_DFL); display_statistics(); raise(SIGINT); } } static void on_timeout() { display_statistics(); exit(0); } static unsigned parse_opt(std::istream& in, opt_format f) { ast_manager m; reg_decl_plugins(m); opt::context opt(m); g_opt = &opt; params_ref p = gparams::get_module("opt"); opt.updt_params(p); switch (f) { case wcnf_t: parse_wcnf(opt, in, g_handles); break; case opb_t: parse_opb(opt, in, g_handles); break; case lp_t: parse_lp(opt, in, g_handles); break; } try { cancel_eh eh(m.limit()); unsigned timeout = std::stoul(gparams::get_value("timeout")); unsigned rlimit = std::stoi(gparams::get_value("rlimit")); scoped_timer timer(timeout, &eh); scoped_rlimit _rlimit(m.limit(), rlimit); expr_ref_vector asms(m); lbool r = opt.optimize(asms); switch (r) { case l_true: std::cout << "sat\n"; break; case l_false: std::cout << "unsat\n"; break; case l_undef: std::cout << "unknown\n"; break; } if (r != l_false && gparams::get_ref().get_bool("model_validate", false)) { model_ref mdl; opt.get_model(mdl); expr_ref_vector hard(m); opt.get_hard_constraints(hard); for (expr* h : hard) { if (!mdl->is_true(h)) { std::cout << mk_pp(h, m) << " evaluates to: " << (*mdl)(h) << "\n"; } } } } catch (z3_exception & ex) { std::cerr << ex.msg() << "\n"; } display_statistics(); g_opt = nullptr; return 0; } unsigned parse_opt(char const* file_name, opt_format f) { g_first_interrupt = true; g_start_time = static_cast(clock()); register_on_timeout_proc(on_timeout); signal(SIGINT, on_ctrl_c); if (file_name) { std::ifstream in(file_name); if (in.bad() || in.fail()) { std::cerr << "(error \"failed to open file '" << file_name << "'\")" << std::endl; exit(ERR_OPEN_FILE); } return parse_opt(in, f); } else { return parse_opt(std::cin, f); } } z3-z3-4.8.7/src/shell/opt_frontend.h000066400000000000000000000004771356505360400172070ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: opt_frontend.h Author: Nikolaj Bjorner (nbjorner) 2014-10-10. --*/ #ifndef OPT_FRONTEND_H_ #define OPT_FRONTEND_H_ enum opt_format { opb_t, wcnf_t, lp_t }; unsigned parse_opt(char const* file_name, opt_format f); #endif /* OPT_FRONTEND_H_ */ z3-z3-4.8.7/src/shell/options.h000066400000000000000000000044321356505360400161740ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ /** \page cmdline Command line options \section informat Input format Z3 understands a set of default file extensions, and will invoke a parser based on the extension. - \ext{smt2} - SMT-LIB 2 format, this is the preferred input format. - \ext{dimacs}, \ext{cnf} - DIMACS format used by regular SAT solvers. - \ext{dl} - Datalog input format. - \ext{smt} - (deprecated) SMT-LIB 1 format. You can tell Z3 explicitly which grammar the input belongs to by using the following options: \cmdopt{smt2} use parser for SMT-LIB 2.0 input format. \cmdopt{dimacs} use dimacs parser to read the input file. \section cmdlinemis Miscellaneous \cmdopt{h\, ?} prints the help message. \cmdopt{version} prints version number of Z3. \cmdopt{v:level} be verbose, where is the verbosity level. \cmdopt{nw} disable warning messages. \cmdopt{ini:file} configuration file. Several parameters are available besides the ones listed by \ty{/h}. These parameters can be loaded from an initialization file by using this option. \cmdopt{ini?} display all available INI file parameters. The available \ref config can also be supplied on the command line as a pair parameter-name=parameter-value. \section cmdlineres Resources \cmdopt{T:timeout} set the timeout (in seconds). Setting this option causes the entire process to exit. It is a reliable way to kill Z3. \cmdopt{t:timeout} set the soft timeout (in seconds). It only kills the current query. \cmdopt{memory:Megabytes} set a limit for virtual memory consumption. This limit for virtual memory consumption is approximate, but in general a good guideline for controlling the memory consumption of Z3. If the memory consumption exceeds the specified number of Megabytes, Z3 exits with a warning message. \section cmdlineout Output \cmdopt{st} display statistics. This option can be used to dump various statistics about the search, such as number of splits, conflict clauses, and quantifier instantiations. \section cmdlinesearch Search heuristics \cmdopt{rs:num} random seed. */ z3-z3-4.8.7/src/shell/smtlib_frontend.cpp000066400000000000000000000047151356505360400202310ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smtlib_frontend.cpp Abstract: Frontend for reading Smtlib input files Author: Nikolaj Bjorner (nbjorner) 2006-11-3. Revision History: Leonardo de Moura: new SMT 2.0 front-end, removed support for .smtc files and smtcmd_solver object. --*/ #include #include #include #include "util/timeout.h" #include "parsers/smt2/smt2parser.h" #include "muz/fp/dl_cmds.h" #include "cmd_context/extra_cmds/dbg_cmds.h" #include "opt/opt_cmds.h" #include "cmd_context/extra_cmds/polynomial_cmds.h" #include "cmd_context/extra_cmds/subpaving_cmds.h" #include "smt/smt2_extra_cmds.h" #include "smt/smt_solver.h" static std::mutex *display_stats_mux = new std::mutex; extern bool g_display_statistics; static clock_t g_start_time; static cmd_context * g_cmd_context = nullptr; static void display_statistics() { std::lock_guard lock(*display_stats_mux); clock_t end_time = clock(); if (g_cmd_context && g_display_statistics) { std::cout.flush(); std::cerr.flush(); if (g_cmd_context) { g_cmd_context->set_regular_stream("stdout"); g_cmd_context->display_statistics(true, ((static_cast(end_time) - static_cast(g_start_time)) / CLOCKS_PER_SEC)); } } } static void on_timeout() { display_statistics(); exit(0); } static void STD_CALL on_ctrl_c(int) { signal (SIGINT, SIG_DFL); display_statistics(); raise(SIGINT); } unsigned read_smtlib2_commands(char const * file_name) { g_start_time = clock(); register_on_timeout_proc(on_timeout); signal(SIGINT, on_ctrl_c); cmd_context ctx; ctx.set_solver_factory(mk_smt_strategic_solver_factory()); install_dl_cmds(ctx); install_dbg_cmds(ctx); install_polynomial_cmds(ctx); install_subpaving_cmds(ctx); install_opt_cmds(ctx); install_smt2_extra_cmds(ctx); g_cmd_context = &ctx; signal(SIGINT, on_ctrl_c); bool result = true; if (file_name) { std::ifstream in(file_name); if (in.bad() || in.fail()) { std::cerr << "(error \"failed to open file '" << file_name << "'\")" << std::endl; exit(ERR_OPEN_FILE); } result = parse_smt2_commands(ctx, in); } else { result = parse_smt2_commands(ctx, std::cin, true); } display_statistics(); g_cmd_context = nullptr; return result ? 0 : 1; } z3-z3-4.8.7/src/shell/smtlib_frontend.h000066400000000000000000000006201356505360400176650ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smtlib_frontend.h Abstract: Smtlib frontend. Author: Leonardo de Moura (leonardo) 2006-11-2. Revision History: --*/ #ifndef SMTLIB_FRONTEND_H_ #define SMTLIB_FRONTEND_H_ unsigned read_smtlib_file(char const * benchmark_file); unsigned read_smtlib2_commands(char const * command_file); #endif /* SMTLIB_FRONTEND_H_ */ z3-z3-4.8.7/src/shell/z3_log_frontend.cpp000066400000000000000000000023251356505360400201270ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: z3_log_frontend.cpp Abstract: Z3 log frontend. Replay a log generated by Z3 Author: Leonardo de Moura (leonardo) 2011-09-26. Revision History: --*/ #include #include #include #include "util/util.h" #include "util/error_codes.h" #include "api/z3_replayer.h" static void solve(char const * stream_name, std::istream & in) { clock_t start_time = clock(); z3_replayer r(in); try { r.parse(); } catch (z3_exception & ex) { std::cerr << "Error at line " << r.get_line() << ": " << ex.msg() << std::endl; } clock_t end_time = clock(); memory::display_max_usage(std::cout); std::cout << "time: " << ((static_cast(end_time) - static_cast(start_time)) / CLOCKS_PER_SEC) << "\n"; } void replay_z3_log(char const * file_name) { if (!file_name) { solve(file_name, std::cin); } else { std::ifstream in(file_name); if (in.bad() || in.fail()) { std::cerr << "Error: failed to open file \"" << file_name << "\".\n"; exit(ERR_OPEN_FILE); } solve(file_name, in); } exit(0); } z3-z3-4.8.7/src/shell/z3_log_frontend.h000066400000000000000000000005541356505360400175760ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: z3_log_frontend.h Abstract: Z3 log frontend. Replay a log generated by Z3 Author: Leonardo de Moura (leonardo) 2011-09-26. Revision History: --*/ #ifndef Z3_LOG_FRONTEND_H_ #define Z3_LOG_FRONTEND_H_ void replay_z3_log(char const * benchmark_file); #endif /* Z3_FRONTEND_H_ */ z3-z3-4.8.7/src/smt/000077500000000000000000000000001356505360400140215ustar00rootroot00000000000000z3-z3-4.8.7/src/smt/CMakeLists.txt000066400000000000000000000035741356505360400165720ustar00rootroot00000000000000z3_add_component(smt SOURCES arith_eq_adapter.cpp arith_eq_solver.cpp asserted_formulas.cpp cached_var_subst.cpp cost_evaluator.cpp dyn_ack.cpp elim_term_ite.cpp expr_context_simplifier.cpp fingerprints.cpp mam.cpp old_interval.cpp qi_queue.cpp smt_almost_cg_table.cpp smt_arith_value.cpp smt_case_split_queue.cpp smt_cg_table.cpp smt_checker.cpp smt_clause.cpp smt_clause_proof.cpp smt_conflict_resolution.cpp smt_consequences.cpp smt_context.cpp smt_context_inv.cpp smt_context_pp.cpp smt_context_stat.cpp smt_enode.cpp smt_farkas_util.cpp smt_for_each_relevant_expr.cpp smt_implied_equalities.cpp smt_internalizer.cpp smt_justification.cpp smt_kernel.cpp smt_literal.cpp smt_lookahead.cpp smt_model_checker.cpp smt_model_finder.cpp smt_model_generator.cpp smt_quantifier.cpp smt_quantifier_stat.cpp smt_quick_checker.cpp smt_relevancy.cpp smt_setup.cpp smt_solver.cpp smt_statistics.cpp smt_theory.cpp smt_value_sort.cpp smt2_extra_cmds.cpp theory_arith.cpp theory_array_bapa.cpp theory_array_base.cpp theory_array.cpp theory_array_full.cpp theory_bv.cpp theory_datatype.cpp theory_dense_diff_logic.cpp theory_diff_logic.cpp theory_dl.cpp theory_dummy.cpp theory_fpa.cpp theory_jobscheduler.cpp theory_lra.cpp theory_opt.cpp theory_pb.cpp theory_recfun.cpp theory_seq.cpp theory_special_relations.cpp theory_str.cpp theory_str_regex.cpp theory_utvpi.cpp theory_wmaxsat.cpp uses_theory.cpp watch_list.cpp COMPONENT_DEPENDENCIES bit_blaster cmd_context euclid fpa grobner nlsat lp macros normal_forms parser_util pattern proofs proto_model simplex substitution ) z3-z3-4.8.7/src/smt/arith_eq_adapter.cpp000066400000000000000000000303501356505360400200220ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: arith_eq_adapter.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-05-25. Revision History: --*/ #include "smt/smt_context.h" #include "smt/arith_eq_adapter.h" #include "ast/ast_pp.h" #include "ast/ast_ll_pp.h" #include "util/stats.h" #include "ast/ast_smt2_pp.h" namespace smt { class already_processed_trail : public trail { // Remark: it is safer to use a trail object, because it guarantees that the enodes // are still alive when the undo operation is performed. // // If a local backtracking stack is used in the class arith_eq_adapter is used, // then we cannot guarantee that. arith_eq_adapter::already_processed & m_already_processed; enode * m_n1; enode * m_n2; public: already_processed_trail(arith_eq_adapter::already_processed & m, enode * n1, enode * n2): m_already_processed(m), m_n1(n1), m_n2(n2) { } void undo(context & ctx) override { m_already_processed.erase(m_n1, m_n2); TRACE("arith_eq_adapter_profile", tout << "del #" << m_n1->get_owner_id() << " #" << m_n2->get_owner_id() << "\n";); } }; /** \brief The atoms m_eq, m_le, and m_ge should be marked as relevant only after m_n1 and m_n2 are marked as relevant. */ class arith_eq_relevancy_eh : public relevancy_eh { expr * m_n1; expr * m_n2; expr * m_eq; expr * m_le; expr * m_ge; public: arith_eq_relevancy_eh(expr * n1, expr * n2, expr * eq, expr * le, expr * ge): m_n1(n1), m_n2(n2), m_eq(eq), m_le(le), m_ge(ge) { } ~arith_eq_relevancy_eh() override {} void operator()(relevancy_propagator & rp) override { if (!rp.is_relevant(m_n1)) return; if (!rp.is_relevant(m_n2)) return; rp.mark_as_relevant(m_eq); rp.mark_as_relevant(m_le); rp.mark_as_relevant(m_ge); } }; void arith_eq_adapter::mk_axioms(enode * n1, enode * n2) { SASSERT(n1 != n2); ast_manager & m = get_manager(); TRACE("arith_eq_adapter_mk_axioms", tout << "#" << n1->get_owner_id() << " #" << n2->get_owner_id() << "\n"; tout << mk_ismt2_pp(n1->get_owner(), m) << "\n" << mk_ismt2_pp(n2->get_owner(), m) << "\n";); if (n1->get_owner_id() > n2->get_owner_id()) std::swap(n1, n2); app * t1 = n1->get_owner(); app * t2 = n2->get_owner(); if (m.is_value(t1) && m.is_value(t2)) { // Nothing to be done // We don't need to create axioms for 2 = 3 return; } if (t1 == t2) { return; } context & ctx = get_context(); CTRACE("arith_eq_adapter_relevancy", !(ctx.is_relevant(n1) && ctx.is_relevant(n2)), tout << "is_relevant(n1): #" << n1->get_owner_id() << " " << ctx.is_relevant(n1) << "\n"; tout << "is_relevant(n2): #" << n2->get_owner_id() << " " << ctx.is_relevant(n2) << "\n"; tout << mk_pp(n1->get_owner(), get_manager()) << "\n"; tout << mk_pp(n2->get_owner(), get_manager()) << "\n"; ctx.display(tout);); // // The atoms d.m_t1_eq_t2, d.m_le, and d.m_ge should only be marked as relevant // after n1 and n2 are marked as relevant. // data d; if (m_already_processed.find(n1, n2, d)) return; TRACE("arith_eq_adapter_profile", tout << "mk #" << n1->get_owner_id() << " #" << n2->get_owner_id() << " " << m_already_processed.size() << " " << ctx.get_scope_level() << "\n";); m_stats.m_num_eq_axioms++; TRACE("arith_eq_adapter_profile_detail", tout << "mk_detail " << mk_bounded_pp(n1->get_owner(), m, 5) << " " << mk_bounded_pp(n2->get_owner(), m, 5) << "\n";); app_ref t1_eq_t2(m); t1_eq_t2 = ctx.mk_eq_atom(t1, t2); SASSERT(!m.is_false(t1_eq_t2)); TRACE("arith_eq_adapter_bug", tout << mk_bounded_pp(t1_eq_t2, m) << "\n" << mk_bounded_pp(t1, m) << "\n" << mk_bounded_pp(t2, m) << "\n";); // UNRESOLVED ISSUE: // // arith_eq_adapter is still creating problems. // The following disabled code fixes the issues, but create performance problems. // The alternative does not works 100%. It generates problems when the literal // created by the adapter during the search is included in a learned clause. // Here is a sequence of events that triggers a crash: // 1) The terms t1 >= t2 and t1 <= t2 are not in simplified form. // For example, let us assume t1 := (* -1 x) and t2 := x. // Since, t1 and t2 were internalized at this point, the following code works. // That is the arith internalizer accepts the formula (+ (* -1 x) (* -1 x)) // that is not in simplified form. Let s be the term (+ (* -1 x) (* -1 x)) // 2) Assume now that a conflict is detected a lemma containing s is created. // 3) The enodes associated with t1, t2 and s are destroyed during backtracking. // 4) The term s is reinternalized at smt::context::reinit_clauses. Term t2 is // also reinitialized, but t1 is not. We only create a "name" for a term (* -1 x) // if it is embedded in a function application. // 5) theory_arith fails to internalize (+ (* -1 x) (* -1 x)), and Z3 crashes. // // Requires that the theory arithmetic internalizer accept non simplified terms of the form t1 - t2 // if t1 and t2 already have slacks (theory variables) associated with them. // It also accepts terms with repeated variables (Issue #429). app * le = nullptr; app * ge = nullptr; if (m_util.is_numeral(t1)) std::swap(t1, t2); if (m_util.is_numeral(t2)) { le = m_util.mk_le(t1, t2); ge = m_util.mk_ge(t1, t2); } else { sort * st = m.get_sort(t1); app_ref minus_one(m_util.mk_numeral(rational::minus_one(), st), m); app_ref zero(m_util.mk_numeral(rational::zero(), st), m); app_ref t3(m_util.mk_mul(minus_one, t2), m); app_ref s(m_util.mk_add(t1, t3), m); le = m_util.mk_le(s, zero); ge = m_util.mk_ge(s, zero); } TRACE("arith_eq_adapter_perf", tout << mk_ismt2_pp(t1_eq_t2, m) << "\n" << mk_ismt2_pp(le, m) << "\n" << mk_ismt2_pp(ge, m) << "\n";); ctx.push_trail(already_processed_trail(m_already_processed, n1, n2)); m_already_processed.insert(n1, n2, data(t1_eq_t2, le, ge)); TRACE("arith_eq_adapter_profile", tout << "insert #" << n1->get_owner_id() << " #" << n2->get_owner_id() << "\n";); ctx.internalize(t1_eq_t2, true); literal t1_eq_t2_lit(ctx.get_bool_var(t1_eq_t2)); TRACE("interface_eq", tout << "core should try true phase first for the equality: " << t1_eq_t2_lit << "\n"; tout << "#" << n1->get_owner_id() << " == #" << n2->get_owner_id() << "\n"; tout << "try_true_first: " << ctx.try_true_first(t1_eq_t2_lit.var()) << "\n";); TRACE("arith_eq_adapter_bug", tout << "le: " << mk_ismt2_pp(le, m) << "\nge: " << mk_ismt2_pp(ge, m) << "\n";); ctx.internalize(le, true); ctx.internalize(ge, true); SASSERT(ctx.lit_internalized(le)); SASSERT(ctx.lit_internalized(ge)); literal le_lit = ctx.get_literal(le); literal ge_lit = ctx.get_literal(ge); if (ctx.try_true_first(t1_eq_t2_lit.var())) { // Remark: I need to propagate the try_true_first flag to the auxiliary atom le_lit and ge_lit. // Otherwise model based theory combination will be ineffective, because if the core // case splits in le_lit and ge_lit before t1_eq_t2_lit it will essentially assign an arbitrary phase to t1_eq_t2_lit. ctx.set_true_first_flag(le_lit.var()); ctx.set_true_first_flag(ge_lit.var()); } theory_id tid = m_owner.get_id(); if (m.proofs_enabled() && m_proof_hint.empty()) { m_proof_hint.push_back(parameter(symbol("triangle-eq"))); } ctx.mk_th_axiom(tid, ~t1_eq_t2_lit, le_lit, m_proof_hint.size(), m_proof_hint.c_ptr()); ctx.mk_th_axiom(tid, ~t1_eq_t2_lit, ge_lit, m_proof_hint.size(), m_proof_hint.c_ptr()); ctx.mk_th_axiom(tid, t1_eq_t2_lit, ~le_lit, ~ge_lit, m_proof_hint.size(), m_proof_hint.c_ptr()); TRACE("arith_eq_adapter", tout << "internalizing: " << " " << mk_pp(le, m) << ": " << le_lit << " " << mk_pp(ge, m) << ": " << ge_lit << " " << mk_pp(t1_eq_t2, m) << ": " << t1_eq_t2_lit << "\n";); if (m_params.m_arith_add_binary_bounds) { TRACE("arith_eq_adapter", tout << "adding binary bounds...\n";); ctx.mk_th_axiom(tid, le_lit, ge_lit, 3, m_proof_hint.c_ptr()); } if (ctx.relevancy()) { relevancy_eh * eh = ctx.mk_relevancy_eh(arith_eq_relevancy_eh(n1->get_owner(), n2->get_owner(), t1_eq_t2, le, ge)); ctx.add_relevancy_eh(n1->get_owner(), eh); ctx.add_relevancy_eh(n2->get_owner(), eh); } if (!m_params.m_arith_lazy_adapter && !ctx.at_base_level() && n1->get_iscope_lvl() <= ctx.get_base_level() && n2->get_iscope_lvl() <= ctx.get_base_level()) { m_restart_pairs.push_back(enode_pair(n1, n2)); } TRACE("arith_eq_adapter_detail", ctx.display(tout);); } void arith_eq_adapter::new_eq_eh(theory_var v1, theory_var v2) { TRACE("arith_eq_adapter", tout << "v" << v1 << " = v" << v2 << " #" << get_enode(v1)->get_owner_id() << " = #" << get_enode(v2)->get_owner_id() << "\n";); TRACE("arith_eq_adapter_bug", tout << mk_bounded_pp(get_enode(v1)->get_owner(), get_manager()) << "\n" << mk_bounded_pp(get_enode(v2)->get_owner(), get_manager()) << "\n";); mk_axioms(get_enode(v1), get_enode(v2)); } void arith_eq_adapter::new_diseq_eh(theory_var v1, theory_var v2) { TRACE("arith_eq_adapter", tout << "v" << v1 << " != v" << v2 << " #" << get_enode(v1)->get_owner_id() << " != #" << get_enode(v2)->get_owner_id() << "\n";); mk_axioms(get_enode(v1), get_enode(v2)); } void arith_eq_adapter::init_search_eh() { m_restart_pairs.reset(); } void arith_eq_adapter::reset_eh() { TRACE("arith_eq_adapter", tout << "reset\n";); m_already_processed .reset(); m_restart_pairs .reset(); m_stats .reset(); } void arith_eq_adapter::restart_eh() { context & ctx = get_context(); TRACE("arith_eq_adapter", tout << "restart\n";); enode_pair_vector tmp(m_restart_pairs); enode_pair_vector::iterator it = tmp.begin(); enode_pair_vector::iterator end = tmp.end(); m_restart_pairs.reset(); for (; it != end && !ctx.inconsistent(); ++it) { TRACE("arith_eq_adapter", tout << "creating arith_eq_adapter axioms at the base level #" << it->first->get_owner_id() << " #" << it->second->get_owner_id() << "\n";); mk_axioms(it->first, it->second); } } void arith_eq_adapter::collect_statistics(::statistics & st) const { st.update("arith eq adapter", m_stats.m_num_eq_axioms); } void arith_eq_adapter::display_already_processed(std::ostream & out) const { obj_pair_map::iterator it = m_already_processed.begin(); obj_pair_map::iterator end = m_already_processed.end(); for (; it != end; ++it) { enode * n1 = it->get_key1(); enode * n2 = it->get_key2(); out << "eq_adapter: #" << n1->get_owner_id() << " #" << n2->get_owner_id() << "\n"; } } }; z3-z3-4.8.7/src/smt/arith_eq_adapter.h000066400000000000000000000051141356505360400174670ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: arith_eq_adapter.h Abstract: Author: Leonardo de Moura (leonardo) 2008-05-25. Revision History: --*/ #ifndef ARITH_EQ_ADAPTER_H_ #define ARITH_EQ_ADAPTER_H_ #include "smt/smt_theory.h" #include "util/obj_pair_hashtable.h" #include "ast/arith_decl_plugin.h" #include "util/statistics.h" namespace smt { struct arith_eq_adapter_stats { unsigned m_num_eq_axioms; void reset() { m_num_eq_axioms = 0; } arith_eq_adapter_stats() { reset(); } }; /** \brief Auxiliary class used to convert (dis) equalities propagated from the core into arith equalities/inequalities atoms. This class is used by the arithmetic theories to handle the (dis) equalities propagated from the logical context. - config 1: recreate axioms at restart - config 2: lazy diseq split */ class arith_eq_adapter { public: arith_eq_adapter_stats m_stats; private: struct data { expr * m_t1_eq_t2; expr * m_le; expr * m_ge; data():m_t1_eq_t2(nullptr), m_le(nullptr), m_ge(nullptr) {} data(expr * t1_eq_t2, expr * le, expr * ge):m_t1_eq_t2(t1_eq_t2), m_le(le), m_ge(ge) {} }; public: typedef obj_pair_map already_processed; private: theory & m_owner; theory_arith_params & m_params; arith_util & m_util; already_processed m_already_processed; enode_pair_vector m_restart_pairs; svector m_proof_hint; context & get_context() const { return m_owner.get_context(); } ast_manager & get_manager() const { return m_owner.get_manager(); } enode * get_enode(theory_var v) const { return m_owner.get_enode(v); } public: arith_eq_adapter(theory & owner, theory_arith_params & params, arith_util & u):m_owner(owner), m_params(params), m_util(u) {} void new_eq_eh(theory_var v1, theory_var v2); void new_diseq_eh(theory_var v1, theory_var v2); void reset_eh(); void init_search_eh(); void restart_eh(); /** \brief Add the eq axioms for n1 and n2. */ void mk_axioms(enode * n1, enode * n2); void collect_statistics(::statistics & st) const; void display_already_processed(std::ostream & out) const; }; }; #endif /* ARITH_EQ_ADAPTER_H_ */ z3-z3-4.8.7/src/smt/arith_eq_solver.cpp000066400000000000000000000421221356505360400177140ustar00rootroot00000000000000/*++ Copyright (c) 2007 Microsoft Corporation Module Name: arith_eq_solver.cpp Abstract: Solver for linear arithmetic equalities. Author: Nikolaj Bjorner (nbjorner) 2012-02-25 --*/ #include "smt/arith_eq_solver.h" arith_eq_solver::arith_eq_solver(ast_manager & m, params_ref const& p): m(m), m_params(p), m_util(m), m_arith_rewriter(m) { m_params.set_bool("gcd_rounding", true); // m_params.set_bool("sum", true); m_arith_rewriter.updt_params(m_params); } /** \brief Return true if the first monomial of t is negative. */ bool arith_eq_solver::is_neg_poly(expr * t) const { if (m_util.is_add(t)) { t = to_app(t)->get_arg(0); } if (m_util.is_mul(t)) { t = to_app(t)->get_arg(0); rational r; bool is_int; if (m_util.is_numeral(t, r, is_int)) return r.is_neg(); } return false; } void arith_eq_solver::prop_mod_const(expr * e, unsigned depth, numeral const& k, expr_ref& result) { SASSERT(m_util.is_int(e)); SASSERT(k.is_int() && k.is_pos()); numeral n; bool is_int; if (depth == 0) { result = e; } else if (m_util.is_add(e) || m_util.is_mul(e)) { expr_ref_vector args(m); expr_ref tmp(m); app* a = to_app(e); for (unsigned i = 0; i < a->get_num_args(); ++i) { prop_mod_const(a->get_arg(i), depth - 1, k, tmp); args.push_back(tmp); } m_arith_rewriter.mk_app(a->get_decl(), args.size(), args.c_ptr(), result); } else if (m_util.is_numeral(e, n, is_int) && is_int) { result = m_util.mk_numeral(mod(n, k), true); } else { result = e; } } void arith_eq_solver::gcd_normalize(vector& values) { numeral g(0); for (unsigned i = 0; !g.is_one() && i < values.size(); ++i) { SASSERT(values[i].is_int()); if (!values[i].is_zero()) { if (g.is_zero()) { g = abs(values[i]); } else { g = gcd(abs(values[i]), g); } } } if (g.is_zero() || g.is_one()) { return; } for (auto &value : values) { value /= g; SASSERT(value.is_int()); } } unsigned arith_eq_solver::find_abs_min(vector& values) { SASSERT(values.size() >= 2); unsigned index = 0; numeral v(0); for (unsigned i = 1; i < values.size(); ++i) { numeral w = abs(values[i]); if (v.is_zero() || (!w.is_zero() && w < v)) { index = i; v = w; } } return index; } #ifdef _TRACE static void print_row(std::ostream& out, vector const& row) { for(unsigned i = 0; i < row.size(); ++i) { out << row[i] << " "; } out << "\n"; } static void print_rows(std::ostream& out, vector > const& rows) { for (unsigned i = 0; i < rows.size(); ++i) { print_row(out, rows[i]); } } #endif // // The gcd of the coefficients to variables have to divide the // coefficient to the constant. // The constant is the last value in the array. // bool arith_eq_solver::gcd_test(vector& values) { SASSERT(values.size() > 0); numeral g(0); numeral first_value = values[0]; for (unsigned i = 1; !g.is_one() && i < values.size(); ++i) { if (!values[i].is_zero()) { if (g.is_zero()) { g = abs(values[i]); } else { g = gcd(abs(values[i]), g); } } } if (g.is_one()) { return true; } if (g.is_zero()) { return first_value.is_zero(); } numeral r = first_value/g; return r.is_int(); } bool arith_eq_solver::solve_integer_equation( vector& values, unsigned& index, bool& is_fresh ) { TRACE("arith_eq_solver", tout << "solving: "; print_row(tout, values); ); // // perform one step of the omega test equality elimination. // // Given: // a1*x1 + a2*x2 + .. + a_n*x_n + a_{n+1} = 0 // // Assume gcd(a1,..,a_n,a_{n+1}) = 1 // Assume gcd(a1,...,a_n) divides a_{n+1} (eg. gcd(a1,..,an) = 1) // // post-condition: values[index] = -1. // // Let a_index be index of least absolute value. // // If |a_index| = 1, then return row and index. // Otherwise: // Let m = |a_index| + 1 // Set // // m*x_index' // = // ((a1 mod_hat m)*x1 + (a2 mod_hat m)*x2 + .. + (a_n mod_hat m)*x_n + (k mod_hat m)) // = // (a1'*x1 + a2'*x2 + .. (-)1*x_index + ...) // // <=> Normalize signs so that sign to x_index is -1. // (-)a1'*x1 + (-)a2'*x2 + .. -1*x_index + ... + m*x_index' = 0 // // Return row, where the coefficient to x_index is implicit. // Instead used the coefficient 'm' at position 'index'. // gcd_normalize(values); if (!gcd_test(values)) { TRACE("arith_eq_solver", tout << "not sat\n"; print_row(tout, values);); return false; } index = find_abs_min(values); SASSERT(1 <= index && index < values.size()); numeral a = values[index]; numeral abs_a = abs(a); if (abs_a.is_zero()) { // The equation is trivial. return true; } if (a.is_one()) { for (auto &value : values) { value.neg(); } } is_fresh = !abs_a.is_one(); if (is_fresh) { numeral m = abs_a + numeral(1); for (auto &value : values) { value = mod_hat(value, m); } if (values[index].is_one()) { for (auto &value : values) { value.neg(); } } SASSERT(values[index].is_minus_one()); values[index] = m; } TRACE("arith_eq_solver", tout << "solved at index " << index << ": "; print_row(tout, values); ); return true; } void arith_eq_solver::substitute( row& r, row const& s, unsigned index ) { SASSERT(1 <= index && index < s.size()); TRACE("arith_eq_solver", tout << "substitute " << index << ":\n"; print_row(tout, r); print_row(tout, s); ); if (index >= r.size()) { return; } numeral c = r[index]; if (c.is_zero()) { // no-op } else if (abs(s[index]).is_one()) { // // s encodes an equation that contains a variable // with a unit coefficient. // // Let // c = r[index] // s = s[index]*x + s'*y = 0 // r = c*x + r'*y = 0 // // => // // 0 // = // -sign(s[index])*c*s + r // = // -s[index]*sign(s[index])*c*x - sign(s[index])*c*s'*y + c*x + r'*y // = // -c*x - sign(s[index])*c*s'*y + c*x + r'*y // = // -sign(s[index])*c*s'*y + r'*y // numeral sign_s = s[index].is_pos()?numeral(1):numeral(-1); for (unsigned i = 0; i < r.size(); ++i) { r[i] -= c*sign_s*s[i]; } for (unsigned i = r.size(); i < s.size(); ++i) { r.push_back(-c*sign_s*s[i]); } } else { // // s encodes a substitution using an auxiliary variable. // the auxiliary variable is at position 'index'. // // Let // c = r[index] // s = s[index]*x + s'*y = 0 // r = c*x + r'*y = 0 // // s encodes : x |-> s[index]*x' + s'*y // // Set: // // r := c*s + r'*y // r[index] = numeral(0); for (unsigned i = 0; i < r.size(); ++i) { r[i] += c*s[i]; } for (unsigned i = r.size(); i < s.size(); ++i) { r.push_back(c*s[i]); } } TRACE("arith_eq_solver", tout << "result: "; print_row(tout, r); ); } bool arith_eq_solver::solve_integer_equations( vector& rows, row& unsat_row ) { // return solve_integer_equations_units(rows, unsat_row); return solve_integer_equations_gcd(rows, unsat_row); } // // Naive integer equation solver where only units are eliminated. // bool arith_eq_solver::solve_integer_equations_units( vector& rows, row& unsat_row ) { TRACE("arith_eq_solver", print_rows(tout << "solving:\n", rows);); unsigned_vector todo, done; for (unsigned i = 0; i < rows.size(); ++i) { todo.push_back(i); row& r = rows[i]; gcd_normalize(r); if (!gcd_test(r)) { unsat_row = r; TRACE("arith_eq_solver", print_row(tout << "input is unsat: ", unsat_row); ); return false; } } for (unsigned i = 0; i < todo.size(); ++i) { row& r = rows[todo[i]]; gcd_normalize(r); if (!gcd_test(r)) { unsat_row = r; TRACE("arith_eq_solver", print_row(tout << "unsat: ", unsat_row); ); return false; } unsigned index = find_abs_min(r); SASSERT(1 <= index && index < r.size()); numeral a = r[index]; numeral abs_a = abs(a); if (abs_a.is_zero()) { continue; } else if (abs_a.is_one()) { for (unsigned j = i+1; j < todo.size(); ++j) { substitute(rows[todo[j]], r, index); } for (unsigned j = 0; j < done.size(); ++j) { row& r2 = rows[done[j]]; if (!r2[index].is_zero()) { substitute(r2, r, index); todo.push_back(done[j]); done.erase(done.begin()+j); --j; } } } else { done.push_back(todo[i]); } } TRACE("arith_eq_solver", tout << ((done.size()<=1)?"solved ":"incomplete check ") << done.size() << "\n"; for (unsigned i = 0; i < done.size(); ++i) { print_row(tout, rows[done[i]]); } ); return true; } // // Partial solver based on the omega test equalities. // unsatisfiability is not preserved when eliminating // auxiliary variables. // bool arith_eq_solver::solve_integer_equations_omega( vector & rows, row& unsat_row ) { unsigned index; bool is_fresh; vector rows_solved; unsigned_vector indices; unsigned_vector aux_indices; for (unsigned i = 0; i < rows.size(); ++i) { rows_solved.push_back(rows[i]); row& r = rows_solved.back(); for (unsigned j = 0; j + 1 < rows_solved.size(); ++j) { substitute(r, rows_solved[j], indices[j]); } if (!solve_integer_equation(r, index, is_fresh)) { unsat_row = r; gcd_normalize(unsat_row); // invert the substitution for every index that is fresh. TRACE("arith_eq_solver", tout << "unsat:\n"; print_row(tout, unsat_row); for (unsigned l = 0; l + 1< rows_solved.size(); ++l) { print_row(tout, rows_solved[l]); }); for (unsigned j = rows_solved.size()-1; j > 0; ) { --j; row& solved_row = rows_solved[j]; unsigned index_j = indices[j]; unsigned aux_index_j = aux_indices[j]; SASSERT(index_j <= aux_index_j); if (unsat_row.size() <= aux_index_j) { unsat_row.resize(aux_index_j+1); } numeral m = solved_row[aux_index_j]; numeral k = unsat_row[aux_index_j]; if (aux_index_j != index_j && !k.is_zero()) { // // solved_row: -x_index + m*sigma + r1 = 0 // unsat_row: k*sigma + r2 = 0 // // <=> // // solved_row: -k*x_index + k*m*sigma + k*r1 = 0 // unsat_row: m*k*sigma + m*r2 = 0 // // => // // m*k*sigma + m*r2 + k*x_index - k*m*sigma - k*r1 = 0 // for (unsigned l = 0; l < unsat_row.size(); ++l) { unsat_row[l] *= m; unsat_row[l] -= k*solved_row[l]; } for (unsigned l = unsat_row.size(); l < solved_row.size(); ++l) { unsat_row.push_back(solved_row[l]); } gcd_normalize(unsat_row); TRACE("arith_eq_solver", tout << "gcd: "; print_row(tout, solved_row); print_row(tout, unsat_row); ); } if (gcd_test(unsat_row)) { TRACE("arith_eq_solver", tout << "missed pure explanation\n";); return true; } SASSERT(!gcd_test(unsat_row)); } return false; } else if (r[index].is_zero()) { // Row is trivial rows_solved.pop_back(); continue; } else if (!abs(r[index]).is_one()) { // // The solution introduces a fresh auxiliary variable. // Make space for this variable as a fresh numeral. // indices.push_back(index); aux_indices.push_back(r.size()); r.push_back(r[index]); r[index] = numeral(-1); // re-solve the same row. --i; } else { indices.push_back(index); aux_indices.push_back(index); } } return true; } // // Eliminate variables by searching for combination of rows where // the coefficients have gcd = 1. // bool arith_eq_solver::solve_integer_equations_gcd( vector & rows, row& unsat_row ) { unsigned_vector live, useful, gcd_pos; vector gcds; rational u, v; if (rows.empty()) { return true; } for (unsigned i = 0; i < rows.size(); ++i) { live.push_back(i); row& r = rows[i]; gcd_normalize(r); if (!gcd_test(r)) { unsat_row = r; TRACE("arith_eq_solver", print_row(tout << "input is unsat: ", unsat_row); ); return false; } } unsigned max_column = rows[0].size(); bool change = true; while (change && !live.empty()) { change = false; for (unsigned i = 1; i < max_column; ++i) { rational g(0); gcds.reset(); gcd_pos.reset(); unsigned j = 0; for (; j < live.size(); ++j) { rational const& k = rows[live[j]][i]; if (k.is_zero()) { continue; } if (g.is_zero()) { g = abs(k); } else { g = gcd(g, abs(k)); } if (abs(g).is_one()) { break; } gcds.push_back(g); gcd_pos.push_back(live[j]); } if (j == live.size()) { continue; } change = true; // found gcd, now identify reduced set of rows with GCD = 1. g = abs(rows[live[j]][i]); useful.push_back(live[j]); unsigned live_pos = j; for (j = gcds.size(); !g.is_one() && j > 0; ) { SASSERT(g.is_pos()); --j; if (j == 0 || !gcd(g, gcds[j-1]).is_one()) { useful.push_back(gcd_pos[j]); g = gcd(g, gcds[j]); SASSERT(j == 0 || gcd(g,gcds[j-1]).is_one()); } } // // we now have a set "useful" of rows whose combined GCD = 1. // pivot the remaining with the first row. // row& r0 = rows[useful[0]]; for (j = 1; j < useful.size(); ++j) { row& r1 = rows[useful[j]]; g = gcd(r0[i], r1[i], u, v); for (unsigned k = 0; k < max_column; ++k) { r0[k] = u*r0[k] + v*r1[k]; } SASSERT(g == r0[i]); } if (!abs(r0[i]).is_one()) { return false; } live.erase(live.begin()+live_pos); for (unsigned l : live) { row& r = rows[l]; if (!r[i].is_zero()) { substitute(r, r0, i); gcd_normalize(r); if (!gcd_test(r)) { unsat_row = r; TRACE("arith_eq_solver", print_row(tout << "unsat: ", unsat_row); ); return false; } } } } } TRACE("arith_eq_solver", tout << ((live.size()<=1)?"solved ":"incomplete check ") << live.size() << "\n"; for (unsigned l : live) print_row(tout, rows[l]); ); return true; } z3-z3-4.8.7/src/smt/arith_eq_solver.h000066400000000000000000000051271356505360400173650ustar00rootroot00000000000000/*++ Copyright (c) 2007 Microsoft Corporation Module Name: arith_eq_solver.h Abstract: Solver for linear arithmetic equalities. Author: Nikolaj Bjorner (nbjorner) 2012-02-25 --*/ #ifndef ARITH_EQ_SOLVER_H_ #define ARITH_EQ_SOLVER_H_ #include "ast/arith_decl_plugin.h" #include "ast/rewriter/arith_rewriter.h" /** \brief Simplifier for the arith family. */ class arith_eq_solver { typedef rational numeral; ast_manager& m; params_ref m_params; arith_util m_util; arith_rewriter m_arith_rewriter; bool is_neg_poly(expr * t) const; void prop_mod_const(expr * e, unsigned depth, numeral const& k, expr_ref& result); bool gcd_test(vector& values); unsigned find_abs_min(vector& values); void gcd_normalize(vector& values); void substitute(vector& r, vector const& s, unsigned index); bool solve_integer_equations_units( vector > & rows, vector& unsat_row ); bool solve_integer_equations_omega( vector > & rows, vector& unsat_row ); void compute_hnf(vector >& A); bool solve_integer_equations_hermite( vector > & rows, vector& unsat_row ); bool solve_integer_equations_gcd( vector > & rows, vector& unsat_row ); public: arith_eq_solver(ast_manager & m, params_ref const& p = params_ref()); ~arith_eq_solver() = default; // Integer linear solver for a single equation. // The array values contains integer coefficients // // Determine integer solutions to: // // a+k = 0 // // where a = sum_i a_i*k_i // typedef vector row; typedef vector matrix; bool solve_integer_equation( row& values, unsigned& index, bool& is_fresh ); // Integer linear solver. // Determine integer solutions to: // // a+k = 0 // // where a = sum_i a_i*k_i // // Solution, if there is any, is returned as a substitution. // The return value is "true". // If there is no solution, then return "false". // together with equality "eq_unsat", such that // // eq_unsat = 0 // // is implied and is unsatisfiable over the integers. // bool solve_integer_equations(vector& rows, row& unsat_row); }; #endif /* ARITH_EQ_SOLVER_H_ */ z3-z3-4.8.7/src/smt/asserted_formulas.cpp000066400000000000000000000466661356505360400202710ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: asserted_formulas.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-06-11. Revision History: --*/ #include "util/warning.h" #include "ast/ast_ll_pp.h" #include "ast/ast_pp.h" #include "ast/for_each_expr.h" #include "ast/well_sorted.h" #include "ast/rewriter/rewriter_def.h" #include "ast/normal_forms/nnf.h" #include "ast/pattern/pattern_inference.h" #include "ast/macros/quasi_macros.h" #include "ast/occurs.h" #include "smt/asserted_formulas.h" asserted_formulas::asserted_formulas(ast_manager & m, smt_params & sp, params_ref const& p): m(m), m_smt_params(sp), m_params(p), m_rewriter(m), m_substitution(m), m_scoped_substitution(m_substitution), m_defined_names(m), m_static_features(m), m_qhead(0), m_macro_manager(m), m_bv_sharing(m), m_inconsistent(false), m_has_quantifiers(false), m_reduce_asserted_formulas(*this), m_distribute_forall(*this), m_pattern_inference(*this), m_refine_inj_axiom(*this), m_max_bv_sharing_fn(*this), m_elim_term_ite(*this), m_pull_nested_quantifiers(*this), m_elim_bvs_from_quantifiers(*this), m_cheap_quant_fourier_motzkin(*this), m_apply_bit2int(*this), m_lift_ite(*this), m_ng_lift_ite(*this), m_find_macros(*this), m_propagate_values(*this), m_nnf_cnf(*this), m_apply_quasi_macros(*this) { m_macro_finder = alloc(macro_finder, m, m_macro_manager); m_elim_and = true; set_eliminate_and(false); } void asserted_formulas::setup() { switch (m_smt_params.m_lift_ite) { case LI_FULL: m_smt_params.m_ng_lift_ite = LI_NONE; break; case LI_CONSERVATIVE: if (m_smt_params.m_ng_lift_ite == LI_CONSERVATIVE) m_smt_params.m_ng_lift_ite = LI_NONE; break; default: break; } if (m_smt_params.m_relevancy_lvl == 0) m_smt_params.m_relevancy_lemma = false; } asserted_formulas::~asserted_formulas() { } void asserted_formulas::push_assertion(expr * e, proof * pr, vector& result) { if (inconsistent()) { return; } expr* e1 = nullptr; if (m.is_false(e)) { result.push_back(justified_expr(m, e, pr)); m_inconsistent = true; } else if (m.is_true(e)) { // skip } else if (m.is_and(e)) { for (unsigned i = 0; i < to_app(e)->get_num_args(); ++i) { expr* arg = to_app(e)->get_arg(i); proof_ref _pr(m.proofs_enabled() ? m.mk_and_elim(pr, i) : nullptr, m); push_assertion(arg, _pr, result); } } else if (m.is_not(e, e1) && m.is_or(e1)) { for (unsigned i = 0; i < to_app(e1)->get_num_args(); ++i) { expr* arg = to_app(e1)->get_arg(i); proof_ref _pr(m.proofs_enabled() ? m.mk_not_or_elim(pr, i) : nullptr, m); expr_ref narg(mk_not(m, arg), m); push_assertion(narg, _pr, result); } } else { result.push_back(justified_expr(m, e, pr)); } } void asserted_formulas::updt_params(params_ref const& p) { m_params.append(p); } void asserted_formulas::set_eliminate_and(bool flag) { if (flag == m_elim_and) return; m_elim_and = flag; if (m_smt_params.m_pull_cheap_ite) m_params.set_bool("pull_cheap_ite", true); m_params.set_bool("elim_and", flag); m_params.set_bool("arith_ineq_lhs", true); m_params.set_bool("sort_sums", true); m_params.set_bool("rewrite_patterns", true); m_params.set_bool("eq2ineq", m_smt_params.m_arith_eq2ineq); m_params.set_bool("gcd_rounding", true); m_params.set_bool("expand_select_store", true); //m_params.set_bool("expand_nested_stores", true); m_params.set_bool("bv_sort_ac", true); m_params.set_bool("som", true); m_rewriter.updt_params(m_params); flush_cache(); } void asserted_formulas::assert_expr(expr * e, proof * _in_pr) { proof_ref in_pr(_in_pr, m), pr(_in_pr, m); expr_ref r(e, m); if (inconsistent()) return; if (m_smt_params.m_preprocess) { TRACE("assert_expr_bug", tout << r << "\n";); set_eliminate_and(false); // do not eliminate and before nnf. m_rewriter(e, r, pr); if (m.proofs_enabled()) { if (e == r) pr = in_pr; else pr = m.mk_modus_ponens(in_pr, pr); } TRACE("assert_expr_bug", tout << "after...\n" << r << "\n";); } m_has_quantifiers |= ::has_quantifiers(e); push_assertion(r, pr, m_formulas); TRACE("asserted_formulas_bug", tout << "after assert_expr\n"; display(tout);); } void asserted_formulas::assert_expr(expr * e) { assert_expr(e, m.proofs_enabled() ? m.mk_asserted(e) : nullptr); } void asserted_formulas::get_assertions(ptr_vector & result) const { for (justified_expr const& je : m_formulas) result.push_back(je.get_fml()); } void asserted_formulas::push_scope() { SASSERT(inconsistent() || m_qhead == m_formulas.size() || m.canceled()); TRACE("asserted_formulas_scopes", tout << "before push: " << m_scopes.size() << "\n";); m_scoped_substitution.push(); m_scopes.push_back(scope()); scope & s = m_scopes.back(); s.m_formulas_lim = m_formulas.size(); SASSERT(inconsistent() || s.m_formulas_lim == m_qhead || m.canceled()); s.m_inconsistent_old = m_inconsistent; m_defined_names.push(); m_elim_term_ite.push(); m_bv_sharing.push_scope(); m_macro_manager.push_scope(); commit(); TRACE("asserted_formulas_scopes", tout << "after push: " << m_scopes.size() << "\n";); } void asserted_formulas::pop_scope(unsigned num_scopes) { TRACE("asserted_formulas_scopes", tout << "before pop " << num_scopes << " of " << m_scopes.size() << "\n";); m_bv_sharing.pop_scope(num_scopes); m_macro_manager.pop_scope(num_scopes); unsigned new_lvl = m_scopes.size() - num_scopes; scope & s = m_scopes[new_lvl]; m_inconsistent = s.m_inconsistent_old; m_defined_names.pop(num_scopes); m_elim_term_ite.pop(num_scopes); m_scoped_substitution.pop(num_scopes); m_formulas.shrink(s.m_formulas_lim); m_qhead = s.m_formulas_lim; m_scopes.shrink(new_lvl); flush_cache(); TRACE("asserted_formulas_scopes", tout << "after pop " << num_scopes << "\n";); } void asserted_formulas::reset() { m_defined_names.reset(); m_qhead = 0; m_formulas.reset(); m_macro_manager.reset(); m_bv_sharing.reset(); m_rewriter.reset(); m_inconsistent = false; } void asserted_formulas::finalize() { reset(); m_substitution.cleanup(); } bool asserted_formulas::check_well_sorted() const { for (justified_expr const& je : m_formulas) { if (!is_well_sorted(m, je.get_fml())) return false; } return true; } void asserted_formulas::reduce() { if (inconsistent()) return; if (canceled()) return; if (m_qhead == m_formulas.size()) return; if (!m_smt_params.m_preprocess) return; if (m_macro_manager.has_macros()) invoke(m_find_macros); TRACE("before_reduce", display(tout);); CASSERT("well_sorted", check_well_sorted()); set_eliminate_and(false); // do not eliminate and before nnf. if (!invoke(m_propagate_values)) return; if (!invoke(m_find_macros)) return; if (!invoke(m_nnf_cnf)) return; set_eliminate_and(true); if (!invoke(m_reduce_asserted_formulas)) return; if (!invoke(m_pull_nested_quantifiers)) return; if (!invoke(m_lift_ite)) return; if (!invoke(m_ng_lift_ite)) return; if (!invoke(m_elim_term_ite)) return; if (!invoke(m_refine_inj_axiom)) return; if (!invoke(m_distribute_forall)) return; if (!invoke(m_find_macros)) return; if (!invoke(m_apply_quasi_macros)) return; if (!invoke(m_apply_bit2int)) return; if (!invoke(m_cheap_quant_fourier_motzkin)) return; if (!invoke(m_pattern_inference)) return; if (!invoke(m_max_bv_sharing_fn)) return; if (!invoke(m_elim_bvs_from_quantifiers)) return; if (!invoke(m_reduce_asserted_formulas)) return; // if (!invoke(m_propagate_values)) return; IF_VERBOSE(10, verbose_stream() << "(smt.simplifier-done)\n";); TRACE("after_reduce", display(tout);); TRACE("after_reduce_ll", ast_mark visited; display_ll(tout, visited);); TRACE("macros", m_macro_manager.display(tout);); flush_cache(); CASSERT("well_sorted",check_well_sorted()); } unsigned asserted_formulas::get_formulas_last_level() const { if (m_scopes.empty()) { return 0; } else { return m_scopes.back().m_formulas_lim; } } bool asserted_formulas::invoke(simplify_fmls& s) { if (!s.should_apply()) return true; IF_VERBOSE(10, verbose_stream() << "(smt." << s.id() << ")\n";); s(); IF_VERBOSE(10000, verbose_stream() << "total size: " << get_total_size() << "\n";); TRACE("reduce_step_ll", ast_mark visited; display_ll(tout, visited);); CASSERT("well_sorted",check_well_sorted()); if (inconsistent() || canceled()) { TRACE("after_reduce", display(tout);); TRACE("after_reduce_ll", ast_mark visited; display_ll(tout, visited);); return false; } else { return true; } } void asserted_formulas::display(std::ostream & out) const { out << "asserted formulas:\n"; for (unsigned i = 0; i < m_formulas.size(); i++) { if (i == m_qhead) out << "[HEAD] ==>\n"; out << mk_pp(m_formulas[i].get_fml(), m) << "\n"; } out << "inconsistent: " << inconsistent() << "\n"; } void asserted_formulas::display_ll(std::ostream & out, ast_mark & pp_visited) const { if (!m_formulas.empty()) { for (justified_expr const& f : m_formulas) ast_def_ll_pp(out, m, f.get_fml(), pp_visited, true, false); out << "asserted formulas:\n"; for (justified_expr const& f : m_formulas) out << "#" << f.get_fml()->get_id() << " "; out << "\n"; } } void asserted_formulas::collect_statistics(statistics & st) const { } void asserted_formulas::swap_asserted_formulas(vector& formulas) { SASSERT(!inconsistent() || !formulas.empty()); m_formulas.shrink(m_qhead); m_formulas.append(formulas); } void asserted_formulas::find_macros_core() { vector new_fmls; unsigned sz = m_formulas.size(); (*m_macro_finder)(sz - m_qhead, m_formulas.c_ptr() + m_qhead, new_fmls); swap_asserted_formulas(new_fmls); reduce_and_solve(); } void asserted_formulas::apply_quasi_macros() { TRACE("before_quasi_macros", display(tout);); vector new_fmls; quasi_macros proc(m, m_macro_manager); while (proc(m_formulas.size() - m_qhead, m_formulas.c_ptr() + m_qhead, new_fmls)) { swap_asserted_formulas(new_fmls); new_fmls.reset(); } TRACE("after_quasi_macros", display(tout);); reduce_and_solve(); } void asserted_formulas::nnf_cnf() { nnf apply_nnf(m, m_defined_names); vector new_fmls; expr_ref_vector push_todo(m); proof_ref_vector push_todo_prs(m); unsigned i = m_qhead; unsigned sz = m_formulas.size(); TRACE("nnf_bug", tout << "i: " << i << " sz: " << sz << "\n";); for (; i < sz; i++) { expr * n = m_formulas[i].get_fml(); TRACE("nnf_bug", tout << "processing:\n" << mk_pp(n, m) << "\n";); proof * pr = m_formulas[i].get_proof(); expr_ref r1(m); proof_ref pr1(m); push_todo.reset(); push_todo_prs.reset(); CASSERT("well_sorted", is_well_sorted(m, n)); apply_nnf(n, push_todo, push_todo_prs, r1, pr1); CASSERT("well_sorted",is_well_sorted(m, r1)); pr = m.proofs_enabled() ? m.mk_modus_ponens(pr, pr1) : nullptr; push_todo.push_back(r1); push_todo_prs.push_back(pr); if (canceled()) { return; } unsigned sz2 = push_todo.size(); for (unsigned k = 0; k < sz2; k++) { expr * n = push_todo.get(k); pr = nullptr; m_rewriter(n, r1, pr1); CASSERT("well_sorted",is_well_sorted(m, r1)); if (canceled()) { return; } if (m.proofs_enabled()) pr = m.mk_modus_ponens(push_todo_prs.get(k), pr1); push_assertion(r1, pr, new_fmls); } } swap_asserted_formulas(new_fmls); } void asserted_formulas::simplify_fmls::operator()() { vector new_fmls; unsigned sz = af.m_formulas.size(); for (unsigned i = af.m_qhead; i < sz; i++) { auto& j = af.m_formulas[i]; expr_ref result(m); proof_ref result_pr(m); simplify(j, result, result_pr); if (m.proofs_enabled()) { if (!result_pr) result_pr = m.mk_rewrite(j.get_fml(), result); result_pr = m.mk_modus_ponens(j.get_proof(), result_pr); } if (j.get_fml() == result) { new_fmls.push_back(j); } else { af.push_assertion(result, result_pr, new_fmls); } if (af.canceled()) return; } af.swap_asserted_formulas(new_fmls); TRACE("asserted_formulas", af.display(tout);); post_op(); } void asserted_formulas::reduce_and_solve() { IF_VERBOSE(10, verbose_stream() << "(smt.reducing)\n";); flush_cache(); // collect garbage m_reduce_asserted_formulas(); } void asserted_formulas::commit() { commit(m_formulas.size()); } void asserted_formulas::commit(unsigned new_qhead) { m_macro_manager.mark_forbidden(new_qhead - m_qhead, m_formulas.c_ptr() + m_qhead); m_expr2depth.reset(); for (unsigned i = m_qhead; i < new_qhead; ++i) { justified_expr const& j = m_formulas[i]; update_substitution(j.get_fml(), j.get_proof()); } m_qhead = new_qhead; } void asserted_formulas::propagate_values() { flush_cache(); unsigned num_prop = 0; unsigned num_iterations = 0; while (!inconsistent() && ++num_iterations < 2) { m_expr2depth.reset(); m_scoped_substitution.push(); unsigned prop = num_prop; TRACE("propagate_values", display(tout << "before:\n");); unsigned i = m_qhead; unsigned sz = m_formulas.size(); for (; i < sz; i++) { prop += propagate_values(i); } flush_cache(); m_scoped_substitution.pop(1); m_expr2depth.reset(); m_scoped_substitution.push(); TRACE("propagate_values", tout << "middle:\n"; display(tout);); i = sz; while (i > m_qhead) { --i; prop += propagate_values(i); } m_scoped_substitution.pop(1); flush_cache(); TRACE("propagate_values", tout << "after:\n"; display(tout);); if (num_prop == prop) { break; } num_prop = prop; } TRACE("asserted_formulas", tout << num_prop << "\n";); if (num_prop > 0) m_reduce_asserted_formulas(); } unsigned asserted_formulas::propagate_values(unsigned i) { expr_ref n(m_formulas[i].get_fml(), m); expr_ref new_n(m); proof_ref new_pr(m); m_rewriter(n, new_n, new_pr); 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.is_false(j.get_fml())) { m_inconsistent = true; } update_substitution(new_n, new_pr); return n != new_n ? 1 : 0; } bool asserted_formulas::update_substitution(expr* n, proof* pr) { expr* lhs, *rhs, *n1; proof_ref pr1(m); if (is_ground(n) && m.is_eq(n, lhs, rhs)) { compute_depth(lhs); compute_depth(rhs); if (is_gt(lhs, rhs)) { TRACE("propagate_values", tout << "insert " << mk_pp(lhs, m) << " -> " << mk_pp(rhs, m) << "\n";); m_scoped_substitution.insert(lhs, rhs, pr); return true; } if (is_gt(rhs, lhs)) { TRACE("propagate_values", tout << "insert " << mk_pp(rhs, m) << " -> " << mk_pp(lhs, m) << "\n";); pr1 = m.proofs_enabled() ? m.mk_symmetry(pr) : nullptr; m_scoped_substitution.insert(rhs, lhs, pr1); return true; } TRACE("propagate_values", tout << "incompatible " << mk_pp(n, m) << "\n";); } if (m.is_not(n, n1)) { pr1 = m.proofs_enabled() ? m.mk_iff_false(pr) : nullptr; m_scoped_substitution.insert(n1, m.mk_false(), pr1); } else { pr1 = m.proofs_enabled() ? m.mk_iff_true(pr) : nullptr; m_scoped_substitution.insert(n, m.mk_true(), pr1); } return false; } /** \brief implement a Knuth-Bendix ordering on expressions. */ bool asserted_formulas::is_gt(expr* lhs, expr* rhs) { if (lhs == rhs) { return false; } // values are always less in ordering than non-values. bool v1 = m.is_value(lhs); bool v2 = m.is_value(rhs); if (!v1 && v2) { return true; } if (v1 && !v2) { return false; } SASSERT(is_ground(lhs) && is_ground(rhs)); if (depth(lhs) > depth(rhs)) { return true; } if (depth(lhs) == depth(rhs) && is_app(lhs) && is_app(rhs)) { app* l = to_app(lhs); app* r = to_app(rhs); if (l->get_decl()->get_id() != r->get_decl()->get_id()) { return l->get_decl()->get_id() > r->get_decl()->get_id(); } if (l->get_num_args() != r->get_num_args()) { return l->get_num_args() > r->get_num_args(); } for (unsigned i = 0; i < l->get_num_args(); ++i) { if (l->get_arg(i) != r->get_arg(i)) { return is_gt(l->get_arg(i), r->get_arg(i)); } } UNREACHABLE(); } return false; } void asserted_formulas::compute_depth(expr* e) { ptr_vector todo; todo.push_back(e); while (!todo.empty()) { e = todo.back(); unsigned d = 0; if (m_expr2depth.contains(e)) { todo.pop_back(); continue; } if (is_app(e)) { app* a = to_app(e); bool visited = true; for (expr* arg : *a) { unsigned d1 = 0; if (m_expr2depth.find(arg, d1)) { d = std::max(d, d1); } else { visited = false; todo.push_back(arg); } } if (!visited) { continue; } } todo.pop_back(); m_expr2depth.insert(e, d + 1); } } proof * asserted_formulas::get_inconsistency_proof() const { if (!inconsistent()) return nullptr; if (!m.proofs_enabled()) return nullptr; for (justified_expr const& j : m_formulas) { if (m.is_false(j.get_fml())) return j.get_proof(); } UNREACHABLE(); return nullptr; } void asserted_formulas::refine_inj_axiom_fn::simplify(justified_expr const& j, expr_ref& n, proof_ref& p) { expr* f = j.get_fml(); if (is_quantifier(f) && simplify_inj_axiom(m, to_quantifier(f), n)) { TRACE("inj_axiom", tout << "simplifying...\n" << mk_pp(f, m) << "\n" << n << "\n";); } else { n = j.get_fml(); } } unsigned asserted_formulas::get_total_size() const { expr_mark visited; unsigned r = 0; for (justified_expr const& j : m_formulas) r += get_num_exprs(j.get_fml(), visited); return r; } #ifdef Z3DEBUG void pp(asserted_formulas & f) { f.display(std::cout); } #endif z3-z3-4.8.7/src/smt/asserted_formulas.h000066400000000000000000000311541356505360400177200ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: asserted_formulas.h Abstract: Author: Leonardo de Moura (leonardo) 2008-06-11. Revision History: --*/ #ifndef ASSERTED_FORMULAS_H_ #define ASSERTED_FORMULAS_H_ #include "util/statistics.h" #include "ast/static_features.h" #include "ast/expr_substitution.h" #include "ast/rewriter/th_rewriter.h" #include "ast/rewriter/bit2int.h" #include "ast/rewriter/maximize_ac_sharing.h" #include "ast/rewriter/distribute_forall.h" #include "ast/rewriter/push_app_ite.h" #include "ast/rewriter/inj_axiom.h" #include "ast/rewriter/bv_elim.h" #include "ast/rewriter/der.h" #include "ast/rewriter/elim_bounds.h" #include "ast/macros/macro_manager.h" #include "ast/macros/macro_finder.h" #include "ast/normal_forms/defined_names.h" #include "ast/normal_forms/pull_quant.h" #include "ast/pattern/pattern_inference.h" #include "smt/params/smt_params.h" #include "smt/elim_term_ite.h" class asserted_formulas { ast_manager & m; smt_params & m_smt_params; params_ref m_params; th_rewriter m_rewriter; expr_substitution m_substitution; scoped_expr_substitution m_scoped_substitution; defined_names m_defined_names; static_features m_static_features; vector m_formulas; unsigned m_qhead; bool m_elim_and; macro_manager m_macro_manager; scoped_ptr m_macro_finder; maximize_bv_sharing_rw m_bv_sharing; bool m_inconsistent; bool m_has_quantifiers; struct scope { unsigned m_formulas_lim; bool m_inconsistent_old; }; svector m_scopes; obj_map m_expr2depth; class simplify_fmls { protected: asserted_formulas& af; ast_manager& m; char const* m_id; public: simplify_fmls(asserted_formulas& af, char const* id): af(af), m(af.m), m_id(id) {} char const* id() const { return m_id; } virtual void simplify(justified_expr const& j, expr_ref& n, proof_ref& p) = 0; virtual bool should_apply() const { return true;} virtual void post_op() {} virtual void operator()(); }; class reduce_asserted_formulas_fn : public simplify_fmls { public: reduce_asserted_formulas_fn(asserted_formulas& af): simplify_fmls(af, "reduce-asserted") {} void simplify(justified_expr const& j, expr_ref& n, proof_ref& p) override { af.m_rewriter(j.get_fml(), n, p); } }; class find_macros_fn : public simplify_fmls { public: find_macros_fn(asserted_formulas& af): simplify_fmls(af, "find-macros") {} void operator()() override { af.find_macros_core(); } bool should_apply() const override { return af.m_smt_params.m_macro_finder && af.has_quantifiers(); } void simplify(justified_expr const& j, expr_ref& n, proof_ref& p) override { UNREACHABLE(); } }; class apply_quasi_macros_fn : public simplify_fmls { public: apply_quasi_macros_fn(asserted_formulas& af): simplify_fmls(af, "find-quasi-macros") {} void operator()() override { af.apply_quasi_macros(); } bool should_apply() const override { return af.m_smt_params.m_quasi_macros && af.has_quantifiers(); } void simplify(justified_expr const& j, expr_ref& n, proof_ref& p) override { UNREACHABLE(); } }; class nnf_cnf_fn : public simplify_fmls { public: nnf_cnf_fn(asserted_formulas& af): simplify_fmls(af, "nnf-cnf") {} void operator()() override { af.nnf_cnf(); } bool should_apply() const override { return af.m_smt_params.m_nnf_cnf || (af.m_smt_params.m_mbqi && af.has_quantifiers()); } void simplify(justified_expr const& j, expr_ref& n, proof_ref& p) override { UNREACHABLE(); } }; class propagate_values_fn : public simplify_fmls { public: propagate_values_fn(asserted_formulas& af): simplify_fmls(af, "propagate-values") {} void operator()() override { af.propagate_values(); } bool should_apply() const override { return af.m_smt_params.m_propagate_values; } void simplify(justified_expr const& j, expr_ref& n, proof_ref& p) override { UNREACHABLE(); } }; class distribute_forall_fn : public simplify_fmls { distribute_forall m_functor; public: distribute_forall_fn(asserted_formulas& af): simplify_fmls(af, "distribute-forall"), m_functor(af.m) {} void simplify(justified_expr const& j, expr_ref& n, proof_ref& p) override { m_functor(j.get_fml(), n); } bool should_apply() const override { return af.m_smt_params.m_distribute_forall && af.has_quantifiers(); } void post_op() override { af.reduce_and_solve(); TRACE("asserted_formulas", af.display(tout);); } }; class pattern_inference_fn : public simplify_fmls { pattern_inference_rw m_infer; public: pattern_inference_fn(asserted_formulas& af): simplify_fmls(af, "pattern-inference"), m_infer(af.m, af.m_smt_params) {} void simplify(justified_expr const& j, expr_ref& n, proof_ref& p) override { m_infer(j.get_fml(), n, p); } bool should_apply() const override { return af.m_smt_params.m_ematching && af.has_quantifiers(); } }; class refine_inj_axiom_fn : public simplify_fmls { public: refine_inj_axiom_fn(asserted_formulas& af): simplify_fmls(af, "refine-injectivity") {} void simplify(justified_expr const& j, expr_ref& n, proof_ref& p) override; bool should_apply() const override { return af.m_smt_params.m_refine_inj_axiom && af.has_quantifiers(); } }; class max_bv_sharing_fn : public simplify_fmls { public: max_bv_sharing_fn(asserted_formulas& af): simplify_fmls(af, "maximizing-bv-sharing") {} void simplify(justified_expr const& j, expr_ref& n, proof_ref& p) override { af.m_bv_sharing(j.get_fml(), n, p); } bool should_apply() const override { return af.m_smt_params.m_max_bv_sharing; } void post_op() override { af.m_reduce_asserted_formulas(); } }; class elim_term_ite_fn : public simplify_fmls { elim_term_ite_rw m_elim; public: elim_term_ite_fn(asserted_formulas& af): simplify_fmls(af, "elim-term-ite"), m_elim(af.m, af.m_defined_names) {} void simplify(justified_expr const& j, expr_ref& n, proof_ref& p) override { m_elim(j.get_fml(), n, p); } bool should_apply() const override { return af.m_smt_params.m_eliminate_term_ite && af.m_smt_params.m_lift_ite != LI_FULL; } void post_op() override { af.m_formulas.append(m_elim.new_defs()); af.reduce_and_solve(); m_elim.reset(); } void push() { m_elim.push(); } void pop(unsigned n) { m_elim.pop(n); } }; #define MK_SIMPLIFIERA(NAME, FUNCTOR, MSG, APP, ARG, REDUCE) \ class NAME : public simplify_fmls { \ FUNCTOR m_functor; \ public: \ NAME(asserted_formulas& af):simplify_fmls(af, MSG), m_functor ARG {} \ virtual void simplify(justified_expr const& j, expr_ref& n, proof_ref& p) { \ m_functor(j.get_fml(), n, p); \ } \ virtual void post_op() { if (REDUCE) af.reduce_and_solve(); } \ virtual bool should_apply() const { return APP; } \ }; \ #define MK_SIMPLIFIERF(NAME, FUNCTOR, MSG, APP, REDUCE) MK_SIMPLIFIERA(NAME, FUNCTOR, MSG, APP, (af.m), REDUCE) MK_SIMPLIFIERF(pull_nested_quantifiers, pull_nested_quant, "pull-nested-quantifiers", af.m_smt_params.m_pull_nested_quantifiers && af.has_quantifiers(), false); MK_SIMPLIFIERF(cheap_quant_fourier_motzkin, elim_bounds_rw, "cheap-fourier-motzkin", af.m_smt_params.m_eliminate_bounds && af.has_quantifiers(), true); MK_SIMPLIFIERF(elim_bvs_from_quantifiers, bv_elim_rw, "eliminate-bit-vectors-from-quantifiers", af.m_smt_params.m_bb_quantifiers, true); MK_SIMPLIFIERF(apply_bit2int, bit2int, "propagate-bit-vector-over-integers", af.m_smt_params.m_simplify_bit2int, true); MK_SIMPLIFIERA(lift_ite, push_app_ite_rw, "lift-ite", af.m_smt_params.m_lift_ite != LI_NONE, (af.m, af.m_smt_params.m_lift_ite == LI_CONSERVATIVE), true); MK_SIMPLIFIERA(ng_lift_ite, ng_push_app_ite_rw, "lift-ite", af.m_smt_params.m_ng_lift_ite != LI_NONE, (af.m, af.m_smt_params.m_ng_lift_ite == LI_CONSERVATIVE), true); reduce_asserted_formulas_fn m_reduce_asserted_formulas; distribute_forall_fn m_distribute_forall; pattern_inference_fn m_pattern_inference; refine_inj_axiom_fn m_refine_inj_axiom; max_bv_sharing_fn m_max_bv_sharing_fn; elim_term_ite_fn m_elim_term_ite; pull_nested_quantifiers m_pull_nested_quantifiers; elim_bvs_from_quantifiers m_elim_bvs_from_quantifiers; cheap_quant_fourier_motzkin m_cheap_quant_fourier_motzkin; apply_bit2int m_apply_bit2int; lift_ite m_lift_ite; ng_lift_ite m_ng_lift_ite; find_macros_fn m_find_macros; propagate_values_fn m_propagate_values; nnf_cnf_fn m_nnf_cnf; apply_quasi_macros_fn m_apply_quasi_macros; bool invoke(simplify_fmls& s); void swap_asserted_formulas(vector& new_fmls); void push_assertion(expr * e, proof * pr, vector& result); bool canceled() { return m.canceled(); } bool check_well_sorted() const; unsigned get_total_size() const; void find_macros_core(); void expand_macros(); void apply_quasi_macros(); void nnf_cnf(); void reduce_and_solve(); void flush_cache() { m_rewriter.reset(); m_rewriter.set_substitution(&m_substitution); } void set_eliminate_and(bool flag); void propagate_values(); unsigned propagate_values(unsigned i); bool update_substitution(expr* n, proof* p); bool is_gt(expr* lhs, expr* rhs); void compute_depth(expr* e); unsigned depth(expr* e) { return m_expr2depth[e]; } void init(unsigned num_formulas, expr * const * formulas, proof * const * prs); public: asserted_formulas(ast_manager & m, smt_params & smtp, params_ref const& p); ~asserted_formulas(); void finalize(); void updt_params(params_ref const& p); bool has_quantifiers() const { return m_has_quantifiers; } void setup(); void assert_expr(expr * e, proof * in_pr); void assert_expr(expr * e); void reset(); void push_scope(); void pop_scope(unsigned num_scopes); bool inconsistent() const { return m_inconsistent; } proof * get_inconsistency_proof() const; void reduce(); unsigned get_num_formulas() const { return m_formulas.size(); } unsigned get_formulas_last_level() const; unsigned get_qhead() const { return m_qhead; } void commit(); void commit(unsigned new_qhead); expr * get_formula(unsigned idx) const { return m_formulas[idx].get_fml(); } proof * get_formula_proof(unsigned idx) const { return m_formulas[idx].get_proof(); } params_ref const& get_params() const { return m_params; } void get_assertions(ptr_vector & result) const; bool empty() const { return m_formulas.empty(); } void display(std::ostream & out) const; void display_ll(std::ostream & out, ast_mark & pp_visited) const; void collect_statistics(statistics & st) const; // ----------------------------------- // // Macros // // ----------------------------------- unsigned get_num_macros() const { return m_macro_manager.get_num_macros(); } unsigned get_first_macro_last_level() const { return m_macro_manager.get_first_macro_last_level(); } func_decl * get_macro_func_decl(unsigned i) const { return m_macro_manager.get_macro_func_decl(i); } func_decl * get_macro_interpretation(unsigned i, expr_ref & interp) const { return m_macro_manager.get_macro_interpretation(i, interp); } quantifier * get_macro_quantifier(func_decl * f) const { return m_macro_manager.get_macro_quantifier(f); } // auxiliary function used to create a logic context based on a model. void insert_macro(func_decl * f, quantifier * m, proof * pr) { m_macro_manager.insert(f, m, pr); } void insert_macro(func_decl * f, quantifier * m, proof * pr, expr_dependency* dep) { m_macro_manager.insert(f, m, pr, dep); } }; #endif /* ASSERTED_FORMULAS_H_ */ z3-z3-4.8.7/src/smt/cached_var_subst.cpp000066400000000000000000000046011356505360400200250ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: cached_var_subst.cpp Abstract: Author: Leonardo de Moura (leonardo) 2009-01-23. Revision History: --*/ #include "smt/cached_var_subst.h" bool cached_var_subst::key_eq_proc::operator()(cached_var_subst::key * k1, cached_var_subst::key * k2) const { if (k1->m_qa != k2->m_qa) return false; if (k1->m_num_bindings != k2->m_num_bindings) return false; for (unsigned i = 0; i < k1->m_num_bindings; i++) if (k1->m_bindings[i] != k2->m_bindings[i]) return false; return true; } cached_var_subst::cached_var_subst(ast_manager & m): m_proc(m), m_refs(m) { } void cached_var_subst::reset() { m_refs.reset(); m_instances.reset(); m_region.reset(); m_new_keys.reset(); } void cached_var_subst::operator()(quantifier * qa, unsigned num_bindings, smt::enode * const * bindings, expr_ref & result) { m_new_keys.reserve(num_bindings+1, 0); key * new_key = m_new_keys[num_bindings]; if (new_key == nullptr) new_key = static_cast(m_region.allocate(sizeof(key) + sizeof(expr*)*num_bindings)); new_key->m_qa = qa; new_key->m_num_bindings = num_bindings; for (unsigned i = 0; i < num_bindings; i++) new_key->m_bindings[i] = bindings[i]->get_owner(); instances::entry * entry = m_instances.insert_if_not_there2(new_key, nullptr); if (entry->get_data().m_key != new_key) { SASSERT(entry->get_data().m_value != 0); // entry was already there m_new_keys[num_bindings] = new_key; // recycle key result = entry->get_data().m_value; return; } SASSERT(entry->get_data().m_value == 0); try { result = m_proc(qa->get_expr(), new_key->m_num_bindings, new_key->m_bindings); } catch (...) { // CMW: The var_subst reducer was interrupted and m_instances is // in an inconsistent state; we need to remove (new_key, 0). m_instances.remove(new_key); throw; // Throw on to smt::qi_queue/smt::solver. } // cache result entry->get_data().m_value = result; // remove key from cache m_new_keys[num_bindings] = 0; // increment reference counters m_refs.push_back(qa); for (unsigned i = 0; i < new_key->m_num_bindings; i++) m_refs.push_back(new_key->m_bindings[i]); m_refs.push_back(result); } z3-z3-4.8.7/src/smt/cached_var_subst.h000066400000000000000000000023661356505360400175000ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: cached_var_subst.h Abstract: Author: Leonardo de Moura (leonardo) 2009-01-23. Revision History: --*/ #ifndef CACHED_VAR_SUBST_H_ #define CACHED_VAR_SUBST_H_ #include "ast/rewriter/var_subst.h" #include "util/map.h" #include "smt/smt_enode.h" class cached_var_subst { struct key { quantifier * m_qa; unsigned m_num_bindings; expr * m_bindings[0]; }; struct key_hash_proc { unsigned operator()(key * k) const { return string_hash(reinterpret_cast(k->m_bindings), sizeof(expr *) * k->m_num_bindings, k->m_qa->get_id()); } }; struct key_eq_proc { bool operator()(key * k1, key * k2) const; }; typedef map instances; var_subst m_proc; expr_ref_vector m_refs; instances m_instances; region m_region; ptr_vector m_new_keys; // mapping from num_bindings -> next key public: cached_var_subst(ast_manager & m); void operator()(quantifier * qa, unsigned num_bindings, smt::enode * const * bindings, expr_ref & result); void reset(); }; #endif /* CACHED_VAR_SUBST_H_ */ z3-z3-4.8.7/src/smt/cost_evaluator.cpp000066400000000000000000000057141356505360400175660ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: cost_evaluator.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-06-14. Revision History: --*/ #include "smt/cost_evaluator.h" #include "util/warning.h" cost_evaluator::cost_evaluator(ast_manager & m): m(m), m_util(m) { } float cost_evaluator::eval(expr * f) const { #define E(IDX) eval(to_app(f)->get_arg(IDX)) if (is_app(f)) { family_id fid = to_app(f)->get_family_id(); if (fid == m.get_basic_family_id()) { switch (to_app(f)->get_decl_kind()) { case OP_TRUE: return 1.0f; case OP_FALSE: return 0.0f; case OP_NOT: return E(0) == 0.0f ? 1.0f : 0.0f; case OP_AND: for (expr* arg : *to_app(f)) if (eval(arg) == 0.0f) return 0.0f; return 1.0f; case OP_OR: for (expr* arg : *to_app(f)) if (eval(arg) != 0.0f) return 1.0f; return 0.0f; case OP_ITE: return E(0) != 0.0f ? E(1) : E(2); case OP_EQ: return E(0) == E(1) ? 1.0f : 0.0f; case OP_XOR: return E(0) != E(1) ? 1.0f : 0.0f; case OP_IMPLIES: if (E(0) == 0.0f) return 1.0f; return E(1) != 0.0f ? 1.0f : 0.0f; default: ; } } else if (fid == m_util.get_family_id()) { switch (to_app(f)->get_decl_kind()) { case OP_NUM: { rational r = to_app(f)->get_decl()->get_parameter(0).get_rational(); return static_cast(numerator(r).get_int64())/static_cast(denominator(r).get_int64()); } case OP_LE: return E(0) <= E(1) ? 1.0f : 0.0f; case OP_GE: return E(0) >= E(1) ? 1.0f : 0.0f; case OP_LT: return E(0) < E(1) ? 1.0f : 0.0f; case OP_GT: return E(0) > E(1) ? 1.0f : 0.0f; case OP_ADD: return E(0) + E(1); case OP_SUB: return E(0) - E(1); case OP_UMINUS: return - E(0); case OP_MUL: return E(0) * E(1); case OP_DIV: { float q = E(1); if (q == 0.0f) { warning_msg("cost function division by zero"); return 1.0f; } return E(0) / q; } default: ; } } } else if (is_var(f)) { unsigned idx = to_var(f)->get_idx(); if (idx < m_num_args) return m_args[m_num_args - idx - 1]; } warning_msg("cost function evaluation error"); return 1.0f; } float cost_evaluator::operator()(expr * f, unsigned num_args, float const * args) { m_num_args = num_args; m_args = args; return eval(f); } z3-z3-4.8.7/src/smt/cost_evaluator.h000066400000000000000000000015461356505360400172320ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: cost_evaluator.h Abstract: Simple evaluator for cost function Author: Leonardo de Moura (leonardo) 2008-06-14. Revision History: --*/ #ifndef COST_EVALUATOR_H_ #define COST_EVALUATOR_H_ #include "ast/ast.h" #include "ast/arith_decl_plugin.h" class cost_evaluator { ast_manager & m; arith_util m_util; unsigned m_num_args; float const * m_args; float eval(expr * f) const; public: cost_evaluator(ast_manager & m); /** I'm using the same standard used in quantifier instantiation. (VAR 0) is stored in the last position of the array. ... (VAR (num_args - 1)) is stored in the first position of the array. */ float operator()(expr * f, unsigned num_args, float const * args); }; #endif /* COST_EVALUATOR_H_ */ z3-z3-4.8.7/src/smt/database.h000066400000000000000000000227571356505360400157530ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ static char const g_pattern_database[] = "(benchmark patterns \n" " :status unknown \n" " :logic ALL \n" " :extrafuns ((?f1 Int Int Int Int) (?f2 Int Int Int) (?f3 Int Int Int) (?f4 Int Int Int)\n" " (?f5 Int Int Int) (?f6 Int Int) (?f7 Int Int) (?f8 Int Int Int) (?f9 Int Int Int)\n" " (?f10 Int) (?f11 Int) (?f12 Int Int) (?f13 Int Int) (?f14 Int Int Int) \n" " (?f15 Int Int) (?f16 Int Int) (?f17 Int Int) (?f18 Int Int) (?f19 Int Int)\n" " (?f20 Int Int) (?f21 Int) (?f22 Int) (?f23 Int) (?f24 Int Int) (?f25 Int Int)\n" " )\n" "\n" " :formula (forall (a Int) (i Int) (e Int) \n" " (= (?f2 (?f1 a i e) i) e)\n" " :pats { (?f1 a i e) }\n" " :weight { 0 })\n" "\n" " :formula (forall (a Int) (i Int) (j Int) (e Int) \n" " (or (= i j) (= (?f2 (?f1 a i e) j) (?f2 a j)))\n" " :pats { (?f2 (?f1 a i e) j) }\n" " :weight { 0 })\n" "\n" " :formula (forall (t0 Int) (t1 Int) (t2 Int) \n" " (or (not (= (?f3 t0 t1) 1))\n" " (not (= (?f3 t1 t2) 1))\n" " (= (?f3 t0 t2) 1))\n" " :pats { (?f3 t0 t1) (?f3 t1 t2) })\n" "\n" " :formula (forall (t0 Int) (t1 Int) \n" " (or (not (= (?f3 t0 t1) 1))\n" " (not (= (?f3 t1 t0) 1))\n" " (= t0 t1))\n" " :pats { (?f3 t0 t1) (?f3 t1 t0) })\n" "\n" " :formula (forall (t0 Int) (t1 Int) (t2 Int) \n" " (or (not (= (?f3 t0 (?f4 t1 t2)) 1))\n" " (= (?f5 t2 t0) (?f4 t1 t2)))\n" " :pats { (?f3 t0 (?f4 t1 t2)) })\n" "\n" " :formula (forall (t Int) \n" " (= (?f25 (?f24 t)) t)\n" " :pats { (?f24 t) })\n" "\n" " :formula (forall (t0 Int) (t1 Int) \n" " (iff (= (?f3 t0 (?f6 t1)) 1)\n" " (not (or (not (= t0 (?f6 (?f7 t0))))\n" " (not (= (?f3 (?f7 t0) t1) 1)))))\n" " :pats { (?f3 t0 (?f6 t1)) })\n" "\n" " :formula (forall (x Int) (t Int) \n" " (or (not (= (?f8 x t) 1))\n" " (= (?f9 x t) x))\n" " :pats { (?f9 x t) })\n" "\n" " :formula (forall (x Int) (t Int) \n" " (or (not (= (?f3 t ?f10) 1))\n" " (iff (= (?f8 x t) 1)\n" " (or (= x ?f11)\n" " (= (?f3 (?f12 x) t) 1))))\n" " :pats { (?f3 t ?f10) (?f8 x t) })\n" "\n" " :formula (forall (e Int) (a Int) (i Int) \n" " (= (?f8 (?f2 (?f2 (?f13 e) a) i)\n" " (?f7 (?f12 a))) 1)\n" " :pats { (?f2 (?f2 (?f13 e) a) i) })\n" "\n" " :formula (forall (x Int) (f Int) (a0 Int) \n" " (or (<= (+ a0 (* -1 (?f15 f))) 0)\n" " (not (= (?f14 x a0) 1))\n" " (= (?f14 (?f2 f x) a0) 1))\n" " :pats { (?f14 (?f2 f x) a0) })\n" "\n" " :formula (forall (a Int) (e Int) (i Int) (a0 Int) \n" " (or (<= (+ a0 (* -1 (?f16 e))) 0)\n" " (not (= (?f14 a a0) 1))\n" " (= (?f14 (?f2 (?f2 e a) i) a0) 1))\n" " :pats { (?f14 (?f2 (?f2 e a) i) a0) })\n" "\n" " :formula (forall (S Int) \n" " (= (?f2 (?f18 S) (?f17 (?f18 S))) 1)\n" " :pats { (?f2 (?f18 S) (?f17 (?f18 S))) })\n" "\n" " :formula (forall (s Int) \n" " (or (not (= 1 (?f19 s)))\n" " (= (?f3 (?f12 s) ?f23) 1))\n" " :pats { (?f19 s) })\n" "\n" " :formula (forall (t Int) \n" " (not (or (= (?f20 t) ?f11)\n" " (not (= (?f8 (?f20 t) ?f21) 1))\n" " (not (= (?f14 (?f20 t) ?f22) 1))))\n" " :pats { (?f20 t) })\n" "\n" " :extrafuns ((?f26 Int Int Int Int) \n" " (?f27 Int Int Int Int Int))\n" " \n" " :formula (forall (A Int) (o Int) (f Int) (v Int)\n" " (= (?f26 (?f27 A o f v) o f) v)\n" " :pats { (?f27 A o f v) }\n" " :weight { 0 }) \n" "\n" " :formula (forall (A Int) (o Int) (f Int) (p Int) (g Int) (v Int)\n" " (or (= o p) (= (?f26 (?f27 A o f v) p g) (?f26 A p g)))\n" " :pats { (?f26 (?f27 A o f v) p g) }\n" " :weight { 0 })\n" "\n" " :formula (forall (A Int) (o Int) (f Int) (p Int) (g Int) (v Int)\n" " (or (= f g) (= (?f26 (?f27 A o f v) p g) (?f26 A p g)))\n" " :pats { (?f26 (?f27 A o f v) p g) }\n" " :weight { 0 })\n" "\n" " :extrapreds ((?f28 Int Int))\n" "\n" " :formula (forall (t Int) (u Int) (v Int)\n" " (or (not (?f28 t u))\n" " (not (?f28 u v))\n" " (?f28 t v))\n" " :pat {(?f28 t u) (?f28 u v)})\n" "\n" " :formula (forall (t Int) (u Int)\n" " (or (not (?f28 t u))\n" " (not (?f28 u t))\n" " (= t u))\n" " :pat {(?f28 t u) (?f28 u t)})\n" "\n" " :extrafuns ((?f29 Int Int) (?f30 Int Int) (?f31 Int Int Int) (?f32 Int) (?f33 Int) (?f34 Int Int Int)\n" " (?f35 Int Int) (?f36 Int) (?f37 Int) (?f38 Int) (?f39 Int Int) (?f40 Int)\n" " (?f41 Int) (?f42 Int Int) (?f43 Int Int) (?f44 Int) (?f45 Int Int))\n" "\n" " :formula (forall (x Int) (p Int)\n" " (or (not (?f28 (?f30 (?f31 x p)) ?f32))\n" " (not (= (?f31 x p) p))\n" " (= x p))\n" " :pat { (?f28 (?f30 (?f31 x p)) ?f32)} )\n" " \n" " :formula (forall (h Int) (o Int) (f Int) (T Int)\n" " (or \n" " (not (= (?f39 h) ?f33))\n" " (= (?f26 h o (?f34 f T)) ?f36)\n" " (not (or (not (= (?f26 h (?f26 h o (?f34 f T)) ?f37) o))\n" " (not (= (?f26 h (?f26 h o (?f34 f T)) ?f38) T)))))\n" " :pat {(?f26 h o (?f34 f T))})\n" "\n" " :formula (forall (h Int) (o Int) (f Int)\n" " (or\n" " (not (= (?f39 h) ?f33))\n" " (= (?f26 h o (?f35 f)) ?f36)\n" " (not (or (not (= (?f26 h (?f26 h o (?f35 f)) ?f37) (?f26 h o ?f37)))\n" " (not (= (?f26 h (?f26 h o (?f35 f)) ?f38) (?f26 h o ?f38))))))\n" " :pat {(?f26 h o (?f35 f))})\n" " \n" " :formula (forall (h Int) (o Int)\n" " (or \n" " (not (= (?f39 h) ?f33))\n" " (= (?f26 h o ?f38) ?f44)\n" " (not (?f28 (?f26 h (?f26 h o ?f37) ?f41) (?f26 h o ?f38)))\n" " (= (?f26 h (?f26 h o ?f37) ?f40) (?f42 (?f26 h o ?f38)))\n" " (not (or (not (= (?f26 h o ?f41) (?f43 o)))\n" " (not (= (?f26 h o ?f40) (?f43 o))))))\n" " :pat {(?f28 (?f26 h (?f26 h o ?f37) ?f41) (?f26 h o ?f38))})\n" "\n" " :formula (forall (T Int) (h Int)\n" " (or (not (= (?f39 h) ?f33))\n" " (= (?f26 h (?f45 T) ?f38) ?f44))\n" " :pat {(?f26 h (?f45 T) ?f38)})\n" "\n" " :extrafuns ((?f46 Int Int Int)\n" " (?f47 Int Int Int)\n" " (?f48 Int Int Int)\n" " (?f49 Int)\n" " (?f50 Int Int Int)\n" " (?f51 Int Int Int)\n" " (?f52 Int Int)\n" " )\n" "\n" " :formula (forall (a Int) (T Int) (i Int) (r Int) (heap Int)\n" " (or (not (= (?f39 heap) ?f33))\n" " (not (?f28 (?f43 a) (?f46 T r)))\n" " (= (?f47 (?f48 (?f26 heap a ?f49) i) T) ?f33))\n" " :pat {(?f28 (?f43 a) (?f46 T r)) (?f48 (?f26 heap a ?f49) i)})\n" "\n" " :formula (forall (a Int) (T Int) (r Int)\n" " (or (= a ?f36) \n" " (not (?f28 (?f43 a) (?f46 T r)))\n" " (= (?f52 a) r))\n" " :pat {(?f28 (?f43 a) (?f46 T r))})\n" "\n" " :extrafuns ((?f53 Int Int Int)\n" " (?f54 Int Int)\n" " (?f55 Int)\n" " (?f56 Int Int)\n" " (?f57 Int)\n" " (?f58 Int)\n" " (?f59 Int Int Int)\n" " (?f60 Int Int Int)\n" " (?f61 Int Int Int)\n" " )\n" "\n" " :extrapreds ((?f62 Int Int))\n" " \n" " :formula (forall (T Int) (ET Int) (r Int)\n" " (or (not (?f28 T (?f53 ET r)))\n" " (= (?f54 T) ?f55))\n" " :pat {(?f28 T (?f53 ET r))})\n" "\n" " :formula (forall (A Int) (r Int) (T Int)\n" " (or\n" " (not (?f28 T (?f46 A r)))\n" " (not (or (not (= T (?f46 (?f56 T) r)))\n" " (not (?f28 (?f56 T) A)))))\n" " :pat {(?f28 T (?f46 A r))})\n" "\n" " :formula (forall (A Int) (r Int) (T Int)\n" " (or (not (?f28 T (?f53 A r)))\n" " (= T (?f53 A r)))\n" " :pat {(?f28 T (?f53 A r))})\n" "\n" " :extrafuns ((?f63 Int Int Int)\n" " (?f64 Int Int Int)\n" " )\n" "\n" " :formula (forall (A Int) (B Int) (C Int)\n" " (or (not (?f28 C (?f63 B A)))\n" " (= (?f64 C A) B))\n" " :pat {(?f28 C (?f63 B A))})\n" " \n" " :formula (forall (o Int) (T Int)\n" " (iff (= (?f47 o T) ?f33)\n" " (or (= o ?f36)\n" " (?f28 (?f43 o) T)))\n" " :pat {(?f47 o T)})\n" "\n" " :formula (forall (o Int) (T Int)\n" " (iff (= (?f51 o T) ?f33)\n" " (or (= o ?f36)\n" " (not (= (?f47 o T) ?f33))))\n" " :pat {(?f51 o T)})\n" "\n" " :formula (forall (h Int) (o Int)\n" " (or (not (= (?f39 h) ?f33))\n" " (= o ?f36)\n" " (not (?f28 (?f43 o) ?f57))\n" " (not (or (not (= (?f26 h o ?f41) (?f43 o)))\n" " (not (= (?f26 h o ?f40) (?f43 o))))))\n" " :pat {(?f28 (?f43 o) ?f57) (?f26 h o ?f41)})\n" "\n" " :formula (forall (h Int) (o Int) (f Int) (T Int)\n" " (or (not (= (?f39 h) ?f33))\n" " (?f62 (?f26 h o (?f60 f T)) T))\n" " :pat {(?f26 h o (?f60 f T))})\n" "\n" " :formula (forall (h Int) (o Int) (f Int)\n" " (or\n" " (not (= (?f39 h) ?f33))\n" " (not (= (?f26 h o ?f58) ?f33))\n" " (= (?f61 h (?f26 h o f)) ?f33))\n" " :pat {(?f61 h (?f26 h o f))})\n" "\n" " :formula (forall (h Int) (s Int) (f Int)\n" " (or (not (= (?f61 h s) ?f33))\n" " (= (?f61 h (?f59 s f)) ?f33))\n" " :pat {(?f61 h (?f59 s f))})\n" "\n" " :extrapreds ((?f65 Int Int))\n" "\n" " :formula (forall (x Int) (f Int) (a0 Int)\n" " (or (<= (+ a0 (* -1 (?f15 f))) 0)\n" " (not (?f65 x a0))\n" " (?f65 (?f2 f x) a0))\n" " :pat {(?f65 (?f2 f x) a0)})\n" "\n" " :formula (forall (a Int) (e Int) (i Int) (a0 Int) \n" " (or (<= (+ a0 (* -1 (?f16 e))) 0)\n" " (not (?f65 a a0))\n" " (?f65 (?f2 (?f2 e a) i) a0))\n" " :pats { (?f65 (?f2 (?f2 e a) i) a0) })\n" "\n" " :formula (forall (e Int) (a Int) (i Int) \n" " (= (?f8 (?f2 (?f2 (?f13 e) a) i)\n" " (?f7 (?f12 a))) ?f33)\n" " :pats { (?f2 (?f2 (?f13 e) a) i) })\n" "\n" " :formula (forall (t0 Int) (t1 Int)\n" " (iff (?f28 t0 (?f6 t1))\n" " (not (or (not (= t0 (?f6 (?f7 t0))))\n" " (not (?f28 (?f7 t0) t1)))))\n" " :pat {(?f28 t0 (?f6 t1))})\n" "\n" " :formula (forall (t0 Int) (t1 Int) (t2 Int) \n" " (or (not (?f28 t0 (?f4 t1 t2)))\n" " (= (?f5 t2 t0) (?f4 t1 t2)))\n" " :pats { (?f28 t0 (?f4 t1 t2)) })\n" "\n" " :formula (forall (t0 Int) (t1 Int) \n" " (iff (?f28 t0 (?f6 t1))\n" " (not (or (not (= t0 (?f6 (?f7 t0))))\n" " (not (?f28 (?f7 t0) t1)))))\n" " :pats { (?f28 t0 (?f6 t1)) })\n" "\n" " :formula (forall (x Int) (t Int) \n" " (or (not (= (?f8 x t) ?f33))\n" " (= (?f9 x t) x))\n" " :pats { (?f9 x t) })\n" "\n" " :formula (forall (x Int) (t Int) \n" " (or (not (?f28 t ?f10))\n" " (iff (= (?f8 x t) ?f33)\n" " (or (= x ?f11)\n" " (?f28 (?f12 x) t))))\n" " :pats { (?f28 t ?f10) (?f8 x t) })\n" "\n" " :formula (forall (e Int) (a Int) (i Int) \n" " (= (?f8 (?f2 (?f2 (?f13 e) a) i)\n" " (?f7 (?f12 a))) 1)\n" " :pats { (?f2 (?f2 (?f13 e) a) i) })\n" " )\n" ; z3-z3-4.8.7/src/smt/database.smt000066400000000000000000000346121356505360400163200ustar00rootroot00000000000000(benchmark patterns :status unknown :logic ALL :extrafuns ((?store Int Int Int Int) (?select Int Int Int) (?PO Int Int Int) (?asChild Int Int Int) (?classDown Int Int Int) (?array Int Int) (?elemtype Int Int) (?is Int Int Int) (?cast Int Int Int) (?Object Int) (?null Int) (?typeof Int Int) (?asElems Int Int) (?isAllocated Int Int Int) (?fClosedTime Int Int) (?eClosedTime Int Int) (?max Int Int) (?asLockSet Int Int) (?isNewArray Int Int) (?classLiteral Int Int) (?Class Int) (?alloc Int) (?arrayType Int) (?f Int Int) (?finv Int Int) ) :formula (forall (a Int) (i Int) (e Int) (= (?select (?store a i e) i) e) :pats { (?store a i e) } :weight { 0 }) :formula (forall (a Int) (i Int) (j Int) (e Int) (or (= i j) (= (?select (?store a i e) j) (?select a j))) :pats { (?select (?store a i e) j) } :weight { 0 }) :formula (forall (t0 Int) (t1 Int) (t2 Int) (or (not (= (?PO t0 t1) 1)) (not (= (?PO t1 t2) 1)) (= (?PO t0 t2) 1)) :pats { (?PO t0 t1) (?PO t1 t2) }) :formula (forall (t0 Int) (t1 Int) (or (not (= (?PO t0 t1) 1)) (not (= (?PO t1 t0) 1)) (= t0 t1)) :pats { (?PO t0 t1) (?PO t1 t0) }) :formula (forall (t0 Int) (t1 Int) (t2 Int) (or (not (= (?PO t0 (?asChild t1 t2)) 1)) (= (?classDown t2 t0) (?asChild t1 t2))) :pats { (?PO t0 (?asChild t1 t2)) }) :formula (forall (t Int) (= (?finv (?f t)) t) :pats { (?f t) }) :formula (forall (t0 Int) (t1 Int) (iff (= (?PO t0 (?array t1)) 1) (not (or (not (= t0 (?array (?elemtype t0)))) (not (= (?PO (?elemtype t0) t1) 1))))) :pats { (?PO t0 (?array t1)) }) :formula (forall (x Int) (t Int) (or (not (= (?is x t) 1)) (= (?cast x t) x)) :pats { (?cast x t) }) :formula (forall (x Int) (t Int) (or (not (= (?PO t ?Object) 1)) (iff (= (?is x t) 1) (or (= x ?null) (= (?PO (?typeof x) t) 1)))) :pats { (?PO t ?Object) (?is x t) }) :formula (forall (e Int) (a Int) (i Int) (= (?is (?select (?select (?asElems e) a) i) (?elemtype (?typeof a))) 1) :pats { (?select (?select (?asElems e) a) i) }) :formula (forall (x Int) (f Int) (a0 Int) (or (<= (+ a0 (* -1 (?fClosedTime f))) 0) (not (= (?isAllocated x a0) 1)) (= (?isAllocated (?select f x) a0) 1)) :pats { (?isAllocated (?select f x) a0) }) :formula (forall (a Int) (e Int) (i Int) (a0 Int) (or (<= (+ a0 (* -1 (?eClosedTime e))) 0) (not (= (?isAllocated a a0) 1)) (= (?isAllocated (?select (?select e a) i) a0) 1)) :pats { (?isAllocated (?select (?select e a) i) a0) }) :formula (forall (S Int) (= (?select (?asLockSet S) (?max (?asLockSet S))) 1) :pats { (?select (?asLockSet S) (?max (?asLockSet S))) }) :formula (forall (s Int) (or (not (= 1 (?isNewArray s))) (= (?PO (?typeof s) ?arrayType) 1)) :pats { (?isNewArray s) }) :formula (forall (t Int) (not (or (= (?classLiteral t) ?null) (not (= (?is (?classLiteral t) ?Class) 1)) (not (= (?isAllocated (?classLiteral t) ?alloc) 1)))) :pats { (?classLiteral t) }) :extrafuns ((?select2 Int Int Int Int) (?store2 Int Int Int Int Int)) :formula (forall (A Int) (o Int) (f Int) (v Int) (= (?select2 (?store2 A o f v) o f) v) :pats { (?store2 A o f v) } :weight { 0 }) :formula (forall (A Int) (o Int) (f Int) (p Int) (g Int) (v Int) (or (= o p) (= (?select2 (?store2 A o f v) p g) (?select2 A p g))) :pats { (?select2 (?store2 A o f v) p g) } :weight { 0 }) :formula (forall (A Int) (o Int) (f Int) (p Int) (g Int) (v Int) (or (= f g) (= (?select2 (?store2 A o f v) p g) (?select2 A p g))) :pats { (?select2 (?store2 A o f v) p g) } :weight { 0 }) :extrapreds ((?subtypes Int Int)) :formula (forall (t Int) (u Int) (v Int) (or (not (?subtypes t u)) (not (?subtypes u v)) (?subtypes t v)) :pat {(?subtypes t u) (?subtypes u v)}) :formula (forall (t Int) (u Int) (or (not (?subtypes t u)) (not (?subtypes u t)) (= t u)) :pat {(?subtypes t u) (?subtypes u t)}) :extrafuns ((?Unbox Int Int) (?UnboxedType Int Int) (?Box Int Int Int) (?System.Object Int) (?Smt.true Int) (?AsRepField Int Int Int) (?AsPeerField Int Int) (?nullObject Int) (?ownerRef_ Int) (?ownerFrame_ Int) (IntsHeap Int Int) (?localinv_ Int) (?inv_ Int) (?BaseClass_ Int Int) (?typeof_ Int Int) (?PeerGroupPlaceholder_ Int) (?ClassRepr Int Int)) :formula (forall (x Int) (p Int) (or (not (?subtypes (?UnboxedType (?Box x p)) ?System.Object)) (not (= (?Box x p) p)) (= x p)) :pat { (?subtypes (?UnboxedType (?Box x p)) ?System.Object)} ) :formula (forall (h Int) (o Int) (f Int) (T Int) (or (not (= (IntsHeap h) ?Smt.true)) (= (?select2 h o (?AsRepField f T)) ?nullObject) (not (or (not (= (?select2 h (?select2 h o (?AsRepField f T)) ?ownerRef_) o)) (not (= (?select2 h (?select2 h o (?AsRepField f T)) ?ownerFrame_) T))))) :pat {(?select2 h o (?AsRepField f T))}) :formula (forall (h Int) (o Int) (f Int) (or (not (= (IntsHeap h) ?Smt.true)) (= (?select2 h o (?AsPeerField f)) ?nullObject) (not (or (not (= (?select2 h (?select2 h o (?AsPeerField f)) ?ownerRef_) (?select2 h o ?ownerRef_))) (not (= (?select2 h (?select2 h o (?AsPeerField f)) ?ownerFrame_) (?select2 h o ?ownerFrame_)))))) :pat {(?select2 h o (?AsPeerField f))}) :formula (forall (h Int) (o Int) (or (not (= (IntsHeap h) ?Smt.true)) (= (?select2 h o ?ownerFrame_) ?PeerGroupPlaceholder_) (not (?subtypes (?select2 h (?select2 h o ?ownerRef_) ?inv_) (?select2 h o ?ownerFrame_))) (= (?select2 h (?select2 h o ?ownerRef_) ?localinv_) (?BaseClass_ (?select2 h o ?ownerFrame_))) (not (or (not (= (?select2 h o ?inv_) (?typeof_ o))) (not (= (?select2 h o ?localinv_) (?typeof_ o)))))) :pat {(?subtypes (?select2 h (?select2 h o ?ownerRef_) ?inv_) (?select2 h o ?ownerFrame_))}) :formula (forall (T Int) (h Int) (or (not (= (IntsHeap h) ?Smt.true)) (= (?select2 h (?ClassRepr T) ?ownerFrame_) ?PeerGroupPlaceholder_)) :pat {(?select2 h (?ClassRepr T) ?ownerFrame_)}) :extrafuns ((?RefArray Int Int Int) (Ints_ Int Int Int) (?RefArrayGet Int Int Int) (?elements_ Int) (?NonNullRefArray Int Int Int) (IntsNotNull_ Int Int Int) (?Rank_ Int Int) ) :formula (forall (a Int) (T Int) (i Int) (r Int) (heap Int) (or (not (= (IntsHeap heap) ?Smt.true)) (not (?subtypes (?typeof_ a) (?RefArray T r))) (= (Ints_ (?RefArrayGet (?select2 heap a ?elements_) i) T) ?Smt.true)) :pat {(?subtypes (?typeof_ a) (?RefArray T r)) (?RefArrayGet (?select2 heap a ?elements_) i)}) :formula (forall (a Int) (T Int) (r Int) (or (= a ?nullObject) (not (?subtypes (?typeof_ a) (?RefArray T r))) (= (?Rank_ a) r)) :pat {(?subtypes (?typeof_ a) (?RefArray T r))}) :extrafuns ((?ValueArray Int Int Int) (?ArrayCategory_ Int Int) (?ArrayCategoryValue_ Int) (?ElementType_ Int Int) (?System.Array Int) (?allocated_ Int) (?StructGet_ Int Int Int) (?AsRangeField Int Int Int) (IntsAllocated Int Int Int) ) :extrapreds ((IntnRange Int Int)) :formula (forall (T Int) (ET Int) (r Int) (or (not (?subtypes T (?ValueArray ET r))) (= (?ArrayCategory_ T) ?ArrayCategoryValue_)) :pat {(?subtypes T (?ValueArray ET r))}) :formula (forall (A Int) (r Int) (T Int) (or (not (?subtypes T (?RefArray A r))) (not (or (not (= T (?RefArray (?ElementType_ T) r))) (not (?subtypes (?ElementType_ T) A))))) :pat {(?subtypes T (?RefArray A r))}) :formula (forall (A Int) (r Int) (T Int) (or (not (?subtypes T (?ValueArray A r))) (= T (?ValueArray A r))) :pat {(?subtypes T (?ValueArray A r))}) :extrafuns ((?AsDirectSubClass Int Int Int) (?OneClassDown Int Int Int) ) :formula (forall (A Int) (B Int) (C Int) (or (not (?subtypes C (?AsDirectSubClass B A))) (= (?OneClassDown C A) B)) :pat {(?subtypes C (?AsDirectSubClass B A))}) :formula (forall (o Int) (T Int) (iff (= (Ints_ o T) ?Smt.true) (or (= o ?nullObject) (?subtypes (?typeof_ o) T))) :pat {(Ints_ o T)}) :formula (forall (o Int) (T Int) (iff (= (IntsNotNull_ o T) ?Smt.true) (or (= o ?nullObject) (not (= (Ints_ o T) ?Smt.true)))) :pat {(IntsNotNull_ o T)}) :formula (forall (h Int) (o Int) (or (not (= (IntsHeap h) ?Smt.true)) (= o ?nullObject) (not (?subtypes (?typeof_ o) ?System.Array)) (not (or (not (= (?select2 h o ?inv_) (?typeof_ o))) (not (= (?select2 h o ?localinv_) (?typeof_ o)))))) :pat {(?subtypes (?typeof_ o) ?System.Array) (?select2 h o ?inv_)}) :formula (forall (h Int) (o Int) (f Int) (T Int) (or (not (= (IntsHeap h) ?Smt.true)) (IntnRange (?select2 h o (?AsRangeField f T)) T)) :pat {(?select2 h o (?AsRangeField f T))}) :formula (forall (h Int) (o Int) (f Int) (or (not (= (IntsHeap h) ?Smt.true)) (not (= (?select2 h o ?allocated_) ?Smt.true)) (= (IntsAllocated h (?select2 h o f)) ?Smt.true)) :pat {(IntsAllocated h (?select2 h o f))}) :formula (forall (h Int) (s Int) (f Int) (or (not (= (IntsAllocated h s) ?Smt.true)) (= (IntsAllocated h (?StructGet_ s f)) ?Smt.true)) :pat {(IntsAllocated h (?StructGet_ s f))}) :extrapreds ((?isAllocated_ Int Int)) :formula (forall (x Int) (f Int) (a0 Int) (or (<= (+ a0 (* -1 (?fClosedTime f))) 0) (not (?isAllocated_ x a0)) (?isAllocated_ (?select f x) a0)) :pat {(?isAllocated_ (?select f x) a0)}) :formula (forall (a Int) (e Int) (i Int) (a0 Int) (or (<= (+ a0 (* -1 (?eClosedTime e))) 0) (not (?isAllocated_ a a0)) (?isAllocated_ (?select (?select e a) i) a0)) :pats { (?isAllocated_ (?select (?select e a) i) a0) }) :formula (forall (e Int) (a Int) (i Int) (= (?is (?select (?select (?asElems e) a) i) (?elemtype (?typeof a))) ?Smt.true) :pats { (?select (?select (?asElems e) a) i) }) :formula (forall (t0 Int) (t1 Int) (iff (?subtypes t0 (?array t1)) (not (or (not (= t0 (?array (?elemtype t0)))) (not (?subtypes (?elemtype t0) t1))))) :pat {(?subtypes t0 (?array t1))}) :formula (forall (t0 Int) (t1 Int) (t2 Int) (or (not (?subtypes t0 (?asChild t1 t2))) (= (?classDown t2 t0) (?asChild t1 t2))) :pats { (?subtypes t0 (?asChild t1 t2)) }) :formula (forall (t0 Int) (t1 Int) (iff (?subtypes t0 (?array t1)) (not (or (not (= t0 (?array (?elemtype t0)))) (not (?subtypes (?elemtype t0) t1))))) :pats { (?subtypes t0 (?array t1)) }) :formula (forall (x Int) (t Int) (or (not (= (?is x t) ?Smt.true)) (= (?cast x t) x)) :pats { (?cast x t) }) :formula (forall (x Int) (t Int) (or (not (?subtypes t ?Object)) (iff (= (?is x t) ?Smt.true) (or (= x ?null) (?subtypes (?typeof x) t)))) :pats { (?subtypes t ?Object) (?is x t) }) :formula (forall (e Int) (a Int) (i Int) (= (?is (?select (?select (?asElems e) a) i) (?elemtype (?typeof a))) 1) :pats { (?select (?select (?asElems e) a) i) }) ) z3-z3-4.8.7/src/smt/diff_logic.h000066400000000000000000002036101356505360400162610ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: diff_logic.h Abstract: Basic support for difference logic Author: Leonardo de Moura (leonardo) 2006-11-21. Revision History: --*/ #ifndef DIFF_LOGIC_H_ #define DIFF_LOGIC_H_ #include "util/vector.h" #include "util/heap.h" #include "util/statistics.h" #include "util/trace.h" #include "util/warning.h" #include "util/uint_set.h" #include typedef int dl_var; typedef enum { DL_UNMARKED = 0, // Node/Variable was not yet found in the forward/backward search. DL_FOUND, // Node/Variable was found but not yet processed. DL_PROCESSED // Node/Variable was found and processed in the forward/backward search/ } dl_search_mark; typedef enum { DL_PROP_UNMARKED = 0, DL_PROP_IRRELEVANT = 1, DL_PROP_RELEVANT = 2, DL_PROP_PROCESSED_RELEVANT = 3, DL_PROP_PROCESSED_IRRELEVANT = 4 } dl_prop_search_mark ; template class dl_edge { typedef typename Ext::numeral numeral; typedef typename Ext::explanation explanation; dl_var m_source; dl_var m_target; numeral m_weight; unsigned m_timestamp; explanation m_explanation; bool m_enabled; public: dl_edge(dl_var s, dl_var t, const numeral & w, unsigned ts, const explanation & ex): m_source(s), m_target(t), m_weight(w), m_timestamp(ts), m_explanation(ex), m_enabled(false) { } dl_var get_source() const { return m_source; } dl_var get_target() const { return m_target; } const numeral & get_weight() const { return m_weight; } const explanation & get_explanation() const { return m_explanation; } unsigned get_timestamp() const { return m_timestamp; } bool is_enabled() const { return m_enabled; } void enable(unsigned timestamp) { SASSERT(!m_enabled); m_timestamp = timestamp; m_enabled = true; } void disable() { m_enabled = false; } }; // Functor for comparing difference logic variables. // This functor is used to implement Dijkstra algorithm (i.e., Heap). template class dl_var_lt { typedef typename Ext::numeral numeral; vector & m_values; public: dl_var_lt(vector & values): m_values(values) { } bool operator()(dl_var v1, dl_var v2) const { return m_values[v1] < m_values[v2]; } }; typedef int edge_id; const edge_id null_edge_id = -1; template class dl_graph { struct stats { unsigned m_propagation_cost; unsigned m_implied_literal_cost; unsigned m_num_implied_literals; unsigned m_num_helpful_implied_literals; unsigned m_num_relax; void reset() { m_propagation_cost = 0; m_implied_literal_cost = 0; m_num_implied_literals = 0; m_num_helpful_implied_literals = 0; m_num_relax = 0; } stats() { reset(); } void collect_statistics(::statistics& st) const { st.update("dl prop steps", m_propagation_cost); st.update("dl impl steps", m_implied_literal_cost); st.update("dl impl lits", m_num_implied_literals); st.update("dl impl conf lits", m_num_helpful_implied_literals); st.update("dl bound relax", m_num_relax); } }; stats m_stats; typedef typename Ext::numeral numeral; typedef typename Ext::explanation explanation; typedef vector assignment; typedef dl_edge edge; typedef vector edges; class assignment_trail { dl_var m_var; numeral m_old_value; public: assignment_trail(dl_var v, const numeral & val): m_var(v), m_old_value(val) { } dl_var get_var() const { return m_var; } const numeral & get_old_value() const { return m_old_value; } }; typedef vector assignment_stack; assignment m_assignment; // per var assignment_stack m_assignment_stack; // temporary stack for restoring the assignment edges m_edges; typedef int_vector edge_id_vector; typedef int_vector dl_var_vector; vector m_out_edges; // per var vector m_in_edges; // per var struct scope { unsigned m_edges_lim; unsigned m_enabled_edges_lim; unsigned m_old_timestamp; scope(unsigned e, unsigned enabled, unsigned t): m_edges_lim(e), m_enabled_edges_lim(enabled), m_old_timestamp(t) { } }; svector m_trail_stack; // forward reachability vector m_gamma; // per var svector m_mark; // per var edge_id_vector m_parent; // per var dl_var_vector m_visited; typedef heap > var_heap; var_heap m_heap; unsigned m_timestamp; unsigned m_last_enabled_edge; edge_id_vector m_enabled_edges; // SCC for cheap equality propagation -- svector m_unfinished_set; // per var int_vector m_dfs_time; // per var dl_var_vector m_roots; dl_var_vector m_unfinished; int m_next_dfs_time; int m_next_scc_id; // ------------------------------------- // activity vector for edges. svector m_activity; bool check_invariant() const { #ifdef Z3DEBUG SASSERT(m_assignment.size() == m_gamma.size()); SASSERT(m_assignment.size() == m_mark.size()); SASSERT(m_assignment.size() == m_parent.size()); SASSERT(m_assignment.size() <= m_heap.get_bounds()); SASSERT(m_in_edges.size() == m_out_edges.size()); int n = m_out_edges.size(); for (dl_var id = 0; id < n; id++) { const edge_id_vector & e_ids = m_out_edges[id]; for (edge_id e_id : e_ids) { SASSERT(static_cast(e_id) <= m_edges.size()); const edge & e = m_edges[e_id]; SASSERT(e.get_source() == id); } } for (dl_var id = 0; id < n; id++) { const edge_id_vector & e_ids = m_in_edges[id]; for (edge_id e_id : e_ids) { SASSERT(static_cast(e_id) <= m_edges.size()); const edge & e = m_edges[e_id]; SASSERT(e.get_target() == id); } } n = m_edges.size(); for (int i = 0; i < n; i++) { const edge & e = m_edges[i]; SASSERT(std::find(m_out_edges[e.get_source()].begin(), m_out_edges[e.get_source()].end(), i) != m_out_edges[e.get_source()].end()); SASSERT(std::find(m_in_edges[e.get_target()].begin(), m_in_edges[e.get_target()].end(), i) != m_in_edges[e.get_target()].end()); } #endif return true; } bool is_feasible(const edge & e) const { return !e.is_enabled() || m_assignment[e.get_target()] - m_assignment[e.get_source()] <= e.get_weight(); } public: // An assignment is feasible if all edges are feasible. bool is_feasible() const { #ifdef Z3DEBUG for (unsigned i = 0; i < m_edges.size(); ++i) { if (!is_feasible(m_edges[i])) { return false; } } #endif return true; } unsigned get_num_edges() const { return m_edges.size(); } unsigned get_num_nodes() const { return m_out_edges.size(); } dl_var get_source(edge_id id) const { return m_edges[id].get_source(); } dl_var get_target(edge_id id) const { return m_edges[id].get_target(); } explanation const & get_explanation(edge_id id) const { return m_edges[id].get_explanation(); } bool is_enabled(edge_id id) const { return m_edges[id].is_enabled(); } bool is_feasible(edge_id id) const { return is_feasible(m_edges[id]); } numeral const& get_weight(edge_id id) const { return m_edges[id].get_weight(); } edge_id_vector const& get_out_edges(dl_var v) const { return m_out_edges[v]; } edge_id_vector const& get_in_edges(dl_var v) const { return m_in_edges[v]; } private: // An assignment is almost feasible if all but edge with idt edge are feasible. bool is_almost_feasible(edge_id id) const { #ifdef Z3DEBUG for (unsigned i = 0; i < m_edges.size(); ++i) { if (id != static_cast(i) && !is_feasible(m_edges[i])) { return false; } } #endif return true; } // Restore the assignment using the information in m_assignment_stack. // This method is called when make_feasible fails. void undo_assignments() { typename assignment_stack::iterator it = m_assignment_stack.end(); typename assignment_stack::iterator begin = m_assignment_stack.begin(); while (it != begin) { --it; TRACE("diff_logic_bug", tout << "undo assignment: " << it->get_var() << " " << it->get_old_value() << "\n";); m_assignment[it->get_var()] = it->get_old_value(); } m_assignment_stack.reset(); } // Store in gamma the normalized weight. The normalized weight is given // by the formula // m_assignment[e.get_source()] - m_assignment[e.get_target()] + e.get_weight() numeral& set_gamma(const edge & e, numeral & gamma) { gamma = m_assignment[e.get_source()]; gamma -= m_assignment[e.get_target()]; gamma += e.get_weight(); return gamma; } void reset_marks() { for (dl_var v : m_visited) { m_mark[v] = DL_UNMARKED; } m_visited.reset(); } bool marks_are_clear() const { for (unsigned i = 0; i < m_mark.size(); ++i) { if (m_mark[i] != DL_UNMARKED) { return false; } } return true; } // Make the assignment feasible. An assignment is feasible if // Forall edge e. m_assignment[e.get_target()] - m_assignment[e.get_source()] <= e.get_weight() // // This method assumes that if the assignment is not feasible, then the only infeasible edge // is the last added edge. bool make_feasible(edge_id id) { SASSERT(is_almost_feasible(id)); SASSERT(!m_edges.empty()); SASSERT(!is_feasible(m_edges[id])); SASSERT(m_assignment_stack.empty()); SASSERT(m_heap.empty()); const edge & last_e = m_edges[id]; dl_var root = last_e.get_source(); m_gamma[root].reset(); dl_var target = last_e.get_target(); numeral gamma; set_gamma(last_e, gamma); m_gamma[target] = gamma; m_mark[target] = DL_PROCESSED; m_parent[target] = id; m_visited.push_back(target); SASSERT(m_gamma[target].is_neg()); acc_assignment(target, gamma); TRACE("arith", tout << id << "\n";); dl_var source = target; while (true) { ++m_stats.m_propagation_cost; if (m_mark[root] != DL_UNMARKED) { // negative cycle was found SASSERT(m_gamma[root].is_neg()); m_heap.reset(); reset_marks(); undo_assignments(); return false; } for (edge_id e_id : m_out_edges[source]) { edge & e = m_edges[e_id]; SASSERT(e.get_source() == source); if (!e.is_enabled()) { continue; } set_gamma(e, gamma); if (gamma.is_neg()) { target = e.get_target(); switch (m_mark[target]) { case DL_UNMARKED: m_gamma[target] = gamma; m_mark[target] = DL_FOUND; m_parent[target] = e_id; m_visited.push_back(target); m_heap.insert(target); break; case DL_FOUND: if (gamma < m_gamma[target]) { m_gamma[target] = gamma; m_parent[target] = e_id; m_heap.decreased(target); } break; case DL_PROCESSED: default: UNREACHABLE(); } } } if (m_heap.empty()) { SASSERT(is_feasible()); reset_marks(); m_assignment_stack.reset(); return true; } source = m_heap.erase_min(); m_mark[source] = DL_PROCESSED; acc_assignment(source, m_gamma[source]); } } edge const* find_relaxed_edge(edge const* e, numeral & gamma) { SASSERT(gamma.is_neg()); dl_var src = e->get_source(); dl_var dst = e->get_target(); numeral w = e->get_weight(); for (edge_id e_id : m_out_edges[src]) { edge const& e2 = m_edges[e_id]; if (e2.get_target() == dst && e2.is_enabled() && // or at least not be inconsistent with current choices e2.get_weight() > w && (e2.get_weight() - w + gamma).is_neg()) { e = &e2; gamma += (e2.get_weight() - w); w = e2.get_weight(); ++m_stats.m_num_relax; } } return e; } public: dl_graph(): m_heap(1024, dl_var_lt(m_gamma)), m_timestamp(0), m_fw(m_mark), m_bw(m_mark) { } void collect_statistics(::statistics& st) const { m_stats.collect_statistics(st); } // Create/Initialize a variable with the given id. // The graph does not have control over the ids assigned by the theory. // That is init_var receives the id as an argument. void init_var(dl_var v) { TRACE("diff_logic_bug", tout << "init_var " << v << "\n";); if (static_cast(v) < m_out_edges.size() && (!m_out_edges[v].empty() || !m_in_edges[v].empty())) { return; } SASSERT(check_invariant()); while (static_cast(v) >= m_out_edges.size()) { m_assignment .push_back(numeral()); m_out_edges .push_back(edge_id_vector()); m_in_edges .push_back(edge_id_vector()); m_gamma .push_back(numeral()); m_mark .push_back(DL_UNMARKED); m_parent .push_back(null_edge_id); } if (static_cast(v) >= m_heap.get_bounds()) { m_heap.set_bounds(v+1); } m_assignment[v].reset(); SASSERT(static_cast(v) < m_heap.get_bounds()); TRACE("diff_logic_bug", tout << "init_var " << v << ", m_assignment[v]: " << m_assignment[v] << "\n";); SASSERT(m_assignment[v].is_zero()); SASSERT(m_out_edges[v].empty()); SASSERT(m_in_edges[v].empty()); SASSERT(m_mark[v] == DL_UNMARKED); SASSERT(check_invariant()); } // Add an new weighted edge "source --weight--> target" with explanation ex. edge_id add_edge(dl_var source, dl_var target, const numeral & weight, const explanation & ex) { // SASSERT(is_feasible()); edge_id new_id = m_edges.size(); m_edges.push_back(edge(source, target, weight, m_timestamp, ex)); m_activity.push_back(0); TRACE("dl_bug", tout << "creating edge:\n"; display_edge(tout, m_edges.back());); m_out_edges[source].push_back(new_id); m_in_edges[target].push_back(new_id); return new_id; } // Return false if the resultant graph has a negative cycle. The negative // cycle can be extracted using traverse_neg_cycle. // The method assumes the graph is feasible before the invocation. bool enable_edge(edge_id id) { edge& e = m_edges[id]; bool r = true; if (!e.is_enabled()) { e.enable(m_timestamp); m_last_enabled_edge = id; m_timestamp++; if (!is_feasible(e)) { r = make_feasible(id); } SASSERT(check_invariant()); SASSERT(!r || is_feasible()); m_enabled_edges.push_back(id); } return r; } // This method should only be invoked when add_edge returns false. // That is, there is a negative cycle in the graph. // It will apply the functor f on every explanation attached to the edges // in the negative cycle. template void traverse_neg_cycle(bool try_relax, Functor & f) { SASSERT(!is_feasible(m_edges[m_last_enabled_edge])); edge_id last_id = m_last_enabled_edge; edge const& last_e = m_edges[last_id]; numeral gamma = m_gamma[last_e.get_source()]; SASSERT(gamma.is_neg()); edge_id e_id = last_id; do { const edge * e = &m_edges[e_id]; if (try_relax) { e = find_relaxed_edge(e, gamma); } inc_activity(e_id); f(e->get_explanation()); e_id = m_parent[e->get_source()]; } while (e_id != last_id); } // // Here is a version that tries to // Find shortcuts on the cycle. // A shortcut is an edge that that is subsumed // by the current edges, but provides for a shorter // path to the conflict. // Example (<= (- a b) k1) (<= (- b c) k2) (<= (- c d) k3) // An edge (<= (- a d) k4) where k1 + k2 + k3 <= k4, but gamma + k4 - (k1+k2+k3) < 0 // is still a conflict. // template void traverse_neg_cycle2(bool try_relax, Functor & f) { static unsigned num_conflicts = 0; ++num_conflicts; SASSERT(!is_feasible(m_edges[m_last_enabled_edge])); vector potentials; svector edges; svector nodes; edge_id last_id = m_last_enabled_edge; edge const& last_e = m_edges[last_id]; numeral potential(0); edge_id e_id = last_id; numeral gamma = m_gamma[last_e.get_source()]; SASSERT(check_gamma(last_id)); do { SASSERT(gamma.is_neg()); edges.push_back(e_id); const edge & e = m_edges[e_id]; dl_var src = e.get_source(); potential += e.get_weight(); // // search for edges that can reduce size of negative cycle. // for (edge_id e_id2 : m_out_edges[src]) { edge const& e2 = m_edges[e_id2]; dl_var src2 = e2.get_target(); if (e_id2 == e_id || !e2.is_enabled()) { continue; } for (unsigned j = 0; j < nodes.size(); ++j) { if (nodes[j] != src2) { continue; } numeral const& weight = e2.get_weight(); numeral delta = weight - potential + potentials[j]; if (delta.is_nonneg() && (gamma + delta).is_neg()) { TRACE("diff_logic_traverse", tout << "Reducing path by "; display_edge(tout, e2); tout << "gamma: " << gamma << " weight: " << weight << "\n"; tout << "enabled: " << e2.is_enabled() << "\n"; tout << "delta: " << delta << "\n"; tout << "literals saved: " << (nodes.size() - j - 1) << "\n"; ); gamma += delta; nodes.shrink(j + 1); potentials.shrink(j + 1); edges.shrink(j + 1); edges.push_back(e_id2); potential = potentials[j] + weight; break; } else { TRACE("diff_logic_traverse", display_edge(tout << "skipping: ", e2);); } } } potentials.push_back(potential); nodes.push_back(src); e_id = m_parent[src]; SASSERT(check_path(potentials, nodes, edges)); } while (e_id != last_id); TRACE("diff_logic_traverse", { tout << "Num conflicts: " << num_conflicts << "\n"; tout << "Resulting path:\n"; for (unsigned i = 0; i < edges.size(); ++i) { display_edge(tout << "potential: " << potentials[i] << " ", m_edges[edges[i]]); } } ); if (!check_explanation(edges.size(), edges.c_ptr())) { throw default_exception("edges are not inconsistent"); } // allow theory to introduce shortcut lemmas. prune_edges(edges, f); for (unsigned i = 0; i < edges.size(); ++i) { edge const& e = m_edges[edges[i]]; f(e.get_explanation()); } } bool can_reach(dl_var src, dl_var dst) { if (static_cast(src) >= m_out_edges.size() || static_cast(dst) >= m_out_edges.size()) { return false; } uint_set target, visited; target.insert(dst); return reachable(src, target, visited, dst); } bool reachable(dl_var start, uint_set const& target, uint_set& visited, dl_var& dst) { visited.reset(); svector nodes; nodes.push_back(start); for (unsigned i = 0; i < nodes.size(); ++i) { dl_var n = nodes[i]; if (visited.contains(n)) continue; visited.insert(n); edge_id_vector & edges = m_out_edges[n]; for (edge_id e_id : edges) { edge & e = m_edges[e_id]; if (!e.is_enabled()) continue; dst = e.get_target(); if (target.contains(dst)) { return true; } nodes.push_back(dst); } } return false; } private: svector m_freq_hybrid; int m_total_count = 0; int m_run_counter = -1; svector m_hybrid_visited, m_hybrid_parent; bool is_connected(numeral const& gamma, bool zero_edge, edge const& e, unsigned timestamp) const { return (gamma.is_zero() || (!zero_edge && gamma.is_neg())) && e.get_timestamp() < timestamp; } int_vector bfs_todo; int_vector dfs_todo; public: template bool find_path(dl_var source, dl_var target, unsigned timestamp, Functor & f) { auto zero_edge = true; unsigned bfs_head = 0; bfs_todo.reset(); dfs_todo.reset(); m_hybrid_visited.resize(m_assignment.size(), m_run_counter++); m_hybrid_parent.resize(m_assignment.size(), -1); bfs_todo.push_back(source); m_hybrid_parent[source] = -1; m_hybrid_visited[source] = m_run_counter; numeral gamma; while (bfs_head < bfs_todo.size() || !dfs_todo.empty()) { m_total_count++; dl_var v; if (!dfs_todo.empty()) { v = dfs_todo.back(); dfs_todo.pop_back(); } else { v = bfs_todo[bfs_head++]; } edge_id_vector & edges = m_out_edges[v]; for (edge_id e_id : edges) { edge & e = m_edges[e_id]; SASSERT(e.get_source() == v); if (!e.is_enabled()) { continue; } set_gamma(e, gamma); if (is_connected(gamma, zero_edge, e, timestamp)) { dl_var curr_target = e.get_target(); if (curr_target == target) { f(e.get_explanation()); m_freq_hybrid[e_id]++; while (true) { int p = m_hybrid_parent[v]; if (p == -1) return true; edge_id eid; bool ret = get_edge_id(p, v, eid); if (eid == null_edge_id || !ret) { return true; } else { edge & e = m_edges[eid]; f(e.get_explanation()); m_freq_hybrid[eid]++; v = p; } } } else if (m_hybrid_visited[curr_target] != m_run_counter) { if (m_freq_hybrid[e_id] > 1) { dfs_todo.push_back(curr_target); } else { bfs_todo.push_back(curr_target); } m_hybrid_visited[curr_target] = m_run_counter; m_hybrid_parent[curr_target] = v; } } } } return false; } // // Create fresh literals obtained by resolving a pair (or more) // literals associated with the edges. // template void prune_edges(svector& edges, Functor & f) { unsigned max_activity = 0; edge_id e_id; for (unsigned i = 0; i < edges.size(); ++i) { e_id = edges[i]; inc_activity(e_id); if (m_activity[e_id] > max_activity) { max_activity = m_activity[e_id]; } } if (edges.size() > 5 && max_activity > 20) { prune_edges_min2(edges, f); } } template void prune_edges_min1(svector& edges, Functor & f) { unsigned min_activity = ~0; unsigned idx = 0; for (unsigned i = 0; i + 1 < edges.size(); ++i) { edge_id e_id = edges[i]; if (m_activity[e_id] < min_activity) { min_activity = m_activity[e_id]; idx = i; } } dl_var dst = get_source(edges[idx+1]); dl_var src = get_target(edges[idx]); f.new_edge(src, dst, 2, edges.begin()+idx); } template void prune_edges_min2(svector& edges, Functor & f) { unsigned min1 = ~0, min2 = ~0, max = 0; unsigned idx1 = 0, idx2 = 0, max_idx = 0; dl_var src, dst; for (unsigned i = 0; i < edges.size(); ++i) { edge_id e_id = edges[i]; if (m_activity[e_id] <= min1) { min2 = min1; min1 = m_activity[e_id]; idx2 = idx1; idx1 = i; } else if (m_activity[e_id] < min2) { min2 = m_activity[e_id]; idx2 = i; } // TBD: use also the edge with the maximal // traversals to create cut-edge. // if (m_activity[e_id] > max) { max = m_activity[e_id]; max_idx = i; } } // // e1 e2 i1 e4 e5 e6 .. e8 i2 e9 e10 // => // e1 e2 e_new d9 e10 // // alternative: // e_new e4 ... e8 is the new edge. // // or both. // if (idx2 < idx1) { std::swap(idx1,idx2); } (void) max_idx; SASSERT(idx1 < idx2 && idx2 < edges.size()); SASSERT(max_idx < edges.size()); dst = get_source(edges[idx2]); src = get_target(edges[idx1]); f.new_edge(src, dst, idx2-idx1+1, edges.begin()+idx1); } // Create a new scope. // That is, save the number of edges in the graph. void push() { // SASSERT(is_feasible()); <<< I relaxed this condition m_trail_stack.push_back(scope(m_edges.size(), m_enabled_edges.size(), m_timestamp)); } // Backtrack num_scopes scopes. // Restore the previous number of edges. void pop(unsigned num_scopes) { unsigned lvl = m_trail_stack.size(); SASSERT(num_scopes <= lvl); unsigned new_lvl = lvl - num_scopes; scope & s = m_trail_stack[new_lvl]; for (unsigned i = m_enabled_edges.size(); i > s.m_enabled_edges_lim; ) { --i; m_edges[m_enabled_edges[i]].disable(); } m_enabled_edges.shrink(s.m_enabled_edges_lim); unsigned old_num_edges = s.m_edges_lim; m_timestamp = s.m_old_timestamp; unsigned num_edges = m_edges.size(); SASSERT(old_num_edges <= num_edges); unsigned to_delete = num_edges - old_num_edges; for (unsigned i = 0; i < to_delete; i++) { const edge & e = m_edges.back(); TRACE("dl_bug", tout << "deleting edge:\n"; display_edge(tout, e);); dl_var source = e.get_source(); dl_var target = e.get_target(); SASSERT(static_cast(m_edges.size()) - 1 == m_out_edges[source].back()); SASSERT(static_cast(m_edges.size()) - 1 == m_in_edges[target].back()); m_out_edges[source].pop_back(); m_in_edges[target].pop_back(); m_edges.pop_back(); } m_trail_stack.shrink(new_lvl); SASSERT(check_invariant()); // SASSERT(is_feasible()); <<< I relaxed the condition in push(), so this assertion is not valid anymore. } // Make m_assignment[v] == zero // The whole assignment is adjusted in a way feasibility is preserved. // This method should only be invoked if the current assignment if feasible. void set_to_zero(dl_var v) { SASSERT(is_feasible()); if (!m_assignment[v].is_zero()) { numeral k = m_assignment[v]; for (auto& a : m_assignment) { a -= k; } SASSERT(is_feasible()); } } // // set assignments of v and w both to 0. // assumption: there are no prior dependencies between v and w. // so the graph is disconnected. // assumption: the current assignment is feasible. // void set_to_zero(dl_var v, dl_var w) { if (!m_assignment[v].is_zero()) { set_to_zero(v); } else { set_to_zero(w); } if (!m_assignment[v].is_zero() || !m_assignment[w].is_zero()) { enable_edge(add_edge(v, w, numeral(0), explanation())); enable_edge(add_edge(w, v, numeral(0), explanation())); SASSERT(is_feasible()); } } private: // Update the assignment of variable v, that is, // m_assignment[v] += inc // This method also stores the old value of v in the assignment stack. void acc_assignment(dl_var v, const numeral & inc) { TRACE("diff_logic_bug", tout << "update v: " << v << " += " << inc << " m_assignment[v] " << m_assignment[v] << "\n";); m_assignment_stack.push_back(assignment_trail(v, m_assignment[v])); m_assignment[v] += inc; } public: void inc_assignment(dl_var v, numeral const& inc) { m_assignment[v] += inc; } struct every_var_proc { bool operator()(dl_var v) const { return true; } }; void display(std::ostream & out) const { display_core(out, every_var_proc()); } void display_agl(std::ostream & out) const { uint_set vars; for (edge const& e : m_edges) { if (e.is_enabled()) { vars.insert(e.get_source()); vars.insert(e.get_target()); } } out << "digraph "" {\n"; unsigned n = m_assignment.size(); for (unsigned v = 0; v < n; v++) { if (vars.contains(v)) { out << "\"" << v << "\" [label=\"" << v << ":" << m_assignment[v] << "\"]\n"; } } for (edge const& e : m_edges) { if (e.is_enabled()) { out << "\"" << e.get_source() << "\"->\"" << e.get_target() << "\"[label=\"" << e.get_weight() << "\"]\n"; } } out << "}\n"; } template void display_core(std::ostream & out, FilterAssignmentProc p) const { display_edges(out); display_assignment(out, p); } void display_edges(std::ostream & out) const { for (edge const& e : m_edges) { if (e.is_enabled()) { display_edge(out, e); } } } std::ostream& display_edge(std::ostream & out, edge_id id) const { return display_edge(out, m_edges[id]); } std::ostream& display_edge(std::ostream & out, const edge & e) const { return out << e.get_explanation() << " (<= (- $" << e.get_target() << " $" << e.get_source() << ") " << e.get_weight() << ") " << e.get_timestamp() << "\n"; } template void display_assignment(std::ostream & out, FilterAssignmentProc p) const { unsigned n = m_assignment.size(); for (unsigned v = 0; v < n; v++) { if (p(v)) { out << "$" << v << " := " << m_assignment[v] << "\n"; } } } // Return true if there is an edge source --> target. // If there is such edge, then the weight is stored in w and the explanation in ex. bool get_edge_weight(dl_var source, dl_var target, numeral & w, explanation & ex) { edge_id_vector & edges = m_out_edges[source]; bool found = false; for (edge_id e_id : edges) { edge & e = m_edges[e_id]; if (e.is_enabled() && e.get_target() == target && (!found || e.get_weight() < w)) { w = e.get_weight(); ex = e.get_explanation(); found = true; } } return found; } // Return true if there is an edge source --> target (also counting disabled edges). // If there is such edge, return its edge_id in parameter id. bool get_edge_id(dl_var source, dl_var target, edge_id & id) const { edge_id_vector const & edges = m_out_edges[source]; for (edge_id e_id : edges) { edge const & e = m_edges[e_id]; if (e.get_target() == target) { id = e_id; return true; } } return false; } edges const & get_all_edges() const { return m_edges; } void get_neighbours_undirected(dl_var current, svector & neighbours) { neighbours.reset(); edge_id_vector & out_edges = m_out_edges[current]; for (edge_id e_id : out_edges) { edge & e = m_edges[e_id]; SASSERT(e.get_source() == current); dl_var neighbour = e.get_target(); neighbours.push_back(neighbour); } edge_id_vector & in_edges = m_in_edges[current]; for (edge_id e_id : in_edges) { edge & e = m_edges[e_id]; SASSERT(e.get_target() == current); dl_var neighbour = e.get_source(); neighbours.push_back(neighbour); } } void dfs_undirected(dl_var start, svector & threads) { threads.reset(); threads.resize(get_num_nodes()); uint_set discovered, explored; svector nodes; discovered.insert(start); nodes.push_back(start); dl_var prev = start; while(!nodes.empty()) { dl_var current = nodes.back(); SASSERT(discovered.contains(current) && !explored.contains(current)); svector neighbours; get_neighbours_undirected(current, neighbours); SASSERT(!neighbours.empty()); bool found = false; for (unsigned i = 0; i < neighbours.size(); ++i) { dl_var next = neighbours[i]; DEBUG_CODE( edge_id id; SASSERT(get_edge_id(current, next, id) || get_edge_id(next, current, id));); if (!discovered.contains(next) && !explored.contains(next)) { TRACE("diff_logic", tout << "thread[" << prev << "] --> " << next << std::endl;); threads[prev] = next; prev = next; discovered.insert(next); nodes.push_back(next); found = true; break; } } SASSERT(!nodes.empty()); if (!found) { explored.insert(current); nodes.pop_back(); } } threads[prev] = start; } void bfs_undirected(dl_var start, svector & parents, svector & depths) { parents.reset(); parents.resize(get_num_nodes()); parents[start] = -1; depths.reset(); depths.resize(get_num_nodes()); uint_set visited; std::deque nodes; visited.insert(start); nodes.push_front(start); while(!nodes.empty()) { dl_var current = nodes.back(); nodes.pop_back(); SASSERT(visited.contains(current)); svector neighbours; get_neighbours_undirected(current, neighbours); SASSERT(!neighbours.empty()); for (unsigned i = 0; i < neighbours.size(); ++i) { dl_var next = neighbours[i]; DEBUG_CODE( edge_id id; SASSERT(get_edge_id(current, next, id) || get_edge_id(next, current, id));); if (!visited.contains(next)) { TRACE("diff_logic", tout << "parents[" << next << "] --> " << current << std::endl;); parents[next] = current; depths[next] = depths[current] + 1; visited.insert(next); nodes.push_front(next); } } } } template void enumerate_edges(dl_var source, dl_var target, Functor& f) { edge_id_vector & edges = m_out_edges[source]; for (edge_id e_id : edges) { edge const& e = m_edges[e_id]; if (e.get_target() == target) { f(e.get_weight(), e.get_explanation()); } } } void reset() { m_assignment .reset(); m_assignment_stack .reset(); m_edges .reset(); m_in_edges .reset(); m_out_edges .reset(); m_trail_stack .reset(); m_gamma .reset(); m_mark .reset(); m_parent .reset(); m_visited .reset(); m_heap .reset(); m_enabled_edges .reset(); m_activity .reset(); } // Compute strongly connected components connected by (normalized) zero edges. void compute_zero_edge_scc(int_vector & scc_id) { m_unfinished_set.reset(); m_dfs_time.reset(); scc_id.reset(); m_roots.reset(); m_unfinished.reset(); int n = m_assignment.size(); m_unfinished_set.resize(n, false); m_dfs_time.resize(n, -1); scc_id.resize(n, -1); m_next_dfs_time = 0; m_next_scc_id = 0; for (dl_var v = 0; v < n; v++) { if (m_dfs_time[v] == -1) { dfs(v, scc_id); } } TRACE("eq_scc", for (dl_var v = 0; v < n; v++) { tout << "$" << v << " -> " << scc_id[v] << "\n"; }); } void dfs(dl_var v, int_vector & scc_id) { m_dfs_time[v] = m_next_dfs_time; m_next_dfs_time++; m_unfinished_set[v] = true; m_unfinished.push_back(v); m_roots.push_back(v); numeral gamma; edge_id_vector & edges = m_out_edges[v]; for (edge_id e_id : edges) { edge & e = m_edges[e_id]; if (!e.is_enabled()) { continue; } SASSERT(e.get_source() == v); set_gamma(e, gamma); if (gamma.is_zero()) { dl_var target = e.get_target(); if (m_dfs_time[target] == -1) { dfs(target, scc_id); } else if (m_unfinished_set[target]) { SASSERT(!m_roots.empty()); while (m_dfs_time[m_roots.back()] > m_dfs_time[target]) { m_roots.pop_back(); SASSERT(!m_roots.empty()); } } } } if (v == m_roots.back()) { dl_var scc_elem; unsigned size = 0; do { scc_elem = m_unfinished.back(); m_unfinished.pop_back(); SASSERT(m_unfinished_set[scc_elem]); m_unfinished_set[scc_elem] = false; scc_id[scc_elem] = m_next_scc_id; size++; } while (scc_elem != v); // Ignore SCC with size 1 if (size == 1) { scc_id[scc_elem] = -1; } else { m_next_scc_id++; } m_roots.pop_back(); } } void compute_zero_succ(dl_var v, int_vector& succ) { unsigned n = m_assignment.size(); m_dfs_time.reset(); m_dfs_time.resize(n, -1); m_dfs_time[v] = 0; succ.push_back(v); numeral gamma; for (dl_var w : succ) { for (edge_id e_id : m_out_edges[w]) { edge & e = m_edges[e_id]; if (e.is_enabled() && set_gamma(e, gamma).is_zero()) { SASSERT(e.get_source() == w); dl_var target = e.get_target(); if (m_dfs_time[target] == -1) { succ.push_back(target); m_dfs_time[target] = 0; } } } } } numeral get_assignment(dl_var v) const { return m_assignment[v]; } void set_assignment(dl_var v, numeral const & n) { m_assignment[v] = n; } unsigned get_timestamp() const { return m_timestamp; } private: void inc_activity(edge_id e_id) { ++m_activity[e_id]; } bool check_explanation(unsigned num_edges, edge_id const* edges) { numeral w; for (unsigned i = 0; i < num_edges; ++i) { edge const& e = m_edges[edges[i]]; unsigned pred = (i>0)?(i-1):(num_edges-1); edge const& e1 = m_edges[edges[pred]]; if (e.get_target() != e1.get_source()) { TRACE("check_explanation", display_edge(tout, e); display_edge(tout, e1); ); return false; } w += e.get_weight(); } if (w.is_nonneg()) { TRACE("check_explanation", tout << "weight: " << w << "\n";); return false; } return true; } bool check_path(vector& potentials, svector& nodes, svector& edges) { // Debug: numeral potential0; for (unsigned i = 0; i < edges.size(); ++i) { potential0 += m_edges[edges[i]].get_weight(); if (potential0 != potentials[i] || nodes[i] != m_edges[edges[i]].get_source()) { TRACE("diff_logic_traverse", tout << "checking index " << i << " "; tout << "potential: " << potentials[i] << " "; display_edge(tout, m_edges[edges[i]]); ); return false; } } return true; } bool check_gamma(edge_id last_id) { edge_id e_id = last_id; numeral gamma2; do { gamma2 += m_edges[e_id].get_weight(); e_id = m_parent[m_edges[e_id].get_source()]; } while (e_id != last_id); return gamma2 == m_gamma[m_edges[last_id].get_source()]; } // Auxliary structure used for breadth-first search. struct bfs_elem { dl_var m_var; int m_parent_idx; edge_id m_edge_id; bfs_elem(dl_var v, int parent_idx, edge_id e): m_var(v), m_parent_idx(parent_idx), m_edge_id(e) { } }; public: // Find the shortest path from source to target using (normalized) zero edges with timestamp less than the given timestamp. // The functor f is applied on every explanation attached to the edges in the shortest path. // Return true if the path exists, false otherwise. // Return true if the path exists, false otherwise. template bool find_shortest_zero_edge_path(dl_var source, dl_var target, unsigned timestamp, Functor & f) { return find_shortest_path_aux(source, target, timestamp, f, true); } template bool find_shortest_reachable_path(dl_var source, dl_var target, unsigned timestamp, Functor & f) { return find_shortest_path_aux(source, target, timestamp, f, false); } template bool find_shortest_path_aux(dl_var source, dl_var target, unsigned timestamp, Functor & f, bool zero_edge) { svector bfs_todo; svector bfs_mark; bfs_mark.resize(m_assignment.size(), false); bfs_todo.push_back(bfs_elem(source, -1, null_edge_id)); bfs_mark[source] = true; numeral gamma; for (unsigned head = 0; head < bfs_todo.size(); ++head) { bfs_elem & curr = bfs_todo[head]; int parent_idx = head; dl_var v = curr.m_var; TRACE("dl_bfs", tout << "processing: " << v << "\n";); edge_id_vector & edges = m_out_edges[v]; for (edge_id e_id : edges) { edge & e = m_edges[e_id]; SASSERT(e.get_source() == v); if (!e.is_enabled()) { continue; } set_gamma(e, gamma); TRACE("dl_bfs", display_edge(tout << "processing edge: ", e) << " gamma: " << gamma << "\n";); if (is_connected(gamma, zero_edge, e, timestamp)) { dl_var curr_target = e.get_target(); TRACE("dl_bfs", tout << "curr_target: " << curr_target << ", mark: " << bfs_mark[curr_target] << "\n";); if (curr_target == target) { TRACE("dl_bfs", tout << "found path\n";); TRACE("dl_eq_bug", tout << "path: " << source << " --> " << target << "\n"; display_edge(tout, e); int tmp_parent_idx = parent_idx; while (true) { bfs_elem & curr = bfs_todo[tmp_parent_idx]; if (curr.m_edge_id == null_edge_id) { break; } else { edge & e = m_edges[curr.m_edge_id]; display_edge(tout, e); tmp_parent_idx = curr.m_parent_idx; } }); TRACE("dl_eq_bug", display_edge(tout, e);); f(e.get_explanation()); while (true) { SASSERT(parent_idx >= 0); bfs_elem & curr = bfs_todo[parent_idx]; if (curr.m_edge_id == null_edge_id) { return true; } else { edge & e = m_edges[curr.m_edge_id]; TRACE("dl_eq_bug", display_edge(tout, e);); f(e.get_explanation()); parent_idx = curr.m_parent_idx; } } } else if (!bfs_mark[curr_target]) { bfs_todo.push_back(bfs_elem(curr_target, parent_idx, e_id)); bfs_mark[curr_target] = true; } } } } return false; } // // Theory propagation: // Given a (newly) added edge id, find the ids of un-asserted edges that // that are subsumed by the id. // Separately, reproduce explanations for those ids. // // The algorithm works in the following way: // 1. Let e = source -- weight --> target be the edge at id. // 2. Compute successors (over the assigned edges) of source, // those traversing source-target and those leaving source over different edges. // compute forward potential of visited nodes. // queue up nodes that are visited, and require the source->target edge. // 3. Compute pre-decessors (over the assigned edges) of target, // those traversing source-target, and those entering target // without visiting source. Maintain only nodes that enter target // compute backward potential of visited nodes. // Queue up nodes that are visited, and require the source->target edge. // 4. traverse the smaller of the two lists. // check if there is an edge between the two sets such that // the weight of the edge is >= than the sum of the two potentials - weight // (since 'weight' is added twice in the traversal. // private: struct dfs_state { class hp_lt { assignment& m_delta; char_vector& m_mark; public: hp_lt(assignment& asgn, char_vector& m) : m_delta(asgn),m_mark(m) {} bool operator()(dl_var v1, dl_var v2) const { numeral const& delta1 = m_delta[v1]; numeral const& delta2 = m_delta[v2]; return delta1 < delta2 || (delta1 == delta2 && m_mark[v1] == DL_PROP_IRRELEVANT && m_mark[v2] == DL_PROP_RELEVANT); } }; assignment m_delta; int_vector m_visited; int_vector m_parent; heap m_heap; unsigned m_num_edges; dfs_state(char_vector& mark): m_heap(1024, hp_lt(m_delta, mark)), m_num_edges(0) {} void re_init(unsigned sz) { m_delta.resize(sz, numeral(0)); m_parent.resize(sz, 0); m_visited.reset(); m_num_edges = 0; m_heap.set_bounds(sz); SASSERT(m_heap.empty()); } void add_size(unsigned n) { m_num_edges += n; } unsigned get_size() const { return m_num_edges; } bool contains(dl_var v) const { // TBD can be done better using custom marking. for (unsigned i = 0; i < m_visited.size(); ++i) { if (v == m_visited[i]) { return true; } } return false; } }; dfs_state m_fw; dfs_state m_bw; void fix_sizes() { m_fw.re_init(m_assignment.size()); m_bw.re_init(m_assignment.size()); } numeral get_reduced_weight(dfs_state& state, dl_var n, edge const& e) { numeral gamma; return state.m_delta[n] + set_gamma(e, gamma); } template void find_relevant(dfs_state& state, edge_id id) { SASSERT(state.m_visited.empty()); SASSERT(state.m_heap.empty()); numeral delta; edge const& e_init = m_edges[id]; vector const& edges = is_fw?m_out_edges:m_in_edges; dl_var target = is_fw?e_init.get_target():e_init.get_source(); dl_var source = is_fw?e_init.get_source():e_init.get_target(); SASSERT(marks_are_clear()); dl_prop_search_mark source_mark = DL_PROP_IRRELEVANT; dl_prop_search_mark target_mark = DL_PROP_RELEVANT; m_mark[source] = source_mark; m_mark[target] = target_mark; state.m_delta[source] = numeral(0); state.m_delta[target] = get_reduced_weight(state, source, e_init); SASSERT(state.m_delta[source] <= state.m_delta[target]); state.m_heap.insert(source); state.m_heap.insert(target); unsigned num_relevant = 1; TRACE("diff_logic", display(tout); ); while (!state.m_heap.empty() && num_relevant > 0) { ++m_stats.m_implied_literal_cost; source = state.m_heap.erase_min(); source_mark = static_cast(m_mark[source]); SASSERT(source_mark == DL_PROP_RELEVANT || source_mark == DL_PROP_IRRELEVANT); state.m_visited.push_back(source); if (source_mark == DL_PROP_RELEVANT) { --num_relevant; state.add_size(edges[source].size()); m_mark[source] = DL_PROP_PROCESSED_RELEVANT; } else { m_mark[source] = DL_PROP_PROCESSED_IRRELEVANT; } TRACE("diff_logic", tout << "source: " << source << "\n";); for (edge_id e_id : edges[source]) { edge const& e = m_edges[e_id]; if (&e == &e_init) { continue; } SASSERT(!is_fw || e.get_source() == source); SASSERT(is_fw || e.get_target() == source); if (!e.is_enabled()) { continue; } TRACE("diff_logic", display_edge(tout, e);); target = is_fw?e.get_target():e.get_source(); delta = get_reduced_weight(state, source, e); SASSERT(delta >= state.m_delta[source]); target_mark = static_cast(m_mark[target]); switch(target_mark) { case DL_PROP_UNMARKED: { state.m_delta[target] = delta; m_mark[target] = source_mark; state.m_heap.insert(target); if (source_mark == DL_PROP_RELEVANT) { ++num_relevant; } state.m_parent[target] = e_id; break; } case DL_PROP_RELEVANT: case DL_PROP_IRRELEVANT: { numeral const& old_delta = state.m_delta[target]; if (delta < old_delta || (delta == old_delta && source_mark == DL_PROP_IRRELEVANT && target_mark == DL_PROP_RELEVANT)) { state.m_delta[target] = delta; m_mark[target] = source_mark; state.m_heap.decreased(target); if (target_mark == DL_PROP_IRRELEVANT && source_mark == DL_PROP_RELEVANT) { ++num_relevant; } if (target_mark == DL_PROP_RELEVANT && source_mark == DL_PROP_IRRELEVANT) { --num_relevant; } state.m_parent[target] = e_id; } break; } case DL_PROP_PROCESSED_RELEVANT: TRACE("diff_logic", tout << delta << " ?> " << state.m_delta[target] << "\n";); SASSERT(delta >= state.m_delta[target]); SASSERT(!(delta == state.m_delta[target] && source_mark == DL_PROP_IRRELEVANT)); break; case DL_PROP_PROCESSED_IRRELEVANT: TRACE("diff_logic", tout << delta << " ?> " << state.m_delta[target] << "\n";); SASSERT(delta >= state.m_delta[target]); break; default: UNREACHABLE(); } } } // // Clear marks using m_visited and m_heap. // unsigned sz = state.m_visited.size(); for (unsigned i = 0; i < sz; ) { dl_var v = state.m_visited[i]; source_mark = static_cast(m_mark[v]); m_mark[v] = DL_PROP_UNMARKED; SASSERT(source_mark == DL_PROP_PROCESSED_RELEVANT || source_mark == DL_PROP_PROCESSED_IRRELEVANT); if (source_mark == DL_PROP_PROCESSED_RELEVANT) { ++i; } else { state.m_visited[i] = state.m_visited[--sz]; state.m_visited.resize(sz); } } TRACE("diff_logic", { tout << (is_fw?"is_fw":"is_bw") << ": "; for (unsigned i = 0; i < state.m_visited.size(); ++i) { tout << state.m_visited[i] << " "; } tout << "\n"; }); for (auto & s : state.m_heap) { SASSERT(m_mark[s] != DL_PROP_UNMARKED); m_mark[s] = DL_PROP_UNMARKED;; } state.m_heap.reset(); SASSERT(marks_are_clear()); } void find_subsumed(edge_id bridge_edge, dfs_state& src, dfs_state& tgt, svector& subsumed) { edge const& e0 = m_edges[bridge_edge]; dl_var a = e0.get_source(); dl_var b = e0.get_target(); numeral n0 = m_assignment[b] - m_assignment[a] - e0.get_weight(); vector const& edges = m_out_edges; TRACE("diff_logic", tout << "$" << a << " a:" << m_assignment[a] << " $" << b << " b: " << m_assignment[b] << " e0: " << e0.get_weight() << " n0: " << n0 << "\n"; display_edge(tout, e0); ); for (unsigned i = 0; i < src.m_visited.size(); ++i) { dl_var c = src.m_visited[i]; numeral n1 = n0 + src.m_delta[c] - m_assignment[c]; for (edge_id e_id : edges[c]) { edge const& e1 = m_edges[e_id]; SASSERT(c == e1.get_source()); if (e1.is_enabled()) { continue; } dl_var d = e1.get_target(); numeral n2 = n1 + tgt.m_delta[d] + m_assignment[d]; if (tgt.contains(d) && n2 <= e1.get_weight()) { TRACE("diff_logic", tout << "$" << c << " delta_c: " << src.m_delta[c] << " c: " << m_assignment[c] << "\n"; tout << "$" << d << " delta_d: " << src.m_delta[d] << " d: " << m_assignment[d] << " n2: " << n2 << " e1: " << e1.get_weight() << "\n"; display_edge(tout << "found: ", e1);); ++m_stats.m_num_implied_literals; subsumed.push_back(e_id); } } } } public: void find_subsumed(edge_id id, svector& subsumed) { fix_sizes(); find_relevant(m_fw, id); find_relevant(m_bw, id); find_subsumed(id, m_bw, m_fw, subsumed); m_fw.m_visited.reset(); m_bw.m_visited.reset(); if (!subsumed.empty()) { TRACE("diff_logic", display(tout); tout << "subsumed\n"; for (unsigned i = 0; i < subsumed.size(); ++i) { display_edge(tout, m_edges[subsumed[i]]); }); } } // Find edges that are directly subsumed by id. void find_subsumed1(edge_id id, svector& subsumed) { edge const& e1 = m_edges[id]; dl_var src = e1.get_source(); dl_var dst = e1.get_target(); edge_id_vector& out_edges = m_out_edges[src]; edge_id_vector& in_edges = m_in_edges[dst]; numeral w = e1.get_weight(); if (out_edges.size() < in_edges.size()) { for (edge_id e_id : out_edges) { ++m_stats.m_implied_literal_cost; edge const& e2 = m_edges[e_id]; if (e_id != id && !e2.is_enabled() && e2.get_target() == dst && e2.get_weight() >= w) { subsumed.push_back(e_id); ++m_stats.m_num_implied_literals; } } } else { for (edge_id e_id : in_edges) { ++m_stats.m_implied_literal_cost; edge const& e2 = m_edges[e_id]; if (e_id != id && !e2.is_enabled() && e2.get_source() == src && e2.get_weight() >= w) { subsumed.push_back(e_id); ++m_stats.m_num_implied_literals; } } } } // // Find edges that are subsumed by id, or is an edge between // a predecessor of id's source and id's destination, or // is an edge between a successor of id's dst, and id's source. // // src - id -> dst // - - // src' dst' // // so searching for: // . src - id' -> dst // . src' - id' -> dst // . src - id' -> dst' // void find_subsumed2(edge_id id, svector& subsumed) { edge const& e1 = m_edges[id]; dl_var src = e1.get_source(); dl_var dst = e1.get_target(); numeral w = e1.get_weight(); numeral w2; find_subsumed1(id, subsumed); for (edge_id e_id : m_in_edges[src]) { edge const& e2 = m_edges[e_id]; if (!e2.is_enabled() || e2.get_source() == dst) { continue; } w2 = e2.get_weight() + w; for (edge_id e_id3 : m_out_edges[e2.get_source()]) { ++m_stats.m_implied_literal_cost; edge const& e3 = m_edges[e_id3]; if (e3.is_enabled() || e3.get_target() != dst) { continue; } if (e3.get_weight() >= w2) { subsumed.push_back(e_id3); ++m_stats.m_num_implied_literals; } } } for (edge_id e_id : m_out_edges[dst]) { edge const& e2 = m_edges[e_id]; if (!e2.is_enabled() || e2.get_target() == src) { continue; } w2 = e2.get_weight() + w; for (edge_id e_id3 : m_in_edges[e2.get_target()]) { ++m_stats.m_implied_literal_cost; edge const& e3 = m_edges[e_id3]; if (e3.is_enabled() || e3.get_source() != src) { continue; } if (e3.get_weight() >= w2) { subsumed.push_back(e_id3); ++m_stats.m_num_implied_literals; } } } } template void explain_subsumed_lazy(edge_id bridge_id, edge_id subsumed_id, Functor& f) { edge const& e1 = m_edges[bridge_id]; edge const& e2 = m_edges[subsumed_id]; dl_var src2 = e2.get_source(); dl_var dst2 = e2.get_target(); unsigned timestamp = e1.get_timestamp(); // // Find path from src2 to dst2 with edges having timestamps no greater than // timestamp, and of length no longer than weight of e2. // // use basic O(m*n) algorithm that traverses each edge once per node. // ++m_stats.m_num_helpful_implied_literals; SASSERT(m_heap.empty()); SASSERT(e1.is_enabled()); m_gamma[src2].reset(); m_gamma[dst2] = e2.get_weight(); m_heap.insert(src2); m_visited.push_back(src2); TRACE("diff_logic", display_edge(tout << "bridge: ", e1); display_edge(tout << "subsumed: ", e2); display(tout); ); while (true) { SASSERT(!m_heap.empty()); dl_var v = m_heap.erase_min(); m_mark[v] = DL_PROCESSED; TRACE("diff_logic", tout << v << "\n";); for (edge_id e_id : m_out_edges[v]) { edge const& e = m_edges[e_id]; if (!e.is_enabled() || e.get_timestamp() > timestamp) { continue; } dl_var w = e.get_target(); numeral gamma = m_gamma[v] + e.get_weight(); if ((m_mark[w] != DL_UNMARKED) && m_gamma[w] <= gamma) { continue; } m_gamma[w] = gamma; m_parent[w] = e_id; TRACE("diff_logic", tout << w << " : " << gamma << " " << e2.get_weight() << "\n";); if (w == dst2 && gamma <= e2.get_weight()) { // found path. reset_marks(); m_heap.reset(); unsigned length = 0; do { inc_activity(m_parent[w]); edge const& ee = m_edges[m_parent[w]]; f(ee.get_explanation()); w = ee.get_source(); ++length; } while (w != src2); return; } switch(m_mark[w]) { case DL_UNMARKED: m_visited.push_back(w); // fall through case DL_PROCESSED: m_mark[w] = DL_FOUND; m_heap.insert(w); break; case DL_FOUND: m_heap.decreased(w); break; } } } UNREACHABLE(); } }; #endif /* DIFF_LOGIC_H_ */ z3-z3-4.8.7/src/smt/dyn_ack.cpp000066400000000000000000000464011356505360400161420ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dyn_ack.cpp Abstract: Dynamic Ackermann's reduction Author: Leonardo de Moura (leonardo) 2007-04-24. Revision History: --*/ #include "smt/smt_context.h" #include "smt/dyn_ack.h" #include "ast/ast_pp.h" namespace smt { /** \brief Justification for dynamic ackermann clause */ class dyn_ack_justification : public justification { app * m_app1; app * m_app2; public: dyn_ack_justification(app * n1, app * n2): justification(false), // dyn_ack_justifications are not stored in regions. m_app1(n1), m_app2(n2) { SASSERT(m_app1->get_num_args() == m_app2->get_num_args()); SASSERT(m_app1->get_decl() == m_app2->get_decl()); SASSERT(m_app1->get_num_args() > 0); SASSERT(m_app1->get_id() < m_app2->get_id()); } char const * get_name() const override { return "dyn-ack"; } void get_antecedents(conflict_resolution & cr) override { } void display_debug_info(conflict_resolution & cr, std::ostream & out) override { ast_manager & m = cr.get_manager(); out << "m_app1:\n" << mk_pp(m_app1, m) << "\n"; out << "m_app2:\n" << mk_pp(m_app2, m) << "\n"; } /** \brief Make a hypothesis (= lhs rhs) for the given equality. The arguments of the given equality eq may have been swapped. That is, \c eq is of the form (= rhs lhs). In this case, we also apply a symmetry rule. \remark if negate == true, then the hypothesis is actually (not (= lhs rhs)) */ proof * mk_hypothesis(ast_manager & m, app * eq, bool negate, expr * lhs, expr * rhs) { SASSERT(m.is_eq(eq) || m.is_iff(eq)); SASSERT((eq->get_arg(0) == lhs && eq->get_arg(1) == rhs) || (eq->get_arg(0) == rhs && eq->get_arg(1) == lhs)); app * h = negate ? m.mk_not(eq) : eq; if (eq->get_arg(0) == lhs && eq->get_arg(1) == rhs) { return m.mk_hypothesis(h); } else { return m.mk_symmetry(m.mk_hypothesis(h)); } } proof * mk_proof(conflict_resolution & cr) override { ast_manager & m = cr.get_manager(); context & ctx = cr.get_context(); unsigned num_args = m_app1->get_num_args(); ptr_buffer prs; ptr_buffer lits; for (unsigned i = 0; i < num_args; i++) { expr * arg1 = m_app1->get_arg(i); expr * arg2 = m_app2->get_arg(i); if (arg1 != arg2) { app * eq = ctx.mk_eq_atom(arg1, arg2); app * neq = m.mk_not(eq); if (std::find(lits.begin(), lits.end(), neq) == lits.end()) { lits.push_back(neq); prs.push_back(mk_hypothesis(m, eq, false, arg1, arg2)); } } } proof * antecedents[2]; antecedents[0] = m.mk_congruence(m_app1, m_app2, prs.size(), prs.c_ptr()); app * eq = ctx.mk_eq_atom(m_app1, m_app2); antecedents[1] = mk_hypothesis(m, eq, true, m_app1, m_app2); proof * false_pr = m.mk_unit_resolution(2, antecedents); lits.push_back(eq); SASSERT(lits.size() >= 2); app * lemma = m.mk_or(lits.size(), lits.c_ptr()); TRACE("dyn_ack", tout << mk_pp(lemma, m) << "\n";); TRACE("dyn_ack", tout << mk_pp(false_pr, m) << "\n";); return m.mk_lemma(false_pr, lemma); } }; dyn_ack_manager::dyn_ack_manager(context & ctx, dyn_ack_params & p): m_context(ctx), m(ctx.get_manager()), m_params(p) { } dyn_ack_manager::~dyn_ack_manager() { reset_app_pairs(); reset_app_triples(); } void dyn_ack_manager::reset_app_pairs() { for (app_pair& p : m_app_pairs) { m.dec_ref(p.first); m.dec_ref(p.second); } m_app_pairs.reset(); } void dyn_ack_manager::init_search_eh() { m_app_pair2num_occs.reset(); reset_app_pairs(); m_to_instantiate.reset(); m_qhead = 0; m_num_instances = 0; m_num_propagations_since_last_gc = 0; m_triple.m_app2num_occs.reset(); reset_app_triples(); m_triple.m_to_instantiate.reset(); m_triple.m_qhead = 0; } void dyn_ack_manager::cg_eh(app * n1, app * n2) { SASSERT(n1->get_decl() == n2->get_decl()); SASSERT(n1->get_num_args() == n2->get_num_args()); SASSERT(n1 != n2); if (m.is_eq(n1)) { return; } if (n1->get_id() > n2->get_id()) std::swap(n1,n2); app_pair p(n1, n2); if (m_instantiated.contains(p)) { return; } unsigned num_occs = 0; if (m_app_pair2num_occs.find(n1, n2, num_occs)) { TRACE("dyn_ack", tout << "used_cg_eh:\n" << mk_pp(n1, m) << "\n" << mk_pp(n2, m) << "\nnum_occs: " << num_occs << "\n";); num_occs++; } else { num_occs = 1; m.inc_ref(n1); m.inc_ref(n2); m_app_pairs.push_back(p); } SASSERT(num_occs > 0); m_app_pair2num_occs.insert(n1, n2, num_occs); #ifdef Z3DEBUG unsigned num_occs2 = 0; SASSERT(m_app_pair2num_occs.find(n1, n2, num_occs2) && num_occs == num_occs2); #endif if (num_occs == m_params.m_dack_threshold) { TRACE("dyn_ack", tout << "found candidate:\n" << mk_pp(n1, m) << "\n" << mk_pp(n2, m) << "\nnum_occs: " << num_occs << "\n";); m_to_instantiate.push_back(p); } } void dyn_ack_manager::eq_eh(app * n1, app * n2, app* r) { if (n1 == n2 || r == n1 || r == n2 || m.is_bool(n1)) { return; } if (n1->get_id() > n2->get_id()) std::swap(n1,n2); TRACE("dyn_ack", tout << mk_pp(n1, m) << " = " << mk_pp(n2, m) << " = " << mk_pp(r, m) << "\n";); app_triple tr(n1, n2, r); if (m_triple.m_instantiated.contains(tr)) { return; } unsigned num_occs = 0; if (m_triple.m_app2num_occs.find(n1, n2, r, num_occs)) { TRACE("dyn_ack", tout << mk_pp(n1, m) << "\n" << mk_pp(n2, m) << mk_pp(r, m) << "\n" << "\nnum_occs: " << num_occs << "\n";); num_occs++; } else { num_occs = 1; m.inc_ref(n1); m.inc_ref(n2); m.inc_ref(r); m_triple.m_apps.push_back(tr); } SASSERT(num_occs > 0); m_triple.m_app2num_occs.insert(n1, n2, r, num_occs); #ifdef Z3DEBUG unsigned num_occs2 = 0; SASSERT(m_triple.m_app2num_occs.find(n1, n2, r, num_occs2) && num_occs == num_occs2); #endif if (num_occs == m_params.m_dack_threshold) { TRACE("dyn_ack", tout << "found candidate:\n" << mk_pp(n1, m) << "\n" << mk_pp(n2, m) << "\n" << mk_pp(r, m) << "\nnum_occs: " << num_occs << "\n";); m_triple.m_to_instantiate.push_back(tr); } } struct app_pair_lt { typedef std::pair app_pair; typedef obj_pair_map app_pair2num_occs; app_pair2num_occs & m_app_pair2num_occs; app_pair_lt(app_pair2num_occs & m): m_app_pair2num_occs(m) { } bool operator()(app_pair const & p1, app_pair const & p2) const { unsigned n1 = 0; unsigned n2 = 0; m_app_pair2num_occs.find(p1.first, p1.second, n1); m_app_pair2num_occs.find(p2.first, p2.second, n2); SASSERT(n1 > 0); SASSERT(n2 > 0); return n1 > n2; } }; void dyn_ack_manager::gc() { TRACE("dyn_ack", tout << "dyn_ack GC\n";); unsigned num_deleted = 0; m_to_instantiate.reset(); m_qhead = 0; svector::iterator it = m_app_pairs.begin(); svector::iterator end = m_app_pairs.end(); svector::iterator it2 = it; for (; it != end; ++it) { app_pair & p = *it; if (m_instantiated.contains(p)) { TRACE("dyn_ack", tout << "1) erasing:\n" << mk_pp(p.first, m) << "\n" << mk_pp(p.second, m) << "\n";); m.dec_ref(p.first); m.dec_ref(p.second); SASSERT(!m_app_pair2num_occs.contains(p.first, p.second)); continue; } unsigned num_occs = 0; m_app_pair2num_occs.find(p.first, p.second, num_occs); // The following invariant is not true. p.first and // p.second may have been instantiated, and removed from // m_app_pair2num_occs, but not from m_app_pairs. // // SASSERT(num_occs > 0); num_occs = static_cast(num_occs * m_params.m_dack_gc_inv_decay); if (num_occs <= 1) { num_deleted++; TRACE("dyn_ack", tout << "2) erasing:\n" << mk_pp(p.first, m) << "\n" << mk_pp(p.second, m) << "\n";); m_app_pair2num_occs.erase(p.first, p.second); m.dec_ref(p.first); m.dec_ref(p.second); continue; } *it2 = p; ++it2; SASSERT(num_occs > 0); m_app_pair2num_occs.insert(p.first, p.second, num_occs); if (num_occs >= m_params.m_dack_threshold) m_to_instantiate.push_back(p); } m_app_pairs.set_end(it2); app_pair_lt f(m_app_pair2num_occs); // app_pair_lt is not a total order on pairs of expressions. // So, we should use stable_sort to avoid different behavior in different platforms. std::stable_sort(m_to_instantiate.begin(), m_to_instantiate.end(), f); // IF_VERBOSE(10, if (num_deleted > 0) verbose_stream() << "dynamic ackermann GC: " << num_deleted << "\n";); } class dyn_ack_clause_del_eh : public clause_del_eh { dyn_ack_manager & m; public: dyn_ack_clause_del_eh(dyn_ack_manager & m): m(m) { } ~dyn_ack_clause_del_eh() override {} void operator()(ast_manager & _m, clause * cls) override { m.del_clause_eh(cls); dealloc(this); } }; void dyn_ack_manager::del_clause_eh(clause * cls) { m_context.m_stats.m_num_del_dyn_ack++; app_pair p((app*)nullptr,(app*)nullptr); if (m_clause2app_pair.find(cls, p)) { SASSERT(p.first && p.second); m_instantiated.erase(p); SASSERT(!m_app_pair2num_occs.contains(p.first, p.second)); return; } app_triple tr(0,0,0); if (m_triple.m_clause2apps.find(cls, tr)) { SASSERT(tr.first && tr.second && tr.third); m_triple.m_instantiated.erase(tr); SASSERT(!m_triple.m_app2num_occs.contains(tr.first, tr.second, tr.third)); return; } } void dyn_ack_manager::propagate_eh() { if (m_params.m_dack == DACK_DISABLED) return; m_num_propagations_since_last_gc++; if (m_num_propagations_since_last_gc > m_params.m_dack_gc) { gc(); m_num_propagations_since_last_gc = 0; } unsigned max_instances = static_cast(m_context.get_num_conflicts() * m_params.m_dack_factor); while (m_num_instances < max_instances && m_qhead < m_to_instantiate.size()) { app_pair & p = m_to_instantiate[m_qhead]; m_qhead++; m_num_instances++; instantiate(p.first, p.second); } while (m_num_instances < max_instances && m_triple.m_qhead < m_triple.m_to_instantiate.size()) { app_triple & p = m_triple.m_to_instantiate[m_triple.m_qhead]; m_triple.m_qhead++; m_num_instances++; instantiate(p.first, p.second, p.third); } } literal dyn_ack_manager::mk_eq(expr * n1, expr * n2) { app * eq = m_context.mk_eq_atom(n1, n2); m_context.internalize(eq, true); literal l = m_context.get_literal(eq); TRACE("dyn_ack", tout << "eq:\n" << mk_pp(eq, m) << "\nliteral: "; m_context.display_literal(tout, l); tout << "\n";); return l; } void dyn_ack_manager::instantiate(app * n1, app * n2) { SASSERT(m_params.m_dack != DACK_DISABLED); SASSERT(n1->get_decl() == n2->get_decl()); SASSERT(n1->get_num_args() == n2->get_num_args()); SASSERT(n1 != n2); m_context.m_stats.m_num_dyn_ack++; TRACE("dyn_ack_inst", tout << "dyn_ack: " << n1->get_id() << " " << n2->get_id() << "\n";); TRACE("dyn_ack", tout << "expanding Ackermann's rule for:\n" << mk_pp(n1, m) << "\n" << mk_pp(n2, m) << "\n";); unsigned num_args = n1->get_num_args(); literal_buffer lits; for (unsigned i = 0; i < num_args; i++) { expr * arg1 = n1->get_arg(i); expr * arg2 = n2->get_arg(i); if (arg1 != arg2) lits.push_back(~mk_eq(arg1, arg2)); } app_pair p(n1, n2); SASSERT(m_app_pair2num_occs.contains(n1, n2)); m_app_pair2num_occs.erase(n1, n2); // pair n1,n2 is still in m_app_pairs m_instantiated.insert(p); lits.push_back(mk_eq(n1, n2)); clause_del_eh * del_eh = alloc(dyn_ack_clause_del_eh, *this); justification * js = nullptr; if (m.proofs_enabled()) js = alloc(dyn_ack_justification, n1, n2); clause * cls = m_context.mk_clause(lits.size(), lits.c_ptr(), js, CLS_TH_LEMMA, del_eh); if (!cls) { dealloc(del_eh); return; } TRACE("dyn_ack_clause", tout << "new clause:\n"; m_context.display_clause_detail(tout, cls); tout << "\n";); m_clause2app_pair.insert(cls, p); } void dyn_ack_manager::reset() { init_search_eh(); m_instantiated.reset(); m_clause2app_pair.reset(); m_triple.m_instantiated.reset(); m_triple.m_clause2apps.reset(); } void dyn_ack_manager::reset_app_triples() { for (app_triple& p : m_triple.m_apps) { m.dec_ref(p.first); m.dec_ref(p.second); m.dec_ref(p.third); } m_triple.m_apps.reset(); } void dyn_ack_manager::instantiate(app * n1, app * n2, app* r) { SASSERT(m_params.m_dack != DACK_DISABLED); SASSERT(n1 != n2 && n1 != r && n2 != r); m_context.m_stats.m_num_dyn_ack++; TRACE("dyn_ack_inst", tout << "dyn_ack: " << n1->get_id() << " " << n2->get_id() << " " << r->get_id() << "\n";); TRACE("dyn_ack", tout << "expanding Ackermann's rule for:\n" << mk_pp(n1, m) << "\n" << mk_pp(n2, m) << "\n" << mk_pp(r, m) << "\n"; ); app_triple tr(n1, n2, r); SASSERT(m_triple.m_app2num_occs.contains(n1, n2, r)); m_triple.m_app2num_occs.erase(n1, n2, r); // pair n1,n2 is still in m_triple.m_apps m_triple.m_instantiated.insert(tr); literal_buffer lits; lits.push_back(~mk_eq(n1, r)); lits.push_back(~mk_eq(n2, r)); lits.push_back(mk_eq(n1, n2)); clause_del_eh * del_eh = alloc(dyn_ack_clause_del_eh, *this); justification * js = nullptr; if (m.proofs_enabled()) js = alloc(dyn_ack_justification, n1, n2); clause * cls = m_context.mk_clause(lits.size(), lits.c_ptr(), js, CLS_TH_LEMMA, del_eh); if (!cls) { dealloc(del_eh); return; } TRACE("dyn_ack_clause", tout << "new clause:\n"; m_context.display_clause_detail(tout, cls); tout << "\n";); m_triple.m_clause2apps.insert(cls, tr); } struct app_triple_lt { typedef triple app_triple; typedef obj_triple_map app_triple2num_occs; app_triple2num_occs & m_app_triple2num_occs; app_triple_lt(app_triple2num_occs & m): m_app_triple2num_occs(m) { } bool operator()(app_triple const & p1, app_triple const & p2) const { unsigned n1 = 0; unsigned n2 = 0; m_app_triple2num_occs.find(p1.first, p1.second, p1.third, n1); m_app_triple2num_occs.find(p2.first, p2.second, p2.third, n2); SASSERT(n1 > 0); SASSERT(n2 > 0); return n1 > n2; } }; void dyn_ack_manager::gc_triples() { TRACE("dyn_ack", tout << "dyn_ack GC\n";); unsigned num_deleted = 0; m_triple.m_to_instantiate.reset(); m_triple.m_qhead = 0; svector::iterator it = m_triple.m_apps.begin(); svector::iterator end = m_triple.m_apps.end(); svector::iterator it2 = it; for (; it != end; ++it) { app_triple & p = *it; if (m_triple.m_instantiated.contains(p)) { TRACE("dyn_ack", tout << "1) erasing:\n" << mk_pp(p.first, m) << "\n" << mk_pp(p.second, m) << "\n";); m.dec_ref(p.first); m.dec_ref(p.second); m.dec_ref(p.third); SASSERT(!m_triple.m_app2num_occs.contains(p.first, p.second, p.third)); continue; } unsigned num_occs = 0; m_triple.m_app2num_occs.find(p.first, p.second, p.third, num_occs); // The following invariant is not true. p.first and // p.second may have been instantiated, and removed from // m_app_triple2num_occs, but not from m_app_triples. // // SASSERT(num_occs > 0); num_occs = static_cast(num_occs * m_params.m_dack_gc_inv_decay); if (num_occs <= 1) { num_deleted++; TRACE("dyn_ack", tout << "2) erasing:\n" << mk_pp(p.first, m) << "\n" << mk_pp(p.second, m) << "\n";); m_triple.m_app2num_occs.erase(p.first, p.second, p.third); m.dec_ref(p.first); m.dec_ref(p.second); m.dec_ref(p.third); continue; } *it2 = p; ++it2; SASSERT(num_occs > 0); m_triple.m_app2num_occs.insert(p.first, p.second, p.third, num_occs); if (num_occs >= m_params.m_dack_threshold) m_triple.m_to_instantiate.push_back(p); } m_triple.m_apps.set_end(it2); app_triple_lt f(m_triple.m_app2num_occs); // app_triple_lt is not a total order std::stable_sort(m_triple.m_to_instantiate.begin(), m_triple.m_to_instantiate.end(), f); // IF_VERBOSE(10, if (num_deleted > 0) verbose_stream() << "dynamic ackermann GC: " << num_deleted << "\n";); } #ifdef Z3DEBUG bool dyn_ack_manager::check_invariant() const { for (auto const& kv : m_clause2app_pair) { app_pair const & p = kv.get_value(); SASSERT(m_instantiated.contains(p)); SASSERT(!m_app_pair2num_occs.contains(p.first, p.second)); } return true; } #endif }; z3-z3-4.8.7/src/smt/dyn_ack.h000066400000000000000000000103771356505360400156120ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dyn_ack.h Abstract: Support code for implementing Dynamic Ackermann's reduction Author: Leonardo de Moura (leonardo) 2007-04-12. Revision History: --*/ #ifndef DYN_ACK_H_ #define DYN_ACK_H_ #include "ast/ast.h" #include "smt/params/dyn_ack_params.h" #include "util/obj_hashtable.h" #include "util/obj_pair_hashtable.h" #include "util/obj_triple_hashtable.h" #include "smt/smt_clause.h" namespace smt { class context; class dyn_ack_manager { typedef std::pair app_pair; typedef obj_pair_map app_pair2num_occs; typedef svector app_pair_vector; typedef obj_pair_hashtable app_pair_set; typedef obj_map clause2app_pair; typedef triple app_triple; typedef obj_triple_map app_triple2num_occs; typedef svector app_triple_vector; typedef obj_triple_hashtable app_triple_set; typedef obj_map clause2app_triple; context & m_context; ast_manager & m; dyn_ack_params & m_params; app_pair2num_occs m_app_pair2num_occs; app_pair_vector m_app_pairs; app_pair_vector m_to_instantiate; unsigned m_qhead; unsigned m_num_instances; unsigned m_num_propagations_since_last_gc; app_pair_set m_instantiated; clause2app_pair m_clause2app_pair; struct _triple { app_triple2num_occs m_app2num_occs; app_triple_vector m_apps; app_triple_vector m_to_instantiate; unsigned m_qhead; unsigned m_num_instances; unsigned m_num_propagations_since_last_gc; app_triple_set m_instantiated; clause2app_triple m_clause2apps; }; _triple m_triple; void gc(); void reset_app_pairs(); friend class dyn_ack_clause_del_eh; void del_clause_eh(clause * cls); void instantiate(app * n1, app * n2); literal mk_eq(expr * n1, expr * n2); void cg_eh(app * n1, app * n2); void eq_eh(app * n1, app * n2, app* r); void instantiate(app * n1, app * n2, app* r); void reset_app_triples(); void gc_triples(); public: dyn_ack_manager(context & ctx, dyn_ack_params & p); ~dyn_ack_manager(); void setup() { } /** \brief This method is invoked before the beginning of the search. */ void init_search_eh(); /** \brief This method is invoked when the congruence rule was used during conflict resolution. */ void used_cg_eh(app * n1, app * n2) { if (m_params.m_dack == DACK_CR) cg_eh(n1, n2); } /** \brief This method is invoked when the congruence rule is the root of a conflict. */ void cg_conflict_eh(app * n1, app * n2) { if (m_params.m_dack == DACK_ROOT) cg_eh(n1, n2); } /** \brief This method is invoked when equalities are used during conflict resolution. */ void used_eq_eh(app * n1, app * n2, app* r) { if (m_params.m_dack_eq) eq_eh(n1, n2, r); } /** \brief This method is invoked when it is safe to expand the new ackermann rule entries. */ void propagate_eh(); void reset(); #ifdef Z3DEBUG bool check_invariant() const; #endif }; }; #endif /* DYN_ACK_H_ */ z3-z3-4.8.7/src/smt/elim_term_ite.cpp000066400000000000000000000015571356505360400173530ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: elim_term_ite.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-06-12. Revision History: --*/ #include "smt/elim_term_ite.h" #include "ast/ast_smt2_pp.h" br_status elim_term_ite_cfg::reduce_app(func_decl* f, unsigned n, expr * const* args, expr_ref& result, proof_ref& result_pr) { if (!m.is_term_ite(f)) { return BR_FAILED; } expr_ref new_def(m); proof_ref new_def_pr(m); app_ref r(m.mk_app(f, n, args), m); app_ref new_r(m); if (!m_defined_names.mk_name(r, new_def, new_def_pr, new_r, result_pr)) { return BR_FAILED; } result = new_r; CTRACE("elim_term_ite_bug", new_def.get() == 0, tout << mk_ismt2_pp(r, m) << "\n";); m_new_defs.push_back(justified_expr(m, new_def, new_def_pr)); return BR_DONE; } z3-z3-4.8.7/src/smt/elim_term_ite.h000066400000000000000000000030171356505360400170110ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: elim_term_ite.h Abstract: Author: Leonardo de Moura (leonardo) 2008-06-12. Revision History: --*/ #ifndef ELIM_TERM_ITE_H_ #define ELIM_TERM_ITE_H_ #include "ast/normal_forms/defined_names.h" #include "ast/rewriter/rewriter.h" #include "ast/justified_expr.h" class elim_term_ite_cfg : public default_rewriter_cfg { ast_manager& m; defined_names & m_defined_names; vector m_new_defs; unsigned_vector m_lim; public: elim_term_ite_cfg(ast_manager & m, defined_names & d): m(m), m_defined_names(d) { // TBD enable_ac_support(false); } virtual ~elim_term_ite_cfg() {} vector const& new_defs() const { return m_new_defs; } br_status reduce_app(func_decl* f, unsigned n, expr *const* args, expr_ref& result, proof_ref& result_pr); void push() { m_lim.push_back(m_new_defs.size()); } void pop(unsigned n) {if (n > 0) { m_new_defs.shrink(m_lim[m_lim.size() - n]); m_lim.shrink(m_lim.size() - n); } } }; class elim_term_ite_rw : public rewriter_tpl { elim_term_ite_cfg m_cfg; public: elim_term_ite_rw(ast_manager& m, defined_names & dn): rewriter_tpl(m, m.proofs_enabled(), m_cfg), m_cfg(m, dn) {} vector const& new_defs() const { return m_cfg.new_defs(); } void push() { m_cfg.push(); } void pop(unsigned n) { m_cfg.pop(n); } }; #endif /* ELIM_TERM_ITE_H_ */ z3-z3-4.8.7/src/smt/expr_context_simplifier.cpp000066400000000000000000000545651356505360400215110ustar00rootroot00000000000000/*++ Copyright (c) 2008 Microsoft Corporation Module Name: expr_context_simplifier.cpp Abstract: Author: Nikolaj Bjorner (nbjorner) 2008-06-03 Revision History: --*/ #include "smt/expr_context_simplifier.h" #include "ast/ast_pp.h" #include "util/obj_hashtable.h" #include "smt/smt_kernel.h" #include "ast/for_each_expr.h" // table lookup before/after simplification. expr_context_simplifier::expr_context_simplifier(ast_manager& m): m_manager(m), m_arith(m), m_trail(m), m_simp(m), m_forward(true) {} void expr_context_simplifier::reduce(expr * m, expr_ref & result) { expr_ref tmp(m_manager); m_mark.reset(); unsigned trail_size = m_trail.size(); m_forward = true; reduce_rec(m, tmp); m_mark.reset(); m_forward = false; reduce_rec(tmp.get(), result); clean_trail(trail_size); } void expr_context_simplifier::reduce_fix(expr * m, expr_ref & result) { expr_ref tmp(m_manager); result = m; do { tmp = result.get(); reduce(tmp.get(), result); } while (tmp.get() != result.get()); } void expr_context_simplifier::reduce_rec(expr * m, expr_ref & result) { // // reduce expr in context evaluation. // bool polarity; if (m_context.find(m, polarity)) { result = polarity ? m_manager.mk_true() : m_manager.mk_false(); } else if (m_mark.is_marked(m) && !m_manager.is_not(m)) { result = m; } else if (is_quantifier(m)) { reduce_rec(to_quantifier(m), result); m_mark.mark(m, true); } else if (is_app(m)) { reduce_rec(to_app(m), result); m_mark.mark(m, true); } else if (is_var(m)) { result = m; m_mark.mark(m, true); } else { UNREACHABLE(); result = m; } } void expr_context_simplifier::reduce_rec(quantifier* q, expr_ref & result) { result = q; #if 0 // // The context assumes that asserted expressions are in NNF with // respect to the quantifier occurrences. // This can be disabled if the strong context simplifier // is called from the API over the Z3_simplify method. // expr_context_simplifier nested(m_manager); expr_ref body_r(m_manager); nested.reduce(q->get_expr(), body_r); if (body_r.get() != q->get_expr()) { result = m_manager.update_quantifier(q, body_r.get()); } else { result = q; } #endif } void expr_context_simplifier::reduce_rec(app * a, expr_ref & result) { if (m_manager.get_basic_family_id() == a->get_family_id()) { switch(a->get_decl_kind()) { case OP_AND: reduce_and(a->get_num_args(), a->get_args(), result); return; case OP_OR: reduce_or(a->get_num_args(), a->get_args(), result); return; case OP_EQ: if (m_manager.is_iff(a)) { expr_ref tmp1(m_manager), tmp2(m_manager); reduce_rec(a->get_arg(0), tmp1); reduce_rec(a->get_arg(1), tmp2); m_simp.mk_iff(tmp1.get(), tmp2.get(), result); return; } break; case OP_XOR: { expr_ref tmp1(m_manager), tmp2(m_manager); reduce_rec(a->get_arg(0), tmp1); reduce_rec(a->get_arg(1), tmp2); m_simp.mk_xor(tmp1.get(), tmp2.get(), result); return; } case OP_NOT: { expr_ref tmp(m_manager); reduce_rec(a->get_arg(0), tmp); m_simp.mk_not(tmp.get(), result); return; } case OP_IMPLIES: { app_ref tmp(m_manager); tmp = m_manager.mk_not(a->get_arg(0)); expr* args[2] = { tmp.get(), a->get_arg(1) }; reduce_or(2, args, result); return; } case OP_ITE: { expr_ref tmp(m_manager), tmp1(m_manager), tmp2(m_manager); reduce_rec(a->get_arg(0), tmp); if (is_true(tmp.get())) { reduce_rec(a->get_arg(1), result); } else if (is_false(tmp.get())) { reduce_rec(a->get_arg(2), result); } else { unsigned trail_size = m_trail.size(); insert_context(tmp.get(), true); reduce_rec(a->get_arg(1), tmp1); clean_trail(trail_size); insert_context(tmp.get(), false); reduce_rec(a->get_arg(2), tmp2); clean_trail(trail_size); m_simp.mk_ite(tmp.get(), tmp1.get(), tmp2.get(), result); } return; } default: break; } } expr_ref_vector args(m_manager); for (unsigned i = 0; i < a->get_num_args(); ++i) { expr_ref tmp(m_manager); reduce_rec(a->get_arg(i), tmp); args.push_back(tmp.get()); } result = m_manager.mk_app(a->get_decl(), args.size(), args.c_ptr()); } void expr_context_simplifier::clean_trail(unsigned old_lim) { for (unsigned i = m_trail.size(); i > old_lim; ) { --i; m_context.erase(m_trail[i].get()); } m_trail.resize(old_lim); } void expr_context_simplifier::insert_context(expr* e, bool polarity) { TRACE("expr_context_simplifier", tout << mk_pp(e, m_manager) << "\n";); if (m_manager.is_not(e)) { e = to_app(e)->get_arg(0); polarity = !polarity; } if (!m_context.contains(e)) { m_context.insert(e, polarity); m_trail.push_back(e); } } bool expr_context_simplifier::insert_arg(bool is_and, expr* arg, expr_ref_vector& args) { expr_ref tmp(m_manager); reduce_rec(arg, tmp); TRACE("expr_context_simplifier", tout << mk_pp(arg, m_manager) << " -> " << mk_pp(tmp.get(), m_manager) << "\n";); if (is_true(tmp.get()) && is_and) { // skip. } else if (is_false(tmp.get()) && !is_and) { // skip. } else if (is_false(tmp.get()) && is_and) { return true; } else if (is_true(tmp.get()) && !is_and) { return true; } else { insert_context(tmp.get(), is_and); if (arg != tmp.get()) { insert_context(arg, is_and); // allow to also use un-simplified version } args.push_back(tmp.get()); } return false; } void expr_context_simplifier::reduce_and_or(bool is_and, unsigned num_args, expr * const* args, expr_ref & result) { expr_ref tmp(m_manager); expr_ref_vector args1(m_manager); unsigned trail_size = m_trail.size(); if (m_forward) { for (unsigned i = 0; i < num_args; ++i) { if (insert_arg(is_and, args[i], args1)) { clean_trail(trail_size); result = is_and?m_manager.mk_false():m_manager.mk_true(); return; } } } else { for (unsigned i = num_args; i > 0; ) { --i; if (insert_arg(is_and, args[i], args1)) { clean_trail(trail_size); result = is_and?m_manager.mk_false():m_manager.mk_true(); return; } } } clean_trail(trail_size); if (is_and) { m_simp.mk_and(args1.size(), args1.c_ptr(), result); } else { m_simp.mk_or(args1.size(), args1.c_ptr(), result); } } void expr_context_simplifier::reduce_and(unsigned num_args, expr * const* args, expr_ref & result) { reduce_and_or(true, num_args, args, result); } void expr_context_simplifier::reduce_or(unsigned num_args, expr * const* args, expr_ref & result) { reduce_and_or(false, num_args, args, result); } bool expr_context_simplifier::is_true(expr* e) const { return m_manager.is_true(e) || (m_manager.is_not(e) && m_manager.is_false(to_app(e)->get_arg(0))); } bool expr_context_simplifier::is_false(expr* e) const { return m_manager.is_false(e) || (m_manager.is_not(e) && m_manager.is_true(to_app(e)->get_arg(0))); } // // This routine performs strong context simplification. // It replaces sub-formulas by a fresh name // and checks if the original formula is equivalent // to the resulting formula if the fresh name is set to // true or false. // otherwise it recursively expands the definition of the // fresh name to match the original formula. // // assert ! (fml <=> n) // //for (fml', n') // check visited // check n' // check !n' // if each a is visited, // fml' by fml'[a/a'] // pop_scope // ow, // let a be a non-visited argument. // push_scope // push (a, n'') // assert (n' <=> f(visited_args, n'', visited_or_non_visited_args)) // // The implementation avoid the stack. It uses the following vectors: // todo - DFS stack // names - collection of fresh names. // is_checked - Boolean to control if contextual equivalence with T or F was checked. // parent_ids - stack of IDs used to identify path down to expression on first visit. // self_ids - stack of IDs used to identify path down to children on first visit. // The parent_ids, self_ids stacks are used to ensure that caching results can be done // in a context dependent way. A cached result is only valid for simplification if // it occurs in the context (on the path) where it was inserted. // expr_strong_context_simplifier::expr_strong_context_simplifier(smt_params& p, ast_manager& m): m_manager(m), m_arith(m), m_fn(nullptr,m), m_solver(m, p) { sort* i_sort = m_arith.mk_int(); m_fn = m.mk_func_decl(symbol(0xbeef101), i_sort, m.mk_bool_sort()); } void expr_strong_context_simplifier::simplify_basic(expr* fml, expr_ref& result) { ast_manager& m = m_manager; // // The context assumes that asserted expressions are in NNF with // respect to the quantifier occurrences. // This can be disabled if the strong context simplifier // is called from the API over the Z3_simplify method. // if (!m.is_bool(fml) || has_quantifiers(fml)) { result = fml; return; } ptr_vector todo; ptr_vector names; svector is_checked; svector parent_ids, self_ids; expr_ref_vector fresh_vars(m); expr_ref_vector trail(m); obj_map > cache; m_solver.push(); unsigned id = 1; expr* n = m.mk_app(m_fn, m_arith.mk_numeral(rational(id++), true)); expr* r, *n2; lbool is_sat; unsigned path_id = 0, self_pos = 0; app * a; unsigned sz; trail.push_back(n); m_solver.assert_expr(m.mk_not(m.mk_iff(fml, n))); todo.push_back(fml); names.push_back(n); is_checked.push_back(false); parent_ids.push_back(0); self_ids.push_back(0); std::pair path_r; m_solver.push(); while (!todo.empty()) { r = nullptr; ptr_buffer args; expr* e = todo.back(); unsigned pos = parent_ids.back(); n = names.back(); bool checked = is_checked.back(); if (cache.contains(e)) { goto done; } if (!m.is_bool(e)) { r = e; goto done; } if (m.is_bool(e) && !checked) { m_solver.push(); m_solver.assert_expr(n); is_sat = m_solver.check(); m_solver.pop(1); if (is_sat == l_false) { r = m.mk_true(); goto done; } } if (m.is_bool(e) && !checked) { m_solver.push(); m_solver.assert_expr(m.mk_not(n)); is_sat = m_solver.check(); m_solver.pop(1); if (is_sat == l_false) { r = m.mk_false(); goto done; } } if (!is_app(e)) { r = e; goto done; } a = to_app(e); if (!is_checked.back()) { self_ids.back() = ++path_id; is_checked.back() = true; } self_pos = self_ids.back(); sz = a->get_num_args(); n2 = nullptr; for (unsigned i = 0; i < sz; ++i) { expr* arg = a->get_arg(i); if (cache.find(arg, path_r)) { if (path_r.first == self_pos) { args.push_back(path_r.second); } else { args.push_back(arg); } } else if (!m.is_bool(arg)) { args.push_back(arg); } else if (!n2) { n2 = m.mk_app(m_fn, m_arith.mk_numeral(rational(id++), true)); todo.push_back(arg); parent_ids.push_back(self_pos); self_ids.push_back(0); names.push_back(n2); trail.push_back(n2); args.push_back(n2); is_checked.push_back(false); } else { args.push_back(arg); } } r = m.mk_app(a->get_decl(), args.size(), args.c_ptr()); trail.push_back(r); if (n2) { m_solver.push(); m_solver.assert_expr(m.mk_eq(r, n)); continue; } done: if (r) { cache.insert(e, std::make_pair(pos, r)); } TRACE("expr_context_simplifier", tout << mk_pp(e, m_manager) << " checked: " << checked << " cached: " << mk_pp(r?r:e, m_manager) << "\n";); todo.pop_back(); parent_ids.pop_back(); self_ids.pop_back(); names.pop_back(); is_checked.pop_back(); m_solver.pop(1); } VERIFY(cache.find(fml, path_r)); m_solver.pop(1); result = path_r.second; } void expr_strong_context_simplifier::simplify_model_based(expr* fml, expr_ref& result) { ast_manager& m = m_manager; // // The context assumes that asserted expressions are in NNF with // respect to the quantifier occurrences. // This can be disabled if the strong context simplifier // is called from the API over the Z3_simplify method. // if (!m.is_bool(fml) || has_quantifiers(fml)) { result = fml; return; } ptr_vector todo; ptr_vector names; svector is_checked; svector parent_ids, self_ids; expr_ref_vector fresh_vars(m); expr_ref_vector trail(m); obj_map > cache; lbool is_sat; expr_ref_vector assignments(m); m_solver.push(); m_solver.assert_expr(fml); is_sat = m_solver.check(); if (is_sat != l_false) { m_solver.get_assignments(assignments); } m_solver.pop(1); if (is_sat == l_false) { result = m.mk_false(); return; } // Collect assignments to sub-formulas from satisfying assignment. obj_map assignment_map; { expr* n1, *n2; for (unsigned i = 0; i < assignments.size(); ++i) { if (m.is_not(assignments[i].get(), n1)) { assignment_map.insert(n1, l_false); } else { assignment_map.insert(assignments[i].get(), l_true); } } todo.push_back(fml); while (!todo.empty()) { expr* n = todo.back(); if (!is_app(n)) { assignment_map.insert(n, l_undef); todo.pop_back(); continue; } app* a = to_app(n); unsigned sz = a->get_num_args(); bool all_visit = true; for (unsigned i = 0; i < sz; ++i) { if (!assignment_map.contains(a->get_arg(i))) { todo.push_back(a->get_arg(i)); all_visit = false; } } if (!all_visit) { continue; } todo.pop_back(); lbool value = l_undef; if (m.is_and(a)) { value = l_true; for (unsigned i = 0; value != l_false && i < sz; ++i) { switch(assignment_map.find(a->get_arg(i))) { case l_false: value = l_false; break; case l_undef: value = l_undef; break; default: break; } } assignment_map.insert(a, value); } else if (m.is_or(a)) { value = l_false; for (unsigned i = 0; value != l_true && i < sz; ++i) { switch(assignment_map.find(a->get_arg(i))) { case l_true: value = l_true; break; case l_undef: value = l_undef; break; default: break; } } assignment_map.insert(a, value); } else if (m.is_not(a)) { switch(assignment_map.find(a->get_arg(0))) { case l_true: value = l_false; break; case l_false: value = l_true; break; default: value = l_undef; break; } assignment_map.insert(a, value); } else if (m.is_implies(a, n1, n2)) { lbool v1 = assignment_map.find(n1); lbool v2 = assignment_map.find(n2); if (v1 == l_false || v2 == l_true) { value = l_true; } else if (v1 == l_true && v2 == l_false) { value = l_false; } else { value = l_undef; } assignment_map.insert(a, value); } else if (m.is_eq(a, n1, n2)) { lbool v1 = assignment_map.find(n1); lbool v2 = assignment_map.find(n2); if (v1 == l_undef || v2 == l_undef) { value = l_undef; } else if (v1 == v2) { value = l_true; } else { value = l_false; } assignment_map.insert(a, value); } else { assignment_map.insert(a, l_undef); } } } m_solver.push(); unsigned id = 1; expr* n = m.mk_app(m_fn, m_arith.mk_numeral(rational(id++), true)); expr* r, *n2; unsigned path_id = 0, self_pos = 0; app * a; unsigned sz; trail.push_back(n); m_solver.assert_expr(m.mk_not(m.mk_iff(fml, n))); todo.push_back(fml); names.push_back(n); is_checked.push_back(false); parent_ids.push_back(0); self_ids.push_back(0); std::pair path_r; m_solver.push(); while (!todo.empty()) { r = nullptr; ptr_buffer args; expr* e = todo.back(); unsigned pos = parent_ids.back(); n = names.back(); bool checked = is_checked.back(); if (cache.contains(e)) { goto done; } if (!m.is_bool(e)) { r = e; goto done; } if (m.is_bool(e) && !checked) { lbool value = l_undef; assignment_map.find(e, value); switch(value) { case l_true: if (is_forced(n, m.mk_true())) { r = m.mk_true(); goto done; } break; case l_false: if (is_forced(n, m.mk_false())) { r = m.mk_false(); goto done; } break; default: // NB. assignments contain just internalized literals, // the literals in the input may not be internalized. // we therefore fall back to the default behavior, which // is to check both cases. if (is_forced(n, m.mk_true())) { r = m.mk_true(); goto done; } if (is_forced(n, m.mk_false())) { r = m.mk_false(); goto done; } break; } } if (!is_app(e)) { r = e; goto done; } a = to_app(e); if (!is_checked.back()) { self_ids.back() = ++path_id; is_checked.back() = true; } self_pos = self_ids.back(); sz = a->get_num_args(); n2 = nullptr; for (unsigned i = 0; i < sz; ++i) { expr* arg = a->get_arg(i); if (cache.find(arg, path_r)) { if (path_r.first == self_pos) { args.push_back(path_r.second); } else { args.push_back(arg); } } else if (!m.is_bool(arg)) { args.push_back(arg); } else if (!n2) { n2 = m.mk_app(m_fn, m_arith.mk_numeral(rational(id++), true)); todo.push_back(arg); parent_ids.push_back(self_pos); self_ids.push_back(0); names.push_back(n2); trail.push_back(n2); args.push_back(n2); is_checked.push_back(false); } else { args.push_back(arg); } } r = m.mk_app(a->get_decl(), args.size(), args.c_ptr()); trail.push_back(r); if (n2) { m_solver.push(); m_solver.assert_expr(m.mk_eq(r, n)); continue; } done: if (r) { cache.insert(e, std::make_pair(pos, r)); } TRACE("expr_context_simplifier", tout << mk_pp(e, m_manager) << " checked: " << checked << " cached: " << mk_pp(r?r:e, m_manager) << "\n";); todo.pop_back(); parent_ids.pop_back(); self_ids.pop_back(); names.pop_back(); is_checked.pop_back(); m_solver.pop(1); } VERIFY(cache.find(fml, path_r)); m_solver.pop(1); result = path_r.second; } bool expr_strong_context_simplifier::is_forced(expr* e, expr* v) { m_solver.push(); m_solver.assert_expr(m_manager.mk_eq(e, v)); lbool is_sat = m_solver.check(); m_solver.pop(1); return (is_sat == l_false); } z3-z3-4.8.7/src/smt/expr_context_simplifier.h000066400000000000000000000047271356505360400211510ustar00rootroot00000000000000/*++ Copyright (c) 2008 Microsoft Corporation Module Name: expr_context_simplifier.h Abstract: Author: Nikolaj Bjorner (nbjorner) 2008-06-03 Revision History: --*/ #ifndef EXPR_CONTEXT_SIMPLIFIER_H_ #define EXPR_CONTEXT_SIMPLIFIER_H_ #include "ast/ast.h" #include "util/obj_hashtable.h" #include "smt/params/smt_params.h" #include "smt/smt_kernel.h" #include "ast/arith_decl_plugin.h" #include "ast/rewriter/bool_rewriter.h" class expr_context_simplifier { typedef obj_map context_map; ast_manager& m_manager; arith_util m_arith; context_map m_context; expr_ref_vector m_trail; bool_rewriter m_simp; expr_mark m_mark; bool m_forward; public: expr_context_simplifier(ast_manager & m); void reduce_fix(expr * m, expr_ref & result); void operator()(expr * m, expr_ref & result) { reduce(m, result); } void insert_context(expr* e, bool polarity); private: void reduce(expr * m, expr_ref & result); void reduce_rec(expr * m, expr_ref & result); void reduce_rec(quantifier* q, expr_ref & result); void reduce_rec(app * a, expr_ref & result); void clean_trail(unsigned old_lim); bool insert_arg(bool is_and, expr* arg, expr_ref_vector& args); void reduce_and_or(bool is_and, unsigned num_args, expr * const* args, expr_ref & result); void reduce_and(unsigned num_args, expr * const* args, expr_ref & result); void reduce_or(unsigned num_args, expr * const* args, expr_ref & result); bool is_true(expr* e) const; bool is_false(expr* e) const; }; class expr_strong_context_simplifier { ast_manager& m_manager; arith_util m_arith; func_decl_ref m_fn; smt::kernel m_solver; void simplify(expr* e, expr_ref& result) { simplify_model_based(e, result); } void simplify_basic(expr* fml, expr_ref& result); void simplify_model_based(expr* fml, expr_ref& result); bool is_forced(expr* e, expr* v); public: expr_strong_context_simplifier(smt_params& p, ast_manager& m); void operator()(expr* e, expr_ref& result) { simplify(e, result); } void operator()(expr_ref& result) { simplify(result.get(), result); } void push() { m_solver.push(); } void pop() { m_solver.pop(1); } void assert_expr(expr* e) { m_solver.assert_expr(e); } void collect_statistics(statistics & st) const { m_solver.collect_statistics(st); } void reset_statistics() { m_solver.reset_statistics(); } }; #endif /* EXPR_CONTEXT_SIMPLIFIER_H_ */ z3-z3-4.8.7/src/smt/fingerprints.cpp000066400000000000000000000120641356505360400172420ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: fingerprints.cpp Abstract: Author: Leonardo de Moura (leonardo) 2007-02-24. Revision History: --*/ #include "smt/fingerprints.h" namespace smt { fingerprint::fingerprint(region & r, void * d, unsigned d_h, expr* def, unsigned n, enode * const * args): m_data(d), m_data_hash(d_h), m_def(def), m_num_args(n), m_args(nullptr) { m_args = new (r) enode*[n]; memcpy(m_args, args, sizeof(enode*) * n); } bool fingerprint_set::fingerprint_eq_proc::operator()(fingerprint const * f1, fingerprint const * f2) const { if (f1->get_data() != f2->get_data()) return false; if (f1->get_num_args() != f2->get_num_args()) return false; unsigned n = f1->get_num_args(); for(unsigned i = 0; i < n; i++) if (f1->get_arg(i) != f2->get_arg(i)) return false; return true; } fingerprint * fingerprint_set::mk_dummy(void * data, unsigned data_hash, unsigned num_args, enode * const * args) { m_tmp.reset(); m_tmp.append(num_args, args); m_dummy.m_data = data; m_dummy.m_data_hash = data_hash; m_dummy.m_num_args = num_args; m_dummy.m_args = m_tmp.c_ptr(); return &m_dummy; } std::ostream& operator<<(std::ostream& out, fingerprint const& f) { out << f.get_data_hash() << " " << " num_args " << f.get_num_args() << " "; for (enode const * arg : f) { out << " " << arg->get_owner_id(); } out << "\n"; return out; } fingerprint * fingerprint_set::insert(void * data, unsigned data_hash, unsigned num_args, enode * const * args, expr* def) { fingerprint * d = mk_dummy(data, data_hash, num_args, args); if (m_set.contains(d)) return nullptr; TRACE("fingerprint_bug", tout << "1) inserting: " << data_hash << " num_args: " << num_args; for (unsigned i = 0; i < num_args; i++) tout << " " << args[i]->get_owner_id(); tout << "\n";); for (unsigned i = 0; i < num_args; i++) d->m_args[i] = d->m_args[i]->get_root(); if (m_set.contains(d)) { TRACE("fingerprint_bug", tout << "failed: " << data_hash << " num_args: " << num_args; for (unsigned i = 0; i < num_args; i++) tout << " " << d->m_args[i]->get_owner_id(); tout << "\n";); return nullptr; } TRACE("fingerprint_bug", tout << "2) inserting: " << *d;); fingerprint * f = new (m_region) fingerprint(m_region, data, data_hash, def, num_args, d->m_args); m_fingerprints.push_back(f); m_defs.push_back(def); m_set.insert(f); return f; } bool fingerprint_set::contains(void * data, unsigned data_hash, unsigned num_args, enode * const * args) { fingerprint * d = mk_dummy(data, data_hash, num_args, args); if (m_set.contains(d)) return true; for (unsigned i = 0; i < num_args; i++) d->m_args[i] = d->m_args[i]->get_root(); if (m_set.contains(d)) return true; return false; } void fingerprint_set::reset() { m_set.reset(); m_fingerprints.reset(); m_defs.reset(); } void fingerprint_set::push_scope() { m_scopes.push_back(m_fingerprints.size()); } void fingerprint_set::pop_scope(unsigned num_scopes) { unsigned lvl = m_scopes.size(); SASSERT(num_scopes <= lvl); unsigned new_lvl = lvl - num_scopes; unsigned old_size = m_scopes[new_lvl]; unsigned size = m_fingerprints.size(); for (unsigned i = old_size; i < size; i++) m_set.erase(m_fingerprints[i]); m_fingerprints.shrink(old_size); m_defs.shrink(old_size); m_scopes.shrink(new_lvl); } void fingerprint_set::display(std::ostream & out) const { out << "fingerprints:\n"; SASSERT(m_set.size() == m_fingerprints.size()); for (fingerprint const * f : m_fingerprints) { out << f->get_data() << " " << *f; } } #ifdef Z3DEBUG /** \brief Slow function for checking if there is a fingerprint congruent to (data args[0] ... args[num_args-1]) */ bool fingerprint_set::slow_contains(void const * data, unsigned data_hash, unsigned num_args, enode * const * args) const { for (fingerprint const* f : m_fingerprints) { if (f->get_data() != data) continue; if (f->get_num_args() != num_args) continue; unsigned i = 0; for (i = 0; i < num_args; i++) if (f->get_arg(i)->get_root() != args[i]->get_root()) break; if (i == num_args) { TRACE("missing_instance_detail", tout << "found instance data: " << data << "=" << *f;); return true; } } return false; } #endif }; z3-z3-4.8.7/src/smt/fingerprints.h000066400000000000000000000061711356505360400167110ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: fingerprints.h Abstract: Author: Leonardo de Moura (leonardo) 2007-02-24. Revision History: --*/ #ifndef FINGERPRINTS_H_ #define FINGERPRINTS_H_ #include "smt/smt_enode.h" #include "util/util.h" namespace smt { class fingerprint { protected: void * m_data; unsigned m_data_hash; expr* m_def; unsigned m_num_args; enode * * m_args; friend class fingerprint_set; fingerprint() {} public: fingerprint(region & r, void * d, unsigned d_hash, expr* def, unsigned n, enode * const * args); void * get_data() const { return m_data; } expr * get_def() const { return m_def; } unsigned get_data_hash() const { return m_data_hash; } unsigned get_num_args() const { return m_num_args; } enode * const * get_args() const { return m_args; } enode * get_arg(unsigned idx) const { SASSERT(idx < m_num_args); return m_args[idx]; } enode * const * begin() const { return m_args; } enode * const * end() const { return begin() + get_num_args(); } friend std::ostream& operator<<(std::ostream& out, fingerprint const& f); }; class fingerprint_set { struct fingerprint_khasher { unsigned operator()(fingerprint const * f) const { return f->get_data_hash(); } }; struct fingerprint_chasher { unsigned operator()(fingerprint const * f, unsigned idx) const { return f->get_arg(idx)->hash(); } }; struct fingerprint_hash_proc { unsigned operator()(fingerprint const * f) const { return get_composite_hash(const_cast(f), f->get_num_args()); } }; struct fingerprint_eq_proc { bool operator()(fingerprint const * f1, fingerprint const * f2) const; }; typedef ptr_hashtable set; region & m_region; set m_set; ptr_vector m_fingerprints; expr_ref_vector m_defs; unsigned_vector m_scopes; ptr_vector m_tmp; fingerprint m_dummy; fingerprint * mk_dummy(void * data, unsigned data_hash, unsigned num_args, enode * const * args); public: fingerprint_set(ast_manager& m, region & r): m_region(r), m_defs(m) {} fingerprint * insert(void * data, unsigned data_hash, unsigned num_args, enode * const * args, expr* def); unsigned size() const { return m_fingerprints.size(); } bool contains(void * data, unsigned data_hash, unsigned num_args, enode * const * args); void reset(); void push_scope(); void pop_scope(unsigned num_scopes); void display(std::ostream & out) const; #ifdef Z3DEBUG bool slow_contains(void const * data, unsigned data_hash, unsigned num_args, enode * const * args) const; #endif }; }; #endif /* FINGERPRINTS_H_ */ z3-z3-4.8.7/src/smt/mam.cpp000066400000000000000000005101161356505360400153030ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: mam.cpp Abstract: Matching Abstract Machine Author: Leonardo de Moura (leonardo) 2007-02-13. Revision History: --*/ #include #include "util/pool.h" #include "util/trail.h" #include "util/stopwatch.h" #include "ast/ast_pp.h" #include "ast/ast_ll_pp.h" #include "ast/ast_smt2_pp.h" #include "smt/mam.h" #include "smt/smt_context.h" // #define _PROFILE_MAM // ----------------------------------------- // Flags for _PROFILE_MAM // // send profiling information to stdout #define _PROFILE_MAM_TO_STDOUT // threshold in secs for being considered expensive #define _PROFILE_MAM_THRESHOLD 30.0 // dump expensive (> _PROFILE_MAM_THRESHOLD) code trees whenever execute_core is executed. #define _PROFILE_MAM_EXPENSIVE // #define _PROFILE_MAM_EXPENSIVE_FREQ 100000 // // ----------------------------------------- // #define _PROFILE_PATH_TREE // ----------------------------------------- // Flags for _PROFILE_PATH_TREE // #define _PROFILE_PATH_TREE_THRESHOLD 20000 // // ----------------------------------------- #define IS_CGR_SUPPORT true namespace smt { // ------------------------------------ // // Trail // // ------------------------------------ class mam_impl; typedef trail_stack mam_trail_stack; typedef trail mam_trail; template class mam_value_trail : public value_trail { public: mam_value_trail(T & value):value_trail(value) {} }; // ------------------------------------ // // Auxiliary // // ------------------------------------ class label_hasher { svector m_lbl2hash; // cache: lbl_id -> hash void mk_lbl_hash(unsigned lbl_id) { unsigned a = 17; unsigned b = 3; unsigned c = lbl_id; mix(a, b, c); m_lbl2hash[lbl_id] = c & (APPROX_SET_CAPACITY - 1); } public: unsigned char operator()(func_decl * lbl) { unsigned lbl_id = lbl->get_decl_id(); if (lbl_id >= m_lbl2hash.size()) m_lbl2hash.resize(lbl_id + 1, -1); if (m_lbl2hash[lbl_id] == -1) { mk_lbl_hash(lbl_id); } SASSERT(m_lbl2hash[lbl_id] >= 0); return m_lbl2hash[lbl_id]; } void display(std::ostream & out) const { out << "lbl-hasher:\n"; bool first = true; for (unsigned i = 0; i < m_lbl2hash.size(); i++) { if (m_lbl2hash[i] != -1) { if (first) first = false; else out << ", "; out << i << " -> " << static_cast(m_lbl2hash[i]); } } out << "\n"; } }; // ------------------------------------ // // Instructions // // ------------------------------------ typedef enum { INIT1=0, INIT2, INIT3, INIT4, INIT5, INIT6, INITN, BIND1, BIND2, BIND3, BIND4, BIND5, BIND6, BINDN, YIELD1, YIELD2, YIELD3, YIELD4, YIELD5, YIELD6, YIELDN, COMPARE, CHECK, FILTER, CFILTER, PFILTER, CHOOSE, NOOP, CONTINUE, GET_ENODE, GET_CGR1, GET_CGR2, GET_CGR3, GET_CGR4, GET_CGR5, GET_CGR6, GET_CGRN, IS_CGR } opcode; struct instruction { opcode m_opcode; instruction * m_next; #ifdef _PROFILE_MAM unsigned m_counter; // how often it was executed #endif bool is_init() const { return m_opcode >= INIT1 && m_opcode <= INITN; } }; struct initn : public instruction { // We need that because starting at Z3 3.0, some associative // operators (e.g., + and *) are represented using n-ary // applications. // We do not need the extra field for INIT1, ..., INIT6. unsigned m_num_args; }; struct compare : public instruction { unsigned m_reg1; unsigned m_reg2; }; struct check : public instruction { unsigned m_reg; enode * m_enode; }; struct filter : public instruction { unsigned m_reg; approx_set m_lbl_set; }; struct pcheck : public instruction { enode * m_enode; approx_set m_lbl_set; }; /** \brief Copy m_enode to register m_oreg */ struct get_enode_instr : public instruction { unsigned m_oreg; enode * m_enode; }; struct choose: public instruction { choose * m_alt; }; /** \brief A depth-2 joint. It is used in CONTINUE instruction. There are 3 forms of joints 1) Variables: (f ... X ...) 2) Ground terms: (f ... t ...) 3) depth 2 joint: (f ... (g ... X ...) ...) Joint2 stores the declaration g, and the position of variable X, and its idx. \remark Z3 has no support for depth 3 joints (f ... (g ... (h ... X ...) ...) ....) */ struct joint2 { func_decl * m_decl; unsigned m_arg_pos; unsigned m_reg; // register that contains the variable joint2(func_decl * f, unsigned pos, unsigned r):m_decl(f), m_arg_pos(pos), m_reg(r) {} }; #define NULL_TAG 0 #define GROUND_TERM_TAG 1 #define VAR_TAG 2 #define NESTED_VAR_TAG 3 struct cont: public instruction { func_decl * m_label; unsigned short m_num_args; unsigned m_oreg; approx_set m_lbl_set; // singleton set containing m_label /* The following field is an array of tagged pointers. Each position contains: 1- null (no joint), NULL_TAG 2- a boxed integer (i.e., register that contains the variable bind) VAR_TAG 3- an enode pointer (ground term) GROUND_TERM_TAG 4- or, a joint2 pointer. NESTED_VAR_TAG The size of the array is m_num_args. */ enode * m_joints[0]; }; struct bind : public instruction { func_decl * m_label; unsigned short m_num_args; unsigned m_ireg; unsigned m_oreg; }; struct get_cgr : public instruction { func_decl * m_label; approx_set m_lbl_set; unsigned short m_num_args; unsigned m_oreg; unsigned m_iregs[0]; }; struct yield : public instruction { quantifier * m_qa; app * m_pat; unsigned short m_num_bindings; unsigned m_bindings[0]; }; struct is_cgr : public instruction { unsigned m_ireg; func_decl * m_label; unsigned short m_num_args; unsigned m_iregs[0]; }; void display_num_args(std::ostream & out, unsigned num_args) { if (num_args <= 6) { out << num_args; } else { out << "N"; } } void display_bind(std::ostream & out, const bind & b) { out << "(BIND"; display_num_args(out, b.m_num_args); out << " " << b.m_label->get_name() << " " << b.m_ireg << " " << b.m_oreg << ")"; } void display_get_cgr(std::ostream & out, const get_cgr & c) { out << "(GET_CGR"; display_num_args(out, c.m_num_args); out << " " << c.m_label->get_name() << " " << c.m_oreg; for (unsigned i = 0; i < c.m_num_args; i++) out << " " << c.m_iregs[i]; out << ")"; } void display_is_cgr(std::ostream & out, const is_cgr & c) { out << "(IS_CGR " << c.m_label->get_name() << " " << c.m_ireg; for (unsigned i = 0; i < c.m_num_args; i++) out << " " << c.m_iregs[i]; out << ")"; } void display_yield(std::ostream & out, const yield & y) { out << "(YIELD"; display_num_args(out, y.m_num_bindings); out << " #" << y.m_qa->get_id(); for (unsigned i = 0; i < y.m_num_bindings; i++) { out << " " << y.m_bindings[i]; } out << ")"; } void display_joints(std::ostream & out, unsigned num_joints, enode * const * joints) { for (unsigned i = 0; i < num_joints; i++) { if (i > 0) out << " "; enode * bare = joints[i]; switch (GET_TAG(bare)) { case NULL_TAG: out << "nil"; break; case GROUND_TERM_TAG: out << "#" << UNTAG(enode*, bare)->get_owner_id(); break; case VAR_TAG: out << UNBOXINT(bare); break; case NESTED_VAR_TAG: out << "(" << UNTAG(joint2*, bare)->m_decl->get_name() << " " << UNTAG(joint2*, bare)->m_arg_pos << " " << UNTAG(joint2*, bare)->m_reg << ")"; break; } } } void display_continue(std::ostream & out, const cont & c) { out << "(CONTINUE " << c.m_label->get_name() << " " << c.m_num_args << " " << c.m_oreg << " " << c.m_lbl_set << " ("; display_joints(out, c.m_num_args, c.m_joints); out << "))"; } void display_filter(std::ostream & out, char const * op, filter const & instr) { out << "(" << op << " " << instr.m_reg << " " << instr.m_lbl_set << ")"; } std::ostream & operator<<(std::ostream & out, const instruction & instr) { switch (instr.m_opcode) { case INIT1: case INIT2: case INIT3: case INIT4: case INIT5: case INIT6: case INITN: out << "(INIT"; if (instr.m_opcode <= INIT6) out << (instr.m_opcode - INIT1 + 1); else out << "N"; out << ")"; break; case BIND1: case BIND2: case BIND3: case BIND4: case BIND5: case BIND6: case BINDN: display_bind(out, static_cast(instr)); break; case GET_CGR1: case GET_CGR2: case GET_CGR3: case GET_CGR4: case GET_CGR5: case GET_CGR6: case GET_CGRN: display_get_cgr(out, static_cast(instr)); break; case IS_CGR: display_is_cgr(out, static_cast(instr)); break; case YIELD1: case YIELD2: case YIELD3: case YIELD4: case YIELD5: case YIELD6: case YIELDN: display_yield(out, static_cast(instr)); break; case CONTINUE: display_continue(out, static_cast(instr)); break; case COMPARE: out << "(COMPARE " << static_cast(instr).m_reg1 << " " << static_cast(instr).m_reg2 << ")"; break; case CHECK: out << "(CHECK " << static_cast(instr).m_reg << " #" << static_cast(instr).m_enode->get_owner_id() << ")"; break; case FILTER: display_filter(out, "FILTER", static_cast(instr)); break; case CFILTER: display_filter(out, "CFILTER", static_cast(instr)); break; case PFILTER: display_filter(out, "PFILTER", static_cast(instr)); break; case GET_ENODE: out << "(GET_ENODE " << static_cast(instr).m_oreg << " #" << static_cast(instr).m_enode->get_owner_id() << ")"; break; case CHOOSE: out << "(CHOOSE)"; break; case NOOP: out << "(NOOP)"; break; } #ifdef _PROFILE_MAM out << "[" << instr.m_counter << "]"; #endif return out; } // ------------------------------------ // // Code Tree // // ------------------------------------ inline enode * get_enode(context & ctx, app * n) { SASSERT(ctx.e_internalized(n)); enode * e = ctx.get_enode(n); SASSERT(e); return e; } inline enode * mk_enode(context & ctx, quantifier * qa, app * n) { ctx.internalize(n, false, ctx.get_generation(qa)); enode * e = ctx.get_enode(n); SASSERT(e); return e; } class code_tree { label_hasher & m_lbl_hasher; func_decl * m_root_lbl; unsigned m_num_args; //!< we need this information to avoid the nary *,+ crash bug bool m_filter_candidates; unsigned m_num_regs; unsigned m_num_choices; instruction * m_root; enode_vector m_candidates; #ifdef Z3DEBUG context * m_context; ptr_vector m_patterns; #endif #ifdef _PROFILE_MAM stopwatch m_watch; unsigned m_counter; #endif friend class compiler; friend class code_tree_manager; void display_seq(std::ostream & out, instruction * head, unsigned indent) const { for (unsigned i = 0; i < indent; i++) { out << " "; } instruction * curr = head; out << *curr; curr = curr->m_next; while (curr != nullptr && curr->m_opcode != CHOOSE && curr->m_opcode != NOOP) { out << "\n"; out << *curr; curr = curr->m_next; } out << "\n"; if (curr != nullptr) { display_children(out, static_cast(curr), indent + 1); } } void display_children(std::ostream & out, choose * first_child, unsigned indent) const { choose * curr = first_child; while (curr != nullptr) { display_seq(out, curr, indent); curr = curr->m_alt; } } #ifdef Z3DEBUG void display_label_hashes_core(std::ostream & out, app * p) const { if (p->is_ground()) { enode * e = get_enode(*m_context, p); SASSERT(e->has_lbl_hash()); out << "#" << e->get_owner_id() << ":" << e->get_lbl_hash() << " "; } else { out << p->get_decl()->get_name() << ":" << m_lbl_hasher(p->get_decl()) << " "; for (unsigned i = 0; i < p->get_num_args(); i++) { expr * arg = p->get_arg(i); if (is_app(arg)) display_label_hashes(out, to_app(arg)); } } } void display_label_hashes(std::ostream & out, app * p) const { ast_manager & m = m_context->get_manager(); if (m.is_pattern(p)) { for (unsigned i = 0; i < p->get_num_args(); i++) { expr * arg = p->get_arg(i); if (is_app(arg)) { display_label_hashes_core(out, to_app(arg)); out << "\n"; } } } else { display_label_hashes_core(out, p); out << "\n"; } } #endif public: code_tree(label_hasher & h, func_decl * lbl, unsigned short num_args, bool filter_candidates): m_lbl_hasher(h), m_root_lbl(lbl), m_num_args(num_args), m_filter_candidates(filter_candidates), m_num_regs(num_args + 1), m_num_choices(0), m_root(nullptr) { DEBUG_CODE(m_context = 0;); #ifdef _PROFILE_MAM m_counter = 0; #endif (void)m_lbl_hasher; } #ifdef _PROFILE_MAM ~code_tree() { #ifdef _PROFILE_MAM_TO_STDOUT std::cout << "killing code tree for: " << m_root_lbl->get_name() << " " << static_cast(m_watch.get_seconds() * 1000) << "\n"; display(std::cout); #endif } stopwatch & get_watch() { return m_watch; } void inc_counter() { m_counter++; } unsigned get_counter() const { return m_counter; } #endif unsigned expected_num_args() const { return m_num_args; } unsigned get_num_regs() const { return m_num_regs; } unsigned get_num_choices() const { return m_num_choices; } func_decl * get_root_lbl() const { return m_root_lbl; } bool filter_candidates() const { return m_filter_candidates; } const instruction * get_root() const { return m_root; } void add_candidate(enode * n) { m_candidates.push_back(n); } bool has_candidates() const { return !m_candidates.empty(); } void reset_candidates() { m_candidates.reset(); } enode_vector const & get_candidates() const { return m_candidates; } #ifdef Z3DEBUG void set_context(context * ctx) { SASSERT(m_context == 0); m_context = ctx; } ptr_vector & get_patterns() { return m_patterns; } #endif void display(std::ostream & out) const { #ifdef Z3DEBUG if (m_context) { ast_manager & m = m_context->get_manager(); out << "patterns:\n"; for (app* a : m_patterns) out << mk_pp(a, m) << "\n"; } #endif out << "function: " << m_root_lbl->get_name(); #ifdef _PROFILE_MAM out << " " << m_watch.get_seconds() << " secs, [" << m_counter << "]"; #endif out << "\n"; out << "num. regs: " << m_num_regs << "\n" << "num. choices: " << m_num_choices << "\n"; display_seq(out, m_root, 0); } }; inline std::ostream & operator<<(std::ostream & out, code_tree const & tree) { tree.display(out); return out; } // ------------------------------------ // // Code Tree Manager // // ------------------------------------ class code_tree_manager { label_hasher & m_lbl_hasher; mam_trail_stack & m_trail_stack; region & m_region; template OP * mk_instr(opcode op, unsigned size) { void * mem = m_region.allocate(size); OP * r = new (mem) OP; r->m_opcode = op; r->m_next = nullptr; #ifdef _PROFILE_MAM r->m_counter = 0; #endif return r; } instruction * mk_init(unsigned n) { SASSERT(n >= 1); opcode op = n <= 6 ? static_cast(INIT1 + n - 1) : INITN; if (op == INITN) { // We store the actual number of arguments for INITN. // Starting at Z3 3.0, some associative operators // (e.g., + and *) are represented using n-ary // applications. initn * r = mk_instr(op, sizeof(initn)); r->m_num_args = n; return r; } else { return mk_instr(op, sizeof(instruction)); } } public: code_tree_manager(label_hasher & h, mam_trail_stack & s): m_lbl_hasher(h), m_trail_stack(s), m_region(s.get_region()) { } code_tree * mk_code_tree(func_decl * lbl, unsigned short num_args, bool filter_candidates) { code_tree * r = alloc(code_tree,m_lbl_hasher, lbl, num_args, filter_candidates); r->m_root = mk_init(num_args); return r; } joint2 * mk_joint2(func_decl * f, unsigned pos, unsigned reg) { return new (m_region) joint2(f, pos, reg); } compare * mk_compare(unsigned reg1, unsigned reg2) { compare * r = mk_instr(COMPARE, sizeof(compare)); r->m_reg1 = reg1; r->m_reg2 = reg2; return r; } check * mk_check(unsigned reg, enode * n) { check * r = mk_instr(CHECK, sizeof(check)); r->m_reg = reg; r->m_enode = n; return r; } filter * mk_filter_core(opcode op, unsigned reg, approx_set s) { filter * r = mk_instr(op, sizeof(filter)); r->m_reg = reg; r->m_lbl_set = s; return r; } filter * mk_filter(unsigned reg, approx_set s) { return mk_filter_core(FILTER, reg, s); } filter * mk_pfilter(unsigned reg, approx_set s) { return mk_filter_core(PFILTER, reg, s); } filter * mk_cfilter(unsigned reg, approx_set s) { return mk_filter_core(CFILTER, reg, s); } get_enode_instr * mk_get_enode(unsigned reg, enode * n) { get_enode_instr * s = mk_instr(GET_ENODE, sizeof(get_enode_instr)); s->m_oreg = reg; s->m_enode = n; return s; } choose * mk_choose(choose * alt) { choose * r = mk_instr(CHOOSE, sizeof(choose)); r->m_alt = alt; return r; } choose * mk_noop() { choose * r = mk_instr(NOOP, sizeof(choose)); r->m_alt = nullptr; return r; } bind * mk_bind(func_decl * lbl, unsigned short num_args, unsigned ireg, unsigned oreg) { SASSERT(num_args >= 1); opcode op = num_args <= 6 ? static_cast(BIND1 + num_args - 1) : BINDN; bind * r = mk_instr(op, sizeof(bind)); r->m_label = lbl; r->m_num_args = num_args; r->m_ireg = ireg; r->m_oreg = oreg; return r; } get_cgr * mk_get_cgr(func_decl * lbl, unsigned oreg, unsigned num_args, unsigned const * iregs) { SASSERT(num_args >= 1); opcode op = num_args <= 6 ? static_cast(GET_CGR1 + num_args - 1) : GET_CGRN; get_cgr * r = mk_instr(op, sizeof(get_cgr) + num_args * sizeof(unsigned)); r->m_label = lbl; r->m_lbl_set.insert(m_lbl_hasher(lbl)); r->m_oreg = oreg; r->m_num_args = num_args; memcpy(r->m_iregs, iregs, sizeof(unsigned) * num_args); return r; } is_cgr * mk_is_cgr(func_decl * lbl, unsigned ireg, unsigned num_args, unsigned const * iregs) { SASSERT(num_args >= 1); is_cgr * r = mk_instr(IS_CGR, sizeof(is_cgr) + num_args * sizeof(unsigned)); r->m_label = lbl; r->m_ireg = ireg; r->m_num_args = num_args; memcpy(r->m_iregs, iregs, sizeof(unsigned) * num_args); return r; } yield * mk_yield(quantifier * qa, app * pat, unsigned num_bindings, unsigned * bindings) { SASSERT(num_bindings >= 1); opcode op = num_bindings <= 6 ? static_cast(YIELD1 + num_bindings - 1) : YIELDN; yield * y = mk_instr(op, sizeof(yield) + num_bindings * sizeof(unsigned)); y->m_qa = qa; y->m_pat = pat; y->m_num_bindings = num_bindings; memcpy(y->m_bindings, bindings, sizeof(unsigned) * num_bindings); return y; } cont * mk_cont(func_decl * lbl, unsigned short num_args, unsigned oreg, approx_set const & s, enode * const * joints) { SASSERT(num_args >= 1); cont * r = mk_instr(CONTINUE, sizeof(cont) + num_args * sizeof(enode*)); r->m_label = lbl; r->m_num_args = num_args; r->m_oreg = oreg; r->m_lbl_set = s; memcpy(r->m_joints, joints, num_args * sizeof(enode *)); return r; } void set_next(instruction * instr, instruction * new_next) { m_trail_stack.push(mam_value_trail(instr->m_next)); instr->m_next = new_next; } void save_num_regs(code_tree * tree) { m_trail_stack.push(mam_value_trail(tree->m_num_regs)); } void save_num_choices(code_tree * tree) { m_trail_stack.push(mam_value_trail(tree->m_num_choices)); } void insert_new_lbl_hash(filter * instr, unsigned h) { m_trail_stack.push(mam_value_trail(instr->m_lbl_set)); instr->m_lbl_set.insert(h); } }; // ------------------------------------ // // Compiler: Pattern ---> Code Tree // // ------------------------------------ class compiler { context & m_context; ast_manager & m; code_tree_manager & m_ct_manager; label_hasher & m_lbl_hasher; bool m_use_filters; ptr_vector m_registers; unsigned_vector m_todo; // list of registers that have patterns to be processed. unsigned_vector m_aux; int_vector m_vars; // -1 the variable is unbound, >= 0 is the register that contains the variable quantifier * m_qa; app * m_mp; code_tree * m_tree; unsigned m_num_choices; bool m_is_tmp_tree; svector m_mp_already_processed; obj_map m_matched_exprs; struct pcheck_checked { func_decl * m_label; enode * m_enode; }; typedef enum { NOT_CHECKED, CHECK_SET, CHECK_SINGLETON } check_mark; svector m_mark; unsigned_vector m_to_reset; ptr_vector m_compatible; ptr_vector m_incompatible; ptr_vector m_seq; void set_register(unsigned reg, expr * p) { m_registers.setx(reg, p, nullptr); } check_mark get_check_mark(unsigned reg) const { return m_mark.get(reg, NOT_CHECKED); } void set_check_mark(unsigned reg, check_mark cm) { m_mark.setx(reg, cm, NOT_CHECKED); } void init(code_tree * t, quantifier * qa, app * mp, unsigned first_idx) { SASSERT(m.is_pattern(mp)); #ifdef Z3DEBUG for (auto cm : m_mark) { SASSERT(cm == NOT_CHECKED); } #endif m_tree = t; m_qa = qa; m_mp = mp; m_num_choices = 0; m_todo.reset(); m_registers.fill(0); app * p = to_app(mp->get_arg(first_idx)); SASSERT(t->get_root_lbl() == p->get_decl()); unsigned num_args = p->get_num_args(); for (unsigned i = 0; i < num_args; i++) { set_register(i+1, p->get_arg(i)); m_todo.push_back(i+1); } unsigned num_decls = m_qa->get_num_decls(); if (num_decls > m_vars.size()) { m_vars.resize(num_decls, -1); } for (unsigned j = 0; j < num_decls; j++) { m_vars[j] = -1; } } /** \brief Return true if all arguments of n are bound variables. That is, during execution time, the variables will be already bound */ bool all_args_are_bound_vars(app * n) { for (expr* arg : *n) { if (!is_var(arg)) return false; if (m_vars[to_var(arg)->get_idx()] == -1) return false; } return true; } /** \see get_stats */ void get_stats_core(app * n, unsigned & sz, unsigned & num_unbound_vars) { sz++; if (n->is_ground()) { return; } for (expr* arg : *n) { if (is_var(arg)) { sz++; unsigned var_id = to_var(arg)->get_idx(); if (m_vars[var_id] == -1) num_unbound_vars++; } else if (is_app(arg)) { get_stats_core(to_app(arg), sz, num_unbound_vars); } } } /** \brief Return statistics for the given pattern \remark Patterns are small. So, it doesn't hurt to use a recursive function. */ void get_stats(app * n, unsigned & sz, unsigned & num_unbound_vars) { sz = 0; num_unbound_vars = 0; get_stats_core(n, sz, num_unbound_vars); } /** \brief Process registers in m_todo. The registers in m_todo that produce non-BIND operations are processed first. Then, a single BIND operation b is produced. After executing this method m_todo will contain the registers in m_todo that produce BIND operations and were not processed, and the registers generated when the operation b was produced. \remark The new operations are appended to m_seq. */ void linearise_core() { m_aux.reset(); app * first_app = nullptr; unsigned first_app_reg; unsigned first_app_sz; unsigned first_app_num_unbound_vars; // generate first the non-BIND operations for (unsigned reg : m_todo) { expr * p = m_registers[reg]; SASSERT(!is_quantifier(p)); TRACE("mam", tout << "lin: " << reg << " " << get_check_mark(reg) << " " << is_var(p) << "\n";); if (is_var(p)) { unsigned var_id = to_var(p)->get_idx(); if (m_vars[var_id] != -1) m_seq.push_back(m_ct_manager.mk_compare(m_vars[var_id], reg)); else m_vars[var_id] = reg; continue; } SASSERT(is_app(p)); if (to_app(p)->is_ground()) { // ground applications are viewed as constants, and eagerly // converted into enodes. enode * e = mk_enode(m_context, m_qa, to_app(p)); m_seq.push_back(m_ct_manager.mk_check(reg, e)); set_check_mark(reg, NOT_CHECKED); // reset mark, register was fully processed. continue; } unsigned matched_reg; if (m_matched_exprs.find(p, matched_reg) && reg != matched_reg) { m_seq.push_back(m_ct_manager.mk_compare(matched_reg, reg)); set_check_mark(reg, NOT_CHECKED); // reset mark, register was fully processed. continue; } m_matched_exprs.insert(p, reg); if (m_use_filters && get_check_mark(reg) != CHECK_SINGLETON) { func_decl * lbl = to_app(p)->get_decl(); approx_set s(m_lbl_hasher(lbl)); m_seq.push_back(m_ct_manager.mk_filter(reg, s)); set_check_mark(reg, CHECK_SINGLETON); } if (first_app) { #if 0 m_aux.push_back(reg); #else // Try to select the best first_app if (first_app_num_unbound_vars == 0) { // first_app doesn't have free vars... so it is the best choice... m_aux.push_back(reg); } else { unsigned sz; unsigned num_unbound_vars; get_stats(to_app(p), sz, num_unbound_vars); if (num_unbound_vars == 0 || sz > first_app_sz || (sz == first_app_sz && num_unbound_vars < first_app_num_unbound_vars)) { // change the first_app m_aux.push_back(first_app_reg); first_app = to_app(p); first_app_reg = reg; first_app_sz = sz; first_app_num_unbound_vars = num_unbound_vars; } else { m_aux.push_back(reg); } } #endif } else { first_app = to_app(p); first_app_reg = reg; get_stats(first_app, first_app_sz, first_app_num_unbound_vars); } } if (first_app) { // m_todo contains at least one (non-ground) application. func_decl * lbl = first_app->get_decl(); unsigned short num_args = first_app->get_num_args(); if (IS_CGR_SUPPORT && all_args_are_bound_vars(first_app)) { // use IS_CGR instead of BIND sbuffer iregs; for (unsigned i = 0; i < num_args; i++) { expr * arg = to_app(first_app)->get_arg(i); SASSERT(is_var(arg)); SASSERT(m_vars[to_var(arg)->get_idx()] != -1); iregs.push_back(m_vars[to_var(arg)->get_idx()]); } m_seq.push_back(m_ct_manager.mk_is_cgr(lbl, first_app_reg, num_args, iregs.c_ptr())); } else { // Generate a BIND operation for this application. unsigned oreg = m_tree->m_num_regs; m_tree->m_num_regs += num_args; for (unsigned j = 0; j < num_args; j++) { set_register(oreg + j, first_app->get_arg(j)); m_aux.push_back(oreg + j); } m_seq.push_back(m_ct_manager.mk_bind(lbl, num_args, first_app_reg, oreg)); m_num_choices++; } set_check_mark(first_app_reg, NOT_CHECKED); // reset mark, register was fully processed. } // make m_aux the new todo list. m_todo.swap(m_aux); } /** \brief Return the number of already bound vars in n. \remark Patterns are small. So, it doesn't hurt to use a recursive function. */ unsigned get_num_bound_vars_core(app * n, bool & has_unbound_vars) { unsigned r = 0; if (n->is_ground()) { return 0; } unsigned num_args = n->get_num_args(); for (unsigned i = 0; i < num_args; i++) { expr * arg = n->get_arg(i); if (is_var(arg)) { unsigned var_id = to_var(arg)->get_idx(); if (m_vars[var_id] != -1) r++; else has_unbound_vars = true; } else if (is_app(arg)) { r += get_num_bound_vars_core(to_app(arg), has_unbound_vars); } } return r; } unsigned get_num_bound_vars(app * n, bool & has_unbound_vars) { has_unbound_vars = false; return get_num_bound_vars_core(n, has_unbound_vars); } /** \brief Compile a pattern where all free variables are already bound. Return the register where the enode congruent to f will be stored. */ unsigned gen_mp_filter(app * n) { if (is_ground(n)) { unsigned oreg = m_tree->m_num_regs; m_tree->m_num_regs += 1; enode * e = mk_enode(m_context, m_qa, n); m_seq.push_back(m_ct_manager.mk_get_enode(oreg, e)); return oreg; } sbuffer iregs; unsigned num_args = n->get_num_args(); for (unsigned i = 0; i < num_args; i++) { expr * arg = n->get_arg(i); if (is_var(arg)) { SASSERT(m_vars[to_var(arg)->get_idx()] != -1); if (m_vars[to_var(arg)->get_idx()] == -1) verbose_stream() << "BUG.....\n"; iregs.push_back(m_vars[to_var(arg)->get_idx()]); } else { iregs.push_back(gen_mp_filter(to_app(arg))); } } unsigned oreg = m_tree->m_num_regs; m_tree->m_num_regs += 1; m_seq.push_back(m_ct_manager.mk_get_cgr(n->get_decl(), oreg, num_args, iregs.c_ptr())); return oreg; } /** \brief Process the rest of a multi-pattern. That is the patterns different from first_idx */ void linearise_multi_pattern(unsigned first_idx) { unsigned num_args = m_mp->get_num_args(); // multi_pattern support for (unsigned i = 1; i < num_args; i++) { // select the pattern with the biggest number of bound variables app * best = nullptr; unsigned best_num_bvars = 0; unsigned best_j = 0; bool found_bounded_mp = false; for (unsigned j = 0; j < m_mp->get_num_args(); j++) { if (m_mp_already_processed[j]) continue; app * p = to_app(m_mp->get_arg(j)); bool has_unbound_vars = false; unsigned num_bvars = get_num_bound_vars(p, has_unbound_vars); if (!has_unbound_vars) { best = p; best_j = j; found_bounded_mp = true; break; } if (best == nullptr || (num_bvars > best_num_bvars)) { best = p; best_num_bvars = num_bvars; best_j = j; } } m_mp_already_processed[best_j] = true; SASSERT(best != 0); app * p = best; func_decl * lbl = p->get_decl(); unsigned short num_args = p->get_num_args(); approx_set s; if (m_use_filters) s.insert(m_lbl_hasher(lbl)); if (found_bounded_mp) { gen_mp_filter(p); } else { // USE CONTINUE unsigned oreg = m_tree->m_num_regs; m_tree->m_num_regs += num_args; ptr_buffer joints; bool has_depth1_joint = false; // VAR_TAG or GROUND_TERM_TAG for (unsigned j = 0; j < num_args; j++) { expr * curr = p->get_arg(j); SASSERT(!is_quantifier(curr)); set_register(oreg + j, curr); m_todo.push_back(oreg + j); if ((is_var(curr) && m_vars[to_var(curr)->get_idx()] >= 0) || (is_app(curr) && (to_app(curr)->is_ground()))) has_depth1_joint = true; } if (has_depth1_joint) { for (unsigned j = 0; j < num_args; j++) { expr * curr = p->get_arg(j); if (is_var(curr)) { unsigned var_id = to_var(curr)->get_idx(); if (m_vars[var_id] >= 0) joints.push_back(BOXTAGINT(enode *, m_vars[var_id], VAR_TAG)); else joints.push_back(NULL_TAG); continue; } SASSERT(is_app(curr)); if (to_app(curr)->is_ground()) { enode * e = mk_enode(m_context, m_qa, to_app(curr)); joints.push_back(TAG(enode *, e, GROUND_TERM_TAG)); continue; } joints.push_back(0); } } else { // Only try to use depth2 joints if there is no depth1 joint. for (unsigned j = 0; j < num_args; j++) { expr * curr = p->get_arg(j); if (!is_app(curr)) { joints.push_back(0); continue; } unsigned num_args2 = to_app(curr)->get_num_args(); unsigned k = 0; for (; k < num_args2; k++) { expr * arg = to_app(curr)->get_arg(k); if (!is_var(arg)) continue; unsigned var_id = to_var(arg)->get_idx(); if (m_vars[var_id] < 0) continue; joint2 * new_joint = m_ct_manager.mk_joint2(to_app(curr)->get_decl(), k, m_vars[var_id]); joints.push_back(TAG(enode *, new_joint, NESTED_VAR_TAG)); break; // found a new joint } if (k == num_args2) joints.push_back(0); // didn't find joint } } SASSERT(joints.size() == num_args); m_seq.push_back(m_ct_manager.mk_cont(lbl, num_args, oreg, s, joints.c_ptr())); m_num_choices++; while (!m_todo.empty()) linearise_core(); } } } /** \brief Produce the operations for the registers in m_todo. */ void linearise(instruction * head, unsigned first_idx) { m_seq.reset(); m_matched_exprs.reset(); while (!m_todo.empty()) linearise_core(); if (m_mp->get_num_args() > 1) { m_mp_already_processed.reset(); m_mp_already_processed.resize(m_mp->get_num_args()); m_mp_already_processed[first_idx] = true; linearise_multi_pattern(first_idx); } #ifdef Z3DEBUG for (unsigned i = 0; i < m_qa->get_num_decls(); i++) { CTRACE("mam_new_bug", m_vars[i] < 0, tout << mk_ismt2_pp(m_qa, m) << "\ni: " << i << " m_vars[i]: " << m_vars[i] << "\n"; tout << "m_mp:\n" << mk_ismt2_pp(m_mp, m) << "\n"; tout << "tree:\n" << *m_tree << "\n"; ); SASSERT(m_vars[i] >= 0); } #endif SASSERT(head->m_next == 0); m_seq.push_back(m_ct_manager.mk_yield(m_qa, m_mp, m_qa->get_num_decls(), reinterpret_cast(m_vars.begin()))); for (instruction * curr : m_seq) { head->m_next = curr; head = curr; } } void set_next(instruction * instr, instruction * new_next) { if (m_is_tmp_tree) instr->m_next = new_next; else m_ct_manager.set_next(instr, new_next); } /* The nodes in the bottom of the code-tree can have a lot of children in big examples. Example: parent-node: (CHOOSE) (CHECK #1 10) (YIELD ...) (CHOOSE) (CHECK #2 10) (YIELD ...) (CHOOSE) (CHECK #3 10) (YIELD ...) (CHOOSE) (CHECK #4 10) (YIELD ...) (CHOOSE) (CHECK #5 10) (YIELD ...) (CHOOSE) (CHECK #6 10) (YIELD ...) (CHOOSE) (CHECK #7 10) (YIELD ...) (CHOOSE) (CHECK #8 10) (YIELD ...) ... The method find_best_child will traverse this big list, and usually will not find a compatible child. So, I limit the number of simple code sequences that can be traversed. */ #define FIND_BEST_CHILD_THRESHOLD 64 choose * find_best_child(choose * first_child) { unsigned num_too_simple = 0; choose * best_child = nullptr; unsigned max_compatibility = 0; choose * curr_child = first_child; while (curr_child != nullptr) { bool simple = false; unsigned curr_compatibility = get_compatibility_measure(curr_child, simple); if (simple) { num_too_simple++; if (num_too_simple > FIND_BEST_CHILD_THRESHOLD) return nullptr; // it is unlikely we will find a compatible node } if (curr_compatibility > max_compatibility) { TRACE("mam", tout << "better child " << best_child << " -> " << curr_child << "\n";); best_child = curr_child; max_compatibility = curr_compatibility; } curr_child = curr_child->m_alt; } return best_child; } bool is_compatible(bind * instr) const { unsigned ireg = instr->m_ireg; expr * n = m_registers[ireg]; return n != nullptr && is_app(n) && // It is wasteful to use a bind of a ground term. // Actually, in the rest of the code I assume that. !is_ground(n) && to_app(n)->get_decl() == instr->m_label && to_app(n)->get_num_args() == instr->m_num_args; } bool is_compatible(compare * instr) const { unsigned reg1 = instr->m_reg1; unsigned reg2 = instr->m_reg2; return m_registers[reg1] != 0 && m_registers[reg1] == m_registers[reg2]; } bool is_compatible(check * instr) const { unsigned reg = instr->m_reg; enode * n = instr->m_enode; if (m_registers[reg] == 0) return false; if (!is_app(m_registers[reg])) return false; if (!to_app(m_registers[reg])->is_ground()) return false; enode * n_prime = mk_enode(m_context, m_qa, to_app(m_registers[reg])); // it is safe to compare the roots because the modifications // on the code tree are chronological. return n->get_root() == n_prime->get_root(); } /** \brief Get the label hash of the pattern stored at register reg. If the pattern is a ground application, then it is viewed as a constant. In this case, we use the field get_lbl_hash() in the enode associated with it. */ unsigned get_pat_lbl_hash(unsigned reg) const { SASSERT(m_registers[reg] != 0); SASSERT(is_app(m_registers[reg])); app * p = to_app(m_registers[reg]); if (p->is_ground()) { enode * e = mk_enode(m_context, m_qa, p); if (!e->has_lbl_hash()) e->set_lbl_hash(m_context); return e->get_lbl_hash(); } else { func_decl * lbl = p->get_decl(); return m_lbl_hasher(lbl); } } /** \brief We say a check operation is semi compatible if it access a register that was not yet processed, and given reg = instr->m_reg 1- is_ground(m_registers[reg]) 2- get_pat_lbl_hash(reg) == m_enode->get_lbl_hash() If that is the case, then a CFILTER is created */ bool is_semi_compatible(check * instr) const { unsigned reg = instr->m_reg; return m_registers[reg] != 0 && // if the register was already checked by another filter, then it doesn't make sense // to check it again. get_check_mark(reg) == NOT_CHECKED && is_ground(m_registers[reg]) && get_pat_lbl_hash(reg) == instr->m_enode->get_lbl_hash(); } /** \brief FILTER is not compatible with ground terms anymore. See CFILTER is the filter used for ground terms. */ bool is_compatible(filter * instr) const { unsigned reg = instr->m_reg; if (m_registers[reg] != 0 && is_app(m_registers[reg]) && !is_ground(m_registers[reg])) { // FILTER is fully compatible if it already contains // the label hash of the pattern stored at reg. unsigned elem = get_pat_lbl_hash(reg); return instr->m_lbl_set.may_contain(elem); } return false; } bool is_cfilter_compatible(filter * instr) const { unsigned reg = instr->m_reg; // only ground terms are considered in CFILTERS if (m_registers[reg] != 0 && is_ground(m_registers[reg])) { // FILTER is fully compatible if it already contains // the label hash of the pattern stored at reg. unsigned elem = get_pat_lbl_hash(reg); return instr->m_lbl_set.may_contain(elem); } return false; } /** \brief See comments at is_semi_compatible(check * instr) and is_compatible(filter * instr). Remark: FILTER is not compatible with ground terms anymore */ bool is_semi_compatible(filter * instr) const { unsigned reg = instr->m_reg; return m_registers[reg] != nullptr && get_check_mark(reg) == NOT_CHECKED && is_app(m_registers[reg]) && !is_ground(m_registers[reg]); } bool is_compatible(cont * instr) const { unsigned oreg = instr->m_oreg; for (unsigned i = 0; i < instr->m_num_args; i++) if (m_registers[oreg + i] != 0) return false; return true; } // Threshold for a code sequence (in number of instructions) to be considered simple. #define SIMPLE_SEQ_THRESHOLD 4 /** \brief Return a "compatibility measure" that quantifies how many operations in the branch starting at child are compatible with the patterns in the registers stored in m_todo. Set simple to true, if the tree starting at child is too simple (no branching and less than SIMPLE_SEQ_THRESHOLD instructions) */ unsigned get_compatibility_measure(choose * child, bool & simple) { simple = true; m_to_reset.reset(); unsigned weight = 0; unsigned num_instr = 0; instruction * curr = child->m_next; while (curr != nullptr && curr->m_opcode != CHOOSE && curr->m_opcode != NOOP) { num_instr++; switch (curr->m_opcode) { case BIND1: case BIND2: case BIND3: case BIND4: case BIND5: case BIND6: case BINDN: if (is_compatible(static_cast(curr))) { weight += 4; // the weight of BIND is bigger than COMPARE and CHECK unsigned ireg = static_cast(curr)->m_ireg; app * n = to_app(m_registers[ireg]); unsigned oreg = static_cast(curr)->m_oreg; unsigned num_args = static_cast(curr)->m_num_args; SASSERT(n->get_num_args() == num_args); for (unsigned i = 0; i < num_args; i++) { set_register(oreg + i, n->get_arg(i)); m_to_reset.push_back(oreg + i); } } break; case COMPARE: if (is_compatible(static_cast(curr))) weight += 2; break; case CHECK: if (is_compatible(static_cast(curr))) weight += 2; else if (m_use_filters && is_semi_compatible(static_cast(curr))) weight += 1; break; case CFILTER: if (is_cfilter_compatible(static_cast(curr))) weight += 2; break; case FILTER: if (is_compatible(static_cast(curr))) weight += 2; else if (is_semi_compatible(static_cast(curr))) weight += 1; break; // TODO: Try to reuse IS_CGR instruction default: break; } curr = curr->m_next; } if (num_instr > SIMPLE_SEQ_THRESHOLD || (curr != nullptr && curr->m_opcode == CHOOSE)) simple = false; for (unsigned r : m_to_reset) m_registers[r] = 0; return weight; } void insert(instruction * head, unsigned first_mp_idx) { for (;;) { m_compatible.reset(); m_incompatible.reset(); TRACE("mam_compiler_detail", tout << "processing head: " << *head << "\n";); instruction * curr = head->m_next; instruction * last = head; while (curr != nullptr && curr->m_opcode != CHOOSE && curr->m_opcode != NOOP) { TRACE("mam_compiler_detail", tout << "processing instr: " << *curr << "\n";); switch (curr->m_opcode) { case BIND1: case BIND2: case BIND3: case BIND4: case BIND5: case BIND6: case BINDN: { bind* bnd = static_cast(curr); if (is_compatible(bnd)) { TRACE("mam_compiler_detail", tout << "compatible\n";); unsigned ireg = bnd->m_ireg; SASSERT(m_todo.contains(ireg)); m_todo.erase(ireg); set_check_mark(ireg, NOT_CHECKED); m_compatible.push_back(curr); app * app = to_app(m_registers[ireg]); unsigned oreg = bnd->m_oreg; unsigned num_args = bnd->m_num_args; for (unsigned i = 0; i < num_args; i++) { set_register(oreg + i, app->get_arg(i)); m_todo.push_back(oreg + i); } } else { TRACE("mam_compiler_detail", tout << "incompatible\n";); m_incompatible.push_back(curr); } break; } case CHECK: { check* chk = static_cast(curr); if (is_compatible(chk)) { TRACE("mam_compiler_detail", tout << "compatible\n";); unsigned reg = chk->m_reg; SASSERT(m_todo.contains(reg)); m_todo.erase(reg); set_check_mark(reg, NOT_CHECKED); m_compatible.push_back(curr); } else if (m_use_filters && is_semi_compatible(chk)) { TRACE("mam_compiler_detail", tout << "semi compatible\n";); unsigned reg = chk->m_reg; enode * n1 = chk->m_enode; // n1->has_lbl_hash may be false, even // when update_filters is invoked before // executing this method. // // Reason: n1 is a ground subterm of a ground term T. // I incorrectly assumed n1->has_lbl_hash() was true because // update_filters executes set_lbl_hash for all maximal ground terms. // And, I also incorrectly assumed that all arguments of check were // maximal ground terms. This is not true. For example, assume // the code_tree already has the pattern // (f (g x) z) // So, when the pattern (f (g b) x) is compiled a check instruction // is created for a ground subterm b of the maximal ground term (g b). if (!n1->has_lbl_hash()) n1->set_lbl_hash(m_context); unsigned h1 = n1->get_lbl_hash(); unsigned h2 = get_pat_lbl_hash(reg); approx_set s(h1); s.insert(h2); filter * new_instr = m_ct_manager.mk_cfilter(reg, s); set_check_mark(reg, CHECK_SET); m_compatible.push_back(new_instr); m_incompatible.push_back(curr); } else { TRACE("mam_compiler_detail", tout << "incompatible " << chk->m_reg << "\n";); m_incompatible.push_back(curr); } break; } case COMPARE: if (is_compatible(static_cast(curr))) { TRACE("mam_compiler_detail", tout << "compatible\n";); unsigned reg1 = static_cast(curr)->m_reg1; unsigned reg2 = static_cast(curr)->m_reg2; SASSERT(m_todo.contains(reg2)); m_todo.erase(reg2); set_check_mark(reg2, NOT_CHECKED); if (is_var(m_registers[reg1])) { m_todo.erase(reg1); set_check_mark(reg1, NOT_CHECKED); unsigned var_id = to_var(m_registers[reg1])->get_idx(); if (m_vars[var_id] == -1) m_vars[var_id] = reg1; } m_compatible.push_back(curr); } else { TRACE("mam_compiler_detail", tout << "incompatible\n";); m_incompatible.push_back(curr); } break; case CFILTER: SASSERT(m_use_filters); if (is_cfilter_compatible(static_cast(curr))) { unsigned reg = static_cast(curr)->m_reg; SASSERT(static_cast(curr)->m_lbl_set.size() == 1); set_check_mark(reg, CHECK_SINGLETON); m_compatible.push_back(curr); } else { m_incompatible.push_back(curr); } break; case FILTER: { filter *flt = static_cast(curr); SASSERT(m_use_filters); if (is_compatible(flt)) { unsigned reg = flt->m_reg; TRACE("mam_compiler_detail", tout << "compatible " << reg << "\n";); CTRACE("mam_compiler_bug", !m_todo.contains(reg), { for (unsigned t : m_todo) { tout << t << " "; } tout << "\nregisters:\n"; unsigned i = 0; for (expr* r : m_registers) { if (r) { tout << i++ << ":\n" << mk_pp(r, m) << "\n"; } } tout << "quantifier:\n" << mk_pp(m_qa, m) << "\n"; tout << "pattern:\n" << mk_pp(m_mp, m) << "\n"; }); SASSERT(m_todo.contains(reg)); if (flt->m_lbl_set.size() == 1) set_check_mark(reg, CHECK_SINGLETON); else set_check_mark(reg, CHECK_SET); m_compatible.push_back(curr); } else if (is_semi_compatible(flt)) { unsigned reg = flt->m_reg; TRACE("mam_compiler_detail", tout << "semi compatible " << reg << "\n";); CTRACE("mam_compiler_bug", !m_todo.contains(reg), { for (unsigned t : m_todo) { tout << t << " "; } tout << "\nregisters:\n"; unsigned i = 0; for (expr* r : m_registers) { if (r) { tout << i++ << ":\n" << mk_pp(r, m) << "\n"; } } tout << "quantifier:\n" << mk_pp(m_qa, m) << "\n"; tout << "pattern:\n" << mk_pp(m_mp, m) << "\n"; }); SASSERT(m_todo.contains(reg)); unsigned h = get_pat_lbl_hash(reg); TRACE("mam_lbl_bug", tout << "curr_set: " << flt->m_lbl_set << "\n"; tout << "new hash: " << h << "\n";); set_check_mark(reg, CHECK_SET); approx_set const & s = flt->m_lbl_set; if (s.size() > 1) { m_ct_manager.insert_new_lbl_hash(flt, h); m_compatible.push_back(curr); } else { SASSERT(s.size() == 1); approx_set new_s(s); new_s.insert(h); filter * new_instr = m_ct_manager.mk_filter(reg, new_s); m_compatible.push_back(new_instr); m_incompatible.push_back(curr); } } else { TRACE("mam_compiler_detail", tout << "incompatible\n";); m_incompatible.push_back(curr); } break; } default: TRACE("mam_compiler_detail", tout << "incompatible\n";); m_incompatible.push_back(curr); break; } last = curr; curr = curr->m_next; } TRACE("mam_compiler", tout << *head << " " << head << "\n"; tout << "m_compatible.size(): " << m_compatible.size() << "\n"; tout << "m_incompatible.size(): " << m_incompatible.size() << "\n";); if (m_incompatible.empty()) { // sequence starting at head is fully compatible SASSERT(curr != 0); SASSERT(curr->m_opcode == CHOOSE); choose * first_child = static_cast(curr); choose * best_child = find_best_child(first_child); TRACE("mam", tout << "best child " << best_child << "\n";); if (best_child == nullptr) { // There is no compatible child // Suppose the sequence is: // head -> c1 -> ... -> (cn == last) -> first_child; // Then we should add // head -> c1 -> ... -> (cn == last) -> new_child // new_child: CHOOSE(first_child) -> linearise choose * new_child = m_ct_manager.mk_choose(first_child); m_num_choices++; set_next(last, new_child); linearise(new_child, first_mp_idx); // DONE return; } else { head = best_child; // CONTINUE from best_child } } else { SASSERT(head->is_init() || !m_compatible.empty()); SASSERT(!m_incompatible.empty()); // Suppose the sequence is: // head -> c1 -> i1 -> c2 -> c3 -> i2 -> first_child_head // where c_j are the compatible instructions, and i_j are the incompatible ones // Then the sequence starting at head should become // head -> c1 -> c2 -> c3 -> new_child_head1 // new_child_head1:CHOOSE(new_child_head2) -> i1 -> i2 -> first_child_head // new_child_head2:NOOP -> linearise() instruction * first_child_head = curr; choose * new_child_head2 = m_ct_manager.mk_noop(); SASSERT(new_child_head2->m_alt == 0); choose * new_child_head1 = m_ct_manager.mk_choose(new_child_head2); m_num_choices++; // set: head -> c1 -> c2 -> c3 -> new_child_head1 curr = head; for (instruction* instr : m_compatible) { set_next(curr, instr); curr = instr; } set_next(curr, new_child_head1); // set: new_child_head1:CHOOSE(new_child_head2) -> i1 -> i2 -> first_child_head curr = new_child_head1; for (instruction* inc : m_incompatible) { if (curr == new_child_head1) curr->m_next = inc; // new_child_head1 is a new node, I don't need to save trail else set_next(curr, inc); curr = inc; } set_next(curr, first_child_head); // build new_child_head2:NOOP -> linearise() linearise(new_child_head2, first_mp_idx); // DONE return; } } } public: compiler(context & ctx, code_tree_manager & ct_mg, label_hasher & h, bool use_filters = true): m_context(ctx), m(ctx.get_manager()), m_ct_manager(ct_mg), m_lbl_hasher(h), m_use_filters(use_filters) { } /** \brief Create a new code tree for the given quantifier. - mp: is a pattern of qa that will be used to create the code tree - first_idx: index of mp that will be the "head" (first to be processed) of the multi-pattern. */ code_tree * mk_tree(quantifier * qa, app * mp, unsigned first_idx, bool filter_candidates) { SASSERT(m.is_pattern(mp)); app * p = to_app(mp->get_arg(first_idx)); unsigned num_args = p->get_num_args(); code_tree * r = m_ct_manager.mk_code_tree(p->get_decl(), num_args, filter_candidates); init(r, qa, mp, first_idx); linearise(r->m_root, first_idx); r->m_num_choices = m_num_choices; TRACE("mam_compiler", tout << "new tree for:\n" << mk_pp(mp, m) << "\n" << *r;); return r; } /** \brief Insert a pattern into the code tree. - is_tmp_tree: trail for update operations is created if is_tmp_tree = false. */ void insert(code_tree * tree, quantifier * qa, app * mp, unsigned first_idx, bool is_tmp_tree) { if (tree->expected_num_args() != to_app(mp->get_arg(first_idx))->get_num_args()) { // We have to check the number of arguments because of nary + and * operators. // The E-matching engine that was built when all + and * applications were binary. // We ignore the pattern if it does not have the expected number of arguments. // This is not the ideal solution, but it avoids possible crashes. return; } m_is_tmp_tree = is_tmp_tree; TRACE("mam_compiler", tout << "updating tree with:\n" << mk_pp(mp, m) << "\n";); TRACE("mam_bug", tout << "before insertion\n" << *tree << "\n";); if (!is_tmp_tree) m_ct_manager.save_num_regs(tree); init(tree, qa, mp, first_idx); m_num_choices = tree->m_num_choices; insert(tree->m_root, first_idx); TRACE("mam_bug", tout << "m_num_choices: " << m_num_choices << "\n";); if (m_num_choices > tree->m_num_choices) { if (!is_tmp_tree) m_ct_manager.save_num_choices(tree); tree->m_num_choices = m_num_choices; } TRACE("mam_bug", tout << "m_num_choices: " << m_num_choices << "\n"; tout << "new tree:\n" << *tree; tout << "todo "; for (auto t : m_todo) tout << t << " "; tout << "\n";); } }; #ifdef Z3DEBUG bool check_lbls(enode * n) { approx_set lbls; approx_set plbls; enode * first = n; do { lbls |= n->get_lbls(); plbls |= n->get_plbls(); n = n->get_next(); } while (first != n); SASSERT(n->get_root()->get_lbls() == lbls); SASSERT(n->get_root()->get_plbls() == plbls); return true; } #endif // ------------------------------------ // // Code Tree Interpreter // // ------------------------------------ struct backtrack_point { const instruction * m_instr; unsigned m_old_max_generation; unsigned m_old_used_enodes_size; union { enode * m_curr; struct { enode_vector * m_to_recycle; enode * const * m_it; enode * const * m_end; }; }; }; typedef svector backtrack_stack; class interpreter { context & m_context; ast_manager & m; mam & m_mam; bool m_use_filters; enode_vector m_registers; enode_vector m_bindings; enode_vector m_args; backtrack_stack m_backtrack_stack; unsigned m_top; const instruction * m_pc; // auxiliary temporary variables unsigned m_max_generation; // the maximum generation of an app enode processed. unsigned m_curr_max_generation; // temporary var used to store a copy of m_max_generation unsigned m_num_args; unsigned m_oreg; enode * m_n1; enode * m_n2; enode * m_app; const bind * m_b; // equalities used for pattern match. The first element of the tuple gives the argument (or null) of some term that was matched against some higher level // structure of the trigger, the second element gives the term that argument is replaced with in order to match the trigger. Used for logging purposes only. vector> m_used_enodes; unsigned m_curr_used_enodes_size; ptr_vector m_pattern_instances; // collect the pattern instances... used for computing min_top_generation and max_top_generation unsigned_vector m_min_top_generation, m_max_top_generation; pool m_pool; enode_vector * mk_enode_vector() { enode_vector * r = m_pool.mk(); r->reset(); return r; } void recycle_enode_vector(enode_vector * v) { m_pool.recycle(v); } void update_max_generation(enode * n, enode * prev) { m_max_generation = std::max(m_max_generation, n->get_generation()); if (m.has_trace_stream()) m_used_enodes.push_back(std::make_tuple(prev, n)); } // We have to provide the number of expected arguments because we have flat-assoc applications such as +. // Flat-assoc applications may have arbitrary number of arguments. enode * get_first_f_app(func_decl * lbl, unsigned num_expected_args, enode * curr) { enode * first = curr; do { if (curr->get_decl() == lbl && curr->is_cgr() && curr->get_num_args() == num_expected_args) { update_max_generation(curr, first); return curr; } curr = curr->get_next(); } while (curr != first); return nullptr; } enode * get_next_f_app(func_decl * lbl, unsigned num_expected_args, enode * first, enode * curr) { curr = curr->get_next(); while (curr != first) { if (curr->get_decl() == lbl && curr->is_cgr() && curr->get_num_args() == num_expected_args) { update_max_generation(curr, first); return curr; } curr = curr->get_next(); } return nullptr; } /** \brief Execute the is_cgr instruction. Return true if succeeded, and false if backtracking is needed. */ bool exec_is_cgr(is_cgr const * pc) { unsigned num_args = pc->m_num_args; enode * n = m_registers[pc->m_ireg]; func_decl * f = pc->m_label; enode * first = n; switch (num_args) { case 1: m_args[0] = m_registers[pc->m_iregs[0]]->get_root(); SASSERT(n != 0); do { if (n->get_decl() == f && n->get_arg(0)->get_root() == m_args[0]) { update_max_generation(n, first); return true; } n = n->get_next(); } while (n != first); return false; case 2: m_args[0] = m_registers[pc->m_iregs[0]]->get_root(); m_args[1] = m_registers[pc->m_iregs[1]]->get_root(); SASSERT(n != 0); do { if (n->get_decl() == f && n->get_arg(0)->get_root() == m_args[0] && n->get_arg(1)->get_root() == m_args[1]) { update_max_generation(n, first); return true; } n = n->get_next(); } while (n != first); return false; default: { m_args.reserve(num_args+1, 0); for (unsigned i = 0; i < num_args; i++) m_args[i] = m_registers[pc->m_iregs[i]]->get_root(); SASSERT(n != 0); do { if (n->get_decl() == f) { unsigned i = 0; for (; i < num_args; i++) { if (n->get_arg(i)->get_root() != m_args[i]) break; } if (i == num_args) { update_max_generation(n, first); return true; } } n = n->get_next(); } while (n != first); return false; } } } enode_vector * mk_depth1_vector(enode * n, func_decl * f, unsigned i); enode_vector * mk_depth2_vector(joint2 * j2, func_decl * f, unsigned i); enode * init_continue(cont const * c, unsigned expected_num_args); void display_reg(std::ostream & out, unsigned reg); void display_instr_input_reg(std::ostream & out, instruction const * instr); void display_pc_info(std::ostream & out); #define INIT_ARGS_SIZE 16 public: interpreter(context & ctx, mam & ma, bool use_filters): m_context(ctx), m(ctx.get_manager()), m_mam(ma), m_use_filters(use_filters) { m_args.resize(INIT_ARGS_SIZE); } ~interpreter() { } void init(code_tree * t) { TRACE("mam_bug", tout << "preparing to match tree:\n" << *t << "\n";); m_registers.reserve(t->get_num_regs(), nullptr); m_bindings.reserve(t->get_num_regs(), nullptr); if (m_backtrack_stack.size() < t->get_num_choices()) m_backtrack_stack.resize(t->get_num_choices()); } void execute(code_tree * t) { TRACE("trigger_bug", tout << "execute for code tree:\n"; t->display(tout);); init(t); if (t->filter_candidates()) { for (enode* app : t->get_candidates()) { TRACE("trigger_bug", tout << "candidate\n" << mk_ismt2_pp(app->get_owner(), m) << "\n";); if (!app->is_marked() && app->is_cgr()) { if (m_context.resource_limits_exceeded() || !execute_core(t, app)) return; app->set_mark(); } } for (enode* app : t->get_candidates()) { if (app->is_marked()) app->unset_mark(); } } else { for (enode* app : t->get_candidates()) { TRACE("trigger_bug", tout << "candidate\n" << mk_ismt2_pp(app->get_owner(), m) << "\n";); if (app->is_cgr()) { TRACE("trigger_bug", tout << "is_cgr\n";); if (m_context.resource_limits_exceeded() || !execute_core(t, app)) return; } } } } // init(t) must be invoked before execute_core bool execute_core(code_tree * t, enode * n); // Return the min, max generation of the enodes in m_pattern_instances. void get_min_max_top_generation(unsigned& min, unsigned& max) { SASSERT(!m_pattern_instances.empty()); if (m_min_top_generation.empty()) { min = max = m_pattern_instances[0]->get_generation(); m_min_top_generation.push_back(min); m_max_top_generation.push_back(max); } else { min = m_min_top_generation.back(); max = m_max_top_generation.back(); } for (unsigned i = m_min_top_generation.size(); i < m_pattern_instances.size(); ++i) { unsigned curr = m_pattern_instances[i]->get_generation(); min = std::min(min, curr); m_min_top_generation.push_back(min); max = std::max(max, curr); m_max_top_generation.push_back(max); } } }; /** \brief Return a vector with the relevant f-parents of n such that n is the i-th argument. */ enode_vector * interpreter::mk_depth1_vector(enode * n, func_decl * f, unsigned i) { enode_vector * v = mk_enode_vector(); n = n->get_root(); enode_vector::const_iterator it = n->begin_parents(); enode_vector::const_iterator end = n->end_parents(); for (; it != end; ++it) { enode * p = *it; if (p->get_decl() == f && i < p->get_num_args() && m_context.is_relevant(p) && p->is_cgr() && p->get_arg(i)->get_root() == n) { v->push_back(p); } } return v; } /** \brief Return a vector with the relevant f-parents of each p in joint2 where n is the i-th argument. We say a p is in joint2 if p is the g-parent of m_registers[j2->m_reg] where g is j2->m_decl, and m_registers[j2->m_reg] is the argument j2->m_arg_pos. */ enode_vector * interpreter::mk_depth2_vector(joint2 * j2, func_decl * f, unsigned i) { enode * n = m_registers[j2->m_reg]->get_root(); if (n->get_num_parents() == 0) return nullptr; unsigned num_args = n->get_num_args(); enode_vector * v = mk_enode_vector(); enode_vector::const_iterator it1 = n->begin_parents(); enode_vector::const_iterator end1 = n->end_parents(); for (; it1 != end1; ++it1) { enode * p = *it1; if (p->get_decl() == j2->m_decl && m_context.is_relevant(p) && p->is_cgr() && p->get_arg(j2->m_arg_pos)->get_root() == n) { // p is in joint2 p = p->get_root(); enode_vector::const_iterator it2 = p->begin_parents(); enode_vector::const_iterator end2 = p->end_parents(); for (; it2 != end2; ++it2) { enode * p2 = *it2; if (p2->get_decl() == f && num_args == n->get_num_args() && m_context.is_relevant(p2) && p2->is_cgr() && p2->get_arg(i)->get_root() == p) { v->push_back(p2); } } } } return v; } enode * interpreter::init_continue(cont const * c, unsigned expected_num_args) { func_decl * lbl = c->m_label; unsigned min_sz = m_context.get_num_enodes_of(lbl); unsigned short num_args = c->m_num_args; enode * r; // quick filter... check if any of the joint points have zero parents... for (unsigned i = 0; i < num_args; i++) { void * bare = c->m_joints[i]; enode * n = nullptr; switch (GET_TAG(bare)) { case NULL_TAG: goto non_depth1; case GROUND_TERM_TAG: n = UNTAG(enode *, bare); break; case VAR_TAG: n = m_registers[UNBOXINT(bare)]; break; case NESTED_VAR_TAG: goto non_depth1; } r = n->get_root(); if (m_use_filters && r->get_plbls().empty_intersection(c->m_lbl_set)) return nullptr; if (r->get_num_parents() == 0) return nullptr; non_depth1: ; } // traverse each joint and select the best one. enode_vector * best_v = nullptr; for (unsigned i = 0; i < num_args; i++) { enode * bare = c->m_joints[i]; enode_vector * curr_v = nullptr; switch (GET_TAG(bare)) { case NULL_TAG: curr_v = nullptr; break; case GROUND_TERM_TAG: curr_v = mk_depth1_vector(UNTAG(enode *, bare), lbl, i); break; case VAR_TAG: curr_v = mk_depth1_vector(m_registers[UNBOXINT(bare)], lbl, i); break; case NESTED_VAR_TAG: curr_v = mk_depth2_vector(UNTAG(joint2 *, bare), lbl, i); break; } if (curr_v != nullptr) { if (curr_v->size() < min_sz && (best_v == nullptr || curr_v->size() < best_v->size())) { if (best_v) recycle_enode_vector(best_v); best_v = curr_v; if (best_v->empty()) { recycle_enode_vector(best_v); return nullptr; } } else { recycle_enode_vector(curr_v); } } } backtrack_point & bp = m_backtrack_stack[m_top]; bp.m_instr = c; bp.m_old_max_generation = m_max_generation; bp.m_old_used_enodes_size = m_used_enodes.size(); if (best_v == nullptr) { TRACE("mam_bug", tout << "m_top: " << m_top << ", m_backtrack_stack.size(): " << m_backtrack_stack.size() << "\n"; tout << *c << "\n";); bp.m_to_recycle = nullptr; bp.m_it = m_context.begin_enodes_of(lbl); bp.m_end = m_context.end_enodes_of(lbl); } else { SASSERT(!best_v->empty()); bp.m_to_recycle = best_v; bp.m_it = best_v->begin(); bp.m_end = best_v->end(); } // find application with the right number of arguments for (; bp.m_it != bp.m_end; ++bp.m_it) { enode * curr = *bp.m_it; if (curr->get_num_args() == expected_num_args && m_context.is_relevant(curr)) break; } if (bp.m_it == bp.m_end) return nullptr; m_top++; update_max_generation(*(bp.m_it), nullptr); return *(bp.m_it); } void interpreter::display_reg(std::ostream & out, unsigned reg) { out << "reg[" << reg << "]: "; enode * n = m_registers[reg]; if (!n) { out << "nil\n"; } else { out << "#" << n->get_owner_id() << ", root: " << n->get_root()->get_owner_id(); if (m_use_filters) out << ", lbls: " << n->get_root()->get_lbls() << " "; out << "\n"; out << mk_pp(n->get_owner(), m) << "\n"; } } void interpreter::display_instr_input_reg(std::ostream & out, const instruction * instr) { switch (instr->m_opcode) { case INIT1: case INIT2: case INIT3: case INIT4: case INIT5: case INIT6: case INITN: display_reg(out, 0); break; case BIND1: case BIND2: case BIND3: case BIND4: case BIND5: case BIND6: case BINDN: display_reg(out, static_cast(instr)->m_ireg); break; case COMPARE: display_reg(out, static_cast(instr)->m_reg1); display_reg(out, static_cast(instr)->m_reg2); break; case CHECK: display_reg(out, static_cast(instr)->m_reg); break; case FILTER: display_reg(out, static_cast(instr)->m_reg); break; case YIELD1: case YIELD2: case YIELD3: case YIELD4: case YIELD5: case YIELD6: case YIELDN: for (unsigned i = 0; i < static_cast(instr)->m_num_bindings; i++) { display_reg(out, static_cast(instr)->m_bindings[i]); } break; default: break; } } void interpreter::display_pc_info(std::ostream & out) { out << "executing: " << *m_pc << "\n"; out << "m_pc: " << m_pc << ", next: " << m_pc->m_next; if (m_pc->m_opcode == CHOOSE) out << ", alt: " << static_cast(m_pc)->m_alt; out << "\n"; display_instr_input_reg(out, m_pc); } bool interpreter::execute_core(code_tree * t, enode * n) { TRACE("trigger_bug", tout << "interpreter::execute_core\n"; t->display(tout); tout << "\nenode\n" << mk_ismt2_pp(n->get_owner(), m) << "\n";); unsigned since_last_check = 0; #ifdef _PROFILE_MAM #ifdef _PROFILE_MAM_EXPENSIVE if (t->get_watch().get_seconds() > _PROFILE_MAM_THRESHOLD && t->get_counter() % _PROFILE_MAM_EXPENSIVE_FREQ == 0) { std::cout << "EXPENSIVE\n"; t->display(std::cout); } #endif t->get_watch().start(); t->inc_counter(); #endif // It doesn't make sense to process an irrelevant enode. TRACE("mam_execute_core", tout << "EXEC " << t->get_root_lbl()->get_name() << "\n";); SASSERT(m_context.is_relevant(n)); m_pattern_instances.reset(); m_min_top_generation.reset(); m_max_top_generation.reset(); m_pattern_instances.push_back(n); m_max_generation = n->get_generation(); if (m.has_trace_stream()) { m_used_enodes.reset(); m_used_enodes.push_back(std::make_tuple(nullptr, n)); // null indicates that n was matched against the trigger at the top-level } m_pc = t->get_root(); m_registers[0] = n; m_top = 0; main_loop: TRACE("mam_int", display_pc_info(tout);); #ifdef _PROFILE_MAM const_cast(m_pc)->m_counter++; #endif switch (m_pc->m_opcode) { case INIT1: m_app = m_registers[0]; if (m_app->get_num_args() != 1) goto backtrack; m_registers[1] = m_app->get_arg(0); m_pc = m_pc->m_next; goto main_loop; case INIT2: m_app = m_registers[0]; if (m_app->get_num_args() != 2) goto backtrack; m_registers[1] = m_app->get_arg(0); m_registers[2] = m_app->get_arg(1); m_pc = m_pc->m_next; goto main_loop; case INIT3: m_app = m_registers[0]; if (m_app->get_num_args() != 3) goto backtrack; m_registers[1] = m_app->get_arg(0); m_registers[2] = m_app->get_arg(1); m_registers[3] = m_app->get_arg(2); m_pc = m_pc->m_next; goto main_loop; case INIT4: m_app = m_registers[0]; if (m_app->get_num_args() != 4) goto backtrack; m_registers[1] = m_app->get_arg(0); m_registers[2] = m_app->get_arg(1); m_registers[3] = m_app->get_arg(2); m_registers[4] = m_app->get_arg(3); m_pc = m_pc->m_next; goto main_loop; case INIT5: m_app = m_registers[0]; if (m_app->get_num_args() != 5) goto backtrack; m_registers[1] = m_app->get_arg(0); m_registers[2] = m_app->get_arg(1); m_registers[3] = m_app->get_arg(2); m_registers[4] = m_app->get_arg(3); m_registers[5] = m_app->get_arg(4); m_pc = m_pc->m_next; goto main_loop; case INIT6: m_app = m_registers[0]; if (m_app->get_num_args() != 6) goto backtrack; m_registers[1] = m_app->get_arg(0); m_registers[2] = m_app->get_arg(1); m_registers[3] = m_app->get_arg(2); m_registers[4] = m_app->get_arg(3); m_registers[5] = m_app->get_arg(4); m_registers[6] = m_app->get_arg(5); m_pc = m_pc->m_next; goto main_loop; case INITN: m_app = m_registers[0]; m_num_args = m_app->get_num_args(); if (m_num_args != static_cast(m_pc)->m_num_args) goto backtrack; for (unsigned i = 0; i < m_num_args; i++) m_registers[i+1] = m_app->get_arg(i); m_pc = m_pc->m_next; goto main_loop; case COMPARE: m_n1 = m_registers[static_cast(m_pc)->m_reg1]; m_n2 = m_registers[static_cast(m_pc)->m_reg2]; SASSERT(m_n1 != 0); SASSERT(m_n2 != 0); if (m_n1->get_root() != m_n2->get_root()) goto backtrack; // We will use the common root when instantiating the quantifier => log the necessary equalities if (m.has_trace_stream()) { m_used_enodes.push_back(std::make_tuple(m_n1, m_n1->get_root())); m_used_enodes.push_back(std::make_tuple(m_n2, m_n2->get_root())); } m_pc = m_pc->m_next; goto main_loop; case CHECK: m_n1 = m_registers[static_cast(m_pc)->m_reg]; m_n2 = static_cast(m_pc)->m_enode; SASSERT(m_n1 != 0); SASSERT(m_n2 != 0); if (m_n1->get_root() != m_n2->get_root()) goto backtrack; // we used the equality m_n1 = m_n2 for the match and need to make sure it ends up in the log if (m.has_trace_stream()) { m_used_enodes.push_back(std::make_tuple(m_n1, m_n2)); } m_pc = m_pc->m_next; goto main_loop; /* CFILTER AND FILTER are handled differently by the compiler The compiler will never merge two CFILTERs with different m_lbl_set fields. Essentially, CFILTER is used to combine CHECK statements, and FILTER for BIND */ case CFILTER: case FILTER: m_n1 = m_registers[static_cast(m_pc)->m_reg]->get_root(); if (static_cast(m_pc)->m_lbl_set.empty_intersection(m_n1->get_lbls())) goto backtrack; m_pc = m_pc->m_next; goto main_loop; case PFILTER: m_n1 = m_registers[static_cast(m_pc)->m_reg]->get_root(); if (static_cast(m_pc)->m_lbl_set.empty_intersection(m_n1->get_plbls())) goto backtrack; m_pc = m_pc->m_next; goto main_loop; case CHOOSE: m_backtrack_stack[m_top].m_instr = m_pc; m_backtrack_stack[m_top].m_old_max_generation = m_max_generation; m_backtrack_stack[m_top].m_old_used_enodes_size = m_used_enodes.size(); m_top++; m_pc = m_pc->m_next; goto main_loop; case NOOP: SASSERT(static_cast(m_pc)->m_alt == 0); m_pc = m_pc->m_next; goto main_loop; case BIND1: #define BIND_COMMON() \ m_n1 = m_registers[static_cast(m_pc)->m_ireg]; \ SASSERT(m_n1 != 0); \ m_oreg = static_cast(m_pc)->m_oreg; \ m_curr_max_generation = m_max_generation; \ m_curr_used_enodes_size = m_used_enodes.size(); \ m_app = get_first_f_app(static_cast(m_pc)->m_label, static_cast(m_pc)->m_num_args, m_n1); \ if (!m_app) \ goto backtrack; \ TRACE("mam_int", tout << "bind candidate: " << mk_pp(m_app->get_owner(), m) << "\n";); \ m_backtrack_stack[m_top].m_instr = m_pc; \ m_backtrack_stack[m_top].m_old_max_generation = m_curr_max_generation; \ m_backtrack_stack[m_top].m_old_used_enodes_size = m_curr_used_enodes_size; \ m_backtrack_stack[m_top].m_curr = m_app; \ m_top++; BIND_COMMON(); m_registers[m_oreg] = m_app->get_arg(0); m_pc = m_pc->m_next; goto main_loop; case BIND2: BIND_COMMON(); m_registers[m_oreg] = m_app->get_arg(0); m_registers[m_oreg+1] = m_app->get_arg(1); m_pc = m_pc->m_next; goto main_loop; case BIND3: BIND_COMMON(); m_registers[m_oreg] = m_app->get_arg(0); m_registers[m_oreg+1] = m_app->get_arg(1); m_registers[m_oreg+2] = m_app->get_arg(2); m_pc = m_pc->m_next; goto main_loop; case BIND4: BIND_COMMON(); m_registers[m_oreg] = m_app->get_arg(0); m_registers[m_oreg+1] = m_app->get_arg(1); m_registers[m_oreg+2] = m_app->get_arg(2); m_registers[m_oreg+3] = m_app->get_arg(3); m_pc = m_pc->m_next; goto main_loop; case BIND5: BIND_COMMON(); m_registers[m_oreg] = m_app->get_arg(0); m_registers[m_oreg+1] = m_app->get_arg(1); m_registers[m_oreg+2] = m_app->get_arg(2); m_registers[m_oreg+3] = m_app->get_arg(3); m_registers[m_oreg+4] = m_app->get_arg(4); m_pc = m_pc->m_next; goto main_loop; case BIND6: BIND_COMMON(); m_registers[m_oreg] = m_app->get_arg(0); m_registers[m_oreg+1] = m_app->get_arg(1); m_registers[m_oreg+2] = m_app->get_arg(2); m_registers[m_oreg+3] = m_app->get_arg(3); m_registers[m_oreg+4] = m_app->get_arg(4); m_registers[m_oreg+5] = m_app->get_arg(5); m_pc = m_pc->m_next; goto main_loop; case BINDN: BIND_COMMON(); m_num_args = static_cast(m_pc)->m_num_args; for (unsigned i = 0; i < m_num_args; i++) m_registers[m_oreg+i] = m_app->get_arg(i); m_pc = m_pc->m_next; goto main_loop; case YIELD1: m_bindings[0] = m_registers[static_cast(m_pc)->m_bindings[0]]; #define ON_MATCH(NUM) \ m_max_generation = std::max(m_max_generation, get_max_generation(NUM, m_bindings.begin())); \ if (m_context.get_cancel_flag()) { \ return false; \ } \ m_mam.on_match(static_cast(m_pc)->m_qa, \ static_cast(m_pc)->m_pat, \ NUM, \ m_bindings.begin(), \ m_max_generation, m_used_enodes) ON_MATCH(1); goto backtrack; case YIELD2: m_bindings[0] = m_registers[static_cast(m_pc)->m_bindings[1]]; m_bindings[1] = m_registers[static_cast(m_pc)->m_bindings[0]]; ON_MATCH(2); goto backtrack; case YIELD3: m_bindings[0] = m_registers[static_cast(m_pc)->m_bindings[2]]; m_bindings[1] = m_registers[static_cast(m_pc)->m_bindings[1]]; m_bindings[2] = m_registers[static_cast(m_pc)->m_bindings[0]]; ON_MATCH(3); goto backtrack; case YIELD4: m_bindings[0] = m_registers[static_cast(m_pc)->m_bindings[3]]; m_bindings[1] = m_registers[static_cast(m_pc)->m_bindings[2]]; m_bindings[2] = m_registers[static_cast(m_pc)->m_bindings[1]]; m_bindings[3] = m_registers[static_cast(m_pc)->m_bindings[0]]; ON_MATCH(4); goto backtrack; case YIELD5: m_bindings[0] = m_registers[static_cast(m_pc)->m_bindings[4]]; m_bindings[1] = m_registers[static_cast(m_pc)->m_bindings[3]]; m_bindings[2] = m_registers[static_cast(m_pc)->m_bindings[2]]; m_bindings[3] = m_registers[static_cast(m_pc)->m_bindings[1]]; m_bindings[4] = m_registers[static_cast(m_pc)->m_bindings[0]]; ON_MATCH(5); goto backtrack; case YIELD6: m_bindings[0] = m_registers[static_cast(m_pc)->m_bindings[5]]; m_bindings[1] = m_registers[static_cast(m_pc)->m_bindings[4]]; m_bindings[2] = m_registers[static_cast(m_pc)->m_bindings[3]]; m_bindings[3] = m_registers[static_cast(m_pc)->m_bindings[2]]; m_bindings[4] = m_registers[static_cast(m_pc)->m_bindings[1]]; m_bindings[5] = m_registers[static_cast(m_pc)->m_bindings[0]]; ON_MATCH(6); goto backtrack; case YIELDN: m_num_args = static_cast(m_pc)->m_num_bindings; for (unsigned i = 0; i < m_num_args; i++) m_bindings[i] = m_registers[static_cast(m_pc)->m_bindings[m_num_args - i - 1]]; ON_MATCH(m_num_args); goto backtrack; case GET_ENODE: m_registers[static_cast(m_pc)->m_oreg] = static_cast(m_pc)->m_enode; m_pc = m_pc->m_next; goto main_loop; case GET_CGR1: #define GET_CGR_COMMON() \ m_n1 = m_context.get_enode_eq_to(static_cast(m_pc)->m_label, static_cast(m_pc)->m_num_args, m_args.c_ptr()); \ if (m_n1 == 0 || !m_context.is_relevant(m_n1)) \ goto backtrack; \ update_max_generation(m_n1, nullptr); \ if (m.has_trace_stream()) { \ for (unsigned i = 0; i < static_cast(m_pc)->m_num_args; ++i) { \ m_used_enodes.push_back(std::make_tuple(m_n1->get_arg(i), m_n1->get_arg(i)->get_root())); \ } \ } \ m_registers[static_cast(m_pc)->m_oreg] = m_n1; \ m_pc = m_pc->m_next; \ goto main_loop; #define SET_VAR(IDX) \ m_args[IDX] = m_registers[static_cast(m_pc)->m_iregs[IDX]]; \ if (m_use_filters && static_cast(m_pc)->m_lbl_set.empty_intersection(m_args[IDX]->get_root()->get_plbls())) { \ TRACE("trigger_bug", tout << "m_args[IDX]->get_root():\n" << mk_ismt2_pp(m_args[IDX]->get_root()->get_owner(), m) << "\n"; \ tout << "cgr set: "; static_cast(m_pc)->m_lbl_set.display(tout); tout << "\n"; \ tout << "node set: "; m_args[IDX]->get_root()->get_plbls().display(tout); tout << "\n";); \ goto backtrack; \ } SET_VAR(0); GET_CGR_COMMON(); case GET_CGR2: SET_VAR(0); SET_VAR(1); GET_CGR_COMMON(); case GET_CGR3: SET_VAR(0); SET_VAR(1); SET_VAR(2); GET_CGR_COMMON(); case GET_CGR4: SET_VAR(0); SET_VAR(1); SET_VAR(2); SET_VAR(3); GET_CGR_COMMON(); case GET_CGR5: SET_VAR(0); SET_VAR(1); SET_VAR(2); SET_VAR(3); SET_VAR(4); GET_CGR_COMMON(); case GET_CGR6: SET_VAR(0); SET_VAR(1); SET_VAR(2); SET_VAR(3); SET_VAR(4); SET_VAR(5); GET_CGR_COMMON(); case GET_CGRN: m_num_args = static_cast(m_pc)->m_num_args; m_args.reserve(m_num_args, 0); for (unsigned i = 0; i < m_num_args; i++) m_args[i] = m_registers[static_cast(m_pc)->m_iregs[i]]; GET_CGR_COMMON(); case IS_CGR: if (!exec_is_cgr(static_cast(m_pc))) goto backtrack; m_pc = m_pc->m_next; goto main_loop; case CONTINUE: m_num_args = static_cast(m_pc)->m_num_args; m_oreg = static_cast(m_pc)->m_oreg; m_app = init_continue(static_cast(m_pc), m_num_args); if (m_app == nullptr) goto backtrack; m_pattern_instances.push_back(m_app); TRACE("mam_int", tout << "continue candidate:\n" << mk_ll_pp(m_app->get_owner(), m);); for (unsigned i = 0; i < m_num_args; i++) m_registers[m_oreg+i] = m_app->get_arg(i); m_pc = m_pc->m_next; goto main_loop; } backtrack: TRACE("mam_int", tout << "backtracking.\n";); if (m_top == 0) { TRACE("mam_int", tout << "no more alternatives.\n";); #ifdef _PROFILE_MAM t->get_watch().stop(); #endif return true; // no more alternatives } backtrack_point & bp = m_backtrack_stack[m_top - 1]; m_max_generation = bp.m_old_max_generation; if (m.has_trace_stream()) m_used_enodes.shrink(bp.m_old_used_enodes_size); TRACE("mam_int", tout << "backtrack top: " << bp.m_instr << " " << *(bp.m_instr) << "\n";); #ifdef _PROFILE_MAM if (bp.m_instr->m_opcode != CHOOSE) // CHOOSE has a different status. It is a control flow backtracking. const_cast(bp.m_instr)->m_counter++; #endif if (since_last_check++ > 100) { since_last_check = 0; if (m_context.resource_limits_exceeded()) { // Soft timeout... // Cleanup before exiting while (m_top != 0) { backtrack_point & bp = m_backtrack_stack[m_top - 1]; if (bp.m_instr->m_opcode == CONTINUE && bp.m_to_recycle) recycle_enode_vector(bp.m_to_recycle); m_top--; } #ifdef _PROFILE_MAM t->get_watch().stop(); #endif return false; } } switch (bp.m_instr->m_opcode) { case CHOOSE: m_pc = static_cast(bp.m_instr)->m_alt; TRACE("mam_int", tout << "alt: " << m_pc << "\n";); SASSERT(m_pc != 0); m_top--; goto main_loop; case BIND1: #define BBIND_COMMON() m_b = static_cast(bp.m_instr); \ m_n1 = m_registers[m_b->m_ireg]; \ m_app = get_next_f_app(m_b->m_label, m_b->m_num_args, m_n1, bp.m_curr); \ if (m_app == 0) { \ m_top--; \ goto backtrack; \ } \ bp.m_curr = m_app; \ TRACE("mam_int", tout << "bind next candidate:\n" << mk_ll_pp(m_app->get_owner(), m);); \ m_oreg = m_b->m_oreg BBIND_COMMON(); m_registers[m_oreg] = m_app->get_arg(0); m_pc = m_b->m_next; goto main_loop; case BIND2: BBIND_COMMON(); m_registers[m_oreg] = m_app->get_arg(0); m_registers[m_oreg+1] = m_app->get_arg(1); m_pc = m_b->m_next; goto main_loop; case BIND3: BBIND_COMMON(); m_registers[m_oreg] = m_app->get_arg(0); m_registers[m_oreg+1] = m_app->get_arg(1); m_registers[m_oreg+2] = m_app->get_arg(2); m_pc = m_b->m_next; goto main_loop; case BIND4: BBIND_COMMON(); m_registers[m_oreg] = m_app->get_arg(0); m_registers[m_oreg+1] = m_app->get_arg(1); m_registers[m_oreg+2] = m_app->get_arg(2); m_registers[m_oreg+3] = m_app->get_arg(3); m_pc = m_b->m_next; goto main_loop; case BIND5: BBIND_COMMON(); m_registers[m_oreg] = m_app->get_arg(0); m_registers[m_oreg+1] = m_app->get_arg(1); m_registers[m_oreg+2] = m_app->get_arg(2); m_registers[m_oreg+3] = m_app->get_arg(3); m_registers[m_oreg+4] = m_app->get_arg(4); m_pc = m_b->m_next; goto main_loop; case BIND6: BBIND_COMMON(); m_registers[m_oreg] = m_app->get_arg(0); m_registers[m_oreg+1] = m_app->get_arg(1); m_registers[m_oreg+2] = m_app->get_arg(2); m_registers[m_oreg+3] = m_app->get_arg(3); m_registers[m_oreg+4] = m_app->get_arg(4); m_registers[m_oreg+5] = m_app->get_arg(5); m_pc = m_b->m_next; goto main_loop; case BINDN: BBIND_COMMON(); m_num_args = m_b->m_num_args; for (unsigned i = 0; i < m_num_args; i++) m_registers[m_oreg+i] = m_app->get_arg(i); m_pc = m_b->m_next; goto main_loop; case CONTINUE: ++bp.m_it; for (; bp.m_it != bp.m_end; ++bp.m_it) { m_app = *bp.m_it; const cont * c = static_cast(bp.m_instr); // bp.m_it may reference an enode in [begin_enodes_of(lbl), end_enodes_of(lbl)) // This enodes are not necessarily relevant. // So, we must check whether m_context.is_relevant(m_app) is true or not. if (m_app->get_num_args() == c->m_num_args && m_context.is_relevant(m_app)) { // update the pattern instance SASSERT(!m_pattern_instances.empty()); if (m_pattern_instances.size() == m_max_top_generation.size()) { m_max_top_generation.pop_back(); m_min_top_generation.pop_back(); } m_pattern_instances.pop_back(); m_pattern_instances.push_back(m_app); // continue succeeded update_max_generation(m_app, nullptr); // null indicates a top-level match TRACE("mam_int", tout << "continue next candidate:\n" << mk_ll_pp(m_app->get_owner(), m);); m_num_args = c->m_num_args; m_oreg = c->m_oreg; for (unsigned i = 0; i < m_num_args; i++) m_registers[m_oreg+i] = m_app->get_arg(i); m_pc = c->m_next; goto main_loop; } } // continue failed if (bp.m_to_recycle) recycle_enode_vector(bp.m_to_recycle); m_top--; goto backtrack; default: UNREACHABLE(); } return false; } // end of execute_core void display_trees(std::ostream & out, const ptr_vector & trees) { unsigned lbl = 0; for (code_tree * tree : trees) { if (tree) { out << "tree for f" << lbl << "\n"; out << *tree; } ++lbl; } } // ------------------------------------ // // A mapping from func_label -> code tree. // // ------------------------------------ class code_tree_map { ast_manager & m; compiler & m_compiler; ptr_vector m_trees; // mapping: func_label -> tree mam_trail_stack & m_trail_stack; #ifdef Z3DEBUG context * m_context; #endif class mk_tree_trail : public mam_trail { ptr_vector & m_trees; unsigned m_lbl_id; public: mk_tree_trail(ptr_vector & t, unsigned id):m_trees(t), m_lbl_id(id) {} void undo(mam_impl & m) override { dealloc(m_trees[m_lbl_id]); m_trees[m_lbl_id] = nullptr; } }; public: code_tree_map(ast_manager & m, compiler & c, mam_trail_stack & s): m(m), m_compiler(c), m_trail_stack(s) { } #ifdef Z3DEBUG void set_context(context * c) { m_context = c; } #endif ~code_tree_map() { std::for_each(m_trees.begin(), m_trees.end(), delete_proc()); } /** \brief Add a pattern to the code tree map. - mp: is used a pattern for qa. - first_idx: index to be used as head of the multi-pattern mp */ void add_pattern(quantifier * qa, app * mp, unsigned first_idx) { (void)m; SASSERT(m.is_pattern(mp)); SASSERT(first_idx < mp->get_num_args()); app * p = to_app(mp->get_arg(first_idx)); func_decl * lbl = p->get_decl(); unsigned lbl_id = lbl->get_decl_id(); m_trees.reserve(lbl_id+1, nullptr); if (m_trees[lbl_id] == nullptr) { m_trees[lbl_id] = m_compiler.mk_tree(qa, mp, first_idx, false); SASSERT(m_trees[lbl_id]->expected_num_args() == p->get_num_args()); DEBUG_CODE(m_trees[lbl_id]->set_context(m_context);); m_trail_stack.push(mk_tree_trail(m_trees, lbl_id)); } else { code_tree * tree = m_trees[lbl_id]; // We have to check the number of arguments because of nary + and * operators. // The E-matching engine that was built when all + and * applications were binary. // We ignore the pattern if it does not have the expected number of arguments. // This is not the ideal solution, but it avoids possible crashes. if (tree->expected_num_args() == p->get_num_args()) { m_compiler.insert(tree, qa, mp, first_idx, false); } } DEBUG_CODE(m_trees[lbl_id]->get_patterns().push_back(mp); m_trail_stack.push(push_back_trail(m_trees[lbl_id]->get_patterns()));); TRACE("trigger_bug", tout << "after add_pattern, first_idx: " << first_idx << "\n"; m_trees[lbl_id]->display(tout);); } void reset() { std::for_each(m_trees.begin(), m_trees.end(), delete_proc()); m_trees.reset(); } code_tree * get_code_tree_for(func_decl * lbl) const { unsigned lbl_id = lbl->get_decl_id(); if (lbl_id < m_trees.size()) return m_trees[lbl_id]; else return nullptr; } ptr_vector::iterator begin_code_trees() { return m_trees.begin(); } ptr_vector::iterator end_code_trees() { return m_trees.end(); } }; // ------------------------------------ // // Path trees AKA inverted path index // // ------------------------------------ /** \brief Temporary object used to encode a path of the form: f.1 -> g.2 -> h.0 These objects are used to update the inverse path index data structure. For example, in the path above, given an enode n, I want to follow the parents p_0 of n that are f-applications, and n is the second argument, then for each such p_0, I want to follow the parents p_1 of p_0 that are g-applications, and p_0 is the third argument. Finally, I want to follow the p_2 parents of p_1 that are h-applications and p_1 is the first argument of p_2. To improve the filtering power of the inverse path index, I'm also storing a ground argument (when possible) in the inverted path index. the idea is to have paths of the form f.1:t.2 -> g.2 -> h.0:s.1 The extra pairs t.2 and s.1 are an extra filter on the parents. The idea is that I want only the f-parents s.t. the third argument is equal to t. */ struct path { func_decl * m_label; unsigned short m_arg_idx; unsigned short m_ground_arg_idx; enode * m_ground_arg; unsigned m_pattern_idx; path * m_child; path (func_decl * lbl, unsigned short arg_idx, unsigned short ground_arg_idx, enode * ground_arg, unsigned pat_idx, path * child): m_label(lbl), m_arg_idx(arg_idx), m_ground_arg_idx(ground_arg_idx), m_ground_arg(ground_arg), m_pattern_idx(pat_idx), m_child(child) { SASSERT(ground_arg != nullptr || ground_arg_idx == 0); } }; bool is_equal(path const * p1, path const * p2) { for (;;) { if (p1->m_label != p2->m_label || p1->m_arg_idx != p2->m_arg_idx || p1->m_pattern_idx != p2->m_pattern_idx || (p1->m_child == nullptr) != (p2->m_child == nullptr)) { return false; } if (p1->m_child == nullptr && p2->m_child == nullptr) return true; p1 = p1->m_child; p2 = p2->m_child; } } typedef ptr_vector paths; /** \brief Inverted path index data structure. See comments at struct path. */ struct path_tree { func_decl * m_label; unsigned short m_arg_idx; unsigned short m_ground_arg_idx; enode * m_ground_arg; code_tree * m_code; approx_set m_filter; path_tree * m_sibling; path_tree * m_first_child; enode_vector * m_todo; // temporary field used to collect candidates #ifdef _PROFILE_PATH_TREE stopwatch m_watch; unsigned m_counter; unsigned m_num_eq_visited; unsigned m_num_neq_visited; bool m_already_displayed; //!< true if the path_tree was already displayed after reaching _PROFILE_PATH_TREE_THRESHOLD #endif path_tree(path * p, label_hasher & h): m_label(p->m_label), m_arg_idx(p->m_arg_idx), m_ground_arg_idx(p->m_ground_arg_idx), m_ground_arg(p->m_ground_arg), m_code(nullptr), m_filter(h(p->m_label)), m_sibling(nullptr), m_first_child(nullptr), m_todo(nullptr) { #ifdef _PROFILE_PATH_TREE m_counter = 0; m_num_eq_visited = 0; m_num_neq_visited = 0; m_already_displayed = false; #endif } void display(std::ostream & out, unsigned indent) { path_tree * curr = this; while (curr != nullptr) { for (unsigned i = 0; i < indent; i++) out << " "; out << curr->m_label->get_name() << ":" << curr->m_arg_idx; if (curr->m_ground_arg) out << ":#" << curr->m_ground_arg->get_owner_id() << ":" << curr->m_ground_arg_idx; out << " " << m_filter << " " << m_code; #ifdef _PROFILE_PATH_TREE out << ", counter: " << m_counter << ", num_eq_visited: " << m_num_eq_visited << ", num_neq_visited: " << m_num_neq_visited << ", avg. : " << static_cast(m_num_neq_visited)/static_cast(m_num_neq_visited+m_num_eq_visited); #endif out << "\n"; curr->m_first_child->display(out, indent+1); curr = curr->m_sibling; } } }; typedef std::pair path_tree_pair; // ------------------------------------ // // Matching Abstract Machine Implementation // // ------------------------------------ class mam_impl : public mam { protected: ast_manager & m; bool m_use_filters; mam_trail_stack m_trail_stack; label_hasher m_lbl_hasher; code_tree_manager m_ct_manager; compiler m_compiler; interpreter m_interpreter; code_tree_map m_trees; ptr_vector m_tmp_trees; ptr_vector m_tmp_trees_to_delete; ptr_vector m_to_match; typedef std::pair qp_pair; svector m_new_patterns; // recently added patterns // m_is_plbl[f] is true, then when f(c_1, ..., c_n) becomes relevant, // for each c_i. c_i->get_root()->lbls().insert(lbl_hash(f)) svector m_is_plbl; // m_is_clbl[f] is true, then when n=f(c_1, ..., c_n) becomes relevant, // n->get_root()->lbls().insert(lbl_hash(f)) svector m_is_clbl; // children labels // auxiliary field used to update data-structures... typedef ptr_vector func_decls; vector m_var_parent_labels; region & m_region; region m_tmp_region; path_tree_pair m_pp[APPROX_SET_CAPACITY][APPROX_SET_CAPACITY]; path_tree * m_pc[APPROX_SET_CAPACITY][APPROX_SET_CAPACITY]; pool m_pool; // temporary field used to update path trees. vector m_var_paths; // temporary field used to collect candidates ptr_vector m_todo; obj_hashtable m_shared_enodes; // ground terms that appear in patterns. enode * m_r1; // temp field enode * m_r2; // temp field class add_shared_enode_trail; friend class add_shared_enode_trail; class add_shared_enode_trail : public mam_trail { enode * m_enode; public: add_shared_enode_trail(enode * n):m_enode(n) {} void undo(mam_impl & m) override { m.m_shared_enodes.erase(m_enode); } }; #ifdef Z3DEBUG bool m_check_missing_instances; #endif enode_vector * mk_tmp_vector() { enode_vector * r = m_pool.mk(); r->reset(); return r; } void recycle(enode_vector * v) { m_pool.recycle(v); } void add_candidate(code_tree * t, enode * app) { if (t != nullptr) { TRACE("mam_candidate", tout << "adding candidate:\n" << mk_ll_pp(app->get_owner(), m);); if (!t->has_candidates()) m_to_match.push_back(t); t->add_candidate(app); } } void add_candidate(enode * app) { func_decl * lbl = app->get_decl(); add_candidate(m_trees.get_code_tree_for(lbl), app); } bool is_plbl(func_decl * lbl) const { unsigned lbl_id = lbl->get_decl_id(); return lbl_id < m_is_plbl.size() && m_is_plbl[lbl_id]; } bool is_clbl(func_decl * lbl) const { unsigned lbl_id = lbl->get_decl_id(); return lbl_id < m_is_clbl.size() && m_is_clbl[lbl_id]; } void update_lbls(enode * n, unsigned elem) { approx_set & r_lbls = n->get_root()->get_lbls(); if (!r_lbls.may_contain(elem)) { m_trail_stack.push(mam_value_trail(r_lbls)); r_lbls.insert(elem); } } void update_clbls(func_decl * lbl) { unsigned lbl_id = lbl->get_decl_id(); m_is_clbl.reserve(lbl_id+1, false); TRACE("trigger_bug", tout << "update_clbls: " << lbl->get_name() << " is already clbl: " << m_is_clbl[lbl_id] << "\n";); TRACE("mam_bug", tout << "update_clbls: " << lbl->get_name() << " is already clbl: " << m_is_clbl[lbl_id] << "\n";); if (m_is_clbl[lbl_id]) return; m_trail_stack.push(set_bitvector_trail(m_is_clbl, lbl_id)); SASSERT(m_is_clbl[lbl_id]); unsigned h = m_lbl_hasher(lbl); for (enode* app : m_context.enodes_of(lbl)) { if (m_context.is_relevant(app)) { update_lbls(app, h); TRACE("mam_bug", tout << "updating labels of: #" << app->get_owner_id() << "\n"; tout << "new_elem: " << h << "\n"; tout << "lbls: " << app->get_lbls() << "\n"; tout << "r.lbls: " << app->get_root()->get_lbls() << "\n";); } } } void update_children_plbls(enode * app, unsigned char elem) { unsigned num_args = app->get_num_args(); for (unsigned i = 0; i < num_args; i++) { enode * c = app->get_arg(i); approx_set & r_plbls = c->get_root()->get_plbls(); if (!r_plbls.may_contain(elem)) { m_trail_stack.push(mam_value_trail(r_plbls)); r_plbls.insert(elem); TRACE("trigger_bug", tout << "updating plabels of:\n" << mk_ismt2_pp(c->get_root()->get_owner(), m) << "\n"; tout << "new_elem: " << static_cast(elem) << "\n"; tout << "plbls: " << c->get_root()->get_plbls() << "\n";); TRACE("mam_bug", tout << "updating plabels of: #" << c->get_root()->get_owner_id() << "\n"; tout << "new_elem: " << static_cast(elem) << "\n"; tout << "plbls: " << c->get_root()->get_plbls() << "\n";); } } } void update_plbls(func_decl * lbl) { unsigned lbl_id = lbl->get_decl_id(); m_is_plbl.reserve(lbl_id+1, false); TRACE("trigger_bug", tout << "update_plbls: " << lbl->get_name() << " is already plbl: " << m_is_plbl[lbl_id] << ", lbl_id: " << lbl_id << "\n"; tout << "mam: " << this << "\n";); TRACE("mam_bug", tout << "update_plbls: " << lbl->get_name() << " is already plbl: " << m_is_plbl[lbl_id] << "\n";); if (m_is_plbl[lbl_id]) return; m_trail_stack.push(set_bitvector_trail(m_is_plbl, lbl_id)); SASSERT(m_is_plbl[lbl_id]); SASSERT(is_plbl(lbl)); unsigned h = m_lbl_hasher(lbl); for (enode * app : m_context.enodes_of(lbl)) { if (m_context.is_relevant(app)) update_children_plbls(app, h); } } void reset_pp_pc() { for (unsigned i = 0; i < APPROX_SET_CAPACITY; i++) { for (unsigned j = 0; j < APPROX_SET_CAPACITY; j++) { m_pp[i][j].first = 0; m_pp[i][j].second = 0; m_pc[i][j] = nullptr; } } } code_tree * mk_code(quantifier * qa, app * mp, unsigned pat_idx) { SASSERT(m.is_pattern(mp)); return m_compiler.mk_tree(qa, mp, pat_idx, true); } void insert_code(path_tree * t, quantifier * qa, app * mp, unsigned pat_idx) { SASSERT(m.is_pattern(mp)); m_compiler.insert(t->m_code, qa, mp, pat_idx, false); } path_tree * mk_path_tree(path * p, quantifier * qa, app * mp) { SASSERT(m.is_pattern(mp)); SASSERT(p != nullptr); unsigned pat_idx = p->m_pattern_idx; path_tree * head = nullptr; path_tree * curr = nullptr; path_tree * prev = nullptr; while (p != nullptr) { curr = new (m_region) path_tree(p, m_lbl_hasher); if (prev) prev->m_first_child = curr; if (!head) head = curr; prev = curr; p = p->m_child; } curr->m_code = mk_code(qa, mp, pat_idx); m_trail_stack.push(new_obj_trail(curr->m_code)); return head; } void insert(path_tree * t, path * p, quantifier * qa, app * mp) { SASSERT(m.is_pattern(mp)); path_tree * head = t; path_tree * prev_sibling = nullptr; bool found_label = false; while (t != nullptr) { if (t->m_label == p->m_label) { found_label = true; if (t->m_arg_idx == p->m_arg_idx && t->m_ground_arg == p->m_ground_arg && t->m_ground_arg_idx == p->m_ground_arg_idx ) { // found compatible node if (t->m_first_child == nullptr) { if (p->m_child == nullptr) { SASSERT(t->m_code != 0); insert_code(t, qa, mp, p->m_pattern_idx); } else { m_trail_stack.push(set_ptr_trail(t->m_first_child)); t->m_first_child = mk_path_tree(p->m_child, qa, mp); } } else { if (p->m_child == nullptr) { if (t->m_code) { insert_code(t, qa, mp, p->m_pattern_idx); } else { m_trail_stack.push(set_ptr_trail(t->m_code)); t->m_code = mk_code(qa, mp, p->m_pattern_idx); m_trail_stack.push(new_obj_trail(t->m_code)); } } else { insert(t->m_first_child, p->m_child, qa, mp); } } return; } } prev_sibling = t; t = t->m_sibling; } m_trail_stack.push(set_ptr_trail(prev_sibling->m_sibling)); prev_sibling->m_sibling = mk_path_tree(p, qa, mp); if (!found_label) { m_trail_stack.push(value_trail(head->m_filter)); head->m_filter.insert(m_lbl_hasher(p->m_label)); } } void update_pc(unsigned char h1, unsigned char h2, path * p, quantifier * qa, app * mp) { if (m_pc[h1][h2]) { insert(m_pc[h1][h2], p, qa, mp); } else { m_trail_stack.push(set_ptr_trail(m_pc[h1][h2])); m_pc[h1][h2] = mk_path_tree(p, qa, mp); } TRACE("mam_path_tree_updt", tout << "updated path tree:\n"; m_pc[h1][h2]->display(tout, 2);); } void update_pp(unsigned char h1, unsigned char h2, path * p1, path * p2, quantifier * qa, app * mp) { if (h1 == h2) { SASSERT(m_pp[h1][h2].second == 0); if (m_pp[h1][h2].first) { insert(m_pp[h1][h2].first, p1, qa, mp); if (!is_equal(p1, p2)) insert(m_pp[h1][h2].first, p2, qa, mp); } else { m_trail_stack.push(set_ptr_trail(m_pp[h1][h2].first)); m_pp[h1][h2].first = mk_path_tree(p1, qa, mp); insert(m_pp[h1][h2].first, p2, qa, mp); } } else { if (h1 > h2) { std::swap(h1, h2); std::swap(p1, p2); } if (m_pp[h1][h2].first) { SASSERT(m_pp[h1][h2].second); insert(m_pp[h1][h2].first, p1, qa, mp); insert(m_pp[h1][h2].second, p2, qa, mp); } else { SASSERT(m_pp[h1][h2].second == 0); m_trail_stack.push(set_ptr_trail(m_pp[h1][h2].first)); m_trail_stack.push(set_ptr_trail(m_pp[h1][h2].second)); m_pp[h1][h2].first = mk_path_tree(p1, qa, mp); m_pp[h1][h2].second = mk_path_tree(p2, qa, mp); } } TRACE("mam_path_tree_updt", tout << "updated path tree:\n"; SASSERT(h1 <= h2); m_pp[h1][h2].first->display(tout, 2); if (h1 != h2) { m_pp[h1][h2].second->display(tout, 2); }); } void update_vars(unsigned short var_id, path * p, quantifier * qa, app * mp) { paths & var_paths = m_var_paths[var_id]; bool found = false; for (path* curr_path : var_paths) { if (is_equal(p, curr_path)) found = true; func_decl * lbl1 = curr_path->m_label; func_decl * lbl2 = p->m_label; update_plbls(lbl1); update_plbls(lbl2); update_pp(m_lbl_hasher(lbl1), m_lbl_hasher(lbl2), curr_path, p, qa, mp); } if (!found) var_paths.push_back(p); } enode * get_ground_arg(app * pat, quantifier * qa, unsigned & pos) { pos = 0; unsigned num_args = pat->get_num_args(); for (unsigned i = 0; i < num_args; i++) { expr * arg = pat->get_arg(i); if (is_ground(arg)) { pos = i; return mk_enode(m_context, qa, to_app(arg)); } } return nullptr; } /** \brief Update inverted path index with respect to pattern pat in the context of path p. pat is a sub-expression of mp->get_arg(pat_idx). mp is a multi-pattern of qa. If p == 0, then mp->get_arg(pat_idx) == pat. */ void update_filters(app * pat, path * p, quantifier * qa, app * mp, unsigned pat_idx) { unsigned short num_args = pat->get_num_args(); unsigned ground_arg_pos = 0; enode * ground_arg = get_ground_arg(pat, qa, ground_arg_pos); func_decl * plbl = pat->get_decl(); for (unsigned short i = 0; i < num_args; i++) { expr * child = pat->get_arg(i); path * new_path = new (m_tmp_region) path(plbl, i, ground_arg_pos, ground_arg, pat_idx, p); if (is_var(child)) { update_vars(to_var(child)->get_idx(), new_path, qa, mp); continue; } SASSERT(is_app(child)); if (to_app(child)->is_ground()) { enode * n = mk_enode(m_context, qa, to_app(child)); update_plbls(plbl); if (!n->has_lbl_hash()) n->set_lbl_hash(m_context); TRACE("mam_bug", tout << "updating pc labels " << plbl->get_name() << " " << static_cast(n->get_lbl_hash()) << "\n"; tout << "#" << n->get_owner_id() << " " << n->get_root()->get_lbls() << "\n"; tout << "relevant: " << m_context.is_relevant(n) << "\n";); update_pc(m_lbl_hasher(plbl), n->get_lbl_hash(), new_path, qa, mp); continue; } func_decl * clbl = to_app(child)->get_decl(); TRACE("mam_bug", tout << "updating pc labels " << plbl->get_name() << " " << clbl->get_name() << "\n";); update_plbls(plbl); update_clbls(clbl); update_pc(m_lbl_hasher(plbl), m_lbl_hasher(clbl), new_path, qa, mp); update_filters(to_app(child), new_path, qa, mp, pat_idx); } } /** \brief Update inverted path index. */ void update_filters(quantifier * qa, app * mp) { TRACE("mam_bug", tout << "updating filters using:\n" << mk_pp(mp, m) << "\n";); unsigned num_vars = qa->get_num_decls(); if (num_vars >= m_var_paths.size()) m_var_paths.resize(num_vars+1); for (unsigned i = 0; i < num_vars; i++) m_var_paths[i].reset(); m_tmp_region.reset(); // Given a multi-pattern (p_1, ..., p_n) // We need to update the filters using patterns: // (p_1, p_2, ..., p_n) // (p_2, p_1, ..., p_n) // ... // (p_n, p_2, ..., p_1) unsigned num_patterns = mp->get_num_args(); for (unsigned i = 0; i < num_patterns; i++) { app * pat = to_app(mp->get_arg(i)); update_filters(pat, nullptr, qa, mp, i); } } void display_filter_info(std::ostream & out) { for (unsigned i = 0; i < APPROX_SET_CAPACITY; i++) { for (unsigned j = 0; j < APPROX_SET_CAPACITY; j++) { if (m_pp[i][j].first) { out << "pp[" << i << "][" << j << "]:\n"; m_pp[i][j].first->display(out, 1); if (i != j) { m_pp[i][j].second->display(out, 1); } } if (m_pc[i][j]) { out << "pc[" << i << "][" << j << "]:\n"; m_pc[i][j]->display(out, 1); } } } } /** \brief Check equality modulo the equality m_r1 = m_r2 */ bool is_eq(enode * n1, enode * n2) { return n1->get_root() == n2->get_root() || (n1->get_root() == m_r1 && n2->get_root() == m_r2) || (n2->get_root() == m_r1 && n1->get_root() == m_r2); } /** \brief Collect new E-matching candidates using the inverted path index t. */ void collect_parents(enode * r, path_tree * t) { if (t == nullptr) return; #ifdef _PROFILE_PATH_TREE t->m_watch.start(); #endif m_todo.reset(); enode_vector * to_unmark = mk_tmp_vector(); enode_vector * to_unmark2 = mk_tmp_vector(); SASSERT(to_unmark->empty()); SASSERT(to_unmark2->empty()); t->m_todo = mk_tmp_vector(); t->m_todo->push_back(r); m_todo.push_back(t); unsigned head = 0; while (head < m_todo.size()) { path_tree * t = m_todo[head]; #ifdef _PROFILE_PATH_TREE t->m_counter++; #endif TRACE("mam_path_tree", tout << "processing:\n"; t->display(tout, 2);); enode_vector * v = t->m_todo; approx_set & filter = t->m_filter; head++; #ifdef _PROFILE_PATH_TREE static unsigned counter = 0; static unsigned total_sz = 0; static unsigned max_sz = 0; counter++; total_sz += v->size(); if (v->size() > max_sz) max_sz = v->size(); if (counter % 100000 == 0) std::cout << "Avg. " << static_cast(total_sz)/static_cast(counter) << ", Max. " << max_sz << "\n"; #endif for (enode* n : *v) { // Two different kinds of mark are used: // - enode mark field: it is used to mark the already processed parents. // - enode mark2 field: it is used to mark the roots already added to be processed in the next level. // // In a previous version of Z3, the "enode mark field" was used to mark both cases. This is incorrect, // and Z3 may fail to find potential new matches. // // The file regression\acu.sx exposed this problem. enode * curr_child = n->get_root(); if (m_use_filters && curr_child->get_plbls().empty_intersection(filter)) continue; #ifdef _PROFILE_PATH_TREE static unsigned counter2 = 0; static unsigned total_sz2 = 0; static unsigned max_sz2 = 0; counter2++; total_sz2 += curr_child->get_num_parents(); if (curr_child->get_num_parents() > max_sz2) max_sz2 = curr_child->get_num_parents(); if (counter2 % 100000 == 0) std::cout << "Avg2. " << static_cast(total_sz2)/static_cast(counter2) << ", Max2. " << max_sz2 << "\n"; #endif TRACE("mam_path_tree", tout << "processing: #" << curr_child->get_owner_id() << "\n";); enode_vector::const_iterator it2 = curr_child->begin_parents(); enode_vector::const_iterator end2 = curr_child->end_parents(); for (; it2 != end2; ++it2) { enode * curr_parent = *it2; #ifdef _PROFILE_PATH_TREE if (curr_parent->is_eq()) t->m_num_eq_visited++; else t->m_num_neq_visited++; #endif // Remark: equality is never in the inverted path index. if (curr_parent->is_eq()) continue; func_decl * lbl = curr_parent->get_decl(); bool is_flat_assoc = lbl->is_flat_associative(); enode * curr_parent_root = curr_parent->get_root(); enode * curr_parent_cg = curr_parent->get_cg(); TRACE("mam_path_tree", tout << "processing parent:\n" << mk_pp(curr_parent->get_owner(), m) << "\n";); TRACE("mam_path_tree", tout << "parent is marked: " << curr_parent->is_marked() << "\n";); if (filter.may_contain(m_lbl_hasher(lbl)) && !curr_parent->is_marked() && (curr_parent_cg == curr_parent || !is_eq(curr_parent_cg, curr_parent_root)) && m_context.is_relevant(curr_parent) ) { path_tree * curr_tree = t; while (curr_tree) { if (curr_tree->m_label == lbl && // Starting at Z3 3.0, some associative operators (e.g., + and *) are represented using n-ary applications. // In this cases, we say the declarations is is_flat_assoc(). // The MAM was implemented in Z3 2.0 when the following invariant was true: // For every application f(x_1, ..., x_n) of a function symbol f, n = f->get_arity(). // Starting at Z3 3.0, this is only true if !f->is_flat_associative(). // Thus, we need the extra checks. (!is_flat_assoc || (curr_tree->m_arg_idx < curr_parent->get_num_args() && curr_tree->m_ground_arg_idx < curr_parent->get_num_args()))) { enode * curr_parent_child = curr_parent->get_arg(curr_tree->m_arg_idx)->get_root(); if (// Filter 1. the curr_child is equal to child of the current parent. curr_child == curr_parent_child && // Filter 2. ( // curr_tree has no support for the filter based on a ground argument. curr_tree->m_ground_arg == nullptr || // checks whether the child of the parent is equal to the expected ground argument. is_eq(curr_tree->m_ground_arg, curr_parent->get_arg(curr_tree->m_ground_arg_idx)) )) { if (curr_tree->m_code) { TRACE("mam_path_tree", tout << "found candidate " << expr_ref(curr_parent->get_owner(), m) << "\n";); add_candidate(curr_tree->m_code, curr_parent); } if (curr_tree->m_first_child) { path_tree * child = curr_tree->m_first_child; if (child->m_todo == nullptr) { child->m_todo = mk_tmp_vector(); m_todo.push_back(child); } if (!curr_parent_root->is_marked2()) { child->m_todo->push_back(curr_parent_root); } } } } curr_tree = curr_tree->m_sibling; } curr_parent->set_mark(); to_unmark->push_back(curr_parent); if (!curr_parent_root->is_marked2()) { curr_parent_root->set_mark2(); to_unmark2->push_back(curr_parent_root); } } } } recycle(t->m_todo); t->m_todo = nullptr; // remove both marks. unmark_enodes(to_unmark->size(), to_unmark->c_ptr()); unmark_enodes2(to_unmark2->size(), to_unmark2->c_ptr()); to_unmark->reset(); to_unmark2->reset(); } recycle(to_unmark); recycle(to_unmark2); #ifdef _PROFILE_PATH_TREE t->m_watch.stop(); if (t->m_counter % _PROFILE_PATH_TREE_THRESHOLD == 0) { std::cout << "EXPENSIVE " << t->m_watch.get_seconds() << " secs, counter: " << t->m_counter << "\n"; t->display(std::cout, 0); t->m_already_displayed = true; } #endif } void process_pp(enode * r1, enode * r2) { approx_set & plbls1 = r1->get_plbls(); approx_set & plbls2 = r2->get_plbls(); TRACE("incremental_matcher", tout << "pp: plbls1: " << plbls1 << ", plbls2: " << plbls2 << "\n";); TRACE("mam_info", tout << "pp: " << plbls1.size() * plbls2.size() << "\n";); if (!plbls1.empty() && !plbls2.empty()) { for (unsigned plbl1 : plbls1) { if (m_context.get_cancel_flag()) { break; } SASSERT(plbls1.may_contain(plbl1)); for (unsigned plbl2 : plbls2) { SASSERT(plbls2.may_contain(plbl2)); unsigned n_plbl1 = plbl1; unsigned n_plbl2 = plbl2; enode * n_r1 = r1; enode * n_r2 = r2; if (n_plbl1 > n_plbl2) { std::swap(n_plbl1, n_plbl2); std::swap(n_r1, n_r2); } if (n_plbl1 == n_plbl2) { SASSERT(m_pp[n_plbl1][n_plbl2].second == 0); if (n_r1->get_num_parents() <= n_r2->get_num_parents()) collect_parents(n_r1, m_pp[n_plbl1][n_plbl2].first); else collect_parents(n_r2, m_pp[n_plbl1][n_plbl2].first); } else { SASSERT(n_plbl1 < n_plbl2); if (n_r1->get_num_parents() <= n_r2->get_num_parents()) collect_parents(n_r1, m_pp[n_plbl1][n_plbl2].first); else collect_parents(n_r2, m_pp[n_plbl1][n_plbl2].second); } } } } } void process_pc(enode * r1, enode * r2) { approx_set & plbls = r1->get_plbls(); approx_set & clbls = r2->get_lbls(); if (!plbls.empty() && !clbls.empty()) { for (unsigned plbl1 : plbls) { if (m_context.get_cancel_flag()) { break; } SASSERT(plbls.may_contain(plbl1)); for (unsigned lbl2 : clbls) { SASSERT(clbls.may_contain(lbl2)); collect_parents(r1, m_pc[plbl1][lbl2]); } } } } void match_new_patterns() { TRACE("mam_new_pat", tout << "matching new patterns:\n";); m_tmp_trees_to_delete.reset(); for (auto const& kv : m_new_patterns) { if (m_context.get_cancel_flag()) { break; } quantifier * qa = kv.first; app * mp = kv.second; SASSERT(m.is_pattern(mp)); app * p = to_app(mp->get_arg(0)); func_decl * lbl = p->get_decl(); if (m_context.get_num_enodes_of(lbl) > 0) { unsigned lbl_id = lbl->get_decl_id(); m_tmp_trees.reserve(lbl_id+1, 0); if (m_tmp_trees[lbl_id] == 0) { m_tmp_trees[lbl_id] = m_compiler.mk_tree(qa, mp, 0, false); m_tmp_trees_to_delete.push_back(lbl); } else { m_compiler.insert(m_tmp_trees[lbl_id], qa, mp, 0, true); } } } for (func_decl * lbl : m_tmp_trees_to_delete) { unsigned lbl_id = lbl->get_decl_id(); code_tree * tmp_tree = m_tmp_trees[lbl_id]; SASSERT(tmp_tree != 0); SASSERT(m_context.get_num_enodes_of(lbl) > 0); m_interpreter.init(tmp_tree); for (enode * app : m_context.enodes_of(lbl)) { if (m_context.is_relevant(app)) m_interpreter.execute_core(tmp_tree, app); } m_tmp_trees[lbl_id] = 0; dealloc(tmp_tree); } m_new_patterns.reset(); } void collect_ground_exprs(quantifier * qa, app * mp) { ptr_buffer todo; unsigned num_patterns = mp->get_num_args(); for (unsigned i = 0; i < num_patterns; i++) { app * pat = to_app(mp->get_arg(i)); TRACE("mam_pat", tout << mk_ismt2_pp(qa, m) << "\npat:\n" << mk_ismt2_pp(pat, m) << "\n";); SASSERT(!pat->is_ground()); todo.push_back(pat); } while (!todo.empty()) { app * n = todo.back(); todo.pop_back(); if (n->is_ground()) { enode * e = mk_enode(m_context, qa, n); m_trail_stack.push(add_shared_enode_trail(e)); m_shared_enodes.insert(e); } else { unsigned num_args = n->get_num_args(); for (unsigned i = 0; i < num_args; i++) { expr * arg = n->get_arg(i); if (is_app(arg)) todo.push_back(to_app(arg)); } } } } public: mam_impl(context & ctx, bool use_filters): mam(ctx), m(ctx.get_manager()), m_use_filters(use_filters), m_trail_stack(*this), m_ct_manager(m_lbl_hasher, m_trail_stack), m_compiler(ctx, m_ct_manager, m_lbl_hasher, use_filters), m_interpreter(ctx, *this, use_filters), m_trees(m, m_compiler, m_trail_stack), m_region(m_trail_stack.get_region()), m_r1(nullptr), m_r2(nullptr) { DEBUG_CODE(m_trees.set_context(&ctx);); DEBUG_CODE(m_check_missing_instances = false;); reset_pp_pc(); } ~mam_impl() override { m_trail_stack.reset(); } void add_pattern(quantifier * qa, app * mp) override { SASSERT(m.is_pattern(mp)); TRACE("trigger_bug", tout << "adding pattern\n" << mk_ismt2_pp(qa, m) << "\n" << mk_ismt2_pp(mp, m) << "\n";); TRACE("mam_bug", tout << "adding pattern\n" << mk_pp(qa, m) << "\n" << mk_pp(mp, m) << "\n";); // Z3 checks if a pattern is ground or not before solving. // Ground patterns are discarded. // However, the simplifier may turn a non-ground pattern into a ground one. // So, we should check it again here. unsigned num_patterns = mp->get_num_args(); for (unsigned i = 0; i < num_patterns; i++) if (is_ground(mp->get_arg(i))) return; // ignore multi-pattern containing ground pattern. update_filters(qa, mp); collect_ground_exprs(qa, mp); m_new_patterns.push_back(qp_pair(qa, mp)); // The matching abstract machine implements incremental // e-matching. So, for a multi-pattern [ p_1, ..., p_n ], // we have to make n insertions. In the i-th insertion, // the pattern p_i is assumed to be the first one. for (unsigned i = 0; i < num_patterns; i++) m_trees.add_pattern(qa, mp, i); } void push_scope() override { m_trail_stack.push_scope(); } void pop_scope(unsigned num_scopes) override { if (!m_to_match.empty()) { for (code_tree* t : m_to_match) { t->reset_candidates(); } m_to_match.reset(); } m_new_patterns.reset(); m_trail_stack.pop_scope(num_scopes); } void reset() override { m_trail_stack.reset(); m_trees.reset(); m_to_match.reset(); m_new_patterns.reset(); m_is_plbl.reset(); m_is_clbl.reset(); reset_pp_pc(); m_tmp_region.reset(); } void display(std::ostream& out) override { out << "mam:\n"; m_lbl_hasher.display(out); ptr_vector::iterator it = m_trees.begin_code_trees(); ptr_vector::iterator end = m_trees.end_code_trees(); for (; it != end; ++it) { if (*it) (*it)->display(out); } } void match() override { TRACE("trigger_bug", tout << "match\n"; display(tout);); for (code_tree* t : m_to_match) { SASSERT(t->has_candidates()); m_interpreter.execute(t); t->reset_candidates(); } m_to_match.reset(); if (!m_new_patterns.empty()) { match_new_patterns(); m_new_patterns.reset(); } } void rematch(bool use_irrelevant) override { ptr_vector::iterator it = m_trees.begin_code_trees(); ptr_vector::iterator end = m_trees.end_code_trees(); unsigned lbl = 0; for (; it != end; ++it, ++lbl) { code_tree * t = *it; if (t) { m_interpreter.init(t); func_decl * lbl = t->get_root_lbl(); for (enode * curr : m_context.enodes_of(lbl)) { if (use_irrelevant || m_context.is_relevant(curr)) m_interpreter.execute_core(t, curr); } } } } #ifdef Z3DEBUG bool check_missing_instances() override { TRACE("missing_instance", tout << "checking for missing instances...\n";); flet l(m_check_missing_instances, true); rematch(false); return true; } #endif void on_match(quantifier * qa, app * pat, unsigned num_bindings, enode * const * bindings, unsigned max_generation, vector> & used_enodes) override { TRACE("trigger_bug", tout << "found match " << mk_pp(qa, m) << "\n";); #ifdef Z3DEBUG if (m_check_missing_instances) { if (!m_context.slow_contains_instance(qa, num_bindings, bindings)) { TRACE("missing_instance", tout << "qa:\n" << mk_ll_pp(qa, m) << "\npat:\n" << mk_ll_pp(pat, m); for (unsigned i = 0; i < num_bindings; i++) tout << "#" << bindings[i]->get_owner_id() << "\n" << mk_ll_pp(bindings[i]->get_owner(), m) << "\n"; ); UNREACHABLE(); } return; } for (unsigned i = 0; i < num_bindings; i++) { SASSERT(bindings[i]->get_generation() <= max_generation); } #endif 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); } bool is_shared(enode * n) const override { return !m_shared_enodes.empty() && m_shared_enodes.contains(n); } // This method is invoked when n becomes relevant. // If lazy == true, then n is not added to the list of candidate enodes for matching. That is, the method just updates the lbls. void relevant_eh(enode * n, bool lazy) override { TRACE("trigger_bug", tout << "relevant_eh:\n" << mk_ismt2_pp(n->get_owner(), m) << "\n"; tout << "mam: " << this << "\n";); TRACE("mam", tout << "relevant_eh: #" << n->get_owner_id() << "\n";); if (n->has_lbl_hash()) update_lbls(n, n->get_lbl_hash()); if (n->get_num_args() > 0) { func_decl * lbl = n->get_decl(); unsigned h = m_lbl_hasher(lbl); TRACE("trigger_bug", tout << "lbl: " << lbl->get_name() << " is_clbl(lbl): " << is_clbl(lbl) << ", is_plbl(lbl): " << is_plbl(lbl) << ", h: " << h << "\n"; tout << "lbl_id: " << lbl->get_decl_id() << "\n";); if (is_clbl(lbl)) update_lbls(n, h); if (is_plbl(lbl)) update_children_plbls(n, h); TRACE("mam_bug", tout << "adding relevant candidate:\n" << mk_ll_pp(n->get_owner(), m) << "\n";); if (!lazy) add_candidate(n); } } bool has_work() const override { return !m_to_match.empty() || !m_new_patterns.empty(); } void add_eq_eh(enode * r1, enode * r2) override { flet l1(m_r1, r1); flet l2(m_r2, r2); TRACE("mam", tout << "add_eq_eh: #" << r1->get_owner_id() << " #" << r2->get_owner_id() << "\n";); TRACE("mam_inc_bug_detail", m_context.display(tout);); TRACE("mam_inc_bug", tout << "before:\n#" << r1->get_owner_id() << " #" << r2->get_owner_id() << "\n"; tout << "r1.lbls: " << r1->get_lbls() << "\n"; tout << "r2.lbls: " << r2->get_lbls() << "\n"; tout << "r1.plbls: " << r1->get_plbls() << "\n"; tout << "r2.plbls: " << r2->get_plbls() << "\n";); process_pc(r1, r2); process_pc(r2, r1); process_pp(r1, r2); approx_set r1_plbls = r1->get_plbls(); approx_set & r2_plbls = r2->get_plbls(); approx_set r1_lbls = r1->get_lbls(); approx_set & r2_lbls = r2->get_lbls(); m_trail_stack.push(mam_value_trail(r2_lbls)); m_trail_stack.push(mam_value_trail(r2_plbls)); r2_lbls |= r1_lbls; r2_plbls |= r1_plbls; TRACE("mam_inc_bug", tout << "after:\n"; tout << "r1.lbls: " << r1->get_lbls() << "\n"; tout << "r2.lbls: " << r2->get_lbls() << "\n"; tout << "r1.plbls: " << r1->get_plbls() << "\n"; tout << "r2.plbls: " << r2->get_plbls() << "\n";); SASSERT(approx_subset(r1->get_plbls(), r2->get_plbls())); SASSERT(approx_subset(r1->get_lbls(), r2->get_lbls())); } }; mam * mk_mam(context & ctx) { return alloc(mam_impl, ctx, true); } }; #ifdef Z3DEBUG void pp(smt::code_tree * c) { c->display(std::cout); } #endif z3-z3-4.8.7/src/smt/mam.h000066400000000000000000000027301356505360400147460ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: mam.h Abstract: Matching Abstract Machine Author: Leonardo de Moura (leonardo) 2007-02-13. Revision History: --*/ #ifndef MAM_H_ #define MAM_H_ #include "ast/ast.h" #include "smt/smt_types.h" #include namespace smt { /** \brief Matching Abstract Machine (MAM) */ class mam { protected: context & m_context; public: mam(context & ctx): m_context(ctx) { } virtual ~mam() { } virtual void add_pattern(quantifier * q, app * mp) = 0; virtual void push_scope() = 0; virtual void pop_scope(unsigned num_scopes) = 0; virtual void match() = 0; virtual void rematch(bool use_irrelevant = false) = 0; virtual bool has_work() const = 0; virtual void relevant_eh(enode * n, bool lazy) = 0; virtual void add_eq_eh(enode * r1, enode * r2) = 0; virtual void reset() = 0; virtual void display(std::ostream& out) = 0; virtual void on_match(quantifier * q, app * pat, unsigned num_bindings, enode * const * bindings, unsigned max_generation, vector> & used_enodes) = 0; virtual bool is_shared(enode * n) const = 0; #ifdef Z3DEBUG virtual bool check_missing_instances() = 0; #endif }; mam * mk_mam(context & ctx); }; #endif /* MAM_H_ */ z3-z3-4.8.7/src/smt/old_interval.cpp000066400000000000000000000564121356505360400172170ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: old_interval.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-12-09. Revision History: --*/ #include "smt/old_interval.h" void ext_numeral::neg() { switch (m_kind) { case MINUS_INFINITY: m_kind = PLUS_INFINITY; break; case FINITE: m_value.neg(); break; case PLUS_INFINITY: m_kind = MINUS_INFINITY; break; } } ext_numeral & ext_numeral::operator+=(ext_numeral const & other) { SASSERT(!is_infinite() || !other.is_infinite() || m_kind == other.m_kind); if (is_infinite()) return *this; SASSERT(m_kind == FINITE); switch (other.m_kind) { case MINUS_INFINITY: m_kind = MINUS_INFINITY; m_value.reset(); return *this; case FINITE: m_value += other.m_value; return *this; case PLUS_INFINITY: m_kind = PLUS_INFINITY; m_value.reset(); return *this; } UNREACHABLE(); return *this; } ext_numeral & ext_numeral::operator-=(ext_numeral const & other) { SASSERT(!is_infinite() || !other.is_infinite() || (m_kind != other.m_kind)); if (is_infinite()) return *this; SASSERT(m_kind == FINITE); switch (other.m_kind) { case MINUS_INFINITY: m_kind = PLUS_INFINITY; m_value.reset(); return *this; case FINITE: m_value -= other.m_value; return *this; case PLUS_INFINITY: m_kind = MINUS_INFINITY; m_value.reset(); return *this; } UNREACHABLE(); return *this; } ext_numeral & ext_numeral::operator*=(ext_numeral const & other) { if (is_zero()) { m_kind = FINITE; return *this; } if (other.is_zero()) { m_kind = FINITE; m_value.reset(); return *this; } if (is_infinite() || other.is_infinite()) { if (sign() == other.sign()) m_kind = PLUS_INFINITY; else m_kind = MINUS_INFINITY; m_value.reset(); return *this; } SASSERT(m_kind == FINITE); m_value *= other.m_value; return *this; } void ext_numeral::expt(unsigned n) { switch (m_kind) { case MINUS_INFINITY: if (n % 2 == 0) m_kind = PLUS_INFINITY; return; case FINITE: m_value = m_value.expt(n); break; case PLUS_INFINITY: // do nothing break; } } void ext_numeral::inv() { SASSERT(!is_zero()); if (is_infinite()) { m_kind = FINITE; m_value.reset(); } else { m_value = rational(1) / m_value; } } void ext_numeral::display(std::ostream & out) const { switch (m_kind) { case MINUS_INFINITY: out << "-oo"; break; case FINITE: out << m_value; break; case PLUS_INFINITY: out << "oo"; break; } } bool operator==(ext_numeral const & n1, ext_numeral const & n2) { return n1.m_kind == n2.m_kind && (n1.is_infinite() || n1.m_value == n2.m_value); } bool operator<(ext_numeral const & n1, ext_numeral const & n2) { if (n1.is_infinite()) return n1.m_kind == ext_numeral::MINUS_INFINITY && n2.m_kind != ext_numeral::MINUS_INFINITY; if (n2.is_infinite()) return n2.m_kind != ext_numeral::MINUS_INFINITY; return n1.m_value < n2.m_value; } /** \brief Create interval (-oo, oo) */ interval::interval(v_dependency_manager & m): m_manager(m), m_lower(false), m_upper(true), m_lower_open(true), m_upper_open(true), m_lower_dep(nullptr), m_upper_dep(nullptr) { } /** \brief Create intervals [l,u], (l,u], [l, u), (l,u), where l and u are numerals. */ interval::interval(v_dependency_manager & m, rational const & lower, bool l_open, v_dependency * l_dep, rational const & upper, bool u_open, v_dependency * u_dep): m_manager(m), m_lower(lower), m_upper(upper), m_lower_open(l_open), m_upper_open(u_open), m_lower_dep(l_dep), m_upper_dep(u_dep) { SASSERT(lower <= upper); SASSERT(lower != upper || !l_open || !u_open); } /** \brief Create intervals [l,u], (l,u], [l, u), (l,u), where l and u are ext_numerals. */ interval::interval(v_dependency_manager & m, ext_numeral const & lower, bool l_open, v_dependency * l_dep, ext_numeral const & upper, bool u_open, v_dependency * u_dep): m_manager(m), m_lower(lower), m_upper(upper), m_lower_open(l_open), m_upper_open(u_open), m_lower_dep(l_dep), m_upper_dep(u_dep) { SASSERT(lower <= upper); SASSERT(lower != upper || !l_open || !u_open); } /** \brief Create interval [val,val] */ interval::interval(v_dependency_manager & m, rational const & val, v_dependency * l_dep, v_dependency * u_dep): m_manager(m), m_lower(val), m_upper(val), m_lower_open(false), m_upper_open(false), m_lower_dep(l_dep), m_upper_dep(u_dep) { } /** \brief Create intervals (-oo, val], (-oo, val), [val, oo), (val, oo) */ interval::interval(v_dependency_manager & m, rational const & val, bool open, bool lower, v_dependency * d): m_manager(m) { if (lower) { m_lower = ext_numeral(val); m_lower_open = open; m_lower_dep = d; m_upper = ext_numeral(true); m_upper_open = true; m_upper_dep = nullptr; } else { m_lower = ext_numeral(false); m_lower_open = true; m_lower_dep = nullptr; m_upper = ext_numeral(val); m_upper_open = open; m_upper_dep = d; } } interval::interval(interval const & other): m_manager(other.m_manager), m_lower(other.m_lower), m_upper(other.m_upper), m_lower_open(other.m_lower_open), m_upper_open(other.m_upper_open), m_lower_dep(other.m_lower_dep), m_upper_dep(other.m_upper_dep) { } interval & interval::operator=(interval const & other) { m_lower = other.m_lower; m_upper = other.m_upper; m_lower_open = other.m_lower_open; m_upper_open = other.m_upper_open; m_lower_dep = other.m_lower_dep; m_upper_dep = other.m_upper_dep; return *this; } interval & interval::operator+=(interval const & other) { m_lower += other.m_lower; m_upper += other.m_upper; m_lower_open |= other.m_lower_open; m_upper_open |= other.m_upper_open; m_lower_dep = m_lower.is_infinite() ? nullptr : m_manager.mk_join(m_lower_dep, other.m_lower_dep); m_upper_dep = m_upper.is_infinite() ? nullptr : m_manager.mk_join(m_upper_dep, other.m_upper_dep); return *this; } void interval::neg() { std::swap(m_lower, m_upper); std::swap(m_lower_open, m_upper_open); std::swap(m_lower_dep, m_upper_dep); m_lower.neg(); m_upper.neg(); } interval & interval::operator-=(interval const & other) { interval tmp(other); tmp.neg(); return operator+=(tmp); } v_dependency * interval::join(v_dependency * d1, v_dependency * d2, v_dependency * d3, v_dependency * d4) { return m_manager.mk_join(m_manager.mk_join(d1, d2), m_manager.mk_join(d3,d4)); } /** \brief Create a new v_dependency using d1, d2, and (opt1 or opt2). */ v_dependency * interval::join_opt(v_dependency * d1, v_dependency * d2, v_dependency * opt1, v_dependency * opt2) { if (opt1 == d1 || opt1 == d2) return join(d1, d2); if (opt2 == d1 || opt2 == d2) return join(d1, d2); if (opt1 == nullptr || opt2 == nullptr) return join(d1, d2); // TODO: more opts... return join(d1, d2, opt1); } interval & interval::operator*=(interval const & other) { #if Z3DEBUG || _TRACE bool contains_zero1 = contains_zero(); bool contains_zero2 = other.contains_zero(); #endif if (is_zero()) { return *this; } if (other.is_zero()) { *this = other; m_lower_dep = m_manager.mk_join(m_lower_dep, m_upper_dep); m_upper_dep = m_lower_dep; return *this; } ext_numeral const & a = m_lower; ext_numeral const & b = m_upper; ext_numeral const & c = other.m_lower; ext_numeral const & d = other.m_upper; bool a_o = m_lower_open; bool b_o = m_upper_open; bool c_o = other.m_lower_open; bool d_o = other.m_upper_open; v_dependency * a_d = m_lower_dep; v_dependency * b_d = m_upper_dep; v_dependency * c_d = other.m_lower_dep; v_dependency * d_d = other.m_upper_dep; TRACE("interval_bug", tout << "operator*= " << *this << " " << other << "\n";); if (is_N()) { if (other.is_N()) { // x <= b <= 0, y <= d <= 0 --> b*d <= x*y // a <= x <= b <= 0, c <= y <= d <= 0 --> x*y <= a*c (we can use the fact that x or y is always negative (i.e., b is neg or d is neg)) TRACE("interval_bug", tout << "(N, N)\n";); ext_numeral new_lower = b * d; ext_numeral new_upper = a * c; // if b = 0 (and the interval is closed), then the lower bound is closed m_lower_open = (is_N0() || other.is_N0()) ? false : (b_o || d_o); m_upper_open = a_o || c_o; SASSERT(a.is_neg() && c.is_neg()); m_lower = new_lower; m_upper = new_upper; m_lower_dep = m_lower.is_infinite() ? nullptr : join(b_d, d_d); m_upper_dep = m_upper.is_infinite() ? nullptr : join_opt(a_d, c_d, b_d, d_d); } else if (other.is_M()) { // a <= x <= b <= 0, y <= d, d > 0 --> a*d <= x*y (uses the fact that b is not positive) // a <= x <= b <= 0, c <= y, c < 0 --> x*y <= a*c (uses the fact that b is not positive) TRACE("interval_bug", tout << "(N, M)\n";); ext_numeral new_lower = a * d; SASSERT(new_lower.is_neg()); ext_numeral new_upper = a * c; SASSERT(new_upper.is_pos()); m_lower_open = a_o || d_o; m_upper_open = a_o || c_o; m_lower = new_lower; m_upper = new_upper; m_lower_dep = m_lower.is_infinite() ? nullptr : join(a_d, d_d, b_d); m_upper_dep = m_upper.is_infinite() ? nullptr : join(a_d, c_d, b_d); } else { // a <= x <= b <= 0, 0 <= c <= y <= d --> a*d <= x*y (uses the fact that x is neg (b is not positive) or y is pos (c is not negative)) // x <= b <= 0, 0 <= c <= y --> x*y <= b*c TRACE("interval_bug", tout << "(N, P)\n";); SASSERT(other.is_P()); ext_numeral new_lower = a * d; ext_numeral new_upper = b * c; bool is_N0_old = is_N0(); // see comment in (P, N) case m_lower_open = a_o || d_o; SASSERT(a.is_neg() && d.is_pos()); m_upper_open = (is_N0_old || other.is_P0()) ? false : (b_o || c_o); m_lower = new_lower; m_upper = new_upper; m_lower_dep = m_lower.is_infinite() ? nullptr : join_opt(a_d, d_d, b_d, c_d); m_upper_dep = m_upper.is_infinite() ? nullptr : join(b_d, c_d); } } else if (is_M()) { if (other.is_N()) { // b > 0, x <= b, c <= y <= d <= 0 --> b*c <= x*y (uses the fact that d is not positive) // a < 0, a <= x, c <= y <= d <= 0 --> x*y <= a*c (uses the fact that d is not positive) TRACE("interval_bug", tout << "(M, N)\n";); ext_numeral new_lower = b * c; SASSERT(new_lower.is_neg()); ext_numeral new_upper = a * c; SASSERT(new_upper.is_pos()); m_lower_open = b_o || c_o; SASSERT(b.is_pos() && c.is_neg()); m_upper_open = a_o || c_o; SASSERT(a.is_neg() && c.is_neg()); m_lower = new_lower; m_upper = new_upper; m_lower_dep = m_lower.is_infinite() ? nullptr : join(b_d, c_d, d_d); m_upper_dep = m_upper.is_infinite() ? nullptr : join(a_d, c_d, d_d); } else if (other.is_M()) { TRACE("interval_bug", tout << "(M, M)\n";); SASSERT(!a.is_zero() && !b.is_zero() && !c.is_zero() && !d.is_zero()); ext_numeral ad = a*d; SASSERT(!ad.is_zero()); ext_numeral bc = b*c; SASSERT(!bc.is_zero()); ext_numeral ac = a*c; SASSERT(!ac.is_zero()); ext_numeral bd = b*d; SASSERT(!bd.is_zero()); bool ad_o = a_o || d_o; bool bc_o = b_o || c_o; bool ac_o = a_o || c_o; bool bd_o = b_o || d_o; if (ad < bc || (ad == bc && !ad_o && bc_o)) { m_lower = ad; m_lower_open = ad_o; } else { m_lower = bc; m_lower_open = bc_o; } if (ac > bd || (ac == bd && !ac_o && bd_o)) { m_upper = ac; m_upper_open = ac_o; } else { m_upper = bd; m_upper_open = bd_o; } m_lower_dep = m_lower.is_infinite() ? nullptr : join(a_d, b_d, c_d, d_d); m_upper_dep = m_upper.is_infinite() ? nullptr : join(a_d, b_d, c_d, d_d); } else { // a < 0, a <= x, 0 <= c <= y <= d --> a*d <= x*y (uses the fact that c is not negative) // b > 0, x <= b, 0 <= c <= y <= d --> x*y <= b*d (uses the fact that c is not negative) TRACE("interval_bug", tout << "(M, P)\n";); SASSERT(other.is_P()); ext_numeral new_lower = a * d; SASSERT(new_lower.is_neg()); ext_numeral new_upper = b * d; SASSERT(new_upper.is_pos()); m_lower_open = a_o || d_o; SASSERT(a.is_neg() && d.is_pos()); m_upper_open = b_o || d_o; SASSERT(b.is_pos() && d.is_pos()); m_lower = new_lower; m_upper = new_upper; m_lower_dep = m_lower.is_infinite() ? nullptr : join(a_d, d_d, c_d); m_upper_dep = m_upper.is_infinite() ? nullptr : join(b_d, d_d, c_d); } } else { SASSERT(is_P()); if (other.is_N()) { // 0 <= a <= x <= b, c <= y <= d <= 0 --> x*y <= b*c (uses the fact that x is pos (a is not neg) or y is neg (d is not pos)) // 0 <= a <= x, y <= d <= 0 --> a*d <= x*y TRACE("interval_bug", tout << "(P, N)\n";); ext_numeral new_lower = b * c; ext_numeral new_upper = a * d; bool is_P0_old = is_P0(); // cache the value of is_P0(), since it may be affected by the next update. m_lower_open = b_o || c_o; SASSERT(b.is_pos() && c.is_neg()); m_upper_open = (is_P0_old || other.is_N0()) ? false : a_o || d_o; m_lower = new_lower; m_upper = new_upper; m_lower_dep = m_lower.is_infinite() ? nullptr : join_opt(b_d, c_d, a_d, d_d); m_upper_dep = m_upper.is_infinite() ? nullptr : join(a_d, d_d); } else if (other.is_M()) { // 0 <= a <= x <= b, c <= y --> b*c <= x*y (uses the fact that a is not negative) // 0 <= a <= x <= b, y <= d --> x*y <= b*d (uses the fact that a is not negative) TRACE("interval_bug", tout << "(P, M)\n";); ext_numeral new_lower = b * c; SASSERT(new_lower.is_neg()); ext_numeral new_upper = b * d; SASSERT(new_upper.is_pos()); m_lower_open = b_o || c_o; m_upper_open = b_o || d_o; m_lower = new_lower; m_upper = new_upper; m_lower_dep = m_lower.is_infinite() ? nullptr : join(b_d, c_d, a_d); m_upper_dep = m_upper.is_infinite() ? nullptr : join(b_d, d_d, a_d); } else { // 0 <= a <= x, 0 <= c <= y --> a*c <= x*y // x <= b, y <= d --> x*y <= b*d (uses the fact that x is pos (a is not negative) or y is pos (c is not negative)) TRACE("interval_bug", tout << "(P, P)\n";); SASSERT(other.is_P()); ext_numeral new_lower = a * c; ext_numeral new_upper = b * d; m_lower_open = (is_P0() || other.is_P0()) ? false : a_o || c_o; m_upper_open = b_o || d_o; SASSERT(b.is_pos() && d.is_pos()); m_lower = new_lower; m_upper = new_upper; m_lower_dep = m_lower.is_infinite() ? nullptr : join(a_d, c_d); m_upper_dep = m_upper.is_infinite() ? nullptr : join_opt(b_d, d_d, a_d, c_d); } } TRACE("interval_bug", tout << "operator*= result: " << *this << "\n";); CTRACE("interval", !(!(contains_zero1 || contains_zero2) || contains_zero()), tout << "contains_zero1: " << contains_zero1 << ", contains_zero2: " << contains_zero2 << ", contains_zero(): " << contains_zero() << "\n";); SASSERT(!(contains_zero1 || contains_zero2) || contains_zero()); return *this; } bool interval::contains_zero() const { TRACE("interval_zero_bug", tout << "contains_zero info: " << *this << "\n"; tout << "m_lower.is_neg(): " << m_lower.is_neg() << "\n"; tout << "m_lower.is_zero: " << m_lower.is_zero() << "\n"; tout << "m_lower_open: " << m_lower_open << "\n"; tout << "m_upper.is_pos(): " << m_upper.is_pos() << "\n"; tout << "m_upper.is_zero: " << m_upper.is_zero() << "\n"; tout << "m_upper_open: " << m_upper_open << "\n"; tout << "result: " << ((m_lower.is_neg() || (m_lower.is_zero() && !m_lower_open)) && (m_upper.is_pos() || (m_upper.is_zero() && !m_upper_open))) << "\n";); return (m_lower.is_neg() || (m_lower.is_zero() && !m_lower_open)) && (m_upper.is_pos() || (m_upper.is_zero() && !m_upper_open)); } bool interval::contains(rational const& v) const { if (!inf().is_infinite()) { if (v < inf().to_rational()) return false; if (v == inf().to_rational() && m_lower_open) return false; } if (!sup().is_infinite()) { if (v > sup().to_rational()) return false; if (v == sup().to_rational() && m_upper_open) return false; } return true; } interval & interval::inv() { // If the interval [l,u] does not contain 0, then 1/[l,u] = [1/u, 1/l] SASSERT(!contains_zero()); if (is_P1()) { // 0 < a <= x --> 1/x <= 1/a // 0 < a <= x <= b --> 1/b <= 1/x (use lower and upper bounds) ext_numeral new_lower = m_upper; SASSERT(!m_upper.is_zero()); new_lower.inv(); ext_numeral new_upper; if (m_lower.is_zero()) { SASSERT(m_lower_open); ext_numeral plus_infinity(true); new_upper = plus_infinity; } else { new_upper = m_lower; new_upper.inv(); } m_lower = new_lower; m_upper = new_upper; std::swap(m_lower_open, m_upper_open); v_dependency * new_upper_dep = m_lower_dep; SASSERT(!m_lower.is_infinite()); m_lower_dep = m_manager.mk_join(m_lower_dep, m_upper_dep); m_upper_dep = new_upper_dep; } else if (is_N1()) { // x <= a < 0 --> 1/a <= 1/x // b <= x <= a < 0 --> 1/b <= 1/x (use lower and upper bounds) ext_numeral new_upper = m_lower; SASSERT(!m_lower.is_zero()); new_upper.inv(); ext_numeral new_lower; if (m_upper.is_zero()) { SASSERT(m_upper_open); ext_numeral minus_infinity(false); new_lower = minus_infinity; } else { new_lower = m_upper; new_lower.inv(); } m_lower = new_lower; m_upper = new_upper; std::swap(m_lower_open, m_upper_open); v_dependency * new_lower_dep = m_upper_dep; SASSERT(!m_upper.is_infinite()); m_upper_dep = m_manager.mk_join(m_lower_dep, m_upper_dep); m_lower_dep = new_lower_dep; } else { UNREACHABLE(); } return *this; } interval & interval::operator/=(interval const & other) { SASSERT(!other.contains_zero()); if (is_zero()) { // 0/other = 0 if other != 0 TRACE("interval", other.display_with_dependencies(tout);); if (other.m_lower.is_pos() || (other.m_lower.is_zero() && other.m_lower_open)) { // other.lower > 0 // x in ([0, 0] / [other.lo, other.up]), for other.lo > 0 // <=> // x >= 0: because y*x >= 0 & y > 0 // x <= 0: because y*x <= 0 & y > 0 m_lower_dep = join(m_lower_dep, other.m_lower_dep); m_upper_dep = join(m_upper_dep, other.m_lower_dep); } else { // assertion must hold since !other.contains_zero() SASSERT(other.m_upper.is_neg() || (other.m_upper.is_zero() && other.m_upper_open)); // other.upper < 0 // x in ([0, 0] / [other.lo, other.up]), for up < 0 // <=> // x >= 0: because y*x <= 0 & y < 0 // x <= 0: because y*x >= 0 & y < 0 v_dependency* lower_dep = m_lower_dep; m_lower_dep = join(m_upper_dep, other.m_upper_dep); m_upper_dep = join(lower_dep, other.m_upper_dep); } return *this; } else { interval tmp(other); tmp.inv(); return operator*=(tmp); } } void interval::expt(unsigned n) { if (n == 1) return; if (n % 2 == 0) { if (m_lower.is_pos()) { // [l, u]^n = [l^n, u^n] if l > 0 // 0 < a <= x --> a^n <= x^n (lower bound guarantees that is positive) // 0 < a <= x <= b --> x^n <= b^n (use lower and upper bound -- need the fact that x is positive) m_lower.expt(n); m_upper.expt(n); m_upper_dep = m_upper.is_infinite() ? nullptr : m_manager.mk_join(m_lower_dep, m_upper_dep); } else if (m_upper.is_neg()) { // [l, u]^n = [u^n, l^n] if u < 0 // a <= x <= b < 0 --> x^n <= a^n (use lower and upper bound -- need the fact that x is negative) // x <= b < 0 --> b^n <= x^n std::swap(m_lower, m_upper); std::swap(m_lower_open, m_upper_open); std::swap(m_lower_dep, m_upper_dep); m_lower.expt(n); m_upper.expt(n); m_upper_dep = m_upper.is_infinite() ? nullptr : m_manager.mk_join(m_lower_dep, m_upper_dep); } else { // [l, u]^n = [0, max{l^n, u^n}] otherwise // we need both bounds to justify upper bound TRACE("interval", tout << "before: " << m_lower << " " << m_upper << " " << n << "\n";); m_lower.expt(n); m_upper.expt(n); TRACE("interval", tout << "after: " << m_lower << " " << m_upper << "\n";); if (m_lower > m_upper || (m_lower == m_upper && !m_lower_open && m_upper_open)) { m_upper = m_lower; m_upper_open = m_lower_open; } m_upper_dep = m_upper.is_infinite() ? nullptr : m_manager.mk_join(m_lower_dep, m_upper_dep); m_lower = ext_numeral(0); m_lower_open = false; m_lower_dep = nullptr; } } else { // Remark: when n is odd x^n is monotonic. m_lower.expt(n); m_upper.expt(n); } } void interval::display(std::ostream & out) const { out << (m_lower_open ? "(" : "[") << m_lower << ", " << m_upper << (m_upper_open ? ")" : "]"); } void interval::display_with_dependencies(std::ostream & out) const { ptr_vector vs; m_manager.linearize(m_lower_dep, vs); m_manager.linearize(m_upper_dep, vs); out << "["; display(out); out << ", "; bool first = true; ::display(out, vs.begin(), vs.end(), ", ", first); out << "]"; } z3-z3-4.8.7/src/smt/old_interval.h000066400000000000000000000165411356505360400166630ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: old_interval.h Abstract: Old interval class. It is still used in some modules. Author: Leonardo de Moura (leonardo) 2008-12-09. Revision History: --*/ #ifndef OLD_INTERVAL_H_ #define OLD_INTERVAL_H_ #include "util/rational.h" #include "util/dependency.h" class ext_numeral { public: enum kind { MINUS_INFINITY, FINITE, PLUS_INFINITY }; private: kind m_kind; rational m_value; explicit ext_numeral(kind k):m_kind(k) {} public: ext_numeral():m_kind(FINITE) {} /* zero */ explicit ext_numeral(bool plus_infinity):m_kind(plus_infinity ? PLUS_INFINITY : MINUS_INFINITY) {} explicit ext_numeral(rational const & val):m_kind(FINITE), m_value(val) {} explicit ext_numeral(int i):m_kind(FINITE), m_value(i) {} ext_numeral(ext_numeral const & other):m_kind(other.m_kind), m_value(other.m_value) {} bool is_infinite() const { return m_kind != FINITE; } bool sign() const { return m_kind == MINUS_INFINITY || (m_kind == FINITE && m_value.is_neg()); } void neg(); bool is_zero() const { return m_kind == FINITE && m_value.is_zero(); } bool is_neg() const { return sign(); } bool is_pos() const { return !is_neg() && !is_zero(); } rational const & to_rational() const { SASSERT(!is_infinite()); return m_value; } ext_numeral & operator+=(ext_numeral const & other); ext_numeral & operator-=(ext_numeral const & other); ext_numeral & operator*=(ext_numeral const & other); void inv(); void expt(unsigned n); void display(std::ostream & out) const; friend bool operator==(ext_numeral const & n1, ext_numeral const & n2); friend bool operator<(ext_numeral const & n1, ext_numeral const & n2); }; bool operator==(ext_numeral const & n1, ext_numeral const & n2); bool operator<(ext_numeral const & n1, ext_numeral const & n2); inline bool operator!=(ext_numeral const & n1, ext_numeral const & n2) { return !operator==(n1,n2); } inline bool operator>(ext_numeral const & n1, ext_numeral const & n2) { return operator<(n2, n1); } inline bool operator<=(ext_numeral const & n1, ext_numeral const & n2) { return !operator>(n1, n2); } inline bool operator>=(ext_numeral const & n1, ext_numeral const & n2) { return !operator<(n1, n2); } inline ext_numeral operator+(ext_numeral const & n1, ext_numeral const & n2) { return ext_numeral(n1) += n2; } inline ext_numeral operator-(ext_numeral const & n1, ext_numeral const & n2) { return ext_numeral(n1) -= n2; } inline ext_numeral operator*(ext_numeral const & n1, ext_numeral const & n2) { return ext_numeral(n1) *= n2; } inline std::ostream & operator<<(std::ostream & out, ext_numeral const & n) { n.display(out); return out; } class old_interval { v_dependency_manager & m_manager; ext_numeral m_lower; ext_numeral m_upper; bool m_lower_open; bool m_upper_open; v_dependency * m_lower_dep; // justification for the lower bound v_dependency * m_upper_dep; // justification for the upper bound v_dependency * join(v_dependency * d1, v_dependency * d2) { return m_manager.mk_join(d1, d2); } v_dependency * join(v_dependency * d1, v_dependency * d2, v_dependency * d3) { return m_manager.mk_join(m_manager.mk_join(d1, d2), d3); } v_dependency * join(v_dependency * d1, v_dependency * d2, v_dependency * d3, v_dependency * d4); v_dependency * join_opt(v_dependency * d1, v_dependency * d2, v_dependency * opt1, v_dependency * opt2); public: explicit old_interval(v_dependency_manager & m); explicit old_interval(v_dependency_manager & m, rational const & lower, bool l_open, v_dependency * l_dep, rational const & upper, bool u_open, v_dependency * u_dep); explicit old_interval(v_dependency_manager & m, rational const & val, v_dependency * l_dep = nullptr, v_dependency * u_dep = nullptr); explicit old_interval(v_dependency_manager & m, rational const & val, bool open, bool lower, v_dependency * d); explicit old_interval(v_dependency_manager & m, ext_numeral const& lower, bool l_open, v_dependency * l_dep, ext_numeral const & upper, bool u_open, v_dependency * u_dep); old_interval(old_interval const & other); bool minus_infinity() const { return m_lower.is_infinite(); } bool plus_infinity() const { return m_upper.is_infinite(); } bool is_lower_open() const { return m_lower_open; } bool is_upper_open() const { return m_upper_open; } v_dependency * get_lower_dependencies() const { return m_lower_dep; } v_dependency * get_upper_dependencies() const { return m_upper_dep; } rational const & get_lower_value() const { SASSERT(!minus_infinity()); return m_lower.to_rational(); } rational const & get_upper_value() const { SASSERT(!plus_infinity()); return m_upper.to_rational(); } old_interval & operator=(old_interval const & other); old_interval & operator+=(old_interval const & other); old_interval & operator-=(old_interval const & other); old_interval & operator*=(old_interval const & other); old_interval & operator*=(rational const & value); old_interval & operator/=(old_interval const & other); bool operator==(old_interval const & other) const { return m_lower == other.m_lower && m_upper == other.m_upper && m_lower_open == other.m_lower_open && m_upper_open == other.m_upper_open; } bool contains_zero() const; bool contains(rational const& v) const; old_interval & inv(); void expt(unsigned n); void neg(); void display(std::ostream & out) const; void display_with_dependencies(std::ostream & out) const; bool is_P() const { return m_lower.is_pos() || m_lower.is_zero(); } bool is_P0() const { return m_lower.is_zero() && !m_lower_open; } bool is_P1() const { return m_lower.is_pos() || (m_lower.is_zero() && m_lower_open); } bool is_N() const { return m_upper.is_neg() || m_upper.is_zero(); } bool is_N0() const { return m_upper.is_zero() && !m_upper_open; } bool is_N1() const { return m_upper.is_neg() || (m_upper.is_zero() && m_upper_open); } bool is_M() const { return m_lower.is_neg() && m_upper.is_pos(); } bool is_zero() const { return m_lower.is_zero() && m_upper.is_zero(); } ext_numeral const& inf() const { return m_lower; } ext_numeral const& sup() const { return m_upper; } }; inline old_interval operator+(old_interval const & i1, old_interval const & i2) { return old_interval(i1) += i2; } inline old_interval operator-(old_interval const & i1, old_interval const & i2) { return old_interval(i1) -= i2; } inline old_interval operator*(old_interval const & i1, old_interval const & i2) { return old_interval(i1) *= i2; } inline old_interval operator/(old_interval const & i1, old_interval const & i2) { return old_interval(i1) /= i2; } inline old_interval expt(old_interval const & i, unsigned n) { old_interval tmp(i); tmp.expt(n); return tmp; } inline std::ostream & operator<<(std::ostream & out, old_interval const & i) { i.display(out); return out; } struct interval_detail{}; inline std::pair wd(old_interval const & i) { interval_detail d; return std::make_pair(i, d); } inline std::ostream & operator<<(std::ostream & out, std::pair const & p) { p.first.display_with_dependencies(out); return out; } // allow "customers" of this file to keep using interval #define interval old_interval #endif /* OLD_INTERVAL_H_ */ z3-z3-4.8.7/src/smt/params/000077500000000000000000000000001356505360400153045ustar00rootroot00000000000000z3-z3-4.8.7/src/smt/params/CMakeLists.txt000066400000000000000000000006011356505360400200410ustar00rootroot00000000000000z3_add_component(smt_params SOURCES dyn_ack_params.cpp preprocessor_params.cpp qi_params.cpp smt_params.cpp theory_arith_params.cpp theory_array_params.cpp theory_bv_params.cpp theory_pb_params.cpp theory_seq_params.cpp theory_str_params.cpp COMPONENT_DEPENDENCIES ast bit_blaster pattern PYG_FILES smt_params_helper.pyg ) z3-z3-4.8.7/src/smt/params/dyn_ack_params.cpp000066400000000000000000000016301356505360400207630ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dyn_ack_params.cpp Abstract: Author: Leonardo de Moura (leonardo) 2007-05-18. Revision History: --*/ #include "smt/params/dyn_ack_params.h" #include "smt/params/smt_params_helper.hpp" void dyn_ack_params::updt_params(params_ref const & _p) { smt_params_helper p(_p); m_dack = static_cast(p.dack()); m_dack_eq = p.dack_eq(); m_dack_factor = p.dack_factor(); m_dack_threshold = p.dack_threshold(); m_dack_gc = p.dack_gc(); m_dack_gc_inv_decay = p.dack_gc_inv_decay(); } #define DISPLAY_PARAM(X) out << #X"=" << X << std::endl; void dyn_ack_params::display(std::ostream & out) const { DISPLAY_PARAM(m_dack); DISPLAY_PARAM(m_dack_eq); DISPLAY_PARAM(m_dack_factor); DISPLAY_PARAM(m_dack_threshold); DISPLAY_PARAM(m_dack_gc); DISPLAY_PARAM(m_dack_gc_inv_decay); }z3-z3-4.8.7/src/smt/params/dyn_ack_params.h000066400000000000000000000020501356505360400204250ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dyn_ack_params.h Abstract: Author: Leonardo de Moura (leonardo) 2007-05-18. Revision History: --*/ #ifndef DYN_ACK_PARAMS_H_ #define DYN_ACK_PARAMS_H_ #include "util/params.h" enum dyn_ack_strategy { DACK_DISABLED, DACK_ROOT, // congruence is the root of the conflict DACK_CR // congruence used during conflict resolution }; struct dyn_ack_params { dyn_ack_strategy m_dack; bool m_dack_eq; double m_dack_factor; unsigned m_dack_threshold; unsigned m_dack_gc; double m_dack_gc_inv_decay; public: dyn_ack_params(params_ref const & p = params_ref()) : m_dack(DACK_ROOT), m_dack_eq(false), m_dack_factor(0.1), m_dack_threshold(10), m_dack_gc(2000), m_dack_gc_inv_decay(0.8) { updt_params(p); } void updt_params(params_ref const & _p); void display(std::ostream & out) const; }; #endif /* DYN_ACK_PARAMS_H_ */ z3-z3-4.8.7/src/smt/params/preprocessor_params.cpp000066400000000000000000000032731356505360400221060ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: preprocessor_params.h Abstract: Preprocess AST before adding them to the logical context Author: Leonardo de Moura (leonardo) 2012-12-02. Revision History: --*/ #include "smt/params/preprocessor_params.h" #include "smt/params/smt_params_helper.hpp" void preprocessor_params::updt_local_params(params_ref const & _p) { smt_params_helper p(_p); m_macro_finder = p.macro_finder(); m_quasi_macros = p.quasi_macros(); m_restricted_quasi_macros = p.restricted_quasi_macros(); m_pull_nested_quantifiers = p.pull_nested_quantifiers(); m_refine_inj_axiom = p.refine_inj_axioms(); } void preprocessor_params::updt_params(params_ref const & p) { pattern_inference_params::updt_params(p); updt_local_params(p); } #define DISPLAY_PARAM(X) out << #X"=" << X << std::endl; void preprocessor_params::display(std::ostream & out) const { pattern_inference_params::display(out); bit_blaster_params::display(out); DISPLAY_PARAM(m_lift_ite); DISPLAY_PARAM(m_ng_lift_ite); DISPLAY_PARAM(m_pull_cheap_ite); DISPLAY_PARAM(m_pull_nested_quantifiers); DISPLAY_PARAM(m_eliminate_term_ite); DISPLAY_PARAM(m_macro_finder); DISPLAY_PARAM(m_propagate_values); DISPLAY_PARAM(m_refine_inj_axiom); DISPLAY_PARAM(m_eliminate_bounds); DISPLAY_PARAM(m_simplify_bit2int); DISPLAY_PARAM(m_nnf_cnf); DISPLAY_PARAM(m_distribute_forall); DISPLAY_PARAM(m_reduce_args); DISPLAY_PARAM(m_quasi_macros); DISPLAY_PARAM(m_restricted_quasi_macros); DISPLAY_PARAM(m_max_bv_sharing); DISPLAY_PARAM(m_pre_simplifier); DISPLAY_PARAM(m_nlquant_elim); } z3-z3-4.8.7/src/smt/params/preprocessor_params.h000066400000000000000000000042531356505360400215520ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: preprocessor_params.h Abstract: Preprocess AST before adding them to the logical context Author: Leonardo de Moura (leonardo) 2008-01-17. Revision History: --*/ #ifndef PREPROCESSOR_PARAMS_H_ #define PREPROCESSOR_PARAMS_H_ #include "ast/pattern/pattern_inference_params.h" #include "ast/rewriter/bit_blaster/bit_blaster_params.h" enum lift_ite_kind { LI_NONE, LI_CONSERVATIVE, LI_FULL }; struct preprocessor_params : public pattern_inference_params, public bit_blaster_params { lift_ite_kind m_lift_ite; lift_ite_kind m_ng_lift_ite; // lift ite for non ground terms bool m_pull_cheap_ite; bool m_pull_nested_quantifiers; bool m_eliminate_term_ite; bool m_macro_finder; bool m_propagate_values; bool m_refine_inj_axiom; bool m_eliminate_bounds; bool m_simplify_bit2int; bool m_nnf_cnf; bool m_distribute_forall; bool m_reduce_args; bool m_quasi_macros; bool m_restricted_quasi_macros; bool m_max_bv_sharing; bool m_pre_simplifier; bool m_nlquant_elim; public: preprocessor_params(params_ref const & p = params_ref()): m_lift_ite(LI_NONE), m_ng_lift_ite(LI_NONE), m_pull_cheap_ite(false), m_pull_nested_quantifiers(false), m_eliminate_term_ite(false), m_macro_finder(false), m_propagate_values(true), m_refine_inj_axiom(true), m_eliminate_bounds(false), m_simplify_bit2int(false), m_nnf_cnf(true), m_distribute_forall(false), m_reduce_args(false), m_quasi_macros(false), m_restricted_quasi_macros(false), m_max_bv_sharing(true), m_pre_simplifier(true), m_nlquant_elim(false) { updt_local_params(p); } void updt_local_params(params_ref const & p); void updt_params(params_ref const & p); void display(std::ostream & out) const; }; #endif /* PREPROCESSOR_PARAMS_H_ */ z3-z3-4.8.7/src/smt/params/qi_params.cpp000066400000000000000000000036631356505360400177740ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qi_params.cpp Abstract: Author: Leonardo de Moura (leonardo) 2012-12-02. Revision History: --*/ #include "smt/params/qi_params.h" #include "smt/params/smt_params_helper.hpp" void qi_params::updt_params(params_ref const & _p) { smt_params_helper p(_p); m_mbqi = p.mbqi(); m_mbqi_max_cexs = p.mbqi_max_cexs(); m_mbqi_max_cexs_incr = p.mbqi_max_cexs_incr(); m_mbqi_max_iterations = p.mbqi_max_iterations(); m_mbqi_trace = p.mbqi_trace(); m_mbqi_force_template = p.mbqi_force_template(); m_mbqi_id = p.mbqi_id(); m_qi_profile = p.qi_profile(); m_qi_profile_freq = p.qi_profile_freq(); m_qi_max_instances = p.qi_max_instances(); m_qi_eager_threshold = p.qi_eager_threshold(); m_qi_lazy_threshold = p.qi_lazy_threshold(); m_qi_cost = p.qi_cost(); m_qi_max_eager_multipatterns = p.qi_max_multi_patterns(); m_qi_quick_checker = static_cast(p.qi_quick_checker()); } #define DISPLAY_PARAM(X) out << #X"=" << X << std::endl; void qi_params::display(std::ostream & out) const { DISPLAY_PARAM(m_qi_cost); DISPLAY_PARAM(m_qi_new_gen); DISPLAY_PARAM(m_qi_eager_threshold); DISPLAY_PARAM(m_qi_lazy_threshold); DISPLAY_PARAM(m_qi_max_eager_multipatterns); DISPLAY_PARAM(m_qi_max_lazy_multipattern_matching); DISPLAY_PARAM(m_qi_profile); DISPLAY_PARAM(m_qi_profile_freq); DISPLAY_PARAM(m_qi_quick_checker); DISPLAY_PARAM(m_qi_lazy_quick_checker); DISPLAY_PARAM(m_qi_promote_unsat); DISPLAY_PARAM(m_qi_max_instances); DISPLAY_PARAM(m_qi_lazy_instantiation); DISPLAY_PARAM(m_qi_conservative_final_check); DISPLAY_PARAM(m_mbqi); DISPLAY_PARAM(m_mbqi_max_cexs); DISPLAY_PARAM(m_mbqi_max_cexs_incr); DISPLAY_PARAM(m_mbqi_max_iterations); DISPLAY_PARAM(m_mbqi_trace); DISPLAY_PARAM(m_mbqi_force_template); DISPLAY_PARAM(m_mbqi_id); } z3-z3-4.8.7/src/smt/params/qi_params.h000066400000000000000000000101401356505360400174250ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: qi_params.h Abstract: Author: Leonardo de Moura (leonardo) 2008-06-15. Revision History: --*/ #ifndef QI_PARAMS_H_ #define QI_PARAMS_H_ #include "util/util.h" #include "util/params.h" enum quick_checker_mode { MC_NO, // do not use (cheap) model checking based instantiation MC_UNSAT, // instantiate unsatisfied instances MC_NO_SAT // instantiate unsatisfied and not-satisfied instances }; struct qi_params { std::string m_qi_cost; std::string m_qi_new_gen; double m_qi_eager_threshold; double m_qi_lazy_threshold; unsigned m_qi_max_eager_multipatterns; unsigned m_qi_max_lazy_multipattern_matching; bool m_qi_profile; unsigned m_qi_profile_freq; quick_checker_mode m_qi_quick_checker; bool m_qi_lazy_quick_checker; bool m_qi_promote_unsat; unsigned m_qi_max_instances; bool m_qi_lazy_instantiation; bool m_qi_conservative_final_check; bool m_mbqi; unsigned m_mbqi_max_cexs; unsigned m_mbqi_max_cexs_incr; unsigned m_mbqi_max_iterations; bool m_mbqi_trace; unsigned m_mbqi_force_template; const char * m_mbqi_id; qi_params(params_ref const & p = params_ref()): /* The "weight 0" performance bug ------------------------------ The parameters m_qi_cost and m_qi_new_gen influence quantifier instantiation. - m_qi_cost: specify the cost of a quantifier instantiation. Z3 will block instantiations using m_qi_eager_threshold and m_qi_lazy_threshold. - m_qi_new_gen: specify how the "generation" tag of an enode created by quantifier instantiation is set. Enodes in the input problem have generation 0. Some combinations of m_qi_cost and m_qi_new_gen will prevent Z3 from breaking matching loops. For example, the "Weight 0" performance bug was triggered by the following combination: - m_qi_cost: (+ weight generation) - m_qi_new_gen: cost If a quantifier has weight 0, then the cost of instantiating it with a term in the input problem has cost 0. The new enodes created during the instantiation will be tagged with generation = const = 0. So, every enode will have generation 0, and consequently every quantifier instantiation will have cost 0. Although dangerous, this feature was requested by the Boogie team. In their case, the patterns are carefully constructed, and there are no matching loops. Moreover, the tag some quantifiers with weight 0 to instruct Z3 to never block their instances. An example is the select-store axiom. They need this feature to be able to analyze code that contains very long execution paths. So, unless requested by the user, the default weight must be > 0. Otherwise, Z3 will execute without any matching loop detection. */ m_qi_cost("(+ weight generation)"), m_qi_new_gen("cost"), m_qi_eager_threshold(10.0), m_qi_lazy_threshold(20.0), // reduced to give a chance to MBQI m_qi_max_eager_multipatterns(0), m_qi_max_lazy_multipattern_matching(2), m_qi_profile(false), m_qi_profile_freq(UINT_MAX), m_qi_quick_checker(MC_NO), m_qi_lazy_quick_checker(true), m_qi_promote_unsat(true), m_qi_max_instances(UINT_MAX), m_qi_lazy_instantiation(false), m_qi_conservative_final_check(false), m_mbqi(true), // enabled by default m_mbqi_max_cexs(1), m_mbqi_max_cexs_incr(1), m_mbqi_max_iterations(1000), m_mbqi_trace(false), m_mbqi_force_template(10), m_mbqi_id(nullptr) { updt_params(p); } void updt_params(params_ref const & p); void display(std::ostream & out) const; }; #endif /* QI_PARAMS_H_ */ z3-z3-4.8.7/src/smt/params/smt_params.cpp000066400000000000000000000121241356505360400201560ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_params.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-02-20. Revision History: --*/ #include "smt/params/smt_params.h" #include "smt/params/smt_params_helper.hpp" #include "util/gparams.h" void smt_params::updt_local_params(params_ref const & _p) { smt_params_helper p(_p); m_auto_config = p.auto_config() && gparams::get_value("auto_config") == "true"; // auto-config is not scoped by smt in gparams. m_random_seed = p.random_seed(); m_relevancy_lvl = p.relevancy(); m_ematching = p.ematching(); m_clause_proof = p.clause_proof(); m_phase_selection = static_cast(p.phase_selection()); m_restart_strategy = static_cast(p.restart_strategy()); m_restart_factor = p.restart_factor(); m_case_split_strategy = static_cast(p.case_split()); m_theory_case_split = p.theory_case_split(); m_theory_aware_branching = p.theory_aware_branching(); m_delay_units = p.delay_units(); m_delay_units_threshold = p.delay_units_threshold(); m_preprocess = _p.get_bool("preprocess", true); // hidden parameter m_max_conflicts = p.max_conflicts(); m_restart_max = p.restart_max(); m_core_validate = p.core_validate(); m_logic = _p.get_sym("logic", m_logic); m_string_solver = p.string_solver(); if (_p.get_bool("arith.greatest_error_pivot", false)) m_arith_pivot_strategy = ARITH_PIVOT_GREATEST_ERROR; else if (_p.get_bool("arith.least_error_pivot", false)) m_arith_pivot_strategy = ARITH_PIVOT_LEAST_ERROR; theory_array_params::updt_params(_p); m_dump_benchmarks = false; m_dump_min_time = 0.5; m_dump_recheck = false; } void smt_params::updt_params(params_ref const & p) { preprocessor_params::updt_params(p); dyn_ack_params::updt_params(p); qi_params::updt_params(p); theory_arith_params::updt_params(p); theory_bv_params::updt_params(p); theory_pb_params::updt_params(p); // theory_array_params::updt_params(p); theory_datatype_params::updt_params(p); updt_local_params(p); } void smt_params::updt_params(context_params const & p) { m_auto_config = p.m_auto_config; m_model = p.m_model; } #define DISPLAY_PARAM(X) out << #X"=" << X << std::endl; void smt_params::display(std::ostream & out) const { preprocessor_params::display(out); dyn_ack_params::display(out); qi_params::display(out); theory_arith_params::display(out); theory_array_params::display(out); theory_bv_params::display(out); theory_pb_params::display(out); theory_datatype_params::display(out); DISPLAY_PARAM(m_display_proof); DISPLAY_PARAM(m_display_dot_proof); DISPLAY_PARAM(m_display_unsat_core); DISPLAY_PARAM(m_check_proof); DISPLAY_PARAM(m_eq_propagation); DISPLAY_PARAM(m_binary_clause_opt); DISPLAY_PARAM(m_relevancy_lvl); DISPLAY_PARAM(m_relevancy_lemma); DISPLAY_PARAM(m_random_seed); DISPLAY_PARAM(m_random_var_freq); DISPLAY_PARAM(m_inv_decay); DISPLAY_PARAM(m_clause_decay); DISPLAY_PARAM(m_random_initial_activity); DISPLAY_PARAM(m_phase_selection); DISPLAY_PARAM(m_phase_caching_on); DISPLAY_PARAM(m_phase_caching_off); DISPLAY_PARAM(m_minimize_lemmas); DISPLAY_PARAM(m_max_conflicts); DISPLAY_PARAM(m_simplify_clauses); DISPLAY_PARAM(m_tick); DISPLAY_PARAM(m_display_features); DISPLAY_PARAM(m_new_core2th_eq); DISPLAY_PARAM(m_ematching); DISPLAY_PARAM(m_clause_proof); DISPLAY_PARAM(m_case_split_strategy); DISPLAY_PARAM(m_rel_case_split_order); DISPLAY_PARAM(m_lookahead_diseq); DISPLAY_PARAM(m_delay_units); DISPLAY_PARAM(m_delay_units_threshold); DISPLAY_PARAM(m_theory_resolve); DISPLAY_PARAM(m_restart_strategy); DISPLAY_PARAM(m_restart_initial); DISPLAY_PARAM(m_restart_factor); DISPLAY_PARAM(m_restart_adaptive); DISPLAY_PARAM(m_agility_factor); DISPLAY_PARAM(m_restart_agility_threshold); DISPLAY_PARAM(m_lemma_gc_strategy); DISPLAY_PARAM(m_lemma_gc_half); DISPLAY_PARAM(m_recent_lemmas_size); DISPLAY_PARAM(m_lemma_gc_initial); DISPLAY_PARAM(m_lemma_gc_factor); DISPLAY_PARAM(m_new_old_ratio); DISPLAY_PARAM(m_new_clause_activity); DISPLAY_PARAM(m_old_clause_activity); DISPLAY_PARAM(m_new_clause_relevancy); DISPLAY_PARAM(m_old_clause_relevancy); DISPLAY_PARAM(m_inv_clause_decay); DISPLAY_PARAM(m_smtlib_dump_lemmas); DISPLAY_PARAM(m_logic); DISPLAY_PARAM(m_profile_res_sub); DISPLAY_PARAM(m_display_bool_var2expr); DISPLAY_PARAM(m_display_ll_bool_var2expr); DISPLAY_PARAM(m_model); DISPLAY_PARAM(m_model_on_timeout); DISPLAY_PARAM(m_model_on_final_check); DISPLAY_PARAM(m_progress_sampling_freq); DISPLAY_PARAM(m_core_validate); DISPLAY_PARAM(m_preprocess); DISPLAY_PARAM(m_user_theory_preprocess_axioms); DISPLAY_PARAM(m_user_theory_persist_axioms); DISPLAY_PARAM(m_at_labels_cex); DISPLAY_PARAM(m_check_at_labels); DISPLAY_PARAM(m_dump_goal_as_smt); DISPLAY_PARAM(m_auto_config); } z3-z3-4.8.7/src/smt/params/smt_params.h000066400000000000000000000236261356505360400176340ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_params.h Abstract: Author: Leonardo de Moura (leonardo) 2008-02-20. Revision History: --*/ #ifndef SMT_PARAMS_H_ #define SMT_PARAMS_H_ #include "ast/ast.h" #include "smt/params/dyn_ack_params.h" #include "smt/params/qi_params.h" #include "smt/params/theory_arith_params.h" #include "smt/params/theory_array_params.h" #include "smt/params/theory_bv_params.h" #include "smt/params/theory_str_params.h" #include "smt/params/theory_seq_params.h" #include "smt/params/theory_pb_params.h" #include "smt/params/theory_datatype_params.h" #include "smt/params/preprocessor_params.h" #include "cmd_context/context_params.h" enum phase_selection { PS_ALWAYS_FALSE, PS_ALWAYS_TRUE, PS_CACHING, PS_CACHING_CONSERVATIVE, PS_CACHING_CONSERVATIVE2, // similar to the previous one, but alternated default config from time to time. PS_RANDOM, PS_OCCURRENCE, PS_THEORY }; enum restart_strategy { RS_GEOMETRIC, RS_IN_OUT_GEOMETRIC, RS_LUBY, RS_FIXED, RS_ARITHMETIC }; enum lemma_gc_strategy { LGC_FIXED, LGC_GEOMETRIC, LGC_AT_RESTART, LGC_NONE }; enum initial_activity { IA_ZERO, // initialized with 0 IA_RANDOM_WHEN_SEARCHING, // random when searching IA_RANDOM // always random }; enum case_split_strategy { CS_ACTIVITY, // case split based on activity CS_ACTIVITY_DELAY_NEW, // case split based on activity but delay new case splits created during the search CS_ACTIVITY_WITH_CACHE, // case split based on activity and cache the activity CS_RELEVANCY, // case split based on relevancy CS_RELEVANCY_ACTIVITY, // case split based on relevancy and activity CS_RELEVANCY_GOAL, // based on relevancy and the current goal CS_ACTIVITY_THEORY_AWARE_BRANCHING // activity-based case split, but theory solvers can manipulate activity }; struct smt_params : public preprocessor_params, public dyn_ack_params, public qi_params, public theory_arith_params, public theory_array_params, public theory_bv_params, public theory_str_params, public theory_seq_params, public theory_pb_params, public theory_datatype_params { bool m_display_proof; bool m_display_dot_proof; bool m_display_unsat_core; bool m_check_proof; bool m_eq_propagation; bool m_binary_clause_opt; unsigned m_relevancy_lvl; bool m_relevancy_lemma; unsigned m_random_seed; double m_random_var_freq; double m_inv_decay; unsigned m_clause_decay; initial_activity m_random_initial_activity; phase_selection m_phase_selection; unsigned m_phase_caching_on; unsigned m_phase_caching_off; bool m_minimize_lemmas; unsigned m_max_conflicts; unsigned m_restart_max; bool m_simplify_clauses; unsigned m_tick; bool m_display_features; bool m_new_core2th_eq; bool m_ematching; bool m_clause_proof; // ----------------------------------- // // Case split strategy // // ----------------------------------- case_split_strategy m_case_split_strategy; unsigned m_rel_case_split_order; bool m_lookahead_diseq; bool m_theory_case_split; bool m_theory_aware_branching; // ----------------------------------- // // Delay units... // // ----------------------------------- bool m_delay_units; unsigned m_delay_units_threshold; // ----------------------------------- // // Conflict resolution // // ----------------------------------- bool m_theory_resolve; // ----------------------------------- // // Restart // // ----------------------------------- restart_strategy m_restart_strategy; unsigned m_restart_initial; double m_restart_factor; bool m_restart_adaptive; double m_agility_factor; double m_restart_agility_threshold; // ----------------------------------- // // Lemma garbage collection // // ----------------------------------- lemma_gc_strategy m_lemma_gc_strategy; bool m_lemma_gc_half; unsigned m_recent_lemmas_size; unsigned m_lemma_gc_initial; double m_lemma_gc_factor; unsigned m_new_old_ratio; //!< the ratio of new and old clauses. unsigned m_new_clause_activity; unsigned m_old_clause_activity; unsigned m_new_clause_relevancy; //!< Max. number of unassigned literals to be considered relevant. unsigned m_old_clause_relevancy; //!< Max. number of unassigned literals to be considered relevant. double m_inv_clause_decay; //!< clause activity decay // ----------------------------------- // // SMT-LIB (debug) pretty printer // // ----------------------------------- bool m_smtlib_dump_lemmas; symbol m_logic; // ----------------------------------- // // Statistics for Profiling // // ----------------------------------- bool m_profile_res_sub; bool m_display_bool_var2expr; bool m_display_ll_bool_var2expr; // ----------------------------------- // // Model generation // // ----------------------------------- bool m_model; bool m_model_on_timeout; bool m_model_on_final_check; // ----------------------------------- // // Progress sampling // // ----------------------------------- unsigned m_progress_sampling_freq; // ----------------------------------- // // Debugging goodies // // ----------------------------------- bool m_core_validate; // ----------------------------------- // // From front_end_params // // ----------------------------------- bool m_preprocess; // temporary hack for disabling all preprocessing.. bool m_user_theory_preprocess_axioms; bool m_user_theory_persist_axioms; bool m_at_labels_cex; // only use labels which contains the @ symbol when building multiple counterexamples. bool m_check_at_labels; // check that @ labels are inserted to generate unique counter-examples. bool m_dump_goal_as_smt; bool m_auto_config; // ----------------------------------- // // Spacer hacking // // ----------------------------------- bool m_dump_benchmarks; double m_dump_min_time; bool m_dump_recheck; // ----------------------------------- // // Solver selection // // ----------------------------------- symbol m_string_solver; smt_params(params_ref const & p = params_ref()): m_display_proof(false), m_display_dot_proof(false), m_display_unsat_core(false), m_check_proof(false), m_eq_propagation(true), m_binary_clause_opt(true), m_relevancy_lvl(2), m_relevancy_lemma(false), m_random_seed(0), m_random_var_freq(0.01), m_inv_decay(1.052), m_clause_decay(1), m_random_initial_activity(IA_RANDOM_WHEN_SEARCHING), m_phase_selection(PS_CACHING_CONSERVATIVE), m_phase_caching_on(400), m_phase_caching_off(100), m_minimize_lemmas(true), m_max_conflicts(UINT_MAX), m_simplify_clauses(true), m_tick(1000), m_display_features(false), m_new_core2th_eq(true), m_ematching(true), m_clause_proof(false), m_case_split_strategy(CS_ACTIVITY_DELAY_NEW), m_rel_case_split_order(0), m_lookahead_diseq(false), m_theory_case_split(false), m_theory_aware_branching(false), m_delay_units(false), m_delay_units_threshold(32), m_theory_resolve(false), m_restart_strategy(RS_IN_OUT_GEOMETRIC), m_restart_initial(100), m_restart_factor(1.1), m_restart_adaptive(true), m_agility_factor(0.9999), m_restart_agility_threshold(0.18), m_lemma_gc_strategy(LGC_FIXED), m_lemma_gc_half(false), m_recent_lemmas_size(100), m_lemma_gc_initial(5000), m_lemma_gc_factor(1.1), m_new_old_ratio(16), m_new_clause_activity(10), m_old_clause_activity(500), m_new_clause_relevancy(45), m_old_clause_relevancy(6), m_inv_clause_decay(1), m_smtlib_dump_lemmas(false), m_logic(symbol::null), m_profile_res_sub(false), m_display_bool_var2expr(false), m_display_ll_bool_var2expr(false), m_model(true), m_model_on_timeout(false), m_model_on_final_check(false), m_progress_sampling_freq(0), m_core_validate(false), m_preprocess(true), // temporary hack for disabling all preprocessing.. m_user_theory_preprocess_axioms(false), m_user_theory_persist_axioms(false), m_at_labels_cex(false), m_check_at_labels(false), m_dump_goal_as_smt(false), m_auto_config(true), m_string_solver(symbol("auto")){ updt_local_params(p); } void updt_local_params(params_ref const & p); void updt_params(params_ref const & p); void updt_params(context_params const & p); void display(std::ostream & out) const; }; #endif /* SMT_PARAMS_H_ */ z3-z3-4.8.7/src/smt/params/smt_params_helper.pyg000066400000000000000000000322401356505360400215330ustar00rootroot00000000000000def_module_params(module_name='smt', class_name='smt_params_helper', description='smt solver based on lazy smt', export=True, params=(('auto_config', BOOL, True, 'automatically configure solver'), ('logic', SYMBOL, '', 'logic used to setup the SMT solver'), ('random_seed', UINT, 0, 'random seed for the smt solver'), ('relevancy', UINT, 2, 'relevancy propagation heuristic: 0 - disabled, 1 - relevancy is tracked by only affects quantifier instantiation, 2 - relevancy is tracked, and an atom is only asserted if it is relevant'), ('macro_finder', BOOL, False, 'try to find universally quantified formulas that can be viewed as macros'), ('quasi_macros', BOOL, False, 'try to find universally quantified formulas that are quasi-macros'), ('restricted_quasi_macros', BOOL, False, 'try to find universally quantified formulas that are restricted quasi-macros'), ('ematching', BOOL, True, 'E-Matching based quantifier instantiation'), ('phase_selection', UINT, 3, 'phase selection heuristic: 0 - always false, 1 - always true, 2 - phase caching, 3 - phase caching conservative, 4 - phase caching conservative 2, 5 - random, 6 - number of occurrences, 7 - theory'), ('restart_strategy', UINT, 1, '0 - geometric, 1 - inner-outer-geometric, 2 - luby, 3 - fixed, 4 - arithmetic'), ('restart_factor', DOUBLE, 1.1, 'when using geometric (or inner-outer-geometric) progression of restarts, it specifies the constant used to multiply the current restart threshold'), ('case_split', UINT, 1, '0 - case split based on variable activity, 1 - similar to 0, but delay case splits created during the search, 2 - similar to 0, but cache the relevancy, 3 - case split based on relevancy (structural splitting), 4 - case split on relevancy and activity, 5 - case split on relevancy and current goal, 6 - activity-based case split with theory-aware branching activity'), ('delay_units', BOOL, False, 'if true then z3 will not restart when a unit clause is learned'), ('delay_units_threshold', UINT, 32, 'maximum number of learned unit clauses before restarting, ignored if delay_units is false'), ('pull_nested_quantifiers', BOOL, False, 'pull nested quantifiers'), ('refine_inj_axioms', BOOL, True, 'refine injectivity axioms'), ('max_conflicts', UINT, UINT_MAX, 'maximum number of conflicts before giving up.'), ('restart.max', UINT, UINT_MAX, 'maximal number of restarts.'), ('mbqi', BOOL, True, 'model based quantifier instantiation (MBQI)'), ('mbqi.max_cexs', UINT, 1, 'initial maximal number of counterexamples used in MBQI, each counterexample generates a quantifier instantiation'), ('mbqi.max_cexs_incr', UINT, 0, 'increment for MBQI_MAX_CEXS, the increment is performed after each round of MBQI'), ('mbqi.max_iterations', UINT, 1000, 'maximum number of rounds of MBQI'), ('mbqi.trace', BOOL, False, 'generate tracing messages for Model Based Quantifier Instantiation (MBQI). It will display a message before every round of MBQI, and the quantifiers that were not satisfied'), ('mbqi.force_template', UINT, 10, 'some quantifiers can be used as templates for building interpretations for functions. Z3 uses heuristics to decide whether a quantifier will be used as a template or not. Quantifiers with weight >= mbqi.force_template are forced to be used as a template'), ('mbqi.id', STRING, '', 'Only use model-based instantiation for quantifiers with id\'s beginning with string'), ('qi.profile', BOOL, False, 'profile quantifier instantiation'), ('qi.profile_freq', UINT, UINT_MAX, 'how frequent results are reported by qi.profile'), ('qi.max_instances', UINT, UINT_MAX, 'maximum number of quantifier instantiations'), ('qi.eager_threshold', DOUBLE, 10.0, 'threshold for eager quantifier instantiation'), ('qi.lazy_threshold', DOUBLE, 20.0, 'threshold for lazy quantifier instantiation'), ('qi.cost', STRING, '(+ weight generation)', 'expression specifying what is the cost of a given quantifier instantiation'), ('qi.max_multi_patterns', UINT, 0, 'specify the number of extra multi patterns'), ('qi.quick_checker', UINT, 0, 'specify quick checker mode, 0 - no quick checker, 1 - using unsat instances, 2 - using both unsat and no-sat instances'), ('bv.reflect', BOOL, True, 'create enode for every bit-vector term'), ('bv.enable_int2bv', BOOL, True, 'enable support for int2bv and bv2int operators'), ('arith.random_initial_value', BOOL, False, 'use random initial values in the simplex-based procedure for linear arithmetic'), ('arith.solver', UINT, 2, 'arithmetic solver: 0 - no solver, 1 - bellman-ford based solver (diff. logic only), 2 - simplex based solver, 3 - floyd-warshall based solver (diff. logic only) and no theory combination 4 - utvpi, 5 - infinitary lra, 6 - lra solver'), ('arith.nl', BOOL, True, '(incomplete) nonlinear arithmetic support based on Groebner basis and interval propagation'), ('arith.nl.gb', BOOL, True, 'groebner Basis computation, this option is ignored when arith.nl=false'), ('arith.nl.branching', BOOL, True, 'branching on integer variables in non linear clusters'), ('arith.nl.rounds', UINT, 1024, 'threshold for number of (nested) final checks for non linear arithmetic'), ('arith.euclidean_solver', BOOL, False, 'eucliean solver for linear integer arithmetic'), ('arith.propagate_eqs', BOOL, True, 'propagate (cheap) equalities'), ('arith.propagation_mode', UINT, 2, '0 - no propagation, 1 - propagate existing literals, 2 - refine bounds'), ('arith.reflect', BOOL, True, 'reflect arithmetical operators to the congruence closure'), ('arith.branch_cut_ratio', UINT, 2, 'branch/cut ratio for linear integer arithmetic'), ('arith.int_eq_branch', BOOL, False, 'branching using derived integer equations'), ('arith.ignore_int', BOOL, False, 'treat integer variables as real'), ('arith.dump_lemmas', BOOL, False, 'dump arithmetic theory lemmas to files'), ('arith.greatest_error_pivot', BOOL, False, 'Pivoting strategy'), ('arith.eager_eq_axioms', BOOL, True, 'eager equality axioms'), ('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'), ('array.weak', BOOL, False, 'weak array theory'), ('array.extensional', BOOL, True, 'extensional array theory'), ('clause_proof', BOOL, False, 'record a clausal proof'), ('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'), ('dack.eq', BOOL, False, 'enable dynamic ackermannization for transtivity of equalities'), ('dack.factor', DOUBLE, 0.1, 'number of instance per conflict'), ('dack.gc', UINT, 2000, 'Dynamic ackermannization garbage collection frequency (per conflict)'), ('dack.gc_inv_decay', DOUBLE, 0.8, 'Dynamic ackermannization garbage collection decay'), ('dack.threshold', UINT, 10, ' number of times the congruence rule must be used before Leibniz\'s axiom is expanded'), ('theory_case_split', BOOL, False, 'Allow the context to use heuristics involving theory case splits, which are a set of literals of which exactly one can be assigned True. If this option is false, the context will generate extra axioms to enforce this instead.'), ('string_solver', SYMBOL, 'seq', 'solver for string/sequence theories. options are: \'z3str3\' (specialized string solver), \'seq\' (sequence solver), \'auto\' (use static features to choose best solver), \'empty\' (a no-op solver that forces an answer unknown if strings were used), \'none\' (no solver)'), ('core.validate', BOOL, False, 'validate unsat core produced by SMT context'), ('seq.split_w_len', BOOL, True, 'enable splitting guided by length constraints'), ('str.strong_arrangements', BOOL, True, 'assert equivalences instead of implications when generating string arrangement axioms'), ('str.aggressive_length_testing', BOOL, False, 'prioritize testing concrete length values over generating more options'), ('str.aggressive_value_testing', BOOL, False, 'prioritize testing concrete string constant values over generating more options'), ('str.aggressive_unroll_testing', BOOL, True, 'prioritize testing concrete regex unroll counts over generating more options'), ('str.fast_length_tester_cache', BOOL, False, 'cache length tester constants instead of regenerating them'), ('str.fast_value_tester_cache', BOOL, True, 'cache value tester constants instead of regenerating them'), ('str.string_constant_cache', BOOL, True, 'cache all generated string constants generated from anywhere in theory_str'), ('str.use_binary_search', BOOL, False, 'use a binary search heuristic for finding concrete length values for free variables in theory_str (set to False to use linear search)'), ('str.binary_search_start', UINT, 64, 'initial upper bound for theory_str binary search'), ('theory_aware_branching', BOOL, False, 'Allow the context to use extra information from theory solvers regarding literal branching prioritization.'), ('str.finite_overlap_models', BOOL, False, 'attempt a finite model search for overlapping variables instead of completely giving up on the arrangement'), ('str.overlap_priority', DOUBLE, -0.1, 'theory-aware priority for overlapping variable cases; use smt.theory_aware_branching=true'), ('str.regex_automata', BOOL, True, 'use automata-based reasoning for regular expressions (Z3str3 only)'), ('str.regex_automata_difficulty_threshold', UINT, 1000, 'difficulty threshold for regex automata heuristics'), ('str.regex_automata_intersection_difficulty_threshold', UINT, 1000, 'difficulty threshold for regex intersection heuristics'), ('str.regex_automata_failed_automaton_threshold', UINT, 10, 'number of failed automaton construction attempts after which a full automaton is automatically built'), ('str.regex_automata_failed_intersection_threshold', UINT, 10, 'number of failed automaton intersection attempts after which intersection is always computed'), ('str.regex_automata_length_attempt_threshold', UINT, 10, 'number of length/path constraint attempts before checking unsatisfiability of regex terms'), ('core.minimize', BOOL, False, 'minimize unsat core produced by SMT context'), ('core.extend_patterns', BOOL, False, 'extend unsat core with literals that trigger (potential) quantifier instances'), ('core.extend_patterns.max_distance', UINT, UINT_MAX, 'limits the distance of a pattern-extended unsat core'), ('core.extend_nonlocal_patterns', BOOL, False, 'extend unsat cores with literals that have quantifiers with patterns that contain symbols which are not in the quantifier\'s body'), ('lemma_gc_strategy', UINT, 0, 'lemma garbage collection strategy: 0 - fixed, 1 - geometric, 2 - at restart, 3 - none'), ('dt_lazy_splits', UINT, 1, 'How lazy datatype splits are performed: 0- eager, 1- lazy for infinite types, 2- lazy'), ('recfun.native', BOOL, True, 'use native rec-fun solver'), ('recfun.depth', UINT, 2, 'initial depth for maxrec expansion') )) z3-z3-4.8.7/src/smt/params/theory_arith_params.cpp000066400000000000000000000067101356505360400220600ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_arith_params.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-05-06. Revision History: --*/ #include "smt/params/theory_arith_params.h" #include "smt/params/smt_params_helper.hpp" #include "ast/rewriter/arith_rewriter_params.hpp" void theory_arith_params::updt_params(params_ref const & _p) { smt_params_helper p(_p); m_arith_random_initial_value = p.arith_random_initial_value(); m_arith_random_seed = p.random_seed(); m_arith_mode = static_cast(p.arith_solver()); m_nl_arith = p.arith_nl(); m_nl_arith_gb = p.arith_nl_gb(); m_nl_arith_branching = p.arith_nl_branching(); m_nl_arith_rounds = p.arith_nl_rounds(); m_arith_euclidean_solver = p.arith_euclidean_solver(); m_arith_propagate_eqs = p.arith_propagate_eqs(); m_arith_branch_cut_ratio = p.arith_branch_cut_ratio(); m_arith_int_eq_branching = p.arith_int_eq_branch(); m_arith_ignore_int = p.arith_ignore_int(); m_arith_bound_prop = static_cast(p.arith_propagation_mode()); m_arith_dump_lemmas = p.arith_dump_lemmas(); m_arith_reflect = p.arith_reflect(); m_arith_eager_eq_axioms = p.arith_eager_eq_axioms(); m_arith_auto_config_simplex = p.arith_auto_config_simplex(); arith_rewriter_params ap(_p); m_arith_eq2ineq = ap.eq2ineq(); } #define DISPLAY_PARAM(X) out << #X"=" << X << std::endl; void theory_arith_params::display(std::ostream & out) const { DISPLAY_PARAM(m_arith_eq2ineq); DISPLAY_PARAM(m_arith_process_all_eqs); DISPLAY_PARAM(m_arith_mode); DISPLAY_PARAM(m_arith_auto_config_simplex); //!< force simplex solver in auto_config DISPLAY_PARAM(m_arith_blands_rule_threshold); DISPLAY_PARAM(m_arith_propagate_eqs); DISPLAY_PARAM(m_arith_bound_prop); DISPLAY_PARAM(m_arith_stronger_lemmas); DISPLAY_PARAM(m_arith_skip_rows_with_big_coeffs); DISPLAY_PARAM(m_arith_max_lemma_size); DISPLAY_PARAM(m_arith_small_lemma_size); DISPLAY_PARAM(m_arith_reflect); DISPLAY_PARAM(m_arith_ignore_int); DISPLAY_PARAM(m_arith_lazy_pivoting_lvl); DISPLAY_PARAM(m_arith_random_seed); DISPLAY_PARAM(m_arith_random_initial_value); DISPLAY_PARAM(m_arith_random_lower); DISPLAY_PARAM(m_arith_random_upper); DISPLAY_PARAM(m_arith_adaptive); DISPLAY_PARAM(m_arith_adaptive_assertion_threshold); DISPLAY_PARAM(m_arith_adaptive_propagation_threshold); DISPLAY_PARAM(m_arith_dump_lemmas); DISPLAY_PARAM(m_arith_eager_eq_axioms); DISPLAY_PARAM(m_arith_branch_cut_ratio); DISPLAY_PARAM(m_arith_int_eq_branching); DISPLAY_PARAM(m_arith_enum_const_mod); DISPLAY_PARAM(m_arith_gcd_test); DISPLAY_PARAM(m_arith_eager_gcd); DISPLAY_PARAM(m_arith_adaptive_gcd); DISPLAY_PARAM(m_arith_propagation_threshold); DISPLAY_PARAM(m_arith_pivot_strategy); DISPLAY_PARAM(m_arith_add_binary_bounds); DISPLAY_PARAM(m_arith_propagation_strategy); DISPLAY_PARAM(m_arith_eq_bounds); DISPLAY_PARAM(m_arith_lazy_adapter); DISPLAY_PARAM(m_arith_fixnum); DISPLAY_PARAM(m_arith_int_only); DISPLAY_PARAM(m_nl_arith); DISPLAY_PARAM(m_nl_arith_gb); DISPLAY_PARAM(m_nl_arith_gb_threshold); DISPLAY_PARAM(m_nl_arith_gb_eqs); DISPLAY_PARAM(m_nl_arith_gb_perturbate); DISPLAY_PARAM(m_nl_arith_max_degree); DISPLAY_PARAM(m_nl_arith_branching); DISPLAY_PARAM(m_nl_arith_rounds); DISPLAY_PARAM(m_arith_euclidean_solver); } z3-z3-4.8.7/src/smt/params/theory_arith_params.h000066400000000000000000000126141356505360400215250ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_arith_params.h Abstract: Author: Leonardo de Moura (leonardo) 2008-05-06. Revision History: --*/ #ifndef THEORY_ARITH_PARAMS_H_ #define THEORY_ARITH_PARAMS_H_ #include #include "util/params.h" enum arith_solver_id { AS_NO_ARITH, // 0 AS_DIFF_LOGIC, // 1 AS_OLD_ARITH, // 2 AS_DENSE_DIFF_LOGIC, // 3 AS_UTVPI, // 4 AS_OPTINF, // 5 AS_NEW_ARITH // 6 }; enum bound_prop_mode { BP_NONE, BP_SIMPLE, // only used for implying literals BP_REFINE // refine known bounds }; enum arith_prop_strategy { ARITH_PROP_AGILITY, ARITH_PROP_PROPORTIONAL }; enum arith_pivot_strategy { ARITH_PIVOT_SMALLEST, ARITH_PIVOT_GREATEST_ERROR, ARITH_PIVOT_LEAST_ERROR }; struct theory_arith_params { bool m_arith_eq2ineq; bool m_arith_process_all_eqs; arith_solver_id m_arith_mode; bool m_arith_auto_config_simplex; //!< force simplex solver in auto_config unsigned m_arith_blands_rule_threshold; bool m_arith_propagate_eqs; bound_prop_mode m_arith_bound_prop; bool m_arith_stronger_lemmas; bool m_arith_skip_rows_with_big_coeffs; unsigned m_arith_max_lemma_size; unsigned m_arith_small_lemma_size; bool m_arith_reflect; bool m_arith_ignore_int; unsigned m_arith_lazy_pivoting_lvl; unsigned m_arith_random_seed; bool m_arith_random_initial_value; int m_arith_random_lower; int m_arith_random_upper; bool m_arith_adaptive; double m_arith_adaptive_assertion_threshold; double m_arith_adaptive_propagation_threshold; bool m_arith_dump_lemmas; bool m_arith_eager_eq_axioms; unsigned m_arith_branch_cut_ratio; bool m_arith_int_eq_branching; bool m_arith_enum_const_mod; bool m_arith_gcd_test; bool m_arith_eager_gcd; bool m_arith_adaptive_gcd; unsigned m_arith_propagation_threshold; arith_pivot_strategy m_arith_pivot_strategy; // used in diff-logic bool m_arith_add_binary_bounds; arith_prop_strategy m_arith_propagation_strategy; // used arith_eq_adapter bool m_arith_eq_bounds; bool m_arith_lazy_adapter; // performance debugging flags bool m_arith_fixnum; bool m_arith_int_only; // non linear support bool m_nl_arith; bool m_nl_arith_gb; unsigned m_nl_arith_gb_threshold; bool m_nl_arith_gb_eqs; bool m_nl_arith_gb_perturbate; unsigned m_nl_arith_max_degree; bool m_nl_arith_branching; unsigned m_nl_arith_rounds; // euclidean solver for tighting bounds bool m_arith_euclidean_solver; theory_arith_params(params_ref const & p = params_ref()): m_arith_eq2ineq(false), m_arith_process_all_eqs(false), m_arith_mode(AS_NEW_ARITH), m_arith_auto_config_simplex(false), m_arith_blands_rule_threshold(1000), m_arith_propagate_eqs(true), m_arith_bound_prop(BP_REFINE), m_arith_stronger_lemmas(true), m_arith_skip_rows_with_big_coeffs(true), m_arith_max_lemma_size(128), m_arith_small_lemma_size(16), m_arith_reflect(true), m_arith_ignore_int(false), m_arith_lazy_pivoting_lvl(0), m_arith_random_seed(0), m_arith_random_initial_value(false), m_arith_random_lower(-1000), m_arith_random_upper(1000), m_arith_adaptive(false), m_arith_adaptive_assertion_threshold(0.2), m_arith_adaptive_propagation_threshold(0.4), m_arith_dump_lemmas(false), m_arith_eager_eq_axioms(true), m_arith_branch_cut_ratio(2), m_arith_int_eq_branching(false), m_arith_enum_const_mod(false), m_arith_gcd_test(true), m_arith_eager_gcd(false), m_arith_adaptive_gcd(false), m_arith_propagation_threshold(UINT_MAX), m_arith_pivot_strategy(ARITH_PIVOT_SMALLEST), m_arith_add_binary_bounds(false), m_arith_propagation_strategy(ARITH_PROP_PROPORTIONAL), m_arith_eq_bounds(false), m_arith_lazy_adapter(false), m_arith_fixnum(false), m_arith_int_only(false), m_nl_arith(true), m_nl_arith_gb(true), m_nl_arith_gb_threshold(512), m_nl_arith_gb_eqs(false), m_nl_arith_gb_perturbate(true), m_nl_arith_max_degree(6), m_nl_arith_branching(true), m_nl_arith_rounds(1024), m_arith_euclidean_solver(false) { updt_params(p); } void updt_params(params_ref const & p); void display(std::ostream & out) const; }; #endif /* THEORY_ARITH_PARAMS_H_ */ z3-z3-4.8.7/src/smt/params/theory_array_params.cpp000066400000000000000000000016431356505360400220670ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_array_params.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-05-06. Revision History: --*/ #include "smt/params/theory_array_params.h" #include "smt/params/smt_params_helper.hpp" void theory_array_params::updt_params(params_ref const & _p) { smt_params_helper p(_p); m_array_weak = p.array_weak(); m_array_extensional = p.array_extensional(); } #define DISPLAY_PARAM(X) out << #X"=" << X << std::endl; void theory_array_params::display(std::ostream & out) const { DISPLAY_PARAM(m_array_mode); DISPLAY_PARAM(m_array_weak); DISPLAY_PARAM(m_array_extensional); DISPLAY_PARAM(m_array_laziness); DISPLAY_PARAM(m_array_delay_exp_axiom); DISPLAY_PARAM(m_array_cg); DISPLAY_PARAM(m_array_always_prop_upward); DISPLAY_PARAM(m_array_lazy_ieq); DISPLAY_PARAM(m_array_lazy_ieq_delay); } z3-z3-4.8.7/src/smt/params/theory_array_params.h000066400000000000000000000051661356505360400215400ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_array_params.h Abstract: Author: Leonardo de Moura (leonardo) 2008-06-01. Revision History: --*/ #ifndef THEORY_ARRAY_PARAMS_H_ #define THEORY_ARRAY_PARAMS_H_ #include "util/params.h" enum array_solver_id { AR_NO_ARRAY, AR_SIMPLE, AR_MODEL_BASED, AR_FULL }; struct theory_array_params { bool m_array_canonize_simplify; bool m_array_simplify; // temporary hack for disabling array simplifier plugin. array_solver_id m_array_mode; bool m_array_weak; bool m_array_extensional; unsigned m_array_laziness; bool m_array_delay_exp_axiom; bool m_array_cg; bool m_array_always_prop_upward; bool m_array_lazy_ieq; unsigned m_array_lazy_ieq_delay; bool m_array_fake_support; // fake support for all array operations to pretend they are satisfiable. theory_array_params(): m_array_canonize_simplify(false), m_array_simplify(true), m_array_mode(AR_FULL), m_array_weak(false), m_array_extensional(true), m_array_laziness(1), m_array_delay_exp_axiom(true), m_array_cg(false), m_array_always_prop_upward(true), // UPWARDs filter is broken... TODO: fix it m_array_lazy_ieq(false), m_array_lazy_ieq_delay(10), m_array_fake_support(false) { } void updt_params(params_ref const & _p); #if 0 void register_params(ini_params & p) { p.register_int_param("array_solver", 0, 3, reinterpret_cast(m_array_mode), "0 - no array, 1 - simple, 2 - model based, 3 - full"); p.register_bool_param("array_weak", m_array_weak); p.register_bool_param("array_extensional", m_array_extensional); p.register_unsigned_param("array_laziness", m_array_laziness); p.register_bool_param("array_delay_exp_axiom", m_array_delay_exp_axiom); p.register_bool_param("array_cg", m_array_cg); p.register_bool_param("array_always_prop_upward", m_array_always_prop_upward, "Disable the built-in filter upwards propagation"); p.register_bool_param("array_lazy_ieq", m_array_lazy_ieq); p.register_unsigned_param("array_lazy_ieq_delay", m_array_lazy_ieq_delay); p.register_bool_param("array_canonize", m_array_canonize_simplify, "Normalize arrays into normal form during simplification"); } #endif void display(std::ostream & out) const; }; #endif /* THEORY_ARRAY_PARAMS_H_ */ z3-z3-4.8.7/src/smt/params/theory_bv_params.cpp000066400000000000000000000016321356505360400213560ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: theory_bv_params.cpp Abstract: Author: Leonardo de Moura (leonardo) 2012-12-02. Revision History: --*/ #include "smt/params/theory_bv_params.h" #include "smt/params/smt_params_helper.hpp" #include "ast/rewriter/bv_rewriter_params.hpp" void theory_bv_params::updt_params(params_ref const & _p) { smt_params_helper p(_p); bv_rewriter_params rp(_p); m_hi_div0 = rp.hi_div0(); m_bv_reflect = p.bv_reflect(); m_bv_enable_int2bv2int = p.bv_enable_int2bv(); } #define DISPLAY_PARAM(X) out << #X"=" << X << std::endl; void theory_bv_params::display(std::ostream & out) const { DISPLAY_PARAM(m_bv_mode); DISPLAY_PARAM(m_hi_div0); DISPLAY_PARAM(m_bv_reflect); DISPLAY_PARAM(m_bv_lazy_le); DISPLAY_PARAM(m_bv_cc); DISPLAY_PARAM(m_bv_blast_max_size); DISPLAY_PARAM(m_bv_enable_int2bv2int); } z3-z3-4.8.7/src/smt/params/theory_bv_params.h000066400000000000000000000021501356505360400210170ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_bv_params.h Abstract: Author: Leonardo de Moura (leonardo) 2008-06-06. Revision History: --*/ #ifndef THEORY_BV_PARAMS_H_ #define THEORY_BV_PARAMS_H_ #include "util/params.h" enum bv_solver_id { BS_NO_BV, BS_BLASTER }; struct theory_bv_params { bv_solver_id m_bv_mode; bool m_hi_div0; //!< if true, uses the hardware interpretation for div0, mod0, ... if false, div0, mod0, ... are considered uninterpreted. bool m_bv_reflect; bool m_bv_lazy_le; bool m_bv_cc; unsigned m_bv_blast_max_size; bool m_bv_enable_int2bv2int; theory_bv_params(params_ref const & p = params_ref()): m_bv_mode(BS_BLASTER), m_hi_div0(false), m_bv_reflect(true), m_bv_lazy_le(false), m_bv_cc(false), m_bv_blast_max_size(INT_MAX), m_bv_enable_int2bv2int(true) { updt_params(p); } void updt_params(params_ref const & p); void display(std::ostream & out) const; }; #endif /* THEORY_BV_PARAMS_H_ */ z3-z3-4.8.7/src/smt/params/theory_datatype_params.h000066400000000000000000000013331356505360400222250ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_datatype_params.h Abstract: Author: Leonardo de Moura (leonardo) 2008-11-04. Revision History: --*/ #ifndef THEORY_DATATYPE_PARAMS_H_ #define THEORY_DATATYPE_PARAMS_H_ #include "smt/params/smt_params_helper.hpp" struct theory_datatype_params { unsigned m_dt_lazy_splits; theory_datatype_params(): m_dt_lazy_splits(1) { } void updt_params(params_ref const & _p) { smt_params_helper p(_p); m_dt_lazy_splits = p.dt_lazy_splits(); } void display(std::ostream & out) const { out << "m_dt_lazy_splits=" << m_dt_lazy_splits << std::endl; } }; #endif /* THEORY_DATATYPE_PARAMS_H_ */ z3-z3-4.8.7/src/smt/params/theory_pb_params.cpp000066400000000000000000000012561356505360400213520ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: theory_pb_params.cpp Abstract: Author: Nikolaj Bjorner (nbjorner) 2014-01-01 Revision History: --*/ #include "smt/params/theory_pb_params.h" #include "smt/params/smt_params_helper.hpp" 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(); } #define DISPLAY_PARAM(X) out << #X"=" << X << std::endl; void theory_pb_params::display(std::ostream & out) const { DISPLAY_PARAM(m_pb_conflict_frequency); DISPLAY_PARAM(m_pb_learn_complements); } z3-z3-4.8.7/src/smt/params/theory_pb_params.h000066400000000000000000000012071356505360400210130ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: theory_pb_params.h Abstract: Author: Nikolaj Bjorner (nbjorner) 2014-01-01 Revision History: --*/ #ifndef THEORY_PB_PARAMS_H_ #define THEORY_PB_PARAMS_H_ #include "util/params.h" struct theory_pb_params { unsigned m_pb_conflict_frequency; bool m_pb_learn_complements; theory_pb_params(params_ref const & p = params_ref()): m_pb_conflict_frequency(1000), m_pb_learn_complements(true) {} void updt_params(params_ref const & p); void display(std::ostream & out) const; }; #endif /* THEORY_PB_PARAMS_H_ */ z3-z3-4.8.7/src/smt/params/theory_seq_params.cpp000066400000000000000000000006041356505360400215350ustar00rootroot00000000000000/*++ Copyright (c) 2018 Microsoft Corporation Module Name: theory_seq_params.cpp Abstract: Parameters for sequence theory plugin Revision History: --*/ #include "smt/params/theory_seq_params.h" #include "smt/params/smt_params_helper.hpp" void theory_seq_params::updt_params(params_ref const & _p) { smt_params_helper p(_p); m_split_w_len = p.seq_split_w_len(); } z3-z3-4.8.7/src/smt/params/theory_seq_params.h000066400000000000000000000011001356505360400211720ustar00rootroot00000000000000/*++ Copyright (c) 2018 Microsoft Corporation Module Name: theory_seq_params.h Abstract: Parameters for sequence theory plugin Revision History: --*/ #ifndef THEORY_SEQ_PARAMS_H #define THEORY_SEQ_PARAMS_H #include "util/params.h" struct theory_seq_params { /* * Enable splitting guided by length constraints */ bool m_split_w_len; theory_seq_params(params_ref const & p = params_ref()): m_split_w_len(true) { updt_params(p); } void updt_params(params_ref const & p); }; #endif /* THEORY_SEQ_PARAMS_H */ z3-z3-4.8.7/src/smt/params/theory_str_params.cpp000066400000000000000000000030521356505360400215550ustar00rootroot00000000000000/*++ Module Name: theory_str_params.cpp Abstract: Parameters for string theory plugin Author: Murphy Berzish (mtrberzi) 2016-12-13 Revision History: --*/ #include "smt/params/theory_str_params.h" #include "smt/params/smt_params_helper.hpp" void theory_str_params::updt_params(params_ref const & _p) { smt_params_helper p(_p); m_StrongArrangements = p.str_strong_arrangements(); m_AggressiveLengthTesting = p.str_aggressive_length_testing(); m_AggressiveValueTesting = p.str_aggressive_value_testing(); m_AggressiveUnrollTesting = p.str_aggressive_unroll_testing(); m_UseFastLengthTesterCache = p.str_fast_length_tester_cache(); m_UseFastValueTesterCache = p.str_fast_value_tester_cache(); m_StringConstantCache = p.str_string_constant_cache(); m_FiniteOverlapModels = p.str_finite_overlap_models(); m_UseBinarySearch = p.str_use_binary_search(); m_BinarySearchInitialUpperBound = p.str_binary_search_start(); m_OverlapTheoryAwarePriority = p.str_overlap_priority(); m_RegexAutomata = p.str_regex_automata(); m_RegexAutomata_DifficultyThreshold = p.str_regex_automata_difficulty_threshold(); m_RegexAutomata_IntersectionDifficultyThreshold = p.str_regex_automata_intersection_difficulty_threshold(); m_RegexAutomata_FailedAutomatonThreshold = p.str_regex_automata_failed_automaton_threshold(); m_RegexAutomata_FailedIntersectionThreshold = p.str_regex_automata_failed_intersection_threshold(); m_RegexAutomata_LengthAttemptThreshold = p.str_regex_automata_length_attempt_threshold(); } z3-z3-4.8.7/src/smt/params/theory_str_params.h000066400000000000000000000112431356505360400212230ustar00rootroot00000000000000/*++ Module Name: theory_str_params.h Abstract: Parameters for string theory plugin Author: Murphy Berzish (mtrberzi) 2016-12-13 Revision History: --*/ #ifndef THEORY_STR_PARAMS_H #define THEORY_STR_PARAMS_H #include "util/params.h" struct theory_str_params { /* * If AssertStrongerArrangements is set to true, * the implications that would normally be asserted during arrangement generation * will instead be asserted as equivalences. * This is a stronger version of the standard axiom. * The Z3str2 axioms can be simulated by setting this to false. */ bool m_StrongArrangements; /* * If AggressiveLengthTesting is true, we manipulate the phase of length tester equalities * to prioritize trying concrete length options over choosing the "more" option. */ bool m_AggressiveLengthTesting; /* * Similarly, if AggressiveValueTesting is true, we manipulate the phase of value tester equalities * to prioritize trying concrete value options over choosing the "more" option. */ bool m_AggressiveValueTesting; /* * If AggressiveUnrollTesting is true, we manipulate the phase of regex unroll tester equalities * to prioritize trying concrete unroll counts over choosing the "more" option. */ bool m_AggressiveUnrollTesting; /* * If UseFastLengthTesterCache is set to true, * length tester terms will not be generated from scratch each time they are needed, * but will be saved in a map and looked up. */ bool m_UseFastLengthTesterCache; /* * If UseFastValueTesterCache is set to true, * value tester terms will not be generated from scratch each time they are needed, * but will be saved in a map and looked up. */ bool m_UseFastValueTesterCache; /* * If StringConstantCache is set to true, * all string constants in theory_str generated from anywhere will be cached and saved. */ bool m_StringConstantCache; /* * If FiniteOverlapModels is set to true, * arrangements that result in overlapping variables will generate a small number of models * to test instead of completely giving up on the case. */ bool m_FiniteOverlapModels; bool m_UseBinarySearch; unsigned m_BinarySearchInitialUpperBound; double m_OverlapTheoryAwarePriority; /* * If RegexAutomata is set to true, * Z3str3 will use automata-based methods to reason about * regular expression constraints. */ bool m_RegexAutomata; /* * RegexAutomata_DifficultyThreshold is the lowest difficulty above which Z3str3 * will not eagerly construct an automaton for a regular expression term. */ unsigned m_RegexAutomata_DifficultyThreshold; /* * RegexAutomata_IntersectionDifficultyThreshold is the lowest difficulty above which Z3str3 * will not eagerly intersect automata to check unsatisfiability. */ unsigned m_RegexAutomata_IntersectionDifficultyThreshold; /* * RegexAutomata_FailedAutomatonThreshold is the number of failed attempts to build an automaton * after which a full automaton (i.e. with no length information) will be built regardless of difficulty. */ unsigned m_RegexAutomata_FailedAutomatonThreshold; /* * RegexAutomaton_FailedIntersectionThreshold is the number of failed attempts to perform automaton * intersection after which intersection will always be performed regardless of difficulty. */ unsigned m_RegexAutomata_FailedIntersectionThreshold; /* * RegexAutomaton_LengthAttemptThreshold is the number of attempts to satisfy length/path constraints * before which we begin checking unsatisfiability of a regex term. */ unsigned m_RegexAutomata_LengthAttemptThreshold; theory_str_params(params_ref const & p = params_ref()): m_StrongArrangements(true), m_AggressiveLengthTesting(false), m_AggressiveValueTesting(false), m_AggressiveUnrollTesting(true), m_UseFastLengthTesterCache(false), m_UseFastValueTesterCache(true), m_StringConstantCache(true), m_FiniteOverlapModels(false), m_UseBinarySearch(false), m_BinarySearchInitialUpperBound(64), m_OverlapTheoryAwarePriority(-0.1), m_RegexAutomata(true), m_RegexAutomata_DifficultyThreshold(1000), m_RegexAutomata_IntersectionDifficultyThreshold(1000), m_RegexAutomata_FailedAutomatonThreshold(10), m_RegexAutomata_FailedIntersectionThreshold(10), m_RegexAutomata_LengthAttemptThreshold(10) { updt_params(p); } void updt_params(params_ref const & p); }; #endif /* THEORY_STR_PARAMS_H */ z3-z3-4.8.7/src/smt/proto_model/000077500000000000000000000000001356505360400163445ustar00rootroot00000000000000z3-z3-4.8.7/src/smt/proto_model/CMakeLists.txt000066400000000000000000000001741356505360400211060ustar00rootroot00000000000000z3_add_component(proto_model SOURCES proto_model.cpp COMPONENT_DEPENDENCIES model rewriter smt_params ) z3-z3-4.8.7/src/smt/proto_model/proto_model.cpp000066400000000000000000000276351356505360400214100ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: proto_model.cpp Abstract: Author: Leonardo de Moura (leonardo) 2007-03-08. Revision History: --*/ #include "ast/ast_pp.h" #include "ast/ast_ll_pp.h" #include "ast/well_sorted.h" #include "ast/array_decl_plugin.h" #include "ast/used_symbols.h" #include "ast/for_each_expr.h" #include "ast/rewriter/var_subst.h" #include "model/model_params.hpp" #include "model/model_v2_pp.h" #include "smt/proto_model/proto_model.h" proto_model::proto_model(ast_manager & m, params_ref const & p): model_core(m), m_eval(*this), m_rewrite(m) { register_factory(alloc(basic_factory, m)); m_user_sort_factory = alloc(user_sort_factory, m); register_factory(m_user_sort_factory); m_model_partial = model_params(p).partial(); } void proto_model::register_aux_decl(func_decl * d, func_interp * fi) { model_core::register_decl(d, fi); m_aux_decls.insert(d); } void proto_model::register_aux_decl(func_decl * d) { m_aux_decls.insert(d); } /** \brief Set new_fi as the new interpretation for f. If f_aux != 0, then assign the old interpretation of f to f_aux. If f_aux == 0, then delete the old interpretation of f. f_aux is marked as a auxiliary declaration. */ void proto_model::reregister_decl(func_decl * f, func_interp * new_fi, func_decl * f_aux) { func_interp * fi = get_func_interp(f); if (fi == nullptr) { register_decl(f, new_fi); } else { if (f_aux != nullptr) { register_decl(f_aux, fi); m_aux_decls.insert(f_aux); } else { dealloc(fi); } m_finterp.insert(f, new_fi); } } expr * proto_model::mk_some_interp_for(func_decl * d) { SASSERT(!has_interpretation(d)); expr * r = get_some_value(d->get_range()); // if t is a function, then it will be the constant function. if (d->get_arity() == 0) { register_decl(d, r); } else { func_interp * new_fi = alloc(func_interp, m, d->get_arity()); new_fi->set_else(r); register_decl(d, new_fi); } return r; } bool proto_model::eval(expr * e, expr_ref & result, bool model_completion) { m_eval.set_model_completion(model_completion); m_eval.set_expand_array_equalities(false); TRACE("model_evaluator", model_v2_pp(tout, *this, true);); try { m_eval(e, result); return true; } catch (model_evaluator_exception & ex) { (void)ex; TRACE("model_evaluator", tout << ex.msg() << "\n";); return false; } } /** \brief Evaluate the expression e in the current model, and store the result in \c result. It returns \c true if succeeded, and false otherwise. If the evaluation fails, then r contains a term that is simplified as much as possible using the interpretations available in the model. When model_completion == true, if the model does not assign an interpretation to a declaration it will build one for it. Moreover, partial functions will also be completed. So, if model_completion == true, the evaluator never fails if it doesn't contain quantifiers. */ /** \brief Replace uninterpreted constants occurring in fi->get_else() by their interpretations. */ void proto_model::cleanup_func_interp(expr_ref_vector& trail, func_interp * fi, func_decl_set & found_aux_fs) { if (!fi->is_partial()) { expr * fi_else = fi->get_else(); fi->set_else(cleanup_expr(trail, fi_else, found_aux_fs)); } } expr* proto_model::cleanup_expr(expr_ref_vector& trail, expr* fi_else, func_decl_set& found_aux_fs) { TRACE("model_bug", tout << "cleaning up:\n" << mk_pp(fi_else, m) << "\n";); trail.reset(); obj_map cache; ptr_buffer todo; ptr_buffer args; todo.push_back(fi_else); expr * a; expr_ref new_t(m); while (!todo.empty()) { a = todo.back(); if (is_uninterp_const(a)) { todo.pop_back(); func_decl * a_decl = to_app(a)->get_decl(); expr * ai = get_const_interp(a_decl); if (ai == nullptr) { ai = get_some_value(a_decl->get_range()); register_decl(a_decl, ai); } cache.insert(a, ai); } else { switch(a->get_kind()) { case AST_APP: { app * t = to_app(a); bool visited = true; args.reset(); for (expr* t_arg : *t) { expr * arg = nullptr; if (!cache.find(t_arg, arg)) { visited = false; todo.push_back(t_arg); } else { args.push_back(arg); } } if (!visited) { continue; } func_decl * f = t->get_decl(); if (m_aux_decls.contains(f)) { TRACE("model_bug", tout << f->get_name() << "\n";); found_aux_fs.insert(f); } new_t = m_rewrite.mk_app(f, args.size(), args.c_ptr()); if (t != new_t.get()) trail.push_back(new_t); todo.pop_back(); cache.insert(t, new_t); break; } default: SASSERT(a != nullptr); cache.insert(a, a); todo.pop_back(); break; } } } VERIFY(cache.find(fi_else, a)); return a; } void proto_model::remove_aux_decls_not_in_set(ptr_vector & decls, func_decl_set const & s) { unsigned j = 0; for (func_decl* f : decls) { if (!m_aux_decls.contains(f) || s.contains(f)) { decls[j++] = f; } } decls.shrink(j); } /** \brief Replace uninterpreted constants occurring in the func_interp's get_else() by their interpretations. */ void proto_model::cleanup() { TRACE("model_bug", model_v2_pp(tout, *this);); func_decl_set found_aux_fs; expr_ref_vector trail(m); for (auto const& kv : m_finterp) { TRACE("model_bug", tout << kv.m_key->get_name() << "\n";); func_interp * fi = kv.m_value; cleanup_func_interp(trail, fi, found_aux_fs); } for (unsigned i = 0; i < m_const_decls.size(); ++i) { func_decl* d = m_const_decls[i]; expr* e = m_interp[d]; expr* r = cleanup_expr(trail, e, found_aux_fs); if (e != r) { register_decl(d, r); } } // TRACE("model_bug", model_v2_pp(tout, *this);); // remove auxiliary declarations that are not used. if (found_aux_fs.size() != m_aux_decls.size()) { remove_aux_decls_not_in_set(m_decls, found_aux_fs); remove_aux_decls_not_in_set(m_func_decls, found_aux_fs); for (func_decl* faux : m_aux_decls) { if (!found_aux_fs.contains(faux)) { TRACE("cleanup_bug", tout << "eliminating " << faux->get_name() << " " << faux->get_ref_count() << "\n";); unregister_decl(faux); } } m_aux_decls.swap(found_aux_fs); } TRACE("model_bug", model_v2_pp(tout, *this);); } value_factory * proto_model::get_factory(family_id fid) { return m_factories.get_plugin(fid); } void proto_model::freeze_universe(sort * s) { SASSERT(m.is_uninterp(s)); m_user_sort_factory->freeze_universe(s); } /** \brief Return the known universe of an uninterpreted sort. */ obj_hashtable const & proto_model::get_known_universe(sort * s) const { return m_user_sort_factory->get_known_universe(s); } ptr_vector const & proto_model::get_universe(sort * s) const { ptr_vector & tmp = const_cast(this)->m_tmp; tmp.reset(); obj_hashtable const & u = get_known_universe(s); for (expr * e : u) { tmp.push_back(e); } return tmp; } unsigned proto_model::get_num_uninterpreted_sorts() const { return m_user_sort_factory->get_num_sorts(); } sort * proto_model::get_uninterpreted_sort(unsigned idx) const { SASSERT(idx < get_num_uninterpreted_sorts()); return m_user_sort_factory->get_sort(idx); } /** \brief Return true if the given sort is uninterpreted and has a finite interpretation in the model. */ bool proto_model::is_finite(sort * s) const { return m.is_uninterp(s) && m_user_sort_factory->is_finite(s); } expr * proto_model::get_some_value(sort * s) { if (m.is_uninterp(s)) { return m_user_sort_factory->get_some_value(s); } else if (value_factory * f = get_factory(s->get_family_id())) { return f->get_some_value(s); } else { // there is no factory for the family id, then assume s is uninterpreted. return m_user_sort_factory->get_some_value(s); } } bool proto_model::get_some_values(sort * s, expr_ref & v1, expr_ref & v2) { if (m.is_uninterp(s)) { return m_user_sort_factory->get_some_values(s, v1, v2); } else if (value_factory * f = get_factory(s->get_family_id())) { return f->get_some_values(s, v1, v2); } else { return false; } } expr * proto_model::get_fresh_value(sort * s) { if (m.is_uninterp(s)) { return m_user_sort_factory->get_fresh_value(s); } else if (value_factory * f = get_factory(s->get_family_id())) { return f->get_fresh_value(s); } else { // Use user_sort_factory if the theory has no support for model construnction. // This is needed when dummy theories are used for arithmetic or arrays. return m_user_sort_factory->get_fresh_value(s); } } void proto_model::register_value(expr * n) { sort * s = m.get_sort(n); if (m.is_uninterp(s)) { m_user_sort_factory->register_value(n); } else { family_id fid = s->get_family_id(); value_factory * f = get_factory(fid); if (f) f->register_value(n); } } void proto_model::compress() { for (func_decl* f : m_func_decls) { func_interp * fi = get_func_interp(f); SASSERT(fi != nullptr); fi->compress(); } } /** \brief Complete the interpretation fi of f if it is partial. If f does not have an interpretation in the given model, then this is a noop. */ void proto_model::complete_partial_func(func_decl * f, bool use_fresh) { func_interp * fi = get_func_interp(f); if (fi && fi->is_partial()) { expr * else_value; if (use_fresh) { else_value = get_fresh_value(f->get_range()); } else { else_value = fi->get_max_occ_result(); } if (else_value == nullptr) else_value = get_some_value(f->get_range()); fi->set_else(else_value); } } /** \brief Set the (else) field of function interpretations... */ void proto_model::complete_partial_funcs(bool use_fresh) { if (m_model_partial) return; // m_func_decls may be "expanded" when we invoke get_some_value. // So, we must not use iterators to traverse it. for (unsigned i = 0; i < m_func_decls.size(); ++i) { complete_partial_func(m_func_decls.get(i), use_fresh); } } model * proto_model::mk_model() { TRACE("proto_model", model_v2_pp(tout << "mk_model\n", *this);); model * mdl = alloc(model, m); for (auto const& kv : m_interp) { mdl->register_decl(kv.m_key, kv.m_value); } for (auto const& kv : m_finterp) { mdl->register_decl(kv.m_key, kv.m_value); m.dec_ref(kv.m_key); } m_finterp.reset(); // m took the ownership of the func_interp's unsigned sz = get_num_uninterpreted_sorts(); for (unsigned i = 0; i < sz; i++) { sort * s = get_uninterpreted_sort(i); TRACE("proto_model", tout << "copying uninterpreted sorts...\n" << mk_pp(s, m) << "\n";); ptr_vector const& buf = get_universe(s); mdl->register_usort(s, buf.size(), buf.c_ptr()); } return mdl; } z3-z3-4.8.7/src/smt/proto_model/proto_model.h000066400000000000000000000066211356505360400210450ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: proto_model.h Abstract: This is the old model object. smt::context used it during model construction and for reporting the model for external consumers. The whole value_factory "business" was due to model construction and unnecessary for external consumers. Future solvers will not use value_factory objects for helping during model construction. After smt::context finishes building the model, it is converted into a new (light) model object. Author: Leonardo de Moura (leonardo) 2007-03-08. Revision History: --*/ #ifndef PROTO_MODEL_H_ #define PROTO_MODEL_H_ #include "model/model_core.h" #include "model/model_evaluator.h" #include "model/value_factory.h" #include "ast/arith_decl_plugin.h" #include "ast/func_decl_dependencies.h" #include "model/model.h" #include "util/params.h" #include "ast/rewriter/th_rewriter.h" class proto_model : public model_core { plugin_manager m_factories; user_sort_factory * m_user_sort_factory; func_decl_set m_aux_decls; ptr_vector m_tmp; model_evaluator m_eval; th_rewriter m_rewrite; bool m_model_partial; expr * mk_some_interp_for(func_decl * d); // Invariant: m_decls subset m_func_decls union m_const_decls union union m_value_decls // Invariant: m_func_decls subset m_decls // Invariant: m_const_decls subset m_decls void remove_aux_decls_not_in_set(ptr_vector & decls, func_decl_set const & s); void cleanup_func_interp(expr_ref_vector& trail, func_interp * fi, func_decl_set & found_aux_fs); expr* cleanup_expr(expr_ref_vector& trail, expr* fi_else, func_decl_set& found_aux_fs); public: proto_model(ast_manager & m, params_ref const & p = params_ref()); ~proto_model() override {} void register_factory(value_factory * f) { m_factories.register_plugin(f); } bool eval(expr * e, expr_ref & result, bool model_completion = false); value_factory * get_factory(family_id fid); expr * get_some_value(sort * s) override; expr * get_fresh_value(sort * s) override; bool get_some_values(sort * s, expr_ref & v1, expr_ref & v2) override; void register_value(expr * n); // // Primitives for building models // void register_aux_decl(func_decl * f, func_interp * fi); void register_aux_decl(func_decl * f); void reregister_decl(func_decl * f, func_interp * new_fi, func_decl * f_aux); void compress(); void cleanup(); // // Primitives for building finite interpretations for uninterpreted sorts, // and retrieving the known universe. // void freeze_universe(sort * s); bool is_finite(sort * s) const; obj_hashtable const & get_known_universe(sort * s) const; ptr_vector const & get_universe(sort * s) const override; unsigned get_num_uninterpreted_sorts() const override; sort * get_uninterpreted_sort(unsigned idx) const override; // // Complete partial function interps // void complete_partial_func(func_decl * f, bool use_fresh); void complete_partial_funcs(bool use_fresh); // // Create final model object. // proto_model is corrupted after that. model * mk_model(); }; typedef ref proto_model_ref; #endif /* PROTO_MODEL_H_ */ z3-z3-4.8.7/src/smt/qi_queue.cpp000066400000000000000000000466651356505360400163630ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: qi_queue.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-06-15. Revision History: --*/ #include "util/warning.h" #include "util/stats.h" #include "ast/ast_pp.h" #include "ast/ast_ll_pp.h" #include "ast/rewriter/var_subst.h" #include "smt/smt_context.h" #include "smt/qi_queue.h" namespace smt { qi_queue::qi_queue(quantifier_manager & qm, context & ctx, qi_params & params): m_qm(qm), m_context(ctx), m(m_context.get_manager()), m_params(params), m_checker(m_context), m_cost_function(m), m_new_gen_function(m), m_parser(m), m_evaluator(m), m_subst(m), m_instances(m) { init_parser_vars(); m_vals.resize(15, 0.0f); } qi_queue::~qi_queue() { } void qi_queue::setup() { TRACE("qi_cost", tout << "qi_cost: " << m_params.m_qi_cost << "\n";); if (!m_parser.parse_string(m_params.m_qi_cost.c_str(), m_cost_function)) { // it is not reasonable to abort here during the creation of smt::context just because an invalid option was provided. // throw default_exception("invalid cost function %s", m_params.m_qi_cost.c_str()); // using warning message instead warning_msg("invalid cost function '%s', switching to default one", m_params.m_qi_cost.c_str()); // Trying again with default function VERIFY(m_parser.parse_string("(+ weight generation)", m_cost_function)); } if (!m_parser.parse_string(m_params.m_qi_new_gen.c_str(), m_new_gen_function)) { // See comment above // throw default_exception("invalid new-gen function %s", m_params.m_qi_new_gen.c_str()); warning_msg("invalid new_gen function '%s', switching to default one", m_params.m_qi_new_gen.c_str()); VERIFY(m_parser.parse_string("cost", m_new_gen_function)); } m_eager_cost_threshold = m_params.m_qi_eager_threshold; } void qi_queue::init_parser_vars() { #define COST 14 m_parser.add_var("cost"); #define MIN_TOP_GENERATION 13 m_parser.add_var("min_top_generation"); #define MAX_TOP_GENERATION 12 m_parser.add_var("max_top_generation"); #define INSTANCES 11 m_parser.add_var("instances"); #define SIZE 10 m_parser.add_var("size"); #define DEPTH 9 m_parser.add_var("depth"); #define GENERATION 8 m_parser.add_var("generation"); #define QUANT_GENERATION 7 m_parser.add_var("quant_generation"); #define WEIGHT 6 m_parser.add_var("weight"); #define VARS 5 m_parser.add_var("vars"); #define PATTERN_WIDTH 4 m_parser.add_var("pattern_width"); #define TOTAL_INSTANCES 3 m_parser.add_var("total_instances"); #define SCOPE 2 m_parser.add_var("scope"); #define NESTED_QUANTIFIERS 1 m_parser.add_var("nested_quantifiers"); #define CS_FACTOR 0 m_parser.add_var("cs_factor"); } quantifier_stat * qi_queue::set_values(quantifier * q, app * pat, unsigned generation, unsigned min_top_generation, unsigned max_top_generation, float cost) { quantifier_stat * stat = m_qm.get_stat(q); m_vals[COST] = cost; m_vals[MIN_TOP_GENERATION] = static_cast(min_top_generation); m_vals[MAX_TOP_GENERATION] = static_cast(max_top_generation); m_vals[INSTANCES] = static_cast(stat->get_num_instances_curr_branch()); m_vals[SIZE] = static_cast(stat->get_size()); m_vals[DEPTH] = static_cast(stat->get_depth()); m_vals[GENERATION] = static_cast(generation); m_vals[QUANT_GENERATION] = static_cast(stat->get_generation()); m_vals[WEIGHT] = static_cast(q->get_weight()); m_vals[VARS] = static_cast(q->get_num_decls()); m_vals[PATTERN_WIDTH] = pat ? static_cast(pat->get_num_args()) : 1.0f; m_vals[TOTAL_INSTANCES] = static_cast(stat->get_num_instances_curr_search()); m_vals[SCOPE] = static_cast(m_context.get_scope_level()); m_vals[NESTED_QUANTIFIERS] = static_cast(stat->get_num_nested_quantifiers()); m_vals[CS_FACTOR] = static_cast(stat->get_case_split_factor()); TRACE("qi_queue_detail", for (unsigned i = 0; i < m_vals.size(); i++) { tout << m_vals[i] << " "; } tout << "\n";); return stat; } float qi_queue::get_cost(quantifier * q, app * pat, unsigned generation, unsigned min_top_generation, unsigned max_top_generation) { quantifier_stat * stat = set_values(q, pat, generation, min_top_generation, max_top_generation, 0); float r = m_evaluator(m_cost_function, m_vals.size(), m_vals.c_ptr()); stat->update_max_cost(r); return r; } unsigned qi_queue::get_new_gen(quantifier * q, unsigned generation, float cost) { // max_top_generation and min_top_generation are not available for computing inc_gen set_values(q, nullptr, generation, 0, 0, cost); float r = m_evaluator(m_new_gen_function, m_vals.size(), m_vals.c_ptr()); return std::max(generation + 1, static_cast(r)); } void qi_queue::insert(fingerprint * f, app * pat, unsigned generation, unsigned min_top_generation, unsigned max_top_generation) { quantifier * q = static_cast(f->get_data()); float cost = get_cost(q, pat, generation, min_top_generation, max_top_generation); TRACE("qi_queue_detail", tout << "new instance of " << q->get_qid() << ", weight " << q->get_weight() << ", generation: " << generation << ", scope_level: " << m_context.get_scope_level() << ", cost: " << cost << "\n"; for (unsigned i = 0; i < f->get_num_args(); i++) { tout << "#" << f->get_arg(i)->get_owner_id() << " d:" << f->get_arg(i)->get_owner()->get_depth() << " "; } tout << "\n";); TRACE("new_entries_bug", tout << "[qi:insert]\n";); m_new_entries.push_back(entry(f, cost, generation)); } void qi_queue::instantiate() { unsigned since_last_check = 0; for (entry & curr : m_new_entries) { fingerprint * f = curr.m_qb; quantifier * qa = static_cast(f->get_data()); if (curr.m_cost <= m_eager_cost_threshold) { instantiate(curr); } else if (m_params.m_qi_promote_unsat && m_checker.is_unsat(qa->get_expr(), f->get_num_args(), f->get_args())) { // do not delay instances that produce a conflict. TRACE("qi_unsat", tout << "promoting instance that produces a conflict\n" << mk_pp(qa, m) << "\n";); instantiate(curr); } else { TRACE("qi_queue", tout << "delaying quantifier instantiation... " << f << "\n" << mk_pp(qa, m) << "\ncost: " << curr.m_cost << "\n";); m_delayed_entries.push_back(curr); } // Periodically check if we didn't run out of time/memory. if (since_last_check++ > 100) { if (m_context.resource_limits_exceeded()) { break; } if (m_context.get_cancel_flag()) { break; } since_last_check = 0; } } m_new_entries.reset(); TRACE("new_entries_bug", tout << "[qi:instantiate]\n";); } void qi_queue::display_instance_profile(fingerprint * f, quantifier * q, unsigned num_bindings, enode * const * bindings, unsigned proof_id, unsigned generation) { if (m.has_trace_stream()) { m.trace_stream() << "[instance] "; m.trace_stream() << static_cast(f); if (m.proofs_enabled()) m.trace_stream() << " #" << proof_id; m.trace_stream() << " ; " << generation; m.trace_stream() << "\n"; } } void qi_queue::instantiate(entry & ent) { fingerprint * f = ent.m_qb; quantifier * q = static_cast(f->get_data()); unsigned generation = ent.m_generation; unsigned num_bindings = f->get_num_args(); enode * const * bindings = f->get_args(); ent.m_instantiated = true; TRACE("qi_queue_profile", tout << q->get_qid() << ", gen: " << generation << " " << *f << " cost: " << ent.m_cost << "\n";); if (m_checker.is_sat(q->get_expr(), num_bindings, bindings)) { TRACE("checker", tout << "instance already satisfied\n";); return; } expr_ref instance(m); m_subst(q, num_bindings, bindings, instance); TRACE("qi_queue", tout << "new instance:\n" << mk_pp(instance, m) << "\n";); TRACE("qi_queue_instance", tout << "new instance:\n" << mk_pp(instance, m) << "\n";); expr_ref s_instance(m); proof_ref pr(m); m_context.get_rewriter()(instance, s_instance, pr); TRACE("qi_queue_bug", tout << "new instance after simplification:\n" << s_instance << "\n";); if (m.is_true(s_instance)) { TRACE("checker", tout << "reduced to true, before:\n" << mk_ll_pp(instance, m);); if (m.has_trace_stream()) { display_instance_profile(f, q, num_bindings, bindings, pr ? pr->get_id() : 0, generation); m.trace_stream() << "[end-of-instance]\n"; } return; } quantifier_stat * stat = m_qm.get_stat(q); stat->inc_num_instances(); if (stat->get_num_instances() % m_params.m_qi_profile_freq == 0) { m_qm.display_stats(verbose_stream(), q); } expr_ref lemma(m); if (m.is_or(s_instance)) { ptr_vector args; args.push_back(m.mk_not(q)); args.append(to_app(s_instance)->get_num_args(), to_app(s_instance)->get_args()); lemma = m.mk_or(args.size(), args.c_ptr()); } else if (m.is_false(s_instance)) { lemma = m.mk_not(q); } else if (m.is_true(s_instance)) { lemma = s_instance; } else { lemma = m.mk_or(m.mk_not(q), s_instance); } m_instances.push_back(lemma); proof_ref pr1(m); unsigned proof_id = 0; if (m.proofs_enabled()) { expr_ref_vector bindings_e(m); for (unsigned i = 0; i < num_bindings; ++i) { bindings_e.push_back(bindings[i]->get_owner()); } app * bare_lemma = m.mk_or(m.mk_not(q), instance); proof * qi_pr = m.mk_quant_inst(bare_lemma, num_bindings, bindings_e.c_ptr()); proof_id = qi_pr->get_id(); if (bare_lemma == lemma) { pr1 = qi_pr; } else if (instance == s_instance) { proof * rw = m.mk_rewrite(bare_lemma, lemma); pr1 = m.mk_modus_ponens(qi_pr, rw); } else { app * bare_s_lemma = m.mk_or(m.mk_not(q), s_instance); proof * prs[1] = { pr.get() }; proof * cg = m.mk_congruence(bare_lemma, bare_s_lemma, 1, prs); proof * rw = m.mk_rewrite(bare_s_lemma, lemma); proof * tr = m.mk_transitivity(cg, rw); pr1 = m.mk_modus_ponens(qi_pr, tr); } m_instances.push_back(pr1); } TRACE("qi_queue", tout << mk_pp(lemma, m) << "\n#" << lemma->get_id() << ":=\n" << mk_ll_pp(lemma, m);); m_stats.m_num_instances++; unsigned gen = get_new_gen(q, generation, ent.m_cost); display_instance_profile(f, q, num_bindings, bindings, proof_id, gen); m_context.internalize_instance(lemma, pr1, gen); if (f->get_def()) { m_context.internalize(f->get_def(), true); } TRACE_CODE({ static unsigned num_useless = 0; if (m.is_or(lemma)) { app * n = to_app(lemma); bool has_unassigned = false; expr * true_child = 0; for (unsigned i = 0; i < n->get_num_args(); i++) { expr * arg = n->get_arg(i); switch(m_context.get_assignment(arg)) { case l_undef: has_unassigned = true; break; case l_true: true_child = arg; break; default: break; } } if (true_child && has_unassigned) { TRACE("qi_queue_profile_detail", tout << "missed:\n" << mk_ll_pp(s_instance, m) << "\n#" << true_child->get_id() << "\n";); num_useless++; if (num_useless % 10 == 0) { TRACE("qi_queue_profile", tout << "num useless: " << num_useless << "\n";); } } } }); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; } void qi_queue::push_scope() { TRACE("new_entries_bug", tout << "[qi:push-scope]\n";); m_scopes.push_back(scope()); SASSERT(m_new_entries.empty()); scope & s = m_scopes.back(); s.m_delayed_entries_lim = m_delayed_entries.size(); s.m_instances_lim = m_instances.size(); s.m_instantiated_trail_lim = m_instantiated_trail.size(); } void qi_queue::pop_scope(unsigned num_scopes) { unsigned new_lvl = m_scopes.size() - num_scopes; scope & s = m_scopes[new_lvl]; unsigned old_sz = s.m_instantiated_trail_lim; unsigned sz = m_instantiated_trail.size(); for (unsigned i = old_sz; i < sz; i++) m_delayed_entries[m_instantiated_trail[i]].m_instantiated = false; m_instantiated_trail.shrink(old_sz); m_delayed_entries.shrink(s.m_delayed_entries_lim); m_instances.shrink(s.m_instances_lim); m_new_entries.reset(); m_scopes.shrink(new_lvl); TRACE("new_entries_bug", tout << "[qi:pop-scope]\n";); } void qi_queue::reset() { m_new_entries.reset(); m_delayed_entries.reset(); m_instances.reset(); m_scopes.reset(); } void qi_queue::init_search_eh() { m_subst.reset(); } bool qi_queue::final_check_eh() { TRACE("qi_queue", display_delayed_instances_stats(tout); tout << "lazy threshold: " << m_params.m_qi_lazy_threshold << ", scope_level: " << m_context.get_scope_level() << "\n";); if (m_params.m_qi_conservative_final_check) { bool init = false; float min_cost = 0.0; unsigned sz = m_delayed_entries.size(); for (unsigned i = 0; i < sz; i++) { entry & e = m_delayed_entries[i]; TRACE("qi_queue", tout << e.m_qb << ", cost: " << e.m_cost << ", instantiated: " << e.m_instantiated << "\n";); if (!e.m_instantiated && e.m_cost <= m_params.m_qi_lazy_threshold && (!init || e.m_cost < min_cost)) { init = true; min_cost = e.m_cost; } } TRACE("qi_queue_min_cost", tout << "min_cost: " << min_cost << ", scope_level: " << m_context.get_scope_level() << "\n";); bool result = true; for (unsigned i = 0; i < sz; i++) { entry & e = m_delayed_entries[i]; TRACE("qi_queue", tout << e.m_qb << ", cost: " << e.m_cost << ", instantiated: " << e.m_instantiated << "\n";); if (!e.m_instantiated && e.m_cost <= min_cost) { TRACE("qi_queue", tout << "lazy quantifier instantiation...\n" << mk_pp(static_cast(e.m_qb->get_data()), m) << "\ncost: " << e.m_cost << "\n";); result = false; m_instantiated_trail.push_back(i); m_stats.m_num_lazy_instances++; instantiate(e); } } return result; } bool result = true; for (unsigned i = 0; i < m_delayed_entries.size(); i++) { entry & e = m_delayed_entries[i]; TRACE("qi_queue", tout << e.m_qb << ", cost: " << e.m_cost << ", instantiated: " << e.m_instantiated << "\n";); if (!e.m_instantiated && e.m_cost <= m_params.m_qi_lazy_threshold) { TRACE("qi_queue", tout << "lazy quantifier instantiation...\n" << mk_pp(static_cast(e.m_qb->get_data()), m) << "\ncost: " << e.m_cost << "\n";); result = false; m_instantiated_trail.push_back(i); m_stats.m_num_lazy_instances++; instantiate(e); } } return result; } struct delayed_qa_info { unsigned m_num; float m_min_cost; float m_max_cost; delayed_qa_info():m_num(0), m_min_cost(0.0f), m_max_cost(0.0f) {} }; void qi_queue::display_delayed_instances_stats(std::ostream & out) const { obj_map qa2info; ptr_vector qas; for (entry const & e : m_delayed_entries) { if (e.m_instantiated) continue; quantifier * qa = static_cast(e.m_qb->get_data()); delayed_qa_info info; if (qa2info.find(qa, info)) { info.m_num++; info.m_min_cost = std::min(info.m_min_cost, e.m_cost); info.m_max_cost = std::min(info.m_max_cost, e.m_cost); } else { qas.push_back(qa); info.m_num = 1; info.m_min_cost = e.m_cost; info.m_max_cost = e.m_cost; } qa2info.insert(qa, info); } for (quantifier * qa : qas) { delayed_qa_info info; qa2info.find(qa, info); out << qa->get_qid() << ": " << info.m_num << " [" << info.m_min_cost << ", " << info.m_max_cost << "]\n"; } } void qi_queue::get_min_max_costs(float & min, float & max) const { min = 0.0f; max = 0.0f; bool found = false; for (unsigned i = 0; i < m_delayed_entries.size(); i++) { if (!m_delayed_entries[i].m_instantiated) { float c = m_delayed_entries[i].m_cost; if (found) { min = std::min(min, c); max = std::max(max, c); } else { found = true; min = c; max = c; } } } } void qi_queue::collect_statistics(::statistics & st) const { st.update("quant instantiations", m_stats.m_num_instances); st.update("lazy quant instantiations", m_stats.m_num_lazy_instances); st.update("missed quant instantiations", m_delayed_entries.size()); float min, max; get_min_max_costs(min, max); st.update("min missed qa cost", min); st.update("max missed qa cost", max); #if 0 if (m_params.m_qi_profile) { out << "missed/delayed quantifier instances:\n"; display_delayed_instances_stats(out); } #endif } }; z3-z3-4.8.7/src/smt/qi_queue.h000066400000000000000000000071461356505360400160170ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: qi_queue.h Abstract: Author: Leonardo de Moura (leonardo) 2008-06-15. Revision History: --*/ #ifndef QI_QUEUE_H_ #define QI_QUEUE_H_ #include "ast/ast.h" #include "smt/smt_quantifier_stat.h" #include "smt/smt_checker.h" #include "smt/smt_quantifier.h" #include "smt/params/qi_params.h" #include "smt/fingerprints.h" #include "parsers/util/cost_parser.h" #include "smt/cost_evaluator.h" #include "smt/cached_var_subst.h" #include "util/statistics.h" namespace smt { class context; struct qi_queue_stats { unsigned m_num_instances, m_num_lazy_instances; void reset() { memset(this, 0, sizeof(qi_queue_stats)); } qi_queue_stats() { reset(); } }; class qi_queue { quantifier_manager & m_qm; context & m_context; ast_manager & m; qi_params & m_params; qi_queue_stats m_stats; checker m_checker; expr_ref m_cost_function; expr_ref m_new_gen_function; cost_parser m_parser; cost_evaluator m_evaluator; cached_var_subst m_subst; svector m_vals; double m_eager_cost_threshold; struct entry { fingerprint * m_qb; float m_cost; unsigned m_generation:31; unsigned m_instantiated:1; entry(fingerprint * f, float c, unsigned g):m_qb(f), m_cost(c), m_generation(g), m_instantiated(false) {} }; svector m_new_entries; svector m_delayed_entries; expr_ref_vector m_instances; unsigned_vector m_instantiated_trail; struct scope { unsigned m_delayed_entries_lim; unsigned m_instances_lim; unsigned m_instantiated_trail_lim; }; svector m_scopes; void init_parser_vars(); quantifier_stat * set_values(quantifier * q, app * pat, unsigned generation, unsigned min_top_generation, unsigned max_top_generation, float cost); float get_cost(quantifier * q, app * pat, unsigned generation, unsigned min_top_generation, unsigned max_top_generation); unsigned get_new_gen(quantifier * q, unsigned generation, float cost); void instantiate(entry & ent); void get_min_max_costs(float & min, float & max) const; void display_instance_profile(fingerprint * f, quantifier * q, unsigned num_bindings, enode * const * bindings, unsigned proof_id, unsigned generation); public: qi_queue(quantifier_manager & qm, context & ctx, qi_params & params); ~qi_queue(); void setup(); /** \brief Insert a new quantifier in the queue, f contains the quantifier and bindings. f->get_data() is the quantifier. */ void insert(fingerprint * f, app * pat, unsigned generation, unsigned min_top_generation, unsigned max_top_generation); void instantiate(); bool has_work() const { return !m_new_entries.empty(); } void init_search_eh(); bool final_check_eh(); void push_scope(); void pop_scope(unsigned num_scopes); void reset(); void display_delayed_instances_stats(std::ostream & out) const; void collect_statistics(::statistics & st) const; }; }; #endif /* QI_QUEUE_H_ */ z3-z3-4.8.7/src/smt/smt2_extra_cmds.cpp000066400000000000000000000026731356505360400176330ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: smt2_extra_cmds.cpp Abstract: Additional SMT-specific commands. Author: Christoph (cwinter) 2017-01-16 Notes: --*/ #include "cmd_context/cmd_context.h" #include "parsers/smt2/smt2parser.h" #include "smt/smt2_extra_cmds.h" class include_cmd : public cmd { char const * m_filename; public: include_cmd() : cmd("include"), m_filename(nullptr) {} char const * get_usage() const override { return ""; } char const * get_descr(cmd_context & ctx) const override { return "include a file"; } unsigned get_arity() const override { return 1; } cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { return CPK_STRING; } void set_next_arg(cmd_context & ctx, char const * val) override { m_filename = val; } void failure_cleanup(cmd_context & ctx) override {} void execute(cmd_context & ctx) override { std::ifstream is(m_filename); if (is.bad() || is.fail()) throw cmd_exception(std::string("failed to open file '") + m_filename + "'"); parse_smt2_commands(ctx, is, false, params_ref(), m_filename); is.close(); } void prepare(cmd_context & ctx) override { reset(ctx); } void reset(cmd_context & ctx) override { m_filename = nullptr; } void finalize(cmd_context & ctx) override { reset(ctx); } }; void install_smt2_extra_cmds(cmd_context & ctx) { ctx.insert(alloc(include_cmd)); } z3-z3-4.8.7/src/smt/smt2_extra_cmds.h000066400000000000000000000005351356505360400172730ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: smt2_extra_cmds.h Abstract: Additional SMT-specific commands. Author: Christoph (cwinter) 2017-01-16 Notes: --*/ #ifndef SMT2_EXTRA_CMDS_H_ #define SMT2_EXTRA_CMDS_H_ class cmd_context; void install_smt2_extra_cmds(cmd_context & ctx); #endif /* SMT2_EXTRA_CMDS_H_ */ z3-z3-4.8.7/src/smt/smt_almost_cg_table.cpp000066400000000000000000000064511356505360400205350ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_almost_cg_table.cpp Abstract: Author: Leonardo de Moura (leonardo) 2009-03-06. Revision History: --*/ #include "smt/smt_almost_cg_table.h" namespace smt { inline unsigned almost_cg_table::cg_hash::arg_hash(enode * n, unsigned idx) const { enode * arg = n->get_arg(idx)->get_root(); if (arg == m_r1 || arg == m_r2) return 17; return arg->hash(); } unsigned almost_cg_table::cg_hash::operator()(enode * n) const { unsigned num_args = n->get_num_args(); unsigned kind_hash = n->get_decl_id(); if (num_args == 1) return kind_hash; unsigned a = 0x9e3779b9; unsigned b = 0x9e3779b9; unsigned c = 11; switch (num_args) { case 2: a += kind_hash; b += arg_hash(n, 0); c += arg_hash(n, 1); mix(a, b, c); return c; case 3: a += kind_hash; b += arg_hash(n, 0); c += arg_hash(n, 1); mix(a, b, c); c += arg_hash(n, 1); mix(a, b, c); return c; default: while (num_args >= 3) { num_args--; a += arg_hash(n, num_args); num_args--; b += arg_hash(n, num_args); num_args--; c += arg_hash(n, num_args); mix(a, b, c); } a += kind_hash; switch (num_args) { case 2: b += arg_hash(n, 1); Z3_fallthrough; case 1: c += arg_hash(n, 0); } mix(a, b, c); return c; } } bool almost_cg_table::cg_eq::operator()(enode * n1, enode * n2) const { if (n1->get_owner()->get_decl() != n2->get_owner()->get_decl()) return false; unsigned num_args = n1->get_num_args(); if (num_args != n2->get_num_args()) return false; for (unsigned j = 0; j < num_args; j++) { enode * arg1 = n1->get_arg(j)->get_root(); enode * arg2 = n2->get_arg(j)->get_root(); if (arg1 == arg2) continue; if ((arg1 == m_r1 || arg1 == m_r2) && (arg2 == m_r1 || arg2 == m_r2)) continue; return false; } return true; } almost_cg_table::almost_cg_table(enode * r1, enode * r2): m_r1(r1), m_r2(r2), m_table(cg_hash(m_r1, m_r2), cg_eq(m_r1, m_r2)) { } void almost_cg_table::reset() { m_region.reset(); m_table.reset(); } void almost_cg_table::insert(enode * n) { table::entry * entry = m_table.find_core(n); if (entry == nullptr) { list * new_lst = new (m_region) list(n, nullptr); m_table.insert(n, new_lst); } else { list * new_lst = new (m_region) list(n, entry->get_data().m_value); entry->get_data().m_value = new_lst; } } list * almost_cg_table::find(enode * n) { list * result = nullptr; m_table.find(n, result); return result; } }; z3-z3-4.8.7/src/smt/smt_almost_cg_table.h000066400000000000000000000035161356505360400202010ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_almost_cg_table.h Abstract: Author: Leonardo de Moura (leonardo) 2009-03-06. Revision History: --*/ #ifndef SMT_ALMOST_CG_TABLE_H_ #define SMT_ALMOST_CG_TABLE_H_ #include "smt/smt_enode.h" #include "util/map.h" namespace smt { /** \brief An index for detecting 'almost' congruences. We say (f t_1 ... t_n) is almost congruent to (f s_1 ... s_n) with respect to (r1,r2) iff for all j in [1,n] j t_j = s_j or (t_j = r1 and s_j = r1) or (t_j = r1 and s_j = r2) or (t_j = r2 and s_j = r1) or (t_j = r2 and s_j = r2) This index is used to speedup is_ext_diseq. */ class almost_cg_table { struct cg_hash { enode * & m_r1; enode * & m_r2; unsigned arg_hash(enode * n, unsigned idx) const; public: cg_hash(enode * & r1, enode * & r2):m_r1(r1), m_r2(r2) {} unsigned operator()(enode * n) const; }; struct cg_eq { enode * & m_r1; enode * & m_r2; public: cg_eq(enode * & r1, enode * & r2):m_r1(r1), m_r2(r2) {} bool operator()(enode * n1, enode * n2) const; }; typedef map *, cg_hash, cg_eq> table; region m_region; enode * m_r1; enode * m_r2; table m_table; public: almost_cg_table(enode * r1 = nullptr, enode * r2 = nullptr); void reset(enode * r1, enode * r2) { m_r1 = r1->get_root(); m_r2 = r2->get_root(); reset(); } void reset(); void insert(enode *); void erase(enode * n) { m_table.erase(n); } list * find(enode *); bool empty() const { return m_table.empty(); } }; }; #endif /* SMT_ALMOST_CG_TABLE_H_ */ z3-z3-4.8.7/src/smt/smt_arith_value.cpp000066400000000000000000000115031356505360400177130ustar00rootroot00000000000000/*++ Copyright (c) 2018 Microsoft Corporation Module Name: smt_arith_value.cpp Abstract: Utility to extract arithmetic values from context. Author: Nikolaj Bjorner (nbjorner) 2018-12-08. Revision History: --*/ #include "ast/ast_pp.h" #include "smt/smt_arith_value.h" namespace smt { arith_value::arith_value(ast_manager& m): m_ctx(nullptr), m(m), a(m) {} 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; bool found = false; bool is_strict1; rational lo1; do { 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; } next = next->get_next(); } while (n != next); CTRACE("arith_value", !found, tout << "value not found for " << mk_pp(e, m_ctx->get_manager()) << "\n";); return found; } 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; bool found = false, is_strict1; rational up1; do { 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; } next = next->get_next(); } while (n != next); CTRACE("arith_value", !found, tout << "value not found for " << mk_pp(e, m_ctx->get_manager()) << "\n";); return found; } 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); TRACE("arith_value", tout << "value not found for " << mk_pp(e, m_ctx->get_manager()) << "\n";); 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); TRACE("arith_value", tout << "value not found for " << mk_pp(e, m_ctx->get_manager()) << "\n";); 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* 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; TRACE("arith_value", tout << "value not found for " << mk_pp(e, m_ctx->get_manager()) << "\n";); 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 (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); TRACE("arith_value", tout << "value not found for " << mk_pp(e, m_ctx->get_manager()) << "\n";); return false; } final_check_status arith_value::final_check() { family_id afid = a.get_family_id(); theory * th = m_ctx->get_theory(afid); return th->final_check_eh(); } }; z3-z3-4.8.7/src/smt/smt_arith_value.h000066400000000000000000000021711356505360400173610ustar00rootroot00000000000000 /*++ Copyright (c) 2018 Microsoft Corporation Module Name: smt_arith_value.h Abstract: Utility to extract arithmetic values from context. Author: Nikolaj Bjorner (nbjorner) 2018-12-08. Revision History: --*/ #pragma once #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; ast_manager& m; arith_util a; theory_mi_arith* m_tha; theory_i_arith* m_thi; theory_lra* m_thr; public: 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(); }; }; z3-z3-4.8.7/src/smt/smt_b_justification.h000066400000000000000000000056551356505360400202440ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_b_justification.h Abstract: Author: Leonardo de Moura (leonardo) 2008-02-19. Revision History: --*/ #ifndef SMT_B_JUSTIFICATION_H_ #define SMT_B_JUSTIFICATION_H_ #include "smt/smt_literal.h" #include "smt/smt_clause.h" namespace smt { /** \brief Proof like object used to track dependencies of boolean propagation. The idea is to reduce the cost of dependency tracking for the most common justifications used during boolean propagation: unit propagation */ class b_justification { void * m_data; public: enum kind { CLAUSE, //!< clause of arbitrary size BIN_CLAUSE, //!< binary clause AXIOM, //!< no justification, it is only use if proof generation is disabled JUSTIFICATION //!< fallback }; b_justification(): m_data(reinterpret_cast(static_cast(AXIOM))) {} b_justification(b_justification const & source): m_data(source.m_data) { } explicit b_justification(clause * c): m_data(TAG(void*, c, CLAUSE)) { } explicit b_justification(literal l): m_data(BOXTAGINT(void*, l.index(), BIN_CLAUSE)) { } explicit b_justification(justification * js): m_data(TAG(void*, js, JUSTIFICATION)) { SASSERT(js); } kind get_kind() const { return static_cast(GET_TAG(m_data)); } clause * get_clause() const { SASSERT(get_kind() == CLAUSE); return UNTAG(clause*, m_data); } justification * get_justification() const { SASSERT(get_kind() == JUSTIFICATION); return UNTAG(justification*, m_data); } literal get_literal() const { SASSERT(get_kind() == BIN_CLAUSE); return to_literal(UNBOXINT(m_data)); } bool operator==(b_justification const & other) const { return m_data == other.m_data; } bool operator!=(b_justification const & other) const { return !operator==(other); } static b_justification mk_axiom() { return b_justification(); } }; const b_justification null_b_justification(static_cast(nullptr)); inline std::ostream& operator<<(std::ostream& out, b_justification::kind k) { switch (k) { case b_justification::CLAUSE: return out << "clause"; case b_justification::BIN_CLAUSE: return out << "bin_clause"; case b_justification::AXIOM: return out << "axiom"; case b_justification::JUSTIFICATION: return out << "theory"; } return out; } typedef std::pair justified_literal; }; #endif /* SMT_B_JUSTIFICATION_H_ */ z3-z3-4.8.7/src/smt/smt_bool_var_data.h000066400000000000000000000076321356505360400176610ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_bool_var_data.h Abstract: Author: Leonardo de Moura (leonardo) 2008-02-25. Revision History: --*/ #ifndef SMT_BOOL_VAR_DATA_H_ #define SMT_BOOL_VAR_DATA_H_ #include "smt/smt_b_justification.h" namespace smt { struct bool_var_data { private: b_justification m_justification; public: unsigned m_scope_lvl:24; //!< scope level of when the variable was assigned. unsigned m_mark:1; unsigned m_assumption:1; unsigned m_phase_available:1; unsigned m_phase:1; private: unsigned m_eq:1; unsigned m_true_first:1; //!< If True, when case splitting try the true phase first. Otherwise, you default phase selection heuristic. unsigned m_enode:1; //!< has enode associated with it. unsigned m_quantifier:1; //!< true if bool var is attached to a quantifier unsigned m_iscope_lvl:23; //!< scope level of when the variable was internalized. unsigned m_atom:1; //!< logical or of m_eq, m_enode, m_quantifier, and m_notify_theory != 0 unsigned m_notify_theory:8; void update_atom_flag() { m_atom = m_eq || m_notify_theory != 0 || m_quantifier || m_enode; } public: unsigned get_intern_level() const { return m_iscope_lvl; } b_justification justification() const { return m_justification; } void set_axiom() { m_justification = b_justification::mk_axiom(); } void set_null_justification() { m_justification = null_b_justification; } void set_justification(b_justification const& j) { m_justification = j; } bool is_atom() const { return m_atom; } theory_id get_theory() const { return m_notify_theory == 0 ? null_theory_id : static_cast(m_notify_theory); } bool is_theory_atom() const { return m_notify_theory != 0; } void set_notify_theory(theory_id thid) { SASSERT(thid > 0 && thid <= 255); m_notify_theory = thid; m_atom = true; } void reset_notify_theory() { m_notify_theory = 0; update_atom_flag(); } bool is_enode() const { return m_enode; } void set_enode_flag() { m_enode = true; m_atom = true; } void reset_enode_flag() { m_enode = false; update_atom_flag(); } bool is_quantifier() const { return m_quantifier; } void set_quantifier_flag() { m_quantifier = true; m_atom = true; } bool is_eq() const { return m_eq; } void set_eq_flag() { m_eq = true; m_atom = true; } void reset_eq_flag() { m_eq = false; update_atom_flag(); } bool try_true_first() const { return m_true_first; } void set_true_first_flag() { m_true_first = true; } void reset_true_first_flag() { m_true_first = false; } void init(unsigned iscope_lvl) { m_justification = null_b_justification; m_scope_lvl = 0; m_mark = false; m_assumption = false; m_phase_available = false; m_phase = false; m_iscope_lvl = iscope_lvl; m_eq = false; m_true_first = false; m_notify_theory = 0; m_enode = false; m_quantifier = false; m_atom = false; } }; }; #endif /* SMT_BOOL_VAR_DATA_H_ */ z3-z3-4.8.7/src/smt/smt_case_split_queue.cpp000066400000000000000000001317571356505360400207600ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_case_split_queue.cpp Abstract: Author: Leonardo de Moura (leonardo) 2009-01-20. Revision History: --*/ #include "smt/smt_context.h" #include "smt/smt_case_split_queue.h" #include "util/warning.h" #include "util/stopwatch.h" #include "ast/for_each_expr.h" #include "ast/ast_pp.h" #include "util/map.h" #include "util/hashtable.h" namespace smt { typedef map > theory_var_priority_map; struct bool_var_act_lt { svector const & m_activity; bool_var_act_lt(svector const & a):m_activity(a) {} bool operator()(bool_var v1, bool_var v2) const { return m_activity[v1] > m_activity[v2]; } }; typedef heap bool_var_act_queue; struct theory_aware_act_lt { svector const & m_activity; theory_var_priority_map const & m_theory_var_priority; theory_aware_act_lt(svector const & act, theory_var_priority_map const & a):m_activity(act),m_theory_var_priority(a) {} bool operator()(bool_var v1, bool_var v2) const { double p_v1, p_v2; if (!m_theory_var_priority.find(v1, p_v1)) { p_v1 = 0.0; } if (!m_theory_var_priority.find(v2, p_v2)) { p_v2 = 0.0; } // add clause activity p_v1 += m_activity[v1]; p_v2 += m_activity[v2]; return p_v1 > p_v2; } }; typedef heap theory_aware_act_queue; /** \brief Case split queue based on activity and random splits. */ class act_case_split_queue : public case_split_queue { protected: context & m_context; smt_params & m_params; bool_var_act_queue m_queue; public: act_case_split_queue(context & ctx, smt_params & p): m_context(ctx), m_params(p), m_queue(1024, bool_var_act_lt(ctx.get_activity_vector())) { } void activity_increased_eh(bool_var v) override { if (m_queue.contains(v)) 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)); m_queue.insert(v); } void del_var_eh(bool_var v) override { if (m_queue.contains(v)) m_queue.erase(v); } void unassign_var_eh(bool_var v) override { if (!m_queue.contains(v)) m_queue.insert(v); } void relevant_eh(expr * n) override {} void init_search_eh() override {} void end_search_eh() override {} void reset() override { m_queue.reset(); } void push_scope() override {} void pop_scope(unsigned num_scopes) override {} void next_case_split(bool_var & next, lbool & phase) override { phase = l_undef; if (m_context.get_random_value() < static_cast(m_params.m_random_var_freq * random_gen::max_value())) { next = m_context.get_random_value() % m_context.get_num_b_internalized(); TRACE("random_split", tout << "next: " << next << " get_assignment(next): " << m_context.get_assignment(next) << "\n";); if (m_context.get_assignment(next) == l_undef) return; } while (!m_queue.empty()) { next = m_queue.erase_min(); if (m_context.get_assignment(next) == l_undef) return; } next = null_bool_var; } void display(std::ostream & out) override { bool first = true; for (unsigned v : m_queue) { if (m_context.get_assignment(v) == l_undef) { if (first) { out << "remaining case-splits:\n"; first = false; } out << "#" << m_context.bool_var2expr(v)->get_id() << " "; } } if (!first) out << "\n"; } ~act_case_split_queue() override {}; }; /** \brief Similar to dact_case_split_queue, but delay case splits created during the search. */ class dact_case_split_queue : public act_case_split_queue { bool_var_act_queue m_delayed_queue; public: dact_case_split_queue(context & ctx, smt_params & p): act_case_split_queue(ctx, p), m_delayed_queue(1024, bool_var_act_lt(ctx.get_activity_vector())) { } void activity_increased_eh(bool_var v) override { act_case_split_queue::activity_increased_eh(v); if (m_queue.contains(v)) m_queue.decreased(v); if (m_delayed_queue.contains(v)) 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); SASSERT(!m_delayed_queue.contains(v)); SASSERT(!m_queue.contains(v)); if (m_context.is_searching()) m_delayed_queue.insert(v); else m_queue.insert(v); } void del_var_eh(bool_var v) override { act_case_split_queue::del_var_eh(v); if (m_delayed_queue.contains(v)) m_delayed_queue.erase(v); } void relevant_eh(expr * n) override {} void init_search_eh() override {} void end_search_eh() override {} void reset() override { act_case_split_queue::reset(); m_delayed_queue.reset(); } void push_scope() override {} void pop_scope(unsigned num_scopes) override {} void next_case_split(bool_var & next, lbool & phase) override { act_case_split_queue::next_case_split(next, phase); if (next != null_bool_var) return; m_queue.swap(m_delayed_queue); SASSERT(m_delayed_queue.empty()); while (!m_queue.empty()) { next = m_queue.erase_min(); if (m_context.get_assignment(next) == l_undef) return; } next = null_bool_var; } }; /** \brief Case split queue based on activity and random splits. */ class cact_case_split_queue : public act_case_split_queue { obj_map m_cache; expr_ref_vector m_cache_domain; public: cact_case_split_queue(context & ctx, smt_params & p): act_case_split_queue(ctx, p), m_cache_domain(ctx.get_manager()) { } void mk_var_eh(bool_var v) override { expr * n = m_context.bool_var2expr(v); double act; if (n && m_cache.find(n, act)) m_context.set_activity(v, act); act_case_split_queue::mk_var_eh(v); } void del_var_eh(bool_var v) override { if (m_context.is_searching()) { double act = m_context.get_activity(v); if (act > 0.0) { expr * n = m_context.bool_var2expr(v); if (n) { m_cache.insert(n, act); m_cache_domain.push_back(n); } } } act_case_split_queue::del_var_eh(v); } void init_search_eh() override { m_cache.reset(); m_cache_domain.reset(); } void end_search_eh() override {} void reset() override { init_search_eh(); } }; static bool has_child_assigned_to(context & ctx, app * parent, lbool val, expr * & undef_child, unsigned order) { ptr_vector undef_children; bool found_undef = false; unsigned num_args = parent->get_num_args(); for (unsigned i = 0; i < num_args; i++) { expr * arg = parent->get_arg(i); lbool arg_val = ctx.get_assignment(arg); if (arg_val == val) return true; if (found_undef && order == 0) continue; if (arg_val == l_undef) { if (order == 1) undef_children.push_back(arg); else undef_child = arg; found_undef = true; } } if (order == 1) { if (undef_children.empty()) { // a bug? } else if (undef_children.size() == 1) { undef_child = undef_children[0]; } else { undef_child = undef_children[ctx.get_random_value() % undef_children.size()]; } } return false; } /** \brief Case split queue based on relevancy propagation */ class rel_case_split_queue : public case_split_queue { struct scope { unsigned m_queue_trail; unsigned m_head_old; unsigned m_queue2_trail; unsigned m_head2_old; }; typedef int_hashtable > bool_var_set; context & m_context; smt_params &m_params; ast_manager & m_manager; ptr_vector m_queue; unsigned m_head; int m_bs_num_bool_vars; //!< Number of boolean variable before starting to search. ptr_vector m_queue2; unsigned m_head2; svector m_scopes; public: rel_case_split_queue(context & ctx, smt_params & p): m_context(ctx), m_params(p), m_manager(ctx.get_manager()), m_head(0), m_bs_num_bool_vars(UINT_MAX), m_head2(0) { } 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 {} void unassign_var_eh(bool_var v) override {} void relevant_eh(expr * n) override { if (!m_manager.is_bool(n)) return; bool is_or = m_manager.is_or(n); bool intern = m_context.b_internalized(n); if (!intern && !is_or) return; bool_var var = null_bool_var; if (intern) { var = m_context.get_bool_var(n); SASSERT(var != null_bool_var); bool is_and = m_manager.is_and(n); lbool val = m_context.get_assignment(var); if (!( val == l_undef || // n was not assigned yet (is_or && val == l_true) || // need to justify a child (is_and && val == l_false) // need to justify a child )) return; } if (!intern && m_context.is_searching()) { SASSERT(is_or); m_queue2.push_back(n); return; } if (var < m_bs_num_bool_vars) m_queue.push_back(n); else m_queue2.push_back(n); } void init_search_eh() override { m_bs_num_bool_vars = m_context.get_num_bool_vars(); } void end_search_eh() override { m_bs_num_bool_vars = UINT_MAX; } void reset() override { m_queue.reset(); m_head = 0; m_queue2.reset(); m_head2 = 0; m_scopes.reset(); } void push_scope() override { m_scopes.push_back(scope()); scope & s = m_scopes.back(); s.m_queue_trail = m_queue.size(); s.m_head_old = m_head; s.m_queue2_trail = m_queue2.size(); s.m_head2_old = m_head2; TRACE("case_split", tout << "head: " << m_head << "\n";); } void pop_scope(unsigned num_scopes) override { SASSERT(num_scopes <= m_scopes.size()); unsigned new_lvl = m_scopes.size() - num_scopes; scope & s = m_scopes[new_lvl]; m_queue.shrink(s.m_queue_trail); m_head = s.m_head_old; m_queue2.shrink(s.m_queue2_trail); m_head2 = s.m_head2_old; m_scopes.shrink(new_lvl); SASSERT(m_head <= m_queue.size()); TRACE("case_split", display(tout); tout << "head: " << m_head << "\n";); } void next_case_split_core(ptr_vector & queue, unsigned & head, bool_var & next, lbool & phase) { phase = l_undef; unsigned sz = queue.size(); for (; head < sz; head++) { expr * curr = queue[head]; bool is_or = m_manager.is_or(curr); bool is_and = m_manager.is_and(curr); bool intern = m_context.b_internalized(curr); SASSERT(intern || is_or); lbool val = l_undef; if (intern) { next = m_context.get_bool_var(curr); val = m_context.get_assignment(next); } else { SASSERT(is_or); // top level clause val = l_true; } if ((is_or && val == l_true) || (is_and && val == l_false)) { expr * undef_child = nullptr; if (!has_child_assigned_to(m_context, to_app(curr), val, undef_child, m_params.m_rel_case_split_order)) { if (m_manager.has_trace_stream()) { m_manager.trace_stream() << "[decide-and-or] #" << curr->get_id() << " #" << undef_child->get_id() << "\n"; } TRACE("case_split", tout << "found AND/OR candidate: #" << curr->get_id() << " #" << undef_child->get_id() << "\n";); literal l = m_context.get_literal(undef_child); next = l.var(); phase = l.sign() ? l_false : l_true; TRACE("case_split", display(tout);); return; } } else if (val == l_undef) { SASSERT(intern && m_context.get_bool_var(curr) == next); TRACE("case_split", tout << "found candidate: #" << curr->get_id() << "\n";); phase = l_undef; TRACE("case_split", display(tout);); return; } } next = null_bool_var; } void next_case_split(bool_var & next, lbool & phase) override { next_case_split_core(m_queue, m_head, next, phase); if (next == null_bool_var) next_case_split_core(m_queue2, m_head2, next, phase); // Force l_false is next is an equality that is known to be disequal in the logical context. if (m_params.m_lookahead_diseq && next != null_bool_var && phase != l_false && m_context.has_enode(next)) { enode * n = m_context.bool_var2enode(next); if (n->is_eq()) { enode * lhs = n->get_arg(0); enode * rhs = n->get_arg(1); if (m_context.is_ext_diseq(lhs, rhs, 2)) phase = l_false; } } } void display_core(std::ostream & out, ptr_vector & queue, unsigned head, unsigned idx) { if (queue.empty()) return; unsigned sz = queue.size(); for (unsigned i = 0; i < sz; i++) { if (i == head) out << "[HEAD" << idx << "]=> "; out << "#" << queue[i]->get_id() << " "; } out << "\n"; } void display(std::ostream & out) override { if (m_queue.empty() && m_queue2.empty()) return; out << "case-splits:\n"; display_core(out, m_queue, m_head, 1); display_core(out, m_queue2, m_head2, 2); } }; /** \brief Case split queue based on relevancy propagation */ class rel_act_case_split_queue : public case_split_queue { struct scope { unsigned m_queue_trail; unsigned m_head_old; }; typedef int_hashtable > bool_var_set; context & m_context; ast_manager & m_manager; smt_params &m_params; ptr_vector m_queue; unsigned m_head; int m_bs_num_bool_vars; //!< Number of boolean variable before starting to search. bool_var_act_queue m_delayed_queue; svector m_scopes; public: rel_act_case_split_queue(context & ctx, smt_params & p): m_context(ctx), m_manager(ctx.get_manager()), m_params(p), m_head(0), m_bs_num_bool_vars(UINT_MAX), m_delayed_queue(1024, bool_var_act_lt(ctx.get_activity_vector())) { } 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); m_delayed_queue.reserve(v+1); m_delayed_queue.insert(v); } } void del_var_eh(bool_var v) override { if (v >= m_bs_num_bool_vars && m_delayed_queue.contains(v)) m_delayed_queue.erase(v); } void unassign_var_eh(bool_var v) override { if (v < m_bs_num_bool_vars) return; if (!m_delayed_queue.contains(v)) m_delayed_queue.insert(v); } void relevant_eh(expr * n) override { if (!m_manager.is_bool(n)) return; bool is_or = m_manager.is_or(n); bool intern = m_context.b_internalized(n); if (!intern && !is_or) return; bool_var var = null_bool_var; if (intern) { var = m_context.get_bool_var(n); SASSERT(var != null_bool_var); bool is_and = m_manager.is_and(n); lbool val = m_context.get_assignment(var); if (!( val == l_undef || // n was not assigned yet (is_or && val == l_true) || // need to justify a child (is_and && val == l_false) // need to justify a child )) return; } if (!intern) { if (!m_context.is_searching()) m_queue.push_back(n); return; } if (var < m_bs_num_bool_vars) m_queue.push_back(n); } void init_search_eh() override { m_bs_num_bool_vars = m_context.get_num_bool_vars(); } void end_search_eh() override { m_bs_num_bool_vars = UINT_MAX; } void reset() override { m_queue.reset(); m_head = 0; m_delayed_queue.reset(); m_scopes.reset(); } void push_scope() override { m_scopes.push_back(scope()); scope & s = m_scopes.back(); s.m_queue_trail = m_queue.size(); s.m_head_old = m_head; TRACE("case_split", tout << "head: " << m_head << "\n";); } void pop_scope(unsigned num_scopes) override { SASSERT(num_scopes <= m_scopes.size()); unsigned new_lvl = m_scopes.size() - num_scopes; scope & s = m_scopes[new_lvl]; m_queue.shrink(s.m_queue_trail); m_head = s.m_head_old; m_scopes.shrink(new_lvl); SASSERT(m_head <= m_queue.size()); TRACE("case_split", display(tout); tout << "head: " << m_head << "\n";); } void next_case_split_core(bool_var & next, lbool & phase) { phase = l_undef; unsigned sz = m_queue.size(); for (; m_head < sz; m_head++) { expr * curr = m_queue[m_head]; bool is_or = m_manager.is_or(curr); bool is_and = m_manager.is_and(curr); bool intern = m_context.b_internalized(curr); SASSERT(intern || is_or); lbool val = l_undef; if (intern) { next = m_context.get_bool_var(curr); val = m_context.get_assignment(next); } else { SASSERT(is_or); // top level clause val = l_true; } if ((is_or && val == l_true) || (is_and && val == l_false)) { expr * undef_child = nullptr; if (!has_child_assigned_to(m_context, to_app(curr), val, undef_child, m_params.m_rel_case_split_order)) { TRACE("case_split", tout << "found AND/OR candidate: #" << curr->get_id() << " #" << undef_child->get_id() << "\n";); literal l = m_context.get_literal(undef_child); next = l.var(); phase = l.sign() ? l_false : l_true; TRACE("case_split", display(tout);); return; } } else if (val == l_undef) { SASSERT(intern && m_context.get_bool_var(curr) == next); TRACE("case_split", tout << "found candidate: #" << curr->get_id() << "\n";); phase = l_undef; TRACE("case_split", display(tout);); return; } } next = null_bool_var; } void next_case_split(bool_var & next, lbool & phase) override { if (m_context.get_random_value() < static_cast(0.02 * random_gen::max_value())) { next = m_context.get_random_value() % m_context.get_num_b_internalized(); TRACE("random_split", tout << "next: " << next << " get_assignment(next): " << m_context.get_assignment(next) << "\n";); if (m_context.get_assignment(next) == l_undef) return; } next_case_split_core(next, phase); if (next != null_bool_var) return; phase = l_undef; while (!m_delayed_queue.empty()) { next = m_delayed_queue.erase_min(); if (m_context.get_assignment(next) == l_undef) return; } next = null_bool_var; } void display_core(std::ostream & out) { if (m_queue.empty()) return; unsigned sz = m_queue.size(); for (unsigned i = 0; i < sz; i++) { if (i == m_head) out << "[HEAD]=> "; out << "#" << m_queue[i]->get_id() << " "; } out << "\n"; } void display(std::ostream & out) override { if (m_queue.empty()) return; out << "case-splits:\n"; display_core(out); } }; /** \brief Case split queue based on relevancy propagation and generation/goal-similarity */ class rel_goal_case_split_queue : public case_split_queue { #if 0 #define GOAL_START() m_goal_time.start() #define GOAL_STOP() m_goal_time.stop() #else #define GOAL_START() do {} while (0) #define GOAL_STOP() do {} while (0) #endif struct queue_entry { expr * m_expr; unsigned m_generation; int m_last_decided; queue_entry(expr * e, unsigned gen): m_expr(e), m_generation(gen), m_last_decided(-1) {} }; struct generation_lt { rel_goal_case_split_queue & m_parent; generation_lt(rel_goal_case_split_queue & p):m_parent(p) {} bool operator()(int v1, int v2) const { unsigned g1 = m_parent.m_queue2[v1].m_generation; unsigned g2 = m_parent.m_queue2[v2].m_generation; if (g1 == g2) return v1 < v2; else return g1 < g2; } }; struct scope { unsigned m_queue_trail; unsigned m_head_old; unsigned m_queue2_trail; unsigned m_generation; expr * m_goal; }; typedef int_hashtable > bool_var_set; context & m_context; smt_params & m_params; ast_manager & m_manager; ptr_vector m_queue; unsigned m_head; int m_bs_num_bool_vars; //!< Number of boolean variable before starting to search. svector m_queue2; svector m_scopes; unsigned m_current_generation; // The heap holds indices into m_queue2, i in m_priority_queue2 <==> m_queue2[i].m_last_assigned == -1 heap m_priority_queue2; expr * m_current_goal; stopwatch m_goal_time; static const unsigned start_gen = 0; static const unsigned goal_gen_decrement = 100; static const unsigned stop_gen = goal_gen_decrement + 1; public: rel_goal_case_split_queue(context & ctx, smt_params & p): m_context(ctx), m_params(p), m_manager(ctx.get_manager()), m_head(0), m_bs_num_bool_vars(UINT_MAX), m_priority_queue2(0, generation_lt(*this)), m_current_goal(nullptr) { set_global_generation(); } 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 {} void unassign_var_eh(bool_var v) override {} void relevant_eh(expr * n) override { if (get_generation(n) == 0 && m_current_generation != 0) set_generation_rec(n, m_current_generation); if (!m_manager.is_bool(n)) return; bool is_or = m_manager.is_or(n); bool intern = m_context.b_internalized(n); if (!intern && !is_or) return; bool_var var = null_bool_var; if (intern) { var = m_context.get_bool_var(n); SASSERT(var != null_bool_var); bool is_and = m_manager.is_and(n); lbool val = m_context.get_assignment(var); if (!( val == l_undef || // n was not assigned yet (is_or && val == l_true) || // need to justify a child (is_and && val == l_false) // need to justify a child )) return; } if (!intern && m_context.is_searching()) { SASSERT(is_or); add_to_queue2(n); return; } if (var < m_bs_num_bool_vars) m_queue.push_back(n); else add_to_queue2(n); } void internalize_instance_eh(expr * e, unsigned gen) override { //lower_generation(e, gen); } void init_search_eh() override { m_bs_num_bool_vars = m_context.get_num_bool_vars(); set_global_generation(); } void end_search_eh() override { m_bs_num_bool_vars = UINT_MAX; } void reset() override { m_queue.reset(); m_head = 0; m_queue2.reset(); m_scopes.reset(); m_priority_queue2.reset(); set_global_generation(); } void push_scope() override { m_scopes.push_back(scope()); scope & s = m_scopes.back(); s.m_queue_trail = m_queue.size(); s.m_head_old = m_head; s.m_queue2_trail = m_queue2.size(); s.m_generation = m_current_generation; s.m_goal = m_current_goal; TRACE("case_split", tout << "head: " << m_head << "\n";); } void pop_scope(unsigned num_scopes) override { SASSERT(num_scopes <= m_scopes.size()); unsigned new_lvl = m_scopes.size() - num_scopes; scope & s = m_scopes[new_lvl]; m_queue.shrink(s.m_queue_trail); m_head = s.m_head_old; m_current_generation = s.m_generation; m_current_goal = s.m_goal; for (unsigned i = s.m_queue2_trail; i < m_queue2.size(); i++) { //TRACE("case_split", tout << "ld[" << i << "] = " << m_queue2[i].m_last_decided << " cont " << SASSERT((m_queue2[i].m_last_decided == -1) == m_priority_queue2.contains(i)); if (m_priority_queue2.contains(i)) m_priority_queue2.erase(i); } for (unsigned i = 0; i < s.m_queue2_trail; i++) { queue_entry & e = m_queue2[i]; if (e.m_last_decided > static_cast(new_lvl)) { SASSERT(!m_priority_queue2.contains(i)); // Note that the generation might be reset by the pop, and we keep the heap // ordered by the old generation. It's unlikely to affect performance I think. m_priority_queue2.insert(i); e.m_last_decided = -1; } } m_queue2.shrink(s.m_queue2_trail); m_scopes.shrink(new_lvl); SASSERT(m_head <= m_queue.size()); TRACE("case_split", display(tout); tout << "head: " << m_head << "\n";); } void next_case_split_core(expr * curr, bool_var & next, lbool & phase) { bool is_or = m_manager.is_or(curr); bool is_and = m_manager.is_and(curr); bool intern = m_context.b_internalized(curr); SASSERT(intern || is_or); lbool val = l_undef; if (intern) { next = m_context.get_bool_var(curr); val = m_context.get_assignment(next); } else { SASSERT(is_or); // top level clause val = l_true; } if ((is_or && val == l_true) || (is_and && val == l_false)) { expr * undef_child = nullptr; if (!has_child_assigned_to(m_context, to_app(curr), val, undef_child, m_params.m_rel_case_split_order)) { if (m_manager.has_trace_stream()) { m_manager.trace_stream() << "[decide-and-or] #" << curr->get_id() << " #" << undef_child->get_id() << "\n"; } TRACE("case_split", tout << "found AND/OR candidate: #" << curr->get_id() << " #" << undef_child->get_id() << "\n";); literal l = m_context.get_literal(undef_child); next = l.var(); phase = l.sign() ? l_false : l_true; TRACE("case_split", display(tout);); return; } } else if (val == l_undef) { SASSERT(intern && m_context.get_bool_var(curr) == next); TRACE("case_split", tout << "found candidate: #" << curr->get_id() << "\n";); phase = l_undef; TRACE("case_split", display(tout);); return; } next = null_bool_var; } void next_case_split(bool_var & next, lbool & phase) override { phase = l_undef; next = null_bool_var; unsigned sz = m_queue.size(); for (; m_head < sz; m_head++) { expr * curr = m_queue[m_head]; next_case_split_core(curr, next, phase); if (next != null_bool_var) return; } while (!m_priority_queue2.empty()) { unsigned idx = static_cast(m_priority_queue2.erase_min()); TRACE("case_split", tout << "q " << m_queue2.size() << " idx " << idx << "\n"; ); SASSERT(idx < m_queue2.size()); queue_entry & e = m_queue2[idx]; SASSERT(e.m_last_decided == -1); e.m_last_decided = m_scopes.size(); next_case_split_core(e.m_expr, next, phase); if (next != null_bool_var) { // Push the last guy back in; the other queue doesn't increment // the m_head in case of return and the code in decide() actually // does the push after calling us m_priority_queue2.insert(idx); e.m_last_decided = -1; return; } } } void display_core(std::ostream & out, ptr_vector & queue, unsigned head, unsigned idx) { if (queue.empty()) return; unsigned sz = queue.size(); for (unsigned i = 0; i < sz; i++) { if (i == head) out << "[HEAD" << idx << "]=> "; out << "#" << queue[i]->get_id() << " "; } out << "\n"; } void display(std::ostream & out) override { if (m_queue.empty() && m_queue2.empty()) return; out << "case-splits:\n"; display_core(out, m_queue, m_head, 1); //display_core(out, m_queue2, m_head2, 2); } void assign_lit_eh(literal l) override { // if (m_current_generation > stop_gen) // m_current_generation--; expr * e = m_context.bool_var2expr(l.var()); if (e == m_current_goal) return; bool sign = l.sign(); if ( ((m_manager.is_and(e) && !sign) || (m_manager.is_or(e) && sign)) && to_app(e)->get_num_args() == 2) { expr * lablit = to_app(e)->get_arg(1); if (m_manager.is_not(lablit)) { sign = !sign; lablit = to_app(lablit)->get_arg(0); } if (sign) return; if (!m_manager.is_label_lit(lablit)) return; TRACE("case_split", tout << "Found goal\n" << mk_pp(e, m_manager) << "\n"; ); set_goal(e); } } private: unsigned get_generation(expr * e) { unsigned maxgen = 0; unsigned mingen = (unsigned)-1; ptr_vector stack; stack.push_back(e); while (!stack.empty()) { unsigned gen; expr * curr; curr = stack.back(); stack.pop_back(); if (m_context.e_internalized(curr)) { gen = m_context.get_enode(curr)->get_generation(); if (gen > maxgen) maxgen = gen; if (gen < mingen) mingen = gen; } else if (is_app(curr)) { app * a = to_app(curr); for (unsigned i = 0; i < a->get_num_args(); ++i) stack.push_back(a->get_arg(i)); } } return maxgen; } void add_to_queue2(expr * e) { int idx = m_queue2.size(); GOAL_START(); m_queue2.push_back(queue_entry(e, get_generation(e))); m_priority_queue2.reserve(idx+1); m_priority_queue2.insert(idx); GOAL_STOP(); } struct set_generation_fn { context & m_context; unsigned m_generation; set_generation_fn(context & ctx, unsigned gen) : m_context(ctx), m_generation(gen) { } void operator()(expr * e) { if (m_context.e_internalized(e)) { enode * n = m_context.get_enode(e); n->set_generation(m_context, m_generation); } } }; void set_generation_rec(expr * e, unsigned gen) { set_generation_fn proc(m_context, gen); for_each_expr(proc, e); } void lower_generation(expr * e, unsigned gen) { ptr_vector stack; stack.push_back(e); while (!stack.empty()) { expr * curr; curr = stack.back(); stack.pop_back(); if (m_context.e_internalized(curr)) { unsigned curr_gen = m_context.get_enode(curr)->get_generation(); if (curr_gen > gen) { // Lower it. set_generation_rec(e, gen); continue; // Don't add children. } else if (curr_gen < gen) { // All the children will be lower as well, don't add them. continue; } } if (is_app(curr)) { app * a = to_app(curr); for (unsigned i = 0; i < a->get_num_args(); ++i) stack.push_back(a->get_arg(i)); } } } void set_goal(expr * e) { if (e == m_current_goal) return; GOAL_START(); m_current_goal = e; #if 1 if (m_current_generation >= goal_gen_decrement) { set_generation_rec(m_current_goal, m_current_generation - goal_gen_decrement); /* m_priority_queue2.reset(); m_priority_queue2.reserve(m_queue2.size()); for (unsigned i = 0; i < m_queue2.size(); ++i) { queue_entry & e = m_queue2[i]; e.m_generation = get_generation(e.m_expr); if (e.m_last_decided == -1) m_priority_queue2.insert(i); } */ } #endif GOAL_STOP(); } void set_global_generation() { m_current_generation = start_gen; m_context.set_global_generation(start_gen); if (m_params.m_qi_eager_threshold < start_gen) m_params.m_qi_eager_threshold += start_gen; } }; class theory_aware_branching_queue : public case_split_queue { protected: context & m_context; smt_params & m_params; theory_var_priority_map m_theory_var_priority; theory_aware_act_queue m_queue; int_hashtable > m_theory_vars; map > m_theory_var_phase; public: theory_aware_branching_queue(context & ctx, smt_params & p): m_context(ctx), m_params(p), m_theory_var_priority(), m_queue(1024, theory_aware_act_lt(ctx.get_activity_vector(), m_theory_var_priority)) { } void activity_increased_eh(bool_var v) override { if (m_queue.contains(v)) 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); } void del_var_eh(bool_var v) override { if (m_queue.contains(v)) m_queue.erase(v); } void unassign_var_eh(bool_var v) override { if (!m_queue.contains(v)) m_queue.insert(v); } void relevant_eh(expr * n) override {} void init_search_eh() override {} void end_search_eh() override {} void reset() override { m_queue.reset(); } void push_scope() override {} void pop_scope(unsigned num_scopes) override {} void next_case_split(bool_var & next, lbool & phase) override { int threshold = static_cast(m_params.m_random_var_freq * random_gen::max_value()); SASSERT(threshold >= 0); if (m_context.get_random_value() < threshold) { SASSERT(m_context.get_num_b_internalized() > 0); next = m_context.get_random_value() % m_context.get_num_b_internalized(); TRACE("random_split", tout << "next: " << next << " get_assignment(next): " << m_context.get_assignment(next) << "\n";); if (m_context.get_assignment(next) == l_undef) return; } while (!m_queue.empty()) { next = m_queue.erase_min(); if (m_context.get_assignment(next) == l_undef) return; } next = null_bool_var; if (m_theory_vars.contains(next)) { if (!m_theory_var_phase.find(next, phase)) { phase = l_undef; } } } void add_theory_aware_branching_info(bool_var v, double priority, lbool phase) override { TRACE("theory_aware_branching", tout << "Add theory-aware branching information for l#" << v << ": priority=" << priority << std::endl;); m_theory_vars.insert(v); m_theory_var_phase.insert(v, phase); m_theory_var_priority.insert(v, priority); if (m_queue.contains(v)) { if (priority > 0.0) { m_queue.decreased(v); } else { m_queue.increased(v); } } // m_theory_queue.reserve(v+1); // m_theory_queue.insert(v); } void display(std::ostream & out) override { bool first = true; bool_var_act_queue::const_iterator it = m_queue.begin(); bool_var_act_queue::const_iterator end = m_queue.end(); for (; it != end ; ++it) { unsigned v = *it; if (m_context.get_assignment(v) == l_undef) { if (first) { out << "remaining case-splits:\n"; first = false; } out << "#" << m_context.bool_var2expr(v)->get_id() << " "; } } if (!first) out << "\n"; } ~theory_aware_branching_queue() override {}; }; case_split_queue * mk_case_split_queue(context & ctx, smt_params & p) { if (p.m_relevancy_lvl < 2 && (p.m_case_split_strategy == CS_RELEVANCY || p.m_case_split_strategy == CS_RELEVANCY_ACTIVITY || p.m_case_split_strategy == CS_RELEVANCY_GOAL)) { warning_msg("relevancy must be enabled to use option CASE_SPLIT=3, 4 or 5"); p.m_case_split_strategy = CS_ACTIVITY; } if (p.m_auto_config && (p.m_case_split_strategy == CS_RELEVANCY || p.m_case_split_strategy == CS_RELEVANCY_ACTIVITY || p.m_case_split_strategy == CS_RELEVANCY_GOAL)) { warning_msg("auto configuration (option AUTO_CONFIG) must be disabled to use option CASE_SPLIT=3, 4 or 5"); p.m_case_split_strategy = CS_ACTIVITY; } switch (p.m_case_split_strategy) { case CS_ACTIVITY_DELAY_NEW: return alloc(dact_case_split_queue, ctx, p); case CS_ACTIVITY_WITH_CACHE: return alloc(cact_case_split_queue, ctx, p); case CS_RELEVANCY: return alloc(rel_case_split_queue, ctx, p); case CS_RELEVANCY_ACTIVITY: return alloc(rel_act_case_split_queue, ctx, p); case CS_RELEVANCY_GOAL: return alloc(rel_goal_case_split_queue, ctx, p); case CS_ACTIVITY_THEORY_AWARE_BRANCHING: return alloc(theory_aware_branching_queue, ctx, p); default: return alloc(act_case_split_queue, ctx, p); } } }; z3-z3-4.8.7/src/smt/smt_case_split_queue.h000066400000000000000000000030341356505360400204070ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_case_split_queue.h Abstract: Author: Leonardo de Moura (leonardo) 2009-01-20. Revision History: --*/ #ifndef SMT_CASE_SPLIT_QUEUE_H_ #define SMT_CASE_SPLIT_QUEUE_H_ #include "smt/smt_types.h" #include "util/heap.h" #include "smt/params/smt_params.h" namespace smt { class context; /** \brief Abstract case split queue. */ 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) {} virtual void unassign_var_eh(bool_var v) = 0; virtual void relevant_eh(expr * n) = 0; virtual void init_search_eh() = 0; virtual void end_search_eh() = 0; virtual void internalize_instance_eh(expr * e, unsigned gen) {} virtual void reset() = 0; virtual void push_scope() = 0; virtual void pop_scope(unsigned num_scopes) = 0; virtual void next_case_split(bool_var & next, lbool & phase) = 0; virtual void display(std::ostream & out) = 0; virtual ~case_split_queue() {} // theory-aware branching hint virtual void add_theory_aware_branching_info(bool_var v, double priority, lbool phase) {} }; case_split_queue * mk_case_split_queue(context & ctx, smt_params & p); }; #endif /* SMT_CASE_SPLIT_QUEUE_H_ */ z3-z3-4.8.7/src/smt/smt_cg_table.cpp000066400000000000000000000173311356505360400171550ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_cg_table.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-02-19. Revision History: --*/ #include "smt/smt_cg_table.h" #include "ast/ast_pp.h" #include "ast/ast_ll_pp.h" namespace smt { // one table per func_decl implementation unsigned cg_table::cg_hash::operator()(enode * n) const { SASSERT(n->get_decl()->is_flat_associative() || n->get_num_args() >= 3); unsigned a, b, c; a = b = 0x9e3779b9; c = 11; unsigned i = n->get_num_args(); while (i >= 3) { i--; a += n->get_arg(i)->get_root()->hash(); i--; b += n->get_arg(i)->get_root()->hash(); i--; c += n->get_arg(i)->get_root()->hash(); mix(a, b, c); } switch (i) { case 2: b += n->get_arg(1)->get_root()->hash(); Z3_fallthrough; case 1: c += n->get_arg(0)->get_root()->hash(); } mix(a, b, c); return c; } bool cg_table::cg_eq::operator()(enode * n1, enode * n2) const { 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; return true; } cg_table::cg_table(ast_manager & m): m_manager(m) { } cg_table::~cg_table() { reset(); } void * cg_table::mk_table_for(func_decl * d) { void * r; SASSERT(d->get_arity() >= 1); switch (d->get_arity()) { case 1: r = TAG(void*, alloc(unary_table), UNARY); SASSERT(GET_TAG(r) == UNARY); return r; case 2: if (d->is_flat_associative()) { // applications of declarations that are flat-assoc (e.g., +) may have many arguments. r = TAG(void*, alloc(table), NARY); SASSERT(GET_TAG(r) == NARY); return r; } else if (d->is_commutative()) { r = TAG(void*, alloc(comm_table, cg_comm_hash(), cg_comm_eq(m_commutativity)), BINARY_COMM); SASSERT(GET_TAG(r) == BINARY_COMM); return r; } else { r = TAG(void*, alloc(binary_table), BINARY); SASSERT(GET_TAG(r) == BINARY); return r; } default: r = TAG(void*, alloc(table), NARY); SASSERT(GET_TAG(r) == NARY); return r; } } unsigned cg_table::set_func_decl_id(enode * n) { func_decl * f = n->get_decl(); unsigned tid; if (!m_func_decl2id.find(f, tid)) { tid = m_tables.size(); m_func_decl2id.insert(f, tid); m_manager.inc_ref(f); SASSERT(tid <= m_tables.size()); m_tables.push_back(mk_table_for(f)); } SASSERT(tid < m_tables.size()); n->set_func_decl_id(tid); DEBUG_CODE({ unsigned tid_prime; SASSERT(m_func_decl2id.find(n->get_decl(), tid_prime) && tid == tid_prime); }); return tid; } void cg_table::reset() { for (void* t : m_tables) { switch (GET_TAG(t)) { case UNARY: dealloc(UNTAG(unary_table*, t)); break; case BINARY: dealloc(UNTAG(binary_table*, t)); break; case BINARY_COMM: dealloc(UNTAG(comm_table*, t)); break; case NARY: dealloc(UNTAG(table*, t)); break; } } m_tables.reset(); for (auto const& kv : m_func_decl2id) { m_manager.dec_ref(kv.m_key); } m_func_decl2id.reset(); } void cg_table::display(std::ostream & out) const { for (auto const& kv : m_func_decl2id) { void * t = m_tables[kv.m_value]; out << mk_pp(kv.m_key, m_manager) << ": "; switch (GET_TAG(t)) { case UNARY: display_unary(out, t); break; case BINARY: display_binary(out, t); break; case BINARY_COMM: display_binary_comm(out, t); break; case NARY: display_nary(out, t); break; } } } void cg_table::display_binary(std::ostream& out, void* t) const { binary_table* tb = UNTAG(binary_table*, t); out << "b "; for (enode* n : *tb) { out << n->get_owner_id() << " " << cg_binary_hash()(n) << " "; } out << "\n"; } void cg_table::display_binary_comm(std::ostream& out, void* t) const { comm_table* tb = UNTAG(comm_table*, t); out << "bc "; for (enode* n : *tb) { out << n->get_owner_id() << " "; } out << "\n"; } void cg_table::display_unary(std::ostream& out, void* t) const { unary_table* tb = UNTAG(unary_table*, t); out << "un "; for (enode* n : *tb) { out << n->get_owner_id() << " "; } out << "\n"; } void cg_table::display_nary(std::ostream& out, void* t) const { table* tb = UNTAG(table*, t); out << "nary "; for (enode* n : *tb) { out << n->get_owner_id() << " "; } out << "\n"; } enode_bool_pair cg_table::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))) { case UNARY: n_prime = UNTAG(unary_table*, t)->insert_if_not_there(n); return enode_bool_pair(n_prime, false); case BINARY: n_prime = UNTAG(binary_table*, t)->insert_if_not_there(n); TRACE("cg_table", tout << "insert: " << n->get_owner_id() << " " << cg_binary_hash()(n) << " inserted: " << (n == n_prime) << " " << n_prime->get_owner_id() << "\n"; display_binary(tout, t); tout << "contains_ptr: " << contains_ptr(n) << "\n";); return enode_bool_pair(n_prime, false); case BINARY_COMM: m_commutativity = false; n_prime = UNTAG(comm_table*, t)->insert_if_not_there(n); return enode_bool_pair(n_prime, m_commutativity); default: n_prime = UNTAG(table*, t)->insert_if_not_there(n); return enode_bool_pair(n_prime, false); } } void cg_table::erase(enode * n) { SASSERT(n->get_num_args() > 0); void * t = get_table(n); switch (static_cast(GET_TAG(t))) { case UNARY: UNTAG(unary_table*, t)->erase(n); break; case BINARY: TRACE("cg_table", tout << "erase: " << n->get_owner_id() << " " << cg_binary_hash()(n) << " contains: " << contains_ptr(n) << "\n";); UNTAG(binary_table*, t)->erase(n); break; case BINARY_COMM: UNTAG(comm_table*, t)->erase(n); break; default: UNTAG(table*, t)->erase(n); break; } } void cg_table::display_compact(std::ostream & out) const { } bool cg_table::check_invariant() const { return true; } }; z3-z3-4.8.7/src/smt/smt_cg_table.h000066400000000000000000000157011356505360400166210ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_cg_table.h Abstract: Author: Leonardo de Moura (leonardo) 2008-02-19. Revision History: --*/ #ifndef SMT_CG_TABLE_H_ #define SMT_CG_TABLE_H_ #include "smt/smt_enode.h" #include "util/hashtable.h" #include "util/chashtable.h" namespace smt { typedef std::pair enode_bool_pair; // one table per function symbol /** \brief Congruence table. */ class cg_table { struct cg_unary_hash { unsigned operator()(enode * n) const { SASSERT(n->get_num_args() == 1); return n->get_arg(0)->get_root()->hash(); } }; struct cg_unary_eq { bool operator()(enode * n1, enode * n2) const { SASSERT(n1->get_num_args() == 1); SASSERT(n2->get_num_args() == 1); SASSERT(n1->get_decl() == n2->get_decl()); return n1->get_arg(0)->get_root() == n2->get_arg(0)->get_root(); } }; typedef chashtable unary_table; struct cg_binary_hash { unsigned operator()(enode * n) const { SASSERT(n->get_num_args() == 2); return combine_hash(n->get_arg(0)->get_root()->hash(), n->get_arg(1)->get_root()->hash()); } }; struct cg_binary_eq { bool operator()(enode * n1, enode * n2) const { SASSERT(n1->get_num_args() == 2); SASSERT(n2->get_num_args() == 2); SASSERT(n1->get_decl() == n2->get_decl()); return n1->get_arg(0)->get_root() == n2->get_arg(0)->get_root() && n1->get_arg(1)->get_root() == n2->get_arg(1)->get_root(); } }; typedef chashtable binary_table; struct cg_comm_hash { unsigned operator()(enode * n) const { SASSERT(n->get_num_args() == 2); unsigned h1 = n->get_arg(0)->get_root()->hash(); unsigned h2 = n->get_arg(1)->get_root()->hash(); if (h1 > h2) std::swap(h1, h2); return hash_u((h1 << 16) | (h2 & 0xFFFF)); } }; struct cg_comm_eq { bool & m_commutativity; cg_comm_eq(bool & c):m_commutativity(c) {} bool operator()(enode * n1, enode * n2) const { SASSERT(n1->get_num_args() == 2); SASSERT(n2->get_num_args() == 2); SASSERT(n1->get_decl() == n2->get_decl()); enode * c1_1 = n1->get_arg(0)->get_root(); enode * c1_2 = n1->get_arg(1)->get_root(); enode * c2_1 = n2->get_arg(0)->get_root(); enode * c2_2 = n2->get_arg(1)->get_root(); if (c1_1 == c2_1 && c1_2 == c2_2) { return true; } if (c1_1 == c2_2 && c1_2 == c2_1) { m_commutativity = true; return true; } return false; } }; typedef chashtable comm_table; struct cg_hash { unsigned operator()(enode * n) const; }; struct cg_eq { bool operator()(enode * n1, enode * n2) const; }; typedef chashtable table; ast_manager & m_manager; bool m_commutativity; //!< true if the last found congruence used commutativity ptr_vector m_tables; obj_map m_func_decl2id; enum table_kind { UNARY, BINARY, BINARY_COMM, NARY }; void * mk_table_for(func_decl * d); unsigned set_func_decl_id(enode * n); void * get_table(enode * n) { unsigned tid = n->get_func_decl_id(); if (tid == UINT_MAX) tid = set_func_decl_id(n); SASSERT(tid < m_tables.size()); return m_tables[tid]; } public: cg_table(ast_manager & m); ~cg_table(); /** \brief Try to insert n into the table. If the table already contains an element n' congruent to n, then do nothing and return n' and a boolean indicating whether n and n' are congruence modulo commutativity, otherwise insert n and return (n,false). */ enode_bool_pair insert(enode * n); void erase(enode * n); bool contains(enode * n) const { SASSERT(n->get_num_args() > 0); void * t = const_cast(this)->get_table(n); switch (static_cast(GET_TAG(t))) { case UNARY: return UNTAG(unary_table*, t)->contains(n); case BINARY: return UNTAG(binary_table*, t)->contains(n); case BINARY_COMM: return UNTAG(comm_table*, t)->contains(n); default: return UNTAG(table*, t)->contains(n); } } enode * find(enode * n) const { SASSERT(n->get_num_args() > 0); enode * r = nullptr; void * t = const_cast(this)->get_table(n); switch (static_cast(GET_TAG(t))) { case UNARY: return UNTAG(unary_table*, t)->find(n, r) ? r : nullptr; case BINARY: return UNTAG(binary_table*, t)->find(n, r) ? r : nullptr; case BINARY_COMM: return UNTAG(comm_table*, t)->find(n, r) ? r : nullptr; default: return UNTAG(table*, t)->find(n, r) ? r : nullptr; } } bool contains_ptr(enode * n) const { enode * r; SASSERT(n->get_num_args() > 0); void * t = const_cast(this)->get_table(n); switch (static_cast(GET_TAG(t))) { case UNARY: return UNTAG(unary_table*, t)->find(n, r) && n == r; case BINARY: return UNTAG(binary_table*, t)->find(n, r) && n == r; case BINARY_COMM: return UNTAG(comm_table*, t)->find(n, r) && n == r; default: return UNTAG(table*, t)->find(n, r) && n == r; } } void reset(); void display(std::ostream & out) const; void display_binary(std::ostream& out, void* t) const; void display_binary_comm(std::ostream& out, void* t) const; void display_unary(std::ostream& out, void* t) const; void display_nary(std::ostream& out, void* t) const; void display_compact(std::ostream & out) const; bool check_invariant() const; }; }; #endif /* SMT_CG_TABLE_H_ */ z3-z3-4.8.7/src/smt/smt_checker.cpp000066400000000000000000000140461356505360400170210ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_checker.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-06-20. Revision History: --*/ #include "smt/smt_context.h" #include "smt/smt_checker.h" #include "ast/ast_ll_pp.h" namespace smt { bool checker::all_args(app * a, bool is_true) { for (expr* arg : *a) { if (!check(arg, is_true)) return false; } return true; } bool checker::any_arg(app * a, bool is_true) { for (expr* arg : *a) { if (check(arg, is_true)) return true; } return false; } bool checker::check_core(expr * n, bool is_true) { SASSERT(m_manager.is_bool(n)); if (m_context.b_internalized(n) && m_context.is_relevant(n)) { lbool val = m_context.get_assignment(n); return val != l_undef && is_true == (val == l_true); } if (!is_app(n)) return false; app * a = to_app(n); if (a->get_family_id() == m_manager.get_basic_family_id()) { switch (a->get_decl_kind()) { case OP_TRUE: return is_true; case OP_FALSE: return !is_true; case OP_NOT: return check(a->get_arg(0), !is_true); case OP_OR: return is_true ? any_arg(a, true) : all_args(a, false); case OP_AND: return is_true ? all_args(a, true) : any_arg(a, false); case OP_EQ: if (!m_manager.is_iff(a)) { enode * lhs = get_enode_eq_to(a->get_arg(0)); enode * rhs = get_enode_eq_to(a->get_arg(1)); if (lhs && rhs && m_context.is_relevant(lhs) && m_context.is_relevant(rhs)) { if (is_true && lhs->get_root() == rhs->get_root()) return true; // if (!is_true && m_context.is_ext_diseq(lhs, rhs, 2)) if (!is_true && m_context.is_diseq(lhs, rhs)) return true; } return false; } else if (is_true) { return (check(a->get_arg(0), true) && check(a->get_arg(1), true)) || (check(a->get_arg(0), false) && check(a->get_arg(1), false)); } else { return (check(a->get_arg(0), true) && check(a->get_arg(1), false)) || (check(a->get_arg(0), false) && check(a->get_arg(1), true)); } case OP_ITE: { if (m_context.lit_internalized(a->get_arg(0)) && m_context.is_relevant(a->get_arg(0))) { switch (m_context.get_assignment(a->get_arg(0))) { case l_false: return check(a->get_arg(2), is_true); case l_undef: return false; case l_true: return check(a->get_arg(1), is_true); } } return check(a->get_arg(1), is_true) && check(a->get_arg(2), is_true); } default: break; } } enode * e = get_enode_eq_to(a); if (e && e->is_bool() && m_context.is_relevant(e)) { lbool val = m_context.get_assignment(e->get_owner()); return val != l_undef && is_true == (val == l_true); } return false; } bool checker::check(expr * n, bool is_true) { bool r; if (n->get_ref_count() > 1 && m_is_true_cache[is_true].find(n, r)) return r; r = check_core(n, is_true); if (n->get_ref_count() > 1) m_is_true_cache[is_true].insert(n, r); return r; } enode * checker::get_enode_eq_to_core(app * n) { ptr_buffer buffer; unsigned num = n->get_num_args(); for (unsigned i = 0; i < num; i++) { enode * arg = get_enode_eq_to(n->get_arg(i)); if (arg == nullptr) return nullptr; buffer.push_back(arg); } enode * e = m_context.get_enode_eq_to(n->get_decl(), num, buffer.c_ptr()); if (e == nullptr) return nullptr; return m_context.is_relevant(e) ? e : nullptr; } enode * checker::get_enode_eq_to(expr * n) { if (is_var(n)) { unsigned idx = to_var(n)->get_idx(); if (idx >= m_num_bindings) return nullptr; return m_bindings[m_num_bindings - idx - 1]; } if (m_context.e_internalized(n) && m_context.is_relevant(n)) return m_context.get_enode(n); if (!is_app(n) || to_app(n)->get_num_args() == 0) return nullptr; enode * r = nullptr; if (n->get_ref_count() > 1 && m_to_enode_cache.find(n, r)) return r; r = get_enode_eq_to_core(to_app(n)); if (n->get_ref_count() > 1) m_to_enode_cache.insert(n, r); return r; } bool checker::is_sat(expr * n, unsigned num_bindings, enode * const * bindings) { flet l1(m_num_bindings, num_bindings); flet l2(m_bindings, bindings); bool r = check(n, true); m_is_true_cache[0].reset(); m_is_true_cache[1].reset(); m_to_enode_cache.reset(); return r; } bool checker::is_unsat(expr * n, unsigned num_bindings, enode * const * bindings) { flet l1(m_num_bindings, num_bindings); flet l2(m_bindings, bindings); bool r = check(n, false); m_is_true_cache[0].reset(); m_is_true_cache[1].reset(); m_to_enode_cache.reset(); return r; } checker::checker(context & c): m_context(c), m_manager(c.get_manager()), m_num_bindings(0), m_bindings(nullptr) { } }; z3-z3-4.8.7/src/smt/smt_checker.h000066400000000000000000000023661356505360400164700ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_checker.h Abstract: Author: Leonardo de Moura (leonardo) 2008-06-20. Revision History: --*/ #ifndef SMT_CHECKER_H_ #define SMT_CHECKER_H_ #include "ast/ast.h" #include "util/obj_hashtable.h" namespace smt { class context; class checker { typedef obj_map expr2bool; typedef obj_map expr2enode; context & m_context; ast_manager & m_manager; expr2bool m_is_true_cache[2]; expr2enode m_to_enode_cache; unsigned m_num_bindings; enode * const * m_bindings; bool all_args(app * a, bool is_true); bool any_arg(app * a, bool is_true); bool check_core(expr * n, bool is_true); bool check(expr * n, bool is_true); enode * get_enode_eq_to_core(app * n); enode * get_enode_eq_to(expr * n); public: checker(context & c); bool is_sat(expr * n, unsigned num_bindings = 0, enode * const * bindings = nullptr); bool is_unsat(expr * n, unsigned num_bindings = 0, enode * const * bindings = nullptr); }; }; #endif /* SMT_CHECKER_H_ */ z3-z3-4.8.7/src/smt/smt_clause.cpp000066400000000000000000000111411356505360400166620ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_clause.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-02-19. 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 { /** \brief Create a new clause. bool_var2expr_map is a mapping from bool_var -> expr, it is only used if save_atoms == true. */ clause * clause::mk(ast_manager & m, unsigned num_lits, literal * lits, clause_kind k, justification * js, clause_del_eh * del_eh, bool save_atoms, expr * const * bool_var2expr_map) { SASSERT(smt::is_axiom(k) || js == nullptr || !js->in_region()); SASSERT(num_lits >= 2); unsigned sz = get_obj_size(num_lits, k, save_atoms, del_eh != nullptr, js != nullptr); void * mem = m.get_allocator().allocate(sz); clause * cls = new (mem) clause(); cls->m_num_literals = num_lits; cls->m_capacity = num_lits; cls->m_kind = k; cls->m_reinit = save_atoms; cls->m_reinternalize_atoms = save_atoms; cls->m_has_atoms = save_atoms; cls->m_has_del_eh = del_eh != nullptr; cls->m_has_justification = js != nullptr; cls->m_deleted = false; SASSERT(!m.proofs_enabled() || js != 0); memcpy(cls->m_lits, lits, sizeof(literal) * num_lits); if (cls->is_lemma()) cls->set_activity(1); if (del_eh) *(const_cast(cls->get_del_eh_addr())) = del_eh; if (js) *(const_cast(cls->get_justification_addr())) = js; if (save_atoms) { for (unsigned i = 0; i < num_lits; i++) { expr * atom = bool_var2expr_map[lits[i].var()]; m.inc_ref(atom); const_cast(cls->get_atoms_addr())[i] = TAG(expr*, atom, lits[i].sign()); } } DEBUG_CODE({ SASSERT(!cls->is_lemma() || cls->get_activity() == 1); SASSERT(cls->get_del_eh() == del_eh); SASSERT(cls->get_justification() == js); for (unsigned i = 0; i < num_lits; i++) { SASSERT((*cls)[i] == lits[i]); SASSERT(!save_atoms || cls->get_atom(i) == bool_var2expr_map[lits[i].var()]); }}); return cls; } void clause::deallocate(ast_manager & m) { clause_del_eh * del_eh = get_del_eh(); if (del_eh) (*del_eh)(m, this); if (is_lemma() && m_has_justification) { justification * js = get_justification(); if (js) { SASSERT(!js->in_region()); js->del_eh(m); dealloc(js); } } unsigned num_atoms = get_num_atoms(); for (unsigned i = 0; i < num_atoms; i++) { SASSERT(m_reinit || get_atom(i) == 0); m.dec_ref(get_atom(i)); } m.get_allocator().deallocate(get_obj_size(m_capacity, get_kind(), m_has_atoms, m_has_del_eh, m_has_justification), this); } void clause::release_atoms(ast_manager & m) { unsigned num_atoms = get_num_atoms(); for (unsigned i = 0; i < num_atoms; i++) { m.dec_ref(get_atom(i)); const_cast(get_atoms_addr())[i] = nullptr; } } 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); } return out << ")"; } 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); } 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 << mk_bounded_pp(disj, m, 3); } }; z3-z3-4.8.7/src/smt/smt_clause.h000066400000000000000000000217771356505360400163470ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_clause.h Abstract: Author: Leonardo de Moura (leonardo) 2008-02-18. Revision History: --*/ #ifndef SMT_CLAUSE_H_ #define SMT_CLAUSE_H_ #include "ast/ast.h" #include "smt/smt_literal.h" #include "util/tptr.h" #include "util/obj_hashtable.h" #include "smt/smt_justification.h" namespace smt { class clause; /** \brief Abstract functor: clause deletion event handler. */ class clause_del_eh { public: virtual ~clause_del_eh() {} virtual void operator()(ast_manager & m, clause * cls) = 0; }; enum clause_kind { CLS_AUX, // an input assumption CLS_TH_AXIOM, // a theory axiom CLS_LEARNED, // learned through conflict resolution CLS_TH_LEMMA // a theory lemma }; inline bool is_axiom(clause_kind k) { return k == CLS_AUX || k == CLS_TH_AXIOM; } inline bool is_lemma(clause_kind k) { return k == CLS_LEARNED || k == CLS_TH_LEMMA; } /** \brief A SMT clause. A clause has several optional fields, I store space for them only if they are actually used. */ class clause { unsigned m_num_literals; unsigned m_capacity:24; //!< some of the clause literals can be simplified and removed, this field contains the original number of literals (used for GC). unsigned m_kind:2; //!< kind unsigned m_reinit:1; //!< true if the clause is in the reinit stack (only for learned clauses and aux_lemmas) unsigned m_reinternalize_atoms:1; //!< true if atoms must be reinitialized during reinitialization unsigned m_has_atoms:1; //!< true if the clause has memory space for storing atoms. unsigned m_has_del_eh:1; //!< true if must notify event handler when deleted. unsigned m_has_justification:1; //!< true if the clause has a justification attached to it. unsigned m_deleted:1; //!< true if the clause is marked for deletion by was not deleted yet because it is referenced by some data-structure (e.g., m_lemmas) literal m_lits[0]; static unsigned get_obj_size(unsigned num_lits, clause_kind k, bool has_atoms, bool has_del_eh, bool has_justification) { unsigned r = sizeof(clause) + sizeof(literal) * num_lits; if (smt::is_lemma(k)) r += sizeof(unsigned); /* dvitek: Fix alignment issues on 64-bit platforms. The * 'if' statement below probably isn't worthwhile since * I'm guessing the allocator is probably going to round * up internally anyway. */ //if (has_atoms || has_del_eh || has_justification) r = (r + (sizeof(void*)-1)) & ~(sizeof(void*)-1); if (has_atoms) r += sizeof(expr*) * num_lits; if (has_del_eh) r += sizeof(clause_del_eh *); if (has_justification) r += sizeof(justification *); return r; } unsigned const * get_activity_addr() const { return reinterpret_cast(m_lits + m_capacity); } unsigned * get_activity_addr() { return reinterpret_cast(m_lits + m_capacity); } clause_del_eh * const * get_del_eh_addr() const { unsigned const * addr = get_activity_addr(); if (is_lemma()) addr ++; /* dvitek: It would be better to use uintptr_t than * size_t, but we need to wait until c++11 support is * really available. */ size_t as_int = reinterpret_cast(addr); as_int = (as_int + sizeof(void*)-1) & ~static_cast(sizeof(void*)-1); return reinterpret_cast(as_int); } justification * const * get_justification_addr() const { clause_del_eh * const * addr = get_del_eh_addr(); if (m_has_del_eh) addr ++; return reinterpret_cast(addr); } expr * const * get_atoms_addr() const { justification * const * addr = get_justification_addr(); if (m_has_justification) addr ++; return reinterpret_cast(addr); } friend class context; void swap_lits(unsigned idx1, unsigned idx2) { SASSERT(idx1 < m_num_literals); SASSERT(idx2 < m_num_literals); std::swap(m_lits[idx1], m_lits[idx2]); } bool is_watch(literal l) const { return m_lits[0] == l || m_lits[1] == l; } void set_literal(unsigned idx, literal l) { m_lits[idx] = l; } void set_num_literals(unsigned n) { SASSERT(!m_reinit); m_num_literals = n; } void set_justification(justification * new_js) { SASSERT(m_has_justification); SASSERT(!m_reinit); SASSERT(!is_lemma() || new_js == 0 || !new_js->in_region()); justification ** js_addr = const_cast(get_justification_addr()); *js_addr = new_js; } void release_atoms(ast_manager & m); public: static clause * mk(ast_manager & m, unsigned num_lits, literal * lits, clause_kind k, justification * js = nullptr, clause_del_eh * del_eh = nullptr, bool save_atoms = false, expr * const * bool_var2expr_map = nullptr); void deallocate(ast_manager & m); clause_kind get_kind() const { return static_cast(m_kind); } bool is_lemma() const { return smt::is_lemma(get_kind()); } bool is_learned() const { return get_kind() == CLS_LEARNED; } bool is_th_lemma() const { return get_kind() == CLS_TH_LEMMA; } bool in_reinit_stack() const { return m_reinit; } bool reinternalize_atoms() const { return m_reinternalize_atoms; } unsigned get_num_literals() const { return m_num_literals; } literal & operator[](unsigned idx) { SASSERT(idx < m_num_literals); return m_lits[idx]; } literal operator[](unsigned idx) const { SASSERT(idx < m_num_literals); return m_lits[idx]; } literal get_literal(unsigned idx) const { return (*this)[idx]; } literal & get_literal(unsigned idx) { return (*this)[idx]; } literal * begin() { return m_lits; } literal * end() { return m_lits + m_num_literals; } literal const * begin() const { return m_lits; } literal const * end() const { return m_lits + m_num_literals; } unsigned get_activity() const { SASSERT(is_lemma()); return *(get_activity_addr()); } void set_activity(unsigned act) { SASSERT(is_lemma()); *(get_activity_addr()) = act; } clause_del_eh * get_del_eh() const { return m_has_del_eh ? *(get_del_eh_addr()) : nullptr; } justification * get_justification() const { return m_has_justification ? *(get_justification_addr()) : nullptr; } unsigned get_num_atoms() const { return m_reinternalize_atoms ? m_num_literals : 0; } expr * get_atom(unsigned idx) const { SASSERT(idx < get_num_atoms()); return UNTAG(expr*, get_atoms_addr()[idx]); } bool get_atom_sign(unsigned idx) const { SASSERT(idx < get_num_atoms()); return GET_TAG(get_atoms_addr()[idx]) == 1; } bool erase_atom(unsigned idx); void inc_clause_activity() { SASSERT(is_lemma()); set_activity(get_activity() + 1); } 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; std::ostream& display_compact(std::ostream & out, ast_manager & m, expr * const * bool_var2expr_map) const; unsigned hash() const { return get_ptr_hash(this); } void mark_as_deleted(ast_manager & m) { SASSERT(!m_deleted); m_deleted = true; clause_del_eh * del_eh = get_del_eh(); if (del_eh) { (*del_eh)(m, this); *(const_cast(get_del_eh_addr())) = nullptr; } } bool deleted() const { return m_deleted; } }; typedef ptr_vector clause_vector; typedef obj_hashtable clause_set; }; #endif /* SMT_CLAUSE_H_ */ z3-z3-4.8.7/src/smt/smt_clause_proof.cpp000066400000000000000000000121611356505360400200720ustar00rootroot00000000000000/*++ Copyright (c) 2019 Microsoft Corporation Module Name: smt_clause_proof.cpp Author: Nikolaj Bjorner (nbjorner) 2019-03-15 Revision History: --*/ #include "smt/smt_clause_proof.h" #include "smt/smt_context.h" #include "ast/ast_pp.h" namespace smt { clause_proof::clause_proof(context& ctx): ctx(ctx), m(ctx.get_manager()), m_lits(m) {} clause_proof::status clause_proof::kind2st(clause_kind k) { switch (k) { case CLS_AUX: return status::assumption; case CLS_TH_AXIOM: return status::th_assumption; case CLS_LEARNED: return status::lemma; case CLS_TH_LEMMA: return status::th_lemma; default: UNREACHABLE(); return status::lemma; } } proof* clause_proof::justification2proof(justification* j) { return (m.proofs_enabled() && j) ? j->mk_proof(ctx.get_cr()) : nullptr; } void clause_proof::add(clause& c) { if (ctx.get_fparams().m_clause_proof) { justification* j = c.get_justification(); proof* pr = justification2proof(j); update(c, kind2st(c.get_kind()), pr); } } void clause_proof::add(unsigned n, literal const* lits, clause_kind k, justification* j) { if (ctx.get_fparams().m_clause_proof) { proof* pr = justification2proof(j); m_lits.reset(); for (unsigned i = 0; i < n; ++i) { literal lit = lits[i]; m_lits.push_back(ctx.literal2expr(lit)); } update(kind2st(k), m_lits, pr); } } void clause_proof::shrink(clause& c, unsigned new_size) { if (ctx.get_fparams().m_clause_proof) { m_lits.reset(); for (unsigned i = 0; i < new_size; ++i) { m_lits.push_back(ctx.literal2expr(c[i])); } update(status::lemma, m_lits, nullptr); for (unsigned i = new_size; i < c.get_num_literals(); ++i) { m_lits.push_back(ctx.literal2expr(c[i])); } update(status::deleted, m_lits, nullptr); } } void clause_proof::add(literal lit, clause_kind k, justification* j) { if (ctx.get_fparams().m_clause_proof) { m_lits.reset(); m_lits.push_back(ctx.literal2expr(lit)); proof* pr = justification2proof(j); update(kind2st(k), m_lits, pr); } } void clause_proof::add(literal lit1, literal lit2, clause_kind k, justification* j) { if (ctx.get_fparams().m_clause_proof) { m_lits.reset(); m_lits.push_back(ctx.literal2expr(lit1)); m_lits.push_back(ctx.literal2expr(lit2)); proof* pr = justification2proof(j); m_trail.push_back(info(kind2st(k), m_lits, pr)); } } void clause_proof::del(clause& c) { update(c, status::deleted, nullptr); } void clause_proof::update(status st, expr_ref_vector& v, proof* p) { TRACE("clause_proof", tout << st << " " << v << "\n";); IF_VERBOSE(3, verbose_stream() << st << " " << v << "\n"); m_trail.push_back(info(st, v, p)); } void clause_proof::update(clause& c, status st, proof* p) { if (ctx.get_fparams().m_clause_proof) { m_lits.reset(); for (literal lit : c) { m_lits.push_back(ctx.literal2expr(lit)); } update(st, m_lits, p); } } proof_ref clause_proof::get_proof() { TRACE("context", tout << "get-proof " << ctx.get_fparams().m_clause_proof << "\n";); if (!ctx.get_fparams().m_clause_proof) { return proof_ref(m); } proof_ref_vector ps(m); for (auto& info : m_trail) { expr_ref fact = mk_or(info.m_clause); proof* pr = info.m_proof; switch (info.m_status) { case status::assumption: ps.push_back(m.mk_assumption_add(pr, fact)); break; case status::lemma: ps.push_back(m.mk_lemma_add(pr, fact)); break; case status::th_assumption: ps.push_back(m.mk_th_assumption_add(pr, fact)); break; case status::th_lemma: ps.push_back(m.mk_th_lemma_add(pr, fact)); break; case status::deleted: ps.push_back(m.mk_redundant_del(fact)); break; } } return proof_ref(m.mk_clause_trail(ps.size(), ps.c_ptr()), m); } std::ostream& operator<<(std::ostream& out, clause_proof::status st) { switch (st) { case clause_proof::status::assumption: return out << "asm"; case clause_proof::status::th_assumption: return out << "th_asm"; case clause_proof::status::lemma: return out << "lem"; case clause_proof::status::th_lemma: return out << "th_lem"; case clause_proof::status::deleted: return out << "del"; default: return out << "unkn"; } } }; z3-z3-4.8.7/src/smt/smt_clause_proof.h000066400000000000000000000042021356505360400175340ustar00rootroot00000000000000/*++ Copyright (c) 2019 Microsoft Corporation Module Name: smt_clause_proof.h Abstract: This module tracks clausal proof objects as a trail of added and removed assumptions (input clauses) theory lemmas and axioms, and lemmas produced from conflict resolution (possibly using theory propagation). Clausal proofs may serve a set of purposes: - detailed diagnostics of general properties of the search. - an interface to proof checking - an interface to replay in trusted bases - an interface to proof pruning methods - an interface to clausal interpolation methods. Author: Nikolaj Bjorner (nbjorner) 2019-03-15 Revision History: --*/ #ifndef SMT_CLAUSE_PROOF_H_ #define SMT_CLAUSE_PROOF_H_ #include "smt/smt_theory.h" #include "smt/smt_clause.h" namespace smt { class context; class justification; class clause_proof { public: enum status { lemma, assumption, th_lemma, th_assumption, deleted }; private: struct info { status m_status; expr_ref_vector m_clause; proof_ref m_proof; info(status st, expr_ref_vector& v, proof* p): m_status(st), m_clause(v), m_proof(p, m_clause.m()) {} }; context& ctx; ast_manager& m; expr_ref_vector m_lits; vector m_trail; void update(status st, expr_ref_vector& v, proof* p); void update(clause& c, status st, proof* p); status kind2st(clause_kind k); proof* justification2proof(justification* j); public: clause_proof(context& ctx); void shrink(clause& c, unsigned new_size); void add(literal lit, clause_kind k, justification* j); void add(literal lit1, literal lit2, clause_kind k, justification* j); void add(clause& c); void add(unsigned n, literal const* lits, clause_kind k, justification* j); void del(clause& c); proof_ref get_proof(); }; std::ostream& operator<<(std::ostream& out, clause_proof::status st); }; #endif /* SMT_CLAUSE_PROOF_H_ */ z3-z3-4.8.7/src/smt/smt_conflict_resolution.cpp000066400000000000000000001625461356505360400215120ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_conflict_resolution.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-02-25. Revision History: --*/ #include "ast/ast_pp.h" #include "ast/ast_ll_pp.h" #include "smt/smt_context.h" #include "smt/smt_conflict_resolution.h" namespace smt { // --------------------------- // // Base class // // --------------------------- conflict_resolution::conflict_resolution(ast_manager & m, context & ctx, dyn_ack_manager & dyn_ack_manager, smt_params const & params, literal_vector const & assigned_literals, vector & watches ): m_manager(m), m_params(params), m_ctx(ctx), m_dyn_ack_manager(dyn_ack_manager), m_assigned_literals(assigned_literals), m_lemma_atoms(m), m_todo_js_qhead(0), m_antecedents(nullptr), m_watches(watches), m_new_proofs(m), m_lemma_proof(m) { } /** \brief Mark all enodes in a 'proof' tree branch starting at n n -> ... -> root */ template void conflict_resolution::mark_enodes_in_trans(enode * n) { SASSERT(n->trans_reaches(n->get_root())); while (n) { if (Set) n->set_mark2(); else n->unset_mark2(); n = n->m_trans.m_target; } } /** \brief Find a common ancestor (anc) of n1 and n2 in the 'proof' tree. The common ancestor is used to produce irredundant transitivity proofs. n1 = a1 = ... = ai = ANC = ... = root n2 = b1 = ... = bj = ANC = ... = root The equalities ANC = ... = root should not be included in the proof of n1 = n2. The irredundant proof for n1 = n2 is: n1 = a1 = ... = ai = ANC = bj = ... = b1 = n2 */ enode * conflict_resolution::find_common_ancestor(enode * n1, enode * n2) { SASSERT(n1->get_root() == n2->get_root()); mark_enodes_in_trans(n1); while (true) { SASSERT(n2); if (n2->is_marked2()) { mark_enodes_in_trans(n1); return n2; } n2 = n2->m_trans.m_target; } } /** \brief Process a eq_justification of lhs and rhs. This method executes a step of eq_justification2literals This method may update m_antecedents, m_todo_js and m_todo_eqs. */ void conflict_resolution::eq_justification2literals(enode * lhs, enode * rhs, eq_justification js) { SASSERT(m_antecedents); TRACE("conflict_detail", ast_manager& m = get_manager(); tout << mk_pp(lhs->get_owner(), m) << " = " << mk_pp(rhs->get_owner(), m); switch (js.get_kind()) { case eq_justification::AXIOM: tout << " axiom\n"; break; case eq_justification::EQUATION: tout << " was asserted\nliteral: "; m_ctx.display_literal(tout, js.get_literal()); tout << "\n"; break; case eq_justification::JUSTIFICATION: tout << " justification\n"; break; case eq_justification::CONGRUENCE: tout << " congruence\n"; break; default: break; }); switch(js.get_kind()) { case eq_justification::AXIOM: break; case eq_justification::EQUATION: m_antecedents->push_back(js.get_literal()); break; case eq_justification::JUSTIFICATION: mark_justification(js.get_justification()); break; case eq_justification::CONGRUENCE: { CTRACE("dyn_ack_target", !lhs->is_eq(), tout << "dyn_ack_target2: " << lhs->get_owner_id() << " " << rhs->get_owner_id() << "\n";); m_dyn_ack_manager.used_cg_eh(lhs->get_owner(), rhs->get_owner()); unsigned num_args = lhs->get_num_args(); SASSERT(num_args == rhs->get_num_args()); if (js.used_commutativity()) { SASSERT(num_args == 2); mark_eq(lhs->get_arg(0), rhs->get_arg(1)); mark_eq(lhs->get_arg(1), rhs->get_arg(0)); } else { for (unsigned i = 0; i < num_args; i++) mark_eq(lhs->get_arg(i), rhs->get_arg(i)); } break; } default: UNREACHABLE(); } } /** \brief Process the transitivity 'proof' from n1 and n2, where n1 and n2 are in the same branch n1 -> ... -> n2 This method may update m_antecedents, m_todo_js and m_todo_eqs. The resultant set of literals is stored in m_antecedents. */ void conflict_resolution::eq_branch2literals(enode * n1, enode * n2) { SASSERT(n1->trans_reaches(n2)); while (n1 != n2) { eq_justification2literals(n1, n1->m_trans.m_target, n1->m_trans.m_justification); n1 = n1->m_trans.m_target; } } /** \brief Process the justification of n1 = n2. This method may update m_antecedents, m_todo_js and m_todo_eqs. The resultant set of literals is stored in m_antecedents. */ void conflict_resolution::eq2literals(enode * n1, enode * n2) { enode * c = find_common_ancestor(n1, n2); eq_branch2literals(n1, c); eq_branch2literals(n2, c); m_dyn_ack_manager.used_eq_eh(n1->get_owner(), n2->get_owner(), c->get_owner()); } /** \brief Extract the antecedent literals from a justification object. The result is stored in result. \remark This method does not reset the vectors m_antecedents, m_todo_js, m_todo_eqs, nor reset the marks in the justification objects. */ void conflict_resolution::justification2literals_core(justification * js, literal_vector & result) { SASSERT(m_todo_js_qhead <= m_todo_js.size()); m_antecedents = &result; mark_justification(js); process_justifications(); } void conflict_resolution::process_justifications() { while (true) { unsigned sz = m_todo_js.size(); while (m_todo_js_qhead < sz) { justification * js = m_todo_js[m_todo_js_qhead]; m_todo_js_qhead++; js->get_antecedents(*this); } while (!m_todo_eqs.empty()) { enode_pair p = m_todo_eqs.back(); m_todo_eqs.pop_back(); eq2literals(p.first, p.second); } if (m_todo_js_qhead == m_todo_js.size()) { m_antecedents = nullptr; return; } } } /** \brief Unset the mark of the justifications stored in m_todo_js */ void conflict_resolution::unmark_justifications(unsigned old_js_qhead) { SASSERT(old_js_qhead <= m_todo_js.size()); justification_vector::iterator it = m_todo_js.begin() + old_js_qhead; justification_vector::iterator end = m_todo_js.end(); for (; it != end; ++it) { (*it)->unset_mark(); } m_todo_js.shrink(old_js_qhead); m_todo_js_qhead = old_js_qhead; m_todo_eqs.reset(); m_already_processed_eqs.reset(); } /** \brief Extract the antecedent literals from a justification object. */ void conflict_resolution::justification2literals(justification * js, literal_vector & result) { SASSERT(m_todo_js.empty()); SASSERT(m_todo_js_qhead == 0); SASSERT(m_todo_eqs.empty()); justification2literals_core(js, result); unmark_justifications(0); SASSERT(m_todo_eqs.empty()); } void conflict_resolution::eq2literals(enode* n1, enode* n2, literal_vector & result) { SASSERT(m_todo_js.empty()); SASSERT(m_todo_js_qhead == 0); SASSERT(m_todo_eqs.empty()); m_antecedents = &result; m_todo_eqs.push_back(enode_pair(n1, n2)); process_justifications(); unmark_justifications(0); SASSERT(m_todo_eqs.empty()); } /** \brief Return maximum scope level of an antecedent literal of js. */ unsigned conflict_resolution::get_justification_max_lvl(justification * js) { unsigned r = 0; literal_vector & antecedents = m_tmp_literal_vector; antecedents.reset(); justification2literals(js, antecedents); for (literal lit : antecedents) r = std::max(r, m_ctx.get_assign_level(lit)); return r; } /** \brief Return the maximum scope level of the antecedent literals of the given justified literal. */ unsigned conflict_resolution::get_max_lvl(literal consequent, b_justification js) { unsigned r = 0; if (consequent != false_literal) r = m_ctx.get_assign_level(consequent); switch (js.get_kind()) { case b_justification::CLAUSE: { clause * cls = js.get_clause(); unsigned num_lits = cls->get_num_literals(); unsigned i = 0; if (consequent != false_literal) { SASSERT((*cls)[0] == consequent || (*cls)[1] == consequent); if ((*cls)[0] == consequent) { i = 1; } else { r = std::max(r, m_ctx.get_assign_level((*cls)[0])); i = 2; } } for(; i < num_lits; i++) r = std::max(r, m_ctx.get_assign_level((*cls)[i])); justification * js = cls->get_justification(); if (js) r = std::max(r, get_justification_max_lvl(js)); break; } case b_justification::BIN_CLAUSE: r = std::max(r, m_ctx.get_assign_level(js.get_literal())); break; case b_justification::AXIOM: break; case b_justification::JUSTIFICATION: r = std::max(r, get_justification_max_lvl(js.get_justification())); break; default: UNREACHABLE(); } return r; } void conflict_resolution::process_antecedent(literal antecedent, unsigned & num_marks) { 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 << "):"; m_ctx.display_literal(tout, antecedent); m_ctx.display_detailed_literal(tout << " ", antecedent) << "\n";); if (!m_ctx.is_marked(var) && lvl > m_ctx.get_base_level()) { m_ctx.set_mark(var); m_ctx.inc_bvar_activity(var); expr * n = m_ctx.bool_var2expr(var); if (is_app(n)) { family_id fid = to_app(n)->get_family_id(); theory * th = m_ctx.get_theory(fid); if (th) th->conflict_resolution_eh(to_app(n), var); } if (get_manager().has_trace_stream()) { get_manager().trace_stream() << "[resolve-lit] " << m_conflict_lvl - lvl << " "; m_ctx.display_literal(get_manager().trace_stream(), ~antecedent) << "\n"; } if (lvl == m_conflict_lvl) { TRACE("conflict", m_ctx.display_literal(tout << "marking:", antecedent) << "\n";); num_marks++; } else { m_lemma.push_back(~antecedent); m_lemma_atoms.push_back(m_ctx.bool_var2expr(var)); } } } void conflict_resolution::process_justification(justification * js, unsigned & num_marks) { literal_vector & antecedents = m_tmp_literal_vector; antecedents.reset(); justification2literals_core(js, antecedents); for (literal l : antecedents) process_antecedent(l, num_marks); } /** \brief Skip literals from levels above m_conflict_lvl. It returns an index idx such that m_assigned_literals[idx] <= m_conflict_lvl, and for all idx' > idx, m_assigned_literals[idx'] > m_conflict_lvl */ unsigned conflict_resolution::skip_literals_above_conflict_level() { unsigned idx = m_assigned_literals.size(); if (idx == 0) { return idx; } idx--; // skip literals from levels above the conflict level while (m_ctx.get_assign_level(m_assigned_literals[idx]) > m_conflict_lvl) { SASSERT(idx > 0); idx--; } return idx; } /** \brief Initialize conflict resolution data-structures. Return false if the conflict cannot be resolved (it is at the search level). */ bool conflict_resolution::initialize_resolve(b_justification conflict, literal not_l, b_justification & js, literal & consequent) { TRACE("conflict_detail", m_ctx.display(tout);); m_lemma.reset(); m_lemma_atoms.reset(); SASSERT(m_ctx.get_search_level() >= m_ctx.get_base_level()); js = conflict; consequent = false_literal; if (not_l != null_literal) consequent = ~not_l; m_conflict_lvl = get_max_lvl(consequent, js); TRACE("conflict_bug", tout << "conflict_lvl: " << m_conflict_lvl << " scope_lvl: " << m_ctx.get_scope_level() << " base_lvl: " << m_ctx.get_base_level() << " search_lvl: " << m_ctx.get_search_level() << "\n"; tout << "js.kind: " << js.get_kind() << "\n"; tout << "consequent: " << consequent << ": "; m_ctx.display_literal_verbose(tout, consequent) << "\n"; m_ctx.display(tout, js); tout << "\n"; ); // m_conflict_lvl can be smaller than m_ctx.get_search_level() when: // there are user level scopes created using the Z3 API, and // the previous levels were already inconsistent, or the inconsistency was // triggered by an axiom or justification proof wrapper, this two kinds // of justification are considered level zero. if (m_conflict_lvl <= m_ctx.get_search_level()) { TRACE("conflict", tout << "problem is unsat\n";); if (m_manager.proofs_enabled()) mk_conflict_proof(conflict, not_l); if (m_ctx.tracking_assumptions()) mk_unsat_core(conflict, not_l); return false; } TRACE("conflict", tout << "conflict_lvl: " << m_conflict_lvl << "\n";); SASSERT(!m_assigned_literals.empty()); SASSERT(m_todo_js.empty()); SASSERT(m_todo_js_qhead == 0); SASSERT(m_todo_eqs.empty()); return true; } /** \brief Cleanup datastructures used during resolve(), minimize lemma (when minimization is enabled), compute m_new_scope_lvl and m_lemma_iscope_lvl, generate proof if needed. This method assumes that the lemma is stored in m_lemma (and the associated atoms in m_lemma_atoms). \warning This method assumes the literals in m_lemma[1] ... m_lemma[m_lemma.size() - 1] are marked. */ 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" << m_lemma << "\n";); TRACE("conflict_verbose",m_ctx.display_literals_verbose(tout << "before minimization:\n", m_lemma) << "\n";); if (m_params.m_minimize_lemmas) minimize_lemma(); TRACE("conflict", m_ctx.display_literals(tout << "after minimization:\n", m_lemma) << "\n";); TRACE("conflict_verbose", m_ctx.display_literals_verbose(tout << "after minimization:\n", m_lemma) << "\n";); TRACE("conflict_bug", m_ctx.display_literals_verbose(tout, m_lemma) << "\n";); literal_vector::iterator it = m_lemma.begin(); literal_vector::iterator end = m_lemma.end(); m_new_scope_lvl = m_ctx.get_search_level(); m_lemma_iscope_lvl = m_ctx.get_intern_level((*it).var()); SASSERT(!m_ctx.is_marked((*it).var())); ++it; for(; it != end; ++it) { bool_var var = (*it).var(); if (var != null_bool_var) { m_ctx.unset_mark(var); unsigned lvl = m_ctx.get_assign_level(var); if (lvl > m_new_scope_lvl) m_new_scope_lvl = lvl; lvl = m_ctx.get_intern_level(var); if (lvl > m_lemma_iscope_lvl) m_lemma_iscope_lvl = lvl; } } TRACE("conflict", tout << "new scope level: " << m_new_scope_lvl << "\n"; tout << "intern. scope level: " << m_lemma_iscope_lvl << "\n";); if (m_manager.proofs_enabled()) mk_conflict_proof(conflict, not_l); } bool conflict_resolution::resolve(b_justification conflict, literal not_l) { b_justification js; literal consequent; if (!initialize_resolve(conflict, not_l, js, consequent)) { return false; } unsigned idx = skip_literals_above_conflict_level(); TRACE("conflict", m_ctx.display_literal_verbose(tout, not_l); m_ctx.display(tout << " ", conflict);); // save space for first uip m_lemma.push_back(null_literal); m_lemma_atoms.push_back(nullptr); unsigned num_marks = 0; if (not_l != null_literal) { TRACE("conflict", tout << "not_l: "; m_ctx.display_literal_verbose(tout, not_l) << "\n";); process_antecedent(not_l, num_marks); } do { if (get_manager().has_trace_stream()) { get_manager().trace_stream() << "[resolve-process] "; m_ctx.display_literal(get_manager().trace_stream(), ~consequent); get_manager().trace_stream() << "\n"; } TRACE("conflict", tout << "processing consequent id: " << idx << " lit: " << consequent << " "; m_ctx.display_literal(tout, consequent); 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); switch (js.get_kind()) { case b_justification::CLAUSE: { clause * cls = js.get_clause(); TRACE("conflict", m_ctx.display_clause_detail(tout, cls);); TRACE("conflict", tout << literal_vector(cls->get_num_literals(), cls->begin()) << "\n";); if (cls->is_lemma()) cls->inc_clause_activity(); unsigned num_lits = cls->get_num_literals(); unsigned i = 0; if (consequent != false_literal) { SASSERT((*cls)[0] == consequent || (*cls)[1] == consequent); if ((*cls)[0] == consequent) { i = 1; } else { literal l = (*cls)[0]; SASSERT(consequent.var() != l.var()); process_antecedent(~l, num_marks); i = 2; } } for(; i < num_lits; i++) { literal l = (*cls)[i]; SASSERT(consequent.var() != l.var()); process_antecedent(~l, num_marks); } justification * js = cls->get_justification(); if (js) process_justification(js, num_marks); break; } case b_justification::BIN_CLAUSE: SASSERT(consequent.var() != js.get_literal().var()); process_antecedent(js.get_literal(), num_marks); break; case b_justification::AXIOM: break; case b_justification::JUSTIFICATION: process_justification(js.get_justification(), num_marks); break; default: UNREACHABLE(); } while (true) { literal l = m_assigned_literals[idx]; if (m_ctx.is_marked(l.var())) break; CTRACE("conflict", m_ctx.get_assign_level(l) != m_conflict_lvl && m_ctx.get_assign_level(l) != m_ctx.get_base_level(), tout << "assign_level(l): " << m_ctx.get_assign_level(l) << ", conflict_lvl: "; tout << m_conflict_lvl << ", l: "; m_ctx.display_literal_verbose(tout, l); tout << "\n"; tout << "num marks: " << num_marks << "\n";); SASSERT(m_ctx.get_assign_level(l) == m_conflict_lvl || // it may also be an (out-of-order) asserted literal m_ctx.get_assign_level(l) == m_ctx.get_base_level()); SASSERT(idx > 0); idx--; } consequent = m_assigned_literals[idx]; bool_var c_var = consequent.var(); SASSERT(m_ctx.get_assign_level(c_var) == m_conflict_lvl); js = m_ctx.get_justification(c_var); idx--; num_marks--; m_ctx.unset_mark(c_var); } while (num_marks > 0); TRACE("conflict", tout << "FUIP: "; m_ctx.display_literal(tout, consequent); tout << "\n";); m_lemma[0] = ~consequent; m_lemma_atoms.set(0, m_ctx.bool_var2expr(consequent.var())); // TODO: // // equality optimization should go here. // finalize_resolve(conflict, not_l); return true; } /** \brief Return an approximation for the set of scope levels where the literals in m_lemma were assigned. */ level_approx_set conflict_resolution::get_lemma_approx_level_set() { level_approx_set result; for (literal l : m_lemma) result.insert(m_ctx.get_assign_level(l)); return result; } /** \brief Restore the size of m_unmark to old_size, and unmark literals at positions [old_size, m_unmark.size()). */ void conflict_resolution::reset_unmark(unsigned old_size) { unsigned curr_size = m_unmark.size(); for(unsigned i = old_size; i < curr_size; i++) m_ctx.unset_mark(m_unmark[i]); m_unmark.shrink(old_size); } /** \brief Restore the size of m_unmark to old_size, and unmark literals at positions [old_size, m_unmark.size()). And unmark all justifications at positions [old_js_qhead, m_todo_js.size()). */ void conflict_resolution::reset_unmark_and_justifications(unsigned old_size, unsigned old_js_qhead) { reset_unmark(old_size); unmark_justifications(old_js_qhead); } /** \brief Process an antecedent for lemma minimization. */ bool conflict_resolution::process_antecedent_for_minimization(literal antecedent) { bool_var var = antecedent.var(); unsigned lvl = m_ctx.get_assign_level(var); if (!m_ctx.is_marked(var) && lvl > m_ctx.get_base_level()) { if (m_lvl_set.may_contain(lvl)) { m_ctx.set_mark(var); m_unmark.push_back(var); m_lemma_min_stack.push_back(var); } else { return false; } } return true; } bool conflict_resolution::process_justification_for_minimization(justification * js) { literal_vector & antecedents = m_tmp_literal_vector; antecedents.reset(); // Invoking justification2literals_core will not reset the caches for visited justifications and eqs. // The method unmark_justifications must be invoked to reset these caches. // Remark: The method reset_unmark_and_justifications invokes unmark_justifications. justification2literals_core(js, antecedents); for (literal l : antecedents) if (!process_antecedent_for_minimization(l)) return false; return true; } /** \brief Return true if lit is implied by other marked literals and/or literals assigned at the base level. The set lvl_set is used as an optimization. The idea is to stop the recursive search with a failure as soon as we find a literal assigned in a level that is not in lvl_set. */ bool conflict_resolution::implied_by_marked(literal lit) { m_lemma_min_stack.reset(); // avoid recursive function m_lemma_min_stack.push_back(lit.var()); unsigned old_size = m_unmark.size(); unsigned old_js_qhead = m_todo_js_qhead; while (!m_lemma_min_stack.empty()) { bool_var var = m_lemma_min_stack.back(); m_lemma_min_stack.pop_back(); b_justification js = m_ctx.get_justification(var); SASSERT(js != null_b_justification); switch(js.get_kind()) { case b_justification::CLAUSE: { clause * cls = js.get_clause(); unsigned num_lits = cls->get_num_literals(); unsigned pos = (*cls)[1].var() == var; for (unsigned i = 0; i < num_lits; i++) { if (pos != i) { literal l = (*cls)[i]; SASSERT(l.var() != var); if (!process_antecedent_for_minimization(~l)) { reset_unmark_and_justifications(old_size, old_js_qhead); return false; } } } justification * js = cls->get_justification(); if (js && !process_justification_for_minimization(js)) { reset_unmark_and_justifications(old_size, old_js_qhead); return false; } break; } case b_justification::BIN_CLAUSE: if (!process_antecedent_for_minimization(js.get_literal())) { reset_unmark_and_justifications(old_size, old_js_qhead); return false; } break; case b_justification::AXIOM: // it is a decision variable from a previous scope level or an assumption if (m_ctx.get_assign_level(var) > m_ctx.get_base_level()) { reset_unmark_and_justifications(old_size, old_js_qhead); return false; } break; case b_justification::JUSTIFICATION: if (m_ctx.is_assumption(var) || !process_justification_for_minimization(js.get_justification())) { reset_unmark_and_justifications(old_size, old_js_qhead); return false; } break; } } return true; } /** \brief Minimize the number of literals in learned_clause_lits. The main idea is to remove literals that are implied by other literals in m_lemma and/or literals assigned in the base levels. */ void conflict_resolution::minimize_lemma() { m_unmark.reset(); m_lvl_set = get_lemma_approx_level_set(); unsigned sz = m_lemma.size(); unsigned i = 1; // the first literal is the FUIP unsigned j = 1; for (; i < sz; i++) { literal l = m_lemma[i]; if (implied_by_marked(l)) { m_unmark.push_back(l.var()); } else { if (j != i) { m_lemma[j] = m_lemma[i]; m_lemma_atoms.set(j, m_lemma_atoms.get(i)); } j++; } } reset_unmark_and_justifications(0, 0); m_lemma .shrink(j); m_lemma_atoms.shrink(j); m_ctx.m_stats.m_num_minimized_lits += sz - j; } /** \brief Return the proof object associated with the equality (= n1 n2) if it already exists. Otherwise, return 0 and add p to the todo-list. */ proof * conflict_resolution::get_proof(enode * n1, enode * n2) { SASSERT(n1 != n2); proof * pr; if (m_eq2proof.find(n1, n2, pr)) { TRACE("proof_gen_bug", tout << "eq2_pr_cached: #" << n1->get_owner_id() << " #" << n2->get_owner_id() << "\n";); return pr; } m_todo_pr.push_back(tp_elem(n1, n2)); return nullptr; } /** \brief Apply symmetry if pr is a proof of (= n2 n1). */ proof * conflict_resolution::norm_eq_proof(enode * n1, enode * n2, proof * pr) { if (!pr) return nullptr; SASSERT(m_manager.has_fact(pr)); app * fact = to_app(m_manager.get_fact(pr)); app * n1_owner = n1->get_owner(); app * n2_owner = n2->get_owner(); bool is_eq = m_manager.is_eq(fact); if (!is_eq || (fact->get_arg(0) != n2_owner && fact->get_arg(1) != n2_owner)) { CTRACE("norm_eq_proof_bug", !m_ctx.is_true(n2) && !m_ctx.is_false(n2), tout << "n1: #" << n1->get_owner_id() << ", n2: #" << n2->get_owner_id() << "\n"; if (fact->get_num_args() == 2) { tout << "fact(0): #" << fact->get_arg(0)->get_id() << ", fact(1): #" << fact->get_arg(1)->get_id() << "\n"; } tout << mk_bounded_pp(n1->get_owner(), m_manager, 10) << "\n"; tout << mk_bounded_pp(n2->get_owner(), m_manager, 10) << "\n"; tout << mk_bounded_pp(fact, m_manager, 10) << "\n"; tout << mk_ll_pp(pr, m_manager, true, false);); SASSERT(m_ctx.is_true(n2) || m_ctx.is_false(n2)); SASSERT(fact == n1_owner || (m_manager.is_not(fact) && fact->get_arg(0) == n1_owner)); if (m_ctx.is_true(n2)) pr = m_manager.mk_iff_true(pr); else pr = m_manager.mk_iff_false(pr); m_new_proofs.push_back(pr); return pr; } TRACE("norm_eq_proof", tout << "#" << n1->get_owner_id() << " = #" << n2->get_owner_id() << "\n"; tout << mk_ll_pp(pr, m_manager, true, false);); SASSERT(m_manager.is_eq(fact)); SASSERT((fact->get_arg(0) == n1->get_owner() && fact->get_arg(1) == n2->get_owner()) || (fact->get_arg(1) == n1->get_owner() && fact->get_arg(0) == n2->get_owner())); if (fact->get_arg(0) == n1_owner && fact->get_arg(1) == n2_owner) return pr; pr = m_manager.mk_symmetry(pr); m_new_proofs.push_back(pr); return pr; } /** \brief Return a proof object for n1 = n2 using the given eq_justification if its antecedents are already available. Otherwise, return 0 and add the missing antecedents to the todo-list. */ proof * conflict_resolution::get_proof(enode * n1, enode * n2, eq_justification js) { unsigned num_args; switch (js.get_kind()) { case eq_justification::AXIOM: UNREACHABLE(); return nullptr; case eq_justification::EQUATION: TRACE("proof_gen_bug", tout << js.get_literal() << "\n"; m_ctx.display_literal_info(tout, js.get_literal());); return norm_eq_proof(n1, n2, get_proof(js.get_literal())); case eq_justification::JUSTIFICATION: return norm_eq_proof(n1, n2, get_proof(js.get_justification())); case eq_justification::CONGRUENCE: num_args = n1->get_num_args(); SASSERT(num_args == n2->get_num_args()); SASSERT(n1->get_owner()->get_decl() == n2->get_owner()->get_decl()); if (js.used_commutativity()) { bool visited = true; SASSERT(num_args == 2); enode * c1_1 = n1->get_arg(0); enode * c1_2 = n1->get_arg(1); enode * c2_1 = n2->get_arg(0); enode * c2_2 = n2->get_arg(1); ptr_buffer prs; if (c1_1 != c2_2) { proof * pr = get_proof(c1_1, c2_2); prs.push_back(pr); if (!pr) visited = false; } if (c1_2 != c2_1) { proof * pr = get_proof(c1_2, c2_1); prs.push_back(pr); if (!pr) visited = false; } if (!visited) return nullptr; app * e1 = n1->get_owner(); app * e2 = n2->get_owner(); app * e2_prime = m_manager.mk_app(e2->get_decl(), e2->get_arg(1), e2->get_arg(0)); proof * pr1 = nullptr; if (!prs.empty()) { pr1 = m_manager.mk_congruence(e1, e2_prime, prs.size(), prs.c_ptr()); m_new_proofs.push_back(pr1); } else { TRACE("comm_proof_bug", tout << "e1: #" << e1->get_id() << " e2: #" << e2->get_id() << "\n" << mk_bounded_pp(e1, m_manager, 10) << "\n" << mk_bounded_pp(e2, m_manager, 10) << "\n";); // SASSERT(e1 == e2); } proof * pr2 = m_manager.mk_commutativity(e2_prime); m_new_proofs.push_back(pr2); return m_manager.mk_transitivity(pr1, pr2); } else { bool visited = true; ptr_buffer prs; for (unsigned i = 0; i < num_args; i++) { enode * c1 = n1->get_arg(i); enode * c2 = n2->get_arg(i); if (c1 != c2) { proof * pr = get_proof(c1, c2); prs.push_back(pr); if (!pr) visited = false; } } if (!visited) return nullptr; proof * pr = m_manager.mk_congruence(n1->get_owner(), n2->get_owner(), prs.size(), prs.c_ptr()); m_new_proofs.push_back(pr); return pr; } default: UNREACHABLE(); return nullptr; } } /** \brief Return the proof object associated with the given literal if it already exists. Otherwise, return 0 and add l to the todo-list. */ proof * conflict_resolution::get_proof(literal l) { proof * pr; if (m_lit2proof.find(l, pr)) { TRACE("proof_gen_bug", tout << "lit2pr_cached: #" << l << "\n";); return pr; } m_todo_pr.push_back(tp_elem(l)); return nullptr; } /** \brief Return a proof object for l using the given b_justification if its antecedents are already available. Otherwise, return 0 and add the missing antecedents to the todo-list. */ proof * conflict_resolution::get_proof(literal l, b_justification js) { #ifdef _TRACE static unsigned invocation_counter = 0; invocation_counter++; #define DUMP_AFTER_NUM_INVOCATIONS 213473 CTRACE("get_proof_bug_after", invocation_counter >= DUMP_AFTER_NUM_INVOCATIONS, tout << "START get_proof\n";); #endif // l is a hypothesis: if it is marked, and the justification for the variable l.var() is js. // we need the second condition, because the core builds proofs as: // // p1: is a proof of "A" // p2: is a proof of "not A" // [unit-resolution p1 p2]: false // // Let us assume that "A" was assigned first during propagation. // Then, the "resolve" method will never select "not A" as a hypothesis. // "not_A" will be the not_l argument in this method. // Since we are assuming that "A" was assigned first", m_ctx.get_justification("A") will be // p1. // // So, the test "m_ctx.get_justification(l.var()) == js" is used to check // if l was assigned before ~l. if ((m_ctx.is_marked(l.var()) && m_ctx.get_justification(l.var()) == js) || (js.get_kind() == b_justification::AXIOM)) { expr_ref l_expr(m_manager); m_ctx.literal2expr(l, l_expr); proof * pr = m_manager.mk_hypothesis(l_expr.get()); m_new_proofs.push_back(pr); return pr; } else { SASSERT(js.get_kind() != b_justification::BIN_CLAUSE); SASSERT(js.get_kind() != b_justification::AXIOM); if (js.get_kind() == b_justification::CLAUSE) { clause * cls = js.get_clause(); justification * js = cls->get_justification(); SASSERT(js); proof * pr = get_proof(js); ptr_buffer prs; bool visited = pr != nullptr; TRACE("get_proof_bug", if (pr != 0) tout << js->get_name() << "\n";); CTRACE("get_proof_bug_after", invocation_counter >= DUMP_AFTER_NUM_INVOCATIONS, if (pr != 0) tout << js->get_name() << "\n";); CTRACE("get_proof_bug_after", invocation_counter >= DUMP_AFTER_NUM_INVOCATIONS, if (pr != 0) js->display_debug_info(*this, tout);); prs.push_back(pr); unsigned num_lits = cls->get_num_literals(); unsigned i = 0; SASSERT(l == false_literal || l == cls->get_literal(0) || l == cls->get_literal(1)); if (l != false_literal) { if (cls->get_literal(0) == l) { i = 1; } else { SASSERT(l == cls->get_literal(1)); proof * pr = get_proof(~cls->get_literal(0)); prs.push_back(pr); if (!pr) visited = false; i = 2; } } for (; i < num_lits; i++) { proof * pr = get_proof(~cls->get_literal(i)); prs.push_back(pr); if (!pr) visited = false; } if (!visited) return nullptr; expr_ref l_exr(m_manager); m_ctx.literal2expr(l, l_exr); TRACE("get_proof_bug", tout << "clause:\n"; for (unsigned i = 0; i < num_lits; i++) { tout << cls->get_literal(i).index() << "\n"; expr_ref l_expr(m_manager); m_ctx.literal2expr(cls->get_literal(i), l_expr); tout << mk_pp(l_expr, m_manager) << "\n"; } tout << "antecedents:\n"; for (unsigned i = 0; i < prs.size(); i++) { tout << mk_pp(m_manager.get_fact(prs[i]), m_manager) << "\n"; } tout << "consequent:\n" << mk_pp(l_exr, m_manager) << "\n";); CTRACE("get_proof_bug_after", invocation_counter >= DUMP_AFTER_NUM_INVOCATIONS, tout << "clause, num_lits: " << num_lits << ":\n"; for (unsigned i = 0; i < num_lits; i++) { tout << cls->get_literal(i).index() << "\n"; expr_ref l_expr(m_manager); m_ctx.literal2expr(cls->get_literal(i), l_expr); tout << mk_pp(l_expr, m_manager) << "\n"; } tout << "antecedents:\n"; for (unsigned i = 0; i < prs.size(); i++) { tout << mk_pp(m_manager.get_fact(prs[i]), m_manager) << "\n"; } tout << "consequent:\n" << mk_pp(l_exr, m_manager) << "\n";); TRACE("get_proof", tout << l.index() << " " << true_literal.index() << " " << false_literal.index() << " "; m_ctx.display_literal(tout, l); tout << " --->\n"; tout << mk_ll_pp(l_exr, m_manager);); CTRACE("get_proof_bug_after", invocation_counter >= DUMP_AFTER_NUM_INVOCATIONS, tout << l.index() << " " << true_literal.index() << " " << false_literal.index() << " "; m_ctx.display_literal(tout, l); tout << " --->\n"; tout << mk_ll_pp(l_exr, m_manager);); pr = m_manager.mk_unit_resolution(prs.size(), prs.c_ptr(), l_exr); m_new_proofs.push_back(pr); return pr; } else { return get_proof(js.get_justification()); } } } /** \brief Return the proof object associated with the given justification if it already exists. Otherwise, return 0 and add js to the todo-list. */ proof * conflict_resolution::get_proof(justification * js) { proof * pr; if (m_js2proof.find(js, pr)) { TRACE("proof_gen_bug", tout << "js2pr_cached: #" << js << "\n";); return pr; } SASSERT(js != 0); TRACE("proof_gen_bug", tout << js << "\n";); m_todo_pr.push_back(tp_elem(js)); return nullptr; } void conflict_resolution::init_mk_proof() { TRACE("proof_gen_bug", tout << "reset_caches\n";); m_new_proofs.reset(); m_todo_pr.reset(); m_eq2proof.reset(); m_lit2proof.reset(); m_js2proof.reset(); for (literal lit : m_lemma) m_ctx.set_mark(lit.var()); } bool conflict_resolution::visit_b_justification(literal l, b_justification js) { // l is a hypothesis: if it is marked, and the justification for the variable l.var() is js. // See: get_proof(literal l, b_justification js) if (m_ctx.is_marked(l.var()) && m_ctx.get_justification(l.var()) == js) return true; SASSERT(js.get_kind() != b_justification::BIN_CLAUSE); CTRACE("visit_b_justification_bug", js.get_kind() == b_justification::AXIOM, tout << "l: " << l << "\n"; m_ctx.display(tout);); if (js.get_kind() == b_justification::AXIOM) return true; SASSERT(js.get_kind() != b_justification::AXIOM); if (js.get_kind() == b_justification::CLAUSE) { clause * cls = js.get_clause(); bool visited = get_proof(cls->get_justification()) != nullptr; unsigned num_lits = cls->get_num_literals(); unsigned i = 0; if (l != false_literal) { if (cls->get_literal(0) == l) { i = 1; } else { SASSERT(cls->get_literal(1) == l); if (get_proof(~cls->get_literal(0)) == nullptr) visited = false; i = 2; } } for (; i < num_lits; i++) { SASSERT(cls->get_literal(i) != l); if (get_proof(~cls->get_literal(i)) == nullptr) visited = false; } return visited; } else return get_proof(js.get_justification()) != nullptr; } void conflict_resolution::mk_proof(literal l, b_justification js) { SASSERT(!m_lit2proof.contains(l)); proof * pr = get_proof(l, js); SASSERT(pr); TRACE("proof_gen_bug", tout << "lit2pr_saved: #" << l << "\n";); m_lit2proof.insert(l, pr); TRACE("mk_proof", tout << mk_bounded_pp(m_ctx.bool_var2expr(l.var()), m_manager, 10) << "\n"; tout << "storing proof for: "; m_ctx.display_literal(tout, l); tout << "\n"; tout << mk_ll_pp(pr, m_manager);); } /** \brief Given that lhs = ... = rhs, and lhs reaches rhs in the 'proof' tree branch. Then, return true if all proof objects needed to create the proof steps are already available. Otherwise return false and update m_todo_pr with info about the proof objects that need to be created. */ bool conflict_resolution::visit_trans_proof(enode * lhs, enode * rhs) { SASSERT(lhs->trans_reaches(rhs)); bool visited = true; while (lhs != rhs) { eq_justification js = lhs->m_trans.m_justification; switch (js.get_kind()) { case eq_justification::AXIOM: UNREACHABLE(); break; case eq_justification::EQUATION: if (get_proof(js.get_literal()) == nullptr) visited = false; break; case eq_justification::JUSTIFICATION: if (get_proof(js.get_justification()) == nullptr) visited = false; break; case eq_justification::CONGRUENCE: { enode * n1 = lhs; enode * n2 = lhs->m_trans.m_target; unsigned num_args = n1->get_num_args(); SASSERT(num_args == n2->get_num_args()); if (js.used_commutativity()) { SASSERT(num_args == 2); enode * c1_1 = n1->get_arg(0); enode * c1_2 = n1->get_arg(1); enode * c2_1 = n2->get_arg(0); enode * c2_2 = n2->get_arg(1); if (c1_1 != c2_2 && get_proof(c1_1, c2_2) == nullptr) visited = false; if (c1_2 != c2_1 && get_proof(c1_2, c2_1) == nullptr) visited = false; } else { for (unsigned i = 0; i < num_args; i++) { enode * c1 = n1->get_arg(i); enode * c2 = n2->get_arg(i); if (c1 != c2 && get_proof(c1, c2) == nullptr) visited = false; } } break; } default: UNREACHABLE(); } lhs = lhs->m_trans.m_target; } return visited; } /** \brief Return true if all proof objects that are used to build the proof that lhs = rhs were already built. If the result is false, then m_todo_pr is updated with info about the proof objects that need to be created. */ bool conflict_resolution::visit_eq_justications(enode * lhs, enode * rhs) { enode * c = find_common_ancestor(lhs, rhs); bool v1 = visit_trans_proof(lhs, c); bool v2 = visit_trans_proof(rhs, c); return v1 && v2; } /** \brief Given that lhs = ... = rhs, and lhs reaches rhs in the trans proof branch, then build a proof object for each equality in the sequence, and insert them into result. */ void conflict_resolution::mk_proof(enode * lhs, enode * rhs, ptr_buffer & result) { SASSERT(lhs->trans_reaches(rhs)); while (lhs != rhs) { eq_justification js = lhs->m_trans.m_justification; enode * n1 = lhs; enode * n2 = lhs->m_trans.m_target; proof * pr = get_proof(n1, n2, js); SASSERT(pr); result.push_back(pr); lhs = lhs->m_trans.m_target; } } void conflict_resolution::mk_proof(enode * lhs, enode * rhs) { SASSERT(!m_eq2proof.contains(lhs, rhs)); enode * c = find_common_ancestor(lhs, rhs); ptr_buffer prs1; mk_proof(lhs, c, prs1); ptr_buffer prs2; mk_proof(rhs, c, prs2); while (!prs2.empty()) { proof * pr = prs2.back(); if (m_manager.proofs_enabled()) { pr = m_manager.mk_symmetry(pr); m_new_proofs.push_back(pr); prs1.push_back(pr); } else { prs1.push_back(pr); } prs2.pop_back(); } proof * pr = nullptr; SASSERT(!prs1.empty()); if (prs1.size() == 1) pr = prs1[0]; else { TRACE("mk_transitivity", unsigned sz = prs1.size(); for (unsigned i = 0; i < sz; i++) { tout << mk_ll_pp(prs1[i], m_manager) << "\n"; }); pr = m_manager.mk_transitivity(prs1.size(), prs1.c_ptr(), lhs->get_owner(), rhs->get_owner()); } m_new_proofs.push_back(pr); TRACE("proof_gen_bug", tout << "eq2pr_saved: #" << lhs->get_owner_id() << " #" << rhs->get_owner_id() << "\n";); m_eq2proof.insert(lhs, rhs, pr); } void conflict_resolution::mk_conflict_proof(b_justification conflict, literal not_l) { SASSERT(conflict.get_kind() != b_justification::BIN_CLAUSE); SASSERT(conflict.get_kind() != b_justification::AXIOM); SASSERT(not_l == null_literal || conflict.get_kind() == b_justification::JUSTIFICATION); TRACE("mk_conflict_proof", tout << "lemma literals:"; for (literal lit : m_lemma) { m_ctx.display_literal(tout << " ", lit); } tout << "\n";); init_mk_proof(); literal consequent; if (not_l == null_literal) { consequent = false_literal; } else { consequent = ~not_l; m_todo_pr.push_back(tp_elem(not_l)); } visit_b_justification(consequent, conflict); while (!m_todo_pr.empty()) { tp_elem & elem = m_todo_pr.back(); switch (elem.m_kind) { case tp_elem::EQUALITY: { enode * lhs = elem.m_lhs; enode * rhs = elem.m_rhs; if (m_eq2proof.contains(lhs, rhs)) m_todo_pr.pop_back(); else if (visit_eq_justications(lhs, rhs)) { m_todo_pr.pop_back(); mk_proof(lhs, rhs); } break; } case tp_elem::JUSTIFICATION: { justification * js = elem.m_js; if (m_js2proof.contains(js)) m_todo_pr.pop_back(); else { proof * pr = js->mk_proof(*this); if (pr) { m_todo_pr.pop_back(); m_new_proofs.push_back(pr); TRACE("proof_gen_bug", tout << "js2pr_saved: #" << js << "\n";); m_js2proof.insert(js, pr); } } break; } case tp_elem::LITERAL: { literal l = to_literal(elem.m_lidx); if (m_lit2proof.contains(l)) m_todo_pr.pop_back(); else { b_justification js = m_ctx.get_justification(l.var()); if (visit_b_justification(l, js)) { m_todo_pr.pop_back(); mk_proof(l, js); } } break; } default: UNREACHABLE(); } } SASSERT(visit_b_justification(consequent, conflict)); proof * pr = nullptr; if (not_l == null_literal) { pr = get_proof(false_literal, conflict); SASSERT(pr); } else { proof * prs[2] = { nullptr, nullptr}; m_lit2proof.find(not_l, prs[0]); SASSERT(prs[0]); prs[1] = get_proof(consequent, conflict); SASSERT(prs[1]); pr = m_manager.mk_unit_resolution(2, prs); } expr_ref_buffer lits(m_manager); for (literal lit : m_lemma) { m_ctx.unset_mark(lit.var()); expr_ref l_expr(m_manager); m_ctx.literal2expr(lit, l_expr); lits.push_back(l_expr); } expr * fact = nullptr; switch (lits.size()) { case 0: fact = nullptr; break; case 1: fact = lits[0]; break; default: fact = m_manager.mk_or(lits.size(), lits.c_ptr()); } if (fact == nullptr) m_lemma_proof = pr; else m_lemma_proof = m_manager.mk_lemma(pr, fact); m_new_proofs.reset(); } void conflict_resolution::process_antecedent_for_unsat_core(literal antecedent) { bool_var var = antecedent.var(); TRACE("conflict", tout << "processing antecedent: "; m_ctx.display_literal_info(tout << antecedent << " ", antecedent); tout << (m_ctx.is_marked(var)?"marked":"not marked"); tout << "\n";); if (!m_ctx.is_marked(var)) { m_ctx.set_mark(var); m_unmark.push_back(var); } if (m_ctx.is_assumption(var)) { m_assumptions.push_back(antecedent); } } void conflict_resolution::process_justification_for_unsat_core(justification * js) { literal_vector & antecedents = m_tmp_literal_vector; antecedents.reset(); justification2literals_core(js, antecedents); for (literal lit : antecedents) process_antecedent_for_unsat_core(lit); } void conflict_resolution::mk_unsat_core(b_justification conflict, literal not_l) { SASSERT(m_ctx.tracking_assumptions()); m_assumptions.reset(); m_unmark.reset(); SASSERT(m_conflict_lvl <= m_ctx.get_search_level()); unsigned search_lvl = m_ctx.get_search_level(); b_justification js = conflict; literal consequent = false_literal; if (not_l != null_literal) { consequent = ~not_l; } int idx = skip_literals_above_conflict_level(); if (not_l != null_literal) process_antecedent_for_unsat_core(consequent); if (m_assigned_literals.empty()) { goto end_unsat_core; } while (true) { 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(); TRACE("unsat_core_bug", m_ctx.display_clause_detail(tout, cls);); unsigned num_lits = cls->get_num_literals(); unsigned i = 0; if (consequent != false_literal) { SASSERT(cls->get_literal(0) == consequent || cls->get_literal(1) == consequent); if (cls->get_literal(0) == consequent) { i = 1; } else { process_antecedent_for_unsat_core(~cls->get_literal(0)); i = 2; } } for(; i < num_lits; i++) { literal l = cls->get_literal(i); process_antecedent_for_unsat_core(~l); } justification * js = cls->get_justification(); if (js) { process_justification_for_unsat_core(js); } break; } case b_justification::BIN_CLAUSE: SASSERT(consequent.var() != js.get_literal().var()); process_antecedent_for_unsat_core(js.get_literal()); break; case b_justification::AXIOM: break; case b_justification::JUSTIFICATION: process_justification_for_unsat_core(js.get_justification()); break; default: UNREACHABLE(); } if (m_ctx.is_assumption(consequent.var())) { m_assumptions.push_back(consequent); } while (idx >= 0) { literal l = m_assigned_literals[idx]; 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())) break; idx--; } if (idx < 0) { goto end_unsat_core; } SASSERT(idx >= 0); consequent = m_assigned_literals[idx]; bool_var c_var = consequent.var(); SASSERT(m_ctx.get_assign_level(c_var) == search_lvl); js = m_ctx.get_justification(c_var); idx--; } end_unsat_core: TRACE("unsat_core", tout << "assumptions:\n"; m_ctx.display_literals(tout, m_assumptions); tout << "\n";); reset_unmark_and_justifications(0, 0); } conflict_resolution * mk_conflict_resolution(ast_manager & m, context & ctx, dyn_ack_manager & dack_manager, smt_params const & params, literal_vector const & assigned_literals, vector & watches) { return alloc(conflict_resolution, m, ctx, dack_manager, params, assigned_literals, watches); } }; z3-z3-4.8.7/src/smt/smt_conflict_resolution.h000066400000000000000000000231151356505360400211430ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_conflict_resolution.h Abstract: Author: Leonardo de Moura (leonardo) 2008-02-25. Revision History: --*/ #ifndef SMT_CONFLICT_RESOLUTION_H_ #define SMT_CONFLICT_RESOLUTION_H_ #include "smt/smt_literal.h" #include "smt/smt_bool_var_data.h" #include "smt/smt_justification.h" #include "smt/smt_enode.h" #include "smt/dyn_ack.h" #include "util/obj_pair_hashtable.h" #include "smt/params/smt_params.h" #include "util/obj_pair_hashtable.h" #include "util/map.h" #include "smt/watch_list.h" #include "util/obj_pair_set.h" typedef approx_set_tpl level_approx_set; namespace smt { typedef std::pair enode_pair; /** \brief Base conflict resolution class. It implements the FUIP strategy. */ class conflict_resolution { protected: typedef obj_pair_set enode_pair_set; ast_manager & m_manager; smt_params const & m_params; context & m_ctx; dyn_ack_manager & m_dyn_ack_manager; literal_vector const & m_assigned_literals; unsigned m_conflict_lvl; literal_vector m_lemma; expr_ref_vector m_lemma_atoms; unsigned m_new_scope_lvl; unsigned m_lemma_iscope_lvl; justification_vector m_todo_js; unsigned m_todo_js_qhead; svector m_todo_eqs; enode_pair_set m_already_processed_eqs; literal_vector * m_antecedents; // Reference for watch lists are used to implement subsumption resolution vector & m_watches; //!< per literal // --------------------------- // // Proof generation // // --------------------------- typedef obj_map js2proof; typedef obj_pair_map eq2proof; typedef map, default_eq > lit2proof; /** \brief Element for the todo-list used to build proofs. */ struct tp_elem { enum { JUSTIFICATION, EQUALITY, LITERAL } m_kind; union { justification * m_js; unsigned m_lidx; struct { enode * m_lhs; enode * m_rhs; }; }; tp_elem(literal l):m_kind(LITERAL), m_lidx(l.index()) {} tp_elem(enode * lhs, enode * rhs):m_kind(EQUALITY), m_lhs(lhs), m_rhs(rhs) {} tp_elem(justification * js):m_kind(JUSTIFICATION), m_js(js) { SASSERT(js);} }; svector m_todo_pr; js2proof m_js2proof; eq2proof m_eq2proof; lit2proof m_lit2proof; proof_ref_vector m_new_proofs; proof_ref m_lemma_proof; literal_vector m_assumptions; public: void setup() { } void mark_justification(justification * js) { if (!js->is_marked()) { js->set_mark(); m_todo_js.push_back(js); } } void mark_eq(enode * n1, enode * n2) { if (n1 != n2) { if (n1->get_owner_id() > n2->get_owner_id()) std::swap(n1, n2); enode_pair p(n1, n2); if (m_already_processed_eqs.insert_if_not_there(p)) { TRACE("conflict_detail_verbose", tout << "marking eq #" << p.first->get_owner_id() << " = #" << p.second->get_owner_id() << "\n";); m_todo_eqs.push_back(p); SASSERT(m_already_processed_eqs.contains(p)); } } } void mark_literal(literal l) { SASSERT(m_antecedents); m_antecedents->push_back(l); } void mark_justified_eq(enode * lhs, enode * rhs, eq_justification js) { eq_justification2literals(lhs, rhs, js); } proof * norm_eq_proof(enode * n1, enode * n2, proof * pr); proof * get_proof(enode_pair const & p); proof * get_proof(enode * n1, enode * n2); proof * get_proof(enode * n1, enode * n2, eq_justification js); proof * get_proof(literal l); proof * get_proof(literal l, b_justification js); proof * get_proof(justification * js); bool visit_b_justification(literal l, b_justification js); void mk_proof(literal l, b_justification js); bool visit_trans_proof(enode * lhs, enode * rhs); bool visit_eq_justications(enode * lhs, enode * rhs); void mk_proof(enode * lhs, enode * rhs, ptr_buffer & result); void mk_proof(enode * lhs, enode * rhs); void init_mk_proof(); void mk_conflict_proof(b_justification conflict, literal not_l); protected: template void mark_enodes_in_trans(enode * n); enode * find_common_ancestor(enode * n1, enode * n2); void eq_justification2literals(enode * lhs, enode * rhs, eq_justification js); void eq_branch2literals(enode * n1, enode * n2); void eq2literals(enode * n1, enode * n2); void justification2literals_core(justification * js, literal_vector & result) ; void process_justifications(); void unmark_justifications(unsigned old_js_qhead); literal_vector m_tmp_literal_vector; unsigned get_justification_max_lvl(justification * js); unsigned get_max_lvl(literal consequent, b_justification js); unsigned skip_literals_above_conflict_level(); void process_antecedent(literal antecedent, unsigned & num_marks); void process_justification(justification * js, unsigned & num_marks); bool_var_vector m_unmark; bool_var_vector m_lemma_min_stack; level_approx_set m_lvl_set; level_approx_set get_lemma_approx_level_set(); void reset_unmark(unsigned old_size); void reset_unmark_and_justifications(unsigned old_size, unsigned old_js_qhead); bool process_antecedent_for_minimization(literal antecedent); bool process_justification_for_minimization(justification * js); bool implied_by_marked(literal lit); void minimize_lemma(); void structural_minimization(); void process_antecedent_for_unsat_core(literal antecedent); void process_justification_for_unsat_core(justification * js); void mk_unsat_core(b_justification conflict, literal not_l); bool initialize_resolve(b_justification conflict, literal not_l, b_justification & js, literal & consequent); void finalize_resolve(b_justification conflict, literal not_l); public: conflict_resolution(ast_manager & m, context & ctx, dyn_ack_manager & dack_manager, smt_params const & params, literal_vector const & assigned_literals, vector & watches ); virtual ~conflict_resolution() {} virtual bool resolve(b_justification conflict, literal not_l); context & get_context() { return m_ctx; } ast_manager & get_manager() { return m_manager; } unsigned get_new_scope_lvl() const { return m_new_scope_lvl; } unsigned get_lemma_intern_lvl() const { return m_lemma_iscope_lvl; } unsigned get_lemma_num_literals() const { return m_lemma.size(); } literal * get_lemma_literals() { return m_lemma.c_ptr(); } expr * * get_lemma_atoms() { return m_lemma_atoms.c_ptr(); } void release_lemma_atoms() { m_lemma_atoms.reset(); } proof * get_lemma_proof() { return m_lemma_proof; } literal_vector::const_iterator begin_unsat_core() const { return m_assumptions.begin(); } literal_vector::const_iterator end_unsat_core() const { return m_assumptions.end(); } void justification2literals(justification * js, literal_vector & result); void eq2literals(enode * n1, enode * n2, literal_vector & result); }; inline void mark_literals(conflict_resolution & cr, unsigned sz, literal const * ls) { for (unsigned i = 0; i < sz; i++) cr.mark_literal(ls[i]); } conflict_resolution * mk_conflict_resolution(ast_manager & m, context & ctx, dyn_ack_manager & dack_manager, smt_params const & params, literal_vector const & assigned_literals, vector & watches ); }; #endif /* SMT_CONFLICT_RESOLUTION_H_ */ z3-z3-4.8.7/src/smt/smt_consequences.cpp000066400000000000000000000567111356505360400201150ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_consequences.cpp Abstract: Tuned consequence finding for smt_context. Author: nbjorner 2016-07-28. Revision History: --*/ #include "util/max_cliques.h" #include "util/stopwatch.h" #include "ast/ast_util.h" #include "ast/ast_pp.h" #include "ast/datatype_decl_plugin.h" #include "model/model_pp.h" #include "smt/smt_context.h" namespace smt { expr_ref context::antecedent2fml(index_set const& vars) { expr_ref_vector premises(m); index_set::iterator it = vars.begin(), end = vars.end(); for (; it != end; ++it) { expr* e = bool_var2expr(*it); e = m_assumption2orig.find(e); premises.push_back(get_assignment(*it) != l_false ? e : m.mk_not(e)); } return mk_and(premises); } // // The literal lit is assigned at the search level, so it follows from the assumptions. // We retrieve the set of assumptions it depends on in the set 's'. // The map m_antecedents contains the association from these literals to the assumptions they depend on. // We then examine the contents of the literal lit and augment the set of consequences in one of the following cases: // - e is a propositional atom and it is one of the variables that is to be fixed. // - e is an equality between a variable and value that is to be fixed. // - e is a data-type recognizer of a variable that is to be fixed. // void context::extract_fixed_consequences(literal lit, index_set const& assumptions, expr_ref_vector& conseq) { datatype_util dt(m); expr* e1, *e2; expr_ref fml(m); if (lit == true_literal) return; expr* e = bool_var2expr(lit.var()); TRACE("context", display(tout << mk_pp(e, m) << "\n");); index_set s; if (assumptions.contains(lit.var())) { s.insert(lit.var()); } else { justify(lit, s); } m_antecedents.insert(lit.var(), s); TRACE("context", display_literal_verbose(tout, lit); for (index_set::iterator it = s.begin(), end = s.end(); it != end; ++it) { tout << " " << *it; } tout << "\n";); bool found = false; if (m_var2val.contains(e)) { found = true; m_var2val.erase(e); e = m_var2orig.find(e); fml = lit.sign() ? m.mk_not(e) : e; } else if (!lit.sign() && m.is_eq(e, e1, e2)) { if (m_var2val.contains(e2) && m.is_value(e1)) { found = true; m_var2val.erase(e2); e2 = m_var2orig.find(e2); std::swap(e1, e2); fml = m.mk_eq(e1, e2); } else if (m_var2val.contains(e1) && m.is_value(e2)) { found = true; m_var2val.erase(e1); e1 = m_var2orig.find(e1); fml = m.mk_eq(e1, e2); } } else if (!lit.sign() && is_app(e) && dt.is_recognizer(to_app(e)->get_decl())) { if (m_var2val.contains(to_app(e)->get_arg(0))) { found = true; fml = m.mk_eq(to_app(e)->get_arg(0), m.mk_const(dt.get_recognizer_constructor(to_app(e)->get_decl()))); m_var2val.erase(to_app(e)->get_arg(0)); } } if (found) { fml = m.mk_implies(antecedent2fml(s), fml); conseq.push_back(fml); } } void context::justify(literal lit, index_set& s) { (void)m; b_justification js = get_justification(lit.var()); switch (js.get_kind()) { case b_justification::CLAUSE: { clause * cls = js.get_clause(); if (!cls) break; for (literal lit2 : *cls) { if (lit2.var() != lit.var()) { s |= m_antecedents.find(lit2.var()); } } break; } case b_justification::BIN_CLAUSE: { s |= m_antecedents.find(js.get_literal().var()); break; } case b_justification::AXIOM: { break; } case b_justification::JUSTIFICATION: { literal_vector literals; m_conflict_resolution->justification2literals(js.get_justification(), literals); for (unsigned j = 0; j < literals.size(); ++j) { if (!m_antecedents.contains(literals[j].var())) { TRACE("context", tout << literals[j] << " " << mk_pp(bool_var2expr(literals[j].var()), m) << "\n";); } s |= m_antecedents.find(literals[j].var()); } break; } } } void context::extract_fixed_consequences(unsigned& start, index_set const& assumptions, expr_ref_vector& conseq) { pop_to_search_lvl(); SASSERT(!inconsistent()); literal_vector const& lits = assigned_literals(); unsigned sz = lits.size(); for (unsigned i = start; i < sz; ++i) { extract_fixed_consequences(lits[i], assumptions, conseq); } start = sz; SASSERT(!inconsistent()); } // // The assignment stack is assumed consistent. // For each Boolean variable, we check if there is a literal assigned to the // opposite value of the reference model, and for non-Boolean variables we // check if the current state forces the variable to be distinct from the reference value. // // For yet to be determined Boolean variables we furthermore force the phase to be opposite // to the reference value in the attempt to let the sat engine emerge with a model that // rules out as many non-fixed variables as possible. // unsigned context::delete_unfixed(expr_ref_vector& unfixed) { ptr_vector to_delete; obj_map::iterator it = m_var2val.begin(), end = m_var2val.end(); for (; it != end; ++it) { expr* k = it->m_key; expr* v = it->m_value; if (m.is_bool(k)) { literal lit = get_literal(k); switch (get_assignment(lit)) { case l_true: if (m.is_false(v)) { to_delete.push_back(k); } else { force_phase(lit.var(), false); } break; case l_false: if (m.is_true(v)) { to_delete.push_back(k); } else { force_phase(lit.var(), true); } break; default: to_delete.push_back(k); break; } } else if (e_internalized(k) && m.are_distinct(v, get_enode(k)->get_root()->get_owner())) { to_delete.push_back(k); } else if (get_assignment(mk_diseq(k, v)) == l_true) { to_delete.push_back(k); } } for (unsigned i = 0; i < to_delete.size(); ++i) { m_var2val.remove(to_delete[i]); unfixed.push_back(to_delete[i]); } return to_delete.size(); } #define are_equal(v, k) (e_internalized(k) && e_internalized(v) && get_enode(k)->get_root() == get_enode(v)->get_root()) // // Extract equalities that are congruent at the search level. // Add a clause to short-circuit the congruence justifications for // next rounds. // unsigned context::extract_fixed_eqs(expr_ref_vector& conseq) { TRACE("context", tout << "extract fixed consequences\n";); ptr_vector to_delete; expr_ref fml(m), eq(m); obj_map::iterator it = m_var2val.begin(), end = m_var2val.end(); for (; it != end; ++it) { expr* k = it->m_key; expr* v = it->m_value; if (!m.is_bool(k) && are_equal(k, v)) { literal_vector literals; m_conflict_resolution->eq2literals(get_enode(v), get_enode(k), literals); index_set s; for (unsigned i = 0; i < literals.size(); ++i) { SASSERT(get_assign_level(literals[i]) <= get_search_level()); s |= m_antecedents.find(literals[i].var()); } fml = m.mk_eq(m_var2orig.find(k), v); fml = m.mk_implies(antecedent2fml(s), fml); conseq.push_back(fml); to_delete.push_back(k); for (unsigned i = 0; i < literals.size(); ++i) { literals[i].neg(); } literal lit = mk_diseq(k, v); literals.push_back(lit); mk_clause(literals.size(), literals.c_ptr(), nullptr); TRACE("context", display_literals_verbose(tout, literals.size(), literals.c_ptr());); } } for (unsigned i = 0; i < to_delete.size(); ++i) { m_var2val.remove(to_delete[i]); } return to_delete.size(); } literal context::mk_diseq(expr* e, expr* val) { if (m.is_bool(e) && b_internalized(e)) { return literal(get_bool_var(e), m.is_true(val)); } else if (m.is_bool(e)) { internalize_formula(e, false); return literal(get_bool_var(e), !m.is_true(val)); } else { expr_ref eq(mk_eq_atom(e, val), m); internalize_formula(eq, false); return literal(get_bool_var(eq), true); } } lbool context::get_consequences(expr_ref_vector const& assumptions0, expr_ref_vector const& vars0, expr_ref_vector& conseq, expr_ref_vector& unfixed) { m_antecedents.reset(); m_antecedents.insert(true_literal.var(), index_set()); pop_to_base_lvl(); expr_ref_vector vars(m), assumptions(m); m_var2val.reset(); m_var2orig.reset(); m_assumption2orig.reset(); bool pushed = false; for (unsigned i = 0; i < vars0.size(); ++i) { expr* v = vars0[i]; if (is_uninterp_const(v)) { vars.push_back(v); m_var2orig.insert(v, v); } else { if (!pushed) { pushed = true; push(); } expr_ref c(m.mk_fresh_const("v", m.get_sort(v)), m); expr_ref eq(m.mk_eq(c, v), m); assert_expr(eq); vars.push_back(c); m_var2orig.insert(c, v); } } for (unsigned i = 0; i < assumptions0.size(); ++i) { expr* a = assumptions0[i]; if (is_uninterp_const(a)) { assumptions.push_back(a); m_assumption2orig.insert(a, a); } else { if (!pushed) { pushed = true; push(); } expr_ref c(m.mk_fresh_const("a", m.get_sort(a)), m); expr_ref eq(m.mk_eq(c, a), m); assert_expr(eq); assumptions.push_back(c); m_assumption2orig.insert(c, a); } } lbool is_sat = check(assumptions.size(), assumptions.c_ptr()); if (is_sat != l_true) { TRACE("context", tout << is_sat << "\n";); if (pushed) pop(1); return is_sat; } index_set _assumptions; for (unsigned i = 0; i < assumptions.size(); ++i) { _assumptions.insert(get_literal(assumptions[i].get()).var()); } model_ref mdl; get_model(mdl); expr_ref_vector trail(m); model_evaluator eval(*mdl.get()); expr_ref val(m); TRACE("context", model_pp(tout, *mdl);); for (unsigned i = 0; i < vars.size(); ++i) { eval(vars[i].get(), val); if (m.is_value(val)) { trail.push_back(val); m_var2val.insert(vars[i].get(), val); } else { unfixed.push_back(vars[i].get()); } } unsigned num_units = 0; extract_fixed_consequences(num_units, _assumptions, conseq); app_ref eq(m); TRACE("context", tout << "vars: " << vars.size() << "\n"; tout << "lits: " << num_units << "\n";); m_case_split_queue->init_search_eh(); unsigned num_iterations = 0; unsigned num_fixed_eqs = 0; unsigned chunk_size = 100; while (!m_var2val.empty()) { obj_map::iterator it = m_var2val.begin(), end = m_var2val.end(); unsigned num_vars = 0; for (; it != end && num_vars < chunk_size; ++it) { if (get_cancel_flag()) { if (pushed) pop(1); return l_undef; } expr* e = it->m_key; expr* val = it->m_value; literal lit = mk_diseq(e, val); mark_as_relevant(lit); if (get_assignment(lit) != l_undef) { continue; } ++num_vars; push_scope(); assign(lit, b_justification::mk_axiom(), true); if (!propagate()) { if (!resolve_conflict() || inconsistent()) { TRACE("context", tout << "inconsistent\n";); SASSERT(inconsistent()); m_conflict = null_b_justification; m_not_l = null_literal; SASSERT(m_search_lvl == get_search_level()); } } } SASSERT(!inconsistent()); ++num_iterations; lbool is_sat = l_undef; while (true) { is_sat = bounded_search(); if (is_sat != l_true && m_last_search_failure != OK) { if (pushed) pop(1); return is_sat; } if (is_sat == l_undef) { IF_VERBOSE(1, verbose_stream() << "(get-consequences inc-limits)\n";); inc_limits(); continue; } break; } if (is_sat == l_false) { SASSERT(inconsistent()); m_conflict = null_b_justification; m_not_l = null_literal; } if (is_sat == l_true) { delete_unfixed(unfixed); } extract_fixed_consequences(num_units, _assumptions, conseq); num_fixed_eqs += extract_fixed_eqs(conseq); IF_VERBOSE(1, display_consequence_progress(verbose_stream(), num_iterations, m_var2val.size(), conseq.size(), unfixed.size(), num_fixed_eqs);); TRACE("context", display_consequence_progress(tout, num_iterations, m_var2val.size(), conseq.size(), unfixed.size(), num_fixed_eqs);); } end_search(); DEBUG_CODE(validate_consequences(assumptions, vars, conseq, unfixed);); if (pushed) { pop(1); } return l_true; } void context::display_consequence_progress(std::ostream& out, unsigned it, unsigned nv, unsigned fixed, unsigned unfixed, unsigned eq) { out << "(get-consequences" << " iterations: " << it << " variables: " << nv << " fixed: " << fixed << " unfixed: " << unfixed << " fixed-eqs: " << eq << ")\n"; } void context::extract_cores(expr_ref_vector const& asms, vector& cores, unsigned& min_core_size) { index_set _asms, _nasms; u_map var2expr; for (unsigned i = 0; i < asms.size(); ++i) { literal lit = get_literal(asms[i]); _asms.insert(lit.index()); _nasms.insert((~lit).index()); var2expr.insert(lit.var(), asms[i]); } m_antecedents.reset(); literal_vector const& lits = assigned_literals(); for (unsigned i = 0; i < lits.size(); ++i) { literal lit = lits[i]; index_set s; if (_asms.contains(lit.index())) { s.insert(lit.var()); } else { justify(lit, s); } m_antecedents.insert(lit.var(), s); if (_nasms.contains(lit.index())) { expr_ref_vector core(m); index_set::iterator it = s.begin(), end = s.end(); for (; it != end; ++it) { core.push_back(var2expr[*it]); } core.push_back(var2expr[lit.var()]); cores.push_back(core); min_core_size = std::min(min_core_size, core.size()); } } } void context::preferred_sat(literal_vector& lits) { bool retry = true; while (retry) { retry = false; for (unsigned i = 0; i < lits.size(); ++i) { literal lit = lits[i]; if (lit == null_literal || get_assignment(lit) != l_undef) { continue; } push_scope(); assign(lit, b_justification::mk_axiom(), true); while (!propagate()) { lits[i] = null_literal; retry = true; if (!resolve_conflict() || inconsistent()) { SASSERT(inconsistent()); return; } } } } } void context::display_partial_assignment(std::ostream& out, expr_ref_vector const& asms, unsigned min_core_size) { unsigned num_true = 0, num_false = 0, num_undef = 0; for (unsigned i = 0; i < asms.size(); ++i) { literal lit = get_literal(asms[i]); if (get_assignment(lit) == l_false) { ++num_false; } if (get_assignment(lit) == l_true) { ++num_true; } if (get_assignment(lit) == l_undef) { ++num_undef; } } out << "(smt.preferred-sat true: " << num_true << " false: " << num_false << " undef: " << num_undef << " min core: " << min_core_size << ")\n"; } lbool context::preferred_sat(expr_ref_vector const& asms, vector& cores) { pop_to_base_lvl(); cores.reset(); setup_context(false); internalize_assertions(); if (m_asserted_formulas.inconsistent() || inconsistent()) { return l_false; } scoped_mk_model smk(*this); init_search(); flet l(m_searching, true); unsigned level = m_scope_lvl; unsigned min_core_size = UINT_MAX; lbool is_sat = l_true; unsigned num_restarts = 0; while (true) { if (m.canceled()) { is_sat = l_undef; break; } literal_vector lits; for (unsigned i = 0; i < asms.size(); ++i) { lits.push_back(get_literal(asms[i])); } preferred_sat(lits); if (inconsistent()) { SASSERT(m_scope_lvl == level); is_sat = l_false; break; } extract_cores(asms, cores, min_core_size); IF_VERBOSE(1, display_partial_assignment(verbose_stream(), asms, min_core_size);); if (min_core_size <= 10) { is_sat = l_undef; break; } is_sat = bounded_search(); if (!restart(is_sat, level)) { break; } ++num_restarts; if (num_restarts >= min_core_size) { is_sat = l_undef; while (num_restarts <= 10*min_core_size) { is_sat = bounded_search(); if (!restart(is_sat, level)) { break; } ++num_restarts; } break; } } end_search(); return check_finalize(is_sat); } struct neg_literal { unsigned negate(unsigned i) { return (~to_literal(i)).index(); } }; lbool context::find_mutexes(expr_ref_vector const& vars, vector& mutexes) { unsigned_vector ps; max_cliques mc; expr_ref lit(m); for (unsigned i = 0; i < vars.size(); ++i) { expr* n = vars[i]; bool neg = m.is_not(n, n); if (b_internalized(n)) { ps.push_back(literal(get_bool_var(n), neg).index()); } } for (unsigned i = 0; i < m_watches.size(); ++i) { watch_list & w = m_watches[i]; for (literal const* it = w.begin_literals(), *end = w.end_literals(); it != end; ++it) { unsigned idx1 = (~to_literal(i)).index(); unsigned idx2 = it->index(); if (idx1 < idx2) { mc.add_edge(idx1, idx2); } } } vector _mutexes; mc.cliques(ps, _mutexes); for (unsigned i = 0; i < _mutexes.size(); ++i) { expr_ref_vector lits(m); for (unsigned j = 0; j < _mutexes[i].size(); ++j) { literal2expr(to_literal(_mutexes[i][j]), lit); lits.push_back(lit); } mutexes.push_back(lits); } return l_true; } // // Validate, in a slow pass, that the current consequences are correctly // extracted. // void context::validate_consequences(expr_ref_vector const& assumptions, expr_ref_vector const& vars, expr_ref_vector const& conseq, expr_ref_vector const& unfixed) { expr_ref tmp(m); SASSERT(!inconsistent()); for (unsigned i = 0; i < conseq.size(); ++i) { push(); for (unsigned j = 0; j < assumptions.size(); ++j) { assert_expr(assumptions[j]); } TRACE("context", tout << "checking: " << mk_pp(conseq[i], m) << "\n";); tmp = m.mk_not(conseq[i]); assert_expr(tmp); VERIFY(check() != l_true); pop(1); } model_ref mdl; for (unsigned i = 0; i < unfixed.size(); ++i) { push(); for (expr* a : assumptions) assert_expr(a); TRACE("context", tout << "checking unfixed: " << mk_pp(unfixed[i], m) << "\n";); lbool is_sat = check(); SASSERT(is_sat != l_false); if (is_sat == l_true) { get_model(mdl); tmp = (*mdl)(unfixed[i]); if (m.is_value(tmp)) { tmp = m.mk_not(m.mk_eq(unfixed[i], tmp)); assert_expr(tmp); is_sat = check(); SASSERT(is_sat != l_false); } } pop(1); } } } z3-z3-4.8.7/src/smt/smt_context.cpp000066400000000000000000005450161356505360400171070ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_context.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-02-19. Revision History: --*/ #include #include "util/luby.h" #include "util/warning.h" #include "util/timeit.h" #include "util/union_find.h" #include "ast/ast_pp.h" #include "ast/ast_ll_pp.h" #include "ast/ast_smt2_pp.h" #include "ast/ast_translation.h" #include "ast/recfun_decl_plugin.h" #include "ast/proofs/proof_checker.h" #include "ast/ast_util.h" #include "ast/well_sorted.h" #include "model/model.h" #include "model/model_pp.h" #include "smt/smt_context.h" #include "smt/smt_quick_checker.h" #include "smt/uses_theory.h" #include "smt/smt_for_each_relevant_expr.h" #include "smt/smt_model_generator.h" #include "smt/smt_model_checker.h" #include "smt/smt_model_finder.h" namespace smt { context::context(ast_manager & m, smt_params & p, params_ref const & _p): m(m), m_fparams(p), m_params(_p), m_setup(*this, p), m_asserted_formulas(m, p, _p), m_rewriter(m), m_qmanager(alloc(quantifier_manager, *this, p, _p)), m_model_generator(alloc(model_generator, m)), m_relevancy_propagator(mk_relevancy_propagator(*this)), m_random(p.m_random_seed), m_flushing(false), m_lemma_id(0), m_progress_callback(nullptr), m_next_progress_sample(0), m_clause_proof(*this), m_fingerprints(m, m_region), m_b_internalized_stack(m), m_e_internalized_stack(m), m_final_check_idx(0), m_is_auxiliary(false), m_cg_table(m), m_is_diseq_tmp(nullptr), m_units_to_reassert(m), m_qhead(0), m_simp_qhead(0), m_simp_counter(0), m_bvar_inc(1.0), m_phase_cache_on(true), m_phase_counter(0), m_phase_default(false), m_conflict(null_b_justification), m_not_l(null_literal), m_conflict_resolution(mk_conflict_resolution(m, *this, m_dyn_ack_manager, p, m_assigned_literals, m_watches)), m_unsat_proof(m), m_dyn_ack_manager(*this, p), m_unknown("unknown"), m_unsat_core(m), #ifdef Z3DEBUG m_trail_enabled(true), #endif m_scope_lvl(0), m_base_lvl(0), m_search_lvl(0), m_generation(0), m_last_search_result(l_undef), m_last_search_failure(UNKNOWN), m_searching(false) { SASSERT(m_scope_lvl == 0); SASSERT(m_base_lvl == 0); SASSERT(m_search_lvl == 0); m_case_split_queue = mk_case_split_queue(*this, p); m_rewriter.updt_params(m_asserted_formulas.get_params()); init(); if (!relevancy()) m_fparams.m_relevancy_lemma = false; m_model_generator->set_context(this); } literal context::translate_literal( literal lit, context& src_ctx, context& dst_ctx, vector b2v, ast_translation& tr) { ast_manager& dst_m = dst_ctx.get_manager(); ast_manager& src_m = src_ctx.get_manager(); expr_ref dst_f(dst_m); SASSERT(lit != false_literal && lit != true_literal); bool_var v = b2v.get(lit.var(), null_bool_var); if (v == null_bool_var) { expr* e = src_ctx.m_bool_var2expr.get(lit.var(), 0); SASSERT(e); dst_f = tr(e); v = dst_ctx.get_bool_var_of_id_option(dst_f->get_id()); if (v != null_bool_var) { } else if (src_m.is_not(e) || src_m.is_and(e) || src_m.is_or(e) || src_m.is_iff(e) || src_m.is_ite(e)) { v = dst_ctx.mk_bool_var(dst_f); } else { dst_ctx.internalize_formula(dst_f, false); v = dst_ctx.get_bool_var(dst_f); } b2v.setx(lit.var(), v, null_bool_var); } return literal(v, lit.sign()); } /** \brief retrieve flag for when cancelation is possible. */ bool context::get_cancel_flag() { return !m.limit().inc(); } void context::updt_params(params_ref const& p) { m_params.append(p); m_asserted_formulas.updt_params(p); } void context::copy(context& src_ctx, context& dst_ctx) { ast_manager& dst_m = dst_ctx.get_manager(); ast_manager& src_m = src_ctx.get_manager(); src_ctx.pop_to_base_lvl(); if (src_ctx.m_base_lvl > 0) { throw default_exception("Cloning contexts within a user-scope is not allowed"); } SASSERT(src_ctx.m_base_lvl == 0); ast_translation tr(src_m, dst_m, false); dst_ctx.set_logic(src_ctx.m_setup.get_logic()); dst_ctx.copy_plugins(src_ctx, dst_ctx); asserted_formulas& src_af = src_ctx.m_asserted_formulas; asserted_formulas& dst_af = dst_ctx.m_asserted_formulas; // Copy asserted formulas. for (unsigned i = 0; i < src_af.get_num_formulas(); ++i) { expr_ref fml(dst_m); proof_ref pr(dst_m); proof* pr_src = src_af.get_formula_proof(i); fml = tr(src_af.get_formula(i)); if (pr_src) { pr = tr(pr_src); } dst_af.assert_expr(fml, pr); } if (!src_ctx.m_setup.already_configured()) { return; } dst_ctx.setup_context(dst_ctx.m_fparams.m_auto_config); dst_ctx.internalize_assertions(); vector b2v; #define TRANSLATE(_lit) translate_literal(_lit, src_ctx, dst_ctx, b2v, tr) for (unsigned i = 0; i < src_ctx.m_assigned_literals.size(); ++i) { literal lit; lit = TRANSLATE(src_ctx.m_assigned_literals[i]); dst_ctx.mk_clause(1, &lit, nullptr, CLS_AUX, nullptr); } #if 0 literal_vector lits; expr_ref_vector cls(src_m); for (unsigned i = 0; i < src_ctx.m_lemmas.size(); ++i) { lits.reset(); cls.reset(); clause& src_cls = *src_ctx.m_lemmas[i]; unsigned sz = src_cls.get_num_literals(); for (unsigned j = 0; j < sz; ++j) { literal lit = TRANSLATE(src_cls.get_literal(j)); lits.push_back(lit); } dst_ctx.mk_clause(lits.size(), lits.c_ptr(), 0, src_cls.get_kind(), 0); } vector::const_iterator it = src_ctx.m_watches.begin(); vector::const_iterator end = src_ctx.m_watches.end(); literal ls[2]; for (unsigned l_idx = 0; it != end; ++it, ++l_idx) { literal l1 = to_literal(l_idx); literal neg_l1 = ~l1; watch_list const & wl = *it; literal const * it2 = wl.begin(); literal const * end2 = wl.end(); for (; it2 != end2; ++it2) { literal l2 = *it2; if (l1.index() < l2.index()) { ls[0] = TRANSLATE(neg_l1); ls[1] = TRANSLATE(l2); dst_ctx.mk_clause(2, ls, 0, CLS_AUX, 0); } } } #endif TRACE("smt_context", src_ctx.display(tout); dst_ctx.display(tout);); } context::~context() { flush(); m_asserted_formulas.finalize(); } void context::copy_plugins(context& src, context& dst) { // copy theory plugins for (theory* old_th : src.m_theory_set) { theory * new_th = old_th->mk_fresh(&dst); dst.register_plugin(new_th); } } context * context::mk_fresh(symbol const * l, smt_params * p, params_ref const& pa) { context * new_ctx = alloc(context, m, p ? *p : m_fparams, pa); new_ctx->m_is_auxiliary = true; new_ctx->set_logic(l == nullptr ? m_setup.get_logic() : *l); copy_plugins(*this, *new_ctx); return new_ctx; } void context::init() { app * t = m.mk_true(); mk_bool_var(t); SASSERT(get_bool_var(t) == true_bool_var); SASSERT(true_literal.var() == true_bool_var); m_assignment[true_literal.index()] = l_true; m_assignment[false_literal.index()] = l_false; if (m.proofs_enabled()) { proof * pr = m.mk_true_proof(); set_justification(true_bool_var, m_bdata[true_bool_var], b_justification(mk_justification(justification_proof_wrapper(*this, pr)))); } else { m_bdata[true_bool_var].set_axiom(); } m_true_enode = mk_enode(t, true, true, false); // internalizer is marking enodes as interpreted whenever the associated ast is a value and a constant. // m_true_enode->mark_as_interpreted(); app * f = m.mk_false(); m_false_enode = mk_enode(f, true, true, false); // m_false_enode->mark_as_interpreted(); } void context::set_progress_callback(progress_callback *cb) { m_progress_callback = cb; } /** \brief This method should be used to create equality atoms during the search. See comments in theory::mk_eq_atom */ app * context::mk_eq_atom(expr * lhs, expr * rhs) { family_id fid = m.get_sort(lhs)->get_family_id(); theory * th = get_theory(fid); if (th) return th->mk_eq_atom(lhs, rhs); if (lhs->get_id() > rhs->get_id()) std::swap(lhs, rhs); return m.mk_eq(lhs, rhs); } void context::set_justification(bool_var v, bool_var_data& d, b_justification const& j) { SASSERT(validate_justification(v, d, j)); d.set_justification(j); } void context::assign_core(literal l, b_justification j, bool decision) { TRACE("assign_core", tout << (decision?"decision: ":"propagating: ") << l << " "; display_literal_verbose(tout, l); tout << " level: " << m_scope_lvl << "\n"; display(tout, j);); m_assigned_literals.push_back(l); m_assignment[l.index()] = l_true; m_assignment[(~l).index()] = l_false; bool_var_data & d = get_bdata(l.var()); set_justification(l.var(), d, j); d.m_scope_lvl = m_scope_lvl; if (m_fparams.m_restart_adaptive && d.m_phase_available) { m_agility *= m_fparams.m_agility_factor; if (!decision && d.m_phase == l.sign()) m_agility += (1.0 - m_fparams.m_agility_factor); } d.m_phase_available = true; d.m_phase = !l.sign(); TRACE("phase_selection", tout << "saving phase, is_pos: " << d.m_phase << " l: " << l << "\n";); TRACE("relevancy", tout << "is_atom: " << d.is_atom() << " is relevant: " << is_relevant_core(l) << "\n";); if (d.is_atom() && (m_fparams.m_relevancy_lvl == 0 || (m_fparams.m_relevancy_lvl == 1 && !d.is_quantifier()) || is_relevant_core(l))) m_atom_propagation_queue.push_back(l); if (m.has_trace_stream()) trace_assign(l, j, decision); m_case_split_queue->assign_lit_eh(l); // a unit is asserted at search level. Mark it as relevant. // this addresses bug... where a literal becomes fixed to true (false) // as a conflict gets assigned misses relevancy (and quantifier instantiation). // if (false && !decision && relevancy() && at_search_level() && !is_relevant_core(l)) { mark_as_relevant(l); } } bool context::bcp() { SASSERT(!inconsistent()); while (m_qhead < m_assigned_literals.size()) { if (get_cancel_flag()) { return true; } literal l = m_assigned_literals[m_qhead]; SASSERT(get_assignment(l) == l_true); m_qhead++; m_simp_counter--; literal not_l = ~l; SASSERT(get_assignment(not_l) == l_false); watch_list & w = m_watches[l.index()]; if (binary_clause_opt_enabled()) { // binary clause propagation b_justification js(l); literal * it = w.begin_literals(); literal * end = w.end_literals(); for(; it != end; ++it) { literal l = *it; switch (get_assignment(l)) { case l_false: m_stats.m_num_bin_propagations++; set_conflict(js, ~l); return false; case l_undef: m_stats.m_num_bin_propagations++; assign_core(l, js); break; case l_true: break; } } } // non-binary clause propagation watch_list::clause_iterator it = w.begin_clause(); watch_list::clause_iterator it2 = it; watch_list::clause_iterator end = w.end_clause(); for(; it != end; ++it) { clause * cls = *it; CTRACE("bcp_bug", cls->get_literal(0) != not_l && cls->get_literal(1) != not_l, display_clause_detail(tout, cls); tout << "not_l: "; display_literal(tout, not_l); tout << " " << not_l << "\n";); SASSERT(cls->get_literal(0) == not_l || cls->get_literal(1) == not_l); if (cls->get_literal(0) == not_l) { cls->set_literal(0, cls->get_literal(1)); cls->set_literal(1, not_l); } SASSERT(cls->get_literal(1) == not_l); literal first_lit = cls->get_literal(0); lbool first_lit_val = get_assignment(first_lit); if (first_lit_val == l_true) { *it2 = *it; // clause is already satisfied, keep it it2++; } else { literal * it3 = cls->begin() + 2; literal * end3 = cls->end(); for(; it3 != end3; ++it3) { if (get_assignment(*it3) != l_false) { // swap literal *it3 with literal at position 0 // the negation of literal *it3 will watch clause cls. m_watches[(~(*it3)).index()].insert_clause(cls); cls->set_literal(1, *it3); *it3 = not_l; goto found_watch; } } // did not find watch... if (first_lit_val == l_false) { // CONFLICT // copy remaining watches while (it < end) { *it2 = *it; it2++; it++; } SASSERT(it2 <= end); w.set_end_clause(it2); SASSERT(is_empty_clause(cls)); set_conflict(b_justification(cls)); return false; } else { // PROPAGATION SASSERT(first_lit_val == l_undef); SASSERT(get_assignment(first_lit) == l_undef); SASSERT(is_unit_clause(cls)); *it2 = *it; it2++; // keep clause m_stats.m_num_propagations++; // It is safe to call assign_core instead of assign because first_lit is unassigned assign_core(first_lit, b_justification(cls)); if (m_fparams.m_relevancy_lemma && cls->is_lemma()) { expr * e = bool_var2expr(first_lit.var()); // IMPORTANT: this kind of propagation asserts negative literals of the form (= A1 A2) where // A1 and A2 are array terms. So, it may be interesting to disable it for array eqs. //if (!(m.is_eq(e) && m.get_sort(to_app(e)->get_arg(0))->get_family_id() == m.get_family_id("array"))) mark_as_relevant(e); } } found_watch:; } } SASSERT(it2 <= end); w.set_end_clause(it2); } return true; } /** \brief Push a new equality for theory th, into the theory equality propagation queue. */ void context::push_new_th_eq(theory_id th, theory_var lhs, theory_var rhs) { SASSERT(lhs != rhs); SASSERT(lhs != null_theory_var); SASSERT(rhs != null_theory_var); SASSERT(th != null_theory_id); SASSERT(get_theory(th)->get_enode(lhs) != get_theory(th)->get_enode(rhs)); m_th_eq_propagation_queue.push_back(new_th_eq(th, lhs, rhs)); } /** \brief Push a new disequality for theory th, into the theory disequality propagation queue. */ void context::push_new_th_diseq(theory_id th, theory_var lhs, theory_var rhs) { SASSERT(lhs != rhs); SASSERT(lhs != null_theory_var); SASSERT(rhs != null_theory_var); SASSERT(th != null_theory_id); theory * t = get_theory(th); if (t->get_enode(lhs)->is_interpreted() && t->get_enode(rhs)->is_interpreted()) return; TRACE("add_diseq", tout << "#" << t->get_enode(lhs)->get_owner_id() << " != " << "#" << t->get_enode(rhs)->get_owner_id() << "\n";); m_th_diseq_propagation_queue.push_back(new_th_eq(th, lhs, rhs)); } class add_eq_trail : public trail { enode * m_r1; enode * m_n1; unsigned m_r2_num_parents; public: add_eq_trail(enode * r1, enode * n1, unsigned r2_num_parents): m_r1(r1), m_n1(n1), m_r2_num_parents(r2_num_parents) { } void undo(context & ctx) override { ctx.undo_add_eq(m_r1, m_n1, m_r2_num_parents); } }; /** \brief Add the equality n1 = n2 with justification js into the logical context. */ void context::add_eq(enode * n1, enode * n2, eq_justification js) { unsigned old_trail_size = m_trail_stack.size(); scoped_suspend_rlimit _suspend_cancel(m.limit()); try { TRACE("add_eq", tout << "assigning: #" << n1->get_owner_id() << " = #" << n2->get_owner_id() << "\n";); TRACE("add_eq_detail", tout << "assigning\n" << enode_pp(n1, *this) << "\n" << enode_pp(n2, *this) << "\n"; tout << "kind: " << js.get_kind() << "\n";); m_stats.m_num_add_eq++; enode * r1 = n1->get_root(); enode * r2 = n2->get_root(); if (r1 == r2) { TRACE("add_eq", tout << "redundant constraint.\n";); return; } if (r1->is_interpreted() && r2->is_interpreted()) { TRACE("add_eq", tout << "interpreted roots conflict.\n";); set_conflict(mk_justification(eq_conflict_justification(n1, n2, js))); return; } // Swap r1 and r2: // 1. if the "equivalence" class of r1 is bigger than the equivalence class of r2 // OR // 2. r1 is interpreted but r2 is not. // // The second condition is used to enforce the invariant that if a class contain // an interpreted enode then the root is also interpreted. if ((r1->get_class_size() > r2->get_class_size() && !r2->is_interpreted()) || r1->is_interpreted()) { SASSERT(!r2->is_interpreted()); std::swap(n1, n2); std::swap(r1, r2); } TRACE("add_eq", tout << "merging: #" << r1->get_owner_id() << " #" << r2->get_owner_id() << " n1: #" << n1->get_owner_id() << "\n";); // It is necessary to propagate relevancy to other elements of // the equivalence class. This is necessary to enforce the invariant // in the field m_parent of the enode class. if (is_relevant(r1)) { // && !m.is_eq(r1->get_owner())) !is_eq HACK // NOTE for !is_eq HACK... the !is_eq HACK does not propagate relevancy when two // equality enodes are congruent. I tested this optimization because in V1 // relevancy is not propagated for congruent equalities. // This occurs in V2, because we use the congruence table to propagate disequalities // efficiently. // I disabled this optimization HACK because it breaks invariants in the rest of the code. // To use it, I need to go over the code and analyze all affected places. mark_as_relevant(r2); } else if (is_relevant(r2)) { // && !m.is_eq(r2->get_owner())) { // !is_eq HACK mark_as_relevant(r1); } push_trail(add_eq_trail(r1, n1, r2->get_num_parents())); m_qmanager->add_eq_eh(r1, r2); merge_theory_vars(n2, n1, js); // 'Proof' tree // n1 -> ... -> r1 // n2 -> ... -> r2 SASSERT(n1->trans_reaches(r1)); invert_trans(n1); n1->m_trans.m_target = n2; n1->m_trans.m_justification = js; n1->m_proof_is_logged = false; SASSERT(r1->trans_reaches(n1)); // --------------- // r1 -> .. -> n1 -> n2 -> ... -> r2 remove_parents_from_cg_table(r1); enode * curr = r1; do { curr->m_root = r2; curr = curr->m_next; } while(curr != r1); SASSERT(r1->get_root() == r2); reinsert_parents_into_cg_table(r1, r2, n1, n2, js); if (n2->is_bool()) propagate_bool_enode_assignment(r1, r2, n1, n2); // Merge "equivalence" classes std::swap(r1->m_next, r2->m_next); // Update "equivalence" class size r2->m_class_size += r1->m_class_size; CASSERT("add_eq", check_invariant()); } catch (...) { // Restore trail size since procedure was interrupted in the middle. // If the add_eq_trail remains on the trail stack, then Z3 may crash when the destructor is invoked. TRACE("add_eq", tout << "add_eq interrupted. This is unsafe " << m.limit().get_cancel_flag() << "\n";); m_trail_stack.shrink(old_trail_size); throw; } } /** \brief When merging to equivalence classes, the parents of the smallest one (that are congruence roots), must be removed from the congruence table since their hash code will change. */ void context::remove_parents_from_cg_table(enode * r1) { // Remove parents from the congruence table for (enode * parent : enode::parents(r1)) { CTRACE("add_eq", !parent->is_marked() && parent->is_cgc_enabled() && parent->is_true_eq() && m_cg_table.contains_ptr(parent), tout << parent->get_owner_id() << "\n";); CTRACE("add_eq", !parent->is_marked() && parent->is_cgc_enabled() && !parent->is_true_eq() && parent->is_cgr() && !m_cg_table.contains_ptr(parent), tout << "cgr !contains " << parent->get_owner_id() << " " << mk_pp(parent->get_decl(), m) << "\n"; for (enode* n : enode::args(parent)) tout << n->get_root()->get_owner_id() << " " << n->get_root()->hash() << " "; tout << "\n"; tout << "contains: " << m_cg_table.contains(parent) << "\n"; if (m_cg_table.contains(parent)) { tout << "owner: " << m_cg_table.find(parent)->get_owner_id() << "\n"; } m_cg_table.display(tout); ); CTRACE("add_eq", !parent->is_marked() && parent->is_cgc_enabled() && !parent->is_true_eq() && !parent->is_cgr() && m_cg_table.contains_ptr(parent), tout << "!cgr contains " << parent->get_owner_id() << "\n";); SASSERT(parent->is_marked() || !parent->is_cgc_enabled() || parent->is_true_eq() || parent->is_cgr() == m_cg_table.contains_ptr(parent)); SASSERT(parent->is_marked() || !parent->is_cgc_enabled() || !parent->is_true_eq() || !m_cg_table.contains_ptr(parent)); if (!parent->is_marked() && parent->is_cgr() && !parent->is_true_eq()) { SASSERT(!parent->is_cgc_enabled() || m_cg_table.contains_ptr(parent)); parent->set_mark(); if (parent->is_cgc_enabled()) { m_cg_table.erase(parent); SASSERT(!m_cg_table.contains_ptr(parent)); } } } } /** \brief Reinsert the parents of r1 that were removed from the cg_table at remove_parents_from_cg_table. Some of these parents will become congruent to other enodes, and a new equality will be propagated. Moreover, this method is also used for doing equality propagation. The parents of r1 that remain as congruence roots are copied to the r2->m_parents. The n1, n2, js arguments are used to implement dynamic ackermanization. js is a justification for n1 and n2 being equal, and the equality n1 = n2 is the one that implied r1 = r2. */ void context::reinsert_parents_into_cg_table(enode * r1, enode * r2, enode * n1, enode * n2, eq_justification js) { enode_vector & r2_parents = r2->m_parents; for (enode * parent : enode::parents(r1)) { if (!parent->is_marked()) continue; parent->unset_mark(); if (parent->is_eq()) { SASSERT(parent->get_num_args() == 2); TRACE("add_eq_bug", tout << "visiting: #" << parent->get_owner_id() << "\n";); enode * lhs = parent->get_arg(0); enode * rhs = parent->get_arg(1); if (lhs->get_root() == rhs->get_root()) { SASSERT(parent->is_true_eq()); unsigned expr_id = parent->get_owner_id(); bool_var v = get_bool_var_of_id(expr_id); lbool val = get_assignment(v); if (val != l_true) { if (val == l_false && js.get_kind() == eq_justification::CONGRUENCE) m_dyn_ack_manager.cg_conflict_eh(n1->get_owner(), n2->get_owner()); assign(literal(v), mk_justification(eq_propagation_justification(lhs, rhs))); } // It is not necessary to reinsert the equality to the congruence table continue; } } if (parent->is_cgc_enabled()) { enode_bool_pair pair = m_cg_table.insert(parent); enode * parent_prime = pair.first; if (parent_prime == parent) { SASSERT(parent); SASSERT(parent->is_cgr()); SASSERT(m_cg_table.contains_ptr(parent)); r2_parents.push_back(parent); continue; } parent->m_cg = parent_prime; SASSERT(!m_cg_table.contains_ptr(parent)); if (parent_prime->m_root != parent->m_root) { bool used_commutativity = pair.second; TRACE("cg", tout << "found new congruence: #" << parent->get_owner_id() << " = #" << parent_prime->get_owner_id() << " used_commutativity: " << used_commutativity << "\n";); push_new_congruence(parent, parent_prime, used_commutativity); } } else { // If congruence closure is not enabled for parent, then I just copy it // to r2_parents r2_parents.push_back(parent); } } } /** \brief A transitivity 'proof' branch is represented by the following sequence starting at n and ending at n->get_root. N1 = n N_{i+1} = N_i->m_trans.m_target and, there is an k such that N_k = n->get_root() This method will invert this branch. */ void context::invert_trans(enode * n) { enode * curr = n->m_trans.m_target; enode * prev = n; eq_justification js = n->m_trans.m_justification; prev->m_trans.m_target = nullptr; prev->m_trans.m_justification = null_eq_justification; prev->m_proof_is_logged = false; while (curr != nullptr) { SASSERT(prev->trans_reaches(n)); enode * new_curr = curr->m_trans.m_target; eq_justification new_js = curr->m_trans.m_justification; curr->m_trans.m_target = prev; curr->m_trans.m_justification = js; curr->m_proof_is_logged = false; prev = curr; js = new_js; curr = new_curr; } } /** \brief Given that r is the root of n, and r contains a theory variable for theory th_id, this method returns a theory variable that is 'closer' to n in the 'proof branch' n -> ... -> r. This method is used to improve the quality of the conflict clauses produced by the logical context. Consider the following example: - Consider the following sequence of equalities: n1 = n2 = n3 = n4 = n5 = n6 - Now, assume that n1 is the root of the equivalence class after each merge. So, the 'proof' branch will have the following shape: n1 <- n2 <- n3 <- n4 <- n5 <- n6 - Assuming that all nodes are attached to theory variable, then the following sequence of equalities is sent to the theory if the method get_closest_var is not used: n1 = n2, n1 = n3, n1 = n4, n1 = n5, n1 = n6 - This sequence is bad, and bad justifications may be produced by theory. For example, assume the following arithmetic constraint n5 < n6 For the arithmetic module, the best justification will be: n1 = n5, n1 = n6 and n5 < n6 This justification contains unnecessary 'junk' to justify that n5 = n6. That is, it proves n5 = n6 using the proofs for n1 = n5 and n1 = n6. When the method get_closest_var is used in the communication with theories, the logical context will send the natural sequence of equalities to the theories: n1 = n2 = n3 = n4 = n5 = n6 */ theory_var context::get_closest_var(enode * n, theory_id th_id) { if (th_id == null_theory_id) return null_theory_var; while (n != nullptr) { theory_var v = n->get_th_var(th_id); if (v != null_theory_var) return v; n = n->m_trans.m_target; } return null_theory_var; } /** \brief Merge the theory variables of n2->get_root() and n1->get_root(), the result is stored in n2->get_root(). New th_var equalities are propagated to the theories. \remark In most cases, an enode is attached to at most one theory var. */ void context::merge_theory_vars(enode * n2, enode * n1, eq_justification js) { enode * r2 = n2->get_root(); enode * r1 = n1->get_root(); if (!r1->has_th_vars() && !r2->has_th_vars()) { TRACE("merge_theory_vars", tout << "Neither have theory vars #" << n1->get_owner()->get_id() << " #" << n2->get_owner()->get_id() << "\n";); return; } theory_id from_th = null_theory_id; if (js.get_kind() == eq_justification::JUSTIFICATION) from_th = js.get_justification()->get_from_theory(); if (r2->m_th_var_list.get_next() == nullptr && r1->m_th_var_list.get_next() == nullptr) { // Common case: r2 and r1 have at most one theory var. theory_id t2 = r2->m_th_var_list.get_th_id(); theory_id t1 = r1->m_th_var_list.get_th_id(); // verbose_stream() << "[merge_theory_vars] t2: " << t2 << ", t1: " << t1 << "\n"; theory_var v2 = m_fparams.m_new_core2th_eq ? get_closest_var(n2, t2) : r2->m_th_var_list.get_th_var(); theory_var v1 = m_fparams.m_new_core2th_eq ? get_closest_var(n1, t1) : r1->m_th_var_list.get_th_var(); TRACE("merge_theory_vars", tout << "v2: " << v2 << " #" << r2->get_owner_id() << ", v1: " << v1 << " #" << r1->get_owner_id() << ", t2: " << t2 << ", t1: " << t1 << "\n";); if (v2 != null_theory_var && v1 != null_theory_var) { if (t1 == t2) { // only send the equality to the theory, if the equality was not propagated by it. if (t1 != from_th) push_new_th_eq(t1, v2, v1); } else { // uncommon case: r2 will have two theory_vars attached to it. r2->add_th_var(v1, t1, m_region); push_new_th_diseqs(r2, v1, get_theory(t1)); push_new_th_diseqs(r1, v2, get_theory(t2)); } } else if (v1 == null_theory_var && v2 != null_theory_var) { push_new_th_diseqs(r1, v2, get_theory(t2)); } else if (v1 != null_theory_var && v2 == null_theory_var) { r2->m_th_var_list.set_th_var(v1); r2->m_th_var_list.set_th_id(t1); TRACE("merge_theory_vars", tout << "push_new_th_diseqs v1: " << v1 << ", t1: " << t1 << "\n";); push_new_th_diseqs(r2, v1, get_theory(t1)); } } else { // r1 and/or r2 have more than one theory variable. TRACE("merge_theory_vars", tout << "#" << r1->get_owner_id() << " == #" << r2->get_owner_id() << "\n";); theory_var_list * l2 = r2->get_th_var_list(); while (l2) { theory_id t2 = l2->get_th_id(); theory_var v2 = m_fparams.m_new_core2th_eq ? get_closest_var(n2, t2) : l2->get_th_var(); SASSERT(v2 != null_theory_var); SASSERT(t2 != null_theory_id); theory_var v1 = m_fparams.m_new_core2th_eq ? get_closest_var(n1, t2) : r1->get_th_var(t2); if (v1 != null_theory_var) { // only send the equality to the theory, if the equality was not propagated by it. if (t2 != from_th) push_new_th_eq(t2, v2, v1); } else { push_new_th_diseqs(r1, v2, get_theory(t2)); } l2 = l2->get_next(); } theory_var_list * l1 = r1->get_th_var_list(); while (l1) { theory_id t1 = l1->get_th_id(); theory_var v1 = m_fparams.m_new_core2th_eq ? get_closest_var(n1, t1) : l1->get_th_var(); SASSERT(v1 != null_theory_var); SASSERT(t1 != null_theory_id); theory_var v2 = r2->get_th_var(t1); if (v2 == null_theory_var) { r2->add_th_var(v1, t1, m_region); push_new_th_diseqs(r2, v1, get_theory(t1)); } l1 = l1->get_next(); } } } /** \brief Propagate the boolean assignment when two equivalence classes are merged. */ void context::propagate_bool_enode_assignment(enode * r1, enode * r2, enode * n1, enode * n2) { SASSERT(n1->is_bool()); SASSERT(n2->is_bool()); SASSERT(r1->is_bool()); SASSERT(r2->is_bool()); if (r2 == m_false_enode || r2 == m_true_enode) { bool sign = r2 == m_false_enode; enode * curr = r1; do { SASSERT(curr != m_false_enode); bool_var v = enode2bool_var(curr); literal l(v, sign); if (get_assignment(l) != l_true) assign(l, mk_justification(eq_root_propagation_justification(curr))); curr = curr->m_next; } while(curr != r1); } else { bool_var v1 = enode2bool_var(n1); bool_var v2 = enode2bool_var(n2); lbool val1 = get_assignment(v1); lbool val2 = get_assignment(v2); if (val1 != val2) { if (val2 == l_undef) propagate_bool_enode_assignment_core(n1, n2); else propagate_bool_enode_assignment_core(n2, n1); } } } /** \brief source and target are boolean enodes, they were proved to be equal, and the boolean variable associated with source is assigned. Then, copy the assignment to the boolean variables associated with target. */ void context::propagate_bool_enode_assignment_core(enode * source, enode * target) { SASSERT(source->is_bool()); SASSERT(target->is_bool()); SASSERT(source->get_root() == target->get_root()); bool_var v_source = enode2bool_var(source); lbool val = get_assignment(v_source); SASSERT(val != l_undef); bool sign = val == l_false; enode * first = target; do { bool_var v2 = enode2bool_var(target); lbool val2 = get_assignment(v2); if (val2 != val) { if (val2 != l_undef && congruent(source, target) && source->get_num_args() > 0) m_dyn_ack_manager.cg_conflict_eh(source->get_owner(), target->get_owner()); assign(literal(v2, sign), mk_justification(mp_iff_justification(source, target))); } target = target->get_next(); } while (first != target); } void context::undo_add_eq(enode * r1, enode * n1, unsigned r2_num_parents) { enode * r2 = r1->get_root(); TRACE("add_eq", tout << "undo_add_eq #" << r1->get_owner_id() << " #" << r2->get_owner_id() << "\n";); // restore r2 class size r2->m_class_size -= r1->m_class_size; // unmerge "equivalence" classes std::swap(r1->m_next, r2->m_next); // remove the parents of r1 that remained as congruence roots enode_vector::iterator it = r2->begin_parents(); enode_vector::iterator end = r2->end_parents(); it += r2_num_parents; for (; it != end; ++it) { enode * parent = *it; if (parent->is_cgc_enabled()) { CTRACE("add_eq", !parent->is_cgr() || !m_cg_table.contains_ptr(parent), tout << "old num_parents: " << r2_num_parents << ", num_parents: " << r2->m_parents.size() << ", parent: #" << parent->get_owner_id() << ", parents: \n"; for (unsigned i = 0; i < r2->m_parents.size(); i++) { tout << "#" << r2->m_parents[i]->get_owner_id() << " "; } display(tout);); SASSERT(parent->is_cgr()); SASSERT(m_cg_table.contains_ptr(parent)); m_cg_table.erase(parent); } } enode * curr = r1; do { curr->m_root = r1; curr = curr->m_next; } while(curr != r1); // restore parents of r2 r2->m_parents.shrink(r2_num_parents); // try to reinsert parents of r1 that are not cgr for (enode * parent : enode::parents(r1)) { TRACE("add_eq_parents", tout << "visiting: #" << parent->get_owner_id() << "\n";); if (parent->is_cgc_enabled()) { enode * cg = parent->m_cg; if (!parent->is_true_eq() && (parent == cg || // parent was root of the congruence class before and after the merge !congruent(parent, cg) // parent was root of the congruence class before but not after the merge )) { enode_bool_pair p = m_cg_table.insert(parent); parent->m_cg = p.first; } } } // restore theory vars if (r2->m_th_var_list.get_next() == nullptr) { // common case: r2 has at most one variable theory_var v2 = r2->m_th_var_list.get_th_var(); if (v2 != null_theory_var) { theory_id t2 = r2->m_th_var_list.get_th_id(); if (get_theory(t2)->get_enode(v2)->get_root() != r2) { SASSERT(get_theory(t2)->get_enode(v2)->get_root() == r1); r2->m_th_var_list.set_th_var(null_theory_var); //remove variable from r2 r2->m_th_var_list.set_th_id(null_theory_id); } } } else { restore_theory_vars(r2, r1); } // 'Proof' tree // r1 -> .. -> n1 -> n2 -> ... -> r2 SASSERT(r1->trans_reaches(r2)); SASSERT(r1->trans_reaches(n1)); n1->m_trans.m_target = nullptr; n1->m_trans.m_justification = null_eq_justification; n1->m_proof_is_logged = false; invert_trans(r1); // --------------- // n1 -> ... -> r1 // n2 -> ... -> r2 SASSERT(n1->trans_reaches(r1)); SASSERT(r1->m_trans.m_target == 0); CASSERT("add_eq", check_invariant()); } /** \brief Auxiliary method for undo_add_eq. It restores the theory variables of a given root enode. This method deletes any theory variable v2 of r2 (for a theory t2) whenever: get_theory(t2)->get_enode(v2)->get_root() != r2 That is, v2 is not equivalent to r2 anymore. */ void context::restore_theory_vars(enode * r2, enode * r1) { SASSERT(r2->get_root() == r2); theory_var_list * new_l2 = nullptr; theory_var_list * l2 = r2->get_th_var_list(); while (l2) { theory_var v2 = l2->get_th_var(); theory_id t2 = l2->get_th_id(); if (get_theory(t2)->get_enode(v2)->get_root() != r2) { SASSERT(get_theory(t2)->get_enode(v2)->get_root() == r1); l2 = l2->get_next(); } else { if (new_l2) { new_l2->set_next(l2); new_l2 = l2; } else { r2->m_th_var_list = *l2; new_l2 = &(r2->m_th_var_list); } l2 = l2->get_next(); } } if (new_l2) { new_l2->set_next(nullptr); } else { r2->m_th_var_list.set_th_var(null_theory_var); r2->m_th_var_list.set_next(nullptr); } } /** \brief This method is invoked when a new disequality is asserted. The disequality is propagated to the theories. */ bool context::add_diseq(enode * n1, enode * n2) { enode * r1 = n1->get_root(); enode * r2 = n2->get_root(); TRACE("add_diseq", tout << "assigning: #" << n1->get_owner_id() << " != #" << n2->get_owner_id() << "\n"; tout << mk_ll_pp(n1->get_owner(), m) << " != "; tout << mk_ll_pp(n2->get_owner(), m) << "\n"; tout << mk_ll_pp(r1->get_owner(), m) << " != "; tout << mk_ll_pp(r2->get_owner(), m) << "\n"; ); DEBUG_CODE( push_trail(push_back_trail(m_diseq_vector)); m_diseq_vector.push_back(enode_pair(n1, n2));); if (r1 == r2) { TRACE("add_diseq_inconsistent", tout << "add_diseq #" << n1->get_owner_id() << " #" << n2->get_owner_id() << " inconsistency, scope_lvl: " << m_scope_lvl << "\n";); //return false; theory_id t1 = r1->m_th_var_list.get_th_id(); if (t1 == null_theory_id) return false; return get_theory(t1)->use_diseqs(); } // Propagate disequalities to theories if (r1->m_th_var_list.get_next() == nullptr && r2->m_th_var_list.get_next() == nullptr) { // common case: r2 and r1 have at most one theory var. theory_id t1 = r1->m_th_var_list.get_th_id(); theory_var v1 = m_fparams.m_new_core2th_eq ? get_closest_var(n1, t1) : r1->m_th_var_list.get_th_var(); theory_var v2 = m_fparams.m_new_core2th_eq ? get_closest_var(n2, t1) : r2->m_th_var_list.get_th_var(); TRACE("add_diseq", tout << "one theory diseq\n"; tout << v1 << " != " << v2 << "\n"; tout << "th1: " << t1 << " th2: " << r2->m_th_var_list.get_th_id() << "\n"; ); if (t1 != null_theory_id && v1 != null_theory_var && v2 != null_theory_var && t1 == r2->m_th_var_list.get_th_id()) { if (get_theory(t1)->use_diseqs()) push_new_th_diseq(t1, v1, v2); } } else { theory_var_list * l1 = r1->get_th_var_list(); while (l1) { theory_id t1 = l1->get_th_id(); theory_var v1 = m_fparams.m_new_core2th_eq ? get_closest_var(n1, t1) : l1->get_th_var(); theory * th = get_theory(t1); TRACE("add_diseq", tout << m.get_family_name(t1) << "\n";); if (th->use_diseqs()) { theory_var v2 = m_fparams.m_new_core2th_eq ? get_closest_var(n2, t1) : r2->get_th_var(t1); if (v2 != null_theory_var) push_new_th_diseq(t1, v1, v2); } l1 = l1->get_next(); } } return true; } /** \brief Return true if n1 and n2 are known to be disequal in the logical context. */ bool context::is_diseq(enode * n1, enode * n2) const { SASSERT(m.get_sort(n1->get_owner()) == m.get_sort(n2->get_owner())); context * _this = const_cast(this); if (!m_is_diseq_tmp) { app * eq = m.mk_eq(n1->get_owner(), n2->get_owner()); m.inc_ref(eq); _this->m_is_diseq_tmp = enode::mk_dummy(m, m_app2enode, eq); } else if (m.get_sort(m_is_diseq_tmp->get_owner()->get_arg(0)) != m.get_sort(n1->get_owner())) { m.dec_ref(m_is_diseq_tmp->get_owner()); app * eq = m.mk_eq(n1->get_owner(), n2->get_owner()); m.inc_ref(eq); m_is_diseq_tmp->m_func_decl_id = UINT_MAX; m_is_diseq_tmp->m_owner = eq; } m_is_diseq_tmp->m_args[0] = n1; m_is_diseq_tmp->m_args[1] = n2; SASSERT(m_is_diseq_tmp->get_num_args() == 2); enode * r = m_cg_table.find(m_is_diseq_tmp); SASSERT((r != 0) == m_cg_table.contains(m_is_diseq_tmp)); TRACE("is_diseq", tout << "r: " << r << "\n";); if (r) { SASSERT(r->is_eq()); literal l = enode2literal(r->get_root()); // SASSERT(result == is_diseq_slow(n1, n2)); return l != true_literal && (l == false_literal || (is_relevant(l) && get_assignment(l) == l_false)); } CTRACE("is_diseq_bug", is_diseq_slow(n1, n2), tout << "#" << n1->get_owner_id() << " #" << n2->get_owner_id() << "\n";); return false; } /** \brief Slow version of is_diseq */ bool context::is_diseq_slow(enode * n1, enode * n2) const { if (n1->get_num_parents() > n2->get_num_parents()) std::swap(n1, n2); for (enode * parent : enode::parents(n1)) { if (parent->is_eq() && is_relevant(parent->get_owner()) && get_assignment(enode2bool_var(parent)) == l_false && ((parent->get_arg(0)->get_root() == n1->get_root() && parent->get_arg(1)->get_root() == n2->get_root()) || (parent->get_arg(1)->get_root() == n1->get_root() && parent->get_arg(0)->get_root() == n2->get_root()))) { TRACE("is_diseq_bug", tout << "parent: #" << parent->get_owner_id() << ", parent->root: #" << parent->get_root()->get_owner_id() << " assignment(parent): " << get_assignment(enode2bool_var(parent)) << " args: #" << parent->get_arg(0)->get_owner_id() << " #" << parent->get_arg(1)->get_owner_id() << "\n";); return true; } } return false; } #define SMALL_NUM_PARENTS 3 bool context::is_ext_diseq(enode * n1, enode * n2, unsigned depth) { enode * r1 = n1->get_root(); enode * r2 = n2->get_root(); if (r1 == r2) return false; if (r1->is_interpreted() && r2->is_interpreted()) return true; if (is_diseq(n1, n2)) return true; if (r1->get_num_parents() > r2->get_num_parents()) { std::swap(n1, n2); std::swap(r1, r2); } if (depth == 0) return false; if (r1->get_num_parents() < SMALL_NUM_PARENTS) { TRACE("is_ext_diseq", tout << enode_pp(n1, *this) << " " << enode_pp(n2, *this) << " " << depth << "\n";); for (enode * p1 : enode::parents(r1)) { if (!is_relevant(p1)) continue; if (p1->is_eq()) continue; if (!p1->is_cgr()) continue; func_decl * f = p1->get_decl(); TRACE("is_ext_diseq", tout << "p1: " << enode_pp(p1, *this) << "\n";); unsigned num_args = p1->get_num_args(); for (enode * p2 : enode::parents(r2)) { if (!is_relevant(p2)) continue; if (p2->is_eq()) continue; if (!p2->is_cgr()) continue; TRACE("is_ext_diseq", tout << "p2: " << enode_pp(p2, *this) << "\n";); if (p1->get_root() != p2->get_root() && p2->get_decl() == f && p2->get_num_args() == num_args) { unsigned j = 0; for (j = 0; j < num_args; j++) { enode * arg1 = p1->get_arg(j)->get_root(); enode * arg2 = p2->get_arg(j)->get_root(); if (arg1 == arg2) continue; if ((arg1 == r1 || arg1 == r2) && (arg2 == r1 || arg2 == r2)) continue; break; } if (j == num_args) { TRACE("is_ext_diseq", tout << "found parents: " << enode_pp(p1, *this) << " " << enode_pp(p2, *this) << "\n";); if (is_ext_diseq(p1, p2, depth - 1)) return true; } } } } } else { if (depth >= m_almost_cg_tables.size()) { unsigned old_sz = m_almost_cg_tables.size(); m_almost_cg_tables.resize(depth+1); for (unsigned i = old_sz; i < depth + 1; i++) m_almost_cg_tables[i] = alloc(almost_cg_table); } almost_cg_table & table = *(m_almost_cg_tables[depth]); table.reset(r1, r2); for (enode * p1 : enode::parents(r1)) { if (!is_relevant(p1)) continue; if (p1->is_eq()) continue; if (!p1->is_cgr()) continue; table.insert(p1); } if (table.empty()) return false; for (enode * p2 : enode::parents(r2)) { if (!is_relevant(p2)) continue; if (p2->is_eq()) continue; if (!p2->is_cgr()) continue; list * ps = table.find(p2); while (ps) { enode * p1 = ps->head(); if (p1->get_root() != p2->get_root() && is_ext_diseq(p1, p2, depth - 1)) return true; ps = ps->tail(); } } } return false; } /** \brief Return an enode n congruent to (f args). If there is no such enode in the E-graph, then return 0. */ enode * context::get_enode_eq_to(func_decl * f, unsigned num_args, enode * const * args) { enode * tmp = m_tmp_enode.set(f, num_args, args); enode * r = m_cg_table.find(tmp); #ifdef Z3DEBUG if (r != nullptr) { SASSERT(r->get_owner()->get_decl() == f); SASSERT(r->get_num_args() == num_args); if (r->is_commutative()) { // TODO } else { for (unsigned i = 0; i < num_args; i++) { expr * arg = r->get_owner()->get_arg(i); SASSERT(e_internalized(arg)); enode * _arg = get_enode(arg); CTRACE("eq_to_bug", args[i]->get_root() != _arg->get_root(), tout << "#" << args[i]->get_owner_id() << " #" << args[i]->get_root()->get_owner_id() << " #" << _arg->get_owner_id() << " #" << _arg->get_root()->get_owner_id() << "\n"; tout << "#" << r->get_owner_id() << "\n"; display(tout);); SASSERT(args[i]->get_root() == _arg->get_root()); } } } #endif return r; } /** \brief Process the equality propagation queue. \remark The method assign_eq adds a new entry on this queue. */ bool context::propagate_eqs() { unsigned i = 0; for (; i < m_eq_propagation_queue.size() && !get_cancel_flag(); i++) { new_eq & entry = m_eq_propagation_queue[i]; add_eq(entry.m_lhs, entry.m_rhs, entry.m_justification); if (inconsistent()) { m_eq_propagation_queue.reset(); return false; } } m_eq_propagation_queue.reset(); return true; } /** \brief Process equalities, theory atoms, etc. */ bool context::propagate_atoms() { SASSERT(!inconsistent()); for (unsigned i = 0; i < m_atom_propagation_queue.size() && !get_cancel_flag(); i++) { SASSERT(!inconsistent()); literal l = m_atom_propagation_queue[i]; bool_var v = l.var(); bool_var_data & d = get_bdata(v); lbool val = get_assignment(v); TRACE("propagate_atoms", tout << "propagating atom, #" << bool_var2expr(v)->get_id() << ", is_enode(): " << d.is_enode() << " tag: " << (d.is_eq()?"eq":"") << (d.is_theory_atom()?"th":"") << (d.is_quantifier()?"q":"") << " " << l << "\n";); SASSERT(val != l_undef); if (d.is_enode()) propagate_bool_var_enode(v); if (inconsistent()) return false; if (d.is_eq()) { app * n = to_app(m_bool_var2expr[v]); SASSERT(m.is_eq(n)); expr * lhs = n->get_arg(0); expr * rhs = n->get_arg(1); if (m.is_bool(lhs)) { // no-op } else if (val == l_true) { add_eq(get_enode(lhs), get_enode(rhs), eq_justification(l)); } else { TRACE("add_diseq", display_eq_detail(tout, bool_var2enode(v));); if (!add_diseq(get_enode(lhs), get_enode(rhs)) && !inconsistent()) { literal n_eq = literal(l.var(), true); set_conflict(b_justification(mk_justification(eq_propagation_justification(get_enode(lhs), get_enode(rhs)))), n_eq); } } } else if (d.is_theory_atom()) { theory * th = m_theories.get_plugin(d.get_theory()); SASSERT(th); th->assign_eh(v, val == l_true); } else if (d.is_quantifier()) { // Remark: when RELEVANCY_LEMMA is true, a quantifier can be asserted to false and marked as relevant. // This happens when a quantifier is part of a lemma (conflict-clause), and this conflict clause // becomes an unit-clause and the remaining literal is the negation of a quantifier. CTRACE("assign_quantifier_bug", get_assignment(v) != l_true, tout << "#" << bool_var2expr(v)->get_id() << " val: " << get_assignment(v) << "\n"; tout << mk_pp(bool_var2expr(v), m) << "\n"; display(tout);); SASSERT(is_quantifier(m_bool_var2expr[v])); if (get_assignment(v) == l_true) { // All universal quantifiers have positive polarity in the input formula. // So, we can ignore quantifiers assigned to false. assign_quantifier(to_quantifier(m_bool_var2expr[v])); } } if (inconsistent()) return false; } m_atom_propagation_queue.reset(); return true; } class set_var_theory_trail : public trail { bool_var m_var; public: set_var_theory_trail(bool_var v):m_var(v) {} void undo(context & ctx) override { bool_var_data & d = ctx.m_bdata[m_var]; d.reset_notify_theory(); } }; void context::set_var_theory(bool_var v, theory_id tid) { SASSERT(get_var_theory(v) == null_theory_var); SASSERT(tid > 0 && tid <= 255); SASSERT(get_intern_level(v) <= m_scope_lvl); if (m_scope_lvl > get_intern_level(v)) push_trail(set_var_theory_trail(v)); bool_var_data & d = m_bdata[v]; d.set_notify_theory(tid); } /** \brief Propagate the truth value assigned to v, to the enode associated with v. Let n be the enode associated with v. Then, this method merges n = true_term (n = false_term) if v was assigned to true (false). */ void context::propagate_bool_var_enode(bool_var v) { SASSERT(get_assignment(v) != l_undef); SASSERT(get_bdata(v).is_enode()); lbool val = get_assignment(v); TRACE("propagate_bool_var_enode_bug", tout << "var: " << v << " #" << bool_var2expr(v)->get_id() << "\n";); SASSERT(v < static_cast(m_b_internalized_stack.size())); enode * n = bool_var2enode(v); CTRACE("mk_bool_var", !n, tout << "No enode for " << v << "\n";); bool sign = val == l_false; if (n->merge_tf()) add_eq(n, sign ? m_false_enode : m_true_enode, eq_justification(literal(v, sign))); enode * r = n->get_root(); if (r == m_true_enode || r == m_false_enode) return; // Move truth value to other elements of the equivalence class if: // 1) n is the root of the equivalence class // 2) n is not the root, but the variable associated with the root is unassigned. if (n == r || !is_relevant(r) || // <<<< added to fix propagation bug. get_assignment(enode2bool_var(r)) != val) { enode * first = n; n = n->get_next(); while (n != first) { bool_var v2 = enode2bool_var(n); if (get_assignment(v2) != val) assign(literal(v2, sign), mk_justification(mp_iff_justification(first, n))); n = n->get_next(); } } } /** \brief Traverse the disequalities of r's equivalence class, and propagate them to the theory. */ void context::push_new_th_diseqs(enode * r, theory_var v, theory * th) { if (!th->use_diseqs()) { TRACE("push_new_th_diseqs", tout << m.get_family_name(th->get_id()) << " not using diseqs\n";); return; } TRACE("push_new_th_diseqs", tout << "#" << r->get_owner_id() << " v" << v << "\n";); theory_id th_id = th->get_id(); for (enode * parent : r->get_parents()) { CTRACE("parent_bug", parent == 0, tout << "#" << r->get_owner_id() << ", num_parents: " << r->get_num_parents() << "\n"; display(tout);); if (parent->is_eq()) { bool_var bv = get_bool_var_of_id(parent->get_owner_id()); if (get_assignment(bv) == l_false) { enode * lhs = parent->get_arg(0); enode * rhs = parent->get_arg(1); TRACE("push_new_th_diseqs", tout << "#" << parent->get_owner_id() << " "; tout << "lhs: #" << lhs->get_owner_id() << ", rhs: #" << rhs->get_owner_id() << ", lhs->root: #" << lhs->get_root()->get_owner_id() << ", rhs->root: #" << rhs->get_root()->get_owner_id() << ", r: #" << r->get_owner_id() << ", r->root: #" << r->get_root()->get_owner_id() << "\n"; ); CTRACE("push_new_th_diseqs", lhs->get_root() != r->get_root() && rhs->get_root() != r->get_root(), tout << "lhs: #" << lhs->get_owner_id() << ", rhs: #" << rhs->get_owner_id() << ", lhs->root: #" << lhs->get_root()->get_owner_id() << ", rhs->root: #" << rhs->get_root()->get_owner_id() << ", r: #" << r->get_owner_id() << ", r->root: #" << r->get_root()->get_owner_id() << "\n"; display(tout);); SASSERT(lhs->get_root() == r->get_root() || rhs->get_root() == r->get_root()); if (rhs->get_root() == r->get_root()) std::swap(lhs, rhs); enode * rhs_root = rhs->get_root(); theory_var rhs_var = m_fparams.m_new_core2th_eq ? get_closest_var(rhs, th_id) : rhs_root->get_th_var(th_id); if (m_fparams.m_new_core2th_eq) { theory_var _v = get_closest_var(lhs, th_id); if (_v != null_theory_var) v = _v; } if (rhs_var != null_theory_var && v != rhs_var /* if v == rhs_var then the context will detect the inconsistency. */) push_new_th_diseq(th_id, v, rhs_var); } } } } /** \brief Return the truth assignment for an expression that is attached to a boolean variable. \pre The expression must be attached to a boolean variable. */ inline lbool context::get_assignment_core(expr * n) const { CTRACE("get_assignment_bug", !b_internalized(n), tout << "n:\n" << mk_pp(n, m) << "\n"; display(tout);); SASSERT(b_internalized(n)); unsigned id = n->get_id(); bool_var var = get_bool_var_of_id(id); SASSERT(var != null_bool_var); return get_assignment(var); } /** \brief Return the truth assignment for an expression. If the expression is a not-application, then its child is inspected instead. */ lbool context::get_assignment(expr * n) const { if (m.is_false(n)) return l_false; expr* arg = nullptr; if (m.is_not(n, arg)) return ~get_assignment_core(arg); return get_assignment_core(n); } lbool context::find_assignment(expr * n) const { if (m.is_false(n)) return l_false; expr* arg = nullptr; if (m.is_not(n, arg)) { if (b_internalized(arg)) return ~get_assignment_core(arg); return l_undef; } if (b_internalized(n)) return get_assignment(n); return l_undef; } /** \brief Return the assignment of a 'boolean' enode. If the enode is not boolean, then return l_undef. */ lbool context::get_assignment(enode * n) const { expr * owner = n->get_owner(); if (!m.is_bool(owner)) return l_undef; if (n == m_false_enode) return l_false; bool_var v = get_bool_var_of_id(owner->get_id()); CTRACE("missing_propagation", v == null_bool_var, tout << mk_pp(owner, m) << "\n";); return get_assignment(v); } /** \brief Return set of assigned literals as expressions. */ void context::get_assignments(expr_ref_vector& assignments) { for (literal lit : m_assigned_literals) { expr_ref e(m); literal2expr(lit, e); assignments.push_back(std::move(e)); } } void context::relevant_eh(expr * n) { if (b_internalized(n)) { bool_var v = get_bool_var(n); bool_var_data & d = get_bdata(v); SASSERT(relevancy()); // Quantifiers are only asserted when marked as relevant. // Other atoms are only asserted when marked as relevant if m_relevancy_lvl >= 2 if (d.is_atom() && (d.is_quantifier() || m_fparams.m_relevancy_lvl >= 2)) { lbool val = get_assignment(v); if (val != l_undef) m_atom_propagation_queue.push_back(literal(v, val == l_false)); } } TRACE("propagate_relevancy", tout << "marking as relevant:\n" << mk_bounded_pp(n, m) << " " << m_scope_lvl << "\n";); m_case_split_queue->relevant_eh(n); if (is_app(n)) { if (e_internalized(n)) { SASSERT(relevancy()); enode * e = get_enode(n); m_qmanager->relevant_eh(e); } theory * propagated_th = nullptr; family_id fid = to_app(n)->get_family_id(); if (fid != m.get_basic_family_id()) { theory * th = get_theory(fid); if (th != nullptr) { th->relevant_eh(to_app(n)); propagated_th = th; // <<< mark that relevancy_eh was already invoked for theory th. } } if (e_internalized(n)) { enode * e = get_enode(n); theory_var_list * l = e->get_th_var_list(); while (l) { theory_id th_id = l->get_th_id(); theory * th = get_theory(th_id); // I don't want to invoke relevant_eh twice for the same n. if (th != propagated_th) th->relevant_eh(to_app(n)); l = l->get_next(); } } } } /** \brief Propagate relevancy using the queue of new assigned literals located at [qhead, m_assigned_literals.size()). */ void context::propagate_relevancy(unsigned qhead) { if (!relevancy()) return; unsigned sz = m_assigned_literals.size(); while (qhead < sz) { literal l = m_assigned_literals[qhead]; SASSERT(get_assignment(l) == l_true); qhead++; bool_var var = l.var(); expr * n = m_bool_var2expr[var]; m_relevancy_propagator->assign_eh(n, !l.sign()); } m_relevancy_propagator->propagate(); } bool context::propagate_theories() { for (theory * t : m_theory_set) { t->propagate(); if (inconsistent()) return false; } return true; } void context::propagate_th_eqs() { for (unsigned i = 0; i < m_th_eq_propagation_queue.size() && !inconsistent(); i++) { new_th_eq curr = m_th_eq_propagation_queue[i]; theory * th = get_theory(curr.m_th_id); SASSERT(th); th->new_eq_eh(curr.m_lhs, curr.m_rhs); DEBUG_CODE( push_trail(push_back_trail(m_propagated_th_eqs)); m_propagated_th_eqs.push_back(curr);); } m_th_eq_propagation_queue.reset(); } void context::propagate_th_diseqs() { for (unsigned i = 0; i < m_th_diseq_propagation_queue.size() && !inconsistent(); i++) { new_th_eq curr = m_th_diseq_propagation_queue[i]; theory * th = get_theory(curr.m_th_id); SASSERT(th); th->new_diseq_eh(curr.m_lhs, curr.m_rhs); DEBUG_CODE( push_trail(push_back_trail(m_propagated_th_diseqs)); m_propagated_th_diseqs.push_back(curr);); } m_th_diseq_propagation_queue.reset(); } bool context::can_theories_propagate() const { for (theory* t : m_theory_set) { if (t->can_propagate()) { return true; } } return false; } bool context::can_propagate() const { return m_qhead != m_assigned_literals.size() || m_relevancy_propagator->can_propagate() || !m_atom_propagation_queue.empty() || m_qmanager->can_propagate() || can_theories_propagate() || !m_eq_propagation_queue.empty() || !m_th_eq_propagation_queue.empty() || !m_th_diseq_propagation_queue.empty(); } /** \brief unit propagation. Cancelation is not safe during propagation at base level because congruences cannot be retracted to a consistent state. */ bool context::propagate() { TRACE("propagate", tout << "propagating... " << m_qhead << ":" << m_assigned_literals.size() << "\n";); while (true) { if (inconsistent()) return false; unsigned qhead = m_qhead; { scoped_suspend_rlimit _suspend_cancel(m.limit(), at_base_level()); if (!bcp()) return false; if (!propagate_th_case_split(qhead)) return false; SASSERT(!inconsistent()); propagate_relevancy(qhead); if (inconsistent()) return false; if (!propagate_atoms()) return false; if (!propagate_eqs()) return false; propagate_th_eqs(); propagate_th_diseqs(); if (inconsistent()) return false; if (!propagate_theories()) return false; } if (!get_cancel_flag()) { scoped_suspend_rlimit _suspend_cancel(m.limit(), at_base_level()); m_qmanager->propagate(); } if (inconsistent()) return false; if (resource_limits_exceeded()) { m_qhead = qhead; return true; } if (!can_propagate()) { CASSERT("diseq_bug", inconsistent() || check_missing_diseq_conflict()); CASSERT("eqc_bool", check_eqc_bool_assignment()); return true; } } } void context::set_conflict(const b_justification & js, literal not_l) { if (!inconsistent()) { TRACE("set_conflict", display_literal_verbose(tout << m_scope_lvl << " ", not_l); display(tout << " ", js); ); m_conflict = js; m_not_l = not_l; } } void context::assign_quantifier(quantifier * q) { TRACE("assumption", tout << mk_pp(q, m) << "\n";); m_qmanager->assign_eh(q); } bool context::contains_instance(quantifier * q, unsigned num_bindings, enode * const * bindings) { return m_fingerprints.contains(q, q->get_id(), num_bindings, bindings); } bool context::add_instance(quantifier * q, app * pat, unsigned num_bindings, enode * const * bindings, expr* def, unsigned max_generation, unsigned min_top_generation, unsigned max_top_generation, vector> & used_enodes) { return m_qmanager->add_instance(q, pat, num_bindings, bindings, def, max_generation, min_top_generation, max_top_generation, used_enodes); } void context::rescale_bool_var_activity() { TRACE("case_split", tout << "rescale\n";); svector::iterator it = m_activity.begin(); svector::iterator end = m_activity.end(); for (; it != end; ++it) *it *= INV_ACTIVITY_LIMIT; m_bvar_inc *= INV_ACTIVITY_LIMIT; } /** \brief Execute next case split, return false if there are no more case splits to be performed. */ bool context::decide() { if (at_search_level() && !m_tmp_clauses.empty()) { switch (decide_clause()) { case l_true: // already satisfied break; case l_undef: // made a decision return true; case l_false: // inconsistent return false; } } bool_var var; lbool phase; m_case_split_queue->next_case_split(var, phase); if (var == null_bool_var) { return false; } TRACE_CODE({ static unsigned counter = 0; counter++; if (counter % 100 == 0) { TRACE("activity_profile", for (unsigned i=0; iget_phase(var); if (ph != l_undef) { is_pos = ph == l_true; break; } } Z3_fallthrough; case PS_CACHING: case PS_CACHING_CONSERVATIVE: case PS_CACHING_CONSERVATIVE2: if (m_phase_cache_on && d.m_phase_available) { TRACE("phase_selection", tout << "using cached value, is_pos: " << m_bdata[var].m_phase << ", var: p" << var << "\n";); is_pos = m_bdata[var].m_phase; } else { TRACE("phase_selection", tout << "setting to false\n";); is_pos = m_phase_default; } break; case PS_ALWAYS_FALSE: is_pos = false; break; case PS_ALWAYS_TRUE: is_pos = true; break; case PS_RANDOM: is_pos = (m_random() % 2 == 0); break; case PS_OCCURRENCE: { literal l(var); is_pos = m_lit_occs[l.index()].size() > m_lit_occs[(~l).index()].size(); break; } default: is_pos = false; UNREACHABLE(); } } } TRACE("decide", tout << "case split " << (is_pos?"pos":"neg") << " p" << var << "\n" << "activity: " << get_activity(var) << "\n";); assign(literal(var, !is_pos), b_justification::mk_axiom(), true); return true; } /** \brief Update counter that is used to enable/disable phase caching. */ void context::update_phase_cache_counter() { m_phase_counter++; if (m_phase_cache_on) { if (m_phase_counter >= m_fparams.m_phase_caching_on) { m_phase_counter = 0; m_phase_cache_on = false; if (m_fparams.m_phase_selection == PS_CACHING_CONSERVATIVE2) m_phase_default = !m_phase_default; } } else { if (m_phase_counter >= m_fparams.m_phase_caching_off) { m_phase_counter = 0; m_phase_cache_on = true; if (m_fparams.m_phase_selection == PS_CACHING_CONSERVATIVE2) m_phase_default = !m_phase_default; } } } /** \brief Create an internal backtracking point */ void context::push_scope() { if (m.has_trace_stream() && !m_is_auxiliary) m.trace_stream() << "[push] " << m_scope_lvl << "\n"; m_scope_lvl++; m_region.push_scope(); m_scopes.push_back(scope()); scope & s = m_scopes.back(); m_relevancy_propagator->push(); s.m_assigned_literals_lim = m_assigned_literals.size(); s.m_trail_stack_lim = m_trail_stack.size(); s.m_aux_clauses_lim = m_aux_clauses.size(); s.m_justifications_lim = m_justifications.size(); s.m_units_to_reassert_lim = m_units_to_reassert.size(); m_qmanager->push(); m_fingerprints.push_scope(); m_case_split_queue->push_scope(); m_asserted_formulas.push_scope(); for (theory* t : m_theory_set) t->push_scope_eh(); CASSERT("context", check_invariant()); } /** \brief Execute generic undo-objects. */ void context::undo_trail_stack(unsigned old_size) { ::undo_trail_stack(*this, m_trail_stack, old_size); } /** \brief Remove watch literal idx from the given clause. \pre idx must be 0 or 1. */ void context::remove_watch_literal(clause * cls, unsigned idx) { m_watches[(~cls->get_literal(idx)).index()].remove_clause(cls); } /** \brief Remove boolean variable from watch lists. */ void context::remove_watch(bool_var v) { literal lit(v); m_watches[lit.index()].reset(); m_watches[(~lit).index()].reset(); } /** \brief Update the index used for backward subsumption. */ void context::remove_lit_occs(clause * cls) { unsigned num_lits = cls->get_num_literals(); for (unsigned i = 0; i < num_lits; i++) { literal l = cls->get_literal(i); m_lit_occs[l.index()].erase(cls); } } void context::remove_cls_occs(clause * cls) { remove_watch_literal(cls, 0); remove_watch_literal(cls, 1); if (lit_occs_enabled()) remove_lit_occs(cls); } /** \brief Delete the given clause. \pre Clause is not in the reinit stack. */ void context::del_clause(bool log, clause * cls) { SASSERT(m_flushing || !cls->in_reinit_stack()); if (log) m_clause_proof.del(*cls); CTRACE("context", !m_flushing, display_clause_smt2(tout << "deleting ", *cls) << "\n";); if (!cls->deleted()) remove_cls_occs(cls); cls->deallocate(m); m_stats.m_num_del_clause++; } /** \brief Delete the clauses in v at locations [old_size .. v.size()) Reduce the size of v to old_size. */ void context::del_clauses(clause_vector & v, unsigned old_size) { SASSERT(old_size <= v.size()); clause_vector::iterator begin = v.begin() + old_size; clause_vector::iterator it = v.end(); while (it != begin) { --it; del_clause(false, *it); } v.shrink(old_size); } /** \brief Undo variable assignments. */ void context::unassign_vars(unsigned old_lim) { SASSERT(old_lim <= m_assigned_literals.size()); unsigned i = m_assigned_literals.size(); while (i != old_lim) { --i; literal l = m_assigned_literals[i]; m_assignment[l.index()] = l_undef; m_assignment[(~l).index()] = l_undef; bool_var v = l.var(); bool_var_data & d = get_bdata(v); d.set_null_justification(); m_case_split_queue->unassign_var_eh(v); } m_assigned_literals.shrink(old_lim); m_qhead = old_lim; SASSERT(m_qhead == m_assigned_literals.size()); } /** \brief Invoke method del_eh for the justification that will be deleted. If the method in_region() returns false, then delete operator is invoked. */ void context::del_justifications(ptr_vector & justifications, unsigned old_lim) { SASSERT(old_lim <= justifications.size()); unsigned i = justifications.size(); while (i != old_lim) { --i; justification * js = justifications[i]; js->del_eh(m); if (!js->in_region()) { dealloc(js); } else { // If the justification is in a region, then explicitly invoke the destructor. // This is needed because some justification objects contains vectors. // The destructors of these vectors need to be invoked. js->~justification(); } } justifications.shrink(old_lim); } /** \brief Return true if all literals of c are assigned to false. */ bool context::is_empty_clause(clause const * c) const { unsigned num_lits = c->get_num_literals(); for(unsigned i = 0; i < num_lits; i++) { literal l = c->get_literal(i); if (get_assignment(l) != l_false) return false; } return true; } /** \brief Return true if the given clause contains one and only one unassigned literal. */ bool context::is_unit_clause(clause const * c) const { bool found = false; unsigned num_lits = c->get_num_literals(); for(unsigned i = 0; i < num_lits; i++) { literal l = c->get_literal(i); switch (get_assignment(l)) { case l_false: break; // skip case l_undef: if (found) return false; else found = true; break; case l_true: return false; // clause is already satisfied. } } return found; } /** \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 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. */ void context::cache_generation(unsigned new_scope_lvl) { if (!m_clauses_to_reinit.empty()) { unsigned lim = m_scope_lvl; if (m_clauses_to_reinit.size() <= lim) { SASSERT(!m_clauses_to_reinit.empty()); lim = m_clauses_to_reinit.size() - 1; } for (unsigned i = new_scope_lvl; i <= lim; i++) { clause_vector & v = m_clauses_to_reinit[i]; for (clause* cls : v) { cache_generation(cls, new_scope_lvl); } } } if (!m_units_to_reassert.empty()) { scope & s = m_scopes[new_scope_lvl]; unsigned i = s.m_units_to_reassert_lim; unsigned sz = m_units_to_reassert.size(); for (; i < sz; i++) { expr * unit = m_units_to_reassert.get(i); cache_generation(unit, new_scope_lvl); } } } /** \brief See cache_generation(unsigned new_scope_lvl) */ void context::cache_generation(clause const * cls, unsigned new_scope_lvl) { cache_generation(cls->get_num_literals(), cls->begin(), new_scope_lvl); } /** \brief See cache_generation(unsigned new_scope_lvl) */ void context::cache_generation(unsigned num_lits, literal const * lits, unsigned new_scope_lvl) { for(unsigned i = 0; i < num_lits; i++) { bool_var v = lits[i].var(); unsigned ilvl = get_intern_level(v); if (ilvl > new_scope_lvl) cache_generation(bool_var2expr(v), new_scope_lvl); } } /** \brief See cache_generation(unsigned new_scope_lvl) */ void context::cache_generation(expr * n, unsigned new_scope_lvl) { ptr_buffer todo; todo.push_back(n); while (!todo.empty()) { expr * n = todo.back(); todo.pop_back(); if (m_cache_generation_visited.contains(n)) continue; m_cache_generation_visited.insert(n); if (is_app(n)) { if (e_internalized(n)) { enode * e = get_enode(n); unsigned ilvl = e->get_iscope_lvl(); if (ilvl <= new_scope_lvl) continue; // node and its children will not be recreated during backtracking TRACE("cached_generation", tout << "caching: #" << n->get_id() << " " << e->get_generation() << "\n";); m_cached_generation.insert(n, e->get_generation()); } for (expr * arg : *to_app(n)) { if (is_app(arg) || is_quantifier(arg)) todo.push_back(arg); } } else if (is_quantifier(n) && b_internalized(n)) { m_cached_generation.insert(n, m_qmanager->get_generation(to_quantifier(n))); todo.push_back(to_quantifier(n)->get_expr()); } } } /** \brief See cache_generation(unsigned new_scope_lvl) */ void context::reset_cache_generation() { m_cache_generation_visited.reset(); m_cached_generation.reset(); } /** \brief Reinitialize learned clauses (lemmas) that contain boolean variables that were deleted during backtracking. \remark num_bool_vars contains the number of boolean variables alive after backtracking. So, a clause contains a dead variable if it contains a literal l where l.var() >= num_bool_vars. */ void context::reinit_clauses(unsigned num_scopes, unsigned num_bool_vars) { TRACE("reinit_clauses_bug", display_watch_lists(tout);); if (m_clauses_to_reinit.empty()) return; unsigned lim = m_scope_lvl + num_scopes; if (m_clauses_to_reinit.size() <= lim) { SASSERT(!m_clauses_to_reinit.empty()); lim = m_clauses_to_reinit.size() - 1; } for (unsigned i = m_scope_lvl+1; i <= lim; i++) { clause_vector & v = m_clauses_to_reinit[i]; for (clause* cls : v) { if (cls->deleted()) { cls->release_atoms(m); cls->m_reinit = false; cls->m_reinternalize_atoms = false; continue; } SASSERT(cls->in_reinit_stack()); bool keep = false; if (cls->reinternalize_atoms()) { SASSERT(cls->get_num_atoms() == cls->get_num_literals()); for (unsigned j = 0; j < 2; j++) { literal l = cls->get_literal(j); if (l.var() < static_cast(num_bool_vars)) { // This boolean variable was not deleted during backtracking // // So, it is still a watch literal. I remove the watch, since // the clause may have new watch-literals after reinitialization. remove_watch_literal(cls, j); } } unsigned num = cls->get_num_literals(); if (lit_occs_enabled()) { for (unsigned j = 0; j < num; j++) { literal l = cls->get_literal(j); if (l.var() < static_cast(num_bool_vars)) { // This boolean variable was not deleted during backtracking // // So, remove it from lit_occs. m_lit_occs[l.index()].erase(cls); } } } unsigned ilvl = 0; (void)ilvl; for (unsigned j = 0; j < num; j++) { expr * atom = cls->get_atom(j); bool sign = cls->get_atom_sign(j); // Atom can be (NOT foo). This can happen, for example, when // the NOT-application is a child of an uninterpreted function symbol. // So, when reinternalizing the NOT-atom I should set the gate_ctx to false, // and force expression to be reinternalized. // Otherwise I set gate_ctx to true bool gate_ctx = !m.is_not(atom); internalize(atom, gate_ctx); SASSERT(b_internalized(atom)); bool_var v = get_bool_var(atom); DEBUG_CODE({ if (get_intern_level(v) > ilvl) ilvl = get_intern_level(v); }); literal l(v, sign); cls->set_literal(j, l); } SASSERT(ilvl <= m_scope_lvl); int w1_idx = select_watch_lit(cls, 0); cls->swap_lits(0, w1_idx); int w2_idx = select_watch_lit(cls, 1); cls->swap_lits(1, w2_idx); add_watch_literal(cls, 0); add_watch_literal(cls, 1); if (lit_occs_enabled()) add_lit_occs(cls); literal l1 = cls->get_literal(0); literal l2 = cls->get_literal(1); if (get_assignment(l1) == l_false) set_conflict(b_justification(cls)); else if (get_assignment(l2) == l_false) assign(l1, b_justification(cls)); TRACE("reinit_clauses", tout << "reinit clause:\n"; display_clause_detail(tout, cls); tout << "\n"; tout << "activity: " << cls->get_activity() << ", num_bool_vars: " << num_bool_vars << ", scope_lvl: " << m_scope_lvl << "\n";); keep = true; } else { SASSERT(!cls->reinternalize_atoms()); literal l1 = cls->get_literal(0); literal l2 = cls->get_literal(1); if (get_assignment(l1) == l_false && is_empty_clause(cls)) { set_conflict(b_justification(cls)); keep = true; } else if (get_assignment(l2) == l_false && get_assignment(l1) == l_undef && is_unit_clause(cls)) { assign(l1, b_justification(cls)); keep = true; } } if (keep && m_scope_lvl > m_base_lvl) { m_clauses_to_reinit[m_scope_lvl].push_back(cls); } else { // clause do not need to be in the reinit stack anymore, // because it will be deleted when the base level is // backtracked. cls->release_atoms(m); cls->m_reinit = false; cls->m_reinternalize_atoms = false; } } v.reset(); } CASSERT("reinit_clauses", check_clauses(m_lemmas)); CASSERT("reinit_clauses", check_lit_occs()); TRACE("reinit_clauses_bug", display_watch_lists(tout);); } void context::reassert_units(unsigned units_to_reassert_lim) { unsigned i = units_to_reassert_lim; unsigned sz = m_units_to_reassert.size(); for (; i < sz; i++) { expr * unit = m_units_to_reassert.get(i); bool gate_ctx = true; internalize(unit, gate_ctx); bool_var v = get_bool_var(unit); bool sign = m_units_to_reassert_sign[i] != 0; literal l(v, sign); assign(l, b_justification::mk_axiom()); TRACE("reassert_units", tout << "reasserting #" << unit->get_id() << " " << sign << " @ " << m_scope_lvl << "\n";); } if (at_base_level()) { m_units_to_reassert.reset(); m_units_to_reassert_sign.reset(); } } /** \brief Backtrack 'num_scopes' scope levels. Return the number of boolean variables before reinitializing clauses. This value is useful because it can be used to detect which boolean variables were deleted. \warning This method will not invoke reset_cache_generation. */ unsigned context::pop_scope_core(unsigned num_scopes) { try { if (m.has_trace_stream() && !m_is_auxiliary) m.trace_stream() << "[pop] " << num_scopes << " " << m_scope_lvl << "\n"; TRACE("context", tout << "backtracking: " << num_scopes << " from " << m_scope_lvl << "\n";); TRACE("pop_scope_detail", display(tout);); SASSERT(num_scopes > 0); SASSERT(num_scopes <= m_scope_lvl); SASSERT(m_scopes.size() == m_scope_lvl); unsigned new_lvl = m_scope_lvl - num_scopes; cache_generation(new_lvl); m_qmanager->pop(num_scopes); m_case_split_queue->pop_scope(num_scopes); TRACE("pop_scope", tout << "backtracking: " << num_scopes << ", new_lvl: " << new_lvl << "\n";); scope & s = m_scopes[new_lvl]; TRACE("context", tout << "backtracking new_lvl: " << new_lvl << "\n";); unsigned units_to_reassert_lim = s.m_units_to_reassert_lim; if (new_lvl < m_base_lvl) { base_scope & bs = m_base_scopes[new_lvl]; del_clauses(m_lemmas, bs.m_lemmas_lim); m_simp_qhead = bs.m_simp_qhead_lim; if (!bs.m_inconsistent) { m_conflict = null_b_justification; m_not_l = null_literal; m_unsat_proof = nullptr; } m_base_scopes.shrink(new_lvl); } else { m_conflict = null_b_justification; m_not_l = null_literal; } del_clauses(m_aux_clauses, s.m_aux_clauses_lim); m_relevancy_propagator->pop(num_scopes); m_fingerprints.pop_scope(num_scopes); unassign_vars(s.m_assigned_literals_lim); undo_trail_stack(s.m_trail_stack_lim); for (theory* th : m_theory_set) { th->pop_scope_eh(num_scopes); } del_justifications(m_justifications, s.m_justifications_lim); m_asserted_formulas.pop_scope(num_scopes); m_eq_propagation_queue.reset(); m_th_eq_propagation_queue.reset(); m_th_diseq_propagation_queue.reset(); m_atom_propagation_queue.reset(); m_region.pop_scope(num_scopes); m_scopes.shrink(new_lvl); m_scope_lvl = new_lvl; if (new_lvl < m_base_lvl) { m_base_lvl = new_lvl; m_search_lvl = new_lvl; // Remark: not really necessary } unsigned num_bool_vars = get_num_bool_vars(); // any variable >= num_bool_vars was deleted during backtracking. reinit_clauses(num_scopes, num_bool_vars); reassert_units(units_to_reassert_lim); TRACE("pop_scope_detail", tout << "end of pop_scope: \n"; display(tout);); CASSERT("context", check_invariant()); return num_bool_vars; } catch (...) { // throwing inside pop is just not cool. UNREACHABLE(); throw; } } void context::pop_scope(unsigned num_scopes) { pop_scope_core(num_scopes); reset_cache_generation(); } void context::pop_to_base_lvl() { SASSERT(m_scope_lvl >= m_base_lvl); if (!at_base_level()) { unsigned num_lvls = m_scope_lvl - m_base_lvl; pop_scope(num_lvls); } SASSERT(m_scope_lvl == m_base_lvl); } void context::pop_to_search_lvl() { unsigned num_levels = m_scope_lvl - get_search_level(); if (num_levels > 0) { pop_scope(num_levels); } } /** \brief Simplify the given clause using the assignment. Return true if the clause was already satisfied, and false otherwise. \remark This method should only be invoked if we are at the base level. */ bool context::simplify_clause(clause& cls) { SASSERT(m_scope_lvl == m_base_lvl); unsigned s = cls.get_num_literals(); if (get_assignment(cls[0]) == l_true || get_assignment(cls[1]) == l_true) { // clause is already satisfied. return true; } literal_buffer simp_lits; unsigned i = 2; unsigned j = i; for(; i < s; i++) { literal l = cls[i]; switch(get_assignment(l)) { case l_false: if (m.proofs_enabled()) simp_lits.push_back(~l); if (lit_occs_enabled()) m_lit_occs[l.index()].erase(&cls); break; case l_undef: if (i != j) { cls.swap_lits(i, j); } j++; break; case l_true: return true; } } if (j < s) { m_clause_proof.shrink(cls, j); cls.set_num_literals(j); SASSERT(j >= 2); } if (m.proofs_enabled() && !simp_lits.empty()) { SASSERT(m_scope_lvl == m_base_lvl); justification * js = cls.get_justification(); justification * new_js = nullptr; if (js->in_region()) new_js = mk_justification(unit_resolution_justification(m_region, js, simp_lits.size(), simp_lits.c_ptr())); else new_js = alloc(unit_resolution_justification, js, simp_lits.size(), simp_lits.c_ptr()); cls.set_justification(new_js); } return false; } /** \brief Simplify the given vector of clauses starting at the given position. Return the number of deleted (already satisfied) clauses. */ unsigned context::simplify_clauses(clause_vector & clauses, unsigned starting_at) { unsigned num_del_clauses = 0; clause_vector::iterator it = clauses.begin(); clause_vector::iterator end = clauses.end(); it += starting_at; clause_vector::iterator it2 = it; for(; it != end; ++it) { clause * cls = *it; SASSERT(!cls->in_reinit_stack()); if (cls->deleted()) { TRACE("simplify_clauses_bug", display_clause(tout << "deleted\n", cls) << "\n";); del_clause(true, cls); num_del_clauses++; } else if (simplify_clause(*cls)) { TRACE("simplify_clauses_bug", display_clause_smt2(tout << "simplified\n", *cls) << "\n";); for (unsigned idx = 0; idx < 2; idx++) { literal l0 = (*cls)[idx]; b_justification l0_js = get_justification(l0.var()); if (l0_js != null_b_justification && l0_js.get_kind() == b_justification::CLAUSE && l0_js.get_clause() == cls) { // cls is the explanation of l0 // it is safe to replace with axiom, we are at the base level. SASSERT(m_scope_lvl == m_base_lvl); bool_var v0 = l0.var(); if (m.proofs_enabled()) { SASSERT(m_search_lvl == m_base_lvl); literal_buffer simp_lits; unsigned num_lits = cls->get_num_literals(); for(unsigned i = 0; i < num_lits; i++) { if (i != idx) { literal l = (*cls)[i]; SASSERT(l != l0); simp_lits.push_back(~l); } } justification * cls_js = cls->get_justification(); justification * js = nullptr; if (!cls_js || cls_js->in_region()) { // If cls_js is 0 or is allocated in a region, then // we can allocate the new justification in a region too. js = mk_justification(unit_resolution_justification(m_region, cls_js, simp_lits.size(), simp_lits.c_ptr())); } else { js = alloc(unit_resolution_justification, cls_js, simp_lits.size(), simp_lits.c_ptr()); // js took ownership of the justification object. cls->set_justification(nullptr); m_justifications.push_back(js); } set_justification(v0, m_bdata[v0], b_justification(js)); } else m_bdata[v0].set_axiom(); } } del_clause(true, cls); num_del_clauses++; } else { *it2 = *it; ++it2; m_simp_counter += cls->get_num_literals(); } } clauses.set_end(it2); CASSERT("simplify_clauses", check_invariant()); return num_del_clauses; } /** \brief Simplify the set of clauses if possible (solver is at base level). */ void context::simplify_clauses() { // Remark: when assumptions are used m_scope_lvl >= m_search_lvl > m_base_lvl. Therefore, no simplification is performed. if (m_scope_lvl > m_base_lvl) return; unsigned sz = m_assigned_literals.size(); SASSERT(m_simp_qhead <= sz); if (m_simp_qhead == sz || m_simp_counter > 0) { TRACE("simplify_clauses", tout << "m_simp_qhead: " << m_simp_qhead << " m_simp_counter: " << m_simp_counter << "\n";); return; } if (m_aux_clauses.empty() && m_lemmas.empty()) { TRACE("simplify_clauses", tout << "no clauses to simplify\n";); return; } TRACE("simplify_clauses_detail", tout << "before:\n"; display_clauses(tout, m_lemmas);); IF_VERBOSE(2, verbose_stream() << "(smt.simplifying-clause-set"; verbose_stream().flush();); SASSERT(check_clauses(m_lemmas)); SASSERT(check_clauses(m_aux_clauses)); // m_simp_counter is used to balance the cost of simplify_clause. // // After executing simplify_clauses, the counter will contain // an approximation of the cost of executing simplify_clauses again. // That is, the number of literals that will need to be visited. // // The value of the counter is decremented each time we visit // a variable during propagation. // m_simp_counter = 0; // the field m_simp_qhead is used to check whether there are // new assigned literals at the base level. m_simp_qhead = m_assigned_literals.size(); unsigned num_del_clauses = 0; SASSERT(m_scope_lvl == m_base_lvl); if (m_base_lvl == 0) { num_del_clauses += simplify_clauses(m_aux_clauses, 0); num_del_clauses += simplify_clauses(m_lemmas, 0); } else { scope & s = m_scopes[m_base_lvl - 1]; base_scope & bs = m_base_scopes[m_base_lvl - 1]; num_del_clauses += simplify_clauses(m_aux_clauses, s.m_aux_clauses_lim); num_del_clauses += simplify_clauses(m_lemmas, bs.m_lemmas_lim); } TRACE("simp_counter", tout << "simp_counter: " << m_simp_counter << " scope_lvl: " << m_scope_lvl << "\n";); IF_VERBOSE(2, verbose_stream() << " :num-deleted-clauses " << num_del_clauses << ")" << std::endl;); TRACE("simplify_clauses_detail", tout << "after:\n"; display_clauses(tout, m_lemmas);); SASSERT(check_clauses(m_lemmas) && check_clauses(m_aux_clauses)); } struct clause_lt { bool operator()(clause * cls1, clause * cls2) const { return cls1->get_activity() > cls2->get_activity(); } }; /** \brief Delete low activity lemmas */ inline void context::del_inactive_lemmas() { if (m_fparams.m_lemma_gc_strategy == LGC_NONE) return; else if (m_fparams.m_lemma_gc_half) del_inactive_lemmas1(); else del_inactive_lemmas2(); m_num_conflicts_since_lemma_gc = 0; if (m_fparams.m_lemma_gc_strategy == LGC_GEOMETRIC) m_lemma_gc_threshold = static_cast(m_lemma_gc_threshold * m_fparams.m_lemma_gc_factor); } /** \brief Delete (approx.) half of low activity lemmas */ void context::del_inactive_lemmas1() { unsigned sz = m_lemmas.size(); unsigned start_at = m_base_lvl == 0 ? 0 : m_base_scopes[m_base_lvl - 1].m_lemmas_lim; SASSERT(start_at <= sz); if (start_at + m_fparams.m_recent_lemmas_size >= sz) return; IF_VERBOSE(2, verbose_stream() << "(smt.delete-inactive-lemmas"; verbose_stream().flush();); SASSERT (m_fparams.m_recent_lemmas_size < sz); unsigned end_at = sz - m_fparams.m_recent_lemmas_size; SASSERT(start_at < end_at); std::stable_sort(m_lemmas.begin() + start_at, m_lemmas.begin() + end_at, clause_lt()); unsigned start_del_at = (start_at + end_at) / 2; unsigned i = start_del_at; unsigned j = i; unsigned num_del_cls = 0; TRACE("del_inactive_lemmas", tout << "sz: " << sz << ", start_at: " << start_at << ", end_at: " << end_at << ", start_del_at: " << start_del_at << "\n";); for (; i < end_at; i++) { clause * cls = m_lemmas[i]; if (can_delete(cls)) { TRACE("del_inactive_lemmas", tout << "deleting: "; display_clause(tout, cls); tout << ", activity: " << cls->get_activity() << "\n";); del_clause(true, cls); num_del_cls++; } else { m_lemmas[j] = cls; j++; } } // keep recent clauses for (; i < sz; i++) { clause * cls = m_lemmas[i]; if (cls->deleted() && can_delete(cls)) { del_clause(true, cls); num_del_cls++; } else { m_lemmas[j++] = cls; } } m_lemmas.shrink(j); if (m_fparams.m_clause_decay > 1) { // rescale activity for (i = start_at; i < j; i++) { clause * cls = m_lemmas[i]; cls->set_activity(cls->get_activity() / m_fparams.m_clause_decay); } } IF_VERBOSE(2, verbose_stream() << " :num-deleted-clauses " << num_del_cls << ")" << std::endl;); } /** \brief More sophisticated version of del_inactive_lemmas. Here the lemmas are divided in two groups (old and new) based on the value of m_new_old_ratio parameter. A clause is deleted/retained based on its activity and relevancy. Clauses with several unassigned literals are considered less relevant. The threshold used for activity and relevancy depends on which group the clauses is in. */ void context::del_inactive_lemmas2() { IF_VERBOSE(2, verbose_stream() << "(smt.delete-inactive-clauses "; verbose_stream().flush();); unsigned sz = m_lemmas.size(); unsigned start_at = m_base_lvl == 0 ? 0 : m_base_scopes[m_base_lvl - 1].m_lemmas_lim; SASSERT(start_at <= sz); unsigned real_sz = sz - start_at; // idx of the first learned clause considered "new" unsigned new_first_idx = start_at + (real_sz / m_fparams.m_new_old_ratio) * (m_fparams.m_new_old_ratio - 1); SASSERT(new_first_idx <= sz); unsigned i = start_at; unsigned j = i; unsigned num_del_cls = 0; for (; i < sz; i++) { clause * cls = m_lemmas[i]; if (can_delete(cls)) { if (cls->deleted()) { // clause is already marked for deletion del_clause(true, cls); num_del_cls++; continue; } // A clause is deleted if it has low activity and the number of unknowns is greater than a threshold. // The activity threshold depends on how old the clause is. unsigned act_threshold = m_fparams.m_old_clause_activity - (m_fparams.m_old_clause_activity - m_fparams.m_new_clause_activity) * ((i - start_at) / real_sz); if (cls->get_activity() < act_threshold) { unsigned rel_threshold = (i >= new_first_idx ? m_fparams.m_new_clause_relevancy : m_fparams.m_old_clause_relevancy); if (more_than_k_unassigned_literals(cls, rel_threshold)) { del_clause(true, cls); num_del_cls++; continue; } } } m_lemmas[j] = cls; j++; cls->set_activity(static_cast(cls->get_activity() / m_fparams.m_inv_clause_decay)); } SASSERT(j <= sz); m_lemmas.shrink(j); IF_VERBOSE(2, verbose_stream() << " :num-deleted-clauses " << num_del_cls << ")" << std::endl;); } /** \brief Return true if "cls" has more than (or equal to) k unassigned literals. */ bool context::more_than_k_unassigned_literals(clause * cls, unsigned k) { SASSERT(k > 0); for (literal l : *cls) { if (get_assignment(l) == l_undef) { k--; if (k == 0) { return true; } } } return false; } #ifdef Z3DEBUG /** \brief Return true if a symbol of the given theory was already internalized. */ bool context::already_internalized_theory(theory * th) const { return already_internalized_theory_core(th, m_b_internalized_stack) || already_internalized_theory_core(th, m_e_internalized_stack); } /** \brief Auxiliary method for #already_internalized_theory. */ bool context::already_internalized_theory_core(theory * th, expr_ref_vector const & s) const { expr_mark visited; family_id fid = th->get_id(); unsigned sz = s.size(); for (unsigned i = 0; i < sz; i++) { expr * n = s.get(i); if (uses_theory(n, fid, visited)) return true; } return false; } #endif void context::register_plugin(theory * th) { if (m_theories.get_plugin(th->get_family_id()) != nullptr) { dealloc(th); return; // context already has a theory for the given family id. } SASSERT(std::find(m_theory_set.begin(), m_theory_set.end(), th) == m_theory_set.end()); SASSERT(!already_internalized_theory(th)); th->init(this); m_theories.register_plugin(th); m_theory_set.push_back(th); { #ifdef Z3DEBUG // It is unsafe to invoke push_trail from the method push_scope_eh. flet l(m_trail_enabled, false); #endif for (unsigned i = 0; i < m_scope_lvl; ++i) th->push_scope_eh(); } } void context::push() { TRACE("unit_subsumption_bug", display(tout << "context::push()\n");); scoped_suspend_rlimit _suspend_cancel(m.limit()); pop_to_base_lvl(); setup_context(false); bool was_consistent = !inconsistent(); internalize_assertions(); // internalize assertions before invoking m_asserted_formulas.push_scope propagate(); if (was_consistent && inconsistent()) { // logical context became inconsistent during user PUSH VERIFY(!resolve_conflict()); // build the proof } push_scope(); m_base_scopes.push_back(base_scope()); base_scope & bs = m_base_scopes.back(); bs.m_lemmas_lim = m_lemmas.size(); bs.m_inconsistent = inconsistent(); bs.m_simp_qhead_lim = m_simp_qhead; m_base_lvl++; m_search_lvl++; // Not really necessary. But, it is useful to enforce the invariant m_search_lvl >= m_base_lvl SASSERT(m_base_lvl <= m_scope_lvl); } void context::pop(unsigned num_scopes) { SASSERT (num_scopes > 0); if (num_scopes > m_scope_lvl) return; pop_to_base_lvl(); pop_scope(num_scopes); } /** \brief Free memory allocated by logical context. */ void context::flush() { flet l1(m_flushing, true); TRACE("flush", tout << "m_scope_lvl: " << m_scope_lvl << "\n";); m_relevancy_propagator = nullptr; m_model_generator->reset(); for (theory* t : m_theory_set) t->flush_eh(); undo_trail_stack(0); m_qmanager = nullptr; del_clauses(m_aux_clauses, 0); del_clauses(m_lemmas, 0); del_justifications(m_justifications, 0); reset_tmp_clauses(); if (m_is_diseq_tmp) { m_is_diseq_tmp->del_eh(m, false); m.dec_ref(m_is_diseq_tmp->get_owner()); enode::del_dummy(m_is_diseq_tmp); m_is_diseq_tmp = nullptr; } std::for_each(m_almost_cg_tables.begin(), m_almost_cg_tables.end(), delete_proc()); } void context::assert_expr_core(expr * e, proof * pr) { if (get_cancel_flag()) return; SASSERT(is_well_sorted(m, e)); TRACE("begin_assert_expr", tout << this << " " << mk_pp(e, m) << "\n";); TRACE("begin_assert_expr_ll", tout << mk_ll_pp(e, m) << "\n";); pop_to_base_lvl(); if (pr == nullptr) m_asserted_formulas.assert_expr(e); else m_asserted_formulas.assert_expr(e, pr); TRACE("end_assert_expr_ll", ast_mark m; m_asserted_formulas.display_ll(tout, m);); } void context::assert_expr(expr * e) { assert_expr(e, nullptr); } void context::assert_expr(expr * e, proof * pr) { timeit tt(get_verbosity_level() >= 100, "smt.simplifying"); assert_expr_core(e, pr); } class case_split_insert_trail : public trail { literal l; public: case_split_insert_trail(literal l): l(l) { } void undo(context & ctx) override { ctx.undo_th_case_split(l); } }; void context::mk_th_case_split(unsigned num_lits, literal * lits) { TRACE("theory_case_split", display_literals_verbose(tout << "theory case split: ", num_lits, lits); tout << std::endl;); // If we don't use the theory case split heuristic, // for each pair of literals (l1, l2) we add the clause (~l1 OR ~l2) // to enforce the condition that at most one literal can be assigned 'true'. if (!m_fparams.m_theory_case_split) { for (unsigned i = 0; i < num_lits; ++i) { for (unsigned j = i+1; j < num_lits; ++j) { literal l1 = lits[i]; literal l2 = lits[j]; mk_clause(~l1, ~l2, (justification*) nullptr); } } } else { literal_vector new_case_split; for (unsigned i = 0; i < num_lits; ++i) { literal l = lits[i]; SASSERT(!m_all_th_case_split_literals.contains(l.index())); m_all_th_case_split_literals.insert(l.index()); push_trail(case_split_insert_trail(l)); new_case_split.push_back(l); } m_th_case_split_sets.push_back(new_case_split); push_trail(push_back_vector >(m_th_case_split_sets)); for (unsigned i = 0; i < num_lits; ++i) { literal l = lits[i]; if (!m_literal2casesplitsets.contains(l.index())) { m_literal2casesplitsets.insert(l.index(), vector()); } m_literal2casesplitsets[l.index()].push_back(new_case_split); } TRACE("theory_case_split", tout << "tracking case split literal set { "; for (unsigned i = 0; i < num_lits; ++i) { tout << lits[i].index() << " "; } tout << "}" << std::endl; ); } } void context::add_theory_aware_branching_info(bool_var v, double priority, lbool phase) { m_case_split_queue->add_theory_aware_branching_info(v, priority, phase); } void context::undo_th_case_split(literal l) { m_all_th_case_split_literals.remove(l.index()); if (m_literal2casesplitsets.contains(l.index())) { if (!m_literal2casesplitsets[l.index()].empty()) { m_literal2casesplitsets[l.index()].pop_back(); } } } bool context::propagate_th_case_split(unsigned qhead) { if (m_all_th_case_split_literals.empty()) return true; // iterate over all literals assigned since the last time this method was called, // not counting any literals that get assigned by this method // this relies on bcp() to give us its old m_qhead and therefore // bcp() should always be called before this method unsigned assigned_literal_end = m_assigned_literals.size(); for (; qhead < assigned_literal_end; ++qhead) { literal l = m_assigned_literals[qhead]; TRACE("theory_case_split", tout << "check literal " << l.index() << std::endl; display_literal_verbose(tout, l); tout << std::endl;); // check if this literal participates in any theory case split if (!m_all_th_case_split_literals.contains(l.index())) { continue; } TRACE("theory_case_split", tout << "assigned literal " << l.index() << " is a theory case split literal" << std::endl;); // now find the sets of literals which contain l vector const& case_split_sets = m_literal2casesplitsets[l.index()]; for (vector::const_iterator it = case_split_sets.begin(); it != case_split_sets.end(); ++it) { literal_vector case_split_set = *it; TRACE("theory_case_split", tout << "found case split set { "; for(literal_vector::iterator set_it = case_split_set.begin(); set_it != case_split_set.end(); ++set_it) { tout << set_it->index() << " "; } tout << "}" << std::endl;); for(literal_vector::iterator set_it = case_split_set.begin(); set_it != case_split_set.end(); ++set_it) { literal l2 = *set_it; if (l2 != l) { b_justification js(l); TRACE("theory_case_split", tout << "case split literal "; l2.display(tout, m, m_bool_var2expr.c_ptr());); assign(~l2, js); if (inconsistent()) { TRACE("theory_case_split", tout << "conflict detected!" << std::endl;); return false; } } } } } // if we get here without detecting a conflict, we're fine return true; } bool context::reduce_assertions() { if (!m_asserted_formulas.inconsistent()) { // SASSERT(at_base_level()); m_asserted_formulas.reduce(); } return m_asserted_formulas.inconsistent(); } static bool is_valid_assumption(ast_manager & m, expr * assumption) { expr* arg; if (!m.is_bool(assumption)) return false; if (is_uninterp_const(assumption)) return true; if (m.is_not(assumption, arg) && is_uninterp_const(arg)) return true; if (!is_app(assumption)) return false; if (to_app(assumption)->get_num_args() == 0) return true; if (m.is_not(assumption, arg) && is_app(arg) && to_app(arg)->get_num_args() == 0) return true; return false; } void context::internalize_proxies(expr_ref_vector const& asms, vector>& asm2proxy) { for (expr* e : asms) { if (is_valid_assumption(m, e)) { asm2proxy.push_back(std::make_pair(e, expr_ref(e, m))); } else { expr_ref proxy(m), fml(m); proxy = m.mk_fresh_const("proxy", m.mk_bool_sort()); fml = m.mk_implies(proxy, e); m_asserted_formulas.assert_expr(fml); asm2proxy.push_back(std::make_pair(e, proxy)); } } // The new assertions are of the form 'proxy => assumption' // so clause simplification is sound even as these are removed after pop_scope. internalize_assertions(); } void context::internalize_assertions() { if (get_cancel_flag()) return; TRACE("internalize_assertions", tout << "internalize_assertions()...\n";); timeit tt(get_verbosity_level() >= 100, "smt.preprocessing"); reduce_assertions(); if (!m_asserted_formulas.inconsistent()) { unsigned sz = m_asserted_formulas.get_num_formulas(); unsigned qhead = m_asserted_formulas.get_qhead(); while (qhead < sz) { if (get_cancel_flag()) { m_asserted_formulas.commit(qhead); return; } expr * f = m_asserted_formulas.get_formula(qhead); proof * pr = m_asserted_formulas.get_formula_proof(qhead); internalize_assertion(f, pr, 0); qhead++; } m_asserted_formulas.commit(); } if (m_asserted_formulas.inconsistent() && !inconsistent()) { proof * pr = m_asserted_formulas.get_inconsistency_proof(); if (pr == nullptr) { set_conflict(b_justification::mk_axiom()); } else { set_conflict(mk_justification(justification_proof_wrapper(*this, pr))); m_unsat_proof = pr; } } TRACE("internalize_assertions", tout << "after internalize_assertions()...\n"; tout << "inconsistent: " << inconsistent() << "\n";); TRACE("after_internalize_assertions", display(tout);); } /** \brief Assumptions must be uninterpreted boolean constants (aka propositional variables). */ bool context::validate_assumptions(expr_ref_vector const& asms) { for (expr* a : asms) { SASSERT(a); if (!is_valid_assumption(m, a)) { warning_msg("an assumption must be a propositional variable or the negation of one"); return false; } } return true; } void context::init_clause(expr_ref_vector const& _clause) { literal_vector lits; for (expr* lit : _clause) { internalize_formula(lit, true); mark_as_relevant(lit); lits.push_back(get_literal(lit)); } clause* clausep = nullptr; if (lits.size() >= 2) { justification* js = nullptr; if (m.proofs_enabled()) { proof * pr = mk_clause_def_axiom(lits.size(), lits.c_ptr(), nullptr); js = mk_justification(justification_proof_wrapper(*this, pr)); } clausep = clause::mk(m, lits.size(), lits.c_ptr(), CLS_AUX, js); } m_tmp_clauses.push_back(std::make_pair(clausep, lits)); } void context::reset_tmp_clauses() { for (auto& p : m_tmp_clauses) { if (p.first) del_clause(false, p.first); } m_tmp_clauses.reset(); } lbool context::decide_clause() { if (m_tmp_clauses.empty()) return l_true; for (auto & tmp_clause : m_tmp_clauses) { literal_vector& lits = tmp_clause.second; literal unassigned = null_literal; for (literal l : lits) { switch (get_assignment(l)) { case l_false: break; case l_true: goto next_clause; default: unassigned = l; } } if (unassigned != null_literal) { shuffle(lits.size(), lits.c_ptr(), m_random); push_scope(); assign(unassigned, b_justification::mk_axiom(), true); return l_undef; } if (lits.size() == 1) { set_conflict(b_justification(), ~lits[0]); } else { set_conflict(b_justification(tmp_clause.first), null_literal); } VERIFY(!resolve_conflict()); return l_false; next_clause: ; } return l_true; } void context::init_assumptions(expr_ref_vector const& asms) { reset_assumptions(); m_literal2assumption.reset(); m_unsat_core.reset(); if (!asms.empty()) { // We must give a chance to the theories to propagate before we create a new scope... propagate(); // Internal backtracking scopes (created with push_scope()) must only be created when we are // in a consistent context. if (inconsistent()) return; if (get_cancel_flag()) return; push_scope(); vector> asm2proxy; internalize_proxies(asms, asm2proxy); for (auto const& p: asm2proxy) { expr_ref curr_assumption = p.second; expr* orig_assumption = p.first; if (m.is_true(curr_assumption)) continue; SASSERT(is_valid_assumption(m, curr_assumption)); proof * pr = m.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); // internalize_assertion marked l as relevant. SASSERT(is_relevant(l)); TRACE("assumptions", tout << l << ":" << curr_assumption << " " << mk_pp(orig_assumption, m) << "\n";); if (m.proofs_enabled()) assign(l, mk_justification(justification_proof_wrapper(*this, pr))); else assign(l, b_justification::mk_axiom()); m_assumptions.push_back(l); get_bdata(l.var()).m_assumption = true; } } m_search_lvl = m_scope_lvl; SASSERT(asms.empty() || m_search_lvl > m_base_lvl); SASSERT(!asms.empty() || m_search_lvl == m_base_lvl); TRACE("after_internalization", display(tout);); } void context::reset_assumptions() { TRACE("unsat_core_bug", tout << "reset " << m_assumptions << "\n";); for (literal lit : m_assumptions) get_bdata(lit.var()).m_assumption = false; m_assumptions.reset(); } bool context::should_research(lbool r) { if (r != l_false || m_unsat_core.empty()) { return false; } for (theory* th : m_theory_set) { if (th->should_research(m_unsat_core)) { return true; } } return false; } lbool context::mk_unsat_core(lbool r) { if (r != l_false) return r; SASSERT(inconsistent()); if (!tracking_assumptions()) { SASSERT(m_assumptions.empty()); return l_false; } uint_set already_found_assumptions; literal_vector::const_iterator it = m_conflict_resolution->begin_unsat_core(); literal_vector::const_iterator end = m_conflict_resolution->end_unsat_core(); for (; it != end; ++it) { literal l = *it; TRACE("unsat_core_bug", tout << "answer literal: " << l << "\n";); SASSERT(get_bdata(l.var()).m_assumption); if (!m_literal2assumption.contains(l.index())) l.neg(); SASSERT(m_literal2assumption.contains(l.index())); if (!already_found_assumptions.contains(l.index())) { already_found_assumptions.insert(l.index()); expr* orig_assumption = m_literal2assumption[l.index()]; m_unsat_core.push_back(orig_assumption); TRACE("assumptions", tout << l << ": " << mk_pp(orig_assumption, m) << "\n";); } } reset_assumptions(); pop_to_base_lvl(); // undo the push_scope() performed by init_assumptions m_search_lvl = m_base_lvl; std::sort(m_unsat_core.c_ptr(), m_unsat_core.c_ptr() + m_unsat_core.size(), ast_lt_proc()); TRACE("unsat_core_bug", tout << "unsat core:\n" << m_unsat_core << "\n";); validate_unsat_core(); // theory validation of unsat core for (theory* th : m_theory_set) { lbool theory_result = th->validate_unsat_core(m_unsat_core); if (theory_result == l_undef) { return l_undef; } } return l_false; } /** \brief Make some checks before starting the search. Return true if succeeded. */ bool context::check_preamble(bool reset_cancel) { if (m.has_trace_stream() && !m_is_auxiliary) m.trace_stream() << "[begin-check] " << m_scope_lvl << "\n"; if (memory::above_high_watermark()) { m_last_search_failure = MEMOUT; return false; } reset_tmp_clauses(); m_unsat_core.reset(); m_stats.m_num_checks++; pop_to_base_lvl(); return true; } /** \brief Execute some finalization code after performing the search. */ lbool context::check_finalize(lbool r) { TRACE("after_search", display(tout << "result: " << r << "\n");); display_profile(verbose_stream()); if (r == l_true && get_cancel_flag()) { r = l_undef; } if (r == l_true && gparams::get_value("model_validate") == "true") { for (theory* t : m_theory_set) { t->validate_model(*m_model); } #if 0 for (literal lit : m_assigned_literals) { if (!is_relevant(lit)) continue; expr* v = m_bool_var2expr[lit.var()]; if (lit.sign() ? m_model->is_true(v) : m_model->is_false(v)) { IF_VERBOSE(10, verbose_stream() << "invalid assignment " << (lit.sign() ? "true" : "false") << " to #" << v->get_id() << " := " << mk_bounded_pp(v, m, 2) << "\n"); } } for (clause* cls : m_aux_clauses) { bool found = false; for (literal lit : *cls) { expr* v = m_bool_var2expr[lit.var()]; if (lit.sign() ? !m_model->is_true(v) : !m_model->is_false(v)) { found = true; break; } } if (!found) { IF_VERBOSE(10, display_clause_smt2(verbose_stream() << "not satisfied:\n", *cls) << "\n"); } } #endif } return r; } /** \brief Setup the logical context based on the current set of asserted formulas and execute the check command. \remark A logical context can only be configured at scope level 0, and before internalizing any formulas. */ lbool context::setup_and_check(bool reset_cancel) { if (!check_preamble(reset_cancel)) return l_undef; SASSERT(m_scope_lvl == 0); SASSERT(!m_setup.already_configured()); setup_context(m_fparams.m_auto_config); internalize_assertions(); expr_ref_vector theory_assumptions(m); add_theory_assumptions(theory_assumptions); if (!theory_assumptions.empty()) { TRACE("search", tout << "Adding theory assumptions to context" << std::endl;); return check(0, nullptr, reset_cancel); } else { TRACE("before_search", display(tout);); return check_finalize(search()); } } config_mode context::get_config_mode(bool use_static_features) const { if (!m_fparams.m_auto_config) return CFG_BASIC; if (use_static_features) return CFG_AUTO; return CFG_LOGIC; } void context::setup_context(bool use_static_features) { if (m_setup.already_configured() || inconsistent()) return; m_setup(get_config_mode(use_static_features)); setup_components(); } void context::setup_components() { m_asserted_formulas.setup(); m_random.set_seed(m_fparams.m_random_seed); m_dyn_ack_manager.setup(); m_conflict_resolution->setup(); if (!relevancy()) m_fparams.m_relevancy_lemma = false; // setup all the theories for (theory* th : m_theory_set) th->setup(); } void context::add_theory_assumptions(expr_ref_vector & theory_assumptions) { for (theory* th : m_theory_set) { th->add_theory_assumptions(theory_assumptions); } } lbool context::check(unsigned num_assumptions, expr * const * assumptions, bool reset_cancel) { if (!check_preamble(reset_cancel)) return l_undef; SASSERT(at_base_level()); setup_context(false); lbool r; do { pop_to_base_lvl(); expr_ref_vector asms(m, num_assumptions, assumptions); internalize_assertions(); add_theory_assumptions(asms); TRACE("unsat_core_bug", tout << asms << "\n";); init_assumptions(asms); TRACE("before_search", display(tout);); r = search(); r = mk_unsat_core(r); } while (should_research(r)); r = check_finalize(r); return r; } lbool context::check(expr_ref_vector const& cube, vector const& clauses) { if (!check_preamble(true)) return l_undef; TRACE("before_search", display(tout);); setup_context(false); lbool r; do { pop_to_base_lvl(); expr_ref_vector asms(cube); internalize_assertions(); add_theory_assumptions(asms); // introducing proxies: if (!validate_assumptions(asms)) return l_undef; for (auto const& clause : clauses) if (!validate_assumptions(clause)) return l_undef; init_assumptions(asms); for (auto const& clause : clauses) init_clause(clause); r = search(); r = mk_unsat_core(r); } while (should_research(r)); r = check_finalize(r); return r; } void context::init_search() { for (theory* th : m_theory_set) { th->init_search_eh(); } m_qmanager->init_search_eh(); m_incomplete_theories.reset(); m_num_conflicts = 0; m_num_conflicts_since_restart = 0; m_num_conflicts_since_lemma_gc = 0; m_num_restarts = 0; m_restart_threshold = m_fparams.m_restart_initial; m_restart_outer_threshold = m_fparams.m_restart_initial; m_agility = 0.0; m_luby_idx = 1; m_lemma_gc_threshold = m_fparams.m_lemma_gc_initial; m_last_search_failure = OK; m_unsat_proof = nullptr; m_unsat_core .reset(); m_dyn_ack_manager .init_search_eh(); m_final_check_idx = 0; m_phase_default = false; m_case_split_queue ->init_search_eh(); m_next_progress_sample = 0; TRACE("literal_occ", display_literal_num_occs(tout);); } void context::end_search() { m_case_split_queue ->end_search_eh(); } void context::inc_limits() { if (m_num_conflicts_since_restart >= m_restart_threshold) { switch (m_fparams.m_restart_strategy) { case RS_GEOMETRIC: m_restart_threshold = static_cast(m_restart_threshold * m_fparams.m_restart_factor); break; case RS_IN_OUT_GEOMETRIC: m_restart_threshold = static_cast(m_restart_threshold * m_fparams.m_restart_factor); if (m_restart_threshold > m_restart_outer_threshold) { m_restart_threshold = m_fparams.m_restart_initial; m_restart_outer_threshold = static_cast(m_restart_outer_threshold * m_fparams.m_restart_factor); } break; case RS_LUBY: m_luby_idx ++; m_restart_threshold = static_cast(get_luby(m_luby_idx) * m_fparams.m_restart_initial); break; case RS_FIXED: break; case RS_ARITHMETIC: m_restart_threshold = static_cast(m_restart_threshold + m_fparams.m_restart_factor); break; default: break; } } m_num_conflicts_since_restart = 0; } lbool context::search() { if (m_asserted_formulas.inconsistent()) return l_false; if (inconsistent()) { VERIFY(!resolve_conflict()); return l_false; } timeit tt(get_verbosity_level() >= 100, "smt.stats"); scoped_mk_model smk(*this); SASSERT(at_search_level()); TRACE("search", display(tout); display_enodes_lbls(tout);); TRACE("search_detail", m_asserted_formulas.display(tout);); init_search(); flet l(m_searching, true); TRACE("after_init_search", display(tout);); IF_VERBOSE(2, verbose_stream() << "(smt.searching)\n";); TRACE("search_lite", tout << "searching...\n";); lbool status = l_undef; unsigned curr_lvl = m_scope_lvl; while (true) { SASSERT(!inconsistent()); status = bounded_search(); TRACE("search_bug", tout << "status: " << status << ", inconsistent: " << inconsistent() << "\n";); TRACE("assigned_literals_per_lvl", display_num_assigned_literals_per_lvl(tout); tout << ", num_assigned: " << m_assigned_literals.size() << "\n";); if (!restart(status, curr_lvl)) { break; } } TRACE("guessed_literals", expr_ref_vector guessed_lits(m); get_guessed_literals(guessed_lits); tout << guessed_lits << "\n";); end_search(); return status; } bool context::restart(lbool& status, unsigned curr_lvl) { if (m_last_search_failure != OK) { if (status != l_false) { // build candidate model before returning mk_proto_model(status); // status = l_undef; } return false; } if (status == l_false) { return false; } if (status == l_true) { SASSERT(!inconsistent()); mk_proto_model(l_true); // possible outcomes DONE l_true, DONE l_undef, CONTINUE quantifier_manager::check_model_result cmr = m_qmanager->check_model(m_proto_model.get(), m_model_generator->get_root2value()); if (cmr == quantifier_manager::SAT) { // done status = l_true; return false; } if (cmr == quantifier_manager::UNKNOWN) { IF_VERBOSE(2, verbose_stream() << "(smt.giveup quantifiers)\n";); // giving up m_last_search_failure = QUANTIFIERS; status = l_undef; return false; } } inc_limits(); if (status == l_true || !m_fparams.m_restart_adaptive || m_agility < m_fparams.m_restart_agility_threshold) { SASSERT(!inconsistent()); IF_VERBOSE(2, verbose_stream() << "(smt.restarting :propagations " << m_stats.m_num_propagations << " :decisions " << m_stats.m_num_decisions << " :conflicts " << m_stats.m_num_conflicts << " :restart " << m_restart_threshold; if (m_fparams.m_restart_strategy == RS_IN_OUT_GEOMETRIC) { verbose_stream() << " :restart-outer " << m_restart_outer_threshold; } if (m_fparams.m_restart_adaptive) { verbose_stream() << " :agility " << m_agility; } verbose_stream() << ")\n"); // execute the restart m_stats.m_num_restarts++; m_num_restarts++; if (m_scope_lvl > curr_lvl) { pop_scope(m_scope_lvl - curr_lvl); SASSERT(at_search_level()); } for (theory* th : m_theory_set) { if (!inconsistent()) th->restart_eh(); } TRACE("mbqi_bug_detail", tout << "before instantiating quantifiers...\n";); if (!inconsistent()) { m_qmanager->restart_eh(); } if (inconsistent()) { VERIFY(!resolve_conflict()); status = l_false; return false; } if (m_num_restarts >= m_fparams.m_restart_max) { status = l_undef; m_last_search_failure = NUM_CONFLICTS; return false; } } if (m_fparams.m_simplify_clauses) simplify_clauses(); if (m_fparams.m_lemma_gc_strategy == LGC_AT_RESTART) del_inactive_lemmas(); status = l_undef; return true; } void context::tick(unsigned & counter) const { counter++; if (counter > m_fparams.m_tick) { IF_VERBOSE(3, verbose_stream() << "(smt.working"; verbose_stream() << " :conflicts " << m_num_conflicts; // verbose_stream() << " lemma avg. activity: " << get_lemma_avg_activity(); if (m_fparams.m_restart_adaptive) verbose_stream() << " :agility " << m_agility; verbose_stream() << ")" << std::endl; verbose_stream().flush();); TRACE("assigned_literals_per_lvl", display_num_assigned_literals_per_lvl(tout); tout << "\n";); counter = 0; } } lbool context::bounded_search() { unsigned counter = 0; TRACE("bounded_search", tout << "starting bounded search...\n";); while (true) { while (!propagate()) { TRACE_CODE({ static bool first_propagate = true; if (first_propagate) { first_propagate = false; TRACE("after_first_propagate", display(tout);); } }); tick(counter); if (!resolve_conflict()) return l_false; SASSERT(m_scope_lvl >= m_base_lvl); if (!inconsistent()) { if (resource_limits_exceeded()) return l_undef; if (get_cancel_flag()) return l_undef; if (m_num_conflicts_since_restart > m_restart_threshold && m_scope_lvl - m_base_lvl > 2) { TRACE("search_bug", tout << "bounded-search return undef, inconsistent: " << inconsistent() << "\n";); return l_undef; // restart } if (m_num_conflicts > m_fparams.m_max_conflicts) { TRACE("search_bug", tout << "bounded-search return undef, inconsistent: " << inconsistent() << "\n";); m_last_search_failure = NUM_CONFLICTS; return l_undef; } } if (m_num_conflicts_since_lemma_gc > m_lemma_gc_threshold && (m_fparams.m_lemma_gc_strategy == LGC_FIXED || m_fparams.m_lemma_gc_strategy == LGC_GEOMETRIC)) { del_inactive_lemmas(); } m_dyn_ack_manager.propagate_eh(); CASSERT("dyn_ack", check_clauses(m_lemmas) && check_clauses(m_aux_clauses)); } if (resource_limits_exceeded() && !inconsistent()) { return l_undef; } if (get_cancel_flag()) return l_undef; if (m_base_lvl == m_scope_lvl && m_fparams.m_simplify_clauses) simplify_clauses(); if (!decide()) { if (inconsistent()) return l_false; final_check_status fcs = final_check(); TRACE("final_check_result", tout << "fcs: " << fcs << " last_search_failure: " << m_last_search_failure << "\n";); switch (fcs) { case FC_DONE: return l_true; case FC_CONTINUE: break; case FC_GIVEUP: return l_undef; } } if (resource_limits_exceeded() && !inconsistent()) { return l_undef; } } } bool context::resource_limits_exceeded() { if (m_searching) { // Some of the flags only make sense to check when searching. // For example, the timer is only started in init_search(). if (m_last_search_failure != OK) return true; if (get_cancel_flag()) { m_last_search_failure = CANCELED; return true; } if (m_progress_callback) { m_progress_callback->fast_progress_sample(); if (m_fparams.m_progress_sampling_freq > 0 && m_timer.ms_timeout(m_next_progress_sample + 1)) { m_progress_callback->slow_progress_sample(); m_next_progress_sample = (unsigned)(m_timer.get_seconds() * 1000) + m_fparams.m_progress_sampling_freq; } } } if (get_cancel_flag()) { m_last_search_failure = CANCELED; return true; } if (memory::above_high_watermark()) { m_last_search_failure = MEMOUT; return true; } return false; } final_check_status context::final_check() { TRACE("final_check", tout << "final_check inconsistent: " << inconsistent() << "\n"; display(tout); display_normalized_enodes(tout);); CASSERT("relevancy", check_relevancy()); if (m_fparams.m_model_on_final_check) { mk_proto_model(l_undef); model_pp(std::cout, *m_proto_model); std::cout << "END_OF_MODEL\n"; std::cout.flush(); } m_stats.m_num_final_checks++; TRACE("final_check_stats", tout << "m_stats.m_num_final_checks = " << m_stats.m_num_final_checks << "\n";); final_check_status ok = m_qmanager->final_check_eh(false); if (ok != FC_DONE) return ok; m_incomplete_theories.reset(); unsigned old_idx = m_final_check_idx; unsigned num_th = m_theory_set.size(); unsigned range = num_th + 1; final_check_status result = FC_DONE; failure f = OK; do { TRACE("final_check_step", tout << "processing: " << m_final_check_idx << ", result: " << result << "\n";); final_check_status ok; if (m_final_check_idx < num_th) { theory * th = m_theory_set[m_final_check_idx]; IF_VERBOSE(100, verbose_stream() << "(smt.final-check \"" << th->get_name() << "\")\n";); ok = th->final_check_eh(); TRACE("final_check_step", tout << "final check '" << th->get_name() << " ok: " << ok << " inconsistent " << inconsistent() << "\n";); if (ok == FC_GIVEUP) { f = THEORY; m_incomplete_theories.push_back(th); } } else { ok = m_qmanager->final_check_eh(true); TRACE("final_check_step", tout << "quantifier ok: " << ok << " " << "inconsistent " << inconsistent() << "\n";); } m_final_check_idx = (m_final_check_idx + 1) % range; // IF_VERBOSE(1000, verbose_stream() << "final check status: " << ok << "\n";); switch (ok) { case FC_DONE: break; case FC_GIVEUP: result = FC_GIVEUP; break; case FC_CONTINUE: return FC_CONTINUE; break; } } while (m_final_check_idx != old_idx); TRACE("final_check_step", tout << "result: " << result << "\n";); if (can_propagate()) { TRACE("final_check_step", tout << "can propagate: continue...\n";); return FC_CONTINUE; } SASSERT(result != FC_DONE || check_th_diseq_propagation()); TRACE("final_check_step", tout << "RESULT final_check: " << result << "\n";); if (result == FC_GIVEUP && f != OK) m_last_search_failure = f; return result; } void context::check_proof(proof * pr) { if (m.proofs_enabled() && m_fparams.m_check_proof) { proof_checker pf(m); expr_ref_vector side_conditions(m); pf.check(pr, side_conditions); } } void context::forget_phase_of_vars_in_current_level() { unsigned head = m_scope_lvl == 0 ? 0 : m_scopes[m_scope_lvl - 1].m_assigned_literals_lim; unsigned sz = m_assigned_literals.size(); for (unsigned i = head; i < sz; i++) { literal l = m_assigned_literals[i]; bool_var v = l.var(); TRACE("forget_phase", tout << "forgetting phase of l: " << l << "\n";); m_bdata[v].m_phase_available = false; } } bool context::resolve_conflict() { m_stats.m_num_conflicts++; m_num_conflicts ++; m_num_conflicts_since_restart ++; m_num_conflicts_since_lemma_gc ++; switch (m_conflict.get_kind()) { case b_justification::CLAUSE: case b_justification::BIN_CLAUSE: m_stats.m_num_sat_conflicts++; break; default: break; } if (m_fparams.m_phase_selection == PS_THEORY || m_fparams.m_phase_selection == PS_CACHING_CONSERVATIVE || m_fparams.m_phase_selection == PS_CACHING_CONSERVATIVE2) forget_phase_of_vars_in_current_level(); m_atom_propagation_queue.reset(); m_eq_propagation_queue.reset(); m_th_eq_propagation_queue.reset(); m_th_diseq_propagation_queue.reset(); if (m_conflict_resolution->resolve(m_conflict, m_not_l)) { unsigned new_lvl = m_conflict_resolution->get_new_scope_lvl(); unsigned num_lits = m_conflict_resolution->get_lemma_num_literals(); literal * lits = m_conflict_resolution->get_lemma_literals(); SASSERT(num_lits > 0); unsigned conflict_lvl = get_assign_level(lits[0]); SASSERT(conflict_lvl <= m_scope_lvl); // When num_lits == 1, then the default behavior is to go // to base-level. If the problem has quantifiers, it may be // too expensive to do that, since all instances will need to // be recreated. If that is the case, I store the assertions in // a special vector and keep reasserting whenever I backtrack. // Moreover, I backtrack only one level. bool delay_forced_restart = m_fparams.m_delay_units && internalized_quantifiers() && num_lits == 1 && conflict_lvl > m_search_lvl + 1 && !m.proofs_enabled() && m_units_to_reassert.size() < m_fparams.m_delay_units_threshold; if (delay_forced_restart) { new_lvl = conflict_lvl - 1; } // Some of the literals/enodes of the conflict clause will be destroyed during // backtracking, and will need to be recreated. However, I want to keep // the generation number for enodes that are going to be recreated. See // comment in cache_generation(unsigned). if (m_conflict_resolution->get_lemma_intern_lvl() > new_lvl) cache_generation(num_lits, lits, new_lvl); SASSERT(new_lvl < m_scope_lvl); TRACE("resolve_conflict_bug", tout << "m_scope_lvl: " << m_scope_lvl << ", new_lvl: " << new_lvl << ", lemma_intern_lvl: " << m_conflict_resolution->get_lemma_intern_lvl() << "\n"; tout << "num_lits: " << num_lits << "\n"; for (unsigned i = 0; i < num_lits; i++) { literal l = lits[i]; tout << l << " "; display_literal(tout, l); tout << ", ilvl: " << get_intern_level(l.var()) << "\n" << mk_pp(bool_var2expr(l.var()), m) << "\n"; }); if (m.has_trace_stream() && !m_is_auxiliary) { m.trace_stream() << "[conflict] "; display_literals(m.trace_stream(), num_lits, lits); m.trace_stream() << "\n"; } #ifdef Z3DEBUG expr_ref_vector expr_lits(m); svector expr_signs; for (unsigned i = 0; i < num_lits; i++) { literal l = lits[i]; if (get_assignment(l) != l_false) { std::cout << l << " " << get_assignment(l) << "\n"; } SASSERT(get_assignment(l) == l_false); expr_lits.push_back(bool_var2expr(l.var())); expr_signs.push_back(l.sign()); } #endif proof * pr = nullptr; if (m.proofs_enabled()) { pr = m_conflict_resolution->get_lemma_proof(); // check_proof(pr); TRACE("context_proof", tout << mk_ll_pp(pr, m);); TRACE("context_proof_hack", static ast_mark visited; ast_ll_pp(tout, m, pr, visited);); } // I invoke pop_scope_core instead of pop_scope because I don't want // to reset cached generations... I need them to rebuild the literals // of the new conflict clause. if (relevancy()) record_relevancy(num_lits, lits); unsigned num_bool_vars = pop_scope_core(m_scope_lvl - new_lvl); SASSERT(m_scope_lvl == new_lvl); // the logical context may still be in conflict after // clauses are reinitialized in pop_scope. if (m_conflict_resolution->get_lemma_intern_lvl() > m_scope_lvl) { expr * * atoms = m_conflict_resolution->get_lemma_atoms(); for (unsigned i = 0; i < num_lits; i++) { literal l = lits[i]; if (l.var() >= static_cast(num_bool_vars)) { // This boolean variable was deleted during backtracking, it need to be recreated. // Remark: atom may be a negative literal (not a). Z3 creates Boolean variables for not-gates that // are nested in terms. Example: let f be a uninterpreted function from Bool -> Int. // Then, given the term (f (not a)), Z3 will create a boolean variable for (not a) when internalizing (f (not a)). expr * atom = atoms[i]; internalize(atom, true); // If atom is actually a negative literal (not a), then get_bool_var will return return null_bool_var. // Thus, we must use get_literal instead. This was a bug/crash in Z3 <= 4.0 literal new_l = get_literal(atom); if (l.sign()) new_l.neg(); // For reference, here is the buggy version // BEGIN BUGGY VERSION // bool_var v = get_bool_var(atom); // CTRACE("resolve_conflict_crash", v == null_bool_var, tout << mk_ismt2_pp(atom, m) << "\n";); // SASSERT(v != null_bool_var); // literal new_l = literal(v, l.sign()); // END BUGGY VERSION lits[i] = new_l; } } } if (relevancy()) restore_relevancy(num_lits, lits); // Resetting the cache manually because I did not invoke pop_scope, but pop_scope_core reset_cache_generation(); TRACE("resolve_conflict_bug", tout << "AFTER m_scope_lvl: " << m_scope_lvl << ", new_lvl: " << new_lvl << ", lemma_intern_lvl: " << m_conflict_resolution->get_lemma_intern_lvl() << "\n"; tout << "num_lits: " << num_lits << "\n"; for (unsigned i = 0; i < num_lits; i++) { literal l = lits[i]; tout << l << " "; display_literal(tout, l); tout << ", ilvl: " << get_intern_level(l.var()) << "\n" << mk_pp(bool_var2expr(l.var()), m) << "\n"; }); #ifdef Z3DEBUG for (unsigned i = 0; i < num_lits; i++) { literal l = lits[i]; if (expr_signs[i] != l.sign()) { expr* real_atom; VERIFY(m.is_not(expr_lits.get(i), real_atom)); // the sign must have flipped when internalizing CTRACE("resolve_conflict_bug", real_atom != bool_var2expr(l.var()), tout << mk_pp(real_atom, m) << "\n" << mk_pp(bool_var2expr(l.var()), m) << "\n";); SASSERT(real_atom == bool_var2expr(l.var())); } else { SASSERT(expr_lits.get(i) == bool_var2expr(l.var())); } } #endif justification * js = nullptr; if (m.proofs_enabled()) { js = alloc(justification_proof_wrapper, *this, pr, false); } #if 0 { static unsigned counter = 0; static uint64_t total = 0; static unsigned max = 0; counter++; total += num_lits; if (num_lits > max) { max = num_lits; } if (counter % 1000 == 0) { verbose_stream() << "[sat] avg. clause size: " << ((double) total/(double) counter) << ", max: " << max << std::endl; for (unsigned i = 0; i < num_lits; i++) { literal l = lits[i]; verbose_stream() << l.sign() << " " << mk_pp(bool_var2expr(l.var()), m) << "\n"; } } } #endif mk_clause(num_lits, lits, js, CLS_LEARNED); if (delay_forced_restart) { SASSERT(num_lits == 1); expr * unit = bool_var2expr(lits[0].var()); bool unit_sign = lits[0].sign(); m_units_to_reassert.push_back(unit); m_units_to_reassert_sign.push_back(unit_sign); TRACE("reassert_units", tout << "asserting #" << unit->get_id() << " " << unit_sign << " @ " << m_scope_lvl << "\n";); } m_conflict_resolution->release_lemma_atoms(); TRACE("context_lemma", tout << "new lemma: "; literal_vector v(num_lits, lits); std::sort(v.begin(), v.end()); for (unsigned i = 0; i < num_lits; i++) { display_literal(tout, v[i]); tout << "\n"; v[i].display(tout, m, m_bool_var2expr.c_ptr()); tout << "\n\n"; } tout << "\n";); decay_bvar_activity(); update_phase_cache_counter(); return true; } else if (m_fparams.m_clause_proof) { m_unsat_proof = m_clause_proof.get_proof(); } else if (m.proofs_enabled()) { m_unsat_proof = m_conflict_resolution->get_lemma_proof(); check_proof(m_unsat_proof); } return false; } /* \brief we record and restore relevancy information for literals in conflict clauses. A literal may have been marked relevant within the scope that gets popped during conflict resolution. In this case, the literal is no longer marked as relevant after the pop. This can cause quantifier instantiation to miss relevant triggers and thereby cause incompleteness. */ void context::record_relevancy(unsigned n, literal const* lits) { m_relevant_conflict_literals.reset(); for (unsigned i = 0; i < n; ++i) { m_relevant_conflict_literals.push_back(is_relevant(lits[i])); } } void context::restore_relevancy(unsigned n, literal const* lits) { for (unsigned i = 0; i < n; ++i) { if (m_relevant_conflict_literals[i] && !is_relevant(lits[i])) { mark_as_relevant(lits[i]); } } } void context::get_relevant_labels(expr* cnstr, buffer & result) { if (m_fparams.m_check_at_labels) { check_at_labels checker(m); if (cnstr && !checker.check(cnstr)) { warning_msg("Boogie generated formula that can require multiple '@' labels in a counter-example"); } else { unsigned nf = m_asserted_formulas.get_num_formulas(); for (unsigned i = 0; i < nf; ++i) { expr* fml = m_asserted_formulas.get_formula(i); if (!checker.check(fml)) { warning_msg("Boogie generated formula that can require multiple '@' labels in a counter-example"); break; } } } } SASSERT(!inconsistent()); for (expr * curr : m_b_internalized_stack) { if (is_relevant(curr) && get_assignment(curr) == l_true) { // if curr is a label literal, then its tags will be copied to result. m.is_label_lit(curr, result); } } } /** \brief Collect relevant literals that may be used to block the current assignment. If at_lbls is true, then only labels that contains '@' are considered. (This is a hack for Boogie). This hack is also available in the Simplify theorem prover. */ void context::get_relevant_labeled_literals(bool at_lbls, expr_ref_vector & result) { SASSERT(!inconsistent()); buffer lbls; for (expr * curr : m_b_internalized_stack) { if (is_relevant(curr) && get_assignment(curr) == l_true) { lbls.reset(); if (m.is_label_lit(curr, lbls)) { bool include = false; if (at_lbls) { // include if there is a label with the '@' sign. for (symbol const& s : lbls) { if (s.contains('@')) { include = true; break; } } } else { include = true; } if (include) result.push_back(curr); } } } } /** \brief Store in result the (relevant) literal assigned by the logical context. */ void context::get_relevant_literals(expr_ref_vector & result) { SASSERT(!inconsistent()); unsigned sz = m_b_internalized_stack.size(); for (unsigned i = 0; i < sz; i++) { expr * curr = m_b_internalized_stack.get(i); if (is_relevant(curr)) { switch (get_assignment(curr)) { case l_true: result.push_back(curr); break; case l_false: result.push_back(m.mk_not(curr)); break; default: break; } } } } /** \brief Store the current set of guessed literals (i.e., case splits). */ void context::get_guessed_literals(expr_ref_vector & result) { // The literals between [m_base_lvl, m_search_lvl) are not guesses but assumptions. SASSERT(m_base_lvl <= m_scopes.size()); if (m_search_lvl == m_scopes.size()) { // do nothing... there are guesses... } for (unsigned i = m_search_lvl; i < m_scope_lvl; i++) { // This method assumes the first literal assigned in a non base scope level is a guess. scope & s = m_scopes[i]; unsigned guess_idx = s.m_assigned_literals_lim; literal guess = m_assigned_literals[guess_idx]; SASSERT(get_justification(guess.var()).get_kind() == b_justification::AXIOM); expr_ref lit(m); literal2expr(guess, lit); result.push_back(std::move(lit)); } } /** \brief Undo object for bool var m_true_first field update. */ class set_true_first_trail : public trail { bool_var m_var; public: set_true_first_trail(bool_var v):m_var(v) {} void undo(context & ctx) override { ctx.m_bdata[m_var].reset_true_first_flag(); } }; void context::set_true_first_flag(bool_var v) { push_trail(set_true_first_trail(v)); bool_var_data & d = m_bdata[v]; d.set_true_first_flag(); } bool context::assume_eq(enode * lhs, enode * rhs) { if (lhs->get_root() == rhs->get_root()) return false; // it is not necessary to assume the eq. expr * _lhs = lhs->get_owner(); expr * _rhs = rhs->get_owner(); expr * eq = mk_eq_atom(_lhs, _rhs); TRACE("assume_eq", tout << "creating interface eq:\n" << mk_pp(eq, m) << "\n";); if (m.is_false(eq)) { return false; } bool r = false; if (!b_internalized(eq)) { // I do not invoke internalize(eq, true), because I want to // mark the try_true_first flag before invoking theory::internalize_eq_eh. // Reason: Theories like arithmetic should be able to know if the try_true_first flag is // marked or not. They use this information to also mark auxiliary atoms such as: // (<= (- x y) 0) // (>= (- y x) 0) // for the new equality atom (= x y). if (m.is_eq(eq)) { internalize_formula_core(to_app(eq), true); bool_var v = get_bool_var(eq); bool_var_data & d = get_bdata(v); d.set_eq_flag(); set_true_first_flag(v); sort * s = m.get_sort(to_app(eq)->get_arg(0)); theory * th = m_theories.get_plugin(s->get_family_id()); if (th) th->internalize_eq_eh(to_app(eq), v); } else { internalize(eq, true); } r = true; m_stats.m_num_interface_eqs++; TRACE("assume_eq", tout << "new internalization.\n";); } bool_var v = get_bool_var(eq); bool_var_data & d = m_bdata[v]; if (!d.try_true_first()) { set_true_first_flag(v); r = true; TRACE("assume_eq", tout << "marked as ieq.\n";); } if (get_assignment(v) == l_undef) { TRACE("assume_eq", tout << "variable is unassigned.\n";); r = true; } if (relevancy() && !is_relevant(eq)) { TRACE("assume_eq", tout << "marking eq as relevant.\n";); mark_as_relevant(eq); r = true; } TRACE("assume_eq", tout << "variable value: " << get_assignment(v) << "\n";); TRACE("assume_eq", tout << "assume_eq result: " << r << "\n";); return r; } bool context::is_shared(enode * n) const { n = n->get_root(); unsigned num_th_vars = n->get_num_th_vars(); if (m.is_ite(n->get_owner())) { return true; } switch (num_th_vars) { case 0: { return false; } case 1: { if (m_qmanager->is_shared(n)) { return true; } // the variable is shared if the equivalence class of n // contains a parent application. theory_var_list * l = n->get_th_var_list(); theory_id th_id = l->get_th_id(); for (enode * parent : enode::parents(n)) { family_id fid = parent->get_owner()->get_family_id(); if (fid != th_id && fid != m.get_basic_family_id()) { TRACE("is_shared", tout << enode_pp(n, *this) << "\nis shared because of:\n" << enode_pp(parent, *this) << "\n";); return true; } } // Some theories implement families of theories. Examples: // Arrays and Tuples. For example, array theory is a // parametric theory, that is, it implements several theories: // (array int int), (array int (array int int)), ... // // Example: // // a : (array int int) // b : (array int int) // x : int // y : int // v : int // w : int // A : (array (array int int) int) // // assert (= b (store a x v)) // assert (= b (store a y w)) // assert (not (= x y)) // assert (not (select A a)) // assert (not (select A b)) // check // // In the example above, 'a' and 'b' are shared variables between // the theories of (array int int) and (array (array int int) int). // Remark: The inconsistency is not going to be detected if they are // not marked as shared. return get_theory(th_id)->is_shared(l->get_th_var()); } default: return true; } } bool context::get_value(enode * n, expr_ref & value) { sort * s = m.get_sort(n->get_owner()); family_id fid = s->get_family_id(); theory * th = get_theory(fid); if (th == nullptr) return false; return th->get_value(n, value); } bool context::update_model(bool refinalize) { final_check_status fcs = FC_DONE; if (refinalize) { fcs = final_check(); } TRACE("opt", tout << (refinalize?"refinalize":"no-op") << " " << fcs << "\n";); if (fcs == FC_DONE) { mk_proto_model(l_true); m_model = m_proto_model->mk_model(); add_rec_funs_to_model(); } return fcs == FC_DONE; } void context::mk_proto_model(lbool r) { TRACE("get_model", display(tout); display_normalized_enodes(tout); display_enodes_lbls(tout); m_fingerprints.display(tout); ); failure fl = get_last_search_failure(); if (fl == MEMOUT || fl == CANCELED || fl == NUM_CONFLICTS || fl == RESOURCE_LIMIT) { TRACE("get_model", tout << "last search failure: " << fl << "\n";); } else if (m_fparams.m_model || m_fparams.m_model_on_final_check || m_qmanager->model_based()) { m_model_generator->reset(); m_proto_model = m_model_generator->mk_model(); m_qmanager->adjust_model(m_proto_model.get()); TRACE("mbqi_bug", tout << "before complete_partial_funcs:\n"; model_pp(tout, *m_proto_model);); m_proto_model->complete_partial_funcs(false); TRACE("mbqi_bug", tout << "before cleanup:\n"; model_pp(tout, *m_proto_model);); m_proto_model->cleanup(); TRACE("mbqi_bug", tout << "after cleanup:\n"; model_pp(tout, *m_proto_model);); IF_VERBOSE(11, model_pp(verbose_stream(), *m_proto_model);); } } proof * context::get_proof() { if (!m_unsat_proof) { m_unsat_proof = m_clause_proof.get_proof(); } TRACE("context", tout << m_unsat_proof << "\n";); return m_unsat_proof; } void context::get_model(model_ref & m) const { if (inconsistent()) m = nullptr; else 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()); } failure context::get_last_search_failure() const { return m_last_search_failure; } void context::add_rec_funs_to_model() { if (!m_model) return; for (unsigned i = 0; !get_cancel_flag() && i < m_asserted_formulas.get_num_formulas(); ++i) { expr* e = m_asserted_formulas.get_formula(i); if (is_quantifier(e)) { quantifier* q = to_quantifier(e); if (!m.is_rec_fun_def(q)) continue; TRACE("context", tout << mk_pp(e, m) << "\n";); SASSERT(q->get_num_patterns() == 2); expr* fn = to_app(q->get_pattern(0))->get_arg(0); expr* body = to_app(q->get_pattern(1))->get_arg(0); SASSERT(is_app(fn)); // reverse argument order so that variable 0 starts at the beginning. expr_ref_vector subst(m); unsigned idx = 0; for (expr* arg : *to_app(fn)) { subst.push_back(m.mk_var(idx++, m.get_sort(arg))); } expr_ref bodyr(m); var_subst sub(m, true); TRACE("context", tout << expr_ref(q, m) << " " << subst << "\n";); bodyr = sub(body, subst.size(), subst.c_ptr()); func_decl* f = to_app(fn)->get_decl(); func_interp* fi = alloc(func_interp, m, f->get_arity()); fi->set_else(bodyr); m_model->register_decl(f, fi); } } recfun::util u(m); func_decl_ref_vector recfuns = u.get_rec_funs(); for (func_decl* f : recfuns) { auto& def = u.get_def(f); expr* rhs = def.get_rhs(); if (!rhs) continue; if (f->get_arity() == 0) { m_model->register_decl(f, rhs); continue; } func_interp* fi = alloc(func_interp, m, f->get_arity()); // reverse argument order so that variable 0 starts at the beginning. expr_ref_vector subst(m); for (unsigned i = 0; i < f->get_arity(); ++i) { subst.push_back(m.mk_var(i, f->get_domain(i))); } var_subst sub(m, true); expr_ref bodyr = sub(rhs, subst.size(), subst.c_ptr()); fi->set_else(bodyr); m_model->register_decl(f, fi); } TRACE("model", tout << *m_model << "\n";); } }; #ifdef Z3DEBUG void pp(smt::context & c) { c.display(std::cout); } #endif z3-z3-4.8.7/src/smt/smt_context.h000066400000000000000000001616041356505360400165510ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_context.h Abstract: Logical context Author: Leonardo de Moura (leonardo) 2008-02-18. Revision History: --*/ #ifndef SMT_CONTEXT_H_ #define SMT_CONTEXT_H_ #include "smt/smt_clause.h" #include "smt/smt_setup.h" #include "smt/smt_enode.h" #include "smt/smt_cg_table.h" #include "smt/smt_b_justification.h" #include "smt/smt_eq_justification.h" #include "smt/smt_justification.h" #include "smt/smt_bool_var_data.h" #include "smt/smt_clause_proof.h" #include "smt/smt_theory.h" #include "smt/smt_quantifier.h" #include "smt/smt_quantifier_stat.h" #include "smt/smt_statistics.h" #include "smt/smt_conflict_resolution.h" #include "smt/smt_relevancy.h" #include "smt/smt_case_split_queue.h" #include "smt/smt_almost_cg_table.h" #include "smt/smt_failure.h" #include "smt/asserted_formulas.h" #include "smt/smt_types.h" #include "smt/dyn_ack.h" #include "ast/ast_smt_pp.h" #include "smt/watch_list.h" #include "util/trail.h" #include "smt/fingerprints.h" #include "util/ref.h" #include "smt/proto_model/proto_model.h" #include "model/model.h" #include "util/timer.h" #include "util/statistics.h" #include "solver/progress_callback.h" #include // there is a significant space overhead with allocating 1000+ contexts in // the case that each context only references a few expressions. // Using a map instead of a vector for the literals can compress space // consumption. #ifdef SPARSE_MAP #define USE_BOOL_VAR_VECTOR 0 #else #define USE_BOOL_VAR_VECTOR 1 #endif namespace smt { class model_generator; class context { friend class model_generator; friend class lookahead; public: statistics m_stats; std::ostream& display_last_failure(std::ostream& out) const; std::string last_failure_as_string() const; void set_reason_unknown(char const* msg) { m_unknown = msg; } void set_progress_callback(progress_callback *callback); protected: ast_manager & m; smt_params & m_fparams; params_ref m_params; setup m_setup; timer m_timer; asserted_formulas m_asserted_formulas; th_rewriter m_rewriter; scoped_ptr m_qmanager; scoped_ptr m_model_generator; scoped_ptr m_relevancy_propagator; random_gen m_random; bool m_flushing; // (debug support) true when flushing mutable unsigned m_lemma_id; progress_callback * m_progress_callback; unsigned m_next_progress_sample; clause_proof m_clause_proof; region m_region; fingerprint_set m_fingerprints; expr_ref_vector m_b_internalized_stack; // stack of the boolean expressions already internalized. // Remark: boolean expressions can also be internalized as // enodes. Examples: boolean expression nested in an // uninterpreted function. expr_ref_vector m_e_internalized_stack; // stack of the expressions already internalized as enodes. ptr_vector m_justifications; unsigned m_final_check_idx; // circular counter used for implementing fairness bool m_is_auxiliary; // used to prevent unwanted information from being logged. // ----------------------------------- // // Equality & Uninterpreted functions // // ----------------------------------- enode * m_true_enode; enode * m_false_enode; app2enode_t m_app2enode; // app -> enode ptr_vector m_enodes; plugin_manager m_theories; // mapping from theory_id -> theory ptr_vector m_theory_set; // set of theories for fast traversal vector m_decl2enodes; // decl -> enode (for decls with arity > 0) enode_vector m_empty_vector; cg_table m_cg_table; struct new_eq { enode * m_lhs; enode * m_rhs; eq_justification m_justification; new_eq() {} new_eq(enode * lhs, enode * rhs, eq_justification const & js): m_lhs(lhs), m_rhs(rhs), m_justification(js) {} }; svector m_eq_propagation_queue; struct new_th_eq { theory_id m_th_id; theory_var m_lhs; theory_var m_rhs; new_th_eq():m_th_id(null_theory_id), m_lhs(null_theory_var), m_rhs(null_theory_var) {} new_th_eq(theory_id id, theory_var l, theory_var r):m_th_id(id), m_lhs(l), m_rhs(r) {} }; svector m_th_eq_propagation_queue; svector m_th_diseq_propagation_queue; #ifdef Z3DEBUG svector m_propagated_th_eqs; svector m_propagated_th_diseqs; svector m_diseq_vector; #endif enode * m_is_diseq_tmp; // auxiliary enode used to find congruent equality atoms. tmp_enode m_tmp_enode; ptr_vector m_almost_cg_tables; // temporary field for is_ext_diseq // ----------------------------------- // // Boolean engine // // ----------------------------------- #if USE_BOOL_VAR_VECTOR svector m_expr2bool_var; // expr id -> bool_var #else u_map m_expr2bool_var; #endif ptr_vector m_bool_var2expr; // bool_var -> expr signed_char_vector m_assignment; //!< mapping literal id -> assignment lbool vector m_watches; //!< per literal vector m_lit_occs; //!< index for backward subsumption svector m_bdata; //!< mapping bool_var -> data svector m_activity; clause_vector m_aux_clauses; clause_vector m_lemmas; vector m_clauses_to_reinit; expr_ref_vector m_units_to_reassert; svector m_units_to_reassert_sign; literal_vector m_assigned_literals; typedef std::pair tmp_clause; vector m_tmp_clauses; unsigned m_qhead; unsigned m_simp_qhead; int m_simp_counter; //!< can become negative scoped_ptr m_case_split_queue; double m_bvar_inc; bool m_phase_cache_on; unsigned m_phase_counter; //!< auxiliary variable used to decide when to turn on/off phase caching bool m_phase_default; //!< default phase when using phase caching // A conflict is usually a single justification. That is, a justification // for false. If m_not_l is not null_literal, then m_conflict is a // justification for l, and the conflict is union of m_no_l and m_conflict; b_justification m_conflict; literal m_not_l; scoped_ptr m_conflict_resolution; proof_ref m_unsat_proof; literal_vector m_atom_propagation_queue; obj_map m_cached_generation; obj_hashtable m_cache_generation_visited; dyn_ack_manager m_dyn_ack_manager; // ----------------------------------- // // Model generation // // ----------------------------------- proto_model_ref m_proto_model; model_ref m_model; std::string m_unknown; void mk_proto_model(lbool r); struct scoped_mk_model { context & m_ctx; scoped_mk_model(context & ctx):m_ctx(ctx) { m_ctx.m_proto_model = nullptr; m_ctx.m_model = nullptr; } ~scoped_mk_model() { if (m_ctx.m_proto_model.get() != nullptr) { m_ctx.m_model = m_ctx.m_proto_model->mk_model(); try { m_ctx.add_rec_funs_to_model(); } catch (...) { // no op } m_ctx.m_proto_model = nullptr; // proto_model is not needed anymore. } } }; // ----------------------------------- // // Unsat core extraction // // ----------------------------------- typedef u_map literal2assumption; literal_vector m_assumptions; literal2assumption m_literal2assumption; // maps an expression associated with a literal to the original assumption expr_ref_vector m_unsat_core; // ----------------------------------- // // Theory case split // // ----------------------------------- uint_set m_all_th_case_split_literals; vector m_th_case_split_sets; u_map< vector > m_literal2casesplitsets; // returns the case split literal sets that a literal participates in // ----------------------------------- // // Accessors // // ----------------------------------- public: ast_manager & get_manager() const { return m; } th_rewriter & get_rewriter() { return m_rewriter; } smt_params & get_fparams() { return m_fparams; } params_ref const & get_params() { return m_params; } void updt_params(params_ref const& p); bool get_cancel_flag(); region & get_region() { return m_region; } bool relevancy() const { return m_fparams.m_relevancy_lvl > 0; } enode * get_enode(expr const * n) const { SASSERT(e_internalized(n)); return m_app2enode[n->get_id()]; } /** \brief Similar to get_enode, but returns 0 if n is to e_internalized. */ enode * find_enode(expr const * n) const { return m_app2enode.get(n->get_id(), 0); } void reset_bool_vars() { m_expr2bool_var.reset(); } bool_var get_bool_var(expr const * n) const { return m_expr2bool_var[n->get_id()]; } bool_var get_bool_var(enode const * n) const { return get_bool_var(n->get_owner()); } bool_var get_bool_var_of_id(unsigned id) const { return m_expr2bool_var[id]; } bool_var get_bool_var_of_id_option(unsigned id) const { return m_expr2bool_var.get(id, null_bool_var); } #if USE_BOOL_VAR_VECTOR void set_bool_var(unsigned id, bool_var v) { m_expr2bool_var.setx(id, v, null_bool_var); } #else void set_bool_var(unsigned id, bool_var v) { if (v == null_bool_var) { m_expr2bool_var.erase(id); } else { m_expr2bool_var.insert(id, v); } } #endif clause_vector const& get_lemmas() const { return m_lemmas; } literal get_literal(expr * n) const; bool has_enode(bool_var v) const { return m_bdata[v].is_enode(); } enode * bool_var2enode(bool_var v) const { SASSERT(m_bdata[v].is_enode()); return m_app2enode[m_bool_var2expr[v]->get_id()]; } bool_var enode2bool_var(enode const * n) const { SASSERT(n->is_bool()); SASSERT(n != m_false_enode); return get_bool_var_of_id(n->get_owner_id()); } literal enode2literal(enode const * n) const { SASSERT(n->is_bool()); return n == m_false_enode ? false_literal : literal(enode2bool_var(n)); } unsigned get_num_bool_vars() const { return m_b_internalized_stack.size(); } bool_var_data & get_bdata(bool_var v) { return m_bdata[v]; } bool_var_data const & get_bdata(bool_var v) const { return m_bdata[v]; } lbool get_lit_assignment(unsigned lit_idx) const { return static_cast(m_assignment[lit_idx]); } lbool get_assignment(literal l) const { return get_lit_assignment(l.index()); } lbool get_assignment(bool_var v) const { return get_assignment(literal(v)); } literal_vector const & assigned_literals() const { return m_assigned_literals; } lbool get_assignment(expr * n) const; // Similar to get_assignment, but returns l_undef if n is not internalized. lbool find_assignment(expr * n) const; lbool get_assignment(enode * n) const; void get_assignments(expr_ref_vector& assignments); b_justification get_justification(bool_var v) const { return get_bdata(v).justification(); } void set_justification(bool_var v, bool_var_data& d, b_justification const& j); bool has_th_justification(bool_var v, theory_id th_id) const { b_justification js = get_justification(v); return js.get_kind() == b_justification::JUSTIFICATION && js.get_justification()->get_from_theory() == th_id; } int get_random_value() { return m_random(); } bool is_searching() const { return m_searching; } svector const & get_activity_vector() const { return m_activity; } double get_activity(bool_var v) const { return m_activity[v]; } void set_activity(bool_var v, double 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; } bool is_assumption(literal l) const { return is_assumption(l.var()); } bool is_marked(bool_var v) const { return get_bdata(v).m_mark; } void set_mark(bool_var v) { SASSERT(!is_marked(v)); get_bdata(v).m_mark = true; } void unset_mark(bool_var v) { SASSERT(is_marked(v)); get_bdata(v).m_mark = false; } /** \brief Return the scope level when v was assigned. */ unsigned get_assign_level(bool_var v) const { return get_bdata(v).m_scope_lvl; } unsigned get_assign_level(literal l) const { return get_assign_level(l.var()); } /** \brief Return the scope level when v was internalized. */ unsigned get_intern_level(bool_var v) const { return get_bdata(v).get_intern_level(); } theory * get_theory(theory_id th_id) const { return m_theories.get_plugin(th_id); } ptr_vector const& theories() const { return m_theories.plugins(); } ptr_vector::const_iterator begin_theories() const { return m_theories.begin(); } ptr_vector::const_iterator end_theories() const { return m_theories.end(); } unsigned get_scope_level() const { return m_scope_lvl; } unsigned get_base_level() const { return m_base_lvl; } bool at_base_level() const { return m_scope_lvl == m_base_lvl; } unsigned get_search_level() const { return m_search_lvl; } bool at_search_level() const { return m_scope_lvl == m_search_lvl; } bool tracking_assumptions() const { return !m_assumptions.empty() && m_search_lvl > m_base_lvl; } expr * bool_var2expr(bool_var v) const { return m_bool_var2expr[v]; } void literal2expr(literal l, expr_ref & result) const { if (l == true_literal) result = m.mk_true(); else if (l == false_literal) result = m.mk_false(); else if (l.sign()) result = m.mk_not(bool_var2expr(l.var())); else result = bool_var2expr(l.var()); } expr_ref literal2expr(literal l) const { expr_ref result(m); literal2expr(l, result); return result; } bool is_true(enode const * n) const { return n == m_true_enode; } bool is_false(enode const * n) const { return n == m_false_enode; } unsigned get_num_enodes_of(func_decl const * decl) const { unsigned id = decl->get_decl_id(); return id < m_decl2enodes.size() ? m_decl2enodes[id].size() : 0; } enode_vector const& enodes_of(func_decl const * d) const { unsigned id = d->get_decl_id(); return id < m_decl2enodes.size() ? m_decl2enodes[id] : m_empty_vector; } enode_vector::const_iterator begin_enodes_of(func_decl const * decl) const { unsigned id = decl->get_decl_id(); return id < m_decl2enodes.size() ? m_decl2enodes[id].begin() : nullptr; } enode_vector::const_iterator end_enodes_of(func_decl const * decl) const { unsigned id = decl->get_decl_id(); return id < m_decl2enodes.size() ? m_decl2enodes[id].end() : nullptr; } ptr_vector const& enodes() const { return m_enodes; } ptr_vector::const_iterator begin_enodes() const { return m_enodes.begin(); } ptr_vector::const_iterator end_enodes() const { return m_enodes.end(); } unsigned get_generation(quantifier * q) const { return m_qmanager->get_generation(q); } /** \brief Return true if the logical context internalized universal quantifiers. */ bool internalized_quantifiers() const { return !m_qmanager->empty(); } /** \brief Return true if the logical context internalized or will internalize universal quantifiers. */ bool has_quantifiers() const { return m_asserted_formulas.has_quantifiers(); } fingerprint * add_fingerprint(void * data, unsigned data_hash, unsigned num_args, enode * const * args, expr* def = nullptr) { return m_fingerprints.insert(data, data_hash, num_args, args, def); } theory_id get_var_theory(bool_var v) const { return get_bdata(v).get_theory(); } friend class set_var_theory_trail; void set_var_theory(bool_var v, theory_id tid); // ----------------------------------- // // Backtracking support // // ----------------------------------- protected: typedef ptr_vector > trail_stack; trail_stack m_trail_stack; #ifdef Z3DEBUG bool m_trail_enabled; #endif public: template void push_trail(const TrailObject & obj) { SASSERT(m_trail_enabled); m_trail_stack.push_back(new (m_region) TrailObject(obj)); } void push_trail_ptr(trail * ptr) { m_trail_stack.push_back(ptr); } protected: unsigned m_scope_lvl; unsigned m_base_lvl; unsigned m_search_lvl; // It is greater than m_base_lvl when assumptions are used. Otherwise, it is equals to m_base_lvl struct scope { unsigned m_assigned_literals_lim; unsigned m_trail_stack_lim; unsigned m_aux_clauses_lim; unsigned m_justifications_lim; unsigned m_units_to_reassert_lim; }; struct base_scope { unsigned m_lemmas_lim; unsigned m_simp_qhead_lim; unsigned m_inconsistent; }; svector m_scopes; svector m_base_scopes; void push_scope(); unsigned pop_scope_core(unsigned num_scopes); void pop_scope(unsigned num_scopes); void undo_trail_stack(unsigned old_size); void unassign_vars(unsigned old_lim); void remove_watch_literal(clause * cls, unsigned idx); void remove_lit_occs(clause * cls); void remove_cls_occs(clause * cls); void del_clause(bool log, clause * cls); void del_clauses(clause_vector & v, unsigned old_size); void del_justifications(ptr_vector & justifications, unsigned old_lim); bool is_unit_clause(clause const * c) const; bool is_empty_clause(clause const * c) const; void cache_generation(unsigned new_scope_lvl); void cache_generation(clause const * cls, unsigned new_scope_lvl); void cache_generation(unsigned num_lits, literal const * lits, unsigned new_scope_lvl); void cache_generation(expr * n, unsigned new_scope_lvl); void reset_cache_generation(); void reinit_clauses(unsigned num_scopes, unsigned num_bool_vars); void reassert_units(unsigned units_to_reassert_lim); public: // \brief exposed for PB solver to participate in GC void remove_watch(bool_var v); // ----------------------------------- // // Internalization // // ----------------------------------- public: bool b_internalized(expr const * n) const { return get_bool_var_of_id_option(n->get_id()) != null_bool_var; } bool lit_internalized(expr const * n) const { return m.is_false(n) || (m.is_not(n) ? b_internalized(to_app(n)->get_arg(0)) : b_internalized(n)); } bool e_internalized(expr const * n) const { return m_app2enode.get(n->get_id(), 0) != 0; } unsigned get_num_b_internalized() const { return m_b_internalized_stack.size(); } expr * get_b_internalized(unsigned idx) const { return m_b_internalized_stack.get(idx); } unsigned get_num_e_internalized() const { return m_e_internalized_stack.size(); } expr * get_e_internalized(unsigned idx) const { return m_e_internalized_stack.get(idx); } /** \brief Return the position (in the assignment stack) of the decision literal at the given scope level. */ unsigned get_decision_literal_pos(unsigned scope_lvl) const { SASSERT(scope_lvl > m_base_lvl); return m_scopes[scope_lvl - 1].m_assigned_literals_lim; } protected: unsigned m_generation; //!< temporary variable used during internalization public: bool binary_clause_opt_enabled() const { return !m.proofs_enabled() && m_fparams.m_binary_clause_opt; } protected: bool_var_data & get_bdata(expr const * n) { return get_bdata(get_bool_var(n)); } bool_var_data const & get_bdata(expr const * n) const { return get_bdata(get_bool_var(n)); } typedef std::pair expr_bool_pair; void ts_visit_child(expr * n, bool gate_ctx, svector & tcolors, svector & fcolors, svector & todo, bool & visited); bool ts_visit_children(expr * n, bool gate_ctx, svector & tcolors, svector & fcolors, svector & todo); void top_sort_expr(expr * n, svector & sorted_exprs); void assert_default(expr * n, proof * pr); void assert_distinct(app * n, proof * pr); void internalize_formula(expr * n, bool gate_ctx); void internalize_eq(app * n, bool gate_ctx); void internalize_distinct(app * n, bool gate_ctx); bool internalize_theory_atom(app * n, bool gate_ctx); void internalize_quantifier(quantifier * q, bool gate_ctx); void internalize_lambda(quantifier * q); void internalize_formula_core(app * n, bool gate_ctx); void set_merge_tf(enode * n, bool_var v, bool is_new_var); friend class set_enode_flag_trail; public: void set_enode_flag(bool_var v, bool is_new_var); protected: void internalize_term(app * n); void internalize_ite_term(app * n); bool internalize_theory_term(app * n); void internalize_uninterpreted(app * n); friend class mk_bool_var_trail; class mk_bool_var_trail : public trail { public: void undo(context & ctx) override { ctx.undo_mk_bool_var(); } }; mk_bool_var_trail m_mk_bool_var_trail; void undo_mk_bool_var(); friend class mk_enode_trail; class mk_enode_trail : public trail { public: void undo(context & ctx) override { ctx.undo_mk_enode(); } }; mk_enode_trail m_mk_enode_trail; void undo_mk_enode(); void apply_sort_cnstr(app * term, enode * e); bool simplify_aux_clause_literals(unsigned & num_lits, literal * lits, literal_buffer & simp_lits); bool simplify_aux_lemma_literals(unsigned & num_lits, literal * lits); void mark_for_reinit(clause * cls, unsigned scope_lvl, bool reinternalize_atoms); unsigned get_max_iscope_lvl(unsigned num_lits, literal const * lits) const; bool use_binary_clause_opt(literal l1, literal l2, bool lemma) const; int select_learned_watch_lit(clause const * cls) const; int select_watch_lit(clause const * cls, int starting_at) const; void add_watch_literal(clause * cls, unsigned idx); proof * mk_clause_def_axiom(unsigned num_lits, literal * lits, expr * root_gate); public: void mk_gate_clause(unsigned num_lits, literal * lits); void mk_gate_clause(literal l1, literal l2); void mk_gate_clause(literal l1, literal l2, literal l3); void mk_gate_clause(literal l1, literal l2, literal l3, literal l4); protected: void mk_root_clause(unsigned num_lits, literal * lits, proof * pr); void mk_root_clause(literal l1, literal l2, proof * pr); void mk_root_clause(literal l1, literal l2, literal l3, proof * pr); void add_and_rel_watches(app * n); void add_or_rel_watches(app * n); void add_ite_rel_watches(app * n); void mk_not_cnstr(app * n); void mk_and_cnstr(app * n); void mk_or_cnstr(app * n); void mk_iff_cnstr(app * n); void mk_ite_cnstr(app * n); bool lit_occs_enabled() const { return m_fparams.m_phase_selection==PS_OCCURRENCE; } void add_lit_occs(clause * cls); public: void ensure_internalized(expr* e); void internalize(expr * n, bool gate_ctx); void internalize(expr * n, bool gate_ctx, unsigned generation); clause * mk_clause(unsigned num_lits, literal * lits, justification * j, clause_kind k = CLS_AUX, clause_del_eh * del_eh = nullptr); void mk_clause(literal l1, literal l2, justification * j); void mk_clause(literal l1, literal l2, literal l3, justification * j); void mk_th_axiom(theory_id tid, unsigned num_lits, literal * lits, unsigned num_params = 0, parameter * params = nullptr); void mk_th_axiom(theory_id tid, literal l1, literal l2, unsigned num_params = 0, parameter * params = nullptr); void mk_th_axiom(theory_id tid, literal l1, literal l2, literal l3, unsigned num_params = 0, parameter * params = nullptr); void mk_th_axiom(theory_id tid, literal_vector const& ls, unsigned num_params = 0, parameter * params = nullptr) { mk_th_axiom(tid, ls.size(), ls.c_ptr(), num_params, params); } /* * Provide a hint to the core solver that the specified literals form a "theory case split". * The core solver will enforce the condition that exactly one of these literals can be * assigned 'true' at any time. * We assume that the theory solver has already asserted the disjunction of these literals * or some other axiom that means at least one of them must be assigned 'true'. */ void mk_th_case_split(unsigned num_lits, literal * lits); /* * Provide a hint to the branching heuristic about the priority of a "theory-aware literal". * Literals marked in this way will always be branched on before unmarked literals, * starting with the literal having the highest priority. */ void add_theory_aware_branching_info(bool_var v, double priority, lbool phase); public: // helper function for trail void undo_th_case_split(literal l); bool propagate_th_case_split(unsigned qhead); bool_var mk_bool_var(expr * n); enode * mk_enode(app * n, bool suppress_args, bool merge_tf, bool cgc_enabled); void attach_th_var(enode * n, theory * th, theory_var v); template justification * mk_justification(Justification const & j) { justification * js = new (m_region) Justification(j); SASSERT(js->in_region()); if (js->has_del_eh()) m_justifications.push_back(js); return js; } // ----------------------------------- // // Engine // // ----------------------------------- protected: lbool m_last_search_result; failure m_last_search_failure; ptr_vector m_incomplete_theories; //!< theories that failed to produce a model bool m_searching; unsigned m_num_conflicts; unsigned m_num_conflicts_since_restart; unsigned m_num_conflicts_since_lemma_gc; unsigned m_num_restarts; unsigned m_num_simplifications; unsigned m_restart_threshold; unsigned m_restart_outer_threshold; unsigned m_luby_idx; double m_agility; unsigned m_lemma_gc_threshold; void assign_core(literal l, b_justification j, bool decision = false); void trace_assign(literal l, b_justification j, bool decision) const; public: void assign(literal l, const b_justification & j, bool decision = false) { SASSERT(l != false_literal); SASSERT(l != null_literal); switch (get_assignment(l)) { case l_false: set_conflict(j, ~l); break; case l_undef: assign_core(l, j, decision); break; case l_true: return; } } void assign(literal l, justification * j, bool decision = false) { assign(l, j ? b_justification(j) : b_justification::mk_axiom(), decision); } friend class set_true_first_trail; void set_true_first_flag(bool_var v); bool try_true_first(bool_var v) const { return get_bdata(v).try_true_first(); } bool assume_eq(enode * lhs, enode * rhs); bool is_shared(enode * n) const; void assign_eq(enode * lhs, enode * rhs, eq_justification const & js) { push_eq(lhs, rhs, js); } /** \brief Force the given phase next time we case split v. This method has no effect if phase caching is disabled. */ void force_phase(bool_var v, bool phase) { bool_var_data & d = get_bdata(v); d.m_phase_available = true; d.m_phase = phase; } void force_phase(literal l) { force_phase(l.var(), !l.sign()); } bool contains_instance(quantifier * q, unsigned num_bindings, enode * const * bindings); bool add_instance(quantifier * q, app * pat, unsigned num_bindings, enode * const * bindings, expr* def, unsigned max_generation, unsigned min_top_generation, unsigned max_top_generation, vector> & used_enodes /*gives the equalities used for the pattern match, see mam.cpp for more info*/); void set_global_generation(unsigned generation) { m_generation = generation; } #ifdef Z3DEBUG bool slow_contains_instance(quantifier const * q, unsigned num_bindings, enode * const * bindings) const { return m_fingerprints.slow_contains(q, q->get_id(), num_bindings, bindings); } #endif void add_eq(enode * n1, enode * n2, eq_justification js); protected: void push_new_th_eq(theory_id th, theory_var lhs, theory_var rhs); void push_new_th_diseq(theory_id th, theory_var lhs, theory_var rhs); friend class add_eq_trail; void remove_parents_from_cg_table(enode * r1); void reinsert_parents_into_cg_table(enode * r1, enode * r2, enode * n1, enode * n2, eq_justification js); void invert_trans(enode * n); theory_var get_closest_var(enode * n, theory_id th_id); void merge_theory_vars(enode * r2, enode * r1, eq_justification js); void propagate_bool_enode_assignment(enode * r1, enode * r2, enode * n1, enode * n2); void propagate_bool_enode_assignment_core(enode * source, enode * target); void undo_add_eq(enode * r1, enode * n1, unsigned r2_num_parents); void restore_theory_vars(enode * r2, enode * r1); void push_eq(enode * lhs, enode * rhs, eq_justification const & js) { if (lhs->get_root() != rhs->get_root()) { m_eq_propagation_queue.push_back(new_eq(lhs, rhs, js)); } } void push_new_congruence(enode * n1, enode * n2, bool used_commutativity) { SASSERT(n1->m_cg == n2); // if (is_relevant(n1)) mark_as_relevant(n2); push_eq(n1, n2, eq_justification::mk_cg(used_commutativity)); } bool add_diseq(enode * n1, enode * n2); void assign_quantifier(quantifier * q); void set_conflict(const b_justification & js, literal not_l); void set_conflict(const b_justification & js) { set_conflict(js, null_literal); } public: void set_conflict(justification * js) { SASSERT(js); set_conflict(b_justification(js)); } bool inconsistent() const { return m_conflict != null_b_justification; } unsigned get_num_conflicts() const { return m_num_conflicts; } static bool is_eq(enode const * n1, enode const * n2) { return n1->get_root() == n2->get_root(); } bool is_diseq(enode * n1, enode * n2) const; bool is_diseq_slow(enode * n1, enode * n2) const; bool is_ext_diseq(enode * n1, enode * n2, unsigned depth); enode * get_enode_eq_to(func_decl * f, unsigned num_args, enode * const * args); protected: bool decide(); void update_phase_cache_counter(); #define ACTIVITY_LIMIT 1e100 #define INV_ACTIVITY_LIMIT 1e-100 void rescale_bool_var_activity(); public: void inc_bvar_activity(bool_var v) { double & act = m_activity[v]; act += m_bvar_inc; if (act > ACTIVITY_LIMIT) rescale_bool_var_activity(); m_case_split_queue->activity_increased_eh(v); TRACE("case_split", tout << "v" << v << " " << m_bvar_inc << " -> " << act << "\n";); } protected: void decay_bvar_activity() { m_bvar_inc *= m_fparams.m_inv_decay; } bool simplify_clause(clause& cls); unsigned simplify_clauses(clause_vector & clauses, unsigned starting_at); void simplify_clauses(); /** \brief Return true if the give clause is justifying some literal. */ bool is_justifying(clause * cls) const { for (unsigned i = 0; i < 2; i++) { b_justification js; js = get_justification((*cls)[i].var()); if (js.get_kind() == b_justification::CLAUSE && js.get_clause() == cls) return true; } return false; } bool can_delete(clause * cls) const { if (cls->in_reinit_stack()) return false; return !is_justifying(cls); } void del_inactive_lemmas(); void del_inactive_lemmas1(); void del_inactive_lemmas2(); bool more_than_k_unassigned_literals(clause * cls, unsigned k); void internalize_assertions(); bool validate_assumptions(expr_ref_vector const& asms); void init_assumptions(expr_ref_vector const& asms); void init_clause(expr_ref_vector const& clause); lbool decide_clause(); void reset_tmp_clauses(); void reset_assumptions(); void add_theory_assumptions(expr_ref_vector & theory_assumptions); lbool mk_unsat_core(lbool result); bool should_research(lbool result); void validate_unsat_core(); void init_search(); void end_search(); lbool search(); void inc_limits(); bool restart(lbool& status, unsigned curr_lvl); void tick(unsigned & counter) const; lbool bounded_search(); final_check_status final_check(); void check_proof(proof * pr); void forget_phase_of_vars_in_current_level(); virtual bool resolve_conflict(); // ----------------------------------- // // Propagation // // ----------------------------------- protected: bool bcp(); bool propagate_eqs(); bool propagate_atoms(); void push_new_th_diseqs(enode * r, theory_var v, theory * th); void propagate_bool_var_enode(bool_var v); bool is_relevant_core(expr * n) const { return m_relevancy_propagator->is_relevant(n); } svector m_relevant_conflict_literals; void record_relevancy(unsigned n, literal const* lits); void restore_relevancy(unsigned n, literal const* lits); public: // event handler for relevancy_propagator class void relevant_eh(expr * n); bool is_relevant(expr * n) const { return !relevancy() || is_relevant_core(n); } bool is_relevant(enode * n) const { return is_relevant(n->get_owner()); } bool is_relevant(bool_var v) const { return is_relevant(bool_var2expr(v)); } bool is_relevant(literal l) const { SASSERT(l != true_literal && l != false_literal); return is_relevant(l.var()); } bool is_relevant_core(literal l) const { return is_relevant_core(bool_var2expr(l.var())); } void mark_as_relevant(expr * n) { m_relevancy_propagator->mark_as_relevant(n); m_relevancy_propagator->propagate(); } void mark_as_relevant(enode * n) { mark_as_relevant(n->get_owner()); } void mark_as_relevant(bool_var v) { mark_as_relevant(bool_var2expr(v)); } void mark_as_relevant(literal l) { mark_as_relevant(l.var()); } template relevancy_eh * mk_relevancy_eh(Eh const & eh) { return m_relevancy_propagator->mk_relevancy_eh(eh); } void add_relevancy_eh(expr * source, relevancy_eh * eh) { m_relevancy_propagator->add_handler(source, eh); } void add_relevancy_dependency(expr * source, expr * target) { m_relevancy_propagator->add_dependency(source, target); } void add_rel_watch(literal l, relevancy_eh * eh) { m_relevancy_propagator->add_watch(bool_var2expr(l.var()), !l.sign(), eh); } void add_rel_watch(literal l, expr * n) { m_relevancy_propagator->add_watch(bool_var2expr(l.var()), !l.sign(), n); } protected: lbool get_assignment_core(expr * n) const; void propagate_relevancy(unsigned qhead); bool propagate_theories(); void propagate_th_eqs(); void propagate_th_diseqs(); bool can_theories_propagate() const; bool propagate(); void add_rec_funs_to_model(); public: bool can_propagate() const; // Retrieve arithmetic values. bool get_arith_lo(expr* e, rational& lo, bool& strict); bool get_arith_up(expr* e, rational& up, bool& strict); bool get_arith_value(expr* e, rational& value); // ----------------------------------- // // Model checking... (must be improved) // // ----------------------------------- public: bool get_value(enode * n, expr_ref & value); // ----------------------------------- // // Pretty Printing // // ----------------------------------- protected: ast_mark m_pp_visited; ast_mark & get_pp_visited() const { return const_cast(m_pp_visited); } public: void display_enode_defs(std::ostream & out) const; void display_bool_var_defs(std::ostream & out) const; void display_asserted_formulas(std::ostream & out) const; std::ostream& display_literal(std::ostream & out, literal l) const; std::ostream& display_detailed_literal(std::ostream & out, literal l) const { l.display(out, m, m_bool_var2expr.c_ptr()); return out; } void display_literal_info(std::ostream & out, literal l) const; std::ostream& display_literals(std::ostream & out, unsigned num_lits, literal const * lits) const; std::ostream& display_literals(std::ostream & out, literal_vector const& lits) const { return display_literals(out, lits.size(), lits.c_ptr()); } std::ostream& display_literal_smt2(std::ostream& out, literal lit) const; std::ostream& display_literals_smt2(std::ostream& out, unsigned num_lits, literal const* lits) const; std::ostream& display_literal_verbose(std::ostream & out, literal lit) const; std::ostream& display_literals_verbose(std::ostream & out, unsigned num_lits, literal const * lits) const; std::ostream& display_literals_verbose(std::ostream & out, literal_vector const& lits) const { return display_literals_verbose(out, lits.size(), lits.c_ptr()); } void display_watch_list(std::ostream & out, literal l) const; void display_watch_lists(std::ostream & out) const; std::ostream& display_clause_detail(std::ostream & out, clause const * cls) const; std::ostream& display_clause(std::ostream & out, clause const * cls) const; std::ostream& display_clause_smt2(std::ostream & out, clause const& cls) const; std::ostream& display_clauses(std::ostream & out, ptr_vector const & v) const; std::ostream& display_binary_clauses(std::ostream & out) const; void display_assignment(std::ostream & out) const; void display_eqc(std::ostream & out) const; void display_app_enode_map(std::ostream & out) const; void display_expr_bool_var_map(std::ostream & out) const; void display_relevant_exprs(std::ostream & out) const; void display_theories(std::ostream & out) const; void display_eq_detail(std::ostream & out, enode * n) const; void display_parent_eqs(std::ostream & out, enode * n) const; void display_hot_bool_vars(std::ostream & out) const; void display_lemma_as_smt_problem(std::ostream & out, unsigned num_antecedents, literal const * antecedents, literal consequent = false_literal, symbol const& logic = symbol::null) const; unsigned display_lemma_as_smt_problem(unsigned num_antecedents, literal const * antecedents, literal consequent = false_literal, symbol const& logic = symbol::null) const; void display_lemma_as_smt_problem(std::ostream & out, unsigned num_antecedents, literal const * antecedents, unsigned num_antecedent_eqs, enode_pair const * antecedent_eqs, literal consequent = false_literal, symbol const& logic = symbol::null) const; unsigned display_lemma_as_smt_problem(unsigned num_antecedents, literal const * antecedents, unsigned num_antecedent_eqs, enode_pair const * antecedent_eqs, literal consequent = false_literal, symbol const& logic = symbol::null) const; void display_assignment_as_smtlib2(std::ostream& out, symbol const& logic = symbol::null) const; void display_normalized_enodes(std::ostream & out) const; void display_enodes_lbls(std::ostream & out) const; void display_decl2enodes(std::ostream & out) const; void display_subexprs_info(std::ostream & out, expr * n) const; void display_var_occs_histogram(std::ostream & out) const; void display_num_min_occs(std::ostream & out) const; void display_profile_res_sub(std::ostream & out) const; void display_profile(std::ostream & out) const; std::ostream& display(std::ostream& out, b_justification j) const; // ----------------------------------- // // Debugging support // // ----------------------------------- protected: #ifdef Z3DEBUG bool is_watching_clause(literal l, clause const * cls) const; bool check_clause(clause const * cls) const; bool check_clauses(clause_vector const & v) const; bool check_watch_list(literal l) const; bool check_watch_list(unsigned l_idx) const; bool check_bin_watch_lists() const; bool check_enode(enode * n) const; bool check_enodes() const; bool check_invariant() const; bool check_eqc_bool_assignment() const; bool check_missing_clause_propagation(clause_vector const & v) const; bool check_missing_bin_clause_propagation() const; bool check_missing_eq_propagation() const; bool check_missing_congruence() const; bool check_missing_bool_enode_propagation() const; bool check_missing_propagation() const; bool check_relevancy(expr_ref_vector const & v) const; bool check_relevancy() const; bool check_bool_var_vector_sizes() const; bool check_th_diseq_propagation() const; bool check_missing_diseq_conflict() const; bool check_lit_occs(literal l) const; bool check_lit_occs() const; #endif // ----------------------------------- // // Introspection // // ----------------------------------- unsigned get_lemma_avg_activity() const; void display_literal_num_occs(std::ostream & out) const; void display_num_assigned_literals_per_lvl(std::ostream & out) const; // ----------------------------------- // // Auxiliary // // ----------------------------------- void init(); void flush(); config_mode get_config_mode(bool use_static_features) const; virtual void setup_context(bool use_static_features); void setup_components(); void pop_to_base_lvl(); void pop_to_search_lvl(); #ifdef Z3DEBUG bool already_internalized_theory(theory * th) const; bool already_internalized_theory_core(theory * th, expr_ref_vector const & s) const; #endif bool check_preamble(bool reset_cancel); lbool check_finalize(lbool r); // ----------------------------------- // // API // // ----------------------------------- void assert_expr_core(expr * e, proof * pr); // copy plugins into a fresh context. void copy_plugins(context& src, context& dst); static literal translate_literal( literal lit, context& src_ctx, context& dst_ctx, vector b2v, ast_translation& tr); /* \brief Utilities for consequence finding. */ typedef hashtable index_set; //typedef uint_set index_set; u_map m_antecedents; obj_map m_var2orig; obj_map m_assumption2orig; obj_map m_var2val; void extract_fixed_consequences(literal lit, index_set const& assumptions, expr_ref_vector& conseq); void extract_fixed_consequences(unsigned& idx, index_set const& assumptions, expr_ref_vector& conseq); void display_consequence_progress(std::ostream& out, unsigned it, unsigned nv, unsigned fixed, unsigned unfixed, unsigned eq); unsigned delete_unfixed(expr_ref_vector& unfixed); unsigned extract_fixed_eqs(expr_ref_vector& conseq); expr_ref antecedent2fml(index_set const& ante); literal mk_diseq(expr* v, expr* val); void validate_consequences(expr_ref_vector const& assumptions, expr_ref_vector const& vars, expr_ref_vector const& conseq, expr_ref_vector const& unfixed); bool validate_justification(bool_var v, bool_var_data const& d, b_justification const& j); void justify(literal lit, index_set& s); void extract_cores(expr_ref_vector const& asms, vector& cores, unsigned& min_core_size); void preferred_sat(literal_vector& literals); void display_partial_assignment(std::ostream& out, expr_ref_vector const& asms, unsigned min_core_size); public: context(ast_manager & m, smt_params & fp, params_ref const & p = params_ref()); virtual ~context(); /** \brief Return a new context containing the same theories and simplifier plugins, but with an empty set of assertions. If l == 0, then the logic of this context is used in the new context. If p == 0, then this->m_params is used */ context * mk_fresh(symbol const * l = nullptr, smt_params * smtp = nullptr, params_ref const & p = params_ref()); static void copy(context& src, context& dst); /** \brief Translate context to use new manager m. */ app * mk_eq_atom(expr * lhs, expr * rhs); bool set_logic(symbol const& logic) { return m_setup.set_logic(logic); } void register_plugin(theory * th); void assert_expr(expr * e); void assert_expr(expr * e, proof * pr); void push(); void pop(unsigned num_scopes); lbool check(unsigned num_assumptions = 0, expr * const * assumptions = nullptr, bool reset_cancel = true); lbool check(expr_ref_vector const& cube, vector const& clauses); lbool get_consequences(expr_ref_vector const& assumptions, expr_ref_vector const& vars, expr_ref_vector& conseq, expr_ref_vector& unfixed); lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes); lbool preferred_sat(expr_ref_vector const& asms, vector& cores); lbool setup_and_check(bool reset_cancel = true); // return 'true' if assertions are inconsistent. bool reduce_assertions(); bool resource_limits_exceeded(); failure get_last_search_failure() const; proof * get_proof(); conflict_resolution& get_cr() { return *m_conflict_resolution.get(); } void get_relevant_labels(expr* cnstr, buffer & result); void get_relevant_labeled_literals(bool at_lbls, expr_ref_vector & result); void get_relevant_literals(expr_ref_vector & result); void get_guessed_literals(expr_ref_vector & result); void internalize_assertion(expr * n, proof * pr, unsigned generation); void internalize_proxies(expr_ref_vector const& asms, vector>& asm2proxy); void internalize_instance(expr * body, proof * pr, unsigned generation) { internalize_assertion(body, pr, generation); if (relevancy()) m_case_split_queue->internalize_instance_eh(body, generation); } bool already_internalized() const { return m_e_internalized_stack.size() > 2 || m_b_internalized_stack.size() > 1; } unsigned get_unsat_core_size() const { return m_unsat_core.size(); } expr * get_unsat_core_expr(unsigned idx) const { 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); void get_proto_model(proto_model_ref & m) const; bool validate_model(); unsigned get_num_asserted_formulas() const { return m_asserted_formulas.get_num_formulas(); } unsigned get_asserted_formulas_last_level() const { return m_asserted_formulas.get_formulas_last_level(); } expr * get_asserted_formula(unsigned idx) const { return m_asserted_formulas.get_formula(idx); } proof * get_asserted_formula_proof(unsigned idx) const { return m_asserted_formulas.get_formula_proof(idx); } void get_asserted_formulas(ptr_vector& r) const { m_asserted_formulas.get_assertions(r); } //proof * const * get_asserted_formula_proofs() const { return m_asserted_formulas.get_formula_proofs(); } void get_assertions(ptr_vector & result) { m_asserted_formulas.get_assertions(result); } void display(std::ostream & out) const; void display_unsat_core(std::ostream & out) const; void collect_statistics(::statistics & st) const; void display_statistics(std::ostream & out) const; void display_istatistics(std::ostream & out) const; // ----------------------------------- // // Macros // // ----------------------------------- public: unsigned get_num_macros() const { return m_asserted_formulas.get_num_macros(); } unsigned get_first_macro_last_level() const { return m_asserted_formulas.get_first_macro_last_level(); } func_decl * get_macro_func_decl(unsigned i) const { return m_asserted_formulas.get_macro_func_decl(i); } func_decl * get_macro_interpretation(unsigned i, expr_ref & interp) const { return m_asserted_formulas.get_macro_interpretation(i, interp); } quantifier * get_macro_quantifier(func_decl * f) const { return m_asserted_formulas.get_macro_quantifier(f); } void insert_macro(func_decl * f, quantifier * m, proof * pr, expr_dependency * dep) { m_asserted_formulas.insert_macro(f, m, pr, dep); } }; struct pp_lit { context & ctx; literal lit; pp_lit(context & ctx, literal lit) : ctx(ctx), lit(lit) {} }; inline std::ostream & operator<<(std::ostream & out, pp_lit const & pp) { return pp.ctx.display_detailed_literal(out, pp.lit); } struct pp_lits { context & ctx; literal const *lits; unsigned len; pp_lits(context & ctx, unsigned len, literal const *lits) : ctx(ctx), lits(lits), len(len) {} pp_lits(context & ctx, literal_vector const& ls) : ctx(ctx), lits(ls.c_ptr()), len(ls.size()) {} }; inline std::ostream & operator<<(std::ostream & out, pp_lits const & pp) { out << "{"; bool first = true; for (unsigned i = 0; i < pp.len; ++i) { if (first) { first = false; } else { out << " or\n"; } pp.ctx.display_detailed_literal(out, pp.lits[i]); } return out << "}"; } struct enode_eq_pp { context const& ctx; enode_pair const& p; enode_eq_pp(enode_pair const& p, context const& ctx): ctx(ctx), p(p) {} }; std::ostream& operator<<(std::ostream& out, enode_eq_pp const& p); struct enode_pp { context const& ctx; enode* n; enode_pp(enode* n, context const& ctx): ctx(ctx), n(n) {} }; std::ostream& operator<<(std::ostream& out, enode_pp const& p); }; #endif /* SMT_CONTEXT_H_ */ z3-z3-4.8.7/src/smt/smt_context_inv.cpp000066400000000000000000000434001356505360400177510ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_context_inv.cpp Abstract: SMT logical contexts: invariant Author: Leonardo de Moura (leonardo) 2008-02-21. Revision History: --*/ #include "smt/smt_context.h" #include "ast/ast_pp.h" #include "ast/ast_ll_pp.h" #include "ast/ast_smt2_pp.h" namespace smt { #ifdef Z3DEBUG bool context::is_watching_clause(literal l, clause const * cls) const { watch_list & wl = const_cast(m_watches[l.index()]); return wl.find_clause(cls) != wl.end_clause(); } bool context::check_clause(clause const * cls) const { SASSERT(is_watching_clause(~cls->get_literal(0), cls)); SASSERT(is_watching_clause(~cls->get_literal(1), cls)); if (lit_occs_enabled()) { for (literal l : *cls) { SASSERT(m_lit_occs[l.index()].contains(const_cast(cls))); } } return true; } bool context::check_clauses(clause_vector const & v) const { for (clause* cls : v) if (!cls->deleted()) check_clause(cls); return true; } bool context::check_watch_list(literal l) const { watch_list & wl = const_cast(m_watches[l.index()]); l.neg(); watch_list::clause_iterator it = wl.begin_clause(); watch_list::clause_iterator end = wl.end_clause(); for (; it != end; ++it) { clause * cls = *it; TRACE("watch_list", tout << "l: "; display_literal(tout, l); tout << "\n"; display_clause(tout, cls); tout << "\n";); SASSERT(l == cls->get_literal(0) || l == cls->get_literal(1)); } return true; } bool context::check_watch_list(unsigned l_idx) const { return check_watch_list(to_literal(l_idx)); } bool context::check_bin_watch_lists() const { if (binary_clause_opt_enabled()) { vector::const_iterator it = m_watches.begin(); vector::const_iterator end = m_watches.end(); for (unsigned l_idx = 0; it != end; ++it, ++l_idx) { literal l1 = to_literal(l_idx); watch_list const & wl = *it; literal const * it2 = wl.begin_literals(); literal const * end2 = wl.end_literals(); for (; it2 != end2; ++it2) { literal l2 = *it2; watch_list const & wl = m_watches[(~l2).index()]; SASSERT(wl.find_literal(~l1) != wl.end_literals()); } } } return true; } bool context::check_lit_occs(literal l) const { clause_set const & v = m_lit_occs[l.index()]; for (clause * cls : v) { unsigned num = cls->get_num_literals(); unsigned i = 0; for (; i < num; i++) if (cls->get_literal(i) == l) break; CTRACE("lit_occs", !(i < num), tout << i << " " << num << "\n"; display_literal(tout, l); tout << "\n"; display_clause(tout, cls); tout << "\n"; tout << "l: " << l.index() << " cls: "; for (unsigned j = 0; j < num; j++) { tout << cls->get_literal(j).index() << " "; } tout << "\n"; display_clause_detail(tout, cls); tout << "\n";); SASSERT(i < num); } return true; } bool context::check_lit_occs() const { if (lit_occs_enabled()) { unsigned num_lits = get_num_bool_vars() * 2; for (unsigned l_idx = 0; l_idx < num_lits; ++l_idx) { check_lit_occs(to_literal(l_idx)); } } return true; } bool context::check_enode(enode * n) const { SASSERT(n->check_invariant()); bool is_true_eq = n->is_true_eq(); bool cg_inv = n->get_num_args() == 0 || (!is_true_eq && (!n->is_cgc_enabled() || n->is_cgr() == (m_cg_table.contains_ptr(n)))) || (is_true_eq && !m_cg_table.contains_ptr(n)); CTRACE("check_enode", !cg_inv, tout << "n: #" << n->get_owner_id() << ", m_cg: #" << n->m_cg->get_owner_id() << ", contains: " << m_cg_table.contains(n) << "\n"; display(tout);); SASSERT(cg_inv); return true; } bool context::check_enodes() const { for (enode* n : m_enodes) { check_enode(n); } return true; } bool context::check_invariant() const { check_lit_occs(); check_bin_watch_lists(); check_clauses(m_aux_clauses); check_clauses(m_lemmas); check_enodes(); SASSERT(m_cg_table.check_invariant()); return true; } bool context::check_missing_clause_propagation(clause_vector const & v) const { for (clause * cls : v) { CTRACE("missing_propagation", is_unit_clause(cls), display_clause_detail(tout, cls); tout << "\n";); SASSERT(!is_unit_clause(cls)); } return true; } bool context::check_missing_bin_clause_propagation() const { if (binary_clause_opt_enabled()) { SASSERT(m_watches.size() == m_assignment.size()); vector::const_iterator it = m_watches.begin(); vector::const_iterator end = m_watches.end(); for (unsigned l_idx = 0; it != end; ++it, ++l_idx) { literal l = to_literal(l_idx); watch_list const & wl = *it; if (get_assignment(l) == l_true) { literal const * it2 = wl.begin_literals(); literal const * end2 = wl.end_literals(); for (; it2 != end2; ++it2) { literal l2 = *it2; SASSERT(get_assignment(l2) == l_true); } } } } return true; } bool context::check_missing_eq_propagation() const { for (enode* n : m_enodes) { SASSERT(!n->is_true_eq() || get_assignment(n) == l_true); if (n->is_eq() && get_assignment(n) == l_true) { SASSERT(n->is_true_eq()); } } return true; } bool context::check_missing_congruence() const { for (enode* n : m_enodes) { for (enode* n2 : m_enodes) { if (n->get_root() != n2->get_root()) { if (n->is_true_eq() && n2->is_true_eq()) continue; CTRACE("missing_propagation", congruent(n, n2), tout << mk_pp(n->get_owner(), m) << "\n" << mk_pp(n2->get_owner(), m) << "\n"; display(tout);); SASSERT(!congruent(n, n2)); } } } return true; } bool context::check_missing_bool_enode_propagation() const { for (enode* n : m_enodes) { if (m.is_bool(n->get_owner()) && get_assignment(n) == l_undef) { enode * first = n; do { CTRACE("missing_propagation", get_assignment(n) != l_undef, tout << mk_pp(first->get_owner(), m) << "\nassignment: " << get_assignment(first) << "\n" << mk_pp(n->get_owner(), m) << "\nassignment: " << get_assignment(n) << "\n";); SASSERT(get_assignment(n) == l_undef); n = n->get_next(); } while (n != first); } } return true; } bool context::check_missing_propagation() const { check_missing_clause_propagation(m_lemmas); check_missing_clause_propagation(m_aux_clauses); check_missing_bin_clause_propagation(); // check_missing_eq_propagation(); check_missing_congruence(); check_missing_bool_enode_propagation(); return true; } bool context::check_relevancy(expr_ref_vector const & v) const { return m_relevancy_propagator->check_relevancy(v); } bool context::check_relevancy() const { if (!relevancy()) return true; check_relevancy(m_b_internalized_stack); check_relevancy(m_e_internalized_stack); unsigned sz = m_asserted_formulas.get_num_formulas(); for (unsigned i = 0; i < sz; i++) { expr * n = m_asserted_formulas.get_formula(i); if (m.is_or(n)) { CTRACE("relevancy_bug", !is_relevant(n), tout << "n: " << mk_ismt2_pp(n, m) << "\n";); SASSERT(is_relevant(n)); TRACE("check_relevancy", tout << "checking:\n" << mk_ll_pp(n, m) << "\n";); SASSERT(m_relevancy_propagator->check_relevancy_or(to_app(n), true)); } else if (m.is_not(n)) { CTRACE("relevancy_bug", !is_relevant(to_app(n)->get_arg(0)), tout << "n: " << mk_ismt2_pp(n, m) << "\n";); SASSERT(is_relevant(to_app(n)->get_arg(0))); } else { CTRACE("relevancy_bug", !is_relevant(n), tout << "n: " << mk_ismt2_pp(n, m) << "\n";); SASSERT(is_relevant(n)); } } return true; } /** \brief Check if expressions attached to bool_variables and enodes have a consistent assignment. For all a, b. root(a) == root(b) ==> get_assignment(a) == get_assignment(b) */ bool context::check_eqc_bool_assignment() const { for (enode* e : m_enodes) { if (m.is_bool(e->get_owner())) { enode * r = e->get_root(); CTRACE("eqc_bool", get_assignment(e) != get_assignment(r), tout << "#" << e->get_owner_id() << "\n" << mk_pp(e->get_owner(), m) << "\n"; tout << "#" << r->get_owner_id() << "\n" << mk_pp(r->get_owner(), m) << "\n"; tout << "assignments: " << get_assignment(e) << " " << get_assignment(r) << "\n"; display(tout);); SASSERT(get_assignment(e) == get_assignment(r)); } } return true; } bool context::check_bool_var_vector_sizes() const { SASSERT(m_assignment.size() == 2 * m_bdata.size()); SASSERT(m_watches.size() == 2 * m_bdata.size()); SASSERT(m_bdata.size() == m_activity.size()); SASSERT(m_bool_var2expr.size() == m_bdata.size()); return true; } /** \brief Check the following property: - for every equality atom (= lhs rhs) assigned to false, relevant: if lhs->get_root() and rhs->get_root() are attached to theory variables v1 and v2 of theory t, then there is an entry (t, v1', v2') in m_propagated_th_diseqs such that, (= get_enode(v1') get_enode(v2')) is congruent to (= lhs rhs). */ bool context::check_th_diseq_propagation() const { TRACE("check_th_diseq_propagation", tout << "m_propagated_th_diseqs.size() " << m_propagated_th_diseqs.size() << "\n";); int num = get_num_bool_vars(); if (inconsistent()) { return true; } for (bool_var v = 0; v < num; v++) { if (has_enode(v)) { enode * n = bool_var2enode(v); if (n->is_eq() && is_relevant(n) && get_assignment(v) == l_false && !m.is_iff(n->get_owner())) { TRACE("check_th_diseq_propagation", tout << "checking: #" << n->get_owner_id() << " " << mk_bounded_pp(n->get_owner(), m) << "\n";); enode * lhs = n->get_arg(0)->get_root(); enode * rhs = n->get_arg(1)->get_root(); if (rhs->is_interpreted() && lhs->is_interpreted()) continue; TRACE("check_th_diseq_propagation", tout << "num. theory_vars: " << lhs->get_num_th_vars() << " " << mk_pp(m.get_sort(lhs->get_owner()), m) << "\n";); theory_var_list * l = lhs->get_th_var_list(); while (l) { theory_id th_id = l->get_th_id(); theory * th = get_theory(th_id); TRACE("check_th_diseq_propagation", tout << "checking theory: " << m.get_family_name(th_id) << "\n";); // if the theory doesn't use diseqs, then the diseqs are not propagated. if (th->use_diseqs() && rhs->get_th_var(th_id) != null_theory_var) { bool found = false; // lhs and rhs are attached to theory th_id for (new_th_eq const& eq : m_propagated_th_diseqs) { if (eq.m_th_id == th_id) { enode * lhs_prime = th->get_enode(eq.m_lhs)->get_root(); enode * rhs_prime = th->get_enode(eq.m_rhs)->get_root(); TRACE("check_th_diseq_propagation", tout << m.get_family_name(eq.m_th_id) << "\n";); if ((lhs == lhs_prime && rhs == rhs_prime) || (rhs == lhs_prime && lhs == rhs_prime)) { TRACE("check_th_diseq_propagation", tout << "ok v" << v << " " << get_assignment(v) << "\n";); found = true; break; } } } if (!found) { // missed theory diseq propagation display(std::cout); std::cout << "checking theory: " << m.get_family_name(th_id) << "\n"; std::cout << "root: #" << n->get_root()->get_owner_id() << " node: #" << n->get_owner_id() << "\n"; std::cout << mk_pp(n->get_owner(), m) << "\n"; std::cout << "lhs: #" << lhs->get_owner_id() << ", rhs: #" << rhs->get_owner_id() << "\n"; std::cout << mk_bounded_pp(lhs->get_owner(), m) << " " << mk_bounded_pp(rhs->get_owner(), m) << "\n"; } VERIFY(found); } l = l->get_next(); } } } } return true; } bool context::check_missing_diseq_conflict() const { for (enode_pair const& p : m_diseq_vector) { enode * n1 = p.first; enode * n2 = p.second; if (n1->get_root() == n2->get_root()) { TRACE("diseq_bug", tout << "n1: #" << n1->get_owner_id() << ", n2: #" << n2->get_owner_id() << ", r: #" << n1->get_root()->get_owner_id() << "\n"; tout << "n1 parents:\n"; display_parent_eqs(tout, n1); tout << "n2 parents:\n"; display_parent_eqs(tout, n2); tout << "r parents:\n"; display_parent_eqs(tout, n1->get_root()); ); UNREACHABLE(); } } return true; } #endif bool context::validate_justification(bool_var v, bool_var_data const& d, b_justification const& j) { if (j.get_kind() == b_justification::CLAUSE && v != true_bool_var) { clause* cls = j.get_clause(); literal l = cls->get_literal(0); if (l.var() != v) { l = cls->get_literal(1); } SASSERT(l.var() == v); SASSERT(m_assignment[l.index()] == l_true); } return true; } bool context::validate_model() { if (!m_proto_model) { return true; } for (literal lit : m_assigned_literals) { if (!is_relevant(lit)) { continue; } expr_ref n(m), res(m); literal2expr(lit, n); if (!is_ground(n)) { continue; } if (is_quantifier(n) && m.is_rec_fun_def(to_quantifier(n))) { continue; } switch (get_assignment(lit)) { case l_undef: break; case l_true: if (!m_proto_model->eval(n, res, false)) return true; CTRACE("mbqi_bug", !m.is_true(res), tout << n << " evaluates to " << res << "\n";); if (m.is_false(res)) { return false; } break; case l_false: if (!m_proto_model->eval(n, res, false)) return true; CTRACE("mbqi_bug", !m.is_false(res), tout << n << " evaluates to " << res << "\n";); if (m.is_true(res)) { return false; } break; } } return true; } /** \brief validate unsat core returned by */ void context::validate_unsat_core() { if (!get_fparams().m_core_validate) { return; } context ctx(get_manager(), get_fparams(), get_params()); ptr_vector assertions; get_assertions(assertions); unsigned sz = assertions.size(); for (unsigned i = 0; i < sz; ++i) { ctx.assert_expr(assertions[i]); } sz = m_unsat_core.size(); for (unsigned i = 0; i < sz; ++i) { ctx.assert_expr(m_unsat_core.get(i)); } lbool res = ctx.check(); switch (res) { case l_false: break; default: throw default_exception("Core could not be validated"); } } }; z3-z3-4.8.7/src/smt/smt_context_pp.cpp000066400000000000000000000553371356505360400176100ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_context_pp.cpp Abstract: SMT logical context: pretty printing Author: Leonardo de Moura (leonardo) 2008-02-21. Revision History: --*/ #include "smt/smt_context.h" #include "ast/ast_ll_pp.h" #include "ast/ast_pp.h" #include "ast/ast_pp_util.h" #include "util/stats.h" namespace smt { std::ostream& context::display_last_failure(std::ostream& out) const { switch(m_last_search_failure) { case OK: return out << "OK"; case UNKNOWN: return out << "UNKNOWN"; case MEMOUT: return out << "MEMOUT"; case CANCELED: return out << "CANCELED"; case NUM_CONFLICTS: return out << "NUM_CONFLICTS"; case RESOURCE_LIMIT: return out << "RESOURCE_LIMIT"; case THEORY: if (!m_incomplete_theories.empty()) { bool first = true; for (theory* th : m_incomplete_theories) { if (first) first = false; else out << " "; out << th->get_name(); } } else { out << "THEORY"; } return out; case QUANTIFIERS: return out << "QUANTIFIERS"; } UNREACHABLE(); return out << "?"; } std::string context::last_failure_as_string() const { std::string r; switch(m_last_search_failure) { case OK: r = m_unknown; break; case MEMOUT: r = "memout"; break; case CANCELED: r = "canceled"; break; case NUM_CONFLICTS: r = "max-conflicts-reached"; break; case THEORY: { r = "(incomplete (theory"; for (theory* t : m_incomplete_theories) { r += " "; r += t->get_name(); } r += "))"; break; } case RESOURCE_LIMIT: r = "(resource limits reached)"; break; case QUANTIFIERS: r = "(incomplete quantifiers)"; break; case UNKNOWN: r = m_unknown; break; } return r; } void context::display_asserted_formulas(std::ostream & out) const { m_asserted_formulas.display_ll(out, get_pp_visited()); } std::ostream& context::display_literal(std::ostream & out, literal l) const { l.display_compact(out, m_bool_var2expr.c_ptr()); return out; } std::ostream& context::display_literals(std::ostream & out, unsigned num_lits, literal const * lits) const { display_compact(out, num_lits, lits, m_bool_var2expr.c_ptr()); return out; } std::ostream& context::display_literal_verbose(std::ostream & out, literal lit) const { return display_literals_verbose(out, 1, &lit); } std::ostream& context::display_literals_verbose(std::ostream & out, unsigned num_lits, literal const * lits) const { display_verbose(out, m, num_lits, lits, m_bool_var2expr.c_ptr(), "\n"); return out; } std::ostream& context::display_literal_smt2(std::ostream& out, literal l) const { if (l.sign()) out << " (not " << mk_bounded_pp(bool_var2expr(l.var()), m, 10) << ") "; else out << " " << mk_bounded_pp(bool_var2expr(l.var()), m, 10) << " "; return out; } std::ostream& context::display_literals_smt2(std::ostream& out, unsigned num_lits, literal const* lits) const { for (unsigned i = 0; i < num_lits; ++i) { display_literal_smt2(out, lits[i]) << "\n"; } return out; } void context::display_literal_info(std::ostream & out, literal l) const { l.display_compact(out, m_bool_var2expr.c_ptr()); display_literal_smt2(out, l); out << "relevant: " << is_relevant(bool_var2expr(l.var())) << ", val: " << get_assignment(l) << "\n"; } void context::display_watch_list(std::ostream & out, literal l) const { display_literal(out, l); out << " watch_list:\n"; watch_list & wl = const_cast(m_watches[l.index()]); watch_list::clause_iterator it = wl.begin_clause(); watch_list::clause_iterator end = wl.end_clause(); for (; it != end; ++it) { display_clause(out, *it); out << "\n"; } } void context::display_watch_lists(std::ostream & out) const { unsigned s = m_watches.size(); for (unsigned l_idx = 0; l_idx < s; l_idx++) { literal l = to_literal(l_idx); display_watch_list(out, l); out << "\n"; } } void context::display_enode_defs(std::ostream & out) const { for (enode * x : m_enodes) { expr * n = x->get_owner(); ast_def_ll_pp(out, m, n, get_pp_visited(), true, false); } } void context::display_bool_var_defs(std::ostream & out) const { unsigned num = get_num_bool_vars(); for (unsigned v = 0; v < num; v++) { expr * n = m_bool_var2expr[v]; ast_def_ll_pp(out, m, n, get_pp_visited(), true, false); } } std::ostream& context::display_clause_detail(std::ostream & out, clause const * cls) const { out << "lemma: " << cls->is_lemma() << "\n"; for (literal l : *cls) { display_literal(out, l); out << ", val: " << get_assignment(l) << ", lvl: " << get_assign_level(l) << ", ilvl: " << get_intern_level(l.var()) << ", var: " << l.var() << "\n" << mk_bounded_pp(bool_var2expr(l.var()), m, 2) << "\n\n"; } return out; } std::ostream& context::display_clause(std::ostream & out, clause const * cls) const { cls->display_compact(out, m, m_bool_var2expr.c_ptr()); return out; } std::ostream& context::display_clause_smt2(std::ostream & out, clause const& cls) const { cls.display_smt2(out, m, m_bool_var2expr.c_ptr()); return out; } std::ostream& context::display_clauses(std::ostream & out, ptr_vector const & v) const { for (clause* cp : v) { display_clause_smt2(out, *cp); out << "\n"; } return out; } std::ostream& context::display_binary_clauses(std::ostream & out) const { bool first = true; unsigned l_idx = 0; for (watch_list const& wl : m_watches) { literal l1 = to_literal(l_idx++); literal neg_l1 = ~l1; literal const * it2 = wl.begin_literals(); literal const * end2 = wl.end_literals(); for (; it2 != end2; ++it2) { literal l2 = *it2; if (l1.index() < l2.index()) { if (first) { out << "binary clauses:\n"; first = false; } expr_ref t1(m), t2(m); literal2expr(neg_l1, t1); literal2expr(l2, t2); expr_ref disj(m.mk_or(t1, t2), m); out << mk_bounded_pp(disj, m, 3) << "\n"; #if 0 out << "(clause "; display_literal(out, neg_l1); out << " "; display_literal(out, l2); out << ")\n"; #endif } } } return out; } void context::display_assignment(std::ostream & out) const { if (!m_assigned_literals.empty()) { 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, 1, &lit, m_bool_var2expr.c_ptr()); out << "\n"; } } } void context::display_assignment_as_smtlib2(std::ostream& out, symbol const& logic) const { ast_smt_pp pp(m); pp.set_benchmark_name("lemma"); pp.set_status("unknown"); pp.set_logic(logic); for (literal lit : m_assigned_literals) { expr_ref n(m); literal2expr(lit, n); pp.add_assumption(n); } pp.display_smt2(out, m.mk_true()); } void context::display_eqc(std::ostream & out) const { bool first = true; for (enode * x : m_enodes) { expr * n = x->get_owner(); expr * r = x->get_root()->get_owner(); if (n != r) { if (first) { out << "equivalence classes:\n"; first = false; } out << "#" << n->get_id() << " -> #" << r->get_id() << ": "; out << mk_pp(n, m) << " -> " << mk_pp(r, m) << "\n"; } } } void context::display_app_enode_map(std::ostream & out) const { if (!m_e_internalized_stack.empty()) { out << "expression -> enode:\n"; unsigned sz = m_e_internalized_stack.size(); for (unsigned i = 0; i < sz; i++) { expr * n = m_e_internalized_stack.get(i); out << "(#" << n->get_id() << " -> e!" << i << ") "; } out << "\n"; } } void context::display_expr_bool_var_map(std::ostream & out) const { if (!m_b_internalized_stack.empty()) { out << "expression -> bool_var:\n"; unsigned sz = m_b_internalized_stack.size(); 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() << " -> " << literal(v, false) << ") "; } out << "\n"; } } void context::display_hot_bool_vars(std::ostream & out) const { out << "hot bool vars:\n"; int num = get_num_bool_vars(); for (bool_var v = 0; v < num; v++) { double val = get_activity(v)/m_bvar_inc; if (val > 10.00) { expr * n = m_b_internalized_stack.get(v); out << "#"; out.width(5); out << std::left; out << n->get_id(); out << " "; out.width(12); out << std::right; out << get_activity(v) << " "; out.width(12); out << val; out << "\n"; } } } void context::display_relevant_exprs(std::ostream & out) const { m_relevancy_propagator->display(out); } void context::display_theories(std::ostream & out) const { for (theory* th : m_theory_set) { th->display(out); } } void context::display(std::ostream & out) const { get_pp_visited().reset(); out << "Logical context:\n"; out << "scope-lvl: " << m_scope_lvl << "\n"; out << "base-lvl: " << m_base_lvl << "\n"; out << "search-lvl: " << m_search_lvl << "\n"; out << "inconsistent(): " << inconsistent() << "\n"; out << "m_asserted_formulas.inconsistent(): " << m_asserted_formulas.inconsistent() << "\n"; display_bool_var_defs(out); display_enode_defs(out); display_asserted_formulas(out); if (!m_aux_clauses.empty()) { out << "auxiliary clauses:\n"; display_clauses(out, m_aux_clauses); } if (!m_lemmas.empty()) { out << "lemmas:\n"; display_clauses(out, m_lemmas); } display_binary_clauses(out); display_assignment(out); display_eqc(out); m_cg_table.display_compact(out); m_case_split_queue->display(out); display_expr_bool_var_map(out); display_app_enode_map(out); display_relevant_exprs(out); display_theories(out); display_decl2enodes(out); display_hot_bool_vars(out); } void context::display_eq_detail(std::ostream & out, enode * n) const { SASSERT(n->is_eq()); out << "#" << n->get_owner_id() << ", root: #" << n->get_root()->get_owner_id() << ", cg: #" << n->m_cg->get_owner_id() << ", val: " << get_assignment(enode2bool_var(n)) << ", lhs: #" << n->get_arg(0)->get_owner_id() << ", rhs: #" << n->get_arg(1)->get_owner_id() << ", lhs->root: #" << n->get_arg(0)->get_root()->get_owner_id() << ", rhs->root: #" << n->get_arg(1)->get_root()->get_owner_id() << ", is_marked: " << n->is_marked() << ", is_relevant: " << is_relevant(n) << ", iscope_lvl: " << n->get_iscope_lvl() << "\n"; } void context::display_parent_eqs(std::ostream & out, enode * n) const { for (enode* parent : n->get_parents()) { if (parent->is_eq()) display_eq_detail(out, parent); } } void context::display_unsat_core(std::ostream & out) const { for (expr* c : m_unsat_core) { out << mk_pp(c, m) << "\n"; } } void context::collect_statistics(::statistics & st) const { st.update("conflicts", m_stats.m_num_conflicts); st.update("decisions", m_stats.m_num_decisions); st.update("propagations", m_stats.m_num_propagations + m_stats.m_num_bin_propagations); st.update("binary propagations", m_stats.m_num_bin_propagations); st.update("restarts", m_stats.m_num_restarts); st.update("final checks", m_stats.m_num_final_checks); st.update("added eqs", m_stats.m_num_add_eq); st.update("mk clause", m_stats.m_num_mk_clause); st.update("del clause", m_stats.m_num_del_clause); st.update("dyn ack", m_stats.m_num_dyn_ack); st.update("interface eqs", m_stats.m_num_interface_eqs); st.update("max generation", m_stats.m_max_generation); st.update("minimized lits", m_stats.m_num_minimized_lits); st.update("num checks", m_stats.m_num_checks); st.update("mk bool var", m_stats.m_num_mk_bool_var); #if 0 // missing? st.update("mk lit", m_stats.m_num_mk_lits); st.update("sat conflicts", m_stats.m_num_sat_conflicts); st.update("del bool var", m_stats.m_num_del_bool_var); st.update("mk enode", m_stats.m_num_mk_enode); st.update("del enode", m_stats.m_num_del_enode); st.update("mk bin clause", m_stats.m_num_mk_bin_clause); st.update("backwd subs", m_stats.m_num_bs); st.update("backwd subs res", m_stats.m_num_bsr); st.update("frwrd subs res", m_stats.m_num_fsr); #endif m_qmanager->collect_statistics(st); m_asserted_formulas.collect_statistics(st); for (theory* th : m_theory_set) { th->collect_statistics(st); } } void context::display_statistics(std::ostream & out) const { ::statistics st; collect_statistics(st); st.display(out); } void context::display_istatistics(std::ostream & out) const { ::statistics st; collect_statistics(st); st.display_internal(out); } void context::display_lemma_as_smt_problem(std::ostream & out, unsigned num_antecedents, literal const * antecedents, literal consequent, symbol const& logic) const { ast_pp_util visitor(m); expr_ref_vector fmls(m); visitor.collect(fmls); expr_ref n(m); for (unsigned i = 0; i < num_antecedents; i++) { literal l = antecedents[i]; literal2expr(l, n); fmls.push_back(std::move(n)); } if (consequent != false_literal) { literal2expr(~consequent, n); fmls.push_back(std::move(n)); } if (logic != symbol::null) out << "(set-logic " << logic << ")\n"; visitor.collect(fmls); visitor.display_decls(out); visitor.display_asserts(out, fmls, true); out << "(check-sat)\n"; } unsigned context::display_lemma_as_smt_problem(unsigned num_antecedents, literal const * antecedents, literal consequent, symbol const& logic) const { std::stringstream strm; strm << "lemma_" << (++m_lemma_id) << ".smt2"; std::ofstream out(strm.str()); TRACE("lemma", tout << strm.str() << "\n";); display_lemma_as_smt_problem(out, num_antecedents, antecedents, consequent, logic); out.close(); return m_lemma_id; } void context::display_lemma_as_smt_problem(std::ostream & out, unsigned num_antecedents, literal const * antecedents, unsigned num_eq_antecedents, enode_pair const * eq_antecedents, literal consequent, symbol const& logic) const { ast_pp_util visitor(m); expr_ref_vector fmls(m); visitor.collect(fmls); expr_ref n(m); for (unsigned i = 0; i < num_antecedents; i++) { literal l = antecedents[i]; literal2expr(l, n); fmls.push_back(n); } for (unsigned i = 0; i < num_eq_antecedents; i++) { enode_pair const & p = eq_antecedents[i]; n = m.mk_eq(p.first->get_owner(), p.second->get_owner()); fmls.push_back(n); } if (consequent != false_literal) { literal2expr(~consequent, n); fmls.push_back(n); } if (logic != symbol::null) out << "(set-logic " << logic << ")\n"; visitor.collect(fmls); visitor.display_decls(out); visitor.display_asserts(out, fmls, true); out << "(check-sat)\n"; } unsigned context::display_lemma_as_smt_problem(unsigned num_antecedents, literal const * antecedents, unsigned num_eq_antecedents, enode_pair const * eq_antecedents, literal consequent, symbol const& logic) const { std::stringstream strm; strm << "lemma_" << (++m_lemma_id) << ".smt2"; std::ofstream out(strm.str()); TRACE("lemma", tout << strm.str() << "\n";); display_lemma_as_smt_problem(out, num_antecedents, antecedents, num_eq_antecedents, eq_antecedents, consequent, logic); out.close(); return m_lemma_id; } /** \brief Display enode definitions #n := (f #i_1 ... #i_n), where #i_k is the root of the k-th argument of the enode #n. */ void context::display_normalized_enodes(std::ostream & out) const { out << "normalized enodes:\n"; for (enode * n : m_enodes) { out << "#"; out.width(5); out << std::left << n->get_owner_id() << " #"; out.width(5); out << n->get_root()->get_owner_id() << " := " << std::right; unsigned num = n->get_owner()->get_num_args(); if (num > 0) out << "("; out << n->get_decl()->get_name(); if (!n->get_decl()->private_parameters()) display_parameters(out, n->get_decl()->get_num_parameters(), n->get_decl()->get_parameters()); for (unsigned i = 0; i < num; i++) { expr * arg = n->get_owner()->get_arg(i); if (e_internalized(arg)) { enode * n = get_enode(arg)->get_root(); out << " #" << n->get_owner_id(); } else { out << " #" << arg->get_id(); } } if (num > 0) out << ")"; if (is_relevant(n)) out << "\t*"; out << "\n"; } } void context::display_enodes_lbls(std::ostream & out) const { for (enode* n : m_enodes) { n->display_lbls(out); } } void context::display_decl2enodes(std::ostream & out) const { out << "decl2enodes:\n"; unsigned id = 0; for (enode_vector const& v : m_decl2enodes) { if (!v.empty()) { out << "id " << id << " ->"; for (enode* n : v) { out << " #" << n->get_owner_id(); } out << "\n"; } ++id; } } void context::display_subexprs_info(std::ostream & out, expr * n) const { ptr_buffer todo; todo.push_back(n); while (!todo.empty()) { expr * n = todo.back(); todo.pop_back(); out << "#"; out.width(6); out << std::left << n->get_id(); out << ", relevant: " << is_relevant(n); if (m.is_bool(n)) { out << ", val: "; out.width(7); out << std::right; if (lit_internalized(n)) out << get_assignment(n); else out << "l_undef"; } if (e_internalized(n)) { enode * e = get_enode(n); out << ", root: #" << e->get_root()->get_owner_id(); } out << "\n"; if (is_app(n)) { for (expr* arg : *to_app(n)) { todo.push_back(arg); } } } } 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: out << "bin " << j.get_literal(); break; case b_justification::CLAUSE: { clause * cls = j.get_clause(); out << "clause "; if (cls) out << literal_vector(cls->get_num_literals(), cls->begin()); if (cls) display_literals_smt2(out << "\n", cls->get_num_literals(), cls->begin()); break; } case b_justification::JUSTIFICATION: { literal_vector lits; const_cast(*m_conflict_resolution).justification2literals(j.get_justification(), lits); out << "justification " << j.get_justification()->get_from_theory() << ": " << lits; break; } default: UNREACHABLE(); break; } return out << "\n"; } void context::trace_assign(literal l, b_justification j, bool decision) const { SASSERT(m.has_trace_stream()); std::ostream & out = m.trace_stream(); out << "[assign] "; display_literal(out, l); if (decision) out << " decision"; out << " "; display(out, j); } std::ostream& operator<<(std::ostream& out, enode_pp const& p) { ast_manager& m = p.ctx.get_manager(); enode* n = p.n; return out << "[#" << n->get_owner_id() << " " << mk_bounded_pp(n->get_owner(), m) << "]"; } std::ostream& operator<<(std::ostream& out, enode_eq_pp const& p) { return out << enode_pp(p.p.first, p.ctx) << " = " << enode_pp(p.p.second, p.ctx) << "\n"; } }; z3-z3-4.8.7/src/smt/smt_context_stat.cpp000066400000000000000000000111121356505360400201230ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_context_stat.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-03-06. Revision History: --*/ #include "smt/smt_context.h" #include "ast/ast_pp.h" namespace smt { unsigned context::get_lemma_avg_activity() const { if (m_lemmas.empty()) return 0; unsigned long long acc = 0; for (clause const* cp : m_lemmas) { acc += cp->get_activity(); } return static_cast(acc / m_lemmas.size()); } static void acc_num_occs(clause * cls, unsigned_vector & lit2num_occs) { for (literal l : *cls) { lit2num_occs[l.index()]++; } } static void acc_num_occs(clause_vector const & v, unsigned_vector & lit2num_occs) { for (auto cp : v) { acc_num_occs(cp, lit2num_occs); } } void context::display_literal_num_occs(std::ostream & out) const { unsigned num_lits = m_assignment.size(); unsigned_vector lit2num_occs; lit2num_occs.resize(num_lits, 0); acc_num_occs(m_aux_clauses, lit2num_occs); acc_num_occs(m_lemmas, lit2num_occs); for (unsigned lidx = 0; lidx < num_lits; lidx++) { literal l = to_literal(lidx); if (lit2num_occs[lidx] > 0) { out << lit2num_occs[lidx] << " "; // display_literal(out, l); out << l.sign() << " " << mk_pp(bool_var2expr(l.var()), m); out << "\n"; } } } void context::display_num_assigned_literals_per_lvl(std::ostream & out) const { unsigned n = 0; out << "["; for (scope const& s : m_scopes) { SASSERT(n <= s.m_assigned_literals_lim); out << (s.m_assigned_literals_lim - n) << " "; n = s.m_assigned_literals_lim; } SASSERT(n <= m_assigned_literals.size()); out << (m_assigned_literals.size() - n) << "]"; } static void acc_var_num_occs(clause * cls, unsigned_vector & var2num_occs) { for (literal l : *cls) { var2num_occs[l.var()]++; } } static void acc_var_num_occs(clause_vector const & v, unsigned_vector & var2num_occs) { for (auto const& n : v) { acc_var_num_occs(n, var2num_occs); } } void context::display_var_occs_histogram(std::ostream & out) const { unsigned num_vars = get_num_bool_vars(); unsigned_vector var2num_occs; var2num_occs.resize(num_vars, 0); acc_var_num_occs(m_aux_clauses, var2num_occs); acc_var_num_occs(m_lemmas, var2num_occs); unsigned_vector histogram; for (unsigned v = 0; v < num_vars; v++) { unsigned num_occs = var2num_occs[v]; histogram.reserve(num_occs+1, 0); histogram[num_occs]++; } out << "number of atoms having k occs:\n"; unsigned sz = histogram.size(); for (unsigned i = 1; i < sz; i++) if (histogram[i] > 0) out << i << ":" << histogram[i] << " "; out << "\n"; } static void acc_var_num_min_occs(clause * cls, unsigned_vector & var2num_min_occs) { unsigned num_lits = cls->get_num_literals(); bool_var min_var = (*cls)[0].var(); for (unsigned i = 1; i < num_lits; i++) { bool_var v = (*cls)[i].var(); if (v < min_var) min_var = v; } var2num_min_occs[min_var]++; } static void acc_var_num_min_occs(clause_vector const & v, unsigned_vector & var2num_min_occs) { for (auto const& c : v) { acc_var_num_min_occs(c, var2num_min_occs); } } void context::display_num_min_occs(std::ostream & out) const { unsigned num_vars = get_num_bool_vars(); unsigned_vector var2num_min_occs; var2num_min_occs.resize(num_vars, 0); acc_var_num_min_occs(m_aux_clauses, var2num_min_occs); acc_var_num_min_occs(m_lemmas, var2num_min_occs); out << "number of min occs:\n"; for (unsigned v = 0; v < num_vars; v++) { if (var2num_min_occs[v] > 0) out << v << ":" << var2num_min_occs[v] << " "; } out << "\n"; } void context::display_profile_res_sub(std::ostream & out) const { display_var_occs_histogram(std::cerr); display_num_min_occs(std::cerr); std::cerr << "\n"; } void context::display_profile(std::ostream & out) const { if (m_fparams.m_profile_res_sub) display_profile_res_sub(out); } }; z3-z3-4.8.7/src/smt/smt_enode.cpp000066400000000000000000000346231356505360400165120ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_enode.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-02-19. Revision History: --*/ #include "smt/smt_context.h" #include "smt/smt_enode.h" namespace smt { /** \brief Initialize an enode in the given memory position. */ enode * enode::init(ast_manager & m, void * mem, app2enode_t const & app2enode, app * owner, unsigned generation, bool suppress_args, bool merge_tf, unsigned iscope_lvl, bool cgc_enabled, bool update_children_parent) { SASSERT(m.is_bool(owner) || !merge_tf); enode * n = new (mem) enode(); n->m_owner = owner; n->m_root = n; n->m_next = n; n->m_cg = nullptr; n->m_class_size = 1; n->m_generation = generation; n->m_func_decl_id = UINT_MAX; n->m_mark = false; n->m_mark2 = false; n->m_interpreted = false; n->m_suppress_args = suppress_args; n->m_eq = m.is_eq(owner); n->m_commutative = n->get_num_args() == 2 && owner->get_decl()->is_commutative(); n->m_bool = m.is_bool(owner); n->m_merge_tf = merge_tf; n->m_cgc_enabled = cgc_enabled; n->m_iscope_lvl = iscope_lvl; n->m_lbl_hash = -1; n->m_proof_is_logged = false; unsigned num_args = n->get_num_args(); for (unsigned i = 0; i < num_args; i++) { enode * arg = app2enode[owner->get_arg(i)->get_id()]; n->m_args[i] = arg; SASSERT(n->get_arg(i) == arg); if (update_children_parent) arg->get_root()->m_parents.push_back(n); } TRACE("mk_enode_detail", tout << "new enode suppress_args: " << n->m_suppress_args << "\n";); SASSERT(n->m_suppress_args == suppress_args); return n; } enode * enode::mk(ast_manager & m, region & r, app2enode_t const & app2enode, app * owner, unsigned generation, bool suppress_args, bool merge_tf, unsigned iscope_lvl, bool cgc_enabled, bool update_children_parent) { SASSERT(m.is_bool(owner) || !merge_tf); unsigned sz = get_enode_size(suppress_args ? 0 : owner->get_num_args()); void * mem = r.allocate(sz); return init(m, mem, app2enode, owner, generation, suppress_args, merge_tf, iscope_lvl, cgc_enabled, update_children_parent); } enode * enode::mk_dummy(ast_manager & m, app2enode_t const & app2enode, app * owner) { unsigned sz = get_enode_size(owner->get_num_args()); void * mem = alloc_svect(char, sz); return init(m, mem, app2enode, owner, 0, false, false, 0, true, false); } void enode::del_eh(ast_manager & m, bool update_children_parent) { SASSERT(m_class_size == 1); SASSERT(m_root == this); SASSERT(m_next == this); unsigned num_args = get_num_args(); for (unsigned i = 0; i < num_args; i++) { enode * arg = get_arg(i); if (update_children_parent) { SASSERT(arg->get_root()->m_parents.back() == this); arg->get_root()->m_parents.pop_back(); } } this->~enode(); } unsigned enode::get_num_th_vars() const { unsigned r = 0; theory_var_list const * l = get_th_var_list(); while(l) { r++; l = l->get_next(); } return r; } /** \brief Return the theory var (in theory th_id) associated with the enode. Return null_theory_var if the enode is not associated with a variable of theory th_id */ theory_var enode::get_th_var(theory_id th_id) const { if (m_th_var_list.get_th_var() == null_theory_var) return null_theory_var; theory_var_list const * l = &m_th_var_list; while (l) { if (l->get_th_id() == th_id) { return l->get_th_var(); } l = l->get_next(); } return null_theory_var; } /** \brief Add the entry (v, id) to the list of theory variables. */ void enode::add_th_var(theory_var v, theory_id id, region & r) { #ifdef Z3DEBUG unsigned old_size = get_num_th_vars(); #endif SASSERT(get_th_var(id) == null_theory_var); if (m_th_var_list.get_th_var() == null_theory_var) { m_th_var_list.set_th_var(v); m_th_var_list.set_th_id(id); m_th_var_list.set_next(nullptr); } else { theory_var_list * l = &m_th_var_list; while (l->get_next() != nullptr) { SASSERT(l->get_th_id() != id); l = l->get_next(); } SASSERT(l); SASSERT(l->get_next() == 0); theory_var_list * new_cell = new (r) theory_var_list(id, v); l->set_next(new_cell); } SASSERT(get_num_th_vars() == old_size + 1); SASSERT(get_th_var(id) == v); } /** \brief Replace the entry (v', id) with the entry (v, id). The enode must have an entry (v', id) */ void enode::replace_th_var(theory_var v, theory_id id) { SASSERT(get_th_var(id) != null_theory_var); theory_var_list * l = get_th_var_list(); while (l) { if (l->get_th_id() == id) { l->set_th_var(v); return; } l = l->get_next(); } UNREACHABLE(); } /** \brief Delete theory variable. It assumes the enode is associated with a variable of the given theory. */ void enode::del_th_var(theory_id id) { SASSERT(get_th_var(id) != null_theory_var); if (m_th_var_list.get_th_id() == id) { theory_var_list * next = m_th_var_list.get_next(); if (next == nullptr) { // most common case m_th_var_list.set_th_var(null_theory_var); m_th_var_list.set_th_id(null_theory_id); m_th_var_list.set_next(nullptr); } else { m_th_var_list = *next; } } else { theory_var_list * prev = get_th_var_list(); theory_var_list * l = prev->get_next(); while (l) { SASSERT(prev->get_next() == l); if (l->get_th_id() == id) { prev->set_next(l->get_next()); return; } prev = l; l = l->get_next(); } UNREACHABLE(); } } /** \brief Push old value of generation on the context trail stack and update the generation. */ void enode::set_generation(context & ctx, unsigned generation) { if (m_generation == generation) return; ctx.push_trail(value_trail(m_generation)); m_generation = generation; } void enode::set_lbl_hash(context & ctx) { SASSERT(m_lbl_hash == -1); // m_lbl_hash should be different from -1, if and only if, // there is a pattern that contains the enode. So, // I use a trail to restore the value of m_lbl_hash to -1. ctx.push_trail(value_trail(m_lbl_hash)); unsigned h = hash_u(get_owner_id()); m_lbl_hash = h & (APPROX_SET_CAPACITY - 1); // propagate modification to the root m_lbls set. approx_set & r_lbls = m_root->m_lbls; if (!r_lbls.may_contain(m_lbl_hash)) { ctx.push_trail(value_trail(r_lbls)); r_lbls.insert(m_lbl_hash); } } enode * enode::get_eq_enode_with_min_gen() { if (m_generation == 0) return this; enode * r = this; enode * curr = this; do { if (curr->m_generation < r->m_generation) { r = curr; if (r->m_generation == 0) return r; } curr = curr->m_next; } while (curr != this); return r; } #ifdef Z3DEBUG bool enode::check_invariant() const { unsigned class_size = 0; bool found_root = false; bool found_this = false; bool has_interpreted = false; // "Equivalence" class structure. enode const * curr = this; do { SASSERT(curr->m_root == m_root); class_size++; if (curr == m_root) found_root = true; if (curr == this) found_this = true; if (curr->is_interpreted()) has_interpreted = true; curr = curr->m_next; } while (curr != this); SASSERT(found_root); SASSERT(found_this); SASSERT(this != m_root || class_size == m_class_size); SASSERT(!has_interpreted || m_root->is_interpreted()); // Parent use-list if (this == m_root) { for (enode* parent : m_parents) { unsigned i = 0; unsigned num_args = parent->get_num_args(); SASSERT(num_args > 0); for (; i < num_args; i++) { enode * arg = parent->get_arg(i); if (arg->get_root() == m_root) break; } SASSERT(i < num_args); } } // Proof tree // m_root is reachable from "this" by following the transitivity proof SASSERT(trans_reaches(m_root)); SASSERT(check_parent_invariant()); return true; } /** \brief Return true if the node is n or n is reached following the m_proof.m_target pointers */ bool enode::trans_reaches(enode * n) const { const enode * curr = this; while (curr != 0) { if (curr == n) { return true; } curr = curr->m_trans.m_target; } return false; } bool enode::check_parent_invariant() const { if (this != m_root) return true; enode const * curr = m_root; do { if (curr != m_root) { for (enode * p : curr->m_parents) { if (!p->is_true_eq() && !m_root->contains_parent_congruent_to(p)) { UNREACHABLE(); } } } curr = curr->m_next; } while (curr != m_root); return true; } bool enode::contains_parent_congruent_to(enode * p) const { for (enode* curr : m_parents) { if (congruent(curr, p)) return true; } return false; } #endif void enode::display_lbls(std::ostream & out) const { out << "#" << get_owner_id() << " -> #" << get_root()->get_owner_id() << ", lbls: " << get_lbls() << ", plbls: " << get_plbls() << ", root->lbls: " << get_root()->get_lbls() << ", root->plbls: " << get_root()->get_plbls(); if (has_lbl_hash()) out << ", lbl-hash: " << static_cast(get_lbl_hash()); out << "\n"; } bool congruent(enode * n1, enode * n2, bool & comm) { comm = false; if (n1->get_owner()->get_decl() != n2->get_owner()->get_decl()) return false; unsigned num_args = n1->get_num_args(); if (num_args != n2->get_num_args()) return false; if (n1->is_commutative()) { enode * c1_1 = n1->get_arg(0)->get_root(); enode * c1_2 = n1->get_arg(1)->get_root(); enode * c2_1 = n2->get_arg(0)->get_root(); enode * c2_2 = n2->get_arg(1)->get_root(); if (c1_1 == c2_1 && c1_2 == c2_2) { return true; } if (c1_1 == c2_2 && c1_2 == c2_1) { comm = true; return true; } return false; } else { for (unsigned i = 0; i < num_args; i++) if (n1->get_arg(i)->get_root() != n2->get_arg(i)->get_root()) return false; return true; } } unsigned get_max_generation(unsigned num_enodes, enode * const * enodes) { unsigned max = 0; for (unsigned i = 0; i < num_enodes; i++) { unsigned curr = enodes[i]->get_generation(); if (curr > max) max = curr; } return max; } void unmark_enodes(unsigned num_enodes, enode * const * enodes) { for (unsigned i = 0; i < num_enodes; i++) enodes[i]->unset_mark(); } void unmark_enodes2(unsigned num_enodes, enode * const * enodes) { for (unsigned i = 0; i < num_enodes; i++) enodes[i]->unset_mark2(); } tmp_enode::tmp_enode(): m_app(0), m_capacity(0), m_enode_data(nullptr) { SASSERT(m_app.get_app()->get_decl() == 0); set_capacity(5); } tmp_enode::~tmp_enode() { dealloc_svect(m_enode_data); } void tmp_enode::set_capacity(unsigned new_capacity) { SASSERT(new_capacity > m_capacity); if (m_enode_data) dealloc_svect(m_enode_data); m_capacity = new_capacity; unsigned sz = sizeof(enode) + m_capacity * sizeof(enode*); m_enode_data = alloc_svect(char, sz); memset(m_enode_data, 0, sz); enode * n = get_enode(); n->m_owner = m_app.get_app(); n->m_root = n; n->m_next = n; n->m_class_size = 1; n->m_cgc_enabled = true; n->m_func_decl_id = UINT_MAX; } enode * tmp_enode::set(func_decl * f, unsigned num_args, enode * const * args) { if (num_args > m_capacity) set_capacity(num_args * 2); enode * r = get_enode(); if (m_app.get_app()->get_decl() != f) { r->m_func_decl_id = UINT_MAX; } m_app.set_decl(f); m_app.set_num_args(num_args); r->m_commutative = num_args == 2 && f->is_commutative(); memcpy(get_enode()->m_args, args, sizeof(enode*)*num_args); return r; } void tmp_enode::reset() { get_enode()->m_func_decl_id = UINT_MAX; } }; z3-z3-4.8.7/src/smt/smt_enode.h000066400000000000000000000342761356505360400161630ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_enode.h Abstract: Author: Leonardo de Moura (leonardo) 2008-02-18. Revision History: --*/ #ifndef SMT_ENODE_H_ #define SMT_ENODE_H_ #include "ast/ast.h" #include "smt/smt_types.h" #include "smt/smt_eq_justification.h" #include "smt/smt_theory_var_list.h" #include "util/approx_set.h" namespace smt { /** \brief Justification for the transitivity rule. */ struct trans_justification { enode * m_target; eq_justification m_justification; trans_justification(): m_target(nullptr), m_justification(null_eq_justification) { } }; /** \ brief Use sparse maps in SMT solver. Define this to use hash maps rather than vectors over ast nodes. This is useful in the case there are many solvers, each referencing few nodes from a large ast manager. There is some unknown performance penalty for this. */ // #define SPARSE_MAP #ifndef SPARSE_MAP typedef ptr_vector app2enode_t; // app -> enode #else class app2enode_t : public u_map { public: void setx(unsigned x, enode *val, enode *def){ if (val == 0) erase(x); else insert(x,val); } }; #endif class tmp_enode; /** \brief Additional data-structure for implementing congruence closure, equality propagation, and the theory central bus of equalities. */ class enode { app * m_owner; //!< The application that 'owns' this enode. enode * m_root; //!< Representative of the equivalence class enode * m_next; //!< Next element in the equivalence class. enode * m_cg; unsigned m_class_size; //!< Size of the equivalence class if the enode is the root. unsigned m_generation; //!< Tracks how many quantifier instantiation rounds were needed to generate this enode. unsigned m_func_decl_id; //!< Id generated by the congruence table for fast indexing. unsigned m_mark:1; //!< Multi-purpose auxiliary mark. unsigned m_mark2:1; //!< Multi-purpose auxiliary mark. unsigned m_interpreted:1; //!< True if the node is an interpreted constant. unsigned m_suppress_args:1; //!< True if the arguments of m_owner should not be accessed by this enode. unsigned m_eq:1; //!< True if it is an equality unsigned m_commutative:1; //!< True if commutative app unsigned m_bool:1; //!< True if it is a boolean enode unsigned m_merge_tf:1; //!< True if the enode should be merged with true/false when the associated boolean variable is assigned. unsigned m_cgc_enabled:1; //!< True if congruence closure is enabled for this enode. unsigned m_iscope_lvl; //!< When the enode was internalized /* The following property is valid for m_parents If this = m_root, then for every term f(a) such that a->get_root() == m_root, there is a f(b) in m_parents such that b->get_root() == m_root, and f(a) and f(b) are congruent. Remark: f(a) and f(b) may have other arguments. Exception: If f(a) and f(b) are terms of the form (= a c) and (= b d), then m_parents will not contains (= b d) if b->get_root() == d->get_root(). Remark regarding relevancy propagation: relevancy is propagated to all elements of an equivalence class. So, if there is a f(a) that is relevant, then the congruent f(b) in m_parents will also be relevant. */ enode_vector m_parents; //!< Parent enodes of the equivalence class. theory_var_list m_th_var_list; //!< List of theories that 'care' about this enode. trans_justification m_trans; //!< A justification for the enode being equal to its root. bool m_proof_is_logged; //!< Indicates that the proof for the enode being equal to its root is in the log. signed char m_lbl_hash; //!< It is different from -1, if enode is used in a pattern approx_set m_lbls; approx_set m_plbls; enode * m_args[0]; //!< Cached args friend class context; friend class euf_manager; friend class conflict_resolution; friend class quantifier_manager; theory_var_list * get_th_var_list() { return m_th_var_list.get_th_var() == null_theory_var ? nullptr : &m_th_var_list; } friend class set_merge_tf_trail; /** \brief Return true if the enode should be merged with the true (false) enodes when the associated boolean variable is assigned to true (false). */ bool merge_tf() const { return m_merge_tf; } friend class add_th_var_trail; friend class replace_th_var_trail; void add_th_var(theory_var v, theory_id id, region & r); void replace_th_var(theory_var v, theory_id id); void del_th_var(theory_id id); friend class tmp_enode; static enode * init(ast_manager & m, void * mem, app2enode_t const & app2enode, app * owner, unsigned generation, bool suppress_args, bool merge_tf, unsigned iscope_lvl, bool cgc_enabled, bool update_children_parent); public: static unsigned get_enode_size(unsigned num_args) { return sizeof(enode) + num_args * sizeof(enode*); } static enode * mk(ast_manager & m, region & r, app2enode_t const & app2enode, app * owner, unsigned generation, bool suppress_args, bool merge_tf, unsigned iscope_lvl, bool cgc_enabled, bool update_children_parent); static enode * mk_dummy(ast_manager & m, app2enode_t const & app2enode, app * owner); static void del_dummy(enode * n) { dealloc_svect(reinterpret_cast(n)); } unsigned get_func_decl_id() const { return m_func_decl_id; } void set_func_decl_id(unsigned id) { m_func_decl_id = id; } void mark_as_interpreted() { SASSERT(!m_interpreted); SASSERT(m_owner->get_num_args() == 0); SASSERT(m_class_size == 1); m_interpreted = true; } void del_eh(ast_manager & m, bool update_children_parent = true); app * get_owner() const { return m_owner; } unsigned get_owner_id() const { return m_owner->get_id(); } func_decl * get_decl() const { return m_owner->get_decl(); } unsigned get_decl_id() const { return m_owner->get_decl()->get_decl_id(); } unsigned hash() const { return m_owner->hash(); } enode * get_root() const { return m_root; } void set_root(enode* r) { m_root = r; } enode * get_next() const { return m_next; } unsigned get_num_args() const { return m_suppress_args ? 0 : m_owner->get_num_args(); } enode * get_arg(unsigned idx) const { SASSERT(idx < get_num_args()); return m_args[idx]; } enode * const * get_args() const { return m_args; } class const_args { enode const& n; public: const_args(enode const& n):n(n) {} const_args(enode const* n):n(*n) {} enode_vector::const_iterator begin() const { return n.m_args; } enode_vector::const_iterator end() const { return n.m_args + n.get_num_args(); } }; class args { enode & n; public: args(enode & n):n(n) {} args(enode * n):n(*n) {} enode_vector::iterator begin() const { return n.m_args; } enode_vector::iterator end() const { return n.m_args + n.get_num_args(); } }; const_args get_const_args() const { return const_args(this); } // args get_args() { return args(this); } // unsigned get_id() const { // return m_id; // } unsigned get_class_size() const { return m_class_size; } bool is_bool() const { return m_bool; } bool is_eq() const { return m_eq; } bool is_true_eq() const { return m_eq && get_arg(0)->get_root() == get_arg(1)->get_root(); } bool is_marked() const { return m_mark; } void set_mark() { SASSERT(!m_mark); m_mark = true; } void unset_mark() { SASSERT(m_mark); m_mark = false; } bool is_marked2() const { return m_mark2; } void set_mark2() { SASSERT(!m_mark2); m_mark2 = true; } void unset_mark2() { SASSERT(m_mark2); m_mark2 = false; } bool is_interpreted() const { return m_interpreted; } /** \brief Return true if node is not a constant and it is the root of its congruence class. \remark if get_num_args() == 0, then is_cgr() = false. */ bool is_cgr() const { return m_cg == this; } enode * get_cg() const { return m_cg; } bool is_cgc_enabled() const { return m_cgc_enabled; } bool is_commutative() const { return m_commutative; } class const_parents { enode const& n; public: const_parents(enode const& _n):n(_n) {} const_parents(enode const* _n):n(*_n) {} enode_vector::const_iterator begin() const { return n.begin_parents(); } enode_vector::const_iterator end() const { return n.end_parents(); } }; class parents { enode& n; public: parents(enode & _n):n(_n) {} parents(enode * _n):n(*_n) {} enode_vector::iterator begin() const { return n.begin_parents(); } enode_vector::iterator end() const { return n.end_parents(); } }; parents get_parents() { return parents(this); } const_parents get_const_parents() const { return const_parents(this); } unsigned get_num_parents() const { return m_parents.size(); } enode_vector::iterator begin_parents() { return m_parents.begin(); } enode_vector::iterator end_parents() { return m_parents.end(); } enode_vector::const_iterator begin_parents() const { return m_parents.begin(); } enode_vector::const_iterator end_parents() const { return m_parents.end(); } theory_var_list const * get_th_var_list() const { return m_th_var_list.get_th_var() == null_theory_var ? nullptr : &m_th_var_list; } bool has_th_vars() const { return m_th_var_list.get_th_var() != null_theory_var; } unsigned get_num_th_vars() const; theory_var get_th_var(theory_id th_id) const; trans_justification get_trans_justification() { return m_trans; } unsigned get_generation() const { return m_generation; } void set_generation(context & ctx, unsigned generation); /** \brief Return the enode n that is in the eqc of *this, and has the minimal generation. That is, there is no other enode with smaller generation. */ enode * get_eq_enode_with_min_gen(); unsigned get_iscope_lvl() const { return m_iscope_lvl; } void set_lbl_hash(context & ctx); bool has_lbl_hash() const { return m_lbl_hash >= 0; } unsigned char get_lbl_hash() const { SASSERT(m_lbl_hash >= 0 && static_cast(m_lbl_hash) < approx_set_traits::capacity); return static_cast(m_lbl_hash); } approx_set & get_lbls() { return m_lbls; } approx_set & get_plbls() { return m_plbls; } const approx_set & get_lbls() const { return m_lbls; } const approx_set & get_plbls() const { return m_plbls; } void display_lbls(std::ostream & out) const; #ifdef Z3DEBUG bool check_invariant() const; bool trans_reaches(enode * n) const; bool check_parent_invariant() const; bool contains_parent_congruent_to(enode * p) const; #endif }; inline bool same_eqc(enode const * n1 , enode const * n2) { return n1->get_root() == n2->get_root(); } /** \brief Return true, if n1 and n2 are congruent. Set comm to true, if the nodes are congruent modulo commutativity. */ bool congruent(enode * n1, enode * n2, bool & comm); inline bool congruent(enode * n1, enode * n2) { bool aux; return congruent(n1, n2, aux); } unsigned get_max_generation(unsigned num_enodes, enode * const * enodes); void unmark_enodes(unsigned num_enodes, enode * const * enodes); void unmark_enodes2(unsigned num_enodes, enode * const * enodes); class tmp_enode { tmp_app m_app; unsigned m_capacity; char * m_enode_data; enode * get_enode() { return reinterpret_cast(m_enode_data); } void set_capacity(unsigned new_capacity); public: tmp_enode(); ~tmp_enode(); enode * set(func_decl * f, unsigned num_args, enode * const * args); void reset(); }; }; #endif /* SMT_ENODE_H_ */ z3-z3-4.8.7/src/smt/smt_eq_justification.h000066400000000000000000000046371356505360400204270ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_eq_justification.h Abstract: Author: Leonardo de Moura (leonardo) 2008-02-19. Revision History: --*/ #ifndef SMT_EQ_JUSTIFICATION_H_ #define SMT_EQ_JUSTIFICATION_H_ #include "smt/smt_literal.h" #include "util/tptr.h" namespace smt { /** \brief Proof like object used to track dependencies of equality propagation. The idea is to reduce the cost of dependency tracking for the most common justifications used during equality propagation: (asserted equality & congruence). */ class eq_justification { void * m_data; public: enum kind { AXIOM, //!< no justification, it is only used when proof generation is disabled CONGRUENCE, EQUATION, //!< asserted equation JUSTIFICATION //!< fallback }; explicit eq_justification(): m_data(reinterpret_cast(static_cast(AXIOM))) { } /** \brief Create a justification for the congruence rule. If commutativity == true, then it means it is a combined justification: commutativity + congruence. */ explicit eq_justification(bool commutativity): m_data(BOXTAGINT(void*, static_cast(commutativity), CONGRUENCE)) { } explicit eq_justification(literal l): m_data(BOXTAGINT(void*, l.index(), EQUATION)) { } explicit eq_justification(justification * js): m_data(TAG(void*, js, JUSTIFICATION)) { } kind get_kind() const { return static_cast(GET_TAG(m_data)); } literal get_literal() const { SASSERT(get_kind() == EQUATION); return to_literal(UNBOXINT(m_data)); } justification * get_justification() const { SASSERT(get_kind() == JUSTIFICATION); return UNTAG(justification*, m_data); } bool used_commutativity() const { SASSERT(get_kind() == CONGRUENCE); return UNBOXINT(m_data) != 0; } static eq_justification mk_axiom() { return eq_justification(); } static eq_justification mk_cg(bool comm = false) { return eq_justification(comm); } }; const eq_justification null_eq_justification(static_cast(nullptr)); }; #endif /* SMT_EQ_JUSTIFICATION_H_ */ z3-z3-4.8.7/src/smt/smt_failure.h000066400000000000000000000012641356505360400165070ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: smt_failure.h Abstract: Failures Author: Leonardo de Moura (leonardo) 2012-02-09. Revision History: --*/ #ifndef SMT_FAILURE_H_ #define SMT_FAILURE_H_ namespace smt { /** \brief Reason for a l_undef result in the check method. */ enum failure { OK, UNKNOWN, MEMOUT, CANCELED, //!< External cancel flag was set NUM_CONFLICTS, //!< Maximum number of conflicts was reached THEORY, //!< Theory is incomplete RESOURCE_LIMIT, QUANTIFIERS //!< Logical context contains universal quantifiers. }; }; #endif z3-z3-4.8.7/src/smt/smt_farkas_util.cpp000066400000000000000000000243141356505360400177200ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: smt_farkas_util.cpp Abstract: Utility for combining inequalities using coefficients obtained from Farkas lemmas. Author: Nikolaj Bjorner (nbjorner) 2013-11-2. Revision History: NB. This utility is specialized to proofs generated by the arithmetic solvers. --*/ #include "smt/smt_farkas_util.h" #include "ast/ast_pp.h" #include "ast/rewriter/th_rewriter.h" #include "ast/rewriter/bool_rewriter.h" namespace smt { farkas_util::farkas_util(ast_manager& m): m(m), a(m), m_ineqs(m), m_split_literals(false), m_time(0) { } void farkas_util::mk_coerce(expr*& e1, expr*& e2) { if (a.is_int(e1) && a.is_real(e2)) { e1 = a.mk_to_real(e1); } else if (a.is_int(e2) && a.is_real(e1)) { e2 = a.mk_to_real(e2); } } // TBD: arith_decl_util now supports coercion, so this should be deprecated. app* farkas_util::mk_add(expr* e1, expr* e2) { mk_coerce(e1, e2); return a.mk_add(e1, e2); } app* farkas_util::mk_mul(expr* e1, expr* e2) { mk_coerce(e1, e2); return a.mk_mul(e1, e2); } app* farkas_util::mk_le(expr* e1, expr* e2) { mk_coerce(e1, e2); return a.mk_le(e1, e2); } app* farkas_util::mk_ge(expr* e1, expr* e2) { mk_coerce(e1, e2); return a.mk_ge(e1, e2); } app* farkas_util::mk_gt(expr* e1, expr* e2) { mk_coerce(e1, e2); return a.mk_gt(e1, e2); } app* farkas_util::mk_lt(expr* e1, expr* e2) { mk_coerce(e1, e2); return a.mk_lt(e1, e2); } void farkas_util::mul(rational const& c, expr* e, expr_ref& res) { expr_ref tmp(m); if (c.is_one()) { tmp = e; } else { tmp = mk_mul(a.mk_numeral(c, c.is_int() && a.is_int(e)), e); } res = mk_add(res, tmp); } bool farkas_util::is_int_sort(app* c) { SASSERT(m.is_eq(c) || a.is_le(c) || a.is_lt(c) || a.is_gt(c) || a.is_ge(c)); SASSERT(a.is_int(c->get_arg(0)) || a.is_real(c->get_arg(0))); return a.is_int(c->get_arg(0)); } bool farkas_util::is_int_sort() { SASSERT(!m_ineqs.empty()); return is_int_sort(m_ineqs[0].get()); } void farkas_util::normalize_coeffs() { rational l(1); for (unsigned i = 0; i < m_coeffs.size(); ++i) { l = lcm(l, denominator(m_coeffs[i])); } if (!l.is_one()) { for (unsigned i = 0; i < m_coeffs.size(); ++i) { m_coeffs[i] *= l; } } m_normalize_factor = l; } app* farkas_util::mk_one() { return a.mk_numeral(rational(1), true); } app* farkas_util::fix_sign(bool is_pos, app* c) { expr* x, *y; SASSERT(m.is_eq(c) || a.is_le(c) || a.is_lt(c) || a.is_gt(c) || a.is_ge(c)); bool is_int = is_int_sort(c); if (is_int && is_pos && (a.is_lt(c, x, y) || a.is_gt(c, y, x))) { return mk_le(mk_add(x, mk_one()), y); } if (is_int && !is_pos && (a.is_le(c, x, y) || a.is_ge(c, y, x))) { // !(x <= y) <=> x > y <=> x >= y + 1 return mk_ge(x, mk_add(y, mk_one())); } if (is_pos) { return c; } if (a.is_le(c, x, y)) return mk_gt(x, y); if (a.is_lt(c, x, y)) return mk_ge(x, y); if (a.is_ge(c, x, y)) return mk_lt(x, y); if (a.is_gt(c, x, y)) return mk_le(x, y); UNREACHABLE(); return c; } void farkas_util::partition_ineqs() { m_reps.reset(); m_his.reset(); ++m_time; for (unsigned i = 0; i < m_ineqs.size(); ++i) { m_reps.push_back(process_term(m_ineqs[i].get())); } unsigned head = 0; while (head < m_ineqs.size()) { unsigned r = find(m_reps[head]); unsigned tail = head; for (unsigned i = head+1; i < m_ineqs.size(); ++i) { if (find(m_reps[i]) == r) { ++tail; if (tail != i) { SASSERT(tail < i); std::swap(m_reps[tail], m_reps[i]); app_ref tmp(m); tmp = m_ineqs[i].get(); m_ineqs[i] = m_ineqs[tail].get(); m_ineqs[tail] = tmp; std::swap(m_coeffs[tail], m_coeffs[i]); } } } head = tail + 1; m_his.push_back(head); } } unsigned farkas_util::find(unsigned idx) { if (m_ts.size() <= idx) { m_roots.resize(idx+1); m_size.resize(idx+1); m_ts.resize(idx+1); m_roots[idx] = idx; m_ts[idx] = m_time; m_size[idx] = 1; return idx; } if (m_ts[idx] != m_time) { m_size[idx] = 1; m_ts[idx] = m_time; m_roots[idx] = idx; return idx; } while (true) { if (m_roots[idx] == idx) { return idx; } idx = m_roots[idx]; } } void farkas_util::merge(unsigned i, unsigned j) { i = find(i); j = find(j); if (i == j) { return; } if (m_size[i] > m_size[j]) { std::swap(i, j); } m_roots[i] = j; m_size[j] += m_size[i]; } unsigned farkas_util::process_term(expr* e) { unsigned r = e->get_id(); ptr_vector todo; ast_mark mark; todo.push_back(e); while (!todo.empty()) { e = todo.back(); todo.pop_back(); if (mark.is_marked(e)) { continue; } mark.mark(e, true); if (is_uninterp(e)) { merge(r, e->get_id()); } if (is_app(e)) { app* a = to_app(e); for (unsigned i = 0; i < a->get_num_args(); ++i) { todo.push_back(a->get_arg(i)); } } } return r; } expr_ref farkas_util::extract_consequence(unsigned lo, unsigned hi) { bool is_int = is_int_sort(); app_ref zero(a.mk_numeral(rational::zero(), is_int), m); expr_ref res(m); res = zero; bool is_strict = false; bool is_eq = true; expr* x, *y; for (unsigned i = lo; i < hi; ++i) { app* c = m_ineqs[i].get(); if (m.is_eq(c, x, y)) { mul(m_coeffs[i], x, res); mul(-m_coeffs[i], y, res); } if (a.is_lt(c, x, y) || a.is_gt(c, y, x)) { mul(m_coeffs[i], x, res); mul(-m_coeffs[i], y, res); is_strict = true; is_eq = false; } if (a.is_le(c, x, y) || a.is_ge(c, y, x)) { mul(m_coeffs[i], x, res); mul(-m_coeffs[i], y, res); is_eq = false; } } zero = a.mk_numeral(rational::zero(), a.is_int(res)); if (is_eq) { res = m.mk_eq(res, zero); } else if (is_strict) { res = mk_lt(res, zero); } else { res = mk_le(res, zero); } res = m.mk_not(res); th_rewriter rw(m); params_ref params; params.set_bool("gcd_rounding", true); rw.updt_params(params); proof_ref pr(m); expr_ref result(m); rw(res, result, pr); fix_dl(result); return result; } void farkas_util::fix_dl(expr_ref& r) { expr* e; if (m.is_not(r, e)) { r = e; fix_dl(r); r = m.mk_not(r); return; } expr* e1, *e2, *e3, *e4; if ((m.is_eq(r, e1, e2) || a.is_lt(r, e1, e2) || a.is_gt(r, e1, e2) || a.is_le(r, e1, e2) || a.is_ge(r, e1, e2))) { if (a.is_add(e1, e3, e4) && a.is_mul(e3)) { r = m.mk_app(to_app(r)->get_decl(), a.mk_add(e4,e3), e2); } } } void farkas_util::reset() { m_ineqs.reset(); m_coeffs.reset(); } void farkas_util::add(rational const & coef, app * c) { bool is_pos = true; expr* e; while (m.is_not(c, e)) { is_pos = !is_pos; c = to_app(e); } if (!coef.is_zero() && !m.is_true(c)) { m_coeffs.push_back(coef); m_ineqs.push_back(fix_sign(is_pos, c)); } } expr_ref farkas_util::get() { TRACE("arith", for (unsigned i = 0; i < m_coeffs.size(); ++i) { tout << m_coeffs[i] << " * (" << mk_pp(m_ineqs[i].get(), m) << ") "; } tout << "\n"; ); m_normalize_factor = rational::one(); expr_ref res(m); if (m_coeffs.empty()) { res = m.mk_false(); return res; } bool is_int = is_int_sort(); if (is_int) { normalize_coeffs(); } if (m_split_literals) { // partition equalities into variable disjoint sets. // take the conjunction of these instead of the // linear combination. partition_ineqs(); expr_ref_vector lits(m); unsigned lo = 0; for (unsigned hi : m_his) { lits.push_back(extract_consequence(lo, hi)); lo = hi; } bool_rewriter(m).mk_or(lits.size(), lits.c_ptr(), res); IF_VERBOSE(2, { if (lits.size() > 1) { verbose_stream() << "combined lemma: " << res << "\n"; } }); } else { res = extract_consequence(0, m_coeffs.size()); } TRACE("arith", for (unsigned i = 0; i < m_coeffs.size(); ++i) { tout << m_coeffs[i] << " * (" << mk_pp(m_ineqs[i].get(), m) << ") "; } tout << "\n"; tout << res << "\n"; ); return res; } } z3-z3-4.8.7/src/smt/smt_farkas_util.h000066400000000000000000000053161356505360400173660ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: smt_farkas_util.h Abstract: Utility for combining inequalities using coefficients obtained from Farkas lemmas. Author: Nikolaj Bjorner (nbjorne) 2013-11-2. Revision History: NB. This utility is specialized to proofs generated by the arithmetic solvers. --*/ #ifndef FARKAS_UTIL_H_ #define FARKAS_UTIL_H_ #include "ast/arith_decl_plugin.h" namespace smt { class farkas_util { ast_manager& m; arith_util a; app_ref_vector m_ineqs; vector m_coeffs; rational m_normalize_factor; // utilities for separating coefficients bool m_split_literals; unsigned m_time; unsigned_vector m_roots, m_size, m_his, m_reps, m_ts; void mk_coerce(expr*& e1, expr*& e2); app* mk_add(expr* e1, expr* e2); app* mk_mul(expr* e1, expr* e2); app* mk_le(expr* e1, expr* e2); app* mk_ge(expr* e1, expr* e2); app* mk_gt(expr* e1, expr* e2); app* mk_lt(expr* e1, expr* e2); void mul(rational const& c, expr* e, expr_ref& res); bool is_int_sort(app* c); bool is_int_sort(); void normalize_coeffs(); app* mk_one(); app* fix_sign(bool is_pos, app* c); void partition_ineqs(); unsigned find(unsigned idx); void merge(unsigned i, unsigned j); unsigned process_term(expr* e); expr_ref extract_consequence(unsigned lo, unsigned hi); void fix_dl(expr_ref& r); public: farkas_util(ast_manager& m); /** \brief Reset state */ void reset(); /** \brief add a multiple of constraint c to the current state */ void add(rational const & coef, app * c); /** \brief Extract the complement of premises multiplied by Farkas coefficients. */ expr_ref get(); /** \brief Coefficients are normalized for integer problems. Retrieve multiplicant for normalization. */ rational const& get_normalize_factor() const { return m_normalize_factor; } /** \brief extract one or multiple consequences based on literal partition. Multiple consequences are strongst modulo a partition of variables. Consequence generation under literal partitioning maintains difference logic constraints. That is, if the original constraints are difference logic, then the consequent produced by literal partitioning is also difference logic. */ void set_split_literals(bool f) { m_split_literals = f; } }; } #endif z3-z3-4.8.7/src/smt/smt_for_each_relevant_expr.cpp000066400000000000000000000220341356505360400221150ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_for_each_relevant_expr.cpp Abstract: Author: Leonardo de Moura (leonardo) 2009-01-05. Revision History: --*/ #include "smt/smt_context.h" #include "smt/smt_for_each_relevant_expr.h" #include "ast/ast_pp.h" #include "ast/ast_ll_pp.h" namespace smt { bool check_at_labels::check(expr* n) { m_first = true; return count_at_labels_pos(n) <= 1; } unsigned check_at_labels::count_at_labels_lit(expr* n, bool polarity) { unsigned count = 0; buffer lbls; bool pos; if (m_manager.is_label_lit(n, lbls) || (m_manager.is_label(n, pos, lbls) && pos == polarity)) { buffer::const_iterator it = lbls.begin(); buffer::const_iterator end = lbls.end(); for (; it != end; ++it) { symbol const & s = *it; if (s.contains('@')) { TRACE("for_each_relevant_expr", tout << "@ label: " << mk_pp(n, m_manager) << "\n";); count += 1; } } } return count; } unsigned check_at_labels::count_at_labels_neg(expr* n) { if (!is_app(n)) { return 0; } app* a = to_app(n); unsigned sz = a->get_num_args(); unsigned count = count_at_labels_lit(n, false); if (m_manager.is_or(n)) { for (unsigned i = 0; i < sz; ++i) { count += count_at_labels_neg(a->get_arg(i)); } } else if (m_manager.is_not(n)) { count = count_at_labels_pos(a->get_arg(0)); } else if (m_manager.is_implies(n)) { count += count_at_labels_pos(a->get_arg(0)); count += count_at_labels_neg(a->get_arg(1)); } else if (m_manager.is_and(n)) { for (unsigned i = 0; i < sz; ++i) { count = std::max(count, count_at_labels_neg(a->get_arg(i))); } } if (count > 1 && m_first) { TRACE("for_each_relevant_expr", tout << mk_pp(n, m_manager) << "\n";); m_first = false; } return count; } unsigned check_at_labels::count_at_labels_pos(expr* n) { if (!is_app(n)) { return 0; } app* a = to_app(n); unsigned sz = a->get_num_args(); unsigned count = count_at_labels_lit(n, true); if (m_manager.is_and(n)) { for (unsigned i = 0; i < sz; ++i) { count += count_at_labels_pos(a->get_arg(i)); } } else if (m_manager.is_not(n)) { count = count_at_labels_neg(a->get_arg(0)); } else if (m_manager.is_implies(n)) { count = std::max(count, count_at_labels_neg(a->get_arg(0))); count = std::max(count, count_at_labels_pos(a->get_arg(1))); } else if (m_manager.is_or(n)) { for (unsigned i = 0; i < sz; ++i) { count = std::max(count, count_at_labels_pos(a->get_arg(i))); } } if (count > 1 && m_first) { TRACE("for_each_relevant_expr", tout << mk_pp(n, m_manager) << "\n";); m_first = false; } return count; } for_each_relevant_expr::for_each_relevant_expr(context & ctx): m_manager(ctx.get_manager()), m_context(ctx) { } void for_each_relevant_expr::operator()(expr * n) { TRACE("for_each_relevant_expr", tout << "#" << n->get_id() << "\n";); } void for_each_relevant_expr::reset() { m_todo.reset(); m_cache.reset(); } inline bool for_each_relevant_expr::is_relevant(expr * n) { return m_context.is_relevant(n); } inline lbool for_each_relevant_expr::get_assignment(expr * n) { if (!m_context.lit_internalized(n)) return l_true; // assume it is a top-level label return m_context.get_assignment(n); } void for_each_relevant_expr::process(expr * n) { TRACE("for_each_relevant_expr", tout << "processing:\n" << mk_bounded_pp(n, m_manager) << "\n";); TRACE("for_each_relevant_expr", tout << "processing:\n" << mk_pp(n, m_manager) << "\n";); if (m_cache.contains(n)) return; m_todo.reset(); m_todo.push_back(n); while (!m_todo.empty()) { expr * curr = m_todo.back(); m_todo.pop_back(); SASSERT(is_relevant(curr)); if (m_cache.contains(curr)) continue; operator()(curr); m_cache.insert(curr); if (!is_app(curr)) continue; if (to_app(curr)->get_family_id() == m_manager.get_basic_family_id()) { switch (to_app(curr)->get_decl_kind()) { case OP_OR: process_or(to_app(curr)); break; case OP_AND: process_and(to_app(curr)); break; case OP_ITE: process_ite(to_app(curr)); break; default: process_app(to_app(curr)); } } else { process_app(to_app(curr)); } } } void for_each_relevant_expr::process_app(app * n) { unsigned sz = n->get_num_args(); for (unsigned i = 0; i < sz; i++) { expr * arg = n->get_arg(i); if (m_cache.contains(arg)) continue; SASSERT(is_relevant(arg)); m_todo.push_back(arg); } } /** \brief Add a relevant child of n (that is assigned to val) to m_todo. \remark Give preference to a child that is already in the cache. */ void for_each_relevant_expr::process_relevant_child(app * n, lbool val) { unsigned sz = n->get_num_args(); TRACE("for_each_relevant_expr", tout << val << " " << mk_bounded_pp(n, m_manager) << "\n";); for (unsigned i = 0; i < sz; i++) { expr * arg = n->get_arg(i); if (!is_relevant(arg)) continue; if (get_assignment(arg) != val) continue; if (m_cache.contains(arg)) { TRACE("for_each_relevant_expr", tout << "justified by: " << mk_bounded_pp(arg, m_manager) << "\n";); return; // the current child justifies n. } } for (unsigned i = 0; i < sz; i++) { expr * arg = n->get_arg(i); if (!is_relevant(arg)) continue; if (get_assignment(arg) != val) continue; TRACE("for_each_relevant_expr", tout << "to_process: " << mk_bounded_pp(arg, m_manager) << "\n";); m_todo.push_back(arg); return; } UNREACHABLE(); } void for_each_relevant_expr::process_and(app * n) { switch (get_assignment(n)) { case l_undef: UNREACHABLE(); break; case l_false: process_relevant_child(n, l_false); break; case l_true: process_app(n); break; } } void for_each_relevant_expr::process_or(app * n) { switch (get_assignment(n)) { case l_undef: UNREACHABLE(); break; case l_false: process_app(n); break; case l_true: process_relevant_child(n, l_true); break; } } void for_each_relevant_expr::process_ite(app * n) { if (!m_cache.contains(n->get_arg(0))) m_todo.push_back(n->get_arg(0)); switch (get_assignment(n->get_arg(0))) { case l_false: if (!m_cache.contains(n->get_arg(2))) m_todo.push_back(n->get_arg(2)); break; case l_undef: UNREACHABLE(); break; case l_true: if (!m_cache.contains(n->get_arg(1))) m_todo.push_back(n->get_arg(1)); break; } } void collect_relevant_label_lits::operator()(expr * n) { TRACE("for_each_relevant_expr", tout << "label: " << m_manager.is_label_lit(n) << " " << " " << get_assignment(n) << " " << mk_bounded_pp(n, m_manager) << "\n";); if (!m_manager.is_label_lit(n)) return; if (get_assignment(n) != l_true) return; m_manager.is_label_lit(n, m_buffer); // copy symbols to buffer } void collect_relevant_labels::operator()(expr * n) { bool pos; TRACE("for_each_relevant_expr", tout << "label: " << m_manager.is_label(n) << " " << get_assignment(n) << " " << mk_bounded_pp(n, m_manager) << "\n";); if (!m_manager.is_label(n, pos)) return; if (pos && (get_assignment(n) != l_true)) return; if (!pos && (get_assignment(n) != l_false)) return; m_manager.is_label(n, pos, m_buffer); // copy symbols to buffer } }; z3-z3-4.8.7/src/smt/smt_for_each_relevant_expr.h000066400000000000000000000057111356505360400215650ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_for_each_relevant_expr.h Abstract: Author: Leonardo de Moura (leonardo) 2009-01-05. Revision History: --*/ #ifndef SMT_FOR_EACH_RELEVANT_EXPR_H_ #define SMT_FOR_EACH_RELEVANT_EXPR_H_ #include "ast/ast.h" #include "util/obj_hashtable.h" #include "util/vector.h" namespace smt { class context; class check_at_labels { ast_manager & m_manager; bool m_first; unsigned count_at_labels_pos(expr* n); unsigned count_at_labels_neg(expr* n); unsigned count_at_labels_lit(expr* n, bool polarity); public: check_at_labels(ast_manager& m) : m_manager(m) {}; /** \brief Check that 'n' as a formula contains at most one @ label within each and-or path. */ bool check(expr* cnstr); }; /** \brief Functor used to traverse the relevant expressions in a logical context. */ class for_each_relevant_expr { protected: ast_manager & m_manager; context & m_context; obj_hashtable m_cache; ptr_vector m_todo; bool m_first; void process_app(app * n); void process_relevant_child(app * n, lbool val); void process_and(app * n); void process_or(app * n); void process_ite(app * n); lbool get_assignment(expr * n); bool is_relevant(expr * n); public: for_each_relevant_expr(context & ctx); virtual ~for_each_relevant_expr() {} /** \brief Visit the relevant sub-expressions of n. That is, only subexpressions m of n, such that m_context.is_relevant(m). This method also tries to minimize the number of subexpressions visited. For each visited expression the method operator() is invoked. Only not-already-visited expressions are visited. */ void process(expr * n); /** \see process */ virtual void operator()(expr * n); /** \brief Reset the cache of already visited expressions. */ void reset(); }; class collect_relevant_label_lits : public for_each_relevant_expr { buffer & m_buffer; public: collect_relevant_label_lits(context & ctx, buffer & b): for_each_relevant_expr(ctx), m_buffer(b) { } ~collect_relevant_label_lits() override {} void operator()(expr * n) override; }; class collect_relevant_labels : public for_each_relevant_expr { buffer & m_buffer; public: collect_relevant_labels(context & ctx, buffer & b): for_each_relevant_expr(ctx), m_buffer(b) { } ~collect_relevant_labels() override {} void operator()(expr * n) override; }; }; #endif /* SMT_FOR_EACH_RELEVANT_EXPR_H_ */ z3-z3-4.8.7/src/smt/smt_implied_equalities.cpp000066400000000000000000000526171356505360400212730ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: smt_implied_equalities.cpp Abstract: Procedure for obtaining implied equalities relative to the state of a solver. Author: Nikolaj Bjorner (nbjorner) 2012-02-29 Revision History: --*/ #include "smt/smt_implied_equalities.h" #include "util/union_find.h" #include "ast/ast_pp.h" #include "ast/array_decl_plugin.h" #include "util/uint_set.h" #include "smt/smt_value_sort.h" #include "model/model_smt2_pp.h" #include "util/stopwatch.h" #include "model/model.h" #include "solver/solver.h" namespace smt { class get_implied_equalities_impl { ast_manager& m; solver& m_solver; union_find_default_ctx m_df; union_find m_uf; array_util m_array_util; stopwatch m_stats_timer; unsigned m_stats_calls; stopwatch m_stats_val_eq_timer; static stopwatch s_timer; static stopwatch s_stats_val_eq_timer; struct term_id { expr_ref term; unsigned id; term_id(expr_ref t, unsigned id): term(t), id(id) {} }; typedef vector term_ids; typedef obj_map sort2term_ids; // partition of terms by sort. void partition_terms(unsigned num_terms, expr* const* terms, sort2term_ids& termids) { for (unsigned i = 0; i < num_terms; ++i) { sort* s = m.get_sort(terms[i]); term_ids& vec = termids.insert_if_not_there2(s, term_ids())->get_data().m_value; vec.push_back(term_id(expr_ref(terms[i],m), i)); } } /** \brief Basic implied equalities method. It performs a simple N^2 loop over all pairs of terms. n1, .., n_k, t1, .., t_l */ void get_implied_equalities_filter_basic(uint_set const& non_values, term_ids& terms) { m_stats_timer.start(); uint_set root_indices; for (unsigned j = 0; j < terms.size(); ++j) { if (terms[j].id == m_uf.find(terms[j].id)) { root_indices.insert(j); } } uint_set::iterator it = non_values.begin(), end = non_values.end(); for (; it != end; ++it) { unsigned i = *it; expr* t = terms[i].term; uint_set::iterator it2 = root_indices.begin(), end2 = root_indices.end(); bool found_root_value = false; for (; it2 != end2; ++it2) { unsigned j = *it2; if (j == i) continue; if (j < i && non_values.contains(j)) continue; if (found_root_value && !non_values.contains(j)) continue; expr* s = terms[j].term; SASSERT(m.get_sort(t) == m.get_sort(s)); ++m_stats_calls; m_solver.push(); m_solver.assert_expr(m.mk_not(m.mk_eq(s, t))); bool is_eq = l_false == m_solver.check_sat(0,nullptr); m_solver.pop(1); TRACE("get_implied_equalities", tout << mk_pp(t, m) << " = " << mk_pp(s, m) << " " << (is_eq?"eq":"unrelated") << "\n";); if (is_eq) { m_uf.merge(terms[i].id, terms[j].id); if (!non_values.contains(j)) { found_root_value = true; } } } } m_stats_timer.stop(); } void get_implied_equalities_basic(term_ids& terms) { for (unsigned i = 0; i < terms.size(); ++i) { if (terms[i].id != m_uf.find(terms[i].id)) { continue; } expr* t = terms[i].term; for (unsigned j = 0; j < i; ++j) { expr* s = terms[j].term; SASSERT(m.get_sort(t) == m.get_sort(s)); ++m_stats_calls; m_stats_timer.start(); m_solver.push(); m_solver.assert_expr(m.mk_not(m.mk_eq(s, t))); bool is_eq = l_false == m_solver.check_sat(0,nullptr); m_solver.pop(1); m_stats_timer.stop(); TRACE("get_implied_equalities", tout << mk_pp(t, m) << " = " << mk_pp(s, m) << " " << (is_eq?"eq":"unrelated") << "\n";); if (is_eq) { m_uf.merge(terms[i].id, terms[j].id); break; } } } } /** \brief Extract implied equalities for a collection of terms in the current context. The routine relies on model values being unique for equal terms. So in particular, arrays that are equal should be canonized to the same value. This is not the case for Z3's models of arrays. Arrays are treated by extensionality: introduce a fresh index and compare the select of the arrays. */ void get_implied_equalities_model_based(model_ref& model, term_ids& terms) { SASSERT(!terms.empty()); sort* srt = m.get_sort(terms[0].term); if (m_array_util.is_array(srt)) { m_solver.push(); unsigned arity = get_array_arity(srt); expr_ref_vector args(m); args.push_back(nullptr); for (unsigned i = 0; i < arity; ++i) { sort* srt_i = get_array_domain(srt, i); expr* idx = m.mk_fresh_const("index", srt_i); args.push_back(idx); } for (unsigned i = 0; i < terms.size(); ++i) { args[0] = terms[i].term; terms[i].term = m.mk_app(m_array_util.get_family_id(), OP_SELECT, 0, nullptr, args.size(), args.c_ptr()); } assert_relevant(terms); VERIFY(m_solver.check_sat(0,nullptr) != l_false); model_ref model1; m_solver.get_model(model1); SASSERT(model1.get()); get_implied_equalities_model_based(model1, terms); m_solver.pop(1); return; } uint_set non_values; if (!is_value_sort(m, srt)) { for (unsigned i = 0; i < terms.size(); ++i) { non_values.insert(i); } get_implied_equalities_filter_basic(non_values, terms); //get_implied_equalities_basic(terms); return; } expr_ref_vector vals(m); expr_ref vl(m), eq(m); obj_map vals_map; m_stats_val_eq_timer.start(); s_stats_val_eq_timer.start(); params_ref p; p.set_bool("produce_models", false); m_solver.updt_params(p); for (unsigned i = 0; i < terms.size(); ++i) { expr* t = terms[i].term; vl = (*model)(t); TRACE("get_implied_equalities", tout << mk_pp(t, m) << " |-> " << mk_pp(vl, m) << "\n";); reduce_value(model, vl); if (!m.is_value(vl)) { TRACE("get_implied_equalities", tout << "Not a value: " << mk_pp(vl, m) << "\n";); non_values.insert(i); continue; } vals.push_back(vl); unsigned_vector& vec = vals_map.insert_if_not_there2(vl, unsigned_vector())->get_data().m_value; bool found = false; for (unsigned j = 0; !found && j < vec.size(); ++j) { expr* s = terms[vec[j]].term; m_solver.push(); m_solver.assert_expr(m.mk_not(m.mk_eq(t, s))); lbool is_sat = m_solver.check_sat(0,nullptr); m_solver.pop(1); TRACE("get_implied_equalities", tout << mk_pp(t, m) << " = " << mk_pp(s, m) << " " << is_sat << "\n";); if (is_sat == l_false) { found = true; m_uf.merge(terms[i].id, terms[vec[j]].id); } } if (!found) { vec.push_back(i); } } m_stats_val_eq_timer.stop(); s_stats_val_eq_timer.stop(); p.set_bool("produce_models", true); m_solver.updt_params(p); if (!non_values.empty()) { TRACE("get_implied_equalities", model_smt2_pp(tout, m, *model, 0);); get_implied_equalities_filter_basic(non_values, terms); //get_implied_equalities_basic(terms); } } void get_implied_equalities_core(model_ref& model, term_ids& terms) { get_implied_equalities_model_based(model, terms); //get_implied_equalities_basic(terms); } void assert_relevant(unsigned num_terms, expr* const* terms) { for (unsigned i = 0; i < num_terms; ++i) { sort* srt = m.get_sort(terms[i]); if (!m_array_util.is_array(srt)) { m_solver.assert_expr(m.mk_app(m.mk_func_decl(symbol("Relevant!"), 1, &srt, m.mk_bool_sort()), terms[i])); } } } void assert_relevant(term_ids& terms) { for (unsigned i = 0; i < terms.size(); ++i) { expr* t = terms[i].term; sort* srt = m.get_sort(t); if (!m_array_util.is_array(srt)) { m_solver.assert_expr(m.mk_app(m.mk_func_decl(symbol("Relevant!"), 1, &srt, m.mk_bool_sort()), t)); } } } void reduce_value(model_ref& model, expr_ref& vl) { expr* c, *e1, *e2; while (m.is_ite(vl, c, e1, e2)) { lbool r = reduce_cond(model, c); switch(r) { case l_true: vl = e1; break; case l_false: vl = e2; break; default: return; } } } lbool reduce_cond(model_ref& model, expr* e) { expr* e1 = nullptr, *e2 = nullptr; if (m.is_eq(e, e1, e2) && m_array_util.is_as_array(e1) && m_array_util.is_as_array(e2)) { if (e1 == e2) { return l_true; } func_decl* f1 = m_array_util.get_as_array_func_decl(to_app(e1)); func_decl* f2 = m_array_util.get_as_array_func_decl(to_app(e2)); func_interp* fi1 = model->get_func_interp(f1); func_interp* fi2 = model->get_func_interp(f2); if (fi1 == fi2) { return l_true; } unsigned n1 = fi1->num_entries(); for (unsigned i = 0; i < n1; ++i) { func_entry const* h1 = fi1->get_entry(i); for (unsigned j = 0; j < fi1->get_arity(); ++j) { if (!m.is_value(h1->get_arg(j))) { return l_undef; } } func_entry* h2 = fi2->get_entry(h1->get_args()); if (h2 && h1->get_result() != h2->get_result() && m.is_value(h1->get_result()) && m.is_value(h2->get_result())) { return l_false; } } } return l_undef; } public: get_implied_equalities_impl(ast_manager& m, solver& s) : m(m), m_solver(s), m_uf(m_df), m_array_util(m), m_stats_calls(0) {} lbool operator()(unsigned num_terms, expr* const* terms, unsigned* class_ids) { params_ref p; p.set_bool("produce_models", true); m_solver.updt_params(p); sort2term_ids termids; stopwatch timer; timer.start(); s_timer.start(); for (unsigned i = 0; i < num_terms; ++i) { m_uf.mk_var(); } m_solver.push(); assert_relevant(num_terms, terms); lbool is_sat = m_solver.check_sat(0,nullptr); if (is_sat != l_false) { model_ref model; m_solver.get_model(model); SASSERT(model.get()); partition_terms(num_terms, terms, termids); sort2term_ids::iterator it = termids.begin(), end = termids.end(); for (; it != end; ++it) { term_ids& term_ids = it->m_value; get_implied_equalities_core(model, term_ids); for (unsigned i = 0; i < term_ids.size(); ++i) { class_ids[term_ids[i].id] = m_uf.find(term_ids[i].id); } } TRACE("get_implied_equalities", for (unsigned i = 0; i < num_terms; ++i) { tout << mk_pp(terms[i], m) << " |-> " << class_ids[i] << "\n"; }); } m_solver.pop(1); timer.stop(); s_timer.stop(); IF_VERBOSE(1, verbose_stream() << s_timer.get_seconds() << "\t" << num_terms << "\t" << timer.get_seconds() << "\t" << m_stats_calls << "\t" << m_stats_timer.get_seconds() << "\t" << m_stats_val_eq_timer.get_seconds() << "\t" << s_stats_val_eq_timer.get_seconds() << "\n";); return is_sat; } }; stopwatch get_implied_equalities_impl::s_timer; stopwatch get_implied_equalities_impl::s_stats_val_eq_timer; lbool implied_equalities(ast_manager& m, solver& solver, unsigned num_terms, expr* const* terms, unsigned* class_ids) { get_implied_equalities_impl gi(m, solver); return gi(num_terms, terms, class_ids); } }; #if 0 // maxsat class for internal purposes. class maxsat { ast_manager& m; solver& m_solver; public: maxsat(solver& s) : m(s.m()), m_solver(s) {} lbool operator()(ptr_vector& soft_cnstrs) { return l_undef; } }; class term_equivs { union_find_default_ctx m_df; union_find m_uf; obj_map m_term2idx; ptr_vector m_idx2term; public: term_equivs(): m_uf(m_df) {} void merge(expr* t, expr* s) { m_uf.merge(var(t), var(s)); } private: unsigned var(expr* t) { map::obj_map_entry* e = m_term2idx.insert_if_not_there(t, m_idx2term.size()); unsigned idx = e->get_data().m_value; if (idx == m_idx2term.size()) { m_idx2term.push_back(t); } return idx; } }; /** \brief class to find implied equalities. It implements the following half-naive algorithm. The algorithm is half-naive because the terms being checked for equivalence class membership are foreign and it is up to the theory integration whether pairs of interface equalities are checked. The idea is that the model-based combination would avoid useless equality literals in the core. An alternative algorithm could use 'distinct' and an efficient solver for 'distinct'. Given terms t1, ..., tn, of the same type. - assert f(t1) = 1, .., f(tn) = n. - find MAX-SAT set A1, let the other literals be in B. - find MAX-SAT set of B, put it in A2, etc. - we now have MAX-SAT sets A1, A2, ... A_m. - terms in each set A_i can be different, but cannot be different at the same time as elements in A_{i+1}. - for i = m to 2 do: - Let A = A_i B = A_{i-1} - assert g(A) = 0, g(B) = 1 - find MAX-SAT set C over this constraint. - For each element t from A\C - check if g(t) = 0 and g(B) = 1 is unsat - minimize core, if there is pair such that - g(t) = 0, g(b) = 1 is unsat, then equality is forced. */ class implied_equalities_finder { ast_manager& m; solver& m_solver; term_equivs m_find; expr_ref_vector m_refs; obj_map m_fs; // t_i -> f(t_i) = i obj_map m_gs; // t_i -> g(t_i) public: implied_equalities_finder(solver& solver): m(solver.m()), m_solver(solver), m_refs(m) {} lbool operator()(unsigned num_terms, expr* const* terms, unsigned* class_ids) { m_find.reset(); // return l_undef; } private: void initialize(unsigned num_terms, expr* const* terms) { sort_ref bv(m); expr_ref eq(m), g(m), eq_proxy(m); symbol f("f"), g("g"); unsigned log_terms = 1, nt = num_terms; while (nt > 0) { log_terms++; nt /= 2; } bv = m_bv.mk_bv_sort(log_terms); for (unsigned i = 0; i < num_terms; ++i) { expr* t = terms[i]; sort* s = m.get_sort(t); eq = m.mk_eq(m.mk_app(m.mk_func_decl(f, 1, &s, bv), t), m_bv.mk_numeral(rational(i), bv)); eq_proxy = m.mk_fresh_const("f", m.mk_bool_sort()); m_solver.assert_expr(m.mk_iff(eq, eq_proxy)); g = m.mk_app(m.mk_func_decl(g, 1, &s, bv), t) m_fs.insert(t, eq_proxy); m_gs.insert(t, g); } } // // For each t in src, check if t can be different from all s in dst. // - if it can, then add t to dst. // - if it cannot, then record equivalence class. // void merge_classes(expr_ref_vector& src, expr_ref_vector& dst, equivs& eqs) { } }; lbool implied_equalities_core_based( solver& solver, unsigned num_terms, expr* const* terms, unsigned* class_ids, unsigned num_assumptions, expr * const * assumptions) { implied_equalities_finder ief(solver); solver.push(); for (unsigned i = 0; i < num_assumptions; ++i) { solver.assert_expr(assumptions[i]); } lbool is_sat = ief(num_terms, terms, class_ids); solver.pop(1); return is_sat; } /** \brief Extract implied equalities for a collection of terms in the current context. The routine uses a partition refinement approach. It assumes that all terms have the same sort. Initially, create the equalities E_1: t0 = t1, E_2: t1 = t2, ..., E_n: t_{n-1} = t_n Check if ! (E_1 & E_2 & ... & E_n) is satisfiable. if it is unsat, then all terms are equal. Otherwise, partition the terms by the equalities that are true in the current model, iterate. This version does not attempt to be economical on how many equalities are introduced and the size of the resulting clauses. The more advanced version of this approach re-uses equalities from a previous iteration and also represents a binary tree of propositional variables that cover multiple equalities. Eg., E_12 => E_1 & E_2, E_34 => E_3 & E_4, ... */ void get_implied_equalities_eq_based(term_ids& terms) { expr_ref_vector eqs(m); if (terms.size() == 1) { return; } m_solver.push(); for (unsigned i = 0; i + 1 < terms.size(); ++i) { expr* eq = m.mk_eq(terms[i].term, terms[i+1].term); expr* eq_lit = m.mk_fresh_const("E", m.mk_bool_sort()); eqs.push_back(eq_lit); m_solver.assert_expr(m.mk_implies(eq_lit, eq)); } m_solver.assert_expr(m.mk_not(m.mk_and(eqs.size(), eqs.c_ptr()))); lbool is_sat = m_solver.check_sat(0,0); switch(is_sat) { case l_false: for (unsigned i = 0; i + 1 < terms.size(); ++i) { m_uf.merge(terms[i].id, terms[i+1].id); } break; default: { term_ids tems2; for (unsigned i = 0; i + 1 < terms.size(); ++i) { expr_ref vl(m); model->eval(terms[i].term, vl); if (m.is_false(vl)) { } } break; } } m_solver.pop(1); } #endif z3-z3-4.8.7/src/smt/smt_implied_equalities.h000066400000000000000000000011631356505360400207260ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: smt_implied_equalities.h Abstract: Procedure for obtaining implied equalities relative to the state of a solver. Author: Nikolaj Bjorner (nbjorner) 2012-02-29 Revision History: --*/ #ifndef SMT_IMPLIED_EQUALITIES_H_ #define SMT_IMPLIED_EQUALITIES_H_ #include "smt/smt_solver.h" #include "util/lbool.h" #include "ast/ast.h" namespace smt { lbool implied_equalities( ast_manager & m, solver & solver, unsigned num_terms, expr* const* terms, unsigned* class_ids); }; #endif z3-z3-4.8.7/src/smt/smt_internalizer.cpp000066400000000000000000002044521356505360400201250ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_internalizer.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-02-20. Revision History: --*/ #include "smt/smt_context.h" #include "ast/expr_stat.h" #include "ast/ast_pp.h" #include "ast/ast_ll_pp.h" #include "ast/ast_smt2_pp.h" #include "smt/smt_model_finder.h" #include "ast/for_each_expr.h" namespace smt { /** \brief Return true if the expression is viewed as a logical gate. */ inline bool is_gate(ast_manager const & m, expr * n) { if (is_app(n) && to_app(n)->get_family_id() == m.get_basic_family_id()) { switch (to_app(n)->get_decl_kind()) { case OP_AND: case OP_OR: case OP_ITE: return true; case OP_EQ: return m.is_bool(to_app(n)->get_arg(0)); default: return false; } } return false; } #define White 0 #define Grey 1 #define Black 2 static int get_color(svector & tcolors, svector & fcolors, expr * n, bool gate_ctx) { svector & colors = gate_ctx ? tcolors : fcolors; if (colors.size() > n->get_id()) return colors[n->get_id()]; return White; } static void set_color(svector & tcolors, svector & fcolors, expr * n, bool gate_ctx, int color) { svector & colors = gate_ctx ? tcolors : fcolors; if (colors.size() <= n->get_id()) { colors.resize(n->get_id()+1, White); } colors[n->get_id()] = color; } /** \brief Return the foreign descendants of n. That is, the descendants of n where the family_id is different from fid. For example the descendants of (+ a (+ (f b) (* 2 (h (+ c d))))) are: - a - (f b) - (h (+ c d)) */ static void get_foreign_descendants(app * n, family_id fid, ptr_buffer & descendants) { SASSERT(n->get_family_id() == fid); SASSERT(fid != null_family_id); ptr_buffer todo; todo.push_back(n); ast_mark visited; while (!todo.empty()) { expr * curr = todo.back(); todo.pop_back(); if (visited.is_marked(n)) { continue; } visited.mark(n, true); if (!is_app(curr) || to_app(curr)->get_family_id() != fid) { descendants.push_back(curr); continue; } SASSERT(is_app(curr)); SASSERT(to_app(curr)->get_family_id() == fid); unsigned j = to_app(curr)->get_num_args(); while (j > 0) { --j; todo.push_back(to_app(curr)->get_arg(j)); } } } void context::ts_visit_child(expr * n, bool gate_ctx, svector & tcolors, svector< int> & fcolors, svector & todo, bool & visited) { if (get_color(tcolors, fcolors, n, gate_ctx) == White) { todo.push_back(expr_bool_pair(n, gate_ctx)); visited = false; } } bool context::ts_visit_children(expr * n, bool gate_ctx, svector & tcolors, svector & fcolors, svector & todo) { if (is_quantifier(n)) return true; SASSERT(is_app(n)); if (m.is_bool(n)) { if (b_internalized(n)) return true; } else { if (e_internalized(n)) return true; } bool visited = true; family_id fid = to_app(n)->get_family_id(); theory * th = m_theories.get_plugin(fid); bool def_int = th == nullptr || th->default_internalizer(); if (!def_int) { ptr_buffer descendants; get_foreign_descendants(to_app(n), fid, descendants); ptr_buffer::iterator it = descendants.begin(); ptr_buffer::iterator end = descendants.end(); for (; it != end; ++it) { expr * arg = *it; ts_visit_child(arg, false, tcolors, fcolors, todo, visited); } return visited; } SASSERT(def_int); if (m.is_term_ite(n)) { ts_visit_child(to_app(n)->get_arg(0), true, tcolors, fcolors, todo, visited); ts_visit_child(to_app(n)->get_arg(1), false, tcolors, fcolors, todo, visited); ts_visit_child(to_app(n)->get_arg(2), false, tcolors, fcolors, todo, visited); return visited; } bool new_gate_ctx = m.is_bool(n) && (is_gate(m, n) || m.is_not(n)); unsigned j = to_app(n)->get_num_args(); while (j > 0) { --j; expr * arg = to_app(n)->get_arg(j); ts_visit_child(arg, new_gate_ctx, tcolors, fcolors, todo, visited); } return visited; } void context::top_sort_expr(expr * n, svector & sorted_exprs) { svector todo; svector tcolors; svector fcolors; todo.push_back(expr_bool_pair(n, true)); while (!todo.empty()) { expr_bool_pair & p = todo.back(); expr * curr = p.first; bool gate_ctx = p.second; switch (get_color(tcolors, fcolors, curr, gate_ctx)) { case White: set_color(tcolors, fcolors, curr, gate_ctx, Grey); ts_visit_children(curr, gate_ctx, tcolors, fcolors, todo); break; case Grey: SASSERT(ts_visit_children(curr, gate_ctx, tcolors, fcolors, todo)); set_color(tcolors, fcolors, curr, gate_ctx, Black); if (n != curr && !m.is_not(curr)) sorted_exprs.push_back(expr_bool_pair(curr, gate_ctx)); break; case Black: todo.pop_back(); break; default: UNREACHABLE(); } } } #define DEEP_EXPR_THRESHOLD 1024 /** \brief Internalize an expression asserted into the logical context using the given proof as a justification. \remark pr is 0 if proofs are disabled. */ void context::internalize_assertion(expr * n, proof * pr, unsigned generation) { TRACE("internalize_assertion", tout << mk_pp(n, m) << "\n";); TRACE("internalize_assertion_ll", tout << mk_ll_pp(n, m) << "\n";); TRACE("generation", tout << "generation: " << m_generation << "\n";); 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 the expression is deep, then execute topological sort to avoid // stack overflow. // a caveat is that theory internalizers do rely on recursive descent so // internalization over these follows top-down TRACE("deep_internalize", tout << "expression is deep: #" << n->get_id() << "\n" << mk_ll_pp(n, m);); svector sorted_exprs; top_sort_expr(n, sorted_exprs); TRACE("deep_internalize", for (auto & kv : sorted_exprs) tout << "#" << kv.first->get_id() << " " << kv.second << "\n"; ); for (auto & kv : sorted_exprs) { expr* e = kv.first; if (!is_app(e) || to_app(e)->get_family_id() == null_family_id || to_app(e)->get_family_id() == m.get_basic_family_id()) internalize(e, kv.second); } } SASSERT(m.is_bool(n)); if (is_gate(m, n)) { switch(to_app(n)->get_decl_kind()) { case OP_AND: { for (expr * arg : *to_app(n)) { internalize(arg, true); literal lit = get_literal(arg); mk_root_clause(1, &lit, pr); } break; } case OP_OR: { literal_buffer lits; for (expr * arg : *to_app(n)) { internalize(arg, true); lits.push_back(get_literal(arg)); } mk_root_clause(lits.size(), lits.c_ptr(), pr); add_or_rel_watches(to_app(n)); break; } case OP_EQ: { expr * lhs = to_app(n)->get_arg(0); expr * rhs = to_app(n)->get_arg(1); internalize(lhs, true); internalize(rhs, true); literal l1 = get_literal(lhs); literal l2 = get_literal(rhs); mk_root_clause(l1, ~l2, pr); mk_root_clause(~l1, l2, pr); break; } case OP_ITE: { expr * c = to_app(n)->get_arg(0); expr * t = to_app(n)->get_arg(1); expr * e = to_app(n)->get_arg(2); internalize(c, true); internalize(t, true); internalize(e, true); literal cl = get_literal(c); literal tl = get_literal(t); literal el = get_literal(e); mk_root_clause(~cl, tl, pr); mk_root_clause(cl, el, pr); add_ite_rel_watches(to_app(n)); break; } default: UNREACHABLE(); } mark_as_relevant(n); } else if (m.is_distinct(n)) { assert_distinct(to_app(n), pr); mark_as_relevant(n); } else { assert_default(n, pr); } } void context::assert_default(expr * n, proof * pr) { internalize(n, true); literal l = get_literal(n); if (l == false_literal) { set_conflict(mk_justification(justification_proof_wrapper(*this, pr))); } else { justification* j = mk_justification(justification_proof_wrapper(*this, pr)); m_clause_proof.add(l, CLS_AUX, j); assign(l, j); mark_as_relevant(l); } } #define DISTINCT_SZ_THRESHOLD 32 void context::assert_distinct(app * n, proof * pr) { TRACE("assert_distinct", tout << mk_pp(n, m) << "\n";); unsigned num_args = n->get_num_args(); if (num_args == 0 || num_args <= DISTINCT_SZ_THRESHOLD || m.proofs_enabled()) { assert_default(n, pr); return; } sort * s = m.get_sort(n->get_arg(0)); sort_ref u(m.mk_fresh_sort("distinct-elems"), m); func_decl_ref f(m.mk_fresh_func_decl("distinct-aux-f", "", 1, &s, u), m); for (expr * arg : *n) { app_ref fapp(m.mk_app(f, arg), m); app_ref val(m.mk_fresh_const("unique-value", u), m); enode * e = mk_enode(val, false, false, true); e->mark_as_interpreted(); app_ref eq(m.mk_eq(fapp, val), m); TRACE("assert_distinct", tout << "eq: " << mk_pp(eq, m) << "\n";); assert_default(eq, nullptr); mark_as_relevant(eq.get()); // TODO: we may want to hide the auxiliary values val and the function f from the model. } } void context::internalize(expr * n, bool gate_ctx, unsigned generation) { flet l(m_generation, generation); m_stats.m_max_generation = std::max(m_generation, m_stats.m_max_generation); internalize(n, gate_ctx); } void context::ensure_internalized(expr* e) { if (!e_internalized(e)) { internalize(e, false); } } /** \brief Internalize the given expression into the logical context. - gate_ctx is true if the expression is in the context of a logical gate. */ void context::internalize(expr * n, bool gate_ctx) { TRACE("internalize", tout << "internalizing:\n" << mk_pp(n, m) << "\n";); TRACE("internalize_bug", tout << "internalizing:\n" << mk_bounded_pp(n, m) << "\n";); if (is_var(n)) { throw default_exception("Formulas should not contain unbound variables"); } if (m.is_bool(n)) { SASSERT(is_quantifier(n) || is_app(n)); internalize_formula(n, gate_ctx); } else if (is_lambda(n)) { internalize_lambda(to_quantifier(n)); } else { SASSERT(is_app(n)); SASSERT(!gate_ctx); internalize_term(to_app(n)); } } /** \brief Internalize the given formula into the logical context. */ void context::internalize_formula(expr * n, bool gate_ctx) { TRACE("internalize_bug", tout << "internalize formula: #" << n->get_id() << ", gate_ctx: " << gate_ctx << "\n" << mk_pp(n, m) << "\n";); SASSERT(m.is_bool(n)); if (m.is_true(n) || m.is_false(n)) return; if (m.is_not(n) && gate_ctx) { // a boolean variable does not need to be created if n a NOT gate is in // the context of a gate. internalize(to_app(n)->get_arg(0), true); return; } if (b_internalized(n)) { // n was already internalized as a boolean. bool_var v = get_bool_var(n); TRACE("internalize_bug", tout << "#" << n->get_id() << " already has bool_var v" << v << "\n";); // n was already internalized as boolean, but an enode was // not associated with it. So, an enode is necessary, if // n is not in the context of a gate and is an application. if (!gate_ctx && is_app(n)) { if (e_internalized(n)) { TRACE("internalize_bug", tout << "forcing enode #" << n->get_id() << " to merge with t/f\n";); enode * e = get_enode(to_app(n)); set_merge_tf(e, v, false); } else { TRACE("internalize_bug", tout << "creating enode for #" << n->get_id() << "\n";); mk_enode(to_app(n), true, /* suppress arguments, we not not use CC for this kind of enode */ true, /* bool enode must be merged with true/false, since it is not in the context of a gate */ false /* CC is not enabled */ ); set_enode_flag(v, false); if (get_assignment(v) != l_undef) propagate_bool_var_enode(v); } SASSERT(has_enode(v)); } return; } if (m.is_eq(n) && !m.is_iff(n)) internalize_eq(to_app(n), gate_ctx); else if (m.is_distinct(n)) internalize_distinct(to_app(n), gate_ctx); else if (is_app(n) && internalize_theory_atom(to_app(n), gate_ctx)) return; else if (is_quantifier(n)) internalize_quantifier(to_quantifier(n), gate_ctx); else internalize_formula_core(to_app(n), gate_ctx); } /** \brief Internalize an equality. */ void context::internalize_eq(app * n, bool gate_ctx) { TRACE("internalize", tout << mk_pp(n, m) << "\n";); SASSERT(!b_internalized(n)); SASSERT(m.is_eq(n)); internalize_formula_core(n, gate_ctx); bool_var v = get_bool_var(n); bool_var_data & d = get_bdata(v); d.set_eq_flag(); sort * s = m.get_sort(n->get_arg(0)); theory * th = m_theories.get_plugin(s->get_family_id()); if (th) th->internalize_eq_eh(n, v); } /** \brief Internalize distinct constructor. */ void context::internalize_distinct(app * n, bool gate_ctx) { TRACE("distinct", tout << "internalizing distinct: " << mk_pp(n, m) << "\n";); SASSERT(!b_internalized(n)); SASSERT(m.is_distinct(n)); expr_ref def(m.mk_distinct_expanded(n->get_num_args(), n->get_args()), m); internalize(def, true); bool_var v = mk_bool_var(n); literal l(v); literal l_def = get_literal(def); mk_gate_clause(~l, l_def); mk_gate_clause(l, ~l_def); add_relevancy_dependency(n, def); if (!gate_ctx) { mk_enode(n, true, true, false); set_enode_flag(v, true); SASSERT(get_assignment(v) == l_undef); } } /** \brief Try to internalize n as a theory atom. Return true if succeeded. The application can be internalize as a theory atom, if there is a theory (plugin) that can internalize n. */ bool context::internalize_theory_atom(app * n, bool gate_ctx) { SASSERT(!b_internalized(n)); theory * th = m_theories.get_plugin(n->get_family_id()); TRACE("datatype_bug", tout << "internalizing theory atom:\n" << mk_pp(n, m) << "\n";); if (!th || !th->internalize_atom(n, gate_ctx)) return false; TRACE("datatype_bug", tout << "internalization succeeded\n" << mk_pp(n, m) << "\n";); SASSERT(b_internalized(n)); TRACE("internalize_theory_atom", tout << "internalizing theory atom: #" << n->get_id() << "\n";); bool_var v = get_bool_var(n); if (!gate_ctx) { // if the formula is not in the context of a gate, then it // must be associated with an enode. if (!e_internalized(n)) { mk_enode(to_app(n), true, /* suppress arguments, we not not use CC for this kind of enode */ true /* bool enode must be merged with true/false, since it is not in the context of a gate */, false /* CC is not enabled */); } else { SASSERT(e_internalized(n)); enode * e = get_enode(n); set_enode_flag(v, true); set_merge_tf(e, v, true); } } if (e_internalized(n)) { set_enode_flag(v, true); if (get_assignment(v) != l_undef) propagate_bool_var_enode(v); } SASSERT(!e_internalized(n) || has_enode(v)); return true; } #ifdef Z3DEBUG struct check_pattern_proc { void operator()(var * v) {} void operator()(quantifier * q) {} void operator()(app * n) { if (is_ground(n)) return; SASSERT(n->get_decl()->is_flat_associative() || n->get_num_args() == n->get_decl()->get_arity()); } }; /** Debugging code: check whether for all (non-ground) applications (f a_1 ... a_n) in t, f->get_arity() == n */ static bool check_pattern(expr * t) { check_pattern_proc p; for_each_expr(p, t); return true; } static bool check_patterns(quantifier * q) { for (unsigned i = 0; i < q->get_num_patterns(); i++) { SASSERT(check_pattern(q->get_pattern(i))); } for (unsigned i = 0; i < q->get_num_no_patterns(); i++) { SASSERT(check_pattern(q->get_no_pattern(i))); } return true; } #endif /** \brief Internalize the given quantifier into the logical context. */ void context::internalize_quantifier(quantifier * q, bool gate_ctx) { TRACE("internalize_quantifier", tout << mk_pp(q, m) << "\n";); CTRACE("internalize_quantifier_zero", q->get_weight() == 0, tout << mk_pp(q, m) << "\n";); SASSERT(gate_ctx); // limitation of the current implementation SASSERT(!b_internalized(q)); SASSERT(is_forall(q)); SASSERT(check_patterns(q)); bool_var v = mk_bool_var(q); unsigned generation = m_generation; unsigned _generation; if (!m_cached_generation.empty() && m_cached_generation.find(q, _generation)) { generation = _generation; } // TODO: do we really need this flag? bool_var_data & d = get_bdata(v); d.set_quantifier_flag(); m_qmanager->add(q, generation); } void context::internalize_lambda(quantifier * q) { TRACE("internalize_quantifier", tout << mk_pp(q, m) << "\n";); SASSERT(is_lambda(q)); app_ref lam_name(m.mk_fresh_const("lambda", m.get_sort(q)), m); app_ref eq(m), lam_app(m); expr_ref_vector vars(m); vars.push_back(lam_name); unsigned sz = q->get_num_decls(); for (unsigned i = 0; i < sz; ++i) { vars.push_back(m.mk_var(sz - i - 1, q->get_decl_sort(i))); } array_util autil(m); lam_app = autil.mk_select(vars.size(), vars.c_ptr()); eq = m.mk_eq(lam_app, q->get_expr()); quantifier_ref fa(m); expr * patterns[1] = { m.mk_pattern(lam_app) }; fa = m.mk_forall(sz, q->get_decl_sorts(), q->get_decl_names(), eq, 0, m.lambda_def_qid(), symbol::null, 1, patterns); internalize_quantifier(fa, true); if (!e_internalized(lam_name)) internalize_uninterpreted(lam_name); m_app2enode.setx(q->get_id(), get_enode(lam_name), nullptr); } /** \brief Internalize gates and (uninterpreted and equality) predicates. */ void context::internalize_formula_core(app * n, bool gate_ctx) { SASSERT(!b_internalized(n)); SASSERT(!e_internalized(n)); CTRACE("resolve_conflict_crash", m.is_not(n), tout << mk_ismt2_pp(n, m) << "\ngate_ctx: " << gate_ctx << "\n";); bool _is_gate = is_gate(m, n) || m.is_not(n); // process args for (expr * arg : *n) { internalize(arg, _is_gate); } CTRACE("internalize_bug", b_internalized(n), tout << mk_ll_pp(n, m) << "\n";); bool is_new_var = false; bool_var v; // n can be already internalized after its children are internalized. // Example (ite-term): (= (ite c 1 0) 1) // // When (ite c 1 0) is internalized, it will force the internalization of (= (ite c 1 0) 1) and (= (ite c 1 0) 0) // // TODO: avoid the problem by delaying the internalization of (= (ite c 1 0) 1) and (= (ite c 1 0) 0). // Add them to a queue. if (!b_internalized(n)) { is_new_var = true; v = mk_bool_var(n); } else { v = get_bool_var(n); } // a formula needs to be associated with an enode when: // 1) it is not in the context of a gate, or // 2) it has arguments and it is not a gate (i.e., uninterpreted predicate or equality). if (!e_internalized(n) && (!gate_ctx || (!_is_gate && n->get_num_args() > 0))) { bool suppress_args = _is_gate || m.is_not(n); bool merge_tf = !gate_ctx; mk_enode(n, suppress_args, merge_tf, true); set_enode_flag(v, is_new_var); SASSERT(has_enode(v)); } // The constraints associated with node 'n' should be asserted // after the bool_var and enode associated with are created. // Reason: incompleteness. An assigned boolean variable is only inserted // in m_atom_propagation_queue if the predicate is_atom() is true. // When the constraints for n are created, they may force v to be assigned. // Now, if v is assigned before being associated with an enode, then // v is not going to be inserted in m_atom_propagation_queue, and // propagate_bool_var_enode() method is not going to be invoked for v. if (is_new_var && n->get_family_id() == m.get_basic_family_id()) { switch (n->get_decl_kind()) { case OP_NOT: SASSERT(!gate_ctx); mk_not_cnstr(to_app(n)); break; case OP_AND: mk_and_cnstr(to_app(n)); add_and_rel_watches(to_app(n)); break; case OP_OR: mk_or_cnstr(to_app(n)); add_or_rel_watches(to_app(n)); break; case OP_EQ: if (m.is_iff(n)) mk_iff_cnstr(to_app(n)); break; case OP_ITE: mk_ite_cnstr(to_app(n)); add_ite_rel_watches(to_app(n)); break; case OP_TRUE: case OP_FALSE: break; case OP_DISTINCT: case OP_IMPLIES: case OP_XOR: UNREACHABLE(); case OP_OEQ: UNREACHABLE(); default: break; } } CTRACE("internalize_bug", e_internalized(n), tout << "#" << n->get_id() << ", merge_tf: " << get_enode(n)->merge_tf() << "\n";); } /** \brief Trail object to disable the m_merge_tf flag of an enode. */ class set_merge_tf_trail : public trail { enode * m_node; public: set_merge_tf_trail(enode * n): m_node(n) { } void undo(context & ctx) override { m_node->m_merge_tf = false; } }; /** \brief Enable the flag m_merge_tf in the given enode. When the flag m_merge_tf is enabled, the enode n will be merged with the true_enode (false_enode) whenever the Boolean variable v is assigned to true (false). If is_new_var is true, then trail is not created for the flag update. */ void context::set_merge_tf(enode * n, bool_var v, bool is_new_var) { SASSERT(bool_var2enode(v) == n); if (!n->m_merge_tf) { if (!is_new_var) push_trail(set_merge_tf_trail(n)); n->m_merge_tf = true; lbool val = get_assignment(v); switch (val) { case l_undef: break; case l_true: if (n->get_root() != m_true_enode->get_root()) push_eq(n, m_true_enode, eq_justification(literal(v, false))); break; case l_false: if (n->get_root() != m_false_enode->get_root()) push_eq(n, m_false_enode, eq_justification(literal(v, true))); break; } } } /** \brief Trail object to disable the m_enode flag of a Boolean variable. The flag m_enode is true for a Boolean variable v, if there is an enode n associated with it. */ class set_enode_flag_trail : public trail { bool_var m_var; public: set_enode_flag_trail(bool_var v): m_var(v) { } void undo(context & ctx) override { bool_var_data & data = ctx.m_bdata[m_var]; data.reset_enode_flag(); } }; /** \brief Enable the flag m_enode in the given boolean variable. That is, the boolean variable is associated with an enode. If is_new_var is true, then trail is not created for the flag uodate. */ void context::set_enode_flag(bool_var v, bool is_new_var) { SASSERT(e_internalized(bool_var2expr(v))); bool_var_data & data = m_bdata[v]; if (!data.is_enode()) { if (!is_new_var) push_trail(set_enode_flag_trail(v)); data.set_enode_flag(); } } /** \brief Internalize the given term into the logical context. */ void context::internalize_term(app * n) { if (e_internalized(n)) { theory * th = m_theories.get_plugin(n->get_family_id()); if (th != nullptr) { // This code is necessary because some theories may decide // not to create theory variables for a nested application. // Example: // Suppose (+ (* 2 x) y) is internalized by arithmetic // and an enode is created for the + and * applications, // but a theory variable is only created for the + application. // The (* 2 x) is internal to the arithmetic module. // Later, the core tries to internalize (f (* 2 x)). // Now, (* 2 x) is not internal to arithmetic anymore, // and a theory variable must be created for it. enode * e = get_enode(n); if (!th->is_attached_to_var(e)) internalize_theory_term(n); } return; } if (m.is_term_ite(n)) { internalize_ite_term(n); return; // it is not necessary to apply sort constraint } else if (internalize_theory_term(n)) { // skip } else { internalize_uninterpreted(n); } SASSERT(e_internalized(n)); enode * e = get_enode(n); apply_sort_cnstr(n, e); } /** \brief Internalize an if-then-else term. */ void context::internalize_ite_term(app * n) { SASSERT(!e_internalized(n)); expr * c = n->get_arg(0); expr * t = n->get_arg(1); expr * e = n->get_arg(2); app_ref eq1(mk_eq_atom(n, t), m); app_ref eq2(mk_eq_atom(n, e), m); mk_enode(n, true /* suppress arguments, I don't want to apply CC on ite terms */, false /* it is a term, so it should not be merged with true/false */, false /* CC is not enabled */); internalize(c, true); internalize(t, false); internalize(e, false); internalize(eq1, true); internalize(eq2, true); literal c_lit = get_literal(c); literal eq1_lit = get_literal(eq1); literal eq2_lit = get_literal(eq2); TRACE("internalize_ite_term_bug", tout << mk_ismt2_pp(n, m) << "\n"; tout << mk_ismt2_pp(c, m) << "\n"; tout << mk_ismt2_pp(t, m) << "\n"; tout << mk_ismt2_pp(e, m) << "\n"; tout << mk_ismt2_pp(eq1, m) << "\n"; tout << mk_ismt2_pp(eq2, m) << "\n"; tout << "literals:\n" << c_lit << " " << eq1_lit << " " << eq2_lit << "\n";); mk_gate_clause(~c_lit, eq1_lit); mk_gate_clause( c_lit, eq2_lit); if (relevancy()) { relevancy_eh * eh = m_relevancy_propagator->mk_term_ite_relevancy_eh(n, eq1, eq2); TRACE("ite_term_relevancy", tout << "#" << n->get_id() << " #" << eq1->get_id() << " #" << eq2->get_id() << "\n";); add_rel_watch(c_lit, eh); add_rel_watch(~c_lit, eh); add_relevancy_eh(n, eh); } SASSERT(e_internalized(n)); } /** \brief Try to internalize a theory term. That is, a theory (plugin) will be invoked to internalize n. Return true if succeeded. It may fail because there is no plugin or the plugin does not support it. */ bool context::internalize_theory_term(app * n) { theory * th = m_theories.get_plugin(n->get_family_id()); if (!th || !th->internalize_term(n)) return false; return true; } /** \brief Internalize an uninterpreted function application or constant. */ void context::internalize_uninterpreted(app * n) { SASSERT(!e_internalized(n)); // process args for (expr * arg : *n) { internalize(arg, false); SASSERT(e_internalized(arg)); } enode * e = mk_enode(n, false, /* do not suppress args */ false, /* it is a term, so it should not be merged with true/false */ true); apply_sort_cnstr(n, e); } /** \brief Create a new boolean variable and associate it with n. */ bool_var context::mk_bool_var(expr * n) { SASSERT(!b_internalized(n)); //SASSERT(!m.is_not(n)); unsigned id = n->get_id(); bool_var v = m_b_internalized_stack.size(); #ifndef _EXTERNAL_RELEASE if (m_fparams.m_display_bool_var2expr) { char const * header = "(iff z3@"; int id_sz = 6; std::cerr.width(id_sz); std::cerr << header << std::left << v << " " << mk_pp(n, m, static_cast(strlen(header)) + id_sz + 1) << ")\n"; } if (m_fparams.m_display_ll_bool_var2expr) { std::cerr << v << " ::=\n" << mk_ll_pp(n, m) << "\n"; } #endif TRACE("mk_bool_var", tout << "creating boolean variable: " << v << " for:\n" << mk_pp(n, m) << " " << n->get_id() << "\n";); TRACE("mk_var_bug", tout << "mk_bool: " << v << "\n";); set_bool_var(id, v); m_bdata.reserve(v+1); m_activity.reserve(v+1); m_bool_var2expr.reserve(v+1); m_bool_var2expr[v] = n; literal l(v, false); literal not_l(v, true); unsigned aux = std::max(l.index(), not_l.index()) + 1; m_assignment.reserve(aux); m_assignment[l.index()] = l_undef; m_assignment[not_l.index()] = l_undef; m_watches.reserve(aux); SASSERT(m_assignment.size() == m_watches.size()); m_watches[l.index()] .reset(); m_watches[not_l.index()] .reset(); if (lit_occs_enabled()) { m_lit_occs.reserve(aux); m_lit_occs[l.index()] .reset(); m_lit_occs[not_l.index()] .reset(); } bool_var_data & data = m_bdata[v]; unsigned iscope_lvl = m_scope_lvl; // record when the boolean variable was internalized. data.init(iscope_lvl); if (m_fparams.m_random_initial_activity == IA_RANDOM || (m_fparams.m_random_initial_activity == IA_RANDOM_WHEN_SEARCHING && m_searching)) m_activity[v] = -((m_random() % 1000) / 1000.0); else m_activity[v] = 0.0; m_case_split_queue->mk_var_eh(v); m_b_internalized_stack.push_back(n); m_trail_stack.push_back(&m_mk_bool_var_trail); m_stats.m_num_mk_bool_var++; SASSERT(check_bool_var_vector_sizes()); return v; } void context::undo_mk_bool_var() { SASSERT(!m_b_internalized_stack.empty()); m_stats.m_num_del_bool_var++; expr * n = m_b_internalized_stack.back(); unsigned n_id = n->get_id(); bool_var v = get_bool_var_of_id(n_id); m_bool_var2expr[v] = nullptr; TRACE("undo_mk_bool_var", tout << "undo_bool: " << v << "\n" << mk_pp(n, m) << "\n" << "m_bdata.size: " << m_bdata.size() << " m_assignment.size: " << m_assignment.size() << "\n";); TRACE("mk_var_bug", tout << "undo_mk_bool: " << v << "\n";); // bool_var_data & d = m_bdata[v]; m_case_split_queue->del_var_eh(v); if (is_quantifier(n)) m_qmanager->del(to_quantifier(n)); set_bool_var(n_id, null_bool_var); m_b_internalized_stack.pop_back(); } /** \brief Create an new enode. \remark If suppress_args is true, then the enode is viewed as a constant in the egraph. */ enode * context::mk_enode(app * n, bool suppress_args, bool merge_tf, bool cgc_enabled) { TRACE("mk_enode_detail", tout << mk_pp(n, m) << "\nsuppress_args: " << suppress_args << ", merge_tf: " << merge_tf << ", cgc_enabled: " << cgc_enabled << "\n";); SASSERT(!e_internalized(n)); unsigned id = n->get_id(); unsigned generation = m_generation; unsigned _generation = 0; if (!m_cached_generation.empty() && m_cached_generation.find(n, _generation)) { generation = _generation; CTRACE("cached_generation", generation != m_generation, tout << "cached_generation: #" << n->get_id() << " " << generation << " " << m_generation << "\n";); } enode * e = enode::mk(m, m_region, m_app2enode, n, generation, suppress_args, merge_tf, m_scope_lvl, cgc_enabled, true); TRACE("mk_enode_detail", tout << "e.get_num_args() = " << e->get_num_args() << "\n";); if (n->get_num_args() == 0 && m.is_unique_value(n)) e->mark_as_interpreted(); TRACE("mk_var_bug", tout << "mk_enode: " << id << "\n";); TRACE("generation", tout << "mk_enode: " << id << " " << generation << "\n";); m_app2enode.setx(id, e, nullptr); m_e_internalized_stack.push_back(n); m_trail_stack.push_back(&m_mk_enode_trail); m_enodes.push_back(e); if (e->get_num_args() > 0) { if (e->is_true_eq()) { bool_var v = enode2bool_var(e); assign(literal(v), mk_justification(eq_propagation_justification(e->get_arg(0), e->get_arg(1)))); e->m_cg = e; } else { if (cgc_enabled) { enode_bool_pair pair = m_cg_table.insert(e); enode * e_prime = pair.first; if (e != e_prime) { e->m_cg = e_prime; bool used_commutativity = pair.second; push_new_congruence(e, e_prime, used_commutativity); } else { e->m_cg = e; } } else { e->m_cg = e; } } if (!e->is_eq()) { unsigned decl_id = n->get_decl()->get_decl_id(); if (decl_id >= m_decl2enodes.size()) m_decl2enodes.resize(decl_id+1); m_decl2enodes[decl_id].push_back(e); } } SASSERT(e_internalized(n)); m_stats.m_num_mk_enode++; TRACE("mk_enode", tout << "created enode: #" << e->get_owner_id() << " for:\n" << mk_pp(n, m) << "\n"; if (e->get_num_args() > 0) { tout << "is_true_eq: " << e->is_true_eq() << " in cg_table: " << m_cg_table.contains_ptr(e) << " is_cgr: " << e->is_cgr() << "\n"; }); if (m.has_trace_stream()) m.trace_stream() << "[attach-enode] #" << n->get_id() << " " << m_generation << "\n"; return e; } void context::undo_mk_enode() { SASSERT(!m_e_internalized_stack.empty()); m_stats.m_num_del_enode++; expr * n = m_e_internalized_stack.back(); TRACE("undo_mk_enode", tout << "undo_enode: #" << n->get_id() << "\n" << mk_pp(n, m) << "\n";); TRACE("mk_var_bug", tout << "undo_mk_enode: " << n->get_id() << "\n";); unsigned n_id = n->get_id(); SASSERT(is_app(n)); enode * e = m_app2enode[n_id]; m_app2enode[n_id] = 0; if (e->is_cgr() && !e->is_true_eq() && e->is_cgc_enabled()) { SASSERT(m_cg_table.contains_ptr(e)); m_cg_table.erase(e); } if (e->get_num_args() > 0 && !e->is_eq()) { unsigned decl_id = to_app(n)->get_decl()->get_decl_id(); SASSERT(decl_id < m_decl2enodes.size()); SASSERT(m_decl2enodes[decl_id].back() == e); m_decl2enodes[decl_id].pop_back(); } e->del_eh(m); SASSERT(m_e_internalized_stack.size() == m_enodes.size()); m_enodes.pop_back(); m_e_internalized_stack.pop_back(); } /** \brief Apply sort constraints on e. */ void context::apply_sort_cnstr(app * term, enode * e) { sort * s = term->get_decl()->get_range(); theory * th = m_theories.get_plugin(s->get_family_id()); if (th) { th->apply_sort_cnstr(e, s); } } /** \brief Return the literal associated with n. */ literal context::get_literal(expr * n) const { if (m.is_not(n)) { CTRACE("get_literal_bug", !b_internalized(to_app(n)->get_arg(0)), tout << mk_ll_pp(n, m) << "\n";); SASSERT(b_internalized(to_app(n)->get_arg(0))); return literal(get_bool_var(to_app(n)->get_arg(0)), true); } else if (m.is_true(n)) { return true_literal; } else if (m.is_false(n)) { return false_literal; } else { SASSERT(b_internalized(n)); return literal(get_bool_var(n), false); } } /** \brief Simplify the literals of an auxiliary clause. An auxiliary clause is transient. So, the current assignment can be used for simplification. The following simplifications are applied: - Duplicates are removed. - Literals assigned to false are removed - If l and ~l are in lits, then return false (the clause is equivalent to true) - If a literal in source is assigned to true, then return false. \remark The removed literals are stored in simp_lits It is safe to use the current assignment to simplify aux clauses because they are deleted during backtracking. */ bool context::simplify_aux_clause_literals(unsigned & num_lits, literal * lits, literal_buffer & simp_lits) { std::sort(lits, lits + num_lits); literal prev = null_literal; unsigned j = 0; for (unsigned i = 0; i < num_lits; i++) { literal curr = lits[i]; lbool val = get_assignment(curr); switch(val) { case l_false: TRACE("simplify_aux_clause_literals", display_literal(tout << get_assign_level(curr) << " " << get_scope_level() << " ", curr); tout << "\n"; ); simp_lits.push_back(~curr); break; // ignore literal // fall through case l_undef: if (curr == ~prev) return false; // clause is equivalent to true if (curr != prev) { prev = curr; if (i != j) lits[j] = lits[i]; j++; } break; case l_true: return false; // clause is equivalent to true } } num_lits = j; return true; } /** \brief Simplify the literals of an auxiliary lemma. An auxiliary lemma has the status of a learned clause, but it is not created by conflict resolution. A dynamic ackermann clause is an example of auxiliary lemma. The following simplifications are applied: - Duplicates are removed. - If a literal is assigned to true at a base level, then return false (the clause is equivalent to true). - If l and ~l are in lits, then return false (source is irrelevant, that is, it is equivalent to true) \remark Literals assigned to false at the base level are not removed because I don't want to create a justification for this kind of simplification. */ bool context::simplify_aux_lemma_literals(unsigned & num_lits, literal * lits) { TRACE("simplify_aux_lemma_literals", tout << "1) "; display_literals(tout, num_lits, lits); tout << "\n";); std::sort(lits, lits + num_lits); TRACE("simplify_aux_lemma_literals", tout << "2) "; display_literals(tout, num_lits, lits); tout << "\n";); literal prev = null_literal; unsigned i = 0; unsigned j = 0; for (; i < num_lits; i++) { literal curr = lits[i]; bool_var var = curr.var(); lbool val = l_undef; if (get_assign_level(var) <= m_base_lvl) val = get_assignment(curr); if (val == l_true) return false; // clause is equivalent to true if (curr == ~prev) return false; // clause is equivalent to true if (curr != prev) { prev = curr; if (i != j) lits[j] = lits[i]; j++; } } num_lits = j; TRACE("simplify_aux_lemma_literals", tout << "3) "; display_literals(tout, num_lits, lits); tout << "\n";); return true; } /** \brief A clause (lemma or aux lemma) may need to be reinitialized for two reasons: 1) Lemmas and aux lemmas may contain literals that were created during the search, and the maximum internalization scope level of its literals is scope_lvl. Since the clauses may remain alive when scope_lvl is backtracked, it must be reinitialised. In this case, reinitialize_atoms must be true. 2) An aux lemma is in conflict or propagated a literal when it was created. Then, we should check whether the aux lemma is still in conflict or propagating a literal after backtracking the current scope level. */ void context::mark_for_reinit(clause * cls, unsigned scope_lvl, bool reinternalize_atoms) { SASSERT(scope_lvl >= m_base_lvl); cls->m_reinit = true; cls->m_reinternalize_atoms = reinternalize_atoms; if (scope_lvl >= m_clauses_to_reinit.size()) m_clauses_to_reinit.resize(scope_lvl+1, clause_vector()); m_clauses_to_reinit[scope_lvl].push_back(cls); } /** \brief Return max({ get_intern_level(var) | var \in lits }) */ unsigned context::get_max_iscope_lvl(unsigned num_lits, literal const * lits) const { unsigned r = 0; for (unsigned i = 0; i < num_lits; i++) { unsigned ilvl = get_intern_level(lits[i].var()); if (ilvl > r) r = ilvl; } return r; } /** \brief Return true if it safe to use the binary clause optimization at this point in time. */ bool context::use_binary_clause_opt(literal l1, literal l2, bool lemma) const { if (!binary_clause_opt_enabled()) return false; // When relevancy is enable binary clauses should not be used. // Reason: when a learned clause becomes unit, it should mark the // unit literal as relevant. When binary_clause_opt is used, // it is not possible to distinguish between learned and non-learned clauses. if (lemma && m_fparams.m_relevancy_lvl >= 2) return false; if (m_base_lvl > 0) return false; if (!lemma && m_scope_lvl > 0) return false; if (get_intern_level(l1.var()) > 0) return false; if (get_intern_level(l2.var()) > 0) return false; return true; } /** \brief The learned clauses (lemmas) produced by the SAT solver have the property that the first literal will be implied by it after backtracking. All other literals are assigned to (or implied to be) false when the learned clause is created. The first watch literal will always be the first literal. The second watch literal is computed by this method. It should be the literal with the highest decision level. If a literal is not assigned, it means it was re-initialized after backtracking. So, its level is assumed to be m_scope_lvl. */ int context::select_learned_watch_lit(clause const * cls) const { SASSERT(cls->get_num_literals() >= 2); int max_false_idx = -1; unsigned max_lvl = UINT_MAX; int num_lits = cls->get_num_literals(); for (int i = 1; i < num_lits; i++) { literal l = cls->get_literal(i); lbool val = get_assignment(l); SASSERT(val == l_false || val == l_undef); unsigned lvl = val == l_false ? get_assign_level(l) : m_scope_lvl; if (max_false_idx == -1 || lvl > max_lvl) { max_false_idx = i; max_lvl = lvl; } } return max_false_idx; } /** \brief Select a watch literal from a set of literals which is different from the literal in position other_watch_lit. I use the following rules to select a watch literal. 1- select a literal l in idx >= starting_at such that get_assignment(l) = l_true, and for all l' in idx' >= starting_at . get_assignment(l') = l_true implies get_level(l) <= get_level(l') The purpose of this rule is to make the clause inactive for as long as possible. A clause is inactive when it contains a literal assigned to true. 2- if there isn't a literal assigned to true, then select an unassigned literal l is in idx >= starting_at 3- if there isn't a literal l in idx >= starting_at such that get_assignment(l) = l_true or get_assignment(l) = l_undef (that is, all literals different from other_watch_lit are assigned to false), then peek the literal l different starting at starting_at such that for all l' starting at starting_at get_level(l) >= get_level(l') Without rule 3, boolean propagation is incomplete, that is, it may miss possible propagations. \remark The method select_lemma_watch_lit is used to select the watch literal for regular learned clauses. */ int context::select_watch_lit(clause const * cls, int starting_at) const { SASSERT(cls->get_num_literals() >= 2); int min_true_idx = -1; int max_false_idx = -1; int unknown_idx = -1; int n = cls->get_num_literals(); for (int i = starting_at; i < n; i++) { literal l = cls->get_literal(i); switch(get_assignment(l)) { case l_false: if (max_false_idx == -1 || get_assign_level(l.var()) > get_assign_level(cls->get_literal(max_false_idx).var())) max_false_idx = i; break; case l_undef: unknown_idx = i; break; case l_true: if (min_true_idx == -1 || get_assign_level(l.var()) < get_assign_level(cls->get_literal(min_true_idx).var())) min_true_idx = i; break; } } if (min_true_idx != -1) return min_true_idx; if (unknown_idx != -1) return unknown_idx; SASSERT(max_false_idx != -1); return max_false_idx; } /** \brief Add watch literal to the given clause. \pre idx must be 0 or 1. */ void context::add_watch_literal(clause * cls, unsigned idx) { SASSERT(idx == 0 || idx == 1); literal l = cls->get_literal(idx); unsigned l_idx = (~l).index(); watch_list & wl = const_cast(m_watches[l_idx]); wl.insert_clause(cls); CASSERT("watch_list", check_watch_list(l_idx)); } /** \brief Create a new clause using the given literals, justification, kind and deletion event handler. The deletion event handler is ignored if binary clause optimization is applicable. */ clause * context::mk_clause(unsigned num_lits, literal * lits, justification * j, clause_kind k, clause_del_eh * del_eh) { TRACE("mk_clause", display_literals_verbose(tout << "creating clause: " << literal_vector(num_lits, lits) << "\n", num_lits, lits) << "\n";); m_clause_proof.add(num_lits, lits, k, j); switch (k) { case CLS_AUX: case CLS_TH_AXIOM: { literal_buffer simp_lits; if (!simplify_aux_clause_literals(num_lits, lits, simp_lits)) return nullptr; // clause is equivalent to true; DEBUG_CODE({ for (unsigned i = 0; i < simp_lits.size(); i++) { SASSERT(get_assignment(simp_lits[i]) == l_true); } }); if (!simp_lits.empty()) { j = mk_justification(unit_resolution_justification(m_region, j, simp_lits.size(), simp_lits.c_ptr())); } break; } case CLS_TH_LEMMA: { if (!simplify_aux_lemma_literals(num_lits, lits)) return nullptr; // clause is equivalent to true // simplify_aux_lemma_literals does not delete literals assigned to false, so // it is not necessary to create a unit_resolution_justification break; } default: break; } TRACE("mk_clause", tout << "after simplification:\n"; display_literals_verbose(tout, num_lits, lits) << "\n";); unsigned activity = 0; if (activity == 0) activity = 1; bool lemma = is_lemma(k); m_stats.m_num_mk_lits += num_lits; switch (num_lits) { case 0: if (j && !j->in_region()) m_justifications.push_back(j); TRACE("mk_clause", tout << "empty clause... setting conflict\n";); set_conflict(j == nullptr ? b_justification::mk_axiom() : b_justification(j)); SASSERT(inconsistent()); return nullptr; case 1: if (j && !j->in_region()) m_justifications.push_back(j); assign(lits[0], j); return nullptr; case 2: if (use_binary_clause_opt(lits[0], lits[1], lemma)) { literal l1 = lits[0]; literal l2 = lits[1]; m_watches[(~l1).index()].insert_literal(l2); m_watches[(~l2).index()].insert_literal(l1); if (get_assignment(l2) == l_false) { assign(l1, b_justification(~l2)); } m_clause_proof.add(l1, l2, k, j); m_stats.m_num_mk_bin_clause++; return nullptr; } default: { m_stats.m_num_mk_clause++; unsigned iscope_lvl = lemma ? get_max_iscope_lvl(num_lits, lits) : 0; SASSERT(m_scope_lvl >= iscope_lvl); bool save_atoms = lemma && iscope_lvl > m_base_lvl; bool reinit = save_atoms; SASSERT(!lemma || j == 0 || !j->in_region()); clause * cls = clause::mk(m, num_lits, lits, k, j, del_eh, save_atoms, m_bool_var2expr.c_ptr()); m_clause_proof.add(*cls); if (lemma) { cls->set_activity(activity); if (k == CLS_LEARNED) { int w2_idx = select_learned_watch_lit(cls); cls->swap_lits(1, w2_idx); } else { SASSERT(k == CLS_TH_LEMMA); int w1_idx = select_watch_lit(cls, 0); cls->swap_lits(0, w1_idx); int w2_idx = select_watch_lit(cls, 1); cls->swap_lits(1, w2_idx); TRACE("mk_th_lemma", display_clause(tout, cls); tout << "\n";); } // display_clause(std::cout, cls); std::cout << "\n"; m_lemmas.push_back(cls); add_watch_literal(cls, 0); add_watch_literal(cls, 1); if (get_assignment(cls->get_literal(0)) == l_false) { set_conflict(b_justification(cls)); if (k == CLS_TH_LEMMA && m_scope_lvl > m_base_lvl) { reinit = true; iscope_lvl = m_scope_lvl; } } else if (get_assignment(cls->get_literal(1)) == l_false) { assign(cls->get_literal(0), b_justification(cls)); if (k == CLS_TH_LEMMA && m_scope_lvl > m_base_lvl) { reinit = true; iscope_lvl = m_scope_lvl; } } if (reinit) mark_for_reinit(cls, iscope_lvl, save_atoms); } else { m_aux_clauses.push_back(cls); add_watch_literal(cls, 0); add_watch_literal(cls, 1); if (get_assignment(cls->get_literal(0)) == l_false) set_conflict(b_justification(cls)); else if (get_assignment(cls->get_literal(1)) == l_false) assign(cls->get_literal(0), b_justification(cls)); } if (lit_occs_enabled()) add_lit_occs(cls); TRACE("add_watch_literal_bug", display_clause_detail(tout, cls);); TRACE("mk_clause_result", display_clause_detail(tout, cls);); CASSERT("mk_clause", check_clause(cls)); return cls; }} } void context::add_lit_occs(clause * cls) { for (literal l : *cls) { m_lit_occs[l.index()].insert(cls); } } void context::mk_clause(literal l1, literal l2, justification * j) { literal ls[2] = { l1, l2 }; mk_clause(2, ls, j); } void context::mk_clause(literal l1, literal l2, literal l3, justification * j) { literal ls[3] = { l1, l2, l3 }; mk_clause(3, ls, j); } void context::mk_th_axiom(theory_id tid, unsigned num_lits, literal * lits, unsigned num_params, parameter * params) { justification * js = nullptr; TRACE("mk_th_axiom", display_literals_verbose(tout, num_lits, lits) << "\n";); if (m.proofs_enabled()) { js = mk_justification(theory_axiom_justification(tid, m_region, num_lits, lits, num_params, params)); } if (m_fparams.m_smtlib_dump_lemmas) { literal_buffer tmp; neg_literals(num_lits, lits, tmp); SASSERT(tmp.size() == num_lits); display_lemma_as_smt_problem(tmp.size(), tmp.c_ptr(), false_literal, m_fparams.m_logic); } mk_clause(num_lits, lits, js, CLS_TH_AXIOM); } void context::mk_th_axiom(theory_id tid, literal l1, literal l2, unsigned num_params, parameter * params) { literal ls[2] = { l1, l2 }; mk_th_axiom(tid, 2, ls, num_params, params); } void context::mk_th_axiom(theory_id tid, literal l1, literal l2, literal l3, unsigned num_params, parameter * params) { literal ls[3] = { l1, l2, l3 }; mk_th_axiom(tid, 3, ls, num_params, params); } proof * context::mk_clause_def_axiom(unsigned num_lits, literal * lits, expr * root_gate) { ptr_buffer new_lits; for (unsigned i = 0; i < num_lits; i++) { literal l = lits[i]; bool_var v = l.var(); expr * atom = m_bool_var2expr[v]; new_lits.push_back(l.sign() ? m.mk_not(atom) : atom); } if (root_gate) new_lits.push_back(m.mk_not(root_gate)); SASSERT(num_lits > 1); expr * fact = m.mk_or(new_lits.size(), new_lits.c_ptr()); return m.mk_def_axiom(fact); } void context::mk_gate_clause(unsigned num_lits, literal * lits) { if (m.proofs_enabled()) { proof * pr = mk_clause_def_axiom(num_lits, lits, nullptr); TRACE("gate_clause", tout << mk_ll_pp(pr, m);); mk_clause(num_lits, lits, mk_justification(justification_proof_wrapper(*this, pr))); } else { mk_clause(num_lits, lits, nullptr); } } void context::mk_gate_clause(literal l1, literal l2) { literal ls[2] = { l1, l2 }; mk_gate_clause(2, ls); } void context::mk_gate_clause(literal l1, literal l2, literal l3) { literal ls[3] = { l1, l2, l3 }; mk_gate_clause(3, ls); } void context::mk_gate_clause(literal l1, literal l2, literal l3, literal l4) { literal ls[4] = { l1, l2, l3, l4 }; mk_gate_clause(4, ls); } void context::mk_root_clause(unsigned num_lits, literal * lits, proof * pr) { if (m.proofs_enabled()) { SASSERT(m.get_fact(pr)); expr * fact = m.get_fact(pr); if (!m.is_or(fact)) { proof * def = mk_clause_def_axiom(num_lits, lits, m.get_fact(pr)); TRACE("gate_clause", tout << mk_ll_pp(def, m) << "\n"; tout << mk_ll_pp(pr, m);); proof * prs[2] = { def, pr }; pr = m.mk_unit_resolution(2, prs); } mk_clause(num_lits, lits, mk_justification(justification_proof_wrapper(*this, pr))); } else { mk_clause(num_lits, lits, nullptr); } } void context::mk_root_clause(literal l1, literal l2, proof * pr) { literal ls[2] = { l1, l2 }; mk_root_clause(2, ls, pr); } void context::mk_root_clause(literal l1, literal l2, literal l3, proof * pr) { literal ls[3] = { l1, l2, l3 }; mk_root_clause(3, ls, pr); } void context::add_and_rel_watches(app * n) { if (relevancy()) { relevancy_eh * eh = m_relevancy_propagator->mk_and_relevancy_eh(n); for (expr * arg : *n) { // if one child is assigned to false, the and-parent must be notified literal l = get_literal(arg); add_rel_watch(~l, eh); } } } void context::add_or_rel_watches(app * n) { if (relevancy()) { relevancy_eh * eh = m_relevancy_propagator->mk_or_relevancy_eh(n); for (expr * arg : *n) { // if one child is assigned to true, the or-parent must be notified literal l = get_literal(arg); add_rel_watch(l, eh); } } } void context::add_ite_rel_watches(app * n) { if (relevancy()) { relevancy_eh * eh = m_relevancy_propagator->mk_ite_relevancy_eh(n); literal l = get_literal(n->get_arg(0)); // when the condition of an ite is assigned to true or false, the ite-parent must be notified. TRACE("propagate_relevant_ite", tout << "#" << n->get_id() << ", eh: " << eh << "\n";); add_rel_watch(l, eh); add_rel_watch(~l, eh); } } void context::mk_not_cnstr(app * n) { SASSERT(b_internalized(n)); bool_var v = get_bool_var(n); literal l(v, false); literal c = get_literal(n->get_arg(0)); mk_gate_clause(~l, ~c); mk_gate_clause(l, c); } void context::mk_and_cnstr(app * n) { literal l = get_literal(n); TRACE("mk_and_cnstr", tout << "l: "; display_literal(tout, l); tout << "\n";); literal_buffer buffer; buffer.push_back(l); for (expr * arg : *n) { literal l_arg = get_literal(arg); TRACE("mk_and_cnstr", tout << "l_arg: "; display_literal(tout, l_arg); tout << "\n";); mk_gate_clause(~l, l_arg); buffer.push_back(~l_arg); } mk_gate_clause(buffer.size(), buffer.c_ptr()); } void context::mk_or_cnstr(app * n) { literal l = get_literal(n); literal_buffer buffer; buffer.push_back(~l); for (expr * arg : *n) { literal l_arg = get_literal(arg); mk_gate_clause(l, ~l_arg); buffer.push_back(l_arg); } mk_gate_clause(buffer.size(), buffer.c_ptr()); } void context::mk_iff_cnstr(app * n) { literal l = get_literal(n); literal l1 = get_literal(n->get_arg(0)); literal l2 = get_literal(n->get_arg(1)); TRACE("mk_iff_cnstr", tout << "l: " << l << ", l1: " << l1 << ", l2: " << l2 << "\n";); mk_gate_clause(~l, l1, ~l2); mk_gate_clause(~l, ~l1 , l2); mk_gate_clause( l, l1, l2); mk_gate_clause( l, ~l1, ~l2); } void context::mk_ite_cnstr(app * n) { literal l = get_literal(n); literal l1 = get_literal(n->get_arg(0)); literal l2 = get_literal(n->get_arg(1)); literal l3 = get_literal(n->get_arg(2)); mk_gate_clause(~l, ~l1, l2); mk_gate_clause(~l, l1, l3); mk_gate_clause(l, ~l1, ~l2); mk_gate_clause(l, l1, ~l3); } /** \brief Trail for add_th_var */ class add_th_var_trail : public trail { enode * m_enode; theory_id m_th_id; #ifdef Z3DEBUG theory_var m_th_var; #endif public: add_th_var_trail(enode * n, theory_id th_id): m_enode(n), m_th_id(th_id) { DEBUG_CODE(m_th_var = n->get_th_var(th_id);); SASSERT(m_th_var != null_theory_var); } void undo(context & ctx) override { theory_var v = m_enode->get_th_var(m_th_id); SASSERT(v != null_theory_var); SASSERT(m_th_var == v); m_enode->del_th_var(m_th_id); enode * root = m_enode->get_root(); if (root != m_enode && root->get_th_var(m_th_id) == v) root->del_th_var(m_th_id); } }; /** \brief Trail for replace_th_var */ class replace_th_var_trail : public trail { enode * m_enode; unsigned m_th_id:8; unsigned m_old_th_var:24; public: replace_th_var_trail(enode * n, theory_id th_id, theory_var old_var): m_enode(n), m_th_id(th_id), m_old_th_var(old_var) { } void undo(context & ctx) override { SASSERT(m_enode->get_th_var(m_th_id) != null_theory_var); m_enode->replace_th_var(m_old_th_var, m_th_id); } }; /** \brief Attach theory var v to the enode n. Enode n is to attached to any theory variable of th. This method should be invoked whenever the theory creates a new theory variable. \remark The methods new_eq_eh and new_diseq_eh of th may be invoked before this method returns. */ void context::attach_th_var(enode * n, theory * th, theory_var v) { SASSERT(!th->is_attached_to_var(n)); theory_id th_id = th->get_id(); theory_var old_v = n->get_th_var(th_id); if (old_v == null_theory_var) { enode * r = n->get_root(); theory_var v2 = r->get_th_var(th_id); n->add_th_var(v, th_id, m_region); push_trail(add_th_var_trail(n, th_id)); if (v2 == null_theory_var) { if (r != n) r->add_th_var(v, th_id, m_region); push_new_th_diseqs(r, v, th); } else if (r != n) { push_new_th_eq(th_id, v2, v); } } else { // Case) there is a variable old_v in the var-list of n. // // Remark: This variable was moved to the var-list of n due to a add_eq. SASSERT(th->get_enode(old_v) != n); // this varialbe is not owned by n SASSERT(n->get_root()->get_th_var(th_id) != null_theory_var); // the root has also a variable in its var-list. n->replace_th_var(v, th_id); push_trail(replace_th_var_trail(n, th_id, old_v)); push_new_th_eq(th_id, v, old_v); } SASSERT(th->is_attached_to_var(n)); } }; z3-z3-4.8.7/src/smt/smt_justification.cpp000066400000000000000000000361151356505360400202710ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_justification.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-02-25. Revision History: --*/ #include "smt/smt_context.h" #include "smt/smt_conflict_resolution.h" #include "ast/ast_pp.h" #include "ast/ast_ll_pp.h" namespace smt { justification_proof_wrapper::justification_proof_wrapper(context & ctx, proof * pr, bool in_region): justification(in_region), m_proof(pr) { ctx.get_manager().inc_ref(pr); } void justification_proof_wrapper::del_eh(ast_manager & m) { m.dec_ref(m_proof); } proof * justification_proof_wrapper::mk_proof(conflict_resolution & cr) { return m_proof; } unit_resolution_justification::unit_resolution_justification(region & r, justification * js, unsigned num_lits, literal const * lits): m_antecedent(js), m_num_literals(num_lits) { SASSERT(!js || js->in_region()); m_literals = new (r) literal[num_lits]; memcpy(m_literals, lits, sizeof(literal) * num_lits); TRACE("unit_resolution_justification_bug", for (unsigned i = 0; i < num_lits; i++) { tout << lits[i] << " "; } tout << "\n";); SASSERT(m_num_literals > 0); } unit_resolution_justification::unit_resolution_justification(justification * js, unsigned num_lits, literal const * lits): justification(false), // object is not allocated in a region m_antecedent(js), m_num_literals(num_lits) { SASSERT(!js || !js->in_region()); m_literals = alloc_vect(num_lits); memcpy(m_literals, lits, sizeof(literal) * num_lits); TRACE("unit_resolution_justification_bug", for (unsigned i = 0; i < num_lits; i++) { tout << lits[i] << " "; } tout << "\n";); SASSERT(num_lits != 0); } unit_resolution_justification::~unit_resolution_justification() { if (!in_region()) { dealloc_svect(m_literals); // I don't need to invoke destructor... dealloc(m_antecedent); } } void unit_resolution_justification::get_antecedents(conflict_resolution & cr) { if (m_antecedent) cr.mark_justification(m_antecedent); for (unsigned i = 0; i < m_num_literals; i++) cr.mark_literal(m_literals[i]); } proof * unit_resolution_justification::mk_proof(conflict_resolution & cr) { SASSERT(m_antecedent); ptr_buffer prs; proof * pr = cr.get_proof(m_antecedent); bool visited = pr != nullptr; prs.push_back(pr); for (unsigned i = 0; i < m_num_literals; i++) { proof * pr = cr.get_proof(m_literals[i]); if (pr == nullptr) visited = false; else prs.push_back(pr); } if (!visited) return nullptr; ast_manager & m = cr.get_manager(); TRACE("unit_resolution_justification_bug", tout << "in mk_proof\n"; for (unsigned i = 0; i < m_num_literals; i++) { tout << m_literals[i] << " "; } tout << "\n"; for (unsigned i = 0; i < prs.size(); i++) { tout << mk_ll_pp(m.get_fact(prs[i]), m); }); return m.mk_unit_resolution(prs.size(), prs.c_ptr()); } void eq_conflict_justification::get_antecedents(conflict_resolution & cr) { SASSERT(m_node1->get_root()->is_interpreted()); SASSERT(m_node2->get_root()->is_interpreted()); cr.mark_eq(m_node1, m_node1->get_root()); cr.mark_eq(m_node2, m_node2->get_root()); cr.mark_justified_eq(m_node1, m_node2, m_js); } proof * eq_conflict_justification::mk_proof(conflict_resolution & cr) { ast_manager & m = cr.get_manager(); bool visited = true; ptr_buffer prs; if (m_node1 != m_node1->get_root()) { proof * pr = cr.get_proof(m_node1, m_node1->get_root()); if (pr && m.proofs_enabled()) pr = m.mk_symmetry(pr); prs.push_back(pr); if (!pr) visited = false; } SASSERT(m_node1 != m_node2); proof * pr = cr.get_proof(m_node1, m_node2, m_js); prs.push_back(pr); if (!pr) visited = false; if (m_node2 != m_node2->get_root()) { proof * pr = cr.get_proof(m_node2, m_node2->get_root()); prs.push_back(pr); if (!pr) visited = false; } if (!visited) return nullptr; expr * lhs = m_node1->get_root()->get_owner(); expr * rhs = m_node2->get_root()->get_owner(); proof * pr1 = m.mk_transitivity(prs.size(), prs.c_ptr(), lhs, rhs); proof * pr2 = m.mk_rewrite(m.mk_eq(lhs, rhs), m.mk_false()); return m.mk_modus_ponens(pr1, pr2); } void eq_root_propagation_justification::get_antecedents(conflict_resolution & cr) { cr.mark_eq(m_node, m_node->get_root()); } proof * eq_root_propagation_justification::mk_proof(conflict_resolution & cr) { ast_manager & m = cr.get_manager(); expr * var = m_node->get_owner(); expr * val = m_node->get_root()->get_owner(); SASSERT(m.is_true(val) || m.is_false(val)); proof * pr1 = cr.get_proof(m_node, m_node->get_root()); if (pr1) { expr * lit; if (m.is_true(val)) lit = var; else lit = m.mk_not(var); proof * pr2 = m.mk_rewrite(m.get_fact(pr1), lit); return m.mk_modus_ponens(pr1, pr2); } return nullptr; } void eq_propagation_justification::get_antecedents(conflict_resolution & cr) { cr.mark_eq(m_node1, m_node2); } proof * eq_propagation_justification::mk_proof(conflict_resolution & cr) { return cr.get_proof(m_node1, m_node2); } void mp_iff_justification::get_antecedents(conflict_resolution & cr) { SASSERT(m_node1 != m_node2); cr.mark_eq(m_node1, m_node2); context & ctx = cr.get_context(); bool_var v = ctx.enode2bool_var(m_node1); lbool val = ctx.get_assignment(v); literal l(v, val == l_false); cr.mark_literal(l); } proof * mp_iff_justification::mk_proof(conflict_resolution & cr) { proof * pr1 = cr.get_proof(m_node1, m_node2); context & ctx = cr.get_context(); bool_var v = ctx.enode2bool_var(m_node1); lbool val = ctx.get_assignment(v); literal l(v, val == l_false); proof * pr2 = cr.get_proof(l); if (pr1 && pr2) { ast_manager & m = cr.get_manager(); proof * pr; SASSERT(m.has_fact(pr1)); SASSERT(m.has_fact(pr2)); app* fact1 = to_app(m.get_fact(pr1)); app* fact2 = to_app(m.get_fact(pr2)); SASSERT(m.is_iff(fact1)); if (fact1->get_arg(1) == fact2) { pr1 = m.mk_symmetry(pr1); fact1 = to_app(m.get_fact(pr1)); } SASSERT(m.is_iff(fact1)); if (l.sign()) { SASSERT(m.is_not(fact2)); expr* lhs = fact1->get_arg(0); expr* rhs = fact1->get_arg(1); if (lhs != fact2->get_arg(0)) { pr1 = m.mk_symmetry(pr1); fact1 = to_app(m.get_fact(pr1)); std::swap(lhs, rhs); } SASSERT(lhs == fact2->get_arg(0)); app* new_lhs = fact2; app* new_rhs = m.mk_not(rhs); pr1 = m.mk_congruence(new_lhs, new_rhs, 1, &pr1); } pr = m.mk_modus_ponens(pr2, pr1); TRACE("mp_iff_justification", tout << mk_pp(fact1, m) << "\n" << mk_pp(fact2, m) << "\n" << mk_pp(m.get_fact(pr), m) << "\n";); return pr; } return nullptr; } simple_justification::simple_justification(region & r, unsigned num_lits, literal const * lits): m_num_literals(num_lits) { if (num_lits != 0) { m_literals = new (r) literal[num_lits]; memcpy(m_literals, lits, sizeof(literal) * num_lits); #ifdef Z3DEBUG for (unsigned i = 0; i < num_lits; i++) { SASSERT(lits[i] != null_literal); } #endif } } void simple_justification::get_antecedents(conflict_resolution & cr) { for (unsigned i = 0; i < m_num_literals; i++) cr.mark_literal(m_literals[i]); } bool simple_justification::antecedent2proof(conflict_resolution & cr, ptr_buffer & result) { bool visited = true; for (unsigned i = 0; i < m_num_literals; i++) { proof * pr = cr.get_proof(m_literals[i]); if (pr == nullptr) visited = false; else result.push_back(pr); } return visited; } proof * theory_axiom_justification::mk_proof(conflict_resolution & cr) { context & ctx = cr.get_context(); ast_manager & m = cr.get_manager(); expr_ref_vector lits(m); for (unsigned i = 0; i < m_num_literals; i++) { expr_ref l(m); ctx.literal2expr(m_literals[i], l); lits.push_back(std::move(l)); } if (lits.size() == 1) return m.mk_th_lemma(m_th_id, lits.get(0), 0, nullptr, m_params.size(), m_params.c_ptr()); else return m.mk_th_lemma(m_th_id, m.mk_or(lits.size(), lits.c_ptr()), 0, nullptr, m_params.size(), m_params.c_ptr()); } proof * theory_propagation_justification::mk_proof(conflict_resolution & cr) { ptr_buffer prs; if (!antecedent2proof(cr, prs)) return nullptr; context & ctx = cr.get_context(); ast_manager & m = cr.get_manager(); expr_ref fact(m); ctx.literal2expr(m_consequent, fact); return m.mk_th_lemma(m_th_id, fact, prs.size(), prs.c_ptr(), m_params.size(), m_params.c_ptr()); } proof * theory_conflict_justification::mk_proof(conflict_resolution & cr) { ptr_buffer prs; if (!antecedent2proof(cr, prs)) return nullptr; ast_manager & m = cr.get_manager(); return m.mk_th_lemma(m_th_id, m.mk_false(), prs.size(), prs.c_ptr(), m_params.size(), m_params.c_ptr()); } ext_simple_justification::ext_simple_justification(region & r, unsigned num_lits, literal const * lits, unsigned num_eqs, enode_pair const * eqs): simple_justification(r, num_lits, lits), m_num_eqs(num_eqs) { m_eqs = new (r) enode_pair[num_eqs]; if (num_eqs != 0) memcpy(m_eqs, eqs, sizeof(enode_pair) * num_eqs); DEBUG_CODE({ for (unsigned i = 0; i < num_eqs; i++) { SASSERT(eqs[i].first->get_root() == eqs[i].second->get_root()); } }); } void ext_simple_justification::get_antecedents(conflict_resolution & cr) { simple_justification::get_antecedents(cr); for (unsigned i = 0; i < m_num_eqs; i++) { enode_pair const & p = m_eqs[i]; cr.mark_eq(p.first, p.second); } } bool ext_simple_justification::antecedent2proof(conflict_resolution & cr, ptr_buffer & result) { bool visited = simple_justification::antecedent2proof(cr, result); for (unsigned i = 0; i < m_num_eqs; i++) { enode_pair const & p = m_eqs[i]; proof * pr = cr.get_proof(p.first, p.second); if (pr == nullptr) visited = false; else result.push_back(pr); } return visited; } proof * ext_theory_propagation_justification::mk_proof(conflict_resolution & cr) { ptr_buffer prs; if (!antecedent2proof(cr, prs)) return nullptr; context & ctx = cr.get_context(); ast_manager & m = cr.get_manager(); expr_ref fact(m); ctx.literal2expr(m_consequent, fact); return m.mk_th_lemma(m_th_id, fact, prs.size(), prs.c_ptr(), m_params.size(), m_params.c_ptr()); } proof * ext_theory_conflict_justification::mk_proof(conflict_resolution & cr) { ptr_buffer prs; if (!antecedent2proof(cr, prs)) return nullptr; ast_manager & m = cr.get_manager(); return m.mk_th_lemma(m_th_id, m.mk_false(), prs.size(), prs.c_ptr(), m_params.size(), m_params.c_ptr()); } proof * ext_theory_eq_propagation_justification::mk_proof(conflict_resolution & cr) { ptr_buffer prs; if (!antecedent2proof(cr, prs)) return nullptr; ast_manager & m = cr.get_manager(); context & ctx = cr.get_context(); expr * fact = ctx.mk_eq_atom(m_lhs->get_owner(), m_rhs->get_owner()); return m.mk_th_lemma(m_th_id, fact, prs.size(), prs.c_ptr(), m_params.size(), m_params.c_ptr()); } theory_lemma_justification::theory_lemma_justification(family_id fid, context & ctx, unsigned num_lits, literal const * lits, unsigned num_params, parameter* params): justification(false), m_th_id(fid), m_params(num_params, params), m_num_literals(num_lits) { ast_manager & m = ctx.get_manager(); m_literals = alloc_svect(expr*, num_lits); for (unsigned i = 0; i < num_lits; i++) { bool sign = lits[i].sign(); expr * v = ctx.bool_var2expr(lits[i].var()); m.inc_ref(v); m_literals[i] = TAG(expr*, v, sign); } SASSERT(!in_region()); } theory_lemma_justification::~theory_lemma_justification() { SASSERT(!in_region()); dealloc_svect(m_literals); } void theory_lemma_justification::del_eh(ast_manager & m) { for (unsigned i = 0; i < m_num_literals; i++) { m.dec_ref(UNTAG(expr*, m_literals[i])); } m_params.reset(); } proof * theory_lemma_justification::mk_proof(conflict_resolution & cr) { ast_manager & m = cr.get_manager(); expr_ref_vector lits(m); for (unsigned i = 0; i < m_num_literals; i++) { bool sign = GET_TAG(m_literals[i]) != 0; expr * v = UNTAG(expr*, m_literals[i]); lits.push_back(sign ? m.mk_not(v) : v); } if (lits.size() == 1) return m.mk_th_lemma(m_th_id, lits.get(0), 0, nullptr, m_params.size(), m_params.c_ptr()); else return m.mk_th_lemma(m_th_id, m.mk_or(lits.size(), lits.c_ptr()), 0, nullptr, m_params.size(), m_params.c_ptr()); } }; z3-z3-4.8.7/src/smt/smt_justification.h000066400000000000000000000351141356505360400177340ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_justification.h Abstract: Proof-like objects for tracking dependencies in the SMT engine, and generating proofs. Author: Leonardo de Moura (leonardo) 2008-02-18. Revision History: --*/ #ifndef SMT_JUSTIFICATION_H_ #define SMT_JUSTIFICATION_H_ #include "ast/ast.h" #include "smt/smt_types.h" #include "smt/smt_literal.h" #include "smt/smt_eq_justification.h" namespace smt { class conflict_resolution; typedef ptr_vector justification_vector; /** \brief Pseudo-proof objects. They are mainly used to track dependencies. When proof generation is enabled, they are also used to produce proofs. Justification objects are allocated using a stack based policy. Actually, there is one exception: justification of lemmas. Lemmas created at scope level n may remain alive even after scope level n is backtracked. Lemmas are deleted by a GC that runs from time to time. So, a justification attached to a lemma will may remain alive after scope level n is backtracked. So, I allow justification objects to be allocated in regions and in the regular heap. The method in_region() should return true if the object is allocated in a region. */ class justification { unsigned m_mark:1; unsigned m_in_region:1; // true if the object was allocated in a region. public: justification(bool in_region = true):m_mark(false), m_in_region(in_region) {} virtual ~justification() {} /** \brief This method should return true if the method del_eh needs to be invoked to free resources. */ virtual bool has_del_eh() const { return false; } /** \brief Free the resources allocated by this object. */ virtual void del_eh(ast_manager & m) { } /** \brief Mark the antecedents the justification object. The antecedents are marked using the mark methods of the conflict_resolution object. */ virtual void get_antecedents(conflict_resolution & cr){ } /** \brief Return the id of the theory that produced the proof object. */ virtual theory_id get_from_theory() const { return null_theory_id; } void set_mark() { SASSERT(!m_mark); m_mark = true; } void unset_mark() { SASSERT(m_mark); m_mark = false; } bool is_marked() const { return m_mark; } unsigned hash() const { return get_ptr_hash(this); } virtual proof * mk_proof(conflict_resolution & cr) = 0; bool in_region() const { return m_in_region; } virtual char const * get_name() const { return "unknown"; } virtual void display_debug_info(conflict_resolution & cr, std::ostream & out) { /* do nothing */ } }; class justification_proof_wrapper : public justification { proof * m_proof; public: justification_proof_wrapper(context & ctx, proof * pr, bool in_region = true); bool has_del_eh() const override { return true; } void del_eh(ast_manager & m) override; proof * mk_proof(conflict_resolution & cr) override; char const * get_name() const override { return "proof-wrapper"; } }; class unit_resolution_justification : public justification { justification * m_antecedent; unsigned m_num_literals; literal * m_literals; public: unit_resolution_justification(region & r, justification * js, unsigned num_lits, literal const * lits); unit_resolution_justification(justification * js, unsigned num_lits, literal const * lits); ~unit_resolution_justification() override; bool has_del_eh() const override { return !in_region() && m_antecedent && m_antecedent->has_del_eh(); } void del_eh(ast_manager & m) override { if (!in_region() && m_antecedent) m_antecedent->del_eh(m); } void get_antecedents(conflict_resolution & cr) override; proof * mk_proof(conflict_resolution & cr) override; char const * get_name() const override { return "unit-resolution"; } }; class eq_conflict_justification : public justification { enode * m_node1; enode * m_node2; eq_justification m_js; public: eq_conflict_justification(enode * n1, enode * n2, eq_justification js): m_node1(n1), m_node2(n2), m_js(js) { } void get_antecedents(conflict_resolution & cr) override; proof * mk_proof(conflict_resolution & cr) override; char const * get_name() const override { return "eq-conflict"; } }; /** \brief Justification for m_node = root */ class eq_root_propagation_justification : public justification { enode * m_node; public: eq_root_propagation_justification(enode * n):m_node(n) { } void get_antecedents(conflict_resolution & cr) override; proof * mk_proof(conflict_resolution & cr) override; char const * get_name() const override { return "eq-root"; } }; /** \brief Justification for m_node1 = m_node2 */ class eq_propagation_justification : public justification { enode * m_node1; enode * m_node2; public: eq_propagation_justification(enode * n1, enode * n2):m_node1(n1), m_node2(n2) { SASSERT(n1 != n2); } void get_antecedents(conflict_resolution & cr) override; proof * mk_proof(conflict_resolution & cr) override; char const * get_name() const override { return "eq-propagation"; } }; /** \brief Justification for p(x) <=> p(y), p(x) ===> p(y) */ class mp_iff_justification : public justification { enode * m_node1; // p(x) enode * m_node2; // p(y) public: mp_iff_justification(enode * n1, enode * n2):m_node1(n1), m_node2(n2) { } void get_antecedents(conflict_resolution & cr) override; proof * mk_proof(conflict_resolution & cr) override; char const * get_name() const override { return "mp-iff"; } }; /** \brief Abstract class for justifications that contains set of literals. */ class simple_justification : public justification { protected: unsigned m_num_literals; literal * m_literals; bool antecedent2proof(conflict_resolution & cr, ptr_buffer & result); public: simple_justification(region & r, unsigned num_lits, literal const * lits); void get_antecedents(conflict_resolution & cr) override; proof * mk_proof(conflict_resolution & cr) override = 0; char const * get_name() const override { return "simple"; } }; class simple_theory_justification : public simple_justification { protected: family_id m_th_id; vector m_params; public: simple_theory_justification( family_id fid, region & r, unsigned num_lits, literal const * lits, unsigned num_params, parameter* params): simple_justification(r, num_lits, lits), m_th_id(fid), m_params(num_params, params) {} ~simple_theory_justification() override {} bool has_del_eh() const override { return !m_params.empty(); } void del_eh(ast_manager & m) override { m_params.reset(); } theory_id get_from_theory() const override { return m_th_id; } }; class theory_axiom_justification : public simple_theory_justification { public: theory_axiom_justification(family_id fid, region & r, unsigned num_lits, literal const * lits, unsigned num_params = 0, parameter* params = nullptr): simple_theory_justification(fid, r, num_lits, lits, num_params, params) {} void get_antecedents(conflict_resolution & cr) override {} proof * mk_proof(conflict_resolution & cr) override; char const * get_name() const override { return "theory-axiom"; } }; class theory_propagation_justification : public simple_theory_justification { literal m_consequent; public: theory_propagation_justification(family_id fid, region & r, unsigned num_lits, literal const * lits, literal consequent, unsigned num_params = 0, parameter* params = nullptr): simple_theory_justification(fid, r, num_lits, lits, num_params, params), m_consequent(consequent) {} proof * mk_proof(conflict_resolution & cr) override; char const * get_name() const override { return "theory-propagation"; } }; class theory_conflict_justification : public simple_theory_justification { public: theory_conflict_justification(family_id fid, region & r, unsigned num_lits, literal const * lits, unsigned num_params = 0, parameter* params = nullptr): simple_theory_justification(fid, r, num_lits, lits, num_params, params) {} proof * mk_proof(conflict_resolution & cr) override; char const * get_name() const override { return "theory-conflict"; } }; /** \brief Abstract class for justifications that contains set of literals and equalities. */ class ext_simple_justification : public simple_justification { protected: unsigned m_num_eqs; enode_pair * m_eqs; bool antecedent2proof(conflict_resolution & cr, ptr_buffer & result); public: ext_simple_justification(region & r, unsigned num_lits, literal const * lits, unsigned num_eqs, enode_pair const * eqs); void get_antecedents(conflict_resolution & cr) override; proof * mk_proof(conflict_resolution & cr) override = 0; char const * get_name() const override { return "ext-simple"; } }; /** \brief Abstract class for justifications that contains set of literals and equalities. */ class ext_theory_simple_justification : public ext_simple_justification { protected: family_id m_th_id; vector m_params; public: ext_theory_simple_justification(family_id fid, region & r, unsigned num_lits, literal const * lits, unsigned num_eqs, enode_pair const * eqs, unsigned num_params = 0, parameter* params = nullptr): ext_simple_justification(r, num_lits, lits, num_eqs, eqs), m_th_id(fid), m_params(num_params, params) {} ~ext_theory_simple_justification() override {} bool has_del_eh() const override { return !m_params.empty(); } void del_eh(ast_manager & m) override { m_params.reset(); } theory_id get_from_theory() const override { return m_th_id; } }; class ext_theory_propagation_justification : public ext_theory_simple_justification { literal m_consequent; public: ext_theory_propagation_justification(family_id fid, region & r, unsigned num_lits, literal const * lits, unsigned num_eqs, enode_pair const * eqs, literal consequent, unsigned num_params = 0, parameter* params = nullptr): ext_theory_simple_justification(fid, r, num_lits, lits, num_eqs, eqs, num_params, params), m_consequent(consequent) {} proof * mk_proof(conflict_resolution & cr) override; char const * get_name() const override { return "ext-theory-propagation"; } }; class ext_theory_conflict_justification : public ext_theory_simple_justification { public: ext_theory_conflict_justification(family_id fid, region & r, unsigned num_lits, literal const * lits, unsigned num_eqs, enode_pair const * eqs, unsigned num_params = 0, parameter* params = nullptr): ext_theory_simple_justification(fid, r, num_lits, lits, num_eqs, eqs, num_params, params) {} proof * mk_proof(conflict_resolution & cr) override; char const * get_name() const override { return "ext-theory-conflict"; } }; class ext_theory_eq_propagation_justification : public ext_theory_simple_justification { enode * m_lhs; enode * m_rhs; public: ext_theory_eq_propagation_justification( family_id fid, region & r, unsigned num_lits, literal const * lits, unsigned num_eqs, enode_pair const * eqs, enode * lhs, enode * rhs, unsigned num_params = 0, parameter* params = nullptr): ext_theory_simple_justification(fid, r, num_lits, lits, num_eqs, eqs, num_params, params), m_lhs(lhs), m_rhs(rhs) {} proof * mk_proof(conflict_resolution & cr) override; char const * get_name() const override { return "ext-theory-eq-propagation"; } }; /** \brief A theory lemma is similar to a theory axiom, but it is attached to a CLS_AUX_LEMMA clause instead of CLS_AUX. So, it cannot be stored in the heap, and it is unsafe to store literals, since it may be deleted during backtracking. Instead, they store a set of pairs (sign, expr). This pair is represented as a tagged pointer. */ class theory_lemma_justification : public justification { family_id m_th_id; vector m_params; unsigned m_num_literals; expr ** m_literals; public: theory_lemma_justification(family_id fid, context & ctx, unsigned num_lits, literal const * lits, unsigned num_params = 0, parameter* params = nullptr); ~theory_lemma_justification() override; bool has_del_eh() const override { return true; } void del_eh(ast_manager & m) override; void get_antecedents(conflict_resolution & cr) override {} proof * mk_proof(conflict_resolution & cr) override; char const * get_name() const override { return "theory-lemma"; } }; }; #endif /* SMT_JUSTIFICATION_H_ */ z3-z3-4.8.7/src/smt/smt_kernel.cpp000066400000000000000000000262151356505360400166760ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: smt_kernel.cpp Abstract: New frontend for smt::context. Author: Leonardo de Moura (leonardo) 2012-02-09. Revision History: --*/ #include "smt/smt_kernel.h" #include "smt/smt_context.h" #include "smt/smt_lookahead.h" #include "ast/ast_smt2_pp.h" #include "smt/params/smt_params_helper.hpp" namespace smt { struct kernel::imp { smt::context m_kernel; params_ref m_params; imp(ast_manager & m, smt_params & fp, params_ref const & p): m_kernel(m, fp, p), m_params(p) { } static void copy(imp& src, imp& dst) { context::copy(src.m_kernel, dst.m_kernel); } smt_params & fparams() { return m_kernel.get_fparams(); } params_ref const & params() { return m_params; } ast_manager & m() const { return m_kernel.get_manager(); } bool set_logic(symbol logic) { return m_kernel.set_logic(logic); } void set_progress_callback(progress_callback * callback) { return m_kernel.set_progress_callback(callback); } void display(std::ostream & out) const { // m_kernel.display(out); <<< for external users it is just junk // TODO: it will be replaced with assertion_stack.display unsigned num = m_kernel.get_num_asserted_formulas(); out << "(kernel"; for (unsigned i = 0; i < num; i++) { expr* f = m_kernel.get_asserted_formula(i); out << "\n " << mk_ismt2_pp(f, m(), 2); } out << ")"; } void assert_expr(expr * e) { TRACE("smt_kernel", tout << "assert:\n" << mk_ismt2_pp(e, m()) << "\n";); m_kernel.assert_expr(e); } void assert_expr(expr * e, proof * pr) { m_kernel.assert_expr(e, pr); } unsigned size() const { return m_kernel.get_num_asserted_formulas(); } void get_formulas(ptr_vector& fmls) const { m_kernel.get_asserted_formulas(fmls); } expr* get_formula(unsigned i) const { return m_kernel.get_asserted_formula(i); } void push() { TRACE("smt_kernel", tout << "push()\n";); m_kernel.push(); } void pop(unsigned num_scopes) { TRACE("smt_kernel", tout << "pop()\n";); m_kernel.pop(num_scopes); } unsigned get_scope_level() const { return m_kernel.get_scope_level(); } lbool setup_and_check() { return m_kernel.setup_and_check(); } bool inconsistent() { return m_kernel.inconsistent(); } lbool check(unsigned num_assumptions, expr * const * assumptions) { return m_kernel.check(num_assumptions, assumptions); } lbool check(expr_ref_vector const& cube, vector const& clause) { return m_kernel.check(cube, clause); } lbool get_consequences(expr_ref_vector const& assumptions, expr_ref_vector const& vars, expr_ref_vector& conseq, expr_ref_vector& unfixed) { return m_kernel.get_consequences(assumptions, vars, conseq, unfixed); } lbool preferred_sat(expr_ref_vector const& asms, vector& cores) { return m_kernel.preferred_sat(asms, cores); } lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes) { return m_kernel.find_mutexes(vars, mutexes); } void get_model(model_ref & m) const { m_kernel.get_model(m); } proof * get_proof() { return m_kernel.get_proof(); } unsigned get_unsat_core_size() const { return m_kernel.get_unsat_core_size(); } 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(); } failure last_failure() const { return m_kernel.get_last_search_failure(); } std::string last_failure_as_string() const { return m_kernel.last_failure_as_string(); } void set_reason_unknown(char const* msg) { m_kernel.set_reason_unknown(msg); } void get_assignments(expr_ref_vector & result) { m_kernel.get_assignments(result); } void get_relevant_labels(expr * cnstr, buffer & result) { m_kernel.get_relevant_labels(cnstr, result); } void get_relevant_labeled_literals(bool at_lbls, expr_ref_vector & result) { m_kernel.get_relevant_labeled_literals(at_lbls, result); } void get_relevant_literals(expr_ref_vector & result) { m_kernel.get_relevant_literals(result); } void get_guessed_literals(expr_ref_vector & result) { m_kernel.get_guessed_literals(result); } expr_ref next_cube() { lookahead lh(m_kernel); return lh.choose(); } void collect_statistics(::statistics & st) const { m_kernel.collect_statistics(st); } void reset_statistics() { } void display_statistics(std::ostream & out) const { m_kernel.display_statistics(out); } void display_istatistics(std::ostream & out) const { m_kernel.display_istatistics(out); } bool canceled() { return m_kernel.get_cancel_flag(); } void updt_params(params_ref const & p) { m_kernel.updt_params(p); } }; kernel::kernel(ast_manager & m, smt_params & fp, params_ref const & p) { m_imp = alloc(imp, m, fp, p); } kernel::~kernel() { dealloc(m_imp); } ast_manager & kernel::m() const { return m_imp->m(); } void kernel::copy(kernel& src, kernel& dst) { imp::copy(*src.m_imp, *dst.m_imp); } bool kernel::set_logic(symbol logic) { return m_imp->set_logic(logic); } void kernel::set_progress_callback(progress_callback * callback) { m_imp->set_progress_callback(callback); } void kernel::assert_expr(expr * e) { m_imp->assert_expr(e); } void kernel::assert_expr(expr_ref_vector const& es) { for (unsigned i = 0; i < es.size(); ++i) { m_imp->assert_expr(es[i]); } } void kernel::assert_expr(expr * e, proof * pr) { m_imp->assert_expr(e, pr); } unsigned kernel::size() const { return m_imp->size(); } expr* kernel::get_formula(unsigned i) const { return m_imp->get_formula(i); } void kernel::push() { m_imp->push(); } void kernel::pop(unsigned num_scopes) { m_imp->pop(num_scopes); } unsigned kernel::get_scope_level() const { return m_imp->get_scope_level(); } void kernel::reset() { ast_manager & _m = m(); smt_params & fps = m_imp->fparams(); params_ref ps = m_imp->params(); m_imp->~imp(); m_imp = new (m_imp) imp(_m, fps, ps); } bool kernel::inconsistent() { return m_imp->inconsistent(); } lbool kernel::setup_and_check() { return m_imp->setup_and_check(); } lbool kernel::check(unsigned num_assumptions, expr * const * assumptions) { lbool r = m_imp->check(num_assumptions, assumptions); TRACE("smt_kernel", tout << "check result: " << r << "\n";); return r; } lbool kernel::check(expr_ref_vector const& cube, vector const& clauses) { return m_imp->check(cube, clauses); } lbool kernel::get_consequences(expr_ref_vector const& assumptions, expr_ref_vector const& vars, expr_ref_vector& conseq, expr_ref_vector& unfixed) { return m_imp->get_consequences(assumptions, vars, conseq, unfixed); } lbool kernel::preferred_sat(expr_ref_vector const& asms, vector& cores) { return m_imp->preferred_sat(asms, cores); } lbool kernel::find_mutexes(expr_ref_vector const& vars, vector& mutexes) { return m_imp->find_mutexes(vars, mutexes); } void kernel::get_model(model_ref & m) const { m_imp->get_model(m); } proof * kernel::get_proof() { return m_imp->get_proof(); } unsigned kernel::get_unsat_core_size() const { return m_imp->get_unsat_core_size(); } expr * kernel::get_unsat_core_expr(unsigned idx) const { return m_imp->get_unsat_core_expr(idx); } failure kernel::last_failure() const { return m_imp->last_failure(); } std::string kernel::last_failure_as_string() const { return m_imp->last_failure_as_string(); } void kernel::set_reason_unknown(char const* msg) { m_imp->set_reason_unknown(msg); } void kernel::get_assignments(expr_ref_vector & result) { m_imp->get_assignments(result); } void kernel::get_relevant_labels(expr * cnstr, buffer & result) { m_imp->get_relevant_labels(cnstr, result); } void kernel::get_relevant_labeled_literals(bool at_lbls, expr_ref_vector & result) { m_imp->get_relevant_labeled_literals(at_lbls, result); } void kernel::get_relevant_literals(expr_ref_vector & result) { m_imp->get_relevant_literals(result); } void kernel::get_guessed_literals(expr_ref_vector & result) { m_imp->get_guessed_literals(result); } expr_ref kernel::next_cube() { return m_imp->next_cube(); } std::ostream& kernel::display(std::ostream & out) const { m_imp->display(out); return out; } void kernel::collect_statistics(::statistics & st) const { m_imp->collect_statistics(st); } void kernel::reset_statistics() { m_imp->reset_statistics(); } void kernel::display_statistics(std::ostream & out) const { m_imp->display_statistics(out); } void kernel::display_istatistics(std::ostream & out) const { m_imp->display_istatistics(out); } bool kernel::canceled() const { return m_imp->canceled(); } void kernel::updt_params(params_ref const & p) { return m_imp->updt_params(p); } void kernel::collect_param_descrs(param_descrs & d) { smt_params_helper::collect_param_descrs(d); } context & kernel::get_context() { 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(); } }; z3-z3-4.8.7/src/smt/smt_kernel.h000066400000000000000000000177461356505360400163540ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: smt_kernel.h Abstract: New frontend for smt::context. The "kernel" tries to hide details of the smt::context object. From now on, clients (code outside of the smt module) should be use smt::kernel instead of smt::context. Author: Leonardo de Moura (leonardo) 2012-02-09. Revision History: I initially called it smt::solver. This was confusing to others since we have the abstract solver API, and smt::kernel is not a subclass of ::solver. To increase the confusion I had a class default_solver that implemented the solver API on top of smt::context. To avoid this problem I renamed them in the following way: smt::solver ---> smt::kernel default_solver ---> smt::solver --*/ #ifndef SMT_KERNEL_H_ #define SMT_KERNEL_H_ #include "ast/ast.h" #include "util/params.h" #include "model/model.h" #include "util/lbool.h" #include "util/statistics.h" #include "smt/smt_failure.h" struct smt_params; class progress_callback; namespace smt { class enode; class context; class kernel { struct imp; imp * m_imp; public: kernel(ast_manager & m, smt_params & fp, params_ref const & p = params_ref()); ~kernel(); static void copy(kernel& src, kernel& dst); ast_manager & m() const; /** \brief Set logic. It must be invoked before any assertions. Return true if succeeded. */ bool set_logic(symbol logic); /** brief Set progress meter. Kernel will invoke the callback from time to time. */ void set_progress_callback(progress_callback * callback); /** \brief Assert the given assetion into the logical context. This method uses the "asserted" proof as a justification for e. */ void assert_expr(expr * e); void assert_expr(expr_ref_vector const& es); /** \brief Assert the given assertion with the given proof as a justification. */ void assert_expr(expr * e, proof * pr); /** \brief Return the number of asserted formulas in the kernel. */ unsigned size() const; /** \brief Return the array of asserted formulas. */ void get_formulas(ptr_vector& r) const; /** \brief return the formula at index idx. */ expr* get_formula(unsigned idx) const; /** \brief Create a backtracking point (aka scope level). */ void push(); /** \brief Backtrack the given number of scope levels. */ void pop(unsigned num_scopes); /** \brief Return the number of backtracking points. */ unsigned get_scope_level() const; /** \brief Reset the kernel. All assertions are erased. */ void reset(); /** \brief Return true if the set of asserted formulas is known to be inconsistent. */ bool inconsistent(); /** \brief Setup the logical context and invoke check. */ lbool setup_and_check(); /** \brief Satisfiability check. */ lbool check(unsigned num_assumptions = 0, expr * const * assumptions = nullptr); lbool check(expr_ref_vector const& asms) { return check(asms.size(), asms.c_ptr()); } lbool check(app_ref_vector const& asms) { return check(asms.size(), (expr* const*)asms.c_ptr()); } lbool check(expr_ref_vector const& cube, vector const& clauses); /** \brief extract consequences among variables. */ lbool get_consequences(expr_ref_vector const& assumptions, expr_ref_vector const& vars, expr_ref_vector& conseq, expr_ref_vector& unfixed); /** \brief find mutually exclusive variables. */ lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes); /** \brief Preferential SAT. */ lbool preferred_sat(expr_ref_vector const& asms, vector& cores); /** \brief Return the model associated with the last check command. */ void get_model(model_ref & m) const; /** \brief Return the proof of unsatisfiability associated with the last check command. */ proof * get_proof(); /** \brief Return the size of the unsat core associated with the last check command. */ unsigned get_unsat_core_size() const; /** \brief Return the i-th expression in the unsat core associated with the last check command. \pre i < get_unsat_core_size() */ expr * get_unsat_core_expr(unsigned i) const; /** \brief Return the reason for failure for the last check command. Failure means, it returned l_undef */ failure last_failure() const; /** \brief Return a string describing the failure. */ std::string last_failure_as_string() const; /** \brief Set the reason for unknown. */ void set_reason_unknown(char const* msg); /** \brief Return the set of formulas assigned by the kernel. */ void get_assignments(expr_ref_vector & result); /** \brief Return the set of relevant labels in the last check command. */ void get_relevant_labels(expr * cnstr, buffer & result); /** \brief Return the relevant labeled_literals in the last check command. */ void get_relevant_labeled_literals(bool at_lbls, expr_ref_vector & result); /** \brief Return the relevant literals in the last check command. */ void get_relevant_literals(expr_ref_vector & result); /** \brief Return the set of guessed literals (decisions) performed in the last check command. */ void get_guessed_literals(expr_ref_vector & result); /** \brief return the next case split literal. */ expr_ref next_cube(); /** \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 (For debubbing purposes) Prints the state of the kernel */ std::ostream& display(std::ostream & out) const; /** \brief Collect runtime statistics. */ void collect_statistics(::statistics & st) const; /** \brief Reset kernel statistics. */ void reset_statistics(); /** \brief Display statistics. */ void display_statistics(std::ostream & out) const; /** \brief Display statistics in low level format. */ void display_istatistics(std::ostream & out) const; /** \brief Return true if the kernel was interrupted. */ bool canceled() const; /** \brief Update configuration parameters. */ void updt_params(params_ref const & p); /** \brief Collect a description of the configuration parameters. */ static void collect_param_descrs(param_descrs & d); /** \brief Return a reference to smt::context. This is a temporary hack to support user theories. TODO: remove this hack. We need to revamp user theories too. This method breaks the abstraction barrier. \warning We should not use this method */ context & get_context(); }; }; #endif z3-z3-4.8.7/src/smt/smt_literal.cpp000066400000000000000000000054621356505360400170530ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_literal.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-02-18. Revision History: --*/ #include "smt/smt_literal.h" #include "ast/ast_pp.h" #include "ast/ast_ll_pp.h" namespace smt { void literal::display(std::ostream & out, ast_manager & m, expr * const * bool_var2expr_map) const { if (*this == true_literal) out << "true"; else if (*this == false_literal) out << "false"; else if (*this == null_literal) out << "null"; else if (sign()) out << "(not " << mk_bounded_pp(bool_var2expr_map[var()], m) << ")"; else out << mk_bounded_pp(bool_var2expr_map[var()], m); } void literal::display_compact(std::ostream & out, expr * const * bool_var2expr_map) const { if (*this == true_literal) out << "true"; else if (*this == false_literal) out << "false"; else if (sign()) out << "(not #" << bool_var2expr_map[var()]->get_id() << ")"; else out << "#" << bool_var2expr_map[var()]->get_id(); } std::ostream & operator<<(std::ostream & out, literal l) { if (l == true_literal) out << "true"; else if (l == false_literal) out << "false"; else if (l.sign()) out << "(not p" << l.var() << ")"; else out << "p" << l.var(); return out; } std::ostream & operator<<(std::ostream & out, const literal_vector & v) { display(out, v.begin(), v.end()); return out; } void display_compact(std::ostream & out, unsigned num_lits, literal const * lits, expr * const * bool_var2expr_map) { for (unsigned i = 0; i < num_lits; i++) { if (i > 0) out << " "; lits[i].display_compact(out, bool_var2expr_map); } } void display_verbose(std::ostream & out, ast_manager& m, unsigned num_lits, literal const * lits, expr * const * bool_var2expr_map, char const* sep) { for (unsigned i = 0; i < num_lits; i++) { if (i > 0) out << sep; lits[i].display(out, m, bool_var2expr_map); } } /** \brief Return true if lits1 subsumes lits2. That is every literal in lits1 is in lits2 */ bool backward_subsumption(unsigned num_lits1, literal const * lits1, unsigned num_lits2, literal const * lits2) { unsigned i = 0; for (; i < num_lits1; i++) { literal l1 = lits1[i]; unsigned j = 0; for (; j < num_lits2; j++) if (l1 == lits2[j]) break; if (j == num_lits2) break; } return i == num_lits1; } }; z3-z3-4.8.7/src/smt/smt_literal.h000066400000000000000000000060631356505360400165160ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_literal.h Abstract: Author: Leonardo de Moura (leonardo) 2008-02-18. Revision History: --*/ #ifndef SMT_LITERAL_H_ #define SMT_LITERAL_H_ #include "ast/ast.h" #include "smt/smt_types.h" #include "util/approx_set.h" namespace smt { /** \brief The literal b is represented by the value 2*b, and the literal (not b) by the value 2*b + 1 */ class literal { int m_val; public: literal():m_val(-2) { SASSERT(var() == null_bool_var && !sign()); } explicit literal(bool_var v, bool sign = false): m_val((v << 1) + static_cast(sign)) { } bool_var var() const { return m_val >> 1; } bool sign() const { return m_val & 1; } int index() const { return m_val; } void neg() { m_val = m_val ^ 1; } friend literal operator~(literal l); friend literal to_literal(int x); void display(std::ostream & out, ast_manager & m, expr * const * bool_var2expr_map) const; void display_compact(std::ostream & out, expr * const * bool_var2expr_map) const; unsigned hash() const { return m_val; } }; inline bool operator==(literal l1, literal l2) { return l1.index() == l2.index(); } inline bool operator!=(literal l1, literal l2) { return l1.index() != l2.index(); } inline bool operator<(literal l1, literal l2) { return l1.index() < l2.index(); } inline literal operator~(literal l) { literal r; r.m_val = l.m_val ^ 1; return r; } inline literal to_literal(int x) { literal l; l.m_val = x; return l; } const literal null_literal; const literal true_literal(true_bool_var, false); const literal false_literal(true_bool_var, true); typedef svector literal_vector; typedef sbuffer literal_buffer; std::ostream & operator<<(std::ostream & out, literal l); std::ostream & operator<<(std::ostream & out, const literal_vector & v); void display_compact(std::ostream & out, unsigned num_lits, literal const * lits, expr * const * bool_var2expr_map); void display_verbose(std::ostream & out, ast_manager& m, unsigned num_lits, literal const * lits, expr * const * bool_var2expr_map, char const* sep = "\n"); template void neg_literals(unsigned num_lits, literal const * lits, T & result) { for (unsigned i = 0; i < num_lits; ++i) result.push_back(~lits[i]); } struct literal2unsigned { unsigned operator()(literal l) const { return l.index(); } }; typedef approx_set_tpl literal_approx_set; bool backward_subsumption(unsigned num_lits1, literal const * lits1, unsigned num_lits2, literal const * lits2); }; #endif /* SMT_LITERAL_H_ */ z3-z3-4.8.7/src/smt/smt_lookahead.cpp000066400000000000000000000071041356505360400173410ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_lookahead.cpp Abstract: Lookahead cuber for SMT Author: nbjorner 2019-05-27. Revision History: --*/ #include #include "ast/ast_smt2_pp.h" #include "smt/smt_lookahead.h" #include "smt/smt_context.h" namespace smt { lookahead::lookahead(context& ctx): ctx(ctx), m(ctx.get_manager()) {} double lookahead::get_score() { double score = 0; for (clause* cp : ctx.m_aux_clauses) { unsigned nf = 0, nu = 0; bool is_taut = false; for (literal lit : *cp) { switch (ctx.get_assignment(lit)) { case l_false: if (ctx.get_assign_level(lit) > 0) { ++nf; } break; case l_true: is_taut = true; break; default: ++nu; break; } } if (!is_taut && nf > 0) { score += pow(0.5, nu); } } return score; } struct lookahead::compare { context& ctx; compare(context& ctx): ctx(ctx) {} bool operator()(bool_var v1, bool_var v2) const { return ctx.get_activity(v1) > ctx.get_activity(v2); } }; expr_ref lookahead::choose() { ctx.pop_to_base_lvl(); unsigned sz = ctx.m_bool_var2expr.size(); bool_var best_v = null_bool_var; double best_score = -1; svector vars; for (bool_var v = 0; v < static_cast(sz); ++v) { expr* b = ctx.bool_var2expr(v); if (b && ctx.get_assignment(v) == l_undef) { vars.push_back(v); } } compare comp(ctx); std::sort(vars.begin(), vars.end(), comp); unsigned nf = 0, nc = 0, ns = 0, bound = 2000; for (bool_var v : vars) { if (!ctx.bool_var2expr(v)) continue; literal lit(v, false); ctx.push_scope(); ctx.assign(lit, b_justification::mk_axiom(), true); ctx.propagate(); bool inconsistent = ctx.inconsistent(); double score1 = get_score(); ctx.pop_scope(1); if (inconsistent) { ctx.assign(~lit, b_justification::mk_axiom(), false); ctx.propagate(); ++nf; continue; } ctx.push_scope(); ctx.assign(~lit, b_justification::mk_axiom(), true); ctx.propagate(); inconsistent = ctx.inconsistent(); double score2 = get_score(); ctx.pop_scope(1); if (inconsistent) { ctx.assign(lit, b_justification::mk_axiom(), false); ctx.propagate(); ++nf; continue; } double score = score1 + score2 + 1024*score1*score2; if (score > best_score) { best_score = score; best_v = v; bound += ns; ns = 0; } ++nc; ++ns; if (ns > bound) { break; } } expr_ref result(m); if (ctx.inconsistent()) { result = m.mk_false(); } else if (best_v != null_bool_var) { result = ctx.bool_var2expr(best_v); } else { result = m.mk_true(); } return result; } } z3-z3-4.8.7/src/smt/smt_lookahead.h000066400000000000000000000007201356505360400170030ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_lookahead.h Abstract: Lookahead solver for SMT Author: nbjorner 2019-05-27. Revision History: --*/ #pragma once #include "ast/ast.h" namespace smt { class context; class lookahead { context& ctx; ast_manager& m; struct compare; double get_score(); public: lookahead(context& ctx); expr_ref choose(); }; } z3-z3-4.8.7/src/smt/smt_model_checker.cpp000066400000000000000000000554701356505360400202070ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_model_checker.cpp Abstract: Model checker Author: Leonardo de Moura (leonardo) 2010-12-03. Revision History: - to support lambdas/array models: binding sk -> (as-array k!0) then include definition for k!0 as part of binding. Binding instance can be a pointer into m_pinned expressions. --*/ #include "ast/normal_forms/pull_quant.h" #include "ast/for_each_expr.h" #include "ast/rewriter/var_subst.h" #include "ast/rewriter/rewriter_def.h" #include "ast/ast_pp.h" #include "ast/array_decl_plugin.h" #include "ast/ast_smt2_pp.h" #include "smt/smt_model_checker.h" #include "smt/smt_context.h" #include "smt/smt_model_finder.h" #include "model/model_pp.h" #include namespace smt { model_checker::model_checker(ast_manager & m, qi_params const & p, model_finder & mf): m(m), m_params(p), m_autil(m), m_qm(nullptr), m_context(nullptr), m_root2value(nullptr), m_model_finder(mf), m_max_cexs(1), m_iteration_idx(0), m_has_rec_fun(false), m_curr_model(nullptr), m_fresh_exprs(m), m_pinned_exprs(m) { } model_checker::~model_checker() { m_aux_context = nullptr; // delete aux context before fparams m_fparams = nullptr; } quantifier * model_checker::get_flat_quantifier(quantifier * q) { return m_model_finder.get_flat_quantifier(q); } void model_checker::set_qm(quantifier_manager & qm) { SASSERT(m_qm == nullptr); SASSERT(m_context == nullptr); m_qm = &qm; m_context = &(m_qm->get_context()); } /** \brief Return a term in the context that evaluates to val. */ expr * model_checker::get_term_from_ctx(expr * val) { init_value2expr(); expr * t = nullptr; m_value2expr.find(val, t); return t; } expr * model_checker::get_type_compatible_term(expr * val) { app* fresh_term; if (is_app(val) && to_app(val)->get_num_args() > 0) { ptr_buffer args; for (expr* arg : *to_app(val)) { args.push_back(get_type_compatible_term(arg)); } fresh_term = m.mk_app(to_app(val)->get_decl(), args.size(), args.c_ptr()); } else { expr * sk_term = get_term_from_ctx(val); if (sk_term != nullptr) { return sk_term; } for (expr* f : m_fresh_exprs) { if (m.get_sort(f) == m.get_sort(val)) { return f; } } fresh_term = m.mk_fresh_const("sk", m.get_sort(val)); } m_fresh_exprs.push_back(fresh_term); m_context->ensure_internalized(fresh_term); return fresh_term; } void model_checker::init_value2expr() { if (m_value2expr.empty()) { // populate m_value2expr for (auto const& kv : *m_root2value) { enode * n = kv.m_key; expr * val = kv.m_value; n = n->get_eq_enode_with_min_gen(); m_value2expr.insert(val, n->get_owner()); } } } expr_ref model_checker::replace_value_from_ctx(expr * e) { init_value2expr(); struct beta_reducer_cfg : default_rewriter_cfg { model_checker& mc; beta_reducer_cfg(model_checker& mc):mc(mc) {} bool get_subst(expr * e, expr* & t, proof *& pr) { t = nullptr; pr = nullptr; mc.m_value2expr.find(e, t); return t != nullptr; } }; struct beta_reducer : public rewriter_tpl { beta_reducer_cfg m_cfg; beta_reducer(model_checker& m): rewriter_tpl(m.m, false, m_cfg), m_cfg(m) {} }; beta_reducer br(*this); expr_ref result(m); br(e, result); return result; } /** \brief Assert in m_aux_context, the constraint sk = e_1 OR ... OR sk = e_n where {e_1, ..., e_n} is the universe. */ void model_checker::restrict_to_universe(expr * sk, obj_hashtable const & universe) { SASSERT(!universe.empty()); ptr_buffer eqs; for (expr * e : universe) { eqs.push_back(m.mk_eq(sk, e)); } expr_ref fml(m.mk_or(eqs.size(), eqs.c_ptr()), m); m_aux_context->assert_expr(fml); } /** \brief Assert the negation of q after applying the interpretation in m_curr_model to the uninterpreted symbols in q. The variables are replaced by skolem constants. These constants are stored in sks. */ void model_checker::assert_neg_q_m(quantifier * q, expr_ref_vector & sks) { expr_ref tmp(m); if (!m_curr_model->eval(q->get_expr(), tmp, true)) { return; } TRACE("model_checker", tout << "q after applying interpretation:\n" << mk_ismt2_pp(tmp, m) << "\n";); ptr_buffer subst_args; unsigned num_decls = q->get_num_decls(); subst_args.resize(num_decls, nullptr); sks.resize(num_decls, nullptr); for (unsigned i = 0; i < num_decls; i++) { sort * s = q->get_decl_sort(num_decls - i - 1); expr * sk = m.mk_fresh_const(nullptr, s); sks[num_decls - i - 1] = sk; subst_args[num_decls - i - 1] = sk; if (m_curr_model->is_finite(s)) { restrict_to_universe(sk, m_curr_model->get_known_universe(s)); } } var_subst s(m); expr_ref sk_body = s(tmp, subst_args.size(), subst_args.c_ptr()); expr_ref r(m); r = m.mk_not(sk_body); TRACE("model_checker", tout << "mk_neg_q_m:\n" << mk_ismt2_pp(r, m) << "\n";); m_aux_context->assert_expr(r); } bool model_checker::add_instance(quantifier * q, model * cex, expr_ref_vector & sks, bool use_inv) { if (cex == nullptr || sks.empty()) { TRACE("model_checker", tout << "no model is available\n";); return false; } array_util autil(m); unsigned num_decls = q->get_num_decls(); // Remark: sks were created for the flat version of q. SASSERT(sks.size() >= num_decls); expr_ref_vector bindings(m), defs(m); expr_ref def(m); bindings.resize(num_decls); unsigned max_generation = 0; for (unsigned i = 0; i < num_decls; i++) { expr * sk = sks.get(num_decls - i - 1); func_decl * sk_d = to_app(sk)->get_decl(); expr_ref sk_value(cex->get_some_const_interp(sk_d), m); if (!sk_value) { TRACE("model_checker", tout << "Could not get value for " << sk_d->get_name() << "\n";); return false; // get_some_value failed... giving up } TRACE("model_checker", tout << "Got some value " << sk_value << "\n";); if (use_inv) { unsigned sk_term_gen; expr * sk_term = m_model_finder.get_inv(q, i, sk_value, sk_term_gen); if (sk_term != nullptr) { TRACE("model_checker", tout << "Found inverse " << mk_pp(sk_term, m) << "\n";); SASSERT(!m.is_model_value(sk_term)); max_generation = std::max(sk_term_gen, max_generation); sk_value = sk_term; } else { TRACE("model_checker", tout << "no inverse value for " << sk_value << "\n";); return false; } } else { expr * sk_term = get_term_from_ctx(sk_value); if (sk_term != nullptr) { sk_value = sk_term; } } if (contains_model_value(sk_value)) { sk_value = get_type_compatible_term(sk_value); } func_decl * f = nullptr; if (autil.is_as_array(sk_value, f) && cex->get_func_interp(f) && cex->get_func_interp(f)->get_interp()) { expr_ref body(cex->get_func_interp(f)->get_interp(), m); ptr_vector sorts(f->get_arity(), f->get_domain()); svector names; for (unsigned i = 0; i < f->get_arity(); ++i) { names.push_back(symbol(i)); } defined_names dn(m); body = replace_value_from_ctx(body); body = m.mk_lambda(sorts.size(), sorts.c_ptr(), names.c_ptr(), body); // sk_value = m.mk_fresh_const(0, m.get_sort(sk_value)); // get rid of as-array body = dn.mk_definition(body, to_app(sk_value)); defs.push_back(body); } bindings.set(num_decls - i - 1, sk_value); } TRACE("model_checker", tout << q->get_qid() << " found (use_inv: " << use_inv << ") new instance: " << bindings << "\n" << defs << "\n";); if (!defs.empty()) def = mk_and(defs); max_generation = std::max(m_qm->get_generation(q), max_generation); add_instance(q, bindings, max_generation, def.get()); return true; } void model_checker::add_instance(quantifier* q, expr_ref_vector const& bindings, unsigned max_generation, expr* def) { SASSERT(q->get_num_decls() == bindings.size()); unsigned offset = m_pinned_exprs.size(); m_pinned_exprs.append(bindings); m_pinned_exprs.push_back(q); m_pinned_exprs.push_back(def); m_new_instances.push_back(instance(q, offset, def, max_generation)); } void model_checker::operator()(expr *n) { if (m.is_model_value(n) /*|| m_autil.is_as_array(n)*/) { throw is_model_value(); } } bool model_checker::contains_model_value(expr* n) { if (m.is_model_value(n) /*|| m_autil.is_as_array(n)*/) { return true; } if (is_app(n) && to_app(n)->get_num_args() == 0) { return false; } m_visited.reset(); try { for_each_expr(*this, m_visited, n); } catch (const is_model_value &) { return true; } return false; } bool model_checker::add_blocking_clause(model * cex, expr_ref_vector & sks) { SASSERT(cex != nullptr); expr_ref_buffer diseqs(m); for (expr * sk : sks) { func_decl * sk_d = to_app(sk)->get_decl(); expr_ref sk_value(cex->get_some_const_interp(sk_d), m); if (!sk_value) { TRACE("model_checker", tout << "no constant interpretation for " << mk_pp(sk, m) << "\n";); return false; // get_some_value failed... aborting add_blocking_clause } diseqs.push_back(m.mk_not(m.mk_eq(sk, sk_value))); } expr_ref blocking_clause(m); blocking_clause = m.mk_or(diseqs.size(), diseqs.c_ptr()); TRACE("model_checker", tout << "blocking clause:\n" << mk_ismt2_pp(blocking_clause, m) << "\n";); m_aux_context->assert_expr(blocking_clause); return true; } struct scoped_ctx_push { context* c; scoped_ctx_push(context* c): c(c) { c->push(); } ~scoped_ctx_push() { c->pop(1); } }; /** \brief Return true if q is satisfied by m_curr_model. */ bool model_checker::check(quantifier * q) { SASSERT(!m_aux_context->relevancy()); scoped_ctx_push _push(m_aux_context.get()); quantifier * flat_q = get_flat_quantifier(q); TRACE("model_checker", tout << "model checking:\n" << expr_ref(flat_q->get_expr(), m) << "\n";); expr_ref_vector sks(m); assert_neg_q_m(flat_q, sks); TRACE("model_checker", tout << "skolems:\n" << sks << "\n";); flet l(m_aux_context->get_fparams().m_array_fake_support, true); lbool r = m_aux_context->check(); TRACE("model_checker", tout << "[complete] model-checker result: " << to_sat_str(r) << "\n";); if (r != l_true) { return r == l_false; // quantifier is satisfied by m_curr_model } model_ref complete_cex; m_aux_context->get_model(complete_cex); // try to find new instances using instantiation sets. m_model_finder.restrict_sks_to_inst_set(m_aux_context.get(), q, sks); unsigned num_new_instances = 0; while (true) { flet l(m_aux_context->get_fparams().m_array_fake_support, true); lbool r = m_aux_context->check(); TRACE("model_checker", tout << "[restricted] model-checker (" << (num_new_instances+1) << ") result: " << to_sat_str(r) << "\n";); if (r != l_true) break; model_ref cex; m_aux_context->get_model(cex); if (!add_instance(q, cex.get(), sks, true)) { break; } num_new_instances++; if (num_new_instances >= m_max_cexs || !add_blocking_clause(cex.get(), sks)) { TRACE("model_checker", tout << "Add blocking clause failed new-instances: " << num_new_instances << " max-cex: " << m_max_cexs << "\n";); // add_blocking_clause failed... stop the search for new counter-examples... break; } } if (num_new_instances == 0) { // failed to create instances when restricting to inst sets... then use result of the complete model check TRACE("model_checker", tout << "using complete_cex result:\n"; model_pp(tout, *complete_cex);); add_instance(q, complete_cex.get(), sks, false); } return false; } bool model_checker::check_rec_fun(quantifier* q, bool strict_rec_fun) { TRACE("model_checker", tout << mk_pp(q, m) << "\n";); SASSERT(q->get_num_patterns() == 2); // first pattern is the function, second is the body. func_decl* f = m.get_rec_fun_decl(q); expr_ref_vector args(m); unsigned num_decls = q->get_num_decls(); args.resize(num_decls, nullptr); var_subst sub(m); expr_ref tmp(m), result(m); for (enode* n : m_context->enodes_of(f)) { if (m_context->is_relevant(n)) { app* e = n->get_owner(); SASSERT(e->get_num_args() == num_decls); for (unsigned i = 0; i < num_decls; ++i) { args[i] = e->get_arg(i); } tmp = sub(q->get_expr(), num_decls, args.c_ptr()); m_curr_model->eval(tmp, result, true); if (strict_rec_fun ? !m.is_true(result) : m.is_false(result)) { add_instance(q, args, 0, nullptr); return false; } TRACE("model_checker", tout << tmp << "\nevaluates to:\n" << result << "\n";); } } return true; } void model_checker::init_aux_context() { if (!m_fparams) { m_fparams = alloc(smt_params, m_context->get_fparams()); m_fparams->m_relevancy_lvl = 0; // no relevancy since the model checking problems are quantifier free m_fparams->m_case_split_strategy = CS_ACTIVITY; // avoid warning messages about smt.case_split >= 3. m_fparams->m_arith_dump_lemmas = false; } if (!m_aux_context) { symbol logic; params_ref p; p.set_bool("arith.dump_lemmas", false); m_aux_context = m_context->mk_fresh(&logic, m_fparams.get(), p); } } bool model_checker::check(proto_model * md, obj_map const & root2value) { SASSERT(md != nullptr); m_root2value = &root2value; if (m_qm->num_quantifiers() == 0) return true; if (m_iteration_idx >= m_params.m_mbqi_max_iterations) { IF_VERBOSE(1, verbose_stream() << "(smt.mbqi \"max instantiations " << m_iteration_idx << " reached\")\n";); m_context->set_reason_unknown("max mbqi instantiations reached"); return false; } m_curr_model = md; m_value2expr.reset(); md->compress(); TRACE("model_checker", tout << "MODEL_CHECKER INVOKED\n"; tout << "model:\n"; model_pp(tout, *m_curr_model);); if (m_params.m_mbqi_trace) { verbose_stream() << "(smt.mbqi \"started\")\n"; } init_aux_context(); bool found_relevant = false; unsigned num_failures = 0; check_quantifiers(false, found_relevant, num_failures); if (found_relevant) m_iteration_idx++; TRACE("model_checker", tout << "model after check:\n"; model_pp(tout, *md);); TRACE("model_checker", tout << "model checker result: " << (num_failures == 0) << "\n";); m_max_cexs += m_params.m_mbqi_max_cexs; if (num_failures == 0 && (!m_context->validate_model() || has_rec_under_quantifiers())) { num_failures = 1; // this time force expanding recursive function definitions // that are not forced true in the current model. check_quantifiers(true, found_relevant, num_failures); } if (num_failures == 0) m_curr_model->cleanup(); if (m_params.m_mbqi_trace) { if (num_failures == 0) verbose_stream() << "(smt.mbqi :succeeded true)\n"; else verbose_stream() << "(smt.mbqi :num-failures " << num_failures << ")\n"; } return num_failures == 0; } struct has_rec_fun_proc { obj_hashtable& m_rec_funs; bool m_has_rec_fun; bool has_rec_fun() const { return m_has_rec_fun; } has_rec_fun_proc(obj_hashtable& rec_funs): m_rec_funs(rec_funs), m_has_rec_fun(false) {} void operator()(app* fn) { m_has_rec_fun |= m_rec_funs.contains(fn->get_decl()); } void operator()(expr*) {} }; bool model_checker::has_rec_under_quantifiers() { if (!m_has_rec_fun) { return false; } obj_hashtable rec_funs; for (quantifier * q : *m_qm) { if (m.is_rec_fun_def(q)) { rec_funs.insert(m.get_rec_fun_decl(q)); } } expr_fast_mark1 visited; has_rec_fun_proc proc(rec_funs); for (quantifier * q : *m_qm) { if (!m.is_rec_fun_def(q)) { quick_for_each_expr(proc, visited, q); if (proc.has_rec_fun()) return true; } } return false; } // // (repeated from defined_names.cpp) // NB. The pattern for lambdas is incomplete. // consider store(a, i, v) == \lambda j . if i = j then v else a[j] // the instantiation rules for store(a, i, v) are: // sotre(a, i, v)[j] = if i = j then v else a[j] with patterns {a[j], store(a, i, v)} { store(a, i, v)[j] } // The first pattern is not included. // TBD use a model-based scheme for extracting instantiations instead of // using multi-patterns. // void model_checker::check_quantifiers(bool strict_rec_fun, bool& found_relevant, unsigned& num_failures) { for (quantifier * q : *m_qm) { if (!(m_qm->mbqi_enabled(q) && m_context->is_relevant(q) && m_context->get_assignment(q) == l_true && !m.is_lambda_def(q))) { continue; } TRACE("model_checker", tout << "Check: " << mk_pp(q, m) << "\n"; tout << m_context->get_assignment(q) << "\n";); if (m_params.m_mbqi_trace && q->get_qid() != symbol::null) { verbose_stream() << "(smt.mbqi :checking " << q->get_qid() << ")\n"; } found_relevant = true; if (m.is_rec_fun_def(q)) { m_has_rec_fun = true; if (!check_rec_fun(q, strict_rec_fun)) { TRACE("model_checker", tout << "checking recursive function failed\n";); num_failures++; } } else if (!check(q)) { if (m_params.m_mbqi_trace || get_verbosity_level() >= 5) { IF_VERBOSE(0, verbose_stream() << "(smt.mbqi :failed " << q->get_qid() << ")\n"); } TRACE("model_checker", tout << "checking quantifier " << mk_pp(q, m) << " failed\n";); num_failures++; } } } void model_checker::init_search_eh() { m_max_cexs = m_params.m_mbqi_max_cexs; m_iteration_idx = 0; } void model_checker::restart_eh() { IF_VERBOSE(100, verbose_stream() << "(smt.mbqi \"instantiating new instances...\")\n";); assert_new_instances(); reset_new_instances(); } bool model_checker::has_new_instances() { TRACE("model_checker", tout << "instances: " << m_new_instances.size() << "\n";); return !m_new_instances.empty(); } void model_checker::reset_new_instances() { m_pinned_exprs.reset(); m_new_instances.reset(); } void model_checker::reset() { reset_new_instances(); } void model_checker::assert_new_instances() { TRACE("model_checker_bug_detail", tout << "assert_new_instances, inconsistent: " << m_context->inconsistent() << "\n";); ptr_buffer bindings; vector> dummy; for (instance const& inst : m_new_instances) { quantifier * q = inst.m_q; if (m_context->b_internalized(q)) { bindings.reset(); unsigned num_decls = q->get_num_decls(); unsigned gen = inst.m_generation; unsigned offset = inst.m_bindings_offset; for (unsigned i = 0; i < num_decls; i++) { expr * b = m_pinned_exprs.get(offset + i); if (!m_context->e_internalized(b)) { TRACE("model_checker", tout << "internalizing b:\n" << mk_pp(b, m) << "\n";); m_context->internalize(b, false, gen); } bindings.push_back(m_context->get_enode(b)); } if (inst.m_def) { m_context->internalize_assertion(inst.m_def, nullptr, gen); } TRACE("model_checker_bug_detail", tout << "instantiating... q:\n" << mk_pp(q, m) << "\n"; tout << "inconsistent: " << m_context->inconsistent() << "\n"; tout << "bindings:\n" << expr_ref_vector(m, num_decls, m_pinned_exprs.c_ptr() + offset) << "\n";); m_context->add_instance(q, nullptr, num_decls, bindings.c_ptr(), inst.m_def, gen, gen, gen, dummy); TRACE("model_checker_bug_detail", tout << "after instantiating, inconsistent: " << m_context->inconsistent() << "\n";); } } } }; z3-z3-4.8.7/src/smt/smt_model_checker.h000066400000000000000000000077661356505360400176610ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_model_checker.h Abstract: Model checker AND Model-based quantifier instantiation. Author: Leonardo de Moura (leonardo) 2010-12-03. Revision History: --*/ #ifndef SMT_MODEL_CHECKER_H_ #define SMT_MODEL_CHECKER_H_ #include "util/obj_hashtable.h" #include "ast/ast.h" #include "ast/array_decl_plugin.h" #include "ast/normal_forms/defined_names.h" #include "smt/params/qi_params.h" #include "smt/params/smt_params.h" class proto_model; class model; namespace smt { class context; class enode; class model_finder; class quantifier_manager; class model_checker { ast_manager & m; // _manager; qi_params const & m_params; array_util m_autil; // copy of smt_params for auxiliary context. // the idea is to use a different configuration for the aux context (e.g., disable relevancy) scoped_ptr m_fparams; quantifier_manager * m_qm; context * m_context; // owner of the model checker obj_map const * m_root2value; // temp field to store mapping received in the check method. model_finder & m_model_finder; scoped_ptr m_aux_context; // Auxiliary context used for model checking quantifiers. unsigned m_max_cexs; unsigned m_iteration_idx; bool m_has_rec_fun; proto_model * m_curr_model; obj_map m_value2expr; expr_ref_vector m_fresh_exprs; friend class instantiation_set; void init_aux_context(); void init_value2expr(); expr * get_term_from_ctx(expr * val); expr * get_type_compatible_term(expr * val); expr_ref replace_value_from_ctx(expr * e); void restrict_to_universe(expr * sk, obj_hashtable const & universe); void assert_neg_q_m(quantifier * q, expr_ref_vector & sks); bool add_blocking_clause(model * cex, expr_ref_vector & sks); bool check(quantifier * q); bool check_rec_fun(quantifier* q, bool strict_rec_fun); bool has_rec_under_quantifiers(); void check_quantifiers(bool strict_rec_fun, bool& found_relevant, unsigned& num_failures); struct instance { quantifier * m_q; unsigned m_generation; expr * m_def; unsigned m_bindings_offset; instance(quantifier * q, unsigned offset, expr* def, unsigned gen):m_q(q), m_generation(gen), m_def(def), m_bindings_offset(offset) {} }; svector m_new_instances; expr_ref_vector m_pinned_exprs; bool add_instance(quantifier * q, model * cex, expr_ref_vector & sks, bool use_inv); void reset_new_instances(); void assert_new_instances(); quantifier * get_flat_quantifier(quantifier * q); struct is_model_value {}; expr_mark m_visited; bool contains_model_value(expr * e); void add_instance(quantifier * q, expr_ref_vector const & bindings, unsigned max_generation, expr * def); public: model_checker(ast_manager & m, qi_params const & p, model_finder & mf); ~model_checker(); void set_qm(quantifier_manager & qm); context * get_context() const { return m_context; } bool check(proto_model * md, obj_map const & root2value); bool has_new_instances(); void init_search_eh(); void restart_eh(); void reset(); void operator()(expr* e); }; }; #endif // _SMT_MODEL_CHECKER_H_ z3-z3-4.8.7/src/smt/smt_model_finder.cpp000066400000000000000000004165061356505360400200530ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_model_finder.cpp Abstract: Model finding goodies for universally quantified formulas. Author: Leonardo de Moura (leonardo) 2010-12-17. Revision History: --*/ #include "util/backtrackable_set.h" #include "ast/ast_util.h" #include "ast/macros/macro_util.h" #include "ast/arith_decl_plugin.h" #include "ast/bv_decl_plugin.h" #include "ast/array_decl_plugin.h" #include "ast/normal_forms/pull_quant.h" #include "ast/rewriter/var_subst.h" #include "ast/for_each_expr.h" #include "ast/ast_pp.h" #include "ast/ast_ll_pp.h" #include "ast/well_sorted.h" #include "ast/ast_smt2_pp.h" #include "model/model_pp.h" #include "smt/smt_model_finder.h" #include "smt/smt_context.h" #include "tactic/tactic_exception.h" namespace smt { namespace mf { // ----------------------------------- // // Auxiliary stuff // // ----------------------------------- // Append the new elements of v2 into v1. v2 should not be used after this operation, since it may suffer destructive updates. template void dappend(ptr_vector & v1, ptr_vector & v2) { if (v2.empty()) return; if (v1.empty()) { v1.swap(v2); return; } for (T* t : v2) { if (!v1.contains(t)) v1.push_back(t); } v2.finalize(); } class evaluator { public: virtual expr * eval(expr * n, bool model_completion) = 0; }; // ----------------------------------- // // Instantiation sets // // ----------------------------------- /** \brief Instantiation sets are the S_{k,j} sets in the Complete quantifier instantiation paper. */ class instantiation_set { ast_manager & m; obj_map m_elems; // and the associated generation obj_map m_inv; expr_mark m_visited; public: instantiation_set(ast_manager & m):m(m) {} ~instantiation_set() { for (auto const& kv : m_elems) { m.dec_ref(kv.m_key); } m_elems.reset(); } obj_map const & get_elems() const { return m_elems; } void insert(expr * n, unsigned generation) { if (m_elems.contains(n) || contains_model_value(n)) return; TRACE("model_finder", tout << mk_pp(n, m) << "\n";); m.inc_ref(n); m_elems.insert(n, generation); SASSERT(!m.is_model_value(n)); } void remove(expr * n) { // We can only remove n if it is in m_elems, AND m_inv was not initialized yet. SASSERT(m_elems.contains(n)); SASSERT(m_inv.empty()); m_elems.erase(n); m.dec_ref(n); } void display(std::ostream & out) const { for (auto const& kv : m_elems) { out << mk_bounded_pp(kv.m_key, m) << " [" << kv.m_value << "]\n"; } out << "inverse:\n"; for (auto const& kv : m_inv) { out << mk_bounded_pp(kv.m_key, m) << " -> " << mk_bounded_pp(kv.m_value, m) << "\n"; } } expr * get_inv(expr * v) const { expr * t = nullptr; m_inv.find(v, t); return t; } unsigned get_generation(expr * t) const { unsigned gen = 0; m_elems.find(t, gen); return gen; } void mk_inverse(evaluator & ev) { for (auto const& kv : m_elems) { expr * t = kv.m_key; SASSERT(!contains_model_value(t)); unsigned gen = kv.m_value; expr * t_val = ev.eval(t, true); if (!t_val) break; TRACE("model_finder", tout << mk_pp(t, m) << " " << mk_pp(t_val, m) << "\n";); expr * old_t = nullptr; if (m_inv.find(t_val, old_t)) { unsigned old_t_gen = 0; SASSERT(m_elems.contains(old_t)); m_elems.find(old_t, old_t_gen); if (gen < old_t_gen) { m_inv.insert(t_val, t); } } else { m_inv.insert(t_val, t); } } } obj_map const & get_inv_map() const { return m_inv; } struct is_model_value {}; void operator()(expr *n) { if (m.is_model_value(n)) { throw is_model_value(); } } bool contains_model_value(expr* n) { if (m.is_model_value(n)) { return true; } if (is_app(n) && to_app(n)->get_num_args() == 0) { return false; } m_visited.reset(); try { for_each_expr(*this, m_visited, n); } catch (const is_model_value &) { return true; } return false; } }; /** During model construction time, we solve several constraints that impose restrictions on how the model for the ground formulas may be extended to a model to the relevant universal quantifiers. The class node and its subclasses are used to solve these constraints. */ // ----------------------------------- // // nodes // // ----------------------------------- /** \brief Base class used to solve model construction constraints. */ class node { unsigned m_id; node * m_find; unsigned m_eqc_size; sort * m_sort; // sort of the elements in the instantiation set. bool m_mono_proj; // relevant for integers & reals & bit-vectors bool m_signed_proj; // relevant for bit-vectors. ptr_vector m_avoid_set; ptr_vector m_exceptions; instantiation_set * m_set; expr * m_else; func_decl * m_proj; public: node(unsigned id, sort * s): m_id(id), m_find(nullptr), m_eqc_size(1), m_sort(s), m_mono_proj(false), m_signed_proj(false), m_set(nullptr), m_else(nullptr), m_proj(nullptr) { } ~node() { if (m_set) dealloc(m_set); } unsigned get_id() const { return m_id; } sort * get_sort() const { return m_sort; } bool is_root() const { return m_find == nullptr; } node * get_root() const { node * curr = const_cast(this); while (!curr->is_root()) { curr = curr->m_find; } SASSERT(curr->is_root()); return curr; } void merge(node * other) { node * r1 = get_root(); node * r2 = other->get_root(); SASSERT(r1->m_set == 0); SASSERT(r2->m_set == 0); SASSERT(r1->get_sort() == r2->get_sort()); if (r1 == r2) return; if (r1->m_eqc_size > r2->m_eqc_size) std::swap(r1, r2); r1->m_find = r2; r2->m_eqc_size += r1->m_eqc_size; if (r1->m_mono_proj) r2->m_mono_proj = true; if (r1->m_signed_proj) r2->m_signed_proj = true; dappend(r2->m_avoid_set, r1->m_avoid_set); dappend(r2->m_exceptions, r1->m_exceptions); } void insert_avoid(node * n) { ptr_vector & as = get_root()->m_avoid_set; if (!as.contains(n)) as.push_back(n); } void insert_exception(expr * n) { ptr_vector & ex = get_root()->m_exceptions; if (!ex.contains(n)) ex.push_back(n); } void set_mono_proj() { get_root()->m_mono_proj = true; } bool is_mono_proj() const { return get_root()->m_mono_proj; } void set_signed_proj() { get_root()->m_signed_proj = true; } bool is_signed_proj() const { return get_root()->m_signed_proj; } void mk_instantiation_set(ast_manager & m) { SASSERT(is_root()); SASSERT(!m_set); m_set = alloc(instantiation_set, m); } void insert(expr * n, unsigned generation) { SASSERT(is_ground(n)); get_root()->m_set->insert(n, generation); } void display(std::ostream & out, ast_manager & m) const { if (is_root()) { out << "root node ------\n"; out << "@" << m_id << " mono: " << m_mono_proj << " signed: " << m_signed_proj << ", sort: " << mk_pp(m_sort, m) << "\n"; out << "avoid-set: "; for (node* n : m_avoid_set) { out << "@" << n->get_root()->get_id() << " "; } out << "\n"; out << "exceptions: "; for (expr * e : m_exceptions) { out << mk_bounded_pp(e, m) << " "; } out << "\n"; if (m_else) out << "else: " << mk_pp(m_else, m, 6) << "\n"; if (m_proj) out << "projection: " << m_proj->get_name() << "\n"; if (m_set) { out << "instantiation-set:\n"; m_set->display(out); } out << "----------------\n"; } else { out << "@" << m_id << " -> @" << get_root()->get_id() << "\n"; } } instantiation_set const * get_instantiation_set() const { return get_root()->m_set; } instantiation_set * get_instantiation_set() { return get_root()->m_set; } ptr_vector const & get_exceptions() const { return get_root()->m_exceptions; } ptr_vector const & get_avoid_set() const { return get_root()->m_avoid_set; } // return true if m_avoid_set.contains(this) bool must_avoid_itself() const { node * r = get_root(); for (node* n : m_avoid_set) { if (r == n->get_root()) return true; } return false; } void set_else(expr * e) { SASSERT(!is_mono_proj()); SASSERT(get_root()->m_else == 0); get_root()->m_else = e; } expr * get_else() const { return get_root()->m_else; } void set_proj(func_decl * f) { SASSERT(get_root()->m_proj == 0); get_root()->m_proj = f; } func_decl * get_proj() const { return get_root()->m_proj; } }; typedef std::pair ast_idx_pair; typedef pair_hash, unsigned_hash> ast_idx_pair_hash; typedef map > key2node; /** \brief Auxiliary class for processing the "Almost uninterpreted fragment" described in the paper: Complete instantiation for quantified SMT formulas The idea is to create node objects based on the information produced by the quantifier_analyzer. */ class auf_solver : public evaluator { ast_manager & m; arith_util m_arith; bv_util m_bv; array_util m_array; ptr_vector m_nodes; unsigned m_next_node_id; key2node m_uvars; key2node m_A_f_is; context * m_context; // Mapping from sort to auxiliary constant. // This auxiliary constant is used as a "witness" that is asserted as different from a // finite number of terms. // It is only safe to use this constant for infinite sorts. obj_map m_sort2k; expr_ref_vector m_ks; // range of m_sort2k // Support for evaluating expressions in the current model. proto_model * m_model; obj_map m_eval_cache[2]; expr_ref_vector m_eval_cache_range; ptr_vector m_root_nodes; expr_ref_vector * m_new_constraints; void reset_sort2k() { m_sort2k.reset(); m_ks.reset(); } void reset_eval_cache() { m_eval_cache[0].reset(); m_eval_cache[1].reset(); m_eval_cache_range.reset(); } node * mk_node(key2node & map, ast * n, unsigned i, sort * s) { node * r = nullptr; ast_idx_pair k(n, i); if (map.find(k, r)) { SASSERT(r->get_sort() == s); return r; } r = alloc(node, m_next_node_id, s); m_next_node_id++; map.insert(k, r); m_nodes.push_back(r); return r; } void display_key2node(std::ostream & out, key2node const & m) const { for (auto const& kv : m) { ast * a = kv.m_key.first; unsigned i = kv.m_key.second; node * n = kv.m_value; out << "#" << a->get_id() << ":" << i << " -> @" << n->get_id() << "\n"; } } void display_A_f_is(std::ostream & out) const { for (auto const& kv : m_A_f_is) { func_decl * f = static_cast(kv.m_key.first); unsigned i = kv.m_key.second; node * n = kv.m_value; out << f->get_name() << ":" << i << " -> @" << n->get_id() << "\n"; } } void flush_nodes() { std::for_each(m_nodes.begin(), m_nodes.end(), delete_proc()); } public: auf_solver(ast_manager & m): m(m), m_arith(m), m_bv(m), m_array(m), m_next_node_id(0), m_context(nullptr), m_ks(m), m_model(nullptr), m_eval_cache_range(m), m_new_constraints(nullptr) { } virtual ~auf_solver() { flush_nodes(); reset_eval_cache(); } void set_context(context * ctx) { SASSERT(m_context== nullptr); m_context = ctx; } ast_manager & get_manager() const { return m; } void reset() { flush_nodes(); m_nodes.reset(); m_next_node_id = 0; m_uvars.reset(); m_A_f_is.reset(); m_root_nodes.reset(); reset_sort2k(); } void set_model(proto_model * m) { reset_eval_cache(); m_model = m; } proto_model * get_model() const { SASSERT(m_model); return m_model; } node * get_uvar(quantifier * q, unsigned i) { SASSERT(i < q->get_num_decls()); sort * s = q->get_decl_sort(q->get_num_decls() - i - 1); return mk_node(m_uvars, q, i, s); } node * get_A_f_i(func_decl * f, unsigned i) { SASSERT(i < f->get_arity()); sort * s = f->get_domain(i); return mk_node(m_A_f_is, f, i, s); } instantiation_set const * get_uvar_inst_set(quantifier * q, unsigned i) const { ast_idx_pair k(q, i); node * r = nullptr; if (m_uvars.find(k, r)) return r->get_instantiation_set(); return nullptr; } void mk_instantiation_sets() { for (node* curr : m_nodes) { if (curr->is_root()) { curr->mk_instantiation_set(m); } } } // For each instantiation_set, remove entries that do not evaluate to values. void cleanup_instantiation_sets() { ptr_vector to_delete; for (node * curr : m_nodes) { if (curr->is_root()) { instantiation_set * s = curr->get_instantiation_set(); to_delete.reset(); obj_map const & elems = s->get_elems(); for (auto const& kv : elems) { expr * n = kv.m_key; expr * n_val = eval(n, true); if (!n_val || !m.is_value(n_val)) to_delete.push_back(n); } for (expr* e : to_delete) { s->remove(e); } } } } void display_nodes(std::ostream & out) const { display_key2node(out, m_uvars); display_A_f_is(out); for (node* n : m_nodes) { n->display(out, m); } } expr * eval(expr * n, bool model_completion) override { expr * r = nullptr; if (m_eval_cache[model_completion].find(n, r)) { return r; } expr_ref tmp(m); if (!m_model->eval(n, tmp, model_completion)) { r = nullptr; TRACE("model_finder", tout << "eval\n" << mk_pp(n, m) << "\n-----> null\n";); } else { r = tmp; TRACE("model_finder", tout << "eval\n" << mk_pp(n, m) << "\n----->\n" << mk_pp(r, m) << "\n";); } m_eval_cache[model_completion].insert(n, r); m_eval_cache_range.push_back(r); return r; } private: /** \brief Collect the interpretations of n->get_exceptions() and the interpretations of the m_else of nodes in n->get_avoid_set() */ void collect_exceptions_values(node * n, ptr_buffer & r) { ptr_vector const & exceptions = n->get_exceptions(); ptr_vector const & avoid_set = n->get_avoid_set(); for (expr* e : exceptions) { expr * val = eval(e, true); SASSERT(val != nullptr); r.push_back(val); } for (node* a : avoid_set) { node * n = a->get_root(); if (!n->is_mono_proj() && n->get_else() != nullptr) { expr * val = eval(n->get_else(), true); SASSERT(val != nullptr); r.push_back(val); } } } /** \brief Return an expr t from the instantiation set of \c n s.t. forall e in n.get_exceptions() eval(t) != eval(e) and forall m in n.get_avoid_set() eval(t) != eval(m.get_else()) If there t1 and t2 satisfying this condition, break ties using the generation of them. Return 0 if such t does not exist. */ expr * pick_instance_diff_exceptions(node * n, ptr_buffer const & ex_vals) { instantiation_set const * s = n->get_instantiation_set(); obj_map const & elems = s->get_elems(); expr * t_result = nullptr; unsigned gen_result = UINT_MAX; for (auto const& kv : elems) { expr * t = kv.m_key; unsigned gen = kv.m_value; expr * t_val = eval(t, true); SASSERT(t_val != nullptr); bool found = false; for (expr* v : ex_vals) { if (!m.are_distinct(t_val, v)) { found = true; break; } } if (!found && (t_result == nullptr || gen < gen_result)) { t_result = t; gen_result = gen; } } return t_result; } // we should not assume that uninterpreted sorts are infinite in benchmarks with quantifiers. bool is_infinite(sort * s) const { return !m.is_uninterp(s) && s->is_infinite(); } /** \brief Return a fresh constant k that is used as a witness for elements that must be different from a set of values. */ app * get_k_for(sort * s) { TRACE("model_finder", tout << sort_ref(s, m) << "\n";); SASSERT(is_infinite(s)); app * r = nullptr; if (m_sort2k.find(s, r)) return r; r = m.mk_fresh_const("k", s); m_model->register_aux_decl(r->get_decl()); m_sort2k.insert(s, r); m_ks.push_back(r); return r; } /** \brief Get the interpretation for k in m_model. If m_model does not provide an interpretation for k, then create a fresh one. Remark: this method uses get_fresh_value, so it may fail. */ expr * get_k_interp(app * k) { sort * s = m.get_sort(k); SASSERT(is_infinite(s)); func_decl * k_decl = k->get_decl(); expr * r = m_model->get_const_interp(k_decl); if (r != nullptr) return r; r = m_model->get_fresh_value(s); if (r == nullptr) return nullptr; m_model->register_decl(k_decl, r); SASSERT(m_model->get_const_interp(k_decl) == r); TRACE("model_finder", tout << mk_pp(r, m) << "\n";); return r; } /** \brief Assert k to be different from the set of exceptions. It invokes get_k_interp that may fail. */ bool assert_k_diseq_exceptions(app * k, ptr_vector const & exceptions) { TRACE("assert_k_diseq_exceptions", tout << "assert_k_diseq_exceptions, " << "k: " << mk_pp(k, m) << "\nexceptions:\n"; for (expr * e : exceptions) tout << mk_pp(e, m) << "\n";); expr * k_interp = get_k_interp(k); if (k_interp == nullptr) return false; for (expr * ex : exceptions) { expr * ex_val = eval(ex, true); if (!m.are_distinct(k_interp, ex_val)) { SASSERT(m_new_constraints); // This constraint cannot be asserted into m_context during model construction. // We must save it, and assert it during a restart. m_new_constraints->push_back(m.mk_not(m.mk_eq(k, ex))); } } return true; } void set_projection_else(node * n) { TRACE("model_finder", n->display(tout, m);); SASSERT(n->is_root()); SASSERT(!n->is_mono_proj()); instantiation_set const * s = n->get_instantiation_set(); ptr_vector const & exceptions = n->get_exceptions(); ptr_vector const & avoid_set = n->get_avoid_set(); obj_map const & elems = s->get_elems(); if (elems.empty()) return; if (!exceptions.empty() || !avoid_set.empty()) { ptr_buffer ex_vals; collect_exceptions_values(n, ex_vals); expr * e = pick_instance_diff_exceptions(n, ex_vals); if (e != nullptr) { n->set_else(e); return; } sort * s = n->get_sort(); TRACE("model_finder", tout << "trying to create k for " << mk_pp(s, m) << ", is_infinite: " << is_infinite(s) << "\n";); if (is_infinite(s)) { app * k = get_k_for(s); if (assert_k_diseq_exceptions(k, exceptions)) { n->insert(k, 0); // add k to the instantiation set n->set_else(k); return; } } // TBD: add support for the else of bitvectors. // Idea: get the term t with the minimal interpretation and use t - 1. } n->set_else((*(elems.begin())).m_key); } /** \brief If m_mono_proj is true and n is int or bv, then for each e in n->get_exceptions(), we must add e-1 and e+1 to the instantiation set. If sort->get_sort() is real, then we do nothing and hope for the best. */ void add_mono_exceptions(node * n) { SASSERT(n->is_mono_proj()); sort * s = n->get_sort(); arith_rewriter arw(m); bv_rewriter brw(m); ptr_vector const & exceptions = n->get_exceptions(); expr_ref e_minus_1(m), e_plus_1(m); if (m_arith.is_int(s)) { expr_ref one(m_arith.mk_int(1), m); arith_rewriter arith_rw(m); for (expr * e : exceptions) { arith_rw.mk_sub(e, one, e_minus_1); arith_rw.mk_add(e, one, e_plus_1); TRACE("mf_simp_bug", tout << "e:\n" << mk_ismt2_pp(e, m) << "\none:\n" << mk_ismt2_pp(one, m) << "\n";); // Note: exceptions come from quantifiers bodies. So, they have generation 0. n->insert(e_plus_1, 0); n->insert(e_minus_1, 0); } } else if (m_bv.is_bv_sort(s)) { expr_ref one(m_bv.mk_numeral(rational(1), s), m); bv_rewriter bv_rw(m); for (expr * e : exceptions) { bv_rw.mk_add(e, one, e_plus_1); bv_rw.mk_sub(e, one, e_minus_1); TRACE("mf_simp_bug", tout << "e:\n" << mk_ismt2_pp(e, m) << "\none:\n" << mk_ismt2_pp(one, m) << "\n";); // Note: exceptions come from quantifiers bodies. So, they have generation 0. n->insert(e_plus_1, 0); n->insert(e_minus_1, 0); } } else { return; } } void get_instantiation_set_values(node * n, ptr_buffer & values) { instantiation_set const * s = n->get_instantiation_set(); obj_hashtable already_found; obj_map const & elems = s->get_elems(); for (auto const& kv : elems) { expr * t = kv.m_key; expr * t_val = eval(t, true); if (t_val && !already_found.contains(t_val)) { values.push_back(t_val); already_found.insert(t_val); } } TRACE("model_finder_bug", tout << "values for the instantiation_set of @" << n->get_id() << "\n"; for (expr * v : values) { tout << mk_pp(v, m) << "\n"; }); } template struct numeral_lt { T& m_util; numeral_lt(T& a): m_util(a) {} bool operator()(expr* e1, expr* e2) { rational v1, v2; if (m_util.is_numeral(e1, v1) && m_util.is_numeral(e2, v2)) { return v1 < v2; } else { return e1->get_id() < e2->get_id(); } } }; struct signed_bv_lt { bv_util& m_bv; unsigned m_bv_size; signed_bv_lt(bv_util& bv, unsigned sz):m_bv(bv), m_bv_size(sz) {} bool operator()(expr * e1, expr * e2) { rational v1, v2; if (m_bv.is_numeral(e1, v1) && m_bv.is_numeral(e2, v2)) { v1 = m_bv.norm(v1, m_bv_size, true); v2 = m_bv.norm(v2, m_bv_size, true); return v1 < v2; } else { return e1->get_id() < e2->get_id(); } } }; void sort_values(node * n, ptr_buffer & values) { sort * s = n->get_sort(); if (m_arith.is_int(s) || m_arith.is_real(s)) { std::sort(values.begin(), values.end(), numeral_lt(m_arith)); } else if (!n->is_signed_proj()) { std::sort(values.begin(), values.end(), numeral_lt(m_bv)); } else { std::sort(values.begin(), values.end(), signed_bv_lt(m_bv, m_bv.get_bv_size(s))); } } void mk_mono_proj(node * n) { add_mono_exceptions(n); ptr_buffer values; get_instantiation_set_values(n, values); if (values.empty()) return; sort_values(n, values); sort * s = n->get_sort(); bool is_arith = m_arith.is_int(s) || m_arith.is_real(s); bool is_signed = n->is_signed_proj(); unsigned sz = values.size(); SASSERT(sz > 0); expr * pi = values[sz - 1]; expr_ref var(m); var = m.mk_var(0, s); for (unsigned i = sz - 1; i >= 1; i--) { expr_ref c(m); if (is_arith) c = m_arith.mk_lt(var, values[i]); else if (!is_signed) c = m.mk_not(m_bv.mk_ule(values[i], var)); else c = m.mk_not(m_bv.mk_sle(values[i], var)); pi = m.mk_ite(c, values[i-1], pi); } func_interp * rpi = alloc(func_interp, m, 1); rpi->set_else(pi); func_decl * p = m.mk_fresh_func_decl(1, &s, s); TRACE("model_finder", tout << expr_ref(pi, m) << "\n";); m_model->register_aux_decl(p, rpi); n->set_proj(p); } void mk_simple_proj(node * n) { TRACE("model_finder", n->display(tout, m);); set_projection_else(n); ptr_buffer values; get_instantiation_set_values(n, values); sort * s = n->get_sort(); func_decl * p = m.mk_fresh_func_decl(1, &s, s); func_interp * pi = alloc(func_interp, m, 1); m_model->register_aux_decl(p, pi); if (n->get_else()) { expr * else_val = eval(n->get_else(), true); pi->set_else(else_val); } for (expr * v : values) { pi->insert_new_entry(&v, v); } n->set_proj(p); } void mk_projections() { for (node * n : m_root_nodes) { SASSERT(n->is_root()); if (n->is_mono_proj()) mk_mono_proj(n); else mk_simple_proj(n); } } /** \brief Store in r the partial functions that have A_f_i nodes. */ void collect_partial_funcs(func_decl_set & r) { for (auto const& kv : m_A_f_is) { func_decl * f = to_func_decl(kv.m_key.first); if (!r.contains(f)) { func_interp * fi = m_model->get_func_interp(f); if (fi == nullptr) { fi = alloc(func_interp, m, f->get_arity()); TRACE("model_finder", tout << "register " << f->get_name() << "\n";); m_model->register_decl(f, fi); SASSERT(fi->is_partial()); } if (fi->is_partial()) { r.insert(f); } } } } /** \brief Make sorts associated with nodes that must avoid themselves finite. Only uninterpreted sorts are considered. This is a trick to be able to handle atoms of the form X = Y where X and Y are variables. See paper "Complete Quantifier Instantiation" for more details. */ void mk_sorts_finite() { for (node * n : m_root_nodes) { SASSERT(n->is_root()); sort * s = n->get_sort(); if (m.is_uninterp(s) && // Making all uninterpreted sorts finite. // n->must_avoid_itself() && !m_model->is_finite(s)) { m_model->freeze_universe(s); } } } void add_elem_to_empty_inst_sets() { obj_map sort2elems; ptr_vector need_fresh; for (node * n : m_root_nodes) { SASSERT(n->is_root()); instantiation_set const * s = n->get_instantiation_set(); TRACE("model_finder", s->display(tout);); obj_map const & elems = s->get_elems(); if (elems.empty()) { // The method get_some_value cannot be used if n->get_sort() is an uninterpreted sort or is a sort built using uninterpreted sorts // (e.g., (Array S S) where S is uninterpreted). The problem is that these sorts do not have a fixed interpretation. // Moreover, a model assigns an arbitrary interpretation to these sorts using "model_values" a model value. // If these module values "leak" inside the logical context, they may affect satisfiability. // sort * ns = n->get_sort(); if (m.is_fully_interp(ns)) { n->insert(m_model->get_some_value(ns), 0); } else { need_fresh.push_back(n); } } else { sort2elems.insert(n->get_sort(), elems.begin()->m_key); } } expr_ref_vector trail(m); for (node * n : need_fresh) { expr * e; sort* s = n->get_sort(); if (!sort2elems.find(s, e)) { e = m.mk_fresh_const("elem", s); trail.push_back(e); sort2elems.insert(s, e); } n->insert(e, 0); TRACE("model_finder", tout << "fresh constant: " << mk_pp(e, m) << "\n";); } } /** \brief Store in m_root_nodes the roots from m_nodes. */ void collect_root_nodes() { m_root_nodes.reset(); for (node * n : m_nodes) { if (n->is_root()) m_root_nodes.push_back(n); } } /** \brief Return the projection function for f at argument i. Return 0, if there is none. \remark This method assumes that mk_projections was already invoked. \remark f may have a non partial interpretation on m_model. This may happen because the evaluator uses model_completion. In the beginning of fix_model() we collected all f with partial interpretations. During the process of computing the projections we used the evaluator with model_completion, and it may have fixed the "else" case of some partial interpretations. This is ok, because in the "limit" the "else" of the interpretation is irrelevant after the projections are applied. */ func_decl * get_f_i_proj(func_decl * f, unsigned i) { node * r = nullptr; ast_idx_pair k(f, i); if (!m_A_f_is.find(k, r)) return nullptr; return r->get_proj(); } /** \brief Complete the interpretation of the functions that were collected in the beginning of fix_model(). */ void complete_partial_funcs(func_decl_set const & partial_funcs) { for (func_decl * f : partial_funcs) { // Complete the current interpretation m_model->complete_partial_func(f, true); unsigned arity = f->get_arity(); func_interp * fi = m_model->get_func_interp(f); if (fi->is_constant()) continue; // there is no point in using the projection for fi, since fi is the constant function. expr_ref_vector args(m); bool has_proj = false; for (unsigned i = 0; i < arity; i++) { var * v = m.mk_var(i, f->get_domain(i)); func_decl * pi = get_f_i_proj(f, i); if (pi != nullptr) { args.push_back(m.mk_app(pi, v)); has_proj = true; } else { args.push_back(v); } } if (has_proj) { // f_aux will be assigned to the old interpretation of f. func_decl * f_aux = m.mk_fresh_func_decl(f->get_name(), symbol::null, arity, f->get_domain(), f->get_range()); func_interp * new_fi = alloc(func_interp, m, arity); new_fi->set_else(m.mk_app(f_aux, args.size(), args.c_ptr())); TRACE("model_finder", tout << "Setting new interpretation for " << f->get_name() << "\n" << mk_pp(new_fi->get_else(), m) << "\n";); m_model->reregister_decl(f, new_fi, f_aux); } } } void mk_inverse(node * n) { SASSERT(n->is_root()); instantiation_set * s = n->get_instantiation_set(); s->mk_inverse(*this); } void mk_inverses() { unsigned offset = m_context->get_random_value(); for (unsigned i = m_root_nodes.size(); i-- > 0; ) { node* n = m_root_nodes[(i + offset) % m_root_nodes.size()]; SASSERT(n->is_root()); mk_inverse(n); } } public: void fix_model(expr_ref_vector & new_constraints) { cleanup_instantiation_sets(); m_new_constraints = &new_constraints; func_decl_set partial_funcs; collect_partial_funcs(partial_funcs); reset_eval_cache(); // will start using model_completion collect_root_nodes(); add_elem_to_empty_inst_sets(); mk_sorts_finite(); mk_projections(); mk_inverses(); complete_partial_funcs(partial_funcs); TRACE("model_finder", tout << "after auf_solver fixing the model\n"; display_nodes(tout); tout << "NEW MODEL:\n"; model_pp(tout, *m_model);); } }; // ----------------------------------- // // qinfo class & subclasses // // ----------------------------------- /* During quantifier internalizations time, we collect bits of information about the quantifier structure. These bits are instances of subclasses of qinfo. */ /** \brief Generic bit of information collected when analyzing quantifiers. The subclasses are defined in the .cpp file. */ class qinfo { public: virtual ~qinfo() {} virtual char const * get_kind() const = 0; virtual bool is_equal(qinfo const * qi) const = 0; virtual void display(std::ostream & out) const { out << "[" << get_kind() << "]"; } // AUF fragment solver virtual void process_auf(quantifier * q, auf_solver & s, context * ctx) = 0; virtual void populate_inst_sets(quantifier * q, auf_solver & s, context * ctx) = 0; // second pass... actually we may need to reach a fixpoint, but if it cannot be found // in two passes, the fixpoint is not finite. virtual void populate_inst_sets2(quantifier * q, auf_solver & s, context * ctx) {} // Macro/Hint support virtual void populate_inst_sets(quantifier * q, func_decl * mhead, ptr_vector & uvar_inst_sets, context * ctx) {} }; class f_var : public qinfo { protected: func_decl * m_f; unsigned m_arg_i; unsigned m_var_j; public: f_var(func_decl * f, unsigned i, unsigned j):m_f(f), m_arg_i(i), m_var_j(j) {} ~f_var() override {} char const * get_kind() const override { return "f_var"; } bool is_equal(qinfo const * qi) const override { if (qi->get_kind() != get_kind()) return false; f_var const * other = static_cast(qi); return m_f == other->m_f && m_arg_i == other->m_arg_i && m_var_j == other->m_var_j; } void display(std::ostream & out) const override { out << "(" << m_f->get_name() << ":" << m_arg_i << " -> v!" << m_var_j << ")"; } void process_auf(quantifier * q, auf_solver & s, context * ctx) override { node * n1 = s.get_A_f_i(m_f, m_arg_i); node * n2 = s.get_uvar(q, m_var_j); CTRACE("model_finder", n1->get_sort() != n2->get_sort(), ast_manager & m = ctx->get_manager(); tout << "sort bug:\n" << mk_ismt2_pp(q->get_expr(), m) << "\n" << mk_ismt2_pp(q, m) << "\n"; tout << "decl(0): " << q->get_decl_name(0) << "\n"; tout << "f: " << m_f->get_name() << " i: " << m_arg_i << "\n"; tout << "v: " << m_var_j << "\n"; n1->get_root()->display(tout, m); n2->get_root()->display(tout, m); tout << "f signature: "; for (unsigned i = 0; i < m_f->get_arity(); i++) tout << mk_pp(m_f->get_domain(i), m) << " "; tout << "-> " << mk_pp(m_f->get_range(), m) << "\n"; ); n1->merge(n2); } void populate_inst_sets(quantifier * q, auf_solver & s, context * ctx) override { node * A_f_i = s.get_A_f_i(m_f, m_arg_i); for (enode * n : ctx->enodes_of(m_f)) { if (ctx->is_relevant(n)) { // Remark: it is incorrect to use // n->get_arg(m_arg_i)->get_root() // instead of // n->get_arg(m_arg_i) // // Due to model based quantifier instantiation, some equivalence // classes are merged by accident. // So, using n->get_arg(m_arg_i)->get_root(), we may miss // a necessary instantiation. enode * e_arg = n->get_arg(m_arg_i); expr * arg = e_arg->get_owner(); A_f_i->insert(arg, e_arg->get_generation()); } } } void populate_inst_sets(quantifier * q, func_decl * mhead, ptr_vector & uvar_inst_sets, context * ctx) override { if (m_f != mhead) return; uvar_inst_sets.reserve(m_var_j+1, 0); if (uvar_inst_sets[m_var_j] == 0) uvar_inst_sets[m_var_j] = alloc(instantiation_set, ctx->get_manager()); instantiation_set * s = uvar_inst_sets[m_var_j]; SASSERT(s != nullptr); for (enode * n : ctx->enodes_of(m_f)) { if (ctx->is_relevant(n)) { enode * e_arg = n->get_arg(m_arg_i); expr * arg = e_arg->get_owner(); s->insert(arg, e_arg->get_generation()); } } } }; class f_var_plus_offset : public f_var { expr_ref m_offset; public: f_var_plus_offset(ast_manager & m, func_decl * f, unsigned i, unsigned j, expr * offset): f_var(f, i, j), m_offset(offset, m) { } ~f_var_plus_offset() override {} char const * get_kind() const override { return "f_var_plus_offset"; } bool is_equal(qinfo const * qi) const override { if (qi->get_kind() != get_kind()) return false; f_var_plus_offset const * other = static_cast(qi); return m_f == other->m_f && m_arg_i == other->m_arg_i && m_var_j == other->m_var_j && m_offset.get() == other->m_offset.get(); } void display(std::ostream & out) const override { out << "(" << m_f->get_name() << ":" << m_arg_i << " - " << mk_bounded_pp(m_offset.get(), m_offset.get_manager()) << " -> v!" << m_var_j << ")"; } void process_auf(quantifier * q, auf_solver & s, context * ctx) override { // just create the nodes /* node * A_f_i = */ s.get_A_f_i(m_f, m_arg_i); /* node * S_j = */ s.get_uvar(q, m_var_j); } void populate_inst_sets(quantifier * q, auf_solver & s, context * ctx) override { // S_j is not necessary equal to A_f_i. node * A_f_i = s.get_A_f_i(m_f, m_arg_i)->get_root(); node * S_j = s.get_uvar(q, m_var_j)->get_root(); if (A_f_i == S_j) { // there is no finite fixpoint... we just copy the i-th arguments of A_f_i - m_offset // hope for the best... node * S_j = s.get_uvar(q, m_var_j); for (enode * n : ctx->enodes_of(m_f)) { if (ctx->is_relevant(n)) { arith_rewriter arith_rw(ctx->get_manager()); bv_util bv(ctx->get_manager()); bv_rewriter bv_rw(ctx->get_manager()); enode * e_arg = n->get_arg(m_arg_i); expr * arg = e_arg->get_owner(); expr_ref arg_minus_k(ctx->get_manager()); if (bv.is_bv(arg)) bv_rw.mk_sub(arg, m_offset, arg_minus_k); else arith_rw.mk_sub(arg, m_offset, arg_minus_k); S_j->insert(arg_minus_k, e_arg->get_generation()); } } } else { // A_f_i != S_j, there is hope for a finite fixpoint. // So, we just populate A_f_i f_var::populate_inst_sets(q, s, ctx); // I must also propagate the monotonicity flag since A_f_i and S_j are not in the // same equivalence class. if (A_f_i->is_mono_proj()) S_j->set_mono_proj(); if (S_j->is_mono_proj()) A_f_i->set_mono_proj(); } } template void copy_instances(node * from, node * to, auf_solver & s) { instantiation_set const * from_s = from->get_instantiation_set(); obj_map const & elems_s = from_s->get_elems(); arith_rewriter arith_rw(m_offset.get_manager()); bv_rewriter bv_rw(m_offset.get_manager()); bool is_bv = bv_util(m_offset.get_manager()).is_bv_sort(from->get_sort()); for (auto const& kv : elems_s) { expr * n = kv.m_key; expr_ref n_k(m_offset.get_manager()); if (PLUS) { if (is_bv) { bv_rw.mk_add(n, m_offset, n_k); } else { arith_rw.mk_add(n, m_offset, n_k); } } else { if (is_bv) { bv_rw.mk_sub(n, m_offset, n_k); } else { arith_rw.mk_sub(n, m_offset, n_k); } } to->insert(n_k, kv.m_value); } } void populate_inst_sets2(quantifier * q, auf_solver & s, context * ctx) override { node * A_f_i = s.get_A_f_i(m_f, m_arg_i)->get_root(); node * S_j = s.get_uvar(q, m_var_j)->get_root(); // If A_f_i == S_j, then there is no finite fixpoint, so we do nothing here. if (A_f_i != S_j) { // enforce // A_f_i - k \subset S_j // S_j + k \subset A_f_i copy_instances(A_f_i, S_j, s); copy_instances(S_j, A_f_i, s); } } void populate_inst_sets(quantifier * q, func_decl * mhead, ptr_vector & uvar_inst_sets, context * ctx) override { // ignored when in macro } }; /** \brief auf_arr is a term (pattern) of the form: FORM := GROUND-TERM | (select FORM VAR) Store in arrays, all enodes that match the pattern */ void get_auf_arrays(app * auf_arr, context * ctx, ptr_buffer & arrays) { if (is_ground(auf_arr)) { if (ctx->e_internalized(auf_arr)) { enode * e = ctx->get_enode(auf_arr); if (ctx->is_relevant(e)) { arrays.push_back(e); } } } else { app * nested_array = to_app(auf_arr->get_arg(0)); ptr_buffer nested_arrays; get_auf_arrays(nested_array, ctx, nested_arrays); for (enode * curr : nested_arrays) { enode_vector::iterator it2 = curr->begin_parents(); enode_vector::iterator end2 = curr->end_parents(); for (; it2 != end2; ++it2) { enode * p = *it2; if (ctx->is_relevant(p) && p->get_owner()->get_decl() == auf_arr->get_decl()) { arrays.push_back(p); } } } } } class select_var : public qinfo { protected: ast_manager & m; array_util m_array; app * m_select; // It must satisfy is_auf_select... see bool is_auf_select(expr * t) const unsigned m_arg_i; unsigned m_var_j; app * get_array() const { return to_app(m_select->get_arg(0)); } func_decl * get_array_func_decl(app * ground_array, auf_solver & s) { TRACE("model_evaluator", tout << expr_ref(ground_array, m) << "\n";); expr * ground_array_interp = s.eval(ground_array, false); if (ground_array_interp != nullptr && m_array.is_as_array(ground_array_interp)) return m_array.get_as_array_func_decl(ground_array_interp); return nullptr; } public: select_var(ast_manager & m, app * s, unsigned i, unsigned j):m(m), m_array(m), m_select(s), m_arg_i(i), m_var_j(j) {} ~select_var() override {} char const * get_kind() const override { return "select_var"; } bool is_equal(qinfo const * qi) const override { if (qi->get_kind() != get_kind()) return false; select_var const * other = static_cast(qi); return m_select == other->m_select && m_arg_i == other->m_arg_i && m_var_j == other->m_var_j; } void display(std::ostream & out) const override { out << "(" << mk_bounded_pp(m_select, m) << ":" << m_arg_i << " -> v!" << m_var_j << ")"; } void process_auf(quantifier * q, auf_solver & s, context * ctx) override { ptr_buffer arrays; get_auf_arrays(get_array(), ctx, arrays); TRACE("select_var", tout << "enodes matching: "; display(tout); tout << "\n"; for (enode* n : arrays) { tout << "#" << n->get_owner()->get_id() << "\n" << mk_pp(n->get_owner(), m) << "\n"; }); node * n1 = s.get_uvar(q, m_var_j); for (enode* n : arrays) { app * ground_array = n->get_owner(); func_decl * f = get_array_func_decl(ground_array, s); if (f) { SASSERT(m_arg_i >= 1); node * n2 = s.get_A_f_i(f, m_arg_i - 1); n1->merge(n2); } } } void populate_inst_sets(quantifier * q, auf_solver & s, context * ctx) override { ptr_buffer arrays; get_auf_arrays(get_array(), ctx, arrays); for (enode * curr : arrays) { app * ground_array = curr->get_owner(); func_decl * f = get_array_func_decl(ground_array, s); if (f) { node * A_f_i = s.get_A_f_i(f, m_arg_i - 1); enode_vector::iterator it2 = curr->begin_parents(); enode_vector::iterator end2 = curr->end_parents(); for (; it2 != end2; ++it2) { enode * p = *it2; if (ctx->is_relevant(p) && p->get_owner()->get_decl() == m_select->get_decl()) { SASSERT(m_arg_i < p->get_owner()->get_num_args()); enode * e_arg = p->get_arg(m_arg_i); A_f_i->insert(e_arg->get_owner(), e_arg->get_generation()); } } } } } }; class var_pair : public qinfo { protected: unsigned m_var_i; unsigned m_var_j; public: var_pair(unsigned i, unsigned j):m_var_i(i), m_var_j(j) { if (m_var_i > m_var_j) std::swap(m_var_i, m_var_j); } ~var_pair() override {} bool is_equal(qinfo const * qi) const override { if (qi->get_kind() != get_kind()) return false; var_pair const * other = static_cast(qi); return m_var_i == other->m_var_i && m_var_j == other->m_var_j; } void display(std::ostream & out) const override { out << "(" << get_kind() << ":v!" << m_var_i << ":v!" << m_var_j << ")"; } void populate_inst_sets(quantifier * q, auf_solver & s, context * ctx) override { // do nothing } }; class x_eq_y : public var_pair { public: x_eq_y(unsigned i, unsigned j):var_pair(i, j) {} char const * get_kind() const override { return "x_eq_y"; } void process_auf(quantifier * q, auf_solver & s, context * ctx) override { node * n1 = s.get_uvar(q, m_var_i); node * n2 = s.get_uvar(q, m_var_j); n1->insert_avoid(n2); if (n1 != n2) n2->insert_avoid(n1); } }; class x_neq_y : public var_pair { public: x_neq_y(unsigned i, unsigned j):var_pair(i, j) {} char const * get_kind() const override { return "x_neq_y"; } void process_auf(quantifier * q, auf_solver & s, context * ctx) override { node * n1 = s.get_uvar(q, m_var_i); node * n2 = s.get_uvar(q, m_var_j); n1->merge(n2); } }; class x_leq_y : public var_pair { public: x_leq_y(unsigned i, unsigned j):var_pair(i, j) {} char const * get_kind() const override { return "x_leq_y"; } void process_auf(quantifier * q, auf_solver & s, context * ctx) override { node * n1 = s.get_uvar(q, m_var_i); node * n2 = s.get_uvar(q, m_var_j); n1->merge(n2); n1->set_mono_proj(); } }; // signed bit-vector comparison class x_sleq_y : public x_leq_y { public: x_sleq_y(unsigned i, unsigned j):x_leq_y(i, j) {} char const * get_kind() const override { return "x_sleq_y"; } void process_auf(quantifier * q, auf_solver & s, context * ctx) override { node * n1 = s.get_uvar(q, m_var_i); node * n2 = s.get_uvar(q, m_var_j); n1->merge(n2); n1->set_mono_proj(); n1->set_signed_proj(); } }; class var_expr_pair : public qinfo { protected: unsigned m_var_i; expr_ref m_t; public: var_expr_pair(ast_manager & m, unsigned i, expr * t): m_var_i(i), m_t(t, m) {} ~var_expr_pair() override {} bool is_equal(qinfo const * qi) const override { if (qi->get_kind() != get_kind()) return false; var_expr_pair const * other = static_cast(qi); return m_var_i == other->m_var_i && m_t.get() == other->m_t.get(); } void display(std::ostream & out) const override { out << "(" << get_kind() << ":v!" << m_var_i << ":" << mk_bounded_pp(m_t.get(), m_t.get_manager()) << ")"; } }; class x_eq_t : public var_expr_pair { public: x_eq_t(ast_manager & m, unsigned i, expr * t): var_expr_pair(m, i, t) {} char const * get_kind() const override { return "x_eq_t"; } void process_auf(quantifier * q, auf_solver & s, context * ctx) override { node * n1 = s.get_uvar(q, m_var_i); n1->insert_exception(m_t); } void populate_inst_sets(quantifier * q, auf_solver & slv, context * ctx) override { unsigned num_vars = q->get_num_decls(); ast_manager & m = ctx->get_manager(); sort * s = q->get_decl_sort(num_vars - m_var_i - 1); if (m.is_uninterp(s)) { // For uninterpreted sorts, we add all terms in the context. // See Section 4.1 in the paper "Complete Quantifier Instantiation" node * S_q_i = slv.get_uvar(q, m_var_i); for (enode * n : ctx->enodes()) { if (ctx->is_relevant(n) && get_sort(n->get_owner()) == s) { S_q_i->insert(n->get_owner(), n->get_generation()); } } } } }; class x_neq_t : public var_expr_pair { public: x_neq_t(ast_manager & m, unsigned i, expr * t): var_expr_pair(m, i, t) {} char const * get_kind() const override { return "x_neq_t"; } void process_auf(quantifier * q, auf_solver & s, context * ctx) override { // make sure that S_q_i is create. s.get_uvar(q, m_var_i); } void populate_inst_sets(quantifier * q, auf_solver & s, context * ctx) override { node * S_q_i = s.get_uvar(q, m_var_i); S_q_i->insert(m_t, 0); } }; class x_gle_t : public var_expr_pair { public: x_gle_t(ast_manager & m, unsigned i, expr * t): var_expr_pair(m, i, t) {} char const * get_kind() const override { return "x_gle_t"; } void process_auf(quantifier * q, auf_solver & s, context * ctx) override { // make sure that S_q_i is create. node * n1 = s.get_uvar(q, m_var_i); n1->set_mono_proj(); } void populate_inst_sets(quantifier * q, auf_solver & s, context * ctx) override { node * S_q_i = s.get_uvar(q, m_var_i); S_q_i->insert(m_t, 0); } }; class cond_macro { protected: ast_manager & m; func_decl * m_f; expr * m_def; expr * m_cond; bool m_ineq; bool m_satisfy_atom; bool m_hint; unsigned m_weight; public: cond_macro(ast_manager & m, func_decl * f, expr * def, expr * cond, bool ineq, bool satisfy_atom, bool hint, unsigned weight): m(m), m_f(f), m_def(def), m_cond(cond), m_ineq(ineq), m_satisfy_atom(satisfy_atom), m_hint(hint), m_weight(weight) { m.inc_ref(m_def); m.inc_ref(m_cond); SASSERT(!m_hint || m_cond == 0); } ~cond_macro() { m.dec_ref(m_def); m.dec_ref(m_cond); } func_decl * get_f() const { return m_f; } expr * get_def() const { return m_def; } expr * get_cond() const { return m_cond; } bool is_unconditional() const { return m_cond == nullptr || m.is_true(m_cond); } bool satisfy_atom() const { return m_satisfy_atom; } bool is_hint() const { return m_hint; } bool is_equal(cond_macro const * other) const { return m_f == other->m_f && m_def == other->m_def && m_cond == other->m_cond; } void display(std::ostream & out) const { out << "[" << m_f->get_name() << " -> " << mk_bounded_pp(m_def, m, 6); if (m_hint) out << " *hint*"; else out << " when " << mk_bounded_pp(m_cond, m, 6); out << "] weight: " << m_weight; } unsigned get_weight() const { return m_weight; } }; // ----------------------------------- // // quantifier_info & quantifier_analyzer // // ----------------------------------- class quantifier_analyzer; /** \brief Store relevant information regarding a particular universal quantifier. This information is populated by quantifier_analyzer. The information is used to (try to) build a model that satisfy the universal quantifier (when it is marked as relevant in the end of the search). */ class quantifier_info { model_finder& m_mf; quantifier_ref m_q; // original quantifier quantifier_ref m_flat_q; // flat version of the quantifier bool m_is_auf; bool m_has_x_eq_y; ptr_vector m_qinfo_vect; func_decl_set m_ng_decls; // declarations used in non-ground applications (basic_family operators are ignored here). ptr_vector m_cond_macros; func_decl * m_the_one; // the macro head used to satisfy the quantifier. this is useful for model checking // when the quantifier is satisfied by a macro/hint, it may not be processed by the AUF solver. // in this case, the quantifier_info stores the instantiation sets. ptr_vector * m_uvar_inst_sets; friend class quantifier_analyzer; void checkpoint() { m_mf.checkpoint("quantifier_info"); } void insert_qinfo(qinfo * qi) { // I'm assuming the number of qinfo's per quantifier is small. So, the linear search is not a big deal. scoped_ptr q(qi); for (qinfo* qi2 : m_qinfo_vect) { checkpoint(); if (qi->is_equal(qi2)) { return; } } m_qinfo_vect.push_back(q.detach()); TRACE("model_finder", tout << "new quantifier qinfo: "; qi->display(tout); tout << "\n";); } void insert_macro(cond_macro * m) { m_cond_macros.push_back(m); } public: typedef ptr_vector::const_iterator macro_iterator; quantifier_info(model_finder& mf, ast_manager & m, quantifier * q): m_mf(mf), m_q(q, m), m_flat_q(m), m_is_auf(true), m_has_x_eq_y(false), m_the_one(nullptr), m_uvar_inst_sets(nullptr) { if (has_quantifiers(q->get_expr()) && !m.is_lambda_def(q)) { proof_ref pr(m); expr_ref new_q(m); pull_quant pull(m); pull(q, new_q, pr); SASSERT(is_well_sorted(m, new_q)); m_flat_q = to_quantifier(new_q); } else { m_flat_q = q; } CTRACE("model_finder_bug", has_quantifiers(m_flat_q->get_expr()), tout << mk_pp(q, m) << "\n" << mk_pp(m_flat_q, m) << "\n";); } ~quantifier_info() { std::for_each(m_qinfo_vect.begin(), m_qinfo_vect.end(), delete_proc()); std::for_each(m_cond_macros.begin(), m_cond_macros.end(), delete_proc()); reset_the_one(); } bool is_auf() const { return m_is_auf; } quantifier * get_flat_q() const { return m_flat_q; } bool unary_function_fragment() const { unsigned sz = m_ng_decls.size(); if (sz > 1) return false; if (sz == 0) return true; func_decl * f = *(m_ng_decls.begin()); return f->get_arity() == 1; } bool has_cond_macros() const { return !m_cond_macros.empty(); } ptr_vector const& macros() const { return m_cond_macros; } void set_the_one(func_decl * m) { m_the_one = m; } func_decl * get_the_one() const { return m_the_one; } bool contains_ng_decl(func_decl * f) const { return m_ng_decls.contains(f); } void display(std::ostream & out) const { ast_manager & m = m_flat_q.get_manager(); out << "info for (flat) quantifier:\n" << mk_pp(m_flat_q.get(), m) << "\n"; out << "IS_AUF: " << m_is_auf << ", has x=y: " << m_has_x_eq_y << "\n"; out << "unary function fragment: " << unary_function_fragment() << "\n"; out << "ng decls: "; for (func_decl * f : m_ng_decls) { out << f->get_name() << " "; } out << "\ninfo bits:\n"; for (qinfo* qi : m_qinfo_vect) { out << " "; qi->display(out); out << "\n"; } out << "\nmacros:\n"; for (cond_macro* cm : m_cond_macros) { out << " "; cm->display(out); out << "\n"; } } void process_auf(auf_solver & s, context * ctx) { for (unsigned i = 0; i < m_flat_q->get_num_decls(); i++) { // make sure a node exists for each variable. s.get_uvar(m_flat_q, i); } for (qinfo * qi : m_qinfo_vect) { qi->process_auf(m_flat_q, s, ctx); } } void populate_inst_sets(auf_solver & s, context * ctx) { for (qinfo * qi : m_qinfo_vect) { qi->populate_inst_sets(m_flat_q, s, ctx); } // second pass for (qinfo * qi : m_qinfo_vect) { checkpoint(); qi->populate_inst_sets2(m_flat_q, s, ctx); } } func_decl_set const & get_ng_decls() const { return m_ng_decls; } void populate_macro_based_inst_sets(context * ctx, evaluator & ev) { SASSERT(m_the_one != 0); if (m_uvar_inst_sets != nullptr) return; m_uvar_inst_sets = alloc(ptr_vector); for (qinfo* qi : m_qinfo_vect) qi->populate_inst_sets(m_flat_q, m_the_one, *m_uvar_inst_sets, ctx); for (instantiation_set * s : *m_uvar_inst_sets) { if (s != nullptr) s->mk_inverse(ev); } } instantiation_set * get_macro_based_inst_set(unsigned vidx, context * ctx, evaluator & ev) { if (m_the_one == nullptr) return nullptr; populate_macro_based_inst_sets(ctx, ev); return m_uvar_inst_sets->get(vidx, 0); } void reset_the_one() { m_the_one = nullptr; if (m_uvar_inst_sets) { std::for_each(m_uvar_inst_sets->begin(), m_uvar_inst_sets->end(), delete_proc()); dealloc(m_uvar_inst_sets); m_uvar_inst_sets = nullptr; } } }; /** \brief Functor used to traverse/analyze a quantifier and fill the structure quantifier_info. */ class quantifier_analyzer { model_finder& m_mf; ast_manager & m; macro_util m_mutil; array_util m_array_util; arith_util m_arith_util; bv_util m_bv_util; quantifier_info * m_info; typedef enum { POS, NEG } polarity; polarity neg(polarity p) { return p == POS ? NEG : POS; } obj_hashtable m_pos_cache; obj_hashtable m_neg_cache; typedef std::pair entry; svector m_ftodo; ptr_vector m_ttodo; void insert_qinfo(qinfo * qi) { SASSERT(m_info); m_info->insert_qinfo(qi); } bool is_var_plus_ground(expr * n, bool & inv, var * & v, expr_ref & t) { return m_mutil.is_var_plus_ground(n, inv, v, t); } bool is_var_plus_ground(expr * n, var * & v, expr_ref & t) { bool inv; TRACE("is_var_plus_ground", tout << mk_pp(n, m) << "\n"; tout << "is_var_plus_ground: " << is_var_plus_ground(n, inv, v, t) << "\n"; tout << "inv: " << inv << "\n";); return is_var_plus_ground(n, inv, v, t) && !inv; } bool is_add(expr * n) const { return m_mutil.is_add(n); } bool is_zero(expr * n) const { return m_mutil.is_zero_safe(n); } bool is_times_minus_one(expr * n, expr * & arg) const { return m_mutil.is_times_minus_one(n, arg); } bool is_le(expr * n) const { return m_mutil.is_le(n); } bool is_le_ge(expr * n) const { return m_mutil.is_le_ge(n); } bool is_signed_le(expr * n) const { return m_bv_util.is_bv_sle(n); } expr * mk_one(sort * s) { return m_bv_util.is_bv_sort(s) ? m_bv_util.mk_numeral(rational(1), s) : m_arith_util.mk_numeral(rational(1), s); } void mk_sub(expr * t1, expr * t2, expr_ref & r) const { m_mutil.mk_sub(t1, t2, r); } void mk_add(expr * t1, expr * t2, expr_ref & r) const { m_mutil.mk_add(t1, t2, r); } bool is_var_and_ground(expr * lhs, expr * rhs, var * & v, expr_ref & t, bool & inv) { inv = false; // true if invert the sign TRACE("is_var_and_ground", tout << "is_var_and_ground: " << mk_ismt2_pp(lhs, m) << " " << mk_ismt2_pp(rhs, m) << "\n";); if (is_var(lhs) && is_ground(rhs)) { v = to_var(lhs); t = rhs; TRACE("is_var_and_ground", tout << "var and ground\n";); return true; } else if (is_var(rhs) && is_ground(lhs)) { v = to_var(rhs); t = lhs; TRACE("is_var_and_ground", tout << "ground and var\n";); return true; } else { expr_ref tmp(m); if (is_var_plus_ground(lhs, inv, v, tmp) && is_ground(rhs)) { if (inv) mk_sub(tmp, rhs, t); else mk_sub(rhs, tmp, t); return true; } if (is_var_plus_ground(rhs, inv, v, tmp) && is_ground(lhs)) { if (inv) mk_sub(tmp, lhs, t); else mk_sub(lhs, tmp, t); return true; } } return false; } bool is_var_and_ground(expr * lhs, expr * rhs, var * & v, expr_ref & t) { bool inv; return is_var_and_ground(lhs, rhs, v, t, inv); } bool is_x_eq_t_atom(expr * n, var * & v, expr_ref & t) { if (!is_app(n)) return false; if (m.is_eq(n)) return is_var_and_ground(to_app(n)->get_arg(0), to_app(n)->get_arg(1), v, t); return false; } bool is_var_minus_var(expr * n, var * & v1, var * & v2) { if (!is_add(n)) return false; expr * arg1 = to_app(n)->get_arg(0); expr * arg2 = to_app(n)->get_arg(1); if (!is_var(arg1)) std::swap(arg1, arg2); if (!is_var(arg1)) return false; expr * arg2_2; if (!is_times_minus_one(arg2, arg2_2)) return false; if (!is_var(arg2_2)) return false; v1 = to_var(arg1); v2 = to_var(arg2_2); return true; } bool is_var_and_var(expr * lhs, expr * rhs, var * & v1, var * & v2) { if (is_var(lhs) && is_var(rhs)) { v1 = to_var(lhs); v2 = to_var(rhs); return true; } return (is_var_minus_var(lhs, v1, v2) && is_zero(rhs)) || (is_var_minus_var(rhs, v1, v2) && is_zero(lhs)); } bool is_x_eq_y_atom(expr * n, var * & v1, var * & v2) { return m.is_eq(n) && is_var_and_var(to_app(n)->get_arg(0), to_app(n)->get_arg(1), v1, v2); } bool is_x_gle_y_atom(expr * n, var * & v1, var * & v2) { return is_le_ge(n) && is_var_and_var(to_app(n)->get_arg(0), to_app(n)->get_arg(1), v1, v2); } bool is_x_gle_t_atom(expr * atom, bool sign, var * & v, expr_ref & t) { if (!is_app(atom)) return false; if (sign) { bool r = is_le_ge(atom) && is_var_and_ground(to_app(atom)->get_arg(0), to_app(atom)->get_arg(1), v, t); CTRACE("is_x_gle_t", r, tout << "is_x_gle_t: " << mk_ismt2_pp(atom, m) << "\n--->\n" << mk_ismt2_pp(v, m) << " " << mk_ismt2_pp(t, m) << "\n"; tout << "sign: " << sign << "\n";); return r; } else { if (is_le_ge(atom)) { expr_ref tmp(m); bool le = is_le(atom); bool inv = false; if (is_var_and_ground(to_app(atom)->get_arg(0), to_app(atom)->get_arg(1), v, tmp, inv)) { if (inv) le = !le; sort * s = m.get_sort(tmp); expr_ref one(m); one = mk_one(s); if (le) mk_add(tmp, one, t); else mk_sub(tmp, one, t); TRACE("is_x_gle_t", tout << "is_x_gle_t: " << mk_ismt2_pp(atom, m) << "\n--->\n" << mk_ismt2_pp(v, m) << " " << mk_ismt2_pp(t, m) << "\n"; tout << "sign: " << sign << "\n";); return true; } } return false; } } void reset_cache() { m_pos_cache.reset(); m_neg_cache.reset(); } obj_hashtable & get_cache(polarity pol) { return pol == POS ? m_pos_cache : m_neg_cache; } void visit_formula(expr * n, polarity pol) { if (is_ground(n)) return; // ground terms do not need to be visited. obj_hashtable & c = get_cache(pol); if (!c.contains(n)) { m_ftodo.push_back(entry(n, pol)); c.insert(n); } } void visit_term(expr * n) { // ground terms do not need to be visited. if (!is_ground(n) && !m_pos_cache.contains(n)) { m_ttodo.push_back(n); m_pos_cache.insert(n); } } /** \brief Process uninterpreted applications. */ void process_u_app(app * t) { unsigned num_args = t->get_num_args(); for (unsigned i = 0; i < num_args; i++) { expr * arg = t->get_arg(i); if (is_var(arg)) { SASSERT(t->get_decl()->get_domain(i) == to_var(arg)->get_sort()); insert_qinfo(alloc(f_var, t->get_decl(), i, to_var(arg)->get_idx())); continue; } var * v; expr_ref k(m); if (is_var_plus_ground(arg, v, k)) { insert_qinfo(alloc(f_var_plus_offset, m, t->get_decl(), i, v->get_idx(), k.get())); continue; } visit_term(arg); } } /** \brief A term \c t is said to be a auf_select if it is of the form (select a i) Where: where a is ground or is_auf_select(a) == true and the indices are ground terms or variables. */ bool is_auf_select(expr * t) const { if (!m_array_util.is_select(t)) return false; expr * a = to_app(t)->get_arg(0); if (!is_ground(a) && !is_auf_select(a)) return false; for (expr * arg : *to_app(t)) { if (!is_ground(arg) && !is_var(arg)) return false; } return true; } /** \brief Process interpreted applications. */ void process_i_app(app * t) { if (is_auf_select(t)) { unsigned num_args = t->get_num_args(); app * array = to_app(t->get_arg(0)); visit_term(array); // array may be a nested array. for (unsigned i = 1; i < num_args; i++) { expr * arg = t->get_arg(i); if (is_var(arg)) { insert_qinfo(alloc(select_var, m, t, i, to_var(arg)->get_idx())); } else { SASSERT(is_ground(arg)); } } } else { for (expr * arg : *t) { visit_term(arg); } } } void process_app(app * t) { SASSERT(!is_ground(t)); if (t->get_family_id() != m.get_basic_family_id()) { m_info->m_ng_decls.insert(t->get_decl()); } if (is_uninterp(t)) { process_u_app(t); } else { process_i_app(t); } } void process_terms_on_stack() { while (!m_ttodo.empty()) { expr * curr = m_ttodo.back(); m_ttodo.pop_back(); if (m.is_bool(curr)) { // formula nested in a term. visit_formula(curr, POS); visit_formula(curr, NEG); continue; } if (is_app(curr)) { process_app(to_app(curr)); } else if (is_var(curr)) { m_info->m_is_auf = false; // unexpected occurrence of variable. } else { SASSERT(is_lambda(curr)); } } } void process_literal(expr * atom, bool sign) { CTRACE("model_finder_bug", is_ground(atom), tout << mk_pp(atom, m) << "\n";); SASSERT(!is_ground(atom)); SASSERT(m.is_bool(atom)); if (is_var(atom)) { if (sign) { // atom (not X) can be viewed as X != true insert_qinfo(alloc(x_neq_t, m, to_var(atom)->get_idx(), m.mk_true())); } else { // atom X can be viewed as X != false insert_qinfo(alloc(x_neq_t, m, to_var(atom)->get_idx(), m.mk_false())); } return; } if (is_app(atom)) { var * v, * v1, * v2; expr_ref t(m); if (is_x_eq_t_atom(atom, v, t)) { if (sign) insert_qinfo(alloc(x_neq_t, m, v->get_idx(), t)); else insert_qinfo(alloc(x_eq_t, m, v->get_idx(), t)); } else if (is_x_eq_y_atom(atom, v1, v2)) { if (sign) insert_qinfo(alloc(x_neq_y, v1->get_idx(), v2->get_idx())); else { m_info->m_has_x_eq_y = true; // this atom is in the fringe of AUF insert_qinfo(alloc(x_eq_y, v1->get_idx(), v2->get_idx())); } } else if (sign && is_x_gle_y_atom(atom, v1, v2)) { if (is_signed_le(atom)) insert_qinfo(alloc(x_sleq_y, v1->get_idx(), v2->get_idx())); else insert_qinfo(alloc(x_leq_y, v1->get_idx(), v2->get_idx())); } else if (is_x_gle_t_atom(atom, sign, v, t)) { insert_qinfo(alloc(x_gle_t, m, v->get_idx(), t)); } else { process_app(to_app(atom)); } return; } SASSERT(is_quantifier(atom)); UNREACHABLE(); } void process_literal(expr * atom, polarity pol) { process_literal(atom, pol == NEG); } void process_and_or(app * n, polarity p) { for (expr* arg : *n) visit_formula(arg, p); } void process_ite(app * n, polarity p) { visit_formula(n->get_arg(0), p); visit_formula(n->get_arg(0), neg(p)); visit_formula(n->get_arg(1), p); visit_formula(n->get_arg(2), p); } void process_iff(app * n) { visit_formula(n->get_arg(0), POS); visit_formula(n->get_arg(0), NEG); visit_formula(n->get_arg(1), POS); visit_formula(n->get_arg(1), NEG); } void checkpoint() { m_mf.checkpoint("quantifier_analyzer"); } void process_formulas_on_stack() { while (!m_ftodo.empty()) { checkpoint(); entry & e = m_ftodo.back(); expr * curr = e.first; polarity pol = e.second; m_ftodo.pop_back(); if (is_app(curr)) { if (to_app(curr)->get_family_id() == m.get_basic_family_id() && m.is_bool(curr)) { switch (static_cast(to_app(curr)->get_decl_kind())) { case OP_IMPLIES: case OP_XOR: UNREACHABLE(); // simplifier eliminated ANDs, IMPLIEs, and XORs break; case OP_OR: case OP_AND: process_and_or(to_app(curr), pol); break; case OP_NOT: visit_formula(to_app(curr)->get_arg(0), neg(pol)); break; case OP_ITE: process_ite(to_app(curr), pol); break; case OP_EQ: if (m.is_bool(to_app(curr)->get_arg(0))) { process_iff(to_app(curr)); } else { process_literal(curr, pol); } break; default: process_literal(curr, pol); break; } } else { process_literal(curr, pol); } } else if (is_var(curr)) { SASSERT(m.is_bool(curr)); process_literal(curr, pol); } else { SASSERT(is_quantifier(curr)); SASSERT(is_lambda(curr)); //UNREACHABLE(); // can't happen, the quantifier is supposed to be flat. } } } void process_formula(expr * n) { SASSERT(m.is_bool(n)); visit_formula(n, POS); } void process_clause(expr * cls) { SASSERT(is_clause(m, cls)); unsigned num_lits = get_clause_num_literals(m, cls); for (unsigned i = 0; i < num_lits; i++) { expr * lit = get_clause_literal(m, cls, i); SASSERT(is_literal(m, lit)); expr * atom; bool sign; get_literal_atom_sign(m, lit, atom, sign); if (!is_ground(atom)) process_literal(atom, sign); } } void collect_macro_candidates(quantifier * q) { macro_util::macro_candidates candidates(m); m_mutil.collect_macro_candidates(q, candidates); unsigned num_candidates = candidates.size(); for (unsigned i = 0; i < num_candidates; i++) { cond_macro * mc = alloc(cond_macro, m, candidates.get_f(i), candidates.get_def(i), candidates.get_cond(i), candidates.ineq(i), candidates.satisfy_atom(i), candidates.hint(i), q->get_weight()); m_info->insert_macro(mc); } } public: quantifier_analyzer(model_finder& mf, ast_manager & m): m_mf(mf), m(m), m_mutil(m), m_array_util(m), m_arith_util(m), m_bv_util(m), m_info(nullptr) { } void operator()(quantifier_info * d) { m_info = d; quantifier * q = d->get_flat_q(); if (m.is_lambda_def(q)) return; expr * e = q->get_expr(); reset_cache(); SASSERT(m_ttodo.empty()); SASSERT(m_ftodo.empty()); if (is_clause(m, e)) { process_clause(e); } else { process_formula(e); } while (!m_ftodo.empty() || !m_ttodo.empty()) { process_formulas_on_stack(); process_terms_on_stack(); } collect_macro_candidates(q); m_info = nullptr; } }; /** \brief Base class for macro solvers. */ class base_macro_solver { protected: ast_manager & m; obj_map const & m_q2info; proto_model * m_model; quantifier_info * get_qinfo(quantifier * q) const { return m_q2info[q]; } void set_else_interp(func_decl * f, expr * f_else) { SASSERT(f_else != nullptr); func_interp * fi = m_model->get_func_interp(f); if (fi == nullptr) { fi = alloc(func_interp, m, f->get_arity()); m_model->register_decl(f, fi); } fi->set_else(f_else); TRACE("model_finder", tout << f->get_name() << " " << mk_pp(f_else, m) << "\n";); } virtual bool process(ptr_vector const & qs, ptr_vector & new_qs, ptr_vector & residue) = 0; public: base_macro_solver(ast_manager & m, obj_map const & q2i): m(m), m_q2info(q2i), m_model(nullptr) { } virtual ~base_macro_solver() {} /** \brief Try to satisfy quantifiers in qs by using macro definitions. Store in new_qs the quantifiers that were not satisfied. Store in residue a subset of the quantifiers that were satisfied but contain information useful for the auf_solver. */ void operator()(proto_model * m, ptr_vector const & qs, ptr_vector & new_qs, ptr_vector & residue) { m_model = m; ptr_vector curr_qs(qs); while (process(curr_qs, new_qs, residue)) { curr_qs.swap(new_qs); new_qs.reset(); } } }; /** \brief The simple macro solver satisfies quantifiers that contain (conditional) macros for a function f that does not occur in any other quantifier. Since f does not occur in any other quantifier, I don't need to track the dependencies of f. That is, recursive definition cannot be created. */ class simple_macro_solver : public base_macro_solver { protected: /** \brief Return true if \c f is in (qs\{q}) */ bool contains(func_decl * f, ptr_vector const & qs, quantifier * q) { for (quantifier * other : qs) { if (q == other) continue; quantifier_info * other_qi = get_qinfo(other); if (other_qi->contains_ng_decl(f)) return true; } return false; } bool process(quantifier * q, ptr_vector const & qs) { quantifier_info * qi = get_qinfo(q); for (cond_macro* m : qi->macros()) { if (!m->satisfy_atom()) continue; func_decl * f = m->get_f(); if (!contains(f, qs, q)) { qi->set_the_one(f); expr * f_else = m->get_def(); SASSERT(f_else != nullptr); // Remark: I can ignore the conditions of m because // I know the (partial) interpretation of f satisfied the ground part. // MBQI will force extra instantiations if the (partial) interpretation of f // does not satisfy the quantifier. // In all other cases the "else" of f will satisfy the quantifier. set_else_interp(f, f_else); TRACE("model_finder", tout << "satisfying the quantifier using simple macro:\n"; m->display(tout); tout << "\n";); return true; // satisfied quantifier } } return false; } bool process(ptr_vector const & qs, ptr_vector & new_qs, ptr_vector & residue) override { bool removed = false; for (quantifier* q : qs) { if (process(q, qs)) removed = true; else new_qs.push_back(q); } return removed; } public: simple_macro_solver(ast_manager & m, obj_map const & q2i): base_macro_solver(m, q2i) {} }; class hint_solver : public base_macro_solver { /* This solver tries to satisfy quantifiers by using macros, cond_macros and hints. The idea is to satisfy a set of quantifiers Q = Q_{f_1} union ... union Q_{f_n} where Q_{f_i} is the set of quantifiers that contain the function f_i. Let f_i = def_i be macros (in this solver conditions are ignored). Let Q_{f_i = def_i} be the set of quantifiers where f_i = def_i is a macro. Then, the set Q can be satisfied using f_1 = def_1 ... f_n = def_n when Q_{f_1} union ... union Q_{f_n} = Q_{f_1 = def_1} ... Q_{f_n = def_n} (*) So, given a set of macros f_1 = def_1, ..., f_n = d_n, it is very easy to check whether they can be used to satisfy all quantifiers that use f_1, ..., f_n in non ground terms. We can find the sets of f_1 = def_1, ..., f_n = def_n that satisfy Q using the following search procedure find(Q) for each f_i = def_i in Q R = greedy(Q_{f_i = def_1}, Q_f_i \ Q_{f_i = def_i}, {f_i}, {f_i = def_i}) if (R != empty-set) return R greedy(Satisfied, Residue, F, F_DEF) if Residue = empty-set return F_DEF for each f_j = def_j in Residue such that f_j not in F New-Satisfied = Satisfied union Q_{f_j = def_j} New-Residue = (Residue union Q_{f_j}) \ New-Satisfied R = greedy(New-Satisfied, New-Residue, F \union {f_j}, F_DEF union {f_j = def_j}) if (R != empty-set) return R This search may be too expensive, and is exponential on the number of different function symbols. Some observations to prune the search space. 1) If f_i occurs in a quantifier without macros, then f_i and any macro using it can be ignored during the search. 2) If f_i = def_i is not a macro in a quantifier q, and there is no other f_j = def_j (i != j) in q, then f_i = def_i can be ignored during the search. */ typedef obj_hashtable quantifier_set; typedef obj_map q_f; typedef obj_pair_map q_f_def; typedef obj_pair_hashtable f_def_set; typedef obj_hashtable expr_set; typedef obj_map f2defs; q_f m_q_f; q_f_def m_q_f_def; ptr_vector m_qsets; f2defs m_f2defs; ptr_vector m_esets; void insert_q_f(quantifier * q, func_decl * f) { SASSERT(!m_forbidden.contains(f)); quantifier_set * s = nullptr; if (!m_q_f.find(f, s)) { s = alloc(quantifier_set); m_q_f.insert(f, s); m_qsets.push_back(s); } SASSERT(s != nullptr); s->insert(q); } void insert_f2def(func_decl * f, expr * def) { expr_set * s = nullptr; if (!m_f2defs.find(f, s)) { s = alloc(expr_set); m_f2defs.insert(f, s); m_esets.push_back(s); } SASSERT(s != nullptr); s->insert(def); } void insert_q_f_def(quantifier * q, func_decl * f, expr * def) { SASSERT(!m_forbidden.contains(f)); quantifier_set * s = nullptr; if (!m_q_f_def.find(f, def, s)) { s = alloc(quantifier_set); m_q_f_def.insert(f, def, s); insert_f2def(f, def); m_qsets.push_back(s); } SASSERT(s != nullptr); s->insert(q); } quantifier_set * get_q_f(func_decl * f) { return m_q_f[f]; } quantifier_set * get_q_f_def(func_decl * f, expr * def) { quantifier_set * s = nullptr; m_q_f_def.find(f, def, s); SASSERT(s != nullptr); return s; } expr_set * get_f_defs(func_decl * f) { return m_f2defs[f]; } void reset_q_fs() { std::for_each(m_qsets.begin(), m_qsets.end(), delete_proc()); std::for_each(m_esets.begin(), m_esets.end(), delete_proc()); m_q_f.reset(); m_q_f_def.reset(); m_qsets.reset(); m_f2defs.reset(); m_esets.reset(); } func_decl_set m_forbidden; func_decl_set m_candidates; bool is_candidate(quantifier * q) const { quantifier_info * qi = get_qinfo(q); for (cond_macro* m : qi->macros()) { if (m->satisfy_atom() && !m_forbidden.contains(m->get_f())) return true; } return false; } void register_decls_as_forbidden(quantifier * q) { quantifier_info * qi = get_qinfo(q); func_decl_set const & ng_decls = qi->get_ng_decls(); for (func_decl* f : ng_decls) { m_forbidden.insert(f); } } void preprocess(ptr_vector const & qs, ptr_vector & qcandidates, ptr_vector & non_qcandidates) { ptr_vector curr(qs); while (true) { for (quantifier * q : curr) { if (is_candidate(q)) { qcandidates.push_back(q); } else { register_decls_as_forbidden(q); non_qcandidates.push_back(q); } } if (curr.size() == qcandidates.size()) return; SASSERT(qcandidates.size() < curr.size()); curr.swap(qcandidates); qcandidates.reset(); } } void mk_q_f_defs(ptr_vector const & qs) { for (quantifier * q : qs) { quantifier_info * qi = get_qinfo(q); func_decl_set const & ng_decls = qi->get_ng_decls(); for (func_decl* f : ng_decls) { if (!m_forbidden.contains(f)) insert_q_f(q, f); } for (cond_macro * m : qi->macros()) { if (m->satisfy_atom() && !m_forbidden.contains(m->get_f())) { insert_q_f_def(q, m->get_f(), m->get_def()); m_candidates.insert(m->get_f()); } } } } static void display_quantifier_set(std::ostream & out, quantifier_set const * s) { for (quantifier* q : *s) { out << q->get_qid() << " "; } out << "\n"; } void display_qcandidates(std::ostream & out, ptr_vector const & qcandidates) const { for (quantifier * q : qcandidates) { out << q->get_qid() << " ->\n" << mk_pp(q, m) << "\n"; quantifier_info * qi = get_qinfo(q); qi->display(out); out << "------\n"; } out << "Sets Q_f\n"; for (auto const& kv : m_q_f) { func_decl * f = kv.m_key; quantifier_set * s = kv.m_value; out << f->get_name() << " -> "; display_quantifier_set(out, s); } out << "Sets Q_{f = def}\n"; for (auto const& kv : m_q_f_def) { func_decl * f = kv.get_key1(); expr * def = kv.get_key2(); quantifier_set * s = kv.get_value(); out << f->get_name() << " " << mk_pp(def, m) << " ->\n"; display_quantifier_set(out, s); } } // // Search: main procedures // struct ev_handler { hint_solver * m_owner; void operator()(quantifier * q, bool ins) { quantifier_info * qi = m_owner->get_qinfo(q); qi->set_the_one(nullptr); } ev_handler(hint_solver * o): m_owner(o) { } }; typedef backtrackable_set qset; typedef backtrackable_set qsset; typedef obj_map f2def; qset m_residue; qsset m_satisfied; f2def m_fs; // set of function symbols (and associated interpretation) that were used to satisfy the quantifiers in m_satisfied. struct found_satisfied_subset {}; void display_search_state(std::ostream & out) const { out << "fs:\n"; for (auto const& kv : m_fs) { out << kv.m_key->get_name() << " "; } out << "\nsatisfied:\n"; for (auto q : m_satisfied) { out << q->get_qid() << " "; } out << "\nresidue:\n"; for (auto q : m_residue) { out << q->get_qid() << " "; } out << "\n"; } bool check_satisfied_residue_invariant() { DEBUG_CODE( for (quantifier * q : m_satisfied) { SASSERT(!m_residue.contains(q)); quantifier_info * qi = get_qinfo(q); SASSERT(qi != nullptr); SASSERT(qi->get_the_one() != nullptr); }); return true; } bool update_satisfied_residue(func_decl * f, expr * def) { bool useful = false; SASSERT(check_satisfied_residue_invariant()); quantifier_set * q_f = get_q_f(f); quantifier_set * q_f_def = get_q_f_def(f, def); for (quantifier * q : *q_f_def) { if (!m_satisfied.contains(q)) { useful = true; m_residue.erase(q); m_satisfied.insert(q); quantifier_info * qi = get_qinfo(q); SASSERT(qi->get_the_one() == 0); qi->set_the_one(f); // remark... event handler will reset it during backtracking. } } if (!useful) return false; for (quantifier * q : *q_f) { if (!m_satisfied.contains(q)) { m_residue.insert(q); } } SASSERT(check_satisfied_residue_invariant()); return true; } /** \brief Extract from m_residue, func_decls that can be used as macros to satisfy it. The candidates must not be elements of m_fs. */ void get_candidates_from_residue(func_decl_set & candidates) { for (quantifier * q : m_residue) { quantifier_info * qi = get_qinfo(q); for (cond_macro * m : qi->macros()) { func_decl * f = m->get_f(); if (m->satisfy_atom() && !m_forbidden.contains(f) && !m_fs.contains(f)) { candidates.insert(f); } } } } #define GREEDY_MAX_DEPTH 10 /* to avoid too expensive search spaces */ /** \brief Try to reduce m_residue using the macros of f. */ void greedy(func_decl * f, unsigned depth) { if (depth >= GREEDY_MAX_DEPTH) return; // failed TRACE("model_finder_hint", tout << "greedy depth: " << depth << ", f: " << f->get_name() << "\n"; display_search_state(tout);); expr_set * s = get_f_defs(f); for (expr * def : *s) { SASSERT(!m_fs.contains(f)); m_satisfied.push_scope(); m_residue.push_scope(); TRACE("model_finder", tout << f->get_name() << " " << mk_pp(def, m) << "\n";); m_fs.insert(f, def); if (update_satisfied_residue(f, def)) { // update was useful greedy(depth + 1); // greedy throws exception in case of success // reachable iff greedy failed. } m_satisfied.pop_scope(); m_residue.pop_scope(); m_fs.erase(f); } } /** \brief check if satisfied subset introduces a cyclic dependency. f_1 = def_1(f_2), ..., f_n = def_n(f_1) */ expr_mark m_visited; obj_hashtable m_acyclic; bool is_cyclic() { m_acyclic.reset(); while (true) { unsigned sz = m_acyclic.size(); if (sz == m_fs.size()) return false; // there are no cyclic dependencies for (auto const& kv : m_fs) { func_decl * f = kv.m_key; if (m_acyclic.contains(f)) continue; if (is_acyclic(kv.m_value)) m_acyclic.insert(f); } if (sz == m_acyclic.size()) return true; // no progress, so dependency cycle found. } } struct occurs {}; struct occurs_check { hint_solver& m_cls; occurs_check(hint_solver& hs): m_cls(hs) {} void operator()(app* n) { if (m_cls.m_fs.contains(n->get_decl()) && !m_cls.m_acyclic.contains(n->get_decl())) throw occurs(); } void operator()(var* n) {} void operator()(quantifier* n) {} }; bool is_acyclic(expr* def) { m_visited.reset(); occurs_check oc(*this); try { for_each_expr(oc, m_visited, def); } catch (const occurs &) { return false; } return true; } /** \brief Try to reduce m_residue (if not empty) by selecting a function f that is a macro in the residue. */ void greedy(unsigned depth) { if (m_residue.empty()) { if (is_cyclic()) return; TRACE("model_finder_hint", tout << "found subset that is satisfied by macros\n"; display_search_state(tout);); throw found_satisfied_subset(); } func_decl_set candidates; get_candidates_from_residue(candidates); TRACE("model_finder_hint", tout << "candidates from residue:\n"; for (func_decl * f : candidates) { tout << f->get_name() << " "; } tout << "\n";); for (func_decl* f : candidates) { greedy(f, depth); } } /** \brief Try to find a set of quantifiers by starting to use the macros of f. This is the "find" procedure in the comments above. The set of satisfied quantifiers is in m_satisfied, and the remaining to be satisfied in m_residue. When the residue becomes empty we throw the exception found_satisfied_subset. */ void process(func_decl * f) { SASSERT(m_satisfied.empty()); SASSERT(m_residue.empty()); greedy(f, 0); } /** \brief Copy the quantifiers from qcandidates to new_qs that are not in m_satisfied. */ void copy_non_satisfied(ptr_vector const & qcandidates, ptr_vector & new_qs) { for (quantifier * q : qcandidates) { if (!m_satisfied.contains(q)) new_qs.push_back(q); } } /** \brief Use m_fs to set the interpretation of the function symbols that were used to satisfy the quantifiers in m_satisfied. */ void set_interp() { for (auto const& kv : m_fs) { func_decl * f = kv.m_key; expr * def = kv.m_value; set_else_interp(f, def); } } void reset() { reset_q_fs(); m_forbidden.reset(); m_candidates.reset(); m_satisfied.reset(); m_residue.reset(); m_fs.reset(); } bool process(ptr_vector const & qs, ptr_vector & new_qs, ptr_vector & residue) override { reset(); ptr_vector qcandidates; preprocess(qs, qcandidates, new_qs); if (qcandidates.empty()) { SASSERT(new_qs.size() == qs.size()); return false; } mk_q_f_defs(qcandidates); TRACE("model_finder_hint", tout << "starting hint-solver search using:\n"; display_qcandidates(tout, qcandidates);); for (func_decl * f : m_candidates) { try { process(f); } catch (const found_satisfied_subset &) { set_interp(); copy_non_satisfied(qcandidates, new_qs); return true; } } // failed... copy everything to new_qs new_qs.append(qcandidates); return false; } public: hint_solver(ast_manager & m, obj_map const & q2i): base_macro_solver(m, q2i), m_satisfied(ev_handler(this)) { } ~hint_solver() override { reset(); } }; /** \brief Satisfy clauses that are not in the AUF fragment but define conditional macros. These clauses are eliminated even if the symbol being defined occurs in other quantifiers. The auf_solver is ineffective in these clauses. \remark Full macros are used even if they are in the AUF fragment. */ class non_auf_macro_solver : public base_macro_solver { func_decl_dependencies & m_dependencies; qi_params const * m_qi_params; bool add_macro(func_decl * f, expr * f_else) { TRACE("model_finder", tout << "trying to add macro for " << f->get_name() << "\n" << mk_pp(f_else, m) << "\n";); func_decl_set * s = m_dependencies.mk_func_decl_set(); m_dependencies.collect_ng_func_decls(f_else, s); if (!m_dependencies.insert(f, s)) { TRACE("model_finder", tout << "failed to add macro\n";); return false; // cyclic dependency } set_else_interp(f, f_else); return true; } // Return true if r1 is a better macro than r2. bool is_better_macro(cond_macro * r1, cond_macro * r2) { if (r2 == nullptr || !r1->is_hint()) return true; if (!r2->is_hint()) return false; SASSERT(r1->is_hint() && r2->is_hint()); if (is_ground(r1->get_def()) && !is_ground(r2->get_def())) return true; return false; } cond_macro * get_macro_for(func_decl * f, quantifier * q) { cond_macro * r = nullptr; quantifier_info * qi = get_qinfo(q); for (cond_macro * m : qi->macros()) { if (m->get_f() == f && !m->is_hint() && is_better_macro(m, r)) r = m; } return r; } typedef std::pair mq_pair; void collect_candidates(ptr_vector const & qs, obj_map & full_macros, func_decl_set & cond_macros) { for (quantifier * q : qs) { quantifier_info * qi = get_qinfo(q); for (cond_macro * m : qi->macros()) { if (!m->is_hint()) { func_decl * f = m->get_f(); TRACE("model_finder", tout << "considering macro for: " << f->get_name() << "\n"; m->display(tout); tout << "\n";); SASSERT(m_qi_params != nullptr); if (m->is_unconditional() && (!qi->is_auf() || m->get_weight() >= m_qi_params->m_mbqi_force_template)) { full_macros.insert(f, std::make_pair(m, q)); cond_macros.erase(f); } else if (!full_macros.contains(f) && !qi->is_auf()) cond_macros.insert(f); } } } } void process_full_macros(obj_map const & full_macros, obj_hashtable & removed) { for (auto const& kv : full_macros) { func_decl * f = kv.m_key; cond_macro * m = kv.m_value.first; quantifier * q = kv.m_value.second; SASSERT(m->is_unconditional()); if (add_macro(f, m->get_def())) { get_qinfo(q)->set_the_one(f); removed.insert(q); } } } void process(func_decl * f, ptr_vector const & qs, obj_hashtable & removed) { expr_ref fi_else(m); ptr_buffer to_remove; for (quantifier * q : qs) { if (removed.contains(q)) continue; cond_macro * cm = get_macro_for(f, q); if (!cm) continue; SASSERT(!cm->is_hint()); if (cm->is_unconditional()) return; // f is part of a full macro... ignoring it. to_remove.push_back(q); if (fi_else.get() == nullptr) { fi_else = cm->get_def(); } else { fi_else = m.mk_ite(cm->get_cond(), cm->get_def(), fi_else); } } if (fi_else.get() != nullptr && add_macro(f, fi_else)) { for (quantifier * q : to_remove) { get_qinfo(q)->set_the_one(f); removed.insert(q); } } } void process_cond_macros(func_decl_set const & cond_macros, ptr_vector const & qs, obj_hashtable & removed) { for (func_decl * f : cond_macros) { process(f, qs, removed); } } bool process(ptr_vector const & qs, ptr_vector & new_qs, ptr_vector & residue) override { obj_map full_macros; func_decl_set cond_macros; obj_hashtable removed; // Possible improvement: sort full_macros & cond_macros using an user provided precedence function. collect_candidates(qs, full_macros, cond_macros); process_full_macros(full_macros, removed); process_cond_macros(cond_macros, qs, removed); for (quantifier * q : qs) { if (removed.contains(q)) continue; new_qs.push_back(q); residue.push_back(q); } return !removed.empty(); } public: non_auf_macro_solver(ast_manager & m, obj_map const & q2i, func_decl_dependencies & d): base_macro_solver(m, q2i), m_dependencies(d), m_qi_params(nullptr) { } void set_params(qi_params const & p) { SASSERT(m_qi_params == 0); m_qi_params = &p; } }; }; // ----------------------------------- // // model finder // // ----------------------------------- model_finder::model_finder(ast_manager & m): m(m), m_context(nullptr), m_analyzer(alloc(quantifier_analyzer, *this, m)), m_auf_solver(alloc(auf_solver, m)), m_dependencies(m), m_sm_solver(alloc(simple_macro_solver, m, m_q2info)), m_hint_solver(alloc(hint_solver, m, m_q2info)), m_nm_solver(alloc(non_auf_macro_solver, m, m_q2info, m_dependencies)), m_new_constraints(m) { } model_finder::~model_finder() { reset(); } void model_finder::checkpoint() { checkpoint("model_finder"); } void model_finder::checkpoint(char const* msg) { if (m_context && m_context->get_cancel_flag()) throw tactic_exception(m_context->get_manager().limit().get_cancel_msg()); } mf::quantifier_info * model_finder::get_quantifier_info(quantifier * q) const { return m_q2info[q]; } void model_finder::set_context(context * ctx) { SASSERT(m_context == 0); m_context = ctx; m_auf_solver->set_context(ctx); m_nm_solver->set_params(ctx->get_fparams()); } void model_finder::register_quantifier(quantifier * q) { TRACE("model_finder", tout << "registering:\n" << mk_pp(q, m) << "\n";); quantifier_info * new_info = alloc(quantifier_info, *this, m, q); m_q2info.insert(q, new_info); m_quantifiers.push_back(q); m_analyzer->operator()(new_info); TRACE("model_finder", tout << "after analyzer:\n"; new_info->display(tout);); } void model_finder::push_scope() { m_scopes.push_back(scope()); scope & s = m_scopes.back(); s.m_quantifiers_lim = m_quantifiers.size(); } void model_finder::restore_quantifiers(unsigned old_size) { unsigned curr_size = m_quantifiers.size(); SASSERT(old_size <= curr_size); for (unsigned i = old_size; i < curr_size; i++) { quantifier * q = m_quantifiers[i]; SASSERT(m_q2info.contains(q)); quantifier_info * info = get_quantifier_info(q); m_q2info.erase(q); dealloc(info); } m_quantifiers.shrink(old_size); } void model_finder::pop_scope(unsigned num_scopes) { unsigned lvl = m_scopes.size(); SASSERT(num_scopes <= lvl); unsigned new_lvl = lvl - num_scopes; scope & s = m_scopes[new_lvl]; restore_quantifiers(s.m_quantifiers_lim); m_scopes.shrink(new_lvl); } void model_finder::reset() { m_scopes.reset(); m_dependencies.reset(); restore_quantifiers(0); SASSERT(m_q2info.empty()); SASSERT(m_quantifiers.empty()); } void model_finder::init_search_eh() { // do nothing in the current version } void model_finder::collect_relevant_quantifiers(ptr_vector & qs) const { for (quantifier * q : m_quantifiers) { if (m_context->is_relevant(q) && m_context->get_assignment(q) == l_true) qs.push_back(q); } } void model_finder::process_auf(ptr_vector const & qs, proto_model * mdl) { m_auf_solver->reset(); m_auf_solver->set_model(mdl); for (quantifier * q : qs) { quantifier_info * qi = get_quantifier_info(q); qi->process_auf(*(m_auf_solver.get()), m_context); } m_auf_solver->mk_instantiation_sets(); for (quantifier * q : qs) { quantifier_info * qi = get_quantifier_info(q); qi->populate_inst_sets(*(m_auf_solver.get()), m_context); } m_auf_solver->fix_model(m_new_constraints); TRACE("model_finder", for (quantifier * q : qs) { quantifier_info * qi = get_quantifier_info(q); quantifier * fq = qi->get_flat_q(); tout << "#" << fq->get_id() << " ->\n" << mk_pp(fq, m) << "\n"; } m_auf_solver->display_nodes(tout);); } void model_finder::process_simple_macros(ptr_vector & qs, ptr_vector & residue, proto_model * m) { ptr_vector new_qs; m_sm_solver->operator()(m, qs, new_qs, residue); qs.swap(new_qs); TRACE("model_finder", tout << "model after processing simple macros:\n"; model_pp(tout, *m);); } void model_finder::process_hint_macros(ptr_vector & qs, ptr_vector & residue, proto_model * m) { ptr_vector new_qs; m_hint_solver->operator()(m, qs, new_qs, residue); qs.swap(new_qs); TRACE("model_finder", tout << "model after processing simple macros:\n"; model_pp(tout, *m);); } void model_finder::process_non_auf_macros(ptr_vector & qs, ptr_vector & residue, proto_model * m) { ptr_vector new_qs; m_nm_solver->operator()(m, qs, new_qs, residue); qs.swap(new_qs); TRACE("model_finder", tout << "model after processing non auf macros:\n"; model_pp(tout, *m);); } /** \brief Clean leftovers from previous invocations to fix_model. */ void model_finder::cleanup_quantifier_infos(ptr_vector const & qs) { for (quantifier* q : qs) { get_quantifier_info(q)->reset_the_one(); } } /** \brief Try to satisfy quantifiers by modifying the model while preserving the satisfiability of all ground formulas asserted into the logical context. */ void model_finder::fix_model(proto_model * m) { if (m_quantifiers.empty()) return; ptr_vector qs; ptr_vector residue; collect_relevant_quantifiers(qs); if (qs.empty()) return; TRACE("model_finder", tout << "trying to satisfy quantifiers, given model:\n"; model_pp(tout, *m);); cleanup_quantifier_infos(qs); m_dependencies.reset(); process_simple_macros(qs, residue, m); process_hint_macros(qs, residue, m); process_non_auf_macros(qs, residue, m); qs.append(residue); process_auf(qs, m); } quantifier * model_finder::get_flat_quantifier(quantifier * q) const { quantifier_info * qinfo = get_quantifier_info(q); SASSERT(qinfo); return qinfo->get_flat_q(); } /** \brief Return the instantiation set associated with var i of q. \remark q is the quantifier before flattening. */ mf::instantiation_set const * model_finder::get_uvar_inst_set(quantifier * q, unsigned i) const { quantifier * flat_q = get_flat_quantifier(q); SASSERT(flat_q->get_num_decls() >= q->get_num_decls()); instantiation_set const * r = m_auf_solver->get_uvar_inst_set(flat_q, flat_q->get_num_decls() - q->get_num_decls() + i); TRACE("model_finder", tout << "q: #" << q->get_id() << "\n" << mk_pp(q,m) << "\nflat_q: " << mk_pp(flat_q, m) << "\ni: " << i << " " << flat_q->get_num_decls() - q->get_num_decls() + i << "\n";); if (r != nullptr) return r; // quantifier was not processed by AUF solver... // it must have been satisfied by "macro"/"hint". quantifier_info * qinfo = get_quantifier_info(q); SASSERT(qinfo); SASSERT(qinfo->get_the_one() != nullptr); return qinfo->get_macro_based_inst_set(i, m_context, *(m_auf_solver.get())); } /** \brief Return an expression in the instantiation-set of q:i that evaluates to val. \remark q is the quantifier before flattening. Store in generation the generation of the result */ expr * model_finder::get_inv(quantifier * q, unsigned i, expr * val, unsigned & generation) const { instantiation_set const * s = get_uvar_inst_set(q, i); if (s == nullptr) return nullptr; expr * t = s->get_inv(val); if (t != nullptr) { generation = s->get_generation(t); } return t; } /** \brief Assert constraints restricting the possible values of the skolem constants can be assigned to. The idea is to restrict them to the values in the instantiation sets. \remark q is the quantifier before flattening. Return true if something was asserted. */ bool model_finder::restrict_sks_to_inst_set(context * aux_ctx, quantifier * q, expr_ref_vector const & sks) { // Note: we currently add instances of q instead of flat_q. // If the user wants instances of flat_q, it should use PULL_NESTED_QUANTIFIERS=true. This option // will guarantee that q == flat_q. // // Since we only care about q (and its bindings), it only makes sense to restrict the variables of q. bool asserted_something = false; unsigned num_decls = q->get_num_decls(); // Remark: sks were created for the flat version of q. SASSERT(get_flat_quantifier(q)->get_num_decls() == sks.size()); SASSERT(sks.size() >= num_decls); for (unsigned i = 0; i < num_decls; i++) { expr * sk = sks.get(num_decls - i - 1); instantiation_set const * s = get_uvar_inst_set(q, i); if (s == nullptr) continue; // nothing to do obj_map const & inv = s->get_inv_map(); if (inv.empty()) continue; // nothing to do ptr_buffer eqs; for (auto const& kv : inv) { expr * val = kv.m_key; eqs.push_back(m.mk_eq(sk, val)); } expr_ref new_cnstr(m); new_cnstr = m.mk_or(eqs.size(), eqs.c_ptr()); TRACE("model_finder", tout << "assert_restriction:\n" << mk_pp(new_cnstr, m) << "\n";); aux_ctx->assert_expr(new_cnstr); asserted_something = true; } return asserted_something; } void model_finder::restart_eh() { unsigned sz = m_new_constraints.size(); if (sz > 0) { for (unsigned i = 0; i < sz; i++) { expr * c = m_new_constraints.get(i); TRACE("model_finder_bug_detail", tout << "asserting new constraint: " << mk_pp(c, m) << "\n";); m_context->internalize(c, true); literal l(m_context->get_literal(c)); m_context->mark_as_relevant(l); // asserting it as an AXIOM m_context->assign(l, b_justification()); } m_new_constraints.reset(); } } }; z3-z3-4.8.7/src/smt/smt_model_finder.h000066400000000000000000000114011356505360400175010ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_model_finder.h Abstract: Model finding goodies for universally quantified formulas. During the search, the finder store information about the quantifiers that are internalized. In an ideal world, quantifiers are only internalized at base level. Given a satisfiable ground formula, Z3 will restrict the interpretation of uninterpreted functions in a finite subset of its domain. The model finder tries to produce a complete interpretation that will also satisfy all universally quantified formulas. During model construction, the model finder will complete the interpretation of uninterpreted functions by propagating basic constraints induced by the body of universally quantified formulas. More information can be found in the following papers: - Complete instantiation for quantified SMT formulas, Yeting Ge and Leonardo de Moura, Conference on Computer Aided Verification (CAV 2009), Grenoble, France, 2009. - Efficiently Solving Quantified Bit-Vector Formula, Christoph Wintersteiger, Youssef Hamadi and Leonardo de Moura, FMCAD, Lugano, Switzerland, 2010. - Bugs, Moles and Skeletons: Symbolic Reasoning for Software Development, Leonardo de Moura, Nikolaj Bjorner, IJCAR, Edinburgh, Scotland, 2010. Author: Leonardo de Moura (leonardo) 2010-12-17. Revision History: --*/ #ifndef SMT_MODEL_FINDER_H_ #define SMT_MODEL_FINDER_H_ #include "ast/ast.h" #include "ast/func_decl_dependencies.h" #include "smt/proto_model/proto_model.h" #include "tactic/tactic_exception.h" namespace smt { class context; namespace mf { class quantifier_info; class quantifier_analyzer; class auf_solver; class simple_macro_solver; class hint_solver; class non_auf_macro_solver; class instantiation_set; }; class model_finder { typedef mf::quantifier_analyzer quantifier_analyzer; typedef mf::quantifier_info quantifier_info; typedef mf::auf_solver auf_solver; typedef mf::simple_macro_solver simple_macro_solver; typedef mf::hint_solver hint_solver; typedef mf::non_auf_macro_solver non_auf_macro_solver; typedef mf::instantiation_set instantiation_set; ast_manager & m; context * m_context; scoped_ptr m_analyzer; scoped_ptr m_auf_solver; obj_map m_q2info; ptr_vector m_quantifiers; func_decl_dependencies m_dependencies; scoped_ptr m_sm_solver; scoped_ptr m_hint_solver; scoped_ptr m_nm_solver; struct scope { unsigned m_quantifiers_lim; }; svector m_scopes; expr_ref_vector m_new_constraints; // new constraints for fresh constants created by the model finder void restore_quantifiers(unsigned old_size); quantifier_info * get_quantifier_info(quantifier * q) const; void collect_relevant_quantifiers(ptr_vector & qs) const; void cleanup_quantifier_infos(ptr_vector const & qs); void process_simple_macros(ptr_vector & qs, ptr_vector & residue, proto_model * m); void process_hint_macros(ptr_vector & qs, ptr_vector & residue, proto_model * m); void process_non_auf_macros(ptr_vector & qs, ptr_vector & residue, proto_model * m); void process_auf(ptr_vector const & qs, proto_model * m); instantiation_set const * get_uvar_inst_set(quantifier * q, unsigned i) const; void checkpoint(); public: model_finder(ast_manager & m); ~model_finder(); void set_context(context * ctx); void register_quantifier(quantifier * q); void push_scope(); void pop_scope(unsigned num_scopes); void reset(); void init_search_eh(); void fix_model(proto_model * m); quantifier * get_flat_quantifier(quantifier * q) const; expr * get_inv(quantifier * q, unsigned i, expr * val, unsigned & generation) const; bool restrict_sks_to_inst_set(context * aux_ctx, quantifier * q, expr_ref_vector const & sks); void restart_eh(); void checkpoint(char const* component); }; }; #endif z3-z3-4.8.7/src/smt/smt_model_generator.cpp000066400000000000000000000476661356505360400206010ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_model_generator.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-10-29. Revision History: --*/ #include "util/ref_util.h" #include "ast/for_each_expr.h" #include "ast/ast_pp.h" #include "ast/ast_smt2_pp.h" #include "ast/array_decl_plugin.h" #include "smt/smt_context.h" #include "smt/smt_model_generator.h" #include "smt/proto_model/proto_model.h" #include "model/model_v2_pp.h" namespace smt { void fresh_value_proc::get_dependencies(buffer& result) { result.push_back(model_value_dependency(m_value)); } std::ostream& operator<<(std::ostream& out, model_value_dependency const& src) { if (src.is_fresh_value()) return out << "fresh!" << src.get_value()->get_idx(); else return out << "#" << src.get_enode()->get_owner_id(); } model_generator::model_generator(ast_manager & m): m(m), m_context(nullptr), m_fresh_idx(1), m_asts(m), m_model(nullptr) { } model_generator::~model_generator() { dec_ref_collection_values(m, m_hidden_ufs); } void model_generator::reset() { m_extra_fresh_values.reset(); m_fresh_idx = 1; m_root2value.reset(); m_asts.reset(); m_model = nullptr; } void model_generator::init_model() { SASSERT(!m_model); // PARAM-TODO smt_params ---> params_ref m_model = alloc(proto_model, m); // , m_context->get_fparams()); for (theory* th : m_context->theories()) { TRACE("model_generator_bug", tout << "init_model for theory: " << th->get_name() << "\n";); th->init_model(*this); } } /** \brief Create the boolean assignment. */ void model_generator::mk_bool_model() { unsigned sz = m_context->get_num_b_internalized(); for (unsigned i = 0; i < sz; i++) { expr * p = m_context->get_b_internalized(i); if (is_uninterp_const(p) && m_context->is_relevant(p)) { SASSERT(m.is_bool(p)); func_decl * d = to_app(p)->get_decl(); lbool val = m_context->get_assignment(p); expr * v = val == l_true ? m.mk_true() : m.mk_false(); m_model->register_decl(d, v); } } } /** \brief Create the mapping root2proc: enode-root -> model_value_proc, and roots. Store the new model_value_proc at procs. */ void model_generator::mk_value_procs(obj_map & root2proc, ptr_vector & roots, ptr_vector & procs) { for (enode * r : m_context->enodes()) { if (r == r->get_root() && m_context->is_relevant(r)) { roots.push_back(r); sort * s = m.get_sort(r->get_owner()); model_value_proc * proc = nullptr; if (m.is_bool(s)) { CTRACE("model", m_context->get_assignment(r) == l_undef, tout << mk_pp(r->get_owner(), m) << "\n";); SASSERT(m_context->get_assignment(r) != l_undef); if (m_context->get_assignment(r) == l_true) proc = alloc(expr_wrapper_proc, m.mk_true()); else proc = alloc(expr_wrapper_proc, m.mk_false()); } else { family_id fid = s->get_family_id(); theory * th = m_context->get_theory(fid); if (th && th->build_models()) { if (r->get_th_var(th->get_id()) != null_theory_var) { proc = th->mk_value(r, *this); SASSERT(proc); } else { TRACE("model", tout << "creating fresh value for #" << r->get_owner_id() << "\n";); proc = alloc(fresh_value_proc, mk_extra_fresh_value(m.get_sort(r->get_owner()))); } } else { proc = mk_model_value(r); SASSERT(proc); } } SASSERT(proc); procs.push_back(proc); root2proc.insert(r, proc); } } } model_value_proc* model_generator::mk_model_value(enode* r) { SASSERT(r == r->get_root()); expr * n = r->get_owner(); if (!m.is_model_value(n)) { sort * s = m.get_sort(r->get_owner()); n = m_model->get_fresh_value(s); CTRACE("model", n == 0, tout << mk_pp(r->get_owner(), m) << "\nsort:\n" << mk_pp(s, m) << "\n"; tout << "is_finite: " << m_model->is_finite(s) << "\n";); } return alloc(expr_wrapper_proc, to_app(n)); } #define White 0 #define Grey 1 #define Black 2 static int get_color(source2color const & colors, source const & s) { int color; if (colors.find(s, color)) return color; return White; } static void set_color(source2color & colors, source const & s, int c) { colors.insert(s, c); } static void visit_child(source const & s, source2color & colors, svector & todo, bool & visited) { if (get_color(colors, s) == White) { todo.push_back(s); visited = false; } } bool model_generator::visit_children(source const & src, ptr_vector const & roots, obj_map const & root2proc, source2color & colors, obj_hashtable & already_traversed, svector & todo) { if (src.is_fresh_value()) { // there is an implicit dependency between a fresh value stub of // sort S and the root enodes of sort S that are not associated with fresh values. // sort * s = src.get_value()->get_sort(); if (already_traversed.contains(s)) return true; bool visited = true; for (enode * r : roots) { if (m.get_sort(r->get_owner()) != s) continue; SASSERT(r == r->get_root()); if (root2proc[r]->is_fresh()) continue; // r is associated with a fresh value... TRACE("mg_top_sort", tout << "fresh!" << src.get_value()->get_idx() << " -> #" << r->get_owner_id() << " " << mk_pp(m.get_sort(r->get_owner()), m) << "\n";); visit_child(source(r), colors, todo, visited); TRACE("mg_top_sort", tout << "visited: " << visited << ", todo.size(): " << todo.size() << "\n";); } already_traversed.insert(s); return visited; } SASSERT(!src.is_fresh_value()); enode * n = src.get_enode(); SASSERT(n == n->get_root()); bool visited = true; model_value_proc * proc = root2proc[n]; buffer dependencies; proc->get_dependencies(dependencies); for (model_value_dependency const& dep : dependencies) { visit_child(dep, colors, todo, visited); } TRACE("mg_top_sort", tout << "src: " << src << " "; tout << mk_pp(n->get_owner(), m) << "\n"; for (model_value_dependency const& dep : dependencies) { tout << "#" << n->get_owner_id() << " -> " << dep << " already visited: " << visited << "\n"; } ); return visited; } void model_generator::process_source(source const & src, ptr_vector const & roots, obj_map const & root2proc, source2color & colors, obj_hashtable & already_traversed, svector & todo, svector & sorted_sources) { TRACE("mg_top_sort", tout << "process source, is_fresh: " << src.is_fresh_value() << " "; tout << src << ", todo.size(): " << todo.size() << "\n";); int color = get_color(colors, src); SASSERT(color != Grey); if (color == Black) return; SASSERT(color == White); todo.push_back(src); while (!todo.empty()) { source curr = todo.back(); TRACE("mg_top_sort", tout << "current source, is_fresh: " << curr.is_fresh_value() << " "; tout << curr << ", todo.size(): " << todo.size() << "\n";); switch (get_color(colors, curr)) { case White: set_color(colors, curr, Grey); visit_children(curr, roots, root2proc, colors, already_traversed, todo); break; case Grey: // SASSERT(visit_children(curr, roots, root2proc, colors, already_traversed, todo)); set_color(colors, curr, Black); TRACE("mg_top_sort", tout << "append " << curr << "\n";); sorted_sources.push_back(curr); break; case Black: todo.pop_back(); break; default: UNREACHABLE(); } } TRACE("mg_top_sort", tout << "END process_source, todo.size(): " << todo.size() << "\n";); } /** \brief Topological sort of 'sources'. Store result in sorted_sources. */ void model_generator::top_sort_sources(ptr_vector const & roots, obj_map const & root2proc, svector & sorted_sources) { svector todo; source2color colors; // The following 'set' of sorts is used to avoid traversing roots looking for enodes of sort S. // That is, a sort S is in already_traversed, if all enodes of sort S in roots were already traversed. obj_hashtable already_traversed; // topological sort // traverse all extra fresh values... for (extra_fresh_value * f : m_extra_fresh_values) { process_source(source(f), roots, root2proc, colors, already_traversed, todo, sorted_sources); } // traverse all enodes that are associated with fresh values... for (enode* r : roots) { if (root2proc[r]->is_fresh()) { process_source(source(r), roots, root2proc, colors, already_traversed, todo, sorted_sources); } } for (enode * r : roots) { process_source(source(r), roots, root2proc, colors, already_traversed, todo, sorted_sources); } } void model_generator::mk_values() { obj_map root2proc; ptr_vector roots; ptr_vector procs; svector sources; buffer dependencies; expr_ref_vector dependency_values(m); mk_value_procs(root2proc, roots, procs); top_sort_sources(roots, root2proc, sources); TRACE("sorted_sources", for (source const& curr : sources) { if (curr.is_fresh_value()) { tout << curr << " " << mk_pp(curr.get_value()->get_sort(), m) << "\n"; } else { enode * n = curr.get_enode(); SASSERT(n->get_root() == n); tout << mk_pp(n->get_owner(), m) << "\n"; sort * s = m.get_sort(n->get_owner()); tout << curr << " " << mk_pp(s, m); tout << " is_fresh: " << root2proc[n]->is_fresh() << "\n"; } } m_context->display(tout); ); scoped_reset _scoped_reset(*this, procs); for (source const& curr : sources) { if (curr.is_fresh_value()) { sort * s = curr.get_value()->get_sort(); TRACE("model_fresh_bug", tout << curr << " : " << mk_pp(s, m) << "\n";); expr * val = m_model->get_fresh_value(s); TRACE("model_fresh_bug", tout << curr << " := #" << (val == nullptr ? UINT_MAX : val->get_id()) << "\n";); m_asts.push_back(val); curr.get_value()->set_value(val); } else { enode * n = curr.get_enode(); SASSERT(n->get_root() == n); TRACE("mg_top_sort", tout << curr << "\n";); dependencies.reset(); dependency_values.reset(); model_value_proc * proc = root2proc[n]; SASSERT(proc); proc->get_dependencies(dependencies); for (model_value_dependency const& d : dependencies) { if (d.is_fresh_value()) { CTRACE("mg_top_sort", !d.get_value()->get_value(), tout << "#" << n->get_owner_id() << " " << mk_pp(n->get_owner(), m) << " -> " << d << "\n";); SASSERT(d.get_value()->get_value()); dependency_values.push_back(d.get_value()->get_value()); } else { enode * child = d.get_enode(); TRACE("mg_top_sort", tout << "#" << n->get_owner_id() << " (" << mk_pp(n->get_owner(), m) << "): " << mk_pp(child->get_owner(), m) << " " << mk_pp(child->get_root()->get_owner(), m) << "\n";); child = child->get_root(); dependency_values.push_back(m_root2value[child]); } } app * val = proc->mk_value(*this, dependency_values); register_value(val); m_asts.push_back(val); m_root2value.insert(n, val); } } // send model for (enode * n : m_context->enodes()) { if (is_uninterp_const(n->get_owner()) && m_context->is_relevant(n)) { func_decl * d = n->get_owner()->get_decl(); TRACE("mg_top_sort", tout << d->get_name() << " " << (m_hidden_ufs.contains(d)?"hidden":"visible") << "\n";); if (m_hidden_ufs.contains(d)) continue; expr * val = get_value(n); m_model->register_decl(d, val); } } } model_generator::scoped_reset::scoped_reset(model_generator& mg, ptr_vector& procs): mg(mg), procs(procs) {} model_generator::scoped_reset::~scoped_reset() { std::for_each(procs.begin(), procs.end(), delete_proc()); std::for_each(mg.m_extra_fresh_values.begin(), mg.m_extra_fresh_values.end(), delete_proc()); mg.m_extra_fresh_values.reset(); } app * model_generator::get_value(enode * n) const { return m_root2value[n->get_root()]; } /** \brief Return true if the interpretation of the function should be included in the model. */ bool model_generator::include_func_interp(func_decl * f) const { family_id fid = f->get_family_id(); if (fid == null_family_id) return !m_hidden_ufs.contains(f); if (fid == m.get_basic_family_id()) return false; theory * th = m_context->get_theory(fid); if (!th) return true; return th->include_func_interp(f); } /** \brief Create (partial) interpretation of function symbols. The "else" is missing. */ void model_generator::mk_func_interps() { unsigned sz = m_context->get_num_e_internalized(); for (unsigned i = 0; i < sz; i++) { expr * t = m_context->get_e_internalized(i); if (!m_context->is_relevant(t)) continue; enode * n = m_context->get_enode(t); unsigned num_args = n->get_num_args(); func_decl * f = n->get_decl(); if (num_args == 0 && include_func_interp(f)) { m_model->register_decl(f, get_value(n)); } else if (num_args > 0 && n->get_cg() == n && include_func_interp(f)) { ptr_buffer args; expr * result = get_value(n); SASSERT(result); for (unsigned j = 0; j < num_args; j++) { app * arg = get_value(n->get_arg(j)); SASSERT(arg); args.push_back(arg); } func_interp * fi = m_model->get_func_interp(f); if (fi == nullptr) { fi = alloc(func_interp, m, f->get_arity()); m_model->register_decl(f, fi); } SASSERT(m_model->has_interpretation(f)); SASSERT(m_model->get_func_interp(f) == fi); // The entry must be new because n->get_cg() == n TRACE("model", tout << "insert new entry for:\n" << mk_ismt2_pp(n->get_owner(), m) << "\nargs: "; for (unsigned i = 0; i < num_args; i++) { tout << "#" << n->get_arg(i)->get_owner_id() << " "; } tout << "\n"; tout << "value: #" << n->get_owner_id() << "\n" << mk_ismt2_pp(result, m) << "\n";); if (fi->get_entry(args.c_ptr()) == nullptr) fi->insert_new_entry(args.c_ptr(), result); } } } extra_fresh_value * model_generator::mk_extra_fresh_value(sort * s) { extra_fresh_value * r = alloc(extra_fresh_value, s, m_fresh_idx); m_fresh_idx++; m_extra_fresh_values.push_back(r); return r; } expr * model_generator::get_some_value(sort * s) { SASSERT(m_model); return m_model->get_some_value(s); } void model_generator::register_value(expr * val) { SASSERT(m_model); m_model->register_value(val); } void model_generator::finalize_theory_models() { for (theory* th : m_context->theories()) th->finalize_model(*this); } void model_generator::register_existing_model_values() { for (enode * r : m_context->enodes()) { if (r == r->get_root() && m_context->is_relevant(r)) { expr * n = r->get_owner(); if (m.is_model_value(n)) { register_value(n); } } } } void model_generator::register_factory(value_factory * f) { m_model->register_factory(f); } void model_generator::register_macros() { unsigned num = m_context->get_num_macros(); TRACE("model", tout << "num. macros: " << num << "\n";); expr_ref v(m); for (unsigned i = 0; i < num; i++) { func_decl * f = m_context->get_macro_interpretation(i, v); func_interp * fi = alloc(func_interp, m, f->get_arity()); fi->set_else(v); TRACE("model", tout << f->get_name() << "\n" << mk_pp(v, m) << "\n";); m_model->register_decl(f, fi); } } proto_model * model_generator::mk_model() { SASSERT(!m_model); TRACE("model_verbose", m_context->display(tout);); init_model(); register_existing_model_values(); mk_bool_model(); mk_values(); mk_func_interps(); finalize_theory_models(); register_macros(); TRACE("model", model_v2_pp(tout, *m_model, true);); return m_model.get(); } }; z3-z3-4.8.7/src/smt/smt_model_generator.h000066400000000000000000000231411356505360400202240ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_model_generator.h Abstract: The model generator builds a (partial) model for the ground formulas in the logical context. The model finder (smt_model_finder.h) tries to extend the partial model to satisfy the quantifiers. Main invariant: the new model still satisfies the ground formulas. The model checker (smt_model_checker.h) checks whether the (new) model satisfies the quantifiers. If it doesn't, new instances are added. Author: Leonardo de Moura (leonardo) 2008-10-29. Revision History: --*/ #ifndef SMT_MODEL_GENERATOR_H_ #define SMT_MODEL_GENERATOR_H_ #include "ast/ast.h" #include "smt/smt_types.h" #include "util/obj_hashtable.h" #include "util/map.h" #include "util/ref.h" class value_factory; class proto_model; namespace smt { // ----------------------------- // // This module builds an interpretation for each relevant expression in the logical context. // // 1) The interpretation of boolean constants is their truth value in the logical context. // // 2) The interpretation of expressions associated with enodes is built using functors (model_value_proc). // Theories as arrays and datatypes many need the interpretation of some expressions to be built before the interpretation of others. // We say this is a dependency. Moreover, some values must be fresh. That is, they should be different // from all other values associated with enodes of a given sort. For example, the array theory // uses fresh values to make sure that some array constants are different from each other. // // So a dependency for building the interpretation of an enode N can be: // a) a fresh value (stub) of sort S: it must be built after the interpretation of all enodes of sort S were assigned. // // b) an enode N': the interpretation of N' must be built before the interpretation of N. // // We say a 'source' is an fresh value or an enode. Note that every dependency is a source, // but not every source is a dependency. // // We use these dependencies to sort (using a topological sort) the sources. The sorted 'sources' specify the // order the interpretations will be built. // // Assumptions regarding dependencies: // // - They are acyclic. // // - All dependencies are marked as relevant. // // - A fresh value stub of sort S depends (implicitly) on all enodes of sort S (that are not associated with fresh values). // So an enode of sort S may not have a dependency of sort S. // // ------------------------------ /** \brief Stub for extra fresh value. */ struct extra_fresh_value { sort * m_sort; unsigned m_idx; expr * m_value; public: extra_fresh_value(sort * s, unsigned idx):m_sort(s), m_idx(idx), m_value(nullptr) {} sort * get_sort() const { return m_sort; } unsigned get_idx() const { return m_idx; } void set_value(expr * n) { SASSERT(!m_value); m_value = n; } expr * get_value() const { return m_value; } }; /** \brief Theories such as arrays and datatypes may need some values to be already available when building a value. We say this a dependency. Object of this class are used to track such dependencies. Example: to build the value (cons 10 nil), the values 10 and nil should be already available. */ class model_value_dependency { bool m_fresh; //!< True if the dependency is a new fresh value; union { enode * m_enode; //!< When m_fresh == false, contains an enode dependency. extra_fresh_value * m_value; //!< When m_fresh == true, contains the sort of the fresh value }; public: model_value_dependency():m_fresh(true), m_value(nullptr) { } explicit model_value_dependency(enode * n):m_fresh(false), m_enode(n->get_root()) {} explicit model_value_dependency(extra_fresh_value * v) :m_fresh(true), m_value(v) { SASSERT(v); } bool is_fresh_value() const { return m_fresh; } enode * get_enode() const { SASSERT(!is_fresh_value()); return m_enode; } extra_fresh_value * get_value() const { SASSERT(is_fresh_value()); return m_value; } }; std::ostream& operator<<(std::ostream& out, model_value_dependency const& d); typedef model_value_dependency source; struct source_hash_proc { unsigned operator()(source const & s) const { return s.is_fresh_value() ? hash_u_u(17, s.get_value()->get_idx()) : hash_u_u(13, s.get_enode()->get_owner_id()); } }; struct source_eq_proc { bool operator()(source const & s1, source const & s2) const { if (s1.is_fresh_value() != s2.is_fresh_value()) return false; if (s1.is_fresh_value()) return s1.get_value()->get_idx() == s2.get_value()->get_idx(); else return s1.get_enode() == s2.get_enode(); } }; typedef map source2color; /** \brief Model value builder. This functor is used to specify the dependencies needed to build a value, and to build the actual value. */ class model_value_proc { public: virtual ~model_value_proc() {} /** \brief Fill result with the dependencies of this functor. That is, to invoke mk_value, the dependencies in result must be constructed. */ virtual void get_dependencies(buffer & result) {} /** \brief The array values has size equal to the size of the argument \c result in get_dependencies. It contain the values built for the dependencies. */ virtual app * mk_value(model_generator & m, expr_ref_vector const & values) = 0; /** \brief Return true if it is associated with a fresh value. */ virtual bool is_fresh() const { return false; } }; /** \brief Simple model_value_proc. It has no dependencies, and just returns a given expression. */ class expr_wrapper_proc : public model_value_proc { app * m_value; public: expr_wrapper_proc(app * v):m_value(v) {} app * mk_value(model_generator & m, expr_ref_vector const & values) override { return m_value; } }; class fresh_value_proc : public model_value_proc { extra_fresh_value * m_value; public: fresh_value_proc(extra_fresh_value * v):m_value(v) {} void get_dependencies(buffer & result) override; app * mk_value(model_generator & m, expr_ref_vector const & values) override { return to_app(values[0]); } bool is_fresh() const override { return true; } }; /** \brief Auxiliary class used during model generation. */ class model_generator { ast_manager & m; context * m_context; ptr_vector m_extra_fresh_values; unsigned m_fresh_idx; obj_map m_root2value; ast_ref_vector m_asts; ref m_model; obj_hashtable m_hidden_ufs; void init_model(); void mk_bool_model(); void mk_value_procs(obj_map & root2proc, ptr_vector & roots, ptr_vector & procs); void mk_values(); bool include_func_interp(func_decl * f) const; void mk_func_interps(); void finalize_theory_models(); void display(std::ostream & out); void register_existing_model_values(); void register_macros(); bool visit_children(source const & src, ptr_vector const & roots, obj_map const & root2proc, source2color & colors, obj_hashtable & already_traversed, svector & todo); void process_source(source const & src, ptr_vector const & roots, obj_map const & root2proc, source2color & colors, obj_hashtable & already_traversed, svector & todo, svector & sorted_sources); void top_sort_sources(ptr_vector const & roots, obj_map const & root2proc, svector & sorted_sources); struct scoped_reset { model_generator& mg; ptr_vector& procs; scoped_reset(model_generator& mg, ptr_vector& procs); ~scoped_reset(); }; public: model_generator(ast_manager & m); ~model_generator(); void reset(); void set_context(context * c) { SASSERT(m_context == 0); m_context = c; } void register_factory(value_factory * f); extra_fresh_value * mk_extra_fresh_value(sort * s); model_value_proc * mk_model_value(enode* r); expr * get_some_value(sort * s); proto_model & get_model() { SASSERT(m_model); return *m_model; } void register_value(expr * val); ast_manager & get_manager() { return m; } proto_model* mk_model(); obj_map const & get_root2value() const { return m_root2value; } app * get_value(enode * n) const; void hide(func_decl * f) { if (!m_hidden_ufs.contains(f)) { m_hidden_ufs.insert(f); m.inc_ref(f); } } }; }; #endif /* SMT_MODEL_GENERATOR_H_ */ z3-z3-4.8.7/src/smt/smt_quantifier.cpp000066400000000000000000000714151356505360400175670ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: smt_quantifier.cpp Abstract: Quantifier reasoning support for smt::context. Author: Leonardo de Moura (leonardo) 2012-02-16. Revision History: --*/ #include "ast/ast_pp.h" #include "ast/ast_ll_pp.h" #include "smt/smt_quantifier.h" #include "smt/smt_context.h" #include "smt/smt_quantifier_stat.h" #include "smt/smt_model_finder.h" #include "smt/smt_model_checker.h" #include "smt/smt_quick_checker.h" #include "smt/mam.h" #include "smt/qi_queue.h" #include "util/obj_hashtable.h" namespace smt { quantifier_manager_plugin * mk_default_plugin(); void log_single_justification(std::ostream & out, enode *en, obj_hashtable &visited, context &ctx, ast_manager &m); /** \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 & 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 (visited.find(it) == visited.end()) visited.insert(it); else break; if (!it->m_proof_is_logged) { 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) { // When the justification of an argument changes m_proof_is_logged is not reset => We need to check if the proofs of all arguments are logged. const unsigned num_args = it->get_num_args(); enode *target = it->get_trans_justification().m_target; for (unsigned i = 0; i < num_args; ++i) { 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) { out << "[eq-expl] #" << root->get_owner_id() << " root\n"; root->m_proof_is_logged = true; } } /** \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 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; theory_id th_id; switch (en->get_trans_justification().m_justification.get_kind()) { case smt::eq_justification::kind::EQUATION: lit = en->get_trans_justification().m_justification.get_literal(); out << "[eq-expl] #" << en->get_owner_id() << " lit #" << ctx.bool_var2expr(lit.var())->get_id() << " ; #" << target->get_owner_id() << "\n"; break; case smt::eq_justification::kind::AXIOM: out << "[eq-expl] #" << en->get_owner_id() << " ax ; #" << target->get_owner_id() << "\n"; break; case smt::eq_justification::kind::CONGRUENCE: if (!en->get_trans_justification().m_justification.used_commutativity()) { num_args = en->get_num_args(); for (unsigned i = 0; i < num_args; ++i) { quantifier_manager::log_justification_to_root(out, en->get_arg(i), visited, ctx, m); quantifier_manager::log_justification_to_root(out, target->get_arg(i), visited, ctx, m); } out << "[eq-expl] #" << en->get_owner_id() << " cg"; for (unsigned i = 0; i < num_args; ++i) { out << " (#" << en->get_arg(i)->get_owner_id() << " #" << target->get_arg(i)->get_owner_id() << ")"; } out << " ; #" << target->get_owner_id() << "\n"; break; } else { // The e-graph only supports commutativity for binary functions out << "[eq-expl] #" << en->get_owner_id() << " cg (#" << en->get_arg(0)->get_owner_id() << " #" << target->get_arg(1)->get_owner_id() << ") (#" << en->get_arg(1)->get_owner_id() << " #" << target->get_arg(0)->get_owner_id() << ") ; #" << target->get_owner_id() << "\n"; break; } case smt::eq_justification::kind::JUSTIFICATION: th_id = en->get_trans_justification().m_justification.get_justification()->get_from_theory(); if (th_id != null_theory_id) { symbol const theory = m.get_family_name(th_id); out << "[eq-expl] #" << en->get_owner_id() << " th " << theory.str() << " ; #" << target->get_owner_id() << "\n"; } else { out << "[eq-expl] #" << en->get_owner_id() << " unknown ; #" << target->get_owner_id() << "\n"; } break; default: out << "[eq-expl] #" << en->get_owner_id() << " unknown ; #" << target->get_owner_id() << "\n"; break; } } struct quantifier_manager::imp { quantifier_manager & m_wrapper; context & m_context; smt_params & m_params; qi_queue m_qi_queue; obj_map m_quantifier_stat; quantifier_stat_gen m_qstat_gen; ptr_vector m_quantifiers; scoped_ptr m_plugin; unsigned m_num_instances; imp(quantifier_manager & wrapper, context & ctx, smt_params & p, quantifier_manager_plugin * plugin): m_wrapper(wrapper), m_context(ctx), m_params(p), m_qi_queue(m_wrapper, ctx, p), m_qstat_gen(ctx.get_manager(), ctx.get_region()), m_plugin(plugin) { m_num_instances = 0; m_qi_queue.setup(); } ast_manager& m() const { return m_context.get_manager(); } bool has_trace_stream() const { return m().has_trace_stream(); } std::ostream & trace_stream() { return m().trace_stream(); } quantifier_stat * get_stat(quantifier * q) const { return m_quantifier_stat.find(q); } unsigned get_generation(quantifier * q) const { return get_stat(q)->get_generation(); } void add(quantifier * q, unsigned generation) { quantifier_stat * stat = m_qstat_gen(q, generation); m_quantifier_stat.insert(q, stat); m_quantifiers.push_back(q); m_plugin->add(q); } void display_stats(std::ostream & out, quantifier * q) { quantifier_stat * s = get_stat(q); unsigned num_instances = s->get_num_instances(); unsigned max_generation = s->get_max_generation(); float max_cost = s->get_max_cost(); if (num_instances > 0) { out << "[quantifier_instances] "; out.width(10); out << q->get_qid().str() << " : "; out.width(6); out << num_instances << " : "; out.width(3); out << max_generation << " : " << max_cost << "\n"; } } void del(quantifier * q) { if (m_params.m_qi_profile) { display_stats(verbose_stream(), q); } m_quantifiers.pop_back(); m_quantifier_stat.erase(q); } bool empty() const { return m_quantifiers.empty(); } bool is_shared(enode * n) const { 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, expr* def, unsigned max_generation, unsigned min_top_generation, unsigned max_top_generation, vector> & used_enodes) { max_generation = std::max(max_generation, get_generation(q)); if (m_num_instances > m_params.m_qi_max_instances) { return false; } get_stat(q)->update_max_generation(max_generation); fingerprint * f = m_context.add_fingerprint(q, q->get_id(), num_bindings, bindings, def); if (f) { if (has_trace_stream()) { 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++; } CTRACE("quantifier_", f != nullptr, tout << expr_ref(q, m()) << " "; for (unsigned i = 0; i < num_bindings; ++i) { tout << expr_ref(bindings[i]->get_owner(), m()) << " "; } tout << "\n"; ); return f != nullptr; } void init_search_eh() { m_num_instances = 0; for (quantifier * q : m_quantifiers) { get_stat(q)->reset_num_instances_curr_search(); } m_qi_queue.init_search_eh(); m_plugin->init_search_eh(); TRACE("smt_params", m_params.display(tout); ); } void assign_eh(quantifier * q) { m_plugin->assign_eh(q); } void add_eq_eh(enode * n1, enode * n2) { m_plugin->add_eq_eh(n1, n2); } void relevant_eh(enode * n) { m_plugin->relevant_eh(n); } void restart_eh() { m_plugin->restart_eh(); } void push() { m_plugin->push(); m_qi_queue.push_scope(); } void pop(unsigned num_scopes) { m_plugin->pop(num_scopes); m_qi_queue.pop_scope(num_scopes); } bool can_propagate() { return m_qi_queue.has_work() || m_plugin->can_propagate(); } void propagate() { m_plugin->propagate(); m_qi_queue.instantiate(); } bool check_quantifier(quantifier* q) { return m_context.is_relevant(q) && m_context.get_assignment(q) == l_true; // && !m().is_rec_fun_def(q); } bool quick_check_quantifiers() { if (m_params.m_qi_quick_checker == MC_NO) return true; if (m_quantifiers.empty()) return true; IF_VERBOSE(10, verbose_stream() << "quick checking quantifiers (unsat)...\n";); quick_checker mc(m_context); bool result = true; for (quantifier* q : m_quantifiers) if (check_quantifier(q) && mc.instantiate_unsat(q)) result = false; if (m_params.m_qi_quick_checker == MC_UNSAT || !result) { m_qi_queue.instantiate(); return result; } // MC_NO_SAT is too expensive (it creates too many irrelevant instances). // we should use MBQI=true instead. IF_VERBOSE(10, verbose_stream() << "quick checking quantifiers (not sat)...\n";); for (quantifier* q : m_quantifiers) if (check_quantifier(q) && mc.instantiate_not_sat(q)) result = false; m_qi_queue.instantiate(); return result; } final_check_status final_check_eh(bool full) { if (full) { IF_VERBOSE(100, if (!m_quantifiers.empty()) verbose_stream() << "(smt.final-check \"quantifiers\")\n";); final_check_status result = m_qi_queue.final_check_eh() ? FC_DONE : FC_CONTINUE; final_check_status presult = m_plugin->final_check_eh(full); if (presult != FC_DONE) result = presult; if (m_context.can_propagate()) result = FC_CONTINUE; if (result == FC_DONE && !m_params.m_qi_lazy_quick_checker && !quick_check_quantifiers()) result = FC_CONTINUE; return result; } else { return m_plugin->final_check_eh(false); } } check_model_result check_model(proto_model * m, obj_map const & root2value) { if (empty()) return SAT; return m_plugin->check_model(m, root2value); } }; quantifier_manager::quantifier_manager(context & ctx, smt_params & fp, params_ref const & p) { m_imp = alloc(imp, *this, ctx, fp, mk_default_plugin()); m_imp->m_plugin->set_manager(*this); } quantifier_manager::~quantifier_manager() { dealloc(m_imp); } context & quantifier_manager::get_context() const { return m_imp->m_context; } void quantifier_manager::set_plugin(quantifier_manager_plugin * plugin) { m_imp->m_plugin = plugin; plugin->set_manager(*this); } void quantifier_manager::add(quantifier * q, unsigned generation) { m_imp->add(q, generation); } void quantifier_manager::del(quantifier * q) { m_imp->del(q); } bool quantifier_manager::empty() const { return m_imp->empty(); } bool quantifier_manager::is_shared(enode * n) const { return m_imp->is_shared(n); } quantifier_stat * quantifier_manager::get_stat(quantifier * q) const { return m_imp->get_stat(q); } unsigned quantifier_manager::get_generation(quantifier * q) const { return m_imp->get_generation(q); } bool quantifier_manager::add_instance(quantifier * q, app * pat, unsigned num_bindings, enode * const * bindings, expr* def, unsigned max_generation, unsigned min_top_generation, unsigned max_top_generation, vector> & used_enodes) { return m_imp->add_instance(q, pat, num_bindings, bindings, def, max_generation, min_top_generation, max_generation, used_enodes); } bool quantifier_manager::add_instance(quantifier * q, unsigned num_bindings, enode * const * bindings, expr* def, unsigned generation) { vector> tmp; return add_instance(q, nullptr, num_bindings, bindings, def, generation, generation, generation, tmp); } void quantifier_manager::init_search_eh() { m_imp->init_search_eh(); } void quantifier_manager::assign_eh(quantifier * q) { m_imp->assign_eh(q); } void quantifier_manager::add_eq_eh(enode * n1, enode * n2) { m_imp->add_eq_eh(n1, n2); } void quantifier_manager::relevant_eh(enode * n) { m_imp->relevant_eh(n); } final_check_status quantifier_manager::final_check_eh(bool full) { return m_imp->final_check_eh(full); } void quantifier_manager::restart_eh() { m_imp->restart_eh(); } bool quantifier_manager::can_propagate() const { return m_imp->can_propagate(); } void quantifier_manager::propagate() { m_imp->propagate(); } bool quantifier_manager::model_based() const { return m_imp->m_plugin->model_based(); } bool quantifier_manager::mbqi_enabled(quantifier *q) const { return m_imp->m_plugin->mbqi_enabled(q); } void quantifier_manager::adjust_model(proto_model * m) { m_imp->m_plugin->adjust_model(m); } quantifier_manager::check_model_result quantifier_manager::check_model(proto_model * m, obj_map const & root2value) { return m_imp->check_model(m, root2value); } void quantifier_manager::push() { m_imp->push(); } void quantifier_manager::pop(unsigned num_scopes) { m_imp->pop(num_scopes); } void quantifier_manager::reset() { context & ctx = m_imp->m_context; smt_params & p = m_imp->m_params; quantifier_manager_plugin * plugin = m_imp->m_plugin->mk_fresh(); m_imp->~imp(); m_imp = new (m_imp) imp(*this, ctx, p, plugin); plugin->set_manager(*this); } void quantifier_manager::display(std::ostream & out) const { } void quantifier_manager::collect_statistics(::statistics & st) const { m_imp->m_qi_queue.collect_statistics(st); } void quantifier_manager::reset_statistics() { } void quantifier_manager::display_stats(std::ostream & out, quantifier * q) const { m_imp->display_stats(out, q); } ptr_vector::const_iterator quantifier_manager::begin_quantifiers() const { return m_imp->m_quantifiers.begin(); } ptr_vector::const_iterator quantifier_manager::end_quantifiers() const { return m_imp->m_quantifiers.end(); } unsigned quantifier_manager::num_quantifiers() const { return m_imp->m_quantifiers.size(); } // The default plugin uses E-matching, MBQI and quick-checker class default_qm_plugin : public quantifier_manager_plugin { quantifier_manager * m_qm; smt_params * m_fparams; context * m_context; scoped_ptr m_mam; scoped_ptr m_lazy_mam; scoped_ptr m_model_finder; scoped_ptr m_model_checker; unsigned m_new_enode_qhead; unsigned m_lazy_matching_idx; bool m_active; public: default_qm_plugin(): m_qm(nullptr), m_context(nullptr), m_new_enode_qhead(0), m_lazy_matching_idx(0), m_active(false) { } ~default_qm_plugin() override { } void set_manager(quantifier_manager & qm) override { SASSERT(m_qm == 0); m_qm = &qm; m_context = &(qm.get_context()); m_fparams = &(m_context->get_fparams()); ast_manager & m = m_context->get_manager(); m_mam = mk_mam(*m_context); m_lazy_mam = mk_mam(*m_context); m_model_finder = alloc(model_finder, m); m_model_checker = alloc(model_checker, m, *m_fparams, *(m_model_finder.get())); m_model_finder->set_context(m_context); m_model_checker->set_qm(qm); } quantifier_manager_plugin * mk_fresh() override { return alloc(default_qm_plugin); } bool model_based() const override { return m_fparams->m_mbqi; } bool mbqi_enabled(quantifier *q) const override { if (!m_fparams->m_mbqi_id) return true; const symbol &s = q->get_qid(); size_t len = strlen(m_fparams->m_mbqi_id); if (s == symbol::null || s.is_numerical()) return len == 0; return strncmp(s.bare_str(), m_fparams->m_mbqi_id, len) == 0; } /* Quantifier id's must begin with the prefix specified by parameter mbqi.id to be instantiated with MBQI. The default value is the empty string, so all quantifiers are instantiated. */ void add(quantifier * q) override { if (m_fparams->m_mbqi && mbqi_enabled(q)) { m_active = true; m_model_finder->register_quantifier(q); } } void del(quantifier * q) override { } void push() override { m_mam->push_scope(); m_lazy_mam->push_scope(); if (m_fparams->m_mbqi) { m_model_finder->push_scope(); } } void pop(unsigned num_scopes) override { m_mam->pop_scope(num_scopes); m_lazy_mam->pop_scope(num_scopes); if (m_fparams->m_mbqi) { m_model_finder->pop_scope(num_scopes); } } void init_search_eh() override { m_lazy_matching_idx = 0; if (m_fparams->m_mbqi) { m_model_finder->init_search_eh(); m_model_checker->init_search_eh(); } } void assign_eh(quantifier * q) override { m_active = true; ast_manager& m = m_context->get_manager(); if (!m_fparams->m_ematching) { return; } if (false && m.is_rec_fun_def(q) && mbqi_enabled(q)) { return; } bool has_unary_pattern = false; unsigned num_patterns = q->get_num_patterns(); for (unsigned i = 0; i < num_patterns; i++) { app * mp = to_app(q->get_pattern(i)); if (mp->get_num_args() == 1) { has_unary_pattern = true; break; } } unsigned num_eager_multi_patterns = m_fparams->m_qi_max_eager_multipatterns; if (!has_unary_pattern) num_eager_multi_patterns++; for (unsigned i = 0, j = 0; i < num_patterns; i++) { app * mp = to_app(q->get_pattern(i)); SASSERT(m.is_pattern(mp)); bool unary = (mp->get_num_args() == 1); if (m.is_rec_fun_def(q) && i > 0) { // add only the first pattern TRACE("quantifier", tout << "skip recursive function body " << mk_ismt2_pp(mp, m) << "\n";); } else if (!unary && j >= num_eager_multi_patterns) { TRACE("quantifier", tout << "delaying (too many multipatterns):\n" << mk_ismt2_pp(mp, m) << "\n" << "j: " << j << " unary: " << unary << " m_params.m_qi_max_eager_multipatterns: " << m_fparams->m_qi_max_eager_multipatterns << " num_eager_multi_patterns: " << num_eager_multi_patterns << "\n";); m_lazy_mam->add_pattern(q, mp); } else { TRACE("quantifier", tout << "adding:\n" << expr_ref(mp, m) << "\n";); m_mam->add_pattern(q, mp); } if (!unary) j++; } } bool use_ematching() const { return m_fparams->m_ematching && !m_qm->empty(); } void add_eq_eh(enode * e1, enode * e2) override { if (use_ematching()) m_mam->add_eq_eh(e1, e2); } void relevant_eh(enode * e) override { if (use_ematching()) { m_mam->relevant_eh(e, false); m_lazy_mam->relevant_eh(e, true); } } bool can_propagate() const override { return m_mam->has_work(); } void restart_eh() override { if (m_fparams->m_mbqi) { m_model_finder->restart_eh(); m_model_checker->restart_eh(); } TRACE("mam_stats", m_mam->display(tout);); } bool is_shared(enode * n) const override { return m_active && (m_mam->is_shared(n) || m_lazy_mam->is_shared(n)); } void adjust_model(proto_model * m) override { if (m_fparams->m_mbqi) { m_model_finder->fix_model(m); } } void propagate() override { m_mam->match(); if (!m_context->relevancy() && use_ematching()) { ptr_vector::const_iterator it = m_context->begin_enodes(); ptr_vector::const_iterator end = m_context->end_enodes(); unsigned sz = static_cast(end - it); if (sz > m_new_enode_qhead) { m_context->push_trail(value_trail(m_new_enode_qhead)); it += m_new_enode_qhead; while (m_new_enode_qhead < sz) { enode * e = *it; m_mam->relevant_eh(e, false); m_lazy_mam->relevant_eh(e, true); m_new_enode_qhead++; it++; } } } } quantifier_manager::check_model_result check_model(proto_model * m, obj_map const & root2value) override { if (m_fparams->m_mbqi) { IF_VERBOSE(10, verbose_stream() << "(smt.mbqi)\n";); if (m_model_checker->check(m, root2value)) { return quantifier_manager::SAT; } else if (m_model_checker->has_new_instances()) { return quantifier_manager::RESTART; } } return quantifier_manager::UNKNOWN; } final_check_status final_check_eh(bool full) override { if (!full) { if (m_fparams->m_qi_lazy_instantiation) return final_check_quant(); return FC_DONE; } else { return final_check_quant(); } } /** \brief Final check related with quantifiers... */ final_check_status final_check_quant() { if (use_ematching()) { if (m_lazy_matching_idx < m_fparams->m_qi_max_lazy_multipattern_matching) { m_lazy_mam->rematch(); m_context->push_trail(value_trail(m_lazy_matching_idx)); m_lazy_matching_idx++; } } return FC_DONE; } }; quantifier_manager_plugin * mk_default_plugin() { return alloc(default_qm_plugin); } }; z3-z3-4.8.7/src/smt/smt_quantifier.h000066400000000000000000000132711356505360400172300ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: smt_quantifier.h Abstract: Quantifier reasoning support for smt::context. Author: Leonardo de Moura (leonardo) 2012-02-16. Revision History: --*/ #ifndef SMT_QUANTIFIER_H_ #define SMT_QUANTIFIER_H_ #include "ast/ast.h" #include "util/statistics.h" #include "util/params.h" #include "smt/smt_types.h" #include class proto_model; struct smt_params; namespace smt { class quantifier_manager_plugin; class quantifier_stat; class quantifier_manager { struct imp; imp * m_imp; public: quantifier_manager(context & ctx, smt_params & fp, params_ref const & p); ~quantifier_manager(); context & get_context() const; void set_plugin(quantifier_manager_plugin * plugin); void add(quantifier * q, unsigned generation); void del(quantifier * q); bool empty() const; bool is_shared(enode * n) const; quantifier_stat * get_stat(quantifier * q) const; unsigned get_generation(quantifier * q) const; static void log_justification_to_root(std::ostream & log, enode *en, obj_hashtable &already_visited, context &ctx, ast_manager &m); bool add_instance(quantifier * q, app * pat, unsigned num_bindings, enode * const * bindings, expr* def, unsigned max_generation, unsigned min_top_generation, unsigned max_top_generation, vector> & used_enodes /*gives the equalities used for the pattern match, see mam.cpp for more info*/); bool add_instance(quantifier * q, unsigned num_bindings, enode * const * bindings, expr* def, unsigned generation = 0); void init_search_eh(); void assign_eh(quantifier * q); void add_eq_eh(enode * n1, enode * n2); void relevant_eh(enode * n); final_check_status final_check_eh(bool full); void restart_eh(); bool can_propagate() const; void propagate(); enum check_model_result { SAT, UNKNOWN, RESTART }; bool model_based() const; bool mbqi_enabled(quantifier *q) const; // can mbqi instantiate this quantifier? void adjust_model(proto_model * m); check_model_result check_model(proto_model * m, obj_map const & root2value); void push(); void pop(unsigned num_scopes); void reset(); void display(std::ostream & out) const; void display_stats(std::ostream & out, quantifier * q) const; void collect_statistics(::statistics & st) const; void reset_statistics(); ptr_vector::const_iterator begin_quantifiers() const; ptr_vector::const_iterator end_quantifiers() const; ptr_vector::const_iterator begin() const { return begin_quantifiers(); } ptr_vector::const_iterator end() const { return end_quantifiers(); } unsigned num_quantifiers() const; }; class quantifier_manager_plugin { public: quantifier_manager_plugin() {} virtual ~quantifier_manager_plugin() {} virtual void set_manager(quantifier_manager & qm) = 0; virtual quantifier_manager_plugin * mk_fresh() = 0; virtual void add(quantifier * q) = 0; virtual void del(quantifier * q) = 0; virtual bool is_shared(enode * n) const = 0; /** \brief This method is invoked whenever q is assigned to true. */ virtual void assign_eh(quantifier * q) = 0; /** \brief This method is invoked whenever n1 and n2 are merged into the same equivalence class. */ virtual void add_eq_eh(enode * n1, enode * n2) = 0; /** \brief This method is invoked whenever n is marked as relevant. */ virtual void relevant_eh(enode * n) = 0; /** \brief This method is invoked when a new search() is started. */ virtual void init_search_eh() = 0; /** \brief Final_check event handler. */ virtual final_check_status final_check_eh(bool full) = 0; /** \brief This method is invoked whenever the solver restarts. */ virtual void restart_eh() = 0; /** \brief Return true if the quantifier_manager can propagate information information back into the core. */ virtual bool can_propagate() const = 0; virtual void propagate() = 0; /** \brief Return true if the plugin is "model based" */ virtual bool model_based() const = 0; /** \brief Is "model based" instantiate allowed to instantiate this quantifier? */ virtual bool mbqi_enabled(quantifier *q) const {return true;} /** \brief Give a change to the plugin to adjust the interpretation of uninterpreted functions. It can basically change the "else" of each uninterpreted function. */ virtual void adjust_model(proto_model * m) = 0; /** \brief Core invokes this method to check whether the candidate interpretation satisfies the quantifiers in the manager. It also provides a mapping from enodes to their interpretations. */ virtual quantifier_manager::check_model_result check_model(proto_model * m, obj_map const & root2value) = 0; virtual void push() = 0; virtual void pop(unsigned num_scopes) = 0; }; }; #endif z3-z3-4.8.7/src/smt/smt_quantifier_instances.h000066400000000000000000000034401356505360400212740ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_quantifier_instances.h Abstract: Author: Leonardo de Moura (leonardo) 2008-03-26. Revision History: --*/ #ifndef SMT_QUANTIFIER_INSTANCES_H_ #define SMT_QUANTIFIER_INSTANCES_H_ namespace smt { class quantifier_instances; class quantifier_instance { quantifier * m_quantifier; double m_cost; enode * m_enodes[0]; quantifier_instance(quantifier * q, enode * const * enodes); friend class quantifier_instances; public: quantifier * get_quantifier() const { return m_quantifier; } unsigned get_num_decls() const { return m_quantifier->get_num_decls(); } double get_cost() const { return m_cost; } enode * const * get_enodes() const { return m_enodes; } bool operator==(quantifier_instance const & other) const; unsigned hash() const; }; class quantifier_instances { struct instance_lt { ptr_vector const & m_stack; instance_lt(ptr_vector const & s): m_stack(s) { } bool operator()(int i1, int i2) const { return m_stack[i1]->get_cost() < m_stack[i2]->get_cost(); } }; context & m_context; ast_manager & m_manager; obj_hashtable m_instances; //!< Set of instances. ptr_vector m_stack; //!< Stack for backtracking. heap m_queue; //!< Instantiation priority queue. unsigned_vector m_scopes; }; }; #endif /* SMT_QUANTIFIER_INSTANCES_H_ */ z3-z3-4.8.7/src/smt/smt_quantifier_stat.cpp000066400000000000000000000071561356505360400206230ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_quantifier_stat.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-02-20. Revision History: --*/ #include "smt/smt_quantifier_stat.h" namespace smt { quantifier_stat::quantifier_stat(unsigned generation): m_size(0), m_depth(0), m_generation(generation), m_case_split_factor(1), m_num_nested_quantifiers(0), m_num_instances(0), m_num_instances_curr_search(0), m_num_instances_curr_branch(0), m_max_generation(0), m_max_cost(0.0f) { } quantifier_stat_gen::quantifier_stat_gen(ast_manager & m, region & r): m_manager(m), m_region(r) { } void quantifier_stat_gen::reset() { m_already_found.reset(); m_todo.reset(); m_case_split_factor = 1; } quantifier_stat * quantifier_stat_gen::operator()(quantifier * q, unsigned generation) { reset(); quantifier_stat * r = new (m_region) quantifier_stat(generation); m_todo.push_back(entry(q->get_expr())); while (!m_todo.empty()) { entry & e = m_todo.back(); expr * n = e.m_expr; unsigned depth = e.m_depth; bool depth_only = e.m_depth_only; m_todo.pop_back(); unsigned old_depth; if (m_already_found.find(n, old_depth)) { if (old_depth >= depth) continue; depth_only = true; } m_already_found.insert(n, depth); if (depth >= r->m_depth) r->m_depth = depth; if (!depth_only) { r->m_size++; if (is_quantifier(n)) r->m_num_nested_quantifiers ++; if (is_app(n) && to_app(n)->get_family_id() == m_manager.get_basic_family_id()) { unsigned num_args = to_app(n)->get_num_args(); // Remark: I'm approximating the case_split factor. // I'm also ignoring the case split factor due to theories. switch (to_app(n)->get_decl_kind()) { case OP_OR: if (depth == 0) m_case_split_factor *= num_args; else m_case_split_factor *= (num_args + 1); break; case OP_AND: if (depth > 0) m_case_split_factor *= (num_args + 1); break; case OP_EQ: if (m_manager.is_iff(n)) { if (depth == 0) m_case_split_factor *= 4; else m_case_split_factor *= 9; } break; case OP_ITE: if (depth == 0) m_case_split_factor *= 4; else m_case_split_factor *= 9; break; default: break; } } } if (is_app(n)) { unsigned j = to_app(n)->get_num_args(); while (j > 0) { --j; m_todo.push_back(entry(to_app(n)->get_arg(j), depth + 1, depth_only)); } } } r->m_case_split_factor = m_case_split_factor.get_value(); return r; } }; z3-z3-4.8.7/src/smt/smt_quantifier_stat.h000066400000000000000000000072111356505360400202600ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_quantifier_stat.h Abstract: Author: Leonardo de Moura (leonardo) 2008-02-20. Revision History: --*/ #ifndef SMT_QUANTIFIER_STAT_H_ #define SMT_QUANTIFIER_STAT_H_ #include "ast/ast.h" #include "util/obj_hashtable.h" #include "util/approx_nat.h" #include "util/region.h" namespace smt { /** \brief Store statistics for quantifiers. This information is used to implement heuristics for quantifier instantiation. */ class quantifier_stat { unsigned m_size; unsigned m_depth; unsigned m_generation; unsigned m_case_split_factor; //!< the product of the size of the clauses created by this quantifier. unsigned m_num_nested_quantifiers; unsigned m_num_instances; unsigned m_num_instances_curr_search; unsigned m_num_instances_curr_branch; //!< only updated if QI_TRACK_INSTANCES is true unsigned m_max_generation; //!< max. generation of an instance float m_max_cost; friend class quantifier_stat_gen; quantifier_stat(unsigned generation); public: unsigned get_size() const { return m_size; } unsigned get_depth() const { return m_depth; } unsigned get_generation() const { return m_generation; } unsigned get_case_split_factor() const { return m_case_split_factor; } unsigned get_num_nested_quantifiers() const { return m_num_nested_quantifiers; } unsigned get_num_instances() const { return m_num_instances; } unsigned get_num_instances_curr_search() const { return m_num_instances_curr_search; } unsigned & get_num_instances_curr_branch() { return m_num_instances_curr_branch; } void inc_num_instances() { m_num_instances++; m_num_instances_curr_search++; } void inc_num_instances_curr_branch() { m_num_instances_curr_branch++; } void reset_num_instances_curr_search() { m_num_instances_curr_search = 0; } void update_max_generation(unsigned g) { if (m_max_generation < g) m_max_generation = g; } unsigned get_max_generation() const { return m_max_generation; } void update_max_cost(float c) { if (m_max_cost < c) m_max_cost = c; } float get_max_cost() const { return m_max_cost; } }; /** \brief Functor used to generate quantifier statistics. */ class quantifier_stat_gen { struct entry { expr * m_expr; unsigned m_depth:31; bool m_depth_only:1; //!< track only the depth of this entry. entry():m_expr(nullptr), m_depth(0), m_depth_only(false) {} entry(expr * n, unsigned depth = 0, bool depth_only = false):m_expr(n), m_depth(depth), m_depth_only(depth_only) {} }; ast_manager & m_manager; region & m_region; obj_map m_already_found; // expression to the max. depth it was reached. svector m_todo; approx_nat m_case_split_factor; void reset(); public: quantifier_stat_gen(ast_manager & m, region & r); quantifier_stat * operator()(quantifier * q, unsigned generation); }; }; #endif /* SMT_QUANTIFIER_STAT_H_ */ z3-z3-4.8.7/src/smt/smt_quick_checker.cpp000066400000000000000000000376761356505360400202330ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_quick_checker.cpp Abstract: Incomplete model checker. Author: Leonardo de Moura (leonardo) 2008-06-20. Revision History: --*/ #include "smt/smt_context.h" #include "smt/smt_quick_checker.h" #include "ast/ast_pp.h" #include namespace smt { quick_checker::collector::collector(context & c): m_context(c), m_manager(c.get_manager()), m_conservative(true) { } void quick_checker::collector::init(quantifier * q) { m_num_vars = q->get_num_decls(); m_already_found.reserve(m_num_vars+1, false); m_candidates.reserve(m_num_vars+1); m_tmp_candidates.reserve(m_num_vars+1); for (unsigned i = 0; i < m_num_vars; i++) { m_already_found[i] = false; m_candidates[i].reset(); } m_cache.reset(); } /** \brief Returns true if there is a term (f ... n' ...) in the logical context such that n' is the i-th argument of f, and n and n' are in the same equivalence class. If f == 0 then true is returned (f == 0 means there is no parent to check) */ bool quick_checker::collector::check_arg(enode * n, func_decl * f, unsigned i) { if (!f || !m_conservative) return true; for (enode * curr : m_context.enodes_of(f)) { if (m_context.is_relevant(curr) && curr->is_cgr() && i < curr->get_num_args() && curr->get_arg(i)->get_root() == n->get_root()) return true; } return false; } void quick_checker::collector::collect_core(app * n, func_decl * p, unsigned i) { func_decl * f = n->get_decl(); unsigned num_args = n->get_num_args(); for (unsigned j = 0; j < num_args; j++) { expr * arg = n->get_arg(j); if (is_var(arg)) { unsigned idx = to_var(arg)->get_idx(); if (idx >= m_num_vars) return; if (m_already_found[idx] && m_conservative) { enode_set & s = m_candidates[idx]; enode_set & ns = m_tmp_candidates[idx]; if (s.empty()) continue; ns.reset(); for (enode * curr : m_context.enodes_of(f)) { if (m_context.is_relevant(curr) && curr->is_cgr() && check_arg(curr, p, i) && j < curr->get_num_args()) { enode * arg = curr->get_arg(j)->get_root(); // intersection if (s.contains(arg)) ns.insert(arg); } } SASSERT(m_conservative); s.swap(ns); } else { m_already_found[idx] = true; enode_set & s = m_candidates[idx]; for (enode * curr : m_context.enodes_of(f)) { if (m_context.is_relevant(curr) && curr->is_cgr() && check_arg(curr, p, i) && j < curr->get_num_args()) { enode * arg = curr->get_arg(j)->get_root(); s.insert(arg); } } } } else { if (n->get_family_id() != m_manager.get_basic_family_id()) collect(arg, n->get_decl(), j); else collect(arg, nullptr, 0); } } } void quick_checker::collector::collect(expr * n, func_decl * f, unsigned idx) { if (is_quantifier(n)) return; if (is_var(n)) return; if (is_ground(n)) return; entry e(n, f, idx); if (m_cache.contains(e)) return; m_cache.insert(e); collect_core(to_app(n), f, idx); } void quick_checker::collector::save_result(vector & candidates) { candidates.reserve(m_num_vars+1); for (unsigned i = 0; i < m_num_vars; i++) { enode_vector & v = candidates[i]; v.reset(); enode_set & s = m_candidates[i]; for (enode * curr : s) { v.push_back(curr); } } TRACE("collector", tout << "candidates:\n"; for (unsigned i = 0; i < m_num_vars; i++) { tout << "var " << i << ":"; enode_vector & v = candidates[i]; for (enode * n : v) tout << " #" << n->get_owner_id(); tout << "\n"; }); } void quick_checker::collector::operator()(quantifier * q, bool conservative, vector & candidates) { flet l(m_conservative, conservative); init(q); TRACE("collector", tout << "model checking: #" << q->get_id() << "\n" << mk_pp(q, m_manager) << "\n";); collect(q->get_expr(), nullptr, 0); save_result(candidates); } quick_checker::quick_checker(context & c): m_context(c), m_manager(c.get_manager()), m_collector(c), m_new_exprs(m_manager) { } /** \brief Instantiate instances unsatisfied by the current model. Return true if new instances were generated. */ bool quick_checker::instantiate_unsat(quantifier * q) { TRACE("quick_checker", tout << "instantiate instances unsatisfied by current model\n" << mk_pp(q, m_manager) << "\n";); m_candidate_vectors.reset(); m_collector(q, true, m_candidate_vectors); m_num_bindings = q->get_num_decls(); return process_candidates(q, true); } /** \brief Instantiate instances not satisfied by the current model. Return true if new instances were generated. */ bool quick_checker::instantiate_not_sat(quantifier * q) { TRACE("quick_checker", tout << "instantiate instances not satisfied by current model\n" << mk_pp(q, m_manager) << "\n";); m_candidate_vectors.reset(); m_collector(q, false, m_candidate_vectors); m_num_bindings = q->get_num_decls(); return process_candidates(q, false); } bool quick_checker::instantiate_not_sat(quantifier * q, unsigned num_candidates, expr * const * candidates) { // initialize m_candidates using the given set of candidates. m_candidate_vectors.reset(); m_num_bindings = q->get_num_decls(); m_candidate_vectors.reserve(m_num_bindings+1); for (unsigned i = 0; i < m_num_bindings; i++) { m_candidate_vectors[i].reset(); sort * s = q->get_decl_sort(i); for (unsigned j = 0; j < num_candidates; j++) { if (m_manager.get_sort(candidates[j]) == s) { expr * n = candidates[j]; m_context.internalize(n, false); enode * e = m_context.get_enode(n); m_candidate_vectors[i].push_back(e); } } } return process_candidates(q, false); } bool quick_checker::process_candidates(quantifier * q, bool unsat) { vector> empty_used_enodes; buffer szs; buffer it; for (unsigned i = 0; i < m_num_bindings; i++) { unsigned sz = m_candidate_vectors[i].size(); if (sz == 0) return false; szs.push_back(sz); it.push_back(0); } TRACE("quick_checker_sizes", tout << mk_pp(q, m_manager) << "\n"; for (unsigned i = 0; i < szs.size(); i++) tout << szs[i] << " "; tout << "\n";); TRACE("quick_checker_candidates", tout << "candidates:\n"; for (unsigned i = 0; i < m_num_bindings; i++) { enode_vector & v = m_candidate_vectors[i]; for (enode * n : v) tout << "#" << n->get_owner_id() << " "; tout << "\n"; }); bool result = false; m_bindings.reserve(m_num_bindings+1, 0); do { for (unsigned i = 0; i < m_num_bindings; i++) m_bindings[m_num_bindings - i - 1] = m_candidate_vectors[i][it[i]]; if (!m_context.contains_instance(q, m_num_bindings, m_bindings.c_ptr())) { bool is_candidate = false; TRACE("quick_checker", tout << "processing bindings:"; for (unsigned i = 0; i < m_num_bindings; i++) tout << " #" << m_bindings[i]->get_owner_id(); tout << "\n";); if (unsat) is_candidate = check_quantifier(q, false); else is_candidate = !check_quantifier(q, true); if (is_candidate) { TRACE("quick_checker", tout << "found new candidate\n";); TRACE("quick_checker_sizes", tout << "found new candidate\n"; for (unsigned i = 0; i < m_num_bindings; i++) tout << "#" << m_bindings[i]->get_owner_id() << " "; tout << "\n";); unsigned max_generation = get_max_generation(m_num_bindings, m_bindings.c_ptr()); if (m_context.add_instance(q, nullptr /* no pattern was used */, m_num_bindings, m_bindings.c_ptr(), nullptr, max_generation, 0, // min_top_generation is only available for instances created by the MAM 0, // max_top_generation is only available for instances created by the MAM empty_used_enodes)) result = true; } } } while (product_iterator_next(szs.size(), szs.c_ptr(), it.c_ptr())); return result; } bool quick_checker::check_quantifier(quantifier * n, bool is_true) { bool r = check(n->get_expr(), is_true); m_new_exprs.reset(); m_check_cache.reset(); m_canonize_cache.reset(); return r; } bool quick_checker::all_args(app * a, bool is_true) { unsigned num_args = a->get_num_args(); for (unsigned i = 0; i < num_args; i++) if (!check(a->get_arg(i), is_true)) return false; return true; } bool quick_checker::any_arg(app * a, bool is_true) { unsigned num_args = a->get_num_args(); for (unsigned i = 0; i < num_args; i++) if (check(a->get_arg(i), is_true)) return true; return false; } bool quick_checker::check_core(expr * n, bool is_true) { SASSERT(m_manager.is_bool(n)); if (m_context.b_internalized(n) && m_context.is_relevant(n)) { lbool val = m_context.get_assignment(n); if (val != l_undef && is_true == (val == l_true)) return true; else return false; } if (!is_app(n)) return false; app * a = to_app(n); if (a->get_family_id() == m_manager.get_basic_family_id()) { switch (a->get_decl_kind()) { case OP_TRUE: return is_true; case OP_FALSE: return !is_true; case OP_NOT: return check(a->get_arg(0), !is_true); case OP_OR: return is_true ? any_arg(a, true) : all_args(a, false); case OP_AND: return is_true ? all_args(a, true) : any_arg(a, false); case OP_ITE: if (check(a->get_arg(0), true)) return check(a->get_arg(1), is_true); else if (check(a->get_arg(0), false)) return check(a->get_arg(2), is_true); else return check(a->get_arg(1), is_true) && check(a->get_arg(2), is_true); case OP_EQ: if (m_manager.is_iff(a)) { if (is_true) return (check(a->get_arg(0), true) && check(a->get_arg(1), true)) || (check(a->get_arg(0), false) && check(a->get_arg(1), false)); else return (check(a->get_arg(0), true) && check(a->get_arg(1), false)) || (check(a->get_arg(0), false) && check(a->get_arg(1), true)); } if (is_true) { return canonize(a->get_arg(0)) == canonize(a->get_arg(1)); } else { expr * lhs = canonize(a->get_arg(0)); expr * rhs = canonize(a->get_arg(1)); if (m_context.e_internalized(lhs) && m_context.is_relevant(lhs) && m_context.e_internalized(rhs) && m_context.is_relevant(rhs) && m_context.get_enode(lhs)->get_root() != m_context.get_enode(rhs)->get_root()) return true; return m_manager.are_distinct(lhs, rhs); } default: break; } } expr * new_a = canonize(a); TRACE("quick_checker_canonizer", tout << "before:\n" << mk_pp(a, m_manager) << "\nafter:\n" << mk_pp(new_a, m_manager) << "\n";); if (m_context.lit_internalized(new_a) && m_context.is_relevant(new_a)) { lbool val = m_context.get_assignment(new_a); if (val != l_undef) return is_true == (val == l_true); } if (is_true && m_manager.is_true(new_a)) return true; if (!is_true && m_manager.is_false(new_a)) return true; return false; } bool quick_checker::check(expr * n, bool is_true) { expr_bool_pair p(n, is_true); bool r; if (m_check_cache.find(p, r)) return r; r = check_core(n, is_true); m_check_cache.insert(p, r); return r; } expr * quick_checker::canonize(expr * n) { if (is_var(n)) { unsigned idx = to_var(n)->get_idx(); if (idx >= m_num_bindings) return n; // VAR 0 is stored in the last position of m_bindings return m_bindings[m_num_bindings - idx - 1]->get_root()->get_owner(); } if (m_context.e_internalized(n)) return m_context.get_enode(n)->get_root()->get_owner(); if (!is_app(n) || to_app(n)->get_num_args() == 0) return n; expr * r; if (m_canonize_cache.find(n, r)) return r; bool has_arg_enodes = true; ptr_buffer new_args; ptr_buffer new_arg_enodes; unsigned num_args = to_app(n)->get_num_args(); for (unsigned i = 0; i < num_args; i++) { expr * arg = canonize(to_app(n)->get_arg(i)); new_args.push_back(arg); if (m_context.e_internalized(arg)) new_arg_enodes.push_back(m_context.get_enode(arg)); else has_arg_enodes = false; } if (has_arg_enodes) { enode * e = m_context.get_enode_eq_to(to_app(n)->get_decl(), num_args, new_arg_enodes.c_ptr()); if (e) { m_canonize_cache.insert(n, e->get_root()->get_owner()); return e->get_root()->get_owner(); } } // substitute by values in the model for (unsigned i = 0; i < num_args; i++) { expr * arg = new_args[i]; if (m_context.e_internalized(arg)) { expr_ref new_value(m_manager); if (m_context.get_value(m_context.get_enode(arg), new_value)) { new_args[i] = new_value; m_new_exprs.push_back(new_value); } } } expr_ref new_expr(m_manager); new_expr = m_context.get_rewriter().mk_app(to_app(n)->get_decl(), num_args, new_args.c_ptr()); m_new_exprs.push_back(new_expr); m_canonize_cache.insert(n, new_expr); return new_expr; } }; z3-z3-4.8.7/src/smt/smt_quick_checker.h000066400000000000000000000073151356505360400176630ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_quick_cheker.h Abstract: Incomplete model checker. Author: Leonardo de Moura (leonardo) 2008-06-20. Revision History: --*/ #ifndef SMT_QUICK_CHECKER_H_ #define SMT_QUICK_CHECKER_H_ #include "ast/ast.h" #include "ast/rewriter/th_rewriter.h" #include "util/obj_hashtable.h" namespace smt { class context; /** \brief Simple object for finding quantifier instantiations that are falsified by the current model. \remark The code for selecting candidates is very naive. */ class quick_checker { typedef obj_hashtable enode_set; /** \brief Functor for collecting candidates for quantifier instantiation. */ class collector { context & m_context; ast_manager & m_manager; bool m_conservative; unsigned m_num_vars; svector m_already_found; // mapping from var_idx -> bool vector m_candidates; // mapping from var_idx -> set of candidates vector m_tmp_candidates; // auxiliary mapping from var_idx -> set of candidates struct entry { expr * m_expr; func_decl * m_parent; unsigned m_parent_pos; entry(expr * n = nullptr, func_decl * d = nullptr, unsigned p = 0):m_expr(n), m_parent(d), m_parent_pos(p) {} unsigned hash() const { return m_parent ? mk_mix(m_expr->get_id(), m_parent->get_id(), m_parent_pos) : m_expr->get_id(); } bool operator==(entry const & e) const { return m_expr == e.m_expr && m_parent == e.m_parent && m_parent_pos == e.m_parent_pos; } }; typedef hashtable, default_eq > cache; cache m_cache; void init(quantifier * q); bool check_arg(enode * n, func_decl * f, unsigned i); void collect_core(app * n, func_decl * p, unsigned i); void collect(expr * n, func_decl * f, unsigned idx); void save_result(vector & candidates); public: collector(context & c); void operator()(quantifier * q, bool conservative, vector & candidates); }; typedef std::pair expr_bool_pair; typedef pair_hash, int_hash> expr_bool_pair_hash; typedef map > check_cache; typedef obj_map canonize_cache; context & m_context; ast_manager & m_manager; collector m_collector; expr_ref_vector m_new_exprs; vector m_candidate_vectors; check_cache m_check_cache; canonize_cache m_canonize_cache; unsigned m_num_bindings; ptr_vector m_bindings; bool all_args(app * a, bool is_true); bool any_arg(app * a, bool is_true); bool check_core(expr * n, bool is_true); bool check(expr * n, bool is_true); bool check_quantifier(quantifier * n, bool is_true); expr * canonize(expr * n); bool process_candidates(quantifier * q, bool unsat); public: quick_checker(context & c); bool instantiate_unsat(quantifier * q); bool instantiate_not_sat(quantifier * q); bool instantiate_not_sat(quantifier * q, unsigned num_candidates, expr * const * candidates); }; }; #endif /* SMT_QUICK_CHECKER_H_ */ z3-z3-4.8.7/src/smt/smt_relevancy.cpp000066400000000000000000000567501356505360400174150ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_relevancy.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-06-04. Revision History: --*/ #include "smt/smt_context.h" #include "smt/smt_relevancy.h" #include "ast/ast_pp.h" #include "ast/ast_ll_pp.h" #include "ast/ast_smt2_pp.h" namespace smt { void relevancy_eh::mark_as_relevant(relevancy_propagator & rp, expr * n) { rp.mark_as_relevant(n); } void relevancy_eh::mark_args_as_relevant(relevancy_propagator & rp, app * n) { unsigned j = n->get_num_args(); while (j > 0) { --j; rp.mark_as_relevant(n->get_arg(j)); } } void simple_relevancy_eh::operator()(relevancy_propagator & rp) { rp.mark_as_relevant(m_target); } void pair_relevancy_eh::operator()(relevancy_propagator & rp) { if (!rp.is_relevant(m_source1)) return; if (!rp.is_relevant(m_source2)) return; rp.mark_as_relevant(m_target); } class and_relevancy_eh : public relevancy_eh { app * m_parent; public: and_relevancy_eh(app * p):m_parent(p) {} ~and_relevancy_eh() override {} void operator()(relevancy_propagator & rp) override; }; class or_relevancy_eh : public relevancy_eh { app * m_parent; public: or_relevancy_eh(app * p):m_parent(p) {} ~or_relevancy_eh() override {} void operator()(relevancy_propagator & rp) override; }; class ite_relevancy_eh : public relevancy_eh { app * m_parent; public: ite_relevancy_eh(app * p):m_parent(p) {} ~ite_relevancy_eh() override {} void operator()(relevancy_propagator & rp) override; }; class ite_term_relevancy_eh : public relevancy_eh { app * m_parent; app * m_then_eq; app * m_else_eq; public: ite_term_relevancy_eh(app * p, app * then_eq, app * else_eq):m_parent(p), m_then_eq(then_eq), m_else_eq(else_eq) {} ~ite_term_relevancy_eh() override {} void operator()(relevancy_propagator & rp) override; }; relevancy_propagator::relevancy_propagator(context & ctx): m_context(ctx) { } bool relevancy_propagator::enabled() const { return m_context.relevancy(); } region & relevancy_propagator::get_region() const { return m_context.get_region(); } ast_manager & relevancy_propagator::get_manager() const { return m_context.get_manager(); } void relevancy_propagator::add_dependency(expr * src, expr * target) { if (!enabled()) return; if (is_relevant(src)) mark_as_relevant(target); else add_handler(src, mk_relevancy_eh(simple_relevancy_eh(target))); } relevancy_eh * relevancy_propagator::mk_or_relevancy_eh(app * n) { SASSERT(get_manager().is_or(n)); return mk_relevancy_eh(or_relevancy_eh(n)); } relevancy_eh * relevancy_propagator::mk_and_relevancy_eh(app * n) { SASSERT(get_manager().is_and(n)); return mk_relevancy_eh(and_relevancy_eh(n)); } relevancy_eh * relevancy_propagator::mk_ite_relevancy_eh(app * n) { SASSERT(get_manager().is_ite(n)); return mk_relevancy_eh(ite_relevancy_eh(n)); } relevancy_eh * relevancy_propagator::mk_term_ite_relevancy_eh(app * c, app * t, app * e) { return mk_relevancy_eh(ite_term_relevancy_eh(c, t, e)); } struct relevancy_propagator_imp : public relevancy_propagator { unsigned m_qhead; expr_ref_vector m_relevant_exprs; uint_set m_is_relevant; typedef list relevancy_ehs; obj_map m_relevant_ehs; obj_map m_watches[2]; struct eh_trail { enum kind { POS_WATCH, NEG_WATCH, HANDLER }; kind m_kind; expr * m_node; eh_trail(expr * n):m_kind(HANDLER), m_node(n) {} eh_trail(expr * n, bool val):m_kind(val ? POS_WATCH : NEG_WATCH), m_node(n) {} kind get_kind() const { return m_kind; } expr * get_node() const { return m_node; } }; svector m_trail; struct scope { unsigned m_relevant_exprs_lim; unsigned m_trail_lim; }; svector m_scopes; bool m_propagating; relevancy_propagator_imp(context & ctx): relevancy_propagator(ctx), m_qhead(0), m_relevant_exprs(ctx.get_manager()), m_propagating(false) {} ~relevancy_propagator_imp() override { undo_trail(0); } relevancy_ehs * get_handlers(expr * n) { relevancy_ehs * r = nullptr; m_relevant_ehs.find(n, r); SASSERT(m_relevant_ehs.contains(n) || r == 0); return r; } void set_handlers(expr * n, relevancy_ehs * ehs) { if (ehs == nullptr) m_relevant_ehs.erase(n); else m_relevant_ehs.insert(n, ehs); } relevancy_ehs * get_watches(expr * n, bool val) { relevancy_ehs * r = nullptr; m_watches[val ? 1 : 0].find(n, r); SASSERT(m_watches[val ? 1 : 0].contains(n) || r == 0); return r; } void set_watches(expr * n, bool val, relevancy_ehs * ehs) { if (ehs == nullptr) m_watches[val ? 1 : 0].erase(n); else m_watches[val ? 1 : 0].insert(n, ehs); } void push_trail(eh_trail const & t) { get_manager().inc_ref(t.get_node()); m_trail.push_back(t); } void add_handler(expr * source, relevancy_eh * eh) override { if (!enabled()) return; if (is_relevant_core(source)) { eh->operator()(*this, source); } else { SASSERT(eh); push_trail(eh_trail(source)); set_handlers(source, new (get_region()) relevancy_ehs(eh, get_handlers(source))); } } void add_watch(expr * n, bool val, relevancy_eh * eh) override { if (!enabled()) return; lbool lval = m_context.find_assignment(n); if (!val) lval = ~lval; switch (lval) { case l_false: return; case l_undef: SASSERT(eh); set_watches(n, val, new (get_region()) relevancy_ehs(eh, get_watches(n, val))); push_trail(eh_trail(n, val)); break; case l_true: eh->operator()(*this, n, val); break; } } void add_watch(expr * n, bool val, expr * target) override { if (!enabled()) return; lbool lval = m_context.find_assignment(n); if (!val) lval = ~lval; switch (lval) { case l_false: return; case l_undef: add_watch(n, val, mk_relevancy_eh(simple_relevancy_eh(target))); break; case l_true: mark_as_relevant(target); propagate(); break; } } 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); } void push() override { m_scopes.push_back(scope()); scope & s = m_scopes.back(); s.m_relevant_exprs_lim = m_relevant_exprs.size(); s.m_trail_lim = m_trail.size(); } void pop(unsigned num_scopes) override { SASSERT(m_context.get_scope_level() == m_scopes.size()); unsigned lvl = m_scopes.size(); SASSERT(num_scopes <= lvl); unsigned new_lvl = lvl - num_scopes; scope & s = m_scopes[new_lvl]; unmark_relevant_exprs(s.m_relevant_exprs_lim); undo_trail(s.m_trail_lim); m_scopes.shrink(new_lvl); } /** \brief Unmark expressions marked as relevant. */ void unmark_relevant_exprs(unsigned old_lim) { SASSERT(old_lim <= m_relevant_exprs.size()); unsigned i = m_relevant_exprs.size(); while (i != old_lim) { --i; expr * n = m_relevant_exprs.get(i); 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); m_qhead = m_relevant_exprs.size(); } void undo_trail(unsigned old_lim) { SASSERT(old_lim <= m_trail.size()); ast_manager & m = get_manager(); unsigned i = m_trail.size(); while (i != old_lim) { --i; eh_trail & t = m_trail[i]; expr * n = t.get_node(); relevancy_ehs * ehs; switch (t.get_kind()) { case eh_trail::POS_WATCH: ehs = get_watches(n, true); SASSERT(ehs); set_watches(n, true, ehs->tail()); break; case eh_trail::NEG_WATCH: ehs = get_watches(n, false); SASSERT(ehs); set_watches(n, false, ehs->tail()); break; case eh_trail::HANDLER: ehs = get_handlers(n); SASSERT(ehs); set_handlers(n, ehs->tail()); break; default: UNREACHABLE(); break; } m.dec_ref(n); } m_trail.shrink(old_lim); } void set_relevant(expr * n) { m_is_relevant.insert(n->get_id()); m_relevant_exprs.push_back(n); m_context.relevant_eh(n); } /** \brief Mark an expression as relevant and propagate the relevancy to its descendants. */ void mark_and_propagate(expr * n) { if (!enabled()) return; if (!is_relevant_core(n)) { mark_as_relevant(n); propagate(); } } /** \brief Mark the given expression as relevant if it is not already marked. */ void mark_as_relevant(expr * n) override { if (!enabled()) return; if (!is_relevant_core(n)) { enode * e = m_context.find_enode(n); if (e != nullptr) { enode * curr = e; do { set_relevant(curr->get_owner()); curr = curr->get_next(); } while (curr != e); } else { set_relevant(n); } } } /** \brief Marks the children of n as relevant. \pre n is marked as relevant. */ void propagate_relevant_app(app * n) { SASSERT(is_relevant_core(n)); unsigned j = n->get_num_args(); while (j > 0) { --j; mark_as_relevant(n->get_arg(j)); } } /** \brief Propagate relevancy for an or-application. */ void propagate_relevant_or(app * n) { SASSERT(get_manager().is_or(n)); lbool val = m_context.find_assignment(n); // If val is l_undef, then the expression // is a root, and no boolean variable was created for it. if (val == l_undef) val = l_true; switch (val) { case l_false: propagate_relevant_app(n); break; case l_undef: break; case l_true: { expr * true_arg = nullptr; unsigned num_args = n->get_num_args(); for (unsigned i = 0; i < num_args; i++) { expr * arg = n->get_arg(i); if (m_context.find_assignment(arg) == l_true) { if (is_relevant_core(arg)) return; else if (!true_arg) true_arg = arg; } } if (true_arg) mark_as_relevant(true_arg); break; } } } /** \brief Propagate relevancy for an and-application. */ void propagate_relevant_and(app * n) { lbool val = m_context.find_assignment(n); switch (val) { case l_false: { expr * false_arg = nullptr; unsigned num_args = n->get_num_args(); for (unsigned i = 0; i < num_args; i++) { expr * arg = n->get_arg(i); if (m_context.find_assignment(arg) == l_false) { if (is_relevant_core(arg)) return; else if (!false_arg) false_arg = arg; } } if (false_arg) mark_as_relevant(false_arg); break; } case l_undef: break; case l_true: propagate_relevant_app(n); break; } } /** \brief Propagate relevancy for an ite-expression. */ void propagate_relevant_ite(app * n) { TRACE("propagate_relevant_ite", tout << "propagating relevancy for #" << n->get_id() << "\n" << mk_pp(n, get_manager()) << "\n";); mark_as_relevant(n->get_arg(0)); switch (m_context.find_assignment(n->get_arg(0))) { case l_false: TRACE("propagate_relevant_ite", tout << "marking as relevant: " << mk_pp(n->get_arg(2), get_manager()) << "\n";); mark_as_relevant(n->get_arg(2)); break; case l_undef: TRACE("propagate_relevant_ite", tout << "ite c is unassigned\n";); break; case l_true: TRACE("propagate_relevant_ite", tout << "marking as relevant: " << mk_pp(n->get_arg(1), get_manager()) << "\n";); mark_as_relevant(n->get_arg(1)); break; } } /** \brief Propagate relevancy to the arguments of recently marked expressions. That is, expressions that are located at positions [m_qhead, m_relevant_exprs.size()) in the stack of relevant expressions. */ void propagate() override { if (m_propagating) { return; } flet l_prop(m_propagating, true); ast_manager & m = get_manager(); while (m_qhead < m_relevant_exprs.size()) { expr * n = m_relevant_exprs.get(m_qhead); TRACE("propagate_relevancy_to_args", tout << "propagating relevancy to args of #" << n->get_id() << "\n";); TRACE("propagate_relevancy", tout << "marking as relevant:\n" << mk_bounded_pp(n, m) << "\n";); SASSERT(is_relevant_core(n)); m_qhead++; if (is_app(n)) { family_id fid = to_app(n)->get_family_id(); if (fid == m.get_basic_family_id()) { switch (to_app(n)->get_decl_kind()) { case OP_OR: propagate_relevant_or(to_app(n)); break; case OP_AND: propagate_relevant_and(to_app(n)); break; case OP_ITE: propagate_relevant_ite(to_app(n)); break; default: propagate_relevant_app(to_app(n)); break; } } else { propagate_relevant_app(to_app(n)); } } relevancy_ehs * ehs = get_handlers(n); while (ehs != nullptr) { ehs->head()->operator()(*this, n); ehs = ehs->tail(); } } } bool can_propagate() const override { return m_qhead < m_relevant_exprs.size(); } void assign_eh(expr * n, bool val) override { if (!enabled()) return; ast_manager & m = get_manager(); SASSERT(enabled()); if (is_relevant_core(n)) { if (m.is_or(n)) propagate_relevant_or(to_app(n)); else if (m.is_and(n)) propagate_relevant_and(to_app(n)); } relevancy_ehs * ehs = get_watches(n, val); while (ehs != nullptr) { ehs->head()->operator()(*this, n, val); ehs = ehs->tail(); } } void display(std::ostream & out) const override { if (enabled() && !m_relevant_exprs.empty()) { out << "relevant exprs:\n"; for (unsigned i = 0; i < m_relevant_exprs.size(); i++) { out << "#" << m_relevant_exprs.get(i)->get_id() << " "; } out << "\n"; } } #ifdef Z3DEBUG bool check_relevancy_app(app * n) const { SASSERT(is_relevant(n)); unsigned num_args = n->get_num_args(); for (unsigned i = 0; i < num_args; i++) { CTRACE("relevancy_bug", !is_relevant(n->get_arg(i)), tout << "n: " << mk_ismt2_pp(n, get_manager()) << "\ni: " << i << "\n";); SASSERT(is_relevant(n->get_arg(i))); } return true; } bool check_relevancy_or(app * n, bool root) const override { lbool val = root ? l_true : m_context.find_assignment(n); if (val == l_false) return check_relevancy_app(n); if (val == l_true) { unsigned num_args = n->get_num_args(); for (unsigned i = 0; i < num_args; i++) { expr * arg = n->get_arg(i); if (m_context.find_assignment(arg) == l_true && is_relevant(arg)) return true; } TRACE("check_relevancy", tout << "failed:\n" << mk_ll_pp(n, get_manager()); display(tout);); UNREACHABLE(); } return true; } bool check_relevancy_and(app * n) const { lbool val = m_context.find_assignment(n); if (val == l_true) return check_relevancy_app(n); if (val == l_false) { unsigned num_args = n->get_num_args(); for (unsigned i = 0; i < num_args; i++) { expr * arg = n->get_arg(i); if (m_context.find_assignment(arg) == l_false && is_relevant(arg)) return true; } UNREACHABLE(); } return true; } bool check_relevancy_ite(app * n) const { SASSERT(is_relevant(n->get_arg(0))); switch (m_context.find_assignment(n->get_arg(0))) { case l_false: if (get_manager().is_bool(n)) { TRACE("ite_bug", tout << mk_bounded_pp(n, get_manager()) << "\n";); SASSERT(is_relevant(n->get_arg(2))); } else { app_ref eq(get_manager()); eq = m_context.mk_eq_atom(n, n->get_arg(2)); SASSERT(is_relevant(eq.get())); } break; case l_undef: break; case l_true: if (get_manager().is_bool(n)) { SASSERT(is_relevant(n->get_arg(1))); } else { app_ref eq(get_manager()); eq = m_context.mk_eq_atom(n, n->get_arg(1)); SASSERT(is_relevant(eq.get())); } break; } return true; } bool check_relevancy(expr_ref_vector const & v) const override { SASSERT(!can_propagate()); ast_manager & m = get_manager(); unsigned sz = v.size(); for (unsigned i = 0; i < sz; i++) { expr * n = v.get(i); if (is_relevant(n)) { TRACE("check_relevancy", tout << "checking:\n" << mk_ll_pp(n, get_manager()) << "internalized: " << m_context.find_enode(n) << "\n";); if (m.is_or(n)) { SASSERT(check_relevancy_or(to_app(n), false)); } else if (m.is_and(n)) { SASSERT(check_relevancy_and(to_app(n))); } else if (m.is_ite(n)) { SASSERT(check_relevancy_ite(to_app(n))); } else if (is_app(n)) { SASSERT(check_relevancy_app(to_app(n))); } else { enode * n0 = m_context.find_enode(n); if (n0 != 0) { enode * n = n0->get_next(); while (n0 != n) { SASSERT(is_relevant(n->get_owner())); n = n->get_next(); } } } } } return true; } #endif }; void and_relevancy_eh::operator()(relevancy_propagator & rp) { if (rp.is_relevant(m_parent)) static_cast(rp).propagate_relevant_and(m_parent); } void or_relevancy_eh::operator()(relevancy_propagator & rp) { if (rp.is_relevant(m_parent)) static_cast(rp).propagate_relevant_or(m_parent); } void ite_relevancy_eh::operator()(relevancy_propagator & rp) { if (rp.is_relevant(m_parent)) { static_cast(rp).propagate_relevant_ite(m_parent); } } void ite_term_relevancy_eh::operator()(relevancy_propagator & rp) { if (!rp.is_relevant(m_parent)) return; rp.mark_as_relevant(m_parent->get_arg(0)); switch (rp.get_context().get_assignment(m_parent->get_arg(0))) { case l_false: TRACE("ite_term_relevancy", tout << "marking else: #" << m_else_eq->get_id() << "\n";); rp.mark_as_relevant(m_else_eq); break; case l_undef: break; case l_true: TRACE("ite_term_relevancy", tout << "marking then: #" << m_then_eq->get_id() << "\n";); rp.mark_as_relevant(m_then_eq); break; } } relevancy_propagator * mk_relevancy_propagator(context & ctx) { return alloc(relevancy_propagator_imp, ctx); } }; z3-z3-4.8.7/src/smt/smt_relevancy.h000066400000000000000000000140441356505360400170500ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_relevancy.h Abstract: Author: Leonardo de Moura (leonardo) 2008-06-04. Revision History: --*/ #ifndef SMT_RELEVANCY_H_ #define SMT_RELEVANCY_H_ #include "ast/ast.h" namespace smt { class context; class relevancy_propagator; class relevancy_eh { protected: void mark_as_relevant(relevancy_propagator & rp, expr * n); void mark_args_as_relevant(relevancy_propagator & rp, app * n); public: relevancy_eh() {} virtual ~relevancy_eh() {} /** \brief This method is invoked when n is marked as relevant. */ virtual void operator()(relevancy_propagator & rp, expr * n) { operator()(rp); } /** \brief This method is invoked when atom is assigned to val. */ virtual void operator()(relevancy_propagator & rp, expr * atom, bool val) { operator()(rp); } /** \brief Fallback for the two previous methods. */ virtual void operator()(relevancy_propagator & rp) {} }; class simple_relevancy_eh : public relevancy_eh { expr * m_target; public: simple_relevancy_eh(expr * t):m_target(t) {} ~simple_relevancy_eh() override {} void operator()(relevancy_propagator & rp) override; }; /** \brief Propagate relevancy to m_target if both m_source1 and m_source2 are relevant. */ class pair_relevancy_eh : public relevancy_eh { expr * m_source1; expr * m_source2; expr * m_target; public: pair_relevancy_eh(expr * s1, expr * s2, expr * t):m_source1(s1), m_source2(s2), m_target(t) {} ~pair_relevancy_eh() override {} void operator()(relevancy_propagator & rp) override; }; /** \brief Relevancy propagator. The relevancy propagation constraints are specified to the relevancy propagator using the methods: - add_handler - add_watch This class also provides helper methods for specifying commonly used constraints. It uses the following API from smt::context - find_assignment(expr * n) - find_enode(expr * n) It notifies smt::context that an expression became relevant by invoking - relevant_eh(expr * n) smt::context notifies the relevancy_propagator that a literal was assigned by invoking assign_eh(n, bool val) */ class relevancy_propagator { protected: context & m_context; public: relevancy_propagator(context & ctx); virtual ~relevancy_propagator() {} context & get_context() { return m_context; } /** \brief Install an event handler that is invoked whenever n is marked as relevant. */ virtual void add_handler(expr * n, relevancy_eh * eh) = 0; /** \brief Install an event handler that is invoked whenever n is assigned to the given value. The relevancy propagator is notified of new assignments by the method assign_eh. */ virtual void add_watch(expr * n, bool val, relevancy_eh * eh) = 0; /** \brief Install an event handler that just marks target as relevant whenever n is assigned to the given value. The relevancy propagator is notified of new assignments by the method assign_eh. */ virtual void add_watch(expr * n, bool val, expr * target) = 0; /** \brief smt::context invokes this method whenever the expression is assigned to true/false */ virtual void assign_eh(expr * n, bool val) = 0; /** \brief Mark the given expression is relevant. */ virtual void mark_as_relevant(expr * n) = 0; /** \brief Return true if the given expression is marked as relevant. */ virtual bool is_relevant(expr * n) const = 0; /** \brief Propagate relevancy using the event handlers specified by add_handler and add_watch, and the structure of the expressions already marked as relevant. */ virtual void propagate() = 0; /** \brief Return true if it can propagate relevancy. */ virtual bool can_propagate() const = 0; /** \brief Create a backtracking point */ virtual void push() = 0; /** \brief Backtrack. */ virtual void pop(unsigned num_scopes) = 0; /** \brief Display relevant expressions. */ virtual void display(std::ostream & out) const = 0; #ifdef Z3DEBUG virtual bool check_relevancy(expr_ref_vector const & v) const = 0; virtual bool check_relevancy_or(app * n, bool root) const = 0; #endif // -------------------------- // // Helper method // // -------------------------- /** \brief Return true if relevancy propagation is enabled. */ bool enabled() const; /** \Brief Return the region allocator for the smt::context that owns this propagator. */ region & get_region() const; /** \Brief Return the ast_manager for the smt::context that owns this propagator. */ ast_manager & get_manager() const; template relevancy_eh * mk_relevancy_eh(Eh const & eh) { return new (get_region()) Eh(eh); } /** \brief Creates an event handler that marks target as relevant whenever src is marked as relevant. */ void add_dependency(expr * src, expr * target); relevancy_eh * mk_or_relevancy_eh(app * n); relevancy_eh * mk_and_relevancy_eh(app * n); relevancy_eh * mk_ite_relevancy_eh(app * n); relevancy_eh * mk_term_ite_relevancy_eh(app * c, app * t, app * e); }; relevancy_propagator * mk_relevancy_propagator(context & ctx); }; #endif /* SMT_RELEVANCY_H_ */ z3-z3-4.8.7/src/smt/smt_setup.cpp000066400000000000000000001204451356505360400165560ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_setup.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-06-24. Revision History: --*/ #include "smt/smt_context.h" #include "smt/smt_setup.h" #include "ast/static_features.h" #include "smt/theory_arith.h" #include "smt/theory_lra.h" #include "smt/theory_dense_diff_logic.h" #include "smt/theory_diff_logic.h" #include "smt/theory_utvpi.h" #include "smt/theory_array.h" #include "smt/theory_array_full.h" #include "smt/theory_bv.h" #include "smt/theory_datatype.h" #include "smt/theory_recfun.h" #include "smt/theory_dummy.h" #include "smt/theory_dl.h" #include "smt/theory_seq_empty.h" #include "smt/theory_seq.h" #include "smt/theory_special_relations.h" #include "smt/theory_pb.h" #include "smt/theory_fpa.h" #include "smt/theory_str.h" #include "smt/theory_jobscheduler.h" namespace smt { setup::setup(context & c, smt_params & params): m_context(c), m_manager(c.get_manager()), m_params(params), m_already_configured(false) { } void setup::operator()(config_mode cm) { SASSERT(m_context.get_scope_level() == 0); SASSERT(!m_context.already_internalized()); SASSERT(!m_already_configured); // if (m_params.m_mbqi && m_params.m_model_compact) { // warning_msg("ignoring MODEL_COMPACT=true because it cannot be used with MBQI=true"); // m_params.m_model_compact = false; // } TRACE("setup", tout << "configuring logical context, logic: " << m_logic << " " << cm << "\n";); m_already_configured = true; switch (cm) { case CFG_BASIC: setup_unknown(); break; case CFG_LOGIC: setup_default(); break; case CFG_AUTO: setup_auto_config(); break; } } void setup::setup_default() { if (m_logic == "QF_UF") setup_QF_UF(); else if (m_logic == "QF_RDL") setup_QF_RDL(); else if (m_logic == "QF_IDL") setup_QF_IDL(); else if (m_logic == "QF_UFIDL") setup_QF_UFIDL(); else if (m_logic == "QF_LRA") setup_QF_LRA(); else if (m_logic == "QF_LIA") setup_QF_LIA(); else if (m_logic == "QF_UFLIA") setup_QF_UFLIA(); else if (m_logic == "QF_UFLRA") setup_QF_UFLRA(); else if (m_logic == "QF_AX") setup_QF_AX(); else if (m_logic == "QF_AUFLIA") setup_QF_AUFLIA(); else if (m_logic == "QF_BV") setup_QF_BV(); else if (m_logic == "QF_AUFBV") setup_QF_AUFBV(); else if (m_logic == "QF_ABV") setup_QF_AUFBV(); else if (m_logic == "QF_UFBV") setup_QF_AUFBV(); else if (m_logic == "QF_BVRE") setup_QF_BVRE(); else if (m_logic == "AUFLIA") setup_AUFLIA(); else if (m_logic == "AUFLIRA") setup_AUFLIRA(); else if (m_logic == "AUFNIRA") setup_AUFNIRA(); else if (m_logic == "AUFLIA+") setup_AUFLIA(); else if (m_logic == "AUFLIA-") setup_AUFLIA(); else if (m_logic == "AUFLIRA+") setup_AUFLIRA(); else if (m_logic == "AUFLIRA-") setup_AUFLIRA(); else if (m_logic == "AUFNIRA+") setup_AUFLIRA(); else if (m_logic == "AUFNIRA-") setup_AUFLIRA(); else if (m_logic == "UFNIA") setup_UFNIA(); else if (m_logic == "UFLRA") setup_UFLRA(); else if (m_logic == "LRA") setup_LRA(); else if (m_logic == "CSP") setup_CSP(); else if (m_logic == "QF_FP") setup_QF_FP(); else if (m_logic == "QF_FPBV" || m_logic == "QF_BVFP") setup_QF_FPBV(); else if (m_logic == "QF_S") setup_QF_S(); else if (m_logic == "QF_DT") setup_QF_DT(); else setup_unknown(); } void setup::setup_auto_config() { static_features st(m_manager); IF_VERBOSE(100, verbose_stream() << "(smt.configuring)\n";); TRACE("setup", tout << "setup, logic: " << m_logic << "\n";); // HACK: do not collect features for QF_BV and QF_AUFBV... since they do not use them... if (m_logic == "QF_BV") { setup_QF_BV(); } else if (m_logic == "QF_AUFBV" || m_logic == "QF_ABV" || m_logic == "QF_UFBV") { setup_QF_AUFBV(); } else { IF_VERBOSE(100, verbose_stream() << "(smt.collecting-features)\n";); ptr_vector fmls; m_context.get_asserted_formulas(fmls); st.collect(fmls.size(), fmls.c_ptr()); IF_VERBOSE(1000, st.display_primitive(verbose_stream());); if (m_logic == "QF_UF") setup_QF_UF(st); else if (m_logic == "QF_RDL") setup_QF_RDL(st); else if (m_logic == "QF_IDL") setup_QF_IDL(st); else if (m_logic == "QF_UFIDL") setup_QF_UFIDL(st); else if (m_logic == "QF_LRA") setup_QF_LRA(st); else if (m_logic == "QF_LIA") setup_QF_LIA(st); else if (m_logic == "QF_UFLIA") setup_QF_UFLIA(st); else if (m_logic == "QF_UFLRA") setup_QF_UFLRA(); else if (m_logic == "QF_AX") setup_QF_AX(st); else if (m_logic == "QF_BVRE") setup_QF_BVRE(); else if (m_logic == "QF_AUFLIA") setup_QF_AUFLIA(st); else if (m_logic == "QF_S") setup_QF_S(); else if (m_logic == "AUFLIA") setup_AUFLIA(st); else if (m_logic == "AUFLIRA") setup_AUFLIRA(); else if (m_logic == "AUFNIRA") setup_AUFNIRA(); else if (m_logic == "AUFLIA+") setup_AUFLIA(); else if (m_logic == "AUFLIA-") setup_AUFLIA(); else if (m_logic == "AUFLIRA+") setup_AUFLIRA(); else if (m_logic == "AUFLIRA-") setup_AUFLIRA(); else if (m_logic == "AUFNIRA+") setup_AUFLIRA(); else if (m_logic == "AUFNIRA-") setup_AUFLIRA(); else if (m_logic == "UFNIA") setup_UFNIA(); else if (m_logic == "QF_DT") setup_QF_DT(); else if (m_logic == "LRA") setup_LRA(); else if (m_logic == "CSP") setup_CSP(); else setup_unknown(st); } } 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 constrains arithmetic, but specified logic does not support it."); } void setup::setup_QF_UF() { m_params.m_relevancy_lvl = 0; m_params.m_nnf_cnf = false; m_params.m_restart_strategy = RS_LUBY; m_params.m_phase_selection = PS_CACHING_CONSERVATIVE2; m_params.m_random_initial_activity = IA_RANDOM; } void setup::setup_QF_DT() { setup_QF_UF(); setup_datatypes(); setup_recfuns(); } void setup::setup_QF_BVRE() { setup_QF_BV(); setup_QF_LIA(); m_context.register_plugin(alloc(theory_seq, m_manager, m_params)); } void setup::setup_QF_UF(static_features const & st) { check_no_arithmetic(st, "QF_UF"); setup_QF_UF(); TRACE("setup", tout << "st.m_num_theories: " << st.m_num_theories << "\n"; tout << "st.m_num_uninterpreted_functions: " << st.m_num_uninterpreted_functions << "\n";); } void setup::setup_QF_RDL() { m_params.m_relevancy_lvl = 0; m_params.m_arith_eq2ineq = true; m_params.m_arith_reflect = false; m_params.m_arith_propagate_eqs = false; m_params.m_nnf_cnf = false; setup_mi_arith(); } static bool is_dense(static_features const & st) { return st.m_num_uninterpreted_constants < 1000 && (st.m_num_arith_eqs + st.m_num_arith_ineqs) > st.m_num_uninterpreted_constants * 9; } static bool is_in_diff_logic(static_features const & st) { return st.m_num_arith_eqs == st.m_num_diff_eqs && st.m_num_arith_terms == st.m_num_diff_terms && st.m_num_arith_ineqs == st.m_num_diff_ineqs; } static bool is_diff_logic(static_features const & st) { return is_in_diff_logic(st) && (st.m_num_diff_ineqs > 0 || st.m_num_diff_eqs > 0 || st.m_num_diff_terms > 0) ; } static void check_no_uninterpreted_functions(static_features const & st, char const * logic) { if (st.m_num_uninterpreted_functions != 0) throw default_exception("Benchmark contains uninterpreted function symbols, but specified logic does not support them."); } void setup::setup_QF_RDL(static_features & st) { if (!is_in_diff_logic(st)) throw default_exception("Benchmark is not in QF_RDL (real difference logic)."); if (st.m_has_int) throw default_exception("Benchmark has integer variables but it is marked as QF_RDL (real difference logic)."); TRACE("setup", tout << "setup_QF_RDL(st)\n";); check_no_uninterpreted_functions(st, "QF_RDL"); m_params.m_relevancy_lvl = 0; m_params.m_arith_eq2ineq = true; m_params.m_arith_reflect = false; m_params.m_arith_propagate_eqs = false; m_params.m_nnf_cnf = false; if (is_dense(st)) { m_params.m_restart_strategy = RS_GEOMETRIC; m_params.m_restart_adaptive = false; m_params.m_phase_selection = PS_CACHING; } // The smi theories use fixed size integers instead of rationals. // They have support for epsilons for modeling strict inequalities, but they // cannot handle rational numbers. // It is only safe to use them when the input does not contain rational numbers. // Moreover, if model construction is enabled, then rational numbers may be needed // to compute the actual value of epsilon even if the input does not have rational numbers. // Example: (x < 1) and (x > 0) if (m_manager.proofs_enabled()) { m_context.register_plugin(alloc(smt::theory_mi_arith, m_manager, m_params)); } else if (!m_params.m_arith_auto_config_simplex && is_dense(st)) { if (!st.m_has_rational && !m_params.m_model && st.arith_k_sum_is_small()) m_context.register_plugin(alloc(smt::theory_dense_smi, m_manager, m_params)); else m_context.register_plugin(alloc(smt::theory_dense_mi, m_manager, m_params)); } else { if (m_params.m_arith_auto_config_simplex || st.m_num_uninterpreted_constants > 4 * st.m_num_bool_constants || st.m_num_ite_terms > 0 /* theory_rdl and theory_frdl do not support ite-terms */) { // if (!st.m_has_rational && !m_params.m_model && st.arith_k_sum_is_small()) { // TRACE("rdl_bug", tout << "using theory_smi_arith\n";); // m_context.register_plugin(alloc(smt::theory_smi_arith, m_manager, m_params)); // } // else { TRACE("rdl_bug", tout << "using theory_mi_arith\n";); //setup_lra_arith(); m_context.register_plugin(alloc(smt::theory_mi_arith, m_manager, m_params)); // } } else { m_params.m_arith_bound_prop = BP_NONE; m_params.m_arith_propagation_strategy = ARITH_PROP_AGILITY; m_params.m_arith_add_binary_bounds = true; if (!st.m_has_rational && !m_params.m_model && st.arith_k_sum_is_small()) m_context.register_plugin(alloc(smt::theory_frdl, m_manager, m_params)); else m_context.register_plugin(alloc(smt::theory_rdl, m_manager, m_params)); } } } void setup::setup_QF_IDL() { TRACE("setup", tout << "setup_QF_IDL()\n";); m_params.m_relevancy_lvl = 0; m_params.m_arith_eq2ineq = true; m_params.m_arith_reflect = false; m_params.m_arith_propagate_eqs = false; m_params.m_arith_small_lemma_size = 30; m_params.m_nnf_cnf = false; setup_lra_arith(); } void setup::setup_QF_IDL(static_features & st) { if (!is_in_diff_logic(st)) throw default_exception("Benchmark is not in QF_IDL (integer difference logic)."); if (st.m_has_real) throw default_exception("Benchmark has real variables but it is marked as QF_IDL (integer difference logic)."); TRACE("setup", tout << "setup QF_IDL, m_arith_k_sum: " << st.m_arith_k_sum << " m_num_diff_terms: " << st.m_num_arith_terms << "\n"; st.display_primitive(tout);); TRACE("setup", tout << "setup_QF_IDL(st)\n";); check_no_uninterpreted_functions(st, "QF_IDL"); m_params.m_relevancy_lvl = 0; m_params.m_arith_eq2ineq = true; m_params.m_arith_reflect = false; m_params.m_arith_propagate_eqs = false; m_params.m_arith_small_lemma_size = 30; m_params.m_nnf_cnf = false; if (st.m_num_uninterpreted_constants > 5000) m_params.m_relevancy_lvl = 2; else if (st.m_cnf && !is_dense(st)) m_params.m_phase_selection = PS_CACHING_CONSERVATIVE2; else m_params.m_phase_selection = PS_CACHING; if (is_dense(st) && st.m_num_bin_clauses + st.m_num_units == st.m_num_clauses) { m_params.m_restart_adaptive = false; m_params.m_restart_strategy = RS_GEOMETRIC; } if (st.m_cnf && st.m_num_units == st.m_num_clauses) { // the problem is just a big conjunction... using randomization to deal with crafted benchmarks m_params.m_random_initial_activity = IA_RANDOM; } TRACE("setup", tout << "RELEVANCY: " << m_params.m_relevancy_lvl << "\n"; tout << "ARITH_EQ_BOUNDS: " << m_params.m_arith_eq_bounds << "\n";); if (m_manager.proofs_enabled()) { m_context.register_plugin(alloc(smt::theory_mi_arith, m_manager, m_params)); } else if (!m_params.m_arith_auto_config_simplex && is_dense(st)) { TRACE("setup", tout << "using dense diff logic...\n";); m_params.m_phase_selection = PS_CACHING_CONSERVATIVE; if (st.arith_k_sum_is_small()) m_context.register_plugin(alloc(smt::theory_dense_si, m_manager, m_params)); else m_context.register_plugin(alloc(smt::theory_dense_i, m_manager, m_params)); } else { // if (st.arith_k_sum_is_small()) { // TRACE("setup", tout << "using small integer simplex...\n";); // m_context.register_plugin(alloc(smt::theory_si_arith, m_manager, m_params)); // } // else { TRACE("setup", tout << "using big integer simplex...\n";); m_context.register_plugin(alloc(smt::theory_i_arith, m_manager, m_params)); // } } } void setup::setup_QF_UFIDL() { TRACE("setup", tout << "setup_QF_UFIDL()\n";); m_params.m_relevancy_lvl = 0; m_params.m_arith_reflect = false; m_params.m_nnf_cnf = false; m_params.m_arith_eq_bounds = true; m_params.m_arith_eq2ineq = true; // m_params.m_phase_selection = PS_THEORY; m_params.m_restart_strategy = RS_GEOMETRIC; m_params.m_restart_factor = 1.5; m_params.m_restart_adaptive = false; setup_lra_arith(); } void setup::setup_QF_UFIDL(static_features & st) { TRACE("setup", tout << "setup_QF_UFIDL(st)\n";); if (st.m_has_real) throw default_exception("Benchmark has real variables but it is marked as QF_UFIDL (uninterpreted functions and difference logic)."); m_params.m_relevancy_lvl = 0; m_params.m_arith_reflect = false; m_params.m_nnf_cnf = false; if (st.m_num_uninterpreted_functions == 0) { m_params.m_arith_eq2ineq = true; m_params.m_arith_propagate_eqs = false; if (is_dense(st)) { m_params.m_arith_small_lemma_size = 128; m_params.m_lemma_gc_half = true; m_params.m_restart_strategy = RS_GEOMETRIC; if (m_manager.proofs_enabled()) { m_context.register_plugin(alloc(smt::theory_mi_arith, m_manager, m_params)); } else if (st.arith_k_sum_is_small()) m_context.register_plugin(alloc(smt::theory_dense_si, m_manager, m_params)); else m_context.register_plugin(alloc(smt::theory_dense_i, m_manager, m_params)); return; } } m_params.m_arith_eq_bounds = true; // m_params.m_phase_selection = PS_THEORY; m_params.m_restart_strategy = RS_GEOMETRIC; m_params.m_restart_factor = 1.5; m_params.m_restart_adaptive = false; if (m_manager.proofs_enabled()) { m_context.register_plugin(alloc(smt::theory_mi_arith, m_manager, m_params)); } // else if (st.arith_k_sum_is_small()) // m_context.register_plugin(alloc(smt::theory_dense_si, m_manager, m_params)); else m_context.register_plugin(alloc(smt::theory_i_arith, m_manager, m_params)); } void setup::setup_QF_LRA() { TRACE("setup", tout << "setup_QF_LRA()\n";); m_params.m_relevancy_lvl = 0; m_params.m_arith_eq2ineq = true; m_params.m_arith_reflect = false; m_params.m_arith_propagate_eqs = false; m_params.m_eliminate_term_ite = true; m_params.m_nnf_cnf = false; m_params.m_phase_selection = PS_THEORY; setup_lra_arith(); } void setup::setup_QF_LRA(static_features const & st) { check_no_uninterpreted_functions(st, "QF_LRA"); m_params.m_relevancy_lvl = 0; m_params.m_arith_eq2ineq = true; m_params.m_arith_reflect = false; m_params.m_arith_propagate_eqs = false; m_params.m_eliminate_term_ite = true; m_params.m_nnf_cnf = false; if (numerator(st.m_arith_k_sum) > rational(2000000) && denominator(st.m_arith_k_sum) > rational(500)) { m_params.m_relevancy_lvl = 2; m_params.m_relevancy_lemma = false; } m_params.m_phase_selection = PS_THEORY; if (!st.m_cnf) { m_params.m_restart_strategy = RS_GEOMETRIC; m_params.m_arith_stronger_lemmas = false; m_params.m_restart_adaptive = false; } m_params.m_arith_small_lemma_size = 32; setup_lra_arith(); } void setup::setup_QF_LIRA(static_features const& st) { setup_mi_arith(); } void setup::setup_QF_LIA() { TRACE("setup", tout << "setup_QF_LIA(st)\n";); m_params.m_relevancy_lvl = 0; m_params.m_arith_eq2ineq = true; m_params.m_arith_reflect = false; m_params.m_arith_propagate_eqs = false; m_params.m_nnf_cnf = false; setup_lra_arith(); } void setup::setup_QF_LIA(static_features const & st) { check_no_uninterpreted_functions(st, "QF_LIA"); TRACE("setup", tout << "QF_LIA setup\n";); m_params.m_relevancy_lvl = 0; m_params.m_arith_eq2ineq = true; m_params.m_arith_reflect = false; m_params.m_arith_propagate_eqs = false; m_params.m_nnf_cnf = false; if (st.m_max_ite_tree_depth > 50) { m_params.m_arith_eq2ineq = false; m_params.m_pull_cheap_ite = true; m_params.m_arith_propagate_eqs = true; m_params.m_relevancy_lvl = 2; m_params.m_relevancy_lemma = false; } else if (st.m_num_clauses == st.m_num_units) { m_params.m_arith_gcd_test = false; m_params.m_arith_branch_cut_ratio = 4; m_params.m_relevancy_lvl = 2; m_params.m_arith_eq2ineq = true; m_params.m_eliminate_term_ite = true; // if (st.m_num_exprs < 5000 && st.m_num_ite_terms < 50) { // safeguard to avoid high memory consumption // TODO: implement analysis function to decide where lift ite is too expensive. // m_params.m_lift_ite = LI_FULL; // } } else { m_params.m_eliminate_term_ite = true; m_params.m_restart_adaptive = false; m_params.m_restart_strategy = RS_GEOMETRIC; m_params.m_restart_factor = 1.5; } if (st.m_num_bin_clauses + st.m_num_units == st.m_num_clauses && st.m_cnf && st.m_arith_k_sum > rational(100000)) { m_params.m_arith_bound_prop = BP_NONE; m_params.m_arith_stronger_lemmas = false; } setup_lra_arith(); } void setup::setup_QF_UFLIA() { m_params.m_relevancy_lvl = 0; m_params.m_arith_reflect = false; m_params.m_nnf_cnf = false; m_params.m_arith_propagation_threshold = 1000; setup_lra_arith(); } void setup::setup_QF_UFLIA(static_features & st) { if (st.m_has_real) throw default_exception("Benchmark has real variables but it is marked as QF_UFLIA (uninterpreted functions and linear integer arithmetic)."); setup_QF_UFLIA(); } void setup::setup_QF_UFLRA() { m_params.m_relevancy_lvl = 0; m_params.m_arith_reflect = false; m_params.m_nnf_cnf = false; setup_lra_arith(); } void setup::setup_QF_BV() { TRACE("setup", tout << "qf-bv\n";); m_params.m_relevancy_lvl = 0; m_params.m_arith_reflect = false; m_params.m_bv_cc = false; m_params.m_bb_ext_gates = true; m_params.m_nnf_cnf = false; m_context.register_plugin(alloc(smt::theory_bv, m_manager, m_params, m_params)); } void setup::setup_QF_AUFBV() { m_params.m_array_mode = AR_SIMPLE; m_params.m_relevancy_lvl = 0; m_params.m_bv_cc = false; m_params.m_bb_ext_gates = true; m_params.m_nnf_cnf = false; m_context.register_plugin(alloc(smt::theory_bv, m_manager, m_params, m_params)); setup_arrays(); } void setup::setup_QF_AX() { TRACE("setup", tout << "QF_AX\n";); m_params.m_array_mode = AR_SIMPLE; m_params.m_nnf_cnf = false; setup_arrays(); } void setup::setup_QF_AX(static_features const & st) { m_params.m_array_mode = st.m_has_ext_arrays ? AR_FULL : AR_SIMPLE; m_params.m_nnf_cnf = false; if (st.m_num_clauses == st.m_num_units) { m_params.m_relevancy_lvl = 0; m_params.m_phase_selection = PS_ALWAYS_FALSE; } else { m_params.m_relevancy_lvl = 2; } setup_arrays(); } void setup::setup_QF_AUFLIA() { TRACE("QF_AUFLIA", tout << "no static features\n";); m_params.m_array_mode = AR_SIMPLE; m_params.m_nnf_cnf = false; m_params.m_relevancy_lvl = 2; m_params.m_restart_strategy = RS_GEOMETRIC; m_params.m_restart_factor = 1.5; m_params.m_phase_selection = PS_CACHING_CONSERVATIVE2; setup_i_arith(); setup_arrays(); } void setup::setup_QF_AUFLIA(static_features const & st) { m_params.m_array_mode = st.m_has_ext_arrays ? AR_FULL : AR_SIMPLE; if (st.m_has_real) throw default_exception("Benchmark has real variables but it is marked as QF_AUFLIA (arrays, uninterpreted functions and linear integer arithmetic)."); m_params.m_nnf_cnf = false; if (st.m_num_clauses == st.m_num_units) { TRACE("QF_AUFLIA", tout << "using relevancy: 0\n";); m_params.m_relevancy_lvl = 0; m_params.m_phase_selection = PS_ALWAYS_FALSE; } else { m_params.m_relevancy_lvl = 0; // it was 2, for some reason 2 doesn't work anymore TODO: investigate m_params.m_restart_strategy = RS_GEOMETRIC; m_params.m_restart_factor = 1.5; m_params.m_phase_selection = PS_CACHING_CONSERVATIVE2; m_params.m_random_initial_activity = IA_ZERO; } setup_i_arith(); setup_arrays(); } void setup::setup_AUFLIA(bool simple_array) { TRACE("setup", tout << "AUFLIA\n";); m_params.m_array_mode = simple_array ? AR_SIMPLE : AR_FULL; m_params.m_pi_use_database = true; m_params.m_phase_selection = PS_ALWAYS_FALSE; m_params.m_restart_strategy = RS_GEOMETRIC; m_params.m_restart_factor = 1.5; m_params.m_eliminate_bounds = true; m_params.m_qi_quick_checker = MC_UNSAT; m_params.m_qi_lazy_threshold = 20; // m_params.m_qi_max_eager_multipatterns = 10; /// <<< HACK m_params.m_mbqi = true; // enabling MBQI and MACRO_FINDER by default :-) // MACRO_FINDER is a horrible for AUFLIA and UFNIA benchmarks (boogie benchmarks in general) // It destroys the existing patterns. // m_params.m_macro_finder = true; // m_params.m_ng_lift_ite = LI_FULL; TRACE("setup", tout << "max_eager_multipatterns: " << m_params.m_qi_max_eager_multipatterns << "\n";); m_context.register_plugin(alloc(smt::theory_i_arith, m_manager, m_params)); setup_arrays(); } void setup::setup_AUFLIA(static_features const & st) { if (st.m_has_real) throw default_exception("Benchmark has real variables but it is marked as AUFLIA (arrays, uninterpreted functions and linear integer arithmetic)."); m_params.m_qi_eager_threshold = st.m_num_quantifiers_with_patterns == 0 ? 5 : 7; setup_AUFLIA(); } void setup::setup_AUFLIRA(bool simple_array) { m_params.m_array_mode = simple_array ? AR_SIMPLE : AR_FULL; m_params.m_phase_selection = PS_ALWAYS_FALSE; m_params.m_eliminate_bounds = true; m_params.m_qi_quick_checker = MC_UNSAT; m_params.m_qi_eager_threshold = 5; // Added for MBQI release m_params.m_qi_lazy_threshold = 20; // m_params.m_macro_finder = true; m_params.m_ng_lift_ite = LI_FULL; m_params.m_pi_max_multi_patterns = 10; //<< it was used for SMT-COMP m_params.m_array_lazy_ieq = true; m_params.m_array_lazy_ieq_delay = 4; // m_params.m_mbqi = true; // enabling MBQI by default :-) // setup_mi_arith(); setup_arrays(); } void setup::setup_UFNIA() { setup_AUFLIA(); } void setup::setup_UFLRA() { setup_AUFLIRA(); } void setup::setup_AUFLIAp() { setup_AUFLIA(); } void setup::setup_AUFNIRA() { setup_AUFLIRA(); } void setup::setup_LRA() { m_params.m_relevancy_lvl = 0; m_params.m_arith_reflect = false; m_params.m_arith_propagate_eqs = false; m_params.m_eliminate_term_ite = true; setup_mi_arith(); } void setup::setup_QF_FP() { setup_QF_BV(); m_context.register_plugin(alloc(smt::theory_fpa, m_manager)); } void setup::setup_QF_FPBV() { setup_QF_BV(); m_context.register_plugin(alloc(smt::theory_fpa, m_manager)); } void setup::setup_QF_S() { if (m_params.m_string_solver == "z3str3") { setup_str(); } else if (m_params.m_string_solver == "seq") { setup_unknown(); } else if (m_params.m_string_solver == "auto") { setup_unknown(); } else if (m_params.m_string_solver == "empty") { m_context.register_plugin(alloc(smt::theory_seq_empty, m_manager)); } else if (m_params.m_string_solver == "none") { // don't register any solver. } else { throw default_exception("invalid parameter for smt.string_solver, valid options are 'z3str3', 'seq', 'auto'"); } } bool is_arith(static_features const & st) { return st.m_num_arith_ineqs > 0 || st.m_num_arith_terms > 0 || st.m_num_arith_eqs > 0; } void setup::setup_i_arith() { if (AS_OLD_ARITH == m_params.m_arith_mode) { m_context.register_plugin(alloc(smt::theory_i_arith, m_manager, m_params)); } else { setup_lra_arith(); } } void setup::setup_lra_arith() { // m_context.register_plugin(alloc(smt::theory_mi_arith, m_manager, m_params)); m_context.register_plugin(alloc(smt::theory_lra, m_manager, m_params)); } void setup::setup_mi_arith() { switch (m_params.m_arith_mode) { case AS_OPTINF: m_context.register_plugin(alloc(smt::theory_inf_arith, m_manager, m_params)); break; case AS_NEW_ARITH: setup_lra_arith(); break; default: m_context.register_plugin(alloc(smt::theory_mi_arith, m_manager, m_params)); break; } } void setup::setup_arith() { static_features st(m_manager); IF_VERBOSE(100, verbose_stream() << "(smt.collecting-features)\n";); ptr_vector fmls; m_context.get_asserted_formulas(fmls); st.collect(fmls.size(), fmls.c_ptr()); IF_VERBOSE(1000, st.display_primitive(verbose_stream());); bool fixnum = st.arith_k_sum_is_small() && m_params.m_arith_fixnum; bool int_only = !st.m_has_rational && !st.m_has_real && m_params.m_arith_int_only; auto mode = m_params.m_arith_mode; if (m_logic == "QF_LIA") { mode = AS_NEW_ARITH; } switch(mode) { case AS_NO_ARITH: m_context.register_plugin(alloc(smt::theory_dummy, m_manager.mk_family_id("arith"), "no arithmetic")); break; case AS_DIFF_LOGIC: m_params.m_arith_eq2ineq = true; if (fixnum) { if (int_only) m_context.register_plugin(alloc(smt::theory_fidl, m_manager, m_params)); else m_context.register_plugin(alloc(smt::theory_frdl, m_manager, m_params)); } else { if (int_only) m_context.register_plugin(alloc(smt::theory_idl, m_manager, m_params)); else m_context.register_plugin(alloc(smt::theory_rdl, m_manager, m_params)); } break; case AS_DENSE_DIFF_LOGIC: m_params.m_arith_eq2ineq = true; if (fixnum) { if (int_only) m_context.register_plugin(alloc(smt::theory_dense_si, m_manager, m_params)); else m_context.register_plugin(alloc(smt::theory_dense_smi, m_manager, m_params)); } else { if (int_only) m_context.register_plugin(alloc(smt::theory_dense_i, m_manager, m_params)); else m_context.register_plugin(alloc(smt::theory_dense_mi, m_manager, m_params)); } break; case AS_UTVPI: m_params.m_arith_eq2ineq = true; if (int_only) m_context.register_plugin(alloc(smt::theory_iutvpi, m_manager)); else m_context.register_plugin(alloc(smt::theory_rutvpi, m_manager)); break; case AS_OPTINF: m_context.register_plugin(alloc(smt::theory_inf_arith, m_manager, m_params)); break; case AS_OLD_ARITH: if (m_params.m_arith_int_only && int_only) m_context.register_plugin(alloc(smt::theory_i_arith, m_manager, m_params)); else m_context.register_plugin(alloc(smt::theory_mi_arith, m_manager, m_params)); break; case AS_NEW_ARITH: if (st.m_num_non_linear != 0 && st.m_has_int) m_context.register_plugin(alloc(smt::theory_mi_arith, m_manager, m_params)); else setup_lra_arith(); break; default: m_context.register_plugin(alloc(smt::theory_mi_arith, m_manager, m_params)); break; } } void setup::setup_bv() { switch(m_params.m_bv_mode) { case BS_NO_BV: m_context.register_plugin(alloc(smt::theory_dummy, m_manager.mk_family_id("bv"), "no bit-vector")); break; case BS_BLASTER: m_context.register_plugin(alloc(smt::theory_bv, m_manager, m_params, m_params)); break; } } void setup::setup_arrays() { switch(m_params.m_array_mode) { case AR_NO_ARRAY: m_context.register_plugin(alloc(smt::theory_dummy, m_manager.mk_family_id("array"), "no array")); break; case AR_SIMPLE: m_context.register_plugin(alloc(smt::theory_array, m_manager, m_params)); break; case AR_MODEL_BASED: throw default_exception("The model-based array theory solver is deprecated"); break; case AR_FULL: m_context.register_plugin(alloc(smt::theory_array_full, m_manager, m_params)); break; } } void setup::setup_datatypes() { TRACE("datatype", tout << "registering theory datatype...\n";); m_context.register_plugin(alloc(theory_datatype, m_manager, m_params)); } void setup::setup_recfuns() { TRACE("recfun", tout << "registering theory recfun...\n";); theory_recfun * th = alloc(theory_recfun, m_manager); m_context.register_plugin(th); } void setup::setup_dl() { m_context.register_plugin(mk_theory_dl(m_manager)); } void setup::setup_seq_str(static_features const & st) { // check params for what to do here when it's ambiguous if (m_params.m_string_solver == "z3str3") { setup_str(); } else if (m_params.m_string_solver == "seq") { setup_seq(); } else if (m_params.m_string_solver == "empty") { m_context.register_plugin(alloc(smt::theory_seq_empty, m_manager)); } else if (m_params.m_string_solver == "none") { // don't register any solver. } else if (m_params.m_string_solver == "auto") { if (st.m_has_seq_non_str) { setup_seq(); } else { setup_str(); } } else { throw default_exception("invalid parameter for smt.string_solver, valid options are 'z3str3', 'seq', 'auto'"); } } void setup::setup_card() { m_context.register_plugin(alloc(theory_pb, m_manager, m_params)); } void setup::setup_fpa() { setup_bv(); m_context.register_plugin(alloc(theory_fpa, m_manager)); } void setup::setup_str() { setup_arith(); m_context.register_plugin(alloc(theory_str, m_manager, m_params)); } void setup::setup_seq() { m_context.register_plugin(alloc(smt::theory_seq, m_manager, m_params)); } void setup::setup_CSP() { setup_unknown(); m_context.register_plugin(alloc(smt::theory_jobscheduler, m_manager)); } void setup::setup_special_relations() { m_context.register_plugin(alloc(smt::theory_special_relations, m_manager)); } void setup::setup_unknown() { static_features st(m_manager); ptr_vector fmls; m_context.get_asserted_formulas(fmls); st.collect(fmls.size(), fmls.c_ptr()); TRACE("setup", tout << "setup_unknown\n";); setup_arith(); setup_arrays(); setup_bv(); setup_datatypes(); setup_recfuns(); setup_dl(); setup_seq_str(st); setup_card(); setup_fpa(); if (st.m_has_sr) setup_special_relations(); } void setup::setup_unknown(static_features & st) { TRACE("setup", tout << "setup_unknown\n";); if (st.m_num_quantifiers > 0) { if (st.m_has_real) setup_AUFLIRA(false); else setup_AUFLIA(false); setup_datatypes(); setup_bv(); setup_dl(); setup_seq_str(st); setup_card(); setup_fpa(); setup_recfuns(); if (st.m_has_sr) setup_special_relations(); return; } TRACE("setup", tout << "num non UF theories: " << st.num_non_uf_theories() << "\n"; tout << "num theories: " << st.num_theories() << "\n"; tout << "is_diff_logic: " << is_diff_logic(st) << "\n"; tout << "is_arith: " << is_arith(st) << "\n"; tout << "has UF: " << st.has_uf() << "\n"; tout << "has real: " << st.m_has_real << "\n"; tout << "has int: " << st.m_has_int << "\n"; tout << "has bv: " << st.m_has_bv << "\n"; tout << "has fpa: " << st.m_has_fpa << "\n"; tout << "has arrays: " << st.m_has_arrays << "\n";); if (st.num_non_uf_theories() == 0) { setup_QF_UF(st); return; } if (st.num_theories() == 1 && is_diff_logic(st)) { if (st.m_has_real && !st.m_has_int) setup_QF_RDL(st); else if (!st.m_has_real && st.m_has_int) setup_QF_IDL(st); else setup_unknown(); return; } if (st.num_theories() == 2 && st.has_uf() && is_diff_logic(st)) { if (!st.m_has_real && st.m_has_int) setup_QF_UFIDL(st); else setup_unknown(); return; } if (st.num_theories() == 1 && is_arith(st)) { if ((st.m_has_int && st.m_has_real) || (st.m_num_non_linear != 0)) setup_QF_LIRA(st); else if (st.m_has_real) setup_QF_LRA(st); else setup_QF_LIA(st); return; } if (st.num_theories() == 2 && st.has_uf() && is_arith(st)) { if (!st.m_has_real && st.m_num_non_linear == 0) setup_QF_UFLIA(st); else if (!st.m_has_int && st.m_num_non_linear == 0) setup_QF_UFLRA(); else setup_unknown(); return; } if (st.num_theories() == 1 && st.m_has_bv) { setup_QF_BV(); return; } if (st.num_theories() == 1 && st.m_has_fpa) { setup_QF_FP(); return; } if (st.num_theories() == 2 && st.m_has_fpa && st.m_has_bv) { setup_QF_FPBV(); return; } if (st.num_theories() == 1 && st.m_has_arrays) { setup_QF_AX(st); return; } if (st.num_theories() == 2 && st.has_uf() && st.m_has_arrays && !st.m_has_ext_arrays && st.m_has_bv) { setup_QF_AUFBV(); return; } if (st.num_theories() == 2 && st.has_uf() && st.m_has_arrays && st.m_has_int) { setup_QF_AUFLIA(st); return; } setup_unknown(); } }; z3-z3-4.8.7/src/smt/smt_setup.h000066400000000000000000000075431356505360400162260ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_setup.h Abstract: Author: Leonardo de Moura (leonardo) 2008-06-24. Revision History: --*/ #ifndef SMT_SETUP_H_ #define SMT_SETUP_H_ #include "ast/ast.h" #include "smt/params/smt_params.h" struct static_features; namespace smt { enum config_mode { CFG_BASIC, // install theories based on user options CFG_LOGIC, // install theories and configure Z3 based on the value of the parameter set-logic. CFG_AUTO // install theories based on static features of the input formula }; class context; /** \brief Object used to setup a logical context. \warning In the current version, we can only setup a logical context at scope level 0, and before internalizing any formula. Auxiliary temporary contexts are used to avoid this limitation. */ class setup { context & m_context; ast_manager & m_manager; smt_params & m_params; symbol m_logic; bool m_already_configured; void setup_auto_config(); void setup_default(); // // setup_() methods do not depend on static features of the formula. So, they are safe to use // even in an incremental setting. // // setup_(static_features & st) can only be used if the logical context will perform a single // check. // void setup_QF_DT(); void setup_QF_UF(); void setup_QF_UF(static_features const & st); void setup_QF_RDL(); void setup_QF_RDL(static_features & st); void setup_QF_IDL(); void setup_QF_IDL(static_features & st); void setup_QF_UFIDL(); void setup_QF_UFIDL(static_features & st); void setup_QF_LRA(); void setup_QF_LRA(static_features const & st); void setup_QF_LIA(); void setup_QF_LIRA(static_features const& st); void setup_QF_LIA(static_features const & st); void setup_QF_UFLIA(); void setup_QF_UFLIA(static_features & st); void setup_QF_UFLRA(); void setup_QF_BV(); void setup_QF_AUFBV(); void setup_QF_AX(); void setup_QF_AX(static_features const & st); void setup_QF_AUFLIA(); void setup_QF_AUFLIA(static_features const & st); void setup_QF_FP(); void setup_QF_FPBV(); void setup_QF_S(); void setup_LRA(); void setup_CSP(); void setup_special_relations(); void setup_AUFLIA(bool simple_array = true); void setup_AUFLIA(static_features const & st); void setup_AUFLIRA(bool simple_array = true); void setup_UFNIA(); void setup_UFLRA(); void setup_AUFLIAp(); void setup_AUFNIRA(); void setup_QF_BVRE(); void setup_unknown(); void setup_unknown(static_features & st); void setup_arrays(); void setup_datatypes(); void setup_recfuns(); void setup_bv(); void setup_arith(); void setup_dl(); void setup_seq_str(static_features const & st); void setup_seq(); void setup_card(); void setup_i_arith(); void setup_mi_arith(); void setup_lra_arith(); void setup_fpa(); void setup_str(); public: setup(context & c, smt_params & params); void mark_already_configured() { m_already_configured = true; } bool already_configured() const { return m_already_configured; } bool set_logic(symbol logic) { if (already_configured()) return false; m_logic = logic; return true; } symbol const & get_logic() const { return m_logic; } void operator()(config_mode cm); }; }; #endif /* SMT_SETUP_H_ */ z3-z3-4.8.7/src/smt/smt_solver.cpp000066400000000000000000000367501356505360400167350ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: smt_solver.cpp Abstract: Wraps smt::kernel as a solver for the external API and cmd_context. Author: Leonardo (leonardo) 2012-10-21 Notes: --*/ #include "solver/solver_na2as.h" #include "smt/smt_kernel.h" #include "ast/reg_decl_plugins.h" #include "smt/params/smt_params.h" #include "smt/params/smt_params_helper.hpp" #include "solver/mus.h" #include "ast/for_each_expr.h" #include "ast/ast_smt2_pp.h" #include "ast/func_decl_dependencies.h" #include "util/dec_ref_util.h" namespace smt { class smt_solver : public solver_na2as { struct cuber { smt_solver& m_solver; unsigned m_round; expr_ref m_result; cuber(smt_solver& s): m_solver(s), m_round(0), m_result(s.get_manager()) {} expr_ref cube() { switch (m_round) { case 0: m_result = m_solver.m_context.next_cube(); break; case 1: m_result = m_solver.get_manager().mk_not(m_result); break; default: m_result = m_solver.get_manager().mk_false(); break; } ++m_round; return m_result; } }; smt_params m_smt_params; smt::kernel m_context; cuber* m_cuber; symbol m_logic; bool m_minimizing_core; bool m_core_extend_patterns; unsigned m_core_extend_patterns_max_distance; bool m_core_extend_nonlocal_patterns; obj_map m_name2assertion; public: smt_solver(ast_manager & m, params_ref const & p, symbol const & l) : solver_na2as(m), m_smt_params(p), m_context(m, m_smt_params), m_cuber(nullptr), m_minimizing_core(false), m_core_extend_patterns(false), m_core_extend_patterns_max_distance(UINT_MAX), m_core_extend_nonlocal_patterns(false) { m_logic = l; if (m_logic != symbol::null) m_context.set_logic(m_logic); updt_params(p); } solver * translate(ast_manager & m, params_ref const & p) override { ast_translation translator(get_manager(), m); smt_solver * result = alloc(smt_solver, m, p, m_logic); smt::kernel::copy(m_context, result->m_context); if (mc0()) result->set_model_converter(mc0()->translate(translator)); for (auto & kv : m_name2assertion) { expr* val = translator(kv.m_value); expr* t = translator(kv.m_key); result->m_name2assertion.insert(t, val); result->solver_na2as::assert_expr(val, t); m.inc_ref(val); } return result; } ~smt_solver() override { dec_ref_values(get_manager(), m_name2assertion); dealloc(m_cuber); } void updt_params(params_ref const & p) override { solver::updt_params(p); m_smt_params.updt_params(solver::get_params()); m_context.updt_params(solver::get_params()); smt_params_helper smth(solver::get_params()); m_core_extend_patterns = smth.core_extend_patterns(); m_core_extend_patterns_max_distance = smth.core_extend_patterns_max_distance(); m_core_extend_nonlocal_patterns = smth.core_extend_nonlocal_patterns(); } params_ref m_params_save; smt_params m_smt_params_save; void push_params() override { m_params_save = params_ref(); m_params_save.copy(solver::get_params()); m_smt_params_save = m_smt_params; } void pop_params() override { m_smt_params = m_smt_params_save; solver::reset_params(m_params_save); } void collect_param_descrs(param_descrs & r) override { m_context.collect_param_descrs(r); } void collect_statistics(statistics & st) const override { m_context.collect_statistics(st); } lbool get_consequences_core(expr_ref_vector const& assumptions, expr_ref_vector const& vars, expr_ref_vector& conseq) override { expr_ref_vector unfixed(m_context.m()); return m_context.get_consequences(assumptions, vars, conseq, unfixed); } lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes) override { return m_context.find_mutexes(vars, mutexes); } void assert_expr_core(expr * t) override { m_context.assert_expr(t); } void assert_expr_core2(expr * t, expr * a) override { if (m_name2assertion.contains(a)) { throw default_exception("named assertion defined twice"); } solver_na2as::assert_expr_core2(t, a); get_manager().inc_ref(t); m_name2assertion.insert(a, t); } void push_core() override { m_context.push(); } void pop_core(unsigned n) override { unsigned cur_sz = m_assumptions.size(); if (n > 0 && cur_sz > 0) { unsigned lvl = m_scopes.size(); SASSERT(n <= lvl); unsigned new_lvl = lvl - n; unsigned old_sz = m_scopes[new_lvl]; for (unsigned i = cur_sz; i > old_sz; ) { --i; expr * key = m_assumptions[i].get(); SASSERT(m_name2assertion.contains(key)); expr * value = m_name2assertion.find(key); m.dec_ref(value); m_name2assertion.erase(key); } } m_context.pop(n); } 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); } lbool check_sat_cc_core(expr_ref_vector const& cube, vector const& clauses) override { 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(); } struct scoped_minimize_core { smt_solver& s; expr_ref_vector m_assumptions; scoped_minimize_core(smt_solver& s) : s(s), m_assumptions(s.m_assumptions) { s.m_minimizing_core = true; s.m_assumptions.reset(); } ~scoped_minimize_core() { s.m_minimizing_core = false; s.m_assumptions.append(m_assumptions); } }; void get_unsat_core(expr_ref_vector & r) override { unsigned sz = m_context.get_unsat_core_size(); for (unsigned i = 0; i < sz; i++) { r.push_back(m_context.get_unsat_core_expr(i)); } if (!m_minimizing_core && smt_params_helper(get_params()).core_minimize()) { scoped_minimize_core scm(*this); mus mus(*this); mus.add_soft(r.size(), r.c_ptr()); expr_ref_vector r2(m); if (l_true == mus.get_mus(r2)) { r.reset(); r.append(r2); } } if (m_core_extend_patterns) add_pattern_literals_to_core(r); if (m_core_extend_nonlocal_patterns) add_nonlocal_pattern_literals_to_core(r); } void get_model_core(model_ref & m) override { m_context.get_model(m); } proof * get_proof() override { return m_context.get_proof(); } std::string reason_unknown() const override { return m_context.last_failure_as_string(); } void set_reason_unknown(char const* msg) override { m_context.set_reason_unknown(msg); } void get_labels(svector & r) override { buffer tmp; m_context.get_relevant_labels(nullptr, tmp); r.append(tmp.size(), tmp.c_ptr()); } ast_manager & get_manager() const override { return m_context.m(); } void set_progress_callback(progress_callback * callback) override { m_context.set_progress_callback(callback); } unsigned get_num_assertions() const override { return m_context.size(); } expr * get_assertion(unsigned idx) const override { SASSERT(idx < get_num_assertions()); return m_context.get_formula(idx); } expr_ref_vector cube(expr_ref_vector& vars, unsigned cutoff) override { ast_manager& m = get_manager(); if (!m_cuber) { m_cuber = alloc(cuber, *this); // force propagation push_core(); pop_core(1); } expr_ref result = m_cuber->cube(); expr_ref_vector lits(m); if (m.is_false(result)) { dealloc(m_cuber); m_cuber = nullptr; } if (m.is_true(result)) { dealloc(m_cuber); m_cuber = nullptr; return lits; } lits.push_back(result); return lits; } struct collect_fds_proc { ast_manager & m; func_decl_set & m_fds; collect_fds_proc(ast_manager & m, func_decl_set & fds) : m(m), m_fds(fds) { } void operator()(var * n) {} void operator()(app * n) { func_decl * fd = n->get_decl(); if (fd->get_family_id() == null_family_id) m_fds.insert_if_not_there(fd); } void operator()(quantifier * n) {} }; struct collect_pattern_fds_proc { ast_manager & m; expr_fast_mark1 m_visited; func_decl_set & m_fds; collect_pattern_fds_proc(ast_manager & m, func_decl_set & fds) : m(m), m_fds(fds) { m_visited.reset(); } void operator()(var * n) {} void operator()(app * n) {} void operator()(quantifier * n) { collect_fds_proc p(m, m_fds); unsigned sz = n->get_num_patterns(); for (unsigned i = 0; i < sz; i++) quick_for_each_expr(p, m_visited, n->get_pattern(i)); sz = n->get_num_no_patterns(); for (unsigned i = 0; i < sz; i++) quick_for_each_expr(p, m_visited, n->get_no_pattern(i)); } }; void collect_pattern_fds(expr_ref & e, func_decl_set & fds) { collect_pattern_fds_proc p(get_manager(), fds); expr_mark visited; for_each_expr(p, visited, e); } void compute_assrtn_fds(expr_ref_vector & core, vector & assrtn_fds) { assrtn_fds.resize(m_name2assertion.size()); unsigned i = 0; for (auto & kv : m_name2assertion) { if (!core.contains(kv.m_key)) { collect_fds_proc p(m, assrtn_fds[i]); expr_fast_mark1 visited; quick_for_each_expr(p, visited, kv.m_value); } ++i; } } bool fds_intersect(func_decl_set & pattern_fds, func_decl_set & assrtn_fds) { for (func_decl * fd : pattern_fds) { if (assrtn_fds.contains(fd)) return true; } return false; } void add_pattern_literals_to_core(expr_ref_vector & core) { ast_manager & m = get_manager(); expr_ref_vector new_core_literals(m); func_decl_set pattern_fds; vector assrtn_fds; for (unsigned d = 0; d < m_core_extend_patterns_max_distance; d++) { new_core_literals.reset(); for (expr* c : core) { expr_ref name(c, m); SASSERT(m_name2assertion.contains(name)); expr_ref assrtn(m_name2assertion.find(name), m); collect_pattern_fds(assrtn, pattern_fds); } if (!pattern_fds.empty()) { if (assrtn_fds.empty()) compute_assrtn_fds(core, assrtn_fds); unsigned i = 0; for (auto & kv : m_name2assertion) { if (!core.contains(kv.m_key) && fds_intersect(pattern_fds, assrtn_fds[i])) new_core_literals.push_back(kv.m_key); ++i; } } core.append(new_core_literals.size(), new_core_literals.c_ptr()); if (new_core_literals.empty()) break; } } struct collect_body_fds_proc { ast_manager & m; func_decl_set & m_fds; collect_body_fds_proc(ast_manager & m, func_decl_set & fds) : m(m), m_fds(fds) { } void operator()(var * n) {} void operator()(app * n) {} void operator()(quantifier * n) { collect_fds_proc p(m, m_fds); expr_fast_mark1 visited; quick_for_each_expr(p, visited, n->get_expr()); } }; void collect_body_func_decls(expr_ref & e, func_decl_set & fds) { ast_manager & m = get_manager(); collect_body_fds_proc p(m, fds); expr_mark visited; for_each_expr(p, visited, e); } void add_nonlocal_pattern_literals_to_core(expr_ref_vector & core) { ast_manager & m = get_manager(); for (auto const& kv : m_name2assertion) { expr_ref name(kv.m_key, m); expr_ref assrtn(kv.m_value, m); if (!core.contains(name)) { func_decl_set pattern_fds, body_fds; collect_pattern_fds(assrtn, pattern_fds); collect_body_func_decls(assrtn, body_fds); for (func_decl *fd : pattern_fds) { if (!body_fds.contains(fd) && !core.contains(name)) { core.push_back(name); break; } } } } } }; }; solver * mk_smt_solver(ast_manager & m, params_ref const & p, symbol const & logic) { return alloc(smt::smt_solver, m, p, logic); } class smt_solver_factory : public solver_factory { public: solver * operator()(ast_manager & m, params_ref const & p, bool proofs_enabled, bool models_enabled, bool unsat_core_enabled, symbol const & logic) override { return mk_smt_solver(m, p, logic); } }; solver_factory * mk_smt_solver_factory() { return alloc(smt_solver_factory); } z3-z3-4.8.7/src/smt/smt_solver.h000066400000000000000000000010471356505360400163710ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: smt_solver.h Abstract: Wraps smt::kernel as a solver for the external API and cmd_context. Author: Leonardo (leonardo) 2012-10-21 Notes: This file was called default_solver.h. It was a bad name. --*/ #ifndef SMT_SOLVER_H_ #define SMT_SOLVER_H_ #include "ast/ast.h" #include "util/params.h" class solver; class solver_factory; solver * mk_smt_solver(ast_manager & m, params_ref const & p, symbol const & logic); solver_factory * mk_smt_solver_factory(); #endif z3-z3-4.8.7/src/smt/smt_statistics.cpp000066400000000000000000000005331356505360400176030ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_statistics.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-02-21. Revision History: --*/ #include #include "smt/smt_statistics.h" namespace smt { void statistics::reset() { memset(this, 0, sizeof(statistics)); } }; z3-z3-4.8.7/src/smt/smt_statistics.h000066400000000000000000000022701356505360400172500ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_statistics.h Abstract: Author: Leonardo de Moura (leonardo) 2008-02-21. Revision History: --*/ #ifndef SMT_STATISTICS_H_ #define SMT_STATISTICS_H_ #include namespace smt { struct statistics { unsigned m_num_propagations; unsigned m_num_bin_propagations; unsigned m_num_conflicts; unsigned m_num_sat_conflicts; unsigned m_num_decisions; unsigned m_num_add_eq; unsigned m_num_restarts; unsigned m_num_final_checks; unsigned m_num_mk_bool_var; unsigned m_num_del_bool_var; unsigned m_num_mk_enode; unsigned m_num_del_enode; unsigned m_num_mk_clause; unsigned m_num_del_clause; unsigned m_num_mk_bin_clause; unsigned m_num_mk_lits; unsigned m_num_dyn_ack; unsigned m_num_del_dyn_ack; unsigned m_num_interface_eqs; unsigned m_max_generation; unsigned m_num_minimized_lits; unsigned m_num_checks; statistics() { reset(); } void reset(); }; }; #endif /* SMT_STATISTICS_H_ */ z3-z3-4.8.7/src/smt/smt_theory.cpp000066400000000000000000000151401356505360400167230ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_theory.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-02-20. Revision History: --*/ #include "smt/smt_context.h" #include "util/buffer.h" #include "ast/ast_ll_pp.h" namespace smt { void theory::init(context * ctx) { SASSERT(m_context == 0); m_context = ctx; m_manager = &(ctx->get_manager()); } void theory::reset_eh() { m_var2enode.reset(); } void theory::push_scope_eh() { SASSERT(m_context); m_var2enode_lim.push_back(m_var2enode.size()); } void theory::pop_scope_eh(unsigned num_scopes) { SASSERT(m_context); unsigned scope_lvl = m_var2enode_lim.size(); SASSERT(num_scopes <= scope_lvl); unsigned new_lvl = scope_lvl - num_scopes; unsigned old_sz = m_var2enode_lim[new_lvl]; m_var2enode.shrink(old_sz); m_var2enode_lim.shrink(new_lvl); } void theory::display_var2enode(std::ostream & out) const { unsigned sz = m_var2enode.size(); for (unsigned v = 0; v < sz; v++) { out << "v" << v << " -> #" << m_var2enode[v]->get_owner_id() << "\n"; } } std::ostream& theory::display_app(std::ostream & out, app * n) const { func_decl * d = n->get_decl(); if (n->get_num_args() == 0) { out << d->get_name(); display_parameters(out, d->get_num_parameters(), d->get_parameters()); } else if (n->get_family_id() == get_family_id()) { out << "(" << d->get_name(); display_parameters(out, d->get_num_parameters(), d->get_parameters()); unsigned num = n->get_num_args(); for (unsigned i = 0; i < num; i++) { out << " "; display_app(out, to_app(n->get_arg(i))); } out << ")"; } else { out << "#" << n->get_id(); } return out; } std::ostream& theory::display_flat_app(std::ostream & out, app * n) const { func_decl * d = n->get_decl(); if (n->get_num_args() == 0) { out << d->get_name(); display_parameters(out, d->get_num_parameters(), d->get_parameters()); } else if (n->get_family_id() == get_family_id()) { out << "(" << d->get_name(); display_parameters(out, d->get_num_parameters(), d->get_parameters()); ptr_buffer todo; todo.push_back(n); while (!todo.empty()) { n = todo.back(); todo.pop_back(); unsigned num = n->get_num_args(); for (unsigned i = 0; i < num; i++) { app * arg = to_app(n->get_arg(i)); if (d->is_associative() && arg->get_decl() == d) { todo.push_back(arg); } else { out << " "; display_app(out, arg); } } } out << ")"; } else { out << "#" << n->get_id(); } return out; } bool theory::is_relevant_and_shared(enode * n) const { context & ctx = get_context(); return ctx.is_relevant(n) && ctx.is_shared(n); } bool theory::assume_eq(enode * n1, enode * n2) { return get_context().assume_eq(n1, n2); } literal theory::mk_eq(expr * a, expr * b, bool gate_ctx) { if (a == b) { return true_literal; } context & ctx = get_context(); app * eq = ctx.mk_eq_atom(a, b); TRACE("mk_var_bug", tout << "mk_eq: " << eq->get_id() << " " << a->get_id() << " " << b->get_id() << "\n"; tout << mk_ll_pp(a, get_manager()) << "\n" << mk_ll_pp(b, get_manager());); ctx.internalize(eq, gate_ctx); return ctx.get_literal(eq); } theory::theory(family_id fid): m_id(fid), m_context(nullptr), m_manager(nullptr) { } theory::~theory() { } void theory::log_axiom_instantiation(app * r, unsigned axiom_id, unsigned num_bindings, app * const * bindings, unsigned pattern_id, const vector> & used_enodes) { ast_manager & m = get_manager(); std::ostream& out = m.trace_stream(); symbol const & family_name = m.get_family_name(get_family_id()); if (pattern_id == UINT_MAX) { out << "[inst-discovered] theory-solving " << static_cast(nullptr) << " " << family_name << "#"; if (axiom_id != UINT_MAX) out << axiom_id; for (unsigned i = 0; i < num_bindings; ++i) { out << " #" << bindings[i]->get_id(); } if (used_enodes.size() > 0) { out << " ;"; for (auto n : used_enodes) { enode *substituted = std::get<1>(n); SASSERT(std::get<0>(n) == nullptr); out << " #" << substituted->get_owner_id(); } } } else { SASSERT(axiom_id != UINT_MAX); obj_hashtable already_visited; for (auto n : used_enodes) { enode *orig = std::get<0>(n); enode *substituted = std::get<1>(n); if (orig != nullptr) { quantifier_manager::log_justification_to_root(out, orig, already_visited, get_context(), get_manager()); quantifier_manager::log_justification_to_root(out, substituted, already_visited, get_context(), get_manager()); } } out << "[new-match] " << static_cast(nullptr) << " " << family_name << "#" << axiom_id << " " << family_name << "#" << pattern_id; for (unsigned i = 0; i < num_bindings; ++i) { out << " #" << bindings[i]->get_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"; out << "[instance] " << static_cast(nullptr) << " #" << r->get_id() << "\n"; out.flush(); } theory_var theory::get_th_var(expr* e) const { return get_th_var(get_context().get_enode(e)); } }; z3-z3-4.8.7/src/smt/smt_theory.h000066400000000000000000000431501356505360400163720ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_theory.h Abstract: Author: Leonardo de Moura (leonardo) 2008-02-20. Revision History: --*/ #ifndef SMT_THEORY_H_ #define SMT_THEORY_H_ #include "smt/smt_enode.h" #include "smt/smt_quantifier.h" #include "util/obj_hashtable.h" #include "util/statistics.h" #include namespace smt { class model_generator; class model_value_proc; class theory { theory_id m_id; context * m_context; ast_manager * m_manager; enode_vector m_var2enode; unsigned_vector m_var2enode_lim; friend class context; friend class arith_value; protected: virtual void init(context * ctx); /* --------------------------------------------------- In the logical context, expressions are 'internalized'. That is, the logical context creates auxiliary data-structures (e.g., enodes) and attach them to the expressions. The logical context does not know the internals of each theory. So, during the internalization process, it notifies the theory (plugin) whenever it finds an application with a theory function symbol. A theory variable created at scope level n must be deleted when scope level n is backtracked. The logical context uses the method is_attached_to_var to decide whether an enode is already associated with a theory variable or not. ------------------------------------------------------ */ virtual theory_var mk_var(enode * n) { SASSERT(!is_attached_to_var(n)); theory_var v = m_var2enode.size(); m_var2enode.push_back(n); return v; } theory_var get_th_var(expr* e) const; theory_var get_th_var(enode* n) const { return n->get_th_var(get_id()); } public: /** \brief Return true if the given enode is attached to a variable of the theory. \remark The result is not equivalent to n->get_th_var(get_id()) != null_theory_var A theory variable v may be in the list of variables of n, but it may be inherited from another enode n' during an equivalence class merge. That is, get_enode(v) != n. */ bool is_attached_to_var(enode const * n) const { theory_var v = n->get_th_var(get_id()); return v != null_theory_var && get_enode(v) == n; } struct scoped_trace_stream { ast_manager& m; scoped_trace_stream(ast_manager& m, std::function& fn): m(m) { if (m.has_trace_stream()) { fn(); } } scoped_trace_stream(theory& th, std::function& fn): m(th.get_manager()) { if (m.has_trace_stream()) { expr_ref body(fn(), m); th.log_axiom_instantiation(body); } } ~scoped_trace_stream() { if (m.has_trace_stream()) { m.trace_stream() << "[end-of-instance]\n"; } } }; protected: /** \brief Return true if the theory uses default internalization: "the internalization of an application internalizes all arguments". Theories like arithmetic do not use default internalization. For example, in the application (+ a (+ b c)), no enode is created for (+ b c). */ virtual bool default_internalizer() const { return true; } /** \brief This method is invoked by the logical context when atom is being internalized. The theory may return false if it does not want to implement the given predicate symbol. After the execution of this method the given atom must be associated with a new boolean variable. */ virtual bool internalize_atom(app * atom, bool gate_ctx) = 0; /** \brief This method is invoked by the logical context after the given equality atom is internalized. */ virtual void internalize_eq_eh(app * atom, bool_var v) { } /** \brief This method is invoked by the logical context when the term is being internalized. The theory may return false if it does not want to implement the given function symbol. After the execution of this method the given term must be associated with a new enode. */ virtual bool internalize_term(app * term) = 0; /** \brief Apply (interpreted) sort constraints on the given enode. */ virtual void apply_sort_cnstr(enode * n, sort * s) { } /** \brief This method is invoked when a truth value is assigned to the given boolean variable. */ virtual void assign_eh(bool_var v, bool is_true) { } /** \brief use the theory to determine phase of the variable. */ virtual lbool get_phase(bool_var v) { return l_undef; } /** \brief Equality propagation (v1 = v2): Core -> Theory */ virtual void new_eq_eh(theory_var v1, theory_var v2) = 0; /** \brief Return true if the theory does something with the disequalities implied by the core. */ virtual bool use_diseqs() const { return true; } /** \brief Disequality propagation (v1 /= v2): Core -> Theory */ virtual void new_diseq_eh(theory_var v1, theory_var v2) = 0; /** \brief This method is invoked when the theory application n is marked as relevant. */ virtual void relevant_eh(app * n) { } /** \brief This method is invoked when a new backtracking point is created. */ virtual void push_scope_eh(); /** \brief This method is invoked during backtracking. */ virtual void pop_scope_eh(unsigned num_scopes); /** \brief This method is invoked when the logical context is being restarted. */ virtual void restart_eh() { } /** \brief This method is called by smt_context before the search starts to get any extra assumptions the theory wants to use. (See theory_str for an example) */ virtual void add_theory_assumptions(expr_ref_vector & assumptions) { } /** \brief This method is called from the smt_context when an unsat core is generated. The theory may change the answer to UNKNOWN by returning l_undef from this method. */ virtual lbool validate_unsat_core(expr_ref_vector & unsat_core) { return l_false; } /** \brief This method is called from the smt_context when an unsat core is generated. The theory may tell the solver to perform iterative deepening by invalidating this unsat core and increasing some resource constraints. */ virtual bool should_research(expr_ref_vector & unsat_core) { return false; } /** \brief This method is invoked before the search starts. */ virtual void init_search_eh() { } /** \brief This method is invoked when the logical context assigned a truth value to all boolean variables and no inconsistency was detected. */ virtual final_check_status final_check_eh() { return FC_DONE; } /** \brief Parametric theories (e.g. Arrays) should implement this method. See example in context::is_shared */ virtual bool is_shared(theory_var v) const { return false; } /** \brief Return true if the theory has something to propagate */ virtual bool can_propagate() { return false; } /** \brief This method is invoked to give a theory a chance to perform theory propagation. */ virtual void propagate() { } /** \brief This method allows a theory to contribute to disequality propagation. */ virtual justification * why_is_diseq(theory_var v1, theory_var v2) { return nullptr; } /** \brief Just releases memory. */ virtual void flush_eh() { } /** \brief This method is invoked when the logical context is being reset. */ virtual void reset_eh(); // ---------------------------------------------------- // // Model validation // // ---------------------------------------------------- virtual void validate_model(model& mdl) {} // ---------------------------------------------------- // // Conflict resolution event handler // // ---------------------------------------------------- public: /** \brief This method is invoked when a theory atom is used during conflict resolution. This allows the theory to bump the activity of the enodes contained in the given atom. */ virtual void conflict_resolution_eh(app * atom, bool_var v) { } public: theory(family_id fid); virtual ~theory(); virtual void setup() { } theory_id get_id() const { return m_id; } family_id get_family_id() const { return m_id; } context & get_context() const { SASSERT(m_context); return *m_context; } context & ctx() const { return get_context(); } ast_manager & get_manager() const { SASSERT(m_manager); return *m_manager; } enode * get_enode(theory_var v) const { SASSERT(v < static_cast(m_var2enode.size())); return m_var2enode[v]; } app * get_expr(theory_var v) const { return get_enode(v)->get_owner(); } /** \brief Return the equivalence class representative of the given theory variable. */ theory_var get_representative(theory_var v) const { SASSERT(v != null_theory_var); theory_var r = get_enode(v)->get_root()->get_th_var(get_id()); SASSERT(r != null_theory_var); return r; } /** \brief Return true if the theory variable is the representative of its equivalence class. */ bool is_representative(theory_var v) const { return get_representative(v) == v; } unsigned get_num_vars() const { return m_var2enode.size(); } unsigned get_old_num_vars(unsigned num_scopes) const { return m_var2enode_lim[m_var2enode_lim.size() - num_scopes]; } virtual void display(std::ostream & out) const = 0; virtual void display_var2enode(std::ostream & out) const; virtual void collect_statistics(::statistics & st) const { } std::ostream& display_app(std::ostream & out, app * n) const; std::ostream& display_flat_app(std::ostream & out, app * n) const; std::ostream& display_var_def(std::ostream & out, theory_var v) const { return display_app(out, get_enode(v)->get_owner()); } std::ostream& display_var_flat_def(std::ostream & out, theory_var v) const { return display_flat_app(out, get_enode(v)->get_owner()); } protected: void log_axiom_instantiation(app * r, unsigned axiom_id = UINT_MAX, unsigned num_bindings = 0, app * const * bindings = nullptr, unsigned pattern_id = UINT_MAX, const vector> & used_enodes = vector>()); void log_axiom_instantiation(expr * r, unsigned axiom_id = UINT_MAX, unsigned num_bindings = 0, app * const * bindings = nullptr, unsigned pattern_id = UINT_MAX, const vector> & used_enodes = vector>()) { log_axiom_instantiation(to_app(r), axiom_id, num_bindings, bindings, pattern_id, used_enodes); } void log_axiom_instantiation(app * r, unsigned num_blamed_enodes, enode ** blamed_enodes) { vector> used_enodes; for (unsigned i = 0; i < num_blamed_enodes; ++i) { used_enodes.push_back(std::make_tuple(nullptr, blamed_enodes[i])); } log_axiom_instantiation(r, UINT_MAX, 0, nullptr, UINT_MAX, used_enodes); } public: /** \brief Assume eqs between variable that are equal with respect to the given table. Table is a hashtable indexed by the variable value. table.contains(v) should be true if there is v' in table such that assignment of v is equal to v'. This method assumes that class VarValueTable contains the methods: - void reset() - theory_var insert_if_not_there(theory_var v) */ template bool assume_eqs(VarValueTable & table) { TRACE("assume_eqs", tout << "starting...\n";); table.reset(); bool result = false; int num = get_num_vars(); for (theory_var v = 0; v < num; v++) { enode * n = get_enode(v); theory_var other = null_theory_var; TRACE("assume_eqs", tout << "#" << n->get_owner_id() << " is_relevant_and_shared: " << is_relevant_and_shared(n) << "\n";); if (n != nullptr && is_relevant_and_shared(n)) { other = table.insert_if_not_there(v); if (other != v) { enode * n2 = get_enode(other); TRACE("assume_eqs", tout << "value(#" << n->get_owner_id() << ") = value(#" << n2->get_owner_id() << ")\n";); if (assume_eq(n, n2)) { TRACE("assume_eqs", tout << "new assumed eq\n";); result = true; } } } } return result; } /** \brief When an eq atom n is created during the search, the default behavior is to make sure that the n->get_arg(0)->get_id() < n->get_arg(1)->get_id(). This may create some redundant atoms, since some theories/families use different conventions in their simplifiers. For example, arithmetic always force a numeral to be in the right hand side. So, this method should be redefined if the default behavior conflicts with a convention used by the theory/family. */ virtual app * mk_eq_atom(expr * lhs, expr * rhs) { if (lhs->get_id() > rhs->get_id()) std::swap(lhs, rhs); return get_manager().mk_eq(lhs, rhs); } literal mk_eq(expr * a, expr * b, bool gate_ctx); // ----------------------------------- // // Model generation // // ----------------------------------- /** \brief Return true if theory support model construction */ virtual bool build_models() const { return true; } virtual void init_model(model_generator & m) { } virtual void finalize_model(model_generator & m) { } /** \brief Return a functor that can build the value (interpretation) for n. */ virtual model_value_proc * mk_value(enode * n, model_generator & mg) { return nullptr; } virtual bool include_func_interp(func_decl* f) { return false; } // ----------------------------------- // // Model checker // // ----------------------------------- virtual bool get_value(enode * n, expr_ref & r) { return false; } virtual char const * get_name() const { return "unknown"; } // ----------------------------------- // // Return a fresh new instance of the given theory. // This function is used to create a fresh context (new_ctx) containing the same theories of the context that owns this theory. // // We need the parameter new_ctx because of user_smt_theory :-( // // ----------------------------------- virtual theory * mk_fresh(context * new_ctx) = 0; protected: // ---------------------------------------------------- // // Auxiliary methods for assume_eqs // // smt::context is not defined at this point. // // ---------------------------------------------------- bool is_relevant_and_shared(enode * n) const; bool assume_eq(enode * n1, enode * n2); }; }; #endif /* SMT_THEORY_H_ */ z3-z3-4.8.7/src/smt/smt_theory_var_list.h000066400000000000000000000032301356505360400202700ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_theory_var_list.h Abstract: Author: Leonardo de Moura (leonardo) 2008-02-19. Revision History: --*/ #ifndef SMT_THEORY_VAR_LIST_H_ #define SMT_THEORY_VAR_LIST_H_ #include "smt/smt_types.h" namespace smt { class theory_var_list { int m_th_id:8; int m_th_var:24; theory_var_list * m_next; public: theory_var_list(): m_th_id(null_theory_id), m_th_var(null_theory_var), m_next(nullptr) { } theory_var_list(theory_id t, theory_var v, theory_var_list * n = nullptr): m_th_id(t), m_th_var(v), m_next(n) { } theory_id get_th_id() const { return m_th_id; } theory_var get_th_var() const { return m_th_var; } theory_var_list * get_next() const { return m_next; } void set_th_id(theory_id id) { m_th_id = id; } void set_th_var(theory_var v) { m_th_var = v; } void set_next(theory_var_list * next) { m_next = next; } }; // 32 bit machine static_assert(sizeof(expr*) != 4 || sizeof(theory_var_list) == sizeof(theory_var_list *) + sizeof(int), "32 bit"); // 64 bit machine static_assert(sizeof(expr*) != 8 || sizeof(theory_var_list) == sizeof(theory_var_list *) + sizeof(int) + /* a structure must be aligned */ sizeof(int), "64 bit"); }; #endif /* SMT_THEORY_VAR_LIST_H_ */ z3-z3-4.8.7/src/smt/smt_types.h000066400000000000000000000031511356505360400162210ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: smt_types.h Abstract: Basic types for the SMT engine Author: Leonardo de Moura (leonardo) 2008-02-19. Revision History: --*/ #ifndef SMT_TYPES_H_ #define SMT_TYPES_H_ #include "util/list.h" #include "util/vector.h" #include "util/hashtable.h" #include "util/lbool.h" class model; namespace smt { /** \brief A boolean variable is just an integer. */ typedef int bool_var; const bool_var null_bool_var = -1; const bool_var true_bool_var = 0; const bool_var first_bool_var = 1; typedef svector bool_var_vector; typedef family_id theory_id; const theory_id null_theory_id = null_family_id; typedef int theory_var; const theory_var null_theory_var = -1; class enode; typedef ptr_vector enode_vector; typedef std::pair enode_pair; typedef svector enode_pair_vector; class context; class theory; class justification; class model_generator; enum final_check_status { FC_DONE, FC_CONTINUE, FC_GIVEUP }; inline std::ostream & operator<<(std::ostream & out, final_check_status st) { switch (st) { case FC_DONE: out << "done"; break; case FC_CONTINUE: out << "continue"; break; case FC_GIVEUP: out << "giveup"; break; } return out; } // if defined, then clauses have an extra mask field used to optimize backward subsumption, and backward/forward subsumption resolution. #define APPROX_LIT_SET }; #endif /* SMT_TYPES_H_ */ z3-z3-4.8.7/src/smt/smt_value_sort.cpp000066400000000000000000000032421356505360400175740ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: smt_value_sort.cpp Abstract: Determine if elements of a given sort can be values. Author: Nikolaj Bjorner (nbjorner) 2012-11-25 Revision History: --*/ #include "smt/smt_value_sort.h" #include "ast/bv_decl_plugin.h" #include "ast/arith_decl_plugin.h" #include "ast/datatype_decl_plugin.h" namespace smt { bool is_value_sort(ast_manager& m, sort* s) { arith_util arith(m); datatype_util data(m); bv_util bv(m); ptr_vector sorts; ast_mark mark; sorts.push_back(s); while (!sorts.empty()) { s = sorts.back(); sorts.pop_back(); if (mark.is_marked(s)) { continue; } mark.mark(s, true); if (arith.is_int_real(s)) { // simple } else if (m.is_bool(s)) { // simple } else if (bv.is_bv_sort(s)) { // simple } else if (data.is_datatype(s)) { ptr_vector const& cs = *data.get_datatype_constructors(s); for (unsigned i = 0; i < cs.size(); ++i) { func_decl* f = cs[i]; for (unsigned j = 0; j < f->get_arity(); ++j) { sorts.push_back(f->get_domain(j)); } } } else { return false; } } return true; } bool is_value_sort(ast_manager& m, expr* e) { return is_value_sort(m, m.get_sort(e)); } } z3-z3-4.8.7/src/smt/smt_value_sort.h000066400000000000000000000007001356505360400172350ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: smt_value_sort.h Abstract: Determine if elements of a given sort can be values. Author: Nikolaj Bjorner (nbjorner) 2012-11-25 Revision History: --*/ #ifndef SMT_VALUE_SORT_H_ #define SMT_VALUE_SORT_H_ #include "ast/ast.h" namespace smt { bool is_value_sort(ast_manager& m, sort* s); bool is_value_sort(ast_manager& m, expr* e); }; #endif z3-z3-4.8.7/src/smt/spanning_tree.h000066400000000000000000000045121356505360400170300ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: spanning_tree.h Abstract: Represent spanning trees with needed operations for Network Simplex Author: Anh-Dung Phan (t-anphan) 2013-11-06 Notes: --*/ #ifndef SPANNING_TREE_H_ #define SPANNING_TREE_H_ #include "smt/diff_logic.h" #include "smt/spanning_tree_base.h" namespace smt { template class thread_spanning_tree : public spanning_tree_base, protected Ext { protected: typedef dl_edge edge; typedef dl_graph graph; typedef typename Ext::numeral numeral; typedef typename Ext::fin_numeral fin_numeral; // Store the parent of a node i in the spanning tree svector m_pred; // Store the number of edge on the path from node i to the root svector m_depth; svector m_thread; // Store the pointer from node i to the next node in depth-first search order svector m_tree; // i |-> edge between (i, m_pred[i]) node_id m_root_t2; graph & m_graph; void swap_order(node_id q, node_id v); node_id find_rev_thread(node_id n) const; void fix_depth(node_id start, node_id after_end); node_id get_final(int start); bool is_preorder_traversal(node_id start, node_id end); node_id get_common_ancestor(node_id u, node_id v); bool is_forward_edge(edge_id e_id) const; bool is_ancestor_of(node_id ancestor, node_id child); public: thread_spanning_tree(graph & g); virtual void initialize(svector const & tree); void get_descendants(node_id start, svector & descendants); virtual void update(edge_id enter_id, edge_id leave_id); void get_path(node_id start, node_id end, svector & path, svector & against); bool in_subtree_t2(node_id child); bool check_well_formed(); }; template class basic_spanning_tree : public thread_spanning_tree { private: graph * m_tree_graph; public: basic_spanning_tree(graph & g); void initialize(svector const & tree); void update(edge_id enter_id, edge_id leave_id); }; } #endif z3-z3-4.8.7/src/smt/spanning_tree_base.h000066400000000000000000000023271356505360400200240ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: spanning_tree_base.h Abstract: Represent spanning trees with needed operations for Network Simplex Author: Anh-Dung Phan (t-anphan) 2013-11-06 Notes: --*/ #ifndef SPANNING_TREE_BASE_H_ #define SPANNING_TREE_BASE_H_ #include "util/util.h" #include "util/vector.h" namespace smt { template inline std::string pp_vector(std::string const & label, TV v) { std::ostringstream oss; oss << label << " "; for (unsigned i = 0; i < v.size(); ++i) { oss << v[i] << " "; } oss << std::endl; return oss.str(); } class spanning_tree_base { public: typedef int node_id; typedef int edge_id; virtual void initialize(svector const & tree) = 0; virtual void get_descendants(node_id start, svector & descendants) = 0; virtual void update(edge_id enter_id, edge_id leave_id) = 0; virtual void get_path(node_id start, node_id end, svector & path, svector & against) = 0; virtual bool in_subtree_t2(node_id child) = 0; virtual bool check_well_formed() = 0; }; } #endif z3-z3-4.8.7/src/smt/spanning_tree_def.h000066400000000000000000000372551356505360400176600ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: spanning_tree_def.h Abstract: Author: Anh-Dung Phan (t-anphan) 2013-11-06 Notes: --*/ #ifndef SPANNING_TREE_DEF_H_ #define SPANNING_TREE_DEF_H_ #include "smt/spanning_tree.h" namespace smt { template thread_spanning_tree::thread_spanning_tree(graph & g) : m_graph(g) { } template void thread_spanning_tree::initialize(svector const & tree) { m_tree = tree; unsigned num_nodes = m_graph.get_num_nodes(); m_pred.resize(num_nodes); m_depth.resize(num_nodes); m_thread.resize(num_nodes); node_id root = num_nodes - 1; m_pred[root] = -1; m_depth[root] = 0; m_thread[root] = 0; // Create artificial edges from/to root node to/from other nodes and initialize the spanning tree for (int i = 0; i < root; ++i) { m_pred[i] = root; m_depth[i] = 1; m_thread[i] = i + 1; } TRACE("network_flow", { tout << pp_vector("Predecessors", m_pred) << pp_vector("Threads", m_thread); tout << pp_vector("Depths", m_depth) << pp_vector("Tree", m_tree); }); } template typename thread_spanning_tree::node_id thread_spanning_tree::get_common_ancestor(node_id u, node_id v) { while (u != v) { if (m_depth[u] > m_depth[v]) u = m_pred[u]; else v = m_pred[v]; } return u; } template void thread_spanning_tree::get_path(node_id start, node_id end, svector & path, svector & against) { node_id join = get_common_ancestor(start, end); path.reset(); while (start != join) { edge_id e_id = m_tree[start]; path.push_back(e_id); against.push_back(is_forward_edge(e_id)); start = m_pred[start]; } while (end != join) { edge_id e_id = m_tree[end]; path.push_back(e_id); against.push_back(!is_forward_edge(e_id)); end = m_pred[end]; } } template bool thread_spanning_tree::is_forward_edge(edge_id e_id) const { node_id start = m_graph.get_source(e_id); node_id end = m_graph.get_target(e_id); SASSERT(m_pred[start] == end || m_pred[end] == start); return m_pred[start] == end; } template void thread_spanning_tree::get_descendants(node_id start, svector & descendants) { descendants.reset(); descendants.push_back(start); node_id u = m_thread[start]; while (m_depth[u] > m_depth[start]) { descendants.push_back(u); u = m_thread[u]; } } template bool thread_spanning_tree::in_subtree_t2(node_id child) { if (m_depth[child] < m_depth[m_root_t2]) { return false; } return is_ancestor_of(m_root_t2, child); } template bool thread_spanning_tree::is_ancestor_of(node_id ancestor, node_id child) { for (node_id n = child; n != -1; n = m_pred[n]) { if (n == ancestor) { return true; } } return false; } /** \brief add entering_edge, remove leaving_edge from spanning tree. Old tree: New tree: root root / \ / \ x y x y / \ / \ / \ / \ u s u s | / / v w v w / \ \ / \ \ z p z p \ \ / q q */ template void thread_spanning_tree::update(edge_id enter_id, edge_id leave_id) { node_id p = m_graph.get_source(enter_id); node_id q = m_graph.get_target(enter_id); node_id u = m_graph.get_source(leave_id); node_id v = m_graph.get_target(leave_id); if (m_pred[u] == v) { std::swap(u, v); } SASSERT(m_pred[v] == u); if (is_ancestor_of(v, p)) { std::swap(p, q); } SASSERT(is_ancestor_of(v, q)); TRACE("network_flow", { tout << "update_spanning_tree: (" << p << ", " << q << ") enters, ("; tout << u << ", " << v << ") leaves\n"; }); // Old threads: alpha -> v -*-> f(v) -> beta | p -*-> f(p) -> gamma // New threads: alpha -> beta | p -*-> f(p) -> v -*-> f(v) -> gamma node_id f_p = get_final(p); node_id f_v = get_final(v); node_id alpha = find_rev_thread(v); node_id beta = m_thread[f_v]; node_id gamma = m_thread[f_p]; if (v != gamma) { m_thread[alpha] = beta; m_thread[f_p] = v; m_thread[f_v] = gamma; } node_id old_pred = m_pred[q]; // Update stem nodes from q to v if (q != v) { for (node_id n = q; n != v; ) { SASSERT(old_pred != u); // the last processed node_id is v SASSERT(-1 != m_pred[old_pred]); int next_old_pred = m_pred[old_pred]; swap_order(n, old_pred); m_tree[old_pred] = m_tree[n]; n = old_pred; old_pred = next_old_pred; } } m_pred[q] = p; m_tree[q] = enter_id; m_root_t2 = q; node_id after_final_q = (v == gamma) ? beta : gamma; fix_depth(q, after_final_q); SASSERT(!in_subtree_t2(p)); SASSERT(in_subtree_t2(q)); SASSERT(!in_subtree_t2(u)); SASSERT(in_subtree_t2(v)); TRACE("network_flow", { tout << pp_vector("Predecessors", m_pred) << pp_vector("Threads", m_thread); tout << pp_vector("Depths", m_depth) << pp_vector("Tree", m_tree); }); } /** swap v and q in tree. - fixup m_thread - fixup m_pred Case 1: final(q) == final(v) ------- Old thread: prev -> v -*-> alpha -> q -*-> final(q) -> next New thread: prev -> q -*-> final(q) -> v -*-> alpha -> next Case 2: final(q) != final(v) ------- Old thread: prev -> v -*-> alpha -> q -*-> final(q) -> beta -*-> final(v) -> next New thread: prev -> q -*-> final(q) -> v -*-> alpha -> beta -*-> final(v) -> next */ template void thread_spanning_tree::swap_order(node_id q, node_id v) { SASSERT(q != v); SASSERT(m_pred[q] == v); SASSERT(is_preorder_traversal(v, get_final(v))); node_id prev = find_rev_thread(v); node_id f_q = get_final(q); node_id f_v = get_final(v); node_id next = m_thread[f_v]; node_id alpha = find_rev_thread(q); if (f_q == f_v) { SASSERT(f_q != v && alpha != next); m_thread[f_q] = v; m_thread[alpha] = next; f_q = alpha; } else { node_id beta = m_thread[f_q]; SASSERT(f_q != v && alpha != beta); m_thread[f_q] = v; m_thread[alpha] = beta; f_q = f_v; } SASSERT(prev != q); m_thread[prev] = q; m_pred[v] = q; // Notes: f_q has to be used since m_depth hasn't been updated yet. SASSERT(is_preorder_traversal(q, f_q)); } /** \brief Check invariants of main data-structures. Spanning tree of m_graph + root is represented using: svector m_states; edge_id |-> edge_state svector m_pred; node_id |-> node svector m_depth; node_id |-> int svector m_thread; node_id |-> node Tree is determined by m_pred: - m_pred[root] == -1 - m_pred[n] = m != n for each node_id n, acyclic until reaching root. - m_depth[m_pred[n]] + 1 == m_depth[n] for each n != root m_thread is a linked list traversing all nodes. Furthermore, the nodes linked in m_thread follows a depth-first traversal order. */ template bool thread_spanning_tree::check_well_formed() { node_id root = m_pred.size()-1; // Check that m_thread traverses each node. // This gets checked using union-find as well. svector found(m_thread.size(), false); found[root] = true; for (node_id x = m_thread[root]; x != root; x = m_thread[x]) { SASSERT(x != m_thread[x]); found[x] = true; } for (unsigned i = 0; i < found.size(); ++i) { SASSERT(found[i]); } // m_pred is acyclic, and points to root. SASSERT(m_pred[root] == -1); SASSERT(m_depth[root] == 0); for (node_id i = 0; i < root; ++i) { SASSERT(m_depth[m_pred[i]] < m_depth[i]); } // m_depth[x] denotes distance from x to the root node for (node_id x = m_thread[root]; x != root; x = m_thread[x]) { SASSERT(m_depth[x] > 0); SASSERT(m_depth[x] == m_depth[m_pred[x]] + 1); } // m_thread forms a spanning tree over [0..root] // Union-find structure svector roots(m_pred.size(), -1); for (node_id x = m_thread[root]; x != root; x = m_thread[x]) { node_id y = m_pred[x]; // We are now going to check the edge between x and y SASSERT(find(roots, x) != find(roots, y)); merge(roots, x, y); } // All nodes belong to the same spanning tree for (unsigned i = 0; i < roots.size(); ++i) { SASSERT(roots[i] + roots.size() == 0 || roots[i] >= 0); } for (unsigned i = 0; i < m_tree.size(); ++i) { node_id src = m_graph.get_source(m_tree[i]); node_id tgt = m_graph.get_target(m_tree[i]); SASSERT(m_pred[src] == tgt || m_pred[tgt] == src); } return true; } static unsigned find(svector& roots, unsigned x) { unsigned old_x = x; while (roots[x] >= 0) { x = roots[x]; } SASSERT(roots[x] < 0); if (old_x != x) { roots[old_x] = x; } return x; } static void merge(svector& roots, unsigned x, unsigned y) { x = find(roots, x); y = find(roots, y); SASSERT(roots[x] < 0 && roots[y] < 0); if (x == y) { return; } if (roots[x] > roots[y]) { std::swap(x, y); } SASSERT(roots[x] <= roots[y]); roots[x] += roots[y]; roots[y] = x; } /** \brief find node_id that points to 'n' in m_thread */ template typename thread_spanning_tree::node_id thread_spanning_tree::find_rev_thread(node_id n) const { node_id ancestor = m_pred[n]; SASSERT(ancestor != -1); while (m_thread[ancestor] != n) { ancestor = m_thread[ancestor]; } return ancestor; } template void thread_spanning_tree::fix_depth(node_id start, node_id after_end) { while (start != after_end) { SASSERT(m_pred[start] != -1); m_depth[start] = m_depth[m_pred[start]]+1; start = m_thread[start]; } } template typename thread_spanning_tree::node_id thread_spanning_tree::get_final(int start) { int n = start; while (m_depth[m_thread[n]] > m_depth[start]) { n = m_thread[n]; } return n; } template bool thread_spanning_tree::is_preorder_traversal(node_id start, node_id end) { // get children of start uint_set children; children.insert(start); node_id root = m_pred.size()-1; for (int i = 0; i < root; ++i) { for (int j = 0; j < root; ++j) { if (children.contains(m_pred[j])) { children.insert(j); } } } // visit children using m_thread children.remove(start); do { start = m_thread[start]; SASSERT(children.contains(start)); children.remove(start); } while (start != end); SASSERT(children.empty()); return true; } // Basic spanning tree template basic_spanning_tree::basic_spanning_tree(graph & g) : thread_spanning_tree(g) { } template void basic_spanning_tree::initialize(svector const & tree) { m_tree_graph = alloc(graph); m_tree = tree; unsigned num_nodes = m_graph.get_num_nodes(); for (unsigned i = 0; i < num_nodes; ++i) { m_tree_graph->init_var(i); } vector const & es = m_graph.get_all_edges(); svector::const_iterator it = m_tree.begin(), end = m_tree.end(); for(; it != end; ++it) { edge const & e = es[*it]; m_tree_graph->add_edge(e.get_source(), e.get_target(), e.get_weight(), explanation()); } node_id root = num_nodes - 1; m_tree_graph->bfs_undirected(root, m_pred, m_depth); m_tree_graph->dfs_undirected(root, m_thread); } template void basic_spanning_tree::update(edge_id enter_id, edge_id leave_id) { if (m_tree_graph) dealloc(m_tree_graph); m_tree_graph = alloc(graph); unsigned num_nodes = m_graph.get_num_nodes(); for (unsigned i = 0; i < num_nodes; ++i) { m_tree_graph->init_var(i); } vector const & es = m_graph.get_all_edges(); svector::const_iterator it = m_tree.begin(), end = m_tree.end(); for(; it != end; ++it) { edge const & e = es[*it]; if (leave_id != *it) { m_tree_graph->add_edge(e.get_source(), e.get_target(), e.get_weight(), explanation()); } } edge const & e = es[enter_id]; m_tree_graph->add_edge(e.get_source(), e.get_target(), e.get_weight(), explanation()); node_id root = num_nodes - 1; m_tree_graph->bfs_undirected(root, m_pred, m_depth); m_tree_graph->dfs_undirected(root, m_thread); vector const & tree_edges = m_tree_graph->get_all_edges(); for (unsigned i = 0; i < tree_edges.size(); ++i) { edge const & e = tree_edges[i]; dl_var src = e.get_source(); dl_var tgt = e.get_target(); edge_id id; VERIFY(m_graph.get_edge_id(src, tgt, id)); SASSERT(tgt == m_pred[src] || src == m_pred[tgt]); if (tgt == m_pred[src]) { m_tree[src] = id; } else { m_tree[tgt] = id; } } node_id p = m_graph.get_source(enter_id); node_id q = m_graph.get_target(enter_id); m_root_t2 = p == m_pred[q] ? q : p; } } #endif z3-z3-4.8.7/src/smt/tactic/000077500000000000000000000000001356505360400152705ustar00rootroot00000000000000z3-z3-4.8.7/src/smt/tactic/CMakeLists.txt000066400000000000000000000004001356505360400200220ustar00rootroot00000000000000z3_add_component(smt_tactic SOURCES ctx_solver_simplify_tactic.cpp smt_tactic.cpp unit_subsumption_tactic.cpp COMPONENT_DEPENDENCIES smt TACTIC_HEADERS ctx_solver_simplify_tactic.h smt_tactic.h unit_subsumption_tactic.h ) z3-z3-4.8.7/src/smt/tactic/ctx_solver_simplify_tactic.cpp000066400000000000000000000211641356505360400234330ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: ctx_solver_simplify_tactic.cpp Abstract: Context simplifier for propagating solver assignments. Author: Nikolaj (nbjorner) 2012-3-6 Notes: --*/ #include "smt/tactic/ctx_solver_simplify_tactic.h" #include "ast/arith_decl_plugin.h" #include "smt/params/smt_params.h" #include "smt/smt_kernel.h" #include "ast/ast_pp.h" #include "ast/rewriter/mk_simplified_app.h" #include "ast/ast_util.h" class ctx_solver_simplify_tactic : public tactic { ast_manager& m; params_ref m_params; smt_params m_front_p; smt::kernel m_solver; arith_util m_arith; mk_simplified_app m_mk_app; func_decl_ref m_fn; obj_map m_fns; unsigned m_num_steps; public: ctx_solver_simplify_tactic(ast_manager & m, params_ref const & p = params_ref()): m(m), m_params(p), m_solver(m, m_front_p), m_arith(m), m_mk_app(m), m_fn(m), m_num_steps(0) { sort* i_sort = m_arith.mk_int(); m_fn = m.mk_func_decl(symbol(0xbeef101), i_sort, m.mk_bool_sort()); } tactic * translate(ast_manager & m) override { return alloc(ctx_solver_simplify_tactic, m, m_params); } ~ctx_solver_simplify_tactic() override { obj_map::iterator it = m_fns.begin(), end = m_fns.end(); for (; it != end; ++it) { m.dec_ref(it->m_value); } m_fns.reset(); } void updt_params(params_ref const & p) override { m_solver.updt_params(p); } void collect_param_descrs(param_descrs & r) override { m_solver.collect_param_descrs(r); } void collect_statistics(statistics & st) const override { st.update("solver-simplify-steps", m_num_steps); } void reset_statistics() override { m_num_steps = 0; } void operator()(goal_ref const & in, goal_ref_buffer & result) override { reduce(*(in.get())); in->inc_depth(); result.push_back(in.get()); } void cleanup() override { reset_statistics(); m_solver.reset(); } protected: void reduce(goal& g) { SASSERT(g.is_well_sorted()); expr_ref fml(m); tactic_report report("ctx-solver-simplify", g); if (g.inconsistent()) return; ptr_vector fmls; g.get_formulas(fmls); fml = mk_and(m, fmls.size(), fmls.c_ptr()); m_solver.push(); reduce(fml); m_solver.pop(1); SASSERT(m_solver.get_scope_level() == 0); TRACE("ctx_solver_simplify_tactic", for (unsigned i = 0; i < fmls.size(); ++i) { tout << mk_pp(fmls[i], m) << "\n"; } tout << "=>\n"; tout << mk_pp(fml, m) << "\n";); DEBUG_CODE( { m_solver.push(); expr_ref fml1(m); fml1 = mk_and(m, fmls.size(), fmls.c_ptr()); fml1 = m.mk_iff(fml, fml1); fml1 = m.mk_not(fml1); m_solver.assert_expr(fml1); lbool is_sat = m_solver.check(); TRACE("ctx_solver_simplify_tactic", tout << "is non-equivalence sat?: " << is_sat << "\n";); if (is_sat == l_true) { TRACE("ctx_solver_simplify_tactic", tout << "result is not equivalent to input\n"; tout << mk_pp(fml1, m) << "\n";); UNREACHABLE(); } m_solver.pop(1); }); g.reset(); g.assert_expr(fml, nullptr, nullptr); IF_VERBOSE(TACTIC_VERBOSITY_LVL, verbose_stream() << "(ctx-solver-simplify :num-steps " << m_num_steps << ")\n";); SASSERT(g.is_well_sorted()); } struct expr_pos { unsigned m_parent; unsigned m_self; unsigned m_idx; expr* m_expr; expr_pos(unsigned p, unsigned s, unsigned i, expr* e): m_parent(p), m_self(s), m_idx(i), m_expr(e) {} expr_pos(): m_parent(0), m_self(0), m_idx(0), m_expr(nullptr) {} }; void reduce(expr_ref& result){ SASSERT(m.is_bool(result)); ptr_vector names; svector todo; expr_ref_vector fresh_vars(m), trail(m); expr_ref res(m), tmp(m); obj_map cache; unsigned id = 1, child_id = 0; expr_ref n2(m), fml(m); unsigned parent_pos = 0, self_pos = 0, self_idx = 0; app * a; unsigned sz; expr_pos path_r; expr_ref_vector args(m); expr_ref n = mk_fresh(id, m.mk_bool_sort()); trail.push_back(n); fml = result.get(); tmp = m.mk_not(m.mk_iff(fml, n)); m_solver.assert_expr(tmp); todo.push_back(expr_pos(0,0,0,fml)); names.push_back(n); m_solver.push(); while (!todo.empty() && !m.canceled()) { expr_ref res(m); args.reset(); expr* e = todo.back().m_expr; self_pos = todo.back().m_self; parent_pos = todo.back().m_parent; self_idx = todo.back().m_idx; n = names.back(); if (cache.contains(e)) { goto done; } if (m.is_bool(e) && simplify_bool(n, res)) { TRACE("ctx_solver_simplify_tactic", tout << "simplified: " << mk_pp(e, m) << " |-> " << mk_pp(res, m) << "\n";); goto done; } if (!is_app(e)) { res = e; goto done; } a = to_app(e); sz = a->get_num_args(); n2 = nullptr; for (unsigned i = 0; i < sz; ++i) { expr* arg = a->get_arg(i); if (cache.find(arg, path_r)) { // // This is a single traversal version of the context // simplifier. It simplifies only the first occurrence of // a sub-term with respect to the context. // if (path_r.m_parent == self_pos && path_r.m_idx == i) { args.push_back(path_r.m_expr); } else { args.push_back(arg); } } else if (!n2) { n2 = mk_fresh(id, m.get_sort(arg)); trail.push_back(n2); todo.push_back(expr_pos(self_pos, child_id++, i, arg)); names.push_back(n2); args.push_back(n2); } else { args.push_back(arg); } } m_mk_app(a->get_decl(), args.size(), args.c_ptr(), res); trail.push_back(res); // child needs to be visited. if (n2) { m_solver.push(); tmp = m.mk_eq(res, n); m_solver.assert_expr(tmp); continue; } done: if (res) { cache.insert(e, expr_pos(parent_pos, self_pos, self_idx, res)); } todo.pop_back(); names.pop_back(); m_solver.pop(1); } if (!m.canceled()) { VERIFY(cache.find(fml, path_r)); result = path_r.m_expr; } } bool simplify_bool(expr* n, expr_ref& res) { expr_ref tmp(m); m_solver.push(); m_solver.assert_expr(n); lbool is_sat = m_solver.check(); m_solver.pop(1); if (is_sat == l_false) { res = m.mk_true(); return true; } m_solver.push(); tmp = m.mk_not(n); m_solver.assert_expr(tmp); is_sat = m_solver.check(); m_solver.pop(1); if (is_sat == l_false) { res = m.mk_false(); return true; } return false; } expr_ref mk_fresh(unsigned& id, sort* s) { func_decl* fn; if (m.is_bool(s)) { fn = m_fn; } else if (!m_fns.find(s, fn)) { fn = m.mk_func_decl(symbol(0xbeef101 + id), m_arith.mk_int(), s); m.inc_ref(fn); m_fns.insert(s, fn); } return expr_ref(m.mk_app(fn, m_arith.mk_numeral(rational(id++), true)), m); } }; tactic * mk_ctx_solver_simplify_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(ctx_solver_simplify_tactic, m, p)); } z3-z3-4.8.7/src/smt/tactic/ctx_solver_simplify_tactic.h000066400000000000000000000010661356505360400230770ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: ctx_solver_simplify_tactic.h Abstract: Context simplifier for propagating solver assignments. Author: Nikolaj (nbjorner) 2012-3-6 Notes: --*/ #ifndef CTX_SOLVER_SIMPLIFY_TACTIC_H_ #define CTX_SOLVER_SIMPLIFY_TACTIC_H_ #include "tactic/tactical.h" tactic * mk_ctx_solver_simplify_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("ctx-solver-simplify", "apply solver-based contextual simplification rules.", "mk_ctx_solver_simplify_tactic(m, p)") */ #endif z3-z3-4.8.7/src/smt/tactic/smt_tactic.cpp000066400000000000000000000271061356505360400201340ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: smt_tactic.h Abstract: smt::context as a tactic. Author: Leonardo (leonardo) 2011-10-18 Notes: --*/ #include "util/lp/lp_params.hpp" #include "ast/rewriter/rewriter_types.h" #include "ast/ast_util.h" #include "smt/smt_kernel.h" #include "smt/params/smt_params.h" #include "smt/params/smt_params_helper.hpp" #include "smt/smt_solver.h" #include "tactic/tactic.h" #include "tactic/tactical.h" #include "tactic/generic_model_converter.h" #include "solver/solver2tactic.h" #include "solver/solver.h" #include "solver/mus.h" #include "solver/parallel_tactic.h" #include "solver/parallel_params.hpp" typedef obj_map expr2expr_map; class smt_tactic : public tactic { smt_params m_params; params_ref m_params_ref; statistics m_stats; smt::kernel * m_ctx; symbol m_logic; progress_callback * m_callback; bool m_candidate_models; bool m_fail_if_inconclusive; public: smt_tactic(params_ref const & p): m_params_ref(p), m_ctx(nullptr), m_callback(nullptr) { updt_params_core(p); TRACE("smt_tactic", tout << "p: " << p << "\n";); } tactic * translate(ast_manager & m) override { return alloc(smt_tactic, m_params_ref); } ~smt_tactic() override { SASSERT(m_ctx == nullptr); } smt_params & fparams() { return m_params; } params_ref & params() { return m_params_ref; } void updt_params_core(params_ref const & p) { m_candidate_models = p.get_bool("candidate_models", false); m_fail_if_inconclusive = p.get_bool("fail_if_inconclusive", true); } void updt_params(params_ref const & p) override { TRACE("smt_tactic", tout << "updt_params: " << p << "\n";); updt_params_core(p); fparams().updt_params(p); m_params_ref.copy(p); m_logic = p.get_sym(symbol("logic"), m_logic); if (m_logic != symbol::null && m_ctx) { m_ctx->set_logic(m_logic); } SASSERT(p.get_bool("auto_config", fparams().m_auto_config) == fparams().m_auto_config); } void collect_param_descrs(param_descrs & r) override { r.insert("candidate_models", CPK_BOOL, "(default: false) create candidate models even when quantifier or theory reasoning is incomplete."); r.insert("fail_if_inconclusive", CPK_BOOL, "(default: true) fail if found unsat (sat) for under (over) approximated goal."); smt_params_helper::collect_param_descrs(r); lp_params::collect_param_descrs(r); } void collect_statistics(statistics & st) const override { if (m_ctx) m_ctx->collect_statistics(st); // ctx is still running... else st.copy(m_stats); } void cleanup() override { } void reset_statistics() override { m_stats.reset(); } void set_logic(symbol const & l) override { m_logic = l; } void set_progress_callback(progress_callback * callback) override { m_callback = callback; } struct scoped_init_ctx { smt_tactic & m_owner; smt_params m_params; // smt-setup overwrites parameters depending on the current assertions. params_ref m_params_ref; scoped_init_ctx(smt_tactic & o, ast_manager & m):m_owner(o) { m_params = o.fparams(); m_params_ref = o.params(); smt::kernel * new_ctx = alloc(smt::kernel, m, m_params, m_params_ref); TRACE("smt_tactic", tout << "logic: " << o.m_logic << "\n";); new_ctx->set_logic(o.m_logic); if (o.m_callback) { new_ctx->set_progress_callback(o.m_callback); } o.m_ctx = new_ctx; } ~scoped_init_ctx() { smt::kernel * d = m_owner.m_ctx; m_owner.m_ctx = nullptr; if (d) dealloc(d); } }; void handle_canceled(goal_ref const & in, goal_ref_buffer & result) { } void operator()(goal_ref const & in, goal_ref_buffer & result) override { try { IF_VERBOSE(10, verbose_stream() << "(smt.tactic start)\n";); SASSERT(in->is_well_sorted()); ast_manager & m = in->m(); TRACE("smt_tactic", tout << this << "\nAUTO_CONFIG: " << fparams().m_auto_config << " HIDIV0: " << fparams().m_hi_div0 << " " << " PREPROCESS: " << fparams().m_preprocess << "\n"; tout << "RELEVANCY: " << fparams().m_relevancy_lvl << "\n"; tout << "fail-if-inconclusive: " << m_fail_if_inconclusive << "\n"; tout << "params_ref: " << m_params_ref << "\n"; tout << "nnf: " << fparams().m_nnf_cnf << "\n";); TRACE("smt_tactic_params", m_params.display(tout);); TRACE("smt_tactic_detail", in->display(tout);); TRACE("smt_tactic_memory", tout << "wasted_size: " << m.get_allocator().get_wasted_size() << "\n";); scoped_init_ctx init(*this, m); SASSERT(m_ctx != 0); expr_ref_vector clauses(m); expr2expr_map bool2dep; ptr_vector assumptions; ref fmc; if (in->unsat_core_enabled()) { extract_clauses_and_dependencies(in, clauses, assumptions, bool2dep, fmc); TRACE("mus", in->display_with_dependencies(tout); tout << clauses << "\n";); if (in->proofs_enabled() && !assumptions.empty()) throw tactic_exception("smt tactic does not support simultaneous generation of proofs and unsat cores"); for (unsigned i = 0; i < clauses.size(); ++i) { m_ctx->assert_expr(clauses[i].get()); } } else if (in->proofs_enabled()) { unsigned sz = in->size(); for (unsigned i = 0; i < sz; i++) { m_ctx->assert_expr(in->form(i), in->pr(i)); } } else { unsigned sz = in->size(); for (unsigned i = 0; i < sz; i++) { m_ctx->assert_expr(in->form(i)); } } if (m_ctx->canceled()) { throw tactic_exception(Z3_CANCELED_MSG); } lbool r; try { if (assumptions.empty()) r = m_ctx->setup_and_check(); else r = m_ctx->check(assumptions.size(), assumptions.c_ptr()); } catch(...) { TRACE("smt_tactic", tout << "exception\n";); m_ctx->collect_statistics(m_stats); throw; } SASSERT(m_ctx); m_ctx->collect_statistics(m_stats); proof * pr = m_ctx->get_proof(); TRACE("smt_tactic", tout << r << " " << pr << "\n";); switch (r) { case l_true: { if (m_fail_if_inconclusive && !in->sat_preserved()) throw tactic_exception("over-approximated goal found to be sat"); // the empty assertion set is trivially satifiable. in->reset(); result.push_back(in.get()); // store the model in a no-op model converter, and filter fresh Booleans if (in->models_enabled()) { model_ref md; m_ctx->get_model(md); buffer r; m_ctx->get_relevant_labels(nullptr, r); labels_vec rv; rv.append(r.size(), r.c_ptr()); model_converter_ref mc; mc = model_and_labels2model_converter(md.get(), rv); mc = concat(fmc.get(), mc.get()); in->add(mc.get()); } return; } case l_false: { if (m_fail_if_inconclusive && !in->unsat_preserved()) { TRACE("smt_tactic", tout << "failed to show to be unsat...\n";); throw tactic_exception("under-approximated goal found to be unsat"); } // formula is unsat, reset the goal, and store false there. in->reset(); expr_dependency * lcore = nullptr; if (in->unsat_core_enabled()) { unsigned sz = m_ctx->get_unsat_core_size(); for (unsigned i = 0; i < sz; i++) { expr * b = m_ctx->get_unsat_core_expr(i); SASSERT(is_uninterp_const(b) && m.is_bool(b)); expr * d = bool2dep.find(b); lcore = m.mk_join(lcore, m.mk_leaf(d)); } } in->assert_expr(m.mk_false(), pr, lcore); result.push_back(in.get()); return; } case l_undef: if (m_ctx->canceled() && !pr) { throw tactic_exception(Z3_CANCELED_MSG); } if (m_fail_if_inconclusive && !m_candidate_models && !pr) { std::stringstream strm; strm << "smt tactic failed to show goal to be sat/unsat " << m_ctx->last_failure_as_string(); throw tactic_exception(strm.str()); } result.push_back(in.get()); if (pr) { in->reset(); in->assert_expr(m.mk_const(symbol("trail"), m.mk_bool_sort()), pr, nullptr); } if (m_candidate_models) { switch (m_ctx->last_failure()) { case smt::NUM_CONFLICTS: case smt::THEORY: case smt::QUANTIFIERS: if (in->models_enabled()) { model_ref md; m_ctx->get_model(md); buffer r; m_ctx->get_relevant_labels(nullptr, r); labels_vec rv; rv.append(r.size(), r.c_ptr()); in->add(model_and_labels2model_converter(md.get(), rv)); } return; default: break; } } if (pr) { return; } throw tactic_exception(m_ctx->last_failure_as_string()); } } catch (rewriter_exception & ex) { throw tactic_exception(ex.msg()); } } }; static tactic * mk_seq_smt_tactic(params_ref const & p) { return alloc(smt_tactic, p); } tactic * mk_parallel_smt_tactic(ast_manager& m, params_ref const& p) { return mk_parallel_tactic(mk_smt_solver(m, p, symbol::null), p); } tactic * mk_smt_tactic(ast_manager& m, params_ref const& p, symbol const& logic) { parallel_params pp(p); return pp.enable() ? mk_parallel_tactic(mk_smt_solver(m, p, logic), p) : mk_seq_smt_tactic(p); } tactic * mk_smt_tactic_using(ast_manager& m, bool auto_config, params_ref const& _p) { parallel_params pp(_p); params_ref p = _p; p.set_bool("auto_config", auto_config); return using_params(pp.enable() ? mk_parallel_smt_tactic(m, p) : mk_seq_smt_tactic(p), p); } z3-z3-4.8.7/src/smt/tactic/smt_tactic.h000066400000000000000000000016651356505360400176030ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: smt_tactic.h Abstract: smt::context as a tactic. Author: Leonardo (leonardo) 2011-10-18 Notes: --*/ #ifndef SMT_TACTIC_H_ #define SMT_TACTIC_H_ #include "util/params.h" #include "ast/ast.h" #include "util/obj_hashtable.h" #include "tactic/goal.h" class tactic; class filter_model_converter; tactic * mk_smt_tactic(ast_manager& m, params_ref const & p = params_ref(), symbol const& logic = symbol::null); // syntax sugar for using_params(mk_smt_tactic(), p) where p = (:auto_config, auto_config) tactic * mk_smt_tactic_using(ast_manager& m, bool auto_config = true, params_ref const & p = params_ref()); tactic * mk_parallel_smt_tactic(ast_manager& m, params_ref const& p); /* ADD_TACTIC("smt", "apply a SAT based SMT solver.", "mk_smt_tactic(m, p)") ADD_TACTIC("psmt", "builtin strategy for SMT tactic in parallel.", "mk_parallel_smt_tactic(m, p)") */ #endif z3-z3-4.8.7/src/smt/tactic/unit_subsumption_tactic.cpp000066400000000000000000000070771356505360400227650ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: unit_subsumption_tactic.cpp Abstract: Simplify goal using subsumption based on unit propagation. Author: Nikolaj Bjorner (nbjorner) 2012-9-6 --*/ #include "smt/tactic/unit_subsumption_tactic.h" #include "smt/smt_context.h" struct unit_subsumption_tactic : public tactic { ast_manager& m; params_ref m_params; smt_params m_fparams; smt::context m_context; expr_ref_vector m_clauses; unsigned m_clause_count; bit_vector m_is_deleted; unsigned_vector m_deleted; unit_subsumption_tactic( ast_manager& m, params_ref const& p): m(m), m_params(p), m_context(m, m_fparams, p), m_clauses(m) { } void cleanup() override {} void operator()(/* in */ goal_ref const & in, /* out */ goal_ref_buffer & result) override { reduce_core(in, result); } void updt_params(params_ref const& p) override { m_params = p; // m_context.updt_params(p); does not exist. } tactic* translate(ast_manager& m) override { return alloc(unit_subsumption_tactic, m, m_params); } void checkpoint() { if (m.canceled()) { throw tactic_exception(m.limit().get_cancel_msg()); } } void reduce_core(goal_ref const& g, goal_ref_buffer& result) { init(g); m_context.push(); assert_clauses(g); m_context.push(); // internalize assertions. prune_clauses(); goal_ref r(g); insert_result(r); r->elim_true(); result.push_back(r.get()); m_context.pop(2); TRACE("unit_subsumption_tactic", g->display(tout); r->display(tout);); } void assert_clauses(goal_ref const& g) { for (unsigned i = 0; i < g->size(); ++i) { m_context.assert_expr(m.mk_iff(new_clause(), g->form(i))); } } void prune_clauses() { for (unsigned i = 0; i < m_clause_count; ++i) { prune_clause(i); } } void prune_clause(unsigned i) { m_context.push(); for (unsigned j = 0; j < m_clause_count; ++j) { if (i == j) { m_context.assert_expr(m.mk_not(m_clauses.get(j))); } else if (!m_is_deleted.get(j)) { m_context.assert_expr(m_clauses.get(j)); } } m_context.push(); // force propagation bool is_unsat = m_context.inconsistent(); m_context.pop(2); if (is_unsat) { TRACE("unit_subsumption_tactic", tout << "Removing clause " << i << "\n";); m_is_deleted.set(i, true); m_deleted.push_back(i); } } void insert_result(goal_ref& result) { for (auto d : m_deleted) result->update(d, m.mk_true()); // TBD proof? } void init(goal_ref const& g) { m_clause_count = 0; m_is_deleted.reset(); m_is_deleted.resize(g->size()); m_deleted.reset(); } expr* new_bool(unsigned& count, expr_ref_vector& v, char const* name) { SASSERT(count <= v.size()); if (count == v.size()) { v.push_back(m.mk_fresh_const(name, m.mk_bool_sort())); } return v[count++].get(); } expr* new_clause() { return new_bool(m_clause_count, m_clauses, "#clause"); } }; tactic * mk_unit_subsumption_tactic(ast_manager & m, params_ref const & p) { return alloc(unit_subsumption_tactic, m, p); } z3-z3-4.8.7/src/smt/tactic/unit_subsumption_tactic.h000066400000000000000000000014171356505360400224220ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: unit_subsumption_tactic.h Abstract: Simplify goal using subsumption based on unit propagation. Author: Nikolaj Bjorner (nbjorner) 2012-9-6 Notes: Background: PDR generates several clauses that subsume each-other. Simplify a goal assuming it is a conjunction of clauses. Subsumed clauses are simplified by using unit-propagation It uses the smt_context for the solver. --*/ #ifndef UNIT_SUBSUMPTION_TACTIC_H_ #define UNIT_SUBSUMPTION_TACTIC_H_ #include "tactic/tactic.h" tactic * mk_unit_subsumption_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("unit-subsume-simplify", "unit subsumption simplification.", "mk_unit_subsumption_tactic(m, p)") */ #endif z3-z3-4.8.7/src/smt/theory_arith.cpp000066400000000000000000000007231356505360400172300ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_arith.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-04-22. Revision History: --*/ #include "smt/theory_arith_def.h" namespace smt { template class theory_arith; template class theory_arith; // template class theory_arith; // template class theory_arith; template class smt::theory_arith; }; z3-z3-4.8.7/src/smt/theory_arith.h000066400000000000000000001641001356505360400166750ustar00rootroot00000000000000 /*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_arith.h Abstract: Author: Leonardo de Moura (leonardo) 2008-04-21. Revision History: --*/ #ifndef THEORY_ARITH_H_ #define THEORY_ARITH_H_ #include "util/map.h" #include "util/heap.h" #include "util/nat_set.h" #include "util/inf_rational.h" #include "util/s_integer.h" #include "util/inf_s_integer.h" #include "util/obj_pair_hashtable.h" #include "util/uint_set.h" #include "ast/arith_decl_plugin.h" #include "model/numeral_factory.h" #include "smt/smt_theory.h" #include "smt/params/theory_arith_params.h" #include "smt/arith_eq_adapter.h" #include "smt/smt_context.h" #include "smt/old_interval.h" #include "smt/arith_eq_solver.h" #include "smt/theory_opt.h" #include "math/grobner/grobner.h" namespace smt { struct theory_arith_stats { unsigned m_conflicts, m_add_rows, m_pivots, m_diseq_cs, m_gomory_cuts, m_branches, m_gcd_tests, m_patches, m_patches_succ; unsigned m_assert_lower, m_assert_upper, m_assert_diseq, m_core2th_eqs, m_core2th_diseqs; unsigned m_th2core_eqs, m_th2core_diseqs, m_bound_props, m_offset_eqs, m_fixed_eqs, m_offline_eqs; unsigned m_max_min; unsigned m_gb_simplify, m_gb_superpose, m_gb_compute_basis, m_gb_num_processed; unsigned m_nl_branching, m_nl_linear, m_nl_bounds, m_nl_cross_nested; void reset() { memset(this, 0, sizeof(theory_arith_stats)); } theory_arith_stats() { reset(); } }; /** - There are 3 kinds of variables in the tableau: base, quasi-base, and non-base - Each base var and quasi-base var v owns a row R(v). - If v is a base var, then R(v) contains v and other non-base variables. - If v is a quasi-base var, then R(v) contains v and other base and non-base variables. - Each quasi-base var occurs only once in the tableau (i.e., it occurs in R(v)). - A quasi-base var does not have upper&lower bounds and distinct set. - A quasi-base var v can be transformed into a base var by eliminating the base vars v' in R(v). This can be accomplished by adding -c * R(v') where c is the coefficient of v' in R(v). - A column is used to store the occurrences of a non-base var v' in rows R(v), where v is a base variable. - An implied bound stores the linear equation that implied it. */ template class theory_arith : public theory, public theory_opt, private Ext { public: typedef typename Ext::numeral numeral; typedef typename Ext::inf_numeral inf_numeral; typedef vector numeral_vector; typedef map, default_eq > rational2var; static const int dead_row_id = -1; protected: bool proofs_enabled() const { return get_manager().proofs_enabled(); } bool coeffs_enabled() const { return proofs_enabled() || m_bound_watch != null_bool_var; } struct linear_monomial { numeral m_coeff; theory_var m_var; linear_monomial():m_var(null_theory_var) {} linear_monomial(numeral const & c, theory_var v):m_coeff(c), m_var(v) {} }; /** \brief A row_entry is: m_var*m_coeff m_col_idx points to the place in the column where the variable occurs. */ struct row_entry { numeral m_coeff; theory_var m_var; union { int m_col_idx; int m_next_free_row_entry_idx; }; row_entry():m_var(0), m_col_idx(0) {} row_entry(numeral const & c, theory_var v): m_coeff(c), m_var(v), m_col_idx(0) {} bool is_dead() const { return m_var == null_theory_var; } }; /** \brief A column entry points to the row and the row_entry within the row that has a non-zero coefficient on the variable associated with the column entry. */ struct col_entry { int m_row_id; union { int m_row_idx; int m_next_free_row_entry_idx; }; col_entry(int r, int i): m_row_id(r), m_row_idx(i) {} col_entry(): m_row_id(0), m_row_idx(0) {} bool is_dead() const { return m_row_id == dead_row_id; } }; struct column; /** \brief A row contains a base variable and set of row_entries. The base variable must occur in the set of row_entries with coefficient 1. */ struct row { vector m_entries; unsigned m_size; // the real size, m_entries contains dead row_entries. int m_base_var; int m_first_free_idx; // first available position. row(); unsigned size() const { return m_size; } unsigned num_entries() const { return m_entries.size(); } void reset(); row_entry & operator[](unsigned idx) { return m_entries[idx]; } row_entry const & operator[](unsigned idx) const { return m_entries[idx]; } typename vector::iterator begin_entries() { return m_entries.begin(); } typename vector::const_iterator begin_entries() const { return m_entries.begin(); } typename vector::iterator end_entries() { return m_entries.end(); } typename vector::const_iterator end_entries() const { return m_entries.end(); } typename vector::iterator begin() { return m_entries.begin(); } typename vector::const_iterator begin() const { return m_entries.begin(); } typename vector::iterator end() { return m_entries.end(); } typename vector::const_iterator end() const { return m_entries.end(); } row_entry & add_row_entry(int & pos_idx); void del_row_entry(unsigned idx); void compress(vector & cols); void compress_if_needed(vector & cols); void save_var_pos(svector & result_map) const; void reset_var_pos(svector & result_map) const; theory_var get_base_var() const { return m_base_var; } #ifdef Z3DEBUG bool is_coeff_of(theory_var v, numeral const & expected) const; #endif void display(std::ostream & out) const; numeral get_denominators_lcm() const; int get_idx_of(theory_var v) const; }; /** \brief A column stores in which rows a variable occurs. The column may have free/dead entries. The field m_first_free_idx is a reference to the first free/dead entry. */ struct column { svector m_entries; unsigned m_size; int m_first_free_idx; column():m_size(0), m_first_free_idx(-1) {} unsigned size() const { return m_size; } unsigned num_entries() const { return m_entries.size(); } void reset(); void compress(vector & rows); void compress_if_needed(vector & rows); void compress_singleton(vector & rows, unsigned singleton_pos); col_entry const * get_first_col_entry() const; col_entry & operator[](unsigned idx) { return m_entries[idx]; } col_entry const & operator[](unsigned idx) const { return m_entries[idx]; } typename svector::iterator begin_entries() { return m_entries.begin(); } typename svector::const_iterator begin_entries() const { return m_entries.begin(); } typename svector::iterator end_entries() { return m_entries.end(); } typename svector::const_iterator end_entries() const { return m_entries.end(); } typename svector::iterator begin() { return m_entries.begin(); } typename svector::const_iterator begin() const { return m_entries.begin(); } typename svector::iterator end() { return m_entries.end(); } typename svector::const_iterator end() const { return m_entries.end(); } col_entry & add_col_entry(int & pos_idx); void del_col_entry(unsigned idx); }; enum bound_kind { B_LOWER, B_UPPER }; friend std::ostream & operator<<(std::ostream & out, bound_kind k) { switch (k) { case B_LOWER: out << ">="; break; case B_UPPER: out << "<="; break; } return out; } typedef svector eq_vector; // keep track of coefficients used for bounds for proof generation. class antecedents_t { literal_vector m_lits; eq_vector m_eqs; vector m_lit_coeffs; vector m_eq_coeffs; vector m_params; bool m_init; bool empty() const { return m_eq_coeffs.empty() && m_lit_coeffs.empty(); } void init(); public: antecedents_t(): m_init(false) {} void reset(); literal_vector const& lits() const { return m_lits; } eq_vector const& eqs() const { return m_eqs; } void push_lit(literal l, numeral const& r, bool proofs_enabled); void push_eq(enode_pair const& p, numeral const& r, bool proofs_enabled); void append(unsigned sz, literal const* ls) { m_lits.append(sz, ls); } void append(unsigned sz, enode_pair const* ps) { m_eqs.append(sz, ps); } unsigned num_params() const { return empty()?0:m_eq_coeffs.size() + m_lit_coeffs.size() + 1; } numeral const* lit_coeffs() const { return m_lit_coeffs.c_ptr(); } numeral const* eq_coeffs() const { return m_eq_coeffs.c_ptr(); } parameter* params(char const* name); std::ostream& display(theory_arith& th, std::ostream& out) const; }; class antecedents { theory_arith& th; antecedents_t& a; public: antecedents(theory_arith& th); ~antecedents(); literal_vector const& lits() const { return a.lits(); } eq_vector const& eqs() const { return a.eqs(); } void push_lit(literal l, numeral const& r, bool e) { a.push_lit(l, r, e); } void push_eq(enode_pair const& p, numeral const& r, bool e) { a.push_eq(p, r, e); } void append(unsigned sz, literal const* ls) { a.append(sz, ls); } void append(unsigned sz, enode_pair const* ps) { a.append(sz, ps); } unsigned num_params() const { return a.num_params(); } numeral const* lit_coeffs() const { return a.lit_coeffs(); } numeral const* eq_coeffs() const { return a.eq_coeffs(); } parameter* params(char const* name) { return a.params(name); } std::ostream& display(std::ostream& out) const { return a.display(th, out); } }; class gomory_cut_justification; class bound { protected: theory_var m_var; inf_numeral m_value; unsigned m_bound_kind:1; unsigned m_atom:1; public: bound(theory_var v, inf_numeral const & val, bound_kind k, bool a): m_var(v), m_value(val), m_bound_kind(k), m_atom(a) { } virtual ~bound() {} theory_var get_var() const { return m_var; } bound_kind get_bound_kind() const { return static_cast(m_bound_kind); } bool is_atom() const { return m_atom; } inf_numeral const & get_value() const { return m_value; } virtual bool has_justification() const { return false; } virtual void push_justification(antecedents& antecedents, numeral const& coeff, bool proofs_enabled) {} virtual void display(theory_arith const& th, std::ostream& out) const; }; enum atom_kind { A_LOWER, A_UPPER }; class atom : public bound { protected: bool_var m_bvar; inf_numeral m_k; unsigned m_atom_kind:2; // atom kind unsigned m_is_true:1; // cache: true if the atom was assigned to true. public: atom(bool_var bv, theory_var v, inf_numeral const & k, atom_kind kind); atom_kind get_atom_kind() const { return static_cast(m_atom_kind); } ~atom() override {} inline inf_numeral const & get_k() const { return m_k; } bool_var get_bool_var() const { return m_bvar; } bool is_true() const { return m_is_true; } void assign_eh(bool is_true, inf_numeral const & epsilon); bool has_justification() const override { return true; } void push_justification(antecedents& a, numeral const& coeff, bool proofs_enabled) override { a.push_lit(literal(get_bool_var(), !m_is_true), coeff, proofs_enabled); } void display(theory_arith const& th, std::ostream& out) const override; }; class eq_bound : public bound { enode * m_lhs; enode * m_rhs; public: eq_bound(theory_var v, inf_numeral const & val, bound_kind k, enode * lhs, enode * rhs): bound(v, val, k, false), m_lhs(lhs), m_rhs(rhs) { SASSERT(m_lhs->get_root() == m_rhs->get_root()); } ~eq_bound() override {} bool has_justification() const override { return true; } void push_justification(antecedents& a, numeral const& coeff, bool proofs_enabled) override { SASSERT(m_lhs->get_root() == m_rhs->get_root()); a.push_eq(enode_pair(m_lhs, m_rhs), coeff, proofs_enabled); } void display(theory_arith const& th, std::ostream& out) const override; }; class derived_bound : public bound { protected: literal_vector m_lits; eq_vector m_eqs; friend class theory_arith; public: derived_bound(theory_var v, inf_numeral const & val, bound_kind k):bound(v, val, k, false) {} ~derived_bound() override {} literal_vector const& lits() const { return m_lits; } eq_vector const& eqs() const { return m_eqs; } bool has_justification() const override { return true; } void push_justification(antecedents& a, numeral const& coeff, bool proofs_enabled) override; virtual void push_lit(literal l, numeral const&) { m_lits.push_back(l); } virtual void push_eq(enode_pair const& p, numeral const&) { m_eqs.push_back(p); } void display(theory_arith const& th, std::ostream& out) const override; }; class justified_derived_bound : public derived_bound { vector m_lit_coeffs; vector m_eq_coeffs; friend class theory_arith; public: justified_derived_bound(theory_var v, inf_numeral const & val, bound_kind k):derived_bound(v, val, k) {} ~justified_derived_bound() override {} bool has_justification() const override { return true; } void push_justification(antecedents& a, numeral const& coeff, bool proofs_enabled) override; void push_lit(literal l, numeral const& coeff) override; void push_eq(enode_pair const& p, numeral const& coeff) override; }; typedef int_hashtable > literal_idx_set; typedef obj_pair_hashtable eq_set; literal_vector m_tmp_acc_lits; eq_vector m_tmp_acc_eqs; literal_idx_set m_tmp_lit_set; eq_set m_tmp_eq_set; void accumulate_justification(bound & b, derived_bound & target, numeral const& coeff, literal_idx_set & lits, eq_set & eqs); inf_numeral normalize_bound(theory_var v, inf_numeral const & k, bound_kind kind); void mk_bound_from_row(theory_var v, inf_numeral const & coeff, bound_kind k, row const & r); typedef ptr_vector atoms; // #define SPARSE_MAP #ifdef SPARSE_MAP typedef u_map bool_var2atom; #else typedef ptr_vector bool_var2atom; #endif struct theory_var_lt { bool operator()(theory_var v1, theory_var v2) const { return v1 < v2; } }; typedef heap var_heap; enum var_kind { NON_BASE, BASE, QUASI_BASE }; struct var_data { unsigned m_row_id:28; // row owned by the variable, irrelevant if kind() == NON_BASE unsigned m_kind:2; unsigned m_is_int:1; unsigned m_nl_propagated:1; var_data(bool is_int = false):m_row_id(0), m_kind(NON_BASE), m_is_int(is_int), m_nl_propagated(false) {} var_kind kind() const { return static_cast(m_kind); } }; class bound_trail { theory_var m_var; bound * m_old_bound; public: bound_trail(theory_var v, bound * b, bool is_upper): m_var(v << 1 | static_cast(is_upper)), m_old_bound(b) { } bool is_upper() const { return (m_var & 1) == 1; } theory_var get_var() const { return m_var >> 1; } bound * get_old_bound() const { return m_old_bound; } }; theory_arith_stats m_stats; theory_arith_params & m_params; arith_util m_util; arith_eq_solver m_arith_eq_solver; bool m_found_unsupported_op; bool m_found_underspecified_op; ptr_vector m_underspecified_ops; arith_eq_adapter m_arith_eq_adapter; vector m_rows; svector m_dead_rows; vector m_columns; // per var svector m_data; // per var vector m_value; // per var, the current assignment for the variable. vector m_old_value; // per var, the old assignment for the variable. ptr_vector m_bounds[2]; // per var, lower bound & upper_bound vector m_var_occs; // per var, atoms that contain a variable svector m_unassigned_atoms; // per var, the number of unassigned atoms that contain a variable. bool_var2atom m_bool_var2atom; // map bool_var -> atom svector m_var_pos; // temporary array used in add_rows atoms m_atoms; // set of theory atoms ptr_vector m_asserted_bounds; // set of asserted bounds unsigned m_asserted_qhead; ptr_vector m_new_atoms; // new bound atoms that have yet to be internalized. svector m_nl_monomials; // non linear monomials svector m_nl_propagated; // non linear monomials that became linear v_dependency_manager m_dep_manager; // for tracking bounds during non-linear reasoning vector m_row_vars; // variables in a given row. Used during internalization to detect repeated variables. unsigned m_row_vars_top; var_heap m_to_patch; // heap containing all variables v s.t. m_value[v] does not satisfy bounds of v. nat_set m_left_basis; // temporary: set of variables that already left the basis in make_feasible bool m_blands_rule; svector m_update_trail_stack; // temporary trail stack used to restore the last feasible assignment. nat_set m_in_update_trail_stack; // set of variables in m_update_trail_stack svector m_to_check; // rows that should be checked for theory propagation nat_set m_in_to_check; // set of rows in m_to_check. inf_numeral m_tmp; random_gen m_random; unsigned m_num_conflicts; unsigned m_branch_cut_counter; bool m_eager_gcd; // true if gcd should be applied at every add_row unsigned m_final_check_idx; // backtracking svector m_bound_trail; svector m_unassigned_atoms_trail; ptr_vector m_bounds_to_delete; struct scope { unsigned m_atoms_lim; unsigned m_bound_trail_lim; unsigned m_unassigned_atoms_trail_lim; unsigned m_asserted_bounds_lim; unsigned m_asserted_qhead_old; unsigned m_bounds_to_delete_lim; unsigned m_nl_monomials_lim; unsigned m_nl_propagated_lim; }; svector m_scopes; literal_vector m_tmp_literal_vector2; antecedents_t m_antecedents[3]; unsigned m_antecedents_index; struct var_value_hash; friend struct var_value_hash; struct var_value_hash { theory_arith & m_th; var_value_hash(theory_arith & th):m_th(th) {} unsigned operator()(theory_var v) const { return m_th.get_value(v).hash(); } }; struct var_value_eq; friend struct var_value_eq; struct var_value_eq { theory_arith & m_th; var_value_eq(theory_arith & th):m_th(th) {} bool operator()(theory_var v1, theory_var v2) const { return m_th.get_value(v1) == m_th.get_value(v2) && m_th.is_int_src(v1) == m_th.is_int_src(v2); } }; typedef int_hashtable var_value_table; var_value_table m_var_value_table; theory_var mk_var(enode * n) override; void found_unsupported_op(app * n); void found_underspecified_op(app * n); bool has_var(expr * v) const { return get_context().e_internalized(v) && get_context().get_enode(v)->get_th_var(get_id()) != null_theory_var; } theory_var expr2var(expr * v) const { SASSERT(get_context().e_internalized(v)); return get_context().get_enode(v)->get_th_var(get_id()); } expr * var2expr(theory_var v) const { return get_enode(v)->get_owner(); } bool reflection_enabled() const; bool reflect(app * n) const; unsigned lazy_pivoting_lvl() const { return m_params.m_arith_lazy_pivoting_lvl; } bool propagate_eqs() const { return m_params.m_arith_propagate_eqs && m_num_conflicts < m_params.m_arith_propagation_threshold; } bool propagate_diseqs() const { return false; } bool random_initial_value() const { return m_params.m_arith_random_initial_value; } int random_lower() const { return m_params.m_arith_random_lower; } int random_upper() const { return m_params.m_arith_random_upper; } unsigned blands_rule_threshold() const { return m_params.m_arith_blands_rule_threshold; } bound_prop_mode propagation_mode() const { return m_num_conflicts < m_params.m_arith_propagation_threshold ? m_params.m_arith_bound_prop : BP_NONE; } bool adaptive() const { return m_params.m_arith_adaptive; } double adaptive_assertion_threshold() const { return m_params.m_arith_adaptive_assertion_threshold; } unsigned max_lemma_size() const { return m_params.m_arith_max_lemma_size; } unsigned small_lemma_size() const { return m_params.m_arith_small_lemma_size; } bool relax_bounds() const { return m_params.m_arith_stronger_lemmas; } bool skip_big_coeffs() const { return m_params.m_arith_skip_rows_with_big_coeffs; } bool dump_lemmas() const { return m_params.m_arith_dump_lemmas; } void dump_lemmas(literal l, antecedents const& ante); void dump_lemmas(literal l, derived_bound const& ante); bool process_atoms() const; unsigned get_num_conflicts() const { return m_num_conflicts; } var_kind get_var_kind(theory_var v) const { return m_data[v].kind(); } bool is_base(theory_var v) const { return v != null_theory_var && get_var_kind(v) == BASE; } bool is_quasi_base(theory_var v) const { return v != null_theory_var && get_var_kind(v) == QUASI_BASE; } bool is_non_base(theory_var v) const { return v != null_theory_var && get_var_kind(v) == NON_BASE; } void set_var_kind(theory_var v, var_kind k) { m_data[v].m_kind = k; } unsigned get_var_row(theory_var v) const { SASSERT(!is_non_base(v)); return m_data[v].m_row_id; } void set_var_row(theory_var v, unsigned r_id) { m_data[v].m_row_id = r_id; } ptr_vector m_todo; bool is_int_expr(expr* e); bool is_int(theory_var v) const { return m_data[v].m_is_int; } bool is_int_src(theory_var v) const { return m_util.is_int(var2expr(v)); } bool is_real(theory_var v) const { return !is_int(v); } bool is_real_src(theory_var v) const { return !is_int_src(v); } bool get_implied_old_value(theory_var v, inf_numeral & r) const; inf_numeral const & get_implied_value(theory_var v) const; inf_numeral const & get_quasi_base_value(theory_var v) const { return get_implied_value(v); } inf_numeral const & get_value(theory_var v) const { return is_quasi_base(v) ? get_quasi_base_value(v) : m_value[v]; } bound * get_bound(theory_var v, bool upper) const { return m_bounds[static_cast(upper)][v]; } bound * lower(theory_var v) const { return m_bounds[0][v]; } bound * upper(theory_var v) const { return m_bounds[1][v]; } inf_numeral const & lower_bound(theory_var v) const { SASSERT(lower(v) != 0); return lower(v)->get_value(); } inf_numeral const & upper_bound(theory_var v) const { SASSERT(upper(v) != 0); return upper(v)->get_value(); } bool below_lower(theory_var v) const { bound * l = lower(v); return l != nullptr && get_value(v) < l->get_value(); } bool above_upper(theory_var v) const { bound * u = upper(v); return u != nullptr && get_value(v) > u->get_value(); } bool below_upper(theory_var v) const { bound * u = upper(v); return u == nullptr || get_value(v) < u->get_value(); } bool above_lower(theory_var v) const { bound * l = lower(v); return l == nullptr || get_value(v) > l->get_value(); } bool at_bound(theory_var v) const; bool at_lower(theory_var v) const { bound * l = lower(v); return l != nullptr && get_value(v) == l->get_value(); } bool at_upper(theory_var v) const { bound * u = upper(v); return u != nullptr && get_value(v) == u->get_value(); } bool is_free(theory_var v) const { return lower(v) == nullptr && upper(v) == nullptr; } bool is_non_free(theory_var v) const { return lower(v) != nullptr || upper(v) != nullptr; } bool is_bounded(theory_var v) const { return lower(v) != nullptr && upper(v) != nullptr; } bool is_free(expr * n) const { SASSERT(get_context().e_internalized(n) && get_context().get_enode(n)->get_th_var(get_id()) != null_theory_var); return is_free(get_context().get_enode(n)->get_th_var(get_id())); } bool is_fixed(theory_var v) const; void set_bound_core(theory_var v, bound * new_bound, bool upper) { m_bounds[static_cast(upper)][v] = new_bound; } void restore_bound(theory_var v, bound * new_bound, bool upper) { set_bound_core(v, new_bound, upper); } void restore_nl_propagated_flag(unsigned old_trail_size); void set_bound(bound * new_bound, bool upper); inf_numeral const & get_epsilon(theory_var v) const { return is_real(v) ? this->m_real_epsilon : this->m_int_epsilon; } bool enable_cgc_for(app * n) const; enode * mk_enode(app * n); void mk_enode_if_reflect(app * n); template void add_row_entry(unsigned r_id, numeral const & coeff, theory_var v); uint_set& row_vars(); class scoped_row_vars; void internalize_internal_monomial(app * m, unsigned r_id); theory_var internalize_add(app * n); theory_var internalize_mul_core(app * m); theory_var internalize_mul(app * m); theory_var internalize_div(app * n); theory_var mk_binary_op(app * n); theory_var internalize_idiv(app * n); theory_var internalize_mod(app * n); theory_var internalize_rem(app * n); theory_var internalize_to_real(app * n); theory_var internalize_to_int(app * n); void internalize_is_int(app * n); theory_var internalize_numeral(app * n); theory_var internalize_numeral(app * n, numeral const& val); theory_var internalize_term_core(app * n); void mk_axiom(expr * n1, expr * n2, bool simplify_conseq = true); void mk_idiv_mod_axioms(expr * dividend, expr * divisor); void mk_div_axiom(expr * dividend, expr * divisor); void mk_rem_axiom(expr * dividend, expr * divisor); void mk_to_int_axiom(app* to_int); void mk_is_int_axiom(app* is_int); unsigned mk_row(); void init_row(unsigned r_id); void collect_vars(unsigned r_id, var_kind k, buffer & result); void normalize_quasi_base_row(unsigned r_id); void quasi_base_row2base_row(unsigned r_id); void normalize_base_row(unsigned r_id); void mk_clause(literal l1, literal l2, unsigned num_params, parameter * params); void mk_clause(literal l1, literal l2, literal l3, unsigned num_params, parameter * params); void mk_bound_axioms(atom * a); void mk_bound_axiom(atom* a1, atom* a2); void flush_bound_axioms(); typename atoms::iterator next_sup(atom* a1, atom_kind kind, typename atoms::iterator it, typename atoms::iterator end, bool& found_compatible); typename atoms::iterator next_inf(atom* a1, atom_kind kind, typename atoms::iterator it, typename atoms::iterator end, bool& found_compatible); typename atoms::iterator first(atom_kind kind, typename atoms::iterator it, typename atoms::iterator end); struct compare_atoms { bool operator()(atom* a1, atom* a2) const { return a1->get_k() < a2->get_k(); } }; bool default_internalizer() const override { return false; } bool internalize_atom(app * n, bool gate_ctx) override; bool internalize_term(app * term) override; void internalize_eq_eh(app * atom, bool_var v) override; void apply_sort_cnstr(enode * n, sort * s) override; void assign_eh(bool_var v, bool is_true) override; void new_eq_eh(theory_var v1, theory_var v2) override; bool use_diseqs() const override; void new_diseq_eh(theory_var v1, theory_var v2) override; void push_scope_eh() override; void pop_scope_eh(unsigned num_scopes) override; void relevant_eh(app * n) override; void restart_eh() override; void init_search_eh() override; /** \brief True if the assignment may be changed during final check. assume_eqs, check_int_feasibility, process_non_linear may change the current assignment to satisfy their respective constraints. However, when they do that the may create inconsistencies in the other modules. I use m_liberal_final_check to avoid infinite loops where the modules keep changing the assignment and no progress is made. If m_liberal_final_check is set to false, these modules will avoid mutating the assignment to satisfy constraints. See also m_changed_assignment flag. */ bool m_liberal_final_check; final_check_status final_check_core(); final_check_status final_check_eh() override; bool can_propagate() override; void propagate() override; bool propagate_core(); void failed(); void flush_eh() override; void reset_eh() override; // ----------------------------------- // // bool_var -> atom mapping // // ----------------------------------- void insert_bv2a(bool_var bv, atom * a) { #ifdef SPARSE_MAP m_bool_var2atom.insert(bv, a); #else m_bool_var2atom.setx(bv, a, 0); #endif } void erase_bv2a(bool_var bv) { #ifdef SPARSE_MAP m_bool_var2atom.erase(bv); #else m_bool_var2atom[bv] = 0; #endif } atom * get_bv2a(bool_var bv) { #ifdef SPARSE_MAP atom * a; m_bool_var2atom.find(bv, a); return a; #else return m_bool_var2atom.get(bv, 0); #endif } // ----------------------------------- // // Add Row // // ----------------------------------- void add_row(unsigned r1, const numeral & coeff, unsigned r2, bool apply_gcd_test); void add_rows(unsigned r1, unsigned sz, linear_monomial * a_xs); // ----------------------------------- // // Assignment management // // ----------------------------------- bool m_changed_assignment; //!< auxiliary variable set to true when the assignment is changed. void save_value(theory_var v); void discard_update_trail(); void restore_assignment(); void update_value_core(theory_var v, inf_numeral const & delta); void update_value(theory_var v, inf_numeral const & delta); void set_value(theory_var v, const inf_numeral & new_val) { update_value(v, new_val - m_value[v]); } // ----------------------------------- // // Pivoting // // ----------------------------------- template void pivot(theory_var x_i, theory_var x_j, numeral const & a_ij, bool apply_gcd_test); template void eliminate(theory_var x_i, bool apply_gcd_test); void update_and_pivot(theory_var x_i, theory_var x_j, numeral const & a_ij, inf_numeral const & x_i_new_val); int get_num_non_free_dep_vars(theory_var v, int best_so_far); theory_var select_blands_pivot_core(theory_var x_i, bool is_below, numeral & out_a_ij); template theory_var select_pivot_core(theory_var x_i, numeral & out_a_ij); theory_var select_pivot(theory_var x_i, bool is_below, numeral & out_a_ij); // ----------------------------------- // // Make feasible // // ----------------------------------- bool make_var_feasible(theory_var x_i); theory_var select_var_to_fix(); theory_var select_lg_error_var(bool least); theory_var select_greatest_error_var() { return select_lg_error_var(false); } theory_var select_least_error_var() { return select_lg_error_var(true); } theory_var select_smallest_var(); bool make_feasible(); void sign_row_conflict(theory_var x_i, bool is_below); // ----------------------------------- // // Assert bound // // ----------------------------------- bool assert_lower(bound * b); bool assert_upper(bound * b); bool assert_bound(bound * b); void sign_bound_conflict(bound * b1, bound * b2); // ----------------------------------- // // Bound propagation // // ----------------------------------- void mark_row_for_bound_prop(unsigned r1); void mark_rows_for_bound_prop(theory_var v); void is_row_useful_for_bound_prop(row const & r, int & lower_idx, int & upper_idx) const; void imply_bound_for_monomial(row const & r, int idx, bool lower); void imply_bound_for_all_monomials(row const & r, bool lower); void explain_bound(row const & r, int idx, bool lower, inf_numeral & delta, antecedents & antecedents); void mk_implied_bound(row const & r, unsigned idx, bool lower, theory_var v, bound_kind kind, inf_numeral const & k); void assign_bound_literal(literal l, row const & r, unsigned idx, bool lower, inf_numeral & delta); void propagate_bounds(); // ----------------------------------- // // Freedom intervals // // ----------------------------------- bool get_freedom_interval(theory_var x_j, bool & inf_l, inf_numeral & l, bool & inf_u, inf_numeral & u, numeral & m); // ----------------------------------- // // Implied eqs // // ----------------------------------- bool try_to_imply_eq(theory_var v1, theory_var v2); // ----------------------------------- // // Assume eqs with randomization // // ----------------------------------- typedef int_hashtable > var_set; var_set m_tmp_var_set; var_set m_tmp_var_set2; svector > m_assume_eq_candidates; unsigned m_assume_eq_head; bool random_update(theory_var v); void mutate_assignment(); bool assume_eqs_core(); bool delayed_assume_eqs(); // ----------------------------------- // // Integrality // // ----------------------------------- void move_non_base_vars_to_bounds(); bool has_infeasible_int_var(); theory_var find_infeasible_int_base_var(); theory_var find_bounded_infeasible_int_base_var(); void branch_infeasible_int_var(theory_var v); bool branch_infeasible_int_equality(); bool constrain_free_vars(row const & r); bool is_gomory_cut_target(row const & r); bool mk_gomory_cut(row const & r); bool gcd_test(row const & r); bool ext_gcd_test(row const & r, numeral const & least_coeff, numeral const & lcm_den, numeral const & consts); bool gcd_test(); void mk_polynomial_ge(unsigned num_args, row_entry const * args, rational const& k, expr_ref & result); bool max_min_infeasible_int_vars(); void patch_int_infeasible_vars(); void fix_non_base_vars(); unsynch_mpq_manager m_es_num_manager; // manager for euclidean solver. struct euclidean_solver_bridge; bool apply_euclidean_solver(); final_check_status check_int_feasibility(); // ----------------------------------- // // Eq propagation // // ----------------------------------- typedef std::pair value_sort_pair; typedef pair_hash, bool_hash> value_sort_pair_hash; typedef map > value2var; value2var m_fixed_var_table; typedef std::pair var_offset; typedef pair_hash > var_offset_hash; typedef map > var_offset2row_id; var_offset2row_id m_var_offset2row_id; bool is_equal(theory_var x, theory_var y) const { return get_enode(x)->get_root() == get_enode(y)->get_root(); } void fixed_var_eh(theory_var v); bool is_offset_row(row const & r, theory_var & x, theory_var & y, numeral & k) const; void propagate_cheap_eq(unsigned rid); void propagate_eq_to_core(theory_var x, theory_var y, antecedents& antecedents); bool is_shared(theory_var v) const override; // ----------------------------------- // // Justification // // ----------------------------------- void set_conflict(unsigned num_literals, literal const * lits, unsigned num_eqs, enode_pair const * eqs, antecedents& antecedents, char const* proof_rule); void set_conflict(antecedents const& ante, antecedents& bounds, char const* proof_rule); void set_conflict(derived_bound const& ante, antecedents& bounds, char const* proof_rule); void collect_fixed_var_justifications(row const & r, antecedents& antecedents) const; // ----------------------------------- // // Backtracking // // ----------------------------------- void push_bound_trail(theory_var v, bound * old_bound, bool is_upper) { m_bound_trail.push_back(bound_trail(v, old_bound, is_upper)); } void push_dec_unassigned_atoms_trail(theory_var v) { m_unassigned_atoms_trail.push_back(v); } void restore_bounds(unsigned old_trail_size); void restore_unassigned_atoms(unsigned old_trail_size); void del_atoms(unsigned old_size); void del_bounds(unsigned old_size); void del_vars(unsigned old_num_vars); void del_row(unsigned r_id); // ----------------------------------- // // Auxiliary methods // // ----------------------------------- col_entry const * get_a_base_row_that_contains(theory_var v); bool all_coeff_int(row const & r) const; col_entry const * get_row_for_eliminating(theory_var v) const; void move_unconstrained_to_base(); void elim_quasi_base_rows(); void remove_fixed_vars_from_base(); void try_to_minimize_rational_coeffs(); /** \brief See comment in theory::mk_eq_atom */ app * mk_eq_atom(expr * lhs, expr * rhs) override { return m_util.mk_eq(lhs, rhs); } // ----------------------------------- // // Maximization/Minimization // // ----------------------------------- row m_tmp_row; void add_tmp_row(row & r1, numeral const & coeff, row const & r2); bool is_safe_to_leave(theory_var x, bool inc, bool& has_int, bool& is_shared); template void add_tmp_row_entry(row & r, numeral const & coeff, theory_var v); enum max_min_t { UNBOUNDED, AT_BOUND, OPTIMIZED, BEST_EFFORT}; max_min_t max_min(theory_var v, bool max, bool maintain_integrality, bool& has_shared); bool has_interface_equality(theory_var v); bool max_min(svector const & vars); max_min_t max_min(row& r, bool max, bool maintain_integrality, bool& has_shared); bool unbounded_gain(inf_numeral const & max_gain) const; bool safe_gain(inf_numeral const& min_gain, inf_numeral const & max_gain) const; void normalize_gain(numeral const& divisor, inf_numeral & max_gain) const; void init_gains(theory_var x, bool inc, inf_numeral& min_gain, inf_numeral& max_gain); bool update_gains(bool inc, theory_var x_i, numeral const& a_ij, inf_numeral& min_gain, inf_numeral& max_gain); bool move_to_bound(theory_var x_i, bool inc, unsigned& best_efforts, bool& has_shared); bool pick_var_to_leave( theory_var x_j, bool inc, numeral & a_ij, inf_numeral& min_gain, inf_numeral& max_gain, bool& shared, theory_var& x_i); // ----------------------------------- // // Non linear // // ----------------------------------- typedef int_hashtable > row_set; bool m_model_depends_on_computed_epsilon; unsigned m_nl_rounds; bool m_nl_gb_exhausted; unsigned m_nl_strategy_idx; // for fairness expr_ref_vector m_nl_new_exprs; typedef obj_map var2num_occs; var2num_occs m_var2num_occs; typedef std::pair var_num_occs; struct var_num_occs_lt { bool operator()(var_num_occs const & vn1, var_num_occs const & vn2) const { return vn1.second > vn2.second; } }; /** \brief A monomial is 'pure' if does not have a numeric coefficient. */ bool is_pure_monomial(expr * m) const { return m_util.is_mul(m) && !m_util.is_numeral(to_app(m)->get_arg(0)); } bool is_pure_monomial(theory_var v) const { return is_pure_monomial(get_enode(v)->get_owner()); } void mark_var(theory_var v, svector & vars, var_set & already_found); void mark_dependents(theory_var v, svector & vars, var_set & already_found, row_set & already_visited_rows); void get_non_linear_cluster(svector & vars); std::pair analyze_monomial(expr * m) const; expr * get_monomial_body(expr * m) const; rational get_monomial_coeff(expr * m) const; unsigned get_num_vars_in_monomial(expr * m) const; typedef std::pair var_power_pair; var_power_pair get_var_and_degree(expr * m, unsigned i) const; void display_monomial(std::ostream & out, expr * m) const; bool propagate_nl_upward(expr * m); bool propagate_nl_downward(expr * m, unsigned i); interval mk_interval_for(theory_var v); interval mk_interval_for(expr * n); void mul_bound_of(expr * var, unsigned power, interval & target); interval evaluate_as_interval(expr * n); void dependency2new_bound(v_dependency * dep, derived_bound& new_bound); void mk_derived_nl_bound(theory_var v, inf_numeral const & coeff, bound_kind k, v_dependency * dep); bool update_bounds_using_interval(theory_var v, interval const & i); bool update_bounds_using_interval(expr * n, interval const & i); bool propagate_nl_bounds(expr * m); bool propagate_nl_bound(expr * m, int i); bool propagate_nl_bounds(); bool is_problematic_non_linear_row(row const & r); bool is_mixed_real_integer(row const & r) const; bool is_integer(row const & r) const; typedef std::pair coeff_expr; void get_polynomial_info(sbuffer const & p, sbuffer & vars); expr * p2expr(sbuffer & p); expr * power(expr * var, unsigned power); expr * mk_nary_mul(unsigned sz, expr * const * args, bool is_int); expr * mk_nary_add(unsigned sz, expr * const * args, bool is_int); expr * mk_nary_add(unsigned sz, expr * const * args); void display_nested_form(std::ostream & out, expr * p); unsigned get_degree_of(expr * m, expr * var); unsigned get_min_degree(sbuffer & p, expr * var); expr * factor(expr * m, expr * var, unsigned d); bool in_monovariate_monomials(sbuffer & p, expr * var, unsigned & i1, rational & c1, unsigned & n1, unsigned & i2, rational & c2, unsigned & n2); expr * horner(sbuffer & p, expr * var); expr * cross_nested(sbuffer & p, expr * var); bool is_cross_nested_consistent(sbuffer & p); bool is_cross_nested_consistent(row const & r); bool is_cross_nested_consistent(svector const & nl_cluster); rational get_value(theory_var v, bool & computed_epsilon); bool check_monomial_assignment(theory_var v, bool & computed_epsilon); bool check_monomial_assignments(); theory_var find_nl_var_for_branching(); bool branch_nl_int_var(theory_var v); bool is_monomial_linear(expr * m) const; numeral get_monomial_fixed_var_product(expr * m) const; expr * get_monomial_non_fixed_var(expr * m) const; bool propagate_linear_monomial(theory_var v); bool propagate_linear_monomials(); grobner::monomial * mk_gb_monomial(rational const & coeff, expr * m, grobner & gb, v_dependency * & dep, var_set & already_found); void add_monomial_def_to_gb(theory_var v, grobner & gb); void add_row_to_gb(row const & r, grobner & gb); void init_grobner_var_order(svector const & nl_cluster, grobner & gb); void init_grobner(svector const & nl_cluster, grobner & gb); interval mk_interval_for(grobner::monomial const * m); void set_conflict(v_dependency * d); bool is_inconsistent(interval const & I, unsigned num_monomials, grobner::monomial * const * monomials, v_dependency * dep); bool is_inconsistent(grobner::equation const * eq, grobner & gb); bool is_inconsistent2(grobner::equation const * eq, grobner & gb); expr * monomial2expr(grobner::monomial const * m, bool is_int); bool internalize_gb_eq(grobner::equation const * eq); enum gb_result { GB_PROGRESS, GB_NEW_EQ, GB_FAIL }; gb_result compute_grobner(svector const & nl_cluster); bool max_min_nl_vars(); final_check_status process_non_linear(); // ----------------------------------- // // Constructor // // ----------------------------------- public: theory_arith(ast_manager & m, theory_arith_params & params); ~theory_arith() override; theory * mk_fresh(context * new_ctx) override; void setup() override; char const * get_name() const override { return "arithmetic"; } // ----------------------------------- // // Model generation // // ----------------------------------- arith_factory * m_factory; numeral m_epsilon; void update_epsilon(const inf_numeral & l, const inf_numeral & u); void compute_epsilon(); void refine_epsilon(); void init_model(model_generator & m) override; model_value_proc * mk_value(enode * n, model_generator & mg) override; // ----------------------------------- // // Model checker // // ----------------------------------- bool get_value(enode * n, expr_ref & r) override; bool include_func_interp(func_decl* f) override; bool get_lower(enode* n, expr_ref& r); bool get_upper(enode* n, expr_ref& r); bool get_lower(enode* n, rational& r, bool &is_strict); bool get_upper(enode* n, rational& r, bool &is_strict); bool to_expr(inf_numeral const& val, bool is_int, expr_ref& r); // ----------------------------------- // // Optimization // // ----------------------------------- expr_ref mk_ge(generic_model_converter& fm, theory_var v, inf_numeral const& val); inf_eps_rational maximize(theory_var v, expr_ref& blocker, bool& has_shared) override; inf_eps_rational value(theory_var v) override; theory_var add_objective(app* term) override; void enable_record_conflict(expr* bound); void record_conflict(unsigned num_lits, literal const * lits, unsigned num_eqs, enode_pair const * eqs, unsigned num_params, parameter* params); inf_eps_rational conflict_minimize(); private: virtual expr_ref mk_gt(theory_var v); bool_var m_bound_watch; inf_eps_rational m_upper_bound; bool get_theory_vars(expr * n, uint_set & vars); public: // ----------------------------------- // // Pretty Printing // // ----------------------------------- public: void collect_statistics(::statistics & st) const override; void display(std::ostream & out) const override; protected: void display_row(std::ostream & out, unsigned r_id, bool compact = true) const; void display_row(std::ostream & out, row const & r, bool compact = true) const; void display_rows(std::ostream & out, bool compact = true) const; void display_row_info(std::ostream & out, unsigned r_id) const; void display_row_info(std::ostream & out, row const & r) const; bool is_one_minus_one_row(row const & r) const; void display_row_shape(std::ostream & out, row const & r) const; void display_rows_shape(std::ostream & out) const; void display_rows_stats(std::ostream & out) const; void display_rows_bignums(std::ostream & out) const; void display_simplified_row(std::ostream & out, row const & r) const; void display_var(std::ostream & out, theory_var v) const; void display_vars(std::ostream & out) const; void display_bound(std::ostream & out, bound * b, unsigned indent = 0) const; void display_atoms(std::ostream & out) const; void display_asserted_atoms(std::ostream & out) const; void display_atom(std::ostream & out, atom * a, bool show_sign) const; void display_bounds_in_smtlib(std::ostream & out) const; void display_bounds_in_smtlib() const; void display_nl_monomials(std::ostream & out) const; void display_coeff_exprs(std::ostream & out, sbuffer const & p) const; void display_interval(std::ostream& out, interval const& i); void display_deps(std::ostream& out, v_dependency* dep); protected: // ----------------------------------- // // Debugging // // ----------------------------------- #ifdef Z3DEBUG bool check_vector_sizes() const; bool check_null_var_pos() const; bool has_var_kind(unsigned r_id, var_kind k) const; bool wf_row(unsigned r_id) const; bool wf_rows() const; bool wf_column(theory_var v) const; bool wf_columns() const; bool valid_assignment() const; bool valid_row_assignment() const; bool valid_row_assignment(row const & r) const; bool satisfy_bounds() const; bool satisfy_integrality() const; #endif }; class mi_ext { public: typedef rational numeral; typedef inf_rational inf_numeral; inf_numeral m_int_epsilon; inf_numeral m_real_epsilon; numeral fractional_part(inf_numeral const& n) { SASSERT(n.is_rational()); return n.get_rational() - floor(n); } static numeral fractional_part(numeral const & n) { return n - floor(n); } static inf_numeral mk_inf_numeral(numeral const & n, numeral const & r) { return inf_numeral(n, r); } static bool is_infinite(inf_numeral const& ) { return false; } mi_ext() : m_int_epsilon(rational(1)), m_real_epsilon(rational(0), true) {} }; class i_ext { public: typedef rational numeral; typedef rational inf_numeral; numeral m_int_epsilon; numeral m_real_epsilon; static numeral fractional_part(numeral const & n) { return n - floor(n); } static inf_numeral mk_inf_numeral(numeral const& n, numeral const& i) { UNREACHABLE(); return inf_numeral(n); } static bool is_infinite(inf_numeral const& ) { return false; } i_ext() : m_int_epsilon(1), m_real_epsilon(1) {} }; class si_ext { public: typedef s_integer numeral; typedef s_integer inf_numeral; numeral m_int_epsilon; numeral m_real_epsilon; static numeral fractional_part(inf_numeral const & n) { return n - floor(n); } static inf_numeral mk_inf_numeral(numeral const& n, numeral const& i) { UNREACHABLE(); return inf_numeral(n); } static bool is_infinite(inf_numeral const& ) { return false; } si_ext(): m_int_epsilon(s_integer(1)), m_real_epsilon(s_integer(1)) {} }; class smi_ext { public: typedef s_integer numeral; typedef inf_s_integer inf_numeral; inf_numeral m_int_epsilon; inf_numeral m_real_epsilon; static numeral fractional_part(const numeral & n) { UNREACHABLE(); return numeral(0); } static numeral fractional_part(const inf_numeral & n) { UNREACHABLE(); return numeral(0); } static inf_numeral mk_inf_numeral(numeral const& n, numeral const& i) { return inf_numeral(n, i); } static bool is_infinite(inf_numeral const& ) { return false; } smi_ext() : m_int_epsilon(s_integer(1)), m_real_epsilon(s_integer(0), true) {} }; class inf_ext { public: typedef rational numeral; typedef inf_eps_rational inf_numeral; inf_numeral m_int_epsilon; inf_numeral m_real_epsilon; numeral fractional_part(inf_numeral const& n) { SASSERT(n.is_rational()); return n.get_rational() - floor(n); } static numeral fractional_part(numeral const & n) { return n - floor(n); } static inf_numeral mk_inf_numeral(numeral const & n, numeral const & r) { return inf_numeral(inf_rational(n, r)); } static bool is_infinite(inf_numeral const& n) { return !n.get_infinity().is_zero(); } inf_ext() : m_int_epsilon(inf_rational(rational(1))), m_real_epsilon(inf_rational(rational(0), true)) {} }; typedef theory_arith theory_mi_arith; typedef theory_arith theory_i_arith; typedef smt::theory_arith theory_inf_arith; // typedef theory_arith theory_si_arith; // typedef theory_arith theory_smi_arith; }; #endif /* THEORY_ARITH_H_ */ z3-z3-4.8.7/src/smt/theory_arith_aux.h000066400000000000000000002512551356505360400175620ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_arith_aux.h Abstract: Author: Leonardo de Moura (leonardo) 2008-04-29. Revision History: --*/ #ifndef THEORY_ARITH_AUX_H_ #define THEORY_ARITH_AUX_H_ #include "util/inf_eps_rational.h" #include "smt/theory_arith.h" #include "smt/smt_farkas_util.h" #include "ast/rewriter/th_rewriter.h" #include "tactic/generic_model_converter.h" namespace smt { // ----------------------------------- // // Rows // // ----------------------------------- template theory_arith::row::row(): m_size(0), m_base_var(null_theory_var), m_first_free_idx(-1) { } template void theory_arith::row::reset() { m_entries.reset(); m_size = 0; m_base_var = -1; m_first_free_idx = -1; } /** \brief Add a new row_entry. The result is a reference to the new row_entry. The position of the new row_entry in the row is stored in pos_idx. */ template typename theory_arith::row_entry & theory_arith::row::add_row_entry(int & pos_idx) { m_size++; if (m_first_free_idx == -1) { pos_idx = m_entries.size(); m_entries.push_back(row_entry()); return m_entries.back(); } else { pos_idx = m_first_free_idx; row_entry & result = m_entries[pos_idx]; SASSERT(result.is_dead()); m_first_free_idx = result.m_next_free_row_entry_idx; return result; } } /** \brief Delete row_entry at position idx. */ template void theory_arith::row::del_row_entry(unsigned idx) { row_entry & t = m_entries[idx]; SASSERT(!t.is_dead()); t.m_next_free_row_entry_idx = m_first_free_idx; t.m_var = null_theory_var; m_size--; SASSERT(t.is_dead()); } /** \brief Remove holes (i.e., dead entries) from the row. */ template void theory_arith::row::compress(vector & cols) { unsigned i = 0; unsigned j = 0; unsigned sz = m_entries.size(); for (; i < sz; i++) { row_entry & t1 = m_entries[i]; if (!t1.is_dead()) { if (i != j) { row_entry & t2 = m_entries[j]; t2.m_coeff.swap(t1.m_coeff); t2.m_var = t1.m_var; t2.m_col_idx = t1.m_col_idx; SASSERT(!t2.is_dead()); column & col = cols[t2.m_var]; col[t2.m_col_idx].m_row_idx = j; } j++; } } SASSERT(j == m_size); m_entries.shrink(m_size); m_first_free_idx = -1; } /** \brief Invoke compress if the row contains too many holes (i.e., dead entries). */ template inline void theory_arith::row::compress_if_needed(vector & cols) { if (size() * 2 < num_entries()) { compress(cols); } } /** \brief Fill the map var -> pos/idx */ template inline void theory_arith::row::save_var_pos(svector & result_map) const { typename vector::const_iterator it = m_entries.begin(); typename vector::const_iterator end = m_entries.end(); unsigned idx = 0; for (; it != end; ++it, ++idx) { if (!it->is_dead()) { result_map[it->m_var] = idx; } } } /** \brief Reset the map var -> pos/idx. That is for all variables v in the row, set result[v] = -1 This method can be viewed as the "inverse" of save_var_pos. */ template inline void theory_arith::row::reset_var_pos(svector & result_map) const { typename vector::const_iterator it = m_entries.begin(); typename vector::const_iterator end = m_entries.end(); for (; it != end; ++it) { if (!it->is_dead()) { result_map[it->m_var] = -1; } } }; #ifdef Z3DEBUG /** \brief Return true if the coefficient of v in the row is equals to 'expected'. */ template bool theory_arith::row::is_coeff_of(theory_var v, numeral const & expected) const { typename vector::const_iterator it = m_entries.begin(); typename vector::const_iterator end = m_entries.end(); for (; it != end; ++it) { if (!it->is_dead() && it->m_var == v) { return it->m_coeff == expected; } } return false; } #endif template void theory_arith::row::display(std::ostream & out) const { out << "v" << m_base_var << ", "; typename vector::const_iterator it = m_entries.begin(); typename vector::const_iterator end = m_entries.end(); for (; it != end; ++it) { if (!it->is_dead()) { out << it->m_coeff << "*v" << it->m_var << " "; } } out << "\n"; } template typename theory_arith::numeral theory_arith::row::get_denominators_lcm() const { numeral r(1); TRACE("lcm_bug", tout << "starting get_denominators_lcm...\n";); typename vector::const_iterator it = m_entries.begin(); typename vector::const_iterator end = m_entries.end(); for (; it != end; ++it) { if (!it->is_dead()) { r = lcm(r, denominator(it->m_coeff)); TRACE("lcm_bug", tout << "it->m_coeff: " << it->m_coeff << ", denominator(it->m_coeff): " << denominator(it->m_coeff) << ", r: " << r << "\n";); } } return r; } template int theory_arith::row::get_idx_of(theory_var v) const { typename vector::const_iterator it = m_entries.begin(); typename vector::const_iterator end = m_entries.end(); for (unsigned idx = 0; it != end; ++it, ++idx) { if (!it->is_dead() && it->m_var == v) return idx; } return -1; } // ----------------------------------- // // Columns // // ----------------------------------- template void theory_arith::column::reset() { m_entries.reset(); m_size = 0; m_first_free_idx = -1; } /** \brief Remove holes (i.e., dead entries) from the column. */ template void theory_arith::column::compress(vector & rows) { unsigned i = 0; unsigned j = 0; unsigned sz = m_entries.size(); for (; i < sz; i++) { col_entry & e1 = m_entries[i]; if (!e1.is_dead()) { if (i != j) { m_entries[j] = e1; row & r = rows[e1.m_row_id]; r[e1.m_row_idx].m_col_idx = j; } j++; } } SASSERT(j == m_size); m_entries.shrink(m_size); m_first_free_idx = -1; } /** \brief Invoke compress if the column contains too many holes (i.e., dead entries). */ template inline void theory_arith::column::compress_if_needed(vector & rows) { if (size() * 2 < num_entries()) { compress(rows); } } /** \brief Special version of compress, that is used when the column contain only one entry located at position singleton_pos. */ template void theory_arith::column::compress_singleton(vector & rows, unsigned singleton_pos) { SASSERT(m_size == 1); if (singleton_pos != 0) { col_entry & s = m_entries[singleton_pos]; m_entries[0] = s; row & r = rows[s.m_row_id]; r[s.m_row_idx].m_col_idx = 0; } m_first_free_idx = -1; m_entries.shrink(1); } template const typename theory_arith::col_entry * theory_arith::column::get_first_col_entry() const { typename svector::const_iterator it = m_entries.begin(); typename svector::const_iterator end = m_entries.end(); for (; it != end; ++it) { if (!it->is_dead()) { return it; } } return nullptr; } template typename theory_arith::col_entry & theory_arith::column::add_col_entry(int & pos_idx) { m_size++; if (m_first_free_idx == -1) { pos_idx = m_entries.size(); m_entries.push_back(col_entry()); return m_entries.back(); } else { pos_idx = m_first_free_idx; col_entry & result = m_entries[pos_idx]; SASSERT(result.is_dead()); m_first_free_idx = result.m_next_free_row_entry_idx; return result; } } template void theory_arith::column::del_col_entry(unsigned idx) { col_entry & c = m_entries[idx]; SASSERT(!c.is_dead()); c.m_row_id = dead_row_id; c.m_next_free_row_entry_idx = m_first_free_idx; m_first_free_idx = idx; m_size--; } // ----------------------------------- // // Antecedents // // ----------------------------------- template void theory_arith::antecedents_t::init() { if (!m_init && !empty()) { m_params.push_back(parameter(symbol("unknown-arith"))); for (unsigned i = 0; i < m_lits.size(); i++) { m_params.push_back(parameter(m_lit_coeffs[i].to_rational())); } for (unsigned i = 0; i < m_eqs.size(); i++) { m_params.push_back(parameter(m_eq_coeffs[i].to_rational())); } m_init = true; } } template void theory_arith::antecedents_t::reset() { m_init = false; m_eq_coeffs.reset(); m_lit_coeffs.reset(); m_eqs.reset(); m_lits.reset(); m_params.reset(); } template void theory_arith::antecedents_t::push_lit(literal l, numeral const& r, bool proofs_enabled) { m_lits.push_back(l); if (proofs_enabled) { m_lit_coeffs.push_back(r); } } template void theory_arith::antecedents_t::push_eq(enode_pair const& p, numeral const& r, bool proofs_enabled) { m_eqs.push_back(p); if (proofs_enabled) { m_eq_coeffs.push_back(r); } } template parameter * theory_arith::antecedents_t::params(char const* name) { if (empty()) return nullptr; init(); m_params[0] = parameter(symbol(name)); return m_params.c_ptr(); } // ----------------------------------- // // Bounds // // ----------------------------------- template void theory_arith::bound::display(theory_arith const& th, std::ostream& out) const { out << "v" << get_var() << " " << get_bound_kind() << " " << get_value(); } // ----------------------------------- // // Atoms // // ----------------------------------- template theory_arith::atom::atom(bool_var bv, theory_var v, inf_numeral const & k, atom_kind kind): bound(v, inf_numeral::zero(), B_LOWER, true), m_bvar(bv), m_k(k), m_atom_kind(kind), m_is_true(false) { } template void theory_arith::atom::assign_eh(bool is_true, inf_numeral const & epsilon) { m_is_true = is_true; if (is_true) { this->m_value = m_k; this->m_bound_kind = static_cast(m_atom_kind); SASSERT((this->m_bound_kind == B_LOWER) == (m_atom_kind == A_LOWER)); } else { if (get_atom_kind() == A_LOWER) { this->m_value = m_k; this->m_value -= epsilon; this->m_bound_kind = B_UPPER; } else { SASSERT(get_atom_kind() == A_UPPER); this->m_value = m_k; this->m_value += epsilon; this->m_bound_kind = B_LOWER; } } } template void theory_arith::atom::display(theory_arith const& th, std::ostream& out) const { literal l(get_bool_var(), !m_is_true); // out << "v" << bound::get_var() << " " << bound::get_bound_kind() << " " << get_k() << " "; // out << l << ":"; th.get_context().display_detailed_literal(out, l); } // ----------------------------------- // // eq_bound // // ----------------------------------- template void theory_arith::eq_bound::display(theory_arith const& th, std::ostream& out) const { ast_manager& m = th.get_manager(); out << "#" << m_lhs->get_owner_id() << " " << mk_pp(m_lhs->get_owner(), m) << " = " << "#" << m_rhs->get_owner_id() << " " << mk_pp(m_rhs->get_owner(), m); } // ----------------------------------- // // Auxiliary methods // // ----------------------------------- template bool theory_arith::at_bound(theory_var v) const { bound * l = lower(v); if (l != nullptr && get_value(v) == l->get_value()) return true; bound * u = upper(v); return u != nullptr && get_value(v) == u->get_value(); } template bool theory_arith::is_fixed(theory_var v) const { bound * l = lower(v); if (l == nullptr) return false; bound * u = upper(v); if (u == nullptr) return false; return l->get_value() == u->get_value(); } template void theory_arith::set_bound(bound * new_bound, bool upper) { SASSERT(new_bound); SASSERT(!upper || new_bound->get_bound_kind() == B_UPPER); SASSERT(upper || new_bound->get_bound_kind() == B_LOWER); theory_var v = new_bound->get_var(); set_bound_core(v, new_bound, upper); if ((propagate_eqs() || propagate_diseqs()) && is_fixed(v)) fixed_var_eh(v); } /** \brief Return the col_entry that points to a base row that contains the given variable. Return 0 if no row contains v. */ template typename theory_arith::col_entry const * theory_arith::get_a_base_row_that_contains(theory_var v) { while (true) { column const & c = m_columns[v]; if (c.size() == 0) return nullptr; int quasi_base_rid = -1; typename svector::const_iterator it = c.begin_entries(); typename svector::const_iterator end = c.end_entries(); for (; it != end; ++it) { if (!it->is_dead()) { unsigned rid = it->m_row_id; row & r = m_rows[rid]; theory_var v = r.get_base_var(); if (v == null_theory_var) { // skip } else if (is_base(v)) { return it; } else if (quasi_base_rid == -1) quasi_base_rid = rid; } } if (quasi_base_rid == -1) { return nullptr; } quasi_base_row2base_row(quasi_base_rid); // There is no guarantee that v is still a variable of row quasi_base_rid. // However, this loop will always terminate since I'm creating // a base row that contains v, or decreasing c.size(). } } /** \brief Return true if all coefficients of the given row are int. */ template bool theory_arith::all_coeff_int(row const & r) const { typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (!it->is_dead() && !it->m_coeff.is_int()) { TRACE("gomory_cut", display_row(tout, r, true);); return false; } } return true; } /** \brief Return the col_entry that points to row that contains the given variable. This row should not be owned by an unconstrained quasi-base variable. Return 0 if failed. This method is used by move_unconstrained_to_base */ template typename theory_arith::col_entry const * theory_arith::get_row_for_eliminating(theory_var v) const { column const & c = m_columns[v]; if (c.size() == 0) return nullptr; typename svector::const_iterator it = c.begin_entries(); typename svector::const_iterator end = c.end_entries(); for (; it != end; ++it) { 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].empty()) continue; if (is_int(v)) { numeral const & c = r[it->m_row_idx].m_coeff; // If c == 1 or c == -1, and all other coefficients of r are integer, // then if we pivot v with the base var of r, we will produce a row // that will guarantee an integer assignment for v, when the // non-base vars have integer assignment. if (!c.is_one() && !c.is_minus_one()) continue; if (!all_coeff_int(r)) continue; } return it; } } return nullptr; } template void theory_arith::move_unconstrained_to_base() { if (lazy_pivoting_lvl() == 0) return; 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].empty() && is_free(v)) { switch (get_var_kind(v)) { case QUASI_BASE: break; case BASE: if (is_int(v) && !all_coeff_int(m_rows[get_var_row(v)])) // If the row contains non integer coefficients, then v may be assigned // to a non-integer value even if all non-base variables are integer. // So, v should not be "eliminated" break; eliminate(v, m_eager_gcd); break; case NON_BASE: { col_entry const * entry = get_row_for_eliminating(v); if (entry) { TRACE("move_unconstrained_to_base", tout << "moving v" << v << " to the base\n";); row & r = m_rows[entry->m_row_id]; SASSERT(r[entry->m_row_idx].m_var == v); pivot(r.get_base_var(), v, r[entry->m_row_idx].m_coeff, m_eager_gcd); SASSERT(is_base(v)); set_var_kind(v, QUASI_BASE); SASSERT(is_quasi_base(v)); } break; } } } } TRACE("move_unconstrained_to_base", tout << "after...\n"; display(tout);); CASSERT("arith", wf_rows()); CASSERT("arith", wf_columns()); CASSERT("arith", valid_row_assignment()); } /** \brief Force all quasi_base rows to become base rows. */ template void theory_arith::elim_quasi_base_rows() { int num = get_num_vars(); for (theory_var v = 0; v < num; v++) { if (is_quasi_base(v)) { quasi_base_row2base_row(get_var_row(v)); } } } /** \brief Remove fixed vars from the base. */ template void theory_arith::remove_fixed_vars_from_base() { int num = get_num_vars(); for (theory_var v = 0; v < num; v++) { if (is_base(v) && is_fixed(v)) { row const & r = m_rows[get_var_row(v)]; typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (!it->is_dead() && it->m_var != v && !is_fixed(it->m_var)) { break; } } if (it != end) { pivot(v, it->m_var, it->m_coeff, false); } } } } /** \brief Try to minimize the number of rational coefficients. The idea is to pivot x_i and x_j whenever there is a row x_i + 1/n * x_j + ... = 0 where - x_i is a base variable - x_j is a non-base variables - x_j is not a fixed variable - The denominator of any other coefficient a_ik divides n (I only consider the coefficient of non-fixed variables) remark if there are more than one variable with such properties, we give preference to free variables, then to variables where upper - lower is maximal. */ template void theory_arith::try_to_minimize_rational_coeffs() { int num = get_num_vars(); for (theory_var v = 0; v < num; v++) { if (!is_base(v) || !is_int(v)) continue; numeral max_den; row const & r = m_rows[get_var_row(v)]; typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (it->is_dead()) continue; if (it->m_var == v) continue; if (is_fixed(it->m_var)) continue; numeral num = numerator(it->m_coeff); if (!num.is_one() && !num.is_minus_one()) continue; numeral den = denominator(it->m_coeff); if (den > max_den) max_den = den; } if (max_den <= numeral(1)) continue; // check whether all a_ik denominators divide max_den it = r.begin_entries(); for (; it != end; ++it) { if (it->is_dead()) continue; if (is_fixed(it->m_var)) continue; numeral den = denominator(it->m_coeff); if (!(max_den / den).is_int()) break; } if (it != end) continue; // pick best candidate theory_var x_j = null_theory_var; numeral a_ij; it = r.begin_entries(); for (; it != end; ++it) { if (it->is_dead()) continue; if (it->m_var == v) continue; if (is_fixed(it->m_var)) continue; numeral num = numerator(it->m_coeff); if (!num.is_one() && !num.is_minus_one()) continue; numeral den = denominator(it->m_coeff); if (den != max_den) continue; if (x_j == null_theory_var || // TODO: add extra cases... is_free(it->m_var) || (is_bounded(x_j) && !is_bounded(it->m_var)) || (is_bounded(x_j) && is_bounded(it->m_var) && (upper_bound(x_j) - lower_bound(x_j) > upper_bound(it->m_var) - lower_bound(it->m_var)))) { x_j = it->m_var; a_ij = it->m_coeff; if (is_free(x_j)) break; } } if (x_j != null_theory_var) pivot(v, x_j, a_ij, false); } } // ----------------------------------- // // Derived bounds // // ----------------------------------- template void theory_arith::derived_bound::push_justification(antecedents& a, numeral const& coeff, bool proofs_enabled) { TRACE("arith", tout << m_lits << " " << m_eqs.size() << "\n";); if (proofs_enabled) { for (literal l : m_lits) a.push_lit(l, coeff, proofs_enabled); for (auto const& e : m_eqs) a.push_eq(e, coeff, proofs_enabled); } else { a.append(m_lits.size(), m_lits.c_ptr()); a.append(m_eqs.size(), m_eqs.c_ptr()); } } template void theory_arith::derived_bound::display(theory_arith const& th, std::ostream& out) const { ast_manager& m = th.get_manager(); out << "v" << bound::get_var() << " " << bound::get_bound_kind() << " " << bound::get_value() << "\n"; out << "expr: " << mk_pp(th.var2expr(bound::get_var()), m) << "\n"; for (auto const& e : m_eqs) { enode* a = e.first; enode* b = e.second; out << " "; out << "#" << a->get_owner_id() << " " << mk_pp(a->get_owner(), m) << " = " << "#" << b->get_owner_id() << " " << mk_pp(b->get_owner(), m) << "\n"; } for (literal l : m_lits) { out << l << ":"; th.get_context().display_detailed_literal(out, l) << "\n"; } } template void theory_arith::justified_derived_bound::push_justification(antecedents& a, numeral const& coeff, bool proofs_enabled) { for (unsigned i = 0; i < this->m_lits.size(); ++i) { a.push_lit(this->m_lits[i], coeff*m_lit_coeffs[i], proofs_enabled); } for (unsigned i = 0; i < this->m_eqs.size(); ++i) { a.push_eq(this->m_eqs[i], coeff*m_eq_coeffs[i], proofs_enabled); } } template void theory_arith::justified_derived_bound::push_lit(literal l, numeral const& coeff) { for (unsigned i = 0; i < this->m_lits.size(); ++i) { if (this->m_lits[i] == l) { m_lit_coeffs[i] += coeff; return; } } this->m_lits.push_back(l); m_lit_coeffs.push_back(coeff); } template void theory_arith::justified_derived_bound::push_eq(enode_pair const& p, numeral const& coeff) { for (unsigned i = 0; i < this->m_eqs.size(); ++i) { if (this->m_eqs[i] == p) { m_eq_coeffs[i] += coeff; return; } } this->m_eqs.push_back(p); m_eq_coeffs.push_back(coeff); } /** \brief Copy the justification of b to new_bound. Only literals and equalities not in lits and eqs are copied. The justification of b is also copied to lits and eqs. */ template void theory_arith::accumulate_justification(bound & b, derived_bound& new_bound, numeral const& coeff, literal_idx_set & lits, eq_set & eqs) { antecedents ante(*this); b.push_justification(ante, coeff, proofs_enabled()); unsigned num_lits = ante.lits().size(); for (unsigned i = 0; i < num_lits; ++i) { literal l = ante.lits()[i]; if (lits.contains(l.index())) continue; if (proofs_enabled()) { new_bound.push_lit(l, ante.lit_coeffs()[i]); } else { new_bound.push_lit(l, numeral::zero()); lits.insert(l.index()); } } unsigned num_eqs = ante.eqs().size(); for (unsigned i = 0; i < num_eqs; ++i) { enode_pair const & p = ante.eqs()[i]; if (eqs.contains(p)) continue; if (proofs_enabled()) { new_bound.push_eq(p, ante.eq_coeffs()[i]); } else { new_bound.push_eq(p, numeral::zero()); eqs.insert(p); } } } template typename theory_arith::inf_numeral theory_arith::normalize_bound(theory_var v, inf_numeral const & k, bound_kind kind) { if (is_real(v)) { return k; } if (kind == B_LOWER) return inf_numeral(ceil(k)); SASSERT(kind == B_UPPER); return inf_numeral(floor(k)); } /** \brief Create a derived bound for v using the given row as an explanation. */ template void theory_arith::mk_bound_from_row(theory_var v, inf_numeral const & k, bound_kind kind, row const & r) { inf_numeral k_norm = normalize_bound(v, k, kind); derived_bound * new_bound = proofs_enabled()?alloc(justified_derived_bound, v, k_norm, kind):alloc(derived_bound, v, k_norm, kind); m_bounds_to_delete.push_back(new_bound); m_asserted_bounds.push_back(new_bound); m_tmp_lit_set.reset(); m_tmp_eq_set.reset(); #ifdef Z3DEBUG inf_numeral val; #endif typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (!it->is_dead()) { theory_var v = it->m_var; bool use_upper = (kind == B_UPPER); if (!it->m_coeff.is_pos()) use_upper = !use_upper; TRACE("derived_bound", tout << "using " << (use_upper ? "upper" : "lower") << " bound of v" << v << "\n";); bound * b = get_bound(v, use_upper); SASSERT(b); DEBUG_CODE({ inf_numeral tmp = b->get_value(); tmp *= it->m_coeff; val += tmp; }); accumulate_justification(*b, *new_bound, it->m_coeff, m_tmp_lit_set, m_tmp_eq_set); } } TRACE("derived_bound", tout << "explanation:\n"; for (literal l : new_bound->m_lits) tout << l << " "; tout << " "; for (auto const& e : new_bound->m_eqs) tout << "#" << e.first->get_owner_id() << "=#" << e.second->get_owner_id() << " "; tout << "\n";); DEBUG_CODE(CTRACE("derived_bound", k != val, tout << "k: " << k << ", k_norm: " << k_norm << ", val: " << val << "\n";);); SASSERT(k == val); } // ----------------------------------- // // Maximization/Minimization // // ----------------------------------- /** \brief Set: row1 <- row1 + coeff * row2, where row1 is a temporary row. \remark Columns do not need to be updated when updating a temporary row. */ template void theory_arith::add_tmp_row(row & r1, numeral const & coeff, row const & r2) { r1.save_var_pos(m_var_pos); // // loop over variables in row2, // add terms in row2 to row1. // #define ADD_TMP_ROW(_SET_COEFF_, _ADD_COEFF_) \ typename vector::const_iterator it = r2.begin_entries(); \ typename vector::const_iterator end = r2.end_entries(); \ for (; it != end; ++it) { \ if (!it->is_dead()) { \ theory_var v = it->m_var; \ int pos = m_var_pos[v]; \ if (pos == -1) { \ /* variable v is not in row1 */ \ int row_idx; \ row_entry & r_entry = r1.add_row_entry(row_idx); \ r_entry.m_var = v; \ _SET_COEFF_; \ } \ else { \ /* variable v is in row1 */ \ row_entry & r_entry = r1[pos]; \ SASSERT(r_entry.m_var == v); \ _ADD_COEFF_; \ if (r_entry.m_coeff.is_zero()) { \ r1.del_row_entry(pos); \ } \ m_var_pos[v] = -1; \ } \ } \ } ((void) 0) if (coeff.is_one()) { ADD_TMP_ROW(r_entry.m_coeff = it->m_coeff, r_entry.m_coeff += it->m_coeff); } else if (coeff.is_minus_one()) { ADD_TMP_ROW(r_entry.m_coeff = it->m_coeff; r_entry.m_coeff.neg(), r_entry.m_coeff -= it->m_coeff); } else { ADD_TMP_ROW(r_entry.m_coeff = it->m_coeff; r_entry.m_coeff *= coeff, r_entry.m_coeff += it->m_coeff * coeff); } r1.reset_var_pos(m_var_pos); } template bool theory_arith::is_safe_to_leave(theory_var x, bool inc, bool& has_int, bool& shared) { context& ctx = get_context(); shared |= ctx.is_shared(get_enode(x)); column & c = m_columns[x]; typename svector::iterator it = c.begin_entries(); typename svector::iterator end = c.end_entries(); has_int = false; bool unbounded = (inc && !upper(x)) || (!inc && !lower(x)); bool was_unsafe = false; for (; it != end; ++it) { if (it->is_dead()) continue; row const & r = m_rows[it->m_row_id]; theory_var s = r.get_base_var(); numeral const & coeff = r[it->m_row_idx].m_coeff; if (s != null_theory_var && is_int(s)) has_int = true; bool is_unsafe = (s != null_theory_var && is_int(s) && !coeff.is_int()); shared |= (s != null_theory_var && ctx.is_shared(get_enode(s))); was_unsafe |= is_unsafe; bool inc_s = coeff.is_neg() ? inc : !inc; unbounded &= !get_bound(s, inc_s); TRACE("opt", tout << "is v" << x << " safe to leave for v" << s << "? " << (is_unsafe?"no":"yes") << " " << (has_int?"int":"real") << " " << (unbounded?"unbounded":"bounded") << "\n"; display_row(tout, r, true);); if (was_unsafe && !unbounded) return false; } return !was_unsafe || unbounded; } template bool theory_arith::get_theory_vars(expr * n, uint_set & vars) { rational r; expr* x, *y; if (m_util.is_numeral(n, r)) { return true; } else if (m_util.is_add(n)) { for (unsigned i = 0; i < to_app(n)->get_num_args(); ++i) { if (!get_theory_vars(to_app(n)->get_arg(i), vars)) { return false; } } } else if (m_util.is_to_real(n, x) || m_util.is_to_int(n, x)) { return get_theory_vars(x, vars); } else if (m_util.is_mul(n, x, y) && m_util.is_numeral(x, r)) { return get_theory_vars(y, vars); } else if (m_util.is_mul(n, y, x) && m_util.is_numeral(x, r)) { return get_theory_vars(y, vars); } else if (!is_app(n)) { return false; } else if (to_app(n)->get_family_id() == m_util.get_family_id()) { return false; } else { context & ctx = get_context(); SASSERT(ctx.e_internalized(n)); enode * e = ctx.get_enode(n); if (is_attached_to_var(e)) { vars.insert(e->get_th_var(get_id())); } return true; } return true; } // // add_objective(expr* term) internalizes the arithmetic term and creates // a row for it if it is not already internalized. // Then return the variable corresponding to the term. // template theory_var theory_arith::add_objective(app* term) { theory_var v = internalize_term_core(term); TRACE("opt", tout << mk_pp(term, get_manager()) << " |-> v" << v << "\n";); SASSERT(!is_quasi_base(v)); if (!is_linear(get_manager(), term)) { v = null_theory_var; } return v; } template inf_eps_rational theory_arith::value(theory_var v) { return inf_eps_rational(get_value(v)); } template inf_eps_rational theory_arith::maximize(theory_var v, expr_ref& blocker, bool& has_shared) { TRACE("bound_bug", display_var(tout, v); display(tout);); has_shared = false; if (!m_nl_monomials.empty()) { has_shared = true; blocker = mk_gt(v); return inf_eps_rational(get_value(v)); } max_min_t r = max_min(v, true, true, has_shared); if (r == UNBOUNDED) { has_shared = false; blocker = get_manager().mk_false(); return inf_eps_rational::infinity(); } else { blocker = mk_gt(v); return inf_eps_rational(get_value(v)); } } /** \brief: Create an atom that enforces the inequality v > val The arithmetical expression encoding the inequality suffices for the theory of arithmetic. */ template expr_ref theory_arith::mk_gt(theory_var v) { ast_manager& m = get_manager(); inf_numeral const& val = get_value(v); expr* obj = get_enode(v)->get_owner(); expr_ref e(m); rational r = val.get_rational(); if (m_util.is_int(m.get_sort(obj))) { if (r.is_int()) { r += rational::one(); } else { r = ceil(r); } e = m_util.mk_numeral(r, m.get_sort(obj)); e = m_util.mk_ge(obj, e); } else { // obj is over the reals. e = m_util.mk_numeral(r, m.get_sort(obj)); if (val.get_infinitesimal().is_neg()) { e = m_util.mk_ge(obj, e); } else { e = m_util.mk_gt(obj, e); } } TRACE("opt", tout << e << "\n";); return e; } /** \brief create atom that enforces: val <= v The atom that enforces the inequality is created directly as opposed to using arithmetical terms. This allows to handle inequalities with non-standard numbers. */ template expr_ref theory_arith::mk_ge(generic_model_converter& fm, theory_var v, inf_numeral const& val) { ast_manager& m = get_manager(); context& ctx = get_context(); std::ostringstream strm; strm << val << " <= " << mk_pp(get_enode(v)->get_owner(), get_manager()); app* b = m.mk_const(symbol(strm.str().c_str()), m.mk_bool_sort()); expr_ref result(b, m); TRACE("opt", tout << result << "\n";); if (!ctx.b_internalized(b)) { fm.hide(b->get_decl()); bool_var bv = ctx.mk_bool_var(b); ctx.set_var_theory(bv, get_id()); // ctx.set_enode_flag(bv, true); atom* a = alloc(atom, bv, v, val, A_LOWER); mk_bound_axioms(a); m_unassigned_atoms[v]++; m_var_occs[v].push_back(a); m_atoms.push_back(a); insert_bv2a(bv, a); TRACE("arith", tout << mk_pp(b, m) << "\n"; display_atom(tout, a, false);); } return result; } /** \brief enable watching bound atom. */ template void theory_arith::enable_record_conflict(expr* bound) { m_params.m_arith_bound_prop = BP_NONE; SASSERT(propagation_mode() == BP_NONE); // bound propagation rules are not (yet) handled. if (bound) { context& ctx = get_context(); m_bound_watch = ctx.get_bool_var(bound); } else { m_bound_watch = null_bool_var; } m_upper_bound = -inf_eps_rational::infinity(); } /** \brief pos < 0 == r(Ax <= b) + q(v <= val) == val' <= q*v & q*v <= q*val q*v - val' >= 0 => (q*v - val' - q*v)/q >= -v == val/q <= v */ template void theory_arith::record_conflict( unsigned num_lits, literal const * lits, unsigned num_eqs, enode_pair const * eqs, unsigned num_params, parameter* params) { ast_manager& m = get_manager(); context& ctx = get_context(); expr_ref tmp(m), vq(m); expr* x, *y, *e; if (null_bool_var == m_bound_watch) { return; } unsigned idx = num_lits; for (unsigned i = 0; i < num_lits; ++i) { if (m_bound_watch == lits[i].var()) { //SASSERT(!lits[i].sign()); idx = i; break; } } if (idx == num_lits) { return; } for (unsigned i = 0; i < num_lits; ++i) { ctx.literal2expr(lits[i], tmp); } for (unsigned i = 0; i < num_eqs; ++i) { enode_pair const& p = eqs[i]; x = p.first->get_owner(); y = p.second->get_owner(); tmp = m.mk_eq(x,y); } SASSERT(num_params == 1 + num_lits + num_eqs); SASSERT(params[0].is_symbol()); SASSERT(params[0].get_symbol() == symbol("farkas")); // for now, just handle this rule. farkas_util farkas(m); rational q; for (unsigned i = 0; i < num_lits; ++i) { parameter const& pa = params[i+1]; SASSERT(pa.is_rational()); if (idx == i) { q = abs(pa.get_rational()); continue; } ctx.literal2expr(lits[i], tmp); farkas.add(abs(pa.get_rational()), to_app(tmp)); } for (unsigned i = 0; i < num_eqs; ++i) { enode_pair const& p = eqs[i]; x = p.first->get_owner(); y = p.second->get_owner(); tmp = m.mk_eq(x,y); parameter const& pa = params[1 + num_lits + i]; SASSERT(pa.is_rational()); farkas.add(abs(pa.get_rational()), to_app(tmp)); } tmp = farkas.get(); if (m.has_trace_stream()) { log_axiom_instantiation(tmp); m.trace_stream() << "[end-of-instance]\n"; } // IF_VERBOSE(1, verbose_stream() << "Farkas result: " << tmp << "\n";); atom* a = get_bv2a(m_bound_watch); SASSERT(a); expr_ref_vector terms(m); vector mults; bool strict = false; if (m_util.is_le(tmp, x, y) || m_util.is_ge(tmp, y, x)) { } else if (m.is_not(tmp, e) && (m_util.is_le(e, y, x) || m_util.is_ge(e, x, y))) { strict = true; } else if (m.is_eq(tmp, x, y)) { } else { UNREACHABLE(); } e = var2expr(a->get_var()); q *= farkas.get_normalize_factor(); SASSERT(!m_util.is_int(e) || q.is_int()); // TBD: not fully handled. if (q.is_one()) { vq = e; } else { vq = m_util.mk_mul(m_util.mk_numeral(q, q.is_int()), e); } vq = m_util.mk_add(m_util.mk_sub(x, y), vq); if (!q.is_one()) { vq = m_util.mk_div(vq, m_util.mk_numeral(q, q.is_int())); } th_rewriter rw(m); rw(vq, tmp); VERIFY(m_util.is_numeral(tmp, q)); if (m_upper_bound < q) { m_upper_bound = q; if (strict) { m_upper_bound -= get_epsilon(a->get_var()); } IF_VERBOSE(1, verbose_stream() << "new upper bound: " << m_upper_bound << "\n";); } } /** \brief find the minimal upper bound on the variable that was last enabled for conflict recording. */ template inf_eps_rational theory_arith::conflict_minimize() { return m_upper_bound; } /** \brief Select tightest variable x_i to pivot with x_j. The goal is to select a x_i such that the value of x_j is increased (decreased) if inc = true (inc = false), and the tableau remains feasible. Store the gain in x_j of the pivoting operation in 'gain'. Note the gain can be too much. That is, it may make x_i infeasible. In this case, instead of pivoting we move x_j to its upper bound (lower bound) when inc = true (inc = false). If no x_i imposes a restriction on x_j, then return null_theory_var. That is, x_j is free to move to its upper bound (lower bound). Get the equations for x_j: x_i1 = coeff_1 * x_j + rest_1 ... x_in = coeff_n * x_j + rest_n gain_k := (upper_bound(x_ik) - value(x_ik))/coeff_k */ template bool theory_arith::pick_var_to_leave( theory_var x_j, // non-base variable to increment/decrement bool inc, numeral & a_ij, // coefficient of x_i inf_numeral& min_gain, // minimal required gain on x_j (integral value on integers) inf_numeral& max_gain, // maximal possible gain on x_j bool& has_shared, // determine if pivot involves shared variable theory_var& x_i) { // base variable to pivot with x_j context& ctx = get_context(); x_i = null_theory_var; init_gains(x_j, inc, min_gain, max_gain); has_shared |= ctx.is_shared(get_enode(x_j)); if (is_int(x_j) && !get_value(x_j).is_int()) { return false; } column & c = m_columns[x_j]; typename svector::iterator it = c.begin_entries(); typename svector::iterator end = c.end_entries(); bool empty_column = true; for (; it != end; ++it) { if (it->is_dead()) continue; empty_column = false; row const & r = m_rows[it->m_row_id]; theory_var s = r.get_base_var(); numeral const & coeff_ij = r[it->m_row_idx].m_coeff; if (update_gains(inc, s, coeff_ij, min_gain, max_gain) || (x_i == null_theory_var && !unbounded_gain(max_gain))) { x_i = s; a_ij = coeff_ij; } has_shared |= ctx.is_shared(get_enode(s)); } TRACE("opt", tout << (safe_gain(min_gain, max_gain)?"safe":"unsafe") << "\n"; tout << "min gain: " << min_gain; tout << " max gain: " << max_gain << "\n"; tout << "v" << x_i << " "; tout << (has_shared?"shared":"not shared") << "\n";); (void) empty_column; SASSERT(!safe_gain(min_gain, max_gain) || empty_column || (unbounded_gain(max_gain) == (x_i == null_theory_var))); return safe_gain(min_gain, max_gain); } template bool theory_arith::unbounded_gain(inf_numeral const & max_gain) const { return max_gain.is_minus_one(); } /* A gain is 'safe' with respect to the tableau if: - the selected variable is unbounded and every base variable where it occurs is unbounded in the direction of the gain. max_gain == -1 is used to indicate unbounded variables. - the selected variable is a rational (min_gain == -1, max_gain >= 0). - */ template bool theory_arith::safe_gain(inf_numeral const& min_gain, inf_numeral const & max_gain) const { return unbounded_gain(max_gain) || min_gain <= max_gain; } /** \brief ensure that maximal gain is divisible by divisor. */ template void theory_arith::normalize_gain(numeral const& divisor, inf_numeral & max_gain) const { SASSERT(divisor.is_int()); if (!divisor.is_minus_one() && !max_gain.is_minus_one()) { max_gain = floor(max_gain/divisor)*divisor; } } /** \brief initialize gains for x_j based on the bounds for x_j. */ template void theory_arith::init_gains( theory_var x, // non-base variable to increment/decrement bool inc, inf_numeral& min_gain, // min value to increment, -1 if rational inf_numeral& max_gain) { // max value to decrement, -1 if unbounded min_gain = -inf_numeral::one(); max_gain = -inf_numeral::one(); if (inc && upper(x)) { max_gain = upper_bound(x) - get_value(x); } else if (!inc && lower(x)) { max_gain = get_value(x) - lower_bound(x); } if (is_int(x)) { min_gain = inf_numeral::one(); } TRACE("opt", tout << "v" << x << " := " << get_value(x) << " " << "min gain: " << min_gain << " " << "max gain: " << max_gain << "\n";); SASSERT(max_gain.is_minus_one() || !max_gain.is_neg()); SASSERT(min_gain.is_minus_one() || min_gain.is_one()); SASSERT(is_int(x) == min_gain.is_one()); } template bool theory_arith::update_gains( bool inc, // increment/decrement x_j theory_var x_i, // potential base variable to pivot numeral const& a_ij, // coefficient of x_j in row where x_i is base. inf_numeral& min_gain, // min value to increment, -1 if rational inf_numeral& max_gain) { // max value to decrement, -1 if unbounded // x_i = row + a_ij*x_j // a_ij > 0, inc -> decrement x_i // a_ij < 0, !inc -> decrement x_i // a_ij denominator SASSERT(!a_ij.is_zero()); if (!safe_gain(min_gain, max_gain)) return false; inf_numeral max_inc = inf_numeral::minus_one(); bool decrement_x_i = (inc && a_ij.is_pos()) || (!inc && a_ij.is_neg()); if (decrement_x_i && lower(x_i)) { max_inc = abs((get_value(x_i) - lower_bound(x_i))/a_ij); } else if (!decrement_x_i && upper(x_i)) { max_inc = abs((upper_bound(x_i) - get_value(x_i))/a_ij); } numeral den_aij(1); bool is_tighter = false; if (is_int(x_i)) den_aij = denominator(a_ij); SASSERT(den_aij.is_pos() && den_aij.is_int()); if (is_int(x_i) && !den_aij.is_one()) { if (min_gain.is_neg()) { min_gain = inf_numeral(den_aij); } else { min_gain = inf_numeral(lcm(min_gain.get_rational(), den_aij)); } normalize_gain(min_gain.get_rational(), max_gain); } if (is_int(x_i) && !max_gain.is_int()) { max_gain = inf_numeral(floor(max_gain)); normalize_gain(min_gain.get_rational(), max_gain); } if (!max_inc.is_minus_one()) { if (is_int(x_i)) { TRACE("opt", tout << "v" << x_i << " a_ij " << a_ij << " " << "min gain: " << min_gain << " " << "max gain: " << max_gain << "\n";); max_inc = floor(max_inc); normalize_gain(min_gain.get_rational(), max_inc); } if (unbounded_gain(max_gain)) { max_gain = max_inc; is_tighter = true; } else if (max_gain > max_inc) { max_gain = max_inc; is_tighter = true; } } TRACE("opt", tout << "v" << x_i << (is_int(x_i)?" int":" real") << " a_ij " << a_ij << " " << "min gain: " << min_gain << " " << "max gain: " << max_gain << " tighter: " << (is_tighter?"true":"false") << "\n";); SASSERT(max_gain.is_minus_one() || !max_gain.is_neg()); SASSERT(min_gain.is_minus_one() || !min_gain.is_neg()); //SASSERT(!is_int(x_i) || min_gain.is_pos()); //SASSERT(!is_int(x_i) || min_gain.is_int()); //SASSERT(!is_int(x_i) || max_gain.is_int()); return is_tighter; } /** \brief Check if bound change affects interface equality. */ template bool theory_arith::has_interface_equality(theory_var x) { theory_var num = get_num_vars(); context& ctx = get_context(); enode* r = get_enode(x)->get_root(); for (theory_var v = 0; v < num; v++) { if (v == x) continue; enode* n = get_enode(v); if (ctx.is_shared(n) && n->get_root() == r) { return true; } } return false; } /** \brief Maximize (Minimize) the given temporary row. Return true if succeeded. */ template typename theory_arith::max_min_t theory_arith::max_min( row & r, bool max, bool maintain_integrality, bool& has_shared) { m_stats.m_max_min++; unsigned best_efforts = 0; bool inc = false; context& ctx = get_context(); SASSERT(!maintain_integrality || valid_assignment()); SASSERT(satisfy_bounds()); numeral a_ij, curr_a_ij, coeff, curr_coeff; inf_numeral min_gain, max_gain, curr_min_gain, curr_max_gain; unsigned round = 0; max_min_t result = OPTIMIZED; has_shared = false; unsigned max_efforts = 10 + (ctx.get_random_value() % 20); while (best_efforts < max_efforts && !ctx.get_cancel_flag()) { theory_var x_j = null_theory_var; theory_var x_i = null_theory_var; bool has_bound = false; max_gain.reset(); min_gain.reset(); ++round; TRACE("opt", tout << "round: " << round << ", max: " << max << "\n"; display_row(tout, r, true); tout << "state:\n"; display(tout);); typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (it->is_dead()) continue; theory_var curr_x_j = it->m_var; theory_var curr_x_i = null_theory_var; SASSERT(is_non_base(curr_x_j)); curr_coeff = it->m_coeff; bool curr_inc = curr_coeff.is_pos() ? max : !max; if ((curr_inc && upper(curr_x_j)) || (!curr_inc && lower(curr_x_j))) { has_bound = true; } if ((curr_inc && at_upper(curr_x_j)) || (!curr_inc && at_lower(curr_x_j))) { // variable cannot be used for max/min. continue; } bool safe_to_leave = pick_var_to_leave(curr_x_j, curr_inc, curr_a_ij, curr_min_gain, curr_max_gain, has_shared, curr_x_i); if (!safe_to_leave) { TRACE("opt", tout << "no variable picked\n";); has_bound = true; best_efforts++; } else if (curr_x_i == null_theory_var) { TRACE("opt", tout << "v" << curr_x_j << " is unrestricted by other variables\n";); // we can increase/decrease curr_x_j as much as we want. x_i = null_theory_var; // unbounded x_j = curr_x_j; inc = curr_inc; min_gain = curr_min_gain; max_gain = curr_max_gain; break; } else if (curr_max_gain > max_gain) { x_i = curr_x_i; x_j = curr_x_j; a_ij = curr_a_ij; coeff = curr_coeff; max_gain = curr_max_gain; min_gain = curr_min_gain; inc = curr_inc; } else if (curr_max_gain.is_zero() && (x_i == null_theory_var || curr_x_i < x_i)) { x_i = curr_x_i; x_j = curr_x_j; a_ij = curr_a_ij; coeff = curr_coeff; max_gain = curr_max_gain; min_gain = curr_min_gain; inc = curr_inc; // continue } } TRACE("opt", tout << "after traversing row:\nx_i: v" << x_i << ", x_j: v" << x_j << ", gain: " << max_gain << "\n"; tout << "best efforts: " << best_efforts << " has shared: " << has_shared << "\n";); if (!has_bound && x_i == null_theory_var && x_j == null_theory_var) { has_shared = false; best_efforts = 0; result = UNBOUNDED; break; } if (x_j == null_theory_var) { TRACE("opt", tout << "row is " << (max ? "maximized" : "minimized") << "\n"; display_row(tout, r, true);); SASSERT(!maintain_integrality || valid_assignment()); SASSERT(satisfy_bounds()); result = OPTIMIZED; break; } if (min_gain.is_pos() && !min_gain.is_one()) { ++best_efforts; } if (x_i == null_theory_var) { // can increase/decrease x_j as much as we want. if (inc && upper(x_j)) { if (max_gain.is_zero()) return BEST_EFFORT; SASSERT(!unbounded_gain(max_gain)); update_value(x_j, max_gain); TRACE("opt", tout << "moved v" << x_j << " to upper bound\n";); SASSERT(!maintain_integrality || valid_assignment()); SASSERT(satisfy_bounds()); continue; } if (!inc && lower(x_j)) { if (max_gain.is_zero()) return BEST_EFFORT; SASSERT(!unbounded_gain(max_gain)); SASSERT(max_gain.is_pos()); max_gain.neg(); update_value(x_j, max_gain); TRACE("opt", tout << "moved v" << x_j << " to lower bound\n";); SASSERT(!maintain_integrality || valid_assignment()); SASSERT(satisfy_bounds()); continue; } #if 0 if (ctx.is_shared(get_enode(x_j)) && has_interface_equality(x_j)) { ++best_efforts; } else { SASSERT(unbounded_gain(max_gain)); has_shared = false; best_efforts = 0; } #endif // // NB. As it stands this is a possibly unsound conclusion for shared theories. // the tradeoff is non-termination for unbounded objectives in the // presence of sharing. // has_shared = false; best_efforts = 0; result = UNBOUNDED; break; } if (!is_fixed(x_j) && is_bounded(x_j) && (upper_bound(x_j) - lower_bound(x_j) == max_gain)) { // can increase/decrease x_j up to upper/lower bound. if (inc) { TRACE("opt", tout << "moved v" << x_j << " to upper bound\n";); } else { max_gain.neg(); TRACE("opt", tout << "moved v" << x_j << " to lower bound\n";); } update_value(x_j, max_gain); SASSERT(!maintain_integrality || valid_assignment()); SASSERT(satisfy_bounds()); continue; } TRACE("opt", tout << "max: " << max << ", x_i: v" << x_i << ", x_j: v" << x_j << ", a_ij: " << a_ij << ", coeff: " << coeff << "\n"; if (upper(x_i)) tout << "upper x_i: " << upper_bound(x_i) << " "; if (lower(x_i)) tout << "lower x_i: " << lower_bound(x_i) << " "; tout << "value x_i: " << get_value(x_i) << "\n"; if (upper(x_j)) tout << "upper x_j: " << upper_bound(x_j) << " "; if (lower(x_j)) tout << "lower x_j: " << lower_bound(x_j) << " "; tout << "value x_j: " << get_value(x_j) << "\n"; ); pivot(x_i, x_j, a_ij, false); SASSERT(is_non_base(x_i)); SASSERT(is_base(x_j)); bool inc_xi = inc?a_ij.is_neg():a_ij.is_pos(); if (!move_to_bound(x_i, inc_xi, best_efforts, has_shared)) { TRACE("opt", tout << "can't move bound fully\n";); // break; // break; } row & r2 = m_rows[get_var_row(x_j)]; coeff.neg(); add_tmp_row(r, coeff, r2); SASSERT(r.get_idx_of(x_j) == -1); SASSERT(!maintain_integrality || valid_assignment()); SASSERT(satisfy_bounds()); } TRACE("opt_verbose", display(tout);); return (best_efforts>0 || ctx.get_cancel_flag())?BEST_EFFORT:result; } /** Move the variable x_i maximally towards its bound as long as bounds of other variables are not violated. Returns false if an integer bound was truncated and no progress was made. */ template bool theory_arith::move_to_bound( theory_var x_i, // variable to move bool inc, // increment variable or decrement unsigned& best_efforts, // is bound move a best effort? bool& has_shared) { // does move include shared variables? inf_numeral min_gain, max_gain; if (is_int(x_i) && !get_value(x_i).is_int()) { ++best_efforts; return false; } init_gains(x_i, inc, min_gain, max_gain); column & c = m_columns[x_i]; typename svector::iterator it = c.begin_entries(); typename svector::iterator end = c.end_entries(); for (; it != end; ++it) { if (it->is_dead()) continue; row const & r = m_rows[it->m_row_id]; theory_var s = r.get_base_var(); numeral const & coeff = r[it->m_row_idx].m_coeff; update_gains(inc, s, coeff, min_gain, max_gain); has_shared |= get_context().is_shared(get_enode(s)); } bool result = false; if (safe_gain(min_gain, max_gain)) { TRACE("opt", tout << "Safe delta: " << max_gain << "\n";); SASSERT(!unbounded_gain(max_gain)); if (!inc) { max_gain.neg(); } update_value(x_i, max_gain); if (!min_gain.is_pos() || min_gain.is_one()) { ++best_efforts; } result = !max_gain.is_zero(); } if (!result) { ++best_efforts; } return result; } /** \brief Add an entry to a temporary row. \remark Columns do not need to be updated when updating a temporary row. */ template template void theory_arith::add_tmp_row_entry(row & r, numeral const & coeff, theory_var v) { int r_idx; row_entry & r_entry = r.add_row_entry(r_idx); r_entry.m_var = v; r_entry.m_coeff = coeff; if (invert) r_entry.m_coeff .neg(); } /** \brief Maximize/Minimize the given variable. The bounds of v are update if procedure succeeds. */ template typename theory_arith::max_min_t theory_arith::max_min(theory_var v, bool max, bool maintain_integrality, bool& has_shared) { expr* e = get_enode(v)->get_owner(); (void)e; SASSERT(!maintain_integrality || valid_assignment()); SASSERT(satisfy_bounds()); SASSERT(!is_quasi_base(v)); if ((max && at_upper(v)) || (!max && at_lower(v))) { TRACE("opt", display_var(tout << "At " << (max?"max: ":"min: ") << mk_pp(e, get_manager()) << " \n", v);); return AT_BOUND; // nothing to be done... } m_tmp_row.reset(); if (is_non_base(v)) { add_tmp_row_entry(m_tmp_row, numeral(1), v); } else { row & r = m_rows[get_var_row(v)]; typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (!it->is_dead() && it->m_var != v) add_tmp_row_entry(m_tmp_row, it->m_coeff, it->m_var); } } max_min_t r = max_min(m_tmp_row, max, maintain_integrality, has_shared); if (r == OPTIMIZED) { TRACE("opt", tout << mk_pp(e, get_manager()) << " " << (max ? "max" : "min") << " value is: " << get_value(v) << "\n"; display_row(tout, m_tmp_row, true); display_row_info(tout, m_tmp_row);); mk_bound_from_row(v, get_value(v), max ? B_UPPER : B_LOWER, m_tmp_row); } else if (r == UNBOUNDED) { TRACE("opt", display_var(tout << "unbounded: " << mk_pp(e, get_manager()) << "\n", v);); } else { TRACE("opt", display_var(tout << "not optimized: " << mk_pp(e, get_manager()) << "\n", v);); } return r; } /** \brief Maximize & Minimize variables in vars. Return false if an inconsistency was detected. */ template bool theory_arith::max_min(svector const & vars) { bool succ = false; bool has_shared = false; svector::const_iterator it = vars.begin(); svector::const_iterator end = vars.end(); for (; it != end; ++it) { if (max_min(*it, true, false, has_shared) == OPTIMIZED && !has_shared) succ = true; if (max_min(*it, false, false, has_shared) == OPTIMIZED && !has_shared) succ = true; } if (succ) { // process new bounds bool r = propagate_core(); TRACE("opt", tout << "after max/min round:\n"; display(tout);); return r; } return true; } // ----------------------------------- // // Freedom intervals // // ----------------------------------- /** \brief See Model-based theory combination paper. Return false if failed to build the freedom interval. \remark If x_j is an integer variable, then m will contain the lcm of the denominators of a_ij. We only consider the a_ij coefficients for x_i */ template bool theory_arith::get_freedom_interval(theory_var x_j, bool & inf_l, inf_numeral & l, bool & inf_u, inf_numeral & u, numeral & m) { if (is_base(x_j)) return false; inf_numeral const & x_j_val = get_value(x_j); column & c = m_columns[x_j]; typename svector::iterator it = c.begin_entries(); typename svector::iterator end = c.end_entries(); inf_l = true; inf_u = true; l.reset(); u.reset(); m = numeral(1); #define IS_FIXED() { if (!inf_l && !inf_u && l == u) goto fi_succeeded; } #define SET_LOWER(VAL) { inf_numeral const & _VAL = VAL; if (inf_l || _VAL > l) { l = _VAL; inf_l = false; } IS_FIXED(); } #define SET_UPPER(VAL) { inf_numeral const & _VAL = VAL; if (inf_u || _VAL < u) { u = _VAL; inf_u = false; } IS_FIXED(); } if (lower(x_j)) { SET_LOWER(lower_bound(x_j)); } if (upper(x_j)) { SET_UPPER(upper_bound(x_j)); } for (; it != end; ++it) { if (!it->is_dead()) { row & r = m_rows[it->m_row_id]; theory_var x_i = r.get_base_var(); if (x_i != null_theory_var && !is_quasi_base(x_i)) { numeral const & a_ij = r[it->m_row_idx].m_coeff; inf_numeral const & x_i_val = get_value(x_i); if (is_int(x_i) && is_int(x_j) && !a_ij.is_int()) m = lcm(m, denominator(a_ij)); bound * x_i_lower = lower(x_i); bound * x_i_upper = upper(x_i); if (a_ij.is_neg()) { if (x_i_lower) { inf_numeral new_l = x_j_val + ((x_i_val - x_i_lower->get_value()) / a_ij); SET_LOWER(new_l); } if (x_i_upper) { inf_numeral new_u = x_j_val + ((x_i_val - x_i_upper->get_value()) / a_ij); SET_UPPER(new_u); } } else { if (x_i_upper) { inf_numeral new_l = x_j_val + ((x_i_val - x_i_upper->get_value()) / a_ij); SET_LOWER(new_l); } if (x_i_lower) { inf_numeral new_u = x_j_val + ((x_i_val - x_i_lower->get_value()) / a_ij); SET_UPPER(new_u); } } } } } fi_succeeded: TRACE("freedom_interval", tout << "freedom variable for:\n"; display_var(tout, x_j); tout << "["; if (inf_l) tout << "-oo"; else tout << l; tout << "; "; if (inf_u) tout << "oo"; else tout << u; tout << "]\n";); return true; } // ----------------------------------- // // Implied eqs // // ----------------------------------- /** \brief Try to check whether v1 == v2 is implied by the current state. If it is return true. */ template bool theory_arith::try_to_imply_eq(theory_var v1, theory_var v2) { SASSERT(v1 != v2); SASSERT(get_value(v1) == get_value(v2)); SASSERT(valid_assignment()); if (is_quasi_base(v1) || is_quasi_base(v2)) return false; m_tmp_row.reset(); if (is_non_base(v1)) { add_tmp_row_entry(m_tmp_row, numeral(1), v1); } else { row & r = m_rows[get_var_row(v1)]; typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (!it->is_dead() && it->m_var != v1) add_tmp_row_entry(m_tmp_row, it->m_coeff, it->m_var); } } m_tmp_row.save_var_pos(m_var_pos); #define ADD_ENTRY(COEFF, VAR) { \ int pos = m_var_pos[VAR]; \ if (pos == -1) { \ add_tmp_row_entry(m_tmp_row, COEFF, VAR); \ } \ else { \ row_entry & r_entry = m_tmp_row[pos]; \ SASSERT(r_entry.m_var == VAR); \ r_entry.m_coeff += COEFF; \ if (r_entry.m_coeff.is_zero()) \ m_tmp_row.del_row_entry(pos); \ m_var_pos[VAR] = -1; \ } \ } if (is_non_base(v2)) { ADD_ENTRY(numeral(-1), v2); } else { row & r = m_rows[get_var_row(v2)]; typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (!it->is_dead() && it->m_var != v2) { numeral c = it->m_coeff; c.neg(); ADD_ENTRY(c, it->m_var); } } } m_tmp_row.reset_var_pos(m_var_pos); SASSERT(m_tmp_row.size() > 0); #if 0 TRACE("imply_eq", display_row_info(tout, m_tmp_row);); m_tmp_acc_lits.reset(); m_tmp_acc_eqs.reset(); m_tmp_lit_set.reset(); m_tmp_eq_set.reset(); if ((OPTIMIZED == max_min(m_tmp_row, true)) && is_zero_row(m_tmp_row, true, m_tmp_acc_lits, m_tmp_acc_eqs, m_tmp_lit_set, m_tmp_eq_set) && (OPTIMIZED == max_min(m_tmp_row, false)) && is_zero_row(m_tmp_row, false, m_tmp_acc_lits, m_tmp_acc_eqs, m_tmp_lit_set, m_tmp_eq_set)) { // v1 == v2 TRACE("imply_eq", tout << "found new implied equality:\n"; display_var(tout, v1); display_var(tout, v2);); // TODO: assert implied equality // return true; } #endif return false; } // ----------------------------------- // // Assume eqs // // The revamped assume eqs try to perturbate the // current assignment using pivoting operations. // // ----------------------------------- #define RANGE 10000 /** \brief Performs a random update on v using its freedom interval. Return true if it was possible to change. */ template bool theory_arith::random_update(theory_var v) { if (is_fixed(v) || !is_non_base(v)) return false; bool inf_l, inf_u; inf_numeral l, u; numeral m; get_freedom_interval(v, inf_l, l, inf_u, u, m); if (inf_l && inf_u) { inf_numeral new_val = inf_numeral(m_random() % (RANGE + 1)); set_value(v, new_val); return true; } if (is_int(v)) { if (!inf_l) { l = ceil(l); if (!m.is_one()) l = m*ceil(l/m); } if (!inf_u) { u = floor(u); if (!m.is_one()) u = m*floor(u/m); } } if (!inf_l && !inf_u && l >= u) return false; if (inf_u) { SASSERT(!inf_l); inf_numeral delta = inf_numeral(m_random() % (RANGE + 1)); inf_numeral new_val = l + m*delta; set_value(v, new_val); return true; } if (inf_l) { SASSERT(!inf_u); inf_numeral delta = inf_numeral(m_random() % (RANGE + 1)); inf_numeral new_val = u - m*delta; set_value(v, new_val); return true; } if (!is_int(v)) { SASSERT(!inf_l && !inf_u); numeral delta = numeral(m_random() % (RANGE + 1)); inf_numeral new_val = l + ((delta * (u - l)) / numeral(RANGE)); set_value(v, new_val); return true; } else { unsigned range = RANGE; numeral r = (u.get_rational() - l.get_rational()) / m; if (r < numeral(RANGE)) range = static_cast(r.get_uint64()); inf_numeral new_val = l + m * (inf_numeral(m_random() % (range + 1))); set_value(v, new_val); return true; } } template void theory_arith::mutate_assignment() { SASSERT(m_to_patch.empty()); remove_fixed_vars_from_base(); int num_vars = get_num_vars(); m_var_value_table.reset(); m_tmp_var_set.reset(); sbuffer candidates; for (theory_var v = 0; v < num_vars; v++) { enode * n1 = get_enode(v); if (!is_relevant_and_shared(n1)) continue; theory_var other = m_var_value_table.insert_if_not_there(v); if (other == v) continue; // first node with the given value... enode * n2 = get_enode(other); if (n1->get_root() == n2->get_root()) continue; if (!is_fixed(v)) { candidates.push_back(v); } else if (!is_fixed(other) && !m_tmp_var_set.contains(other)) { m_tmp_var_set.insert(other); candidates.push_back(other); } } if (candidates.empty()) return; m_tmp_var_set.reset(); m_tmp_var_set2.reset(); for (theory_var v : candidates) { SASSERT(!is_fixed(v)); if (is_base(v)) { row & r = m_rows[get_var_row(v)]; typename vector::const_iterator it2 = r.begin_entries(); typename vector::const_iterator end2 = r.end_entries(); for (; it2 != end2; ++it2) { if (!it2->is_dead() && it2->m_var != v && !is_fixed(it2->m_var) && random_update(it2->m_var)) break; } } else { random_update(v); } } SASSERT(m_to_patch.empty()); } /** \brief We must redefine this method, because theory of arithmetic contains underspecified operators such as division by 0. (/ a b) is essentially an uninterpreted function when b = 0. Thus, 'a' must be considered a shared var if it is the child of an underspecified operator. */ template bool theory_arith::is_shared(theory_var v) const { if (!m_found_underspecified_op) { return false; } enode * n = get_enode(v); enode * r = n->get_root(); enode_vector::const_iterator it = r->begin_parents(); enode_vector::const_iterator end = r->end_parents(); TRACE("shared", tout << get_context().get_scope_level() << " " << v << " " << r->get_num_parents() << "\n";); for (; it != end; ++it) { enode * parent = *it; app * o = parent->get_owner(); if (o->get_family_id() == get_id()) { switch (o->get_decl_kind()) { case OP_DIV: case OP_IDIV: case OP_REM: case OP_MOD: return true; default: break; } } } return false; } template bool theory_arith::assume_eqs_core() { // See comment in m_liberal_final_check declaration if (m_liberal_final_check) mutate_assignment(); TRACE("assume_eq_int", display(tout);); unsigned old_sz = m_assume_eq_candidates.size(); TRACE("func_interp_bug", display(tout);); m_var_value_table.reset(); bool result = false; int num = get_num_vars(); for (theory_var v = 0; v < num; v++) { enode * n = get_enode(v); TRACE("func_interp_bug", tout << mk_pp(n->get_owner(), get_manager()) << " -> " << m_value[v] << " root #" << n->get_root()->get_owner_id() << " " << is_relevant_and_shared(n) << "\n";); if (!is_relevant_and_shared(n)) { continue; } theory_var other = null_theory_var; other = m_var_value_table.insert_if_not_there(v); if (other == v) { continue; } enode * n2 = get_enode(other); if (n->get_root() == n2->get_root()) { continue; } TRACE("func_interp_bug", tout << "adding to assume_eq queue #" << n->get_owner_id() << " #" << n2->get_owner_id() << "\n";); m_assume_eq_candidates.push_back(std::make_pair(other, v)); result = true; } if (result) get_context().push_trail(restore_size_trail, false>(m_assume_eq_candidates, old_sz)); return delayed_assume_eqs(); // return this->assume_eqs(m_var_value_table); } template bool theory_arith::delayed_assume_eqs() { if (m_assume_eq_head == m_assume_eq_candidates.size()) return false; get_context().push_trail(value_trail(m_assume_eq_head)); while (m_assume_eq_head < m_assume_eq_candidates.size()) { std::pair const & p = m_assume_eq_candidates[m_assume_eq_head]; theory_var v1 = p.first; theory_var v2 = p.second; m_assume_eq_head++; CTRACE("func_interp_bug", get_value(v1) == get_value(v2) && get_enode(v1)->get_root() != get_enode(v2)->get_root(), tout << "assuming eq: #" << get_enode(v1)->get_owner_id() << " = #" << get_enode(v2)->get_owner_id() << "\n";); if (get_value(v1) == get_value(v2) && get_enode(v1)->get_root() != get_enode(v2)->get_root() && assume_eq(get_enode(v1), get_enode(v2))) { return true; } } return false; } #if 0 /** \brief Check if the given row is implying a zero upper/lower bound. Accumulate the justification in the given vectors. Return true if it is. */ template bool theory_arith::is_zero_row(row const & r, bool upper, literal_vector & lit_vect, eq_vector & eq_vect, literal_idx_set & lits, eq_set & eqs) { inf_numeral val; typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (!it->is_dead()) { theory_var v = it->m_var; bool use_upper = upper; if (!it->m_coeff.is_pos()) use_upper = !use_upper; bound * b = get_bound(v, use_upper); inf_numeral tmp = b->get_value(); tmp *= it->m_coeff; val += tmp; SASSERT(b); acc_umulate_justification(*b, lit_vect, eq_vect, lits, eqs); } } return val.is_zero(); } #endif }; #endif /* THEORY_ARITH_AUX_H_ */ z3-z3-4.8.7/src/smt/theory_arith_core.h000066400000000000000000004271331356505360400177150ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_arith_core.h Abstract: Author: Leonardo de Moura (leonardo) 2008-04-22. Revision History: --*/ #ifndef THEORY_ARITH_CORE_H_ #define THEORY_ARITH_CORE_H_ #include "ast/ast_pp.h" #include "ast/ast_ll_pp.h" #include "ast/ast_smt2_pp.h" #include "smt/smt_context.h" #include "smt/theory_arith.h" #include "smt/smt_model_generator.h" namespace smt { template void theory_arith::found_unsupported_op(app * n) { if (!m_found_unsupported_op) { TRACE("arith", tout << "found non supported expression:\n" << mk_pp(n, get_manager()) << "\n";); get_context().push_trail(value_trail(m_found_unsupported_op)); m_found_unsupported_op = true; } } template void theory_arith::found_underspecified_op(app * n) { context& ctx = get_context(); m_underspecified_ops.push_back(n); ctx.push_trail(push_back_vector>(m_underspecified_ops)); if (!m_found_underspecified_op) { TRACE("arith", tout << "found underspecified expression:\n" << mk_pp(n, get_manager()) << "\n";); ctx.push_trail(value_trail(m_found_underspecified_op)); m_found_underspecified_op = true; } expr* e = nullptr; if (m_util.is_div(n)) { e = m_util.mk_div0(n->get_arg(0), n->get_arg(1)); } else if (m_util.is_idiv(n)) { e = m_util.mk_idiv0(n->get_arg(0), n->get_arg(1)); } else if (m_util.is_rem(n)) { e = m_util.mk_rem0(n->get_arg(0), n->get_arg(1)); } else if (m_util.is_mod(n)) { e = m_util.mk_mod0(n->get_arg(0), n->get_arg(1)); } else if (m_util.is_power(n)) { e = m_util.mk_power0(n->get_arg(0), n->get_arg(1)); } if (e) { ctx.assign(mk_eq(e, n, false), nullptr); } } template bool theory_arith::process_atoms() const { if (!adaptive()) return true; unsigned total_conflicts = get_context().get_num_conflicts(); if (total_conflicts < 10) return true; double f = static_cast(get_num_conflicts())/static_cast(total_conflicts); TRACE_CODE({ static unsigned counter = 0; counter++; if (counter % 1000 == 0) { TRACE("arith_adaptive", tout << "arith_conflicts: " << get_num_conflicts() << " total_conflicts: " << total_conflicts << " factor: " << f << "\n";); } }); return f >= adaptive_assertion_threshold(); } template bool theory_arith::is_int_expr(expr* e) { #if 0 return m_util.is_int(e); #else return m_util.is_int_expr(e); #endif } template theory_var theory_arith::mk_var(enode * n) { theory_var r = theory::mk_var(n); SASSERT(r == static_cast(m_columns.size())); SASSERT(check_vector_sizes()); bool is_int = is_int_expr(n->get_owner()); TRACE("mk_arith_var", tout << mk_pp(n->get_owner(), get_manager()) << " is_int: " << is_int << "\n";); m_columns .push_back(column()); m_data .push_back(var_data(is_int)); if (random_initial_value()) { unsigned val = (m_random()%(random_upper() - random_lower())) + random_lower(); m_value .push_back(inf_numeral(val)); } else { m_value .push_back(inf_numeral()); } m_old_value .push_back(inf_numeral()); SASSERT(m_var_occs.size() == static_cast(r)); m_var_occs .push_back(atoms()); SASSERT(m_var_occs.back().empty()); m_unassigned_atoms .push_back(0); m_var_pos .push_back(-1); m_bounds[0] .push_back(0); m_bounds[1] .push_back(0); if (r >= static_cast(m_to_patch.get_bounds())) m_to_patch.set_bounds(r + 1); m_in_update_trail_stack.assure_domain(r); m_left_basis.assure_domain(r); m_in_to_check.assure_domain(r); if (is_pure_monomial(n->get_owner())) m_nl_monomials.push_back(r); SASSERT(check_vector_sizes()); SASSERT(m_var_occs[r].empty()); TRACE("mk_arith_var", tout << "#" << n->get_owner_id() << " :=\n" << mk_ll_pp(n->get_owner(), get_manager()) << "\n"; tout << "is_attached_to_var: " << is_attached_to_var(n) << ", var: " << n->get_th_var(get_id()) << "\n";); get_context().attach_th_var(n, this, r); SASSERT(m_var_occs.back().empty()); return r; } template inline bool theory_arith::reflection_enabled() const { return m_params.m_arith_reflect; } template inline bool theory_arith::reflect(app * n) const { if (reflection_enabled()) return true; // reflect everything // Every underspecified operator must be reflected in the egraph. // Although it is underspecified, it is still a function. // For example, a/b != a/0 then we must have b != 0 // I use the Egraph to enforce that. if (n->get_family_id() == get_id()) { switch (n->get_decl_kind()) { case OP_DIV: case OP_IDIV: case OP_REM: case OP_MOD: return true; default: break; } } return false; } template inline bool theory_arith::enable_cgc_for(app * n) const { // Congruence closure is not enabled for (+ ...) and (* ...) applications. return !(n->get_family_id() == get_id() && (n->get_decl_kind() == OP_ADD || n->get_decl_kind() == OP_MUL)); } /** \brief Create an enode for n. */ template enode * theory_arith::mk_enode(app * n) { context & ctx = get_context(); if (ctx.e_internalized(n)) return ctx.get_enode(n); else return ctx.mk_enode(n, !reflect(n), false, enable_cgc_for(n)); } /** \brief Create an enode for n if reflection is enabled. */ template void theory_arith::mk_enode_if_reflect(app * n) { if (reflection_enabled()) { // make sure that n is in the e-graph mk_enode(n); } } /** \brief access the current set of variables associated with row. */ template uint_set& theory_arith::row_vars() { SASSERT(m_row_vars_top > 0); return m_row_vars[m_row_vars_top-1]; } /** \brief Add coeff * v to the row r. The column is also updated. */ template template void theory_arith::add_row_entry(unsigned r_id, numeral const & coeff, theory_var v) { row & r = m_rows[r_id]; column & c = m_columns[v]; if (row_vars().contains(v)) { typename vector::iterator it = r.begin_entries(); typename vector::iterator end = r.end_entries(); bool found = false; for (; !found && it != end; ++it) { SASSERT(!it->is_dead()); if (it->m_var == v) { if (invert) { it->m_coeff -= coeff; } else { it->m_coeff += coeff; } found = true; } } SASSERT(found); return; } row_vars().insert(v); int r_idx; row_entry & r_entry = r.add_row_entry(r_idx); int c_idx; col_entry & c_entry = c.add_col_entry(c_idx); r_entry.m_var = v; r_entry.m_coeff = coeff; if (invert) r_entry.m_coeff .neg(); r_entry.m_col_idx = c_idx; c_entry.m_row_id = r_id; c_entry.m_row_idx = r_idx; } /** \brief Internalize the monomial of a polynomial. Store the monomial in the given row. The monomial is negated before being inserted into the row. */ template void theory_arith::internalize_internal_monomial(app * m, unsigned r_id) { context & ctx = get_context(); if (ctx.e_internalized(m)) { enode * e = ctx.get_enode(m); if (is_attached_to_var(e)) { // there is already a theory variable (i.e., name) for m. theory_var v = e->get_th_var(get_id()); add_row_entry(r_id, numeral::minus_one(), v); return; } } rational _val1, _val2; expr* arg1, *arg2; if (m_util.is_mul(m, arg1, arg2) && m_util.is_numeral(arg1, _val1) && is_app(arg1) && is_app(arg2)) { SASSERT(m->get_num_args() == 2); if (m_util.is_numeral(arg2, _val2)) { numeral val(_val1 + _val2); if (reflection_enabled()) { internalize_term_core(to_app(arg1)); internalize_term_core(to_app(arg2)); mk_enode(m); } theory_var v = internalize_numeral(m, val); add_row_entry(r_id, numeral::one(), v); return; } numeral val(_val1); theory_var v = internalize_term_core(to_app(arg2)); if (reflection_enabled()) { internalize_term_core(to_app(arg1)); mk_enode(m); } add_row_entry(r_id, val, v); } else { theory_var v = internalize_term_core(m); add_row_entry(r_id, numeral::minus_one(), v); } } /** \brief Internalize a polynomial (+ h t). Return an alias for the monomial, that is, a variable v such that v = (+ h t) is a new row in the tableau. */ template theory_var theory_arith::internalize_add(app * n) { TRACE("add_bug", tout << "n: " << mk_pp(n, get_manager()) << "\n";); CTRACE("internalize_add_bug", n->get_num_args() == 2 && n->get_arg(0) == n->get_arg(1), tout << "n: " << mk_pp(n, get_manager()) << "\n";); SASSERT(m_util.is_add(n)); unsigned r_id = mk_row(); scoped_row_vars _sc(m_row_vars, m_row_vars_top); for (expr* arg : *n) { if (is_var(arg)) { std::ostringstream strm; strm << mk_pp(n, get_manager()) << " contains a free variable"; throw default_exception(strm.str()); } internalize_internal_monomial(to_app(arg), r_id); } enode * e = mk_enode(n); theory_var v = e->get_th_var(get_id()); if (v == null_theory_var) { v = mk_var(e); add_row_entry(r_id, numeral::one(), v); init_row(r_id); } else { // HACK: n was already internalized by the internalize_internal_monomial or internalize_internal_add call above. // This can happen when one of calls invoke (indirectly) mk_axiom. // For example, they contain a nested to_int(t) term. // TODO: reimplement mk_axiom. The current implementation is flaky. // I should cache the axioms that need to be created. They should only be internalized after we finished internalizing the // current atom. Several other theories have similar problems. del_row(r_id); } return v; } /** \brief Internalize a term (* x y z) that does not contain a coefficient (numeral). */ template theory_var theory_arith::internalize_mul_core(app * m) { TRACE("internalize_mul_core", tout << "internalizing...\n" << mk_pp(m,get_manager()) << "\n";); if (!m_util.is_mul(m)) return internalize_term_core(m); for (expr* arg : *m) { theory_var v = internalize_term_core(to_app(arg)); if (v == null_theory_var) { mk_var(mk_enode(to_app(arg))); } } enode * e = mk_enode(m); theory_var v = e->get_th_var(get_id()); if (v == null_theory_var) { v = mk_var(e); } return v; } /** \brief Internalize the terms of the form (* c (* t1 ... tn)) and (* t1 ... tn). Return an alias for the term. */ template theory_var theory_arith::internalize_mul(app * m) { rational _val; TRACE("arith", tout << m->get_num_args() << " " << mk_pp(m, get_manager()) << "\n";); SASSERT(m_util.is_mul(m)); expr* arg0 = m->get_arg(0); expr* arg1 = m->get_arg(1); if (m_util.is_numeral(arg1)) { std::swap(arg0, arg1); } if (m_util.is_numeral(arg0, _val) && !m_util.is_numeral(arg1) && m->get_num_args() == 2) { numeral val(_val); if (_val.is_zero()) { return internalize_numeral(m, val); } unsigned r_id = mk_row(); scoped_row_vars _sc(m_row_vars, m_row_vars_top); if (is_var(arg1)) { std::ostringstream strm; strm << mk_pp(m, get_manager()) << " contains a free variable"; throw default_exception(strm.str()); } if (reflection_enabled()) internalize_term_core(to_app(arg0)); theory_var v = internalize_mul_core(to_app(arg1)); add_row_entry(r_id, val, v); enode * e = mk_enode(m); theory_var s = mk_var(e); add_row_entry(r_id, numeral::one(), s); init_row(r_id); return s; } else { return internalize_mul_core(m); } } template theory_var theory_arith::mk_binary_op(app * n) { SASSERT(n->get_num_args() == 2); context & ctx = get_context(); if (ctx.e_internalized(n)) return expr2var(n); ctx.internalize(n->get_arg(0), false); ctx.internalize(n->get_arg(1), false); enode * e = mk_enode(n); return mk_var(e); } template theory_var theory_arith::internalize_div(app * n) { rational r(1); theory_var s = mk_binary_op(n); context & ctx = get_context(); if (!m_util.is_numeral(n->get_arg(1), r) || r.is_zero()) found_underspecified_op(n); if (!ctx.relevancy()) mk_div_axiom(n->get_arg(0), n->get_arg(1)); return s; } template theory_var theory_arith::internalize_idiv(app * n) { rational r; theory_var s = mk_binary_op(n); context & ctx = get_context(); if (!m_util.is_numeral(n->get_arg(1), r) || r.is_zero()) found_underspecified_op(n); app * mod = m_util.mk_mod(n->get_arg(0), n->get_arg(1)); ctx.internalize(mod, false); if (ctx.relevancy()) ctx.add_relevancy_dependency(n, mod); return s; } template theory_var theory_arith::internalize_mod(app * n) { TRACE("arith_mod", tout << "internalizing...\n" << mk_pp(n, get_manager()) << "\n";); rational r(1); theory_var s = mk_binary_op(n); context & ctx = get_context(); if (!m_util.is_numeral(n->get_arg(1), r) || r.is_zero()) found_underspecified_op(n); if (!ctx.relevancy()) mk_idiv_mod_axioms(n->get_arg(0), n->get_arg(1)); return s; } template theory_var theory_arith::internalize_rem(app * n) { rational r(1); theory_var s = mk_binary_op(n); context & ctx = get_context(); if (!m_util.is_numeral(n->get_arg(1), r) || r.is_zero()) found_underspecified_op(n); if (!ctx.relevancy()) { mk_rem_axiom(n->get_arg(0), n->get_arg(1)); } return s; } template void theory_arith::mk_axiom(expr * ante, expr * conseq, bool simplify_conseq) { ast_manager & m = get_manager(); context & ctx = get_context(); th_rewriter & s = ctx.get_rewriter(); expr_ref s_ante(m), s_conseq(m); expr* s_conseq_n, * s_ante_n; bool negated; s(ante, s_ante); if (ctx.get_cancel_flag()) return; negated = m.is_not(s_ante, s_ante_n); if (negated) s_ante = s_ante_n; ctx.internalize(s_ante, false); literal l_ante = ctx.get_literal(s_ante); if (negated) l_ante.neg(); s_conseq = conseq; if (simplify_conseq) s(conseq, s_conseq); if (ctx.get_cancel_flag()) return; negated = m.is_not(s_conseq, s_conseq_n); if (negated) s_conseq = s_conseq_n; ctx.internalize(s_conseq, false); literal l_conseq = ctx.get_literal(s_conseq); if (negated) l_conseq.neg(); TRACE("arith_axiom", tout << mk_pp(ante, m) << "\n" << mk_pp(conseq, m) << "\n"; tout << s_ante << "\n" << s_conseq << "\n"; tout << l_ante << "\n" << l_conseq << "\n";); // literal lits[2] = {l_ante, l_conseq}; if (m.has_trace_stream()) { app_ref body(m); body = m.mk_or(ante, conseq); log_axiom_instantiation(body); } mk_clause(l_ante, l_conseq, 0, nullptr); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; if (ctx.relevancy()) { if (l_ante == false_literal) { ctx.mark_as_relevant(l_conseq); } else { // We must mark the antecedent as relevant, otherwise the // core will not propagate it to the theory of arithmetic. // In a previous version, we were not doing that. // The core was assigning it to true, this assignment was inconsistent with // the state of the theory of arithmetic, but the conflict was not detected // because it was not propagated to this theory. ctx.mark_as_relevant(l_ante); ctx.add_rel_watch(~l_ante, s_conseq); // mark consequent as relevant if antecedent is false. } } } template void theory_arith::mk_div_axiom(expr * p, expr * q) { if (!m_util.is_zero(q)) { ast_manager & m = get_manager(); expr_ref div(m), zero(m), eqz(m), eq(m); TRACE("div_axiom_bug", tout << "expanding div_axiom for: " << mk_pp(p, m) << " / " << mk_pp(q, m) << "\n";); div = m_util.mk_div(p, q); zero = m_util.mk_numeral(rational(0), false); eqz = m.mk_eq(q, zero); eq = m.mk_eq(m_util.mk_mul(q, div), p); TRACE("div_axiom_bug", tout << "eqz: " << mk_pp(eqz, m) << "\neq: " << mk_pp(eq, m) << "\n";); mk_axiom(eqz, eq); } } 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); div = m_util.mk_idiv(dividend, divisor); mod = m_util.mk_mod(dividend, divisor); zero = m_util.mk_int(0); one = m_util.mk_int(1); abs_divisor = m_util.mk_sub(m.mk_ite(m_util.mk_lt(divisor, zero), m_util.mk_sub(zero, divisor), divisor), one); s(abs_divisor); eqz = m.mk_eq(divisor, zero); eq = m.mk_eq(m_util.mk_add(m_util.mk_mul(divisor, div), mod), dividend); lower = m_util.mk_ge(mod, zero); upper = m_util.mk_le(mod, abs_divisor); TRACE("div_axiom_bug", tout << "eqz: " << eqz << " neq: " << eq << "\n"; tout << "lower: " << lower << "\n"; tout << "upper: " << upper << "\n";); mk_axiom(eqz, eq, false); mk_axiom(eqz, lower, false); 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)) { rational j(0); #if 1 literal_buffer lits; expr_ref mod_j(m); while(j < k) { mod_j = m.mk_eq(mod, m_util.mk_numeral(j, true)); if (m.has_trace_stream()) log_axiom_instantiation(mod_j); ctx.internalize(mod_j, false); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; literal lit(ctx.get_literal(mod_j)); lits.push_back(lit); ctx.mark_as_relevant(lit); j += rational(1); } ctx.mk_th_axiom(get_id(), lits.size(), lits.begin()); #else // performs slightly worse. literal_buffer lits; expr_ref mod_j(m), div_j(m), num_j(m), n_mod_j(m), n_div_j(m); context& ctx = get_context(); while(j < k) { num_j = m_util.mk_numeral(j, true); mod_j = m.mk_eq(mod, num_j); div_j = m.mk_eq(dividend, m_util.mk_add(m_util.mk_mul(div, divisor), num_j)); n_mod_j = m.mk_not(mod_j); n_div_j = m.mk_not(div_j); mk_axiom(n_mod_j, div_j); mk_axiom(n_div_j, mod_j); j += rational(1); } #endif } #if 0 // e-matching is too restrictive for multiplication. // also suffers from use-after free so formulas have to be pinned in solver. // if (!m_util.is_numeral(divisor)) { // // forall x . (or (= y 0) (= (div (* x y) y) x)) // forall x . (=> (= y 0) (= (div (* x y) y) (div 0 0))) // sort* intS = m_util.mk_int(); var_ref v(m.mk_var(0, intS), m); app_ref mul(m_util.mk_mul(divisor, v), m); app_ref div(m_util.mk_idiv(mul, divisor), m); expr_ref divp1(m.mk_pattern(div), m); app_ref mul2(m_util.mk_mul(v, divisor), m); app_ref div2(m_util.mk_idiv(mul2, divisor), m); expr_ref divp2(m.mk_pattern(div2), m); expr_ref fml1(m.mk_or(m.mk_not(eqz), m.mk_eq(div, m_util.mk_idiv(zero, zero))), m); expr_ref fml2(m.mk_or(eqz, m.mk_eq(div, v)), m); symbol name("?x"); expr* pats[2] = { divp1, divp2 }; expr_ref fml(m); fml = m.mk_forall(1, &intS, &name, fml1, 0, symbol::null, symbol::null, 2, pats, 0, nullptr); proof_ref pr(m.mk_asserted(fml), m); ctx.internalize_assertion(fml, pr, 0); fml = m.mk_forall(1, &intS, &name, fml2, 0, symbol::null, symbol::null, 2, pats, 0, nullptr); pr = m.mk_asserted(fml); ctx.internalize_assertion(fml, pr, 0); } #endif } } template void theory_arith::mk_rem_axiom(expr * dividend, expr * divisor) { // if divisor is zero, then rem is an uninterpreted function. ast_manager & m = get_manager(); expr * zero = m_util.mk_numeral(rational(0), true); expr * rem = m_util.mk_rem(dividend, divisor); expr * mod = m_util.mk_mod(dividend, divisor); expr_ref dltz(m), eq1(m), eq2(m); dltz = m_util.mk_lt(divisor, zero); eq1 = m.mk_eq(rem, mod); eq2 = m.mk_eq(rem, m_util.mk_sub(zero, mod)); // n < 0 || rem(a,n) = mod(a, n) mk_axiom(dltz, eq1); dltz = m.mk_not(dltz); // !n < 0 || rem(a,n) = -mod(a, n) mk_axiom(dltz, eq2); } // // create the term: s := x - to_real(to_int(x)) // add the bounds 0 <= s < 1 // template void theory_arith::mk_to_int_axiom(app * n) { SASSERT(m_util.is_to_int(n)); ast_manager & m = get_manager(); expr* x = n->get_arg(0); // to_int (to_real x) = x if (m_util.is_to_real(x)) { mk_axiom(m.mk_false(), m.mk_eq(to_app(x)->get_arg(0), n)); return; } expr_ref to_r(m_util.mk_to_real(n), m); expr_ref diff(m_util.mk_add(x, m_util.mk_mul(m_util.mk_real(-1), to_r)), m); expr_ref lo(m_util.mk_ge(diff, m_util.mk_real(0)), m); expr_ref hi(m_util.mk_ge(diff, m_util.mk_real(1)), m); hi = m.mk_not(hi); mk_axiom(m.mk_false(), lo, false); mk_axiom(m.mk_false(), hi, false); } template theory_var theory_arith::internalize_to_int(app * n) { SASSERT(n->get_num_args() == 1); context & ctx = get_context(); if (ctx.e_internalized(n)) return expr2var(n); /* theory_var arg = */ internalize_term_core(to_app(n->get_arg(0))); enode * e = mk_enode(n); theory_var r = mk_var(e); if (!ctx.relevancy()) mk_to_int_axiom(n); return r; } // // Create the axiom (iff (is_int x) (= x (to_real (to_int x)))) // template void theory_arith::mk_is_int_axiom(app * n) { SASSERT(m_util.is_is_int(n)); ast_manager & m = get_manager(); expr* x = n->get_arg(0); expr* eq = m.mk_eq(m_util.mk_to_real(m_util.mk_to_int(x)), x); mk_axiom(m.mk_not(n), eq); mk_axiom(m.mk_not(eq), n); } template void theory_arith::internalize_is_int(app * n) { SASSERT(n->get_num_args() == 1); context & ctx = get_context(); if (ctx.b_internalized(n)) return; /* theory_var arg = */ internalize_term_core(to_app(n->get_arg(0))); enode * e = mk_enode(n); /* theory_var r = */ mk_var(e); if (!ctx.relevancy()) mk_is_int_axiom(n); } // create the row: r - arg = 0 template theory_var theory_arith::internalize_to_real(app * n) { SASSERT(n->get_num_args() == 1); context & ctx = get_context(); if (ctx.e_internalized(n)) return expr2var(n); TRACE("to_real_bug", tout << "to-real\n" << mk_ismt2_pp(n, get_manager()) << "\n";); theory_var arg = internalize_term_core(to_app(n->get_arg(0))); // n may be internalized by the call above if n is of the form (to_real (to_int t)) // The internalizer for (to_int t) will create (to_real (to_int t)) and internalize it. // This is a recurrent bug in Z3. TODO: I should create a queue of axioms that need to be asserted. // This queue is processes only after we finish the internalization of the current assertion. if (ctx.e_internalized(n)) return expr2var(n); enode * e = mk_enode(n); theory_var r = mk_var(e); unsigned r_id = mk_row(); scoped_row_vars _sc(m_row_vars, m_row_vars_top); add_row_entry(r_id, numeral(1), arg); add_row_entry(r_id, numeral(1), r); init_row(r_id); return r; } template theory_var theory_arith::internalize_numeral(app * n) { rational _val; VERIFY(m_util.is_numeral(n, _val)); numeral val(_val); return internalize_numeral(n, val); } template theory_var theory_arith::internalize_numeral(app * n, numeral const& val) { context& ctx = get_context(); if (ctx.e_internalized(n)) { return mk_var(ctx.get_enode(n)); } enode * e = mk_enode(n); // internalizer is marking enodes as interpreted whenever the associated ast is a value and a constant. // e->mark_as_interpreted(); theory_var v = mk_var(e); inf_numeral ival(val); bound * l = alloc(bound, v, ival, B_LOWER, false); bound * u = alloc(bound, v, ival, B_UPPER, false); set_bound(l, false); set_bound(u, true); m_bounds_to_delete.push_back(l); m_bounds_to_delete.push_back(u); m_value[v] = ival; return v; } template class theory_arith::scoped_row_vars { unsigned& m_top; public: scoped_row_vars(vector& row_vars, unsigned& top): m_top(top) { SASSERT(row_vars.size() >= top); if (row_vars.size() == top) { row_vars.push_back(uint_set()); } row_vars[top].reset(); ++m_top; } ~scoped_row_vars() { --m_top; } }; /** \brief Internalize the given term and return an alias for it. Return null_theory_var if the term was not implemented by the theory yet. */ template theory_var theory_arith::internalize_term_core(app * n) { TRACE("arith_internalize_detail", tout << "internalize_term_core:\n" << mk_pp(n, get_manager()) << "\n";); context & ctx = get_context(); if (ctx.e_internalized(n)) { enode * e = ctx.get_enode(n); if (is_attached_to_var(e)) return e->get_th_var(get_id()); } SASSERT(!m_util.is_sub(n)); SASSERT(!m_util.is_uminus(n)); if (m_util.is_add(n)) return internalize_add(n); else if (m_util.is_mul(n)) return internalize_mul(n); else if (m_util.is_div(n)) return internalize_div(n); else if (m_util.is_idiv(n)) return internalize_idiv(n); else if (m_util.is_mod(n)) return internalize_mod(n); else if (m_util.is_rem(n)) return internalize_rem(n); else if (m_util.is_to_real(n)) return internalize_to_real(n); else if (m_util.is_to_int(n)) return internalize_to_int(n); else if (m_util.is_numeral(n)) return internalize_numeral(n); if (m_util.is_power(n)) { // unsupported found_unsupported_op(n); return mk_binary_op(n); } if (m_util.is_irrational_algebraic_numeral(n)) { // unsupported found_unsupported_op(n); enode * e = mk_enode(n); return mk_var(e); } if (m_util.get_family_id() == n->get_family_id()) { found_unsupported_op(n); if (ctx.e_internalized(n)) return expr2var(n); for (unsigned i = 0; i < n->get_num_args(); ++i) { ctx.internalize(n->get_arg(i), false); } return mk_var(mk_enode(n)); } TRACE("arith_internalize_detail", tout << "before:\n" << mk_pp(n, get_manager()) << "\n";); if (!ctx.e_internalized(n)) ctx.internalize(n, false); TRACE("arith_internalize_detail", tout << "after:\n" << mk_pp(n, get_manager()) << "\n";); enode * e = ctx.get_enode(n); if (!is_attached_to_var(e)) return mk_var(e); else return e->get_th_var(get_id()); } /** \brief Create a new empty row. Return the new row id. */ template unsigned theory_arith::mk_row() { unsigned r; if (m_dead_rows.empty()) { r = m_rows.size(); m_rows.push_back(row()); } else { r = m_dead_rows.back(); m_dead_rows.pop_back(); } m_in_to_check.assure_domain(r); SASSERT(m_rows[r].size() == 0); SASSERT(m_rows[r].num_entries() == 0); return r; } /** \brief Initialize a new row, the last monomial is going to be the owner of the row. The last monomial must have coeff 1. */ template void theory_arith::init_row(unsigned r_id) { row & r = m_rows[r_id]; SASSERT(r.m_first_free_idx == -1); SASSERT(r.size() != 0); SASSERT(r.size() == r.num_entries()); SASSERT(r[r.size() - 1].m_coeff.is_one()); theory_var s = r[r.size() - 1].m_var; r.m_base_var = s; set_var_row(s, r_id); TRACE("init_row_bug", tout << "before:\n"; display_row_info(tout, r);); if (lazy_pivoting_lvl() > 2) { set_var_kind(s, QUASI_BASE); normalize_quasi_base_row(r_id); SASSERT(!has_var_kind(get_var_row(s), QUASI_BASE)); } else { normalize_base_row(r_id); SASSERT(get_var_kind(s) == BASE); SASSERT(!has_var_kind(get_var_row(s), BASE)); } TRACE("init_row_bug", tout << "after:\n"; display_row_info(tout, r);); if (propagation_mode() != BP_NONE) mark_row_for_bound_prop(r_id); SASSERT(r.is_coeff_of(s, numeral::one())); SASSERT(wf_row(r_id)); } /** \brief Collect variables in the given row that have the given kind, but a different from the row main var (i.e., var that owns the row). The inv of the coefficients is also stored in result */ template void theory_arith::collect_vars(unsigned r_id, var_kind k, buffer & result) { row & r = m_rows[r_id]; theory_var base = r.m_base_var; typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); unsigned idx = 0; for (; it != end; ++it, ++idx) { if (!it->is_dead() && get_var_kind(it->m_var) == k && it->m_var != base) { numeral c = it->m_coeff; c.neg(); result.push_back(linear_monomial(c, it->m_var)); } } } /** \brief Normalize row as a quasi base row, it does not contain quasi-base variables different from r.m_base_var. */ template void theory_arith::normalize_quasi_base_row(unsigned r_id) { buffer to_add; collect_vars(r_id, QUASI_BASE, to_add); add_rows(r_id, to_add.size(), to_add.c_ptr()); SASSERT(!has_var_kind(r_id, QUASI_BASE)); } /** \brief Convert a quasi-base row into a base row. */ template void theory_arith::quasi_base_row2base_row(unsigned r_id) { TRACE("quasi_base_row2base_row", tout << "quasi_base_row2base_row...\n";); buffer to_add; collect_vars(r_id, BASE, to_add); TRACE("quasi_base_bug_detail", display_row_info(tout, r_id); for (unsigned i = 0; i < to_add.size(); i++) { theory_var v = to_add[i].m_var; SASSERT(is_base(v)); SASSERT(!has_var_kind(get_var_row(v), BASE)); tout << "coeff: " << to_add[i].m_coeff << ", var: #" << get_enode(v)->get_owner_id() << "\n"; display_row_info(tout, get_var_row(v)); tout << "\n"; }); add_rows(r_id, to_add.size(), to_add.c_ptr()); theory_var s = m_rows[r_id].get_base_var(); set_var_kind(s, BASE); inf_numeral tmp; if (get_implied_old_value(s, tmp)) { // This code is necessary because of the method // restore_assignment. That is, the invariant // valid_row_assignment() could be invalidated when // restore_assignment is executed. // // Remark: The method restore_assignment will restore the // old value of variables, and the value of s should be // compatible with them. // // For example, consider the following scenario: // // 1) s is a quasi-base var, s depends on x, and value of x is v0 // // 2) x is updated to v1, but the update does not affect s (s is a quasi-base var). // // 3) quasi_base_row2base_row is executed, and we compute the value of s using // the current value of x (v1). // // 4) a conflict is detected, and the value of x is restored to v0. // // 5) if this branch is deleted, the row owned by s will not satisfy // valid_row_assignment. // m_value[s] = tmp; SASSERT(!m_in_update_trail_stack.contains(s)); save_value(s); } m_value[s] = get_implied_value(s); TRACE("valid_row_assignment_bug", display_row_info(tout, r_id);); SASSERT(!has_var_kind(r_id, BASE)); SASSERT(!has_var_kind(r_id, QUASI_BASE)); SASSERT(valid_row_assignment(m_rows[r_id])); } /** \brief Normalize row as a base row. A base row does not contain quasi-base and base variables different from r.m_base_var. */ template void theory_arith::normalize_base_row(unsigned r_id) { if (lazy_pivoting_lvl() > 0) normalize_quasi_base_row(r_id); quasi_base_row2base_row(r_id); } template void theory_arith::mk_clause(literal l1, literal l2, unsigned num_params, parameter * params) { TRACE("arith", literal lits[2]; lits[0] = l1; lits[1] = l2; get_context().display_literals_verbose(tout, 2, lits); tout << "\n";); get_context().mk_th_axiom(get_id(), l1, l2, num_params, params); } template void theory_arith::mk_clause(literal l1, literal l2, literal l3, unsigned num_params, parameter * params) { TRACE("arith", literal lits[3]; lits[0] = l1; lits[1] = l2; lits[2] = l3; get_context().display_literals_verbose(tout, 3, lits); tout << "\n";); get_context().mk_th_axiom(get_id(), l1, l2, l3, num_params, params); } template void theory_arith::mk_bound_axioms(atom * a1) { theory_var v = a1->get_var(); atoms & occs = m_var_occs[v]; TRACE("mk_bound_axioms", tout << "add bound axioms for v" << v << " " << a1 << "\n";); if (!get_context().is_searching()) { // // NB. We make an assumption that user push calls propagation // before internal scopes are pushed. This flushes all newly // asserted atoms into the right context. // m_new_atoms.push_back(a1); return; } inf_numeral const & k1(a1->get_k()); atom_kind kind1 = a1->get_atom_kind(); TRACE("mk_bound_axioms", display_atom(tout << "making bound axioms for " << a1 << " ", a1, true); tout << "\n";); typename atoms::iterator it = occs.begin(); typename atoms::iterator end = occs.end(); typename atoms::iterator lo_inf = end, lo_sup = end; typename atoms::iterator hi_inf = end, hi_sup = end; for (; it != end; ++it) { atom * a2 = *it; inf_numeral const & k2(a2->get_k()); atom_kind kind2 = a2->get_atom_kind(); TRACE("mk_bound_axioms", display_atom(tout << "compare " << a2 << " ", a2, true); tout << "\n";); if (k1 == k2 && kind1 == kind2) { continue; } SASSERT(k1 != k2 || kind1 != kind2); if (kind2 == A_LOWER) { if (k2 < k1) { if (lo_inf == end || k2 > (*lo_inf)->get_k()) { lo_inf = it; } } else if (lo_sup == end || k2 < (*lo_sup)->get_k()) { lo_sup = it; } } else if (k2 < k1) { if (hi_inf == end || k2 > (*hi_inf)->get_k()) { hi_inf = it; } } else if (hi_sup == end || k2 < (*hi_sup)->get_k()) { hi_sup = it; } } if (lo_inf != end) mk_bound_axiom(a1, *lo_inf); if (lo_sup != end) mk_bound_axiom(a1, *lo_sup); if (hi_inf != end) mk_bound_axiom(a1, *hi_inf); if (hi_sup != end) mk_bound_axiom(a1, *hi_sup); } template void theory_arith::mk_bound_axiom(atom* a1, atom* a2) { TRACE("mk_bound_axioms", tout << a1 << " " << a2 << "\n";); theory_var v = a1->get_var(); literal l1(a1->get_bool_var()); literal l2(a2->get_bool_var()); inf_numeral const & k1(a1->get_k()); inf_numeral const & k2(a2->get_k()); atom_kind kind1 = a1->get_atom_kind(); atom_kind kind2 = a2->get_atom_kind(); bool v_is_int = is_int(v); SASSERT(v == a2->get_var()); if (k1 == k2 && kind1 == kind2) return; SASSERT(k1 != k2 || kind1 != kind2); parameter coeffs[3] = { parameter(symbol("farkas")), parameter(rational(1)), parameter(rational(1)) }; if (kind1 == A_LOWER) { if (kind2 == A_LOWER) { if (k2 <= k1) { mk_clause(~l1, l2, 3, coeffs); } else { mk_clause(l1, ~l2, 3, coeffs); } } else if (k1 <= k2) { // k1 <= k2, k1 <= x or x <= k2 mk_clause(l1, l2, 3, coeffs); } else { // k1 > hi_inf, k1 <= x => ~(x <= hi_inf) mk_clause(~l1, ~l2, 3, coeffs); if (v_is_int && k1 == k2 + inf_numeral(1)) { // k1 <= x or x <= k1-1 mk_clause(l1, l2, 3, coeffs); } } } else if (kind2 == A_LOWER) { if (k1 >= k2) { // k1 >= lo_inf, k1 >= x or lo_inf <= x mk_clause(l1, l2, 3, coeffs); } else { // k1 < k2, k2 <= x => ~(x <= k1) mk_clause(~l1, ~l2, 3, coeffs); if (v_is_int && k1 == k2 - inf_numeral(1)) { // x <= k1 or k1+l <= x mk_clause(l1, l2, 3, coeffs); } } } else { // kind1 == A_UPPER, kind2 == A_UPPER if (k1 >= k2) { // k1 >= k2, x <= k2 => x <= k1 mk_clause(l1, ~l2, 3, coeffs); } else { // k1 <= hi_sup , x <= k1 => x <= hi_sup mk_clause(~l1, l2, 3, coeffs); } } } template void theory_arith::flush_bound_axioms() { CTRACE("arith_verbose", !m_new_atoms.empty(), tout << "flush bound axioms\n";); while (!m_new_atoms.empty()) { ptr_vector atoms; atoms.push_back(m_new_atoms.back()); m_new_atoms.pop_back(); theory_var v = atoms.back()->get_var(); for (unsigned i = 0; i < m_new_atoms.size(); ++i) { if (m_new_atoms[i]->get_var() == v) { atoms.push_back(m_new_atoms[i]); m_new_atoms[i] = m_new_atoms.back(); m_new_atoms.pop_back(); --i; } } CTRACE("arith", atoms.size() > 1, for (unsigned i = 0; i < atoms.size(); ++i) { atoms[i]->display(*this, tout); tout << "\n"; }); ptr_vector occs(m_var_occs[v]); std::sort(atoms.begin(), atoms.end(), compare_atoms()); std::sort(occs.begin(), occs.end(), compare_atoms()); typename atoms::iterator begin1 = occs.begin(); typename atoms::iterator begin2 = occs.begin(); typename atoms::iterator end = occs.end(); begin1 = first(A_LOWER, begin1, end); begin2 = first(A_UPPER, begin2, end); typename atoms::iterator lo_inf = begin1, lo_sup = begin1; typename atoms::iterator hi_inf = begin2, hi_sup = begin2; typename atoms::iterator lo_inf1 = begin1, lo_sup1 = begin1; typename atoms::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) { atom* a1 = atoms[i]; lo_inf1 = next_inf(a1, A_LOWER, lo_inf, end, flo_inf); hi_inf1 = next_inf(a1, A_UPPER, hi_inf, end, fhi_inf); lo_sup1 = next_sup(a1, A_LOWER, lo_sup, end, flo_sup); hi_sup1 = next_sup(a1, A_UPPER, 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; if (hi_sup1 != end) hi_sup = hi_sup1; if (!flo_inf) lo_inf = end; if (!fhi_inf) hi_inf = end; if (!flo_sup) lo_sup = end; if (!fhi_sup) hi_sup = end; visited.insert(a1); if (lo_inf1 != end && lo_inf != end && !visited.contains(*lo_inf)) mk_bound_axiom(a1, *lo_inf); if (lo_sup1 != end && lo_sup != end && !visited.contains(*lo_sup)) mk_bound_axiom(a1, *lo_sup); if (hi_inf1 != end && hi_inf != end && !visited.contains(*hi_inf)) mk_bound_axiom(a1, *hi_inf); if (hi_sup1 != end && hi_sup != end && !visited.contains(*hi_sup)) mk_bound_axiom(a1, *hi_sup); } } } template typename theory_arith::atoms::iterator theory_arith::first( atom_kind kind, typename atoms::iterator it, typename atoms::iterator end) { for (; it != end; ++it) { atom* a = *it; if (a->get_atom_kind() == kind) return it; } return end; } template typename theory_arith::atoms::iterator theory_arith::next_inf( atom* a1, atom_kind kind, typename atoms::iterator it, typename atoms::iterator end, bool& found_compatible) { inf_numeral const & k1(a1->get_k()); typename atoms::iterator result = end; found_compatible = false; for (; it != end; ++it) { atom * a2 = *it; if (a1 == a2) continue; if (a2->get_atom_kind() != kind) continue; inf_numeral const & k2(a2->get_k()); found_compatible = true; if (k2 <= k1) { result = it; } else { break; } } return result; } template typename theory_arith::atoms::iterator theory_arith::next_sup( atom* a1, atom_kind kind, typename atoms::iterator it, typename atoms::iterator end, bool& found_compatible) { inf_numeral const & k1(a1->get_k()); found_compatible = false; for (; it != end; ++it) { atom * a2 = *it; if (a1 == a2) continue; if (a2->get_atom_kind() != kind) continue; inf_numeral const & k2(a2->get_k()); found_compatible = true; if (k1 < k2) { return it; } } return end; } template bool theory_arith::internalize_atom(app * n, bool gate_ctx) { TRACE("arith_internalize", tout << "internalizing atom:\n" << mk_pp(n, this->get_manager()) << "\n";); context & ctx = get_context(); SASSERT(m_util.is_le(n) || m_util.is_ge(n) || m_util.is_is_int(n)); SASSERT(!ctx.b_internalized(n)); atom_kind kind; if (m_util.is_is_int(n)) { internalize_is_int(n); if (ctx.b_internalized(n)) { TRACE("arith_internalize", tout << "term was re-internalized: #" << n->get_id() << "\n";); return true; } bool_var bv = ctx.mk_bool_var(n); ctx.set_var_theory(bv, get_id()); return true; } if (m_util.is_le(n)) kind = A_UPPER; else kind = A_LOWER; if (!is_app(n->get_arg(0)) || !is_app(n->get_arg(1))) { return false; } app * lhs = to_app(n->get_arg(0)); app * rhs = to_app(n->get_arg(1)); expr * rhs2; if (m_util.is_to_real(rhs, rhs2) && is_app(rhs2)) { rhs = to_app(rhs2); } if (!m_util.is_numeral(rhs)) { UNREACHABLE(); throw default_exception("malformed atomic constraint"); } theory_var v = internalize_term_core(lhs); if (v == null_theory_var) { TRACE("arith_internalize", tout << "failed to internalize: #" << n->get_id() << "\n";); return false; } if (ctx.b_internalized(n)) { TRACE("arith_internalize", tout << "term was re-internalized: #" << n->get_id() << "\n";); return true; } bool_var bv = ctx.mk_bool_var(n); ctx.set_var_theory(bv, get_id()); rational _k; VERIFY(m_util.is_numeral(rhs, _k)); if (is_int(v) && !_k.is_int()) { if (kind == A_UPPER) { _k = floor(_k); } else { _k = ceil(_k); } } inf_numeral k(_k); atom * a = alloc(atom, bv, v, k, kind); mk_bound_axioms(a); m_unassigned_atoms[v]++; atoms & occs = m_var_occs[v]; occs.push_back(a); m_atoms.push_back(a); insert_bv2a(bv, a); TRACE("arith_internalize", tout << "succeeded... v" << v << " " << kind << " " << k << "\n"; for (unsigned i = 0; i + 1 < occs.size(); ++i) tout << occs[i] << "\n";); return true; } template bool theory_arith::internalize_term(app * term) { TRACE("arith_internalize", tout << "internalising term:\n" << mk_pp(term, this->get_manager()) << "\n";); theory_var v = internalize_term_core(term); TRACE("arith_internalize", tout << "theory_var: " << v << "\n";); return v != null_theory_var; } template void theory_arith::internalize_eq_eh(app * atom, bool_var v) { expr* _lhs, *_rhs; if (m_params.m_arith_eager_eq_axioms && get_manager().is_eq(atom, _lhs, _rhs) && is_app(_lhs) && is_app(_rhs)) { context & ctx = get_context(); app * lhs = to_app(_lhs); app * rhs = to_app(_rhs); enode * n1 = ctx.get_enode(lhs); enode * n2 = ctx.get_enode(rhs); // The expression atom may be a theory axiom. In this case, it may not be in simplified form. // So, an atom such as (= a a) may occur. The procedure mk_axioms, expects n1 != n2. // So, we should check it. It doesn't make sense to create an axiom for (= a a) in the arith_eq_adapter. if (n1->get_th_var(get_id()) != null_theory_var && n2->get_th_var(get_id()) != null_theory_var && n1 != n2) { TRACE("mk_axioms_bug", tout << mk_bounded_pp(atom, get_manager(), 5) << "\n";); m_arith_eq_adapter.mk_axioms(n1, n2); } } } template void theory_arith::apply_sort_cnstr(enode * n, sort * s) { // do nothing... } template void theory_arith::assign_eh(bool_var v, bool is_true) { TRACE("arith_verbose", tout << "p" << v << " := " << (is_true?"true":"false") << "\n";); atom * a = get_bv2a(v); if (!a) return; SASSERT(get_context().get_assignment(a->get_bool_var()) != l_undef); SASSERT((get_context().get_assignment(a->get_bool_var()) == l_true) == is_true); a->assign_eh(is_true, get_epsilon(a->get_var())); m_asserted_bounds.push_back(a); } template void theory_arith::relevant_eh(app * n) { TRACE("arith_relevant_eh", tout << "relevant_eh: " << mk_pp(n, get_manager()) << "\n";); if (m_util.is_mod(n)) mk_idiv_mod_axioms(n->get_arg(0), n->get_arg(1)); else if (m_util.is_rem(n)) mk_rem_axiom(n->get_arg(0), n->get_arg(1)); else if (m_util.is_div(n)) mk_div_axiom(n->get_arg(0), n->get_arg(1)); else if (m_util.is_to_int(n)) mk_to_int_axiom(n); else if (m_util.is_is_int(n)) mk_is_int_axiom(n); } template void theory_arith::new_eq_eh(theory_var v1, theory_var v2) { TRACE("arith_new_eq_eh", tout << "#" << get_enode(v1)->get_owner_id() << " = #" << get_enode(v2)->get_owner_id() << "\n";); TRACE("arith_new_eq_eh_detail", tout << mk_pp(get_enode(v1)->get_owner(), get_manager()) << "\n" << mk_pp(get_enode(v2)->get_owner(), get_manager()) << "\n";); enode * n1 = get_enode(v1); if (!m_util.is_int(n1->get_owner()) && !m_util.is_real(n1->get_owner())) { return; } if (m_params.m_arith_eq_bounds) { enode * n2 = get_enode(v2); SASSERT(n1->get_root() == n2->get_root()); if (m_util.is_numeral(n1->get_owner())) { std::swap(v1, v2); std::swap(n1, n2); } rational k; bound * b1 = nullptr; bound * b2 = nullptr; if (m_util.is_numeral(n2->get_owner(), k)) { inf_numeral val(k); b1 = alloc(eq_bound, v1, val, B_LOWER, n1, n2); b2 = alloc(eq_bound, v1, val, B_UPPER, n1, n2); } else { if (n1->get_owner_id() > n2->get_owner_id()) std::swap(n1, n2); sort * st = get_manager().get_sort(n1->get_owner()); app * minus_one = m_util.mk_numeral(rational::minus_one(), st); app * s = m_util.mk_add(n1->get_owner(), m_util.mk_mul(minus_one, n2->get_owner())); context & ctx = get_context(); ctx.internalize(s, false); enode * e_s = ctx.get_enode(s); ctx.mark_as_relevant(e_s); SASSERT(is_attached_to_var(e_s)); theory_var v_s = e_s->get_th_var(get_id()); b1 = alloc(eq_bound, v_s, inf_numeral::zero(), B_LOWER, n1, n2); b2 = alloc(eq_bound, v_s, inf_numeral::zero(), B_UPPER, n1, n2); } m_bounds_to_delete.push_back(b1); m_bounds_to_delete.push_back(b2); m_asserted_bounds.push_back(b1); m_asserted_bounds.push_back(b2); } else { m_arith_eq_adapter.new_eq_eh(v1, v2); } } template bool theory_arith::use_diseqs() const { return true; } template void theory_arith::new_diseq_eh(theory_var v1, theory_var v2) { TRACE("arith_new_diseq_eh", tout << mk_bounded_pp(get_enode(v1)->get_owner(), get_manager()) << "\n" << mk_bounded_pp(get_enode(v2)->get_owner(), get_manager()) << "\n";); m_stats.m_assert_diseq++; m_arith_eq_adapter.new_diseq_eh(v1, v2); } template void theory_arith::restart_eh() { m_arith_eq_adapter.restart_eh(); } template void theory_arith::init_search_eh() { TRACE("arith_init_search", display(tout);); m_num_conflicts = 0; m_branch_cut_counter = 0; m_eager_gcd = m_params.m_arith_eager_gcd; if (lazy_pivoting_lvl() == 1) elim_quasi_base_rows(); move_unconstrained_to_base(); m_arith_eq_adapter.init_search_eh(); m_final_check_idx = 0; m_nl_gb_exhausted = false; m_nl_strategy_idx = 0; } template final_check_status theory_arith::final_check_core() { m_model_depends_on_computed_epsilon = false; unsigned old_idx = m_final_check_idx; final_check_status result = FC_DONE; final_check_status ok; do { if (get_context().get_cancel_flag()) { return FC_GIVEUP; } SASSERT(m_to_patch.empty()); TRACE("arith", tout << "m_final_check_idx: " << m_final_check_idx << ", result: " << result << "\n";); switch (m_final_check_idx) { case 0: ok = check_int_feasibility(); TRACE("arith", tout << "check_int_feasibility(), ok: " << ok << "\n";); break; case 1: if (assume_eqs_core()) ok = FC_CONTINUE; else ok = FC_DONE; TRACE("arith", tout << "assume_eqs(), ok: " << ok << "\n";); break; default: ok = process_non_linear(); TRACE("arith", tout << "non_linear(), ok: " << ok << "\n";); break; } m_final_check_idx = (m_final_check_idx + 1) % 3; switch (ok) { case FC_DONE: break; case FC_GIVEUP: result = FC_GIVEUP; break; case FC_CONTINUE: TRACE("arith", tout << "continue arith..." << (get_context().inconsistent()?"inconsistent\n":"\n");); return FC_CONTINUE; } } while (m_final_check_idx != old_idx); if (result == FC_DONE && m_found_unsupported_op) { TRACE("arith", tout << "Found unsupported operation\n";); result = FC_GIVEUP; } return result; } template final_check_status theory_arith::final_check_eh() { TRACE("arith_eq_adapter_info", m_arith_eq_adapter.display_already_processed(tout);); TRACE("arith", display(tout);); if (!propagate_core()) return FC_CONTINUE; if (delayed_assume_eqs()) return FC_CONTINUE; get_context().push_trail(value_trail(m_final_check_idx)); m_liberal_final_check = true; m_changed_assignment = false; final_check_status result = final_check_core(); if (result != FC_DONE) return result; if (!m_changed_assignment) return FC_DONE; m_liberal_final_check = false; m_changed_assignment = false; result = final_check_core(); TRACE("arith", tout << "result: " << result << "\n";); return result; } template bool theory_arith::can_propagate() { return process_atoms() && m_asserted_qhead < m_asserted_bounds.size(); } template void theory_arith::propagate() { TRACE("arith_propagate", tout << "propagate\n"; display(tout);); if (!process_atoms()) return; propagate_core(); } template bool theory_arith::propagate_core() { CASSERT("arith", wf_rows()); CASSERT("arith", wf_columns()); CASSERT("arith", valid_row_assignment()); flush_bound_axioms(); propagate_linear_monomials(); while (m_asserted_qhead < m_asserted_bounds.size()) { bound * b = m_asserted_bounds[m_asserted_qhead]; m_asserted_qhead++; if (!assert_bound(b)) { failed(); return false; } } if (!make_feasible()) { failed(); return false; } if (get_context().get_cancel_flag()) { return true; } CASSERT("arith", satisfy_bounds()); discard_update_trail(); SASSERT(m_update_trail_stack.empty()); propagate_bounds(); SASSERT(m_asserted_qhead == m_asserted_bounds.size()); SASSERT(m_update_trail_stack.empty()); CASSERT("arith", wf_rows()); CASSERT("arith", wf_columns()); CASSERT("arith", valid_row_assignment()); CASSERT("arith", satisfy_bounds()); return true; } template void theory_arith::failed() { restore_assignment(); m_to_patch.reset(); m_to_check.reset(); m_in_to_check.reset(); } template void theory_arith::flush_eh() { std::for_each(m_atoms.begin(), m_atoms.end(), delete_proc()); m_atoms.reset(); std::for_each(m_bounds_to_delete.begin(), m_bounds_to_delete.end(), delete_proc()); m_bounds_to_delete.reset(); } template void theory_arith::reset_eh() { m_stats.reset(); m_rows .reset(); m_arith_eq_adapter .reset_eh(); m_dead_rows .reset(); m_columns .reset(); m_data .reset(); m_value .reset(); m_old_value .reset(); m_bounds[0] .reset(); m_bounds[1] .reset(); m_var_occs .reset(); m_unassigned_atoms .reset(); m_bool_var2atom .reset(); m_var_pos .reset(); std::for_each(m_atoms.begin(), m_atoms.end(), delete_proc()); m_atoms .reset(); std::for_each(m_bounds_to_delete.begin(), m_bounds_to_delete.end(), delete_proc()); m_bounds_to_delete.reset(); m_asserted_bounds .reset(); m_asserted_qhead = 0; m_to_patch .reset(); m_left_basis .reset(); m_blands_rule = false; m_update_trail_stack .reset(); m_in_update_trail_stack .reset(); m_to_check .reset(); m_in_to_check .reset(); m_num_conflicts = 0; m_bound_trail .reset(); m_unassigned_atoms_trail .reset(); m_scopes .reset(); m_nl_monomials .reset(); m_nl_propagated .reset(); m_nl_rounds = 0; m_nl_gb_exhausted = false; m_nl_strategy_idx = 0; theory::reset_eh(); } /** \brief Compute the value of a base or quasi-base variable using the value of the dependent variables. */ template typename theory_arith::inf_numeral const & theory_arith::get_implied_value(theory_var v) const { SASSERT(is_quasi_base(v) || is_base(v)); inf_numeral & sum = const_cast *>(this)->m_tmp; sum.reset(); unsigned r_id = get_var_row(v); row const & r = m_rows[r_id]; for (row_entry const& re : r) { if (!re.is_dead() && re.m_var != v) { SASSERT(!is_quasi_base(re.m_var)); SASSERT(get_value(re.m_var) == m_value[re.m_var]); sum += re.m_coeff * get_value(re.m_var); } } sum.neg(); return sum; } /** \brief Compute the value of a base or quasi-base variable using the old value of the dependent variables. By old, we mean the value of the variable in the beginning of propagate(). Store the result in 'result'. Return true if the old value is different from the current value. */ template bool theory_arith::get_implied_old_value(theory_var v, inf_numeral & result) const { SASSERT(is_quasi_base(v) || is_base(v)); bool is_diff = false; result.reset(); unsigned r_id = get_var_row(v); row const & r = m_rows[r_id]; for (row_entry const& re : r) { if (!re.is_dead() && re.m_var != v) { theory_var v2 = re.m_var; SASSERT(!is_quasi_base(v2)); SASSERT(get_value(v2) == m_value[v2]); if (m_in_update_trail_stack.contains(v2)) { result += re.m_coeff * m_old_value[v2]; is_diff = true; } else { result += re.m_coeff * m_value[v2]; } } } result.neg(); return is_diff; } template theory_arith::theory_arith(ast_manager & m, theory_arith_params & params): theory(m.mk_family_id("arith")), m_params(params), m_util(m), m_arith_eq_solver(m), m_found_unsupported_op(false), m_found_underspecified_op(false), m_arith_eq_adapter(*this, params, m_util), m_asserted_qhead(0), m_row_vars_top(0), m_to_patch(1024), m_blands_rule(false), m_random(params.m_arith_random_seed), m_num_conflicts(0), m_branch_cut_counter(0), m_eager_gcd(m_params.m_arith_eager_gcd), m_final_check_idx(0), m_antecedents_index(0), m_var_value_table(DEFAULT_HASHTABLE_INITIAL_CAPACITY, var_value_hash(*this), var_value_eq(*this)), m_liberal_final_check(true), m_changed_assignment(false), m_assume_eq_head(0), m_model_depends_on_computed_epsilon(false), m_nl_rounds(0), m_nl_gb_exhausted(false), m_nl_new_exprs(m), m_bound_watch(null_bool_var) { } template theory_arith::~theory_arith() { } template theory* theory_arith::mk_fresh(context* new_ctx) { return alloc(theory_arith, new_ctx->get_manager(), new_ctx->get_fparams()); } template void theory_arith::setup() { m_random.set_seed(m_params.m_arith_random_seed); theory::setup(); } // ----------------------------------- // // Add Row // // ----------------------------------- /** \brief Set: row1 <- row1 + coeff * row2 */ template void theory_arith::add_row(unsigned rid1, const numeral & coeff, unsigned rid2, bool apply_gcd_test) { m_stats.m_add_rows++; if (propagation_mode() != BP_NONE) mark_row_for_bound_prop(rid1); row & r1 = m_rows[rid1]; row & r2 = m_rows[rid2]; CASSERT("row_assignment_bug", valid_row_assignment(r1)); CASSERT("row_assignment_bug", valid_row_assignment(r2)); r1.compress_if_needed(m_columns); r2.compress_if_needed(m_columns); CASSERT("arith", check_null_var_pos()); r1.save_var_pos(m_var_pos); // // loop over variables in row2, // add terms in row2 to row1. // #define ADD_ROW(_SET_COEFF_, _ADD_COEFF_) \ typename vector::const_iterator it = r2.begin_entries(); \ typename vector::const_iterator end = r2.end_entries(); \ for (; it != end; ++it) { \ if (!it->is_dead()) { \ theory_var v = it->m_var; \ int pos = m_var_pos[v]; \ if (pos == -1) { \ /* variable v is not in row1 */ \ int row_idx; \ row_entry & r_entry = r1.add_row_entry(row_idx); \ r_entry.m_var = v; \ _SET_COEFF_; \ column & c = m_columns[v]; \ int col_idx; \ col_entry & c_entry = c.add_col_entry(col_idx); \ r_entry.m_col_idx = col_idx; \ c_entry.m_row_id = rid1; \ c_entry.m_row_idx = row_idx; \ } \ else { \ /* variable v is in row1 */ \ row_entry & r_entry = r1[pos]; \ SASSERT(r_entry.m_var == v); \ _ADD_COEFF_; \ if (r_entry.m_coeff.is_zero()) { \ int col_idx = r_entry.m_col_idx; \ r1.del_row_entry(pos); \ column & c = m_columns[v]; \ c.del_col_entry(col_idx); \ } \ m_var_pos[v] = -1; \ } \ } \ } ((void) 0) if (coeff.is_one()) { ADD_ROW(r_entry.m_coeff = it->m_coeff, r_entry.m_coeff += it->m_coeff); } else if (coeff.is_minus_one()) { ADD_ROW(r_entry.m_coeff = it->m_coeff; r_entry.m_coeff.neg(), r_entry.m_coeff -= it->m_coeff); } else { ADD_ROW(r_entry.m_coeff = it->m_coeff; r_entry.m_coeff *= coeff, r_entry.m_coeff += it->m_coeff * coeff); } r1.reset_var_pos(m_var_pos); CASSERT("arith", check_null_var_pos()); CASSERT("row_assignment_bug", valid_row_assignment(r1)); CASSERT("row_assignment_bug", valid_row_assignment(r2)); if (apply_gcd_test) { theory_var v = r1.get_base_var(); if (is_int(v) && !get_value(v).is_int()) gcd_test(r1); } } /** \brief Set r1 <- r1 + a_xs[0].m_coeff * get_var_row(a_xs[0].m_var) + ... + a_xs[0].m_coeff * get_var_row(a_xs[sz-1].m_var) \pre For all i in [0..sz-1]. not is_non_base(a_xs[i]) */ template void theory_arith::add_rows(unsigned r1, unsigned sz, linear_monomial * a_xs) { if (sz == 0) return; for (unsigned i = 0; i < sz; i++) { linear_monomial & m = a_xs[i]; numeral c = m.m_coeff; theory_var v = m.m_var; SASSERT(!is_non_base(v)); add_row(r1, c, get_var_row(v), false); } get_manager().limit().inc(sz); } // ----------------------------------- // // Assignment management // // ----------------------------------- template void theory_arith::save_value(theory_var v) { SASSERT(!is_quasi_base(v)); if (!m_in_update_trail_stack.contains(v)) { m_in_update_trail_stack.insert(v); SASSERT(m_value[v] == get_value(v)); m_old_value[v] = m_value[v]; m_update_trail_stack.push_back(v); TRACE("save_value", tout << "v" << v << " = " << get_value(v) << "\n";); } m_changed_assignment = true; } template void theory_arith::discard_update_trail() { m_in_update_trail_stack.reset(); m_update_trail_stack.reset(); } template void theory_arith::restore_assignment() { CASSERT("arith", valid_row_assignment()); TRACE("restore_assignment_bug", tout << "START restore_assignment...\n";); for (theory_var v : m_update_trail_stack) { TRACE("restore_assignment_bug", tout << "restoring v" << v << " <- " << m_old_value[v] << "\n";); SASSERT(!is_quasi_base(v)); SASSERT(m_in_update_trail_stack.contains(v)); m_value[v] = m_old_value[v]; } m_update_trail_stack.reset(); m_in_update_trail_stack.reset(); CASSERT("arith", valid_row_assignment()); } /** \brief m_value[v] += delta */ template void theory_arith::update_value_core(theory_var v, inf_numeral const & delta) { save_value(v); m_value[v] += delta; if (is_base(v) && !m_to_patch.contains(v) && (below_lower(v) || above_upper(v))) { m_to_patch.insert(v); } get_manager().limit().inc(); } /** \brief m_value[v] += delta, and update dependent (non-base) variables. */ template void theory_arith::update_value(theory_var v, inf_numeral const & delta) { update_value_core(v, delta); column & c = m_columns[v]; c.compress_if_needed(m_rows); inf_numeral delta2; for (auto& ce : c) { if (!ce.is_dead()) { row & r = m_rows[ce.m_row_id]; theory_var s = r.get_base_var(); if (s != null_theory_var && !is_quasi_base(s)) { delta2 = delta; delta2 *= r[ce.m_row_idx].m_coeff; delta2.neg(); update_value_core(s, delta2); } } } } // ----------------------------------- // // Pivoting // // ----------------------------------- /** \brief Make x_j the new base variable for row of x_i x_j is assumed to have coefficient a_ij. Let row_id = m_base_var[x_i] rows[row_id] <- rows[row_id] / a_ij rows[other] <- row[other] - rows[row_id] * a_kj Where a_kj is the coefficient of x_j in row[other] */ template template void theory_arith::pivot(theory_var x_i, theory_var x_j, numeral const & a_ij, bool apply_gcd_test) { TRACE("arith_pivoting", tout << "pivoting: v" << x_i << ", v" << x_j << "\n";); m_stats.m_pivots++; SASSERT(is_base(x_i) || is_quasi_base(x_i)); SASSERT(x_i != x_j); int r_id = get_var_row(x_i); row & r = m_rows[r_id]; SASSERT(r.is_coeff_of(x_j, a_ij)); #define DIVIDE_ROW(_ADJUST_COEFF_) \ typename vector::iterator it = r.begin_entries(); \ typename vector::iterator end = r.end_entries(); \ for (; it != end; ++it) { \ if (!it->is_dead()) { \ _ADJUST_COEFF_; \ } \ } ((void) 0) if (a_ij.is_minus_one()) { DIVIDE_ROW(it->m_coeff.neg()); } else if (!a_ij.is_one()) { numeral tmp = a_ij; DIVIDE_ROW(it->m_coeff /= tmp); } get_manager().limit().inc(r.size()); set_var_row(x_i, -1); set_var_row(x_j, r_id); SASSERT(r.m_base_var == x_i); r.m_base_var = x_j; set_var_kind(x_i, NON_BASE); set_var_kind(x_j, BASE); eliminate(x_j, apply_gcd_test); CASSERT("arith", wf_rows()); CASSERT("arith", wf_columns()); CASSERT("arith", valid_row_assignment()); TRACE("arith_pivot", tout << "after pivoting:\n"; display(tout);); TRACE("pivot_shape", display_rows_shape(tout);); TRACE("pivot_stats", display_rows_stats(tout);); TRACE_CODE({ static unsigned val = 0; val ++; if (val % 100 == 0) { TRACE("pivot_bignums", display_rows_bignums(tout);); }}); } /** \brief Eliminate x_i from the rows different from get_var_row(x_i) If Lazy = true, then x_i is only eliminated from base rows. */ template template void theory_arith::eliminate(theory_var x_i, bool apply_gcd_test) { SASSERT(is_base(x_i) || is_quasi_base(x_i)); unsigned r_id = get_var_row(x_i); column & c = m_columns[x_i]; numeral a_kj; typename svector::iterator it = c.begin_entries(); typename svector::iterator end = c.end_entries(); int i = 0; int s_pos = -1; for (; it != end; ++it, ++i) { if (!it->is_dead()) { unsigned r1_sz = m_rows[r_id].size(); if (it->m_row_id != static_cast(r_id)) { row & r2 = m_rows[it->m_row_id]; theory_var s2 = r2.m_base_var; if (s2 != null_theory_var && (!Lazy || is_base(s2))) { a_kj = r2[it->m_row_idx].m_coeff; a_kj.neg(); add_row(it->m_row_id, a_kj, r_id, apply_gcd_test); get_manager().limit().inc((r1_sz + r2.size()) * (a_kj.storage_size())); } } else { s_pos = i; } } } CTRACE("eliminate", !Lazy && c.size() != 1, tout << "eliminating v" << x_i << ", Lazy: " << Lazy << ", c.size: " << c.size() << "\n"; display(tout);); SASSERT(Lazy || c.size() == 1); if (c.size() == 1) { // When lazy pivoting is used, then after pivoting c may // not be a singleton c.compress_singleton(m_rows, s_pos); } } /** \brief Set x_j to be the new base variable of row owned by x_i a_ij - coefficient of x_j in the row owned by x_i x_i_new_val - new value of x_i */ template void theory_arith::update_and_pivot(theory_var x_i, theory_var x_j, numeral const & a_ij, inf_numeral const & x_i_new_val) { CASSERT("arith", valid_row_assignment()); TRACE("update_and_pivot_bug_detail", display(tout);); SASSERT(is_base(x_i)); inf_numeral theta = m_value[x_i]; TRACE("update_and_pivot_bug", tout << "theta 1) " << theta << " " << x_i_new_val << "\n";); theta -= x_i_new_val; TRACE("update_and_pivot_bug", tout << "theta 2) " << theta << " " << a_ij << "\n";); theta /= a_ij; TRACE("update_and_pivot_bug", tout << "theta 3) " << theta << "\n";); update_value(x_j, theta); CTRACE("arith", get_value(x_i) != x_i_new_val, tout << "x_i: " << x_i << ", x_j: " << x_j << ", a_ij: " << a_ij << ", x_i_new_val: " << x_i_new_val << "\n"; tout << "new val: " << get_value(x_i) << ", theta: " << theta << "\n"; display(tout);); SASSERT(get_value(x_i) == x_i_new_val); if (!m_to_patch.contains(x_j) && (below_lower(x_j) || above_upper(x_j))) m_to_patch.insert(x_j); pivot(x_i, x_j, a_ij, m_eager_gcd); CASSERT("arith", valid_row_assignment()); } /** \brief Return the number of base variables that are non free and are v dependent. The function adds 1 to the result if v is non free. The function returns with a partial result r if r > best_so_far. This function is used to select the pivot variable. */ template int theory_arith::get_num_non_free_dep_vars(theory_var v, int best_so_far) { int result = is_non_free(v); column & c = m_columns[v]; typename svector::const_iterator it = c.begin_entries(); typename svector::const_iterator end = c.end_entries(); for (; it != end; ++it) { if (!it->is_dead()) { row & r = m_rows[it->m_row_id]; theory_var s = r.get_base_var(); if (s != null_theory_var && is_base(s)) { result += is_non_free(s); if (result > best_so_far) return result; } } } return result; } /** \brief Using Bland's rule, select a variable x_j in the row r defining the base var x_i, s.t. x_j can be used to patch the error in x_i. Return null_theory_var if there is no x_j. Otherwise, return x_j and store its coefficient in out_a_ij. */ template theory_var theory_arith::select_blands_pivot_core(theory_var x_i, bool is_below, numeral & out_a_ij) { SASSERT(is_base(x_i)); theory_var max = get_num_vars(); theory_var result = max; row const & r = m_rows[get_var_row(x_i)]; typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (!it->is_dead()) { theory_var x_j = it->m_var; numeral const & a_ij = it->m_coeff; bool is_neg = is_below ? a_ij.is_neg() : a_ij.is_pos(); bool is_pos = !is_neg; if (x_i != x_j && ((is_pos && above_lower(x_j)) || (is_neg && below_upper(x_j)))) { SASSERT(is_non_base(x_j)); if (x_j < result) { result = x_j; out_a_ij = a_ij; } } } } return result < max ? result : null_theory_var; } /** \brief Select a variable x_j in the row r defining the base var x_i, s.t. x_j can be used to patch the error in x_i. Return null_theory_var if there is no x_j. Otherwise, return x_j and store its coefficient in out_a_ij. The argument is_below is true (false) if x_i is below its lower bound (above its upper bound). */ template template theory_var theory_arith::select_pivot_core(theory_var x_i, numeral & out_a_ij) { SASSERT(is_base(x_i)); theory_var max = get_num_vars(); theory_var result = max; row const & r = m_rows[get_var_row(x_i)]; int best_col_sz = INT_MAX; int best_so_far = INT_MAX; int n = 0; typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (!it->is_dead()) { theory_var x_j = it->m_var; numeral const & a_ij = it->m_coeff; bool is_neg = is_below ? a_ij.is_neg() : a_ij.is_pos(); bool is_pos = !is_neg; if (x_i != x_j && ((is_pos && above_lower(x_j)) || (is_neg && below_upper(x_j)))) { int num = get_num_non_free_dep_vars(x_j, best_so_far); int col_sz = m_columns[x_j].size(); if (num < best_so_far || (num == best_so_far && col_sz < best_col_sz)) { result = x_j; out_a_ij = a_ij; best_so_far = num; best_col_sz = col_sz; n = 1; } else if (num == best_so_far && col_sz == best_col_sz) { n++; if (m_random()%n == 0) { result = x_j; out_a_ij = a_ij; } } } } } return result < max ? result : null_theory_var; } /** \brief Wrapper for select_blands_pivot_core and select_pivot_core */ template theory_var theory_arith::select_pivot(theory_var x_i, bool is_below, numeral & out_a_ij) { TRACE("select_pivot", tout << "m_blands_rule: " << m_blands_rule << " v" << x_i << "\n";); CTRACE("select_pivot_info", x_i > 500, get_context().display(tout);); if (m_blands_rule) return select_blands_pivot_core(x_i, is_below, out_a_ij); else if (is_below) return select_pivot_core(x_i, out_a_ij); else return select_pivot_core(x_i, out_a_ij); } // ----------------------------------- // // Make feasible // // ----------------------------------- /** \brief Make the given variable feasible. This method assumes that x_i is a base var. Return false if it was not possible to make x_i feasible. */ template bool theory_arith::make_var_feasible(theory_var x_i) { CTRACE("arith_bug", !is_base(x_i), tout << "x_i: " << x_i << ", below_lower(x_i): " << below_lower(x_i) << ", above_upper(x_i): " << above_upper(x_i) << "\n"; display(tout);); SASSERT(is_base(x_i)); bool is_below; if (below_lower(x_i)) { is_below = true; } else if (above_upper(x_i)) { is_below = false; } else { // x_i is already feasible return true; } TRACE("make_var_feasible", display_row(tout, get_var_row(x_i), false);); numeral a_ij; theory_var x_j = select_pivot(x_i, is_below, a_ij); if (x_j != null_theory_var) { SASSERT(is_base(x_i)); TRACE("pivot_bug", display_row_info(tout, get_var_row(x_i));); update_and_pivot(x_i, x_j, a_ij, get_bound(x_i, !is_below)->get_value()); return true; } else { // conflict detected sign_row_conflict(x_i, is_below); return false; } } template theory_var theory_arith::select_lg_error_var(bool least) { TRACE("select_pivot", tout << "starting...\n";); theory_var best = null_theory_var; inf_numeral best_error; inf_numeral curr_error; for (theory_var v : m_to_patch) { if (below_lower(v)) curr_error = lower(v)->get_value() - get_value(v); else if (above_upper(v)) curr_error = get_value(v) - upper(v)->get_value(); else continue; SASSERT(curr_error > inf_numeral(0)); if (best == null_theory_var || (!least && curr_error > best_error) || (least && curr_error < best_error)) { TRACE("select_pivot", tout << "best: " << best << " v" << v << ", best_error: " << best_error << ", curr_error: " << curr_error << "\n";); best = v; best_error = curr_error; } } if (best == null_theory_var) m_to_patch.clear(); // all variables are satisfied else m_to_patch.erase(best); return best; } template theory_var theory_arith::select_smallest_var() { return m_to_patch.erase_min(); } template theory_var theory_arith::select_var_to_fix() { if (m_blands_rule) return select_smallest_var(); switch (m_params.m_arith_pivot_strategy) { case ARITH_PIVOT_GREATEST_ERROR: return select_greatest_error_var(); case ARITH_PIVOT_LEAST_ERROR: return select_least_error_var(); default: return select_smallest_var(); } } /** \brief Return true if it was possible to patch all variables in m_to_patch. */ template bool theory_arith::make_feasible() { TRACE("arith_make_feasible", tout << "make_feasible\n"; display(tout);); CASSERT("arith", wf_rows()); CASSERT("arith", wf_columns()); CASSERT("arith", valid_row_assignment()); m_left_basis.reset(); m_blands_rule = false; unsigned num_repeated = 0; while (!m_to_patch.empty()) { theory_var v = select_var_to_fix(); if (v == null_theory_var) { // all variables were satisfied... SASSERT(m_to_patch.empty()); break; } if (!m_blands_rule) { if (m_left_basis.contains(v)) { num_repeated++; if (num_repeated > blands_rule_threshold()) { TRACE("blands_rule", tout << "using blands rule, " << num_repeated << "\n";); // std::cerr << "BLANDS RULE...\n"; m_blands_rule = true; } } else { m_left_basis.insert(v); } } if (!make_var_feasible(v)) { TRACE("arith_make_feasible", tout << "make_feasible: unsat\n"; display(tout);); return false; } TRACE("arith_make_feasible_detail", display(tout);); if (get_context().get_cancel_flag()) { return true; } } TRACE("arith_make_feasible", tout << "make_feasible: sat\n"; display(tout);); CASSERT("arith", wf_rows()); CASSERT("arith", wf_columns()); CASSERT("arith", valid_row_assignment()); CASSERT("arith", satisfy_bounds()); return true; } /** \brief A row is in a sign inconsistency when it is implying a lower (upper) bound on x_i, which is above (below) its known upper (lower) bound. */ template void theory_arith::sign_row_conflict(theory_var x_i, bool is_below) { inf_numeral delta; row const & r = m_rows[get_var_row(x_i)]; int idx = r.get_idx_of(x_i); SASSERT(idx >= 0); bound * b = nullptr; // Remark: // if x_i is an integer variable, then delta can be negative: // // Example: x_i <= 0 get_value(x_i) = 1/4 // // The value is above the upper bound. // Since x_i is an integer, get_epsilon(x_i) = 1, and delta = -3/4 if (is_below) { SASSERT(below_lower(x_i)); b = lower(x_i); if (relax_bounds()) { delta = b->get_value(); delta -= get_value(x_i); delta -= get_epsilon(x_i); if (delta.is_neg()) delta.reset(); } } else { SASSERT(above_upper(x_i)); b = upper(x_i); if (relax_bounds()) { delta = get_value(x_i); delta -= b->get_value(); delta -= get_epsilon(x_i); if (delta.is_neg()) delta.reset(); } } antecedents ante(*this); explain_bound(r, idx, !is_below, delta, ante); b->push_justification(ante, numeral(1), coeffs_enabled()); TRACE("sign_row_conflict", tout << "v" << x_i << " is_below: " << is_below << " delta: " << delta << "\n"; display_var(tout, x_i); tout << "is_below_lower: " << below_lower(x_i) << ", is_above_upper: " << above_upper(x_i) << "\n"; display_row(tout, r, true); display_row(tout, r, false); ante.display(tout);); set_conflict(ante, ante, "farkas"); } // ----------------------------------- // // Assert bound // // ----------------------------------- /** \brief Assert x >= k, return false if a conflict is detected. */ template bool theory_arith::assert_lower(bound * b) { SASSERT(b->get_bound_kind() == B_LOWER); theory_var v = b->get_var(); inf_numeral const & k = b->get_value(); bound * u = upper(v); bound * l = lower(v); if (u && k > u->get_value()) { sign_bound_conflict(u, b); return false; } if (l && k <= l->get_value()) { // redundant return true; } switch (get_var_kind(v)) { case QUASI_BASE: quasi_base_row2base_row(get_var_row(v)); SASSERT(get_var_kind(v) == BASE); case BASE: if (!m_to_patch.contains(v) && get_value(v) < k) { TRACE("to_patch_bug", tout << "need to be patched (assert_lower): "; display_var(tout, v);); m_to_patch.insert(v); } break; case NON_BASE: if (get_value(v) < k) set_value(v, k); break; } push_bound_trail(v, l, false); set_bound(b, false); if (propagation_mode() != BP_NONE) mark_rows_for_bound_prop(v); return true; } /** \brief Assert x <= k, return false if a conflict is detected. */ template bool theory_arith::assert_upper(bound * b) { SASSERT(b->get_bound_kind() == B_UPPER); theory_var v = b->get_var(); inf_numeral const & k = b->get_value(); TRACE("arith", display_bound(tout, b); tout << "v" << v << " <= " << k << "\n";); bound * u = upper(v); bound * l = lower(v); if (l && k < l->get_value()) { sign_bound_conflict(l, b); return false; } if (u && k >= u->get_value()) { // redundant return true; } switch (get_var_kind(v)) { case QUASI_BASE: quasi_base_row2base_row(get_var_row(v)); SASSERT(get_var_kind(v) == BASE); case BASE: if (!m_to_patch.contains(v) && get_value(v) > k) { TRACE("to_patch_bug", tout << "need to be patched (assert upper): "; display_var(tout, v);); m_to_patch.insert(v); } break; case NON_BASE: if (get_value(v) > k) set_value(v, k); break; } push_bound_trail(v, u, true); set_bound(b, true); if (propagation_mode() != BP_NONE) mark_rows_for_bound_prop(v); return true; } template bool theory_arith::assert_bound(bound * b) { TRACE("assert_bound", display_bound(tout, b); display(tout);); theory_var v = b->get_var(); if (b->is_atom()) { CTRACE("unassigned_atoms", m_unassigned_atoms[v] <= 0, display_var(tout, v);); SASSERT(m_unassigned_atoms[v] > 0); push_dec_unassigned_atoms_trail(v); m_unassigned_atoms[v]--; } bool result = true; switch (b->get_bound_kind()) { case B_LOWER: m_stats.m_assert_lower++; result = assert_lower(b); break; case B_UPPER: m_stats.m_assert_upper++; result = assert_upper(b); break; } TRACE("arith_bound", tout << "result: " << result << "\n"; display(tout);); return result; } /** \brief Sign a conflict because the bounds b1 and b2 are contradictory. */ template void theory_arith::sign_bound_conflict(bound * b1, bound * b2) { SASSERT(b1->get_var() == b2->get_var()); antecedents ante(*this); b1->push_justification(ante, numeral(1), coeffs_enabled()); b2->push_justification(ante, numeral(1), coeffs_enabled()); TRACE("arith_conflict", tout << "bound conflict v" << b1->get_var() << "\n"; tout << "bounds: " << b1 << " " << b2 << "\n";); set_conflict(ante, ante, "farkas"); } // ----------------------------------- // // Bound propagation // // ----------------------------------- /** \brief Mark the row r1 for bound propagation. */ template void theory_arith::mark_row_for_bound_prop(unsigned r1) { if (!m_in_to_check.contains(r1) && m_rows[r1].m_base_var != null_theory_var) { m_in_to_check.insert(r1); m_to_check.push_back(r1); } } /** \brief Mark all rows that contain v for bound propagation. */ template void theory_arith::mark_rows_for_bound_prop(theory_var v) { for (col_entry const& ce : m_columns[v]) { if (!ce.is_dead()) mark_row_for_bound_prop(ce.m_row_id); } } /** \brief Given a row: a_1 * x_1 + ... + a_n * x_n = 0 Claim: If forall i in [1..n]. (a_i > 0 => upper(x_i) != oo) and (a_i < 0 => lower(x_i) != -oo) Then row implies a lower bound for every monomial in the row. Proof: Without loss of generality, we consider the monomial a_1 * x_1 a_1 * x_1 = - a_2 * x_2 - ... - a_n * x_n = (Sum_{a_i < 0} -a_i * x_i) + (Sum_{a_j > 0} -a_j * x_j) >= (Sum_{a_i < 0} -a_i * lower(x_i)) + (Sum_{a_j > 0} -a_j * upper(x_j)) If one the condition fails for the monomial a_i * x_i, then the row can still be used to imply a lower bound for this monomial. -4*x + 2*y - 3*z = 0 y <= 1 z >= 1 -x = -2*y + 3*z >= -2 + 3 >= 1 -4*x >= 1 Remark: the lower bound is not for the variable, but for the monomial. Claim: If forall i in [1..n]. (a_i > 0 => lower(x_i) != oo) and (a_i < 0 => upper(x_i) != -oo) Then row implies a upper bound for every monomial in the row. Proof: similar to the previous claim. The result is stored in lower_idx and upper_idx - lower_idx >= 0 : row can imply a lower bound for the monomial at 'lower_idx' - lower_idx == -1 : row can imply a lower bound for every monomial in the row. - lower_idx == -2 : row cannot be used to imply a lower bound. - upper_idx >= 0 : row can imply a upper bound for the monomial at 'upper_idx' - upper_idx == -1 : row can imply a upper bound for every monomial in the row. - upper_idx == -2 : row cannot be used to imply a upper bound. */ template void theory_arith::is_row_useful_for_bound_prop(row const & r, int & lower_idx, int & upper_idx) const { lower_idx = -1; upper_idx = -1; typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (int i = 0; it != end; ++it, ++i) { if (!it->is_dead()) { #define UPDATE_IDX(IDX) IDX = IDX == -1 ? i : -2 if (skip_big_coeffs() && it->m_coeff.is_big()) { TRACE("is_row_useful", tout << "skipping row that contains big number...\n"; display_row_info(tout, r);); lower_idx = -2; upper_idx = -2; return; } bool is_pos = it->m_coeff.is_pos(); if (lower(it->m_var) == nullptr) { if (is_pos) { UPDATE_IDX(upper_idx); } else { UPDATE_IDX(lower_idx); } } if (upper(it->m_var) == nullptr) { if (is_pos) { UPDATE_IDX(lower_idx); } else { UPDATE_IDX(upper_idx); } } if (lower_idx == -2 && upper_idx == -2) return; } } } /** \brief Imply a lower/upper bound for the monomial stored at position idx. Then this bound is used to produce a bound for the monomial variable. */ template void theory_arith::imply_bound_for_monomial(row const & r, int idx, bool is_lower) { row_entry const & entry = r[idx]; if (m_unassigned_atoms[entry.m_var] > 0) { inf_numeral implied_k; typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (int idx2 = 0; it != end; ++it, ++idx2) { if (!it->is_dead() && idx != idx2) { bound * b = get_bound(it->m_var, is_lower ? it->m_coeff.is_pos() : it->m_coeff.is_neg()); SASSERT(b); // implied_k -= it->m_coeff * b->get_value(); implied_k.submul(it->m_coeff, b->get_value()); } } implied_k /= entry.m_coeff; if (entry.m_coeff.is_pos() == is_lower) { // implied_k is a lower bound for entry.m_var bound * curr = lower(entry.m_var); if (curr == nullptr || implied_k > curr->get_value()) { TRACE("arith_imply_bound", tout << "implying lower bound for v" << entry.m_var << " " << implied_k << " using row:\n"; display_row_info(tout, r); display_var(tout, entry.m_var);); mk_implied_bound(r, idx, is_lower, entry.m_var, B_LOWER, implied_k); } } else { // implied_k is an upper bound for it->m_var bound * curr = upper(entry.m_var); if (curr == nullptr || implied_k < curr->get_value()) { TRACE("arith_imply_bound", tout << "implying upper bound for v" << entry.m_var << " " << implied_k << " using row:\n"; display_row_info(tout, r); display_var(tout, entry.m_var);); mk_implied_bound(r, idx, is_lower, entry.m_var, B_UPPER, implied_k); } } } } /** \brief Auxiliary method. See is_row_useful_for_bound_prop If is_lower = true (false), then imply a lower (upper) bound for all monomials in the row. The monomial bounds are used to compute bounds for the monomial variables. */ template void theory_arith::imply_bound_for_all_monomials(row const & r, bool is_lower) { // Traverse the row once and compute // bb = (Sum_{a_i < 0} -a_i*lower(x_i)) + (Sum_{a_j > 0} -a_j * upper(x_j)) If is_lower = true // bb = (Sum_{a_i > 0} -a_i*lower(x_i)) + (Sum_{a_j < 0} -a_j * upper(x_j)) If is_lower = false inf_numeral bb; for (row_entry const& re : r) { if (!re.is_dead()) { inf_numeral const & b = get_bound(re.m_var, is_lower ? re.m_coeff.is_pos() : re.m_coeff.is_neg())->get_value(); // bb -= re.m_coeff * b; bb.submul(re.m_coeff, b); } } inf_numeral implied_k; typename vector::const_iterator it = r.begin(); typename vector::const_iterator end = r.end(); for (int idx = 0; it != end; ++it, ++idx) { if (!it->is_dead() && m_unassigned_atoms[it->m_var] > 0) { inf_numeral const & b = get_bound(it->m_var, is_lower ? it->m_coeff.is_pos() : it->m_coeff.is_neg())->get_value(); implied_k = bb; // implied_k += it->m_coeff * b; implied_k.addmul(it->m_coeff, b); // implied_k is a bound for the monomial in position it implied_k /= it->m_coeff; TRACE("arith_imply_bound", display_var(tout, it->m_var); tout << "implied bound: " << (it->m_coeff.is_pos() ? ">=" : "<=") << implied_k << "\n";); if (it->m_coeff.is_pos() == is_lower) { // implied_k is a lower bound for it->m_var bound * curr = lower(it->m_var); if (curr == nullptr || implied_k > curr->get_value()) { // improved lower bound TRACE("arith_imply_bound", tout << "implying lower bound for v" << it->m_var << " " << implied_k << " using row:\n"; display_row_info(tout, r); display_var(tout, it->m_var);); mk_implied_bound(r, idx, is_lower, it->m_var, B_LOWER, implied_k); } } else { // implied_k is an upper bound for it->m_var bound * curr = upper(it->m_var); if (curr == nullptr || implied_k < curr->get_value()) { // improved upper bound TRACE("arith_imply_bound", tout << "implying upper bound for v" << it->m_var << " " << implied_k << " using row:\n"; display_row_info(tout, r); display_var(tout, it->m_var);); mk_implied_bound(r, idx, is_lower, it->m_var, B_UPPER, implied_k); } } } } } /** \brief Create an explanation for the lower/upper bound of the variable at position idx. \remark delta is used for relaxing the explanation. That is, the implied bound can be delta weaker the the computed value. \remark the is_lower parameter is a little bit counterintuitive. It says if the other monomials imply a lower (upper) bound for the monomial at position idx. Store the result in 'antecedent' */ template void theory_arith::explain_bound(row const & r, int idx, bool is_lower, inf_numeral & delta, antecedents& ante) { SASSERT(delta >= inf_numeral::zero()); TRACE("arith_conflict", tout << "relax: " << relax_bounds() << " lits: " << ante.lits().size() << " eqs: " << ante.eqs().size() << " idx: " << idx << "\n";); if (!relax_bounds() && (!ante.lits().empty() || !ante.eqs().empty())) { return; } context & ctx = get_context(); row_entry const & entry = r[idx]; numeral coeff = entry.m_coeff; if (relax_bounds()) { // if the variable v at position idx can have a delta increase (decrease) of 'delta', then // the monomial (coeff * v) at position idx can have a delta increase (decrease) of '|coeff| * delta' if (coeff.is_neg()) coeff.neg(); delta *= coeff; // adjust delta } typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); TRACE("arith_proof", display_row(tout, r, false); ); for (int idx2 = 0; it != end; ++it, ++idx2) { if (!it->is_dead() && idx != idx2) { bound * b = get_bound(it->m_var, is_lower ? it->m_coeff.is_pos() : it->m_coeff.is_neg()); SASSERT(b); if (!b->has_justification()) { continue; } if (!relax_bounds() || delta.is_zero()) { TRACE("propagate_bounds", tout << "push justification: v" << it->m_var << "\n";); b->push_justification(ante, it->m_coeff, coeffs_enabled()); continue; } numeral coeff = it->m_coeff; bool is_b_lower = b->get_bound_kind() == B_LOWER; if (coeff.is_neg()) coeff.neg(); numeral inv_coeff(1); inv_coeff /= coeff; inf_numeral k_1 = b->get_value(); inf_numeral limit_k1; // if the max decrease (increase) of the curr monomial (coeff * v2) is delta, then // the maximal decrease (increase) of v2 is (1/|coeff| * delta) if (is_b_lower) { limit_k1 = k_1; // limit_k1 -= delta * coeff; limit_k1.submul(inv_coeff, delta); } else { limit_k1 = k_1; // limit_k1 += delta * coeff; limit_k1.addmul(inv_coeff, delta); } TRACE("propagate_bounds_bug", tout << "is_b_lower: " << is_b_lower << " k1: " << k_1 << " limit_k1: " << limit_k1 << " delta: " << delta << " coeff: " << coeff << "\n";); inf_numeral k_2 = k_1; atom * new_atom = nullptr; atoms const & as = m_var_occs[it->m_var]; for (atom * a : as) { if (a == b) continue; bool_var bv = a->get_bool_var(); lbool val = ctx.get_assignment(bv); if (val == l_undef) continue; // TODO: check if the following line is a bottleneck TRACE("arith", tout << "v" << a->get_bool_var() << " " << (val == l_true) << "\n";); a->assign_eh(val == l_true, get_epsilon(a->get_var())); if (val != l_undef && a->get_bound_kind() == b->get_bound_kind()) { SASSERT((ctx.get_assignment(bv) == l_true) == a->is_true()); inf_numeral a_val = a->get_value(); if (is_b_lower) { if (a_val >= limit_k1 && a_val < k_2) { k_2 = a_val; new_atom = a; } } else { if (a_val <= limit_k1 && a_val > k_2) { k_2 = a_val; new_atom = a; } } } } SASSERT(!is_b_lower || k_2 <= k_1); SASSERT(is_b_lower || k_2 >= k_1); if (new_atom == nullptr) { b->push_justification(ante, coeff, coeffs_enabled()); continue; } SASSERT(!is_b_lower || k_2 < k_1); SASSERT(is_b_lower || k_2 > k_1); if (is_b_lower) { TRACE("propagate_bounds", tout << "coeff: " << coeff << ", k_1 - k_2: " << k_1 - k_2 << ", delta: " << delta << "\n";); delta -= coeff*(k_1 - k_2); } else { TRACE("propagate_bounds", tout << "coeff: " << coeff << ", k_2 - k_1: " << k_2 - k_1 << ", delta: " << delta << "\n";); delta -= coeff*(k_2 - k_1); } TRACE("propagate_bounds", tout << "delta (after replace): " << delta << "\n";); new_atom->push_justification(ante, coeff, coeffs_enabled()); SASSERT(delta >= inf_numeral::zero()); } } } template void theory_arith::mk_implied_bound(row const & r, unsigned idx, bool is_lower, theory_var v, bound_kind kind, inf_numeral const & k) { atoms const & as = m_var_occs[v]; inf_numeral const & epsilon = get_epsilon(v); inf_numeral delta; for (atom* a : as) { bool_var bv = a->get_bool_var(); literal l(bv); if (get_context().get_assignment(bv) == l_undef) { inf_numeral const & k2 = a->get_k(); delta.reset(); if (a->get_atom_kind() == A_LOWER) { // v >= k k >= k2 |- v >= k2 if (kind == B_LOWER && k >= k2) { if (relax_bounds()) { delta = k; delta -= k2; } TRACE("propagate_bounds", tout << "v" << v << " >= " << k << ", v" << v << " >= " << k2 << ", delta: " << delta << "\n"; display_row(tout, r);); assign_bound_literal(l, r, idx, is_lower, delta); } // v <= k k < k2 |- v < k2 |- not v >= k2 if (kind == B_UPPER && k < k2) { // it is not sufficient to check whether k < k2. // example: // k = -1/5*epsilon // k2 = 0 // Thus, v <= -1/5*epsilon // (not v >= 0) which is equivalent to v <= -epsilon. delta = k2; delta -= k; delta -= epsilon; if (delta.is_nonneg()) { TRACE("propagate_bounds", tout << "v" << v << " <= " << k << ", not v" << v << " >= " << k2 << ", delta: " << delta << "\n"; display_row(tout, r);); assign_bound_literal(~l, r, idx, is_lower, delta); } } } else { // v >= k k > k2 |- v > k2 |- not v <= k2 if (kind == B_LOWER && k > k2) { // it is not sufficient to check whether k > k2. // see example above. delta = k; delta -= k2; delta -= epsilon; if (delta.is_nonneg()) { TRACE("propagate_bounds", tout << "v" << v << " >= " << k << ", not v" << v << " <= " << k2 << ", delta: " << delta << "\n"; display_row(tout, r);); assign_bound_literal(~l, r, idx, is_lower, delta); } } // v <= k k <= k2 |- v <= k2 if (kind == B_UPPER && k <= k2) { if (relax_bounds()) { delta = k2; delta -= k; } TRACE("propagate_bounds", tout << "v" << v << " <= " << k << ", v" << v << " <= " << k2 << ", delta: " << delta << "\n"; display_row(tout, r);); assign_bound_literal(l, r, idx, is_lower, delta); } } } } } template void theory_arith::dump_lemmas(literal l, antecedents const& ante) { context & ctx = get_context(); if (dump_lemmas()) { TRACE("arith", ante.display(tout) << " --> "; ctx.display_detailed_literal(tout, l); tout << "\n";); ctx.display_lemma_as_smt_problem(ante.lits().size(), ante.lits().c_ptr(), ante.eqs().size(), ante.eqs().c_ptr(), l); } } template void theory_arith::dump_lemmas(literal l, derived_bound const& ante) { context & ctx = get_context(); if (dump_lemmas()) { ctx.display_lemma_as_smt_problem(ante.lits().size(), ante.lits().c_ptr(), ante.eqs().size(), ante.eqs().c_ptr(), l); } } template void theory_arith::assign_bound_literal(literal l, row const & r, unsigned idx, bool is_lower, inf_numeral & delta) { m_stats.m_bound_props++; context & ctx = get_context(); antecedents ante(*this); explain_bound(r, idx, is_lower, delta, ante); TRACE("propagate_bounds", ante.display(tout) << " --> "; ctx.display_detailed_literal(tout, l); tout << "\n";); dump_lemmas(l, ante); if (ante.lits().size() < small_lemma_size() && ante.eqs().empty()) { literal_vector & lits = m_tmp_literal_vector2; lits.reset(); lits.push_back(l); literal_vector::const_iterator it = ante.lits().begin(); literal_vector::const_iterator end = ante.lits().end(); for (; it != end; ++it) lits.push_back(~(*it)); justification * js = nullptr; if (proofs_enabled()) { js = alloc(theory_lemma_justification, get_id(), ctx, lits.size(), lits.c_ptr(), ante.num_params(), ante.params("assign-bounds")); } ctx.mk_clause(lits.size(), lits.c_ptr(), js, CLS_TH_LEMMA, nullptr); } else { region & r = ctx.get_region(); ctx.assign(l, ctx.mk_justification( ext_theory_propagation_justification( get_id(), r, ante.lits().size(), ante.lits().c_ptr(), ante.eqs().size(), ante.eqs().c_ptr(), l, ante.num_params(), ante.params("assign-bounds")))); } } /** \brief Traverse rows in m_to_check and try do derive improved bounds for the variables occurring in them. */ template void theory_arith::propagate_bounds() { TRACE("propagate_bounds_detail", display(tout);); typename svector::iterator it = m_to_check.begin(); typename svector::iterator end = m_to_check.end(); for (; it != end; ++it) { row & r = m_rows[*it]; if (r.get_base_var() != null_theory_var) { if (r.size() < max_lemma_size()) { // Ignore big rows. int lower_idx; int upper_idx; is_row_useful_for_bound_prop(r, lower_idx, upper_idx); if (lower_idx >= 0) { imply_bound_for_monomial(r, lower_idx, true); } else if (lower_idx == -1) { imply_bound_for_all_monomials(r, true); } if (upper_idx >= 0) { imply_bound_for_monomial(r, upper_idx, false); } else if (upper_idx == -1) { imply_bound_for_all_monomials(r, false); } // sneaking cheap eq detection in this loop propagate_cheap_eq(*it); } #if 0 theory_var v = r.get_base_var(); if (!is_int(v) || get_value(v).is_int()) { // If an integer value is not assigned to an integer value, then // bound propagation can diverge. m_in_to_check.remove(v); } #endif } } m_to_check.reset(); m_in_to_check.reset(); } // ----------------------------------- // // Justification // // ----------------------------------- template void theory_arith::set_conflict(antecedents const& ante, antecedents& bounds, char const* proof_rule) { set_conflict(ante.lits().size(), ante.lits().c_ptr(), ante.eqs().size(), ante.eqs().c_ptr(), bounds, proof_rule); dump_lemmas(false_literal, ante); } template void theory_arith::set_conflict(derived_bound const& ante, antecedents& bounds, char const* proof_rule) { set_conflict(ante.lits().size(), ante.lits().c_ptr(), ante.eqs().size(), ante.eqs().c_ptr(), bounds, proof_rule); dump_lemmas(false_literal, ante); } template void theory_arith::set_conflict(unsigned num_literals, literal const * lits, unsigned num_eqs, enode_pair const * eqs, antecedents& bounds, char const* proof_rule) { SASSERT(num_literals != 0 || num_eqs != 0); context & ctx = get_context(); m_stats.m_conflicts++; m_num_conflicts++; TRACE("arith_conflict", tout << "scope: " << ctx.get_scope_level() << "\n"; for (unsigned i = 0; i < num_literals; i++) { ctx.display_detailed_literal(tout, lits[i]); tout << " "; if (coeffs_enabled()) { tout << "bound: " << bounds.lit_coeffs()[i] << "\n"; } tout << "\n"; } for (unsigned i = 0; i < num_eqs; i++) { tout << "#" << eqs[i].first->get_owner_id() << "=#" << eqs[i].second->get_owner_id() << " "; if (coeffs_enabled()) { tout << "bound: " << bounds.eq_coeffs()[i] << "\n"; } } for (unsigned i = 0; i < bounds.num_params(); ++i) { tout << bounds.params(proof_rule)[i] << "\n"; } tout << "\n";); record_conflict(num_literals, lits, num_eqs, eqs, bounds.num_params(), bounds.params(proof_rule)); ctx.set_conflict( ctx.mk_justification( ext_theory_conflict_justification(get_id(), ctx.get_region(), num_literals, lits, num_eqs, eqs, bounds.num_params(), bounds.params(proof_rule)))); } /** \brief Collect the proofs for the fixed variables in the given row. Store the proofs in result. */ template void theory_arith::collect_fixed_var_justifications(row const & r, antecedents& antecedents) const { for (row_entry const& re : r) { if (!re.is_dead() && is_fixed(re.m_var)) { lower(re.m_var)->push_justification(antecedents, re.m_coeff, coeffs_enabled()); upper(re.m_var)->push_justification(antecedents, re.m_coeff, coeffs_enabled()); } } } // ----------------------------------- // // Model generation // // ----------------------------------- // // The arithmetic module uses infinitesimals. So, // an inf_numeral (n,k) represents n + k*epsilon // where epsilon is a very small number. // In order to generate a model, we need to compute // a value for epsilon in a way all bounds remain // satisfied. // // 1) Handling inequalities: (n1, k1) <= (n2, k2) // // The only interesting case is n1 < n2 and k1 > k2. // Using the definition of infinitesimal numbers // we have: // n1 + k1 * epsilon <= n2 + k2 - epsilon // Therefore: // epsilon <= (n2 - n1) / (k1 - k2) // // Starting at Z3 V2.0, we split disequalities. // So, we do not need to handle them. If we decide // to support them again in the future: // // 2) Handling disequalities: (n1, k1) /= n2 // // case a) k1 is positive and n1 < n2 // Thus, epsilon < (n2 - n1) / k1 // => epsilon <= (n2 - n1) / 2*k1 // // case b) k1 is negative and n1 > n2 // Similarly, epsilon <= (n2 - n1) / 2*k1 // /** \brief Update the value of epsilon using the inequality l <= u */ template void theory_arith::update_epsilon(const inf_numeral & l, const inf_numeral & u) { if (l.get_rational() < u.get_rational() && l.get_infinitesimal() > u.get_infinitesimal()) { numeral new_epsilon = (u.get_rational() - l.get_rational()) / (l.get_infinitesimal() - u.get_infinitesimal()); if (new_epsilon < m_epsilon) { m_epsilon = new_epsilon; } } SASSERT(m_epsilon.is_pos()); } template void theory_arith::compute_epsilon() { m_epsilon = numeral(1); theory_var num = get_num_vars(); for (theory_var v = 0; v < num; v++) { bound * l = lower(v); bound * u = upper(v); if (l != nullptr) update_epsilon(l->get_value(), get_value(v)); if (u != nullptr) update_epsilon(get_value(v), u->get_value()); } TRACE("epsilon_bug", tout << "epsilon: " << m_epsilon << "\n";); } /** The epsilon computed by compute_epsilon may accidentally make two shared variables to have the same assignment. This method keeps dividing epsilon by 2 until this "clash" does not occur. Here is an example of the problem Assignment: x -> 9.5 y -> 10 - epsilon x and y have different assignments. However, if compute_epsilon sets epsilon to 0.5, then x and y become 9.5. However, an equality is not propagated to the core since in the assignment above they are assigned to distinct values. This bug was reported by Marcello Bersani. Remark: this is not really a soundness bug. The result sat/unsat produced by Z3 was still correct. However, the model construction was incorrect. Perhaps, this explains why this bug was not detected before. */ template void theory_arith::refine_epsilon() { while (true) { rational2var mapping; theory_var num = get_num_vars(); bool refine = false; for (theory_var v = 0; v < num; v++) { if (is_int_src(v)) continue; if (!get_context().is_shared(get_enode(v))) continue; inf_numeral const & val = get_value(v); if (Ext::is_infinite(val)) { continue; } rational value = val.get_rational().to_rational() + m_epsilon.to_rational() * val.get_infinitesimal().to_rational(); theory_var v2; if (mapping.find(value, v2)) { SASSERT(!is_int_src(v2)); if (get_value(v) != get_value(v2)) { // v and v2 are not known to be equal. // The choice of m_epsilon is making them equal. TRACE("refine_epsilon", tout << "v" << v << " v" << v2 << " " << get_value(v) << " " << get_value(v2) << " " << value << std::endl; ); refine = true; break; } } else { mapping.insert(value, v); } } if (!refine) return; numeral two(2); m_epsilon = m_epsilon / two; TRACE("refine_epsilon", tout << "new epsilon..." << m_epsilon << std::endl;); } } template void theory_arith::init_model(model_generator & m) { TRACE("theory_arith", tout << "init model invoked...\n"; for (app* n : m_underspecified_ops) { tout << mk_pp(n, get_manager()) << "\n"; }); m_factory = alloc(arith_factory, get_manager()); m.register_factory(m_factory); compute_epsilon(); if (!m_model_depends_on_computed_epsilon) { refine_epsilon(); } } template bool theory_arith::include_func_interp(func_decl* f) { return m_util.is_div0(f) || m_util.is_idiv0(f) || m_util.is_power0(f) || m_util.is_rem0(f) || m_util.is_mod0(f); } template model_value_proc * theory_arith::mk_value(enode * n, model_generator & mg) { theory_var v = n->get_th_var(get_id()); SASSERT(v != null_theory_var); inf_numeral const & val = get_value(v); rational num = val.get_rational().to_rational() + m_epsilon.to_rational() * val.get_infinitesimal().to_rational(); if (is_int(v) && !num.is_int()) { TRACE("arith", tout << "Truncating non-integer value. This is possible for non-linear constraints v" << v << " " << num << "\n";); num = floor(num); } return alloc(expr_wrapper_proc, m_factory->mk_num_value(num, m_util.is_int(var2expr(v)))); } // ----------------------------------- // // Model checker support // // ----------------------------------- template bool theory_arith::to_expr(inf_numeral const& val, bool is_int, expr_ref & r) { if (val.get_infinitesimal().is_zero()) { numeral _val = val.get_rational(); r = m_util.mk_numeral(_val.to_rational(), is_int); return true; } else { return false; } } template bool theory_arith::get_value(enode * n, expr_ref & r) { theory_var v = n->get_th_var(get_id()); inf_numeral val; return v != null_theory_var && (val = get_value(v), (!is_int(v) || val.is_int())) && to_expr(val, is_int(v), r); } template bool theory_arith::get_lower(enode * n, expr_ref & r) { theory_var v = n->get_th_var(get_id()); bound* b = (v == null_theory_var) ? nullptr : lower(v); return b && to_expr(b->get_value(), is_int(v), r); } template bool theory_arith::get_upper(enode * n, expr_ref & r) { theory_var v = n->get_th_var(get_id()); bound* b = (v == null_theory_var) ? nullptr : upper(v); return b && to_expr(b->get_value(), is_int(v), r); } template bool theory_arith::get_lower(enode * n, rational& r, bool& is_strict) { theory_var v = n->get_th_var(get_id()); bound* b = (v == null_theory_var) ? nullptr : lower(v); return b && (r = b->get_value().get_rational().to_rational(), is_strict = b->get_value().get_infinitesimal().is_pos(), true); } template bool theory_arith::get_upper(enode * n, rational& r, bool& is_strict) { theory_var v = n->get_th_var(get_id()); bound* b = (v == null_theory_var) ? nullptr : upper(v); return b && (r = b->get_value().get_rational().to_rational(), is_strict = b->get_value().get_infinitesimal().is_neg(), true); } // ----------------------------------- // // Backtracking // // ----------------------------------- template void theory_arith::push_scope_eh() { theory::push_scope_eh(); m_scopes.push_back(scope()); scope & s = m_scopes.back(); s.m_atoms_lim = m_atoms.size(); s.m_bound_trail_lim = m_bound_trail.size(); s.m_unassigned_atoms_trail_lim = m_unassigned_atoms_trail.size(); s.m_asserted_bounds_lim = m_asserted_bounds.size(); s.m_asserted_qhead_old = m_asserted_qhead; s.m_bounds_to_delete_lim = m_bounds_to_delete.size(); s.m_nl_monomials_lim = m_nl_monomials.size(); s.m_nl_propagated_lim = m_nl_propagated.size(); CASSERT("arith", wf_rows()); CASSERT("arith", wf_columns()); CASSERT("arith", valid_row_assignment()); CASSERT("arith", satisfy_bounds()); } template void theory_arith::pop_scope_eh(unsigned num_scopes) { CASSERT("arith", wf_rows()); CASSERT("arith", wf_columns()); CASSERT("arith", valid_row_assignment()); TRACE("arith_pop_scope_bug", display(tout);); // The m_update_trail_stack may not be empty. // In an old version, only propagate_core and methods invoked by propagate_core were // inserting elements in the m_update_trail_stack stack. // This is not true anymore. The method quasi_base_row2base_row also does that. // So, restore_assignment must be invoked. In most cases, it is a noop since the stack is empty. restore_assignment(); m_to_patch.reset(); unsigned lvl = m_scopes.size(); SASSERT(num_scopes <= lvl); unsigned new_lvl = lvl - num_scopes; scope & s = m_scopes[new_lvl]; restore_bounds(s.m_bound_trail_lim); restore_unassigned_atoms(s.m_unassigned_atoms_trail_lim); m_asserted_bounds.shrink(s.m_asserted_bounds_lim); m_asserted_qhead = s.m_asserted_qhead_old; TRACE("arith_pop_scope_bug", tout << "num_vars: " << get_num_vars() << ", num_old_vars: " << get_old_num_vars(num_scopes) << "\n";); restore_nl_propagated_flag(s.m_nl_propagated_lim); m_nl_monomials.shrink(s.m_nl_monomials_lim); del_atoms(s.m_atoms_lim); del_bounds(s.m_bounds_to_delete_lim); del_vars(get_old_num_vars(num_scopes)); m_scopes.shrink(new_lvl); theory::pop_scope_eh(num_scopes); VERIFY(make_feasible()); SASSERT(m_to_patch.empty()); m_to_check.reset(); m_in_to_check.reset(); m_new_atoms.reset(); CASSERT("arith", wf_rows()); CASSERT("arith", wf_columns()); CASSERT("arith", valid_row_assignment()); CASSERT("arith", satisfy_bounds()); } template void theory_arith::restore_nl_propagated_flag(unsigned old_trail_size) { typename svector::iterator begin = m_nl_propagated.begin() + old_trail_size; typename svector::iterator it = m_nl_propagated.end(); while (it != begin) { --it; SASSERT(m_data[*it].m_nl_propagated); m_data[*it].m_nl_propagated = false; } m_nl_propagated.shrink(old_trail_size); } template void theory_arith::restore_bounds(unsigned old_trail_size) { CASSERT("arith", wf_rows()); typename svector::iterator begin = m_bound_trail.begin() + old_trail_size; typename svector::iterator it = m_bound_trail.end(); while (it != begin) { --it; theory_var v = it->get_var(); bound * b = it->get_old_bound(); SASSERT(is_base(v) || is_non_base(v)); restore_bound(v, b, it->is_upper()); if (lazy_pivoting_lvl() > 2 && b == nullptr && is_base(v) && is_free(v)) { SASSERT(!has_var_kind(get_var_row(v), BASE)); SASSERT(!has_var_kind(get_var_row(v), QUASI_BASE)); eliminate(v, false); SASSERT(m_columns[v].size() == 1); SASSERT(!has_var_kind(get_var_row(v), BASE)); SASSERT(!has_var_kind(get_var_row(v), QUASI_BASE)); set_var_kind(v, QUASI_BASE); } } m_bound_trail.shrink(old_trail_size); CASSERT("arith", wf_rows()); } template void theory_arith::restore_unassigned_atoms(unsigned old_trail_size) { svector::iterator begin = m_unassigned_atoms_trail.begin() + old_trail_size; svector::iterator it = m_unassigned_atoms_trail.end(); while (it != begin) { --it; m_unassigned_atoms[*it]++; } m_unassigned_atoms_trail.shrink(old_trail_size); } template void theory_arith::del_atoms(unsigned old_size) { typename atoms::iterator begin = m_atoms.begin() + old_size; typename atoms::iterator it = m_atoms.end(); while (it != begin) { --it; atom * a = *it; theory_var v = a->get_var(); bool_var bv = a->get_bool_var(); erase_bv2a(bv); SASSERT(m_var_occs[v].back() == a); m_var_occs[v].pop_back(); dealloc(a); } m_atoms.shrink(old_size); } template void theory_arith::del_bounds(unsigned old_size) { typename ptr_vector::iterator begin = m_bounds_to_delete.begin() + old_size; typename ptr_vector::iterator it = m_bounds_to_delete.end(); while (it != begin) { --it; bound * b = *it; dealloc(b); } m_bounds_to_delete.shrink(old_size); } template void theory_arith::del_vars(unsigned old_num_vars) { int num_vars = get_num_vars(); SASSERT(num_vars >= static_cast(old_num_vars)); if (num_vars != static_cast(old_num_vars)) { theory_var v = num_vars; while (v > static_cast(old_num_vars)) { --v; switch (get_var_kind(v)) { case QUASI_BASE: SASSERT(m_columns[v].size() == 1); del_row(get_var_row(v)); TRACE("arith_make_feasible", tout << "del row v" << v << "\n";); break; case BASE: SASSERT(lazy_pivoting_lvl() != 0 || m_columns[v].size() == 1); if (lazy_pivoting_lvl() > 0) eliminate(v, false); TRACE("arith_make_feasible", tout << "del row v" << v << "\n";); del_row(get_var_row(v)); break; case NON_BASE: { col_entry const * entry = get_a_base_row_that_contains(v); if (entry) { row & r = m_rows[entry->m_row_id]; SASSERT(is_base(r.get_base_var())); SASSERT(r[entry->m_row_idx].m_var == v); pivot(r.get_base_var(), v, r[entry->m_row_idx].m_coeff, false); SASSERT(is_base(v)); del_row(get_var_row(v)); TRACE("arith_make_feasible", tout << "del row v" << v << "\n";); } else { TRACE("arith_make_feasible", tout << "no row v" << v << "\n";); } break; } } m_in_update_trail_stack.remove(v); m_left_basis.remove(v); m_in_to_check.remove(v); } m_columns .shrink(old_num_vars); m_data .shrink(old_num_vars); m_value .shrink(old_num_vars); m_old_value .shrink(old_num_vars); m_var_occs .shrink(old_num_vars); m_unassigned_atoms.shrink(old_num_vars); m_var_pos .shrink(old_num_vars); m_bounds[0] .shrink(old_num_vars); m_bounds[1] .shrink(old_num_vars); SASSERT(check_vector_sizes()); } SASSERT(m_var_occs.size() == old_num_vars); } template void theory_arith::del_row(unsigned r_id) { row & r = m_rows[r_id]; for (row_entry const& re : r) { if (!re.is_dead()) { column & c = m_columns[re.m_var]; c.del_col_entry(re.m_col_idx); } } r.m_base_var = null_theory_var; r.reset(); m_dead_rows.push_back(r_id); } /** \brief reset and retrieve built-in explanation hints for arithmetic lemmas. */ template theory_arith::antecedents::antecedents(theory_arith& th): th(th), a(th.m_antecedents[th.m_antecedents_index]) { SASSERT(th.m_antecedents_index < 3); a.reset(); ++th.m_antecedents_index; } template theory_arith::antecedents::~antecedents() { --th.m_antecedents_index; } }; #endif /* THEORY_ARITH_CORE_H_ */ z3-z3-4.8.7/src/smt/theory_arith_def.h000066400000000000000000000010451356505360400175110ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_arith_def.h Abstract: Author: Leonardo de Moura (leonardo) 2008-04-22. Revision History: --*/ #ifndef THEORY_ARITH_DEF_H_ #define THEORY_ARITH_DEF_H_ #include "smt/theory_arith.h" #include "smt/theory_arith_core.h" #include "smt/theory_arith_aux.h" #include "smt/theory_arith_inv.h" #include "smt/theory_arith_pp.h" #include "smt/theory_arith_int.h" #include "smt/theory_arith_eq.h" #include "smt/theory_arith_nl.h" #endif /* THEORY_ARITH_DEF_H_ */ z3-z3-4.8.7/src/smt/theory_arith_eq.h000066400000000000000000000327371356505360400173740ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_arith_eq.h Abstract: Author: Leonardo de Moura (leonardo) 2008-06-22. Revision History: --*/ #ifndef THEORY_ARITH_EQ_H_ #define THEORY_ARITH_EQ_H_ // #define PROFILE_OFFSET_ROW #ifdef PROFILE_OFFSET_ROW #include "util/stopwatch.h" #undef max #undef min #endif namespace smt { /** \brief This method is invoked when a variable was non fixed and become fixed. */ template void theory_arith::fixed_var_eh(theory_var v) { if (!propagate_eqs()) return; SASSERT(is_fixed(v)); // WARNINING: it is not safe to use get_value(v) here, since // get_value(v) may not satisfy v bounds at this point. CTRACE("arith_bug", !lower_bound(v).is_rational(), display_var(tout, v);); SASSERT(lower_bound(v).is_rational()); numeral const & val = lower_bound(v).get_rational(); value_sort_pair key(val, is_int_src(v)); TRACE("arith_eq", tout << mk_pp(get_enode(v)->get_owner(), get_manager()) << " = " << val << "\n";); theory_var v2; if (m_fixed_var_table.find(key, v2)) { if (v2 < static_cast(get_num_vars()) && is_fixed(v2) && lower_bound(v2).get_rational() == val) { // It only makes sense to propagate equality to the core when v and v2 have the same sort. // The table m_fixed_var_table is not restored during backtrack. So, it may // contain invalid (key -> value) pairs. So, we must check whether v2 is really equal to val (previous test) AND it has // the same sort of v. The following test was missing in a previous version of Z3. if (!is_equal(v, v2) && is_int_src(v) == is_int_src(v2)) { antecedents ante(*this); // // v <= k <= v2 => v <= v2 // v >= k >= v2 => v >= v2 // lower(v)->push_justification(ante, numeral::zero(), proofs_enabled()); upper(v2)->push_justification(ante, numeral::zero(), proofs_enabled()); lower(v2)->push_justification(ante, numeral::zero(), proofs_enabled()); upper(v)->push_justification(ante, numeral::zero(), proofs_enabled()); TRACE("arith_eq", tout << "propagate eq: v" << v << " = v" << v2 << "\n"; display_var(tout, v); display_var(tout, v2);); m_stats.m_fixed_eqs++; propagate_eq_to_core(v, v2, ante); } } else { // the original fixed variable v2 was deleted or its bounds were removed // during backtracking. m_fixed_var_table.erase(key); m_fixed_var_table.insert(key, v); } } else { m_fixed_var_table.insert(key, v); } } /** \brief Returns true if r is a offset row. A offset row is a row that can be written as: x = y + M where x and y are non fixed variables, and M is linear polynomials where all variables are fixed, and M evaluates to k. When true is returned, x, y and k are stored in the given arguments. \remark The following rule is used to select x and y. - if the base variable is not fixed, then x is the base var. - otherwise x is the smallest var. */ template bool theory_arith::is_offset_row(row const & r, theory_var & x, theory_var & y, numeral & k) const { #ifdef PROFILE_OFFSET_ROW static stopwatch timer; static unsigned total = 0; static unsigned ok = 0; timer.start(); total ++; #endif // Quick check without using big numbers... // Check if there are more than 2 unbounded vars. unsigned bad = 0; typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (!it->is_dead()) { theory_var v = it->m_var; if (lower(v) != nullptr && upper(v) != nullptr) continue; bad++; if (bad > 2) { #ifdef PROFILE_OFFSET_ROW timer.stop(); #endif return false; } } } // Full check using == for big numbers... x = null_theory_var; y = null_theory_var; it = r.begin_entries(); for (; it != end; ++it) { if (!it->is_dead()) { theory_var v = it->m_var; if (is_fixed(v)) continue; if (it->m_coeff.is_one() && x == null_theory_var) { x = v; continue; } if (it->m_coeff.is_minus_one() && y == null_theory_var) { y = v; continue; } #ifdef PROFILE_OFFSET_ROW timer.stop(); #endif return false; } } if (x == null_theory_var && y == null_theory_var) { #ifdef PROFILE_OFFSET_ROW timer.stop(); #endif return false; } k.reset(); it = r.begin_entries(); for (; it != end; ++it) { if (!it->is_dead()) { theory_var v = it->m_var; if (v == x || v == y) continue; SASSERT(is_fixed(v)); k -= it->m_coeff * lower_bound(v).get_rational(); } } #ifdef PROFILE_OFFSET_ROW timer.stop(); ok++; if (ok % 100000 == 0) { TRACE("arith_eq", tout << total << " " << ok << " " << static_cast(ok)/static_cast(total) << " " << timer.get_seconds() << "\n"; tout.flush();); } #endif if (y == null_theory_var) return true; if (x == null_theory_var) { std::swap(x, y); k.neg(); SASSERT(x != null_theory_var); return true; } if (r.get_base_var() != x && x > y) { std::swap(x, y); k.neg(); } return true; } /** \brief Cheap propagation of equalities x_i = x_j, when x_i = y + k x_j = y + k This equalities are detected by maintaining a map: (y, k) -> row_id when a row is of the form x = y + k This methods checks whether the given row is an offset row (See is_offset_row), and uses the map to find new equalities if that is the case. */ template void theory_arith::propagate_cheap_eq(unsigned rid) { if (!propagate_eqs()) return; TRACE("arith_eq_verbose", tout << "checking if row " << rid << " can propagate equality.\n"; display_row_info(tout, rid);); row const & r = m_rows[rid]; theory_var x; theory_var y; numeral k; if (is_offset_row(r, x, y, k)) { if (y == null_theory_var) { // x is an implied fixed var at k. value_sort_pair key(k, is_int_src(x)); theory_var x2; if (m_fixed_var_table.find(key, x2) && x2 < static_cast(get_num_vars()) && is_fixed(x2) && lower_bound(x2).get_rational() == k && // We must check whether x2 is an integer. // The table m_fixed_var_table is not restored during backtrack. So, it may // contain invalid (key -> value) pairs. // So, we must check whether x2 is really equal to k (previous test) // AND has the same sort of x. // The following test was missing in a previous version of Z3. is_int_src(x) == is_int_src(x2) && !is_equal(x, x2)) { antecedents ante(*this); collect_fixed_var_justifications(r, ante); // // x1 <= k1 x1 >= k1, x2 <= x1 + k2 x2 >= x1 + k2 // TRACE("arith_eq_propagation", tout << "fixed\n";); lower(x2)->push_justification(ante, numeral::zero(), proofs_enabled()); upper(x2)->push_justification(ante, numeral::zero(), proofs_enabled()); m_stats.m_fixed_eqs++; propagate_eq_to_core(x, x2, ante); } } if (k.is_zero() && y != null_theory_var && !is_equal(x, y) && is_int_src(x) == is_int_src(y)) { // found equality x = y antecedents ante(*this); collect_fixed_var_justifications(r, ante); TRACE("arith_eq", tout << "propagate eq using x-y=0 row:\n"; display_row_info(tout, r);); m_stats.m_offset_eqs++; propagate_eq_to_core(x, y, ante); } int row_id; var_offset key(y, k); if (m_var_offset2row_id.find(key, row_id)) { row & r2 = m_rows[row_id]; if (r.get_base_var() == r2.get_base_var()) { // it is the same row. return; } theory_var x2; theory_var y2; numeral k2; if (r2.get_base_var() != null_theory_var && is_offset_row(r2, x2, y2, k2)) { bool new_eq = false; #ifdef _TRACE bool swapped = false; #endif if (y == y2 && k == k2) { new_eq = true; } else if (y2 != null_theory_var) { #ifdef _TRACE swapped = true; #endif std::swap(x2, y2); k2.neg(); if (y == y2 && k == k2) { new_eq = true; } } if (new_eq) { if (!is_equal(x, x2) && is_int_src(x) == is_int_src(x2)) { SASSERT(y == y2 && k == k2); antecedents ante(*this); collect_fixed_var_justifications(r, ante); collect_fixed_var_justifications(r2, ante); TRACE("arith_eq", tout << "propagate eq two rows:\n"; tout << "swapped: " << swapped << "\n"; tout << "x : v" << x << "\n"; tout << "x2 : v" << x2 << "\n"; display_row_info(tout, r); display_row_info(tout, r2);); m_stats.m_offset_eqs++; propagate_eq_to_core(x, x2, ante); } return; } } // the original row was delete or it is not offset row anymore ===> remove it from table m_var_offset2row_id.erase(key); } // add new entry m_var_offset2row_id.insert(key, rid); } } template void theory_arith::propagate_eq_to_core(theory_var x, theory_var y, antecedents& antecedents) { // Ignore equality if variables are already known to be equal. ast_manager& m = get_manager(); if (is_equal(x, y)) return; // I doesn't make sense to propagate an equality (to the core) of variables of different sort. if (m.get_sort(var2expr(x)) != m.get_sort(var2expr(y))) { TRACE("arith", tout << mk_pp(var2expr(x), m) << " = " << mk_pp(var2expr(y), m) << "\n";); return; } context & ctx = get_context(); region & r = ctx.get_region(); enode * _x = get_enode(x); enode * _y = get_enode(y); eq_vector const& eqs = antecedents.eqs(); literal_vector const& lits = antecedents.lits(); justification * js = ctx.mk_justification( ext_theory_eq_propagation_justification( get_id(), r, lits.size(), lits.c_ptr(), eqs.size(), eqs.c_ptr(), _x, _y, antecedents.num_params(), antecedents.params("eq-propagate"))); TRACE("arith_eq", tout << "detected equality: #" << _x->get_owner_id() << " = #" << _y->get_owner_id() << "\n"; display_var(tout, x); display_var(tout, y);); TRACE("arith_eq_propagation", for (unsigned i = 0; i < lits.size(); ++i) { ctx.display_detailed_literal(tout, lits[i]); tout << "\n"; } for (unsigned i = 0; i < eqs.size(); ++i) { tout << mk_pp(eqs[i].first->get_owner(), m) << " = " << mk_pp(eqs[i].second->get_owner(), m) << "\n"; } tout << " ==> "; tout << mk_pp(_x->get_owner(), m) << " = " << mk_pp(_y->get_owner(), m) << "\n"; ); ctx.assign_eq(_x, _y, eq_justification(js)); } }; #endif /* THEORY_ARITH_EQ_H_ */ z3-z3-4.8.7/src/smt/theory_arith_int.h000066400000000000000000001616771356505360400175670ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_arith_int.h Abstract: Author: Leonardo de Moura (leonardo) 2008-06-17. Revision History: --*/ #ifndef THEORY_ARITH_INT_H_ #define THEORY_ARITH_INT_H_ #include "util/numeral_buffer.h" #include "ast/ast_ll_pp.h" #include "ast/well_sorted.h" #include "ast/ast_smt2_pp.h" #include "math/euclid/euclidean_solver.h" namespace smt { // ----------------------------------- // // Integrality // // ----------------------------------- /** \brief Move non base variables to one of its bounds. If the variable does not have bounds, it is integer, but it is not assigned to an integer value, then the variable is set to an integer value. In mixed integer/real problems moving a real variable to a bound could cause an integer value to have an infinitesimal. Such an assignment would disable mk_gomory_cut, and Z3 would loop. */ template void theory_arith::move_non_base_vars_to_bounds() { theory_var num = get_num_vars(); for (theory_var v = 0; v < num; v++) { if (is_non_base(v)) { bound * l = lower(v); bound * u = upper(v); const inf_numeral & val = get_value(v); if (l != nullptr && u != nullptr) { if (val != l->get_value() && val != u->get_value()) set_value(v, l->get_value()); } else if (l != nullptr) { if (val != l->get_value()) set_value(v, l->get_value()); } else if (u != nullptr) { if (val != u->get_value()) set_value(v, u->get_value()); } else { if (is_int(v) && !val.is_int()) { inf_numeral new_val(floor(val)); set_value(v, new_val); } } } } } /** \brief Returns true if the there is an integer variable that is not assigned to an integer value. */ template bool theory_arith::has_infeasible_int_var() { int num = get_num_vars(); for (theory_var v = 0; v < num; v++) { if (is_int(v) && !get_value(v).is_int()) return true; } return false; } /** \brief Find an integer base var that is not assigned to an integer value, but is bounded (i.e., it has lower and upper bounds). Return null_var_id if all integer base variables are assigned to integer values. If there are multiple variables satisfying the condition above, then select the one with the tightest bound. */ template theory_var theory_arith::find_bounded_infeasible_int_base_var() { theory_var result = null_theory_var; numeral range; numeral new_range; numeral small_range_thresold(1024); unsigned n = 0; typename vector::const_iterator it = m_rows.begin(); typename vector::const_iterator end = m_rows.end(); for (; it != end; ++it) { theory_var v = it->get_base_var(); if (v == null_theory_var) continue; if (!is_base(v)) continue; if (!is_int(v)) continue; if (get_value(v).is_int()) continue; if (!is_bounded(v)) continue; numeral const & l = lower_bound(v).get_rational(); numeral const & u = upper_bound(v).get_rational(); new_range = u; new_range -= l; if (new_range > small_range_thresold) continue; if (result == null_theory_var) { result = v; range = new_range; n = 1; continue; } if (new_range < range) { n = 1; result = v; range = new_range; continue; } if (new_range == range) { n++; if (m_random() % n == 0) { result = v; range = new_range; continue; } } } return result; } /** \brief Find an integer base var that is not assigned to an integer value. Return null_var_id if all integer base variables are assigned to integer values. \remark This method gives preference to bounded integer variables. If all variables are unbounded, then it selects a random one. */ template theory_var theory_arith::find_infeasible_int_base_var() { theory_var v = find_bounded_infeasible_int_base_var(); if (v != null_theory_var) { TRACE("find_infeasible_int_base_var", display_var(tout, v);); return v; } unsigned n = 0; theory_var r = null_theory_var; #define SELECT_VAR(VAR) if (r == null_theory_var) { n = 1; r = VAR; } else { n++; SASSERT(n >= 2); if (m_random() % n == 0) r = VAR; } typename vector::const_iterator it = m_rows.begin(); typename vector::const_iterator end = m_rows.end(); for (; it != end; ++it) { theory_var v = it->get_base_var(); if (v != null_theory_var && is_base(v) && is_int(v) && !get_value(v).is_int()) { SELECT_VAR(v); } } if (r == null_theory_var) { it = m_rows.begin(); for (; it != end; ++it) { theory_var v = it->get_base_var(); if (v != null_theory_var && is_quasi_base(v) && is_int(v) && !get_value(v).is_int()) { quasi_base_row2base_row(get_var_row(v)); SELECT_VAR(v); } } if (r == null_theory_var) return null_theory_var; } CASSERT("arith", wf_rows()); CASSERT("arith", wf_columns()); return r; } /** \brief Create "branch and bound" case-split. */ template void theory_arith::branch_infeasible_int_var(theory_var v) { SASSERT(is_int(v)); SASSERT(!get_value(v).is_int()); ast_manager & m = get_manager(); m_stats.m_branches++; numeral k = ceil(get_value(v)); rational _k = k.to_rational(); TRACE("arith_int", tout << "branching v" << v << " = " << get_value(v) << "\n"; display_var(tout, v); tout << "k = " << k << ", _k = "<< _k << std::endl; ); expr_ref bound(m); expr* e = get_enode(v)->get_owner(); bound = m_util.mk_ge(e, m_util.mk_numeral(_k, m_util.is_int(e))); if (m.has_trace_stream()) { app_ref body(m); body = m.mk_or(to_app(bound), m.mk_not(to_app(bound))); log_axiom_instantiation(body); } TRACE("arith_int", tout << mk_bounded_pp(bound, m) << "\n";); context & ctx = get_context(); ctx.internalize(bound, true); ctx.mark_as_relevant(bound.get()); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; } /** \brief Create a "cut from proof" lemma. The set of rows where the base variable is tight are extracted. These row equalities are checked for integer feasiability. If they are not integer feasible, then an integer infeasible equation, that is implied from the extracted equalities is extracted. The extracted equality a*x = 0 is blocked by asserting the disjunction (a*x > 0 \/ a*x < 0) */ template bool theory_arith::branch_infeasible_int_equality() { typename vector::const_iterator it = m_rows.begin(); typename vector::const_iterator end = m_rows.end(); vector > rows; unsigned max_row = 1; // all rows should contain a constant in the last position. u_map var2index; // map theory variables to positions in 'rows'. u_map index2var; // map back positions in 'rows' to theory variables. for (; it != end; ++it) { theory_var b = it->get_base_var(); if (b == null_theory_var) { TRACE("arith_int", display_row(tout << "null: ", *it, true); ); continue; } bool is_tight = false; numeral const_coeff(0); bound* l = lower(b), *u = upper(b); if (l != nullptr && get_value(b) - inf_numeral(1) < l->get_value()) { SASSERT(l->get_value() <= get_value(b)); is_tight = true; const_coeff = l->get_value().get_rational(); } else if (u != nullptr && get_value(b) + inf_numeral(1) > u->get_value()) { SASSERT(get_value(b) <= u->get_value()); is_tight = true; const_coeff = u->get_value().get_rational(); } if (!is_tight) { TRACE("arith_int", display_row(tout << "!tight: ", *it, true); display_var(tout, b); ); continue; } rows.push_back(vector(max_row)); vector& row = rows.back(); numeral denom(1); unsigned index = 0; typename vector::const_iterator it_r = it->begin_entries(); typename vector::const_iterator end_r = it->end_entries(); for (; it_r != end_r && is_tight; ++it_r) { if (it_r->is_dead()) continue; theory_var x = it_r->m_var; if (x == b) continue; numeral coeff = it_r->m_coeff; if (is_fixed(x)) { const_coeff += coeff*lower(x)->get_value().get_rational(); continue; } if (!is_int(x)) { TRACE("theory_arith_int", display_row(tout << "!int: ", *it, true); ); is_tight = false; continue; } if (var2index.find(x, index)) { row[index] = coeff.to_rational(); } else { row.push_back(coeff.to_rational()); var2index.insert(x, max_row); index2var.insert(max_row, x); ++max_row; } numeral tmp_coeff = denominator(coeff); denom = lcm(tmp_coeff, denom); } if (!is_tight) { rows.pop_back(); continue; } row[0] = const_coeff.to_rational(); numeral tmp_const_coeff = denominator(const_coeff); denom = lcm(tmp_const_coeff, denom); if (!denom.is_one()) { for (unsigned i = 0; i < max_row; ++i) { row[i] *= denom.to_rational(); } } TRACE("theory_arith_int", tout << "extracted row:\n"; for (unsigned i = 0; i < max_row; ++i) { tout << row[i] << " "; } tout << " = 0\n"; tout << "base value: " << get_value(b) << "\n"; display_row(tout, *it, true); ); } // // Align the sizes of rows. // The sizes are monotonically increasing. // for (unsigned i = 0; i < rows.size(); ++i) { unsigned sz = rows[i].size(); SASSERT(sz <= max_row); if (sz == max_row) { break; } rows[i].resize(max_row); } vector unsat_row; if (m_arith_eq_solver.solve_integer_equations(rows, unsat_row)) { // The equalities were integer feasiable. return false; } buffer pol; for (unsigned i = 1; i < unsat_row.size(); ++i) { numeral c(unsat_row[i]); if (!c.is_zero()) { theory_var var; if (!index2var.find(i, var)) { UNREACHABLE(); } pol.push_back(row_entry(c, var)); } } if (pol.empty()) { TRACE("theory_arith_int", tout << "The witness is trivial\n";); return false; } expr_ref p1(get_manager()), p2(get_manager()); mk_polynomial_ge(pol.size(), pol.c_ptr(), -unsat_row[0]+rational(1), p1); for (unsigned i = 0; i < pol.size(); ++i) { pol[i].m_coeff.neg(); } mk_polynomial_ge(pol.size(), pol.c_ptr(), unsat_row[0]+rational(1), p2); context& ctx = get_context(); if (get_manager().has_trace_stream()) { app_ref body(get_manager()); body = get_manager().mk_or(p1, p2); log_axiom_instantiation(body); } ctx.internalize(p1, false); ctx.internalize(p2, false); literal l1(ctx.get_literal(p1)), l2(ctx.get_literal(p2)); ctx.mark_as_relevant(p1.get()); ctx.mark_as_relevant(p2.get()); ctx.mk_th_axiom(get_id(), l1, l2); if (get_manager().has_trace_stream()) get_manager().trace_stream() << "[end-of-instance]\n"; TRACE("arith_int", tout << "cut: (or " << mk_pp(p1, get_manager()) << " " << mk_pp(p2, get_manager()) << ")\n"; ); return true; } /** \brief Create bounds for (non base) free vars in the given row. Return true if at least one variable was constrained. This method is used to enable the application of gomory cuts. */ template bool theory_arith::constrain_free_vars(row const & r) { bool result = false; theory_var b = r.get_base_var(); SASSERT(b != null_theory_var); typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (!it->is_dead() && it->m_var != b && is_free(it->m_var)) { theory_var v = it->m_var; expr* e = get_enode(v)->get_owner(); bool _is_int = m_util.is_int(e); expr * bound = m_util.mk_ge(e, m_util.mk_numeral(rational::zero(), _is_int)); context & ctx = get_context(); if (get_manager().has_trace_stream()) log_axiom_instantiation(bound); ctx.internalize(bound, true); if (get_manager().has_trace_stream()) get_manager().trace_stream() << "[end-of-instance]\n"; ctx.mark_as_relevant(bound); result = true; } } return result; } /** \brief Return true if it is possible to apply a gomory cut on the given row. \sa constrain_free_vars */ template bool theory_arith::is_gomory_cut_target(row const & r) { TRACE("gomory_cut", r.display(tout);); theory_var b = r.get_base_var(); typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { // All non base variables must be at their bounds and assigned to rationals (that is, infinitesimals are not allowed). if (!it->is_dead() && it->m_var != b && (!at_bound(it->m_var) || !get_value(it->m_var).is_rational())) { TRACE("gomory_cut", tout << "row is not gomory cut target:\n"; display_var(tout, it->m_var); tout << "at_bound: " << at_bound(it->m_var) << "\n"; tout << "infinitesimal: " << !get_value(it->m_var).is_rational() << "\n";); return false; } } return true; } template void theory_arith::mk_polynomial_ge(unsigned num_args, row_entry const * args, rational const& k, expr_ref & result) { // Remark: the polynomials internalized by theory_arith may not satisfy poly_simplifier_plugin->wf_polynomial assertion. bool all_int = true; for (unsigned i = 0; i < num_args && all_int; ++i) { all_int = is_int(args[i].m_var); } ast_manager & m = get_manager(); expr_ref_vector _args(m); for (unsigned i = 0; i < num_args; i++) { rational _k = args[i].m_coeff.to_rational(); expr * x = get_enode(args[i].m_var)->get_owner(); if (m_util.is_int(x) && !all_int) x = m_util.mk_to_real(x); if (_k.is_one()) _args.push_back(x); else _args.push_back(m_util.mk_mul(m_util.mk_numeral(_k, m_util.is_int(x)), x)); } expr_ref pol(m); pol = m_util.mk_add(_args.size(), _args.c_ptr()); result = m_util.mk_ge(pol, m_util.mk_numeral(k, all_int)); TRACE("arith_mk_polynomial", tout << "before simplification:\n" << result << "\n";); proof_ref pr(m); get_context().get_rewriter()(result, result, pr); TRACE("arith_mk_polynomial", tout << "after simplification:\n" << result << "\n";); SASSERT(is_well_sorted(get_manager(), result)); } template class theory_arith::gomory_cut_justification : public ext_theory_propagation_justification { public: gomory_cut_justification(family_id fid, region & r, unsigned num_lits, literal const * lits, unsigned num_eqs, enode_pair const * eqs, antecedents& bounds, literal consequent): ext_theory_propagation_justification(fid, r, num_lits, lits, num_eqs, eqs, consequent, bounds.num_params(), bounds.params("gomory-cut")) { } // Remark: the assignment must be propagated back to arith theory_id get_from_theory() const override { return null_theory_id; } }; /** \brief Create a gomory cut for the given row. */ template bool theory_arith::mk_gomory_cut(row const & r) { // The following assertion is wrong. It may be violated in mixed-integer problems. // SASSERT(!all_coeff_int(r)); theory_var x_i = r.get_base_var(); SASSERT(is_int(x_i)); // The following assertion is wrong. It may be violated in mixed-real-interger problems. // The check is_gomory_cut_target will discard rows where any variable contains infinitesimals. // SASSERT(m_value[x_i].is_rational()); // infinitesimals are not used for integer variables SASSERT(!m_value[x_i].is_int()); // the base variable is not assigned to an integer value. if (constrain_free_vars(r) || !is_gomory_cut_target(r)) { TRACE("gomory_cut", tout << "failed to apply gomory cut:\n"; tout << "constrain_free_vars(r): " << constrain_free_vars(r) << "\n";); return false; } TRACE("gomory_cut", tout << "applying cut at:\n"; display_row_info(tout, r);); antecedents ante(*this); m_stats.m_gomory_cuts++; // gomory will be pol >= k numeral k(1); buffer pol; numeral f_0 = Ext::fractional_part(m_value[x_i]); numeral one_minus_f_0 = numeral(1) - f_0; SASSERT(!f_0.is_zero()); SASSERT(!one_minus_f_0.is_zero()); numeral lcm_den(1); unsigned num_ints = 0; typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (!it->is_dead() && it->m_var != x_i) { theory_var x_j = it->m_var; numeral a_ij = it->m_coeff; a_ij.neg(); // make the used format compatible with the format used in: Integrating Simplex with DPLL(T) if (is_real(x_j)) { numeral new_a_ij; if (at_lower(x_j)) { if (a_ij.is_pos()) { new_a_ij = a_ij / one_minus_f_0; } else { new_a_ij = a_ij / f_0; new_a_ij.neg(); } k.addmul(new_a_ij, lower_bound(x_j).get_rational()); lower(x_j)->push_justification(ante, new_a_ij, coeffs_enabled()); } else { SASSERT(at_upper(x_j)); if (a_ij.is_pos()) { new_a_ij = a_ij / f_0; new_a_ij.neg(); // the upper terms are inverted. } else { new_a_ij = a_ij / one_minus_f_0; } k.addmul(new_a_ij, upper_bound(x_j).get_rational()); upper(x_j)->push_justification(ante, new_a_ij, coeffs_enabled()); } TRACE("gomory_cut_detail", tout << a_ij << "*v" << x_j << " k: " << k << "\n";); pol.push_back(row_entry(new_a_ij, x_j)); } else { ++num_ints; SASSERT(is_int(x_j)); numeral f_j = Ext::fractional_part(a_ij); TRACE("gomory_cut_detail", tout << a_ij << "*v" << x_j << "\n"; tout << "fractional_part: " << Ext::fractional_part(a_ij) << "\n"; tout << "f_j: " << f_j << "\n"; tout << "f_0: " << f_0 << "\n"; tout << "one_minus_f_0: " << one_minus_f_0 << "\n";); if (!f_j.is_zero()) { numeral new_a_ij; if (at_lower(x_j)) { if (f_j <= one_minus_f_0) { new_a_ij = f_j / one_minus_f_0; } else { new_a_ij = (numeral(1) - f_j) / f_0; } k.addmul(new_a_ij, lower_bound(x_j).get_rational()); lower(x_j)->push_justification(ante, new_a_ij, coeffs_enabled()); } else { SASSERT(at_upper(x_j)); if (f_j <= f_0) { new_a_ij = f_j / f_0; } else { new_a_ij = (numeral(1) - f_j) / one_minus_f_0; } new_a_ij.neg(); // the upper terms are inverted k.addmul(new_a_ij, upper_bound(x_j).get_rational()); upper(x_j)->push_justification(ante, new_a_ij, coeffs_enabled()); } TRACE("gomory_cut_detail", tout << "new_a_ij: " << new_a_ij << " k: " << k << "\n";); pol.push_back(row_entry(new_a_ij, x_j)); lcm_den = lcm(lcm_den, denominator(new_a_ij)); } } } } CTRACE("empty_pol", pol.empty(), display_row_info(tout, r);); expr_ref bound(get_manager()); if (pol.empty()) { SASSERT(k.is_pos()); // conflict 0 >= k where k is positive set_conflict(ante, ante, "gomory-cut"); return true; } else if (pol.size() == 1) { theory_var v = pol[0].m_var; k /= pol[0].m_coeff; bool is_lower = pol[0].m_coeff.is_pos(); if (is_int(v) && !k.is_int()) { k = is_lower?ceil(k):floor(k); } rational _k = k.to_rational(); if (is_lower) bound = m_util.mk_ge(get_enode(v)->get_owner(), m_util.mk_numeral(_k, is_int(v))); else bound = m_util.mk_le(get_enode(v)->get_owner(), m_util.mk_numeral(_k, is_int(v))); } else { if (num_ints > 0) { lcm_den = lcm(lcm_den, denominator(k)); TRACE("gomory_cut_detail", tout << "k: " << k << " lcm_den: " << lcm_den << "\n"; for (unsigned i = 0; i < pol.size(); i++) { tout << pol[i].m_coeff << " " << pol[i].m_var << "\n"; } tout << "k: " << k << "\n";); SASSERT(lcm_den.is_pos()); if (!lcm_den.is_one()) { // normalize coefficients of integer parameters to be integers. unsigned n = pol.size(); for (unsigned i = 0; i < n; i++) { pol[i].m_coeff *= lcm_den; SASSERT(!is_int(pol[i].m_var) || pol[i].m_coeff.is_int()); } k *= lcm_den; } TRACE("gomory_cut_detail", tout << "after *lcm\n"; for (unsigned i = 0; i < pol.size(); i++) { tout << pol[i].m_coeff << " * v" << pol[i].m_var << "\n"; } tout << "k: " << k << "\n";); } mk_polynomial_ge(pol.size(), pol.c_ptr(), k.to_rational(), bound); } TRACE("gomory_cut", tout << "new cut:\n" << bound << "\n"; ante.display(tout);); literal l = null_literal; context & ctx = get_context(); if (get_manager().has_trace_stream()) log_axiom_instantiation(bound); ctx.internalize(bound, true); if (get_manager().has_trace_stream()) get_manager().trace_stream() << "[end-of-instance]\n"; l = ctx.get_literal(bound); ctx.mark_as_relevant(l); dump_lemmas(l, ante); ctx.assign(l, ctx.mk_justification( gomory_cut_justification( get_id(), ctx.get_region(), ante.lits().size(), ante.lits().c_ptr(), ante.eqs().size(), ante.eqs().c_ptr(), ante, l))); return true; } /** \brief Return false if the row failed the GCD test, that is, a conflict was detected. \remark if the variables with the least coefficient are bounded, then the ext_gcd_test is invoked. */ template bool theory_arith::gcd_test(row const & r) { if (!m_params.m_arith_gcd_test) return true; m_stats.m_gcd_tests++; numeral lcm_den = r.get_denominators_lcm(); TRACE("gcd_test_bug", r.display(tout); tout << "lcm: " << lcm_den << "\n";); numeral consts(0); numeral gcds(0); numeral least_coeff(0); bool least_coeff_is_bounded = false; typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (!it->is_dead()) { if (is_fixed(it->m_var)) { // WARNINING: it is not safe to use get_value(it->m_var) here, since // get_value(it->m_var) may not satisfy it->m_var bounds at this point. numeral aux = lcm_den * it->m_coeff; consts += aux * lower_bound(it->m_var).get_rational(); } else if (is_real(it->m_var)) { return true; } else if (gcds.is_zero()) { gcds = abs(lcm_den * it->m_coeff); least_coeff = gcds; least_coeff_is_bounded = is_bounded(it->m_var); } else { numeral aux = abs(lcm_den * it->m_coeff); gcds = gcd(gcds, aux); if (aux < least_coeff) { least_coeff = aux; least_coeff_is_bounded = is_bounded(it->m_var); } else if (least_coeff_is_bounded && aux == least_coeff) { least_coeff_is_bounded = is_bounded(it->m_var); } } SASSERT(gcds.is_int()); SASSERT(least_coeff.is_int()); TRACE("gcd_test_bug", tout << "coeff: " << it->m_coeff << ", gcds: " << gcds << " least_coeff: " << least_coeff << " consts: " << consts << "\n";); } } if (gcds.is_zero()) { // All variables are fixed. // This theory guarantees that the assignment satisfies each row, and // fixed integer variables are assigned to integer values. return true; } if (!(consts / gcds).is_int()) { TRACE("gcd_test", tout << "row failed the GCD test:\n"; display_row_info(tout, r);); antecedents ante(*this); collect_fixed_var_justifications(r, ante); context & ctx = get_context(); ctx.set_conflict( ctx.mk_justification( ext_theory_conflict_justification( get_id(), ctx.get_region(), ante.lits().size(), ante.lits().c_ptr(), ante.eqs().size(), ante.eqs().c_ptr(), ante.num_params(), ante.params("gcd-test")))); return false; } if (least_coeff.is_one() && !least_coeff_is_bounded) { SASSERT(gcds.is_one()); return true; } if (least_coeff_is_bounded) { return ext_gcd_test(r, least_coeff, lcm_den, consts); } return true; } /** \brief Auxiliary method for gcd_test. */ template bool theory_arith::ext_gcd_test(row const & r, numeral const & least_coeff, numeral const & lcm_den, numeral const & consts) { numeral gcds(0); numeral l(consts); numeral u(consts); antecedents ante(*this); typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (!it->is_dead() && !is_fixed(it->m_var)) { theory_var v = it->m_var; SASSERT(!is_real(v)); numeral ncoeff = lcm_den * it->m_coeff; SASSERT(ncoeff.is_int()); numeral abs_ncoeff = abs(ncoeff); if (abs_ncoeff == least_coeff) { SASSERT(is_bounded(v)); if (ncoeff.is_pos()) { // l += ncoeff * lower_bound(v).get_rational(); l.addmul(ncoeff, lower_bound(v).get_rational()); // u += ncoeff * upper_bound(v).get_rational(); u.addmul(ncoeff, upper_bound(v).get_rational()); } else { // l += ncoeff * upper_bound(v).get_rational(); l.addmul(ncoeff, upper_bound(v).get_rational()); // u += ncoeff * lower_bound(v).get_rational(); u.addmul(ncoeff, lower_bound(v).get_rational()); } lower(v)->push_justification(ante, it->m_coeff, coeffs_enabled()); upper(v)->push_justification(ante, it->m_coeff, coeffs_enabled()); } else if (gcds.is_zero()) { gcds = abs_ncoeff; } else { gcds = gcd(gcds, abs_ncoeff); } SASSERT(gcds.is_int()); } } if (gcds.is_zero()) { return true; } numeral l1 = ceil(l/gcds); numeral u1 = floor(u/gcds); if (u1 < l1) { TRACE("gcd_test", tout << "row failed the extended GCD test:\n"; display_row_info(tout, r);); collect_fixed_var_justifications(r, ante); context & ctx = get_context(); ctx.set_conflict( ctx.mk_justification( ext_theory_conflict_justification( get_id(), ctx.get_region(), ante.lits().size(), ante.lits().c_ptr(), ante.eqs().size(), ante.eqs().c_ptr(), ante.num_params(), ante.params("gcd-test")))); return false; } return true; } /** \brief Return true if all rows pass the GCD test. */ template bool theory_arith::gcd_test() { if (!m_params.m_arith_gcd_test) return true; if (m_eager_gcd) return true; typename vector::const_iterator it = m_rows.begin(); typename vector::const_iterator end = m_rows.end(); for (; it != end; ++it) { theory_var v = it->get_base_var(); if (v != null_theory_var && is_int(v) && !get_value(v).is_int() && !gcd_test(*it)) { if (m_params.m_arith_adaptive_gcd) m_eager_gcd = true; return false; } } return true; } /** \brief Try to create bounds for unbounded infeasible integer variables. Return false if an inconsistency is detected. */ template bool theory_arith::max_min_infeasible_int_vars() { var_set & already_processed = m_tmp_var_set; already_processed.reset(); svector vars; for (;;) { vars.reset(); // Collect infeasible integer variables. typename vector::const_iterator it = m_rows.begin(); typename vector::const_iterator end = m_rows.end(); for (; it != end; ++it) { theory_var v = it->get_base_var(); if (v != null_theory_var && is_int(v) && !get_value(v).is_int() && !is_bounded(v) && !already_processed.contains(v)) { vars.push_back(v); already_processed.insert(v); } } if (vars.empty()) return true; if (max_min(vars)) return false; } } /** \brief Try to patch int infeasible vars using freedom intervals. */ template void theory_arith::patch_int_infeasible_vars() { SASSERT(m_to_patch.empty()); int num = get_num_vars(); bool inf_l, inf_u; inf_numeral l, u; numeral m; for (theory_var v = 0; v < num; v++) { if (!is_non_base(v)) continue; get_freedom_interval(v, inf_l, l, inf_u, u, m); if (m.is_one() && get_value(v).is_int()) continue; // check whether value of v is already a multiple of m. if ((get_value(v).get_rational() / m).is_int()) continue; TRACE("patch_int", tout << "TARGET v" << v << " -> ["; if (inf_l) tout << "-oo"; else tout << ceil(l); tout << ", "; if (inf_u) tout << "oo"; else tout << floor(u); tout << "]"; tout << ", m: " << m << ", val: " << get_value(v) << ", is_int: " << is_int(v) << "\n";); if (!inf_l) l = ceil(l); if (!inf_u) u = floor(u); if (!m.is_one()) { if (!inf_l) l = m*ceil(l/m); if (!inf_u) u = m*floor(u/m); } if (!inf_l && !inf_u && l > u) continue; // cannot patch if (!inf_l) set_value(v, l); else if (!inf_u) set_value(v, u); else set_value(v, inf_numeral(0)); } SASSERT(m_to_patch.empty()); } /** \brief Force all non basic variables to be assigned to integer values. */ template void theory_arith::fix_non_base_vars() { int num = get_num_vars(); for (theory_var v = 0; v < num; v++) { if (!is_non_base(v)) continue; if (!is_int(v)) continue; if (get_value(v).is_int()) continue; inf_numeral new_val(floor(get_value(v))); set_value(v, new_val); } if (!make_feasible()) failed(); } template struct theory_arith::euclidean_solver_bridge { typedef numeral_buffer mpz_buffer; theory_arith & t; euclidean_solver m_solver; unsigned_vector m_tv2v; // theory var to euclidean solver var svector m_j2v; // justification to theory var // aux fields unsigned_vector m_xs; mpz_buffer m_as; unsigned_vector m_js; typedef euclidean_solver::var evar; typedef euclidean_solver::justification ejustification; euclidean_solver_bridge(theory_arith & _t):t(_t), m_solver(&t.m_es_num_manager), m_as(m_solver.m()) {} evar mk_var(theory_var v) { m_tv2v.reserve(v+1, UINT_MAX); if (m_tv2v[v] == UINT_MAX) m_tv2v[v] = m_solver.mk_var(); return m_tv2v[v]; } /** \brief Given a monomial, retrieve its coefficient and the power product That is, mon = a * pp */ void get_monomial(expr * mon, rational & a, expr * & pp) { expr * a_expr; if (t.m_util.is_mul(mon, a_expr, pp) && t.m_util.is_numeral(a_expr, a)) return; a = rational(1); pp = mon; } /** \brief Return the theory var associated with the given power product. */ theory_var get_theory_var(expr * pp) { context & ctx = t.get_context(); if (ctx.e_internalized(pp)) { enode * e = ctx.get_enode(pp); if (t.is_attached_to_var(e)) return e->get_th_var(t.get_id()); } return null_theory_var; } /** \brief Create an euclidean_solver variable for the given power product, if it has a theory variable associated with it. */ evar mk_var(expr * pp) { theory_var v = get_theory_var(pp); if (v == null_theory_var) return UINT_MAX; return mk_var(v); } /** \brief Return the euclidean_solver variable associated with the given power product. Return UINT_MAX, if it doesn't have one. */ evar get_var(expr * pp) { theory_var v = get_theory_var(pp); if (v == null_theory_var || v >= static_cast(m_tv2v.size())) return UINT_MAX; return m_tv2v[v]; } void assert_eqs() { // traverse definitions looking for equalities mpz c, a; mpz one; euclidean_solver::numeral_manager & m = m_solver.m(); m.set(one, 1); mpz_buffer & as = m_as; unsigned_vector & xs = m_xs; int num = t.get_num_vars(); for (theory_var v = 0; v < num; v++) { if (!t.is_fixed(v)) continue; if (!t.is_int(v)) continue; // only integer variables expr * n = t.get_enode(v)->get_owner(); if (t.m_util.is_numeral(n)) continue; // skip stupid equality c - c = 0 inf_numeral const & val = t.get_value(v); rational num = val.get_rational().to_rational(); SASSERT(num.is_int()); num.neg(); m.set(c, num.to_mpq().numerator()); ejustification j = m_solver.mk_justification(); m_j2v.reserve(j+1, null_theory_var); m_j2v[j] = v; as.reset(); xs.reset(); bool failed = false; unsigned num_args; expr * const * args; if (t.m_util.is_add(n)) { num_args = to_app(n)->get_num_args(); args = to_app(n)->get_args(); } else { num_args = 1; args = &n; } for (unsigned i = 0; i < num_args; i++) { expr * arg = args[i]; expr * pp; rational a_val; get_monomial(arg, a_val, pp); if (!a_val.is_int()) { failed = true; break; } evar x = mk_var(pp); if (x == UINT_MAX) { failed = true; break; } m.set(a, a_val.to_mpq().numerator()); as.push_back(a); xs.push_back(x); } if (!failed) { m_solver.assert_eq(as.size(), as.c_ptr(), xs.c_ptr(), c, j); TRACE("euclidean_solver", tout << "add definition: v" << v << " := " << mk_ismt2_pp(n, t.get_manager()) << "\n";); } else { TRACE("euclidean_solver", tout << "failed for:\n" << mk_ismt2_pp(n, t.get_manager()) << "\n";); } } m.del(a); m.del(c); m.del(one); } void mk_bound(theory_var v, rational k, bool lower, bound * old_bound, unsigned_vector const & js) { derived_bound * new_bound = alloc(derived_bound, v, inf_numeral(k), lower ? B_LOWER : B_UPPER); t.m_tmp_lit_set.reset(); t.m_tmp_eq_set.reset(); if (old_bound != nullptr) { t.accumulate_justification(*old_bound, *new_bound, numeral(0) /* refine for proof gen */, t.m_tmp_lit_set, t.m_tmp_eq_set); } unsigned_vector::const_iterator it = js.begin(); unsigned_vector::const_iterator end = js.end(); for (; it != end; ++it) { ejustification j = *it; theory_var fixed_v = m_j2v[j]; SASSERT(fixed_v != null_theory_var); t.accumulate_justification(*(t.lower(fixed_v)), *new_bound, numeral(0) /* refine for proof gen */, t.m_tmp_lit_set, t.m_tmp_eq_set); t.accumulate_justification(*(t.upper(fixed_v)), *new_bound, numeral(0) /* refine for proof gen */, t.m_tmp_lit_set, t.m_tmp_eq_set); } t.m_bounds_to_delete.push_back(new_bound); t.m_asserted_bounds.push_back(new_bound); } void mk_lower(theory_var v, rational k, bound * old_bound, unsigned_vector const & js) { mk_bound(v, k, true, old_bound, js); } void mk_upper(theory_var v, rational k, bound * old_bound, unsigned_vector const & js) { mk_bound(v, k, false, old_bound, js); } bool tight_bounds(theory_var v) { SASSERT(!t.is_fixed(v)); euclidean_solver::numeral_manager & m = m_solver.m(); expr * n = t.get_enode(v)->get_owner(); SASSERT(!t.m_util.is_numeral(n)); // should not be a numeral since v is not fixed. bool propagated = false; mpz a; mpz c; rational g; // gcd of the coefficients of the variables that are not in m_solver rational c2; bool init_g = false; mpz_buffer & as = m_as; unsigned_vector & xs = m_xs; as.reset(); xs.reset(); unsigned num_args; expr * const * args; if (t.m_util.is_add(n)) { num_args = to_app(n)->get_num_args(); args = to_app(n)->get_args(); } else { num_args = 1; args = &n; } for (unsigned j = 0; j < num_args; j++) { expr * arg = args[j]; expr * pp; rational a_val; get_monomial(arg, a_val, pp); if (!a_val.is_int()) goto cleanup; evar x = get_var(pp); if (x == UINT_MAX) { a_val = abs(a_val); if (init_g) g = gcd(g, a_val); else g = a_val; init_g = true; if (g.is_one()) goto cleanup; // gcd of the coeffs is one. } else { m.set(a, a_val.to_mpq().numerator()); as.push_back(a); xs.push_back(x); } } m_js.reset(); m_solver.normalize(as.size(), as.c_ptr(), xs.c_ptr(), c, a, c, m_js); if (init_g) { if (!m.is_zero(a)) g = gcd(g, rational(a)); } else { g = rational(a); } TRACE("euclidean_solver", tout << "tightening " << mk_ismt2_pp(t.get_enode(v)->get_owner(), t.get_manager()) << "\n"; tout << "g: " << g << ", a: " << m.to_string(a) << ", c: " << m.to_string(c) << "\n"; t.display_var(tout, v);); if (g.is_one()) goto cleanup; CTRACE("euclidean_solver_zero", g.is_zero(), tout << "tightening " << mk_ismt2_pp(t.get_enode(v)->get_owner(), t.get_manager()) << "\n"; tout << "g: " << g << ", a: " << m.to_string(a) << ", c: " << m.to_string(c) << "\n"; t.display(tout); tout << "------------\nSolver state:\n"; m_solver.display(tout);); if (g.is_zero()) { // The definition of v is equal to (0 + c). // That is, v is fixed at c. // The justification is just m_js, the existing bounds of v are not needed for justifying the new bounds for v. c2 = rational(c); TRACE("euclidean_solver_new", tout << "new fixed: " << c2 << "\n";); propagated = true; mk_lower(v, c2, nullptr, m_js); mk_upper(v, c2, nullptr, m_js); } else { TRACE("euclidean_solver", tout << "inequality can be tightned, since all coefficients are multiple of: " << g << "\n";); // Let l and u be the current lower and upper bounds. // Then, the following new bounds can be generated: // // l' := g*ceil((l - c)/g) + c // u' := g*floor((l - c)/g) + c bound * l = t.lower(v); bound * u = t.upper(v); c2 = rational(c); if (l != nullptr) { rational l_old = l->get_value().get_rational().to_rational(); rational l_new = g*ceil((l_old - c2)/g) + c2; TRACE("euclidean_solver_new", tout << "new lower: " << l_new << " old: " << l_old << "\n"; tout << "c: " << c2 << " ceil((l_old - c2)/g): " << (ceil((l_old - c2)/g)) << "\n";); if (l_new > l_old) { propagated = true; mk_lower(v, l_new, l, m_js); } } if (u != nullptr) { rational u_old = u->get_value().get_rational().to_rational(); rational u_new = g*floor((u_old - c2)/g) + c2; TRACE("euclidean_solver_new", tout << "new upper: " << u_new << " old: " << u_old << "\n";); if (u_new < u_old) { propagated = true; mk_upper(v, u_new, u, m_js); } } } cleanup: m.del(a); m.del(c); return propagated; } bool tight_bounds() { bool propagated = false; context & ctx = t.get_context(); // try to apply solution set to every definition int num = t.get_num_vars(); for (theory_var v = 0; v < num; v++) { if (t.is_fixed(v)) continue; // skip equations... if (!t.is_int(v)) continue; // skip non integer definitions... if (t.lower(v) == nullptr && t.upper(v) == nullptr) continue; // there is nothing to be tightned if (tight_bounds(v)) propagated = true; if (ctx.inconsistent()) break; } return propagated; } bool operator()() { TRACE("euclidean_solver", t.display(tout);); assert_eqs(); m_solver.solve(); if (m_solver.inconsistent()) { // TODO: set conflict TRACE("euclidean_solver_conflict", tout << "conflict detected...\n"; m_solver.display(tout);); return false; } return tight_bounds(); } }; template bool theory_arith::apply_euclidean_solver() { TRACE("euclidean_solver", tout << "executing euclidean solver...\n";); euclidean_solver_bridge esb(*this); if (esb()) { propagate_core(); return true; } return false; } /** \brief Return FC_DONE if the assignment is int feasible. Otherwise, apply GCD test, branch and bound and Gomory Cuts. */ template final_check_status theory_arith::check_int_feasibility() { TRACE("arith_int_detail", get_context().display(tout);); if (!has_infeasible_int_var()) { TRACE("arith", tout << "FC_DONE 1...\n"; display(tout);); return FC_DONE; } TRACE("arith", int num = get_num_vars(); for (theory_var v = 0; v < num; v++) { if (is_int(v) && !get_value(v).is_int()) { numeral f1 = get_value(v).get_rational() - floor(get_value(v).get_rational()); numeral f2 = ceil(get_value(v).get_rational()) - get_value(v).get_rational(); if (f2 < f1) f1 = f2; tout << "v" << v << " -> " << f1 << " "; display_var(tout, v); } }); TRACE("arith_int_fracs_min_max", numeral max(0); numeral min(1); int num = get_num_vars(); for (theory_var v = 0; v < num; v++) { if (is_int(v) && !get_value(v).is_int()) { numeral f1 = get_value(v).get_rational() - floor(get_value(v).get_rational()); numeral f2 = ceil(get_value(v).get_rational()) - get_value(v).get_rational(); if (f1 < min) min = f1; if (f2 < min) min = f2; if (f1 > max) max = f1; if (f2 > max) max = f2; } } tout << "max: " << max << ", min: " << min << "\n";); if (m_params.m_arith_ignore_int) { TRACE("arith", tout << "Ignore int: give up\n";); return FC_GIVEUP; } if (!gcd_test()) return FC_CONTINUE; if (m_params.m_arith_euclidean_solver) apply_euclidean_solver(); if (get_context().inconsistent()) return FC_CONTINUE; remove_fixed_vars_from_base(); TRACE("arith_int_freedom", int num = get_num_vars(); bool inf_l; bool inf_u; inf_numeral l; inf_numeral u; numeral m; for (theory_var v = 0; v < num; v++) { if (is_non_base(v)) { get_freedom_interval(v, inf_l, l, inf_u, u, m); if ((!m.is_one() /* && !l.is_zero() */) || !get_value(v).is_int()) { tout << "TARGET v" << v << " -> ["; if (inf_l) tout << "-oo"; else tout << ceil(l); tout << ", "; if (inf_u) tout << "oo"; else tout << floor(u); tout << "]"; tout << ", m: " << m << ", val: " << get_value(v) << ", is_int: " << is_int(v) << "\n"; } } }); m_stats.m_patches++; patch_int_infeasible_vars(); fix_non_base_vars(); if (get_context().inconsistent()) return FC_CONTINUE; TRACE("arith_int_inf", int num = get_num_vars(); for (theory_var v = 0; v < num; v++) { if (is_int(v) && !get_value(v).is_int()) { display_var(tout, v); } }); TRACE("arith_int_rows", unsigned num = 0; typename vector::const_iterator it = m_rows.begin(); typename vector::const_iterator end = m_rows.end(); for (; it != end; ++it) { theory_var v = it->get_base_var(); if (v == null_theory_var) continue; if (is_int(v) && !get_value(v).is_int()) { num++; display_simplified_row(tout, *it); tout << "\n"; } } tout << "num infeasible: " << num << "\n";); theory_var int_var = find_infeasible_int_base_var(); if (int_var == null_theory_var) { m_stats.m_patches_succ++; TRACE("arith_int_incomp", tout << "FC_DONE 2...\n"; display(tout);); return m_liberal_final_check || !m_changed_assignment ? FC_DONE : FC_CONTINUE; } #if 0 if (find_bounded_infeasible_int_base_var() == null_theory_var) { // TODO: this is too expensive... I should replace it by a procedure // that refine bounds using the current state of the tableau. if (!max_min_infeasible_int_vars()) return FC_CONTINUE; if (!gcd_test()) return FC_CONTINUE; } #endif m_branch_cut_counter++; // TODO: add giveup code TRACE("gomory_cut", tout << m_branch_cut_counter << ", " << m_params.m_arith_branch_cut_ratio << std::endl;); if (m_branch_cut_counter % m_params.m_arith_branch_cut_ratio == 0) { TRACE("opt_verbose", display(tout);); move_non_base_vars_to_bounds(); if (!make_feasible()) { TRACE("arith_int", tout << "failed to move variables to bounds.\n";); failed(); return FC_CONTINUE; } theory_var int_var = find_infeasible_int_base_var(); if (int_var != null_theory_var) { TRACE("arith_int", tout << "v" << int_var << " does not have an integer assignment: " << get_value(int_var) << "\n";); SASSERT(is_base(int_var)); row const & r = m_rows[get_var_row(int_var)]; if (!mk_gomory_cut(r)) { TRACE("gomory_cut", tout << "silent failure\n";); } return FC_CONTINUE; } } else { if (m_params.m_arith_int_eq_branching && branch_infeasible_int_equality()) { return FC_CONTINUE; } theory_var int_var = find_infeasible_int_base_var(); if (int_var != null_theory_var) { TRACE("arith_int", tout << "v" << int_var << " does not have an integer assignment: " << get_value(int_var) << "\n";); // apply branching branch_infeasible_int_var(int_var); return FC_CONTINUE; } } return m_liberal_final_check || !m_changed_assignment ? FC_DONE : FC_CONTINUE; } }; #endif /* THEORY_ARITH_INT_H_ */ z3-z3-4.8.7/src/smt/theory_arith_inv.h000066400000000000000000000174421356505360400175570ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_arith_inv.h Abstract: Author: Leonardo de Moura (leonardo) 2008-05-02. Revision History: --*/ #ifndef THEORY_ARITH_INV_H_ #define THEORY_ARITH_INV_H_ #include "smt/theory_arith.h" #include "ast/ast_pp.h" namespace smt { #ifdef Z3DEBUG template bool theory_arith::check_vector_sizes() const { SASSERT(m_columns.size() == m_data.size()); SASSERT(m_value.size() == m_data.size()); SASSERT(m_old_value.size() == m_data.size()); SASSERT(m_unassigned_atoms.size() == m_data.size()); SASSERT(m_var_pos.size() == m_data.size()); SASSERT(m_bounds[0].size() == m_data.size()); SASSERT(m_bounds[1].size() == m_data.size()); return true; } /** \brief Check whether all entries of m_var_pos are -1 */ template bool theory_arith::check_null_var_pos() const { svector::const_iterator it = m_var_pos.begin(); svector::const_iterator end = m_var_pos.end(); for (; it != end; ++it) { SASSERT(*it == -1); } return true; } /** \brief Return true if the given row has a variable different from r.m_base_var that has the given kind. */ template bool theory_arith::has_var_kind(unsigned r_id, var_kind k) const { row const & r = m_rows[r_id]; theory_var base = r.m_base_var; typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (!it->is_dead() && get_var_kind(it->m_var) == k && it->m_var != base) return true; } return false; } /** \brief Return true if the given row is well formed. */ template bool theory_arith::wf_row(unsigned r_id) const { buffer already_found; already_found.resize(get_num_vars(), false); row const & r = m_rows[r_id]; if (r.m_base_var != null_theory_var) { int i = 0; theory_var s = r.m_base_var; SASSERT(is_base(s) || is_quasi_base(s)); CTRACE("arith_bug", !(!is_base(s) || (!has_var_kind(r_id, BASE) && !has_var_kind(r_id, QUASI_BASE))), display_row_info(tout, r_id);); SASSERT(!is_base(s) || (!has_var_kind(r_id, BASE) && !has_var_kind(r_id, QUASI_BASE))); CTRACE("arith_bug", is_quasi_base(s) && has_var_kind(r_id, QUASI_BASE), display_row_info(tout, r_id);); SASSERT(!is_quasi_base(s) || !has_var_kind(r_id, QUASI_BASE)); SASSERT(r.is_coeff_of(s, numeral::one())); typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it, ++i) { if (!it->is_dead()) { CTRACE("row_bug", already_found[it->m_var], display_row_info(tout, r_id);); SASSERT(!already_found[it->m_var]); already_found[it->m_var] = true; column const & c = m_columns[it->m_var]; CTRACE("row_bug", it->m_coeff.is_zero(), display_row_info(tout, r_id);); SASSERT(!it->m_coeff.is_zero()); SASSERT(c[it->m_col_idx].m_row_id == static_cast(r_id)); SASSERT(c[it->m_col_idx].m_row_idx == i); } } } return true; } /** \brief Return true if all rows are well formed. */ template bool theory_arith::wf_rows() const { unsigned num = m_rows.size(); for (unsigned r_id = 0; r_id < num; r_id++) { SASSERT(wf_row(r_id)); if (m_rows[r_id].m_base_var == null_theory_var) { SASSERT(std::find(m_dead_rows.begin(), m_dead_rows.end(), r_id) != m_dead_rows.end()); } } return true; } /** \brief Return true if the column associated with v is well formed. */ template bool theory_arith::wf_column(theory_var v) const { column const & c = m_columns[v]; int i = 0; typename svector::const_iterator it = c.begin_entries(); typename svector::const_iterator end = c.end_entries(); for (; it != end; ++it, ++i) { if (!it->is_dead()) { row const & r = m_rows[it->m_row_id]; CTRACE("wf_column", r.size() == 0, tout << "v" << v << ", it->m_row_id: " << it->m_row_id << "\n"; display_row_info(tout, r); display(tout);); SASSERT(r.size() != 0); SASSERT(r[it->m_row_idx].m_var == v); SASSERT(r[it->m_row_idx].m_col_idx == i); } } SASSERT(!is_quasi_base(v) || c.size() == 1); return true; } /** \brief Return true if all columns are well formed. */ template bool theory_arith::wf_columns() const { int num = get_num_vars(); for (theory_var v = 0; v < num; v++) { SASSERT(wf_column(v)); } return true; } /** \brief Return true if all rows evaluate to zero. \remark Quasi-base rows don't need to be checked. */ template bool theory_arith::valid_row_assignment() const { TRACE("valid_row_assignment", display(tout);); typename vector::const_iterator it = m_rows.begin(); typename vector::const_iterator end = m_rows.end(); for (; it != end; ++it) { if (it->get_base_var() != null_theory_var) { SASSERT(valid_row_assignment(*it)); } } return true; } template bool theory_arith::valid_row_assignment(row const & r) const { theory_var s = r.get_base_var(); SASSERT(is_base(s) || is_quasi_base(s)); if (s != null_theory_var && is_base(s)) { inf_numeral sum; typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (!it->is_dead()) { sum += it->m_coeff * m_value[it->m_var]; } } CTRACE("valid_row_assignment_bug", !sum.is_zero(), tout << "checking: "; display_row_info(tout, r);); SASSERT(sum.is_zero()); } return true; } template bool theory_arith::satisfy_bounds() const { int num = get_num_vars(); for (theory_var v = 0; v < num; v++) { CTRACE("bound_bug", below_lower(v) || above_upper(v), display_var(tout, v); display(tout);); SASSERT(!below_lower(v)); SASSERT(!above_upper(v)); if (below_lower(v) || above_upper(v)) return false; } return true; } template bool theory_arith::satisfy_integrality() const { int num = get_num_vars(); for (theory_var v = 0; v < num; v++) { if (is_int(v) && !get_value(v).is_int()) { TRACE("bound_bug", display_var(tout, v); display(tout);); return false; } } return true; } template bool theory_arith::valid_assignment() const { if (valid_row_assignment() && satisfy_bounds() && satisfy_integrality()) { return true; } TRACE("arith", display(tout);); return false; } #endif }; #endif /* THEORY_ARITH_INV_H_ */ z3-z3-4.8.7/src/smt/theory_arith_nl.h000066400000000000000000003010361356505360400173670ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_arith_nl.h Abstract: Author: Leonardo de Moura (leonardo) 2008-12-08. Revision History: --*/ #ifndef THEORY_ARITH_NL_H_ #define THEORY_ARITH_NL_H_ #include "ast/ast_smt2_pp.h" namespace smt { template expr * theory_arith::mk_nary_mul(unsigned sz, expr * const * args, bool is_int) { if (sz == 0) return m_util.mk_numeral(rational(1), is_int); if (sz == 1) return args[0]; if (sz == 2) return m_util.mk_mul(args[0], args[1]); if (m_util.is_numeral(args[0])) return m_util.mk_mul(args[0], m_util.mk_mul(sz - 1, args + 1)); return m_util.mk_mul(sz, args); } template expr * theory_arith::mk_nary_add(unsigned sz, expr * const * args, bool is_int) { if (sz == 0) return m_util.mk_numeral(rational(0), is_int); if (sz == 1) return args[0]; return m_util.mk_add(sz, args); } template expr * theory_arith::mk_nary_add(unsigned sz, expr * const * args) { SASSERT(sz != 0); return mk_nary_add(sz, args, false); } /** \brief Insert v into vars and already_found if v is not already in already_found. */ template void theory_arith::mark_var(theory_var v, svector & vars, var_set & already_found) { if (already_found.contains(v)) return; already_found.insert(v); vars.push_back(v); } /** \brief Invoke mark_var for all variables in rows that contain v. */ template void theory_arith::mark_dependents(theory_var v, svector & vars, var_set & already_found, row_set & already_visited_rows) { if (is_pure_monomial(v)) { expr * n = var2expr(v); SASSERT(m_util.is_mul(n)); for (expr * curr : *to_app(n)) { theory_var v = expr2var(curr); SASSERT(v != null_theory_var); mark_var(v, vars, already_found); } } if (is_fixed(v)) return; column & c = m_columns[v]; typename svector::iterator it = c.begin_entries(); typename svector::iterator end = c.end_entries(); for (; it != end; ++it) { if (it->is_dead() || already_visited_rows.contains(it->m_row_id)) continue; TRACE("non_linear_bug", tout << "visiting row: " << it->m_row_id << "\n";); already_visited_rows.insert(it->m_row_id); row & r = m_rows[it->m_row_id]; theory_var s = r.get_base_var(); // ignore quasi base vars... actually they should not be used if the problem is non linear... if (is_quasi_base(s)) continue; // If s is a base variable different from v and it is free, then this row can be ignored. // It doesn't need to be part of the non linear cluster. For all purposes, this variable // was eliminated by substitution. if (is_free(s) && s != v) continue; typename vector::const_iterator it2 = r.begin_entries(); typename vector::const_iterator end2 = r.end_entries(); for (; it2 != end2; ++it2) { if (!it2->is_dead() && !is_fixed(it2->m_var)) mark_var(it2->m_var, vars, already_found); } } } /** \brief Store in vars the variables that are in the non linear cluster of constraints, and are not satisfied by the current assignment. */ template void theory_arith::get_non_linear_cluster(svector & vars) { if (m_nl_monomials.empty()) return; var_set already_found; row_set already_visited_rows; context & ctx = get_context(); for (theory_var v : m_nl_monomials) { expr * n = var2expr(v); if (ctx.is_relevant(n)) mark_var(v, vars, already_found); } // NB: vars changes inside of loop for (unsigned idx = 0; idx < vars.size(); ++idx) { theory_var v = vars[idx]; TRACE("non_linear", tout << "marking dependents of: v" << v << "\n";); mark_dependents(v, vars, already_found, already_visited_rows); } TRACE("non_linear", tout << "variables in non linear cluster:\n"; for (theory_var v : vars) tout << "v" << v << " "; tout << "\n";); } /** \brief Return the number of variables that do not have bounds associated with it. The result is 0, 1, or 2. The value 2 means "2 or more". The second value is the idx of the variable that does not have bounds associated with it. It is only useful when the first value is 1. The second value is -1 if such variable does not exist, that is, the first value is 0. \remark if a variables has an even number of occurrences, then I consider that it has a bound associated with it. Examples: 1) Assume x1, x4 have bounds: analyze_monomial(x1 * x2 * x2 * x3 * x3 * x3 * x4) --> (1,2) Explanation: x2 doesn't have bounds, but x2 has an even power. So x2*x2 has bound [0, oo). So, there is one variable without bounds x3. It is the third variable in the monomial, then its idx is 2. */ template std::pair theory_arith::analyze_monomial(expr * m) const { SASSERT(is_pure_monomial(m)); expr * var = nullptr; unsigned power = 0; unsigned c = 0; int free_var_idx = -1; int idx = 0; for (unsigned i = 0; i < to_app(m)->get_num_args(); i++) { expr * arg = to_app(m)->get_arg(i); if (var == nullptr) { var = arg; power = 1; } else if (arg == var) { power++; } else { if (power % 2 == 1 && is_free(var)) { c++; free_var_idx = idx; if (c > 1) return std::make_pair(2, free_var_idx); } var = arg; power = 1; idx++; } } if (power % 2 == 1 && is_free(var)) { c++; free_var_idx = idx; } return std::make_pair(c, free_var_idx); } /** \brief Given a monomial c*M, return M */ template expr * theory_arith::get_monomial_body(expr * m) const { SASSERT(m_util.is_mul(m)); if (m_util.is_numeral(to_app(m)->get_arg(0))) return to_app(m)->get_arg(1); return m; } /** \brief Given a monomial c*M, return c */ template rational theory_arith::get_monomial_coeff(expr * m) const { SASSERT(m_util.is_mul(m)); rational r; if (m_util.is_numeral(to_app(m)->get_arg(0), r)) return r; return rational(1); } /** \brief Return the number of distinct variables in the given monomial. */ template unsigned theory_arith::get_num_vars_in_monomial(expr * m) const { SASSERT(m_util.is_mul(m)); m = get_monomial_body(m); SASSERT(!m_util.is_numeral(m)); if (m_util.is_mul(m)) { unsigned num_vars = 0; expr * var = nullptr; for (expr * curr : *to_app(m)) { if (var != curr) { num_vars++; var = curr; } } return num_vars; } else { return 1; } } /** \brief Return the i-th var of m and its power. */ template typename theory_arith::var_power_pair theory_arith::get_var_and_degree(expr * m, unsigned i) const { SASSERT(m_util.is_mul(m)); SASSERT(i < get_num_vars_in_monomial(m)); m = get_monomial_body(m); if (m_util.is_mul(m)) { unsigned curr_idx = 0; expr * var = nullptr; unsigned power = 0; for (expr * arg : *to_app(m)) { if (var == nullptr) { var = arg; power = 1; } else if (var == arg) { power++; } else { if (curr_idx == i) return var_power_pair(var, power); curr_idx++; var = arg; power = 1; } } SASSERT(curr_idx == i); return var_power_pair(var, power); } else { SASSERT(i == 0); return var_power_pair(m, 1); } } /** \brief Return an interval using the bounds for v. */ template interval theory_arith::mk_interval_for(theory_var v) { bound * l = lower(v); bound * u = upper(v); if (l && u) { return interval(m_dep_manager, l->get_value().get_rational().to_rational(), !l->get_value().get_infinitesimal().to_rational().is_zero(), m_dep_manager.mk_leaf(l), u->get_value().get_rational().to_rational(), !u->get_value().get_infinitesimal().to_rational().is_zero(), m_dep_manager.mk_leaf(u)); } else if (l) { return interval(m_dep_manager, l->get_value().get_rational().to_rational(), !l->get_value().get_infinitesimal().to_rational().is_zero(), true, m_dep_manager.mk_leaf(l)); } else if (u) { return interval(m_dep_manager, u->get_value().get_rational().to_rational(), !u->get_value().get_infinitesimal().to_rational().is_zero(), false, m_dep_manager.mk_leaf(u)); } else { return interval(m_dep_manager); } } /** \brief Return an interval for the given expression using its bounds. */ template interval theory_arith::mk_interval_for(expr * n) { if (!has_var(n)) return interval(m_dep_manager); return mk_interval_for(expr2var(n)); } /** \brief target *= [lower(var), upper(var)]^power */ template void theory_arith::mul_bound_of(expr * var, unsigned power, interval & target) { theory_var v = expr2var(var); interval i = mk_interval_for(v); TRACE("non_linear", display_interval(tout << "bound: ",i); tout << i << "\n"; tout << mk_pp(var, get_manager()) << "\n"; tout << "power " << power << ": " << expt(i, power) << "\n"; display_interval(tout << "target before: ", target); tout << "\n";); i.expt(power); target *= i; get_manager().limit().inc((target.is_lower_open() || target.minus_infinity()) ? 1 : target.get_lower_value().bitsize()); get_manager().limit().inc((target.is_upper_open() || target.plus_infinity()) ? 1 : target.get_upper_value().bitsize()); TRACE("non_linear", display_interval(tout << "target after: ", target); tout << "\n";); } /** \brief Evaluate the given expression using interval arithmetic. - If a subexpression is internalized, then mk_interval_for is used to compute its interval. - Only +, *, and numerals are handled. */ template interval theory_arith::evaluate_as_interval(expr * n) { expr* arg; rational val; TRACE("nl_evaluate", tout << "evaluating: " << mk_bounded_pp(n, get_manager(), 10) << "\n";); if (has_var(n)) { TRACE("nl_evaluate", tout << "n has a variable associated with it\n";); TRACE("cross_nested_eval_bug", display_nested_form(tout, n); tout << "\ninterval: " << mk_interval_for(n) << "\n"; display_var(tout, expr2var(n));); return mk_interval_for(n); } else if (m_util.is_add(n)) { TRACE("nl_evaluate", tout << "is add\n";); interval r(m_dep_manager, rational(0)); for (expr* arg : *to_app(n)) { r += evaluate_as_interval(arg); } TRACE("cross_nested_eval_bug", display_nested_form(tout, n); tout << "\ninterval: " << r << "\n";); return r; } else if (m_util.is_mul(n)) { TRACE("nl_evaluate", tout << "is mul\n";); interval r(m_dep_manager, get_monomial_coeff(n)); unsigned num_vars = get_num_vars_in_monomial(n); for (unsigned i = 0; i < num_vars; i++) { var_power_pair p = get_var_and_degree(n, i); expr * var = p.first; unsigned power = p.second; interval it = evaluate_as_interval(var); it.expt(power); r *= it; } TRACE("nl_evaluate", display_nested_form(tout, n); tout << "\ninterval: " << r << "\n";); return r; } else if (m_util.is_to_real(n, arg)) { return evaluate_as_interval(arg); } else if (m_util.is_numeral(n, val)) { TRACE("nl_evaluate", tout << "is numeral\n";); return interval(m_dep_manager, val); } else { TRACE("nl_evaluate", tout << "is unknown\n";); return interval(m_dep_manager); } } template void theory_arith::display_monomial(std::ostream & out, expr * n) const { bool first = true; unsigned num_vars = get_num_vars_in_monomial(n); for (unsigned i = 0; i < num_vars; i++) { var_power_pair p = get_var_and_degree(n, i); SASSERT(p.first != 0); if (first) first = false; else out << " * "; out << mk_bounded_pp(p.first, get_manager()) << "^" << p.second; } } template void theory_arith::dependency2new_bound(v_dependency * dep, derived_bound& new_bound) { ptr_vector bounds; m_dep_manager.linearize(dep, bounds); m_tmp_lit_set.reset(); m_tmp_eq_set.reset(); for (void* _b : bounds) { bound * b = static_cast(_b); accumulate_justification(*b, new_bound, numeral::zero(), m_tmp_lit_set, m_tmp_eq_set); } } /** \brief Create a new derived bound. The justification is stored in the object dep. */ template void theory_arith::mk_derived_nl_bound(theory_var v, inf_numeral const & coeff, bound_kind k, v_dependency * dep) { inf_numeral coeff_norm = normalize_bound(v, coeff, k); derived_bound * new_bound = alloc(derived_bound, v, coeff_norm, k); m_bounds_to_delete.push_back(new_bound); m_asserted_bounds.push_back(new_bound); // copy justification to new bound dependency2new_bound(dep, *new_bound); TRACE("non_linear", new_bound->display(*this, tout); tout << "\n";); } /** \brief Update the bounds of v, using the interval i. Return true if i improves the bounds of v. */ template bool theory_arith::update_bounds_using_interval(theory_var v, interval const & i) { SASSERT(v != null_theory_var); bool r = false; if (!i.minus_infinity()) { inf_numeral new_lower(i.get_lower_value()); if (i.is_lower_open()) { if (is_int(v)) { if (new_lower.is_int()) { new_lower += rational::one(); } else { new_lower = ceil(new_lower.get_rational()); } } else { new_lower += get_epsilon(v); } } bound * old_lower = lower(v); if (old_lower == nullptr || new_lower > old_lower->get_value()) { TRACE("non_linear", tout << "NEW lower bound for v" << v << " " << new_lower << "\n"; display_interval(tout, i); tout << "\n";); mk_derived_nl_bound(v, new_lower, B_LOWER, i.get_lower_dependencies()); r = true; } } if (!i.plus_infinity()) { inf_numeral new_upper(i.get_upper_value()); if (i.is_upper_open()) { if (is_int(v)) { if (new_upper.is_int()) { new_upper -= rational::one(); } else { new_upper = floor(new_upper.get_rational()); } } else { new_upper -= get_epsilon(v); } } bound * old_upper = upper(v); if (old_upper == nullptr || new_upper < old_upper->get_value()) { TRACE("non_linear", tout << "NEW upper bound for v" << v << " " << new_upper << "\n"; display_interval(tout, i); tout << "\n";); mk_derived_nl_bound(v, new_upper, B_UPPER, i.get_upper_dependencies()); r = true; } } return r; } template bool theory_arith::update_bounds_using_interval(expr * n, interval const & i) { SASSERT(expr2var(n) != null_theory_var); TRACE("non_linear", tout << "NL bounds for m: " << i << "\n" << mk_pp(n, get_manager()) << "\n";); return update_bounds_using_interval(expr2var(n), i); } /** \brief Use the bounds of the variables to build a bound for m. */ template bool theory_arith::propagate_nl_upward(expr * m) { SASSERT(is_pure_monomial(m)); unsigned num_vars = get_num_vars_in_monomial(m); interval new_bounds(m_dep_manager, rational(1)); // TODO: the following code can be improved it is quadratic on the degree of the monomial. TRACE("nl_arith_bug", tout << "processing upward:\n" << mk_pp(m, get_manager()) << "\n";); for (unsigned i = 0; i < num_vars; i++) { var_power_pair p = get_var_and_degree(m, i); expr * var = p.first; unsigned power = p.second; TRACE("nl_arith_bug", tout << "interval before: " << new_bounds << "\n"; theory_var v = expr2var(var); interval i = mk_interval_for(v); display_var(tout, v); tout << "interval for var: " << i << "\n" << mk_pp(var, get_manager()) << "\npower: " << power << " " << expt(i, power) << "\n";); mul_bound_of(var, power, new_bounds); TRACE("nl_arith_bug", tout << "interval after: " << new_bounds << "\n";); } return update_bounds_using_interval(m, new_bounds); } /** \brief Propagate a bound to the i-th variable of the given monomial using the bounds of m and other variables in m. \remark We do not support roots in interval... so, if the i-th var has power != 1 the method returns without doing anything. */ template bool theory_arith::propagate_nl_downward(expr * n, unsigned i) { SASSERT(is_pure_monomial(n)); SASSERT(i < get_num_vars_in_monomial(n)); var_power_pair p = get_var_and_degree(n, i); expr * v = p.first; unsigned power = p.second; if (power != 1) return false; // TODO: remove, when the n-th root is implemented in interval. unsigned num_vars = get_num_vars_in_monomial(n); interval other_bounds(m_dep_manager, rational(1)); // TODO: the following code can be improved it is quadratic on the degree of the monomial. for (unsigned i = 0; i < num_vars; i++) { var_power_pair p = get_var_and_degree(n, i); if (p.first == v) continue; expr * var = p.first; unsigned power = p.second; mul_bound_of(var, power, other_bounds); } if (other_bounds.contains_zero()) return false; // interval division requires that divisor doesn't contain 0. interval r = mk_interval_for(n); TRACE("nl_arith_bug", tout << "m: " << mk_ismt2_pp(n, get_manager()) << "\nv: " << mk_ismt2_pp(v, get_manager()) << "\npower: " << power << "\n"; tout << "num_vars: " << num_vars << "\n"; display_interval(tout << "monomial bounds\n", r); display_interval(tout << "other bounds\n", other_bounds); ); r /= other_bounds; return update_bounds_using_interval(v, r); } /** \brief Try to propagate a bound using the given non linear monomial. Return true if some bound was propagated. If i == -1, then use the bound of the variables to propagate a bound for the monomial m. If i != -1, then it is the index of the variable that I will compute bounds for. */ template bool theory_arith::propagate_nl_bound(expr * m, int i) { TRACE("propagate_nl_bound", tout << "propagate using i: " << i << "\n"; display_monomial(tout, m); tout << "\n";); if (i == -1) return propagate_nl_upward(m); else return propagate_nl_downward(m, i); } /** \brief The given monomial and its elements have bounds. Propagate bounds to all of them. Return true if some bound was propagated. */ template bool theory_arith::propagate_nl_bounds(expr * m) { TRACE("non_linear", tout << "propagate several bounds using:\n"; display_monomial(tout, m); tout << "\n";); bool result = propagate_nl_upward(m); unsigned num_vars = get_num_vars_in_monomial(m); for (unsigned i = 0; i < num_vars; i++) if (propagate_nl_downward(m, i)) { m_stats.m_nl_bounds++; result = true; } return result; } /** \brief Try to propagate bounds using non linear monomials. Return true if some bound was propagated. */ template bool theory_arith::propagate_nl_bounds() { m_dep_manager.reset(); bool propagated = false; context & ctx = get_context(); for (unsigned i = 0; i < m_nl_monomials.size(); i++) { theory_var v = m_nl_monomials[i]; expr * m = var2expr(v); if (!ctx.is_relevant(m)) continue; std::pair p = analyze_monomial(m); TRACE("propagate_nl_bound", tout << "m: " << mk_ismt2_pp(m, get_manager()) << "\n" << "p: " << p.first << " " << p.second << "\n";); unsigned num_bad_vars = p.first; int free_var_idx = p.second; SASSERT(num_bad_vars != 1 || free_var_idx != -1); if (num_bad_vars >= 2) continue; bool is_free_m = is_free(m); TRACE("propagate_nl_bound", tout << "is_free_m: " << is_free_m << "\n";); if (num_bad_vars == 1 && is_free_m) continue; if (num_bad_vars == 0) { if (!is_free_m) { if (propagate_nl_bounds(m)) propagated = true; } else { if (propagate_nl_bound(m, -1)) { m_stats.m_nl_bounds++; propagated = true; } } } else { SASSERT (!is_free_m); if (propagate_nl_bound(m, free_var_idx)) { m_stats.m_nl_bounds++; propagated = true; } } } return propagated; } /** \brief Return the value of v as a rational. If computed_epsilon = false and v has an infinitesimal, then compute_epsilon() is invoked. */ template rational theory_arith::get_value(theory_var v, bool & computed_epsilon) { inf_numeral const & val = get_value(v); if (!val.get_infinitesimal().is_zero() && !computed_epsilon) { compute_epsilon(); computed_epsilon = true; m_model_depends_on_computed_epsilon = true; } return val.get_rational().to_rational() + m_epsilon.to_rational() * val.get_infinitesimal().to_rational(); } /** \brief Return true if for the monomial x_1 * ... * x_n associated with v, the following holds: get_value(x_1) * ... * get_value(x_n) = get_value(v) */ template bool theory_arith::check_monomial_assignment(theory_var v, bool & computed_epsilon) { SASSERT(is_pure_monomial(var2expr(v))); expr * m = var2expr(v); rational val(1), v_val; for (expr* arg : *to_app(m)) { theory_var curr = expr2var(arg); SASSERT(curr != null_theory_var); v_val = get_value(curr, computed_epsilon); TRACE("non_linear", tout << mk_pp(arg, get_manager()) << " = " << v_val << "\n";); val *= v_val; } v_val = get_value(v, computed_epsilon); TRACE("non_linear", tout << "v" << v << " := " << v_val << " == " << val << "\n";); return v_val == val; } /** \brief Return true if for every monomial x_1 * ... * x_n, get_value(x_1) * ... * get_value(x_n) = get_value(x_1 * ... * x_n) */ template bool theory_arith::check_monomial_assignments() { bool computed_epsilon = false; context & ctx = get_context(); svector::const_iterator it = m_nl_monomials.begin(); svector::const_iterator end = m_nl_monomials.end(); for (; it != end; ++it) { TRACE("non_linear", tout << "v" << *it << " is relevant: " << ctx.is_relevant(get_enode(*it)) << "\n"; tout << "check_monomial_assignments result: " << check_monomial_assignment(*it, computed_epsilon) << "\n"; tout << "computed_epsilon: " << computed_epsilon << "\n";); if (ctx.is_relevant(get_enode(*it)) && !check_monomial_assignment(*it, computed_epsilon)) { TRACE("non_linear_failed", tout << "check_monomial_assignment failed for:\n" << mk_ismt2_pp(var2expr(*it), get_manager()) << "\n"; display_var(tout, *it);); return false; } } return true; } /** \brief Try to find an integer variable for performing branching in the non linear cluster. The idea is select a variable in a monomial with an invalid assignment. I give preference to variables with small ranges. If no variable is bounded, then select a random one. Free variables are not considered. */ template theory_var theory_arith::find_nl_var_for_branching() { TRACE("nl_branching", tout << "looking for variable to branch...\n"; display(tout);); context & ctx = get_context(); theory_var target = null_theory_var; bool bounded = false; unsigned n = 0; numeral range; for (unsigned j = 0; j < m_nl_monomials.size(); ++j) { theory_var v = m_nl_monomials[j]; if (is_real(v)) continue; bool computed_epsilon = false; bool r = check_monomial_assignment(v, computed_epsilon); if (!r) { expr * m = get_enode(v)->get_owner(); SASSERT(is_pure_monomial(m)); for (unsigned i = 0; i < to_app(m)->get_num_args(); i++) { expr * arg = to_app(m)->get_arg(i); theory_var curr = ctx.get_enode(arg)->get_th_var(get_id()); TRACE("nl_branching", tout << "target: v" << target << ", curr: v" << curr << "\n";); if (!is_fixed(curr) && is_int(curr)) { if (is_bounded(curr)) { numeral new_range; new_range = upper_bound(curr).get_rational(); new_range -= lower_bound(curr).get_rational(); if (!bounded || new_range < range) { target = curr; range = new_range; bounded = true; } } else if (!bounded) { n++; TRACE("nl_branching", tout << "n: " << n << "\n";); if (m_random()%n == 0) target = curr; SASSERT(target != null_theory_var); } SASSERT(target != null_theory_var); } TRACE("nl_branching", tout << "after target: v" << target << "\n";); } } } return target; } /** \brief Branch on an integer variable. This method is invoked when v is part of a non linear monomial that is not satisfied by the current assignment. if v >= l, then create the case split v >= l+1 else v <= u, then create the case split v <= u-1 else create the bound v = 0 and case split on it. */ template bool theory_arith::branch_nl_int_var(theory_var v) { TRACE("non_linear", tout << "BRANCHING on v" << v << "\n";); m_stats.m_nl_branching++; SASSERT(is_int(v)); expr * bound = nullptr; if (lower(v)) bound = m_util.mk_le(var2expr(v), m_util.mk_numeral(lower_bound(v).get_rational().to_rational(), true)); else if (upper(v)) bound = m_util.mk_ge(var2expr(v), m_util.mk_numeral(upper_bound(v).get_rational().to_rational(), true)); else bound = m_util.mk_eq(var2expr(v), m_util.mk_numeral(rational(0), true)); TRACE("non_linear", tout << "new bound:\n" << mk_pp(bound, get_manager()) << "\n";); context & ctx = get_context(); ast_manager & m = get_manager(); if (m.has_trace_stream()) { app_ref body(m); body = m.mk_or(bound, m.mk_not(bound)); log_axiom_instantiation(body); } ctx.internalize(bound, true); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; ctx.mark_as_relevant(bound); literal l = ctx.get_literal(bound); SASSERT(!l.sign()); ctx.set_true_first_flag(l.var()); // force the context to case split to true first, independently of the phase selection strategy. return true; } /** \brief Return true if the given monomial is linear. */ template bool theory_arith::is_monomial_linear(expr * m) const { SASSERT(is_pure_monomial(m)); unsigned num_nl_vars = 0; for (expr* arg : *to_app(m)) { theory_var _var = expr2var(arg); if (!is_fixed(_var)) { num_nl_vars++; } else if (lower_bound(_var).is_zero()) { return true; } } return num_nl_vars <= 1; } /** \brief Return the product of the value of the fixed variables in the monomial m. */ template typename theory_arith::numeral theory_arith::get_monomial_fixed_var_product(expr * m) const { SASSERT(is_pure_monomial(m)); numeral r(1); for (unsigned i = 0; i < to_app(m)->get_num_args(); i++) { expr * arg = to_app(m)->get_arg(i); theory_var _var = expr2var(arg); if (is_fixed(_var)) r *= lower_bound(_var).get_rational(); } TRACE("arith", tout << mk_pp(m, get_manager()) << " " << r << "\n";); return r; } /** \brief Return the first non fixed variable in the given monomial. Return 0, if the monomial does not have a non fixed variable. */ template expr * theory_arith::get_monomial_non_fixed_var(expr * m) const { SASSERT(is_pure_monomial(m)); for (unsigned i = 0; i < to_app(m)->get_num_args(); i++) { expr * arg = to_app(m)->get_arg(i); theory_var _var = expr2var(arg); if (!is_fixed(_var)) return arg; } return nullptr; } /** \brief Propagate linear monomial. Check whether the give monomial became linear and propagate. */ template bool theory_arith::propagate_linear_monomial(theory_var v) { TRACE("non_linear", tout << "checking whether v" << v << " became linear...\n";); if (m_data[v].m_nl_propagated) return false; // already propagated this monomial. expr * m = var2expr(v); if (!is_monomial_linear(m)) return false; // monomial is not linear. m_stats.m_nl_linear++; m_data[v].m_nl_propagated = true; m_nl_propagated.push_back(v); TRACE("non_linear", tout << "v" << v << " is linear " << mk_pp(m, get_manager()) << "\n";); numeral k = get_monomial_fixed_var_product(m); TRACE("non_linear", tout << "new linear monomial... k: " << k << "\n";); expr * x_n = k.is_zero() ? nullptr : get_monomial_non_fixed_var(m); TRACE("non_linear_bug", if (x_n != 0) { tout << "x_n: " << mk_bounded_pp(x_n, get_manager()) << "\nx_n: #" << x_n->get_id() << "\n"; }); context & ctx = get_context(); derived_bound * new_lower = nullptr; derived_bound * new_upper = nullptr; if (x_n != nullptr) { // All but one of the x_i variables are assigned. // Let x_n be the unassigned variable. // Then, we know that x_1*...*x_n = k*x_n, where k is the product of beta(x_1)*...*beta(x_{n-1}) // beta(x_i) == lower(x_i) // Let m be (* x_1 ... x_n), then assert equality // (= (+ (* x_1 ... x_n) (* -k x_n)) 0) when x_1 ... x_{n-1} are fixed variables. // where k = lower(x_1)*...*lower(x_{n-1}) TRACE("non_linear", tout << "x_n: " << mk_pp(x_n, get_manager()) << "\n";); k.neg(); expr * k_x_n = k.is_one() ? x_n : m_util.mk_mul(m_util.mk_numeral(k.to_rational(), is_int(v)), x_n); expr * rhs = m_util.mk_add(m, k_x_n); TRACE("non_linear_bug", tout << "rhs: " << mk_bounded_pp(rhs, get_manager(),5) << "\ninternalized: " << ctx.e_internalized(rhs) << "\n";); if (!has_var(rhs)) { ctx.internalize(rhs, false); ctx.mark_as_relevant(rhs); } TRACE("non_linear_bug", tout << "enode: " << get_context().get_enode(rhs) << " enode_id: " << get_context().get_enode(rhs)->get_owner_id() << "\n";); theory_var new_v = expr2var(rhs); SASSERT(new_v != null_theory_var); new_lower = alloc(derived_bound, new_v, inf_numeral(0), B_LOWER); new_upper = alloc(derived_bound, new_v, inf_numeral(0), B_UPPER); } else { // One of the x_i variables is zero, // or all of them are assigned. // Assert the equality // (= (* x_1 ... x_n) k) TRACE("non_linear", tout << "all variables are fixed, and bound is: " << k << "\n";); new_lower = alloc(derived_bound, v, inf_numeral(k), B_LOWER); new_upper = alloc(derived_bound, v, inf_numeral(k), B_UPPER); } SASSERT(new_lower != 0); SASSERT(new_upper != 0); m_bounds_to_delete.push_back(new_lower); m_asserted_bounds.push_back(new_lower); m_bounds_to_delete.push_back(new_upper); m_asserted_bounds.push_back(new_upper); // Add the justification for new_lower and new_upper. // The justification is the lower and upper bounds of all fixed variables. m_tmp_lit_set.reset(); m_tmp_eq_set.reset(); SASSERT(is_pure_monomial(m)); bool found_zero = false; for (unsigned i = 0; !found_zero && i < to_app(m)->get_num_args(); i++) { expr * arg = to_app(m)->get_arg(i); theory_var _var = expr2var(arg); if (is_fixed(_var)) { bound * l = lower(_var); bound * u = upper(_var); if (l->get_value().is_zero()) { /* if zero was found, then it is the explanation */ SASSERT(k.is_zero()); found_zero = true; m_tmp_lit_set.reset(); m_tmp_eq_set.reset(); new_lower->m_lits.reset(); new_lower->m_eqs.reset(); } accumulate_justification(*l, *new_lower, numeral::zero(), m_tmp_lit_set, m_tmp_eq_set); TRACE("non_linear", for (literal l : new_lower->m_lits) { ctx.display_detailed_literal(tout, l) << " "; } tout << "\n";); accumulate_justification(*u, *new_lower, numeral::zero(), m_tmp_lit_set, m_tmp_eq_set); TRACE("non_linear", for (literal l : new_lower->m_lits) { ctx.display_detailed_literal(tout, l) << " "; } tout << "\n";); } } new_upper->m_lits.append(new_lower->m_lits); new_upper->m_eqs.append(new_lower->m_eqs); TRACE("non_linear", new_lower->display(*this, tout << "lower: "); tout << "\n"; new_upper->display(*this, tout << "upper: "); tout << "\n"; for (literal lit : new_upper->m_lits) { ctx.display_detailed_literal(tout, lit) << " "; } tout << "\n";); return true; } /** \brief Traverse all non linear monomials, and check the ones that became linear and propagate. Return true if propagated. */ template bool theory_arith::propagate_linear_monomials() { TRACE("non_linear", tout << "propagating linear monomials...\n";); bool p = false; // CMW: m_nl_monomials is sometimes modified while executing // propagate_linear_monomial(...), invalidating the iterator `it'. // (Via the relevancy propagation that internalizes a new axiom // in mk_div_axiom and possibly others.) I'm replacing the iterator // with an index `i'. // Was previously: // svector::const_iterator it = m_nl_monomials.begin(); // svector::const_iterator end = m_nl_monomials.end(); // for (; it != end; ++it) { // theory_var v = *it; for (unsigned i = 0; i < m_nl_monomials.size(); i++) { theory_var v = m_nl_monomials[i]; if (propagate_linear_monomial(v)) p = true; } CTRACE("non_linear", p, display(tout);); return p; } /* Interval arithmetic does not satisfy distributivity. Actually, it satisfies the sub-distributivity property: x*(y + z) \subseteq x*y + x*z The sub-distributivity property only holds if condensation is not used. For example: x * (x^3 + 1) \subseteq x*x^3 + x, but it is not the case that x * (x^3 + 1) \subseteq x^4 + x for example, for x = [-2,1] x*(x^3+1) = [-7, 14] x^4 + x = [-2, 17] This weakness of AI is known as the "dependency problem", which comes from the decorrelation of the multiple occurrences of one variable during interval evaluation. Given a polynomial: p(x) = a_0 + a_1 * x + ... + a_n * x^n The horner extension is: h_p(x) = a_0 + x*(a_1 + ... + x*(a_{n-1} + a_n * x) + ...) The horner extension of p(x) = x^4 + x^3 + 2*x is: h_p(x) = x(2 + x^3(1 + x)) The horner extension evaluates tighter intervals when condensation is not used. Remark: there is no guarantee that horner extension will provide a tighter interval than a sum of monomials when condensation is used. For multivariate polynomials nested (or cross nested) forms are used. The idea is to select one variable, and pretend the other are parameters. The horner form is computed for the selected variable, and the computation continues for the polynomials on the parameters. As described above, the horner form is not optimal with respect to to condensation. I use the following two properties to deal with monovariate polynomials with two monomials: p(x) = a*x^n + b*x^{n+m} for n >= m is equivalent to b*x^{n-m}*[(x^{m} + a/(2b))^2 - (a/2b)^2] This polynomial provides tight bound when n and m have the same parity and: 1) a*b > 0 and (lower(x) >= 0 or upper(x)^m <= -a/b) 2) a*b < 0 and (upper(x) <= 0 or lower(x)^m >= a/b) This polynomial also provides tight bounds when n = m, and the polynomial is simplified to, and n and m may have arbitrary parities: b*[(x^{n} + a/(2b))^2 - (a/2b)^2] Example: x > 1 x^2 - x <= 0 is unsatisfiable If we compute the bounds for x^2 - x we obtain (-oo, oo). On the other hand, if we compute the bounds for (x - 1/2)^2 - 1/4 we obtain the bounds (0, oo), and the inconsistency is detected. Remark: In Z3, I condensate multiple occurrences of a variable when evaluating monomials. So, the interval for a monomial is always tight. Remark: M1*(M2 + M3) is more precise than M1 * M2 + M1 * M3, if intersection(Vars(M1), union(Vars(M2), Vars(M3))) = empty-set, Remark: A trivial consequence of Moore's theorem for interval arithmetic. If two monomials M1 and M2 do not share variables, then the interval for M1 + M2 is tight. */ /** \brief Check whether the same variable occurs in two different monomials. \remark Fixed variables are ignored. \remark A trivial consequence of Moore's theorem for interval arithmetic. If two monomials M1 and M2 do not share variables, then the interval for M1 + M2 is tight. */ template bool theory_arith::is_problematic_non_linear_row(row const & r) { m_tmp_var_set.reset(); typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (!it->is_dead()) { theory_var v = it->m_var; if (is_fixed(v)) continue; if (is_pure_monomial(v)) { expr * m = var2expr(v); for (expr* arg : *to_app(m)) { theory_var curr = expr2var(arg); if (m_tmp_var_set.contains(curr)) return true; } SASSERT(m == var2expr(v)); for (expr* arg : *to_app(m)) { theory_var curr = expr2var(arg); if (!is_fixed(curr)) m_tmp_var_set.insert(curr); } } else { if (m_tmp_var_set.contains(v)) return true; SASSERT(!is_fixed(v)); m_tmp_var_set.insert(v); } } } return false; } /** \brief Return true if the row mixes real and integer variables. This kind of row cannot be converted back to an expression, since expressions in Z3 cannot have mixed sorts. */ template bool theory_arith::is_mixed_real_integer(row const & r) const { bool found_int = false; bool found_real = false; typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (it->is_dead()) continue; theory_var v = it->m_var; // TODO: possible improvement... ignore fixed variables. // If we implement this improvement, we are actually changing the contract of this function // and we will also have to fix the affected functions. if (is_int(v)) found_int = true; if (is_real(v)) found_real = true; if (found_int && found_real) return true; } return false; } /** \brief Return true if the row contains only integer variables. */ template bool theory_arith::is_integer(row const & r) const { typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (it->is_dead()) continue; theory_var v = it->m_var; // TODO: possible improvement... ignore fixed variables. if (!is_int(v)) return false; } return true; } template void theory_arith::display_coeff_exprs(std::ostream & out, sbuffer const & p) const { typename sbuffer::const_iterator it = p.begin(); typename sbuffer::const_iterator end = p.end(); for (bool first = true; it != end; ++it) { if (first) first = false; else out << "+\n"; out << it->first << " * " << mk_pp(it->second, get_manager()) << "\n"; } } /** \brief Traverse p and store in vars the (non-fixed) variables that occur in more than one monomial. The number of occurrences is also stored. */ template void theory_arith::get_polynomial_info(sbuffer const & p, sbuffer & varinfo) { context & ctx = get_context(); varinfo.reset(); m_var2num_occs.reset(); #define ADD_OCC(VAR) if (has_var(VAR) && !is_fixed(expr2var(VAR))) { \ TRACE("nl_info", tout << "adding occ: " << mk_bounded_pp(VAR, get_manager()) << "\n";); \ unsigned occs = 0; \ m_var2num_occs.find(VAR, occs); \ occs++; \ m_var2num_occs.insert(VAR, occs); \ } for (coeff_expr const& kv : p) { expr * m = kv.second; if (is_pure_monomial(m)) { unsigned num_vars = get_num_vars_in_monomial(m); for (unsigned i = 0; i < num_vars; i++) { var_power_pair p = get_var_and_degree(m, i); ADD_OCC(p.first); } } else if (m_util.is_numeral(m)) { continue; } else if (ctx.e_internalized(m)) { ADD_OCC(m); } else { ctx.internalize(m, false); ADD_OCC(m); } } // Update the number of occurrences in the result vector. for (auto const& vn : m_var2num_occs) { if (vn.m_value > 1) varinfo.push_back(var_num_occs(vn.m_key, vn.m_value)); } } /** \brief Convert p into an expression. */ template expr * theory_arith::p2expr(sbuffer & p) { SASSERT(!p.empty()); ptr_buffer args; for (coeff_expr const& ce : p) { rational const & c = ce.first; expr * var = ce.second; if (!c.is_one()) { rational c2; expr * m = nullptr; if (m_util.is_numeral(var, c2)) m = m_util.mk_numeral(c*c2, m_util.is_int(var) && c.is_int() && c2.is_int()); else m = m_util.mk_mul(m_util.mk_numeral(c, c.is_int() && m_util.is_int(var)), var); m_nl_new_exprs.push_back(m); args.push_back(m); } else { args.push_back(var); } } SASSERT(!args.empty()); expr * r = mk_nary_add(args.size(), args.c_ptr()); m_nl_new_exprs.push_back(r); TRACE("p2expr_bug", display_coeff_exprs(tout, p); tout << mk_pp(r, get_manager()) << "\n";); return r; } /** \brief Return expression representing: var^power */ template expr * theory_arith::power(expr * var, unsigned power) { SASSERT(power > 0); expr * r = var; for (unsigned i = 1; i < power; i++) r = m_util.mk_mul(var, r); m_nl_new_exprs.push_back(r); return r; } /** \brief Return true if var only occurs in two monovariate monomials, and return its power and coefficients and these monomials. The arguments i1 and i2 contain the position in p of the two monomials. */ template bool theory_arith::in_monovariate_monomials(sbuffer & p, expr * var, unsigned & i1, rational & c1, unsigned & n1, unsigned & i2, rational & c2, unsigned & n2) { int idx = 0; #define SET_RESULT(POWER) { \ if (idx == 0) { \ c1 = it->first; \ n1 = POWER; \ idx = 1; \ i1 = i; \ } \ else if (idx == 1) { \ c2 = it->first; \ n2 = POWER; \ idx = 2; \ i2 = i; \ } \ else \ return false; \ } typename sbuffer::const_iterator it = p.begin(); typename sbuffer::const_iterator end = p.end(); for (unsigned i = 0; it != end; ++it, ++i) { expr * m = it->second; if (is_pure_monomial(m)) { unsigned num_vars = get_num_vars_in_monomial(m); for (unsigned j = 0; j < num_vars; j++) { var_power_pair p = get_var_and_degree(m, j); if (p.first == var) { if (num_vars > 1) return false; SET_RESULT(p.second); } } } else if (m == var) { SET_RESULT(1); } } if (idx != 2) return false; return true; } /** \brief Display a nested form expression */ template void theory_arith::display_nested_form(std::ostream & out, expr * p) { if (has_var(p)) { out << "#" << p->get_id(); } else if (m_util.is_add(p)) { SASSERT(!has_var(p)); out << "("; for (unsigned i = 0; i < to_app(p)->get_num_args(); i++) { if (i > 0) out << " + "; display_nested_form(out, to_app(p)->get_arg(i)); } out << ")"; } else if (m_util.is_mul(p)) { rational c = get_monomial_coeff(p); bool first = true; if (!c.is_one()) { out << c; first = false; } unsigned num_vars = get_num_vars_in_monomial(p); for (unsigned i = 0; i < num_vars; i++) { if (first) first = false; else out << "*"; var_power_pair pair = get_var_and_degree(p, i); expr * var = pair.first; unsigned power = pair.second; display_nested_form(out, var); if (power != 1) out << "^" << power; } } else { rational val; if (m_util.is_numeral(p, val)) out << val; else out << "[unknown #" << p->get_id() << "]"; } } /** \brief Return the degree of var in m. */ template unsigned theory_arith::get_degree_of(expr * m, expr * var) { if (m == var) return 1; if (is_pure_monomial(m)) { unsigned num_vars = get_num_vars_in_monomial(m); for (unsigned i = 0; i < num_vars; i++) { var_power_pair p = get_var_and_degree(m, i); if (p.first == var) return p.second; } } return 0; } /** \brief Return the minimal degree of var in the polynomial p. */ template unsigned theory_arith::get_min_degree(sbuffer & p, expr * var) { SASSERT(!p.empty()); SASSERT(var != 0); // get monomial where the degree of var is min. unsigned d = UINT_MAX; // min. degree of var sbuffer::const_iterator it = p.begin(); sbuffer::const_iterator end = p.end(); for (; it != end; ++it) { expr * m = it->second; d = std::min(d, get_degree_of(m, var)); if (d == 0) return d; } SASSERT(d != UINT_MAX); return d; } /** \brief Divide m by var^d. */ template expr * theory_arith::factor(expr * m, expr * var, unsigned d) { TRACE("factor", tout << "m: " << mk_pp(m, get_manager()) << "\nvar: " << mk_pp(var, get_manager()) << "\nd: " << d << "\n";); if (d == 0) return m; if (m == var) { SASSERT(d == 1); expr * result = m_util.mk_numeral(rational(1), m_util.is_int(var)); m_nl_new_exprs.push_back(result); return result; } SASSERT(is_pure_monomial(m)); unsigned idx = 0; ptr_buffer new_args; for (expr * arg : *to_app(m)) { if (arg == var) { if (idx < d) idx++; else new_args.push_back(arg); } else { new_args.push_back(arg); } } SASSERT(idx == d); TRACE("factor_bug", tout << "new_args:\n"; for(unsigned i = 0; i < new_args.size(); i++) tout << mk_pp(new_args[i], get_manager()) << "\n";); expr * result = mk_nary_mul(new_args.size(), new_args.c_ptr(), m_util.is_int(var)); m_nl_new_exprs.push_back(result); TRACE("factor", tout << "result: " << mk_pp(result, get_manager()) << "\n";); return result; } /** \brief Return the horner extension of p with respect to var. */ template expr * theory_arith::horner(sbuffer & p, expr * var) { SASSERT(!p.empty()); SASSERT(var != 0); unsigned d = get_min_degree(p, var); TRACE("horner_bug", tout << "poly:\n"; for (unsigned i = 0; i < p.size(); i++) { if (i > 0) tout << " + "; tout << p[i].first << "*" << mk_pp(p[i].second, get_manager()); } tout << "\n"; tout << "var: " << mk_pp(var, get_manager()) << "\n"; tout << "min_degree: " << d << "\n";); sbuffer e; // monomials/x^d where var occurs with degree d sbuffer r; // rest for (auto const& kv : p) { expr * m = kv.second; expr * f = factor(m, var, d); if (get_degree_of(m, var) == d) { e.push_back(coeff_expr(kv.first, f)); } else { SASSERT(get_degree_of(m, var) > d); r.push_back(coeff_expr(kv.first, f)); } } expr * s = cross_nested(e, nullptr); if (!r.empty()) { expr * q = horner(r, var); // TODO: improve here s = m_util.mk_add(q, s); } expr * result = s; if (d != 0) { expr * xd = power(var, d); result = m_util.mk_mul(xd, s); } m_nl_new_exprs.push_back(result); return result; } /** \brief Convert the polynomial p into an equivalent cross nested expression. The idea is to obtain an expression e where evaluate_as_interval(e) is more precise than evaluate_as_interval(p). If var != 0, then it is used for performing the horner extension */ template expr * theory_arith::cross_nested(sbuffer & p, expr * var) { TRACE("non_linear", tout << "p.size: " << p.size() << "\n";); if (var == nullptr) { sbuffer varinfo; get_polynomial_info(p, varinfo); if (varinfo.empty()) return p2expr(p); sbuffer::const_iterator it = varinfo.begin(); sbuffer::const_iterator end = varinfo.end(); var = it->first; unsigned max = it->second; ++it; for (; it != end; ++it) { if (it->second > max) { var = it->first; max = it->second; } } } SASSERT(var != 0); unsigned i1 = UINT_MAX; unsigned i2 = UINT_MAX; rational a, b; unsigned n = UINT_MAX; unsigned nm = UINT_MAX; if (in_monovariate_monomials(p, var, i1, a, n, i2, b, nm) && n != nm) { CTRACE("in_monovariate_monomials", n == nm, for (unsigned i = 0; i < p.size(); i++) { if (i > 0) tout << " + "; tout << p[i].first << "*" << mk_pp(p[i].second, get_manager()); } tout << "\n"; tout << "var: " << mk_pp(var, get_manager()) << "\n"; tout << "i1: " << i1 << "\n"; tout << "a: " << a << "\n"; tout << "n: " << n << "\n"; tout << "i2: " << i2 << "\n"; tout << "b: " << b << "\n"; tout << "nm: " << nm << "\n";); SASSERT(n != nm); expr * new_expr = nullptr; if (nm < n) { std::swap(n, nm); std::swap(a, b); } SASSERT(nm > n); unsigned m = nm - n; if (n % 2 == m % 2 && n >= m) { // b*x^{n-m}*[(x^{m} + a/(2b))^2 - (a/2b)^2] // b*[(x^{m} + a/(2b))^2 - (a/2b)^2] for n == m rational a2b = a; expr * xm = power(var, m); a2b /= (rational(2) * b); // we cannot create a numeral that has sort int, but it is a rational. if (!m_util.is_int(var) || a2b.is_int()) { rational ma2b2 = a2b * a2b; ma2b2.neg(); expr * xm_a2b = m_util.mk_add(m_util.mk_numeral(a2b, m_util.is_int(var)), xm); expr * xm_a2b2 = m_util.mk_mul(xm_a2b, xm_a2b); expr * rhs = m_util.mk_add(xm_a2b2, m_util.mk_numeral(ma2b2, m_util.is_int(var))); expr * rhs2 = nullptr; if (n > m) rhs2 = m_util.mk_mul(power(var, n - m), rhs); else rhs2 = rhs; new_expr = b.is_one() ? rhs2 : m_util.mk_mul(m_util.mk_numeral(b, m_util.is_int(var)), rhs2); m_nl_new_exprs.push_back(new_expr); TRACE("non_linear", tout << "new_expr:\n"; display_nested_form(tout, new_expr); tout << "\n";); sbuffer rest; unsigned sz = p.size(); for (unsigned i = 0; i < sz; i++) { if (i != i1 && i != i2) rest.push_back(p[i]); } if (rest.empty()) return new_expr; TRACE("non_linear", tout << "rest size: " << rest.size() << ", i1: " << i1 << ", i2: " << i2 << "\n";); expr * h = cross_nested(rest, nullptr); expr * r = m_util.mk_add(new_expr, h); m_nl_new_exprs.push_back(r); return r; } } } return horner(p, var); } /** \brief Check whether the given polynomial is consistent with respect to the known bounds. The polynomial is converted into an equivalent cross nested form. */ template bool theory_arith::is_cross_nested_consistent(sbuffer & p) { sbuffer varinfo; get_polynomial_info(p, varinfo); if (varinfo.empty()) return true; std::stable_sort(varinfo.begin(), varinfo.end(), var_num_occs_lt()); TRACE("cross_nested", tout << "var num occs:\n"; for (auto const& kv : varinfo) { tout << mk_bounded_pp(kv.first, get_manager()) << " -> " << kv.second << "\n"; }); for (auto const& kv : varinfo) { m_nl_new_exprs.reset(); expr * var = kv.first; expr * cn = cross_nested(p, var); // Remark: cn may not be well-sorted because, since a row may contain mixed integer/real monomials. // This is not really a problem, since evaluate_as_interval will work even if cn is not well-sorted. if (!cn) continue; TRACE("cross_nested", tout << "nested form for var:\n" << mk_ismt2_pp(var, get_manager()) << "\n"; display_nested_form(tout, cn); tout << "\n"; tout << "c:\n" << mk_ismt2_pp(cn, get_manager()) << "\n";); interval i = evaluate_as_interval(cn); TRACE("cross_nested", tout << "interval: " << i << "\n";); v_dependency * d = nullptr; if (!i.minus_infinity() && (i.get_lower_value().is_pos() || (i.get_lower_value().is_zero() && i.is_lower_open()))) d = i.get_lower_dependencies(); else if (!i.plus_infinity() && (i.get_upper_value().is_neg() || (i.get_upper_value().is_zero() && i.is_upper_open()))) d = i.get_upper_dependencies(); if (d) { TRACE("cross_nested", tout << "nested form conflict: " << i << "\n";); set_conflict(d); return false; } } return true; } /** \brief Check whether the polynomial represented by the current row is consistent with respect to the known bound when converted into a equivalent cross nested form. */ template bool theory_arith::is_cross_nested_consistent(row const & r) { TRACE("cross_nested", tout << "is_cross_nested_consistent:\n"; display_row(tout, r, false);); if (!is_problematic_non_linear_row(r)) return true; TRACE("cross_nested", tout << "problematic...\n";); /* The method is_cross_nested converts rows back to expressions. The conversion back to expressions may create sort incorrect expressions. This is in some sense ok, since these expressions are temporary, but the sort incorrect expressions may generate assertion violations. Sort incorrect expressions may be created in the following cases: 1) mixed real int rows. 2) int rows that contain non integer coefficients. 3) int rows that when converted to cross nested form use non integer coefficients. There are several ways to deal with this problem: a) Ignore the assertion violations. Disadvantage: it will prevent us from running Z3 in debug mode on some benchmarks. b) Remove the assertions. Disadvantage: these assertions helped us to find many important bugs in Z3 c) Disable the assertions temporally. This sounds like a big HACK. d) Use a different data-structure to represent polynomials in cross-nested form. Disadvantage: code duplication, the data-structure is essentially identical to the ASTs we are using right now. e) Disable the test when we cannot create a well-sorted expression. I'm temporally using this solution. I implemented the following logic: 1) (mixed real int) Disable the test. Most benchmarks do not contain mixed int real variables. 2) (int coeffs) I multiply the row by a constant to force it to have only integer coefficients. 3) (new non-int coeffs) This only happens in an optional step in the conversion. Now, for int rows, I only apply this optional step only if non-int coeffs are not created. */ if (!get_manager().int_real_coercions() && is_mixed_real_integer(r)) return true; // giving up... see comment above TRACE("cross_nested", tout << "checking problematic row...\n";); rational c = rational::one(); if (is_integer(r)) c = r.get_denominators_lcm().to_rational(); TRACE("non_linear", tout << "check problematic row:\n"; display_row(tout, r); display_row(tout, r, false);); sbuffer p; for (row_entry const& re : r) { if (!re.is_dead()) p.push_back(coeff_expr(re.m_coeff.to_rational() * c, var2expr(re.m_var))); } SASSERT(!p.empty()); CTRACE("cross_nested_bug", !c.is_one(), tout << "c: " << c << "\n"; display_row(tout, r); tout << "---> p (coeffs, exprs):\n"; display_coeff_exprs(tout, p);); return is_cross_nested_consistent(p); } /** \brief Check whether an inconsistency can be found using cross nested form in the non linear cluster. */ template bool theory_arith::is_cross_nested_consistent(svector const & nl_cluster) { for (theory_var v : nl_cluster) { if (!is_base(v)) continue; m_stats.m_nl_cross_nested++; row const & r = m_rows[get_var_row(v)]; if (!is_cross_nested_consistent(r)) return false; } return true; } #define FIXED 0 #define QUOTED_FIXED 1 #define BOUNDED 2 #define QUOTED_BOUNDED 3 #define NOT_FREE 4 #define QUOTED_NOT_FREE 5 #define FREE 6 #define QUOTED_FREE 7 #define MAX_DEFAULT_WEIGHT 7 /** \brief Initialize variable order for grobner basis computation. Make: "quoted free vars" > "free vars" > "quoted variables with lower or upper bounds" > "variables with lower or upper bounds" > "quoted bounded variables" > "bounded variables" > "quoted fixed variables" > "fixed variables" */ template void theory_arith::init_grobner_var_order(svector const & nl_cluster, grobner & gb) { // Initialize variable order for (theory_var v : nl_cluster) { expr * var = var2expr(v); if (is_fixed(v)) { gb.set_weight(var, is_pure_monomial(var) ? QUOTED_FIXED : FIXED); } else if (is_bounded(v)) { gb.set_weight(var, is_pure_monomial(var) ? QUOTED_BOUNDED : BOUNDED); } else if (lower(v) || upper(v)) { gb.set_weight(var, is_pure_monomial(var) ? QUOTED_NOT_FREE : NOT_FREE); } else { SASSERT(is_free(v)); gb.set_weight(var, is_pure_monomial(var) ? QUOTED_FREE : FREE); } } } /** \brief Create a new monomial using the given coeff and m. Fixed variables in m are substituted by their values. The arg dep is updated to store these dependencies. The set already_found is updated with the fixed variables in m. A variable is only added to dep if it is not already in already_found. Return null if the monomial was simplified to 0. */ template grobner::monomial * theory_arith::mk_gb_monomial(rational const & _coeff, expr * m, grobner & gb, v_dependency * & dep, var_set & already_found) { ptr_buffer vars; rational coeff = _coeff; rational r; #undef PROC_VAR #define PROC_VAR(VAR) { \ if (m_util.is_numeral(VAR, r)) { \ coeff *= r; \ } \ else { \ theory_var _var = expr2var(VAR); \ if (is_fixed(_var)) { \ if (!already_found.contains(_var)) { \ already_found.insert(_var); \ dep = m_dep_manager.mk_join(dep, m_dep_manager.mk_join(m_dep_manager.mk_leaf(lower(_var)), m_dep_manager.mk_leaf(upper(_var)))); \ } \ coeff *= lower_bound(_var).get_rational().to_rational(); \ } \ else { \ vars.push_back(VAR); \ } \ } \ } if (m_util.is_mul(m)) { coeff *= get_monomial_coeff(m); m = get_monomial_body(m); if (m_util.is_mul(m)) { SASSERT(is_pure_monomial(m)); for (unsigned i = 0; i < to_app(m)->get_num_args(); i++) { expr * arg = to_app(m)->get_arg(i); PROC_VAR(arg); } } else { PROC_VAR(m); } } else { PROC_VAR(m); } if (!coeff.is_zero()) return gb.mk_monomial(coeff, vars.size(), vars.c_ptr()); else return nullptr; } /** \brief Send the given row to the grobner basis object. All fixed variables are substituted before sending the row to gb. */ template void theory_arith::add_row_to_gb(row const & r, grobner & gb) { TRACE("non_linear", tout << "adding row to gb\n"; display_row(tout, r);); ptr_buffer monomials; v_dependency * dep = nullptr; m_tmp_var_set.reset(); typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (!it->is_dead()) { rational coeff = it->m_coeff.to_rational(); expr * m = var2expr(it->m_var); TRACE("non_linear", tout << "monomial: " << mk_pp(m, get_manager()) << "\n";); grobner::monomial * new_m = mk_gb_monomial(coeff, m, gb, dep, m_tmp_var_set); TRACE("non_linear", tout << "new monomial:\n"; if (new_m) gb.display_monomial(tout, *new_m); else tout << "null"; tout << "\n";); if (new_m) monomials.push_back(new_m); } } gb.assert_eq_0(monomials.size(), monomials.c_ptr(), dep); } /** \brief v must be a pure monomial. That is, v = (quote (* x_1 ... x_n)) Add the monomial (quote (* x_1 ... x_n)) = x_1 * ... * x_n. Fixed variables are substituted. */ template void theory_arith::add_monomial_def_to_gb(theory_var v, grobner & gb) { ptr_buffer monomials; v_dependency * dep = nullptr; m_tmp_var_set.reset(); expr * m = var2expr(v); SASSERT(is_pure_monomial(m)); grobner::monomial * new_m = mk_gb_monomial(rational(1), m, gb, dep, m_tmp_var_set); if (new_m) monomials.push_back(new_m); rational coeff(-1); if (is_fixed(v)) { dep = m_dep_manager.mk_join(dep, m_dep_manager.mk_join(m_dep_manager.mk_leaf(lower(v)), m_dep_manager.mk_leaf(upper(v)))); coeff *= lower_bound(v).get_rational().to_rational(); if (!coeff.is_zero()) monomials.push_back(gb.mk_monomial(coeff, 0, nullptr)); } else { monomials.push_back(gb.mk_monomial(coeff, 1, &m)); } gb.assert_eq_0(monomials.size(), monomials.c_ptr(), dep); } /** Initialize grobner basis data structure using the non linear cluster. The GB is initialized using rows and non linear monomials. */ template void theory_arith::init_grobner(svector const & nl_cluster, grobner & gb) { init_grobner_var_order(nl_cluster, gb); for (theory_var v : nl_cluster) { if (is_base(v)) { row const & r = m_rows[get_var_row(v)]; add_row_to_gb(r, gb); } if (is_pure_monomial(v) && !m_data[v].m_nl_propagated && is_fixed(v)) { add_monomial_def_to_gb(v, gb); } } } /** \brief Return the interval for the given monomial */ template interval theory_arith::mk_interval_for(grobner::monomial const * m) { interval r(m_dep_manager, rational(m->get_coeff())); expr * var = nullptr; unsigned power = 0; unsigned num_vars = m->get_degree(); for (unsigned i = 0; i < num_vars; i++) { expr * curr = m->get_var(i); if (var == nullptr) { var = curr; power = 1; } else if (curr == var) { power++; } else { mul_bound_of(var, power, r); var = curr; power = 1; } } if (var != nullptr) mul_bound_of(var, power, r); return r; } /** \brief Set a conflict using a dependency object. */ template void theory_arith::set_conflict(v_dependency * d) { antecedents ante(*this); derived_bound b(null_theory_var, inf_numeral(0), B_LOWER); dependency2new_bound(d, b); set_conflict(b, ante, "arith_nl"); TRACE("non_linear", for (literal lit : b.m_lits) get_context().display_literal_verbose(tout, lit) << "\n"; tout << "\n";); } /** \brief Return true if I.get_lower() <= - M_1 - ... - M_n <= I.get_upper() is inconsistent. Where M_i is monomials[i] and n = num_monomials. A conflict will also be set using the bounds of the variables occurring in the monomials M_i's. */ template bool theory_arith::is_inconsistent(interval const & I, unsigned num_monomials, grobner::monomial * const * monomials, v_dependency * dep) { interval r(I); for (unsigned i = 0; i < num_monomials; i++) { grobner::monomial const * m = monomials[i]; r += mk_interval_for(m); if (r.minus_infinity() && r.plus_infinity()) return false; } TRACE("non_linear_bug", tout << "is_inconsistent, r: " << r << "\n";); v_dependency * interval_deps = nullptr; bool conflict = false; if (!r.minus_infinity() && (r.get_lower_value().is_pos() || (r.get_lower_value().is_zero() && r.is_lower_open()))) { interval_deps = r.get_lower_dependencies(); conflict = true; TRACE("non_linear_bug", tout << "is inconsistent, interval_deps: " << interval_deps << "\n";); } else if (!r.plus_infinity() && (r.get_upper_value().is_neg() || (r.get_upper_value().is_zero() && r.is_upper_open()))) { interval_deps = r.get_upper_dependencies(); conflict = true; TRACE("non_linear_bug", tout << "is inconsistent, interval_deps: " << interval_deps << "\n";); } // interval_deps cannot be used to check if a conflict was detected, since interval_deps may be 0 even when r does not contain 0 if (conflict) { TRACE("non_linear", tout << "conflicting interval for = 0 equation: " << r << "\n";); set_conflict(m_dep_manager.mk_join(interval_deps, dep)); return true; } return false; } /** \brief Return true if the equation is inconsistent, and sign a conflict. */ template bool theory_arith::is_inconsistent(grobner::equation const * eq, grobner & gb) { interval zero(m_dep_manager, rational(0)); if (is_inconsistent(zero, eq->get_num_monomials(), eq->get_monomials(), eq->get_dependency())) { TRACE("non_linear", tout << "found conflict\n"; gb.display_equation(tout, *eq);); return true; } return false; } /** \brief Return true if the given monomial c*M is squared. The square root of the c is stored in r. */ bool is_perfect_square(grobner::monomial const * m, rational & r) { unsigned num_vars = m->get_degree(); if (num_vars % 2 == 1) return false; if (!m->get_coeff().is_perfect_square(r)) return false; expr * var = nullptr; unsigned power = 0; for (unsigned i = 0; i < num_vars; i++) { expr * curr = m->get_var(i); if (var == nullptr) { var = curr; power = 1; } else if (var == curr) { power++; } else { if (power % 2 == 1) return false; var = curr; power = 1; } } return power % 2 == 0; } /** \brief Return m1m2 is of the form (-2ab)*M1*M2 assuming that m1_sq = a^2*M1*M1 m2_sq = b^2*M2*M2 */ bool is_perfect_square(grobner::monomial const * m1_sq, rational const & a, grobner::monomial const * m2_sq, rational const & b, grobner::monomial const * m1m2) { DEBUG_CODE({ rational a1; rational b1; SASSERT(is_perfect_square(m1_sq, a1) && a == a1 && is_perfect_square(m2_sq, b1) && b == b1); }); if (m1m2->get_coeff().is_nonneg()) return false; rational c(-2); c *= a; c *= b; if (m1m2->get_coeff() != c) return false; unsigned num1 = m1_sq->get_degree(); unsigned num2 = m2_sq->get_degree(); unsigned num12 = m1m2->get_degree(); if (num1 + num2 != num12 * 2) return false; unsigned i1, i2, i12; i1 = i2 = i12 = 0; while (true) { expr * v1 = nullptr; expr * v2 = nullptr; expr * v12 = nullptr; if (i1 < num1) v1 = m1_sq->get_var(i1); if (i2 < num2) v2 = m2_sq->get_var(i2); if (i12 < num12) v12 = m1m2->get_var(i12); if (v1 == nullptr && v2 == nullptr && v12 == nullptr) return true; if (v12 == nullptr) return false; if (v1 == v12) { SASSERT(m1_sq->get_var(i1+1) == v1); i1 += 2; i12 ++; } else if (v2 == v12) { SASSERT(m2_sq->get_var(i2+1) == v2); i2 += 2; i12 ++; } else { return false; } } } /** \brief Return true if the equation is inconsistent. In this version, perfect squares are eliminated, and replaced with the interval [0, oo), if the interval associated with them is less precise than [0, oo). \remark I track only simple perfect squares of the form (M1 - M2)^2, where M1 and M2 are arbitrary monomials. */ template bool theory_arith::is_inconsistent2(grobner::equation const * eq, grobner & gb) { // TODO: a possible improvement: create a quotation for (M1 - M2)^2 // instead of trying to find it in a specific equation. // This approach is more precise, but more expensive // since a new row must be created. buffer intervals; unsigned num = eq->get_num_monomials(); for (unsigned i = 0; i < num; i++) { grobner::monomial const * m = eq->get_monomial(i); intervals.push_back(mk_interval_for(m)); } sbuffer deleted; deleted.resize(num, false); ptr_buffer monomials; // try to eliminate monomials that form perfect squares of the form (M1 - M2)^2 for (unsigned i = 0; i < num; i++) { grobner::monomial const * m1 = eq->get_monomial(i); rational a; if (deleted[i]) continue; if (!is_perfect_square(m1, a)) { monomials.push_back(const_cast(m1)); continue; } TRACE("non_linear", tout << "found perfect square monomial m1: "; gb.display_monomial(tout, *m1); tout << "\n";); // try to find another perfect square unsigned j = i + 1; for (; j < num; j++) { if (deleted[j]) continue; grobner::monomial const * m2 = eq->get_monomial(j); rational b; if (!is_perfect_square(m2, b)) continue; TRACE("non_linear", tout << "found perfect square monomial m2: "; gb.display_monomial(tout, *m2); tout << "\n";); // try to find -2*root(m1)*root(m2) // This monomial must be smaller than m1, since m2 is smaller than m1. unsigned k = i + 1; for (; k < num; k++) { if (deleted[k]) continue; grobner::monomial const * m1m2 = eq->get_monomial(k); if (!is_perfect_square(m1, a, m2, b, m1m2)) continue; // m1, m2, and m1m2 form a perfect square. // check if [0, oo) provides a better lowerbound than adding the intervals of m1, m2 and m1m2; TRACE("non_linear", tout << "found perfect square (M1-M2)^2:\n"; gb.display_monomial(tout, *m1); tout << "\n"; gb.display_monomial(tout, *m2); tout << "\n"; gb.display_monomial(tout, *m1m2); tout << "\n";); interval I = intervals[i]; I += intervals[j]; I += intervals[k]; if (I.minus_infinity() || I.get_lower_value().is_neg()) { TRACE("non_linear", tout << "the lower bound improved when perfect square is eliminated.\n";); // Found improvement... // mark these monomials as deleted deleted[i] = true; deleted[j] = true; deleted[k] = true; break; } } if (k < num) break; // found perfect square } if (j == num) { // didn't find perfect square of the form (M1-M2)^2 monomials.push_back(const_cast(m1)); } } if (monomials.size() == num) return false; // didn't find any perfect square. interval ge_zero(m_dep_manager, rational(0), false, true, nullptr); if (is_inconsistent(ge_zero, monomials.size(), monomials.c_ptr(), eq->get_dependency())) { TRACE("non_linear", tout << "found conflict\n"; gb.display_equation(tout, *eq);); return true; } return false; } template expr * theory_arith::monomial2expr(grobner::monomial const * m, bool is_int) { unsigned num_vars = m->get_degree(); ptr_buffer args; if (!m->get_coeff().is_one()) args.push_back(m_util.mk_numeral(m->get_coeff(), is_int)); for (unsigned j = 0; j < num_vars; j++) args.push_back(m->get_var(j)); return mk_nary_mul(args.size(), args.c_ptr(), is_int); } /** \brief Assert the new equation in the simplex tableau. */ template bool theory_arith::internalize_gb_eq(grobner::equation const * eq) { bool is_int = false; unsigned num_monomials = eq->get_num_monomials(); for (unsigned i = 0; i < num_monomials; i++) { grobner::monomial const * m = eq->get_monomial(i); unsigned degree = m->get_degree(); if (degree > m_params.m_nl_arith_max_degree) return false; if (degree > 0) is_int = m_util.is_int(m->get_var(0)); } rational k; ptr_buffer args; for (unsigned i = 0; i < num_monomials; i++) { grobner::monomial const * m = eq->get_monomial(i); if (m->get_degree() == 0) k -= m->get_coeff(); else args.push_back(monomial2expr(eq->get_monomial(i), is_int)); } context & ctx = get_context(); th_rewriter& s = ctx.get_rewriter(); expr_ref pol(get_manager()); SASSERT(!args.empty()); pol = mk_nary_add(args.size(), args.c_ptr()); expr_ref s_pol(get_manager()); proof_ref pr(get_manager()); TRACE("gb_bug", tout << mk_ll_pp(pol, get_manager()) << "\n";); s(pol, s_pol, pr); if (!has_var(s_pol)) { TRACE("spol_bug", tout << "internalizing...\n" << mk_ll_pp(s_pol, get_manager()) << "\n";); ctx.internalize(s_pol, false); ctx.mark_as_relevant(s_pol.get()); } SASSERT(has_var(s_pol.get())); // s_pol = k theory_var v = expr2var(s_pol); // v = k CTRACE("spol_bug", v == null_theory_var, tout << mk_ll_pp(s_pol, get_manager()) << "\n"; display(tout);); SASSERT(v != null_theory_var); // assert bounds for s_pol mk_derived_nl_bound(v, inf_numeral(k), B_LOWER, eq->get_dependency()); mk_derived_nl_bound(v, inf_numeral(k), B_UPPER, eq->get_dependency()); TRACE("non_linear", tout << "inserted new equation into the tableau\n"; display_var(tout, v);); return true; } /** \brief Compute Grobner basis, return true if a conflict or new fixed variables were detected. */ template typename theory_arith::gb_result theory_arith::compute_grobner(svector const & nl_cluster) { if (m_nl_gb_exhausted) return GB_FAIL; grobner gb(get_manager(), m_dep_manager); init_grobner(nl_cluster, gb); TRACE("non_linear", display(tout);); bool warn = false; unsigned next_weight = MAX_DEFAULT_WEIGHT + 1; // next weight using during perturbation phase. ptr_vector eqs; while (true) { TRACE("non_linear_gb", tout << "before:\n"; gb.display(tout);); bool r = false; gb.compute_basis_init(); while (!r && gb.get_num_new_equations() < m_params.m_nl_arith_gb_threshold) { if (get_context().get_cancel_flag()) { warn = true; break; } r = gb.compute_basis_step(); } m_stats.m_gb_simplify += gb.m_stats.m_simplify; m_stats.m_gb_superpose += gb.m_stats.m_superpose; m_stats.m_gb_num_processed += gb.m_stats.m_num_processed; m_stats.m_gb_compute_basis++; if (!r && !warn) { IF_VERBOSE(3, verbose_stream() << "Grobner basis computation interrupted. Increase threshold using NL_ARITH_GB_THRESHOLD=\n";); get_context().push_trail(value_trail(m_nl_gb_exhausted)); m_nl_gb_exhausted = true; warn = true; } if (get_context().get_cancel_flag()) { return GB_FAIL; } TRACE("non_linear_gb", tout << "after:\n"; gb.display(tout);); // Scan the grobner basis eqs, and look for inconsistencies. eqs.reset(); gb.get_equations(eqs); TRACE("grobner_bug", tout << "after gb\n";); for (grobner::equation* eq : eqs) { TRACE("grobner_bug", gb.display_equation(tout, *eq);); if (is_inconsistent(eq, gb)) return GB_PROGRESS; if (is_inconsistent2(eq, gb)) return GB_PROGRESS; } // Scan the grobner basis eqs for equations of the form x - k = 0 or x = 0 is found, and x is not fixed, // then assert bounds for x, and continue gb_result result = GB_FAIL; if (m_params.m_nl_arith_gb_eqs) { for (grobner::equation* eq : eqs) { if (!eq->is_linear_combination()) { TRACE("non_linear", tout << "processing new equality:\n"; gb.display_equation(tout, *eq);); TRACE("non_linear_bug", tout << "processing new equality:\n"; gb.display_equation(tout, *eq);); if (internalize_gb_eq(eq)) result = GB_NEW_EQ; } } } if (result != GB_FAIL) return result; if (!m_params.m_nl_arith_gb_perturbate) return result; if (m_nl_gb_exhausted) return result; // Try to change the variable order... in such a way the leading term is modified. // I only consider linear equations... (HACK) // Moreover, I do not change the weight of a variable more than once in this loop. bool modified = false; for (grobner::equation const* eq : eqs) { unsigned num_monomials = eq->get_num_monomials(); CTRACE("grobner_bug", num_monomials <= 0, gb.display_equation(tout, *eq);); if (num_monomials == 0) continue; // HACK: the equation 0 = 0, should have been discarded by the GB module. if (eq->get_monomial(0)->get_degree() != 1) continue; for (unsigned j = 1; j < num_monomials; j++) { grobner::monomial const * m = eq->get_monomial(j); if (m->get_degree() != 1) continue; expr * var = m->get_var(0); if (gb.get_weight(var) > MAX_DEFAULT_WEIGHT) continue; // variable was already updated TRACE("non_linear", tout << "increased weight of: " << mk_bounded_pp(var, get_manager()) << "\n";); gb.set_weight(var, next_weight); next_weight++; gb.update_order(); TRACE("non_linear", tout << "after updating order\n"; gb.display(tout);); modified = true; break; } if (modified) break; } if (!modified) return result; } } /** \brief Maximize/Minimize variables in non linear monomials. */ template bool theory_arith::max_min_nl_vars() { var_set already_found; svector vars; for (theory_var v : m_nl_monomials) { mark_var(v, vars, already_found); expr * n = var2expr(v); SASSERT(is_pure_monomial(n)); for (expr * curr : *to_app(n)) { theory_var v = expr2var(curr); SASSERT(v != null_theory_var); mark_var(v, vars, already_found); } } return max_min(vars); } /** \brief Process non linear constraints. */ template final_check_status theory_arith::process_non_linear() { m_model_depends_on_computed_epsilon = false; if (m_nl_monomials.empty()) return FC_DONE; if (check_monomial_assignments()) { return FC_DONE; } if (!m_params.m_nl_arith) { TRACE("non_linear", tout << "Non-linear is not enabled\n";); return FC_GIVEUP; } TRACE("process_non_linear", display(tout);); if (m_nl_rounds > m_params.m_nl_arith_rounds) { TRACE("non_linear", tout << "GIVEUP non linear problem...\n";); IF_VERBOSE(3, verbose_stream() << "Max. non linear arithmetic rounds. Increase threshold using NL_ARITH_ROUNDS=\n";); return FC_GIVEUP; } get_context().push_trail(value_trail(m_nl_rounds)); m_nl_rounds++; elim_quasi_base_rows(); move_non_base_vars_to_bounds(); TRACE("non_linear_verbose", tout << "processing non linear constraints...\n"; get_context().display(tout);); if (!make_feasible()) { TRACE("non_linear", tout << "failed to move variables to bounds.\n";); failed(); return FC_CONTINUE; } if (!max_min_nl_vars()) return FC_CONTINUE; if (check_monomial_assignments()) { return m_liberal_final_check || !m_changed_assignment ? FC_DONE : FC_CONTINUE; } svector vars; get_non_linear_cluster(vars); bool progress; unsigned old_idx = m_nl_strategy_idx; get_context().push_trail(value_trail(m_nl_strategy_idx)); do { progress = false; switch (m_nl_strategy_idx) { case 0: if (propagate_nl_bounds()) { propagate_core(); progress = true; } break; case 1: if (!is_cross_nested_consistent(vars)) progress = true; break; case 2: if (m_params.m_nl_arith_gb) { switch(compute_grobner(vars)) { case GB_PROGRESS: progress = true; break; case GB_NEW_EQ: progress = true; propagate_core(); break; case GB_FAIL: break; } } break; case 3: if (m_params.m_nl_arith_branching) { theory_var target = find_nl_var_for_branching(); if (target != null_theory_var && branch_nl_int_var(target)) progress = true; } break; } m_nl_strategy_idx = (m_nl_strategy_idx + 1) % 4; if (progress) return FC_CONTINUE; } while (m_nl_strategy_idx != old_idx); if (check_monomial_assignments()) { return m_liberal_final_check || !m_changed_assignment ? FC_DONE : FC_CONTINUE; } TRACE("non_linear", display(tout);); return FC_GIVEUP; } }; #endif /* THEORY_ARITH_NL_H_ */ z3-z3-4.8.7/src/smt/theory_arith_pp.h000066400000000000000000000460571356505360400174060ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_arith_pp.h Abstract: Author: Leonardo de Moura (leonardo) 2008-05-05. Revision History: --*/ #ifndef THEORY_ARITH_PP_H_ #define THEORY_ARITH_PP_H_ #include "smt/theory_arith.h" #include "ast/ast_smt_pp.h" #include "util/stats.h" namespace smt { template void theory_arith::collect_statistics(::statistics & st) const { st.update("arith conflicts", m_stats.m_conflicts); st.update("arith add rows", m_stats.m_add_rows); st.update("arith pivots", m_stats.m_pivots); st.update("arith assert lower", m_stats.m_assert_lower); st.update("arith assert upper", m_stats.m_assert_upper); st.update("arith assert diseq", m_stats.m_assert_diseq); st.update("arith bound prop", m_stats.m_bound_props); st.update("arith fixed eqs", m_stats.m_fixed_eqs); st.update("arith offset eqs", m_stats.m_offset_eqs); st.update("arith gcd tests", m_stats.m_gcd_tests); st.update("arith ineq splits", m_stats.m_branches); st.update("arith gomory cuts", m_stats.m_gomory_cuts); st.update("arith patches", m_stats.m_patches); st.update("arith patches_succ", m_stats.m_patches_succ); st.update("arith max-min", m_stats.m_max_min); st.update("arith grobner", m_stats.m_gb_compute_basis); st.update("arith pseudo nonlinear", m_stats.m_nl_linear); st.update("arith nonlinear bounds", m_stats.m_nl_bounds); st.update("arith nonlinear horner", m_stats.m_nl_cross_nested); m_arith_eq_adapter.collect_statistics(st); } template void theory_arith::display(std::ostream & out) const { if (get_num_vars() == 0) return; out << "Theory arithmetic:\n"; display_vars(out); display_nl_monomials(out); display_rows(out, true); display_rows(out, false); display_atoms(out); display_asserted_atoms(out); } template void theory_arith::display_nl_monomials(std::ostream & out) const { if (m_nl_monomials.empty()) return; out << "non linear monomials:\n"; svector::const_iterator it = m_nl_monomials.begin(); svector::const_iterator end = m_nl_monomials.end(); for (; it != end; ++it) display_var(out, *it); } template void theory_arith::display_row(std::ostream & out, unsigned r_id, bool compact) const { out << r_id << " "; display_row(out, m_rows[r_id], compact); } template void theory_arith::display_row(std::ostream & out, row const & r, bool compact) const { typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); out << "(v" << r.get_base_var() << ") : "; bool first = true; for (; it != end; ++it) { if (!it->is_dead()) { if (first) first = false; else out << " + "; theory_var s = it->m_var; numeral const & c = it->m_coeff; if (!c.is_one()) out << c << "*"; if (compact) { out << "v" << s; if (is_fixed(s)) { out << ":" << lower(s)->get_value(); } } else display_var_flat_def(out, s); } } out << "\n"; } template void theory_arith::display_rows(std::ostream & out, bool compact) const { if (compact) out << "rows (compact view):\n"; else out << "rows (expanded view):\n"; unsigned num = m_rows.size(); for (unsigned r_id = 0; r_id < num; r_id++) { if (m_rows[r_id].m_base_var != null_theory_var) { display_row(out, r_id, compact); } } } template void theory_arith::display_row_shape(std::ostream & out, row const & r) const { typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (!it->is_dead()) { numeral const & c = it->m_coeff; if (c.is_one()) out << "1"; else if (c.is_minus_one()) out << "-"; else if (c.is_int() && c.to_rational().is_small()) out << "i"; else if (c.is_int() && !c.to_rational().is_small()) out << "I"; else if (c.to_rational().is_small()) out << "r"; else out << "R"; } } out << "\n"; } template bool theory_arith::is_one_minus_one_row(row const & r) const { typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (!it->is_dead()) { numeral const & c = it->m_coeff; if (!c.is_one() && !c.is_minus_one()) return false; } } return true; } template void theory_arith::display_rows_shape(std::ostream & out) const { unsigned num = m_rows.size(); unsigned num_trivial = 0; for (unsigned r_id = 0; r_id < num; r_id++) { row const & r = m_rows[r_id]; if (r.m_base_var != null_theory_var) { if (is_one_minus_one_row(r)) num_trivial++; else display_row_shape(out, r); } } out << "num. trivial: " << num_trivial << "\n"; } template void theory_arith::display_rows_bignums(std::ostream & out) const { unsigned num = m_rows.size(); for (unsigned r_id = 0; r_id < num; r_id++) { row const & r = m_rows[r_id]; if (r.m_base_var != null_theory_var) { typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (!it->is_dead()) { numeral const & c = it->m_coeff; if (c.to_rational().is_big()) { std::string str = c.to_rational().to_string(); if (str.length() > 48) out << str << "\n"; } } } } } } template void theory_arith::display_rows_stats(std::ostream & out) const { unsigned num_vars = get_num_vars(); unsigned num_rows = 0; unsigned num_non_zeros = 0; unsigned num_ones = 0; unsigned num_minus_ones = 0; unsigned num_small_ints = 0; unsigned num_big_ints = 0; unsigned num_small_rats = 0; unsigned num_big_rats = 0; for (unsigned r_id = 0; r_id < m_rows.size(); r_id++) { row const & r = m_rows[r_id]; if (r.m_base_var != null_theory_var) { num_rows++; typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (!it->is_dead()) { numeral const & c = it->m_coeff; num_non_zeros++; if (c.is_one()) num_ones++; else if (c.is_minus_one()) num_minus_ones++; else if (c.is_int() && c.to_rational().is_small()) num_small_ints++; else if (c.is_int() && !c.to_rational().is_small()) num_big_ints++; else if (c.to_rational().is_small()) num_small_rats++; else num_big_rats++; } } } } out << "A: " << num_rows << " X " << num_vars << "\n"; out << "avg. row: " << num_non_zeros / num_rows << ", num. non zeros: " << num_non_zeros << "\n"; unsigned spc = 6; out.width(spc); out << 1 << "|"; out.width(spc); out << -1 << "|"; out.width(spc); out << "i"; out << "|"; out.width(spc); out << "I"; out << "|"; out.width(spc); out << "r"; out << "|"; out.width(spc); out << "R"; out << "\n"; out.width(spc); out << num_ones << "|"; out.width(spc); out << num_minus_ones << "|"; out.width(spc); out << num_small_ints; out << "|"; out.width(spc); out << num_big_ints; out << "|"; out.width(spc); out << num_small_rats; out << "|"; out.width(spc); out << num_big_rats; out << "\n"; } template void theory_arith::display_row_info(std::ostream & out, unsigned r_id) const { out << r_id << " "; display_row_info(out, m_rows[r_id]); } template void theory_arith::display_row_info(std::ostream & out, row const & r) const { display_row(out, r, true); typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) if (!it->is_dead()) display_var(out, it->m_var); } /** \brief Display row after substituting fixed variables. */ template void theory_arith::display_simplified_row(std::ostream & out, row const & r) const { bool has_rat_coeff = false; numeral k; typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); out << "(v" << r.get_base_var() << ") : "; bool first = true; for (; it != end; ++it) { if (it->is_dead()) continue; theory_var v = it->m_var; numeral const & c = it->m_coeff; if (is_fixed(v)) { k += c * lower_bound(v).get_rational(); continue; } if (!c.is_int()) has_rat_coeff = true; if (first) first = false; else out << " + "; if (!c.is_one()) out << c << "*"; out << "v" << v; } if (!k.is_zero()) { if (!first) out << " + "; out << k; } out << "\n"; if (has_rat_coeff) { typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) if (!it->is_dead() && (is_base(it->m_var) || (!is_fixed(it->m_var) && (lower(it->m_var) || upper(it->m_var))))) display_var(out, it->m_var); } } template void theory_arith::display_var(std::ostream & out, theory_var v) const { out << "v"; out.width(4); out << std::left << v; out << " #"; out.width(4); out << get_enode(v)->get_owner_id(); out << std::right; out << " lo:"; out.width(10); if (lower(v)) { out << lower(v)->get_value(); } else { out << "-oo"; } out << ", up:"; out.width(10); if (upper(v)) { out << upper(v)->get_value(); } else { out << "oo"; } out << ", value: "; out.width(10); out << get_value(v); out << ", occs: "; out.width(4); out << m_columns[v].size(); out << ", atoms: "; out.width(4); out << m_var_occs[v].size(); out << (is_int(v) ? ", int " : ", real"); switch (get_var_kind(v)) { case NON_BASE: out << ", non-base "; break; case QUASI_BASE: out << ", quasi-base"; break; case BASE: out << ", base "; break; } out << ", shared: " << get_context().is_shared(get_enode(v)); out << ", unassigned: " << m_unassigned_atoms[v]; out << ", rel: " << get_context().is_relevant(get_enode(v)); out << ", def: "; display_var_flat_def(out, v); out << "\n"; } template void theory_arith::display_vars(std::ostream & out) const { out << "vars:\n"; int n = get_num_vars(); int inf_vars = 0; int int_inf_vars = 0; for (theory_var v = 0; v < n; v++) { if ((lower(v) && lower(v)->get_value() > get_value(v)) || (upper(v) && upper(v)->get_value() < get_value(v))) inf_vars++; if (is_int(v) && !get_value(v).is_int()) int_inf_vars++; } out << "infeasibles = " << inf_vars << " int_inf = " << int_inf_vars << std::endl; for (theory_var v = 0; v < n; v++) { display_var(out, v); } } template void theory_arith::display_bound(std::ostream & out, bound * b, unsigned indent) const { for (unsigned i = 0; i < indent; i++) out << " "; b->display(*this, out); out << "\n"; } template std::ostream& theory_arith::antecedents_t::display(theory_arith& th, std::ostream & out) const { th.get_context().display_literals_verbose(out, lits().size(), lits().c_ptr()); if (!lits().empty()) out << "\n"; ast_manager& m = th.get_manager(); for (auto const& e : m_eqs) { out << mk_pp(e.first->get_owner(), m) << " "; out << mk_pp(e.second->get_owner(), m) << "\n"; } return out; } template void theory_arith::display_deps(std::ostream & out, v_dependency* dep) { ptr_vector bounds; m_dep_manager.linearize(dep, bounds); m_tmp_lit_set.reset(); m_tmp_eq_set.reset(); for (void *_b : bounds) { bound * b = static_cast(_b); b->display(*this, out << "\n"); } } template void theory_arith::display_interval(std::ostream & out, interval const& i) { i.display(out); display_deps(out << "\nlo:", i.get_lower_dependencies()); display_deps(out << "\nhi:", i.get_upper_dependencies()); } template void theory_arith::display_atoms(std::ostream & out) const { out << "atoms:\n"; for (atom * a : m_atoms) display_atom(out, a, false); } template void theory_arith::display_asserted_atoms(std::ostream & out) const { out << "asserted atoms:\n"; for (unsigned i = 0; i < m_asserted_qhead; i++) { bound * b = m_asserted_bounds[i]; if (b->is_atom()) display_atom(out, static_cast(b), true); } if (m_asserted_qhead < m_asserted_bounds.size()) { out << "delayed atoms:\n"; for (unsigned i = m_asserted_qhead; i < m_asserted_bounds.size(); i++) { bound * b = m_asserted_bounds[i]; if (b->is_atom()) display_atom(out, static_cast(b), true); } } } template void theory_arith::display_atom(std::ostream & out, atom * a, bool show_sign) const { theory_var v = a->get_var(); inf_numeral const & k = a->get_k(); enode * e = get_enode(v); if (show_sign) { if (!a->is_true()) out << "not "; else out << " "; } out << "v"; out.width(3); out << std::left << v << " #"; out.width(3); out << e->get_owner_id(); out << std::right; out << " "; if (a->get_atom_kind() == A_LOWER) out << ">="; else out << "<="; out << " "; out.width(6); out << k << " "; display_var_flat_def(out, v); out << "\n"; } template void theory_arith::display_bounds_in_smtlib(std::ostream & out) const { ast_manager & m = get_manager(); ast_smt_pp pp(m); pp.set_benchmark_name("lemma"); int n = get_num_vars(); for (theory_var v = 0; v < n; v++) { expr * n = get_enode(v)->get_owner(); if (is_fixed(v)) { inf_numeral k_inf = lower_bound(v); rational k = k_inf.get_rational().to_rational(); expr_ref eq(m); eq = m.mk_eq(n, m_util.mk_numeral(k, is_int(v))); pp.add_assumption(eq); } else { if (lower(v) != nullptr) { inf_numeral k_inf = lower_bound(v); rational k = k_inf.get_rational().to_rational(); expr_ref ineq(m); if (k_inf.get_infinitesimal().is_zero()) ineq = m_util.mk_le(m_util.mk_numeral(k, is_int(v)), n); else ineq = m_util.mk_lt(m_util.mk_numeral(k, is_int(v)), n); pp.add_assumption(ineq); } if (upper(v) != nullptr) { inf_numeral k_inf = upper_bound(v); rational k = k_inf.get_rational().to_rational(); expr_ref ineq(m); if (k_inf.get_infinitesimal().is_zero()) ineq = m_util.mk_le(n, m_util.mk_numeral(k, is_int(v))); else ineq = m_util.mk_lt(n, m_util.mk_numeral(k, is_int(v))); pp.add_assumption(ineq); } } } pp.display_smt2(out, m.mk_true()); } template void theory_arith::display_bounds_in_smtlib() const { char buffer[128]; static int id = 0; #ifdef _WINDOWS sprintf_s(buffer, Z3_ARRAYSIZE(buffer), "arith_%d.smt", id); #else sprintf(buffer, "arith_%d.smt", id); #endif std::ofstream out(buffer); display_bounds_in_smtlib(out); out.close(); id++; } }; #endif /* THEORY_ARITH_PP_H_ */ z3-z3-4.8.7/src/smt/theory_array.cpp000066400000000000000000000417441356505360400172470ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_array.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-06-01. Revision History: --*/ #include "smt/smt_context.h" #include "smt/theory_array.h" #include "ast/ast_ll_pp.h" #include "ast/ast_pp.h" #include "util/stats.h" namespace smt { theory_array::theory_array(ast_manager & m, theory_array_params & params): theory_array_base(m), m_params(params), m_find(*this), m_trail_stack(*this), m_final_check_idx(0) { } theory_array::~theory_array() { std::for_each(m_var_data.begin(), m_var_data.end(), delete_proc()); m_var_data.reset(); } void theory_array::init(context * ctx) { theory_array_base::init(ctx); if (!ctx->relevancy()) m_params.m_array_laziness = 0; } void theory_array::merge_eh(theory_var v1, theory_var v2, theory_var, theory_var) { // v1 is the new root TRACE("array", tout << "merging v" << v1 << " v" << v2 << "\n"; display_var(tout, v1); tout << mk_pp(get_enode(v1)->get_owner(), get_manager()) << " <- " << mk_pp(get_enode(v2)->get_owner(), get_manager()) << "\n";); SASSERT(v1 == find(v1)); var_data * d1 = m_var_data[v1]; var_data * d2 = m_var_data[v2]; if (!d1->m_prop_upward && d2->m_prop_upward) set_prop_upward(v1); for (enode* n : d2->m_stores) add_store(v1, n); for (enode* n : d2->m_parent_stores) add_parent_store(v1, n); for (enode* n : d2->m_parent_selects) add_parent_select(v1, n); TRACE("array", tout << "after merge\n"; display_var(tout, v1);); } void theory_array::unmerge_eh(theory_var v1, theory_var v2) { // do nothing } theory_var theory_array::mk_var(enode * n) { ast_manager& m = get_manager(); context& ctx = get_context(); theory_var r = theory_array_base::mk_var(n); VERIFY(r == static_cast(m_find.mk_var())); SASSERT(r == static_cast(m_var_data.size())); m_var_data.push_back(alloc(var_data)); var_data * d = m_var_data[r]; TRACE("array", tout << mk_bounded_pp(n->get_owner(), m) << "\nis_array: " << is_array_sort(n) << ", is_select: " << is_select(n) << ", is_store: " << is_store(n) << "\n";); d->m_is_array = is_array_sort(n); if (d->m_is_array) register_sort(m.get_sort(n->get_owner())); d->m_is_select = is_select(n); if (is_store(n)) d->m_stores.push_back(n); ctx.attach_th_var(n, this, r); if (m_params.m_array_laziness <= 1 && is_store(n)) instantiate_axiom1(n); return r; } void theory_array::add_parent_select(theory_var v, enode * s) { if (m_params.m_array_cg && !s->is_cgr()) return; SASSERT(is_select(s)); v = find(v); var_data * d = m_var_data[v]; d->m_parent_selects.push_back(s); TRACE("array", tout << v << " " << mk_pp(s->get_owner(), get_manager()) << " " << mk_pp(get_enode(v)->get_owner(), get_manager()) << "\n";); m_trail_stack.push(push_back_trail(d->m_parent_selects)); for (enode* n : d->m_stores) { instantiate_axiom2a(s, n); } if (!m_params.m_array_delay_exp_axiom && d->m_prop_upward) { for (enode* store : d->m_parent_stores) { SASSERT(is_store(store)); if (!m_params.m_array_cg || store->is_cgr()) { if (m_params.m_array_weak) { found_unsupported_op(store->get_owner()); break; } instantiate_axiom2b(s, store); } } } } void theory_array::add_parent_store(theory_var v, enode * s) { if (m_params.m_array_cg && !s->is_cgr()) return; SASSERT(is_store(s)); v = find(v); var_data * d = m_var_data[v]; d->m_parent_stores.push_back(s); m_trail_stack.push(push_back_trail(d->m_parent_stores)); if (d->m_prop_upward && !m_params.m_array_delay_exp_axiom) { for (enode* n : d->m_parent_selects) { if (!m_params.m_array_cg || n->is_cgr()) { if (m_params.m_array_weak) { found_unsupported_op(s); break; } instantiate_axiom2b(n, s); } } } } bool theory_array::instantiate_axiom2b_for(theory_var v) { bool result = false; var_data * d = m_var_data[v]; for (enode* n1 : d->m_parent_stores) for (enode * n2 : d->m_parent_selects) if (instantiate_axiom2b(n2, n1)) result = true; return result; } /** \brief Mark v for upward propagation. That is, enables the propagation of select(v, i) to store(v,j,k). */ void theory_array::set_prop_upward(theory_var v) { v = find(v); var_data * d = m_var_data[v]; if (!d->m_prop_upward) { TRACE("array", tout << "#" << v << "\n";); m_trail_stack.push(reset_flag_trail(d->m_prop_upward)); d->m_prop_upward = true; if (m_params.m_array_weak) { found_unsupported_op(v); return; } if (!m_params.m_array_delay_exp_axiom) instantiate_axiom2b_for(v); for (enode * n : d->m_stores) set_prop_upward(n); } } void theory_array::set_prop_upward(enode * store) { if (is_store(store)) { theory_var st_v = store->get_arg(0)->get_th_var(get_id()); set_prop_upward(st_v); } } void theory_array::set_prop_upward(theory_var v, var_data* d) { unsigned sz = d->m_stores.size(); for (unsigned i = 0; i < sz; ++i) { set_prop_upward(d->m_stores[i]); } } /** \brief Return the size of the equivalence class for array terms that can be expressed as \lambda i : Index . [.. (select a i) ..] */ unsigned theory_array::get_lambda_equiv_size(theory_var v, var_data* d) { return d->m_stores.size(); } void theory_array::add_store(theory_var v, enode * s) { if (m_params.m_array_cg && !s->is_cgr()) return; SASSERT(is_store(s)); v = find(v); var_data * d = m_var_data[v]; unsigned lambda_equiv_class_size = get_lambda_equiv_size(v, d); if (m_params.m_array_always_prop_upward || lambda_equiv_class_size >= 1) { set_prop_upward(v, d); } d->m_stores.push_back(s); m_trail_stack.push(push_back_trail(d->m_stores)); for (enode * n : d->m_parent_selects) { SASSERT(is_select(n)); instantiate_axiom2a(n, s); } if (m_params.m_array_always_prop_upward || lambda_equiv_class_size >= 1) set_prop_upward(s); } void theory_array::instantiate_axiom1(enode * store) { TRACE("array", tout << "axiom 1:\n" << mk_bounded_pp(store->get_owner(), get_manager()) << "\n";); SASSERT(is_store(store)); m_stats.m_num_axiom1++; assert_store_axiom1(store); } void theory_array::instantiate_axiom2a(enode * select, enode * store) { TRACE("array", tout << "axiom 2a: #" << select->get_owner_id() << " #" << store->get_owner_id() << "\n";); SASSERT(is_select(select)); SASSERT(is_store(store)); if (assert_store_axiom2(store, select)) m_stats.m_num_axiom2a++; } bool theory_array::instantiate_axiom2b(enode * select, enode * store) { TRACE("array_axiom2b", tout << "axiom 2b: #" << select->get_owner_id() << " #" << store->get_owner_id() << "\n";); SASSERT(is_select(select)); SASSERT(is_store(store)); if (assert_store_axiom2(store, select)) { m_stats.m_num_axiom2b++; return true; } return false; } void theory_array::instantiate_extensionality(enode * a1, enode * a2) { TRACE("array", tout << "extensionality: #" << a1->get_owner_id() << " #" << a2->get_owner_id() << "\n";); SASSERT(is_array_sort(a1)); SASSERT(is_array_sort(a2)); if (m_params.m_array_extensional && assert_extensionality(a1, a2)) m_stats.m_num_extensionality++; } bool theory_array::internalize_atom(app * atom, bool) { return internalize_term(atom); } // // Internalize the term. If it has already been internalized, return false. // bool theory_array::internalize_term_core(app * n) { TRACE("array_bug", tout << mk_bounded_pp(n, get_manager()) << "\n";); context & ctx = get_context(); unsigned num_args = n->get_num_args(); for (unsigned i = 0; i < num_args; i++) ctx.internalize(n->get_arg(i), false); if (ctx.e_internalized(n)) { return false; } enode * e = ctx.mk_enode(n, false, false, true); if (!is_attached_to_var(e)) mk_var(e); if (get_manager().is_bool(n)) { bool_var bv = ctx.mk_bool_var(n); ctx.set_var_theory(bv, get_id()); ctx.set_enode_flag(bv, true); } return true; } bool theory_array::internalize_term(app * n) { if (!is_store(n) && !is_select(n)) { if (!is_array_ext(n)) found_unsupported_op(n); return false; } TRACE("array_bug", tout << mk_bounded_pp(n, get_manager()) << "\n";); if (!internalize_term_core(n)) { return true; } context & ctx = get_context(); enode * arg0 = ctx.get_enode(n->get_arg(0)); if (!is_attached_to_var(arg0)) mk_var(arg0); if (m_params.m_array_laziness == 0) { theory_var v_arg = arg0->get_th_var(get_id()); SASSERT(v_arg != null_theory_var); if (is_select(n)) { add_parent_select(v_arg, ctx.get_enode(n)); } else if (is_store(n)) { add_parent_store(v_arg, ctx.get_enode(n)); } } return true; } void theory_array::apply_sort_cnstr(enode * n, sort * s) { SASSERT(is_array_sort(s)); if (!is_attached_to_var(n)) mk_var(n); } void theory_array::new_eq_eh(theory_var v1, theory_var v2) { m_find.merge(v1, v2); enode* n1 = get_enode(v1), *n2 = get_enode(v2); if (n1->get_owner()->get_decl()->is_lambda() || n2->get_owner()->get_decl()->is_lambda()) { assert_congruent(n1, n2); } } void theory_array::new_diseq_eh(theory_var v1, theory_var v2) { v1 = find(v1); v2 = find(v2); var_data * d1 = m_var_data[v1]; if (d1->m_is_array) { SASSERT(m_var_data[v2]->m_is_array); TRACE("ext", tout << "extensionality:\n" << mk_bounded_pp(get_enode(v1)->get_owner(), get_manager(), 5) << "\n" << mk_bounded_pp(get_enode(v2)->get_owner(), get_manager(), 5) << "\n";); instantiate_extensionality(get_enode(v1), get_enode(v2)); } } void theory_array::relevant_eh(app * n) { if (m_params.m_array_laziness == 0) return; if (get_manager().is_ite(n)) { TRACE("array", tout << "relevant ite " << mk_pp(n, get_manager()) << "\n";); } if (!is_store(n) && !is_select(n)) return; context & ctx = get_context(); enode * arg = ctx.get_enode(n->get_arg(0)); theory_var v_arg = arg->get_th_var(get_id()); SASSERT(v_arg != null_theory_var); if (is_select(n)) { add_parent_select(v_arg, ctx.get_enode(n)); } else { SASSERT(is_store(n)); if (m_params.m_array_laziness > 1) instantiate_axiom1(ctx.get_enode(n)); add_parent_store(v_arg, ctx.get_enode(n)); } } void theory_array::push_scope_eh() { theory_array_base::push_scope_eh(); m_trail_stack.push_scope(); } void theory_array::pop_scope_eh(unsigned num_scopes) { m_trail_stack.pop_scope(num_scopes); unsigned num_old_vars = get_old_num_vars(num_scopes); std::for_each(m_var_data.begin() + num_old_vars, m_var_data.end(), delete_proc()); m_var_data.shrink(num_old_vars); theory_array_base::pop_scope_eh(num_scopes); SASSERT(m_find.get_num_vars() == m_var_data.size()); SASSERT(m_find.get_num_vars() == get_num_vars()); } final_check_status theory_array::final_check_eh() { m_final_check_idx++; final_check_status r = FC_DONE; if (m_params.m_array_lazy_ieq) { // Delay the creation of interface equalities... The // motivation is too give other theories and quantifier // instantiation to do something useful during final // check. if (m_final_check_idx % m_params.m_array_lazy_ieq_delay != 0) { assert_delayed_axioms(); r = FC_CONTINUE; } else { if (mk_interface_eqs_at_final_check() == FC_CONTINUE) r = FC_CONTINUE; else r = assert_delayed_axioms(); } } else { if (m_final_check_idx % 2 == 1) { if (assert_delayed_axioms() == FC_CONTINUE) r = FC_CONTINUE; else r = mk_interface_eqs_at_final_check(); } else { if (mk_interface_eqs_at_final_check() == FC_CONTINUE) r = FC_CONTINUE; else r = assert_delayed_axioms(); } } if (r == FC_DONE && m_found_unsupported_op && !get_context().get_fparams().m_array_fake_support) r = FC_GIVEUP; CTRACE("array", r != FC_DONE || m_found_unsupported_op, tout << r << "\n";); return r; } final_check_status theory_array::assert_delayed_axioms() { if (!m_params.m_array_delay_exp_axiom) return FC_DONE; final_check_status r = FC_DONE; unsigned num_vars = get_num_vars(); for (unsigned v = 0; v < num_vars; v++) { var_data * d = m_var_data[v]; if (d->m_prop_upward && instantiate_axiom2b_for(v)) r = FC_CONTINUE; } return r; } final_check_status theory_array::mk_interface_eqs_at_final_check() { unsigned n = mk_interface_eqs(); m_stats.m_num_eq_splits += n; if (n > 0) return FC_CONTINUE; return FC_DONE; } void theory_array::reset_eh() { m_trail_stack.reset(); std::for_each(m_var_data.begin(), m_var_data.end(), delete_proc()); m_var_data.reset(); theory_array_base::reset_eh(); } void theory_array::display(std::ostream & out) const { unsigned num_vars = get_num_vars(); if (num_vars == 0) return; out << "Theory array:\n"; for (unsigned v = 0; v < num_vars; v++) { display_var(out, v); } } // TODO: move to another file void theory_array::display_ids(std::ostream & out, unsigned n, enode * const * v) { for (unsigned i = 0; i < n; i++) { if (i > 0) out << " "; out << "#" << v[i]->get_owner_id(); } } void theory_array::display_var(std::ostream & out, theory_var v) const { var_data const * d = m_var_data[v]; out << "v"; out.width(4); out << std::left << v; out << " #"; out.width(4); out << get_enode(v)->get_owner_id() << " -> #"; out.width(4); out << get_enode(find(v))->get_owner_id(); out << std::right; out << " is_array: " << d->m_is_array << " is_select: " << d->m_is_select << " upward: " << d->m_prop_upward; out << " stores: {"; display_ids(out, d->m_stores.size(), d->m_stores.c_ptr()); out << "} p_stores: {"; display_ids(out, d->m_parent_stores.size(), d->m_parent_stores.c_ptr()); out << "} p_selects: {"; display_ids(out, d->m_parent_selects.size(), d->m_parent_selects.c_ptr()); out << "}"; out << "\n"; } void theory_array::collect_statistics(::statistics & st) const { st.update("array ax1", m_stats.m_num_axiom1); st.update("array ax2", m_stats.m_num_axiom2a); st.update("array exp ax2", m_stats.m_num_axiom2b); st.update("array ext ax", m_stats.m_num_extensionality); st.update("array splits", m_stats.m_num_eq_splits); } }; z3-z3-4.8.7/src/smt/theory_array.h000066400000000000000000000110201356505360400166740ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_array.h Abstract: Author: Leonardo de Moura (leonardo) 2008-06-01. Revision History: --*/ #ifndef THEORY_ARRAY_H_ #define THEORY_ARRAY_H_ #include "smt/theory_array_base.h" #include "smt/params/theory_array_params.h" #include "util/union_find.h" namespace smt { struct theory_array_stats { unsigned m_num_axiom1, m_num_axiom2a, m_num_axiom2b, m_num_extensionality, m_num_eq_splits; unsigned m_num_map_axiom, m_num_default_map_axiom; unsigned m_num_select_const_axiom, m_num_default_store_axiom, m_num_default_const_axiom, m_num_default_as_array_axiom; unsigned m_num_select_as_array_axiom; void reset() { memset(this, 0, sizeof(theory_array_stats)); } theory_array_stats() { reset(); } }; class theory_array : public theory_array_base { protected: typedef trail_stack th_trail_stack; typedef union_find th_union_find; struct var_data { ptr_vector m_stores; ptr_vector m_parent_selects; ptr_vector m_parent_stores; bool m_prop_upward; bool m_is_array; bool m_is_select; var_data():m_prop_upward(false), m_is_array(false), m_is_select(false) {} }; ptr_vector m_var_data; theory_array_params& m_params; theory_array_stats m_stats; th_union_find m_find; th_trail_stack m_trail_stack; unsigned m_final_check_idx; void init(context * ctx) override; theory_var mk_var(enode * n) override; bool internalize_atom(app * atom, bool gate_ctx) override; bool internalize_term(app * term) override; void apply_sort_cnstr(enode * n, sort * s) override; void new_eq_eh(theory_var v1, theory_var v2) override; void new_diseq_eh(theory_var v1, theory_var v2) override; void relevant_eh(app * n) override; void push_scope_eh() override; void pop_scope_eh(unsigned num_scopes) override; final_check_status final_check_eh() override; void reset_eh() override; void init_search_eh() override { m_final_check_idx = 0; } virtual void set_prop_upward(theory_var v); virtual void set_prop_upward(enode* n); virtual void set_prop_upward(theory_var v, var_data* d); virtual unsigned get_lambda_equiv_size(theory_var v, var_data* d); theory_var find(theory_var v) const { return m_find.find(v); } bool is_root(theory_var v) const { return m_find.is_root(v); } virtual void add_parent_select(theory_var v, enode * s); void add_parent_store(theory_var v, enode * s); void add_store(theory_var v, enode * s); bool internalize_term_core(app * term); void instantiate_axiom2a(enode * select, enode * store); bool instantiate_axiom2b(enode * select, enode * store); void instantiate_axiom1(enode * store); void instantiate_extensionality(enode * a1, enode * a2); void instantiate_congruent(enode * a1, enode * a2); bool instantiate_axiom2b_for(theory_var v); virtual final_check_status assert_delayed_axioms(); final_check_status mk_interface_eqs_at_final_check(); static void display_ids(std::ostream & out, unsigned n, enode * const * v); public: theory_array(ast_manager & m, theory_array_params & params); ~theory_array() override; theory * mk_fresh(context * new_ctx) override { return alloc(theory_array, new_ctx->get_manager(), new_ctx->get_fparams()); } char const * get_name() const override { return "array"; } virtual void display_var(std::ostream & out, theory_var v) const; void display(std::ostream & out) const override; void collect_statistics(::statistics & st) const override; th_trail_stack & get_trail_stack() { return m_trail_stack; } virtual void merge_eh(theory_var v1, theory_var v2, theory_var, theory_var); static void after_merge_eh(theory_var r1, theory_var r2, theory_var v1, theory_var v2) {} void unmerge_eh(theory_var v1, theory_var v2); ptr_vector const& parent_selects(enode* n) { return m_var_data[find(n->get_root()->get_th_var(get_id()))]->m_parent_selects; } }; }; #endif /* THEORY_ARRAY_H_ */ z3-z3-4.8.7/src/smt/theory_array_bapa.cpp000066400000000000000000000562031356505360400202260ustar00rootroot00000000000000/*++ Copyright (c) 2019 Microsoft Corporation Module Name: theory_array_bapa.cpp Abstract: Saturation procedure for BAPA predicates. Assume there is a predicate Size(S, n) for S : Array(T, Bool) and n : Int The predicate is true if S is a set of size n. Size(S, n), Size(T, m) S, T are intersecting. n != m or S != T D --------------------------------------------------------- Size(S, n) => Size(S\T, k1), Size(S n T, k2), n = k1 + k2 Size(T, m) => Size(T\S, k3), SIze(S n T, k2), m = k2 + k3 Size(S, n) P -------------------- Size(S, n) => n >= 0 Size(S, n), is infinite domain B ------------------------------ Size(S, n) => default(S) = false Size(S, n), Size(S, m) F -------------------------------- Size(S, n), Size(S, m) => n = m Fixing values during final check: Size(S, n) V ------------------- assume value(n) = n Size(S, n), S[i1], ..., S[ik] O ------------------------------- ~distinct(i1, ... ik) or n >= k Size(S,n) Ak -------------------------------------------------- S[i1] & .. & S[ik] & distinct(i1, .., ik) or n < k Q: Is this sufficient? Axiom A1 could be adjusted to add new elements i' until there are k witnesses for Size(S, k). This is quite bad when k is very large. Instead rely on stably infiniteness or other domain properties of the theories. When A is finite domain, or there are quantifiers there could be constraints that force domain sizes so domain sizes may have to be enforced. A succinct way would be through domain comprehension assertions. Finite domains: Size(S, n), is finite domain ---------------------------- S <= |A| Size(S, n), !S[i1], .... !S[ik], S is finite domain ---------------------------------------------------------- default(S) = false or ~distinct(i1,..,ik) or |A| - k <= n ~Size(S, m) is negative on all occurrences, S is finite domain --------------------------------------------------------------- Size(S, n) n fresh. Model construction for infinite domains when all Size(S, m) are negative for S. Author: Nikolaj Bjorner 2019-04-13 Revision History: */ #include "ast/ast_util.h" #include "ast/ast_pp.h" #include "ast/rewriter/array_rewriter.h" #include "smt/smt_context.h" #include "smt/smt_arith_value.h" #include "smt/theory_array_full.h" #include "smt/theory_array_bapa.h" #if 0 - set of native select terms that are true - set of auxiliary select terms. - n1, n2, n3, n4. - a1, a2, a3, a4, a5. - - add select terms, such that first #endif namespace smt { class theory_array_bapa::imp { struct sz_info { bool m_is_leaf; // has it been split into disjoint subsets already? rational m_size; // set to >= integer if fixed in final check, otherwise -1 literal m_literal; // literal that enforces value is set. obj_map m_selects; sz_info(): m_is_leaf(true), m_size(rational::minus_one()), m_literal(null_literal) {} }; typedef std::pair func_decls; ast_manager& m; theory_array_full& th; arith_util m_arith; array_util m_autil; th_rewriter m_rw; arith_value m_arith_value; ast_ref_vector m_pinned; obj_map m_sizeof; obj_map m_size_limit; obj_map m_index_skolems; obj_map m_size_limit_sort2skolems; unsigned m_max_set_enumeration; context& ctx() { return th.get_context(); } void reset() { for (auto& kv : m_sizeof) { dealloc(kv.m_value); } } bool is_true(expr* e) { return is_true(ctx().get_literal(e)); } bool is_true(enode* e) { return is_true(e->get_owner()); } bool is_true(literal l) { return ctx().is_relevant(l) && ctx().get_assignment(l) == l_true; } bool is_leaf(sz_info& i) const { return i.m_is_leaf; } bool is_leaf(sz_info* i) const { return is_leaf(*i); } enode* get_root(expr* e) { return ctx().get_enode(e)->get_root(); } bool is_select(enode* n) { return th.is_select(n); } app_ref mk_select(expr* a, expr* i) { expr* args[2] = { a, i }; return app_ref(m_autil.mk_select(2, args), m); } literal get_literal(expr* e) { return ctx().get_literal(e); } literal mk_literal(expr* e) { if (!ctx().e_internalized(e)) ctx().internalize(e, false); literal lit = get_literal(e); ctx().mark_as_relevant(lit); return lit; } literal mk_eq(expr* a, expr* b) { literal lit = th.mk_eq(a, b, false); ctx().mark_as_relevant(lit); return lit; } void mk_th_axiom(literal l1, literal l2) { literal lits[2] = { l1, l2 }; mk_th_axiom(2, lits); } void mk_th_axiom(literal l1, literal l2, literal l3) { literal lits[3] = { l1, l2, l3 }; mk_th_axiom(3, lits); } void mk_th_axiom(unsigned n, literal* lits) { TRACE("array", ctx().display_literals_verbose(tout, n, lits) << "\n";); ctx().mk_th_axiom(th.get_id(), n, lits); } void update_indices() { for (auto const& kv : m_sizeof) { app* k = kv.m_key; sz_info& v = *kv.m_value; v.m_selects.reset(); if (is_true(k) && is_leaf(v)) { enode* set = get_root(k->get_arg(0)); for (enode* parent : enode::parents(set)) { if (is_select(parent) && parent->get_arg(0)->get_root() == set) { if (is_true(parent)) { v.m_selects.insert(parent->get_arg(1)->get_root(), parent->get_owner()); } } } } } } /** F: Size(S, k1) & Size(S, k2) => k1 = k2 */ lbool ensure_functional() { lbool result = l_true; obj_map parents; for (auto const& kv : m_sizeof) { app* sz1 = kv.m_key; if (!is_true(sz1)) { continue; } enode* r = get_root(sz1->get_arg(0)); app* sz2 = nullptr; if (parents.find(r, sz2)) { expr* k1 = sz1->get_arg(1); expr* k2 = sz2->get_arg(1); if (get_root(k1) != get_root(k2)) { mk_th_axiom(~get_literal(sz1), ~get_literal(sz2), mk_eq(k1, k2)); result = l_false; } } else { parents.insert(r, sz1); } } return result; } /** Enforce D */ lbool ensure_disjoint() { auto i = m_sizeof.begin(), end = m_sizeof.end(); for (; i != end; ++i) { auto& kv = *i; if (!kv.m_value->m_is_leaf) { continue; } for (auto j = i; ++j != end; ) { if (j->m_value->m_is_leaf && !ensure_disjoint(i->m_key, j->m_key)) { return l_false; } } } return l_true; } bool ensure_disjoint(app* sz1, app* sz2) { sz_info& i1 = *m_sizeof[sz1]; sz_info& i2 = *m_sizeof[sz2]; SASSERT(i1.m_is_leaf); SASSERT(i2.m_is_leaf); expr* s = sz1->get_arg(0); expr* t = sz2->get_arg(0); if (m.get_sort(s) != m.get_sort(t)) { return true; } enode* r1 = get_root(s); enode* r2 = get_root(t); if (r1 == r2) { return true; } if (!ctx().is_diseq(r1, r2) && ctx().assume_eq(r1, r2)) { return false; } if (do_intersect(i1.m_selects, i2.m_selects)) { add_disjoint(sz1, sz2); return false; } return true; } bool do_intersect(obj_map const& s, obj_map const& t) const { if (s.size() > t.size()) { return do_intersect(t, s); } for (auto const& idx : s) if (t.contains(idx.m_key)) return true; return false; } void add_disjoint(app* sz1, app* sz2) { sz_info& i1 = *m_sizeof[sz1]; sz_info& i2 = *m_sizeof[sz2]; SASSERT(i1.m_is_leaf); SASSERT(i2.m_is_leaf); expr* t = sz1->get_arg(0); expr* s = sz2->get_arg(0); expr_ref tms = mk_subtract(t, s); expr_ref smt = mk_subtract(s, t); expr_ref tns = mk_intersect(t, s); #if 0 std::cout << tms << "\n"; std::cout << smt << "\n"; std::cout << tns << "\n"; #endif if (tns == sz1) { std::cout << "SEEN " << tms << "\n"; } if (tns == sz2) { std::cout << "SEEN " << smt << "\n"; } ctx().push_trail(value_trail(i1.m_is_leaf, false)); ctx().push_trail(value_trail(i2.m_is_leaf, false)); expr_ref k1(m), k2(m), k3(m); expr_ref sz_tms(m), sz_tns(m), sz_smt(m); k1 = m_autil.mk_card(tms); k2 = m_autil.mk_card(tns); k3 = m_autil.mk_card(smt); sz_tms = m_autil.mk_has_size(tms, k1); sz_tns = m_autil.mk_has_size(tns, k2); sz_smt = m_autil.mk_has_size(smt, k3); propagate(sz1, sz_tms); propagate(sz1, sz_tns); propagate(sz2, sz_smt); propagate(sz2, sz_tns); propagate(sz1, mk_eq(k1 + k2, sz1->get_arg(1))); propagate(sz2, mk_eq(k3 + k2, sz2->get_arg(1))); } expr_ref mk_subtract(expr* t, expr* s) { expr_ref d(m_autil.mk_setminus(t, s), m); m_rw(d); return d; } expr_ref mk_intersect(expr* t, expr* s) { expr_ref i(m_autil.mk_intersection(t, s), m); m_rw(i); return i; } void propagate(expr* assumption, expr* conseq) { propagate(assumption, mk_literal(conseq)); } void propagate(expr* assumption, literal conseq) { mk_th_axiom(~mk_literal(assumption), conseq); } /** Enforce V */ lbool ensure_values_assigned() { lbool result = l_true; for (auto const& kv : m_sizeof) { app* k = kv.m_key; sz_info& i = *kv.m_value; if (is_leaf(&i) && (i.m_literal == null_literal || !is_true(i.m_literal))) { rational value; expr* sz = k->get_arg(1); if (!m_arith_value.get_value(sz, value)) { return l_undef; } literal lit = mk_eq(sz, m_arith.mk_int(value)); ctx().set_true_first_flag(lit.var()); ctx().push_trail(value_trail(i.m_literal, lit)); ctx().push_trail(value_trail(i.m_size, value)); result = l_false; } } return result; } /** Enforce Ak, */ lbool ensure_non_empty() { for (auto const& kv : m_sizeof) { sz_info& i = *kv.m_value; app* set_sz = kv.m_key; if (is_true(set_sz) && is_leaf(i) && i.m_selects.size() < i.m_size) { expr* set = set_sz->get_arg(0); expr_ref le(m_arith.mk_le(set_sz->get_arg(1), m_arith.mk_int(0)), m); literal le_lit = mk_literal(le); literal sz_lit = mk_literal(set_sz); for (unsigned k = i.m_selects.size(); rational(k) < i.m_size; ++k) { expr_ref idx = mk_index_skolem(set_sz, set, k); app_ref sel(mk_select(set, idx), m); mk_th_axiom(~sz_lit, le_lit, mk_literal(sel)); TRACE("array", tout << idx << " " << sel << " " << i.m_size << "\n";); } return l_false; } } return l_true; } // create skolem function that is injective on integers (ensures uniqueness). expr_ref mk_index_skolem(app* sz, expr* a, unsigned n) { func_decls fg; sort* s = m.get_sort(a); if (!m_index_skolems.find(s, fg)) { sort* idx_sort = get_array_domain(s, 0); sort* dom1[2] = { s, m_arith.mk_int() }; sort* dom2[1] = { idx_sort }; func_decl* f = m.mk_fresh_func_decl("to-index", "", 2, dom1, idx_sort); func_decl* g = m.mk_fresh_func_decl("from-index", "", 1, dom2, m_arith.mk_int()); fg = std::make_pair(f, g); m_index_skolems.insert(s, fg); m_pinned.push_back(f); m_pinned.push_back(g); m_pinned.push_back(s); } expr_ref nV(m_arith.mk_int(n), m); expr_ref result(m.mk_app(fg.first, a, nV), m); expr_ref le(m_arith.mk_le(sz->get_arg(1), nV), m); // set-has-size(a, k) => k <= n or g(f(a,n)) = n mk_th_axiom(~mk_literal(sz), mk_literal(le), mk_eq(nV, m.mk_app(fg.second, result))); return result; } /** Enforce O */ lbool ensure_no_overflow() { for (auto const& kv : m_sizeof) { if (is_true(kv.m_key) && is_leaf(kv.m_value)) { lbool r = ensure_no_overflow(kv.m_key, *kv.m_value); if (r != l_true) return r; } } return l_true; } lbool ensure_no_overflow(app* sz, sz_info& info) { SASSERT(!info.m_size.is_neg()); if (info.m_size < info.m_selects.size()) { for (auto i = info.m_selects.begin(), e = info.m_selects.end(); i != e; ++i) { for (auto j = i; ++j != e; ) { if (ctx().assume_eq(i->m_key, j->m_key)) { return l_false; } } } // if all is exhausted, then add axiom: set-has-size(s, n) & s[indices] & all-diff(indices) => n >= |indices| literal_vector lits; lits.push_back(~mk_literal(sz)); for (auto const& kv : info.m_selects) { lits.push_back(~mk_literal(kv.m_value)); } if (info.m_selects.size() > 1) { ptr_vector args; for (auto const& kv : info.m_selects) { args.push_back(kv.m_key->get_owner()); } expr_ref diff(m.mk_distinct(args.size(), args.c_ptr()), m); lits.push_back(~mk_literal(diff)); } expr_ref ge(m_arith.mk_ge(sz->get_arg(1), m_arith.mk_int(info.m_selects.size())), m); lits.push_back(mk_literal(ge)); mk_th_axiom(lits.size(), lits.c_ptr()); return l_false; } return l_true; } class remove_sz : public trail { obj_map & m_table; app* m_obj; public: remove_sz(obj_map& tab, app* t): m_table(tab), m_obj(t) {} ~remove_sz() override {} void undo(context& ctx) override { dealloc(m_table[m_obj]); m_table.remove(m_obj); } }; std::ostream& display(std::ostream& out) { for (auto const& kv : m_sizeof) { display(out << mk_pp(kv.m_key, m) << ": ", *kv.m_value); } return out; } std::ostream& display(std::ostream& out, sz_info& sz) { return out << (sz.m_is_leaf ? "leaf": "") << " size: " << sz.m_size << " selects: " << sz.m_selects.size() << "\n"; } public: imp(theory_array_full& th): m(th.get_manager()), th(th), m_arith(m), m_autil(m), m_rw(m), m_arith_value(m), m_pinned(m) { context& ctx = th.get_context(); m_arith_value.init(&ctx); m_max_set_enumeration = 4; } ~imp() { reset(); } void internalize_term(app* term) { if (th.is_set_has_size(term)) { internalize_size(term); } else if (th.is_set_card(term)) { internalize_card(term); } } /** * Size(S, n) => n >= 0, default(S) = false */ void internalize_size(app* term) { SASSERT(ctx().e_internalized(term)); literal lit = mk_literal(term); expr* s = term->get_arg(0); expr* n = term->get_arg(1); mk_th_axiom(~lit, mk_literal(m_arith.mk_ge(n, m_arith.mk_int(0)))); sort_size const& sz = m.get_sort(s)->get_num_elements(); if (sz.is_infinite()) { mk_th_axiom(~lit, mk_eq(th.mk_default(s), m.mk_false())); } else { warning_msg("correct handling of finite domains is TBD"); // add upper bound on size of set. // add case where default(S) = true, and add negative elements. } m_sizeof.insert(term, alloc(sz_info)); m_size_limit.insert(s, rational(2)); assert_size_limit(s, n); ctx().push_trail(remove_sz(m_sizeof, term)); } /** \brief whenever there is a cardinality function, it includes an axiom that entails the set is finite. */ void internalize_card(app* term) { SASSERT(ctx().e_internalized(term)); app_ref has_size(m_autil.mk_has_size(term->get_arg(0), term), m); literal lit = mk_literal(has_size); ctx().assign(lit, nullptr); } lbool trace_call(char const* msg, lbool r) { if (r != l_true) { IF_VERBOSE(2, verbose_stream() << msg << "\n"); } return r; } final_check_status final_check() { final_check_status st = m_arith_value.final_check(); if (st != FC_DONE) return st; lbool r = trace_call("ensure_functional", ensure_functional()); if (r == l_true) update_indices(); if (r == l_true) r = trace_call("ensure_disjoint", ensure_disjoint()); if (r == l_true) r = trace_call("eensure_values_assigned", ensure_values_assigned()); if (r == l_true) r = trace_call("ensure_non_empty", ensure_non_empty()); if (r == l_true) r = trace_call("ensure_no_overflow", ensure_no_overflow()); CTRACE("array", r != l_true, display(tout);); switch (r) { case l_true: return FC_DONE; case l_false: return FC_CONTINUE; case l_undef: return FC_GIVEUP; } return FC_GIVEUP; } void init_model() { for (auto const& kv : m_sizeof) { sz_info& i = *kv.m_value; app* sz = kv.m_key; if (is_true(sz) && is_leaf(i) && rational(i.m_selects.size()) != i.m_size) { warning_msg("models for BAPA is TBD"); break; } } } bool should_research(expr_ref_vector & unsat_core) { expr* set, *sz; for (auto & e : unsat_core) { if (is_app(e) && is_size_limit(to_app(e), set, sz)) { inc_size_limit(set, sz); return true; } } return false; } void inc_size_limit(expr* set, expr* sz) { IF_VERBOSE(2, verbose_stream() << "inc value " << mk_pp(set, m) << "\n"); m_size_limit[set] *= rational(2); assert_size_limit(set, sz); } bool is_size_limit(app* e, expr*& set, expr*& sz) { func_decl* d = nullptr; if (e->get_num_args() > 0 && m_size_limit_sort2skolems.find(m.get_sort(e->get_arg(0)), d) && d == e->get_decl()) { set = e->get_arg(0); sz = e->get_arg(1); return true; } else { return false; } } // has-size(s,n) & size-limit(s, n, k) => n <= k app_ref mk_size_limit(expr* set, expr* sz) { func_decl* sk = nullptr; sort* s = m.get_sort(set); if (!m_size_limit_sort2skolems.find(s, sk)) { sort* dom[3] = { s, m_arith.mk_int(), m_arith.mk_int() }; sk = m.mk_fresh_func_decl("value-limit", "", 3, dom, m.mk_bool_sort()); m_pinned.push_back(sk); m_size_limit_sort2skolems.insert(s, sk); } return app_ref(m.mk_app(sk, set, sz, m_arith.mk_int(m_size_limit[set])), m); } void assert_size_limit(expr* set, expr* sz) { app_ref set_sz(m_autil.mk_has_size(set, sz), m); app_ref lim(m_arith.mk_int(m_size_limit[set]), m); app_ref size_limit = mk_size_limit(set, sz); mk_th_axiom(~mk_literal(set_sz), ~mk_literal(size_limit), mk_literal(m_arith.mk_le(sz, lim))); } void add_theory_assumptions(expr_ref_vector & assumptions) { for (auto const& kv : m_sizeof) { expr* set = kv.m_key->get_arg(0); expr* sz = kv.m_key->get_arg(1); assumptions.push_back(mk_size_limit(set, sz)); } TRACE("array", tout << "ASSUMPTIONS: " << assumptions << "\n";); } }; theory_array_bapa::theory_array_bapa(theory_array_full& th) { m_imp = alloc(imp, th); } theory_array_bapa::~theory_array_bapa() { dealloc(m_imp); } void theory_array_bapa::internalize_term(app* term) { m_imp->internalize_term(term); } final_check_status theory_array_bapa::final_check() { return m_imp->final_check(); } void theory_array_bapa::init_model() { m_imp->init_model(); } bool theory_array_bapa::should_research(expr_ref_vector & unsat_core) { return m_imp->should_research(unsat_core); } void theory_array_bapa::add_theory_assumptions(expr_ref_vector & assumptions) { m_imp->add_theory_assumptions(assumptions); } } z3-z3-4.8.7/src/smt/theory_array_bapa.h000066400000000000000000000014151356505360400176660ustar00rootroot00000000000000/*++ Copyright (c) 2019 Microsoft Corporation Module Name: theory_array_bapa.h Abstract: Author: Nikolaj Bjorner 2019-04-13 Revision History: --*/ #ifndef THEORY_ARRAY_BAPA_H_ #define THEORY_ARRAY_BAPA_H_ #include "ast/ast.h" #include "smt/smt_theory.h" namespace smt { class theory_array_full; class theory_array_bapa { class imp; imp* m_imp; public: theory_array_bapa(theory_array_full& th); ~theory_array_bapa(); void internalize_term(app* term); final_check_status final_check(); void init_model(); bool should_research(expr_ref_vector & unsat_core); void add_theory_assumptions(expr_ref_vector & assumptions); }; }; #endif /* THEORY_ARRAY_BAPA_H_ */ z3-z3-4.8.7/src/smt/theory_array_base.cpp000066400000000000000000001146631356505360400202420ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_array_base.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-06-02. Revision History: --*/ #include "smt/smt_context.h" #include "smt/theory_array_base.h" #include "ast/ast_ll_pp.h" #include "ast/ast_pp.h" #include "smt/smt_model_generator.h" #include "model/func_interp.h" #include "ast/ast_smt2_pp.h" namespace smt { theory_array_base::theory_array_base(ast_manager & m): theory(m.mk_family_id("array")), m_found_unsupported_op(false) { } void theory_array_base::found_unsupported_op(expr * n) { if (!get_context().get_fparams().m_array_fake_support && !m_found_unsupported_op) { TRACE("array", tout << mk_ll_pp(n, get_manager()) << "\n";); get_context().push_trail(value_trail(m_found_unsupported_op)); m_found_unsupported_op = true; } } app * theory_array_base::mk_select(unsigned num_args, expr * const * args) { app * r = get_manager().mk_app(get_family_id(), OP_SELECT, 0, nullptr, num_args, args); TRACE("mk_var_bug", tout << "mk_select: " << r->get_id() << " num_args: " << num_args; for (unsigned i = 0; i < num_args; i++) tout << " " << args[i]->get_id(); tout << "\n";); return r; } app * theory_array_base::mk_store(unsigned num_args, expr * const * args) { return get_manager().mk_app(get_family_id(), OP_STORE, 0, nullptr, num_args, args); } app * theory_array_base::mk_default(expr * a) { sort * s = get_manager().get_sort(a); unsigned num_params = get_dimension(s); parameter const* params = s->get_info()->get_parameters(); return get_manager().mk_app(get_family_id(), OP_ARRAY_DEFAULT, num_params, params, 1, & a); } unsigned theory_array_base::get_dimension(sort * s) const { SASSERT(s->is_sort_of(get_family_id(), ARRAY_SORT)); SASSERT(s->get_info()->get_num_parameters() >= 2); return s->get_info()->get_num_parameters()-1; } void theory_array_base::assert_axiom(unsigned num_lits, literal * lits) { context & ctx = get_context(); TRACE("array_axiom", tout << "literals:\n"; for (unsigned i = 0; i < num_lits; ++i) { expr * e = ctx.bool_var2expr(lits[i].var()); if (lits[i].sign()) tout << "not "; tout << mk_pp(e, get_manager()) << " "; tout << "\n"; }); ctx.mk_th_axiom(get_id(), num_lits, lits); } void theory_array_base::assert_axiom(literal l1, literal l2) { literal ls[2] = { l1, l2 }; assert_axiom(2, ls); } void theory_array_base::assert_axiom(literal l) { assert_axiom(1, &l); } void theory_array_base::assert_store_axiom1_core(enode * e) { app * n = e->get_owner(); SASSERT(is_store(n)); context & ctx = get_context(); ast_manager & m = get_manager(); ptr_buffer sel_args; unsigned num_args = n->get_num_args(); SASSERT(num_args >= 3); sel_args.push_back(n); for (unsigned i = 1; i < num_args - 1; ++i) { sel_args.push_back(to_app(n->get_arg(i))); } expr_ref sel(m); sel = mk_select(sel_args.size(), sel_args.c_ptr()); expr * val = n->get_arg(num_args - 1); TRACE("array", tout << mk_bounded_pp(sel, m) << " = " << mk_bounded_pp(val, m) << "\n";); if (m.proofs_enabled()) { literal l(mk_eq(sel, val, true)); ctx.mark_as_relevant(l); if (m.has_trace_stream()) log_axiom_instantiation(ctx.bool_var2expr(l.var())); assert_axiom(l); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; } else { TRACE("mk_var_bug", tout << "mk_sel: " << sel->get_id() << "\n";); ctx.internalize(sel, false); ctx.assign_eq(ctx.get_enode(sel), ctx.get_enode(val), eq_justification::mk_axiom()); ctx.mark_as_relevant(sel.get()); } } /** \brief Assert axiom 2: FORALL a, i_i, ..., i_n, j_1, ..., j_n i_1 /= j_1 => select(store(a, i_1, ..., i_n, v), j_1, ..., j_n) = select(a, j_1, ..., j_n) and ... and i_n /= j_n => select(store(a, i_1, ..., i_n, v), j_1, ..., j_n) = select(a, j_1, ..., j_n) */ void theory_array_base::assert_store_axiom2_core(enode * store, enode * select) { TRACE("array", tout << "generating axiom2: #" << store->get_owner_id() << " #" << select->get_owner_id() << "\n"; tout << mk_bounded_pp(store->get_owner(), get_manager()) << "\n" << mk_bounded_pp(select->get_owner(), get_manager()) << "\n";); SASSERT(is_store(store)); SASSERT(is_select(select)); SASSERT(store->get_num_args() == 1 + select->get_num_args()); ptr_buffer sel1_args, sel2_args; context & ctx = get_context(); ast_manager & m = get_manager(); enode * a = store->get_arg(0); enode * const * is = select->get_args() + 1; enode * const * js = store->get_args() + 1; unsigned num_args = select->get_num_args() - 1; sel1_args.push_back(store->get_owner()); sel2_args.push_back(a->get_owner()); for (unsigned i = 0; i < num_args; i++) { sel1_args.push_back(is[i]->get_owner()); sel2_args.push_back(is[i]->get_owner()); } expr_ref sel1(m), sel2(m); bool init = false; literal conseq = null_literal; expr * conseq_expr = nullptr; for (unsigned i = 0; i < num_args; i++) { enode * idx1 = js[i]; enode * idx2 = is[i]; if (idx1->get_root() == idx2->get_root()) { TRACE("array_bug", tout << "indexes are equal... skipping...\n";); continue; } if (!init) { sel1 = mk_select(sel1_args.size(), sel1_args.c_ptr()); sel2 = mk_select(sel2_args.size(), sel2_args.c_ptr()); if (sel1 == sel2) { TRACE("array_bug", tout << "sel1 and sel2 are equal:\n";); break; } init = true; TRACE("array", tout << mk_bounded_pp(sel1, m) << " " << mk_bounded_pp(sel2, m) << "\n";); conseq = mk_eq(sel1, sel2, true); conseq_expr = ctx.bool_var2expr(conseq.var()); } literal ante = mk_eq(idx1->get_owner(), idx2->get_owner(), true); ctx.mark_as_relevant(ante); // ctx.force_phase(ante); ctx.add_rel_watch(~ante, conseq_expr); // ctx.mark_as_relevant(conseq_expr); TRACE("array", tout << "asserting axiom2: " << ante << "\n";); TRACE("array_map_bug", tout << "axiom2:\n"; tout << mk_ismt2_pp(idx1->get_owner(), m) << "\n=\n" << mk_ismt2_pp(idx2->get_owner(), m); tout << "\nimplies\n" << mk_ismt2_pp(conseq_expr, m) << "\n";); if (m.has_trace_stream()) { app_ref body(m); body = m.mk_or(ctx.bool_var2expr(ante.var()), conseq_expr); log_axiom_instantiation(body); } assert_axiom(ante, conseq); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; } } bool theory_array_base::assert_store_axiom2(enode * store, enode * select) { unsigned num_args = select->get_num_args(); unsigned i = 1; for (; i < num_args; i++) if (store->get_arg(i)->get_root() != select->get_arg(i)->get_root()) break; if (i == num_args) return false; if (get_context().add_fingerprint(store, store->get_owner_id(), select->get_num_args() - 1, select->get_args() + 1)) { TRACE("array", tout << "adding axiom2 to todo queue\n";); m_axiom2_todo.push_back(std::make_pair(store, select)); return true; } TRACE("array", tout << "axiom already instantiated: #" << store->get_owner_id() << " #" << select->get_owner_id() << "\n";); return false; } func_decl_ref_vector * theory_array_base::register_sort(sort * s_array) { unsigned dimension = get_dimension(s_array); func_decl_ref_vector * ext_skolems = nullptr; if (!m_sort2skolem.find(s_array, ext_skolems)) { array_util util(get_manager()); ast_manager & m = get_manager(); ext_skolems = alloc(func_decl_ref_vector, m); for (unsigned i = 0; i < dimension; ++i) { func_decl * ext_sk_decl = util.mk_array_ext(s_array, i); ext_skolems->push_back(ext_sk_decl); } m_sort2skolem.insert(s_array, ext_skolems); m_sorts_trail.push_back(s_array); } return ext_skolems; } bool theory_array_base::value_eq_proc::operator()(enode * n1, enode * n2) const { SASSERT(n1->get_num_args() == n2->get_num_args()); unsigned n = n1->get_num_args(); // skipping first argument of the select. for(unsigned i = 1; i < n; i++) { if (n1->get_arg(i)->get_root() != n2->get_arg(i)->get_root()) { return false; } } return true; } /** \brief Return true if there is a select(v1', i1) and a select(v2', i2) such that: v1' = v1, v2' = v2, i1 = i2, select(v1', i1) /= select(v2', i2) in the logical context. */ bool theory_array_base::already_diseq(enode * v1, enode * v2) { context & ctx = get_context(); enode * r1 = v1->get_root(); enode * r2 = v2->get_root(); if (r1->get_class_size() > r2->get_class_size()) { std::swap(r1, r2); } m_array_value.reset(); // populate m_array_value if the select(a, i) parent terms of r1 for (enode* parent : r1->get_const_parents()) { if (parent->is_cgr() && ctx.is_relevant(parent) && is_select(parent->get_owner()) && parent->get_arg(0)->get_root() == r1) { m_array_value.insert(parent); } } // traverse select(a, i) parent terms of r2 trying to find a match. for (enode * parent : r2->get_const_parents()) { enode * other; if (parent->is_cgr() && ctx.is_relevant(parent) && is_select(parent->get_owner()) && parent->get_arg(0)->get_root() == r2 && m_array_value.find(parent, other)) { if (ctx.is_diseq(parent, other)) { TRACE("array_ext", tout << "selects are disequal\n";); return true; } } } return false; } bool theory_array_base::assert_extensionality(enode * n1, enode * n2) { context & ctx = get_context(); if (n1->get_owner_id() > n2->get_owner_id()) std::swap(n1, n2); enode * nodes[2] = { n1, n2 }; if (!ctx.add_fingerprint(this, 0, 2, nodes)) return false; // axiom was already instantiated if (already_diseq(n1, n2)) return false; m_extensionality_todo.push_back(std::make_pair(n1, n2)); return true; } void theory_array_base::assert_congruent(enode * a1, enode * a2) { TRACE("array", tout << "congruent: #" << a1->get_owner_id() << " #" << a2->get_owner_id() << "\n";); SASSERT(is_array_sort(a1)); SASSERT(is_array_sort(a2)); context & ctx = get_context(); if (a1->get_owner_id() > a2->get_owner_id()) std::swap(a1, a2); enode * nodes[2] = { a1, a2 }; if (!ctx.add_fingerprint(this, 1, 2, nodes)) return; // axiom was already instantiated m_congruent_todo.push_back(std::make_pair(a1, a2)); } void theory_array_base::assert_extensionality_core(enode * n1, enode * n2) { app * e1 = n1->get_owner(); app * e2 = n2->get_owner(); context & ctx = get_context(); ast_manager & m = get_manager(); func_decl_ref_vector * funcs = nullptr; sort * s = m.get_sort(e1); VERIFY(m_sort2skolem.find(s, funcs)); unsigned dimension = funcs->size(); expr_ref_vector args1(m), args2(m); args1.push_back(e1); args2.push_back(e2); for (unsigned i = 0; i < dimension; i++) { expr * k = m.mk_app(funcs->get(i), e1, e2); args1.push_back(k); args2.push_back(k); } expr * sel1 = mk_select(args1.size(), args1.c_ptr()); expr * sel2 = mk_select(args2.size(), args2.c_ptr()); TRACE("ext", tout << mk_bounded_pp(sel1, m) << "\n" << mk_bounded_pp(sel2, m) << "\n";); literal n1_eq_n2 = mk_eq(e1, e2, true); literal sel1_eq_sel2 = mk_eq(sel1, sel2, true); ctx.mark_as_relevant(n1_eq_n2); ctx.mark_as_relevant(sel1_eq_sel2); if (m.has_trace_stream()) { app_ref body(m); body = m.mk_implies(m.mk_not(ctx.bool_var2expr(n1_eq_n2.var())), m.mk_not(ctx.bool_var2expr(sel1_eq_sel2.var()))); log_axiom_instantiation(body); } assert_axiom(n1_eq_n2, ~sel1_eq_sel2); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; } /** \brief assert n1 = n2 => forall vars . (n1 vars) = (n2 vars) */ void theory_array_base::assert_congruent_core(enode * n1, enode * n2) { app * e1 = n1->get_owner(); app * e2 = n2->get_owner(); context & ctx = get_context(); ast_manager & m = get_manager(); sort* s = m.get_sort(e1); unsigned dimension = get_array_arity(s); literal n1_eq_n2 = mk_eq(e1, e2, true); ctx.mark_as_relevant(n1_eq_n2); expr_ref_vector args1(m), args2(m); expr_ref f1 = instantiate_lambda(e1); expr_ref f2 = instantiate_lambda(e2); args1.push_back(f1); args2.push_back(f2); svector names; sort_ref_vector sorts(m); for (unsigned i = 0; i < dimension; i++) { sort * srt = get_array_domain(s, i); sorts.push_back(srt); names.push_back(symbol(i)); expr * k = m.mk_var(dimension - i - 1, srt); args1.push_back(k); args2.push_back(k); } expr * sel1 = mk_select(dimension+1, args1.c_ptr()); expr * sel2 = mk_select(dimension+1, args2.c_ptr()); expr * eq = m.mk_eq(sel1, sel2); expr_ref q(m.mk_forall(dimension, sorts.c_ptr(), names.c_ptr(), eq), m); ctx.get_rewriter()(q); if (!ctx.b_internalized(q)) { ctx.internalize(q, true); } literal fa_eq = ctx.get_literal(q); ctx.mark_as_relevant(fa_eq); assert_axiom(~n1_eq_n2, fa_eq); } expr_ref theory_array_base::instantiate_lambda(app* e) { ast_manager& m = get_manager(); quantifier * q = m.is_lambda_def(e->get_decl()); expr_ref f(e, m); if (q) { var_subst sub(m, false); f = sub(q, e->get_num_args(), e->get_args()); } return f; } bool theory_array_base::can_propagate() { return !m_axiom1_todo.empty() || !m_axiom2_todo.empty() || !m_extensionality_todo.empty() || !m_congruent_todo.empty(); } void theory_array_base::propagate() { while (can_propagate()) { for (unsigned i = 0; i < m_axiom1_todo.size(); i++) assert_store_axiom1_core(m_axiom1_todo[i]); m_axiom1_todo.reset(); for (unsigned i = 0; i < m_axiom2_todo.size(); i++) assert_store_axiom2_core(m_axiom2_todo[i].first, m_axiom2_todo[i].second); m_axiom2_todo.reset(); for (unsigned i = 0; i < m_extensionality_todo.size(); i++) assert_extensionality_core(m_extensionality_todo[i].first, m_extensionality_todo[i].second); for (unsigned i = 0; i < m_congruent_todo.size(); i++) assert_congruent_core(m_congruent_todo[i].first, m_congruent_todo[i].second); m_extensionality_todo.reset(); m_congruent_todo.reset(); } } /** \brief Return true if v is shared between two different "instances" of the array theory. It is shared if it is used in more than one role. The possible roles are: array, index, and value. Example: (store v i j) <--- v is used as an array (select A v) <--- v is used as an index (store A i v) <--- v is used as an value */ bool theory_array_base::is_shared(theory_var v) const { enode * n = get_enode(v); enode * r = n->get_root(); bool is_array = false; bool is_index = false; bool is_value = false; int num_roles = 0; #define SET_ARRAY(arg) if (arg->get_root() == r && !is_array) { is_array = true; num_roles++; } if (num_roles > 1) return true #define SET_INDEX(arg) if (arg->get_root() == r && !is_index) { is_index = true; num_roles++; } if (num_roles > 1) return true #define SET_VALUE(arg) if (arg->get_root() == r && !is_value) { is_value = true; num_roles++; } if (num_roles > 1) return true enode_vector::const_iterator it = r->begin_parents(); enode_vector::const_iterator end = r->end_parents(); for (; it != end; ++it) { enode * parent = *it; #if 0 if (!ctx.is_relevant(parent)) continue; #endif unsigned num_args = parent->get_num_args(); if (is_store(parent)) { SET_ARRAY(parent->get_arg(0)); for (unsigned i = 1; i < num_args - 1; i++) { SET_INDEX(parent->get_arg(i)); } SET_VALUE(parent->get_arg(num_args - 1)); } else if (is_select(parent)) { SET_ARRAY(parent->get_arg(0)); for (unsigned i = 1; i < num_args; i++) { SET_INDEX(parent->get_arg(i)); } } else if (is_const(parent)) { SET_VALUE(parent->get_arg(0)); } } return false; } #if 0 void theory_array_base::collect_shared_vars(sbuffer & result) { TRACE("array_shared", tout << "collecting shared vars...\n";); context & ctx = get_context(); ptr_buffer to_unmark; unsigned num_vars = get_num_vars(); for (unsigned i = 0; i < num_vars; i++) { enode * n = get_enode(i); if (ctx.is_relevant(n) && ctx.is_shared(n)) { enode * r = n->get_root(); if (!r->is_marked() && is_array_sort(r)) { TRACE("array_shared", tout << "new shared var: #" << r->get_owner_id() << "\n";); r->set_mark(); to_unmark.push_back(r); theory_var r_th_var = r->get_th_var(get_id()); SASSERT(r_th_var != null_theory_var); result.push_back(r_th_var); } } } unmark_enodes(to_unmark.size(), to_unmark.c_ptr()); } #else void theory_array_base::collect_shared_vars(sbuffer & result) { TRACE("array_shared", tout << "collecting shared vars...\n";); context & ctx = get_context(); ptr_buffer to_unmark; unsigned num_vars = get_num_vars(); for (unsigned i = 0; i < num_vars; i++) { enode * n = get_enode(i); if (ctx.is_relevant(n)) { enode * r = n->get_root(); if (!r->is_marked()){ if(is_array_sort(r) && ctx.is_shared(r)) { TRACE("array_shared", tout << "new shared var: #" << r->get_owner_id() << "\n";); theory_var r_th_var = r->get_th_var(get_id()); SASSERT(r_th_var != null_theory_var); result.push_back(r_th_var); } r->set_mark(); to_unmark.push_back(r); } } } unmark_enodes(to_unmark.size(), to_unmark.c_ptr()); } #endif /** \brief Create interface variables for shared array variables. Return the number of new interface equalities. */ unsigned theory_array_base::mk_interface_eqs() { context & ctx = get_context(); ast_manager & m = get_manager(); sbuffer roots; collect_shared_vars(roots); unsigned result = 0; sbuffer::iterator it1 = roots.begin(); sbuffer::iterator end1 = roots.end(); for (; it1 != end1; ++it1) { TRACE("array_bug", tout << "mk_interface_eqs: processing: v" << *it1 << "\n";); theory_var v1 = *it1; enode * n1 = get_enode(v1); sort * s1 = m.get_sort(n1->get_owner()); sbuffer::iterator it2 = it1; ++it2; for (; it2 != end1; ++it2) { theory_var v2 = *it2; enode * n2 = get_enode(v2); sort * s2 = m.get_sort(n2->get_owner()); if (s1 == s2 && !ctx.is_diseq(n1, n2)) { app * eq = mk_eq_atom(n1->get_owner(), n2->get_owner()); if (!ctx.b_internalized(eq) || !ctx.is_relevant(eq)) { result++; ctx.internalize(eq, true); ctx.mark_as_relevant(eq); } } } } return result; } void theory_array_base::push_scope_eh() { m_scopes.push_back(scope(m_sorts_trail.size())); theory::push_scope_eh(); } void theory_array_base::pop_scope_eh(unsigned num_scopes) { reset_queues(); scope const & s = m_scopes[m_scopes.size() - num_scopes]; restore_sorts(s.m_sorts_trail_lim); m_scopes.shrink(m_scopes.size()-num_scopes); theory::pop_scope_eh(num_scopes); } void theory_array_base::restore_sorts(unsigned old_size) { while (m_sorts_trail.size() > old_size) { sort * s = m_sorts_trail.back(); func_decl_ref_vector * funcs = nullptr; if (m_sort2skolem.find(s, funcs)) { m_sort2skolem.remove(s); dealloc(funcs); } m_sorts_trail.pop_back(); } } void theory_array_base::reset_eh() { reset_queues(); pop_scope_eh(0); theory::reset_eh(); } void theory_array_base::reset_queues() { m_axiom1_todo.reset(); m_axiom2_todo.reset(); m_extensionality_todo.reset(); m_congruent_todo.reset(); } void theory_array_base::set_default(theory_var v, enode* n) { TRACE("array", tout << "set default: " << v << " " << mk_pp(n->get_owner(), get_manager()) << "\n";); v = mg_find(v); if (m_defaults[v] == 0) { m_defaults[v] = n; } } enode* theory_array_base::get_default(theory_var v) { return m_defaults[mg_find(v)]; } theory_var theory_array_base::mg_find(theory_var n) { if (m_parents[n] < 0) { return n; } theory_var n0 = n; n = m_parents[n0]; if (m_parents[n] < -1) { return n; } while (m_parents[n] >= 0) { n = m_parents[n]; } // compress path. while (m_parents[n0] >= 0) { theory_var n1 = m_parents[n0]; m_parents[n0] = n; n0 = n1; } return n; } void theory_array_base::mg_merge(theory_var n, theory_var m) { n = mg_find(n); m = mg_find(m); if (n != m) { SASSERT(m_parents[n] < 0); SASSERT(m_parents[m] < 0); if (m_parents[n] > m_parents[m]) { std::swap(n, m); } m_parents[n] += m_parents[m]; m_parents[m] = n; if (m_defaults[n] == 0) { m_defaults[n] = m_defaults[m]; } CTRACE("array", m_defaults[m], tout << mk_pp(m_defaults[m]->get_root()->get_owner(), get_manager()) << "\n"; tout << mk_pp(m_defaults[n]->get_root()->get_owner(), get_manager()) << "\n"; ); // NB. it may be the case that m_defaults[m] != m_defaults[n] // when m and n are finite arrays. } } void theory_array_base::init_model(model_generator & m) { m_factory = alloc(array_factory, get_manager(), m.get_model()); m.register_factory(m_factory); m_use_unspecified_default = is_unspecified_default_ok(); collect_defaults(); collect_selects(); propagate_selects(); if (m_bapa) m_bapa->init_model(); } /** \brief It is ok to use an unspecified default value for arrays, when the logical context does not contain store, default and const terms. That is, other modules (such as smt_model_finder) may set the default value to an arbitrary value. */ bool theory_array_base::is_unspecified_default_ok() const { context & ctx = get_context(); int num_vars = get_num_vars(); for (theory_var v = 0; v < num_vars; ++v) { enode * n = get_enode(v); // If n is not relevant, then it should not be used to set defaults. if (!ctx.is_relevant(n)) continue; if (is_store(n) || is_const(n) || is_default(n) || is_set_has_size(n)) return false; } return true; } void theory_array_base::collect_defaults() { int num_vars = get_num_vars(); m_defaults.reset(); m_else_values.reset(); m_parents.reset(); m_parents.resize(num_vars, -1); m_defaults.resize(num_vars); m_else_values.resize(num_vars); if (m_use_unspecified_default) return; context & ctx = get_context(); // // Create equivalence classes for defaults. // for (theory_var v = 0; v < num_vars; ++v) { enode * n = get_enode(v); // If n is not relevant, then it should not be used to set defaults. if (!ctx.is_relevant(n)) continue; theory_var r = get_representative(v); mg_merge(v, r); if (is_store(n)) { theory_var w = n->get_arg(0)->get_th_var(get_id()); SASSERT(w != null_theory_var); mg_merge(v, get_representative(w)); TRACE("array", tout << "merge: " << mk_pp(n->get_owner(), get_manager()) << " " << v << " " << w << "\n";); } else if (is_const(n)) { set_default(v, n->get_arg(0)); } else if (is_default(n)) { theory_var w = n->get_arg(0)->get_th_var(get_id()); SASSERT(w != null_theory_var); set_default(w, n); } } } unsigned theory_array_base::sel_hash::operator()(enode * n) const { return get_composite_hash(n, n->get_num_args() - 1, sel_khasher(), sel_chasher()); } bool theory_array_base::sel_eq::operator()(enode * n1, enode * n2) const { SASSERT(n1->get_num_args() == n2->get_num_args()); unsigned num_args = n1->get_num_args(); for (unsigned i = 1; i < num_args; i++) { if (n1->get_arg(i)->get_root() != n2->get_arg(i)->get_root()) return false; } return true; } theory_array_base::select_set * theory_array_base::get_select_set(enode * n) { enode * r = n->get_root(); select_set * set = nullptr; m_selects.find(r, set); if (set == nullptr) { set = alloc(select_set); m_selects.insert(r, set); m_selects_domain.push_back(r); m_selects_range.push_back(set); } return set; } void theory_array_base::collect_selects() { int num_vars = get_num_vars(); m_selects.reset(); m_selects_domain.reset(); m_selects_range.reset(); for (theory_var v = 0; v < num_vars; ++v) { enode * r = get_enode(v)->get_root(); if (is_representative(v) && get_context().is_relevant(r)) { for (enode * parent : r->get_const_parents()) { if (parent->get_cg() == parent && get_context().is_relevant(parent) && is_select(parent) && parent->get_arg(0)->get_root() == r) { select_set * s = get_select_set(r); SASSERT(!s->contains(parent) || (*(s->find(parent)))->get_root() == parent->get_root()); s->insert(parent); } } } } } void theory_array_base::propagate_select_to_store_parents(enode * r, enode * sel, enode_pair_vector & todo) { SASSERT(r->get_root() == r); SASSERT(is_select(sel)); if (!get_context().is_relevant(r)) { return; } for (enode * parent : r->get_const_parents()) { if (get_context().is_relevant(parent) && is_store(parent) && parent->get_arg(0)->get_root() == r) { // propagate upward select_set * parent_sel_set = get_select_set(parent); enode * parent_root = parent->get_root(); if (parent_sel_set->contains(sel)) continue; SASSERT(sel->get_num_args() + 1 == parent->get_num_args()); // check whether the sel idx was overwritten by the store unsigned num_args = sel->get_num_args(); unsigned i = 1; for (; i < num_args; i++) { if (sel->get_arg(i)->get_root() != parent->get_arg(i)->get_root()) break; } if (i < num_args) { SASSERT(!parent_sel_set->contains(sel) || (*(parent_sel_set->find(sel)))->get_root() == sel->get_root()); parent_sel_set->insert(sel); todo.push_back(std::make_pair(parent_root, sel)); } } } } void theory_array_base::propagate_selects_to_store_parents(enode * r, enode_pair_vector & todo) { select_set * sel_set = get_select_set(r); for (enode* sel : *sel_set) { SASSERT(is_select(sel)); propagate_select_to_store_parents(r, sel, todo); } } void theory_array_base::propagate_selects() { enode_pair_vector todo; for (enode * r : m_selects_domain) { propagate_selects_to_store_parents(r, todo); } for (unsigned qhead = 0; qhead < todo.size(); qhead++) { enode_pair & pair = todo[qhead]; enode * r = pair.first; enode * sel = pair.second; propagate_select_to_store_parents(r, sel, todo); } } void theory_array_base::finalize_model(model_generator & m) { std::for_each(m_selects_range.begin(), m_selects_range.end(), delete_proc()); } class array_value_proc : public model_value_proc { family_id m_fid; sort * m_sort; unsigned m_num_entries; unsigned m_dim; //!< number of dimensions; app * m_else; bool m_unspecified_else; svector m_dependencies; public: array_value_proc(family_id fid, sort * s, extra_fresh_value * v): m_fid(fid), m_sort(s), m_num_entries(0), m_dim(0), m_else(nullptr), m_unspecified_else(false) { m_dependencies.push_back(model_value_dependency(v)); } array_value_proc(family_id fid, sort * s, app * else_value): m_fid(fid), m_sort(s), m_num_entries(0), m_dim(0), m_else(else_value), m_unspecified_else(false) { } array_value_proc(family_id fid, sort * s, enode * else_value): m_fid(fid), m_sort(s), m_num_entries(0), m_dim(0), m_else(nullptr), m_unspecified_else(false) { m_dependencies.push_back(model_value_dependency(else_value)); } array_value_proc(family_id fid, sort * s): m_fid(fid), m_sort(s), m_num_entries(0), m_dim(0), m_else(nullptr), m_unspecified_else(true) { } ~array_value_proc() override {} void add_entry(unsigned num_args, enode * const * args, enode * value) { SASSERT(num_args > 0); SASSERT(m_dim == 0 || m_dim == num_args); m_dim = num_args; m_num_entries ++; for (unsigned i = 0; i < num_args; i++) m_dependencies.push_back(model_value_dependency(args[i])); m_dependencies.push_back(model_value_dependency(value)); } void get_dependencies(buffer & result) override { result.append(m_dependencies.size(), m_dependencies.c_ptr()); } app * mk_value(model_generator & mg, expr_ref_vector const & values) override { // values must have size = m_num_entries * (m_dim + 1) + ((m_else || m_unspecified_else) ? 0 : 1) // an array value is a lookup table + else_value // each entry has m_dim indexes that map to a value. ast_manager & m = mg.get_manager(); SASSERT(values.size() == m_dependencies.size()); SASSERT(values.size() == m_num_entries * (m_dim + 1) + ((m_else || m_unspecified_else) ? 0 : 1)); unsigned arity = get_array_arity(m_sort); func_decl * f = mk_aux_decl_for_array_sort(m, m_sort); func_interp * fi = alloc(func_interp, m, arity); mg.get_model().register_decl(f, fi); unsigned idx = 0; if (m_else || m_unspecified_else) { fi->set_else(m_else); } else { fi->set_else(to_app(values[0])); idx = 1; } ptr_buffer args; for (unsigned i = 0; i < m_num_entries; i++) { args.reset(); // copy indices for (unsigned j = 0; j < m_dim; j++, idx++) args.push_back(values[idx]); expr * result = values[idx]; idx++; fi->insert_entry(args.c_ptr(), result); } parameter p[1] = { parameter(f) }; return m.mk_app(m_fid, OP_AS_ARRAY, 1, p); } }; bool theory_array_base::include_func_interp(func_decl* f) { return is_decl_of(f, get_id(), OP_ARRAY_EXT); } model_value_proc * theory_array_base::mk_value(enode * n, model_generator & m) { SASSERT(get_context().is_relevant(n)); theory_var v = n->get_th_var(get_id()); SASSERT(v != null_theory_var); sort * s = get_manager().get_sort(n->get_owner()); enode * else_val_n = get_default(v); array_value_proc * result = nullptr; if (m_use_unspecified_default) { SASSERT(else_val_n == 0); result = alloc(array_value_proc, get_id(), s); } else { if (else_val_n != nullptr) { SASSERT(get_context().is_relevant(else_val_n)); result = alloc(array_value_proc, get_id(), s, else_val_n); } else { theory_var r = mg_find(v); void * else_val = m_else_values[r]; // DISABLED. It seems wrong, since different nodes can share the same // else_val according to the mg class. // SASSERT(else_val == 0 || get_context().is_relevant(UNTAG(app*, else_val))); if (else_val == nullptr) { sort * range = to_sort(s->get_parameter(s->get_num_parameters() - 1).get_ast()); // IMPORTANT: // The implementation should not assume a fresh value is created for // the else_val if the range is finite if (range->is_infinite()) else_val = TAG(void*, m.mk_extra_fresh_value(range), 1); else else_val = TAG(void*, m.get_some_value(range), 0); m_else_values[r] = else_val; } if (GET_TAG(else_val) == 0) { result = alloc(array_value_proc, get_id(), s, UNTAG(app*, else_val)); } else { result = alloc(array_value_proc, get_id(), s, UNTAG(extra_fresh_value*, else_val)); } } } SASSERT(result != 0); select_set * sel_set = nullptr; m_selects.find(n->get_root(), sel_set); if (sel_set != nullptr) { ptr_buffer args; for (enode * select : *sel_set) { args.reset(); unsigned num = select->get_num_args(); for (unsigned j = 1; j < num; ++j) args.push_back(select->get_arg(j)); SASSERT(get_context().is_relevant(select)); result->add_entry(args.size(), args.c_ptr(), select); } } TRACE("array", tout << mk_pp(n->get_root()->get_owner(), get_manager()) << "\n"; if (sel_set) { for (enode* s : *sel_set) { tout << "#" << s->get_root()->get_owner()->get_id() << " " << mk_pp(s->get_owner(), get_manager()) << "\n"; } } if (else_val_n) { tout << "else: " << mk_pp(else_val_n->get_owner(), get_manager()) << "\n"; }); return result; } }; z3-z3-4.8.7/src/smt/theory_array_base.h000066400000000000000000000210151356505360400176730ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_array_base.h Abstract: Author: Leonardo de Moura (leonardo) 2008-06-02. Revision History: --*/ #ifndef THEORY_ARRAY_BASE_H_ #define THEORY_ARRAY_BASE_H_ #include "smt/smt_theory.h" #include "smt/theory_array_bapa.h" #include "ast/array_decl_plugin.h" #include "model/array_factory.h" namespace smt { class theory_array_base : public theory { friend class theory_array_bapa; protected: bool m_found_unsupported_op; void found_unsupported_op(expr * n); void found_unsupported_op(enode* n) { found_unsupported_op(n->get_owner()); } void found_unsupported_op(theory_var v) { found_unsupported_op(get_enode(v)->get_owner()); } bool is_store(app const* n) const { return n->is_app_of(get_id(), OP_STORE); } bool is_map(app const* n) const { return n->is_app_of(get_id(), OP_ARRAY_MAP); } bool is_select(app const* n) const { return n->is_app_of(get_id(), OP_SELECT); } bool is_default(app const* n) const { return n->is_app_of(get_id(), OP_ARRAY_DEFAULT); } bool is_const(app const* n) const { return n->is_app_of(get_id(), OP_CONST_ARRAY); } bool is_array_ext(app const * n) const { return n->is_app_of(get_id(), OP_ARRAY_EXT); } bool is_as_array(app const * n) const { return n->is_app_of(get_id(), OP_AS_ARRAY); } bool is_array_sort(sort const* s) const { return s->is_sort_of(get_id(), ARRAY_SORT); } bool is_array_sort(app const* n) const { return is_array_sort(get_manager().get_sort(n)); } bool is_set_has_size(app const* n) const { return n->is_app_of(get_id(), OP_SET_HAS_SIZE); } bool is_set_card(app const* n) const { return n->is_app_of(get_id(), OP_SET_CARD); } bool is_store(enode const * n) const { return is_store(n->get_owner()); } bool is_map(enode const* n) const { return is_map(n->get_owner()); } bool is_select(enode const* n) const { return is_select(n->get_owner()); } bool is_const(enode const* n) const { return is_const(n->get_owner()); } bool is_as_array(enode const * n) const { return is_as_array(n->get_owner()); } bool is_default(enode const* n) const { return is_default(n->get_owner()); } bool is_array_sort(enode const* n) const { return is_array_sort(n->get_owner()); } bool is_set_has_size(enode const* n) const { return is_set_has_size(n->get_owner()); } bool is_set_carde(enode const* n) const { return is_set_card(n->get_owner()); } app * mk_select(unsigned num_args, expr * const * args); app * mk_store(unsigned num_args, expr * const * args); app * mk_default(expr* a); unsigned get_dimension(sort* s) const; ptr_vector m_axiom1_todo; enode_pair_vector m_axiom2_todo; enode_pair_vector m_extensionality_todo; enode_pair_vector m_congruent_todo; scoped_ptr m_bapa; void assert_axiom(unsigned num_lits, literal * lits); void assert_axiom(literal l1, literal l2); void assert_axiom(literal l); void assert_store_axiom1_core(enode * n); void assert_store_axiom2_core(enode * store, enode * select); void assert_store_axiom1(enode * n) { m_axiom1_todo.push_back(n); } bool assert_store_axiom2(enode * store, enode * select); void assert_extensionality_core(enode * a1, enode * a2); bool assert_extensionality(enode * a1, enode * a2); expr_ref instantiate_lambda(app* e); void assert_congruent_core(enode * a1, enode * a2); void assert_congruent(enode * a1, enode * a2); // -------------------------------------------------- // Array sort -> extensionality skolems // // -------------------------------------------------- ptr_vector m_sorts_trail; obj_map m_sort2skolem; func_decl_ref_vector * register_sort(sort * s_array); // -------------------------------------------------- // array_value table // // Use select(A, i) nodes to represent an assignment for A. // This structure is used to minimize the number of times the // extensionality axiom is applied. // // -------------------------------------------------- struct value_chasher { unsigned operator()(enode const * n, unsigned idx) const { return n->get_arg(idx+1)->get_root()->hash(); } }; struct value_khasher { unsigned operator()(enode * n) const { return 17; } }; struct value_hash_proc { unsigned operator()(enode * n) const { return get_composite_hash(n, n->get_num_args() - 1); } }; struct value_eq_proc { bool operator()(enode * n1, enode * n2) const; }; typedef ptr_hashtable array_value; array_value m_array_value; bool already_diseq(enode * v1, enode * v2); // -------------------------------------------------- // Backtracking // // // -------------------------------------------------- struct scope { unsigned m_sorts_trail_lim; scope(unsigned l):m_sorts_trail_lim(l) {} }; svector m_scopes; void restore_sorts(unsigned old_size); // -------------------------------------------------- // Interface // // // -------------------------------------------------- bool is_shared(theory_var v) const override; void collect_shared_vars(sbuffer & result); unsigned mk_interface_eqs(); bool can_propagate() override; void propagate() override; void push_scope_eh() override; void pop_scope_eh(unsigned num_scopes) override; void reset_eh() override; void reset_queues(); // ----------------------------------- // // Model generation // // ----------------------------------- // I need a set of select enodes where select(A,i) = select(B,j) if i->get_root() == j->get_root() struct sel_khasher { unsigned operator()(enode const * n) const { return 0; } }; struct sel_chasher { unsigned operator()(enode const * n, unsigned idx) const { return n->get_arg(idx+1)->get_root()->hash(); } }; struct sel_hash { unsigned operator()(enode * n) const; }; struct sel_eq { bool operator()(enode * n1, enode * n2) const; }; typedef ptr_hashtable select_set; array_factory * m_factory; ptr_vector m_defaults; // temporary field for model construction ptr_vector m_else_values; // tagged pointer: expr or extra_fresh_value svector m_parents; // temporary field for model construction obj_map m_selects; // mapping from array -> relevant selects ptr_vector m_selects_domain; ptr_vector m_selects_range; bool m_use_unspecified_default; // temporary field for model construction theory_var mg_find(theory_var v); void mg_merge(theory_var n, theory_var m); void set_default(theory_var v, enode* n); enode* get_default(theory_var v); void init_model(model_generator & m) override; bool is_unspecified_default_ok() const; void collect_defaults(); void collect_selects(); void propagate_select_to_store_parents(enode * r, enode * sel, enode_pair_vector & todo); void propagate_selects_to_store_parents(enode * r, enode_pair_vector & todo); void propagate_selects(); select_set * get_select_set(enode * n); void finalize_model(model_generator & m) override; model_value_proc * mk_value(enode * n, model_generator & m) override; bool include_func_interp(func_decl* f) override; public: theory_array_base(ast_manager & m); ~theory_array_base() override { restore_sorts(0); } }; }; #endif /* THEORY_ARRAY_BASE_H_ */ z3-z3-4.8.7/src/smt/theory_array_full.cpp000066400000000000000000000736301356505360400202700ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_array_full.cpp Abstract: Author: Nikolaj Bjorner 2008-22-10 Revision History: --*/ #include "util/stats.h" #include "ast/ast_ll_pp.h" #include "ast/ast_pp.h" #include "ast/ast_util.h" #include "ast/ast_smt2_pp.h" #include "smt/smt_context.h" #include "smt/theory_array_full.h" namespace smt { theory_array_full::theory_array_full(ast_manager & m, theory_array_params & params) : theory_array(m, params), m_sort2epsilon(m) {} theory_array_full::~theory_array_full() { std::for_each(m_var_data_full.begin(), m_var_data_full.end(), delete_proc()); m_var_data_full.reset(); } theory* theory_array_full::mk_fresh(context* new_ctx) { return alloc(theory_array_full, new_ctx->get_manager(), m_params); } void theory_array_full::add_map(theory_var v, enode* s) { if (m_params.m_array_cg && !s->is_cgr()) { return; } SASSERT(is_map(s)); v = find(v); var_data_full * d_full = m_var_data_full[v]; var_data * d = m_var_data[v]; // // TODO: defaulting to exhaustive up-propagation. // instead apply stratified filter. set_prop_upward(v,d); d_full->m_maps.push_back(s); m_trail_stack.push(push_back_trail(d_full->m_maps)); for (unsigned i = 0; i < d->m_parent_selects.size(); ++i) { enode* n = d->m_parent_selects[i]; SASSERT(is_select(n)); instantiate_select_map_axiom(n, s); } set_prop_upward(s); } bool theory_array_full::instantiate_axiom_map_for(theory_var v) { bool result = false; var_data * d = m_var_data[v]; var_data_full * d_full = m_var_data_full[v]; 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; } } return result; } void theory_array_full::add_parent_map(theory_var v, enode* s) { if (m_params.m_array_cg && !s->is_cgr()) { return; } SASSERT(v != null_theory_var); SASSERT(is_map(s)); v = find(v); var_data * d = m_var_data[v]; var_data_full * d_full = m_var_data_full[v]; d_full->m_parent_maps.push_back(s); m_trail_stack.push(push_back_trail(d_full->m_parent_maps)); if (!m_params.m_array_delay_exp_axiom && d->m_prop_upward) { for (enode * n : d->m_parent_selects) { if (!m_params.m_array_cg || n->is_cgr()) { if (m_params.m_array_weak) { found_unsupported_op(s); break; } instantiate_select_map_axiom(n, s); } } } } // // set set_prop_upward on root and recursively on children if necessary. // void theory_array_full::set_prop_upward(theory_var v) { v = find(v); var_data * d = m_var_data[v]; if (!d->m_prop_upward) { m_trail_stack.push(reset_flag_trail(d->m_prop_upward)); d->m_prop_upward = true; TRACE("array", tout << "#" << v << "\n";); if (m_params.m_array_weak) { found_unsupported_op(v); return; } if (!m_params.m_array_delay_exp_axiom) { instantiate_axiom2b_for(v); instantiate_axiom_map_for(v); } var_data_full * d2 = m_var_data_full[v]; for (enode * n : d->m_stores) { set_prop_upward(n); } for (enode * n : d2->m_maps) { set_prop_upward(n); } for (enode * n : d2->m_consts) { set_prop_upward(n); } } } // // call set_prop_upward on array arguments. // void theory_array_full::set_prop_upward(enode * n) { TRACE("array", tout << mk_pp(n->get_owner(), get_manager()) << "\n";); if (is_store(n)) { set_prop_upward(n->get_arg(0)->get_th_var(get_id())); } else if (is_map(n)) { for (enode* arg : enode::args(n)) { set_prop_upward(arg->get_th_var(get_id())); } } } void theory_array_full::set_prop_upward(theory_var v, var_data* d) { if (m_params.m_array_always_prop_upward || !d->m_stores.empty()) { theory_array::set_prop_upward(v, d); } else { var_data_full * d2 = m_var_data_full[v]; unsigned sz = d2->m_maps.size(); for(unsigned i = 0; i < sz; ++i) { set_prop_upward(d2->m_maps[i]); } } } unsigned theory_array_full::get_lambda_equiv_size(theory_var v, var_data* d) { var_data_full * d2 = m_var_data_full[v]; return d->m_stores.size() + 2*d2->m_consts.size() + 2*d2->m_maps.size(); } void theory_array_full::add_const(theory_var v, enode* cnst) { var_data * d = m_var_data[v]; unsigned lambda_equiv_class_size = get_lambda_equiv_size(v, d); if (m_params.m_array_always_prop_upward || lambda_equiv_class_size >= 1) { set_prop_upward(v, d); } ptr_vector & consts = m_var_data_full[v]->m_consts; m_trail_stack.push(push_back_trail(consts)); consts.push_back(cnst); instantiate_default_const_axiom(cnst); for (enode * n : d->m_parent_selects) { SASSERT(is_select(n)); instantiate_select_const_axiom(n, cnst); } } void theory_array_full::add_as_array(theory_var v, enode* arr) { var_data * d = m_var_data[v]; unsigned lambda_equiv_class_size = get_lambda_equiv_size(v, d); if (m_params.m_array_always_prop_upward || lambda_equiv_class_size >= 1) { set_prop_upward(v, d); } ptr_vector & as_arrays = m_var_data_full[v]->m_as_arrays; m_trail_stack.push(push_back_trail(as_arrays)); as_arrays.push_back(arr); instantiate_default_as_array_axiom(arr); for (enode * n : d->m_parent_selects) { SASSERT(is_select(n)); instantiate_select_as_array_axiom(n, arr); } } void theory_array_full::reset_eh() { theory_array::reset_eh(); std::for_each(m_var_data_full.begin(), m_var_data_full.end(), delete_proc()); m_var_data_full.reset(); m_eqs.reset(); m_eqsv.reset(); } void theory_array_full::display_var(std::ostream & out, theory_var v) const { theory_array::display_var(out, v); var_data_full const * d = m_var_data_full[v]; out << " maps: {"; display_ids(out, d->m_maps.size(), d->m_maps.c_ptr()); out << "} p_parent_maps: {"; display_ids(out, d->m_parent_maps.size(), d->m_parent_maps.c_ptr()); out << "} p_const: {"; display_ids(out, d->m_consts.size(), d->m_consts.c_ptr()); out << "}\n"; } theory_var theory_array_full::mk_var(enode * n) { theory_var r = theory_array::mk_var(n); SASSERT(r == static_cast(m_var_data_full.size())); m_var_data_full.push_back(alloc(var_data_full)); var_data_full * d = m_var_data_full.back(); if (is_map(n)) { instantiate_default_map_axiom(n); d->m_maps.push_back(n); } else if (is_const(n)) { instantiate_default_const_axiom(n); d->m_consts.push_back(n); } else if (is_default(n)) { // no-op } else if (is_as_array(n)) { instantiate_default_as_array_axiom(n); d->m_as_arrays.push_back(n); } return r; } bool theory_array_full::internalize_atom(app * atom, bool) { return internalize_term(atom); } bool theory_array_full::internalize_term(app * n) { context & ctx = get_context(); if (ctx.e_internalized(n)) { return true; } TRACE("array", tout << mk_pp(n, get_manager()) << "\n";); if (is_store(n) || is_select(n)) { return theory_array::internalize_term(n); } if (!is_const(n) && !is_default(n) && !is_map(n) && !is_as_array(n) && !is_set_has_size(n) && !is_set_card(n)) { if (!is_array_ext(n)) found_unsupported_op(n); return false; } if (!internalize_term_core(n)) { return true; } if (is_map(n) || is_array_ext(n)) { for (expr* e : *n) { enode* arg = ctx.get_enode(e); if (!is_attached_to_var(arg)) { mk_var(arg); } } } else if (is_default(n)) { enode* arg0 = ctx.get_enode(n->get_arg(0)); if (!is_attached_to_var(arg0)) { mk_var(arg0); } } else if (is_set_has_size(n) || is_set_card(n)) { if (!m_bapa) { m_bapa = alloc(theory_array_bapa, *this); } m_bapa->internalize_term(n); } enode* node = ctx.get_enode(n); if (!is_attached_to_var(node)) { mk_var(node); } if (is_default(n)) { enode* arg0 = ctx.get_enode(n->get_arg(0)); theory_var v_arg = arg0->get_th_var(get_id()); add_parent_default(v_arg); } else if (is_map(n)) { for (expr* e : *n) { enode* arg = ctx.get_enode(e); theory_var v_arg = arg->get_th_var(get_id()); add_parent_map(v_arg, node); } instantiate_default_map_axiom(node); } else if (is_const(n)) { instantiate_default_const_axiom(node); } else if (is_as_array(n)) { // The array theory is not a decision procedure // for as-array. // Ex: (as-array f) = (as-array g) & f(0) = 0 & g(0) = 1 // There is nothing to propagate the disequality. // Even if there was, as-array on interpreted // functions will be incomplete. // The instantiation operations are still sound to include. found_unsupported_op(n); instantiate_default_as_array_axiom(node); } else if (is_array_ext(n)) { SASSERT(n->get_num_args() == 2); instantiate_extensionality(ctx.get_enode(n->get_arg(0)), ctx.get_enode(n->get_arg(1))); } return true; } void theory_array_full::merge_eh(theory_var v1, theory_var v2, theory_var u, theory_var w) { theory_array::merge_eh(v1, v2, u, w); // v1 is the new root SASSERT(v1 == find(v1)); var_data_full * d2 = m_var_data_full[v2]; for (enode * n : d2->m_maps) { add_map(v1, n); } for (enode * n : d2->m_parent_maps) { add_parent_map(v1, n); } for (enode * n : d2->m_consts) { add_const(v1, n); } for (enode * n : d2->m_as_arrays) { add_as_array(v1, n); } TRACE("array", tout << mk_pp(get_enode(v1)->get_owner(), get_manager()) << "\n"; tout << mk_pp(get_enode(v2)->get_owner(), get_manager()) << "\n"; tout << "merge in\n"; display_var(tout, v2); tout << "after merge\n"; display_var(tout, v1);); } void theory_array_full::add_parent_default(theory_var v) { SASSERT(v != null_theory_var); v = find(v); var_data* d = m_var_data[v]; TRACE("array", tout << "v" << v << " " << mk_pp(get_enode(v)->get_owner(), get_manager()) << " " << d->m_prop_upward << " " << m_params.m_array_delay_exp_axiom << "\n";); for (enode * store : d->m_stores) { SASSERT(is_store(store)); instantiate_default_store_axiom(store); } if (!m_params.m_array_delay_exp_axiom && d->m_prop_upward) { if (m_params.m_array_weak) { found_unsupported_op(v); } else { instantiate_parent_stores_default(v); } } } void theory_array_full::add_parent_select(theory_var v, enode * s) { TRACE("array", tout << v << " select parent: " << mk_pp(s->get_owner(), get_manager()) << "\n"; display_var(tout, v); ); theory_array::add_parent_select(v,s); v = find(v); var_data_full* d_full = m_var_data_full[v]; var_data* d = m_var_data[v]; for (enode * n : d_full->m_consts) { instantiate_select_const_axiom(s, n); } for (enode * map : d_full->m_maps) { SASSERT(is_map(map)); instantiate_select_map_axiom(s, map); } if (!m_params.m_array_delay_exp_axiom && d->m_prop_upward) { for (enode * map : d_full->m_parent_maps) { SASSERT(is_map(map)); if (!m_params.m_array_cg || map->is_cgr()) { if (m_params.m_array_weak) { found_unsupported_op(s); break; } instantiate_select_map_axiom(s, map); } } } } void theory_array_full::relevant_eh(app* n) { TRACE("array", tout << mk_pp(n, get_manager()) << "\n";); theory_array::relevant_eh(n); if (!is_default(n) && !is_select(n) && !is_map(n) && !is_const(n) && !is_as_array(n)){ return; } context & ctx = get_context(); enode* node = ctx.get_enode(n); if (is_select(n)) { enode * arg = ctx.get_enode(n->get_arg(0)); theory_var v = arg->get_th_var(get_id()); SASSERT(v != null_theory_var); add_parent_select(find(v), node); } else if (is_default(n)) { enode * arg = ctx.get_enode(n->get_arg(0)); theory_var v = arg->get_th_var(get_id()); SASSERT(v != null_theory_var); set_prop_upward(v); add_parent_default(find(v)); } else if (is_const(n)) { instantiate_default_const_axiom(node); theory_var v = node->get_th_var(get_id()); set_prop_upward(v); add_parent_default(find(v)); } else if (is_map(n)) { for (expr * e : *n) { enode* arg = ctx.get_enode(e); theory_var v_arg = find(arg->get_th_var(get_id())); add_parent_map(v_arg, node); set_prop_upward(v_arg); } instantiate_default_map_axiom(node); } else if (is_as_array(n)) { instantiate_default_as_array_axiom(node); } } bool theory_array_full::should_research(expr_ref_vector & unsat_core) { return m_bapa && m_bapa->should_research(unsat_core); } void theory_array_full::add_theory_assumptions(expr_ref_vector & assumptions) { if (m_bapa) m_bapa->add_theory_assumptions(assumptions); } // // Assert axiom: // select(map[f](a, ... d), i) = f(select(a,i),...,select(d,i)) // bool theory_array_full::instantiate_select_map_axiom(enode* sl, enode* mp) { app* map = mp->get_owner(); app* select = sl->get_owner(); SASSERT(is_map(map)); SASSERT(is_select(select)); SASSERT(map->get_num_args() > 0); func_decl* f = to_func_decl(map->get_decl()->get_parameter(0).get_ast()); context& ctx = get_context(); ast_manager& m = get_manager(); TRACE("array_map_bug", tout << "invoked instantiate_select_map_axiom\n"; tout << sl->get_owner_id() << " " << mp->get_owner_id() << "\n"; tout << mk_ismt2_pp(sl->get_owner(), m) << "\n" << mk_ismt2_pp(mp->get_owner(), m) << "\n";); if (!ctx.add_fingerprint(mp, mp->get_owner_id(), sl->get_num_args() - 1, sl->get_args() + 1)) { return false; } TRACE("array_map_bug", tout << "new axiom\n";); m_stats.m_num_map_axiom++; TRACE("array", tout << mk_bounded_pp(mp->get_owner(), get_manager()) << "\n"; tout << mk_bounded_pp(sl->get_owner(), get_manager()) << "\n";); unsigned num_args = select->get_num_args(); unsigned num_arrays = map->get_num_args(); ptr_buffer args1, args2; vector > args2l; args1.push_back(map); for (expr* ar : *map) { ptr_vector arg; arg.push_back(ar); args2l.push_back(arg); } for (unsigned i = 1; i < num_args; ++i) { expr* arg = select->get_arg(i); for (unsigned j = 0; j < num_arrays; ++j) { args2l[j].push_back(arg); } args1.push_back(arg); } for (unsigned j = 0; j < num_arrays; ++j) { expr* sel = mk_select(args2l[j].size(), args2l[j].c_ptr()); args2.push_back(sel); } expr_ref sel1(m), sel2(m); sel1 = mk_select(args1.size(), args1.c_ptr()); sel2 = m.mk_app(f, args2.size(), args2.c_ptr()); ctx.get_rewriter()(sel2); ctx.internalize(sel1, false); ctx.internalize(sel2, false); TRACE("array_map_bug", tout << "select-map axiom\n" << mk_ismt2_pp(sel1, m) << "\n=\n" << mk_ismt2_pp(sel2,m) << "\n";); return try_assign_eq(sel1, sel2); } // // // Assert axiom: // default(map[f](a,..,d)) = f(default(a),..,default(d)) // bool theory_array_full::instantiate_default_map_axiom(enode* mp) { SASSERT(is_map(mp)); app* map = mp->get_owner(); ast_manager& m = get_manager(); context& ctx = get_context(); if (!ctx.add_fingerprint(this, m_default_map_fingerprint, 1, &mp)) { return false; } TRACE("array", tout << mk_bounded_pp(map, get_manager()) << "\n";); m_stats.m_num_default_map_axiom++; func_decl* f = to_func_decl(map->get_decl()->get_parameter(0).get_ast()); SASSERT(map->get_num_args() == f->get_arity()); ptr_buffer args2; for (expr* arg : *map) { args2.push_back(mk_default(arg)); } expr_ref def2(m.mk_app(f, args2.size(), args2.c_ptr()), m); ctx.get_rewriter()(def2); expr* def1 = mk_default(map); ctx.internalize(def1, false); ctx.internalize(def2, false); return try_assign_eq(def1, def2); } bool theory_array_full::instantiate_default_const_axiom(enode* cnst) { context& ctx = get_context(); if (!ctx.add_fingerprint(this, m_default_const_fingerprint, 1, &cnst)) { return false; } m_stats.m_num_default_const_axiom++; SASSERT(is_const(cnst)); TRACE("array", tout << mk_bounded_pp(cnst->get_owner(), get_manager()) << "\n";); expr* val = cnst->get_arg(0)->get_owner(); expr* def = mk_default(cnst->get_owner()); ctx.internalize(def, false); return try_assign_eq(val, def); } /** * instantiate f(ep1,ep2,..,ep_n) = default(as-array f) * it is disabled to avoid as-array terms during search. */ bool theory_array_full::instantiate_default_as_array_axiom(enode* arr) { return false; #if 0 context& ctx = get_context(); if (!ctx.add_fingerprint(this, m_default_as_array_fingerprint, 1, &arr)) { return false; } m_stats.m_num_default_as_array_axiom++; SASSERT(is_as_array(arr)); TRACE("array", tout << mk_bounded_pp(arr->get_owner(), get_manager()) << "\n";); expr* def = mk_default(arr->get_owner()); func_decl * f = array_util(get_manager()).get_as_array_func_decl(arr->get_owner()); ptr_vector args; for (unsigned i = 0; i < f->get_arity(); ++i) { args.push_back(mk_epsilon(f->get_domain(i))); } expr_ref val(get_manager().mk_app(f, args.size(), args.c_ptr()), get_manager()); ctx.internalize(def, false); ctx.internalize(val.get(), false); return try_assign_eq(val.get(), def); #endif } bool theory_array_full::has_large_domain(app* array_term) { SASSERT(is_array_sort(array_term)); sort* s = get_manager().get_sort(array_term); unsigned dim = get_dimension(s); parameter const * params = s->get_info()->get_parameters(); rational sz(1); for (unsigned i = 0; i < dim; ++i) { SASSERT(params[i].is_ast()); sort* d = to_sort(params[i].get_ast()); if (d->is_infinite() || d->is_very_big()) { return true; } sz *= rational(d->get_num_elements().size(),rational::ui64()); if (sz >= rational(1 << 20)) { return true; } } return false; } // // Assert axiom: // select(const v, i_1, ..., i_n) = v // bool theory_array_full::instantiate_select_const_axiom(enode* select, enode* cnst) { SASSERT(is_const(cnst)); SASSERT(is_select(select)); SASSERT(cnst->get_num_args() == 1); context& ctx = get_context(); unsigned num_args = select->get_num_args(); if (!ctx.add_fingerprint(cnst, cnst->get_owner_id(), select->get_num_args() - 1, select->get_args() + 1)) { return false; } m_stats.m_num_select_const_axiom++; ptr_buffer sel_args; sel_args.push_back(cnst->get_owner()); for (unsigned short i = 1; i < num_args; ++i) { sel_args.push_back(select->get_owner()->get_arg(i)); } expr * sel = mk_select(sel_args.size(), sel_args.c_ptr()); expr * val = cnst->get_owner()->get_arg(0); TRACE("array", tout << "new select-const axiom...\n"; tout << "const: " << mk_bounded_pp(cnst->get_owner(), get_manager()) << "\n"; tout << "select: " << mk_bounded_pp(select->get_owner(), get_manager()) << "\n"; tout << " sel/const: " << mk_bounded_pp(sel, get_manager()) << "\n"; tout << "value: " << mk_bounded_pp(val, get_manager()) << "\n"; tout << "#" << sel->get_id() << " = #" << val->get_id() << "\n"; ); ctx.internalize(sel, false); return try_assign_eq(sel,val); } // // Assert axiom: // select(as-array f, i_1, ..., i_n) = (f i_1 ... i_n) // bool theory_array_full::instantiate_select_as_array_axiom(enode* select, enode* arr) { SASSERT(is_as_array(arr->get_owner())); SASSERT(is_select(select)); SASSERT(arr->get_num_args() == 0); context& ctx = get_context(); unsigned num_args = select->get_num_args(); if (!ctx.add_fingerprint(arr, arr->get_owner_id(), select->get_num_args() - 1, select->get_args() + 1)) { return false; } m_stats.m_num_select_as_array_axiom++; ptr_buffer sel_args; sel_args.push_back(arr->get_owner()); for (unsigned short i = 1; i < num_args; ++i) { sel_args.push_back(select->get_owner()->get_arg(i)); } expr * sel = mk_select(sel_args.size(), sel_args.c_ptr()); func_decl * f = array_util(get_manager()).get_as_array_func_decl(arr->get_owner()); expr_ref val(get_manager().mk_app(f, sel_args.size()-1, sel_args.c_ptr()+1), get_manager()); TRACE("array", tout << "new select-as-array axiom...\n"; tout << "as-array: " << mk_bounded_pp(arr->get_owner(), get_manager()) << "\n"; tout << "select: " << mk_bounded_pp(select->get_owner(), get_manager()) << "\n"; tout << " sel/as-array: " << mk_bounded_pp(sel, get_manager()) << "\n"; tout << "value: " << mk_bounded_pp(val.get(), get_manager()) << "\n"; tout << "#" << sel->get_id() << " = #" << val->get_id() << "\n"; ); ctx.internalize(sel, false); ctx.internalize(val.get(), false); return try_assign_eq(sel,val); } bool theory_array_full::instantiate_default_store_axiom(enode* store) { SASSERT(is_store(store)); SASSERT(store->get_num_args() >= 3); app* store_app = store->get_owner(); context& ctx = get_context(); ast_manager& m = get_manager(); if (!ctx.add_fingerprint(this, m_default_store_fingerprint, store->get_num_args(), store->get_args())) { return false; } m_stats.m_num_default_store_axiom++; app* def1; app* def2; TRACE("array", tout << mk_bounded_pp(store_app, m) << "\n";); if (has_large_domain(store_app)) { def2 = mk_default(store_app->get_arg(0)); } else { // // let A = store(B, i, v) // // Add: // default(A) = ite(epsilon = i, v, default(B)) // expr_ref_vector eqs(m); unsigned num_args = store_app->get_num_args(); for (unsigned i = 1; i + 1 < num_args; ++i) { sort* srt = m.get_sort(store_app->get_arg(i)); app* ep = mk_epsilon(srt); eqs.push_back(m.mk_eq(ep, store_app->get_arg(i))); } expr_ref eq(m); eq = mk_and(eqs); expr* defA = mk_default(store_app->get_arg(0)); def2 = m.mk_ite(eq, store_app->get_arg(num_args-1), defA); } def1 = mk_default(store_app); ctx.internalize(def1, false); ctx.internalize(def2, false); return try_assign_eq(def1, def2); } app* theory_array_full::mk_epsilon(sort* s) { app* eps = nullptr; if (m_sort2epsilon.find(s, eps)) { return eps; } eps = get_manager().mk_fresh_const("epsilon", s); m_trail_stack.push( ast2ast_trail(m_sort2epsilon, s, eps)); return eps; } final_check_status theory_array_full::assert_delayed_axioms() { final_check_status r = FC_DONE; if (!m_params.m_array_delay_exp_axiom) { r = FC_DONE; } else { r = theory_array::assert_delayed_axioms(); unsigned num_vars = get_num_vars(); for (unsigned v = 0; v < num_vars; v++) { var_data * d = m_var_data[v]; if (d->m_prop_upward && instantiate_axiom_map_for(v)) r = FC_CONTINUE; if (d->m_prop_upward) { if (m_params.m_array_weak) { found_unsupported_op(v); } else if (instantiate_parent_stores_default(v)) r = FC_CONTINUE; } } } while (!m_eqsv.empty()) { literal eq = m_eqsv.back(); m_eqsv.pop_back(); get_context().mark_as_relevant(eq); assert_axiom(eq); r = FC_CONTINUE; } if (r == FC_DONE && m_bapa) { r = m_bapa->final_check(); } if (r == FC_DONE && m_found_unsupported_op) r = FC_GIVEUP; return r; } bool theory_array_full::instantiate_parent_stores_default(theory_var v) { SASSERT(v != null_theory_var); TRACE("array", tout << "v" << v << "\n";); v = find(v); var_data* d = m_var_data[v]; bool result = false; for (enode * store : d->m_parent_stores) { SASSERT(is_store(store)); if ((!m_params.m_array_cg || store->is_cgr()) && instantiate_default_store_axiom(store)) { result = true; } } return result; } bool theory_array_full::try_assign_eq(expr* v1, expr* v2) { TRACE("array", tout << mk_bounded_pp(v1, get_manager()) << "\n==\n" << mk_bounded_pp(v2, get_manager()) << "\n";); if (m_eqs.contains(v1, v2)) { return false; } else { m_eqs.insert(v1, v2, true); literal eq(mk_eq(v1, v2, true)); if (get_manager().has_trace_stream()) log_axiom_instantiation(get_context().bool_var2expr(eq.var())); get_context().mark_as_relevant(eq); assert_axiom(eq); if (get_manager().has_trace_stream()) get_manager().trace_stream() << "[end-of-instance]\n"; // m_eqsv.push_back(eq); return true; } } void theory_array_full::pop_scope_eh(unsigned num_scopes) { unsigned num_old_vars = get_old_num_vars(num_scopes); theory_array::pop_scope_eh(num_scopes); std::for_each(m_var_data_full.begin() + num_old_vars, m_var_data_full.end(), delete_proc()); m_var_data_full.shrink(num_old_vars); m_eqs.reset(); m_eqsv.reset(); } void theory_array_full::collect_statistics(::statistics & st) const { theory_array::collect_statistics(st); st.update("array map ax", m_stats.m_num_map_axiom); st.update("array def const", m_stats.m_num_default_const_axiom); st.update("array sel const", m_stats.m_num_select_const_axiom); st.update("array def store", m_stats.m_num_default_store_axiom); st.update("array def as-array", m_stats.m_num_default_as_array_axiom); st.update("array sel as-array", m_stats.m_num_select_as_array_axiom); } } z3-z3-4.8.7/src/smt/theory_array_full.h000066400000000000000000000070651356505360400177340ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_array_full.h Abstract: Author: Nikolaj Bjorner 2008-22-10 Revision History: --*/ #ifndef THEORY_ARRAY_FULL_H_ #define THEORY_ARRAY_FULL_H_ #include "smt/theory_array.h" #include "ast/ast_trail.h" namespace smt { class theory_array_full : public theory_array { struct var_data_full { ptr_vector m_maps; ptr_vector m_consts; ptr_vector m_as_arrays; ptr_vector m_parent_maps; var_data_full() {} }; ptr_vector m_var_data_full; ast2ast_trailmap m_sort2epsilon; obj_pair_map m_eqs; svector m_eqsv; static unsigned const m_default_map_fingerprint = UINT_MAX - 112; static unsigned const m_default_store_fingerprint = UINT_MAX - 113; static unsigned const m_default_const_fingerprint = UINT_MAX - 115; static unsigned const m_default_as_array_fingerprint = UINT_MAX - 116; protected: //virtual final_check_status final_check_eh(); void reset_eh() override; void set_prop_upward(theory_var v) override; void set_prop_upward(enode* n) override; void set_prop_upward(theory_var v, var_data* d) override; unsigned get_lambda_equiv_size(theory_var v, var_data* d) override; bool internalize_term(app * term) override; bool internalize_atom(app * atom, bool gate_ctx) override; void pop_scope_eh(unsigned num_scopes) override; theory_var mk_var(enode * n) override; void relevant_eh(app * n) override; bool should_research(expr_ref_vector & unsat_core) override; void add_theory_assumptions(expr_ref_vector & assumptions) override; void add_const(theory_var v, enode* c); void add_map(theory_var v, enode* s); void add_parent_map(theory_var v, enode* s); void add_as_array(theory_var v, enode* arr); void add_parent_select(theory_var v, enode * s) override; void add_parent_default(theory_var v); final_check_status assert_delayed_axioms() override; bool instantiate_default_const_axiom(enode* cnst); bool instantiate_default_store_axiom(enode* store); bool instantiate_default_map_axiom(enode* map); bool instantiate_default_as_array_axiom(enode* arr); bool instantiate_parent_stores_default(theory_var v); bool has_large_domain(app* array_term); app* mk_epsilon(sort* s); bool instantiate_select_const_axiom(enode* select, enode* cnst); bool instantiate_select_as_array_axiom(enode* select, enode* arr); bool instantiate_select_map_axiom(enode* select, enode* map); bool instantiate_axiom_map_for(theory_var v); bool try_assign_eq(expr* n1, expr* n2); void assign_eqs(); public: theory_array_full(ast_manager & m, theory_array_params & params); ~theory_array_full() override; theory * mk_fresh(context * new_ctx) override; void merge_eh(theory_var v1, theory_var v2, theory_var, theory_var) override; void display_var(std::ostream & out, theory_var v) const override; void collect_statistics(::statistics & st) const override; void init(context* ctx) override { // the parent class is theory_array. // theory::init(ctx); theory_array::init(ctx); } }; }; #endif /* THEORY_ARRAY_H_ */ z3-z3-4.8.7/src/smt/theory_bv.cpp000066400000000000000000002342431356505360400165360ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_bv.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-06-06. Revision History: --*/ #include "smt/smt_context.h" #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 { void theory_bv::init(context * ctx) { theory::init(ctx); } theory_var theory_bv::mk_var(enode * n) { theory_var r = theory::mk_var(n); m_find.mk_var(); m_bits.push_back(literal_vector()); m_wpos.push_back(0); m_zero_one_bits.push_back(zero_one_bits()); get_context().attach_th_var(n, this, r); return r; } app * theory_bv::mk_bit2bool(app * bv, unsigned idx) { parameter p(idx); expr * args[1] = {bv}; return get_manager().mk_app(get_id(), OP_BIT2BOOL, 1, &p, 1, args); } void theory_bv::mk_bits(theory_var v) { enode * n = get_enode(v); app * owner = n->get_owner(); 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); ctx.internalize(bit, true); bool_var b = ctx.get_bool_var(bit); bits.push_back(literal(b)); } } class mk_atom_trail : public trail { bool_var m_var; public: mk_atom_trail(bool_var v):m_var(v) {} void undo(theory_bv & th) override { theory_bv::atom * a = th.get_bv2a(m_var); a->~atom(); th.erase_bv2a(m_var); } }; void theory_bv::mk_bit2bool(app * n) { 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)) { // This may happen if bit2bool(x) is in a conflict // clause that is being reinitialized, and x was not reinitialized // yet. // So, we internalize x (i.e., arg) ctx.internalize(first_arg, false); SASSERT(ctx.e_internalized(first_arg)); // In most cases, when x is internalized, its bits are created. // They are created because x is a bit-vector operation or apply_sort_cnstr is invoked. // However, there is an exception. The method apply_sort_cnstr is not invoked for ite-terms. // So, I execute get_var on the enode attached to first_arg. // This will force a theory variable to be created if it does not already exist. // 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); } enode * arg = ctx.get_enode(first_arg); // The argument was already internalized, but it may not have a theory variable associated with it. // For example, for ite-terms the method apply_sort_cnstr is not invoked. // See comment in the then-branch. theory_var v_arg = arg->get_th_var(get_id()); if (v_arg == null_theory_var) { // The method get_var will create a theory variable for arg. // As a side-effect the bits for arg will also be created. get_var(arg); SASSERT(ctx.b_internalized(n)); } else if (!ctx.b_internalized(n)) { SASSERT(v_arg != null_theory_var); bool_var bv = ctx.mk_bool_var(n); ctx.set_var_theory(bv, get_id()); bit_atom * a = new (get_region()) bit_atom(); insert_bv2a(bv, a); m_trail_stack.push(mk_atom_trail(bv)); unsigned idx = n->get_decl()->get_parameter(0).get_int(); SASSERT(a->m_occs == 0); a->m_occs = new (get_region()) var_pos_occ(v_arg, idx); #if 0 // possible fix for #2182, but effect of fix needs to be checked. if (idx < m_bits[v_arg].size()) { //std::cout << mk_pp(n, get_manager()) << "\n"; ctx.mk_th_axiom(get_id(), m_bits[v_arg][idx], literal(bv, true)); ctx.mk_th_axiom(get_id(), ~m_bits[v_arg][idx], literal(bv, false)); } #endif } // 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) { context & ctx = get_context(); for (expr* arg : *n) { ctx.internalize(arg, false); } } enode * theory_bv::mk_enode(app * n) { context & ctx = get_context(); enode * e; if (ctx.e_internalized(n)) { e = ctx.get_enode(n); } else { e = ctx.mk_enode(n, !m_params.m_bv_reflect, false, m_params.m_bv_cc); mk_var(e); } SASSERT(e->get_th_var(get_id()) != null_theory_var); return e; } theory_var theory_bv::get_var(enode * n) { theory_var v = n->get_th_var(get_id()); if (v == null_theory_var) { v = mk_var(n); mk_bits(v); } return v; } enode * theory_bv::get_arg(enode * n, unsigned idx) { if (m_params.m_bv_reflect) { return n->get_arg(idx); } else { context & ctx = get_context(); app * arg = to_app(n->get_owner()->get_arg(idx)); SASSERT(ctx.e_internalized(arg)); return ctx.get_enode(arg); } } inline theory_var theory_bv::get_arg_var(enode * n, unsigned idx) { return get_var(get_arg(n, idx)); } void theory_bv::get_bits(theory_var v, expr_ref_vector & r) { context & ctx = get_context(); literal_vector & bits = m_bits[v]; for (literal lit : bits) { expr_ref l(get_manager()); ctx.literal2expr(lit, l); r.push_back(l); } } inline void theory_bv::get_bits(enode * n, expr_ref_vector & r) { get_bits(get_var(n), r); } inline void theory_bv::get_arg_bits(enode * n, unsigned idx, expr_ref_vector & r) { get_bits(get_arg_var(n, idx), r); } inline void theory_bv::get_arg_bits(app * n, unsigned idx, expr_ref_vector & r) { context & ctx = get_context(); app * arg = to_app(n->get_arg(idx)); SASSERT(ctx.e_internalized(arg)); get_bits(ctx.get_enode(arg), r); } class add_var_pos_trail : public trail { theory_bv::bit_atom * m_atom; public: add_var_pos_trail(theory_bv::bit_atom * a):m_atom(a) {} void undo(theory_bv & th) override { SASSERT(m_atom->m_occs); m_atom->m_occs = m_atom->m_occs->m_next; } }; /** \brief v1[idx] = ~v2[idx], then v1 /= v2 is a theory axiom. */ void theory_bv::mk_new_diseq_axiom(theory_var v1, theory_var v2, unsigned idx) { SASSERT(m_bits[v1][idx] == ~m_bits[v2][idx]); TRACE("bv_diseq_axiom", tout << "found new diseq axiom\n"; display_var(tout, v1); display_var(tout, v2);); // found new disequality m_stats.m_num_diseq_static++; enode * e1 = get_enode(v1); enode * e2 = get_enode(v2); literal l = ~(mk_eq(e1->get_owner(), e2->get_owner(), true)); context & ctx = get_context(); ast_manager & m = get_manager(); expr * eq = ctx.bool_var2expr(l.var()); if (m.has_trace_stream()) { app_ref body(m); body = m.mk_implies(m.mk_eq(mk_bit2bool(get_enode(v1)->get_owner(), idx), m.mk_not(mk_bit2bool(get_enode(v2)->get_owner(), idx))), m.mk_not(eq)); log_axiom_instantiation(body); } ctx.mk_th_axiom(get_id(), 1, &l); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; if (ctx.relevancy()) { relevancy_eh * eh = ctx.mk_relevancy_eh(pair_relevancy_eh(e1->get_owner(), e2->get_owner(), eq)); ctx.add_relevancy_eh(e1->get_owner(), eh); ctx.add_relevancy_eh(e2->get_owner(), eh); } } void theory_bv::register_true_false_bit(theory_var v, unsigned idx) { SASSERT(m_bits[v][idx] == true_literal || m_bits[v][idx] == false_literal); bool is_true = (m_bits[v][idx] == true_literal); zero_one_bits & bits = m_zero_one_bits[v]; bits.push_back(zero_one_bit(v, idx, is_true)); } /** \brief v[idx] = ~v'[idx], then v /= v' is a theory axiom. */ void theory_bv::find_new_diseq_axioms(var_pos_occ * occs, theory_var v, unsigned idx) { literal l = m_bits[v][idx]; l.neg(); while (occs) { theory_var v2 = occs->m_var; unsigned idx2 = occs->m_idx; if (idx == idx2 && m_bits[v2][idx2] == l && get_bv_size(v2) == get_bv_size(v)) mk_new_diseq_axiom(v, v2, idx); occs = occs->m_next; } } /** \brief Add bit l to the given variable. */ void theory_bv::add_bit(theory_var v, literal l) { context & ctx = get_context(); literal_vector & bits = m_bits[v]; unsigned idx = bits.size(); bits.push_back(l); if (l.var() == true_bool_var) { register_true_false_bit(v, idx); } else { theory_id th_id = ctx.get_var_theory(l.var()); if (th_id == get_id()) { atom * a = get_bv2a(l.var()); SASSERT(a && a->is_bit()); bit_atom * b = static_cast(a); find_new_diseq_axioms(b->m_occs, v, idx); m_trail_stack.push(add_var_pos_trail(b)); b->m_occs = new (get_region()) var_pos_occ(v, idx, b->m_occs); } else { SASSERT(th_id == null_theory_id); ctx.set_var_theory(l.var(), get_id()); SASSERT(ctx.get_var_theory(l.var()) == get_id()); bit_atom * b = new (get_region()) bit_atom(); insert_bv2a(l.var(), b); m_trail_stack.push(mk_atom_trail(l.var())); SASSERT(b->m_occs == 0); b->m_occs = new (get_region()) var_pos_occ(v, idx); } } } void theory_bv::simplify_bit(expr * s, expr_ref & r) { // proof_ref p(get_manager()); // if (get_context().at_base_level()) // ctx.get_rewriter()(s, r, p); // else r = s; } void theory_bv::init_bits(enode * n, expr_ref_vector const & bits) { context & ctx = get_context(); ast_manager & m = get_manager(); theory_var v = n->get_th_var(get_id()); SASSERT(v != null_theory_var); unsigned sz = bits.size(); SASSERT(get_bv_size(n) == sz); m_bits[v].reset(); for (unsigned i = 0; i < sz; i++) { expr * bit = bits.get(i); expr_ref s_bit(m); simplify_bit(bit, s_bit); ctx.internalize(s_bit, true); literal l = ctx.get_literal(s_bit.get()); TRACE("init_bits", tout << "bit " << i << " of #" << n->get_owner_id() << "\n" << mk_ll_pp(s_bit, m) << "\n";); add_bit(v, l); } find_wpos(v); } /** \brief Find an unassigned bit for m_wpos[v], if such bit cannot be found invoke fixed_var_eh */ void theory_bv::find_wpos(theory_var v) { context & ctx = get_context(); literal_vector const & bits = m_bits[v]; unsigned sz = bits.size(); unsigned & wpos = m_wpos[v]; unsigned init = wpos; for (; wpos < sz; wpos++) { TRACE("find_wpos", tout << "curr bit: " << bits[wpos] << "\n";); if (ctx.get_assignment(bits[wpos]) == l_undef) { TRACE("find_wpos", tout << "moved wpos of v" << v << " to " << wpos << "\n";); return; } } wpos = 0; for (; wpos < init; wpos++) { if (ctx.get_assignment(bits[wpos]) == l_undef) { TRACE("find_wpos", tout << "moved wpos of v" << v << " to " << wpos << "\n";); return; } } TRACE("find_wpos", tout << "v" << v << " is a fixed variable.\n";); fixed_var_eh(v); } class fixed_eq_justification : public justification { theory_bv & m_th; theory_var m_var1; theory_var m_var2; void mark_bits(conflict_resolution & cr, literal_vector const & bits) { context & ctx = cr.get_context(); for (literal lit : bits) { if (lit.var() != true_bool_var) { if (ctx.get_assignment(lit) == l_true) cr.mark_literal(lit); else cr.mark_literal(~lit); } } } void get_proof(conflict_resolution & cr, literal l, ptr_buffer & prs, bool & visited) { if (l.var() == true_bool_var) return; proof * pr = nullptr; if (cr.get_context().get_assignment(l) == l_true) pr = cr.get_proof(l); else pr = cr.get_proof(~l); if (pr) prs.push_back(pr); else visited = false; } public: fixed_eq_justification(theory_bv & th, theory_var v1, theory_var v2): m_th(th), m_var1(v1), m_var2(v2) { } void get_antecedents(conflict_resolution & cr) override { mark_bits(cr, m_th.m_bits[m_var1]); mark_bits(cr, m_th.m_bits[m_var2]); } proof * mk_proof(conflict_resolution & cr) override { ptr_buffer prs; context & ctx = cr.get_context(); bool visited = true; literal_vector const & bits1 = m_th.m_bits[m_var1]; literal_vector const & bits2 = m_th.m_bits[m_var2]; literal_vector::const_iterator it1 = bits1.begin(); literal_vector::const_iterator it2 = bits2.begin(); literal_vector::const_iterator end1 = bits1.end(); for (; it1 != end1; ++it1, ++it2) { get_proof(cr, *it1, prs, visited); get_proof(cr, *it2, prs, visited); } if (!visited) return nullptr; expr * fact = ctx.mk_eq_atom(m_th.get_enode(m_var1)->get_owner(), m_th.get_enode(m_var2)->get_owner()); ast_manager & m = ctx.get_manager(); return m.mk_th_lemma(get_from_theory(), fact, prs.size(), prs.c_ptr()); } theory_id get_from_theory() const override { return m_th.get_id(); } char const * get_name() const override { return "bv-fixed-eq"; } }; void theory_bv::add_fixed_eq(theory_var v1, theory_var v2) { if (v1 > v2) { std::swap(v1, v2); } 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";); literal_vector eqs; for (unsigned i = 0; i < sz; ++i) { literal l1 = m_bits[v1][i]; literal l2 = m_bits[v2][i]; expr_ref e1(m), e2(m); e1 = mk_bit2bool(o1, i); e2 = mk_bit2bool(o2, i); literal eq = mk_eq(e1, e2, true); if (m.has_trace_stream()) { app_ref body(m); body = m.mk_implies(m.mk_not(ctx.bool_var2expr(eq.var())), m.mk_not(ctx.bool_var2expr(oeq.var()))); log_axiom_instantiation(body); } ctx.mk_th_axiom(get_id(), l1, ~l2, ~eq); ctx.mk_th_axiom(get_id(), ~l1, l2, ~eq); ctx.mk_th_axiom(get_id(), l1, l2, eq); ctx.mk_th_axiom(get_id(), ~l1, ~l2, eq); ctx.mk_th_axiom(get_id(), eq, ~oeq); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; eqs.push_back(~eq); } eqs.push_back(oeq); ctx.mk_th_axiom(get_id(), eqs.size(), eqs.c_ptr()); } void theory_bv::fixed_var_eh(theory_var v) { numeral val; VERIFY(get_fixed_value(v, val)); unsigned sz = get_bv_size(v); value_sort_pair key(val, sz); theory_var v2; if (m_fixed_var_table.find(key, v2)) { numeral val2; if (v2 < static_cast(get_num_vars()) && is_bv(v2) && get_bv_size(v2) == sz && get_fixed_value(v2, val2) && val == val2) { if (get_enode(v)->get_root() != get_enode(v2)->get_root()) { SASSERT(get_bv_size(v) == get_bv_size(v2)); context & ctx = get_context(); TRACE("fixed_var_eh", tout << "detected equality: v" << v << " = v" << v2 << "\n"; display_var(tout, v); display_var(tout, v2);); m_stats.m_num_th2core_eq++; add_fixed_eq(v, v2); justification * js = ctx.mk_justification(fixed_eq_justification(*this, v, v2)); ctx.assign_eq(get_enode(v), get_enode(v2), eq_justification(js)); m_fixed_var_table.insert(key, v2); } } else { // the original fixed variable v2 was deleted or it is not fixed anymore. m_fixed_var_table.erase(key); m_fixed_var_table.insert(key, v); } } else { m_fixed_var_table.insert(key, v); } } bool theory_bv::get_fixed_value(theory_var v, numeral & result) const { context & ctx = get_context(); result.reset(); literal_vector const & bits = m_bits[v]; literal_vector::const_iterator it = bits.begin(); literal_vector::const_iterator end = bits.end(); for (unsigned i = 0; it != end; ++it, ++i) { switch (ctx.get_assignment(*it)) { case l_false: break; case l_undef: return false; case l_true: result += m_bb.power(i); break; } } return true; } bool theory_bv::get_fixed_value(app* x, numeral & result) const { context& ctx = get_context(); if (!ctx.e_internalized(x)) return false; enode * e = ctx.get_enode(x); theory_var v = e->get_th_var(get_id()); return get_fixed_value(v, result); } void theory_bv::internalize_num(app * n) { SASSERT(!get_context().e_internalized(n)); ast_manager & m = get_manager(); numeral val; unsigned sz; m_util.is_numeral(n, val, sz); enode * e = mk_enode(n); // internalizer is marking enodes as interpreted whenever the associated ast is a value and a constant. // e->mark_as_interpreted(); theory_var v = e->get_th_var(get_id()); expr_ref_vector bits(m); m_bb.num2bits(val, sz, bits); SASSERT(bits.size() == sz); literal_vector & c_bits = m_bits[v]; for (unsigned i = 0; i < sz; i++) { expr * l = bits.get(i); if (m.is_true(l)) { c_bits.push_back(true_literal); } else { SASSERT(m.is_false(l)); c_bits.push_back(false_literal); } register_true_false_bit(v, i); } fixed_var_eh(v); } void theory_bv::internalize_mkbv(app* n) { ast_manager& m = get_manager(); expr_ref_vector bits(m); process_args(n); enode * e = mk_enode(n); bits.append(n->get_num_args(), n->get_args()); init_bits(e, bits); } void theory_bv::internalize_bv2int(app* n) { SASSERT(!get_context().e_internalized(n)); context& ctx = get_context(); TRACE("bv", tout << mk_bounded_pp(n, get_manager()) << "\n";); process_args(n); mk_enode(n); if (!ctx.relevancy()) { assert_bv2int_axiom(n); } } void theory_bv::assert_bv2int_axiom(app * n) { // // create the axiom: // n = bv2int(k) = ite(bit2bool(k[sz-1],2^{sz-1},0) + ... + ite(bit2bool(k[0],1,0)) // SASSERT(get_context().e_internalized(n)); SASSERT(m_util.is_bv2int(n)); ast_manager & m = get_manager(); TRACE("bv2int_bug", tout << "bv2int:\n" << mk_pp(n, m) << "\n";); context & ctx = get_context(); sort * int_sort = m.get_sort(n); app * k = to_app(n->get_arg(0)); SASSERT(m_util.is_bv_sort(m.get_sort(k))); expr_ref_vector k_bits(m); enode * k_enode = mk_enode(k); get_bits(k_enode, k_bits); unsigned sz = m_util.get_bv_size(k); expr_ref_vector args(m); expr_ref zero(m_autil.mk_numeral(numeral(0), int_sort), m); numeral num(1); for (unsigned i = 0; i < sz; ++i) { // Remark: A previous version of this method was using // // expr* b = mk_bit2bool(k,i); // // This is not correct. The predicate bit2bool is an // internal construct, and it was not meant for building // axioms directly. It is used to represent the bits of a // constant, and in some cases the bits of a complicated // bit-vector expression. In most cases, the bits of a // composite bit-vector expression T are just boolean // combinations of bit2bool atoms of the bit-vector // constants contained in T. So, instead of using // mk_bit2bool to access a particular bit of T, we should // use the method get_bits. // expr * b = k_bits.get(i); expr_ref n(m_autil.mk_numeral(num, int_sort), m); args.push_back(m.mk_ite(b, n, zero)); num *= numeral(2); } expr_ref sum(m_autil.mk_add(sz, args.c_ptr()), m); th_rewriter rw(m); rw(sum); literal l(mk_eq(n, sum, false)); TRACE("bv", tout << mk_pp(n, m) << "\n"; tout << mk_pp(sum, m) << "\n"; ctx.display_literal_verbose(tout, l); tout << "\n"; ); ctx.mark_as_relevant(l); if (m.has_trace_stream()) log_axiom_instantiation(ctx.bool_var2expr(l.var())); ctx.mk_th_axiom(get_id(), 1, &l); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; } void theory_bv::internalize_int2bv(app* n) { SASSERT(!get_context().e_internalized(n)); SASSERT(n->get_num_args() == 1); context& ctx = get_context(); process_args(n); mk_enode(n); theory_var v = ctx.get_enode(n)->get_th_var(get_id()); mk_bits(v); if (!ctx.relevancy()) { assert_int2bv_axiom(n); } } void theory_bv::assert_int2bv_axiom(app* n) { // // create the axiom: // bv2int(n) = e mod 2^bit_width // where n = int2bv(e) // // Create the axioms: // bit2bool(i,n) == ((e div 2^i) mod 2 != 0) // for i = 0,.., sz-1 // SASSERT(get_context().e_internalized(n)); SASSERT(m_util.is_int2bv(n)); ast_manager & m = get_manager(); context& ctx = get_context(); parameter param(m_autil.mk_int()); expr* n_expr = n; expr* e = n->get_arg(0); expr_ref lhs(m), rhs(m); lhs = m.mk_app(get_id(), OP_BV2INT, 1, ¶m, 1, &n_expr); unsigned sz = m_util.get_bv_size(n); numeral mod = power(numeral(2), sz); rhs = m_autil.mk_mod(e, m_autil.mk_numeral(mod, true)); literal l(mk_eq(lhs, rhs, false)); ctx.mark_as_relevant(l); if (m.has_trace_stream()) log_axiom_instantiation(ctx.bool_var2expr(l.var())); ctx.mk_th_axiom(get_id(), 1, &l); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; TRACE("bv", tout << mk_pp(lhs, m) << " == \n"; tout << mk_pp(rhs, m) << "\n"; ); expr_ref_vector n_bits(m); enode * n_enode = mk_enode(n); get_bits(n_enode, n_bits); for (unsigned i = 0; i < sz; ++i) { numeral div = power(numeral(2), i); mod = numeral(2); rhs = m_autil.mk_idiv(e, m_autil.mk_numeral(div,true)); rhs = m_autil.mk_mod(rhs, m_autil.mk_numeral(mod, true)); rhs = m.mk_eq(rhs, m_autil.mk_numeral(rational(1), true)); lhs = n_bits.get(i); TRACE("bv", tout << mk_pp(lhs, m) << " == " << mk_pp(rhs, m) << "\n";); l = literal(mk_eq(lhs, rhs, false)); ctx.mark_as_relevant(l); if (m.has_trace_stream()) log_axiom_instantiation(ctx.bool_var2expr(l.var())); ctx.mk_th_axiom(get_id(), 1, &l); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; } } #define MK_UNARY(NAME, BLAST_OP) \ void theory_bv::NAME(app * n) { \ SASSERT(!get_context().e_internalized(n)); \ SASSERT(n->get_num_args() == 1); \ process_args(n); \ ast_manager & m = get_manager(); \ enode * e = mk_enode(n); \ expr_ref_vector arg1_bits(m), bits(m); \ get_arg_bits(e, 0, arg1_bits); \ m_bb.BLAST_OP(arg1_bits.size(), arg1_bits.c_ptr(), bits); \ init_bits(e, bits); \ } #define MK_BINARY(NAME, BLAST_OP) \ void theory_bv::NAME(app * n) { \ SASSERT(!get_context().e_internalized(n)); \ SASSERT(n->get_num_args() == 2); \ process_args(n); \ ast_manager & m = get_manager(); \ enode * e = mk_enode(n); \ expr_ref_vector arg1_bits(m), arg2_bits(m), bits(m); \ get_arg_bits(e, 0, arg1_bits); \ get_arg_bits(e, 1, arg2_bits); \ SASSERT(arg1_bits.size() == arg2_bits.size()); \ m_bb.BLAST_OP(arg1_bits.size(), arg1_bits.c_ptr(), arg2_bits.c_ptr(), bits); \ init_bits(e, bits); \ } #define MK_AC_BINARY(NAME, BLAST_OP) \ void theory_bv::NAME(app * n) { \ SASSERT(!get_context().e_internalized(n)); \ SASSERT(n->get_num_args() >= 2); \ process_args(n); \ ast_manager & m = get_manager(); \ enode * e = mk_enode(n); \ expr_ref_vector arg_bits(m); \ expr_ref_vector bits(m); \ expr_ref_vector new_bits(m); \ unsigned i = n->get_num_args(); \ --i; \ get_arg_bits(e, i, bits); \ while (i > 0) { \ --i; \ arg_bits.reset(); \ get_arg_bits(e, i, arg_bits); \ SASSERT(arg_bits.size() == bits.size()); \ new_bits.reset(); \ m_bb.BLAST_OP(arg_bits.size(), arg_bits.c_ptr(), bits.c_ptr(), new_bits); \ bits.swap(new_bits); \ } \ init_bits(e, bits); \ TRACE("bv_verbose", tout << arg_bits << " " << bits << " " << new_bits << "\n";); \ } #define MK_BINARY_COND(NAME, BLAST_OP) \ void theory_bv::NAME(app * n) { \ SASSERT(!get_context().e_internalized(n)); \ SASSERT(n->get_num_args() == 2); \ process_args(n); \ ast_manager & m = get_manager(); \ context& ctx = get_context(); \ enode * e = mk_enode(n); \ expr_ref_vector arg1_bits(m), arg2_bits(m), bits(m); \ expr_ref cond(m), s_cond(m); \ get_arg_bits(e, 0, arg1_bits); \ get_arg_bits(e, 1, arg2_bits); \ SASSERT(arg1_bits.size() == arg2_bits.size()); \ m_bb.BLAST_OP(arg1_bits.size(), arg1_bits.c_ptr(), arg2_bits.c_ptr(), bits, cond); \ init_bits(e, bits); \ simplify_bit(cond, s_cond); \ ctx.internalize(s_cond, true); \ literal l(ctx.get_literal(s_cond)); \ ctx.mark_as_relevant(l); \ ctx.mk_th_axiom(get_id(), 1, &l); \ TRACE("bv", tout << mk_pp(cond, get_manager()) << "\n"; tout << l << "\n";); \ } void theory_bv::internalize_sub(app *n) { SASSERT(!get_context().e_internalized(n)); SASSERT(n->get_num_args() == 2); process_args(n); ast_manager & m = get_manager(); enode * e = mk_enode(n); expr_ref_vector arg1_bits(m), arg2_bits(m), bits(m); get_arg_bits(e, 0, arg1_bits); get_arg_bits(e, 1, arg2_bits); SASSERT(arg1_bits.size() == arg2_bits.size()); expr_ref carry(m); m_bb.mk_subtracter(arg1_bits.size(), arg1_bits.c_ptr(), arg2_bits.c_ptr(), bits, carry); init_bits(e, bits); } MK_UNARY(internalize_not, mk_not); MK_UNARY(internalize_redand, mk_redand); MK_UNARY(internalize_redor, mk_redor); MK_AC_BINARY(internalize_add, mk_adder); MK_AC_BINARY(internalize_mul, mk_multiplier); MK_BINARY(internalize_udiv, mk_udiv); MK_BINARY(internalize_sdiv, mk_sdiv); MK_BINARY(internalize_urem, mk_urem); MK_BINARY(internalize_srem, mk_srem); MK_BINARY(internalize_smod, mk_smod); MK_BINARY(internalize_shl, mk_shl); MK_BINARY(internalize_lshr, mk_lshr); MK_BINARY(internalize_ashr, mk_ashr); MK_BINARY(internalize_ext_rotate_left, mk_ext_rotate_left); MK_BINARY(internalize_ext_rotate_right, mk_ext_rotate_right); MK_AC_BINARY(internalize_and, mk_and); MK_AC_BINARY(internalize_or, mk_or); MK_AC_BINARY(internalize_xor, mk_xor); MK_AC_BINARY(internalize_nand, mk_nand); MK_AC_BINARY(internalize_nor, mk_nor); MK_AC_BINARY(internalize_xnor, mk_xnor); MK_BINARY(internalize_comp, mk_comp); #define MK_PARAMETRIC_UNARY(NAME, BLAST_OP) \ void theory_bv::NAME(app * n) { \ SASSERT(!get_context().e_internalized(n)); \ SASSERT(n->get_num_args() == 1); \ process_args(n); \ ast_manager & m = get_manager(); \ enode * e = mk_enode(n); \ expr_ref_vector arg1_bits(m), bits(m); \ get_arg_bits(e, 0, arg1_bits); \ unsigned param = n->get_decl()->get_parameter(0).get_int(); \ m_bb.BLAST_OP(arg1_bits.size(), arg1_bits.c_ptr(), param, bits); \ init_bits(e, bits); \ } MK_PARAMETRIC_UNARY(internalize_sign_extend, mk_sign_extend); MK_PARAMETRIC_UNARY(internalize_zero_extend, mk_zero_extend); MK_PARAMETRIC_UNARY(internalize_rotate_left, mk_rotate_left); MK_PARAMETRIC_UNARY(internalize_rotate_right, mk_rotate_right); void theory_bv::internalize_concat(app * n) { process_args(n); enode * e = mk_enode(n); theory_var v = e->get_th_var(get_id()); unsigned num_args = n->get_num_args(); unsigned i = num_args; m_bits[v].reset(); while (i > 0) { i--; theory_var arg = get_arg_var(e, i); for (literal lit : m_bits[arg]) { add_bit(v, lit); } } find_wpos(v); } void theory_bv::internalize_extract(app * n) { SASSERT(n->get_num_args() == 1); process_args(n); enode * e = mk_enode(n); theory_var v = e->get_th_var(get_id()); theory_var arg = get_arg_var(e, 0); unsigned start = n->get_decl()->get_parameter(1).get_int(); unsigned end = n->get_decl()->get_parameter(0).get_int(); SASSERT(start <= end); literal_vector & arg_bits = m_bits[arg]; m_bits[v].reset(); for (unsigned i = start; i <= end; ++i) add_bit(v, arg_bits[i]); find_wpos(v); } bool theory_bv::internalize_term(app * term) { scoped_suspend_rlimit _suspend_cancel(get_manager().limit()); try { SASSERT(term->get_family_id() == get_family_id()); TRACE("bv", tout << "internalizing term: " << mk_bounded_pp(term, get_manager()) << "\n";); if (approximate_term(term)) { return false; } switch (term->get_decl_kind()) { case OP_BV_NUM: internalize_num(term); return true; case OP_BADD: internalize_add(term); return true; case OP_BSUB: internalize_sub(term); return true; case OP_BMUL: internalize_mul(term); return true; case OP_BSDIV_I: internalize_sdiv(term); return true; case OP_BUDIV_I: internalize_udiv(term); return true; case OP_BSREM_I: internalize_srem(term); return true; case OP_BUREM_I: internalize_urem(term); return true; case OP_BSMOD_I: internalize_smod(term); return true; case OP_BAND: internalize_and(term); return true; case OP_BOR: internalize_or(term); return true; case OP_BNOT: internalize_not(term); return true; case OP_BXOR: internalize_xor(term); return true; case OP_BNAND: internalize_nand(term); return true; case OP_BNOR: internalize_nor(term); return true; case OP_BXNOR: internalize_xnor(term); return true; case OP_CONCAT: internalize_concat(term); return true; case OP_SIGN_EXT: internalize_sign_extend(term); return true; case OP_ZERO_EXT: internalize_zero_extend(term); return true; case OP_EXTRACT: internalize_extract(term); return true; case OP_BREDOR: internalize_redor(term); return true; case OP_BREDAND: internalize_redand(term); return true; case OP_BCOMP: internalize_comp(term); return true; case OP_BSHL: internalize_shl(term); return true; case OP_BLSHR: internalize_lshr(term); return true; case OP_BASHR: internalize_ashr(term); return true; case OP_ROTATE_LEFT: internalize_rotate_left(term); return true; case OP_ROTATE_RIGHT: internalize_rotate_right(term); return true; case OP_EXT_ROTATE_LEFT: internalize_ext_rotate_left(term); return true; case OP_EXT_ROTATE_RIGHT: internalize_ext_rotate_right(term); return true; case OP_BSDIV0: return false; case OP_BUDIV0: return false; case OP_BSREM0: return false; case OP_BUREM0: return false; case OP_BSMOD0: return false; case OP_MKBV: internalize_mkbv(term); return true; case OP_INT2BV: if (m_params.m_bv_enable_int2bv2int) { internalize_int2bv(term); } return m_params.m_bv_enable_int2bv2int; case OP_BV2INT: if (m_params.m_bv_enable_int2bv2int) { internalize_bv2int(term); } return m_params.m_bv_enable_int2bv2int; default: TRACE("bv_op", tout << "unsupported operator: " << mk_ll_pp(term, get_manager()) << "\n";); UNREACHABLE(); return false; } } catch (z3_exception& ex) { IF_VERBOSE(1, verbose_stream() << "internalize_term: " << ex.msg() << "\n";); throw; } } #define MK_NO_OVFL(NAME, OP) \ void theory_bv::NAME(app *n) { \ SASSERT(n->get_num_args() == 2); \ process_args(n); \ ast_manager & m = get_manager(); \ context & ctx = get_context(); \ expr_ref_vector arg1_bits(m), arg2_bits(m); \ get_arg_bits(n, 0, arg1_bits); \ get_arg_bits(n, 1, arg2_bits); \ expr_ref out(m); \ m_bb.OP(arg1_bits.size(), arg1_bits.c_ptr(), arg2_bits.c_ptr(), out); \ expr_ref s_out(m); \ simplify_bit(out, s_out); \ ctx.internalize(s_out, true); \ literal def = ctx.get_literal(s_out); \ literal l(ctx.mk_bool_var(n)); \ ctx.set_var_theory(l.var(), get_id()); \ le_atom * a = new (get_region()) le_atom(l, def); /* abuse le_atom */ \ insert_bv2a(l.var(), a); \ m_trail_stack.push(mk_atom_trail(l.var())); \ /* smul_no_overflow and umul_no_overflow are using the le_atom (THIS IS A BIG HACK)... */ \ /* the connection between the l and def was never realized when */ \ /* relevancy() is true and m_bv_lazy_le is false (the default configuration). */ \ /* So, we need to check also the m_bv_lazy_le flag here. */ \ /* Maybe, we should rename the le_atom to bridge_atom, and m_bv_lazy_le option to m_bv_lazy_bridge. */ \ if (!ctx.relevancy() || !m_params.m_bv_lazy_le) { \ ctx.mk_th_axiom(get_id(), l, ~def); \ ctx.mk_th_axiom(get_id(), ~l, def); \ } \ } MK_NO_OVFL(internalize_umul_no_overflow, mk_umul_no_overflow); MK_NO_OVFL(internalize_smul_no_overflow, mk_smul_no_overflow); MK_NO_OVFL(internalize_smul_no_underflow, mk_smul_no_underflow); template void theory_bv::internalize_le(app * n) { SASSERT(n->get_num_args() == 2); process_args(n); ast_manager & m = get_manager(); context & ctx = get_context(); expr_ref_vector arg1_bits(m), arg2_bits(m); get_arg_bits(n, 0, arg1_bits); get_arg_bits(n, 1, arg2_bits); expr_ref le(m); if (Signed) m_bb.mk_sle(arg1_bits.size(), arg1_bits.c_ptr(), arg2_bits.c_ptr(), le); else m_bb.mk_ule(arg1_bits.size(), arg1_bits.c_ptr(), arg2_bits.c_ptr(), le); expr_ref s_le(m); simplify_bit(le, s_le); ctx.internalize(s_le, true); literal def = ctx.get_literal(s_le); literal l(ctx.mk_bool_var(n)); ctx.set_var_theory(l.var(), get_id()); le_atom * a = new (get_region()) le_atom(l, def); insert_bv2a(l.var(), a); m_trail_stack.push(mk_atom_trail(l.var())); if (!ctx.relevancy() || !m_params.m_bv_lazy_le) { ctx.mk_th_axiom(get_id(), l, ~def); ctx.mk_th_axiom(get_id(), ~l, def); } } bool theory_bv::internalize_carry(app * n, bool gate_ctx) { context & ctx = get_context(); ctx.internalize(n->get_arg(0), true); ctx.internalize(n->get_arg(1), true); ctx.internalize(n->get_arg(2), true); bool is_new_var = false; bool_var v; if (!ctx.b_internalized(n)) { is_new_var = true; v = ctx.mk_bool_var(n); literal r(v); literal l1 = ctx.get_literal(n->get_arg(0)); literal l2 = ctx.get_literal(n->get_arg(1)); literal l3 = ctx.get_literal(n->get_arg(2)); ctx.mk_gate_clause(~r, l1, l2); ctx.mk_gate_clause(~r, l1, l3); ctx.mk_gate_clause(~r, l2, l3); ctx.mk_gate_clause( r, ~l1, ~l2); ctx.mk_gate_clause( r, ~l1, ~l3); ctx.mk_gate_clause( r, ~l2, ~l3); } else { v = ctx.get_bool_var(n); } if (!ctx.e_internalized(n) && !gate_ctx) { bool suppress_args = true; bool merge_tf = !gate_ctx; ctx.mk_enode(n, suppress_args, merge_tf, true); ctx.set_enode_flag(v, is_new_var); } return true; } bool theory_bv::internalize_xor3(app * n, bool gate_ctx) { context & ctx = get_context(); ctx.internalize(n->get_arg(0), true); ctx.internalize(n->get_arg(1), true); ctx.internalize(n->get_arg(2), true); bool is_new_var = false; bool_var v; if (!ctx.b_internalized(n)) { is_new_var = true; v = ctx.mk_bool_var(n); literal r(v); literal l1 = ctx.get_literal(n->get_arg(0)); literal l2 = ctx.get_literal(n->get_arg(1)); literal l3 = ctx.get_literal(n->get_arg(2)); ctx.mk_gate_clause(~r, l1, l2, l3); ctx.mk_gate_clause(~r, ~l1, ~l2, l3); ctx.mk_gate_clause(~r, ~l1, l2, ~l3); ctx.mk_gate_clause(~r, l1, ~l2, ~l3); ctx.mk_gate_clause( r, ~l1, l2, l3); ctx.mk_gate_clause( r, l1, ~l2, l3); ctx.mk_gate_clause( r, l1, l2, ~l3); ctx.mk_gate_clause( r, ~l1, ~l2, ~l3); } else { v = ctx.get_bool_var(n); } if (!ctx.e_internalized(n) && !gate_ctx) { bool suppress_args = true; bool merge_tf = !gate_ctx; ctx.mk_enode(n, suppress_args, merge_tf, true); ctx.set_enode_flag(v, is_new_var); } return true; } bool theory_bv::internalize_atom(app * atom, bool gate_ctx) { TRACE("bv", tout << "internalizing atom: " << mk_bounded_pp(atom, get_manager()) << "\n";); SASSERT(atom->get_family_id() == get_family_id()); if (approximate_term(atom)) { return false; } switch (atom->get_decl_kind()) { case OP_BIT2BOOL: mk_bit2bool(atom); return true; case OP_ULEQ: internalize_le(atom); return true; case OP_SLEQ: internalize_le(atom); return true; case OP_XOR3: return internalize_xor3(atom, gate_ctx); case OP_CARRY: return internalize_carry(atom, gate_ctx); case OP_BUMUL_NO_OVFL: internalize_umul_no_overflow(atom); return true; case OP_BSMUL_NO_OVFL: internalize_smul_no_overflow(atom); return true; case OP_BSMUL_NO_UDFL: internalize_smul_no_underflow(atom); return true; default: UNREACHABLE(); return false; } } // // Determine whether bit-vector expression should be approximated // based on the number of bits used by the arguments. // bool theory_bv::approximate_term(app* n) { if (m_params.m_bv_blast_max_size == INT_MAX) { return false; } unsigned num_args = n->get_num_args(); 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); 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";); get_context().push_trail(value_trail(m_approximates_large_bvs)); m_approximates_large_bvs = true; } return true; } } return false; } void theory_bv::apply_sort_cnstr(enode * n, sort * s) { if (!is_attached_to_var(n) && !approximate_term(n->get_owner())) { mk_bits(mk_var(n)); } } void theory_bv::new_eq_eh(theory_var v1, theory_var v2) { TRACE("bv_eq", tout << "new_eq: " << mk_pp(get_enode(v1)->get_owner(), get_manager()) << " = " << mk_pp(get_enode(v2)->get_owner(), get_manager()) << "\n";); TRACE("bv", tout << "new_eq_eh v" << v1 << " = v" << v2 << " relevant1: " << get_context().is_relevant(get_enode(v1)) << " relevant2: " << get_context().is_relevant(get_enode(v2)) << "\n";); m_find.merge(v1, v2); } void theory_bv::new_diseq_eh(theory_var v1, theory_var v2) { if (is_bv(v1)) { SASSERT(m_bits[v1].size() == m_bits[v2].size()); expand_diseq(v1, v2); } } void theory_bv::expand_diseq(theory_var v1, theory_var v2) { SASSERT(get_bv_size(v1) == get_bv_size(v2)); context & ctx = get_context(); ast_manager & m = get_manager(); 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(); literal_vector const & bits2 = m_bits[v2]; literal_vector::const_iterator it2 = bits2.begin(); for (; it1 != end1; ++it1, ++it2) { if (*it1 == ~(*it2)) 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; expr_ref_vector exprs(m); 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) { expr_ref l1(m), l2(m), diff(m); ctx.literal2expr(*it1, l1); ctx.literal2expr(*it2, l2); m_bb.mk_xor(l1, l2, diff); ctx.internalize(diff, true); literal arg = ctx.get_literal(diff); lits.push_back(arg); exprs.push_back(diff); } m_stats.m_num_diseq_dynamic++; if (m.has_trace_stream()) { app_ref body(m); body = m.mk_implies(m.mk_not(ctx.bool_var2expr(eq.var())), m.mk_or(exprs.size(), exprs.c_ptr())); log_axiom_instantiation(body); } ctx.mk_th_axiom(get_id(), lits.size(), lits.c_ptr()); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; } void theory_bv::assign_eh(bool_var v, bool is_true) { atom * a = get_bv2a(v); TRACE("bv", tout << "assert: v" << v << " #" << get_context().bool_var2expr(v)->get_id() << " is_true: " << is_true << "\n";); if (a->is_bit()) { // The following optimization is not correct. // Boolean variables created for performing bit-blasting are reused. // See regression\trevor6.smt for example. // // if (ctx.has_th_justification(v, get_id())) { // TRACE("bv", tout << "has th_justification\n";); // return; // } m_prop_queue.reset(); bit_atom * b = static_cast(a); var_pos_occ * curr = b->m_occs; while (curr) { m_prop_queue.push_back(var_pos(curr->m_var, curr->m_idx)); curr = curr->m_next; } 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 } } void theory_bv::propagate_bits() { context & ctx = get_context(); for (unsigned i = 0; i < m_prop_queue.size(); i++) { var_pos const & entry = m_prop_queue[i]; theory_var v = entry.first; unsigned idx = entry.second; if (m_wpos[v] == idx) find_wpos(v); literal_vector & bits = m_bits[v]; literal bit = bits[idx]; lbool val = ctx.get_assignment(bit); if (val == l_undef) { continue; } theory_var v2 = next(v); TRACE("bv_bit_prop", tout << "propagating #" << get_enode(v)->get_owner_id() << "[" << idx << "] = " << val << "\n";); literal antecedent = bit; if (val == l_false) { antecedent.neg(); } while (v2 != v) { literal_vector & bits2 = m_bits[v2]; literal bit2 = bits2[idx]; 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; if (val == l_false) { consequent.neg(); } assign_bit(consequent, v, v2, idx, antecedent, false); if (ctx.inconsistent()) { TRACE("bv_bit_prop", tout << "inconsistent " << bit << " " << bit2 << "\n";); m_prop_queue.reset(); return; } } v2 = next(v2); } } m_prop_queue.reset(); TRACE("bv_bit_prop", tout << "done propagating\n";); } void theory_bv::assign_bit(literal consequent, theory_var v1, theory_var v2, unsigned idx, literal antecedent, bool propagate_eqc) { m_stats.m_num_bit2core++; context & ctx = get_context(); ast_manager & m = get_manager(); SASSERT(ctx.get_assignment(antecedent) == l_true); SASSERT(m_bits[v2][idx].var() == consequent.var()); SASSERT(consequent.var() != antecedent.var()); TRACE("bv_bit_prop", tout << "assigning: "; ctx.display_literal(tout, consequent); tout << " using "; ctx.display_literal(tout, antecedent); tout << " #" << get_enode(v1)->get_owner_id() << " #" << get_enode(v2)->get_owner_id() << " idx: " << idx << "\n"; tout << "propagate_eqc: " << propagate_eqc << "\n";); if (consequent == false_literal) { m_stats.m_num_conflicts++; ctx.set_conflict(mk_bit_eq_justification(v1, v2, consequent, antecedent)); } else { ctx.assign(consequent, mk_bit_eq_justification(v1, v2, consequent, antecedent)); literal_vector lits; lits.push_back(~consequent); lits.push_back(antecedent); literal eq = mk_eq(get_enode(v1)->get_owner(), get_enode(v2)->get_owner(), false); lits.push_back(~eq); if (m.has_trace_stream()) { app_ref body(m); body = m.mk_implies(ctx.bool_var2expr(eq.var()), m.mk_implies(ctx.bool_var2expr(consequent.var()), ctx.bool_var2expr(antecedent.var()))); log_axiom_instantiation(body); } ctx.mk_th_axiom(get_id(), lits.size(), lits.c_ptr()); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; if (m_wpos[v2] == idx) find_wpos(v2); // REMARK: bit_eq_justification is marked as a theory_bv justification. // Thus, the assignment to consequent will not be notified back to the theory. // So, we need to propagate the assignment to other bits. bool_var bv = consequent.var(); atom * a = get_bv2a(bv); SASSERT(a->is_bit()); bit_atom * b = static_cast(a); var_pos_occ * curr = b->m_occs; while (curr) { TRACE("assign_bit_bug", tout << "curr->m_var: v" << curr->m_var << ", curr->m_idx: " << curr->m_idx << ", v2: v" << v2 << ", idx: " << idx << "\n"; tout << "find(curr->m_var): v" << find(curr->m_var) << ", find(v2): v" << find(v2) << "\n"; tout << "is bit of #" << get_enode(curr->m_var)->get_owner_id() << "\n"; ); // If find(curr->m_var) == find(v2) && curr->m_idx == idx and propagate_eqc == false, then // this bit will be propagated to the equivalence class of v2 by assign_bit caller. if (propagate_eqc || find(curr->m_var) != find(v2) || curr->m_idx != idx) m_prop_queue.push_back(var_pos(curr->m_var, curr->m_idx)); curr = curr->m_next; } } } void theory_bv::relevant_eh(app * n) { ast_manager & m = get_manager(); context & ctx = get_context(); TRACE("bv", tout << "relevant: " << mk_pp(n, m) << "\n";); if (m.is_bool(n)) { bool_var v = ctx.get_bool_var(n); atom * a = get_bv2a(v); if (a && !a->is_bit()) { le_atom * le = static_cast(a); ctx.mark_as_relevant(le->m_def); if (m_params.m_bv_lazy_le) { ctx.mk_th_axiom(get_id(), le->m_var, ~le->m_def); ctx.mk_th_axiom(get_id(), ~le->m_var, le->m_def); } } } else if (m_params.m_bv_enable_int2bv2int && m_util.is_bv2int(n)) { ctx.mark_as_relevant(n->get_arg(0)); assert_bv2int_axiom(n); } else if (m_params.m_bv_enable_int2bv2int && m_util.is_int2bv(n)) { ctx.mark_as_relevant(n->get_arg(0)); assert_int2bv_axiom(n); } else if (ctx.e_internalized(n)) { enode * e = ctx.get_enode(n); theory_var v = e->get_th_var(get_id()); if (v != null_theory_var) { literal_vector & bits = m_bits[v]; for (literal lit : bits) { ctx.mark_as_relevant(lit); } } } } 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) { TRACE("bv",tout << num_scopes << "\n";); m_trail_stack.pop_scope(num_scopes); unsigned num_old_vars = get_old_num_vars(num_scopes); 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); } final_check_status theory_bv::final_check_eh() { SASSERT(check_invariant()); if (m_approximates_large_bvs) { return FC_GIVEUP; } return FC_DONE; } void theory_bv::reset_eh() { pop_scope_eh(m_trail_stack.get_num_scopes()); m_bool_var2atom.reset(); m_fixed_var_table.reset(); theory::reset_eh(); } bool theory_bv::include_func_interp(func_decl* f) { SASSERT(f->get_family_id() == get_family_id()); switch (f->get_decl_kind()) { case OP_BSDIV0: case OP_BUDIV0: case OP_BSREM0: case OP_BUREM0: case OP_BSMOD0: return true; default: return false; } return false; } theory_bv::theory_bv(ast_manager & m, theory_bv_params const & params, bit_blaster_params const & bb_params): theory(m.mk_family_id("bv")), m_params(params), m_util(m), m_autil(m), m_bb(m, bb_params), 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() { } theory* theory_bv::mk_fresh(context* new_ctx) { return alloc(theory_bv, new_ctx->get_manager(), m_params, m_bb.get_params()); } void theory_bv::merge_eh(theory_var r1, theory_var r2, theory_var v1, theory_var v2) { TRACE("bv", tout << "merging: #" << get_enode(v1)->get_owner_id() << " #" << get_enode(v2)->get_owner_id() << "\n";); TRACE("bv_bit_prop", tout << "merging: #" << get_enode(v1)->get_owner_id() << " #" << get_enode(v2)->get_owner_id() << "\n";); if (!merge_zero_one_bits(r1, r2)) { TRACE("bv", tout << "conflict detected\n";); return; // conflict was detected } m_prop_queue.reset(); context & ctx = get_context(); literal_vector & bits1 = m_bits[v1]; literal_vector & bits2 = m_bits[v2]; SASSERT(bits1.size() == bits2.size()); unsigned sz = bits1.size(); bool changed; TRACE("bv", tout << "bits size: " << sz << "\n";); do { // This outerloop is necessary to avoid missing propagation steps. // For example, let's assume that bits1 and bits2 contains the following // sequence of bits: // b4 b3 b2 b1 // b5 b4 b3 b2 // Let's also assume that b1 is assigned, and b2, b3, b4, and b5 are not. // Only the propagation from b1 to b2 is performed by the first iteration of this // loop. // // In the worst case, we need to execute this loop bits1.size() times. // // Remark: the assignment to b2 is marked as a bv theory propagation, // then it is not notified to the bv theory. changed = false; for (unsigned idx = 0; idx < sz; idx++) { literal bit1 = bits1[idx]; literal bit2 = bits2[idx]; CTRACE("bv_bug", bit1 == ~bit2, display_var(tout, v1); display_var(tout, v2); tout << "idx: " << idx << "\n";); SASSERT(bit1 != ~bit2); lbool val1 = ctx.get_assignment(bit1); lbool val2 = ctx.get_assignment(bit2); if (val1 == val2) continue; changed = true; if (val1 != l_undef && val2 != l_undef) { TRACE("bv", tout << "inconsistent "; display_var(tout, v1); display_var(tout, v2); tout << "idx: " << idx << "\n";); } if (val1 != l_undef) { literal antecedent = bit1; literal consequent = bit2; if (val1 == l_false) { consequent.neg(); antecedent.neg(); } assign_bit(consequent, v1, v2, idx, antecedent, true); } else if (val2 != l_undef) { literal antecedent = bit2; literal consequent = bit1; if (val2 == l_false) { consequent.neg(); antecedent.neg(); } assign_bit(consequent, v2, v1, idx, antecedent, true); } if (ctx.inconsistent()) return; if (val1 != l_undef && val2 != l_undef && val1 != val2) { UNREACHABLE(); } } } while(changed); propagate_bits(); } bool theory_bv::merge_zero_one_bits(theory_var r1, theory_var r2) { zero_one_bits & bits2 = m_zero_one_bits[r2]; if (bits2.empty()) return true; zero_one_bits & bits1 = m_zero_one_bits[r1]; unsigned bv_size = get_bv_size(r1); SASSERT(bv_size == get_bv_size(r2)); m_merge_aux[0].reserve(bv_size+1, null_theory_var); m_merge_aux[1].reserve(bv_size+1, null_theory_var); #define RESET_MERGET_AUX() for (auto & zo : bits1) m_merge_aux[zo.m_is_true][zo.m_idx] = null_theory_var; DEBUG_CODE(for (unsigned i = 0; i < bv_size; i++) { SASSERT(m_merge_aux[0][i] == null_theory_var || m_merge_aux[1][i] == null_theory_var); }); // save info about bits1 for (auto & zo : bits1) m_merge_aux[zo.m_is_true][zo.m_idx] = zo.m_owner; // check if bits2 is consistent with bits1, and copy new bits to bits1 for (auto & zo : bits2) { theory_var v2 = zo.m_owner; theory_var v1 = m_merge_aux[!zo.m_is_true][zo.m_idx]; if (v1 != null_theory_var) { // conflict was detected ... v1 and v2 have complementary bits SASSERT(m_bits[v1][zo.m_idx] == ~(m_bits[v2][zo.m_idx])); SASSERT(m_bits[v1].size() == m_bits[v2].size()); mk_new_diseq_axiom(v1, v2, zo.m_idx); RESET_MERGET_AUX(); return false; } if (m_merge_aux[zo.m_is_true][zo.m_idx] == null_theory_var) { // copy missing variable to bits1 bits1.push_back(zo); } } // reset m_merge_aux vector RESET_MERGET_AUX(); DEBUG_CODE(for (unsigned i = 0; i < bv_size; i++) { SASSERT(m_merge_aux[0][i] == null_theory_var || m_merge_aux[1][i] == null_theory_var); }); 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; theory_id m_th_id; // TODO: steal 4 bits from each one of the following literas and use them to represent the th_id. literal m_consequent; literal m_antecedent; public: bit_eq_justification(theory_id th_id, enode * v1, enode * v2, literal c, literal a): m_v1(v1), m_v2(v2), m_th_id(th_id), m_consequent(c), m_antecedent(a) {} void get_antecedents(conflict_resolution & cr) override { cr.mark_eq(m_v1, m_v2); if (m_antecedent.var() != true_bool_var) cr.mark_literal(m_antecedent); } proof * mk_proof(conflict_resolution & cr) override { bool visited = true; ptr_buffer prs; proof * pr = cr.get_proof(m_v1, m_v2); if (pr) prs.push_back(pr); else visited = false; if (m_antecedent.var() != true_bool_var) { proof * pr = cr.get_proof(m_antecedent); if (pr) prs.push_back(pr); else visited = false; } if (!visited) return nullptr; context & ctx = cr.get_context(); ast_manager & m = cr.get_manager(); expr_ref fact(m); ctx.literal2expr(m_consequent, fact); return m.mk_th_lemma(get_from_theory(), fact, prs.size(), prs.c_ptr()); } theory_id get_from_theory() const override { return m_th_id; } char const * get_name() const override { return "bv-bit-eq"; } }; inline justification * theory_bv::mk_bit_eq_justification(theory_var v1, theory_var v2, literal consequent, literal antecedent) { return get_context().mk_justification(bit_eq_justification(get_id(), get_enode(v1), get_enode(v2), consequent, antecedent)); } void theory_bv::unmerge_eh(theory_var v1, theory_var v2) { // v1 was the root of the equivalence class // I must remove the zero_one_bits that are from v2. // REMARK: it is unsafe to invoke check_zero_one_bits, since // the enode associated with v1 and v2 may have already been // deleted. // // The logical context trail_stack is popped before // the theories pop_scope_eh is invoked. zero_one_bits & bits = m_zero_one_bits[v1]; if (bits.empty()) { // SASSERT(check_zero_one_bits(v1)); // SASSERT(check_zero_one_bits(v2)); return; } unsigned j = bits.size(); while (j > 0) { --j; zero_one_bit & bit = bits[j]; if (find(bit.m_owner) == v1) { bits.shrink(j+1); // SASSERT(check_zero_one_bits(v1)); // SASSERT(check_zero_one_bits(v2)); return; } } bits.shrink(0); // SASSERT(check_zero_one_bits(v1)); // SASSERT(check_zero_one_bits(v2)); } void theory_bv::init_model(model_generator & m) { m_factory = alloc(bv_factory, get_manager()); m.register_factory(m_factory); } model_value_proc * theory_bv::mk_value(enode * n, model_generator & mg) { numeral val; theory_var v = n->get_th_var(get_id()); SASSERT(v != null_theory_var); #ifdef Z3DEBUG bool r = #endif get_fixed_value(v, val); SASSERT(r); return alloc(expr_wrapper_proc, m_factory->mk_num_value(val, get_bv_size(v))); } void theory_bv::display_var(std::ostream & out, theory_var v) const { out << "v"; out.width(4); out << std::left << v; out << " #"; out.width(4); out << get_enode(v)->get_owner_id() << " -> #"; out.width(4); out << get_enode(find(v))->get_owner_id(); out << std::right << ", bits:"; context & ctx = get_context(); literal_vector const & bits = m_bits[v]; for (literal lit : bits) { out << " "; ctx.display_literal(out, lit); } numeral val; if (get_fixed_value(v, val)) out << ", value: " << val; out << "\n"; } void theory_bv::display_bit_atom(std::ostream & out, bool_var v, bit_atom const * a) const { context & ctx = get_context(); out << "#" << ctx.bool_var2expr(v)->get_id() << " ->"; var_pos_occ * curr = a->m_occs; while (curr) { out << " #" << get_enode(curr->m_var)->get_owner_id() << "[" << curr->m_idx << "]"; curr = curr->m_next; } out << "\n"; } void theory_bv::display_atoms(std::ostream & out) const { out << "atoms:\n"; context & ctx = get_context(); unsigned num = ctx.get_num_bool_vars(); for (unsigned v = 0; v < num; v++) { atom * a = get_bv2a(v); if (a && a->is_bit()) display_bit_atom(out, v, static_cast(a)); } } void theory_bv::display(std::ostream & out) const { unsigned num_vars = get_num_vars(); if (num_vars == 0) return; out << "Theory bv:\n"; for (unsigned v = 0; v < num_vars; v++) { display_var(out, v); } display_atoms(out); } void theory_bv::collect_statistics(::statistics & st) const { st.update("bv conflicts", m_stats.m_num_conflicts); st.update("bv diseqs", m_stats.m_num_diseq_static); st.update("bv dynamic diseqs", m_stats.m_num_diseq_dynamic); st.update("bv bit2core", m_stats.m_num_bit2core); st.update("bv->core eq", m_stats.m_num_th2core_eq); st.update("bv dynamic eqs", m_stats.m_num_eq_dynamic); } #ifdef Z3DEBUG bool theory_bv::check_assignment(theory_var v) { context & ctx = get_context(); if (!is_root(v)) return true; if (!ctx.is_relevant(get_enode(v))) { return true; } theory_var v2 = v; literal_vector const & bits2 = m_bits[v2]; theory_var v1 = v2; do { literal_vector const & bits1 = m_bits[v1]; SASSERT(bits1.size() == bits2.size()); unsigned sz = bits1.size(); for (unsigned i = 0; i < sz; i++) { literal bit1 = bits1[i]; literal bit2 = bits2[i]; lbool val1 = ctx.get_assignment(bit1); lbool val2 = ctx.get_assignment(bit2); CTRACE("bv_bug", val1 != val2, tout << "equivalence class is inconsistent, i: " << i << "\n"; display_var(tout, v1); display_var(tout, v2); tout << "val1: " << val1 << " lvl: " << ctx.get_assign_level(bit1.var()) << " bit " << bit1 << "\n"; tout << "val2: " << val2 << " lvl: " << ctx.get_assign_level(bit2.var()) << " bit " << bit2 << "\n";); SASSERT(val1 == val2); } SASSERT(ctx.is_relevant(get_enode(v1))); v1 = next(v1); } while (v1 != v); return true; } /** \brief Check whether m_zero_one_bits is an accurate summary of the bits in the equivalence class rooted by v. \remark The method does nothing if v is not the root of the equivalence class. */ bool theory_bv::check_zero_one_bits(theory_var v) { if (get_context().inconsistent()) return true; // property is only valid if the context is not in a conflict. if (is_root(v) && is_bv(v)) { svector bits[2]; unsigned num_bits = 0; unsigned bv_sz = get_bv_size(v); bits[0].resize(bv_sz, false); bits[1].resize(bv_sz, false); theory_var curr = v; do { literal_vector const & lits = m_bits[curr]; for (unsigned i = 0; i < lits.size(); i++) { literal l = lits[i]; if (l.var() == true_bool_var) { unsigned is_true = (l == true_literal); SASSERT(!bits[!is_true][i]); // no complementary bits if (!bits[is_true][i]) { bits[is_true][i] = true; num_bits++; } } } curr = next(curr); } while (curr != v); zero_one_bits const & _bits = m_zero_one_bits[v]; SASSERT(_bits.size() == num_bits); svector already_found; already_found.resize(bv_sz, false); for (auto & zo : _bits) { SASSERT(find(zo.m_owner) == v); SASSERT(bits[zo.m_is_true][zo.m_idx]); SASSERT(!already_found[zo.m_idx]); already_found[zo.m_idx] = true; } } return true; } bool theory_bv::check_invariant() { unsigned num = get_num_vars(); for (unsigned v = 0; v < num; v++) { check_assignment(v); check_zero_one_bits(v); } return true; } #endif }; z3-z3-4.8.7/src/smt/theory_bv.h000066400000000000000000000277121356505360400162040ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_bv.h Abstract: Author: Leonardo de Moura (leonardo) 2008-06-03. Revision History: --*/ #ifndef THEORY_BV_H_ #define THEORY_BV_H_ #include "ast/rewriter/bit_blaster/bit_blaster.h" #include "util/trail.h" #include "util/union_find.h" #include "ast/arith_decl_plugin.h" #include "model/numeral_factory.h" #include "smt/smt_theory.h" #include "smt/params/theory_bv_params.h" namespace smt { struct theory_bv_stats { unsigned m_num_diseq_static, m_num_diseq_dynamic, m_num_bit2core, m_num_th2core_eq, m_num_conflicts; unsigned m_num_eq_dynamic; void reset() { memset(this, 0, sizeof(theory_bv_stats)); } theory_bv_stats() { reset(); } }; class theory_bv : public theory { typedef rational numeral; typedef trail_stack th_trail_stack; typedef union_find th_union_find; typedef std::pair var_pos; class atom { public: virtual ~atom() {} virtual bool is_bit() const = 0; }; struct var_pos_occ { theory_var m_var; unsigned m_idx; var_pos_occ * m_next; var_pos_occ(theory_var v = null_theory_var, unsigned idx = 0, var_pos_occ * next = nullptr):m_var(v), m_idx(idx), m_next(next) {} }; struct bit_atom : public atom { var_pos_occ * m_occs; bit_atom():m_occs(nullptr) {} ~bit_atom() override {} bool is_bit() const override { return true; } }; struct le_atom : public atom { literal m_var; literal m_def; le_atom(literal v, literal d):m_var(v), m_def(d) {} ~le_atom() override {} bool is_bit() const override { return false; } }; /** \brief Structure used to store the position of a bitvector variable that contains the true_literal/false_literal. Remark: the implementation assumes that bitvector variables containing complementary bits are never merged. I assert a disequality (not (= x y)) whenever x and y contain complementary bits. However, this is too expensive when the bit is the true_literal or false_literal. The number of disequalities is too big. To avoid this problem, each equivalence class has a set of its true_literal and false_literal bits in the form of svector. Before merging two classes we just check if the merge is valid by traversing these vectors. */ struct zero_one_bit { theory_var m_owner; //!< variable that owns the bit: useful for backtracking unsigned m_idx:31; unsigned m_is_true:1; zero_one_bit(theory_var v = null_theory_var, unsigned idx = UINT_MAX, bool is_true = false): m_owner(v), m_idx(idx), m_is_true(is_true) {} }; typedef svector zero_one_bits; #ifdef SPARSE_MAP typedef u_map bool_var2atom; void insert_bv2a(bool_var bv, atom * a) { m_bool_var2atom.insert(bv, a); } void erase_bv2a(bool_var bv) { m_bool_var2atom.erase(bv); } atom * get_bv2a(bool_var bv) const { atom * a; m_bool_var2atom.find(bv, a); return a; } #else typedef ptr_vector bool_var2atom; void insert_bv2a(bool_var bv, atom * a) { m_bool_var2atom.setx(bv, a, 0); } void erase_bv2a(bool_var bv) { m_bool_var2atom[bv] = 0; } atom * get_bv2a(bool_var bv) const { return m_bool_var2atom.get(bv, 0); } #endif theory_bv_stats m_stats; theory_bv_params const & m_params; bv_util m_util; arith_util m_autil; bit_blaster m_bb; th_trail_stack m_trail_stack; th_union_find m_find; vector m_bits; // per var, the bits of a given variable. svector m_wpos; // per var, watch position for fixed variable detection. vector m_zero_one_bits; // per var, see comment in the struct zero_one_bit bool_var2atom m_bool_var2atom; typedef svector vars; typedef std::pair value_sort_pair; typedef pair_hash, unsigned_hash> value_sort_pair_hash; typedef map > value2var; 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; theory_var find(theory_var v) const { return m_find.find(v); } theory_var next(theory_var v) const { return m_find.next(v); } bool is_root(theory_var v) const { return m_find.is_root(v); } unsigned get_bv_size(app const * n) const { return m_util.get_bv_size(n); } unsigned get_bv_size(enode const * n) const { return m_util.get_bv_size(n->get_owner()); } unsigned get_bv_size(theory_var v) const { return get_bv_size(get_enode(v)); } bool is_bv(app const* n) const { return m_util.is_bv_sort(get_manager().get_sort(n)); } bool is_bv(enode const* n) const { return is_bv(n->get_owner()); } bool is_bv(theory_var v) const { return is_bv(get_enode(v)); } region & get_region() { return m_trail_stack.get_region(); } bool is_numeral(theory_var v) const { return m_util.is_numeral(get_enode(v)->get_owner()); } app * mk_bit2bool(app * bv, unsigned idx); void mk_bits(theory_var v); friend class mk_atom_trail; void mk_bit2bool(app * n); void process_args(app * n); enode * mk_enode(app * n); theory_var get_var(enode * n); enode * get_arg(enode * n, unsigned idx); theory_var get_arg_var(enode * n, unsigned idx); void get_bits(theory_var v, expr_ref_vector & r); void get_bits(enode * n, expr_ref_vector & r); void get_arg_bits(enode * n, unsigned idx, expr_ref_vector & r); void get_arg_bits(app * n, unsigned idx, expr_ref_vector & r); friend class add_var_pos_trail; void simplify_bit(expr * s, expr_ref & r); void mk_new_diseq_axiom(theory_var v1, theory_var v2, unsigned idx); friend class register_true_false_bit_trail; void register_true_false_bit(theory_var v, unsigned idx); void find_new_diseq_axioms(var_pos_occ * occs, theory_var v, unsigned idx); void add_bit(theory_var v, literal l); void init_bits(enode * n, expr_ref_vector const & bits); void find_wpos(theory_var v); friend class fixed_eq_justification; void fixed_var_eh(theory_var v); void add_fixed_eq(theory_var v1, theory_var v2); bool get_fixed_value(theory_var v, numeral & result) const; void internalize_num(app * n); void internalize_add(app * n); void internalize_sub(app * n); void internalize_mul(app * n); void internalize_udiv(app * n); void internalize_sdiv(app * n); void internalize_urem(app * n); void internalize_srem(app * n); void internalize_smod(app * n); void internalize_shl(app * n); void internalize_lshr(app * n); void internalize_ashr(app * n); void internalize_ext_rotate_left(app * n); void internalize_ext_rotate_right(app * n); void internalize_and(app * n); void internalize_or(app * n); void internalize_not(app * n); void internalize_nand(app * n); void internalize_nor(app * n); void internalize_xor(app * n); void internalize_xnor(app * n); void internalize_concat(app * n); void internalize_sign_extend(app * n); void internalize_zero_extend(app * n); void internalize_extract(app * n); void internalize_redand(app * n); void internalize_redor(app * n); void internalize_comp(app * n); void internalize_rotate_left(app * n); void internalize_rotate_right(app * n); void internalize_bv2int(app* n); void internalize_int2bv(app* n); void internalize_mkbv(app* n); void internalize_umul_no_overflow(app *n); void internalize_smul_no_overflow(app *n); void internalize_smul_no_underflow(app *n); bool approximate_term(app* n); template void internalize_le(app * atom); bool internalize_xor3(app * n, bool gate_ctx); bool internalize_carry(app * n, bool gate_ctx); justification * mk_bit_eq_justification(theory_var v1, theory_var v2, literal consequent, literal antecedent); void propagate_bits(); void assign_bit(literal consequent, theory_var v1, theory_var v2, unsigned idx, literal antecedent, bool propagate_eqc); void assert_int2bv_axiom(app* n); void assert_bv2int_axiom(app* n); protected: void init(context * ctx) override; theory_var mk_var(enode * n) override; bool internalize_atom(app * atom, bool gate_ctx) override; bool internalize_term(app * term) override; void apply_sort_cnstr(enode * n, sort * s) override; void new_eq_eh(theory_var v1, theory_var v2) override; void new_diseq_eh(theory_var v1, theory_var v2) override; virtual void expand_diseq(theory_var v1, theory_var v2); void assign_eh(bool_var v, bool is_true) override; void relevant_eh(app * n) override; void push_scope_eh() override; void pop_scope_eh(unsigned num_scopes) override; final_check_status final_check_eh() override; void reset_eh() override; 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; // ----------------------------------- // // Model generation // // ----------------------------------- bv_factory * m_factory; void init_model(model_generator & m) override; model_value_proc * mk_value(enode * n, model_generator & mg) override; public: theory_bv(ast_manager & m, theory_bv_params const & params, bit_blaster_params const & bb_params); ~theory_bv() override; theory * mk_fresh(context * new_ctx) override; char const * get_name() const override { return "bit-vector"; } th_trail_stack & get_trail_stack() { return m_trail_stack; } void merge_eh(theory_var, theory_var, theory_var v1, theory_var v2); void after_merge_eh(theory_var r1, theory_var r2, theory_var v1, theory_var v2) { SASSERT(check_zero_one_bits(r1)); } void unmerge_eh(theory_var v1, theory_var v2); void display_var(std::ostream & out, theory_var v) const; void display_bit_atom(std::ostream & out, bool_var v, bit_atom const * a) const; void display_atoms(std::ostream & out) const; void display(std::ostream & out) const override; void collect_statistics(::statistics & st) const override; bool get_fixed_value(app* x, numeral & result) const; #ifdef Z3DEBUG bool check_assignment(theory_var v); bool check_invariant(); bool check_zero_one_bits(theory_var v); #endif }; }; #endif /* THEORY_BV_H_ */ z3-z3-4.8.7/src/smt/theory_datatype.cpp000066400000000000000000001220711356505360400177350ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_datatype.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-10-31. Revision History: --*/ #include "util/stats.h" #include "ast/ast_pp.h" #include "ast/ast_ll_pp.h" #include "ast/ast_smt2_pp.h" #include "smt/smt_context.h" #include "smt/theory_datatype.h" #include "smt/theory_array.h" #include "smt/smt_model_generator.h" namespace smt { class dt_eq_justification : public ext_theory_eq_propagation_justification { public: dt_eq_justification(family_id fid, region & r, literal antecedent, enode * lhs, enode * rhs): ext_theory_eq_propagation_justification(fid, r, 1, &antecedent, 0, nullptr, lhs, rhs) { } // Remark: the assignment must be propagated back to the datatype theory. theory_id get_from_theory() const override { return null_theory_id; } }; theory_datatype::final_check_st::final_check_st(theory_datatype * th) : th(th) { SASSERT(th->m_to_unmark.empty()); SASSERT(th->m_to_unmark2.empty()); th->m_used_eqs.reset(); th->m_stack.reset(); th->m_parent.reset(); } theory_datatype::final_check_st::~final_check_st() { unmark_enodes(th->m_to_unmark.size(), th->m_to_unmark.c_ptr()); unmark_enodes2(th->m_to_unmark2.size(), th->m_to_unmark2.c_ptr()); th->m_to_unmark.reset(); th->m_to_unmark2.reset(); th->m_used_eqs.reset(); th->m_stack.reset(); th->m_parent.reset(); } void theory_datatype::oc_mark_on_stack(enode * n) { n = n->get_root(); n->set_mark(); m_to_unmark.push_back(n); } void theory_datatype::oc_mark_cycle_free(enode * n) { n = n->get_root(); n->set_mark2(); m_to_unmark2.push_back(n); } void theory_datatype::oc_push_stack(enode * n) { m_stack.push_back(std::make_pair(EXIT, n)); m_stack.push_back(std::make_pair(ENTER, n)); } theory* theory_datatype::mk_fresh(context* new_ctx) { return alloc(theory_datatype, new_ctx->get_manager(), m_params); } /** \brief Assert the axiom (antecedent => lhs = rhs) antecedent may be null_literal */ void theory_datatype::assert_eq_axiom(enode * lhs, expr * rhs, literal antecedent) { ast_manager & m = get_manager(); context & ctx = get_context(); if (m.proofs_enabled()) { literal l(mk_eq(lhs->get_owner(), rhs, true)); ctx.mark_as_relevant(l); if (antecedent != null_literal) { literal lits[2] = {l, ~antecedent}; ctx.mk_th_axiom(get_id(), 2, lits); } else { literal lits[1] = {l}; ctx.mk_th_axiom(get_id(), 1, lits); } } else { ctx.internalize(rhs, false); TRACE("datatype", tout << "adding axiom:\n" << mk_pp(lhs->get_owner(), m) << "\n=\n" << mk_pp(rhs, m) << "\n";); if (antecedent == null_literal) { ctx.assign_eq(lhs, ctx.get_enode(rhs), eq_justification::mk_axiom()); } else if (ctx.get_assignment(antecedent) != l_true) { literal l(mk_eq(lhs->get_owner(), rhs, true)); ctx.mark_as_relevant(l); ctx.mark_as_relevant(antecedent); literal lits[2] = {l, ~antecedent}; ctx.mk_th_axiom(get_id(), 2, lits); } else { SASSERT(ctx.get_assignment(antecedent) == l_true); region & r = ctx.get_region(); enode * _rhs = ctx.get_enode(rhs); justification * js = ctx.mk_justification(dt_eq_justification(get_id(), r, antecedent, lhs, _rhs)); TRACE("datatype", tout << "assigning... #" << lhs->get_owner_id() << " #" << _rhs->get_owner_id() << "\n"; tout << "v" << lhs->get_th_var(get_id()) << " v" << _rhs->get_th_var(get_id()) << "\n";); TRACE("datatype_detail", display(tout);); ctx.assign_eq(lhs, _rhs, eq_justification(js)); } } } /** \brief Assert the equality (= n (c (acc_1 n) ... (acc_m n))) where where acc_i are the accessors of constructor c. */ void theory_datatype::assert_is_constructor_axiom(enode * n, func_decl * c, literal antecedent) { TRACE("datatype_bug", tout << "creating axiom (= n (c (acc_1 n) ... (acc_m n))) for\n" << mk_pp(n->get_owner(), get_manager()) << "\n";); m_stats.m_assert_cnstr++; SASSERT(m_util.is_constructor(c)); SASSERT(m_util.is_datatype(get_manager().get_sort(n->get_owner()))); ast_manager & m = get_manager(); ptr_vector args; ptr_vector const & accessors = *m_util.get_constructor_accessors(c); SASSERT(c->get_arity() == accessors.size()); for (func_decl * d : accessors) { SASSERT(d->get_arity() == 1); expr * acc = m.mk_app(d, n->get_owner()); args.push_back(acc); } expr_ref mk(m.mk_app(c, args.size(), args.c_ptr()), m); if (m.has_trace_stream()) { app_ref body(m); body = m.mk_eq(n->get_owner(), mk); if (antecedent != null_literal) { body = m.mk_implies(get_context().bool_var2expr(antecedent.var()), body); } log_axiom_instantiation(body, 1, &n); } assert_eq_axiom(n, mk, antecedent); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; } /** \brief Given a constructor n := (c a_1 ... a_m) assert the axioms (= (acc_1 n) a_1) ... (= (acc_m n) a_m) */ void theory_datatype::assert_accessor_axioms(enode * n) { m_stats.m_assert_accessor++; SASSERT(is_constructor(n)); ast_manager & m = get_manager(); func_decl * d = n->get_decl(); ptr_vector const & accessors = *m_util.get_constructor_accessors(d); SASSERT(n->get_num_args() == accessors.size()); app_ref_vector bindings(m); vector> used_enodes; used_enodes.push_back(std::make_tuple(nullptr, n)); for (unsigned i = 0; i < n->get_num_args(); ++i) { bindings.push_back(n->get_arg(i)->get_owner()); } unsigned base_id = get_manager().has_trace_stream() && accessors.size() > 0 ? m_util.get_plugin()->get_axiom_base_id(d->get_name()) : 0; unsigned i = 0; for (func_decl * acc : accessors) { app_ref acc_app(m.mk_app(acc, n->get_owner()), m); enode * arg = n->get_arg(i); if (m.has_trace_stream()) { app_ref body(m); body = m.mk_eq(arg->get_owner(), acc_app); log_axiom_instantiation(body, base_id + 3*i, bindings.size(), bindings.c_ptr(), base_id - 3, used_enodes); } assert_eq_axiom(arg, acc_app, null_literal); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; ++i; } } /** \brief Sign a conflict for r := is_mk(a), c := mk(...), not(r), and c == a. */ void theory_datatype::sign_recognizer_conflict(enode * c, enode * r) { SASSERT(is_constructor(c)); SASSERT(is_recognizer(r)); SASSERT(m_util.get_recognizer_constructor(r->get_decl()) == c->get_decl()); SASSERT(c->get_root() == r->get_arg(0)->get_root()); TRACE("recognizer_conflict", tout << mk_ismt2_pp(c->get_owner(), get_manager()) << "\n" << mk_ismt2_pp(r->get_owner(), get_manager()) << "\n";); context & ctx = get_context(); literal l(ctx.enode2bool_var(r)); SASSERT(ctx.get_assignment(l) == l_false); l.neg(); SASSERT(ctx.get_assignment(l) == l_true); enode_pair p(c, r->get_arg(0)); region & reg = ctx.get_region(); ctx.set_conflict(ctx.mk_justification(ext_theory_conflict_justification(get_id(), reg, 1, &l, 1, &p))); } /** \brief Given a field update n := { r with field := v } for constructor C, assert the axioms: (=> (is-C r) (= (acc_j n) (acc_j r))) for acc_j != field (=> (is-C r) (= (field n) v)) for acc_j != field (=> (not (is-C r)) (= n r)) */ void theory_datatype::assert_update_field_axioms(enode * n) { m_stats.m_assert_update_field++; SASSERT(is_update_field(n)); context & ctx = get_context(); ast_manager & m = get_manager(); app* own = n->get_owner(); expr* arg1 = own->get_arg(0); func_decl * upd = n->get_decl(); func_decl * acc = to_func_decl(upd->get_parameter(0).get_ast()); func_decl * con = m_util.get_accessor_constructor(acc); func_decl * rec = m_util.get_constructor_is(con); ptr_vector const & accessors = *m_util.get_constructor_accessors(con); app_ref rec_app(m.mk_app(rec, arg1), m); ctx.internalize(rec_app, false); literal is_con(ctx.get_bool_var(rec_app)); for (func_decl* acc1 : accessors) { enode* arg; if (acc1 == acc) { arg = n->get_arg(1); } else { app* acc_app = m.mk_app(acc1, arg1); ctx.internalize(acc_app, false); arg = ctx.get_enode(acc_app); } app_ref acc_own(m.mk_app(acc1, own), m); if (m.has_trace_stream()) { app_ref body(m); body = m.mk_implies(rec_app, m.mk_eq(arg->get_owner(), acc_own)); log_axiom_instantiation(body, 1, &n); } assert_eq_axiom(arg, acc_own, is_con); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; } // update_field is identity if 'n' is not created by a matching constructor. app_ref imp(m.mk_implies(m.mk_not(rec_app), m.mk_eq(n->get_owner(), arg1)), m); if (m.has_trace_stream()) log_axiom_instantiation(imp, 1, &n); assert_eq_axiom(n, arg1, ~is_con); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; } theory_var theory_datatype::mk_var(enode * n) { theory_var r = theory::mk_var(n); VERIFY(r == static_cast(m_find.mk_var())); SASSERT(r == static_cast(m_var_data.size())); m_var_data.push_back(alloc(var_data)); var_data * d = m_var_data[r]; context & ctx = get_context(); ctx.attach_th_var(n, this, r); if (is_constructor(n)) { d->m_constructor = n; assert_accessor_axioms(n); } else if (is_update_field(n)) { assert_update_field_axioms(n); } else { ast_manager & m = get_manager(); sort * s = m.get_sort(n->get_owner()); if (m_util.get_datatype_num_constructors(s) == 1) { func_decl * c = m_util.get_datatype_constructors(s)->get(0); assert_is_constructor_axiom(n, c, null_literal); } else { if (m_params.m_dt_lazy_splits == 0 || (m_params.m_dt_lazy_splits == 1 && !s->is_infinite())) mk_split(r); } } return r; } bool theory_datatype::internalize_atom(app * atom, bool gate_ctx) { TRACE("datatype", tout << "internalizing atom:\n" << mk_pp(atom, get_manager()) << "\n";); return internalize_term(atom); } bool theory_datatype::internalize_term(app * term) { TRACE("datatype", tout << "internalizing term:\n" << mk_pp(term, get_manager()) << "\n";); context & ctx = get_context(); unsigned num_args = term->get_num_args(); for (unsigned i = 0; i < num_args; i++) ctx.internalize(term->get_arg(i), false); // the internalization of the arguments may trigger the internalization of term. if (ctx.e_internalized(term)) return true; enode * e = ctx.mk_enode(term, false, get_manager().is_bool(term), true); // possible optimization, the third argument may be set to false, if the term (actually, atom) is not in the context of a gate. if (get_manager().is_bool(term)) { bool_var bv = ctx.mk_bool_var(term); ctx.set_var_theory(bv, get_id()); ctx.set_enode_flag(bv, true); } if (is_constructor(term) || is_update_field(term)) { SASSERT(!is_attached_to_var(e)); // *** We must create a theory variable for each argument that has sort datatype *** // // The apply_sort_cnstr method will not create a theory // variable for an expression N when sort of N has an // infinite number of elements. // // This may create problems during model construction. // For example, suppose we have // x1 = cons(v1, x2) // and x1 and x2 are lists of integers. // This sort has an infinite number of elements. So, in principle, // we do not need a theory variable for x2. // Recall that if an expression is not associated with a // theory variable, then a fresh value is associated with // it. // Moreover, fresh variables of sort S can only be created after the // interpretation for each (relevant) expression of sort S in the // logical context is created. Returning to the example, // to create the interpretation of x1 we need the // interpretation for x2. So, x2 cannot be a fresh value, // since it would have to be created after x1. // for (unsigned i = 0; i < num_args; i++) { enode * arg = e->get_arg(i); sort * s = get_manager().get_sort(arg->get_owner()); if (m_autil.is_array(s) && m_util.is_datatype(get_array_range(s))) { app_ref def(m_autil.mk_default(arg->get_owner()), get_manager()); if (!ctx.e_internalized(def)) { ctx.internalize(def, false); } arg = ctx.get_enode(def); } if (!m_util.is_datatype(s)) continue; if (is_attached_to_var(arg)) continue; mk_var(arg); } mk_var(e); } else { SASSERT(is_accessor(term) || is_recognizer(term)); SASSERT(term->get_num_args() == 1); enode * arg = e->get_arg(0); if (!is_attached_to_var(arg)) mk_var(arg); SASSERT(is_attached_to_var(arg)); } if (is_recognizer(term)) { enode * arg = e->get_arg(0); theory_var v = arg->get_th_var(get_id()); SASSERT(v != null_theory_var); // When relevancy propagation is enabled, the recognizer is only added when it is marked as relevant. if (!ctx.relevancy()) add_recognizer(v, e); } return true; } void theory_datatype::apply_sort_cnstr(enode * n, sort * s) { // Remark: If s is an infinite sort, then it is not necessary to create // a theory variable. // // Actually, when the logical context has quantifiers, it is better to // disable this optimization. // Example: // // (forall (l list) (a Int) (= (len (cons a l)) (+ (len l) 1))) // (assert (> (len a) 1) // // If the theory variable is not created for 'a', then a wrong model will be generated. TRACE("datatype", tout << "apply_sort_cnstr: #" << n->get_owner_id() << " " << mk_pp(n->get_owner(), get_manager()) << "\n";); TRACE("datatype_bug", tout << "apply_sort_cnstr:\n" << mk_pp(n->get_owner(), get_manager()) << " "; tout << m_util.is_datatype(s) << " "; if (m_util.is_datatype(s)) tout << "is-infinite: " << s->is_infinite() << " "; if (m_util.is_datatype(s)) tout << "attached: " << is_attached_to_var(n) << " "; tout << "\n";); if (!is_attached_to_var(n) && (get_context().has_quantifiers() || (m_util.is_datatype(s) && m_util.has_nested_arrays()) || (m_util.is_datatype(s) && !s->is_infinite()))) { mk_var(n); } } void theory_datatype::new_eq_eh(theory_var v1, theory_var v2) { m_find.merge(v1, v2); } bool theory_datatype::use_diseqs() const { return false; } void theory_datatype::new_diseq_eh(theory_var v1, theory_var v2) { UNREACHABLE(); } void theory_datatype::assign_eh(bool_var v, bool is_true) { context & ctx = get_context(); enode * n = ctx.bool_var2enode(v); if (!is_recognizer(n)) return; TRACE("datatype", tout << "assigning recognizer: #" << n->get_owner_id() << " is_true: " << is_true << "\n" << enode_pp(n, ctx) << "\n";); SASSERT(n->get_num_args() == 1); enode * arg = n->get_arg(0); theory_var tv = arg->get_th_var(get_id()); tv = m_find.find(tv); var_data * d = m_var_data[tv]; func_decl * r = n->get_decl(); func_decl * c = m_util.get_recognizer_constructor(r); if (is_true) { SASSERT(tv != null_theory_var); if (d->m_constructor != nullptr && d->m_constructor->get_decl() == c) return; // do nothing assert_is_constructor_axiom(arg, c, literal(v)); } else { if (d->m_constructor != nullptr) { if (d->m_constructor->get_decl() == c) { // conflict sign_recognizer_conflict(d->m_constructor, n); } } else { propagate_recognizer(tv, n); } } } void theory_datatype::relevant_eh(app * n) { context & ctx = get_context(); TRACE("datatype", tout << "relevant_eh: " << mk_pp(n, get_manager()) << "\n";); SASSERT(ctx.relevancy()); if (is_recognizer(n)) { SASSERT(ctx.e_internalized(n)); enode * e = ctx.get_enode(n); theory_var v = e->get_arg(0)->get_th_var(get_id()); SASSERT(v != null_theory_var); add_recognizer(v, e); } } void theory_datatype::push_scope_eh() { theory::push_scope_eh(); m_trail_stack.push_scope(); } void theory_datatype::pop_scope_eh(unsigned num_scopes) { m_trail_stack.pop_scope(num_scopes); unsigned num_old_vars = get_old_num_vars(num_scopes); std::for_each(m_var_data.begin() + num_old_vars, m_var_data.end(), delete_proc()); m_var_data.shrink(num_old_vars); theory::pop_scope_eh(num_scopes); SASSERT(m_find.get_num_vars() == m_var_data.size()); SASSERT(m_find.get_num_vars() == get_num_vars()); } final_check_status theory_datatype::final_check_eh() { int num_vars = get_num_vars(); final_check_status r = FC_DONE; final_check_st _guard(this); // RAII for managing state for (int v = 0; v < num_vars; v++) { if (v == static_cast(m_find.find(v))) { enode * node = get_enode(v); if (!oc_cycle_free(node) && occurs_check(node)) { // conflict was detected... // return... return FC_CONTINUE; } if (m_params.m_dt_lazy_splits > 0) { // using lazy case splits... var_data * d = m_var_data[v]; if (d->m_constructor == nullptr) { mk_split(v); r = FC_CONTINUE; } } } } return r; } // Assuming `app` is equal to a constructor term, return the constructor enode inline enode * theory_datatype::oc_get_cstor(enode * app) { theory_var v = app->get_root()->get_th_var(get_id()); SASSERT(v != null_theory_var); v = m_find.find(v); var_data * d = m_var_data[v]; SASSERT(d->m_constructor); 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; ast_manager& m = get_manager(); 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; } sort * s = m.get_sort(arg->get_owner()); if (m_autil.is_array(s) && m_util.is_datatype(get_array_range(s))) { for (enode* aarg : get_array_args(arg)) { if (aarg->get_root() == child->get_root()) { if (aarg != child) { m_used_eqs.push_back(enode_pair(aarg, 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";); // first: explain that root=v, given that app=cstor(...,v,...) 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* 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) { m_used_eqs.push_back(enode_pair(app, root)); } TRACE("datatype", tout << "occurs_check\n"; for (enode_pair const& p : m_used_eqs) { tout << enode_eq_pp(p, get_context()); }); } // start exploring subgraph below `app` bool theory_datatype::occurs_check_enter(enode * app) { app = app->get_root(); theory_var v = app->get_th_var(get_id()); if (v == null_theory_var) { return false; } v = m_find.find(v); var_data * d = m_var_data[v]; if (!d->m_constructor) { return false; } enode * parent = d->m_constructor; oc_mark_on_stack(parent); for (enode * arg : enode::args(parent)) { if (oc_cycle_free(arg)) { continue; } if (oc_on_stack(arg)) { // arg was explored before app, and is still on the stack: cycle occurs_check_explain(parent, arg); return true; } // explore `arg` (with parent) expr* earg = arg->get_owner(); sort* s = get_manager().get_sort(earg); if (m_util.is_datatype(s)) { m_parent.insert(arg->get_root(), parent); oc_push_stack(arg); } else if (m_autil.is_array(s) && m_util.is_datatype(get_array_range(s))) { for (enode* aarg : get_array_args(arg)) { if (oc_cycle_free(aarg)) { continue; } if (oc_on_stack(aarg)) { occurs_check_explain(parent, aarg); return true; } if (m_util.is_datatype(get_manager().get_sort(aarg->get_owner()))) { m_parent.insert(aarg->get_root(), parent); oc_push_stack(aarg); } } } } return false; } ptr_vector const& theory_datatype::get_array_args(enode* n) { m_array_args.reset(); context& ctx = get_context(); theory_array* th = dynamic_cast(ctx.get_theory(m_autil.get_family_id())); for (enode* p : th->parent_selects(n)) { m_array_args.push_back(p); } app_ref def(m_autil.mk_default(n->get_owner()), get_manager()); m_array_args.push_back(ctx.get_enode(def)); return m_array_args; } /** \brief Check if n can be reached starting from n and following equalities and constructors. For example, occur_check(a1) returns true in the following set of equalities: a1 = cons(v1, a2) a2 = cons(v2, a3) a3 = cons(v3, a1) */ bool theory_datatype::occurs_check(enode * n) { TRACE("datatype", tout << "occurs check: " << enode_pp(n, get_context()) << "\n";); m_stats.m_occurs_check++; bool res = false; oc_push_stack(n); // DFS traversal from `n`. Look at top element and explore it. while (!res && !m_stack.empty()) { stack_op op = m_stack.back().first; enode * app = m_stack.back().second; m_stack.pop_back(); if (oc_cycle_free(app)) continue; TRACE("datatype", tout << "occurs check loop: " << enode_pp(app, get_context()) << (op==ENTER?" enter":" exit")<< "\n";); switch (op) { case ENTER: res = occurs_check_enter(app); break; case EXIT: oc_mark_cycle_free(app); break; } } if (res) { // m_used_eqs should contain conflict context & ctx = get_context(); region & r = ctx.get_region(); ctx.set_conflict(ctx.mk_justification(ext_theory_conflict_justification(get_id(), r, 0, nullptr, m_used_eqs.size(), m_used_eqs.c_ptr()))); } return res; } void theory_datatype::reset_eh() { m_trail_stack.reset(); std::for_each(m_var_data.begin(), m_var_data.end(), delete_proc()); m_var_data.reset(); theory::reset_eh(); m_util.reset(); m_stats.reset(); } bool theory_datatype::is_shared(theory_var v) const { // In principle, parametric theories such as Array Theory and // Datatype Theory need to implement this method. However, the datatype theory // propagates all implied equalities. And, the is_shared method is essentially used // to create interface equalities. So, it is safe to return false. return false; } theory_datatype::theory_datatype(ast_manager & m, theory_datatype_params & p): theory(m.mk_family_id("datatype")), m_params(p), m_util(m), m_autil(m), m_find(*this), m_trail_stack(*this) { } theory_datatype::~theory_datatype() { std::for_each(m_var_data.begin(), m_var_data.end(), delete_proc()); m_var_data.reset(); } void theory_datatype::display(std::ostream & out) const { unsigned num_vars = get_num_vars(); if (num_vars == 0) return; out << "Theory datatype:\n"; for (unsigned v = 0; v < num_vars; v++) display_var(out, v); } void theory_datatype::collect_statistics(::statistics & st) const { st.update("datatype occurs check", m_stats.m_occurs_check); st.update("datatype splits", m_stats.m_splits); st.update("datatype constructor ax", m_stats.m_assert_cnstr); st.update("datatype accessor ax", m_stats.m_assert_accessor); st.update("datatype update ax", m_stats.m_assert_update_field); } void theory_datatype::display_var(std::ostream & out, theory_var v) const { var_data * d = m_var_data[v]; out << "v" << v << " #" << get_enode(v)->get_owner_id() << " -> v" << m_find.find(v) << " "; if (d->m_constructor) out << enode_pp(d->m_constructor, get_context()); else out << "(null)"; out << "\n"; } bool theory_datatype::include_func_interp(func_decl* f) { return false; } void theory_datatype::init_model(model_generator & m) { m_factory = alloc(datatype_factory, get_manager(), m.get_model()); m.register_factory(m_factory); } class datatype_value_proc : public model_value_proc { func_decl * m_constructor; svector m_dependencies; public: datatype_value_proc(func_decl * d):m_constructor(d) {} void add_dependency(enode * n) { m_dependencies.push_back(model_value_dependency(n)); } ~datatype_value_proc() override {} void get_dependencies(buffer & result) override { result.append(m_dependencies.size(), m_dependencies.c_ptr()); } app * mk_value(model_generator & mg, expr_ref_vector const & values) override { SASSERT(values.size() == m_dependencies.size()); return mg.get_manager().mk_app(m_constructor, values.size(), values.c_ptr()); } }; model_value_proc * theory_datatype::mk_value(enode * n, model_generator & mg) { theory_var v = n->get_th_var(get_id()); v = m_find.find(v); SASSERT(v != null_theory_var); var_data * d = m_var_data[v]; SASSERT(d->m_constructor); func_decl * c_decl = d->m_constructor->get_decl(); datatype_value_proc * result = alloc(datatype_value_proc, c_decl); for (enode* arg : enode::args(d->m_constructor)) { result->add_dependency(arg); } TRACE("datatype", tout << mk_pp(n->get_owner(), get_manager()) << "\n"; tout << "depends on\n"; for (enode* arg : enode::args(d->m_constructor)) { tout << " " << mk_pp(arg->get_owner(), get_manager()) << "\n"; }); return result; } void theory_datatype::merge_eh(theory_var v1, theory_var v2, theory_var, theory_var) { // v1 is the new root TRACE("datatype", tout << "merging v" << v1 << " v" << v2 << "\n";); SASSERT(v1 == static_cast(m_find.find(v1))); var_data * d1 = m_var_data[v1]; var_data * d2 = m_var_data[v2]; if (d2->m_constructor != nullptr) { context & ctx = get_context(); if (d1->m_constructor != nullptr && d1->m_constructor->get_decl() != d2->m_constructor->get_decl()) { region & r = ctx.get_region(); enode_pair p(d1->m_constructor, d2->m_constructor); SASSERT(d1->m_constructor->get_root() == d2->m_constructor->get_root()); ctx.set_conflict(ctx.mk_justification(ext_theory_conflict_justification(get_id(), r, 0, nullptr, 1, &p))); } if (d1->m_constructor == nullptr) { m_trail_stack.push(set_ptr_trail(d1->m_constructor)); // check whether there is a recognizer in d1 that conflicts with d2->m_constructor; if (!d1->m_recognizers.empty()) { unsigned c_idx = m_util.get_constructor_idx(d2->m_constructor->get_decl()); enode * recognizer = d1->m_recognizers[c_idx]; if (recognizer != nullptr && ctx.get_assignment(recognizer) == l_false) { sign_recognizer_conflict(d2->m_constructor, recognizer); return; } } d1->m_constructor = d2->m_constructor; } } for (enode* e : d2->m_recognizers) if (e) add_recognizer(v1, e); } void theory_datatype::unmerge_eh(theory_var v1, theory_var v2) { // do nothing } void theory_datatype::add_recognizer(theory_var v, enode * recognizer) { SASSERT(is_recognizer(recognizer)); context & ctx = get_context(); v = m_find.find(v); var_data * d = m_var_data[v]; 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), 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] == 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) { // do nothing... // If recognizer assignment was already processed, then // d->m_constructor is already set. // Otherwise, it will be set when assign_eh is invoked. return; } if (val == l_false && d->m_constructor != nullptr) { func_decl * c_decl = m_util.get_recognizer_constructor(recognizer->get_decl()); if (d->m_constructor->get_decl() == c_decl) { // conflict sign_recognizer_conflict(d->m_constructor, recognizer); } return; } SASSERT(val == l_undef || (val == l_false && d->m_constructor == 0)); d->m_recognizers[c_idx] = recognizer; m_trail_stack.push(set_vector_idx_trail(d->m_recognizers, c_idx)); if (val == l_false) { propagate_recognizer(v, recognizer); } } } /** \brief Propagate a recognizer assigned to false. */ void theory_datatype::propagate_recognizer(theory_var v, enode * recognizer) { SASSERT(is_recognizer(recognizer)); SASSERT(static_cast(m_find.find(v)) == v); context & ctx = get_context(); SASSERT(ctx.get_assignment(recognizer) == l_false); unsigned num_unassigned = 0; unsigned unassigned_idx = UINT_MAX; enode * n = get_enode(v); sort * dt = get_manager().get_sort(n->get_owner()); var_data * d = m_var_data[v]; CTRACE("datatype", d->m_recognizers.empty(), ctx.display(tout);); SASSERT(!d->m_recognizers.empty()); literal_vector lits; enode_pair_vector eqs; ptr_vector::const_iterator it = d->m_recognizers.begin(); ptr_vector::const_iterator end = d->m_recognizers.end(); for (unsigned idx = 0; it != end; ++it, ++idx) { enode * r = *it; if (r && ctx.get_assignment(r) == l_true) return; // nothing to be propagated if (r && ctx.get_assignment(r) == l_false) { SASSERT(r->get_num_args() == 1); lits.push_back(literal(ctx.enode2bool_var(r), true)); if (n != r->get_arg(0)) { // Argument of the current recognizer is not necessarily equal to n. // This can happen when n and r->get_arg(0) are in the same equivalence class. // We must add equality as an assumption to the conflict or propagation SASSERT(n->get_root() == r->get_arg(0)->get_root()); eqs.push_back(enode_pair(n, r->get_arg(0))); } continue; } if (num_unassigned == 0) unassigned_idx = idx; num_unassigned++; } TRACE("datatype", tout << "propagate " << num_unassigned << " eqs: " << eqs.size() << "\n";); if (num_unassigned == 0) { // conflict SASSERT(!lits.empty()); 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) << "\n"; } for (auto const& p : eqs) { tout << enode_eq_pp(p, ctx); }); ctx.set_conflict(ctx.mk_justification(ext_theory_conflict_justification(get_id(), reg, lits.size(), lits.c_ptr(), eqs.size(), eqs.c_ptr()))); } else if (num_unassigned == 1) { // propagate remaining recognizer SASSERT(!lits.empty()); enode * r = d->m_recognizers[unassigned_idx]; literal consequent; if (!r) { ptr_vector const & constructors = *m_util.get_datatype_constructors(dt); func_decl * rec = m_util.get_constructor_is(constructors[unassigned_idx]); app * rec_app = get_manager().mk_app(rec, n->get_owner()); ctx.internalize(rec_app, false); consequent = literal(ctx.get_bool_var(rec_app)); } else { consequent = literal(ctx.enode2bool_var(r)); } ctx.mark_as_relevant(consequent); region & reg = ctx.get_region(); ctx.assign(consequent, ctx.mk_justification(ext_theory_propagation_justification(get_id(), reg, lits.size(), lits.c_ptr(), eqs.size(), eqs.c_ptr(), consequent))); } else { // there are more than 2 unassigned recognizers... // if eager splits are enabled... create new case split if (m_params.m_dt_lazy_splits == 0 || (!dt->is_infinite() && m_params.m_dt_lazy_splits == 1)) mk_split(v); } } /** \brief Create a new case split for v. That is, create the atom (is_mk v) and mark it as relevant. If first is true, it means that v does not have recognizer yet. */ void theory_datatype::mk_split(theory_var v) { context & ctx = get_context(); ast_manager & m = get_manager(); v = m_find.find(v); enode * n = get_enode(v); sort * s = m.get_sort(n->get_owner()); func_decl * non_rec_c = m_util.get_non_rec_constructor(s); unsigned non_rec_idx = m_util.get_constructor_idx(non_rec_c); var_data * d = m_var_data[v]; SASSERT(d->m_constructor == nullptr); func_decl * r = nullptr; m_stats.m_splits++; TRACE("datatype_bug", tout << "non_rec_c: " << non_rec_c->get_name() << " #rec: " << d->m_recognizers.size() << "\n";); if (d->m_recognizers.empty()) { r = m_util.get_constructor_is(non_rec_c); } else { enode * recognizer = d->m_recognizers[non_rec_idx]; if (recognizer == nullptr) { r = m_util.get_constructor_is(non_rec_c); } else if (!ctx.is_relevant(recognizer)) { ctx.mark_as_relevant(recognizer); return; } else if (ctx.get_assignment(recognizer) != l_false) { // if is l_true, then we are done // otherwise wait recognizer to be assigned. return; } else { // look for a slot of d->m_recognizers that is 0, or it is not marked as relevant and is unassigned. ptr_vector::const_iterator it = d->m_recognizers.begin(); ptr_vector::const_iterator end = d->m_recognizers.end(); for (unsigned idx = 0; it != end; ++it, ++idx) { enode * curr = *it; if (curr == nullptr) { ptr_vector const & constructors = *m_util.get_datatype_constructors(s); // found empty slot... r = m_util.get_constructor_is(constructors[idx]); break; } else if (!ctx.is_relevant(curr)) { ctx.mark_as_relevant(curr); return; } else if (ctx.get_assignment(curr) != l_false) { return; } } if (r == nullptr) return; // all recognizers are asserted to false... conflict will be detected... } } SASSERT(r != nullptr); app * r_app = m.mk_app(r, n->get_owner()); TRACE("datatype", tout << "creating split: " << mk_pp(r_app, m) << "\n";); ctx.internalize(r_app, false); bool_var bv = ctx.get_bool_var(r_app); ctx.set_true_first_flag(bv); ctx.mark_as_relevant(bv); } }; z3-z3-4.8.7/src/smt/theory_datatype.h000066400000000000000000000133751356505360400174100ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_datatype.h Abstract: Author: Leonardo de Moura (leonardo) 2008-10-31. Revision History: --*/ #ifndef THEORY_DATATYPE_H_ #define THEORY_DATATYPE_H_ #include "util/union_find.h" #include "ast/array_decl_plugin.h" #include "ast/datatype_decl_plugin.h" #include "model/datatype_factory.h" #include "smt/smt_theory.h" #include "smt/params/theory_datatype_params.h" namespace smt { class theory_datatype : public theory { typedef trail_stack th_trail_stack; typedef union_find th_union_find; struct var_data { ptr_vector m_recognizers; //!< recognizers of this equivalence class that are being watched. enode * m_constructor; //!< constructor of this equivalence class, 0 if there is no constructor in the eqc. var_data(): m_constructor(nullptr) { } }; struct stats { unsigned m_occurs_check, m_splits; unsigned m_assert_cnstr, m_assert_accessor, m_assert_update_field; void reset() { memset(this, 0, sizeof(stats)); } stats() { reset(); } }; theory_datatype_params & m_params; datatype_util m_util; array_util m_autil; ptr_vector m_var_data; th_union_find m_find; th_trail_stack m_trail_stack; datatype_factory * m_factory; stats m_stats; bool is_constructor(app * f) const { return m_util.is_constructor(f); } bool is_recognizer(app * f) const { return m_util.is_recognizer(f); } bool is_accessor(app * f) const { return m_util.is_accessor(f); } bool is_update_field(app * f) const { return m_util.is_update_field(f); } bool is_constructor(enode * n) const { return is_constructor(n->get_owner()); } bool is_recognizer(enode * n) const { return is_recognizer(n->get_owner()); } bool is_accessor(enode * n) const { return is_accessor(n->get_owner()); } bool is_update_field(enode * n) const { return m_util.is_update_field(n->get_owner()); } void assert_eq_axiom(enode * lhs, expr * rhs, literal antecedent); void assert_is_constructor_axiom(enode * n, func_decl * c, literal antecedent); void assert_accessor_axioms(enode * n); void assert_update_field_axioms(enode * n); void add_recognizer(theory_var v, enode * recognizer); void propagate_recognizer(theory_var v, enode * r); void sign_recognizer_conflict(enode * c, enode * r); typedef enum { ENTER, EXIT } stack_op; typedef obj_map parent_tbl; typedef std::pair stack_entry; ptr_vector m_to_unmark; ptr_vector m_to_unmark2; enode_pair_vector m_used_eqs; // conflict, if any parent_tbl m_parent; // parent explanation for occurs_check svector m_stack; // stack for DFS for occurs_check void oc_mark_on_stack(enode * n); bool oc_on_stack(enode * n) const { return n->get_root()->is_marked(); } void oc_mark_cycle_free(enode * n); bool oc_cycle_free(enode * n) const { return n->get_root()->is_marked2(); } void oc_push_stack(enode * n); ptr_vector m_array_args; ptr_vector const& get_array_args(enode* n); // class for managing state of final_check class final_check_st { theory_datatype * th; public: final_check_st(theory_datatype * th); ~final_check_st(); }; enode * oc_get_cstor(enode * n); 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); void display_var(std::ostream & out, theory_var v) const; protected: theory_var mk_var(enode * n) override; bool internalize_atom(app * atom, bool gate_ctx) override; bool internalize_term(app * term) override; void apply_sort_cnstr(enode * n, sort * s) override; void new_eq_eh(theory_var v1, theory_var v2) override; bool use_diseqs() const override; void new_diseq_eh(theory_var v1, theory_var v2) override; void assign_eh(bool_var v, bool is_true) override; void relevant_eh(app * n) override; void push_scope_eh() override; void pop_scope_eh(unsigned num_scopes) override; final_check_status final_check_eh() override; void reset_eh() override; void restart_eh() override { m_util.reset(); } bool is_shared(theory_var v) const override; public: theory_datatype(ast_manager & m, theory_datatype_params & p); ~theory_datatype() override; theory * mk_fresh(context * new_ctx) override; void display(std::ostream & out) const override; void collect_statistics(::statistics & st) const override; void init_model(model_generator & m) override; model_value_proc * mk_value(enode * n, model_generator & m) override; th_trail_stack & get_trail_stack() { return m_trail_stack; } virtual void merge_eh(theory_var v1, theory_var v2, theory_var, theory_var); static void after_merge_eh(theory_var r1, theory_var r2, theory_var v1, theory_var v2) {} void unmerge_eh(theory_var v1, theory_var v2); char const * get_name() const override { return "datatype"; } bool include_func_interp(func_decl* f) override; }; }; #endif /* THEORY_DATATYPE_H_ */ z3-z3-4.8.7/src/smt/theory_dense_diff_logic.cpp000066400000000000000000000007311356505360400213630ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_dense_diff_logic.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-05-16. Revision History: --*/ #include "smt/theory_dense_diff_logic_def.h" namespace smt { template class theory_dense_diff_logic; template class theory_dense_diff_logic; template class theory_dense_diff_logic; template class theory_dense_diff_logic; }; z3-z3-4.8.7/src/smt/theory_dense_diff_logic.h000066400000000000000000000251551356505360400210370ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_dense_diff_logic.h Abstract: Author: Leonardo de Moura (leonardo) 2008-05-16. Revision History: TODO: eager equality propagation --*/ #ifndef THEORY_DENSE_DIFF_LOGIC_H_ #define THEORY_DENSE_DIFF_LOGIC_H_ #include "smt/theory_arith.h" #include "smt/params/theory_arith_params.h" #include "ast/arith_decl_plugin.h" #include "smt/arith_eq_adapter.h" #include "smt/theory_opt.h" namespace smt { struct theory_dense_diff_logic_statistics { unsigned m_num_assertions; unsigned m_num_propagations; void reset() { m_num_assertions = 0; m_num_propagations = 0; } theory_dense_diff_logic_statistics() { reset(); } }; template class theory_dense_diff_logic : public theory, public theory_opt, private Ext { public: theory_dense_diff_logic_statistics m_stats; private: typedef typename Ext::inf_numeral numeral; class atom { typedef typename Ext::inf_numeral numeral; bool_var m_bvar; theory_var m_source; theory_var m_target; numeral m_offset; public: atom(bool_var bv, theory_var source, theory_var target, numeral const & offset): m_bvar(bv), m_source(source), m_target(target), m_offset(offset) { } bool_var get_bool_var() const { return m_bvar; } theory_var get_source() const { return m_source; } theory_var get_target() const { return m_target; } numeral const & get_offset() const { return m_offset; } }; typedef ptr_vector atoms; typedef ptr_vector bool_var2atom; struct edge { theory_var m_source; theory_var m_target; numeral m_offset; literal m_justification; edge():m_source(null_theory_var), m_target(null_theory_var), m_justification(null_literal) {} edge(theory_var s, theory_var t, numeral const & offset, literal js): m_source(s), m_target(t), m_offset(offset), m_justification(js) { } }; typedef int edge_id; typedef vector edges; static const edge_id null_edge_id = -1; static const edge_id self_edge_id = 0; struct cell { edge_id m_edge_id; numeral m_distance; atoms m_occs; cell(): m_edge_id(null_edge_id) { } }; struct cell_trail { unsigned short m_source; unsigned short m_target; edge_id m_old_edge_id; numeral m_old_distance; cell_trail(unsigned short s, unsigned short t, edge_id old_edge_id, numeral const & old_distance): m_source(s), m_target(t), m_old_edge_id(old_edge_id), m_old_distance(old_distance) {} }; typedef vector row; typedef vector matrix; struct scope { unsigned m_atoms_lim; unsigned m_edges_lim; unsigned m_cell_trail_lim; }; theory_arith_params & m_params; arith_util m_autil; arith_eq_adapter m_arith_eq_adapter; atoms m_atoms; atoms m_bv2atoms; edges m_edges; // list of asserted edges matrix m_matrix; svector m_is_int; vector m_cell_trail; svector m_scopes; bool m_non_diff_logic_exprs; // For optimization purpose typedef vector > objective_term; vector m_objectives; vector m_objective_consts; vector m_objective_assignments; struct f_target { theory_var m_target; numeral m_new_distance; }; typedef std::pair var_pair; typedef vector f_targets; literal_vector m_tmp_literals; svector m_tmp_pairs; f_targets m_f_targets; vector m_assignment; struct var_value_hash; friend struct var_value_hash; struct var_value_hash { theory_dense_diff_logic & m_th; var_value_hash(theory_dense_diff_logic & th):m_th(th) {} unsigned operator()(theory_var v) const { return m_th.m_assignment[v].hash(); } }; struct var_value_eq; friend struct var_value_eq; struct var_value_eq { theory_dense_diff_logic & m_th; var_value_eq(theory_dense_diff_logic & th):m_th(th) {} bool operator()(theory_var v1, theory_var v2) const { return m_th.m_assignment[v1] == m_th.m_assignment[v2] && m_th.is_int(v1) == m_th.is_int(v2); } }; typedef int_hashtable var_value_table; var_value_table m_var_value_table; // ----------------------------------- // // Auxiliary // // ----------------------------------- bool is_int(theory_var v) const { return m_is_int[v]; } bool is_real(theory_var v) const { return !is_int(v); } numeral const & get_epsilon(theory_var v) const { return is_real(v) ? this->m_real_epsilon : this->m_int_epsilon; } bool is_times_minus_one(expr * n, app * & r) const { expr * _r; if (m_autil.is_times_minus_one(n, _r)) { r = to_app(_r); return true; } return false; } app * mk_zero_for(expr * n); theory_var mk_var(enode * n) override; theory_var internalize_term_core(app * n); void found_non_diff_logic_expr(expr * n); bool is_connected(theory_var source, theory_var target) const { return m_matrix[source][target].m_edge_id != null_edge_id; } void mk_clause(literal l1, literal l2); void mk_clause(literal l1, literal l2, literal l3); void add_edge(theory_var source, theory_var target, numeral const & offset, literal l); void update_cells(); void propagate_using_cell(theory_var source, theory_var target); void get_antecedents(theory_var source, theory_var target, literal_vector & result); void assign_literal(literal l, theory_var source, theory_var target); void restore_cells(unsigned old_size); void del_atoms(unsigned old_size); void del_vars(unsigned old_num_vars); void init_model(); bool internalize_objective(expr * n, rational const& m, rational& r, objective_term & objective); expr_ref mk_ineq(theory_var v, inf_eps const& val, bool is_strict); #ifdef Z3DEBUG bool check_vector_sizes() const; bool check_matrix() const; #endif public: numeral const & get_distance(theory_var source, theory_var target) const { SASSERT(is_connected(source, target)); return m_matrix[source][target].m_distance; } // ----------------------------------- // // Internalization // // ----------------------------------- bool internalize_atom(app * n, bool gate_ctx) override; bool internalize_term(app * term) override; void internalize_eq_eh(app * atom, bool_var v) override; void apply_sort_cnstr(enode * n, sort * s) override; void assign_eh(bool_var v, bool is_true) override; void new_eq_eh(theory_var v1, theory_var v2) override; bool use_diseqs() const override; void new_diseq_eh(theory_var v1, theory_var v2) override; void conflict_resolution_eh(app * atom, bool_var v) override; void push_scope_eh() override; void pop_scope_eh(unsigned num_scopes) override; void restart_eh() override; void init_search_eh() override; final_check_status final_check_eh() override; bool can_propagate() override; void propagate() override; void flush_eh() override; void reset_eh() override; bool dump_lemmas() const { return m_params.m_arith_dump_lemmas; } void display(std::ostream & out) const override; virtual void display_atom(std::ostream & out, atom * a) const; void collect_statistics(::statistics & st) const override; // ----------------------------------- // // Model generation // // ----------------------------------- arith_factory * m_factory; rational m_epsilon; // void update_epsilon(const inf_numeral & l, const inf_numeral & u); void compute_epsilon(); void fix_zero(); void init_model(model_generator & m) override; model_value_proc * mk_value(enode * n, model_generator & mg) override; // ----------------------------------- // // Optimization // // ----------------------------------- inf_eps_rational maximize(theory_var v, expr_ref& blocker, bool& has_shared) override; inf_eps_rational value(theory_var v) override; theory_var add_objective(app* term) override; virtual expr_ref mk_gt(theory_var v, inf_eps const& val); expr_ref mk_ge(generic_model_converter& fm, theory_var v, inf_eps const& val); // ----------------------------------- // // Main // // ----------------------------------- public: theory_dense_diff_logic(ast_manager & m, theory_arith_params & p); ~theory_dense_diff_logic() override { reset_eh(); } theory * mk_fresh(context * new_ctx) override; char const * get_name() const override { return "difference-logic"; } /** \brief See comment in theory::mk_eq_atom */ app * mk_eq_atom(expr * lhs, expr * rhs) override { return m_autil.mk_eq(lhs, rhs); } }; typedef theory_dense_diff_logic theory_dense_mi; typedef theory_dense_diff_logic theory_dense_i; typedef theory_dense_diff_logic theory_dense_smi; typedef theory_dense_diff_logic theory_dense_si; }; #endif /* THEORY_DENSE_DIFF_LOGIC_H_ */ z3-z3-4.8.7/src/smt/theory_dense_diff_logic_def.h000066400000000000000000001255051356505360400216550ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_dense_diff_logic_def.h Abstract: Author: Leonardo de Moura (leonardo) 2008-05-16. Revision History: --*/ #ifndef THEORY_DENSE_DIFF_LOGIC_DEF_H_ #define THEORY_DENSE_DIFF_LOGIC_DEF_H_ #include "smt/smt_context.h" #include "smt/theory_dense_diff_logic.h" #include "ast/ast_pp.h" #include "smt/smt_model_generator.h" #include "math/simplex/simplex.h" #include "math/simplex/simplex_def.h" namespace smt { template theory_dense_diff_logic::theory_dense_diff_logic(ast_manager & m, theory_arith_params & p): theory(m.mk_family_id("arith")), m_params(p), m_autil(m), m_arith_eq_adapter(*this, p, m_autil), m_non_diff_logic_exprs(false), m_var_value_table(DEFAULT_HASHTABLE_INITIAL_CAPACITY, var_value_hash(*this), var_value_eq(*this)) { m_edges.push_back(edge()); } template theory* theory_dense_diff_logic::mk_fresh(context * new_ctx) { return alloc(theory_dense_diff_logic, new_ctx->get_manager(), m_params); } template inline app * theory_dense_diff_logic::mk_zero_for(expr * n) { return m_autil.mk_numeral(rational(0), get_manager().get_sort(n)); } template theory_var theory_dense_diff_logic::mk_var(enode * n) { theory_var v = theory::mk_var(n); bool is_int = m_autil.is_int(n->get_owner()); m_is_int.push_back(is_int); m_f_targets.push_back(f_target()); for (auto& rows : m_matrix) { rows.push_back(cell()); } m_matrix.push_back(row()); row & r = m_matrix.back(); SASSERT(r.empty()); r.resize(v+1); cell & c = m_matrix[v][v]; c.m_edge_id = self_edge_id; c.m_distance.reset(); SASSERT(check_vector_sizes()); get_context().attach_th_var(n, this, v); return v; } template theory_var theory_dense_diff_logic::internalize_term_core(app * n) { context & ctx = get_context(); if (ctx.e_internalized(n)) { enode * e = ctx.get_enode(n); if (is_attached_to_var(e)) return e->get_th_var(get_id()); } rational _k; if (m_autil.is_add(n) && to_app(n)->get_num_args() == 2 && m_autil.is_numeral(to_app(n)->get_arg(0), _k)) { numeral k(_k); if (m_params.m_arith_reflect) internalize_term_core(to_app(to_app(n)->get_arg(0))); theory_var s = internalize_term_core(to_app(to_app(n)->get_arg(1))); enode * e = ctx.mk_enode(n, !m_params.m_arith_reflect, false, true); theory_var v = mk_var(e); add_edge(s, v, k, null_literal); k.neg(); add_edge(v, s, k, null_literal); return v; } else if (m_autil.is_numeral(n, _k)) { enode * e = ctx.mk_enode(n, false, false, true); // internalizer is marking enodes as interpreted whenever the associated ast is a value and a constant. // e->mark_as_interpreted(); theory_var v = mk_var(e); if (_k.is_zero()) return v; theory_var z = internalize_term_core(mk_zero_for(n)); numeral k(_k); add_edge(z, v, k, null_literal); k.neg(); add_edge(v, z, k, null_literal); return v; } else if (!m_autil.is_arith_expr(n)) { if (!ctx.e_internalized(n)) ctx.internalize(n, false); enode * e = ctx.get_enode(n); if (!is_attached_to_var(e)) return mk_var(e); else return e->get_th_var(get_id()); } else { return null_theory_var; } } template void theory_dense_diff_logic::found_non_diff_logic_expr(expr * n) { if (!m_non_diff_logic_exprs) { TRACE("non_diff_logic", tout << "found non diff logic expression:\n" << mk_pp(n, get_manager()) << "\n";); get_context().push_trail(value_trail(m_non_diff_logic_exprs)); IF_VERBOSE(0, verbose_stream() << "(smt.diff_logic: non-diff logic expression " << mk_pp(n, get_manager()) << ")\n";); m_non_diff_logic_exprs = true; } } template bool theory_dense_diff_logic::internalize_atom(app * n, bool gate_ctx) { if (memory::above_high_watermark()) { found_non_diff_logic_expr(n); // little hack... TODO: change to no_memory and return l_undef if SAT return false; } TRACE("ddl", tout << "internalizing atom:\n" << mk_pp(n, get_manager()) << "\n";); context & ctx = get_context(); SASSERT(!ctx.b_internalized(n)); SASSERT(m_autil.is_le(n) || m_autil.is_ge(n)); theory_var source, target; SASSERT(m_autil.is_le(n) || m_autil.is_ge(n)); app * lhs = to_app(n->get_arg(0)); app * rhs = to_app(n->get_arg(1)); SASSERT(m_autil.is_numeral(rhs)); rational _k; m_autil.is_numeral(rhs, _k); numeral offset(_k); app * s, * t; expr *arg1, *arg2; if (m_autil.is_add(lhs, arg1, arg2) && is_times_minus_one(arg2, s)) { t = to_app(arg1); } else if (m_autil.is_add(lhs, arg1, arg2) && is_times_minus_one(arg1, s)) { t = to_app(arg2); } else if (m_autil.is_mul(lhs, arg1, arg2) && m_autil.is_minus_one(arg1)) { s = to_app(arg2); t = mk_zero_for(s); } else if (!m_autil.is_arith_expr(lhs)) { t = to_app(lhs); s = mk_zero_for(t); } else { TRACE("ddl", tout << "failed to internalize:\n" << mk_pp(n, get_manager()) << "\n";); found_non_diff_logic_expr(n); return false; } TRACE("arith", tout << expr_ref(lhs, get_manager()) << " " << expr_ref(s, get_manager()) << " " << expr_ref(t, get_manager()) << "\n";); source = internalize_term_core(s); target = internalize_term_core(t); if (source == null_theory_var || target == null_theory_var) { TRACE("ddl", tout << "failed to internalize:\n" << mk_pp(n, get_manager()) << "\n";); found_non_diff_logic_expr(n); return false; } SASSERT(source != null_theory_var && target != null_theory_var); if (m_autil.is_ge(n)) { std::swap(source, target); offset.neg(); } bool_var bv = ctx.mk_bool_var(n); ctx.set_var_theory(bv, get_id()); atom * a = alloc(atom, bv, source, target, offset); m_atoms.push_back(a); m_bv2atoms.setx(bv, a, 0); m_matrix[source][target].m_occs.push_back(a); m_matrix[target][source].m_occs.push_back(a); TRACE("ddl", tout << "succeeded internalizing:\n" << mk_pp(n, get_manager()) << "\n";); return true; } template void theory_dense_diff_logic::mk_clause(literal l1, literal l2) { get_context().mk_th_axiom(get_id(), l1, l2); } template void theory_dense_diff_logic::mk_clause(literal l1, literal l2, literal l3) { get_context().mk_th_axiom(get_id(), l1, l2, l3); } template bool theory_dense_diff_logic::internalize_term(app * term) { if (memory::above_high_watermark()) { found_non_diff_logic_expr(term); // little hack... TODO: change to no_memory and return l_undef if SAT return false; } TRACE("ddl", tout << "internalizing term: " << mk_pp(term, get_manager()) << "\n";); theory_var v = internalize_term_core(term); TRACE("ddl", tout << mk_pp(term, get_manager()) << "\ninternalization result: " << (v != null_theory_var) << "\n";); if (v == null_theory_var) found_non_diff_logic_expr(term); return v != null_theory_var; } template void theory_dense_diff_logic::internalize_eq_eh(app * atom, bool_var v) { if (memory::above_high_watermark()) return; context & ctx = get_context(); app * lhs = to_app(atom->get_arg(0)); app * rhs = to_app(atom->get_arg(1)); app * s; if (m_autil.is_add(lhs) && to_app(lhs)->get_num_args() == 2 && is_times_minus_one(to_app(lhs)->get_arg(1), s) && m_autil.is_numeral(rhs)) { // force axioms for (= (+ x (* -1 y)) k) // this is necessary because (+ x (* -1 y)) is not a diff logic term. m_arith_eq_adapter.mk_axioms(ctx.get_enode(lhs), ctx.get_enode(rhs)); return; } if (m_params.m_arith_eager_eq_axioms) { enode * n1 = ctx.get_enode(lhs); enode * n2 = ctx.get_enode(rhs); if (n1->get_th_var(get_id()) != null_theory_var && n2->get_th_var(get_id()) != null_theory_var) m_arith_eq_adapter.mk_axioms(n1, n2); } } template void theory_dense_diff_logic::apply_sort_cnstr(enode * n, sort * s) { // do nothing... } template void theory_dense_diff_logic::assign_eh(bool_var v, bool is_true) { if (get_context().has_th_justification(v, get_id())) { TRACE("ddl", tout << "ignoring atom propagated by the theory.\n";); return; } atom * a = m_bv2atoms.get(v, 0); if (!a) { SASSERT(get_manager().is_eq(get_context().bool_var2expr(v))); return; } m_stats.m_num_assertions++; literal l = literal(v, !is_true); theory_var s = a->get_source(); theory_var t = a->get_target(); numeral k = a->get_offset(); TRACE("assign_profile", tout << "#" << get_enode(s)->get_owner_id() << " #" << get_enode(t)->get_owner_id() << " " << k << "\n";); if (l.sign()) { k.neg(); k -= get_epsilon(s); add_edge(t, s, k, l); } else { add_edge(s, t, k, l); } TRACE("ddl_detail", display(tout);); } template void theory_dense_diff_logic::new_eq_eh(theory_var v1, theory_var v2) { m_arith_eq_adapter.new_eq_eh(v1, v2); } template bool theory_dense_diff_logic::use_diseqs() const { return true; } template void theory_dense_diff_logic::new_diseq_eh(theory_var v1, theory_var v2) { m_arith_eq_adapter.new_diseq_eh(v1, v2); } template void theory_dense_diff_logic::conflict_resolution_eh(app * atom, bool_var v) { // do nothing } template void theory_dense_diff_logic::push_scope_eh() { theory::push_scope_eh(); m_scopes.push_back(scope()); scope & s = m_scopes.back(); s.m_atoms_lim = m_atoms.size(); s.m_edges_lim = m_edges.size(); s.m_cell_trail_lim = m_cell_trail.size(); } template void theory_dense_diff_logic::pop_scope_eh(unsigned num_scopes) { unsigned lvl = m_scopes.size(); SASSERT(num_scopes <= lvl); unsigned new_lvl = lvl - num_scopes; scope & s = m_scopes[new_lvl]; restore_cells(s.m_cell_trail_lim); m_edges.shrink(s.m_edges_lim); del_atoms(s.m_atoms_lim); del_vars(get_old_num_vars(num_scopes)); m_scopes.shrink(new_lvl); theory::pop_scope_eh(num_scopes); } template void theory_dense_diff_logic::restore_cells(unsigned old_size) { unsigned sz = m_cell_trail.size(); unsigned i = sz; while (i > old_size) { i--; cell_trail & t = m_cell_trail[i]; cell & c = m_matrix[t.m_source][t.m_target]; c.m_edge_id = t.m_old_edge_id; c.m_distance = t.m_old_distance; } m_cell_trail.shrink(old_size); } template void theory_dense_diff_logic::del_atoms(unsigned old_size) { typename atoms::iterator begin = m_atoms.begin() + old_size; typename atoms::iterator it = m_atoms.end(); while (it != begin) { --it; atom * a = *it; TRACE("del_atoms", tout << "deleting: p" << a->get_bool_var() << "\n";); m_bv2atoms[a->get_bool_var()] = 0; theory_var s = a->get_source(); theory_var t = a->get_target(); TRACE("del_atoms", tout << "m_matrix.size() " << m_matrix.size() << ", m_matrix[s].size() " << m_matrix[s].size() << ", m_matrix[t].size(): " << m_matrix[t].size() << ", t: " << t << ", s: " << s << "\n";); SASSERT(m_matrix[s][t].m_occs.back() == a); SASSERT(m_matrix[t][s].m_occs.back() == a); m_matrix[s][t].m_occs.pop_back(); m_matrix[t][s].m_occs.pop_back(); dealloc(a); } m_atoms.shrink(old_size); } template void theory_dense_diff_logic::del_vars(unsigned old_num_vars) { int num_vars = get_num_vars(); SASSERT(num_vars >= static_cast(old_num_vars)); if (num_vars != static_cast(old_num_vars)) { m_is_int.shrink(old_num_vars); m_f_targets.shrink(old_num_vars); m_matrix.shrink(old_num_vars); for (auto& cells : m_matrix) { cells.shrink(old_num_vars); } } } template void theory_dense_diff_logic::restart_eh() { m_arith_eq_adapter.restart_eh(); } template void theory_dense_diff_logic::init_search_eh() { m_arith_eq_adapter.init_search_eh(); } template final_check_status theory_dense_diff_logic::final_check_eh() { init_model(); if (assume_eqs(m_var_value_table)) return FC_CONTINUE; // logical context contains arithmetic expressions that are not // in the difference logic fragment. if (m_non_diff_logic_exprs) return FC_GIVEUP; return FC_DONE; } template bool theory_dense_diff_logic::can_propagate() { // do nothing return false; } template void theory_dense_diff_logic::propagate() { // do nothing } template void theory_dense_diff_logic::flush_eh() { // do nothing } template void theory_dense_diff_logic::reset_eh() { del_atoms(0); m_atoms .reset(); m_bv2atoms .reset(); m_edges .reset(); m_matrix .reset(); m_is_int .reset(); m_f_targets .reset(); m_cell_trail .reset(); m_scopes .reset(); m_non_diff_logic_exprs = false; m_edges.push_back(edge()); theory::reset_eh(); } /** \brief Store in results the antecedents that justify that the distance between source and target. */ template void theory_dense_diff_logic::get_antecedents(theory_var source, theory_var target, literal_vector & result) { TRACE("ddl", tout << "get_antecedents, source: #" << get_enode(source)->get_owner_id() << ", target: #" << get_enode(target)->get_owner_id() << "\n";); CTRACE("ddl", !is_connected(source, target), display(tout);); SASSERT(is_connected(source, target)); svector & todo = m_tmp_pairs; todo.reset(); if (source != target) todo.push_back(var_pair(source, target)); while (!todo.empty()) { var_pair & curr = todo.back(); theory_var s = curr.first; theory_var t = curr.second; todo.pop_back(); SASSERT(is_connected(s, t)); cell & c = m_matrix[s][t]; SASSERT(c.m_edge_id != self_edge_id); edge & e = m_edges[c.m_edge_id]; if (e.m_justification != null_literal) result.push_back(e.m_justification); if (s != e.m_source) todo.push_back(var_pair(s, e.m_source)); if (e.m_target != t) todo.push_back(var_pair(e.m_target, t)); } } template void theory_dense_diff_logic::update_cells() { edge_id new_edge_id = m_edges.size() - 1; edge & last = m_edges.back(); theory_var s = last.m_source; theory_var t = last.m_target; numeral const & k = last.m_offset; // Compute set F of nodes such that: // x in F iff // k + d(t, x) < d(s, x) numeral new_dist; row & t_row = m_matrix[t]; typename row::iterator it = t_row.begin(); typename row::iterator end = t_row.end(); typename f_targets::iterator fbegin = m_f_targets.begin(); typename f_targets::iterator target = fbegin; for (theory_var x = 0; it != end; ++it, ++x) { if (it->m_edge_id != null_edge_id && x != s) { new_dist = k; new_dist += it->m_distance; cell & s_x = m_matrix[s][x]; TRACE("ddl", tout << "s: #" << get_enode(s)->get_owner_id() << " x: #" << get_enode(x)->get_owner_id() << " new_dist: " << new_dist << "\n"; tout << "already has edge: " << s_x.m_edge_id << " old dist: " << s_x.m_distance << "\n";); if (s_x.m_edge_id == null_edge_id || new_dist < s_x.m_distance) { target->m_target = x; target->m_new_distance = new_dist; ++target; } } } typename f_targets::iterator fend = target; // For each node y such that y --> s, and for each node x in F, // check whether d(y, s) + new_dist(x) < d(y, x). typename matrix::iterator it2 = m_matrix.begin(); typename matrix::iterator end2 = m_matrix.end(); for (theory_var y = 0; it2 != end2; ++it2, ++y) { if (y != t) { row & r = *it2; cell & c = r[s]; if (c.m_edge_id != null_edge_id) { numeral const & d_y_s = c.m_distance; target = fbegin; for (; target != fend; ++target) { theory_var x = target->m_target; if (x != y) { new_dist = d_y_s; new_dist += target->m_new_distance; cell & y_x = m_matrix[y][x]; if (y_x.m_edge_id == null_edge_id || new_dist < y_x.m_distance) { m_cell_trail.push_back(cell_trail(y, x, y_x.m_edge_id, y_x.m_distance)); y_x.m_edge_id = new_edge_id; y_x.m_distance = new_dist; if (!y_x.m_occs.empty()) { propagate_using_cell(y, x); } } } } } } } CASSERT("ddl", check_matrix()); } template void theory_dense_diff_logic::assign_literal(literal l, theory_var source, theory_var target) { context & ctx = get_context(); literal_vector & antecedents = m_tmp_literals; antecedents.reset(); get_antecedents(source, target, antecedents); ctx.assign(l, ctx.mk_justification(theory_propagation_justification(get_id(), ctx.get_region(), antecedents.size(), antecedents.c_ptr(), l))); } template void theory_dense_diff_logic::propagate_using_cell(theory_var source, theory_var target) { cell & c = m_matrix[source][target]; SASSERT(c.m_edge_id != null_edge_id); numeral neg_dist = c.m_distance; neg_dist.neg(); context & ctx = get_context(); typename atoms::const_iterator it = c.m_occs.begin(); typename atoms::const_iterator end = c.m_occs.end(); for (; it != end; ++it) { atom * a = *it; if (ctx.get_assignment(a->get_bool_var()) == l_undef) { if (a->get_source() == source) { SASSERT(a->get_target() == target); if (c.m_distance <= a->get_offset()) { m_stats.m_num_propagations++; TRACE("ddl", tout << "asserting atom to true: "; display_atom(tout, a); tout << "distance(#" << get_enode(source)->get_owner_id() << ", #" << get_enode(target)->get_owner_id() << "): " << c.m_distance << "\n";); assign_literal(literal(a->get_bool_var(), false), source, target); } } else { SASSERT(a->get_source() == target); SASSERT(a->get_target() == source); if (neg_dist > a->get_offset()) { m_stats.m_num_propagations++; TRACE("ddl", tout << "asserting atom to true: "; display_atom(tout, a); tout << "distance(#" << get_enode(source)->get_owner_id() << ", #" << get_enode(target)->get_owner_id() << "): " << c.m_distance << "\n";); assign_literal(literal(a->get_bool_var(), true), source, target); } } } } } template inline void theory_dense_diff_logic::add_edge(theory_var source, theory_var target, numeral const & offset, literal l) { TRACE("ddl", tout << "trying adding edge: #" << get_enode(source)->get_owner_id() << " -- " << offset << " --> #" << get_enode(target)->get_owner_id() << "\n";); cell & c_inv = m_matrix[target][source]; if (c_inv.m_edge_id != null_edge_id && - c_inv.m_distance > offset) { // conflict detected. TRACE("ddl", tout << "conflict detected: #" << get_enode(source)->get_owner_id() << " #" << get_enode(target)->get_owner_id() << " offset: " << offset << ", c_inv.m_edge_id: " << c_inv.m_edge_id << ", c_inv.m_distance: " << c_inv.m_distance << "\n";); literal_vector & antecedents = m_tmp_literals; antecedents.reset(); get_antecedents(target, source, antecedents); if (l != null_literal) antecedents.push_back(l); context & ctx = get_context(); region & r = ctx.get_region(); ctx.set_conflict(ctx.mk_justification(theory_conflict_justification(get_id(), r, antecedents.size(), antecedents.c_ptr()))); if (dump_lemmas()) { ctx.display_lemma_as_smt_problem(antecedents.size(), antecedents.c_ptr(), false_literal); } return; } cell & c = m_matrix[source][target]; if (c.m_edge_id == null_edge_id || offset < c.m_distance) { TRACE("ddl", tout << "adding edge: #" << get_enode(source)->get_owner_id() << " -- " << offset << " --> #" << get_enode(target)->get_owner_id() << "\n";); m_edges.push_back(edge(source, target, offset, l)); update_cells(); } } #ifdef Z3DEBUG template bool theory_dense_diff_logic::check_vector_sizes() const { SASSERT(m_matrix.size() == m_f_targets.size()); SASSERT(m_is_int.size() == m_matrix.size()); typename matrix::const_iterator it = m_matrix.begin(); typename matrix::const_iterator end = m_matrix.end(); for (; it != end; ++it) { SASSERT(it->size() == m_matrix.size()); } return true; } template bool theory_dense_diff_logic::check_matrix() const { int sz = m_matrix.size(); for (theory_var i = 0; i < sz; i++) { for (theory_var j = 0; j < sz; j++) { cell const & c = m_matrix[i][j]; if (c.m_edge_id == self_edge_id) { SASSERT(i == j); SASSERT(c.m_distance.is_zero()); } else if (c.m_edge_id != null_edge_id) { edge const & e = m_edges[c.m_edge_id]; theory_var s = e.m_source; theory_var t = e.m_target; numeral k = get_distance(i, s); k += e.m_offset; k += get_distance(t, j); if (c.m_distance != k) { CTRACE("ddl", c.m_distance != k, tout << "i: " << i << " j: " << j << " k: " << k << " c.m_distance: " << c.m_distance << "\n"; display(tout);); SASSERT(c.m_distance == k); } } } } return true; } #endif template void theory_dense_diff_logic::display(std::ostream & out) const { out << "Theory dense difference logic:\n"; display_var2enode(out); typename matrix::const_iterator it1 = m_matrix.begin(); typename matrix::const_iterator end1 = m_matrix.end(); for (int v1 = 0; it1 != end1; ++it1, ++v1) { typename row::const_iterator it2 = it1->begin(); typename row::const_iterator end2 = it1->end(); for (int v2 = 0; it2 != end2; ++it2, ++v2) { if (it2->m_edge_id != null_edge_id && it2->m_edge_id != self_edge_id) { out << "#"; out.width(5); out << std::left << get_enode(v1)->get_owner_id() << " -- "; out.width(10); out << std::left << it2->m_distance << " : id"; out.width(5); out << std::left << it2->m_edge_id << " --> #"; out << get_enode(v2)->get_owner_id() << "\n"; } } } out << "atoms:\n"; typename atoms::const_iterator it2 = m_atoms.begin(); typename atoms::const_iterator end2 = m_atoms.end(); for (;it2 != end2; ++it2) { atom * a = *it2; display_atom(out, a); } } template void theory_dense_diff_logic::display_atom(std::ostream & out, atom * a) const { out << "#"; out.width(5); out << std::left << get_enode(a->get_target())->get_owner_id() << " - #"; out.width(5); out << std::left << get_enode(a->get_source())->get_owner_id() << " <= "; out.width(10); out << std::left << a->get_offset() << " assignment: " << get_context().get_assignment(a->get_bool_var()) << "\n"; } template void theory_dense_diff_logic::collect_statistics(::statistics & st) const { st.update("dd assertions", m_stats.m_num_assertions); st.update("dd propagations", m_stats.m_num_propagations); m_arith_eq_adapter.collect_statistics(st); } /** \brief Build a model for doing model-based theory combination. */ template void theory_dense_diff_logic::init_model() { int num_vars = get_num_vars(); m_assignment.reset(); m_assignment.resize(num_vars); for (int i = 0; i < num_vars; i++) { row & r = m_matrix[i]; numeral & d = m_assignment[i]; for (int j = 0; j < num_vars; j++) { if (i != j) { cell & c = r[j]; if (c.m_edge_id != null_edge_id && c.m_distance < d) { d = c.m_distance; } } } } for (int i = 0; i < num_vars; i++) m_assignment[i].neg(); TRACE("ddl_model", tout << "ddl model\n"; for (theory_var v = 0; v < num_vars; v++) { tout << "#" << mk_pp(get_enode(v)->get_owner(), get_manager()) << " = " << m_assignment[v] << "\n"; }); } /** The arithmetic module uses infinitesimals. So, an inf_numeral (n,k) represents n + k*epsilon where epsilon is a very small number. In order to generate a model, we need to compute a value for epsilon in a way all inequalities remain satisfied. assume we have the inequality x - y <= (n_c, k_c) where the interpretation for x and y is: (n_x, k_x) and (n_y, k_y). So, (n_x, k_x) <= (n_y + n_c, k_y + k_c) The only interesting case is n_x < n_y + n_c and k_x > k_y + k_c. Using the definition of infinitesimal numbers we have: n_x + k_x * epsilon <= n_y + n_c + (k_y + k_c) * epsilon Therefore: epsilon <= (n_y + n_c - n_x) / (k_x - k_y - k_c) */ template void theory_dense_diff_logic::compute_epsilon() { m_epsilon = rational(1); typename edges::const_iterator it = m_edges.begin(); typename edges::const_iterator end = m_edges.end(); // first edge is null SASSERT(it->m_target == null_theory_var); SASSERT(it->m_source == null_theory_var); ++it; for (; it != end; ++it) { edge const & e = *it; rational n_x = m_assignment[e.m_target].get_rational().to_rational(); rational k_x = m_assignment[e.m_target].get_infinitesimal().to_rational(); rational n_y = m_assignment[e.m_source].get_rational().to_rational(); rational k_y = m_assignment[e.m_source].get_infinitesimal().to_rational(); rational n_c = e.m_offset.get_rational().to_rational(); rational k_c = e.m_offset.get_infinitesimal().to_rational(); TRACE("epsilon", tout << "(n_x,k_x): " << n_x << ", " << k_x << ", (n_y,k_y): " << n_y << ", " << k_y << ", (n_c,k_c): " << n_c << ", " << k_c << "\n";); if (n_x < n_y + n_c && k_x > k_y + k_c) { rational new_epsilon = (n_y + n_c - n_x) / (k_x - k_y - k_c); if (new_epsilon < m_epsilon) { TRACE("epsilon", tout << "new epsilon: " << new_epsilon << "\n";); m_epsilon = new_epsilon; } } } } template void theory_dense_diff_logic::fix_zero() { int num_vars = get_num_vars(); for (int v = 0; v < num_vars; ++v) { enode * n = get_enode(v); if (m_autil.is_zero(n->get_owner()) && !m_assignment[v].is_zero()) { numeral val = m_assignment[v]; sort * s = get_manager().get_sort(n->get_owner()); // adjust the value of all variables that have the same sort. for (int v2 = 0; v2 < num_vars; ++v2) { enode * n2 = get_enode(v2); if (get_manager().get_sort(n2->get_owner()) == s) { m_assignment[v2] -= val; } } SASSERT(m_assignment[v].is_zero()); } } TRACE("ddl_model", tout << "ddl model\n"; for (theory_var v = 0; v < num_vars; v++) { tout << "#" << mk_pp(get_enode(v)->get_owner(), get_manager()) << " = " << m_assignment[v] << "\n"; }); } template void theory_dense_diff_logic::init_model(model_generator & m) { m_factory = alloc(arith_factory, get_manager()); m.register_factory(m_factory); fix_zero(); compute_epsilon(); } template model_value_proc * theory_dense_diff_logic::mk_value(enode * n, model_generator & mg) { theory_var v = n->get_th_var(get_id()); SASSERT(v != null_theory_var); numeral const & val = m_assignment[v]; rational num = val.get_rational().to_rational() + m_epsilon * val.get_infinitesimal().to_rational(); return alloc(expr_wrapper_proc, m_factory->mk_num_value(num, is_int(v))); } // TBD: code is common to both sparse and dense difference logic solvers. template bool theory_dense_diff_logic::internalize_objective(expr * n, rational const& m, rational& q, objective_term & objective) { // Compile term into objective_term format rational r; expr* x, *y; if (m_autil.is_numeral(n, r)) { q += r; } else if (m_autil.is_add(n)) { for (unsigned i = 0; i < to_app(n)->get_num_args(); ++i) { if (!internalize_objective(to_app(n)->get_arg(i), m, q, objective)) { return false; } } } else if (m_autil.is_mul(n, x, y) && m_autil.is_numeral(x, r)) { return internalize_objective(y, m*r, q, objective); } else if (m_autil.is_mul(n, y, x) && m_autil.is_numeral(x, r)) { return internalize_objective(y, m*r, q, objective); } else if (!is_app(n)) { return false; } else if (to_app(n)->get_family_id() == m_autil.get_family_id()) { return false; } else { context& ctx = get_context(); enode * e = nullptr; theory_var v = 0; if (ctx.e_internalized(n)) { e = ctx.get_enode(to_app(n)); } else { ctx.internalize(n, false); e = ctx.get_enode(n); } v = e->get_th_var(get_id()); if (v == null_theory_var) { v = mk_var(e); } objective.push_back(std::make_pair(v, m)); } return true; } template inf_eps_rational theory_dense_diff_logic::value(theory_var v) { objective_term const& objective = m_objectives[v]; inf_eps r = inf_eps(m_objective_consts[v]); for (unsigned i = 0; i < objective.size(); ++i) { numeral n = m_assignment[v]; rational r1 = n.get_rational().to_rational(); rational r2 = n.get_infinitesimal().to_rational(); r += objective[i].second * inf_eps(rational(0), inf_rational(r1, r2)); } return r; } template inf_eps_rational theory_dense_diff_logic::maximize(theory_var v, expr_ref& blocker, bool& has_shared) { typedef simplex::simplex Simplex; ast_manager& m = get_manager(); Simplex S(m.limit()); objective_term const& objective = m_objectives[v]; has_shared = false; IF_VERBOSE(4, for (unsigned i = 0; i < objective.size(); ++i) { verbose_stream() << objective[i].second << " * v" << objective[i].first << " "; } verbose_stream() << " + " << m_objective_consts[v] << "\n";); unsynch_mpq_manager mgr; unsynch_mpq_inf_manager inf_mgr; unsigned num_nodes = get_num_vars(); unsigned num_edges = m_edges.size(); S.ensure_var(num_nodes + num_edges + m_objectives.size()); for (unsigned i = 0; i < num_nodes; ++i) { numeral const& a = m_assignment[i]; rational fin = a.get_rational().to_rational(); rational inf = a.get_infinitesimal().to_rational(); mpq_inf q(mgr.dup(fin.to_mpq()), mgr.dup(inf.to_mpq())); S.set_value(i, q); inf_mgr.del(q); } for (unsigned i = 0; i < num_nodes; ++i) { enode * n = get_enode(i); if (m_autil.is_zero(n->get_owner())) { S.set_lower(v, mpq_inf(mpq(0), mpq(0))); S.set_upper(v, mpq_inf(mpq(0), mpq(0))); break; } } svector vars; scoped_mpq_vector coeffs(mgr); coeffs.push_back(mpq(1)); coeffs.push_back(mpq(-1)); coeffs.push_back(mpq(-1)); vars.resize(3); for (unsigned i = 0; i < num_edges; ++i) { edge const& e = m_edges[i]; if (e.m_source == null_theory_var || e.m_target == null_theory_var) { continue; } unsigned base_var = num_nodes + i; vars[0] = e.m_target; vars[1] = e.m_source; vars[2] = base_var; S.add_row(base_var, 3, vars.c_ptr(), coeffs.c_ptr()); // t - s <= w // t - s - b = 0, b >= w numeral const& w = e.m_offset; rational fin = w.get_rational().to_rational(); rational inf = w.get_infinitesimal().to_rational(); mpq_inf q(mgr.dup(fin.to_mpq()), mgr.dup(inf.to_mpq())); S.set_upper(base_var, q); inf_mgr.del(q); } unsigned w = num_nodes + num_edges + v; // add objective function as row. coeffs.reset(); vars.reset(); for (unsigned i = 0; i < objective.size(); ++i) { coeffs.push_back(objective[i].second.to_mpq()); vars.push_back(objective[i].first); } coeffs.push_back(mpq(1)); vars.push_back(w); Simplex::row row = S.add_row(w, vars.size(), vars.c_ptr(), coeffs.c_ptr()); TRACE("opt", S.display(tout); display(tout);); // optimize lbool is_sat = S.make_feasible(); if (is_sat == l_undef) { blocker = m.mk_false(); return inf_eps::infinity(); } TRACE("opt", S.display(tout); ); SASSERT(is_sat != l_false); lbool is_fin = S.minimize(w); switch (is_fin) { case l_true: { simplex::mpq_ext::eps_numeral const& val = S.get_value(w); inf_rational r(-rational(val.first), -rational(val.second)); TRACE("opt", tout << r << " " << "\n"; S.display_row(tout, row, true);); Simplex::row_iterator it = S.row_begin(row), end = S.row_end(row); expr_ref_vector& core = m_objective_assignments[v]; expr_ref tmp(m); core.reset(); for (; it != end; ++it) { unsigned v = it->m_var; if (num_nodes <= v && v < num_nodes + num_edges) { unsigned edge_id = v - num_nodes; literal lit = m_edges[edge_id].m_justification; if (lit != null_literal) { get_context().literal2expr(lit, tmp); core.push_back(tmp); } } TRACE("opt", tout << core << "\n";); } for (unsigned i = 0; i < num_nodes; ++i) { mpq_inf const& val = S.get_value(i); rational q(val.first), eps(val.second); numeral a(q); m_assignment[i] = a; // TBD: if epsilon is != 0, then adjust a by some small fraction. } inf_eps result(rational(0), r); blocker = mk_gt(v, result); IF_VERBOSE(10, verbose_stream() << blocker << "\n";); r += m_objective_consts[v]; return inf_eps(rational(0), r); } default: TRACE("opt", tout << "unbounded\n"; ); blocker = m.mk_false(); return inf_eps::infinity(); } } template theory_var theory_dense_diff_logic::add_objective(app* term) { TRACE("opt", tout << mk_pp(term, get_manager()) << "\n";); objective_term objective; theory_var result = m_objectives.size(); rational q(1), r(0); expr_ref_vector vr(get_manager()); if (!is_linear(get_manager(), term)) { result = null_theory_var; } else if (internalize_objective(term, q, r, objective)) { m_objectives.push_back(objective); m_objective_consts.push_back(r); m_objective_assignments.push_back(vr); } else { result = null_theory_var; } return result; } template expr_ref theory_dense_diff_logic::mk_gt(theory_var v, inf_eps const& val) { return mk_ineq(v, val, true); } template expr_ref theory_dense_diff_logic::mk_ge( generic_model_converter& fm, theory_var v, inf_eps const& val) { return mk_ineq(v, val, false); } template expr_ref theory_dense_diff_logic::mk_ineq(theory_var v, inf_eps const& val, bool is_strict) { ast_manager& m = get_manager(); objective_term const& t = m_objectives[v]; expr_ref e(m), f(m), f2(m); TRACE("opt", tout << "mk_ineq " << v << " " << val << "\n";); if (t.size() == 1 && t[0].second.is_one()) { f = get_enode(t[0].first)->get_owner(); } else if (t.size() == 1 && t[0].second.is_minus_one()) { f = m_autil.mk_uminus(get_enode(t[0].first)->get_owner()); } else if (t.size() == 2 && t[0].second.is_one() && t[1].second.is_minus_one()) { f = get_enode(t[0].first)->get_owner(); f2 = get_enode(t[1].first)->get_owner(); f = m_autil.mk_sub(f, f2); } else if (t.size() == 2 && t[1].second.is_one() && t[0].second.is_minus_one()) { f = get_enode(t[1].first)->get_owner(); f2 = get_enode(t[0].first)->get_owner(); f = m_autil.mk_sub(f, f2); } else { // expr_ref_vector const& core = m_objective_assignments[v]; f = m.mk_and(core.size(), core.c_ptr()); if (is_strict) { f = m.mk_not(f); } TRACE("arith", tout << "block: " << f << "\n";); return f; } e = m_autil.mk_numeral(val.get_rational(), m.get_sort(f)); if (val.get_infinitesimal().is_neg()) { if (is_strict) { f = m_autil.mk_ge(f, e); } else { expr_ref_vector const& core = m_objective_assignments[v]; f = m.mk_and(core.size(), core.c_ptr()); } } else { if (is_strict) { f = m_autil.mk_gt(f, e); } else { f = m_autil.mk_ge(f, e); } } return f; } }; #endif /* THEORY_DENSE_DIFF_LOGIC_DEF_H_ */ z3-z3-4.8.7/src/smt/theory_diff_logic.cpp000066400000000000000000000012431356505360400202040ustar00rootroot00000000000000/*++ Copyright (c) 2008 Microsoft Corporation Module Name: theory_diff_logic.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-04-21. Nikolaj Bjorner (nbjorner) 2008-05-05 Revision History: --*/ #include "smt/theory_diff_logic.h" #include "util/rational.h" #include "smt/theory_diff_logic_def.h" #include "math/simplex/sparse_matrix_def.h" namespace smt { template class theory_diff_logic; template class theory_diff_logic; template class theory_diff_logic; template class theory_diff_logic; }; namespace simplex { template class simplex; template class sparse_matrix; }; z3-z3-4.8.7/src/smt/theory_diff_logic.h000066400000000000000000000330051356505360400176520ustar00rootroot00000000000000/*++ Copyright (c) 2008 Microsoft Corporation Module Name: theory_diff_logic.h Abstract: Author: Leonardo de Moura (leonardo) 2008-04-21. Nikolaj Bjorner (nbjorner) 2008-05-05 Revision History: --*/ #ifndef THEORY_DIFF_LOGIC_H_ #define THEORY_DIFF_LOGIC_H_ #include "util/rational.h" #include "util/inf_rational.h" #include "util/inf_int_rational.h" #include "util/s_integer.h" #include "util/inf_s_integer.h" #include "util/map.h" #include "ast/arith_decl_plugin.h" #include "model/numeral_factory.h" #include "smt/smt_theory.h" #include "smt/diff_logic.h" #include "smt/smt_justification.h" #include "smt/params/smt_params.h" #include "smt/arith_eq_adapter.h" #include "smt/smt_model_generator.h" #include "smt/smt_clause.h" #include "smt/theory_opt.h" #include "math/simplex/simplex.h" #include "math/simplex/simplex_def.h" // The DL theory can represent term such as n + k, where n is an enode and k is a numeral. namespace smt { struct theory_diff_logic_statistics { unsigned m_num_conflicts; unsigned m_num_assertions; unsigned m_num_th2core_eqs; unsigned m_num_core2th_eqs; unsigned m_num_core2th_diseqs; unsigned m_num_core2th_new_diseqs; void reset() { memset(this, 0, sizeof(*this)); } theory_diff_logic_statistics() { reset(); } }; template class theory_diff_logic : public theory, public theory_opt, private Ext { typedef typename Ext::numeral numeral; typedef simplex::simplex Simplex; typedef inf_eps_rational inf_eps; class atom { bool_var m_bvar; bool m_true; int m_pos; int m_neg; public: atom(bool_var bv, int pos, int neg): m_bvar(bv), m_true(false), m_pos(pos), m_neg(neg) { } ~atom() {} bool_var get_bool_var() const { return m_bvar; } bool is_true() const { return m_true; } void assign_eh(bool is_true) { m_true = is_true; } int get_asserted_edge() const { return this->m_true?m_pos:m_neg; } int get_pos() const { return m_pos; } int get_neg() const { return m_neg; } std::ostream& display(theory_diff_logic const& th, std::ostream& out) const; }; typedef ptr_vector atoms; typedef u_map bool_var2atom; // Auxiliary info for propagating cheap equalities class eq_prop_info { int m_scc_id; numeral m_delta; theory_var m_root; public: eq_prop_info(int scc_id, const numeral & d, theory_var r = null_theory_var): m_scc_id(scc_id), m_delta(d), m_root(r) { } theory_var get_root() const { return m_root; } unsigned hash() const { return mk_mix(static_cast(m_scc_id), m_delta.hash(), 0x9e3779b9); } bool operator==(const eq_prop_info & info) const { return m_scc_id == info.m_scc_id && m_delta == info.m_delta; } }; struct eq_prop_info_hash_proc { unsigned operator()(eq_prop_info * info) const { return info->hash(); } }; struct eq_prop_info_eq_proc { bool operator()(eq_prop_info * info1, eq_prop_info * info2) const { return *info1 == *info2; } }; typedef ptr_hashtable eq_prop_info_set; // Extension for diff_logic core. struct GExt : public Ext { typedef literal explanation; }; // Functor used to collect the proofs for a conflict due to // a negative cycle. class nc_functor { literal_vector m_antecedents; theory_diff_logic& m_super; public: nc_functor(theory_diff_logic& s) : m_super(s) {} void reset(); literal_vector const& get_lits() const { return m_antecedents; } void operator()(literal const & ex) { if (ex != null_literal) { m_antecedents.push_back(ex); } } void new_edge(dl_var src, dl_var dst, unsigned num_edges, edge_id const* edges) { m_super.new_edge(src, dst, num_edges, edges); } }; struct scope { unsigned m_atoms_lim; unsigned m_asserted_atoms_lim; unsigned m_asserted_qhead_old; }; typedef dl_graph Graph; smt_params & m_params; arith_util m_util; arith_eq_adapter m_arith_eq_adapter; theory_diff_logic_statistics m_stats; Graph m_graph; theory_var m_zero; // cache the variable representing the zero variable. int_vector m_scc_id; // Cheap equality propagation eq_prop_info_set m_eq_prop_info_set; // set of existing equality prop infos ptr_vector m_eq_prop_infos; app_ref_vector m_terms; svector m_signs; ptr_vector m_atoms; ptr_vector m_asserted_atoms; // set of asserted atoms unsigned m_asserted_qhead; bool_var2atom m_bool_var2atom; svector m_scopes; unsigned m_num_core_conflicts; unsigned m_num_propagation_calls; double m_agility; bool m_is_lia; bool m_non_diff_logic_exprs; arith_factory * m_factory; rational m_delta; nc_functor m_nc_functor; // For optimization purpose typedef vector > objective_term; vector m_objectives; vector m_objective_consts; vector m_objective_assignments; vector m_objective_rows; Simplex m_S; unsigned m_num_simplex_edges; // Set a conflict due to a negative cycle. void set_neg_cycle_conflict(); void new_edge(dl_var src, dl_var dst, unsigned num_edges, edge_id const* edges); // Create a new theory variable. theory_var mk_var(enode* n) override; virtual theory_var mk_var(app* n); void compute_delta(); void found_non_diff_logic_expr(expr * n); bool is_interpreted(app* n) const { return get_family_id() == n->get_family_id(); } public: theory_diff_logic(ast_manager& m, smt_params & params): theory(m.mk_family_id("arith")), m_params(params), m_util(m), m_arith_eq_adapter(*this, params, m_util), m_zero(null_theory_var), m_terms(m), m_asserted_qhead(0), m_num_core_conflicts(0), m_num_propagation_calls(0), m_agility(0.5), m_is_lia(true), m_non_diff_logic_exprs(false), m_factory(nullptr), m_nc_functor(*this), m_S(m.limit()), m_num_simplex_edges(0) { } ~theory_diff_logic() override { reset_eh(); } theory * mk_fresh(context * new_ctx) override; char const * get_name() const override { return "difference-logic"; } /** \brief See comment in theory::mk_eq_atom */ app * mk_eq_atom(expr * lhs, expr * rhs) override { return m_util.mk_eq(lhs, rhs); } void init(context * ctx) override; bool internalize_atom(app * atom, bool gate_ctx) override; bool internalize_term(app * term) override; void internalize_eq_eh(app * atom, bool_var v) override; void assign_eh(bool_var v, bool is_true) override; void new_eq_eh(theory_var v1, theory_var v2) override; bool use_diseqs() const override { return true; } void new_diseq_eh(theory_var v1, theory_var v2) override; void push_scope_eh() override; void pop_scope_eh(unsigned num_scopes) override; void restart_eh() override { m_arith_eq_adapter.restart_eh(); } void relevant_eh(app* e) override {} void init_search_eh() override { m_arith_eq_adapter.init_search_eh(); } final_check_status final_check_eh() override; bool is_shared(theory_var v) const override { return false; } bool can_propagate() override { return m_asserted_qhead != m_asserted_atoms.size(); } void propagate() override; justification * why_is_diseq(theory_var v1, theory_var v2) override { NOT_IMPLEMENTED_YET(); return nullptr; } // virtual void flush_eh(); void reset_eh() override; void init_model(model_generator & m) override; model_value_proc * mk_value(enode * n, model_generator & mg) override; void display(std::ostream & out) const override; void collect_statistics(::statistics & st) const override; // ----------------------------------- // // Optimization // // ----------------------------------- expr_ref mk_ge(generic_model_converter& fm, theory_var v, inf_eps const& val); inf_eps maximize(theory_var v, expr_ref& blocker, bool& has_shared) override; inf_eps value(theory_var v) override; theory_var add_objective(app* term) override; bool internalize_objective(expr * n, rational const& m, rational& r, objective_term & objective); private: expr_ref mk_gt(theory_var v, inf_eps const& val); expr_ref mk_ineq(theory_var v, inf_eps const& val, bool is_strict); virtual void new_eq_eh(theory_var v1, theory_var v2, justification& j); virtual void new_diseq_eh(theory_var v1, theory_var v2, justification& j); bool decompose_linear(app_ref_vector& args, svector& signs); bool is_sign(expr* n, bool& sign); bool is_negative(app* n, app*& m); void del_atoms(unsigned old_size); void propagate_core(); bool propagate_atom(atom* a); theory_var mk_term(app* n); theory_var mk_num(app* n, rational const& r); bool is_offset(app* n, app*& v, app*& offset, rational& r); bool is_consistent() const; bool reflect() const { return m_params.m_arith_reflect; } bool theory_resolve() const { return m_params.m_theory_resolve; } unsigned lazy_pivoting_lvl() const { return m_params.m_arith_lazy_pivoting_lvl; } bool propagate_eqs() const { return m_params.m_arith_propagate_eqs; } bool dump_lemmas() const { return m_params.m_arith_dump_lemmas; } theory_var expand(bool pos, theory_var v, rational & k); void new_eq_or_diseq(bool is_eq, theory_var v1, theory_var v2, justification& eq_just); void get_eq_antecedents(theory_var v1, theory_var v2, unsigned timestamp, conflict_resolution & cr); void get_implied_bound_antecedents(edge_id bridge_edge, edge_id subsumed_edge, conflict_resolution & cr); theory_var get_zero() const { return m_zero; } void inc_conflicts(); // Optimization: // convert variables, edges and objectives to simplex. unsigned node2simplex(unsigned v); unsigned edge2simplex(unsigned e); unsigned obj2simplex(unsigned v); unsigned num_simplex_vars(); bool is_simplex_edge(unsigned e); unsigned simplex2edge(unsigned e); void update_simplex(Simplex& S); }; struct idl_ext { // TODO: It doesn't need to be a rational, but a bignum integer. static const bool m_int_theory = true; typedef rational numeral; typedef rational fin_numeral; numeral m_epsilon; idl_ext() : m_epsilon(1) {} }; struct sidl_ext { static const bool m_int_theory = true; typedef s_integer numeral; typedef s_integer fin_numeral; numeral m_epsilon; sidl_ext() : m_epsilon(1) {} }; struct rdl_ext { static const bool m_int_theory = false; typedef inf_int_rational numeral; typedef rational fin_numeral; numeral m_epsilon; rdl_ext() : m_epsilon(rational(), true) {} }; // // there is no s_rational class, so // instead we use multi-precision rationals. // struct srdl_ext : public rdl_ext {}; typedef theory_diff_logic theory_idl; typedef theory_diff_logic theory_fidl; typedef theory_diff_logic theory_rdl; typedef theory_diff_logic theory_frdl; }; #endif /* THEORY_DIFF_LOGIC_H_ */ z3-z3-4.8.7/src/smt/theory_diff_logic_def.h000066400000000000000000001240641356505360400204760ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_diff_logic_def.h Abstract: Difference Logic Author: Leonardo de Moura (leonardo) 2006-11-29. Nikolaj Bjorner (nbjorner) 2008-05-11 Revision History: 2008-05-11 ported from v1.2. Add theory propagation. --*/ #ifndef THEORY_DIFF_LOGIC_DEF_H_ #define THEORY_DIFF_LOGIC_DEF_H_ #include "util/map.h" #include "util/warning.h" #include "ast/ast_pp.h" #include "smt/theory_diff_logic.h" #include "smt/smt_context.h" #include "smt/smt_model_generator.h" #include "model/model_implicant.h" using namespace smt; template std::ostream& theory_diff_logic::atom::display(theory_diff_logic const& th, std::ostream& out) const { context& ctx = th.get_context(); lbool asgn = ctx.get_assignment(m_bvar); //SASSERT(asgn == l_undef || ((asgn == l_true) == m_true)); bool sign = (l_undef == asgn) || m_true; return out << literal(m_bvar, sign) << " " << mk_pp(ctx.bool_var2expr(m_bvar), th.get_manager()) << " "; if (l_undef == asgn) { out << "unassigned\n"; } else { th.m_graph.display_edge(out, get_asserted_edge()); } return out; } // ----------------------------------------- // theory_diff_logic::nc_functor template void theory_diff_logic::nc_functor::reset() { m_antecedents.reset(); } // ----------------------------------------- // theory_diff_logic template void theory_diff_logic::init(context * ctx) { theory::init(ctx); app* zero; enode* e; zero = m_util.mk_numeral(rational(0), true); e = ctx->mk_enode(zero, false, false, true); SASSERT(!is_attached_to_var(e)); m_zero = mk_var(e); } template bool theory_diff_logic::internalize_term(app * term) { bool result = null_theory_var != mk_term(term); CTRACE("arith", !result, tout << "Did not internalize " << mk_pp(term, get_manager()) << "\n";); if (!result) { TRACE("non_diff_logic", tout << "Terms may not be internalized\n";); found_non_diff_logic_expr(term); } return result; } template class diff_logic_bounds { bool m_inf_is_set; bool m_sup_is_set; bool m_eq_found; literal m_inf_l; literal m_sup_l; literal m_eq_l; numeral m_inf_w; numeral m_sup_w; numeral m_w; public: diff_logic_bounds() { reset(numeral(0)); } void reset(numeral const& w) { m_inf_is_set = false; m_sup_is_set = false; m_eq_found = false; m_inf_l = null_literal; m_sup_l = null_literal; m_eq_l = null_literal; m_w = w; } void operator()(numeral const& w, literal l) { if (l != null_literal) { if ((w < m_w) && (!m_inf_is_set || w > m_inf_w)) { m_inf_w = w; m_inf_l = l; m_inf_is_set = true; } else if ((w > m_w) && (!m_sup_is_set || w < m_sup_w)) { m_sup_w = w; m_sup_l = l; m_sup_is_set = true; } else if (w == m_w) { m_eq_found = true; m_eq_l = l; } } } bool get_inf(numeral& w, literal& l) const { w = m_inf_w; l = m_inf_l; return m_inf_is_set; } bool get_sup(numeral& w, literal& l) const { w = m_sup_w; l = m_sup_l; return m_sup_is_set; } bool get_eq(literal& l) const { l = m_eq_l; return m_eq_found; } }; // // Atoms are of the form x + -1*y <= k, or x + -1*y = k // template void theory_diff_logic::found_non_diff_logic_expr(expr * n) { if (!m_non_diff_logic_exprs) { TRACE("non_diff_logic", tout << "found non diff logic expression:\n" << mk_pp(n, get_manager()) << "\n";); IF_VERBOSE(0, verbose_stream() << "(smt.diff_logic: non-diff logic expression " << mk_pp(n, get_manager()) << ")\n";); get_context().push_trail(value_trail(m_non_diff_logic_exprs)); m_non_diff_logic_exprs = true; } } template bool theory_diff_logic::internalize_atom(app * n, bool gate_ctx) { context & ctx = get_context(); if (!m_util.is_le(n) && !m_util.is_ge(n)) { found_non_diff_logic_expr(n); return false; } SASSERT(m_util.is_le(n) || m_util.is_ge(n)); SASSERT(!ctx.b_internalized(n)); bool is_ge = m_util.is_ge(n); bool_var bv; rational kr; theory_var source, target; // target - source <= k app * lhs = to_app(n->get_arg(0)); app * rhs = to_app(n->get_arg(1)); if (!m_util.is_numeral(rhs)) { std::swap(rhs, lhs); is_ge = !is_ge; } if (!m_util.is_numeral(rhs, kr)) { found_non_diff_logic_expr(n); return false; } numeral k(kr); m_terms.reset(); m_signs.reset(); m_terms.push_back(lhs); m_signs.push_back(true); if (!decompose_linear(m_terms, m_signs)) { found_non_diff_logic_expr(n); return false; } if (m_terms.size() == 2 && m_signs[0] != m_signs[1]) { target = mk_var(m_terms[0].get()); source = mk_var(m_terms[1].get()); if (!m_signs[0]) { std::swap(target, source); } } else { target = mk_var(lhs); source = get_zero(); } if (is_ge) { std::swap(target, source); k.neg(); } bv = ctx.mk_bool_var(n); ctx.set_var_theory(bv, get_id()); literal l(bv); // // Create axioms for situations as: // x - y <= 5 => x - y <= 7 // if (m_params.m_arith_add_binary_bounds) { literal l0; numeral k0; diff_logic_bounds bounds; bounds.reset(k); m_graph.enumerate_edges(source, target, bounds); if (bounds.get_eq(l0)) { ctx.mk_th_axiom(get_id(),~l0,l); ctx.mk_th_axiom(get_id(),~l,l0); } else { if (bounds.get_inf(k0, l0)) { SASSERT(k0 <= k); ctx.mk_th_axiom(get_id(),~l0,l); } if (bounds.get_sup(k0, l0)) { SASSERT(k <= k0); ctx.mk_th_axiom(get_id(),~l,l0); } } } edge_id pos = m_graph.add_edge(source, target, k, l); k.neg(); if (m_util.is_int(lhs)) { SASSERT(k.is_int()); k -= numeral(1); } else { m_is_lia = false; k -= this->m_epsilon; } edge_id neg = m_graph.add_edge(target, source, k, ~l); atom * a = alloc(atom, bv, pos, neg); m_atoms.push_back(a); m_bool_var2atom.insert(bv, a); TRACE("arith", tout << mk_pp(n, get_manager()) << "\n"; m_graph.display_edge(tout << "pos: ", pos); m_graph.display_edge(tout << "neg: ", neg); ); return true; } template void theory_diff_logic::internalize_eq_eh(app * atom, bool_var v) { context & ctx = get_context(); TRACE("arith", tout << mk_pp(atom, get_manager()) << "\n";); app * lhs = to_app(atom->get_arg(0)); app * rhs = to_app(atom->get_arg(1)); app * s; if (m_util.is_add(lhs) && to_app(lhs)->get_num_args() == 2 && is_negative(to_app(to_app(lhs)->get_arg(1)), s) && m_util.is_numeral(rhs)) { // force axioms for (= (+ x (* -1 y)) k) // this is necessary because (+ x (* -1 y)) is not a diff logic term. m_arith_eq_adapter.mk_axioms(ctx.get_enode(lhs), ctx.get_enode(rhs)); return; } if (m_params.m_arith_eager_eq_axioms) { enode * n1 = ctx.get_enode(lhs); enode * n2 = ctx.get_enode(rhs); if (n1->get_th_var(get_id()) != null_theory_var && n2->get_th_var(get_id()) != null_theory_var) m_arith_eq_adapter.mk_axioms(n1, n2); } } template void theory_diff_logic::assign_eh(bool_var v, bool is_true) { m_stats.m_num_assertions++; atom * a = nullptr; VERIFY (m_bool_var2atom.find(v, a)); SASSERT(a); SASSERT(get_context().get_assignment(v) != l_undef); SASSERT((get_context().get_assignment(v) == l_true) == is_true); a->assign_eh(is_true); m_asserted_atoms.push_back(a); } template void theory_diff_logic::collect_statistics(::statistics & st) const { st.update("dl conflicts", m_stats.m_num_conflicts); st.update("dl asserts", m_stats.m_num_assertions); st.update("core->dl eqs", m_stats.m_num_core2th_eqs); st.update("core->dl diseqs", m_stats.m_num_core2th_diseqs); m_arith_eq_adapter.collect_statistics(st); m_graph.collect_statistics(st); } template void theory_diff_logic::push_scope_eh() { theory::push_scope_eh(); m_graph.push(); m_scopes.push_back(scope()); scope & s = m_scopes.back(); s.m_atoms_lim = m_atoms.size(); s.m_asserted_atoms_lim = m_asserted_atoms.size(); s.m_asserted_qhead_old = m_asserted_qhead; } template void theory_diff_logic::pop_scope_eh(unsigned num_scopes) { unsigned lvl = m_scopes.size(); SASSERT(num_scopes <= lvl); unsigned new_lvl = lvl - num_scopes; scope & s = m_scopes[new_lvl]; del_atoms(s.m_atoms_lim); m_asserted_atoms.shrink(s.m_asserted_atoms_lim); m_asserted_qhead = s.m_asserted_qhead_old; m_scopes.shrink(new_lvl); unsigned num_edges = m_graph.get_num_edges(); m_graph.pop(num_scopes); if (num_edges != m_graph.get_num_edges() && m_num_simplex_edges > 0) { m_S.reset(); m_num_simplex_edges = 0; m_objective_rows.reset(); } theory::pop_scope_eh(num_scopes); } template final_check_status theory_diff_logic::final_check_eh() { if (can_propagate()) { propagate_core(); return FC_CONTINUE; } TRACE("arith_final", display(tout); ); // either will already be zero (as we don't do mixed constraints). m_graph.set_to_zero(m_zero); SASSERT(is_consistent()); if (m_non_diff_logic_exprs) { return FC_GIVEUP; } return FC_DONE; } template void theory_diff_logic::del_atoms(unsigned old_size) { typename atoms::iterator begin = m_atoms.begin() + old_size; typename atoms::iterator it = m_atoms.end(); while (it != begin) { --it; atom * a = *it; bool_var bv = a->get_bool_var(); m_bool_var2atom.erase(bv); dealloc(a); } m_atoms.shrink(old_size); } template bool theory_diff_logic::decompose_linear(app_ref_vector& terms, svector& signs) { for (unsigned i = 0; i < terms.size(); ++i) { app* n = terms[i].get(); if (m_util.is_add(n)) { expr* arg = n->get_arg(0); if (!is_app(arg)) return false; terms[i] = to_app(arg); for (unsigned j = 1; j < n->get_num_args(); ++j) { arg = n->get_arg(j); if (!is_app(arg)) return false; terms.push_back(to_app(arg)); signs.push_back(signs[i]); } --i; continue; } expr* x, *y; bool sign; if (m_util.is_mul(n, x, y)) { if (is_sign(x, sign) && is_app(y)) { terms[i] = to_app(y); signs[i] = (signs[i] == sign); --i; } else if (is_sign(y, sign) && is_app(x)) { terms[i] = to_app(x); signs[i] = (signs[i] == sign); --i; } continue; } if (m_util.is_uminus(n, x) && is_app(x)) { terms[i] = to_app(x); signs[i] = !signs[i]; --i; continue; } } return true; } template bool theory_diff_logic::is_sign(expr* n, bool& sign) { rational r; expr* x; if (m_util.is_numeral(n, r)) { if (r.is_one()) { sign = true; return true; } if (r.is_minus_one()) { sign = false; return true; } } else if (m_util.is_uminus(n, x)) { if (is_sign(x, sign)) { sign = !sign; return true; } } return false; } template bool theory_diff_logic::is_negative(app* n, app*& m) { expr* a0, *a1, *a2; rational r; if (!m_util.is_mul(n, a0, a1)) { return false; } if (m_util.is_numeral(a1)) { std::swap(a0, a1); } if (m_util.is_numeral(a0, r) && r.is_minus_one() && is_app(a1)) { m = to_app(a1); return true; } if (m_util.is_uminus(a1)) { std::swap(a0, a1); } if (m_util.is_uminus(a0, a2) && m_util.is_numeral(a2, r) && r.is_one() && is_app(a1)) { m = to_app(a1); return true; } return false; } template void theory_diff_logic::propagate() { if (m_params.m_arith_adaptive) { switch(m_params.m_arith_propagation_strategy) { case ARITH_PROP_PROPORTIONAL: { ++m_num_propagation_calls; if (m_num_propagation_calls * (m_stats.m_num_conflicts + 1) > m_params.m_arith_adaptive_propagation_threshold * get_context().m_stats.m_num_conflicts) { m_num_propagation_calls = 1; TRACE("arith_prop", tout << "propagating: " << m_num_propagation_calls << "\n";); propagate_core(); } else { TRACE("arith_prop", tout << "skipping propagation " << m_num_propagation_calls << "\n";); } break; } case ARITH_PROP_AGILITY: { // update agility with factor generated by other conflicts. double g = m_params.m_arith_adaptive_propagation_threshold; while (m_num_core_conflicts < get_context().m_stats.m_num_conflicts) { m_agility = m_agility*g; ++m_num_core_conflicts; } ++m_num_propagation_calls; bool do_propagate = (m_num_propagation_calls * m_agility > m_params.m_arith_adaptive_propagation_threshold); TRACE("arith_prop", tout << (do_propagate?"propagating: ":"skipping ") << " " << m_num_propagation_calls << " agility: " << m_agility << "\n";); if (do_propagate) { m_num_propagation_calls = 0; propagate_core(); } break; } default: UNREACHABLE(); propagate_core(); } } else { propagate_core(); } } template void theory_diff_logic::inc_conflicts() { m_stats.m_num_conflicts++; if (m_params.m_arith_adaptive) { double g = m_params.m_arith_adaptive_propagation_threshold; m_agility = m_agility*g + 1 - g; } } template void theory_diff_logic::propagate_core() { bool consistent = true; while (consistent && can_propagate()) { atom * a = m_asserted_atoms[m_asserted_qhead]; m_asserted_qhead++; consistent = propagate_atom(a); } } template bool theory_diff_logic::propagate_atom(atom* a) { context& ctx = get_context(); TRACE("arith", a->display(*this, tout); tout << "\n";); if (ctx.inconsistent()) { return false; } int edge_id = a->get_asserted_edge(); if (!m_graph.enable_edge(edge_id)) { set_neg_cycle_conflict(); return false; } return true; } template void theory_diff_logic::new_edge(dl_var src, dl_var dst, unsigned num_edges, edge_id const* edges) { if (!theory_resolve()) { return; } TRACE("dl_activity", tout << "\n";); context& ctx = get_context(); numeral w(0); for (unsigned i = 0; i < num_edges; ++i) { w += m_graph.get_weight(edges[i]); } enode* e1 = get_enode(src); enode* e2 = get_enode(dst); expr* n1 = e1->get_owner(); expr* n2 = e2->get_owner(); bool is_int = m_util.is_int(n1); rational num = w.get_rational().to_rational(); expr_ref le(get_manager()); if (w.is_rational()) { // x - y <= w expr* n3 = m_util.mk_numeral(num, is_int); n2 = m_util.mk_mul(m_util.mk_numeral(rational(-1), is_int), n2); le = m_util.mk_le(m_util.mk_add(n1,n2), n3); } else { // x - y < w // <=> // not (x - y >= w) // <=> // not (y - x <= -w) // SASSERT(w.get_infinitesimal().is_neg()); expr* n3 = m_util.mk_numeral(-num, is_int); n1 = m_util.mk_mul(m_util.mk_numeral(rational(-1), is_int), n1); le = m_util.mk_le(m_util.mk_add(n2,n1), n3); le = get_manager().mk_not(le); } if (get_manager().has_trace_stream())log_axiom_instantiation(le); ctx.internalize(le, false); if (get_manager().has_trace_stream()) get_manager().trace_stream() << "[end-of-instance]\n"; ctx.mark_as_relevant(le.get()); literal lit(ctx.get_literal(le)); bool_var bv = lit.var(); atom* a = nullptr; m_bool_var2atom.find(bv, a); SASSERT(a); literal_vector lits; for (unsigned i = 0; i < num_edges; ++i) { lits.push_back(~m_graph.get_explanation(edges[i])); } lits.push_back(lit); TRACE("dl_activity", tout << mk_pp(le, get_manager()) << "\n"; tout << "edge: " << a->get_pos() << "\n"; ctx.display_literals_verbose(tout, lits.size(), lits.c_ptr()); tout << "\n"; ); justification * js = nullptr; if (get_manager().proofs_enabled()) { vector params; params.push_back(parameter(symbol("farkas"))); params.resize(lits.size()+1, parameter(rational(1))); js = new (ctx.get_region()) theory_lemma_justification(get_id(), ctx, lits.size(), lits.c_ptr(), params.size(), params.c_ptr()); } ctx.mk_clause(lits.size(), lits.c_ptr(), js, CLS_TH_LEMMA, nullptr); if (dump_lemmas()) { symbol logic(m_is_lia ? "QF_LIA" : "QF_LRA"); ctx.display_lemma_as_smt_problem(lits.size(), lits.c_ptr(), false_literal, logic); } #if 0 TRACE("arith", tout << "shortcut:\n"; for (unsigned i = 0; i < num_edges; ++i) { edge_id e = edges[i]; // tgt <= src + w numeral w = m_graph.get_weight(e); dl_var tgt = m_graph.get_target(e); dl_var src = m_graph.get_source(e); if (i + 1 < num_edges) { dl_var tgt2 = m_graph.get_target(edges[i+1]); SASSERT(src == tgt2); } tout << "$" << tgt << " <= $" << src << " + " << w << "\n"; } { numeral w = m_graph.get_weight(e_id); dl_var tgt = m_graph.get_target(e_id); dl_var src = m_graph.get_source(e_id); tout << "$" << tgt << " <= $" << src << " + " << w << "\n"; } ); #endif } template void theory_diff_logic::set_neg_cycle_conflict() { m_nc_functor.reset(); m_graph.traverse_neg_cycle2(m_params.m_arith_stronger_lemmas, m_nc_functor); inc_conflicts(); literal_vector const& lits = m_nc_functor.get_lits(); context & ctx = get_context(); TRACE("arith_conflict", tout << "conflict: "; for (unsigned i = 0; i < lits.size(); ++i) { ctx.display_literal_info(tout, lits[i]); } tout << "\n"; ); if (dump_lemmas()) { symbol logic(m_is_lia ? "QF_LIA" : "QF_LRA"); ctx.display_lemma_as_smt_problem(lits.size(), lits.c_ptr(), false_literal, logic); } vector params; if (get_manager().proofs_enabled()) { params.push_back(parameter(symbol("farkas"))); for (unsigned i = 0; i <= lits.size(); ++i) { params.push_back(parameter(rational(1))); } } ctx.set_conflict( ctx.mk_justification( ext_theory_conflict_justification( get_id(), ctx.get_region(), lits.size(), lits.c_ptr(), 0, nullptr, params.size(), params.c_ptr()))); } template bool theory_diff_logic::is_offset(app* n, app*& v, app*& offset, rational& r) { if (!m_util.is_add(n)) { return false; } if (n->get_num_args() == 2 && m_util.is_numeral(n->get_arg(0), r)) { v = to_app(n->get_arg(1)); offset = to_app(n->get_arg(0)); return true; } if (n->get_num_args() == 2 && m_util.is_numeral(n->get_arg(1), r)) { v = to_app(n->get_arg(0)); offset = to_app(n->get_arg(1)); return true; } return false; } template theory_var theory_diff_logic::mk_term(app* n) { SASSERT(!m_util.is_sub(n)); SASSERT(!m_util.is_uminus(n)); app* a, *offset; theory_var source, target; enode* e; context& ctx = get_context(); TRACE("arith", tout << mk_pp(n, get_manager()) << "\n";); rational r; if (m_util.is_numeral(n, r)) { return mk_num(n, r); } else if (is_offset(n, a, offset, r)) { // n = a + k source = mk_var(a); for (unsigned i = 0; i < n->get_num_args(); ++i) { expr* arg = n->get_arg(i); if (!ctx.e_internalized(arg)) { ctx.internalize(arg, false); } } e = get_context().mk_enode(n, false, false, true); target = mk_var(e); numeral k(r); // target - source <= k, source - target <= -k m_graph.enable_edge(m_graph.add_edge(source, target, k, null_literal)); m_graph.enable_edge(m_graph.add_edge(target, source, -k, null_literal)); return target; } else if (m_util.is_add(n)) { return null_theory_var; } else if (m_util.is_mul(n)) { return null_theory_var; } else if (m_util.is_div(n)) { return null_theory_var; } else if (m_util.is_idiv(n)) { return null_theory_var; } else if (m_util.is_mod(n)) { return null_theory_var; } else if (m_util.is_rem(n)) { return null_theory_var; } else { return mk_var(n); } } template theory_var theory_diff_logic::mk_num(app* n, rational const& r) { theory_var v = null_theory_var; enode* e = nullptr; context& ctx = get_context(); if (r.is_zero()) { v = get_zero(); } else if (ctx.e_internalized(n)) { e = ctx.get_enode(n); v = e->get_th_var(get_id()); SASSERT(v != null_theory_var); } else { theory_var zero = get_zero(); SASSERT(n->get_num_args() == 0); e = ctx.mk_enode(n, false, false, true); v = mk_var(e); // internalizer is marking enodes as interpreted whenever the associated ast is a value and a constant. // e->mark_as_interpreted(); numeral k(r); // v = k: v - zero <= k, zero - v <= - k m_graph.enable_edge(m_graph.add_edge(zero, v, k, null_literal)); m_graph.enable_edge(m_graph.add_edge(v, zero, -k, null_literal)); } return v; } template theory_var theory_diff_logic::mk_var(enode* n) { theory_var v = theory::mk_var(n); TRACE("diff_logic_vars", tout << "mk_var: " << v << "\n";); m_graph.init_var(v); get_context().attach_th_var(n, this, v); return v; } template theory_var theory_diff_logic::mk_var(app* n) { context & ctx = get_context(); enode* e = nullptr; theory_var v = null_theory_var; if (ctx.e_internalized(n)) { e = ctx.get_enode(n); v = e->get_th_var(get_id()); } else { ctx.internalize(n, false); e = ctx.get_enode(n); } if (v == null_theory_var) { v = mk_var(e); } if (is_interpreted(n)) { TRACE("non_diff_logic", tout << "Variable should not be interpreted\n";); found_non_diff_logic_expr(n); } TRACE("arith", tout << mk_pp(n, get_manager()) << " |-> " << v << "\n";); return v; } template void theory_diff_logic::reset_eh() { for (unsigned i = 0; i < m_atoms.size(); ++i) { dealloc(m_atoms[i]); } m_graph .reset(); m_zero = null_theory_var; m_atoms .reset(); m_asserted_atoms .reset(); m_stats .reset(); m_scopes .reset(); m_asserted_qhead = 0; m_num_core_conflicts = 0; m_num_propagation_calls = 0; m_agility = 0.5; m_is_lia = true; m_non_diff_logic_exprs = false; m_objectives .reset(); m_objective_consts.reset(); m_objective_assignments.reset(); theory::reset_eh(); } template void theory_diff_logic::compute_delta() { m_delta = rational(1); unsigned num_edges = m_graph.get_num_edges(); for (unsigned i = 0; i < num_edges; ++i) { if (!m_graph.is_enabled(i)) { continue; } numeral w = m_graph.get_weight(i); dl_var tgt = m_graph.get_target(i); dl_var src = m_graph.get_source(i); rational n_x = m_graph.get_assignment(tgt).get_rational().to_rational(); rational k_x = m_graph.get_assignment(tgt).get_infinitesimal().to_rational(); rational n_y = m_graph.get_assignment(src).get_rational().to_rational(); rational k_y = m_graph.get_assignment(src).get_infinitesimal().to_rational(); rational n_c = w.get_rational().to_rational(); rational k_c = w.get_infinitesimal().to_rational(); TRACE("epsilon", tout << "(n_x,k_x): " << n_x << ", " << k_x << ", (n_y,k_y): " << n_y << ", " << k_y << ", (n_c,k_c): " << n_c << ", " << k_c << "\n";); if (n_x < n_y + n_c && k_x > k_y + k_c) { rational new_delta = (n_y + n_c - n_x) / (k_x - k_y - k_c); if (new_delta < m_delta) { TRACE("epsilon", tout << "new delta: " << new_delta << "\n";); m_delta = new_delta; } } } } template void theory_diff_logic::init_model(smt::model_generator & m) { m_factory = alloc(arith_factory, get_manager()); m.register_factory(m_factory); compute_delta(); } template model_value_proc * theory_diff_logic::mk_value(enode * n, model_generator & mg) { theory_var v = n->get_th_var(get_id()); SASSERT(v != null_theory_var); numeral val = m_graph.get_assignment(v); rational num = val.get_rational().to_rational() + m_delta * val.get_infinitesimal().to_rational(); TRACE("arith", tout << mk_pp(n->get_owner(), get_manager()) << " |-> " << num << "\n";); return alloc(expr_wrapper_proc, m_factory->mk_num_value(num, m_util.is_int(n->get_owner()))); } template void theory_diff_logic::display(std::ostream & out) const { for (unsigned i = 0; i < m_atoms.size(); ++i) { m_atoms[i]->display(*this, out); } m_graph.display(out); } template bool theory_diff_logic::is_consistent() const { DEBUG_CODE( context& ctx = get_context(); for (unsigned i = 0; i < m_atoms.size(); ++i) { atom* a = m_atoms[i]; bool_var bv = a->get_bool_var(); lbool asgn = ctx.get_assignment(bv); if (ctx.is_relevant(ctx.bool_var2expr(bv)) && asgn != l_undef) { SASSERT((asgn == l_true) == a->is_true()); int edge_id = a->get_asserted_edge(); SASSERT(m_graph.is_enabled(edge_id)); SASSERT(m_graph.is_feasible(edge_id)); } }); return m_graph.is_feasible(); } template theory_var theory_diff_logic::expand(bool pos, theory_var v, rational & k) { context& ctx = get_context(); enode* e = get_enode(v); rational r; for (;;) { app* n = e->get_owner(); if (m_util.is_add(n) && n->get_num_args() == 2) { app* x = to_app(n->get_arg(0)); app* y = to_app(n->get_arg(1)); if (m_util.is_numeral(x, r)) { e = ctx.get_enode(y); } else if (m_util.is_numeral(y, r)) { e = ctx.get_enode(x); } v = e->get_th_var(get_id()); SASSERT(v != null_theory_var); if (v == null_theory_var) { break; } if (pos) { k += r; } else { k -= r; } } else { break; } } return v; } template void theory_diff_logic::new_eq_or_diseq(bool is_eq, theory_var v1, theory_var v2, justification& eq_just) { rational k; theory_var s = expand(true, v1, k); theory_var t = expand(false, v2, k); context& ctx = get_context(); ast_manager& m = get_manager(); if (s == t) { if (is_eq != k.is_zero()) { // conflict 0 /= k; inc_conflicts(); ctx.set_conflict(&eq_just); } } else { // // Create equality ast, internalize_atom // assign the corresponding equality literal. // app_ref eq(m), s2(m), t2(m); app* s1 = get_enode(s)->get_owner(); app* t1 = get_enode(t)->get_owner(); s2 = m_util.mk_sub(t1, s1); t2 = m_util.mk_numeral(k, m.get_sort(s2.get())); // t1 - s1 = k eq = m.mk_eq(s2.get(), t2.get()); if (m.has_trace_stream()) { app_ref body(m); body = m.mk_eq(m.mk_eq(m_util.mk_add(s1, t2), t1), eq); log_axiom_instantiation(body); } TRACE("diff_logic", tout << v1 << " .. " << v2 << "\n"; tout << mk_pp(eq.get(), m) <<"\n";); if (!internalize_atom(eq.get(), false)) { UNREACHABLE(); } if (m.has_trace_stream()) get_manager().trace_stream() << "[end-of-instance]\n"; literal l(ctx.get_literal(eq.get())); if (!is_eq) { l = ~l; } ctx.assign(l, b_justification(&eq_just), false); } } template void theory_diff_logic::new_eq_eh( theory_var v1, theory_var v2, justification& j) { m_stats.m_num_core2th_eqs++; new_eq_or_diseq(true, v1, v2, j); } template void theory_diff_logic::new_diseq_eh( theory_var v1, theory_var v2, justification& j) { m_stats.m_num_core2th_diseqs++; new_eq_or_diseq(false, v1, v2, j); } template void theory_diff_logic::new_eq_eh(theory_var v1, theory_var v2) { m_arith_eq_adapter.new_eq_eh(v1, v2); } template void theory_diff_logic::new_diseq_eh(theory_var v1, theory_var v2) { m_arith_eq_adapter.new_diseq_eh(v1, v2); } struct imp_functor { conflict_resolution & m_cr; imp_functor(conflict_resolution& cr) : m_cr(cr) {} void operator()(literal l) { m_cr.mark_literal(l); } }; template void theory_diff_logic::get_eq_antecedents( theory_var v1, theory_var v2, unsigned timestamp, conflict_resolution & cr) { imp_functor functor(cr); VERIFY(m_graph.find_shortest_zero_edge_path(v1, v2, timestamp, functor)); VERIFY(m_graph.find_shortest_zero_edge_path(v2, v1, timestamp, functor)); } template void theory_diff_logic::get_implied_bound_antecedents(edge_id bridge_edge, edge_id subsumed_edge, conflict_resolution & cr) { imp_functor f(cr); m_graph.explain_subsumed_lazy(bridge_edge, subsumed_edge, f); } template unsigned theory_diff_logic::node2simplex(unsigned v) { return m_objectives.size() + 2*v + 1; } template unsigned theory_diff_logic::edge2simplex(unsigned e) { return m_objectives.size() + 2*e; } template unsigned theory_diff_logic::obj2simplex(unsigned e) { return e; } template unsigned theory_diff_logic::num_simplex_vars() { return m_objectives.size() + std::max(2*m_graph.get_num_edges(),2*m_graph.get_num_nodes()+1); } template bool theory_diff_logic::is_simplex_edge(unsigned e) { if (e < m_objectives.size()) return false; e -= m_objectives.size(); return (0 == (e & 0x1)); } template unsigned theory_diff_logic::simplex2edge(unsigned e) { SASSERT(is_simplex_edge(e)); return (e - m_objectives.size())/2; } template void theory_diff_logic::update_simplex(Simplex& S) { unsynch_mpq_inf_manager inf_mgr; unsynch_mpq_manager& mgr = inf_mgr.get_mpq_manager(); unsigned num_nodes = m_graph.get_num_nodes(); vector > const& es = m_graph.get_all_edges(); S.ensure_var(num_simplex_vars()); for (unsigned i = 0; i < num_nodes; ++i) { numeral const& a = m_graph.get_assignment(i); rational fin = a.get_rational().to_rational(); rational inf = a.get_infinitesimal().to_rational(); mpq_inf q; inf_mgr.set(q, fin.to_mpq(), inf.to_mpq()); S.set_value(node2simplex(i), q); inf_mgr.del(q); } S.set_lower(node2simplex(get_zero()), mpq_inf(mpq(0), mpq(0))); S.set_upper(node2simplex(get_zero()), mpq_inf(mpq(0), mpq(0))); svector vars; scoped_mpq_vector coeffs(mgr); coeffs.push_back(mpq(1)); coeffs.push_back(mpq(-1)); coeffs.push_back(mpq(-1)); vars.resize(3); for (unsigned i = m_num_simplex_edges; i < es.size(); ++i) { // t - s <= w // => // t - s - b = 0, b >= w dl_edge const& e = es[i]; unsigned base_var = edge2simplex(i); vars[0] = node2simplex(e.get_target()); vars[1] = node2simplex(e.get_source()); vars[2] = base_var; S.add_row(base_var, 3, vars.c_ptr(), coeffs.c_ptr()); } m_num_simplex_edges = es.size(); for (unsigned i = 0; i < es.size(); ++i) { dl_edge const& e = es[i]; unsigned base_var = edge2simplex(i); if (e.is_enabled()) { numeral const& w = e.get_weight(); rational fin = w.get_rational().to_rational(); rational inf = w.get_infinitesimal().to_rational(); mpq_inf q; inf_mgr.set(q, fin.to_mpq(), inf.to_mpq()); S.set_upper(base_var, q); inf_mgr.del(q); } else { S.unset_upper(base_var); } } for (unsigned v = m_objective_rows.size(); v < m_objectives.size(); ++v) { unsigned w = obj2simplex(v); objective_term const& objective = m_objectives[v]; // add objective function as row. coeffs.reset(); vars.reset(); for (unsigned i = 0; i < objective.size(); ++i) { coeffs.push_back(objective[i].second.to_mpq()); vars.push_back(node2simplex(objective[i].first)); } coeffs.push_back(mpq(1)); vars.push_back(w); Simplex::row row = S.add_row(w, vars.size(), vars.c_ptr(), coeffs.c_ptr()); m_objective_rows.push_back(row); } } template typename theory_diff_logic::inf_eps theory_diff_logic::value(theory_var v) { objective_term const& objective = m_objectives[v]; inf_eps r = inf_eps(m_objective_consts[v]); for (unsigned i = 0; i < objective.size(); ++i) { numeral n = m_graph.get_assignment(v); rational r1 = n.get_rational().to_rational(); rational r2 = n.get_infinitesimal().to_rational(); r += objective[i].second * inf_eps(rational(0), inf_rational(r1, r2)); } return r; } template typename theory_diff_logic::inf_eps theory_diff_logic::maximize(theory_var v, expr_ref& blocker, bool& has_shared) { has_shared = false; Simplex& S = m_S; ast_manager& m = get_manager(); update_simplex(S); TRACE("arith", objective_term const& objective = m_objectives[v]; for (unsigned i = 0; i < objective.size(); ++i) { tout << "Coefficient " << objective[i].second << " of theory_var " << objective[i].first << "\n"; } tout << "Free coefficient " << m_objective_consts[v] << "\n"; ); TRACE("opt", S.display(tout); display(tout);); // optimize lbool is_sat = S.make_feasible(); if (is_sat == l_undef) { blocker = m.mk_false(); return inf_eps::infinity(); } TRACE("opt", S.display(tout); ); SASSERT(is_sat != l_false); unsigned w = obj2simplex(v); lbool is_fin = S.minimize(w); switch (is_fin) { case l_true: { simplex::mpq_ext::eps_numeral const& val = S.get_value(w); inf_rational r(-rational(val.first), -rational(val.second)); Simplex::row row = m_objective_rows[v]; TRACE("opt", tout << r << " " << "\n"; S.display_row(tout, row, true);); Simplex::row_iterator it = S.row_begin(row), end = S.row_end(row); expr_ref_vector& core = m_objective_assignments[v]; expr_ref tmp(m); core.reset(); for (; it != end; ++it) { unsigned v = it->m_var; if (is_simplex_edge(v)) { unsigned edge_id = simplex2edge(v); literal lit = m_graph.get_explanation(edge_id); get_context().literal2expr(lit, tmp); core.push_back(tmp); } } compute_delta(); for (unsigned i = 0; i < m_graph.get_num_nodes(); ++i) { unsigned w = node2simplex(i); simplex::mpq_ext::eps_numeral const& val = S.get_value(w); rational r = rational(val.first) + m_delta*rational(val.second); m_graph.set_assignment(i, numeral(r)); } inf_eps r1(rational(0), r); blocker = mk_gt(v, r1); return inf_eps(rational(0), r + m_objective_consts[v]); } default: TRACE("opt", tout << "unbounded\n"; ); blocker = m.mk_false(); return inf_eps::infinity(); } } template theory_var theory_diff_logic::add_objective(app* term) { objective_term objective; theory_var result = m_objectives.size(); rational q(1), r(0); expr_ref_vector vr(get_manager()); if (!is_linear(get_manager(), term)) { result = null_theory_var; } else if (internalize_objective(term, q, r, objective)) { m_objectives.push_back(objective); m_objective_consts.push_back(r); m_objective_assignments.push_back(vr); } else { result = null_theory_var; } return result; } template expr_ref theory_diff_logic::mk_ineq(theory_var v, inf_eps const& val, bool is_strict) { ast_manager& m = get_manager(); objective_term const& t = m_objectives[v]; expr_ref e(m), f(m), f2(m); if (t.size() == 1 && t[0].second.is_one()) { f = get_enode(t[0].first)->get_owner(); } else if (t.size() == 1 && t[0].second.is_minus_one()) { f = m_util.mk_uminus(get_enode(t[0].first)->get_owner()); } else if (t.size() == 2 && t[0].second.is_one() && t[1].second.is_minus_one()) { f = get_enode(t[0].first)->get_owner(); f2 = get_enode(t[1].first)->get_owner(); f = m_util.mk_sub(f, f2); } else if (t.size() == 2 && t[1].second.is_one() && t[0].second.is_minus_one()) { f = get_enode(t[1].first)->get_owner(); f2 = get_enode(t[0].first)->get_owner(); f = m_util.mk_sub(f, f2); } else { // expr_ref_vector const& core = m_objective_assignments[v]; f = m.mk_and(core.size(), core.c_ptr()); if (is_strict) { f = m.mk_not(f); } return f; } inf_eps new_val = val; // - inf_rational(m_objective_consts[v]); e = m_util.mk_numeral(new_val.get_rational(), m.get_sort(f)); if (new_val.get_infinitesimal().is_neg()) { if (is_strict) { f = m_util.mk_ge(f, e); } else { expr_ref_vector const& core = m_objective_assignments[v]; f = m.mk_and(core.size(), core.c_ptr()); } } else { if (is_strict) { f = m_util.mk_gt(f, e); } else { f = m_util.mk_ge(f, e); } } return f; } template expr_ref theory_diff_logic::mk_gt(theory_var v, inf_eps const& val) { return mk_ineq(v, val, true); } template expr_ref theory_diff_logic::mk_ge(generic_model_converter& fm, theory_var v, inf_eps const& val) { return mk_ineq(v, val, false); } #if 0 context & ctx = get_context(); model_ref mdl; ctx.get_model(mdl); ptr_vector formulas(ctx.get_num_asserted_formulas(), ctx.get_asserted_formulas()); ast_manager& m = get_manager(); model_implicant impl_extractor(m); expr_ref_vector implicants = impl_extractor.minimize_literals(formulas, mdl); return m.mk_and(o, m.mk_not(m.mk_and(implicants.size(), implicants.c_ptr()))); #endif template bool theory_diff_logic::internalize_objective(expr * n, rational const& m, rational& q, objective_term & objective) { // Compile term into objective_term format rational r; expr* x, *y; if (m_util.is_numeral(n, r)) { q += r; } else if (m_util.is_add(n)) { for (unsigned i = 0; i < to_app(n)->get_num_args(); ++i) { if (!internalize_objective(to_app(n)->get_arg(i), m, q, objective)) { return false; } } } else if (m_util.is_mul(n, x, y) && m_util.is_numeral(x, r)) { return internalize_objective(y, m*r, q, objective); } else if (m_util.is_mul(n, y, x) && m_util.is_numeral(x, r)) { return internalize_objective(y, m*r, q, objective); } else if (!is_app(n)) { return false; } else if (to_app(n)->get_family_id() == m_util.get_family_id()) { return false; } else { theory_var v = mk_var(to_app(n)); objective.push_back(std::make_pair(v, m)); } return true; } template theory* theory_diff_logic::mk_fresh(context* new_ctx) { return alloc(theory_diff_logic, new_ctx->get_manager(), m_params); } #endif /* THEORY_DIFF_LOGIC_DEF_H_ */ z3-z3-4.8.7/src/smt/theory_dl.cpp000066400000000000000000000231041356505360400165160ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: theory_dl.h Abstract: Theory for DL constants. DL constants are discrete and ordered by the linear order LT. Constants have a parameter which indicates the numeric value that ranges from 0 up to the size of the domain. The procedure works by a simple reduction to bit-vectors. We enforce an injection into bit-vectors. Author: Nikolaj Bjorner (nbjorner) 2011-1-10 Revision History: --*/ #include "ast/dl_decl_plugin.h" #include "ast/bv_decl_plugin.h" #include "ast/ast_pp.h" #include "model/value_factory.h" #include "smt/smt_theory.h" #include "smt/smt_model_generator.h" #include "smt/theory_bv.h" #include "smt/smt_context.h" // Basic approach: reduce theory to bit-vectors: // // rep(c(n)) = n // LT(x,y) <=> rep(x) < rep(y) // val(rep(x)) = x // 0 <= rep(x) <= max_value // namespace smt { class dl_factory : public simple_factory { datalog::dl_decl_util& m_util; public: dl_factory(datalog::dl_decl_util& u, proto_model& m): simple_factory(u.get_manager(), u.get_family_id()), m_util(u) {} app * mk_value_core(unsigned const & val, sort * s) override { return m_util.mk_numeral(val, s); } }; class theory_dl : public theory { datalog::dl_decl_util m_util; bv_util m_bv; ast_ref_vector m_trail; obj_map m_reps; obj_map m_vals; ast_manager& m() { return get_manager(); } datalog::dl_decl_util& u() { return m_util; } bv_util& b() { return m_bv; } class dl_value_proc : public smt::model_value_proc { theory_dl& m_th; smt::enode* m_node; public: dl_value_proc(theory_dl& th, smt::enode* n) : m_th(th), m_node(n) {} void get_dependencies(buffer & result) override {} app * mk_value(smt::model_generator & mg, expr_ref_vector const & ) override { smt::context& ctx = m_th.get_context(); app* result = nullptr; expr* n = m_node->get_owner(); sort* s = m_th.m().get_sort(n); func_decl* r, *v; m_th.get_rep(s, r, v); app_ref rep_of(m_th.m()); rep_of = m_th.m().mk_app(r, m_node->get_owner()); theory_id bv_id = m_th.m().mk_family_id("bv"); theory_bv* th_bv = dynamic_cast(ctx.get_theory(bv_id)); SASSERT(th_bv); rational val; if (ctx.e_internalized(rep_of) && th_bv && th_bv->get_fixed_value(rep_of.get(), val)) { result = m_th.u().mk_numeral(val.get_int64(), s); } else { result = m_th.u().mk_numeral(0, s); } TRACE("theory_dl", tout << mk_pp(result, m_th.m()) << "\n";); return result; } }; public: theory_dl(ast_manager& m): theory(m.mk_family_id("datalog_relation")), m_util(m), m_bv(m), m_trail(m) { } char const * get_name() const override { return "datalog"; } bool internalize_atom(app * atom, bool gate_ctx) override { TRACE("theory_dl", tout << mk_pp(atom, m()) << "\n";); context& ctx = get_context(); if (ctx.b_internalized(atom)) { return true; } switch(atom->get_decl_kind()) { case datalog::OP_DL_LT: { app* a = to_app(atom->get_arg(0)); app* b = to_app(atom->get_arg(1)); ctx.internalize(a, false); ctx.internalize(b, false); literal l(ctx.mk_bool_var(atom)); ctx.set_var_theory(l.var(), get_id()); mk_lt(a,b); return true; } default: break; } return false; } bool internalize_term(app * term) override { TRACE("theory_dl", tout << mk_pp(term, m()) << "\n";); if (u().is_finite_sort(term)) { return mk_rep(term); } else { return false; } } void new_eq_eh(theory_var v1, theory_var v2) override { } void new_diseq_eh(theory_var v1, theory_var v2) override { } theory * mk_fresh(context * new_ctx) override { return alloc(theory_dl, new_ctx->get_manager()); } void init_model(smt::model_generator & m) override { m.register_factory(alloc(dl_factory, m_util, m.get_model())); } smt::model_value_proc * mk_value(smt::enode * n, smt::model_generator&) override { return alloc(dl_value_proc, *this, n); } void apply_sort_cnstr(enode * n, sort * s) override { app* term = n->get_owner(); if (u().is_finite_sort(term)) { mk_rep(term); } } void relevant_eh(app * n) override { if (u().is_finite_sort(n)) { sort* s = m().get_sort(n); func_decl* r, *v; get_rep(s, r, v); if (n->get_decl() != v) { expr* rep = m().mk_app(r, n); uint64_t vl; if (u().is_numeral_ext(n, vl)) { assert_cnstr(m().mk_eq(rep, mk_bv_constant(vl, s))); } else { assert_cnstr(m().mk_eq(m().mk_app(v,rep), n)); assert_cnstr(b().mk_ule(rep, max_value(s))); } } } } void display(std::ostream & out) const override { } private: void get_rep(sort* s, func_decl*& r, func_decl*& v) { if(!m_reps.find(s, r) || !m_vals.find(s,v)) { SASSERT(!m_reps.contains(s)); sort* bv = b().mk_sort(64); r = m().mk_func_decl(m_util.get_family_id(), datalog::OP_DL_REP, 0, nullptr, 1, &s, bv); v = m().mk_func_decl(m_util.get_family_id(), datalog::OP_DL_ABS, 0, nullptr, 1, &bv, s); m_reps.insert(s, r); m_vals.insert(s, v); add_trail(r); add_trail(v); get_context().push_trail(insert_obj_map(m_reps, s)); get_context().push_trail(insert_obj_map(m_vals, s)); } } bool mk_rep(app* n) { context & ctx = get_context(); unsigned num_args = n->get_num_args(); enode * e = nullptr; for (unsigned i = 0; i < num_args; i++) { ctx.internalize(n->get_arg(i), false); } if (ctx.e_internalized(n)) { e = ctx.get_enode(n); } else { e = ctx.mk_enode(n, false, false, true); } if (is_attached_to_var(e)) { return false; } TRACE("theory_dl", tout << mk_pp(n, m()) << "\n";); theory_var var = mk_var(e); ctx.attach_th_var(e, this, var); return true; } app* mk_bv_constant(uint64_t val, sort* s) { return b().mk_numeral(rational(val, rational::ui64()), 64); } app* max_value(sort* s) { uint64_t sz; VERIFY(u().try_get_size(s, sz)); SASSERT(sz > 0); return mk_bv_constant(sz-1, s); } void mk_lt(app* x, app* y) { sort* s = m().get_sort(x); func_decl* r, *v; get_rep(s, r, v); app_ref lt(m()), le(m()); lt = u().mk_lt(x,y); le = b().mk_ule(m().mk_app(r,y),m().mk_app(r,x)); context& ctx = get_context(); if (m().has_trace_stream()) { app_ref body(m()); body = m().mk_eq(lt, le); log_axiom_instantiation(body); } ctx.internalize(lt, false); ctx.internalize(le, false); literal lit1(ctx.get_literal(lt)); literal lit2(ctx.get_literal(le)); ctx.mark_as_relevant(lit1); ctx.mark_as_relevant(lit2); literal lits1[2] = { lit1, lit2 }; literal lits2[2] = { ~lit1, ~lit2 }; ctx.mk_th_axiom(get_id(), 2, lits1); ctx.mk_th_axiom(get_id(), 2, lits2); if (m().has_trace_stream()) m().trace_stream() << "[end-of-instance]\n"; } void assert_cnstr(expr* e) { TRACE("theory_dl", tout << mk_pp(e, m()) << "\n";); context& ctx = get_context(); if (m().has_trace_stream()) log_axiom_instantiation(e); ctx.internalize(e, false); if (m().has_trace_stream()) m().trace_stream() << "[end-of-instance]\n"; literal lit(ctx.get_literal(e)); ctx.mark_as_relevant(lit); ctx.mk_th_axiom(get_id(), 1, &lit); } void add_trail(ast* a) { m_trail.push_back(a); get_context().push_trail(push_back_vector(m_trail)); } }; theory* mk_theory_dl(ast_manager& m) { return alloc(theory_dl, m); } }; z3-z3-4.8.7/src/smt/theory_dl.h000066400000000000000000000005331356505360400161640ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: theory_dl.h Abstract: Basic theory solver for DL finite domains. Author: Nikolaj Bjorner (nbjorner) 2011-10-3 Revision History: --*/ #ifndef THEORY_DL_H_ #define THEORY_DL_H_ namespace smt { theory* mk_theory_dl(ast_manager& m); }; #endif /* THEORY_DL_H_ */ z3-z3-4.8.7/src/smt/theory_dummy.cpp000066400000000000000000000026321356505360400172550ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_dummy.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-12-30. Revision History: --*/ #include "smt/smt_context.h" #include "smt/theory_dummy.h" namespace smt { void theory_dummy::found_theory_expr() { if (!m_theory_exprs) { get_context().push_trail(value_trail(m_theory_exprs)); m_theory_exprs = true; } } theory_dummy::theory_dummy(family_id fid, char const * name): theory(fid), m_theory_exprs(false), m_name(name) { } bool theory_dummy::internalize_atom(app * atom, bool gate_ctx) { found_theory_expr(); return false; } bool theory_dummy::internalize_term(app * term) { found_theory_expr(); return false; } void theory_dummy::new_eq_eh(theory_var v1, theory_var v2) { UNREACHABLE(); } bool theory_dummy::use_diseqs() const { return false; } void theory_dummy::new_diseq_eh(theory_var v1, theory_var v2) { UNREACHABLE(); } void theory_dummy::reset_eh() { m_theory_exprs = true; theory::reset_eh(); } final_check_status theory_dummy::final_check_eh() { return m_theory_exprs ? FC_GIVEUP : FC_DONE; } char const * theory_dummy::get_name() const { return m_name; } }; z3-z3-4.8.7/src/smt/theory_dummy.h000066400000000000000000000026651356505360400167300ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_no_arith.h Abstract: Author: Leonardo de Moura (leonardo) 2008-12-30. Revision History: --*/ #ifndef THEORY_DUMMY_H_ #define THEORY_DUMMY_H_ #include "smt/smt_theory.h" namespace smt { /** \brief Do nothing theory. Tracks whether theory expressions were internalized. When theory expressions were internalized, it returns FC_GIVEUP in the final_check_eh. */ class theory_dummy : public theory { bool m_theory_exprs; char const * m_name; void found_theory_expr(); protected: bool internalize_atom(app * atom, bool gate_ctx) override; bool internalize_term(app * term) override; void new_eq_eh(theory_var v1, theory_var v2) override; bool use_diseqs() const override; void new_diseq_eh(theory_var v1, theory_var v2) override; void reset_eh() override; final_check_status final_check_eh() override; bool build_models() const override { return false; } void display(std::ostream& out) const override {} public: theory_dummy(family_id fid, char const * name); ~theory_dummy() override {} theory * mk_fresh(context * new_ctx) override { return alloc(theory_dummy, get_family_id(), m_name); } char const * get_name() const override; }; }; #endif /* THEORY_DUMMY_H_ */ z3-z3-4.8.7/src/smt/theory_fpa.cpp000066400000000000000000000766601356505360400167040ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: theory_fpa.cpp Abstract: Floating-Point Theory Plugin Author: Christoph (cwinter) 2014-04-23 Revision History: --*/ #include "ast/ast_smt2_pp.h" #include "smt/smt_context.h" #include "smt/theory_fpa.h" #include "smt/theory_bv.h" #include "smt/smt_model_generator.h" #include "ast/fpa/bv2fpa_converter.h" namespace smt { class fpa2bv_conversion_trail_elem : public trail { ast_manager & m; obj_map & m_map; expr_ref key; public: fpa2bv_conversion_trail_elem(ast_manager & m, obj_map & map, expr * e) : m(m), m_map(map), key(e, m) { } ~fpa2bv_conversion_trail_elem() override { } void undo(theory_fpa & th) override { expr * val = m_map.find(key); m_map.remove(key); m.dec_ref(key); m.dec_ref(val); key = nullptr; } }; void theory_fpa::fpa2bv_converter_wrapped::mk_const(func_decl * f, expr_ref & result) { SASSERT(f->get_family_id() == null_family_id); SASSERT(f->get_arity() == 0); expr * r; if (m_const2bv.find(f, r)) { result = r; } else { sort * s = f->get_range(); expr_ref bv(m); bv = m_th.wrap(m.mk_const(f)); unsigned bv_sz = m_th.m_bv_util.get_bv_size(bv); unsigned sbits = m_th.m_fpa_util.get_sbits(s); SASSERT(bv_sz == m_th.m_fpa_util.get_ebits(s) + sbits); result = m_util.mk_fp(m_bv_util.mk_extract(bv_sz - 1, bv_sz - 1, bv), m_bv_util.mk_extract(bv_sz - 2, sbits - 1, bv), m_bv_util.mk_extract(sbits - 2, 0, bv)); SASSERT(m_th.m_fpa_util.is_float(result)); m_const2bv.insert(f, result); m.inc_ref(f); m.inc_ref(result); } } void theory_fpa::fpa2bv_converter_wrapped::mk_rm_const(func_decl * f, expr_ref & result) { SASSERT(f->get_family_id() == null_family_id); SASSERT(f->get_arity() == 0); expr * r; if (m_rm_const2bv.find(f, r)) { result = r; } else { SASSERT(is_rm(f->get_range())); expr_ref bv(m); bv = m_th.wrap(m.mk_const(f)); result = m_util.mk_bv2rm(bv); m_rm_const2bv.insert(f, result); m.inc_ref(f); m.inc_ref(result); } } theory_fpa::theory_fpa(ast_manager & m) : theory(m.mk_family_id("fpa")), m_converter(m, this), m_rw(m, m_converter, params_ref()), m_th_rw(m), m_trail_stack(*this), m_fpa_util(m_converter.fu()), m_bv_util(m_converter.bu()), m_arith_util(m_converter.au()), m_is_initialized(false) { params_ref p; p.set_bool("arith_lhs", true); m_th_rw.updt_params(p); } theory_fpa::~theory_fpa() { m_trail_stack.reset(); if (m_is_initialized) { ast_manager & m = get_manager(); dec_ref_map_key_values(m, m_conversions); dec_ref_collection_values(m, m_is_added_to_model); m_converter.reset(); m_rw.reset(); m_th_rw.reset(); m_is_initialized = false; } SASSERT(m_trail_stack.get_num_scopes() == 0); SASSERT(m_conversions.empty()); SASSERT(m_is_added_to_model.empty()); } void theory_fpa::init(context * ctx) { smt::theory::init(ctx); m_is_initialized = true; } app * theory_fpa::fpa_value_proc::mk_value(model_generator & mg, expr_ref_vector const & values) { TRACE("t_fpa_detail", ast_manager & m = m_th.get_manager(); for (unsigned i = 0; i < values.size(); i++) tout << "value[" << i << "] = " << mk_ismt2_pp(values[i], m) << std::endl;); mpf_manager & mpfm = m_fu.fm(); unsynch_mpz_manager & mpzm = mpfm.mpz_manager(); app * result; scoped_mpz bias(mpzm); mpzm.power(mpz(2), m_ebits - 1, bias); mpzm.dec(bias); scoped_mpz sgn_z(mpzm), sig_z(mpzm), exp_z(mpzm); unsigned bv_sz; if (values.size() == 1) { SASSERT(m_bu.is_bv(values[0])); SASSERT(m_bu.get_bv_size(values[0]) == (m_ebits + m_sbits)); rational all_r(0); scoped_mpz all_z(mpzm); VERIFY(m_bu.is_numeral(values[0], all_r, bv_sz)); SASSERT(bv_sz == (m_ebits + m_sbits)); SASSERT(all_r.is_int()); mpzm.set(all_z, all_r.to_mpq().numerator()); mpzm.machine_div2k(all_z, m_ebits + m_sbits - 1, sgn_z); mpzm.mod(all_z, mpfm.m_powers2(m_ebits + m_sbits - 1), all_z); mpzm.machine_div2k(all_z, m_sbits - 1, exp_z); mpzm.mod(all_z, mpfm.m_powers2(m_sbits - 1), all_z); mpzm.set(sig_z, all_z); } else if (values.size() == 3) { rational sgn_r(0), exp_r(0), sig_r(0); bool r = m_bu.is_numeral(values[0], sgn_r, bv_sz); SASSERT(r && bv_sz == 1); r = m_bu.is_numeral(values[1], exp_r, bv_sz); SASSERT(r && bv_sz == m_ebits); r = m_bu.is_numeral(values[2], sig_r, bv_sz); SASSERT(r && bv_sz == m_sbits - 1); (void)r; SASSERT(mpzm.is_one(sgn_r.to_mpq().denominator())); SASSERT(mpzm.is_one(exp_r.to_mpq().denominator())); SASSERT(mpzm.is_one(sig_r.to_mpq().denominator())); mpzm.set(sgn_z, sgn_r.to_mpq().numerator()); mpzm.set(exp_z, exp_r.to_mpq().numerator()); mpzm.set(sig_z, sig_r.to_mpq().numerator()); } else UNREACHABLE(); scoped_mpz exp_u = exp_z - bias; SASSERT(mpzm.is_int64(exp_u)); scoped_mpf f(mpfm); mpfm.set(f, m_ebits, m_sbits, mpzm.is_one(sgn_z), mpzm.get_int64(exp_u), sig_z); result = m_fu.mk_value(f); TRACE("t_fpa", tout << "result: [" << mpzm.to_string(sgn_z) << "," << mpzm.to_string(exp_z) << "," << mpzm.to_string(sig_z) << "] --> " << mk_ismt2_pp(result, m_th.get_manager()) << std::endl;); return result; } app * theory_fpa::fpa_rm_value_proc::mk_value(model_generator & mg, expr_ref_vector const & values) { SASSERT(values.size() == 1); TRACE("t_fpa_detail", ast_manager & m = m_th.get_manager(); for (unsigned i = 0; i < values.size(); i++) tout << "value[" << i << "] = " << mk_ismt2_pp(values[i], m) << std::endl;); app * result = nullptr; unsigned bv_sz; rational val(0); VERIFY(m_bu.is_numeral(values[0], val, bv_sz)); SASSERT(bv_sz == 3); switch (val.get_uint64()) { case BV_RM_TIES_TO_AWAY: result = m_fu.mk_round_nearest_ties_to_away(); break; case BV_RM_TIES_TO_EVEN: result = m_fu.mk_round_nearest_ties_to_even(); break; case BV_RM_TO_NEGATIVE: result = m_fu.mk_round_toward_negative(); break; case BV_RM_TO_POSITIVE: result = m_fu.mk_round_toward_positive(); break; case BV_RM_TO_ZERO: default: result = m_fu.mk_round_toward_zero(); } TRACE("t_fpa", tout << "result: " << mk_ismt2_pp(result, m_th.get_manager()) << std::endl;); return result; } app_ref theory_fpa::wrap(expr * e) { SASSERT(m_fpa_util.is_float(e) || m_fpa_util.is_rm(e)); SASSERT(!m_fpa_util.is_bvwrap(e)); ast_manager & m = get_manager(); app_ref res(m); if (m_fpa_util.is_fp(e)) { expr * cargs[3] = { to_app(e)->get_arg(0), to_app(e)->get_arg(1), to_app(e)->get_arg(2) }; expr_ref tmp(m_bv_util.mk_concat(3, cargs), m); m_th_rw(tmp); res = to_app(tmp); } else { sort * es = m.get_sort(e); sort_ref bv_srt(m); if (m_converter.is_rm(es)) bv_srt = m_bv_util.mk_sort(3); else { SASSERT(m_converter.is_float(es)); unsigned ebits = m_fpa_util.get_ebits(es); unsigned sbits = m_fpa_util.get_sbits(es); bv_srt = m_bv_util.mk_sort(ebits + sbits); } func_decl_ref wrap_fd(m); wrap_fd = m.mk_func_decl(get_family_id(), OP_FPA_BVWRAP, 0, nullptr, 1, &es, bv_srt); res = m.mk_app(wrap_fd, e); } return res; } app_ref theory_fpa::unwrap(expr * e, sort * s) { SASSERT(!m_fpa_util.is_fp(e)); SASSERT(m_bv_util.is_bv(e)); SASSERT(m_fpa_util.is_float(s) || m_fpa_util.is_rm(s)); ast_manager & m = get_manager(); app_ref res(m); unsigned bv_sz = m_bv_util.get_bv_size(e); if (m_fpa_util.is_rm(s)) { SASSERT(bv_sz == 3); res = m.mk_ite(m.mk_eq(e, m_bv_util.mk_numeral(BV_RM_TIES_TO_AWAY, 3)), m_fpa_util.mk_round_nearest_ties_to_away(), m.mk_ite(m.mk_eq(e, m_bv_util.mk_numeral(BV_RM_TIES_TO_EVEN, 3)), m_fpa_util.mk_round_nearest_ties_to_even(), m.mk_ite(m.mk_eq(e, m_bv_util.mk_numeral(BV_RM_TO_NEGATIVE, 3)), m_fpa_util.mk_round_toward_negative(), m.mk_ite(m.mk_eq(e, m_bv_util.mk_numeral(BV_RM_TO_POSITIVE, 3)), m_fpa_util.mk_round_toward_positive(), m_fpa_util.mk_round_toward_zero())))); } else { SASSERT(m_fpa_util.is_float(s)); unsigned sbits = m_fpa_util.get_sbits(s); SASSERT(bv_sz == m_fpa_util.get_ebits(s) + sbits); res = m_fpa_util.mk_fp(m_bv_util.mk_extract(bv_sz - 1, bv_sz - 1, e), m_bv_util.mk_extract(bv_sz - 2, sbits - 1, e), m_bv_util.mk_extract(sbits - 2, 0, e)); } return res; } expr_ref theory_fpa::convert_atom(expr * e) { ast_manager & m = get_manager(); TRACE("t_fpa_detail", tout << "converting atom: " << mk_ismt2_pp(e, get_manager()) << std::endl;); expr_ref res(m); proof_ref pr(m); m_rw(e, res); m_th_rw(res, res); SASSERT(is_app(res)); SASSERT(m.is_bool(res)); return res; } expr_ref theory_fpa::convert_term(expr * e) { SASSERT(m_fpa_util.is_rm(e) || m_fpa_util.is_float(e)); ast_manager & m = get_manager(); expr_ref e_conv(m), res(m); proof_ref pr(m); m_rw(e, e_conv); TRACE("t_fpa_detail", tout << "term: " << mk_ismt2_pp(e, get_manager()) << std::endl; tout << "converted term: " << mk_ismt2_pp(e_conv, get_manager()) << std::endl;); if (m_fpa_util.is_rm(e)) { SASSERT(m_fpa_util.is_bv2rm(e_conv)); expr_ref bv_rm(m); m_th_rw(to_app(e_conv)->get_arg(0), bv_rm); res = m_fpa_util.mk_bv2rm(bv_rm); } else if (m_fpa_util.is_float(e)) { SASSERT(m_fpa_util.is_fp(e_conv)); expr_ref sgn(m), sig(m), exp(m); m_converter.split_fp(e_conv, sgn, exp, sig); m_th_rw(sgn); m_th_rw(exp); m_th_rw(sig); res = m_fpa_util.mk_fp(sgn, exp, sig); } else UNREACHABLE(); return res; } expr_ref theory_fpa::convert_conversion_term(expr * e) { SASSERT(to_app(e)->get_family_id() == get_family_id()); /* This is for the conversion functions fp.to_* */ expr_ref res(get_manager()); m_rw(e, res); m_th_rw(res, res); return res; } expr_ref theory_fpa::convert(expr * e) { ast_manager & m = get_manager(); expr_ref res(m); expr * ccnv; TRACE("t_fpa", tout << "converting " << mk_ismt2_pp(e, m) << std::endl;); if (m_conversions.find(e, ccnv)) { res = ccnv; TRACE("t_fpa_detail", tout << "cached:" << std::endl; tout << mk_ismt2_pp(e, m) << std::endl << " -> " << std::endl << mk_ismt2_pp(res, m) << std::endl;); } else { if (m_fpa_util.is_fp(e)) res = e; else if (m.is_bool(e)) res = convert_atom(e); else if (m_fpa_util.is_float(e) || m_fpa_util.is_rm(e)) res = convert_term(e); else res = convert_conversion_term(e); TRACE("t_fpa_detail", tout << "converted; caching:" << std::endl; tout << mk_ismt2_pp(e, m) << std::endl << " -> " << std::endl << mk_ismt2_pp(res, m) << std::endl;); m_conversions.insert(e, res); m.inc_ref(e); m.inc_ref(res); m_trail_stack.push(fpa2bv_conversion_trail_elem(m, m_conversions, e)); } return res; } expr_ref theory_fpa::mk_side_conditions() { ast_manager & m = get_manager(); context & ctx = get_context(); expr_ref res(m), t(m); proof_ref t_pr(m); res = m.mk_true(); expr_ref_vector::iterator it = m_converter.m_extra_assertions.begin(); expr_ref_vector::iterator end = m_converter.m_extra_assertions.end(); for (; it != end; it++) { ctx.get_rewriter()(*it, t, t_pr); res = m.mk_and(res, t); } m_converter.m_extra_assertions.reset(); m_th_rw(res); CTRACE("t_fpa", !m.is_true(res), tout << "side condition: " << mk_ismt2_pp(res, m) << std::endl;); return res; } void theory_fpa::assert_cnstr(expr * e) { if (get_manager().is_true(e)) return; TRACE("t_fpa_detail", tout << "asserting " << mk_ismt2_pp(e, get_manager()) << "\n";); context & ctx = get_context(); if (get_manager().has_trace_stream()) log_axiom_instantiation(e); ctx.internalize(e, false); if (get_manager().has_trace_stream()) get_manager().trace_stream() << "[end-of-instance]\n"; literal lit(ctx.get_literal(e)); ctx.mark_as_relevant(lit); ctx.mk_th_axiom(get_id(), 1, &lit); } void theory_fpa::attach_new_th_var(enode * n) { context & ctx = get_context(); theory_var v = mk_var(n); ctx.attach_th_var(n, this, v); TRACE("t_fpa", tout << "new theory var: " << mk_ismt2_pp(n->get_owner(), get_manager()) << " := " << v << "\n";); } bool theory_fpa::internalize_atom(app * atom, bool gate_ctx) { TRACE("t_fpa_internalize", tout << "internalizing atom: " << mk_ismt2_pp(atom, get_manager()) << std::endl;); SASSERT(atom->get_family_id() == get_family_id()); ast_manager & m = get_manager(); context & ctx = get_context(); if (ctx.b_internalized(atom)) return true; unsigned num_args = atom->get_num_args(); for (unsigned i = 0; i < num_args; i++) ctx.internalize(atom->get_arg(i), false); literal l(ctx.mk_bool_var(atom)); ctx.set_var_theory(l.var(), get_id()); expr_ref bv_atom(convert_atom(atom)); expr_ref bv_atom_w_side_c(m), atom_eq(m); bv_atom_w_side_c = m.mk_and(bv_atom, mk_side_conditions()); m_th_rw(bv_atom_w_side_c); atom_eq = m.mk_eq(atom, bv_atom_w_side_c); assert_cnstr(atom_eq); return true; } bool theory_fpa::internalize_term(app * term) { TRACE("t_fpa_internalize", tout << "internalizing term: " << mk_ismt2_pp(term, get_manager()) << "\n";); SASSERT(term->get_family_id() == get_family_id()); SASSERT(!get_context().e_internalized(term)); ast_manager & m = get_manager(); context & ctx = get_context(); unsigned num_args = term->get_num_args(); for (unsigned i = 0; i < num_args; i++) ctx.internalize(term->get_arg(i), false); enode * e = (ctx.e_internalized(term)) ? ctx.get_enode(term) : ctx.mk_enode(term, false, false, true); if (!is_attached_to_var(e)) { attach_new_th_var(e); // The conversion operators fp.to_* appear in non-FP constraints. // The corresponding constraints will not be translated and added // via convert(...) and assert_cnstr(...) in initialize_atom(...). // Therefore, we translate and assert them here. fpa_op_kind k = (fpa_op_kind)term->get_decl_kind(); switch (k) { case OP_FPA_TO_FP: case OP_FPA_TO_UBV: case OP_FPA_TO_SBV: case OP_FPA_TO_REAL: case OP_FPA_TO_IEEE_BV: { expr_ref conv(m); conv = convert(term); assert_cnstr(m.mk_eq(term, conv)); assert_cnstr(mk_side_conditions()); break; } default: /* ignore */; } } return true; } void theory_fpa::apply_sort_cnstr(enode * n, sort * s) { TRACE("t_fpa", tout << "apply sort cnstr for: " << mk_ismt2_pp(n->get_owner(), get_manager()) << "\n";); SASSERT(s->get_family_id() == get_family_id()); SASSERT(m_fpa_util.is_float(s) || m_fpa_util.is_rm(s)); SASSERT(m_fpa_util.is_float(n->get_owner()) || m_fpa_util.is_rm(n->get_owner())); SASSERT(n->get_owner()->get_decl()->get_range() == s); ast_manager & m = get_manager(); context & ctx = get_context(); app_ref owner(n->get_owner(), m); if (!is_attached_to_var(n)) { attach_new_th_var(n); if (m_fpa_util.is_rm(s)) { // For every RM term, we need to make sure that it's // associated bit-vector is within the valid range. if (!m_fpa_util.is_bv2rm(owner)) { expr_ref valid(m), limit(m); limit = m_bv_util.mk_numeral(4, 3); valid = m_bv_util.mk_ule(wrap(owner), limit); assert_cnstr(valid); } } if (!ctx.relevancy()) relevant_eh(owner); } } void theory_fpa::new_eq_eh(theory_var x, theory_var y) { ast_manager & m = get_manager(); enode * e_x = get_enode(x); enode * e_y = get_enode(y); TRACE("t_fpa", tout << "new eq: " << x << " = " << y << std::endl; tout << mk_ismt2_pp(e_x->get_owner(), m) << std::endl << " = " << std::endl << mk_ismt2_pp(e_y->get_owner(), m) << std::endl;); fpa_util & fu = m_fpa_util; expr_ref xe(m), ye(m); xe = e_x->get_owner(); ye = e_y->get_owner(); if (m_fpa_util.is_bvwrap(xe) || m_fpa_util.is_bvwrap(ye)) return; expr_ref xc(m), yc(m); xc = convert(xe); yc = convert(ye); TRACE("t_fpa_detail", tout << "xc = " << mk_ismt2_pp(xc, m) << std::endl << "yc = " << mk_ismt2_pp(yc, m) << std::endl;); expr_ref c(m); if ((fu.is_float(xe) && fu.is_float(ye)) || (fu.is_rm(xe) && fu.is_rm(ye))) m_converter.mk_eq(xc, yc, c); else c = m.mk_eq(xc, yc); m_th_rw(c); expr_ref xe_eq_ye(m), c_eq_iff(m); xe_eq_ye = m.mk_eq(xe, ye); c_eq_iff = m.mk_iff(xe_eq_ye, c); assert_cnstr(c_eq_iff); assert_cnstr(mk_side_conditions()); return; } void theory_fpa::new_diseq_eh(theory_var x, theory_var y) { ast_manager & m = get_manager(); enode * e_x = get_enode(x); enode * e_y = get_enode(y); TRACE("t_fpa", tout << "new diseq: " << x << " != " << y << std::endl; tout << mk_ismt2_pp(e_x->get_owner(), m) << std::endl << " != " << std::endl << mk_ismt2_pp(e_y->get_owner(), m) << std::endl;); fpa_util & fu = m_fpa_util; expr_ref xe(m), ye(m); xe = e_x->get_owner(); ye = e_y->get_owner(); if (m_fpa_util.is_bvwrap(xe) || m_fpa_util.is_bvwrap(ye)) return; expr_ref xc(m), yc(m); xc = convert(xe); yc = convert(ye); expr_ref c(m); if ((fu.is_float(xe) && fu.is_float(ye))|| (fu.is_rm(xe) && fu.is_rm(ye))) { m_converter.mk_eq(xc, yc, c); c = m.mk_not(c); } else { expr_ref xc_eq_yc(m); xc_eq_yc = m.mk_eq(xc, yc); c = m.mk_not(xc_eq_yc); } m_th_rw(c); expr_ref xe_eq_ye(m), not_xe_eq_ye(m), c_eq_iff(m); xe_eq_ye = m.mk_eq(xe, ye); not_xe_eq_ye = m.mk_not(xe_eq_ye); c_eq_iff = m.mk_iff(not_xe_eq_ye, c); assert_cnstr(c_eq_iff); assert_cnstr(mk_side_conditions()); return; } theory* theory_fpa::mk_fresh(context* new_ctx) { return alloc(theory_fpa, new_ctx->get_manager()); } void theory_fpa::push_scope_eh() { theory::push_scope_eh(); m_trail_stack.push_scope(); } void theory_fpa::pop_scope_eh(unsigned num_scopes) { m_trail_stack.pop_scope(num_scopes); TRACE("t_fpa", tout << "pop " << num_scopes << "; now " << m_trail_stack.get_num_scopes() << "\n";); theory::pop_scope_eh(num_scopes); } void theory_fpa::assign_eh(bool_var v, bool is_true) { ast_manager & m = get_manager(); context & ctx = get_context(); expr * e = ctx.bool_var2expr(v); TRACE("t_fpa", tout << "assign_eh for: " << v << " (" << is_true << "):\n" << mk_ismt2_pp(e, m) << "\n";); expr_ref converted(m); converted = m.mk_and(convert(e), mk_side_conditions()); expr_ref cnstr(m); cnstr = (is_true) ? m.mk_implies(e, converted) : m.mk_implies(converted, e); m_th_rw(cnstr); assert_cnstr(cnstr); } void theory_fpa::relevant_eh(app * n) { ast_manager & m = get_manager(); TRACE("t_fpa", tout << "relevant_eh for: " << mk_ismt2_pp(n, m) << "\n";); mpf_manager & mpfm = m_fpa_util.fm(); if (m_fpa_util.is_float(n) || m_fpa_util.is_rm(n)) { if (!m_fpa_util.is_fp(n)) { expr_ref wrapped(m), c(m); wrapped = wrap(n); mpf_rounding_mode rm; scoped_mpf val(mpfm); if (m_fpa_util.is_rm_numeral(n, rm)) { expr_ref rm_num(m); rm_num = m_bv_util.mk_numeral(rm, 3); c = m.mk_eq(wrapped, rm_num); assert_cnstr(c); } else if (m_fpa_util.is_numeral(n, val)) { expr_ref bv_val_e(m), cc_args(m); bv_val_e = convert(n); SASSERT(is_app(bv_val_e)); SASSERT(to_app(bv_val_e)->get_num_args() == 3); app_ref bv_val_a(m); bv_val_a = to_app(bv_val_e.get()); expr * args[] = { bv_val_a->get_arg(0), bv_val_a->get_arg(1), bv_val_a->get_arg(2) }; cc_args = m_bv_util.mk_concat(3, args); c = m.mk_eq(wrapped, cc_args); assert_cnstr(c); assert_cnstr(mk_side_conditions()); } else { expr_ref wu(m); wu = m.mk_eq(unwrap(wrapped, m.get_sort(n)), n); TRACE("t_fpa", tout << "w/u eq: " << std::endl << mk_ismt2_pp(wu, get_manager()) << std::endl;); assert_cnstr(wu); } } } else if (n->get_family_id() == get_family_id()) { // These are the conversion functions fp.to_* */ SASSERT(!m_fpa_util.is_float(n) && !m_fpa_util.is_rm(n)); } else { /* Theory variables can be merged when (= bv-term (bvwrap fp-term)), in which case context::relevant_eh may call theory_fpa::relevant_eh after theory_bv::relevant_eh, regardless of whether theory_fpa is interested in this term. But, this can only happen because of (bvwrap ...) terms, i.e., `n' must be a bit-vector expression, which we can safely ignore. */ SASSERT(m_bv_util.is_bv(n)); } } void theory_fpa::reset_eh() { TRACE("t_fpa", tout << "reset_eh\n";); pop_scope_eh(m_trail_stack.get_num_scopes()); m_converter.reset(); m_rw.reset(); m_th_rw.reset(); m_trail_stack.pop_scope(m_trail_stack.get_num_scopes()); if (m_factory) { dealloc(m_factory); m_factory = nullptr; } ast_manager & m = get_manager(); dec_ref_map_key_values(m, m_conversions); dec_ref_collection_values(m, m_is_added_to_model); theory::reset_eh(); } final_check_status theory_fpa::final_check_eh() { TRACE("t_fpa", tout << "final_check_eh\n";); SASSERT(m_converter.m_extra_assertions.empty()); return FC_DONE; } void theory_fpa::init_model(model_generator & mg) { TRACE("t_fpa", tout << "initializing model" << std::endl; display(tout);); ast_manager & m = get_manager(); m_factory = alloc(fpa_value_factory, m, get_family_id()); mg.register_factory(m_factory); } model_value_proc * theory_fpa::mk_value(enode * n, model_generator & mg) { TRACE("t_fpa", tout << "mk_value for: " << mk_ismt2_pp(n->get_owner(), get_manager()) << " (sort " << mk_ismt2_pp(get_manager().get_sort(n->get_owner()), get_manager()) << ")\n";); ast_manager & m = get_manager(); context & ctx = get_context(); app_ref owner(m); owner = n->get_owner(); // If the owner is not internalized, it doesn't have an enode associated. SASSERT(ctx.e_internalized(owner)); if (m_fpa_util.is_rm_numeral(owner) || m_fpa_util.is_numeral(owner)) { return alloc(expr_wrapper_proc, owner); } model_value_proc * res = nullptr; app_ref wrapped(m); wrapped = wrap(owner); SASSERT(m_bv_util.is_bv(wrapped)); CTRACE("t_fpa_detail", !ctx.e_internalized(wrapped), tout << "Model dependency not internalized: " << mk_ismt2_pp(wrapped, m) << " (owner " << (!ctx.e_internalized(owner) ? "not" : "is") << " internalized)" << std::endl;); if (m_fpa_util.is_fp(owner)) { SASSERT(to_app(owner)->get_num_args() == 3); app_ref a0(m), a1(m), a2(m); a0 = to_app(owner->get_arg(0)); a1 = to_app(owner->get_arg(1)); a2 = to_app(owner->get_arg(2)); unsigned ebits = m_fpa_util.get_ebits(m.get_sort(owner)); unsigned sbits = m_fpa_util.get_sbits(m.get_sort(owner)); fpa_value_proc * vp = alloc(fpa_value_proc, this, ebits, sbits); vp->add_dependency(ctx.get_enode(a0)); vp->add_dependency(ctx.get_enode(a1)); vp->add_dependency(ctx.get_enode(a2)); TRACE("t_fpa_detail", tout << "Depends on: " << mk_ismt2_pp(a0, m) << " eq. cls. #" << get_enode(a0)->get_root()->get_owner()->get_id() << std::endl << mk_ismt2_pp(a1, m) << " eq. cls. #" << get_enode(a1)->get_root()->get_owner()->get_id() << std::endl << mk_ismt2_pp(a2, m) << " eq. cls. #" << get_enode(a2)->get_root()->get_owner()->get_id() << std::endl;); res = vp; } else if (m_fpa_util.is_bv2rm(owner)) { SASSERT(to_app(owner)->get_num_args() == 1); app_ref a0(m); a0 = to_app(owner->get_arg(0)); fpa_rm_value_proc * vp = alloc(fpa_rm_value_proc, this); vp->add_dependency(ctx.get_enode(a0)); TRACE("t_fpa_detail", tout << "Depends on: " << mk_ismt2_pp(a0, m) << " eq. cls. #" << get_enode(a0)->get_root()->get_owner()->get_id() << std::endl;); res = vp; } else if (ctx.e_internalized(wrapped)) { if (m_fpa_util.is_rm(owner)) { fpa_rm_value_proc * vp = alloc(fpa_rm_value_proc, this); vp->add_dependency(ctx.get_enode(wrapped)); res = vp; } else if (m_fpa_util.is_float(owner)) { unsigned ebits = m_fpa_util.get_ebits(m.get_sort(owner)); unsigned sbits = m_fpa_util.get_sbits(m.get_sort(owner)); fpa_value_proc * vp = alloc(fpa_value_proc, this, ebits, sbits); enode * en = ctx.get_enode(wrapped); vp->add_dependency(en); TRACE("t_fpa_detail", tout << "Depends on: " << mk_ismt2_pp(wrapped, m) << " eq. cls. #" << en->get_root()->get_owner()->get_id() << std::endl;); res = vp; } } else { unsigned ebits = m_fpa_util.get_ebits(m.get_sort(owner)); unsigned sbits = m_fpa_util.get_sbits(m.get_sort(owner)); return alloc(expr_wrapper_proc, m_fpa_util.mk_pzero(ebits, sbits)); } SASSERT(res != 0); return res; } void theory_fpa::finalize_model(model_generator & mg) { ast_manager & m = get_manager(); proto_model & mdl = mg.get_model(); proto_model new_model(m); bv2fpa_converter bv2fp(m, m_converter); obj_hashtable seen; bv2fp.convert_min_max_specials(&mdl, &new_model, seen); bv2fp.convert_uf2bvuf(&mdl, &new_model, seen); for (obj_hashtable::iterator it = seen.begin(); it != seen.end(); it++) mdl.unregister_decl(*it); for (unsigned i = 0; i < new_model.get_num_constants(); i++) { func_decl * f = new_model.get_constant(i); mdl.register_decl(f, new_model.get_const_interp(f)); } for (unsigned i = 0; i < new_model.get_num_functions(); i++) { func_decl * f = new_model.get_function(i); func_interp * fi = new_model.get_func_interp(f)->copy(); mdl.register_decl(f, fi); } } void theory_fpa::display(std::ostream & out) const { ast_manager & m = get_manager(); context & ctx = get_context(); bool first = true; for (enode* n : ctx.enodes()) { theory_var v = n->get_th_var(get_family_id()); if (v != -1) { if (first) out << "fpa theory variables:" << std::endl; out << v << " -> " << mk_ismt2_pp(n->get_owner(), m) << std::endl; first = false; } } // if there are no fpa theory variables, was fp ever used? if (first) return; out << "bv theory variables:" << std::endl; for (enode * n : ctx.enodes()) { theory_var v = n->get_th_var(m_bv_util.get_family_id()); if (v != -1) out << v << " -> " << mk_ismt2_pp(n->get_owner(), m) << std::endl; } out << "arith theory variables:" << std::endl; for (enode* n : ctx.enodes()) { theory_var v = n->get_th_var(m_arith_util.get_family_id()); if (v != -1) out << v << " -> " << mk_ismt2_pp(n->get_owner(), m) << std::endl; } out << "equivalence classes:\n"; for (enode * n : ctx.enodes()) { expr * e = n->get_owner(); expr * r = n->get_root()->get_owner(); out << r->get_id() << " --> " << mk_ismt2_pp(e, m) << std::endl; } } }; z3-z3-4.8.7/src/smt/theory_fpa.h000066400000000000000000000145721356505360400163430ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: theory_fpa.h Abstract: Floating-Point Theory Plugin Author: Christoph (cwinter) 2014-04-23 Revision History: --*/ #ifndef THEORY_FPA_H_ #define THEORY_FPA_H_ #include "smt/smt_theory.h" #include "util/trail.h" #include "ast/fpa/fpa2bv_converter.h" #include "ast/fpa/fpa2bv_rewriter.h" #include "ast/rewriter/th_rewriter.h" #include "model/value_factory.h" #include "smt/smt_model_generator.h" namespace smt { class fpa_value_factory : public value_factory { fpa_util m_util; virtual app * mk_value_core(mpf const & val, sort * s) { SASSERT(m_util.get_ebits(s) == val.get_ebits()); SASSERT(m_util.get_sbits(s) == val.get_sbits()); return m_util.mk_value(val); } public: fpa_value_factory(ast_manager & m, family_id fid) : value_factory(m, fid), m_util(m) {} ~fpa_value_factory() override {} expr * get_some_value(sort * s) override { mpf_manager & mpfm = m_util.fm(); if (m_util.is_rm(s)) return m_util.mk_round_toward_zero(); else { scoped_mpf q(mpfm); mpfm.set(q, m_util.get_ebits(s), m_util.get_sbits(s), 0); return m_util.mk_value(q); } } bool get_some_values(sort * s, expr_ref & v1, expr_ref & v2) override { mpf_manager & mpfm = m_util.fm(); if (m_util.is_rm(s)) v1 = v2 = m_util.mk_round_toward_zero(); else { scoped_mpf q(mpfm); mpfm.set(q, m_util.get_ebits(s), m_util.get_sbits(s), 0); v1 = m_util.mk_value(q); mpfm.set(q, m_util.get_ebits(s), m_util.get_sbits(s), 1); v2 = m_util.mk_value(q); } return true; } expr * get_fresh_value(sort * s) override { return get_some_value(s); } void register_value(expr * n) override { /* Ignore */ } app * mk_value(mpf const & x) { return m_util.mk_value(x); } }; class theory_fpa : public theory { protected: typedef trail_stack th_trail_stack; class fpa2bv_converter_wrapped : public fpa2bv_converter { public: theory_fpa & m_th; fpa2bv_converter_wrapped(ast_manager & m, theory_fpa * th) : fpa2bv_converter(m), m_th(*th) {} virtual ~fpa2bv_converter_wrapped() {} void mk_const(func_decl * f, expr_ref & result) override; void mk_rm_const(func_decl * f, expr_ref & result) override; }; class fpa_value_proc : public model_value_proc { protected: theory_fpa & m_th; ast_manager & m; fpa_util & m_fu; bv_util & m_bu; buffer m_deps; unsigned m_ebits; unsigned m_sbits; public: fpa_value_proc(theory_fpa * th, unsigned ebits, unsigned sbits) : m_th(*th), m(th->get_manager()), m_fu(th->m_fpa_util), m_bu(th->m_bv_util), m_ebits(ebits), m_sbits(sbits) {} ~fpa_value_proc() override {} void add_dependency(enode * e) { m_deps.push_back(model_value_dependency(e)); } void get_dependencies(buffer & result) override { result.append(m_deps); } app * mk_value(model_generator & mg, expr_ref_vector const & values) override; }; class fpa_rm_value_proc : public model_value_proc { theory_fpa & m_th; ast_manager & m; fpa_util & m_fu; bv_util & m_bu; buffer m_deps; public: fpa_rm_value_proc(theory_fpa * th) : m_th(*th), m(th->get_manager()), m_fu(th->m_fpa_util), m_bu(th->m_bv_util) { (void) m_th; } void add_dependency(enode * e) { m_deps.push_back(model_value_dependency(e)); } void get_dependencies(buffer & result) override { result.append(m_deps); } ~fpa_rm_value_proc() override {} app * mk_value(model_generator & mg, expr_ref_vector const & values) override; }; protected: fpa2bv_converter_wrapped m_converter; fpa2bv_rewriter m_rw; th_rewriter m_th_rw; th_trail_stack m_trail_stack; fpa_value_factory * m_factory; fpa_util & m_fpa_util; bv_util & m_bv_util; arith_util & m_arith_util; obj_map m_conversions; bool m_is_initialized; obj_hashtable m_is_added_to_model; final_check_status final_check_eh() override; bool internalize_atom(app * atom, bool gate_ctx) override; bool internalize_term(app * term) override; void apply_sort_cnstr(enode * n, sort * s) override; void new_eq_eh(theory_var, theory_var) override; void new_diseq_eh(theory_var, theory_var) override; void push_scope_eh() override; void pop_scope_eh(unsigned num_scopes) override; void reset_eh() override; theory* mk_fresh(context* new_ctx) override; char const * get_name() const override { return "fpa"; } model_value_proc * mk_value(enode * n, model_generator & mg) override; void assign_eh(bool_var v, bool is_true) override; void relevant_eh(app * n) override; void init_model(model_generator & m) override; void finalize_model(model_generator & mg) override; public: theory_fpa(ast_manager & m); ~theory_fpa() override; void init(context * ctx) override; void display(std::ostream & out) const override; protected: expr_ref mk_side_conditions(); expr_ref convert(expr * e); expr_ref convert_atom(expr * e); expr_ref convert_term(expr * e); expr_ref convert_conversion_term(expr * e); void attach_new_th_var(enode * n); void assert_cnstr(expr * e); app_ref wrap(expr * e); app_ref unwrap(expr * e, sort * s); }; }; #endif /* THEORY_FPA_H_ */ z3-z3-4.8.7/src/smt/theory_jobscheduler.cpp000066400000000000000000001275041356505360400206010ustar00rootroot00000000000000/*++ Copyright (c) 2018 Microsoft Corporation Module Name: theory_jobscheduler.cpp Abstract: Author: Nikolaj Bjorner (nbjorner) 2018-09-08. Revision History: TODO: - arithmetic interface - propagation queue: - register bounds on start times to propagate energy constraints - more general registration mechanism for arithmetic theory. - interact with opt - jobs without resources - complain or add dummy resource? At which level. Features: - properties - priority - try mss based from outside - job-goal - try optimization based on arithmetic solver. - earliest start, latest start - constraint level - add constraints gradually - resource groups - resource groups like a resource - resources bound to resource groups within time intervals - job can require to use K resources from a resource group simultaneously. --*/ #include "ast/ast_pp.h" #include "smt/theory_jobscheduler.h" #include "smt/smt_context.h" #include "smt/smt_arith_value.h" #include "smt/smt_model_generator.h" namespace smt { theory_var theory_jobscheduler::mk_var(enode * n) { theory_var v = theory::mk_var(n); return v; } bool theory_jobscheduler::internalize_term(app * term) { context & ctx = get_context(); if (ctx.e_internalized(term)) return true; for (expr* arg : *term) { if (!ctx.e_internalized(arg)) ctx.internalize(arg, false); } enode* e = ctx.mk_enode(term, false, false, true); theory_var v = mk_var(e); ctx.attach_th_var(e, this, v); ctx.mark_as_relevant(e); return true; } bool theory_jobscheduler::internalize_atom(app * atom, bool gate_ctx) { TRACE("csp", tout << mk_pp(atom, m) << "\n";); context & ctx = get_context(); SASSERT(u.is_model(atom)); for (expr* arg : *atom) { if (!ctx.e_internalized(arg)) ctx.internalize(arg, false); internalize_cmd(arg); } add_done(); bool_var bv = ctx.mk_bool_var(atom); ctx.set_var_theory(bv, get_id()); return true; } struct symbol_cmp { bool operator()(symbol const& s1, symbol const& s2) const { return lt(s1, s2); } }; // TBD: stronger parameter validation void theory_jobscheduler::internalize_cmd(expr* cmd) { symbol key, val; rational r; expr* job, *resource; unsigned j = 0, res = 0, cap = 0, loadpct = 100, level = 0; time_t start = 0, end = std::numeric_limits::max(), capacity = 0; js_job_goal goal; js_optimization_objective obj; properties ps; if (u.is_set_preemptable(cmd, job) && u.is_job(job, j)) { set_preemptable(j, true); } else if (u.is_add_resource_available(cmd, resource, loadpct, cap, start, end, ps) && u.is_resource(resource, res)) { std::sort(ps.begin(), ps.end(), symbol_cmp()); (void) cap; // TBD add_resource_available(res, loadpct, start, end, ps); } else if (u.is_add_job_resource(cmd, job, resource, loadpct, capacity, end, ps) && u.is_job(job, j) && u.is_resource(resource, res)) { std::sort(ps.begin(), ps.end(), symbol_cmp()); add_job_resource(j, res, loadpct, capacity, end, ps); } else if (u.is_job_goal(cmd, goal, level, job) && u.is_job(job, j)) { // skip } else if (u.is_objective(cmd, obj)) { // skip } else { invalid_argument("command not recognized ", cmd); } } void theory_jobscheduler::invalid_argument(char const* msg, expr* arg) { std::ostringstream strm; strm << msg << mk_pp(arg, m); throw default_exception(strm.str()); } void theory_jobscheduler::new_eq_eh(theory_var v1, theory_var v2) { enode* e1 = get_enode(v1); enode* root = e1->get_root(); unsigned r; if (u.is_resource(root->get_owner(), r)) { enode* next = e1; do { unsigned j; if (u.is_job2resource(next->get_owner(), j) && !m_jobs[j].m_is_bound) { m_bound_jobs.push_back(j); m_jobs[j].m_is_bound = true; } next = next->get_next(); } while (e1 != next); } } void theory_jobscheduler::new_diseq_eh(theory_var v1, theory_var v2) { } void theory_jobscheduler::push_scope_eh() { scope s; s.m_bound_jobs_lim = m_bound_jobs.size(); s.m_bound_qhead = m_bound_qhead; m_scopes.push_back(s); } void theory_jobscheduler::pop_scope_eh(unsigned num_scopes) { unsigned new_lvl = m_scopes.size() - num_scopes; scope const& s = m_scopes[new_lvl]; for (unsigned i = s.m_bound_jobs_lim; i < m_bound_jobs.size(); ++i) { unsigned j = m_bound_jobs[i]; m_jobs[j].m_is_bound = false; } m_bound_jobs.shrink(s.m_bound_jobs_lim); m_bound_qhead = s.m_bound_qhead; m_scopes.shrink(new_lvl); } final_check_status theory_jobscheduler::final_check_eh() { TRACE("csp", tout << "\n";); bool blocked = false; for (unsigned j = 0; j < m_jobs.size(); ++j) { if (split_job2resource(j)) { return FC_CONTINUE; } } for (unsigned r = 0; r < m_resources.size(); ++r) { if (constrain_resource_energy(r)) { blocked = true; } } for (unsigned j = 0; j < m_jobs.size(); ++j) { if (constrain_end_time_interval(j, resource(j))) { blocked = true; } } if (blocked) return FC_CONTINUE; return FC_DONE; } bool theory_jobscheduler::can_propagate() { return m_bound_qhead < m_bound_jobs.size(); } literal theory_jobscheduler::mk_literal(expr * e) { expr_ref _e(e, m); context& ctx = get_context(); if (!ctx.e_internalized(e)) { ctx.internalize(e, false); } ctx.mark_as_relevant(ctx.get_enode(e)); return ctx.get_literal(e); } literal theory_jobscheduler::mk_ge(expr* e, time_t t) { return mk_literal(mk_ge_expr(e, t)); } expr* theory_jobscheduler::mk_ge_expr(expr* e, time_t t) { return a.mk_ge(e, a.mk_int(rational(t, rational::ui64()))); } literal theory_jobscheduler::mk_ge(enode* e, time_t t) { return mk_ge(e->get_owner(), t); } literal theory_jobscheduler::mk_le(expr* e, time_t t) { return mk_literal(mk_le_expr(e, t)); } expr* theory_jobscheduler::mk_le_expr(expr* e, time_t t) { return a.mk_le(e, a.mk_int(rational(t, rational::ui64()))); } literal theory_jobscheduler::mk_le(enode* l, enode* r) { context& ctx = get_context(); expr_ref le(a.mk_le(l->get_owner(), r->get_owner()), m); ctx.get_rewriter()(le); return mk_literal(le); } literal theory_jobscheduler::mk_le(enode* e, time_t t) { return mk_le(e->get_owner(), t); } literal theory_jobscheduler::mk_eq_lit(expr * l, expr * r) { literal lit = mk_eq(l, r, false); get_context().mark_as_relevant(lit); return lit; } /** * iterator of job overlaps. */ theory_jobscheduler::job_overlap::job_overlap(vector& starts, vector& ends): m_start(0), m_starts(starts), m_ends(ends), s_idx(0), e_idx(0) { job_time::compare cmp; std::sort(starts.begin(), starts.end(), cmp); std::sort(ends.begin(), ends.end(), cmp); } bool theory_jobscheduler::job_overlap::next() { if (s_idx == m_starts.size()) { return false; } do { m_start = std::max(m_start, m_starts[s_idx].m_time); // add jobs that start before or at m_start while (s_idx < m_starts.size() && m_starts[s_idx].m_time <= m_start) { m_jobs.insert(m_starts[s_idx].m_job); ++s_idx; } // remove jobs that end before m_start. while (e_idx < m_ends.size() && m_ends[e_idx].m_time < m_start) { m_jobs.remove(m_ends[e_idx].m_job); ++e_idx; } } // as long as set of jobs increments, add to m_start while (s_idx < m_starts.size() && e_idx < m_ends.size() && m_starts[s_idx].m_time <= m_ends[e_idx].m_time); return true; } /** * r = resource(j) & start(j) >= slb => end(j) >= ect(j, r, slb) * * note: not used so far * note: subsumed by constrain_end_time_interval used in final-check */ void theory_jobscheduler::propagate_end_time(unsigned j, unsigned r) { time_t slb = est(j); time_t clb = ect(j, r, slb); context& ctx = get_context(); if (clb > end(j)) { job_info const& ji = m_jobs[j]; literal start_ge_lo = mk_ge(ji.m_start, slb); if (ctx.get_assignment(start_ge_lo) != l_true) { return; } enode_pair eq(ji.m_job2resource, m_resources[r].m_resource); if (eq.first->get_root() != eq.second->get_root()) { return; } literal end_ge_lo = mk_ge(ji.m_end, clb); // Initialization ensures that satisfiable states have completion time below end. ast_manager& m = get_manager(); if (m.has_trace_stream()) { app_ref body(m); body = 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())); log_axiom_instantiation(body); m.trace_stream() << "[end-of-instance]\n"; } region& r = ctx.get_region(); ctx.assign(end_ge_lo, ctx.mk_justification( ext_theory_propagation_justification(get_id(), r, 1, &start_ge_lo, 1, &eq, end_ge_lo, 0, nullptr))); } } /** * For time interval [t0, t1] the end-time can be computed as a function * of start time based on resource load availability. * * r = resource(j) & t1 >= start(j) >= t0 => end(j) = start(j) + ect(j, r, t0) - t0 */ 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";); vector& available = m_resources[r].m_available; if (!resource_available(r, s, idx1)) return false; if (!resource_available(jr, available[idx1])) return false; time_t e = ect(j, r, s); TRACE("csp", tout << "job: " << j << " resource: " << r << " ect: " << e << "\n";); if (!resource_available(r, e, idx2)) return false; // infeasible.. if (!resource_available(jr, available[idx2])) return false; time_t start1 = available[idx1].m_start; time_t end1 = available[idx1].m_end; unsigned cap1 = available[idx1].m_loadpct; time_t start2 = available[idx2].m_start; time_t end2 = available[idx2].m_end; unsigned cap2 = available[idx2].m_loadpct; // calculate minimal start1 <= t0 <= s, such that ect(j, r, t0) >= start2 // calculate maximal s <= t1 <= end1, such that ect(j, r, t1) <= end2 time_t delta1 = (s - start1)*cap1; time_t delta2 = (e - start2)*cap2; time_t t0, t1; if (delta1 <= delta2) { t0 = start1; } else { // solve for t0: // (s - t0)*cap1 = (e - start2)*cap2; t0 = s - (delta2 / cap1); } delta1 = (end1 - s)*cap1; delta2 = (end2 - e)*cap2; if (delta1 <= delta2) { t1 = end1; } else { // solve for t1: // (t1 - s)*cap1 = (end2 - e)*cap2 t1 = s + (delta2 / cap1); } time_t delta = ect(j, r, t0) - t0; if (end(j) == start(j) + delta) { return false; } TRACE("csp", tout << "job: " << j << " resource " << r << " t0: " << t0 << " t1: " << t1 << " delta: " << delta << "\n";); literal_vector lits; enode* start_e = m_jobs[j].m_start; enode* end_e = m_jobs[j].m_end; lits.push_back(~mk_eq_lit(m_jobs[j].m_job2resource, m_resources[r].m_resource)); lits.push_back(~mk_ge(start_e, t0)); lits.push_back(~mk_le(start_e, t1)); expr_ref rhs(a.mk_add(start_e->get_owner(), a.mk_int(rational(delta, rational::ui64()))), m); lits.push_back(mk_eq_lit(end_e->get_owner(), rhs)); context& ctx = get_context(); ctx.mk_clause(lits.size(), lits.c_ptr(), nullptr, CLS_TH_LEMMA, nullptr); ast_manager& m = get_manager(); if (m.has_trace_stream()) { app_ref body(m); body = m.mk_implies(m.mk_and(ctx.bool_var2expr(lits[0].var()), ctx.bool_var2expr(lits[1].var()), ctx.bool_var2expr(lits[2].var())), ctx.bool_var2expr(lits[3].var())); log_axiom_instantiation(body); m.trace_stream() << "[end-of-instance]\n"; } return true; } /** * Ensure that job overlaps don't exceed available energy */ bool theory_jobscheduler::constrain_resource_energy(unsigned r) { bool blocked = false; vector starts, ends; res_info const& ri = m_resources[r]; for (unsigned j : ri.m_jobs) { if (resource(j) == r) { starts.push_back(job_time(j, start(j))); ends.push_back(job_time(j, end(j))); } } job_overlap overlap(starts, ends); while (overlap.next()) { unsigned cap = 0; auto const& jobs = overlap.jobs(); for (auto j : jobs) { cap += get_job_resource(j, r).m_loadpct; if (cap > 100) { block_job_overlap(r, jobs, j); blocked = true; goto try_next_overlap; } } try_next_overlap: ; } return blocked; } void theory_jobscheduler::block_job_overlap(unsigned r, uint_set const& jobs, unsigned last_job) { // // block the following case: // each job is assigned to r. // max { start(j) | j0..last_job } <= min { end(j) | j0..last_job } // joint capacity of jobs exceeds availability of resource. // time_t max_start = 0; unsigned max_j = last_job; for (auto j : jobs) { if (max_start < start(j)) { max_start = start(j); max_j = j; } if (j == last_job) break; } literal_vector lits; for (auto j : jobs) { // create literals for: // resource(j) == r // m_jobs[j].m_start <= m_jobs[max_j].m_start; // m_jobs[max_j].m_start <= m_jobs[j].m_end; lits.push_back(~mk_eq_lit(m_jobs[j].m_job2resource, m_resources[r].m_resource)); if (j != max_j) { lits.push_back(~mk_le(m_jobs[j].m_start, m_jobs[max_j].m_start)); lits.push_back(~mk_le(m_jobs[max_j].m_start, m_jobs[j].m_end)); } if (j == last_job) break; } context& ctx = get_context(); ctx.mk_clause(lits.size(), lits.c_ptr(), nullptr, CLS_TH_LEMMA, nullptr); } void theory_jobscheduler::propagate() { while (m_bound_qhead < m_bound_jobs.size()) { unsigned j = m_bound_jobs[m_bound_qhead++]; unsigned r = 0; 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); } } void theory_jobscheduler::propagate_job2resource(unsigned j, unsigned r) { job_info const& ji = m_jobs[j]; res_info const& ri = m_resources[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); vector const& available = ri.m_available; // TBD: needs to take properties into account unsigned i = 0; if (!first_available(jr, ri, i)) return; while (true) { unsigned next = i + 1; if (!first_available(jr, ri, next)) return; SASSERT(available[i].m_end < available[next].m_start); assert_job_not_in_gap(j, r, i, next, eq); if (!ji.m_is_preemptable && available[i].m_end + 1 < available[i+1].m_start) { assert_job_non_preemptable(j, r, i, next, eq); } i = next; } } theory_jobscheduler::theory_jobscheduler(ast_manager& m): theory(m.get_family_id("csp")), m(m), u(m), a(m), m_bound_qhead(0) { } 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_finite_capacity_end; for (auto const& s : jr.m_properties) { out << " " << s; } out << "\n"; } std::ostream& theory_jobscheduler::display(std::ostream & out, job_info const& j) const { for (job_resource const& jr : j.m_resources) { display(out << " ", jr) << "\n"; } return out; } std::ostream& theory_jobscheduler::display(std::ostream & out, res_available const& r) const { return out << "[" << r.m_start << ":" << r.m_end << "] @ " << r.m_loadpct << "%"; for (auto const& s : r.m_properties) { out << " " << s; } out << "\n"; } std::ostream& theory_jobscheduler::display(std::ostream & out, res_info const& r) const { for (res_available const& ra : r.m_available) { display(out << " ", ra); } return out; } void theory_jobscheduler::display(std::ostream & out) const { out << "jobscheduler:\n"; for (unsigned j = 0; j < m_jobs.size(); ++j) { display(out << "job " << j << ":\n", m_jobs[j]) << "\n"; } for (unsigned r = 0; r < m_resources.size(); ++r) { display(out << "resource " << r << ":\n", m_resources[r]) << "\n"; } } void theory_jobscheduler::collect_statistics(::statistics & st) const { } bool theory_jobscheduler::include_func_interp(func_decl* f) { return f->get_decl_kind() == OP_JS_START || f->get_decl_kind() == OP_JS_END || f->get_decl_kind() == OP_JS_JOB2RESOURCE; } void theory_jobscheduler::init_model(model_generator & m) { } model_value_proc * theory_jobscheduler::mk_value(enode * n, model_generator & mg) { return alloc(expr_wrapper_proc, n->get_root()->get_owner()); } bool theory_jobscheduler::get_value(enode * n, expr_ref & r) { std::cout << mk_pp(n->get_owner(), m) << "\n"; return false; } theory * theory_jobscheduler::mk_fresh(context * new_ctx) { return alloc(theory_jobscheduler, new_ctx->get_manager()); } time_t theory_jobscheduler::get_lo(expr* e) { 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()) { return val.get_uint64(); } return 0; } time_t theory_jobscheduler::get_up(expr* e) { 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()) { return val.get_uint64(); } return std::numeric_limits::max(); } time_t theory_jobscheduler::get_value(expr* e) { arith_value av(get_manager()); av.init(&get_context()); rational val; if (av.get_value_equiv(e, val) && val.is_uint64()) { return val.get_uint64(); } return 0; } time_t theory_jobscheduler::est(unsigned j) { return get_lo(m_jobs[j].m_start->get_owner()); } time_t theory_jobscheduler::lst(unsigned j) { return get_up(m_jobs[j].m_start->get_owner()); } time_t theory_jobscheduler::ect(unsigned j) { return get_lo(m_jobs[j].m_end->get_owner()); } time_t theory_jobscheduler::lct(unsigned j) { return get_up(m_jobs[j].m_end->get_owner()); } time_t theory_jobscheduler::start(unsigned j) { return get_value(m_jobs[j].m_start->get_owner()); } time_t theory_jobscheduler::end(unsigned j) { return get_value(m_jobs[j].m_end->get_owner()); } unsigned theory_jobscheduler::resource(unsigned j) { unsigned r; enode* next = m_jobs[j].m_job2resource, *n = next; do { if (u.is_resource(next->get_owner(), r)) { return r; } next = next->get_next(); } while (next != n); return 0; } void theory_jobscheduler::set_preemptable(unsigned j, bool is_preemptable) { ensure_job(j).m_is_preemptable = is_preemptable; } 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); } 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); app_ref end(u.mk_end(j), m); app_ref res(u.mk_job2resource(j), m); if (!ctx.e_internalized(job)) ctx.internalize(job, false); if (!ctx.e_internalized(start)) ctx.internalize(start, false); if (!ctx.e_internalized(end)) ctx.internalize(end, false); if (!ctx.e_internalized(res)) ctx.internalize(res, false); ji.m_job = ctx.get_enode(job); ji.m_start = ctx.get_enode(start); 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, finite_capacity_end, ps)); SASSERT(!ri.m_jobs.contains(j)); ri.m_jobs.push_back(j); } void theory_jobscheduler::add_resource_available(unsigned r, unsigned max_loadpct, time_t start, time_t end, properties const& ps) { SASSERT(get_context().at_base_level()); SASSERT(1 <= max_loadpct && max_loadpct <= 100); SASSERT(start <= end); res_info& ri = ensure_resource(r); ri.m_available.push_back(res_available(max_loadpct, start, end, ps)); } /* * Initialize the state based on the set of jobs and resources added. * Ensure that the availability slots for each resource is sorted by time. * * For each resource j: * est(j) <= start(j) <= end(j) <= lct(j) * * possible strengthenings: * - start(j) <= lst(j) * - start(j) + min_completion_time(j) <= end(j) * - start(j) + max_completion_time(j) >= end(j) * * makespan constraints? * */ void theory_jobscheduler::add_done() { TRACE("csp", tout << "add-done begin\n";); context & ctx = get_context(); // sort availability intervals for (res_info& ri : m_resources) { vector& available = ri.m_available; res_available::compare cmp; std::sort(available.begin(), available.end(), cmp); } literal lit; unsigned job_id = 0; for (job_info const& ji : m_jobs) { if (ji.m_resources.empty()) { throw default_exception("every job should be associated with at least one resource"); } // start(j) <= end(j) lit = mk_le(ji.m_start, ji.m_end); if (m.has_trace_stream()) log_axiom_instantiation(ctx.bool_var2expr(lit.var())); ctx.mk_th_axiom(get_id(), 1, &lit); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; time_t start_lb = std::numeric_limits::max(); time_t runtime_lb = std::numeric_limits::max(); time_t end_ub = 0; // , runtime_ub = 0; 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()) { 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 lit = mk_ge(ji.m_start, start_lb); if (m.has_trace_stream()) log_axiom_instantiation(ctx.bool_var2expr(lit.var())); ctx.mk_th_axiom(get_id(), 1, &lit); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; // end(j) <= end_ub lit = mk_le(ji.m_end, end_ub); if (m.has_trace_stream()) log_axiom_instantiation(ctx.bool_var2expr(lit.var())); ctx.mk_th_axiom(get_id(), 1, &lit); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; // start(j) + runtime_lb <= end(j) // end(j) <= start(j) + runtime_ub ++job_id; } TRACE("csp", tout << "add-done end\n";); } // 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_finite_capacity_end); context& ctx = get_context(); if (m.has_trace_stream()) { app_ref body(m); body = m.mk_implies(ctx.bool_var2expr(eq.var()), ctx.bool_var2expr(l2.var())); log_axiom_instantiation(body); } 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)) void theory_jobscheduler::assert_last_start_time(unsigned j, unsigned r, literal eq) { context& ctx = get_context(); time_t t; if (lst(j, r, t)) { literal le = mk_le(m_jobs[j].m_start, t); if (m.has_trace_stream()) { app_ref body(m); body = m.mk_implies(ctx.bool_var2expr(eq.var()), ctx.bool_var2expr(le.var())); log_axiom_instantiation(body); } ctx.mk_th_axiom(get_id(), ~eq, le); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; } else { eq.neg(); if (m.has_trace_stream()) { app_ref body(m); body = m.mk_not(ctx.bool_var2expr(eq.var())); log_axiom_instantiation(body); } ctx.mk_th_axiom(get_id(), 1, &eq); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; } } // resource(j) = r => start(j) >= available[0].m_start void theory_jobscheduler::assert_first_start_time(unsigned j, unsigned r, literal eq) { job_resource const& jr = get_job_resource(j, r); unsigned idx = 0; if (!first_available(jr, m_resources[r], idx)) return; vector& available = m_resources[r].m_available; literal l2 = mk_ge(m_jobs[j].m_start, available[idx].m_start); context& ctx = get_context(); if (m.has_trace_stream()) { app_ref body(m); body = m.mk_implies(ctx.bool_var2expr(eq.var()), ctx.bool_var2expr(l2.var())); log_axiom_instantiation(body); } ctx.mk_th_axiom(get_id(), ~eq, l2); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; } // resource(j) = r => start(j) <= end[idx] || start[idx+1] <= start(j); void theory_jobscheduler::assert_job_not_in_gap(unsigned j, unsigned r, unsigned idx, unsigned idx1, literal eq) { job_resource const& jr = get_job_resource(j, r); (void) jr; vector& available = m_resources[r].m_available; SASSERT(resource_available(jr, available[idx])); literal l2 = mk_ge(m_jobs[j].m_start, available[idx1].m_start); literal l3 = mk_le(m_jobs[j].m_start, available[idx].m_end); context& ctx = get_context(); if (m.has_trace_stream()) { app_ref body(m); body = m.mk_implies(ctx.bool_var2expr(eq.var()), m.mk_or(ctx.bool_var2expr(l2.var()), ctx.bool_var2expr(l3.var()))); log_axiom_instantiation(body); } ctx.mk_th_axiom(get_id(), ~eq, l2, l3); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; } // resource(j) = r => end(j) <= end[idx] || start[idx+1] <= start(j); void theory_jobscheduler::assert_job_non_preemptable(unsigned j, unsigned r, unsigned idx, unsigned idx1, literal eq) { vector& available = m_resources[r].m_available; job_resource const& jr = get_job_resource(j, r); (void) jr; SASSERT(resource_available(jr, available[idx])); literal l2 = mk_le(m_jobs[j].m_end, available[idx].m_end); literal l3 = mk_ge(m_jobs[j].m_start, available[idx1].m_start); context& ctx = get_context(); if (m.has_trace_stream()) { app_ref body(m); body = m.mk_implies(ctx.bool_var2expr(eq.var()), m.mk_or(ctx.bool_var2expr(l2.var()), ctx.bool_var2expr(l3.var()))); log_axiom_instantiation(body); } ctx.mk_th_axiom(get_id(), ~eq, l2, l3); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; } /** * bind a job to one of the resources it can run on. */ bool theory_jobscheduler::split_job2resource(unsigned j) { job_info const& ji = m_jobs[j]; context& ctx = get_context(); if (ji.m_is_bound) return false; auto const& jrs = ji.m_resources; for (job_resource const& jr : jrs) { unsigned r = jr.m_resource_id; res_info const& ri = m_resources[r]; enode* e1 = ji.m_job2resource; enode* e2 = ri.m_resource; if (ctx.is_diseq(e1, e2)) continue; literal eq = mk_eq_lit(e1, e2); if (m.has_trace_stream()) { app_ref body(m); body = m.mk_or(ctx.bool_var2expr(eq.var()), m.mk_not(ctx.bool_var2expr(eq.var()))); log_axiom_instantiation(body); m.trace_stream() << "[end-of-instance]\n"; } if (ctx.get_assignment(eq) != l_false) { ctx.mark_as_relevant(eq); if (assume_eq(e1, e2)) { return true; } } } literal_vector lits; expr_ref_vector exprs(m); for (job_resource const& jr : jrs) { unsigned r = jr.m_resource_id; res_info const& ri = m_resources[r]; enode* e1 = ji.m_job2resource; enode* e2 = ri.m_resource; literal eq = mk_eq_lit(e1, e2); lits.push_back(eq); exprs.push_back(ctx.bool_var2expr(eq.var())); } if (m.has_trace_stream()) { app_ref body(m); body = m.mk_or(exprs.size(), exprs.c_ptr()); log_axiom_instantiation(body); } ctx.mk_th_axiom(get_id(), lits.size(), lits.c_ptr()); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; return true; } /** * check that each job is run on some resource according to * requested capacity. * * Check that the sum of jobs run in each time instance * does not exceed capacity. */ void theory_jobscheduler::validate_assignment() { vector> start_times, end_times; start_times.reserve(m_resources.size()); end_times.reserve(m_resources.size()); for (unsigned j = 0; j < m_jobs.size(); ++j) { unsigned r = resource(j); start_times[r].push_back(job_time(j, start(j))); end_times[r].push_back(job_time(j, end(j))); if (ect(j, r, start(j)) > end(j)) { throw default_exception("job not assigned full capacity"); } unsigned idx; if (!resource_available(r, start(j), idx)) { throw default_exception("resource is not available at job start time"); } } for (unsigned r = 0; r < m_resources.size(); ++r) { // order jobs running on r by start, end-time intervals // then consume ordered list to find jobs in scope. job_overlap overlap(start_times[r], end_times[r]); while (overlap.next()) { // check that sum of job loads does not exceed 100% unsigned cap = 0; for (auto j : overlap.jobs()) { cap += get_job_resource(j, r).m_loadpct; } if (cap > 100) { throw default_exception("capacity on resource exceeded"); } } } } 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]]; } /** * find idx, if any, such that t is within the time interval of available[idx] */ bool theory_jobscheduler::resource_available(unsigned r, time_t t, unsigned& idx) { vector& available = m_resources[r].m_available; unsigned lo = 0, hi = available.size(), mid = hi / 2; while (lo < hi) { SASSERT(lo <= mid && mid < hi); res_available const& ra = available[mid]; if (ra.m_start <= t && t <= ra.m_end) { idx = mid; return true; } else if (ra.m_start > t && mid > 0) { hi = mid; mid = lo + (mid - lo) / 2; } else if (ra.m_end < t) { lo = mid + 1; mid += (hi - mid) / 2; } else { break; } } return false; } /** * compute earliest completion time for job j on resource r starting at time start. */ time_t theory_jobscheduler::ect(unsigned j, unsigned r, time_t start) { job_resource const& jr = get_job_resource(j, r); vector& available = m_resources[r].m_available; unsigned j_load_pct = jr.m_loadpct; time_t cap = jr.m_capacity; unsigned idx = 0; if (!resource_available(r, start, idx)) { TRACE("csp", tout << "resource is not available for " << j << " " << r << "\n";); return std::numeric_limits::max(); } SASSERT(cap > 0); for (; idx < available.size(); ++idx) { if (!resource_available(jr, available[idx])) continue; start = std::max(start, available[idx].m_start); time_t end = available[idx].m_end; unsigned load_pct = available[idx].m_loadpct; time_t delta = solve_for_capacity(load_pct, j_load_pct, start, end); TRACE("csp", tout << "delta: " << delta << " capacity: " << cap << " load " << load_pct << " jload: " << j_load_pct << " start: " << start << " end " << end << "\n";); if (delta > cap) { end = solve_for_end(load_pct, j_load_pct, start, cap); cap = 0; } else { cap -= delta; } if (cap == 0) { return end; } } return std::numeric_limits::max(); } /** * find end, such that cap = (load / job_load_pct) * (end - start + 1) */ time_t theory_jobscheduler::solve_for_end(unsigned load_pct, unsigned job_load_pct, time_t start, time_t cap) { SASSERT(load_pct > 0); SASSERT(job_load_pct > 0); // cap = (load / job_load_pct) * (end - start + 1) // <=> // end - start + 1 = (cap * job_load_pct) / load // <=> // end = start - 1 + (cap * job_load_pct) / load // <=> // end = (load * (start - 1) + cap * job_load_pct) / load unsigned load = std::min(load_pct, job_load_pct); return (load * (start - 1) + cap * job_load_pct) / load; } /** * find start, such that cap = (load / job_load_pct) * (end - start + 1) */ time_t theory_jobscheduler::solve_for_start(unsigned load_pct, unsigned job_load_pct, time_t end, time_t cap) { SASSERT(load_pct > 0); SASSERT(job_load_pct > 0); // cap = (load / job_load_pct) * (end - start + 1) // <=> // end - start + 1 = (cap * job_load_pct) / load // <=> // start = end + 1 - (cap * job_load_pct) / load // <=> // start = (load * (end + 1) - cap * job_load_pct) / load unsigned load = std::min(load_pct, job_load_pct); return (load * (end + 1) - cap * job_load_pct) / load; } /** * find cap, such that cap = (load / job_load_pct) * (end - start + 1) */ time_t theory_jobscheduler::solve_for_capacity(unsigned load_pct, unsigned job_load_pct, time_t start, time_t end) { SASSERT(job_load_pct > 0); unsigned load = std::min(load_pct, job_load_pct); return (load * (end - start + 1)) / job_load_pct; } /** * Compute last start time for job on resource r. */ bool theory_jobscheduler::lst(unsigned j, unsigned r, time_t& start) { start = 0; job_resource const& jr = get_job_resource(j, r); vector& available = m_resources[r].m_available; unsigned j_load_pct = jr.m_loadpct; time_t cap = jr.m_capacity; for (unsigned idx = available.size(); idx-- > 0; ) { if (!resource_available(jr, available[idx])) continue; start = available[idx].m_start; time_t end = available[idx].m_end; unsigned load_pct = available[idx].m_loadpct; time_t delta = solve_for_capacity(load_pct, j_load_pct, start, end); if (delta > cap) { start = solve_for_start(load_pct, j_load_pct, end, cap); cap = 0; } else { cap -= delta; } if (cap == 0) { return true; } } return false; } /** * \brief check that job properties is a subset of resource properties. * It assumes that both vectors are sorted. */ 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; } unsigned j = 0, i = 0; for (; i < jps.size() && j < rps.size(); ) { if (jps[i] == rps[j]) { ++i; ++j; } else if (lt(rps[j], jps[i])) { ++j; } else { break; } } return i == jps.size(); } /** * \brief minimal current resource available for job resource, includes idx. */ bool theory_jobscheduler::first_available(job_resource const& jr, res_info const& ri, unsigned& idx) const { for (; idx < ri.m_available.size(); ++idx) { if (resource_available(jr, ri.m_available[idx])) return true; } return false; } /** * \brief maximal previous resource available for job resource, excludes idx. */ bool theory_jobscheduler::last_available(job_resource const& jr, res_info const& ri, unsigned& idx) const { while (idx-- > 0) { if (resource_available(jr, ri.m_available[idx])) return true; } return false; } }; z3-z3-4.8.7/src/smt/theory_jobscheduler.h000066400000000000000000000220061356505360400202350ustar00rootroot00000000000000/*++ Copyright (c) 2018 Microsoft Corporation Module Name: theory_jobscheduling.h Abstract: Propagation solver for jobscheduling problems. It relies on an external module to tighten bounds of job variables. Author: Nikolaj Bjorner (nbjorner) 2018-09-08. Revision History: --*/ #pragma once #include "util/uint_set.h" #include "ast/csp_decl_plugin.h" #include "ast/arith_decl_plugin.h" #include "smt/smt_theory.h" namespace smt { typedef uint64_t time_t; class theory_jobscheduler : public theory { public: typedef svector properties; protected: struct job_resource { unsigned m_resource_id; // id of resource time_t m_capacity; // amount of resource to use unsigned m_loadpct; // assuming loadpct 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_finite_capacity_end(end), m_properties(ps) {} }; struct job_time { unsigned m_job; time_t m_time; job_time(unsigned j, time_t time): m_job(j), m_time(time) {} struct compare { bool operator()(job_time const& jt1, job_time const& jt2) const { return jt1.m_time < jt2.m_time; } }; }; struct job_info { bool m_is_preemptable; // can job be pre-empted vector m_resources; // resources allowed to run job. u_map m_resource2index; // resource to index into vector enode* m_job; enode* m_start; enode* m_end; enode* m_job2resource; bool m_is_bound; job_info(): m_is_preemptable(false), m_job(nullptr), m_start(nullptr), m_end(nullptr), m_job2resource(nullptr), m_is_bound(false) {} }; struct res_available { unsigned m_loadpct; time_t m_start; time_t m_end; properties m_properties; res_available(unsigned load_pct, time_t start, time_t end, properties const& ps): m_loadpct(load_pct), m_start(start), m_end(end), m_properties(ps) {} struct compare { bool operator()(res_available const& ra1, res_available const& ra2) const { return ra1.m_start < ra2.m_start; } }; }; struct res_info { unsigned_vector m_jobs; // jobs allocated to run on resource vector m_available; // time intervals where resource is available enode* m_resource; enode* m_makespan; res_info(): m_resource(nullptr), m_makespan(nullptr) {} }; ast_manager& m; csp_util u; arith_util a; vector m_jobs; vector m_resources; unsigned_vector m_bound_jobs; unsigned m_bound_qhead; struct scope { unsigned m_bound_jobs_lim; unsigned m_bound_qhead; }; svector m_scopes; protected: theory_var mk_var(enode * n) override; bool internalize_atom(app * atom, bool gate_ctx) override; bool internalize_term(app * term) override; void assign_eh(bool_var v, bool is_true) override {} void new_eq_eh(theory_var v1, theory_var v2) override; void new_diseq_eh(theory_var v1, theory_var v2) override; void push_scope_eh() override; void pop_scope_eh(unsigned num_scopes) override; final_check_status final_check_eh() override; bool can_propagate() override; void propagate() override; public: theory_jobscheduler(ast_manager& m); ~theory_jobscheduler() override {} void display(std::ostream & out) const override; void collect_statistics(::statistics & st) const override; bool include_func_interp(func_decl* f) override; void init_model(model_generator & m) override; model_value_proc * mk_value(enode * n, model_generator & mg) override; bool get_value(enode * n, expr_ref & r) override; 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); void add_job_resource(unsigned j, unsigned r, unsigned loadpct, time_t cap, time_t end, properties const& ps); void add_resource_available(unsigned r, unsigned max_loadpct, time_t start, time_t end, properties const& ps); void add_done(); // assignments time_t est(unsigned j); // earliest start time of job j time_t lst(unsigned j); // last start time time_t ect(unsigned j); // earliest completion time time_t lct(unsigned j); // last completion time time_t start(unsigned j); // start time of job j time_t end(unsigned j); // end time of job j time_t get_lo(expr* e); time_t get_up(expr* e); time_t get_value(expr* e); unsigned resource(unsigned j); // resource of job j // derived bounds time_t ect(unsigned j, unsigned r, time_t start); bool lst(unsigned j, unsigned r, time_t& t); bool resource_available(job_resource const& jr, res_available const& ra) const; bool first_available(job_resource const& jr, res_info const& ri, unsigned& idx) const; bool last_available(job_resource const& jr, res_info const& ri, unsigned& idx) const; time_t solve_for_start(unsigned load_pct, unsigned job_load_pct, time_t end, time_t cap); time_t solve_for_end(unsigned load_pct, unsigned job_load_pct, time_t start, time_t cap); time_t solve_for_capacity(unsigned load_pct, unsigned job_load_pct, time_t start, time_t end); // validate assignment void validate_assignment(); bool resource_available(unsigned r, time_t t, unsigned& idx); // load available on resource r at time t. 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); void propagate_job2resource(unsigned j, unsigned r); // final check constraints bool constrain_end_time_interval(unsigned j, unsigned r); bool constrain_resource_energy(unsigned r); bool split_job2resource(unsigned j); void assert_last_end_time(unsigned j, unsigned r, job_resource const& jr, literal eq); void assert_last_start_time(unsigned j, unsigned r, literal eq); void assert_first_start_time(unsigned j, unsigned r, literal eq); void assert_job_not_in_gap(unsigned j, unsigned r, unsigned idx, unsigned idx1, literal eq); void assert_job_non_preemptable(unsigned j, unsigned r, unsigned idx, unsigned idx1, literal eq); void block_job_overlap(unsigned r, uint_set const& jobs, unsigned last_job); class job_overlap { time_t m_start; vector & m_starts, &m_ends; unsigned s_idx, e_idx; // index into starts/ends uint_set m_jobs; public: job_overlap(vector& starts, vector& ends); bool next(); uint_set const& jobs() const { return m_jobs; } }; // term builders literal mk_ge(expr* e, time_t t); expr* mk_ge_expr(expr* e, time_t t); literal mk_ge(enode* e, time_t t); literal mk_le(expr* e, time_t t); expr* mk_le_expr(expr* e, time_t t); literal mk_le(enode* e, time_t t); literal mk_le(enode* l, enode* r); literal mk_literal(expr* e); literal mk_eq_lit(enode * l, enode * r) { return mk_eq_lit(l->get_owner(), r->get_owner()); } literal mk_eq_lit(expr * l, expr * r); void internalize_cmd(expr* cmd); void unrecognized_argument(expr* arg) { invalid_argument("unrecognized argument ", arg); } void invalid_argument(char const* msg, expr* arg); std::ostream& display(std::ostream & out, res_info const& r) const; std::ostream& display(std::ostream & out, res_available const& r) const; std::ostream& display(std::ostream & out, job_info const& r) const; std::ostream& display(std::ostream & out, job_resource const& r) const; }; }; z3-z3-4.8.7/src/smt/theory_lra.cpp000066400000000000000000004126641356505360400167120ustar00rootroot00000000000000/*++ Copyright (c) 2016 Microsoft Corporation Module Name: theory_lra.cpp Abstract: Author: Lev Nachmanson (levnach) 2016-25-3 Nikolaj Bjorner (nbjorner) Revision History: --*/ #include "util/stopwatch.h" #include "util/lp/lp_solver.h" #include "util/lp/lp_primal_simplex.h" #include "util/lp/lp_dual_simplex.h" #include "util/lp/indexed_value.h" #include "util/lp/lar_solver.h" #include "util/nat_set.h" #include "util/optional.h" #include "util/lp/lp_params.hpp" #include "util/inf_rational.h" #include "util/cancel_eh.h" #include "util/scoped_timer.h" #include "util/nat_set.h" #include "util/lp/nra_solver.h" #include "ast/ast_pp.h" #include "model/numeral_factory.h" #include "smt/smt_theory.h" #include "smt/smt_context.h" #include "smt/theory_lra.h" #include "smt/smt_model_generator.h" #include "smt/arith_eq_adapter.h" #include "tactic/generic_model_converter.h" #include "math/polynomial/algebraic_numbers.h" #include "math/polynomial/polynomial.h" namespace lp_api { enum bound_kind { lower_t, upper_t }; std::ostream& operator<<(std::ostream& out, bound_kind const& k) { switch (k) { case lower_t: return out << "<="; case upper_t: return out << ">="; } return out; } class bound { smt::bool_var m_bv; smt::theory_var m_var; bool m_is_int; rational m_value; bound_kind m_bound_kind; public: bound(smt::bool_var bv, smt::theory_var v, bool is_int, rational const & val, bound_kind k): m_bv(bv), m_var(v), m_is_int(is_int), m_value(val), m_bound_kind(k) { } virtual ~bound() {} smt::theory_var get_var() const { return m_var; } smt::bool_var get_bv() const { return m_bv; } bound_kind get_bound_kind() const { return m_bound_kind; } bool is_int() const { return m_is_int; } rational const& get_value() const { return m_value; } inf_rational get_value(bool is_true) const { if (is_true) return inf_rational(m_value); // v >= value or v <= value if (m_is_int) { SASSERT(m_value.is_int()); if (m_bound_kind == lower_t) return inf_rational(m_value - rational::one()); // v <= value - 1 return inf_rational(m_value + rational::one()); // v >= value + 1 } else { if (m_bound_kind == lower_t) return inf_rational(m_value, false); // v <= value - epsilon return inf_rational(m_value, true); // v >= value + epsilon } } virtual std::ostream& display(std::ostream& out) const { return out << m_value << " " << get_bound_kind() << " v" << get_var(); } }; std::ostream& operator<<(std::ostream& out, bound const& b) { return b.display(out); } struct stats { unsigned m_assert_lower; unsigned m_assert_upper; unsigned m_add_rows; unsigned m_bounds_propagations; unsigned m_num_iterations; unsigned m_num_iterations_with_no_progress; unsigned m_need_to_solve_inf; unsigned m_fixed_eqs; unsigned m_conflicts; unsigned m_bound_propagations1; unsigned m_bound_propagations2; unsigned m_assert_diseq; unsigned m_gomory_cuts; stats() { reset(); } void reset() { memset(this, 0, sizeof(*this)); } }; typedef optional opt_inf_rational; } namespace smt { typedef ptr_vector lp_bounds; class theory_lra::imp { struct scope { unsigned m_bounds_lim; unsigned m_idiv_lim; unsigned m_asserted_qhead; unsigned m_asserted_atoms_lim; unsigned m_underspecified_lim; unsigned m_var_trail_lim; expr* m_not_handled; }; struct delayed_atom { unsigned m_bv; bool m_is_true; delayed_atom(unsigned b, bool t): m_bv(b), m_is_true(t) {} }; class resource_limit : public lp::lp_resource_limit { imp& m_imp; public: resource_limit(imp& i): m_imp(i) { } bool get_cancel_flag() override { return m_imp.m.canceled(); } }; theory_lra& th; ast_manager& m; theory_arith_params& m_arith_params; arith_util a; bool m_has_int; arith_eq_adapter m_arith_eq_adapter; vector m_columns; // temporary values kept during internalization struct internalize_state { expr_ref_vector m_terms; vector m_coeffs; svector m_vars; rational m_offset; ptr_vector m_terms_to_internalize; internalize_state(ast_manager& m): m_terms(m) {} void reset() { m_terms.reset(); m_coeffs.reset(); m_offset.reset(); m_vars.reset(); m_terms_to_internalize.reset(); } }; ptr_vector m_internalize_states; unsigned m_internalize_head; class scoped_internalize_state { imp& m_imp; internalize_state& m_st; internalize_state& push_internalize(imp& i) { if (i.m_internalize_head == i.m_internalize_states.size()) { i.m_internalize_states.push_back(alloc(internalize_state, i.m)); } internalize_state& st = *i.m_internalize_states[i.m_internalize_head++]; st.reset(); return st; } public: scoped_internalize_state(imp& i): m_imp(i), m_st(push_internalize(i)) {} ~scoped_internalize_state() { --m_imp.m_internalize_head; } expr_ref_vector& terms() { return m_st.m_terms; } vector& coeffs() { return m_st.m_coeffs; } svector& vars() { return m_st.m_vars; } rational& offset() { return m_st.m_offset; } ptr_vector& terms_to_internalize() { return m_st.m_terms_to_internalize; } void push(expr* e, rational c) { m_st.m_terms.push_back(e); m_st.m_coeffs.push_back(c); } void set_back(unsigned i) { if (terms().size() == i + 1) return; terms()[i] = terms().back(); coeffs()[i] = coeffs().back(); terms().pop_back(); coeffs().pop_back(); } }; typedef vector> var_coeffs; svector m_theory_var2var_index; // translate from theory variables to lar vars svector m_var_index2theory_var; // reverse map from lp_solver variables to theory variables svector m_term_index2theory_var; // reverse map from lp_solver variables to theory variables var_coeffs m_left_side; // constraint left side mutable std::unordered_map m_variable_values; // current model lp::var_index m_one_var; lp::var_index m_zero_var; lp::var_index m_rone_var; lp::var_index m_rzero_var; enum constraint_source { inequality_source, equality_source, definition_source, null_source }; svector m_constraint_sources; svector m_inequalities; // asserted rows corresponding to inequality literals. svector m_equalities; // asserted rows corresponding to equalities. svector m_definitions; // asserted rows corresponding to definitions svector m_asserted_atoms; expr* m_not_handled; ptr_vector m_underspecified; ptr_vector m_idiv_terms; unsigned_vector m_var_trail; vector > m_use_list; // bounds where variables are used. // attributes for incremental version: u_map m_bool_var2bound; vector m_bounds; unsigned_vector m_unassigned_bounds; unsigned_vector m_bounds_trail; unsigned m_asserted_qhead; svector m_to_check; // rows that should be checked for theory propagation svector > m_assume_eq_candidates; unsigned m_assume_eq_head; unsigned m_num_conflicts; // non-linear arithmetic scoped_ptr m_nra; bool m_use_nra_model; scoped_ptr m_a1, m_a2; // integer arithmetic scoped_ptr m_lia; struct var_value_eq { imp & m_th; var_value_eq(imp & th):m_th(th) {} bool operator()(theory_var v1, theory_var v2) const { if (m_th.is_int(v1) != m_th.is_int(v2)) { return false; } return m_th.is_eq(v1, v2); } }; struct var_value_hash { imp & m_th; var_value_hash(imp & th):m_th(th) {} unsigned operator()(theory_var v) const { if (m_th.m_use_nra_model) { return m_th.is_int(v); } else { return (unsigned)std::hash()(m_th.get_value(v)); } } }; int_hashtable m_model_eqs; svector m_scopes; lp_api::stats m_stats; arith_factory* m_factory; scoped_ptr m_solver; resource_limit m_resource_limit; lp_bounds m_new_bounds; context& ctx() const { return th.get_context(); } theory_id get_id() const { return th.get_id(); } bool is_int(theory_var v) const { return is_int(get_enode(v)); } bool is_int(enode* n) const { return a.is_int(n->get_owner()); } enode* get_enode(theory_var v) const { return th.get_enode(v); } enode* get_enode(expr* e) const { return ctx().get_enode(e); } expr* get_owner(theory_var v) const { return get_enode(v)->get_owner(); } void init_solver() { if (m_solver) return; reset_variable_values(); m_theory_var2var_index.reset(); m_solver = alloc(lp::lar_solver); lp_params lp(ctx().get_params()); m_solver->settings().set_resource_limit(m_resource_limit); m_solver->settings().simplex_strategy() = static_cast(lp.simplex_strategy()); m_solver->settings().bound_propagation() = BP_NONE != propagation_mode(); m_solver->settings().m_enable_hnf = lp.enable_hnf(); m_solver->set_track_pivoted_rows(lp.bprop_on_pivoted_rows()); // todo : do not use m_arith_branch_cut_ratio for deciding on cheap cuts unsigned branch_cut_ratio = ctx().get_fparams().m_arith_branch_cut_ratio; m_solver->set_cut_strategy(branch_cut_ratio); 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() { if (!m_nra) { m_nra = alloc(nra::solver, *m_solver.get(), m.limit(), ctx().get_params()); for (auto const& _s : m_scopes) { (void)_s; m_nra->push(); } } } lp::var_index add_const(int c, lp::var_index& var, bool is_int) { if (var != UINT_MAX) { return var; } app_ref cnst(a.mk_numeral(rational(c), is_int), m); TRACE("arith", tout << "add " << cnst << "\n";); mk_enode(cnst); theory_var v = mk_var(cnst); var = m_solver->add_var(v, true); m_theory_var2var_index.setx(v, var, UINT_MAX); m_var_index2theory_var.setx(var, v, UINT_MAX); m_var_trail.push_back(v); add_def_constraint(m_solver->add_var_bound(var, lp::GE, rational(c))); add_def_constraint(m_solver->add_var_bound(var, lp::LE, rational(c))); return var; } lp::var_index get_one(bool is_int) { return add_const(1, is_int ? m_one_var : m_rone_var, is_int); } lp::var_index get_zero(bool is_int) { return add_const(0, is_int ? m_zero_var : m_rzero_var, is_int); } void found_not_handled(expr* n) { m_not_handled = n; if (is_app(n) && is_underspecified(to_app(n))) { TRACE("arith", tout << "Unhandled: " << mk_pp(n, m) << "\n";); m_underspecified.push_back(to_app(n)); } } bool is_numeral(expr* term, rational& r) { rational mul(1); do { if (a.is_numeral(term, r)) { r *= mul; return true; } if (a.is_uminus(term, term)) { mul.neg(); continue; } if (a.is_to_real(term, term)) { continue; } return false; } while (false); return false; } void linearize_term(expr* term, scoped_internalize_state& st) { st.push(term, rational::one()); linearize(st); } void linearize_ineq(expr* lhs, expr* rhs, scoped_internalize_state& st) { st.push(lhs, rational::one()); st.push(rhs, rational::minus_one()); linearize(st); } void linearize(scoped_internalize_state& st) { expr_ref_vector & terms = st.terms(); svector& vars = st.vars(); vector& coeffs = st.coeffs(); rational& offset = st.offset(); rational r; expr* n1, *n2; unsigned index = 0; while (index < terms.size()) { SASSERT(index >= vars.size()); expr* n = terms[index].get(); st.terms_to_internalize().push_back(n); if (a.is_add(n)) { for (expr* arg : *to_app(n)) { st.push(arg, coeffs[index]); } st.set_back(index); } else if (a.is_sub(n)) { unsigned sz = to_app(n)->get_num_args(); terms[index] = to_app(n)->get_arg(0); for (unsigned i = 1; i < sz; ++i) { st.push(to_app(n)->get_arg(i), -coeffs[index]); } } else if (a.is_mul(n, n1, n2) && is_numeral(n1, r)) { coeffs[index] *= r; terms[index] = n2; st.terms_to_internalize().push_back(n1); } else if (a.is_mul(n, n1, n2) && is_numeral(n2, r)) { coeffs[index] *= r; terms[index] = n1; st.terms_to_internalize().push_back(n2); } else if (a.is_mul(n)) { theory_var v; internalize_mul(to_app(n), v, r); coeffs[index] *= r; coeffs[vars.size()] = coeffs[index]; vars.push_back(v); ++index; } else if (a.is_numeral(n, r)) { offset += coeffs[index]*r; ++index; } else if (a.is_uminus(n, n1)) { coeffs[index].neg(); terms[index] = n1; } else if (is_app(n) && a.get_family_id() == to_app(n)->get_family_id()) { bool is_first = !ctx().e_internalized(n); app* t = to_app(n); internalize_args(t); mk_enode(t); theory_var v = mk_var(n); coeffs[vars.size()] = coeffs[index]; vars.push_back(v); ++index; if (!is_first) { // skip recursive internalization } else if (a.is_to_int(n, n1)) { if (!ctx().relevancy()) mk_to_int_axiom(t); } else if (a.is_to_real(n, n1)) { theory_var v1 = mk_var(n1); internalize_eq(v, v1); } else if (a.is_idiv(n, n1, n2)) { if (!a.is_numeral(n2, r)) found_not_handled(n); m_idiv_terms.push_back(n); app * mod = a.mk_mod(n1, n2); ctx().internalize(mod, false); if (ctx().relevancy()) ctx().add_relevancy_dependency(n, mod); } else if (a.is_mod(n, n1, n2)) { if (!ctx().relevancy()) mk_idiv_mod_axioms(n1, n2); } else if (a.is_rem(n, n1, n2)) { if (!a.is_numeral(n2, r)) found_not_handled(n); if (!ctx().relevancy()) mk_rem_axiom(n1, n2); } else if (a.is_div(n, n1, n2)) { if (!a.is_numeral(n2, r)) found_not_handled(n); if (!ctx().relevancy()) mk_div_axiom(n1, n2); } else if (a.is_power(n)) { found_not_handled(n); } else { found_not_handled(n); } } else { if (is_app(n)) { internalize_args(to_app(n)); } theory_var v = mk_var(n); coeffs[vars.size()] = coeffs[index]; vars.push_back(v); ++index; } } for (unsigned i = st.terms_to_internalize().size(); i-- > 0; ) { expr* n = st.terms_to_internalize()[i]; if (is_app(n)) { mk_enode(to_app(n)); } } st.terms_to_internalize().reset(); } void internalize_args(app* t) { for (unsigned i = 0; reflect(t) && i < t->get_num_args(); ++i) { if (!ctx().e_internalized(t->get_arg(i))) { ctx().internalize(t->get_arg(i), false); } } } void internalize_mul(app* t, theory_var& v, rational& r) { SASSERT(a.is_mul(t)); bool _has_var = has_var(t); if (!_has_var) { internalize_args(t); mk_enode(t); } r = rational::one(); rational r1; v = mk_var(t); svector vars; ptr_buffer todo; todo.push_back(t); while (!todo.empty()) { expr* n = todo.back(); todo.pop_back(); if (a.is_mul(n)) { for (expr* arg : *to_app(n)) { todo.push_back(arg); } } else if (a.is_numeral(n, r1)) { r *= r1; } else { if (!ctx().e_internalized(n)) { ctx().internalize(n, false); } vars.push_back(get_var_index(mk_var(n))); } } TRACE("arith", tout << "v" << v << "(" << get_var_index(v) << ") := " << mk_pp(t, m) << " " << _has_var << "\n";); if (!_has_var) { ensure_nra(); m_nra->add_monomial(get_var_index(v), vars.size(), vars.c_ptr()); } } enode * mk_enode(app * n) { TRACE("arith", tout << expr_ref(n, m) << "\n";); if (ctx().e_internalized(n)) { return get_enode(n); } else { return ctx().mk_enode(n, !reflect(n), false, enable_cgc_for(n)); } } bool enable_cgc_for(app * n) const { // Congruence closure is not enabled for (+ ...) and (* ...) applications. return !(n->get_family_id() == get_id() && (n->get_decl_kind() == OP_ADD || n->get_decl_kind() == OP_MUL)); } void mk_clause(literal l1, literal l2, unsigned num_params, parameter * params) { TRACE("arith", literal lits[2]; lits[0] = l1; lits[1] = l2; ctx().display_literals_verbose(tout, 2, lits); tout << "\n";); ctx().mk_th_axiom(get_id(), l1, l2, num_params, params); } void mk_clause(literal l1, literal l2, literal l3, unsigned num_params, parameter * params) { TRACE("arith", literal lits[3]; lits[0] = l1; lits[1] = l2; lits[2] = l3; ctx().display_literals_verbose(tout, 3, lits); tout << "\n";); ctx().mk_th_axiom(get_id(), l1, l2, l3, num_params, params); } bool is_underspecified(app* n) const { if (n->get_family_id() == get_id()) { switch (n->get_decl_kind()) { case OP_DIV: case OP_IDIV: case OP_REM: case OP_MOD: return true; default: break; } } return false; } bool reflect(app* n) const { return m_arith_params.m_arith_reflect || is_underspecified(n); } bool has_var(expr* n) { if (!ctx().e_internalized(n)) { return false; } enode* e = get_enode(n); return th.is_attached_to_var(e); } theory_var mk_var(expr* n, bool internalize = true) { if (!ctx().e_internalized(n)) { ctx().internalize(n, false); } enode* e = get_enode(n); theory_var v; if (!th.is_attached_to_var(e)) { v = th.mk_var(e); SASSERT(m_bounds.size() <= static_cast(v) || m_bounds[v].empty()); if (m_bounds.size() <= static_cast(v)) { m_bounds.push_back(lp_bounds()); m_unassigned_bounds.push_back(0); } ctx().attach_th_var(e, &th, v); } else { v = e->get_th_var(get_id()); } SASSERT(null_theory_var != v); return v; } lp::var_index get_var_index(theory_var v) { lp::var_index result = UINT_MAX; if (m_theory_var2var_index.size() > static_cast(v)) { result = m_theory_var2var_index[v]; } if (result == UINT_MAX) { result = m_solver->add_var(v, is_int(v)); m_has_int |= is_int(v); m_theory_var2var_index.setx(v, result, UINT_MAX); m_var_index2theory_var.setx(result, v, UINT_MAX); m_var_trail.push_back(v); } return result; } void init_left_side(scoped_internalize_state& st) { SASSERT(all_zeros(m_columns)); svector const& vars = st.vars(); vector const& coeffs = st.coeffs(); for (unsigned i = 0; i < vars.size(); ++i) { theory_var var = vars[i]; rational const& coeff = coeffs[i]; if (m_columns.size() <= static_cast(var)) { m_columns.setx(var, coeff, rational::zero()); } else { m_columns[var] += coeff; } } m_left_side.clear(); // reset the coefficients after they have been used. for (unsigned i = 0; i < vars.size(); ++i) { theory_var var = vars[i]; rational const& r = m_columns[var]; if (!r.is_zero()) { m_left_side.push_back(std::make_pair(r, get_var_index(var))); m_columns[var].reset(); } } SASSERT(all_zeros(m_columns)); } bool all_zeros(vector const& v) const { for (rational const& r : v) { if (!r.is_zero()) { return false; } } return true; } void add_eq_constraint(lp::constraint_index index, enode* n1, enode* n2) { m_constraint_sources.setx(index, equality_source, null_source); m_equalities.setx(index, enode_pair(n1, n2), enode_pair(0, 0)); ++m_stats.m_add_rows; } void add_ineq_constraint(lp::constraint_index index, literal lit) { m_constraint_sources.setx(index, inequality_source, null_source); m_inequalities.setx(index, lit, null_literal); ++m_stats.m_add_rows; TRACE("arith", m_solver->print_constraint(index, tout) << "\n";); } void add_def_constraint(lp::constraint_index index) { m_constraint_sources.setx(index, definition_source, null_source); m_definitions.setx(index, null_theory_var, null_theory_var); ++m_stats.m_add_rows; } void add_def_constraint(lp::constraint_index index, theory_var v) { m_constraint_sources.setx(index, definition_source, null_source); m_definitions.setx(index, v, null_theory_var); ++m_stats.m_add_rows; } void internalize_eq(theory_var v1, theory_var v2) { app_ref term(m.mk_fresh_const("eq", a.mk_real()), m); scoped_internalize_state st(*this); st.vars().push_back(v1); st.vars().push_back(v2); st.coeffs().push_back(rational::one()); st.coeffs().push_back(rational::minus_one()); theory_var z = internalize_linearized_def(term, st); lp::var_index vi = get_var_index(z); add_def_constraint(m_solver->add_var_bound(vi, lp::LE, rational::zero())); add_def_constraint(m_solver->add_var_bound(vi, lp::GE, rational::zero())); TRACE("arith", { expr* o1 = get_enode(v1)->get_owner(); expr* o2 = get_enode(v2)->get_owner(); tout << "v" << v1 << " = " << "v" << v2 << ": " << mk_pp(o1, m) << " = " << mk_pp(o2, m) << "\n"; }); } void del_bounds(unsigned old_size) { for (unsigned i = m_bounds_trail.size(); i > old_size; ) { --i; unsigned v = m_bounds_trail[i]; lp_api::bound* b = m_bounds[v].back(); // del_use_lists(b); dealloc(b); m_bounds[v].pop_back(); } m_bounds_trail.shrink(old_size); } void updt_unassigned_bounds(theory_var v, int inc) { TRACE("arith", tout << "v" << v << " " << m_unassigned_bounds[v] << " += " << inc << "\n";); ctx().push_trail(vector_value_trail(m_unassigned_bounds, v)); m_unassigned_bounds[v] += inc; } bool is_unit_var(scoped_internalize_state& st) { return st.offset().is_zero() && st.vars().size() == 1 && st.coeffs()[0].is_one(); } bool is_one(scoped_internalize_state& st) { return st.offset().is_one() && st.vars().empty(); } bool is_zero(scoped_internalize_state& st) { return st.offset().is_zero() && st.vars().empty(); } theory_var internalize_def(app* term, scoped_internalize_state& st) { TRACE("arith", tout << expr_ref(term, m) << "\n";); if (ctx().e_internalized(term)) { IF_VERBOSE(0, verbose_stream() << "repeated term\n";); return mk_var(term, false); } linearize_term(term, st); if (is_unit_var(st)) { return st.vars()[0]; } else { theory_var v = mk_var(term); SASSERT(null_theory_var != v); st.coeffs().resize(st.vars().size() + 1); st.coeffs()[st.vars().size()] = rational::minus_one(); st.vars().push_back(v); return v; } } // term - v = 0 theory_var internalize_def(app* term) { scoped_internalize_state st(*this); linearize_term(term, st); return internalize_linearized_def(term, st); } theory_var internalize_linearized_def(app* term, scoped_internalize_state& st) { if (is_unit_var(st)) { return st.vars()[0]; } else if (is_one(st)) { return get_one(a.is_int(term)); } else if (is_zero(st)) { return get_zero(a.is_int(term)); } else { init_left_side(st); theory_var v = mk_var(term); lp::var_index vi = m_theory_var2var_index.get(v, UINT_MAX); TRACE("arith", tout << mk_pp(term, m) << " v" << v << "\n";); if (vi == UINT_MAX) { rational const& offset = st.offset(); if (!offset.is_zero()) { m_left_side.push_back(std::make_pair(offset, get_one(a.is_int(term)))); } SASSERT(!m_left_side.empty()); vi = m_solver->add_term(m_left_side); m_theory_var2var_index.setx(v, vi, UINT_MAX); if (m_solver->is_term(vi)) { m_term_index2theory_var.setx(m_solver->adjust_term_index(vi), v, UINT_MAX); } else { m_var_index2theory_var.setx(vi, v, UINT_MAX); } m_var_trail.push_back(v); TRACE("arith_verbose", tout << "v" << v << " := " << mk_pp(term, m) << " slack: " << vi << " scopes: " << m_scopes.size() << "\n"; m_solver->print_term(m_solver->get_term(vi), tout) << "\n";); } rational val; if (a.is_numeral(term, val)) { m_fixed_var_table.insert(value_sort_pair(val, is_int(v)), v); } return v; } } public: imp(theory_lra& th, ast_manager& m, theory_arith_params& ap): th(th), m(m), m_arith_params(ap), a(m), m_has_int(false), m_arith_eq_adapter(th, ap, a), m_internalize_head(0), m_one_var(UINT_MAX), m_zero_var(UINT_MAX), m_rone_var(UINT_MAX), m_rzero_var(UINT_MAX), m_not_handled(nullptr), m_asserted_qhead(0), m_assume_eq_head(0), m_num_conflicts(0), m_use_nra_model(false), m_model_eqs(DEFAULT_HASHTABLE_INITIAL_CAPACITY, var_value_hash(*this), var_value_eq(*this)), m_solver(nullptr), m_resource_limit(*this) { } ~imp() { del_bounds(0); std::for_each(m_internalize_states.begin(), m_internalize_states.end(), delete_proc()); } void init(context* ctx) { init_solver(); } void internalize_is_int(app * n) { SASSERT(a.is_is_int(n)); (void) mk_enode(n); if (!ctx().relevancy()) mk_is_int_axiom(n); } bool internalize_atom(app * atom, bool gate_ctx) { SASSERT(!ctx().b_internalized(atom)); bool_var bv = ctx().mk_bool_var(atom); ctx().set_var_theory(bv, get_id()); expr* n1, *n2; rational r; lp_api::bound_kind k; theory_var v = null_theory_var; if (a.is_le(atom, n1, n2) && is_numeral(n2, r) && is_app(n1)) { v = internalize_def(to_app(n1)); k = lp_api::upper_t; } else if (a.is_ge(atom, n1, n2) && is_numeral(n2, r) && is_app(n1)) { v = internalize_def(to_app(n1)); k = lp_api::lower_t; } else if (a.is_is_int(atom)) { internalize_is_int(atom); return true; } else { TRACE("arith", tout << "Could not internalize " << mk_pp(atom, m) << "\n";); found_not_handled(atom); return true; } lp_api::bound* b = alloc(lp_api::bound, bv, v, is_int(v), r, k); m_bounds[v].push_back(b); updt_unassigned_bounds(v, +1); m_bounds_trail.push_back(v); m_bool_var2bound.insert(bv, b); TRACE("arith_verbose", tout << "Internalized " << mk_pp(atom, m) << "\n";); mk_bound_axioms(*b); //add_use_lists(b); return true; } bool internalize_term(app * term) { if (ctx().e_internalized(term) && th.is_attached_to_var(ctx().get_enode(term))) { // skip } else { internalize_def(term); } return true; } bool is_arith(enode* n) { return n && n->get_th_var(get_id()) != null_theory_var; } void internalize_eq_eh(app * atom, bool_var) { TRACE("arith_verbose", tout << mk_pp(atom, m) << "\n";); expr* lhs = nullptr, *rhs = nullptr; VERIFY(m.is_eq(atom, lhs, rhs)); enode * n1 = get_enode(lhs); enode * n2 = get_enode(rhs); if (is_arith(n1) && is_arith(n2) && n1 != n2) { m_arith_eq_adapter.mk_axioms(n1, n2); } } void assign_eh(bool_var v, bool is_true) { TRACE("arith", tout << mk_pp(ctx().bool_var2expr(v), m) << " " << (is_true?"true":"false") << "\n";); m_asserted_atoms.push_back(delayed_atom(v, is_true)); } lbool get_phase(bool_var v) { lp_api::bound* b; if (!m_bool_var2bound.find(v, b)) { return l_undef; } lp::lconstraint_kind k = lp::EQ; switch (b->get_bound_kind()) { case lp_api::lower_t: k = lp::GE; break; case lp_api::upper_t: k = lp::LE; break; default: break; } auto vi = get_var_index(b->get_var()); return m_solver->compare_values(vi, k, b->get_value()) ? l_true : l_false; } void new_eq_eh(theory_var v1, theory_var v2) { // or internalize_eq(v1, v2); m_arith_eq_adapter.new_eq_eh(v1, v2); } bool use_diseqs() const { return true; } void new_diseq_eh(theory_var v1, theory_var v2) { TRACE("arith", tout << "v" << v1 << " != " << "v" << v2 << "\n";); ++m_stats.m_assert_diseq; m_arith_eq_adapter.new_diseq_eh(v1, v2); } void push_scope_eh() { TRACE("arith", tout << "push\n";); m_scopes.push_back(scope()); scope& s = m_scopes.back(); s.m_bounds_lim = m_bounds_trail.size(); s.m_asserted_qhead = m_asserted_qhead; s.m_idiv_lim = m_idiv_terms.size(); s.m_asserted_atoms_lim = m_asserted_atoms.size(); s.m_not_handled = m_not_handled; s.m_underspecified_lim = m_underspecified.size(); s.m_var_trail_lim = m_var_trail.size(); m_solver->push(); if (m_nra) m_nra->push(); } void pop_scope_eh(unsigned num_scopes) { TRACE("arith", tout << "pop " << num_scopes << "\n";); if (num_scopes == 0) { return; } unsigned old_size = m_scopes.size() - num_scopes; del_bounds(m_scopes[old_size].m_bounds_lim); for (unsigned i = m_scopes[old_size].m_var_trail_lim; i < m_var_trail.size(); ++i) { lp::var_index vi = m_theory_var2var_index[m_var_trail[i]]; if (m_solver->is_term(vi)) { unsigned ti = m_solver->adjust_term_index(vi); m_term_index2theory_var[ti] = UINT_MAX; } else if (vi < m_var_index2theory_var.size()) { m_var_index2theory_var[vi] = UINT_MAX; } m_theory_var2var_index[m_var_trail[i]] = UINT_MAX; } m_idiv_terms.shrink(m_scopes[old_size].m_idiv_lim); m_asserted_atoms.shrink(m_scopes[old_size].m_asserted_atoms_lim); m_asserted_qhead = m_scopes[old_size].m_asserted_qhead; m_underspecified.shrink(m_scopes[old_size].m_underspecified_lim); m_var_trail.shrink(m_scopes[old_size].m_var_trail_lim); m_not_handled = m_scopes[old_size].m_not_handled; m_scopes.resize(old_size); m_solver->pop(num_scopes); // VERIFY(l_false != make_feasible()); m_new_bounds.reset(); m_to_check.reset(); if (m_nra) m_nra->pop(num_scopes); TRACE("arith", tout << "num scopes: " << num_scopes << " new scope level: " << m_scopes.size() << "\n";); } void restart_eh() { m_arith_eq_adapter.restart_eh(); } void relevant_eh(app* n) { TRACE("arith", tout << mk_pp(n, m) << "\n";); expr* n1, *n2; if (a.is_mod(n, n1, n2)) mk_idiv_mod_axioms(n1, n2); else if (a.is_rem(n, n1, n2)) mk_rem_axiom(n1, n2); else if (a.is_div(n, n1, n2)) mk_div_axiom(n1, n2); else if (a.is_to_int(n)) mk_to_int_axiom(n); else if (a.is_is_int(n)) mk_is_int_axiom(n); } // n < 0 || rem(a, n) = mod(a, n) // !n < 0 || rem(a, n) = -mod(a, n) void mk_rem_axiom(expr* dividend, expr* divisor) { expr_ref zero(a.mk_int(0), m); expr_ref rem(a.mk_rem(dividend, divisor), m); expr_ref mod(a.mk_mod(dividend, divisor), m); expr_ref mmod(a.mk_uminus(mod), m); expr_ref degz_expr(a.mk_ge(divisor, zero), m); literal dgez = mk_literal(degz_expr); literal pos = th.mk_eq(rem, mod, false); literal neg = th.mk_eq(rem, mmod, false); if (m.has_trace_stream()) { app_ref body(m); body = m.mk_ite(degz_expr, ctx().bool_var2expr(pos.var()), ctx().bool_var2expr(neg.var())); th.log_axiom_instantiation(body); } mk_axiom(~dgez, pos); mk_axiom( dgez, neg); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; } // q = 0 or q * (p div q) = p void mk_div_axiom(expr* p, expr* q) { if (a.is_zero(q)) return; literal eqz = th.mk_eq(q, a.mk_real(0), false); literal eq = th.mk_eq(a.mk_mul(q, a.mk_div(p, q)), p, false); if (m.has_trace_stream()) { app_ref body(m); body = m.mk_implies(m.mk_not(ctx().bool_var2expr(eqz.var())), ctx().bool_var2expr(eq.var())); th.log_axiom_instantiation(body); } mk_axiom(eqz, eq); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; } // to_int (to_real x) = x // to_real(to_int(x)) <= x < to_real(to_int(x)) + 1 void mk_to_int_axiom(app* n) { expr* x = nullptr, *y = nullptr; VERIFY (a.is_to_int(n, x)); if (a.is_to_real(x, y)) { if (m.has_trace_stream()) { app_ref body(m); body = m.mk_eq(n, y); th.log_axiom_instantiation(body); } mk_axiom(th.mk_eq(y, n, false)); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; } else { expr_ref to_r(a.mk_to_real(n), m); expr_ref lo(a.mk_le(a.mk_sub(to_r, x), a.mk_real(0)), m); expr_ref hi(a.mk_ge(a.mk_sub(x, to_r), a.mk_real(1)), m); if (m.has_trace_stream()) th.log_axiom_instantiation(lo); mk_axiom(mk_literal(lo)); if (m.has_trace_stream()) { m.trace_stream() << "[end-of-instance]\n"; expr_ref body(m); body = m.mk_not(hi); th.log_axiom_instantiation(body); } mk_axiom(~mk_literal(hi)); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; } } // is_int(x) <=> to_real(to_int(x)) = x void mk_is_int_axiom(app* n) { expr* x = nullptr; VERIFY(a.is_is_int(n, x)); literal eq = th.mk_eq(a.mk_to_real(a.mk_to_int(x)), x, false); literal is_int = ctx().get_literal(n); if (m.has_trace_stream()) { app_ref body(m); body = m.mk_iff(n, ctx().bool_var2expr(eq.var())); th.log_axiom_instantiation(body); } mk_axiom(~is_int, eq); mk_axiom(is_int, ~eq); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; } void mk_idiv_mod_axioms(expr * p, expr * q) { if (a.is_zero(q)) { return; } TRACE("arith", tout << expr_ref(p, m) << " " << expr_ref(q, m) << "\n";); // if q is zero, then idiv and mod are uninterpreted functions. expr_ref div(a.mk_idiv(p, q), m); expr_ref mod(a.mk_mod(p, q), m); expr_ref zero(a.mk_int(0), m); literal eq = th.mk_eq(a.mk_add(a.mk_mul(q, div), mod), p, false); literal mod_ge_0 = mk_literal(a.mk_ge(mod, zero)); literal div_ge_0 = mk_literal(a.mk_ge(div, zero)); literal div_le_0 = mk_literal(a.mk_le(div, zero)); literal p_ge_0 = mk_literal(a.mk_ge(p, zero)); literal p_le_0 = mk_literal(a.mk_le(p, zero)); rational k(0); expr_ref upper(m); if (a.is_numeral(q, k)) { if (k.is_pos()) { upper = a.mk_numeral(k - 1, true); } else if (k.is_neg()) { upper = a.mk_numeral(-k - 1, true); } } else { k = rational::zero(); } context& c = ctx(); if (!k.is_zero()) { if (m.has_trace_stream()) { app_ref body(m); body = m.mk_implies(m.mk_not(m.mk_eq(q, zero)), c.bool_var2expr(eq.var())); th.log_axiom_instantiation(body); } mk_axiom(eq); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; if (m.has_trace_stream()) { app_ref body(m); body = m.mk_implies(m.mk_not(m.mk_eq(q, zero)), c.bool_var2expr(mod_ge_0.var())); th.log_axiom_instantiation(body); } mk_axiom(mod_ge_0); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; if (m.has_trace_stream()) { app_ref body(m); body = m.mk_implies(m.mk_not(m.mk_eq(q, zero)), a.mk_le(mod, upper)); th.log_axiom_instantiation(body); } mk_axiom(mk_literal(a.mk_le(mod, upper))); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; if (k.is_pos()) { if (m.has_trace_stream()) { app_ref body(m); body = m.mk_implies(m.mk_and(a.mk_gt(q, zero), c.bool_var2expr(p_ge_0.var())), c.bool_var2expr(div_ge_0.var())); th.log_axiom_instantiation(body); } mk_axiom(~p_ge_0, div_ge_0); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; if (m.has_trace_stream()) { app_ref body(m); body = m.mk_implies(m.mk_and(a.mk_gt(q, zero), c.bool_var2expr(p_le_0.var())), c.bool_var2expr(div_le_0.var())); th.log_axiom_instantiation(body); } mk_axiom(~p_le_0, div_le_0); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; } else { if (m.has_trace_stream()) { app_ref body(m); body = m.mk_implies(m.mk_and(a.mk_lt(q, zero), c.bool_var2expr(p_ge_0.var())), c.bool_var2expr(div_le_0.var())); th.log_axiom_instantiation(body); } mk_axiom(~p_ge_0, div_le_0); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; if (m.has_trace_stream()) { app_ref body(m); body = m.mk_implies(m.mk_and(a.mk_lt(q, zero), c.bool_var2expr(p_le_0.var())), c.bool_var2expr(div_ge_0.var())); th.log_axiom_instantiation(body); } mk_axiom(~p_le_0, div_ge_0); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; } } else { // q >= 0 or p = (p mod q) + q * (p div q) // q <= 0 or p = (p mod q) + q * (p div q) // q >= 0 or (p mod q) >= 0 // q <= 0 or (p mod q) >= 0 // q <= 0 or (p mod q) < q // q >= 0 or (p mod q) < -q literal q_ge_0 = mk_literal(a.mk_ge(q, zero)); literal q_le_0 = mk_literal(a.mk_le(q, zero)); if (m.has_trace_stream()) { app_ref body(m); body = m.mk_implies(m.mk_not(m.mk_eq(q, zero)), c.bool_var2expr(eq.var())); th.log_axiom_instantiation(body); } mk_axiom(q_ge_0, eq); mk_axiom(q_le_0, eq); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; if (m.has_trace_stream()) { app_ref body(m); body = m.mk_implies(m.mk_not(m.mk_eq(q, zero)), c.bool_var2expr(mod_ge_0.var())); th.log_axiom_instantiation(body); } mk_axiom(q_ge_0, mod_ge_0); mk_axiom(q_le_0, mod_ge_0); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; if (m.has_trace_stream()) { app_ref body(m); body = m.mk_implies(a.mk_lt(q, zero), a.mk_lt(a.mk_sub(mod, q), zero)); th.log_axiom_instantiation(body); } mk_axiom(q_le_0, ~mk_literal(a.mk_ge(a.mk_sub(mod, q), zero))); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; if (m.has_trace_stream()) { app_ref body(m); body = m.mk_implies(a.mk_lt(q, zero), a.mk_lt(a.mk_add(mod, q), zero)); th.log_axiom_instantiation(body); } mk_axiom(q_ge_0, ~mk_literal(a.mk_ge(a.mk_add(mod, q), zero))); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; if (m.has_trace_stream()) { app_ref body(m); body = m.mk_implies(m.mk_and(a.mk_gt(q, zero), c.bool_var2expr(p_ge_0.var())), c.bool_var2expr(div_ge_0.var())); th.log_axiom_instantiation(body); } mk_axiom(q_le_0, ~p_ge_0, div_ge_0); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; if (m.has_trace_stream()) { app_ref body(m); body = m.mk_implies(m.mk_and(a.mk_gt(q, zero), c.bool_var2expr(p_le_0.var())), c.bool_var2expr(div_le_0.var())); th.log_axiom_instantiation(body); } mk_axiom(q_le_0, ~p_le_0, div_le_0); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; if (m.has_trace_stream()) { app_ref body(m); body = m.mk_implies(m.mk_and(a.mk_lt(q, zero), c.bool_var2expr(p_ge_0.var())), c.bool_var2expr(div_le_0.var())); th.log_axiom_instantiation(body); } mk_axiom(q_ge_0, ~p_ge_0, div_le_0); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; if (m.has_trace_stream()) { app_ref body(m); body = m.mk_implies(m.mk_and(a.mk_lt(q, zero), c.bool_var2expr(p_le_0.var())), c.bool_var2expr(div_ge_0.var())); th.log_axiom_instantiation(body); } mk_axiom(q_ge_0, ~p_le_0, div_ge_0); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; } if (m_arith_params.m_arith_enum_const_mod && k.is_pos() && k < rational(8)) { unsigned _k = k.get_unsigned(); literal_buffer lits; expr_ref_vector exprs(m); for (unsigned j = 0; j < _k; ++j) { literal mod_j = th.mk_eq(mod, a.mk_int(j), false); lits.push_back(mod_j); exprs.push_back(c.bool_var2expr(mod_j.var())); ctx().mark_as_relevant(mod_j); } if (m.has_trace_stream()) { app_ref body(m); body = m.mk_or(exprs.size(), exprs.c_ptr()); th.log_axiom_instantiation(body); } ctx().mk_th_axiom(get_id(), lits.size(), lits.begin()); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; } } void mk_axiom(literal l) { ctx().mk_th_axiom(get_id(), false_literal, l); if (ctx().relevancy()) { ctx().mark_as_relevant(l); } } void mk_axiom(literal l1, literal l2) { if (l1 == false_literal) { mk_axiom(l2); return; } ctx().mk_th_axiom(get_id(), l1, l2); if (ctx().relevancy()) { ctx().mark_as_relevant(l1); ctx().add_rel_watch(~l1, ctx().bool_var2expr(l2.var())); } } void mk_axiom(literal l1, literal l2, literal l3) { ctx().mk_th_axiom(get_id(), l1, l2, l3); if (ctx().relevancy()) { ctx().mark_as_relevant(l1); ctx().add_rel_watch(~l1, ctx().bool_var2expr(l2.var())); ctx().add_rel_watch(~l2, ctx().bool_var2expr(l3.var())); } } literal mk_literal(expr* e) { expr_ref pinned(e, m); TRACE("mk_bool_var", tout << pinned << " " << pinned->get_id() << "\n";); if (!ctx().e_internalized(e)) { ctx().internalize(e, false); } return ctx().get_literal(e); } void init_search_eh() { m_arith_eq_adapter.init_search_eh(); m_num_conflicts = 0; } bool can_get_value(theory_var v) const { return (v != null_theory_var) && (v < static_cast(m_theory_var2var_index.size())) && (UINT_MAX != m_theory_var2var_index[v]) && (!m_variable_values.empty() || m_solver->is_term(m_theory_var2var_index[v])); } bool can_get_bound(theory_var v) const { return (v != null_theory_var) && (v < static_cast(m_theory_var2var_index.size())) && (UINT_MAX != m_theory_var2var_index[v]); } bool can_get_ivalue(theory_var v) const { if (v == null_theory_var || (v >= static_cast(m_theory_var2var_index.size()))) return false; return m_solver->var_is_registered(m_theory_var2var_index[v]); } mutable vector> m_todo_terms; lp::impq get_ivalue(theory_var v) const { SASSERT(can_get_ivalue(v)); lp::var_index vi = m_theory_var2var_index[v]; if (!m_solver->is_term(vi)) return m_solver->get_column_value(vi); m_todo_terms.push_back(std::make_pair(vi, rational::one())); lp::impq result(0); while (!m_todo_terms.empty()) { vi = m_todo_terms.back().first; rational coeff = m_todo_terms.back().second; m_todo_terms.pop_back(); if (m_solver->is_term(vi)) { const lp::lar_term& term = m_solver->get_term(vi); for (const auto & i: term) { m_todo_terms.push_back(std::make_pair(i.var(), coeff * i.coeff())); } } else { result += m_solver->get_column_value(vi) * coeff; } } return result; } rational get_value(theory_var v) const { if (v == null_theory_var || v >= static_cast(m_theory_var2var_index.size())) { TRACE("arith", tout << "Variable v" << v << " not internalized\n";); return rational::zero(); } lp::var_index vi = m_theory_var2var_index[v]; if (m_variable_values.count(vi) > 0) return m_variable_values[vi]; if (!m_solver->is_term(vi)) { TRACE("arith", tout << "not a term v" << v << "\n";); return rational::zero(); } m_todo_terms.push_back(std::make_pair(vi, rational::one())); rational result(0); while (!m_todo_terms.empty()) { lp::var_index wi = m_todo_terms.back().first; rational coeff = m_todo_terms.back().second; m_todo_terms.pop_back(); if (m_solver->is_term(wi)) { const lp::lar_term& term = m_solver->get_term(wi); for (const auto & i : term) { if (m_variable_values.count(i.var()) > 0) { result += m_variable_values[i.var()] * coeff * i.coeff(); } else { m_todo_terms.push_back(std::make_pair(i.var(), coeff * i.coeff())); } } } else { result += m_variable_values[wi] * coeff; } } m_variable_values[vi] = result; return result; } void init_variable_values() { reset_variable_values(); if (!m.canceled() && m_solver.get() && th.get_num_vars() > 0) { TRACE("arith", tout << "update variable values\n";); m_solver->get_model(m_variable_values); } } void reset_variable_values() { m_variable_values.clear(); } bool assume_eqs() { svector vars; theory_var sz = static_cast(th.get_num_vars()); for (theory_var v = 0; v < sz; ++v) { if (th.is_relevant_and_shared(get_enode(v))) { vars.push_back(m_theory_var2var_index[v]); } } if (vars.empty()) { return false; } init_variable_values(); TRACE("arith", for (theory_var v = 0; v < sz; ++v) { if (th.is_relevant_and_shared(get_enode(v))) { tout << "v" << v << " "; } } tout << "\n"; ); if (!m_use_nra_model) { m_solver->random_update(vars.size(), vars.c_ptr()); } m_model_eqs.reset(); TRACE("arith", display(tout);); unsigned old_sz = m_assume_eq_candidates.size(); bool result = false; int start = ctx().get_random_value(); for (theory_var i = 0; i < sz; ++i) { theory_var v = (i + start) % sz; enode* n1 = get_enode(v); if (!th.is_relevant_and_shared(n1)) { continue; } if (!can_get_value(v)) { continue; } theory_var other = m_model_eqs.insert_if_not_there(v); TRACE("arith", tout << "insert: v" << v << " := " << get_value(v) << " found: v" << other << "\n";); if (other == v) { continue; } enode* n2 = get_enode(other); if (n1->get_root() != n2->get_root()) { TRACE("arith", tout << enode_eq_pp(enode_pair(n1, n2), ctx()); tout << "v" << v << " = " << "v" << other << "\n";); m_assume_eq_candidates.push_back(std::make_pair(v, other)); result = true; } } if (result) { ctx().push_trail(restore_size_trail, false>(m_assume_eq_candidates, old_sz)); } return delayed_assume_eqs(); } bool delayed_assume_eqs() { if (m_assume_eq_head == m_assume_eq_candidates.size()) return false; ctx().push_trail(value_trail(m_assume_eq_head)); while (m_assume_eq_head < m_assume_eq_candidates.size()) { std::pair const & p = m_assume_eq_candidates[m_assume_eq_head]; theory_var v1 = p.first; theory_var v2 = p.second; enode* n1 = get_enode(v1); enode* n2 = get_enode(v2); m_assume_eq_head++; CTRACE("arith", is_eq(v1, v2) && n1->get_root() != n2->get_root(), tout << "assuming eq: v" << v1 << " = v" << v2 << "\n";); if (is_eq(v1, v2) && n1->get_root() != n2->get_root() && th.assume_eq(n1, n2)) { return true; } } return false; } bool is_eq(theory_var v1, theory_var v2) { if (m_use_nra_model) { return m_nra->am().eq(nl_value(v1, *m_a1), nl_value(v2, *m_a2)); } else { return get_value(v1) == get_value(v2); } } bool has_delayed_constraints() const { return !m_asserted_atoms.empty(); } final_check_status final_check_eh() { IF_VERBOSE(12, verbose_stream() << "final-check " << m_solver->get_status() << "\n"); m_use_nra_model = false; lbool is_sat = l_true; if (m_solver->get_status() != lp::lp_status::OPTIMAL) { is_sat = make_feasible(); } final_check_status st = FC_DONE; switch (is_sat) { case l_true: if (delayed_assume_eqs()) { return FC_CONTINUE; } if (assume_eqs()) { return FC_CONTINUE; } switch (check_lia()) { case l_true: break; case l_false: return FC_CONTINUE; case l_undef: TRACE("arith", tout << "check-lia giveup\n";); st = FC_CONTINUE; break; } switch (check_nra()) { case l_true: break; case l_false: return FC_CONTINUE; case l_undef: TRACE("arith", tout << "check-nra giveup\n";); st = FC_GIVEUP; break; } if (m_not_handled != nullptr) { TRACE("arith", tout << "unhandled operator " << mk_pp(m_not_handled, m) << "\n";); st = FC_GIVEUP; } return st; case l_false: set_conflict(); return FC_CONTINUE; case l_undef: TRACE("arith", tout << "check feasiable is undef\n";); return m.canceled() ? FC_CONTINUE : FC_GIVEUP; default: UNREACHABLE(); break; } TRACE("arith", tout << "default giveup\n";); return FC_GIVEUP; } // create a bound atom representing term >= k is lower_bound is true, and term <= k if it is false app_ref mk_bound(lp::lar_term const& term, rational const& k, bool lower_bound) { bool is_int = k.is_int(); rational offset = -k; u_map coeffs; term2coeffs(term, coeffs, rational::one(), offset); offset.neg(); TRACE("arith", m_solver->print_term(term, tout << "term: ") << "\n"; for (auto const& kv : coeffs) { tout << "v" << kv.m_key << " * " << kv.m_value << "\n"; } tout << offset << "\n"; rational g(0); for (auto const& kv : coeffs) { g = gcd(g, kv.m_value); } tout << "gcd: " << g << "\n"; ); if (is_int) { // 3x + 6y >= 5 -> x + 3y >= 5/3, then x + 3y >= 2 // 3x + 6y <= 5 -> x + 3y <= 1 rational g = gcd_reduce(coeffs); if (!g.is_one()) { if (lower_bound) { TRACE("arith", tout << "lower: " << offset << " / " << g << " = " << offset / g << " >= " << ceil(offset / g) << "\n";); offset = ceil(offset / g); } else { TRACE("arith", tout << "upper: " << offset << " / " << g << " = " << offset / g << " <= " << floor(offset / g) << "\n";); offset = floor(offset / g); } } } if (!coeffs.empty() && coeffs.begin()->m_value.is_neg()) { offset.neg(); lower_bound = !lower_bound; for (auto& kv : coeffs) kv.m_value.neg(); } app_ref atom(m); app_ref t = coeffs2app(coeffs, rational::zero(), is_int); if (lower_bound) { atom = a.mk_ge(t, a.mk_numeral(offset, is_int)); } else { atom = a.mk_le(t, a.mk_numeral(offset, is_int)); } TRACE("arith", tout << t << ": " << atom << "\n"; m_solver->print_term(term, tout << "bound atom: ") << (lower_bound?" >= ":" <= ") << k << "\n";); ctx().internalize(atom, true); ctx().mark_as_relevant(atom.get()); return atom; } bool make_sure_all_vars_have_bounds() { if (!m_has_int) { return true; } unsigned nv = std::min(th.get_num_vars(), m_theory_var2var_index.size()); bool all_bounded = true; for (unsigned v = 0; v < nv; ++v) { lp::var_index vi = m_theory_var2var_index[v]; if (vi == UINT_MAX) continue; if (!m_solver->is_term(vi) && !var_has_bound(vi, true) && !var_has_bound(vi, false)) { lp::lar_term term; term.add_monomial(rational::one(), vi); app_ref b = mk_bound(term, rational::zero(), true); TRACE("arith", tout << "added bound " << b << "\n";); IF_VERBOSE(2, verbose_stream() << "bound: " << b << "\n"); all_bounded = false; } } return all_bounded; } /** * n = (div p q) * * (div p q) * q + (mod p q) = p, (mod p q) >= 0 * * 0 < q => (p/q <= v(p)/v(q) => n <= floor(v(p)/v(q))) * 0 < q => (v(p)/v(q) <= p/q => v(p)/v(q) - 1 < n) * */ bool is_bounded(expr* n) { expr* x = nullptr, *y = nullptr; while (true) { if (a.is_idiv(n, x, y) && a.is_numeral(y)) { n = x; } else if (a.is_mod(n, x, y) && a.is_numeral(y)) { return true; } else if (a.is_numeral(n)) { return true; } else { return false; } } } bool check_idiv_bounds() { if (m_idiv_terms.empty()) { return true; } init_variable_values(); bool all_divs_valid = true; for (expr* n : m_idiv_terms) { expr* p = nullptr, *q = nullptr; VERIFY(a.is_idiv(n, p, q)); theory_var v = mk_var(n); theory_var v1 = mk_var(p); rational r1 = get_value(v1); rational r2; if (!r1.is_int() || r1.is_neg()) { // TBD // r1 = 223/4, r2 = 2, r = 219/8 // take ceil(r1), floor(r1), ceil(r2), floor(r2), for floor(r2) > 0 // then // p/q <= ceil(r1)/floor(r2) => n <= div(ceil(r1), floor(r2)) // p/q >= floor(r1)/ceil(r2) => n >= div(floor(r1), ceil(r2)) continue; } if (a.is_numeral(q, r2) && r2.is_pos()) { rational val_v = get_value(v); if (val_v == div(r1, r2)) continue; if (!is_bounded(n)) { TRACE("arith", tout << "unbounded " << expr_ref(n, m) << "\n";); continue; } TRACE("arith", tout << get_value(v) << " != " << r1 << " div " << r2 << "\n";); rational div_r = div(r1, r2); // p <= q * div(r1, q) + q - 1 => div(p, q) <= div(r1, r2) // p >= q * div(r1, q) => div(r1, q) <= div(p, q) rational mul(1); rational hi = r2 * div_r + r2 - 1; rational lo = r2 * div_r; // used to normalize inequalities so they // don't appear as 8*x >= 15, but x >= 2 expr *n1 = nullptr, *n2 = nullptr; if (a.is_mul(p, n1, n2) && is_numeral(n1, mul) && mul.is_pos()) { p = n2; hi = floor(hi/mul); lo = ceil(lo/mul); } literal p_le_r1 = mk_literal(a.mk_le(p, a.mk_numeral(hi, true))); literal p_ge_r1 = mk_literal(a.mk_ge(p, a.mk_numeral(lo, true))); literal n_le_div = mk_literal(a.mk_le(n, a.mk_numeral(div_r, true))); literal n_ge_div = mk_literal(a.mk_ge(n, a.mk_numeral(div_r, true))); if (m.has_trace_stream()) { app_ref body(m); body = m.mk_implies(ctx().bool_var2expr(p_le_r1.var()), ctx().bool_var2expr(n_le_div.var())); th.log_axiom_instantiation(body); } mk_axiom(~p_le_r1, n_le_div); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; if (m.has_trace_stream()) { app_ref body(m); body = m.mk_implies(ctx().bool_var2expr(p_ge_r1.var()), ctx().bool_var2expr(n_ge_div.var())); th.log_axiom_instantiation(body); } mk_axiom(~p_ge_r1, n_ge_div); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; all_divs_valid = false; TRACE("arith", tout << r1 << " div " << r2 << "\n"; literal_vector lits; lits.push_back(~p_le_r1); lits.push_back(n_le_div); ctx().display_literals_verbose(tout, lits) << "\n\n"; lits[0] = ~p_ge_r1; lits[1] = n_ge_div; ctx().display_literals_verbose(tout, lits) << "\n";); continue; } } return all_divs_valid; } expr_ref var2expr(lp::var_index v) { std::ostringstream name; name << "v" << m_solver->local2external(v); return expr_ref(m.mk_const(symbol(name.str().c_str()), a.mk_int()), m); } expr_ref multerm(rational const& r, expr* e) { if (r.is_one()) return expr_ref(e, m); return expr_ref(a.mk_mul(a.mk_numeral(r, true), e), m); } expr_ref term2expr(lp::lar_term const& term) { expr_ref t(m); expr_ref_vector ts(m); for (auto const& p : term) { lp::var_index wi = p.var(); if (m_solver->is_term(wi)) { ts.push_back(multerm(p.coeff(), term2expr(m_solver->get_term(wi)))); } else { ts.push_back(multerm(p.coeff(), var2expr(wi))); } } if (ts.size() == 1) { t = ts.back(); } else { t = a.mk_add(ts.size(), ts.c_ptr()); } return t; } expr_ref constraint2fml(lp::constraint_index ci) { lp::lar_base_constraint const& c = *m_solver->constraints()[ci]; expr_ref fml(m); expr_ref_vector ts(m); rational rhs = c.m_right_side; for (auto cv : c.get_left_side_coefficients()) { ts.push_back(multerm(cv.first, var2expr(cv.second))); } switch (c.m_kind) { case lp::LE: fml = a.mk_le(a.mk_add(ts.size(), ts.c_ptr()), a.mk_numeral(rhs, true)); break; case lp::LT: fml = a.mk_lt(a.mk_add(ts.size(), ts.c_ptr()), a.mk_numeral(rhs, true)); break; case lp::GE: fml = a.mk_ge(a.mk_add(ts.size(), ts.c_ptr()), a.mk_numeral(rhs, true)); break; case lp::GT: fml = a.mk_gt(a.mk_add(ts.size(), ts.c_ptr()), a.mk_numeral(rhs, true)); break; case lp::EQ: fml = m.mk_eq(a.mk_add(ts.size(), ts.c_ptr()), a.mk_numeral(rhs, true)); break; } return fml; } void dump_cut_lemma(std::ostream& out, lp::lar_term const& term, lp::mpq const& k, lp::explanation const& ex, bool upper) { m_solver->print_term(term, out << "bound: "); out << (upper?" <= ":" >= ") << k << "\n"; for (auto const& p : term) { lp::var_index wi = p.var(); out << p.coeff() << " * "; if (m_solver->is_term(wi)) { m_solver->print_term(m_solver->get_term(wi), out) << "\n"; } else { out << "v" << m_solver->local2external(wi) << "\n"; } } for (auto const& ev : ex.m_explanation) { m_solver->print_constraint(ev.second, out << ev.first << ": "); } expr_ref_vector fmls(m); for (auto const& ev : ex.m_explanation) { fmls.push_back(constraint2fml(ev.second)); } expr_ref t(term2expr(term), m); if (upper) { fmls.push_back(m.mk_not(a.mk_ge(t, a.mk_numeral(k, true)))); } else { fmls.push_back(m.mk_not(a.mk_le(t, a.mk_numeral(k, true)))); } ast_pp_util visitor(m); visitor.collect(fmls); visitor.display_decls(out); visitor.display_asserts(out, fmls, true); out << "(check-sat)\n"; } lbool check_lia() { if (m.canceled()) { TRACE("arith", tout << "canceled\n";); return l_undef; } lbool lia_check = l_undef; if (!check_idiv_bounds()) { return l_false; } m_explanation.reset(); switch (m_lia->check()) { case lp::lia_move::sat: lia_check = l_true; break; case lp::lia_move::branch: { TRACE("arith", tout << "branch\n";); app_ref b = mk_bound(m_lia->get_term(), m_lia->get_offset(), !m_lia->is_upper()); if (m.has_trace_stream()) { app_ref body(m); body = m.mk_or(b, m.mk_not(b)); th.log_axiom_instantiation(body); m.trace_stream() << "[end-of-instance]\n"; } IF_VERBOSE(2, verbose_stream() << "branch " << b << "\n";); // branch on term >= k + 1 // branch on term <= k // TBD: ctx().force_phase(ctx().get_literal(b)); // at this point we have a new unassigned atom that the // SAT core assigns a value to lia_check = l_false; break; } case lp::lia_move::cut: { TRACE("arith", tout << "cut\n";); ++m_stats.m_gomory_cuts; // m_explanation implies term <= k app_ref b = mk_bound(m_lia->get_term(), m_lia->get_offset(), !m_lia->is_upper()); if (m.has_trace_stream()) { th.log_axiom_instantiation(b); m.trace_stream() << "[end-of-instance]\n"; } IF_VERBOSE(2, verbose_stream() << "cut " << b << "\n"); TRACE("arith", dump_cut_lemma(tout, m_lia->get_term(), m_lia->get_offset(), m_lia->get_explanation(), m_lia->is_upper());); m_eqs.reset(); m_core.reset(); m_params.reset(); for (auto const& ev : m_lia->get_explanation().m_explanation) { if (!ev.first.is_zero()) { set_evidence(ev.second); } } literal lit(ctx().get_bool_var(b), false); TRACE("arith", ctx().display_lemma_as_smt_problem(tout << "new cut:\n", m_core.size(), m_core.c_ptr(), m_eqs.size(), m_eqs.c_ptr(), lit); display(tout);); assign(lit); lia_check = l_false; break; } case lp::lia_move::conflict: TRACE("arith", tout << "conflict\n";); // ex contains unsat core m_explanation = m_lia->get_explanation().m_explanation; set_conflict1(); lia_check = l_false; break; case lp::lia_move::undef: TRACE("arith", tout << "lia undef\n";); lia_check = l_undef; break; case lp::lia_move::continue_with_check: lia_check = l_undef; break; default: UNREACHABLE(); } return lia_check; } lbool check_nra() { m_use_nra_model = false; if (m.canceled()) { TRACE("arith", tout << "canceled\n";); return l_undef; } if (!m_nra) return l_true; if (!m_nra->need_check()) return l_true; m_a1 = nullptr; m_a2 = nullptr; lbool r = m_nra->check(m_explanation); m_a1 = alloc(scoped_anum, m_nra->am()); m_a2 = alloc(scoped_anum, m_nra->am()); switch (r) { case l_false: set_conflict1(); break; case l_true: m_use_nra_model = true; if (assume_eqs()) { return l_false; } break; case l_undef: TRACE("arith", tout << "nra-undef\n";); default: break; } return r; } /** \brief We must redefine this method, because theory of arithmetic contains underspecified operators such as division by 0. (/ a b) is essentially an uninterpreted function when b = 0. Thus, 'a' must be considered a shared var if it is the child of an underspecified operator. if merge(a / b, x + y) and a / b is root, then x + y become shared and all z + u in equivalence class of x + y. TBD: when the set of underspecified subterms is small, compute the shared variables below it. Recompute the set if there are merges that invalidate it. Use the set to determine if a variable is shared. */ bool is_shared(theory_var v) const { if (m_underspecified.empty()) { return false; } enode * n = get_enode(v); enode * r = n->get_root(); unsigned usz = m_underspecified.size(); TRACE("shared", tout << ctx().get_scope_level() << " " << v << " " << r->get_num_parents() << "\n";); if (r->get_num_parents() > 2*usz) { for (unsigned i = 0; i < usz; ++i) { app* u = m_underspecified[i]; unsigned sz = u->get_num_args(); for (unsigned j = 0; j < sz; ++j) { if (ctx().get_enode(u->get_arg(j))->get_root() == r) { return true; } } } } else { for (enode * parent : r->get_const_parents()) { if (is_underspecified(parent->get_owner())) { return true; } } } return false; } bool can_propagate() { return m_asserted_atoms.size() > m_asserted_qhead; } void propagate() { flush_bound_axioms(); if (!can_propagate()) { return; } while (m_asserted_qhead < m_asserted_atoms.size() && !ctx().inconsistent()) { bool_var bv = m_asserted_atoms[m_asserted_qhead].m_bv; bool is_true = m_asserted_atoms[m_asserted_qhead].m_is_true; m_to_check.push_back(bv); lp_api::bound& b = *m_bool_var2bound.find(bv); assert_bound(bv, is_true, b); ++m_asserted_qhead; } if (ctx().inconsistent()) { m_to_check.reset(); return; } lbool lbl = make_feasible(); switch(lbl) { case l_false: TRACE("arith", tout << "propagation conflict\n";); set_conflict(); break; case l_true: propagate_basic_bounds(); propagate_bounds_with_lp_solver(); break; case l_undef: break; } } void propagate_bounds_with_lp_solver() { if (BP_NONE == propagation_mode()) { return; } int num_of_p = m_solver->settings().st().m_num_of_implied_bounds; (void)num_of_p; local_bound_propagator bp(*this); m_solver->propagate_bounds_for_touched_rows(bp); if (m.canceled()) { return; } int new_num_of_p = m_solver->settings().st().m_num_of_implied_bounds; (void)new_num_of_p; CTRACE("arith", new_num_of_p > num_of_p, tout << "found " << new_num_of_p << " implied bounds\n";); if (m_solver->get_status() == lp::lp_status::INFEASIBLE) { set_conflict(); } else { for (unsigned i = 0; !m.canceled() && !ctx().inconsistent() && i < bp.m_ibounds.size(); ++i) { propagate_lp_solver_bound(bp.m_ibounds[i]); } } } bool bound_is_interesting(unsigned vi, lp::lconstraint_kind kind, const rational & bval) const { theory_var v; if (m_solver->is_term(vi)) { v = m_term_index2theory_var.get(m_solver->adjust_term_index(vi), null_theory_var); } else { v = m_var_index2theory_var.get(vi, null_theory_var); } if (v == null_theory_var) return false; if (m_unassigned_bounds[v] == 0 || m_bounds.size() <= static_cast(v)) { return false; } lp_bounds const& bounds = m_bounds[v]; for (unsigned i = 0; i < bounds.size(); ++i) { lp_api::bound* b = bounds[i]; if (ctx().get_assignment(b->get_bv()) != l_undef) { continue; } literal lit = is_bound_implied(kind, bval, *b); if (lit == null_literal) { continue; } return true; } return false; } struct local_bound_propagator: public lp::bound_propagator { imp & m_imp; local_bound_propagator(imp& i) : bound_propagator(*i.m_solver), m_imp(i) {} bool bound_is_interesting(unsigned j, lp::lconstraint_kind kind, const rational & v) override { return m_imp.bound_is_interesting(j, kind, v); } void consume(rational const& v, lp::constraint_index j) override { m_imp.set_evidence(j); m_imp.m_explanation.push_back(std::make_pair(v, j)); } }; void propagate_lp_solver_bound(lp::implied_bound& be) { theory_var v; lp::var_index vi = be.m_j; if (m_solver->is_term(vi)) { v = m_term_index2theory_var.get(m_solver->adjust_term_index(vi), null_theory_var); } else { v = m_var_index2theory_var.get(vi, null_theory_var); } if (v == null_theory_var) return; TRACE("arith", tout << "v" << v << " " << be.kind() << " " << be.m_bound << "\n"; // if (m_unassigned_bounds[v] == 0) m_solver->print_bound_evidence(be, tout); ); if (m_unassigned_bounds[v] == 0 || m_bounds.size() <= static_cast(v)) { TRACE("arith", tout << "return\n";); return; } lp_bounds const& bounds = m_bounds[v]; bool first = true; for (unsigned i = 0; i < bounds.size(); ++i) { lp_api::bound* b = bounds[i]; if (ctx().get_assignment(b->get_bv()) != l_undef) { continue; } literal lit = is_bound_implied(be.kind(), be.m_bound, *b); if (lit == null_literal) { continue; } TRACE("arith", tout << lit << " bound: " << *b << " first: " << first << "\n";); m_solver->settings().st().m_num_of_implied_bounds ++; if (first) { first = false; m_core.reset(); m_eqs.reset(); m_params.reset(); m_explanation.clear(); local_bound_propagator bp(*this); m_solver->explain_implied_bound(be, bp); } CTRACE("arith", m_unassigned_bounds[v] == 0, tout << "missed bound\n";); updt_unassigned_bounds(v, -1); TRACE("arith", ctx().display_literals_verbose(tout, m_core); tout << "\n --> "; ctx().display_literal_verbose(tout, lit); tout << "\n"; display_evidence(tout, m_explanation); m_solver->print_implied_bound(be, tout); ); DEBUG_CODE( for (auto& lit : m_core) { SASSERT(ctx().get_assignment(lit) == l_true); }); ++m_stats.m_bound_propagations1; assign(lit); } } literal_vector m_core2; void assign(literal lit) { // SASSERT(validate_assign(lit)); dump_assign(lit); if (m_core.size() < small_lemma_size() && m_eqs.empty()) { m_core2.reset(); for (auto const& c : m_core) { m_core2.push_back(~c); } m_core2.push_back(lit); justification * js = nullptr; if (proofs_enabled()) { js = alloc(theory_lemma_justification, get_id(), ctx(), m_core2.size(), m_core2.c_ptr(), m_params.size(), m_params.c_ptr()); } ctx().mk_clause(m_core2.size(), m_core2.c_ptr(), js, CLS_TH_LEMMA, nullptr); } else { ctx().assign( lit, ctx().mk_justification( ext_theory_propagation_justification( get_id(), ctx().get_region(), m_core.size(), m_core.c_ptr(), m_eqs.size(), m_eqs.c_ptr(), lit, m_params.size(), m_params.c_ptr()))); } } literal is_bound_implied(lp::lconstraint_kind k, rational const& value, lp_api::bound const& b) const { if ((k == lp::LE || k == lp::LT) && b.get_bound_kind() == lp_api::upper_t && value <= b.get_value()) { TRACE("arith", tout << "v <= value <= b.get_value() => v <= b.get_value() \n";); return literal(b.get_bv(), false); } if ((k == lp::GE || k == lp::GT) && b.get_bound_kind() == lp_api::lower_t && b.get_value() <= value) { TRACE("arith", tout << "b.get_value() <= value <= v => b.get_value() <= v \n";); return literal(b.get_bv(), false); } if (k == lp::LE && b.get_bound_kind() == lp_api::lower_t && value < b.get_value()) { TRACE("arith", tout << "v <= value < b.get_value() => v < b.get_value()\n";); return literal(b.get_bv(), true); } if (k == lp::LT && b.get_bound_kind() == lp_api::lower_t && value <= b.get_value()) { TRACE("arith", tout << "v < value <= b.get_value() => v < b.get_value()\n";); return literal(b.get_bv(), true); } if (k == lp::GE && b.get_bound_kind() == lp_api::upper_t && b.get_value() < value) { TRACE("arith", tout << "b.get_value() < value <= v => b.get_value() < v\n";); return literal(b.get_bv(), true); } if (k == lp::GT && b.get_bound_kind() == lp_api::upper_t && b.get_value() <= value) { TRACE("arith", tout << "b.get_value() <= value < v => b.get_value() < v\n";); return literal(b.get_bv(), true); } return null_literal; } void mk_bound_axioms(lp_api::bound& b) { if (!ctx().is_searching()) { // // NB. We make an assumption that user push calls propagation // before internal scopes are pushed. This flushes all newly // asserted atoms into the right context. // m_new_bounds.push_back(&b); return; } theory_var v = b.get_var(); lp_api::bound_kind kind1 = b.get_bound_kind(); rational const& k1 = b.get_value(); lp_bounds & bounds = m_bounds[v]; lp_api::bound* end = nullptr; lp_api::bound* lo_inf = end, *lo_sup = end; lp_api::bound* hi_inf = end, *hi_sup = end; for (lp_api::bound* other : bounds) { if (other == &b) continue; if (b.get_bv() == other->get_bv()) continue; lp_api::bound_kind kind2 = other->get_bound_kind(); rational const& k2 = other->get_value(); if (k1 == k2 && kind1 == kind2) { // the bounds are equivalent. continue; } SASSERT(k1 != k2 || kind1 != kind2); if (kind2 == lp_api::lower_t) { if (k2 < k1) { if (lo_inf == end || k2 > lo_inf->get_value()) { lo_inf = other; } } else if (lo_sup == end || k2 < lo_sup->get_value()) { lo_sup = other; } } else if (k2 < k1) { if (hi_inf == end || k2 > hi_inf->get_value()) { hi_inf = other; } } else if (hi_sup == end || k2 < hi_sup->get_value()) { hi_sup = other; } } if (lo_inf != end) mk_bound_axiom(b, *lo_inf); if (lo_sup != end) mk_bound_axiom(b, *lo_sup); if (hi_inf != end) mk_bound_axiom(b, *hi_inf); if (hi_sup != end) mk_bound_axiom(b, *hi_sup); } void mk_bound_axiom(lp_api::bound& b1, lp_api::bound& b2) { theory_var v = b1.get_var(); literal l1(b1.get_bv()); literal l2(b2.get_bv()); rational const& k1 = b1.get_value(); rational const& k2 = b2.get_value(); lp_api::bound_kind kind1 = b1.get_bound_kind(); lp_api::bound_kind kind2 = b2.get_bound_kind(); bool v_is_int = is_int(v); SASSERT(v == b2.get_var()); if (k1 == k2 && kind1 == kind2) return; SASSERT(k1 != k2 || kind1 != kind2); parameter coeffs[3] = { parameter(symbol("farkas")), parameter(rational(1)), parameter(rational(1)) }; if (kind1 == lp_api::lower_t) { if (kind2 == lp_api::lower_t) { if (k2 <= k1) { mk_clause(~l1, l2, 3, coeffs); } else { mk_clause(l1, ~l2, 3, coeffs); } } else if (k1 <= k2) { // k1 <= k2, k1 <= x or x <= k2 mk_clause(l1, l2, 3, coeffs); } else { // k1 > hi_inf, k1 <= x => ~(x <= hi_inf) mk_clause(~l1, ~l2, 3, coeffs); if (v_is_int && k1 == k2 + rational(1)) { // k1 <= x or x <= k1-1 mk_clause(l1, l2, 3, coeffs); } } } else if (kind2 == lp_api::lower_t) { if (k1 >= k2) { // k1 >= lo_inf, k1 >= x or lo_inf <= x mk_clause(l1, l2, 3, coeffs); } else { // k1 < k2, k2 <= x => ~(x <= k1) mk_clause(~l1, ~l2, 3, coeffs); if (v_is_int && k1 == k2 - rational(1)) { // x <= k1 or k1+l <= x mk_clause(l1, l2, 3, coeffs); } } } else { // kind1 == A_UPPER, kind2 == A_UPPER if (k1 >= k2) { // k1 >= k2, x <= k2 => x <= k1 mk_clause(l1, ~l2, 3, coeffs); } else { // k1 <= hi_sup , x <= k1 => x <= hi_sup mk_clause(~l1, l2, 3, coeffs); } } } typedef lp_bounds::iterator iterator; void flush_bound_axioms() { CTRACE("arith", !m_new_bounds.empty(), tout << "flush bound axioms\n";); while (!m_new_bounds.empty()) { lp_bounds atoms; atoms.push_back(m_new_bounds.back()); m_new_bounds.pop_back(); theory_var v = atoms.back()->get_var(); for (unsigned i = 0; i < m_new_bounds.size(); ++i) { if (m_new_bounds[i]->get_var() == v) { atoms.push_back(m_new_bounds[i]); m_new_bounds[i] = m_new_bounds.back(); m_new_bounds.pop_back(); --i; } } CTRACE("arith_verbose", !atoms.empty(), for (unsigned i = 0; i < atoms.size(); ++i) { atoms[i]->display(tout); tout << "\n"; }); lp_bounds occs(m_bounds[v]); std::sort(atoms.begin(), atoms.end(), compare_bounds()); std::sort(occs.begin(), occs.end(), compare_bounds()); iterator begin1 = occs.begin(); iterator begin2 = occs.begin(); iterator end = occs.end(); begin1 = first(lp_api::lower_t, begin1, end); begin2 = first(lp_api::upper_t, begin2, end); iterator lo_inf = begin1, lo_sup = begin1; iterator hi_inf = begin2, hi_sup = 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]; 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; if (hi_sup1 != end) hi_sup = hi_sup1; if (!flo_inf) lo_inf = end; if (!fhi_inf) hi_inf = end; if (!flo_sup) lo_sup = end; if (!fhi_sup) hi_sup = end; visited.insert(a1); if (lo_inf1 != end && lo_inf != end && !visited.contains(*lo_inf)) mk_bound_axiom(*a1, **lo_inf); if (lo_sup1 != end && lo_sup != end && !visited.contains(*lo_sup)) mk_bound_axiom(*a1, **lo_sup); if (hi_inf1 != end && hi_inf != end && !visited.contains(*hi_inf)) mk_bound_axiom(*a1, **hi_inf); if (hi_sup1 != end && hi_sup != end && !visited.contains(*hi_sup)) mk_bound_axiom(*a1, **hi_sup); } } } struct compare_bounds { bool operator()(lp_api::bound* a1, lp_api::bound* a2) const { return a1->get_value() < a2->get_value(); } }; lp_bounds::iterator first( lp_api::bound_kind kind, iterator it, iterator end) { for (; it != end; ++it) { lp_api::bound* a = *it; if (a->get_bound_kind() == kind) return it; } return end; } lp_bounds::iterator next_inf( lp_api::bound* a1, lp_api::bound_kind kind, iterator it, iterator end, bool& found_compatible) { rational const & k1(a1->get_value()); iterator result = end; found_compatible = false; for (; it != end; ++it) { lp_api::bound * a2 = *it; if (a1 == a2) continue; if (a2->get_bound_kind() != kind) continue; rational const & k2(a2->get_value()); found_compatible = true; if (k2 <= k1) { result = it; } else { break; } } return result; } lp_bounds::iterator next_sup( lp_api::bound* a1, lp_api::bound_kind kind, iterator it, iterator end, bool& found_compatible) { rational const & k1(a1->get_value()); found_compatible = false; for (; it != end; ++it) { lp_api::bound * a2 = *it; if (a1 == a2) continue; if (a2->get_bound_kind() != kind) continue; rational const & k2(a2->get_value()); found_compatible = true; if (k1 < k2) { return it; } } return end; } void propagate_basic_bounds() { for (auto const& bv : m_to_check) { lp_api::bound& b = *m_bool_var2bound.find(bv); propagate_bound(bv, ctx().get_assignment(bv) == l_true, b); if (ctx().inconsistent()) break; } m_to_check.reset(); } // for glb lo': lo' < lo: // lo <= x -> lo' <= x // lo <= x -> ~(x <= lo') // for lub hi': hi' > hi // x <= hi -> x <= hi' // x <= hi -> ~(x >= hi') void propagate_bound(bool_var bv, bool is_true, lp_api::bound& b) { if (BP_NONE == propagation_mode()) { return; } lp_api::bound_kind k = b.get_bound_kind(); theory_var v = b.get_var(); inf_rational val = b.get_value(is_true); lp_bounds const& bounds = m_bounds[v]; SASSERT(!bounds.empty()); if (bounds.size() == 1) return; if (m_unassigned_bounds[v] == 0) return; bool v_is_int = is_int(v); literal lit1(bv, !is_true); literal lit2 = null_literal; bool find_glb = (is_true == (k == lp_api::lower_t)); TRACE("arith", tout << "find_glb: " << find_glb << " is_true: " << is_true << " k: " << k << " is_lower: " << (k == lp_api::lower_t) << "\n";); if (find_glb) { rational glb; lp_api::bound* lb = nullptr; for (lp_api::bound* b2 : bounds) { if (b2 == &b) continue; rational const& val2 = b2->get_value(); if (((is_true || v_is_int) ? val2 < val : val2 <= val) && (!lb || glb < val2)) { lb = b2; glb = val2; } } if (!lb) return; bool sign = lb->get_bound_kind() != lp_api::lower_t; lit2 = literal(lb->get_bv(), sign); } else { rational lub; lp_api::bound* ub = nullptr; for (lp_api::bound* b2 : bounds) { if (b2 == &b) continue; rational const& val2 = b2->get_value(); if (((is_true || v_is_int) ? val < val2 : val <= val2) && (!ub || val2 < lub)) { ub = b2; lub = val2; } } if (!ub) return; bool sign = ub->get_bound_kind() != lp_api::upper_t; lit2 = literal(ub->get_bv(), sign); } TRACE("arith", ctx().display_literal_verbose(tout, lit1); ctx().display_literal_verbose(tout << " => ", lit2); tout << "\n";); updt_unassigned_bounds(v, -1); ++m_stats.m_bound_propagations2; m_params.reset(); m_core.reset(); m_eqs.reset(); m_core.push_back(lit1); m_params.push_back(parameter(symbol("farkas"))); m_params.push_back(parameter(rational(1))); m_params.push_back(parameter(rational(1))); assign(lit2); ++m_stats.m_bounds_propagations; } svector m_todo_vars; void add_use_lists(lp_api::bound* b) { theory_var v = b->get_var(); lp::var_index vi = get_var_index(v); if (!m_solver->is_term(vi)) { return; } m_todo_vars.push_back(vi); while (!m_todo_vars.empty()) { vi = m_todo_vars.back(); m_todo_vars.pop_back(); lp::lar_term const& term = m_solver->get_term(vi); for (auto const& p : term) { lp::var_index wi = p.var(); if (m_solver->is_term(wi)) { m_todo_vars.push_back(wi); } else { unsigned w = m_var_index2theory_var[wi]; m_use_list.reserve(w + 1, ptr_vector()); m_use_list[w].push_back(b); } } } } void del_use_lists(lp_api::bound* b) { theory_var v = b->get_var(); lp::var_index vi = m_theory_var2var_index[v]; if (!m_solver->is_term(vi)) { return; } m_todo_vars.push_back(vi); while (!m_todo_vars.empty()) { vi = m_todo_vars.back(); m_todo_vars.pop_back(); lp::lar_term const& term = m_solver->get_term(vi); for (auto const& coeff : term) { lp::var_index wi = coeff.var(); if (m_solver->is_term(wi)) { m_todo_vars.push_back(wi); } else { unsigned w = m_var_index2theory_var[wi]; SASSERT(m_use_list[w].back() == b); m_use_list[w].pop_back(); } } } } // // propagate bounds to compound terms // The idea is that if bounds on all variables in an inequality ax + by + cz >= k // have been assigned we may know the truth value of the inequality by using simple // bounds propagation. // void propagate_bound_compound(bool_var bv, bool is_true, lp_api::bound& b) { theory_var v = b.get_var(); TRACE("arith", tout << mk_pp(get_owner(v), m) << "\n";); if (static_cast(v) >= m_use_list.size()) { return; } for (auto const& vb : m_use_list[v]) { if (ctx().get_assignment(vb->get_bv()) != l_undef) { TRACE("arith_verbose", display_bound(tout << "assigned ", *vb) << "\n";); continue; } inf_rational r; // x + y // x >= 0, y >= 1 -> x + y >= 1 // x <= 0, y <= 2 -> x + y <= 2 literal lit = null_literal; if (lp_api::lower_t == vb->get_bound_kind()) { if (get_glb(*vb, r) && r >= vb->get_value()) { // vb is assigned true lit = literal(vb->get_bv(), false); } else if (get_lub(*vb, r) && r < vb->get_value()) { // vb is assigned false lit = literal(vb->get_bv(), true); } } else { if (get_glb(*vb, r) && r > vb->get_value()) { // VB <= value < val(VB) lit = literal(vb->get_bv(), true); } else if (get_lub(*vb, r) && r <= vb->get_value()) { // val(VB) <= value lit = literal(vb->get_bv(), false); } } if (lit != null_literal) { TRACE("arith", ctx().display_literals_verbose(tout, m_core); tout << "\n --> "; ctx().display_literal_verbose(tout, lit); tout << "\n"; ); assign(lit); } else { TRACE("arith_verbose", display_bound(tout << "skip ", *vb) << "\n";); } } } bool get_lub(lp_api::bound const& b, inf_rational& lub) { return get_bound(b, lub, true); } bool get_glb(lp_api::bound const& b, inf_rational& glb) { return get_bound(b, glb, false); } std::ostream& display_bound(std::ostream& out, lp_api::bound const& b) { return out << mk_pp(ctx().bool_var2expr(b.get_bv()), m); } bool get_bound(lp_api::bound const& b, inf_rational& r, bool is_lub) { m_core.reset(); m_eqs.reset(); m_params.reset(); r.reset(); theory_var v = b.get_var(); lp::var_index vi = m_theory_var2var_index[v]; SASSERT(m_solver->is_term(vi)); lp::lar_term const& term = m_solver->get_term(vi); for (auto const & coeff : term.m_coeffs) { lp::var_index wi = coeff.first; lp::constraint_index ci; rational value; bool is_strict; if (m_solver->is_term(wi)) { return false; } if (coeff.second.is_neg() == is_lub) { // -3*x ... <= lub based on lower bound for x. if (!m_solver->has_lower_bound(wi, ci, value, is_strict)) { return false; } if (is_strict) { r += inf_rational(rational::zero(), coeff.second.is_pos()); } } else { if (!m_solver->has_upper_bound(wi, ci, value, is_strict)) { return false; } if (is_strict) { r += inf_rational(rational::zero(), coeff.second.is_pos()); } } r += value * coeff.second; set_evidence(ci); } TRACE("arith_verbose", tout << (is_lub?"lub":"glb") << " is " << r << "\n";); return true; } void assert_bound(bool_var bv, bool is_true, lp_api::bound& b) { if (m_solver->get_status() == lp::lp_status::INFEASIBLE) { return; } scoped_internalize_state st(*this); st.vars().push_back(b.get_var()); st.coeffs().push_back(rational::one()); init_left_side(st); lp::lconstraint_kind k = lp::EQ; bool is_int = b.is_int(); switch (b.get_bound_kind()) { case lp_api::lower_t: k = is_true ? lp::GE : (is_int ? lp::LE : lp::LT); break; case lp_api::upper_t: k = is_true ? lp::LE : (is_int ? lp::GE : lp::GT); break; } if (k == lp::LT || k == lp::LE) { ++m_stats.m_assert_lower; } else { ++m_stats.m_assert_upper; } auto vi = get_var_index(b.get_var()); rational bound = b.get_value(); lp::constraint_index ci; if (is_int && !is_true) { rational bound = b.get_value(false).get_rational(); ci = m_solver->add_var_bound(vi, k, bound); } else { ci = m_solver->add_var_bound(vi, k, b.get_value()); } TRACE("arith", tout << "v" << b.get_var() << "\n";); add_ineq_constraint(ci, literal(bv, !is_true)); propagate_eqs(vi, ci, k, b); } // // fixed equalities. // A fixed equality is inferred if there are two variables v1, v2 whose // upper and lower bounds coincide. // Then the equality v1 == v2 is propagated to the core. // typedef std::pair constraint_bound; vector m_lower_terms; vector m_upper_terms; typedef std::pair value_sort_pair; typedef pair_hash, bool_hash> value_sort_pair_hash; typedef map > value2var; value2var m_fixed_var_table; void propagate_eqs(lp::var_index vi, lp::constraint_index ci, lp::lconstraint_kind k, lp_api::bound& b) { if (propagate_eqs()) { rational const& value = b.get_value(); if (k == lp::GE) { 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) { if (set_upper_bound(vi, ci, value) && has_lower_bound(vi, ci, value)) { fixed_var_eh(b.get_var(), value); } } } } bool dump_lemmas() const { return m_arith_params.m_arith_dump_lemmas; } bool propagate_eqs() const { return m_arith_params.m_arith_propagate_eqs && m_num_conflicts < m_arith_params.m_arith_propagation_threshold; } bound_prop_mode propagation_mode() const { return m_num_conflicts < m_arith_params.m_arith_propagation_threshold ? m_arith_params.m_arith_bound_prop : BP_NONE; } unsigned small_lemma_size() const { return m_arith_params.m_arith_small_lemma_size; } bool proofs_enabled() const { return m.proofs_enabled(); } bool use_tableau() const { return lp_params(ctx().get_params()).simplex_strategy() < 2; } bool set_upper_bound(lp::var_index vi, lp::constraint_index ci, rational const& v) { return set_bound(vi, ci, v, false); } bool set_lower_bound(lp::var_index vi, lp::constraint_index ci, rational const& v) { return set_bound(vi, ci, v, true); } 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. 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; } } } bool var_has_bound(lp::var_index vi, bool is_lower) { bool is_strict = false; rational b; lp::constraint_index ci; if (is_lower) { return m_solver->has_lower_bound(vi, ci, b, is_strict); } else { return m_solver->has_upper_bound(vi, ci, b, is_strict); } } bool has_upper_bound(lp::var_index vi, lp::constraint_index& ci, rational const& bound) { return has_bound(vi, ci, bound, false); } bool has_lower_bound(lp::var_index vi, lp::constraint_index& ci, rational const& bound) { return has_bound(vi, ci, bound, true); } bool has_bound(lp::var_index vi, lp::constraint_index& ci, rational const& bound, bool is_lower) { if (m_solver->is_term(vi)) { lp::var_index ti = m_solver->adjust_term_index(vi); theory_var v = m_term_index2theory_var.get(ti, null_theory_var); rational val; TRACE("arith", tout << vi << " " << v << "\n";); if (v != null_theory_var && a.is_numeral(get_owner(v), val) && bound == val) { ci = UINT_MAX; return bound == val; } auto& vec = is_lower ? m_lower_terms : m_upper_terms; if (vec.size() > ti) { constraint_bound& b = vec[ti]; ci = b.first; return ci != UINT_MAX && bound == b.second; } else { return false; } } else { bool is_strict = false; rational b; if (is_lower) { return m_solver->has_lower_bound(vi, ci, b, is_strict) && b == bound && !is_strict; } else { return m_solver->has_upper_bound(vi, ci, b, is_strict) && b == bound && !is_strict; } } } bool is_equal(theory_var x, theory_var y) const { return get_enode(x)->get_root() == get_enode(y)->get_root(); } void fixed_var_eh(theory_var v1, rational const& bound) { theory_var v2; value_sort_pair key(bound, is_int(v1)); if (m_fixed_var_table.find(key, v2)) { if (static_cast(v2) < th.get_num_vars() && !is_equal(v1, v2)) { auto vi1 = get_var_index(v1); auto vi2 = get_var_index(v2); lp::constraint_index ci1, ci2, ci3, ci4; TRACE("arith", tout << "fixed: " << mk_pp(get_owner(v1), m) << " " << mk_pp(get_owner(v2), m) << " " << bound << " " << has_lower_bound(vi2, ci3, bound) << "\n";); if (has_lower_bound(vi2, ci3, bound) && has_upper_bound(vi2, ci4, bound)) { VERIFY (has_lower_bound(vi1, ci1, bound)); VERIFY (has_upper_bound(vi1, ci2, bound)); ++m_stats.m_fixed_eqs; m_core.reset(); m_eqs.reset(); set_evidence(ci1); set_evidence(ci2); set_evidence(ci3); set_evidence(ci4); enode* x = get_enode(v1); enode* y = get_enode(v2); justification* js = ctx().mk_justification( ext_theory_eq_propagation_justification( get_id(), ctx().get_region(), m_core.size(), m_core.c_ptr(), m_eqs.size(), m_eqs.c_ptr(), x, y, 0, nullptr)); TRACE("arith", for (literal c : m_core) { ctx().display_detailed_literal(tout, c); tout << "\n"; } for (enode_pair const& p : m_eqs) { tout << enode_eq_pp(p, ctx()); } tout << " ==> "; tout << enode_pp(x, ctx()) << " = " << enode_pp(y, ctx()) << "\n"; ); // parameters are TBD. // SASSERT(validate_eq(x, y)); ctx().assign_eq(x, y, eq_justification(js)); } } else { // bounds on v2 were changed. m_fixed_var_table.insert(key, v1); } } else { m_fixed_var_table.insert(key, v1); } } lbool make_feasible() { auto status = m_solver->find_feasible_solution(); TRACE("arith_verbose", display(tout);); switch (status) { case lp::lp_status::INFEASIBLE: return l_false; case lp::lp_status::FEASIBLE: case lp::lp_status::OPTIMAL: // SASSERT(m_solver->all_constraints_hold()); return l_true; case lp::lp_status::TIME_EXHAUSTED: default: TRACE("arith", tout << "status treated as inconclusive: " << status << "\n";); // TENTATIVE_UNBOUNDED, UNBOUNDED, TENTATIVE_DUAL_UNBOUNDED, DUAL_UNBOUNDED, // FLOATING_POINT_ERROR, TIME_EXAUSTED, ITERATIONS_EXHAUSTED, EMPTY, UNSTABLE return l_undef; } } vector> m_explanation; literal_vector m_core; svector m_eqs; vector m_params; // lp::constraint_index const null_constraint_index = UINT_MAX; // not sure what a correct fix is void set_evidence(lp::constraint_index idx) { if (idx == UINT_MAX) { return; } switch (m_constraint_sources[idx]) { case inequality_source: { literal lit = m_inequalities[idx]; SASSERT(lit != null_literal); m_core.push_back(lit); break; } case equality_source: { SASSERT(m_equalities[idx].first != nullptr); SASSERT(m_equalities[idx].second != nullptr); m_eqs.push_back(m_equalities[idx]); break; } case definition_source: { // skip definitions (these are treated as hard constraints) break; } default: UNREACHABLE(); break; } } void set_conflict() { m_explanation.clear(); m_solver->get_infeasibility_explanation(m_explanation); set_conflict1(); } void set_conflict1() { m_eqs.reset(); m_core.reset(); m_params.reset(); // m_solver->shrink_explanation_to_minimum(m_explanation); // todo, enable when perf is fixed /* static unsigned cn = 0; static unsigned num_l = 0; num_l+=m_explanation.size(); std::cout << num_l / (++cn) << "\n"; */ ++m_num_conflicts; ++m_stats.m_conflicts; TRACE("arith", tout << "scope: " << ctx().get_scope_level() << "\n"; display_evidence(tout, m_explanation); ); TRACE("arith", display(tout);); for (auto const& ev : m_explanation) { if (!ev.first.is_zero()) { set_evidence(ev.second); } } // SASSERT(validate_conflict()); dump_conflict(); ctx().set_conflict( ctx().mk_justification( ext_theory_conflict_justification( get_id(), ctx().get_region(), m_core.size(), m_core.c_ptr(), m_eqs.size(), m_eqs.c_ptr(), m_params.size(), m_params.c_ptr()))); } justification * why_is_diseq(theory_var v1, theory_var v2) { return nullptr; } void reset_eh() { m_use_nra_model = false; m_arith_eq_adapter.reset_eh(); m_solver = nullptr; m_internalize_head = 0; m_not_handled = nullptr; del_bounds(0); m_unassigned_bounds.reset(); m_asserted_qhead = 0; m_assume_eq_head = 0; m_scopes.reset(); m_stats.reset(); m_to_check.reset(); reset_variable_values(); } void init_model(model_generator & mg) { init_variable_values(); m_factory = alloc(arith_factory, m); mg.register_factory(m_factory); TRACE("arith", display(tout);); } nlsat::anum const& nl_value(theory_var v, scoped_anum& r) { SASSERT(m_nra); SASSERT(m_use_nra_model); lp::var_index vi = m_theory_var2var_index[v]; if (m_solver->is_term(vi)) { m_todo_terms.push_back(std::make_pair(vi, rational::one())); TRACE("arith", tout << "v" << v << " := w" << vi << "\n"; m_solver->print_term(m_solver->get_term(vi), tout) << "\n";); m_nra->am().set(r, 0); while (!m_todo_terms.empty()) { rational wcoeff = m_todo_terms.back().second; vi = m_todo_terms.back().first; m_todo_terms.pop_back(); lp::lar_term const& term = m_solver->get_term(vi); TRACE("arith", m_solver->print_term(term, tout) << "\n";); scoped_anum r1(m_nra->am()); rational c1(0); m_nra->am().set(r1, c1.to_mpq()); m_nra->am().add(r, r1, r); for (auto const & arg : term) { lp::var_index wi = arg.var(); c1 = arg.coeff() * wcoeff; if (m_solver->is_term(wi)) { m_todo_terms.push_back(std::make_pair(wi, c1)); } else { m_nra->am().set(r1, c1.to_mpq()); m_nra->am().mul(m_nra->value(wi), r1, r1); m_nra->am().add(r1, r, r); } } } return r; } else { return m_nra->value(vi); } } model_value_proc * mk_value(enode * n, model_generator & mg) { theory_var v = n->get_th_var(get_id()); expr* o = n->get_owner(); if (m_use_nra_model) { anum const& an = nl_value(v, *m_a1); if (a.is_int(o) && !m_nra->am().is_int(an)) { return alloc(expr_wrapper_proc, a.mk_numeral(rational::zero(), a.is_int(o))); } return alloc(expr_wrapper_proc, a.mk_numeral(nl_value(v, *m_a1), a.is_int(o))); } else { rational r = get_value(v); TRACE("arith", tout << "v" << v << " := " << r << "\n";); SASSERT(!a.is_int(o) || r.is_int()); if (a.is_int(o) && !r.is_int()) r = floor(r); return alloc(expr_wrapper_proc, m_factory->mk_value(r, m.get_sort(o))); } } bool get_value(enode* n, rational& val) { theory_var v = n->get_th_var(get_id()); if (!can_get_bound(v)) return false; lp::var_index vi = m_theory_var2var_index[v]; if (m_solver->has_value(vi, val)) { TRACE("arith", tout << expr_ref(n->get_owner(), m) << " := " << val << "\n";); if (is_int(n) && !val.is_int()) return false; return true; } else { return false; } } bool get_value(enode* n, expr_ref& r) { rational val; if (get_value(n, val)) { r = a.mk_numeral(val, is_int(n)); return true; } else { return false; } } bool get_lower(enode* n, rational& val, bool& is_strict) { theory_var v = n->get_th_var(get_id()); if (!can_get_bound(v)) { TRACE("arith", tout << "cannot get lower for " << v << "\n";); return false; } lp::var_index vi = m_theory_var2var_index[v]; lp::constraint_index ci; return m_solver->has_lower_bound(vi, ci, val, is_strict); } bool get_lower(enode* n, expr_ref& r) { bool is_strict; rational val; if (get_lower(n, val, is_strict) && !is_strict) { r = a.mk_numeral(val, is_int(n)); return true; } return false; } bool get_upper(enode* n, rational& val, bool& is_strict) { theory_var v = n->get_th_var(get_id()); if (!can_get_bound(v)) return false; lp::var_index vi = m_theory_var2var_index[v]; lp::constraint_index ci; return m_solver->has_upper_bound(vi, ci, val, is_strict); } bool get_upper(enode* n, expr_ref& r) { bool is_strict; rational val; if (get_upper(n, val, is_strict) && !is_strict) { r = a.mk_numeral(val, is_int(n)); return true; } return false; } // Auxiliary verification utilities. struct scoped_arith_mode { smt_params& p; scoped_arith_mode(smt_params& p) : p(p) { p.m_arith_mode = AS_OLD_ARITH; } ~scoped_arith_mode() { p.m_arith_mode = AS_NEW_ARITH; } }; void dump_conflict() { if (dump_lemmas()) { unsigned id = ctx().display_lemma_as_smt_problem(m_core.size(), m_core.c_ptr(), m_eqs.size(), m_eqs.c_ptr(), false_literal); (void)id; } } bool validate_conflict() { if (m_arith_params.m_arith_mode != AS_NEW_ARITH) return true; scoped_arith_mode _sa(ctx().get_fparams()); context nctx(m, ctx().get_fparams(), ctx().get_params()); add_background(nctx); cancel_eh eh(m.limit()); scoped_timer timer(1000, &eh); bool result = l_true != nctx.check(); CTRACE("arith", !result, ctx().display_lemma_as_smt_problem(tout, m_core.size(), m_core.c_ptr(), m_eqs.size(), m_eqs.c_ptr(), false_literal);); return result; } void dump_assign(literal lit) { if (dump_lemmas()) { unsigned id = ctx().display_lemma_as_smt_problem(m_core.size(), m_core.c_ptr(), m_eqs.size(), m_eqs.c_ptr(), lit); (void)id; } } bool validate_assign(literal lit) { if (m_arith_params.m_arith_mode != AS_NEW_ARITH) return true; scoped_arith_mode _sa(ctx().get_fparams()); context nctx(m, ctx().get_fparams(), ctx().get_params()); m_core.push_back(~lit); add_background(nctx); m_core.pop_back(); cancel_eh eh(m.limit()); scoped_timer timer(1000, &eh); bool result = l_true != nctx.check(); CTRACE("arith", !result, ctx().display_lemma_as_smt_problem(tout, m_core.size(), m_core.c_ptr(), m_eqs.size(), m_eqs.c_ptr(), lit); display(tout);); return result; } bool validate_eq(enode* x, enode* y) { if (m_arith_params.m_arith_mode == AS_NEW_ARITH) return true; context nctx(m, ctx().get_fparams(), ctx().get_params()); add_background(nctx); nctx.assert_expr(m.mk_not(m.mk_eq(x->get_owner(), y->get_owner()))); cancel_eh eh(m.limit()); scoped_timer timer(1000, &eh); return l_true != nctx.check(); } void add_background(context& nctx) { for (literal c : m_core) { expr_ref tmp(m); ctx().literal2expr(c, tmp); nctx.assert_expr(tmp); } for (auto const& eq : m_eqs) { nctx.assert_expr(m.mk_eq(eq.first->get_owner(), eq.second->get_owner())); } } theory_lra::inf_eps value(theory_var v) { lp::impq ival = get_ivalue(v); return inf_eps(rational(0), inf_rational(ival.x, ival.y)); } theory_lra::inf_eps maximize(theory_var v, expr_ref& blocker, bool& has_shared) { lp::impq term_max; lp::lp_status st; lp::var_index vi = 0; if (!can_get_bound(v)) { TRACE("arith", tout << "cannot get bound for v" << v << "\n";); st = lp::lp_status::UNBOUNDED; } else { vi = m_theory_var2var_index[v]; st = m_solver->maximize_term(vi, term_max); } switch (st) { case lp::lp_status::OPTIMAL: { init_variable_values(); TRACE("arith", display(tout << st << " v" << v << " vi: " << vi << "\n");); inf_rational val = get_value(v); // inf_rational val(term_max.x, term_max.y); blocker = mk_gt(v); return inf_eps(rational::zero(), val); } case lp::lp_status::FEASIBLE: { inf_rational val = get_value(v); TRACE("arith", display(tout << st << " v" << v << " vi: " << vi << "\n");); blocker = mk_gt(v); return inf_eps(rational::zero(), val); } default: SASSERT(st == lp::lp_status::UNBOUNDED); TRACE("arith", display(tout << st << " v" << v << " vi: " << vi << "\n");); has_shared = false; blocker = m.mk_false(); return inf_eps(rational::one(), inf_rational()); } } expr_ref mk_gt(theory_var v) { lp::impq val = get_ivalue(v); expr* obj = get_enode(v)->get_owner(); rational r = val.x; expr_ref e(m); if (a.is_int(m.get_sort(obj))) { if (r.is_int()) { r += rational::one(); } else { r = ceil(r); } e = a.mk_numeral(r, m.get_sort(obj)); e = a.mk_ge(obj, e); } else { e = a.mk_numeral(r, m.get_sort(obj)); if (val.y.is_neg()) { e = a.mk_ge(obj, e); } else { e = a.mk_gt(obj, e); } } TRACE("opt", tout << "v" << v << " " << val << " " << r << " " << e << "\n";); return e; } theory_var add_objective(app* term) { TRACE("opt", tout << expr_ref(term, m) << "\n";); return internalize_def(term); } void term2coeffs(lp::lar_term const& term, u_map& coeffs, rational const& coeff, rational& offset) { for (const auto & ti : term) { theory_var w; if (m_solver->is_term(ti.var())) { //w = m_term_index2theory_var.get(m_solver->adjust_term_index(ti.var()), null_theory_var); //if (w == null_theory_var) // if extracting expressions directly from nested term lp::lar_term const& term1 = m_solver->get_term(ti.var()); rational coeff2 = coeff * ti.coeff(); term2coeffs(term1, coeffs, coeff2, offset); continue; } else { w = m_var_index2theory_var[ti.var()]; } rational c0(0); coeffs.find(w, c0); coeffs.insert(w, c0 + ti.coeff() * coeff); } } app_ref coeffs2app(u_map const& coeffs, rational const& offset, bool is_int) { expr_ref_vector args(m); for (auto const& kv : coeffs) { theory_var w = kv.m_key; expr* o = get_enode(w)->get_owner(); if (kv.m_value.is_zero()) { // continue } else if (kv.m_value.is_one()) { args.push_back(o); } else { args.push_back(a.mk_mul(a.mk_numeral(kv.m_value, is_int), o)); } } if (!offset.is_zero()) { args.push_back(a.mk_numeral(offset, is_int)); } switch (args.size()) { case 0: return app_ref(a.mk_numeral(rational::zero(), is_int), m); case 1: return app_ref(to_app(args[0].get()), m); default: return app_ref(a.mk_add(args.size(), args.c_ptr()), m); } } app_ref mk_term(lp::lar_term const& term, bool is_int) { u_map coeffs; rational offset; term2coeffs(term, coeffs, rational::one(), offset); return coeffs2app(coeffs, offset, is_int); } rational gcd_reduce(u_map& coeffs) { rational g(0); for (auto const& kv : coeffs) { g = gcd(g, kv.m_value); } if (g.is_zero()) return rational::one(); if (!g.is_one()) { for (auto& kv : coeffs) { kv.m_value /= g; } } return g; } app_ref mk_obj(theory_var v) { lp::var_index vi = get_var_index(v); bool is_int = a.is_int(get_enode(v)->get_owner()); if (m_solver->is_term(vi)) { return mk_term(m_solver->get_term(vi), is_int); } else { theory_var w = m_var_index2theory_var[vi]; return app_ref(get_enode(w)->get_owner(), m); } } expr_ref mk_ge(generic_model_converter& fm, theory_var v, inf_rational const& val) { rational r = val.get_rational(); bool is_strict = val.get_infinitesimal().is_pos(); app_ref b(m); bool is_int = a.is_int(get_enode(v)->get_owner()); if (is_strict) { b = a.mk_le(mk_obj(v), a.mk_numeral(r, is_int)); } else { b = a.mk_ge(mk_obj(v), a.mk_numeral(r, is_int)); } if (!ctx().b_internalized(b)) { fm.hide(b->get_decl()); bool_var bv = ctx().mk_bool_var(b); ctx().set_var_theory(bv, get_id()); // ctx().set_enode_flag(bv, true); lp_api::bound_kind bkind = lp_api::bound_kind::lower_t; if (is_strict) bkind = lp_api::bound_kind::upper_t; lp_api::bound* a = alloc(lp_api::bound, bv, v, is_int, r, bkind); mk_bound_axioms(*a); updt_unassigned_bounds(v, +1); m_bounds[v].push_back(a); m_bounds_trail.push_back(v); m_bool_var2bound.insert(bv, a); TRACE("arith", tout << mk_pp(b, m) << "\n";); } if (is_strict) { b = m.mk_not(b); } TRACE("arith", tout << b << "\n";); return expr_ref(b, m); } void display(std::ostream & out) const { if (m_solver) { m_solver->print_constraints(out); m_solver->print_terms(out); // auto pp = lp ::core_solver_pretty_printer(m_solver->m_mpq_lar_core_solver.m_r_solver, out); // pp.print(); } unsigned nv = th.get_num_vars(); for (unsigned v = 0; v < nv; ++v) { if (!ctx().is_relevant(get_enode(v))) out << "irr: "; out << "v" << v; if (can_get_value(v)) out << " = " << get_value(v); if (is_int(v)) out << ", int"; if (ctx().is_shared(get_enode(v))) out << ", shared"; out << " := "; th.display_var_flat_def(out, v) << "\n"; } } void display_evidence(std::ostream& out, vector> const& evidence) { for (auto const& ev : evidence) { expr_ref e(m); SASSERT(!ev.first.is_zero()); if (ev.first.is_zero()) { continue; } unsigned idx = ev.second; switch (m_constraint_sources.get(idx, null_source)) { case inequality_source: { literal lit = m_inequalities[idx]; ctx().literal2expr(lit, e); out << e << " " << ctx().get_assignment(lit) << "\n"; break; } case equality_source: out << enode_eq_pp(m_equalities[idx], ctx()); break; case definition_source: { theory_var v = m_definitions[idx]; if (v != null_theory_var) out << "def: v" << v << " := " << mk_pp(th.get_enode(v)->get_owner(), m) << "\n"; break; } case null_source: default: UNREACHABLE(); break; } } for (auto const& ev : evidence) { m_solver->print_constraint(ev.second, out << ev.first << ": "); } } void collect_statistics(::statistics & st) const { m_arith_eq_adapter.collect_statistics(st); st.update("arith-lower", m_stats.m_assert_lower); st.update("arith-upper", m_stats.m_assert_upper); st.update("arith-rows", m_stats.m_add_rows); st.update("arith-propagations", m_stats.m_bounds_propagations); st.update("arith-iterations", m_stats.m_num_iterations); st.update("arith-factorizations", m_solver->settings().st().m_num_factorizations); st.update("arith-pivots", m_stats.m_need_to_solve_inf); st.update("arith-plateau-iterations", m_stats.m_num_iterations_with_no_progress); st.update("arith-fixed-eqs", m_stats.m_fixed_eqs); st.update("arith-conflicts", m_stats.m_conflicts); st.update("arith-bound-propagations-lp", m_stats.m_bound_propagations1); st.update("arith-bound-propagations-cheap", m_stats.m_bound_propagations2); st.update("arith-diseq", m_stats.m_assert_diseq); st.update("arith-make-feasible", m_solver->settings().st().m_make_feasible); st.update("arith-max-columns", m_solver->settings().st().m_max_cols); st.update("arith-max-rows", m_solver->settings().st().m_max_rows); st.update("gcd-calls", m_solver->settings().st().m_gcd_calls); st.update("gcd-conflict", m_solver->settings().st().m_gcd_conflicts); st.update("cube-calls", m_solver->settings().st().m_cube_calls); st.update("cube-success", m_solver->settings().st().m_cube_success); st.update("arith-patches", m_solver->settings().st().m_patches); st.update("arith-patches-success", m_solver->settings().st().m_patches_success); st.update("arith-hnf-calls", m_solver->settings().st().m_hnf_cutter_calls); st.update("arith-hnf-cuts", m_solver->settings().st().m_hnf_cuts); } }; theory_lra::theory_lra(ast_manager& m, theory_arith_params& ap): theory(m.get_family_id("arith")) { m_imp = alloc(imp, *this, m, ap); } theory_lra::~theory_lra() { dealloc(m_imp); } theory* theory_lra::mk_fresh(context* new_ctx) { return alloc(theory_lra, new_ctx->get_manager(), new_ctx->get_fparams()); } void theory_lra::init(context * ctx) { theory::init(ctx); m_imp->init(ctx); } bool theory_lra::internalize_atom(app * atom, bool gate_ctx) { return m_imp->internalize_atom(atom, gate_ctx); } bool theory_lra::internalize_term(app * term) { return m_imp->internalize_term(term); } void theory_lra::internalize_eq_eh(app * atom, bool_var v) { m_imp->internalize_eq_eh(atom, v); } void theory_lra::assign_eh(bool_var v, bool is_true) { m_imp->assign_eh(v, is_true); } lbool theory_lra::get_phase(bool_var v) { return m_imp->get_phase(v); } void theory_lra::new_eq_eh(theory_var v1, theory_var v2) { m_imp->new_eq_eh(v1, v2); } bool theory_lra::use_diseqs() const { return m_imp->use_diseqs(); } void theory_lra::new_diseq_eh(theory_var v1, theory_var v2) { m_imp->new_diseq_eh(v1, v2); } void theory_lra::push_scope_eh() { theory::push_scope_eh(); m_imp->push_scope_eh(); } void theory_lra::pop_scope_eh(unsigned num_scopes) { m_imp->pop_scope_eh(num_scopes); theory::pop_scope_eh(num_scopes); } void theory_lra::restart_eh() { m_imp->restart_eh(); } void theory_lra::relevant_eh(app* e) { m_imp->relevant_eh(e); } void theory_lra::init_search_eh() { m_imp->init_search_eh(); } final_check_status theory_lra::final_check_eh() { return m_imp->final_check_eh(); } bool theory_lra::is_shared(theory_var v) const { return m_imp->is_shared(v); } bool theory_lra::can_propagate() { return m_imp->can_propagate(); } void theory_lra::propagate() { m_imp->propagate(); } justification * theory_lra::why_is_diseq(theory_var v1, theory_var v2) { return m_imp->why_is_diseq(v1, v2); } void theory_lra::reset_eh() { m_imp->reset_eh(); } void theory_lra::init_model(model_generator & m) { m_imp->init_model(m); } model_value_proc * theory_lra::mk_value(enode * n, model_generator & mg) { return m_imp->mk_value(n, mg); } bool theory_lra::get_value(enode* n, rational& r) { return m_imp->get_value(n, r); } bool theory_lra::get_value(enode* n, expr_ref& r) { return m_imp->get_value(n, r); } bool theory_lra::get_lower(enode* n, expr_ref& r) { return m_imp->get_lower(n, r); } bool theory_lra::get_upper(enode* n, expr_ref& r) { return m_imp->get_upper(n, r); } bool theory_lra::get_lower(enode* n, rational& r, bool& is_strict) { return m_imp->get_lower(n, r, is_strict); } bool theory_lra::get_upper(enode* n, rational& r, bool& is_strict) { return m_imp->get_upper(n, r, is_strict); } void theory_lra::display(std::ostream & out) const { m_imp->display(out); } void theory_lra::collect_statistics(::statistics & st) const { m_imp->collect_statistics(st); } theory_lra::inf_eps theory_lra::value(theory_var v) { return m_imp->value(v); } theory_lra::inf_eps theory_lra::maximize(theory_var v, expr_ref& blocker, bool& has_shared) { return m_imp->maximize(v, blocker, has_shared); } theory_var theory_lra::add_objective(app* term) { return m_imp->add_objective(term); } expr_ref theory_lra::mk_ge(generic_model_converter& fm, theory_var v, inf_rational const& val) { return m_imp->mk_ge(fm, v, val); } } z3-z3-4.8.7/src/smt/theory_lra.h000066400000000000000000000051641356505360400163500ustar00rootroot00000000000000/*++ Copyright (c) 2016 Microsoft Corporation Module Name: theory_lra.h Abstract: Author: Lev Nachmanson (levnach) 2016-25-3 Nikolaj Bjorner (nbjorner) Revision History: --*/ #pragma once #include "smt/theory_opt.h" namespace smt { class theory_lra : public theory, public theory_opt { class imp; imp* m_imp; public: theory_lra(ast_manager& m, theory_arith_params& ap); ~theory_lra() override; theory* mk_fresh(context* new_ctx) override; char const* get_name() const override { return "arithmetic"; } void init(context * ctx) override; bool internalize_atom(app * atom, bool gate_ctx) override; bool internalize_term(app * term) override; void internalize_eq_eh(app * atom, bool_var v) override; void assign_eh(bool_var v, bool is_true) override; lbool get_phase(bool_var v) override; void new_eq_eh(theory_var v1, theory_var v2) override; bool use_diseqs() const override; void new_diseq_eh(theory_var v1, theory_var v2) override; void push_scope_eh() override; void pop_scope_eh(unsigned num_scopes) override; void restart_eh() override; void relevant_eh(app* e) override; void init_search_eh() override; final_check_status final_check_eh() override; bool is_shared(theory_var v) const override; bool can_propagate() override; void propagate() override; justification * why_is_diseq(theory_var v1, theory_var v2) override; // virtual void flush_eh(); void reset_eh() override; void init_model(model_generator & m) override; model_value_proc * mk_value(enode * n, model_generator & mg) override; bool get_value(enode* n, expr_ref& r) override; bool get_value(enode* n, rational& r); bool get_lower(enode* n, expr_ref& r); bool get_upper(enode* n, expr_ref& r); bool get_lower(enode* n, rational& r, bool& is_strict); bool get_upper(enode* n, rational& r, bool& is_strict); void display(std::ostream & out) const override; void collect_statistics(::statistics & st) const override; // optimization expr_ref mk_ge(generic_model_converter& fm, theory_var v, inf_rational const& val); inf_eps value(theory_var) override; inf_eps maximize(theory_var v, expr_ref& blocker, bool& has_shared) override; theory_var add_objective(app* term) override; }; } z3-z3-4.8.7/src/smt/theory_opt.cpp000066400000000000000000000035311356505360400167230ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: theory_opt.cpp Abstract: Interface utilities used by optimization providing theory solvers. Author: Nikolaj Bjorner (nbjorner) 2013-10-18 Notes: --*/ #include "ast/arith_decl_plugin.h" #include "smt/smt_types.h" #include "smt/smt_theory.h" #include "smt/theory_opt.h" namespace smt { bool theory_opt::is_linear(ast_manager& m, expr* term) { arith_util a(m); ptr_vector todo; ast_mark mark; todo.push_back(term); expr* t1, *t2; while (!todo.empty()) { term = todo.back(); todo.pop_back(); if (mark.is_marked(term)) { continue; } mark.mark(term, true); if (!is_app(term)) { return false; } app* t = to_app(term); if (t->get_family_id() != a.get_family_id()) { // done } else if (a.is_add(t) || a.is_to_real(t) || a.is_to_int(t) || a.is_uminus(t) || a.is_numeral(t) || a.is_sub(t)) { todo.append(t->get_num_args(), t->get_args()); } else if (a.is_mul(t, t1, t2)) { if (is_numeral(a, t1)) { todo.push_back(t2); } else if (is_numeral(a, t2)) { todo.push_back(t1); } else { return false; } } else { return false; } } return true; } bool theory_opt::is_numeral(arith_util& a, expr* term) { while (a.is_uminus(term) || a.is_to_real(term) || a.is_to_int(term)) { term = to_app(term)->get_arg(0); } return a.is_numeral(term); } }; z3-z3-4.8.7/src/smt/theory_opt.h000066400000000000000000000015011356505360400163630ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: theory_opt.h Abstract: Interface utilities used by optimization providing theory solvers. Author: Nikolaj Bjorner (nbjorner) 2013-10-18 Notes: --*/ #include "util/inf_rational.h" #include "util/inf_eps_rational.h" #include "ast/arith_decl_plugin.h" #ifndef THEORY_OPT_H_ #define THEORY_OPT_H_ class generic_model_converter; namespace smt { class theory_opt { public: typedef inf_eps_rational inf_eps; virtual inf_eps value(theory_var) = 0; virtual inf_eps maximize(theory_var v, expr_ref& blocker, bool& has_shared) = 0; virtual theory_var add_objective(app* term) = 0; bool is_linear(ast_manager& m, expr* term); bool is_numeral(arith_util& a, expr* term); }; } #endif z3-z3-4.8.7/src/smt/theory_pb.cpp000066400000000000000000002345531356505360400165340ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: theory_pb.cpp Abstract: Pseudo-Boolean theory plugin. Author: Nikolaj Bjorner (nbjorner) 2013-11-05 Notes: --*/ #include #include "smt/theory_pb.h" #include "smt/smt_context.h" #include "smt/smt_kernel.h" #include "ast/ast_pp.h" #include "util/sorting_network.h" #include "util/uint_set.h" #include "smt/smt_model_generator.h" #include "ast/rewriter/pb_rewriter_def.h" #include "math/simplex/sparse_matrix_def.h" #include "math/simplex/simplex_def.h" namespace smt { class pb_lit_rewriter_util { public: typedef std::pair arg_t; typedef vector args_t; typedef rational numeral; literal negate(literal l) { return ~l; } void display(std::ostream& out, literal l) { out << l; } bool is_negated(literal l) const { return l.sign(); } bool is_true(literal l) const { return l == true_literal; } bool is_false(literal l) const { return l == false_literal; } struct compare { bool operator()(arg_t const& a, arg_t const& b) { return a.first < b.first; } }; }; unsigned theory_pb::arg_t::get_hash() const { return get_composite_hash(*this, size()); } bool theory_pb::arg_t::operator==(arg_t const& other) const { if (size() != other.size()) return false; for (unsigned i = 0; i < size(); ++i) { if (lit(i) != other.lit(i)) return false; if (coeff(i) != other.coeff(i)) return false; } return true; } void theory_pb::arg_t::remove_negations() { for (unsigned i = 0; i < size(); ++i) { if (lit(i).sign()) { (*this)[i].first.neg(); (*this)[i].second.neg(); m_k += coeff(i); } } } void theory_pb::arg_t::negate() { numeral sum(0); for (unsigned i = 0; i < size(); ++i) { (*this)[i].first.neg(); sum += coeff(i); } m_k = sum - m_k + numeral::one(); VERIFY(l_undef == normalize(false)); } lbool theory_pb::arg_t::normalize(bool is_eq) { pb_lit_rewriter_util pbu; pb_rewriter_util util(pbu); return util.normalize(*this, m_k, is_eq); } void theory_pb::arg_t::prune(bool is_eq) { pb_lit_rewriter_util pbu; pb_rewriter_util util(pbu); util.prune(*this, m_k, is_eq); } std::ostream& theory_pb::arg_t::display(context& ctx, std::ostream& out, bool values) const { for (unsigned i = 0; i < size(); ++i) { literal l(lit(i)); if (!coeff(i).is_one()) { out << coeff(i) << "*"; } out << l; if (values) { out << "@(" << ctx.get_assignment(l); if (ctx.get_assignment(l) != l_undef) { out << ":" << ctx.get_assign_level(l); } out << ")"; } if (i + 1 < size()) { out << " + "; } } out << " ~ " << k() << "\n"; return out; } app_ref theory_pb::arg_t::to_expr(bool is_eq, context& ctx, ast_manager& m) { expr_ref tmp(m); app_ref result(m); svector coeffs; expr_ref_vector args(m); for (unsigned i = 0; i < size(); ++i) { ctx.literal2expr(lit(i), tmp); args.push_back(tmp); coeffs.push_back(coeff(i)); } pb_util pb(m); if (is_eq) { result = pb.mk_eq(coeffs.size(), coeffs.c_ptr(), args.c_ptr(), k()); } else { result = pb.mk_ge(coeffs.size(), coeffs.c_ptr(), args.c_ptr(), k()); } return result; } void theory_pb::ineq::reset() { m_max_watch.reset(); m_watch_sz = 0; m_watch_sum.reset(); m_num_propagations = 0; m_args[0].reset(); m_args[0].m_k.reset(); m_args[1].reset(); m_args[1].m_k.reset(); m_nfixed = 0; m_max_sum.reset(); m_min_sum.reset(); } void theory_pb::ineq::unique() { pb_lit_rewriter_util pbu; pb_rewriter_util util(pbu); util.unique(m_args[0], m_args[0].m_k, m_is_eq); } void theory_pb::ineq::post_prune() { if (!m_args[0].empty() && is_ge()) { m_args[0].negate(); m_args[0].negate(); m_args[1].reset(); m_args[1].m_k = m_args[0].m_k; m_args[1].append(m_args[0]); m_args[1].negate(); SASSERT(m_args[0].size() == m_args[1].size()); SASSERT(m_args[0].well_formed()); SASSERT(m_args[1].well_formed()); } } void theory_pb::ineq::negate() { SASSERT(!m_is_eq); m_lit.neg(); } void theory_pb::ineq::prune() { m_args[0].prune(m_is_eq); } lbool theory_pb::ineq::normalize() { return m_args[0].normalize(m_is_eq); } app_ref theory_pb::ineq::to_expr(context& ctx, ast_manager& m) { return args().to_expr(m_is_eq, ctx, m); } bool theory_pb::arg_t::well_formed() const { SASSERT(k().is_pos()); uint_set vars; numeral sum = numeral::zero(); for (unsigned i = 0; i < size(); ++i) { SASSERT(coeff(i) <= k()); SASSERT(numeral::one() <= coeff(i)); SASSERT(lit(i) != true_literal); SASSERT(lit(i) != false_literal); SASSERT(lit(i) != null_literal); SASSERT(!vars.contains(lit(i).var())); vars.insert(lit(i).var()); sum += coeff(i); } SASSERT(sum >= k()); return true; } // ----------------------------- // cardinality constraints void theory_pb::card::negate() { m_lit.neg(); unsigned sz = size(); for (unsigned i = 0; i < sz; ++i) { m_args[i].neg(); } m_bound = sz - m_bound + 1; SASSERT(sz >= m_bound && m_bound > 0); } app_ref theory_pb::card::to_expr(theory_pb& th) { ast_manager& m = th.get_manager(); expr_ref_vector args(m); for (unsigned i = 0; i < size(); ++i) { args.push_back(th.literal2expr(m_args[i])); } return app_ref(th.pb.mk_at_least_k(args.size(), args.c_ptr(), k()), m); } lbool theory_pb::card::assign(theory_pb& th, literal alit) { // literal is assigned to false. context& ctx = th.get_context(); unsigned sz = size(); unsigned bound = k(); TRACE("pb", th.display(tout << "assign: " << m_lit << " " << ~alit << " " << bound << "\n", *this, true);); SASSERT(0 < bound && bound < sz); SASSERT(ctx.get_assignment(alit) == l_false); SASSERT(ctx.get_assignment(m_lit) == l_true); unsigned index = 0; for (index = 0; index <= bound; ++index) { if (lit(index) == alit) { break; } } if (index == bound + 1) { // literal is no longer watched. return l_undef; } SASSERT(index <= bound); SASSERT(lit(index) == alit); // find a literal to swap with: for (unsigned i = bound + 1; i < sz; ++i) { literal lit2 = lit(i); if (ctx.get_assignment(lit2) != l_false) { TRACE("pb", tout << "swap " << lit2 << "\n";); std::swap(m_args[index], m_args[i]); th.watch_literal(lit2, this); return l_undef; } } // conflict if (bound != index && ctx.get_assignment(lit(bound)) == l_false) { TRACE("pb", tout << "conflict " << lit(bound) << " " << alit << "\n";); set_conflict(th, alit); return l_false; } TRACE("pb", tout << "no swap " << index << " " << alit << "\n";); // there are no literals to swap with, // prepare for unit propagation by swapping the false literal into // position bound. Then literals in positions 0..bound-1 have to be // assigned l_true. if (index != bound) { std::swap(m_args[index], m_args[bound]); } SASSERT(th.validate_unit_propagation(*this)); for (unsigned i = 0; i < bound && !ctx.inconsistent(); ++i) { th.add_assign(*this, lit(i)); } return ctx.inconsistent() ? l_false : l_true; } /** \brief The conflict clause position for cardinality constraint have the following properties: 0. The position for the literal corresponding to the cardinality constraint. 1. The literal at position 0 of the cardinality constraint. 2. The asserting literal. 3. .. the remaining false literals. */ void theory_pb::card::set_conflict(theory_pb& th, literal l) { SASSERT(validate_conflict(th)); context& ctx = th.get_context(); (void)ctx; literal_vector& lits = th.get_literals(); SASSERT(ctx.get_assignment(l) == l_false); SASSERT(ctx.get_assignment(lit()) == l_true); lits.push_back(~lit()); lits.push_back(l); unsigned sz = size(); for (unsigned i = m_bound; i < sz; ++i) { SASSERT(ctx.get_assignment(m_args[i]) == l_false); lits.push_back(m_args[i]); } th.add_clause(*this, lits); } bool theory_pb::card::validate_conflict(theory_pb& th) { context& ctx = th.get_context(); unsigned num_false = 0; for (unsigned i = 0; i < size(); ++i) { if (ctx.get_assignment(m_args[i]) == l_false) { ++num_false; } } return size() - num_false < m_bound; } bool theory_pb::card::validate_assign(theory_pb& th, literal_vector const& lits, literal l) { context& ctx = th.get_context(); VERIFY(ctx.get_assignment(l) == l_undef); for (unsigned i = 0; i < lits.size(); ++i) { SASSERT(ctx.get_assignment(lits[i]) == l_true); } return size() - lits.size() <= m_bound; } void theory_pb::card::init_watch(theory_pb& th, bool is_true) { context& ctx = th.get_context(); th.clear_watch(*this); if (lit().sign() == is_true) { negate(); } SASSERT(ctx.get_assignment(lit()) == l_true); unsigned j = 0, sz = size(), bound = k(); if (bound == sz) { for (unsigned i = 0; i < sz && !ctx.inconsistent(); ++i) { th.add_assign(*this, lit(i)); } return; } // put the non-false literals into the head. for (unsigned i = 0; i < sz; ++i) { if (ctx.get_assignment(lit(i)) != l_false) { if (j != i) { std::swap(m_args[i], m_args[j]); } ++j; } } DEBUG_CODE( bool is_false = false; for (unsigned k = 0; k < sz; ++k) { SASSERT(!is_false || ctx.get_assignment(lit(k)) == l_false); is_false = ctx.get_assignment(lit(k)) == l_false; }); // j is the number of non-false, sz - j the number of false. if (j < bound) { SASSERT(0 < bound && bound < sz); literal alit = lit(j); // // we need the assignment level of the asserting literal to be maximal. // such that conflict resolution can use the asserting literal as a starting // point. // for (unsigned i = bound; i < sz; ++i) { if (ctx.get_assign_level(alit) < ctx.get_assign_level(lit(i))) { std::swap(m_args[j], m_args[i]); alit = lit(j); } } set_conflict(th, alit); } else if (j == bound) { for (unsigned i = 0; i < bound && !ctx.inconsistent(); ++i) { th.add_assign(*this, lit(i)); } } else { for (unsigned i = 0; i <= bound; ++i) { th.watch_literal(lit(i), this); } } } void theory_pb::card::add_arg(literal lit) { if (lit == false_literal) { return; } else if (lit == true_literal) { if (m_bound > 0) { --m_bound; } } else { m_args.push_back(lit); } } void theory_pb::card::inc_propagations(theory_pb& th) { ++m_num_propagations; } // ------------------------ // theory_pb theory_pb::theory_pb(ast_manager& m, theory_pb_params& p): theory(m.mk_family_id("pb")), m_params(p), pb(m), m_restart_lim(3), m_restart_inc(0), m_antecedent_exprs(m), m_cardinality_exprs(m) { m_learn_complements = p.m_pb_learn_complements; m_conflict_frequency = p.m_pb_conflict_frequency; } theory_pb::~theory_pb() { reset_eh(); } theory * theory_pb::mk_fresh(context * new_ctx) { return alloc(theory_pb, new_ctx->get_manager(), m_params); } bool theory_pb::internalize_atom(app * atom, bool gate_ctx) { context& ctx = get_context(); ast_manager& m = get_manager(); if (ctx.b_internalized(atom)) { return true; } SASSERT(!ctx.b_internalized(atom)); m_stats.m_num_predicates++; if (pb.is_aux_bool(atom)) { bool_var abv = ctx.mk_bool_var(atom); ctx.set_var_theory(abv, get_id()); return true; } if (internalize_card(atom, gate_ctx)) { return true; } SASSERT(pb.is_at_most_k(atom) || pb.is_le(atom) || pb.is_ge(atom) || pb.is_at_least_k(atom) || pb.is_eq(atom)); 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); numeral& k = c->m_args[0].m_k; arg_t& args = c->m_args[0]; // extract literals and coefficients. for (unsigned i = 0; i < num_args; ++i) { expr* arg = atom->get_arg(i); literal l = compile_arg(arg); numeral c = pb.get_coeff(atom, i); switch (ctx.get_assignment(l)) { case l_true: k -= c; break; case l_false: break; default: args.push_back(std::make_pair(l, c)); break; } } if (pb.is_at_most_k(atom) || pb.is_le(atom)) { k = -k; for (auto& a : args) { a.first.neg(); k += a.second; } } c->unique(); lbool is_true = c->normalize(); c->prune(); c->post_prune(); TRACE("pb", display(tout, *c); tout << " := " << lit << " " << is_true << "\n";); switch (is_true) { case l_false: lit.neg(); // fall-through case l_true: ctx.mk_th_axiom(get_id(), 1, &lit); dealloc(c); return true; case l_undef: break; } if (c->k().is_one() && c->is_ge()) { literal_vector& lits = get_literals(); lits.push_back(~lit); for (unsigned i = 0; i < c->size(); ++i) { lits.push_back(c->lit(i)); SASSERT(c->coeff(i).is_one()); ctx.mk_th_axiom(get_id(), lit, ~c->lit(i)); } ctx.mk_th_axiom(get_id(), lits.size(), lits.c_ptr()); return true; } // maximal coefficient: scoped_mpz& max_watch = c->m_max_watch; max_watch.reset(); for (auto const& a : args) { mpz const& num = a.second.to_mpq().numerator(); if (m_mpz_mgr.lt(max_watch, num)) { max_watch = num; } } 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; } literal theory_pb::compile_arg(expr* arg) { context& ctx = get_context(); ast_manager& m = get_manager(); bool_var bv; bool has_bv = false; bool negate = m.is_not(arg, arg); SASSERT(!m.is_not(arg)); if (!ctx.b_internalized(arg)) { ctx.internalize(arg, false); } if (ctx.b_internalized(arg)) { bv = ctx.get_bool_var(arg); if (is_uninterp(arg) && null_theory_var == ctx.get_var_theory(bv)) { ctx.set_var_theory(bv, get_id()); } has_bv = (ctx.get_var_theory(bv) == get_id()); } else if (m.is_true(arg)) { bv = true_bool_var; has_bv = true; } else if (m.is_false(arg)) { bv = true_bool_var; has_bv = true; negate = !negate; } // assumes relevancy level = 2 or 0. // TBD: should should have been like an uninterpreted // function internalize, where enodes for each argument // is available. if (!has_bv) { app_ref tmp(m), fml(m); tmp = pb.mk_fresh_bool(); fml = m.mk_iff(tmp, arg); TRACE("pb", tout << "create proxy " << fml << "\n";); ctx.internalize(fml, false); SASSERT(ctx.b_internalized(tmp)); bv = ctx.get_bool_var(tmp); SASSERT(get_id() == ctx.get_var_theory(bv)); literal lit(ctx.get_bool_var(fml)); ctx.mk_th_axiom(get_id(), 1, &lit); ctx.mark_as_relevant(tmp.get()); } return negate?~literal(bv):literal(bv); } void theory_pb::del_watch(ineq_watch& watch, unsigned index, ineq& c, unsigned ineq_index) { SASSERT(c.is_ge()); if (index < watch.size()) { std::swap(watch[index], watch[watch.size()-1]); } watch.pop_back(); SASSERT(ineq_index < c.watch_size()); scoped_mpz coeff(m_mpz_mgr); coeff = c.ncoeff(ineq_index); if (ineq_index + 1 < c.watch_size()) { std::swap(c.args()[ineq_index], c.args()[c.watch_size()-1]); } --c.m_watch_sz; c.m_watch_sum -= coeff; if (coeff == c.max_watch()) { coeff = c.ncoeff(0); for (unsigned i = 1; coeff != c.max_watch() && i < c.watch_size(); ++i) { if (coeff < c.ncoeff(i)) coeff = c.ncoeff(i); } c.set_max_watch(coeff); } // current index of unwatched literal is c.watch_size(). } void theory_pb::add_watch(ineq& c, unsigned i) { SASSERT(c.is_ge()); literal lit = c.lit(i); scoped_mpz coeff(m_mpz_mgr); coeff = c.ncoeff(i); c.m_watch_sum += coeff; SASSERT(i >= c.watch_size()); if (i > c.watch_size()) { std::swap(c.args()[i], c.args()[c.watch_size()]); } ++c.m_watch_sz; if (coeff > c.max_watch()) { c.set_max_watch(coeff); } watch_literal(lit, &c); } void theory_pb::init_watch(bool_var v) { if (m_var_infos.size() <= static_cast(v)) { m_var_infos.resize(static_cast(v)+100); } } 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()]; if (ineqs == nullptr) { 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::unwatch_literal(literal lit, ineq* c) { ptr_vector* ineqs = m_var_infos[lit.var()].m_lit_watch[lit.sign()]; if (ineqs) { remove(*ineqs, c); } } void theory_pb::remove(ptr_vector& ineqs, ineq* c) { unsigned sz = ineqs.size(); for (unsigned j = 0; j < sz; ++j) { if (ineqs[j] == c) { std::swap(ineqs[j], ineqs[sz-1]); ineqs.pop_back(); return; } } } // ---------------------------- // cardinality constraints class theory_pb::card_justification : public justification { card& m_card; family_id m_fid; literal m_lit; public: card_justification(card& c, literal lit, family_id fid) : justification(true), m_card(c), m_fid(fid), m_lit(lit) {} card& get_card() { return m_card; } void get_antecedents(conflict_resolution& cr) override { cr.mark_literal(m_card.lit()); for (unsigned i = m_card.k(); i < m_card.size(); ++i) { cr.mark_literal(~m_card.lit(i)); } } theory_id get_from_theory() const override { return m_fid; } proof* mk_proof(smt::conflict_resolution& cr) override { ptr_buffer prs; ast_manager& m = cr.get_context().get_manager(); expr_ref fact(m); cr.get_context().literal2expr(m_lit, fact); bool all_valid = true; proof* pr = nullptr; pr = cr.get_proof(m_card.lit()); all_valid &= pr != nullptr; prs.push_back(pr); for (unsigned i = m_card.k(); i < m_card.size(); ++i) { pr = cr.get_proof(~m_card.lit(i)); all_valid &= pr != nullptr; prs.push_back(pr); } if (!all_valid) { return nullptr; } else { return m.mk_th_lemma(m_fid, fact, prs.size(), prs.c_ptr()); } } }; bool theory_pb::is_cardinality_constraint(app * atom) { if (pb.is_ge(atom) && pb.has_unit_coefficients(atom)) { return true; } if (pb.is_at_least_k(atom)) { return true; } return false; } bool theory_pb::internalize_card(app * atom, bool gate_ctx) { context& ctx = get_context(); if (ctx.b_internalized(atom)) { return true; } if (!is_cardinality_constraint(atom)) { return false; } unsigned num_args = atom->get_num_args(); bool_var abv = ctx.mk_bool_var(atom); ctx.set_var_theory(abv, get_id()); unsigned bound = pb.get_k(atom).get_unsigned(); literal lit(abv); if (bound == 0) { ctx.mk_th_axiom(get_id(), 1, &lit); return true; } if (bound > num_args) { lit.neg(); ctx.mk_th_axiom(get_id(), 1, &lit); return true; } // hack to differentiate constraints that come from input vs. lemmas. bool aux = pb.is_at_least_k(atom); card* c = alloc(card, lit, bound, aux); for (expr* arg : *atom) { c->add_arg(compile_arg(arg)); } if (bound == c->size() || bound == 1) { // } if (bound == c->size()) { card2conjunction(*c); dealloc(c); } else if (1 == c->size()) { card2disjunction(*c); dealloc(c); } else { SASSERT(0 < c->k() && c->k() < c->size()); // initialize compilation thresholds, TBD init_watch(abv); m_var_infos[abv].m_card = c; m_card_trail.push_back(abv); } return true; } // \brief define cardinality constraint as conjunction. // void theory_pb::card2conjunction(card const& c) { context& ctx = get_context(); literal lit = c.lit(); literal_vector& lits = get_literals(); for (unsigned i = 0; i < c.size(); ++i) { lits.push_back(~c.lit(i)); } lits.push_back(lit); ctx.mk_th_axiom(get_id(), lits.size(), lits.c_ptr()); for (unsigned i = 0; i < c.size(); ++i) { literal lits2[2] = { ~lit, c.lit(i) }; ctx.mk_th_axiom(get_id(), 2, lits2); } } void theory_pb::card2disjunction(card const& c) { context& ctx = get_context(); literal lit = c.lit(); literal_vector& lits = get_literals(); for (unsigned i = 0; i < c.size(); ++i) { lits.push_back(c.lit(i)); } lits.push_back(~lit); ctx.mk_th_axiom(get_id(), lits.size(), lits.c_ptr()); for (unsigned i = 0; i < c.size(); ++i) { literal lits2[2] = { lit, ~c.lit(i) }; ctx.mk_th_axiom(get_id(), 2, lits2); } } void theory_pb::watch_literal(literal lit, card* c) { init_watch(lit.var()); ptr_vector* cards = m_var_infos[lit.var()].m_lit_cwatch[lit.sign()]; if (cards == nullptr) { cards = alloc(ptr_vector); m_var_infos[lit.var()].m_lit_cwatch[lit.sign()] = cards; } cards->push_back(c); } void theory_pb::unwatch_literal(literal lit, card* c) { if (m_var_infos.size() <= static_cast(lit.var())) { return; } ptr_vector* cards = m_var_infos[lit.var()].m_lit_cwatch[lit.sign()]; if (cards) { remove(*cards, c); } } void theory_pb::remove(ptr_vector& cards, card* c) { for (unsigned j = 0; j < cards.size(); ++j) { if (cards[j] == c) { std::swap(cards[j], cards[cards.size()-1]); cards.pop_back(); break; } } } std::ostream& theory_pb::display(std::ostream& out, card const& c, bool values) const { context& ctx = get_context(); out << c.lit(); if (c.lit() != null_literal) { if (values) { out << "@(" << ctx.get_assignment(c.lit()); if (ctx.get_assignment(c.lit()) != l_undef) { out << ":" << ctx.get_assign_level(c.lit()); } out << ")"; } ctx.display_literal_verbose(out, c.lit()); out << "\n"; } else { out << " "; } for (unsigned i = 0; i < c.size(); ++i) { literal l = c.lit(i); out << l; if (values) { out << "@(" << ctx.get_assignment(l); if (ctx.get_assignment(l) != l_undef) { out << ":" << ctx.get_assign_level(l); } out << ") "; } } out << " >= " << c.k() << "\n"; if (c.all_propagations()) out << "propagations: " << c.all_propagations() << "\n"; return out; } void theory_pb::add_clause(card& c, literal_vector const& lits) { m_stats.m_num_conflicts++; context& ctx = get_context(); 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()); } ctx.mk_clause(lits.size(), lits.c_ptr(), js, CLS_TH_LEMMA, nullptr); } SASSERT(ctx.inconsistent()); } void theory_pb::add_assign(card& c, literal l) { context& ctx = get_context(); if (ctx.get_assignment(l) == l_true) { return; } c.inc_propagations(*this); m_stats.m_num_propagations++; TRACE("pb", tout << "#prop: " << c.num_propagations() << " - " << c.lit() << " => " << l << "\n"; display(tout, c, true) << "\n";); SASSERT(validate_unit_propagation(c)); ctx.assign(l, ctx.mk_justification(card_justification(c, l, get_id()))); } void theory_pb::clear_watch(card& c) { unsigned sz = std::min(c.k() + 1, c.size()); for (unsigned i = 0; i < sz; ++i) { unwatch_literal(c.lit(i), &c); } } // void theory_pb::collect_statistics(::statistics& st) const { st.update("pb resolves", m_stats.m_num_resolves); 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); } void theory_pb::reset_eh() { for (unsigned i = 0; i < m_var_infos.size(); ++i) { m_var_infos[i].reset(); } m_ineqs_trail.reset(); m_ineqs_lim.reset(); m_card_trail.reset(); m_card_lim.reset(); m_stats.reset(); } void theory_pb::new_eq_eh(theory_var v1, theory_var v2) { UNREACHABLE(); } final_check_status theory_pb::final_check_eh() { TRACE("pb", display(tout);); DEBUG_CODE(validate_final_check();); return FC_DONE; } void theory_pb::assign_eh(bool_var v, bool is_true) { ptr_vector* ineqs = nullptr; context& ctx = get_context(); literal nlit(v, is_true); init_watch(v); TRACE("pb", tout << "assign: " << ~nlit << "\n";); ineqs = m_var_infos[v].m_lit_watch[nlit.sign()]; if (ineqs != nullptr) { for (unsigned i = 0; i < ineqs->size(); ++i) { SASSERT((*ineqs)[i]->is_ge()); if (assign_watch_ge(v, is_true, *ineqs, i)) { // i was removed from watch list. --i; } } } ineq* c = m_var_infos[v].m_ineq; if (c != nullptr) { VERIFY(c->is_ge()); assign_ineq(*c, is_true); } ptr_vector* cards = m_var_infos[v].m_lit_cwatch[nlit.sign()]; if (cards != nullptr && !cards->empty() && !ctx.inconsistent()) { ptr_vector::iterator it = cards->begin(), it2 = it, end = cards->end(); for (; it != end; ++it) { if (ctx.get_assignment((*it)->lit()) != l_true) { continue; } switch ((*it)->assign(*this, nlit)) { case l_false: // conflict for (; it != end; ++it, ++it2) { *it2 = *it; } SASSERT(ctx.inconsistent()); cards->set_end(it2); return; case l_undef: // watch literal was swapped break; case l_true: // unit propagation, keep watching the literal if (it2 != it) { *it2 = *it; } ++it2; break; } } cards->set_end(it2); } card* crd = m_var_infos[v].m_card; if (crd != nullptr && !ctx.inconsistent()) { crd->init_watch(*this, is_true); } } literal_vector& theory_pb::get_all_literals(ineq& c, bool negate) { context& ctx = get_context(); literal_vector& lits = get_literals(); for (unsigned i = 0; i < c.size(); ++i) { literal l = c.lit(i); switch(ctx.get_assignment(l)) { case l_true: lits.push_back(negate?(~l):l); break; case l_false: lits.push_back(negate?l:(~l)); break; default: break; } } return lits; } literal_vector& theory_pb::get_helpful_literals(ineq& c, bool negate) { scoped_mpz sum(m_mpz_mgr); mpz const& k = c.mpz_k(); context& ctx = get_context(); literal_vector& lits = get_literals(); for (unsigned i = 0; sum < k && i < c.size(); ++i) { literal l = c.lit(i); if (ctx.get_assignment(l) == l_true) { sum += c.ncoeff(i); if (negate) l = ~l; lits.push_back(l); } } SASSERT(sum >= k); return lits; } literal_vector& theory_pb::get_unhelpful_literals(ineq& c, bool negate) { context& ctx = get_context(); literal_vector& lits = get_literals(); for (unsigned i = 0; i < c.size(); ++i) { literal l = c.lit(i); if (ctx.get_assignment(l) == l_false) { if (negate) l = ~l; lits.push_back(l); } } return lits; } class theory_pb::negate_ineq : public trail { ineq& c; public: negate_ineq(ineq& c): c(c) {} void undo(context& ctx) override { c.negate(); } }; /** \brief propagate assignment to inequality. This is a basic, non-optimized implementation based on the assumption that inequalities are mostly units and/or relatively few compared to number of argumets. */ void theory_pb::assign_ineq(ineq& c, bool is_true) { context& ctx = get_context(); 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)); SASSERT(c.is_ge()); unsigned sz = c.size(); if (c.lit().sign() == is_true) { c.negate(); ctx.push_trail(negate_ineq(c)); } scoped_mpz maxsum(m_mpz_mgr), mininc(m_mpz_mgr); for (unsigned i = 0; i < sz; ++i) { lbool asgn = ctx.get_assignment(c.lit(i)); if (asgn != l_false) { maxsum += c.ncoeff(i); } if (asgn == l_undef && (mininc.is_zero() || mininc > c.ncoeff(i))) { mininc = c.ncoeff(i); } } TRACE("pb", tout << "assign: " << c.lit() << "\n"; display(tout, c); ); if (maxsum < c.mpz_k()) { literal_vector& lits = get_unhelpful_literals(c, false); lits.push_back(~c.lit()); add_clause(c, lits); } else { init_watch_literal(c); SASSERT(c.m_watch_sum >= c.mpz_k()); DEBUG_CODE(validate_watch(c);); } // 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) { 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)); } } } } /** \brief propagate assignment to equality. */ void theory_pb::assign_eq(ineq& c, bool is_true) { SASSERT(c.is_eq()); UNREACHABLE(); } /** \brief v is assigned in inequality c. Update current bounds and watch list. Optimize for case where the c.lit() is True. This covers the case where inequalities are unit literals and formulas in negation normal form (inequalities are closed under negation). */ bool theory_pb::assign_watch_ge(bool_var v, bool is_true, ineq_watch& watch, unsigned watch_index) { bool removed = false; context& ctx = get_context(); ineq& c = *watch[watch_index]; unsigned w = c.find_lit(v, 0, c.watch_size()); SASSERT(ctx.get_assignment(c.lit()) == l_true); SASSERT(is_true == c.lit(w).sign()); // // watch_sum is decreased. // Adjust set of watched literals. // scoped_mpz k_coeff(m_mpz_mgr), k(m_mpz_mgr); k = c.mpz_k(); k_coeff = k; k_coeff += c.ncoeff(w); bool add_more = c.watch_sum() < k_coeff + c.max_watch(); for (unsigned i = c.watch_size(); add_more && i < c.size(); ++i) { if (ctx.get_assignment(c.lit(i)) != l_false) { add_watch(c, i); add_more = c.watch_sum() < k_coeff + c.max_watch(); } } if (c.watch_sum() < k_coeff) { // // L: 3*x1 + 2*x2 + x4 >= 3, but x1 <- 0, x2 <- 0 // create clause x1 or x2 or ~L // literal_vector& lits = get_unhelpful_literals(c, false); lits.push_back(~c.lit()); add_clause(c, lits); } else { del_watch(watch, watch_index, c, w); removed = true; SASSERT(c.watch_sum() >= k); if (c.watch_sum() < k + c.max_watch()) { // // opportunities for unit propagation for unassigned // literals whose coefficients satisfy // c.watch_sum() < k // // L: 3*x1 + 2*x2 + x4 >= 3, but x1 <- 0 // Create clauses x1 or ~L or x2 // x1 or ~L or x4 // literal_vector& lits = get_unhelpful_literals(c, true); lits.push_back(c.lit()); scoped_mpz deficit(m_mpz_mgr); deficit = c.watch_sum() - k; for (unsigned i = 0; i < c.size(); ++i) { if (ctx.get_assignment(c.lit(i)) == l_undef && deficit < c.ncoeff(i)) { DEBUG_CODE(validate_assign(c, lits, c.lit(i));); add_assign(c, lits, c.lit(i)); // break; } } } // // else: c.watch_sum() >= k + c.max_watch() // } TRACE("pb", tout << "assign: " << literal(v,!is_true) << "\n"; display(tout, c); ); return removed; } struct theory_pb::psort_expr { context& ctx; ast_manager& m; theory_pb& th; pb_util pb; typedef smt::literal pliteral; typedef smt::literal_vector pliteral_vector; psort_expr(context& c, theory_pb& th): ctx(c), m(c.get_manager()), th(th), pb(m) {} literal fresh(char const* ) { app_ref y(m); y = pb.mk_fresh_bool(); return literal(ctx.mk_bool_var(y)); } literal mk_max(unsigned n, literal const* lits) { expr_ref_vector es(m); expr_ref tmp(m); for (unsigned i = 0; i < n; ++i) { ctx.literal2expr(lits[i], tmp); es.push_back(tmp); } tmp = m.mk_or(es.size(), es.c_ptr()); bool_var v = ctx.b_internalized(tmp)?ctx.get_bool_var(tmp):ctx.mk_bool_var(tmp); return literal(v); } literal mk_min(unsigned n, literal const* lits) { expr_ref_vector es(m); expr_ref tmp(m); for (unsigned i = 0; i < n; ++i) { ctx.literal2expr(lits[i], tmp); es.push_back(tmp); } tmp = m.mk_and(es.size(), es.c_ptr()); bool_var v = ctx.b_internalized(tmp)?ctx.get_bool_var(tmp):ctx.mk_bool_var(tmp); return literal(v); } literal mk_not(literal a) { return ~a; } void mk_clause(unsigned n, literal const* ls) { literal_vector tmp(n, ls); ctx.mk_clause(n, tmp.c_ptr(), th.justify(tmp), CLS_AUX, nullptr); } literal mk_false() { return false_literal; } literal mk_true() { return true_literal; } std::ostream& pp(std::ostream& out, literal l) { return out << l; } }; // for testing literal theory_pb::assert_ge(context& ctx, unsigned k, unsigned n, literal const* xs) { theory_pb_params p; theory_pb th(ctx.get_manager(), p); psort_expr ps(ctx, th); psort_nw sort(ps); return sort.ge(false, k, n, xs); } void theory_pb::inc_propagations(ineq& c) { ++c.m_num_propagations; } void theory_pb::restart_eh() { } bool theory_pb::gc() { context& ctx = get_context(); unsigned z = 0, nz = 0; m_occs.reset(); for (unsigned i = 0; i < m_card_trail.size(); ++i) { bool_var v = m_card_trail[i]; if (v == null_bool_var) continue; card* c = m_var_infos[v].m_card; if (c) { c->reset_propagations(); literal lit = c->lit(); if (c->is_aux() && ctx.get_assign_level(lit) > ctx.get_search_level()) { double activity = ctx.get_activity(v); if (activity <= 0) { nz++; } else { z++; clear_watch(*c); m_var_infos[v].m_card = nullptr; dealloc(c); m_card_trail[i] = null_bool_var; ctx.remove_watch(v); // TBD: maybe v was used in a clause for propagation. m_occs.insert(v); } } } } clause_vector const& lemmas = ctx.get_lemmas(); for (unsigned i = 0; i < lemmas.size(); ++i) { clause* cl = lemmas[i]; if (!cl->deleted()) { for (literal lit : *cl) { if (m_occs.contains(lit.var())) { break; } } } } //std::cout << "zs: " << z << " nzs: " << nz << " lemmas: " << ctx.get_lemmas().size() << " trail: " << m_card_trail.size() << "\n"; return z*10 >= nz; m_occs.reset(); for (unsigned i = 0; i < lemmas.size(); ++i) { clause* cl = lemmas[i]; unsigned sz = cl->get_num_literals(); for (unsigned j = 0; j < sz; ++j) { unsigned idx = cl->get_literal(j).index(); m_occs.insert(idx); } } } void theory_pb::init_search_eh() { } void theory_pb::push_scope_eh() { m_ineqs_lim.push_back(m_ineqs_trail.size()); m_card_lim.push_back(m_card_trail.size()); } void theory_pb::pop_scope_eh(unsigned num_scopes) { // remove inequalities. unsigned new_lim = m_ineqs_lim.size()-num_scopes; unsigned sz = m_ineqs_lim[new_lim]; while (m_ineqs_trail.size() > sz) { bool_var v = m_ineqs_trail.back(); ineq* c = m_var_infos[v].m_ineq; clear_watch(*c); m_var_infos[v].m_ineq = nullptr; m_ineqs_trail.pop_back(); dealloc(c); } m_ineqs_lim.resize(new_lim); new_lim = m_card_lim.size() - num_scopes; sz = m_card_lim[new_lim]; while (m_card_trail.size() > sz) { bool_var v = m_card_trail.back(); m_card_trail.pop_back(); if (v != null_bool_var) { card* c = m_var_infos[v].m_card; clear_watch(*c); m_var_infos[v].m_card = nullptr; dealloc(c); } } m_card_lim.resize(new_lim); } void theory_pb::clear_watch(ineq& c) { for (unsigned i = 0; i < c.size(); ++i) { literal w = c.lit(i); unwatch_literal(w, &c); } c.m_watch_sum.reset(); c.m_watch_sz = 0; c.m_max_watch.reset(); c.m_nfixed = 0; c.m_max_sum.reset(); c.m_min_sum.reset(); } class theory_pb::unwatch_ge : public trail { theory_pb& pb; ineq& c; public: unwatch_ge(theory_pb& p, ineq& c): pb(p), c(c) {} void undo(context& ctx) override { for (unsigned i = 0; i < c.watch_size(); ++i) { pb.unwatch_literal(c.lit(i), &c); } c.m_watch_sz = 0; c.m_watch_sum.reset(); c.m_max_watch.reset(); } }; void theory_pb::init_watch_literal(ineq& c) { context& ctx = get_context(); scoped_mpz max_k(m_mpz_mgr); c.m_watch_sum.reset(); c.m_watch_sz = 0; c.m_max_watch.reset(); bool watch_more = true; for (unsigned i = 0; watch_more && i < c.size(); ++i) { if (ctx.get_assignment(c.lit(i)) != l_false) { add_watch(c, i); max_k = c.mpz_k(); max_k += c.max_watch(); watch_more = c.m_watch_sum < max_k; } } ctx.push_trail(unwatch_ge(*this, c)); } void theory_pb::init_watch_ineq(ineq& c) { c.m_min_sum.reset(); c.m_max_sum.reset(); c.m_nfixed = 0; c.m_watch_sum.reset(); c.m_max_watch.reset(); c.m_watch_sz = 0; for (unsigned i = 0; i < c.size(); ++i) { c.m_max_sum += c.ncoeff(i); } } class theory_pb::pb_justification : public theory_propagation_justification { ineq& m_ineq; public: pb_justification(ineq& c, family_id fid, region & r, unsigned num_lits, literal const * lits, literal consequent): theory_propagation_justification(fid, r, num_lits, lits, consequent), m_ineq(c) {} ineq& get_ineq() { return m_ineq; } }; void theory_pb::add_assign(ineq& c, literal_vector const& lits, literal l) { inc_propagations(c); m_stats.m_num_propagations++; context& ctx = get_context(); TRACE("pb", tout << "#prop:" << c.m_num_propagations << " - " << lits; tout << " => " << l << "\n"; display(tout, c, true);); SASSERT(validate_antecedents(lits)); ctx.assign(l, ctx.mk_justification( pb_justification( c, get_id(), ctx.get_region(), lits.size(), lits.c_ptr(), l))); } void theory_pb::add_clause(ineq& c, literal_vector const& lits) { inc_propagations(c); m_stats.m_num_conflicts++; context& ctx = get_context(); TRACE("pb", tout << "#prop:" << c.m_num_propagations << " - " << lits << "\n"; display(tout, c, true);); justification* js = nullptr; 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_TH_LEMMA, nullptr); } int theory_pb::get_coeff(bool_var v) const { return m_coeffs.get(v, 0); } int theory_pb::get_abs_coeff(bool_var v) const { int coeff = get_coeff(v); if (coeff < 0) coeff = -coeff; return coeff; } void theory_pb::reset_coeffs() { for (unsigned i = 0; i < m_active_vars.size(); ++i) { m_coeffs[m_active_vars[i]] = 0; } m_active_vars.reset(); } void theory_pb::process_antecedent(literal l, int offset) { context& ctx = get_context(); SASSERT(ctx.get_assignment(l) == l_false); bool_var v = l.var(); unsigned lvl = ctx.get_assign_level(v); if (lvl > ctx.get_base_level() && !ctx.is_marked(v) && lvl == m_conflict_lvl) { ctx.set_mark(v); ++m_num_marks; } inc_coeff(l, offset); } void theory_pb::process_card(card& c, int offset) { context& ctx = get_context(); SASSERT(c.k() <= c.size()); SASSERT(ctx.get_assignment(c.lit()) == l_true); for (unsigned i = c.k(); i < c.size(); ++i) { process_antecedent(c.lit(i), offset); } for (unsigned i = 0; i < c.k(); ++i) { inc_coeff(c.lit(i), offset); } if (ctx.get_assign_level(c.lit()) > ctx.get_base_level()) { m_antecedents.push_back(c.lit()); } } bool theory_pb::validate_lemma() { int value = -m_bound; context& ctx = get_context(); normalize_active_coeffs(); for (unsigned i = 0; i < m_active_vars.size(); ++i) { bool_var v = m_active_vars[i]; int coeff = get_coeff(v); SASSERT(coeff != 0); if (coeff < 0 && ctx.get_assignment(v) != l_true) { value -= coeff; } else if (coeff > 0 && ctx.get_assignment(v) != l_false) { value += coeff; } } if (value >= 0) { display_resolved_lemma(verbose_stream() << "not validated\n"); } // std::cout << "bound: " << m_bound << " value " << value << " coeffs: " << m_active_vars.size() << " lemma is " << (value >= 0 ? "sat" : "unsat") << "\n"; return value < 0; } bool theory_pb::validate_implies(app_ref& A, app_ref& B) { static bool validating = true; // false; if (validating) return true; validating = true; ast_manager& m = get_manager(); smt_params fp; kernel k(m, fp); expr_ref notB(m.mk_not(B), m); k.assert_expr(A); k.assert_expr(notB); lbool is_sat = k.check(); validating = false; // std::cout << is_sat << "\n"; if (is_sat == l_true) { std::cout << A << "\n"; std::cout << B << "\n"; } SASSERT(is_sat != l_true); return true; } app_ref theory_pb::justification2expr(b_justification& js, literal conseq) { ast_manager& m = get_manager(); app_ref result(m.mk_true(), m); expr_ref_vector args(m); vector coeffs; switch(js.get_kind()) { case b_justification::CLAUSE: { clause& cls = *js.get_clause(); justification* cjs = cls.get_justification(); if (cjs && !is_proof_justification(*cjs)) { break; } for (unsigned i = 0; i < cls.get_num_literals(); ++i) { literal lit = cls.get_literal(i); args.push_back(literal2expr(lit)); } result = m.mk_or(args.size(), args.c_ptr()); break; } case b_justification::BIN_CLAUSE: result = m.mk_or(literal2expr(conseq), literal2expr(~js.get_literal())); break; case b_justification::AXIOM: break; case b_justification::JUSTIFICATION: { justification* j = js.get_justification(); card_justification* pbj = nullptr; if (j->get_from_theory() == get_id()) { pbj = dynamic_cast(j); } if (pbj != nullptr) { card& c2 = pbj->get_card(); result = card2expr(c2); } break; } default: break; } return result; } int theory_pb::arg_max(int& max_coeff) { max_coeff = 0; int arg_max = -1; while (!m_active_coeffs.empty()) { max_coeff = m_active_coeffs.back(); if (m_coeff2args[max_coeff].empty()) { m_active_coeffs.pop_back(); } else { arg_max = m_coeff2args[max_coeff].back(); m_coeff2args[max_coeff].pop_back(); break; } } return arg_max; } /** \brief retrieve asserting literal. Prefer p, otherwise find a literal with maximal level. Note that an asserting literal should be false with respect to the resolvent inequality. */ literal theory_pb::get_asserting_literal(literal p) { context& ctx = get_context(); unsigned lvl = 0; TRACE("pb", tout << p << " " << ctx.get_assignment(p) << "\n";); if (ctx.get_assignment(p) == l_false && get_abs_coeff(p.var()) != 0 && p == literal(p.var(), get_coeff(p.var()) < 0)) { return p; } for (bool_var v : m_active_vars) { literal lit(v, get_coeff(v) < 0); if (ctx.get_assignment(lit) == l_false && ctx.get_assign_level(lit) > lvl) { p = lit; } } return p; } void theory_pb::reset_arg_max() { for (unsigned i = 0; i < m_active_vars.size(); ++i) { int coeff = get_abs_coeff(m_active_vars[i]); if (static_cast(m_coeff2args.size()) > coeff) { m_coeff2args[coeff].reset(); } } } bool theory_pb::init_arg_max() { if (m_coeff2args.size() < (1 << 10)) { m_coeff2args.resize(1 << 10); } m_active_coeffs.reset(); if (m_active_vars.empty()) { return false; } for (unsigned i = 0; i < m_active_vars.size(); ++i) { bool_var v = m_active_vars[i]; int coeff = get_abs_coeff(v); if (coeff >= static_cast(m_coeff2args.size())) { reset_arg_max(); return false; } if (m_coeff2args[coeff].empty()) { m_active_coeffs.push_back(coeff); } m_coeff2args[coeff].push_back(v); } std::sort(m_active_coeffs.begin(), m_active_coeffs.end()); return 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(); for (; i < sz; ++i) { bool_var v = m_active_vars[i]; if (!m_active_var_set.contains(v) && get_coeff(v) != 0) { m_active_var_set.insert(v); if (j != i) { m_active_vars[j] = m_active_vars[i]; } ++j; } } sz = j; m_active_vars.shrink(sz); } void theory_pb::inc_coeff(literal l, int offset) { SASSERT(offset > 0); bool_var v = l.var(); SASSERT(v != null_bool_var); if (static_cast(m_coeffs.size()) <= v) { m_coeffs.resize(v + 1, 0); } int coeff0 = m_coeffs[v]; if (coeff0 == 0) { m_active_vars.push_back(v); } int inc = l.sign() ? -offset : offset; int coeff1 = inc + coeff0; m_coeffs[v] = coeff1; if (coeff0 > 0 && inc < 0) { m_bound -= coeff0 - std::max(0, coeff1); } else if (coeff0 < 0 && inc > 0) { m_bound -= std::min(0, coeff1) - coeff0; } } /** \brief attempt a cut and simplification of constraints. */ void theory_pb::cut() { unsigned g = 0; for (unsigned i = 0; g != 1 && i < m_active_vars.size(); ++i) { bool_var v = m_active_vars[i]; int coeff = get_abs_coeff(v); if (coeff == 0) { continue; } if (m_bound < coeff) { if (get_coeff(v) > 0) { m_coeffs[v] = m_bound; } else { m_coeffs[v] = -m_bound; } coeff = m_bound; } SASSERT(0 < coeff && coeff <= m_bound); if (g == 0) { g = static_cast(coeff); } else { g = u_gcd(g, static_cast(coeff)); } } if (g >= 2) { normalize_active_coeffs(); for (auto v : m_active_vars) { m_coeffs[v] /= static_cast(g); } m_bound = (m_bound + g - 1) / g; TRACE("pb", display_resolved_lemma(tout << "cut\n");); } } bool theory_pb::can_propagate() { return false; } void theory_pb::propagate() { } bool theory_pb::resolve_conflict(card& c, literal_vector const& confl) { TRACE("pb", display(tout, c, true); ); bool_var v; context& ctx = get_context(); ast_manager& m = get_manager(); m_conflict_lvl = 0; for (literal lit : confl) { SASSERT(ctx.get_assignment(lit) == l_false); m_conflict_lvl = std::max(m_conflict_lvl, ctx.get_assign_level(lit)); } if (m_conflict_lvl < ctx.get_assign_level(c.lit()) || m_conflict_lvl == ctx.get_base_level()) { return false; } reset_coeffs(); m_num_marks = 0; m_bound = c.k(); m_antecedents.reset(); m_resolved.reset(); literal_vector ante; process_card(c, 1); app_ref A(m), B(m), C(m); DEBUG_CODE(A = c.to_expr(*this);); // point into stack of assigned literals literal_vector const& lits = ctx.assigned_literals(); SASSERT(!lits.empty()); unsigned idx = lits.size()-1; b_justification js; literal conseq = ~confl[2]; int bound = 1; while (m_num_marks > 0) { v = conseq.var(); int offset = get_abs_coeff(v); if (offset == 0) { goto process_next_resolvent; } SASSERT(validate_lemma()); if (offset > 1000) { while (m_num_marks > 0 && idx > 0) { v = lits[idx].var(); if (ctx.is_marked(v)) { ctx.unset_mark(v); } --idx; } return false; } SASSERT(offset > 0); js = ctx.get_justification(v); TRACE("pb", display_resolved_lemma(tout << conseq << "\n"); ctx.display(tout, js);); m_resolved.push_back(conseq); // // Resolve selected conseq with antecedents. // bound = 1; switch(js.get_kind()) { case b_justification::CLAUSE: { inc_coeff(conseq, offset); clause& cls = *js.get_clause(); justification* cjs = cls.get_justification(); if (cjs && !is_proof_justification(*cjs)) { TRACE("pb", tout << "skipping justification for clause over: " << conseq << " " << typeid(*cjs).name() << "\n";); break; } unsigned num_lits = cls.get_num_literals(); if (cls.get_literal(0) == conseq) { process_antecedent(cls.get_literal(1), offset); } else { SASSERT(cls.get_literal(1) == conseq); process_antecedent(cls.get_literal(0), offset); } for (unsigned i = 2; i < num_lits; ++i) { process_antecedent(cls.get_literal(i), offset); } TRACE("pb", tout << literal_vector(cls.get_num_literals(), cls.begin()) << "\n";); break; } case b_justification::BIN_CLAUSE: inc_coeff(conseq, offset); process_antecedent(~js.get_literal(), offset); break; case b_justification::AXIOM: bound = 0; break; case b_justification::JUSTIFICATION: { justification* j = js.get_justification(); card_justification* pbj = nullptr; if (j->get_from_theory() == get_id()) { pbj = dynamic_cast(j); } if (pbj == nullptr) { TRACE("pb", tout << "skip justification for " << conseq << "\n";); 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(); process_card(c2, offset); bound = c2.k(); } break; } default: UNREACHABLE(); } m_bound += offset * bound; DEBUG_CODE( B = justification2expr(js, conseq); C = active2expr(); B = m.mk_and(A, B); validate_implies(B, C); A = C;); cut(); process_next_resolvent: // find the next marked variable in the assignment stack // while (true) { conseq = lits[idx]; v = conseq.var(); if (ctx.is_marked(v)) break; SASSERT(idx > 0); --idx; } SASSERT(ctx.get_assign_level(v) == m_conflict_lvl); ctx.unset_mark(v); --idx; --m_num_marks; } SASSERT(validate_lemma()); TRACE("pb", display_resolved_lemma(tout << "done\n");); normalize_active_coeffs(); if (m_bound > 0 && m_active_vars.empty()) { return false; } int slack = -m_bound; for (bool_var v : m_active_vars) { slack += get_abs_coeff(v); } 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; ) { 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); if (coeff < 0 && !lit.sign()) { slack += coeff; m_antecedents.push_back(lit); } else if (coeff > 0 && lit.sign()) { slack -= coeff; m_antecedents.push_back(lit); } } } SASSERT(slack < 0); ++m_stats.m_num_resolves; SASSERT(validate_antecedents(m_antecedents)); TRACE("pb", tout << "assign " << m_antecedents << " ==> " << alit << "\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 (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); ); return true; } bool theory_pb::is_proof_justification(justification const& j) const { return typeid(smt::justification_proof_wrapper) == typeid(j); } justification* theory_pb::justify(literal l1, literal l2) { literal lits[2] = { l1, l2 }; justification* js = nullptr; if (proofs_enabled()) { js = get_context().mk_justification(theory_axiom_justification(get_id(), get_context().get_region(), 2, lits)); } return js; } justification* theory_pb::justify(literal_vector const& lits) { justification* js = nullptr; if (proofs_enabled()) { js = get_context().mk_justification(theory_axiom_justification(get_id(), get_context().get_region(), lits.size(), lits.c_ptr())); } return js; } // debug methods void theory_pb::validate_watch(ineq const& c) const { scoped_mpz sum(m_mpz_mgr), max(m_mpz_mgr); for (unsigned i = 0; i < c.watch_size(); ++i) { sum += c.ncoeff(i); if (max < c.ncoeff(i)) { max = c.ncoeff(i); } } SASSERT(c.watch_sum() == sum); SASSERT(sum >= c.mpz_k()); SASSERT(max == c.max_watch()); } void theory_pb::validate_assign(ineq const& c, literal_vector const& lits, literal l) const { uint_set nlits; for (literal lit : lits) { SASSERT(get_context().get_assignment(lit) == l_true); nlits.insert((~lit).index()); } SASSERT(get_context().get_assignment(l) == l_undef); SASSERT(get_context().get_assignment(c.lit()) == l_true); nlits.insert(l.index()); numeral sum = numeral::zero(); for (unsigned i = 0; i < c.size(); ++i) { literal lit = c.lit(i); if (!nlits.contains(lit.index())) { sum += c.coeff(i); } } CTRACE("pb", (sum >= c.k()), display(tout << "invalid assign" , c, true); for (literal lit : lits) tout << lit << " "; tout << " => " << l << "\n";); SASSERT(sum < c.k()); } void theory_pb::validate_final_check() { 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(); for (unsigned i = 0; i < c.size(); ++i) { switch(ctx.get_assignment(c.lit(i))) { case l_true: sum += c.coeff(i); case l_undef: maxsum += c.coeff(i); break; case l_false: break; } } TRACE("pb", display(tout << "validate: ", c, true); tout << "sum: " << sum << " " << maxsum << " "; tout << ctx.get_assignment(c.lit()) << "\n"; ); SASSERT(sum <= maxsum); SASSERT(!c.is_ge() || (sum >= c.k()) == (ctx.get_assignment(c.lit()) == l_true)); SASSERT(!c.is_ge() || (maxsum < c.k()) == (ctx.get_assignment(c.lit()) == l_false)); SASSERT(!c.is_eq() || (sum == c.k()) == (ctx.get_assignment(c.lit()) == l_true)); } bool theory_pb::validate_antecedents(literal_vector const& lits) { context& ctx = get_context(); for (literal lit : lits) { if (ctx.get_assignment(lit) != l_true) { return false; } } return true; } bool theory_pb::validate_unit_propagation(card const& c) { context& ctx = get_context(); for (unsigned i = c.k(); i < c.size(); ++i) { if (ctx.get_assignment(c.lit(i)) != l_false) { return false; } } return true; } app_ref theory_pb::literal2expr(literal lit) { ast_manager& m = get_manager(); app_ref arg(m.mk_const(symbol(lit.var()), m.mk_bool_sort()), m); return app_ref(lit.sign() ? m.mk_not(arg) : arg, m); } app_ref theory_pb::active2expr() { ast_manager& m = get_manager(); expr_ref_vector args(m); vector coeffs; normalize_active_coeffs(); for (unsigned i = 0; i < m_active_vars.size(); ++i) { bool_var v = m_active_vars[i]; literal lit(v, get_coeff(v) < 0); args.push_back(literal2expr(lit)); coeffs.push_back(rational(get_abs_coeff(v))); } rational k(m_bound); return app_ref(pb.mk_ge(args.size(), coeffs.c_ptr(), args.c_ptr(), k), m); } // display methods void theory_pb::display_resolved_lemma(std::ostream& out) const { context& ctx = get_context(); out << "num marks: " << m_num_marks << "\n"; out << "conflict level: " << m_conflict_lvl << "\n"; for (literal r : m_resolved) { out << ctx.get_assign_level(r) << ": " << r << " "; ctx.display(out, ctx.get_justification(r.var())); } if (!m_antecedents.empty()) { out << m_antecedents << " ==> "; } uint_set seen; bool first = true; for (bool_var v: m_active_vars) { if (seen.contains(v)) { continue; } seen.insert(v); int coeff = get_coeff(v); if (coeff == 0) { continue; } if (!first) { out << " + "; } literal lit(v, coeff < 0); if (coeff > 1) { out << coeff << " * "; } else if (coeff < -1) { out << (-coeff) << " * "; } out << lit << "(" << ctx.get_assignment(lit) << "@" << ctx.get_assign_level(lit) << ")"; first = false; } out << " >= " << m_bound << "\n"; } std::ostream& theory_pb::display(std::ostream& out, arg_t const& c, bool values) const { return c.display(get_context(), out, values); } std::ostream& theory_pb::display(std::ostream& out, ineq const& c, bool values) const { ast_manager& m = get_manager(); context& ctx = get_context(); out << c.lit(); if (c.lit() != null_literal) { if (values) { out << "@(" << ctx.get_assignment(c.lit()); if (ctx.get_assignment(c.lit()) != l_undef) { out << ":" << ctx.get_assign_level(c.lit()); } out << ")"; } expr_ref tmp(m); ctx.literal2expr(c.lit(), tmp); out << " " << tmp << "\n"; } else { out << " "; } for (unsigned i = 0; i < c.size(); ++i) { literal l(c.lit(i)); if (!c.coeff(i).is_one()) { out << c.coeff(i) << "*"; } out << l; if (values) { out << "@(" << ctx.get_assignment(l); if (ctx.get_assignment(l) != l_undef) { out << ":" << ctx.get_assign_level(l); } out << ")"; } if (i + 1 == c.watch_size()) { out << " .w "; } if (i + 1 < c.size()) { out << " + "; } } out << (c.is_ge()?" >= ":" = ") << c.k() << "\n"; if (c.m_num_propagations) out << "propagations: " << c.m_num_propagations << " "; if (c.m_max_watch.is_pos()) out << "max_watch: " << c.max_watch() << " "; if (c.watch_size()) out << "watch size: " << c.watch_size() << " "; if (c.m_watch_sum.is_pos()) out << "watch-sum: " << c.watch_sum() << " "; if (!c.m_max_sum.is_zero()) out << "sum: [" << c.min_sum() << ":" << c.max_sum() << "] "; if (c.m_num_propagations || c.m_max_watch.is_pos() || c.watch_size() || c.m_watch_sum.is_pos() || !c.m_max_sum.is_zero()) out << "\n"; return out; } class theory_pb::pb_model_value_proc : public model_value_proc { app* m_app; svector m_dependencies; public: pb_model_value_proc(app* a): m_app(a) {} void add(enode* n) { m_dependencies.push_back(model_value_dependency(n)); } void get_dependencies(buffer & result) override { result.append(m_dependencies.size(), m_dependencies.c_ptr()); } app * mk_value(model_generator & mg, expr_ref_vector const& values) override { ast_manager& m = mg.get_manager(); SASSERT(values.size() == m_dependencies.size()); SASSERT(values.size() == m_app->get_num_args()); pb_util u(m); rational sum(0); for (unsigned i = 0; i < m_app->get_num_args(); ++i) { if (!m.is_true(values[i]) && !m.is_false(values[i])) { return m_app; } if (m.is_true(values[i])) { sum += u.get_coeff(m_app, i); } } rational k = u.get_k(m_app); switch(m_app->get_decl_kind()) { case OP_AT_MOST_K: return (sum <= k)?m.mk_true():m.mk_false(); case OP_AT_LEAST_K: return (sum >= k)?m.mk_true():m.mk_false(); case OP_PB_LE: return (sum <= k)?m.mk_true():m.mk_false(); case OP_PB_GE: return (sum >= k)?m.mk_true():m.mk_false(); default: UNREACHABLE(); return nullptr; } return nullptr; } }; class pb_factory : public value_factory { public: pb_factory(ast_manager& m, family_id fid): value_factory(m, fid) {} expr * get_some_value(sort * s) override { return m_manager.mk_true(); } bool get_some_values(sort * s, expr_ref & v1, expr_ref & v2) override { v1 = m_manager.mk_true(); v2 = m_manager.mk_false(); return true; } expr * get_fresh_value(sort * s) override { return nullptr; } void register_value(expr * n) override { } }; void theory_pb::init_model(model_generator & m) { m.register_factory(alloc(pb_factory, get_manager(), get_id())); } model_value_proc * theory_pb::mk_value(enode * n, model_generator & mg) { context& ctx = get_context(); app* a = n->get_owner(); pb_model_value_proc* p = alloc(pb_model_value_proc, a); for (unsigned i = 0; i < a->get_num_args(); ++i) { p->add(ctx.get_enode(a->get_arg(i))); } return p; } void theory_pb::display_watch(std::ostream& out, bool_var v, bool sign) const { ineq_watch const* w = m_var_infos[v].m_lit_watch[sign]; if (!w) return; ineq_watch const& wl = *w; out << "watch: " << literal(v, sign) << " |-> "; for (unsigned i = 0; i < wl.size(); ++i) { out << wl[i]->lit() << " "; } out << "\n"; } void theory_pb::display(std::ostream& out) const { for (unsigned vi = 0; vi < m_var_infos.size(); ++vi) { display_watch(out, vi, false); display_watch(out, vi, true); } for (unsigned vi = 0; vi < m_var_infos.size(); ++vi) { ineq* c = m_var_infos[vi].m_ineq; if (c) { display(out, *c, true); } } for (unsigned vi = 0; vi < m_var_infos.size(); ++vi) { card* c = m_var_infos[vi].m_card; if (c) { display(out, *c, true); } } } } z3-z3-4.8.7/src/smt/theory_pb.h000066400000000000000000000373171356505360400162000ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: theory_pb.h Abstract: Cardinality theory plugin. Author: Nikolaj Bjorner (nbjorner) 2013-11-05 Notes: This custom theory handles cardinality constraints It performs unit propagation and switches to creating sorting circuits if it keeps having to propagate (create new clauses). --*/ #include "smt/smt_theory.h" #include "ast/pb_decl_plugin.h" #include "smt/smt_clause.h" #include "smt/smt_b_justification.h" #include "smt/params/theory_pb_params.h" #include "math/simplex/simplex.h" namespace smt { class theory_pb : public theory { struct psort_expr; class pb_justification; class pb_model_value_proc; class unwatch_ge; class rewatch_vars; class negate_ineq; class remove_var; class undo_bound; class card_justification; typedef rational numeral; typedef unsynch_mpq_inf_manager eps_manager; typedef _scoped_numeral scoped_eps_numeral; struct arg_t : public vector > { numeral m_k; // invariants: m_k > 0, coeffs[i] > 0 unsigned get_hash() const; bool operator==(arg_t const& other) const; numeral const& k() const { return m_k; } struct hash { unsigned operator()(arg_t const& i) const { return i.get_hash(); } }; struct eq { bool operator()(arg_t const& a, arg_t const& b) const { return a == b; } }; struct child_hash { unsigned operator()(arg_t const& args, unsigned idx) const { return args[idx].first.hash() ^ args[idx].second.hash(); } }; struct kind_hash { unsigned operator()(arg_t const& args) const { return args.size(); } }; void remove_negations(); void negate(); lbool normalize(bool is_eq); void prune(bool is_eq); literal lit(unsigned i) const { return (*this)[i].first; } numeral const & coeff(unsigned i) const { return (*this)[i].second; } std::ostream& display(context& ctx, std::ostream& out, bool values = false) const; app_ref to_expr(bool is_eq, context& ctx, ast_manager& m); bool well_formed() const; }; struct stats { unsigned m_num_conflicts; unsigned m_num_propagations; unsigned m_num_predicates; unsigned m_num_resolves; void reset() { memset(this, 0, sizeof(*this)); } stats() { reset(); } }; struct ineq { unsynch_mpz_manager& m_mpz; // mpz manager. literal m_lit; // literal representing predicate bool m_is_eq; // is this an = or >=. arg_t m_args[2]; // encode args[0]*coeffs[0]+...+args[n-1]*coeffs[n-1] >= k(); // Watch the first few positions until the sum satisfies: // sum coeffs[i] >= m_lower + max_watch scoped_mpz m_max_watch; // maximal coefficient. unsigned m_watch_sz; // number of literals being watched. scoped_mpz m_watch_sum; // maximal sum of watch literals. // Watch infrastructure for = and unassigned >=: unsigned m_nfixed; // number of variables that are fixed. scoped_mpz m_max_sum; // maximal possible sum. scoped_mpz m_min_sum; // minimal possible sum. unsigned m_num_propagations; ineq(unsynch_mpz_manager& m, literal l, bool is_eq) : m_mpz(m), m_lit(l), m_is_eq(is_eq), m_max_watch(m), m_watch_sum(m), m_max_sum(m), m_min_sum(m) { reset(); } arg_t const& args() const { return m_args[m_lit.sign()]; } arg_t& args() { return m_args[m_lit.sign()]; } literal lit() const { return m_lit; } numeral const & k() const { return args().m_k; } mpz const & mpz_k() const { return k().to_mpq().numerator(); } literal lit(unsigned i) const { return args()[i].first; } numeral const & coeff(unsigned i) const { return args()[i].second; } class mpz const& ncoeff(unsigned i) const { return coeff(i).to_mpq().numerator(); } unsigned size() const { return args().size(); } scoped_mpz const& watch_sum() const { return m_watch_sum; } scoped_mpz const& max_watch() const { return m_max_watch; } void set_max_watch(mpz const& n) { m_max_watch = n; } unsigned watch_size() const { return m_watch_sz; } // variable watch infrastructure scoped_mpz const& min_sum() const { return m_min_sum; } scoped_mpz const& max_sum() const { return m_max_sum; } unsigned nfixed() const { return m_nfixed; } bool vwatch_initialized() const { return !m_mpz.is_zero(max_sum()); } void vwatch_reset() { m_min_sum.reset(); m_max_sum.reset(); m_nfixed = 0; } unsigned find_lit(bool_var v, unsigned begin, unsigned end) { while (lit(begin).var() != v) { ++begin; SASSERT(begin < end); } return begin; } void reset(); void negate(); lbool normalize(); void unique(); void prune(); void post_prune(); app_ref to_expr(context& ctx, ast_manager& m); bool is_eq() const { return m_is_eq; } bool is_ge() const { return !m_is_eq; } }; // cardinality constraint args >= bound // unit propagation on cardinality constraints is valid if the literals // from k() up to size are false. // In this case the literals 0..k()-1 need to be true. // The literals in position 0..k() are watched. // whenever they are assigned to false, then find a literal among // k() + 1.. sz() to swap with. // If none are available, then perform unit propagation. // class card { literal m_lit; // literal representing predicate literal_vector m_args; unsigned m_bound; unsigned m_num_propagations; unsigned m_all_propagations; bool m_aux; public: card(literal l, unsigned bound, bool is_aux): m_lit(l), m_bound(bound), m_num_propagations(0), m_all_propagations(0), m_aux(is_aux) { SASSERT(bound > 0); } literal lit() const { return m_lit; } literal lit(unsigned i) const { return m_args[i]; } unsigned k() const { return m_bound; } unsigned size() const { return m_args.size(); } unsigned all_propagations() const { return m_all_propagations; } unsigned num_propagations() const { return m_num_propagations; } void add_arg(literal l); void init_watch(theory_pb& th, bool is_true); lbool assign(theory_pb& th, literal lit); void negate(); app_ref to_expr(theory_pb& th); void inc_propagations(theory_pb& th); void reset_propagations() { m_all_propagations += m_num_propagations; m_num_propagations = 0; } bool is_aux() const { return m_aux; } private: bool validate_conflict(theory_pb& th); bool validate_assign(theory_pb& th, literal_vector const& lits, literal l); void set_conflict(theory_pb& th, literal l); }; typedef ptr_vector card_watch; typedef ptr_vector ineq_watch; typedef map arg_map; struct var_info { ineq_watch* m_lit_watch[2]; ineq* m_ineq; card_watch* m_lit_cwatch[2]; card* m_card; var_info(): m_ineq(nullptr), m_card(nullptr) { m_lit_watch[0] = nullptr; m_lit_watch[1] = nullptr; m_lit_cwatch[0] = nullptr; m_lit_cwatch[1] = nullptr; } void reset() { dealloc(m_lit_watch[0]); dealloc(m_lit_watch[1]); dealloc(m_ineq); dealloc(m_lit_cwatch[0]); dealloc(m_lit_cwatch[1]); dealloc(m_card); } }; theory_pb_params m_params; svector m_var_infos; mutable unsynch_mpz_manager m_mpz_mgr; // Simplex: manager mpz numerals unsigned_vector m_ineqs_trail; unsigned_vector m_ineqs_lim; literal_vector m_literals; // temporary vector pb_util pb; stats m_stats; unsigned m_conflict_frequency; bool m_learn_complements; unsigned m_restart_lim; unsigned m_restart_inc; uint_set m_occs; // internalize_atom: literal compile_arg(expr* arg); void init_watch(bool_var v); // general purpose pb constraints 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_ineq(ineq& c); void clear_watch(ineq& c); void watch_literal(literal lit, ineq* c); void unwatch_literal(literal w, 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_ineq(ineq& c, bool is_true); void assign_eq(ineq& c, bool is_true); // cardinality constraints // these are cheaper to handle than general purpose PB constraints // and in the common case PB constraints with small coefficients can // be handled using cardinality constraints. unsigned_vector m_card_trail; unsigned_vector m_card_lim; bool is_cardinality_constraint(app * atom); bool internalize_card(app * atom, bool gate_ctx); void card2conjunction(card const& c); void card2disjunction(card const& c); void watch_literal(literal lit, card* c); void unwatch_literal(literal w, card* c); void add_clause(card& c, literal_vector const& lits); void add_assign(card& c, literal l); void remove(ptr_vector& cards, card* c); void clear_watch(card& c); bool gc(); std::ostream& display(std::ostream& out, card const& c, bool values = false) const; // simplex: bool check_feasible(); std::ostream& display(std::ostream& out, ineq const& c, bool values = false) const; std::ostream& display(std::ostream& out, arg_t const& c, bool values = false) const; void display(std::ostream& out) const override; void display_watch(std::ostream& out, bool_var v, bool sign) const; void display_resolved_lemma(std::ostream& out) const; void add_clause(ineq& c, literal_vector const& lits); void add_assign(ineq& c, literal_vector const& lits, literal l); literal_vector& get_lits(); literal_vector& get_all_literals(ineq& c, bool negate); literal_vector& get_helpful_literals(ineq& c, bool negate); literal_vector& get_unhelpful_literals(ineq& c, bool negate); void inc_propagations(ineq& c); // // Conflict resolution, cutting plane derivation. // unsigned m_num_marks; literal_vector m_resolved; unsigned m_conflict_lvl; // Conflict PB constraints svector m_coeffs; svector m_active_vars; int m_bound; literal_vector m_antecedents; tracked_uint_set m_active_var_set; expr_ref_vector m_antecedent_exprs; svector m_antecedent_signs; expr_ref_vector m_cardinality_exprs; svector m_cardinality_signs; void normalize_active_coeffs(); void inc_coeff(literal l, int offset); int get_coeff(bool_var v) const; int get_abs_coeff(bool_var v) const; int arg_max(int& coeff); literal_vector& get_literals() { m_literals.reset(); return m_literals; } vector > m_coeff2args; unsigned_vector m_active_coeffs; bool init_arg_max(); void reset_arg_max(); void reset_coeffs(); literal get_asserting_literal(literal conseq); bool resolve_conflict(card& c, literal_vector const& conflict_clause); void process_antecedent(literal l, int offset); void process_card(card& c, int offset); void cut(); bool is_proof_justification(justification const& j) const; void hoist_maximal_values(); 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); bool validate_antecedents(literal_vector const& lits); bool validate_implies(app_ref& A, app_ref& B); app_ref active2expr(); app_ref literal2expr(literal lit); app_ref card2expr(card& c) { return c.to_expr(*this); } app_ref justification2expr(b_justification& js, literal conseq); bool proofs_enabled() const { return get_manager().proofs_enabled(); } justification* justify(literal l1, literal l2); justification* justify(literal_vector const& lits); public: theory_pb(ast_manager& m, theory_pb_params& p); ~theory_pb() override; theory * mk_fresh(context * new_ctx) override; bool internalize_atom(app * atom, bool gate_ctx) override; bool internalize_term(app * term) override { UNREACHABLE(); return false; } void new_eq_eh(theory_var v1, theory_var v2) override; void new_diseq_eh(theory_var v1, theory_var v2) override { } bool use_diseqs() const override { return false; } bool build_models() const override { return false; } final_check_status final_check_eh() override; void reset_eh() override; void assign_eh(bool_var v, bool is_true) override; void init_search_eh() override; void push_scope_eh() override; void pop_scope_eh(unsigned num_scopes) override; void restart_eh() override; void collect_statistics(::statistics & st) const override; model_value_proc * mk_value(enode * n, model_generator & mg) override; void init_model(model_generator & m) override; bool include_func_interp(func_decl* f) override { return false; } bool can_propagate() override; void propagate() override; static literal assert_ge(context& ctx, unsigned k, unsigned n, literal const* xs); }; }; z3-z3-4.8.7/src/smt/theory_recfun.cpp000066400000000000000000000376301356505360400174120ustar00rootroot00000000000000/*++ Copyright (c) 2018 Microsoft Corporation, Simon Cuares Module Name: theory_recfun.cpp Abstract: Theory responsible for unrolling recursive functions Author: Simon Cuares December 2017 Revision History: --*/ #include "util/stats.h" #include "ast/ast_util.h" #include "ast/for_each_expr.h" #include "smt/theory_recfun.h" #include "smt/params/smt_params_helper.hpp" #define TRACEFN(x) TRACE("recfun", tout << x << '\n';) namespace smt { theory_recfun::theory_recfun(ast_manager & m) : theory(m.mk_family_id("recfun")), m(m), m_plugin(*reinterpret_cast(m.get_plugin(get_family_id()))), m_util(m_plugin.u()), m_preds(m), m_max_depth(0), m_q_case_expand(), m_q_body_expand() { } theory_recfun::~theory_recfun() { reset_queues(); } char const * theory_recfun::get_name() const { return "recfun"; } theory* theory_recfun::mk_fresh(context* new_ctx) { return alloc(theory_recfun, new_ctx->get_manager()); } void theory_recfun::init(context* ctx) { theory::init(ctx); smt_params_helper p(ctx->get_params()); m_max_depth = p.recfun_depth(); if (m_max_depth < 2) m_max_depth = 2; } void theory_recfun::init_search_eh() { } bool theory_recfun::internalize_atom(app * atom, bool gate_ctx) { TRACEFN(mk_pp(atom, m)); if (!u().has_defs()) { return false; } for (expr * arg : *atom) { ctx().internalize(arg, false); } if (!ctx().e_internalized(atom)) { ctx().mk_enode(atom, false, true, false); } if (!ctx().b_internalized(atom)) { bool_var v = ctx().mk_bool_var(atom); ctx().set_var_theory(v, get_id()); } return true; } bool theory_recfun::internalize_term(app * term) { if (!u().has_defs()) { return false; } for (expr* e : *term) { ctx().internalize(e, false); } // the internalization of the arguments may have triggered the internalization of term. if (!ctx().e_internalized(term)) { ctx().mk_enode(term, false, false, true); if (!ctx().relevancy() && u().is_defined(term)) { push_case_expand(alloc(case_expansion, u(), term)); } } return true; } void theory_recfun::reset_queues() { for (auto* e : m_q_case_expand) { dealloc(e); } m_q_case_expand.reset(); for (auto* e : m_q_body_expand) { dealloc(e); } m_q_body_expand.reset(); m_q_clauses.clear(); } void theory_recfun::reset_eh() { reset_queues(); m_stats.reset(); theory::reset_eh(); } /* * when `n` becomes relevant, if it's `f(t1...tn)` with `f` defined, * then case-expand `n`. If it's a macro we can also immediately * body-expand it. */ void theory_recfun::relevant_eh(app * n) { SASSERT(ctx().relevancy()); TRACEFN("relevant_eh: (defined) " << u().is_defined(n) << " " << mk_pp(n, m)); if (u().is_defined(n) && u().has_defs()) { push_case_expand(alloc(case_expansion, u(), n)); } } void theory_recfun::push_scope_eh() { theory::push_scope_eh(); m_preds_lim.push_back(m_preds.size()); } void theory_recfun::pop_scope_eh(unsigned num_scopes) { theory::pop_scope_eh(num_scopes); reset_queues(); // restore depth book-keeping unsigned new_lim = m_preds_lim.size()-num_scopes; #if 0 // depth tracking of recursive unfolding is // turned off when enabling this code: unsigned start = m_preds_lim[new_lim]; for (unsigned i = start; i < m_preds.size(); ++i) { m_pred_depth.remove(m_preds.get(i)); } m_preds.resize(start); #endif m_preds_lim.shrink(new_lim); } void theory_recfun::restart_eh() { TRACEFN("restart"); reset_queues(); theory::restart_eh(); } bool theory_recfun::can_propagate() { return ! (m_q_case_expand.empty() && m_q_body_expand.empty() && m_q_clauses.empty()); } void theory_recfun::propagate() { for (literal_vector & c : m_q_clauses) { TRACEFN("add axiom " << pp_lits(ctx(), c)); ctx().mk_th_axiom(get_id(), c); } m_q_clauses.clear(); for (unsigned i = 0; i < m_q_case_expand.size(); ++i) { case_expansion* e = m_q_case_expand[i]; if (e->m_def->is_fun_macro()) { // body expand immediately assert_macro_axiom(*e); } else { // case expand SASSERT(e->m_def->is_fun_defined()); assert_case_axioms(*e); } dealloc(e); m_q_case_expand[i] = nullptr; } m_stats.m_case_expansions += m_q_case_expand.size(); m_q_case_expand.reset(); for (unsigned i = 0; i < m_q_body_expand.size(); ++i) { assert_body_axiom(*m_q_body_expand[i]); dealloc(m_q_body_expand[i]); m_q_body_expand[i] = nullptr; } m_stats.m_body_expansions += m_q_body_expand.size(); m_q_body_expand.reset(); } /** * make clause `depth_limit => ~guard` * the guard appears at a depth below the current cutoff. */ void theory_recfun::assert_max_depth_limit(expr* guard) { literal_vector c; app_ref dlimit = m_util.mk_depth_limit_pred(m_max_depth); c.push_back(~mk_literal(dlimit)); c.push_back(~mk_literal(guard)); TRACEFN("max-depth limit: add clause " << pp_lits(ctx(), c)); m_q_clauses.push_back(std::move(c)); } /** * retrieve depth associated with predicate or expression. */ unsigned theory_recfun::get_depth(expr* e) { SASSERT(u().is_defined(e) || u().is_case_pred(e)); unsigned d = 0; m_pred_depth.find(e, d); TRACEFN("depth " << d << " " << mk_pp(e, m)); return d; } /** * Update depth of subterms of e with respect to d. */ void theory_recfun::set_depth_rec(unsigned d, expr* e) { struct insert_c { theory_recfun& th; unsigned m_depth; insert_c(theory_recfun& th, unsigned d): th(th), m_depth(d) {} void operator()(app* e) { th.set_depth(m_depth, e); } void operator()(quantifier*) {} void operator()(var*) {} }; insert_c proc(*this, d); for_each_expr(proc, e); } void theory_recfun::set_depth(unsigned depth, expr* e) { if ((u().is_defined(e) || u().is_case_pred(e)) && !m_pred_depth.contains(e)) { m_pred_depth.insert(e, depth); m_preds.push_back(e); TRACEFN("depth " << depth << " : " << mk_pp(e, m)); } } /** * if `is_true` and `v = C_f_i(t1...tn)`, * then body-expand i-th case of `f(t1...tn)` */ void theory_recfun::assign_eh(bool_var v, bool is_true) { expr* e = ctx().bool_var2expr(v); if (is_true && u().is_case_pred(e)) { TRACEFN("assign_case_pred_true " << mk_pp(e, m)); // body-expand push_body_expand(alloc(body_expansion, u(), to_app(e))); } } // replace `vars` by `args` in `e` expr_ref theory_recfun::apply_args( unsigned depth, recfun::vars const & vars, ptr_vector const & args, expr * e) { SASSERT(is_standard_order(vars)); var_subst subst(m, true); expr_ref new_body(m); new_body = subst(e, args.size(), args.c_ptr()); ctx().get_rewriter()(new_body); // simplify set_depth_rec(depth + 1, new_body); return new_body; } literal theory_recfun::mk_literal(expr* e) { ctx().internalize(e, false); literal lit = ctx().get_literal(e); ctx().mark_as_relevant(lit); return lit; } literal theory_recfun::mk_eq_lit(expr* l, expr* r) { literal lit; if (m.is_true(r) || m.is_false(r)) { std::swap(l, r); } if (m.is_true(l)) { lit = mk_literal(r); } else if (m.is_false(l)) { lit = ~mk_literal(r); } else { lit = mk_eq(l, r, false); } ctx().mark_as_relevant(lit); return lit; } /** * For functions f(args) that are given as macros f(vs) = rhs * * 1. substitute `e.args` for `vs` into the macro rhs * 2. add unit clause `f(args) = rhs` */ void theory_recfun::assert_macro_axiom(case_expansion & e) { m_stats.m_macro_expansions++; TRACEFN("case expansion " << pp_case_expansion(e, m) << "\n"); SASSERT(e.m_def->is_fun_macro()); auto & vars = e.m_def->get_vars(); expr_ref lhs(e.m_lhs, m); unsigned depth = get_depth(e.m_lhs); expr_ref rhs(apply_args(depth, vars, e.m_args, e.m_def->get_rhs()), m); literal lit = mk_eq_lit(lhs, rhs); if (m.has_trace_stream()) log_axiom_instantiation(ctx().bool_var2expr(lit.var())); ctx().mk_th_axiom(get_id(), 1, &lit); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; TRACEFN("macro expansion yields " << mk_pp(rhs, m) << "\n" << "literal " << pp_lit(ctx(), lit)); } /** * Add case axioms for every case expansion path. * * assert `p(args) <=> And(guards)` (with CNF on the fly) * * also body-expand paths that do not depend on any defined fun */ void theory_recfun::assert_case_axioms(case_expansion & e) { TRACEFN("assert_case_axioms "<< pp_case_expansion(e,m) << " with " << e.m_def->get_cases().size() << " cases"); SASSERT(e.m_def->is_fun_defined()); // add case-axioms for all case-paths auto & vars = e.m_def->get_vars(); literal_vector preds; expr_ref_vector pred_exprs(m); for (recfun::case_def const & c : e.m_def->get_cases()) { // applied predicate to `args` app_ref pred_applied = c.apply_case_predicate(e.m_args); // cut off cases below max-depth unsigned depth = get_depth(e.m_lhs); set_depth(depth, pred_applied); SASSERT(u().owns_app(pred_applied)); literal concl = mk_literal(pred_applied); preds.push_back(concl); pred_exprs.push_back(pred_applied); if (c.is_immediate()) { body_expansion be(pred_applied, c, e.m_args); assert_body_axiom(be); } else if (depth >= m_max_depth) { assert_max_depth_limit(pred_applied); continue; } literal_vector guards; expr_ref_vector exprs(m); guards.push_back(concl); for (auto & g : c.get_guards()) { expr_ref ga = apply_args(depth, vars, e.m_args, g); literal guard = mk_literal(ga); guards.push_back(~guard); exprs.push_back(m.mk_not(ga)); literal c[2] = {~concl, guard}; if (m.has_trace_stream()) { app_ref body(m); body = m.mk_implies(pred_applied, ga); log_axiom_instantiation(body); } ctx().mk_th_axiom(get_id(), 2, c); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; } if (m.has_trace_stream()) { app_ref body(m); body = m.mk_implies(m.mk_not(pred_applied), m.mk_or(exprs.size(), exprs.c_ptr())); log_axiom_instantiation(body); } ctx().mk_th_axiom(get_id(), guards); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; } // the disjunction of branches is asserted // to close the available cases. if (m.has_trace_stream()) { app_ref body(m); body = m.mk_or(pred_exprs.size(), pred_exprs.c_ptr()); log_axiom_instantiation(body); } ctx().mk_th_axiom(get_id(), preds); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; } /** * For a guarded definition guards => f(vars) = rhs * and occurrence f(args) * * substitute `args` for `vars` in guards, and rhs * add axiom guards[args/vars] => f(args) = rhs[args/vars] * */ void theory_recfun::assert_body_axiom(body_expansion & e) { recfun::def & d = *e.m_cdef->get_def(); auto & vars = d.get_vars(); auto & args = e.m_args; SASSERT(is_standard_order(vars)); unsigned depth = get_depth(e.m_pred); expr_ref lhs(u().mk_fun_defined(d, args), m); expr_ref rhs = apply_args(depth, vars, args, e.m_cdef->get_rhs()); literal_vector clause; expr_ref_vector exprs(m); for (auto & g : e.m_cdef->get_guards()) { expr_ref guard = apply_args(depth, vars, args, g); clause.push_back(~mk_literal(guard)); exprs.push_back(guard); if (clause.back() == true_literal) { TRACEFN("body " << pp_body_expansion(e,m) << "\n" << clause << "\n" << guard); return; } if (clause.back() == false_literal) { clause.pop_back(); } } clause.push_back(mk_eq_lit(lhs, rhs)); if (m.has_trace_stream()) { app_ref body(m); body = m.mk_implies(m.mk_and(exprs.size(), exprs.c_ptr()), m.mk_eq(lhs, rhs)); log_axiom_instantiation(body); } ctx().mk_th_axiom(get_id(), clause); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; TRACEFN("body " << pp_body_expansion(e,m)); TRACEFN(pp_lits(ctx(), clause)); } final_check_status theory_recfun::final_check_eh() { TRACEFN("final\n"); if (can_propagate()) { propagate(); return FC_CONTINUE; } return FC_DONE; } void theory_recfun::add_theory_assumptions(expr_ref_vector & assumptions) { if (u().has_defs()) { app_ref dlimit = m_util.mk_depth_limit_pred(m_max_depth); TRACEFN("add_theory_assumption " << mk_pp(dlimit.get(), m)); assumptions.push_back(dlimit); } } // if `dlimit` occurs in unsat core, return 'true' bool theory_recfun::should_research(expr_ref_vector & unsat_core) { for (auto & e : unsat_core) { if (u().is_depth_limit(e)) { m_max_depth = (3 * m_max_depth) / 2; IF_VERBOSE(1, verbose_stream() << "(smt.recfun :increase-depth " << m_max_depth << ")\n"); return true; } } return false; } void theory_recfun::display(std::ostream & out) const { out << "recfun{}\n"; } void theory_recfun::collect_statistics(::statistics & st) const { st.update("recfun macro expansion", m_stats.m_macro_expansions); st.update("recfun case expansion", m_stats.m_case_expansions); st.update("recfun body expansion", m_stats.m_body_expansions); } std::ostream& operator<<(std::ostream & out, theory_recfun::pp_case_expansion const & e) { return out << "case_exp(" << mk_pp(e.e.m_lhs, e.m) << ")"; } std::ostream& operator<<(std::ostream & out, theory_recfun::pp_body_expansion const & e) { out << "body_exp(" << e.e.m_cdef->get_decl()->get_name(); for (auto* t : e.e.m_args) { out << " " << mk_pp(t,e.m); } return out << ")"; } } z3-z3-4.8.7/src/smt/theory_recfun.h000066400000000000000000000136661356505360400170620ustar00rootroot00000000000000/*++ Copyright (c) 2018 Microsoft Corporation Module Name: theory_recfun.h Abstract: Theory responsible for unrolling recursive functions Author: Simon Cuares December 2017 Revision History: --*/ #ifndef THEORY_RECFUN_H_ #define THEORY_RECFUN_H_ #include "smt/smt_theory.h" #include "smt/smt_context.h" #include "ast/ast_pp.h" #include "ast/recfun_decl_plugin.h" namespace smt { class theory_recfun : public theory { struct stats { unsigned m_case_expansions, m_body_expansions, m_macro_expansions; void reset() { memset(this, 0, sizeof(stats)); } stats() { reset(); } }; // one case-expansion of `f(t1...tn)` struct case_expansion { app * m_lhs; // the term to expand recfun::def * m_def; ptr_vector m_args; case_expansion(recfun::util& u, app * n) : m_lhs(n), m_def(nullptr), m_args() { SASSERT(u.is_defined(n)); func_decl * d = n->get_decl(); m_def = &u.get_def(d); m_args.append(n->get_num_args(), n->get_args()); } case_expansion(case_expansion const & from) : m_lhs(from.m_lhs), m_def(from.m_def), m_args(from.m_args) {} case_expansion(case_expansion && from) : m_lhs(from.m_lhs), m_def(from.m_def), m_args(std::move(from.m_args)) {} }; struct pp_case_expansion { case_expansion & e; ast_manager & m; pp_case_expansion(case_expansion & e, ast_manager & m) : e(e), m(m) {} }; friend std::ostream& operator<<(std::ostream&, pp_case_expansion const &); // one body-expansion of `f(t1...tn)` using a `C_f_i(t1...tn)` struct body_expansion { app* m_pred; recfun::case_def const * m_cdef; ptr_vector 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()); } body_expansion(app* pred, recfun::case_def const & d, ptr_vector & args) : m_pred(pred), m_cdef(&d), m_args(args) {} body_expansion(body_expansion const & from): m_pred(from.m_pred), m_cdef(from.m_cdef), m_args(from.m_args) {} body_expansion(body_expansion && from) : m_pred(from.m_pred), m_cdef(from.m_cdef), m_args(std::move(from.m_args)) {} }; struct pp_body_expansion { body_expansion & e; ast_manager & m; pp_body_expansion(body_expansion & e, ast_manager & m) : e(e), m(m) {} }; friend std::ostream& operator<<(std::ostream&, pp_body_expansion const &); ast_manager& m; recfun::decl::plugin& m_plugin; recfun::util& m_util; stats m_stats; // book-keeping for depth of predicates obj_map m_pred_depth; expr_ref_vector m_preds; unsigned_vector m_preds_lim; unsigned m_max_depth; // for fairness and termination ptr_vector m_q_case_expand; ptr_vector m_q_body_expand; vector m_q_clauses; recfun::util & u() const { return m_util; } bool is_defined(app * f) const { return u().is_defined(f); } bool is_case_pred(app * f) const { return u().is_case_pred(f); } bool is_defined(enode * e) const { return is_defined(e->get_owner()); } bool is_case_pred(enode * e) const { return is_case_pred(e->get_owner()); } void reset_queues(); expr_ref apply_args(unsigned depth, recfun::vars const & vars, ptr_vector const & args, expr * e); //!< substitute variables by args void assert_macro_axiom(case_expansion & e); void assert_case_axioms(case_expansion & e); void assert_body_axiom(body_expansion & e); literal mk_literal(expr* e); void assert_max_depth_limit(expr* guard); unsigned get_depth(expr* e); void set_depth(unsigned d, expr* e); void set_depth_rec(unsigned d, expr* e); literal mk_eq_lit(expr* l, expr* r); bool is_standard_order(recfun::vars const& vars) const { 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); } void push_body_expand(body_expansion* e) { m_q_body_expand.push_back(e); } bool internalize_atom(app * atom, bool gate_ctx) override; bool internalize_term(app * term) override; void reset_eh() override; void relevant_eh(app * n) override; char const * get_name() const override; final_check_status final_check_eh() override; void assign_eh(bool_var v, bool is_true) override; void push_scope_eh() override; void pop_scope_eh(unsigned num_scopes) override; void restart_eh() override; bool can_propagate() override; void propagate() override; bool should_research(expr_ref_vector &) override; void new_eq_eh(theory_var v1, theory_var v2) override {} void new_diseq_eh(theory_var v1, theory_var v2) override {} void add_theory_assumptions(expr_ref_vector & assumptions) override; void init(context* ctx) override; public: theory_recfun(ast_manager & m); ~theory_recfun() override; theory * mk_fresh(context * new_ctx) override; void init_search_eh() override; void display(std::ostream & out) const override; void collect_statistics(::statistics & st) const override; }; } #endif z3-z3-4.8.7/src/smt/theory_seq.cpp000066400000000000000000006245561356505360400167310ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: theory_seq.cpp Abstract: Native theory solver for sequences. Author: Nikolaj Bjorner (nbjorner) 2015-6-12 Outline: A cascading sequence of solvers: - simplify_and_solve_eqs - canonize equality - solve_unit_eq: x = t, where x not in t. - solve_binary_eq: xa = bx -> a = b, xa = bx - solve_nth_eq: x = unit(nth(x,0)).unit(nth(x,1)).unit(nth(x,2)...unit(nth(x,n-1)) - solve_itos: itos(i) = "" -> i < 0 - check_contains - (f,dep) = canonize(contains(a, b)) lit := |b| > |a| f := true -> conflict f := false -> solved value(lit) = l_true -> solved f := s = t -> dep -> s != t f := f1 & f2 -> dep -> ~f1 | ~f2 f := f1 | f2 -> dep -> ~f1 & ~f2 - solve_nqs - s_i = t_i, d_i <- solve(s = t) - create literals for s_i = t_i - if one of created literals is false, done. - conflict if all created literals are true - fixed_length - len(s) = k -> s = unit(nth(0,s)).unit(nth(1,s))....unit(nth(n-1,s)) - len_based_split s = x.xs t = y.ys, len(x) = len(y) -> x = y & xs = ys s = x.xs t = y.ys, len(x) = len(y) + offset -> y = x*Z, Z*xs = ys s = x.x'.xs, t = y.y'.ys, len(xs) = len(ys) -> xs = ys - check_int_string e := itos(n), len(e) = v, v > 0 -> n := stoi(e), len(e) = v, v > 0 -> - 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 - reduce_length_eq x1...xn = y1...ym, len(x1...xk) = len(y1...yj) -> x1...xk = y1..yj, x{k+1}..xn = y{j+1}..ym - branch_unit_variable len(x) = n -> x = unit(a1)unit(a2)...unit(a_n) - branch_binary_variable x ++ units1 = units2 ++ y -> x is prefix of units2 or x = units2 ++ y1, y = y1 ++ y2, y2 = units2 - branch_variable - branch_variable_mb s = xs, t = ys, each x_i, y_j has a length. based on length comparisons decompose into smaller equalities. - branch_variable_eq cycle through branch options - branch_ternary_variable1 - branch_ternary_variable2 - check_length_coherence len(e) >= lo => e = unit(nth(0,e)).unit(nth(1,e))....unit(nth(lo-1,e)).seq len(e) <= lo => seq = empty len(e) <= hi => len(seq) <= hi - lo - check_extensionality --*/ #include #include "ast/ast_pp.h" #include "ast/ast_ll_pp.h" #include "ast/ast_trail.h" #include "ast/for_each_expr.h" #include "model/value_factory.h" #include "smt/smt_context.h" #include "smt/smt_model_generator.h" #include "smt/theory_seq.h" #include "smt/theory_arith.h" #include "smt/theory_lra.h" #include "smt/smt_kernel.h" using namespace smt; struct display_expr { ast_manager& m; display_expr(ast_manager& m): m(m) {} std::ostream& display(std::ostream& out, sym_expr* e) const { return e->display(out); } }; class seq_expr_solver : public expr_solver { kernel m_kernel; public: seq_expr_solver(ast_manager& m, smt_params& fp): m_kernel(m, fp) {} lbool check_sat(expr* e) override { m_kernel.push(); 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; } }; void theory_seq::solution_map::update(expr* e, expr* r, dependency* d) { if (e == r) { return; } m_cache.reset(); std::pair value; if (m_map.find(e, value)) { add_trail(DEL, e, value.first, value.second); } value.first = r; value.second = d; m_map.insert(e, value); add_trail(INS, e, r, d); } void theory_seq::solution_map::add_trail(map_update op, expr* l, expr* r, dependency* d) { m_updates.push_back(op); m_lhs.push_back(l); m_rhs.push_back(r); m_deps.push_back(d); } bool theory_seq::solution_map::is_root(expr* e) const { return !m_map.contains(e); } // e1 -> ... -> e2 // e2 -> e3 // e1 -> .... -> e3 // e1 -> ... x, e2 -> ... x void theory_seq::solution_map::find_rec(expr* e, svector >& finds) { dependency* d = nullptr; std::pair value(e, d); do { e = value.first; d = m_dm.mk_join(d, value.second); finds.push_back(value); } while (m_map.find(e, value)); } bool theory_seq::solution_map::find1(expr* e, expr*& r, dependency*& d) { std::pair value; if (m_map.find(e, value)) { d = m_dm.mk_join(d, value.second); r = value.first; return true; } else { return false; } } expr* theory_seq::solution_map::find(expr* e, dependency*& d) { std::pair value; d = nullptr; expr* result = e; while (m_map.find(result, value)) { d = m_dm.mk_join(d, value.second); SASSERT(result != value.first); SASSERT(e != value.first); result = value.first; } return result; } expr* theory_seq::solution_map::find(expr* e) { std::pair value; while (m_map.find(e, value)) { e = value.first; } return e; } void theory_seq::solution_map::pop_scope(unsigned num_scopes) { if (num_scopes == 0) return; m_cache.reset(); unsigned start = m_limit[m_limit.size() - num_scopes]; for (unsigned i = m_updates.size(); i-- > start; ) { if (m_updates[i] == INS) { m_map.remove(m_lhs.get(i)); } else { m_map.insert(m_lhs.get(i), std::make_pair(m_rhs.get(i), m_deps[i])); } } m_updates.resize(start); m_lhs.resize(start); m_rhs.resize(start); m_deps.resize(start); m_limit.resize(m_limit.size() - num_scopes); } void theory_seq::solution_map::display(std::ostream& out) const { for (auto const& kv : m_map) { out << mk_bounded_pp(kv.m_key, m, 2) << " |-> " << mk_bounded_pp(kv.m_value.first, m, 2) << "\n"; } } bool theory_seq::exclusion_table::contains(expr* e, expr* r) const { if (e->get_id() > r->get_id()) { std::swap(e, r); } return m_table.contains(std::make_pair(e, r)); } void theory_seq::exclusion_table::update(expr* e, expr* r) { if (e->get_id() > r->get_id()) { std::swap(e, r); } if (e != r && !m_table.contains(std::make_pair(e, r))) { m_lhs.push_back(e); m_rhs.push_back(r); m_table.insert(std::make_pair(e, r)); } } void theory_seq::exclusion_table::pop_scope(unsigned num_scopes) { if (num_scopes == 0) return; unsigned start = m_limit[m_limit.size() - num_scopes]; for (unsigned i = start; i < m_lhs.size(); ++i) { m_table.erase(std::make_pair(m_lhs.get(i), m_rhs.get(i))); } m_lhs.resize(start); m_rhs.resize(start); m_limit.resize(m_limit.size() - num_scopes); } void theory_seq::exclusion_table::display(std::ostream& out) const { for (auto const& kv : m_table) { out << mk_bounded_pp(kv.first, m, 2) << " != " << mk_bounded_pp(kv.second, m, 2) << "\n"; } } theory_seq::theory_seq(ast_manager& m, theory_seq_params const & params): theory(m.mk_family_id("seq")), m(m), m_params(params), m_rep(m, m_dm), m_lts_checked(false), m_eq_id(0), m_find(*this), m_overlap(m), m_overlap2(m), m_len_prop_lvl(-1), m_factory(nullptr), m_exclude(m), 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), m_res(m), m_max_unfolding_depth(1), m_max_unfolding_lit(null_literal), m_new_solution(false), m_new_propagation(false), m_mk_aut(m) { m_prefix = "seq.p.suffix"; m_suffix = "seq.s.prefix"; m_accept = "aut.accept"; m_tail = "seq.tail"; m_seq_first = "seq.first"; m_seq_last = "seq.last"; m_indexof_left = "seq.idx.left"; m_indexof_right = "seq.idx.right"; m_aut_step = "aut.step"; m_pre = "seq.pre"; // (seq.pre s l): prefix of string s of length l m_post = "seq.post"; // (seq.post s l): suffix of string s of length l m_eq = "seq.eq"; m_seq_align = "seq.align"; } theory_seq::~theory_seq() { m_trail_stack.reset(); } void theory_seq::init(context* ctx) { theory::init(ctx); m_arith_value.init(ctx); } #define TRACEFIN(s) { TRACE("seq", tout << ">>" << s << "\n";); IF_VERBOSE(31, verbose_stream() << s << "\n"); } struct scoped_enable_trace { scoped_enable_trace() { enable_trace("seq"); } ~scoped_enable_trace() { disable_trace("seq"); } }; 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; TRACEFIN("solve_eqs"); return FC_CONTINUE; } if (check_contains()) { ++m_stats.m_propagate_contains; TRACEFIN("propagate_contains"); return FC_CONTINUE; } if (check_lts()) { TRACEFIN("check_lts"); return FC_CONTINUE; } if (solve_nqs(0)) { ++m_stats.m_solve_nqs; TRACEFIN("solve_nqs"); return FC_CONTINUE; } if (fixed_length(true)) { ++m_stats.m_fixed_length; TRACEFIN("zero_length"); return FC_CONTINUE; } if (m_params.m_split_w_len && len_based_split()) { ++m_stats.m_branch_variable; TRACEFIN("split_based_on_length"); return FC_CONTINUE; } if (fixed_length()) { ++m_stats.m_fixed_length; TRACEFIN("fixed_length"); return FC_CONTINUE; } if (check_int_string()) { ++m_stats.m_int_string; TRACEFIN("int_string"); return FC_CONTINUE; } if (reduce_length_eq()) { ++m_stats.m_branch_variable; TRACEFIN("reduce_length"); return FC_CONTINUE; } if (branch_unit_variable()) { ++m_stats.m_branch_variable; TRACEFIN("branch_unit_variable"); return FC_CONTINUE; } if (branch_binary_variable()) { ++m_stats.m_branch_variable; TRACEFIN("branch_binary_variable"); return FC_CONTINUE; } if (branch_variable()) { ++m_stats.m_branch_variable; TRACEFIN("branch_variable"); return FC_CONTINUE; } if (check_length_coherence()) { ++m_stats.m_check_length_coherence; TRACEFIN("check_length_coherence"); return FC_CONTINUE; } if (!check_extensionality()) { ++m_stats.m_extensionality; TRACEFIN("extensionality"); return FC_CONTINUE; } if (branch_nqs()) { ++m_stats.m_branch_nqs; TRACEFIN("branch_ne"); return FC_CONTINUE; } if (is_solved()) { //scoped_enable_trace _se; TRACEFIN("is_solved"); TRACE("seq", display(tout);); return FC_DONE; } TRACEFIN("give_up"); return FC_GIVEUP; } 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())) { TRACE("seq", tout << "length\n";); return true; } } return false; } bool theory_seq::branch_binary_variable() { for (auto const& e : m_eqs) { if (branch_binary_variable(e)) { TRACE("seq", display_equation(tout, e);); return true; } } return false; } bool theory_seq::branch_binary_variable(eq const& e) { if (is_complex(e)) { return false; } ptr_vector xs, ys; expr_ref x(m), y(m); bool is_binary = is_binary_eq(e.ls(), e.rs(), x, xs, ys, y); if (!is_binary) { is_binary = is_binary_eq(e.rs(), e.ls(), x, xs, ys, y); } if (!is_binary) { return false; } if (x == y) { return false; } // Equation is of the form x ++ xs = ys ++ y // where xs, ys are units. // x is either a prefix of ys, all of ys ++ y or ys ++ y1, such that y = y1 ++ y2, y2 = xs rational lenX, lenY; context& ctx = get_context(); if (branch_variable_eq(e)) { return true; } if (!get_length(x, lenX)) { add_length_to_eqc(x); return true; } if (!get_length(y, lenY)) { add_length_to_eqc(y); return true; } if (lenX + rational(xs.size()) != lenY + rational(ys.size())) { // |x| - |y| = |ys| - |xs| expr_ref a(mk_sub(mk_len(x), mk_len(y)), m); expr_ref b(m_autil.mk_int(ys.size()-xs.size()), m); propagate_lit(e.dep(), 0, nullptr, mk_eq(a, b, false)); return true; } if (lenX <= rational(ys.size())) { expr_ref_vector Ys(m); Ys.append(ys.size(), ys.c_ptr()); branch_unit_variable(e.dep(), x, Ys); return true; } expr_ref le(m_autil.mk_le(mk_len(x), m_autil.mk_int(ys.size())), m); literal lit = mk_literal(le); if (l_false == ctx.get_assignment(lit)) { // |x| > |ys| => x = ys ++ y1, y = y1 ++ y2, y2 = xs expr_ref Y1(mk_skolem(symbol("seq.left"), x, y), m); expr_ref Y2(mk_skolem(symbol("seq.right"), x, y), m); ys.push_back(Y1); expr_ref ysY1 = mk_concat(ys); expr_ref xsE = mk_concat(xs); expr_ref Y1Y2 = mk_concat(Y1, Y2); dependency* dep = e.dep(); propagate_eq(dep, ~lit, x, ysY1); propagate_eq(dep, ~lit, y, Y1Y2); propagate_eq(dep, ~lit, Y2, xsE); } else { ctx.mark_as_relevant(lit); } return true; } bool theory_seq::branch_unit_variable() { bool result = false; for (auto const& e : m_eqs) { if (is_unit_eq(e.ls(), e.rs())) { branch_unit_variable(e.dep(), e.ls()[0], e.rs()); result = true; break; } else if (is_unit_eq(e.rs(), e.ls())) { branch_unit_variable(e.dep(), e.rs()[0], e.ls()); result = true; break; } } CTRACE("seq", result, tout << "branch unit variable";); return result; } /** \brief ls := X... == rs := abcdef */ 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; } for (auto const& elem : rs) { if (!m_util.str.is_unit(elem)) { return false; } } return true; } void theory_seq::branch_unit_variable(dependency* dep, expr* X, expr_ref_vector const& units) { SASSERT(is_var(X)); context& ctx = get_context(); rational lenX; if (!get_length(X, lenX)) { TRACE("seq", tout << "enforce length on " << mk_bounded_pp(X, m, 2) << "\n";); add_length_to_eqc(X); return; } if (lenX > rational(units.size())) { expr_ref le(m_autil.mk_le(mk_len(X), m_autil.mk_int(units.size())), m); TRACE("seq", tout << "propagate length on " << mk_bounded_pp(X, m, 2) << "\n";); propagate_lit(dep, 0, nullptr, mk_literal(le)); return; } SASSERT(lenX.is_unsigned()); unsigned lX = lenX.get_unsigned(); if (lX == 0) { TRACE("seq", tout << "set empty length " << mk_bounded_pp(X, m, 2) << "\n";); set_empty(X); } else { literal lit = mk_eq(m_autil.mk_int(lX), mk_len(X), false); if (l_true == ctx.get_assignment(lit)) { expr_ref R(m_util.str.mk_concat(lX, units.c_ptr()), m); propagate_eq(dep, lit, X, R); TRACE("seq", tout << "propagate " << mk_pp(X, m) << " " << R << "\n";); } else { TRACE("seq", tout << "set phase " << mk_pp(X, m) << "\n";); ctx.mark_as_relevant(lit); ctx.force_phase(lit); } } } bool theory_seq::branch_ternary_variable1() { 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; } } return false; } bool theory_seq::branch_ternary_variable2() { 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; } } return false; } bool theory_seq::eq_unit(expr* const& l, expr* const &r) const { return l == r || is_unit_nth(l) || is_unit_nth(r); } // exists x, y, rs' != empty s.t. (ls = x ++ rs' ++ y & rs = rs') || (ls = rs' ++ x && rs = y ++ rs') // TBD: spec comment above doesn't seem to match what this function does. unsigned_vector theory_seq::overlap(expr_ref_vector const& ls, expr_ref_vector const& rs) { SASSERT(!ls.empty() && !rs.empty()); unsigned_vector result; expr_ref l = mk_concat(ls); expr_ref r = mk_concat(rs); expr_ref pair(m.mk_eq(l,r), m); if (m_overlap.find(pair, result)) { return result; } result.reset(); for (unsigned i = 0; i < ls.size(); ++i) { if (eq_unit(ls[i], rs.back())) { bool same = rs.size() > i; for (unsigned j = 0; same && j < i; ++j) { same = eq_unit(ls[j], rs[rs.size() - 1 - i + j]); } if (same) result.push_back(i+1); } } m_overlap.insert(pair, result); return result; } // exists x, y, rs' != empty s.t. (ls = x ++ rs' ++ y & rs = rs') || (ls = x ++ rs' && rs = rs' ++ y) unsigned_vector theory_seq::overlap2(expr_ref_vector const& ls, expr_ref_vector const& rs) { SASSERT(!ls.empty() && !rs.empty()); unsigned_vector res; expr_ref l = mk_concat(ls); expr_ref r = mk_concat(rs); expr_ref pair(m.mk_eq(l,r), m); if (m_overlap2.find(pair, res)) { return res; } unsigned_vector result; for (unsigned i = 0; i < ls.size(); ++i) { if (eq_unit(ls[i],rs[0])) { bool same = true; unsigned j = i+1; while (j < ls.size() && j-i < rs.size()) { if (!eq_unit(ls[j], rs[j-i])) { same = false; break; } ++j; } if (same) result.push_back(i); } } m_overlap2.insert(pair, result); return result; } bool theory_seq::branch_ternary_variable_base( dependency* dep, unsigned_vector indexes, expr* const& x, expr_ref_vector const& xs, expr* const& y1, expr_ref_vector const& ys, expr* const& y2) { context& ctx = get_context(); bool change = false; for (auto ind : indexes) { TRACE("seq", tout << "ind = " << ind << "\n";); expr_ref xs2E(m); if (xs.size() > ind) { xs2E = m_util.str.mk_concat(xs.size()-ind, xs.c_ptr()+ind); } else { xs2E = m_util.str.mk_empty(m.get_sort(x)); } literal lit1 = mk_literal(m_autil.mk_le(mk_len(y2), m_autil.mk_int(xs.size()-ind))); if (ctx.get_assignment(lit1) == l_undef) { TRACE("seq", tout << "base case init\n";); ctx.mark_as_relevant(lit1); ctx.force_phase(lit1); change = true; continue; } else if (ctx.get_assignment(lit1) == l_true) { TRACE("seq", tout << "base case: true branch\n";); literal_vector lits; lits.push_back(lit1); propagate_eq(dep, lits, y2, xs2E, true); if (ind > ys.size()) { expr_ref xs1E(m_util.str.mk_concat(ind-ys.size(), xs.c_ptr()), m); expr_ref xxs1E = mk_concat(x, xs1E); propagate_eq(dep, lits, xxs1E, y1, true); } else if (ind == ys.size()) { propagate_eq(dep, lits, x, y1, true); } else { expr_ref ys1E(m_util.str.mk_concat(ys.size()-ind, ys.c_ptr()), m); expr_ref y1ys1E = mk_concat(y1, ys1E); propagate_eq(dep, lits, x, y1ys1E, true); } return true; } else { TRACE("seq", tout << "base case: false branch\n";); continue; } } return change; } // Equation is of the form x ++ xs = y1 ++ ys ++ y2 where xs, ys are units. bool theory_seq::branch_ternary_variable(eq const& e, bool flag1) { expr_ref_vector xs(m), ys(m); expr_ref x(m), y1(m), y2(m); bool is_ternary = is_ternary_eq(e.ls(), e.rs(), x, xs, y1, ys, y2, flag1); if (!is_ternary) { is_ternary = is_ternary_eq(e.rs(), e.ls(), x, xs, y1, ys, y2, flag1); } if (!is_ternary) { return false; } rational lenX, lenY1, lenY2; context& ctx = get_context(); if (!get_length(x, lenX)) { add_length_to_eqc(x); } if (!get_length(y1, lenY1)) { add_length_to_eqc(y1); } if (!get_length(y2, lenY2)) { add_length_to_eqc(y2); } SASSERT(!xs.empty() && !ys.empty()); unsigned_vector indexes = overlap(xs, ys); if (branch_ternary_variable_base(e.dep(), indexes, x, xs, y1, ys, y2)) return true; // x ++ xs = y1 ++ ys ++ y2 => x = y1 ++ ys ++ z, z ++ xs = y2 expr_ref xsE = mk_concat(xs); expr_ref ysE = mk_concat(ys); expr_ref y1ys = mk_concat(y1, ysE); expr_ref Z(mk_skolem(m_seq_align, y2, xsE, x, y1ys), m); expr_ref ZxsE = mk_concat(Z, xsE); expr_ref y1ysZ = mk_concat(y1ys, Z); if (indexes.empty()) { TRACE("seq", tout << "one case\n";); TRACE("seq", display_equation(tout, e);); literal_vector lits; dependency* dep = e.dep(); propagate_eq(dep, lits, x, y1ysZ, true); propagate_eq(dep, lits, y2, ZxsE, true); } else { expr_ref ge(m_autil.mk_ge(mk_len(y2), m_autil.mk_int(xs.size())), m); literal lit2 = mk_literal(ge); if (ctx.get_assignment(lit2) == l_undef) { TRACE("seq", tout << "rec case init\n";); ctx.mark_as_relevant(lit2); ctx.force_phase(lit2); } else if (ctx.get_assignment(lit2) == l_true) { TRACE("seq", tout << "true branch\n";); TRACE("seq", display_equation(tout, e);); literal_vector lits; lits.push_back(lit2); dependency* dep = e.dep(); propagate_eq(dep, lits, x, y1ysZ, true); propagate_eq(dep, lits, y2, ZxsE, true); } else { return branch_ternary_variable_base(e.dep(), indexes, x, xs, y1, ys, y2); } } return true; } bool theory_seq::branch_ternary_variable_base2(dependency* dep, unsigned_vector indexes, expr_ref_vector const& xs, expr* const& x, expr* const& y1, expr_ref_vector const& ys, expr* const& y2) { context& ctx = get_context(); bool change = false; for (auto ind : indexes) { expr_ref xs1E(m); if (ind > 0) { xs1E = m_util.str.mk_concat(ind, xs.c_ptr()); } else { xs1E = m_util.str.mk_empty(m.get_sort(x)); } literal lit1 = mk_literal(m_autil.mk_le(mk_len(y1), m_autil.mk_int(ind))); if (ctx.get_assignment(lit1) == l_undef) { TRACE("seq", tout << "base case init\n";); ctx.mark_as_relevant(lit1); ctx.force_phase(lit1); change = true; continue; } else if (ctx.get_assignment(lit1) == l_true) { TRACE("seq", tout << "base case: true branch\n";); literal_vector lits; lits.push_back(lit1); propagate_eq(dep, lits, y1, xs1E, true); if (xs.size() - ind > ys.size()) { expr_ref xs2E(m_util.str.mk_concat(xs.size()-ind-ys.size(), xs.c_ptr()+ind+ys.size()), m); expr_ref xs2x = mk_concat(xs2E, x); propagate_eq(dep, lits, xs2x, y2, true); } else if (xs.size() - ind == ys.size()) { propagate_eq(dep, lits, x, y2, true); } else { expr_ref ys1E(m_util.str.mk_concat(ys.size()-xs.size()+ind, ys.c_ptr()+xs.size()-ind), m); expr_ref ys1y2 = mk_concat(ys1E, y2); propagate_eq(dep, lits, x, ys1y2, true); } return true; } else { TRACE("seq", tout << "base case: false branch\n";); continue; } } return change; } // Equation is of the form xs ++ x = y1 ++ ys ++ y2 where xs, ys are units. bool theory_seq::branch_ternary_variable2(eq const& e, bool flag1) { expr_ref_vector xs(m), ys(m); expr_ref x(m), y1(m), y2(m); bool is_ternary = is_ternary_eq2(e.ls(), e.rs(), xs, x, y1, ys, y2, flag1); if (!is_ternary) { is_ternary = is_ternary_eq2(e.rs(), e.ls(), xs, x, y1, ys, y2, flag1); } if (!is_ternary) { return false; } rational lenX, lenY1, lenY2; context& ctx = get_context(); if (!get_length(x, lenX)) { add_length_to_eqc(x); } if (!get_length(y1, lenY1)) { add_length_to_eqc(y1); } if (!get_length(y2, lenY2)) { add_length_to_eqc(y2); } SASSERT(!xs.empty() && !ys.empty()); unsigned_vector indexes = overlap2(xs, ys); if (branch_ternary_variable_base2(e.dep(), indexes, xs, x, y1, ys, y2)) return true; // xs ++ x = y1 ++ ys ++ y2 => xs ++ z = y1, x = z ++ ys ++ y2 expr_ref xsE = mk_concat(xs); expr_ref ysE = mk_concat(ys); expr_ref ysy2 = mk_concat(ysE, y2); expr_ref Z(mk_skolem(m_seq_align, x, ysy2, y1, xsE), m); expr_ref xsZ = mk_concat(xsE, Z); expr_ref Zysy2 = mk_concat(Z, ysy2); if (indexes.empty()) { TRACE("seq", tout << "one case\n";); TRACE("seq", display_equation(tout, e);); literal_vector lits; dependency* dep = e.dep(); propagate_eq(dep, lits, x, Zysy2, true); propagate_eq(dep, lits, y1, xsZ, true); } else { expr_ref ge(m_autil.mk_ge(mk_len(y1), m_autil.mk_int(xs.size())), m); literal lit2 = mk_literal(ge); if (ctx.get_assignment(lit2) == l_undef) { TRACE("seq", tout << "rec case init\n";); ctx.mark_as_relevant(lit2); ctx.force_phase(lit2); } else if (ctx.get_assignment(lit2) == l_true) { TRACE("seq", tout << "true branch\n";); TRACE("seq", display_equation(tout, e);); literal_vector lits; lits.push_back(lit2); dependency* dep = e.dep(); propagate_eq(dep, lits, x, Zysy2, true); propagate_eq(dep, lits, y1, xsZ, true); } else { return branch_ternary_variable_base2(e.dep(), indexes, xs, x, y1, ys, y2); } } return true; } bool theory_seq::branch_quat_variable() { for (auto const& e : m_eqs) { if (branch_quat_variable(e)) { return true; } } return false; } // Equation is of the form x1 ++ xs ++ x2 = y1 ++ ys ++ y2 where xs, ys are units. bool theory_seq::branch_quat_variable(eq const& e) { expr_ref_vector xs(m), ys(m); expr_ref x1_l(m), x2(m), y1_l(m), y2(m); bool is_quat = is_quat_eq(e.ls(), e.rs(), x1_l, xs, x2, y1_l, ys, y2); if (!is_quat) { return false; } rational lenX1, lenX2, lenY1, lenY2; context& ctx = get_context(); if (!get_length(x1_l, lenX1)) { add_length_to_eqc(x1_l); } if (!get_length(y1_l, lenY1)) { add_length_to_eqc(y1_l); } if (!get_length(x2, lenX2)) { add_length_to_eqc(x2); } if (!get_length(y2, lenY2)) { add_length_to_eqc(y2); } SASSERT(!xs.empty() && !ys.empty()); xs.push_back(x2); expr_ref xsx2 = mk_concat(xs); ys.push_back(y2); expr_ref ysy2 = mk_concat(ys); expr_ref x1(x1_l, m); expr_ref y1(y1_l, m); expr_ref sub(mk_sub(mk_len(x1_l), mk_len(y1_l)), m); expr_ref le(m_autil.mk_le(sub, m_autil.mk_int(0)), m); literal lit2 = mk_literal(le); if (ctx.get_assignment(lit2) == l_undef) { TRACE("seq", tout << "init branch\n";); TRACE("seq", display_equation(tout, e);); ctx.mark_as_relevant(lit2); ctx.force_phase(lit2); } else if (ctx.get_assignment(lit2) == l_false) { // |x1| > |y1| => x1 = y1 ++ z1, z1 ++ xs ++ x2 = ys ++ y2 TRACE("seq", tout << "false branch\n";); TRACE("seq", display_equation(tout, e);); expr_ref Z1(mk_skolem(m_seq_align, ysy2, xsx2, x1, y1), m); expr_ref y1Z1 = mk_concat(y1, Z1); expr_ref Z1xsx2 = mk_concat(Z1, xsx2); literal_vector lits; lits.push_back(~lit2); dependency* dep = e.dep(); propagate_eq(dep, lits, x1, y1Z1, true); propagate_eq(dep, lits, Z1xsx2, ysy2, true); } else if (ctx.get_assignment(lit2) == l_true) { // |x1| <= |y1| => x1 ++ z2 = y1, xs ++ x2 = z2 ++ ys ++ y2 TRACE("seq", tout << "true branch\n";); TRACE("seq", display_equation(tout, e);); expr_ref Z2(mk_skolem(m_seq_align, xsx2, ysy2, y1, x1), m); expr_ref x1Z2 = mk_concat(x1, Z2); expr_ref Z2ysy2 = mk_concat(Z2, ysy2); literal_vector lits; lits.push_back(lit2); dependency* dep = e.dep(); propagate_eq(dep, lits, x1Z2, y1, true); propagate_eq(dep, lits, xsx2, Z2ysy2, true); } return true; } void theory_seq::len_offset(expr* e, rational val) { context & ctx = get_context(); expr *l1 = nullptr, *l2 = nullptr, *l21 = nullptr, *l22 = nullptr; rational fact; if (m_autil.is_add(e, l1, l2) && m_autil.is_mul(l2, l21, l22) && m_autil.is_numeral(l21, fact) && fact.is_minus_one()) { if (ctx.e_internalized(l1) && ctx.e_internalized(l22)) { enode* r1 = get_root(l1), *n1 = r1; enode* r2 = get_root(l22), *n2 = r2; expr *e1 = nullptr, *e2 = nullptr; do { if (m_util.str.is_length(n1->get_owner(), e1)) break; n1 = n1->get_next(); } while (n1 != r1); do { if (m_util.str.is_length(n2->get_owner(), e2)) break; n2 = n2->get_next(); } while (n2 != r2); obj_map tmp; if (m_util.str.is_length(n1->get_owner(), e1) && m_util.str.is_length(n2->get_owner(), e2) && m_len_offset.find(r1, tmp)) { tmp.insert(r2, val.get_int32()); m_len_offset.insert(r1, tmp); TRACE("seq", tout << "a length pair: " << mk_pp(e1, m) << ", " << mk_pp(e2, m) << "\n";); return; } } } } // Find the length offset from the congruence closure core void theory_seq::prop_arith_to_len_offset() { context & ctx = get_context(); obj_hashtable const_set; ptr_vector::const_iterator it = ctx.begin_enodes(); ptr_vector::const_iterator end = ctx.end_enodes(); for (; it != end; ++it) { enode * root = (*it)->get_root(); rational val; if (m_autil.is_numeral(root->get_owner(), val) && val.is_neg()) { if (const_set.contains(root)) continue; const_set.insert(root); TRACE("seq", tout << "offset: " << mk_pp(root->get_owner(), m) << "\n";); enode *next = root->get_next(); while (next != root) { TRACE("seq", tout << "eqc: " << mk_pp(next->get_owner(), m) << "\n";); len_offset(next->get_owner(), val); next = next->get_next(); } } } } int theory_seq::find_fst_non_empty_idx(expr_ref_vector const& xs) { context & ctx = get_context(); for (unsigned i = 0; i < xs.size(); ++i) { expr* x = xs[i]; if (!is_var(x)) return -1; expr_ref e = mk_len(x); if (ctx.e_internalized(e)) { enode* root = ctx.get_enode(e)->get_root(); rational val; if (m_autil.is_numeral(root->get_owner(), val) && val.is_zero()) { continue; } } return i; } return -1; } expr* theory_seq::find_fst_non_empty_var(expr_ref_vector const& x) { int i = find_fst_non_empty_idx(x); if (i >= 0) return x[i]; return nullptr; } void theory_seq::find_max_eq_len(expr_ref_vector const& ls, expr_ref_vector const& rs) { context& ctx = get_context(); if (ls.size() >= 2 && rs.size() >= 2) { expr_ref len1(m_autil.mk_int(0), m), len2(m_autil.mk_int(0), m); int l_fst = find_fst_non_empty_idx(ls); int r_fst = find_fst_non_empty_idx(rs); if (l_fst<0 || r_fst<0) return; unsigned j = 2 + l_fst; rational lo1(-1), hi1(-1), lo2(-1), hi2(-1); if (j >= ls.size()) { lo1 = 0; hi1 = 0; } while (j < ls.size()) { rational lo(-1), hi(-1); if (m_util.str.is_unit(ls.get(j))) { lo = 1; hi = 1; } else { 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()) lo1 = lo; else lo1 += lo; } if (!hi.is_minus_one()) { if (hi1.is_minus_one()) hi1 = hi; else if (hi1.is_nonneg()) hi1 += hi; } else { hi1 = rational(-2); } len1 = mk_add(len1, mk_len(ls.get(j))); j++; } j = 2 + r_fst; if (j >= rs.size()) { lo2 = 0; hi2 = 0; } while (j < rs.size()) { rational lo(-1), hi(-1); if (m_util.str.is_unit(rs.get(j))) { lo = 1; hi = 1; } else { 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()) lo2 = lo; else lo2 += lo; } if (!hi.is_minus_one()) { if (hi2.is_minus_one()) hi2 = hi; else if (hi1.is_nonneg()) hi2 += hi; } else { hi2 = rational(-2); } len2 = mk_add(len2, mk_len(rs.get(j))); j++; } if (m_autil.is_numeral(len1) && m_autil.is_numeral(len2)) return; TRACE("seq", tout << lo1 << ", " << hi1 << ", " << lo2 << ", " << hi2 << "\n";); if (!lo1.is_neg() && !hi2.is_neg() && lo1 > hi2) return; if (!lo2.is_neg() && !hi1.is_neg() && lo2 > hi1) return; literal lit1 = null_literal; if (hi1.is_zero()) { if (!is_var(rs[1 + r_fst])) return; lit1 = mk_literal(m_autil.mk_le(len2, len1)); TRACE("seq", tout << mk_pp(len1, m) << " >= " << mk_pp(len2, m) << "\n";); } else if (hi2.is_zero()) { if (!is_var(ls[1 + l_fst])) return; lit1 = mk_literal(m_autil.mk_le(len1, len2)); TRACE("seq", tout << mk_pp(len1, m) << " <= " << mk_pp(len2, m) << "\n";); } else { lit1 = mk_eq(len1, len2, false); TRACE("seq", tout << mk_pp(len1, m) << " = " << mk_pp(len2, m) << "\n";); } if (ctx.get_assignment(lit1) == l_undef) { ctx.mark_as_relevant(lit1); ctx.force_phase(lit1); } } } // TODO: propagate length offsets for last vars bool theory_seq::find_better_rep(expr_ref_vector const& ls, expr_ref_vector const& rs, unsigned idx, dependency*& deps, expr_ref_vector & res) { context& ctx = get_context(); if (ls.empty() || rs.empty()) return false; expr* l_fst = find_fst_non_empty_var(ls); 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)) { return false; } if (l_fst) { len_l_fst = mk_len(l_fst); } root2 = get_root(len_r_fst); // Offset = 0, No change 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() != 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 && 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; } } } // Offset != 0, Changed obj_map tmp; 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() != 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; } } } } } return false; } bool theory_seq::has_len_offset(expr_ref_vector const& ls, expr_ref_vector const& rs, int & offset) { context& ctx = get_context(); 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); if (!ctx.e_internalized(len_r_fst)) return false; enode* root2 = ctx.get_enode(len_r_fst)->get_root(); 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; } bool theory_seq::len_based_split() { unsigned sz = m_eqs.size(); if (sz == 0) return false; if ((int) get_context().get_scope_level() > m_len_prop_lvl) { m_len_prop_lvl = get_context().get_scope_level(); prop_arith_to_len_offset(); if (!m_len_offset.empty()) { for (unsigned i = sz-1; i > 0; --i) { eq const& e = m_eqs[i]; expr_ref_vector new_ls(m); dependency *deps = e.dep(); if (find_better_rep(e.ls(), e.rs(), i, deps, new_ls)) { expr_ref_vector rs(m); rs.append(e.rs().size(), e.rs().c_ptr()); m_eqs.set(i, eq(m_eq_id++, new_ls, rs, deps)); TRACE("seq", display_equation(tout, m_eqs[i]);); } } } } for (auto const& e : m_eqs) { if (len_based_split(e)) { return true; } } return false; } bool theory_seq::len_based_split(eq const& e) { context& ctx = get_context(); expr_ref_vector const& ls = e.ls(); expr_ref_vector const& rs = e.rs(); int offset_orig = 0; if (!has_len_offset(ls, rs, offset_orig)) return false; TRACE("seq", tout << "split based on length\n";); TRACE("seq", display_equation(tout, e);); expr_ref x11(m_util.str.mk_concat(1, ls.c_ptr()), m); expr_ref x12(m_util.str.mk_concat(ls.size()-1, ls.c_ptr()+1), m); expr_ref y11(m_util.str.mk_concat(1, rs.c_ptr()), m); expr_ref y12(m_util.str.mk_concat(rs.size()-1, rs.c_ptr()+1), m); expr_ref lenX11 = mk_len(x11); expr_ref lenY11(m); expr_ref Z(m); int offset = 0; if (offset_orig != 0) { lenY11 = m_autil.mk_add(mk_len(y11), m_autil.mk_int(offset_orig)); if (offset_orig > 0) { offset = offset_orig; Z = mk_skolem(m_seq_align, y12, x12, x11, y11); y11 = mk_concat(y11, Z); x12 = mk_concat(Z, x12); } else { offset = -offset_orig; Z = mk_skolem(m_seq_align, x12, y12, y11, x11); x11 = mk_concat(x11, Z); y12 = mk_concat(Z, y12); } } else { lenY11 = mk_len(y11); } dependency* dep = e.dep(); literal_vector lits; literal lit1 = mk_eq(lenX11, lenY11, false); if (ctx.get_assignment(lit1) != l_true) { return false; } lits.push_back(lit1); if (ls.size() >= 2 && rs.size() >= 2 && (ls.size() > 2 || rs.size() > 2)) { expr_ref len1(m_autil.mk_int(0),m), len2(m_autil.mk_int(0),m); for (unsigned i = 2; i < ls.size(); ++i) { len1 = mk_add(len1, mk_len(ls[i])); } for (unsigned i = 2; i < rs.size(); ++i) { len2 = mk_add(len2, mk_len(rs[i])); } literal lit2; if (!m_autil.is_numeral(len1) && !m_autil.is_numeral(len2)) { lit2 = mk_eq(len1, len2, false); } else { expr_ref eq_len(m.mk_eq(len1, len2), m); lit2 = mk_literal(eq_len); } if (ctx.get_assignment(lit2) == l_true) { lits.push_back(lit2); TRACE("seq", tout << mk_pp(len1, m) << " = " << mk_pp(len2, m) << "\n";); expr_ref lhs(m), rhs(m); if (ls.size() > 2) lhs = m_util.str.mk_concat(ls.size()-2, ls.c_ptr()+2); else lhs = m_util.str.mk_empty(m.get_sort(x11)); if (rs.size() > 2) rhs = m_util.str.mk_concat(rs.size()-2, rs.c_ptr()+2); else rhs = m_util.str.mk_empty(m.get_sort(x11)); propagate_eq(dep, lits, lhs, rhs, true); lits.pop_back(); } } if (offset != 0) { expr_ref lenZ = mk_len(Z); propagate_eq(dep, lits, lenZ, m_autil.mk_int(offset), false); } propagate_eq(dep, lits, y11, x11, true); propagate_eq(dep, lits, x12, y12, false); 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) { vector len1, len2; if (!is_complex(e)) { continue; } if (e.ls().empty() || e.rs().empty() || (!is_var(e.ls()[0]) && !is_var(e.rs()[0]))) { continue; } if (!enforce_length(e.ls(), len1) || !enforce_length(e.rs(), len2)) { change = true; continue; } rational l1, l2; for (const auto& elem : len1) l1 += elem; for (const auto& elem : len2) l2 += elem; if (l1 != l2) { expr_ref l = mk_concat(e.ls()); expr_ref r = mk_concat(e.rs()); expr_ref lnl = mk_len(l), lnr = mk_len(r); if (propagate_eq(e.dep(), lnl, lnr, false)) { change = true; } continue; } if (split_lengths(e.dep(), e.ls(), e.rs(), len1, len2)) { TRACE("seq", tout << "split lengths\n";); change = true; break; } } return change; } bool theory_seq::is_complex(eq const& e) { unsigned num_vars1 = 0, num_vars2 = 0; for (auto const& elem : e.ls()) { if (is_var(elem)) ++num_vars1; } for (auto const& elem : e.rs()) { if (is_var(elem)) ++num_vars2; } return num_vars1 > 0 && num_vars2 > 0 && num_vars1 + num_vars2 > 2; } /* \brief Decompose ls = rs into Xa = bYc, such that 1. - X != Y - |b| <= |X| <= |bY| in current model - b is non-empty. 2. X != Y - b is empty - |X| <= |Y| 3. |X| = 0 - propagate X = empty */ bool theory_seq::split_lengths(dependency* dep, expr_ref_vector const& ls, expr_ref_vector const& rs, vector const& ll, vector const& rl) { context& ctx = get_context(); expr_ref X(m), Y(m), b(m); if (ls.empty() || rs.empty()) { return false; } if (is_var(ls[0]) && ll[0].is_zero()) { return set_empty(ls[0]); } if (is_var(rs[0]) && rl[0].is_zero()) { return set_empty(rs[0]); } if (is_var(rs[0]) && !is_var(ls[0])) { return split_lengths(dep, rs, ls, rl, ll); } if (!is_var(ls[0])) { return false; } X = ls[0]; rational lenX = ll[0]; expr_ref_vector bs(m); SASSERT(lenX.is_pos()); rational lenB(0), lenY(0); for (unsigned i = 0; lenX > lenB && i < rs.size(); ++i) { bs.push_back(rs[i]); lenY = rl[i]; lenB += lenY; } SASSERT(lenX <= lenB); SASSERT(!bs.empty()); Y = bs.back(); bs.pop_back(); if (!is_var(Y) && !m_util.str.is_unit(Y)) { TRACE("seq", tout << "TBD: non variable or unit split: " << Y << "\n";); return false; } if (X == Y) { TRACE("seq", tout << "Cycle: " << X << "\n";); return false; } if (lenY.is_zero()) { return set_empty(Y); } b = mk_concat(bs, m.get_sort(X)); SASSERT(X != Y); // |b| < |X| <= |b| + |Y| => x = bY1, Y = Y1Y2 expr_ref lenXE = mk_len(X); expr_ref lenYE = mk_len(Y); expr_ref lenb = mk_len(b); expr_ref le1(m_autil.mk_le(mk_sub(lenXE, lenb), m_autil.mk_int(0)), m); expr_ref le2(m_autil.mk_le(mk_sub(mk_sub(lenXE, lenb), lenYE), m_autil.mk_int(0)), m); literal lit1(~mk_literal(le1)); literal lit2(mk_literal(le2)); literal_vector lits; lits.push_back(lit1); lits.push_back(lit2); if (ctx.get_assignment(lit1) != l_true || ctx.get_assignment(lit2) != l_true) { ctx.mark_as_relevant(lit1); ctx.mark_as_relevant(lit2); } else if (m_util.str.is_unit(Y)) { SASSERT(lenB == lenX); bs.push_back(Y); expr_ref bY(m_util.str.mk_concat(bs), m); propagate_eq(dep, lits, X, bY, true); } else { SASSERT(is_var(Y)); expr_ref Y1(mk_skolem(symbol("seq.left"), X, b, Y), m); expr_ref Y2(mk_skolem(symbol("seq.right"), X, b, Y), m); expr_ref bY1 = mk_concat(b, Y1); expr_ref Y1Y2 = mk_concat(Y1, Y2); propagate_eq(dep, lits, X, bY1, true); propagate_eq(dep, lits, Y, Y1Y2, true); } return true; } bool theory_seq::set_empty(expr* x) { add_axiom(~mk_eq(m_autil.mk_int(0), mk_len(x), false), mk_eq_empty(x)); return true; } bool theory_seq::enforce_length(expr_ref_vector const& es, vector & len) { bool all_have_length = true; rational val; zstring s; for (expr* e : es) { if (m_util.str.is_unit(e)) { len.push_back(rational(1)); } else if (m_util.str.is_empty(e)) { len.push_back(rational(0)); } else if (m_util.str.is_string(e, s)) { len.push_back(rational(s.length())); } else if (get_length(e, val)) { len.push_back(val); } else { add_length_to_eqc(e); all_have_length = false; } } return all_have_length; } bool theory_seq::branch_variable_eq() { context& ctx = get_context(); unsigned sz = m_eqs.size(); int start = ctx.get_random_value(); for (unsigned i = 0; i < sz; ++i) { unsigned k = (i + start) % sz; eq const& e = m_eqs[k]; if (branch_variable_eq(e)) { TRACE("seq", tout << "branch variable\n";); return true; } } return ctx.inconsistent(); } 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";); bool found = find_branch_candidate(s, e.dep(), e.ls(), e.rs()); insert_branch_start(2*id, s); if (found) { return true; } s = find_branch_start(2*id + 1); found = find_branch_candidate(s, e.dep(), e.rs(), e.ls()); insert_branch_start(2*id + 1, s); return found; } void theory_seq::insert_branch_start(unsigned k, unsigned s) { m_branch_start.insert(k, s); m_trail_stack.push(pop_branch(k)); } unsigned theory_seq::find_branch_start(unsigned k) { unsigned s = 0; if (m_branch_start.find(k, s)) { return s; } return 0; } expr_ref_vector theory_seq::expand_strings(expr_ref_vector const& es) { expr_ref_vector ls(m); for (expr* e : es) { zstring s; if (m_util.str.is_string(e, s)) { for (unsigned i = 0; i < s.length(); ++i) { ls.push_back(m_util.str.mk_unit(m_util.str.mk_char(s, i))); } } else { ls.push_back(e); } } return ls; } bool theory_seq::find_branch_candidate(unsigned& start, dependency* dep, expr_ref_vector const& _ls, expr_ref_vector const& _rs) { expr_ref_vector ls = expand_strings(_ls); expr_ref_vector rs = expand_strings(_rs); if (ls.empty()) { return false; } expr* l = ls.get(0); if (!is_var(l)) { return false; } TRACE("seq", tout << mk_pp(l, m) << ": " << get_context().get_scope_level() << " - start:" << start << "\n";); expr_ref v0(m); v0 = m_util.str.mk_empty(m.get_sort(l)); if (can_be_equal(ls.size() - 1, ls.c_ptr() + 1, rs.size(), rs.c_ptr())) { if (l_false != assume_equality(l, v0)) { TRACE("seq", tout << mk_pp(l, m) << " " << v0 << "\n";); return true; } } for (; start < rs.size(); ++start) { unsigned j = start; SASSERT(!m_util.str.is_concat(rs.get(j))); SASSERT(!m_util.str.is_string(rs.get(j))); if (l == rs.get(j)) { return false; } if (!can_be_equal(ls.size() - 1, ls.c_ptr() + 1, rs.size() - j - 1, rs.c_ptr() + j + 1)) { continue; } v0 = mk_concat(j + 1, rs.c_ptr()); if (l_false != assume_equality(l, v0)) { TRACE("seq", tout << mk_pp(l, m) << " " << v0 << "\n";); ++start; return true; } } bool all_units = true; for (expr* r : rs) { if (!m_util.str.is_unit(r)) { all_units = false; break; } } if (all_units) { context& ctx = get_context(); literal_vector lits; lits.push_back(~mk_eq_empty(l)); for (unsigned i = 0; i < rs.size(); ++i) { if (can_be_equal(ls.size() - 1, ls.c_ptr() + 1, rs.size() - i - 1, rs.c_ptr() + i + 1)) { v0 = mk_concat(i + 1, rs.c_ptr()); lits.push_back(~mk_eq(l, v0, false)); } } for (literal lit : lits) { switch (ctx.get_assignment(lit)) { case l_true: break; case l_false: start = 0; return true; case l_undef: ctx.force_phase(~lit); start = 0; return true; } } set_conflict(dep, lits); TRACE("seq", tout << "start: " << start << "\n"; for (literal lit : lits) { ctx.display_literal_verbose(tout << lit << ": ", lit) << "\n"; ctx.display(tout, ctx.get_justification(lit.var())); tout << "\n"; }); return true; } return false; } bool theory_seq::can_be_equal(unsigned szl, expr* const* ls, unsigned szr, expr* const* rs) const { unsigned i = 0; for (; i < szl && i < szr; ++i) { if (m.are_distinct(ls[i], rs[i])) { return false; } if (!m.are_equal(ls[i], rs[i])) { break; } } if (i == szr) { std::swap(ls, rs); std::swap(szl, szr); } if (i == szl && i < szr) { for (; i < szr; ++i) { if (m_util.str.is_unit(rs[i])) { return false; } } } return true; } lbool theory_seq::assume_equality(expr* l, expr* r) { context& ctx = get_context(); if (m_exclude.contains(l, r)) { return l_false; } expr_ref eq(m.mk_eq(l, r), m); m_rewrite(eq); if (m.is_true(eq)) { return l_true; } if (m.is_false(eq)) { return l_false; } TRACE("seq", tout << mk_pp(l, m) << " = " << mk_pp(r, m) << "\n";); enode* n1 = ensure_enode(l); enode* n2 = ensure_enode(r); if (n1->get_root() == n2->get_root()) { return l_true; } if (ctx.is_diseq(n1, n2)) { return l_false; } if (false && ctx.is_diseq_slow(n1, n2)) { return l_false; } ctx.mark_as_relevant(n1); ctx.mark_as_relevant(n2); if (!ctx.assume_eq(n1, n2)) { return l_false; } return ctx.get_assignment(mk_eq(l, r, false)); } bool theory_seq::propagate_length_coherence(expr* e) { expr_ref head(m), tail(m); rational lo, hi; if (!is_var(e) || !m_rep.is_root(e)) { return false; } if (!lower_bound2(e, lo) || !lo.is_pos() || lo >= rational(2048)) { return false; } TRACE("seq", tout << "Unsolved " << mk_pp(e, m); if (!lower_bound2(e, lo)) lo = -rational::one(); if (!upper_bound(mk_len(e), hi)) hi = -rational::one(); tout << " lo: " << lo << " hi: " << hi << "\n"; ); expr_ref seq(e, m); expr_ref_vector elems(m); unsigned _lo = lo.get_unsigned(); for (unsigned j = 0; j < _lo; ++j) { mk_decompose(seq, head, tail); elems.push_back(head); seq = tail; } expr_ref emp(m_util.str.mk_empty(m.get_sort(e)), m); elems.push_back(seq); tail = mk_concat(elems.size(), elems.c_ptr()); // 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)); 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(len_e, m_autil.mk_numeral(hi, true)), m); if (hi == lo) { add_axiom(~mk_literal(high1), mk_seq_eq(seq, emp)); } else { expr_ref high2(m_autil.mk_le(mk_len(seq), m_autil.mk_numeral(hi-lo, true)), m); add_axiom(~mk_literal(high1), mk_literal(high2)); } } else { assume_equality(seq, emp); } return true; } bool theory_seq::check_length_coherence(expr* e) { if (is_var(e) && m_rep.is_root(e)) { if (!check_length_coherence0(e)) { expr_ref emp(m_util.str.mk_empty(m.get_sort(e)), m); expr_ref head(m), tail(m); // e = emp \/ e = unit(head.elem(e))*tail(e) mk_decompose(e, head, tail); expr_ref conc = mk_concat(head, tail); if (propagate_is_conc(e, conc)) { assume_equality(tail, emp); } } return true; } return false; } bool theory_seq::check_length_coherence0(expr* e) { if (is_var(e) && m_rep.is_root(e)) { expr_ref emp(m_util.str.mk_empty(m.get_sort(e)), m); if (propagate_length_coherence(e) || l_false != assume_equality(e, emp)) { if (!get_context().at_base_level()) { m_trail_stack.push(push_replay(alloc(replay_length_coherence, m, e))); } return true; } } return false; } bool theory_seq::check_length_coherence() { for (expr* l : m_length) { expr* e = nullptr; VERIFY(m_util.str.is_length(l, e)); if (check_length_coherence0(e)) { return true; } } bool change = false; unsigned sz = m_length.size(); for (unsigned i = 0; i < sz; ++i) { expr* l = m_length.get(i); expr* e = nullptr; VERIFY(m_util.str.is_length(l, e)); if (check_length_coherence(e)) { return true; } enode* n = ensure_enode(e); enode* r = n->get_root(); if (r != n && has_length(r->get_owner())) { continue; } if (add_length_to_eqc(e)) { change = true; } } return change; } bool theory_seq::fixed_length(bool is_zero) { bool found = false; for (auto e : m_length) { if (fixed_length(e, is_zero)) { found = true; } } return found; } bool theory_seq::fixed_length(expr* len_e, bool is_zero) { rational 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; } if (is_skolem(m_tail, e) || is_skolem(m_seq_first, e) || is_skolem(m_indexof_left, e) || is_skolem(m_indexof_right, e) || m_fixed.contains(e)) { return false; } context& ctx = get_context(); m_trail_stack.push(insert_obj_trail(m_fixed, e)); m_fixed.insert(e); expr_ref seq(e, m), head(m), tail(m); if (lo.is_zero()) { seq = m_util.str.mk_empty(m.get_sort(e)); } else if (!is_zero) { unsigned _lo = lo.get_unsigned(); expr_ref_vector elems(m); for (unsigned j = 0; j < _lo; ++j) { mk_decompose(seq, head, tail); elems.push_back(head); seq = tail; } seq = mk_concat(elems.size(), elems.c_ptr()); } TRACE("seq", tout << "Fixed: " << mk_bounded_pp(e, m, 2) << " " << lo << "\n";); 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, len_e))); } return true; } /* lit => s != "" */ void theory_seq::propagate_non_empty(literal lit, expr* s) { SASSERT(get_context().get_assignment(lit) == l_true); propagate_lit(nullptr, 1, &lit, ~mk_eq_empty(s)); } bool theory_seq::propagate_is_conc(expr* e, expr* conc) { TRACE("seq", tout << mk_pp(conc, m) << " is non-empty\n";); context& ctx = get_context(); literal lit = ~mk_eq_empty(e); if (ctx.get_assignment(lit) == l_true) { propagate_lit(nullptr, 1, &lit, mk_eq(e, conc, false)); expr_ref e1(e, m), e2(conc, m); new_eq_eh(m_dm.mk_leaf(assumption(lit)), ctx.get_enode(e1), ctx.get_enode(e2)); return true; } else { return false; } } bool theory_seq::is_unit_nth(expr* e) const { expr *s = nullptr; return m_util.str.is_unit(e, s) && m_util.str.is_nth_i(s); } bool theory_seq::is_tail(expr* e, expr*& s, unsigned& idx) const { rational r; return is_skolem(m_tail, e) && m_autil.is_numeral(to_app(e)->get_arg(1), r) && (idx = r.get_unsigned(), s = to_app(e)->get_arg(0), true); } bool theory_seq::is_eq(expr* e, expr*& a, expr*& b) const { return is_skolem(m_eq, e) && (a = to_app(e)->get_arg(0), b = to_app(e)->get_arg(1), true); } bool theory_seq::is_pre(expr* e, expr*& s, expr*& i) { return is_skolem(m_pre, e) && (s = to_app(e)->get_arg(0), i = to_app(e)->get_arg(1), true); } bool theory_seq::is_post(expr* e, expr*& s, expr*& i) { return is_skolem(m_post, e) && (s = to_app(e)->get_arg(0), i = to_app(e)->get_arg(1), true); } expr_ref theory_seq::mk_nth(expr* s, expr* idx) { expr_ref result(m_util.str.mk_nth_i(s, idx), m); return result; } expr_ref theory_seq::mk_sk_ite(expr* c, expr* t, expr* e) { return mk_skolem(symbol("seq.if"), c, t, e, nullptr, m.get_sort(t)); } expr_ref theory_seq::mk_last(expr* s) { zstring str; if (m_util.str.is_string(s, str) && str.length() > 0) { return expr_ref(m_util.str.mk_char(str, str.length()-1), m); } sort* char_sort = nullptr; VERIFY(m_util.is_seq(m.get_sort(s), char_sort)); return mk_skolem(m_seq_last, s, nullptr, nullptr, nullptr, char_sort); } expr_ref theory_seq::mk_first(expr* s) { zstring str; if (m_util.str.is_string(s, str) && str.length() > 0) { return expr_ref(m_util.str.mk_string(str.extract(0, str.length()-1)), m); } return mk_skolem(m_seq_first, s); } void theory_seq::mk_decompose(expr* e, expr_ref& head, expr_ref& tail) { expr* e1 = nullptr, *e2 = nullptr; zstring s; rational r; if (m_util.str.is_empty(e)) { head = m_util.str.mk_unit(mk_nth(e, m_autil.mk_int(0))); tail = e; } else if (m_util.str.is_string(e, s)) { head = m_util.str.mk_unit(m_util.str.mk_char(s, 0)); tail = m_util.str.mk_string(s.extract(1, s.length()-1)); } else if (m_util.str.is_unit(e)) { head = e; tail = m_util.str.mk_empty(m.get_sort(e)); } else if (m_util.str.is_concat(e, e1, e2) && m_util.str.is_unit(e1)) { head = e1; tail = e2; } else if (is_skolem(m_tail, e) && m_autil.is_numeral(to_app(e)->get_arg(1), r)) { app* a = to_app(e); expr* s = a->get_arg(0); expr* idx = m_autil.mk_int(r.get_unsigned() + 1); head = m_util.str.mk_unit(mk_nth(s, idx)); tail = mk_skolem(m_tail, s, idx); } else { head = m_util.str.mk_unit(mk_nth(e, m_autil.mk_int(0))); tail = mk_skolem(m_tail, e, m_autil.mk_int(0)); } } /* \brief Check extensionality (for sequences). */ bool theory_seq::check_extensionality() { context& ctx = get_context(); unsigned sz = get_num_vars(); unsigned_vector seqs; for (unsigned v = 0; v < sz; ++v) { enode* n1 = get_enode(v); expr* o1 = n1->get_owner(); if (n1 != n1->get_root()) { continue; } if (!seqs.empty() && ctx.is_relevant(n1) && m_util.is_seq(o1) && ctx.is_shared(n1)) { dependency* dep = nullptr; expr_ref e1(m); if (!canonize(o1, dep, e1)) { return false; } for (theory_var v : seqs) { enode* n2 = get_enode(v); expr* o2 = n2->get_owner(); if (m.get_sort(o1) != m.get_sort(o2)) { continue; } if (ctx.is_diseq(n1, n2) || m_exclude.contains(o1, o2)) { continue; } expr_ref e2(m); if (!canonize(n2->get_owner(), dep, e2)) { return false; } m_lhs.reset(); m_rhs.reset(); bool change = false; if (!m_seq_rewrite.reduce_eq(e1, e2, m_lhs, m_rhs, change)) { TRACE("seq", tout << "exclude " << mk_pp(o1, m) << " " << mk_pp(o2, m) << "\n";); m_exclude.update(o1, o2); continue; } bool excluded = false; for (unsigned j = 0; !excluded && j < m_lhs.size(); ++j) { if (m_exclude.contains(m_lhs.get(j), m_rhs.get(j))) { TRACE("seq", tout << "excluded " << j << " " << m_lhs << " " << m_rhs << "\n";); excluded = true; } } if (excluded) { continue; } TRACE("seq", tout << m_lhs << " = " << m_rhs << "\n";); ctx.assume_eq(n1, n2); return false; } } seqs.push_back(v); } return true; } /* \brief check negated contains constraints. */ bool theory_seq::check_contains() { context & ctx = get_context(); for (unsigned i = 0; !ctx.inconsistent() && i < m_ncs.size(); ++i) { if (solve_nc(i)) { if (i + 1 != m_ncs.size()) { nc n = m_ncs[m_ncs.size()-1]; m_ncs.set(i, n); --i; } m_ncs.pop_back(); } } return m_new_propagation || ctx.inconsistent(); } bool theory_seq::check_lts() { context& ctx = get_context(); if (m_lts.empty() || m_lts_checked) { return false; } unsigned sz = m_lts.size(); m_trail_stack.push(value_trail(m_lts_checked)); m_lts_checked = true; expr* a = nullptr, *b = nullptr, *c = nullptr, *d = nullptr; bool is_strict1, is_strict2; for (unsigned i = 0; i + 1 < sz; ++i) { expr* p1 = m_lts[i]; VERIFY(m_util.str.is_lt(p1, a, b) || m_util.str.is_le(p1, a, b)); literal r1 = ctx.get_literal(p1); if (ctx.get_assignment(r1) == l_false) { std::swap(a, b); r1.neg(); is_strict1 = m_util.str.is_le(p1); } else { is_strict1 = m_util.str.is_lt(p1); } for (unsigned j = i + 1; j < sz; ++j) { expr* p2 = m_lts[j]; VERIFY(m_util.str.is_lt(p2, c, d) || m_util.str.is_le(p2, c, d)); literal r2 = ctx.get_literal(p2); if (ctx.get_assignment(r2) == l_false) { std::swap(c, d); r2.neg(); is_strict2 = m_util.str.is_le(p2); } else { is_strict2 = m_util.str.is_lt(p2); } if (ctx.get_enode(b)->get_root() == ctx.get_enode(c)->get_root()) { literal eq = (b == c) ? true_literal : mk_eq(b, c, false); bool is_strict = is_strict1 || is_strict2; if (is_strict) { add_axiom(~r1, ~r2, ~eq, mk_literal(m_util.str.mk_lex_lt(a, d))); } else { add_axiom(~r1, ~r2, ~eq, mk_literal(m_util.str.mk_lex_le(a, d))); } } } } return true; } /* - Eqs = 0 - Diseqs evaluate to false - lengths are coherent. */ bool theory_seq::is_solved() { if (!m_eqs.empty()) { TRACE("seq", tout << "(seq.giveup " << m_eqs[0].ls() << " = " << m_eqs[0].rs() << " is unsolved)\n";); IF_VERBOSE(10, verbose_stream() << "(seq.giveup " << m_eqs[0].ls() << " = " << m_eqs[0].rs() << " is unsolved)\n";); return false; } for (unsigned i = 0; i < m_automata.size(); ++i) { if (!m_automata[i]) { TRACE("seq", tout << "(seq.giveup regular expression did not compile to automaton)\n";); IF_VERBOSE(10, verbose_stream() << "(seq.giveup regular expression did not compile to automaton)\n";); return false; } } if (!m_ncs.empty()) { TRACE("seq", display_nc(tout << "(seq.giveup ", m_ncs[0]); tout << " is unsolved)\n";); IF_VERBOSE(10, display_nc(verbose_stream() << "(seq.giveup ", m_ncs[0]); verbose_stream() << " is unsolved)\n";); return false; } #if 0 // debug code context& ctx = get_context(); for (enode* n : ctx.enodes()) { expr* e = nullptr; rational len1, len2; if (m_util.str.is_length(n->get_owner(), e)) { VERIFY(get_length(e, len1)); dependency* dep = nullptr; expr_ref r = canonize(e, dep); if (get_length(r, len2)) { SASSERT(len1 == len2); } else { IF_VERBOSE(0, verbose_stream() << r << "does not have a length\n"); } } } #endif return true; } /** \brief while extracting dependency literals ensure that they have all been asserted on the context. */ bool theory_seq::linearize(dependency* dep, enode_pair_vector& eqs, literal_vector& lits) const { context & ctx = get_context(); DEBUG_CODE(for (literal lit : lits) SASSERT(ctx.get_assignment(lit) == l_true); ); bool asserted = true; svector assumptions; const_cast(m_dm).linearize(dep, assumptions); for (assumption const& a : assumptions) { if (a.lit != null_literal) { lits.push_back(a.lit); asserted &= ctx.get_assignment(a.lit) == l_true; } if (a.n1 != nullptr) { eqs.push_back(enode_pair(a.n1, a.n2)); } } if (!asserted) { IF_VERBOSE(0, verbose_stream() << "not asserted\n";); context& ctx = get_context(); for (assumption const& a : assumptions) { if (a.lit != null_literal) { IF_VERBOSE(0, verbose_stream() << a.lit << " " << ctx.get_assignment(a.lit) << " "; ctx.display_literal_info(verbose_stream(), a.lit); ctx.display_detailed_literal(verbose_stream(), a.lit) << "\n";); } } exit(0); } return asserted; } void theory_seq::propagate_lit(dependency* dep, unsigned n, literal const* _lits, literal lit) { if (lit == true_literal) return; context& ctx = get_context(); literal_vector lits(n, _lits); if (lit == false_literal) { set_conflict(dep, lits); return; } ctx.mark_as_relevant(lit); enode_pair_vector eqs; if (!linearize(dep, eqs, lits)) return; TRACE("seq", tout << "scope: " << ctx.get_scope_level() << "\n"; tout << lits << "\n"; ctx.display_detailed_literal(tout << "assert:", lit); ctx.display_literals_verbose(tout << " <- ", lits); if (!lits.empty()) tout << "\n"; display_deps(tout, dep);); 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)); m_new_propagation = true; ctx.assign(lit, js); std::function fn = [&]() { expr* r = ctx.bool_var2expr(lit.var()); if (lit.sign()) r = m.mk_not(r); return r; }; scoped_trace_stream _sts(*this, fn); } void theory_seq::set_conflict(dependency* dep, literal_vector const& _lits) { context& ctx = get_context(); enode_pair_vector eqs; literal_vector lits(_lits); if (!linearize(dep, eqs, lits)) return; TRACE("seq", display_deps(tout << "assert conflict:", lits, eqs);); m_new_propagation = true; 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))); } bool theory_seq::propagate_eq(dependency* dep, enode* n1, enode* n2) { if (n1->get_root() == n2->get_root()) { return false; } context& ctx = get_context(); literal_vector lits; enode_pair_vector eqs; if (!linearize(dep, eqs, lits)) { TRACE("seq", tout << "not linearized\n";); return false; } TRACE("seq_verbose", tout << "assert: " << mk_bounded_pp(n1->get_owner(), m) << " = " << mk_bounded_pp(n2->get_owner(), m) << " <-\n"; display_deps(tout, dep); ); TRACE("seq", tout << "assert: " << mk_bounded_pp(n1->get_owner(), m) << " = " << mk_bounded_pp(n2->get_owner(), m) << " <-\n" << lits << "\n"; ); justification* js = ctx.mk_justification( ext_theory_eq_propagation_justification( get_id(), ctx.get_region(), lits.size(), lits.c_ptr(), eqs.size(), eqs.c_ptr(), n1, n2)); { std::function fn = [&]() { return m.mk_eq(n1->get_owner(), n2->get_owner()); }; scoped_trace_stream _sts(*this, fn); ctx.assign_eq(n1, n2, eq_justification(js)); } m_new_propagation = true; enforce_length_coherence(n1, n2); return true; } bool theory_seq::propagate_eq(dependency* dep, expr* e1, expr* e2, bool add_eq) { literal_vector lits; return propagate_eq(dep, lits, e1, e2, add_eq); } bool theory_seq::propagate_eq(dependency* dep, literal lit, expr* e1, expr* e2, bool add_to_eqs) { literal_vector lits; lits.push_back(lit); return propagate_eq(dep, lits, e1, e2, add_to_eqs); } void theory_seq::enforce_length_coherence(enode* n1, enode* n2) { expr* o1 = n1->get_owner(); expr* o2 = n2->get_owner(); if (m_util.str.is_concat(o1) && m_util.str.is_concat(o2)) { return; } if (has_length(o1) && !has_length(o2)) { add_length_to_eqc(o2); } else if (has_length(o2) && !has_length(o1)) { add_length_to_eqc(o1); } } bool theory_seq::lift_ite(expr_ref_vector const& ls, expr_ref_vector const& rs, dependency* deps) { if (ls.size() != 1 || rs.size() != 1) { return false; } context& ctx = get_context(); expr* c = nullptr, *t = nullptr, *e = nullptr; expr* l = ls[0], *r = rs[0]; if (m.is_ite(r)) { std::swap(l, r); } if (!m.is_ite(l, c, t, e)) { return false; } switch (ctx.find_assignment(c)) { case l_undef: return false; case l_true: deps = mk_join(deps, ctx.get_literal(c)); m_eqs.push_back(mk_eqdep(t, r, deps)); return true; case l_false: deps = mk_join(deps, ~ctx.get_literal(c)); m_eqs.push_back(mk_eqdep(e, r, deps)); return true; } return false; } bool theory_seq::simplify_eq(expr_ref_vector& ls, expr_ref_vector& rs, dependency* deps) { context& ctx = get_context(); expr_ref_vector lhs(m), rhs(m); bool changed = false; TRACE("seq", for (expr* l : ls) tout << "s#" << l->get_id() << " " << mk_bounded_pp(l, m, 2) << "\n"; tout << " = \n"; for (expr* r : rs) tout << "s#" << r->get_id() << " " << mk_bounded_pp(r, m, 2) << "\n";); if (!m_seq_rewrite.reduce_eq(ls, rs, lhs, rhs, changed)) { // equality is inconsistent. TRACE("seq_verbose", tout << ls << " != " << rs << "\n";); set_conflict(deps); return true; } if (!changed) { SASSERT(lhs.empty() && rhs.empty()); return false; } TRACE("seq", tout << "reduced to\n"; for (expr* l : lhs) tout << mk_bounded_pp(l, m, 2) << "\n"; tout << " = \n"; for (expr* r : rhs) tout << mk_bounded_pp(r, m, 2) << "\n"; ); SASSERT(lhs.size() == rhs.size()); m_seq_rewrite.add_seqs(ls, rs, lhs, rhs); if (lhs.empty()) { TRACE("seq", tout << "solved\n";); return true; } TRACE("seq_verbose", tout << ls << " = " << rs << "\n"; tout << lhs << " = " << rhs << "\n";); for (unsigned i = 0; !ctx.inconsistent() && i < lhs.size(); ++i) { expr_ref li(lhs.get(i), m); expr_ref ri(rhs.get(i), m); if (solve_unit_eq(li, ri, deps)) { // no-op } else if (m_util.is_seq(li) || m_util.is_re(li)) { TRACE("seq_verbose", tout << "inserting " << li << " = " << ri << "\n";); m_eqs.push_back(mk_eqdep(li, ri, deps)); } else { propagate_eq(deps, ensure_enode(li), ensure_enode(ri)); } } TRACE("seq_verbose", if (!ls.empty() || !rs.empty()) tout << ls << " = " << rs << ";\n"; for (unsigned i = 0; i < lhs.size(); ++i) { tout << mk_pp(lhs.get(i), m) << " = " << mk_pp(rhs.get(i), m) << ";\n"; }); return true; } bool theory_seq::solve_itos(expr_ref_vector const& ls, expr_ref_vector const& rs, dependency* dep) { expr* e = nullptr; if (ls.size() == 1 && rs.empty() && m_util.str.is_itos(ls[0], e)) { literal lit = mk_simplified_literal(m_autil.mk_le(e, m_autil.mk_int(-1))); propagate_lit(dep, 0, nullptr, lit); return true; } if (rs.size() == 1 && ls.empty() && m_util.str.is_itos(rs[0], e)) { literal lit = mk_simplified_literal(m_autil.mk_le(e, m_autil.mk_int(-1))); propagate_lit(dep, 0, nullptr, lit); return true; } return false; } bool theory_seq::solve_nth_eq2(expr_ref_vector const& ls, expr_ref_vector const& rs, dependency* deps) { rational n; expr* s = nullptr, *idx = nullptr; if (ls.size() == 1 && m_util.str.is_nth_i(ls[0], s, idx)) { expr_ref lhs(m_util.str.mk_at(s, idx), m); expr_ref rhs(m_util.str.mk_concat(rs.size(), rs.c_ptr()), m); expr_ref_vector ls1(m); ls1.push_back(lhs); expr_ref_vector rs1(m); rs1.push_back(m_util.str.mk_unit(rhs)); m_eqs.push_back(eq(m_eq_id++, ls1, rs1, deps)); return true; } return false; } bool theory_seq::solve_nth_eq1(expr_ref_vector const& ls, expr_ref_vector const& rs, dependency* dep) { if (solve_nth_eq2(ls, rs, dep)) { return true; } if (ls.size() != 1 || rs.size() <= 1) { return false; } expr* l = ls.get(0); rational val; if (!get_length(l, val) || val != rational(rs.size())) { return false; } for (unsigned i = 0; i < rs.size(); ++i) { unsigned k = 0; expr* ru = nullptr, *r = nullptr; if (m_util.str.is_unit(rs.get(i), ru) && m_util.str.is_nth_i(ru, r, k) && k == i && r == l) { continue; } return false; } add_solution(l, mk_concat(rs, m.get_sort(l)), dep); return true; } bool theory_seq::solve_unit_eq(expr_ref_vector const& l, expr_ref_vector const& r, dependency* deps) { if (l.size() == 1 && is_var(l[0]) && !occurs(l[0], r) && add_solution(l[0], mk_concat(r, m.get_sort(l[0])), deps)) { return true; } if (r.size() == 1 && is_var(r[0]) && !occurs(r[0], l) && add_solution(r[0], mk_concat(l, m.get_sort(r[0])), deps)) { return true; } return false; } bool theory_seq::reduce_length(expr* l, expr* r, literal_vector& lits) { expr_ref len1(m), len2(m); lits.reset(); if (get_length(l, len1, lits) && get_length(r, len2, lits) && len1 == len2) { return true; } else { return false; } } bool theory_seq::solve_unit_eq(expr* l, expr* r, dependency* deps) { if (l == r) { return true; } if (is_var(l) && !occurs(l, r) && add_solution(l, r, deps)) { return true; } if (is_var(r) && !occurs(r, l) && add_solution(r, l, deps)) { return true; } return false; } bool theory_seq::occurs(expr* a, expr_ref_vector const& b) { for (auto const& elem : b) { if (a == elem || m.is_ite(elem)) return true; } return false; } bool theory_seq::occurs(expr* a, expr* b) { // true if a occurs under an interpreted function or under left/right selector. SASSERT(is_var(a)); SASSERT(m_todo.empty()); expr* e1 = nullptr, *e2 = nullptr; m_todo.push_back(b); while (!m_todo.empty()) { b = m_todo.back(); if (a == b || m.is_ite(b)) { m_todo.reset(); return true; } m_todo.pop_back(); if (m_util.str.is_concat(b, e1, e2)) { m_todo.push_back(e1); m_todo.push_back(e2); } else if (m_util.str.is_unit(b, e1)) { m_todo.push_back(e1); } else if (m_util.str.is_nth_i(b, e1, e2)) { m_todo.push_back(e1); } } return false; } bool theory_seq::is_var(expr* a) const { return m_util.is_seq(a) && !m_util.str.is_concat(a) && !m_util.str.is_empty(a) && !m_util.str.is_string(a) && !m_util.str.is_unit(a) && !m_util.str.is_itos(a) && !m_util.str.is_nth_i(a) && !m.is_ite(a); } bool theory_seq::add_solution(expr* l, expr* r, dependency* deps) { if (l == r) { return false; } m_new_solution = true; m_rep.update(l, r, deps); enode* n1 = ensure_enode(l); enode* n2 = ensure_enode(r); TRACE("seq", tout << mk_bounded_pp(l, m, 2) << " ==> " << mk_bounded_pp(r, m, 2) << "\n"; display_deps(tout, deps); tout << "#" << n1->get_owner_id() << " ==> #" << n2->get_owner_id() << "\n"; tout << (n1->get_root() == n2->get_root()) << "\n";); propagate_eq(deps, n1, n2); return true; } bool theory_seq::solve_eqs(unsigned i) { context& ctx = get_context(); bool change = false; for (; !ctx.inconsistent() && i < m_eqs.size(); ++i) { eq const& e = m_eqs[i]; if (solve_eq(e.ls(), e.rs(), e.dep(), i)) { if (i + 1 != m_eqs.size()) { eq e1 = m_eqs[m_eqs.size()-1]; m_eqs.set(i, e1); --i; } ++m_stats.m_num_reductions; m_eqs.pop_back(); change = true; } TRACE("seq_verbose", display_equations(tout);); } return change || m_new_propagation || ctx.inconsistent(); } bool theory_seq::solve_eq(expr_ref_vector const& l, expr_ref_vector const& r, dependency* deps, unsigned idx) { context& ctx = get_context(); expr_ref_vector& ls = m_ls; expr_ref_vector& rs = m_rs; rs.reset(); ls.reset(); dependency* dep2 = nullptr; bool change = false; if (!canonize(l, ls, dep2, change)) return false; if (!canonize(r, rs, dep2, change)) return false; deps = m_dm.mk_join(dep2, deps); TRACE("seq_verbose", tout << l << " = " << r << " ==> "; tout << ls << " = " << rs << "\n"; display_deps(tout, deps); ); if (!ctx.inconsistent() && simplify_eq(ls, rs, deps)) { TRACE("seq", tout << "simplified\n";); return true; } if (!ctx.inconsistent() && lift_ite(ls, rs, deps)) { return true; } TRACE("seq_verbose", tout << ls << " = " << rs << "\n";); if (ls.empty() && rs.empty()) { return true; } if (!ctx.inconsistent() && solve_unit_eq(ls, rs, deps)) { TRACE("seq", tout << "unit\n";); return true; } if (!ctx.inconsistent() && solve_binary_eq(ls, rs, deps)) { TRACE("seq", tout << "binary\n";); return true; } if (!ctx.inconsistent() && solve_nth_eq1(ls, rs, deps)) { return true; } if (!ctx.inconsistent() && solve_nth_eq1(rs, ls, deps)) { return true; } if (!ctx.inconsistent() && solve_itos(rs, ls, deps)) { return true; } if (!ctx.inconsistent() && change) { // The propagation step from arithmetic state (e.g. length offset) to length constraints if (get_context().get_scope_level() == 0) { prop_arith_to_len_offset(); } TRACE("seq", tout << "inserting equality\n";); bool updated = false; if (!m_len_offset.empty()) { // Find a better equivalent term for lhs of an equation based on length constraints expr_ref_vector new_ls(m); if (find_better_rep(ls, rs, idx, deps, new_ls)) { eq const & new_eq = eq(m_eq_id++, new_ls, rs, deps); m_eqs.push_back(new_eq); TRACE("seq", tout << "find_better_rep\n";); TRACE("seq", display_equation(tout, new_eq);); updated = true; } } if (!updated) { m_eqs.push_back(eq(m_eq_id++, ls, rs, deps)); } TRACE("seq", tout << "simplified\n";); return true; } return false; } bool theory_seq::propagate_max_length(expr* l, expr* r, dependency* deps) { unsigned idx; expr* s; if (m_util.str.is_empty(l)) { std::swap(l, r); } rational hi; if (is_tail(l, s, idx) && has_length(s) && m_util.str.is_empty(r) && !upper_bound(s, hi)) { propagate_lit(deps, 0, nullptr, mk_literal(m_autil.mk_le(mk_len(s), m_autil.mk_int(idx+1)))); return true; } return false; } bool theory_seq::is_binary_eq(expr_ref_vector const& ls, expr_ref_vector const& rs, expr_ref& x, ptr_vector& xs, ptr_vector& ys, expr_ref& y) { if (ls.size() > 1 && is_var(ls[0]) && rs.size() > 1 && is_var(rs.back())) { xs.reset(); ys.reset(); x = ls[0]; y = rs.back(); for (unsigned i = 1; i < ls.size(); ++i) { if (!m_util.str.is_unit(ls[i])) return false; } for (unsigned i = 0; i < rs.size()-1; ++i) { if (!m_util.str.is_unit(rs[i])) return false; } xs.append(ls.size()-1, ls.c_ptr() + 1); ys.append(rs.size()-1, rs.c_ptr()); return true; } return false; } bool theory_seq::is_quat_eq(expr_ref_vector const& ls, expr_ref_vector const& rs, expr_ref& x1, expr_ref_vector& xs, expr_ref& x2, expr_ref& y1, expr_ref_vector& ys, expr_ref& y2) { if (ls.size() > 1 && is_var(ls[0]) && is_var(ls.back()) && rs.size() > 1 && is_var(rs[0]) && is_var(rs.back())) { unsigned l_start = 1; for (; l_start < ls.size()-1; ++l_start) { if (m_util.str.is_unit(ls[l_start])) break; } if (l_start == ls.size()-1) return false; unsigned l_end = l_start; for (; l_end < ls.size()-1; ++l_end) { if (!m_util.str.is_unit(ls[l_end])) break; } --l_end; unsigned r_start = 1; for (; r_start < rs.size()-1; ++r_start) { if (m_util.str.is_unit(rs[r_start])) break; } if (r_start == rs.size()-1) return false; unsigned r_end = r_start; for (; r_end < rs.size()-1; ++r_end) { if (!m_util.str.is_unit(rs[r_end])) break; } --r_end; for (unsigned i = l_start; i < l_end+1; ++i) { if (!m_util.str.is_unit(ls[i])) return false; } for (unsigned i = r_start; i < r_end+1; ++i) { if (!m_util.str.is_unit(rs[i])) return false; } xs.reset(); xs.append(l_end-l_start+1, ls.c_ptr()+l_start); x1 = m_util.str.mk_concat(l_start, ls.c_ptr()); x2 = m_util.str.mk_concat(ls.size()-l_end-1, ls.c_ptr()+l_end+1); ys.reset(); ys.append(r_end-r_start+1, rs.c_ptr()+r_start); y1 = m_util.str.mk_concat(r_start, rs.c_ptr()); y2 = m_util.str.mk_concat(rs.size()-r_end-1, rs.c_ptr()+r_end+1); return true; } return false; } bool theory_seq::is_ternary_eq(expr_ref_vector const& ls, expr_ref_vector const& rs, expr_ref& x, expr_ref_vector& xs, expr_ref& y1, expr_ref_vector& ys, expr_ref& y2, bool flag1) { if (ls.size() > 1 && (is_var(ls[0]) || flag1) && rs.size() > 1 && is_var(rs[0]) && is_var(rs.back())) { unsigned l_start = ls.size()-1; for (; l_start > 0; --l_start) { if (!m_util.str.is_unit(ls[l_start])) break; } if (l_start == ls.size()-1) return false; ++l_start; unsigned r_end = rs.size()-2; for (; r_end > 0; --r_end) { if (m_util.str.is_unit(rs[r_end])) break; } if (r_end == 0) return false; unsigned r_start = r_end; for (; r_start > 0; --r_start) { if (!m_util.str.is_unit(rs[r_start])) break; } ++r_start; for (unsigned i = l_start; i < ls.size(); ++i) { if (!m_util.str.is_unit(ls[i])) return false; } for (unsigned i = r_start; i < r_end+1; ++i) { if (!m_util.str.is_unit(rs[i])) return false; } xs.reset(); xs.append(ls.size()-l_start, ls.c_ptr()+l_start); x = m_util.str.mk_concat(l_start, ls.c_ptr()); ys.reset(); ys.append(r_end-r_start+1, rs.c_ptr()+r_start); y1 = m_util.str.mk_concat(r_start, rs.c_ptr()); y2 = m_util.str.mk_concat(rs.size()-r_end-1, rs.c_ptr()+r_end+1); return true; } return false; } bool theory_seq::is_ternary_eq2(expr_ref_vector const& ls, expr_ref_vector const& rs, expr_ref_vector& xs, expr_ref& x, expr_ref& y1, expr_ref_vector& ys, expr_ref& y2, bool flag1) { if (ls.size() > 1 && (is_var(ls.back()) || flag1) && rs.size() > 1 && is_var(rs[0]) && is_var(rs.back())) { unsigned l_start = 0; for (; l_start < ls.size()-1; ++l_start) { if (!m_util.str.is_unit(ls[l_start])) break; } if (l_start == 0) return false; unsigned r_start = 1; for (; r_start < rs.size()-1; ++r_start) { if (m_util.str.is_unit(rs[r_start])) break; } if (r_start == rs.size()-1) return false; unsigned r_end = r_start; for (; r_end < rs.size()-1; ++r_end) { if (!m_util.str.is_unit(rs[r_end])) break; } --r_end; for (unsigned i = 0; i < l_start; ++i) { if (!m_util.str.is_unit(ls[i])) return false; } for (unsigned i = r_start; i < r_end+1; ++i) { if (!m_util.str.is_unit(rs[i])) return false; } xs.reset(); xs.append(l_start, ls.c_ptr()); x = m_util.str.mk_concat(ls.size()-l_start, ls.c_ptr()+l_start); ys.reset(); ys.append(r_end-r_start+1, rs.c_ptr()+r_start); y1 = m_util.str.mk_concat(r_start, rs.c_ptr()); y2 = m_util.str.mk_concat(rs.size()-r_end-1, rs.c_ptr()+r_end+1); return true; } return false; } bool theory_seq::reduce_length_eq(expr_ref_vector const& ls, expr_ref_vector const& rs, dependency* deps) { if (ls.empty() || rs.empty()) { return false; } if (ls.size() <= 1 && rs.size() <= 1) { return false; } SASSERT(ls.size() > 1 || rs.size() > 1); literal_vector lits; expr_ref l(ls[0], m), r(rs[0], m); if (reduce_length(l, r, lits)) { expr_ref_vector lhs(m), rhs(m); lhs.append(ls.size()-1, ls.c_ptr() + 1); rhs.append(rs.size()-1, rs.c_ptr() + 1); SASSERT(!lhs.empty() || !rhs.empty()); deps = mk_join(deps, lits); 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; } l = ls.back(); r = rs.back(); if (reduce_length(l, r, lits)) { expr_ref_vector lhs(m), rhs(m); lhs.append(ls.size()-1, ls.c_ptr()); 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)); propagate_eq(deps, lits, l, r, true); return true; } rational len1, len2, len; if (ls.size() > 1 && get_length(ls[0], len1) && get_length(rs[0], len2) && len1 >= len2) { unsigned j = 1; for (; j < rs.size() && len1 > len2 && get_length(rs[j], len); ++j) { len2 += len; } if (len1 == len2 && 0 < j && j < rs.size() && reduce_length(1, j, true, ls, rs, deps)) { TRACE("seq", tout << "l equal\n";); return true; } } if (rs.size() > 1 && get_length(rs[0], len1) && get_length(ls[0], len2) && len1 > len2) { unsigned j = 1; for (; j < ls.size() && len1 > len2 && get_length(ls[j], len); ++j) { len2 += len; } if (len1 == len2 && 0 < j && j < ls.size() && reduce_length(j, 1, true, ls, rs, deps)) { TRACE("seq", tout << "r equal\n";); return true; } } if (ls.size() > 1 && get_length(ls.back(), len1) && get_length(rs.back(), len2) && len1 >= len2) { unsigned j = rs.size()-1; for (; j > 0 && len1 > len2 && get_length(rs[j-1], len); --j) { len2 += len; } if (len1 == len2 && 0 < j && j < rs.size() && reduce_length(ls.size()-1, rs.size()-j, false, ls, rs, deps)) { TRACE("seq", tout << "l suffix equal\n";); return true; } } if (rs.size() > 1 && get_length(rs.back(), len1) && get_length(ls.back(), len2) && len1 > len2) { unsigned j = ls.size()-1; for (; j > 0 && len1 > len2 && get_length(ls[j-1], len); --j) { len2 += len; } if (len1 == len2 && 0 < j && j < ls.size() && reduce_length(ls.size()-j, rs.size()-1, false, ls, rs, deps)) { TRACE("seq", tout << "r suffix equal\n";); return true; } } return false; } bool theory_seq::reduce_length(unsigned i, unsigned j, bool front, expr_ref_vector const& ls, expr_ref_vector const& rs, dependency* deps) { context& ctx = get_context(); expr* const* ls1 = ls.c_ptr(); expr* const* ls2 = ls.c_ptr()+i; expr* const* rs1 = rs.c_ptr(); expr* const* rs2 = rs.c_ptr()+j; unsigned l1 = i; unsigned l2 = ls.size()-i; unsigned r1 = j; unsigned r2 = rs.size()-j; if (!front) { std::swap(ls1, ls2); std::swap(rs1, rs2); std::swap(l1, l2); std::swap(r1, r2); } SASSERT(0 < l1 && l1 < ls.size()); SASSERT(0 < r1 && r1 < rs.size()); expr_ref l = mk_concat(l1, ls1); expr_ref r = mk_concat(r1, rs1); expr_ref lenl = mk_len(l); expr_ref lenr = mk_len(r); literal lit = mk_eq(lenl, lenr, false); if (ctx.get_assignment(lit) == l_true) { expr_ref_vector lhs(m), rhs(m); lhs.append(l2, ls2); rhs.append(r2, rs2); for (auto const& e : m_eqs) { if (e.ls() == lhs && e.rs() == rhs) { return false; } } deps = mk_join(deps, lit); m_eqs.push_back(eq(m_eq_id++, lhs, rhs, deps)); propagate_eq(deps, l, r, true); TRACE("seq", tout << "propagate eq\n" << m_eqs.size() << "\nlhs: " << lhs << "\nrhs: " << rhs << "\n";); return true; } else { return false; } } bool theory_seq::solve_binary_eq(expr_ref_vector const& ls, expr_ref_vector const& rs, dependency* dep) { context& ctx = get_context(); ptr_vector xs, ys; expr_ref x(m), y(m); bool is_binary = is_binary_eq(ls, rs, x, xs, ys, y); if (!is_binary) { is_binary = is_binary_eq(rs, ls, x, xs, ys, y); } if (!is_binary) { return false; } // Equation is of the form x ++ xs = ys ++ y // where xs, ys are units. if (x != y) { return false; } if (xs.size() != ys.size()) { TRACE("seq", tout << "binary conflict\n";); set_conflict(dep); return false; } if (xs.empty()) { // this should have been solved already UNREACHABLE(); return false; } // Equation is of the form x ++ xs = ys ++ x // where |xs| = |ys| are units of same length // then xs is a wrap-around of ys // x ++ ab = ba ++ x // if (xs.size() == 1) { enode* n1 = ensure_enode(xs[0]); enode* n2 = ensure_enode(ys[0]); if (n1->get_root() == n2->get_root()) { return false; } literal eq = mk_eq(xs[0], ys[0], false); switch (ctx.get_assignment(eq)) { case l_false: { literal_vector conflict; conflict.push_back(~eq); TRACE("seq", tout << conflict << "\n";); set_conflict(dep, conflict); break; } case l_true: break; case l_undef: ctx.mark_as_relevant(eq); propagate_lit(dep, 0, nullptr, eq); m_new_propagation = true; break; } } return false; } bool theory_seq::get_length(expr* e, expr_ref& len, literal_vector& lits) { context& ctx = get_context(); expr* s, *i, *l; rational r; if (m_util.str.is_extract(e, s, i, l)) { // 0 <= i <= len(s), 0 <= l, i + l <= len(s) expr_ref zero(m_autil.mk_int(0), m); expr_ref ls = mk_len(s); expr_ref ls_minus_i_l(mk_sub(mk_sub(ls, i),l), m); bool i_is_zero = m_autil.is_numeral(i, r) && r.is_zero(); literal i_ge_0 = i_is_zero?true_literal:mk_simplified_literal(m_autil.mk_ge(i, zero)); literal i_lt_len_s = ~mk_simplified_literal(m_autil.mk_ge(mk_sub(i, ls), zero)); 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 _lits[4] = { i_ge_0, i_lt_len_s, li_ge_ls, l_ge_zero }; if (ctx.get_assignment(i_ge_0) == l_true && ctx.get_assignment(i_lt_len_s) == l_true && ctx.get_assignment(li_ge_ls) == l_true && ctx.get_assignment(l_ge_zero) == l_true) { len = l; lits.append(4, _lits); return true; } TRACE("seq", tout << mk_pp(e, m) << "\n"; ctx.display_literals_verbose(tout, 4, _lits); tout << "\n"; for (unsigned i = 0; i < 4; ++i) tout << ctx.get_assignment(_lits[i]) << "\n";); } else if (m_util.str.is_at(e, s, i)) { // has length 1 if 0 <= i < len(s) expr_ref zero(m_autil.mk_int(0), m); bool i_is_zero = m_autil.is_numeral(i, r) && r.is_zero(); literal i_ge_0 = i_is_zero?true_literal:mk_simplified_literal(m_autil.mk_ge(i, zero)); literal i_lt_len_s = ~mk_simplified_literal(m_autil.mk_ge(mk_sub(i, mk_len(s)), zero)); literal _lits[2] = { i_ge_0, i_lt_len_s}; if (ctx.get_assignment(i_ge_0) == l_true && 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; } } else if (is_pre(e, s, i)) { expr_ref zero(m_autil.mk_int(0), m); bool i_is_zero = m_autil.is_numeral(i, r) && r.is_zero(); literal i_ge_0 = i_is_zero?true_literal:mk_simplified_literal(m_autil.mk_ge(i, zero)); literal i_lt_len_s = ~mk_simplified_literal(m_autil.mk_ge(mk_sub(i, mk_len(s)), zero)); literal _lits[2] = { i_ge_0, i_lt_len_s }; if (ctx.get_assignment(i_ge_0) == l_true && 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; } } else if (is_post(e, s, l)) { expr_ref zero(m_autil.mk_int(0), m); literal l_ge_0 = mk_simplified_literal(m_autil.mk_ge(l, zero)); literal l_le_len_s = mk_simplified_literal(m_autil.mk_ge(mk_sub(mk_len(s), l), zero)); literal _lits[2] = { l_ge_0, l_le_len_s }; if (ctx.get_assignment(l_ge_0) == l_true && 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; } } 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_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)) { len = m_autil.mk_int(1); return true; } return false; } bool theory_seq::branch_nqs() { if (m_nqs.empty()) { return false; } ne n = m_nqs[0]; branch_nq(n); if (m_nqs.size() > 1) { ne n2 = m_nqs[m_nqs.size() - 1]; m_nqs.set(0, n2); } m_nqs.pop_back(); return true; } void theory_seq::branch_nq(ne const& n) { context& ctx = get_context(); literal_vector lits; enode_pair_vector eqs; VERIFY(linearize(n.dep(), eqs, lits)); for (literal & l : lits) { l.neg(); } for (enode_pair const& p : eqs) { lits.push_back(mk_eq(p.first->get_owner(), p.second->get_owner(), false)); } literal eq_len = mk_eq(mk_len(n.l()), mk_len(n.r()), false); literal len_gt = mk_literal(m_autil.mk_ge(mk_len(n.l()), m_autil.mk_int(1))); expr_ref h1(m), t1(m), h2(m), t2(m); mk_decompose(n.l(), h1, t1); mk_decompose(n.r(), h2, t2); ctx.mark_as_relevant(eq_len); ctx.mark_as_relevant(len_gt); lits.push_back(~eq_len); // Deps => |l| != |r| or |l| > 0 lits.push_back(len_gt); literal_vector lits1(lits); lits.pop_back(); // Deps => |l| != |r| or h1 != h2 or t1 != t2 lits.push_back(~mk_eq(h1, h2, false)); lits.push_back(~mk_eq(t1, t2, false)); literal_vector lits2(lits); lits.pop_back(); ctx.mk_th_axiom(get_id(), lits1.size(), lits1.c_ptr()); ctx.mk_th_axiom(get_id(), lits2.size(), lits2.c_ptr()); } bool theory_seq::solve_nqs(unsigned i) { context & ctx = get_context(); for (; !ctx.inconsistent() && i < m_nqs.size(); ++i) { if (solve_ne(i)) { if (i + 1 != m_nqs.size()) { ne n = m_nqs[m_nqs.size()-1]; m_nqs.set(i, n); --i; } m_nqs.pop_back(); } } return m_new_propagation || ctx.inconsistent(); } bool theory_seq::solve_ne(unsigned idx) { context& ctx = get_context(); ne const& n = m_nqs[idx]; TRACE("seq", display_disequation(tout << "solve: ", n);); unsigned num_undef_lits = 0; for (literal lit : n.lits()) { switch (ctx.get_assignment(lit)) { case l_false: TRACE("seq", display_disequation(tout << "has false literal\n", n); ctx.display_literal_verbose(tout, lit); tout << "\n" << lit << " " << ctx.is_relevant(lit) << "\n"; ); return true; case l_true: break; case l_undef: ++num_undef_lits; break; } } bool updated = false; dependency* new_deps = n.dep(); vector new_ls, new_rs; literal_vector new_lits(n.lits()); for (unsigned i = 0; i < n.ls().size(); ++i) { expr_ref_vector& ls = m_ls; expr_ref_vector& rs = m_rs; expr_ref_vector& lhs = m_lhs; expr_ref_vector& rhs = m_rhs; ls.reset(); rs.reset(); lhs.reset(); rhs.reset(); dependency* deps = nullptr; bool change = false; if (!canonize(n.ls(i), ls, deps, change)) return false; if (!canonize(n.rs(i), rs, deps, change)) return false; if (!m_seq_rewrite.reduce_eq(ls, rs, lhs, rhs, change)) { TRACE("seq", display_disequation(tout << "reduces to false: ", n); tout << n.ls(i) << " -> " << ls << "\n"; tout << n.rs(i) << " -> " << rs << "\n";); return true; } else if (!change) { TRACE("seq", tout << "no change " << n.ls(i) << " " << n.rs(i) << "\n";); if (updated) { new_ls.push_back(n.ls(i)); new_rs.push_back(n.rs(i)); } continue; } else { if (!updated) { for (unsigned j = 0; j < i; ++j) { new_ls.push_back(n.ls(j)); new_rs.push_back(n.rs(j)); } } updated = true; if (!ls.empty() || !rs.empty()) { new_ls.push_back(ls); new_rs.push_back(rs); } TRACE("seq", tout << lhs << " != " << rhs << "\n"; for (unsigned j = 0; j < new_ls.size(); ++j) tout << new_ls[j] << " != " << new_rs[j] << "\n"; tout << n.ls(i) << " != " << n.rs(i) << "\n";); for (unsigned j = 0; j < lhs.size(); ++j) { expr* nl = lhs[j].get(); expr* nr = rhs[j].get(); if (m_util.is_seq(nl) || m_util.is_re(nl)) { ls.reset(); rs.reset(); m_util.str.get_concat(nl, ls); m_util.str.get_concat(nr, rs); new_ls.push_back(ls); new_rs.push_back(rs); } else if (nl != nr) { literal lit(mk_eq(nl, nr, false)); ctx.mark_as_relevant(lit); new_lits.push_back(lit); switch (ctx.get_assignment(lit)) { case l_false: return true; case l_true: break; case l_undef: ++num_undef_lits; m_new_propagation = true; break; } } } new_deps = m_dm.mk_join(deps, new_deps); } } TRACE("seq", display_disequation(tout, n);); if (!updated && num_undef_lits == 0) { return false; } if (!updated) { for (unsigned j = 0; j < n.ls().size(); ++j) { new_ls.push_back(n.ls(j)); new_rs.push_back(n.rs(j)); } } if (num_undef_lits == 1 && new_ls.empty()) { literal_vector lits; literal undef_lit = null_literal; for (literal lit : new_lits) { switch (ctx.get_assignment(lit)) { case l_true: lits.push_back(lit); break; case l_false: UNREACHABLE(); break; case l_undef: SASSERT(undef_lit == null_literal); undef_lit = lit; break; } } TRACE("seq", tout << "propagate: " << undef_lit << "\n";); SASSERT(undef_lit != null_literal); propagate_lit(new_deps, lits.size(), lits.c_ptr(), ~undef_lit); return true; } if (updated) { if (num_undef_lits == 0 && new_ls.empty()) { TRACE("seq", tout << "conflict\n";); dependency* deps1 = nullptr; if (explain_eq(n.l(), n.r(), deps1)) { literal diseq = mk_eq(n.l(), n.r(), false); if (ctx.get_assignment(diseq) == l_false) { new_lits.reset(); new_lits.push_back(~diseq); new_deps = deps1; TRACE("seq", tout << "conflict explained\n";); } } set_conflict(new_deps, new_lits); SASSERT(m_new_propagation); } else { m_nqs.push_back(ne(n.l(), n.r(), new_ls, new_rs, new_lits, new_deps)); TRACE("seq", display_disequation(tout << "updated: ", m_nqs[m_nqs.size()-1]);); } } return updated; } bool theory_seq::solve_nc(unsigned idx) { nc const& n = m_ncs[idx]; dependency* deps = n.deps(); literal len_gt = n.len_gt(); context& ctx = get_context(); expr_ref c(m); if (!canonize(n.contains(), deps, c)) { return false; } expr* a = nullptr, *b = nullptr; CTRACE("seq", c != n.contains(), tout << n.contains() << " =>\n" << c << "\n";); if (m.is_true(c)) { literal_vector lits; set_conflict(deps, lits); return true; } if (m.is_false(c)) { return true; } if (ctx.get_assignment(len_gt) == l_true) { VERIFY(m_util.str.is_contains(n.contains(), a, b)); add_length_to_eqc(a); add_length_to_eqc(b); TRACE("seq", tout << len_gt << " is true\n";); return true; } if (m.is_eq(c, a, b)) { literal eq = mk_eq(a, b, false); propagate_lit(deps, 0, nullptr, ~eq); return true; } if (m.is_or(c)) { for (expr* arg : *to_app(c)) { expr_ref ci(arg, m); m_ncs.push_back(nc(ci, len_gt, deps)); } m_new_propagation = true; return true; } if (m.is_and(c)) { enode_pair_vector eqs; literal_vector lits; 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)); } for (expr* arg : *to_app(c)) { if (m.is_eq(arg, a, b)) { lits.push_back(~mk_eq(a, b, false)); } else { lits.push_back(~mk_literal(arg)); } } TRACE("seq", ctx.display_literals_verbose(tout, lits.size(), lits.c_ptr()) << "\n";); ctx.mk_th_axiom(get_id(), lits.size(), lits.c_ptr()); if (m.has_trace_stream()) { expr_ref_vector exprs(m); for (literal l : lits) { expr* e = ctx.bool_var2expr(l.var()); if (l.sign()) e = m.mk_not(e); exprs.push_back(e); } app_ref body(m); body = m.mk_or(exprs.size(), exprs.c_ptr()); log_axiom_instantiation(body); m.trace_stream() << "[end-of-instance]\n"; } return true; } if (c != n.contains()) { m_ncs.push_back(nc(c, len_gt, deps)); m_new_propagation = true; return true; } return false; } theory_seq::cell* theory_seq::mk_cell(cell* p, expr* e, dependency* d) { cell* c = alloc(cell, p, e, d); m_all_cells.push_back(c); return c; } void theory_seq::unfold(cell* c, ptr_vector& cons) { dependency* dep = nullptr; expr* a, *e1, *e2; if (m_rep.find1(c->m_expr, a, dep)) { cell* c1 = mk_cell(c, a, m_dm.mk_join(dep, c->m_dep)); unfold(c1, cons); } else if (m_util.str.is_concat(c->m_expr, e1, e2)) { cell* c1 = mk_cell(c, e1, c->m_dep); cell* c2 = mk_cell(nullptr, e2, nullptr); unfold(c1, cons); unfold(c2, cons); } else { cons.push_back(c); } c->m_last = cons.size()-1; } // // a -> a1.a2, a2 -> a3.a4 -> ... // b -> b1.b2, b2 -> b3.a4 // // e1 // void theory_seq::display_explain(std::ostream& out, unsigned indent, expr* e) { expr* e1, *e2, *a; dependency* dep = nullptr; smt2_pp_environment_dbg env(m); params_ref p; for (unsigned i = 0; i < indent; ++i) out << " "; ast_smt2_pp(out, e, env, p, indent); out << "\n"; if (m_rep.find1(e, a, dep)) { display_explain(out, indent + 1, a); } else if (m_util.str.is_concat(e, e1, e2)) { display_explain(out, indent + 1, e1); display_explain(out, indent + 1, e2); } } bool theory_seq::explain_eq(expr* e1, expr* e2, dependency*& dep) { if (e1 == e2) { return true; } expr* a1, *a2; ptr_vector v1, v2; unsigned cells_sz = m_all_cells.size(); cell* c1 = mk_cell(nullptr, e1, nullptr); cell* c2 = mk_cell(nullptr, e2, nullptr); unfold(c1, v1); unfold(c2, v2); unsigned i = 0, j = 0; TRACE("seq", tout << "1:\n"; display_explain(tout, 0, e1); tout << "2:\n"; display_explain(tout, 0, e2);); bool result = true; while (i < v1.size() || j < v2.size()) { if (i == v1.size()) { while (j < v2.size() && m_util.str.is_empty(v2[j]->m_expr)) { dep = m_dm.mk_join(dep, v2[j]->m_dep); ++j; } result = j == v2.size(); break; } if (j == v2.size()) { while (i < v1.size() && m_util.str.is_empty(v1[i]->m_expr)) { dep = m_dm.mk_join(dep, v1[i]->m_dep); ++i; } result = i == v1.size(); break; } cell* c1 = v1[i]; cell* c2 = v2[j]; e1 = c1->m_expr; e2 = c2->m_expr; if (e1 == e2) { if (c1->m_parent && c2->m_parent && c1->m_parent->m_expr == c2->m_parent->m_expr) { TRACE("seq", tout << "parent: " << mk_pp(e1, m) << " " << mk_pp(c1->m_parent->m_expr, m) << "\n";); c1 = c1->m_parent; c2 = c2->m_parent; v1[c1->m_last] = c1; i = c1->m_last; v2[c2->m_last] = c2; j = c2->m_last; } else { dep = m_dm.mk_join(dep, c1->m_dep); dep = m_dm.mk_join(dep, c2->m_dep); ++i; ++j; } } else if (m_util.str.is_empty(e1)) { dep = m_dm.mk_join(dep, c1->m_dep); ++i; } else if (m_util.str.is_empty(e2)) { dep = m_dm.mk_join(dep, c2->m_dep); ++j; } else if (m_util.str.is_unit(e1, a1) && m_util.str.is_unit(e2, a2)) { if (explain_eq(a1, a2, dep)) { ++i; ++j; } else { result = false; break; } } else { TRACE("seq", tout << "Could not solve " << mk_pp(e1, m) << " = " << mk_pp(e2, m) << "\n";); result = false; break; } } m_all_cells.resize(cells_sz); return result; } bool theory_seq::explain_empty(expr_ref_vector& es, dependency*& dep) { while (!es.empty()) { expr* e = es.back(); if (m_util.str.is_empty(e)) { es.pop_back(); continue; } expr* a; if (m_rep.find1(e, a, dep)) { es.pop_back(); m_util.str.get_concat(a, es); continue; } TRACE("seq", tout << "Could not set to empty: " << es << "\n";); return false; } return true; } bool theory_seq::simplify_and_solve_eqs() { context & ctx = get_context(); m_new_solution = true; while (m_new_solution && !ctx.inconsistent()) { m_new_solution = false; solve_eqs(0); } return m_new_propagation || ctx.inconsistent(); } void theory_seq::internalize_eq_eh(app * atom, bool_var v) { } bool theory_seq::internalize_atom(app* a, bool) { return internalize_term(a); } bool theory_seq::internalize_term(app* term) { context & ctx = get_context(); if (ctx.e_internalized(term)) { enode* e = ctx.get_enode(term); mk_var(e); return true; } for (auto arg : *term) { mk_var(ensure_enode(arg)); } if (m.is_bool(term)) { bool_var bv = ctx.mk_bool_var(term); ctx.set_var_theory(bv, get_id()); ctx.mark_as_relevant(bv); } enode* e = nullptr; if (ctx.e_internalized(term)) { e = ctx.get_enode(term); } else { e = ctx.mk_enode(term, false, m.is_bool(term), true); } mk_var(e); if (!ctx.relevancy()) { relevant_eh(term); } return true; } void theory_seq::add_length(expr* e, expr* l) { TRACE("seq", tout << get_context().get_scope_level() << " " << mk_bounded_pp(e, m, 2) << "\n";); 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)); } /* ensure that all elements in equivalence class occur under an application of 'length' */ bool theory_seq::add_length_to_eqc(expr* e) { enode* n = ensure_enode(e); enode* n1 = n; bool change = false; do { expr* o = n->get_owner(); if (!has_length(o)) { expr_ref len(m_util.str.mk_length(o), m); enque_axiom(len); add_length(o, len); change = true; } n = n->get_next(); } while (n1 != n); return change; } void theory_seq::add_int_string(expr* e) { m_int_string.push_back(e); m_trail_stack.push(push_back_vector(m_int_string)); } bool theory_seq::check_int_string() { bool change = false; for (expr * e : m_int_string) { if (check_int_string(e)) { change = true; } } return change; } 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)); } void theory_seq::add_stoi_axiom(expr* e) { TRACE("seq", tout << mk_pp(e, m) << "\n";); expr* s = nullptr; VERIFY (m_util.str.is_stoi(e, s)); // stoi(s) >= -1 literal l = mk_simplified_literal(m_autil.mk_ge(e, m_autil.mk_int(-1))); add_axiom(l); // stoi("") = -1 add_axiom(~mk_literal(m_util.str.mk_is_empty(s)), mk_eq(m_util.str.mk_stoi(s), m_autil.mk_int(-1), false)); } void theory_seq::add_itos_axiom(expr* e) { rational val; expr* n = nullptr; TRACE("seq", tout << mk_pp(e, m) << "\n";); VERIFY(m_util.str.is_itos(e, n)); // itos(n) = "" <=> n < 0 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) != "" // itos(n) = "" or n >= 0 add_axiom(~eq1, ~ge0); add_axiom(eq1, ge0); // n >= 0 => stoi(itos(n)) = n app_ref stoi(m_util.str.mk_stoi(e), m); add_axiom(~ge0, mk_preferred_eq(stoi, n)); // itos(n) does not start with "0" when n > 0 // n = 0 or at(itos(n),0) != "0" // alternative: n >= 0 => itos(stoi(itos(n))) = itos(n) add_axiom(mk_eq(n, zero, false), ~mk_eq(m_util.str.mk_at(e,zero), m_util.str.mk_string(symbol("0")), false)); // add_axiom(~ge0, mk_preferred_eq(m_util.str.mk_itos(stoi), e)); } 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; } add_length_to_eqc(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; } add_length_to_eqc(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 void theory_seq::add_si_axiom(expr* e, expr* n, unsigned k) { context& ctx = get_context(); zstring s; expr_ref ith_char(m), num(m), coeff(m); expr_ref_vector nums(m), chars(m); expr_ref len = mk_len(e); literal len_eq_k = mk_preferred_eq(len, m_autil.mk_int(k)); literal ge0 = mk_literal(m_autil.mk_ge(n, m_autil.mk_int(0))); literal_vector digits; digits.push_back(~len_eq_k); digits.push_back(ge0); ensure_digit_axiom(); for (unsigned i = 0; i < k; ++i) { ith_char = mk_nth(e, m_autil.mk_int(i)); literal isd = is_digit(ith_char); 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)); } ++m_stats.m_add_axiom; ctx.mk_th_axiom(get_id(), digits.size(), digits.c_ptr()); rational c(1); for (unsigned i = k; i-- > 0; c *= rational(10)) { coeff = m_autil.mk_int(c); nums[i] = m_autil.mk_mul(coeff, nums.get(i)); } num = m_autil.mk_add(nums.size(), nums.c_ptr()); ctx.get_rewriter()(num); m_new_propagation = true; add_axiom(~len_eq_k, ~ge0, mk_preferred_eq(n, num)); add_axiom(~len_eq_k, ~ge0, mk_preferred_eq(e, m_util.str.mk_concat(chars))); SASSERT(k > 0); rational lb = power(rational(10), k - 1); rational ub = power(rational(10), k) - 1; arith_util& a = m_autil; literal lbl = mk_literal(a.mk_ge(n, a.mk_int(lb))); literal ge_k = mk_literal(a.mk_ge(len, a.mk_int(k))); // n >= lb => len(s) >= k add_axiom(~lbl, ge_k); } void theory_seq::apply_sort_cnstr(enode* n, sort* s) { mk_var(n); } void theory_seq::display(std::ostream & out) const { if (m_eqs.empty() && m_nqs.empty() && m_rep.empty() && m_exclude.empty()) { return; } out << "Theory seq\n"; if (!m_eqs.empty()) { out << "Equations:\n"; display_equations(out); } if (!m_nqs.empty()) { display_disequations(out); } if (!m_re2aut.empty()) { out << "Regex\n"; for (auto const& kv : m_re2aut) { out << mk_pp(kv.m_key, m) << "\n"; display_expr disp(m); if (kv.m_value) { kv.m_value->display(out, disp); } } } if (!m_rep.empty()) { out << "Solved equations:\n"; m_rep.display(out); } if (!m_exclude.empty()) { out << "Exclusions:\n"; m_exclude.display(out); } 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_bounded_pp(e, m, 3) << " [" << lo << ":" << hi << "]\n"; } } if (!m_ncs.empty()) { out << "Non contains:\n"; for (auto const& nc : m_ncs) { display_nc(out, nc); } } } std::ostream& theory_seq::display_nc(std::ostream& out, nc const& nc) const { out << "not " << mk_bounded_pp(nc.contains(), m, 2) << "\n"; display_deps(out << " <- ", nc.deps()) << "\n"; return out; } std::ostream& theory_seq::display_equations(std::ostream& out) const { for (auto const& e : m_eqs) { display_equation(out, e); } return out; } std::ostream& theory_seq::display_equation(std::ostream& out, eq const& e) const { bool first = true; for (expr* a : e.ls()) { if (first) first = false; else out << "\n"; out << mk_bounded_pp(a, m, 2); } out << " = "; for (expr* a : e.rs()) { if (first) first = false; else out << "\n"; out << mk_bounded_pp(a, m, 2); } out << " <- \n"; return display_deps(out, e.dep()); } std::ostream& theory_seq::display_disequations(std::ostream& out) const { bool first = true; for (ne const& n : m_nqs) { if (first) out << "Disequations:\n"; first = false; display_disequation(out, n); } return out; } std::ostream& theory_seq::display_disequation(std::ostream& out, ne const& e) const { for (literal lit : e.lits()) { out << lit << " "; } if (!e.lits().empty()) { out << "\n"; } for (unsigned j = 0; j < e.ls().size(); ++j) { for (expr* t : e.ls(j)) { out << mk_bounded_pp(t, m, 2) << " "; } out << " != "; for (expr* t : e.rs(j)) { out << mk_bounded_pp(t, m, 2) << " "; } out << "\n"; } if (e.dep()) { display_deps(out, e.dep()); } return out; } std::ostream& theory_seq::display_deps(std::ostream& out, literal_vector const& lits, enode_pair_vector const& eqs) const { context& ctx = get_context(); smt2_pp_environment_dbg env(m); params_ref p; for (auto const& eq : eqs) { out << " (= " << mk_bounded_pp(eq.first->get_owner(), m, 2) << "\n " << mk_bounded_pp(eq.second->get_owner(), m, 2) << ")\n"; } for (literal l : lits) { if (l == true_literal) { out << " true"; } else if (l == false_literal) { out << " false"; } else { expr* e = ctx.bool_var2expr(l.var()); if (l.sign()) { out << " (not " << mk_bounded_pp(e, m) << ")"; } else { out << " " << mk_bounded_pp(e, m); } } out << "\n"; } return out; } std::ostream& theory_seq::display_deps(std::ostream& out, dependency* dep) const { literal_vector lits; enode_pair_vector eqs; linearize(dep, eqs, lits); display_deps(out, lits, eqs); return out; } void theory_seq::collect_statistics(::statistics & st) const { st.update("seq num splits", m_stats.m_num_splits); st.update("seq num reductions", m_stats.m_num_reductions); st.update("seq length coherence", m_stats.m_check_length_coherence); st.update("seq branch", m_stats.m_branch_variable); st.update("seq solve !=", m_stats.m_solve_nqs); st.update("seq solve =", m_stats.m_solve_eqs); st.update("seq branch !=", m_stats.m_branch_nqs); st.update("seq add axiom", m_stats.m_add_axiom); 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() { m_re2aut.reset(); m_res.reset(); m_automata.reset(); } void theory_seq::init_model(expr_ref_vector const& es) { expr_ref new_s(m); for (auto e : es) { dependency* eqs = nullptr; expr_ref s(m); if (!canonize(e, eqs, s)) s = e; if (is_var(s)) { new_s = m_factory->get_fresh_value(m.get_sort(s)); m_rep.update(s, new_s, eqs); } } } void theory_seq::finalize_model(model_generator& mg) { m_rep.pop_scope(1); } void theory_seq::init_model(model_generator & mg) { m_rep.push_scope(); m_factory = alloc(seq_factory, get_manager(), get_family_id(), mg.get_model()); mg.register_factory(m_factory); for (ne const& n : m_nqs) { m_factory->register_value(n.l()); m_factory->register_value(n.r()); } for (ne const& n : m_nqs) { for (unsigned i = 0; i < n.ls().size(); ++i) { init_model(n.ls(i)); init_model(n.rs(i)); } } } class theory_seq::seq_value_proc : public model_value_proc { enum source_t { unit_source, int_source, string_source }; theory_seq& th; sort* m_sort; svector m_dependencies; ptr_vector m_strings; svector m_source; public: seq_value_proc(theory_seq& th, sort* s): th(th), m_sort(s) { } ~seq_value_proc() override {} void add_unit(enode* n) { m_dependencies.push_back(model_value_dependency(n)); m_source.push_back(unit_source); } void add_int(enode* n) { m_dependencies.push_back(model_value_dependency(n)); m_source.push_back(int_source); } void add_string(expr* n) { m_strings.push_back(n); m_source.push_back(string_source); } void get_dependencies(buffer & result) override { result.append(m_dependencies.size(), m_dependencies.c_ptr()); } void add_buffer(svector& sbuffer, zstring const& zs) { for (unsigned i = 0; i < zs.length(); ++i) { sbuffer.push_back(zs[i]); } } app * mk_value(model_generator & mg, expr_ref_vector const & values) override { 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; unsigned ch; for (source_t src : m_source) { switch (src) { case unit_source: { VERIFY(th.m_util.is_const_char(values[j++], ch)); sbuffer.push_back(ch); break; } case string_source: { dependency* deps = nullptr; expr_ref tmp(th.m); if (!th.canonize(m_strings[k], deps, tmp)) tmp = m_strings[k]; zstring zs; if (th.m_util.str.is_string(tmp, zs)) { add_buffer(sbuffer, zs); } else { TRACE("seq", tout << "Not a string: " << tmp << "\n";); } ++k; break; } case int_source: { std::ostringstream strm; arith_util arith(th.m); VERIFY(arith.is_numeral(values[j++], val)); if (val.is_neg()) { strm << ""; } else { strm << val; } zstring zs(strm.str().c_str()); add_buffer(sbuffer, zs); break; } } // TRACE("seq", tout << src << " " << sbuffer << "\n";); } result = th.m_util.str.mk_string(zstring(sbuffer.size(), sbuffer.c_ptr())); } else { for (source_t src : m_source) { switch (src) { case unit_source: args.push_back(th.m_util.str.mk_unit(values[j++])); break; case string_source: args.push_back(m_strings[k++]); break; case int_source: UNREACHABLE(); break; } } result = th.mk_concat(args, m_sort); 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(); 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_ite_concat(e, concats); sort* srt = m.get_sort(e); seq_value_proc* sv = alloc(seq_value_proc, *this, srt); TRACE("seq", tout << mk_pp(e, m) << "\n";); for (expr* c : concats) { expr *c1; TRACE("seq", tout << mk_pp(c, m) << "\n";); if (m_util.str.is_unit(c, c1)) { if (ctx.e_internalized(c1)) { sv->add_unit(ctx.get_enode(c1)); } } else if (m_util.str.is_itos(c, c1)) { if (ctx.e_internalized(c1)) { sv->add_int(ctx.get_enode(c1)); } } else if (m_util.str.is_string(c)) { sv->add_string(c); } else { sv->add_string(mk_value(to_app(c))); } } return sv; } else { return alloc(expr_wrapper_proc, mk_value(e)); } } 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); val = m_factory->get_some_value(m.get_sort(result)); if (val) { result = val; } } else { m_rewrite(result); } m_factory->add_trail(result); TRACE("seq", tout << mk_pp(e, m) << " -> " << result << "\n";); m_rep.update(e, result, nullptr); return to_app(result); } void theory_seq::validate_model(model& mdl) { for (auto const& eq : m_eqs) { expr_ref_vector ls = eq.ls(); expr_ref_vector rs = eq.rs(); expr_ref l(m_util.str.mk_concat(ls), m); expr_ref r(m_util.str.mk_concat(rs), m); if (!mdl.are_equal(l, r)) { IF_VERBOSE(0, verbose_stream() << "equality failed: " << l << " = " << r << "\nbut\n" << mdl(l) << " != " << mdl(r) << "\n"); } } for (auto const& ne : m_nqs) { expr_ref l = ne.l(); expr_ref r = ne.r(); if (mdl.are_equal(l, r)) { IF_VERBOSE(0, verbose_stream() << "disequality failed: " << l << " != " << r << "\n" << mdl(l) << "\n" << mdl(r) << "\n"); } } for (auto const& ex : m_exclude) { expr_ref l(ex.first, m); expr_ref r(ex.second, m); if (mdl.are_equal(l, r)) { IF_VERBOSE(0, verbose_stream() << "exclude " << l << " = " << r << " = " << mdl(l) << "\n"); } } for (auto const& nc : m_ncs) { expr_ref p = nc.contains(); if (!mdl.is_false(p)) { IF_VERBOSE(0, verbose_stream() << p << " evaluates to " << mdl(p) << "\n"); } } #if 0 // to enable this check need to add m_util.str.is_skolem(f); to theory_seq::include_func_interp for (auto const& kv : m_rep) { expr_ref l(kv.m_key, m); expr_ref r(kv.m_value.first, m); if (!mdl.are_equal(l, r)) { enode* ln = ensure_enode(l); enode* rn = ensure_enode(r); IF_VERBOSE(0, verbose_stream() << "bad representation: " << l << " ->\n" << r << "\nbut\n" << mdl(l) << "\n->\n" << mdl(r) << "\n" << "nodes: #" << ln->get_owner_id() << " #" << rn->get_owner_id() << "\n" << "roots: #" << ln->get_root()->get_owner_id() << " #" << rn->get_root()->get_owner_id() << "\n"; ); } } #endif #if 0 ptr_vector fmls; context& ctx = get_context(); ctx.get_asserted_formulas(fmls); validate_model_proc proc(*this, mdl); for (expr* f : fmls) { for_each_expr(proc, f); } #endif } theory_var theory_seq::mk_var(enode* n) { if (!m_util.is_seq(n->get_owner()) && !m_util.is_re(n->get_owner())) { return null_theory_var; } if (is_attached_to_var(n)) { return n->get_th_var(get_id()); } else { theory_var v = theory::mk_var(n); m_find.mk_var(); get_context().attach_th_var(n, this, v); get_context().mark_as_relevant(n); return v; } } bool theory_seq::can_propagate() { return m_axioms_head < m_axioms.size() || !m_replay.empty() || m_new_solution; } bool theory_seq::canonize(expr* e, dependency*& eqs, expr_ref& result) { if (!expand(e, eqs, result)) { return false; } TRACE("seq", tout << mk_bounded_pp(e, m, 2) << " expands to\n" << mk_bounded_pp(result, m, 2) << "\n";); m_rewrite(result); TRACE("seq", tout << mk_bounded_pp(e, m, 2) << " rewrites to\n" << mk_bounded_pp(result, m, 2) << "\n";); return true; } bool theory_seq::canonize(expr* e, expr_ref_vector& es, dependency*& eqs, bool& change) { expr* e1, *e2; expr_ref e3(e, m); while (true) { if (m_util.str.is_concat(e3, e1, e2)) { if (!canonize(e1, es, eqs, change)) { return false; } e3 = e2; change = true; } else if (m_util.str.is_empty(e3)) { change = true; break; } else { expr_ref e4(m); if (!expand(e3, eqs, e4)) { return false; } change |= e4 != e3; m_util.str.get_concat(e4, es); break; } } return true; } bool theory_seq::canonize(expr_ref_vector const& es, expr_ref_vector& result, dependency*& eqs, bool& change) { for (expr* e : es) { if (!canonize(e, result, eqs, change)) return false; SASSERT(!m_util.str.is_concat(e) || change); } return true; } bool theory_seq::expand(expr* e, dependency*& eqs, expr_ref& result) { unsigned sz = m_expand_todo.size(); m_expand_todo.push_back(e); while (m_expand_todo.size() != sz) { expr* e = m_expand_todo.back(); bool r = expand1(e, eqs, result); if (!r) { return false; } if (result) { SASSERT(m_expand_todo.back() == e); m_expand_todo.pop_back(); } } return true; } expr_ref theory_seq::try_expand(expr* e, dependency*& eqs){ expr_ref result(m); expr_dep ed; if (m_rep.find_cache(e, ed)) { if (e != ed.first) { eqs = m_dm.mk_join(eqs, ed.second); } result = ed.first; } else { m_expand_todo.push_back(e); } return result; } bool theory_seq::expand1(expr* e0, dependency*& eqs, expr_ref& result) { result = try_expand(e0, eqs); if (result) return true; dependency* deps = nullptr; expr* e = m_rep.find(e0, deps); expr* e1, *e2, *e3; expr_ref arg1(m), arg2(m); context& ctx = get_context(); if (m_util.str.is_concat(e, e1, e2)) { arg1 = try_expand(e1, deps); arg2 = try_expand(e2, deps); if (!arg1 || !arg2) return true; result = mk_concat(arg1, arg2); } else if (m_util.str.is_empty(e) || m_util.str.is_string(e)) { result = e; } else if (m_util.str.is_prefix(e, e1, e2)) { arg1 = try_expand(e1, deps); arg2 = try_expand(e2, deps); if (!arg1 || !arg2) return true; result = m_util.str.mk_prefix(arg1, arg2); } else if (m_util.str.is_suffix(e, e1, e2)) { arg1 = try_expand(e1, deps); arg2 = try_expand(e2, deps); if (!arg1 || !arg2) return true; result = m_util.str.mk_suffix(arg1, arg2); } else if (m_util.str.is_contains(e, e1, e2)) { arg1 = try_expand(e1, deps); arg2 = try_expand(e2, deps); if (!arg1 || !arg2) return true; result = m_util.str.mk_contains(arg1, arg2); } else if (m_util.str.is_unit(e, e1)) { arg1 = try_expand(e1, deps); if (!arg1) return true; result = m_util.str.mk_unit(arg1); } else if (m_util.str.is_index(e, e1, e2)) { arg1 = try_expand(e1, deps); arg2 = try_expand(e2, deps); if (!arg1 || !arg2) return true; result = m_util.str.mk_index(arg1, arg2, m_autil.mk_int(0)); } else if (m_util.str.is_index(e, e1, e2, e3)) { arg1 = try_expand(e1, deps); arg2 = try_expand(e2, deps); if (!arg1 || !arg2) return true; result = m_util.str.mk_index(arg1, arg2, e3); } else if (m_util.str.is_last_index(e, e1, e2)) { arg1 = try_expand(e1, deps); arg2 = try_expand(e2, deps); if (!arg1 || !arg2) return true; result = m_util.str.mk_last_index(arg1, arg2); } else if (m.is_ite(e, e1, e2, e3)) { literal lit(mk_literal(e1)); switch (ctx.get_assignment(lit)) { case l_true: deps = m_dm.mk_join(deps, m_dm.mk_leaf(assumption(lit))); result = try_expand(e2, deps); if (!result) return true; break; case l_false: deps = m_dm.mk_join(deps, m_dm.mk_leaf(assumption(~lit))); result = try_expand(e3, deps); if (!result) return true; break; case l_undef: ctx.mark_as_relevant(lit); m_new_propagation = true; TRACE("seq", tout << "undef: " << mk_bounded_pp(e, m, 2) << "\n"; tout << lit << "@ level: " << ctx.get_scope_level() << "\n";); return false; } } else if (m_util.str.is_itos(e, e1)) { rational val; TRACE("seq", tout << mk_bounded_pp(e, m, 2) << "\n";); if (get_num_value(e1, val)) { TRACE("seq", tout << mk_bounded_pp(e, m, 2) << " -> " << val << "\n";); expr_ref num(m), res(m); num = m_autil.mk_numeral(val, true); if (!ctx.e_internalized(num)) { ctx.internalize(num, false); } enode* n1 = ctx.get_enode(num); enode* n2 = ctx.get_enode(e1); res = m_util.str.mk_string(symbol(val.to_string().c_str())); #if 1 if (val.is_neg()) { result = e; } // TBD remove this: using roots is unsound for propagation. else if (n1->get_root() == n2->get_root()) { result = res; deps = m_dm.mk_join(deps, m_dm.mk_leaf(assumption(n1, n2))); } else { TRACE("seq", tout << "mk equalities\n";); literal l1 = mk_preferred_eq(num, e1); literal l2 = mk_preferred_eq(e, res); TRACE("seq", tout << "add axiom " << l1 << " " << l2 << "\n";); add_axiom(l1, ~l2); add_axiom(~l1, l2); result = e; } #else TRACE("seq", tout << "add axiom\n";); add_axiom(~mk_eq(num, e1, false), mk_eq(e, res, false)); add_axiom(mk_eq(num, e1, false), ~mk_eq(e, res, false)); result = e; #endif } else { result = e; } } else { result = e; } if (result == e0) { deps = nullptr; } expr_dep edr(result, deps); m_rep.add_cache(e0, edr); eqs = m_dm.mk_join(eqs, deps); TRACE("seq_verbose", tout << mk_pp(e0, m) << " |--> " << result << "\n"; if (eqs) display_deps(tout, eqs);); return true; } void theory_seq::add_dependency(dependency*& dep, enode* a, enode* b) { if (a != b) { dep = m_dm.mk_join(dep, m_dm.mk_leaf(assumption(a, b))); } } void theory_seq::propagate() { context & ctx = get_context(); while (m_axioms_head < m_axioms.size() && !ctx.inconsistent()) { expr_ref e(m); e = m_axioms[m_axioms_head].get(); deque_axiom(e); ++m_axioms_head; } while (!m_replay.empty() && !ctx.inconsistent()) { apply* app = m_replay[m_replay.size() - 1]; TRACE("seq", tout << "replay at level: " << ctx.get_scope_level() << "\n";); (*app)(*this); m_replay.pop_back(); } if (m_new_solution) { simplify_and_solve_eqs(); m_new_solution = false; } } void theory_seq::enque_axiom(expr* e) { if (!m_axiom_set.contains(e)) { TRACE("seq", tout << "add axiom " << mk_bounded_pp(e, m) << "\n";); m_axioms.push_back(e); m_axiom_set.insert(e); m_trail_stack.push(push_back_vector(m_axioms)); m_trail_stack.push(insert_obj_trail(m_axiom_set, e));; } } void theory_seq::deque_axiom(expr* n) { TRACE("seq", tout << "deque: " << mk_bounded_pp(n, m, 2) << "\n";); if (m_util.str.is_length(n)) { add_length_axiom(n); } else if (m_util.str.is_empty(n) && !has_length(n) && !m_has_length.empty()) { add_length_to_eqc(n); } else if (m_util.str.is_index(n)) { add_indexof_axiom(n); } else if (m_util.str.is_last_index(n)) { add_last_indexof_axiom(n); } else if (m_util.str.is_replace(n)) { add_replace_axiom(n); } else if (m_util.str.is_extract(n)) { add_extract_axiom(n); } else if (m_util.str.is_at(n)) { add_at_axiom(n); } else if (m_util.str.is_nth_i(n)) { add_nth_axiom(n); } else if (m_util.str.is_string(n)) { add_elim_string_axiom(n); } else if (m_util.str.is_itos(n)) { add_itos_axiom(n); } else if (m_util.str.is_stoi(n)) { add_stoi_axiom(n); } else if (m_util.str.is_lt(n)) { add_lt_axiom(n); } else if (m_util.str.is_le(n)) { add_le_axiom(n); } else if (m_util.str.is_unit(n)) { add_unit_axiom(n); } } /* encode that s is not contained in of xs1 where s1 is all of s, except the last element. s = "" or s = s1*(unit c) s = "" or !contains(x*s1, s) */ void theory_seq::tightest_prefix(expr* s, expr* x) { expr_ref s1 = mk_first(s); expr_ref c = mk_last(s); expr_ref s1c = mk_concat(s1, m_util.str.mk_unit(c)); literal s_eq_emp = mk_eq_empty(s); add_axiom(s_eq_emp, mk_seq_eq(s, s1c)); add_axiom(s_eq_emp, ~mk_literal(m_util.str.mk_contains(mk_concat(x, s1), s))); } /* [[str.indexof]](w, w2, i) is the smallest n such that for some some w1, w3 - w = w1w2w3 - i <= n = |w1| if [[str.contains]](w, w2) = true, |w2| > 0 and i >= 0. [[str.indexof]](w,w2,i) = -1 otherwise. let i = Index(t, s, offset): // index of s in t starting at offset. |t| = 0 => |s| = 0 or indexof(t,s,offset) = -1 |t| = 0 & |s| = 0 => indexof(t,s,offset) = 0 offset >= len(t) => |s| = 0 or i = -1 len(t) != 0 & !contains(t, s) => i = -1 offset = 0 & len(t) != 0 & contains(t, s) => t = xsy & i = len(x) tightest_prefix(x, s) 0 <= offset < len(t) => xy = t & len(x) = offset & (-1 = indexof(y, s, 0) => -1 = i) & (indexof(y, s, 0) >= 0 => indexof(t, s, 0) + offset = i) offset < 0 => i = -1 optional lemmas: (len(s) > len(t) -> i = -1) (len(s) <= len(t) -> i <= len(t)-len(s)) */ void theory_seq::add_indexof_axiom(expr* i) { expr* s = nullptr, *t = nullptr, *offset = nullptr; rational r; VERIFY(m_util.str.is_index(i, t, s) || m_util.str.is_index(i, t, s, offset)); expr_ref minus_one(m_autil.mk_int(-1), m); expr_ref zero(m_autil.mk_int(0), m); expr_ref xsy(m); literal cnt = mk_literal(m_util.str.mk_contains(t, s)); literal i_eq_m1 = mk_eq(i, minus_one, false); literal i_eq_0 = mk_eq(i, zero, false); literal s_eq_empty = mk_eq_empty(s); literal t_eq_empty = mk_eq_empty(t); // |t| = 0 => |s| = 0 or indexof(t,s,offset) = -1 // ~contains(t,s) <=> indexof(t,s,offset) = -1 add_axiom(cnt, i_eq_m1); // add_axiom(~cnt, ~i_eq_m1); add_axiom(~t_eq_empty, s_eq_empty, i_eq_m1); if (!offset || (m_autil.is_numeral(offset, r) && r.is_zero())) { expr_ref x = mk_skolem(m_indexof_left, t, s); expr_ref y = mk_skolem(m_indexof_right, t, s); xsy = mk_concat(x, s, y); expr_ref lenx = mk_len(x); // |s| = 0 => indexof(t,s,0) = 0 // contains(t,s) & |s| != 0 => t = xsy & indexof(t,s,0) = |x| add_axiom(~s_eq_empty, i_eq_0); add_axiom(~cnt, s_eq_empty, mk_seq_eq(t, xsy)); add_axiom(~cnt, s_eq_empty, mk_eq(i, lenx, false)); add_axiom(~cnt, mk_literal(m_autil.mk_ge(i, zero))); tightest_prefix(s, x); } else { // offset >= len(t) => |s| = 0 or indexof(t, s, offset) = -1 // offset > len(t) => indexof(t, s, offset) = -1 // offset = len(t) & |s| = 0 => indexof(t, s, offset) = offset expr_ref len_t = mk_len(t); literal offset_ge_len = mk_simplified_literal(m_autil.mk_ge(mk_sub(offset, len_t), zero)); literal offset_le_len = mk_simplified_literal(m_autil.mk_le(mk_sub(offset, len_t), zero)); literal i_eq_offset = mk_eq(i, offset, false); add_axiom(~offset_ge_len, s_eq_empty, i_eq_m1); add_axiom(offset_le_len, i_eq_m1); add_axiom(~offset_ge_len, ~offset_le_len, ~s_eq_empty, i_eq_offset); expr_ref x = mk_skolem(m_indexof_left, t, s, offset); expr_ref y = mk_skolem(m_indexof_right, t, s, offset); expr_ref indexof0(m_util.str.mk_index(y, s, zero), m); expr_ref offset_p_indexof0(m_autil.mk_add(offset, indexof0), m); literal offset_ge_0 = mk_simplified_literal(m_autil.mk_ge(offset, zero)); // 0 <= offset & offset < len(t) => t = xy // 0 <= offset & offset < len(t) => len(x) = offset // 0 <= offset & offset < len(t) & -1 = indexof(y,s,0) = -1 => -1 = i // 0 <= offset & offset < len(t) & indexof(y,s,0) >= 0 = -1 => // -1 = indexof(y,s,0) + offset = indexof(t, s, offset) add_axiom(~offset_ge_0, offset_ge_len, mk_seq_eq(t, mk_concat(x, y))); add_axiom(~offset_ge_0, offset_ge_len, mk_eq(mk_len(x), offset, false)); add_axiom(~offset_ge_0, offset_ge_len, ~mk_eq(indexof0, minus_one, false), i_eq_m1); add_axiom(~offset_ge_0, offset_ge_len, ~mk_simplified_literal(m_autil.mk_ge(indexof0, zero)), mk_eq(offset_p_indexof0, i, false)); // offset < 0 => -1 = i add_axiom(offset_ge_0, i_eq_m1); } } /** !contains(t, s) => i = -1 |t| = 0 => |s| = 0 or i = -1 |t| = 0 & |s| = 0 => i = 0 |t| != 0 & contains(t, s) => t = xsy & i = len(x) |s| = 0 or s = s_head*s_tail |s| = 0 or !contains(s_tail*y, s) */ void theory_seq::add_last_indexof_axiom(expr* i) { expr* s = nullptr, *t = nullptr; VERIFY(m_util.str.is_last_index(i, t, s)); expr_ref minus_one(m_autil.mk_int(-1), m); expr_ref zero(m_autil.mk_int(0), m); expr_ref s_head(m), s_tail(m); expr_ref x = mk_skolem(symbol("seq.last_indexof_left"), t, s); expr_ref y = mk_skolem(symbol("seq.last_indexof_right"), t, s); mk_decompose(s, s_head, s_tail); literal cnt = mk_literal(m_util.str.mk_contains(t, s)); literal cnt2 = mk_literal(m_util.str.mk_contains(mk_concat(s_tail, y), s)); literal i_eq_m1 = mk_eq(i, minus_one, false); literal i_eq_0 = mk_eq(i, zero, false); literal s_eq_empty = mk_eq_empty(s); literal t_eq_empty = mk_eq_empty(t); expr_ref xsy = mk_concat(x, s, y); add_axiom(cnt, i_eq_m1); add_axiom(~t_eq_empty, s_eq_empty, i_eq_m1); add_axiom(~t_eq_empty, ~s_eq_empty, i_eq_0); add_axiom(t_eq_empty, ~cnt, mk_seq_eq(t, xsy)); add_axiom(t_eq_empty, ~cnt, mk_eq(i, mk_len(x), false)); add_axiom(s_eq_empty, mk_eq(s, mk_concat(s_head, s_tail), false)); add_axiom(s_eq_empty, ~cnt2); } /* let r = replace(a, s, t) a = "" => s = "" or r = a contains(a, s) or r = a s = "" => r = t+a tightest_prefix(s, x) (contains(a, s) -> r = xty & a = xsy) & (!contains(a, s) -> r = a) */ void theory_seq::add_replace_axiom(expr* r) { context& ctx = get_context(); expr* a = nullptr, *s = nullptr, *t = nullptr; VERIFY(m_util.str.is_replace(r, a, s, t)); expr_ref x = mk_skolem(m_indexof_left, a, s); expr_ref y = mk_skolem(m_indexof_right, a, s); expr_ref xty = mk_concat(x, t, y); expr_ref xsy = mk_concat(x, s, y); literal a_emp = mk_eq_empty(a, true); literal s_emp = mk_eq_empty(s, true); literal cnt = mk_literal(m_util.str.mk_contains(a, s)); add_axiom(~a_emp, s_emp, mk_seq_eq(r, a)); add_axiom(cnt, mk_seq_eq(r, a)); add_axiom(~s_emp, mk_seq_eq(r, mk_concat(t, a))); add_axiom(~cnt, a_emp, s_emp, mk_seq_eq(a, xsy)); add_axiom(~cnt, a_emp, s_emp, mk_seq_eq(r, xty)); ctx.force_phase(cnt); tightest_prefix(s, x); } expr_ref theory_seq::add_elim_string_axiom(expr* n) { zstring s; TRACE("seq", tout << mk_pp(n, m) << "\n";); VERIFY(m_util.str.is_string(n, s)); if (s.length() == 0) { return expr_ref(n, m); } expr_ref result(m_util.str.mk_unit(m_util.str.mk_char(s, s.length()-1)), m); for (unsigned i = s.length()-1; i-- > 0; ) { result = mk_concat(m_util.str.mk_unit(m_util.str.mk_char(s, i)), result); } add_axiom(mk_eq(n, result, false)); m_rep.update(n, result, nullptr); m_new_solution = true; return result; } /* let n = len(x) - len(a ++ b) = len(a) + len(b) if x = a ++ b - len(unit(u)) = 1 if x = unit(u) - len(str) = str.length() if x = str - len(empty) = 0 if x = empty - len(int.to.str(i)) >= 1 if x = int.to.str(i) and more generally if i = 0 then 1 else 1+floor(log(|i|)) - len(x) >= 0 otherwise */ void theory_seq::add_length_axiom(expr* n) { context& ctx = get_context(); expr* x = nullptr; VERIFY(m_util.str.is_length(n, x)); if (m_util.str.is_concat(x) || m_util.str.is_unit(x) || m_util.str.is_empty(x) || m_util.str.is_string(x)) { expr_ref len(n, m); m_rewrite(len); SASSERT(n != len); add_axiom(mk_eq(len, n, false)); } 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)))); } if (!ctx.at_base_level()) { m_trail_stack.push(push_replay(alloc(replay_axiom, m, n))); } } void theory_seq::add_itos_length_axiom(expr* len) { expr* x = nullptr, *n = nullptr; VERIFY(m_util.str.is_length(len, x)); VERIFY(m_util.str.is_itos(x, n)); unsigned num_char1 = 1, num_char2 = 1; rational len1, len2; rational ten(10); if (get_num_value(n, len1)) { if (len1.is_neg()) { return; } // 0 <= x < 10 // 10 <= x < 100 // 100 <= x < 1000 rational upper(10); while (len1 > upper) { ++num_char1; upper *= ten; } SASSERT(len1 <= upper); } if (get_num_value(len, len2) && len2.is_unsigned()) { num_char2 = len2.get_unsigned(); } unsigned num_char = std::max(num_char1, num_char2); literal len_le(mk_literal(m_autil.mk_le(len, m_autil.mk_int(num_char)))); literal len_ge(mk_literal(m_autil.mk_ge(len, m_autil.mk_int(num_char)))); literal n_ge_0(mk_literal(m_autil.mk_ge(n, m_autil.mk_int(0)))); add_axiom(~n_ge_0, mk_literal(m_autil.mk_ge(len, m_autil.mk_int(1)))); if (num_char == 1) { literal n_ge_10(mk_literal(m_autil.mk_ge(n, m_autil.mk_int(10)))); add_axiom(~n_ge_0, n_ge_10, len_le); add_axiom(~len_le, ~n_ge_10); return; } rational hi(1); for (unsigned i = 2; i < num_char; ++i) { hi *= ten; } // n >= hi*10 <=> len >= num_chars // n < 100*hi <=> len <= num_chars literal n_ge_10hi = mk_literal(m_autil.mk_ge(n, m_autil.mk_numeral(ten*hi, true))); literal n_ge_100hi = mk_literal(m_autil.mk_ge(n, m_autil.mk_numeral(ten*ten*hi, true))); add_axiom(~n_ge_10hi, len_ge); add_axiom(n_ge_10hi, ~len_ge); add_axiom(n_ge_100hi, len_le); add_axiom(~n_ge_100hi, ~len_le); } void theory_seq::propagate_in_re(expr* n, bool is_true) { TRACE("seq", tout << mk_pp(n, m) << " <- " << (is_true?"true":"false") << "\n";); expr_ref tmp(n, m); m_rewrite(tmp); if (m.is_true(tmp)) { if (!is_true) { literal_vector lits; lits.push_back(mk_literal(n)); set_conflict(nullptr, lits); } return; } else if (m.is_false(tmp)) { if (is_true) { literal_vector lits; lits.push_back(~mk_literal(n)); set_conflict(nullptr, lits); } return; } 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) { re = m_util.re.mk_complement(re); lit.neg(); } 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) && entry.m_re != re) { m_trail_stack.push(vector_value_trail(m_s_in_re, i)); m_s_in_re[i].m_active = false; 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_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, re, a)); m_trail_stack.push(push_back_vector>(m_s_in_re)); expr_ref len = mk_len(s); expr_ref zero(m_autil.mk_int(0), m); unsigned_vector states; a->get_epsilon_closure(a->init(), states); lits.push_back(~lit); expr_ref_vector exprs(m); for (unsigned st : states) { literal acc = mk_accept(s, zero, re, st); lits.push_back(acc); exprs.push_back(ctx.bool_var2expr(acc.var())); } if (lits.size() == 2) { propagate_lit(nullptr, 1, &lit, lits[1]); } else { TRACE("seq", ctx.display_literals_verbose(tout, lits) << "\n";); std::function fn = [&]() { return m.mk_implies(n, m.mk_or(exprs.size(), exprs.c_ptr())); }; scoped_trace_stream _sts(*this, fn); ctx.mk_th_axiom(get_id(), lits.size(), lits.c_ptr()); } } expr_ref theory_seq::mk_sub(expr* a, expr* b) { expr_ref result(m_autil.mk_sub(a, b), m); m_rewrite(result); return result; } expr_ref theory_seq::mk_add(expr* a, expr* b) { expr_ref result(m_autil.mk_add(a, b), m); m_rewrite(result); return result; } expr_ref theory_seq::mk_len(expr* s) { expr_ref result(m_util.str.mk_length(s), m); m_rewrite(result); return result; } enode* theory_seq::ensure_enode(expr* e) { context& ctx = get_context(); if (!ctx.e_internalized(e)) { ctx.internalize(e, false); } enode* n = ctx.get_enode(e); ctx.mark_as_relevant(n); return n; } template static T* get_th_arith(context& ctx, theory_id afid, expr* e) { theory* th = ctx.get_theory(afid); if (th && ctx.e_internalized(e)) { return dynamic_cast(th); } else { return nullptr; } } bool theory_seq::get_num_value(expr* e, rational& val) const { return m_arith_value.get_value_equiv(e, val) && val.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. bool theory_seq::lower_bound2(expr* _e, rational& lo) { context& ctx = get_context(); expr_ref e = mk_len(_e); expr_ref _lo(m); theory_mi_arith* tha = get_th_arith(ctx, m_autil.get_family_id(), e); if (!tha) { theory_i_arith* thi = get_th_arith(ctx, m_autil.get_family_id(), e); if (!thi || !thi->get_lower(ctx.get_enode(e), _lo) || !m_autil.is_numeral(_lo, lo)) return false; } enode *ee = ctx.get_enode(e); if (tha && (!tha->get_lower(ee, _lo) || m_autil.is_numeral(_lo, lo))) { enode *next = ee->get_next(); bool flag = false; while (next != ee) { if (!m_autil.is_numeral(next->get_owner()) && !m_util.str.is_length(next->get_owner())) { expr *var = next->get_owner(); TRACE("seq_verbose", tout << mk_pp(var, m) << "\n";); expr_ref _lo2(m); rational lo2; if (tha->get_lower(next, _lo2) && m_autil.is_numeral(_lo2, lo2) && lo2>lo) { flag = true; lo = lo2; literal low(mk_literal(m_autil.mk_ge(var, _lo2))); add_axiom(~low, mk_literal(m_autil.mk_ge(e, _lo2))); } } next = next->get_next(); } if (flag) return true; if (!tha->get_lower(ee, _lo)) return false; } return true; } bool theory_seq::get_length(expr* e, rational& val) { rational val1; expr_ref len(m), len_val(m); expr* e1 = nullptr, *e2 = nullptr; ptr_vector todo; todo.push_back(e); val.reset(); zstring s; while (!todo.empty()) { expr* c = todo.back(); todo.pop_back(); if (m_util.str.is_concat(c, e1, e2)) { todo.push_back(e1); todo.push_back(e2); } else if (m_util.str.is_unit(c)) { val += rational(1); } else if (m_util.str.is_empty(c)) { continue; } else if (m_util.str.is_string(c, s)) { val += rational(s.length()); } else if (!has_length(c)) { TRACE("seq", tout << "literal has no length " << mk_pp(c, m) << "\n";); return false; } else { len = mk_len(c); if (m_arith_value.get_value(len, val1)) { val += val1; } else { TRACE("seq", tout << "length has not been internalized " << mk_pp(c, m) << "\n";); return false; } } } CTRACE("seq", !val.is_int(), tout << "length is not an integer\n";); return val.is_int(); } /* let e = extract(s, i, l) i is start index, l is length of substring starting at index. i < 0 => e = "" i >= |s| => e = "" l <= 0 => e = "" 0 <= i < |s| & l > 0 => s = xey, |x| = i, |e| = min(l, |s|-i) this translates to: 0 <= i <= |s| -> s = xey 0 <= i <= |s| -> len(x) = i 0 <= i <= |s| & 0 <= l <= |s| - i -> |e| = l 0 <= i <= |s| & |s| < l + i -> |e| = |s| - i |e| = 0 <=> i < 0 | |s| <= i | l <= 0 | |s| <= 0 It follows that: |e| = min(l, |s| - i) for 0 <= i < |s| and 0 < |l| */ void theory_seq::add_extract_axiom(expr* e) { TRACE("seq", tout << mk_bounded_pp(e, m, 2) << "\n";); expr* s = nullptr, *i = nullptr, *l = nullptr; VERIFY(m_util.str.is_extract(e, s, i, l)); if (is_tail(s, i, l)) { add_tail_axiom(e, s); return; } if (is_drop_last(s, i, l)) { add_drop_last_axiom(e, s); return; } if (is_extract_prefix0(s, i, l)) { add_extract_prefix_axiom(e, s, l); return; } if (is_extract_suffix(s, i, l)) { 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); expr_ref le = mk_len(e); expr_ref ls_minus_i_l(mk_sub(mk_sub(ls, i), l), m); expr_ref y(mk_skolem(m_post, s, ls_minus_i_l), m); expr_ref xe = mk_concat(x, e); expr_ref xey = mk_concat(x, e, y); expr_ref zero(m_autil.mk_int(0), m); literal i_ge_0 = mk_simplified_literal(m_autil.mk_ge(i, zero)); literal i_le_ls = mk_simplified_literal(m_autil.mk_le(mk_sub(i, ls), zero)); literal ls_le_i = mk_simplified_literal(m_autil.mk_le(mk_sub(ls, i), zero)); literal li_ge_ls = mk_simplified_literal(m_autil.mk_ge(ls_minus_i_l, zero)); literal l_ge_0 = mk_simplified_literal(m_autil.mk_ge(l, zero)); literal l_le_0 = mk_simplified_literal(m_autil.mk_le(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, ~i_le_ls, mk_seq_eq(xey, s)); add_axiom(~i_ge_0, ~i_le_ls, mk_eq(lx, i, false)); add_axiom(~i_ge_0, ~i_le_ls, ~l_ge_0, ~li_ge_ls, mk_eq(le, l, false)); add_axiom(~i_ge_0, ~i_le_ls, li_ge_ls, mk_eq(le, mk_sub(ls, i), false)); add_axiom(~i_ge_0, ~i_le_ls, l_ge_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); add_axiom(~l_le_0, le_is_0); add_axiom(~le_is_0, ~i_ge_0, ls_le_i, ls_le_0, l_le_0); } void theory_seq::add_tail_axiom(expr* e, expr* s) { expr_ref head(m), tail(m); mk_decompose(s, head, tail); TRACE("seq", tout << "tail " << mk_bounded_pp(e, m, 2) << " " << mk_bounded_pp(s, m, 2) << "\n";); 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) { TRACE("seq", tout << "drop last " << mk_bounded_pp(e, m, 2) << " " << mk_bounded_pp(s, m, 2) << "\n";); 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) { rational i1; if (!m_autil.is_numeral(i, i1) || !i1.is_zero()) { return false; } expr_ref l2(m), l1(l, m); l2 = mk_sub(mk_len(s), m_autil.mk_int(1)); m_rewrite(l1); m_rewrite(l2); return l1 == l2; } bool theory_seq::is_tail(expr* s, expr* i, expr* l) { rational i1; if (!m_autil.is_numeral(i, i1) || !i1.is_one()) { return false; } expr_ref l2(m), l1(l, m); l2 = mk_sub(mk_len(s), m_autil.mk_int(1)); m_rewrite(l1); m_rewrite(l2); return l1 == l2; } bool theory_seq::is_extract_prefix0(expr* s, expr* i, expr* l) { rational i1; return m_autil.is_numeral(i, i1) && i1.is_zero(); } bool theory_seq::is_extract_suffix(expr* s, expr* i, expr* l) { expr_ref len(m_autil.mk_add(l, i), m); m_rewrite(len); return m_util.str.is_length(len, l) && l == s; } /* 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 << "prefix " << mk_bounded_pp(e, m, 2) << " " << mk_bounded_pp(s, m, 2) << " " << mk_bounded_pp(l, m, 2) << "\n";); expr_ref le = mk_len(e); expr_ref ls = mk_len(s); expr_ref ls_minus_l(mk_sub(ls, l), m); expr_ref y(mk_skolem(m_post, s, ls_minus_l), m); expr_ref zero(m_autil.mk_int(0), m); expr_ref ey = mk_concat(e, y); literal l_ge_0 = mk_simplified_literal(m_autil.mk_ge(l, zero)); literal l_le_s = mk_simplified_literal(m_autil.mk_le(mk_sub(l, ls), zero)); add_axiom(~l_ge_0, ~l_le_s, mk_seq_eq(s, ey)); 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 => e = empty i > len(s) => e = empty */ void theory_seq::add_extract_suffix_axiom(expr* e, expr* s, expr* i) { TRACE("seq", tout << "suffix " << mk_bounded_pp(e, m, 2) << " " << mk_bounded_pp(s, m, 2) << "\n";); expr_ref x(mk_skolem(m_pre, s, i), m); expr_ref lx = mk_len(x); 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_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)); add_axiom(~i_ge_0, ~i_le_s, mk_eq(i, lx, false)); add_axiom(i_ge_0, le_is_0); add_axiom(i_le_s, le_is_0); } /* let e = at(s, i) 0 <= i < len(s) -> s = xey & len(x) = i & len(e) = 1 i < 0 \/ i >= len(s) -> e = empty */ void theory_seq::add_at_axiom(expr* e) { TRACE("seq", tout << "at-axiom: " << get_context().get_scope_level() << " " << mk_bounded_pp(e, m) << "\n";); expr* s = nullptr, *i = nullptr; VERIFY(m_util.str.is_at(e, s, i)); expr_ref zero(m_autil.mk_int(0), m); expr_ref one(m_autil.mk_int(1), m); 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)); rational iv; if (m_autil.is_numeral(i, iv) && iv.is_unsigned()) { 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)); } /** \brief i >= 0 i < len(s) => unit(nth_i(s, i)) = at(s, i) nth_i(unit(nth_i(s, i)), 0) = nth_i(s, i) */ void theory_seq::add_nth_axiom(expr* e) { expr* s = nullptr, *i = nullptr; rational n; zstring str; VERIFY(m_util.str.is_nth_i(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)); } else { expr_ref zero(m_autil.mk_int(0), m); 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)); // at(s,i) = [nth(s,i)] expr_ref rhs(s, m); expr_ref lhs(m_util.str.mk_unit(e), m); if (!m_util.str.is_at(s) || zero != i) rhs = m_util.str.mk_at(s, i); add_axiom(~i_ge_0, i_ge_len_s, mk_eq(lhs, rhs, 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_bounded_pp(s, m, 2) << " " << mk_bounded_pp(idx, m, 2) << "\n";); rational r; SASSERT(get_context().get_assignment(lit) == l_true); VERIFY(m_autil.is_numeral(idx, r) && r.is_unsigned()); unsigned _idx = r.get_unsigned(); expr_ref head(m), tail(m), conc(m), len1(m), len2(m); expr_ref_vector elems(m); expr* s2 = s; for (unsigned j = 0; j <= _idx; ++j) { mk_decompose(s2, head, tail); elems.push_back(head); len1 = mk_len(s2); len2 = m_autil.mk_add(m_autil.mk_int(1), mk_len(tail)); propagate_eq(lit, len1, len2, false); s2 = tail; } elems.push_back(s2); conc = mk_concat(elems, m.get_sort(s)); propagate_eq(lit, s, conc, true); } literal theory_seq::mk_simplified_literal(expr * _e) { expr_ref e(_e, m); m_rewrite(e); return mk_literal(e); } literal theory_seq::mk_literal(expr* _e) { expr_ref e(_e, m); context& ctx = get_context(); ensure_enode(e); return ctx.get_literal(e); } literal theory_seq::mk_seq_eq(expr* a, expr* b) { SASSERT(m_util.is_seq(a)); return mk_literal(mk_skolem(m_eq, a, b, nullptr, nullptr, m.mk_bool_sort())); } literal theory_seq::mk_preferred_eq(expr* a, expr* b) { context& ctx = get_context(); ctx.assume_eq(ensure_enode(a), ensure_enode(b)); literal lit = mk_eq(a, b, false); ctx.force_phase(lit); return lit; } literal theory_seq::mk_eq_empty(expr* _e, bool phase) { context& ctx = get_context(); expr_ref e(_e, m); SASSERT(m_util.is_seq(e)); expr_ref emp(m); zstring s; if (m_util.str.is_empty(e)) { return true_literal; } expr_ref_vector concats(m); m_util.str.get_concat(e, concats); for (auto c : concats) { if (m_util.str.is_unit(c)) { return false_literal; } if (m_util.str.is_string(c, s) && s.length() > 0) { return false_literal; } } emp = m_util.str.mk_empty(m.get_sort(e)); literal lit = mk_eq(e, emp, false); ctx.force_phase(phase?lit:~lit); ctx.mark_as_relevant(lit); return lit; } void theory_seq::push_lit_as_expr(literal l, expr_ref_vector& buf) { expr* e = get_context().bool_var2expr(l.var()); if (l.sign()) e = m.mk_not(e); buf.push_back(e); } void theory_seq::add_axiom(literal l1, literal l2, literal l3, literal l4, literal l5) { context& ctx = get_context(); literal_vector lits; expr_ref_vector exprs(m); if (l1 == true_literal || l2 == true_literal || l3 == true_literal || l4 == true_literal || l5 == true_literal) return; if (l1 != null_literal && l1 != false_literal) { ctx.mark_as_relevant(l1); lits.push_back(l1); push_lit_as_expr(l1, exprs); } if (l2 != null_literal && l2 != false_literal) { ctx.mark_as_relevant(l2); lits.push_back(l2); push_lit_as_expr(l2, exprs); } if (l3 != null_literal && l3 != false_literal) { ctx.mark_as_relevant(l3); lits.push_back(l3); push_lit_as_expr(l3, exprs); } if (l4 != null_literal && l4 != false_literal) { ctx.mark_as_relevant(l4); lits.push_back(l4); push_lit_as_expr(l4, exprs); } if (l5 != null_literal && l5 != false_literal) { ctx.mark_as_relevant(l5); lits.push_back(l5); push_lit_as_expr(l5, exprs); } TRACE("seq", ctx.display_literals_verbose(tout << "assert:", lits) << "\n";); m_new_propagation = true; ++m_stats.m_add_axiom; { std::function fn = [&]() { return m.mk_or(exprs.size(), exprs.c_ptr()); }; scoped_trace_stream _sts(*this, fn); ctx.mk_th_axiom(get_id(), lits.size(), lits.c_ptr()); } #if 0 if (!ctx.at_base_level() && l2 == null_literal) { m_trail_stack.push(push_replay(alloc(replay_unit_literal, m, ctx.bool_var2expr(l1.var()), l1.sign()))); } #endif } 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 rs(m), concats(m); m_util.str.get_concat(e, concats); for (unsigned i = 0; i < concats.size(); ++i) { expr_ref tmp(coalesce_chars(concats.get(i)), m); if (m_util.str.is_empty(tmp)) continue; zstring zs, a; bool flag = false; while (m_util.str.is_string(tmp, a)) { if (flag) zs = zs + a; else zs = a; flag = true; if (i < concats.size()-1) tmp = coalesce_chars(concats[++i].get()); else { ++i; break; } } if (flag) { rs.push_back(m_util.str.mk_string(zs)); if (i < concats.size()) rs.push_back(tmp); } else rs.push_back(tmp); } 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; } } 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) { expr* es[4] = { e1, e2, e3, e4 }; unsigned len = e4?4:(e3?3:(e2?2:1)); if (!range) { range = m.get_sort(e1); } expr_ref_vector pinned(m); if (name == m_seq_align) { for (unsigned i = 0; i < len; ++i) { pinned.push_back(coalesce_chars(es[i])); es[i] = pinned.back(); TRACE("seq", tout << mk_pp(es[i], m) << "\n";); } } return expr_ref(m_util.mk_skolem(name, len, es, range), m); } bool theory_seq::is_skolem(symbol const& s, expr* e) const { return m_util.is_skolem(e) && to_app(e)->get_decl()->get_parameter(0).get_symbol() == s; } theory_seq::dependency* theory_seq::mk_join(dependency* deps, literal lit) { return m_dm.mk_join(deps, m_dm.mk_leaf(assumption(lit))); } theory_seq::dependency* theory_seq::mk_join(dependency* deps, literal_vector const& lits) { for (literal l : lits) { deps = mk_join(deps, l); } return deps; } bool theory_seq::propagate_eq(literal lit, expr* e1, expr* e2, bool add_to_eqs) { literal_vector lits; lits.push_back(lit); return propagate_eq(nullptr, lits, e1, e2, add_to_eqs); } bool theory_seq::propagate_eq(dependency* deps, literal_vector const& _lits, expr* e1, expr* e2, bool add_to_eqs) { context& ctx = get_context(); enode* n1 = ensure_enode(e1); enode* n2 = ensure_enode(e2); if (n1->get_root() == n2->get_root()) { return false; } ctx.mark_as_relevant(n1); ctx.mark_as_relevant(n2); literal_vector lits(_lits); enode_pair_vector eqs; if (!linearize(deps, eqs, lits)) { IF_VERBOSE(10, verbose_stream() << "not linearized " << mk_bounded_pp(e1, m, 2) << " " << mk_bounded_pp(e2, m, 2) << "\n"); return false; } if (add_to_eqs) { deps = mk_join(deps, _lits); new_eq_eh(deps, n1, n2); } TRACE("seq_verbose", tout << "assert: #" << e1->get_id() << " " << mk_pp(e1, m) << " = " << mk_pp(e2, m) << " <- \n"; if (!lits.empty()) { ctx.display_literals_verbose(tout, lits) << "\n"; }); TRACE("seq", tout << "assert:" << mk_bounded_pp(e1, m, 2) << " = " << mk_bounded_pp(e2, m, 2) << " <- \n"; tout << lits << "\n"; tout << "#" << e1->get_id() << "\n"; ); justification* js = ctx.mk_justification( ext_theory_eq_propagation_justification( get_id(), ctx.get_region(), lits.size(), lits.c_ptr(), eqs.size(), eqs.c_ptr(), n1, n2)); m_new_propagation = true; std::function fn = [&]() { return m.mk_eq(e1, e2); }; scoped_trace_stream _sts(*this, fn); ctx.assign_eq(n1, n2, eq_justification(js)); return true; } void theory_seq::assign_eh(bool_var v, bool is_true) { context & ctx = get_context(); expr* e = ctx.bool_var2expr(v); expr* e1 = nullptr, *e2 = nullptr; expr_ref f(m); literal lit(v, !is_true); TRACE("seq", tout << (is_true?"":"not ") << mk_bounded_pp(e, m) << "\n";); if (m_util.str.is_prefix(e, e1, e2)) { if (is_true) { f = mk_skolem(m_prefix, e1, e2); f = mk_concat(e1, f); propagate_eq(lit, f, e2, true); } else { propagate_not_prefix(e); } } else if (m_util.str.is_suffix(e, e1, e2)) { if (is_true) { f = mk_skolem(m_suffix, e1, e2); f = mk_concat(f, e1); propagate_eq(lit, f, e2, true); } else { propagate_not_suffix(e); } } else if (m_util.str.is_contains(e, e1, e2)) { expr_ref_vector disj(m); // disabled pending regression on issue 1196 if (false && m_seq_rewrite.reduce_contains(e1, e2, disj)) { literal_vector lits; literal lit = mk_literal(e); lits.push_back(~lit); for (expr* d : disj) { lits.push_back(mk_literal(d)); } ++m_stats.m_add_axiom; { std::function fn = [&]() { return m.mk_implies(e, m.mk_or(disj.size(), disj.c_ptr())); }; scoped_trace_stream _sts(*this, fn); ctx.mk_th_axiom(get_id(), lits.size(), lits.c_ptr()); } for (expr* d : disj) { add_axiom(lit, ~mk_literal(d)); } } else if (is_true) { expr_ref f1 = mk_skolem(m_indexof_left, e1, e2); 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); dependency* dep = m_dm.mk_leaf(assumption(lit)); literal len_gt = mk_simplified_literal(m_autil.mk_le(mk_sub(mk_len(e1), mk_len(e2)), m_autil.mk_int(-1))); ctx.force_phase(len_gt); m_ncs.push_back(nc(expr_ref(e, m), len_gt, dep)); } } else if (is_accept(e)) { if (is_true) { propagate_accept(lit, e); } } else if (is_step(e)) { if (is_true) { propagate_step(lit, e); } } else if (is_eq(e, e1, e2)) { if (is_true) { propagate_eq(lit, e1, e2, true); } } else if (m_util.str.is_in_re(e)) { propagate_in_re(e, is_true); } else if (is_skolem(symbol("seq.is_digit"), e)) { // no-op } else if (is_max_unfolding(e)) { // no-op } else if (m_util.str.is_lt(e) || m_util.str.is_le(e)) { m_lts.push_back(e); } else if (m_util.str.is_nth_i(e) || m_util.str.is_nth_u(e)) { // no-op } else if (m_util.is_skolem(e)) { // no-op } else { TRACE("seq", tout << mk_pp(e, m) << "\n";); UNREACHABLE(); } } void theory_seq::new_eq_eh(theory_var v1, theory_var v2) { enode* n1 = get_enode(v1); enode* n2 = get_enode(v2); dependency* deps = m_dm.mk_leaf(assumption(n1, n2)); 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 << mk_bounded_pp(n1->get_owner(), m) << " = " << mk_bounded_pp(n2->get_owner(), m) << "\n";); if (n1 != n2 && m_util.is_seq(n1->get_owner())) { theory_var v1 = n1->get_th_var(get_id()); theory_var v2 = n2->get_th_var(get_id()); if (m_find.find(v1) == m_find.find(v2)) { return; } m_find.merge(v1, v2); expr_ref o1(n1->get_owner(), m); expr_ref o2(n2->get_owner(), m); TRACE("seq", tout << mk_bounded_pp(o1, m) << " = " << mk_bounded_pp(o2, m) << "\n";); m_eqs.push_back(mk_eqdep(o1, o2, deps)); solve_eqs(m_eqs.size()-1); enforce_length_coherence(n1, n2); } else if (n1 != n2 && m_util.is_re(n1->get_owner())) { // create an expression for the symmetric difference and imply it is empty. enode_pair_vector eqs; literal_vector lits; context& ctx = get_context(); 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"); } } } void theory_seq::new_diseq_eh(theory_var v1, theory_var v2) { enode* n1 = get_enode(v1); enode* n2 = get_enode(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() << ": " << mk_bounded_pp(eq, m, 2) << "\n";); m_rewrite(eq); if (!m.is_false(eq)) { literal lit = mk_eq(e1, e2, false); get_context().mark_as_relevant(lit); if (m_util.str.is_empty(e2)) { std::swap(e1, e2); } dependency* dep = m_dm.mk_leaf(assumption(~lit)); m_nqs.push_back(ne(e1, e2, dep)); if (get_context().get_assignment(lit) != l_undef) { solve_nqs(m_nqs.size() - 1); } } } void theory_seq::push_scope_eh() { theory::push_scope_eh(); m_rep.push_scope(); m_exclude.push_scope(); m_dm.push_scope(); m_trail_stack.push_scope(); m_trail_stack.push(value_trail(m_axioms_head)); m_eqs.push_scope(); m_nqs.push_scope(); m_ncs.push_scope(); m_lts.push_scope(); } void theory_seq::pop_scope_eh(unsigned num_scopes) { context& ctx = get_context(); m_trail_stack.pop_scope(num_scopes); theory::pop_scope_eh(num_scopes); m_dm.pop_scope(num_scopes); m_rep.pop_scope(num_scopes); m_exclude.pop_scope(num_scopes); m_eqs.pop_scope(num_scopes); m_nqs.pop_scope(num_scopes); m_ncs.pop_scope(num_scopes); m_lts.pop_scope(num_scopes); m_rewrite.reset(); if (ctx.get_base_level() > ctx.get_scope_level() - num_scopes) { m_replay.reset(); } if (m_len_prop_lvl > (int) ctx.get_scope_level()) { m_len_prop_lvl = ctx.get_scope_level(); m_len_offset.reset(); } } void theory_seq::restart_eh() { } void theory_seq::relevant_eh(app* n) { if (m_util.str.is_index(n) || m_util.str.is_replace(n) || m_util.str.is_extract(n) || m_util.str.is_at(n) || m_util.str.is_nth_i(n) || m_util.str.is_empty(n) || m_util.str.is_string(n) || m_util.str.is_itos(n) || m_util.str.is_stoi(n) || m_util.str.is_lt(n) || m_util.str.is_unit(n) || m_util.str.is_le(n)) { enque_axiom(n); } if (m_util.str.is_itos(n) || m_util.str.is_stoi(n)) { add_int_string(n); } expr* arg; if (m_util.str.is_length(n, arg) && !has_length(arg) && get_context().e_internalized(arg)) { add_length_to_eqc(arg); } } eautomaton* theory_seq::get_automaton(expr* re) { eautomaton* result = nullptr; if (m_re2aut.find(re, result)) { return result; } if (!m_mk_aut.has_solver()) { m_mk_aut.set_solver(alloc(seq_expr_solver, m, get_context().get_fparams())); } result = m_mk_aut(re); CTRACE("seq", result, { display_expr d(m); result->display(tout, d); }); m_automata.push_back(result); m_re2aut.insert(re, result); m_res.push_back(re); return result; } literal theory_seq::mk_accept(expr* s, expr* idx, expr* re, expr* state) { expr_ref_vector args(m); args.push_back(s).push_back(idx).push_back(re).push_back(state); return mk_literal(m_util.mk_skolem(m_accept, args.size(), args.c_ptr(), m.mk_bool_sort())); } bool theory_seq::is_accept(expr* e, expr*& s, expr*& idx, expr*& re, unsigned& i, eautomaton*& aut) { if (is_accept(e)) { rational r; s = to_app(e)->get_arg(0); idx = to_app(e)->get_arg(1); re = to_app(e)->get_arg(2); TRACE("seq", tout << mk_pp(re, m) << "\n";); VERIFY(m_autil.is_numeral(to_app(e)->get_arg(3), r)); SASSERT(r.is_unsigned()); i = r.get_unsigned(); aut = get_automaton(re); return true; } else { return false; } } bool theory_seq::is_step(expr* e) const { return is_skolem(m_aut_step, e); } bool theory_seq::is_step(expr* e, expr*& s, expr*& idx, expr*& re, expr*& i, expr*& j, expr*& t) const { if (is_step(e)) { s = to_app(e)->get_arg(0); idx = to_app(e)->get_arg(1); re = to_app(e)->get_arg(2); i = to_app(e)->get_arg(3); j = to_app(e)->get_arg(4); t = to_app(e)->get_arg(5); return true; } else { return false; } } expr_ref theory_seq::mk_step(expr* s, expr* idx, expr* re, unsigned i, unsigned j, expr* t) { expr_ref_vector args(m); args.push_back(s).push_back(idx).push_back(re); args.push_back(m_autil.mk_int(i)); args.push_back(m_autil.mk_int(j)); args.push_back(t); return expr_ref(m_util.mk_skolem(m_aut_step, args.size(), args.c_ptr(), m.mk_bool_sort()), m); } /** step(s, idx, re, i, j, t) -> nth(s, idx) == t & len(s) > idx step(s, idx, re, i, j, t) -> accept(s, idx + 1, re, j) */ void theory_seq::propagate_step(literal lit, expr* step) { SASSERT(get_context().get_assignment(lit) == l_true); expr* re = nullptr, *s = nullptr, *t = nullptr, *idx = nullptr, *i = nullptr, *j = nullptr; VERIFY(is_step(step, s, idx, re, i, j, t)); 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(len_s, lo) && lo.is_unsigned() && lo >= _idx) { // skip } else { propagate_lit(nullptr, 1, &lit, ~mk_literal(m_autil.mk_le(len_s, idx))); } ensure_nth(lit, s, idx); expr_ref idx1(m_autil.mk_int(_idx + 1), m); propagate_lit(nullptr, 1, &lit, mk_accept(s, idx1, re, j)); } /** acc(s, idx, re, i) -> \/ step(s, idx, re, i, j, t) if i is non-final acc(s, idx, re, i) -> len(s) <= idx \/ step(s, idx, re, i, j, t) if i is final acc(s, idx, re, i) -> len(s) >= idx if i is final acc(s, idx, re, i) -> len(s) > idx if i is non-final 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; } expr_ref len = mk_len(e); literal_vector lits; lits.push_back(~lit); if (aut->is_final_state(src)) { lits.push_back(mk_literal(m_autil.mk_le(len, idx))); propagate_lit(nullptr, 1, &lit, mk_literal(m_autil.mk_ge(len, idx))); } 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 << mk_pp(acc, m) << " #moves " << mvs.size() << "\n";); expr_ref_vector exprs(m); for (auto const& mv : mvs) { expr_ref nth = mk_nth(e, idx); expr_ref t = mv.t()->accept(nth); get_context().get_rewriter()(t); expr_ref step_e(mk_step(e, idx, re, src, mv.dst(), t), m); literal step = mk_literal(step_e); lits.push_back(step); exprs.push_back(step_e); } { std::function fn = [&]() { return m.mk_implies(acc, m.mk_or(exprs.size(), exprs.c_ptr())); }; scoped_trace_stream _sts(*this, fn); ctx.mk_th_axiom(get_id(), lits.size(), lits.c_ptr()); } 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(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); } } bool theory_seq::should_research(expr_ref_vector & unsat_core) { TRACE("seq", tout << unsat_core << " " << m_util.has_re() << "\n";); if (!m_util.has_re()) { return false; } for (auto & e : unsat_core) { if (is_max_unfolding(e)) { m_max_unfolding_depth = (3 * m_max_unfolding_depth) / 2 + 1; IF_VERBOSE(1, verbose_stream() << "(smt.seq :increase-depth " << m_max_unfolding_depth << ")\n"); return true; } } return false; } /* !prefix(e1,e2) => e1 != "" !prefix(e1,e2) => len(e1) > len(e2) or e1 = xcy & e2 = xdz & c != d */ void theory_seq::propagate_not_prefix(expr* e) { context& ctx = get_context(); expr* e1 = nullptr, *e2 = nullptr; VERIFY(m_util.str.is_prefix(e, e1, e2)); literal lit = ctx.get_literal(e); SASSERT(ctx.get_assignment(lit) == l_false); dependency * deps = nullptr; expr_ref cont(m); if (canonize(e, deps, cont) && m.is_true(cont)) { propagate_lit(deps, 0, nullptr, lit); return; } propagate_non_empty(~lit, e1); literal e1_gt_e2 = mk_simplified_literal(m_autil.mk_ge(mk_sub(mk_len(e1), mk_len(e2)), m_autil.mk_int(1))); sort* char_sort = nullptr; VERIFY(m_util.is_seq(m.get_sort(e1), char_sort)); expr_ref x = mk_skolem(symbol("seq.prefix.x"), e1, e2); expr_ref y = mk_skolem(symbol("seq.prefix.y"), e1, e2); expr_ref z = mk_skolem(symbol("seq.prefix.z"), e1, e2); expr_ref c = mk_skolem(symbol("seq.prefix.c"), e1, e2, nullptr, nullptr, char_sort); expr_ref d = mk_skolem(symbol("seq.prefix.d"), e1, e2, nullptr, nullptr, char_sort); add_axiom(lit, e1_gt_e2, mk_seq_eq(e1, mk_concat(x, m_util.str.mk_unit(c), y))); add_axiom(lit, e1_gt_e2, mk_seq_eq(e2, mk_concat(x, m_util.str.mk_unit(d), z)), mk_seq_eq(e2, x)); add_axiom(lit, e1_gt_e2, ~mk_eq(c, d, false)); } /* !suffix(e1,e2) => e1 != "" !suffix(e1,e2) => len(e1) > len(e2) or e1 = ycx & e2 = zdx & c != d */ void theory_seq::propagate_not_suffix(expr* e) { context& ctx = get_context(); expr* e1 = nullptr, *e2 = nullptr; VERIFY(m_util.str.is_suffix(e, e1, e2)); literal lit = ctx.get_literal(e); SASSERT(ctx.get_assignment(lit) == l_false); dependency * deps = nullptr; expr_ref cont(m); if (canonize(e, deps, cont) && m.is_true(cont)) { propagate_lit(deps, 0, nullptr, lit); return; } propagate_non_empty(~lit, e1); literal e1_gt_e2 = mk_simplified_literal(m_autil.mk_ge(mk_sub(mk_len(e1), mk_len(e2)), m_autil.mk_int(1))); sort* char_sort = nullptr; VERIFY(m_util.is_seq(m.get_sort(e1), char_sort)); expr_ref x = mk_skolem(symbol("seq.suffix.x"), e1, e2); expr_ref y = mk_skolem(symbol("seq.suffix.y"), e1, e2); expr_ref z = mk_skolem(symbol("seq.suffix.z"), e1, e2); expr_ref c = mk_skolem(symbol("seq.suffix.c"), e1, e2, nullptr, nullptr, char_sort); expr_ref d = mk_skolem(symbol("seq.suffix.d"), e1, e2, nullptr, nullptr, char_sort); add_axiom(lit, e1_gt_e2, mk_seq_eq(e1, mk_concat(y, m_util.str.mk_unit(c), x))); add_axiom(lit, e1_gt_e2, mk_seq_eq(e2, mk_concat(z, m_util.str.mk_unit(d), x))); add_axiom(lit, e1_gt_e2, ~mk_eq(c, d, false)); } void theory_seq::add_unit_axiom(expr* n) { expr* u = nullptr; VERIFY(m_util.str.is_unit(n, u)); sort* s = m.get_sort(u); expr_ref rhs(mk_skolem(symbol("inv-unit"), n, nullptr, nullptr, nullptr, s), m); add_axiom(mk_eq(u, rhs, false)); } /** e1 < e2 => e1 = empty or e1 = xcy e1 < e2 => e1 = empty or c < d e1 < e2 => e2 = xdz !(e1 < e2) => e1 = e2 or e2 = empty or e2 = xdz !(e1 < e2) => e1 = e2 or e2 = empty or d < c !(e1 < e2) => e1 = e2 or e1 = xcy !(e1 = e2) or !(e1 < e2) optional: e1 < e2 or e1 = e2 or e2 < e1 !(e1 = e2) or !(e2 < e1) !(e1 < e2) or !(e2 < e1) */ void theory_seq::add_lt_axiom(expr* n) { expr* e1 = nullptr, *e2 = nullptr; VERIFY(m_util.str.is_lt(n, e1, e2)); sort* s = m.get_sort(e1); sort* char_sort = nullptr; VERIFY(m_util.is_seq(s, char_sort)); literal lt = mk_literal(n); expr_ref x = mk_skolem(symbol("str.lt.x"), e1, e2); expr_ref y = mk_skolem(symbol("str.lt.y"), e1, e2); expr_ref z = mk_skolem(symbol("str.lt.z"), e1, e2); expr_ref c = mk_skolem(symbol("str.lt.c"), e1, e2, nullptr, nullptr, char_sort); expr_ref d = mk_skolem(symbol("str.lt.d"), e1, e2, nullptr, nullptr, char_sort); expr_ref xcy(mk_concat(x, m_util.str.mk_unit(c), y), m); expr_ref xdz(mk_concat(x, m_util.str.mk_unit(d), z), m); expr_ref empty_string(m_util.str.mk_empty(s), m); literal emp1 = mk_eq(e1, empty_string, false); literal emp2 = mk_eq(e2, empty_string, false); literal eq = mk_eq(e1, e2, false); literal e1xcy = mk_eq(e1, xcy, false); literal e2xdz = mk_eq(e2, xdz, false); literal ltcd = mk_literal(m_util.mk_lt(c, d)); literal ltdc = mk_literal(m_util.mk_lt(d, c)); add_axiom(~lt, e2xdz); add_axiom(~lt, emp1, e1xcy); add_axiom(~lt, emp1, ltcd); add_axiom(lt, eq, e1xcy); add_axiom(lt, eq, emp2, ltdc); add_axiom(lt, eq, emp2, e2xdz); add_axiom(~eq, ~lt); } /** e1 <= e2 <=> e1 < e2 or e1 = e2 */ void theory_seq::add_le_axiom(expr* n) { expr* e1 = nullptr, *e2 = nullptr; VERIFY(m_util.str.is_le(n, e1, e2)); literal lt = mk_literal(m_util.str.mk_lex_lt(e1, e2)); literal le = mk_literal(n); literal eq = mk_eq(e1, e2, false); add_axiom(~le, lt, eq); add_axiom(~eq, le); add_axiom(~lt, le); } bool theory_seq::canonizes(bool sign, expr* e) { context& ctx = get_context(); dependency* deps = nullptr; expr_ref cont(m); if (!canonize(e, deps, cont)) cont = e; TRACE("seq", tout << mk_bounded_pp(e, m, 2) << " -> " << mk_bounded_pp(cont, m, 2) << "\n"; if (deps) display_deps(tout, deps);); if ((m.is_true(cont) && !sign) || (m.is_false(cont) && sign)) { TRACE("seq", display(tout); tout << ctx.get_assignment(ctx.get_literal(e)) << "\n";); propagate_lit(deps, 0, nullptr, ctx.get_literal(e)); return true; } if ((m.is_false(cont) && !sign) || (m.is_true(cont) && sign)) { TRACE("seq", display(tout);); return true; } return false; } 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_ite_concat(e1, concats); e = e2; continue; } concats.push_back(e); return; } } z3-z3-4.8.7/src/smt/theory_seq.h000066400000000000000000001006511356505360400163570ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: theory_seq.h Abstract: Native theory solver for sequences. Author: Nikolaj Bjorner (nbjorner) 2015-6-12 Revision History: --*/ #ifndef THEORY_SEQ_H_ #define THEORY_SEQ_H_ #include "ast/seq_decl_plugin.h" #include "ast/rewriter/th_rewriter.h" #include "ast/ast_trail.h" #include "util/scoped_vector.h" #include "util/scoped_ptr_vector.h" #include "math/automata/automaton.h" #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 { class theory_seq : public theory { struct assumption { enode* n1, *n2; literal lit; assumption(enode* n1, enode* n2): n1(n1), n2(n2), lit(null_literal) {} assumption(literal lit): n1(nullptr), n2(nullptr), lit(lit) {} }; typedef scoped_dependency_manager dependency_manager; typedef dependency_manager::dependency dependency; typedef trail_stack th_trail_stack; typedef std::pair expr_dep; typedef obj_map eqdep_map_t; typedef union_find th_union_find; class seq_value_proc; struct validate_model_proc; // cache to track evaluations under equalities class eval_cache { eqdep_map_t m_map; expr_ref_vector m_trail; public: eval_cache(ast_manager& m): m_trail(m) {} bool find(expr* v, expr_dep& r) const { return m_map.find(v, r); } void insert(expr* v, expr_dep& r) { m_trail.push_back(v); m_trail.push_back(r.first); m_map.insert(v, r); } void reset() { m_map.reset(); m_trail.reset(); } }; // map from variables to representatives // + a cache for normalization. class solution_map { enum map_update { INS, DEL }; ast_manager& m; dependency_manager& m_dm; eqdep_map_t m_map; eval_cache m_cache; expr_ref_vector m_lhs, m_rhs; ptr_vector m_deps; svector m_updates; unsigned_vector m_limit; void add_trail(map_update op, expr* l, expr* r, dependency* d); public: solution_map(ast_manager& m, dependency_manager& dm): m(m), m_dm(dm), m_cache(m), m_lhs(m), m_rhs(m) {} bool empty() const { return m_map.empty(); } void update(expr* e, expr* r, dependency* d); void add_cache(expr* v, expr_dep& r) { m_cache.insert(v, r); } bool find_cache(expr* v, expr_dep& r) { return m_cache.find(v, r); } expr* find(expr* e, dependency*& d); expr* find(expr* e); bool find1(expr* a, expr*& b, dependency*& dep); void find_rec(expr* e, svector >& finds); bool is_root(expr* e) const; void cache(expr* e, expr* r, dependency* d); void reset_cache() { m_cache.reset(); } void push_scope() { m_limit.push_back(m_updates.size()); } void pop_scope(unsigned num_scopes); void display(std::ostream& out) const; eqdep_map_t::iterator begin() { return m_map.begin(); } eqdep_map_t::iterator end() { return m_map.end(); } }; // Table of current disequalities class exclusion_table { public: typedef obj_pair_hashtable table_t; protected: ast_manager& m; table_t m_table; expr_ref_vector m_lhs, m_rhs; unsigned_vector m_limit; public: exclusion_table(ast_manager& m): m(m), m_lhs(m), m_rhs(m) {} ~exclusion_table() { } bool empty() const { return m_table.empty(); } void update(expr* e, expr* r); bool contains(expr* e, expr* r) const; void push_scope() { m_limit.push_back(m_lhs.size()); } void pop_scope(unsigned num_scopes); void display(std::ostream& out) const; table_t::iterator begin() const { return m_table.begin(); } table_t::iterator end() const { return m_table.end(); } }; // Asserted or derived equality with dependencies class eq { unsigned m_id; expr_ref_vector m_lhs; expr_ref_vector m_rhs; dependency* m_dep; public: eq(unsigned id, expr_ref_vector& l, expr_ref_vector& r, dependency* d): m_id(id), m_lhs(l), m_rhs(r), m_dep(d) {} eq(eq const& other): m_id(other.m_id), m_lhs(other.m_lhs), m_rhs(other.m_rhs), m_dep(other.m_dep) {} eq& operator=(eq const& other) { if (this != &other) { m_lhs.reset(); m_rhs.reset(); m_lhs.append(other.m_lhs); m_rhs.append(other.m_rhs); m_dep = other.m_dep; m_id = other.m_id; } return *this; } expr_ref_vector const& ls() const { return m_lhs; } expr_ref_vector const& rs() const { return m_rhs; } dependency* dep() const { return m_dep; } unsigned id() const { return m_id; } }; eq mk_eqdep(expr* l, expr* r, dependency* dep) { expr_ref_vector ls(m), rs(m); m_util.str.get_concat(l, ls); m_util.str.get_concat(r, rs); return eq(m_eq_id++, ls, rs, dep); } class ne { expr_ref m_l, m_r; vector m_lhs, m_rhs; literal_vector m_lits; dependency* m_dep; public: ne(expr_ref const& l, expr_ref const& r, dependency* dep): m_l(l), m_r(r), m_dep(dep) { expr_ref_vector ls(l.get_manager()); ls.push_back(l); expr_ref_vector rs(r.get_manager()); rs.push_back(r); m_lhs.push_back(ls); m_rhs.push_back(rs); } ne(expr_ref const& _l, expr_ref const& _r, vector const& l, vector const& r, literal_vector const& lits, dependency* dep): m_l(_l), m_r(_r), m_lhs(l), m_rhs(r), m_lits(lits), m_dep(dep) {} ne(ne const& other): m_l(other.m_l), m_r(other.m_r), m_lhs(other.m_lhs), m_rhs(other.m_rhs), m_lits(other.m_lits), m_dep(other.m_dep) {} ne& operator=(ne const& other) { if (this != &other) { m_l = other.m_l; m_r = other.m_r; m_lhs.reset(); m_lhs.append(other.m_lhs); m_rhs.reset(); m_rhs.append(other.m_rhs); m_lits.reset(); m_lits.append(other.m_lits); m_dep = other.m_dep; } return *this; } vector const& ls() const { return m_lhs; } vector const& rs() const { return m_rhs; } expr_ref_vector const& ls(unsigned i) const { return m_lhs[i]; } expr_ref_vector const& rs(unsigned i) const { return m_rhs[i]; } literal_vector const& lits() const { return m_lits; } literal lits(unsigned i) const { return m_lits[i]; } dependency* dep() const { return m_dep; } expr_ref const& l() const { return m_l; } expr_ref const& r() const { return m_r; } }; class nc { expr_ref m_contains; literal m_len_gt; dependency* m_dep; public: nc(expr_ref const& c, literal len_gt, dependency* dep): m_contains(c), m_len_gt(len_gt), m_dep(dep) {} nc(nc const& other): m_contains(other.m_contains), m_len_gt(other.m_len_gt), m_dep(other.m_dep) {} nc& operator=(nc const& other) { if (this != &other) { m_contains = other.m_contains; m_dep = other.m_dep; m_len_gt = other.m_len_gt; } return *this; } dependency* deps() const { return m_dep; } expr_ref const& contains() const { return m_contains; } literal len_gt() const { return m_len_gt; } }; class apply { public: virtual ~apply() {} virtual void operator()(theory_seq& th) = 0; }; class replay_length_coherence : public apply { expr_ref m_e; public: replay_length_coherence(ast_manager& m, expr* e) : m_e(e, m) {} ~replay_length_coherence() override {} void operator()(theory_seq& th) override { th.check_length_coherence(m_e); m_e.reset(); } }; class replay_fixed_length : public apply { expr_ref m_e; public: replay_fixed_length(ast_manager& m, expr* e) : m_e(e, m) {} ~replay_fixed_length() override {} void operator()(theory_seq& th) override { th.fixed_length(m_e); m_e.reset(); } }; class replay_axiom : public apply { expr_ref m_e; public: replay_axiom(ast_manager& m, expr* e) : m_e(e, m) {} ~replay_axiom() override {} void operator()(theory_seq& th) override { th.enque_axiom(m_e); m_e.reset(); } }; class replay_unit_literal : public apply { expr_ref m_e; bool m_sign; public: replay_unit_literal(ast_manager& m, expr* e, bool sign) : m_e(e, m), m_sign(sign) {} ~replay_unit_literal() override {} void operator()(theory_seq& th) override { literal lit = th.mk_literal(m_e); if (m_sign) lit.neg(); th.add_axiom(lit); m_e.reset(); } }; class replay_is_axiom : public apply { expr_ref m_e; public: replay_is_axiom(ast_manager& m, expr* e) : m_e(e, m) {} ~replay_is_axiom() override {} void operator()(theory_seq& th) override { th.check_int_string(m_e); m_e.reset(); } }; class push_replay : public trail { apply* m_apply; public: push_replay(apply* app): m_apply(app) {} void undo(theory_seq& th) override { th.m_replay.push_back(m_apply); } }; class pop_branch : public trail { unsigned k; public: pop_branch(unsigned k): k(k) {} void undo(theory_seq& th) override { th.m_branch_start.erase(k); } }; struct s_in_re { literal m_lit; expr* m_s; expr* m_re; eautomaton* m_aut; bool m_active; s_in_re(literal l, expr*s, expr* re, eautomaton* aut): m_lit(l), m_s(s), m_re(re), m_aut(aut), m_active(true) {} }; void erase_index(unsigned idx, unsigned i); struct stats { stats() { reset(); } void reset() { memset(this, 0, sizeof(stats)); } unsigned m_num_splits; unsigned m_num_reductions; unsigned m_propagate_automata; unsigned m_check_length_coherence; unsigned m_branch_variable; unsigned m_branch_nqs; unsigned m_solve_nqs; unsigned m_solve_eqs; unsigned m_add_axiom; unsigned m_extensionality; unsigned m_fixed_length; unsigned m_propagate_contains; unsigned m_int_string; }; typedef hashtable rational_set; ast_manager& m; theory_seq_params const& m_params; dependency_manager m_dm; solution_map m_rep; // unification representative. scoped_vector m_eqs; // set of current equations. scoped_vector m_nqs; // set of current disequalities. scoped_vector m_ncs; // set of non-contains constraints. scoped_vector m_lts; // set of asserted str.<, str.<= literals bool m_lts_checked; unsigned m_eq_id; th_union_find m_find; obj_ref_map m_overlap; obj_ref_map m_overlap2; obj_map> m_len_offset; int m_len_prop_lvl; seq_factory* m_factory; // value factory exclusion_table m_exclude; // set of asserted disequalities. expr_ref_vector m_axioms; // list of axioms to add. obj_hashtable m_axiom_set; 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_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; symbol m_tail, m_seq_first, m_seq_last, m_indexof_left, m_indexof_right, m_aut_step; symbol m_pre, m_post, m_eq, m_seq_align; ptr_vector m_todo; expr_ref_vector m_ls, m_rs, m_lhs, m_rhs; // maintain automata with regular expressions. scoped_ptr_vector m_automata; obj_map m_re2aut; expr_ref_vector m_res; unsigned m_max_unfolding_depth; literal m_max_unfolding_lit; vector m_s_in_re; bool m_new_solution; // new solution added bool m_new_propagation; // new propagation to core re2automaton m_mk_aut; obj_hashtable m_fixed; // string variables that are fixed length. void init(context* ctx) override; final_check_status final_check_eh() override; bool internalize_atom(app* atom, bool) override; bool internalize_term(app*) override; void internalize_eq_eh(app * atom, bool_var v) override; void new_eq_eh(theory_var, theory_var) override; void new_diseq_eh(theory_var, theory_var) override; void assign_eh(bool_var v, bool is_true) override; bool can_propagate() override; void propagate() override; void push_scope_eh() override; void pop_scope_eh(unsigned num_scopes) override; void restart_eh() override; void relevant_eh(app* n) override; bool should_research(expr_ref_vector &) override; void add_theory_assumptions(expr_ref_vector & assumptions) override; theory* mk_fresh(context* new_ctx) override { return alloc(theory_seq, new_ctx->get_manager(), m_params); } char const * get_name() const override { return "seq"; } bool include_func_interp(func_decl* f) override { return m_util.str.is_nth_u(f); } theory_var mk_var(enode* n) override; void apply_sort_cnstr(enode* n, sort* s) override; void display(std::ostream & out) const override; void collect_statistics(::statistics & st) const override; model_value_proc * mk_value(enode * n, model_generator & mg) override; void init_model(model_generator & mg) override; void finalize_model(model_generator & mg) override; void init_search_eh() override; void validate_model(model& mdl) 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(); int find_fst_non_empty_idx(expr_ref_vector const& x); expr* find_fst_non_empty_var(expr_ref_vector const& x); void find_max_eq_len(expr_ref_vector const& ls, expr_ref_vector const& rs); bool has_len_offset(expr_ref_vector const& ls, expr_ref_vector const& rs, int & diff); bool find_better_rep(expr_ref_vector const& ls, expr_ref_vector const& rs, unsigned idx, dependency*& deps, expr_ref_vector & res); // final check bool simplify_and_solve_eqs(); // solve unitary equalities 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_eq(); // branch on a variable, by an alignment among variable boundaries. bool is_solved(); bool check_length_coherence(); bool check_length_coherence0(expr* e); bool check_length_coherence(expr* e); 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(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); unsigned_vector overlap2(expr_ref_vector const& ls, expr_ref_vector const& rs); bool branch_ternary_variable_base(dependency* dep, unsigned_vector indexes, expr* const& x, expr_ref_vector const& xs, expr* const& y1, expr_ref_vector const& ys, expr* const& y2); bool branch_ternary_variable_base2(dependency* dep, unsigned_vector indexes, expr_ref_vector const& xs, expr* const& x, expr* const& y1, expr_ref_vector const& ys, expr* const& y2); bool branch_ternary_variable(eq const& e, bool flag1 = false); bool branch_ternary_variable2(eq const& e, bool flag1 = false); bool branch_quat_variable(eq const& e); bool len_based_split(eq const& e); bool is_unit_eq(expr_ref_vector const& ls, expr_ref_vector const& rs); bool propagate_length_coherence(expr* e); bool split_lengths(dependency* dep, expr_ref_vector const& ls, expr_ref_vector const& rs, 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(); bool check_lts(); bool solve_eqs(unsigned start); bool solve_eq(expr_ref_vector const& l, expr_ref_vector const& r, dependency* dep, unsigned idx); bool simplify_eq(expr_ref_vector& l, expr_ref_vector& r, dependency* dep); bool lift_ite(expr_ref_vector const& l, expr_ref_vector const& r, dependency* dep); bool solve_unit_eq(expr* l, expr* r, dependency* dep); bool solve_unit_eq(expr_ref_vector const& l, expr_ref_vector const& r, dependency* dep); bool solve_nth_eq1(expr_ref_vector const& ls, expr_ref_vector const& rs, dependency* dep); bool solve_nth_eq2(expr_ref_vector const& ls, expr_ref_vector const& rs, dependency* dep); bool solve_itos(expr_ref_vector const& ls, expr_ref_vector const& rs, dependency* dep); bool is_binary_eq(expr_ref_vector const& l, expr_ref_vector const& r, expr_ref& x, ptr_vector& xunits, ptr_vector& yunits, expr_ref& y); bool is_quat_eq(expr_ref_vector const& ls, expr_ref_vector const& rs, expr_ref& x1, expr_ref_vector& xs, expr_ref& x2, expr_ref& y1, expr_ref_vector& ys, expr_ref& y2); bool is_ternary_eq(expr_ref_vector const& ls, expr_ref_vector const& rs, expr_ref& x, expr_ref_vector& xs, expr_ref& y1, expr_ref_vector& ys, expr_ref& y2, bool flag1); bool is_ternary_eq2(expr_ref_vector const& ls, expr_ref_vector const& rs, expr_ref_vector& xs, expr_ref& x, expr_ref& y1, expr_ref_vector& ys, expr_ref& y2, bool flag1); bool solve_binary_eq(expr_ref_vector const& l, expr_ref_vector const& r, dependency* dep); bool propagate_max_length(expr* l, expr* r, dependency* dep); bool get_length(expr* s, expr_ref& len, literal_vector& lits); bool reduce_length(expr* l, expr* r, literal_vector& lits); bool reduce_length_eq(expr_ref_vector const& ls, expr_ref_vector const& rs, dependency* deps); bool reduce_length(unsigned i, unsigned j, bool front, expr_ref_vector const& ls, expr_ref_vector const& rs, dependency* deps); expr_ref mk_empty(sort* s) { return expr_ref(m_util.str.mk_empty(s), m); } expr_ref mk_concat(unsigned n, expr*const* es) { return expr_ref(m_util.str.mk_concat(n, es), m); } expr_ref mk_concat(expr_ref_vector const& es, sort* s) { if (es.empty()) return mk_empty(s); return mk_concat(es.size(), es.c_ptr()); } expr_ref mk_concat(expr_ref_vector const& es) { SASSERT(!es.empty()); return expr_ref(m_util.str.mk_concat(es.size(), es.c_ptr()), m); } expr_ref mk_concat(ptr_vector const& es) { SASSERT(!es.empty()); return mk_concat(es.size(), es.c_ptr()); } expr_ref mk_concat(expr* e1, expr* e2) { return expr_ref(m_util.str.mk_concat(e1, e2), m); } expr_ref mk_concat(expr* e1, expr* e2, expr* e3) { return expr_ref(m_util.str.mk_concat(e1, e2, e3), m); } bool solve_nqs(unsigned i); bool solve_ne(unsigned i); bool solve_nc(unsigned i); bool branch_nqs(); void branch_nq(ne const& n); struct cell { cell* m_parent; expr* m_expr; dependency* m_dep; unsigned m_last; cell(cell* p, expr* e, dependency* d): m_parent(p), m_expr(e), m_dep(d), m_last(0) {} }; scoped_ptr_vector m_all_cells; cell* mk_cell(cell* p, expr* e, dependency* d); void unfold(cell* c, ptr_vector& cons); void display_explain(std::ostream& out, unsigned indent, expr* e); bool explain_eq(expr* e1, expr* e2, dependency*& dep); bool explain_empty(expr_ref_vector& es, dependency*& dep); // asserting consequences bool linearize(dependency* dep, enode_pair_vector& eqs, literal_vector& lits) const; void propagate_lit(dependency* dep, literal lit) { propagate_lit(dep, 0, nullptr, lit); } void propagate_lit(dependency* dep, unsigned n, literal const* lits, literal lit); bool propagate_eq(dependency* dep, enode* n1, enode* n2); bool propagate_eq(literal lit, expr* e1, expr* e2, bool add_to_eqs); bool propagate_eq(dependency* dep, literal_vector const& lits, expr* e1, expr* e2, bool add_to_eqs = true); bool propagate_eq(dependency* dep, expr* e1, expr* e2, bool add_to_eqs = true); bool propagate_eq(dependency* dep, literal lit, expr* e1, expr* e2, bool add_to_eqs = true); void set_conflict(dependency* dep, literal_vector const& lits = literal_vector()); u_map m_branch_start; void insert_branch_start(unsigned k, unsigned s); unsigned find_branch_start(unsigned k); bool find_branch_candidate(unsigned& start, dependency* dep, expr_ref_vector const& ls, expr_ref_vector const& rs); expr_ref_vector expand_strings(expr_ref_vector const& es); bool can_be_equal(unsigned szl, expr* const* ls, unsigned szr, expr* const* rs) const; lbool assume_equality(expr* l, expr* r); // variable solving utilities bool occurs(expr* a, expr* b); bool occurs(expr* a, expr_ref_vector const& b); bool is_var(expr* b) const; bool add_solution(expr* l, expr* r, dependency* dep); bool is_unit_nth(expr* a) const; bool is_tail(expr* a, expr*& s, unsigned& idx) const; bool is_eq(expr* e, expr*& a, expr*& b) const; bool is_pre(expr* e, expr*& s, expr*& i); bool is_post(expr* e, expr*& s, expr*& i); expr_ref mk_sk_ite(expr* c, expr* t, expr* f); expr_ref mk_nth(expr* s, expr* idx); expr_ref mk_last(expr* e); expr_ref mk_first(expr* e); bool canonize(expr* e, dependency*& eqs, expr_ref& result); bool canonize(expr* e, expr_ref_vector& es, dependency*& eqs, bool& change); bool canonize(expr_ref_vector const& es, expr_ref_vector& result, dependency*& eqs, bool& change); ptr_vector m_expand_todo; bool expand(expr* e, dependency*& eqs, expr_ref& result); bool expand1(expr* e, dependency*& eqs, expr_ref& result); expr_ref try_expand(expr* e, dependency*& eqs); void add_dependency(dependency*& dep, enode* a, enode* b); // terms whose meaning are encoded using axioms. void enque_axiom(expr* e); void deque_axiom(expr* e); void push_lit_as_expr(literal l, expr_ref_vector& buf); void add_axiom(literal l1, literal l2 = null_literal, literal l3 = null_literal, literal l4 = null_literal, literal l5 = null_literal); void add_indexof_axiom(expr* e); void add_last_indexof_axiom(expr* e); void add_replace_axiom(expr* e); void add_extract_axiom(expr* e); void add_length_axiom(expr* n); void add_tail_axiom(expr* e, expr* s); void add_drop_last_axiom(expr* e, expr* s); void add_extract_prefix_axiom(expr* e, expr* s, expr* l); void add_extract_suffix_axiom(expr* e, expr* s, expr* i); bool is_tail(expr* s, expr* i, expr* l); bool is_drop_last(expr* s, expr* i, expr* l); bool is_extract_prefix0(expr* s, expr* i, expr* l); bool is_extract_suffix(expr* s, expr* i, expr* l); bool has_length(expr *e) const { return m_has_length.contains(e); } void add_length(expr* e, expr* l); bool add_length_to_eqc(expr* n); bool enforce_length(expr_ref_vector const& es, vector& len); void enforce_length_coherence(enode* n1, enode* n2); // model-check the functions that convert integers to strings and the other way. void add_int_string(expr* e); bool check_int_string(); bool check_int_string(expr* e); expr_ref add_elim_string_axiom(expr* n); void add_at_axiom(expr* n); void add_lt_axiom(expr* n); void add_le_axiom(expr* n); void add_unit_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); bool add_stoi_val_axiom(expr* n); bool add_itos_val_axiom(expr* n); void add_si_axiom(expr* s, expr* i, unsigned sz); void ensure_digit_axiom(); literal is_digit(expr* ch); expr_ref digit2int(expr* ch); void add_itos_length_axiom(expr* n); literal mk_literal(expr* n); literal mk_simplified_literal(expr* n); literal mk_eq_empty(expr* n, bool phase = true); literal mk_seq_eq(expr* a, expr* b); literal mk_preferred_eq(expr* a, expr* b); void tightest_prefix(expr* s, expr* x); expr_ref mk_sub(expr* a, expr* b); expr_ref mk_add(expr* a, expr* b); expr_ref mk_len(expr* s); enode* ensure_enode(expr* a); enode* get_root(expr* a) { return ensure_enode(a)->get_root(); } dependency* mk_join(dependency* deps, literal lit); dependency* mk_join(dependency* deps, literal_vector const& lits); // arithmetic integration bool get_num_value(expr* s, rational& val) const; bool lower_bound(expr* s, rational& lo) const; bool lower_bound2(expr* s, rational& lo); bool upper_bound(expr* s, rational& hi) const; bool get_length(expr* s, rational& val); void mk_decompose(expr* e, expr_ref& head, expr_ref& tail); 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; void set_incomplete(app* term); // automata utilities void propagate_in_re(expr* n, bool is_true); eautomaton* get_automaton(expr* e); literal mk_accept(expr* s, expr* idx, expr* re, expr* state); literal mk_accept(expr* s, expr* idx, expr* re, unsigned i) { return mk_accept(s, idx, re, m_autil.mk_int(i)); } bool is_accept(expr* acc) const { return is_skolem(m_accept, acc); } bool is_accept(expr* acc, expr*& s, expr*& idx, expr*& re, unsigned& i, eautomaton*& aut); expr_ref mk_step(expr* s, expr* tail, expr* re, unsigned i, unsigned j, expr* t); bool is_step(expr* e, expr*& s, expr*& tail, expr*& re, expr*& i, expr*& j, expr*& t) const; bool is_step(expr* e) const; bool is_max_unfolding(expr* e) const { return is_skolem(symbol("seq.max_unfolding_depth"), e); } expr_ref mk_max_unfolding_depth() { return mk_skolem(symbol("seq.max_unfolding_depth"), m_autil.mk_int(m_max_unfolding_depth), nullptr, nullptr, nullptr, m.mk_bool_sort()); } void propagate_not_prefix(expr* e); void propagate_not_suffix(expr* e); void ensure_nth(literal lit, expr* s, expr* idx); bool canonizes(bool sign, expr* e); void propagate_non_empty(literal lit, expr* s); bool propagate_is_conc(expr* e, expr* conc); void propagate_step(literal lit, expr* n); void propagate_accept(literal lit, expr* e); void new_eq_eh(dependency* dep, enode* n1, enode* n2); // diagnostics std::ostream& display_equations(std::ostream& out) const; std::ostream& display_equation(std::ostream& out, eq const& e) const; std::ostream& display_disequations(std::ostream& out) const; std::ostream& display_disequation(std::ostream& out, ne const& e) const; std::ostream& display_deps(std::ostream& out, dependency* deps) const; std::ostream& display_deps(std::ostream& out, literal_vector const& lits, enode_pair_vector const& eqs) const; std::ostream& display_nc(std::ostream& out, nc const& nc) const; public: theory_seq(ast_manager& m, theory_seq_params const & params); ~theory_seq() override; // model building app* mk_value(app* a); th_trail_stack& get_trail_stack() { return m_trail_stack; } void merge_eh(theory_var, theory_var, theory_var v1, theory_var v2) {} void after_merge_eh(theory_var r1, theory_var r2, theory_var v1, theory_var v2) { } void unmerge_eh(theory_var v1, theory_var v2) {} }; }; #endif /* THEORY_SEQ_H_ */ z3-z3-4.8.7/src/smt/theory_seq_empty.h000066400000000000000000000026421356505360400175760ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: theory_seq_empty.h Abstract: Author: Nikolaj Bjorner (nbjorner) 2011-14-11 Revision History: --*/ #ifndef THEORY_SEQ_EMPTY_H_ #define THEORY_SEQ_EMPTY_H_ #include "smt/smt_theory.h" #include "ast/seq_decl_plugin.h" #include "model/seq_factory.h" namespace smt { class theory_seq_empty : public theory { bool m_used; final_check_status final_check_eh() override { return m_used?FC_GIVEUP:FC_DONE; } bool internalize_atom(app*, bool) override { if (!m_used) { get_context().push_trail(value_trail(m_used)); m_used = true; } return false; } bool internalize_term(app*) override { return internalize_atom(nullptr,false); } void new_eq_eh(theory_var, theory_var) override { } void new_diseq_eh(theory_var, theory_var) override {} theory* mk_fresh(context* new_ctx) override { return alloc(theory_seq_empty, new_ctx->get_manager()); } char const * get_name() const override { return "seq-empty"; } void display(std::ostream& out) const override {} public: theory_seq_empty(ast_manager& m):theory(m.mk_family_id("seq")), m_used(false) {} void init_model(model_generator & mg) override { mg.register_factory(alloc(seq_factory, get_manager(), get_family_id(), mg.get_model())); } }; }; #endif /* THEORY_SEQ_EMPTY_H_ */ z3-z3-4.8.7/src/smt/theory_special_relations.cpp000066400000000000000000001244431356505360400216270ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: theory_special_relations.cpp Abstract: Special Relations theory plugin. Author: Nikolaj Bjorner (nbjorner) 2015-9-16 Ashutosh Gupta 2016 Notes: --*/ #include #include "ast/reg_decl_plugins.h" #include "ast/datatype_decl_plugin.h" #include "ast/recfun_decl_plugin.h" #include "ast/ast_pp.h" #include "ast/rewriter/recfun_replace.h" #include "smt/smt_context.h" #include "smt/theory_arith.h" #include "smt/theory_special_relations.h" namespace smt { func_decl* theory_special_relations::relation::next() { if (!m_next) { sort* s = decl()->get_domain(0); sort* domain[2] = {s, s}; m_next = m.mk_fresh_func_decl("next", "", 2, domain, s); } return m_next; } void theory_special_relations::relation::push() { m_scopes.push_back(scope()); scope& s = m_scopes.back(); s.m_asserted_atoms_lim = m_asserted_atoms.size(); s.m_asserted_qhead_old = m_asserted_qhead; m_graph.push(); m_ufctx.get_trail_stack().push_scope(); } void theory_special_relations::relation::pop(unsigned num_scopes) { unsigned new_lvl = m_scopes.size() - num_scopes; scope& s = m_scopes[new_lvl]; m_asserted_atoms.shrink(s.m_asserted_atoms_lim); m_asserted_qhead = s.m_asserted_qhead_old; m_scopes.shrink(new_lvl); m_graph.pop(num_scopes); m_ufctx.get_trail_stack().pop_scope(num_scopes); } void theory_special_relations::relation::ensure_var(theory_var v) { while ((unsigned)v > m_uf.mk_var()); if ((unsigned)v >= m_graph.get_num_nodes()) { m_graph.init_var(v); } } bool theory_special_relations::relation::new_eq_eh(literal l, theory_var v1, theory_var v2) { ensure_var(v1); ensure_var(v2); literal_vector ls; ls.push_back(l); return m_graph.add_non_strict_edge(v1, v2, ls) && m_graph.add_non_strict_edge(v2, v1, ls); } std::ostream& theory_special_relations::relation::display(theory_special_relations const& th, std::ostream& out) const { out << mk_pp(m_decl, th.get_manager()); for (unsigned i = 0; i < m_decl->get_num_parameters(); ++i) { th.get_manager().display(out << " ", m_decl->get_parameter(i)); } out << ":\n"; m_graph.display(out); out << "explanation: " << m_explanation << "\n"; m_uf.display(out); for (atom* ap : m_asserted_atoms) { th.display_atom(out, *ap); } return out; } theory_special_relations::theory_special_relations(ast_manager& m): theory(m.mk_family_id("special_relations")), m_util(m), m_can_propagate(false) { } theory_special_relations::~theory_special_relations() { reset_eh(); } theory * theory_special_relations::mk_fresh(context * new_ctx) { return alloc(theory_special_relations, new_ctx->get_manager()); } /** \brief for term := next(next(a,b),c) for relation f assert f(term,c) or term != c assert f(term,c) or term != next(a,b) assert f(term,c) or term != b assert f(term,c) or term != a */ void theory_special_relations::internalize_next(func_decl* f, app* term) { context& ctx = get_context(); ast_manager& m = get_manager(); func_decl* nxt = term->get_decl(); expr* src = term->get_arg(0); expr* dst = term->get_arg(1); expr_ref f_rel(m.mk_app(f, src, dst), m); ensure_enode(term); ensure_enode(f_rel); literal f_lit = ctx.get_literal(f_rel); src = term; while (to_app(src)->get_decl() == nxt) { dst = to_app(src)->get_arg(1); src = to_app(src)->get_arg(0); ctx.mk_th_axiom(get_id(), f_lit, ~mk_eq(term, src, false)); ctx.mk_th_axiom(get_id(), f_lit, ~mk_eq(term, dst, false)); } } bool theory_special_relations::internalize_term(app * term) { return false; } bool theory_special_relations::internalize_atom(app * atm, bool gate_ctx) { SASSERT(m_util.is_special_relation(atm)); relation* r = 0; ast_manager& m = get_manager(); if (!m_relations.find(atm->get_decl(), r)) { r = alloc(relation, m_util.get_property(atm), atm->get_decl(), m); m_relations.insert(atm->get_decl(), r); for (unsigned i = 0; i < m_atoms_lim.size(); ++i) r->push(); } context& ctx = get_context(); expr* arg0 = atm->get_arg(0); expr* arg1 = atm->get_arg(1); theory_var v0 = mk_var(arg0); theory_var v1 = mk_var(arg1); bool_var v = ctx.mk_bool_var(atm); ctx.set_var_theory(v, get_id()); atom* a = alloc(atom, v, *r, v0, v1); m_atoms.push_back(a); TRACE("special_relations", tout << mk_pp(atm, m) << " : bv" << v << " v" << a->v1() << " v" << a->v2() << ' ' << gate_ctx << "\n";); m_bool_var2atom.insert(v, a); return true; } theory_var theory_special_relations::mk_var(expr* e) { context& ctx = get_context(); if (!ctx.e_internalized(e)) { ctx.internalize(e, false); } enode * n = ctx.get_enode(e); theory_var v = n->get_th_var(get_id()); if (null_theory_var == v) { v = theory::mk_var(n); TRACE("special_relations", tout << "v" << v << " := " << mk_pp(e, get_manager()) << "\n";); ctx.attach_th_var(n, this, v); } return v; } void theory_special_relations::new_eq_eh(theory_var v1, theory_var v2) { app* t1 = get_expr(v1); app* t2 = get_expr(v2); literal eq = mk_eq(t1, t2, false); for (auto const& kv : m_relations) { relation& r = *kv.m_value; if (!r.new_eq_eh(eq, v1, v2)) { set_neg_cycle_conflict(r); break; } } } final_check_status theory_special_relations::final_check_eh() { TRACE("special_relations", tout << "\n";); for (auto const& kv : m_relations) { lbool r = final_check(*kv.m_value); switch (r) { case l_undef: return FC_GIVEUP; case l_false: return FC_CONTINUE; default: break; } } bool new_equality = false; for (auto const& kv : m_relations) { if (extract_equalities(*kv.m_value)) { new_equality = true; } if (get_context().inconsistent()) { return FC_CONTINUE; } } if (new_equality) { return FC_CONTINUE; } else { return FC_DONE; } } lbool theory_special_relations::final_check_lo(relation& r) { // all constraints are saturated by propagation. return l_true; } enode* theory_special_relations::ensure_enode(expr* e) { context& ctx = get_context(); if (!ctx.e_internalized(e)) { ctx.internalize(e, false); } enode* n = ctx.get_enode(e); ctx.mark_as_relevant(n); return n; } literal theory_special_relations::mk_literal(expr* _e) { expr_ref e(_e, get_manager()); ensure_enode(e); return get_context().get_literal(e); } theory_var theory_special_relations::mk_var(enode* n) { if (is_attached_to_var(n)) { return n->get_th_var(get_id()); } else { theory_var v = theory::mk_var(n); get_context().attach_th_var(n, this, v); get_context().mark_as_relevant(n); return v; } } lbool theory_special_relations::final_check_plo(relation& r) { // // ensure that !Rxy -> Ryx between connected components // (where Rzx & Rzy or Rxz & Ryz for some z) // lbool res = l_true; for (unsigned i = 0; res == l_true && i < r.m_asserted_atoms.size(); ++i) { atom& a = *r.m_asserted_atoms[i]; if (!a.phase() && r.m_uf.find(a.v1()) == r.m_uf.find(a.v2())) { res = enable(a); } } return res; } lbool theory_special_relations::final_check_tc(relation& r) { // // Ensure that Rxy -> TC(R)xy // func_decl* tcf = r.decl(); func_decl* f = to_func_decl(tcf->get_parameter(0).get_ast()); context& ctx = get_context(); ast_manager& m = get_manager(); bool new_assertion = false; graph r_graph; for (enode* n : ctx.enodes_of(f)) { literal lit = ctx.enode2literal(n); if (l_true == ctx.get_assignment(lit)) { expr* e = ctx.bool_var2expr(lit.var()); expr* arg1 = to_app(e)->get_arg(0); expr* arg2 = to_app(e)->get_arg(1); expr_ref tc_app(m.mk_app(tcf, arg1, arg2), m); enode* tcn = ensure_enode(tc_app); if (ctx.get_assignment(tcn) != l_true) { literal consequent = ctx.get_literal(tc_app); justification* j = ctx.mk_justification(theory_propagation_justification(get_id(), ctx.get_region(), 1, &lit, consequent)); TRACE("special_relations", tout << "propagate: " << tc_app << "\n";); ctx.assign(consequent, j); new_assertion = true; } else { theory_var v1 = get_representative(get_th_var(arg1)); theory_var v2 = get_representative(get_th_var(arg2)); r_graph.init_var(v1); r_graph.init_var(v2); literal_vector ls; r_graph.enable_edge(r_graph.add_edge(v1, v2, s_integer(0), ls)); } } } // // Ensure that TC(R)xy -> Rxz1 Rz1z2 .. Rzky // // if not Rxy and no path in graph: // Introduce next(x,y), such that // TC(R)(x,y) => R(x,y) or TR(R)(next(x,y),y) & R(x,next(x,y)) // // next(x,y) is fresh unless R(x,y) is true: // R(x,y) or x != next(x,y) // R(x,y) or y != next(x,y) // unsigned sz = r.m_asserted_atoms.size(); for (unsigned i = 0; i < sz; ++i) { atom& a = *r.m_asserted_atoms[i]; if (a.phase()) { bool_var bv = a.var(); expr* arg1 = get_expr(a.v1()); expr* arg2 = get_expr(a.v2()); // we need reachability in the R graph not R* graph theory_var r1 = get_representative(a.v1()); theory_var r2 = get_representative(a.v2()); if (r_graph.can_reach(r1, r2)) { TRACE("special_relations", tout << a.v1() << ": " << mk_pp(arg1, m) << " -> " << a.v2() << ": " << mk_pp(arg2, m) << " is positive reachable\n"; r.m_graph.display(tout); ); continue; } expr_ref f_app(m.mk_app(f, arg1, arg2), m); ensure_enode(f_app); literal f_lit = ctx.get_literal(f_app); switch (ctx.get_assignment(f_lit)) { case l_true: UNREACHABLE(); // it should already be the case that v1 and reach v2 in the graph. // whenever f(n1, n2) is asserted. break; case l_false: { // // Add the axioms: // TC(R)(x,y) => R(x,y) or TC(R)(next(x,y),y) // TC(R)(x,y) => R(x,y) or R(x,next(x,y)) // R(x,y) or next(x,y) != x // R(x,y) or next(x,y) != y, // and recursively on all next subterms of x. // Add the literal R(next(x,y),y) - set case split preference to true. // // TBD: perhaps replace by recursion unfolding similar to theory_rec_fun // app_ref next(r.next(arg1, arg2), m); internalize_next(f, next); expr_ref a2next(m.mk_app(f, arg1, next), m); expr_ref next2b(m.mk_app(tcf, next, arg2), m); expr_ref next_b(m.mk_app(f, next, arg2), m); ensure_enode(a2next); ensure_enode(next2b); ensure_enode(next_b); literal next2b_l = ctx.get_literal(next2b); literal a2next_l = ctx.get_literal(a2next); if (ctx.get_assignment(next2b_l) == l_true && ctx.get_assignment(a2next_l) == l_true) { break; } ctx.mk_th_axiom(get_id(), ~literal(bv), f_lit, a2next_l); ctx.mk_th_axiom(get_id(), ~literal(bv), f_lit, next2b_l); expr* nxt = next; while (r.is_next(nxt)) { expr* left = to_app(nxt)->get_arg(0); expr* right = to_app(nxt)->get_arg(1); ctx.assign(~mk_eq(next, left, false), nullptr); ctx.assign(~mk_eq(next, right, false), nullptr); nxt = left; } ctx.set_true_first_flag(ctx.get_literal(next_b).var()); new_assertion = true; break; } case l_undef: ctx.set_true_first_flag(bv); TRACE("special_relations", tout << f_app << " is undefined\n";); new_assertion = true; break; } } } if (new_assertion) { TRACE("special_relations", tout << "new assertion\n";); return l_false; } return final_check_po(r); } lbool theory_special_relations::final_check_to(relation& r) { uint_set visited, target; for (atom* ap : r.m_asserted_atoms) { atom& a = *ap; if (a.phase()) { continue; } TRACE("special_relations", tout << a.v1() << " !<= " << a.v2() << "\n";); target.reset(); theory_var w; // v1 !<= v2 is asserted target.insert(a.v1()); if (r.m_graph.reachable(a.v2(), target, visited, w)) { // we already have v2 <= v1 TRACE("special_relations", tout << "already: " << a.v2() << " <= " << a.v1() << "\n";); continue; } // the nodes visited from v1 become target for v2 if (r.m_graph.reachable(a.v2(), visited, target, w)) { // // we have the following: // v1 <= w // v2 <= w // v1 !<= v2 // // enforce the assertion // // v1 <= w & v2 <= w & v1 !<= v2 -> v2 <= v1 // unsigned timestamp = r.m_graph.get_timestamp(); r.m_explanation.reset(); r.m_graph.find_shortest_reachable_path(a.v1(), w, timestamp, r); r.m_graph.find_shortest_reachable_path(a.v2(), w, timestamp, r); TRACE("special_relations", tout << "added edge\n";); r.m_explanation.push_back(a.explanation()); literal_vector const& lits = r.m_explanation; if (!r.m_graph.add_non_strict_edge(a.v2(), a.v1(), lits)) { set_neg_cycle_conflict(r); return l_false; } } target.reset(); visited.reset(); target.insert(a.v2()); if (r.m_graph.reachable(a.v1(), target, visited, w)) { // we have v1 <= v2 unsigned timestamp = r.m_graph.get_timestamp(); r.m_explanation.reset(); r.m_graph.find_shortest_reachable_path(a.v1(), w, timestamp, r); r.m_explanation.push_back(a.explanation()); set_conflict(r); } } return l_true; } lbool theory_special_relations::enable(atom& a) { if (!a.enable()) { relation& r = a.get_relation(); set_neg_cycle_conflict(r); return l_false; } else { return l_true; } } void theory_special_relations::set_neg_cycle_conflict(relation& r) { r.m_explanation.reset(); r.m_graph.traverse_neg_cycle2(false, r); set_conflict(r); } void theory_special_relations::set_conflict(relation& r) { literal_vector const& lits = r.m_explanation; context & ctx = get_context(); TRACE("special_relations", ctx.display_literals_verbose(tout, lits) << "\n";); vector params; ctx.set_conflict( ctx.mk_justification( ext_theory_conflict_justification( get_id(), ctx.get_region(), lits.size(), lits.c_ptr(), 0, 0, params.size(), params.c_ptr()))); } lbool theory_special_relations::final_check(relation& r) { lbool res = propagate(r); if (res != l_true) return res; switch (r.m_property) { case sr_lo: res = final_check_lo(r); break; case sr_po: res = final_check_po(r); break; case sr_plo: res = final_check_plo(r); break; case sr_to: res = final_check_to(r); break; case sr_tc: res = final_check_tc(r); break; default: UNREACHABLE(); res = l_undef; break; } TRACE("special_relations", r.display(*this, tout << res << "\n");); return res; } bool theory_special_relations::extract_equalities(relation& r) { switch (r.m_property) { case sr_tc: return false; default: break; } bool new_eq = false; int_vector scc_id; u_map roots; context& ctx = get_context(); ast_manager& m = get_manager(); (void)m; r.m_graph.compute_zero_edge_scc(scc_id); int start = ctx.get_random_value(); for (unsigned idx = 0, j = 0; !ctx.inconsistent() && idx < scc_id.size(); ++idx) { unsigned i = (start + idx) % scc_id.size(); if (scc_id[i] == -1) { continue; } enode* x = get_enode(i); if (roots.find(scc_id[i], j)) { enode* y = get_enode(j); if (x->get_root() != y->get_root()) { new_eq = true; unsigned timestamp = r.m_graph.get_timestamp(); r.m_explanation.reset(); r.m_graph.find_shortest_zero_edge_path(i, j, timestamp, r); r.m_graph.find_shortest_zero_edge_path(j, i, timestamp, r); literal_vector const& lits = r.m_explanation; TRACE("special_relations", ctx.display_literals_verbose(tout << mk_pp(x->get_owner(), m) << " = " << mk_pp(y->get_owner(), m) << "\n", lits) << "\n";); IF_VERBOSE(20, ctx.display_literals_verbose(verbose_stream() << mk_pp(x->get_owner(), m) << " = " << mk_pp(y->get_owner(), m) << "\n", lits) << "\n";); eq_justification js(ctx.mk_justification(ext_theory_eq_propagation_justification(get_id(), ctx.get_region(), lits.size(), lits.c_ptr(), 0, nullptr, x, y))); ctx.assign_eq(x, y, js); } } else { roots.insert(scc_id[i], i); } } return new_eq; } /* \brief Propagation for piecewise linear orders */ lbool theory_special_relations::propagate_plo(atom& a) { lbool res = l_true; relation& r = a.get_relation(); if (a.phase()) { r.m_uf.merge(a.v1(), a.v2()); res = enable(a); } else if (r.m_uf.find(a.v1()) == r.m_uf.find(a.v2())) { res = enable(a); } return res; } lbool theory_special_relations::propagate_po(atom& a) { lbool res = l_true; if (a.phase()) { relation& r = a.get_relation(); r.m_uf.merge(a.v1(), a.v2()); res = enable(a); } return res; } lbool theory_special_relations::propagate_tc(atom& a) { if (a.phase()) { VERIFY(a.enable()); relation& r = a.get_relation(); r.m_uf.merge(a.v1(), a.v2()); } return l_true; } lbool theory_special_relations::final_check_po(relation& r) { for (atom* ap : r.m_asserted_atoms) { atom& a = *ap; if (!a.phase() && r.m_uf.find(a.v1()) == r.m_uf.find(a.v2())) { // v1 !-> v2 // find v1 -> v3 -> v4 -> v2 path r.m_explanation.reset(); unsigned timestamp = r.m_graph.get_timestamp(); bool found_path = r.m_graph.find_shortest_reachable_path(a.v1(), a.v2(), timestamp, r); if (found_path) { TRACE("special_relations", tout << "check po conflict\n";); r.m_explanation.push_back(a.explanation()); set_conflict(r); return l_false; } } } return l_true; } void theory_special_relations::propagate() { if (m_can_propagate) { for (auto const& kv : m_relations) { propagate(*kv.m_value); } m_can_propagate = false; } } lbool theory_special_relations::propagate(relation& r) { lbool res = l_true; while (res == l_true && r.m_asserted_qhead < r.m_asserted_atoms.size()) { atom& a = *r.m_asserted_atoms[r.m_asserted_qhead]; switch (r.m_property) { case sr_lo: res = enable(a); break; case sr_plo: res = propagate_plo(a); break; case sr_po: res = propagate_po(a); break; case sr_tc: res = propagate_tc(a); break; default: if (a.phase()) { res = enable(a); } break; } ++r.m_asserted_qhead; } return res; } void theory_special_relations::reset_eh() { for (auto const& kv : m_relations) { dealloc(kv.m_value); } m_relations.reset(); del_atoms(0); } void theory_special_relations::assign_eh(bool_var v, bool is_true) { TRACE("special_relations", tout << "assign bv" << v << " " << (is_true?" <- true":" <- false") << "\n";); atom* a = m_bool_var2atom[v]; a->set_phase(is_true); a->get_relation().m_asserted_atoms.push_back(a); m_can_propagate = true; } void theory_special_relations::push_scope_eh() { theory::push_scope_eh(); for (auto const& kv : m_relations) { kv.m_value->push(); } m_atoms_lim.push_back(m_atoms.size()); } void theory_special_relations::pop_scope_eh(unsigned num_scopes) { for (auto const& kv : m_relations) { kv.m_value->pop(num_scopes); } unsigned new_lvl = m_atoms_lim.size() - num_scopes; del_atoms(m_atoms_lim[new_lvl]); m_atoms_lim.shrink(new_lvl); theory::pop_scope_eh(num_scopes); } void theory_special_relations::del_atoms(unsigned old_size) { atoms::iterator begin = m_atoms.begin() + old_size; atoms::iterator it = m_atoms.end(); while (it != begin) { --it; atom* a = *it; m_bool_var2atom.erase(a->var()); dealloc(a); } m_atoms.shrink(old_size); } void theory_special_relations::collect_statistics(::statistics & st) const { for (auto const& kv : m_relations) { kv.m_value->m_graph.collect_statistics(st); } } model_value_proc * theory_special_relations::mk_value(enode * n, model_generator & mg) { UNREACHABLE(); return nullptr; } void theory_special_relations::ensure_strict(graph& g) { unsigned sz = g.get_num_edges(); for (unsigned i = 0; i < sz; ++i) { if (!g.is_enabled(i)) continue; if (g.get_weight(i) != s_integer(0)) continue; dl_var src = g.get_source(i); dl_var dst = g.get_target(i); if (get_enode(src)->get_root() == get_enode(dst)->get_root()) continue; VERIFY(g.add_strict_edge(src, dst, literal_vector())); } TRACE("special_relations", g.display(tout);); } /** src1 <= i, src2 <= i, src1 != src2 => src1 !<= src2 */ void theory_special_relations::ensure_tree(graph& g) { unsigned sz = g.get_num_nodes(); for (unsigned i = 0; i < sz; ++i) { int_vector const& edges = g.get_in_edges(i); for (unsigned j = 0; j < edges.size(); ++j) { edge_id e1 = edges[j]; if (!g.is_enabled(e1)) continue; SASSERT ((int)i == g.get_target(e1)); dl_var src1 = g.get_source(e1); for (unsigned k = j + 1; k < edges.size(); ++k) { edge_id e2 = edges[k]; if (!g.is_enabled(e2)) continue; dl_var src2 = g.get_source(e2); if (get_enode(src1)->get_root() != get_enode(src2)->get_root() && disconnected(g, src1, src2)) { VERIFY(g.add_strict_edge(src1, src2, literal_vector())); } } } } TRACE("special_relations", g.display(tout);); } bool theory_special_relations::disconnected(graph const& g, dl_var u, dl_var v) const { s_integer val_u = g.get_assignment(u); s_integer val_v = g.get_assignment(v); if (val_u == val_v) return u != v; if (val_u < val_v) { std::swap(u, v); std::swap(val_u, val_v); } SASSERT(val_u > val_v); svector todo; todo.push_back(u); while (!todo.empty()) { u = todo.back(); todo.pop_back(); if (u == v) { return false; } SASSERT(g.get_assignment(u) <= val_u); if (g.get_assignment(u) <= val_v) { continue; } for (edge_id e : g.get_out_edges(u)) { if (is_strict_neighbour_edge(g, e)) { todo.push_back(g.get_target(e)); } } } return true; } expr_ref theory_special_relations::mk_inj(relation& r, model_generator& mg) { ast_manager& m = get_manager(); r.push(); ensure_strict(r.m_graph); func_decl_ref fn(m); expr_ref result(m); arith_util arith(m); sort* const* ty = r.decl()->get_domain(); fn = m.mk_fresh_func_decl("inj", 1, ty, arith.mk_int()); unsigned sz = r.m_graph.get_num_nodes(); func_interp* fi = alloc(func_interp, m, 1); for (unsigned i = 0; i < sz; ++i) { s_integer val = r.m_graph.get_assignment(i); expr* arg = get_expr(i); fi->insert_new_entry(&arg, arith.mk_numeral(val.to_rational(), true)); } TRACE("special_relations", r.m_graph.display(tout);); r.pop(1); fi->set_else(arith.mk_numeral(rational(0), true)); mg.get_model().register_decl(fn, fi); result = arith.mk_le(m.mk_app(fn,m.mk_var(0, *ty)), m.mk_app(fn, m.mk_var(1, *ty))); return result; } expr_ref theory_special_relations::mk_class(relation& r, model_generator& mg) { ast_manager& m = get_manager(); expr_ref result(m); func_decl_ref fn(m); arith_util arith(m); func_interp* fi = alloc(func_interp, m, 1); sort* const* ty = r.decl()->get_domain(); fn = m.mk_fresh_func_decl("class", 1, ty, arith.mk_int()); unsigned sz = r.m_graph.get_num_nodes(); for (unsigned i = 0; i < sz; ++i) { unsigned val = r.m_uf.find(i); expr* arg = get_expr(i); fi->insert_new_entry(&arg, arith.mk_numeral(rational(val), true)); } fi->set_else(arith.mk_numeral(rational(0), true)); mg.get_model().register_decl(fn, fi); result = m.mk_eq(m.mk_app(fn, m.mk_var(0, *ty)), m.mk_app(fn, m.mk_var(1, *ty))); return result; } expr_ref theory_special_relations::mk_interval(relation& r, model_generator& mg, unsigned_vector & lo, unsigned_vector& hi) { graph const& g = r.m_graph; ast_manager& m = get_manager(); expr_ref result(m); func_decl_ref lofn(m), hifn(m); arith_util arith(m); func_interp* lofi = alloc(func_interp, m, 1); func_interp* hifi = alloc(func_interp, m, 1); sort* const* ty = r.decl()->get_domain(); lofn = m.mk_fresh_func_decl("lo", 1, ty, arith.mk_int()); hifn = m.mk_fresh_func_decl("hi", 1, ty, arith.mk_int()); unsigned sz = g.get_num_nodes(); for (unsigned i = 0; i < sz; ++i) { expr* arg = get_expr(i); lofi->insert_new_entry(&arg, arith.mk_numeral(rational(lo[i]), true)); hifi->insert_new_entry(&arg, arith.mk_numeral(rational(hi[i]), true)); } lofi->set_else(arith.mk_numeral(rational(0), true)); hifi->set_else(arith.mk_numeral(rational(0), true)); mg.get_model().register_decl(lofn, lofi); mg.get_model().register_decl(hifn, hifi); result = m.mk_and(arith.mk_le(m.mk_app(lofn, m.mk_var(0, *ty)), m.mk_app(lofn, m.mk_var(1, *ty))), arith.mk_le(m.mk_app(hifn, m.mk_var(1, *ty)), m.mk_app(hifn, m.mk_var(0, *ty)))); return result; } void theory_special_relations::init_model_lo(relation& r, model_generator& m) { expr_ref inj = mk_inj(r, m); func_interp* fi = alloc(func_interp, get_manager(), 2); fi->set_else(inj); m.get_model().register_decl(r.decl(), fi); } void theory_special_relations::init_model_plo(relation& r, model_generator& mg) { expr_ref inj = mk_inj(r, mg); expr_ref cls = mk_class(r, mg); func_interp* fi = alloc(func_interp, get_manager(), 2); fi->set_else(get_manager().mk_and(inj, cls)); mg.get_model().register_decl(r.decl(), fi); } /** \brief model for a partial order, is a recursive function that evaluates membership in partial order over a fixed set of edges. It runs in O(e*n^2) where n is the number of vertices and e number of edges. connected(A, dst, S) = let (A',S') = next1(a1, b1, A, next1(a2, b2, A, ... S, (nil, S))) if A' = nil then false else if member(dst, A') then true else connected(A', dst, S') next1(a, b, A, S, (A',S')) = if member(a, A) and not member(b, S) then (cons(b, A'), cons(b, S')) else (A',S') */ void theory_special_relations::init_model_po(relation& r, model_generator& mg, bool is_reflexive) { ast_manager& m = get_manager(); sort* s = r.m_decl->get_domain(0); datatype_util dt(m); recfun::util rf(m); recfun::decl::plugin& p = rf.get_plugin(); func_decl_ref nil(m), is_nil(m), cons(m), is_cons(m), hd(m), tl(m); sort_ref listS(dt.mk_list_datatype(s, symbol("List"), cons, is_cons, hd, tl, nil, is_nil), m); func_decl_ref fst(m), snd(m), pair(m); expr_ref nilc(m.mk_const(nil), m); expr* T = m.mk_true(); expr* F = m.mk_false(); func_decl* memf, *nextf, *connectedf; { sort* dom[2] = { s, listS }; recfun::promise_def mem = p.ensure_def(symbol("member"), 2, dom, m.mk_bool_sort(), true); memf = mem.get_def()->get_decl(); var_ref xV(m.mk_var(1, s), m); var_ref SV(m.mk_var(0, listS), m); var_ref yV(m), vV(m), wV(m); expr* x = xV, *S = SV; expr_ref mem_body(m); mem_body = m.mk_ite(m.mk_app(is_nil, S), F, m.mk_ite(m.mk_eq(m.mk_app(hd, S), x), T, m.mk_app(memf, x, m.mk_app(tl, S)))); recfun_replace rep(m); var* vars[2] = { xV, SV }; p.set_definition(rep, mem, 2, vars, mem_body); } sort_ref tup(dt.mk_pair_datatype(listS, listS, fst, snd, pair), m); { sort* dom[5] = { s, s, listS, listS, tup }; recfun::promise_def nxt = p.ensure_def(symbol("next"), 5, dom, tup, true); nextf = nxt.get_def()->get_decl(); expr_ref next_body(m); var_ref aV(m.mk_var(4, s), m); var_ref bV(m.mk_var(3, s), m); var_ref AV(m.mk_var(2, listS), m); var_ref SV(m.mk_var(1, listS), m); var_ref tupV(m.mk_var(0, tup), m); expr* a = aV, *b = bV, *A = AV, *S = SV, *t = tupV; next_body = m.mk_ite(m.mk_and(m.mk_app(memf, a, A), m.mk_not(m.mk_app(memf, b, S))), m.mk_app(pair, m.mk_app(cons, b, m.mk_app(fst, t)), m.mk_app(cons, b, m.mk_app(snd, t))), t); recfun_replace rep(m); var* vars[5] = { aV, bV, AV, SV, tupV }; p.set_definition(rep, nxt, 5, vars, next_body); } { sort* dom[3] = { listS, s, listS }; recfun::promise_def connected = p.ensure_def(symbol("connected"), 3, dom, m.mk_bool_sort(), true); connectedf = connected.get_def()->get_decl(); var_ref AV(m.mk_var(2, listS), m); var_ref dstV(m.mk_var(1, s), m); var_ref SV(m.mk_var(0, listS), m); expr* A = AV, *dst = dstV, *S = SV; expr_ref connected_body(m); connected_body = m.mk_app(pair, nilc.get(), S); for (atom* ap : r.m_asserted_atoms) { atom& a = *ap; if (!a.phase()) continue; SASSERT(get_context().get_assignment(a.var()) == l_true); expr* x = get_enode(a.v1())->get_root()->get_owner(); expr* y = get_enode(a.v2())->get_root()->get_owner(); expr* cb = connected_body; expr* args[5] = { x, y, A, S, cb }; connected_body = m.mk_app(nextf, 5, args); } expr_ref Ap(m.mk_app(fst, connected_body.get()), m); expr_ref Sp(m.mk_app(snd, connected_body.get()), m); connected_body = m.mk_ite(m.mk_eq(Ap, nilc), F, m.mk_ite(m.mk_app(memf, dst, Ap), T, m.mk_app(connectedf, Ap, dst, Sp))); TRACE("special_relations", tout << connected_body << "\n";); recfun_replace rep(m); var* vars[3] = { AV, dstV, SV }; p.set_definition(rep, connected, 3, vars, connected_body); } { var_ref xV(m.mk_var(0, s), m); var_ref yV(m.mk_var(1, s), m); expr* x = xV, *y = yV; func_interp* fi = alloc(func_interp, m, 2); expr_ref consx(m.mk_app(cons, x, nilc), m); expr_ref pred(m.mk_app(connectedf, consx, y, consx), m); if (is_reflexive) { pred = m.mk_or(pred, m.mk_eq(x, y)); } fi->set_else(pred); mg.get_model().register_decl(r.decl(), fi); } } /** \brief map each node to an interval of numbers, such that the children are proper sub-intervals. Then the <= relation becomes interval containment. 1. For each vertex, count the number of nodes below it in the transitive closure. Store the result in num_children. 2. Identify each root. 3. Process children, assigning unique (and disjoint) intervals. 4. Extract interpretation. */ void theory_special_relations::init_model_to(relation& r, model_generator& mg) { unsigned_vector num_children, lo, hi; graph const& g = r.m_graph; r.push(); ensure_strict(r.m_graph); ensure_tree(r.m_graph); count_children(g, num_children); assign_interval(g, num_children, lo, hi); expr_ref iv = mk_interval(r, mg, lo, hi); r.pop(1); func_interp* fi = alloc(func_interp, get_manager(), 2); fi->set_else(iv); mg.get_model().register_decl(r.decl(), fi); } bool theory_special_relations::is_neighbour_edge(graph const& g, edge_id edge) const { CTRACE("special_relations_verbose", g.is_enabled(edge), tout << edge << ": " << g.get_source(edge) << " " << g.get_target(edge) << " "; tout << (g.get_assignment(g.get_target(edge)) - g.get_assignment(g.get_source(edge))) << "\n";); return g.is_enabled(edge) && g.get_assignment(g.get_source(edge)) + s_integer(1) == g.get_assignment(g.get_target(edge)); } bool theory_special_relations::is_strict_neighbour_edge(graph const& g, edge_id e) const { return is_neighbour_edge(g, e) && g.get_weight(e) != s_integer(0); } void theory_special_relations::count_children(graph const& g, unsigned_vector& num_children) { unsigned sz = g.get_num_nodes(); svector nodes; num_children.resize(sz, 0); svector processed(sz, false); for (unsigned i = 0; i < sz; ++i) nodes.push_back(i); while (!nodes.empty()) { dl_var v = nodes.back(); if (processed[v]) { nodes.pop_back(); continue; } unsigned nc = 1; bool all_p = true; for (edge_id e : g.get_out_edges(v)) { if (is_strict_neighbour_edge(g, e)) { dl_var dst = g.get_target(e); TRACE("special_relations", tout << v << " -> " << dst << "\n";); if (!processed[dst]) { all_p = false; nodes.push_back(dst); } nc += num_children[dst]; } } if (all_p) { nodes.pop_back(); num_children[v] = nc; processed[v] = true; } } TRACE("special_relations", for (unsigned i = 0; i < sz; ++i) { tout << i << ": " << num_children[i] << "\n"; }); } void theory_special_relations::assign_interval(graph const& g, unsigned_vector const& num_children, unsigned_vector& lo, unsigned_vector& hi) { svector nodes; unsigned sz = g.get_num_nodes(); lo.resize(sz, 0); hi.resize(sz, 0); unsigned offset = 0; for (unsigned i = 0; i < sz; ++i) { bool is_root = true; int_vector const& edges = g.get_in_edges(i); for (edge_id e_id : edges) { is_root &= !g.is_enabled(e_id); } if (is_root) { lo[i] = offset; hi[i] = offset + num_children[i] - 1; offset = hi[i] + 1; nodes.push_back(i); } } while (!nodes.empty()) { dl_var v = nodes.back(); int_vector const& edges = g.get_out_edges(v); unsigned l = lo[v]; unsigned h = hi[v]; (void)h; nodes.pop_back(); for (unsigned i = 0; i < edges.size(); ++i) { SASSERT(l <= h); if (is_strict_neighbour_edge(g, edges[i])) { dl_var dst = g.get_target(edges[i]); lo[dst] = l; hi[dst] = l + num_children[dst] - 1; l = hi[dst] + 1; nodes.push_back(dst); } } SASSERT(l == h); } } void theory_special_relations::init_model(model_generator & m) { for (auto const& kv : m_relations) { switch (kv.m_value->m_property) { case sr_lo: init_model_lo(*kv.m_value, m); break; case sr_plo: init_model_plo(*kv.m_value, m); break; case sr_to: init_model_to(*kv.m_value, m); break; case sr_po: init_model_po(*kv.m_value, m, true); break; case sr_tc: init_model_po(*kv.m_value, m, true); break; default: // other 28 combinations of 0x1F NOT_IMPLEMENTED_YET(); } } } void theory_special_relations::display(std::ostream & out) const { if (m_relations.empty()) return; out << "Theory Special Relations\n"; display_var2enode(out); for (auto const& kv : m_relations) { kv.m_value->display(*this, out); } } void theory_special_relations::collect_asserted_po_atoms(vector>& atoms) const { for (auto const& kv : m_relations) { relation& r = *kv.m_value; if (r.m_property != sr_po) continue; for (atom* ap : r.m_asserted_atoms) { atoms.push_back(std::make_pair(ap->var(), ap->phase())); } } } void theory_special_relations::display_atom(std::ostream & out, atom& a) const { context& ctx = get_context(); expr* e = ctx.bool_var2expr(a.var()); out << (a.phase() ? "" : "(not ") << mk_pp(e, get_manager()) << (a.phase() ? "" : ")") << "\n"; } } z3-z3-4.8.7/src/smt/theory_special_relations.h000066400000000000000000000175231356505360400212740ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: theory_special_relations.h Abstract: Special Relations theory plugin. Author: Nikolaj Bjorner (nbjorner) 2015-9-16 Notes: --*/ #ifndef THEORY_SPECIAL_RELATIONS_H_ #define THEORY_SPECIAL_RELATIONS_H_ #include "ast/special_relations_decl_plugin.h" #include "smt/smt_theory.h" #include "smt/theory_diff_logic.h" #include "util/union_find.h" #include "util/rational.h" namespace smt { class theory_special_relations : public theory { struct relation; class atom { bool_var m_bvar; relation& m_relation; bool m_phase; theory_var m_v1; theory_var m_v2; edge_id m_pos; edge_id m_neg; public: atom(bool_var b, relation& r, theory_var v1, theory_var v2): m_bvar(b), m_relation(r), m_phase(true), m_v1(v1), m_v2(v2) { r.ensure_var(v1); r.ensure_var(v2); literal_vector ls; ls.push_back(literal(b, false)); m_pos = r.m_graph.add_edge(v1, v2, s_integer(0), ls); // v1 <= v2 ls[0] = literal(b, true); m_neg = r.m_graph.add_edge(v2, v1, s_integer(-1), ls); // v2 + 1 <= v1 } bool_var var() const { return m_bvar;} relation& get_relation() const { return m_relation; } bool phase() const { return m_phase; } void set_phase(bool b) { m_phase = b; } theory_var v1() const { return m_v1; } theory_var v2() const { return m_v2; } literal explanation() const { return literal(m_bvar, !m_phase); } bool enable() { edge_id edge = m_phase?m_pos:m_neg; return m_relation.m_graph.enable_edge(edge); } }; typedef ptr_vector atoms; struct scope { unsigned m_asserted_atoms_lim; unsigned m_asserted_qhead_old; }; struct int_ext : public sidl_ext { typedef literal_vector explanation; }; struct graph : public dl_graph { bool add_strict_edge(theory_var v1, theory_var v2, literal_vector const& j) { // v1 + 1 <= v2 return enable_edge(add_edge(v1, v2, s_integer(-1), j)); } bool add_non_strict_edge(theory_var v1, theory_var v2, literal_vector const& j) { // v1 <= v2 return enable_edge(add_edge(v1, v2, s_integer(0), j)); } }; typedef union_find union_find_t; struct relation { ast_manager& m; func_decl_ref m_next; sr_property m_property; func_decl* m_decl; atoms m_asserted_atoms; // set of asserted atoms unsigned m_asserted_qhead; svector m_scopes; graph m_graph; union_find_default_ctx m_ufctx; union_find_t m_uf; literal_vector m_explanation; relation(sr_property p, func_decl* d, ast_manager& m): m(m), m_next(m), m_property(p), m_decl(d), m_asserted_qhead(0), m_uf(m_ufctx) {} func_decl* decl() { return m_decl; } app_ref next(expr* a, expr* b) { return app_ref(m.mk_app(next(), a, b), m); } bool is_next(expr* n) { return is_app(n) && next() == to_app(n)->get_decl(); } func_decl* next(); void push(); void pop(unsigned num_scopes); void ensure_var(theory_var v); bool new_eq_eh(literal l, theory_var v1, theory_var v2); void operator()(literal_vector const & ex) { m_explanation.append(ex); } void new_edge(dl_var src, dl_var dst, unsigned num_edges, edge_id const* edges) {} bool add_strict_edge(theory_var v1, theory_var v2, literal_vector const& j); bool add_non_strict_edge(theory_var v1, theory_var v2, literal_vector const& j); std::ostream& display(theory_special_relations const& sr, std::ostream& out) const; }; typedef u_map bool_var2atom; special_relations_util m_util; atoms m_atoms; unsigned_vector m_atoms_lim; obj_map m_relations; bool_var2atom m_bool_var2atom; bool m_can_propagate; void del_atoms(unsigned old_size); lbool final_check(relation& r); lbool final_check_po(relation& r); lbool final_check_lo(relation& r); lbool final_check_plo(relation& r); lbool final_check_to(relation& r); lbool final_check_tc(relation& r); lbool propagate(relation& r); lbool enable(atom& a); bool extract_equalities(relation& r); void set_neg_cycle_conflict(relation& r); void set_conflict(relation& r); lbool propagate_plo(atom& a); lbool propagate_po(atom& a); lbool propagate_tc(atom& a); theory_var mk_var(expr* e); void count_children(graph const& g, unsigned_vector& num_children); void ensure_strict(graph& g); void ensure_tree(graph& g); void assign_interval(graph const& g, unsigned_vector const& num_children, unsigned_vector& lo, unsigned_vector& hi); expr_ref mk_inj(relation& r, model_generator& m); expr_ref mk_class(relation& r, model_generator& m); expr_ref mk_interval(relation& r, model_generator& mg, unsigned_vector & lo, unsigned_vector& hi); void init_model_lo(relation& r, model_generator& m); void init_model_to(relation& r, model_generator& m); void init_model_po(relation& r, model_generator& m, bool is_reflexive); void init_model_plo(relation& r, model_generator& m); bool is_neighbour_edge(graph const& g, edge_id id) const; bool is_strict_neighbour_edge(graph const& g, edge_id id) const; bool disconnected(graph const& g, dl_var u, dl_var v) const; literal mk_literal(expr* _e); enode* ensure_enode(expr* e); theory_var mk_var(enode* n) override; void collect_asserted_po_atoms(vector< std::pair >& atoms) const; void display_atom(std::ostream & out, atom& a) const; void internalize_next(func_decl* f, app * term); public: theory_special_relations(ast_manager& m); ~theory_special_relations() override; theory * mk_fresh(context * new_ctx) override; bool internalize_atom(app * atom, bool gate_ctx) override; bool internalize_term(app * term) override; void new_eq_eh(theory_var v1, theory_var v2) override; void new_diseq_eh(theory_var v1, theory_var v2) override {} bool use_diseqs() const override { return false; } bool build_models() const override { return true; } final_check_status final_check_eh() override; void reset_eh() override; void assign_eh(bool_var v, bool is_true) override; void init_search_eh() override {} void push_scope_eh() override; void pop_scope_eh(unsigned num_scopes) override; void restart_eh() override {} void collect_statistics(::statistics & st) const override; model_value_proc * mk_value(enode * n, model_generator & mg) override; void init_model(model_generator & m) override; bool can_propagate() override { return m_can_propagate; } void propagate() override; void display(std::ostream & out) const override; }; } #endif z3-z3-4.8.7/src/smt/theory_str.cpp000066400000000000000000017133221356505360400167400ustar00rootroot00000000000000/*++ Module Name: theory_str.cpp Abstract: String Theory Plugin Author: Murphy Berzish and Yunhui Zheng Revision History: --*/ #include "ast/ast_smt2_pp.h" #include "smt/smt_context.h" #include "smt/theory_str.h" #include "smt/smt_model_generator.h" #include "ast/ast_pp.h" #include "ast/ast_ll_pp.h" #include #include #include "smt/theory_seq_empty.h" #include "smt/theory_arith.h" #include "ast/ast_util.h" #include "ast/rewriter/seq_rewriter.h" #include "smt_kernel.h" namespace smt { theory_str::theory_str(ast_manager & m, theory_str_params const & params): theory(m.mk_family_id("seq")), m_params(params), /* Options */ opt_EagerStringConstantLengthAssertions(true), opt_VerifyFinalCheckProgress(false), opt_LCMUnrollStep(2), opt_NoQuickReturn_IntegerTheory(false), opt_DisableIntegerTheoryIntegration(false), opt_DeferEQCConsistencyCheck(false), opt_CheckVariableScope(true), opt_ConcatOverlapAvoid(true), /* Internal setup */ search_started(false), m_autil(m), u(m), sLevel(0), finalCheckProgressIndicator(false), m_trail(m), m_factory(nullptr), m_mk_aut(m), m_unused_id(0), m_delayed_axiom_setup_terms(m), m_delayed_assertions_todo(m), m_persisted_axioms(m), m_persisted_axiom_todo(m), tmpStringVarCount(0), tmpXorVarCount(0), tmpLenTestVarCount(0), tmpValTestVarCount(0), avoidLoopCut(true), loopDetected(false), m_theoryStrOverlapAssumption_term(m), contains_map(m), string_int_conversion_terms(m), totalCacheAccessCount(0), cacheHitCount(0), cacheMissCount(0), m_fresh_id(0), m_trail_stack(*this), m_find(*this) { initialize_charset(); } theory_str::~theory_str() { m_trail_stack.reset(); for (eautomaton * aut : regex_automata) { dealloc(aut); } regex_automata.clear(); } expr * theory_str::mk_string(zstring const& str) { if (m_params.m_StringConstantCache) { ++totalCacheAccessCount; expr * val; if (stringConstantCache.find(str, val)) { return val; } else { val = u.str.mk_string(str); m_trail.push_back(val); stringConstantCache.insert(str, val); return val; } } else { return u.str.mk_string(str); } } expr * theory_str::mk_string(const char * str) { symbol sym(str); return u.str.mk_string(sym); } class seq_expr_solver : public expr_solver { kernel m_kernel; public: seq_expr_solver(ast_manager& m, smt_params& fp): m_kernel(m, fp) {} lbool check_sat(expr* e) override { m_kernel.push(); m_kernel.assert_expr(e); lbool r = m_kernel.check(); m_kernel.pop(1); return r; } }; void theory_str::init(context * ctx) { theory::init(ctx); m_mk_aut.set_solver(alloc(seq_expr_solver, get_manager(), get_context().get_fparams())); } void theory_str::initialize_charset() { bool defaultCharset = true; if (defaultCharset) { // valid C strings can't contain the null byte ('\0') charSetSize = 255; char_set.resize(256, 0); int idx = 0; // small letters for (int i = 97; i < 123; i++) { char_set[idx] = (char) i; charSetLookupTable[char_set[idx]] = idx; idx++; } // caps for (int i = 65; i < 91; i++) { char_set[idx] = (char) i; charSetLookupTable[char_set[idx]] = idx; idx++; } // numbers for (int i = 48; i < 58; i++) { char_set[idx] = (char) i; charSetLookupTable[char_set[idx]] = idx; idx++; } // printable marks - 1 for (int i = 32; i < 48; i++) { char_set[idx] = (char) i; charSetLookupTable[char_set[idx]] = idx; idx++; } // printable marks - 2 for (int i = 58; i < 65; i++) { char_set[idx] = (char) i; charSetLookupTable[char_set[idx]] = idx; idx++; } // printable marks - 3 for (int i = 91; i < 97; i++) { char_set[idx] = (char) i; charSetLookupTable[char_set[idx]] = idx; idx++; } // printable marks - 4 for (int i = 123; i < 127; i++) { char_set[idx] = (char) i; charSetLookupTable[char_set[idx]] = idx; idx++; } // non-printable - 1 for (int i = 1; i < 32; i++) { char_set[idx] = (char) i; charSetLookupTable[char_set[idx]] = idx; idx++; } // non-printable - 2 for (int i = 127; i < 256; i++) { char_set[idx] = (char) i; charSetLookupTable[char_set[idx]] = idx; idx++; } } else { const char setset[] = { 'a', 'b', 'c' }; int fSize = sizeof(setset) / sizeof(char); char_set.resize(fSize, 0); charSetSize = fSize; for (int i = 0; i < charSetSize; i++) { char_set[i] = setset[i]; charSetLookupTable[setset[i]] = i; } } } void theory_str::assert_axiom(expr * _e) { if (_e == nullptr) return; if (opt_VerifyFinalCheckProgress) { finalCheckProgressIndicator = true; } if (get_manager().is_true(_e)) return; context & ctx = get_context(); ast_manager& m = get_manager(); TRACE("str", tout << "asserting " << mk_ismt2_pp(_e, m) << std::endl;); expr_ref e(_e, m); //th_rewriter rw(m); //rw(e); if (!ctx.b_internalized(e)) { ctx.internalize(e, false); } literal lit(ctx.get_literal(e)); ctx.mark_as_relevant(lit); if (m.has_trace_stream()) log_axiom_instantiation(e); ctx.mk_th_axiom(get_id(), 1, &lit); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; // crash/error avoidance: add all axioms to the trail m_trail.push_back(e); //TRACE("str", tout << "done asserting " << mk_ismt2_pp(e, get_manager()) << std::endl;); } expr * theory_str::rewrite_implication(expr * premise, expr * conclusion) { ast_manager & m = get_manager(); return m.mk_or(mk_not(m, premise), conclusion); } void theory_str::assert_implication(expr * premise, expr * conclusion) { ast_manager & m = get_manager(); TRACE("str", tout << "asserting implication " << mk_ismt2_pp(premise, m) << " -> " << mk_ismt2_pp(conclusion, m) << std::endl;); expr_ref axiom(m.mk_or(mk_not(m, premise), conclusion), m); assert_axiom(axiom); } bool theory_str::internalize_atom(app * atom, bool gate_ctx) { return internalize_term(atom); } bool theory_str::internalize_term(app * term) { context & ctx = get_context(); ast_manager & m = get_manager(); SASSERT(term->get_family_id() == get_family_id()); TRACE("str", tout << "internalizing term: " << mk_ismt2_pp(term, get_manager()) << std::endl;); // emulation of user_smt_theory::internalize_term() unsigned num_args = term->get_num_args(); for (unsigned i = 0; i < num_args; ++i) { ctx.internalize(term->get_arg(i), false); } if (ctx.e_internalized(term)) { enode * e = ctx.get_enode(term); mk_var(e); return true; } // m_parents.push_back(term); enode * e = ctx.mk_enode(term, false, m.is_bool(term), true); if (m.is_bool(term)) { bool_var bv = ctx.mk_bool_var(term); ctx.set_var_theory(bv, get_id()); ctx.set_enode_flag(bv, true); } // make sure every argument is attached to a theory variable for (unsigned i = 0; i < num_args; ++i) { enode * arg = e->get_arg(i); theory_var v_arg = mk_var(arg); TRACE("str", tout << "arg has theory var #" << v_arg << std::endl;); (void)v_arg; } theory_var v = mk_var(e); TRACE("str", tout << "term has theory var #" << v << std::endl;); (void)v; if (opt_EagerStringConstantLengthAssertions && u.str.is_string(term)) { TRACE("str", tout << "eagerly asserting length of string term " << mk_pp(term, m) << std::endl;); m_basicstr_axiom_todo.insert(e); } return true; } enode* theory_str::ensure_enode(expr* e) { context& ctx = get_context(); if (!ctx.e_internalized(e)) { ctx.internalize(e, false); } enode* n = ctx.get_enode(e); ctx.mark_as_relevant(n); return n; } void theory_str::refresh_theory_var(expr * e) { ast_manager & m = get_manager(); enode * en = ensure_enode(e); theory_var v = mk_var(en); (void)v; TRACE("str", tout << "refresh " << mk_pp(e, get_manager()) << ": v#" << v << std::endl;); if (m.get_sort(e) == u.str.mk_string_sort()) { m_basicstr_axiom_todo.push_back(en); } } theory_var theory_str::mk_var(enode* n) { TRACE("str", tout << "mk_var for " << mk_pp(n->get_owner(), get_manager()) << std::endl;); ast_manager & m = get_manager(); if (!(m.get_sort(n->get_owner()) == u.str.mk_string_sort())) { return null_theory_var; } if (is_attached_to_var(n)) { TRACE("str", tout << "already attached to theory var" << std::endl;); return n->get_th_var(get_id()); } else { theory_var v = theory::mk_var(n); m_find.mk_var(); TRACE("str", tout << "new theory var v#" << v << " find " << m_find.find(v) << std::endl;); get_context().attach_th_var(n, this, v); get_context().mark_as_relevant(n); return v; } } static void cut_vars_map_copy(obj_map & dest, obj_map & src) { for (auto const& kv : src) { dest.insert(kv.m_key, 1); } } bool theory_str::has_self_cut(expr * n1, expr * n2) { if (!cut_var_map.contains(n1)) { return false; } if (!cut_var_map.contains(n2)) { return false; } if (cut_var_map[n1].empty() || cut_var_map[n2].empty()) { return false; } for (auto const& kv : cut_var_map[n1].top()->vars) { if (cut_var_map[n2].top()->vars.contains(kv.m_key)) { return true; } } return false; } void theory_str::add_cut_info_one_node(expr * baseNode, int slevel, expr * node) { // crash avoidance? m_trail.push_back(baseNode); m_trail.push_back(node); if (!cut_var_map.contains(baseNode)) { T_cut * varInfo = alloc(T_cut); m_cut_allocs.push_back(varInfo); varInfo->level = slevel; varInfo->vars.insert(node, 1); cut_var_map.insert(baseNode, std::stack()); cut_var_map[baseNode].push(varInfo); TRACE("str", tout << "add var info for baseNode=" << mk_pp(baseNode, get_manager()) << ", node=" << mk_pp(node, get_manager()) << " [" << slevel << "]" << std::endl;); } else { if (cut_var_map[baseNode].empty()) { T_cut * varInfo = alloc(T_cut); m_cut_allocs.push_back(varInfo); varInfo->level = slevel; varInfo->vars.insert(node, 1); cut_var_map[baseNode].push(varInfo); TRACE("str", tout << "add var info for baseNode=" << mk_pp(baseNode, get_manager()) << ", node=" << mk_pp(node, get_manager()) << " [" << slevel << "]" << std::endl;); } else { if (cut_var_map[baseNode].top()->level < slevel) { T_cut * varInfo = alloc(T_cut); m_cut_allocs.push_back(varInfo); varInfo->level = slevel; cut_vars_map_copy(varInfo->vars, cut_var_map[baseNode].top()->vars); varInfo->vars.insert(node, 1); cut_var_map[baseNode].push(varInfo); TRACE("str", tout << "add var info for baseNode=" << mk_pp(baseNode, get_manager()) << ", node=" << mk_pp(node, get_manager()) << " [" << slevel << "]" << std::endl;); } else if (cut_var_map[baseNode].top()->level == slevel) { cut_var_map[baseNode].top()->vars.insert(node, 1); TRACE("str", tout << "add var info for baseNode=" << mk_pp(baseNode, get_manager()) << ", node=" << mk_pp(node, get_manager()) << " [" << slevel << "]" << std::endl;); } else { get_manager().raise_exception("entered illegal state during add_cut_info_one_node()"); } } } } void theory_str::add_cut_info_merge(expr * destNode, int slevel, expr * srcNode) { // crash avoidance? m_trail.push_back(destNode); m_trail.push_back(srcNode); if (!cut_var_map.contains(srcNode)) { get_manager().raise_exception("illegal state in add_cut_info_merge(): cut_var_map doesn't contain srcNode"); } if (cut_var_map[srcNode].empty()) { get_manager().raise_exception("illegal state in add_cut_info_merge(): cut_var_map[srcNode] is empty"); } if (!cut_var_map.contains(destNode)) { T_cut * varInfo = alloc(T_cut); m_cut_allocs.push_back(varInfo); varInfo->level = slevel; cut_vars_map_copy(varInfo->vars, cut_var_map[srcNode].top()->vars); cut_var_map.insert(destNode, std::stack()); cut_var_map[destNode].push(varInfo); TRACE("str", tout << "merge var info for destNode=" << mk_pp(destNode, get_manager()) << ", srcNode=" << mk_pp(srcNode, get_manager()) << " [" << slevel << "]" << std::endl;); } else { if (cut_var_map[destNode].empty() || cut_var_map[destNode].top()->level < slevel) { T_cut * varInfo = alloc(T_cut); m_cut_allocs.push_back(varInfo); varInfo->level = slevel; cut_vars_map_copy(varInfo->vars, cut_var_map[destNode].top()->vars); cut_vars_map_copy(varInfo->vars, cut_var_map[srcNode].top()->vars); cut_var_map[destNode].push(varInfo); TRACE("str", tout << "merge var info for destNode=" << mk_pp(destNode, get_manager()) << ", srcNode=" << mk_pp(srcNode, get_manager()) << " [" << slevel << "]" << std::endl;); } else if (cut_var_map[destNode].top()->level == slevel) { cut_vars_map_copy(cut_var_map[destNode].top()->vars, cut_var_map[srcNode].top()->vars); TRACE("str", tout << "merge var info for destNode=" << mk_pp(destNode, get_manager()) << ", srcNode=" << mk_pp(srcNode, get_manager()) << " [" << slevel << "]" << std::endl;); } else { get_manager().raise_exception("illegal state in add_cut_info_merge(): inconsistent slevels"); } } } void theory_str::check_and_init_cut_var(expr * node) { if (cut_var_map.contains(node)) { return; } else if (!u.str.is_string(node)) { add_cut_info_one_node(node, -1, node); } } literal theory_str::mk_literal(expr* _e) { ast_manager & m = get_manager(); expr_ref e(_e, m); context& ctx = get_context(); ensure_enode(e); return ctx.get_literal(e); } app * theory_str::mk_int(int n) { return m_autil.mk_numeral(rational(n), true); } app * theory_str::mk_int(rational & q) { return m_autil.mk_numeral(q, true); } expr * theory_str::mk_internal_lenTest_var(expr * node, int lTries) { ast_manager & m = get_manager(); std::stringstream ss; ss << "$$_len_" << mk_ismt2_pp(node, m) << "_" << lTries << "_" << tmpLenTestVarCount; tmpLenTestVarCount += 1; std::string name = ss.str(); app * var = mk_str_var(name); internal_lenTest_vars.insert(var); m_trail.push_back(var); return var; } expr * theory_str::mk_internal_valTest_var(expr * node, int len, int vTries) { ast_manager & m = get_manager(); std::stringstream ss; ss << "$$_val_" << mk_ismt2_pp(node, m) << "_" << len << "_" << vTries << "_" << tmpValTestVarCount; tmpValTestVarCount += 1; std::string name = ss.str(); app * var = mk_str_var(name); internal_valTest_vars.insert(var); m_trail.push_back(var); return var; } void theory_str::track_variable_scope(expr * var) { if (internal_variable_scope_levels.find(sLevel) == internal_variable_scope_levels.end()) { internal_variable_scope_levels[sLevel] = obj_hashtable(); } internal_variable_scope_levels[sLevel].insert(var); } app * theory_str::mk_internal_xor_var() { return mk_int_var("$$_xor"); } app * theory_str::mk_fresh_const(char const* name, sort* s) { string_buffer<64> buffer; buffer << name; buffer << "!tmp"; buffer << m_fresh_id; m_fresh_id++; return u.mk_skolem(symbol(buffer.c_str()), 0, nullptr, s); } app * theory_str::mk_int_var(std::string name) { context & ctx = get_context(); ast_manager & m = get_manager(); TRACE("str", tout << "creating integer variable " << name << " at scope level " << sLevel << std::endl;); sort * int_sort = m.mk_sort(m_autil.get_family_id(), INT_SORT); app * a = mk_fresh_const(name.c_str(), int_sort); ctx.internalize(a, false); 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. //mk_var(ctx.get_enode(a)); m_trail.push_back(a); //variable_set.insert(a); //internal_variable_set.insert(a); //track_variable_scope(a); return a; } app * theory_str::mk_unroll_bound_var() { return mk_int_var("unroll"); } app * theory_str::mk_unroll_test_var() { app * v = mk_str_var("unrollTest"); // was uRt internal_unrollTest_vars.insert(v); track_variable_scope(v); return v; } app * theory_str::mk_str_var(std::string name) { context & ctx = get_context(); TRACE("str", tout << "creating string variable " << name << " at scope level " << sLevel << std::endl;); sort * string_sort = u.str.mk_string_sort(); app * a = mk_fresh_const(name.c_str(), string_sort); m_trail.push_back(a); TRACE("str", tout << "a->get_family_id() = " << a->get_family_id() << std::endl << "this->get_family_id() = " << this->get_family_id() << std::endl;); // I have a hunch that this may not get internalized for free... ctx.internalize(a, false); SASSERT(ctx.get_enode(a) != nullptr); SASSERT(ctx.e_internalized(a)); // this might help?? mk_var(ctx.get_enode(a)); m_basicstr_axiom_todo.push_back(ctx.get_enode(a)); TRACE("str", tout << "add " << mk_pp(a, get_manager()) << " to m_basicstr_axiom_todo" << std::endl;); variable_set.insert(a); internal_variable_set.insert(a); track_variable_scope(a); return a; } app * theory_str::mk_regex_rep_var() { context & ctx = get_context(); sort * string_sort = u.str.mk_string_sort(); app * a = mk_fresh_const("regex", string_sort); m_trail.push_back(a); ctx.internalize(a, false); 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)); TRACE("str", tout << "add " << mk_pp(a, get_manager()) << " to m_basicstr_axiom_todo" << std::endl;); variable_set.insert(a); //internal_variable_set.insert(a); regex_variable_set.insert(a); track_variable_scope(a); return a; } void theory_str::add_nonempty_constraint(expr * s) { context & ctx = get_context(); ast_manager & m = get_manager(); expr_ref ax1(mk_not(m, ctx.mk_eq_atom(s, mk_string(""))), m); assert_axiom(ax1); { // build LHS expr_ref len_str(mk_strlen(s), m); SASSERT(len_str); // build RHS expr_ref zero(m_autil.mk_numeral(rational(0), true), m); SASSERT(zero); // build LHS > RHS and assert // we have to build !(LHS <= RHS) instead expr_ref lhs_gt_rhs(mk_not(m, m_autil.mk_le(len_str, zero)), m); SASSERT(lhs_gt_rhs); assert_axiom(lhs_gt_rhs); } } app * theory_str::mk_nonempty_str_var() { context & ctx = get_context(); ast_manager & m = get_manager(); std::stringstream ss; ss << tmpStringVarCount; tmpStringVarCount++; std::string name = "$$_str" + ss.str(); TRACE("str", tout << "creating nonempty string variable " << name << " at scope level " << sLevel << std::endl;); sort * string_sort = u.str.mk_string_sort(); app * a = mk_fresh_const(name.c_str(), string_sort); ctx.internalize(a, false); SASSERT(ctx.get_enode(a) != nullptr); // this might help?? mk_var(ctx.get_enode(a)); // assert a variation of the basic string axioms that ensures this string is nonempty { // build LHS expr_ref len_str(mk_strlen(a), m); SASSERT(len_str); // build RHS expr_ref zero(m_autil.mk_numeral(rational(0), true), m); SASSERT(zero); // build LHS > RHS and assert // we have to build !(LHS <= RHS) instead expr_ref lhs_gt_rhs(mk_not(m, m_autil.mk_le(len_str, zero)), m); SASSERT(lhs_gt_rhs); assert_axiom(lhs_gt_rhs); } // add 'a' to variable sets, so we can keep track of it m_trail.push_back(a); variable_set.insert(a); internal_variable_set.insert(a); track_variable_scope(a); return a; } app * theory_str::mk_unroll(expr * n, expr * bound) { context & ctx = get_context(); ast_manager & m = get_manager(); expr * args[2] = {n, bound}; app * unrollFunc = get_manager().mk_app(get_id(), _OP_RE_UNROLL, 0, nullptr, 2, args); m_trail.push_back(unrollFunc); expr_ref_vector items(m); items.push_back(ctx.mk_eq_atom(ctx.mk_eq_atom(bound, mk_int(0)), ctx.mk_eq_atom(unrollFunc, mk_string("")))); items.push_back(m_autil.mk_ge(bound, mk_int(0))); items.push_back(m_autil.mk_ge(mk_strlen(unrollFunc), mk_int(0))); expr_ref finalAxiom(mk_and(items), m); SASSERT(finalAxiom); assert_axiom(finalAxiom); return unrollFunc; } app * theory_str::mk_contains(expr * haystack, expr * needle) { app * contains = u.str.mk_contains(haystack, needle); // TODO double-check semantics/argument order m_trail.push_back(contains); // immediately force internalization so that axiom setup does not fail get_context().internalize(contains, false); set_up_axioms(contains); return contains; } // note, this invokes "special-case" handling for the start index being 0 app * theory_str::mk_indexof(expr * haystack, expr * needle) { app * indexof = u.str.mk_index(haystack, needle, mk_int(0)); m_trail.push_back(indexof); // immediately force internalization so that axiom setup does not fail get_context().internalize(indexof, false); set_up_axioms(indexof); return indexof; } app * theory_str::mk_strlen(expr * e) { /*if (m_strutil.is_string(e)) {*/ if (false) { zstring strval; u.str.is_string(e, strval); unsigned int len = strval.length(); return m_autil.mk_numeral(rational(len), true); } else { if (false) { // use cache app * lenTerm = nullptr; if (!length_ast_map.find(e, lenTerm)) { lenTerm = u.str.mk_length(e); length_ast_map.insert(e, lenTerm); m_trail.push_back(lenTerm); } return lenTerm; } else { // always regen return u.str.mk_length(e); } } } /* * 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 nullptr. * (note: this function was strTheory::Concat()) */ expr * theory_str::mk_concat_const_str(expr * n1, expr * n2) { bool n1HasEqcValue = false; bool n2HasEqcValue = false; expr * v1 = get_eqc_value(n1, n1HasEqcValue); expr * v2 = get_eqc_value(n2, n2HasEqcValue); if (u.str.is_string(v1)) { n1HasEqcValue = true; } if (u.str.is_string(v2)) { n2HasEqcValue = true; } if (n1HasEqcValue && n2HasEqcValue) { zstring n1_str; u.str.is_string(v1, n1_str); zstring n2_str; u.str.is_string(v2, n2_str); zstring result = n1_str + n2_str; return mk_string(result); } else if (n1HasEqcValue && !n2HasEqcValue) { zstring n1_str; u.str.is_string(v1, n1_str); if (n1_str.empty()) { return n2; } } else if (!n1HasEqcValue && n2HasEqcValue) { zstring n2_str; u.str.is_string(v2, n2_str); if (n2_str.empty()) { return n1; } } return nullptr; } expr * theory_str::mk_concat(expr * n1, expr * n2) { context & ctx = get_context(); ast_manager & m = get_manager(); ENSURE(n1 != nullptr); ENSURE(n2 != nullptr); bool n1HasEqcValue = false; bool n2HasEqcValue = false; n1 = get_eqc_value(n1, n1HasEqcValue); n2 = get_eqc_value(n2, n2HasEqcValue); if (n1HasEqcValue && n2HasEqcValue) { return mk_concat_const_str(n1, n2); } else if (n1HasEqcValue && !n2HasEqcValue) { bool n2_isConcatFunc = u.str.is_concat(to_app(n2)); zstring n1_str; u.str.is_string(n1, n1_str); if (n1_str.empty()) { return n2; } if (n2_isConcatFunc) { expr * n2_arg0 = to_app(n2)->get_arg(0); expr * n2_arg1 = to_app(n2)->get_arg(1); if (u.str.is_string(n2_arg0)) { n1 = mk_concat_const_str(n1, n2_arg0); // n1 will be a constant n2 = n2_arg1; } } } else if (!n1HasEqcValue && n2HasEqcValue) { zstring n2_str; u.str.is_string(n2, n2_str); if (n2_str.empty()) { return n1; } if (u.str.is_concat(to_app(n1))) { expr * n1_arg0 = to_app(n1)->get_arg(0); expr * n1_arg1 = to_app(n1)->get_arg(1); if (u.str.is_string(n1_arg1)) { n1 = n1_arg0; n2 = mk_concat_const_str(n1_arg1, n2); // n2 will be a constant } } } else { if (u.str.is_concat(to_app(n1)) && u.str.is_concat(to_app(n2))) { expr * n1_arg0 = to_app(n1)->get_arg(0); expr * n1_arg1 = to_app(n1)->get_arg(1); expr * n2_arg0 = to_app(n2)->get_arg(0); expr * n2_arg1 = to_app(n2)->get_arg(1); if (u.str.is_string(n1_arg1) && u.str.is_string(n2_arg0)) { expr * tmpN1 = n1_arg0; expr * tmpN2 = mk_concat_const_str(n1_arg1, n2_arg0); n1 = mk_concat(tmpN1, tmpN2); n2 = n2_arg1; } } } //------------------------------------------------------ // * expr * ast1 = mk_2_arg_app(ctx, td->Concat, n1, n2); // * expr * ast2 = mk_2_arg_app(ctx, td->Concat, n1, n2); // Z3 treats (ast1) and (ast2) as two different nodes. //------------------------------------------------------- expr * concatAst = nullptr; if (!concat_astNode_map.find(n1, n2, concatAst)) { concatAst = u.str.mk_concat(n1, n2); m_trail.push_back(concatAst); concat_astNode_map.insert(n1, n2, concatAst); expr_ref concat_length(mk_strlen(concatAst), m); ptr_vector childrenVector; get_nodes_in_concat(concatAst, childrenVector); expr_ref_vector items(m); for (auto el : childrenVector) { items.push_back(mk_strlen(el)); } expr_ref lenAssert(ctx.mk_eq_atom(concat_length, m_autil.mk_add(items.size(), items.c_ptr())), m); assert_axiom(lenAssert); } return concatAst; } bool theory_str::can_propagate() { return !m_basicstr_axiom_todo.empty() || !m_str_eq_todo.empty() || !m_concat_axiom_todo.empty() || !m_concat_eval_todo.empty() || !m_library_aware_axiom_todo.empty() || !m_delayed_axiom_setup_terms.empty() || !m_persisted_axiom_todo.empty() || (search_started && !m_delayed_assertions_todo.empty()) ; } void theory_str::propagate() { context & ctx = get_context(); while (can_propagate()) { TRACE("str", tout << "propagating..." << std::endl;); while(true) { // this can potentially recursively activate itself unsigned start_count = m_basicstr_axiom_todo.size(); ptr_vector axioms_tmp(m_basicstr_axiom_todo); for (auto const& el : axioms_tmp) { instantiate_basic_string_axioms(el); } unsigned end_count = m_basicstr_axiom_todo.size(); if (end_count > start_count) { TRACE("str", tout << "new basic string axiom terms added -- checking again" << std::endl;); continue; } else { break; } } m_basicstr_axiom_todo.reset(); TRACE("str", tout << "reset m_basicstr_axiom_todo" << std::endl;); for (auto const& pair : m_str_eq_todo) { enode * lhs = pair.first; enode * rhs = pair.second; handle_equality(lhs->get_owner(), rhs->get_owner()); } m_str_eq_todo.reset(); for (auto const& el : m_concat_axiom_todo) { instantiate_concat_axiom(el); } m_concat_axiom_todo.reset(); for (auto const& el : m_concat_eval_todo) { try_eval_concat(el); } m_concat_eval_todo.reset(); while(true) { // Special handling: terms can recursively set up other terms // (e.g. indexof can instantiate other indexof terms). // - Copy the list so it can potentially be modified during setup. // - Don't clear this list if new ones are added in the process; // instead, set up all the new terms before proceeding. // TODO see if any other propagate() worklists need this kind of handling // TODO we really only need to check the new ones on each pass unsigned start_count = m_library_aware_axiom_todo.size(); ptr_vector axioms_tmp(m_library_aware_axiom_todo); for (auto const& e : axioms_tmp) { app * a = e->get_owner(); if (u.str.is_stoi(a)) { instantiate_axiom_str_to_int(e); } else if (u.str.is_itos(a)) { instantiate_axiom_int_to_str(e); } else if (u.str.is_at(a)) { instantiate_axiom_CharAt(e); } else if (u.str.is_prefix(a)) { instantiate_axiom_prefixof(e); } else if (u.str.is_suffix(a)) { instantiate_axiom_suffixof(e); } else if (u.str.is_contains(a)) { instantiate_axiom_Contains(e); } else if (u.str.is_index(a)) { instantiate_axiom_Indexof(e); } else if (u.str.is_extract(a)) { instantiate_axiom_Substr(e); } else if (u.str.is_replace(a)) { instantiate_axiom_Replace(e); } else if (u.str.is_in_re(a)) { instantiate_axiom_RegexIn(e); } else { TRACE("str", tout << "BUG: unhandled library-aware term " << mk_pp(e->get_owner(), get_manager()) << std::endl;); NOT_IMPLEMENTED_YET(); } } unsigned end_count = m_library_aware_axiom_todo.size(); if (end_count > start_count) { TRACE("str", tout << "new library-aware terms added during axiom setup -- checking again" << std::endl;); continue; } else { break; } } m_library_aware_axiom_todo.reset(); for (auto el : m_delayed_axiom_setup_terms) { // I think this is okay ctx.internalize(el, false); set_up_axioms(el); } m_delayed_axiom_setup_terms.reset(); for (expr * a : m_persisted_axiom_todo) { assert_axiom(a); } m_persisted_axiom_todo.reset(); if (search_started) { for (auto const& el : m_delayed_assertions_todo) { assert_axiom(el); } m_delayed_assertions_todo.reset(); } } } /* * Attempt to evaluate a concat over constant strings, * and if this is possible, assert equality between the * flattened string and the original term. */ void theory_str::try_eval_concat(enode * cat) { app * a_cat = cat->get_owner(); SASSERT(u.str.is_concat(a_cat)); context & ctx = get_context(); ast_manager & m = get_manager(); TRACE("str", tout << "attempting to flatten " << mk_pp(a_cat, m) << std::endl;); std::stack worklist; zstring flattenedString(""); bool constOK = true; { app * arg0 = to_app(a_cat->get_arg(0)); app * arg1 = to_app(a_cat->get_arg(1)); worklist.push(arg1); worklist.push(arg0); } while (constOK && !worklist.empty()) { app * evalArg = worklist.top(); worklist.pop(); zstring nextStr; if (u.str.is_string(evalArg, nextStr)) { flattenedString = flattenedString + nextStr; } else if (u.str.is_concat(evalArg)) { app * arg0 = to_app(evalArg->get_arg(0)); app * arg1 = to_app(evalArg->get_arg(1)); worklist.push(arg1); worklist.push(arg0); } else { TRACE("str", tout << "non-constant term in concat -- giving up." << std::endl;); constOK = false; break; } } if (constOK) { TRACE("str", tout << "flattened to \"" << flattenedString.encode().c_str() << "\"" << std::endl;); expr_ref constStr(mk_string(flattenedString), m); expr_ref axiom(ctx.mk_eq_atom(a_cat, constStr), m); assert_axiom(axiom); } } /* * Instantiate an axiom of the following form: * Length(Concat(x, y)) = Length(x) + Length(y) */ void theory_str::instantiate_concat_axiom(enode * cat) { app * a_cat = cat->get_owner(); SASSERT(u.str.is_concat(a_cat)); ast_manager & m = get_manager(); TRACE("str", tout << "instantiating concat axiom for " << mk_ismt2_pp(a_cat, m) << std::endl;); // build LHS expr_ref len_xy(m); len_xy = mk_strlen(a_cat); SASSERT(len_xy); // build RHS: start by extracting x and y from Concat(x, y) SASSERT(a_cat->get_num_args() == 2); app * a_x = to_app(a_cat->get_arg(0)); app * a_y = to_app(a_cat->get_arg(1)); expr_ref len_x(m); len_x = mk_strlen(a_x); SASSERT(len_x); expr_ref len_y(m); len_y = mk_strlen(a_y); SASSERT(len_y); // now build len_x + len_y expr_ref len_x_plus_len_y(m); len_x_plus_len_y = m_autil.mk_add(len_x, len_y); SASSERT(len_x_plus_len_y); // finally assert equality between the two subexpressions app * eq = m.mk_eq(len_xy, len_x_plus_len_y); SASSERT(eq); assert_axiom(eq); } /* * Add axioms that are true for any string variable: * 1. Length(x) >= 0 * 2. Length(x) == 0 <=> x == "" * If the term is a string constant, we can assert something stronger: * Length(x) == strlen(x) */ void theory_str::instantiate_basic_string_axioms(enode * str) { context & ctx = get_context(); ast_manager & m = get_manager(); TRACE("str", tout << "set up basic string axioms on " << mk_pp(str->get_owner(), m) << std::endl;); { sort * a_sort = m.get_sort(str->get_owner()); sort * str_sort = u.str.mk_string_sort(); if (a_sort != str_sort) { TRACE("str", tout << "WARNING: not setting up string axioms on non-string term " << mk_pp(str->get_owner(), m) << std::endl;); return; } } // TESTING: attempt to avoid a crash here when a variable goes out of scope if (str->get_iscope_lvl() > ctx.get_scope_level()) { TRACE("str", tout << "WARNING: skipping axiom setup on out-of-scope string term" << std::endl;); return; } // generate a stronger axiom for constant strings app * a_str = str->get_owner(); if (u.str.is_string(a_str)) { expr_ref len_str(m); len_str = mk_strlen(a_str); SASSERT(len_str); zstring strconst; u.str.is_string(str->get_owner(), strconst); TRACE("str", tout << "instantiating constant string axioms for \"" << strconst.encode().c_str() << "\"" << std::endl;); unsigned int l = strconst.length(); expr_ref len(m_autil.mk_numeral(rational(l), true), m); literal lit(mk_eq(len_str, len, false)); ctx.mark_as_relevant(lit); if (m.has_trace_stream()) log_axiom_instantiation(ctx.bool_var2expr(lit.var())); ctx.mk_th_axiom(get_id(), 1, &lit); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; } else { // build axiom 1: Length(a_str) >= 0 { // build LHS expr_ref len_str(m); len_str = mk_strlen(a_str); SASSERT(len_str); // build RHS expr_ref zero(m); zero = m_autil.mk_numeral(rational(0), true); SASSERT(zero); // build LHS >= RHS and assert app * lhs_ge_rhs = m_autil.mk_ge(len_str, zero); SASSERT(lhs_ge_rhs); TRACE("str", tout << "string axiom 1: " << mk_ismt2_pp(lhs_ge_rhs, m) << std::endl;); assert_axiom(lhs_ge_rhs); } // build axiom 2: Length(a_str) == 0 <=> a_str == "" { // build LHS of iff expr_ref len_str(m); len_str = mk_strlen(a_str); SASSERT(len_str); expr_ref zero(m); zero = m_autil.mk_numeral(rational(0), true); SASSERT(zero); expr_ref lhs(m); lhs = ctx.mk_eq_atom(len_str, zero); SASSERT(lhs); // build RHS of iff expr_ref empty_str(m); empty_str = mk_string(""); SASSERT(empty_str); expr_ref rhs(m); rhs = ctx.mk_eq_atom(a_str, empty_str); SASSERT(rhs); // build LHS <=> RHS and assert TRACE("str", tout << "string axiom 2: " << mk_ismt2_pp(lhs, m) << " <=> " << mk_ismt2_pp(rhs, m) << std::endl;); literal l(mk_eq(lhs, rhs, true)); ctx.mark_as_relevant(l); if (m.has_trace_stream()) log_axiom_instantiation(ctx.bool_var2expr(l.var())); ctx.mk_th_axiom(get_id(), 1, &l); if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; } } } /* * Add an axiom of the form: * (lhs == rhs) -> ( Length(lhs) == Length(rhs) ) */ void theory_str::instantiate_str_eq_length_axiom(enode * lhs, enode * rhs) { context & ctx = get_context(); ast_manager & m = get_manager(); app * a_lhs = lhs->get_owner(); app * a_rhs = rhs->get_owner(); // build premise: (lhs == rhs) expr_ref premise(ctx.mk_eq_atom(a_lhs, a_rhs), m); // build conclusion: ( Length(lhs) == Length(rhs) ) expr_ref len_lhs(mk_strlen(a_lhs), m); SASSERT(len_lhs); expr_ref len_rhs(mk_strlen(a_rhs), m); SASSERT(len_rhs); expr_ref conclusion(ctx.mk_eq_atom(len_lhs, len_rhs), m); TRACE("str", tout << "string-eq length-eq axiom: " << mk_ismt2_pp(premise, m) << " -> " << mk_ismt2_pp(conclusion, m) << std::endl;); assert_implication(premise, conclusion); } void theory_str::instantiate_axiom_CharAt(enode * e) { context & ctx = get_context(); ast_manager & m = get_manager(); expr* arg0, *arg1; app * expr = e->get_owner(); if (axiomatized_terms.contains(expr)) { TRACE("str", tout << "already set up CharAt axiom for " << mk_pp(expr, m) << std::endl;); return; } axiomatized_terms.insert(expr); VERIFY(u.str.is_at(expr, arg0, arg1)); TRACE("str", tout << "instantiate CharAt axiom for " << mk_pp(expr, m) << std::endl;); expr_ref ts0(mk_str_var("ts0"), m); expr_ref ts1(mk_str_var("ts1"), m); expr_ref ts2(mk_str_var("ts2"), m); expr_ref cond(m.mk_and( m_autil.mk_ge(arg1, mk_int(0)), m_autil.mk_lt(arg1, mk_strlen(arg0))), m); expr_ref_vector and_item(m); and_item.push_back(ctx.mk_eq_atom(arg0, mk_concat(ts0, mk_concat(ts1, ts2)))); and_item.push_back(ctx.mk_eq_atom(arg1, mk_strlen(ts0))); and_item.push_back(ctx.mk_eq_atom(mk_strlen(ts1), mk_int(1))); expr_ref thenBranch(::mk_and(and_item)); expr_ref elseBranch(ctx.mk_eq_atom(ts1, mk_string("")), m); expr_ref axiom(m.mk_ite(cond, thenBranch, elseBranch), m); expr_ref reductionVar(ctx.mk_eq_atom(expr, ts1), m); expr_ref finalAxiom(m.mk_and(axiom, reductionVar), m); get_context().get_rewriter()(finalAxiom); assert_axiom(finalAxiom); } void theory_str::instantiate_axiom_prefixof(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 prefixof axiom for " << mk_pp(expr, m) << std::endl;); return; } axiomatized_terms.insert(expr); TRACE("str", tout << "instantiate prefixof axiom for " << mk_pp(expr, m) << std::endl;); expr_ref ts0(mk_str_var("ts0"), m); expr_ref ts1(mk_str_var("ts1"), m); expr_ref_vector innerItems(m); innerItems.push_back(ctx.mk_eq_atom(expr->get_arg(1), mk_concat(ts0, ts1))); innerItems.push_back(ctx.mk_eq_atom(mk_strlen(ts0), mk_strlen(expr->get_arg(0)))); innerItems.push_back(m.mk_ite(ctx.mk_eq_atom(ts0, expr->get_arg(0)), expr, mk_not(m, expr))); expr_ref then1(m.mk_and(innerItems.size(), innerItems.c_ptr()), m); SASSERT(then1); // the top-level condition is Length(arg0) >= Length(arg1) expr_ref topLevelCond( m_autil.mk_ge( m_autil.mk_add( mk_strlen(expr->get_arg(1)), m_autil.mk_mul(mk_int(-1), mk_strlen(expr->get_arg(0)))), mk_int(0)) , m); SASSERT(topLevelCond); expr_ref finalAxiom(m.mk_ite(topLevelCond, then1, mk_not(m, expr)), m); SASSERT(finalAxiom); assert_axiom(finalAxiom); } void theory_str::instantiate_axiom_suffixof(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 suffixof axiom for " << mk_pp(expr, m) << std::endl;); return; } axiomatized_terms.insert(expr); TRACE("str", tout << "instantiate suffixof axiom for " << mk_pp(expr, m) << std::endl;); expr_ref ts0(mk_str_var("ts0"), m); expr_ref ts1(mk_str_var("ts1"), m); expr_ref_vector innerItems(m); innerItems.push_back(ctx.mk_eq_atom(expr->get_arg(1), mk_concat(ts0, ts1))); innerItems.push_back(ctx.mk_eq_atom(mk_strlen(ts1), mk_strlen(expr->get_arg(0)))); innerItems.push_back(m.mk_ite(ctx.mk_eq_atom(ts1, expr->get_arg(0)), expr, mk_not(m, expr))); expr_ref then1(m.mk_and(innerItems.size(), innerItems.c_ptr()), m); SASSERT(then1); // the top-level condition is Length(arg0) >= Length(arg1) expr_ref topLevelCond( m_autil.mk_ge( m_autil.mk_add( mk_strlen(expr->get_arg(1)), m_autil.mk_mul(mk_int(-1), mk_strlen(expr->get_arg(0)))), mk_int(0)) , m); SASSERT(topLevelCond); expr_ref finalAxiom(m.mk_ite(topLevelCond, then1, mk_not(m, expr)), m); SASSERT(finalAxiom); assert_axiom(finalAxiom); } void theory_str::instantiate_axiom_Contains(enode * e) { context & ctx = get_context(); ast_manager & m = get_manager(); app * ex = e->get_owner(); if (axiomatized_terms.contains(ex)) { TRACE("str", tout << "already set up Contains axiom for " << mk_pp(ex, m) << std::endl;); return; } axiomatized_terms.insert(ex); // quick path, because this is necessary due to rewriter behaviour // at minimum it should fix z3str/concat-006.smt2 zstring haystackStr, needleStr; if (u.str.is_string(ex->get_arg(0), haystackStr) && u.str.is_string(ex->get_arg(1), needleStr)) { TRACE("str", tout << "eval constant Contains term " << mk_pp(ex, m) << std::endl;); if (haystackStr.contains(needleStr)) { assert_axiom(ex); } else { assert_axiom(mk_not(m, ex)); } return; } { // register Contains() expr * str = ex->get_arg(0); expr * substr = ex->get_arg(1); contains_map.push_back(ex); std::pair key = std::pair(str, substr); contain_pair_bool_map.insert(str, substr, ex); if (!contain_pair_idx_map.contains(str)) { contain_pair_idx_map.insert(str, std::set>()); } if (!contain_pair_idx_map.contains(substr)) { contain_pair_idx_map.insert(substr, std::set>()); } contain_pair_idx_map[str].insert(key); contain_pair_idx_map[substr].insert(key); } TRACE("str", tout << "instantiate Contains axiom for " << mk_pp(ex, m) << std::endl;); expr_ref ts0(mk_str_var("ts0"), m); expr_ref ts1(mk_str_var("ts1"), m); expr_ref breakdownAssert(ctx.mk_eq_atom(ex, ctx.mk_eq_atom(ex->get_arg(0), mk_concat(ts0, mk_concat(ex->get_arg(1), ts1)))), m); SASSERT(breakdownAssert); assert_axiom(breakdownAssert); } void theory_str::instantiate_axiom_Indexof(enode * e) { context & ctx = get_context(); th_rewriter & rw = ctx.get_rewriter(); ast_manager & m = get_manager(); app * ex = e->get_owner(); if (axiomatized_terms.contains(ex)) { TRACE("str", tout << "already set up str.indexof axiom for " << mk_pp(ex, m) << std::endl;); return; } SASSERT(ex->get_num_args() == 3); { // Attempt to rewrite to an integer constant. If this succeeds, // assert equality with that constant. // The rewriter normally takes care of this for terms that are in scope // at the beginning of the search. // We perform the check here to catch terms that are added during the search. expr_ref rwex(ex, m); rw(rwex); if (m_autil.is_numeral(rwex)) { TRACE("str", tout << "constant expression " << mk_pp(ex, m) << " simplifies to " << mk_pp(rwex, m) << std::endl;); assert_axiom(ctx.mk_eq_atom(ex, rwex)); axiomatized_terms.insert(ex); return; } } // if the third argument is exactly the integer 0, we can use this "simple" indexof; // otherwise, we call the "extended" version expr * startingPosition = ex->get_arg(2); rational startingInteger; if (!m_autil.is_numeral(startingPosition, startingInteger) || !startingInteger.is_zero()) { // "extended" indexof term with prefix instantiate_axiom_Indexof_extended(e); return; } axiomatized_terms.insert(ex); expr * exHaystack = nullptr; expr * exNeedle = nullptr; expr * exIndex = nullptr; u.str.is_index(ex, exHaystack, exNeedle, exIndex); TRACE("str", tout << "instantiate str.indexof 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 indexAst(mk_int_var("index"), m); expr_ref condAst1(mk_contains(ex->get_arg(0), ex->get_arg(1)), m); expr_ref condAst2(m.mk_not(ctx.mk_eq_atom(exNeedle, mk_string(""))), m); expr_ref condAst(m.mk_and(condAst1, condAst2), m); //expr_ref condAst(mk_contains(ex->get_arg(0), ex->get_arg(1)), m); SASSERT(condAst); // ----------------------- // true branch expr_ref_vector thenItems(m); // args[0] = x1 . args[1] . x2 thenItems.push_back(ctx.mk_eq_atom(ex->get_arg(0), mk_concat(x1, mk_concat(ex->get_arg(1), x2)))); // indexAst = |x1| thenItems.push_back(ctx.mk_eq_atom(indexAst, 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(indexAst, mk_strlen(ex->get_arg(1)), mk_int(-1)), m); SASSERT(tmpLen); 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, ex->get_arg(1)))); expr_ref thenBranch(m.mk_and(thenItems.size(), thenItems.c_ptr()), m); SASSERT(thenBranch); // ----------------------- // false branch expr_ref elseBranch(m.mk_ite( ctx.mk_eq_atom(exNeedle, mk_string("")), ctx.mk_eq_atom(indexAst, mk_int(0)), ctx.mk_eq_atom(indexAst, mk_int(-1)) ), m); SASSERT(elseBranch); expr_ref breakdownAssert(m.mk_ite(condAst, thenBranch, elseBranch), m); SASSERT(breakdownAssert); expr_ref reduceToIndex(ctx.mk_eq_atom(ex, indexAst), m); SASSERT(reduceToIndex); expr_ref finalAxiom(m.mk_and(breakdownAssert, reduceToIndex), m); rw(finalAxiom); assert_axiom(finalAxiom); { // heuristic: integrate with str.contains information // (but don't introduce it if it isn't already in the instance) expr_ref haystack(ex->get_arg(0), m), needle(ex->get_arg(1), m), startIdx(ex->get_arg(2), m); expr_ref zeroAst(mk_int(0), m); // (H contains N) <==> (H indexof N, 0) >= 0 expr_ref premise(u.str.mk_contains(haystack, needle), m); ctx.internalize(premise, false); expr_ref conclusion(m_autil.mk_ge(ex, zeroAst), m); expr_ref containsAxiom(ctx.mk_eq_atom(premise, conclusion), m); SASSERT(containsAxiom); // we can't assert this during init_search as it breaks an invariant if the instance becomes inconsistent //m_delayed_axiom_setup_terms.push_back(containsAxiom); } } void theory_str::instantiate_axiom_Indexof_extended(enode * _e) { context & ctx = get_context(); th_rewriter & rw = ctx.get_rewriter(); ast_manager & m = get_manager(); app * e = _e->get_owner(); if (axiomatized_terms.contains(e)) { TRACE("str", tout << "already set up extended str.indexof axiom for " << mk_pp(e, m) << std::endl;); return; } SASSERT(e->get_num_args() == 3); axiomatized_terms.insert(e); TRACE("str", tout << "instantiate extended str.indexof axiom for " << mk_pp(e, m) << std::endl;); // str.indexof(H, N, i): // i < 0 --> -1 // i == 0 --> str.indexof(H, N, 0) // i >= len(H) --> -1 // 0 < i < len(H) --> // H = hd ++ tl // len(hd) = i // i + str.indexof(tl, N, 0) expr * H = nullptr; // "haystack" expr * N = nullptr; // "needle" expr * i = nullptr; // start index u.str.is_index(e, H, N, i); expr_ref minus_one(m_autil.mk_numeral(rational::minus_one(), true), m); expr_ref zero(m_autil.mk_numeral(rational::zero(), true), m); expr_ref empty_string(mk_string(""), m); // case split // case 1: i < 0 { expr_ref premise(m_autil.mk_le(i, minus_one), m); expr_ref conclusion(ctx.mk_eq_atom(e, minus_one), m); assert_implication(premise, conclusion); } // case 1.1: N == "" and i out of range { expr_ref premiseNEmpty(ctx.mk_eq_atom(N, empty_string), m); // range check expr_ref premiseRangeLower(m_autil.mk_le(i, minus_one), m); expr_ref premiseRangeUpper(m_autil.mk_ge(m_autil.mk_add(i, m_autil.mk_mul(minus_one, mk_strlen(H))), zero), m); expr_ref premiseRange(m.mk_or(premiseRangeLower, premiseRangeUpper), m); expr_ref premise(m.mk_and(premiseNEmpty, premiseRange), m); expr_ref conclusion(ctx.mk_eq_atom(e, minus_one), m); expr_ref finalAxiom(rewrite_implication(premise, conclusion), m); rw(finalAxiom); assert_axiom(finalAxiom); } // case 1.2: N == "" and i within range { expr_ref premiseNEmpty(ctx.mk_eq_atom(N, empty_string), m); // range check expr_ref premiseRangeLower(m_autil.mk_le(i, minus_one), m); expr_ref premiseRangeUpper(m_autil.mk_ge(m_autil.mk_add(i, m_autil.mk_mul(minus_one, mk_strlen(H))), zero), m); expr_ref premiseRange(m.mk_not(m.mk_or(premiseRangeLower, premiseRangeUpper)), m); expr_ref premise(m.mk_and(premiseNEmpty, premiseRange), m); expr_ref conclusion(ctx.mk_eq_atom(e, i), m); expr_ref finalAxiom(rewrite_implication(premise, conclusion), m); rw(finalAxiom); assert_axiom(finalAxiom); } // case 2: i = 0 { expr_ref premise1(ctx.mk_eq_atom(i, zero), m); expr_ref premise2(m.mk_not(ctx.mk_eq_atom(N, empty_string)), m); expr_ref premise(m.mk_and(premise1, premise2), m); rw(premise); // reduction to simpler case expr_ref conclusion(ctx.mk_eq_atom(e, mk_indexof(H, N)), m); assert_implication(premise, conclusion); } // case 3: i >= len(H) { //expr_ref _premise(m_autil.mk_ge(i, mk_strlen(H)), m); //expr_ref premise(_premise); //th_rewriter rw(m); //rw(premise); expr_ref premise1(m_autil.mk_ge(m_autil.mk_add(i, m_autil.mk_mul(minus_one, mk_strlen(H))), zero), m); expr_ref premise2(m.mk_not(ctx.mk_eq_atom(N, empty_string)), m); expr_ref premise(m.mk_and(premise1, premise2), m); rw(premise); expr_ref conclusion(ctx.mk_eq_atom(e, minus_one), m); assert_implication(premise, conclusion); } // case 3.5: H doesn't contain N { expr_ref premise(m.mk_not(u.str.mk_contains(H, N)), m); expr_ref conclusion(ctx.mk_eq_atom(e, minus_one), m); rw(premise); assert_implication(premise, conclusion); } // case 4: 0 < i < len(H), N non-empty, and H contains N { expr_ref premise1(m_autil.mk_gt(i, zero), m); //expr_ref premise2(m_autil.mk_lt(i, mk_strlen(H)), m); expr_ref premise2(m.mk_not(m_autil.mk_ge(m_autil.mk_add(i, m_autil.mk_mul(minus_one, mk_strlen(H))), zero)), m); expr_ref premise3(u.str.mk_contains(H, N), m); expr_ref premise4(m.mk_not(ctx.mk_eq_atom(N, mk_string(""))), m); expr_ref_vector premises(m); premises.push_back(premise1); premises.push_back(premise2); premises.push_back(premise3); premises.push_back(premise4); expr_ref _premise(mk_and(premises), m); expr_ref premise(_premise); rw(premise); expr_ref hd(mk_str_var("hd"), m); expr_ref tl(mk_str_var("tl"), m); expr_ref_vector conclusion_terms(m); conclusion_terms.push_back(ctx.mk_eq_atom(H, mk_concat(hd, tl))); conclusion_terms.push_back(ctx.mk_eq_atom(mk_strlen(hd), i)); conclusion_terms.push_back(u.str.mk_contains(tl, N)); conclusion_terms.push_back(ctx.mk_eq_atom(e, m_autil.mk_add(i, mk_indexof(tl, N)))); expr_ref conclusion(mk_and(conclusion_terms), m); assert_implication(premise, conclusion); } { // heuristic: integrate with str.contains information // (but don't introduce it if it isn't already in the instance) // (0 <= i < len(H)) ==> (H contains N) <==> (H indexof N, i) >= 0 expr_ref precondition1(m_autil.mk_gt(i, minus_one), m); //expr_ref precondition2(m_autil.mk_lt(i, mk_strlen(H)), m); expr_ref precondition2(m.mk_not(m_autil.mk_ge(m_autil.mk_add(i, m_autil.mk_mul(minus_one, mk_strlen(H))), zero)), m); expr_ref precondition3(m.mk_not(ctx.mk_eq_atom(N, mk_string(""))), m); expr_ref precondition(m.mk_and(precondition1, precondition2, precondition3), m); rw(precondition); expr_ref premise(u.str.mk_contains(H, N), m); ctx.internalize(premise, false); expr_ref conclusion(m_autil.mk_ge(e, zero), m); expr_ref containsAxiom(ctx.mk_eq_atom(premise, conclusion), m); expr_ref finalAxiom(rewrite_implication(precondition, containsAxiom), m); SASSERT(finalAxiom); // we can't assert this during init_search as it breaks an invariant if the instance becomes inconsistent m_delayed_assertions_todo.push_back(finalAxiom); } } void theory_str::instantiate_axiom_LastIndexof(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 LastIndexof axiom for " << mk_pp(expr, m) << std::endl;); return; } axiomatized_terms.insert(expr); TRACE("str", tout << "instantiate LastIndexof axiom for " << mk_pp(expr, m) << std::endl;); expr_ref x1(mk_str_var("x1"), m); expr_ref x2(mk_str_var("x2"), m); expr_ref indexAst(mk_int_var("index"), m); expr_ref_vector items(m); // args[0] = x1 . args[1] . x2 expr_ref eq1(ctx.mk_eq_atom(expr->get_arg(0), mk_concat(x1, mk_concat(expr->get_arg(1), x2))), m); expr_ref arg0HasArg1(mk_contains(expr->get_arg(0), expr->get_arg(1)), m); // arg0HasArg1 = Contains(args[0], args[1]) items.push_back(ctx.mk_eq_atom(arg0HasArg1, eq1)); expr_ref condAst(arg0HasArg1, m); //---------------------------- // true branch expr_ref_vector thenItems(m); thenItems.push_back(m_autil.mk_ge(indexAst, mk_int(0))); // args[0] = x1 . args[1] . x2 // x1 doesn't contain args[1] thenItems.push_back(mk_not(m, mk_contains(x2, expr->get_arg(1)))); thenItems.push_back(ctx.mk_eq_atom(indexAst, mk_strlen(x1))); bool canSkip = false; zstring arg1Str; if (u.str.is_string(expr->get_arg(1), arg1Str)) { if (arg1Str.length() == 1) { canSkip = true; } } if (!canSkip) { // args[0] = x3 . x4 /\ |x3| = |x1| + 1 /\ ! contains(x4, 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(indexAst, mk_int(1)), m); thenItems.push_back(ctx.mk_eq_atom(expr->get_arg(0), mk_concat(x3, x4))); thenItems.push_back(ctx.mk_eq_atom(mk_strlen(x3), tmpLen)); thenItems.push_back(mk_not(m, mk_contains(x4, expr->get_arg(1)))); } //---------------------------- // else branch expr_ref_vector elseItems(m); elseItems.push_back(ctx.mk_eq_atom(indexAst, mk_int(-1))); items.push_back(m.mk_ite(condAst, m.mk_and(thenItems.size(), thenItems.c_ptr()), m.mk_and(elseItems.size(), elseItems.c_ptr()))); expr_ref breakdownAssert(m.mk_and(items.size(), items.c_ptr()), m); SASSERT(breakdownAssert); expr_ref reduceToIndex(ctx.mk_eq_atom(expr, indexAst), m); SASSERT(reduceToIndex); expr_ref finalAxiom(m.mk_and(breakdownAssert, reduceToIndex), m); SASSERT(finalAxiom); assert_axiom(finalAxiom); } void theory_str::instantiate_axiom_Substr(enode * e) { context & ctx = get_context(); ast_manager & m = get_manager(); expr* substrBase = nullptr; expr* substrPos = nullptr; expr* substrLen = nullptr; app * expr = e->get_owner(); if (axiomatized_terms.contains(expr)) { TRACE("str", tout << "already set up Substr axiom for " << mk_pp(expr, m) << std::endl;); return; } axiomatized_terms.insert(expr); TRACE("str", tout << "instantiate Substr axiom for " << mk_pp(expr, m) << std::endl;); VERIFY(u.str.is_extract(expr, substrBase, substrPos, substrLen)); expr_ref zero(m_autil.mk_numeral(rational::zero(), true), m); expr_ref minusOne(m_autil.mk_numeral(rational::minus_one(), true), m); SASSERT(zero); SASSERT(minusOne); expr_ref_vector argumentsValid_terms(m); // pos >= 0 argumentsValid_terms.push_back(m_autil.mk_ge(substrPos, zero)); // pos < strlen(base) // --> pos + -1*strlen(base) < 0 argumentsValid_terms.push_back(mk_not(m, m_autil.mk_ge( m_autil.mk_add(substrPos, m_autil.mk_mul(minusOne, mk_strlen(substrBase))), zero))); // len >= 0 argumentsValid_terms.push_back(m_autil.mk_ge(substrLen, zero)); // (pos+len) >= strlen(base) // --> pos + len + -1*strlen(base) >= 0 expr_ref lenOutOfBounds(m_autil.mk_ge( m_autil.mk_add(substrPos, substrLen, m_autil.mk_mul(minusOne, mk_strlen(substrBase))), zero), m); expr_ref argumentsValid = mk_and(argumentsValid_terms); // Case 1: pos < 0 or pos >= strlen(base) or len < 0 // ==> (Substr ...) = "" expr_ref case1_premise(m.mk_not(argumentsValid), m); expr_ref case1_conclusion(ctx.mk_eq_atom(expr, mk_string("")), m); expr_ref case1(m.mk_implies(case1_premise, case1_conclusion), m); // Case 2: (pos >= 0 and pos < strlen(base) and len >= 0) and (pos+len) >= strlen(base) // ==> base = t0.t1 AND len(t0) = pos AND (Substr ...) = t1 expr_ref t0(mk_str_var("t0"), m); expr_ref t1(mk_str_var("t1"), m); expr_ref case2_conclusion(m.mk_and( ctx.mk_eq_atom(substrBase, mk_concat(t0,t1)), ctx.mk_eq_atom(mk_strlen(t0), substrPos), ctx.mk_eq_atom(expr, t1)), m); expr_ref case2(m.mk_implies(m.mk_and(argumentsValid, lenOutOfBounds), case2_conclusion), m); // Case 3: (pos >= 0 and pos < strlen(base) and len >= 0) and (pos+len) < strlen(base) // ==> base = t2.t3.t4 AND len(t2) = pos AND len(t3) = len AND (Substr ...) = t3 expr_ref t2(mk_str_var("t2"), m); expr_ref t3(mk_str_var("t3"), m); expr_ref t4(mk_str_var("t4"), m); expr_ref_vector case3_conclusion_terms(m); case3_conclusion_terms.push_back(ctx.mk_eq_atom(substrBase, mk_concat(t2, mk_concat(t3, t4)))); case3_conclusion_terms.push_back(ctx.mk_eq_atom(mk_strlen(t2), substrPos)); case3_conclusion_terms.push_back(ctx.mk_eq_atom(mk_strlen(t3), substrLen)); case3_conclusion_terms.push_back(ctx.mk_eq_atom(expr, t3)); expr_ref case3_conclusion(mk_and(case3_conclusion_terms), m); expr_ref case3(m.mk_implies(m.mk_and(argumentsValid, m.mk_not(lenOutOfBounds)), case3_conclusion), m); { th_rewriter rw(m); expr_ref case1_rw(case1, m); rw(case1_rw); assert_axiom(case1_rw); expr_ref case2_rw(case2, m); rw(case2_rw); assert_axiom(case2_rw); expr_ref case3_rw(case3, m); rw(case3_rw); assert_axiom(case3_rw); } } // (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 * 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(ex); 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(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(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(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, 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, ex->get_arg(0)), m); th_rewriter rw(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(ex, result), m); expr_ref reduceToResult_rw(reduceToResult, m); rw(reduceToResult_rw); assert_axiom(reduceToResult_rw); } void theory_str::instantiate_axiom_str_to_int(enode * e) { context & ctx = get_context(); ast_manager & m = get_manager(); app * ex = e->get_owner(); if (axiomatized_terms.contains(ex)) { TRACE("str", tout << "already set up str.to-int axiom for " << mk_pp(ex, m) << std::endl;); return; } axiomatized_terms.insert(ex); TRACE("str", tout << "instantiate str.to-int axiom for " << mk_pp(ex, m) << std::endl;); // let expr = (str.to-int S) // axiom 1: expr >= -1 // axiom 2: expr = 0 <==> S = "0" // axiom 3: expr >= 1 ==> len(S) > 0 AND S[0] != "0" expr * S = ex->get_arg(0); { expr_ref axiom1(m_autil.mk_ge(ex, m_autil.mk_numeral(rational::minus_one(), true)), m); SASSERT(axiom1); assert_axiom(axiom1); } { expr_ref lhs(ctx.mk_eq_atom(ex, m_autil.mk_numeral(rational::zero(), true)), m); expr_ref rhs(ctx.mk_eq_atom(S, mk_string("0")), m); expr_ref axiom2(ctx.mk_eq_atom(lhs, rhs), m); SASSERT(axiom2); assert_axiom(axiom2); } { expr_ref premise(m_autil.mk_ge(ex, m_autil.mk_numeral(rational::one(), true)), m); // S >= 1 --> S in [1-9][0-9]* expr_ref re_positiveInteger(u.re.mk_concat( u.re.mk_range(mk_string("1"), mk_string("9")), u.re.mk_star(u.re.mk_range(mk_string("0"), mk_string("9")))), m); expr_ref conclusion(mk_RegexIn(S, re_positiveInteger), m); SASSERT(premise); SASSERT(conclusion); assert_implication(premise, conclusion); } } void theory_str::instantiate_axiom_int_to_str(enode * e) { context & ctx = get_context(); ast_manager & m = get_manager(); app * ex = e->get_owner(); if (axiomatized_terms.contains(ex)) { TRACE("str", tout << "already set up str.from-int axiom for " << mk_pp(ex, m) << std::endl;); return; } axiomatized_terms.insert(ex); TRACE("str", tout << "instantiate str.from-int axiom for " << mk_pp(ex, m) << std::endl;); // axiom 1: N < 0 <==> (str.from-int N) = "" expr * N = ex->get_arg(0); { expr_ref axiom1_lhs(mk_not(m, m_autil.mk_ge(N, m_autil.mk_numeral(rational::zero(), true))), m); expr_ref axiom1_rhs(ctx.mk_eq_atom(ex, mk_string("")), m); expr_ref axiom1(ctx.mk_eq_atom(axiom1_lhs, axiom1_rhs), m); SASSERT(axiom1); assert_axiom(axiom1); } } expr * theory_str::mk_RegexIn(expr * str, expr * regexp) { app * regexIn = u.re.mk_in_re(str, regexp); // immediately force internalization so that axiom setup does not fail get_context().internalize(regexIn, false); set_up_axioms(regexIn); return regexIn; } static zstring str2RegexStr(zstring str) { zstring res(""); int len = str.length(); for (int i = 0; i < len; i++) { char nc = str[i]; // 12 special chars if (nc == '\\' || nc == '^' || nc == '$' || nc == '.' || nc == '|' || nc == '?' || nc == '*' || nc == '+' || nc == '(' || nc == ')' || nc == '[' || nc == '{') { res = res + zstring("\\"); } char tmp[2] = {(char)str[i], '\0'}; res = res + zstring(tmp); } return res; } zstring theory_str::get_std_regex_str(expr * regex) { app * a_regex = to_app(regex); if (u.re.is_to_re(a_regex)) { expr * regAst = a_regex->get_arg(0); zstring regAstVal; u.str.is_string(regAst, regAstVal); zstring regStr = str2RegexStr(regAstVal); return regStr; } else if (u.re.is_concat(a_regex)) { expr * reg1Ast = a_regex->get_arg(0); expr * reg2Ast = a_regex->get_arg(1); zstring reg1Str = get_std_regex_str(reg1Ast); zstring reg2Str = get_std_regex_str(reg2Ast); return zstring("(") + reg1Str + zstring(")(") + reg2Str + zstring(")"); } else if (u.re.is_union(a_regex)) { expr * reg1Ast = a_regex->get_arg(0); expr * reg2Ast = a_regex->get_arg(1); zstring reg1Str = get_std_regex_str(reg1Ast); zstring reg2Str = get_std_regex_str(reg2Ast); return zstring("(") + reg1Str + zstring(")|(") + reg2Str + zstring(")"); } else if (u.re.is_star(a_regex)) { expr * reg1Ast = a_regex->get_arg(0); zstring reg1Str = get_std_regex_str(reg1Ast); return zstring("(") + reg1Str + zstring(")*"); } else if (u.re.is_range(a_regex)) { expr * range1 = a_regex->get_arg(0); expr * range2 = a_regex->get_arg(1); zstring range1val, range2val; u.str.is_string(range1, range1val); u.str.is_string(range2, range2val); return zstring("[") + range1val + zstring("-") + range2val + zstring("]"); } else if (u.re.is_loop(a_regex)) { expr * body; unsigned lo, hi; // There are two variants of loop: a 2-argument version and a 3-argument version. if (u.re.is_loop(a_regex, body, lo, hi)) { rational rLo(lo); rational rHi(hi); zstring bodyStr = get_std_regex_str(body); return zstring("(") + bodyStr + zstring("{") + zstring(rLo.to_string().c_str()) + zstring(",") + zstring(rHi.to_string().c_str()) + zstring("})"); } else if (u.re.is_loop(a_regex, body, lo)) { rational rLo(lo); zstring bodyStr = get_std_regex_str(body); return zstring("(") + bodyStr + zstring("{") + zstring(rLo.to_string().c_str()) + zstring("+") + zstring("})"); } else { TRACE("str", tout << "BUG: unrecognized regex term " << mk_pp(regex, get_manager()) << std::endl;); UNREACHABLE(); return zstring(""); } } else if (u.re.is_full_seq(a_regex)) { return zstring("(.*)"); } else if (u.re.is_full_char(a_regex)) { return zstring("str.allchar"); } else { TRACE("str", tout << "BUG: unrecognized regex term " << mk_pp(regex, get_manager()) << std::endl;); UNREACHABLE(); return zstring(""); } } void theory_str::instantiate_axiom_RegexIn(enode * e) { context & ctx = get_context(); ast_manager & m = get_manager(); app * ex = e->get_owner(); if (axiomatized_terms.contains(ex)) { TRACE("str", tout << "already set up RegexIn axiom for " << mk_pp(ex, m) << std::endl;); return; } axiomatized_terms.insert(ex); TRACE("str", tout << "instantiate RegexIn axiom for " << mk_pp(ex, m) << std::endl;); { zstring regexStr = get_std_regex_str(ex->get_arg(1)); std::pair key1(ex->get_arg(0), regexStr); // skip Z3str's map check, because we already check if we set up axioms on this term regex_in_bool_map[key1] = ex; if (!regex_in_var_reg_str_map.contains(ex->get_arg(0))) { regex_in_var_reg_str_map.insert(ex->get_arg(0), std::set()); } regex_in_var_reg_str_map[ex->get_arg(0)].insert(regexStr); } expr_ref str(ex->get_arg(0), m); app * regex = to_app(ex->get_arg(1)); if (m_params.m_RegexAutomata) { regex_terms.insert(ex); if (!regex_terms_by_string.contains(str)) { regex_terms_by_string.insert(str, ptr_vector()); } regex_terms_by_string[str].push_back(ex); // stop setting up axioms here, we do this differently return; } // quick reference for the following code: // - ex: top-level regex membership term // - str: string term appearing in ex // - regex: regex term appearing in ex // ex ::= (str.in.re str regex) if (u.re.is_to_re(regex)) { expr_ref rxStr(regex->get_arg(0), m); // want to assert 'expr IFF (str == rxStr)' expr_ref rhs(ctx.mk_eq_atom(str, rxStr), m); expr_ref finalAxiom(m.mk_iff(ex, rhs), m); SASSERT(finalAxiom); assert_axiom(finalAxiom); TRACE("str", tout << "set up Str2Reg: (RegexIn " << mk_pp(str, m) << " " << mk_pp(regex, m) << ")" << std::endl;); } else if (u.re.is_concat(regex)) { expr_ref var1(mk_regex_rep_var(), m); expr_ref var2(mk_regex_rep_var(), m); expr_ref rhs(mk_concat(var1, var2), m); expr_ref rx1(regex->get_arg(0), m); expr_ref rx2(regex->get_arg(1), m); expr_ref var1InRegex1(mk_RegexIn(var1, rx1), m); expr_ref var2InRegex2(mk_RegexIn(var2, rx2), m); expr_ref_vector items(m); items.push_back(var1InRegex1); items.push_back(var2InRegex2); items.push_back(ctx.mk_eq_atom(ex, ctx.mk_eq_atom(str, rhs))); expr_ref finalAxiom(mk_and(items), m); SASSERT(finalAxiom); assert_axiom(finalAxiom); } else if (u.re.is_union(regex)) { expr_ref var1(mk_regex_rep_var(), m); expr_ref var2(mk_regex_rep_var(), m); expr_ref orVar(m.mk_or(ctx.mk_eq_atom(str, var1), ctx.mk_eq_atom(str, var2)), m); expr_ref regex1(regex->get_arg(0), m); expr_ref regex2(regex->get_arg(1), m); expr_ref var1InRegex1(mk_RegexIn(var1, regex1), m); expr_ref var2InRegex2(mk_RegexIn(var2, regex2), m); expr_ref_vector items(m); items.push_back(var1InRegex1); items.push_back(var2InRegex2); items.push_back(ctx.mk_eq_atom(ex, orVar)); assert_axiom(mk_and(items)); } else if (u.re.is_star(regex)) { // slightly more complex due to the unrolling step. expr_ref regex1(regex->get_arg(0), m); expr_ref unrollCount(mk_unroll_bound_var(), m); expr_ref unrollFunc(mk_unroll(regex1, unrollCount), m); expr_ref_vector items(m); items.push_back(ctx.mk_eq_atom(ex, ctx.mk_eq_atom(str, unrollFunc))); items.push_back(ctx.mk_eq_atom(ctx.mk_eq_atom(unrollCount, mk_int(0)), ctx.mk_eq_atom(unrollFunc, mk_string("")))); expr_ref finalAxiom(mk_and(items), m); SASSERT(finalAxiom); assert_axiom(finalAxiom); } else if (u.re.is_range(regex)) { // (re.range "A" "Z") unfolds to (re.union "A" "B" ... "Z"); // we rewrite to expr IFF (str = "A" or str = "B" or ... or str = "Z") expr_ref lo(regex->get_arg(0), m); expr_ref hi(regex->get_arg(1), m); zstring str_lo, str_hi; SASSERT(u.str.is_string(lo)); SASSERT(u.str.is_string(hi)); u.str.is_string(lo, str_lo); u.str.is_string(hi, str_hi); SASSERT(str_lo.length() == 1); SASSERT(str_hi.length() == 1); unsigned int c1 = str_lo[0]; unsigned int c2 = str_hi[0]; if (c1 > c2) { // exchange unsigned int tmp = c1; c1 = c2; c2 = tmp; } expr_ref_vector range_cases(m); for (unsigned int ch = c1; ch <= c2; ++ch) { zstring s_ch(ch); expr_ref rhs(ctx.mk_eq_atom(str, u.str.mk_string(s_ch)), m); range_cases.push_back(rhs); } expr_ref rhs(mk_or(range_cases), m); expr_ref finalAxiom(m.mk_iff(ex, rhs), m); SASSERT(finalAxiom); assert_axiom(finalAxiom); } else if (u.re.is_full_seq(regex)) { // trivially true for any string! assert_axiom(ex); } else if (u.re.is_full_char(regex)) { // any char = any string of length 1 expr_ref rhs(ctx.mk_eq_atom(mk_strlen(str), mk_int(1)), m); expr_ref finalAxiom(m.mk_iff(ex, rhs), m); SASSERT(finalAxiom); assert_axiom(finalAxiom); } else { TRACE("str", tout << "ERROR: unknown regex expression " << mk_pp(regex, m) << "!" << std::endl;); NOT_IMPLEMENTED_YET(); } } void theory_str::attach_new_th_var(enode * n) { context & ctx = get_context(); theory_var v = mk_var(n); ctx.attach_th_var(n, this, v); TRACE("str", tout << "new theory var: " << mk_ismt2_pp(n->get_owner(), get_manager()) << " := v#" << v << std::endl;); } void theory_str::reset_eh() { TRACE("str", tout << "resetting" << std::endl;); m_trail_stack.reset(); m_basicstr_axiom_todo.reset(); m_str_eq_todo.reset(); m_concat_axiom_todo.reset(); pop_scope_eh(get_context().get_scope_level()); } /* * Check equality among equivalence class members of LHS and RHS * to discover an incorrect LHS == RHS. * For example, if we have y2 == "str3" * and the equivalence classes are * { y2, (Concat ce m2) } * { "str3", (Concat abc x2) } * then y2 can't be equal to "str3". * Then add an assertion: (y2 == (Concat ce m2)) AND ("str3" == (Concat abc x2)) -> (y2 != "str3") */ bool theory_str::new_eq_check(expr * lhs, expr * rhs) { context & ctx = get_context(); ast_manager & m = get_manager(); // skip this check if we defer consistency checking, as we can do it for every EQC in final check if (!opt_DeferEQCConsistencyCheck) { check_concat_len_in_eqc(lhs); check_concat_len_in_eqc(rhs); } // Now we iterate over all pairs of terms across both EQCs // and check whether we can show that any pair of distinct terms // cannot possibly be equal. // If that's the case, we assert an axiom to that effect and stop. expr * eqc_nn1 = lhs; do { expr * eqc_nn2 = rhs; do { TRACE("str", tout << "checking whether " << mk_pp(eqc_nn1, m) << " and " << mk_pp(eqc_nn2, m) << " can be equal" << std::endl;); // inconsistency check: value if (!can_two_nodes_eq(eqc_nn1, eqc_nn2)) { TRACE("str", tout << "inconsistency detected: " << mk_pp(eqc_nn1, m) << " cannot be equal to " << mk_pp(eqc_nn2, m) << std::endl;); expr_ref to_assert(mk_not(m, ctx.mk_eq_atom(eqc_nn1, eqc_nn2)), m); assert_axiom(to_assert); // this shouldn't use the integer theory at all, so we don't allow the option of quick-return return false; } if (!check_length_consistency(eqc_nn1, eqc_nn2)) { TRACE("str", tout << "inconsistency detected: " << mk_pp(eqc_nn1, m) << " and " << mk_pp(eqc_nn2, m) << " have inconsistent lengths" << std::endl;); if (opt_NoQuickReturn_IntegerTheory){ TRACE("str", tout << "continuing in new_eq_check() due to opt_NoQuickReturn_IntegerTheory" << std::endl;); } else { return false; } } eqc_nn2 = get_eqc_next(eqc_nn2); } while (eqc_nn2 != rhs); eqc_nn1 = get_eqc_next(eqc_nn1); } while (eqc_nn1 != lhs); if (!contains_map.empty()) { check_contain_in_new_eq(lhs, rhs); } if (!regex_in_bool_map.empty() && !m_params.m_RegexAutomata) { TRACE("str", tout << "checking regex consistency" << std::endl;); check_regex_in(lhs, rhs); } // okay, all checks here passed return true; } // support for user_smt_theory-style EQC handling app * theory_str::get_ast(theory_var v) { return get_enode(v)->get_owner(); } theory_var theory_str::get_var(expr * n) const { if (!is_app(n)) { return null_theory_var; } context & ctx = get_context(); if (ctx.e_internalized(to_app(n))) { enode * e = ctx.get_enode(to_app(n)); return e->get_th_var(get_id()); } return null_theory_var; } // simulate Z3_theory_get_eqc_next() expr * theory_str::get_eqc_next(expr * n) { theory_var v = get_var(n); if (v != null_theory_var) { theory_var r = m_find.next(v); return get_ast(r); } return n; } void theory_str::group_terms_by_eqc(expr * n, std::set & concats, std::set & vars, std::set & consts) { expr * eqcNode = n; do { app * ast = to_app(eqcNode); if (u.str.is_concat(ast)) { expr * simConcat = simplify_concat(ast); if (simConcat != ast) { if (u.str.is_concat(to_app(simConcat))) { concats.insert(simConcat); } else { if (u.str.is_string(simConcat)) { consts.insert(simConcat); } else { vars.insert(simConcat); } } } else { concats.insert(simConcat); } } else if (u.str.is_string(ast)) { consts.insert(ast); } else { vars.insert(ast); } eqcNode = get_eqc_next(eqcNode); } while (eqcNode != n); } void theory_str::get_nodes_in_concat(expr * node, ptr_vector & nodeList) { app * a_node = to_app(node); if (!u.str.is_concat(a_node)) { nodeList.push_back(node); return; } else { SASSERT(a_node->get_num_args() == 2); expr * leftArg = a_node->get_arg(0); expr * rightArg = a_node->get_arg(1); get_nodes_in_concat(leftArg, nodeList); get_nodes_in_concat(rightArg, nodeList); } } // previously Concat() in strTheory.cpp // 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 nullptr if this is not possible. expr * theory_str::eval_concat(expr * n1, expr * n2) { bool n1HasEqcValue = false; bool n2HasEqcValue = false; expr * v1 = get_eqc_value(n1, n1HasEqcValue); expr * v2 = get_eqc_value(n2, n2HasEqcValue); if (n1HasEqcValue && n2HasEqcValue) { zstring n1_str, n2_str; u.str.is_string(v1, n1_str); u.str.is_string(v2, n2_str); zstring result = n1_str + n2_str; return mk_string(result); } else if (n1HasEqcValue && !n2HasEqcValue) { zstring v1_str; u.str.is_string(v1, v1_str); if (v1_str.empty()) { return n2; } } else if (n2HasEqcValue && !n1HasEqcValue) { zstring v2_str; u.str.is_string(v2, v2_str); if (v2_str.empty()) { return n1; } } // give up return nullptr; } // trace code helper inline std::string rational_to_string_if_exists(const rational & x, bool x_exists) { if (x_exists) { return x.to_string(); } else { return "?"; } } /* * The inputs: * ~ nn: non const node * ~ eq_str: the equivalent constant string of nn * Iterate the parent of all eqc nodes of nn, looking for: * ~ concat node * to see whether some concat nodes can be simplified. */ void theory_str::simplify_parent(expr * nn, expr * eq_str) { ast_manager & m = get_manager(); context & ctx = get_context(); TRACE("str", tout << "simplifying parents of " << mk_ismt2_pp(nn, m) << " with respect to " << mk_ismt2_pp(eq_str, m) << std::endl;); ctx.internalize(nn, false); zstring eq_strValue; u.str.is_string(eq_str, eq_strValue); expr * n_eqNode = nn; do { enode * n_eq_enode = ctx.get_enode(n_eqNode); TRACE("str", tout << "considering all parents of " << mk_ismt2_pp(n_eqNode, m) << std::endl << "associated n_eq_enode has " << n_eq_enode->get_num_parents() << " parents" << std::endl;); // the goal of this next bit is to avoid dereferencing a bogus e_parent in the following loop. // what I imagine is causing this bug is that, for example, we examine some parent, we add an axiom that involves it, // and the parent_it iterator becomes invalidated, because we indirectly modified the container that we're iterating over. enode_vector current_parents; for (enode_vector::const_iterator parent_it = n_eq_enode->begin_parents(); parent_it != n_eq_enode->end_parents(); parent_it++) { current_parents.insert(*parent_it); } for (enode_vector::iterator parent_it = current_parents.begin(); parent_it != current_parents.end(); ++parent_it) { enode * e_parent = *parent_it; SASSERT(e_parent != nullptr); app * a_parent = e_parent->get_owner(); TRACE("str", tout << "considering parent " << mk_ismt2_pp(a_parent, m) << std::endl;); if (u.str.is_concat(a_parent)) { expr * arg0 = a_parent->get_arg(0); expr * arg1 = a_parent->get_arg(1); rational parentLen; bool parentLen_exists = get_len_value(a_parent, parentLen); if (arg0 == n_eq_enode->get_owner()) { rational arg0Len, arg1Len; bool arg0Len_exists = get_len_value(eq_str, arg0Len); bool arg1Len_exists = get_len_value(arg1, arg1Len); TRACE("str", tout << "simplify_parent #1:" << std::endl << "* parent = " << mk_ismt2_pp(a_parent, m) << std::endl << "* |parent| = " << rational_to_string_if_exists(parentLen, parentLen_exists) << std::endl << "* |arg0| = " << rational_to_string_if_exists(arg0Len, arg0Len_exists) << std::endl << "* |arg1| = " << rational_to_string_if_exists(arg1Len, arg1Len_exists) << std::endl; ); (void)arg0Len_exists; if (parentLen_exists && !arg1Len_exists) { TRACE("str", tout << "make up len for arg1" << std::endl;); expr_ref implyL11(m.mk_and(ctx.mk_eq_atom(mk_strlen(a_parent), mk_int(parentLen)), ctx.mk_eq_atom(mk_strlen(arg0), mk_int(arg0Len))), m); rational makeUpLenArg1 = parentLen - arg0Len; if (makeUpLenArg1.is_nonneg()) { expr_ref implyR11(ctx.mk_eq_atom(mk_strlen(arg1), mk_int(makeUpLenArg1)), m); assert_implication(implyL11, implyR11); } else { expr_ref neg(mk_not(m, implyL11), m); assert_axiom(neg); } } // (Concat n_eqNode arg1) /\ arg1 has eq const expr * concatResult = eval_concat(eq_str, arg1); if (concatResult != nullptr) { bool arg1HasEqcValue = false; expr * arg1Value = get_eqc_value(arg1, arg1HasEqcValue); expr_ref implyL(m); if (arg1 != arg1Value) { expr_ref eq_ast1(m); eq_ast1 = ctx.mk_eq_atom(n_eqNode, eq_str); SASSERT(eq_ast1); expr_ref eq_ast2(m); eq_ast2 = ctx.mk_eq_atom(arg1, arg1Value); SASSERT(eq_ast2); implyL = m.mk_and(eq_ast1, eq_ast2); } else { implyL = ctx.mk_eq_atom(n_eqNode, eq_str); } if (!in_same_eqc(a_parent, concatResult)) { expr_ref implyR(m); implyR = ctx.mk_eq_atom(a_parent, concatResult); SASSERT(implyR); assert_implication(implyL, implyR); } } else if (u.str.is_concat(to_app(n_eqNode))) { expr_ref simpleConcat(m); simpleConcat = mk_concat(eq_str, arg1); if (!in_same_eqc(a_parent, simpleConcat)) { expr_ref implyL(m); implyL = ctx.mk_eq_atom(n_eqNode, eq_str); SASSERT(implyL); expr_ref implyR(m); implyR = ctx.mk_eq_atom(a_parent, simpleConcat); SASSERT(implyR); assert_implication(implyL, implyR); } } } // if (arg0 == n_eq_enode->get_owner()) if (arg1 == n_eq_enode->get_owner()) { rational arg0Len, arg1Len; bool arg0Len_exists = get_len_value(arg0, arg0Len); bool arg1Len_exists = get_len_value(eq_str, arg1Len); TRACE("str", tout << "simplify_parent #2:" << std::endl << "* parent = " << mk_ismt2_pp(a_parent, m) << std::endl << "* |parent| = " << rational_to_string_if_exists(parentLen, parentLen_exists) << std::endl << "* |arg0| = " << rational_to_string_if_exists(arg0Len, arg0Len_exists) << std::endl << "* |arg1| = " << rational_to_string_if_exists(arg1Len, arg1Len_exists) << std::endl; ); (void)arg1Len_exists; if (parentLen_exists && !arg0Len_exists) { TRACE("str", tout << "make up len for arg0" << std::endl;); expr_ref implyL11(m.mk_and(ctx.mk_eq_atom(mk_strlen(a_parent), mk_int(parentLen)), ctx.mk_eq_atom(mk_strlen(arg1), mk_int(arg1Len))), m); rational makeUpLenArg0 = parentLen - arg1Len; if (makeUpLenArg0.is_nonneg()) { expr_ref implyR11(ctx.mk_eq_atom(mk_strlen(arg0), mk_int(makeUpLenArg0)), m); assert_implication(implyL11, implyR11); } else { expr_ref neg(mk_not(m, implyL11), m); assert_axiom(neg); } } // (Concat arg0 n_eqNode) /\ arg0 has eq const expr * concatResult = eval_concat(arg0, eq_str); if (concatResult != nullptr) { bool arg0HasEqcValue = false; expr * arg0Value = get_eqc_value(arg0, arg0HasEqcValue); expr_ref implyL(m); if (arg0 != arg0Value) { expr_ref eq_ast1(m); eq_ast1 = ctx.mk_eq_atom(n_eqNode, eq_str); SASSERT(eq_ast1); expr_ref eq_ast2(m); eq_ast2 = ctx.mk_eq_atom(arg0, arg0Value); SASSERT(eq_ast2); implyL = m.mk_and(eq_ast1, eq_ast2); } else { implyL = ctx.mk_eq_atom(n_eqNode, eq_str); } if (!in_same_eqc(a_parent, concatResult)) { expr_ref implyR(m); implyR = ctx.mk_eq_atom(a_parent, concatResult); SASSERT(implyR); assert_implication(implyL, implyR); } } else if (u.str.is_concat(to_app(n_eqNode))) { expr_ref simpleConcat(m); simpleConcat = mk_concat(arg0, eq_str); if (!in_same_eqc(a_parent, simpleConcat)) { expr_ref implyL(m); implyL = ctx.mk_eq_atom(n_eqNode, eq_str); SASSERT(implyL); expr_ref implyR(m); implyR = ctx.mk_eq_atom(a_parent, simpleConcat); SASSERT(implyR); assert_implication(implyL, implyR); } } } // if (arg1 == n_eq_enode->get_owner //--------------------------------------------------------- // Case (2-1) begin: (Concat n_eqNode (Concat str var)) if (arg0 == n_eqNode && u.str.is_concat(to_app(arg1))) { app * a_arg1 = to_app(arg1); TRACE("str", tout << "simplify_parent #3" << std::endl;); expr * r_concat_arg0 = a_arg1->get_arg(0); if (u.str.is_string(r_concat_arg0)) { expr * combined_str = eval_concat(eq_str, r_concat_arg0); SASSERT(combined_str); expr * r_concat_arg1 = a_arg1->get_arg(1); expr_ref implyL(m); implyL = ctx.mk_eq_atom(n_eqNode, eq_str); expr * simplifiedAst = mk_concat(combined_str, r_concat_arg1); if (!in_same_eqc(a_parent, simplifiedAst)) { expr_ref implyR(m); implyR = ctx.mk_eq_atom(a_parent, simplifiedAst); assert_implication(implyL, implyR); } } } // Case (2-1) end: (Concat n_eqNode (Concat str var)) //--------------------------------------------------------- //--------------------------------------------------------- // Case (2-2) begin: (Concat (Concat var str) n_eqNode) if (u.str.is_concat(to_app(arg0)) && arg1 == n_eqNode) { app * a_arg0 = to_app(arg0); TRACE("str", tout << "simplify_parent #4" << std::endl;); expr * l_concat_arg1 = a_arg0->get_arg(1); if (u.str.is_string(l_concat_arg1)) { expr * combined_str = eval_concat(l_concat_arg1, eq_str); SASSERT(combined_str); expr * l_concat_arg0 = a_arg0->get_arg(0); expr_ref implyL(m); implyL = ctx.mk_eq_atom(n_eqNode, eq_str); expr * simplifiedAst = mk_concat(l_concat_arg0, combined_str); if (!in_same_eqc(a_parent, simplifiedAst)) { expr_ref implyR(m); implyR = ctx.mk_eq_atom(a_parent, simplifiedAst); assert_implication(implyL, implyR); } } } // Case (2-2) end: (Concat (Concat var str) n_eqNode) //--------------------------------------------------------- // Have to look up one more layer: if the parent of the concat is another concat //------------------------------------------------- // Case (3-1) begin: (Concat (Concat var n_eqNode) str ) if (arg1 == n_eqNode) { for (enode_vector::iterator concat_parent_it = e_parent->begin_parents(); concat_parent_it != e_parent->end_parents(); concat_parent_it++) { enode * e_concat_parent = *concat_parent_it; app * concat_parent = e_concat_parent->get_owner(); if (u.str.is_concat(concat_parent)) { expr * concat_parent_arg0 = concat_parent->get_arg(0); expr * concat_parent_arg1 = concat_parent->get_arg(1); if (concat_parent_arg0 == a_parent && u.str.is_string(concat_parent_arg1)) { TRACE("str", tout << "simplify_parent #5" << std::endl;); expr * combinedStr = eval_concat(eq_str, concat_parent_arg1); SASSERT(combinedStr); expr_ref implyL(m); implyL = ctx.mk_eq_atom(n_eqNode, eq_str); expr * simplifiedAst = mk_concat(arg0, combinedStr); if (!in_same_eqc(concat_parent, simplifiedAst)) { expr_ref implyR(m); implyR = ctx.mk_eq_atom(concat_parent, simplifiedAst); assert_implication(implyL, implyR); } } } } } // Case (3-1) end: (Concat (Concat var n_eqNode) str ) // Case (3-2) begin: (Concat str (Concat n_eqNode var) ) if (arg0 == n_eqNode) { for (enode_vector::iterator concat_parent_it = e_parent->begin_parents(); concat_parent_it != e_parent->end_parents(); concat_parent_it++) { enode * e_concat_parent = *concat_parent_it; app * concat_parent = e_concat_parent->get_owner(); if (u.str.is_concat(concat_parent)) { expr * concat_parent_arg0 = concat_parent->get_arg(0); expr * concat_parent_arg1 = concat_parent->get_arg(1); if (concat_parent_arg1 == a_parent && u.str.is_string(concat_parent_arg0)) { TRACE("str", tout << "simplify_parent #6" << std::endl;); expr * combinedStr = eval_concat(concat_parent_arg0, eq_str); SASSERT(combinedStr); expr_ref implyL(m); implyL = ctx.mk_eq_atom(n_eqNode, eq_str); expr * simplifiedAst = mk_concat(combinedStr, arg1); if (!in_same_eqc(concat_parent, simplifiedAst)) { expr_ref implyR(m); implyR = ctx.mk_eq_atom(concat_parent, simplifiedAst); assert_implication(implyL, implyR); } } } } } // Case (3-2) end: (Concat str (Concat n_eqNode var) ) } // if is_concat(a_parent) } // for parent_it : n_eq_enode->begin_parents() // check next EQC member n_eqNode = get_eqc_next(n_eqNode); } while (n_eqNode != nn); } expr * theory_str::simplify_concat(expr * node) { ast_manager & m = get_manager(); context & ctx = get_context(); std::map resolvedMap; ptr_vector argVec; get_nodes_in_concat(node, argVec); for (unsigned i = 0; i < argVec.size(); ++i) { bool vArgHasEqcValue = false; expr * vArg = get_eqc_value(argVec[i], vArgHasEqcValue); if (vArg != argVec[i]) { resolvedMap[argVec[i]] = vArg; } } if (resolvedMap.empty()) { // no simplification possible return node; } else { expr * resultAst = mk_string(""); for (unsigned i = 0; i < argVec.size(); ++i) { bool vArgHasEqcValue = false; expr * vArg = get_eqc_value(argVec[i], vArgHasEqcValue); resultAst = mk_concat(resultAst, vArg); } TRACE("str", tout << mk_ismt2_pp(node, m) << " is simplified to " << mk_ismt2_pp(resultAst, m) << std::endl;); if (in_same_eqc(node, resultAst)) { TRACE("str", tout << "SKIP: both concats are already in the same equivalence class" << std::endl;); } else { expr_ref_vector items(m); int pos = 0; for (auto itor : resolvedMap) { items.push_back(ctx.mk_eq_atom(itor.first, itor.second)); pos += 1; } expr_ref premise(mk_and(items), m); expr_ref conclusion(ctx.mk_eq_atom(node, resultAst), m); assert_implication(premise, conclusion); } return resultAst; } } // Modified signature of Z3str2's inferLenConcat(). // Returns true iff nLen can be inferred by this method // (i.e. the equivalent of a len_exists flag in get_len_value()). bool theory_str::infer_len_concat(expr * n, rational & nLen) { context & ctx = get_context(); ast_manager & m = get_manager(); expr * arg0 = to_app(n)->get_arg(0); expr * arg1 = to_app(n)->get_arg(1); rational arg0_len, arg1_len; bool arg0_len_exists = get_len_value(arg0, arg0_len); bool arg1_len_exists = get_len_value(arg1, arg1_len); rational tmp_len; bool nLen_exists = get_len_value(n, tmp_len); if (arg0_len_exists && arg1_len_exists && !nLen_exists) { expr_ref_vector l_items(m); // if (mk_strlen(arg0) != mk_int(arg0_len)) { { l_items.push_back(ctx.mk_eq_atom(mk_strlen(arg0), mk_int(arg0_len))); } // if (mk_strlen(arg1) != mk_int(arg1_len)) { { l_items.push_back(ctx.mk_eq_atom(mk_strlen(arg1), mk_int(arg1_len))); } expr_ref axl(m.mk_and(l_items.size(), l_items.c_ptr()), m); rational nnLen = arg0_len + arg1_len; expr_ref axr(ctx.mk_eq_atom(mk_strlen(n), mk_int(nnLen)), m); TRACE("str", tout << "inferred (Length " << mk_pp(n, m) << ") = " << nnLen << std::endl;); assert_implication(axl, axr); nLen = nnLen; return true; } else { return false; } } void theory_str::infer_len_concat_arg(expr * n, rational len) { if (len.is_neg()) { return; } context & ctx = get_context(); ast_manager & m = get_manager(); expr * arg0 = to_app(n)->get_arg(0); expr * arg1 = to_app(n)->get_arg(1); rational arg0_len, arg1_len; bool arg0_len_exists = get_len_value(arg0, arg0_len); bool arg1_len_exists = get_len_value(arg1, arg1_len); expr_ref_vector l_items(m); expr_ref axr(m); axr.reset(); // if (mk_length(t, n) != mk_int(ctx, len)) { { l_items.push_back(ctx.mk_eq_atom(mk_strlen(n), mk_int(len))); } if (!arg0_len_exists && arg1_len_exists) { //if (mk_length(t, arg1) != mk_int(ctx, arg1_len)) { { l_items.push_back(ctx.mk_eq_atom(mk_strlen(arg1), mk_int(arg1_len))); } rational arg0Len = len - arg1_len; if (arg0Len.is_nonneg()) { axr = ctx.mk_eq_atom(mk_strlen(arg0), mk_int(arg0Len)); } else { // could negate } } else if (arg0_len_exists && !arg1_len_exists) { //if (mk_length(t, arg0) != mk_int(ctx, arg0_len)) { { l_items.push_back(ctx.mk_eq_atom(mk_strlen(arg0), mk_int(arg0_len))); } rational arg1Len = len - arg0_len; if (arg1Len.is_nonneg()) { axr = ctx.mk_eq_atom(mk_strlen(arg1), mk_int(arg1Len)); } else { // could negate } } else { } if (axr) { expr_ref axl(m.mk_and(l_items.size(), l_items.c_ptr()), m); assert_implication(axl, axr); } } void theory_str::infer_len_concat_equality(expr * nn1, expr * nn2) { rational nnLen; bool nnLen_exists = get_len_value(nn1, nnLen); if (!nnLen_exists) { nnLen_exists = get_len_value(nn2, nnLen); } // case 1: // Known: a1_arg0 and a1_arg1 // Unknown: nn1 if (u.str.is_concat(to_app(nn1))) { rational nn1ConcatLen; bool nn1ConcatLen_exists = infer_len_concat(nn1, nn1ConcatLen); if (nnLen_exists && nn1ConcatLen_exists) { nnLen = nn1ConcatLen; } } // case 2: // Known: a1_arg0 and a1_arg1 // Unknown: nn1 if (u.str.is_concat(to_app(nn2))) { rational nn2ConcatLen; bool nn2ConcatLen_exists = infer_len_concat(nn2, nn2ConcatLen); if (nnLen_exists && nn2ConcatLen_exists) { nnLen = nn2ConcatLen; } } if (nnLen_exists) { if (u.str.is_concat(to_app(nn1))) { infer_len_concat_arg(nn1, nnLen); } if (u.str.is_concat(to_app(nn2))) { infer_len_concat_arg(nn2, nnLen); } } /* if (isConcatFunc(t, nn2)) { int nn2ConcatLen = inferLenConcat(t, nn2); if (nnLen == -1 && nn2ConcatLen != -1) nnLen = nn2ConcatLen; } if (nnLen != -1) { if (isConcatFunc(t, nn1)) { inferLenConcatArg(t, nn1, nnLen); } if (isConcatFunc(t, nn2)) { inferLenConcatArg(t, nn2, nnLen); } } */ } void theory_str::add_theory_aware_branching_info(expr * term, double priority, lbool phase) { context & ctx = get_context(); ctx.internalize(term, false); bool_var v = ctx.get_bool_var(term); ctx.add_theory_aware_branching_info(v, priority, phase); } void theory_str::generate_mutual_exclusion(expr_ref_vector & terms) { context & ctx = get_context(); // pull each literal out of the arrangement disjunction literal_vector ls; for (expr * e : terms) { literal l = ctx.get_literal(e); ls.push_back(l); } ctx.mk_th_case_split(ls.size(), ls.c_ptr()); } void theory_str::print_cut_var(expr * node, std::ofstream & xout) { ast_manager & m = get_manager(); xout << "Cut info of " << mk_pp(node, m) << std::endl; if (cut_var_map.contains(node)) { if (!cut_var_map[node].empty()) { xout << "[" << cut_var_map[node].top()->level << "] "; for (auto const& kv : cut_var_map[node].top()->vars) { xout << mk_pp(kv.m_key, m) << ", "; } xout << std::endl; } } } /* * Handle two equivalent Concats. */ void theory_str::simplify_concat_equality(expr * nn1, expr * nn2) { ast_manager & m = get_manager(); context & ctx = get_context(); app * a_nn1 = to_app(nn1); SASSERT(a_nn1->get_num_args() == 2); app * a_nn2 = to_app(nn2); SASSERT(a_nn2->get_num_args() == 2); expr * a1_arg0 = a_nn1->get_arg(0); expr * a1_arg1 = a_nn1->get_arg(1); expr * a2_arg0 = a_nn2->get_arg(0); expr * a2_arg1 = a_nn2->get_arg(1); rational a1_arg0_len, a1_arg1_len, a2_arg0_len, a2_arg1_len; bool a1_arg0_len_exists = get_len_value(a1_arg0, a1_arg0_len); bool a1_arg1_len_exists = get_len_value(a1_arg1, a1_arg1_len); bool a2_arg0_len_exists = get_len_value(a2_arg0, a2_arg0_len); bool a2_arg1_len_exists = get_len_value(a2_arg1, a2_arg1_len); TRACE("str", tout << "nn1 = " << mk_ismt2_pp(nn1, m) << std::endl << "nn2 = " << mk_ismt2_pp(nn2, m) << std::endl;); TRACE("str", tout << "len(" << mk_pp(a1_arg0, m) << ") = " << (a1_arg0_len_exists ? a1_arg0_len.to_string() : "?") << std::endl << "len(" << mk_pp(a1_arg1, m) << ") = " << (a1_arg1_len_exists ? a1_arg1_len.to_string() : "?") << std::endl << "len(" << mk_pp(a2_arg0, m) << ") = " << (a2_arg0_len_exists ? a2_arg0_len.to_string() : "?") << std::endl << "len(" << mk_pp(a2_arg1, m) << ") = " << (a2_arg1_len_exists ? a2_arg1_len.to_string() : "?") << std::endl << std::endl;); infer_len_concat_equality(nn1, nn2); if (a1_arg0 == a2_arg0) { if (!in_same_eqc(a1_arg1, a2_arg1)) { expr_ref premise(ctx.mk_eq_atom(nn1, nn2), m); expr_ref eq1(ctx.mk_eq_atom(a1_arg1, a2_arg1), m); expr_ref eq2(ctx.mk_eq_atom(mk_strlen(a1_arg1), mk_strlen(a2_arg1)), m); expr_ref conclusion(m.mk_and(eq1, eq2), m); assert_implication(premise, conclusion); } TRACE("str", tout << "SKIP: a1_arg0 == a2_arg0" << std::endl;); return; } if (a1_arg1 == a2_arg1) { if (!in_same_eqc(a1_arg0, a2_arg0)) { expr_ref premise(ctx.mk_eq_atom(nn1, nn2), m); expr_ref eq1(ctx.mk_eq_atom(a1_arg0, a2_arg0), m); expr_ref eq2(ctx.mk_eq_atom(mk_strlen(a1_arg0), mk_strlen(a2_arg0)), m); expr_ref conclusion(m.mk_and(eq1, eq2), m); assert_implication(premise, conclusion); } TRACE("str", tout << "SKIP: a1_arg1 == a2_arg1" << std::endl;); return; } // quick path if (in_same_eqc(a1_arg0, a2_arg0)) { if (in_same_eqc(a1_arg1, a2_arg1)) { TRACE("str", tout << "SKIP: a1_arg0 =~ a2_arg0 and a1_arg1 =~ a2_arg1" << std::endl;); return; } else { TRACE("str", tout << "quick path 1-1: a1_arg0 =~ a2_arg0" << std::endl;); expr_ref premise(m.mk_and(ctx.mk_eq_atom(nn1, nn2), ctx.mk_eq_atom(a1_arg0, a2_arg0)), m); expr_ref conclusion(m.mk_and(ctx.mk_eq_atom(a1_arg1, a2_arg1), ctx.mk_eq_atom(mk_strlen(a1_arg1), mk_strlen(a2_arg1))), m); assert_implication(premise, conclusion); return; } } else { if (in_same_eqc(a1_arg1, a2_arg1)) { TRACE("str", tout << "quick path 1-2: a1_arg1 =~ a2_arg1" << std::endl;); expr_ref premise(m.mk_and(ctx.mk_eq_atom(nn1, nn2), ctx.mk_eq_atom(a1_arg1, a2_arg1)), m); expr_ref conclusion(m.mk_and(ctx.mk_eq_atom(a1_arg0, a2_arg0), ctx.mk_eq_atom(mk_strlen(a1_arg0), mk_strlen(a2_arg0))), m); assert_implication(premise, conclusion); return; } } // quick path 2-1 if (a1_arg0_len_exists && a2_arg0_len_exists && a1_arg0_len == a2_arg0_len) { if (!in_same_eqc(a1_arg0, a2_arg0)) { TRACE("str", tout << "quick path 2-1: len(nn1.arg0) == len(nn2.arg0)" << std::endl;); expr_ref ax_l1(ctx.mk_eq_atom(nn1, nn2), m); expr_ref ax_l2(ctx.mk_eq_atom(mk_strlen(a1_arg0), mk_strlen(a2_arg0)), m); expr_ref ax_r1(ctx.mk_eq_atom(a1_arg0, a2_arg0), m); expr_ref ax_r2(ctx.mk_eq_atom(a1_arg1, a2_arg1), m); expr_ref premise(m.mk_and(ax_l1, ax_l2), m); expr_ref conclusion(m.mk_and(ax_r1, ax_r2), m); assert_implication(premise, conclusion); if (opt_NoQuickReturn_IntegerTheory) { TRACE("str", tout << "bypassing quick return from the end of this case" << std::endl;); } else { return; } } } if (a1_arg1_len_exists && a2_arg1_len_exists && a1_arg1_len == a2_arg1_len) { if (!in_same_eqc(a1_arg1, a2_arg1)) { TRACE("str", tout << "quick path 2-2: len(nn1.arg1) == len(nn2.arg1)" << std::endl;); expr_ref ax_l1(ctx.mk_eq_atom(nn1, nn2), m); expr_ref ax_l2(ctx.mk_eq_atom(mk_strlen(a1_arg1), mk_strlen(a2_arg1)), m); expr_ref ax_r1(ctx.mk_eq_atom(a1_arg0, a2_arg0), m); expr_ref ax_r2(ctx.mk_eq_atom(a1_arg1, a2_arg1), m); expr_ref premise(m.mk_and(ax_l1, ax_l2), m); expr_ref conclusion(m.mk_and(ax_r1, ax_r2), m); assert_implication(premise, conclusion); if (opt_NoQuickReturn_IntegerTheory) { TRACE("str", tout << "bypassing quick return from the end of this case" << std::endl;); } else { return; } } } expr_ref new_nn1(simplify_concat(nn1), m); expr_ref new_nn2(simplify_concat(nn2), m); app * a_new_nn1 = to_app(new_nn1); app * a_new_nn2 = to_app(new_nn2); TRACE("str", tout << "new_nn1 = " << mk_ismt2_pp(new_nn1, m) << std::endl << "new_nn2 = " << mk_ismt2_pp(new_nn2, m) << std::endl;); if (new_nn1 == new_nn2) { TRACE("str", tout << "equal concats, return" << std::endl;); return; } if (!can_two_nodes_eq(new_nn1, new_nn2)) { expr_ref detected(mk_not(m, ctx.mk_eq_atom(new_nn1, new_nn2)), m); TRACE("str", tout << "inconsistency detected: " << mk_ismt2_pp(detected, m) << std::endl;); assert_axiom(detected); return; } // check whether new_nn1 and new_nn2 are still concats bool n1IsConcat = u.str.is_concat(a_new_nn1); bool n2IsConcat = u.str.is_concat(a_new_nn2); if (!n1IsConcat && n2IsConcat) { TRACE("str", tout << "nn1_new is not a concat" << std::endl;); if (u.str.is_string(a_new_nn1)) { simplify_parent(new_nn2, new_nn1); } return; } else if (n1IsConcat && !n2IsConcat) { TRACE("str", tout << "nn2_new is not a concat" << std::endl;); if (u.str.is_string(a_new_nn2)) { simplify_parent(new_nn1, new_nn2); } return; } else if (!n1IsConcat && !n2IsConcat) { // normally this should never happen, because group_terms_by_eqc() should have pre-simplified // as much as possible. however, we make a defensive check here just in case TRACE("str", tout << "WARNING: nn1_new and nn2_new both simplify to non-concat terms" << std::endl;); return; } expr * v1_arg0 = a_new_nn1->get_arg(0); expr * v1_arg1 = a_new_nn1->get_arg(1); expr * v2_arg0 = a_new_nn2->get_arg(0); expr * v2_arg1 = a_new_nn2->get_arg(1); if (!in_same_eqc(new_nn1, new_nn2) && (nn1 != new_nn1 || nn2 != new_nn2)) { int ii4 = 0; expr* item[3]; if (nn1 != new_nn1) { item[ii4++] = ctx.mk_eq_atom(nn1, new_nn1); } if (nn2 != new_nn2) { item[ii4++] = ctx.mk_eq_atom(nn2, new_nn2); } item[ii4++] = ctx.mk_eq_atom(nn1, nn2); expr_ref premise(m.mk_and(ii4, item), m); expr_ref conclusion(ctx.mk_eq_atom(new_nn1, new_nn2), m); assert_implication(premise, conclusion); } // start to split both concats check_and_init_cut_var(v1_arg0); check_and_init_cut_var(v1_arg1); check_and_init_cut_var(v2_arg0); check_and_init_cut_var(v2_arg1); //************************************************************* // case 1: concat(x, y) = concat(m, n) //************************************************************* if (is_concat_eq_type1(new_nn1, new_nn2)) { process_concat_eq_type1(new_nn1, new_nn2); return; } //************************************************************* // case 2: concat(x, y) = concat(m, "str") //************************************************************* if (is_concat_eq_type2(new_nn1, new_nn2)) { process_concat_eq_type2(new_nn1, new_nn2); return; } //************************************************************* // case 3: concat(x, y) = concat("str", n) //************************************************************* if (is_concat_eq_type3(new_nn1, new_nn2)) { process_concat_eq_type3(new_nn1, new_nn2); return; } //************************************************************* // case 4: concat("str1", y) = concat("str2", n) //************************************************************* if (is_concat_eq_type4(new_nn1, new_nn2)) { process_concat_eq_type4(new_nn1, new_nn2); return; } //************************************************************* // case 5: concat(x, "str1") = concat(m, "str2") //************************************************************* if (is_concat_eq_type5(new_nn1, new_nn2)) { process_concat_eq_type5(new_nn1, new_nn2); return; } //************************************************************* // case 6: concat("str1", y) = concat(m, "str2") //************************************************************* if (is_concat_eq_type6(new_nn1, new_nn2)) { process_concat_eq_type6(new_nn1, new_nn2); return; } } /* * Returns true if attempting to process a concat equality between lhs and rhs * will result in overlapping variables (false otherwise). */ bool theory_str::will_result_in_overlap(expr * lhs, expr * rhs) { ast_manager & m = get_manager(); expr_ref new_nn1(simplify_concat(lhs), m); expr_ref new_nn2(simplify_concat(rhs), m); app * a_new_nn1 = to_app(new_nn1); app * a_new_nn2 = to_app(new_nn2); bool n1IsConcat = u.str.is_concat(a_new_nn1); bool n2IsConcat = u.str.is_concat(a_new_nn2); if (!n1IsConcat && !n2IsConcat) { // we simplified both sides to non-concat expressions... return false; } expr * v1_arg0 = a_new_nn1->get_arg(0); expr * v1_arg1 = a_new_nn1->get_arg(1); expr * v2_arg0 = a_new_nn2->get_arg(0); expr * v2_arg1 = a_new_nn2->get_arg(1); TRACE("str", tout << "checking whether " << mk_pp(new_nn1, m) << " and " << mk_pp(new_nn1, m) << " might overlap." << std::endl;); check_and_init_cut_var(v1_arg0); check_and_init_cut_var(v1_arg1); check_and_init_cut_var(v2_arg0); check_and_init_cut_var(v2_arg1); //************************************************************* // case 1: concat(x, y) = concat(m, n) //************************************************************* if (is_concat_eq_type1(new_nn1, new_nn2)) { TRACE("str", tout << "Type 1 check." << std::endl;); expr * x = to_app(new_nn1)->get_arg(0); expr * y = to_app(new_nn1)->get_arg(1); expr * m = to_app(new_nn2)->get_arg(0); expr * n = to_app(new_nn2)->get_arg(1); if (has_self_cut(m, y)) { TRACE("str", tout << "Possible overlap found" << std::endl; print_cut_var(m, tout); print_cut_var(y, tout);); return true; } else if (has_self_cut(x, n)) { TRACE("str", tout << "Possible overlap found" << std::endl; print_cut_var(x, tout); print_cut_var(n, tout);); return true; } else { return false; } } //************************************************************* // case 2: concat(x, y) = concat(m, "str") //************************************************************* if (is_concat_eq_type2(new_nn1, new_nn2)) { expr * y = nullptr; expr * m = nullptr; expr * v1_arg0 = to_app(new_nn1)->get_arg(0); expr * v1_arg1 = to_app(new_nn1)->get_arg(1); expr * v2_arg0 = to_app(new_nn2)->get_arg(0); expr * v2_arg1 = to_app(new_nn2)->get_arg(1); if (u.str.is_string(v1_arg1) && !u.str.is_string(v2_arg1)) { m = v1_arg0; y = v2_arg1; } else { m = v2_arg0; y = v1_arg1; } if (has_self_cut(m, y)) { TRACE("str", tout << "Possible overlap found" << std::endl; print_cut_var(m, tout); print_cut_var(y, tout);); return true; } else { return false; } } //************************************************************* // case 3: concat(x, y) = concat("str", n) //************************************************************* if (is_concat_eq_type3(new_nn1, new_nn2)) { expr * v1_arg0 = to_app(new_nn1)->get_arg(0); expr * v1_arg1 = to_app(new_nn1)->get_arg(1); expr * v2_arg0 = to_app(new_nn2)->get_arg(0); expr * v2_arg1 = to_app(new_nn2)->get_arg(1); expr * x = nullptr; expr * n = nullptr; if (u.str.is_string(v1_arg0) && !u.str.is_string(v2_arg0)) { n = v1_arg1; x = v2_arg0; } else { n = v2_arg1; x = v1_arg0; } if (has_self_cut(x, n)) { TRACE("str", tout << "Possible overlap found" << std::endl; print_cut_var(x, tout); print_cut_var(n, tout);); return true; } else { return false; } } //************************************************************* // case 4: concat("str1", y) = concat("str2", n) //************************************************************* if (is_concat_eq_type4(new_nn1, new_nn2)) { // This case can never result in an overlap. return false; } //************************************************************* // case 5: concat(x, "str1") = concat(m, "str2") //************************************************************* if (is_concat_eq_type5(new_nn1, new_nn2)) { // This case can never result in an overlap. return false; } //************************************************************* // case 6: concat("str1", y) = concat(m, "str2") //************************************************************* if (is_concat_eq_type6(new_nn1, new_nn2)) { expr * v1_arg0 = to_app(new_nn1)->get_arg(0); expr * v1_arg1 = to_app(new_nn1)->get_arg(1); expr * v2_arg0 = to_app(new_nn2)->get_arg(0); expr * v2_arg1 = to_app(new_nn2)->get_arg(1); expr * y = nullptr; expr * m = nullptr; if (u.str.is_string(v1_arg0)) { y = v1_arg1; m = v2_arg0; } else { y = v2_arg1; m = v1_arg0; } if (has_self_cut(m, y)) { TRACE("str", tout << "Possible overlap found" << std::endl; print_cut_var(m, tout); print_cut_var(y, tout);); return true; } else { return false; } } TRACE("str", tout << "warning: unrecognized concat case" << std::endl;); return false; } /************************************************************* * Type 1: concat(x, y) = concat(m, n) * x, y, m and n all variables *************************************************************/ bool theory_str::is_concat_eq_type1(expr * concatAst1, expr * concatAst2) { expr * x = to_app(concatAst1)->get_arg(0); expr * y = to_app(concatAst1)->get_arg(1); expr * m = to_app(concatAst2)->get_arg(0); expr * n = to_app(concatAst2)->get_arg(1); if (!u.str.is_string(x) && !u.str.is_string(y) && !u.str.is_string(m) && !u.str.is_string(n)) { return true; } else { return false; } } void theory_str::process_concat_eq_type1(expr * concatAst1, expr * concatAst2) { ast_manager & mgr = get_manager(); context & ctx = get_context(); bool overlapAssumptionUsed = false; TRACE("str", tout << "process_concat_eq TYPE 1" << std::endl << "concatAst1 = " << mk_ismt2_pp(concatAst1, mgr) << std::endl << "concatAst2 = " << mk_ismt2_pp(concatAst2, mgr) << std::endl; ); if (!u.str.is_concat(to_app(concatAst1))) { TRACE("str", tout << "concatAst1 is not a concat function" << std::endl;); return; } if (!u.str.is_concat(to_app(concatAst2))) { TRACE("str", tout << "concatAst2 is not a concat function" << std::endl;); return; } expr * x = to_app(concatAst1)->get_arg(0); expr * y = to_app(concatAst1)->get_arg(1); expr * m = to_app(concatAst2)->get_arg(0); expr * n = to_app(concatAst2)->get_arg(1); rational x_len, y_len, m_len, n_len; bool x_len_exists = get_len_value(x, x_len); bool y_len_exists = get_len_value(y, y_len); bool m_len_exists = get_len_value(m, m_len); bool n_len_exists = get_len_value(n, n_len); int splitType = -1; if (x_len_exists && m_len_exists) { TRACE("str", tout << "length values found: x/m" << std::endl;); if (x_len < m_len) { splitType = 0; } else if (x_len == m_len) { splitType = 1; } else { splitType = 2; } } if (splitType == -1 && y_len_exists && n_len_exists) { TRACE("str", tout << "length values found: y/n" << std::endl;); if (y_len > n_len) { splitType = 0; } else if (y_len == n_len) { splitType = 1; } else { splitType = 2; } } TRACE("str", tout << "len(x) = " << (x_len_exists ? x_len.to_string() : "?") << std::endl << "len(y) = " << (y_len_exists ? y_len.to_string() : "?") << std::endl << "len(m) = " << (m_len_exists ? m_len.to_string() : "?") << std::endl << "len(n) = " << (n_len_exists ? n_len.to_string() : "?") << std::endl << "split type " << splitType << std::endl; ); expr * t1 = nullptr; expr * t2 = nullptr; expr * xorFlag = nullptr; std::pair key1(concatAst1, concatAst2); std::pair key2(concatAst2, concatAst1); // check the entries in this map to make sure they're still in scope // before we use them. std::map, std::map >::iterator entry1 = varForBreakConcat.find(key1); std::map, std::map >::iterator entry2 = varForBreakConcat.find(key2); bool entry1InScope; if (entry1 == varForBreakConcat.end()) { entry1InScope = false; } else { if (internal_variable_set.find((entry1->second)[0]) == internal_variable_set.end() || internal_variable_set.find((entry1->second)[1]) == internal_variable_set.end() /*|| internal_variable_set.find((entry1->second)[2]) == internal_variable_set.end() */) { entry1InScope = false; } else { entry1InScope = true; } } bool entry2InScope; if (entry2 == varForBreakConcat.end()) { entry2InScope = false; } else { if (internal_variable_set.find((entry2->second)[0]) == internal_variable_set.end() || internal_variable_set.find((entry2->second)[1]) == internal_variable_set.end() /* || internal_variable_set.find((entry2->second)[2]) == internal_variable_set.end() */) { entry2InScope = false; } else { entry2InScope = true; } } TRACE("str", tout << "entry 1 " << (entry1InScope ? "in scope" : "not in scope") << std::endl << "entry 2 " << (entry2InScope ? "in scope" : "not in scope") << std::endl;); if (!entry1InScope && !entry2InScope) { t1 = mk_nonempty_str_var(); t2 = mk_nonempty_str_var(); xorFlag = mk_internal_xor_var(); check_and_init_cut_var(t1); check_and_init_cut_var(t2); varForBreakConcat[key1][0] = t1; varForBreakConcat[key1][1] = t2; varForBreakConcat[key1][2] = xorFlag; } else { // match found if (entry1InScope) { t1 = varForBreakConcat[key1][0]; t2 = varForBreakConcat[key1][1]; xorFlag = varForBreakConcat[key1][2]; } else { t1 = varForBreakConcat[key2][0]; t2 = varForBreakConcat[key2][1]; xorFlag = varForBreakConcat[key2][2]; } refresh_theory_var(t1); add_nonempty_constraint(t1); refresh_theory_var(t2); add_nonempty_constraint(t2); } // For split types 0 through 2, we can get away with providing // fewer split options since more length information is available. if (splitType == 0) { //-------------------------------------- // Type 0: M cuts Y. // len(x) < len(m) || len(y) > len(n) //-------------------------------------- expr_ref_vector ax_l_items(mgr); expr_ref_vector ax_r_items(mgr); ax_l_items.push_back(ctx.mk_eq_atom(concatAst1, concatAst2)); expr_ref x_t1(mk_concat(x, t1), mgr); expr_ref t1_n(mk_concat(t1, n), mgr); ax_r_items.push_back(ctx.mk_eq_atom(m, x_t1)); ax_r_items.push_back(ctx.mk_eq_atom(y, t1_n)); if (m_len_exists && x_len_exists) { ax_l_items.push_back(ctx.mk_eq_atom(mk_strlen(x), mk_int(x_len))); ax_l_items.push_back(ctx.mk_eq_atom(mk_strlen(m), mk_int(m_len))); rational m_sub_x = m_len - x_len; ax_r_items.push_back(ctx.mk_eq_atom(mk_strlen(t1), mk_int(m_sub_x))); } else { ax_l_items.push_back(ctx.mk_eq_atom(mk_strlen(y), mk_int(y_len))); ax_l_items.push_back(ctx.mk_eq_atom(mk_strlen(n), mk_int(n_len))); rational y_sub_n = y_len - n_len; ax_r_items.push_back(ctx.mk_eq_atom(mk_strlen(t1), mk_int(y_sub_n))); } expr_ref ax_l(mk_and(ax_l_items), mgr); expr_ref ax_r(mk_and(ax_r_items), mgr); if (!has_self_cut(m, y)) { // Cut Info add_cut_info_merge(t1, sLevel, m); add_cut_info_merge(t1, sLevel, y); if (m_params.m_StrongArrangements) { expr_ref ax_strong(ctx.mk_eq_atom(ax_l, ax_r), mgr); assert_axiom(ax_strong); } else { assert_implication(ax_l, ax_r); } } else { loopDetected = true; if (m_params.m_FiniteOverlapModels) { expr_ref tester = set_up_finite_model_test(concatAst1, concatAst2); assert_implication(ax_l, tester); add_theory_aware_branching_info(tester, m_params.m_OverlapTheoryAwarePriority, l_true); } else { TRACE("str", tout << "AVOID LOOP: SKIPPED" << std::endl;); TRACE("str", {print_cut_var(m, tout); print_cut_var(y, tout);}); if (!overlapAssumptionUsed) { overlapAssumptionUsed = true; assert_implication(ax_l, m_theoryStrOverlapAssumption_term); } } } } else if (splitType == 1) { // Type 1: // len(x) = len(m) || len(y) = len(n) expr_ref ax_l1(ctx.mk_eq_atom(concatAst1, concatAst2), mgr); expr_ref ax_l2(mgr.mk_or(ctx.mk_eq_atom(mk_strlen(x), mk_strlen(m)), ctx.mk_eq_atom(mk_strlen(y), mk_strlen(n))), mgr); expr_ref ax_l(mgr.mk_and(ax_l1, ax_l2), mgr); expr_ref ax_r(mgr.mk_and(ctx.mk_eq_atom(x,m), ctx.mk_eq_atom(y,n)), mgr); assert_implication(ax_l, ax_r); } else if (splitType == 2) { // Type 2: X cuts N. // len(x) > len(m) || len(y) < len(n) expr_ref m_t2(mk_concat(m, t2), mgr); expr_ref t2_y(mk_concat(t2, y), mgr); expr_ref_vector ax_l_items(mgr); ax_l_items.push_back(ctx.mk_eq_atom(concatAst1, concatAst2)); expr_ref_vector ax_r_items(mgr); ax_r_items.push_back(ctx.mk_eq_atom(x, m_t2)); ax_r_items.push_back(ctx.mk_eq_atom(t2_y, n)); if (m_len_exists && x_len_exists) { ax_l_items.push_back(ctx.mk_eq_atom(mk_strlen(x), mk_int(x_len))); ax_l_items.push_back(ctx.mk_eq_atom(mk_strlen(m), mk_int(m_len))); rational x_sub_m = x_len - m_len; ax_r_items.push_back(ctx.mk_eq_atom(mk_strlen(t2), mk_int(x_sub_m))); } else { ax_l_items.push_back(ctx.mk_eq_atom(mk_strlen(y), mk_int(y_len))); ax_l_items.push_back(ctx.mk_eq_atom(mk_strlen(n), mk_int(n_len))); rational n_sub_y = n_len - y_len; ax_r_items.push_back(ctx.mk_eq_atom(mk_strlen(t2), mk_int(n_sub_y))); } expr_ref ax_l(mk_and(ax_l_items), mgr); expr_ref ax_r(mk_and(ax_r_items), mgr); if (!has_self_cut(x, n)) { // Cut Info add_cut_info_merge(t2, sLevel, x); add_cut_info_merge(t2, sLevel, n); if (m_params.m_StrongArrangements) { expr_ref ax_strong(ctx.mk_eq_atom(ax_l, ax_r), mgr); assert_axiom(ax_strong); } else { assert_implication(ax_l, ax_r); } } else { loopDetected = true; if (m_params.m_FiniteOverlapModels) { expr_ref tester = set_up_finite_model_test(concatAst1, concatAst2); assert_implication(ax_l, tester); add_theory_aware_branching_info(tester, m_params.m_OverlapTheoryAwarePriority, l_true); } else { TRACE("str", tout << "AVOID LOOP: SKIPPED" << std::endl;); TRACE("str", {print_cut_var(m, tout); print_cut_var(y, tout);}); if (!overlapAssumptionUsed) { overlapAssumptionUsed = true; assert_implication(ax_l, m_theoryStrOverlapAssumption_term); } } } } else if (splitType == -1) { // Here we don't really have a choice. We have no length information at all... // This vector will eventually contain one term for each possible arrangement we explore. expr_ref_vector arrangement_disjunction(mgr); // break option 1: m cuts y // len(x) < len(m) || len(y) > len(n) if (!avoidLoopCut || !has_self_cut(m, y)) { expr_ref_vector and_item(mgr); // break down option 1-1 expr_ref x_t1(mk_concat(x, t1), mgr); expr_ref t1_n(mk_concat(t1, n), mgr); and_item.push_back(ctx.mk_eq_atom(m, x_t1)); and_item.push_back(ctx.mk_eq_atom(y, t1_n)); expr_ref x_plus_t1(m_autil.mk_add(mk_strlen(x), mk_strlen(t1)), mgr); and_item.push_back(ctx.mk_eq_atom(mk_strlen(m), x_plus_t1)); // These were crashing the solver because the integer theory // expects a constant on the right-hand side. // The things we want to assert here are len(m) > len(x) and len(y) > len(n). // We rewrite A > B as A-B > 0 and then as not(A-B <= 0), // and then, *because we aren't allowed to use subtraction*, // as not(A + -1*B <= 0) and_item.push_back( mgr.mk_not(m_autil.mk_le( m_autil.mk_add(mk_strlen(m), m_autil.mk_mul(mk_int(-1), mk_strlen(x))), mk_int(0))) ); and_item.push_back( mgr.mk_not(m_autil.mk_le( m_autil.mk_add(mk_strlen(y),m_autil.mk_mul(mk_int(-1), mk_strlen(n))), mk_int(0))) ); expr_ref option1(mk_and(and_item), mgr); arrangement_disjunction.push_back(option1); add_theory_aware_branching_info(option1, 0.1, l_true); add_cut_info_merge(t1, ctx.get_scope_level(), m); add_cut_info_merge(t1, ctx.get_scope_level(), y); } else { loopDetected = true; if (m_params.m_FiniteOverlapModels) { expr_ref tester = set_up_finite_model_test(concatAst1, concatAst2); arrangement_disjunction.push_back(tester); add_theory_aware_branching_info(tester, m_params.m_OverlapTheoryAwarePriority, l_true); } else { TRACE("str", tout << "AVOID LOOP: SKIPPED" << std::endl;); TRACE("str", {print_cut_var(m, tout); print_cut_var(y, tout);}); if (!overlapAssumptionUsed) { overlapAssumptionUsed = true; arrangement_disjunction.push_back(m_theoryStrOverlapAssumption_term); } } } // break option 2: // x = m . t2 // n = t2 . y if (!avoidLoopCut || !has_self_cut(x, n)) { expr_ref_vector and_item(mgr); // break down option 1-2 expr_ref m_t2(mk_concat(m, t2), mgr); expr_ref t2_y(mk_concat(t2, y), mgr); and_item.push_back(ctx.mk_eq_atom(x, m_t2)); and_item.push_back(ctx.mk_eq_atom(n, t2_y)); expr_ref m_plus_t2(m_autil.mk_add(mk_strlen(m), mk_strlen(t2)), mgr); and_item.push_back(ctx.mk_eq_atom(mk_strlen(x), m_plus_t2)); // want len(x) > len(m) and len(n) > len(y) and_item.push_back( mgr.mk_not(m_autil.mk_le( m_autil.mk_add(mk_strlen(x), m_autil.mk_mul(mk_int(-1), mk_strlen(m))), mk_int(0))) ); and_item.push_back( mgr.mk_not(m_autil.mk_le( m_autil.mk_add(mk_strlen(n), m_autil.mk_mul(mk_int(-1), mk_strlen(y))), mk_int(0))) ); expr_ref option2(mk_and(and_item), mgr); arrangement_disjunction.push_back(option2); add_theory_aware_branching_info(option2, 0.1, l_true); add_cut_info_merge(t2, ctx.get_scope_level(), x); add_cut_info_merge(t2, ctx.get_scope_level(), n); } else { loopDetected = true; if (m_params.m_FiniteOverlapModels) { expr_ref tester = set_up_finite_model_test(concatAst1, concatAst2); arrangement_disjunction.push_back(tester); add_theory_aware_branching_info(tester, m_params.m_OverlapTheoryAwarePriority, l_true); } else { TRACE("str", tout << "AVOID LOOP: SKIPPED" << std::endl;); TRACE("str", {print_cut_var(x, tout); print_cut_var(n, tout);}); if (!overlapAssumptionUsed) { overlapAssumptionUsed = true; arrangement_disjunction.push_back(m_theoryStrOverlapAssumption_term); } } } // option 3: // x = m, y = n if (can_two_nodes_eq(x, m) && can_two_nodes_eq(y, n)) { expr_ref_vector and_item(mgr); and_item.push_back(ctx.mk_eq_atom(x, m)); and_item.push_back(ctx.mk_eq_atom(y, n)); and_item.push_back(ctx.mk_eq_atom(mk_strlen(x), mk_strlen(m))); and_item.push_back(ctx.mk_eq_atom(mk_strlen(y), mk_strlen(n))); expr_ref option3(mk_and(and_item), mgr); arrangement_disjunction.push_back(option3); // prioritize this case, it is easier add_theory_aware_branching_info(option3, 0.5, l_true); } if (!arrangement_disjunction.empty()) { expr_ref premise(ctx.mk_eq_atom(concatAst1, concatAst2), mgr); expr_ref conclusion(mk_or(arrangement_disjunction), mgr); if (m_params.m_StrongArrangements) { expr_ref ax_strong(ctx.mk_eq_atom(premise, conclusion), mgr); assert_axiom(ax_strong); } else { assert_implication(premise, conclusion); } // assert mutual exclusion between each branch of the arrangement generate_mutual_exclusion(arrangement_disjunction); } else { TRACE("str", tout << "STOP: no split option found for two EQ concats." << std::endl;); } } // (splitType == -1) } /************************************************************* * Type 2: concat(x, y) = concat(m, "str") *************************************************************/ bool theory_str::is_concat_eq_type2(expr * concatAst1, expr * concatAst2) { expr * v1_arg0 = to_app(concatAst1)->get_arg(0); expr * v1_arg1 = to_app(concatAst1)->get_arg(1); expr * v2_arg0 = to_app(concatAst2)->get_arg(0); expr * v2_arg1 = to_app(concatAst2)->get_arg(1); if ((!u.str.is_string(v1_arg0)) && u.str.is_string(v1_arg1) && (!u.str.is_string(v2_arg0)) && (!u.str.is_string(v2_arg1))) { return true; } else if ((!u.str.is_string(v2_arg0)) && u.str.is_string(v2_arg1) && (!u.str.is_string(v1_arg0)) && (!u.str.is_string(v1_arg1))) { return true; } else { return false; } } void theory_str::process_concat_eq_type2(expr * concatAst1, expr * concatAst2) { ast_manager & mgr = get_manager(); context & ctx = get_context(); bool overlapAssumptionUsed = false; TRACE("str", tout << "process_concat_eq TYPE 2" << std::endl << "concatAst1 = " << mk_ismt2_pp(concatAst1, mgr) << std::endl << "concatAst2 = " << mk_ismt2_pp(concatAst2, mgr) << std::endl; ); if (!u.str.is_concat(to_app(concatAst1))) { TRACE("str", tout << "concatAst1 is not a concat function" << std::endl;); return; } if (!u.str.is_concat(to_app(concatAst2))) { TRACE("str", tout << "concatAst2 is not a concat function" << std::endl;); return; } expr * x = nullptr; expr * y = nullptr; expr * strAst = nullptr; expr * m = nullptr; expr * v1_arg0 = to_app(concatAst1)->get_arg(0); expr * v1_arg1 = to_app(concatAst1)->get_arg(1); expr * v2_arg0 = to_app(concatAst2)->get_arg(0); expr * v2_arg1 = to_app(concatAst2)->get_arg(1); if (u.str.is_string(v1_arg1) && !u.str.is_string(v2_arg1)) { m = v1_arg0; strAst = v1_arg1; x = v2_arg0; y = v2_arg1; } else { m = v2_arg0; strAst = v2_arg1; x = v1_arg0; y = v1_arg1; } zstring strValue; u.str.is_string(strAst, strValue); rational x_len, y_len, m_len, str_len; bool x_len_exists = get_len_value(x, x_len); bool y_len_exists = get_len_value(y, y_len); bool m_len_exists = get_len_value(m, m_len); bool str_len_exists = true; str_len = rational(strValue.length()); // setup expr * xorFlag = nullptr; expr * temp1 = nullptr; std::pair key1(concatAst1, concatAst2); std::pair key2(concatAst2, concatAst1); // check the entries in this map to make sure they're still in scope // before we use them. std::map, std::map >::iterator entry1 = varForBreakConcat.find(key1); std::map, std::map >::iterator entry2 = varForBreakConcat.find(key2); // prevent checking scope for the XOR term, as it's always in the same scope as the split var bool entry1InScope; if (entry1 == varForBreakConcat.end()) { entry1InScope = false; } else { if (internal_variable_set.find((entry1->second)[0]) == internal_variable_set.end() /*|| internal_variable_set.find((entry1->second)[1]) == internal_variable_set.end()*/ ) { entry1InScope = false; } else { entry1InScope = true; } } bool entry2InScope; if (entry2 == varForBreakConcat.end()) { entry2InScope = false; } else { if (internal_variable_set.find((entry2->second)[0]) == internal_variable_set.end() /*|| internal_variable_set.find((entry2->second)[1]) == internal_variable_set.end()*/ ) { entry2InScope = false; } else { entry2InScope = true; } } TRACE("str", tout << "entry 1 " << (entry1InScope ? "in scope" : "not in scope") << std::endl << "entry 2 " << (entry2InScope ? "in scope" : "not in scope") << std::endl;); if (!entry1InScope && !entry2InScope) { temp1 = mk_nonempty_str_var(); xorFlag = mk_internal_xor_var(); varForBreakConcat[key1][0] = temp1; varForBreakConcat[key1][1] = xorFlag; } else { if (entry1InScope) { temp1 = varForBreakConcat[key1][0]; xorFlag = varForBreakConcat[key1][1]; } else if (entry2InScope) { temp1 = varForBreakConcat[key2][0]; xorFlag = varForBreakConcat[key2][1]; } refresh_theory_var(temp1); add_nonempty_constraint(temp1); } int splitType = -1; if (x_len_exists && m_len_exists) { if (x_len < m_len) splitType = 0; else if (x_len == m_len) splitType = 1; else splitType = 2; } if (splitType == -1 && y_len_exists && str_len_exists) { if (y_len > str_len) splitType = 0; else if (y_len == str_len) splitType = 1; else splitType = 2; } TRACE("str", tout << "Split type " << splitType << std::endl;); // Provide fewer split options when length information is available. if (splitType == 0) { // M cuts Y // | x | y | // | m | str | expr_ref temp1_strAst(mk_concat(temp1, strAst), mgr); if (can_two_nodes_eq(y, temp1_strAst)) { expr_ref_vector l_items(mgr); l_items.push_back(ctx.mk_eq_atom(concatAst1, concatAst2)); expr_ref_vector r_items(mgr); expr_ref x_temp1(mk_concat(x, temp1), mgr); r_items.push_back(ctx.mk_eq_atom(m, x_temp1)); r_items.push_back(ctx.mk_eq_atom(y, temp1_strAst)); if (x_len_exists && m_len_exists) { l_items.push_back(ctx.mk_eq_atom(mk_strlen(x), mk_int(x_len))); l_items.push_back(ctx.mk_eq_atom(mk_strlen(m), mk_int(m_len))); rational m_sub_x = (m_len - x_len); r_items.push_back(ctx.mk_eq_atom(mk_strlen(temp1), mk_int(m_sub_x))); } else { l_items.push_back(ctx.mk_eq_atom(mk_strlen(y), mk_int(y_len))); l_items.push_back(ctx.mk_eq_atom(mk_strlen(strAst), mk_int(str_len))); rational y_sub_str = (y_len - str_len); r_items.push_back(ctx.mk_eq_atom(mk_strlen(temp1), mk_int(y_sub_str))); } expr_ref ax_l(mk_and(l_items), mgr); expr_ref ax_r(mk_and(r_items), mgr); if (!avoidLoopCut || !(has_self_cut(m, y))) { // break down option 2-1 add_cut_info_merge(temp1, sLevel, y); add_cut_info_merge(temp1, sLevel, m); if (m_params.m_StrongArrangements) { expr_ref ax_strong(ctx.mk_eq_atom(ax_l, ax_r), mgr); assert_axiom(ax_strong); } else { assert_implication(ax_l, ax_r); } } else { loopDetected = true; if (m_params.m_FiniteOverlapModels) { expr_ref tester = set_up_finite_model_test(concatAst1, concatAst2); assert_implication(ax_l, tester); add_theory_aware_branching_info(tester, m_params.m_OverlapTheoryAwarePriority, l_true); } else { TRACE("str", tout << "AVOID LOOP: SKIP" << std::endl;); TRACE("str", {print_cut_var(m, tout); print_cut_var(y, tout);}); if (!overlapAssumptionUsed) { overlapAssumptionUsed = true; assert_implication(ax_l, m_theoryStrOverlapAssumption_term); } } } } } else if (splitType == 1) { // | x | y | // | m | str | expr_ref ax_l1(ctx.mk_eq_atom(concatAst1, concatAst2), mgr); expr_ref ax_l2(mgr.mk_or( ctx.mk_eq_atom(mk_strlen(x), mk_strlen(m)), ctx.mk_eq_atom(mk_strlen(y), mk_strlen(strAst))), mgr); expr_ref ax_l(mgr.mk_and(ax_l1, ax_l2), mgr); expr_ref ax_r(mgr.mk_and(ctx.mk_eq_atom(x, m), ctx.mk_eq_atom(y, strAst)), mgr); assert_implication(ax_l, ax_r); } else if (splitType == 2) { // m cut y, // | x | y | // | m | str | rational lenDelta; expr_ref_vector l_items(mgr); l_items.push_back(ctx.mk_eq_atom(concatAst1, concatAst2)); if (x_len_exists && m_len_exists) { l_items.push_back(ctx.mk_eq_atom(mk_strlen(x), mk_int(x_len))); l_items.push_back(ctx.mk_eq_atom(mk_strlen(m), mk_int(m_len))); lenDelta = x_len - m_len; } else { l_items.push_back(ctx.mk_eq_atom(mk_strlen(y), mk_int(y_len))); lenDelta = str_len - y_len; } TRACE("str", tout << "xLen? " << (x_len_exists ? "yes" : "no") << std::endl << "mLen? " << (m_len_exists ? "yes" : "no") << std::endl << "yLen? " << (y_len_exists ? "yes" : "no") << std::endl << "xLen = " << x_len.to_string() << std::endl << "yLen = " << y_len.to_string() << std::endl << "mLen = " << m_len.to_string() << std::endl << "strLen = " << str_len.to_string() << std::endl << "lenDelta = " << lenDelta.to_string() << std::endl << "strValue = \"" << strValue << "\" (len=" << strValue.length() << ")" << "\n" ; ); zstring part1Str = strValue.extract(0, lenDelta.get_unsigned()); zstring part2Str = strValue.extract(lenDelta.get_unsigned(), strValue.length() - lenDelta.get_unsigned()); expr_ref prefixStr(mk_string(part1Str), mgr); expr_ref x_concat(mk_concat(m, prefixStr), mgr); expr_ref cropStr(mk_string(part2Str), mgr); if (can_two_nodes_eq(x, x_concat) && can_two_nodes_eq(y, cropStr)) { expr_ref_vector r_items(mgr); r_items.push_back(ctx.mk_eq_atom(x, x_concat)); r_items.push_back(ctx.mk_eq_atom(y, cropStr)); expr_ref ax_l(mk_and(l_items), mgr); expr_ref ax_r(mk_and(r_items), mgr); if (m_params.m_StrongArrangements) { expr_ref ax_strong(ctx.mk_eq_atom(ax_l, ax_r), mgr); assert_axiom(ax_strong); } else { assert_implication(ax_l, ax_r); } } else { // negate! It's impossible to split str with these lengths TRACE("str", tout << "CONFLICT: Impossible to split str with these lengths." << std::endl;); expr_ref ax_l(mk_and(l_items), mgr); assert_axiom(mgr.mk_not(ax_l)); } } else { // Split type -1: no idea about the length... expr_ref_vector arrangement_disjunction(mgr); expr_ref temp1_strAst(mk_concat(temp1, strAst), mgr); // m cuts y if (can_two_nodes_eq(y, temp1_strAst)) { if (!avoidLoopCut || !has_self_cut(m, y)) { // break down option 2-1 expr_ref_vector and_item(mgr); expr_ref x_temp1(mk_concat(x, temp1), mgr); and_item.push_back(ctx.mk_eq_atom(m, x_temp1)); and_item.push_back(ctx.mk_eq_atom(y, temp1_strAst)); and_item.push_back(ctx.mk_eq_atom(mk_strlen(m), m_autil.mk_add(mk_strlen(x), mk_strlen(temp1)))); expr_ref option1(mk_and(and_item), mgr); arrangement_disjunction.push_back(option1); add_theory_aware_branching_info(option1, 0.1, l_true); add_cut_info_merge(temp1, ctx.get_scope_level(), y); add_cut_info_merge(temp1, ctx.get_scope_level(), m); } else { loopDetected = true; if (m_params.m_FiniteOverlapModels) { expr_ref tester = set_up_finite_model_test(concatAst1, concatAst2); arrangement_disjunction.push_back(tester); add_theory_aware_branching_info(tester, m_params.m_OverlapTheoryAwarePriority, l_true); } else { TRACE("str", tout << "AVOID LOOP: SKIPPED" << std::endl;); TRACE("str", {print_cut_var(m, tout); print_cut_var(y, tout);}); if (!overlapAssumptionUsed) { overlapAssumptionUsed = true; arrangement_disjunction.push_back(m_theoryStrOverlapAssumption_term); } } } } for (unsigned int i = 0; i <= strValue.length(); ++i) { zstring part1Str = strValue.extract(0, i); zstring part2Str = strValue.extract(i, strValue.length() - i); expr_ref prefixStr(mk_string(part1Str), mgr); expr_ref x_concat(mk_concat(m, prefixStr), mgr); expr_ref cropStr(mk_string(part2Str), mgr); if (can_two_nodes_eq(x, x_concat) && can_two_nodes_eq(y, cropStr)) { // break down option 2-2 expr_ref_vector and_item(mgr); and_item.push_back(ctx.mk_eq_atom(x, x_concat)); and_item.push_back(ctx.mk_eq_atom(y, cropStr)); and_item.push_back(ctx.mk_eq_atom(mk_strlen(y), mk_int(part2Str.length()))); expr_ref option2(mk_and(and_item), mgr); arrangement_disjunction.push_back(option2); double priority; // prioritize the option where y is equal to the original string if (i == 0) { priority = 0.5; } else { priority = 0.1; } add_theory_aware_branching_info(option2, priority, l_true); } } if (!arrangement_disjunction.empty()) { expr_ref implyR(mk_or(arrangement_disjunction), mgr); if (m_params.m_StrongArrangements) { expr_ref implyLHS(ctx.mk_eq_atom(concatAst1, concatAst2), mgr); expr_ref ax_strong(ctx.mk_eq_atom(implyLHS, implyR), mgr); assert_axiom(ax_strong); } else { assert_implication(ctx.mk_eq_atom(concatAst1, concatAst2), implyR); } generate_mutual_exclusion(arrangement_disjunction); } else { TRACE("str", tout << "STOP: Should not split two EQ concats." << std::endl;); } } // (splitType == -1) } /************************************************************* * Type 3: concat(x, y) = concat("str", n) *************************************************************/ bool theory_str::is_concat_eq_type3(expr * concatAst1, expr * concatAst2) { expr * v1_arg0 = to_app(concatAst1)->get_arg(0); expr * v1_arg1 = to_app(concatAst1)->get_arg(1); expr * v2_arg0 = to_app(concatAst2)->get_arg(0); expr * v2_arg1 = to_app(concatAst2)->get_arg(1); if (u.str.is_string(v1_arg0) && (!u.str.is_string(v1_arg1)) && (!u.str.is_string(v2_arg0)) && (!u.str.is_string(v2_arg1))) { return true; } else if (u.str.is_string(v2_arg0) && (!u.str.is_string(v2_arg1)) && (!u.str.is_string(v1_arg0)) && (!u.str.is_string(v1_arg1))) { return true; } else { return false; } } void theory_str::process_concat_eq_type3(expr * concatAst1, expr * concatAst2) { ast_manager & mgr = get_manager(); context & ctx = get_context(); bool overlapAssumptionUsed = false; TRACE("str", tout << "process_concat_eq TYPE 3" << std::endl << "concatAst1 = " << mk_ismt2_pp(concatAst1, mgr) << std::endl << "concatAst2 = " << mk_ismt2_pp(concatAst2, mgr) << std::endl; ); if (!u.str.is_concat(to_app(concatAst1))) { TRACE("str", tout << "concatAst1 is not a concat function" << std::endl;); return; } if (!u.str.is_concat(to_app(concatAst2))) { TRACE("str", tout << "concatAst2 is not a concat function" << std::endl;); return; } expr * v1_arg0 = to_app(concatAst1)->get_arg(0); expr * v1_arg1 = to_app(concatAst1)->get_arg(1); expr * v2_arg0 = to_app(concatAst2)->get_arg(0); expr * v2_arg1 = to_app(concatAst2)->get_arg(1); expr * x = nullptr; expr * y = nullptr; expr * strAst = nullptr; expr * n = nullptr; if (u.str.is_string(v1_arg0) && !u.str.is_string(v2_arg0)) { strAst = v1_arg0; n = v1_arg1; x = v2_arg0; y = v2_arg1; } else { strAst = v2_arg0; n = v2_arg1; x = v1_arg0; y = v1_arg1; } zstring strValue; u.str.is_string(strAst, strValue); rational x_len, y_len, str_len, n_len; bool x_len_exists = get_len_value(x, x_len); bool y_len_exists = get_len_value(y, y_len); str_len = rational((unsigned)(strValue.length())); bool n_len_exists = get_len_value(n, n_len); expr_ref xorFlag(mgr); expr_ref temp1(mgr); std::pair key1(concatAst1, concatAst2); std::pair key2(concatAst2, concatAst1); // check the entries in this map to make sure they're still in scope // before we use them. std::map, std::map >::iterator entry1 = varForBreakConcat.find(key1); std::map, std::map >::iterator entry2 = varForBreakConcat.find(key2); bool entry1InScope; if (entry1 == varForBreakConcat.end()) { entry1InScope = false; } else { if (internal_variable_set.find((entry1->second)[0]) == internal_variable_set.end() /* || internal_variable_set.find((entry1->second)[1]) == internal_variable_set.end() */) { entry1InScope = false; } else { entry1InScope = true; } } bool entry2InScope; if (entry2 == varForBreakConcat.end()) { entry2InScope = false; } else { if (internal_variable_set.find((entry2->second)[0]) == internal_variable_set.end() /* || internal_variable_set.find((entry2->second)[1]) == internal_variable_set.end() */) { entry2InScope = false; } else { entry2InScope = true; } } TRACE("str", tout << "entry 1 " << (entry1InScope ? "in scope" : "not in scope") << std::endl << "entry 2 " << (entry2InScope ? "in scope" : "not in scope") << std::endl;); if (!entry1InScope && !entry2InScope) { temp1 = mk_nonempty_str_var(); xorFlag = mk_internal_xor_var(); varForBreakConcat[key1][0] = temp1; varForBreakConcat[key1][1] = xorFlag; } else { if (entry1InScope) { temp1 = varForBreakConcat[key1][0]; xorFlag = varForBreakConcat[key1][1]; } else if (varForBreakConcat.find(key2) != varForBreakConcat.end()) { temp1 = varForBreakConcat[key2][0]; xorFlag = varForBreakConcat[key2][1]; } refresh_theory_var(temp1); add_nonempty_constraint(temp1); } int splitType = -1; if (x_len_exists) { if (x_len < str_len) splitType = 0; else if (x_len == str_len) splitType = 1; else splitType = 2; } if (splitType == -1 && y_len_exists && n_len_exists) { if (y_len > n_len) splitType = 0; else if (y_len == n_len) splitType = 1; else splitType = 2; } TRACE("str", tout << "Split type " << splitType << std::endl;); // Provide fewer split options when length information is available. if (splitType == 0) { // | x | y | // | str | n | expr_ref_vector litems(mgr); litems.push_back(ctx.mk_eq_atom(concatAst1, concatAst2)); rational prefixLen; if (!x_len_exists) { prefixLen = str_len - (y_len - n_len); litems.push_back(ctx.mk_eq_atom(mk_strlen(y), mk_int(y_len))); litems.push_back(ctx.mk_eq_atom(mk_strlen(n), mk_int(n_len))); } else { prefixLen = x_len; litems.push_back(ctx.mk_eq_atom(mk_strlen(x), mk_int(x_len))); } zstring prefixStr = strValue.extract(0, prefixLen.get_unsigned()); rational str_sub_prefix = str_len - prefixLen; zstring suffixStr = strValue.extract(prefixLen.get_unsigned(), str_sub_prefix.get_unsigned()); expr_ref prefixAst(mk_string(prefixStr), mgr); expr_ref suffixAst(mk_string(suffixStr), mgr); expr_ref ax_l(mgr.mk_and(litems.size(), litems.c_ptr()), mgr); expr_ref suf_n_concat(mk_concat(suffixAst, n), mgr); if (can_two_nodes_eq(x, prefixAst) && can_two_nodes_eq(y, suf_n_concat)) { expr_ref_vector r_items(mgr); r_items.push_back(ctx.mk_eq_atom(x, prefixAst)); r_items.push_back(ctx.mk_eq_atom(y, suf_n_concat)); if (m_params.m_StrongArrangements) { expr_ref ax_strong(ctx.mk_eq_atom(ax_l, mk_and(r_items)), mgr); assert_axiom(ax_strong); } else { assert_implication(ax_l, mk_and(r_items)); } } else { // negate! It's impossible to split str with these lengths TRACE("str", tout << "CONFLICT: Impossible to split str with these lengths." << std::endl;); assert_axiom(mgr.mk_not(ax_l)); } } else if (splitType == 1) { expr_ref ax_l1(ctx.mk_eq_atom(concatAst1, concatAst2), mgr); expr_ref ax_l2(mgr.mk_or( ctx.mk_eq_atom(mk_strlen(x), mk_strlen(strAst)), ctx.mk_eq_atom(mk_strlen(y), mk_strlen(n))), mgr); expr_ref ax_l(mgr.mk_and(ax_l1, ax_l2), mgr); expr_ref ax_r(mgr.mk_and(ctx.mk_eq_atom(x, strAst), ctx.mk_eq_atom(y, n)), mgr); if (m_params.m_StrongArrangements) { expr_ref ax_strong(ctx.mk_eq_atom(ax_l, ax_r), mgr); assert_axiom(ax_strong); } else { assert_implication(ax_l, ax_r); } } else if (splitType == 2) { // | x | y | // | str | n | expr_ref_vector litems(mgr); litems.push_back(ctx.mk_eq_atom(concatAst1, concatAst2)); rational tmpLen; if (!x_len_exists) { tmpLen = n_len - y_len; litems.push_back(ctx.mk_eq_atom(mk_strlen(y), mk_int(y_len))); litems.push_back(ctx.mk_eq_atom(mk_strlen(n), mk_int(n_len))); } else { tmpLen = x_len - str_len; litems.push_back(ctx.mk_eq_atom(mk_strlen(x), mk_int(x_len))); } expr_ref ax_l(mgr.mk_and(litems.size(), litems.c_ptr()), mgr); expr_ref str_temp1(mk_concat(strAst, temp1), mgr); expr_ref temp1_y(mk_concat(temp1, y), mgr); if (can_two_nodes_eq(x, str_temp1)) { if (!avoidLoopCut || !(has_self_cut(x, n))) { expr_ref_vector r_items(mgr); r_items.push_back(ctx.mk_eq_atom(x, str_temp1)); r_items.push_back(ctx.mk_eq_atom(n, temp1_y)); r_items.push_back(ctx.mk_eq_atom(mk_strlen(temp1), mk_int(tmpLen))); expr_ref ax_r(mk_and(r_items), mgr); //Cut Info add_cut_info_merge(temp1, sLevel, x); add_cut_info_merge(temp1, sLevel, n); if (m_params.m_StrongArrangements) { expr_ref ax_strong(ctx.mk_eq_atom(ax_l, ax_r), mgr); assert_axiom(ax_strong); } else { assert_implication(ax_l, ax_r); } } else { loopDetected = true; if (m_params.m_FiniteOverlapModels) { expr_ref tester = set_up_finite_model_test(concatAst1, concatAst2); assert_implication(ax_l, tester); add_theory_aware_branching_info(tester, m_params.m_OverlapTheoryAwarePriority, l_true); } else { TRACE("str", tout << "AVOID LOOP: SKIPPED" << std::endl;); TRACE("str", {print_cut_var(x, tout); print_cut_var(n, tout);}); if (!overlapAssumptionUsed) { overlapAssumptionUsed = true; assert_implication(ax_l, m_theoryStrOverlapAssumption_term); } } } } // else { // // negate! It's impossible to split str with these lengths // __debugPrint(logFile, "[Conflict] Negate! It's impossible to split str with these lengths @ %d.\n", __LINE__); // addAxiom(t, Z3_mk_not(ctx, ax_l), __LINE__); // } } else { // Split type -1. We know nothing about the length... expr_ref_vector arrangement_disjunction(mgr); int pos = 1; for (unsigned int i = 0; i <= strValue.length(); i++) { zstring part1Str = strValue.extract(0, i); zstring part2Str = strValue.extract(i, strValue.length() - i); expr_ref cropStr(mk_string(part1Str), mgr); expr_ref suffixStr(mk_string(part2Str), mgr); expr_ref y_concat(mk_concat(suffixStr, n), mgr); if (can_two_nodes_eq(x, cropStr) && can_two_nodes_eq(y, y_concat)) { expr_ref_vector and_item(mgr); // break down option 3-1 expr_ref x_eq_str(ctx.mk_eq_atom(x, cropStr), mgr); and_item.push_back(x_eq_str); ++pos; and_item.push_back(ctx.mk_eq_atom(y, y_concat)); and_item.push_back(ctx.mk_eq_atom(mk_strlen(x), mk_strlen(cropStr))); ++pos; // and_item[pos++] = Z3_mk_eq(ctx, or_item[option], Z3_mk_eq(ctx, mk_length(t, y), mk_length(t, y_concat))); // adding length constraint for _ = constStr seems slowing things down. expr_ref option1(mk_and(and_item), mgr); arrangement_disjunction.push_back(option1); double priority; if (i == strValue.length()) { priority = 0.5; } else { priority = 0.1; } add_theory_aware_branching_info(option1, priority, l_true); } } expr_ref strAst_temp1(mk_concat(strAst, temp1), mgr); //-------------------------------------------------------- // x cut n //-------------------------------------------------------- if (can_two_nodes_eq(x, strAst_temp1)) { if (!avoidLoopCut || !(has_self_cut(x, n))) { // break down option 3-2 expr_ref_vector and_item(mgr); expr_ref temp1_y(mk_concat(temp1, y), mgr); and_item.push_back(ctx.mk_eq_atom(x, strAst_temp1)); ++pos; and_item.push_back(ctx.mk_eq_atom(n, temp1_y)); ++pos; and_item.push_back(ctx.mk_eq_atom(mk_strlen(x), m_autil.mk_add(mk_strlen(strAst), mk_strlen(temp1)) ) ); ++pos; expr_ref option2(mk_and(and_item), mgr); arrangement_disjunction.push_back(option2); add_theory_aware_branching_info(option2, 0.1, l_true); add_cut_info_merge(temp1, sLevel, x); add_cut_info_merge(temp1, sLevel, n); } else { loopDetected = true; if (m_params.m_FiniteOverlapModels) { expr_ref tester = set_up_finite_model_test(concatAst1, concatAst2); arrangement_disjunction.push_back(tester); add_theory_aware_branching_info(tester, m_params.m_OverlapTheoryAwarePriority, l_true); } else { TRACE("str", tout << "AVOID LOOP: SKIPPED." << std::endl;); TRACE("str", {print_cut_var(x, tout); print_cut_var(n, tout);}); if (!overlapAssumptionUsed) { overlapAssumptionUsed = true; arrangement_disjunction.push_back(m_theoryStrOverlapAssumption_term); } } } } if (!arrangement_disjunction.empty()) { expr_ref implyR(mk_or(arrangement_disjunction), mgr); if (m_params.m_StrongArrangements) { expr_ref ax_lhs(ctx.mk_eq_atom(concatAst1, concatAst2), mgr); expr_ref ax_strong(ctx.mk_eq_atom(ax_lhs, implyR), mgr); assert_axiom(ax_strong); } else { assert_implication(ctx.mk_eq_atom(concatAst1, concatAst2), implyR); } generate_mutual_exclusion(arrangement_disjunction); } else { TRACE("str", tout << "STOP: should not split two eq. concats" << std::endl;); } } } /************************************************************* * Type 4: concat("str1", y) = concat("str2", n) *************************************************************/ bool theory_str::is_concat_eq_type4(expr * concatAst1, expr * concatAst2) { expr * v1_arg0 = to_app(concatAst1)->get_arg(0); expr * v1_arg1 = to_app(concatAst1)->get_arg(1); expr * v2_arg0 = to_app(concatAst2)->get_arg(0); expr * v2_arg1 = to_app(concatAst2)->get_arg(1); if (u.str.is_string(v1_arg0) && (!u.str.is_string(v1_arg1)) && u.str.is_string(v2_arg0) && (!u.str.is_string(v2_arg1))) { return true; } else { return false; } } void theory_str::process_concat_eq_type4(expr * concatAst1, expr * concatAst2) { ast_manager & mgr = get_manager(); context & ctx = get_context(); TRACE("str", tout << "process_concat_eq TYPE 4" << std::endl << "concatAst1 = " << mk_ismt2_pp(concatAst1, mgr) << std::endl << "concatAst2 = " << mk_ismt2_pp(concatAst2, mgr) << std::endl; ); if (!u.str.is_concat(to_app(concatAst1))) { TRACE("str", tout << "concatAst1 is not a concat function" << std::endl;); return; } if (!u.str.is_concat(to_app(concatAst2))) { TRACE("str", tout << "concatAst2 is not a concat function" << std::endl;); return; } expr * v1_arg0 = to_app(concatAst1)->get_arg(0); expr * v1_arg1 = to_app(concatAst1)->get_arg(1); expr * v2_arg0 = to_app(concatAst2)->get_arg(0); expr * v2_arg1 = to_app(concatAst2)->get_arg(1); expr * str1Ast = v1_arg0; expr * y = v1_arg1; expr * str2Ast = v2_arg0; expr * n = v2_arg1; zstring str1Value, str2Value; u.str.is_string(str1Ast, str1Value); u.str.is_string(str2Ast, str2Value); unsigned int str1Len = str1Value.length(); unsigned int str2Len = str2Value.length(); int commonLen = (str1Len > str2Len) ? str2Len : str1Len; if (str1Value.extract(0, commonLen) != str2Value.extract(0, commonLen)) { TRACE("str", tout << "Conflict: " << mk_ismt2_pp(concatAst1, mgr) << " has no common prefix with " << mk_ismt2_pp(concatAst2, mgr) << std::endl;); expr_ref toNegate(mgr.mk_not(ctx.mk_eq_atom(concatAst1, concatAst2)), mgr); assert_axiom(toNegate); return; } else { if (str1Len > str2Len) { zstring deltaStr = str1Value.extract(str2Len, str1Len - str2Len); expr_ref tmpAst(mk_concat(mk_string(deltaStr), y), mgr); if (!in_same_eqc(tmpAst, n)) { // break down option 4-1 expr_ref implyR(ctx.mk_eq_atom(n, tmpAst), mgr); if (m_params.m_StrongArrangements) { expr_ref ax_strong(ctx.mk_eq_atom( ctx.mk_eq_atom(concatAst1, concatAst2), implyR ), mgr); assert_axiom(ax_strong); } else { assert_implication(ctx.mk_eq_atom(concatAst1, concatAst2), implyR); } } } else if (str1Len == str2Len) { if (!in_same_eqc(n, y)) { //break down option 4-2 expr_ref implyR(ctx.mk_eq_atom(n, y), mgr); if (m_params.m_StrongArrangements) { expr_ref ax_strong(ctx.mk_eq_atom( ctx.mk_eq_atom(concatAst1, concatAst2), implyR ), mgr); assert_axiom(ax_strong); } else { assert_implication(ctx.mk_eq_atom(concatAst1, concatAst2), implyR); } } } else { zstring deltaStr = str2Value.extract(str1Len, str2Len - str1Len); expr_ref tmpAst(mk_concat(mk_string(deltaStr), n), mgr); if (!in_same_eqc(y, tmpAst)) { //break down option 4-3 expr_ref implyR(ctx.mk_eq_atom(y, tmpAst), mgr); if (m_params.m_StrongArrangements) { expr_ref ax_strong(ctx.mk_eq_atom( ctx.mk_eq_atom(concatAst1, concatAst2), implyR ), mgr); assert_axiom(ax_strong); } else { assert_implication(ctx.mk_eq_atom(concatAst1, concatAst2), implyR); } } } } } /************************************************************* * case 5: concat(x, "str1") = concat(m, "str2") *************************************************************/ bool theory_str::is_concat_eq_type5(expr * concatAst1, expr * concatAst2) { expr * v1_arg0 = to_app(concatAst1)->get_arg(0); expr * v1_arg1 = to_app(concatAst1)->get_arg(1); expr * v2_arg0 = to_app(concatAst2)->get_arg(0); expr * v2_arg1 = to_app(concatAst2)->get_arg(1); if ((!u.str.is_string(v1_arg0)) && u.str.is_string(v1_arg1) && (!u.str.is_string(v2_arg0)) && u.str.is_string(v2_arg1)) { return true; } else { return false; } } void theory_str::process_concat_eq_type5(expr * concatAst1, expr * concatAst2) { ast_manager & mgr = get_manager(); context & ctx = get_context(); TRACE("str", tout << "process_concat_eq TYPE 5" << std::endl << "concatAst1 = " << mk_ismt2_pp(concatAst1, mgr) << std::endl << "concatAst2 = " << mk_ismt2_pp(concatAst2, mgr) << std::endl; ); if (!u.str.is_concat(to_app(concatAst1))) { TRACE("str", tout << "concatAst1 is not a concat function" << std::endl;); return; } if (!u.str.is_concat(to_app(concatAst2))) { TRACE("str", tout << "concatAst2 is not a concat function" << std::endl;); return; } expr * v1_arg0 = to_app(concatAst1)->get_arg(0); expr * v1_arg1 = to_app(concatAst1)->get_arg(1); expr * v2_arg0 = to_app(concatAst2)->get_arg(0); expr * v2_arg1 = to_app(concatAst2)->get_arg(1); expr * x = v1_arg0; expr * str1Ast = v1_arg1; expr * m = v2_arg0; expr * str2Ast = v2_arg1; zstring str1Value, str2Value; u.str.is_string(str1Ast, str1Value); u.str.is_string(str2Ast, str2Value); unsigned int str1Len = str1Value.length(); unsigned int str2Len = str2Value.length(); int cLen = (str1Len > str2Len) ? str2Len : str1Len; if (str1Value.extract(str1Len - cLen, cLen) != str2Value.extract(str2Len - cLen, cLen)) { TRACE("str", tout << "Conflict: " << mk_ismt2_pp(concatAst1, mgr) << " has no common suffix with " << mk_ismt2_pp(concatAst2, mgr) << std::endl;); expr_ref toNegate(mgr.mk_not(ctx.mk_eq_atom(concatAst1, concatAst2)), mgr); assert_axiom(toNegate); return; } else { if (str1Len > str2Len) { zstring deltaStr = str1Value.extract(0, str1Len - str2Len); expr_ref x_deltaStr(mk_concat(x, mk_string(deltaStr)), mgr); if (!in_same_eqc(m, x_deltaStr)) { expr_ref implyR(ctx.mk_eq_atom(m, x_deltaStr), mgr); if (m_params.m_StrongArrangements) { expr_ref ax_strong(ctx.mk_eq_atom( ctx.mk_eq_atom(concatAst1, concatAst2), implyR ), mgr); assert_axiom(ax_strong); } else { assert_implication(ctx.mk_eq_atom(concatAst1, concatAst2), implyR); } } } else if (str1Len == str2Len) { // test if (!in_same_eqc(x, m)) { expr_ref implyR(ctx.mk_eq_atom(x, m), mgr); if (m_params.m_StrongArrangements) { expr_ref ax_strong(ctx.mk_eq_atom( ctx.mk_eq_atom(concatAst1, concatAst2), implyR ), mgr); assert_axiom(ax_strong); } else { assert_implication(ctx.mk_eq_atom(concatAst1, concatAst2), implyR); } } } else { zstring deltaStr = str2Value.extract(0, str2Len - str1Len); expr_ref m_deltaStr(mk_concat(m, mk_string(deltaStr)), mgr); if (!in_same_eqc(x, m_deltaStr)) { expr_ref implyR(ctx.mk_eq_atom(x, m_deltaStr), mgr); if (m_params.m_StrongArrangements) { expr_ref ax_strong(ctx.mk_eq_atom( ctx.mk_eq_atom(concatAst1, concatAst2), implyR ), mgr); assert_axiom(ax_strong); } else { assert_implication(ctx.mk_eq_atom(concatAst1, concatAst2), implyR); } } } } } /************************************************************* * case 6: concat("str1", y) = concat(m, "str2") *************************************************************/ bool theory_str::is_concat_eq_type6(expr * concatAst1, expr * concatAst2) { expr * v1_arg0 = to_app(concatAst1)->get_arg(0); expr * v1_arg1 = to_app(concatAst1)->get_arg(1); expr * v2_arg0 = to_app(concatAst2)->get_arg(0); expr * v2_arg1 = to_app(concatAst2)->get_arg(1); if (u.str.is_string(v1_arg0) && (!u.str.is_string(v1_arg1)) && (!u.str.is_string(v2_arg0)) && u.str.is_string(v2_arg1)) { return true; } else if (u.str.is_string(v2_arg0) && (!u.str.is_string(v2_arg1)) && (!u.str.is_string(v1_arg0)) && u.str.is_string(v1_arg1)) { return true; } else { return false; } } void theory_str::process_concat_eq_type6(expr * concatAst1, expr * concatAst2) { ast_manager & mgr = get_manager(); context & ctx = get_context(); TRACE("str", tout << "process_concat_eq TYPE 6" << std::endl << "concatAst1 = " << mk_ismt2_pp(concatAst1, mgr) << std::endl << "concatAst2 = " << mk_ismt2_pp(concatAst2, mgr) << std::endl; ); if (!u.str.is_concat(to_app(concatAst1))) { TRACE("str", tout << "concatAst1 is not a concat function" << std::endl;); return; } if (!u.str.is_concat(to_app(concatAst2))) { TRACE("str", tout << "concatAst2 is not a concat function" << std::endl;); return; } expr * v1_arg0 = to_app(concatAst1)->get_arg(0); expr * v1_arg1 = to_app(concatAst1)->get_arg(1); expr * v2_arg0 = to_app(concatAst2)->get_arg(0); expr * v2_arg1 = to_app(concatAst2)->get_arg(1); expr * str1Ast = nullptr; expr * y = nullptr; expr * m = nullptr; expr * str2Ast = nullptr; if (u.str.is_string(v1_arg0)) { str1Ast = v1_arg0; y = v1_arg1; m = v2_arg0; str2Ast = v2_arg1; } else { str1Ast = v2_arg0; y = v2_arg1; m = v1_arg0; str2Ast = v1_arg1; } zstring str1Value, str2Value; u.str.is_string(str1Ast, str1Value); u.str.is_string(str2Ast, str2Value); unsigned int str1Len = str1Value.length(); unsigned int str2Len = str2Value.length(); //---------------------------------------- //(a) |---str1---|----y----| // |--m--|-----str2-----| // //(b) |---str1---|----y----| // |-----m----|--str2---| // //(c) |---str1---|----y----| // |------m------|-str2-| //---------------------------------------- std::list overlapLen; overlapLen.push_back(0); for (unsigned int i = 1; i <= str1Len && i <= str2Len; i++) { if (str1Value.extract(str1Len - i, i) == str2Value.extract(0, i)) overlapLen.push_back(i); } //---------------------------------------------------------------- expr * commonVar = nullptr; expr * xorFlag = nullptr; std::pair key1(concatAst1, concatAst2); std::pair key2(concatAst2, concatAst1); // check the entries in this map to make sure they're still in scope // before we use them. std::map, std::map >::iterator entry1 = varForBreakConcat.find(key1); std::map, std::map >::iterator entry2 = varForBreakConcat.find(key2); bool entry1InScope; if (entry1 == varForBreakConcat.end()) { entry1InScope = false; } else { if (internal_variable_set.find((entry1->second)[0]) == internal_variable_set.end() /* || internal_variable_set.find((entry1->second)[1]) == internal_variable_set.end() */) { entry1InScope = false; } else { entry1InScope = true; } } bool entry2InScope; if (entry2 == varForBreakConcat.end()) { entry2InScope = false; } else { if (internal_variable_set.find((entry2->second)[0]) == internal_variable_set.end() /* || internal_variable_set.find((entry2->second)[1]) == internal_variable_set.end() */) { entry2InScope = false; } else { entry2InScope = true; } } TRACE("str", tout << "entry 1 " << (entry1InScope ? "in scope" : "not in scope") << std::endl << "entry 2 " << (entry2InScope ? "in scope" : "not in scope") << std::endl;); if (!entry1InScope && !entry2InScope) { commonVar = mk_nonempty_str_var(); xorFlag = mk_internal_xor_var(); varForBreakConcat[key1][0] = commonVar; varForBreakConcat[key1][1] = xorFlag; } else { if (entry1InScope) { commonVar = (entry1->second)[0]; xorFlag = (entry1->second)[1]; } else { commonVar = (entry2->second)[0]; xorFlag = (entry2->second)[1]; } refresh_theory_var(commonVar); add_nonempty_constraint(commonVar); } bool overlapAssumptionUsed = false; expr_ref_vector arrangement_disjunction(mgr); int pos = 1; if (!avoidLoopCut || !has_self_cut(m, y)) { expr_ref_vector and_item(mgr); expr_ref str1_commonVar(mk_concat(str1Ast, commonVar), mgr); and_item.push_back(ctx.mk_eq_atom(m, str1_commonVar)); pos += 1; expr_ref commonVar_str2(mk_concat(commonVar, str2Ast), mgr); and_item.push_back(ctx.mk_eq_atom(y, commonVar_str2)); pos += 1; and_item.push_back(ctx.mk_eq_atom(mk_strlen(m), m_autil.mk_add(mk_strlen(str1Ast), mk_strlen(commonVar)) )); pos += 1; // addItems[0] = mk_length(t, commonVar); // addItems[1] = mk_length(t, str2Ast); // and_item[pos++] = Z3_mk_eq(ctx, or_item[option], Z3_mk_eq(ctx, mk_length(t, y), Z3_mk_add(ctx, 2, addItems))); expr_ref option1(mk_and(and_item), mgr); arrangement_disjunction.push_back(option1); add_theory_aware_branching_info(option1, 0.1, l_true); } else { loopDetected = true; if (m_params.m_FiniteOverlapModels) { expr_ref tester = set_up_finite_model_test(concatAst1, concatAst2); arrangement_disjunction.push_back(tester); add_theory_aware_branching_info(tester, m_params.m_OverlapTheoryAwarePriority, l_true); } else { TRACE("str", tout << "AVOID LOOP: SKIPPED." << std::endl;); TRACE("str", print_cut_var(m, tout); print_cut_var(y, tout);); // only add the overlap assumption one time if (!overlapAssumptionUsed) { arrangement_disjunction.push_back(m_theoryStrOverlapAssumption_term); overlapAssumptionUsed = true; } } } for (unsigned int overLen : overlapLen) { zstring prefix = str1Value.extract(0, str1Len - overLen); zstring suffix = str2Value.extract(overLen, str2Len - overLen); expr_ref_vector and_item(mgr); expr_ref prefixAst(mk_string(prefix), mgr); expr_ref x_eq_prefix(ctx.mk_eq_atom(m, prefixAst), mgr); and_item.push_back(x_eq_prefix); pos += 1; and_item.push_back( ctx.mk_eq_atom(mk_strlen(m), mk_strlen(prefixAst))); pos += 1; // adding length constraint for _ = constStr seems slowing things down. expr_ref suffixAst(mk_string(suffix), mgr); expr_ref y_eq_suffix(ctx.mk_eq_atom(y, suffixAst), mgr); and_item.push_back(y_eq_suffix); pos += 1; and_item.push_back(ctx.mk_eq_atom(mk_strlen(y), mk_strlen(suffixAst))); pos += 1; expr_ref option2(mk_and(and_item), mgr); arrangement_disjunction.push_back(option2); double priority; // prefer the option "str1" = x if (prefix == str1Value) { priority = 0.5; } else { priority = 0.1; } add_theory_aware_branching_info(option2, priority, l_true); } // case 6: concat("str1", y) = concat(m, "str2") expr_ref implyR(mk_or(arrangement_disjunction), mgr); if (m_params.m_StrongArrangements) { expr_ref ax_strong(ctx.mk_eq_atom( ctx.mk_eq_atom(concatAst1, concatAst2), implyR ), mgr); assert_axiom(ax_strong); } else { assert_implication(ctx.mk_eq_atom(concatAst1, concatAst2), implyR); } generate_mutual_exclusion(arrangement_disjunction); } void theory_str::process_unroll_eq_const_str(expr * unrollFunc, expr * constStr) { if (!u.re.is_unroll(to_app(unrollFunc))) { return; } if (!u.str.is_string(constStr)) { return; } expr * funcInUnroll = to_app(unrollFunc)->get_arg(0); zstring strValue; u.str.is_string(constStr, strValue); TRACE("str", tout << "unrollFunc: " << mk_pp(unrollFunc, get_manager()) << std::endl << "constStr: " << mk_pp(constStr, get_manager()) << std::endl;); if (strValue == "") { return; } if (u.re.is_to_re(to_app(funcInUnroll))) { unroll_str2reg_constStr(unrollFunc, constStr); return; } } void theory_str::process_concat_eq_unroll(expr * concat, expr * unroll) { context & ctx = get_context(); ast_manager & mgr = get_manager(); TRACE("str", tout << "concat = " << mk_pp(concat, mgr) << ", unroll = " << mk_pp(unroll, mgr) << std::endl;); expr_ref toAssert(mgr); expr * _toAssert; if (!concat_eq_unroll_ast_map.find(concat, unroll, _toAssert)) { expr_ref arg1(to_app(concat)->get_arg(0), mgr); expr_ref arg2(to_app(concat)->get_arg(1), mgr); expr_ref r1(to_app(unroll)->get_arg(0), mgr); expr_ref t1(to_app(unroll)->get_arg(1), mgr); expr_ref v1(mk_regex_rep_var(), mgr); expr_ref v2(mk_regex_rep_var(), mgr); expr_ref v3(mk_regex_rep_var(), mgr); expr_ref v4(mk_regex_rep_var(), mgr); expr_ref v5(mk_regex_rep_var(), mgr); expr_ref t2(mk_unroll_bound_var(), mgr); expr_ref t3(mk_unroll_bound_var(), mgr); expr_ref emptyStr(mk_string(""), mgr); expr_ref unroll1(mk_unroll(r1, t2), mgr); expr_ref unroll2(mk_unroll(r1, t3), mgr); expr_ref op0(ctx.mk_eq_atom(t1, mk_int(0)), mgr); expr_ref op1(m_autil.mk_ge(t1, mk_int(1)), mgr); expr_ref_vector op1Items(mgr); expr_ref_vector op2Items(mgr); op1Items.push_back(ctx.mk_eq_atom(arg1, emptyStr)); op1Items.push_back(ctx.mk_eq_atom(arg2, emptyStr)); op1Items.push_back(ctx.mk_eq_atom(mk_strlen(arg1), mk_int(0))); op1Items.push_back(ctx.mk_eq_atom(mk_strlen(arg2), mk_int(0))); expr_ref opAnd1(ctx.mk_eq_atom(op0, mk_and(op1Items)), mgr); expr_ref v1v2(mk_concat(v1, v2), mgr); op2Items.push_back(ctx.mk_eq_atom(arg1, v1v2)); op2Items.push_back(ctx.mk_eq_atom(mk_strlen(arg1), m_autil.mk_add(mk_strlen(v1), mk_strlen(v2)))); expr_ref v3v4(mk_concat(v3, v4), mgr); op2Items.push_back(ctx.mk_eq_atom(arg2, v3v4)); op2Items.push_back(ctx.mk_eq_atom(mk_strlen(arg2), m_autil.mk_add(mk_strlen(v3), mk_strlen(v4)))); op2Items.push_back(ctx.mk_eq_atom(v1, unroll1)); op2Items.push_back(ctx.mk_eq_atom(mk_strlen(v1), mk_strlen(unroll1))); op2Items.push_back(ctx.mk_eq_atom(v4, unroll2)); op2Items.push_back(ctx.mk_eq_atom(mk_strlen(v4), mk_strlen(unroll2))); expr_ref v2v3(mk_concat(v2, v3), mgr); op2Items.push_back(ctx.mk_eq_atom(v5, v2v3)); reduce_virtual_regex_in(v5, r1, op2Items); op2Items.push_back(ctx.mk_eq_atom(mk_strlen(v5), m_autil.mk_add(mk_strlen(v2), mk_strlen(v3)))); op2Items.push_back(ctx.mk_eq_atom(m_autil.mk_add(t2, t3), m_autil.mk_add(t1, mk_int(-1)))); expr_ref opAnd2(ctx.mk_eq_atom(op1, mk_and(op2Items)), mgr); toAssert = mgr.mk_and(opAnd1, opAnd2); m_trail.push_back(toAssert); concat_eq_unroll_ast_map.insert(concat, unroll, toAssert); } else { toAssert = _toAssert; } assert_axiom(toAssert); } void theory_str::unroll_str2reg_constStr(expr * unrollFunc, expr * eqConstStr) { context & ctx = get_context(); ast_manager & m = get_manager(); expr * str2RegFunc = to_app(unrollFunc)->get_arg(0); expr * strInStr2RegFunc = to_app(str2RegFunc)->get_arg(0); expr * oriCnt = to_app(unrollFunc)->get_arg(1); zstring strValue; u.str.is_string(eqConstStr, strValue); zstring regStrValue; u.str.is_string(strInStr2RegFunc, regStrValue); unsigned int strLen = strValue.length(); unsigned int regStrLen = regStrValue.length(); SASSERT(regStrLen != 0); // this should never occur -- the case for empty string is handled elsewhere unsigned int cnt = strLen / regStrLen; expr_ref implyL(ctx.mk_eq_atom(unrollFunc, eqConstStr), m); expr_ref implyR1(ctx.mk_eq_atom(oriCnt, mk_int(cnt)), m); expr_ref implyR2(ctx.mk_eq_atom(mk_strlen(unrollFunc), mk_int(strLen)), m); expr_ref axiomRHS(m.mk_and(implyR1, implyR2), m); SASSERT(implyL); SASSERT(axiomRHS); assert_implication(implyL, axiomRHS); } /* * Look through the equivalence class of n to find a string constant. * Return that constant if it is found, and set hasEqcValue to true. * Otherwise, return n, and set hasEqcValue to false. */ expr * theory_str::get_eqc_value(expr * n, bool & hasEqcValue) { return z3str2_get_eqc_value(n, hasEqcValue); } // Simulate the behaviour of get_eqc_value() from Z3str2. // We only check m_find for a string constant. expr * theory_str::z3str2_get_eqc_value(expr * n , bool & hasEqcValue) { theory_var curr = get_var(n); if (curr != null_theory_var) { curr = m_find.find(curr); theory_var first = curr; do { expr* a = get_ast(curr); if (u.str.is_string(a)) { hasEqcValue = true; return a; } curr = m_find.next(curr); } while (curr != first && curr != null_theory_var); } hasEqcValue = false; return n; } 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; } // check root of the eqc for an integer constant // if an integer constant exists in the eqc, it should be the root enode * en_e = ctx.get_enode(e); enode * root_e = en_e->get_root(); if (m_autil.is_numeral(root_e->get_owner(), val) && val.is_int()) { TRACE("str", tout << mk_pp(e, m) << " ~= " << mk_pp(root_e->get_owner(), m) << std::endl;); return true; } else { TRACE("str", tout << "root of eqc of " << mk_pp(e, m) << " is not a numeral" << std::endl;); return false; } } bool theory_str::lower_bound(expr* _e, rational& lo) { if (opt_DisableIntegerTheoryIntegration) { TRACE("str", tout << "WARNING: integer theory integration disabled" << std::endl;); return false; } arith_value v(get_manager()); v.init(&get_context()); bool strict; return v.get_lo_equiv(_e, lo, strict); } bool theory_str::upper_bound(expr* _e, rational& hi) { if (opt_DisableIntegerTheoryIntegration) { TRACE("str", tout << "WARNING: integer theory integration disabled" << std::endl;); return false; } arith_value v(get_manager()); v.init(&get_context()); bool strict; return v.get_up_equiv(_e, hi, strict); } bool theory_str::get_len_value(expr* e, rational& val) { if (opt_DisableIntegerTheoryIntegration) { TRACE("str", tout << "WARNING: integer theory integration disabled" << std::endl;); return false; } context& ctx = get_context(); ast_manager & m = get_manager(); TRACE("str", tout << "checking len value of " << mk_ismt2_pp(e, m) << std::endl;); rational val1; expr_ref len(m), len_val(m); expr* e1, *e2; ptr_vector todo; todo.push_back(e); val.reset(); while (!todo.empty()) { expr* c = todo.back(); todo.pop_back(); if (u.str.is_concat(to_app(c))) { e1 = to_app(c)->get_arg(0); e2 = to_app(c)->get_arg(1); todo.push_back(e1); todo.push_back(e2); } else if (u.str.is_string(to_app(c))) { zstring tmp; u.str.is_string(to_app(c), tmp); unsigned int sl = tmp.length(); val += rational(sl); } else { len = mk_strlen(c); // debugging TRACE("str", { tout << mk_pp(len, m) << ":" << std::endl << (ctx.is_relevant(len.get()) ? "relevant" : "not relevant") << std::endl << (ctx.e_internalized(len) ? "internalized" : "not internalized") << std::endl ; if (ctx.e_internalized(len)) { enode * e_len = ctx.get_enode(len); tout << "has " << e_len->get_num_th_vars() << " theory vars" << std::endl; // eqc debugging { tout << "dump equivalence class of " << mk_pp(len, get_manager()) << std::endl; enode * nNode = ctx.get_enode(len); enode * eqcNode = nNode; do { app * ast = eqcNode->get_owner(); tout << mk_pp(ast, get_manager()) << std::endl; eqcNode = eqcNode->get_next(); } while (eqcNode != nNode); } } }); if (ctx.e_internalized(len) && get_arith_value(len, val1)) { val += val1; TRACE("str", tout << "integer theory: subexpression " << mk_ismt2_pp(len, m) << " has length " << val1 << std::endl;); } else { TRACE("str", tout << "integer theory: subexpression " << mk_ismt2_pp(len, m) << " has no length assignment; bailing out" << std::endl;); return false; } } } TRACE("str", tout << "length of " << mk_ismt2_pp(e, m) << " is " << val << std::endl;); return val.is_int(); } /* * Decide whether n1 and n2 are already in the same equivalence class. * This only checks whether the core considers them to be equal; * they may not actually be equal. */ bool theory_str::in_same_eqc(expr * n1, expr * n2) { if (n1 == n2) return true; context & ctx = get_context(); // similar to get_eqc_value(), make absolutely sure // that we've set this up properly for the context if (!ctx.e_internalized(n1)) { TRACE("str", tout << "WARNING: expression " << mk_ismt2_pp(n1, get_manager()) << " was not internalized" << std::endl;); ctx.internalize(n1, false); } if (!ctx.e_internalized(n2)) { TRACE("str", tout << "WARNING: expression " << mk_ismt2_pp(n2, get_manager()) << " was not internalized" << std::endl;); ctx.internalize(n2, false); } expr * curr = get_eqc_next(n1); while (curr != n1) { if (curr == n2) return true; curr = get_eqc_next(curr); } return false; } expr * theory_str::collect_eq_nodes(expr * n, expr_ref_vector & eqcSet) { expr * constStrNode = nullptr; expr * ex = n; do { if (u.str.is_string(to_app(ex))) { constStrNode = ex; } eqcSet.push_back(ex); ex = get_eqc_next(ex); } while (ex != n); return constStrNode; } /* * Collect constant strings (from left to right) in an AST node. */ void theory_str::get_const_str_asts_in_node(expr * node, expr_ref_vector & astList) { if (u.str.is_string(node)) { astList.push_back(node); //} else if (getNodeType(t, node) == my_Z3_Func) { } else if (is_app(node)) { app * func_app = to_app(node); unsigned int argCount = func_app->get_num_args(); for (unsigned int i = 0; i < argCount; i++) { expr * argAst = func_app->get_arg(i); get_const_str_asts_in_node(argAst, astList); } } } void theory_str::check_contain_by_eqc_val(expr * varNode, expr * constNode) { context & ctx = get_context(); ast_manager & m = get_manager(); TRACE("str", tout << "varNode = " << mk_pp(varNode, m) << ", constNode = " << mk_pp(constNode, m) << std::endl;); expr_ref_vector litems(m); if (contain_pair_idx_map.contains(varNode)) { for (auto entry : contain_pair_idx_map[varNode]) { expr * strAst = entry.first; expr * substrAst = entry.second; expr * boolVar = nullptr; if (!contain_pair_bool_map.find(strAst, substrAst, boolVar)) { TRACE("str", tout << "warning: no entry for boolVar in contain_pair_bool_map" << std::endl;); } // we only want to inspect the Contains terms where either of strAst or substrAst // are equal to varNode. TRACE("t_str_detail", tout << "considering Contains with strAst = " << mk_pp(strAst, m) << ", substrAst = " << mk_pp(substrAst, m) << "..." << std::endl;); if (varNode != strAst && varNode != substrAst) { TRACE("str", tout << "varNode not equal to strAst or substrAst, skip" << std::endl;); continue; } TRACE("str", tout << "varNode matched one of strAst or substrAst. Continuing" << std::endl;); // varEqcNode is str if (strAst == varNode) { expr_ref implyR(m); litems.reset(); if (strAst != constNode) { litems.push_back(ctx.mk_eq_atom(strAst, constNode)); } zstring strConst; u.str.is_string(constNode, strConst); bool subStrHasEqcValue = false; expr * substrValue = get_eqc_value(substrAst, subStrHasEqcValue); if (substrValue != substrAst) { litems.push_back(ctx.mk_eq_atom(substrAst, substrValue)); } if (subStrHasEqcValue) { // subStr has an eqc constant value zstring subStrConst; u.str.is_string(substrValue, subStrConst); TRACE("t_str_detail", tout << "strConst = " << strConst << ", subStrConst = " << subStrConst << "\n";); if (strConst.contains(subStrConst)) { //implyR = ctx.mk_eq(ctx, boolVar, Z3_mk_true(ctx)); implyR = boolVar; } else { //implyR = Z3_mk_eq(ctx, boolVar, Z3_mk_false(ctx)); implyR = mk_not(m, boolVar); } } else { // ------------------------------------------------------------------------------------------------ // subStr doesn't have an eqc constant value // however, subStr equals to some concat(arg_1, arg_2, ..., arg_n) // if arg_j is a constant and is not a part of the strConst, it's sure that the contains is false // ** This check is needed here because the "strConst" and "strAst" may not be in a same eqc yet // ------------------------------------------------------------------------------------------------ // collect eqc concat std::set eqcConcats; get_concats_in_eqc(substrAst, eqcConcats); for (expr * aConcat : eqcConcats) { expr_ref_vector constList(m); bool counterEgFound = false; get_const_str_asts_in_node(aConcat, constList); for (auto const& cst : constList) { zstring pieceStr; u.str.is_string(cst, pieceStr); if (!strConst.contains(pieceStr)) { counterEgFound = true; if (aConcat != substrAst) { litems.push_back(ctx.mk_eq_atom(substrAst, aConcat)); } implyR = mk_not(m, boolVar); break; } } if (counterEgFound) { TRACE("str", tout << "Inconsistency found!" << std::endl;); break; } } } // add assertion if (implyR) { expr_ref implyLHS(mk_and(litems), m); assert_implication(implyLHS, implyR); } } // varEqcNode is subStr else if (substrAst == varNode) { expr_ref implyR(m); litems.reset(); if (substrAst != constNode) { litems.push_back(ctx.mk_eq_atom(substrAst, constNode)); } bool strHasEqcValue = false; expr * strValue = get_eqc_value(strAst, strHasEqcValue); if (strValue != strAst) { litems.push_back(ctx.mk_eq_atom(strAst, strValue)); } if (strHasEqcValue) { zstring strConst, subStrConst; u.str.is_string(strValue, strConst); u.str.is_string(constNode, subStrConst); if (strConst.contains(subStrConst)) { //implyR = Z3_mk_eq(ctx, boolVar, Z3_mk_true(ctx)); implyR = boolVar; } else { // implyR = Z3_mk_eq(ctx, boolVar, Z3_mk_false(ctx)); implyR = mk_not(m, boolVar); } } // add assertion if (implyR) { expr_ref implyLHS(mk_and(litems), m); assert_implication(implyLHS, implyR); } } } // for (itor1 : contains_map) } // if varNode in contain_pair_idx_map } void theory_str::check_contain_by_substr(expr * varNode, expr_ref_vector & willEqClass) { context & ctx = get_context(); ast_manager & m = get_manager(); expr_ref_vector litems(m); if (contain_pair_idx_map.contains(varNode)) { for (auto entry : contain_pair_idx_map[varNode]) { expr * strAst = entry.first; expr * substrAst = entry.second; expr * boolVar = nullptr; if (!contain_pair_bool_map.find(strAst, substrAst, boolVar)) { TRACE("str", tout << "warning: no entry for boolVar in contain_pair_bool_map" << std::endl;); } // we only want to inspect the Contains terms where either of strAst or substrAst // are equal to varNode. TRACE("t_str_detail", tout << "considering Contains with strAst = " << mk_pp(strAst, m) << ", substrAst = " << mk_pp(substrAst, m) << "..." << std::endl;); if (varNode != strAst && varNode != substrAst) { TRACE("str", tout << "varNode not equal to strAst or substrAst, skip" << std::endl;); continue; } TRACE("str", tout << "varNode matched one of strAst or substrAst. Continuing" << std::endl;); if (substrAst == varNode) { bool strAstHasVal = false; expr * strValue = get_eqc_value(strAst, strAstHasVal); if (strAstHasVal) { TRACE("str", tout << mk_pp(strAst, m) << " has constant eqc value " << mk_pp(strValue, m) << std::endl;); if (strValue != strAst) { litems.push_back(ctx.mk_eq_atom(strAst, strValue)); } zstring strConst; u.str.is_string(strValue, strConst); // iterate eqc (also eqc-to-be) of substr for (auto itAst : willEqClass) { bool counterEgFound = false; if (u.str.is_concat(to_app(itAst))) { expr_ref_vector constList(m); // get constant strings in concat app * aConcat = to_app(itAst); get_const_str_asts_in_node(aConcat, constList); for (auto cst : constList) { zstring pieceStr; u.str.is_string(cst, pieceStr); if (!strConst.contains(pieceStr)) { TRACE("str", tout << "Inconsistency found!" << std::endl;); counterEgFound = true; if (aConcat != substrAst) { litems.push_back(ctx.mk_eq_atom(substrAst, aConcat)); } expr_ref implyLHS(mk_and(litems), m); expr_ref implyR(mk_not(m, boolVar), m); assert_implication(implyLHS, implyR); break; } } } if (counterEgFound) { break; } } } } } } // varNode in contain_pair_idx_map } bool theory_str::in_contain_idx_map(expr * n) { return contain_pair_idx_map.contains(n); } void theory_str::check_contain_by_eq_nodes(expr * n1, expr * n2) { context & ctx = get_context(); ast_manager & m = get_manager(); if (in_contain_idx_map(n1) && in_contain_idx_map(n2)) { for (auto const& key1 : contain_pair_idx_map[n1]) { // keysItor1 is on set {<.., n1>, ..., , ...} //std::pair key1 = *keysItor1; if (key1.first == n1 && key1.second == n2) { expr_ref implyL(m); expr_ref implyR(contain_pair_bool_map[key1], m); if (n1 != n2) { implyL = ctx.mk_eq_atom(n1, n2); assert_implication(implyL, implyR); } else { assert_axiom(implyR); } } //for (keysItor2 = contain_pair_idx_map[n2].begin(); keysItor2 != contain_pair_idx_map[n2].end(); keysItor2++) { for (auto const& key2 : contain_pair_idx_map[n2]) { // keysItor2 is on set {<.., n2>, ..., , ...} //std::pair key2 = *keysItor2; // skip if the pair is eq if (key1 == key2) { continue; } // *************************** // Case 1: Contains(m, ...) /\ Contains(n, ) /\ m = n // *************************** if (key1.first == n1 && key2.first == n2) { expr * subAst1 = key1.second; expr * subAst2 = key2.second; bool subAst1HasValue = false; bool subAst2HasValue = false; expr * subValue1 = get_eqc_value(subAst1, subAst1HasValue); expr * subValue2 = get_eqc_value(subAst2, subAst2HasValue); TRACE("str", tout << "(Contains " << mk_pp(n1, m) << " " << mk_pp(subAst1, m) << ")" << std::endl; tout << "(Contains " << mk_pp(n2, m) << " " << mk_pp(subAst2, m) << ")" << std::endl; if (subAst1 != subValue1) { tout << mk_pp(subAst1, m) << " = " << mk_pp(subValue1, m) << std::endl; } if (subAst2 != subValue2) { tout << mk_pp(subAst2, m) << " = " << mk_pp(subValue2, m) << std::endl; } ); if (subAst1HasValue && subAst2HasValue) { expr_ref_vector litems1(m); if (n1 != n2) { litems1.push_back(ctx.mk_eq_atom(n1, n2)); } if (subValue1 != subAst1) { litems1.push_back(ctx.mk_eq_atom(subAst1, subValue1)); } if (subValue2 != subAst2) { litems1.push_back(ctx.mk_eq_atom(subAst2, subValue2)); } zstring subConst1, subConst2; u.str.is_string(subValue1, subConst1); u.str.is_string(subValue2, subConst2); expr_ref implyR(m); if (subConst1 == subConst2) { // key1.first = key2.first /\ key1.second = key2.second // ==> (containPairBoolMap[key1] = containPairBoolMap[key2]) implyR = ctx.mk_eq_atom(contain_pair_bool_map[key1], contain_pair_bool_map[key2]); } else if (subConst1.contains(subConst2)) { // key1.first = key2.first /\ Contains(key1.second, key2.second) // ==> (containPairBoolMap[key1] --> containPairBoolMap[key2]) implyR = rewrite_implication(contain_pair_bool_map[key1], contain_pair_bool_map[key2]); } else if (subConst2.contains(subConst1)) { // key1.first = key2.first /\ Contains(key2.second, key1.second) // ==> (containPairBoolMap[key2] --> containPairBoolMap[key1]) implyR = rewrite_implication(contain_pair_bool_map[key2], contain_pair_bool_map[key1]); } if (implyR) { if (litems1.empty()) { assert_axiom(implyR); } else { assert_implication(mk_and(litems1), implyR); } } } else { expr_ref_vector subAst1Eqc(m); expr_ref_vector subAst2Eqc(m); collect_eq_nodes(subAst1, subAst1Eqc); collect_eq_nodes(subAst2, subAst2Eqc); if (subAst1Eqc.contains(subAst2)) { // ----------------------------------------------------------- // * key1.first = key2.first /\ key1.second = key2.second // --> containPairBoolMap[key1] = containPairBoolMap[key2] // ----------------------------------------------------------- expr_ref_vector litems2(m); if (n1 != n2) { litems2.push_back(ctx.mk_eq_atom(n1, n2)); } if (subAst1 != subAst2) { litems2.push_back(ctx.mk_eq_atom(subAst1, subAst2)); } expr_ref implyR(ctx.mk_eq_atom(contain_pair_bool_map[key1], contain_pair_bool_map[key2]), m); if (litems2.empty()) { assert_axiom(implyR); } else { assert_implication(mk_and(litems2), implyR); } } else { // ----------------------------------------------------------- // * key1.first = key2.first // check eqc(key1.second) and eqc(key2.second) // ----------------------------------------------------------- //expr_ref_vector::iterator eqItorSub1 = subAst1Eqc.begin(); //for (; eqItorSub1 != subAst1Eqc.end(); eqItorSub1++) { for (auto eqSubVar1 : subAst1Eqc) { //expr_ref_vector::iterator eqItorSub2 = subAst2Eqc.begin(); //for (; eqItorSub2 != subAst2Eqc.end(); eqItorSub2++) { for (auto eqSubVar2 : subAst2Eqc) { // ------------ // key1.first = key2.first /\ containPairBoolMap[] // ==> (containPairBoolMap[key1] --> containPairBoolMap[key2]) // ------------ { expr_ref_vector litems3(m); if (n1 != n2) { litems3.push_back(ctx.mk_eq_atom(n1, n2)); } if (eqSubVar1 != subAst1) { litems3.push_back(ctx.mk_eq_atom(subAst1, eqSubVar1)); } if (eqSubVar2 != subAst2) { litems3.push_back(ctx.mk_eq_atom(subAst2, eqSubVar2)); } std::pair tryKey1 = std::make_pair(eqSubVar1, eqSubVar2); if (contain_pair_bool_map.contains(tryKey1)) { TRACE("str", tout << "(Contains " << mk_pp(eqSubVar1, m) << " " << mk_pp(eqSubVar2, m) << ")" << std::endl;); litems3.push_back(contain_pair_bool_map[tryKey1]); expr_ref implR(rewrite_implication(contain_pair_bool_map[key1], contain_pair_bool_map[key2]), m); assert_implication(mk_and(litems3), implR); } } // ------------ // key1.first = key2.first /\ containPairBoolMap[] // ==> (containPairBoolMap[key2] --> containPairBoolMap[key1]) // ------------ { expr_ref_vector litems4(m); if (n1 != n2) { litems4.push_back(ctx.mk_eq_atom(n1, n2)); } if (eqSubVar1 != subAst1) { litems4.push_back(ctx.mk_eq_atom(subAst1, eqSubVar1)); } if (eqSubVar2 != subAst2) { litems4.push_back(ctx.mk_eq_atom(subAst2, eqSubVar2)); } std::pair tryKey2 = std::make_pair(eqSubVar2, eqSubVar1); if (contain_pair_bool_map.contains(tryKey2)) { TRACE("str", tout << "(Contains " << mk_pp(eqSubVar2, m) << " " << mk_pp(eqSubVar1, m) << ")" << std::endl;); litems4.push_back(contain_pair_bool_map[tryKey2]); expr_ref implR(rewrite_implication(contain_pair_bool_map[key2], contain_pair_bool_map[key1]), m); assert_implication(mk_and(litems4), implR); } } } } } } } // *************************** // Case 2: Contains(..., m) /\ Contains(... , n) /\ m = n // *************************** else if (key1.second == n1 && key2.second == n2) { expr * str1 = key1.first; expr * str2 = key2.first; bool str1HasValue = false; bool str2HasValue = false; expr * strVal1 = get_eqc_value(str1, str1HasValue); expr * strVal2 = get_eqc_value(str2, str2HasValue); TRACE("str", tout << "(Contains " << mk_pp(str1, m) << " " << mk_pp(n1, m) << ")" << std::endl; tout << "(Contains " << mk_pp(str2, m) << " " << mk_pp(n2, m) << ")" << std::endl; if (str1 != strVal1) { tout << mk_pp(str1, m) << " = " << mk_pp(strVal1, m) << std::endl; } if (str2 != strVal2) { tout << mk_pp(str2, m) << " = " << mk_pp(strVal2, m) << std::endl; } ); if (str1HasValue && str2HasValue) { expr_ref_vector litems1(m); if (n1 != n2) { litems1.push_back(ctx.mk_eq_atom(n1, n2)); } if (strVal1 != str1) { litems1.push_back(ctx.mk_eq_atom(str1, strVal1)); } if (strVal2 != str2) { litems1.push_back(ctx.mk_eq_atom(str2, strVal2)); } zstring const1, const2; u.str.is_string(strVal1, const1); u.str.is_string(strVal2, const2); expr_ref implyR(m); if (const1 == const2) { // key1.second = key2.second /\ key1.first = key2.first // ==> (containPairBoolMap[key1] = containPairBoolMap[key2]) implyR = ctx.mk_eq_atom(contain_pair_bool_map[key1], contain_pair_bool_map[key2]); } else if (const1.contains(const2)) { // key1.second = key2.second /\ Contains(key1.first, key2.first) // ==> (containPairBoolMap[key2] --> containPairBoolMap[key1]) implyR = rewrite_implication(contain_pair_bool_map[key2], contain_pair_bool_map[key1]); } else if (const2.contains(const1)) { // key1.first = key2.first /\ Contains(key2.first, key1.first) // ==> (containPairBoolMap[key1] --> containPairBoolMap[key2]) implyR = rewrite_implication(contain_pair_bool_map[key1], contain_pair_bool_map[key2]); } if (implyR) { if (litems1.empty()) { assert_axiom(implyR); } else { assert_implication(mk_and(litems1), implyR); } } } else { expr_ref_vector str1Eqc(m); expr_ref_vector str2Eqc(m); collect_eq_nodes(str1, str1Eqc); collect_eq_nodes(str2, str2Eqc); if (str1Eqc.contains(str2)) { // ----------------------------------------------------------- // * key1.first = key2.first /\ key1.second = key2.second // --> containPairBoolMap[key1] = containPairBoolMap[key2] // ----------------------------------------------------------- expr_ref_vector litems2(m); if (n1 != n2) { litems2.push_back(ctx.mk_eq_atom(n1, n2)); } if (str1 != str2) { litems2.push_back(ctx.mk_eq_atom(str1, str2)); } expr_ref implyR(ctx.mk_eq_atom(contain_pair_bool_map[key1], contain_pair_bool_map[key2]), m); if (litems2.empty()) { assert_axiom(implyR); } else { assert_implication(mk_and(litems2), implyR); } } else { // ----------------------------------------------------------- // * key1.second = key2.second // check eqc(key1.first) and eqc(key2.first) // ----------------------------------------------------------- for (auto const& eqStrVar1 : str1Eqc) { for (auto const& eqStrVar2 : str2Eqc) { { expr_ref_vector litems3(m); if (n1 != n2) { litems3.push_back(ctx.mk_eq_atom(n1, n2)); } if (eqStrVar1 != str1) { litems3.push_back(ctx.mk_eq_atom(str1, eqStrVar1)); } if (eqStrVar2 != str2) { litems3.push_back(ctx.mk_eq_atom(str2, eqStrVar2)); } std::pair tryKey1 = std::make_pair(eqStrVar1, eqStrVar2); if (contain_pair_bool_map.contains(tryKey1)) { TRACE("str", tout << "(Contains " << mk_pp(eqStrVar1, m) << " " << mk_pp(eqStrVar2, m) << ")" << std::endl;); litems3.push_back(contain_pair_bool_map[tryKey1]); // ------------ // key1.second = key2.second /\ containPairBoolMap[] // ==> (containPairBoolMap[key2] --> containPairBoolMap[key1]) // ------------ expr_ref implR(rewrite_implication(contain_pair_bool_map[key2], contain_pair_bool_map[key1]), m); assert_implication(mk_and(litems3), implR); } } { expr_ref_vector litems4(m); if (n1 != n2) { litems4.push_back(ctx.mk_eq_atom(n1, n2)); } if (eqStrVar1 != str1) { litems4.push_back(ctx.mk_eq_atom(str1, eqStrVar1)); } if (eqStrVar2 != str2) { litems4.push_back(ctx.mk_eq_atom(str2, eqStrVar2)); } std::pair tryKey2 = std::make_pair(eqStrVar2, eqStrVar1); if (contain_pair_bool_map.contains(tryKey2)) { TRACE("str", tout << "(Contains " << mk_pp(eqStrVar2, m) << " " << mk_pp(eqStrVar1, m) << ")" << std::endl;); litems4.push_back(contain_pair_bool_map[tryKey2]); // ------------ // key1.first = key2.first /\ containPairBoolMap[] // ==> (containPairBoolMap[key1] --> containPairBoolMap[key2]) // ------------ expr_ref implR(rewrite_implication(contain_pair_bool_map[key1], contain_pair_bool_map[key2]), m); assert_implication(mk_and(litems4), implR); } } } } } } } } if (n1 == n2) { break; } } } // (in_contain_idx_map(n1) && in_contain_idx_map(n2)) } void theory_str::check_contain_in_new_eq(expr * n1, expr * n2) { if (contains_map.empty()) { return; } ast_manager & m = get_manager(); TRACE("str", tout << "consistency check for contains wrt. " << mk_pp(n1, m) << " and " << mk_pp(n2, m) << std::endl;); expr_ref_vector willEqClass(m); expr * constStrAst_1 = collect_eq_nodes(n1, willEqClass); expr * constStrAst_2 = collect_eq_nodes(n2, willEqClass); expr * constStrAst = (constStrAst_1 != nullptr) ? constStrAst_1 : constStrAst_2; TRACE("str", tout << "eqc of n1 is {"; for (expr * el : willEqClass) { tout << " " << mk_pp(el, m); } tout << std::endl; if (constStrAst == nullptr) { tout << "constStrAst = NULL" << std::endl; } else { tout << "constStrAst = " << mk_pp(constStrAst, m) << std::endl; } ); // step 1: we may have constant values for Contains checks now if (constStrAst != nullptr) { for (auto a : willEqClass) { if (a == constStrAst) { continue; } check_contain_by_eqc_val(a, constStrAst); } } else { // no concrete value to be put in eqc, solely based on context // Check here is used to detected the facts as follows: // * known: contains(Z, Y) /\ Z = "abcdefg" /\ Y = M // * new fact: M = concat(..., "jio", ...) // Note that in this branch, either M or concat(..., "jio", ...) has a constant value // So, only need to check // * "EQC(M) U EQC(concat(..., "jio", ...))" as substr and // * If strAst registered has an eqc constant in the context // ------------------------------------------------------------- for (auto a : willEqClass) { check_contain_by_substr(a, willEqClass); } } // ------------------------------------------ // step 2: check for b1 = contains(x, m), b2 = contains(y, n) // (1) x = y /\ m = n ==> b1 = b2 // (2) x = y /\ Contains(const(m), const(n)) ==> (b1 -> b2) // (3) x = y /\ Contains(const(n), const(m)) ==> (b2 -> b1) // (4) x = y /\ containPairBoolMap[] ==> (b1 -> b2) // (5) x = y /\ containPairBoolMap[] ==> (b2 -> b1) // (6) Contains(const(x), const(y)) /\ m = n ==> (b2 -> b1) // (7) Contains(const(y), const(x)) /\ m = n ==> (b1 -> b2) // (8) containPairBoolMap[] /\ m = n ==> (b2 -> b1) // (9) containPairBoolMap[] /\ m = n ==> (b1 -> b2) // ------------------------------------------ for (auto varAst1 : willEqClass) { for (auto varAst2 : willEqClass) { check_contain_by_eq_nodes(varAst1, varAst2); } } } expr * theory_str::dealias_node(expr * node, std::map & varAliasMap, std::map & concatAliasMap) { if (variable_set.find(node) != variable_set.end()) { return get_alias_index_ast(varAliasMap, node); } else if (u.str.is_concat(to_app(node))) { return get_alias_index_ast(concatAliasMap, node); } return node; } void theory_str::get_grounded_concats(unsigned depth, expr* node, std::map & varAliasMap, std::map & concatAliasMap, std::map & varConstMap, std::map & concatConstMap, std::map > & varEqConcatMap, std::map, std::set > > & groundedMap) { if (u.re.is_unroll(to_app(node))) { return; } // ************************************************** // first deAlias the node if it is a var or concat // ************************************************** node = dealias_node(node, varAliasMap, concatAliasMap); if (groundedMap.find(node) != groundedMap.end()) { return; } IF_VERBOSE(100, verbose_stream() << "concats " << depth << "\n"; if (depth > 100) verbose_stream() << mk_pp(node, get_manager()) << "\n"; ); // haven't computed grounded concats for "node" (de-aliased) // --------------------------------------------------------- context & ctx = get_context(); // const strings: node is de-aliased if (u.str.is_string(node)) { std::vector concatNodes; concatNodes.push_back(node); groundedMap[node][concatNodes].clear(); // no condition } // Concat functions else if (u.str.is_concat(to_app(node))) { // if "node" equals to a constant string, thenjust push the constant into the concat vector // Again "node" has been de-aliased at the very beginning if (concatConstMap.find(node) != concatConstMap.end()) { std::vector concatNodes; concatNodes.push_back(concatConstMap[node]); groundedMap[node][concatNodes].clear(); groundedMap[node][concatNodes].insert(ctx.mk_eq_atom(node, concatConstMap[node])); } // node doesn't have eq constant value. Process its children. else { // merge arg0 and arg1 expr * arg0 = to_app(node)->get_arg(0); expr * arg1 = to_app(node)->get_arg(1); expr * arg0DeAlias = dealias_node(arg0, varAliasMap, concatAliasMap); expr * arg1DeAlias = dealias_node(arg1, varAliasMap, concatAliasMap); get_grounded_concats(depth + 1, arg0DeAlias, varAliasMap, concatAliasMap, varConstMap, concatConstMap, varEqConcatMap, groundedMap); get_grounded_concats(depth + 1, arg1DeAlias, varAliasMap, concatAliasMap, varConstMap, concatConstMap, varEqConcatMap, groundedMap); std::map, std::set >::iterator arg0_grdItor = groundedMap[arg0DeAlias].begin(); std::map, std::set >::iterator arg1_grdItor; for (; arg0_grdItor != groundedMap[arg0DeAlias].end(); arg0_grdItor++) { arg1_grdItor = groundedMap[arg1DeAlias].begin(); for (; arg1_grdItor != groundedMap[arg1DeAlias].end(); arg1_grdItor++) { std::vector ndVec; ndVec.insert(ndVec.end(), arg0_grdItor->first.begin(), arg0_grdItor->first.end()); size_t arg0VecSize = arg0_grdItor->first.size(); size_t arg1VecSize = arg1_grdItor->first.size(); if (arg0VecSize > 0 && arg1VecSize > 0 && u.str.is_string(arg0_grdItor->first[arg0VecSize - 1]) && u.str.is_string(arg1_grdItor->first[0])) { ndVec.pop_back(); ndVec.push_back(mk_concat(arg0_grdItor->first[arg0VecSize - 1], arg1_grdItor->first[0])); for (size_t i = 1; i < arg1VecSize; i++) { ndVec.push_back(arg1_grdItor->first[i]); } } else { ndVec.insert(ndVec.end(), arg1_grdItor->first.begin(), arg1_grdItor->first.end()); } // only insert if we don't know "node = concat(ndVec)" since one set of condition leads to this is enough if (groundedMap[node].find(ndVec) == groundedMap[node].end()) { groundedMap[node][ndVec]; if (arg0 != arg0DeAlias) { groundedMap[node][ndVec].insert(ctx.mk_eq_atom(arg0, arg0DeAlias)); } groundedMap[node][ndVec].insert(arg0_grdItor->second.begin(), arg0_grdItor->second.end()); if (arg1 != arg1DeAlias) { groundedMap[node][ndVec].insert(ctx.mk_eq_atom(arg1, arg1DeAlias)); } groundedMap[node][ndVec].insert(arg1_grdItor->second.begin(), arg1_grdItor->second.end()); } } } } } // string variables else if (variable_set.find(node) != variable_set.end()) { // deAliasedVar = Constant if (varConstMap.find(node) != varConstMap.end()) { std::vector concatNodes; concatNodes.push_back(varConstMap[node]); groundedMap[node][concatNodes].clear(); groundedMap[node][concatNodes].insert(ctx.mk_eq_atom(node, varConstMap[node])); } // deAliasedVar = someConcat else if (varEqConcatMap.find(node) != varEqConcatMap.end()) { expr * eqConcat = varEqConcatMap[node].begin()->first; expr * deAliasedEqConcat = dealias_node(eqConcat, varAliasMap, concatAliasMap); get_grounded_concats(depth + 1, deAliasedEqConcat, varAliasMap, concatAliasMap, varConstMap, concatConstMap, varEqConcatMap, groundedMap); std::map, std::set >::iterator grdItor = groundedMap[deAliasedEqConcat].begin(); for (; grdItor != groundedMap[deAliasedEqConcat].end(); grdItor++) { std::vector ndVec; ndVec.insert(ndVec.end(), grdItor->first.begin(), grdItor->first.end()); // only insert if we don't know "node = concat(ndVec)" since one set of condition leads to this is enough if (groundedMap[node].find(ndVec) == groundedMap[node].end()) { // condition: node = deAliasedEqConcat groundedMap[node][ndVec].insert(ctx.mk_eq_atom(node, deAliasedEqConcat)); // appending conditions for "deAliasedEqConcat = CONCAT(ndVec)" groundedMap[node][ndVec].insert(grdItor->second.begin(), grdItor->second.end()); } } } // node (has been de-aliased) != constant && node (has been de-aliased) != any concat // just push in the deAliasedVar else { std::vector concatNodes; concatNodes.push_back(node); groundedMap[node][concatNodes]; } } } void theory_str::print_grounded_concat(expr * node, std::map, std::set > > & groundedMap) { TRACE("str", tout << mk_pp(node, get_manager()) << std::endl;); if (groundedMap.find(node) != groundedMap.end()) { std::map, std::set >::iterator itor = groundedMap[node].begin(); for (; itor != groundedMap[node].end(); ++itor) { TRACE("str", tout << "\t[grounded] "; std::vector::const_iterator vIt = itor->first.begin(); for (; vIt != itor->first.end(); ++vIt) { tout << mk_pp(*vIt, get_manager()) << ", "; } tout << std::endl; tout << "\t[condition] "; std::set::iterator sIt = itor->second.begin(); for (; sIt != itor->second.end(); sIt++) { tout << mk_pp(*sIt, get_manager()) << ", "; } tout << std::endl; ); } } else { TRACE("str", tout << "not found" << std::endl;); } } bool theory_str::is_partial_in_grounded_concat(const std::vector & strVec, const std::vector & subStrVec) { size_t strCnt = strVec.size(); size_t subStrCnt = subStrVec.size(); if (strCnt == 0 || subStrCnt == 0) { return false; } // The assumption is that all consecutive constant strings are merged into one node if (strCnt < subStrCnt) { return false; } if (subStrCnt == 1) { zstring subStrVal; if (u.str.is_string(subStrVec[0], subStrVal)) { for (size_t i = 0; i < strCnt; i++) { zstring strVal; if (u.str.is_string(strVec[i], strVal)) { if (strVal.contains(subStrVal)) { return true; } } } } else { for (size_t i = 0; i < strCnt; i++) { if (strVec[i] == subStrVec[0]) { return true; } } } return false; } else { for (size_t i = 0; i <= (strCnt - subStrCnt); i++) { // The first node in subStrVect should be // * constant: a suffix of a note in strVec[i] // * variable: bool firstNodesOK = true; zstring subStrHeadVal; if (u.str.is_string(subStrVec[0], subStrHeadVal)) { zstring strHeadVal; if (u.str.is_string(strVec[i], strHeadVal)) { if (strHeadVal.length() >= subStrHeadVal.length()) { zstring suffix = strHeadVal.extract(strHeadVal.length() - subStrHeadVal.length(), subStrHeadVal.length()); if (suffix != subStrHeadVal) { firstNodesOK = false; } } else { firstNodesOK = false; } } else { if (subStrVec[0] != strVec[i]) { firstNodesOK = false; } } } if (!firstNodesOK) { continue; } // middle nodes bool midNodesOK = true; for (size_t j = 1; j < subStrCnt - 1; j++) { if (subStrVec[j] != strVec[i + j]) { midNodesOK = false; break; } } if (!midNodesOK) { continue; } // tail nodes size_t tailIdx = i + subStrCnt - 1; zstring subStrTailVal; if (u.str.is_string(subStrVec[subStrCnt - 1], subStrTailVal)) { zstring strTailVal; if (u.str.is_string(strVec[tailIdx], strTailVal)) { if (strTailVal.length() >= subStrTailVal.length()) { zstring prefix = strTailVal.extract(0, subStrTailVal.length()); if (prefix == subStrTailVal) { return true; } else { continue; } } else { continue; } } } else { if (subStrVec[subStrCnt - 1] == strVec[tailIdx]) { return true; } else { continue; } } } return false; } } void theory_str::check_subsequence(expr* str, expr* strDeAlias, expr* subStr, expr* subStrDeAlias, expr* boolVar, std::map, std::set > > & groundedMap) { context & ctx = get_context(); ast_manager & m = get_manager(); std::map, std::set >::iterator itorStr = groundedMap[strDeAlias].begin(); std::map, std::set >::iterator itorSubStr; for (; itorStr != groundedMap[strDeAlias].end(); itorStr++) { itorSubStr = groundedMap[subStrDeAlias].begin(); for (; itorSubStr != groundedMap[subStrDeAlias].end(); itorSubStr++) { bool contain = is_partial_in_grounded_concat(itorStr->first, itorSubStr->first); if (contain) { expr_ref_vector litems(m); if (str != strDeAlias) { litems.push_back(ctx.mk_eq_atom(str, strDeAlias)); } if (subStr != subStrDeAlias) { litems.push_back(ctx.mk_eq_atom(subStr, subStrDeAlias)); } //litems.insert(itorStr->second.begin(), itorStr->second.end()); //litems.insert(itorSubStr->second.begin(), itorSubStr->second.end()); for (std::set::const_iterator i1 = itorStr->second.begin(); i1 != itorStr->second.end(); ++i1) { litems.push_back(*i1); } for (std::set::const_iterator i1 = itorSubStr->second.begin(); i1 != itorSubStr->second.end(); ++i1) { litems.push_back(*i1); } expr_ref implyR(boolVar, m); if (litems.empty()) { assert_axiom(implyR); } else { expr_ref implyL(mk_and(litems), m); assert_implication(implyL, implyR); } } } } } void theory_str::compute_contains(std::map & varAliasMap, std::map & concatAliasMap, std::map & varConstMap, std::map & concatConstMap, std::map > & varEqConcatMap) { std::map, std::set > > groundedMap; for (auto const& kv : contain_pair_bool_map) { expr* containBoolVar = kv.get_value(); expr* str = kv.get_key1(); expr* subStr = kv.get_key2(); expr* strDeAlias = dealias_node(str, varAliasMap, concatAliasMap); expr* subStrDeAlias = dealias_node(subStr, varAliasMap, concatAliasMap); get_grounded_concats(0, strDeAlias, varAliasMap, concatAliasMap, varConstMap, concatConstMap, varEqConcatMap, groundedMap); get_grounded_concats(0, subStrDeAlias, varAliasMap, concatAliasMap, varConstMap, concatConstMap, varEqConcatMap, groundedMap); // debugging print_grounded_concat(strDeAlias, groundedMap); print_grounded_concat(subStrDeAlias, groundedMap); check_subsequence(str, strDeAlias, subStr, subStrDeAlias, containBoolVar, groundedMap); } } bool theory_str::can_concat_eq_str(expr * concat, zstring& str) { unsigned int strLen = str.length(); if (u.str.is_concat(to_app(concat))) { ptr_vector args; get_nodes_in_concat(concat, args); expr * ml_node = args[0]; expr * mr_node = args[args.size() - 1]; zstring ml_str; if (u.str.is_string(ml_node, ml_str)) { unsigned int ml_len = ml_str.length(); if (ml_len > strLen) { return false; } unsigned int cLen = ml_len; if (ml_str != str.extract(0, cLen)) { return false; } } zstring mr_str; if (u.str.is_string(mr_node, mr_str)) { unsigned int mr_len = mr_str.length(); if (mr_len > strLen) { return false; } unsigned int cLen = mr_len; if (mr_str != str.extract(strLen - cLen, cLen)) { return false; } } unsigned int sumLen = 0; for (unsigned int i = 0 ; i < args.size() ; i++) { expr * oneArg = args[i]; zstring arg_str; if (u.str.is_string(oneArg, arg_str)) { if (!str.contains(arg_str)) { return false; } sumLen += arg_str.length(); } } if (sumLen > strLen) { return false; } } return true; } bool theory_str::can_concat_eq_concat(expr * concat1, expr * concat2) { if (u.str.is_concat(to_app(concat1)) && u.str.is_concat(to_app(concat2))) { { // Suppose concat1 = (Concat X Y) and concat2 = (Concat M N). expr * concat1_mostL = getMostLeftNodeInConcat(concat1); expr * concat2_mostL = getMostLeftNodeInConcat(concat2); // if both X and M are constant strings, check whether they have the same prefix zstring concat1_mostL_str, concat2_mostL_str; if (u.str.is_string(concat1_mostL, concat1_mostL_str) && u.str.is_string(concat2_mostL, concat2_mostL_str)) { unsigned int cLen = std::min(concat1_mostL_str.length(), concat2_mostL_str.length()); if (concat1_mostL_str.extract(0, cLen) != concat2_mostL_str.extract(0, cLen)) { return false; } } } { // Similarly, if both Y and N are constant strings, check whether they have the same suffix expr * concat1_mostR = getMostRightNodeInConcat(concat1); expr * concat2_mostR = getMostRightNodeInConcat(concat2); zstring concat1_mostR_str, concat2_mostR_str; if (u.str.is_string(concat1_mostR, concat1_mostR_str) && u.str.is_string(concat2_mostR, concat2_mostR_str)) { unsigned int cLen = std::min(concat1_mostR_str.length(), concat2_mostR_str.length()); if (concat1_mostR_str.extract(concat1_mostR_str.length() - cLen, cLen) != concat2_mostR_str.extract(concat2_mostR_str.length() - cLen, cLen)) { return false; } } } } return true; } /* * Check whether n1 and n2 could be equal. * Returns true if n1 could equal n2 (maybe), * and false if n1 is definitely not equal to n2 (no). */ bool theory_str::can_two_nodes_eq(expr * n1, expr * n2) { app * n1_curr = to_app(n1); app * n2_curr = to_app(n2); // case 0: n1_curr is const string, n2_curr is const string zstring n1_curr_str, n2_curr_str; if (u.str.is_string(n1_curr, n1_curr_str) && u.str.is_string(n2_curr, n2_curr_str)) { TRACE("str", tout << "checking string constants: n1=" << n1_curr_str << ", n2=" << n2_curr_str << std::endl;); if (n1_curr_str == n2_curr_str) { // TODO(mtrberzi) potential correction: if n1_curr != n2_curr, // assert that these two terms are in fact equal, because they ought to be return true; } else { return false; } } // case 1: n1_curr is concat, n2_curr is const string else if (u.str.is_concat(n1_curr) && u.str.is_string(n2_curr)) { zstring n2_curr_str; u.str.is_string(n2_curr, n2_curr_str); if (!can_concat_eq_str(n1_curr, n2_curr_str)) { return false; } } // case 2: n2_curr is concat, n1_curr is const string else if (u.str.is_concat(n2_curr) && u.str.is_string(n1_curr)) { zstring n1_curr_str; u.str.is_string(n1_curr, n1_curr_str); if (!can_concat_eq_str(n2_curr, n1_curr_str)) { return false; } } // case 3: both are concats else if (u.str.is_concat(n1_curr) && u.str.is_concat(n2_curr)) { if (!can_concat_eq_concat(n1_curr, n2_curr)) { return false; } } return true; } // was checkLength2ConstStr() in Z3str2 // returns true if everything is OK, or false if inconsistency detected // - note that these are different from the semantics in Z3str2 bool theory_str::check_length_const_string(expr * n1, expr * constStr) { ast_manager & mgr = get_manager(); context & ctx = get_context(); zstring tmp; u.str.is_string(constStr, tmp); rational strLen(tmp.length()); if (u.str.is_concat(to_app(n1))) { ptr_vector args; expr_ref_vector items(mgr); get_nodes_in_concat(n1, args); rational sumLen(0); for (unsigned int i = 0; i < args.size(); ++i) { rational argLen; bool argLen_exists = get_len_value(args[i], argLen); if (argLen_exists) { if (!u.str.is_string(args[i])) { items.push_back(ctx.mk_eq_atom(mk_strlen(args[i]), mk_int(argLen))); } TRACE("str", tout << "concat arg: " << mk_pp(args[i], mgr) << " has len = " << argLen.to_string() << std::endl;); sumLen += argLen; if (sumLen > strLen) { items.push_back(ctx.mk_eq_atom(n1, constStr)); expr_ref toAssert(mgr.mk_not(mk_and(items)), mgr); TRACE("str", tout << "inconsistent length: concat (len = " << sumLen << ") <==> string constant (len = " << strLen << ")" << std::endl;); assert_axiom(toAssert); return false; } } } } else { // !is_concat(n1) rational oLen; bool oLen_exists = get_len_value(n1, oLen); if (oLen_exists && oLen != strLen) { TRACE("str", tout << "inconsistent length: var (len = " << oLen << ") <==> string constant (len = " << strLen << ")" << std::endl;); expr_ref l(ctx.mk_eq_atom(n1, constStr), mgr); expr_ref r(ctx.mk_eq_atom(mk_strlen(n1), mk_strlen(constStr)), mgr); assert_implication(l, r); return false; } } rational unused; if (get_len_value(n1, unused) == false) { expr_ref l(ctx.mk_eq_atom(n1, constStr), mgr); expr_ref r(ctx.mk_eq_atom(mk_strlen(n1), mk_strlen(constStr)), mgr); assert_implication(l, r); } return true; } bool theory_str::check_length_concat_concat(expr * n1, expr * n2) { context & ctx = get_context(); ast_manager & mgr = get_manager(); ptr_vector concat1Args; ptr_vector concat2Args; get_nodes_in_concat(n1, concat1Args); get_nodes_in_concat(n2, concat2Args); bool concat1LenFixed = true; bool concat2LenFixed = true; expr_ref_vector items(mgr); rational sum1(0), sum2(0); for (unsigned int i = 0; i < concat1Args.size(); ++i) { expr * oneArg = concat1Args[i]; rational argLen; bool argLen_exists = get_len_value(oneArg, argLen); if (argLen_exists) { sum1 += argLen; if (!u.str.is_string(oneArg)) { items.push_back(ctx.mk_eq_atom(mk_strlen(oneArg), mk_int(argLen))); } } else { concat1LenFixed = false; } } for (unsigned int i = 0; i < concat2Args.size(); ++i) { expr * oneArg = concat2Args[i]; rational argLen; bool argLen_exists = get_len_value(oneArg, argLen); if (argLen_exists) { sum2 += argLen; if (!u.str.is_string(oneArg)) { items.push_back(ctx.mk_eq_atom(mk_strlen(oneArg), mk_int(argLen))); } } else { concat2LenFixed = false; } } items.push_back(ctx.mk_eq_atom(n1, n2)); bool conflict = false; if (concat1LenFixed && concat2LenFixed) { if (sum1 != sum2) { conflict = true; } } else if (!concat1LenFixed && concat2LenFixed) { if (sum1 > sum2) { conflict = true; } } else if (concat1LenFixed && !concat2LenFixed) { if (sum1 < sum2) { conflict = true; } } if (conflict) { TRACE("str", tout << "inconsistent length detected in concat <==> concat" << std::endl;); expr_ref toAssert(mgr.mk_not(mk_and(items)), mgr); assert_axiom(toAssert); return false; } return true; } bool theory_str::check_length_concat_var(expr * concat, expr * var) { context & ctx = get_context(); ast_manager & mgr = get_manager(); rational varLen; bool varLen_exists = get_len_value(var, varLen); if (!varLen_exists) { return true; } else { rational sumLen(0); ptr_vector args; expr_ref_vector items(mgr); get_nodes_in_concat(concat, args); for (unsigned int i = 0; i < args.size(); ++i) { expr * oneArg = args[i]; rational argLen; bool argLen_exists = get_len_value(oneArg, argLen); if (argLen_exists) { if (!u.str.is_string(oneArg) && !argLen.is_zero()) { items.push_back(ctx.mk_eq_atom(mk_strlen(oneArg), mk_int(argLen))); } sumLen += argLen; if (sumLen > varLen) { TRACE("str", tout << "inconsistent length detected in concat <==> var" << std::endl;); items.push_back(ctx.mk_eq_atom(mk_strlen(var), mk_int(varLen))); items.push_back(ctx.mk_eq_atom(concat, var)); expr_ref toAssert(mgr.mk_not(mk_and(items)), mgr); assert_axiom(toAssert); return false; } } } return true; } } bool theory_str::check_length_var_var(expr * var1, expr * var2) { context & ctx = get_context(); ast_manager & mgr = get_manager(); rational var1Len, var2Len; bool var1Len_exists = get_len_value(var1, var1Len); bool var2Len_exists = get_len_value(var2, var2Len); if (var1Len_exists && var2Len_exists && var1Len != var2Len) { TRACE("str", tout << "inconsistent length detected in var <==> var" << std::endl;); expr_ref_vector items(mgr); items.push_back(ctx.mk_eq_atom(mk_strlen(var1), mk_int(var1Len))); items.push_back(ctx.mk_eq_atom(mk_strlen(var2), mk_int(var2Len))); items.push_back(ctx.mk_eq_atom(var1, var2)); expr_ref toAssert(mgr.mk_not(mk_and(items)), mgr); assert_axiom(toAssert); return false; } return true; } // returns true if everything is OK, or false if inconsistency detected // - note that these are different from the semantics in Z3str2 bool theory_str::check_length_eq_var_concat(expr * n1, expr * n2) { // n1 and n2 are not const string: either variable or concat bool n1Concat = u.str.is_concat(to_app(n1)); bool n2Concat = u.str.is_concat(to_app(n2)); if (n1Concat && n2Concat) { return check_length_concat_concat(n1, n2); } // n1 is concat, n2 is variable else if (n1Concat && (!n2Concat)) { return check_length_concat_var(n1, n2); } // n1 is variable, n2 is concat else if ((!n1Concat) && n2Concat) { return check_length_concat_var(n2, n1); } // n1 and n2 are both variables else { return check_length_var_var(n1, n2); } return true; } // returns false if an inconsistency is detected, or true if no inconsistencies were found // - note that these are different from the semantics of checkLengConsistency() in Z3str2 bool theory_str::check_length_consistency(expr * n1, expr * n2) { if (u.str.is_string(n1) && u.str.is_string(n2)) { // consistency has already been checked in can_two_nodes_eq(). return true; } else if (u.str.is_string(n1) && (!u.str.is_string(n2))) { return check_length_const_string(n2, n1); } else if (u.str.is_string(n2) && (!u.str.is_string(n1))) { return check_length_const_string(n1, n2); } else { // n1 and n2 are vars or concats return check_length_eq_var_concat(n1, n2); } return true; } // Modified signature: returns true if nothing was learned, or false if at least one axiom was asserted. // (This is used for deferred consistency checking) bool theory_str::check_concat_len_in_eqc(expr * concat) { bool no_assertions = true; expr * eqc_n = concat; do { if (u.str.is_concat(to_app(eqc_n))) { rational unused; bool status = infer_len_concat(eqc_n, unused); if (status) { no_assertions = false; } } eqc_n = get_eqc_next(eqc_n); } while (eqc_n != concat); return no_assertions; } // Convert a regular expression to an e-NFA using Thompson's construction void nfa::convert_re(expr * e, unsigned & start, unsigned & end, seq_util & u) { start = next_id(); end = next_id(); if (u.re.is_to_re(e)) { app * a = to_app(e); expr * arg_str = a->get_arg(0); zstring str; if (u.str.is_string(arg_str, str)) { if (str.length() == 0) { // transitioning on the empty string is handled specially TRACE("str", tout << "empty string epsilon-move " << start << " --> " << end << std::endl;); make_epsilon_move(start, end); } else { TRACE("str", tout << "build NFA for '" << str << "'" << "\n";); /* * For an n-character string, we make (n-1) intermediate states, * labelled i_(0) through i_(n-2). * Then we construct the following transitions: * start --str[0]--> i_(0) --str[1]--> i_(1) --...--> i_(n-2) --str[n-1]--> final */ unsigned last = start; for (int i = 0; i <= ((int)str.length()) - 2; ++i) { unsigned i_state = next_id(); make_transition(last, str[i], i_state); TRACE("str", tout << "string transition " << last << "--" << str[i] << "--> " << i_state << "\n";); last = i_state; } make_transition(last, str[(str.length() - 1)], end); TRACE("str", tout << "string transition " << last << "--" << str[(str.length() - 1)] << "--> " << end << "\n";); } } else { // ! u.str.is_string(arg_str, str) TRACE("str", tout << "WARNING: invalid string constant in str.to.re! Cancelling." << std::endl;); u.get_manager().raise_exception("invalid term in str.to.re, argument must be a string constant"); m_valid = false; return; } } else if (u.re.is_concat(e)){ app * a = to_app(e); expr * re1 = a->get_arg(0); expr * re2 = a->get_arg(1); unsigned start1, end1; convert_re(re1, start1, end1, u); unsigned start2, end2; convert_re(re2, start2, end2, u); // start --e--> start1 --...--> end1 --e--> start2 --...--> end2 --e--> end make_epsilon_move(start, start1); make_epsilon_move(end1, start2); make_epsilon_move(end2, end); TRACE("str", tout << "concat NFA: start = " << start << ", end = " << end << std::endl;); } else if (u.re.is_union(e)) { app * a = to_app(e); expr * re1 = a->get_arg(0); expr * re2 = a->get_arg(1); unsigned start1, end1; convert_re(re1, start1, end1, u); unsigned start2, end2; convert_re(re2, start2, end2, u); // start --e--> start1 ; start --e--> start2 // end1 --e--> end ; end2 --e--> end make_epsilon_move(start, start1); make_epsilon_move(start, start2); make_epsilon_move(end1, end); make_epsilon_move(end2, end); TRACE("str", tout << "union NFA: start = " << start << ", end = " << end << std::endl;); } else if (u.re.is_star(e)) { app * a = to_app(e); expr * subex = a->get_arg(0); unsigned start_subex, end_subex; convert_re(subex, start_subex, end_subex, u); // start --e--> start_subex, start --e--> end // end_subex --e--> start_subex, end_subex --e--> end make_epsilon_move(start, start_subex); make_epsilon_move(start, end); make_epsilon_move(end_subex, start_subex); make_epsilon_move(end_subex, end); TRACE("str", tout << "star NFA: start = " << start << ", end = " << end << std::endl;); } else if (u.re.is_range(e)) { // range('a', 'z') // start --'a'--> end // start --'b'--> end // ... // start --'z'--> end app * a = to_app(e); expr * c1 = a->get_arg(0); expr * c2 = a->get_arg(1); zstring s_c1, s_c2; u.str.is_string(c1, s_c1); u.str.is_string(c2, s_c2); unsigned int id1 = s_c1[0]; unsigned int id2 = s_c2[0]; if (id1 > id2) { unsigned int tmp = id1; id1 = id2; id2 = tmp; } for (unsigned int i = id1; i <= id2; ++i) { char ch = (char)i; make_transition(start, ch, end); } TRACE("str", tout << "range NFA: start = " << start << ", end = " << end << std::endl;); } else if (u.re.is_full_seq(e)) { // effectively the same as .* where . can be any single character // start --e--> tmp // tmp --e--> end // tmp --C--> tmp for every character C unsigned tmp = next_id(); make_epsilon_move(start, tmp); make_epsilon_move(tmp, end); for (unsigned int i = 0; i < 256; ++i) { char ch = (char)i; make_transition(tmp, ch, tmp); } TRACE("str", tout << "re.all NFA: start = " << start << ", end = " << end << std::endl;); } else if (u.re.is_full_char(e)) { // effectively . (match any one character) for (unsigned int i = 0; i < 256; ++i) { char ch = (char)i; make_transition(start, ch, end); } TRACE("str", tout << "re.allchar NFA: start = " << start << ", end = " << end << std::endl;); } else { TRACE("str", tout << "invalid regular expression" << std::endl;); m_valid = false; return; } } void nfa::epsilon_closure(unsigned start, std::set & closure) { std::deque worklist; closure.insert(start); worklist.push_back(start); while(!worklist.empty()) { unsigned state = worklist.front(); worklist.pop_front(); if (epsilon_map.find(state) != epsilon_map.end()) { for (std::set::iterator it = epsilon_map[state].begin(); it != epsilon_map[state].end(); ++it) { unsigned new_state = *it; if (closure.find(new_state) == closure.end()) { closure.insert(new_state); worklist.push_back(new_state); } } } } } bool nfa::matches(zstring input) { /* * Keep a set of all states the NFA can currently be in. * Initially this is the e-closure of m_start_state * For each character A in the input string, * the set of next states contains * all states in transition_map[S][A] for each S in current_states, * and all states in epsilon_map[S] for each S in current_states. * After consuming the entire input string, * the match is successful iff current_states contains m_end_state. */ std::set current_states; epsilon_closure(m_start_state, current_states); for (unsigned i = 0; i < input.length(); ++i) { char A = (char)input[i]; std::set next_states; for (std::set::iterator it = current_states.begin(); it != current_states.end(); ++it) { unsigned S = *it; // check transition_map if (transition_map[S].find(A) != transition_map[S].end()) { next_states.insert(transition_map[S][A]); } } // take e-closure over next_states to compute the actual next_states std::set epsilon_next_states; for (std::set::iterator it = next_states.begin(); it != next_states.end(); ++it) { unsigned S = *it; std::set closure; epsilon_closure(S, closure); epsilon_next_states.insert(closure.begin(), closure.end()); } current_states = epsilon_next_states; } if (current_states.find(m_end_state) != current_states.end()) { return true; } else { return false; } } void theory_str::check_regex_in(expr * nn1, expr * nn2) { context & ctx = get_context(); ast_manager & m = get_manager(); expr_ref_vector eqNodeSet(m); expr * constStr_1 = collect_eq_nodes(nn1, eqNodeSet); expr * constStr_2 = collect_eq_nodes(nn2, eqNodeSet); expr * constStr = (constStr_1 != nullptr) ? constStr_1 : constStr_2; if (constStr == nullptr) { return; } else { expr_ref_vector::iterator itor = eqNodeSet.begin(); for (; itor != eqNodeSet.end(); itor++) { if (regex_in_var_reg_str_map.contains(*itor)) { std::set::iterator strItor = regex_in_var_reg_str_map[*itor].begin(); for (; strItor != regex_in_var_reg_str_map[*itor].end(); strItor++) { zstring regStr = *strItor; zstring constStrValue; u.str.is_string(constStr, constStrValue); std::pair key1 = std::make_pair(*itor, regStr); if (regex_in_bool_map.find(key1) != regex_in_bool_map.end()) { expr * boolVar = regex_in_bool_map[key1]; // actually the RegexIn term app * a_regexIn = to_app(boolVar); expr * regexTerm = a_regexIn->get_arg(1); // TODO figure out regex NFA stuff if (!regex_nfa_cache.contains(regexTerm)) { TRACE("str", tout << "regex_nfa_cache: cache miss" << std::endl;); regex_nfa_cache.insert(regexTerm, nfa(u, regexTerm)); } else { TRACE("str", tout << "regex_nfa_cache: cache hit" << std::endl;); } nfa regexNFA = regex_nfa_cache[regexTerm]; ENSURE(regexNFA.is_valid()); bool matchRes = regexNFA.matches(constStrValue); TRACE("str", tout << mk_pp(*itor, m) << " in " << regStr << " : " << (matchRes ? "yes" : "no") << std::endl;); expr_ref implyL(ctx.mk_eq_atom(*itor, constStr), m); if (matchRes) { assert_implication(implyL, boolVar); } else { assert_implication(implyL, mk_not(m, boolVar)); } } } } } } } /* * strArgmt::solve_concat_eq_str() * Solve concatenations of the form: * const == Concat(const, X) * const == Concat(X, const) */ void theory_str::solve_concat_eq_str(expr * concat, expr * str) { ast_manager & m = get_manager(); context & ctx = get_context(); TRACE("str", tout << mk_ismt2_pp(concat, m) << " == " << mk_ismt2_pp(str, m) << std::endl;); zstring const_str; if (u.str.is_concat(to_app(concat)) && u.str.is_string(to_app(str), const_str)) { app * a_concat = to_app(concat); SASSERT(a_concat->get_num_args() == 2); expr * a1 = a_concat->get_arg(0); expr * a2 = a_concat->get_arg(1); if (const_str.empty()) { TRACE("str", tout << "quick path: concat == \"\"" << std::endl;); // assert the following axiom: // ( (Concat a1 a2) == "" ) -> ( (a1 == "") AND (a2 == "") ) expr_ref premise(ctx.mk_eq_atom(concat, str), m); expr_ref c1(ctx.mk_eq_atom(a1, str), m); expr_ref c2(ctx.mk_eq_atom(a2, str), m); expr_ref conclusion(m.mk_and(c1, c2), m); assert_implication(premise, conclusion); return; } bool arg1_has_eqc_value = false; bool arg2_has_eqc_value = false; expr * arg1 = get_eqc_value(a1, arg1_has_eqc_value); expr * arg2 = get_eqc_value(a2, arg2_has_eqc_value); expr_ref newConcat(m); if (arg1 != a1 || arg2 != a2) { TRACE("str", tout << "resolved concat argument(s) to eqc string constants" << std::endl;); expr_ref_vector item1(m); if (a1 != arg1) { item1.push_back(ctx.mk_eq_atom(a1, arg1)); } if (a2 != arg2) { item1.push_back(ctx.mk_eq_atom(a2, arg2)); } expr_ref implyL1(mk_and(item1), m); newConcat = mk_concat(arg1, arg2); if (newConcat != str) { expr_ref implyR1(ctx.mk_eq_atom(concat, newConcat), m); assert_implication(implyL1, implyR1); } } else { newConcat = concat; } if (newConcat == str) { return; } if (!u.str.is_concat(to_app(newConcat))) { return; } if (arg1_has_eqc_value && arg2_has_eqc_value) { // Case 1: Concat(const, const) == const TRACE("str", tout << "Case 1: Concat(const, const) == const" << std::endl;); zstring arg1_str, arg2_str; u.str.is_string(arg1, arg1_str); u.str.is_string(arg2, arg2_str); zstring result_str = arg1_str + arg2_str; if (result_str != const_str) { // Inconsistency TRACE("str", tout << "inconsistency detected: \"" << arg1_str << "\" + \"" << arg2_str << "\" != \"" << const_str << "\"" << "\n";); expr_ref equality(ctx.mk_eq_atom(concat, str), m); expr_ref diseq(mk_not(m, equality), m); assert_axiom(diseq); return; } } else if (!arg1_has_eqc_value && arg2_has_eqc_value) { // Case 2: Concat(var, const) == const TRACE("str", tout << "Case 2: Concat(var, const) == const" << std::endl;); zstring arg2_str; u.str.is_string(arg2, arg2_str); unsigned int resultStrLen = const_str.length(); unsigned int arg2StrLen = arg2_str.length(); if (resultStrLen < arg2StrLen) { // Inconsistency TRACE("str", tout << "inconsistency detected: \"" << arg2_str << "\" is longer than \"" << const_str << "\"," << " so cannot be concatenated with anything to form it" << "\n";); expr_ref equality(ctx.mk_eq_atom(newConcat, str), m); expr_ref diseq(mk_not(m, equality), m); assert_axiom(diseq); return; } else { int varStrLen = resultStrLen - arg2StrLen; zstring firstPart = const_str.extract(0, varStrLen); zstring secondPart = const_str.extract(varStrLen, arg2StrLen); if (arg2_str != secondPart) { // Inconsistency TRACE("str", tout << "inconsistency detected: " << "suffix of concatenation result expected \"" << secondPart << "\", " << "actually \"" << arg2_str << "\"" << "\n";); expr_ref equality(ctx.mk_eq_atom(newConcat, str), m); expr_ref diseq(mk_not(m, equality), m); assert_axiom(diseq); return; } else { expr_ref tmpStrConst(mk_string(firstPart), m); expr_ref premise(ctx.mk_eq_atom(newConcat, str), m); expr_ref conclusion(ctx.mk_eq_atom(arg1, tmpStrConst), m); assert_implication(premise, conclusion); return; } } } else if (arg1_has_eqc_value && !arg2_has_eqc_value) { // Case 3: Concat(const, var) == const TRACE("str", tout << "Case 3: Concat(const, var) == const" << std::endl;); zstring arg1_str; u.str.is_string(arg1, arg1_str); unsigned int resultStrLen = const_str.length(); unsigned int arg1StrLen = arg1_str.length(); if (resultStrLen < arg1StrLen) { // Inconsistency TRACE("str", tout << "inconsistency detected: \"" << arg1_str << "\" is longer than \"" << const_str << "\"," << " so cannot be concatenated with anything to form it" << "\n";); expr_ref equality(ctx.mk_eq_atom(newConcat, str), m); expr_ref diseq(m.mk_not(equality), m); assert_axiom(diseq); return; } else { int varStrLen = resultStrLen - arg1StrLen; zstring firstPart = const_str.extract(0, arg1StrLen); zstring secondPart = const_str.extract(arg1StrLen, varStrLen); if (arg1_str != firstPart) { // Inconsistency TRACE("str", tout << "inconsistency detected: " << "prefix of concatenation result expected \"" << secondPart << "\", " << "actually \"" << arg1_str << "\"" << "\n";); expr_ref equality(ctx.mk_eq_atom(newConcat, str), m); expr_ref diseq(m.mk_not(equality), m); assert_axiom(diseq); return; } else { expr_ref tmpStrConst(mk_string(secondPart), m); expr_ref premise(ctx.mk_eq_atom(newConcat, str), m); expr_ref conclusion(ctx.mk_eq_atom(arg2, tmpStrConst), m); assert_implication(premise, conclusion); return; } } } else { // Case 4: Concat(var, var) == const TRACE("str", tout << "Case 4: Concat(var, var) == const" << std::endl;); if (eval_concat(arg1, arg2) == nullptr) { rational arg1Len, arg2Len; bool arg1Len_exists = get_len_value(arg1, arg1Len); bool arg2Len_exists = get_len_value(arg2, arg2Len); rational concatStrLen((unsigned)const_str.length()); if (arg1Len_exists || arg2Len_exists) { expr_ref ax_l1(ctx.mk_eq_atom(concat, str), m); expr_ref ax_l2(m); zstring prefixStr, suffixStr; if (arg1Len_exists) { if (arg1Len.is_neg()) { TRACE("str", tout << "length conflict: arg1Len = " << arg1Len << ", concatStrLen = " << concatStrLen << std::endl;); expr_ref toAssert(m_autil.mk_ge(mk_strlen(arg1), mk_int(0)), m); assert_axiom(toAssert); return; } else if (arg1Len > concatStrLen) { TRACE("str", tout << "length conflict: arg1Len = " << arg1Len << ", concatStrLen = " << concatStrLen << std::endl;); expr_ref ax_r1(m_autil.mk_le(mk_strlen(arg1), mk_int(concatStrLen)), m); assert_implication(ax_l1, ax_r1); return; } prefixStr = const_str.extract(0, arg1Len.get_unsigned()); rational concat_minus_arg1 = concatStrLen - arg1Len; suffixStr = const_str.extract(arg1Len.get_unsigned(), concat_minus_arg1.get_unsigned()); ax_l2 = ctx.mk_eq_atom(mk_strlen(arg1), mk_int(arg1Len)); } else { // arg2's length is available if (arg2Len.is_neg()) { TRACE("str", tout << "length conflict: arg2Len = " << arg2Len << ", concatStrLen = " << concatStrLen << std::endl;); expr_ref toAssert(m_autil.mk_ge(mk_strlen(arg2), mk_int(0)), m); assert_axiom(toAssert); return; } else if (arg2Len > concatStrLen) { TRACE("str", tout << "length conflict: arg2Len = " << arg2Len << ", concatStrLen = " << concatStrLen << std::endl;); expr_ref ax_r1(m_autil.mk_le(mk_strlen(arg2), mk_int(concatStrLen)), m); assert_implication(ax_l1, ax_r1); return; } rational concat_minus_arg2 = concatStrLen - arg2Len; prefixStr = const_str.extract(0, concat_minus_arg2.get_unsigned()); suffixStr = const_str.extract(concat_minus_arg2.get_unsigned(), arg2Len.get_unsigned()); ax_l2 = ctx.mk_eq_atom(mk_strlen(arg2), mk_int(arg2Len)); } // consistency check if (u.str.is_concat(to_app(arg1)) && !can_concat_eq_str(arg1, prefixStr)) { expr_ref ax_r(m.mk_not(ax_l2), m); assert_implication(ax_l1, ax_r); return; } if (u.str.is_concat(to_app(arg2)) && !can_concat_eq_str(arg2, suffixStr)) { expr_ref ax_r(m.mk_not(ax_l2), m); assert_implication(ax_l1, ax_r); return; } expr_ref_vector r_items(m); r_items.push_back(ctx.mk_eq_atom(arg1, mk_string(prefixStr))); r_items.push_back(ctx.mk_eq_atom(arg2, mk_string(suffixStr))); if (!arg1Len_exists) { r_items.push_back(ctx.mk_eq_atom(mk_strlen(arg1), mk_int(prefixStr.length()))); } if (!arg2Len_exists) { r_items.push_back(ctx.mk_eq_atom(mk_strlen(arg2), mk_int(suffixStr.length()))); } expr_ref lhs(m.mk_and(ax_l1, ax_l2), m); expr_ref rhs(mk_and(r_items), m); assert_implication(lhs, rhs); } else { /* ! (arg1Len != 1 || arg2Len != 1) */ expr_ref xorFlag(m); std::pair key1(arg1, arg2); std::pair key2(arg2, arg1); // check the entries in this map to make sure they're still in scope // before we use them. std::map, std::map >::iterator entry1 = varForBreakConcat.find(key1); std::map, std::map >::iterator entry2 = varForBreakConcat.find(key2); bool entry1InScope; if (entry1 == varForBreakConcat.end()) { TRACE("str", tout << "key1 no entry" << std::endl;); entry1InScope = false; } else { // OVERRIDE. entry1InScope = true; TRACE("str", tout << "key1 entry" << std::endl;); /* if (internal_variable_set.find((entry1->second)[0]) == internal_variable_set.end()) { TRACE("str", tout << "key1 entry not in scope" << std::endl;); entry1InScope = false; } else { TRACE("str", tout << "key1 entry in scope" << std::endl;); entry1InScope = true; } */ } bool entry2InScope; if (entry2 == varForBreakConcat.end()) { TRACE("str", tout << "key2 no entry" << std::endl;); entry2InScope = false; } else { // OVERRIDE. entry2InScope = true; TRACE("str", tout << "key2 entry" << std::endl;); /* if (internal_variable_set.find((entry2->second)[0]) == internal_variable_set.end()) { TRACE("str", tout << "key2 entry not in scope" << std::endl;); entry2InScope = false; } else { TRACE("str", tout << "key2 entry in scope" << std::endl;); entry2InScope = true; } */ } TRACE("str", tout << "entry 1 " << (entry1InScope ? "in scope" : "not in scope") << std::endl << "entry 2 " << (entry2InScope ? "in scope" : "not in scope") << std::endl;); if (!entry1InScope && !entry2InScope) { xorFlag = mk_internal_xor_var(); varForBreakConcat[key1][0] = xorFlag; } else if (entry1InScope) { xorFlag = varForBreakConcat[key1][0]; } else { // entry2InScope xorFlag = varForBreakConcat[key2][0]; } int concatStrLen = const_str.length(); int and_count = 1; expr_ref_vector arrangement_disjunction(m); for (int i = 0; i < concatStrLen + 1; ++i) { expr_ref_vector and_items(m); zstring prefixStr = const_str.extract(0, i); zstring suffixStr = const_str.extract(i, concatStrLen - i); // skip invalid options if (u.str.is_concat(to_app(arg1)) && !can_concat_eq_str(arg1, prefixStr)) { continue; } if (u.str.is_concat(to_app(arg2)) && !can_concat_eq_str(arg2, suffixStr)) { continue; } expr_ref prefixAst(mk_string(prefixStr), m); expr_ref arg1_eq (ctx.mk_eq_atom(arg1, prefixAst), m); and_items.push_back(arg1_eq); and_count += 1; expr_ref suffixAst(mk_string(suffixStr), m); expr_ref arg2_eq (ctx.mk_eq_atom(arg2, suffixAst), m); and_items.push_back(arg2_eq); and_count += 1; arrangement_disjunction.push_back(mk_and(and_items)); } expr_ref implyL(ctx.mk_eq_atom(concat, str), m); expr_ref implyR1(m); if (arrangement_disjunction.empty()) { // negate expr_ref concat_eq_str(ctx.mk_eq_atom(concat, str), m); expr_ref negate_ast(m.mk_not(concat_eq_str), m); assert_axiom(negate_ast); } else { implyR1 = mk_or(arrangement_disjunction); if (m_params.m_StrongArrangements) { expr_ref ax_strong(ctx.mk_eq_atom(implyL, implyR1), m); assert_axiom(ax_strong); } else { assert_implication(implyL, implyR1); } generate_mutual_exclusion(arrangement_disjunction); } } /* (arg1Len != 1 || arg2Len != 1) */ } /* if (Concat(arg1, arg2) == nullptr) */ } } } expr_ref theory_str::set_up_finite_model_test(expr * lhs, expr * rhs) { context & ctx = get_context(); ast_manager & m = get_manager(); TRACE("str", tout << "activating finite model testing for overlapping concats " << mk_pp(lhs, m) << " and " << mk_pp(rhs, m) << std::endl;); std::map concatMap; std::map unrollMap; std::map varMap; classify_ast_by_type(lhs, varMap, concatMap, unrollMap); classify_ast_by_type(rhs, varMap, concatMap, unrollMap); TRACE("str", tout << "found vars:"; for (std::map::iterator it = varMap.begin(); it != varMap.end(); ++it) { tout << " " << mk_pp(it->first, m); } tout << std::endl; ); expr_ref testvar(mk_str_var("finiteModelTest"), m); m_trail.push_back(testvar); ptr_vector varlist; for (std::map::iterator it = varMap.begin(); it != varMap.end(); ++it) { expr * v = it->first; varlist.push_back(v); } // make things easy for the core wrt. testvar expr_ref t1(ctx.mk_eq_atom(testvar, mk_string("")), m); expr_ref t_yes(ctx.mk_eq_atom(testvar, mk_string("yes")), m); expr_ref testvaraxiom(m.mk_or(t1, t_yes), m); assert_axiom(testvaraxiom); finite_model_test_varlists.insert(testvar, varlist); m_trail_stack.push(insert_obj_map >(finite_model_test_varlists, testvar) ); return t_yes; } void theory_str::finite_model_test(expr * testvar, expr * str) { context & ctx = get_context(); ast_manager & m = get_manager(); zstring s; if (!u.str.is_string(str, s)) return; if (s == "yes") { TRACE("str", tout << "start finite model test for " << mk_pp(testvar, m) << std::endl;); ptr_vector & vars = finite_model_test_varlists[testvar]; for (ptr_vector::iterator it = vars.begin(); it != vars.end(); ++it) { expr * v = *it; bool v_has_eqc = false; get_eqc_value(v, v_has_eqc); if (v_has_eqc) { TRACE("str", tout << "variable " << mk_pp(v,m) << " already equivalent to a string constant" << std::endl;); continue; } // check for any sort of existing length tester we might interfere with if (m_params.m_UseBinarySearch) { if (binary_search_len_tester_stack.contains(v) && !binary_search_len_tester_stack[v].empty()) { TRACE("str", tout << "already found existing length testers for " << mk_pp(v, m) << std::endl;); continue; } else { // start binary search as normal expr_ref implLhs(ctx.mk_eq_atom(testvar, str), m); expr_ref implRhs(binary_search_length_test(v, nullptr, ""), m); assert_implication(implLhs, implRhs); } } else { bool map_effectively_empty = false; if (!fvar_len_count_map.contains(v)) { map_effectively_empty = true; } if (!map_effectively_empty) { map_effectively_empty = true; if (fvar_lenTester_map.contains(v)) { for (expr * indicator : fvar_lenTester_map[v]) { if (internal_variable_set.contains(indicator)) { map_effectively_empty = false; break; } } } } if (map_effectively_empty) { TRACE("str", tout << "no existing length testers for " << mk_pp(v, m) << std::endl;); rational v_len; rational v_lower_bound; rational v_upper_bound; expr_ref vLengthExpr(mk_strlen(v), m); if (get_len_value(v, v_len)) { TRACE("str", tout << "length = " << v_len.to_string() << std::endl;); v_lower_bound = v_len; v_upper_bound = v_len; } else { bool lower_bound_exists = lower_bound(vLengthExpr, v_lower_bound); bool upper_bound_exists = upper_bound(vLengthExpr, v_upper_bound); TRACE("str", tout << "bounds = [" << (lower_bound_exists?v_lower_bound.to_string():"?") << ".." << (upper_bound_exists?v_upper_bound.to_string():"?") << "]" << std::endl;); // make sure the bounds are non-negative if (lower_bound_exists && v_lower_bound.is_neg()) { v_lower_bound = rational::zero(); } if (upper_bound_exists && v_upper_bound.is_neg()) { v_upper_bound = rational::zero(); } if (lower_bound_exists && upper_bound_exists) { // easiest case. we will search within these bounds } else if (upper_bound_exists && !lower_bound_exists) { // search between 0 and the upper bound v_lower_bound = rational::zero(); } else if (lower_bound_exists && !upper_bound_exists) { // check some finite portion of the search space v_upper_bound = v_lower_bound + rational(10); } else { // no bounds information v_lower_bound = rational::zero(); v_upper_bound = v_lower_bound + rational(10); } } // now create a fake length tester over this finite disjunction of lengths fvar_len_count_map.insert(v, 1); unsigned int testNum = fvar_len_count_map[v]; expr_ref indicator(mk_internal_lenTest_var(v, testNum), m); SASSERT(indicator); m_trail.push_back(indicator); if (!fvar_lenTester_map.contains(v)) { fvar_lenTester_map.insert(v, ptr_vector()); } fvar_lenTester_map[v].shrink(0); fvar_lenTester_map[v].push_back(indicator); lenTester_fvar_map.insert(indicator, v); expr_ref_vector orList(m); expr_ref_vector andList(m); for (rational l = v_lower_bound; l <= v_upper_bound; l += rational::one()) { zstring lStr = zstring(l.to_string().c_str()); expr_ref str_indicator(mk_string(lStr), m); expr_ref or_expr(ctx.mk_eq_atom(indicator, str_indicator), m); orList.push_back(or_expr); expr_ref and_expr(ctx.mk_eq_atom(or_expr, ctx.mk_eq_atom(vLengthExpr, m_autil.mk_numeral(l, true))), m); andList.push_back(and_expr); } andList.push_back(mk_or(orList)); expr_ref implLhs(ctx.mk_eq_atom(testvar, str), m); expr_ref implRhs(mk_and(andList), m); assert_implication(implLhs, implRhs); } else { TRACE("str", tout << "already found existing length testers for " << mk_pp(v, m) << std::endl;); continue; } } } // foreach (v in vars) } // (s == "yes") } void theory_str::more_len_tests(expr * lenTester, zstring lenTesterValue) { ast_manager & m = get_manager(); if (lenTester_fvar_map.contains(lenTester)) { expr * fVar = lenTester_fvar_map[lenTester]; expr_ref toAssert(gen_len_val_options_for_free_var(fVar, lenTester, lenTesterValue), m); TRACE("str", tout << "asserting more length tests for free variable " << mk_ismt2_pp(fVar, m) << std::endl;); if (toAssert) { assert_axiom(toAssert); } } } void theory_str::more_value_tests(expr * valTester, zstring valTesterValue) { ast_manager & m = get_manager(); (void)m; expr * fVar = valueTester_fvar_map[valTester]; if (m_params.m_UseBinarySearch) { if (!binary_search_len_tester_stack.contains(fVar) || binary_search_len_tester_stack[fVar].empty()) { TRACE("str", tout << "WARNING: no active length testers for " << mk_pp(fVar, m) << std::endl;); NOT_IMPLEMENTED_YET(); } expr * effectiveLenInd = binary_search_len_tester_stack[fVar].back(); bool hasEqcValue; expr * len_indicator_value = get_eqc_value(effectiveLenInd, hasEqcValue); if (!hasEqcValue) { TRACE("str", tout << "WARNING: length tester " << mk_pp(effectiveLenInd, m) << " at top of stack for " << mk_pp(fVar, m) << " has no EQC value" << std::endl;); } else { // safety check zstring effectiveLenIndiStr; u.str.is_string(len_indicator_value, effectiveLenIndiStr); if (effectiveLenIndiStr == "more" || effectiveLenIndiStr == "less") { TRACE("str", tout << "ERROR: illegal state -- requesting 'more value tests' but a length tester is not yet concrete!" << std::endl;); UNREACHABLE(); } expr * valueAssert = gen_free_var_options(fVar, effectiveLenInd, effectiveLenIndiStr, valTester, valTesterValue); TRACE("str", tout << "asserting more value tests for free variable " << mk_ismt2_pp(fVar, m) << std::endl;); if (valueAssert != nullptr) { assert_axiom(valueAssert); } } } else { int lenTesterCount; if (fvar_lenTester_map.contains(fVar)) { lenTesterCount = fvar_lenTester_map[fVar].size(); } else { lenTesterCount = 0; } expr * effectiveLenInd = nullptr; zstring effectiveLenIndiStr = ""; for (int i = 0; i < lenTesterCount; ++i) { expr * len_indicator_pre = fvar_lenTester_map[fVar][i]; bool indicatorHasEqcValue = false; expr * len_indicator_value = get_eqc_value(len_indicator_pre, indicatorHasEqcValue); if (indicatorHasEqcValue) { zstring len_pIndiStr; u.str.is_string(len_indicator_value, len_pIndiStr); if (len_pIndiStr != "more") { effectiveLenInd = len_indicator_pre; effectiveLenIndiStr = len_pIndiStr; break; } } } expr * valueAssert = gen_free_var_options(fVar, effectiveLenInd, effectiveLenIndiStr, valTester, valTesterValue); TRACE("str", tout << "asserting more value tests for free variable " << mk_ismt2_pp(fVar, m) << std::endl;); if (valueAssert != nullptr) { assert_axiom(valueAssert); } } } bool theory_str::free_var_attempt(expr * nn1, expr * nn2) { zstring nn2_str; if (internal_lenTest_vars.contains(nn1) && u.str.is_string(nn2, nn2_str)) { TRACE("str", tout << "acting on equivalence between length tester var " << mk_pp(nn1, get_manager()) << " and constant " << mk_pp(nn2, get_manager()) << std::endl;); more_len_tests(nn1, nn2_str); return true; } else if (internal_valTest_vars.contains(nn1) && u.str.is_string(nn2, nn2_str)) { if (nn2_str == "more") { TRACE("str", tout << "acting on equivalence between value var " << mk_pp(nn1, get_manager()) << " and constant " << mk_pp(nn2, get_manager()) << std::endl;); more_value_tests(nn1, nn2_str); } return true; } else if (internal_unrollTest_vars.contains(nn1)) { return true; } else { return false; } } void theory_str::handle_equality(expr * lhs, expr * rhs) { ast_manager & m = get_manager(); context & ctx = get_context(); // both terms must be of sort String sort * lhs_sort = m.get_sort(lhs); sort * rhs_sort = m.get_sort(rhs); sort * str_sort = u.str.mk_string_sort(); // Pick up new terms added during the search (e.g. recursive function expansion). if (!existing_toplevel_exprs.contains(lhs)) { existing_toplevel_exprs.insert(lhs); set_up_axioms(lhs); propagate(); } if (!existing_toplevel_exprs.contains(rhs)) { existing_toplevel_exprs.insert(rhs); set_up_axioms(rhs); propagate(); } if (lhs_sort != str_sort || rhs_sort != str_sort) { TRACE("str", tout << "skip equality: not String sort" << std::endl;); return; } /* // temporarily disabled, we are borrowing these testers for something else if (m_params.m_FiniteOverlapModels && !finite_model_test_varlists.empty()) { if (finite_model_test_varlists.contains(lhs)) { finite_model_test(lhs, rhs); return; } else if (finite_model_test_varlists.contains(rhs)) { finite_model_test(rhs, lhs); return; } } */ if (free_var_attempt(lhs, rhs) || free_var_attempt(rhs, lhs)) { return; } if (u.str.is_concat(to_app(lhs)) && u.str.is_concat(to_app(rhs))) { bool nn1HasEqcValue = false; bool nn2HasEqcValue = false; expr * nn1_value = get_eqc_value(lhs, nn1HasEqcValue); expr * nn2_value = get_eqc_value(rhs, nn2HasEqcValue); if (nn1HasEqcValue && !nn2HasEqcValue) { simplify_parent(rhs, nn1_value); } if (!nn1HasEqcValue && nn2HasEqcValue) { simplify_parent(lhs, nn2_value); } expr * nn1_arg0 = to_app(lhs)->get_arg(0); expr * nn1_arg1 = to_app(lhs)->get_arg(1); expr * nn2_arg0 = to_app(rhs)->get_arg(0); expr * nn2_arg1 = to_app(rhs)->get_arg(1); if (nn1_arg0 == nn2_arg0 && in_same_eqc(nn1_arg1, nn2_arg1)) { TRACE("str", tout << "skip: lhs arg0 == rhs arg0" << std::endl;); return; } if (nn1_arg1 == nn2_arg1 && in_same_eqc(nn1_arg0, nn2_arg0)) { TRACE("str", tout << "skip: lhs arg1 == rhs arg1" << std::endl;); return; } } if (opt_DeferEQCConsistencyCheck) { TRACE("str", tout << "opt_DeferEQCConsistencyCheck is set; deferring new_eq_check call" << std::endl;); } else { // newEqCheck() -- check consistency wrt. existing equivalence classes if (!new_eq_check(lhs, rhs)) { return; } } // BEGIN new_eq_handler() in strTheory check_eqc_empty_string(lhs, rhs); instantiate_str_eq_length_axiom(ctx.get_enode(lhs), ctx.get_enode(rhs)); // group terms by equivalence class (groupNodeInEqc()) std::set eqc_concat_lhs; std::set eqc_var_lhs; std::set eqc_const_lhs; group_terms_by_eqc(lhs, eqc_concat_lhs, eqc_var_lhs, eqc_const_lhs); std::set eqc_concat_rhs; std::set eqc_var_rhs; std::set eqc_const_rhs; group_terms_by_eqc(rhs, eqc_concat_rhs, eqc_var_rhs, eqc_const_rhs); TRACE("str", tout << "lhs eqc:" << std::endl; tout << "Concats:" << std::endl; for (std::set::iterator it = eqc_concat_lhs.begin(); it != eqc_concat_lhs.end(); ++it) { expr * ex = *it; tout << mk_ismt2_pp(ex, get_manager()) << std::endl; } tout << "Variables:" << std::endl; for (std::set::iterator it = eqc_var_lhs.begin(); it != eqc_var_lhs.end(); ++it) { expr * ex = *it; tout << mk_ismt2_pp(ex, get_manager()) << std::endl; } tout << "Constants:" << std::endl; for (std::set::iterator it = eqc_const_lhs.begin(); it != eqc_const_lhs.end(); ++it) { expr * ex = *it; tout << mk_ismt2_pp(ex, get_manager()) << std::endl; } tout << "rhs eqc:" << std::endl; tout << "Concats:" << std::endl; for (std::set::iterator it = eqc_concat_rhs.begin(); it != eqc_concat_rhs.end(); ++it) { expr * ex = *it; tout << mk_ismt2_pp(ex, get_manager()) << std::endl; } tout << "Variables:" << std::endl; for (std::set::iterator it = eqc_var_rhs.begin(); it != eqc_var_rhs.end(); ++it) { expr * ex = *it; tout << mk_ismt2_pp(ex, get_manager()) << std::endl; } tout << "Constants:" << std::endl; for (std::set::iterator it = eqc_const_rhs.begin(); it != eqc_const_rhs.end(); ++it) { expr * ex = *it; tout << mk_ismt2_pp(ex, get_manager()) << std::endl; } ); // step 1: Concat == Concat check_eqc_concat_concat(eqc_concat_lhs, eqc_concat_rhs); // step 2: Concat == Constant 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.empty()) { expr* conStr = *(eqc_const_rhs.begin()); std::set::iterator itor1 = eqc_concat_lhs.begin(); for (; itor1 != eqc_concat_lhs.end(); itor1++) { solve_concat_eq_str(*itor1, conStr); } } // simplify parents wrt. the equivalence class of both sides bool nn1HasEqcValue = false; bool nn2HasEqcValue = false; // we want the Z3str2 eqc check here... expr * nn1_value = z3str2_get_eqc_value(lhs, nn1HasEqcValue); expr * nn2_value = z3str2_get_eqc_value(rhs, nn2HasEqcValue); if (nn1HasEqcValue && !nn2HasEqcValue) { simplify_parent(rhs, nn1_value); } if (!nn1HasEqcValue && nn2HasEqcValue) { simplify_parent(lhs, nn2_value); } expr * nn1EqConst = nullptr; std::set nn1EqUnrollFuncs; get_eqc_allUnroll(lhs, nn1EqConst, nn1EqUnrollFuncs); expr * nn2EqConst = nullptr; std::set nn2EqUnrollFuncs; get_eqc_allUnroll(rhs, nn2EqConst, nn2EqUnrollFuncs); if (nn2EqConst != nullptr) { for (std::set::iterator itor1 = nn1EqUnrollFuncs.begin(); itor1 != nn1EqUnrollFuncs.end(); itor1++) { process_unroll_eq_const_str(*itor1, nn2EqConst); } } if (nn1EqConst != nullptr) { for (std::set::iterator itor2 = nn2EqUnrollFuncs.begin(); itor2 != nn2EqUnrollFuncs.end(); itor2++) { process_unroll_eq_const_str(*itor2, nn1EqConst); } } } // Check that a string's length can be 0 iff it is the empty string. void theory_str::check_eqc_empty_string(expr * lhs, expr * rhs) { context & ctx = get_context(); ast_manager & m = get_manager(); rational nn1Len, nn2Len; bool nn1Len_exists = get_len_value(lhs, nn1Len); bool nn2Len_exists = get_len_value(rhs, nn2Len); expr_ref emptyStr(mk_string(""), m); if (nn1Len_exists && nn1Len.is_zero()) { if (!in_same_eqc(lhs, emptyStr) && rhs != emptyStr) { expr_ref eql(ctx.mk_eq_atom(mk_strlen(lhs), mk_int(0)), m); expr_ref eqr(ctx.mk_eq_atom(lhs, emptyStr), m); expr_ref toAssert(ctx.mk_eq_atom(eql, eqr), m); assert_axiom(toAssert); } } if (nn2Len_exists && nn2Len.is_zero()) { if (!in_same_eqc(rhs, emptyStr) && lhs != emptyStr) { expr_ref eql(ctx.mk_eq_atom(mk_strlen(rhs), mk_int(0)), m); expr_ref eqr(ctx.mk_eq_atom(rhs, emptyStr), m); expr_ref toAssert(ctx.mk_eq_atom(eql, eqr), m); assert_axiom(toAssert); } } } 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.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++) { if (eqc_concat_rhs.find(*itor1) != eqc_concat_rhs.end()) { hasCommon = 1; break; } } for (; itor2 != eqc_concat_rhs.end(); itor2++) { if (eqc_concat_lhs.find(*itor2) != eqc_concat_lhs.end()) { hasCommon = 1; break; } } if (hasCommon == 0) { if (opt_ConcatOverlapAvoid) { bool found = false; // check each pair and take the first ones that won't immediately overlap for (itor1 = eqc_concat_lhs.begin(); itor1 != eqc_concat_lhs.end() && !found; ++itor1) { expr * concat_lhs = *itor1; for (itor2 = eqc_concat_rhs.begin(); itor2 != eqc_concat_rhs.end() && !found; ++itor2) { expr * concat_rhs = *itor2; if (will_result_in_overlap(concat_lhs, concat_rhs)) { TRACE("str", tout << "Concats " << mk_pp(concat_lhs, m) << " and " << mk_pp(concat_rhs, m) << " will result in overlap; skipping." << std::endl;); } else { TRACE("str", tout << "Concats " << mk_pp(concat_lhs, m) << " and " << mk_pp(concat_rhs, m) << " won't overlap. Simplifying here." << std::endl;); simplify_concat_equality(concat_lhs, concat_rhs); found = true; break; } } } if (!found) { TRACE("str", tout << "All pairs of concats expected to overlap, falling back." << std::endl;); simplify_concat_equality(*(eqc_concat_lhs.begin()), *(eqc_concat_rhs.begin())); } } else { // default behaviour simplify_concat_equality(*(eqc_concat_lhs.begin()), *(eqc_concat_rhs.begin())); } } } } bool theory_str::is_var(expr * e) const { ast_manager & m = get_manager(); sort * ex_sort = m.get_sort(e); sort * str_sort = u.str.mk_string_sort(); // non-string-sort terms cannot be string variables if (ex_sort != str_sort) return false; // string constants cannot be variables if (u.str.is_string(e)) return false; if (u.str.is_concat(e) || u.str.is_at(e) || u.str.is_extract(e) || u.str.is_replace(e) || u.str.is_itos(e)) return false; if (m.is_ite(e)) return false; return true; } void theory_str::set_up_axioms(expr * ex) { ast_manager & m = get_manager(); context & ctx = get_context(); sort * ex_sort = m.get_sort(ex); sort * str_sort = u.str.mk_string_sort(); sort * bool_sort = m.mk_bool_sort(); family_id m_arith_fid = m.mk_family_id("arith"); sort * int_sort = m.mk_sort(m_arith_fid, INT_SORT); if (ex_sort == str_sort) { TRACE("str", tout << "setting up axioms for " << mk_ismt2_pp(ex, get_manager()) << ": expr is of sort String" << std::endl;); // set up basic string axioms enode * n = ctx.get_enode(ex); SASSERT(n); m_basicstr_axiom_todo.push_back(n); TRACE("str", tout << "add " << mk_pp(ex, m) << " to m_basicstr_axiom_todo" << std::endl;); if (is_app(ex)) { app * ap = to_app(ex); if (u.str.is_concat(ap)) { // if ex is a concat, set up concat axioms later m_concat_axiom_todo.push_back(n); // we also want to check whether we can eval this concat, // in case the rewriter did not totally finish with this term m_concat_eval_todo.push_back(n); } else if (u.str.is_length(ap)) { // if the argument is a variable, // keep track of this for later, we'll need it during model gen expr * var = ap->get_arg(0); app * aVar = to_app(var); if (aVar->get_num_args() == 0 && !u.str.is_string(aVar)) { input_var_in_len.insert(var); } } else if (u.str.is_at(ap) || u.str.is_extract(ap) || u.str.is_replace(ap)) { m_library_aware_axiom_todo.push_back(n); } else if (u.str.is_itos(ap)) { TRACE("str", tout << "found string-integer conversion term: " << mk_pp(ex, get_manager()) << std::endl;); string_int_conversion_terms.push_back(ap); m_library_aware_axiom_todo.push_back(n); } else if (is_var(ex)) { // if ex is a variable, add it to our list of variables TRACE("str", tout << "tracking variable " << mk_ismt2_pp(ap, get_manager()) << std::endl;); variable_set.insert(ex); ctx.mark_as_relevant(ex); // this might help?? theory_var v = mk_var(n); TRACE("str", tout << "variable " << mk_ismt2_pp(ap, get_manager()) << " is #" << v << std::endl;); (void)v; } } } else if (ex_sort == bool_sort && !is_quantifier(ex)) { TRACE("str", tout << "setting up axioms for " << mk_ismt2_pp(ex, get_manager()) << ": expr is of sort Bool" << std::endl;); // set up axioms for boolean terms ensure_enode(ex); if (ctx.e_internalized(ex)) { enode * n = ctx.get_enode(ex); SASSERT(n); if (is_app(ex)) { app * ap = to_app(ex); if (u.str.is_prefix(ap) || u.str.is_suffix(ap) || u.str.is_contains(ap) || u.str.is_in_re(ap)) { m_library_aware_axiom_todo.push_back(n); } } } else { TRACE("str", tout << "WARNING: Bool term " << mk_ismt2_pp(ex, get_manager()) << " not internalized. Delaying axiom setup to prevent a crash." << std::endl;); ENSURE(!search_started); // infinite loop prevention m_delayed_axiom_setup_terms.push_back(ex); return; } } else if (ex_sort == int_sort) { TRACE("str", tout << "setting up axioms for " << mk_ismt2_pp(ex, get_manager()) << ": expr is of sort Int" << std::endl;); // set up axioms for integer terms enode * n = ensure_enode(ex); SASSERT(n); if (is_app(ex)) { app * ap = to_app(ex); if (u.str.is_index(ap)) { m_library_aware_axiom_todo.push_back(n); } else if (u.str.is_stoi(ap)) { TRACE("str", tout << "found string-integer conversion term: " << mk_pp(ex, get_manager()) << std::endl;); string_int_conversion_terms.push_back(ap); m_library_aware_axiom_todo.push_back(n); } } } else { if (u.str.is_non_string_sequence(ex)) { TRACE("str", tout << "ERROR: Z3str3 does not support non-string sequence terms. Aborting." << std::endl;); m.raise_exception("Z3str3 does not support non-string sequence terms."); } TRACE("str", tout << "setting up axioms for " << mk_ismt2_pp(ex, get_manager()) << ": expr is of wrong sort, ignoring" << std::endl;); } // if expr is an application, recursively inspect all arguments if (is_app(ex)) { app * term = to_app(ex); unsigned num_args = term->get_num_args(); for (unsigned i = 0; i < num_args; i++) { set_up_axioms(term->get_arg(i)); } } } void theory_str::add_theory_assumptions(expr_ref_vector & assumptions) { TRACE("str", tout << "add overlap assumption for theory_str" << std::endl;); const char* strOverlap = "!!TheoryStrOverlapAssumption!!"; seq_util m_sequtil(get_manager()); sort * s = get_manager().mk_bool_sort(); m_theoryStrOverlapAssumption_term = expr_ref(mk_fresh_const(strOverlap, s), get_manager()); assumptions.push_back(get_manager().mk_not(m_theoryStrOverlapAssumption_term)); } lbool theory_str::validate_unsat_core(expr_ref_vector & unsat_core) { app * target_term = to_app(get_manager().mk_not(m_theoryStrOverlapAssumption_term)); get_context().internalize(target_term, false); enode* e1 = get_context().get_enode(target_term); for (unsigned i = 0; i < unsat_core.size(); ++i) { app * core_term = to_app(unsat_core.get(i)); // not sure if this is the correct way to compare terms in this context if (!get_context().e_internalized(core_term)) continue; enode *e2 = get_context().get_enode(core_term); if (e1 == e2) { TRACE("str", tout << "overlap detected in unsat core, changing UNSAT to UNKNOWN" << std::endl;); return l_undef; } } return l_false; } void theory_str::init_search_eh() { context & ctx = get_context(); TRACE("str", tout << "dumping all asserted formulas:" << std::endl; unsigned nFormulas = ctx.get_num_asserted_formulas(); for (unsigned i = 0; i < nFormulas; ++i) { expr * ex = ctx.get_asserted_formula(i); tout << mk_pp(ex, get_manager()) << (ctx.is_relevant(ex) ? " (rel)" : " (NOT REL)") << std::endl; } ); /* * Recursive descent through all asserted formulas to set up axioms. * Note that this is just the input structure and not necessarily things * that we know to be true or false. We're just doing this to see * which terms are explicitly mentioned. */ unsigned nFormulas = ctx.get_num_asserted_formulas(); for (unsigned i = 0; i < nFormulas; ++i) { expr * ex = ctx.get_asserted_formula(i); set_up_axioms(ex); } // this might be cheating but we need to make sure that certain maps are populated // before the first call to new_eq_eh() propagate(); TRACE("str", tout << "search started" << std::endl;); search_started = true; } void theory_str::new_eq_eh(theory_var x, theory_var y) { //TRACE("str", tout << "new eq: v#" << x << " = v#" << y << std::endl;); TRACE("str", tout << "new eq: " << mk_ismt2_pp(get_enode(x)->get_owner(), get_manager()) << " = " << mk_ismt2_pp(get_enode(y)->get_owner(), get_manager()) << std::endl;); /* if (m_find.find(x) == m_find.find(y)) { return; } */ handle_equality(get_enode(x)->get_owner(), get_enode(y)->get_owner()); // replicate Z3str2 behaviour: merge eqc **AFTER** handle_equality m_find.merge(x, y); } void theory_str::new_diseq_eh(theory_var x, theory_var y) { //TRACE("str", tout << "new diseq: v#" << x << " != v#" << y << std::endl;); TRACE("str", tout << "new diseq: " << mk_ismt2_pp(get_enode(x)->get_owner(), get_manager()) << " != " << mk_ismt2_pp(get_enode(y)->get_owner(), get_manager()) << std::endl;); } void theory_str::relevant_eh(app * n) { TRACE("str", tout << "relevant: " << mk_ismt2_pp(n, get_manager()) << std::endl;); } void theory_str::assign_eh(bool_var v, bool is_true) { expr * e = get_context().bool_var2expr(v); TRACE("str", tout << "assert: v" << v << " " << mk_pp(e, get_manager()) << " is_true: " << is_true << std::endl;); if (!existing_toplevel_exprs.contains(e)) { existing_toplevel_exprs.insert(e); set_up_axioms(e); propagate(); } } void theory_str::push_scope_eh() { theory::push_scope_eh(); m_trail_stack.push_scope(); sLevel += 1; TRACE("str", tout << "push to " << sLevel << std::endl;); TRACE_CODE(if (is_trace_enabled("t_str_dump_assign_on_scope_change")) { dump_assignments(); }); } void theory_str::recursive_check_variable_scope(expr * ex) { ast_manager & m = get_manager(); if (is_app(ex)) { app * a = to_app(ex); if (a->get_num_args() == 0) { // we only care about string variables sort * s = m.get_sort(ex); sort * string_sort = u.str.mk_string_sort(); if (s != string_sort) { return; } // base case: string constant / var if (u.str.is_string(a)) { return; } else { // assume var if (variable_set.find(ex) == variable_set.end() && internal_variable_set.find(ex) == internal_variable_set.end()) { TRACE("str", tout << "WARNING: possible reference to out-of-scope variable " << mk_pp(ex, m) << std::endl;); } } } else { for (unsigned i = 0; i < a->get_num_args(); ++i) { recursive_check_variable_scope(a->get_arg(i)); } } } } void theory_str::check_variable_scope() { if (!opt_CheckVariableScope) { return; } if (!is_trace_enabled("t_str_detail")) { return; } TRACE("str", tout << "checking scopes of variables in the current assignment" << std::endl;); context & ctx = get_context(); ast_manager & m = get_manager(); expr_ref_vector assignments(m); ctx.get_assignments(assignments); for (expr_ref_vector::iterator i = assignments.begin(); i != assignments.end(); ++i) { expr * ex = *i; recursive_check_variable_scope(ex); } } void theory_str::add_persisted_axiom(expr * a) { m_persisted_axioms.push_back(a); } void theory_str::pop_scope_eh(unsigned num_scopes) { sLevel -= num_scopes; TRACE("str", tout << "pop " << num_scopes << " to " << sLevel << std::endl;); TRACE_CODE(if (is_trace_enabled("t_str_dump_assign_on_scope_change")) { dump_assignments(); }); // list of expr* to remove from cut_var_map ptr_vector cutvarmap_removes; 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.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.empty()) { cutvarmap_removes.insert(varItor->m_key); } varItor++; } if (!cutvarmap_removes.empty()) { ptr_vector::iterator it = cutvarmap_removes.begin(); for (; it != cutvarmap_removes.end(); ++it) { expr * ex = *it; cut_var_map.remove(ex); } } ptr_vector new_m_basicstr; for (ptr_vector::iterator it = m_basicstr_axiom_todo.begin(); it != m_basicstr_axiom_todo.end(); ++it) { enode * e = *it; TRACE("str", tout << "consider deleting " << mk_pp(e->get_owner(), get_manager()) << ", enode scope level is " << e->get_iscope_lvl() << std::endl;); if (e->get_iscope_lvl() <= (unsigned)sLevel) { new_m_basicstr.push_back(e); } } m_basicstr_axiom_todo.reset(); m_basicstr_axiom_todo = new_m_basicstr; for (expr * e : m_persisted_axioms) { TRACE("str", tout << "persist axiom: " << mk_pp(e, get_manager()) << std::endl;); m_persisted_axiom_todo.push_back(e); } m_trail_stack.pop_scope(num_scopes); theory::pop_scope_eh(num_scopes); //check_variable_scope(); } void theory_str::dump_assignments() { TRACE_CODE( ast_manager & m = get_manager(); context & ctx = get_context(); tout << "dumping all assignments:" << std::endl; expr_ref_vector assignments(m); ctx.get_assignments(assignments); for (expr_ref_vector::iterator i = assignments.begin(); i != assignments.end(); ++i) { expr * ex = *i; tout << mk_ismt2_pp(ex, m) << (ctx.is_relevant(ex) ? "" : " (NOT REL)") << std::endl; } ); } // returns true if needle appears as a subterm anywhere under haystack, // or if needle appears in the same EQC as a subterm anywhere under haystack bool theory_str::term_appears_as_subterm(expr * needle, expr * haystack) { if (in_same_eqc(needle, haystack)) { return true; } if (is_app(haystack)) { app * a_haystack = to_app(haystack); for (unsigned i = 0; i < a_haystack->get_num_args(); ++i) { expr * subterm = a_haystack->get_arg(i); if (term_appears_as_subterm(needle, subterm)) { return true; } } } // not found return false; } void theory_str::classify_ast_by_type(expr * node, std::map & varMap, std::map & concatMap, std::map & unrollMap) { // check whether the node is a string variable; // testing set membership here bypasses several expensive checks. // note that internal variables don't count if they're only length tester / value tester vars. if (variable_set.find(node) != variable_set.end() && internal_lenTest_vars.find(node) == internal_lenTest_vars.end() && internal_valTest_vars.find(node) == internal_valTest_vars.end() && internal_unrollTest_vars.find(node) == internal_unrollTest_vars.end()) { if (varMap[node] != 1) { TRACE("str", tout << "new variable: " << mk_pp(node, get_manager()) << std::endl;); } varMap[node] = 1; } // check whether the node is a function that we want to inspect else if (is_app(node)) { app * aNode = to_app(node); if (u.str.is_length(aNode)) { // Length return; } else if (u.str.is_concat(aNode)) { expr * arg0 = aNode->get_arg(0); expr * arg1 = aNode->get_arg(1); bool arg0HasEq = false; bool arg1HasEq = false; expr * arg0Val = get_eqc_value(arg0, arg0HasEq); expr * arg1Val = get_eqc_value(arg1, arg1HasEq); int canskip = 0; zstring tmp; u.str.is_string(arg0Val, tmp); if (arg0HasEq && tmp.empty()) { canskip = 1; } u.str.is_string(arg1Val, tmp); if (canskip == 0 && arg1HasEq && tmp.empty()) { canskip = 1; } if (canskip == 0 && concatMap.find(node) == concatMap.end()) { concatMap[node] = 1; } } else if (u.re.is_unroll(aNode)) { // Unroll if (unrollMap.find(node) == unrollMap.end()) { unrollMap[node] = 1; } } // recursively visit all arguments for (unsigned i = 0; i < aNode->get_num_args(); ++i) { expr * arg = aNode->get_arg(i); classify_ast_by_type(arg, varMap, concatMap, unrollMap); } } } // NOTE: this function used to take an argument `Z3_ast node`; // it was not used and so was removed from the signature void theory_str::classify_ast_by_type_in_positive_context(std::map & varMap, std::map & concatMap, std::map & unrollMap) { context & ctx = get_context(); ast_manager & m = get_manager(); expr_ref_vector assignments(m); ctx.get_assignments(assignments); for (expr_ref_vector::iterator it = assignments.begin(); it != assignments.end(); ++it) { expr * argAst = *it; // the original code jumped through some hoops to check whether the AST node // is a function, then checked whether that function is "interesting". // however, the only thing that's considered "interesting" is an equality predicate. // so we bypass a huge amount of work by doing the following... if (m.is_eq(argAst)) { TRACE("str", tout << "eq ast " << mk_pp(argAst, m) << " is between args of sort " << m.get_sort(to_app(argAst)->get_arg(0))->get_name() << std::endl;); classify_ast_by_type(argAst, varMap, concatMap, unrollMap); } } } inline expr * theory_str::get_alias_index_ast(std::map & aliasIndexMap, expr * node) { if (aliasIndexMap.find(node) != aliasIndexMap.end()) return aliasIndexMap[node]; else return node; } inline expr * theory_str::getMostLeftNodeInConcat(expr * node) { app * aNode = to_app(node); if (!u.str.is_concat(aNode)) { return node; } else { expr * concatArgL = aNode->get_arg(0); return getMostLeftNodeInConcat(concatArgL); } } inline expr * theory_str::getMostRightNodeInConcat(expr * node) { app * aNode = to_app(node); if (!u.str.is_concat(aNode)) { return node; } else { expr * concatArgR = aNode->get_arg(1); return getMostRightNodeInConcat(concatArgR); } } void theory_str::trace_ctx_dep(std::ofstream & tout, std::map & aliasIndexMap, std::map & var_eq_constStr_map, std::map > & var_eq_concat_map, std::map > & var_eq_unroll_map, std::map & concat_eq_constStr_map, std::map > & concat_eq_concat_map, std::map > & unrollGroupMap) { #ifdef _TRACE context & ctx = get_context(); ast_manager & mgr = get_manager(); { tout << "(0) alias: variables" << std::endl; std::map > aliasSumMap; std::map::iterator itor0 = aliasIndexMap.begin(); for (; itor0 != aliasIndexMap.end(); itor0++) { aliasSumMap[itor0->second][itor0->first] = 1; } std::map >::iterator keyItor = aliasSumMap.begin(); for (; keyItor != aliasSumMap.end(); keyItor++) { tout << " * "; tout << mk_pp(keyItor->first, mgr); tout << " : "; std::map::iterator innerItor = keyItor->second.begin(); for (; innerItor != keyItor->second.end(); innerItor++) { tout << mk_pp(innerItor->first, mgr); tout << ", "; } tout << std::endl; } tout << std::endl; } { tout << "(1) var = constStr:" << std::endl; std::map::iterator itor1 = var_eq_constStr_map.begin(); for (; itor1 != var_eq_constStr_map.end(); itor1++) { tout << " * "; tout << mk_pp(itor1->first, mgr); tout << " = "; tout << mk_pp(itor1->second, mgr); if (!in_same_eqc(itor1->first, itor1->second)) { tout << " (not true in ctx)"; } tout << std::endl; } tout << std::endl; } { tout << "(2) var = concat:" << std::endl; std::map >::iterator itor2 = var_eq_concat_map.begin(); for (; itor2 != var_eq_concat_map.end(); itor2++) { tout << " * "; tout << mk_pp(itor2->first, mgr); tout << " = { "; std::map::iterator i_itor = itor2->second.begin(); for (; i_itor != itor2->second.end(); i_itor++) { tout << mk_pp(i_itor->first, mgr); tout << ", "; } tout << std::endl; } tout << std::endl; } { tout << "(3) var = unrollFunc:" << std::endl; std::map >::iterator itor2 = var_eq_unroll_map.begin(); for (; itor2 != var_eq_unroll_map.end(); itor2++) { tout << " * " << mk_pp(itor2->first, mgr) << " = { "; std::map::iterator i_itor = itor2->second.begin(); for (; i_itor != itor2->second.end(); i_itor++) { tout << mk_pp(i_itor->first, mgr) << ", "; } tout << " }" << std::endl; } tout << std::endl; } { tout << "(4) concat = constStr:" << std::endl; std::map::iterator itor3 = concat_eq_constStr_map.begin(); for (; itor3 != concat_eq_constStr_map.end(); itor3++) { tout << " * "; tout << mk_pp(itor3->first, mgr); tout << " = "; tout << mk_pp(itor3->second, mgr); tout << std::endl; } tout << std::endl; } { tout << "(5) eq concats:" << std::endl; std::map >::iterator itor4 = concat_eq_concat_map.begin(); for (; itor4 != concat_eq_concat_map.end(); itor4++) { if (itor4->second.size() > 1) { std::map::iterator i_itor = itor4->second.begin(); tout << " * "; for (; i_itor != itor4->second.end(); i_itor++) { tout << mk_pp(i_itor->first, mgr); tout << " , "; } tout << std::endl; } } tout << std::endl; } { tout << "(6) eq unrolls:" << std::endl; std::map >::iterator itor5 = unrollGroupMap.begin(); for (; itor5 != unrollGroupMap.end(); itor5++) { tout << " * "; std::set::iterator i_itor = itor5->second.begin(); for (; i_itor != itor5->second.end(); i_itor++) { tout << mk_pp(*i_itor, mgr) << ", "; } tout << std::endl; } tout << std::endl; } { tout << "(7) unroll = concats:" << std::endl; std::map >::iterator itor5 = unrollGroupMap.begin(); for (; itor5 != unrollGroupMap.end(); itor5++) { tout << " * "; expr * unroll = itor5->first; tout << mk_pp(unroll, mgr) << std::endl; enode * e_curr = ctx.get_enode(unroll); enode * e_curr_end = e_curr; do { app * curr = e_curr->get_owner(); if (u.str.is_concat(curr)) { tout << " >>> " << mk_pp(curr, mgr) << std::endl; } e_curr = e_curr->get_next(); } while (e_curr != e_curr_end); tout << std::endl; } tout << std::endl; } #else return; #endif // _TRACE } /* * Dependence analysis from current context assignment * - "freeVarMap" contains a set of variables that doesn't constrained by Concats. * But it's possible that it's bounded by unrolls * For the case of * (1) var1 = unroll(r1, t1) * var1 is in the freeVarMap * > should unroll r1 for var1 * (2) var1 = unroll(r1, t1) /\ var1 = Concat(var2, var3) * var2, var3 are all in freeVar * > should split the unroll function so that var2 and var3 are bounded by new unrolls */ int theory_str::ctx_dep_analysis(std::map & strVarMap, std::map & freeVarMap, std::map > & unrollGroupMap, std::map > & var_eq_concat_map) { std::map concatMap; std::map unrollMap; std::map aliasIndexMap; std::map var_eq_constStr_map; std::map concat_eq_constStr_map; std::map > var_eq_unroll_map; std::map > concat_eq_concat_map; std::map > depMap; context & ctx = get_context(); ast_manager & m = get_manager(); // note that the old API concatenated these assignments into // a massive conjunction; we may have the opportunity to avoid that here expr_ref_vector assignments(m); ctx.get_assignments(assignments); // Step 1: get variables / concat AST appearing in the context // the thing we iterate over should just be variable_set - internal_variable_set // so we avoid computing the set difference (but this might be slower) for (expr* var : variable_set) { //for(obj_hashtable::iterator it = variable_set.begin(); it != variable_set.end(); ++it) { //expr* var = *it; if (internal_variable_set.find(var) == internal_variable_set.end()) { TRACE("str", tout << "new variable: " << mk_pp(var, m) << std::endl;); strVarMap[var] = 1; } } classify_ast_by_type_in_positive_context(strVarMap, concatMap, unrollMap); std::map aliasUnrollSet; std::map::iterator unrollItor = unrollMap.begin(); for (; unrollItor != unrollMap.end(); ++unrollItor) { if (aliasUnrollSet.find(unrollItor->first) != aliasUnrollSet.end()) { continue; } expr * aRoot = nullptr; enode * e_currEqc = ctx.get_enode(unrollItor->first); enode * e_curr = e_currEqc; do { app * curr = e_currEqc->get_owner(); if (u.re.is_unroll(curr)) { if (aRoot == nullptr) { aRoot = curr; } aliasUnrollSet[curr] = aRoot; } e_currEqc = e_currEqc->get_next(); } while (e_currEqc != e_curr); } for (unrollItor = unrollMap.begin(); unrollItor != unrollMap.end(); unrollItor++) { expr * unrFunc = unrollItor->first; expr * urKey = aliasUnrollSet[unrFunc]; unrollGroupMap[urKey].insert(unrFunc); } // Step 2: collect alias relation // e.g. suppose we have the equivalence class {x, y, z}; // then we set aliasIndexMap[y] = x // and aliasIndexMap[z] = x std::map::iterator varItor = strVarMap.begin(); for (; varItor != strVarMap.end(); ++varItor) { if (aliasIndexMap.find(varItor->first) != aliasIndexMap.end()) { continue; } expr * aRoot = nullptr; expr * curr = varItor->first; do { if (variable_set.find(curr) != variable_set.end()) { if (aRoot == nullptr) { aRoot = curr; } else { aliasIndexMap[curr] = aRoot; } } curr = get_eqc_next(curr); } while (curr != varItor->first); } // Step 3: Collect interested cases varItor = strVarMap.begin(); for (; varItor != strVarMap.end(); ++varItor) { expr * deAliasNode = get_alias_index_ast(aliasIndexMap, varItor->first); // Case 1: variable = string constant // e.g. z = "str1" ::= var_eq_constStr_map[z] = "str1" if (var_eq_constStr_map.find(deAliasNode) == var_eq_constStr_map.end()) { bool nodeHasEqcValue = false; expr * nodeValue = get_eqc_value(deAliasNode, nodeHasEqcValue); if (nodeHasEqcValue) { var_eq_constStr_map[deAliasNode] = nodeValue; } } // Case 2: var_eq_concat // e.g. z = concat("str1", b) ::= var_eq_concat[z][concat(c, "str2")] = 1 // var_eq_unroll // e.g. z = unroll(...) ::= var_eq_unroll[z][unroll(...)] = 1 if (var_eq_concat_map.find(deAliasNode) == var_eq_concat_map.end()) { expr * curr = get_eqc_next(deAliasNode); while (curr != deAliasNode) { app * aCurr = to_app(curr); // collect concat if (u.str.is_concat(aCurr)) { expr * arg0 = aCurr->get_arg(0); expr * arg1 = aCurr->get_arg(1); bool arg0HasEqcValue = false; bool arg1HasEqcValue = false; expr * arg0_value = get_eqc_value(arg0, arg0HasEqcValue); expr * arg1_value = get_eqc_value(arg1, arg1HasEqcValue); bool is_arg0_emptyStr = false; if (arg0HasEqcValue) { zstring strval; u.str.is_string(arg0_value, strval); if (strval.empty()) { is_arg0_emptyStr = true; } } bool is_arg1_emptyStr = false; if (arg1HasEqcValue) { zstring strval; u.str.is_string(arg1_value, strval); if (strval.empty()) { is_arg1_emptyStr = true; } } if (!is_arg0_emptyStr && !is_arg1_emptyStr) { var_eq_concat_map[deAliasNode][curr] = 1; } } else if (u.re.is_unroll(to_app(curr))) { var_eq_unroll_map[deAliasNode][curr] = 1; } curr = get_eqc_next(curr); } } } // for(varItor in strVarMap) // -------------------------------------------------- // * collect aliasing relation among eq concats // e.g EQC={concat1, concat2, concat3} // concats_eq_Index_map[concat2] = concat1 // concats_eq_Index_map[concat3] = concat1 // -------------------------------------------------- std::map concats_eq_index_map; std::map::iterator concatItor = concatMap.begin(); for(; concatItor != concatMap.end(); ++concatItor) { if (concats_eq_index_map.find(concatItor->first) != concats_eq_index_map.end()) { continue; } expr * aRoot = nullptr; expr * curr = concatItor->first; do { if (u.str.is_concat(to_app(curr))) { if (aRoot == nullptr) { aRoot = curr; } else { concats_eq_index_map[curr] = aRoot; } } curr = get_eqc_next(curr); } while (curr != concatItor->first); } concatItor = concatMap.begin(); for(; concatItor != concatMap.end(); ++concatItor) { expr * deAliasConcat = nullptr; if (concats_eq_index_map.find(concatItor->first) != concats_eq_index_map.end()) { deAliasConcat = concats_eq_index_map[concatItor->first]; } else { deAliasConcat = concatItor->first; } // (3) concat_eq_conststr, e.g. concat(a,b) = "str1" if (concat_eq_constStr_map.find(deAliasConcat) == concat_eq_constStr_map.end()) { bool nodeHasEqcValue = false; expr * nodeValue = get_eqc_value(deAliasConcat, nodeHasEqcValue); if (nodeHasEqcValue) { concat_eq_constStr_map[deAliasConcat] = nodeValue; } } // (4) concat_eq_concat, e.g. // concat(a,b) = concat("str1", c) AND z = concat(a,b) AND z = concat(e,f) if (concat_eq_concat_map.find(deAliasConcat) == concat_eq_concat_map.end()) { expr * curr = deAliasConcat; do { if (u.str.is_concat(to_app(curr))) { // curr cannot be reduced if (concatMap.find(curr) != concatMap.end()) { concat_eq_concat_map[deAliasConcat][curr] = 1; } } curr = get_eqc_next(curr); } while (curr != deAliasConcat); } } // print some debugging info TRACE("str", trace_ctx_dep(tout, aliasIndexMap, var_eq_constStr_map, var_eq_concat_map, var_eq_unroll_map, concat_eq_constStr_map, concat_eq_concat_map, unrollGroupMap);); if (!contain_pair_bool_map.empty()) { compute_contains(aliasIndexMap, concats_eq_index_map, var_eq_constStr_map, concat_eq_constStr_map, var_eq_concat_map); } // step 4: dependence analysis // (1) var = string constant for (std::map::iterator itor = var_eq_constStr_map.begin(); itor != var_eq_constStr_map.end(); ++itor) { expr * var = get_alias_index_ast(aliasIndexMap, itor->first); expr * strAst = itor->second; depMap[var][strAst] = 1; } // (2) var = concat for (std::map >::iterator itor = var_eq_concat_map.begin(); itor != var_eq_concat_map.end(); ++itor) { expr * var = get_alias_index_ast(aliasIndexMap, itor->first); for (std::map::iterator itor1 = itor->second.begin(); itor1 != itor->second.end(); ++itor1) { expr * concat = itor1->first; std::map inVarMap; std::map inConcatMap; std::map inUnrollMap; classify_ast_by_type(concat, inVarMap, inConcatMap, inUnrollMap); for (std::map::iterator itor2 = inVarMap.begin(); itor2 != inVarMap.end(); ++itor2) { expr * varInConcat = get_alias_index_ast(aliasIndexMap, itor2->first); if (!(depMap[var].find(varInConcat) != depMap[var].end() && depMap[var][varInConcat] == 1)) { depMap[var][varInConcat] = 2; } } } } for (std::map >::iterator itor = var_eq_unroll_map.begin(); itor != var_eq_unroll_map.end(); itor++) { expr * var = get_alias_index_ast(aliasIndexMap, itor->first); for (std::map::iterator itor1 = itor->second.begin(); itor1 != itor->second.end(); itor1++) { expr * unrollFunc = itor1->first; std::map inVarMap; std::map inConcatMap; std::map inUnrollMap; classify_ast_by_type(unrollFunc, inVarMap, inConcatMap, inUnrollMap); for (std::map::iterator itor2 = inVarMap.begin(); itor2 != inVarMap.end(); itor2++) { expr * varInFunc = get_alias_index_ast(aliasIndexMap, itor2->first); TRACE("str", tout << "var in unroll = " << mk_ismt2_pp(itor2->first, m) << std::endl << "dealiased var = " << mk_ismt2_pp(varInFunc, m) << std::endl;); // it's possible that we have both (Unroll $$_regVar_0 $$_unr_0) /\ (Unroll abcd $$_unr_0), // while $$_regVar_0 = "abcd" // have to exclude such cases bool varHasValue = false; get_eqc_value(varInFunc, varHasValue); if (varHasValue) continue; if (depMap[var].find(varInFunc) == depMap[var].end()) { depMap[var][varInFunc] = 6; } } } } // (3) concat = string constant for (std::map::iterator itor = concat_eq_constStr_map.begin(); itor != concat_eq_constStr_map.end(); itor++) { expr * concatAst = itor->first; expr * constStr = itor->second; std::map inVarMap; std::map inConcatMap; std::map inUnrollMap; classify_ast_by_type(concatAst, inVarMap, inConcatMap, inUnrollMap); for (std::map::iterator itor2 = inVarMap.begin(); itor2 != inVarMap.end(); itor2++) { expr * varInConcat = get_alias_index_ast(aliasIndexMap, itor2->first); if (!(depMap[varInConcat].find(constStr) != depMap[varInConcat].end() && depMap[varInConcat][constStr] == 1)) depMap[varInConcat][constStr] = 3; } } // (4) equivalent concats // - possibility 1 : concat("str", v1) = concat(concat(v2, v3), v4) = concat(v5, v6) // ==> v2, v5 are constrained by "str" // - possibility 2 : concat(v1, "str") = concat(v2, v3) = concat(v4, v5) // ==> v2, v4 are constrained by "str" //-------------------------------------------------------------- std::map mostLeftNodes; std::map mostRightNodes; std::map mLIdxMap; std::map > mLMap; std::map mRIdxMap; std::map > mRMap; std::set nSet; for (std::map >::iterator itor = concat_eq_concat_map.begin(); itor != concat_eq_concat_map.end(); itor++) { mostLeftNodes.clear(); mostRightNodes.clear(); expr * mLConst = nullptr; expr * mRConst = nullptr; for (std::map::iterator itor1 = itor->second.begin(); itor1 != itor->second.end(); itor1++) { expr * concatNode = itor1->first; expr * mLNode = getMostLeftNodeInConcat(concatNode); zstring strval; if (u.str.is_string(to_app(mLNode), strval)) { if (mLConst == nullptr && strval.empty()) { mLConst = mLNode; } } else { mostLeftNodes[mLNode] = concatNode; } expr * mRNode = getMostRightNodeInConcat(concatNode); if (u.str.is_string(to_app(mRNode), strval)) { if (mRConst == nullptr && strval.empty()) { mRConst = mRNode; } } else { mostRightNodes[mRNode] = concatNode; } } if (mLConst != nullptr) { // ------------------------------------------------------------------------------------- // The left most variable in a concat is constrained by a constant string in eqc concat // ------------------------------------------------------------------------------------- // e.g. Concat(x, ...) = Concat("abc", ...) // ------------------------------------------------------------------------------------- for (std::map::iterator itor1 = mostLeftNodes.begin(); itor1 != mostLeftNodes.end(); itor1++) { expr * deVar = get_alias_index_ast(aliasIndexMap, itor1->first); if (depMap[deVar].find(mLConst) == depMap[deVar].end() || depMap[deVar][mLConst] != 1) { depMap[deVar][mLConst] = 4; } } } { // ------------------------------------------------------------------------------------- // The left most variables in eqc concats are constrained by each other // ------------------------------------------------------------------------------------- // e.g. concat(x, ...) = concat(u, ...) = ... // x and u are constrained by each other // ------------------------------------------------------------------------------------- nSet.clear(); std::map::iterator itl = mostLeftNodes.begin(); for (; itl != mostLeftNodes.end(); itl++) { bool lfHasEqcValue = false; get_eqc_value(itl->first, lfHasEqcValue); if (lfHasEqcValue) continue; expr * deVar = get_alias_index_ast(aliasIndexMap, itl->first); nSet.insert(deVar); } if (nSet.size() > 1) { int lId = -1; for (std::set::iterator itor2 = nSet.begin(); itor2 != nSet.end(); itor2++) { if (mLIdxMap.find(*itor2) != mLIdxMap.end()) { lId = mLIdxMap[*itor2]; break; } } if (lId == -1) lId = static_cast(mLMap.size()); for (std::set::iterator itor2 = nSet.begin(); itor2 != nSet.end(); itor2++) { bool itorHasEqcValue = false; get_eqc_value(*itor2, itorHasEqcValue); if (itorHasEqcValue) continue; mLIdxMap[*itor2] = lId; mLMap[lId].insert(*itor2); } } } if (mRConst != nullptr) { for (std::map::iterator itor1 = mostRightNodes.begin(); itor1 != mostRightNodes.end(); itor1++) { expr * deVar = get_alias_index_ast(aliasIndexMap, itor1->first); if (depMap[deVar].find(mRConst) == depMap[deVar].end() || depMap[deVar][mRConst] != 1) { depMap[deVar][mRConst] = 5; } } } { nSet.clear(); std::map::iterator itr = mostRightNodes.begin(); for (; itr != mostRightNodes.end(); itr++) { expr * deVar = get_alias_index_ast(aliasIndexMap, itr->first); nSet.insert(deVar); } if (nSet.size() > 1) { int rId = -1; std::set::iterator itor2 = nSet.begin(); for (; itor2 != nSet.end(); itor2++) { if (mRIdxMap.find(*itor2) != mRIdxMap.end()) { rId = mRIdxMap[*itor2]; break; } } if (rId == -1) rId = static_cast(mRMap.size()); for (itor2 = nSet.begin(); itor2 != nSet.end(); itor2++) { bool rHasEqcValue = false; get_eqc_value(*itor2, rHasEqcValue); if (rHasEqcValue) continue; mRIdxMap[*itor2] = rId; mRMap[rId].insert(*itor2); } } } } // print the dependence map TRACE("str", tout << "Dependence Map" << std::endl; for(std::map >::iterator itor = depMap.begin(); itor != depMap.end(); itor++) { tout << mk_pp(itor->first, m); rational nnLen; bool nnLen_exists = get_len_value(itor->first, nnLen); tout << " [len = " << (nnLen_exists ? nnLen.to_string() : "?") << "] \t-->\t"; for (std::map::iterator itor1 = itor->second.begin(); itor1 != itor->second.end(); itor1++) { tout << mk_pp(itor1->first, m) << "(" << itor1->second << "), "; } tout << std::endl; } ); // step, errr, 5: compute free variables based on the dependence map // the case dependence map is empty, every var in VarMap is free //--------------------------------------------------------------- // remove L/R most var in eq concat since they are constrained with each other std::map > lrConstrainedMap; for (std::map >::iterator itor = mLMap.begin(); itor != mLMap.end(); itor++) { for (std::set::iterator it1 = itor->second.begin(); it1 != itor->second.end(); it1++) { std::set::iterator it2 = it1; it2++; for (; it2 != itor->second.end(); it2++) { expr * n1 = *it1; expr * n2 = *it2; lrConstrainedMap[n1][n2] = 1; lrConstrainedMap[n2][n1] = 1; } } } for (std::map >::iterator itor = mRMap.begin(); itor != mRMap.end(); itor++) { for (std::set::iterator it1 = itor->second.begin(); it1 != itor->second.end(); it1++) { std::set::iterator it2 = it1; it2++; for (; it2 != itor->second.end(); it2++) { expr * n1 = *it1; expr * n2 = *it2; lrConstrainedMap[n1][n2] = 1; lrConstrainedMap[n2][n1] = 1; } } } 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 lrConstrained = 0; std::map::iterator lrit = freeVarMap.begin(); for (; lrit != freeVarMap.end(); lrit++) { if (lrConstrainedMap[var].find(lrit->first) != lrConstrainedMap[var].end()) { lrConstrained = 1; break; } } if (lrConstrained == 0) { freeVarMap[var] = 1; } } } } else { // if the keys in aliasIndexMap are not contained in keys in depMap, they are free // e.g., x= y /\ x = z /\ t = "abc" // aliasIndexMap[y]= x, aliasIndexMap[z] = x // depMap t ~ "abc"(1) // x should be free std::map::iterator itor2 = strVarMap.begin(); for (; itor2 != strVarMap.end(); itor2++) { if (aliasIndexMap.find(itor2->first) != aliasIndexMap.end()) { expr * var = aliasIndexMap[itor2->first]; if (depMap.find(var) == depMap.end()) { if (lrConstrainedMap.find(var) == lrConstrainedMap.end()) { freeVarMap[var] = 1; } else { int lrConstrained = 0; std::map::iterator lrit = freeVarMap.begin(); for (; lrit != freeVarMap.end(); lrit++) { if (lrConstrainedMap[var].find(lrit->first) != lrConstrainedMap[var].end()) { lrConstrained = 1; break; } } if (lrConstrained == 0) { freeVarMap[var] = 1; } } } } else if (aliasIndexMap.find(itor2->first) == aliasIndexMap.end()) { // if a variable is not in aliasIndexMap and not in depMap, it's free if (depMap.find(itor2->first) == depMap.end()) { expr * var = itor2->first; if (lrConstrainedMap.find(var) == lrConstrainedMap.end()) { freeVarMap[var] = 1; } else { int lrConstrained = 0; std::map::iterator lrit = freeVarMap.begin(); for (; lrit != freeVarMap.end(); lrit++) { if (lrConstrainedMap[var].find(lrit->first) != lrConstrainedMap[var].end()) { lrConstrained = 1; break; } } if (lrConstrained == 0) { freeVarMap[var] = 1; } } } } } std::map >::iterator itor = depMap.begin(); for (; itor != depMap.end(); itor++) { for (std::map::iterator itor1 = itor->second.begin(); itor1 != itor->second.end(); itor1++) { if (variable_set.find(itor1->first) != variable_set.end()) { // expr type = var expr * var = get_alias_index_ast(aliasIndexMap, itor1->first); // if a var is dep on itself and all dependence are type 2, it's a free variable // e.g {y --> x(2), y(2), m --> m(2), n(2)} y,m are free { if (depMap.find(var) == depMap.end()) { if (freeVarMap.find(var) == freeVarMap.end()) { if (lrConstrainedMap.find(var) == lrConstrainedMap.end()) { freeVarMap[var] = 1; } else { int lrConstrained = 0; std::map::iterator lrit = freeVarMap.begin(); for (; lrit != freeVarMap.end(); lrit++) { if (lrConstrainedMap[var].find(lrit->first) != lrConstrainedMap[var].end()) { lrConstrained = 1; break; } } if (lrConstrained == 0) { freeVarMap[var] = 1; } } } else { freeVarMap[var] = freeVarMap[var] + 1; } } } } } } } return 0; } // Check agreement between integer and string theories for the term a = (str.to-int S). // Returns true if axioms were added, and false otherwise. bool theory_str::finalcheck_str2int(app * a) { SASSERT(u.str.is_stoi(a)); bool axiomAdd = false; context & ctx = get_context(); ast_manager & m = get_manager(); expr * S = a->get_arg(0); // check integer theory rational Ival; bool Ival_exists = get_arith_value(a, Ival); if (Ival_exists) { TRACE("str", tout << "integer theory assigns " << mk_pp(a, m) << " = " << Ival.to_string() << std::endl;); // if that value is not -1, we can assert (str.to.int S) = Ival --> S = "Ival" if (!Ival.is_minus_one()) { zstring Ival_str(Ival.to_string().c_str()); expr_ref premise(ctx.mk_eq_atom(a, m_autil.mk_numeral(Ival, true)), m); expr_ref conclusion(ctx.mk_eq_atom(S, mk_string(Ival_str)), m); expr_ref axiom(rewrite_implication(premise, conclusion), m); if (!string_int_axioms.contains(axiom)) { string_int_axioms.insert(axiom); assert_axiom(axiom); m_trail_stack.push(insert_obj_trail(string_int_axioms, axiom)); axiomAdd = true; } } } else { TRACE("str", tout << "integer theory has no assignment for " << mk_pp(a, m) << std::endl;); expr_ref is_zero(ctx.mk_eq_atom(a, m_autil.mk_int(0)), m); /* literal is_zero_l = */ mk_literal(is_zero); axiomAdd = true; TRACE("str", ctx.display(tout);); // NOT_IMPLEMENTED_YET(); } bool S_hasEqcValue; expr * S_str = get_eqc_value(S, S_hasEqcValue); if (S_hasEqcValue) { zstring str; u.str.is_string(S_str, str); bool valid = true; rational convertedRepresentation(0); rational ten(10); if (str.length() == 0) { valid = false; } else { for (unsigned i = 0; i < str.length(); ++i) { if (!('0' <= str[i] && str[i] <= '9')) { valid = false; break; } else { // accumulate char digit = (int)str[i]; std::string sDigit(1, digit); int val = atoi(sDigit.c_str()); convertedRepresentation = (ten * convertedRepresentation) + rational(val); } } } // TODO this duplicates code a bit, we can simplify the branch on "conclusion" only if (valid) { expr_ref premise(ctx.mk_eq_atom(S, mk_string(str)), m); expr_ref conclusion(ctx.mk_eq_atom(a, m_autil.mk_numeral(convertedRepresentation, true)), m); expr_ref axiom(rewrite_implication(premise, conclusion), m); if (!string_int_axioms.contains(axiom)) { string_int_axioms.insert(axiom); assert_axiom(axiom); m_trail_stack.push(insert_obj_trail(string_int_axioms, axiom)); axiomAdd = true; } } else { expr_ref premise(ctx.mk_eq_atom(S, mk_string(str)), m); expr_ref conclusion(ctx.mk_eq_atom(a, m_autil.mk_numeral(rational::minus_one(), true)), m); expr_ref axiom(rewrite_implication(premise, conclusion), m); if (!string_int_axioms.contains(axiom)) { string_int_axioms.insert(axiom); assert_axiom(axiom); m_trail_stack.push(insert_obj_trail(string_int_axioms, axiom)); axiomAdd = true; } } } return axiomAdd; } bool theory_str::finalcheck_int2str(app * a) { bool axiomAdd = false; context & ctx = get_context(); ast_manager & m = get_manager(); expr * N = a->get_arg(0); // check string theory bool Sval_expr_exists; expr * Sval_expr = get_eqc_value(a, Sval_expr_exists); if (Sval_expr_exists) { zstring Sval; u.str.is_string(Sval_expr, Sval); TRACE("str", tout << "string theory assigns \"" << mk_pp(a, m) << " = " << Sval << "\n";); // empty string --> integer value < 0 if (Sval.empty()) { // ignore this. we should already assert the axiom for what happens when the string is "" } else { // nonempty string --> convert to correct integer value, or disallow it rational convertedRepresentation(0); rational ten(10); bool conversionOK = true; for (unsigned i = 0; i < Sval.length(); ++i) { char digit = (int)Sval[i]; if (isdigit((int)digit)) { std::string sDigit(1, digit); int val = atoi(sDigit.c_str()); convertedRepresentation = (ten * convertedRepresentation) + rational(val); } else { // not a digit, invalid TRACE("str", tout << "str.to-int argument contains non-digit character '" << digit << "'" << std::endl;); conversionOK = false; break; } } if (conversionOK) { expr_ref premise(ctx.mk_eq_atom(a, mk_string(Sval)), m); expr_ref conclusion(ctx.mk_eq_atom(N, m_autil.mk_numeral(convertedRepresentation, true)), m); expr_ref axiom(rewrite_implication(premise, conclusion), m); if (!string_int_axioms.contains(axiom)) { string_int_axioms.insert(axiom); assert_axiom(axiom); m_trail_stack.push(insert_obj_trail(string_int_axioms, axiom)); axiomAdd = true; } } else { expr_ref axiom(m.mk_not(ctx.mk_eq_atom(a, mk_string(Sval))), m); // always assert this axiom because this is a conflict clause assert_axiom(axiom); axiomAdd = true; } } } else { TRACE("str", tout << "string theory has no assignment for " << mk_pp(a, m) << std::endl;); NOT_IMPLEMENTED_YET(); } return axiomAdd; } void theory_str::collect_var_concat(expr * node, std::set & varSet, std::set & concatSet) { if (variable_set.find(node) != variable_set.end()) { if (internal_lenTest_vars.find(node) == internal_lenTest_vars.end()) { varSet.insert(node); } } else if (is_app(node)) { app * aNode = to_app(node); if (u.str.is_length(aNode)) { // Length return; } if (u.str.is_concat(aNode)) { if (concatSet.find(node) == concatSet.end()) { concatSet.insert(node); } } // recursively visit all arguments for (unsigned i = 0; i < aNode->get_num_args(); ++i) { expr * arg = aNode->get_arg(i); collect_var_concat(arg, varSet, concatSet); } } } bool theory_str::propagate_length_within_eqc(expr * var) { bool res = false; ast_manager & m = get_manager(); context & ctx = get_context(); TRACE("str", tout << "propagate_length_within_eqc: " << mk_ismt2_pp(var, m) << std::endl ;); rational varLen; if (! get_len_value(var, varLen)) { bool hasLen = false; expr * nodeWithLen= var; do { if (get_len_value(nodeWithLen, varLen)) { hasLen = true; break; } nodeWithLen = get_eqc_next(nodeWithLen); } while (nodeWithLen != var); if (hasLen) { // var = nodeWithLen --> |var| = |nodeWithLen| expr_ref_vector l_items(m); expr_ref varEqNode(ctx.mk_eq_atom(var, nodeWithLen), m); l_items.push_back(varEqNode); expr_ref nodeWithLenExpr (mk_strlen(nodeWithLen), m); expr_ref varLenExpr (mk_int(varLen), m); expr_ref lenEqNum(ctx.mk_eq_atom(nodeWithLenExpr, varLenExpr), m); l_items.push_back(lenEqNum); expr_ref axl(m.mk_and(l_items.size(), l_items.c_ptr()), m); expr_ref varLen(mk_strlen(var), m); expr_ref axr(ctx.mk_eq_atom(varLen, mk_int(varLen)), m); assert_implication(axl, axr); TRACE("str", tout << mk_ismt2_pp(axl, m) << std::endl << " ---> " << std::endl << mk_ismt2_pp(axr, m);); res = true; } } return res; } bool theory_str::propagate_length(std::set & varSet, std::set & concatSet, std::map & exprLenMap) { context & ctx = get_context(); ast_manager & m = get_manager(); expr_ref_vector assignments(m); ctx.get_assignments(assignments); bool axiomAdded = false; // collect all concats in context for (expr_ref_vector::iterator it = assignments.begin(); it != assignments.end(); ++it) { if (! ctx.is_relevant(*it)) { continue; } if (m.is_eq(*it)) { collect_var_concat(*it, varSet, concatSet); } } // iterate each concat // if a concat doesn't have length info, check if the length of all leaf nodes can be resolved for (std::set::iterator it = concatSet.begin(); it != concatSet.end(); it++) { expr * concat = *it; rational lenValue; expr_ref concatlenExpr (mk_strlen(concat), m) ; bool allLeafResolved = true; if (! get_arith_value(concatlenExpr, lenValue)) { // 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;); std::set leafNodes; get_unique_non_concat_nodes(concat, leafNodes); expr_ref_vector l_items(m); for (std::set::iterator leafIt = leafNodes.begin(); leafIt != leafNodes.end(); ++leafIt) { rational leafLenValue; if (get_len_value(*leafIt, leafLenValue)) { expr_ref leafItLenExpr (mk_strlen(*leafIt), m); expr_ref leafLenValueExpr (mk_int(leafLenValue), m); expr_ref lcExpr (ctx.mk_eq_atom(leafItLenExpr, leafLenValueExpr), m); l_items.push_back(lcExpr); } else { allLeafResolved = false; break; } } if (allLeafResolved) { expr_ref axl(m.mk_and(l_items.size(), l_items.c_ptr()), m); expr_ref lenValueExpr (mk_int(lenValue), m); expr_ref axr(ctx.mk_eq_atom(concatlenExpr, lenValueExpr), m); assert_implication(axl, axr); TRACE("str", tout << mk_ismt2_pp(axl, m) << std::endl << " ---> " << std::endl << mk_ismt2_pp(axr, m)<< std::endl;); axiomAdded = true; } } } } // if no concat length is propagated, check the length of variables. if (! axiomAdded) { for (std::set::iterator it = varSet.begin(); it != varSet.end(); it++) { expr * var = *it; rational lenValue; expr_ref varlen (mk_strlen(var), m) ; if (! get_arith_value(varlen, lenValue)) { if (propagate_length_within_eqc(var)) { axiomAdded = true; } } } } return axiomAdded; } void theory_str::get_unique_non_concat_nodes(expr * node, std::set & argSet) { app * a_node = to_app(node); if (!u.str.is_concat(a_node)) { argSet.insert(node); return; } else { SASSERT(a_node->get_num_args() == 2); expr * leftArg = a_node->get_arg(0); expr * rightArg = a_node->get_arg(1); get_unique_non_concat_nodes(leftArg, argSet); get_unique_non_concat_nodes(rightArg, argSet); } } final_check_status theory_str::final_check_eh() { context & ctx = get_context(); ast_manager & m = get_manager(); //expr_ref_vector assignments(m); //ctx.get_assignments(assignments); if (opt_VerifyFinalCheckProgress) { finalCheckProgressIndicator = false; } TRACE("str", tout << "final check" << std::endl;); TRACE_CODE(if (is_trace_enabled("t_str_dump_assign")) { dump_assignments(); }); check_variable_scope(); if (opt_DeferEQCConsistencyCheck) { TRACE("str", tout << "performing deferred EQC consistency check" << std::endl;); std::set eqc_roots; for (ptr_vector::const_iterator it = ctx.begin_enodes(); it != ctx.end_enodes(); ++it) { enode * e = *it; enode * root = e->get_root(); eqc_roots.insert(root); } bool found_inconsistency = false; for (std::set::iterator it = eqc_roots.begin(); it != eqc_roots.end(); ++it) { enode * e = *it; app * a = e->get_owner(); if (!(m.get_sort(a) == u.str.mk_string_sort())) { TRACE("str", tout << "EQC root " << mk_pp(a, m) << " not a string term; skipping" << std::endl;); } else { TRACE("str", tout << "EQC root " << mk_pp(a, m) << " is a string term. Checking this EQC" << std::endl;); // first call check_concat_len_in_eqc() on each member of the eqc enode * e_it = e; enode * e_root = e_it; do { bool status = check_concat_len_in_eqc(e_it->get_owner()); if (!status) { TRACE("str", tout << "concat-len check asserted an axiom on " << mk_pp(e_it->get_owner(), m) << std::endl;); found_inconsistency = true; } e_it = e_it->get_next(); } while (e_it != e_root); // now grab any two distinct elements from the EQC and call new_eq_check() on them enode * e1 = e; enode * e2 = e1->get_next(); if (e1 != e2) { TRACE("str", tout << "deferred new_eq_check() over EQC of " << mk_pp(e1->get_owner(), m) << " and " << mk_pp(e2->get_owner(), m) << std::endl;); bool result = new_eq_check(e1->get_owner(), e2->get_owner()); if (!result) { TRACE("str", tout << "new_eq_check found inconsistencies" << std::endl;); found_inconsistency = true; } } } } if (found_inconsistency) { TRACE("str", tout << "Found inconsistency in final check! Returning to search." << std::endl;); return FC_CONTINUE; } else { TRACE("str", tout << "Deferred consistency check passed. Continuing in final check." << std::endl;); } } // run dependence analysis to find free string variables std::map varAppearInAssign; std::map freeVar_map; std::map > unrollGroup_map; std::map > var_eq_concat_map; int conflictInDep = ctx_dep_analysis(varAppearInAssign, freeVar_map, unrollGroup_map, var_eq_concat_map); if (conflictInDep == -1) { return FC_DONE; } // enhancement: improved backpropagation of string constants into var=concat terms bool backpropagation_occurred = false; for (std::map >::iterator veqc_map_it = var_eq_concat_map.begin(); veqc_map_it != var_eq_concat_map.end(); ++veqc_map_it) { expr * var = veqc_map_it->first; for (std::map::iterator concat_map_it = veqc_map_it->second.begin(); concat_map_it != veqc_map_it->second.end(); ++concat_map_it) { app * concat = to_app(concat_map_it->first); expr * concat_lhs = concat->get_arg(0); expr * concat_rhs = concat->get_arg(1); // If the concat LHS and RHS both have a string constant in their EQC, // but the var does not, then we assert an axiom of the form // (lhs = "lhs" AND rhs = "rhs") --> (Concat lhs rhs) = "lhsrhs" bool concat_lhs_haseqc, concat_rhs_haseqc, var_haseqc; expr * concat_lhs_str = get_eqc_value(concat_lhs, concat_lhs_haseqc); expr * concat_rhs_str = get_eqc_value(concat_rhs, concat_rhs_haseqc); get_eqc_value(var, var_haseqc); if (concat_lhs_haseqc && concat_rhs_haseqc && !var_haseqc) { TRACE("str", tout << "backpropagate into " << mk_pp(var, m) << " = " << mk_pp(concat, m) << std::endl << "LHS ~= " << mk_pp(concat_lhs_str, m) << " RHS ~= " << mk_pp(concat_rhs_str, m) << std::endl;); zstring lhsString, rhsString; u.str.is_string(concat_lhs_str, lhsString); u.str.is_string(concat_rhs_str, rhsString); zstring concatString = lhsString + rhsString; // special handling: don't assert that string constants are equal to themselves expr_ref_vector lhs_terms(m); if (!u.str.is_string(concat_lhs)) { lhs_terms.push_back(ctx.mk_eq_atom(concat_lhs, concat_lhs_str)); } if (!u.str.is_string(concat_rhs)) { lhs_terms.push_back(ctx.mk_eq_atom(concat_rhs, concat_rhs_str)); } if (lhs_terms.empty()) { // no assumptions on LHS expr_ref rhs(ctx.mk_eq_atom(concat, mk_string(concatString)), m); assert_axiom(rhs); } else { expr_ref lhs(mk_and(lhs_terms), m); expr_ref rhs(ctx.mk_eq_atom(concat, mk_string(concatString)), m); assert_implication(lhs, rhs); } backpropagation_occurred = true; } } } if (backpropagation_occurred) { TRACE("str", tout << "Resuming search due to axioms added by backpropagation." << std::endl;); return FC_CONTINUE; } // enhancement: improved backpropagation of length information { std::set varSet; std::set concatSet; std::map exprLenMap; bool length_propagation_occurred = propagate_length(varSet, concatSet, exprLenMap); if (length_propagation_occurred) { TRACE("str", tout << "Resuming search due to axioms added by length propagation." << std::endl;); return FC_CONTINUE; } } // regex automata if (m_params.m_RegexAutomata) { solve_regex_automata(); } // RegexAutomata bool needToAssignFreeVars = false; std::set free_variables; std::set unused_internal_variables; { // Z3str2 free variables check std::map::iterator itor = varAppearInAssign.begin(); for (; itor != varAppearInAssign.end(); ++itor) { /* std::string vName = std::string(Z3_ast_to_string(ctx, itor->first)); if (vName.length() >= 3 && vName.substr(0, 3) == "$$_") continue; */ if (internal_variable_set.find(itor->first) != internal_variable_set.end() || regex_variable_set.find(itor->first) != regex_variable_set.end()) { // this can be ignored, I think TRACE("str", tout << "free internal variable " << mk_pp(itor->first, m) << " ignored" << std::endl;); continue; } bool hasEqcValue = false; get_eqc_value(itor->first, hasEqcValue); if (!hasEqcValue) { TRACE("str", tout << "found free variable " << mk_pp(itor->first, m) << std::endl;); needToAssignFreeVars = true; free_variables.insert(itor->first); // break; } else { // debug // TRACE("str", tout << "variable " << mk_pp(itor->first, m) << " = " << mk_pp(eqcString, m) << std::endl;); } } } if (!needToAssignFreeVars) { // check string-int terms bool addedStrIntAxioms = false; for (unsigned i = 0; i < string_int_conversion_terms.size(); ++i) { app * ex = to_app(string_int_conversion_terms[i].get()); if (u.str.is_stoi(ex)) { bool axiomAdd = finalcheck_str2int(ex); if (axiomAdd) { addedStrIntAxioms = true; } } else if (u.str.is_itos(ex)) { bool axiomAdd = finalcheck_int2str(ex); if (axiomAdd) { addedStrIntAxioms = true; } } else { UNREACHABLE(); } } if (addedStrIntAxioms) { TRACE("str", tout << "Resuming search due to addition of string-integer conversion axioms." << std::endl;); return FC_CONTINUE; } // We must be be 100% certain that if there are any regex constraints, // the string assignment for each variable is consistent with the automaton. // The (probably) easiest way to do this is to ensure // that we have path constraints set up for every assigned regex term. if (m_params.m_RegexAutomata && !regex_terms.empty()) { for (obj_hashtable::iterator it = regex_terms.begin(); it != regex_terms.end(); ++it) { expr * str_in_re = *it; expr * str; expr * re; u.str.is_in_re(str_in_re, str, re); lbool current_assignment = ctx.get_assignment(str_in_re); if (current_assignment == l_undef) { continue; } if (!regex_terms_with_path_constraints.contains(str_in_re)) { TRACE("str", tout << "assigned regex term " << mk_pp(str_in_re, m) << " has no path constraints -- continuing search" << std::endl;); return FC_CONTINUE; } } // foreach (str.in.re in regex_terms) } if (unused_internal_variables.empty()) { TRACE("str", tout << "All variables are assigned. Done!" << std::endl;); return FC_DONE; } else { TRACE("str", tout << "Assigning decoy values to free internal variables." << std::endl;); for (std::set::iterator it = unused_internal_variables.begin(); it != unused_internal_variables.end(); ++it) { expr * var = *it; expr_ref assignment(m.mk_eq(var, mk_string("**unused**")), m); assert_axiom(assignment); } return FC_CONTINUE; } } CTRACE("str", needToAssignFreeVars, tout << "Need to assign values to the following free variables:" << std::endl; for (expr* v : free_variables) { tout << mk_ismt2_pp(v, m) << std::endl; } tout << "freeVar_map has the following entries:" << std::endl; for (auto const& kv : freeVar_map) { expr * var = kv.first; tout << mk_ismt2_pp(var, m) << std::endl; } ); // ----------------------------------------------------------- // variables in freeVar are those not bounded by Concats // classify variables in freeVarMap: // (1) freeVar = unroll(r1, t1) // (2) vars are not bounded by either concat or unroll // ----------------------------------------------------------- std::map > fv_unrolls_map; std::set tmpSet; expr * constValue = nullptr; for (std::map::iterator fvIt2 = freeVar_map.begin(); fvIt2 != freeVar_map.end(); fvIt2++) { expr * var = fvIt2->first; tmpSet.clear(); get_eqc_allUnroll(var, constValue, tmpSet); if (!tmpSet.empty()) { fv_unrolls_map[var] = tmpSet; } } // erase var bounded by an unroll function from freeVar_map for (std::map >::iterator fvIt3 = fv_unrolls_map.begin(); fvIt3 != fv_unrolls_map.end(); fvIt3++) { expr * var = fvIt3->first; TRACE("str", tout << "erase free variable " << mk_pp(var, m) << " from freeVar_map, it is bounded by an Unroll" << std::endl;); freeVar_map.erase(var); } // collect the case: // * Concat(X, Y) = unroll(r1, t1) /\ Concat(X, Y) = unroll(r2, t2) // concatEqUnrollsMap[Concat(X, Y)] = {unroll(r1, t1), unroll(r2, t2)} std::map > concatEqUnrollsMap; for (std::map >::iterator urItor = unrollGroup_map.begin(); urItor != unrollGroup_map.end(); urItor++) { expr * unroll = urItor->first; expr * curr = unroll; do { if (u.str.is_concat(to_app(curr))) { concatEqUnrollsMap[curr].insert(unroll); concatEqUnrollsMap[curr].insert(unrollGroup_map[unroll].begin(), unrollGroup_map[unroll].end()); } enode * e_curr = ctx.get_enode(curr); curr = e_curr->get_next()->get_owner(); // curr = get_eqc_next(curr); } while (curr != unroll); } std::map > concatFreeArgsEqUnrollsMap; std::set fvUnrollSet; for (std::map >::iterator concatItor = concatEqUnrollsMap.begin(); concatItor != concatEqUnrollsMap.end(); concatItor++) { expr * concat = concatItor->first; expr * concatArg1 = to_app(concat)->get_arg(0); expr * concatArg2 = to_app(concat)->get_arg(1); bool arg1Bounded = false; bool arg2Bounded = false; // arg1 if (variable_set.find(concatArg1) != variable_set.end()) { if (freeVar_map.find(concatArg1) == freeVar_map.end()) { arg1Bounded = true; } else { fvUnrollSet.insert(concatArg1); } } else if (u.str.is_concat(to_app(concatArg1))) { if (concatEqUnrollsMap.find(concatArg1) == concatEqUnrollsMap.end()) { arg1Bounded = true; } } // arg2 if (variable_set.find(concatArg2) != variable_set.end()) { if (freeVar_map.find(concatArg2) == freeVar_map.end()) { arg2Bounded = true; } else { fvUnrollSet.insert(concatArg2); } } else if (u.str.is_concat(to_app(concatArg2))) { if (concatEqUnrollsMap.find(concatArg2) == concatEqUnrollsMap.end()) { arg2Bounded = true; } } if (!arg1Bounded && !arg2Bounded) { concatFreeArgsEqUnrollsMap[concat].insert( concatEqUnrollsMap[concat].begin(), concatEqUnrollsMap[concat].end()); } } for (std::set::iterator vItor = fvUnrollSet.begin(); vItor != fvUnrollSet.end(); vItor++) { TRACE("str", tout << "remove " << mk_pp(*vItor, m) << " from freeVar_map" << std::endl;); freeVar_map.erase(*vItor); } // Assign free variables std::set fSimpUnroll; constValue = nullptr; { TRACE("str", tout << "free var map (#" << freeVar_map.size() << "):" << std::endl; for (std::map::iterator freeVarItor1 = freeVar_map.begin(); freeVarItor1 != freeVar_map.end(); freeVarItor1++) { expr * freeVar = freeVarItor1->first; rational lenValue; bool lenValue_exists = get_len_value(freeVar, lenValue); tout << mk_pp(freeVar, m) << " [depCnt = " << freeVarItor1->second << ", length = " << (lenValue_exists ? lenValue.to_string() : "?") << "]" << std::endl; } ); } for (std::map >::iterator fvIt2 = concatFreeArgsEqUnrollsMap.begin(); fvIt2 != concatFreeArgsEqUnrollsMap.end(); fvIt2++) { expr * concat = fvIt2->first; for (std::set::iterator urItor = fvIt2->second.begin(); urItor != fvIt2->second.end(); urItor++) { expr * unroll = *urItor; process_concat_eq_unroll(concat, unroll); } } // -------- // experimental free variable assignment - begin // * special handling for variables that are not used in concat // -------- bool testAssign = true; if (!testAssign) { for (std::map::iterator fvIt = freeVar_map.begin(); fvIt != freeVar_map.end(); fvIt++) { expr * freeVar = fvIt->first; /* std::string vName = std::string(Z3_ast_to_string(ctx, freeVar)); if (vName.length() >= 9 && vName.substr(0, 9) == "$$_regVar") { continue; } */ expr * toAssert = gen_len_val_options_for_free_var(freeVar, nullptr, ""); if (toAssert != nullptr) { assert_axiom(toAssert); } } } else { process_free_var(freeVar_map); } // experimental free variable assignment - end // now deal with removed free variables that are bounded by an unroll TRACE("str", tout << "fv_unrolls_map (#" << fv_unrolls_map.size() << "):" << std::endl;); for (std::map >::iterator fvIt1 = fv_unrolls_map.begin(); fvIt1 != fv_unrolls_map.end(); fvIt1++) { expr * var = fvIt1->first; fSimpUnroll.clear(); get_eqc_simpleUnroll(var, constValue, fSimpUnroll); if (fSimpUnroll.empty()) { gen_assign_unroll_reg(fv_unrolls_map[var]); } else { expr * toAssert = gen_assign_unroll_Str2Reg(var, fSimpUnroll); if (toAssert != nullptr) { assert_axiom(toAssert); } } } if (opt_VerifyFinalCheckProgress && !finalCheckProgressIndicator) { TRACE("str", tout << "BUG: no progress in final check, giving up!!" << std::endl;); m.raise_exception("no progress in theory_str final check"); } return FC_CONTINUE; // since by this point we've added axioms } inline zstring int_to_string(int i) { std::stringstream ss; ss << i; std::string str = ss.str(); return zstring(str.c_str()); } inline std::string longlong_to_string(long long i) { std::stringstream ss; ss << i; return ss.str(); } void theory_str::print_value_tester_list(svector > & testerList) { TRACE("str", int ss = testerList.size(); tout << "valueTesterList = {"; for (int i = 0; i < ss; ++i) { if (i % 4 == 0) { tout << std::endl; } tout << "(" << testerList[i].first << ", "; tout << mk_pp(testerList[i].second, get_manager()); tout << "), "; } tout << std::endl << "}" << std::endl; ); } zstring theory_str::gen_val_string(int len, int_vector & encoding) { SASSERT(charSetSize > 0); SASSERT(!char_set.empty()); std::string re(len, char_set[0]); for (int i = 0; i < (int) encoding.size() - 1; i++) { int idx = encoding[i]; re[len - 1 - i] = char_set[idx]; } return zstring(re.c_str()); } /* * The return value indicates whether we covered the search space. * - If the next encoding is valid, return false * - Otherwise, return true */ bool theory_str::get_next_val_encode(int_vector & base, int_vector & next) { SASSERT(charSetSize > 0); TRACE("str", tout << "base vector: [ "; for (unsigned i = 0; i < base.size(); ++i) { tout << base[i] << " "; } tout << "]" << std::endl; ); int s = 0; int carry = 0; next.reset(); for (int i = 0; i < (int) base.size(); i++) { if (i == 0) { s = base[i] + 1; carry = s / charSetSize; s = s % charSetSize; next.push_back(s); } else { s = base[i] + carry; carry = s / charSetSize; s = s % charSetSize; next.push_back(s); } } if (next[next.size() - 1] > 0) { next.reset(); return true; } else { return false; } } expr* theory_str::gen_val_options(expr * freeVar, expr * len_indicator, expr * val_indicator, zstring lenStr, int tries) { ast_manager & m = get_manager(); context & ctx = get_context(); int distance = 32; // ---------------------------------------------------------------------------------------- // generate value options encoding // encoding is a vector of size (len + 1) // e.g, len = 2, // encoding {1, 2, 0} means the value option is "charSet[2]"."charSet[1]" // the last item in the encoding indicates whether the whole space is covered // for example, if the charSet = {a, b}. All valid encodings are // {0, 0, 0}, {1, 0, 0}, {0, 1, 0}, {1, 1, 0} // if add 1 to the last one, we get // {0, 0, 1} // the last item "1" shows this is not a valid encoding, and we have covered all space // ---------------------------------------------------------------------------------------- int len = atoi(lenStr.encode().c_str()); bool coverAll = false; vector options; int_vector base; TRACE("str", tout << "freeVar = " << mk_ismt2_pp(freeVar, m) << std::endl << "len_indicator = " << mk_ismt2_pp(len_indicator, m) << std::endl << "val_indicator = " << mk_ismt2_pp(val_indicator, m) << std::endl << "lenstr = " << lenStr << "\n" << "tries = " << tries << "\n"; if (m_params.m_AggressiveValueTesting) { tout << "note: aggressive value testing is enabled" << std::endl; } ); if (tries == 0) { base = int_vector(len + 1, 0); coverAll = false; } else { expr * lastestValIndi = fvar_valueTester_map[freeVar][len][tries - 1].second; TRACE("str", tout << "last value tester = " << mk_ismt2_pp(lastestValIndi, m) << std::endl;); coverAll = get_next_val_encode(val_range_map[lastestValIndi], base); } size_t l = (tries) * distance; size_t h = l; for (int i = 0; i < distance; i++) { if (coverAll) break; options.push_back(base); h++; coverAll = get_next_val_encode(options[options.size() - 1], base); } val_range_map.insert(val_indicator, options[options.size() - 1]); TRACE("str", tout << "value tester encoding " << "{" << std::endl; int_vector vec = val_range_map[val_indicator]; for (int_vector::iterator it = vec.begin(); it != vec.end(); ++it) { tout << *it << std::endl; } tout << "}" << std::endl; ); // ---------------------------------------------------------------------------------------- expr_ref_vector orList(m), andList(m); for (size_t i = l; i < h; i++) { orList.push_back(m.mk_eq(val_indicator, mk_string(longlong_to_string(i).c_str()) )); if (m_params.m_AggressiveValueTesting) { literal lit = mk_eq(val_indicator, mk_string(longlong_to_string(i).c_str()), false); ctx.mark_as_relevant(lit); ctx.force_phase(lit); } zstring aStr = gen_val_string(len, options[i - l]); expr * strAst; if (m_params.m_UseFastValueTesterCache) { if (!valueTesterCache.find(aStr, strAst)) { strAst = mk_string(aStr); valueTesterCache.insert(aStr, strAst); m_trail.push_back(strAst); } } else { strAst = mk_string(aStr); } andList.push_back(m.mk_eq(orList[orList.size() - 1].get(), m.mk_eq(freeVar, strAst))); } if (!coverAll) { orList.push_back(m.mk_eq(val_indicator, mk_string("more"))); if (m_params.m_AggressiveValueTesting) { literal l = mk_eq(val_indicator, mk_string("more"), false); ctx.mark_as_relevant(l); ctx.force_phase(~l); } } andList.push_back(mk_or(orList)); expr_ref valTestAssert = mk_and(andList); // --------------------------------------- // If the new value tester is $$_val_x_16_i // Should add ($$_len_x_j = 16) /\ ($$_val_x_16_i = "more") // --------------------------------------- andList.reset(); andList.push_back(m.mk_eq(len_indicator, mk_string(lenStr))); for (int i = 0; i < tries; i++) { expr * vTester = fvar_valueTester_map[freeVar][len][i].second; if (vTester != val_indicator) andList.push_back(m.mk_eq(vTester, mk_string("more"))); } expr_ref assertL = mk_and(andList); // (assertL => valTestAssert) <=> (!assertL OR valTestAssert) return m.mk_or(m.mk_not(assertL), valTestAssert); } expr * theory_str::gen_free_var_options(expr * freeVar, expr * len_indicator, zstring len_valueStr, expr * valTesterInCbEq, zstring valTesterValueStr) { ast_manager & m = get_manager(); int len = atoi(len_valueStr.encode().c_str()); // check whether any value tester is actually in scope TRACE("str", tout << "checking scope of previous value testers" << std::endl;); bool map_effectively_empty = true; if (fvar_valueTester_map.contains(freeVar) && fvar_valueTester_map[freeVar].find(len) != fvar_valueTester_map[freeVar].end()) { // there's *something* in the map, but check its scope for (auto const& entry : fvar_valueTester_map[freeVar][len]) { expr * aTester = entry.second; if (!internal_variable_set.contains(aTester)) { TRACE("str", tout << mk_pp(aTester, m) << " out of scope" << std::endl;); } else { TRACE("str", tout << mk_pp(aTester, m) << " in scope" << std::endl;); map_effectively_empty = false; break; } } } if (map_effectively_empty) { TRACE("str", tout << "no previous value testers, or none of them were in scope" << std::endl;); int tries = 0; expr * val_indicator = mk_internal_valTest_var(freeVar, len, tries); valueTester_fvar_map.insert(val_indicator, freeVar); if (!fvar_valueTester_map.contains(freeVar)) { fvar_valueTester_map.insert(freeVar, std::map > >()); } fvar_valueTester_map[freeVar][len].push_back(std::make_pair(sLevel, val_indicator)); print_value_tester_list(fvar_valueTester_map[freeVar][len]); return gen_val_options(freeVar, len_indicator, val_indicator, len_valueStr, tries); } else { TRACE("str", tout << "checking previous value testers" << std::endl;); print_value_tester_list(fvar_valueTester_map[freeVar][len]); // go through all previous value testers // If some doesn't have an eqc value, add its assertion again. int testerTotal = fvar_valueTester_map[freeVar][len].size(); int i = 0; for (; i < testerTotal; i++) { expr * aTester = fvar_valueTester_map[freeVar][len][i].second; // it's probably worth checking scope here, actually if (!internal_variable_set.contains(aTester)) { TRACE("str", tout << "value tester " << mk_pp(aTester, m) << " out of scope, skipping" << std::endl;); continue; } if (aTester == valTesterInCbEq) { break; } bool anEqcHasValue = false; get_eqc_value(aTester, anEqcHasValue); if (!anEqcHasValue) { TRACE("str", tout << "value tester " << mk_ismt2_pp(aTester, m) << " doesn't have an equivalence class value." << std::endl;); refresh_theory_var(aTester); expr_ref makeupAssert(gen_val_options(freeVar, len_indicator, aTester, len_valueStr, i), m); TRACE("str", tout << "var: " << mk_ismt2_pp(freeVar, m) << std::endl << mk_ismt2_pp(makeupAssert, m) << std::endl;); assert_axiom(makeupAssert); } else { // TRACE("str", tout << "value tester " << mk_ismt2_pp(aTester, m) // << " == " << mk_ismt2_pp(aTester_eqc_value, m) << std::endl;); } } if (valTesterValueStr == "more") { expr * valTester = nullptr; if (i + 1 < testerTotal) { valTester = fvar_valueTester_map[freeVar][len][i + 1].second; refresh_theory_var(valTester); } else { valTester = mk_internal_valTest_var(freeVar, len, i + 1); valueTester_fvar_map.insert(valTester, freeVar); fvar_valueTester_map[freeVar][len].push_back(std::make_pair(sLevel, valTester)); print_value_tester_list(fvar_valueTester_map[freeVar][len]); } return gen_val_options(freeVar, len_indicator, valTester, len_valueStr, i + 1); } return nullptr; } } void theory_str::reduce_virtual_regex_in(expr * var, expr * regex, expr_ref_vector & items) { context & ctx = get_context(); ast_manager & mgr = get_manager(); TRACE("str", tout << "reduce regex " << mk_pp(regex, mgr) << " with respect to variable " << mk_pp(var, mgr) << std::endl;); app * regexFuncDecl = to_app(regex); if (u.re.is_to_re(regexFuncDecl)) { // --------------------------------------------------------- // var \in Str2Reg(s1) // ==> // var = s1 /\ length(var) = length(s1) // --------------------------------------------------------- expr * strInside = to_app(regex)->get_arg(0); items.push_back(ctx.mk_eq_atom(var, strInside)); items.push_back(ctx.mk_eq_atom(mk_strlen(var), mk_strlen(strInside))); return; } // RegexUnion else if (u.re.is_union(regexFuncDecl)) { // --------------------------------------------------------- // var \in RegexUnion(r1, r2) // ==> // (var = newVar1 \/ var = newVar2) // (var = newVar1 --> length(var) = length(newVar1)) /\ (var = newVar2 --> length(var) = length(newVar2)) // /\ (newVar1 \in r1) /\ (newVar2 \in r2) // --------------------------------------------------------- expr_ref newVar1(mk_regex_rep_var(), mgr); expr_ref newVar2(mk_regex_rep_var(), mgr); items.push_back(mgr.mk_or(ctx.mk_eq_atom(var, newVar1), ctx.mk_eq_atom(var, newVar2))); items.push_back(mgr.mk_or( mgr.mk_not(ctx.mk_eq_atom(var, newVar1)), ctx.mk_eq_atom(mk_strlen(var), mk_strlen(newVar1)))); items.push_back(mgr.mk_or( mgr.mk_not(ctx.mk_eq_atom(var, newVar2)), ctx.mk_eq_atom(mk_strlen(var), mk_strlen(newVar2)))); expr * regArg1 = to_app(regex)->get_arg(0); reduce_virtual_regex_in(newVar1, regArg1, items); expr * regArg2 = to_app(regex)->get_arg(1); reduce_virtual_regex_in(newVar2, regArg2, items); return; } // RegexConcat else if (u.re.is_concat(regexFuncDecl)) { // --------------------------------------------------------- // var \in RegexConcat(r1, r2) // ==> // (var = newVar1 . newVar2) /\ (length(var) = length(vewVar1 . newVar2) ) // /\ (newVar1 \in r1) /\ (newVar2 \in r2) // --------------------------------------------------------- expr_ref newVar1(mk_regex_rep_var(), mgr); expr_ref newVar2(mk_regex_rep_var(), mgr); expr_ref concatAst(mk_concat(newVar1, newVar2), mgr); items.push_back(ctx.mk_eq_atom(var, concatAst)); items.push_back(ctx.mk_eq_atom(mk_strlen(var), m_autil.mk_add(mk_strlen(newVar1), mk_strlen(newVar2)))); expr * regArg1 = to_app(regex)->get_arg(0); reduce_virtual_regex_in(newVar1, regArg1, items); expr * regArg2 = to_app(regex)->get_arg(1); reduce_virtual_regex_in(newVar2, regArg2, items); return; } // Unroll else if (u.re.is_star(regexFuncDecl)) { // --------------------------------------------------------- // var \in Star(r1) // ==> // var = unroll(r1, t1) /\ |var| = |unroll(r1, t1)| // --------------------------------------------------------- expr * regArg = to_app(regex)->get_arg(0); expr_ref unrollCnt(mk_unroll_bound_var(), mgr); expr_ref unrollFunc(mk_unroll(regArg, unrollCnt), mgr); items.push_back(ctx.mk_eq_atom(var, unrollFunc)); items.push_back(ctx.mk_eq_atom(mk_strlen(var), mk_strlen(unrollFunc))); return; } // re.range else if (u.re.is_range(regexFuncDecl)) { // var in range("a", "z") // ==> // (var = "a" or var = "b" or ... or var = "z") expr_ref lo(regexFuncDecl->get_arg(0), mgr); expr_ref hi(regexFuncDecl->get_arg(1), mgr); zstring str_lo, str_hi; SASSERT(u.str.is_string(lo)); SASSERT(u.str.is_string(hi)); u.str.is_string(lo, str_lo); u.str.is_string(hi, str_hi); SASSERT(str_lo.length() == 1); SASSERT(str_hi.length() == 1); unsigned int c1 = str_lo[0]; unsigned int c2 = str_hi[0]; if (c1 > c2) { // exchange unsigned int tmp = c1; c1 = c2; c2 = tmp; } expr_ref_vector range_cases(mgr); for (unsigned int ch = c1; ch <= c2; ++ch) { zstring s_ch(ch); expr_ref rhs(ctx.mk_eq_atom(var, u.str.mk_string(s_ch)), mgr); range_cases.push_back(rhs); } expr_ref rhs(mk_or(range_cases), mgr); SASSERT(rhs); assert_axiom(rhs); return; } else { get_manager().raise_exception("unrecognized regex operator"); UNREACHABLE(); } } void theory_str::gen_assign_unroll_reg(std::set & unrolls) { context & ctx = get_context(); ast_manager & mgr = get_manager(); expr_ref_vector items(mgr); for (std::set::iterator itor = unrolls.begin(); itor != unrolls.end(); itor++) { expr * unrFunc = *itor; TRACE("str", tout << "generating assignment for unroll " << mk_pp(unrFunc, mgr) << std::endl;); expr * regexInUnr = to_app(unrFunc)->get_arg(0); expr * cntInUnr = to_app(unrFunc)->get_arg(1); items.reset(); rational low, high; bool low_exists = lower_bound(cntInUnr, low); (void)low_exists; bool high_exists = upper_bound(cntInUnr, high); (void)high_exists; TRACE("str", tout << "unroll " << mk_pp(unrFunc, mgr) << std::endl; rational unrLenValue; bool unrLenValue_exists = get_len_value(unrFunc, unrLenValue); tout << "unroll length: " << (unrLenValue_exists ? unrLenValue.to_string() : "?") << std::endl; rational cntInUnrValue; bool cntHasValue = get_arith_value(cntInUnr, cntInUnrValue); tout << "unroll count: " << (cntHasValue ? cntInUnrValue.to_string() : "?") << " low = " << (low_exists ? low.to_string() : "?") << " high = " << (high_exists ? high.to_string() : "?") << std::endl; ); expr_ref toAssert(mgr); if (low.is_neg()) { toAssert = m_autil.mk_ge(cntInUnr, mk_int(0)); } else { if (!unroll_var_map.contains(unrFunc)) { expr_ref newVar1(mk_regex_rep_var(), mgr); expr_ref newVar2(mk_regex_rep_var(), mgr); expr_ref concatAst(mk_concat(newVar1, newVar2), mgr); expr_ref newCnt(mk_unroll_bound_var(), mgr); expr_ref newUnrollFunc(mk_unroll(regexInUnr, newCnt), mgr); // unroll(r1, t1) = newVar1 . newVar2 items.push_back(ctx.mk_eq_atom(unrFunc, concatAst)); items.push_back(ctx.mk_eq_atom(mk_strlen(unrFunc), m_autil.mk_add(mk_strlen(newVar1), mk_strlen(newVar2)))); // mk_strlen(unrFunc) >= mk_strlen(newVar{1,2}) items.push_back(m_autil.mk_ge(m_autil.mk_add(mk_strlen(unrFunc), m_autil.mk_mul(mk_int(-1), mk_strlen(newVar1))), mk_int(0))); items.push_back(m_autil.mk_ge(m_autil.mk_add(mk_strlen(unrFunc), m_autil.mk_mul(mk_int(-1), mk_strlen(newVar2))), mk_int(0))); // newVar1 \in r1 reduce_virtual_regex_in(newVar1, regexInUnr, items); items.push_back(ctx.mk_eq_atom(cntInUnr, m_autil.mk_add(newCnt, mk_int(1)))); items.push_back(ctx.mk_eq_atom(newVar2, newUnrollFunc)); items.push_back(ctx.mk_eq_atom(mk_strlen(newVar2), mk_strlen(newUnrollFunc))); toAssert = ctx.mk_eq_atom( m_autil.mk_ge(cntInUnr, mk_int(1)), mk_and(items)); // option 0 expr_ref op0(ctx.mk_eq_atom(cntInUnr, mk_int(0)), mgr); expr_ref ast1(ctx.mk_eq_atom(unrFunc, mk_string("")), mgr); expr_ref ast2(ctx.mk_eq_atom(mk_strlen(unrFunc), mk_int(0)), mgr); expr_ref and1(mgr.mk_and(ast1, ast2), mgr); // put together toAssert = mgr.mk_and(ctx.mk_eq_atom(op0, and1), toAssert); unroll_var_map.insert(unrFunc, toAssert); } else { toAssert = unroll_var_map[unrFunc]; } } m_trail.push_back(toAssert); assert_axiom(toAssert); } } static int computeGCD(int x, int y) { if (x == 0) { return y; } while (y != 0) { if (x > y) { x = x - y; } else { y = y - x; } } return x; } static int computeLCM(int a, int b) { int temp = computeGCD(a, b); return temp ? (a / temp * b) : 0; } static zstring get_unrolled_string(zstring core, int count) { zstring res(""); for (int i = 0; i < count; i++) { res = res + core; } return res; } expr * theory_str::gen_assign_unroll_Str2Reg(expr * n, std::set & unrolls) { context & ctx = get_context(); ast_manager & mgr = get_manager(); int lcm = 1; int coreValueCount = 0; expr * oneUnroll = nullptr; zstring oneCoreStr(""); for (std::set::iterator itor = unrolls.begin(); itor != unrolls.end(); itor++) { expr * str2RegFunc = to_app(*itor)->get_arg(0); expr * coreVal = to_app(str2RegFunc)->get_arg(0); zstring coreStr; u.str.is_string(coreVal, coreStr); if (oneUnroll == nullptr) { oneUnroll = *itor; oneCoreStr = coreStr; } coreValueCount++; int core1Len = coreStr.length(); lcm = computeLCM(lcm, core1Len); } // bool canHaveNonEmptyAssign = true; expr_ref_vector litems(mgr); zstring lcmStr = get_unrolled_string(oneCoreStr, (lcm / oneCoreStr.length())); for (std::set::iterator itor = unrolls.begin(); itor != unrolls.end(); itor++) { expr * str2RegFunc = to_app(*itor)->get_arg(0); expr * coreVal = to_app(str2RegFunc)->get_arg(0); zstring coreStr; u.str.is_string(coreVal, coreStr); unsigned int core1Len = coreStr.length(); zstring uStr = get_unrolled_string(coreStr, (lcm / core1Len)); if (uStr != lcmStr) { canHaveNonEmptyAssign = false; } litems.push_back(ctx.mk_eq_atom(n, *itor)); } if (canHaveNonEmptyAssign) { return gen_unroll_conditional_options(n, unrolls, lcmStr); } else { expr_ref implyL(mk_and(litems), mgr); expr_ref implyR(ctx.mk_eq_atom(n, mk_string("")), mgr); // want to return (implyL -> implyR) expr * final_axiom = rewrite_implication(implyL, implyR); return final_axiom; } } expr * theory_str::gen_unroll_conditional_options(expr * var, std::set & unrolls, zstring lcmStr) { context & ctx = get_context(); ast_manager & mgr = get_manager(); int dist = opt_LCMUnrollStep; expr_ref_vector litems(mgr); expr_ref moreAst(mk_string("more"), mgr); for (expr * e : unrolls) { expr_ref item(ctx.mk_eq_atom(var, e), mgr); TRACE("str", tout << "considering unroll " << mk_pp(item, mgr) << std::endl;); litems.push_back(item); } // handle out-of-scope entries in unroll_tries_map ptr_vector outOfScopeTesters; for (expr * tester : unroll_tries_map[var][unrolls]) { bool inScope = internal_unrollTest_vars.contains(tester); TRACE("str", tout << "unroll test var " << mk_pp(tester, mgr) << (inScope ? " in scope" : " out of scope") << std::endl;); if (!inScope) { outOfScopeTesters.push_back(tester); } } for (expr* e : outOfScopeTesters) { unroll_tries_map[var][unrolls].erase(e); } if (unroll_tries_map[var][unrolls].empty()) { unroll_tries_map[var][unrolls].push_back(mk_unroll_test_var()); } int tries = unroll_tries_map[var][unrolls].size(); for (int i = 0; i < tries; i++) { expr * tester = unroll_tries_map[var][unrolls][i]; // TESTING refresh_theory_var(tester); bool testerHasValue = false; expr * testerVal = get_eqc_value(tester, testerHasValue); if (!testerHasValue) { // generate make-up assertion int l = i * dist; int h = (i + 1) * dist; expr_ref lImp(mk_and(litems), mgr); expr_ref rImp(gen_unroll_assign(var, lcmStr, tester, l, h), mgr); SASSERT(lImp); TRACE("str", tout << "lImp = " << mk_pp(lImp, mgr) << std::endl;); SASSERT(rImp); TRACE("str", tout << "rImp = " << mk_pp(rImp, mgr) << std::endl;); expr_ref toAssert(mgr.mk_or(mgr.mk_not(lImp), rImp), mgr); SASSERT(toAssert); TRACE("str", tout << "Making up assignments for variable which is equal to unbounded Unroll" << std::endl;); m_trail.push_back(toAssert); return toAssert; // note: this is how the code looks in Z3str2's strRegex.cpp:genUnrollConditionalOptions. // the return is in the same place // insert [tester = "more"] to litems so that the implyL for next tester is correct litems.push_back(ctx.mk_eq_atom(tester, moreAst)); } else { zstring testerStr; u.str.is_string(testerVal, testerStr); TRACE("str", tout << "Tester [" << mk_pp(tester, mgr) << "] = " << testerStr << "\n";); if (testerStr == "more") { litems.push_back(ctx.mk_eq_atom(tester, moreAst)); } } } expr * tester = mk_unroll_test_var(); unroll_tries_map[var][unrolls].push_back(tester); int l = tries * dist; int h = (tries + 1) * dist; expr_ref lImp(mk_and(litems), mgr); expr_ref rImp(gen_unroll_assign(var, lcmStr, tester, l, h), mgr); SASSERT(lImp); SASSERT(rImp); expr_ref toAssert(mgr.mk_or(mgr.mk_not(lImp), rImp), mgr); SASSERT(toAssert); TRACE("str", tout << "Generating assignment for variable which is equal to unbounded Unroll" << std::endl;); m_trail.push_back(toAssert); return toAssert; } expr * theory_str::gen_unroll_assign(expr * var, zstring lcmStr, expr * testerVar, int l, int h) { context & ctx = get_context(); ast_manager & mgr = get_manager(); TRACE("str", tout << "entry: var = " << mk_pp(var, mgr) << ", lcmStr = " << lcmStr << ", l = " << l << ", h = " << h << "\n";); if (m_params.m_AggressiveUnrollTesting) { TRACE("str", tout << "note: aggressive unroll testing is active" << std::endl;); } expr_ref_vector orItems(mgr); expr_ref_vector andItems(mgr); for (int i = l; i < h; i++) { zstring iStr = int_to_string(i); expr_ref testerEqAst(ctx.mk_eq_atom(testerVar, mk_string(iStr)), mgr); TRACE("str", tout << "testerEqAst = " << mk_pp(testerEqAst, mgr) << std::endl;); if (m_params.m_AggressiveUnrollTesting) { literal l = mk_eq(testerVar, mk_string(iStr), false); ctx.mark_as_relevant(l); ctx.force_phase(l); } orItems.push_back(testerEqAst); zstring unrollStrInstance = get_unrolled_string(lcmStr, i); expr_ref x1(ctx.mk_eq_atom(testerEqAst, ctx.mk_eq_atom(var, mk_string(unrollStrInstance))), mgr); TRACE("str", tout << "x1 = " << mk_pp(x1, mgr) << std::endl;); andItems.push_back(x1); expr_ref x2(ctx.mk_eq_atom(testerEqAst, ctx.mk_eq_atom(mk_strlen(var), mk_int(i * lcmStr.length()))), mgr); TRACE("str", tout << "x2 = " << mk_pp(x2, mgr) << std::endl;); andItems.push_back(x2); } expr_ref testerEqMore(ctx.mk_eq_atom(testerVar, mk_string("more")), mgr); TRACE("str", tout << "testerEqMore = " << mk_pp(testerEqMore, mgr) << std::endl;); if (m_params.m_AggressiveUnrollTesting) { literal l = mk_eq(testerVar, mk_string("more"), false); ctx.mark_as_relevant(l); ctx.force_phase(~l); } orItems.push_back(testerEqMore); int nextLowerLenBound = h * lcmStr.length(); expr_ref more2(ctx.mk_eq_atom(testerEqMore, //Z3_mk_ge(mk_length(t, var), mk_int(ctx, nextLowerLenBound)) m_autil.mk_ge(m_autil.mk_add(mk_strlen(var), mk_int(-1 * nextLowerLenBound)), mk_int(0)) ), mgr); TRACE("str", tout << "more2 = " << mk_pp(more2, mgr) << std::endl;); andItems.push_back(more2); expr_ref finalOR(mgr.mk_or(orItems.size(), orItems.c_ptr()), mgr); TRACE("str", tout << "finalOR = " << mk_pp(finalOR, mgr) << std::endl;); andItems.push_back(mk_or(orItems)); expr_ref finalAND(mgr.mk_and(andItems.size(), andItems.c_ptr()), mgr); TRACE("str", tout << "finalAND = " << mk_pp(finalAND, mgr) << std::endl;); // doing the following avoids a segmentation fault m_trail.push_back(finalAND); return finalAND; } expr * theory_str::gen_len_test_options(expr * freeVar, expr * indicator, int tries) { ast_manager & m = get_manager(); context & ctx = get_context(); expr_ref freeVarLen(mk_strlen(freeVar), m); SASSERT(freeVarLen); { rational freeVar_len_value; if (get_len_value(freeVar, freeVar_len_value)) { TRACE("str", tout << "special case: length of freeVar is known to be " << freeVar_len_value << std::endl;); expr_ref concreteOption(ctx.mk_eq_atom(indicator, mk_string(freeVar_len_value.to_string().c_str()) ), m); expr_ref concreteValue(ctx.mk_eq_atom( ctx.mk_eq_atom(indicator, mk_string(freeVar_len_value.to_string().c_str()) ), ctx.mk_eq_atom(freeVarLen, m_autil.mk_numeral(freeVar_len_value, true))), m); expr_ref finalAxiom(m.mk_and(concreteOption, concreteValue), m); SASSERT(finalAxiom); m_trail.push_back(finalAxiom); return finalAxiom; } } expr_ref_vector orList(m); expr_ref_vector andList(m); int distance = 3; int l = (tries - 1) * distance; int h = tries * distance; TRACE("str", tout << "building andList and orList" << std::endl; if (m_params.m_AggressiveLengthTesting) { tout << "note: aggressive length testing is active" << std::endl; } ); // experimental theory-aware case split support literal_vector case_split_literals; for (int i = l; i < h; ++i) { expr_ref str_indicator(m); if (m_params.m_UseFastLengthTesterCache) { rational ri(i); expr * lookup_val; if(lengthTesterCache.find(ri, lookup_val)) { str_indicator = expr_ref(lookup_val, m); } else { // no match; create and insert zstring i_str = int_to_string(i); expr_ref new_val(mk_string(i_str), m); lengthTesterCache.insert(ri, new_val); m_trail.push_back(new_val); str_indicator = expr_ref(new_val, m); } } else { zstring i_str = int_to_string(i); str_indicator = expr_ref(mk_string(i_str), m); } expr_ref or_expr(ctx.mk_eq_atom(indicator, str_indicator), m); orList.push_back(or_expr); double priority; // give high priority to small lengths if this is available if (i <= 5) { priority = 0.3; } else { // prioritize over "more" priority = 0.2; } add_theory_aware_branching_info(or_expr, priority, l_true); if (m_params.m_AggressiveLengthTesting) { literal l = mk_eq(indicator, str_indicator, false); ctx.mark_as_relevant(l); ctx.force_phase(l); } case_split_literals.insert(mk_eq(freeVarLen, mk_int(i), false)); expr_ref and_expr(ctx.mk_eq_atom(orList.get(orList.size() - 1), m.mk_eq(freeVarLen, mk_int(i))), m); andList.push_back(and_expr); } expr_ref more_option(ctx.mk_eq_atom(indicator, mk_string("more")), m); orList.push_back(more_option); // decrease priority of this option add_theory_aware_branching_info(more_option, -0.1, l_true); if (m_params.m_AggressiveLengthTesting) { literal l = mk_eq(indicator, mk_string("more"), false); ctx.mark_as_relevant(l); ctx.force_phase(~l); } andList.push_back(ctx.mk_eq_atom(orList.get(orList.size() - 1), m_autil.mk_ge(freeVarLen, mk_int(h)))); /* { // more experimental theory case split support expr_ref tmp(m_autil.mk_ge(freeVarLen, mk_int(h)), m); ctx.internalize(m_autil.mk_ge(freeVarLen, mk_int(h)), false); case_split_literals.push_back(ctx.get_literal(tmp)); ctx.mk_th_case_split(case_split_literals.size(), case_split_literals.c_ptr()); } */ expr_ref_vector or_items(m); expr_ref_vector and_items(m); for (unsigned i = 0; i < orList.size(); ++i) { or_items.push_back(orList.get(i)); } and_items.push_back(mk_or(or_items)); for(unsigned i = 0; i < andList.size(); ++i) { and_items.push_back(andList.get(i)); } TRACE("str", tout << "check: " << mk_pp(mk_and(and_items), m) << std::endl;); expr_ref lenTestAssert = mk_and(and_items); SASSERT(lenTestAssert); TRACE("str", tout << "crash avoidance lenTestAssert: " << mk_pp(lenTestAssert, m) << std::endl;); int testerCount = tries - 1; if (testerCount > 0) { expr_ref_vector and_items_LHS(m); expr_ref moreAst(mk_string("more"), m); for (int i = 0; i < testerCount; ++i) { expr * indicator = fvar_lenTester_map[freeVar][i]; if (internal_variable_set.find(indicator) == internal_variable_set.end()) { TRACE("str", tout << "indicator " << mk_pp(indicator, m) << " out of scope; continuing" << std::endl;); continue; } else { TRACE("str", tout << "indicator " << mk_pp(indicator, m) << " in scope" << std::endl;); and_items_LHS.push_back(ctx.mk_eq_atom(indicator, moreAst)); } } expr_ref assertL(mk_and(and_items_LHS), m); SASSERT(assertL); expr * finalAxiom = m.mk_or(m.mk_not(assertL), lenTestAssert.get()); SASSERT(finalAxiom != nullptr); TRACE("str", tout << "crash avoidance finalAxiom: " << mk_pp(finalAxiom, m) << std::endl;); return finalAxiom; } else { TRACE("str", tout << "crash avoidance lenTestAssert.get(): " << mk_pp(lenTestAssert.get(), m) << std::endl;); m_trail.push_back(lenTestAssert.get()); return lenTestAssert.get(); } } // Return an expression of the form // (tester = "less" | tester = "N" | tester = "more") & // (tester = "less" iff len(freeVar) < N) & (tester = "more" iff len(freeVar) > N) & (tester = "N" iff len(freeVar) = N)) expr_ref theory_str::binary_search_case_split(expr * freeVar, expr * tester, binary_search_info & bounds, literal_vector & case_split) { context & ctx = get_context(); ast_manager & m = get_manager(); rational N = bounds.midPoint; rational N_minus_one = N - rational::one(); rational N_plus_one = N + rational::one(); expr_ref lenFreeVar(mk_strlen(freeVar), m); TRACE("str", tout << "create case split for free var " << mk_pp(freeVar, m) << " over " << mk_pp(tester, m) << " with midpoint " << N << std::endl;); expr_ref_vector combinedCaseSplit(m); expr_ref_vector testerCases(m); expr_ref caseLess(ctx.mk_eq_atom(tester, mk_string("less")), m); testerCases.push_back(caseLess); combinedCaseSplit.push_back(ctx.mk_eq_atom(caseLess, m_autil.mk_le(lenFreeVar, m_autil.mk_numeral(N_minus_one, true) ))); expr_ref caseMore(ctx.mk_eq_atom(tester, mk_string("more")), m); testerCases.push_back(caseMore); combinedCaseSplit.push_back(ctx.mk_eq_atom(caseMore, m_autil.mk_ge(lenFreeVar, m_autil.mk_numeral(N_plus_one, true) ))); expr_ref caseEq(ctx.mk_eq_atom(tester, mk_string(N.to_string().c_str())), m); testerCases.push_back(caseEq); combinedCaseSplit.push_back(ctx.mk_eq_atom(caseEq, ctx.mk_eq_atom(lenFreeVar, m_autil.mk_numeral(N, true)))); combinedCaseSplit.push_back(mk_or(testerCases)); // force internalization on all terms in testerCases so we can extract literals for (unsigned i = 0; i < testerCases.size(); ++i) { expr * testerCase = testerCases.get(i); if (!ctx.b_internalized(testerCase)) { ctx.internalize(testerCase, false); } literal l = ctx.get_literal(testerCase); case_split.push_back(l); } expr_ref final_term(mk_and(combinedCaseSplit), m); SASSERT(final_term); TRACE("str", tout << "final term: " << mk_pp(final_term, m) << std::endl;); return final_term; } expr * theory_str::binary_search_length_test(expr * freeVar, expr * previousLenTester, zstring previousLenTesterValue) { ast_manager & m = get_manager(); if (binary_search_len_tester_stack.contains(freeVar) && !binary_search_len_tester_stack[freeVar].empty()) { TRACE("str", tout << "checking existing length testers for " << mk_pp(freeVar, m) << std::endl; for (ptr_vector::const_iterator it = binary_search_len_tester_stack[freeVar].begin(); it != binary_search_len_tester_stack[freeVar].end(); ++it) { expr * tester = *it; tout << mk_pp(tester, m) << ": "; if (binary_search_len_tester_info.contains(tester)) { binary_search_info & bounds = binary_search_len_tester_info[tester]; tout << "[" << bounds.lowerBound << " | " << bounds.midPoint << " | " << bounds.upperBound << "]!" << bounds.windowSize; } else { tout << "[WARNING: no bounds info available]"; } bool hasEqcValue; expr * testerEqcValue = get_eqc_value(tester, hasEqcValue); if (hasEqcValue) { tout << " = " << mk_pp(testerEqcValue, m); } else { tout << " [no eqc value]"; } tout << std::endl; } ); expr * lastTester = binary_search_len_tester_stack[freeVar].back(); bool lastTesterHasEqcValue; expr * lastTesterValue = get_eqc_value(lastTester, lastTesterHasEqcValue); zstring lastTesterConstant; if (!lastTesterHasEqcValue) { TRACE("str", tout << "length tester " << mk_pp(lastTester, m) << " at top of stack doesn't have an EQC value yet" << std::endl;); // check previousLenTester if (previousLenTester == lastTester) { lastTesterConstant = previousLenTesterValue; TRACE("str", tout << "invoked with previousLenTester info matching top of stack" << std::endl;); } else { TRACE("str", tout << "WARNING: unexpected reordering of length testers!" << std::endl;); UNREACHABLE(); return nullptr; } } else { u.str.is_string(lastTesterValue, lastTesterConstant); } TRACE("str", tout << "last length tester is assigned \"" << lastTesterConstant << "\"" << "\n";); if (lastTesterConstant == "more" || lastTesterConstant == "less") { // use the previous bounds info to generate a new midpoint binary_search_info lastBounds; if (!binary_search_len_tester_info.find(lastTester, lastBounds)) { // unexpected TRACE("str", tout << "WARNING: no bounds information available for last tester!" << std::endl;); UNREACHABLE(); } TRACE("str", tout << "last bounds are [" << lastBounds.lowerBound << " | " << lastBounds.midPoint << " | " << lastBounds.upperBound << "]!" << lastBounds.windowSize << std::endl;); binary_search_info newBounds; expr * newTester = nullptr; if (lastTesterConstant == "more") { // special case: if the midpoint, upper bound, and window size are all equal, // we double the window size and adjust the bounds if (lastBounds.midPoint == lastBounds.upperBound && lastBounds.upperBound == lastBounds.windowSize) { TRACE("str", tout << "search hit window size; expanding" << std::endl;); newBounds.lowerBound = lastBounds.windowSize + rational::one(); newBounds.windowSize = lastBounds.windowSize * rational(2); newBounds.upperBound = newBounds.windowSize; newBounds.calculate_midpoint(); } else if (false) { // handle the case where the midpoint can't be increased further // (e.g. a window like [50 | 50 | 50]!64 and we don't answer "50") } else { // general case newBounds.lowerBound = lastBounds.midPoint + rational::one(); newBounds.windowSize = lastBounds.windowSize; newBounds.upperBound = lastBounds.upperBound; newBounds.calculate_midpoint(); } if (!binary_search_next_var_high.find(lastTester, newTester)) { newTester = mk_internal_lenTest_var(freeVar, newBounds.midPoint.get_int32()); binary_search_next_var_high.insert(lastTester, newTester); } refresh_theory_var(newTester); } else if (lastTesterConstant == "less") { if (false) { // handle the case where the midpoint can't be decreased further // (e.g. a window like [0 | 0 | 0]!64 and we don't answer "0" } else { // general case newBounds.upperBound = lastBounds.midPoint - rational::one(); newBounds.windowSize = lastBounds.windowSize; newBounds.lowerBound = lastBounds.lowerBound; newBounds.calculate_midpoint(); } if (!binary_search_next_var_low.find(lastTester, newTester)) { newTester = mk_internal_lenTest_var(freeVar, newBounds.midPoint.get_int32()); binary_search_next_var_low.insert(lastTester, newTester); } refresh_theory_var(newTester); } TRACE("str", tout << "new bounds are [" << newBounds.lowerBound << " | " << newBounds.midPoint << " | " << newBounds.upperBound << "]!" << newBounds.windowSize << std::endl;); binary_search_len_tester_stack[freeVar].push_back(newTester); m_trail_stack.push(binary_search_trail(binary_search_len_tester_stack, freeVar)); binary_search_len_tester_info.insert(newTester, newBounds); m_trail_stack.push(insert_obj_map(binary_search_len_tester_info, newTester)); literal_vector case_split_literals; expr_ref next_case_split(binary_search_case_split(freeVar, newTester, newBounds, case_split_literals)); m_trail.push_back(next_case_split); // ctx.mk_th_case_split(case_split_literals.size(), case_split_literals.c_ptr()); return next_case_split; } else { // lastTesterConstant is a concrete value TRACE("str", tout << "length is fixed; generating models for free var" << std::endl;); // defensive check that this length did not converge on a negative value. binary_search_info lastBounds; if (!binary_search_len_tester_info.find(lastTester, lastBounds)) { // unexpected TRACE("str", tout << "WARNING: no bounds information available for last tester!" << std::endl;); UNREACHABLE(); } if (lastBounds.midPoint.is_neg()) { TRACE("str", tout << "WARNING: length search converged on a negative value. Negating this constraint." << std::endl;); expr_ref axiom(m_autil.mk_ge(mk_strlen(freeVar), m_autil.mk_numeral(rational::zero(), true)), m); return axiom; } // length is fixed expr * valueAssert = gen_free_var_options(freeVar, lastTester, lastTesterConstant, nullptr, zstring("")); return valueAssert; } } else { // no length testers yet TRACE("str", tout << "no length testers for " << mk_pp(freeVar, m) << std::endl;); binary_search_len_tester_stack.insert(freeVar, ptr_vector()); expr * firstTester; rational lowerBound(0); rational upperBound(m_params.m_BinarySearchInitialUpperBound); rational windowSize(upperBound); rational midPoint(floor(upperBound / rational(2))); if (!binary_search_starting_len_tester.find(freeVar, firstTester)) { firstTester = mk_internal_lenTest_var(freeVar, midPoint.get_int32()); binary_search_starting_len_tester.insert(freeVar, firstTester); } refresh_theory_var(firstTester); { rational freeVar_len_value; if (get_len_value(freeVar, freeVar_len_value)) { TRACE("str", tout << "special case: length of freeVar is known to be " << freeVar_len_value << std::endl;); midPoint = freeVar_len_value; upperBound = midPoint * 2; windowSize = upperBound; } } binary_search_len_tester_stack[freeVar].push_back(firstTester); m_trail_stack.push(binary_search_trail(binary_search_len_tester_stack, freeVar)); binary_search_info new_info(lowerBound, midPoint, upperBound, windowSize); binary_search_len_tester_info.insert(firstTester, new_info); m_trail_stack.push(insert_obj_map(binary_search_len_tester_info, firstTester)); literal_vector case_split_literals; expr_ref initial_case_split(binary_search_case_split(freeVar, firstTester, new_info, case_split_literals)); m_trail.push_back(initial_case_split); // ctx.mk_th_case_split(case_split_literals.size(), case_split_literals.c_ptr()); return initial_case_split; } } // ----------------------------------------------------------------------------------------------------- // True branch will be taken in final_check: // - When we discover a variable is "free" for the first time // lenTesterInCbEq = NULL // lenTesterValue = "" // False branch will be taken when invoked by new_eq_eh(). // - After we set up length tester for a "free" var in final_check, // when the tester is assigned to some value (e.g. "more" or "4"), // lenTesterInCbEq != NULL, and its value will be passed by lenTesterValue // The difference is that in new_eq_eh(), lenTesterInCbEq and its value have NOT been put into a same eqc // ----------------------------------------------------------------------------------------------------- expr * theory_str::gen_len_val_options_for_free_var(expr * freeVar, expr * lenTesterInCbEq, zstring lenTesterValue) { ast_manager & m = get_manager(); TRACE("str", tout << "gen for free var " << mk_ismt2_pp(freeVar, m) << std::endl;); if (m_params.m_UseBinarySearch) { TRACE("str", tout << "using binary search heuristic" << std::endl;); return binary_search_length_test(freeVar, lenTesterInCbEq, lenTesterValue); } else { bool map_effectively_empty = false; if (!fvar_len_count_map.contains(freeVar)) { TRACE("str", tout << "fvar_len_count_map is empty" << std::endl;); map_effectively_empty = true; } if (!map_effectively_empty) { // check whether any entries correspond to variables that went out of scope; // if every entry is out of scope then the map counts as being empty // assume empty and find a counterexample map_effectively_empty = true; if (fvar_lenTester_map.contains(freeVar)) { for (expr * indicator : fvar_lenTester_map[freeVar]) { if (internal_variable_set.find(indicator) != internal_variable_set.end()) { TRACE("str", tout <<"found active internal variable " << mk_ismt2_pp(indicator, m) << " in fvar_lenTester_map[freeVar]" << std::endl;); map_effectively_empty = false; break; } } } CTRACE("str", map_effectively_empty, tout << "all variables in fvar_lenTester_map[freeVar] out of scope" << std::endl;); } if (map_effectively_empty) { // no length assertions for this free variable have ever been added. TRACE("str", tout << "no length assertions yet" << std::endl;); fvar_len_count_map.insert(freeVar, 1); unsigned int testNum = fvar_len_count_map[freeVar]; expr_ref indicator(mk_internal_lenTest_var(freeVar, testNum), m); SASSERT(indicator); // since the map is "effectively empty", we can remove those variables that have left scope... if (!fvar_lenTester_map.contains(freeVar)) { fvar_lenTester_map.insert(freeVar, ptr_vector()); } fvar_lenTester_map[freeVar].shrink(0); fvar_lenTester_map[freeVar].push_back(indicator); lenTester_fvar_map.insert(indicator, freeVar); expr * lenTestAssert = gen_len_test_options(freeVar, indicator, testNum); SASSERT(lenTestAssert != nullptr); return lenTestAssert; } else { TRACE("str", tout << "found previous in-scope length assertions" << std::endl;); expr * effectiveLenInd = nullptr; zstring effectiveLenIndiStr(""); int lenTesterCount; if (fvar_lenTester_map.contains(freeVar)) { lenTesterCount = fvar_lenTester_map[freeVar].size(); } else { lenTesterCount = 0; } TRACE("str", tout << lenTesterCount << " length testers in fvar_lenTester_map[" << mk_pp(freeVar, m) << "]:" << std::endl; for (int i = 0; i < lenTesterCount; ++i) { expr * len_indicator = fvar_lenTester_map[freeVar][i]; tout << mk_pp(len_indicator, m) << ": "; bool effectiveInScope = (internal_variable_set.find(len_indicator) != internal_variable_set.end()); tout << (effectiveInScope ? "in scope" : "NOT in scope"); tout << std::endl; } ); int i = 0; for (; i < lenTesterCount; ++i) { expr * len_indicator_pre = fvar_lenTester_map[freeVar][i]; // check whether this is in scope as well if (internal_variable_set.find(len_indicator_pre) == internal_variable_set.end()) { TRACE("str", tout << "length indicator " << mk_pp(len_indicator_pre, m) << " not in scope" << std::endl;); continue; } bool indicatorHasEqcValue = false; expr * len_indicator_value = get_eqc_value(len_indicator_pre, indicatorHasEqcValue); TRACE("str", tout << "length indicator " << mk_ismt2_pp(len_indicator_pre, m) << " = " << mk_ismt2_pp(len_indicator_value, m) << std::endl;); if (indicatorHasEqcValue) { zstring len_pIndiStr; u.str.is_string(len_indicator_value, len_pIndiStr); if (len_pIndiStr != "more") { effectiveLenInd = len_indicator_pre; effectiveLenIndiStr = len_pIndiStr; break; } } else { if (lenTesterInCbEq != len_indicator_pre) { TRACE("str", tout << "WARNING: length indicator " << mk_ismt2_pp(len_indicator_pre, m) << " does not have an equivalence class value." << " i = " << i << ", lenTesterCount = " << lenTesterCount << std::endl;); if (i > 0) { effectiveLenInd = fvar_lenTester_map[freeVar][i - 1]; bool effectiveHasEqcValue; expr * effective_eqc_value = get_eqc_value(effectiveLenInd, effectiveHasEqcValue); bool effectiveInScope = (internal_variable_set.find(effectiveLenInd) != internal_variable_set.end()); TRACE("str", tout << "checking effective length indicator " << mk_pp(effectiveLenInd, m) << ": " << (effectiveInScope ? "in scope" : "NOT in scope") << ", "; if (effectiveHasEqcValue) { tout << "~= " << mk_pp(effective_eqc_value, m); } else { tout << "no eqc string constant"; } tout << std::endl;); (void)effectiveInScope; if (effectiveLenInd == lenTesterInCbEq) { effectiveLenIndiStr = lenTesterValue; } else { if (effectiveHasEqcValue) { u.str.is_string(effective_eqc_value, effectiveLenIndiStr); } else { NOT_IMPLEMENTED_YET(); } } } break; } // lenTesterInCbEq == len_indicator_pre else { if (lenTesterValue != "more") { effectiveLenInd = len_indicator_pre; effectiveLenIndiStr = lenTesterValue; break; } } } // !indicatorHasEqcValue } // for (i : [0..lenTesterCount-1]) if (effectiveLenIndiStr == "more" || effectiveLenIndiStr == "") { TRACE("str", tout << "length is not fixed; generating length tester options for free var" << std::endl;); expr_ref indicator(m); unsigned int testNum = 0; TRACE("str", tout << "effectiveLenIndiStr = " << effectiveLenIndiStr << ", i = " << i << ", lenTesterCount = " << lenTesterCount << "\n";); if (i == lenTesterCount) { fvar_len_count_map[freeVar] = fvar_len_count_map[freeVar] + 1; testNum = fvar_len_count_map[freeVar]; indicator = mk_internal_lenTest_var(freeVar, testNum); fvar_lenTester_map[freeVar].push_back(indicator); lenTester_fvar_map.insert(indicator, freeVar); } else { indicator = fvar_lenTester_map[freeVar][i]; refresh_theory_var(indicator); testNum = i + 1; } expr * lenTestAssert = gen_len_test_options(freeVar, indicator, testNum); SASSERT(lenTestAssert != nullptr); return lenTestAssert; } else { // if we are performing automata-based reasoning and the term associated with // this length tester is in any way constrained by regex terms, // do not perform value testing -- this term is not a free variable. if (m_params.m_RegexAutomata) { std::map, expr*>::iterator it = regex_in_bool_map.begin(); for (; it != regex_in_bool_map.end(); ++it) { expr * re = it->second; expr * re_str = to_app(re)->get_arg(0); // recursive descent through all subterms of re_str to see if any of them are // - the same as freeVar // - in the same EQC as freeVar if (term_appears_as_subterm(freeVar, re_str)) { TRACE("str", tout << "prevent value testing on free var " << mk_pp(freeVar, m) << " as it belongs to one or more regex constraints." << std::endl;); return nullptr; } } } TRACE("str", tout << "length is fixed; generating models for free var" << std::endl;); // length is fixed expr * valueAssert = gen_free_var_options(freeVar, effectiveLenInd, effectiveLenIndiStr, nullptr, zstring("")); return valueAssert; } } // fVarLenCountMap.find(...) } // !UseBinarySearch } void theory_str::get_concats_in_eqc(expr * n, std::set & concats) { expr * eqcNode = n; do { if (u.str.is_concat(to_app(eqcNode))) { concats.insert(eqcNode); } eqcNode = get_eqc_next(eqcNode); } while (eqcNode != n); } void theory_str::get_var_in_eqc(expr * n, std::set & varSet) { expr * eqcNode = n; do { if (variable_set.find(eqcNode) != variable_set.end()) { varSet.insert(eqcNode); } eqcNode = get_eqc_next(eqcNode); } while (eqcNode != n); } bool cmpvarnames(expr * lhs, expr * rhs) { symbol lhs_name = to_app(lhs)->get_decl()->get_name(); symbol rhs_name = to_app(rhs)->get_decl()->get_name(); return lhs_name.str() < rhs_name.str(); } void theory_str::process_free_var(std::map & freeVar_map) { context & ctx = get_context(); obj_hashtable eqcRepSet; obj_hashtable leafVarSet; std::map > aloneVars; for (auto const& kv : freeVar_map) { expr * freeVar = kv.first; // skip all regular expression vars if (regex_variable_set.contains(freeVar)) { continue; } // Iterate the EQC of freeVar, its eqc variable should not be in the eqcRepSet. // If found, have to filter it out std::set eqVarSet; get_var_in_eqc(freeVar, eqVarSet); bool duplicated = false; expr * dupVar = nullptr; for (expr* eq : eqVarSet) { if (eqcRepSet.contains(eq)) { duplicated = true; dupVar = eq; break; } } if (duplicated && dupVar != nullptr) { TRACE("str", tout << "Duplicated free variable found:" << mk_pp(freeVar, get_manager()) << " = " << mk_ismt2_pp(dupVar, get_manager()) << " (SKIP)" << std::endl;); continue; } else { eqcRepSet.insert(freeVar); } } for (expr * freeVar : eqcRepSet) { // has length constraint initially bool standAlone = !input_var_in_len.contains(freeVar); // iterate parents if (standAlone) { // I hope this works! if (!ctx.e_internalized(freeVar)) { ctx.internalize(freeVar, false); } enode * e_freeVar = ctx.get_enode(freeVar); enode_vector::iterator it = e_freeVar->begin_parents(); for (; it != e_freeVar->end_parents(); ++it) { expr * parentAst = (*it)->get_owner(); if (u.str.is_concat(to_app(parentAst))) { standAlone = false; break; } } } if (standAlone) { rational len_value; bool len_value_exists = get_len_value(freeVar, len_value); if (len_value_exists) { leafVarSet.insert(freeVar); } else { aloneVars[-1].insert(freeVar); } } else { leafVarSet.insert(freeVar); } } for (expr* lv : leafVarSet) { expr * toAssert = gen_len_val_options_for_free_var(lv, nullptr, ""); // gen_len_val_options_for_free_var() can legally return NULL, // as methods that it calls may assert their own axioms instead. if (toAssert) assert_axiom(toAssert); } for (auto const& kv : aloneVars) { for (expr* av : kv.second) { expr * toAssert = gen_len_val_options_for_free_var(av, nullptr, ""); // same deal with returning a NULL axiom here if (toAssert) assert_axiom(toAssert); } } } /* * Collect all unroll functions * and constant string in eqc of node n */ void theory_str::get_eqc_allUnroll(expr * n, expr * &constStr, std::set & unrollFuncSet) { constStr = nullptr; unrollFuncSet.clear(); expr * curr = n; do { if (u.str.is_string(to_app(curr))) { constStr = curr; } else if (u.re.is_unroll(to_app(curr))) { if (unrollFuncSet.find(curr) == unrollFuncSet.end()) { unrollFuncSet.insert(curr); } } curr = get_eqc_next(curr); } while (curr != n); } // Collect simple Unroll functions (whose core is Str2Reg) and constant strings in the EQC of n. void theory_str::get_eqc_simpleUnroll(expr * n, expr * &constStr, std::set & unrollFuncSet) { constStr = nullptr; unrollFuncSet.clear(); expr * curr = n; do { if (u.str.is_string(to_app(curr))) { constStr = curr; } else if (u.re.is_unroll(to_app(curr))) { expr * core = to_app(curr)->get_arg(0); if (u.re.is_to_re(to_app(core))) { if (unrollFuncSet.find(curr) == unrollFuncSet.end()) { unrollFuncSet.insert(curr); } } } curr = get_eqc_next(curr); } while (curr != n); } void theory_str::init_model(model_generator & mg) { //TRACE("str", tout << "initializing model" << std::endl; display(tout);); m_factory = alloc(str_value_factory, get_manager(), get_family_id()); mg.register_factory(m_factory); } /* * Helper function for mk_value(). * Attempts to resolve the expression 'n' to a string constant. * Stronger than get_eqc_value() in that it will perform recursive descent * through every subexpression and attempt to resolve those to concrete values as well. * Returns the concrete value obtained from this process, * guaranteed to satisfy m_strutil.is_string(), * if one could be obtained, * or else returns NULL if no concrete value was derived. */ app * theory_str::mk_value_helper(app * n) { if (u.str.is_string(n)) { return n; } else if (u.str.is_concat(n)) { // recursively call this function on each argument SASSERT(n->get_num_args() == 2); expr * a0 = n->get_arg(0); expr * a1 = n->get_arg(1); app * a0_conststr = mk_value_helper(to_app(a0)); app * a1_conststr = mk_value_helper(to_app(a1)); if (a0_conststr != nullptr && a1_conststr != nullptr) { zstring a0_s, a1_s; u.str.is_string(a0_conststr, a0_s); u.str.is_string(a1_conststr, a1_s); zstring result = a0_s + a1_s; return to_app(mk_string(result)); } } // fallback path // try to find some constant string, anything, in the equivalence class of n bool hasEqc = false; expr * n_eqc = get_eqc_value(n, hasEqc); if (hasEqc) { return to_app(n_eqc); } else { return nullptr; } } model_value_proc * theory_str::mk_value(enode * n, model_generator & mg) { TRACE("str", tout << "mk_value for: " << mk_ismt2_pp(n->get_owner(), get_manager()) << " (sort " << mk_ismt2_pp(get_manager().get_sort(n->get_owner()), get_manager()) << ")" << std::endl;); ast_manager & m = get_manager(); app_ref owner(m); owner = n->get_owner(); // If the owner is not internalized, it doesn't have an enode associated. SASSERT(get_context().e_internalized(owner)); app * val = mk_value_helper(owner); if (val != nullptr) { return alloc(expr_wrapper_proc, val); } else { TRACE("str", tout << "WARNING: failed to find a concrete value, falling back" << std::endl;); std::ostringstream unused; unused << "**UNUSED**" << (m_unused_id++); return alloc(expr_wrapper_proc, to_app(mk_string(unused.str().c_str()))); } } void theory_str::finalize_model(model_generator & mg) {} void theory_str::display(std::ostream & out) const { out << "TODO: theory_str display" << std::endl; } }; /* namespace smt */ z3-z3-4.8.7/src/smt/theory_str.h000066400000000000000000000753601356505360400164070ustar00rootroot00000000000000/*++ Module Name: theory_str.h Abstract: String Theory Plugin Author: Murphy Berzish and Yunhui Zheng Revision History: --*/ #ifndef _THEORY_STR_H_ #define _THEORY_STR_H_ #include "util/trail.h" #include "util/union_find.h" #include "util/scoped_ptr_vector.h" #include "util/hashtable.h" #include "ast/ast_pp.h" #include "ast/arith_decl_plugin.h" #include "ast/rewriter/th_rewriter.h" #include "ast/rewriter/seq_rewriter.h" #include "ast/seq_decl_plugin.h" #include "model/value_factory.h" #include "smt/smt_theory.h" #include "smt/params/theory_str_params.h" #include "smt/smt_model_generator.h" #include "smt/smt_arith_value.h" #include #include #include #include namespace smt { typedef hashtable symbol_set; typedef int_hashtable > integer_set; class str_value_factory : public value_factory { seq_util u; symbol_set m_strings; std::string delim; unsigned m_next; public: str_value_factory(ast_manager & m, family_id fid) : value_factory(m, fid), u(m), delim("!"), m_next(0) {} ~str_value_factory() override {} expr * get_some_value(sort * s) override { return u.str.mk_string(symbol("some value")); } bool get_some_values(sort * s, expr_ref & v1, expr_ref & v2) override { v1 = u.str.mk_string(symbol("value 1")); v2 = u.str.mk_string(symbol("value 2")); return true; } expr * get_fresh_value(sort * s) override { if (u.is_string(s)) { while (true) { std::ostringstream strm; strm << delim << std::hex << (m_next++) << std::dec << delim; symbol sym(strm.str().c_str()); if (m_strings.contains(sym)) continue; m_strings.insert(sym); return u.str.mk_string(sym); } } sort* seq = nullptr; if (u.is_re(s, seq)) { expr* v0 = get_fresh_value(seq); return u.re.mk_to_re(v0); } TRACE("t_str", tout << "unexpected sort in get_fresh_value(): " << mk_pp(s, m_manager) << std::endl;); UNREACHABLE(); return nullptr; } void register_value(expr * n) override { /* Ignore */ } }; // NSB: added operator[] and contains to obj_pair_hashtable class theory_str_contain_pair_bool_map_t : public obj_pair_map {}; template class binary_search_trail : public trail { obj_map > & target; expr * entry; public: binary_search_trail(obj_map > & target, expr * entry) : target(target), entry(entry) {} ~binary_search_trail() override {} void undo(Ctx & ctx) override { TRACE("t_str_binary_search", tout << "in binary_search_trail::undo()" << std::endl;); if (target.contains(entry)) { if (!target[entry].empty()) { target[entry].pop_back(); } else { TRACE("t_str_binary_search", tout << "WARNING: attempt to remove length tester from an empty stack" << std::endl;); } } else { TRACE("t_str_binary_search", tout << "WARNING: attempt to access length tester map via invalid key" << std::endl;); } } }; class nfa { protected: bool m_valid; unsigned m_next_id; unsigned next_id() { unsigned retval = m_next_id; ++m_next_id; return retval; } unsigned m_start_state; unsigned m_end_state; std::map > transition_map; std::map > epsilon_map; void make_transition(unsigned start, char symbol, unsigned end) { transition_map[start][symbol] = end; } void make_epsilon_move(unsigned start, unsigned end) { epsilon_map[start].insert(end); } // Convert a regular expression to an e-NFA using Thompson's construction void convert_re(expr * e, unsigned & start, unsigned & end, seq_util & u); public: nfa(seq_util & u, expr * e) : m_valid(true), m_next_id(0), m_start_state(0), m_end_state(0) { convert_re(e, m_start_state, m_end_state, u); } nfa() : m_valid(false), m_next_id(0), m_start_state(0), m_end_state(0) {} bool is_valid() const { return m_valid; } void epsilon_closure(unsigned start, std::set & closure); bool matches(zstring input); }; class regex_automaton_under_assumptions { protected: expr * re_term; eautomaton * aut; bool polarity; bool assume_lower_bound; rational lower_bound; bool assume_upper_bound; rational upper_bound; public: regex_automaton_under_assumptions() : re_term(nullptr), aut(nullptr), polarity(false), assume_lower_bound(false), assume_upper_bound(false) {} regex_automaton_under_assumptions(expr * re_term, eautomaton * aut, bool polarity) : re_term(re_term), aut(aut), polarity(polarity), assume_lower_bound(false), assume_upper_bound(false) {} void set_lower_bound(rational & lb) { lower_bound = lb; assume_lower_bound = true; } void unset_lower_bound() { assume_lower_bound = false; } void set_upper_bound(rational & ub) { upper_bound = ub; assume_upper_bound = true; } void unset_upper_bound() { assume_upper_bound = false; } bool get_lower_bound(rational & lb) const { if (assume_lower_bound) { lb = lower_bound; return true; } else { return false; } } bool get_upper_bound(rational & ub) const { if (assume_upper_bound) { ub = upper_bound; return true; } else { return false; } } eautomaton * get_automaton() const { return aut; } expr * get_regex_term() const { return re_term; } bool get_polarity() const { return polarity; } virtual ~regex_automaton_under_assumptions() { // don't free str_in_re or aut; // they are managed separately } }; class theory_str : public theory { struct T_cut { int level; obj_map vars; T_cut() { level = -100; } }; typedef trail_stack th_trail_stack; typedef union_find th_union_find; typedef map, default_eq > rational_map; struct zstring_hash_proc { unsigned operator()(zstring const & s) const { return string_hash(s.encode().c_str(), static_cast(s.length()), 17); } }; typedef map > string_map; protected: theory_str_params const & m_params; /* * Setting EagerStringConstantLengthAssertions to true allows some methods, * in particular internalize_term(), to add * length assertions about relevant string constants. * Note that currently this should always be set to 'true', or else *no* length assertions * will be made about string constants. */ bool opt_EagerStringConstantLengthAssertions; /* * If VerifyFinalCheckProgress is set to true, continuing after final check is invoked * without asserting any new axioms is considered a bug and will throw an exception. */ bool opt_VerifyFinalCheckProgress; /* * This constant controls how eagerly we expand unrolls in unbounded regex membership tests. */ int opt_LCMUnrollStep; /* * If NoQuickReturn_IntegerTheory is set to true, * integer theory integration checks that assert axioms * will not return from the function after asserting their axioms. * The default behaviour of Z3str2 is to set this to 'false'. This may be incorrect. */ bool opt_NoQuickReturn_IntegerTheory; /* * If DisableIntegerTheoryIntegration is set to true, * ALL calls to the integer theory integration methods * (get_arith_value, get_len_value, lower_bound, upper_bound) * will ignore what the arithmetic solver believes about length terms, * and will return no information. * * This reduces performance significantly, but can be useful to enable * if it is suspected that string-integer integration, or the arithmetic solver itself, * might have a bug. * * The default behaviour of Z3str2 is to set this to 'false'. */ bool opt_DisableIntegerTheoryIntegration; /* * If DeferEQCConsistencyCheck is set to true, * expensive calls to new_eq_check() will be deferred until final check, * at which time the consistency of *all* string equivalence classes will be validated. */ bool opt_DeferEQCConsistencyCheck; /* * If CheckVariableScope is set to true, * pop_scope_eh() and final_check_eh() will run extra checks * to determine whether the current assignment * contains references to any internal variables that are no longer in scope. */ bool opt_CheckVariableScope; /* * If ConcatOverlapAvoid is set to true, * the check to simplify Concat = Concat in handle_equality() will * avoid simplifying wrt. pairs of Concat terms that will immediately * result in an overlap. (false = Z3str2 behaviour) */ bool opt_ConcatOverlapAvoid; bool search_started; arith_util m_autil; seq_util u; int sLevel; bool finalCheckProgressIndicator; expr_ref_vector m_trail; // trail for generated terms str_value_factory * m_factory; re2automaton m_mk_aut; // Unique identifier appended to unused variables to ensure that model construction // does not introduce equalities when they weren't enforced. unsigned m_unused_id; // terms we couldn't go through set_up_axioms() with because they weren't internalized expr_ref_vector m_delayed_axiom_setup_terms; ptr_vector m_basicstr_axiom_todo; svector > m_str_eq_todo; ptr_vector m_concat_axiom_todo; ptr_vector m_string_constant_length_todo; ptr_vector m_concat_eval_todo; expr_ref_vector m_delayed_assertions_todo; // enode lists for library-aware/high-level string terms (e.g. substr, contains) ptr_vector m_library_aware_axiom_todo; // list of axioms that are re-asserted every time the scope is popped expr_ref_vector m_persisted_axioms; expr_ref_vector m_persisted_axiom_todo; // hashtable of all exprs for which we've already set up term-specific axioms -- // this prevents infinite recursive descent with respect to axioms that // include an occurrence of the term for which axioms are being generated obj_hashtable axiomatized_terms; // hashtable of all top-level exprs for which set_up_axioms() has been called obj_hashtable existing_toplevel_exprs; int tmpStringVarCount; int tmpXorVarCount; int tmpLenTestVarCount; int tmpValTestVarCount; // obj_pair_map > varForBreakConcat; std::map, std::map > varForBreakConcat; bool avoidLoopCut; bool loopDetected; obj_map > cut_var_map; scoped_ptr_vector m_cut_allocs; expr_ref m_theoryStrOverlapAssumption_term; obj_hashtable variable_set; obj_hashtable internal_variable_set; obj_hashtable regex_variable_set; std::map > internal_variable_scope_levels; obj_hashtable internal_lenTest_vars; obj_hashtable internal_valTest_vars; obj_hashtable internal_unrollTest_vars; obj_hashtable input_var_in_len; obj_map fvar_len_count_map; obj_map > fvar_lenTester_map; obj_map lenTester_fvar_map; obj_map > > > fvar_valueTester_map; obj_map valueTester_fvar_map; obj_map val_range_map; // This can't be an expr_ref_vector because the constructor is wrong, // we would need to modify the allocator so we pass in ast_manager obj_map, ptr_vector > > unroll_tries_map; obj_map unroll_var_map; obj_pair_map concat_eq_unroll_ast_map; expr_ref_vector contains_map; theory_str_contain_pair_bool_map_t contain_pair_bool_map; obj_map > > contain_pair_idx_map; // TBD: do a curried map for determinism. std::map, expr*> regex_in_bool_map; obj_map > regex_in_var_reg_str_map; // regex automata scoped_ptr_vector m_automata; ptr_vector regex_automata; obj_hashtable regex_terms; obj_map > regex_terms_by_string; // S --> [ (str.in.re S *) ] obj_map > regex_automaton_assumptions; // RegEx --> [ aut+assumptions ] obj_map regex_nfa_cache; // Regex term --> NFA obj_hashtable regex_terms_with_path_constraints; // set of string terms which have had path constraints asserted in the current scope obj_hashtable regex_terms_with_length_constraints; // set of regex terms which had had length constraints asserted in the current scope obj_map regex_term_to_length_constraint; // (str.in.re S R) -> (length constraint over S wrt. R) obj_map > regex_term_to_extra_length_vars; // extra length vars used in regex_term_to_length_constraint entries // keep track of the last lower/upper bound we saw for each string term // so we don't perform duplicate work obj_map regex_last_lower_bound; obj_map regex_last_upper_bound; // each counter maps a (str.in.re) expression to an integer. // use helper functions regex_inc_counter() and regex_get_counter() to access obj_map regex_length_attempt_count; obj_map regex_fail_count; obj_map regex_intersection_fail_count; obj_map > string_chars; // S --> [S_0, S_1, ...] for character terms S_i svector char_set; std::map charSetLookupTable; int charSetSize; obj_pair_map concat_astNode_map; // all (str.to-int) and (int.to-str) terms expr_ref_vector string_int_conversion_terms; obj_hashtable string_int_axioms; // used when opt_FastLengthTesterCache is true rational_map lengthTesterCache; // used when opt_FastValueTesterCache is true string_map valueTesterCache; string_map stringConstantCache; unsigned long totalCacheAccessCount; unsigned long cacheHitCount; unsigned long cacheMissCount; unsigned m_fresh_id; // cache mapping each string S to Length(S) obj_map length_ast_map; th_trail_stack m_trail_stack; th_union_find m_find; theory_var get_var(expr * n) const; expr * get_eqc_next(expr * n); app * get_ast(theory_var i); // binary search heuristic data struct binary_search_info { rational lowerBound; rational midPoint; rational upperBound; rational windowSize; binary_search_info() : lowerBound(rational::zero()), midPoint(rational::zero()), upperBound(rational::zero()), windowSize(rational::zero()) {} binary_search_info(rational lower, rational mid, rational upper, rational windowSize) : lowerBound(lower), midPoint(mid), upperBound(upper), windowSize(windowSize) {} void calculate_midpoint() { midPoint = floor(lowerBound + ((upperBound - lowerBound) / rational(2)) ); } }; // maps a free string var to a stack of active length testers. // can use binary_search_trail to record changes to this object obj_map > binary_search_len_tester_stack; // maps a length tester var to the *active* search window obj_map binary_search_len_tester_info; // maps a free string var to the first length tester to be (re)used obj_map binary_search_starting_len_tester; // maps a length tester to the next length tester to be (re)used if the split is "low" obj_map binary_search_next_var_low; // maps a length tester to the next length tester to be (re)used if the split is "high" obj_map binary_search_next_var_high; // finite model finding data // maps a finite model tester var to a list of variables that will be tested obj_map > finite_model_test_varlists; protected: void assert_axiom(expr * e); void assert_implication(expr * premise, expr * conclusion); expr * rewrite_implication(expr * premise, expr * conclusion); expr * mk_string(zstring const& str); expr * mk_string(const char * str); app * mk_strlen(expr * e); expr * mk_concat(expr * n1, expr * n2); expr * mk_concat_const_str(expr * n1, expr * n2); app * mk_contains(expr * haystack, expr * needle); app * mk_indexof(expr * haystack, expr * needle); app * mk_fresh_const(char const* name, sort* s); literal mk_literal(expr* _e); app * mk_int(int n); app * mk_int(rational & q); void check_and_init_cut_var(expr * node); void add_cut_info_one_node(expr * baseNode, int slevel, expr * node); void add_cut_info_merge(expr * destNode, int slevel, expr * srcNode); bool has_self_cut(expr * n1, expr * n2); // for ConcatOverlapAvoid bool will_result_in_overlap(expr * lhs, expr * rhs); void track_variable_scope(expr * var); app * mk_str_var(std::string name); app * mk_int_var(std::string name); app * mk_nonempty_str_var(); app * mk_internal_xor_var(); expr * mk_internal_valTest_var(expr * node, int len, int vTries); app * mk_regex_rep_var(); app * mk_unroll_bound_var(); app * mk_unroll_test_var(); void add_nonempty_constraint(expr * s); void instantiate_concat_axiom(enode * cat); void try_eval_concat(enode * cat); void instantiate_basic_string_axioms(enode * str); void instantiate_str_eq_length_axiom(enode * lhs, enode * rhs); void instantiate_axiom_CharAt(enode * e); void instantiate_axiom_prefixof(enode * e); void instantiate_axiom_suffixof(enode * e); void instantiate_axiom_Contains(enode * e); void instantiate_axiom_Indexof(enode * e); void instantiate_axiom_Indexof_extended(enode * e); void instantiate_axiom_LastIndexof(enode * e); void instantiate_axiom_Substr(enode * e); void instantiate_axiom_Replace(enode * e); void instantiate_axiom_str_to_int(enode * e); void instantiate_axiom_int_to_str(enode * e); void add_persisted_axiom(expr * a); expr * mk_RegexIn(expr * str, expr * regexp); void instantiate_axiom_RegexIn(enode * e); app * mk_unroll(expr * n, expr * bound); void process_unroll_eq_const_str(expr * unrollFunc, expr * constStr); void unroll_str2reg_constStr(expr * unrollFunc, expr * eqConstStr); void process_concat_eq_unroll(expr * concat, expr * unroll); // regex automata and length-aware regex void solve_regex_automata(); unsigned estimate_regex_complexity(expr * re); unsigned estimate_regex_complexity_under_complement(expr * re); unsigned estimate_automata_intersection_difficulty(eautomaton * aut1, eautomaton * aut2); bool check_regex_length_linearity(expr * re); bool check_regex_length_linearity_helper(expr * re, bool already_star); expr_ref infer_all_regex_lengths(expr * lenVar, expr * re, expr_ref_vector & freeVariables); void check_subterm_lengths(expr * re, integer_set & lens); void find_automaton_initial_bounds(expr * str_in_re, eautomaton * aut); bool refine_automaton_lower_bound(eautomaton * aut, rational current_lower_bound, rational & refined_lower_bound); bool refine_automaton_upper_bound(eautomaton * aut, rational current_upper_bound, rational & refined_upper_bound); expr_ref generate_regex_path_constraints(expr * stringTerm, eautomaton * aut, rational lenVal, expr_ref & characterConstraints); void aut_path_add_next(u_map& next, expr_ref_vector& trail, unsigned idx, expr* cond); expr_ref aut_path_rewrite_constraint(expr * cond, expr * ch_var); void regex_inc_counter(obj_map & counter_map, expr * key); unsigned regex_get_counter(obj_map & counter_map, expr * key); void set_up_axioms(expr * ex); void handle_equality(expr * lhs, expr * rhs); app * mk_value_helper(app * n); expr * get_eqc_value(expr * n, bool & hasEqcValue); expr * z3str2_get_eqc_value(expr * n , bool & hasEqcValue); bool in_same_eqc(expr * n1, expr * n2); expr * collect_eq_nodes(expr * n, expr_ref_vector & eqcSet); bool is_var(expr * e) const; bool get_arith_value(expr* e, rational& val) const; bool get_len_value(expr* e, rational& val); bool lower_bound(expr* _e, rational& lo); bool upper_bound(expr* _e, rational& hi); bool can_two_nodes_eq(expr * n1, expr * n2); bool can_concat_eq_str(expr * concat, zstring& str); bool can_concat_eq_concat(expr * concat1, expr * concat2); bool check_concat_len_in_eqc(expr * concat); void check_eqc_empty_string(expr * lhs, expr * rhs); void check_eqc_concat_concat(std::set & eqc_concat_lhs, std::set & eqc_concat_rhs); bool check_length_consistency(expr * n1, expr * n2); bool check_length_const_string(expr * n1, expr * constStr); bool check_length_eq_var_concat(expr * n1, expr * n2); bool check_length_concat_concat(expr * n1, expr * n2); bool check_length_concat_var(expr * concat, expr * var); bool check_length_var_var(expr * var1, expr * var2); void check_contain_in_new_eq(expr * n1, expr * n2); void check_contain_by_eqc_val(expr * varNode, expr * constNode); void check_contain_by_substr(expr * varNode, expr_ref_vector & willEqClass); void check_contain_by_eq_nodes(expr * n1, expr * n2); bool in_contain_idx_map(expr * n); void compute_contains(std::map & varAliasMap, std::map & concatAliasMap, std::map & varConstMap, std::map & concatConstMap, std::map > & varEqConcatMap); expr * dealias_node(expr * node, std::map & varAliasMap, std::map & concatAliasMap); void get_grounded_concats(unsigned depth, expr* node, std::map & varAliasMap, std::map & concatAliasMap, std::map & varConstMap, std::map & concatConstMap, std::map > & varEqConcatMap, std::map, std::set > > & groundedMap); void print_grounded_concat(expr * node, std::map, std::set > > & groundedMap); void check_subsequence(expr* str, expr* strDeAlias, expr* subStr, expr* subStrDeAlias, expr* boolVar, std::map, std::set > > & groundedMap); bool is_partial_in_grounded_concat(const std::vector & strVec, const std::vector & subStrVec); void get_nodes_in_concat(expr * node, ptr_vector & nodeList); expr * simplify_concat(expr * node); void simplify_parent(expr * nn, expr * eq_str); void simplify_concat_equality(expr * lhs, expr * rhs); void solve_concat_eq_str(expr * concat, expr * str); void infer_len_concat_equality(expr * nn1, expr * nn2); bool infer_len_concat(expr * n, rational & nLen); void infer_len_concat_arg(expr * n, rational len); bool is_concat_eq_type1(expr * concatAst1, expr * concatAst2); bool is_concat_eq_type2(expr * concatAst1, expr * concatAst2); bool is_concat_eq_type3(expr * concatAst1, expr * concatAst2); bool is_concat_eq_type4(expr * concatAst1, expr * concatAst2); bool is_concat_eq_type5(expr * concatAst1, expr * concatAst2); bool is_concat_eq_type6(expr * concatAst1, expr * concatAst2); void process_concat_eq_type1(expr * concatAst1, expr * concatAst2); void process_concat_eq_type2(expr * concatAst1, expr * concatAst2); void process_concat_eq_type3(expr * concatAst1, expr * concatAst2); void process_concat_eq_type4(expr * concatAst1, expr * concatAst2); void process_concat_eq_type5(expr * concatAst1, expr * concatAst2); void process_concat_eq_type6(expr * concatAst1, expr * concatAst2); void print_cut_var(expr * node, std::ofstream & xout); void generate_mutual_exclusion(expr_ref_vector & exprs); void add_theory_aware_branching_info(expr * term, double priority, lbool phase); bool new_eq_check(expr * lhs, expr * rhs); void group_terms_by_eqc(expr * n, std::set & concats, std::set & vars, std::set & consts); int ctx_dep_analysis(std::map & strVarMap, std::map & freeVarMap, std::map > & unrollGroupMap, std::map > & var_eq_concat_map); void trace_ctx_dep(std::ofstream & tout, std::map & aliasIndexMap, std::map & var_eq_constStr_map, std::map > & var_eq_concat_map, std::map > & var_eq_unroll_map, std::map & concat_eq_constStr_map, std::map > & concat_eq_concat_map, std::map > & unrollGroupMap); bool term_appears_as_subterm(expr * needle, expr * haystack); void classify_ast_by_type(expr * node, std::map & varMap, std::map & concatMap, std::map & unrollMap); void classify_ast_by_type_in_positive_context(std::map & varMap, std::map & concatMap, std::map & unrollMap); expr * mk_internal_lenTest_var(expr * node, int lTries); expr * gen_len_val_options_for_free_var(expr * freeVar, expr * lenTesterInCbEq, zstring lenTesterValue); void process_free_var(std::map & freeVar_map); expr * gen_len_test_options(expr * freeVar, expr * indicator, int tries); expr * gen_free_var_options(expr * freeVar, expr * len_indicator, zstring len_valueStr, expr * valTesterInCbEq, zstring valTesterValueStr); expr* gen_val_options(expr * freeVar, expr * len_indicator, expr * val_indicator, zstring lenStr, int tries); void print_value_tester_list(svector > & testerList); bool get_next_val_encode(int_vector & base, int_vector & next); zstring gen_val_string(int len, int_vector & encoding); // binary search heuristic expr * binary_search_length_test(expr * freeVar, expr * previousLenTester, zstring previousLenTesterValue); expr_ref binary_search_case_split(expr * freeVar, expr * tester, binary_search_info & bounds, literal_vector & case_split_lits); bool free_var_attempt(expr * nn1, expr * nn2); void more_len_tests(expr * lenTester, zstring lenTesterValue); void more_value_tests(expr * valTester, zstring valTesterValue); expr * get_alias_index_ast(std::map & aliasIndexMap, expr * node); expr * getMostLeftNodeInConcat(expr * node); expr * getMostRightNodeInConcat(expr * node); void get_var_in_eqc(expr * n, std::set & varSet); void get_concats_in_eqc(expr * n, std::set & concats); void get_const_str_asts_in_node(expr * node, expr_ref_vector & constList); expr * eval_concat(expr * n1, expr * n2); bool finalcheck_str2int(app * a); bool finalcheck_int2str(app * a); // strRegex void get_eqc_allUnroll(expr * n, expr * &constStr, std::set & unrollFuncSet); void get_eqc_simpleUnroll(expr * n, expr * &constStr, std::set & unrollFuncSet); void gen_assign_unroll_reg(std::set & unrolls); expr * gen_assign_unroll_Str2Reg(expr * n, std::set & unrolls); expr * gen_unroll_conditional_options(expr * var, std::set & unrolls, zstring lcmStr); expr * gen_unroll_assign(expr * var, zstring lcmStr, expr * testerVar, int l, int h); void reduce_virtual_regex_in(expr * var, expr * regex, expr_ref_vector & items); void check_regex_in(expr * nn1, expr * nn2); zstring get_std_regex_str(expr * r); void dump_assignments(); void initialize_charset(); void check_variable_scope(); void recursive_check_variable_scope(expr * ex); void collect_var_concat(expr * node, std::set & varSet, std::set & concatSet); bool propagate_length(std::set & varSet, std::set & concatSet, std::map & exprLenMap); void get_unique_non_concat_nodes(expr * node, std::set & argSet); bool propagate_length_within_eqc(expr * var); // TESTING void refresh_theory_var(expr * e); expr_ref set_up_finite_model_test(expr * lhs, expr * rhs); void finite_model_test(expr * v, expr * c); public: theory_str(ast_manager & m, theory_str_params const & params); ~theory_str() override; char const * get_name() const override { return "seq"; } void display(std::ostream & out) const override; bool overlapping_variables_detected() const { return loopDetected; } th_trail_stack& get_trail_stack() { return m_trail_stack; } void merge_eh(theory_var, theory_var, theory_var v1, theory_var v2) {} void after_merge_eh(theory_var r1, theory_var r2, theory_var v1, theory_var v2) { } void unmerge_eh(theory_var v1, theory_var v2) {} protected: bool internalize_atom(app * atom, bool gate_ctx) override; bool internalize_term(app * term) override; virtual enode* ensure_enode(expr* e); theory_var mk_var(enode * n) override; void new_eq_eh(theory_var, theory_var) override; void new_diseq_eh(theory_var, theory_var) override; theory* mk_fresh(context*) override { return alloc(theory_str, get_manager(), m_params); } void init(context * ctx) override; void init_search_eh() override; void add_theory_assumptions(expr_ref_vector & assumptions) override; lbool validate_unsat_core(expr_ref_vector & unsat_core) override; void relevant_eh(app * n) override; void assign_eh(bool_var v, bool is_true) override; void push_scope_eh() override; void pop_scope_eh(unsigned num_scopes) override; void reset_eh() override; bool can_propagate() override; void propagate() override; final_check_status final_check_eh() override; virtual void attach_new_th_var(enode * n); void init_model(model_generator & m) override; model_value_proc * mk_value(enode * n, model_generator & mg) override; void finalize_model(model_generator & mg) override; }; }; #endif /* _THEORY_STR_H_ */ z3-z3-4.8.7/src/smt/theory_str_regex.cpp000066400000000000000000002436171356505360400201360ustar00rootroot00000000000000/*++ Module Name: theory_str_regex.cpp Abstract: Regular expression components for Z3str3 (theory_str). Author: Murphy Berzish (2019-10-25) Revision History: --*/ #include "smt/theory_str.h" namespace smt { // saturating unsigned addition unsigned inline _qadd(unsigned a, unsigned b) { if (a == UINT_MAX || b == UINT_MAX) { return UINT_MAX; } unsigned result = a + b; if (result < a || result < b) { return UINT_MAX; } return result; } // saturating unsigned multiply unsigned inline _qmul(unsigned a, unsigned b) { if (a == UINT_MAX || b == UINT_MAX) { return UINT_MAX; } if (a == 0 || b == 0) { return 0; } unsigned result = a * b; if (result < a || result < b) { return UINT_MAX; } return result; } void theory_str::solve_regex_automata() { context & ctx = get_context(); ast_manager & m = get_manager(); // TODO since heuristics might fail, the "no progress" flag might need to be handled specially here bool regex_axiom_add = false; for (obj_hashtable::iterator it = regex_terms.begin(); it != regex_terms.end(); ++it) { expr * str_in_re = *it; expr * str = nullptr; expr * re = nullptr; u.str.is_in_re(str_in_re, str, re); if (!ctx.b_internalized(str_in_re)) { TRACE("str", tout << "regex term " << mk_pp(str_in_re, m) << " not internalized; fixing and continuing" << std::endl;); ctx.internalize(str_in_re, false); finalCheckProgressIndicator = true; continue; } lbool current_assignment = ctx.get_assignment(str_in_re); TRACE("str", tout << "regex term: " << mk_pp(str, m) << " in " << mk_pp(re, m) << " : " << current_assignment << std::endl;); if (current_assignment == l_undef) { continue; } if (!regex_terms_with_length_constraints.contains(str_in_re)) { if (current_assignment == l_true && check_regex_length_linearity(re)) { TRACE("str", tout << "regex length constraints expected to be linear -- generating and asserting them" << std::endl;); if (regex_term_to_length_constraint.contains(str_in_re)) { // use existing length constraint expr * top_level_length_constraint = nullptr; regex_term_to_length_constraint.find(str_in_re, top_level_length_constraint); ptr_vector extra_length_vars; regex_term_to_extra_length_vars.find(str_in_re, extra_length_vars); assert_axiom(top_level_length_constraint); for(ptr_vector::iterator it = extra_length_vars.begin(); it != extra_length_vars.end(); ++it) { expr * v = *it; refresh_theory_var(v); expr_ref len_constraint(m_autil.mk_ge(v, m_autil.mk_numeral(rational::zero(), true)), m); assert_axiom(len_constraint); } } else { // generate new length constraint expr_ref_vector extra_length_vars(m); expr_ref _top_level_length_constraint = infer_all_regex_lengths(mk_strlen(str), re, extra_length_vars); expr_ref top_level_length_constraint(_top_level_length_constraint, m); th_rewriter rw(m); rw(top_level_length_constraint); TRACE("str", tout << "top-level length constraint: " << mk_pp(top_level_length_constraint, m) << std::endl;); // assert and track length constraint assert_axiom(top_level_length_constraint); for(expr_ref_vector::iterator it = extra_length_vars.begin(); it != extra_length_vars.end(); ++it) { expr * v = *it; expr_ref len_constraint(m_autil.mk_ge(v, m_autil.mk_numeral(rational::zero(), true)), m); assert_axiom(len_constraint); } regex_term_to_length_constraint.insert(str_in_re, top_level_length_constraint); ptr_vector vtmp; for(expr_ref_vector::iterator it = extra_length_vars.begin(); it != extra_length_vars.end(); ++it) { vtmp.push_back(*it); } regex_term_to_extra_length_vars.insert(str_in_re, vtmp); } regex_terms_with_length_constraints.insert(str_in_re); m_trail_stack.push(insert_obj_trail(regex_terms_with_length_constraints, str_in_re)); regex_axiom_add = true; } } // re not in regex_terms_with_length_constraints rational exact_length_value; if (get_len_value(str, exact_length_value)) { TRACE("str", tout << "exact length of " << mk_pp(str, m) << " is " << exact_length_value << std::endl;); if (regex_terms_with_path_constraints.contains(str_in_re)) { TRACE("str", tout << "term " << mk_pp(str_in_re, m) << " already has path constraints set up" << std::endl;); continue; } // find a consistent automaton for this term bool found = false; regex_automaton_under_assumptions assumption; if (regex_automaton_assumptions.contains(re) && !regex_automaton_assumptions[re].empty()){ for (svector::iterator it = regex_automaton_assumptions[re].begin(); it != regex_automaton_assumptions[re].end(); ++it) { regex_automaton_under_assumptions autA = *it; rational assumed_upper_bound, assumed_lower_bound; bool assumes_upper_bound = autA.get_upper_bound(assumed_upper_bound); bool assumes_lower_bound = autA.get_lower_bound(assumed_lower_bound); if (!assumes_upper_bound && !assumes_lower_bound) { // automaton with no assumptions is always usable assumption = autA; found = true; break; } // TODO check consistency of bounds assumptions } // foreach(a in regex_automaton_assumptions) } if (found) { if (exact_length_value.is_zero()) { // check consistency of 0-length solution with automaton eautomaton * aut = assumption.get_automaton(); bool zero_solution = false; unsigned initial_state = aut->init(); if (aut->is_final_state(initial_state)) { zero_solution = true; } else { unsigned_vector eps_states; aut->get_epsilon_closure(initial_state, eps_states); for (unsigned_vector::iterator it = eps_states.begin(); it != eps_states.end(); ++it) { unsigned state = *it; if (aut->is_final_state(state)) { zero_solution = true; break; } } } // now check polarity of automaton wrt. original term if ( (current_assignment == l_true && !assumption.get_polarity()) || (current_assignment == l_false && assumption.get_polarity())) { // invert sense zero_solution = !zero_solution; } if (zero_solution) { TRACE("str", tout << "zero-length solution OK -- asserting empty path constraint" << std::endl;); expr_ref_vector lhs_terms(m); if (current_assignment == l_true) { lhs_terms.push_back(str_in_re); } else { lhs_terms.push_back(m.mk_not(str_in_re)); } lhs_terms.push_back(ctx.mk_eq_atom(mk_strlen(str), m_autil.mk_numeral(exact_length_value, true))); expr_ref lhs(mk_and(lhs_terms), m); expr_ref rhs(ctx.mk_eq_atom(str, mk_string("")), m); assert_implication(lhs, rhs); regex_terms_with_path_constraints.insert(str_in_re); m_trail_stack.push(insert_obj_trail(regex_terms_with_path_constraints, str_in_re)); } else { TRACE("str", tout << "zero-length solution not admitted by this automaton -- asserting conflict clause" << std::endl;); expr_ref_vector lhs_terms(m); if (current_assignment == l_true) { lhs_terms.push_back(str_in_re); } else { lhs_terms.push_back(m.mk_not(str_in_re)); } lhs_terms.push_back(ctx.mk_eq_atom(mk_strlen(str), m_autil.mk_numeral(exact_length_value, true))); expr_ref lhs(mk_and(lhs_terms), m); expr_ref conflict(m.mk_not(lhs), m); assert_axiom(conflict); } regex_axiom_add = true; regex_inc_counter(regex_length_attempt_count, re); continue; } else { expr_ref pathConstraint(m); expr_ref characterConstraints(m); pathConstraint = generate_regex_path_constraints(str, assumption.get_automaton(), exact_length_value, characterConstraints); TRACE("str", tout << "generated regex path constraint " << mk_pp(pathConstraint, m) << std::endl;); TRACE("str", tout << "character constraints are " << mk_pp(characterConstraints, m) << std::endl;); expr_ref_vector lhs_terms(m); if (current_assignment == l_true) { lhs_terms.push_back(str_in_re); } else { lhs_terms.push_back(m.mk_not(str_in_re)); } lhs_terms.push_back(ctx.mk_eq_atom(mk_strlen(str), m_autil.mk_numeral(exact_length_value, true))); expr_ref lhs(mk_and(lhs_terms), m); // If the path constraint comes out as "false", this means there are no paths of that length // in the automaton. If the polarity is the same, we can assert a conflict clause. // If the polarity is opposite, we ignore the path constraint. if (m.is_false(pathConstraint)) { if ( (current_assignment == l_true && assumption.get_polarity()) || (current_assignment == l_false && !assumption.get_polarity())) { // automaton and constraint have same polarity -- assert conflict clause TRACE("str", tout << "path constraint is false with matching polarity; asserting conflict clause" << std::endl;); expr_ref conflict(m.mk_not(mk_and(lhs_terms)), m); assert_axiom(conflict); // don't set up "regex_terms_with_path_constraints" as a conflict clause is not a path constraint } else { // automaton and constraint have opposite polarity -- ignore path constraint TRACE("str", tout << "path constraint is false with opposite polarity; ignoring path constraint" << std::endl;); assert_implication(lhs, characterConstraints); regex_terms_with_path_constraints.insert(str_in_re); m_trail_stack.push(insert_obj_trail(regex_terms_with_path_constraints, str_in_re)); } regex_axiom_add = true; } else { // If the automaton was built with the same polarity as the constraint, // assert directly. Otherwise, negate the path constraint if ( (current_assignment == l_true && assumption.get_polarity()) || (current_assignment == l_false && !assumption.get_polarity())) { TRACE("str", tout << "automaton and regex term have same polarity" << std::endl;); expr_ref rhs(m.mk_and(pathConstraint, characterConstraints), m); assert_implication(lhs, rhs); } else { TRACE("str", tout << "automaton and regex term have opposite polarity" << std::endl;); expr_ref rhs(m.mk_and(m.mk_not(pathConstraint), characterConstraints), m); assert_implication(lhs, rhs); } regex_terms_with_path_constraints.insert(str_in_re); m_trail_stack.push(insert_obj_trail(regex_terms_with_path_constraints, str_in_re)); regex_axiom_add = true; } // increment LengthAttemptCount regex_inc_counter(regex_length_attempt_count, re); TRACE("str", { unsigned v = regex_get_counter(regex_length_attempt_count, re); tout << "length attempt count for " << mk_pp(re, m) << " is " << v << std::endl; }); continue; } } else { // no automata available, or else all bounds assumptions are invalid unsigned expected_complexity = estimate_regex_complexity(re); if (expected_complexity <= m_params.m_RegexAutomata_DifficultyThreshold || regex_get_counter(regex_fail_count, str_in_re) >= m_params.m_RegexAutomata_FailedAutomatonThreshold) { CTRACE("str", regex_get_counter(regex_fail_count, str_in_re) >= m_params.m_RegexAutomata_FailedAutomatonThreshold, tout << "failed automaton threshold reached for " << mk_pp(str_in_re, m) << " -- automatically constructing full automaton" << std::endl;); eautomaton * aut = m_mk_aut(re); aut->compress(); regex_automata.push_back(aut); regex_automaton_under_assumptions new_aut(re, aut, true); if (!regex_automaton_assumptions.contains(re)) { regex_automaton_assumptions.insert(re, svector()); } regex_automaton_assumptions[re].push_back(new_aut); TRACE("str", tout << "add new automaton for " << mk_pp(re, m) << ": no assumptions" << std::endl;); regex_axiom_add = true; find_automaton_initial_bounds(str_in_re, aut); } else { regex_inc_counter(regex_fail_count, str_in_re); } continue; } } // get_len_value() expr_ref str_len(mk_strlen(str), m); rational lower_bound_value; rational upper_bound_value; bool lower_bound_exists = lower_bound(str_len, lower_bound_value); bool upper_bound_exists = upper_bound(str_len, upper_bound_value); CTRACE("str", lower_bound_exists, tout << "lower bound of " << mk_pp(str, m) << " is " << lower_bound_value << std::endl;); CTRACE("str", upper_bound_exists, tout << "upper bound of " << mk_pp(str, m) << " is " << upper_bound_value << std::endl;); bool new_lower_bound_info = true; bool new_upper_bound_info = true; // check last seen lower/upper bound to avoid performing duplicate work if (regex_last_lower_bound.contains(str)) { rational last_lb_value; regex_last_lower_bound.find(str, last_lb_value); if (last_lb_value == lower_bound_value) { new_lower_bound_info = false; } } if (regex_last_upper_bound.contains(str)) { rational last_ub_value; regex_last_upper_bound.find(str, last_ub_value); if (last_ub_value == upper_bound_value) { new_upper_bound_info = false; } } if (new_lower_bound_info) { regex_last_lower_bound.insert(str, lower_bound_value); } if (new_upper_bound_info) { regex_last_upper_bound.insert(str, upper_bound_value); } if (upper_bound_exists && new_upper_bound_info) { // check current assumptions if (regex_automaton_assumptions.contains(re) && !regex_automaton_assumptions[re].empty()){ // one or more existing assumptions. // see if the (current best) upper bound can be refined // (note that if we have an automaton with no assumption, // this automatically counts as best) bool need_assumption = true; regex_automaton_under_assumptions last_assumption; rational last_ub = rational::minus_one(); for (svector::iterator it = regex_automaton_assumptions[re].begin(); it != regex_automaton_assumptions[re].end(); ++it) { regex_automaton_under_assumptions autA = *it; if ((current_assignment == l_true && autA.get_polarity() == false) || (current_assignment == l_false && autA.get_polarity() == true)) { // automaton uses incorrect polarity continue; } rational this_ub; if (autA.get_upper_bound(this_ub)) { if (last_ub == rational::minus_one() || this_ub < last_ub) { last_ub = this_ub; last_assumption = autA; } } else { need_assumption = false; last_assumption = autA; break; } } if (!last_ub.is_minus_one() || !need_assumption) { CTRACE("str", !need_assumption, tout << "using automaton with full length information" << std::endl;); CTRACE("str", need_assumption, tout << "using automaton with assumed upper bound of " << last_ub << std::endl;); rational refined_upper_bound; bool solution_at_upper_bound = refine_automaton_upper_bound(last_assumption.get_automaton(), upper_bound_value, refined_upper_bound); TRACE("str", tout << "refined upper bound is " << refined_upper_bound << (solution_at_upper_bound?", solution at upper bound":", no solution at upper bound") << std::endl;); expr_ref_vector lhs(m); if (current_assignment == l_false) { lhs.push_back(m.mk_not(str_in_re)); } else { lhs.push_back(str_in_re); } if (need_assumption) { lhs.push_back(m_autil.mk_le(str_len, m_autil.mk_numeral(last_ub, true))); } lhs.push_back(m_autil.mk_le(str_len, m_autil.mk_numeral(upper_bound_value, true))); expr_ref_vector rhs(m); if (solution_at_upper_bound) { if (refined_upper_bound.is_minus_one()) { // If there are solutions at the upper bound but not below it, make the bound exact. rhs.push_back(ctx.mk_eq_atom(str_len, m_autil.mk_numeral(upper_bound_value, true))); } else { // If there are solutions at and below the upper bound, add an additional bound. rhs.push_back(m.mk_or( ctx.mk_eq_atom(str_len, m_autil.mk_numeral(upper_bound_value, true)), m_autil.mk_le(str_len, m_autil.mk_numeral(refined_upper_bound, true)) )); } } else { if (refined_upper_bound.is_minus_one()) { // If there are no solutions at or below the upper bound, assert a conflict clause. rhs.push_back(m.mk_not(m_autil.mk_le(str_len, m_autil.mk_numeral(upper_bound_value, true)))); } else { // If there are solutions below the upper bound but not at it, refine the bound. rhs.push_back(m_autil.mk_le(str_len, m_autil.mk_numeral(refined_upper_bound, true))); } } if (!rhs.empty()) { expr_ref lhs_terms(mk_and(lhs), m); expr_ref rhs_terms(mk_and(rhs), m); assert_implication(lhs_terms, rhs_terms); regex_axiom_add = true; } } } else { // no existing automata/assumptions. // if it's easy to construct a full automaton for R, do so unsigned expected_complexity = estimate_regex_complexity(re); bool failureThresholdExceeded = (regex_get_counter(regex_fail_count, str_in_re) >= m_params.m_RegexAutomata_FailedAutomatonThreshold); if (expected_complexity <= m_params.m_RegexAutomata_DifficultyThreshold || failureThresholdExceeded) { eautomaton * aut = m_mk_aut(re); aut->compress(); regex_automata.push_back(aut); regex_automaton_under_assumptions new_aut(re, aut, true); if (!regex_automaton_assumptions.contains(re)) { regex_automaton_assumptions.insert(re, svector()); } regex_automaton_assumptions[re].push_back(new_aut); TRACE("str", tout << "add new automaton for " << mk_pp(re, m) << ": no assumptions" << std::endl;); regex_axiom_add = true; find_automaton_initial_bounds(str_in_re, aut); } else { // TODO check negation? // TODO construct a partial automaton for R to the given upper bound? if (false) { } else { regex_inc_counter(regex_fail_count, str_in_re); } } continue; } // if we have *any* automaton for R, and the upper bound is not too large, // finitize the automaton (if we have not already done so) and assert all solutions if (upper_bound_value < 50) { // TODO better metric for threshold // NOT_IMPLEMENTED_YET(); // TODO(mtrberzi) } } else { // !upper_bound_exists // no upper bound information if (lower_bound_exists && !lower_bound_value.is_zero() && new_lower_bound_info) { // nonzero lower bound, no upper bound // check current assumptions if (regex_automaton_assumptions.contains(re) && !regex_automaton_assumptions[re].empty()){ // one or more existing assumptions. // see if the (current best) lower bound can be refined // (note that if we have an automaton with no assumption, // this automatically counts as best) bool need_assumption = true; regex_automaton_under_assumptions last_assumption; rational last_lb = rational::zero(); // the default for (svector::iterator it = regex_automaton_assumptions[re].begin(); it != regex_automaton_assumptions[re].end(); ++it) { regex_automaton_under_assumptions autA = *it; if ((current_assignment == l_true && autA.get_polarity() == false) || (current_assignment == l_false && autA.get_polarity() == true)) { // automaton uses incorrect polarity continue; } rational this_lb; if (autA.get_lower_bound(this_lb)) { if (this_lb > last_lb) { last_lb = this_lb; last_assumption = autA; } } else { need_assumption = false; last_assumption = autA; break; } } if (!last_lb.is_zero() || !need_assumption) { CTRACE("str", !need_assumption, tout << "using automaton with full length information" << std::endl;); CTRACE("str", need_assumption, tout << "using automaton with assumed lower bound of " << last_lb << std::endl;); rational refined_lower_bound; bool solution_at_lower_bound = refine_automaton_lower_bound(last_assumption.get_automaton(), lower_bound_value, refined_lower_bound); TRACE("str", tout << "refined lower bound is " << refined_lower_bound << (solution_at_lower_bound?", solution at lower bound":", no solution at lower bound") << std::endl;); expr_ref_vector lhs(m); if (current_assignment == l_false) { lhs.push_back(m.mk_not(str_in_re)); } else { lhs.push_back(str_in_re); } if (need_assumption) { lhs.push_back(m_autil.mk_ge(str_len, m_autil.mk_numeral(last_lb, true))); } lhs.push_back(m_autil.mk_ge(str_len, m_autil.mk_numeral(lower_bound_value, true))); expr_ref_vector rhs(m); if (solution_at_lower_bound) { if (refined_lower_bound.is_minus_one()) { // If there are solutions at the lower bound but not above it, make the bound exact. rhs.push_back(ctx.mk_eq_atom(str_len, m_autil.mk_numeral(lower_bound_value, true))); } else { // If there are solutions at and above the lower bound, add an additional bound. // DISABLED as this is causing non-termination in the integer solver. --mtrberzi /* rhs.push_back(m.mk_or( ctx.mk_eq_atom(str_len, m_autil.mk_numeral(lower_bound_value, true)), m_autil.mk_ge(str_len, m_autil.mk_numeral(refined_lower_bound, true)) )); */ } } else { if (refined_lower_bound.is_minus_one()) { // If there are no solutions at or above the lower bound, assert a conflict clause. rhs.push_back(m.mk_not(m_autil.mk_ge(str_len, m_autil.mk_numeral(lower_bound_value, true)))); } else { // If there are solutions above the lower bound but not at it, refine the bound. rhs.push_back(m_autil.mk_ge(str_len, m_autil.mk_numeral(refined_lower_bound, true))); } } if (!rhs.empty()) { expr_ref lhs_terms(mk_and(lhs), m); expr_ref rhs_terms(mk_and(rhs), m); assert_implication(lhs_terms, rhs_terms); regex_axiom_add = true; } } } else { // no existing automata/assumptions. // if it's easy to construct a full automaton for R, do so unsigned expected_complexity = estimate_regex_complexity(re); bool failureThresholdExceeded = (regex_get_counter(regex_fail_count, str_in_re) >= m_params.m_RegexAutomata_FailedAutomatonThreshold); if (expected_complexity <= m_params.m_RegexAutomata_DifficultyThreshold || failureThresholdExceeded) { eautomaton * aut = m_mk_aut(re); aut->compress(); regex_automata.push_back(aut); regex_automaton_under_assumptions new_aut(re, aut, true); if (!regex_automaton_assumptions.contains(re)) { regex_automaton_assumptions.insert(re, svector()); } regex_automaton_assumptions[re].push_back(new_aut); TRACE("str", tout << "add new automaton for " << mk_pp(re, m) << ": no assumptions" << std::endl;); regex_axiom_add = true; find_automaton_initial_bounds(str_in_re, aut); } else { // TODO check negation? // TODO construct a partial automaton for R to the given lower bound? if (false) { } else { regex_inc_counter(regex_fail_count, str_in_re); } } continue; } } else { // !lower_bound_exists // no bounds information // check for existing automata; // try to construct an automaton if we don't have one yet // and doing so without bounds is not difficult bool existingAutomata = (regex_automaton_assumptions.contains(re) && !regex_automaton_assumptions[re].empty()); bool failureThresholdExceeded = (regex_get_counter(regex_fail_count, str_in_re) >= m_params.m_RegexAutomata_FailedAutomatonThreshold); if (!existingAutomata) { unsigned expected_complexity = estimate_regex_complexity(re); if (expected_complexity <= m_params.m_RegexAutomata_DifficultyThreshold || failureThresholdExceeded) { eautomaton * aut = m_mk_aut(re); aut->compress(); regex_automata.push_back(aut); regex_automaton_under_assumptions new_aut(re, aut, true); if (!regex_automaton_assumptions.contains(re)) { regex_automaton_assumptions.insert(re, svector()); } regex_automaton_assumptions[re].push_back(new_aut); TRACE("str", tout << "add new automaton for " << mk_pp(re, m) << ": no assumptions" << std::endl;); regex_axiom_add = true; find_automaton_initial_bounds(str_in_re, aut); } else { regex_inc_counter(regex_fail_count, str_in_re); } } else { regex_inc_counter(regex_fail_count, str_in_re); } } } } // foreach (entry in regex_terms) for (obj_map >::iterator it = regex_terms_by_string.begin(); it != regex_terms_by_string.end(); ++it) { // TODO do we need to check equivalence classes of strings here? expr * str = it->m_key; ptr_vector str_in_re_terms = it->m_value; svector intersect_constraints; // we may find empty intersection before checking every constraint; // this vector keeps track of which ones actually take part in intersection svector used_intersect_constraints; // choose an automaton/assumption for each assigned (str.in.re) // that's consistent with the current length information for (ptr_vector::iterator term_it = str_in_re_terms.begin(); term_it != str_in_re_terms.end(); ++term_it) { expr * _unused = nullptr; expr * re = nullptr; SASSERT(u.str.is_in_re(*term_it)); u.str.is_in_re(*term_it, _unused, re); rational exact_len; bool has_exact_len = get_len_value(str, exact_len); rational lb, ub; bool has_lower_bound = lower_bound(mk_strlen(str), lb); bool has_upper_bound = upper_bound(mk_strlen(str), ub); if (regex_automaton_assumptions.contains(re) && !regex_automaton_assumptions[re].empty()){ for (svector::iterator aut_it = regex_automaton_assumptions[re].begin(); aut_it != regex_automaton_assumptions[re].end(); ++aut_it) { regex_automaton_under_assumptions aut = *aut_it; rational aut_ub; bool assume_ub = aut.get_upper_bound(aut_ub); rational aut_lb; bool assume_lb = aut.get_lower_bound(aut_lb); bool consistent = true; if (assume_ub) { // check consistency of assumed upper bound if (has_exact_len) { if (exact_len > aut_ub) { consistent = false; } } else { if (has_upper_bound && ub > aut_ub) { consistent = false; } } } if (assume_lb) { // check consistency of assumed lower bound if (has_exact_len) { if (exact_len < aut_lb) { consistent = false; } } else { if (has_lower_bound && lb < aut_lb) { consistent = false; } } } if (consistent) { intersect_constraints.push_back(aut); break; } } } } // foreach(term in str_in_re_terms) 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 == nullptr) { // start somewhere aut_inter = aut.get_automaton(); used_intersect_constraints.push_back(aut); continue; } TRACE("str", { unsigned v = regex_get_counter(regex_length_attempt_count, aut.get_regex_term()); tout << "length attempt count of " << mk_pp(aut.get_regex_term(), m) << " is " << v << ", threshold is " << m_params.m_RegexAutomata_LengthAttemptThreshold << std::endl; }); if (regex_get_counter(regex_length_attempt_count, aut.get_regex_term()) >= m_params.m_RegexAutomata_LengthAttemptThreshold) { unsigned intersectionDifficulty = estimate_automata_intersection_difficulty(aut_inter, aut.get_automaton()); TRACE("str", tout << "intersection difficulty is " << intersectionDifficulty << std::endl;); if (intersectionDifficulty <= m_params.m_RegexAutomata_IntersectionDifficultyThreshold || regex_get_counter(regex_intersection_fail_count, aut.get_regex_term()) >= m_params.m_RegexAutomata_FailedIntersectionThreshold) { expr * str_in_re_term(u.re.mk_in_re(str, aut.get_regex_term())); lbool current_assignment = ctx.get_assignment(str_in_re_term); // if the assignment is consistent with our assumption, use the automaton directly; // otherwise, complement it (and save that automaton for next time) // TODO we should cache these intermediate results // TODO do we need to push the intermediates into a vector for deletion anyway? if ( (current_assignment == l_true && aut.get_polarity()) || (current_assignment == l_false && !aut.get_polarity())) { aut_inter = m_mk_aut.mk_product(aut_inter, aut.get_automaton()); m_automata.push_back(aut_inter); } else { // need to complement first expr_ref rc(u.re.mk_complement(aut.get_regex_term()), m); eautomaton * aut_c = m_mk_aut(rc); regex_automata.push_back(aut_c); // TODO is there any way to build a complement automaton from an existing one? // this discards length information aut_inter = m_mk_aut.mk_product(aut_inter, aut_c); m_automata.push_back(aut_inter); } used_intersect_constraints.push_back(aut); if (aut_inter->is_empty()) { break; } } else { // failed intersection regex_inc_counter(regex_intersection_fail_count, aut.get_regex_term()); } } } // foreach(entry in intersect_constraints) if (aut_inter != nullptr) { aut_inter->compress(); } TRACE("str", tout << "intersected " << used_intersect_constraints.size() << " constraints" << std::endl;); expr_ref_vector conflict_terms(m); expr_ref conflict_lhs(m); for (svector::iterator aut_it = used_intersect_constraints.begin(); aut_it != used_intersect_constraints.end(); ++aut_it) { regex_automaton_under_assumptions aut = *aut_it; expr * str_in_re_term(u.re.mk_in_re(str, aut.get_regex_term())); lbool current_assignment = ctx.get_assignment(str_in_re_term); if (current_assignment == l_true) { conflict_terms.push_back(str_in_re_term); } else if (current_assignment == l_false) { conflict_terms.push_back(m.mk_not(str_in_re_term)); } // add length assumptions, if any rational ub; if (aut.get_upper_bound(ub)) { expr_ref ub_term(m_autil.mk_le(mk_strlen(str), m_autil.mk_numeral(ub, true)), m); conflict_terms.push_back(ub_term); } rational lb; if (aut.get_lower_bound(lb)) { expr_ref lb_term(m_autil.mk_ge(mk_strlen(str), m_autil.mk_numeral(lb, true)), m); conflict_terms.push_back(lb_term); } } conflict_lhs = mk_and(conflict_terms); 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)) { // initial state is final and it is the only final state // if there are no moves from the initial state, // the only solution is the empty string if (aut_inter->get_moves_from(initial_state).empty()) { TRACE("str", tout << "product automaton only accepts empty string" << std::endl;); expr_ref rhs1(ctx.mk_eq_atom(str, mk_string("")), m); expr_ref rhs2(ctx.mk_eq_atom(mk_strlen(str), m_autil.mk_numeral(rational::zero(), true)), m); expr_ref rhs(m.mk_and(rhs1, rhs2), m); assert_implication(conflict_lhs, rhs); regex_axiom_add = true; } } } 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); add_persisted_axiom(conflict_clause); regex_axiom_add = true; } } // foreach (entry in regex_terms_by_string) if (regex_axiom_add) { //return FC_CONTINUE; } } unsigned theory_str::estimate_regex_complexity(expr * re) { ENSURE(u.is_re(re)); expr * sub1; expr * sub2; unsigned lo, hi; if (u.re.is_to_re(re, sub1)) { if (!u.str.is_string(sub1)) throw default_exception("regular expressions must be built from string literals"); zstring str; u.str.is_string(sub1, str); return str.length(); } else if (u.re.is_complement(re, sub1)) { return estimate_regex_complexity_under_complement(sub1); } else if (u.re.is_concat(re, sub1, sub2)) { unsigned cx1 = estimate_regex_complexity(sub1); unsigned cx2 = estimate_regex_complexity(sub2); return _qadd(cx1, cx2); } else if (u.re.is_union(re, sub1, sub2)) { unsigned cx1 = estimate_regex_complexity(sub1); unsigned cx2 = estimate_regex_complexity(sub2); return _qadd(cx1, cx2); } else if (u.re.is_star(re, sub1) || u.re.is_plus(re, sub1)) { unsigned cx = estimate_regex_complexity(sub1); return _qmul(2, cx); } else if (u.re.is_loop(re, sub1, lo, hi) || u.re.is_loop(re, sub1, lo)) { unsigned cx = estimate_regex_complexity(sub1); return _qadd(lo, cx); } else if (u.re.is_range(re, sub1, sub2)) { SASSERT(u.str.is_string(sub1)); SASSERT(u.str.is_string(sub2)); zstring str1, str2; u.str.is_string(sub1, str1); u.str.is_string(sub2, str2); SASSERT(str1.length() == 1); SASSERT(str2.length() == 1); return 1 + str2[0] - str1[0]; } else if (u.re.is_full_char(re) || u.re.is_full_seq(re)) { return 1; } else { TRACE("str", tout << "WARNING: unknown regex term " << mk_pp(re, get_manager()) << std::endl;); return 1; } } unsigned theory_str::estimate_regex_complexity_under_complement(expr * re) { ENSURE(u.is_re(re)); expr * sub1; expr * sub2; unsigned lo, hi; if (u.re.is_to_re(re, sub1)) { SASSERT(u.str.is_string(sub1)); zstring str; u.str.is_string(sub1, str); return str.length(); } else if (u.re.is_complement(re, sub1)) { // Why don't we return the regular complexity here? // We could, but this might be called from under another complemented subexpression. // It's better to give a worst-case complexity. return estimate_regex_complexity_under_complement(sub1); } else if (u.re.is_concat(re, sub1, sub2)) { unsigned cx1 = estimate_regex_complexity_under_complement(sub1); unsigned cx2 = estimate_regex_complexity_under_complement(sub2); return _qadd(_qmul(2, cx1), cx2); } else if (u.re.is_union(re, sub1, sub2)) { unsigned cx1 = estimate_regex_complexity_under_complement(sub1); unsigned cx2 = estimate_regex_complexity_under_complement(sub2); return _qmul(cx1, cx2); } else if (u.re.is_star(re, sub1) || u.re.is_plus(re, sub1) || u.re.is_loop(re, sub1, lo, hi) || u.re.is_loop(re, sub1, lo)) { unsigned cx = estimate_regex_complexity_under_complement(sub1); return _qmul(2, cx); } else if (u.re.is_range(re, sub1, sub2)) { SASSERT(u.str.is_string(sub1)); SASSERT(u.str.is_string(sub2)); zstring str1, str2; u.str.is_string(sub1, str1); u.str.is_string(sub2, str2); SASSERT(str1.length() == 1); SASSERT(str2.length() == 1); return 1 + str2[0] - str1[0]; } else if (u.re.is_full_char(re) || u.re.is_full_seq(re)) { return 1; } else { TRACE("str", tout << "WARNING: unknown regex term " << mk_pp(re, get_manager()) << std::endl;); return 1; } } unsigned theory_str::estimate_automata_intersection_difficulty(eautomaton * aut1, eautomaton * aut2) { ENSURE(aut1 != nullptr); ENSURE(aut2 != nullptr); return _qmul(aut1->num_states(), aut2->num_states()); } // Check whether a regex translates well to a linear set of length constraints. bool theory_str::check_regex_length_linearity(expr * re) { return check_regex_length_linearity_helper(re, false); } bool theory_str::check_regex_length_linearity_helper(expr * re, bool already_star) { expr * sub1; expr * sub2; unsigned lo, hi; if (u.re.is_to_re(re)) { return true; } else if (u.re.is_concat(re, sub1, sub2)) { return check_regex_length_linearity_helper(sub1, already_star) && check_regex_length_linearity_helper(sub2, already_star); } else if (u.re.is_union(re, sub1, sub2)) { return check_regex_length_linearity_helper(sub1, already_star) && check_regex_length_linearity_helper(sub2, already_star); } else if (u.re.is_star(re, sub1) || u.re.is_plus(re, sub1)) { if (already_star) { return false; } else { return check_regex_length_linearity_helper(sub1, true); } } else if (u.re.is_range(re)) { return true; } else if (u.re.is_full_char(re)) { return true; } else if (u.re.is_full_seq(re)) { return true; } else if (u.re.is_complement(re)) { // TODO can we do better? return false; } else if (u.re.is_loop(re, sub1, lo, hi) || u.re.is_loop(re, sub1, lo)) { return check_regex_length_linearity_helper(sub1, already_star); } else { TRACE("str", tout << "WARNING: unknown regex term " << mk_pp(re, get_manager()) << std::endl;); UNREACHABLE(); return false; } } // note: returns an empty set `lens` if something went wrong void theory_str::check_subterm_lengths(expr * re, integer_set & lens) { expr * sub1; expr * sub2; unsigned lo, hi; if (u.re.is_to_re(re, sub1)) { SASSERT(u.str.is_string(sub1)); zstring str; u.str.is_string(sub1, str); lens.insert(str.length()); } else if (u.re.is_concat(re, sub1, sub2)) { integer_set lens_1, lens_2; check_subterm_lengths(sub1, lens_1); check_subterm_lengths(sub2, lens_2); if (lens_1.empty() || lens_2.empty()) { lens.reset(); } else { // take all pairwise lengths for (integer_set::iterator it1 = lens_1.begin(); it1 != lens_1.end(); ++it1) { for(integer_set::iterator it2 = lens_2.begin(); it2 != lens_2.end(); ++it2) { int l1 = *it1; int l2 = *it2; lens.insert(l1 + l2); } } } } else if (u.re.is_union(re, sub1, sub2)) { integer_set lens_1, lens_2; check_subterm_lengths(sub1, lens_1); check_subterm_lengths(sub2, lens_2); if (lens_1.empty() || lens_2.empty()) { lens.reset(); } else { // take all possibilities from either side for (integer_set::iterator it1 = lens_1.begin(); it1 != lens_1.end(); ++it1) { lens.insert(*it1); } for (integer_set::iterator it2 = lens_2.begin(); it2 != lens_2.end(); ++it2) { lens.insert(*it2); } } } else if (u.re.is_star(re, sub1) || u.re.is_plus(re, sub1)) { // this is bad -- term generation requires this not to appear lens.reset(); } else if (u.re.is_range(re, sub1, sub2)) { SASSERT(u.str.is_string(sub1)); SASSERT(u.str.is_string(sub2)); zstring str1, str2; u.str.is_string(sub1, str1); u.str.is_string(sub2, str2); SASSERT(str1.length() == 1); SASSERT(str2.length() == 1); lens.insert(1); } else if (u.re.is_full_char(re)) { lens.insert(1); } else if (u.re.is_full_seq(re)) { lens.reset(); } else if (u.re.is_complement(re)) { lens.reset(); } else if (u.re.is_loop(re, sub1, lo, hi)) { integer_set lens_1; check_subterm_lengths(sub1, lens_1); for (unsigned i = lo; i <= hi; ++i) { for (auto j : lens_1) { lens.insert(i * j); } } } else { TRACE("str", tout << "WARNING: unknown regex term " << mk_pp(re, get_manager()) << std::endl;); lens.reset(); } } /* * Infer all length constraints implied by the given regular expression `re` * in order to constrain `lenVar` (which must be of sort Int). * This assumes that `re` appears in a positive context. * Returns a Boolean formula expressing the appropriate constraints over `lenVar`. * In some cases, the returned formula requires one or more free integer variables to be created. * These variables are returned in the reference parameter `freeVariables`. * Extra assertions should be made for these free variables constraining them to be non-negative. */ expr_ref theory_str::infer_all_regex_lengths(expr * lenVar, expr * re, expr_ref_vector & freeVariables) { ENSURE(u.is_re(re)); context & ctx = get_context(); ast_manager & m = get_manager(); expr * sub1; expr * sub2; unsigned lo, hi; if (u.re.is_to_re(re, sub1)) { if (!u.str.is_string(sub1)) throw default_exception("regular expressions must be built from string literals"); zstring str; u.str.is_string(sub1, str); rational strlen(str.length()); expr_ref retval(ctx.mk_eq_atom(lenVar, m_autil.mk_numeral(strlen, true)), m); return retval; } else if (u.re.is_union(re, sub1, sub2)) { expr_ref r1 = infer_all_regex_lengths(lenVar, sub1, freeVariables); expr_ref r2 = infer_all_regex_lengths(lenVar, sub2, freeVariables); expr_ref retval(m.mk_or(r1, r2), m); return retval; } else if (u.re.is_concat(re, sub1, sub2)) { expr * v1 = mk_int_var("rlen1"); expr * v2 = mk_int_var("rlen2"); freeVariables.push_back(v1); freeVariables.push_back(v2); expr_ref r1 = infer_all_regex_lengths(v1, sub1, freeVariables); expr_ref r2 = infer_all_regex_lengths(v2, sub2, freeVariables); expr_ref_vector finalResult(m); finalResult.push_back(ctx.mk_eq_atom(lenVar, m_autil.mk_add(v1, v2))); finalResult.push_back(r1); finalResult.push_back(r2); expr_ref retval(mk_and(finalResult), m); return retval; } else if (u.re.is_star(re, sub1) || u.re.is_plus(re, sub1)) { // stars are generated as a linear combination of all possible subterm lengths; // this requires that there are no stars under this one /* expr * v = mk_int_var("rlen"); expr * n = mk_int_var("rstar"); freeVariables.push_back(v); freeVariables.push_back(n); expr_ref rsub = infer_all_regex_lengths(v, sub1, freeVariables); expr_ref_vector finalResult(m); finalResult.push_back(rsub); finalResult.push_back(ctx.mk_eq_atom(lenVar, m_autil.mk_mul(v, n))); expr_ref retval(mk_and(finalResult), m); return retval; */ integer_set subterm_lens; check_subterm_lengths(sub1, subterm_lens); if (subterm_lens.empty()) { // somehow generation was impossible expr_ref retval(m_autil.mk_ge(lenVar, m_autil.mk_numeral(rational::zero(), true)), m); return retval; } else { TRACE("str", tout << "subterm lengths:"; for(integer_set::iterator it = subterm_lens.begin(); it != subterm_lens.end(); ++it) { tout << " " << *it; } tout << std::endl;); expr_ref_vector sum_terms(m); for (integer_set::iterator it = subterm_lens.begin(); it != subterm_lens.end(); ++it) { rational lenOption(*it); expr * n = mk_int_var("rstar"); freeVariables.push_back(n); expr_ref term(m_autil.mk_mul(m_autil.mk_numeral(lenOption, true), n), m); expr_ref term2(term, m); if (u.re.is_plus(re)) { // n effectively starts at 1 term2 = m_autil.mk_add(m_autil.mk_numeral(lenOption, true), term); } sum_terms.push_back(term2); } expr_ref retval(ctx.mk_eq_atom(lenVar, m_autil.mk_add_simplify(sum_terms)), m); return retval; } } else if (u.re.is_loop(re, sub1, lo, hi)) { expr * v1 = mk_int_var("rlen"); freeVariables.push_back(v1); expr_ref r1 = infer_all_regex_lengths(v1, sub1, freeVariables); expr_ref_vector v1_choices(m); for (unsigned i = lo; i <= hi; ++i) { rational rI(i); expr_ref v1_i(ctx.mk_eq_atom(lenVar, m_autil.mk_mul(m_autil.mk_numeral(rI, true), v1)), m); v1_choices.push_back(v1_i); } expr_ref_vector finalResult(m); finalResult.push_back(r1); finalResult.push_back(mk_or(v1_choices)); expr_ref retval(mk_and(finalResult), m); SASSERT(retval); return retval; } else if (u.re.is_range(re, sub1, sub2)) { SASSERT(u.str.is_string(sub1)); SASSERT(u.str.is_string(sub2)); zstring str1, str2; u.str.is_string(sub1, str1); u.str.is_string(sub2, str2); SASSERT(str1.length() == 1); SASSERT(str2.length() == 1); expr_ref retval(ctx.mk_eq_atom(lenVar, m_autil.mk_numeral(rational::one(), true)), m); return retval; } else if (u.re.is_full_char(re)) { expr_ref retval(ctx.mk_eq_atom(lenVar, m_autil.mk_numeral(rational::one(), true)), m); return retval; } else if (u.re.is_full_seq(re)) { // match any unbounded string expr_ref retval(m_autil.mk_ge(lenVar, m_autil.mk_numeral(rational::zero(), true)), m); return retval; } else if (u.re.is_complement(re)) { // skip complement for now, in general this is difficult to predict expr_ref retval(m_autil.mk_ge(lenVar, m_autil.mk_numeral(rational::zero(), true)), m); return retval; } else { TRACE("str", tout << "WARNING: unknown regex term " << mk_pp(re, m) << std::endl;); expr_ref retval(m_autil.mk_ge(lenVar, m_autil.mk_numeral(rational::zero(), true)), m); return retval; } } /* * Assert initial lower and upper bounds for the positive constraint (str in re) corresponding * to the automaton `aut`. * This asserts a constraint of the form: * str_in_re --> (len(str) ?= 0 OR len(str) >= lb) AND len(str) <= ub * where the upper bound clause is omitted if the upper bound doesn't exist * and the equality with 0 is based on whether solutions of length 0 are allowed. */ void theory_str::find_automaton_initial_bounds(expr * str_in_re, eautomaton * aut) { ENSURE(aut != nullptr); context & ctx = get_context(); ast_manager & m = get_manager(); expr_ref_vector rhs(m); expr * str = nullptr; expr * re = nullptr; u.str.is_in_re(str_in_re, str, re); expr_ref strlen(mk_strlen(str), m); // lower bound first rational nonzero_lower_bound; bool zero_sol_exists = refine_automaton_lower_bound(aut, rational::zero(), nonzero_lower_bound); if (zero_sol_exists) { regex_last_lower_bound.insert(str, rational::zero()); // solution at 0 if (!nonzero_lower_bound.is_minus_one()) { expr_ref rhs1(ctx.mk_eq_atom(strlen, m_autil.mk_numeral(rational::zero(), true)), m); expr_ref rhs2(m_autil.mk_ge(strlen, m_autil.mk_numeral(nonzero_lower_bound, true)), m); rhs.push_back(m.mk_or(rhs1, rhs2)); } else { // shouldn't happen UNREACHABLE(); } } else { // no solution at 0 if (!nonzero_lower_bound.is_minus_one()) { regex_last_lower_bound.insert(str, nonzero_lower_bound); expr_ref rhs2(m_autil.mk_ge(strlen, m_autil.mk_numeral(nonzero_lower_bound, true)), m); rhs.push_back(rhs2); } else { // shouldn't happen UNREACHABLE(); } } // TODO upper bound check if (!rhs.empty()) { expr_ref lhs(str_in_re, m); expr_ref _rhs(mk_and(rhs), m); assert_implication(lhs, _rhs); } } /* * Refine the lower bound on the length of a solution to a given automaton. * The method returns TRUE if a solution of length `current_lower_bound` exists, * and FALSE otherwise. In addition, the reference parameter `refined_lower_bound` * is assigned the length of the shortest solution longer than `current_lower_bound` * if it exists, or -1 otherwise. */ bool theory_str::refine_automaton_lower_bound(eautomaton * aut, rational current_lower_bound, rational & refined_lower_bound) { ENSURE(aut != nullptr); if (aut->final_states().empty()) { // no solutions at all refined_lower_bound = rational::minus_one(); return false; } // from here we assume that there is a final state reachable from the initial state unsigned_vector search_queue; // populate search_queue with all states reachable from the epsilon-closure of start state aut->get_epsilon_closure(aut->init(), search_queue); unsigned search_depth = 0; hashtable> next_states; unsigned_vector next_search_queue; bool found_solution_at_lower_bound = false; while (!search_queue.empty()) { // if we are at the lower bound, check for final states if (search_depth == current_lower_bound.get_unsigned()) { for (unsigned_vector::iterator it = search_queue.begin(); it != search_queue.end(); ++it) { unsigned state = *it; if (aut->is_final_state(state)) { found_solution_at_lower_bound = true; break; } } // end phase 1 break; } next_states.reset(); next_search_queue.clear(); // move one step along all states for (unsigned_vector::iterator it = search_queue.begin(); it != search_queue.end(); ++it) { unsigned src = *it; eautomaton::moves next_moves; aut->get_moves_from(src, next_moves, true); for (eautomaton::moves::iterator move_it = next_moves.begin(); move_it != next_moves.end(); ++move_it) { unsigned dst = move_it->dst(); if (!next_states.contains(dst)) { next_states.insert(dst); next_search_queue.push_back(dst); } } } search_queue.clear(); search_queue.append(next_search_queue); search_depth += 1; } // !search_queue.empty() // if we got here before reaching the lower bound, // there aren't any solutions at or above it, so stop if (search_depth < current_lower_bound.get_unsigned()) { refined_lower_bound = rational::minus_one(); return false; } // phase 2: continue exploring the automaton above the lower bound SASSERT(search_depth == current_lower_bound.get_unsigned()); while (!search_queue.empty()) { if (search_depth > current_lower_bound.get_unsigned()) { // check if we have found a solution above the lower bound for (unsigned_vector::iterator it = search_queue.begin(); it != search_queue.end(); ++it) { unsigned state = *it; if (aut->is_final_state(state)) { // this is a solution at a depth higher than the lower bound refined_lower_bound = rational(search_depth); return found_solution_at_lower_bound; } } } next_states.reset(); next_search_queue.clear(); // move one step along all states for (unsigned_vector::iterator it = search_queue.begin(); it != search_queue.end(); ++it) { unsigned src = *it; eautomaton::moves next_moves; aut->get_moves_from(src, next_moves, true); for (eautomaton::moves::iterator move_it = next_moves.begin(); move_it != next_moves.end(); ++move_it) { unsigned dst = move_it->dst(); if (!next_states.contains(dst)) { next_states.insert(dst); next_search_queue.push_back(dst); } } } search_queue.clear(); search_queue.append(next_search_queue); search_depth += 1; } // if we reached this point, we explored the whole automaton and didn't find any // solutions above the lower bound refined_lower_bound = rational::minus_one(); return found_solution_at_lower_bound; } /* * Refine the upper bound on the length of a solution to a given automaton. * The method returns TRUE if a solution of length `current_upper_bound` exists, * and FALSE otherwise. In addition, the reference parameter `refined_upper_bound` * is assigned the length of the longest solution shorter than `current_upper_bound`, * if a shorter solution exists, or -1 otherwise. */ bool theory_str::refine_automaton_upper_bound(eautomaton * aut, rational current_upper_bound, rational & refined_upper_bound) { ENSURE(aut != nullptr); if (aut->final_states().empty()) { // no solutions at all! refined_upper_bound = rational::minus_one(); return false; } // from here we assume there is a final state reachable from the initial state unsigned_vector search_queue; // populate search queue with all states reachable from the epsilon-closure of the start state aut->get_epsilon_closure(aut->init(), search_queue); rational last_solution_depth = rational::minus_one(); bool found_solution_at_upper_bound = false; unsigned search_depth = 0; hashtable > next_states; unsigned_vector next_search_queue; while(!search_queue.empty()) { // see if any of the current states are final for (unsigned_vector::iterator it = search_queue.begin(); it != search_queue.end(); ++it) { unsigned src = *it; if (aut->is_final_state(src)) { if (search_depth == current_upper_bound.get_unsigned()) { found_solution_at_upper_bound = true; } else { last_solution_depth = rational(search_depth); } break; } } if (search_depth == current_upper_bound.get_unsigned()) { break; } next_states.reset(); next_search_queue.clear(); // move one step along all states for (unsigned_vector::iterator it = search_queue.begin(); it != search_queue.end(); ++it) { unsigned src = *it; eautomaton::moves next_moves; aut->get_moves_from(src, next_moves, true); for (eautomaton::moves::iterator moves_it = next_moves.begin(); moves_it != next_moves.end(); ++moves_it) { unsigned dst = moves_it->dst(); if (!next_states.contains(dst)) { next_states.insert(dst); next_search_queue.push_back(dst); } } } search_queue.clear(); search_queue.append(next_search_queue); search_depth += 1; } //!search_queue.empty() refined_upper_bound = last_solution_depth; return found_solution_at_upper_bound; } void theory_str::aut_path_add_next(u_map& next, expr_ref_vector& trail, unsigned idx, expr* cond) { expr* acc; if (!get_manager().is_true(cond) && next.find(idx, acc)) { expr* args[2] = { cond, acc }; cond = mk_or(get_manager(), 2, args); } trail.push_back(cond); next.insert(idx, cond); } expr_ref theory_str::aut_path_rewrite_constraint(expr * cond, expr * ch_var) { context & ctx = get_context(); ast_manager & m = get_manager(); expr_ref retval(m); unsigned char_val = 0; expr * lhs; expr * rhs; 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); retval = u.str.mk_string(str_const); return retval; } else if (is_var(cond)) { TRACE("str", tout << "substitute var" << std::endl;); retval = ch_var; return retval; } else if (m.is_eq(cond, lhs, rhs)) { // handle this specially because the sort of the equality will change expr_ref new_lhs(aut_path_rewrite_constraint(lhs, ch_var), m); SASSERT(new_lhs); expr_ref new_rhs(aut_path_rewrite_constraint(rhs, ch_var), m); SASSERT(new_rhs); retval = ctx.mk_eq_atom(new_lhs, new_rhs); return retval; } else if (m.is_bool(cond)) { TRACE("str", tout << "rewrite boolean term " << mk_pp(cond, m) << std::endl;); app * a_cond = to_app(cond); expr_ref_vector rewritten_args(m); for (unsigned i = 0; i < a_cond->get_num_args(); ++i) { expr * argI = a_cond->get_arg(i); expr_ref new_arg(aut_path_rewrite_constraint(argI, ch_var), m); SASSERT(new_arg); rewritten_args.push_back(new_arg); } retval = m.mk_app(a_cond->get_decl(), rewritten_args.c_ptr()); TRACE("str", tout << "final rewritten term is " << mk_pp(retval, m) << std::endl;); return retval; } else { TRACE("str", tout << "ERROR: unrecognized automaton path constraint " << mk_pp(cond, m) << ", cannot translate" << std::endl;); retval = nullptr; return retval; } } /* * Create finite path constraints for the string variable `str` with respect to the automaton `aut`. * The returned expression is the right-hand side of a constraint of the form * (str in re) AND (|str| = len) AND (any applicable length assumptions on aut) -> (rhs AND character constraints). * The character constraints, which are (str = c0 . c1 . (...) . cn) and (|c0| = 1, ...), * are returned in `characterConstraints`. */ expr_ref theory_str::generate_regex_path_constraints(expr * stringTerm, eautomaton * aut, rational lenVal, expr_ref & characterConstraints) { ENSURE(aut != nullptr); context & ctx = get_context(); ast_manager & m = get_manager(); if (lenVal.is_zero()) { // if any state in the epsilon-closure of the start state is accepting, // then the empty string is in this language unsigned_vector states; bool has_final = false; aut->get_epsilon_closure(aut->init(), states); for (unsigned i = 0; i < states.size() && !has_final; ++i) { has_final = aut->is_final_state(states[i]); } if (has_final) { // empty string is OK, assert axiom expr_ref rhs(ctx.mk_eq_atom(stringTerm, mk_string("")), m); SASSERT(rhs); //regex_automata_assertions.insert(stringTerm, final_axiom); //m_trail_stack.push(insert_obj_map(regex_automata_assertions, stringTerm) ); return rhs; } else { // negate -- the empty string isn't in the language //expr_ref conflict(m.mk_not(mk_and(toplevel_lhs)), m); //assert_axiom(conflict); expr_ref conflict(m.mk_false(), m); return conflict; } } // lenVal.is_zero() expr_ref_vector pathChars(m); expr_ref_vector pathChars_len_constraints(m); // reuse character terms over the same string if (string_chars.contains(stringTerm)) { // find out whether we have enough characters already ptr_vector old_chars; string_chars.find(stringTerm, old_chars); if (old_chars.size() < lenVal.get_unsigned()) { for (unsigned i = old_chars.size(); i < lenVal.get_unsigned(); ++i) { std::stringstream ss; ss << "ch" << i; expr_ref ch(mk_str_var(ss.str()), m); m_trail.push_back(ch); old_chars.push_back(ch); } } string_chars.insert(stringTerm, old_chars); // now we're guaranteed to have at least the right number of characters in old_chars for (unsigned i = 0; i < lenVal.get_unsigned(); ++i) { expr_ref ch(old_chars.get(i), m); refresh_theory_var(ch); pathChars.push_back(ch); pathChars_len_constraints.push_back(ctx.mk_eq_atom(mk_strlen(ch), m_autil.mk_numeral(rational::one(), true))); } } else { ptr_vector new_chars; for (unsigned i = 0; i < lenVal.get_unsigned(); ++i) { std::stringstream ss; ss << "ch" << i; expr_ref ch(mk_str_var(ss.str()), m); pathChars.push_back(ch); pathChars_len_constraints.push_back(ctx.mk_eq_atom(mk_strlen(ch), m_autil.mk_numeral(rational::one(), true))); new_chars.push_back(ch); } string_chars.insert(stringTerm, new_chars); } // modification of code in seq_rewriter::mk_str_in_regexp() expr_ref_vector trail(m); u_map maps[2]; bool select_map = false; expr_ref ch(m), cond(m); eautomaton::moves mvs; maps[0].insert(aut->init(), m.mk_true()); // is_accepted(a, aut) & some state in frontier is final. for (unsigned i = 0; i < lenVal.get_unsigned(); ++i) { u_map& frontier = maps[select_map]; u_map& next = maps[!select_map]; select_map = !select_map; ch = pathChars.get(i); next.reset(); u_map::iterator it = frontier.begin(), end = frontier.end(); for (; it != end; ++it) { mvs.reset(); unsigned state = it->m_key; expr* acc = it->m_value; aut->get_moves_from(state, mvs, false); for (unsigned j = 0; j < mvs.size(); ++j) { eautomaton::move const& mv = mvs[j]; SASSERT(mv.t()); if (mv.t()->is_char() && m.is_value(mv.t()->get_char())) { // change this to a string constraint expr_ref cond_rhs = aut_path_rewrite_constraint(mv.t()->get_char(), ch); SASSERT(cond_rhs); cond = ctx.mk_eq_atom(ch, cond_rhs); SASSERT(cond); expr * args[2] = {cond, acc}; cond = mk_and(m, 2, args); aut_path_add_next(next, trail, mv.dst(), cond); } else if (mv.t()->is_range()) { expr_ref range_lo(mv.t()->get_lo(), m); expr_ref range_hi(mv.t()->get_hi(), m); unsigned lo_val, hi_val; 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) { // 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; 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)); } cond_rhs = mk_or(cond_rhs_terms); SASSERT(cond_rhs); expr * args[2] = {cond_rhs, acc}; cond = mk_and(m, 2, args); aut_path_add_next(next, trail, mv.dst(), cond); } else { TRACE("str", tout << "warning: non-bitvectors in automaton range predicate" << std::endl;); UNREACHABLE(); } } else if (mv.t()->is_pred()) { // rewrite this constraint over string terms expr_ref cond_rhs = aut_path_rewrite_constraint(mv.t()->get_pred(), ch); SASSERT(cond_rhs); if (m.is_false(cond_rhs)) { continue; } else if (m.is_true(cond_rhs)) { aut_path_add_next(next, trail, mv.dst(), acc); continue; } expr * args[2] = {cond_rhs, acc}; cond = mk_and(m, 2, args); aut_path_add_next(next, trail, mv.dst(), cond); } } } } 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) { unsigned_vector states; bool has_final = false; aut->get_epsilon_closure(it->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); } } expr_ref result(mk_or(ors)); TRACE("str", tout << "regex path constraint: " << mk_pp(result, m) << "\n";); expr_ref concat_rhs(m); if (pathChars.size() == 1) { concat_rhs = ctx.mk_eq_atom(stringTerm, pathChars.get(0)); } else { expr_ref acc(pathChars.get(0), m); for (unsigned i = 1; i < pathChars.size(); ++i) { acc = mk_concat(acc, pathChars.get(i)); } concat_rhs = ctx.mk_eq_atom(stringTerm, acc); } //expr_ref toplevel_rhs(m.mk_and(result, mk_and(pathChars_len_constraints), concat_rhs), m); characterConstraints = m.mk_and(mk_and(pathChars_len_constraints), concat_rhs); //expr_ref final_axiom(rewrite_implication(mk_and(toplevel_lhs), toplevel_rhs), m); //regex_automata_assertions.insert(stringTerm, final_axiom); //m_trail_stack.push(insert_obj_map(regex_automata_assertions, stringTerm) ); return result; } void theory_str::regex_inc_counter(obj_map & counter_map, expr * key) { unsigned old_v; if (counter_map.find(key, old_v)) { unsigned new_v = old_v += 1; counter_map.insert(key, new_v); } else { counter_map.insert(key, 1); } } unsigned theory_str::regex_get_counter(obj_map & counter_map, expr * key) { unsigned v; if (counter_map.find(key, v)) { return v; } else { counter_map.insert(key, 0); return 0; } } }; /* namespace smt */ z3-z3-4.8.7/src/smt/theory_utvpi.cpp000066400000000000000000000111721356505360400172700ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: theory_utvpi.h Author: Nikolaj Bjorner (nbjorner) 2013-04-26 Revision History: The implementaton is derived from theory_diff_logic. --*/ #include "smt/theory_utvpi.h" #include "smt/theory_utvpi_def.h" namespace smt { template class theory_utvpi; template class theory_utvpi; // similar to test_diff_logic: utvpi_tester::utvpi_tester(ast_manager& m): m(m), a(m) {} bool utvpi_tester::operator()(expr* e) { m_todo.reset(); m_mark.reset(); m_todo.push_back(e); expr* e1, *e2; while (!m_todo.empty()) { expr* e = m_todo.back(); m_todo.pop_back(); if (!m_mark.is_marked(e)) { m_mark.mark(e, true); if (is_var(e)) { continue; } if (!is_app(e)) { return false; } app* ap = to_app(e); if (m.is_eq(ap, e1, e2)) { if (!linearize(e1, e2)) { return false; } } else if (ap->get_family_id() == m.get_basic_family_id()) { continue; } else if (a.is_le(e, e1, e2) || a.is_ge(e, e2, e1) || a.is_lt(e, e1, e2) || a.is_gt(e, e2, e1)) { if (!linearize(e1, e2)) { return false; } } else if (is_uninterp_const(e)) { continue; } else { return false; } } } return true; } vector > const& utvpi_tester::get_linearization() const { SASSERT(m_terms.size() <= 2); return m_terms; } bool utvpi_tester::operator()(unsigned num_fmls, expr* const* fmls) { for (unsigned i = 0; i < num_fmls; ++i) { if (!(*this)(fmls[i])) { return false; } } return true; } bool utvpi_tester::linearize(expr* e) { m_terms.reset(); m_terms.push_back(std::make_pair(e, rational(1))); return linearize(); } bool utvpi_tester::linearize(expr* e1, expr* e2) { m_terms.reset(); m_terms.push_back(std::make_pair(e1, rational(1))); m_terms.push_back(std::make_pair(e2, rational(-1))); return linearize(); } bool utvpi_tester::linearize() { m_weight.reset(); m_coeff_map.reset(); while (!m_terms.empty()) { expr* e1, *e2; rational num; rational mul = m_terms.back().second; expr* e = m_terms.back().first; m_terms.pop_back(); if (a.is_add(e)) { for (unsigned i = 0; i < to_app(e)->get_num_args(); ++i) { m_terms.push_back(std::make_pair(to_app(e)->get_arg(i), mul)); } } else if (a.is_mul(e, e1, e2) && a.is_numeral(e1, num)) { m_terms.push_back(std::make_pair(e2, mul*num)); } else if (a.is_mul(e, e2, e1) && a.is_numeral(e1, num)) { m_terms.push_back(std::make_pair(e2, mul*num)); } else if (a.is_sub(e, e1, e2)) { m_terms.push_back(std::make_pair(e1, mul)); m_terms.push_back(std::make_pair(e2, -mul)); } else if (a.is_uminus(e, e1)) { m_terms.push_back(std::make_pair(e1, -mul)); } else if (a.is_numeral(e, num)) { m_weight += num*mul; } else if (a.is_to_real(e, e1)) { m_terms.push_back(std::make_pair(e1, mul)); } else if (!is_uninterp_const(e)) { return false; } else { m_coeff_map.insert_if_not_there2(e, rational(0))->get_data().m_value += mul; } } obj_map::iterator it = m_coeff_map.begin(); obj_map::iterator end = m_coeff_map.end(); for (; it != end; ++it) { rational r = it->m_value; if (r.is_zero()) { continue; } m_terms.push_back(std::make_pair(it->m_key, r)); if (m_terms.size() > 2) { return false; } if (!r.is_one() && !r.is_minus_one()) { return false; } } return true; } } z3-z3-4.8.7/src/smt/theory_utvpi.h000066400000000000000000000230201356505360400167300ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: theory_utvpi.h Abstract: use Bellman Ford traversal algorithm for UTVPI. Author: Nikolaj Bjorner (nbjorner) 2013-04-26 Revision History: The implementaton is derived from theory_diff_logic. --*/ #ifndef THEORY_UTVPI_H_ #define THEORY_UTVPI_H_ #include "smt/theory_diff_logic.h" namespace smt { class utvpi_tester { ast_manager& m; arith_util a; ptr_vector m_todo; ast_mark m_mark; obj_map m_coeff_map; rational m_weight; vector > m_terms; public: utvpi_tester(ast_manager& m); // test if formula is in the Horn inequality fragment: bool operator()(expr* fml); bool operator()(unsigned num_fmls, expr* const* fmls); // linearize inequality/equality bool linearize(expr* e); bool linearize(expr* e1, expr* e2); // retrieve linearization vector > const& get_linearization() const; rational const& get_weight() const { return m_weight; } private: bool linearize(); }; template class theory_utvpi : public theory, private Ext { typedef typename Ext::numeral numeral; typedef theory_var th_var; typedef svector th_var_vector; typedef vector > coeffs; class assignment_trail; class parent_trail; struct GExt : public Ext { typedef std::pair explanation; }; class atom { protected: bool_var m_bvar; bool m_true; int m_pos; int m_neg; public: atom(bool_var bv, int pos, int neg) : m_bvar(bv), m_true(false), m_pos(pos), m_neg(neg) {} ~atom() {} bool_var get_bool_var() const { return m_bvar; } void assign_eh(bool is_true) { m_true = is_true; } int get_asserted_edge() const { return this->m_true?m_pos:m_neg; } int get_pos() const { return m_pos; } int get_neg() const { return m_neg; } std::ostream& display(theory_utvpi const& th, std::ostream& out) const; }; typedef svector atoms; struct scope { unsigned m_atoms_lim; unsigned m_asserted_atoms_lim; unsigned m_asserted_qhead_old; }; struct stats { unsigned m_num_conflicts; unsigned m_num_assertions; unsigned m_num_core2th_eqs; unsigned m_num_core2th_diseqs; void reset() { memset(this, 0, sizeof(*this)); } stats() { reset(); } }; // Functor used to collect the proofs for a conflict due to // a negative cycle. class nc_functor { literal_vector m_antecedents; unsigned_vector m_coeffs; theory_utvpi& m_super; public: nc_functor(theory_utvpi& s) : m_super(s) {} void reset() { m_antecedents.reset(); m_coeffs.reset(); } literal_vector const& get_lits() const { return m_antecedents; } unsigned_vector const& get_coeffs() const { return m_coeffs; } void operator()(std::pair const & ex) { if (ex.first != null_literal) { m_antecedents.push_back(ex.first); m_coeffs.push_back(ex.second); } } void new_edge(dl_var src, dl_var dst, unsigned num_edges, edge_id const* edges) { m_super.new_edge(src, dst, num_edges, edges); } }; stats m_stats; smt_params m_params; arith_util a; arith_eq_adapter m_arith_eq_adapter; th_var m_zero; //cache the variable representing the zero variable. dl_graph m_graph; nc_functor m_nc_functor; atoms m_atoms; unsigned_vector m_asserted_atoms; // set of asserted atoms unsigned m_asserted_qhead; u_map m_bool_var2atom; svector m_scopes; double m_agility; bool m_lia; bool m_lra; bool m_non_utvpi_exprs; utvpi_tester m_test; arith_factory * m_factory; rational m_delta; // Set a conflict due to a negative cycle. void set_conflict(); void new_edge(dl_var src, dl_var dst, unsigned num_edges, edge_id const* edges) {} // Create a new theory variable. th_var mk_var(enode* n) override; virtual th_var mk_var(expr* n); void compute_delta(); void found_non_utvpi_expr(expr * n); bool is_interpreted(app* n) const { return n->get_family_id() == get_family_id(); } public: theory_utvpi(ast_manager& m); ~theory_utvpi() override; theory * mk_fresh(context * new_ctx) override; char const * get_name() const override { return "utvpi-logic"; } /** \brief See comment in theory::mk_eq_atom */ app * mk_eq_atom(expr * lhs, expr * rhs) override { return a.mk_eq(lhs, rhs); } void init(context * ctx) override; bool internalize_atom(app * atom, bool gate_ctx) override; bool internalize_term(app * term) override; void internalize_eq_eh(app * atom, bool_var v) override; void assign_eh(bool_var v, bool is_true) override; void new_eq_eh(th_var v1, th_var v2) override { m_stats.m_num_core2th_eqs++; m_arith_eq_adapter.new_eq_eh(v1, v2); } bool use_diseqs() const override { return true; } void new_diseq_eh(th_var v1, th_var v2) override { m_arith_eq_adapter.new_diseq_eh(v1, v2); } void push_scope_eh() override; void pop_scope_eh(unsigned num_scopes) override; void restart_eh() override { m_arith_eq_adapter.restart_eh(); } void relevant_eh(app* e) override {} void init_search_eh() override { m_arith_eq_adapter.init_search_eh(); } final_check_status final_check_eh() override; bool is_shared(th_var v) const override { return false; } bool can_propagate() override { SASSERT(m_asserted_qhead <= m_asserted_atoms.size()); return m_asserted_qhead != m_asserted_atoms.size(); } void propagate() override; justification * why_is_diseq(th_var v1, th_var v2) override { UNREACHABLE(); return nullptr; } void reset_eh() override; void init_model(model_generator & m) override; model_value_proc * mk_value(enode * n, model_generator & mg) override; void display(std::ostream & out) const override; void collect_statistics(::statistics & st) const override; private: rational mk_value(theory_var v, bool is_int); bool is_parity_ok(unsigned v) const; void enforce_parity(); bool eval(expr* e); void model_validate(); rational eval_num(expr* e); bool check_z_consistency(); virtual void new_eq_eh(th_var v1, th_var v2, justification& j) { m_stats.m_num_core2th_eqs++; new_eq_or_diseq(true, v1, v2, j); } virtual void new_diseq_eh(th_var v1, th_var v2, justification& j) { m_stats.m_num_core2th_diseqs++; new_eq_or_diseq(false, v1, v2, j); } void negate(coeffs& coeffs, rational& weight); numeral mk_weight(bool is_real, bool is_strict, rational const& w) const; void mk_coeffs(vector >const& terms, coeffs& coeffs, rational& w); void del_atoms(unsigned old_size); bool propagate_atom(atom const& a); th_var mk_term(app* n); th_var mk_num(app* n, rational const& r); bool is_consistent() const; th_var expand(bool pos, th_var v, rational & k); void new_eq_or_diseq(bool is_eq, th_var v1, th_var v2, justification& eq_just); th_var get_zero(sort* s) const { return m_zero; } th_var get_zero(expr* e) const { return get_zero(get_manager().get_sort(e)); } void inc_conflicts(); edge_id add_ineq(vector > const& terms, numeral const& weight, literal l); bool enable_edge(edge_id id); th_var to_var(th_var v) const { return 2*v; } th_var from_var(th_var v) const { return v/2; } th_var pos(th_var v) const { return v & 0xFFFFFFFE; } th_var neg(th_var v) const { return v | 0x1; } }; typedef theory_utvpi theory_rutvpi; typedef theory_utvpi theory_iutvpi; }; #endif /* THEORY_UTVPI_H_ */ z3-z3-4.8.7/src/smt/theory_utvpi_def.h000066400000000000000000001001351356505360400175510ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: theory_utvpi_def.h Abstract: Implementation of UTVPI solver. Author: Nikolaj Bjorner (nbjorner) 2013-04-26 Revision History: 1. introduce x^+ and x^-, such that 2*x := x^+ - x^- 2. rewrite constraints as follows: x - y <= k => x^+ - y^+ <= k y^- - x^- <= k x <= k => x^+ - x^- <= 2k x + y <= k => x^+ - y^- <= k y^+ - x^- <= k - x - y <= k => x^- - y^+ <= k y^- - x^+ <= k 3. Solve for x^+ and x^- 4. Check parity condition for integers (see Lahiri and Musuvathi 05) This checks if x^+ and x^- are in the same component but of different parities. 5. Enforce parity on variables. This checks if x^+ and x^- have different parities. If they have different parities, the assignment to one of the variables is decremented (choose the variable that is not tightly constrained with 0). The process that adjusts parities converges: Suppose we break a parity of a different variable y while fixing x's parity. A cyclic breaking/fixing of parities implies there is a strongly connected component between x, y and the two polarities of the variables. This contradicts the test in 4. 6. extract model for M(x) := (M(x^+)- M(x^-))/2 --*/ #ifndef THEORY_UTVPI_DEF_H_ #define THEORY_UTVPI_DEF_H_ #include "smt/theory_utvpi.h" #include "util/heap.h" #include "ast/ast_pp.h" #include "smt/smt_context.h" namespace smt { template theory_utvpi::theory_utvpi(ast_manager& m): theory(m.mk_family_id("arith")), a(m), m_arith_eq_adapter(*this, m_params, a), m_zero(null_theory_var), m_nc_functor(*this), m_asserted_qhead(0), m_agility(0.5), m_lia(false), m_lra(false), m_non_utvpi_exprs(false), m_test(m), m_factory(nullptr) { } template theory_utvpi::~theory_utvpi() { reset_eh(); } template std::ostream& theory_utvpi::atom::display(theory_utvpi const& th, std::ostream& out) const { context& ctx = th.get_context(); lbool asgn = ctx.get_assignment(m_bvar); bool sign = (l_undef == l_false); return out << literal(m_bvar, sign) << " " << mk_pp(ctx.bool_var2expr(m_bvar), th.get_manager()) << " "; if (l_undef == asgn) { out << "unassigned\n"; } else { th.m_graph.display_edge(out, get_asserted_edge()); } return out; } template theory_var theory_utvpi::mk_var(enode* n) { th_var v = theory::mk_var(n); TRACE("utvpi", tout << v << " " << mk_pp(n->get_owner(), get_manager()) << "\n";); m_graph.init_var(to_var(v)); m_graph.init_var(neg(to_var(v))); get_context().attach_th_var(n, this, v); return v; } template theory_var theory_utvpi::mk_var(expr* n) { context & ctx = get_context(); enode* e = nullptr; th_var v = null_theory_var; m_lia |= a.is_int(n); m_lra |= a.is_real(n); if (!is_app(n)) { return v; } if (ctx.e_internalized(n)) { e = ctx.get_enode(n); v = e->get_th_var(get_id()); } else { ctx.internalize(n, false); e = ctx.get_enode(n); } if (v == null_theory_var) { v = mk_var(e); } if (is_interpreted(to_app(n))) { found_non_utvpi_expr(n); } return v; } template void theory_utvpi::reset_eh() { m_graph .reset(); m_zero = null_theory_var; m_atoms .reset(); m_asserted_atoms .reset(); m_stats .reset(); m_scopes .reset(); m_asserted_qhead = 0; m_agility = 0.5; m_lia = false; m_lra = false; m_non_utvpi_exprs = false; theory::reset_eh(); } template void theory_utvpi::new_eq_or_diseq(bool is_eq, th_var v1, th_var v2, justification& eq_just) { rational k; th_var s = expand(true, v1, k); th_var t = expand(false, v2, k); context& ctx = get_context(); ast_manager& m = get_manager(); if (s == t) { if (is_eq != k.is_zero()) { // conflict 0 /= k; inc_conflicts(); ctx.set_conflict(&eq_just); } } else { // // Create equality ast, internalize_atom // assign the corresponding equality literal. // app_ref eq(m), s2(m), t2(m); app* s1 = get_enode(s)->get_owner(); app* t1 = get_enode(t)->get_owner(); s2 = a.mk_sub(t1, s1); t2 = a.mk_numeral(k, m.get_sort(s2.get())); // t1 - s1 = k eq = m.mk_eq(s2.get(), t2.get()); TRACE("utvpi", tout << v1 << " .. " << v2 << "\n"; tout << mk_pp(eq.get(), m) <<"\n";); if (!internalize_atom(eq.get(), false)) { UNREACHABLE(); } literal l(ctx.get_literal(eq.get())); if (!is_eq) { l = ~l; } ctx.assign(l, b_justification(&eq_just), false); } } template void theory_utvpi::inc_conflicts() { m_stats.m_num_conflicts++; if (m_params.m_arith_adaptive) { double g = m_params.m_arith_adaptive_propagation_threshold; m_agility = m_agility*g + 1 - g; } } template void theory_utvpi::set_conflict() { inc_conflicts(); literal_vector const& lits = m_nc_functor.get_lits(); context & ctx = get_context(); IF_VERBOSE(2, verbose_stream() << "conflict:\n"; for (unsigned i = 0; i < lits.size(); ++i) { ast_manager& m = get_manager(); expr_ref e(m); ctx.literal2expr(lits[i], e); verbose_stream() << mk_pp(e, m) << "\n"; } verbose_stream() << "\n";); TRACE("utvpi", tout << "conflict: "; for (unsigned i = 0; i < lits.size(); ++i) { ctx.display_literal_info(tout, lits[i]); } tout << "\n"; ); if (m_params.m_arith_dump_lemmas) { symbol logic(m_lra ? (m_lia?"QF_LIRA":"QF_LRA") : "QF_LIA"); ctx.display_lemma_as_smt_problem(lits.size(), lits.c_ptr(), false_literal, logic); } vector params; if (get_manager().proofs_enabled()) { params.push_back(parameter(symbol("farkas"))); for (unsigned i = 0; i < m_nc_functor.get_coeffs().size(); ++i) { params.push_back(parameter(rational(m_nc_functor.get_coeffs()[i]))); } } ctx.set_conflict( ctx.mk_justification( ext_theory_conflict_justification( get_id(), ctx.get_region(), lits.size(), lits.c_ptr(), 0, nullptr, params.size(), params.c_ptr()))); m_nc_functor.reset(); } template void theory_utvpi::found_non_utvpi_expr(expr* n) { if (!m_non_utvpi_exprs) { std::stringstream msg; msg << "found non utvpi logic expression:\n" << mk_pp(n, get_manager()) << "\n"; TRACE("utvpi", tout << msg.str();); warning_msg("%s", msg.str().c_str()); get_context().push_trail(value_trail(m_non_utvpi_exprs)); m_non_utvpi_exprs = true; } } template void theory_utvpi::init(context* ctx) { theory::init(ctx); m_zero = mk_var(ctx->mk_enode(a.mk_numeral(rational(0), true), false, false, true)); } /** \brief Create negated literal. The negation of: E <= 0 -E + epsilon <= 0 or -E + 1 <= 0 */ template void theory_utvpi::negate(coeffs& coeffs, rational& weight) { for (unsigned i = 0; i < coeffs.size(); ++i) { coeffs[i].second.neg(); } weight.neg(); } template typename theory_utvpi::numeral theory_utvpi::mk_weight(bool is_real, bool is_strict, rational const& w) const { if (is_strict) { return numeral(w) + (is_real?Ext::m_epsilon:numeral(1)); } else { return numeral(w); } } template void theory_utvpi::mk_coeffs(vector > const& terms, coeffs& coeffs, rational& w) { coeffs.reset(); w = m_test.get_weight(); for (unsigned i = 0; i < terms.size(); ++i) { coeffs.push_back(std::make_pair(mk_var(terms[i].first), terms[i].second)); } } template void theory_utvpi::internalize_eq_eh(app * atom, bool_var v) { context & ctx = get_context(); app * lhs = to_app(atom->get_arg(0)); app * rhs = to_app(atom->get_arg(1)); if (a.is_numeral(rhs)) { std::swap(rhs, lhs); } if (!a.is_numeral(rhs)) { return; } if (a.is_add(lhs) || a.is_sub(lhs)) { // force axioms for (= (+ x y) k) // this is necessary because (+ x y) is not expressible as a utvpi term. m_arith_eq_adapter.mk_axioms(ctx.get_enode(lhs), ctx.get_enode(rhs)); } } template bool theory_utvpi::internalize_atom(app * n, bool) { context & ctx = get_context(); if (!a.is_le(n) && !a.is_ge(n) && !a.is_lt(n) && !a.is_gt(n)) { found_non_utvpi_expr(n); return false; } SASSERT(!ctx.b_internalized(n)); expr* e1 = n->get_arg(0), *e2 = n->get_arg(1); if (a.is_ge(n) || a.is_gt(n)) { std::swap(e1, e2); } bool is_strict = a.is_gt(n) || a.is_lt(n); bool cl = m_test.linearize(e1, e2); if (!cl) { found_non_utvpi_expr(n); return false; } rational w; coeffs coeffs; mk_coeffs(m_test.get_linearization(), coeffs, w); bool_var bv = ctx.mk_bool_var(n); ctx.set_var_theory(bv, get_id()); literal l(bv); numeral w1 = mk_weight(a.is_real(e1), is_strict, w); edge_id pos = add_ineq(coeffs, w1, l); negate(coeffs, w); numeral w2 = mk_weight(a.is_real(e1), !is_strict, w); edge_id neg = add_ineq(coeffs, w2, ~l); m_bool_var2atom.insert(bv, m_atoms.size()); m_atoms.push_back(atom(bv, pos, neg)); TRACE("utvpi", tout << mk_pp(n, get_manager()) << "\n"; m_graph.display_edge(tout << "pos: ", pos); m_graph.display_edge(tout << "neg: ", neg); ); return true; } template bool theory_utvpi::internalize_term(app * term) { bool result = null_theory_var != mk_term(term); CTRACE("utvpi", !result, tout << "Did not internalize " << mk_pp(term, get_manager()) << "\n";); return result; } template void theory_utvpi::assign_eh(bool_var v, bool is_true) { m_stats.m_num_assertions++; unsigned idx = m_bool_var2atom.find(v); SASSERT(get_context().get_assignment(v) != l_undef); SASSERT((get_context().get_assignment(v) == l_true) == is_true); m_atoms[idx].assign_eh(is_true); m_asserted_atoms.push_back(idx); } template void theory_utvpi::push_scope_eh() { theory::push_scope_eh(); m_graph.push(); m_scopes.push_back(scope()); scope & s = m_scopes.back(); s.m_atoms_lim = m_atoms.size(); s.m_asserted_atoms_lim = m_asserted_atoms.size(); s.m_asserted_qhead_old = m_asserted_qhead; } template void theory_utvpi::pop_scope_eh(unsigned num_scopes) { unsigned lvl = m_scopes.size(); SASSERT(num_scopes <= lvl); unsigned new_lvl = lvl - num_scopes; scope & s = m_scopes[new_lvl]; del_atoms(s.m_atoms_lim); m_asserted_atoms.shrink(s.m_asserted_atoms_lim); m_asserted_qhead = s.m_asserted_qhead_old; m_scopes.shrink(new_lvl); m_graph.pop(num_scopes); theory::pop_scope_eh(num_scopes); } template final_check_status theory_utvpi::final_check_eh() { SASSERT(is_consistent()); if (can_propagate()) { propagate(); return FC_CONTINUE; } else if (!check_z_consistency()) { return FC_CONTINUE; } else if (m_non_utvpi_exprs) { return FC_GIVEUP; } else { return FC_DONE; } } template bool theory_utvpi::check_z_consistency() { int_vector scc_id; m_graph.compute_zero_edge_scc(scc_id); unsigned sz = get_num_vars(); for (unsigned i = 0; i < sz; ++i) { enode* e = get_enode(i); if (!a.is_int(e->get_owner())) { continue; } th_var v1 = to_var(i); th_var v2 = neg(v1); rational r1 = m_graph.get_assignment(v1).get_rational(); rational r2 = m_graph.get_assignment(v2).get_rational(); SASSERT(r1.is_int()); SASSERT(r2.is_int()); if (r1.is_even() == r2.is_even()) { continue; } if (scc_id[v1] != scc_id[v2]) { continue; } if (scc_id[v1] == -1) { continue; } // they are in the same SCC and have different parities => contradiction. m_nc_functor.reset(); VERIFY(m_graph.find_shortest_zero_edge_path(v1, v2, UINT_MAX, m_nc_functor)); VERIFY(m_graph.find_shortest_zero_edge_path(v2, v1, UINT_MAX, m_nc_functor)); IF_VERBOSE(1, verbose_stream() << "parity conflict " << mk_pp(e->get_owner(), get_manager()) << "\n";); set_conflict(); return false; } return true; } template void theory_utvpi::display(std::ostream& out) const { for (unsigned i = 0; i < m_atoms.size(); ++i) { m_atoms[i].display(*this, out); out << "\n"; } m_graph.display(out); } template void theory_utvpi::collect_statistics(::statistics& st) const { st.update("utvpi conflicts", m_stats.m_num_conflicts); st.update("utvpi asserts", m_stats.m_num_assertions); st.update("core->utvpi eqs", m_stats.m_num_core2th_eqs); st.update("core->utvpi diseqs", m_stats.m_num_core2th_diseqs); m_arith_eq_adapter.collect_statistics(st); m_graph.collect_statistics(st); } template void theory_utvpi::del_atoms(unsigned old_size) { typename atoms::iterator begin = m_atoms.begin() + old_size; typename atoms::iterator it = m_atoms.end(); while (it != begin) { --it; m_bool_var2atom.erase(it->get_bool_var()); } m_atoms.shrink(old_size); } template void theory_utvpi::propagate() { bool consistent = true; while (consistent && can_propagate()) { unsigned idx = m_asserted_atoms[m_asserted_qhead]; m_asserted_qhead++; consistent = propagate_atom(m_atoms[idx]); } } template bool theory_utvpi::propagate_atom(atom const& a) { context& ctx = get_context(); TRACE("utvpi", a.display(*this, tout); tout << "\n";); if (ctx.inconsistent()) { return false; } int edge_id = a.get_asserted_edge(); if (!enable_edge(edge_id)) { m_graph.traverse_neg_cycle2(m_params.m_arith_stronger_lemmas, m_nc_functor); set_conflict(); return false; } return true; } template theory_var theory_utvpi::mk_term(app* n) { TRACE("utvpi", tout << mk_pp(n, get_manager()) << "\n";); context& ctx = get_context(); bool cl = m_test.linearize(n); if (!cl) { found_non_utvpi_expr(n); return null_theory_var; } coeffs coeffs; rational w; mk_coeffs(m_test.get_linearization(), coeffs, w); if (coeffs.empty()) { return mk_num(n, w); } if (coeffs.size() == 1 && coeffs[0].second.is_one()) { return coeffs[0].first; } if (coeffs.size() == 2) { // do not create an alias. return null_theory_var; } for (unsigned i = 0; i < n->get_num_args(); ++i) { mk_term(to_app(n->get_arg(i))); } th_var target = mk_var(ctx.mk_enode(n, false, false, true)); coeffs.push_back(std::make_pair(target, rational(-1))); VERIFY(enable_edge(add_ineq(coeffs, numeral(w), null_literal))); negate(coeffs, w); VERIFY(enable_edge(add_ineq(coeffs, numeral(w), null_literal))); return target; } template theory_var theory_utvpi::mk_num(app* n, rational const& r) { theory_var v = null_theory_var; context& ctx = get_context(); if (r.is_zero()) { v = m_zero; } else if (ctx.e_internalized(n)) { enode* e = ctx.get_enode(n); v = e->get_th_var(get_id()); SASSERT(v != null_theory_var); } else { v = mk_var(ctx.mk_enode(n, false, false, true)); // v = k: v <= k k <= v coeffs coeffs; coeffs.push_back(std::make_pair(v, rational(-1))); VERIFY(enable_edge(add_ineq(coeffs, numeral(r), null_literal))); coeffs.back().second.neg(); VERIFY(enable_edge(add_ineq(coeffs, numeral(-r), null_literal))); } return v; } template theory_var theory_utvpi::expand(bool pos, th_var v, rational & k) { context& ctx = get_context(); enode* e = get_enode(v); expr* x, *y; rational r; for (;;) { app* n = e->get_owner(); if (a.is_add(n, x, y)) { if (a.is_numeral(x, r)) { e = ctx.get_enode(y); } else if (a.is_numeral(y, r)) { e = ctx.get_enode(x); } v = e->get_th_var(get_id()); SASSERT(v != null_theory_var); if (v == null_theory_var) { break; } if (pos) { k += r; } else { k -= r; } } else { break; } } return v; } // m_graph(source, target, weight, ex); // target - source <= weight template edge_id theory_utvpi::add_ineq(vector > const& terms, numeral const& weight, literal l) { SASSERT(terms.size() <= 2); SASSERT(terms.size() < 1 || terms[0].second.is_one() || terms[0].second.is_minus_one()); SASSERT(terms.size() < 2 || terms[1].second.is_one() || terms[1].second.is_minus_one()); th_var v1 = null_theory_var, v2 = null_theory_var; bool pos1 = true, pos2 = true; if (!terms.empty()) { v1 = terms[0].first; pos1 = terms[0].second.is_one(); SASSERT(v1 != null_theory_var); SASSERT(pos1 || terms[0].second.is_minus_one()); } if (terms.size() >= 2) { v2 = terms[1].first; pos2 = terms[1].second.is_one(); SASSERT(v1 != null_theory_var); SASSERT(pos2 || terms[1].second.is_minus_one()); } // TRACE("utvpi", tout << (pos1?"$":"-$") << v1 << (pos2?" + $":" - $") << v2 << " + " << weight << " <= 0\n";); edge_id id = m_graph.get_num_edges(); th_var w1 = to_var(v1), w2 = to_var(v2); if (terms.size() == 1 && pos1) { m_graph.add_edge(neg(w1), pos(w1), -weight-weight, std::make_pair(l,2)); m_graph.add_edge(neg(w1), pos(w1), -weight-weight, std::make_pair(l,2)); } else if (terms.size() == 1 && !pos1) { m_graph.add_edge(pos(w1), neg(w1), -weight-weight, std::make_pair(l,2)); m_graph.add_edge(pos(w1), neg(w1), -weight-weight, std::make_pair(l,2)); } else if (pos1 && pos2) { m_graph.add_edge(neg(w2), pos(w1), -weight, std::make_pair(l,1)); m_graph.add_edge(neg(w1), pos(w2), -weight, std::make_pair(l,1)); } else if (pos1 && !pos2) { m_graph.add_edge(pos(w2), pos(w1), -weight, std::make_pair(l,1)); m_graph.add_edge(neg(w1), neg(w2), -weight, std::make_pair(l,1)); } else if (!pos1 && pos2) { m_graph.add_edge(neg(w2), neg(w1), -weight, std::make_pair(l,1)); m_graph.add_edge(pos(w1), pos(w2), -weight, std::make_pair(l,1)); } else { m_graph.add_edge(pos(w1), neg(w2), -weight, std::make_pair(l,1)); m_graph.add_edge(pos(w2), neg(w1), -weight, std::make_pair(l,1)); } return id; } template bool theory_utvpi::enable_edge(edge_id id) { return (id == null_edge_id) || (m_graph.enable_edge(id) && m_graph.enable_edge(id+1)); } template bool theory_utvpi::is_consistent() const { return m_graph.is_feasible(); } template bool theory_utvpi::is_parity_ok(unsigned i) const { th_var v1 = to_var(i); th_var v2 = neg(v1); rational r1 = m_graph.get_assignment(v1).get_rational(); rational r2 = m_graph.get_assignment(v2).get_rational(); return r1.is_even() == r2.is_even(); } /** \brief adjust values for variables in the difference graph such that for variables of integer sort it is the case that x^+ - x^- is even. The informal justification for the procedure enforce_parity relies on a set of properties: 1. the graph does not contain a strongly connected component where x^+ and x+- are connected. They can be independently changed. This is checked prior to enforce_parity. 2. When x^+ - x^- is odd, the values are adjusted by first decrementing the value of x^+, provided x^- is not 0-dependent. Otherwise decrement x^-. x^- is "0-dependent" if there is a set of tight inequalities from x^+ to x^-. 3. The affinity to x^+ (the same component of x^+) ensures that the parity is broken only a finite number of times when traversing that component. Namely, suppose that the parity of y gets broken when fixing 'x'. Then first note that 'y' cannot be equal to 'x'. If it were, then we have a state where: parity(x^+) != parity(x^-) and parity(y^+) == parity(y^-) but x^+ and y^+ are tightly connected and x^- and y^- are also tightly connected using two copies of the same inequalities. This is a contradiction. Thus, 'y' cannot be equal to 'x' if 'y's parity gets broken when repairing 'x'. */ template void theory_utvpi::enforce_parity() { unsigned_vector todo; unsigned sz = get_num_vars(); for (unsigned i = 0; i < sz; ++i) { enode* e = get_enode(i); if (a.is_int(e->get_owner()) && !is_parity_ok(i)) { todo.push_back(i); } } if (todo.empty()) { return; } while (!todo.empty()) { unsigned i = todo.back(); todo.pop_back(); if (is_parity_ok(i)) { continue; } th_var v1 = to_var(i); th_var v2 = neg(v1); int_vector zero_v; m_graph.compute_zero_succ(v1, zero_v); for (unsigned j = 0; j < zero_v.size(); ++j) { if (zero_v[j] == v2) { zero_v.reset(); m_graph.compute_zero_succ(v2, zero_v); break; } } TRACE("utvpi", tout << "Disparity: " << v1 << "\n"; for (unsigned j = 0; j < zero_v.size(); ++j) { tout << "decrement: " << zero_v[j] << "\n"; }); for (unsigned j = 0; j < zero_v.size(); ++j) { int v = zero_v[j]; m_graph.inc_assignment(v, numeral(-1)); th_var k = from_var(v); if (!is_parity_ok(k)) { todo.push_back(k); } } } SASSERT(m_graph.is_feasible()); DEBUG_CODE( for (unsigned i = 0; i < sz; ++i) { enode* e = get_enode(i); if (a.is_int(e->get_owner()) && !is_parity_ok(i)) { IF_VERBOSE(0, verbose_stream() << "disparities not fixed\n";); UNREACHABLE(); } }); } // models: template void theory_utvpi::init_model(model_generator & m) { m_factory = alloc(arith_factory, get_manager()); m.register_factory(m_factory); enforce_parity(); m_graph.set_to_zero(to_var(m_zero), neg(to_var(m_zero))); compute_delta(); DEBUG_CODE(model_validate();); } template void theory_utvpi::model_validate() { context& ctx = get_context(); unsigned sz = m_atoms.size(); for (unsigned i = 0; i < sz; ++i) { bool_var b = m_atoms[i].get_bool_var(); if (!ctx.is_relevant(b)) { continue; } bool ok = true; expr* e = ctx.bool_var2expr(b); lbool assign = ctx.get_assignment(b); switch(assign) { case l_true: ok = eval(e); break; case l_false: ok = !eval(e); break; default: break; } CTRACE("utvpi", !ok, tout << "validation failed:\n"; tout << "Assignment: " << assign << "\n"; m_atoms[i].display(*this, tout); tout << "\n"; display(tout); m_graph.display_agl(tout); ); if (!ok) { std::cout << "validation failed:\n"; std::cout << "Assignment: " << assign << "\n"; m_atoms[i].display(*this, std::cout); std::cout << "\n"; display(std::cout); m_graph.display_agl(std::cout); } // CTRACE("utvpi", ok, tout << "validation success: " << mk_pp(e, get_manager()) << "\n";); SASSERT(ok); } } template bool theory_utvpi::eval(expr* e) { expr* e1, *e2; if (a.is_le(e, e1, e2) || a.is_ge(e, e2, e1)) { return eval_num(e1) <= eval_num(e2); } if (a.is_lt(e, e1, e2) || a.is_gt(e, e2, e1)) { return eval_num(e1) < eval_num(e2); } if (get_manager().is_eq(e, e1, e2)) { return eval_num(e1) == eval_num(e2); } TRACE("utvpi", tout << "expression not handled: " << mk_pp(e, get_manager()) << "\n";); return false; } template rational theory_utvpi::eval_num(expr* e) { rational r; expr* e1, *e2; if (a.is_numeral(e, r)) { return r; } if (a.is_sub(e, e1, e2)) { return eval_num(e1) - eval_num(e2); } if (a.is_add(e)) { r.reset(); for (unsigned i = 0; i < to_app(e)->get_num_args(); ++i) { r += eval_num(to_app(e)->get_arg(i)); } return r; } if (a.is_mul(e)) { r = rational(1); for (unsigned i = 0; i < to_app(e)->get_num_args(); ++i) { r *= eval_num(to_app(e)->get_arg(i)); } return r; } if (a.is_uminus(e, e1)) { return -eval_num(e1); } if (a.is_to_real(e, e1)) { return eval_num(e1); } if (is_uninterp_const(e)) { return mk_value(mk_var(e), a.is_int(e)); } TRACE("utvpi", tout << "expression not handled: " << mk_pp(e, get_manager()) << "\n";); UNREACHABLE(); return rational(0); } template rational theory_utvpi::mk_value(th_var v, bool is_int) { SASSERT(v != null_theory_var); numeral val1 = m_graph.get_assignment(to_var(v)); numeral val2 = m_graph.get_assignment(neg(to_var(v))); numeral val = val1 - val2; rational num = val.get_rational() + (m_delta * val.get_infinitesimal().to_rational()); num = num/rational(2); SASSERT(!is_int || num.is_int()); TRACE("utvpi", expr* n = get_enode(v)->get_owner(); tout << mk_pp(n, get_manager()) << " |-> (" << val1 << " - " << val2 << ")/2 = " << num << "\n";); return num; } template model_value_proc * theory_utvpi::mk_value(enode * n, model_generator & mg) { theory_var v = n->get_th_var(get_id()); bool is_int = a.is_int(n->get_owner()); rational num = mk_value(v, is_int); TRACE("utvpi", tout << mk_pp(n->get_owner(), get_manager()) << " |-> " << num << "\n";); return alloc(expr_wrapper_proc, m_factory->mk_num_value(num, is_int)); } /** \brief Compute numeral values for the infinitesimals to satisfy the inequalities. */ template void theory_utvpi::compute_delta() { m_delta = rational(1); unsigned sz = m_graph.get_num_edges(); for (unsigned i = 0; i < sz; ++i) { if (!m_graph.is_enabled(i)) { continue; } numeral w = m_graph.get_weight(i); numeral tgt = m_graph.get_assignment(m_graph.get_target(i)); numeral src = m_graph.get_assignment(m_graph.get_source(i)); numeral b = tgt - src - w; SASSERT(b.is_nonpos()); rational eps_r = b.get_infinitesimal(); // Given: b <= 0 // suppose that 0 < b.eps // then we have 0 > b.num // then delta must ensure: // 0 >= b.num + delta*b.eps // <=> // -b.num/b.eps >= delta if (eps_r.is_pos()) { rational num_r = -b.get_rational(); SASSERT(num_r.is_pos()); rational new_delta = num_r/eps_r; if (new_delta < m_delta) { m_delta = new_delta; } } } } template theory* theory_utvpi::mk_fresh(context* new_ctx) { return alloc(theory_utvpi, new_ctx->get_manager()); } }; #endif z3-z3-4.8.7/src/smt/theory_wmaxsat.cpp000066400000000000000000000302401356505360400176020ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: theory_wmaxsat.h Abstract: Weighted Max-SAT theory plugin. Author: Nikolaj Bjorner (nbjorner) 2013-11-05 Notes: --*/ #include #include "smt/smt_context.h" #include "ast/ast_pp.h" #include "smt/theory_wmaxsat.h" #include "smt/smt_justification.h" namespace smt { theory_wmaxsat::theory_wmaxsat(ast_manager& m, generic_model_converter& mc): theory(m.mk_family_id("weighted_maxsat")), m_mc(mc), m_vars(m), m_fmls(m), m_zweights(m_mpz), m_old_values(m_mpz), m_zcost(m_mpz), m_zmin_cost(m_mpz), m_found_optimal(false), m_propagate(false), m_normalize(false), m_den(1) {} theory_wmaxsat::~theory_wmaxsat() { m_old_values.reset(); } /** \brief return the complement of variables that are currently assigned. */ void theory_wmaxsat::get_assignment(svector& result) { result.reset(); if (!m_found_optimal) { for (unsigned i = 0; i < m_vars.size(); ++i) { result.push_back(false); } } else { std::sort(m_cost_save.begin(), m_cost_save.end()); for (unsigned i = 0,j = 0; i < m_vars.size(); ++i) { if (j < m_cost_save.size() && m_cost_save[j] == static_cast(i)) { result.push_back(false); ++j; } else { result.push_back(true); } } } TRACE("opt", tout << "cost save: "; for (unsigned i = 0; i < m_cost_save.size(); ++i) { tout << m_cost_save[i] << " "; } tout << "\nvars: "; for (unsigned i = 0; i < m_vars.size(); ++i) { tout << mk_pp(m_vars[i].get(), get_manager()) << " "; } tout << "\nassignment: "; for (unsigned i = 0; i < result.size(); ++i) { tout << result[i] << " "; } tout << "\n";); } void theory_wmaxsat::init_search_eh() { m_propagate = true; } expr* theory_wmaxsat::assert_weighted(expr* fml, rational const& w) { context & ctx = get_context(); ast_manager& m = get_manager(); app_ref var(m), wfml(m); var = m.mk_fresh_const("w", m.mk_bool_sort()); m_mc.hide(var); wfml = m.mk_or(var, fml); ctx.assert_expr(wfml); m_rweights.push_back(w); m_vars.push_back(var); m_fmls.push_back(fml); m_assigned.push_back(false); m_enabled.push_back(true); m_normalize = true; bool_var bv = register_var(var, true); (void)bv; TRACE("opt", tout << "inc: " << ctx.inconsistent() << " enable: v" << m_bool2var[bv] << " b" << bv << " " << mk_pp(var, get_manager()) << "\n" << wfml << "\n";); return var; } void theory_wmaxsat::disable_var(expr* var) { context& ctx = get_context(); SASSERT(ctx.b_internalized(var)); bool_var bv = ctx.get_bool_var(var); theory_var tv = m_bool2var[bv]; m_enabled[tv] = false; TRACE("opt", tout << "disable: v" << tv << " b" << bv << " " << mk_pp(var, get_manager()) << "\n";); } bool_var theory_wmaxsat::register_var(app* var, bool attach) { context & ctx = get_context(); bool_var bv; SASSERT(!ctx.e_internalized(var)); enode* x = ctx.mk_enode(var, false, true, true); if (ctx.b_internalized(var)) { bv = ctx.get_bool_var(var); } else { bv = ctx.mk_bool_var(var); } ctx.set_enode_flag(bv, true); if (attach) { ctx.set_var_theory(bv, get_id()); theory_var v = mk_var(x); ctx.attach_th_var(x, this, v); m_bool2var.insert(bv, v); 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; } rational theory_wmaxsat::get_cost() { unsynch_mpq_manager mgr; scoped_mpq q(mgr); mgr.set(q, m_zcost, m_den.to_mpq().numerator()); return rational(q); } void theory_wmaxsat::init_min_cost(rational const& r) { m_rmin_cost = r; m_zmin_cost = (m_rmin_cost * m_den).to_mpq().numerator(); } void theory_wmaxsat::assign_eh(bool_var v, bool is_true) { if (is_true) { if (m_normalize) normalize(); context& ctx = get_context(); theory_var tv = m_bool2var[v]; if (m_assigned[tv] || !m_enabled[tv]) return; scoped_mpz w(m_mpz); w = m_zweights[tv]; ctx.push_trail(numeral_trail(m_zcost, m_old_values)); ctx.push_trail(push_back_vector >(m_costs)); ctx.push_trail(value_trail(m_assigned[tv])); m_zcost += w; TRACE("opt", tout << "Assign v" << tv << " weight: " << w << " cost: " << m_zcost << " " << mk_pp(m_vars[m_bool2var[v]].get(), get_manager()) << "\n";); m_costs.push_back(tv); m_assigned[tv] = true; if (m_zcost >= m_zmin_cost) { block(); } else { m_can_propagate = true; } } } final_check_status theory_wmaxsat::final_check_eh() { if (m_normalize) normalize(); TRACE("opt", tout << "cost: " << m_zcost << " min cost: " << m_zmin_cost << "\n";); return FC_DONE; } void theory_wmaxsat::reset_eh() { theory::reset_eh(); reset_local(); } void theory_wmaxsat::reset_local() { m_vars.reset(); m_fmls.reset(); m_rweights.reset(); m_rmin_cost.reset(); m_zweights.reset(); m_zcost.reset(); m_zmin_cost.reset(); m_cost_save.reset(); m_bool2var.reset(); m_var2bool.reset(); m_propagate = false; m_can_propagate = false; m_found_optimal = false; m_assigned.reset(); m_enabled.reset(); } void theory_wmaxsat::propagate() { context& ctx = get_context(); for (unsigned i = 0; m_propagate && i < m_vars.size(); ++i) { bool_var bv = m_var2bool[i]; lbool asgn = ctx.get_assignment(bv); if (asgn == l_true) { assign_eh(bv, true); } } m_propagate = false; //while (m_found_optimal && max_unassigned_is_blocked() && !ctx.inconsistent()) { } m_can_propagate = false; } bool theory_wmaxsat::is_optimal() const { return !m_found_optimal || m_zcost < m_zmin_cost; } expr_ref theory_wmaxsat::mk_block() { ++m_stats.m_num_blocks; ast_manager& m = get_manager(); expr_ref_vector disj(m); compare_cost compare_cost(*this); svector costs(m_costs); std::sort(costs.begin(), costs.end(), compare_cost); scoped_mpz weight(m_mpz); m_mpz.reset(weight); for (unsigned i = 0; i < costs.size() && m_mpz.lt(weight, m_zmin_cost); ++i) { theory_var tv = costs[i]; if (m_enabled[tv]) { weight += m_zweights[tv]; disj.push_back(m.mk_not(m_vars[tv].get())); } } if (is_optimal()) { m_found_optimal = true; m_cost_save.reset(); m_cost_save.append(m_costs); TRACE("opt", tout << "costs: "; for (unsigned i = 0; i < m_costs.size(); ++i) { tout << mk_pp(get_enode(m_costs[i])->get_owner(), get_manager()) << " "; } tout << "\n"; //get_context().display(tout); ); } expr_ref result(m.mk_or(disj.size(), disj.c_ptr()), m); TRACE("opt", tout << result << " weight: " << weight << "\n"; tout << "cost: " << m_zcost << " min-cost: " << m_zmin_cost << "\n";); return result; } void theory_wmaxsat::restart_eh() {} void theory_wmaxsat::block() { if (m_vars.empty()) { return; } ++m_stats.m_num_blocks; context& ctx = get_context(); literal_vector lits; compare_cost compare_cost(*this); svector costs(m_costs); std::sort(costs.begin(), costs.end(), compare_cost); scoped_mpz weight(m_mpz); m_mpz.reset(weight); for (unsigned i = 0; i < costs.size() && weight < m_zmin_cost; ++i) { weight += m_zweights[costs[i]]; lits.push_back(literal(m_var2bool[costs[i]])); } TRACE("opt", ctx.display_literals_verbose(tout, lits); tout << "\n";); ctx.set_conflict( ctx.mk_justification( ext_theory_conflict_justification(get_id(), ctx.get_region(), lits.size(), lits.c_ptr(), 0, nullptr, 0, nullptr))); } bool theory_wmaxsat::max_unassigned_is_blocked() { context& ctx = get_context(); unsigned max_unassigned = m_max_unassigned_index; if (max_unassigned < m_sorted_vars.size() && m_zcost + m_zweights[m_sorted_vars[max_unassigned]] < m_zmin_cost) { return false; } // update value of max-unassigned while (max_unassigned < m_sorted_vars.size() && ctx.get_assignment(m_var2bool[m_sorted_vars[max_unassigned]]) != l_undef) { ++max_unassigned; } // if (max_unassigned > m_max_unassigned_index) { ctx.push_trail(value_trail(m_max_unassigned_index)); m_max_unassigned_index = max_unassigned; } if (max_unassigned < m_sorted_vars.size() && m_zcost + m_zweights[m_sorted_vars[max_unassigned]] >= m_zmin_cost) { theory_var tv = m_sorted_vars[max_unassigned]; propagate(m_var2bool[tv]); m_max_unassigned_index++; return true; } return false; } void theory_wmaxsat::propagate(bool_var v) { ++m_stats.m_num_propagations; context& ctx = get_context(); literal_vector lits; literal lit(v, true); SASSERT(ctx.get_assignment(lit) == l_undef); for (unsigned i = 0; i < m_costs.size(); ++i) { bool_var w = m_var2bool[m_costs[i]]; lits.push_back(literal(w)); } TRACE("opt", ctx.display_literals_verbose(tout, lits.size(), lits.c_ptr()); ctx.display_literal_verbose(tout << " --> ", lit);); region& r = ctx.get_region(); ctx.assign(lit, ctx.mk_justification( ext_theory_propagation_justification( get_id(), r, lits.size(), lits.c_ptr(), 0, nullptr, lit, 0, nullptr))); } void theory_wmaxsat::normalize() { m_den = rational::one(); for (unsigned i = 0; i < m_rweights.size(); ++i) { if (m_enabled[i]) { m_den = lcm(m_den, denominator(m_rweights[i])); } } m_den = lcm(m_den, denominator(m_rmin_cost)); SASSERT(!m_den.is_zero()); m_zweights.reset(); m_sorted_vars.reset(); for (unsigned i = 0; i < m_rweights.size(); ++i) { rational r = m_rweights[i]*m_den; SASSERT(r.is_int()); mpq const& q = r.to_mpq(); m_zweights.push_back(q.numerator()); m_sorted_vars.push_back(i); } compare_cost compare_cost(*this); std::sort(m_sorted_vars.begin(), m_sorted_vars.end(), compare_cost); m_max_unassigned_index = 0; m_zcost.reset(); rational r = m_rmin_cost * m_den; m_zmin_cost = r.to_mpq().numerator(); m_normalize = false; } }; z3-z3-4.8.7/src/smt/theory_wmaxsat.h000066400000000000000000000115621356505360400172550ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: theory_wmaxsat.h Abstract: Weighted Max-SAT theory plugin. Author: Nikolaj Bjorner (nbjorner) 2013-11-05 Notes: --*/ #ifndef THEORY_WMAXSAT_H_ #define THEORY_WMAXSAT_H_ #include "smt/smt_theory.h" #include "smt/smt_clause.h" #include "tactic/generic_model_converter.h" namespace smt { class theory_wmaxsat : public theory { struct stats { unsigned m_num_blocks; unsigned m_num_propagations; void reset() { memset(this, 0, sizeof(*this)); } stats() { reset(); } }; generic_model_converter& m_mc; mutable unsynch_mpz_manager m_mpz; app_ref_vector m_vars; // Auxiliary variables per soft clause expr_ref_vector m_fmls; // Formulas per soft clause vector m_rweights; // weights of theory variables. scoped_mpz_vector m_zweights; scoped_mpz_vector m_old_values; svector m_costs; // set of asserted theory variables unsigned m_max_unassigned_index; // index of literal that is not yet assigned and has maximal weight. svector m_sorted_vars; // set of all theory variables, sorted by cost svector m_cost_save; // set of asserted theory variables rational m_rmin_cost; // current maximal cost assignment. scoped_mpz m_zcost; // current sum of asserted costs scoped_mpz m_zmin_cost; // current maximal cost assignment. bool m_found_optimal; u_map m_bool2var; // bool_var -> theory_var svector m_var2bool; // theory_var -> bool_var bool m_propagate; bool m_can_propagate; bool m_normalize; rational m_den; // lcm of denominators for rational weights. svector m_assigned, m_enabled; stats m_stats; public: theory_wmaxsat(ast_manager& m, generic_model_converter& mc); ~theory_wmaxsat() override; void get_assignment(svector& result); expr* assert_weighted(expr* fml, rational const& w); void disable_var(expr* var); bool_var register_var(app* var, bool attach); rational get_cost(); void init_min_cost(rational const& r); class numeral_trail : public trail { typedef scoped_mpz T; T & m_value; scoped_mpz_vector& m_old_values; public: numeral_trail(T & value, scoped_mpz_vector& old): m_value(value), m_old_values(old) { old.push_back(value); } ~numeral_trail() override { } void undo(context & ctx) override { m_value = m_old_values.back(); m_old_values.shrink(m_old_values.size() - 1); } }; void init_search_eh() override; void assign_eh(bool_var v, bool is_true) override; final_check_status final_check_eh() override; bool use_diseqs() const override { return false; } bool build_models() const override { return false; } void reset_local(); void reset_eh() override; theory * mk_fresh(context * new_ctx) override { return nullptr; } bool internalize_atom(app * atom, bool gate_ctx) override { return false; } bool internalize_term(app * term) override { return false; } void new_eq_eh(theory_var v1, theory_var v2) override { } void new_diseq_eh(theory_var v1, theory_var v2) override { } void display(std::ostream& out) const override {} void restart_eh() override; void collect_statistics(::statistics & st) const override { st.update("wmaxsat num blocks", m_stats.m_num_blocks); st.update("wmaxsat num props", m_stats.m_num_propagations); } bool can_propagate() override { return m_propagate || m_can_propagate; } void propagate() override; bool is_optimal() const; expr_ref mk_block(); private: void block(); void propagate(bool_var v); void normalize(); bool max_unassigned_is_blocked(); class compare_cost { theory_wmaxsat& m_th; public: compare_cost(theory_wmaxsat& t):m_th(t) {} bool operator() (theory_var v, theory_var w) const { return m_th.m_mpz.gt(m_th.m_zweights[v], m_th.m_zweights[w]); } }; }; }; #endif z3-z3-4.8.7/src/smt/uses_theory.cpp000066400000000000000000000016221356505360400170770ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: uses_theory.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-10-21. Revision History: --*/ #include "smt/uses_theory.h" #include "ast/for_each_expr.h" bool uses_theory(expr * n, family_id fid) { expr_mark visited; return uses_theory(n, fid, visited); } namespace uses_theory_ns { struct found {}; struct proc { family_id m_fid; proc(family_id fid):m_fid(fid) {} void operator()(var * n) {} void operator()(app * n) { if (n->get_family_id() == m_fid) throw found(); } void operator()(quantifier * n) {} }; }; bool uses_theory(expr * n, family_id fid, expr_mark & visited) { uses_theory_ns::proc p(fid); try { for_each_expr(p, visited, n); } catch (const uses_theory_ns::found &) { return true; } return false; } z3-z3-4.8.7/src/smt/uses_theory.h000066400000000000000000000013001356505360400165350ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: uses_theory.h Abstract: Author: Leonardo de Moura (leonardo) 2008-10-21. Revision History: --*/ #ifndef USES_THEORY_H_ #define USES_THEORY_H_ #include "ast/ast.h" /** \brief Return true if the given expression contains a symbol of the given theory. */ bool uses_theory(expr * n, family_id fid); /** \brief Return true if the given expression contains a symbol of the given theory. Only the expressions not marked as visited are checked. The set visited is updated with the new checked expressions. */ bool uses_theory(expr * n, family_id fid, expr_mark & visited); #endif /* USES_THEORY_H_ */ z3-z3-4.8.7/src/smt/watch_list.cpp000066400000000000000000000071331356505360400166720ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: watch_list.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-05-23. Revision History: --*/ #include "smt/watch_list.h" namespace smt { #define DEFAULT_WATCH_LIST_SIZE (sizeof(clause *) * 4) #if defined(__LP64__) || defined(_WIN64) // make sure data is aligned in 64 bit machines #define HEADER_SIZE (4 * sizeof(unsigned)) #else #define HEADER_SIZE (3 * sizeof(unsigned)) #endif void watch_list::destroy() { if (m_data) { dealloc_svect(reinterpret_cast(m_data) - HEADER_SIZE); } } void watch_list::expand() { if (m_data == nullptr) { unsigned size = DEFAULT_WATCH_LIST_SIZE + HEADER_SIZE; unsigned * mem = reinterpret_cast(alloc_svect(char, size)); #if defined(__LP64__) || defined(_WIN64) ++mem; // make sure data is aligned in 64 bit machines #endif *mem = 0; ++mem; *mem = DEFAULT_WATCH_LIST_SIZE; ++mem; *mem = DEFAULT_WATCH_LIST_SIZE; ++mem; m_data = reinterpret_cast(mem); SASSERT( begin_lits_core() % sizeof(literal) == 0 ); } else { unsigned curr_begin_bin = begin_lits_core(); unsigned curr_capacity = end_lits_core(); unsigned bin_bytes = curr_capacity - curr_begin_bin; /* dvitek: Added +3&~3U to fix alignment issues on * sparc64/solaris. ("literal"s must be 4-byte aligned). Should * also help performance elsewhere. */ 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(); #if defined(__LP64__) || defined(_WIN64) ++mem; // make sure data is aligned in 64 bit machines #endif *mem = curr_end_cls; ++mem; SASSERT(bin_bytes <= new_capacity); unsigned new_begin_bin = new_capacity - bin_bytes; *mem = new_begin_bin; ++mem; *mem = new_capacity; ++mem; memcpy(mem, m_data, curr_end_cls); memcpy(reinterpret_cast(mem) + new_begin_bin, m_data + curr_begin_bin, bin_bytes); destroy(); m_data = reinterpret_cast(mem); SASSERT( begin_lits_core() % sizeof(literal) == 0 ); } } void watch_list::remove_clause(clause * c) { clause_iterator begin = begin_clause(); clause_iterator end = end_clause(); clause_iterator it = std::find(begin, end, c); if (it == end) { return; } clause_iterator prev = it; ++it; for(; it != end; ++it, ++prev) { *prev = *it; } end_cls_core() -= sizeof(clause *); } void watch_list::remove_literal(literal l) { literal * begin = begin_literals(); literal * end = end_literals(); literal * it = std::find(begin, end, l); if (it == end) { return; } literal * prev = it; while (it != begin) { SASSERT(it == prev); --it; *prev = *it; --prev; } SASSERT(prev == begin); begin_lits_core() += sizeof(literal); } }; z3-z3-4.8.7/src/smt/watch_list.h000066400000000000000000000125451356505360400163420ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: watch_list.h Abstract: Author: Leonardo de Moura (leonardo) 2008-05-23. Revision History: --*/ #ifndef WATCH_LIST_H_ #define WATCH_LIST_H_ #include "smt/smt_clause.h" #include "util/memory_manager.h" namespace smt { /** \brief List of clauses and literals watching a given literal. ------------------------------------------------------------------------------------------- | end_nbegin | begin_lits | end | regular clauses | -> <- | literals | ------------------------------------------------------------------------------------------- ^ ^ ^ ^ | | | | m_data end_cls begin_lits end_lits When this class is used to implement unit propagation, a literal l1 in m_watch_list[l2] represents the binary clause (or l1 (not l2)) */ class watch_list { char * m_data; void expand(); unsigned & end_cls_core() { SASSERT(m_data); return reinterpret_cast(m_data)[-3]; } unsigned end_cls() { return m_data ? end_cls_core() : 0; } unsigned & begin_lits_core() { SASSERT(m_data); return reinterpret_cast(m_data)[-2]; } unsigned begin_lits_core() const { SASSERT(m_data); return reinterpret_cast(m_data)[-2]; } unsigned begin_lits() const { return m_data ? begin_lits_core() : 0; } unsigned & end_lits_core() { SASSERT(m_data); return reinterpret_cast(m_data)[-1]; } unsigned end_lits_core() const { SASSERT(m_data); return reinterpret_cast(m_data)[-1]; } unsigned end_lits() const { return m_data ? end_lits_core() : 0; } void destroy(); public: watch_list(): m_data(nullptr) { } watch_list(watch_list && other) : m_data(nullptr) { std::swap(m_data, other.m_data); } ~watch_list() { destroy(); } unsigned size() const { if (m_data) { return reinterpret_cast(m_data)[-3] + reinterpret_cast(m_data)[-1] - reinterpret_cast(m_data)[-2]; } return 0; } typedef clause ** clause_iterator; void reset() { if (m_data) { end_cls_core() = 0; begin_lits_core() = end_lits_core(); } } void reset_and_release_memory() { destroy(); m_data = nullptr; } clause_iterator begin_clause() { return reinterpret_cast(m_data); } clause_iterator end_clause() { return reinterpret_cast(m_data + end_cls()); } clause_iterator find_clause(clause const * c) { return std::find(begin_clause(), end_clause(), c); } literal * begin_literals() { return reinterpret_cast(m_data + begin_lits()); } literal * end_literals() { return reinterpret_cast(m_data + end_lits()); } literal const * begin_literals() const { return reinterpret_cast(m_data + begin_lits()); } literal const * end_literals() const { return reinterpret_cast(m_data + end_lits()); } literal * find_literal(literal const & l) { return std::find(begin_literals(), end_literals(), l); } literal const * find_literal(literal const & l) const { return std::find(begin_literals(), end_literals(), l); } void insert_clause(clause * c) { if (m_data == nullptr || end_cls_core() + sizeof(clause *) >= begin_lits_core()) { expand(); } *(reinterpret_cast(m_data + end_cls_core())) = c; end_cls_core() += sizeof(clause *); } void insert_literal(literal const & l) { if (m_data == nullptr || begin_lits_core() <= end_cls_core() + sizeof(literal)) { expand(); } SASSERT(begin_lits_core() >= sizeof(literal)); begin_lits_core() -= sizeof(literal); *(reinterpret_cast(m_data + begin_lits_core())) = l; } void remove_clause(clause * c); void remove_literal(literal l); void set_end_clause(clause_iterator new_end) { SASSERT(new_end <= end_clause()); if (m_data) { end_cls_core() = static_cast(reinterpret_cast(new_end) - m_data); } } }; }; #endif /* WATCH_LIST_H_ */ z3-z3-4.8.7/src/solver/000077500000000000000000000000001356505360400145305ustar00rootroot00000000000000z3-z3-4.8.7/src/solver/CMakeLists.txt000066400000000000000000000006041356505360400172700ustar00rootroot00000000000000z3_add_component(solver SOURCES check_sat_result.cpp combined_solver.cpp mus.cpp parallel_tactic.cpp smt_logics.cpp solver.cpp solver_na2as.cpp solver_pool.cpp solver2tactic.cpp tactic2solver.cpp COMPONENT_DEPENDENCIES model tactic PYG_FILES combined_solver_params.pyg parallel_params.pyg PYG_FILES solver_params.pyg ) z3-z3-4.8.7/src/solver/check_sat_result.cpp000066400000000000000000000031151356505360400205560ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: check_sat_result.cpp Abstract: Abstract interface for storing the result produced by a check_sat like command Author: Leonardo (leonardo) 2012-11-01 Notes: --*/ #include "solver/check_sat_result.h" void check_sat_result::set_reason_unknown(event_handler& eh) { switch (eh.caller_id()) { case UNSET_EH_CALLER: break; case CTRL_C_EH_CALLER: set_reason_unknown("interrupted from keyboard"); break; case TIMEOUT_EH_CALLER: set_reason_unknown("timeout"); break; case RESLIMIT_EH_CALLER: set_reason_unknown("max. resource limit exceeded"); break; case API_INTERRUPT_EH_CALLER: set_reason_unknown("interrupted"); break; } } simple_check_sat_result::simple_check_sat_result(ast_manager & m): m_core(m), m_proof(m) { } simple_check_sat_result::~simple_check_sat_result() { } void simple_check_sat_result::collect_statistics(statistics & st) const { st.copy(m_stats); } void simple_check_sat_result::get_unsat_core(expr_ref_vector & r) { if (m_status == l_false) { r.reset(); r.append(m_core.size(), m_core.c_ptr()); } } void simple_check_sat_result::get_model_core(model_ref & m) { if (m_status != l_false) m = m_model; else m = nullptr; } proof * simple_check_sat_result::get_proof() { return m_proof; } std::string simple_check_sat_result::reason_unknown() const { return m_unknown; } void simple_check_sat_result::get_labels(svector & r) { } z3-z3-4.8.7/src/solver/check_sat_result.h000066400000000000000000000057011356505360400202260ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: check_sat_result.h Abstract: Abstract interface for storing the result produced by a check_sat like command Author: Leonardo (leonardo) 2012-01-23 Notes: --*/ #ifndef CHECK_SAT_RESULT_H_ #define CHECK_SAT_RESULT_H_ #include "model/model.h" #include "util/lbool.h" #include "util/statistics.h" #include "util/event_handler.h" #include "tactic/model_converter.h" /** \brief Abstract interface for the result of a (check-sat) like command. It encapsulates information such as: - the actual result: l_true (satisfiable), l_false (unsatisfiable), l_undef (unknown) - statistics - model (if the result is satisfiable) - proof (if the result is unsatisfiable) - unsat-core (if the result is unsatisfiable) - reason-unknown (if the result is unknown, i.e., the solver failed to solve the problem) - label (if the result is satisfiable) this is legacy for Boogie */ class check_sat_result { protected: unsigned m_ref_count; lbool m_status; model_converter_ref m_mc0; public: check_sat_result():m_ref_count(0), m_status(l_undef) {} virtual ~check_sat_result() {} void inc_ref() { m_ref_count++; } void dec_ref() { SASSERT(m_ref_count > 0); m_ref_count--; if (m_ref_count == 0) dealloc(this); } lbool set_status(lbool r) { return m_status = r; } lbool status() const { return m_status; } virtual void collect_statistics(statistics & st) const = 0; virtual void get_unsat_core(expr_ref_vector & r) = 0; void set_model_converter(model_converter* mc) { m_mc0 = mc; } model_converter* mc0() const { return m_mc0.get(); } virtual void get_model_core(model_ref & m) = 0; void get_model(model_ref & m) { get_model_core(m); if (m && mc0()) (*mc0())(m); } virtual proof * get_proof() = 0; virtual std::string reason_unknown() const = 0; virtual void set_reason_unknown(char const* msg) = 0; void set_reason_unknown(event_handler& eh); virtual void get_labels(svector & r) = 0; virtual ast_manager& get_manager() const = 0; }; /** \brief Very simple implementation of the check_sat_result object. */ struct simple_check_sat_result : public check_sat_result { statistics m_stats; model_ref m_model; expr_ref_vector m_core; proof_ref m_proof; std::string m_unknown; simple_check_sat_result(ast_manager & m); ~simple_check_sat_result() override; ast_manager& get_manager() const override { return m_proof.get_manager(); } void collect_statistics(statistics & st) const override; void get_unsat_core(expr_ref_vector & r) override; void get_model_core(model_ref & m) override; proof * get_proof() override; std::string reason_unknown() const override; void get_labels(svector & r) override; void set_reason_unknown(char const* msg) override { m_unknown = msg; } }; #endif z3-z3-4.8.7/src/solver/combined_solver.cpp000066400000000000000000000271351356505360400204160ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: combined_solver.cpp Abstract: Implements the solver API by combining two solvers. This is a replacement for the strategic_solver class. Author: Leonardo (leonardo) 2012-12-11 Notes: --*/ #include "util/scoped_timer.h" #include "util/common_msgs.h" #include "ast/ast_pp.h" #include "solver/solver.h" #include "solver/combined_solver_params.hpp" #define PS_VB_LVL 15 /** \brief Implementation of the solver API that combines two given solvers. The combined solver has two modes: - non-incremental - incremental In non-incremental mode, the first solver is used. In incremental mode, the second one is used. A timeout for the second solver can be specified. If the timeout is reached, then the first solver is executed. The object switches to incremental when: - push is used - assertions are performed after a check_sat - parameter ignore_solver1==false */ class combined_solver : public solver { public: // Behavior when the incremental solver returns unknown. enum inc_unknown_behavior { IUB_RETURN_UNDEF, // just return unknown IUB_USE_TACTIC_IF_QF, // invoke tactic if problem is quantifier free IUB_USE_TACTIC // invoke tactic }; private: bool m_inc_mode; bool m_check_sat_executed; bool m_use_solver1_results; ref m_solver1; ref m_solver2; // We delay sending assertions to solver 2 // This is relevant for big benchmarks that are meant to be solved // by a non-incremental solver. ); bool m_ignore_solver1; inc_unknown_behavior m_inc_unknown_behavior; unsigned m_inc_timeout; void switch_inc_mode() { m_inc_mode = true; } struct aux_timeout_eh : public event_handler { solver * m_solver; volatile bool m_canceled; aux_timeout_eh(solver * s):m_solver(s), m_canceled(false) {} ~aux_timeout_eh() override { if (m_canceled) { m_solver->get_manager().limit().dec_cancel(); } } void operator()(event_handler_caller_t caller_id) override { m_canceled = true; m_solver->get_manager().limit().inc_cancel(); } }; void updt_local_params(params_ref const & _p) { combined_solver_params p(_p); m_inc_timeout = p.solver2_timeout(); m_ignore_solver1 = p.ignore_solver1(); m_inc_unknown_behavior = static_cast(p.solver2_unknown()); } ast_manager& get_manager() const override { return m_solver1->get_manager(); } bool has_quantifiers() const { unsigned sz = get_num_assertions(); for (unsigned i = 0; i < sz; i++) { if (::has_quantifiers(get_assertion(i))) return true; } return false; } bool use_solver1_when_undef() const { switch (m_inc_unknown_behavior) { case IUB_RETURN_UNDEF: return false; case IUB_USE_TACTIC_IF_QF: return !has_quantifiers(); case IUB_USE_TACTIC: return true; default: UNREACHABLE(); return false; } } public: combined_solver(solver * s1, solver * s2, params_ref const & p) { m_solver1 = s1; m_solver2 = s2; updt_local_params(p); m_inc_mode = false; m_check_sat_executed = false; m_use_solver1_results = true; } solver* translate(ast_manager& m, params_ref const& p) override { TRACE("solver", tout << "translate\n";); solver* s1 = m_solver1->translate(m, p); solver* s2 = m_solver2->translate(m, p); combined_solver* r = alloc(combined_solver, s1, s2, p); r->m_inc_mode = m_inc_mode; r->m_check_sat_executed = m_check_sat_executed; r->m_use_solver1_results = m_use_solver1_results; return r; } void updt_params(params_ref const & p) override { solver::updt_params(p); m_solver1->updt_params(p); m_solver2->updt_params(p); updt_local_params(p); } void collect_param_descrs(param_descrs & r) override { m_solver1->collect_param_descrs(r); m_solver2->collect_param_descrs(r); combined_solver_params::collect_param_descrs(r); } void set_produce_models(bool f) override { m_solver1->set_produce_models(f); m_solver2->set_produce_models(f); } void assert_expr_core(expr * t) override { if (m_check_sat_executed) switch_inc_mode(); m_solver1->assert_expr(t); m_solver2->assert_expr(t); } void assert_expr_core2(expr * t, expr * a) override { if (m_check_sat_executed) switch_inc_mode(); m_solver1->assert_expr(t, a); m_solver2->assert_expr(t, a); } void push() override { switch_inc_mode(); m_solver1->push(); m_solver2->push(); TRACE("pop", tout << "push\n";); } void pop(unsigned n) override { TRACE("pop", tout << n << "\n";); switch_inc_mode(); m_solver1->pop(n); m_solver2->pop(n); } unsigned get_scope_level() const override { return m_solver1->get_scope_level(); } lbool get_consequences(expr_ref_vector const& asms, expr_ref_vector const& vars, expr_ref_vector& consequences) override { switch_inc_mode(); m_use_solver1_results = false; try { return m_solver2->get_consequences(asms, vars, consequences); } catch (z3_exception& ex) { if (get_manager().canceled()) { throw; } else { set_reason_unknown(ex.msg()); } } return l_undef; } lbool check_sat_core(unsigned num_assumptions, expr * const * assumptions) override { m_check_sat_executed = true; m_use_solver1_results = false; if (get_num_assumptions() != 0 || num_assumptions > 0 || // assumptions were provided m_ignore_solver1) { // must use incremental solver switch_inc_mode(); 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_core(num_assumptions, assumptions); if (r != l_undef || !use_solver1_when_undef() || get_manager().canceled()) { return r; } } else { IF_VERBOSE(PS_VB_LVL, verbose_stream() << "(combined-solver \"using solver 2 (with timeout)\")\n";); aux_timeout_eh eh(m_solver2.get()); lbool r = l_undef; try { scoped_timer timer(m_inc_timeout, &eh); r = m_solver2->check_sat_core(num_assumptions, assumptions); } catch (z3_exception&) { if (!eh.m_canceled) { throw; } } if ((r != l_undef || !use_solver1_when_undef()) && !eh.m_canceled) { return r; } } IF_VERBOSE(PS_VB_LVL, verbose_stream() << "(combined-solver \"solver 2 failed, trying solver1\")\n";); } IF_VERBOSE(PS_VB_LVL, verbose_stream() << "(combined-solver \"using solver 1\")\n";); m_use_solver1_results = true; return m_solver1->check_sat_core(num_assumptions, assumptions); } void set_progress_callback(progress_callback * callback) override { m_solver1->set_progress_callback(callback); m_solver2->set_progress_callback(callback); } unsigned get_num_assertions() const override { return m_solver1->get_num_assertions(); } expr * get_assertion(unsigned idx) const override { return m_solver1->get_assertion(idx); } unsigned get_num_assumptions() const override { return m_solver1->get_num_assumptions() + m_solver2->get_num_assumptions(); } expr_ref_vector cube(expr_ref_vector& vars, unsigned backtrack_level) override { switch_inc_mode(); return m_solver2->cube(vars, backtrack_level); } expr * get_assumption(unsigned idx) const override { unsigned c1 = m_solver1->get_num_assumptions(); if (idx < c1) return m_solver1->get_assumption(idx); return m_solver2->get_assumption(idx - c1); } std::ostream& display(std::ostream & out, unsigned n, expr* const* es) const override { return m_solver1->display(out, n, es); } void collect_statistics(statistics & st) const override { m_solver2->collect_statistics(st); if (m_use_solver1_results) m_solver1->collect_statistics(st); } void get_unsat_core(expr_ref_vector & r) override { if (m_use_solver1_results) m_solver1->get_unsat_core(r); else m_solver2->get_unsat_core(r); } void get_model_core(model_ref & m) override { if (m_use_solver1_results) m_solver1->get_model(m); else 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(); } proof * get_proof() override { if (m_use_solver1_results) return m_solver1->get_proof(); else return m_solver2->get_proof(); } std::string reason_unknown() const override { if (m_use_solver1_results) return m_solver1->reason_unknown(); else return m_solver2->reason_unknown(); } void set_reason_unknown(char const* msg) override { m_solver1->set_reason_unknown(msg); m_solver2->set_reason_unknown(msg); } void get_labels(svector & r) override { if (m_use_solver1_results) return m_solver1->get_labels(r); else return m_solver2->get_labels(r); } }; solver * mk_combined_solver(solver * s1, solver * s2, params_ref const & p) { return alloc(combined_solver, s1, s2, p); } class combined_solver_factory : public solver_factory { scoped_ptr m_f1; scoped_ptr m_f2; public: combined_solver_factory(solver_factory * f1, solver_factory * f2):m_f1(f1), m_f2(f2) {} ~combined_solver_factory() override {} solver * operator()(ast_manager & m, params_ref const & p, bool proofs_enabled, bool models_enabled, bool unsat_core_enabled, symbol const & logic) override { return mk_combined_solver((*m_f1)(m, p, proofs_enabled, models_enabled, unsat_core_enabled, logic), (*m_f2)(m, p, proofs_enabled, models_enabled, unsat_core_enabled, logic), p); } }; solver_factory * mk_combined_solver_factory(solver_factory * f1, solver_factory * f2) { return alloc(combined_solver_factory, f1, f2); } z3-z3-4.8.7/src/solver/combined_solver.h000066400000000000000000000010621356505360400200520ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: combined_solver.cpp Abstract: Implements the solver API by combining two solvers. This is a replacement for the strategic_solver class. Author: Leonardo (leonardo) 2012-12-11 Notes: --*/ #ifndef COMBINED_SOLVER_H_ #define COMBINED_SOLVER_H_ #include "util/params.h" class solver; class solver_factory; solver * mk_combined_solver(solver * s1, solver * s2, params_ref const & p); solver_factory * mk_combined_solver_factory(solver_factory * f1, solver_factory * f2); #endif z3-z3-4.8.7/src/solver/combined_solver_params.pyg000066400000000000000000000012161356505360400217660ustar00rootroot00000000000000def_module_params('combined_solver', description='combines two solvers: non-incremental (solver1) and incremental (solver2)', export=True, params=(('solver2_timeout', UINT, UINT_MAX, "fallback to solver 1 after timeout even when in incremental model"), ('ignore_solver1', BOOL, False, "if true, solver 2 is always used"), ('solver2_unknown', UINT, 1, "what should be done when solver 2 returns unknown: 0 - just return unknown, 1 - execute solver 1 if quantifier free problem, 2 - execute solver 1") )) z3-z3-4.8.7/src/solver/mus.cpp000066400000000000000000000264241356505360400160500ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: mus.cpp Abstract: MUS extraction. Author: Nikolaj Bjorner (nbjorner) 2014-20-7 Notes: --*/ #include "solver/solver.h" #include "solver/mus.h" #include "ast/ast_pp.h" #include "ast/ast_util.h" #include "model/model_evaluator.h" struct mus::imp { typedef obj_hashtable expr_set; solver& m_solver; ast_manager& m; expr_ref_vector m_lit2expr; expr_ref_vector m_assumptions; obj_map m_expr2lit; model_ref m_model; expr_ref_vector m_soft; vector m_weights; rational m_weight; imp(solver& s): m_solver(s), m(s.get_manager()), m_lit2expr(m), m_assumptions(m), m_soft(m) {} void reset() { m_lit2expr.reset(); m_expr2lit.reset(); m_assumptions.reset(); } bool is_literal(expr* lit) const { expr* l; return is_uninterp_const(lit) || (m.is_not(lit, l) && is_uninterp_const(l)); } unsigned add_soft(expr* lit) { //SASSERT(is_literal(lit)); unsigned idx = m_lit2expr.size(); m_expr2lit.insert(lit, idx); m_lit2expr.push_back(lit); TRACE("mus", tout << idx << ": " << mk_pp(lit, m) << "\n" << m_lit2expr << "\n";); return idx; } void add_assumption(expr* lit) { m_assumptions.push_back(lit); } lbool get_mus(expr_ref_vector& mus) { m_model.reset(); mus.reset(); if (m_lit2expr.size() == 1) { mus.push_back(m_lit2expr.back()); return l_true; } return get_mus1(mus); } lbool get_mus1(expr_ref_vector& mus) { ptr_vector unknown(m_lit2expr.size(), m_lit2expr.c_ptr()); expr_ref_vector core_exprs(m); TRACE("mus", m_solver.display(tout);); while (!unknown.empty()) { IF_VERBOSE(12, verbose_stream() << "(mus reducing core: " << unknown.size() << " new core: " << mus.size() << ")\n";); TRACE("mus", display_vec(tout << "core: ", unknown); display_vec(tout << "mus: ", mus);); expr* lit = unknown.back(); unknown.pop_back(); expr_ref not_lit(mk_not(m, lit), m); lbool is_sat = l_undef; { scoped_append _sa1(*this, mus, unknown); scoped_append _sa2(*this, mus, m_assumptions); mus.push_back(not_lit); is_sat = m_solver.check_sat(mus); } switch (is_sat) { case l_undef: return is_sat; case l_true: mus.push_back(lit); update_model(); break; default: core_exprs.reset(); m_solver.get_unsat_core(core_exprs); if (!core_exprs.contains(not_lit)) { // unknown := core_exprs \ mus unknown.reset(); for (expr* c : core_exprs) { if (!mus.contains(c)) { unknown.push_back(c); } } TRACE("mus", tout << "core exprs:" << core_exprs << "\n"; display_vec(tout << "core:", unknown); display_vec(tout << "mus:", mus); ); } break; } } // SASSERT(is_core(mus)); return l_true; } // use correction sets lbool get_mus2(expr_ref_vector& mus) { expr* lit = nullptr; lbool is_sat; ptr_vector unknown(m_lit2expr.size(), m_lit2expr.c_ptr()); while (!unknown.empty()) { IF_VERBOSE(12, verbose_stream() << "(mus reducing core: " << unknown.size() << " new core: " << mus.size() << ")\n";); { scoped_append _sa1(*this, mus, m_assumptions); is_sat = get_next_mcs(mus, unknown, lit); } if (l_false == is_sat) { mus.push_back(lit); } else { return is_sat; } } //SASSERT(is_core(mus)); return l_true; } // find the next literal to be a member of a core. lbool get_next_mcs(expr_ref_vector& mus, ptr_vector& unknown, expr*& core_literal) { ptr_vector mss; expr_ref_vector nmcs(m); expr_set core, min_core, nmcs_set; bool min_core_valid = false; expr* min_lit = nullptr; while (!unknown.empty()) { expr* lit = unknown.back(); unknown.pop_back(); model_ref mdl; scoped_append assume_mss(*this, mus, mss); // current satisfied literals scoped_append assume_nmcs(*this, mus, nmcs); // current non-satisfied literals scoped_append assume_lit(*this, mus, lit); // current unknown literal switch (m_solver.check_sat(mus)) { case l_true: { TRACE("mus", tout << "literal can be satisfied: " << mk_pp(lit, m) << "\n";); mss.push_back(lit); m_solver.get_model(mdl); model_evaluator eval(*mdl.get()); for (unsigned i = 0; i < unknown.size(); ) { expr_ref tmp(m); eval(unknown[i], tmp); if (m.is_true(tmp)) { mss.push_back(unknown[i]); unknown[i] = unknown.back(); unknown.pop_back(); } else { ++i; } } break; } case l_false: TRACE("mus", tout << "literal is in a core: " << mk_pp(lit, m) << "\n";); nmcs.push_back(mk_not(m, lit)); nmcs_set.insert(nmcs.back()); get_core(core); if (!core.contains(lit)) { // The current mus is already a core. unknown.reset(); return l_true; } if (have_intersection(nmcs_set, core)) { // can't use this core directly. Hypothetically, we // could try to combine min_core with core and // see if the combination produces a better minimal core. SASSERT(min_core_valid); break; } if (!min_core_valid || core.size() < min_core.size()) { // The current core is smallest so far, so we get fewer unknowns from it. min_core = core; min_core_valid = true; min_lit = lit; } break; case l_undef: return l_undef; } } SASSERT(min_core_valid); if (!min_core_valid) { // all unknown soft constraints were satisfiable return l_true; } expr_set mss_set; for (expr* e : mss) { mss_set.insert(e); } for (expr * e : min_core) { if (mss_set.contains(e) && min_lit != e) { unknown.push_back(e); } } core_literal = min_lit; return l_false; } void get_core(expr_set& core) { core.reset(); expr_ref_vector core_exprs(m); m_solver.get_unsat_core(core_exprs); for (expr* c : core_exprs) { if (m_expr2lit.contains(c)) { core.insert(c); } } } bool have_intersection(expr_set const& A, expr_set const& B) { if (A.size() < B.size()) { expr_set::iterator it = A.begin(), end = A.end(); for (; it != end; ++it) { if (B.contains(*it)) return true; } } else { expr_set::iterator it = B.begin(), end = B.end(); for (; it != end; ++it) { if (A.contains(*it)) return true; } } return false; } bool is_core(expr_ref_vector const& mus) { return l_false == m_solver.check_sat(mus); } class scoped_append { expr_ref_vector& m_fmls; unsigned m_size; public: scoped_append(imp& imp, expr_ref_vector& fmls1, expr_set const& fmls2): m_fmls(fmls1), m_size(fmls1.size()) { expr_set::iterator it = fmls2.begin(), end = fmls2.end(); for (; it != end; ++it) { fmls1.push_back(*it); } } scoped_append(imp& imp, expr_ref_vector& fmls1, expr_ref_vector const& fmls2): m_fmls(fmls1), m_size(fmls1.size()) { fmls1.append(fmls2); } scoped_append(imp& imp, expr_ref_vector& fmls1, ptr_vector const& fmls2): m_fmls(fmls1), m_size(fmls1.size()) { fmls1.append(fmls2.size(), fmls2.c_ptr()); } scoped_append(imp& imp, expr_ref_vector& fmls1, expr* fml): m_fmls(fmls1), m_size(fmls1.size()) { fmls1.push_back(fml); } ~scoped_append() { m_fmls.shrink(m_size); } }; template void display_vec(std::ostream& out, T const& v) const { for (unsigned i = 0; i < v.size(); ++i) { out << v[i] << " "; } out << "\n"; } void display_vec(std::ostream& out, expr_ref_vector const& v) const { for (unsigned i = 0; i < v.size(); ++i) out << mk_pp(v[i], m) << " "; out << "\n"; } void display_vec(std::ostream& out, ptr_vector const& v) const { for (unsigned i = 0; i < v.size(); ++i) out << mk_pp(v[i], m) << " "; out << "\n"; } void set_soft(unsigned sz, expr* const* soft, rational const* weights) { m_model.reset(); m_weight.reset(); m_soft.append(sz, soft); m_weights.append(sz, weights); for (unsigned i = 0; i < sz; ++i) { m_weight += weights[i]; } } void update_model() { if (m_soft.empty()) return; model_ref mdl; expr_ref tmp(m); m_solver.get_model(mdl); rational w; for (unsigned i = 0; i < m_soft.size(); ++i) { if (!mdl->is_true(m_soft.get(i))) { w += m_weights[i]; } } if (w < m_weight || !m_model.get()) { m_model = mdl; m_weight = w; } } rational get_best_model(model_ref& mdl) { mdl = m_model; return m_weight; } }; mus::mus(solver& s) { m_imp = alloc(imp, s); } mus::~mus() { dealloc(m_imp); } unsigned mus::add_soft(expr* lit) { return m_imp->add_soft(lit); } void mus::add_assumption(expr* lit) { return m_imp->add_assumption(lit); } lbool mus::get_mus(expr_ref_vector& mus) { return m_imp->get_mus(mus); } void mus::reset() { m_imp->reset(); } void mus::set_soft(unsigned sz, expr* const* soft, rational const* weights) { m_imp->set_soft(sz, soft, weights); } rational mus::get_best_model(model_ref& mdl) { return m_imp->get_best_model(mdl); } z3-z3-4.8.7/src/solver/mus.h000066400000000000000000000027701356505360400155130ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: mus.h Abstract: Basic MUS extraction Author: Nikolaj Bjorner (nbjorner) 2014-20-7 Notes: --*/ #ifndef MUS_H_ #define MUS_H_ class mus { struct imp; imp * m_imp; public: mus(solver& s); ~mus(); /** Add soft constraint. Assume that the solver context enforces that cls is equivalent to a disjunction of args. Assume also that cls is a literal. */ unsigned add_soft(expr* cls); void add_soft(unsigned sz, expr* const* clss) { for (unsigned i = 0; i < sz; ++i) add_soft(clss[i]); } /** Additional assumption for solver to be used along with solver context, but not used in core computation. This facility is useful when querying for a core over only a subset of soft constraints. It has the same logical functionality as asserting 'lit' to the solver and pushing a scope (and popping the scope before the solver is used for other constraints). */ void add_assumption(expr* lit); lbool get_mus(expr_ref_vector& mus); void reset(); /** Instrument MUS extraction to also provide the minimal penalty model, if any is found. The minimal penalty model has the least weight for the supplied soft constraints. */ void set_soft(unsigned sz, expr* const* soft, rational const* weights); rational get_best_model(model_ref& mdl); }; #endif z3-z3-4.8.7/src/solver/parallel_params.pyg000066400000000000000000000026051356505360400204130ustar00rootroot00000000000000def_module_params('parallel', description='parameters for parallel solver', class_name='parallel_params', export=True, params=( ('enable', BOOL, False, 'enable parallel solver by default on selected tactics (for QF_BV)'), ('threads.max', UINT, 10000, 'caps maximal number of threads below the number of processors'), ('conquer.batch_size', UINT, 100, 'number of cubes to batch together for fast conquer'), ('conquer.restart.max', UINT, 5, 'maximal number of restarts during conquer phase'), ('conquer.delay', UINT, 10, 'delay of cubes until applying conquer'), ('conquer.backtrack_frequency', UINT, 10, 'frequency to apply core minimization during conquer'), ('simplify.exp', DOUBLE, 1, 'restart and inprocess max is multiplied by simplify.exp ^ depth'), ('simplify.max_conflicts', UINT, UINT_MAX, 'maximal number of conflicts during simplifcation phase'), ('simplify.restart.max', UINT, 5000, 'maximal number of restarts during simplification phase'), ('simplify.inprocess.max', UINT, 2, 'maximal number of inprocessing steps during simplification'), )) z3-z3-4.8.7/src/solver/parallel_tactic.cpp000066400000000000000000000571611356505360400203710ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: parallel_tactic.cpp Abstract: Parallel tactic based on cubing. Author: Nikolaj Bjorner (nbjorner) 2017-10-9 Miguel Neves Notes: A task comprises of a non-empty sequence of cubes, a type and parameters It invokes the following procedure: 1. Clone the state with the remaining cubes if there is more than one cube. Re-enqueue the remaining cubes. 2. Apply simplifications and pre-processing according to configuration. 3. Cube using the parameter settings prescribed in m_params. 4. Optionally pass the cubes as assumptions and solve each sub-cube with a prescribed resource bound. 5. Assemble cubes that could not be solved and create a cube state. --*/ #include #include #include #include #include "util/scoped_ptr_vector.h" #include "ast/ast_util.h" #include "ast/ast_translation.h" #include "solver/solver.h" #include "solver/solver2tactic.h" #include "tactic/tactic.h" #include "tactic/tactical.h" #include "solver/parallel_tactic.h" #include "solver/parallel_params.hpp" class parallel_tactic : public tactic { class solver_state; class task_queue { std::mutex m_mutex; std::condition_variable m_cond; ptr_vector m_tasks; ptr_vector m_active; unsigned m_num_waiters; volatile bool m_shutdown; void inc_wait() { std::lock_guard lock(m_mutex); ++m_num_waiters; } void dec_wait() { std::lock_guard lock(m_mutex); --m_num_waiters; } solver_state* try_get_task() { solver_state* st = nullptr; std::lock_guard lock(m_mutex); if (!m_tasks.empty()) { st = m_tasks.back(); m_tasks.pop_back(); m_active.push_back(st); } return st; } public: task_queue(): m_num_waiters(0), m_shutdown(false) {} ~task_queue() { reset(); } void shutdown() { if (!m_shutdown) { m_shutdown = true; m_cond.notify_all(); std::lock_guard lock(m_mutex); for (solver_state* st : m_active) { st->m().limit().cancel(); } } } bool in_shutdown() const { return m_shutdown; } void add_task(solver_state* task) { std::lock_guard lock(m_mutex); m_tasks.push_back(task); if (m_num_waiters > 0) { m_cond.notify_one(); } } bool is_idle() { std::lock_guard lock(m_mutex); return m_tasks.empty() && m_num_waiters > 0; } solver_state* get_task() { while (!m_shutdown) { inc_wait(); solver_state* st = try_get_task(); if (st) { dec_wait(); return st; } { std::unique_lock lock(m_mutex); m_cond.wait(lock); } dec_wait(); } return nullptr; } void task_done(solver_state* st) { std::lock_guard lock(m_mutex); m_active.erase(st); if (m_tasks.empty() && m_active.empty()) { m_shutdown = true; m_cond.notify_all(); } } void reset() { for (auto* t : m_tasks) dealloc(t); for (auto* t : m_active) dealloc(t); m_tasks.reset(); m_active.reset(); } std::ostream& display(std::ostream& out) { std::lock_guard lock(m_mutex); out << "num_tasks " << m_tasks.size() << " active: " << m_active.size() << "\n"; for (solver_state* st : m_tasks) { st->display(out); } return out; } }; class cube_var { expr_ref_vector m_vars; expr_ref_vector m_cube; public: cube_var(expr_ref_vector const& c, expr_ref_vector const& vs): m_vars(vs), m_cube(c) {} cube_var(cube_var const& other): m_vars(other.m_vars), m_cube(other.m_cube) {} cube_var operator()(ast_translation& tr) { expr_ref_vector vars(tr(m_vars)); expr_ref_vector cube(tr(m_cube)); return cube_var(cube, vars); } expr_ref_vector const& cube() const { return m_cube; } expr_ref_vector const& vars() const { return m_vars; } }; class solver_state { scoped_ptr m_manager; // ownership handle to ast_manager vector m_cubes; // set of cubes to process by task expr_ref_vector m_asserted_cubes; // set of cubes asserted on the current solver expr_ref_vector m_assumptions; // set of auxiliary assumptions passed in params_ref m_params; // configuration parameters ref m_solver; // solver state unsigned m_depth; // number of nested calls to cubing double m_width; // estimate of fraction of problem handled by state public: solver_state(ast_manager* m, solver* s, params_ref const& p): m_manager(m), m_asserted_cubes(s->get_manager()), m_assumptions(s->get_manager()), m_params(p), m_solver(s), m_depth(0), m_width(1.0) { } ast_manager& m() { return m_solver->get_manager(); } solver& get_solver() { return *m_solver; } solver* copy_solver() { return m_solver->translate(m_solver->get_manager(), m_params); } solver const& get_solver() const { return *m_solver; } void set_assumptions(ptr_vector const& asms) { m_assumptions.append(asms.size(), asms.c_ptr()); } bool has_assumptions() const { return !m_assumptions.empty(); } solver_state* clone() { SASSERT(!m_cubes.empty()); ast_manager& m = m_solver->get_manager(); 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); for (auto& c : m_cubes) st->m_cubes.push_back(c(tr)); for (expr* c : m_asserted_cubes) st->m_asserted_cubes.push_back(tr(c)); for (expr* c : m_assumptions) st->m_assumptions.push_back(tr(c)); st->m_depth = m_depth; st->m_width = m_width; return st; } vector const& cubes() const { return m_cubes; } // remove up to n cubes from list of cubes. vector split_cubes(unsigned n) { vector result; while (n-- > 0 && !m_cubes.empty()) { result.push_back(m_cubes.back()); m_cubes.pop_back(); } return result; } void set_cubes(vector& c) { m_cubes.reset(); m_cubes.append(c); } void inc_depth(unsigned inc) { m_depth += inc; } void inc_width(unsigned w) { m_width *= w; } double get_width() const { return m_width; } unsigned get_depth() const { return m_depth; } lbool simplify() { lbool r = l_undef; IF_VERBOSE(2, verbose_stream() << "(parallel.tactic simplify-1)\n";); set_simplify_params(true); // retain blocked r = get_solver().check_sat(m_assumptions); if (r != l_undef) return r; IF_VERBOSE(2, verbose_stream() << "(parallel.tactic simplify-2)\n";); set_simplify_params(false); // remove blocked r = get_solver().check_sat(m_assumptions); return r; } void assert_cube(expr_ref_vector const& cube) { get_solver().assert_expr(cube); m_asserted_cubes.append(cube); } void set_cube_params() { } void set_conquer_params() { set_conquer_params(get_solver()); } void set_conquer_params(solver& s) { parallel_params pp(m_params); params_ref p; p.copy(m_params); p.set_bool("gc.burst", true); // apply eager gc p.set_uint("simplify.delay", 1000); // delay simplification by 1000 conflicts p.set_bool("lookahead_simplify", false); p.set_uint("restart.max", pp.conquer_restart_max()); p.set_uint("inprocess.max", UINT_MAX); // base bounds on restart.max s.updt_params(p); } void set_simplify_params(bool retain_blocked) { parallel_params pp(m_params); params_ref p; p.copy(m_params); double exp = pp.simplify_exp(); exp = std::max(exp, 1.0); unsigned mult = static_cast(pow(exp, m_depth - 1)); p.set_uint("inprocess.max", pp.simplify_inprocess_max() * mult); p.set_uint("restart.max", pp.simplify_restart_max() * mult); p.set_bool("lookahead_simplify", true); p.set_bool("retain_blocked_clauses", retain_blocked); p.set_uint("max_conflicts", pp.simplify_max_conflicts()); if (m_depth > 1) p.set_uint("bce_delay", 0); get_solver().updt_params(p); } bool canceled() { return m().canceled(); } std::ostream& display(std::ostream& out) { out << ":depth " << m_depth << " :width " << m_width << "\n"; out << ":asserted " << m_asserted_cubes.size() << "\n"; return out; } }; private: solver_ref m_solver; ast_manager& m_manager; params_ref m_params; sref_vector m_models; expr_ref_vector m_core; unsigned m_num_threads; statistics m_stats; task_queue m_queue; std::mutex m_mutex; double m_progress; unsigned m_branches; unsigned m_backtrack_frequency; unsigned m_conquer_delay; 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; void init() { parallel_params pp(m_params); m_num_threads = std::min((unsigned) std::thread::hardware_concurrency(), pp.threads_max()); m_progress = 0; m_has_undef = false; m_allsat = false; m_branches = 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; m_params.set_bool("override_incremental", true); m_core.reset(); } void log_branches(lbool status) { IF_VERBOSE(1, verbose_stream() << "(tactic.parallel :progress " << m_progress << "% "; if (status == l_true) verbose_stream() << ":status sat"; if (status == l_undef) verbose_stream() << ":status unknown"; 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) { if (b == 0) return; { std::lock_guard lock(m_mutex); m_branches += b; } log_branches(l_false); } void dec_branch() { std::lock_guard lock(m_mutex); --m_branches; } void collect_core(expr_ref_vector const& core) { std::lock_guard lock(m_mutex); ast_translation tr(core.get_manager(), m_manager); expr_ref_vector core1(tr(core)); for (expr* c : core1) { if (!m_core.contains(c)) m_core.push_back(c); } } void close_branch(solver_state& s, lbool status) { double f = 100.0 / s.get_width(); { std::lock_guard lock(m_mutex); m_progress += f; --m_branches; } log_branches(status); } void report_sat(solver_state& s, solver* conquer) { close_branch(s, l_true); model_ref 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) { ast_translation tr(s.m(), m_manager); mdl = mdl->translate(tr); } m_models.push_back(mdl.get()); } if (!m_allsat) { m_queue.shutdown(); } } 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(s); close_branch(s, l_false); if (s.has_assumptions()) { expr_ref_vector core(s.m()); s.get_solver().get_unsat_core(core); collect_core(core); } } void report_undef(solver_state& s) { m_has_undef = true; close_branch(s, l_undef); } void cube_and_conquer(solver_state& s) { ast_manager& m = s.m(); vector cube, hard_cubes, cubes; expr_ref_vector vars(m); cube_again: // extract up to one cube and add it. cube.reset(); cube.append(s.split_cubes(1)); SASSERT(cube.size() <= 1); IF_VERBOSE(2, verbose_stream() << "(tactic.parallel :split-cube " << cube.size() << ")\n";); if (!s.cubes().empty()) m_queue.add_task(s.clone()); if (!cube.empty()) { s.assert_cube(cube.get(0).cube()); vars.reset(); vars.append(cube.get(0).vars()); } simplify_again: s.inc_depth(1); // simplify if (canceled(s)) return; switch (s.simplify()) { case l_undef: break; case l_true: report_sat(s, nullptr); return; case l_false: report_unsat(s); return; } if (canceled(s)) return; if (memory_pressure()) { goto simplify_again; } // extract cubes. cubes.reset(); s.set_cube_params(); solver_ref conquer; unsigned cutoff = UINT_MAX; bool first = true; unsigned num_backtracks = 0, width = 0; while (cutoff > 0 && !canceled(s)) { expr_ref_vector c = s.get_solver().cube(vars, cutoff); if (c.empty()) { goto simplify_again; } if (m.is_false(c.back())) { break; } lbool is_sat = l_undef; if (!s.has_assumptions() && width >= m_conquer_delay && !conquer) { conquer = s.copy_solver(); s.set_conquer_params(*conquer.get()); } if (conquer) { is_sat = conquer->check_sat(c); } switch (is_sat) { case l_false: cutoff = c.size(); backtrack(*conquer.get(), c, (num_backtracks++) % m_backtrack_frequency == 0); if (cutoff != c.size()) { IF_VERBOSE(0, verbose_stream() << "(tactic.parallel :backtrack " << cutoff << " -> " << c.size() << ")\n"); cutoff = c.size(); } inc_unsat(s); log_branches(l_false); break; case l_true: report_sat(s, conquer.get()); if (conquer) { collect_statistics(*conquer.get()); } return; case l_undef: ++width; IF_VERBOSE(2, verbose_stream() << "(tactic.parallel :cube " << c.size() << " :vars " << vars.size() << ")\n"); cubes.push_back(cube_var(c, vars)); cutoff = UINT_MAX; break; } if (cubes.size() >= conquer_batch_size()) { spawn_cubes(s, 10*width, cubes); first = false; cubes.reset(); } } if (conquer) { collect_statistics(*conquer.get()); } if (cubes.empty() && first) { report_unsat(s); } else if (cubes.empty()) { dec_branch(); } else { s.inc_width(width); add_branches(cubes.size()-1); s.set_cubes(cubes); goto cube_again; } } void spawn_cubes(solver_state& s, unsigned width, vector& cubes) { if (cubes.empty()) return; add_branches(cubes.size()); s.set_cubes(cubes); solver_state* s1 = s.clone(); s1->inc_width(width); m_queue.add_task(s1); } /* * \brief backtrack from unsatisfiable core */ void backtrack(solver& s, expr_ref_vector& asms, bool full) { ast_manager& m = s.get_manager(); expr_ref_vector core(m); obj_hashtable hcore; s.get_unsat_core(core); while (!asms.empty() && !core.contains(asms.back())) asms.pop_back(); if (!full) return; //s.assert_expr(m.mk_not(mk_and(core))); if (!asms.empty()) { expr* last = asms.back(); expr_ref not_last(mk_not(m, last), m); asms.pop_back(); asms.push_back(not_last); lbool r = s.check_sat(asms); asms.pop_back(); if (r != l_false) { asms.push_back(last); return; } core.reset(); s.get_unsat_core(core); if (core.contains(not_last)) { //s.assert_expr(m.mk_not(mk_and(core))); r = s.check_sat(asms); } if (r == l_false) { backtrack(s, asms, full); } } } bool canceled(solver_state& s) { if (s.canceled()) { m_has_undef = true; return true; } else { return false; } } bool memory_pressure() { return memory::above_high_watermark(); } void run_solver() { try { while (solver_state* st = m_queue.get_task()) { cube_and_conquer(*st); collect_statistics(*st); m_queue.task_done(st); if (st->m().canceled()) m_queue.shutdown(); IF_VERBOSE(1, display(verbose_stream());); dealloc(st); } } catch (z3_exception& ex) { IF_VERBOSE(1, verbose_stream() << ex.msg() << "\n";); if (m_queue.in_shutdown()) return; m_queue.shutdown(); std::lock_guard lock(m_mutex); if (ex.has_error_code()) { m_exn_code = ex.error_code(); } else { m_exn_msg = ex.msg(); m_exn_code = -1; } } } void collect_statistics(solver_state& s) { collect_statistics(s.get_solver()); } void collect_statistics(solver& s) { std::lock_guard lock(m_mutex); s.collect_statistics(m_stats); } lbool solve(model_ref& mdl) { add_branches(1); vector threads; for (unsigned i = 0; i < m_num_threads; ++i) threads.push_back(std::thread([this]() { run_solver(); })); for (std::thread& t : threads) t.join(); m_manager.limit().reset_cancel(); if (m_exn_code == -1) throw default_exception(std::move(m_exn_msg)); if (m_exn_code != 0) throw z3_error(m_exn_code); if (!m_models.empty()) { mdl = m_models.back(); return l_true; } if (m_has_undef) return l_undef; return l_false; } std::ostream& display(std::ostream& out) { unsigned n_models, n_unsat; double n_progress; { std::lock_guard lock(m_mutex); n_models = m_models.size(); n_unsat = m_num_unsat; n_progress = m_progress; } m_stats.display(out); m_queue.display(out); out << "(tactic.parallel :unsat " << n_unsat << " :progress " << n_progress << "% :models " << n_models << ")\n"; return out; } public: parallel_tactic(solver* s, params_ref const& p) : m_solver(s), m_manager(s->get_manager()), m_params(p), m_core(m_manager) { init(); } void operator ()(const goal_ref & g,goal_ref_buffer & result) override { fail_if_proof_generation("parallel-tactic", g); ast_manager& m = g->m(); solver* s = m_solver->translate(m, m_params); solver_state* st = alloc(solver_state, nullptr, s, m_params); m_queue.add_task(st); expr_ref_vector clauses(m); ptr_vector assumptions; obj_map bool2dep; ref fmc; expr_dependency * lcore = nullptr; proof* pr = nullptr; extract_clauses_and_dependencies(g, clauses, assumptions, bool2dep, fmc); for (expr * clause : clauses) { s->assert_expr(clause); } st->set_assumptions(assumptions); model_ref mdl; lbool is_sat = solve(mdl); switch (is_sat) { case l_true: g->reset(); if (g->models_enabled()) { g->add(concat(fmc.get(), model2model_converter(mdl.get()))); } break; case l_false: SASSERT(!g->proofs_enabled()); for (expr * c : m_core) { lcore = m.mk_join(lcore, m.mk_leaf(bool2dep.find(c))); } g->assert_expr(m.mk_false(), pr, lcore); break; case l_undef: if (m.canceled()) { throw tactic_exception(Z3_CANCELED_MSG); } break; } result.push_back(g.get()); } unsigned conquer_batch_size() const { parallel_params pp(m_params); return pp.conquer_batch_size(); } void cleanup() override { m_queue.reset(); } tactic* translate(ast_manager& m) override { solver* s = m_solver->translate(m, m_params); return alloc(parallel_tactic, s, m_params); } void updt_params(params_ref const & p) override { m_params.copy(p); parallel_params pp(p); m_conquer_delay = pp.conquer_delay(); } void collect_statistics(statistics & st) const override { st.copy(m_stats); st.update("par unsat", m_num_unsat); st.update("par models", m_models.size()); st.update("par progress", m_progress); } void reset_statistics() override { m_stats.reset(); } }; tactic * mk_parallel_tactic(solver* s, params_ref const& p) { return alloc(parallel_tactic, s, p); } z3-z3-4.8.7/src/solver/parallel_tactic.h000066400000000000000000000005551356505360400200310ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: parallel_tactic.h Abstract: Parallel tactic in the style of Treengeling. Author: Nikolaj Bjorner (nbjorner) 2017-10-9 Notes: --*/ #ifndef PARALLEL_TACTIC_H_ #define PARALLEL_TACTIC_H_ class tactic; class solver; tactic * mk_parallel_tactic(solver* s, params_ref const& p); #endif z3-z3-4.8.7/src/solver/progress_callback.h000066400000000000000000000010541356505360400203610ustar00rootroot00000000000000/*++ Copyright (c) 2008 Microsoft Corporation Module Name: progress_callback.h Abstract: Virtual callback for reporting progress. Author: Michal Moskal (micmo) 2009-02-17. Revision History: --*/ #ifndef PROGRESS_CALLBACK_H_ #define PROGRESS_CALLBACK_H_ class progress_callback { public: virtual ~progress_callback() {} // Called on every check for resource limit exceeded (much more frequent). virtual void fast_progress_sample() {} // Less frequent invoked. virtual void slow_progress_sample() {} }; #endif z3-z3-4.8.7/src/solver/smt_logics.cpp000066400000000000000000000074161356505360400174070ustar00rootroot00000000000000/*++ Copyright (c) 2016 Microsoft Corporation Module Name: smt_logics.cpp Abstract: Module for recognizing SMT logics. Author: Nikolaj Bjorner (nbjorner) 2016-11-4 Revision History: --*/ #include "util/symbol.h" #include "solver/smt_logics.h" bool smt_logics::supported_logic(symbol const & s) { return logic_has_uf(s) || logic_is_allcsp(s) || logic_has_fd(s) || logic_has_arith(s) || logic_has_bv(s) || logic_has_array(s) || logic_has_seq(s) || logic_has_str(s) || logic_has_horn(s) || logic_has_fpa(s); } bool smt_logics::logic_has_reals_only(symbol const& s) { return s == "QF_RDL" || s == "QF_LRA" || s == "UFLRA" || s == "LRA" || s == "RDL" || s == "NRA" || s == "QF_NRA" || s == "QF_UFNRA" || s == "QF_UFLRA"; } bool smt_logics::logic_has_arith(symbol const & s) { return s == "QF_LRA" || s == "QF_LIA" || s == "QF_RDL" || s == "QF_IDL" || s == "QF_AUFLIA" || s == "QF_ALIA" || s == "QF_AUFLIRA" || s == "QF_AUFNIA" || s == "QF_AUFNIRA" || s == "QF_ANIA" || s == "QF_LIRA" || s == "QF_UFLIA" || s == "QF_UFLRA" || s == "QF_UFIDL" || s == "QF_UFRDL" || s == "QF_NIA" || s == "QF_NRA" || s == "QF_NIRA" || s == "QF_UFNRA" || s == "QF_UFNIA" || s == "QF_UFNIRA" || s == "QF_BVRE" || s == "ALIA" || s == "AUFLIA" || s == "AUFLIRA" || s == "AUFNIA" || s == "AUFNIRA" || s == "UFLIA" || s == "UFLRA" || s == "UFNRA" || s == "UFNIRA" || s == "NIA" || s == "NRA" || s == "UFNIA" || s == "LIA" || s == "LRA" || s == "UFIDL" || s == "QF_FP" || s == "FP" || s == "QF_FPBV" || s == "QF_BVFP" || s == "QF_S" || logic_is_allcsp(s) || s == "QF_FD" || s == "HORN" || s == "QF_FPLRA"; } bool smt_logics::logic_has_bv(symbol const & s) { return s == "UFBV" || s == "AUFBV" || s == "ABV" || s == "BV" || s == "QF_BV" || s == "QF_UFBV" || s == "QF_ABV" || s == "QF_AUFBV" || s == "QF_BVRE" || s == "QF_FPBV" || s == "FP" || s == "QF_BVFP" || logic_is_allcsp(s) || s == "QF_FD" || s == "HORN"; } bool smt_logics::logic_has_array(symbol const & s) { return s == "QF_AX" || s == "QF_AUFLIA" || s == "QF_ANIA" || s == "QF_ALIA" || s == "QF_AUFLIRA" || s == "QF_AUFNIA" || s == "QF_AUFNIRA" || s == "ALIA" || s == "AUFLIA" || s == "AUFLIRA" || s == "AUFNIA" || s == "AUFNIRA" || s == "AUFBV" || s == "ABV" || logic_is_allcsp(s) || s == "QF_ABV" || s == "QF_AUFBV" || s == "HORN"; } bool smt_logics::logic_has_seq(symbol const & s) { return s == "QF_BVRE" || s == "QF_S" || logic_is_allcsp(s); } bool smt_logics::logic_has_str(symbol const & s) { return s == "QF_S" || logic_is_allcsp(s); } bool smt_logics::logic_has_fpa(symbol const & 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) { return s == "QF_UF" || s == "UF" || s == "QF_DT"; } bool smt_logics::logic_has_horn(symbol const& s) { return s == "HORN"; } bool smt_logics::logic_has_pb(symbol const& s) { return s == "QF_FD" || logic_is_allcsp(s) || logic_has_horn(s); } bool smt_logics::logic_has_datatype(symbol const& s) { return s == "QF_FD" || logic_is_allcsp(s) || s == "QF_DT"; } z3-z3-4.8.7/src/solver/smt_logics.h000066400000000000000000000023401356505360400170430ustar00rootroot00000000000000/*++ Copyright (c) 2016 Microsoft Corporation Module Name: smt_logics.h Abstract: Module for recognizing SMT logics. Author: Nikolaj Bjorner (nbjorner) 2016-11-4 Revision History: --*/ #ifndef SMT_LOGICS_H_ #define SMT_LOGICS_H_ class smt_logics { public: smt_logics() {} static bool supported_logic(symbol const & s); static bool logic_has_reals_only(symbol const& l); static bool logic_is_all(symbol const& s) { return s == "ALL"; } static bool logic_is_csp(symbol const& s) { return s == "CSP"; } static bool logic_is_allcsp(symbol const& s) { return logic_is_all(s) || logic_is_csp(s); } static bool logic_has_uf(symbol const& s); static bool logic_has_arith(symbol const & s); static bool logic_has_bv(symbol const & s); static bool logic_has_array(symbol const & s); static bool logic_has_seq(symbol const & s); static bool logic_has_str(symbol const & s); static bool logic_has_fpa(symbol const & s); static bool logic_has_horn(symbol const& s); static bool logic_has_pb(symbol const& s); static bool logic_has_fd(symbol const& s) { return s == "QF_FD"; } static bool logic_has_datatype(symbol const& s); }; #endif /* SMT_LOGICS_H_ */ z3-z3-4.8.7/src/solver/solver.cpp000066400000000000000000000230161356505360400165500ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: solver.cpp Abstract: abstract solver interface Author: Leonardo (leonardo) 2011-03-19 Notes: --*/ #include "util/common_msgs.h" #include "util/stopwatch.h" #include "ast/ast_util.h" #include "ast/ast_pp.h" #include "ast/ast_pp_util.h" #include "ast/display_dimacs.h" #include "tactic/model_converter.h" #include "solver/solver.h" #include "solver/solver_params.hpp" #include "model/model_evaluator.h" #include "model/model_params.hpp" unsigned solver::get_num_assertions() const { UNREACHABLE(); return 0; } expr * solver::get_assertion(unsigned idx) const { UNREACHABLE(); return nullptr; } std::ostream& solver::display(std::ostream & out, unsigned n, expr* const* assumptions) const { expr_ref_vector fmls(get_manager()); get_assertions(fmls); ast_pp_util visitor(get_manager()); model_converter_ref mc = get_model_converter(); if (mc.get()) { mc->set_env(&visitor); } visitor.collect(fmls); visitor.collect(n, assumptions); visitor.display_decls(out); visitor.display_asserts(out, fmls, true); if (mc.get()) { mc->display(out); mc->set_env(nullptr); } 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) { fmls.push_back(get_assertion(i)); } } expr_ref_vector solver::get_assertions() const { expr_ref_vector result(get_manager()); get_assertions(result); return result; } struct scoped_assumption_push { expr_ref_vector& m_vec; scoped_assumption_push(expr_ref_vector& v, expr* e): m_vec(v) { v.push_back(e); } ~scoped_assumption_push() { m_vec.pop_back(); } }; lbool solver::get_consequences(expr_ref_vector const& asms, expr_ref_vector const& vars, expr_ref_vector& consequences) { try { return get_consequences_core(asms, vars, consequences); } catch (z3_exception& ex) { if (asms.get_manager().canceled()) { set_reason_unknown(Z3_CANCELED_MSG); return l_undef; } else { set_reason_unknown(ex.msg()); } throw; } } lbool solver::get_consequences_core(expr_ref_vector const& asms, expr_ref_vector const& vars, expr_ref_vector& consequences) { ast_manager& m = asms.get_manager(); lbool is_sat = check_sat(asms); if (is_sat != l_true) { return is_sat; } model_ref model; get_model(model); expr_ref tmp(m), nlit(m), lit(m), val(m); expr_ref_vector asms1(asms); model_evaluator eval(*model.get()); unsigned k = 0; for (unsigned i = 0; i < vars.size(); ++i) { expr_ref_vector core(m); tmp = vars[i]; val = eval(tmp); if (!m.is_value(val)) { // vars[i] is unfixed continue; } if (m.is_bool(tmp) && is_uninterp_const(tmp)) { if (m.is_true(val)) { nlit = m.mk_not(tmp); lit = tmp; } else if (m.is_false(val)) { nlit = tmp; lit = m.mk_not(tmp); } else { // vars[i] is unfixed continue; } scoped_assumption_push _scoped_push(asms1, nlit); is_sat = check_sat(asms1); switch (is_sat) { case l_undef: return is_sat; case l_true: // vars[i] is unfixed break; case l_false: get_unsat_core(core); k = 0; for (unsigned j = 0; j < core.size(); ++j) { if (core[j].get() != nlit) { core[k] = core[j].get(); ++k; } } core.resize(k); consequences.push_back(m.mk_implies(mk_and(core), lit)); break; } } else { lit = m.mk_eq(tmp, val); nlit = m.mk_not(lit); scoped_push _scoped_push(*this); assert_expr(nlit); is_sat = check_sat(asms); switch (is_sat) { case l_undef: return is_sat; case l_true: // vars[i] is unfixed break; case l_false: get_unsat_core(core); consequences.push_back(m.mk_implies(mk_and(core), lit)); break; } } } return l_true; } lbool solver::find_mutexes(expr_ref_vector const& vars, vector& mutexes) { return l_true; } lbool solver::preferred_sat(expr_ref_vector const& asms, vector& cores) { return check_sat(0, nullptr); } static bool is_m_atom(ast_manager& m, expr* f) { if (!is_app(f)) return true; app* _f = to_app(f); family_id bfid = m.get_basic_family_id(); if (_f->get_family_id() != bfid) return true; if (_f->get_num_args() > 0 && m.is_bool(_f->get_arg(0))) return false; return m.is_eq(f) || m.is_distinct(f); } bool solver::is_literal(ast_manager& m, expr* e) { return is_m_atom(m, e) || (m.is_not(e, e) && is_m_atom(m, e)); } void solver::assert_expr(expr* f) { expr_ref fml(f, get_manager()); if (m_enforce_model_conversion) { model_converter_ref mc = get_model_converter(); if (mc) { (*mc)(fml); } } assert_expr_core(fml); } void solver::assert_expr(expr* f, expr* t) { ast_manager& m = get_manager(); expr_ref fml(f, m); expr_ref a(t, m); if (m_enforce_model_conversion) { model_converter_ref mc = get_model_converter(); if (mc) { (*mc)(fml); // (*mc)(a); } } assert_expr_core2(fml, a); } void solver::collect_param_descrs(param_descrs & r) { solver_params sp(m_params); sp.collect_param_descrs(r); model_params mp(m_params); mp.collect_param_descrs(r); 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; 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); 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 = get_manager(); expr_ref_vector fmls(m), result(m), tmp(m); get_assertions(fmls); obj_map units; for (expr* f : fmls) { if (m.is_not(f, f) && is_literal(m, f)) { m.inc_ref(f); units.insert(f, false); } else if (is_literal(m, f)) { m.inc_ref(f); units.insert(f, true); } } model_converter_ref mc = get_model_converter(); if (mc) { mc->get_units(units); } for (auto const& kv : units) { tmp.push_back(kv.m_key); if (kv.m_value) result.push_back(kv.m_key); else result.push_back(m.mk_not(kv.m_key)); } for (expr* e : tmp) { m.dec_ref(e); } return result; } 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(); expr_mark marked; unsigned sz0 = fmls.size(); for (unsigned i = 0; i < fmls.size(); ++i) { expr* f = fmls.get(i); if (marked.is_marked(f)) continue; marked.mark(f); if (!is_app(f)) { if (i >= sz0) result.push_back(f); continue; } app* _f = to_app(f); if (_f->get_family_id() == bfid) { // basic objects are true/false/and/or/not/=/distinct // and proof objects (that are not Boolean). if (i < sz0 && m.is_not(f) && is_m_atom(m, _f->get_arg(0))) { marked.mark(_f->get_arg(0)); } else if (_f->get_num_args() > 0 && m.is_bool(_f->get_arg(0))) { fmls.append(_f->get_num_args(), _f->get_args()); } else if (i >= sz0 && is_m_atom(m, f)) { result.push_back(f); } } else { if (i >= sz0) result.push_back(f); } } 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().limit().inc(0)) { 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); } } z3-z3-4.8.7/src/solver/solver.h000066400000000000000000000202101356505360400162060ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: solver.h Abstract: abstract solver interface Author: Leonardo (leonardo) 2011-03-19 Notes: --*/ #ifndef SOLVER_H_ #define SOLVER_H_ #include "solver/check_sat_result.h" #include "solver/progress_callback.h" #include "util/params.h" class solver; class model_converter; class solver_factory { public: virtual ~solver_factory() {} virtual solver * operator()(ast_manager & m, params_ref const & p, bool proofs_enabled, bool models_enabled, bool unsat_core_enabled, symbol const & logic) = 0; }; solver_factory * mk_smt_strategic_solver_factory(symbol const & logic = symbol::null); /** \brief Abstract interface for making solvers available in the Z3 API and front-ends such as SMT 2.0 and (legacy) SMT 1.0. It provides the basic functionality for incremental solvers. - assertions - push/pop - parameter setting (updt_params) - statistics - results based on check_sat_result API */ class solver : public check_sat_result { params_ref m_params; bool m_enforce_model_conversion; symbol m_cancel_backup_file; public: solver(): m_enforce_model_conversion(false) {} ~solver() override {} /** \brief Creates a clone of the solver. */ virtual solver* translate(ast_manager& m, params_ref const& p) = 0; /** \brief Update the solver internal settings. */ virtual void updt_params(params_ref const & p); /** \brief reset parameters. */ virtual void reset_params(params_ref const& p); /** \brief Retrieve set of parameters set on solver. */ virtual params_ref const& get_params() const { return m_params; } /** \brief Store in \c r a description of the configuration parameters available in this solver. */ virtual void collect_param_descrs(param_descrs & r); /** \brief Push a parameter state. It is restored upon pop. Only a single scope of push is supported. */ virtual void push_params() {} /** \brief Pop a parameter state. \sa push_params. */ virtual void pop_params() {} /** \brief Enable/Disable model generation for this solver object. It is invoked before init(m, logic). The user may optionally invoke it after init(m, logic). */ virtual void set_produce_models(bool f) {} /** \brief Add a new formula to the assertion stack. */ void assert_expr(expr* f); virtual void assert_expr_core(expr * t) = 0; void assert_expr(expr_ref_vector const& ts) { for (expr* e : ts) assert_expr(e); } void assert_expr(ptr_vector const& ts) { for (expr* e : ts) assert_expr(e); } /** \brief Add a new formula \c t to the assertion stack, and "tag" it with \c a. The propositional variable \c a is used to track the use of \c t in a proof of unsatisfiability. */ void assert_expr(expr * t, expr* a); virtual void assert_expr_core2(expr * t, expr * a) = 0; /** \brief Create a backtracking point. */ virtual void push() = 0; /** \brief Remove \c n backtracking points. All assertions between the pop and matching push are removed. */ virtual void pop(unsigned n) = 0; /** \brief Return the number of backtracking points. */ virtual unsigned get_scope_level() const = 0; /** \brief Check if the set of assertions in the assertion stack is satisfiable modulo the given assumptions. If it is unsatisfiable, and unsat-core generation is enabled. Then, the unsat-core is a subset of these assumptions. */ 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()); } lbool check_sat(app_ref_vector const& asms) { return check_sat(asms.size(), (expr* const*)asms.c_ptr()); } lbool check_sat() { return check_sat(0, nullptr); } /** \brief Check satisfiability modulo a cube and a clause. The cube corresponds to auxiliary assumptions. The clause as an auxiliary disjunction that is also assumed for the check. */ virtual lbool check_sat_cc(expr_ref_vector const& cube, vector const& clauses) { if (clauses.empty()) return check_sat(cube.size(), cube.c_ptr()); NOT_IMPLEMENTED_YET(); } /** \brief Set a progress callback procedure that is invoked by this solver during check_sat. This is essentially for backward compatibility and integration with VCC tools. */ virtual void set_progress_callback(progress_callback * callback) = 0; /** \brief Return the number of assertions in the assertion stack. */ virtual unsigned get_num_assertions() const; /** \brief Return the assertion at position idx in the assertion stack. */ virtual expr * get_assertion(unsigned idx) const; /** \brief Retrieves assertions as a vector. */ void get_assertions(expr_ref_vector& fmls) const; expr_ref_vector get_assertions() const; /** \brief The number of tracked assumptions (see assert_expr(t, a)). */ virtual unsigned get_num_assumptions() const = 0; /** \brief Retrieves the idx'th tracked assumption (see assert_expr(t, a)). */ virtual expr * get_assumption(unsigned idx) const = 0; /** \brief under assumptions, asms, retrieve set of consequences that fix values for expressions that can be built from vars. The consequences are clauses whose first literal constrain one of the functions from vars and the other literals are negations of literals from asms. */ virtual lbool get_consequences(expr_ref_vector const& asms, expr_ref_vector const& vars, expr_ref_vector& consequences); /** \brief Find maximal subsets A' of A such that |A'| <= 1. These subsets look somewhat similar to cores: cores have the property that |~A'| >= 1, where ~A' is the set of negated formulas from A' */ virtual lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes); /** \brief Preferential SAT. Prefer assumptions to be true, produce cores that witness cases when not all assumptions can be met. by default, preferred sat ignores the assumptions. */ virtual lbool preferred_sat(expr_ref_vector const& asms, vector& cores); /** \brief extract a lookahead candidates for branching. */ virtual expr_ref_vector cube(expr_ref_vector& vars, unsigned backtrack_level) = 0; /** \brief Display the content of this solver. */ 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. */ virtual model_converter_ref get_model_converter() const { return m_mc0; } /** \brief extract units from solver. */ expr_ref_vector get_units(); 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; class scoped_push { solver& s; bool m_nopop; public: scoped_push(solver& s):s(s), m_nopop(false) { s.push(); } ~scoped_push() { if (!m_nopop) s.pop(1); } 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; inline std::ostream& operator<<(std::ostream& out, solver const& s) { return s.display(out); } #endif z3-z3-4.8.7/src/solver/solver2tactic.cpp000066400000000000000000000144351356505360400200270ustar00rootroot00000000000000/*++ Copyright (c) 2016 Microsoft Corporation Module Name: solver2tactic.cpp Abstract: Convert solver to a tactic. Author: Nikolaj Bjorner (nbjorner) 2016-10-17 Notes: --*/ #include "solver/solver.h" #include "tactic/tactic.h" #include "tactic/generic_model_converter.h" #include "solver/solver2tactic.h" #include "ast/ast_util.h" typedef obj_map expr2expr_map; void extract_clauses_and_dependencies(goal_ref const& g, expr_ref_vector& clauses, ptr_vector& assumptions, expr2expr_map& bool2dep, ref& fmc) { expr2expr_map dep2bool; ptr_vector deps; ast_manager& m = g->m(); expr_ref_vector clause(m); unsigned sz = g->size(); for (unsigned i = 0; i < sz; i++) { expr * f = g->form(i); expr_dependency * d = g->dep(i); if (d == nullptr || !g->unsat_core_enabled()) { clauses.push_back(f); } else { // create clause (not d1 \/ ... \/ not dn \/ f) when the d's are the assumptions/dependencies of f. clause.reset(); clause.push_back(f); deps.reset(); m.linearize(d, deps); SASSERT(!deps.empty()); // d != 0, then deps must not be empty ptr_vector::iterator it = deps.begin(); ptr_vector::iterator end = deps.end(); for (; it != end; ++it) { expr * d = *it; if (is_uninterp_const(d) && m.is_bool(d)) { // no need to create a fresh boolean variable for d if (!bool2dep.contains(d)) { assumptions.push_back(d); bool2dep.insert(d, d); } clause.push_back(m.mk_not(d)); } else { // must normalize assumption expr * b = nullptr; if (!dep2bool.find(d, b)) { b = m.mk_fresh_const(nullptr, m.mk_bool_sort()); dep2bool.insert(d, b); bool2dep.insert(b, d); assumptions.push_back(b); if (!fmc) { fmc = alloc(generic_model_converter, m, "solver2tactic"); } fmc->hide(to_app(b)->get_decl()); } clause.push_back(m.mk_not(b)); } } SASSERT(clause.size() > 1); expr_ref cls(m); cls = mk_or(m, clause.size(), clause.c_ptr()); clauses.push_back(cls); } } } class solver2tactic : public tactic { ast_manager& m; ref m_solver; params_ref m_params; statistics m_st; public: solver2tactic(solver* s): m(s->get_manager()), m_solver(s) {} void updt_params(params_ref const & p) override { m_params.append(p); m_solver->updt_params(p); } void collect_param_descrs(param_descrs & r) override { m_solver->collect_param_descrs(r); } void operator()(/* in */ goal_ref const & in, /* out */ goal_ref_buffer & result) override { expr_ref_vector clauses(m); expr2expr_map bool2dep; ptr_vector assumptions; ref fmc; extract_clauses_and_dependencies(in, clauses, assumptions, bool2dep, fmc); ref local_solver = m_solver->translate(m, m_params); local_solver->assert_expr(clauses); TRACE("solver2tactic", tout << "clauses asserted\n";); lbool r; try { r = local_solver->check_sat(assumptions.size(), assumptions.c_ptr()); } catch (...) { local_solver->collect_statistics(m_st); throw; } TRACE("solver2tactic", tout << "check sat result " << r << "\n";); proof* pr = local_solver->get_proof(); if (pr) in->set(proof2proof_converter(m, pr)); local_solver->collect_statistics(m_st); switch (r) { case l_true: if (in->models_enabled()) { model_ref mdl; local_solver->get_model(mdl); model_converter_ref mc; mc = model2model_converter(mdl.get()); mc = concat(fmc.get(), mc.get()); mc = concat(local_solver->mc0(), mc.get()); in->add(mc.get()); } in->reset(); result.push_back(in.get()); break; case l_false: { in->reset(); expr_dependency_ref lcore(m); if (in->unsat_core_enabled()) { expr_ref_vector core(m); local_solver->get_unsat_core(core); for (expr* c : core) { lcore = m.mk_join(lcore, m.mk_leaf(bool2dep.find(c))); } } in->assert_expr(m.mk_false(), pr, lcore); result.push_back(in.get()); in->set(dependency_converter::unit(lcore)); break; } case l_undef: if (m.canceled()) { throw tactic_exception(Z3_CANCELED_MSG); } model_converter_ref mc; mc = local_solver->get_model_converter(); mc = concat(fmc.get(), mc.get()); in->reset(); in->add(mc.get()); unsigned sz = local_solver->get_num_assertions(); for (unsigned i = 0; i < sz; ++i) { in->assert_expr(local_solver->get_assertion(i)); } result.push_back(in.get()); break; } } void collect_statistics(statistics & st) const override { st.copy(m_st); } void reset_statistics() override { m_st.reset(); } void cleanup() override { } void reset() override { cleanup(); } void set_logic(symbol const & l) override {} void set_progress_callback(progress_callback * callback) override { m_solver->set_progress_callback(callback); } tactic * translate(ast_manager & m) override { return alloc(solver2tactic, m_solver->translate(m, m_params)); } }; tactic* mk_solver2tactic(solver* s) { return alloc(solver2tactic, s); } z3-z3-4.8.7/src/solver/solver2tactic.h000066400000000000000000000010601356505360400174620ustar00rootroot00000000000000/*++ Copyright (c) 2016 Microsoft Corporation Module Name: solver2tactic.h Abstract: Convert solver to a tactic. Author: Nikolaj Bjorner (nbjorner) 2016-10-17 Notes: --*/ #ifndef SOLVER2TACTIC_H_ #define SOLVER2TACTIC_H_ #include "tactic/tactic.h" #include "tactic/generic_model_converter.h" class solver; tactic * mk_solver2tactic(solver* s); void extract_clauses_and_dependencies(goal_ref const& g, expr_ref_vector& clauses, ptr_vector& assumptions, obj_map& bool2dep, ref& fmc); #endif z3-z3-4.8.7/src/solver/solver_na2as.cpp000066400000000000000000000057541356505360400176450ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: solver_na2as.cpp Abstract: Solver that implements "named" assertions using assumptions (aka answer literals). That is, a named assertion assert_expr(t, a) is mapped into a implies t and 'a' is used as an extra assumption for check_sat. Author: Leonardo (leonardo) 2012-11-02 Notes: --*/ #include "solver/solver_na2as.h" #include "ast/ast_smt2_pp.h" solver_na2as::solver_na2as(ast_manager & m): m(m), m_assumptions(m) { } solver_na2as::~solver_na2as() {} void solver_na2as::assert_expr_core2(expr * t, expr * a) { if (a == nullptr) { assert_expr_core(t); } else { SASSERT(is_uninterp_const(a)); SASSERT(m.is_bool(a)); TRACE("solver_na2as", tout << "asserting\n" << mk_ismt2_pp(t, m) << "\n" << mk_ismt2_pp(a, m) << "\n";); m_assumptions.push_back(a); expr_ref new_t(m); new_t = m.mk_implies(a, t); assert_expr_core(new_t); } } struct append_assumptions { expr_ref_vector & m_assumptions; unsigned m_old_sz; append_assumptions(expr_ref_vector & _m_assumptions, unsigned num_assumptions, expr * const * assumptions): m_assumptions(_m_assumptions) { m_old_sz = m_assumptions.size(); m_assumptions.append(num_assumptions, assumptions); } ~append_assumptions() { m_assumptions.shrink(m_old_sz); } }; 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_core2(m_assumptions.size(), m_assumptions.c_ptr()); } lbool solver_na2as::check_sat_cc(const expr_ref_vector &assumptions, vector const &clauses) { if (clauses.empty()) return check_sat(assumptions.size(), assumptions.c_ptr()); append_assumptions app(m_assumptions, assumptions.size(), assumptions.c_ptr()); return check_sat_cc_core(m_assumptions, clauses); } lbool solver_na2as::get_consequences(expr_ref_vector const& asms, expr_ref_vector const& vars, expr_ref_vector& consequences) { append_assumptions app(m_assumptions, asms.size(), asms.c_ptr()); return get_consequences_core(m_assumptions, vars, consequences); } lbool solver_na2as::find_mutexes(expr_ref_vector const& vars, vector& mutexes) { return l_true; } void solver_na2as::push() { m_scopes.push_back(m_assumptions.size()); push_core(); } void solver_na2as::pop(unsigned n) { if (n > 0) { unsigned lvl = m_scopes.size(); n = std::min(lvl, n); pop_core(n); unsigned new_lvl = lvl - n; restore_assumptions(m_scopes[new_lvl]); m_scopes.shrink(new_lvl); } } void solver_na2as::restore_assumptions(unsigned old_sz) { m_assumptions.shrink(old_sz); } unsigned solver_na2as::get_scope_level() const { return m_scopes.size(); } z3-z3-4.8.7/src/solver/solver_na2as.h000066400000000000000000000035741356505360400173100ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: solver_na2as.h Abstract: Solver that implements "named" assertions using assumptions (aka answer literals). That is, a named assertion assert_expr(t, a) is mapped into a implies t and 'a' is used as an extra assumption for check_sat. Author: Leonardo (leonardo) 2012-11-02 Notes: --*/ #ifndef SOLVER_NA2AS_H_ #define SOLVER_NA2AS_H_ #include "solver/solver.h" class solver_na2as : public solver { protected: ast_manager & m; expr_ref_vector m_assumptions; unsigned_vector m_scopes; void restore_assumptions(unsigned old_sz); public: solver_na2as(ast_manager & m); ~solver_na2as() override; void assert_expr_core2(expr * t, expr * a) override; // Subclasses of solver_na2as should redefine the following *_core methods instead of these ones. 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; unsigned get_scope_level() const override; unsigned get_num_assumptions() const override { return m_assumptions.size(); } expr * get_assumption(unsigned idx) const override { return m_assumptions[idx]; } 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_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; }; #endif z3-z3-4.8.7/src/solver/solver_params.pyg000066400000000000000000000011321356505360400201230ustar00rootroot00000000000000 def_module_params('solver', description='solver parameters', export=True, params=(('enforce_model_conversion', BOOL, False, "apply model transformation on new assertions"), ('smtlib2_log', SYMBOL, '', "file to save solver interaction"), ('cancel_backup_file', SYMBOL, '', "file to save partial search state if search is canceled"), ('timeout', UINT, UINT_MAX, "timeout on the solver object; overwrites a global timeout"), )) z3-z3-4.8.7/src/solver/solver_pool.cpp000066400000000000000000000311711356505360400176020ustar00rootroot00000000000000/** Copyright (c) 2017 Microsoft Corporation Module Name: solver_pool.cpp Abstract: Maintain a pool of solvers Author: Nikolaj Bjorner Notes: --*/ #include "solver/solver_pool.h" #include "solver/solver_na2as.h" #include "ast/proofs/proof_utils.h" #include "ast/ast_util.h" class pool_solver : public solver_na2as { solver_pool& m_pool; app_ref m_pred; proof_ref m_proof; ref m_base; expr_ref_vector m_assertions; unsigned m_head; expr_ref_vector m_flat; bool m_pushed; bool m_in_delayed_scope; bool m_dump_benchmarks; double m_dump_threshold; unsigned m_dump_counter; bool is_virtual() const { return !m.is_true(m_pred); } public: pool_solver(solver* b, solver_pool& pool, app_ref& pred): solver_na2as(pred.get_manager()), m_pool(pool), m_pred(pred), m_proof(m), m_base(b), m_assertions(m), m_head(0), m_flat(m), m_pushed(false), m_in_delayed_scope(false), m_dump_benchmarks(false), m_dump_threshold(5.0), m_dump_counter(0) { if (is_virtual()) { solver_na2as::assert_expr_core2(m.mk_true(), pred); } updt_params(m_base->get_params()); } ~pool_solver() override { if (m_pushed) pop(get_scope_level()); if (is_virtual()) { m_pred = m.mk_not(m_pred); m_base->assert_expr(m_pred); } } solver* base_solver() { return m_base.get(); } solver* translate(ast_manager& m, params_ref const& p) override { UNREACHABLE(); return nullptr; } void updt_params(params_ref const& p) override { solver::updt_params(p); m_base->updt_params(p); m_dump_benchmarks = solver::get_params().get_bool("dump_benchmarks", false); m_dump_threshold = solver::get_params().get_double("dump_threshold", 5.0); } void push_params() override {m_base->push_params();} void pop_params() override {m_base->pop_params();} void collect_param_descrs(param_descrs & r) override { m_base->collect_param_descrs(r); } void collect_statistics(statistics & st) const override { m_base->collect_statistics(st); } unsigned get_num_assertions() const override { return m_base->get_num_assertions(); } expr * get_assertion(unsigned idx) const override { return m_base->get_assertion(idx); } void get_unsat_core(expr_ref_vector& r) override { m_base->get_unsat_core(r); unsigned j = 0; for (unsigned i = 0; i < r.size(); ++i) if (m_pred != r.get(i)) r[j++] = r.get(i); r.shrink(j); } unsigned get_num_assumptions() const override { unsigned sz = solver_na2as::get_num_assumptions(); return is_virtual() ? sz - 1 : sz; } proof * get_proof() override { scoped_watch _t_(m_pool.m_proof_watch); if (!m_proof.get()) { m_proof = m_base->get_proof(); if (m_proof) { elim_aux_assertions pc(m_pred); pc(m, m_proof, m_proof); } } return m_proof; } void internalize_assertions() { SASSERT(!m_pushed || m_head == m_assertions.size()); for (unsigned sz = m_assertions.size(); m_head < sz; ++m_head) { expr_ref f(m); f = m.mk_implies(m_pred, (m_assertions.get(m_head))); m_base->assert_expr(f); } } 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(); } 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); m_pool.m_stats.m_num_checks++; stopwatch sw; sw.start(); internalize_assertions(); lbool res = m_base->check_sat(num_assumptions, assumptions); sw.stop(); switch (res) { case l_true: m_pool.m_check_sat_watch.add(sw); m_pool.m_stats.m_num_sat_checks++; break; case l_undef: m_pool.m_check_undef_watch.add(sw); m_pool.m_stats.m_num_undef_checks++; break; default: break; } set_status(res); if (m_dump_benchmarks && sw.get_seconds() >= m_dump_threshold) { expr_ref_vector cube(m, num_assumptions, assumptions); vector clauses; dump_benchmark(cube, clauses, res, sw.get_seconds()); } return res; } lbool check_sat_cc_core(expr_ref_vector const & cube, vector const & clauses) override { SASSERT(!m_pushed || get_scope_level() > 0); m_proof.reset(); scoped_watch _t_(m_pool.m_check_watch); m_pool.m_stats.m_num_checks++; stopwatch sw; sw.start(); internalize_assertions(); lbool res = m_base->check_sat_cc(cube, clauses); sw.stop(); switch (res) { case l_true: m_pool.m_check_sat_watch.add(sw); m_pool.m_stats.m_num_sat_checks++; break; case l_undef: m_pool.m_check_undef_watch.add(sw); m_pool.m_stats.m_num_undef_checks++; break; default: break; } set_status(res); if (m_dump_benchmarks && sw.get_seconds() >= m_dump_threshold) { dump_benchmark(cube, clauses, res, sw.get_seconds()); } return res; } void push_core() override { SASSERT(!m_pushed || get_scope_level() > 0); if (m_in_delayed_scope) { // second push internalize_assertions(); m_base->push(); m_pushed = true; m_in_delayed_scope = false; } if (!m_pushed) { m_in_delayed_scope = true; } else { SASSERT(!m_in_delayed_scope); m_base->push(); } } void pop_core(unsigned n) override { unsigned lvl = get_scope_level(); SASSERT(!m_pushed || lvl > 0); if (m_pushed) { SASSERT(!m_in_delayed_scope); m_base->pop(n); m_pushed = lvl - n > 0; } else { m_in_delayed_scope = lvl - n > 0; } } void assert_expr_core(expr * e) override { SASSERT(!m_pushed || get_scope_level() > 0); if (m.is_true(e)) return; if (m_in_delayed_scope) { internalize_assertions(); m_base->push(); m_pushed = true; m_in_delayed_scope = false; } if (m_pushed) { m_base->assert_expr(e); } else { m_flat.push_back(e); flatten_and(m_flat); m_assertions.append(m_flat); m_flat.reset(); } } void get_model_core(model_ref & _m) override { m_base->get_model_core(_m); } expr * get_assumption(unsigned idx) const override { return solver_na2as::get_assumption(idx + is_virtual()); } std::string reason_unknown() const override { return m_base->reason_unknown(); } void set_reason_unknown(char const* msg) override { return m_base->set_reason_unknown(msg); } void get_labels(svector & r) override { return m_base->get_labels(r); } void set_progress_callback(progress_callback * callback) override { m_base->set_progress_callback(callback); } expr_ref_vector cube(expr_ref_vector& vars, unsigned ) override { return expr_ref_vector(m); } ast_manager& get_manager() const override { return m_base->get_manager(); } void refresh(solver* new_base) { SASSERT(!m_pushed); m_head = 0; m_base = new_base; } void reset() { SASSERT(!m_pushed); m_head = 0; m_assertions.reset(); m_pool.refresh(m_base.get()); } private: void dump_benchmark(const expr_ref_vector &cube, vector const & clauses, lbool last_status, double last_time) { std::string file_name = mk_file_name(); std::ofstream out(file_name); if (!out) { IF_VERBOSE(0, verbose_stream() << "could not open file " << file_name << " for output\n"); return; } out << "(set-info :status " << lbool2status(last_status) << ")\n"; m_base->display(out, cube.size(), cube.c_ptr()); for (auto const& clause : clauses) { out << ";; extra clause\n"; out << "(assert (or "; for (auto *lit : clause) out << mk_pp(lit, m) << " "; out << "))\n"; } out << "(check-sat"; for (auto * lit : cube) out << " " << mk_pp(lit, m); out << ")\n"; out << "(exit)\n"; ::statistics st; m_base->collect_statistics(st); st.update("time", last_time); st.display_smt2(out); out.close(); } char const* lbool2status(lbool r) const { switch (r) { case l_true: return "sat"; case l_false: return "unsat"; case l_undef: return "unknown"; } return "?"; } std::string mk_file_name() { std::stringstream file_name; file_name << "pool_solver"; if (is_virtual()) file_name << "_" << m_pred->get_decl()->get_name(); file_name << "_" << (m_dump_counter++) << ".smt2"; return file_name.str(); } }; solver_pool::solver_pool(solver* base_solver, unsigned num_pools): m_base_solver(base_solver), m_num_pools(num_pools), m_current_pool(0) { SASSERT(num_pools > 0); } ptr_vector solver_pool::get_base_solvers() const { ptr_vector solvers; for (solver* s0 : m_solvers) { pool_solver* s = dynamic_cast(s0); if (!solvers.contains(s->base_solver())) { solvers.push_back(s->base_solver()); } } return solvers; } void solver_pool::updt_params(const params_ref &p) { m_base_solver->updt_params(p); for (solver *s : m_solvers) s->updt_params(p); } void solver_pool::collect_statistics(statistics &st) const { ptr_vector solvers = get_base_solvers(); for (solver* s : solvers) s->collect_statistics(st); st.update("time.pool_solver.smt.total", m_check_watch.get_seconds()); st.update("time.pool_solver.smt.total.sat", m_check_sat_watch.get_seconds()); st.update("time.pool_solver.smt.total.undef", m_check_undef_watch.get_seconds()); st.update("time.pool_solver.proof", m_proof_watch.get_seconds()); st.update("pool_solver.checks", m_stats.m_num_checks); st.update("pool_solver.checks.sat", m_stats.m_num_sat_checks); st.update("pool_solver.checks.undef", m_stats.m_num_undef_checks); } void solver_pool::reset_statistics() { #if 0 ptr_vector solvers = get_base_solvers(); for (solver* s : solvers) { s->reset_statistics(); } #endif m_stats.reset(); m_check_sat_watch.reset(); m_check_undef_watch.reset(); m_check_watch.reset(); m_proof_watch.reset(); } /** \brief Create a fresh solver instance. The first num_pools solvers are independent and use a fresh instance of the base solver. Subsequent solvers reuse the first num_polls base solvers, rotating among the first num_pools. */ solver* solver_pool::mk_solver() { ref base_solver; ast_manager& m = m_base_solver->get_manager(); if (m_solvers.size() < m_num_pools) { base_solver = m_base_solver->translate(m, m_base_solver->get_params()); } else { solver* s = m_solvers[(m_current_pool++) % m_num_pools]; base_solver = dynamic_cast(s)->base_solver(); } std::stringstream name; name << "vsolver#" << m_solvers.size(); app_ref pred(m.mk_const(symbol(name.str().c_str()), m.mk_bool_sort()), m); pool_solver* solver = alloc(pool_solver, base_solver.get(), *this, pred); m_solvers.push_back(solver); return solver; } void solver_pool::reset_solver(solver* s) { pool_solver* ps = dynamic_cast(s); SASSERT(ps); if (ps) ps->reset(); } void solver_pool::refresh(solver* base_solver) { ast_manager& m = m_base_solver->get_manager(); ref new_base = m_base_solver->translate(m, m_base_solver->get_params()); for (solver* s0 : m_solvers) { pool_solver* s = dynamic_cast(s0); if (base_solver == s->base_solver()) { s->refresh(new_base.get()); } } } z3-z3-4.8.7/src/solver/solver_pool.h000066400000000000000000000026221356505360400172460ustar00rootroot00000000000000/** Copyright (c) 2017 Microsoft Corporation Module Name: solver_pool.h Abstract: Maintain a pool of solvers Author: Nikolaj Bjorner Arie Gurfinkel Notes: This is a revision of spacer_virtual_solver consolidated with spacer_smt_context_manager by Arie Gurfinkel Use this module as a replacement for spacer_smt_context_manager. --*/ #ifndef SOLVER_POOL_H_ #define SOLVER_POOL_H_ #include "solver/solver.h" #include "util/stopwatch.h" class pool_solver; class solver_pool { friend class pool_solver; struct stats { unsigned m_num_checks; unsigned m_num_sat_checks; unsigned m_num_undef_checks; stats() { reset(); } void reset() { memset(this, 0, sizeof(*this)); } }; ref m_base_solver; unsigned m_num_pools; unsigned m_current_pool; sref_vector m_solvers; stats m_stats; stopwatch m_check_watch; stopwatch m_check_sat_watch; stopwatch m_check_undef_watch; stopwatch m_proof_watch; void refresh(solver* s); ptr_vector get_base_solvers() const; public: solver_pool(solver* base_solver, unsigned num_pools); void collect_statistics(statistics &st) const; void reset_statistics(); solver* mk_solver(); void reset_solver(solver* s); void updt_params(const params_ref &p); }; #endif z3-z3-4.8.7/src/solver/tactic2solver.cpp000066400000000000000000000236621356505360400200310ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: tactic2solver.cpp Abstract: Wrapper for implementing the solver interface using a tactic. This is a light version of the strategic solver. Author: Leonardo (leonardo) 2012-01-23 Notes: --*/ #include "solver/tactic2solver.h" #include "solver/solver_na2as.h" #include "tactic/tactic.h" #include "ast/ast_translation.h" #include "solver/mus.h" /** \brief Simulates the incremental solver interface using a tactic. Every query will be solved from scratch. So, this is not a good option for applications trying to solve many easy queries that a similar to each other. */ namespace { class tactic2solver : public solver_na2as { expr_ref_vector m_assertions; unsigned_vector m_scopes; ref m_result; tactic_ref m_tactic; ref m_mc; symbol m_logic; bool m_produce_models; bool m_produce_proofs; bool m_produce_unsat_cores; statistics m_stats; public: tactic2solver(ast_manager & m, tactic * t, params_ref const & p, bool produce_proofs, bool produce_models, bool produce_unsat_cores, symbol const & logic); ~tactic2solver() override; solver* translate(ast_manager& m, params_ref const& p) override; void updt_params(params_ref const & p) override; void collect_param_descrs(param_descrs & r) override; void set_produce_models(bool f) override { m_produce_models = f; } void assert_expr_core(expr * t) override; ast_manager& get_manager() const override; void push_core() override; void pop_core(unsigned n) 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; void get_model_core(model_ref & m) override; proof * get_proof() override; std::string reason_unknown() const override; void set_reason_unknown(char const* msg) override; void get_labels(svector & r) override {} void set_progress_callback(progress_callback * callback) override {} unsigned get_num_assertions() const override; expr * get_assertion(unsigned idx) const override; expr_ref_vector cube(expr_ref_vector& vars, unsigned ) override { set_reason_unknown("cubing is not supported on tactics"); IF_VERBOSE(1, verbose_stream() << "cubing is not supported on tactics\n"); return expr_ref_vector(get_manager()); } 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"); } }; ast_manager& tactic2solver::get_manager() const { return m_assertions.get_manager(); } tactic2solver::tactic2solver(ast_manager & m, tactic * t, params_ref const & p, bool produce_proofs, bool produce_models, bool produce_unsat_cores, symbol const & logic): solver_na2as(m), m_assertions(m) { m_tactic = t; m_logic = logic; solver::updt_params(p); m_produce_models = produce_models; m_produce_proofs = produce_proofs; m_produce_unsat_cores = produce_unsat_cores; } tactic2solver::~tactic2solver() { } void tactic2solver::updt_params(params_ref const & p) { solver::updt_params(p); } void tactic2solver::collect_param_descrs(param_descrs & r) { if (m_tactic.get()) m_tactic->collect_param_descrs(r); } void tactic2solver::assert_expr_core(expr * t) { m_assertions.push_back(t); m_result = nullptr; } void tactic2solver::push_core() { m_scopes.push_back(m_assertions.size()); m_result = nullptr; TRACE("pop", tout << m_scopes.size() << "\n";); } void tactic2solver::pop_core(unsigned n) { TRACE("pop", tout << m_scopes.size() << " " << n << "\n";); n = std::min(m_scopes.size(), n); unsigned new_lvl = m_scopes.size() - n; unsigned old_sz = m_scopes[new_lvl]; m_assertions.shrink(old_sz); m_scopes.shrink(new_lvl); m_result = nullptr; } 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(); m_result = alloc(simple_check_sat_result, m); m_tactic->cleanup(); m_tactic->set_logic(m_logic); m_tactic->updt_params(get_params()); // parameters are allowed to overwrite logic. goal_ref g = alloc(goal, m, m_produce_proofs, m_produce_models, m_produce_unsat_cores); for (expr* e : m_assertions) { g->assert_expr(e); } for (unsigned i = 0; i < num_assumptions; i++) { proof_ref pr(m.mk_asserted(assumptions[i]), m); expr_dependency_ref ans(m.mk_leaf(assumptions[i]), m); g->assert_expr(assumptions[i], pr, ans); } model_ref md; proof_ref pr(m); expr_dependency_ref core(m); std::string reason_unknown = "unknown"; labels_vec labels; try { switch (::check_sat(*m_tactic, g, md, labels, pr, core, reason_unknown)) { case l_true: m_result->set_status(l_true); break; case l_false: m_result->set_status(l_false); break; default: m_result->set_status(l_undef); if (!reason_unknown.empty()) m_result->m_unknown = reason_unknown; if (num_assumptions == 0 && m_scopes.empty()) { m_assertions.reset(); g->get_formulas(m_assertions); } break; } m_mc = g->mc(); TRACE("tactic", if (m_mc) m_mc->display(tout);); } catch (z3_error & ex) { TRACE("tactic2solver", tout << "exception: " << ex.msg() << "\n";); m_result->m_proof = pr; throw ex; } catch (z3_exception & ex) { TRACE("tactic2solver", tout << "exception: " << ex.msg() << "\n";); m_result->set_status(l_undef); m_result->m_unknown = ex.msg(); m_result->m_proof = pr; } m_tactic->collect_statistics(m_result->m_stats); m_tactic->collect_statistics(m_stats); m_result->m_model = md; m_result->m_proof = pr; if (m_produce_unsat_cores) { ptr_vector core_elems; m.linearize(core, core_elems); m_result->m_core.append(core_elems.size(), core_elems.c_ptr()); } m_tactic->cleanup(); return m_result->status(); } solver* tactic2solver::translate(ast_manager& m, params_ref const& p) { tactic* t = m_tactic->translate(m); tactic2solver* r = alloc(tactic2solver, m, t, p, m_produce_proofs, m_produce_models, m_produce_unsat_cores, m_logic); r->m_result = nullptr; if (!m_scopes.empty()) { throw default_exception("translation of contexts is only supported at base level"); } ast_translation tr(m_assertions.get_manager(), m, false); for (unsigned i = 0; i < get_num_assertions(); ++i) { r->m_assertions.push_back(tr(get_assertion(i))); } return r; } void tactic2solver::collect_statistics(statistics & st) const { st.copy(m_stats); //SASSERT(m_stats.size() > 0); } void tactic2solver::get_unsat_core(expr_ref_vector & r) { if (m_result.get()) { m_result->get_unsat_core(r); } } void tactic2solver::get_model_core(model_ref & m) { if (m_result.get()) { m_result->get_model_core(m); } } proof * tactic2solver::get_proof() { if (m_result.get()) return m_result->get_proof(); else return nullptr; } std::string tactic2solver::reason_unknown() const { if (m_result.get()) return m_result->reason_unknown(); else return std::string("unknown"); } void tactic2solver::set_reason_unknown(char const* msg) { if (m_result.get()) { m_result->set_reason_unknown(msg); } } unsigned tactic2solver::get_num_assertions() const { return m_assertions.size(); } expr * tactic2solver::get_assertion(unsigned idx) const { return m_assertions.get(idx); } } solver * mk_tactic2solver(ast_manager & m, tactic * t, params_ref const & p, bool produce_proofs, bool produce_models, bool produce_unsat_cores, symbol const & logic) { return alloc(tactic2solver, m, t, p, produce_proofs, produce_models, produce_unsat_cores, logic); } namespace { class tactic2solver_factory : public solver_factory { ref m_tactic; public: tactic2solver_factory(tactic * t):m_tactic(t) { } ~tactic2solver_factory() override {} solver * operator()(ast_manager & m, params_ref const & p, bool proofs_enabled, bool models_enabled, bool unsat_core_enabled, symbol const & logic) override { return mk_tactic2solver(m, m_tactic.get(), p, proofs_enabled, models_enabled, unsat_core_enabled, logic); } }; class tactic_factory2solver_factory : public solver_factory { tactic_factory m_factory; public: tactic_factory2solver_factory(tactic_factory f):m_factory(f) { } solver * operator()(ast_manager & m, params_ref const & p, bool proofs_enabled, bool models_enabled, bool unsat_core_enabled, symbol const & logic) override { tactic * t = (*m_factory)(m, p); return mk_tactic2solver(m, t, p, proofs_enabled, models_enabled, unsat_core_enabled, logic); } }; } solver_factory * mk_tactic2solver_factory(tactic * t) { return alloc(tactic2solver_factory, t); } solver_factory * mk_tactic_factory2solver_factory(tactic_factory f) { return alloc(tactic_factory2solver_factory, f); } z3-z3-4.8.7/src/solver/tactic2solver.h000066400000000000000000000020031356505360400174600ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: tactic2solver.h Abstract: Wrapper for implementing the external solver interface using a tactic. This is a light version of the strategic solver. Author: Leonardo (leonardo) 2012-01-23 Notes: --*/ #ifndef TACTIC2SOLVER_H_ #define TACTIC2SOLVER_H_ #include "util/params.h" class ast_manager; class tactic; class solver; class solver_factory; typedef tactic* (*tactic_factory)(ast_manager&, const params_ref&); solver * mk_tactic2solver(ast_manager & m, tactic * t = nullptr, params_ref const & p = params_ref(), bool produce_proofs = false, bool produce_models = true, bool produce_unsat_cores = false, symbol const & logic = symbol::null); solver_factory * mk_tactic2solver_factory(tactic * t); solver_factory * mk_tactic_factory2solver_factory(tactic_factory f); #endif z3-z3-4.8.7/src/tactic/000077500000000000000000000000001356505360400144655ustar00rootroot00000000000000z3-z3-4.8.7/src/tactic/CMakeLists.txt000066400000000000000000000010171356505360400172240ustar00rootroot00000000000000z3_add_component(tactic SOURCES dependency_converter.cpp equiv_proof_converter.cpp generic_model_converter.cpp goal.cpp goal_num_occurs.cpp goal_shared_occs.cpp goal_util.cpp horn_subsume_model_converter.cpp model_converter.cpp probe.cpp proof_converter.cpp replace_proof_converter.cpp sine_filter.cpp tactical.cpp tactic.cpp COMPONENT_DEPENDENCIES ast model TACTIC_HEADERS probe.h sine_filter.h tactic.h PYG_FILES tactic_params.pyg ) z3-z3-4.8.7/src/tactic/aig/000077500000000000000000000000001356505360400152255ustar00rootroot00000000000000z3-z3-4.8.7/src/tactic/aig/CMakeLists.txt000066400000000000000000000002151356505360400177630ustar00rootroot00000000000000z3_add_component(aig_tactic SOURCES aig.cpp aig_tactic.cpp COMPONENT_DEPENDENCIES tactic TACTIC_HEADERS aig_tactic.h ) z3-z3-4.8.7/src/tactic/aig/aig.cpp000066400000000000000000001562721356505360400165060ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: aig.cpp Abstract: And-inverted graphs Author: Leonardo (leonardo) 2011-05-13 Notes: --*/ #include "tactic/aig/aig.h" #include "tactic/goal.h" #include "ast/ast_smt2_pp.h" #define USE_TWO_LEVEL_RULES #define FIRST_NODE_ID (UINT_MAX/2) struct aig; class aig_lit { friend class aig_ref; aig * m_ref; public: aig_lit(aig * n = nullptr):m_ref(n) {} aig_lit(aig_ref const & r):m_ref(static_cast(r.m_ref)) {} bool is_inverted() const { return (reinterpret_cast(m_ref) & static_cast(1)) == static_cast(1); } void invert() { m_ref = reinterpret_cast(reinterpret_cast(m_ref) ^ static_cast(1)); } aig * ptr() const { return reinterpret_cast(reinterpret_cast(m_ref) & ~static_cast(1)); } aig * ptr_non_inverted() const { SASSERT(!is_inverted()); return m_ref; } bool is_null() const { return m_ref == nullptr; } friend bool operator==(aig_lit const & r1, aig_lit const & r2) { return r1.m_ref == r2.m_ref; } friend bool operator!=(aig_lit const & r1, aig_lit const & r2) { return r1.m_ref != r2.m_ref; } aig_lit & operator=(aig_lit const & r) { m_ref = r.m_ref; return *this; } static aig_lit null; }; aig_lit aig_lit::null; struct aig { unsigned m_id; unsigned m_ref_count; aig_lit m_children[2]; unsigned m_mark:1; aig() {} }; #if Z3DEBUG inline bool is_true(aig_lit const & r) { return !r.is_inverted() && r.ptr_non_inverted()->m_id == 0; } #endif // inline bool is_false(aig_lit const & r) { return r.is_inverted() && r.ptr()->m_id == 0; } inline bool is_var(aig * n) { return n->m_children[0].is_null(); } inline bool is_var(aig_lit const & n) { return is_var(n.ptr()); } inline unsigned id(aig_lit const & n) { return n.ptr()->m_id; } inline unsigned ref_count(aig_lit const & n) { return n.ptr()->m_ref_count; } inline aig_lit left(aig * n) { return n->m_children[0]; } inline aig_lit right(aig * n) { return n->m_children[1]; } inline aig_lit left(aig_lit const & n) { return left(n.ptr()); } inline aig_lit right(aig_lit const & n) { return right(n.ptr()); } inline unsigned to_idx(aig * p) { SASSERT(!is_var(p)); return p->m_id - FIRST_NODE_ID; } static void unmark(unsigned sz, aig * const * ns) { for (unsigned i = 0; i < sz; i++) { ns[i]->m_mark = false; } } struct aig_hash { unsigned operator()(aig * n) const { SASSERT(!is_var(n)); return hash_u_u(id(n->m_children[0]), id(n->m_children[1])); } }; struct aig_eq { bool operator()(aig * n1, aig * n2) const { SASSERT(!is_var(n1)); SASSERT(!is_var(n2)); return n1->m_children[0] == n2->m_children[0] && n1->m_children[1] == n2->m_children[1]; } }; class aig_table : public chashtable { public: }; struct aig_lit_lt { bool operator()(aig_lit const & l1, aig_lit const & l2) const { if (id(l1) < id(l2)) return true; if (id(l1) == id(l2)) return l1.is_inverted() && !l2.is_inverted(); return false; } }; struct aig_manager::imp { id_gen m_var_id_gen; id_gen m_node_id_gen; aig_table m_table; unsigned m_num_aigs; expr_ref_vector m_var2exprs; small_object_allocator m_allocator; ptr_vector m_to_delete; aig_lit m_true; aig_lit m_false; bool m_default_gate_encoding; unsigned long long m_max_memory; void dec_ref_core(aig * n) { SASSERT(n->m_ref_count > 0); n->m_ref_count--; if (n->m_ref_count == 0) m_to_delete.push_back(n); } void checkpoint() { if (memory::get_allocation_size() > m_max_memory) throw aig_exception(TACTIC_MAX_MEMORY_MSG); if (m().canceled()) throw aig_exception(m().limit().get_cancel_msg()); } void dec_ref_core(aig_lit const & r) { dec_ref_core(r.ptr()); } void dec_ref_result(aig * n) { SASSERT(n->m_ref_count > 0); n->m_ref_count--; } void dec_ref_result(aig_lit const & r) { dec_ref_result(r.ptr()); } void process_to_delete() { while (!m_to_delete.empty()) { aig * n = m_to_delete.back(); m_to_delete.pop_back(); delete_node(n); } } void delete_node(aig * n) { TRACE("aig_lit_count", tout << "deleting: "; display_ref(tout, n); tout << "\n";); SASSERT(m_num_aigs > 0); m_num_aigs--; if (is_var(n)) { m_var_id_gen.recycle(n->m_id); m_var2exprs.set(n->m_id, nullptr); } else { m_table.erase(n); m_node_id_gen.recycle(n->m_id); dec_ref_core(n->m_children[0]); dec_ref_core(n->m_children[1]); } m_allocator.deallocate(sizeof(aig), n); } aig * allocate_node() { return static_cast(m_allocator.allocate(sizeof(aig))); } aig * mk_var(expr * t) { m_num_aigs++; aig * r = allocate_node(); r->m_id = m_var_id_gen.mk(); r->m_ref_count = 0; r->m_mark = false; r->m_children[0] = aig_lit(); SASSERT(r->m_id <= m_var2exprs.size()); if (r->m_id == m_var2exprs.size()) m_var2exprs.push_back(t); else m_var2exprs.set(r->m_id, t); return r; } aig_lit mk_node_core(aig_lit const & l, aig_lit const & r) { aig * new_node = allocate_node(); new_node->m_children[0] = l; new_node->m_children[1] = r; aig * old_node = m_table.insert_if_not_there(new_node); if (old_node != new_node) { m_allocator.deallocate(sizeof(aig), new_node); return aig_lit(old_node); } m_num_aigs++; new_node->m_id = m_node_id_gen.mk(); new_node->m_ref_count = 0; new_node->m_mark = false; SASSERT(new_node->m_ref_count == 0); inc_ref(l); inc_ref(r); return aig_lit(new_node); } bool is_not_eq(aig_lit const & l, aig_lit const & r) const { return l.ptr() == r.ptr() && l.is_inverted() != r.is_inverted(); } /** \brief Create an AIG representing (l and r) Apply two-level minimization rules that guarantee that locally the size is decreasing, and globally is not increasing. */ aig_lit mk_node(aig_lit l, aig_lit r) { start: bool sign1 = l.is_inverted(); aig * n1 = l.ptr(); bool sign2 = r.is_inverted(); aig * n2 = r.ptr(); if (n1->m_id == 0) { if (sign1) return m_false; // false and r else return r; // true and r } if (n2->m_id == 0) { if (sign2) return m_false; // l and false; else return l; // l and true; } if (n1 == n2) { if (sign1 == sign2) return l; // l and l; else return m_false; // l and not l } #ifdef USE_TWO_LEVEL_RULES if (!is_var(n1)) { aig_lit a = n1->m_children[0]; aig_lit b = n1->m_children[1]; if (is_not_eq(a, r) || is_not_eq(b, r)) { if (sign1) { // substitution return r; // (not (a and b)) and r --> r IF a = (not r) or b = (not r) } else { // contradiction return m_false; // (a and b) and r --> false IF a = (not r) or b = (not r) } } if (a == r) { if (sign1) { // substitution // not (a and b) and r --> (not b) and r IF a == r l = b; l.invert(); goto start; } else { // substitution return l; // (a and b) and r --> (a and b) IF a = r } } if (b == r) { if (sign1) { // substitution // not (a and b) and r --> (not a) and r IF b == r l = a; l.invert(); goto start; } else { // substitution return l; // (a and b) and r --> (a and b) IF b = r; } } if (!is_var(n2)) { aig_lit c = n2->m_children[0]; aig_lit d = n2->m_children[1]; if (!sign1 && !sign2) { // contradiction if (is_not_eq(a, c) || is_not_eq(a, d) || is_not_eq(b, c) || is_not_eq(b, d)) return m_false; // (a and b) and (c and d) --> false IF a = not(c) OR a = not(d) or b = not(c) or b = not(d) // idempotence if (a == c || b == c) { r = d; // (a and b) and (c and d) --> (a and b) and d IF a == c or b == c goto start; } // idempotence if (b == c || b == d) { l = a; // (a and b) and (c and d) --> a and (c and d) IF b == c or b == d goto start; } // idempotence if (a == d || b == d) { r = c; goto start; // (a and b) and (c and d) --> (a and b) and c IF a == d or b == d } // idempotence if (a == c || a == d) { l = b; // (a and b) and (c and d) --> b and (c and d) IF a == c or a == d goto start; } } if (sign1 && !sign2) { // subsumption if (is_not_eq(a, c) || is_not_eq(a, d) || is_not_eq(b, c) || is_not_eq(b, d)) return r; // not (a and b) and (c and d) --> (c and d) // substitution if (b == c || b == d) { // not (a and b) and (c and d) --> (not a) and (c and d) a.invert(); l = a; goto start; } // substitution if (a == c || a == d) { // not (a and b) and (c and d) --> (not b) and (c and d) b.invert(); l = b; goto start; } } if (!sign1 && sign2) { // subsumption if (is_not_eq(a, c) || is_not_eq(a, d) || is_not_eq(b, c) || is_not_eq(b, d)) return l; // (a and b) and not (c and d) --> (a and b) // substitution if (c == a || c == b) { // (a and b) and not (c and d) --> (a and b) and (not d); d.invert(); r = d; goto start; } // substitution if (d == a || d == b) { // (a and b) and not (c and d) --> (a and b) and (not c); c.invert(); r = c; goto start; } } if (sign1 && sign2) { // resolution if (a == c && is_not_eq(b, d)) { a.invert(); // (not (a and b)) and (not (a and (not b))) --> not a return a; } SASSERT(!(a == d && is_not_eq(b, c))); // cannot happen because we sort args // resolution if (is_not_eq(a, c) && b == d) { b.invert(); // (not (a and b)) and (not (a and (not b))) --> not b return b; } SASSERT(!(is_not_eq(a, d) && b == c)); // cannot happen because we sort args } } } if (!is_var(n2)) { aig_lit a = n2->m_children[0]; aig_lit b = n2->m_children[1]; if (is_not_eq(l, a) || is_not_eq(l, b)) { if (sign2) { // substitution return l; // l and (not (a and b)) --> l IF a = (not l) or b = (not l) } else { // contradiction return m_false; // l and (a and b) --> false IF a = (not l) or b = (not l) } } if (a == l) { if (sign2) { // substitution // l and not (a and b) and r --> l and (not b) IF a == l r = b; r.invert(); goto start; } else { // substitution return r; // l and (a and b) --> (a and b) IF a = l; } } if (b == l) { if (sign2) { // substitution // l and not (a and b) --> l and (not a) IF b == l r = a; r.invert(); goto start; } else { // substitution return r; // l and (a and b) --> (a and b) IF b = l; } } } #endif if (n1->m_id > n2->m_id) return mk_node_core(r, l); else return mk_node_core(l, r); } struct expr2aig { struct frame { app * m_t; unsigned m_idx; unsigned m_spos; frame(app * t, unsigned spos):m_t(t), m_idx(0), m_spos(spos) {} }; imp & m; svector m_frame_stack; svector m_result_stack; obj_map m_cache; expr2aig(imp & _m):m(_m) {} ~expr2aig() { obj_map::iterator it = m_cache.begin(); obj_map::iterator end = m_cache.end(); for (; it != end; ++it) { TRACE("expr2aig", tout << "dec-ref: "; m.display_ref(tout, it->m_value); tout << " ref-count: " << ref_count(it->m_value) << "\n";); m.dec_ref(it->m_value); } restore_result_stack(0); } void save_result(aig_lit & r) { m.inc_ref(r); m_result_stack.push_back(r); } void cache_result(expr * t, aig_lit const & r) { TRACE("expr2aig", tout << "caching:\n" << mk_ismt2_pp(t, m.m()) << "\n---> "; m.display_ref(tout, r); tout << "\n";); SASSERT(!m_cache.contains(t)); m.inc_ref(r); m_cache.insert(t, r); } bool is_cached(expr * t) { aig_lit r; if (m_cache.find(t, r)) { save_result(r); return true; } return false; } void process_var(expr * t) { if (is_cached(t)) return; aig_lit r(m.mk_var(t)); SASSERT(ref_count(r) == 0); cache_result(t, r); save_result(r); } void mk_frame(app * t) { m_frame_stack.push_back(frame(t, m_result_stack.size())); } bool visit(expr * t) { if (is_app(t)) { app * tapp = to_app(t); if (tapp->get_family_id() == m.m().get_basic_family_id()) { switch (tapp->get_decl_kind()) { case OP_TRUE: save_result(m.m_true); return true; case OP_FALSE: save_result(m.m_false); return true; case OP_EQ: if (!m.m().is_bool(tapp->get_arg(0))) break; case OP_NOT: case OP_OR: case OP_AND: case OP_XOR: case OP_IMPLIES: case OP_ITE: if (tapp->get_ref_count() > 1 && is_cached(tapp)) return true; mk_frame(tapp); return false; default: break; } } process_var(t); return true; } else { // quantifiers and free variables are handled as aig variables process_var(t); return true; } } void restore_result_stack(unsigned old_sz) { unsigned sz = m_result_stack.size(); SASSERT(old_sz <= sz); for (unsigned i = old_sz; i < sz; i++) m.dec_ref(m_result_stack[i]); m_result_stack.shrink(old_sz); } void save_node_result(unsigned spos, aig_lit r) { m.inc_ref(r); restore_result_stack(spos); save_result(r); SASSERT(ref_count(r) >= 2); m.dec_ref(r); } void mk_or(unsigned spos) { SASSERT(spos <= m_result_stack.size()); unsigned num = m_result_stack.size() - spos; aig_lit r = m.mk_or(num, m_result_stack.begin() + spos); save_node_result(spos, r); } void mk_and(unsigned spos) { SASSERT(spos <= m_result_stack.size()); unsigned num = m_result_stack.size() - spos; aig_lit r = m.mk_and(num, m_result_stack.begin() + spos); save_node_result(spos, r); } void mk_ite(unsigned spos) { SASSERT(spos + 3 == m_result_stack.size()); aig_lit r = m.mk_ite(m_result_stack[spos], m_result_stack[spos+1], m_result_stack[spos+2]); save_node_result(spos, r); } void mk_iff(unsigned spos) { SASSERT(spos + 2 == m_result_stack.size()); aig_lit r = m.mk_iff(m_result_stack[spos], m_result_stack[spos+1]); save_node_result(spos, r); } void mk_xor(unsigned spos) { SASSERT(spos + 2 == m_result_stack.size()); aig_lit r = m.mk_xor(m_result_stack[spos], m_result_stack[spos+1]); save_node_result(spos, r); } void mk_implies(unsigned spos) { SASSERT(spos + 2 == m_result_stack.size()); aig_lit r = m.mk_implies(m_result_stack[spos], m_result_stack[spos+1]); save_node_result(spos, r); } void mk_aig(frame & fr) { SASSERT(fr.m_t->get_family_id() == m.m().get_basic_family_id()); switch (fr.m_t->get_decl_kind()) { case OP_NOT: m_result_stack[fr.m_spos].invert(); break; case OP_OR: mk_or(fr.m_spos); break; case OP_AND: mk_and(fr.m_spos); break; case OP_EQ: SASSERT(m.m().is_bool(fr.m_t->get_arg(0))); mk_iff(fr.m_spos); break; case OP_XOR: mk_xor(fr.m_spos); break; case OP_IMPLIES: mk_implies(fr.m_spos); break; case OP_ITE: mk_ite(fr.m_spos); break; default: UNREACHABLE(); } if (fr.m_t->get_ref_count() > 1) { cache_result(fr.m_t, m_result_stack.back()); } } aig_lit operator()(expr * n) { SASSERT(check_cache()); if (!visit(n)) { while (!m_frame_stack.empty()) { loop: m.checkpoint(); frame & fr = m_frame_stack.back(); if (fr.m_idx == 0 && fr.m_t->get_ref_count() > 1) { if (is_cached(fr.m_t)) { m_frame_stack.pop_back(); continue; } } unsigned num_args = fr.m_t->get_num_args(); while (fr.m_idx < num_args) { expr * arg = fr.m_t->get_arg(fr.m_idx); fr.m_idx++; if (!visit(arg)) goto loop; } mk_aig(fr); m_frame_stack.pop_back(); } } SASSERT(m_result_stack.size() == 1); aig_lit r = m_result_stack.back(); m_result_stack.pop_back(); m.dec_ref_result(r); SASSERT(check_cache()); return r; } bool check_cache() const { for (auto const& kv : m_cache) { VERIFY(ref_count(kv.m_value) > 0); } return true; } }; /** \brief Return true if the AIG represents an if-then-else */ template bool is_ite_core(aig * n, aig_lit & c, aig_lit & t, aig_lit & e) const { if (is_var(n)) return false; aig_lit l = left(n); aig_lit r = right(n); if (l.is_inverted() && r.is_inverted()) { aig * l_ptr = l.ptr(); aig * r_ptr = r.ptr(); if (is_var(l_ptr) || is_var(r_ptr)) return false; aig_lit l1 = left(l_ptr); aig_lit l2 = right(l_ptr); aig_lit r1 = left(r_ptr); aig_lit r2 = right(r_ptr); if (is_not_eq(l1, r1)) { if (Collect) { l1.invert(); l2.invert(); r1.invert(); r2.invert(); if (l1.is_inverted()) { c = r1; t = l2; e = r2; } else { c = l1; t = r2; e = l2; } } return true; } else if (is_not_eq(l1, r2)) { if (Collect) { l1.invert(); l2.invert(); r1.invert(); r2.invert(); if (l1.is_inverted()) { c = r2; t = l2; e = r1; } else { c = l1; t = r1; e = l2; } } return true; } else if (is_not_eq(l2, r1)) { if (Collect) { l1.invert(); l2.invert(); r1.invert(); r2.invert(); if (l2.is_inverted()) { c = r1; t = l1; e = r2; } else { c = l2; t = r2; e = l1; } } return true; } else if (is_not_eq(l2, r2)) { if (Collect) { l1.invert(); l2.invert(); r1.invert(); r2.invert(); if (l2.is_inverted()) { c = r2; t = l1; e = r1; } else { c = l2; t = r1; e = l1; } } return true; } } return false; } bool is_ite(aig * n, aig_lit & c, aig_lit & t, aig_lit & e) const { return is_ite_core(n, c, t, e); } bool is_ite(aig * n) const { static aig_lit c, t, e; return is_ite_core(n, c, t, e); } /** \brief Return true if the AIG represents an iff */ bool is_iff(aig * n) const { if (is_var(n)) return false; aig_lit l = left(n); aig_lit r = right(n); if (l.is_inverted() && r.is_inverted()) { if (is_var(l) || is_var(r)) return false; return is_not_eq(left(l), left(r)) && is_not_eq(right(l), right(r)); } return false; } expr * var2expr(aig * n) const { SASSERT(is_var(n)); return m_var2exprs[n->m_id]; } struct aig2expr { imp & m; ast_manager & ast_mng; enum kind { AIG_AND, AIG_AUX_AND, // does not have an associated expr AIG_ITE }; struct frame { aig * m_node; unsigned m_kind:2; unsigned m_first:1; frame(aig * n, kind k):m_node(n), m_kind(k), m_first(true) {} }; expr_ref_vector m_cache; svector m_frame_stack; aig2expr(imp & _m):m(_m), ast_mng(m.m()), m_cache(ast_mng) {} expr * get_cached(aig * n) { if (is_var(n)) { return n->m_id == 0 ? ast_mng.mk_true() : m.var2expr(n); } else { CTRACE("aig2expr", !is_cached(n), tout << "invalid get_cached for "; m.display_ref(tout, n); tout << "\n";); SASSERT(is_cached(n)); return m_cache.get(to_idx(n)); } } expr * invert(expr * n) { if (ast_mng.is_not(n)) return to_app(n)->get_arg(0); if (ast_mng.is_true(n)) return ast_mng.mk_false(); SASSERT(!ast_mng.is_false(n)); return ast_mng.mk_not(n); } expr * get_cached(aig_lit const & n) { if (n.is_inverted()) return invert(get_cached(n.ptr())); else return get_cached(n.ptr()); } bool is_cached(aig * n) { if (is_var(n)) return true; unsigned idx = to_idx(n); if (idx >= m_cache.size()) { m_cache.resize(idx+1); return false; } return m_cache.get(idx) != nullptr; } void cache_result(aig * n, expr * t) { unsigned idx = to_idx(n); SASSERT(idx < m_cache.size()); SASSERT(m_cache.get(idx) == 0); m_cache.set(idx, t); } void visit_and_child(aig_lit c, bool & visited) { aig * n = c.ptr(); if (is_cached(n)) return; if (m.is_ite(n)) m_frame_stack.push_back(frame(n, AIG_ITE)); else if (!c.is_inverted() && n->m_ref_count == 1) m_frame_stack.push_back(frame(n, AIG_AUX_AND)); else m_frame_stack.push_back(frame(n, AIG_AND)); visited = false; } void visit_ite_child(aig_lit c, bool & visited) { aig * n = c.ptr(); if (is_cached(n)) return; m_frame_stack.push_back(frame(n, m.is_ite(n) ? AIG_ITE : AIG_AND)); visited = false; } ptr_vector m_and_children; ptr_vector m_and_todo; void add_child(aig_lit c) { aig * n = c.ptr(); if (c.is_inverted()) { // adding (not c) since I build an OR node m_and_children.push_back(get_cached(n)); return; } if (is_cached(n)) { m_and_children.push_back(invert(get_cached(n))); return; } SASSERT(n->m_ref_count == 1); m_and_todo.push_back(n); } void mk_and(aig * n) { m_and_children.reset(); m_and_todo.reset(); add_child(left(n)); add_child(right(n)); while (!m_and_todo.empty()) { aig * t = m_and_todo.back(); SASSERT(!is_var(t)); m_and_todo.pop_back(); add_child(left(t)); add_child(right(t)); } expr * r = ast_mng.mk_not(ast_mng.mk_or(m_and_children.size(), m_and_children.c_ptr())); cache_result(n, r); TRACE("aig2expr", tout << "caching AND "; m.display_ref(tout, n); tout << "\n";); } void mk_ite(aig * n) { aig_lit c, t, e; VERIFY(m.is_ite(n, c, t, e)); if (c.is_inverted()) { c.invert(); std::swap(t, e); } expr * r; if (m.is_not_eq(t, e)) { r = ast_mng.mk_iff(get_cached(c), get_cached(t)); } else { r = ast_mng.mk_ite(get_cached(c), get_cached(t), get_cached(e)); } cache_result(n, r); TRACE("aig2expr", tout << "caching ITE/IFF "; m.display_ref(tout, n); tout << "\n";); } /** \brief Return an expression representing the negation of p. */ expr * process_root(aig * r) { if (is_cached(r)) return get_cached(r); m_frame_stack.push_back(frame(r, m.is_ite(r) ? AIG_ITE : AIG_AND)); while (!m_frame_stack.empty()) { m.checkpoint(); frame & fr = m_frame_stack.back(); aig * n = fr.m_node; if (is_cached(n)) { m_frame_stack.pop_back(); continue; } if (fr.m_first) { fr.m_first = false; bool visited = true; switch (fr.m_kind) { case AIG_AND: case AIG_AUX_AND: visit_and_child(left(n), visited); visit_and_child(right(n), visited); break; case AIG_ITE: { aig_lit a = left(left(n)); aig_lit b = right(left(n)); aig_lit c = left(right(n)); aig_lit d = right(right(n)); visit_ite_child(a, visited); visit_ite_child(b, visited); if (c.ptr() != a.ptr() && c.ptr() != b.ptr()) visit_ite_child(c, visited); if (d.ptr() != a.ptr() && d.ptr() != b.ptr()) visit_ite_child(d, visited); break; } default: UNREACHABLE(); break; } if (!visited) continue; } switch (fr.m_kind){ case AIG_AUX_AND: // do nothing TRACE("aig2expr", tout << "skipping aux AND "; m.display_ref(tout, n); tout << "\n";); break; case AIG_AND: mk_and(n); break; case AIG_ITE: mk_ite(n); break; default: UNREACHABLE(); break; } m_frame_stack.pop_back(); } return get_cached(r); } /** \brief (Debugging) Naive AIG -> EXPR */ void naive(aig_lit const & l, expr_ref & r) { expr_ref_vector cache(ast_mng); ptr_vector todo; todo.push_back(l.ptr()); while (!todo.empty()) { aig * t = todo.back(); if (is_var(t)) { todo.pop_back(); continue; } unsigned idx = to_idx(t); cache.reserve(idx+1); if (cache.get(idx) != nullptr) { todo.pop_back(); continue; } bool ok = true; for (unsigned i = 0; i < 2; i++) { aig * c = t->m_children[i].ptr(); if (!is_var(c) && cache.get(to_idx(c), nullptr) == nullptr) { todo.push_back(c); ok = false; } } if (!ok) continue; expr * args[2]; for (unsigned i = 0; i < 2; i++) { aig_lit l = t->m_children[i]; aig * c = l.ptr(); if (is_var(c)) args[i] = m.m_var2exprs.get(c->m_id); else args[i] = cache.get(to_idx(c), nullptr); if (!l.is_inverted()) args[i] = invert(args[i]); } cache.set(idx, ast_mng.mk_not(ast_mng.mk_or(args[0], args[1]))); todo.pop_back(); } aig * c = l.ptr(); if (is_var(c)) r = m.m_var2exprs.get(c->m_id); else r = cache.get(to_idx(c)); if (l.is_inverted()) r = invert(r); } void operator()(aig_lit const & l, expr_ref & r) { naive(l, r); } void operator()(aig_lit const & l, goal & g) { g.reset(); sbuffer roots; roots.push_back(l); while (!roots.empty()) { aig_lit n = roots.back(); roots.pop_back(); if (n.is_inverted()) { g.assert_expr(invert(process_root(n.ptr())), nullptr, nullptr); continue; } aig * p = n.ptr(); if (m.is_ite(p)) { g.assert_expr(process_root(p), nullptr, nullptr); continue; } if (is_var(p)) { g.assert_expr(m.var2expr(p), nullptr, nullptr); continue; } roots.push_back(left(p)); roots.push_back(right(p)); } } }; struct max_sharing_proc { struct frame { aig * m_node; unsigned short m_idx; frame(aig * n):m_node(n), m_idx(0) {} }; imp & m; svector m_frame_stack; svector m_result_stack; svector m_cache; ptr_vector m_saved; max_sharing_proc(imp & _m):m(_m) {} ~max_sharing_proc() { reset_saved(); } void reset_saved() { m.dec_array_ref(m_saved.size(), m_saved.c_ptr()); m_saved.finalize(); } void reset_cache() { m_cache.finalize(); reset_saved(); } void push_result(aig_lit n) { m_result_stack.push_back(n); if (!n.is_null()) m.inc_ref(n); } bool is_cached(aig * p) { SASSERT(!is_var(p)); if (p->m_ref_count <= 1) return false; unsigned idx = to_idx(p); if (idx >= m_cache.size()) { m_cache.resize(idx+1, aig_lit::null); return false; } aig_lit c = m_cache[idx]; if (!c.is_null()) { push_result(c); return true; } return false; } bool visit(aig * p) { if (is_var(p)) { push_result(nullptr); return true; } if (is_cached(p)) return true; m_frame_stack.push_back(frame(p)); return false; } bool visit(aig_lit l) { return visit(l.ptr()); } void save_result(aig * o, aig_lit n) { SASSERT(!is_var(o)); if (o->m_ref_count > 1) { unsigned idx = to_idx(o); if (idx >= m_cache.size()) m_cache.resize(idx+1, aig_lit::null); m_cache[idx] = n; m_saved.push_back(o); m_saved.push_back(n.ptr()); m.inc_ref(o); m.inc_ref(n); } if (o != n.ptr()) { push_result(n); } else { SASSERT(!n.is_inverted()); push_result(aig_lit::null); } } void pop2_result() { aig_lit r1 = m_result_stack.back(); m_result_stack.pop_back(); aig_lit r2 = m_result_stack.back(); m_result_stack.pop_back(); if (!r1.is_null()) m.dec_ref(r1); if (!r2.is_null()) m.dec_ref(r2); } bool improve_sharing_left(aig * o, aig_lit n) { SASSERT(!left(n).is_inverted()); SASSERT(!is_var(left(n))); aig_lit a = left(left(n)); aig_lit b = right(left(n)); aig_lit c = right(n); TRACE("max_sharing", tout << "trying (and "; m.display_ref(tout, a); tout << " (and "; m.display_ref(tout, b); tout << " "; m.display_ref(tout, c); tout << "))\n";); aig_lit bc = m.mk_and(b, c); m.inc_ref(bc); if (ref_count(bc) > 1) { aig_lit r = m.mk_and(a, bc); if (n.is_inverted()) r.invert(); save_result(o, r); m.dec_ref(bc); TRACE("max_sharing", tout << "improved:\n"; m.display(tout, o); tout << "---->\n"; m.display(tout, r);); return true; } m.dec_ref(bc); TRACE("max_sharing", tout << "trying (and "; m.display_ref(tout, a); tout << " (and "; m.display_ref(tout, c); tout << " "; m.display_ref(tout, b); tout << "))\n";); aig_lit ac = m.mk_and(a, c); m.inc_ref(ac); if (ref_count(ac) > 1) { aig_lit r = m.mk_and(b, ac); if (n.is_inverted()) r.invert(); save_result(o, r); m.dec_ref(ac); TRACE("max_sharing", tout << "improved:\n"; m.display(tout, o); tout << "---->\n"; m.display(tout, r);); return true; } m.dec_ref(ac); return false; } bool improve_sharing_right(aig * o, aig_lit n) { SASSERT(!right(n).is_inverted()); SASSERT(!is_var(right(n))); aig_lit a = left(n); aig_lit b = left(right(n)); aig_lit c = right(right(n)); TRACE("max_sharing", tout << "trying (and (and "; m.display_ref(tout, a); tout << " "; m.display_ref(tout, b); tout << ") "; m.display_ref(tout, c); tout << ")\n";); aig_lit ab = m.mk_and(a, b); m.inc_ref(ab); if (ref_count(ab) > 1) { aig_lit r = m.mk_and(ab, c); if (n.is_inverted()) r.invert(); save_result(o, r); m.dec_ref(ab); TRACE("max_sharing", tout << "improved:\n"; m.display(tout, o); tout << "---->\n"; m.display(tout, r);); return true; } m.dec_ref(ab); aig_lit ac = m.mk_and(a, c); TRACE("max_sharing", tout << "trying (and (and "; m.display_ref(tout, a); tout << " "; m.display_ref(tout, c); tout << ") "; m.display_ref(tout, b); tout << ")\n";); m.inc_ref(ac); if (ref_count(ac) > 1) { aig_lit r = m.mk_and(ac, b); if (n.is_inverted()) r.invert(); save_result(o, r); m.dec_ref(ac); TRACE("max_sharing", tout << "improved:\n"; m.display(tout, o); tout << "---->\n"; m.display(tout, r);); return true; } m.dec_ref(ac); return false; } void improve_sharing_core(aig * o, aig_lit n) { if (!is_var(n)) { aig_lit l = left(n); if (!l.is_inverted() && ref_count(l) == 1 && !is_var(l) && improve_sharing_left(o, n)) return; aig_lit r = right(n); if (!r.is_inverted() && ref_count(r) == 1 && !is_var(r) && improve_sharing_right(o, n)) return; } save_result(o, n); } void improve_sharing(aig * p) { unsigned sz = m_result_stack.size(); aig_lit new_l = m_result_stack[sz-2]; aig_lit new_r = m_result_stack[sz-1]; if (new_l.is_null() && new_r.is_null()) { pop2_result(); improve_sharing_core(p, aig_lit(p)); return; } aig_lit l = left(p); aig_lit r = right(p); if (!new_l.is_null()) { if (l.is_inverted()) new_l.invert(); l = new_l; } if (!new_r.is_null()) { if (r.is_inverted()) new_r.invert(); r = new_r; } aig_lit n = m.mk_and(l, r); m.inc_ref(n); pop2_result(); improve_sharing_core(p, n); m.dec_ref(n); } void process(aig * p) { if (visit(p)) return; while (!m_frame_stack.empty()) { start: frame & fr = m_frame_stack.back(); aig * n = fr.m_node; TRACE("max_sharing", tout << "processing "; m.display_ref(tout, n); tout << " idx: " << fr.m_idx << "\n";); switch (fr.m_idx) { case 0: fr.m_idx++; if (!visit(left(n))) goto start; case 1: fr.m_idx++; if (!visit(right(n))) goto start; default: if (!is_cached(n)) improve_sharing(n); m_frame_stack.pop_back(); break; } } } aig_lit operator()(aig_lit p) { process(p.ptr()); SASSERT(m_result_stack.size() == 1); aig_lit r = m_result_stack.back(); TRACE("max_sharing", tout << "r.is_null(): " << r.is_null() << "\n";); SASSERT(r.is_null() || ref_count(r) >= 1); reset_cache(); if (r.is_null()) { r = p; m.inc_ref(r); } else if (p.is_inverted()) { r.invert(); } m_result_stack.pop_back(); TRACE("max_sharing", tout << "result:\n"; m.display(tout, r);); m.dec_ref_result(r); return r; } }; public: imp(ast_manager & m, unsigned long long max_memory, bool default_gate_encoding): m_var_id_gen(0), m_node_id_gen(FIRST_NODE_ID), m_num_aigs(0), m_var2exprs(m), m_allocator("aig"), m_true(mk_var(m.mk_true())) { SASSERT(is_true(m_true)); m_false = m_true; m_false.invert(); inc_ref(m_true); inc_ref(m_false); m_max_memory = max_memory; m_default_gate_encoding = default_gate_encoding; } ~imp() { dec_ref(m_true); dec_ref(m_false); SASSERT(m_num_aigs == 0); } ast_manager & m() const { return m_var2exprs.get_manager(); } void inc_ref(aig * n) { n->m_ref_count++; } void inc_ref(aig_lit const & r) { inc_ref(r.ptr()); } void dec_ref(aig * n) { dec_ref_core(n); process_to_delete(); } void dec_ref(aig_lit const & r) { dec_ref(r.ptr()); } void dec_array_ref(unsigned sz, aig * const * ns) { for (unsigned i = 0; i < sz; i++) if (ns[i]) dec_ref(ns[i]); } aig_lit mk_and(aig_lit r1, aig_lit r2) { aig_lit r = mk_node(r1, r2); TRACE("mk_and_bug", display(tout, r1); tout << "AND\n"; display(tout, r2); tout << "-->\n"; display(tout, r); tout << "\n";); return r; } aig_lit mk_and(unsigned num, aig_lit * args) { switch (num) { case 0: return m_true; case 1: return args[0]; case 2: return mk_and(args[0], args[1]); default: // No need to use stable_sort, aig_lit_lt is a total order on AIG nodes std::sort(args, args+num, aig_lit_lt()); aig_lit r = mk_and(args[0], args[1]); inc_ref(r); for (unsigned i = 2; i < num; i++) { aig_lit new_r = mk_and(r, args[i]); inc_ref(new_r); dec_ref(r); r = new_r; } dec_ref_result(r); return r; } } aig_lit mk_or(aig_lit r1, aig_lit r2) { r1.invert(); r2.invert(); aig_lit r = mk_and(r1, r2); r.invert(); return r; } aig_lit mk_or(unsigned num, aig_lit * args) { switch (num) { case 0: return m_false; case 1: return args[0]; case 2: return mk_or(args[0], args[1]); default: std::sort(args, args+num, aig_lit_lt()); aig_lit r = mk_or(args[0], args[1]); inc_ref(r); for (unsigned i = 2; i < num; i++) { aig_lit new_r = mk_or(r, args[i]); inc_ref(new_r); dec_ref(r); r = new_r; } dec_ref_result(r); return r; } } aig_lit mk_ite(aig_lit c, aig_lit t, aig_lit e) { if (m_default_gate_encoding) { t.invert(); aig_lit n1 = mk_and(c, t); // c and (not t) c.invert(); e.invert(); aig_lit n2 = mk_and(c, e); // (not c) and (not e) inc_ref(n1); inc_ref(n2); n1.invert(); n2.invert(); aig_lit r = mk_and(n1, n2); inc_ref(r); dec_ref(n1); dec_ref(n2); dec_ref_result(r); return r; } else { aig_lit n1 = mk_and(c, t); inc_ref(n1); c.invert(); aig_lit n2 = mk_and(c, e); inc_ref(n2); n1.invert(); n2.invert(); aig_lit r = mk_and(n1, n2); r.invert(); inc_ref(r); dec_ref(n1); dec_ref(n2); dec_ref_result(r); return r; } } aig_lit mk_iff(aig_lit lhs, aig_lit rhs) { if (m_default_gate_encoding) { rhs.invert(); aig_lit n1 = mk_and(lhs, rhs); // lhs and (not rhs) lhs.invert(); rhs.invert(); aig_lit n2 = mk_and(lhs, rhs); // (not lhs) and rhs inc_ref(n1); inc_ref(n2); n1.invert(); n2.invert(); aig_lit r = mk_and(n1, n2); inc_ref(r); dec_ref(n1); dec_ref(n2); dec_ref_result(r); return r; } else { aig_lit n1 = mk_and(lhs, rhs); // lhs and rhs inc_ref(n1); lhs.invert(); rhs.invert(); aig_lit n2 = mk_and(lhs, rhs); // not lhs and not rhs inc_ref(n2); n1.invert(); n2.invert(); aig_lit r = mk_and(n1, n2); r.invert(); inc_ref(r); dec_ref(n1); dec_ref(n2); dec_ref_result(r); return r; } } aig_lit mk_xor(aig_lit lhs, aig_lit rhs) { lhs.invert(); return mk_iff(lhs, rhs); } aig_lit mk_implies(aig_lit lhs, aig_lit rhs) { lhs.invert(); return mk_or(lhs, rhs); } aig_lit mk_aig(expr * t) { aig_lit r; { expr2aig proc(*this); r = proc(t); inc_ref(r); } dec_ref_result(r); return r; } template aig_lit mk_aig(S const & s) { aig_lit r; r = m_true; inc_ref(r); try { expr2aig proc(*this); unsigned sz = s.size(); for (unsigned i = 0; i < sz; i++) { SASSERT(ref_count(r) >= 1); expr * t = s.form(i); aig_lit n = proc(t); inc_ref(n); aig_lit new_r = mk_and(r, n); SASSERT(proc.check_cache()); inc_ref(new_r); dec_ref(r); dec_ref(n); SASSERT(proc.check_cache()); r = new_r; } SASSERT(ref_count(r) >= 1); } catch (const aig_exception & ex) { dec_ref(r); throw ex; } dec_ref_result(r); return r; } void to_formula(aig_lit const & r, goal & g) { aig2expr proc(*this); proc(r, g); } void to_formula(aig_lit const & r, expr_ref & result) { aig2expr proc(*this); proc(r, result); } aig_lit max_sharing(aig_lit l) { max_sharing_proc p(*this); return p(l); } void display_ref(std::ostream & out, aig * r) const { if (is_var(r)) out << "#" << r->m_id; else out << "@" << (r->m_id - FIRST_NODE_ID); } void display_ref(std::ostream & out, aig_lit const & r) const { if (r.is_inverted()) out << "-"; display_ref(out, r.ptr()); } void display(std::ostream & out, aig_lit const & r) const { display_ref(out, r); out << "\n"; ptr_vector queue; unsigned qhead = 0; queue.push_back(r.ptr()); while (qhead < queue.size()) { aig * n = queue[qhead]; qhead++; display_ref(out, n); out << ": "; if (is_var(n)) { out << mk_ismt2_pp(m_var2exprs[n->m_id], m()) << "\n"; } else { display_ref(out, n->m_children[0]); out << " "; display_ref(out, n->m_children[1]); out << "\n"; aig * c1 = n->m_children[0].ptr(); aig * c2 = n->m_children[1].ptr(); if (!c1->m_mark) { c1->m_mark = true; queue.push_back(c1); } if (!c2->m_mark) { c2->m_mark = true; queue.push_back(c2); } } } unmark(queue.size(), queue.c_ptr()); } void display_smt2_ref(std::ostream & out, aig_lit const & r) const { if (r.is_inverted()) out << "(not "; if (is_var(r)) { out << mk_ismt2_pp(var2expr(r.ptr()), m()); } else { out << "aig" << to_idx(r.ptr()); } if (r.is_inverted()) out << ")"; } void display_smt2(std::ostream & out, aig_lit const & r) const { ptr_vector to_unmark; ptr_vector todo; todo.push_back(r.ptr()); while (!todo.empty()) { aig * t = todo.back(); if (t->m_mark) { todo.pop_back(); continue; } if (is_var(t)) { to_unmark.push_back(t); t->m_mark = true; todo.pop_back(); continue; } bool visited = true; for (unsigned i = 0; i < 2; i++) { aig_lit c = t->m_children[i]; aig * c_ptr = c.ptr(); if (!c_ptr->m_mark) { todo.push_back(c_ptr); visited = false; } } if (!visited) continue; to_unmark.push_back(t); t->m_mark = true; out << "(define-fun aig" << to_idx(t) << " () Bool (and"; for (unsigned i = 0; i < 2; i++) { out << " "; display_smt2_ref(out, t->m_children[i]); } out << "))\n"; todo.pop_back(); } out << "(assert "; display_smt2_ref(out, r); out << ")\n"; unmark(to_unmark.size(), to_unmark.c_ptr()); } unsigned get_num_aigs() const { return m_num_aigs; } }; aig_ref::aig_ref(): m_manager(nullptr), m_ref(nullptr) { } aig_ref::aig_ref(aig_manager & m, aig_lit const & l): m_manager(&m), m_ref(l.m_ref) { m.m_imp->inc_ref(l); } aig_ref::~aig_ref() { if (m_ref != nullptr) { m_manager->m_imp->dec_ref(aig_lit(*this)); } } aig_ref & aig_ref::operator=(aig_ref const & r) { if (r.m_ref != nullptr) r.m_manager->m_imp->inc_ref(aig_lit(r)); if (m_ref != nullptr) m_manager->m_imp->dec_ref(aig_lit(*this)); m_ref = r.m_ref; m_manager = r.m_manager; return *this; } aig_manager::aig_manager(ast_manager & m, unsigned long long max, bool default_gate_encoding) { m_imp = alloc(imp, m, max, default_gate_encoding); } aig_manager::~aig_manager() { dealloc(m_imp); } void aig_manager::set_max_memory(unsigned long long max) { m_imp->m_max_memory = max; } aig_ref aig_manager::mk_aig(expr * n) { return aig_ref(*this, m_imp->mk_aig(n)); } aig_ref aig_manager::mk_aig(goal const & s) { return aig_ref(*this, m_imp->mk_aig(s)); } aig_ref aig_manager::mk_not(aig_ref const & r) { aig_lit l(r); l.invert(); return aig_ref(*this, l); } aig_ref aig_manager::mk_and(aig_ref const & r1, aig_ref const & r2) { return aig_ref(*this, m_imp->mk_and(aig_lit(r1), aig_lit(r2))); } aig_ref aig_manager::mk_or(aig_ref const & r1, aig_ref const & r2) { return aig_ref(*this, m_imp->mk_or(aig_lit(r1), aig_lit(r2))); } aig_ref aig_manager::mk_iff(aig_ref const & r1, aig_ref const & r2) { return aig_ref(*this, m_imp->mk_iff(aig_lit(r1), aig_lit(r2))); } aig_ref aig_manager::mk_ite(aig_ref const & r1, aig_ref const & r2, aig_ref const & r3) { return aig_ref(*this, m_imp->mk_ite(aig_lit(r1), aig_lit(r2), aig_lit(r3))); } void aig_manager::max_sharing(aig_ref & r) { r = aig_ref(*this, m_imp->max_sharing(aig_lit(r))); } void aig_manager::to_formula(aig_ref const & r, goal & g) { SASSERT(!g.proofs_enabled()); SASSERT(!g.unsat_core_enabled()); return m_imp->to_formula(aig_lit(r), g); } void aig_manager::to_formula(aig_ref const & r, expr_ref & res) { return m_imp->to_formula(aig_lit(r), res); } void aig_manager::display(std::ostream & out, aig_ref const & r) const { m_imp->display(out, aig_lit(r)); } void aig_manager::display_smt2(std::ostream & out, aig_ref const & r) const { m_imp->display_smt2(out, aig_lit(r)); } unsigned aig_manager::get_num_aigs() const { return m_imp->get_num_aigs(); } z3-z3-4.8.7/src/tactic/aig/aig.h000066400000000000000000000042211356505360400161350ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: aig.h Abstract: And-inverted graphs Author: Leonardo (leonardo) 2011-05-13 Notes: --*/ #ifndef AIG_H_ #define AIG_H_ #include "ast/ast.h" #include "tactic/tactic_exception.h" class goal; class aig_lit; class aig_manager; class aig_exception : public tactic_exception { public: aig_exception(char const * msg):tactic_exception(msg) {} }; class aig_ref { friend class aig_lit; friend class aig_manager; aig_manager * m_manager; void * m_ref; aig_ref(aig_manager & m, aig_lit const & l); public: aig_ref(); ~aig_ref(); aig_ref & operator=(aig_ref const & r); bool operator==(aig_ref const & r) const { return m_ref == r.m_ref; } bool operator!=(aig_ref const & r) const { return m_ref != r.m_ref; } }; class aig_manager { struct imp; imp * m_imp; friend class aig_ref; public: // If default_gate_encoding == true, then // ite(a, b, c) is encoded as (NOT a OR b) AND (a OR c) // iff(a, b) is encoded as (NOT a OR b) AND (a OR NOT b) // // If default_gate_encoding == false, then // ite(a, b, c) is encoded as (a AND b) OR (NOT a AND c) // iff(a, b) is encoded as (a AND b) OR (NOT a AND NOT b) aig_manager(ast_manager & m, unsigned long long max_memory = UINT64_MAX, bool default_gate_encoding = true); ~aig_manager(); void set_max_memory(unsigned long long max); aig_ref mk_aig(expr * n); aig_ref mk_aig(goal const & g); aig_ref mk_not(aig_ref const & r); aig_ref mk_and(aig_ref const & r1, aig_ref const & r2); aig_ref mk_or(aig_ref const & r1, aig_ref const & r2); aig_ref mk_iff(aig_ref const & r1, aig_ref const & r2); aig_ref mk_ite(aig_ref const & r1, aig_ref const & r2, aig_ref const & r3); void max_sharing(aig_ref & r); void to_formula(aig_ref const & r, expr_ref & result); void to_formula(aig_ref const & r, goal & result); void display(std::ostream & out, aig_ref const & r) const; void display_smt2(std::ostream & out, aig_ref const & r) const; unsigned get_num_aigs() const; }; #endif z3-z3-4.8.7/src/tactic/aig/aig_tactic.cpp000066400000000000000000000057671356505360400200370ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: aig_tactic.cpp Abstract: Tactic for minimizing circuits using AIGs. Author: Leonardo (leonardo) 2011-10-24 Notes: --*/ #include "tactic/tactical.h" #include "tactic/aig/aig.h" class aig_manager; class aig_tactic : public tactic { unsigned long long m_max_memory; bool m_aig_gate_encoding; bool m_aig_per_assertion; aig_manager * m_aig_manager; struct mk_aig_manager { aig_tactic & m_owner; mk_aig_manager(aig_tactic & o, ast_manager & m):m_owner(o) { aig_manager * mng = alloc(aig_manager, m, o.m_max_memory, o.m_aig_gate_encoding); m_owner.m_aig_manager = mng; } ~mk_aig_manager() { dealloc(m_owner.m_aig_manager); m_owner.m_aig_manager = nullptr; } }; public: aig_tactic(params_ref const & p = params_ref()):m_aig_manager(nullptr) { updt_params(p); } tactic * translate(ast_manager & m) override { aig_tactic * t = alloc(aig_tactic); t->m_max_memory = m_max_memory; t->m_aig_gate_encoding = m_aig_gate_encoding; t->m_aig_per_assertion = m_aig_per_assertion; return t; } void updt_params(params_ref const & p) override { m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); m_aig_gate_encoding = p.get_bool("aig_default_gate_encoding", true); m_aig_per_assertion = p.get_bool("aig_per_assertion", true); } void collect_param_descrs(param_descrs & r) override { insert_max_memory(r); r.insert("aig_per_assertion", CPK_BOOL, "(default: true) process one assertion at a time."); } void operator()(goal_ref const & g) { SASSERT(g->is_well_sorted()); mk_aig_manager mk(*this, g->m()); if (m_aig_per_assertion) { for (unsigned i = 0; i < g->size(); i++) { aig_ref r = m_aig_manager->mk_aig(g->form(i)); m_aig_manager->max_sharing(r); expr_ref new_f(g->m()); m_aig_manager->to_formula(r, new_f); expr_dependency * ed = g->dep(i); g->update(i, new_f, nullptr, ed); } } else { fail_if_unsat_core_generation("aig", g); aig_ref r = m_aig_manager->mk_aig(*(g.get())); g->reset(); // save memory m_aig_manager->max_sharing(r); m_aig_manager->to_formula(r, *(g.get())); } SASSERT(g->is_well_sorted()); } void operator()(goal_ref const & g, goal_ref_buffer & result) override { fail_if_proof_generation("aig", g); tactic_report report("aig", *g); operator()(g); g->inc_depth(); TRACE("aig", g->display(tout);); result.push_back(g.get()); } void cleanup() override {} }; tactic * mk_aig_tactic(params_ref const & p) { return clean(alloc(aig_tactic, p)); } z3-z3-4.8.7/src/tactic/aig/aig_tactic.h000066400000000000000000000006641356505360400174730ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: aig_tactic.h Abstract: Tactic for minimizing circuits using AIGs. Author: Leonardo (leonardo) 2011-10-24 Notes: --*/ #ifndef AIG_TACTIC_H_ #define AIG_TACTIC_H_ #include "util/params.h" class tactic; tactic * mk_aig_tactic(params_ref const & p = params_ref()); /* ADD_TACTIC("aig", "simplify Boolean structure using AIGs.", "mk_aig_tactic()") */ #endif z3-z3-4.8.7/src/tactic/arith/000077500000000000000000000000001356505360400155745ustar00rootroot00000000000000z3-z3-4.8.7/src/tactic/arith/CMakeLists.txt000066400000000000000000000020721356505360400203350ustar00rootroot00000000000000z3_add_component(arith_tactics SOURCES add_bounds_tactic.cpp arith_bounds_tactic.cpp bound_manager.cpp bound_propagator.cpp bv2int_rewriter.cpp bv2real_rewriter.cpp card2bv_tactic.cpp degree_shift_tactic.cpp diff_neq_tactic.cpp eq2bv_tactic.cpp factor_tactic.cpp fix_dl_var_tactic.cpp fm_tactic.cpp lia2card_tactic.cpp lia2pb_tactic.cpp linear_equation.cpp nla2bv_tactic.cpp normalize_bounds_tactic.cpp pb2bv_model_converter.cpp pb2bv_tactic.cpp probe_arith.cpp propagate_ineqs_tactic.cpp purify_arith_tactic.cpp recover_01_tactic.cpp COMPONENT_DEPENDENCIES core_tactics sat TACTIC_HEADERS add_bounds_tactic.h card2bv_tactic.h degree_shift_tactic.h diff_neq_tactic.h eq2bv_tactic.h factor_tactic.h fix_dl_var_tactic.h fm_tactic.h lia2pb_tactic.h lia2card_tactic.h nla2bv_tactic.h normalize_bounds_tactic.h pb2bv_tactic.h probe_arith.h propagate_ineqs_tactic.h purify_arith_tactic.h recover_01_tactic.h ) z3-z3-4.8.7/src/tactic/arith/add_bounds_tactic.cpp000066400000000000000000000115041356505360400217320ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: add_bounds_tactic.h Abstract: Tactic for bounding unbounded variables. Author: Leonardo de Moura (leonardo) 2011-10-22. Revision History: --*/ #include "tactic/tactical.h" #include "ast/arith_decl_plugin.h" #include "ast/ast_smt2_pp.h" #include "tactic/arith/bound_manager.h" struct is_unbounded_proc { struct found {}; arith_util m_util; bound_manager & m_bm; is_unbounded_proc(bound_manager & bm):m_util(bm.m()), m_bm(bm) {} void operator()(app * t) { if (is_uninterp_const(t) && (m_util.is_int(t) || m_util.is_real(t)) && (!m_bm.has_lower(t) || !m_bm.has_upper(t))) throw found(); } void operator()(var *) {} void operator()(quantifier*) {} }; bool is_unbounded(goal const & g) { ast_manager & m = g.m(); bound_manager bm(m); bm(g); is_unbounded_proc proc(bm); return test(g, proc); } class is_unbounded_probe : public probe { public: result operator()(goal const & g) override { return is_unbounded(g); } ~is_unbounded_probe() override {} }; probe * mk_is_unbounded_probe() { return alloc(is_unbounded_probe); } class add_bounds_tactic : public tactic { struct imp { ast_manager & m; rational m_lower; rational m_upper; imp(ast_manager & _m, params_ref const & p): m(_m) { updt_params(p); } void updt_params(params_ref const & p) { m_lower = p.get_rat("add_bound_lower", rational(-2)); m_upper = p.get_rat("add_bound_upper", rational(2)); } struct add_bound_proc { arith_util m_util; bound_manager & m_bm; goal & m_goal; rational const & m_lower; rational const & m_upper; unsigned m_num_bounds; add_bound_proc(bound_manager & bm, goal & g, rational const & l, rational const & u): m_util(bm.m()), m_bm(bm), m_goal(g), m_lower(l), m_upper(u) { m_num_bounds = 0; } void operator()(app * t) { if (is_uninterp_const(t) && (m_util.is_int(t) || m_util.is_real(t))) { if (!m_bm.has_lower(t)) { m_goal.assert_expr(m_util.mk_le(t, m_util.mk_numeral(m_upper, m_util.is_int(t)))); m_num_bounds++; } if (!m_bm.has_upper(t)) { m_goal.assert_expr(m_util.mk_ge(t, m_util.mk_numeral(m_lower, m_util.is_int(t)))); m_num_bounds++; } } } void operator()(var *) {} void operator()(quantifier*) {} }; void operator()(goal_ref const & g, goal_ref_buffer & result) { tactic_report report("add-bounds", *g); bound_manager bm(m); expr_fast_mark1 visited; add_bound_proc proc(bm, *(g.get()), m_lower, m_upper); unsigned sz = g->size(); for (unsigned i = 0; i < sz; i++) quick_for_each_expr(proc, visited, g->form(i)); visited.reset(); g->inc_depth(); result.push_back(g.get()); if (proc.m_num_bounds > 0) g->updt_prec(goal::UNDER); report_tactic_progress(":added-bounds", proc.m_num_bounds); TRACE("add_bounds", g->display(tout);); } }; imp * m_imp; params_ref m_params; public: add_bounds_tactic(ast_manager & m, params_ref const & p): m_params(p) { m_imp = alloc(imp, m, p); } tactic * translate(ast_manager & m) override { return alloc(add_bounds_tactic, m, m_params); } ~add_bounds_tactic() override { dealloc(m_imp); } void updt_params(params_ref const & p) override { m_params = p; m_imp->updt_params(p); } void collect_param_descrs(param_descrs & r) override { r.insert("add_bound_lower", CPK_NUMERAL, "(default: -2) lower bound to be added to unbounded variables."); r.insert("add_bound_upper", CPK_NUMERAL, "(default: 2) upper bound to be added to unbounded variables."); } void operator()(goal_ref const & g, goal_ref_buffer & result) override { (*m_imp)(g, result); } void cleanup() override { imp * d = alloc(imp, m_imp->m, m_params); std::swap(d, m_imp); dealloc(d); } }; tactic * mk_add_bounds_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(add_bounds_tactic, m, p)); } z3-z3-4.8.7/src/tactic/arith/add_bounds_tactic.h000066400000000000000000000014071356505360400214000ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: add_bounds.h Abstract: Tactic for bounding unbounded variables. Author: Leonardo de Moura (leonardo) 2011-06-30. Revision History: --*/ #ifndef ADD_BOUNDS_H_ #define ADD_BOUNDS_H_ #include "util/params.h" class ast_manager; class goal; class tactic; class probe; bool is_unbounded(goal const & g); probe * mk_is_unbounded_probe(); tactic * mk_add_bounds_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("add-bounds", "add bounds to unbounded variables (under approximation).", "mk_add_bounds_tactic(m, p)") ADD_PROBE("is-unbounded", "true if the goal contains integer/real constants that do not have lower/upper bounds.", "mk_is_unbounded_probe()") */ #endif z3-z3-4.8.7/src/tactic/arith/arith_bounds_tactic.cpp000066400000000000000000000107711356505360400223160ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "tactic/arith/arith_bounds_tactic.h" #include "ast/arith_decl_plugin.h" struct arith_bounds_tactic : public tactic { ast_manager& m; arith_util a; arith_bounds_tactic(ast_manager& m): m(m), a(m) { } ast_manager& get_manager() { return m; } void operator()(/* in */ goal_ref const & in, /* out */ goal_ref_buffer & result) override { bounds_arith_subsumption(in, result); } tactic* translate(ast_manager & mgr) override { return alloc(arith_bounds_tactic, mgr); } void checkpoint() { if (m.canceled()) { throw tactic_exception(m.limit().get_cancel_msg()); } } struct info { rational r; unsigned idx; bool is_strict;}; /** \brief Basic arithmetic subsumption simplification based on bounds. */ void mk_proof(proof_ref& pr, goal_ref const& s, unsigned i, unsigned j) { if (s->proofs_enabled()) { proof* th_lemma = m.mk_th_lemma(a.get_family_id(), m.mk_implies(s->form(i), s->form(j)), 0, nullptr); pr = m.mk_modus_ponens(s->pr(i), th_lemma); } } bool is_le_or_lt(expr* e, expr*& e1, expr*& e2, bool& is_strict) { bool is_negated = m.is_not(e, e); if ((!is_negated && (a.is_le(e, e1, e2) || a.is_ge(e, e2, e1))) || (is_negated && (a.is_lt(e, e2, e1) || a.is_gt(e, e1, e2)))) { is_strict = false; return true; } if ((!is_negated && (a.is_lt(e, e1, e2) || a.is_gt(e, e2, e1))) || (is_negated && (a.is_le(e, e2, e1) || a.is_ge(e, e1, e2)))) { is_strict = true; return true; } return false; } void bounds_arith_subsumption(goal_ref const& g, goal_ref_buffer& result) { info inf; rational r; goal_ref s(g); // initialize result. obj_map lower, upper; expr* e1, *e2; TRACE("arith_subsumption", s->display(tout); ); for (unsigned i = 0; i < s->size(); ++i) { checkpoint(); expr* lemma = s->form(i); bool is_strict = false; bool is_lower = false; if (!is_le_or_lt(lemma, e1, e2, is_strict)) { continue; } // e1 <= e2 or e1 < e2 if (a.is_numeral(e2, r)) { is_lower = true; } else if (a.is_numeral(e1, r)) { is_lower = false; } else { continue; } proof_ref new_pr(m); if (is_lower && upper.find(e1, inf)) { if (inf.r > r || (inf.r == r && is_strict && !inf.is_strict)) { mk_proof(new_pr, s, i, inf.idx); s->update(inf.idx, m.mk_true(), new_pr); inf.r = r; inf.is_strict = is_strict; inf.idx = i; upper.insert(e1, inf); } else { mk_proof(new_pr, s, inf.idx, i); s->update(i, m.mk_true(), new_pr); } } else if (is_lower) { inf.r = r; inf.is_strict = is_strict; inf.idx = i; upper.insert(e1, inf); } else if (!is_lower && lower.find(e2, inf)) { if (inf.r < r || (inf.r == r && is_strict && !inf.is_strict)) { mk_proof(new_pr, s, i, inf.idx); s->update(inf.idx, m.mk_true(), new_pr); inf.r = r; inf.is_strict = is_strict; inf.idx = i; lower.insert(e2, inf); } else { mk_proof(new_pr, s, inf.idx, i); s->update(i, m.mk_true()); } } else if (!is_lower) { inf.r = r; inf.is_strict = is_strict; inf.idx = i; lower.insert(e2, inf); } } s->elim_true(); result.push_back(s.get()); TRACE("arith_subsumption", s->display(tout); ); } void cleanup() override {} }; tactic * mk_arith_bounds_tactic(ast_manager & m, params_ref const & p) { return alloc(arith_bounds_tactic, m); } z3-z3-4.8.7/src/tactic/arith/arith_bounds_tactic.h000066400000000000000000000017471356505360400217660ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: arith_bounds_tactic.h Abstract: Fast/rudimentary arithmetic subsumption tactic. Author: Nikolaj Bjorner (nbjorner) 2012-9-6 Notes: Background: The Farkas learner in PDR generates tons of inequalities that contain redundancies. It therefore needs a fast way to reduce these redundancies before passing the results to routines that are more expensive. The arith subsumption_strategy encapsulates a rudimentary routine for simplifying inequalities. Additional simplification routines can be added here or composed with this strategy. Note: The bound_manager subsumes some of the collection methods used for assembling bounds, but it does not have a way to check for subsumption of atoms. --*/ #ifndef ARITH_BOUNDS_TACTIC_H_ #define ARITH_BOUNDS_TACTIC_H_ #include "tactic/tactic.h" tactic * mk_arith_bounds_tactic(ast_manager & m, params_ref const & p = params_ref()); #endif z3-z3-4.8.7/src/tactic/arith/bound_manager.cpp000066400000000000000000000160641356505360400211100ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: bound_manager.cpp Abstract: Collect bounds. Author: Leonardo (leonardo) 2011-05-16 Notes: --*/ #include "tactic/arith/bound_manager.h" #include "ast/ast_smt2_pp.h" #include "tactic/goal.h" bound_manager::bound_manager(ast_manager & m): m_util(m) { } bound_manager::~bound_manager() { reset(); } bound_manager* bound_manager::translate(ast_manager& dst_m) { bound_manager* result = alloc(bound_manager, dst_m); ast_translation tr(m(), dst_m); expr_dependency_translation edtr(tr); for (auto& kv : m_lowers) result->m_lowers.insert(tr(kv.m_key), kv.m_value); for (auto& kv : m_uppers) result->m_uppers.insert(tr(kv.m_key), kv.m_value); for (auto& kv : m_lower_deps) result->m_lower_deps.insert(tr(kv.m_key), edtr(kv.m_value)); for (auto& kv : m_upper_deps) result->m_upper_deps.insert(tr(kv.m_key), edtr(kv.m_value)); for (expr* e : m_bounded_vars) result->m_bounded_vars.push_back(tr(e)); return result; } static decl_kind swap_decl(decl_kind k) { switch (k) { case OP_LE: return OP_GE; case OP_LT: return OP_GT; case OP_GE: return OP_LE; case OP_GT: return OP_LT; default: UNREACHABLE(); return k; } } decl_kind bound_manager::neg(decl_kind k) { switch (k) { case OP_LE: return OP_GT; case OP_LT: return OP_GE; case OP_GE: return OP_LT; case OP_GT: return OP_LE; default: UNREACHABLE(); return k; } } void bound_manager::norm(numeral & n, decl_kind & k) { switch (k) { case OP_LE: return; case OP_GE: return; case OP_LT: // x < n --> x <= n-1 n--; k = OP_LE; return; case OP_GT: // x > n --> x >= n+1 n++; k = OP_GE; return; default: return; } } static bool is_lower(decl_kind k) { return k == OP_GT || k == OP_GE; } static bool is_strict(decl_kind k) { return k == OP_LT || k == OP_GT; } bool bound_manager::is_numeral(expr* v, numeral& n, bool& is_int) { expr* w; if (m_util.is_uminus(v, w) && is_numeral(w, n, is_int)) { n.neg(); return true; } return m_util.is_numeral(v, n, is_int); } void bound_manager::operator()(expr * f, expr_dependency * d) { TRACE("bound_manager", tout << "processing:\n" << mk_ismt2_pp(f, m()) << "\n";); expr * v; numeral n; if (is_disjunctive_bound(f, d)) return; if (is_equality_bound(f, d)) return; bool pos = true; while (m().is_not(f, f)) pos = !pos; if (!is_app(f)) return; app * t = to_app(f); if (t->get_family_id() != m_util.get_family_id()) return; decl_kind k = t->get_decl_kind(); if (k != OP_LE && k != OP_GE && k != OP_LT && k != OP_GT) return; expr * lhs = t->get_arg(0); expr * rhs = t->get_arg(1); bool is_int; if (is_uninterp_const(lhs) && is_numeral(rhs, n, is_int)) { v = lhs; } else if (is_uninterp_const(rhs) && is_numeral(lhs, n, is_int)) { v = rhs; k = swap_decl(k); } else { return; } if (!pos) k = neg(k); if (is_int) norm(n, k); TRACE("bound_manager", tout << "found bound for:\n" << mk_ismt2_pp(v, m()) << "\n";); bool strict = is_strict(k); if (is_lower(k)) { insert_lower(v, strict, n, d); } else { insert_upper(v, strict, n, d); } } void bound_manager::insert_upper(expr * v, bool strict, numeral const & n, expr_dependency * d) { limit old; if (m_uppers.find(v, old)) { if (n < old.first || (n == old.first && strict && !old.second)) { // improved bound m_uppers.insert(v, limit(n, strict)); if (d) m_upper_deps.insert(v, d); } } else { m_uppers.insert(v, limit(n, strict)); if (d) m_upper_deps.insert(v, d); if (!m_lowers.contains(v)) { m_bounded_vars.push_back(v); m().inc_ref(v); } } } void bound_manager::insert_lower(expr * v, bool strict, numeral const & n, expr_dependency * d) { limit old; if (m_lowers.find(v, old)) { if (n > old.first || (n == old.first && strict && !old.second)) { // improved bound m_lowers.insert(v, limit(n, strict)); if (d) m_lower_deps.insert(v, d); } } else { m_lowers.insert(v, limit(n, strict)); if (d) m_lower_deps.insert(v, d); if (!m_uppers.contains(v)) { m_bounded_vars.push_back(v); m().inc_ref(v); } } } bool bound_manager::is_equality_bound(expr * f, expr_dependency * d) { expr* x, *y; if (!m().is_eq(f, x, y)) { return false; } if (!is_uninterp_const(x)) { std::swap(x, y); } numeral n; bool is_int; if (is_uninterp_const(x) && is_numeral(y, n, is_int)) { insert_lower(x, false, n, d); insert_upper(x, false, n, d); return true; } else { return false; } } bool bound_manager::is_disjunctive_bound(expr * f, expr_dependency * d) { numeral lo, hi, n; if (!m().is_or(f)) return false; unsigned sz = to_app(f)->get_num_args(); if (sz == 0) return false; expr * x, * y, * v = nullptr; bool is_int; for (unsigned i = 0; i < sz; ++i) { expr * e = to_app(f)->get_arg(i); if (!m().is_eq(e, x, y)) return false; if (is_uninterp_const(x) && is_numeral(y, n, is_int) && is_int && (x == v || v == nullptr)) { if (v == nullptr) { v = x; lo = hi = n; } if (n < lo) lo = n; if (n > hi) hi = n; } else if (is_uninterp_const(y) && is_numeral(x, n, is_int) && is_int && (y == v || v == nullptr)) { if (v == nullptr) { v = y; lo = hi = n; } if (n < lo) lo = n; if (n > hi) hi = n; } else { return false; } } TRACE("bound_manager", tout << "bounds: " << lo << " " << hi << "\n";); insert_lower(v, false, lo, d); insert_upper(v, false, hi, d); return true; } void bound_manager::operator()(goal const & g) { unsigned sz = g.size(); for (unsigned i = 0; i < sz; i++) { operator()(g.form(i), g.dep(i)); } } void bound_manager::reset() { m().dec_array_ref(m_bounded_vars.size(), m_bounded_vars.c_ptr()); m_bounded_vars.finalize(); m_lowers.finalize(); m_uppers.finalize(); m_lower_deps.finalize(); m_upper_deps.finalize(); } void bound_manager::display(std::ostream & out) const { numeral n; bool strict; for (iterator it = begin(); it != end(); ++it) { expr * v = *it; if (has_lower(v, n, strict)) out << n << " " << (strict ? "<" : "<="); else out << "-oo <"; out << " " << mk_ismt2_pp(v, m()) << " "; if (has_upper(v, n, strict)) out << (strict ? "<" : "<=") << " " << n; else out << "< oo"; out << "\n"; } } z3-z3-4.8.7/src/tactic/arith/bound_manager.h000066400000000000000000000052421356505360400205510ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: bound_manager.h Abstract: Collect bounds. Author: Leonardo (leonardo) 2011-05-16 Notes: --*/ #ifndef BOUND_MANAGER_H_ #define BOUND_MANAGER_H_ #include "ast/ast.h" #include "ast/arith_decl_plugin.h" class goal; class bound_manager { public: typedef rational numeral; private: typedef std::pair limit; arith_util m_util; obj_map m_lowers; obj_map m_uppers; obj_map m_lower_deps; obj_map m_upper_deps; ptr_vector m_bounded_vars; bool is_disjunctive_bound(expr * f, expr_dependency * d); bool is_equality_bound(expr * f, expr_dependency * d); bool is_numeral(expr* v, rational& n, bool& is_int); void insert_lower(expr * v, bool strict, numeral const & n, expr_dependency * d); void insert_upper(expr * v, bool strict, numeral const & n, expr_dependency * d); public: static decl_kind neg(decl_kind k); static void norm(numeral & n, decl_kind & k); bound_manager(ast_manager & m); ~bound_manager(); bound_manager* translate(ast_manager& dst_m); ast_manager & m() const { return m_util.get_manager(); } void operator()(goal const & g); void operator()(expr * n, expr_dependency * d = nullptr); bool has_lower(expr * c, numeral & v, bool & strict) const { limit l; if (m_lowers.find(c, l)) { v = l.first; strict = l.second; return true; } return false; } bool has_upper(expr * c, numeral & v, bool & strict) const { limit l; if (m_uppers.find(c, l)) { v = l.first; strict = l.second; return true; } return false; } expr_dependency * lower_dep(expr * c) const { expr_dependency * d; if (m_lower_deps.find(c, d)) return d; return nullptr; } expr_dependency * upper_dep(expr * c) const { expr_dependency * d; if (m_upper_deps.find(c, d)) return d; return nullptr; } bool has_lower(expr * c) const { return m_lowers.contains(c); } bool has_upper(expr * c) const { return m_uppers.contains(c); } typedef ptr_vector::const_iterator iterator; /** \brief Iterator for all bounded constants. */ iterator begin() const { return m_bounded_vars.begin(); } iterator end() const { return m_bounded_vars.end(); } void reset(); // for debugging purposes void display(std::ostream & out) const; }; #endif z3-z3-4.8.7/src/tactic/arith/bound_propagator.cpp000066400000000000000000000743211356505360400216540ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: bound_propagator.cpp Abstract: Bound propagators for arithmetic. Support class for implementing strategies and search procedures Author: Leonardo de Moura (leonardo) 2011-06-18. Revision History: --*/ #include "tactic/arith/bound_propagator.h" #include // ------------------------------- // Bound Relaxation configuration // // The idea is to minimize errors in floating point computations // // If RELAX_BOUNDS is undefined, then bound relaxation is disabled. // Otherwise, lower bounds l are relaxed using the formula // PRECISION * floor(l * INV_PRECISION + TOLERANCE) // and upper bounds u as: // PRECISION * ceil(u * INV_PRECISION - TOLERANCE) // In the LP literature, the suggested values are // l := 10^-5 * floor(l*10^5 + 10^-6) // u := 10^-5 * ceil(u*10^5 - 10^-6) // I'm using the following values because of strict bounds // l := 10^-6 * floor(l*10^6 + 10^-7) // u := 10^-6 * ceil(u*10^6 - 10^-7) #define RELAX_BOUNDS #define TOLERANCE 0.0000001 #define PRECISION 0.000001 #define INV_PRECISION 1000000.0 // ------------------------------- bound_propagator::bound::bound(numeral_manager & m, mpq const & k, double approx_k, bool lower, bool strict, unsigned lvl, unsigned ts, bkind bk, unsigned c_idx, assumption a, bound * prev): m_approx_k(approx_k), m_lower(lower), m_strict(strict), m_kind(bk), m_level(lvl), m_timestamp(ts), m_prev(prev) { m.set(m_k, k); if (bk == DERIVED) m_constraint_idx = c_idx; else m_assumption = a; } bound_propagator::bound_propagator(numeral_manager & _m, allocator & a, params_ref const & p): m(_m), m_allocator(a), m_eq_manager(m, a) { m_timestamp = 0; m_qhead = 0; m_conflict = null_var; updt_params(p); reset_statistics(); } bound_propagator::~bound_propagator() { m.del(m_tmp); reset(); } void bound_propagator::del_constraints_core() { constraint_vector::iterator it = m_constraints.begin(); constraint_vector::iterator end = m_constraints.end(); for (; it != end; ++it) { del_constraint(*it); } m_constraints.reset(); } void bound_propagator::del_constraints() { SASSERT(scope_lvl() == 0); if (m_constraints.empty()) return; del_constraints_core(); m_constraints.finalize(); vector::iterator it = m_watches.begin(); vector::iterator end = m_watches.end(); for (; it != end; ++it) it->finalize(); } void bound_propagator::del_constraint(constraint & c) { switch (c.m_kind) { case LINEAR: m_eq_manager.del(c.m_eq); break; default: UNREACHABLE(); break; } } void bound_propagator::updt_params(params_ref const & p) { m_max_refinements = p.get_uint("bound_max_refinements", 16); m_threshold = p.get_double("bound_threshold", 0.05); m_small_interval = p.get_double("bound_small_interval", 128); m_strict2double = p.get_double("strict2double", 0.00001); } void bound_propagator::get_param_descrs(param_descrs & r) { r.insert("bound_max_refinements", CPK_UINT, "(default: 16) maximum number of bound refinements (per round) for unbounded variables."); r.insert("bound_threshold", CPK_DOUBLE, "(default: 0.05) bound propagation improvement threshold ratio."); } void bound_propagator::collect_statistics(statistics & st) const { st.update("bound conflicts", m_conflicts); st.update("bound propagations", m_propagations); st.update("bound false alarms", m_false_alarms); } void bound_propagator::reset_statistics() { m_conflicts = 0; m_propagations = 0; m_false_alarms = 0; } void bound_propagator::mk_var(var x, bool is_int) { m_is_int.reserve(x+1, false); m_dead.reserve(x+1, true); m_lowers.reserve(x+1, 0); m_uppers.reserve(x+1, 0); m_lower_refinements.reserve(x+1, 0); m_upper_refinements.reserve(x+1, 0); m_watches.reserve(x+1); SASSERT(m_dead[x]); m_is_int[x] = is_int; m_dead[x] = false; m_lowers[x] = 0; m_uppers[x] = 0; m_lower_refinements[x] = 0; m_upper_refinements[x] = 0; m_watches[x].reset(); } void bound_propagator::del_var(var x) { SASSERT(!m_dead[x]); m_dead[x] = true; // mark constraints containing x as dead. wlist & wl = m_watches[x]; wlist::iterator it = wl.begin(); wlist::iterator end = wl.end(); for (; it != end; ++it) { m_constraints[*it].m_dead = true; } } void bound_propagator::mk_eq(unsigned sz, mpq * as, var * xs) { linear_equation * eq = m_eq_manager.mk(sz, as, xs); init_eq(eq); } void bound_propagator::mk_eq(unsigned sz, mpz * as, var * xs) { linear_equation * eq = m_eq_manager.mk(sz, as, xs); init_eq(eq); } void bound_propagator::init_eq(linear_equation * eq) { if (eq == nullptr) return; unsigned c_idx = m_constraints.size(); m_constraints.push_back(constraint()); constraint & new_c = m_constraints.back(); new_c.m_kind = LINEAR; new_c.m_dead = false; new_c.m_timestamp = 0; new_c.m_act = 0; new_c.m_counter = 0; new_c.m_eq = eq; unsigned sz = eq->size(); for (unsigned i = 0; i < sz; i++) { m_watches[eq->x(i)].push_back(c_idx); } if (propagate(c_idx) && scope_lvl() > 0) m_reinit_stack.push_back(c_idx); } void bound_propagator::push() { m_scopes.push_back(scope()); scope & s = m_scopes.back(); s.m_trail_limit = m_trail.size(); s.m_qhead_old = m_qhead; s.m_reinit_stack_limit = m_reinit_stack.size(); s.m_timestamp_old = m_timestamp; s.m_in_conflict = inconsistent(); } void bound_propagator::undo_trail(unsigned old_sz) { SASSERT(old_sz <= m_trail.size()); unsigned i = m_trail.size(); while (i > old_sz) { --i; trail_info & info = m_trail.back(); var x = info.x(); bool is_lower = info.is_lower(); m_trail.pop_back(); bound * b; if (is_lower) { b = m_lowers[x]; m_lowers[x] = b->m_prev; } else { b = m_uppers[x]; m_uppers[x] = b->m_prev; } m.del(b->m_k); b->~bound(); m_allocator.deallocate(sizeof(bound), b); } SASSERT(m_trail.size() == old_sz); } void bound_propagator::pop(unsigned num_scopes) { unsigned lvl = scope_lvl(); SASSERT(num_scopes <= lvl); unsigned new_lvl = lvl - num_scopes; scope & s = m_scopes[new_lvl]; undo_trail(s.m_trail_limit); m_timestamp = s.m_timestamp_old; m_qhead = s.m_qhead_old; if (!s.m_in_conflict) m_conflict = null_var; unsigned reinit_stack_sz = s.m_reinit_stack_limit; m_scopes.shrink(new_lvl); // reinitialize unsigned i = reinit_stack_sz; unsigned j = reinit_stack_sz; unsigned sz = m_reinit_stack.size(); for (; i < sz; i++) { unsigned c_idx = m_reinit_stack[i]; bool p = propagate(c_idx); if (new_lvl > 0 && p) { m_reinit_stack[j] = c_idx; j++; } } m_reinit_stack.shrink(j); } bool bound_propagator::assert_lower_core(var x, mpq & k, bool strict, bkind bk, unsigned c_idx, assumption a) { if (is_int(x)) { if (m.is_int(k)) { if (strict) m.inc(k); } else { m.ceil(k, k); } SASSERT(m.is_int(k)); strict = false; } TRACE("bound_propagator_detail", tout << "new lower x" << x << " " << m.to_string(k) << " strict: " << strict << "\n";); bound * old_lower = m_lowers[x]; if (old_lower) { bool improves = m.gt(k, old_lower->m_k) || (!old_lower->m_strict && strict && m.eq(k, old_lower->m_k)); if (!improves) { if (bk == DERIVED) { TRACE("bound_propagator_detail", tout << "false alarm\n";); m_false_alarms++; } return false; } } if (bk == DERIVED) { TRACE("bound_propagator_derived", tout << "new lower x" << x << " " << m.to_string(k) << " strict: " << strict << "\n";); m_propagations++; } if (scope_lvl() == 0 && bk == DERIVED) bk = AXIOM; // don't need justification at level 0 double approx_k = m.get_double(k); TRACE("new_bound", tout << "x" << x << " lower: " << m.to_string(k) << " approx: " << approx_k << "\n";); #ifdef RELAX_BOUNDS approx_k = PRECISION*floor(approx_k*INV_PRECISION + TOLERANCE); TRACE("new_bound", tout << "x" << x << " lower: " << m.to_string(k) << " relaxed approx: " << approx_k << "\n";); #endif void * mem = m_allocator.allocate(sizeof(bound)); bound * new_lower = new (mem) bound(m, k, approx_k, true, strict, scope_lvl(), m_timestamp, bk, c_idx, a, old_lower); m_timestamp++; m_lowers[x] = new_lower; m_trail.push_back(trail_info(x, true)); m_lower_refinements[x]++; check_feasibility(x); return true; } bool bound_propagator::assert_upper_core(var x, mpq & k, bool strict, bkind bk, unsigned c_idx, assumption a) { if (is_int(x)) { if (m.is_int(k)) { if (strict) m.dec(k); } else { m.floor(k, k); } SASSERT(m.is_int(k)); strict = false; } TRACE("bound_propagator_detail", tout << "new upper x" << x << " " << m.to_string(k) << " strict: " << strict << "\n";); bound * old_upper = m_uppers[x]; if (old_upper) { bool improves = m.lt(k, old_upper->m_k) || (!old_upper->m_strict && strict && m.eq(k, old_upper->m_k)); if (!improves) { if (bk == DERIVED) { TRACE("bound_propagator_detail", tout << "false alarm\n";); m_false_alarms++; } return false; } } if (bk == DERIVED) { m_propagations++; TRACE("bound_propagator_derived", tout << "new upper x" << x << " " << m.to_string(k) << " strict: " << strict << "\n";); } if (scope_lvl() == 0 && bk == DERIVED) bk = AXIOM; // don't need justification at level 0 double approx_k = m.get_double(k); TRACE("new_bound", tout << "x" << x << " upper: " << m.to_string(k) << " approx: " << approx_k << "\n";); #ifdef RELAX_BOUNDS approx_k = PRECISION*ceil(approx_k*INV_PRECISION - TOLERANCE); TRACE("new_bound", tout << "x" << x << " upper: " << m.to_string(k) << " relaxed approx: " << approx_k << "\n";); #endif void * mem = m_allocator.allocate(sizeof(bound)); bound * new_upper = new (mem) bound(m, k, approx_k, false, strict, scope_lvl(), m_timestamp, bk, c_idx, a, m_uppers[x]); m_timestamp++; m_uppers[x] = new_upper; m_trail.push_back(trail_info(x, false)); m_upper_refinements[x]++; check_feasibility(x); return true; } bool bound_propagator::get_interval_size(var x, double & r) const { bound * l = m_lowers[x]; bound * u = m_uppers[x]; if (l && u) { r = u->m_approx_k - l->m_approx_k; return true; } return false; } template bool bound_propagator::relevant_bound(var x, double new_k) const { TRACE("bound_propagator_detail", tout << "relevant_bound x" << x << " " << new_k << " LOWER: " << LOWER << "\n"; if (LOWER && has_lower(x)) tout << "old: " << m.to_string(m_lowers[x]->m_k) << " | " << m_lowers[x]->m_approx_k << "\n"; if (!LOWER && has_upper(x)) tout << "old: " << m.to_string(m_uppers[x]->m_k) << " | " << m_uppers[x]->m_approx_k << "\n";); bound * b = LOWER ? m_lowers[x] : m_uppers[x]; if (b == nullptr) return true; // variable did not have a bound double interval_size; bool bounded = get_interval_size(x, interval_size); if (!is_int(x)) { // check if the improvement is significant double improvement; double abs_k = b->m_approx_k; if (abs_k < 0.0) abs_k -= abs_k; if (bounded) improvement = m_threshold * std::max(std::min(interval_size, abs_k), 1.0); else improvement = m_threshold * std::max(abs_k, 1.0); if (LOWER) { if (new_k <= b->m_approx_k + improvement) { TRACE("bound_propagator", tout << "LOWER new: " << new_k << " old: " << b->m_approx_k << " improvement is too small\n";); return false; // improvement is too small } } else { if (new_k >= b->m_approx_k - improvement) { TRACE("bound_propagator", tout << "UPPER new: " << new_k << " old: " << b->m_approx_k << " improvement is too small\n";); return false; // improvement is too small } } } else { if (LOWER) { if (new_k < b->m_approx_k + 1.0) return false; // no improvement } else { if (new_k > b->m_approx_k - 1.0) return false; // no improvement } } if (bounded && interval_size <= m_small_interval) return true; if (LOWER) return m_lower_refinements[x] < m_max_refinements; else return m_upper_refinements[x] < m_max_refinements; } bool bound_propagator::relevant_lower(var x, double approx_k) const { return relevant_bound(x, approx_k); } bool bound_propagator::relevant_upper(var x, double approx_k) const { return relevant_bound(x, approx_k); } void bound_propagator::check_feasibility(var x) { if (inconsistent()) return; bound * l = m_lowers[x]; bound * u = m_uppers[x]; if (l && u) { if (m.lt(l->m_k, u->m_k)) return; if (!l->m_strict && !u->m_strict && m.eq(l->m_k, u->m_k)) return; m_conflict = x; m_conflicts++; SASSERT(inconsistent()); TRACE("bound_propagator", tout << "inconsistency detected: x" << x << "\n"; display(tout);); } } void bound_propagator::propagate() { m_to_reset_ts.reset(); while (m_qhead < m_trail.size()) { if (inconsistent()) break; trail_info & info = m_trail[m_qhead]; var x = info.x(); bool is_lower = info.is_lower(); bound * b = is_lower ? m_lowers[x] : m_uppers[x]; SASSERT(b); unsigned ts = b->m_timestamp; TRACE("bound_propagator_detail", tout << "propagating x" << x << "\n";); m_qhead++; wlist const & wl = m_watches[x]; wlist::const_iterator it = wl.begin(); wlist::const_iterator end = wl.end(); for (; it != end; ++it) { unsigned c_idx = *it; constraint & c = m_constraints[c_idx]; // We don't need to visit c if it was already propagated using b. // Whenever we visit c we store in c.m_timestamp the current timestamp // So, we know that c was already propagated any bound using bounds with timestamp lower than c.m_timestamp. if (ts >= c.m_timestamp) { if (c.m_timestamp == 0) m_to_reset_ts.push_back(c_idx); c.m_timestamp = m_timestamp; propagate(c_idx); } } } unsigned_vector::iterator it = m_to_reset_ts.begin(); unsigned_vector::iterator end = m_to_reset_ts.end(); for (; it != end; ++it) m_constraints[*it].m_timestamp = 0; } bool bound_propagator::propagate(unsigned c_idx) { constraint const & c = m_constraints[c_idx]; if (c.m_dead) return false; if (c.m_kind == LINEAR) return propagate_eq(c_idx); return false; } bool bound_propagator::propagate_eq(unsigned c_idx) { constraint const & c = m_constraints[c_idx]; linear_equation * eq = c.m_eq; #if 0 { static unsigned counter = 0; static unsigned visited = 0; counter++; visited += eq->size(); if (counter % 1000 == 0) verbose_stream() << "[bound-propagator] :propagate-eq " << counter << " :visited-vars " << visited << std::endl; } #endif TRACE("bound_propagator_detail", tout << "propagating using eq: "; m_eq_manager.display(tout, *eq); tout << "\n";); // ll = (Sum_{a_i < 0} -a_i*lower(x_i)) + (Sum_{a_i > 0} -a_i * upper(x_i)) // uu = (Sum_{a_i > 0} -a_i*lower(x_i)) + (Sum_{a_i < 0} -a_i * upper(x_i)) unsigned ll_i = UINT_MAX; // position of the variable that couldn't contribute to ll unsigned uu_i = UINT_MAX; // position of the variable that coundn't contribute to uu bool ll_failed = false; bool uu_failed = false; double ll = 0.0; double uu = 0.0; unsigned sz = eq->size(); for (unsigned i = 0; i < sz; i++) { var x_i = eq->x(i); double a_i = eq->approx_a(i); bound * l_i = m_lowers[x_i]; bound * u_i = m_uppers[x_i]; if (a_i < 0.0) { if (!ll_failed) { if (l_i == nullptr) { if (ll_i == UINT_MAX) ll_i = i; else ll_failed = true; } else { ll -= a_i * l_i->m_approx_k; } } if (!uu_failed) { if (u_i == nullptr) { if (uu_i == UINT_MAX) uu_i = i; else uu_failed = true; } else { uu -= a_i * u_i->m_approx_k; } } } else { if (!ll_failed) { if (u_i == nullptr) { if (ll_i == UINT_MAX) ll_i = i; else ll_failed = true; } else { ll -= a_i * u_i->m_approx_k; } } if (!uu_failed) { if (l_i == nullptr) { if (uu_i == UINT_MAX) uu_i = i; else uu_failed = true; } else { uu -= a_i * l_i->m_approx_k; } } } if (ll_failed && uu_failed) return false; // nothing to propagate } bool propagated = false; SASSERT(!ll_failed || !uu_failed); if (ll_i == UINT_MAX || uu_i == UINT_MAX) { for (unsigned i = 0; i < sz; i++) { var x_i = eq->x(i); double a_i = eq->approx_a(i); bound * l_i = m_lowers[x_i]; bound * u_i = m_uppers[x_i]; // ll = (Sum_{a_i < 0} -a_i*lower(x_i)) + (Sum_{a_i > 0} -a_i * upper(x_i)) // uu = (Sum_{a_i > 0} -a_i*lower(x_i)) + (Sum_{a_i < 0} -a_i * upper(x_i)) if (ll_i == UINT_MAX) { // can propagate a lower bound for a_i*x_i if (a_i > 0.0) { // can propagate a lower bound for x_i double new_k = (ll + a_i * u_i->m_approx_k)/a_i; if (relevant_lower(x_i, new_k) && propagate_lower(c_idx, i)) propagated = true; } else { // a_i < 0.0 // can propagate a upper bound for x_i double new_k = (ll + a_i * l_i->m_approx_k)/a_i; if (relevant_upper(x_i, new_k) && propagate_upper(c_idx, i)) propagated = true; } } if (uu_i == UINT_MAX) { // can propagate an upper bound for a_i*x_i if (a_i > 0.0) { // can propagate a upper bound for x_i double new_k = (uu + a_i * l_i->m_approx_k)/a_i; if (relevant_upper(x_i, new_k) && propagate_upper(c_idx, i)) propagated = true; } else { // a_i < 0.0 // can propagate a lower bound for x_i double new_k = (uu + a_i * u_i->m_approx_k)/a_i; if (relevant_lower(x_i, new_k) && propagate_lower(c_idx, i)) propagated = true; } } } } if (!ll_failed && ll_i != UINT_MAX) { // can propagate a lower bound for the monomial at position ll_i var x_i = eq->x(ll_i); double a_i = eq->approx_a(ll_i); double new_k = ll/a_i; if (a_i > 0.0) { if (relevant_lower(x_i, new_k) && propagate_lower(c_idx, ll_i)) propagated = true; } else { if (relevant_upper(x_i, new_k) && propagate_upper(c_idx, ll_i)) propagated = true; } } if (!uu_failed && uu_i != UINT_MAX) { // can propagate a upper bound for the monomial at position uu_i var x_i = eq->x(uu_i); double a_i = eq->approx_a(uu_i); double new_k = uu/a_i; if (a_i > 0.0) { if (relevant_upper(x_i, new_k) && propagate_upper(c_idx, uu_i)) propagated = true; } else { if (relevant_lower(x_i, new_k) && propagate_lower(c_idx, uu_i)) propagated = true; } } return propagated; } /** \brief Try to propagate a lower bound for the variable stored at position i, using mpq's (rationals). When this method is invoked, we know that all other variables have the "right" bounds, and using doubles we improve the current known bound. */ bool bound_propagator::propagate_lower(unsigned c_idx, unsigned i) { constraint const & c = m_constraints[c_idx]; linear_equation * eq = c.m_eq; var x_i = eq->x(i); mpz const & a_i = eq->a(i); unsigned sz = eq->size(); mpq k; bool strict = false; bool neg_a_i = m.is_neg(a_i); for (unsigned j = 0; j < sz; j++) { if (i == j) continue; var x_j = eq->x(j); mpz const & a_j = eq->a(j); bound * b_j = (m.is_neg(a_j) == neg_a_i) ? m_uppers[x_j] : m_lowers[x_j]; TRACE("bound_propagator_step_detail", tout << "k: " << m.to_string(k) << " b_j->m_k: " << m.to_string(b_j->m_k) << " a_j: " << m.to_string(a_j) << "\n";); SASSERT(b_j); if (b_j->m_strict) strict = true; m.addmul(k, a_j, b_j->m_k, k); } TRACE("bound_propagator_step_detail", tout << "k: " << m.to_string(k) << "\n";); m.neg(k); m.div(k, a_i, k); TRACE("bound_propagator_step", tout << "propagating lower x" << x_i << " " << m.to_string(k) << " strict: " << strict << " using\n"; m_eq_manager.display(tout, *eq); tout << "\n"; display_bounds_of(tout, *eq);); bool r = assert_lower_core(x_i, k, strict, DERIVED, c_idx, null_assumption); m.del(k); return r; } /** \brief Try to propagate a upper bound for the variable stored at position i, using mpq's (rationals). When this method is invoked, we know that all other variables have the "right" bounds, and using doubles we improve the current known bound. */ bool bound_propagator::propagate_upper(unsigned c_idx, unsigned i) { constraint const & c = m_constraints[c_idx]; linear_equation * eq = c.m_eq; var x_i = eq->x(i); mpz const & a_i = eq->a(i); unsigned sz = eq->size(); mpq k; bool strict = false; bool neg_a_i = m.is_neg(a_i); for (unsigned j = 0; j < sz; j++) { if (i == j) continue; var x_j = eq->x(j); mpz const & a_j = eq->a(j); bound * b_j = (m.is_neg(a_j) == neg_a_i) ? m_lowers[x_j] : m_uppers[x_j]; SASSERT(b_j); if (b_j->m_strict) strict = true; m.addmul(k, a_j, b_j->m_k, k); } m.neg(k); m.div(k, a_i, k); TRACE("bound_propagator_step", tout << "propagating upper x" << x_i << " " << m.to_string(k) << " strict: " << strict << " using\n"; m_eq_manager.display(tout, *eq); tout << "\n"; display_bounds_of(tout, *eq);); bool r = assert_upper_core(x_i, k, strict, DERIVED, c_idx, null_assumption); m.del(k); return r; } void bound_propagator::reset() { undo_trail(0); del_constraints_core(); m_constraints.finalize(); m_is_int.finalize(); m_dead.finalize(); m_lowers.finalize(); m_uppers.finalize(); m_watches.finalize(); m_trail.finalize(); m_qhead = 0; m_reinit_stack.finalize(); m_lower_refinements.finalize(); m_upper_refinements.finalize(); m_timestamp = 0; m_conflict = null_var; m_scopes.finalize(); } bool bound_propagator::lower(var x, mpq & k, bool & strict, unsigned & ts) const { bound * b = m_lowers[x]; if (!b) return false; m.set(k, b->m_k); strict = b->m_strict; ts = b->m_timestamp; return true; } bool bound_propagator::upper(var x, mpq & k, bool & strict, unsigned & ts) const { bound * b = m_uppers[x]; if (!b) return false; m.set(k, b->m_k); strict = b->m_strict; ts = b->m_timestamp; return true; } bound_propagator::bound * bound_propagator::bound::at(unsigned timestamp) { bound * r = this; while (r != nullptr && r->m_timestamp >= timestamp) r = r->m_prev; return r; } /** \brief Return true if the coefficient of x in eq is positive */ bool bound_propagator::is_a_i_pos(linear_equation const & eq, var x) const { unsigned i = eq.pos(x); if (i == UINT_MAX) return false; return m.is_pos(eq.a(i)); } void bound_propagator::explain(var x, bound * b, unsigned ts, assumption_vector & ex) const { if (!b) return; b = b->at(ts); if (!b) return; if (b->m_kind == AXIOM || b->m_kind == DECISION) return; if (b->m_kind == ASSUMPTION) { ex.push_back(b->m_assumption); return; } svector & todo = const_cast(this)->m_todo; todo.reset(); unsigned qhead = 0; todo.push_back(var_bound(x, b)); b->m_mark = true; while (qhead < todo.size()) { var_bound & vb = todo[qhead]; qhead ++; var x = vb.first; bound * b = vb.second; SASSERT(b->kind() == ASSUMPTION || b->kind() == DERIVED); if (b->kind() == ASSUMPTION) { ex.push_back(b->m_assumption); continue; } SASSERT(b->kind() == DERIVED); constraint const & c = m_constraints[b->m_constraint_idx]; switch (c.m_kind) { case LINEAR: { linear_equation * eq = c.m_eq; bool is_lower = b->is_lower(); if (!is_a_i_pos(*eq, x)) is_lower = !is_lower; unsigned sz = eq->size(); for (unsigned i = 0; i < sz; i++) { var x_i = eq->x(i); if (x_i == x) continue; bound * b = (m.is_neg(eq->a(i)) == is_lower) ? m_lowers[x_i] : m_uppers[x_i]; SASSERT(b); if (b->kind() == DERIVED || b->kind() == ASSUMPTION) { if (!b->m_mark) { b->m_mark = true; todo.push_back(var_bound(x_i, b)); } } } break; } default: break; } } unsigned sz = todo.size(); for (unsigned i = 0; i < sz; i++) todo[i].second->m_mark = false; todo.reset(); } /** \brief Compute lower (upper) bound for the linear polynomial as[0]*xs[0] + ... + as[sz-1]*xs[sz-1] Return false if the lower (upper) bound is -oo (oo) */ template bool bound_propagator::get_bound(unsigned sz, Numeral const * as, var const * xs, mpq & r, bool & st) const { st = false; m.reset(r); for (unsigned i = 0; i < sz; i++) { var x_i = xs[i]; Numeral const & a_i = as[i]; if (m.is_zero(a_i)) continue; bound * b = (m.is_neg(a_i) == LOWER) ? m_uppers[x_i] : m_lowers[x_i]; if (!b) { m.reset(r); return false; } if (b->m_strict) st = true; m.addmul(r, a_i, b->m_k, r); } return true; } bool bound_propagator::lower(unsigned sz, mpq const * as, var const * xs, mpq & r, bool & st) const { return get_bound(sz, as, xs, r, st); } bool bound_propagator::upper(unsigned sz, mpq const * as, var const * xs, mpq & r, bool & st) const { return get_bound(sz, as, xs, r, st); } void bound_propagator::display_bounds_of(std::ostream & out, linear_equation const & eq) const { for (unsigned i = 0; i < eq.size(); i++) { display_var_bounds(out, eq.x(i)); out << "\n"; } } void bound_propagator::display_var_bounds(std::ostream & out, var x, bool approx, bool precise) const { if (m_lowers[x]) { if (precise) out << m.to_string(m_lowers[x]->m_k); if (precise && approx) out << " | "; if (approx) out << m_lowers[x]->m_approx_k; out << " " << (m_lowers[x]->m_strict ? "<" : "<="); } else { out << "-oo <"; } out << " x" << x << " "; if (m_uppers[x]) { out << (m_uppers[x]->m_strict ? "<" : "<=") << " "; if (precise) out << m.to_string(m_uppers[x]->m_k); if (precise && approx) out << " | "; if (approx) out << m_uppers[x]->m_approx_k; } else { out << "< oo"; } } void bound_propagator::display_bounds(std::ostream & out, bool approx, bool precise) const { unsigned num_vars = m_dead.size(); for (unsigned x = 0; x < num_vars; x++) { if (!is_dead(x)) { display_var_bounds(out, x, approx, precise); out << "\n"; } } } void bound_propagator::display_constraints(std::ostream & out) const { constraint_vector::const_iterator it = m_constraints.begin(); constraint_vector::const_iterator end = m_constraints.end(); for (; it != end; ++it) { constraint const & c = *it; if (c.m_kind == LINEAR) { m_eq_manager.display(out, *(c.m_eq)); out << "\n"; } } } void bound_propagator::display(std::ostream & out) const { display_bounds(out); display_constraints(out); } z3-z3-4.8.7/src/tactic/arith/bound_propagator.h000066400000000000000000000246311356505360400213200ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: bound_propagator.h Abstract: Bound propagators for arithmetic. Support class for implementing strategies and search procedures Author: Leonardo de Moura (leonardo) 2011-06-18. Revision History: --*/ #ifndef BOUND_PROPAGATOR_H_ #define BOUND_PROPAGATOR_H_ #include "util/mpq.h" #include "util/vector.h" #include "util/params.h" #include "util/statistics.h" #include "util/numeral_buffer.h" #include "tactic/arith/linear_equation.h" class bound_propagator { public: typedef unsigned var; typedef unsigned assumption; typedef unsynch_mpq_manager numeral_manager; typedef unsigned_vector assumption_vector; typedef unsigned constraint_id; typedef numeral_buffer mpz_buffer; typedef svector double_vector; static const assumption null_assumption = UINT_MAX; static const var null_var = UINT_MAX; static const unsigned null_constraint_idx = UINT_MAX; class trail_info { unsigned m_x_lower; public: trail_info(var x, bool is_lower):m_x_lower((x << 1) + static_cast(is_lower)) {} trail_info():m_x_lower(UINT_MAX) {} var x() const { return m_x_lower >> 1; } bool is_lower() const { return (m_x_lower & 1) != 0; } }; protected: enum ckind { LINEAR // only linear equalities so far. }; /** \brief Constraints don't need justification. */ class constraint { friend class bound_propagator; unsigned m_kind:2; unsigned m_dead:1; unsigned m_timestamp; // Constraint tried to propagate new bounds using bounds with timestamp < m_timestamp. unsigned m_act; // activity unsigned m_counter; // number of times the constraint propagated union { linear_equation * m_eq; }; }; enum bkind { AXIOM, // doesn't need justification ASSUMPTION, // aka external case-split, it is used to connect with external search engine. DERIVED, // implied DECISION // internal case-split }; struct bound { mpq m_k; double m_approx_k; unsigned m_lower:1; unsigned m_strict:1; unsigned m_mark:1; unsigned m_kind:2; unsigned m_level:27; unsigned m_timestamp; union { assumption m_assumption; unsigned m_constraint_idx; }; bound * m_prev; bound(numeral_manager & m, mpq const & k, double approx_k, bool lower, bool strict, unsigned lvl, unsigned ts, bkind bk, unsigned c_idx, assumption a, bound * prev); bound * at(unsigned timestamp); bkind kind() const { return static_cast(m_kind); } bool is_lower() const { return m_lower != 0; } }; typedef ptr_vector var2bound; typedef svector var_vector; typedef svector constraint_vector; typedef unsigned_vector c_idx_vector; typedef c_idx_vector wlist; typedef small_object_allocator allocator; typedef linear_equation_manager lin_eq_manager; numeral_manager & m; allocator & m_allocator; lin_eq_manager m_eq_manager; constraint_vector m_constraints; char_vector m_is_int; char_vector m_dead; var2bound m_lowers; var2bound m_uppers; vector m_watches; svector m_trail; unsigned m_qhead; c_idx_vector m_reinit_stack; unsigned_vector m_lower_refinements; // number of times a lower bound was propagated for each variable (loop prevention) unsigned_vector m_upper_refinements; // number of times a upper bound was propagated for each variable (loop prevention) unsigned m_timestamp; var m_conflict; mpq m_tmp; struct scope { unsigned m_trail_limit; unsigned m_qhead_old; unsigned m_reinit_stack_limit; unsigned m_timestamp_old:31; unsigned m_in_conflict:1; }; svector m_scopes; unsigned_vector m_to_reset_ts; // temp field: ids of the constraints we must reset the field m_timestamp // config unsigned m_max_refinements; // maximum number of refinements per round double m_small_interval; double m_threshold; // improvement threshold double m_strict2double; // statistics unsigned m_conflicts; unsigned m_propagations; unsigned m_false_alarms; void del_constraint(constraint & cnstr); void del_constraints_core(); template bool relevant_bound(var x, double approx_k) const; bool relevant_lower(var x, double approx_k) const; bool relevant_upper(var x, double approx_k) const; bool get_interval_size(var x, double & r) const; void check_feasibility(var x); bool assert_lower_core(var x, mpq & k, bool strict, bkind bk, unsigned c_idx, assumption a); bool assert_upper_core(var x, mpq & k, bool strict, bkind bk, unsigned c_idx, assumption a); bool propagate(unsigned c_idx); bool propagate_eq(unsigned c_idx); bool propagate_lower(unsigned c_idx, unsigned i); bool propagate_upper(unsigned c_idx, unsigned i); void undo_trail(unsigned old_sz); typedef std::pair var_bound; svector m_todo; void explain(var x, bound * b, unsigned ts, assumption_vector & ex) const; bool is_a_i_pos(linear_equation const & eq, var x) const; template bool get_bound(unsigned sz, Numeral const * as, var const * xs, mpq & r, bool & st) const; void init_eq(linear_equation * eq); public: bound_propagator(numeral_manager & m, allocator & a, params_ref const & p); ~bound_propagator(); void updt_params(params_ref const & p); static void get_param_descrs(param_descrs & r); void collect_statistics(statistics & st) const; void reset_statistics(); double strict2double() const { return m_strict2double; } bool is_int(var x) const { return m_is_int[x] != 0; } unsigned scope_lvl() const { return m_scopes.size(); } void mk_var(var x, bool is_int); void del_var(var x); bool is_dead(var x) const { return m_dead[x] != 0; } void mk_eq(unsigned sz, mpq * as, var * xs); void mk_eq(unsigned sz, mpz * as, var * xs); void del_constraints(); void assert_lower(var x, mpq const & k, bool strict, assumption a = null_assumption) { m.set(m_tmp, k); assert_lower_core(x, m_tmp, strict, a == null_assumption ? AXIOM : ASSUMPTION, 0, a); } void assert_upper(var x, mpq const & k, bool strict, assumption a = null_assumption) { m.set(m_tmp, k); assert_upper_core(x, m_tmp, strict, a == null_assumption ? AXIOM : ASSUMPTION, 0, a); } void assert_decided_lower(var x, mpq const & k) { m.set(m_tmp, k); assert_lower_core(x, m_tmp, false, DECISION, 0, null_assumption); } void assert_decided_upper(var x, mpq const & k) { m.set(m_tmp, k); assert_upper_core(x, m_tmp, false, DECISION, 0, null_assumption); } void propagate(); void push(); void pop(unsigned num_scopes); void reset(); bool has_lower(var x) const { return m_lowers[x] != 0; } bool has_upper(var x) const { return m_uppers[x] != 0; } bool lower(var x, mpq & k, bool & strict, unsigned & ts) const; bool upper(var x, mpq & k, bool & strict, unsigned & ts) const; bool is_fixed(var x) const { return has_lower(x) && has_upper(x) && m.eq(m_lowers[x]->m_k, m_uppers[x]->m_k) && !inconsistent(); } mpq const & lower(var x, bool & strict) const { SASSERT(has_lower(x)); bound * b = m_lowers[x]; strict = b->m_strict; return b->m_k; } mpq const & upper(var x, bool & strict) const { SASSERT(has_upper(x)); bound * b = m_uppers[x]; strict = b->m_strict; return b->m_k; } mpq const & lower(var x) const { SASSERT(has_lower(x)); return m_lowers[x]->m_k; } mpq const & upper(var x) const { SASSERT(has_upper(x)); return m_uppers[x]->m_k; } double approx_lower(var x) const { SASSERT(has_lower(x)); return m_lowers[x]->m_strict ? m_lowers[x]->m_approx_k + m_strict2double : m_lowers[x]->m_approx_k; } double approx_upper(var x) const { SASSERT(has_upper(x)); return m_uppers[x]->m_strict ? m_uppers[x]->m_approx_k - m_strict2double : m_uppers[x]->m_approx_k; } bool is_zero(var x) const { return has_lower(x) && has_upper(x) && m.is_zero(lower(x)) && m.is_zero(upper(x)); } void explain_lower(var x, unsigned ts, assumption_vector & ex) const { explain(x, m_lowers[x], ts, ex); } void explain_upper(var x, unsigned ts, assumption_vector & ex) const { explain(x, m_uppers[x], ts, ex); } void explain_lower(var x, assumption_vector & ex) const { explain_lower(x, m_timestamp, ex); } void explain_upper(var x, assumption_vector & ex) const { explain_upper(x, m_timestamp, ex); } var conflict_var() const { return m_conflict; } bool inconsistent() const { return m_conflict != null_var; } unsigned trail_size() const { return m_trail.size(); } unsigned qhead() const { return m_qhead; } typedef svector::const_iterator trail_iterator; trail_iterator begin_trail() const { return m_trail.begin(); } trail_iterator end_trail() const { return m_trail.end(); } bool lower(unsigned sz, mpq const * as, var const * xs, mpq & r, bool & st) const; bool upper(unsigned sz, mpq const * as, var const * xs, mpq & r, bool & st) const; void display(std::ostream & out) const; void display_var_bounds(std::ostream & out, var x, bool approx = true, bool precise = true) const; void display_bounds(std::ostream & out, bool approx = true, bool precise = true) const; void display_precise_bounds(std::ostream & out) const { display_bounds(out, false, true); } void display_approx_bounds(std::ostream & out) const { display_bounds(out, true, false); } void display_constraints(std::ostream & out) const; void display_bounds_of(std::ostream & out, linear_equation const & eq) const; unsigned get_num_false_alarms() const { return m_false_alarms; } unsigned get_num_propagations() const { return m_propagations; } }; #endif z3-z3-4.8.7/src/tactic/arith/bv2int_rewriter.cpp000066400000000000000000000461271356505360400214410ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: bv2int_rewriter.cpp Abstract: Basic rewriting rules for bv2int propagation. Author: Nikolaj (nbjorner) 2011-05-05 Notes: --*/ #include "tactic/arith/bv2int_rewriter.h" #include "tactic/tactic_exception.h" #include "ast/rewriter/rewriter_def.h" #include "ast/ast_pp.h" #include "ast/ast_util.h" void bv2int_rewriter_ctx::update_params(params_ref const& p) { m_max_size = p.get_uint("max_bv_size", m_max_size); } struct lt_rational { bool operator()(rational const& a, rational const& b) const { return a < b; } }; void bv2int_rewriter_ctx::collect_power2(goal const& s) { ast_manager& m = m_trail.get_manager(); arith_util arith(m); bv_util bv(m); for (unsigned j = 0; j < s.size(); ++j) { expr* f = s.form(j); if (!m.is_or(f)) continue; unsigned sz = to_app(f)->get_num_args(); expr* x, *y, *v = nullptr; rational n; vector bounds; bool is_int, ok = true; for (unsigned i = 0; ok && i < sz; ++i) { expr* e = to_app(f)->get_arg(i); if (!m.is_eq(e, x, y)) { ok = false; break; } if (arith.is_numeral(y, n, is_int) && is_int && (x == v || v == nullptr)) { v = x; bounds.push_back(n); } else if (arith.is_numeral(x, n, is_int) && is_int && (y == v || v == nullptr)) { v = y; bounds.push_back(n); } else { ok = false; break; } } if (!ok || !v) continue; SASSERT(!bounds.empty()); lt_rational lt; // lt is a total order on rationals. std::sort(bounds.begin(), bounds.end(), lt); rational p(1); unsigned num_bits = 0; for (unsigned i = 0; ok && i < bounds.size(); ++i) { ok = (p == bounds[i]); p *= rational(2); ++num_bits; } if (!ok) continue; unsigned log2 = 0; for (unsigned i = 1; i <= num_bits; i *= 2) ++log2; if(log2 == 0) continue; expr* logx = m.mk_fresh_const("log2_v", bv.mk_sort(log2)); logx = bv.mk_zero_extend(num_bits - log2, logx); m_trail.push_back(logx); TRACE("bv2int_rewriter", tout << mk_pp(v, m) << " |-> " << mk_pp(logx, m) << "\n";); m_power2.insert(v, logx); } } bool bv2int_rewriter_ctx::is_power2(expr* x, expr*& log_x) { return m_power2.find(x, log_x); } bv2int_rewriter::bv2int_rewriter(ast_manager & m, bv2int_rewriter_ctx& ctx) :m_manager(m), m_ctx(ctx), m_bv(m), m_arith(m) { } br_status bv2int_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { if (f->get_family_id() == m_arith.get_family_id()) { switch (f->get_decl_kind()) { case OP_NUM: return BR_FAILED; case OP_LE: SASSERT(num_args == 2); return mk_le(args[0], args[1], result); case OP_GE: SASSERT(num_args == 2); return mk_ge(args[0], args[1], result); case OP_LT: SASSERT(num_args == 2); return mk_lt(args[0], args[1], result); case OP_GT: SASSERT(num_args == 2); return mk_gt(args[0], args[1], result); case OP_ADD: return mk_add(num_args, args, result); case OP_MUL: return mk_mul(num_args, args, result); case OP_SUB: return mk_sub(num_args, args, result); case OP_DIV: return BR_FAILED; case OP_IDIV: SASSERT(num_args == 2); return mk_idiv(args[0], args[1], result); case OP_MOD: SASSERT(num_args == 2); return mk_mod(args[0], args[1], result); case OP_REM: SASSERT(num_args == 2); return mk_rem(args[0], args[1], result); case OP_UMINUS: SASSERT(num_args == 1); return mk_uminus(args[0], result); case OP_TO_REAL: return BR_FAILED; case OP_TO_INT: return BR_FAILED; case OP_IS_INT: return BR_FAILED; default: return BR_FAILED; } } if (f->get_family_id() == m().get_basic_family_id()) { switch (f->get_decl_kind()) { case OP_EQ: SASSERT(num_args == 2); return mk_eq(args[0], args[1], result); case OP_ITE: SASSERT(num_args == 3); return mk_ite(args[0], args[1], args[2], result); case OP_DISTINCT: if (num_args >= 2 && m_arith.is_int(args[0])) { expr_ref_vector eqs(m()); for (unsigned i = 0; i < num_args; ++i) { for (unsigned j = i + 1; j < num_args; ++j) { if (BR_DONE != mk_eq(args[i], args[j], result)) { return BR_FAILED; } eqs.push_back(result); } } result = m().mk_not(mk_or(eqs)); return BR_DONE; } return BR_FAILED; default: return BR_FAILED; } } return BR_FAILED; } br_status bv2int_rewriter::mk_le(expr * s, expr * t, expr_ref & result) { expr_ref s1(m()), t1(m()), s2(m()), t2(m()); if (is_bv2int(s, s1) && is_bv2int(t, t1)) { align_sizes(s1, t1, false); result = m_bv.mk_ule(s1, t1); return BR_DONE; } if (is_bv2int_diff(s, s1, s2) && is_bv2int_diff(t, t1, t2)) { // s1 - s2 <= t1 - t2 // <=> // s1 + t2 <= t1 + s2 // s1 = mk_bv_add(s1, t2, false); t1 = mk_bv_add(t1, s2, false); align_sizes(s1, t1, false); result = m_bv.mk_ule(s1, t1); return BR_DONE; } if (is_sbv2int(s, s1) && is_sbv2int(t, t1)) { align_sizes(s1, t1, true); result = m_bv.mk_sle(s1, t1); return BR_DONE; } return BR_FAILED; } br_status bv2int_rewriter::mk_lt(expr * arg1, expr * arg2, expr_ref & result) { result = m().mk_not(m_arith.mk_le(arg2, arg1)); return BR_REWRITE2; } br_status bv2int_rewriter::mk_ge(expr * arg1, expr * arg2, expr_ref & result) { return mk_le(arg2, arg1, result); } br_status bv2int_rewriter::mk_gt(expr * arg1, expr * arg2, expr_ref & result) { result = m().mk_not(m_arith.mk_le(arg1, arg2)); return BR_REWRITE2; } br_status bv2int_rewriter::mk_ite(expr* c, expr* s, expr* t, expr_ref& result) { expr_ref s1(m()), t1(m()); if (is_bv2int(s, s1) && is_bv2int(t, t1)) { align_sizes(s1, t1, false); result = m_bv.mk_bv2int(m().mk_ite(c, s1, t1)); return BR_DONE; } if (is_sbv2int(s, s1) && is_sbv2int(t, t1)) { align_sizes(s1, t1, true); result = mk_sbv2int(m().mk_ite(c, s1, t1)); return BR_DONE; } return BR_FAILED; } br_status bv2int_rewriter::mk_eq(expr * s, expr * t, expr_ref & result) { expr_ref s1(m()), t1(m()), s2(m()), t2(m()); if (is_bv2int(s, s1) && is_bv2int(t, t1)) { align_sizes(s1, t1, false); result = m().mk_eq(s1, t1); return BR_DONE; } if (is_bv2int_diff(s, s1, s2) && is_bv2int_diff(t, t1, t2)) { s1 = mk_bv_add(s1, t2, false); t1 = mk_bv_add(s2, t1, false); align_sizes(s1, t1, false); result = m().mk_eq(s1, t1); return BR_DONE; } if (is_sbv2int(s, s1) && is_sbv2int(t, t1)) { align_sizes(s1, t1, true); result = m().mk_eq(s1, t1); return BR_DONE; } return BR_FAILED; } br_status bv2int_rewriter::mk_idiv(expr * arg1, expr * arg2, expr_ref & result) { // TBD return BR_FAILED; } br_status bv2int_rewriter::mk_mod(expr * s, expr * t, expr_ref & result) { expr_ref s1(m()), s2(m()), t1(m()); if (is_bv2int(s, s1) && is_bv2int(t, t1)) { align_sizes(s1, t1, false); result = m_bv.mk_bv2int(m_bv.mk_bv_urem(s1, t1)); TRACE("bv2int_rewriter", tout << mk_pp(result,m()) << "\n";); return BR_DONE; } // // (s1 - s2) mod t1 = (s1 + (t1 - (s2 mod t1))) mod t1 // if (is_bv2int_diff(s, s1, s2) && is_bv2int(t, t1)) { expr_ref u1(m()); align_sizes(s1, t1, false); u1 = m_bv.mk_bv_urem(s1, t1); u1 = m_bv.mk_bv_sub(t1, u1); u1 = mk_bv_add(s1, u1, false); align_sizes(u1, t1, false); result = m_bv.mk_bv2int(m_bv.mk_bv_urem(u1, t1)); TRACE("bv2int_rewriter", tout << mk_pp(result,m()) << "\n";); return BR_DONE; } #if 0 // TBD: check semantics if (is_sbv2int(s, s1) && is_sbv2int(t, t1)) { align_sizes(s1, t1, true); result = mk_sbv2int(m_bv.mk_bv_srem(s1, t1)); return BR_DONE; } #endif return BR_FAILED; } br_status bv2int_rewriter::mk_rem(expr * arg1, expr * arg2, expr_ref & result) { // TBD return BR_FAILED; } br_status bv2int_rewriter::mk_uminus(expr * s, expr_ref & result) { expr_ref s1(m()), s2(m()); if (is_bv2int_diff(s, s1, s2)) { result = m_arith.mk_sub(m_bv.mk_bv2int(s2), m_bv.mk_bv2int(s1)); return BR_DONE; } if (is_sbv2int(s, s1)) { result = mk_sbv2int(m_bv.mk_bv_neg(s1)); return BR_DONE; } return BR_FAILED; } br_status bv2int_rewriter::mk_add(unsigned num_args, expr * const* args, expr_ref& result) { br_status r = BR_DONE; SASSERT(num_args > 0); result = args[0]; for (unsigned i = 1; r == BR_DONE && i < num_args; ++i) { r = mk_add(result, args[i], result); } return r; } void bv2int_rewriter::align_sizes(expr_ref& s, expr_ref& t, bool is_signed) { unsigned sz1 = m_bv.get_bv_size(s); unsigned sz2 = m_bv.get_bv_size(t); if (sz1 > sz2 && is_signed) { t = mk_extend(sz1-sz2, t, true); } if (sz1 > sz2 && !is_signed) { t = mk_extend(sz1-sz2, t, false); } if (sz1 < sz2 && is_signed) { s = mk_extend(sz2-sz1, s, true); } if (sz1 < sz2 && !is_signed) { s = mk_extend(sz2-sz1, s, false); } } bool bv2int_rewriter::is_zero(expr* n) { rational r; unsigned sz; return m_bv.is_numeral(n, r, sz) && r.is_zero(); } expr* bv2int_rewriter::mk_bv_add(expr* s, expr* t, bool is_signed) { SASSERT(m_bv.is_bv(s)); SASSERT(m_bv.is_bv(t)); if (is_zero(s)) { return t; } if (is_zero(t)) { return s; } expr_ref s1(s, m()), t1(t, m()); align_sizes(s1, t1, is_signed); s1 = mk_extend(1, s1, is_signed); t1 = mk_extend(1, t1, is_signed); return m_bv.mk_bv_add(s1, t1); } br_status bv2int_rewriter::mk_add(expr* s, expr* t, expr_ref& result) { expr_ref s1(m()), t1(m()), s2(m()), t2(m()); if (is_bv2int(s, s1) && is_bv2int(t, t1)) { result = m_bv.mk_bv2int(mk_bv_add(s1, t1, false)); return BR_DONE; } if (is_bv2int_diff(s, s1, s2) && is_bv2int_diff(t, t1, t2)) { // s1 - s2 + t1 - t2 // = // s1 + t1 - (s2 + t2) // t1 = m_bv.mk_bv2int(mk_bv_add(s1, t1, false)); t2 = m_bv.mk_bv2int(mk_bv_add(s2, t2, false)); result = m_arith.mk_sub(t1, t2); return BR_DONE; } if (is_sbv2int(s, s1) && is_sbv2int(t, t1)) { result = mk_sbv2int(mk_bv_add(s1, t1, true)); return BR_DONE; } return BR_FAILED; } br_status bv2int_rewriter::mk_mul(unsigned num_args, expr * const* args, expr_ref& result) { br_status r = BR_DONE; SASSERT(num_args > 0); result = args[0]; for (unsigned i = 1; r == BR_DONE && i < num_args; ++i) { r = mk_mul(result, args[i], result); } return r; } expr* bv2int_rewriter::mk_bv_mul(expr* s, expr* t, bool is_signed) { SASSERT(m_bv.is_bv(s)); SASSERT(m_bv.is_bv(t)); if (is_zero(s)) { return s; } if (is_zero(t)) { return t; } rational r; unsigned sz; if (m_bv.is_numeral(s, r, sz) && r.is_one()) { return t; } if (m_bv.is_numeral(t, r, sz) && r.is_one()) { return s; } expr_ref s1(s, m()), t1(t, m()); align_sizes(s1, t1, is_signed); unsigned n = m_bv.get_bv_size(t1); unsigned max_bits = m_ctx.get_max_num_bits(); bool add_side_conds = 2*n > max_bits; if (n >= max_bits) { // } else if (2*n > max_bits) { s1 = mk_extend(max_bits-n, s1, is_signed); t1 = mk_extend(max_bits-n, t1, is_signed); } else { s1 = mk_extend(n, s1, is_signed); t1 = mk_extend(n, t1, is_signed); } if (add_side_conds) { if (is_signed) { m_ctx.add_side_condition(m_bv.mk_bvsmul_no_ovfl(s1, t1)); m_ctx.add_side_condition(m_bv.mk_bvsmul_no_udfl(s1, t1)); } else { m_ctx.add_side_condition(m_bv.mk_bvumul_no_ovfl(s1, t1)); } } return m_bv.mk_bv_mul(s1, t1); } br_status bv2int_rewriter::mk_mul(expr* s, expr* t, expr_ref& result) { expr_ref s1(m()), s2(m()), t1(m()), t2(m()); if ((is_shl1(s, s1) && is_bv2int(t, t1)) || (is_shl1(t, s1) && is_bv2int(s, t1))) { unsigned n = m_bv.get_bv_size(s1); unsigned m = m_bv.get_bv_size(t1); s1 = mk_extend(m, s1, false); t1 = mk_extend(n, t1, false); result = m_bv.mk_bv2int(m_bv.mk_bv_shl(t1, s1)); return BR_DONE; } if (is_bv2int(s, s1) && is_bv2int(t, t1)) { result = m_bv.mk_bv2int(mk_bv_mul(s1, t1, false)); return BR_DONE; } if ((is_bv2int(s, s1) && is_bv2int_diff(t, t1, t2)) || (is_bv2int(t, s1) && is_bv2int_diff(s, t1, t2))) { t1 = m_bv.mk_bv2int(mk_bv_mul(s1, t1, false)); t2 = m_bv.mk_bv2int(mk_bv_mul(s1, t2, false)); result = m_arith.mk_sub(t1, t2); return BR_DONE; } if (is_sbv2int(s, s1) && is_sbv2int(t, t1)) { result = mk_sbv2int(mk_bv_mul(s1, t1, true)); return BR_DONE; } return BR_FAILED; } br_status bv2int_rewriter::mk_sub(unsigned num_args, expr * const* args, expr_ref& result) { br_status r = BR_DONE; SASSERT(num_args > 0); result = args[0]; for (unsigned i = 1; r == BR_DONE && i < num_args; ++i) { r = mk_sub(result, args[i], result); } return r; } br_status bv2int_rewriter::mk_sub(expr* s, expr* t, expr_ref& result) { expr_ref s1(m()), t1(m()), s2(m()), t2(m()); if (is_bv2int_diff(s, s1, s2) && is_bv2int_diff(t, t1, t2)) { // s1 - s2 - (t1 - t2) // = // s1 + t2 - (t1 + s2) // s1 = m_bv.mk_bv2int(mk_bv_add(s1, t2, false)); s2 = m_bv.mk_bv2int(mk_bv_add(s2, t1, false)); result = m_arith.mk_sub(s1, s2); return BR_DONE; } if (is_sbv2int(s, s1) && is_sbv2int(t, t1)) { align_sizes(s1, t1, true); s1 = m_bv.mk_sign_extend(1, s1); t1 = m_bv.mk_sign_extend(1, t1); result = mk_sbv2int(m_bv.mk_bv_sub(s1, t1)); return BR_DONE; } return BR_FAILED; } bool bv2int_rewriter::is_bv2int(expr* n, expr_ref& s) { rational k; bool is_int; if (m_bv.is_bv2int(n)) { s = to_app(n)->get_arg(0); return true; } if (m_arith.is_numeral(n, k, is_int) && is_int && !k.is_neg()) { unsigned sz = k.get_num_bits(); s = m_bv.mk_numeral(k, m_bv.mk_sort(sz)); return true; } return false; } bool bv2int_rewriter::is_shl1(expr* n, expr_ref& s) { expr* s1, *s2; rational r; unsigned bv_size; if(m_bv.is_bv2int(n, s2) && m_bv.is_bv_shl(s2, s1, s2) && m_bv.is_numeral(s1, r, bv_size) && r.is_one()) { s = s2; return true; } return false; } bool bv2int_rewriter::is_bv2int_diff(expr* n, expr_ref& s, expr_ref& t) { if (is_bv2int(n, s)) { t = m_bv.mk_numeral(0, 1); return true; } rational k; bool is_int; if (m_arith.is_numeral(n, k, is_int) && is_int) { SASSERT(k.is_neg()); k.neg(); unsigned sz = k.get_num_bits(); t = m_bv.mk_numeral(k, m_bv.mk_sort(sz)); s = m_bv.mk_numeral(0, 1); return true; } // // bv2int(a) - bv2int(b) // expr *e1, *e2; if (m_arith.is_sub(n, e1, e2) && is_bv2int(e1, s) && is_bv2int(e2, t)) { return true; } if (m_arith.is_add(n, e1, e2) && m_arith.is_numeral(e1, k, is_int) && is_int && k.is_neg() && is_bv2int(e2, s)) { k.neg(); unsigned sz = k.get_num_bits(); t = m_bv.mk_numeral(k, m_bv.mk_sort(sz)); return true; } if (m_arith.is_add(n, e1, e2) && m_arith.is_numeral(e2, k, is_int) && is_int && k.is_neg() && is_bv2int(e1, s)) { k.neg(); unsigned sz = k.get_num_bits(); t = m_bv.mk_numeral(k, m_bv.mk_sort(sz)); return true; } return false; } bool bv2int_rewriter::is_sbv2int(expr* n, expr_ref& s) { if (is_bv2int(n, s)) { s = m_bv.mk_zero_extend(1, s); return true; } expr_ref u1(m()), u2(m()); if (is_bv2int_diff(n, u1, u2)) { align_sizes(u1, u2, false); u1 = mk_extend(1, u1, false); u2 = mk_extend(1, u2, false); s = m_bv.mk_bv_sub(u1, u2); return true; } // ite(bv1 == b[n-1:n-1], bv2int(b[0:n-2]) - 2^{n-1}, bv2int(b[0:n-2])) expr* c, *t, *e1, *c1, *c2, *c3, *t1, *t2, *e2, *e3; rational k; bool is_int; unsigned lo, hi, lo1, hi1, sz; if (m().is_ite(n, c, t, e1) && m().is_eq(c, c1, c2) && m_bv.is_numeral(c1, k, sz) && k.is_one() && sz == 1 && m_bv.is_extract(c2, lo, hi, c3) && lo == hi && lo == m_bv.get_bv_size(c3) - 1 && m_arith.is_sub(t, t1, t2) && e1 == t1 && m_bv.is_bv2int(e1, e2) && m_bv.is_extract(e2, lo1, hi1, e3) && lo1 == 0 && hi1 == hi-1 && m_arith.is_numeral(t2, k, is_int) && is_int && k == rational::power_of_two(hi) ) { s = e3; return true; } #if 0 // bv2int(b[0:n-2]) - ite(bv1 == b[n-1:n-1], 2^{n-1}, 0) if (m().is_sub(n, e1, e2) && m_bv.is_bv2int(e1, e3) && m_bv.is_extract(e3, lo, hi, e4) && lo == 0 && hi == m_bv.get_bv_size(e4) - 2 && m().is_ite(e2, t1, t2, t3) && m().is_eq(t1, c1, c2) && m_bv.is_numeral(c1, k, sz) && k.is_one() && sz == 1 && m_bv.is_extract(c2, lo1, hi1, c3) && lo1 == h1 + 1 && hi1 == lo1 && c3 == e4 && m_arith.is_numeral(t2, )) { } #endif return false; } expr* bv2int_rewriter::mk_sbv2int(expr* b) { // // ite(bit1 = b[n-1:n-1], bv2int(b[0:n-2]) - 2^{n-1}, bv2int(b[0:n-2])) // expr* bv1 = m_bv.mk_numeral(1, 1); unsigned n = m_bv.get_bv_size(b); expr* c = m().mk_eq(bv1, m_bv.mk_extract(n-1, n-1, b)); expr* e = m_bv.mk_bv2int(m_bv.mk_extract(n-2, 0, b)); expr* t = m_arith.mk_sub(e, m_arith.mk_numeral(power(rational(2), n-1), true)); return m().mk_ite(c, t, e); } expr* bv2int_rewriter::mk_extend(unsigned sz, expr* b, bool is_signed) { if (sz == 0) { return b; } if (sz > m_ctx.get_max_num_bits()) { throw tactic_exception(TACTIC_MAX_MEMORY_MSG); } rational r; unsigned bv_sz; if (is_signed) { return m_bv.mk_sign_extend(sz, b); } else if (m_bv.is_numeral(b, r, bv_sz)) { return m_bv.mk_numeral(r, bv_sz + sz); } else { return m_bv.mk_zero_extend(sz, b); } } template class rewriter_tpl; z3-z3-4.8.7/src/tactic/arith/bv2int_rewriter.h000066400000000000000000000104521356505360400210760ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: bv2int_rewriter.h Abstract: Basic rewriting rules for bv2int propagation. Author: Nikolaj (nbjorner) 2011-05-05 Notes: --*/ #ifndef BV2INT_REWRITER_H_ #define BV2INT_REWRITER_H_ #include "ast/ast.h" #include "ast/rewriter/rewriter.h" #include "ast/bv_decl_plugin.h" #include "ast/arith_decl_plugin.h" #include "util/params.h" #include "tactic/goal.h" class bv2int_rewriter_ctx { unsigned m_max_size; expr_ref_vector m_side_conditions; obj_map m_power2; expr_ref_vector m_trail; public: bv2int_rewriter_ctx(ast_manager& m, params_ref const& p, unsigned max_size) : m_max_size(max_size), m_side_conditions(m), m_trail(m) { update_params(p); } void reset() { m_side_conditions.reset(); m_trail.reset(); m_power2.reset(); } void add_side_condition(expr* e) { m_side_conditions.push_back(e); } unsigned num_side_conditions() const { return m_side_conditions.size(); } expr* const* side_conditions() const { return m_side_conditions.c_ptr(); } unsigned get_max_num_bits() const { return m_max_size; } void collect_power2(goal const & s); bool is_power2(expr* x, expr*& log_x); obj_map const& power2() const { return m_power2; } private: void update_params(params_ref const& p); }; class bv2int_rewriter { ast_manager & m_manager; bv2int_rewriter_ctx& m_ctx; bv_util m_bv; arith_util m_arith; public: bv2int_rewriter(ast_manager & m, bv2int_rewriter_ctx& ctx); ast_manager & m() const { return m_manager; } br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result); void mk_app(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { if (mk_app_core(f, num_args, args, result) == BR_FAILED) result = m().mk_app(f, num_args, args); } private: br_status mk_eq(expr * arg1, expr * arg2, expr_ref & result); br_status mk_ite(expr* c, expr* s, expr* t, expr_ref& result); br_status mk_le(expr * arg1, expr * arg2, expr_ref & result); br_status mk_lt(expr * arg1, expr * arg2, expr_ref & result); br_status mk_ge(expr * arg1, expr * arg2, expr_ref & result); br_status mk_gt(expr * arg1, expr * arg2, expr_ref & result); br_status mk_idiv(expr * arg1, expr * arg2, expr_ref & result); br_status mk_mod(expr * arg1, expr * arg2, expr_ref & result); br_status mk_rem(expr * arg1, expr * arg2, expr_ref & result); br_status mk_add(unsigned num_args, expr * const * args, expr_ref & result); br_status mk_mul(unsigned num_args, expr * const * args, expr_ref & result); br_status mk_sub(unsigned num_args, expr * const * args, expr_ref & result); br_status mk_add(expr* s, expr* t, expr_ref& result); br_status mk_mul(expr* s, expr* t, expr_ref& result); br_status mk_sub(expr* s, expr* t, expr_ref& result); br_status mk_uminus(expr* e, expr_ref & result); bool is_bv2int(expr* e, expr_ref& s); bool is_sbv2int(expr* e, expr_ref& s); bool is_bv2int_diff(expr* e, expr_ref& s, expr_ref& t); bool is_zero(expr* e); bool is_shl1(expr* e, expr_ref& s); expr* mk_bv_add(expr* s, expr* t, bool is_signed); expr* mk_bv_mul(expr* s, expr* t, bool is_signed); expr* mk_sbv2int(expr* s); expr* mk_extend(unsigned sz, expr* b, bool is_signed); void align_sizes(expr_ref& s, expr_ref& t, bool is_signed); }; struct bv2int_rewriter_cfg : public default_rewriter_cfg { bv2int_rewriter m_r; bool rewrite_patterns() const { return false; } bool flat_assoc(func_decl * f) 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; return m_r.mk_app_core(f, num, args, result); } bv2int_rewriter_cfg(ast_manager & m, bv2int_rewriter_ctx& ctx):m_r(m, ctx) {} }; class bv2int_rewriter_star : public rewriter_tpl { bv2int_rewriter_cfg m_cfg; public: bv2int_rewriter_star(ast_manager & m, bv2int_rewriter_ctx& ctx): rewriter_tpl(m, false, m_cfg), m_cfg(m, ctx) {} }; #endif z3-z3-4.8.7/src/tactic/arith/bv2real_rewriter.cpp000066400000000000000000000537361356505360400215760ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: bv2real_rewriter.cpp Abstract: Basic rewriting rules for bv2real propagation. Author: Nikolaj (nbjorner) 2011-08-05 Notes: --*/ #include "tactic/arith/bv2real_rewriter.h" #include "ast/rewriter/rewriter_def.h" #include "ast/ast_pp.h" #include "ast/for_each_expr.h" bv2real_util::bv2real_util(ast_manager& m, rational const& default_root, rational const& default_divisor, unsigned max_num_bits) : m_manager(m), m_arith(m), m_bv(m), m_decls(m), m_pos_le(m), m_pos_lt(m), m_side_conditions(m), m_default_root(default_root), m_default_divisor(default_divisor), m_max_divisor(rational(2)*default_divisor), m_max_num_bits(max_num_bits) { sort* real = m_arith.mk_real(); sort* domain[2] = { real, real }; m_pos_lt = m.mk_fresh_func_decl("<","",2,domain,m.mk_bool_sort()); m_pos_le = m.mk_fresh_func_decl("<=","",2,domain,m.mk_bool_sort()); m_decls.push_back(m_pos_lt); m_decls.push_back(m_pos_le); } bool bv2real_util::is_bv2real(func_decl* f) const { return m_decl2sig.contains(f); } bool bv2real_util::is_bv2real(func_decl* f, unsigned num_args, expr* const* args, expr*& m, expr*& n, rational& d, rational& r) const { bvr_sig sig; if (!m_decl2sig.find(f, sig)) { return false; } SASSERT(num_args == 2); m = args[0]; n = args[1]; d = sig.m_d; r = sig.m_r; SASSERT(sig.m_d.is_int() && sig.m_d.is_pos()); SASSERT(sig.m_r.is_int() && sig.m_r.is_pos()); SASSERT(m_bv.get_bv_size(m) == sig.m_msz); SASSERT(m_bv.get_bv_size(n) == sig.m_nsz); return true; } bool bv2real_util::is_bv2real(expr* e, expr*& m, expr*& n, rational& d, rational& r) const { if (!is_app(e)) return false; func_decl* f = to_app(e)->get_decl(); return is_bv2real(f, to_app(e)->get_num_args(), to_app(e)->get_args(), m, n, d, r); } class bv2real_util::contains_bv2real_proc { bv2real_util const& m_util; public: class found {}; contains_bv2real_proc(bv2real_util const& u): m_util(u) {} void operator()(app* a) { if (m_util.is_bv2real(a->get_decl())) { throw found(); } } void operator()(var*) {} void operator()(quantifier*) {} }; bool bv2real_util::contains_bv2real(expr* e) const { contains_bv2real_proc p(*this); try { for_each_expr(p, e); } catch (const contains_bv2real_proc::found &) { return true; } return false; } bool bv2real_util::mk_bv2real(expr* _s, expr* _t, rational& d, rational& r, expr_ref& result) { expr_ref s(_s,m()), t(_t,m()); if (align_divisor(s, t, d)) { result = mk_bv2real_c(s, t, d, r); return true; } else { return false; } } expr* bv2real_util::mk_bv2real_c(expr* s, expr* t, rational const& d, rational const& r) { bvr_sig sig; sig.m_msz = m_bv.get_bv_size(s); sig.m_nsz = m_bv.get_bv_size(t); sig.m_d = d; sig.m_r = r; func_decl* f; if (!m_sig2decl.find(sig, f)) { sort* domain[2] = { m_manager.get_sort(s), m_manager.get_sort(t) }; sort* real = m_arith.mk_real(); f = m_manager.mk_fresh_func_decl("bv2real", "", 2, domain, real); m_decls.push_back(f); m_sig2decl.insert(sig, f); m_decl2sig.insert(f, sig); } return m_manager.mk_app(f, s, t); } void bv2real_util::mk_bv2real_reduced(expr* s, expr* t, rational const& d, rational const& r, expr_ref & result) { expr_ref s1(m()), t1(m()), r1(m()); rational num; mk_sbv2real(s, s1); mk_sbv2real(t, t1); mk_div(s1, d, s1); mk_div(t1, d, t1); r1 = a().mk_power(a().mk_numeral(r, false), a().mk_numeral(rational(1,2),false)); t1 = a().mk_mul(t1, r1); result = a().mk_add(s1, t1); } void bv2real_util::mk_div(expr* e, rational const& d, expr_ref& result) { result = a().mk_div(e, a().mk_numeral(rational(d), false)); } void bv2real_util::mk_sbv2real(expr* e, expr_ref& result) { rational r; unsigned bv_size = m_bv.get_bv_size(e); rational bsize = power(rational(2), bv_size); expr_ref bvr(a().mk_to_real(m_bv.mk_bv2int(e)), m()); expr_ref c(m_bv.mk_sle(m_bv.mk_numeral(rational(0), bv_size), e), m()); result = m().mk_ite(c, bvr, a().mk_sub(bvr, a().mk_numeral(bsize, false))); } expr* bv2real_util::mk_bv_mul(rational const& n, expr* t) { if (n.is_one()) return t; expr* s = mk_sbv(n); return mk_bv_mul(s, t); } void bv2real_util::align_divisors(expr_ref& s1, expr_ref& s2, expr_ref& t1, expr_ref& t2, rational& d1, rational& d2) { if (d1 == d2) { return; } // s/d1 ~ t/d2 <=> lcm*s/d1 ~ lcm*t/d2 <=> (lcm/d1)*s ~ (lcm/d2)*t // s/d1 ~ t/d2 <=> s/gcd*d1' ~ t/gcd*d2' <=> d2'*s/lcm ~ d1'*t/lcm rational g = gcd(d1,d2); rational l = lcm(d1,d2); rational d1g = d1/g; rational d2g = d2/g; s1 = mk_bv_mul(d2g, s1); s2 = mk_bv_mul(d2g, s2); t1 = mk_bv_mul(d1g, t1); t2 = mk_bv_mul(d1g, t2); d1 = l; d2 = l; } expr* bv2real_util::mk_bv_mul(expr* s, expr* t) { SASSERT(m_bv.is_bv(s)); SASSERT(m_bv.is_bv(t)); if (is_zero(s)) { return s; } if (is_zero(t)) { return t; } expr_ref s1(s, m()), t1(t, m()); align_sizes(s1, t1); unsigned n = m_bv.get_bv_size(t1); unsigned max_bits = get_max_num_bits(); bool add_side_conds = 2*n > max_bits; if (n >= max_bits) { // nothing } else if (2*n > max_bits) { s1 = mk_extend(max_bits-n, s1); t1 = mk_extend(max_bits-n, t1); } else { s1 = mk_extend(n, s1); t1 = mk_extend(n, t1); } if (add_side_conds) { add_side_condition(m_bv.mk_bvsmul_no_ovfl(s1, t1)); add_side_condition(m_bv.mk_bvsmul_no_udfl(s1, t1)); } return m_bv.mk_bv_mul(s1, t1); } bool bv2real_util::is_zero(expr* n) { rational r; unsigned sz; return m_bv.is_numeral(n, r, sz) && r.is_zero(); } expr* bv2real_util::mk_bv_add(expr* s, expr* t) { SASSERT(m_bv.is_bv(s)); SASSERT(m_bv.is_bv(t)); if (is_zero(s)) { return t; } if (is_zero(t)) { return s; } expr_ref s1(s, m()), t1(t, m()); align_sizes(s1, t1); s1 = mk_extend(1, s1); t1 = mk_extend(1, t1); return m_bv.mk_bv_add(s1, t1); } void bv2real_util::align_sizes(expr_ref& s, expr_ref& t) { unsigned sz1 = m_bv.get_bv_size(s); unsigned sz2 = m_bv.get_bv_size(t); if (sz1 > sz2) { t = mk_extend(sz1-sz2, t); } else if (sz1 < sz2) { s = mk_extend(sz2-sz1, s); } } expr* bv2real_util::mk_sbv(rational const& n) { SASSERT(n.is_int()); if (n.is_neg()) { rational m = abs(n); unsigned nb = m.get_num_bits(); return m_bv.mk_bv_neg(m_bv.mk_numeral(m, nb+1)); } else { unsigned nb = n.get_num_bits(); return m_bv.mk_numeral(n, nb+1); } } expr* bv2real_util::mk_bv_sub(expr* s, expr* t) { expr_ref s1(s, m()), t1(t, m()); align_sizes(s1, t1); s1 = mk_extend(1, s1); t1 = mk_extend(1, t1); return m_bv.mk_bv_sub(s1, t1); } expr* bv2real_util::mk_extend(unsigned sz, expr* b) { if (sz == 0) { return b; } rational r; unsigned bv_sz; if (m_bv.is_numeral(b, r, bv_sz) && power(rational(2),bv_sz-1) > r) { return m_bv.mk_numeral(r, bv_sz + sz); } return m_bv.mk_sign_extend(sz, b); } bool bv2real_util::is_bv2real(expr* n, expr_ref& s, expr_ref& t, rational& d, rational& r) { expr* _s, *_t; if (is_bv2real(n, _s, _t, d, r)) { s = _s; t = _t; return true; } rational k; bool is_int; if (m_arith.is_numeral(n, k, is_int) && !is_int) { d = denominator(k); r = default_root(); s = mk_sbv(numerator(k)); t = mk_sbv(rational(0)); return true; } return false; } bool bv2real_util::align_divisor(expr_ref& s, expr_ref& t, rational& d) { if (d > max_divisor()) { // // if divisor is over threshold, then divide s and t // add side condition that s, t are divisible. // rational overflow = d / max_divisor(); if (!overflow.is_int()) return false; if (!mk_is_divisible_by(s, overflow)) return false; if (!mk_is_divisible_by(t, overflow)) return false; d = max_divisor(); } return true; } bool bv2real_util::mk_is_divisible_by(expr_ref& s, rational const& _overflow) { rational overflow(_overflow); SASSERT(overflow.is_int()); SASSERT(overflow.is_pos()); SASSERT(!overflow.is_one()); TRACE("bv2real_rewriter", tout << mk_pp(s, m()) << " " << overflow << "\n";); unsigned power2 = 0; while ((overflow % rational(2)) == rational(0)) { power2++; overflow = div(overflow, rational(2)); } if (power2 > 0) { unsigned sz = m_bv.get_bv_size(s); if (sz <= power2) { add_side_condition(m().mk_eq(s, m_bv.mk_numeral(rational(0), sz))); s = m_bv.mk_numeral(rational(0), 1); } else { expr* s1 = m_bv.mk_extract(power2-1, 0, s); add_side_condition(m().mk_eq(s1, m_bv.mk_numeral(rational(0), power2))); s = m_bv.mk_extract(sz-1, power2, s); } } TRACE("bv2real_rewriter", tout << mk_pp(s, m()) << " " << overflow << "\n";); return overflow.is_one(); } // --------------------------------------------------------------------- // bv2real_rewriter bv2real_rewriter::bv2real_rewriter(ast_manager& m, bv2real_util& util): m_manager(m), m_util(util), m_bv(m), m_arith(m) {} br_status bv2real_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { TRACE("bv2real_rewriter", tout << mk_pp(f, m()) << " "; for (unsigned i = 0; i < num_args; ++i) { tout << mk_pp(args[i], m()) << " "; } tout << "\n";); if(f->get_family_id() == m_arith.get_family_id()) { switch (f->get_decl_kind()) { case OP_NUM: return BR_FAILED; case OP_LE: SASSERT(num_args == 2); return mk_le(args[0], args[1], result); case OP_GE: SASSERT(num_args == 2); return mk_ge(args[0], args[1], result); case OP_LT: SASSERT(num_args == 2); return mk_lt(args[0], args[1], result); case OP_GT: SASSERT(num_args == 2); return mk_gt(args[0], args[1], result); case OP_ADD: return mk_add(num_args, args, result); case OP_MUL: return mk_mul(num_args, args, result); case OP_SUB: return mk_sub(num_args, args, result); case OP_DIV: SASSERT(num_args == 2); return mk_div(args[0], args[1], result); case OP_IDIV: return BR_FAILED; case OP_MOD: return BR_FAILED; case OP_REM: return BR_FAILED; case OP_UMINUS: SASSERT(num_args == 1); return mk_uminus(args[0], result); case OP_TO_REAL: return BR_FAILED; // TBD case OP_TO_INT: return BR_FAILED; // TBD case OP_IS_INT: return BR_FAILED; // TBD default: return BR_FAILED; } } if (f->get_family_id() == m().get_basic_family_id()) { switch (f->get_decl_kind()) { case OP_EQ: SASSERT(num_args == 2); return mk_eq(args[0], args[1], result); case OP_ITE: SASSERT(num_args == 3); return mk_ite(args[0], args[1], args[2], result); default: return BR_FAILED; } } if (u().is_pos_ltf(f)) { SASSERT(num_args == 2); return mk_lt_pos(args[0], args[1], result); } if (u().is_pos_lef(f)) { SASSERT(num_args == 2); return mk_le_pos(args[0], args[1], result); } return BR_FAILED; } bool bv2real_rewriter::mk_le(expr* s, expr* t, bool is_pos, bool is_neg, expr_ref& result) { expr_ref s1(m()), s2(m()), t1(m()), t2(m()); rational d1, d2, r1, r2; SASSERT(is_pos || is_neg); if (u().is_bv2real(s, s1, s2, d1, r1) && u().is_bv2real(t, t1, t2, d2, r2) && r1 == r2 && r1 == rational(2)) { // // (s1 + s2*sqrt(2))/d1 <= (t1 + t2*sqrt(2))/d2 // <=> // // let s1 = s1*d2-t1*d1, t2 = s2*d2-t2*d1 // // // s1 + s2*sqrt(2) <= 0 // <= // s1 + s2*approx(sign(s2),sqrt(2)) <= 0 // or (s1 = 0 & s2 = 0) // // If s2 is negative use an under-approximation for sqrt(r). // If s2 is positive use an over-approximation for sqrt(r). // e.g., r = 2, then 5/4 and 3/2 are under/over approximations. // Then s1 + s2*approx(sign(s2), r) <= 0 => s1 + s2*sqrt(r) <= 0 u().align_divisors(s1, s2, t1, t2, d1, d2); s1 = u().mk_bv_sub(s1, t1); s2 = u().mk_bv_sub(s2, t2); unsigned s2_size = m_bv.get_bv_size(s2); expr_ref le_proxy(m().mk_fresh_const("le_proxy",m().mk_bool_sort()), m()); u().add_aux_decl(to_app(le_proxy)->get_decl()); expr_ref gt_proxy(m().mk_not(le_proxy), m()); expr_ref s2_is_nonpos(m_bv.mk_sle(s2, m_bv.mk_numeral(rational(0), s2_size)), m()); expr_ref under(u().mk_bv_add(u().mk_bv_mul(rational(4), s1), u().mk_bv_mul(rational(5), s2)), m()); expr_ref z1(m_bv.mk_numeral(rational(0), m_bv.get_bv_size(under)), m()); expr_ref le_under(m_bv.mk_sle(under, z1), m()); expr_ref over(u().mk_bv_add(u().mk_bv_mul(rational(2), s1), u().mk_bv_mul(rational(3), s2)), m()); expr_ref z2(m_bv.mk_numeral(rational(0), m_bv.get_bv_size(over)), m()); expr_ref le_over(m_bv.mk_sle(over, z2), m()); // predicate may occur in positive polarity. if (is_pos) { // s1 + s2*sqrt(2) <= 0 <== s2 <= 0 & s1 + s2*(5/4) <= 0; 4*s1 + 5*s2 <= 0 expr* e1 = m().mk_implies(m().mk_and(le_proxy, s2_is_nonpos), le_under); // s1 + s2*sqrt(2) <= 0 <== s2 > 0 & s1 + s2*(3/2); 0 <=> 2*s1 + 3*s2 <= 0 expr* e2 = m().mk_implies(m().mk_and(le_proxy, m().mk_not(s2_is_nonpos)), le_over); u().add_side_condition(e1); u().add_side_condition(e2); } // predicate may occur in negative polarity. if (is_neg) { // s1 + s2*sqrt(2) > 0 <== s2 > 0 & s1 + s2*(5/4) > 0; 4*s1 + 5*s2 > 0 expr* e3 = m().mk_implies(m().mk_and(gt_proxy, m().mk_not(s2_is_nonpos)), m().mk_not(le_under)); // s1 + s2*sqrt(2) > 0 <== s2 <= 0 & s1 + s2*(3/2) > 0 <=> 2*s1 + 3*s2 > 0 expr* e4 = m().mk_implies(m().mk_and(gt_proxy, s2_is_nonpos), m().mk_not(le_over)); u().add_side_condition(e3); u().add_side_condition(e4); } TRACE("bv2real_rewriter", tout << "mk_le\n";); if (is_pos) { result = le_proxy; } else { result = gt_proxy; } return true; } return false; } br_status bv2real_rewriter::mk_le_pos(expr * s, expr * t, expr_ref & result) { if (mk_le(s, t, true, false, result)) { return BR_DONE; } return BR_FAILED; } br_status bv2real_rewriter::mk_lt_pos(expr * s, expr * t, expr_ref & result) { if (mk_le(t, s, false, true, result)) { return BR_DONE; } return BR_FAILED; } br_status bv2real_rewriter::mk_le(expr * s, expr * t, expr_ref & result) { expr_ref s1(m()), s2(m()), t1(m()), t2(m()); rational d1, d2, r1, r2; if (mk_le(s, t, true, true, result)) { return BR_DONE; } if (u().is_bv2real(s, s1, s2, d1, r1) && u().is_bv2real(t, t1, t2, d2, r2) && r1 == r2) { // // somewhat expensive approach without having // polarity information for sound approximation. // // Convert to: // t1 + t2*sqrt(r) >= 0 // then to: // // (t1 >= 0 && t2 <= 0 => t1^2 >= t2^2*r) // (t1 <= 0 && t2 >= 0 => t1^2 <= t2^2*r) // (t1 >= 0 || t2 >= 0) // // A cheaper approach is to approximate > under the assumption // that > occurs in positive polarity. // then if t2 is negative use an over-approximation for sqrt(r) // if t2 is positive use an under-approximation for sqrt(r). // e.g., r = 2, then 5/4 and 3/2 are under/over approximations. // Then t1 + t2*approx(sign(t2), r) > 0 => t1 + t2*sqrt(r) > 0 // u().align_divisors(s1, s2, t1, t2, d1, d2); t1 = u().mk_bv_sub(t1, s1); t2 = u().mk_bv_sub(t2, s2); expr_ref z1(m()), z2(m()); z1 = m_bv.mk_numeral(rational(0), m_bv.get_bv_size(t1)); z2 = m_bv.mk_numeral(rational(0), m_bv.get_bv_size(t2)); expr* gz1 = m_bv.mk_sle(z1, t1); expr* lz1 = m_bv.mk_sle(t1, z1); expr* gz2 = m_bv.mk_sle(z2, t2); expr* lz2 = m_bv.mk_sle(t2, z2); expr_ref t12(u().mk_bv_mul(t1, t1), m()); expr_ref t22(u().mk_bv_mul(r1, u().mk_bv_mul(t2, t2)), m()); u().align_sizes(t12, t22); expr* ge = m_bv.mk_sle(t22, t12); expr* le = m_bv.mk_sle(t12, t22); expr* e1 = m().mk_or(gz1, gz2); expr* e2 = m().mk_or(m().mk_not(gz1), m().mk_not(lz2), ge); expr* e3 = m().mk_or(m().mk_not(gz2), m().mk_not(lz1), le); result = m().mk_and(e1, e2, e3); TRACE("bv2real_rewriter", tout << "\n";); return BR_DONE; } return BR_FAILED; } br_status bv2real_rewriter::mk_lt(expr * arg1, expr * arg2, expr_ref & result) { result = m().mk_not(m_arith.mk_le(arg2, arg1)); return BR_REWRITE2; } br_status bv2real_rewriter::mk_ge(expr * arg1, expr * arg2, expr_ref & result) { return mk_le(arg2, arg1, result); } br_status bv2real_rewriter::mk_gt(expr * arg1, expr * arg2, expr_ref & result) { result = m().mk_not(m_arith.mk_le(arg1, arg2)); return BR_REWRITE2; } br_status bv2real_rewriter::mk_ite(expr* c, expr* s, expr* t, expr_ref& result) { expr_ref s1(m()), s2(m()), t1(m()), t2(m()); rational d1, d2, r1, r2; if (u().is_bv2real(s, s1, s2, d1, r1) && u().is_bv2real(t, t1, t2, d2, r2) && r1 == r2) { u().align_divisors(s1, s2, t1, t2, d1, d2); u().align_sizes(s1, t1); u().align_sizes(s2, t2); if (u().mk_bv2real(m().mk_ite(c, s1, t1), m().mk_ite(c, s2, t2), d1, r1, result)) { return BR_DONE; } } return BR_FAILED; } br_status bv2real_rewriter::mk_eq(expr * s, expr * t, expr_ref & result) { expr_ref s1(m()), s2(m()), t1(m()), t2(m()); rational d1, d2, r1, r2; if (u().is_bv2real(s, s1, s2, d1, r1) && u().is_bv2real(t, t1, t2, d2, r2) && r1 == r2) { u().align_divisors(s1, s2, t1, t2, d1, d2); u().align_sizes(s1, t1); u().align_sizes(s2, t2); result = m().mk_and(m().mk_eq(s1, t1), m().mk_eq(s2, t2)); return BR_DONE; } return BR_FAILED; } br_status bv2real_rewriter::mk_uminus(expr * s, expr_ref & result) { expr_ref s1(m()), s2(m()); rational d1, r1; if (u().is_bv2real(s, s1, s2, d1, r1)) { s1 = u().mk_extend(1, s1); s2 = u().mk_extend(1, s2); if (u().mk_bv2real(m_bv.mk_bv_neg(s1), m_bv.mk_bv_neg(s2), d1, r1, result)) { return BR_DONE; } } return BR_FAILED; } br_status bv2real_rewriter::mk_add(unsigned num_args, expr * const* args, expr_ref& result) { br_status r = BR_DONE; SASSERT(num_args > 0); result = args[0]; for (unsigned i = 1; r == BR_DONE && i < num_args; ++i) { r = mk_add(result, args[i], result); } return r; } br_status bv2real_rewriter::mk_add(expr* s, expr* t, expr_ref& result) { expr_ref s1(m()), s2(m()), t1(m()), t2(m()); rational d1, d2, r1, r2; if (u().is_bv2real(s, s1, s2, d1, r1) && u().is_bv2real(t, t1, t2, d2, r2) && r1 == r2) { u().align_divisors(s1, s2, t1, t2, d1, d2); if (u().mk_bv2real(u().mk_bv_add(s1, t1), u().mk_bv_add(t2, s2), d1, r1, result)) { return BR_DONE; } } return BR_FAILED; } br_status bv2real_rewriter::mk_mul(unsigned num_args, expr * const* args, expr_ref& result) { br_status r = BR_DONE; SASSERT(num_args > 0); result = args[0]; for (unsigned i = 1; r == BR_DONE && i < num_args; ++i) { r = mk_mul(result, args[i], result); } return r; } br_status bv2real_rewriter::mk_div(expr* s, expr* t, expr_ref& result) { return BR_FAILED; } br_status bv2real_rewriter::mk_mul(expr* s, expr* t, expr_ref& result) { // TBD: optimize expr_ref s1(m()), t1(m()), s2(m()), t2(m()); rational d1, d2, r1, r2; if (u().is_bv2real(s, s1, s2, d1, r1) && u().is_bv2real(t, t1, t2, d2, r2) && r1 == r2) { // s1*t1 + r1*(s2*t2) + (s1*t2 + s2*t2)*r1 expr_ref u1(m()), u2(m()); u1 = u().mk_bv_add(u().mk_bv_mul(s1, t1), u().mk_bv_mul(r1, u().mk_bv_mul(t2, s2))); u2 = u().mk_bv_add(u().mk_bv_mul(s1, t2), u().mk_bv_mul(s2, t1)); rational tmp = d1*d2; if (u().mk_bv2real(u1, u2, tmp, r1, result)) { return BR_DONE; } } return BR_FAILED; } br_status bv2real_rewriter::mk_sub(unsigned num_args, expr * const* args, expr_ref& result) { br_status r = BR_DONE; SASSERT(num_args > 0); result = args[0]; for (unsigned i = 1; r == BR_DONE && i < num_args; ++i) { r = mk_sub(result, args[i], result); } return r; } br_status bv2real_rewriter::mk_sub(expr* s, expr* t, expr_ref& result) { expr_ref s1(m()), s2(m()), t1(m()), t2(m()); rational d1, d2, r1, r2; if (u().is_bv2real(s, s1, s2, d1, r1) && u().is_bv2real(t, t1, t2, d2, r2) && r1 == r2) { u().align_divisors(s1, s2, t1, t2, d1, d2); if (u().mk_bv2real(u().mk_bv_sub(s1, t1), u().mk_bv_sub(s2, t2), d1, r1, result)) { return BR_DONE; } } return BR_FAILED; } template class rewriter_tpl; br_status bv2real_elim_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { expr* m, *n; rational d, r; if (m_util.is_bv2real(f, num_args, args, m, n, d, r)) { m_util.mk_bv2real_reduced(m, n, d, r, result); return BR_REWRITE_FULL; } return BR_FAILED; } template class rewriter_tpl; z3-z3-4.8.7/src/tactic/arith/bv2real_rewriter.h000066400000000000000000000204501356505360400212260ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: bv2real_rewriter.h Abstract: Basic rewriting rules for bv2real propagation. Author: Nikolaj (nbjorner) 2011-08-05 Notes: --*/ #ifndef BV2REAL_REWRITER_H_ #define BV2REAL_REWRITER_H_ #include "ast/ast.h" #include "ast/rewriter/rewriter.h" #include "ast/bv_decl_plugin.h" #include "ast/arith_decl_plugin.h" // // bv2real[d,r](n,m) has interpretation: // sbv2int(n)/d + sbv2int(m)/d*sqrt(r) // where // sbv2int is signed bit-vector 2 integer. // class bv2real_util { struct bvr_sig { unsigned m_msz, m_nsz; rational m_d, m_r; }; struct bvr_eq { bool operator()(bvr_sig const& x, bvr_sig const& y) const { return x.m_msz == y.m_msz && x.m_nsz == y.m_nsz && x.m_d == y.m_d && x.m_r == y.m_r; } }; struct bvr_hash { unsigned operator()(bvr_sig const& x) const { unsigned a[3] = { x.m_msz, x.m_nsz, x.m_d.hash() }; return string_hash((char const*)a, 12, x.m_r.hash()); } }; ast_manager& m_manager; arith_util m_arith; bv_util m_bv; func_decl_ref_vector m_decls; func_decl_ref m_pos_le; func_decl_ref m_pos_lt; expr_ref_vector m_side_conditions; map m_sig2decl; obj_map m_decl2sig; rational m_default_root; rational m_default_divisor; rational m_max_divisor; unsigned m_max_num_bits; class contains_bv2real_proc; public: bv2real_util(ast_manager& m, rational const& default_root, rational const& default_divisor, unsigned max_num_bits); void reset() { m_side_conditions.reset(); } bool is_bv2real(func_decl* f) const; bool is_bv2real(func_decl* f, unsigned num_args, expr* const* args, expr*& m, expr*& n, rational& d, rational& r) const; bool is_bv2real(expr* e, expr*& n, expr*& m, rational& d, rational& r) const; bool is_bv2real(expr* e, expr*& n, expr*& m, rational& d); bool contains_bv2real(expr* e) const; bool mk_bv2real(expr* s, expr* t, rational& d, rational& r, expr_ref& result); expr* mk_bv2real_c(expr* s, expr* t, rational const& d, rational const& r); expr* mk_bv2real(expr* n, expr* m) { return mk_bv2real_c(n, m, default_divisor(), default_root()); } void mk_bv2real_reduced(expr* s, expr* t, expr_ref & result) { mk_bv2real_reduced(s, t, default_divisor(), default_root(), result); } void mk_bv2real_reduced(expr* s, expr* t, rational const& d, rational const& r, expr_ref & result); // // Positive polarity comparison operators. // Translation of positive polarity comparison requires fewer clauses. // bool is_pos_ltf(func_decl* f) const { return f == m_pos_lt; } bool is_pos_lef(func_decl* f) const { return f == m_pos_le; } bool is_pos_lt(expr const* e) const { return is_app(e) && is_pos_ltf(to_app(e)->get_decl()); } bool is_pos_le(expr const* e) const { return is_app(e) && is_pos_lef(to_app(e)->get_decl()); } MATCH_BINARY(is_pos_lt); MATCH_BINARY(is_pos_le); expr* mk_pos_lt(expr* s, expr* t) { return m().mk_app(m_pos_lt, s, t); } expr* mk_pos_le(expr* s, expr* t) { return m().mk_app(m_pos_le, s, t); } rational const& default_root() const { return m_default_root; } rational const& default_divisor() const { return m_default_divisor; } rational const& max_divisor() const { return m_max_divisor; } unsigned get_max_num_bits() const { return m_max_num_bits; } void add_side_condition(expr* e) { m_side_conditions.push_back(e); } unsigned num_side_conditions() const { return m_side_conditions.size(); } expr* const* side_conditions() const { return m_side_conditions.c_ptr(); } bool is_zero(expr* e); expr* mk_bv_add(expr* s, expr* t); expr* mk_bv_sub(expr* s, expr* t); expr* mk_bv_mul(expr* s, expr* t); expr* mk_bv_mul(rational const& n, expr* t); expr* mk_extend(unsigned sz, expr* b); expr* mk_sbv(rational const& n); void align_sizes(expr_ref& s, expr_ref& t); void align_divisors(expr_ref& s1, expr_ref& s2, expr_ref& t1, expr_ref& t2, rational& d1, rational& d2); bool is_bv2real(expr* n, expr_ref& s, expr_ref& t, rational& d, rational& r); bool align_divisor(expr_ref& s, expr_ref& t, rational& d); bool mk_is_divisible_by(expr_ref& s, rational const& _overflow); void add_aux_decl(func_decl* f) { m_decls.push_back(f); } unsigned num_aux_decls() const { return m_decls.size(); } func_decl* get_aux_decl(unsigned i) const { return m_decls[i]; } private: ast_manager & m() const { return m_manager; } arith_util & a() { return m_arith; } void mk_div(expr* e, rational const& d, expr_ref& result); void mk_sbv2real(expr* e, expr_ref& result); }; class bv2real_rewriter { ast_manager & m_manager; bv2real_util& m_util; bv_util m_bv; arith_util m_arith; public: bv2real_rewriter(ast_manager & m, bv2real_util& util); br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result); private: ast_manager & m() const { return m_manager; } arith_util & a() { return m_arith; } bv2real_util& u() { return m_util; } br_status mk_eq(expr * arg1, expr * arg2, expr_ref & result); br_status mk_ite(expr* c, expr* s, expr* t, expr_ref& result); bool mk_le(expr* s, expr* t, bool is_pos, bool is_neg, expr_ref& result); br_status mk_le(expr * arg1, expr * arg2, expr_ref & result); br_status mk_lt(expr * arg1, expr * arg2, expr_ref & result); br_status mk_le_pos(expr * arg1, expr * arg2, expr_ref & result); br_status mk_lt_pos(expr * arg1, expr * arg2, expr_ref & result); br_status mk_ge(expr * arg1, expr * arg2, expr_ref & result); br_status mk_gt(expr * arg1, expr * arg2, expr_ref & result); br_status mk_add(unsigned num_args, expr * const * args, expr_ref & result); br_status mk_mul(unsigned num_args, expr * const * args, expr_ref & result); br_status mk_sub(unsigned num_args, expr * const * args, expr_ref & result); br_status mk_div(expr* s, expr* t, expr_ref& result); br_status mk_add(expr* s, expr* t, expr_ref& result); br_status mk_mul(expr* s, expr* t, expr_ref& result); br_status mk_sub(expr* s, expr* t, expr_ref& result); br_status mk_uminus(expr* e, expr_ref & result); }; struct bv2real_rewriter_cfg : public default_rewriter_cfg { bv2real_rewriter m_r; bool rewrite_patterns() const { return false; } bool flat_assoc(func_decl * f) 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; return m_r.mk_app_core(f, num, args, result); } bv2real_rewriter_cfg(ast_manager & m, bv2real_util& u):m_r(m, u) {} }; class bv2real_rewriter_star : public rewriter_tpl { bv2real_rewriter_cfg m_cfg; public: bv2real_rewriter_star(ast_manager & m, bv2real_util& u): rewriter_tpl(m, false, m_cfg), m_cfg(m, u) {} }; /** \brief replace le(bv2real(a),bv2real(b)) by under-approximation. */ class bv2real_elim_rewriter { bv2real_util& m_util; public: bv2real_elim_rewriter(bv2real_util& util) : m_util(util) {} br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result); }; struct bv2real_elim_rewriter_cfg : public default_rewriter_cfg { bv2real_elim_rewriter m_r; bool rewrite_patterns() const { return false; } bool flat_assoc(func_decl * f) 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; return m_r.mk_app_core(f, num, args, result); } bv2real_elim_rewriter_cfg(bv2real_util& u):m_r(u) {} }; class bv2real_elim_rewriter_star : public rewriter_tpl { bv2real_elim_rewriter_cfg m_cfg; public: bv2real_elim_rewriter_star(ast_manager & m, bv2real_util& u): rewriter_tpl(m, false, m_cfg), m_cfg(u) {} }; #endif z3-z3-4.8.7/src/tactic/arith/card2bv_tactic.cpp000066400000000000000000000055571356505360400211660ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: card2bv_tactic.cpp Abstract: Tactic for converting Pseudo-Boolean constraints to BV Author: Nikolaj Bjorner (nbjorner) 2014-03-20 Notes: --*/ #include "tactic/tactical.h" #include "ast/ast_smt2_pp.h" #include "tactic/arith/card2bv_tactic.h" #include "ast/rewriter/pb2bv_rewriter.h" #include "ast/ast_util.h" #include "ast/ast_pp.h" #include "tactic/generic_model_converter.h" class card2bv_tactic : public tactic { ast_manager & m; params_ref m_params; public: card2bv_tactic(ast_manager & m, params_ref const & p): m(m), m_params(p) { } tactic * translate(ast_manager & m) override { return alloc(card2bv_tactic, m, m_params); } ~card2bv_tactic() override { } void updt_params(params_ref const & p) override { m_params = p; } void collect_param_descrs(param_descrs & r) override { r.insert("keep_cardinality_constraints", CPK_BOOL, "(default: true) retain cardinality constraints for solver"); } void operator()(goal_ref const & g, goal_ref_buffer & result) override { TRACE("card2bv-before", g->display(tout);); SASSERT(g->is_well_sorted()); result.reset(); tactic_report report("card2bv", *g); th_rewriter rw1(m, m_params); pb2bv_rewriter rw2(m, m_params); if (g->inconsistent()) { result.push_back(g.get()); return; } expr_ref new_f1(m), new_f2(m); proof_ref new_pr1(m), new_pr2(m); for (unsigned idx = 0; !g->inconsistent() && idx < g->size(); idx++) { rw1(g->form(idx), new_f1, new_pr1); TRACE("card2bv", tout << "Rewriting " << mk_ismt2_pp(new_f1.get(), m) << std::endl;); rw2(false, new_f1, new_f2, new_pr2); if (m.proofs_enabled()) { new_pr1 = m.mk_modus_ponens(g->pr(idx), new_pr1); new_pr1 = m.mk_modus_ponens(new_pr1, new_pr2); } g->update(idx, new_f2, new_pr1, g->dep(idx)); } expr_ref_vector fmls(m); rw2.flush_side_constraints(fmls); for (expr* e : fmls) { g->assert_expr(e); } func_decl_ref_vector const& fns = rw2.fresh_constants(); if (!fns.empty()) { generic_model_converter* filter = alloc(generic_model_converter, m, "card2bv"); for (func_decl* f : fns) filter->hide(f); g->add(filter); } g->inc_depth(); result.push_back(g.get()); TRACE("card2bv", g->display(tout);); SASSERT(g->is_well_sorted()); } void cleanup() override { } }; tactic * mk_card2bv_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(card2bv_tactic, m, p)); } z3-z3-4.8.7/src/tactic/arith/card2bv_tactic.h000066400000000000000000000067721356505360400206330ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: card2bv_tactic.cpp Abstract: Tactic for converting Pseudo-Boolean constraints to BV Author: Nikolaj Bjorner (nbjorner) 2014-03-20 Notes: --*/ #ifndef CARD2BV_TACTIC_H_ #define CARD2BV_TACTIC_H_ #include "util/params.h" #include "ast/pb_decl_plugin.h" #include "ast/rewriter/th_rewriter.h" #include "ast/rewriter/rewriter.h" #include #include "util/sorting_network.h" class ast_manager; class tactic; namespace pb { class card2bv_rewriter { public: typedef expr* pliteral; typedef ptr_vector pliteral_vector; private: ast_manager& m; arith_util au; pb_util pb; bv_util bv; psort_nw m_sort; expr_ref_vector m_lemmas; expr_ref_vector m_trail; unsigned get_num_bits(func_decl* f); void mk_bv(func_decl * f, unsigned sz, expr * const* args, expr_ref & result); br_status mk_shannon(func_decl * f, unsigned sz, expr * const* args, expr_ref & result); expr* negate(expr* e); expr* mk_ite(expr* c, expr* hi, expr* lo); bool is_or(func_decl* f); bool is_and(func_decl* f); bool is_atmost1(func_decl* f, unsigned sz, expr * const* args, expr_ref& result); expr_ref mk_atmost1(unsigned sz, expr * const* args); void mk_at_most_1_small(bool last, unsigned n, pliteral const* xs, expr_ref_vector& result, expr_ref_vector& ors); public: card2bv_rewriter(ast_manager& m); br_status mk_app_core(func_decl * f, unsigned sz, expr * const* args, expr_ref & result); void mk_assert(func_decl * f, unsigned sz, expr * const* args, expr_ref & result, expr_ref_vector& lemmas); // definitions used for sorting network pliteral mk_false() { return m.mk_false(); } pliteral mk_true() { return m.mk_true(); } pliteral mk_max(pliteral a, pliteral b) { return trail(m.mk_or(a, b)); } pliteral mk_min(pliteral a, pliteral b) { return trail(m.mk_and(a, b)); } pliteral mk_not(pliteral a) { if (m.is_not(a,a)) return a; return trail(m.mk_not(a)); } std::ostream& pp(std::ostream& out, pliteral lit); pliteral fresh(); pliteral trail(pliteral l); void mk_clause(unsigned n, pliteral const* lits); }; struct card2bv_rewriter_cfg : public default_rewriter_cfg { card2bv_rewriter m_r; bool rewrite_patterns() const { return false; } bool flat_assoc(func_decl * f) 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; return m_r.mk_app_core(f, num, args, result); } card2bv_rewriter_cfg(ast_manager & m):m_r(m) {} }; class card_pb_rewriter : public rewriter_tpl { card2bv_rewriter_cfg m_cfg; pb_util pb; expr_ref_vector m_lemmas; public: card_pb_rewriter(ast_manager & m): rewriter_tpl(m, false, m_cfg), m_cfg(m), pb(m), m_lemmas(m) {} void rewrite(expr* e, expr_ref& result); expr_ref_vector& lemmas() { return m_lemmas; } }; }; tactic * mk_card2bv_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("card2bv", "convert pseudo-boolean constraints to bit-vectors.", "mk_card2bv_tactic(m, p)") */ #endif z3-z3-4.8.7/src/tactic/arith/degree_shift_tactic.cpp000066400000000000000000000250101356505360400222550ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: degree_shift_tactic.cpp Abstract: Simple degree shift procedure. Basic idea: if goal G contains a real variable x, x occurs with degrees d_1, ..., d_k in G, and n = gcd(d_1, ..., d_k) > 1. Then, replace x^n with a new fresh variable y. Author: Leonardo de Moura (leonardo) 2011-12-30. Revision History: --*/ #include "tactic/tactical.h" #include "tactic/generic_model_converter.h" #include "ast/arith_decl_plugin.h" #include "tactic/core/simplify_tactic.h" #include "ast/ast_smt2_pp.h" #include "ast/rewriter/rewriter_def.h" class degree_shift_tactic : public tactic { struct imp { ast_manager & m; arith_util m_autil; obj_map m_var2degree; obj_map m_var2var; obj_map m_var2pr; expr_ref_vector m_pinned; ptr_vector m_todo; rational m_one; bool m_produce_models; bool m_produce_proofs; expr * mk_power(expr * t, rational const & k) { if (k.is_one()) return t; else return m_autil.mk_power(t, m_autil.mk_numeral(k, false)); } struct rw_cfg : public default_rewriter_cfg { imp & o; rw_cfg(imp & _o):o(_o) {} br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { arith_util & u = o.m_autil; if (!is_decl_of(f, u.get_family_id(), OP_POWER) || !is_app(args[0])) return BR_FAILED; ast_manager & m = o.m; rational g; app * t = to_app(args[0]); if (!o.m_var2degree.find(t, g)) return BR_FAILED; SASSERT(g > rational(1)); SASSERT(g.is_int()); rational k; VERIFY(u.is_numeral(args[1], k)); SASSERT(gcd(k, g) == g); rational new_k = div(k, g); expr * new_arg = o.m_var2var.find(t); result = o.mk_power(new_arg, new_k); if (o.m_produce_proofs) { proof * pr = o.m_var2pr.find(t); app * fact = m.mk_eq(m.mk_app(f, num, args), result); result_pr = m.mk_th_lemma(u.get_family_id(), fact, 1, &pr); } return BR_DONE; } }; class rw : public rewriter_tpl { rw_cfg m_cfg; public: rw(imp & o): rewriter_tpl(o.m, o.m_produce_proofs, m_cfg), m_cfg(o) { } }; scoped_ptr m_rw; imp(ast_manager & _m): m(_m), m_autil(_m), m_pinned(_m), m_one(1), m_rw(nullptr) { } void checkpoint() { if (m.canceled()) throw tactic_exception(m.limit().get_cancel_msg()); } void visit(expr * t, expr_fast_mark1 & visited) { if (!visited.is_marked(t)) { visited.mark(t); m_todo.push_back(t); } } void save_degree(expr * t, rational const & k) { SASSERT(k.is_int()); if (is_uninterp_const(t) && m_autil.is_real(t)) { rational old_k; if (m_var2degree.find(to_app(t), old_k)) { old_k = gcd(k, old_k); m_var2degree.insert(to_app(t), old_k); } else { m_var2degree.insert(to_app(t), k); } } } void visit_args(expr * t, expr_fast_mark1 & visited) { if (is_app(t)) { for (expr * arg : *to_app(t)) { save_degree(arg, m_one); visit(arg, visited); } } } void collect(expr * t, expr_fast_mark1 & visited) { rational k; visit(t, visited); while (!m_todo.empty()) { checkpoint(); expr * t = m_todo.back(); m_todo.pop_back(); if (is_var(t)) continue; if (is_quantifier(t)) { unsigned num_children = to_quantifier(t)->get_num_children(); for (unsigned i = 0; i < num_children; i ++) visit(to_quantifier(t)->get_child(i), visited); } else { SASSERT(is_app(t)); if (m_autil.is_power(t) && m_autil.is_numeral(to_app(t)->get_arg(1), k) && k.is_int() && k.is_pos()) { expr * arg = to_app(t)->get_arg(0); save_degree(arg, k); visit_args(arg, visited); } else { visit_args(t, visited); } } } } void display_candidates(std::ostream & out) { out << "candidates:\n"; for (auto const& kv : m_var2degree) { if (!kv.m_value.is_one()) { out << "POWER: " << kv.m_value << "\n" << mk_ismt2_pp(kv.m_key, m) << "\n"; } } } void collect(goal const & g) { m_var2degree.reset(); expr_fast_mark1 visited; unsigned sz = g.size(); for (unsigned i = 0; i < sz; i++) { collect(g.form(i), visited); } TRACE("degree_shift", display_candidates(tout);); } void discard_non_candidates() { m_pinned.reset(); ptr_vector to_delete; for (auto const& kv : m_var2degree) { if (kv.m_value.is_one()) to_delete.push_back(kv.m_key); else m_pinned.push_back(kv.m_key); // make sure it is not deleted during simplifications } for (app* a : to_delete) m_var2degree.erase(a); } void prepare_substitution(model_converter_ref & mc) { SASSERT(!m_var2degree.empty()); generic_model_converter * xmc = nullptr; if (m_produce_models) { xmc = alloc(generic_model_converter, m, "degree_shift"); mc = xmc; } for (auto const& kv : m_var2degree) { SASSERT(kv.m_value.is_int()); SASSERT(kv.m_value >= rational(2)); app * fresh = m.mk_fresh_const(nullptr, kv.m_key->get_decl()->get_range()); m_pinned.push_back(fresh); m_var2var.insert(kv.m_key, fresh); if (m_produce_models) { xmc->hide(fresh->get_decl()); xmc->add(kv.m_key->get_decl(), mk_power(fresh, rational(1)/kv.m_value)); } if (m_produce_proofs) { expr * s = mk_power(kv.m_key, kv.m_value); expr * eq = m.mk_eq(fresh, s); proof * pr1 = m.mk_def_intro(eq); proof * result_pr = m.mk_apply_def(fresh, s, pr1); m_pinned.push_back(result_pr); m_var2pr.insert(kv.m_key, result_pr); } } } void operator()(goal_ref const & g, goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); m_produce_proofs = g->proofs_enabled(); m_produce_models = g->models_enabled(); tactic_report report("degree_shift", *g); collect(*g); model_converter_ref mc; discard_non_candidates(); if (!m_var2degree.empty()) { prepare_substitution(mc); m_rw = alloc(rw, *this); // substitute expr_ref new_curr(m); proof_ref new_pr(m); unsigned size = g->size(); for (unsigned idx = 0; idx < size; idx++) { checkpoint(); expr * curr = g->form(idx); (*m_rw)(curr, new_curr, new_pr); if (m_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)); } // add >= 0 constraints for variables with even degree for (auto const& kv : m_var2degree) { SASSERT(kv.m_value.is_int()); SASSERT(kv.m_value >= rational(2)); if (kv.m_value.is_even()) { app * new_var = m_var2var.find(kv.m_key); app * new_c = m_autil.mk_ge(new_var, m_autil.mk_numeral(rational(0), false)); proof * new_pr = nullptr; if (m_produce_proofs) { proof * pr = m_var2pr.find(kv.m_key); new_pr = m.mk_th_lemma(m_autil.get_family_id(), new_c, 1, &pr); } g->assert_expr(new_c, new_pr, nullptr); } } } g->inc_depth(); g->add(mc.get()); result.push_back(g.get()); TRACE("degree_shift", g->display(tout); if (mc) mc->display(tout);); SASSERT(g->is_well_sorted()); } }; imp * m_imp; public: degree_shift_tactic(ast_manager & m) { m_imp = alloc(imp, m); } tactic * translate(ast_manager & m) override { return alloc(degree_shift_tactic, m); } ~degree_shift_tactic() override { dealloc(m_imp); } void operator()(goal_ref const & in, goal_ref_buffer & result) override { (*m_imp)(in, result); } void cleanup() override { imp * d = alloc(imp, m_imp->m); std::swap(d, m_imp); dealloc(d); } }; tactic * mk_degree_shift_tactic(ast_manager & m, params_ref const & p) { params_ref mul2power_p; mul2power_p.set_bool("mul_to_power", true); return and_then(using_params(mk_simplify_tactic(m), mul2power_p), clean(alloc(degree_shift_tactic, m))); } z3-z3-4.8.7/src/tactic/arith/degree_shift_tactic.h000066400000000000000000000014211356505360400217220ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: degree_shift_tactic.h Abstract: Simple degree shift procedure. Basic idea: if goal G contains a real variable x, x occurs with degrees d_1, ..., d_k in G, and n = gcd(d_1, ..., d_k) > 1. Then, replace x^n with a new fresh variable y. Author: Leonardo de Moura (leonardo) 2011-12-30. Revision History: --*/ #ifndef DEGREE_SHIFT_TACTIC_H_ #define DEGREE_SHIFT_TACTIC_H_ #include "util/params.h" class ast_manager; class tactic; tactic * mk_degree_shift_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("degree-shift", "try to reduce degree of polynomials (remark: :mul2power simplification is automatically applied).", "mk_degree_shift_tactic(m, p)") */ #endif z3-z3-4.8.7/src/tactic/arith/diff_neq_tactic.cpp000066400000000000000000000316651356505360400214150ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: diff_neq_tactic.cpp Abstract: Solver for integer problems that contains literals of the form k <= x x <= k x - y != k And all variables are bounded. Author: Leonardo de Moura (leonardo) 2012-02-07. Revision History: --*/ #include "tactic/tactical.h" #include "ast/arith_decl_plugin.h" #include "ast/ast_smt2_pp.h" #include "model/model.h" class diff_neq_tactic : public tactic { struct imp { ast_manager & m; arith_util u; typedef unsigned var; expr_ref_vector m_var2expr; obj_map m_expr2var; svector m_lower; svector m_upper; struct diseq { var m_y; int m_k; diseq(var y, int k):m_y(y), m_k(k) {} }; typedef svector diseqs; vector m_var_diseqs; typedef svector decision_stack; decision_stack m_stack; bool m_produce_models; rational m_max_k; rational m_max_neg_k; unsigned m_num_conflicts; imp(ast_manager & _m, params_ref const & p): m(_m), u(m), m_var2expr(m) { updt_params(p); } void updt_params(params_ref const & p) { m_max_k = rational(p.get_uint("diff_neq_max_k", 1024)); m_max_neg_k = -m_max_k; if (m_max_k >= rational(INT_MAX/2)) m_max_k = rational(INT_MAX/2); } void throw_not_supported() { throw tactic_exception("goal is not diff neq"); } unsigned num_vars() const { return m_upper.size(); } var mk_var(expr * t) { SASSERT(is_uninterp_const(t)); var x; if (m_expr2var.find(t, x)) return x; x = m_upper.size(); m_expr2var.insert(t, x); m_var2expr.push_back(t); m_lower.push_back(INT_MIN); // unknown m_upper.push_back(INT_MAX); // unknown m_var_diseqs.push_back(diseqs()); return x; } void process_le(expr * lhs, expr * rhs) { if (!u.is_int(lhs)) throw_not_supported(); rational k; if (is_uninterp_const(lhs) && u.is_numeral(rhs, k) && m_max_neg_k <= k && k <= m_max_k) { var x = mk_var(lhs); int _k = static_cast(k.get_int64()); m_upper[x] = std::min(m_upper[x], _k); } else if (is_uninterp_const(rhs) && u.is_numeral(lhs, k) && m_max_neg_k <= k && k <= m_max_k) { var x = mk_var(rhs); int _k = static_cast(k.get_int64()); m_lower[x] = std::max(m_lower[x], _k); } else { throw_not_supported(); } } // process t1 - t2 != k void process_neq_core(expr * t1, expr * t2, int k) { var x1 = mk_var(t1); var x2 = mk_var(t2); if (x1 == x2) throw_not_supported(); // must simplify first if (x1 < x2) { std::swap(x1, x2); k = -k; } m_var_diseqs[x1].push_back(diseq(x2, k)); } void process_neq(expr * lhs, expr * rhs) { if (!u.is_int(lhs)) throw_not_supported(); if (is_uninterp_const(lhs) && is_uninterp_const(rhs)) { process_neq_core(lhs, rhs, 0); return; } if (u.is_numeral(lhs)) std::swap(lhs, rhs); rational k; if (!u.is_numeral(rhs, k)) throw_not_supported(); if (!(m_max_neg_k <= k && k <= m_max_k)) throw_not_supported(); int _k = static_cast(k.get_int64()); expr * t1, * t2, * mt1, * mt2; if (u.is_add(lhs, t1, t2)) { if (is_uninterp_const(t1) && u.is_times_minus_one(t2, mt2) && is_uninterp_const(mt2)) process_neq_core(t1, mt2, _k); else if (is_uninterp_const(t2) && u.is_times_minus_one(t1, mt1) && is_uninterp_const(mt1)) process_neq_core(t2, mt1, _k); else throw_not_supported(); } else { throw_not_supported(); } } // throws exception if contains unbounded variable void check_unbounded() { unsigned num = num_vars(); for (var x = 0; x < num; x++) { if (m_lower[x] == INT_MIN || m_upper[x] == INT_MAX) throw_not_supported(); // possible extension: support bound normalization here if (m_lower[x] != 0) throw_not_supported(); // use bound normalizer } } void compile(goal const & g) { expr * lhs; expr * rhs; unsigned sz = g.size(); for (unsigned i = 0; i < sz; i++) { expr * f = g.form(i); TRACE("diff_neq_tactic", tout << "processing: " << mk_ismt2_pp(f, m) << "\n";); if (u.is_le(f, lhs, rhs)) process_le(lhs, rhs); else if (u.is_ge(f, lhs, rhs)) process_le(rhs, lhs); else if (m.is_not(f, f) && m.is_eq(f, lhs, rhs)) process_neq(lhs, rhs); else throw_not_supported(); } check_unbounded(); } void display(std::ostream & out) { unsigned num = num_vars(); for (var x = 0; x < num; x++) { out << m_lower[x] << " <= " << mk_ismt2_pp(m_var2expr.get(x), m) << " <= " << m_upper[x] << "\n"; } for (var x = 0; x < num; x++) { diseqs::iterator it = m_var_diseqs[x].begin(); diseqs::iterator end = m_var_diseqs[x].end(); for (; it != end; ++it) { out << mk_ismt2_pp(m_var2expr.get(x), m) << " != " << mk_ismt2_pp(m_var2expr.get(it->m_y), m) << " + " << it->m_k << "\n"; } } } void display_model(std::ostream & out) { unsigned num = m_stack.size(); for (var x = 0; x < num; x++) { out << mk_ismt2_pp(m_var2expr.get(x), m) << " := " << m_stack[x] << "\n"; } } svector m_forbidden; // make sure m_forbidden.size() > max upper bound void init_forbidden() { int max = 0; unsigned num = num_vars(); for (var x = 0; x < num; x++) { if (m_upper[x] > max) max = m_upper[x]; } m_forbidden.reset(); m_forbidden.resize(max+1, false); } // Return a value v s.t. v >= starting_at and v <= m_upper[x] and all diseqs in m_var_diseqs[x] are satisfied. // Return -1 if such value does not exist. int choose_value(var x, int starting_at) { int max = starting_at-1; int v = starting_at; int upper = m_upper[x]; if (starting_at > upper) return -1; diseqs const & ds = m_var_diseqs[x]; diseqs::const_iterator it = ds.begin(); diseqs::const_iterator end = ds.end(); for (; it != end; ++it) { int bad_v = m_stack[it->m_y] + it->m_k; if (bad_v < v) continue; if (bad_v > upper) continue; if (bad_v == v) { while (true) { v++; if (v > upper) return -1; if (!m_forbidden[v]) break; m_forbidden[v] = false; } continue; } SASSERT(bad_v > v && bad_v <= upper); m_forbidden[bad_v] = true; if (bad_v > max) max = bad_v; } // reset forbidden for (int i = starting_at + 1; i <= max; i++) m_forbidden[i] = false; DEBUG_CODE({ for (unsigned i = 0; i < m_forbidden.size(); i++) { SASSERT(!m_forbidden[i]); } }); return v; } bool extend_model(var x) { int v = choose_value(x, 0); if (v == -1) return false; m_stack.push_back(v); return true; } bool resolve_conflict() { m_num_conflicts++; while (!m_stack.empty()) { int v = m_stack.back(); m_stack.pop_back(); var x = m_stack.size(); v = choose_value(x, v+1); if (v != -1) { m_stack.push_back(v); return true; } } return false; } bool search() { m_num_conflicts = 0; init_forbidden(); unsigned nvars = num_vars(); while (m_stack.size() < nvars) { if (m.canceled()) throw tactic_exception(m.limit().get_cancel_msg()); TRACE("diff_neq_tactic", display_model(tout);); var x = m_stack.size(); if (extend_model(x)) continue; if (!resolve_conflict()) return false; } TRACE("diff_neq_tactic", display_model(tout);); return true; } model * mk_model() { model * md = alloc(model, m); unsigned num = num_vars(); SASSERT(m_stack.size() == num); for (var x = 0; x < num; x++) { func_decl * d = to_app(m_var2expr.get(x))->get_decl(); md->register_decl(d, u.mk_numeral(rational(m_stack[x]), true)); } return md; } void operator()(goal_ref const & g, goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); m_produce_models = g->models_enabled(); result.reset(); tactic_report report("diff-neq", *g); fail_if_proof_generation("diff-neq", g); fail_if_unsat_core_generation("diff-neq", g); if (g->inconsistent()) { result.push_back(g.get()); return; } compile(*g); TRACE("diff_neq_tactic", g->display(tout); display(tout);); bool r = search(); report_tactic_progress(":conflicts", m_num_conflicts); if (r) { if (m_produce_models) { g->add(model2model_converter(mk_model())); } g->reset(); } else { g->assert_expr(m.mk_false()); } g->inc_depth(); result.push_back(g.get()); TRACE("diff_neq", g->display(tout);); SASSERT(g->is_well_sorted()); } }; imp * m_imp; params_ref m_params; public: diff_neq_tactic(ast_manager & m, params_ref const & p): m_params(p) { m_imp = alloc(imp, m, p); } tactic * translate(ast_manager & m) override { return alloc(diff_neq_tactic, m, m_params); } ~diff_neq_tactic() override { dealloc(m_imp); } void updt_params(params_ref const & p) override { m_params = p; m_imp->updt_params(p); } void collect_param_descrs(param_descrs & r) override { r.insert("diff_neq_max_k", CPK_UINT, "(default: 1024) maximum variable upper bound for diff neq solver."); } void collect_statistics(statistics & st) const override { st.update("conflicts", m_imp->m_num_conflicts); } void reset_statistics() override { m_imp->m_num_conflicts = 0; } /** \brief Fix a DL variable in s to 0. If s is not really in the difference logic fragment, then this is a NOOP. */ void operator()(goal_ref const & in, goal_ref_buffer & result) override { (*m_imp)(in, result); } void cleanup() override { imp * d = alloc(imp, m_imp->m, m_params); d->m_num_conflicts = m_imp->m_num_conflicts; std::swap(d, m_imp); dealloc(d); } }; tactic * mk_diff_neq_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(diff_neq_tactic, m, p)); } z3-z3-4.8.7/src/tactic/arith/diff_neq_tactic.h000066400000000000000000000014411356505360400210470ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: diff_neq_tactic.h Abstract: Solver for integer problems that contains literals of the form k <= x x <= k x - y != k And all variables are bounded. Author: Leonardo de Moura (leonardo) 2012-02-07. Revision History: --*/ #ifndef DIFF_NEQ_TACTIC_H_ #define DIFF_NEQ_TACTIC_H_ #include "util/params.h" class ast_manager; class tactic; tactic * mk_diff_neq_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("diff-neq", "specialized solver for integer arithmetic problems that contain only atoms of the form (<= k x) (<= x k) and (not (= (- x y) k)), where x and y are constants and k is a numeral, and all constants are bounded.", "mk_diff_neq_tactic(m, p)") */ #endif z3-z3-4.8.7/src/tactic/arith/eq2bv_tactic.cpp000066400000000000000000000242651356505360400206570ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: eq2bv_tactic.cpp Abstract: Extract integer variables that are used as finite domain indicators. The integer variables can only occur in equalities. Author: Nikolaj Bjorner (nbjorner) 2015-8-19 Notes: --*/ #include "tactic/tactical.h" #include "tactic/arith/bound_manager.h" #include "ast/ast_pp.h" #include "ast/arith_decl_plugin.h" #include "ast/bv_decl_plugin.h" #include "ast/rewriter/rewriter_def.h" #include "ast/ast_util.h" #include "ast/ast_pp_util.h" class eq2bv_tactic : public tactic { struct eq_rewriter_cfg : public default_rewriter_cfg { ast_manager& m; eq2bv_tactic& t; bool is_fd(expr* x, expr* y, expr_ref& result) { expr* z; rational r; if (t.m_fd.find(x, z) && t.a.is_numeral(y, r)) { result = m.mk_eq(z, t.bv.mk_numeral(r, m.get_sort(z))); return true; } else { return false; } } br_status mk_app_core(func_decl* f, unsigned sz, expr*const* es, expr_ref& result) { if (m.is_eq(f)) { if (is_fd(es[0], es[1], result)) { return BR_DONE; } else if (is_fd(es[1], es[0], result)) { return BR_DONE; } } return BR_FAILED; } bool rewrite_patterns() const { return false; } bool flat_assoc(func_decl * f) 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; return mk_app_core(f, num, args, result); } eq_rewriter_cfg(eq2bv_tactic& t):m(t.m), t(t) {} }; class eq_rewriter : public rewriter_tpl { eq_rewriter_cfg m_cfg; public: eq_rewriter(eq2bv_tactic& t): rewriter_tpl(t.m, false, m_cfg), m_cfg(t) {} }; class bvmc : public model_converter { obj_map m_map; public: void insert(func_decl* c_new, func_decl* c_old) { m_map.insert(c_new, c_old); } void operator()(model_ref& mdl) override { ast_manager& m = mdl->get_manager(); bv_util bv(m); arith_util a(m); rational r; model_ref new_m = alloc(model, m); new_m->copy_func_interps(*mdl); new_m->copy_usort_interps(*mdl); unsigned sz = mdl->get_num_constants(), bvsz; for (unsigned i = 0; i < sz; ++i) { func_decl* f = mdl->get_constant(i), *g; expr* val = mdl->get_const_interp(f); if (m_map.find(f, g) && bv.is_numeral(val, r, bvsz)) { val = a.mk_numeral(r, true); new_m->register_decl(g, val); } else { new_m->register_decl(f, val); } } mdl = new_m; } model_converter* translate(ast_translation & translator) override { bvmc* v = alloc(bvmc); for (auto const& kv : m_map) { v->m_map.insert(translator(kv.m_key), translator(kv.m_value)); } return v; } void display(std::ostream & out) override { for (auto const& kv : m_map) { out << "(model-set " << kv.m_key->get_name() << " " << kv.m_value->get_name() << ")\n"; } } void get_units(obj_map& units) override { units.reset(); } }; public: ast_manager & m; arith_util a; bv_util bv; eq_rewriter m_rw; expr_ref_vector m_trail; bound_manager m_bounds; obj_map m_fd; obj_map m_max; expr_mark m_nonfd; ptr_vector m_todo; eq2bv_tactic(ast_manager & _m): m(_m), a(m), bv(m), m_rw(*this), m_trail(m), m_bounds(m) { } ~eq2bv_tactic() override { } void updt_params(params_ref const & p) override { } void operator()(goal_ref const & g, goal_ref_buffer & result) override { SASSERT(g->is_well_sorted()); m_trail.reset(); m_fd.reset(); m_max.reset(); m_nonfd.reset(); m_bounds.reset(); ref mc1 = alloc(bvmc); tactic_report report("eq2bv", *g); m_bounds(*g); for (unsigned i = 0; i < g->size(); i++) { collect_fd(g->form(i)); } cleanup_fd(mc1); if (m_max.empty()) { result.push_back(g.get()); return; } for (unsigned i = 0; i < g->size(); i++) { expr_ref new_curr(m); proof_ref new_pr(m); if (is_bound(g->form(i))) { g->update(i, m.mk_true(), nullptr, nullptr); continue; } m_rw(g->form(i), new_curr, new_pr); if (m.proofs_enabled() && !new_pr) { new_pr = m.mk_rewrite(g->form(i), new_curr); new_pr = m.mk_modus_ponens(g->pr(i), new_pr); } g->update(i, new_curr, new_pr, g->dep(i)); } obj_map::iterator it = m_max.begin(), end = m_max.end(); for (; it != end; ++it) { expr* c = it->m_key; bool strict; rational r; expr_ref fml(m); if (m_bounds.has_lower(c, r, strict) && !r.is_neg()) { SASSERT(!strict); expr* d = m_fd.find(c); fml = bv.mk_ule(bv.mk_numeral(r, m.get_sort(d)), d); g->assert_expr(fml, m_bounds.lower_dep(c)); } if (m_bounds.has_upper(c, r, strict) && !r.is_neg()) { SASSERT(!strict); expr* d = m_fd.find(c); fml = bv.mk_ule(d, bv.mk_numeral(r, m.get_sort(d))); g->assert_expr(fml, m_bounds.upper_dep(c)); } } g->inc_depth(); g->add(mc1.get()); result.push_back(g.get()); TRACE("pb", g->display(tout);); SASSERT(g->is_well_sorted()); } tactic * translate(ast_manager & m) override { return alloc(eq2bv_tactic, m); } void collect_param_descrs(param_descrs & r) override { } void cleanup() override { } void cleanup_fd(ref& mc) { SASSERT(m_fd.empty()); ptr_vector rm; obj_map::iterator it = m_max.begin(), end = m_max.end(); for (; it != end; ++it) { if (m_nonfd.is_marked(it->m_key)) { rm.push_back(it->m_key); } } for (unsigned i = 0; i < rm.size(); ++i) { m_max.erase(rm[i]); } it = m_max.begin(); end = m_max.end(); for (; it != end; ++it) { // ensure there are enough elements. bool strict; rational val; if (m_bounds.has_upper(it->m_key, val, strict)) { SASSERT(!strict); if (val.get_unsigned() > it->m_value) it->m_value = val.get_unsigned(); } else { ++it->m_value; } unsigned p = next_power_of_two(it->m_value); if (p <= 1) p = 2; if (it->m_value == p) p *= 2; unsigned n = log2(p); app* z = m.mk_fresh_const("z", bv.mk_sort(n)); m_trail.push_back(z); m_fd.insert(it->m_key, z); mc->insert(z->get_decl(), to_app(it->m_key)->get_decl()); } } bool is_var_const_pair(expr* e, expr* c, unsigned& k) { rational r; if (is_uninterp_const(e) && a.is_numeral(c, r) && r.is_unsigned() && !m_nonfd.is_marked(e)) { k = r.get_unsigned(); return true; } else { return false; } } bool is_upper(expr* f) { expr* e1, *e2; unsigned k; if ((a.is_le(f, e1, e2) || a.is_ge(f, e2, e1)) && is_var_const_pair(e1, e2, k)) { SASSERT(m_bounds.has_upper(e1)); return true; } return false; } bool is_lower(expr* f) { expr* e1, *e2; unsigned k; if ((a.is_le(f, e1, e2) || a.is_ge(f, e2, e1)) && is_var_const_pair(e2, e1, k)) { SASSERT(m_bounds.has_lower(e2)); return true; } return false; } bool is_bound(expr* f) { return is_lower(f) || is_upper(f); } void collect_fd(expr* f) { if (is_bound(f)) return; m_todo.push_back(f); while (!m_todo.empty()) { f = m_todo.back(); m_todo.pop_back(); if (m_nonfd.is_marked(f)) { continue; } m_nonfd.mark(f, true); expr* e1, *e2; if (m.is_eq(f, e1, e2)) { if (is_fd(e1, e2) || is_fd(e2, e1)) { continue; } } if (is_app(f)) { m_todo.append(to_app(f)->get_num_args(), to_app(f)->get_args()); } else if (is_quantifier(f)) { m_todo.push_back(to_quantifier(f)->get_expr()); } } } bool is_fd(expr* v, expr* c) { unsigned val; rational r; if (is_uninterp_const(v) && a.is_numeral(c, r) && !m_nonfd.is_marked(v) && a.is_int(v) && r.is_unsigned()) { val = r.get_unsigned(); add_fd(v, val); return true; } return false; } void add_fd(expr* c, unsigned val) { unsigned val2; if (!m_max.find(c, val2) || val2 < val) { m_max.insert(c, val); } } }; tactic * mk_eq2bv_tactic(ast_manager & m) { return clean(alloc(eq2bv_tactic, m)); } z3-z3-4.8.7/src/tactic/arith/eq2bv_tactic.h000066400000000000000000000010771356505360400203200ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: eq2bv_tactic.h Abstract: Extract integer variables that are used as finite domain indicators. The integer variables can only occur in equalities. Author: Nikolaj Bjorner (nbjorner) 2015-8-19 Notes: --*/ #ifndef EQ2BV_TACTIC_H_ #define EQ2BV_TACTIC_H_ #include "util/params.h" class ast_manager; class tactic; tactic * mk_eq2bv_tactic(ast_manager & m); /* ADD_TACTIC("eq2bv", "convert integer variables used as finite domain elements to bit-vectors.", "mk_eq2bv_tactic(m)") */ #endif z3-z3-4.8.7/src/tactic/arith/factor_tactic.cpp000066400000000000000000000256501356505360400211150ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: factor_tactic.cpp Abstract: Polynomial factorization tactic. Author: Leonardo de Moura (leonardo) 2012-02-03 Revision History: --*/ #include "tactic/tactical.h" #include "ast/expr2polynomial.h" #include "ast/rewriter/rewriter_def.h" class factor_tactic : public tactic { struct rw_cfg : public default_rewriter_cfg { ast_manager & m; arith_util m_util; unsynch_mpq_manager m_qm; polynomial::manager m_pm; default_expr2polynomial m_expr2poly; polynomial::factor_params m_fparams; bool m_split_factors; rw_cfg(ast_manager & _m, params_ref const & p): m(_m), m_util(_m), m_pm(m.limit(), m_qm), m_expr2poly(m, m_pm) { updt_params(p); } void updt_params(params_ref const & p) { m_split_factors = p.get_bool("split_factors", true); m_fparams.updt_params(p); } expr * mk_mul(unsigned sz, expr * const * args) { SASSERT(sz > 0); if (sz == 1) return args[0]; return m_util.mk_mul(sz, args); } expr * mk_zero_for(expr * arg) { return m_util.mk_numeral(rational(0), m_util.is_int(arg)); } // p1^k1 * p2^k2 = 0 --> p1*p2 = 0 void mk_eq(polynomial::factors const & fs, expr_ref & result) { expr_ref_buffer args(m); expr_ref arg(m); for (unsigned i = 0; i < fs.distinct_factors(); i++) { m_expr2poly.to_expr(fs[i], true, arg); args.push_back(arg); } result = m.mk_eq(mk_mul(args.size(), args.c_ptr()), mk_zero_for(arg)); } // p1^k1 * p2^k2 = 0 --> p1 = 0 or p2 = 0 void mk_split_eq(polynomial::factors const & fs, expr_ref & result) { expr_ref_buffer args(m); expr_ref arg(m); for (unsigned i = 0; i < fs.distinct_factors(); i++) { m_expr2poly.to_expr(fs[i], true, arg); args.push_back(m.mk_eq(arg, mk_zero_for(arg))); } if (args.size() == 1) result = args[0]; else result = m.mk_or(args.size(), args.c_ptr()); } decl_kind flip(decl_kind k) { SASSERT(k == OP_LT || k == OP_GT || k == OP_LE || k == OP_GE); switch (k) { case OP_LT: return OP_GT; case OP_LE: return OP_GE; case OP_GT: return OP_LT; case OP_GE: return OP_LE; default: UNREACHABLE(); return k; } } // p1^{2*k1} * p2^{2*k2 + 1} >=< 0 // --> // (p1^2)*p2 >=<0 void mk_comp(decl_kind k, polynomial::factors const & fs, expr_ref & result) { SASSERT(k == OP_LT || k == OP_GT || k == OP_LE || k == OP_GE); expr_ref_buffer args(m); expr_ref arg(m); for (unsigned i = 0; i < fs.distinct_factors(); i++) { m_expr2poly.to_expr(fs[i], true, arg); if (fs.get_degree(i) % 2 == 0) arg = m_util.mk_power(arg, m_util.mk_numeral(rational(2), m_util.is_int(arg))); args.push_back(arg); } expr * lhs = mk_mul(args.size(), args.c_ptr()); result = m.mk_app(m_util.get_family_id(), k, lhs, mk_zero_for(lhs)); } // See mk_split_strict_comp and mk_split_nonstrict_comp void split_even_odd(bool strict, polynomial::factors const & fs, expr_ref_buffer & even_eqs, expr_ref_buffer & odd_factors) { expr_ref arg(m); for (unsigned i = 0; i < fs.distinct_factors(); i++) { m_expr2poly.to_expr(fs[i], true, arg); if (fs.get_degree(i) % 2 == 0) { expr * eq = m.mk_eq(arg, mk_zero_for(arg)); if (strict) even_eqs.push_back(m.mk_not(eq)); else even_eqs.push_back(eq); } else { odd_factors.push_back(arg); } } } // Strict case // p1^{2*k1} * p2^{2*k2 + 1} >< 0 // --> // p1 != 0 and p2 >< 0 // // Nonstrict // p1^{2*k1} * p2^{2*k2 + 1} >=< 0 // --> // p1 = 0 or p2 >=< 0 // void mk_split_comp(decl_kind k, polynomial::factors const & fs, expr_ref & result) { SASSERT(k == OP_LT || k == OP_GT || k == OP_LE || k == OP_GE); bool strict = (k == OP_LT) || (k == OP_GT); expr_ref_buffer args(m); expr_ref_buffer odd_factors(m); split_even_odd(strict, fs, args, odd_factors); if (odd_factors.empty()) { if (k == OP_LT) { result = m.mk_false(); return; } if (k == OP_GE) { result = m.mk_true(); return; } } else { args.push_back(m.mk_app(m_util.get_family_id(), k, mk_mul(odd_factors.size(), odd_factors.c_ptr()), mk_zero_for(odd_factors[0]))); } SASSERT(!args.empty()); if (args.size() == 1) result = args[0]; else if (strict) result = m.mk_and(args.size(), args.c_ptr()); else result = m.mk_or(args.size(), args.c_ptr()); } br_status factor(func_decl * f, expr * lhs, expr * rhs, expr_ref & result) { polynomial_ref p1(m_pm); polynomial_ref p2(m_pm); scoped_mpz d1(m_qm); scoped_mpz d2(m_qm); m_expr2poly.to_polynomial(lhs, p1, d1); m_expr2poly.to_polynomial(rhs, p2, d2); TRACE("factor_tactic_bug", tout << "lhs: " << mk_ismt2_pp(lhs, m) << "\n"; tout << "p1: " << p1 << "\n"; tout << "d1: " << d1 << "\n"; tout << "rhs: " << mk_ismt2_pp(rhs, m) << "\n"; tout << "p2: " << p2 << "\n"; tout << "d2: " << d2 << "\n";); scoped_mpz lcm(m_qm); m_qm.lcm(d1, d2, lcm); m_qm.div(lcm, d1, d1); m_qm.div(lcm, d2, d2); m_qm.neg(d2); polynomial_ref p(m_pm); p = m_pm.addmul(d1, m_pm.mk_unit(), p1, d2, m_pm.mk_unit(), p2); if (is_const(p)) return BR_FAILED; polynomial::factors fs(m_pm); TRACE("factor_tactic_bug", tout << "p: " << p << "\n";); m_pm.factor(p, fs, m_fparams); SASSERT(fs.distinct_factors() > 0); TRACE("factor_tactic_bug", tout << "factors:\n"; fs.display(tout); tout << "\n";); if (fs.distinct_factors() == 1 && fs.get_degree(0) == 1) return BR_FAILED; if (m.is_eq(f)) { if (m_split_factors) mk_split_eq(fs, result); else mk_eq(fs, result); } else { decl_kind k = f->get_decl_kind(); if (m_qm.is_neg(fs.get_constant())) k = flip(k); if (m_split_factors) mk_split_comp(k, fs, result); else mk_comp(k, fs, result); } return BR_DONE; } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { if (num != 2) return BR_FAILED; if (m.is_eq(f) && (m_util.is_arith_expr(args[0]) || m_util.is_arith_expr(args[1])) && (!m.is_bool(args[0]))) return factor(f, args[0], args[1], result); if (f->get_family_id() != m_util.get_family_id()) return BR_FAILED; switch (f->get_decl_kind()) { case OP_LT: case OP_GT: case OP_LE: case OP_GE: return factor(f, args[0], args[1], result); } return BR_FAILED; } }; struct rw : public rewriter_tpl { rw_cfg m_cfg; rw(ast_manager & m, params_ref const & p): rewriter_tpl(m, m.proofs_enabled(), m_cfg), m_cfg(m, p) { } }; struct imp { ast_manager & m; rw m_rw; 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("factor", *g); bool produce_proofs = g->proofs_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->inc_depth(); result.push_back(g.get()); TRACE("factor", g->display(tout);); SASSERT(g->is_well_sorted()); } }; imp * m_imp; params_ref m_params; public: factor_tactic(ast_manager & m, params_ref const & p): m_params(p) { m_imp = alloc(imp, m, p); } tactic * translate(ast_manager & m) override { return alloc(factor_tactic, m, m_params); } ~factor_tactic() override { dealloc(m_imp); } void updt_params(params_ref const & p) override { m_params = p; m_imp->m_rw.cfg().updt_params(p); } void collect_param_descrs(param_descrs & r) override { r.insert("split_factors", CPK_BOOL, "(default: true) apply simplifications such as (= (* p1 p2) 0) --> (or (= p1 0) (= p2 0))."); polynomial::factor_params::get_param_descrs(r); } void operator()(goal_ref const & in, goal_ref_buffer & result) override { try { (*m_imp)(in, result); } catch (z3_error & ex) { throw ex; } catch (z3_exception & ex) { throw tactic_exception(ex.msg()); } } void cleanup() override { imp * d = alloc(imp, m_imp->m, m_params); std::swap(d, m_imp); dealloc(d); } }; tactic * mk_factor_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(factor_tactic, m, p)); } z3-z3-4.8.7/src/tactic/arith/factor_tactic.h000066400000000000000000000007531356505360400205570ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: factor_tactic.h Abstract: Polynomial factorization tactic. Author: Leonardo de Moura (leonardo) 2012-02-03 Revision History: --*/ #ifndef FACTOR_TACTIC_H_ #define FACTOR_TACTIC_H_ #include "util/params.h" class ast_manager; class tactic; tactic * mk_factor_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("factor", "polynomial factorization.", "mk_factor_tactic(m, p)") */ #endif z3-z3-4.8.7/src/tactic/arith/fix_dl_var_tactic.cpp000066400000000000000000000260151356505360400217500ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: fix_dl_var_tactic.cpp Abstract: Fix a difference logic variable to 0. If the problem is in the difference logic fragment, that is, all arithmetic terms are of the form (x + k), and the arithmetic atoms are of the form x - y <= k or x - y = k. Then, we can set one variable to 0. This is useful because, many bounds can be exposed after this operation is performed. Author: Leonardo de Moura (leonardo) 2011-12-19 Revision History: --*/ #include "tactic/tactical.h" #include "ast/rewriter/th_rewriter.h" #include "tactic/generic_model_converter.h" #include "ast/arith_decl_plugin.h" #include "ast/expr_substitution.h" #include "ast/ast_smt2_pp.h" class fix_dl_var_tactic : public tactic { struct is_target { struct failed {}; ast_manager & m; arith_util & m_util; expr_fast_mark1 * m_visited; ptr_vector m_todo; obj_map m_occs; obj_map m_non_nested_occs; is_target(arith_util & u): m(u.get_manager()), m_util(u) { } void throw_failed(expr * ctx1, expr * ctx2 = nullptr) { TRACE("fix_dl_var", tout << mk_ismt2_pp(ctx1, m) << "\n"; if (ctx2) tout << mk_ismt2_pp(ctx2, m) << "\n";); throw failed(); } bool is_arith(expr * n) { sort * s = m.get_sort(n); return s->get_family_id() == m_util.get_family_id(); } // Return true if n is uninterpreted with respect to arithmetic. bool is_uninterp(expr * n) { return is_app(n) && to_app(n)->get_family_id() != m_util.get_family_id(); } // Remark: we say an expression is nested, if it occurs inside the boolean structure of the formula. // That is, the expression is not part of an unit clause comprising of a single inequality/equality. void inc_occ(expr * n, bool nested) { if (is_uninterp_const(n) && is_arith(n)) { obj_map::obj_map_entry * entry = m_occs.insert_if_not_there2(to_app(n), 0); entry->get_data().m_value++; if (!nested) { entry = m_non_nested_occs.insert_if_not_there2(to_app(n), 0); entry->get_data().m_value++; } } } void visit(expr * n, bool nested) { inc_occ(n, nested); if (!m_visited->is_marked(n)) { m_visited->mark(n); m_todo.push_back(n); } } void process_app(app * t) { unsigned num = t->get_num_args(); for (unsigned i = 0; i < num; i++) visit(t->get_arg(i), false); } void process_arith_atom(expr * lhs, expr * rhs, bool nested) { if (is_uninterp(lhs) && is_uninterp(rhs)) { visit(lhs, nested); visit(rhs, nested); return; } if (m_util.is_numeral(lhs)) std::swap(lhs, rhs); if (!m_util.is_numeral(rhs)) throw_failed(lhs, rhs); expr * t, * ms, * s; // check if lhs is of the form: (+ t (* (- 1) s)) if (m_util.is_add(lhs, t, ms) && m_util.is_times_minus_one(ms, s) && is_uninterp(t) && is_uninterp(s)) { visit(t, nested); visit(s, nested); } else { CTRACE("fix_dl_var", m_util.is_add(lhs, t, ms), s = 0; tout << "is_times_minus_one: " << m_util.is_times_minus_one(ms, s) << "\n"; tout << "is_uninterp(t): " << is_uninterp(t) << "\n"; tout << "t.family_id(): " << (is_app(t) ? to_app(t)->get_family_id() : -1) << "\n"; tout << "util.family_id: " << m_util.get_family_id() << "\n"; if (s) { tout << "is_uninterp(s): " << is_uninterp(s) << "\n"; tout << "s.family_id(): " << (is_app(s) ? to_app(s)->get_family_id() : -1) << "\n"; }); throw_failed(lhs, rhs); } } void process_eq(app * t, bool nested) { if (!is_arith(t->get_arg(0))) { process_app(t); return; } process_arith_atom(t->get_arg(0), t->get_arg(1), nested); } void process_arith(app * t, bool nested) { if (m.is_bool(t)) { process_arith_atom(t->get_arg(0), t->get_arg(1), nested); return; } // check if t is of the form c + k expr * c, * k; if (m_util.is_add(t, k, c) && is_uninterp(c) && m_util.is_numeral(k)) { visit(c, nested); } else { throw_failed(t); } } void process(expr * n) { if (m_visited->is_marked(n)) return; while (m.is_not(n, n)) ; if (is_app(n) && to_app(n)->get_family_id() == m_util.get_family_id()) { process_arith(to_app(n), false); return; } m_todo.push_back(n); m_visited->mark(n); while (!m_todo.empty()) { expr * n = m_todo.back(); m_todo.pop_back(); if (!is_app(n)) throw_failed(n); app * t = to_app(n); if (m.is_eq(t)) process_eq(t, true); else if (t->get_family_id() == m_util.get_family_id()) process_arith(t, true); else process_app(t); } } app * most_occs(obj_map & occs, unsigned & best) { app * r = nullptr; best = 0; obj_map::iterator it = occs.begin(); obj_map::iterator end = occs.end(); for (; it != end; ++it) { if (it->m_value > best) { best = it->m_value; r = it->m_key; } } return r; } // TODO make it a parameter #define NESTED_PENALTY 10 app * most_occs() { // We try to choose a variable that when set to 0 will generate many bounded variables. // That is why we give preference to variables occurring in non-nested inequalities. unsigned best1, best2; app * r1, * r2; r1 = most_occs(m_non_nested_occs, best1); r2 = most_occs(m_occs, best2); TRACE("fix_dl_var_choice", if (r1) { tout << "r1 occs: " << best1 << "\n"; tout << mk_ismt2_pp(r1, m) << "\n"; } if (r2) { tout << "r2 occs: " << best2 << "\n"; tout << mk_ismt2_pp(r2, m) << "\n"; }); if (best2 > NESTED_PENALTY * best1) return r2; else return r1; } app * operator()(goal const & g) { try { expr_fast_mark1 visited; m_visited = &visited; unsigned sz = g.size(); for (unsigned i = 0; i < sz; i++) { process(g.form(i)); } return most_occs(); } catch (const failed &) { return nullptr; } } }; struct imp { ast_manager & m; arith_util u; th_rewriter m_rw; bool m_produce_models; imp(ast_manager & _m, params_ref const & p): m(_m), u(m), m_rw(m, p) { } void updt_params(params_ref const & p) { m_rw.updt_params(p); } void operator()(goal_ref const & g, goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); tactic_report report("fix-dl-var", *g); bool produce_proofs = g->proofs_enabled(); m_produce_models = g->models_enabled(); app * var = is_target(u)(*g); if (var != nullptr) { IF_VERBOSE(TACTIC_VERBOSITY_LVL, verbose_stream() << "(fixing-at-zero " << var->get_decl()->get_name() << ")\n";); tactic_report report("fix-dl-var", *g); expr_substitution subst(m); app * zero = u.mk_numeral(rational(0), u.is_int(var)); subst.insert(var, zero); m_rw.set_substitution(&subst); if (m_produce_models) { generic_model_converter * mc = alloc(generic_model_converter, m, "fix_dl"); mc->add(var, zero); g->add(mc); } expr_ref new_curr(m); proof_ref new_pr(m); unsigned size = g->size(); for (unsigned idx = 0; !g->inconsistent() && 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->inc_depth(); } result.push_back(g.get()); TRACE("fix_dl_var", g->display(tout);); SASSERT(g->is_well_sorted()); } }; imp * m_imp; params_ref m_params; public: fix_dl_var_tactic(ast_manager & m, params_ref const & p): m_params(p) { m_imp = alloc(imp, m, p); } tactic * translate(ast_manager & m) override { return alloc(fix_dl_var_tactic, m, m_params); } ~fix_dl_var_tactic() override { dealloc(m_imp); } void updt_params(params_ref const & p) override { m_params = p; m_imp->updt_params(p); } void collect_param_descrs(param_descrs & r) override { th_rewriter::get_param_descrs(r); } void operator()(goal_ref const & in, goal_ref_buffer & result) override { try { (*m_imp)(in, result); } catch (rewriter_exception & ex) { throw tactic_exception(ex.msg()); } } void cleanup() override { imp * d = alloc(imp, m_imp->m, m_params); std::swap(d, m_imp); dealloc(d); } }; tactic * mk_fix_dl_var_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(fix_dl_var_tactic, m, p)); } z3-z3-4.8.7/src/tactic/arith/fix_dl_var_tactic.h000066400000000000000000000016021356505360400214100ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: fix_dl_var_tactic.h Abstract: Fix a difference logic variable to 0. If the problem is in the difference logic fragment, that is, all arithmetic terms are of the form (x + k), and the arithmetic atoms are of the form x - y <= k or x - y = k. Then, we can set one variable to 0. This is useful because, many bounds can be exposed after this operation is performed. Author: Leonardo (leonardo) 2011-12-29 Notes: --*/ #ifndef FIX_DL_VAR_TACTIC_H_ #define FIX_DL_VAR_TACTIC_H_ #include "util/params.h" class ast_manager; class tactic; tactic * mk_fix_dl_var_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("fix-dl-var", "if goal is in the difference logic fragment, then fix the variable with the most number of occurrences at 0.", "mk_fix_dl_var_tactic(m, p)") */ #endif z3-z3-4.8.7/src/tactic/arith/fm_tactic.cpp000066400000000000000000001711461356505360400202430ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: fm_tactic.cpp Abstract: Use Fourier-Motzkin to eliminate variables. This strategy can handle conditional bounds (i.e., clauses with at most one constraint). The strategy mk_occf can be used to put the formula in OCC form. Author: Leonardo de Moura (leonardo) 2012-02-04. Revision History: --*/ #include "tactic/arith/fm_tactic.h" #include "tactic/tactical.h" #include "ast/arith_decl_plugin.h" #include "ast/for_each_expr.h" #include "ast/ast_smt2_pp.h" #include "ast/ast_pp.h" #include "util/id_gen.h" #include "model/model_evaluator.h" #include "model/model_v2_pp.h" #include "tactic/core/simplify_tactic.h" class fm_tactic : public tactic { typedef ptr_vector clauses; typedef unsigned var; typedef int bvar; typedef int literal; typedef svector var_vector; struct fm_model_converter : public model_converter { ast_manager & m; ptr_vector m_xs; vector m_clauses; enum r_kind { NONE, LOWER, UPPER }; bool is_false(model_ref & md, app * p) { SASSERT(is_uninterp_const(p)); expr * val = md->get_const_interp(p->get_decl()); if (val == nullptr) { // if it is don't care, then set to false md->register_decl(p->get_decl(), m.mk_false()); return true; } return m.is_false(val); } r_kind process(func_decl * x, expr * cls, arith_util & u, model& ev, rational & r) { unsigned num_lits; expr * const * lits; if (m.is_or(cls)) { num_lits = to_app(cls)->get_num_args(); lits = to_app(cls)->get_args(); } else { num_lits = 1; lits = &cls; } bool is_lower = false; bool found = false; for (unsigned i = 0; i < num_lits; i++) { expr * l = lits[i]; expr * atom; if (is_uninterp_const(l) || (m.is_not(l, atom) && is_uninterp_const(atom))) { if (ev.is_true(l)) return NONE; // clause was satisfied } else { found = true; bool neg = m.is_not(l, l); SASSERT(u.is_le(l) || u.is_ge(l)); bool strict = neg; rational a_val; if (u.is_ge(l)) neg = !neg; expr * lhs = to_app(l)->get_arg(0); expr * rhs = to_app(l)->get_arg(1); rational c; u.is_numeral(rhs, c); if (neg) c.neg(); unsigned num_mons; expr * const * mons; if (u.is_add(lhs)) { num_mons = to_app(lhs)->get_num_args(); mons = to_app(lhs)->get_args(); } else { num_mons = 1; mons = &lhs; } for (unsigned j = 0; j < num_mons; j++) { expr * monomial = mons[j]; expr * ai; expr * xi; rational ai_val; if (u.is_mul(monomial, ai, xi)) { u.is_numeral(ai, ai_val); } else { xi = monomial; ai_val = rational(1); } if (u.is_to_real(xi)) xi = to_app(xi)->get_arg(0); SASSERT(is_uninterp_const(xi)); if (x == to_app(xi)->get_decl()) { a_val = ai_val; if (neg) a_val.neg(); } else { expr_ref val(m); val = ev(monomial); SASSERT(u.is_numeral(val)); rational tmp; u.is_numeral(val, tmp); if (neg) tmp.neg(); c -= tmp; } } if (u.is_int(x->get_range()) && strict) { // a*x < c --> a*x <= c-1 SASSERT(c.is_int()); c--; } is_lower = a_val.is_neg(); c /= a_val; if (u.is_int(x->get_range())) { if (is_lower) c = ceil(c); else c = floor(c); } r = c; } } (void)found; SASSERT(found); return is_lower ? LOWER : UPPER; } public: fm_model_converter(ast_manager & _m):m(_m) {} ~fm_model_converter() override { m.dec_array_ref(m_xs.size(), m_xs.c_ptr()); vector::iterator it = m_clauses.begin(); vector::iterator end = m_clauses.end(); for (; it != end; ++it) m.dec_array_ref(it->size(), it->c_ptr()); } void insert(func_decl * x, clauses & c) { m.inc_ref(x); m.inc_array_ref(c.size(), c.c_ptr()); m_xs.push_back(x); m_clauses.push_back(clauses()); m_clauses.back().swap(c); } void get_units(obj_map& units) override { units.reset(); } void operator()(model_ref & md) override { TRACE("fm_mc", model_v2_pp(tout, *md); display(tout);); model::scoped_model_completion _sc(*md, true); //model_evaluator ev(*(md.get())); //ev.set_model_completion(true); arith_util u(m); unsigned i = m_xs.size(); while (i > 0) { --i; func_decl * x = m_xs[i]; rational lower; rational upper; rational val; bool has_lower = false; bool has_upper = false; TRACE("fm_mc", tout << "processing " << x->get_name() << "\n";); clauses::iterator it = m_clauses[i].begin(); clauses::iterator end = m_clauses[i].end(); for (; it != end; ++it) { if (m.canceled()) throw tactic_exception(m.limit().get_cancel_msg()); switch (process(x, *it, u, *md, val)) { case NONE: TRACE("fm_mc", tout << "no bound for:\n" << mk_ismt2_pp(*it, m) << "\n";); break; case LOWER: TRACE("fm_mc", tout << "lower bound: " << val << " for:\n" << mk_ismt2_pp(*it, m) << "\n";); if (!has_lower || val > lower) lower = val; has_lower = true; break; case UPPER: TRACE("fm_mc", tout << "upper bound: " << val << " for:\n" << mk_ismt2_pp(*it, m) << "\n";); if (!has_upper || val < upper) upper = val; has_upper = true; break; } } expr * x_val; if (u.is_int(x->get_range())) { if (has_lower) x_val = u.mk_numeral(lower, true); else if (has_upper) x_val = u.mk_numeral(upper, true); else x_val = u.mk_numeral(rational(0), true); } else { if (has_lower && has_upper) x_val = u.mk_numeral((upper + lower)/rational(2), false); else if (has_lower) x_val = u.mk_numeral(lower + rational(1), false); else if (has_upper) x_val = u.mk_numeral(upper - rational(1), false); else x_val = u.mk_numeral(rational(0), false); } TRACE("fm_mc", tout << x->get_name() << " --> " << mk_ismt2_pp(x_val, m) << "\n";); md->register_decl(x, x_val); } TRACE("fm_mc", model_v2_pp(tout, *md);); } void display(std::ostream & out) override { out << "(fm-model-converter"; SASSERT(m_xs.size() == m_clauses.size()); unsigned sz = m_xs.size(); for (unsigned i = 0; i < sz; i++) { out << "\n(" << m_xs[i]->get_name(); clauses const & cs = m_clauses[i]; clauses::const_iterator it = cs.begin(); clauses::const_iterator end = cs.end(); for (; it != end; ++it) { out << "\n " << mk_ismt2_pp(*it, m, 2); } out << ")"; } out << ")\n"; } model_converter * translate(ast_translation & translator) override { ast_manager & to_m = translator.to(); fm_model_converter * res = alloc(fm_model_converter, to_m); unsigned sz = m_xs.size(); for (unsigned i = 0; i < sz; i++) { func_decl * new_x = translator(m_xs[i]); to_m.inc_ref(new_x); res->m_xs.push_back(new_x); clauses const & cs = m_clauses[i]; res->m_clauses.push_back(clauses()); clauses & new_cs = res->m_clauses.back(); clauses::const_iterator it = cs.begin(); clauses::const_iterator end = cs.end(); for (; it != end; ++it) { app * new_c = translator(*it); to_m.inc_ref(new_c); new_cs.push_back(new_c); } } return res; } }; // Encode the constraint // lits \/ ( as[0]*xs[0] + ... + as[num_vars-1]*xs[num_vars-1] <= c // if strict is true, then <= is <. struct constraint { static unsigned get_obj_size(unsigned num_lits, unsigned num_vars) { return sizeof(constraint) + num_lits*sizeof(literal) + num_vars*(sizeof(var) + sizeof(rational)); } unsigned m_id; unsigned m_num_lits:29; unsigned m_strict:1; unsigned m_dead:1; unsigned m_mark:1; unsigned m_num_vars; literal * m_lits; var * m_xs; rational * m_as; rational m_c; expr_dependency * m_dep; ~constraint() { rational * it = m_as; rational * end = it + m_num_vars; for (; it != end; ++it) it->~rational(); } unsigned hash() const { return hash_u(m_id); } }; typedef ptr_vector constraints; class constraint_set { unsigned_vector m_id2pos; constraints m_set; public: typedef constraints::const_iterator iterator; bool contains(constraint const & c) const { if (c.m_id >= m_id2pos.size()) return false; return m_id2pos[c.m_id] != UINT_MAX; } bool empty() const { return m_set.empty(); } unsigned size() const { return m_set.size(); } void insert(constraint & c) { unsigned id = c.m_id; m_id2pos.reserve(id+1, UINT_MAX); if (m_id2pos[id] != UINT_MAX) return; // already in the set unsigned pos = m_set.size(); m_id2pos[id] = pos; m_set.push_back(&c); } void erase(constraint & c) { unsigned id = c.m_id; if (id >= m_id2pos.size()) return; unsigned pos = m_id2pos[id]; if (pos == UINT_MAX) return; m_id2pos[id] = UINT_MAX; unsigned last_pos = m_set.size() - 1; if (pos != last_pos) { constraint * last_c = m_set[last_pos]; m_set[pos] = last_c; m_id2pos[last_c->m_id] = pos; } m_set.pop_back(); } constraint & erase() { SASSERT(!empty()); constraint & c = *m_set.back(); m_id2pos[c.m_id] = UINT_MAX; m_set.pop_back(); return c; } void reset() { m_id2pos.reset(); m_set.reset(); } void finalize() { m_id2pos.finalize(); m_set.finalize(); } iterator begin() const { return m_set.begin(); } iterator end() const { return m_set.end(); } }; struct imp { ast_manager & m; small_object_allocator m_allocator; arith_util m_util; constraints m_constraints; expr_ref_vector m_bvar2expr; signed_char_vector m_bvar2sign; obj_map m_expr2bvar; char_vector m_is_int; char_vector m_forbidden; expr_ref_vector m_var2expr; obj_map m_expr2var; unsigned_vector m_var2pos; vector m_lowers; vector m_uppers; obj_hashtable m_forbidden_set; // variables that cannot be eliminated because occur in non OCC ineq part goal_ref m_new_goal; ref m_mc; id_gen m_id_gen; bool m_produce_models; bool m_fm_real_only; unsigned m_fm_limit; unsigned m_fm_cutoff1; unsigned m_fm_cutoff2; unsigned m_fm_extra; bool m_fm_occ; unsigned long long m_max_memory; unsigned m_counter; bool m_inconsistent; expr_dependency_ref m_inconsistent_core; constraint_set m_sub_todo; // --------------------------- // // OCC clause recognizer // // --------------------------- bool is_literal(expr * t) const { expr * atom; return is_uninterp_const(t) || (m.is_not(t, atom) && is_uninterp_const(atom)); } bool is_constraint(expr * t) const { return !is_literal(t); } bool is_var(expr * t, expr * & x) const { if (is_uninterp_const(t)) { x = t; return true; } else if (m_util.is_to_real(t) && is_uninterp_const(to_app(t)->get_arg(0))) { x = to_app(t)->get_arg(0); return true; } return false; } bool is_var(expr * t) const { expr * x; return is_var(t, x); } bool is_linear_mon_core(expr * t, expr * & x) const { expr * c; if (m_util.is_mul(t, c, x) && m_util.is_numeral(c) && is_var(x, x)) return true; return is_var(t, x); } bool is_linear_mon(expr * t) const { expr * x; return is_linear_mon_core(t, x); } bool is_linear_pol(expr * t) const { unsigned num_mons; expr * const * mons; if (m_util.is_add(t)) { num_mons = to_app(t)->get_num_args(); mons = to_app(t)->get_args(); } else { num_mons = 1; mons = &t; } expr_fast_mark2 visited; bool all_forbidden = true; for (unsigned i = 0; i < num_mons; i++) { expr * x; if (!is_linear_mon_core(mons[i], x)) return false; if (visited.is_marked(x)) return false; // duplicates are not supported... must simplify first visited.mark(x); if (!m_forbidden_set.contains(to_app(x)->get_decl()) && (!m_fm_real_only || !m_util.is_int(x))) all_forbidden = false; } return !all_forbidden; } bool is_linear_ineq(expr * t) const { m.is_not(t, t); expr * lhs, * rhs; TRACE("is_occ_bug", tout << mk_pp(t, m) << "\n";); if (m_util.is_le(t, lhs, rhs) || m_util.is_ge(t, lhs, rhs)) { if (!m_util.is_numeral(rhs)) return false; return is_linear_pol(lhs); } return false; } bool is_occ(expr * t) { if (m_fm_occ && m.is_or(t)) { unsigned num = to_app(t)->get_num_args(); bool found = false; for (unsigned i = 0; i < num; i++) { expr * l = to_app(t)->get_arg(i); if (is_literal(l)) { continue; } else if (is_linear_ineq(l)) { if (found) return false; found = true; } else { return false; } } return found; } return is_linear_ineq(t); } // --------------------------- // // Memory mng // // --------------------------- void del_constraint(constraint * c) { m.dec_ref(c->m_dep); m_sub_todo.erase(*c); m_id_gen.recycle(c->m_id); c->~constraint(); unsigned sz = constraint::get_obj_size(c->m_num_lits, c->m_num_vars); m_allocator.deallocate(sz, c); } void del_constraints(unsigned sz, constraint * const * cs) { for (unsigned i = 0; i < sz; i++) del_constraint(cs[i]); } void reset_constraints() { del_constraints(m_constraints.size(), m_constraints.c_ptr()); m_constraints.reset(); } constraint * mk_constraint(unsigned num_lits, literal * lits, unsigned num_vars, var * xs, rational * as, rational & c, bool strict, expr_dependency * dep) { unsigned sz = constraint::get_obj_size(num_lits, num_vars); char * mem = static_cast(m_allocator.allocate(sz)); char * mem_as = mem + sizeof(constraint); char * mem_lits = mem_as + sizeof(rational)*num_vars; char * mem_xs = mem_lits + sizeof(literal)*num_lits; constraint * cnstr = new (mem) constraint(); cnstr->m_id = m_id_gen.mk(); cnstr->m_num_lits = num_lits; cnstr->m_dead = false; cnstr->m_mark = false; cnstr->m_strict = strict; cnstr->m_num_vars = num_vars; cnstr->m_lits = reinterpret_cast(mem_lits); for (unsigned i = 0; i < num_lits; i++) cnstr->m_lits[i] = lits[i]; cnstr->m_xs = reinterpret_cast(mem_xs); cnstr->m_as = reinterpret_cast(mem_as); for (unsigned i = 0; i < num_vars; i++) { TRACE("mk_constraint_bug", tout << "xs[" << i << "]: " << xs[i] << "\n";); cnstr->m_xs[i] = xs[i]; new (cnstr->m_as + i) rational(as[i]); } cnstr->m_c = c; DEBUG_CODE({ for (unsigned i = 0; i < num_vars; i++) { SASSERT(cnstr->m_xs[i] == xs[i]); SASSERT(cnstr->m_as[i] == as[i]); } }); cnstr->m_dep = dep; m.inc_ref(dep); return cnstr; } // --------------------------- // // Util // // --------------------------- unsigned num_vars() const { return m_is_int.size(); } // multiply as and c, by the lcm of their denominators void mk_int(unsigned num, rational * as, rational & c) { rational l = denominator(c); for (unsigned i = 0; i < num; i++) l = lcm(l, denominator(as[i])); if (l.is_one()) return; c *= l; SASSERT(c.is_int()); for (unsigned i = 0; i < num; i++) { as[i] *= l; SASSERT(as[i].is_int()); } } void normalize_coeffs(constraint & c) { if (c.m_num_vars == 0) return; // compute gcd of all coefficients rational g = c.m_c; if (g.is_neg()) g.neg(); for (unsigned i = 0; i < c.m_num_vars; i++) { if (g.is_one()) break; if (c.m_as[i].is_pos()) g = gcd(c.m_as[i], g); else g = gcd(-c.m_as[i], g); } if (g.is_one()) return; c.m_c /= g; for (unsigned i = 0; i < c.m_num_vars; i++) c.m_as[i] /= g; } void display(std::ostream & out, constraint const & c) const { for (unsigned i = 0; i < c.m_num_lits; i++) { literal l = c.m_lits[i]; if (sign(l)) out << "~"; bvar p = lit2bvar(l); out << mk_ismt2_pp(m_bvar2expr[p], m); out << " "; } out << "("; if (c.m_num_vars == 0) out << "0"; for (unsigned i = 0; i < c.m_num_vars; i++) { if (i > 0) out << " + "; if (!c.m_as[i].is_one()) out << c.m_as[i] << "*"; out << mk_ismt2_pp(m_var2expr.get(c.m_xs[i]), m); } if (c.m_strict) out << " < "; else out << " <= "; out << c.m_c; out << ")"; } /** \brief Return true if c1 subsumes c2 c1 subsumes c2 If 1) All literals of c1 are literals of c2 2) polynomial of c1 == polynomial of c2 3) c1.m_c <= c2.m_c */ bool subsumes(constraint const & c1, constraint const & c2) { if (&c1 == &c2) return false; // quick checks first if (c1.m_num_lits > c2.m_num_lits) return false; if (c1.m_num_vars != c2.m_num_vars) return false; if (c1.m_c > c2.m_c) return false; if (!c1.m_strict && c2.m_strict && c1.m_c == c2.m_c) return false; m_counter += c1.m_num_lits + c2.m_num_lits; for (unsigned i = 0; i < c1.m_num_vars; i++) { m_var2pos[c1.m_xs[i]] = i; } bool failed = false; for (unsigned i = 0; i < c2.m_num_vars; i++) { unsigned pos1 = m_var2pos[c2.m_xs[i]]; if (pos1 == UINT_MAX || c1.m_as[pos1] != c2.m_as[i]) { failed = true; break; } } for (unsigned i = 0; i < c1.m_num_vars; i++) { m_var2pos[c1.m_xs[i]] = UINT_MAX; } if (failed) return false; for (unsigned i = 0; i < c2.m_num_lits; i++) { literal l = c2.m_lits[i]; bvar b = lit2bvar(l); SASSERT(m_bvar2sign[b] == 0); m_bvar2sign[b] = sign(l) ? -1 : 1; } for (unsigned i = 0; i < c1.m_num_lits; i++) { literal l = c1.m_lits[i]; bvar b = lit2bvar(l); char s = sign(l) ? -1 : 1; if (m_bvar2sign[b] != s) { failed = true; break; } } for (unsigned i = 0; i < c2.m_num_lits; i++) { literal l = c2.m_lits[i]; bvar b = lit2bvar(l); m_bvar2sign[b] = 0; } if (failed) return false; return true; } void backward_subsumption(constraint const & c) { if (c.m_num_vars == 0) return; var best = UINT_MAX; unsigned best_sz = UINT_MAX; bool best_lower = false; for (unsigned i = 0; i < c.m_num_vars; i++) { var xi = c.m_xs[i]; if (is_forbidden(xi)) continue; // variable is not in the index bool neg_a = c.m_as[i].is_neg(); constraints & cs = neg_a ? m_lowers[xi] : m_uppers[xi]; if (cs.size() < best_sz) { best = xi; best_sz = cs.size(); best_lower = neg_a; } } if (best_sz == 0) return; if (best == UINT_MAX) return; // none of the c variables are in the index. constraints & cs = best_lower ? m_lowers[best] : m_uppers[best]; m_counter += cs.size(); constraints::iterator it = cs.begin(); constraints::iterator it2 = it; constraints::iterator end = cs.end(); for (; it != end; ++it) { constraint * c2 = *it; if (c2->m_dead) continue; if (subsumes(c, *c2)) { TRACE("fm_subsumption", display(tout, c); tout << "\nsubsumed:\n"; display(tout, *c2); tout << "\n";); c2->m_dead = true; continue; } *it2 = *it; ++it2; } cs.set_end(it2); } void subsume() { while (!m_sub_todo.empty()) { constraint & c = m_sub_todo.erase(); if (c.m_dead) continue; backward_subsumption(c); } } // --------------------------- // // Initialization // // --------------------------- imp(ast_manager & _m, params_ref const & p): m(_m), m_allocator("fm-tactic"), m_util(m), m_bvar2expr(m), m_var2expr(m), m_inconsistent_core(m) { updt_params(p); } ~imp() { reset_constraints(); } void updt_params(params_ref const & p) { m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); m_fm_real_only = p.get_bool("fm_real_only", true); m_fm_limit = p.get_uint("fm_limit", 5000000); m_fm_cutoff1 = p.get_uint("fm_cutoff1", 8); m_fm_cutoff2 = p.get_uint("fm_cutoff2", 256); m_fm_extra = p.get_uint("fm_extra", 0); m_fm_occ = p.get_bool("fm_occ", false); } struct forbidden_proc { imp & m_owner; forbidden_proc(imp & o):m_owner(o) {} void operator()(::var * n) {} void operator()(app * n) { if (is_uninterp_const(n) && m_owner.m.get_sort(n)->get_family_id() == m_owner.m_util.get_family_id()) { m_owner.m_forbidden_set.insert(n->get_decl()); } } void operator()(quantifier * n) {} }; void init_forbidden_set(goal const & g) { m_forbidden_set.reset(); expr_fast_mark1 visited; forbidden_proc proc(*this); unsigned sz = g.size(); for (unsigned i = 0; i < sz; i++) { expr * f = g.form(i); if (is_occ(f)) continue; TRACE("is_occ_bug", tout << "not OCC:\n" << mk_ismt2_pp(f, m) << "\n";); quick_for_each_expr(proc, visited, f); } } void init(goal const & g) { m_sub_todo.reset(); m_id_gen.reset(); reset_constraints(); m_bvar2expr.reset(); m_bvar2sign.reset(); m_bvar2expr.push_back(nullptr); // bvar 0 is not used m_bvar2sign.push_back(0); m_expr2var.reset(); m_is_int.reset(); m_var2pos.reset(); m_forbidden.reset(); m_var2expr.reset(); m_expr2var.reset(); m_lowers.reset(); m_uppers.reset(); m_new_goal = nullptr; m_mc = nullptr; m_counter = 0; m_inconsistent = false; m_inconsistent_core = nullptr; init_forbidden_set(g); } // --------------------------- // // Internal data-structures // // --------------------------- static bool sign(literal l) { return l < 0; } static bvar lit2bvar(literal l) { return l < 0 ? -l : l; } bool is_int(var x) const { return m_is_int[x] != 0; } bool is_forbidden(var x) const { return m_forbidden[x] != 0; } bool all_int(constraint const & c) const { for (unsigned i = 0; i < c.m_num_vars; i++) { if (!is_int(c.m_xs[i])) return false; } return true; } app * to_expr(constraint const & c) { expr * ineq; if (c.m_num_vars == 0) { // 0 < k (for k > 0) --> true // 0 <= 0 -- > true if (c.m_c.is_pos() || (!c.m_strict && c.m_c.is_zero())) return m.mk_true(); ineq = nullptr; } else { bool int_cnstr = all_int(c); ptr_buffer ms; for (unsigned i = 0; i < c.m_num_vars; i++) { expr * x = m_var2expr.get(c.m_xs[i]); if (!int_cnstr && is_int(c.m_xs[i])) x = m_util.mk_to_real(x); if (c.m_as[i].is_one()) ms.push_back(x); else ms.push_back(m_util.mk_mul(m_util.mk_numeral(c.m_as[i], int_cnstr), x)); } expr * lhs; if (c.m_num_vars == 1) lhs = ms[0]; else lhs = m_util.mk_add(ms.size(), ms.c_ptr()); expr * rhs = m_util.mk_numeral(c.m_c, int_cnstr); if (c.m_strict) { ineq = m.mk_not(m_util.mk_ge(lhs, rhs)); } else { ineq = m_util.mk_le(lhs, rhs); } } if (c.m_num_lits == 0) { if (ineq) return to_app(ineq); else return m.mk_false(); } ptr_buffer lits; for (unsigned i = 0; i < c.m_num_lits; i++) { literal l = c.m_lits[i]; if (sign(l)) lits.push_back(m.mk_not(m_bvar2expr.get(lit2bvar(l)))); else lits.push_back(m_bvar2expr.get(lit2bvar(l))); } if (ineq) lits.push_back(ineq); if (lits.size() == 1) return to_app(lits[0]); else return m.mk_or(lits.size(), lits.c_ptr()); } var mk_var(expr * t) { SASSERT(is_uninterp_const(t)); SASSERT(m_util.is_int(t) || m_util.is_real(t)); var x = m_var2expr.size(); m_var2expr.push_back(t); bool is_int = m_util.is_int(t); m_is_int.push_back(is_int); m_var2pos.push_back(UINT_MAX); m_expr2var.insert(t, x); m_lowers.push_back(constraints()); m_uppers.push_back(constraints()); bool forbidden = m_forbidden_set.contains(to_app(t)->get_decl()) || (m_fm_real_only && is_int); m_forbidden.push_back(forbidden); SASSERT(m_var2expr.size() == m_is_int.size()); SASSERT(m_lowers.size() == m_is_int.size()); SASSERT(m_uppers.size() == m_is_int.size()); SASSERT(m_forbidden.size() == m_is_int.size()); SASSERT(m_var2pos.size() == m_is_int.size()); return x; } bvar mk_bvar(expr * t) { SASSERT(is_uninterp_const(t)); SASSERT(m.is_bool(t)); bvar p = m_bvar2expr.size(); m_bvar2expr.push_back(t); m_bvar2sign.push_back(0); SASSERT(m_bvar2expr.size() == m_bvar2sign.size()); m_expr2bvar.insert(t, p); SASSERT(p > 0); return p; } var to_var(expr * t) { var x; if (!m_expr2var.find(t, x)) x = mk_var(t); SASSERT(m_expr2var.contains(t)); SASSERT(m_var2expr.get(x) == t); TRACE("to_var_bug", tout << mk_ismt2_pp(t, m) << " --> " << x << "\n";); return x; } bvar to_bvar(expr * t) { bvar p; if (m_expr2bvar.find(t, p)) return p; return mk_bvar(t); } literal to_literal(expr * t) { if (m.is_not(t, t)) return -to_bvar(t); else return to_bvar(t); } void add_constraint(expr * f, expr_dependency * dep) { SASSERT(!m.is_or(f) || m_fm_occ); sbuffer lits; sbuffer xs; buffer as; rational c; bool strict = false; unsigned num; expr * const * args; if (m.is_or(f)) { num = to_app(f)->get_num_args(); args = to_app(f)->get_args(); } else { num = 1; args = &f; } #if Z3DEBUG bool found_ineq = false; #endif for (unsigned i = 0; i < num; i++) { expr * l = args[i]; if (is_literal(l)) { lits.push_back(to_literal(l)); } else { // found inequality SASSERT(!found_ineq); DEBUG_CODE(found_ineq = true;); bool neg = m.is_not(l, l); SASSERT(m_util.is_le(l) || m_util.is_ge(l)); strict = neg; if (m_util.is_ge(l)) neg = !neg; expr * lhs = to_app(l)->get_arg(0); expr * rhs = to_app(l)->get_arg(1); m_util.is_numeral(rhs, c); if (neg) c.neg(); unsigned num_mons; expr * const * mons; if (m_util.is_add(lhs)) { num_mons = to_app(lhs)->get_num_args(); mons = to_app(lhs)->get_args(); } else { num_mons = 1; mons = &lhs; } bool all_int = true; for (unsigned j = 0; j < num_mons; j++) { expr * monomial = mons[j]; expr * a; rational a_val; expr * x; if (m_util.is_mul(monomial, a, x)) { VERIFY(m_util.is_numeral(a, a_val)); } else { x = monomial; a_val = rational(1); } if (neg) a_val.neg(); VERIFY(is_var(x, x)); xs.push_back(to_var(x)); as.push_back(a_val); if (!is_int(xs.back())) all_int = false; } mk_int(as.size(), as.c_ptr(), c); if (all_int && strict) { strict = false; c--; } } } TRACE("to_var_bug", tout << "before mk_constraint: "; for (unsigned i = 0; i < xs.size(); i++) tout << " " << xs[i]; tout << "\n";); constraint * new_c = mk_constraint(lits.size(), lits.c_ptr(), xs.size(), xs.c_ptr(), as.c_ptr(), c, strict, dep); TRACE("to_var_bug", tout << "add_constraint: "; display(tout, *new_c); tout << "\n";); VERIFY(register_constraint(new_c)); } bool is_false(constraint const & c) const { return c.m_num_lits == 0 && c.m_num_vars == 0 && (c.m_c.is_neg() || (c.m_strict && c.m_c.is_zero())); } bool register_constraint(constraint * c) { normalize_coeffs(*c); if (is_false(*c)) { del_constraint(c); m_inconsistent = true; TRACE("add_constraint_bug", tout << "is false "; display(tout, *c); tout << "\n";); return false; } bool r = false; for (unsigned i = 0; i < c->m_num_vars; i++) { var x = c->m_xs[i]; if (!is_forbidden(x)) { r = true; if (c->m_as[i].is_neg()) m_lowers[x].push_back(c); else m_uppers[x].push_back(c); } } if (r) { m_sub_todo.insert(*c); m_constraints.push_back(c); return true; } else { TRACE("add_constraint_bug", tout << "all variables are forbidden "; display(tout, *c); tout << "\n";); m_new_goal->assert_expr(to_expr(*c), nullptr, c->m_dep); del_constraint(c); return false; } } void init_use_list(goal const & g) { unsigned sz = g.size(); for (unsigned i = 0; i < sz; i++) { if (m_inconsistent) return; expr * f = g.form(i); if (is_occ(f)) add_constraint(f, g.dep(i)); else m_new_goal->assert_expr(f, nullptr, g.dep(i)); } } unsigned get_cost(var x) const { unsigned long long r = static_cast(m_lowers[x].size()) * static_cast(m_uppers[x].size()); if (r > UINT_MAX) return UINT_MAX; return static_cast(r); } typedef std::pair x_cost; struct x_cost_lt { char_vector const m_is_int; x_cost_lt(char_vector & is_int):m_is_int(is_int) {} bool operator()(x_cost const & p1, x_cost const & p2) const { // Integer variables with cost 0 can be eliminated even if they depend on real variables. // Cost 0 == no lower or no upper bound. if (p1.second == 0) { if (p2.second > 0) return true; return p1.first < p2.first; } if (p2.second == 0) return false; bool int1 = m_is_int[p1.first] != 0; bool int2 = m_is_int[p2.first] != 0; return (!int1 && int2) || (int1 == int2 && p1.second < p2.second); } }; void sort_candidates(var_vector & xs) { svector x_cost_vector; unsigned num = num_vars(); for (var x = 0; x < num; x++) { if (!is_forbidden(x)) { x_cost_vector.push_back(x_cost(x, get_cost(x))); } } // x_cost_lt is not a total order on variables std::stable_sort(x_cost_vector.begin(), x_cost_vector.end(), x_cost_lt(m_is_int)); TRACE("fm", svector::iterator it2 = x_cost_vector.begin(); svector::iterator end2 = x_cost_vector.end(); for (; it2 != end2; ++it2) { tout << "(" << mk_ismt2_pp(m_var2expr.get(it2->first), m) << " " << it2->second << ") "; } tout << "\n";); svector::iterator it2 = x_cost_vector.begin(); svector::iterator end2 = x_cost_vector.end(); for (; it2 != end2; ++it2) { xs.push_back(it2->first); } } void cleanup_constraints(constraints & cs) { unsigned j = 0; unsigned sz = cs.size(); for (unsigned i = 0; i < sz; i++) { constraint * c = cs[i]; if (c->m_dead) continue; cs[j] = c; j++; } cs.shrink(j); } // Set all_int = true if all variables in c are int. // Set unit_coeff = true if the coefficient of x in c is 1 or -1. // If all_int = false, then unit_coeff may not be set. void analyze(constraint const & c, var x, bool & all_int, bool & unit_coeff) const { all_int = true; unit_coeff = true; for (unsigned i = 0; i < c.m_num_vars; i++) { if (!is_int(c.m_xs[i])) { all_int = false; return; } if (c.m_xs[i] == x) { unit_coeff = (c.m_as[i].is_one() || c.m_as[i].is_minus_one()); } } } void analyze(constraints const & cs, var x, bool & all_int, bool & unit_coeff) const { all_int = true; unit_coeff = true; constraints::const_iterator it = cs.begin(); constraints::const_iterator end = cs.end(); for (; it != end; ++it) { bool curr_unit_coeff; analyze(*(*it), x, all_int, curr_unit_coeff); if (!all_int) return; if (!curr_unit_coeff) unit_coeff = false; } } // An integer variable x may be eliminated, if // 1- All variables in the constraints it occur are integer. // 2- The coefficient of x in all lower bounds (or all upper bounds) is unit. bool can_eliminate(var x) const { if (!is_int(x)) return true; bool all_int; bool l_unit, u_unit; analyze(m_lowers[x], x, all_int, l_unit); if (!all_int) return false; analyze(m_uppers[x], x, all_int, u_unit); return all_int && (l_unit || u_unit); } void copy_constraints(constraints const & s, clauses & t) { constraints::const_iterator it = s.begin(); constraints::const_iterator end = s.end(); for (; it != end; ++it) { app * c = to_expr(*(*it)); t.push_back(c); } } clauses tmp_clauses; void save_constraints(var x) { if (m_produce_models) { tmp_clauses.reset(); copy_constraints(m_lowers[x], tmp_clauses); copy_constraints(m_uppers[x], tmp_clauses); m_mc->insert(to_app(m_var2expr.get(x))->get_decl(), tmp_clauses); } } void mark_constraints_dead(constraints const & cs) { constraints::const_iterator it = cs.begin(); constraints::const_iterator end = cs.end(); for (; it != end; ++it) (*it)->m_dead = true; } void mark_constraints_dead(var x) { save_constraints(x); mark_constraints_dead(m_lowers[x]); mark_constraints_dead(m_uppers[x]); } void get_coeff(constraint const & c, var x, rational & a) { for (unsigned i = 0; i < c.m_num_vars; i++) { if (c.m_xs[i] == x) { a = c.m_as[i]; return; } } UNREACHABLE(); } var_vector new_xs; vector new_as; svector new_lits; constraint * resolve(constraint const & l, constraint const & u, var x) { m_counter += l.m_num_vars + u.m_num_vars + l.m_num_lits + u.m_num_lits; rational a, b; get_coeff(l, x, a); get_coeff(u, x, b); SASSERT(a.is_neg()); SASSERT(b.is_pos()); a.neg(); SASSERT(!is_int(x) || a.is_one() || b.is_one()); new_xs.reset(); new_as.reset(); rational new_c = l.m_c*b + u.m_c*a; bool new_strict = l.m_strict || u.m_strict; for (unsigned i = 0; i < l.m_num_vars; i++) { var xi = l.m_xs[i]; if (xi == x) continue; unsigned pos = new_xs.size(); new_xs.push_back(xi); SASSERT(m_var2pos[xi] == UINT_MAX); m_var2pos[xi] = pos; new_as.push_back(l.m_as[i] * b); SASSERT(new_xs[m_var2pos[xi]] == xi); SASSERT(new_xs.size() == new_as.size()); } for (unsigned i = 0; i < u.m_num_vars; i++) { var xi = u.m_xs[i]; if (xi == x) continue; unsigned pos = m_var2pos[xi]; if (pos == UINT_MAX) { new_xs.push_back(xi); new_as.push_back(u.m_as[i] * a); } else { new_as[pos] += u.m_as[i] * a; } } // remove zeros and check whether all variables are int bool all_int = true; unsigned sz = new_xs.size(); unsigned j = 0; for (unsigned i = 0; i < sz; i++) { if (new_as[i].is_zero()) continue; if (!is_int(new_xs[i])) all_int = false; if (i != j) { new_xs[j] = new_xs[i]; new_as[j] = new_as[i]; } j++; } new_xs.shrink(j); new_as.shrink(j); if (all_int && new_strict) { new_strict = false; new_c --; } // reset m_var2pos for (unsigned i = 0; i < l.m_num_vars; i++) { m_var2pos[l.m_xs[i]] = UINT_MAX; } if (new_xs.empty() && (new_c.is_pos() || (!new_strict && new_c.is_zero()))) { // literal is true TRACE("fm", tout << "resolution " << x << " consequent literal is always true: \n"; display(tout, l); tout << "\n"; display(tout, u); tout << "\n";); return nullptr; // no constraint needs to be created. } new_lits.reset(); for (unsigned i = 0; i < l.m_num_lits; i++) { literal lit = l.m_lits[i]; bvar p = lit2bvar(lit); m_bvar2sign[p] = sign(lit) ? -1 : 1; new_lits.push_back(lit); } bool tautology = false; for (unsigned i = 0; i < u.m_num_lits && !tautology; i++) { literal lit = u.m_lits[i]; bvar p = lit2bvar(lit); switch (m_bvar2sign[p]) { case 0: new_lits.push_back(lit); break; case -1: if (!sign(lit)) tautology = true; break; case 1: if (sign(lit)) tautology = true; break; default: UNREACHABLE(); } } // reset m_bvar2sign for (unsigned i = 0; i < l.m_num_lits; i++) { literal lit = l.m_lits[i]; bvar p = lit2bvar(lit); m_bvar2sign[p] = 0; } if (tautology) { TRACE("fm", tout << "resolution " << x << " tautology: \n"; display(tout, l); tout << "\n"; display(tout, u); tout << "\n";); return nullptr; } expr_dependency * new_dep = m.mk_join(l.m_dep, u.m_dep); if (new_lits.empty() && new_xs.empty() && (new_c.is_neg() || (new_strict && new_c.is_zero()))) { TRACE("fm", tout << "resolution " << x << " inconsistent: \n"; display(tout, l); tout << "\n"; display(tout, u); tout << "\n";); m_inconsistent = true; m_inconsistent_core = new_dep; return nullptr; } constraint * new_cnstr = mk_constraint(new_lits.size(), new_lits.c_ptr(), new_xs.size(), new_xs.c_ptr(), new_as.c_ptr(), new_c, new_strict, new_dep); TRACE("fm", tout << "resolution " << x << "\n"; display(tout, l); tout << "\n"; display(tout, u); tout << "\n---->\n"; display(tout, *new_cnstr); tout << "\n"; tout << "new_dep: " << new_dep << "\n";); return new_cnstr; } ptr_vector new_constraints; bool try_eliminate(var x) { constraints & l = m_lowers[x]; constraints & u = m_uppers[x]; cleanup_constraints(l); cleanup_constraints(u); if (l.empty() || u.empty()) { // easy case mark_constraints_dead(x); TRACE("fm", tout << "variables was eliminated (trivial case)\n";); return true; } unsigned num_lowers = l.size(); unsigned num_uppers = u.size(); if (num_lowers > m_fm_cutoff1 && num_uppers > m_fm_cutoff1) return false; if (num_lowers * num_uppers > m_fm_cutoff2) return false; if (!can_eliminate(x)) return false; m_counter += num_lowers * num_uppers; TRACE("fm_bug", tout << "eliminating " << mk_ismt2_pp(m_var2expr.get(x), m) << "\nlowers:\n"; display_constraints(tout, l); tout << "uppers:\n"; display_constraints(tout, u);); unsigned num_old_cnstrs = num_uppers + num_lowers; unsigned limit = num_old_cnstrs + m_fm_extra; unsigned num_new_cnstrs = 0; new_constraints.reset(); for (unsigned i = 0; i < num_lowers; i++) { for (unsigned j = 0; j < num_uppers; j++) { if (m_inconsistent || num_new_cnstrs > limit) { TRACE("fm", tout << "too many new constraints: " << num_new_cnstrs << "\n";); del_constraints(new_constraints.size(), new_constraints.c_ptr()); return false; } constraint const & l_c = *(l[i]); constraint const & u_c = *(u[j]); constraint * new_c = resolve(l_c, u_c, x); if (new_c != nullptr) { num_new_cnstrs++; new_constraints.push_back(new_c); } } } mark_constraints_dead(x); unsigned sz = new_constraints.size(); m_counter += sz; for (unsigned i = 0; i < sz; i++) { constraint * c = new_constraints[i]; backward_subsumption(*c); register_constraint(c); } TRACE("fm", tout << "variables was eliminated old: " << num_old_cnstrs << " new_constraints: " << sz << "\n";); return true; } void copy_remaining(vector & v2cs) { vector::iterator it = v2cs.begin(); vector::iterator end = v2cs.end(); for (; it != end; ++it) { constraints & cs = *it; constraints::iterator it2 = cs.begin(); constraints::iterator end2 = cs.end(); for (; it2 != end2; ++it2) { constraint * c = *it2; if (!c->m_dead) { c->m_dead = true; expr * new_f = to_expr(*c); TRACE("fm_bug", tout << "asserting...\n" << mk_ismt2_pp(new_f, m) << "\nnew_dep: " << c->m_dep << "\n";); m_new_goal->assert_expr(new_f, nullptr, c->m_dep); } } } v2cs.finalize(); } // Copy remaining clauses to m_new_goal void copy_remaining() { copy_remaining(m_uppers); copy_remaining(m_lowers); } void checkpoint() { if (m.canceled()) throw tactic_exception(m.limit().get_cancel_msg()); if (memory::get_allocation_size() > m_max_memory) throw tactic_exception(TACTIC_MAX_MEMORY_MSG); } void operator()(goal_ref const & g, goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); tactic_report report("fm", *g); fail_if_proof_generation("fm", g); m_produce_models = g->models_enabled(); init(*g); m_new_goal = alloc(goal, *g, true); SASSERT(m_new_goal->depth() == g->depth()); SASSERT(m_new_goal->prec() == g->prec()); m_new_goal->inc_depth(); init_use_list(*g); if (m_inconsistent) { m_new_goal->reset(); m_new_goal->assert_expr(m.mk_false(), nullptr, m_inconsistent_core); } else { TRACE("fm", display(tout);); subsume(); var_vector candidates; sort_candidates(candidates); unsigned eliminated = 0; if (m_produce_models) m_mc = alloc(fm_model_converter, m); unsigned num = candidates.size(); for (unsigned i = 0; i < num; i++) { checkpoint(); if (m_counter > m_fm_limit) break; m_counter++; if (try_eliminate(candidates[i])) eliminated++; if (m_inconsistent) { m_new_goal->reset(); m_new_goal->assert_expr(m.mk_false(), nullptr, m_inconsistent_core); break; } } report_tactic_progress(":fm-eliminated", eliminated); report_tactic_progress(":fm-cost", m_counter); if (!m_inconsistent) { copy_remaining(); m_new_goal->add(concat(g->mc(), m_mc.get())); } } reset_constraints(); result.push_back(m_new_goal.get()); TRACE("fm", m_new_goal->display(tout);); SASSERT(m_new_goal->is_well_sorted()); } void display_constraints(std::ostream & out, constraints const & cs) const { constraints::const_iterator it = cs.begin(); constraints::const_iterator end = cs.end(); for (; it != end; ++it) { out << " "; display(out, *(*it)); out << "\n"; } } void display(std::ostream & out) const { unsigned num = num_vars(); for (var x = 0; x < num; x++) { if (is_forbidden(x)) continue; out << mk_ismt2_pp(m_var2expr.get(x), m) << "\n"; display_constraints(out, m_lowers[x]); display_constraints(out, m_uppers[x]); } } }; imp * m_imp; params_ref m_params; public: fm_tactic(ast_manager & m, params_ref const & p): m_params(p) { m_imp = alloc(imp, m, p); } tactic * translate(ast_manager & m) override { return alloc(fm_tactic, m, m_params); } ~fm_tactic() override { dealloc(m_imp); } void updt_params(params_ref const & p) override { m_params = p; m_imp->updt_params(p); } void collect_param_descrs(param_descrs & r) override { insert_produce_models(r); insert_max_memory(r); r.insert("fm_real_only", CPK_BOOL, "(default: true) consider only real variables for fourier-motzkin elimination."); r.insert("fm_occ", CPK_BOOL, "(default: false) consider inequalities occurring in clauses for FM."); r.insert("fm_limit", CPK_UINT, "(default: 5000000) maximum number of constraints, monomials, clauses visited during FM."); r.insert("fm_cutoff1", CPK_UINT, "(default: 8) first cutoff for FM based on maximum number of lower/upper occurrences."); r.insert("fm_cutoff2", CPK_UINT, "(default: 256) second cutoff for FM based on num_lower * num_upper occurrences."); r.insert("fm_extra", CPK_UINT, "(default: 0) max. increase on the number of inequalities for each FM variable elimination step."); } void cleanup() override { imp * d = alloc(imp, m_imp->m, m_params); std::swap(d, m_imp); dealloc(d); } void operator()(goal_ref const & in, goal_ref_buffer & result) override { (*m_imp)(in, result); } }; tactic * mk_fm_tactic(ast_manager & m, params_ref const & p) { params_ref s_p = p; s_p.set_bool("arith_lhs", true); s_p.set_bool("elim_and", true); s_p.set_bool("som", true); return and_then(using_params(mk_simplify_tactic(m, s_p), s_p), clean(alloc(fm_tactic, m, p))); } z3-z3-4.8.7/src/tactic/arith/fm_tactic.h000066400000000000000000000012551356505360400177010ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: fm_tactic.h Abstract: Use Fourier-Motzkin to eliminate variables. This strategy can handle conditional bounds (i.e., clauses with at most one constraint). The strategy mk_occf can be used to put the formula in OCC form. Author: Leonardo de Moura (leonardo) 2012-02-04. Revision History: --*/ #ifndef FM_TACTIC_H_ #define FM_TACTIC_H_ #include "util/params.h" class ast_manager; class tactic; tactic * mk_fm_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("fm", "eliminate variables using fourier-motzkin elimination.", "mk_fm_tactic(m, p)") */ #endif z3-z3-4.8.7/src/tactic/arith/lia2card_tactic.cpp000066400000000000000000000315011356505360400213100ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: lia2card_tactic.cpp Abstract: Convert 0-1 integer variables cardinality constraints to built-in cardinality operator. Author: Nikolaj Bjorner (nbjorner) 2013-11-5 Notes: --*/ #include "ast/ast_pp.h" #include "ast/pb_decl_plugin.h" #include "ast/arith_decl_plugin.h" #include "ast/rewriter/rewriter_def.h" #include "ast/rewriter/expr_safe_replace.h" #include "ast/ast_util.h" #include "ast/ast_pp_util.h" #include "tactic/tactical.h" #include "tactic/arith/bound_manager.h" #include "tactic/generic_model_converter.h" class lia2card_tactic : public tactic { struct bound { unsigned m_lo; unsigned m_hi; expr* m_expr; bound(unsigned lo, unsigned hi, expr* b): m_lo(lo), m_hi(hi), m_expr(b) {} bound(): m_lo(0), m_hi(0), m_expr(nullptr) {} }; struct lia_rewriter_cfg : public default_rewriter_cfg { ast_manager& m; lia2card_tactic& t; arith_util a; expr_ref_vector args; vector coeffs; rational coeff; bool is_pb(expr* x, expr* y, expr_ref_vector& args, vector& coeffs, rational& coeff) { args.reset(); coeffs.reset(); coeff.reset(); return t.get_pb_sum(x, rational::one(), args, coeffs, coeff) && t.get_pb_sum(y, -rational::one(), args, coeffs, coeff); } bool is_le(expr* x, expr* y, expr_ref& result) { if (is_pb(x, y, args, coeffs, coeff)) { result = t.mk_le(coeffs.size(), coeffs.c_ptr(), args.c_ptr(), -coeff); return true; } else { return false; } } br_status mk_app_core(func_decl* f, unsigned sz, expr*const* es, expr_ref& result) { if (is_decl_of(f, a.get_family_id(), OP_LE) && is_le(es[0], es[1], result)) { } else if (is_decl_of(f, a.get_family_id(), OP_GE) && is_le(es[1], es[0], result)) { } else if (is_decl_of(f, a.get_family_id(), OP_LT) && is_le(es[1], es[0], result)) { result = m.mk_not(result); } else if (is_decl_of(f, a.get_family_id(), OP_GT) && is_le(es[0], es[1], result)) { result = m.mk_not(result); } else if (m.is_eq(f) && is_pb(es[0], es[1], args, coeffs, coeff)) { result = t.mk_eq(coeffs.size(), coeffs.c_ptr(), args.c_ptr(), -coeff); } else { return BR_FAILED; } TRACE("pbsum", tout << expr_ref(m.mk_app(f, sz, es), m) << " ==>\n" << result << "\n";); return BR_DONE; } bool rewrite_patterns() const { return false; } bool flat_assoc(func_decl * f) 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; return mk_app_core(f, num, args, result); } lia_rewriter_cfg(lia2card_tactic& t):m(t.m), t(t), a(m), args(m) {} }; class lia_rewriter : public rewriter_tpl { lia_rewriter_cfg m_cfg; public: lia_rewriter(lia2card_tactic& t): rewriter_tpl(t.m, false, m_cfg), m_cfg(t) {} }; public: typedef obj_map bounds_map; ast_manager & m; arith_util a; lia_rewriter m_rw; params_ref m_params; pb_util m_pb; mutable ptr_vector* m_todo; bounds_map m_bounds; bool m_compile_equality; unsigned m_max_ub; ref m_mc; lia2card_tactic(ast_manager & _m, params_ref const & p): m(_m), a(m), m_rw(*this), m_pb(m), m_todo(alloc(ptr_vector)), m_compile_equality(true) { m_max_ub = 100; } ~lia2card_tactic() override { dealloc(m_todo); } void updt_params(params_ref const & p) override { m_params = p; m_compile_equality = p.get_bool("compile_equality", true); } expr_ref mk_bounded(expr_ref_vector& axioms, app* x, unsigned lo, unsigned hi) { expr_ref_vector xs(m); expr_ref last_v(m); if (!m_mc) m_mc = alloc(generic_model_converter, m, "lia2card"); if (hi == 0) { expr* r = a.mk_int(0); m_mc->add(x->get_decl(), r); return expr_ref(r, m); } if (lo > 0) { xs.push_back(a.mk_int(lo)); } for (unsigned i = lo; i < hi; ++i) { checkpoint(); expr_ref v(m.mk_fresh_const(x->get_decl()->get_name(), m.mk_bool_sort()), m); if (last_v) axioms.push_back(m.mk_implies(v, last_v)); xs.push_back(m.mk_ite(v, a.mk_int(1), a.mk_int(0))); m_mc->hide(v); last_v = v; } expr* r = a.mk_add(xs.size(), xs.c_ptr()); m_mc->add(x->get_decl(), r); return expr_ref(r, m); } void checkpoint() { if (m.canceled()) { throw tactic_exception(m.limit().get_cancel_msg()); } } void operator()(goal_ref const & g, goal_ref_buffer & result) override { SASSERT(g->is_well_sorted()); m_bounds.reset(); m_mc.reset(); expr_ref_vector axioms(m); expr_safe_replace rep(m); TRACE("pb", g->display(tout);); tactic_report report("lia2card", *g); bound_manager bounds(m); bounds(*g); for (expr* x : bounds) { checkpoint(); bool s1 = false, s2 = false; rational lo, hi; if (a.is_int(x) && is_uninterp_const(x) && bounds.has_lower(x, lo, s1) && !s1 && lo.is_unsigned() && bounds.has_upper(x, hi, s2) && !s2 && hi.is_unsigned() && hi.get_unsigned() - lo.get_unsigned() <= m_max_ub) { expr_ref b = mk_bounded(axioms, to_app(x), lo.get_unsigned(), hi.get_unsigned()); rep.insert(x, b); m_bounds.insert(x, bound(lo.get_unsigned(), hi.get_unsigned(), b)); TRACE("pb", tout << "add bound " << mk_pp(x, m) << "\n";); } } for (unsigned i = 0; i < g->size(); i++) { checkpoint(); expr_ref new_curr(m), tmp(m); proof_ref new_pr(m); rep(g->form(i), tmp); m_rw(tmp, new_curr, new_pr); if (m.proofs_enabled() && !new_pr) { new_pr = m.mk_rewrite(g->form(i), new_curr); new_pr = m.mk_modus_ponens(g->pr(i), new_pr); } // IF_VERBOSE(0, verbose_stream() << mk_pp(g->form(i), m) << "\n--->\n" << new_curr << "\n";); g->update(i, new_curr, new_pr, g->dep(i)); } for (expr* a : axioms) { g->assert_expr(a); } if (m_mc) g->add(m_mc.get()); g->inc_depth(); result.push_back(g.get()); TRACE("pb", g->display(tout);); SASSERT(g->is_well_sorted()); m_bounds.reset(); } expr* mk_le(unsigned sz, rational const* weights, expr* const* args, rational const& w) { if (sz == 0) { return w.is_neg()?m.mk_false():m.mk_true(); } if (sz == 1 && weights[0].is_one() && w >= rational::one()) { return m.mk_true(); } if (sz == 1 && weights[0].is_one() && w.is_zero()) { return m.mk_not(args[0]); } if (w.is_neg()) { DEBUG_CODE(for (unsigned i = 0; i < sz; ++i) SASSERT(weights[i].is_nonneg()); ); return m.mk_false(); } return m_pb.mk_le(sz, weights, args, w); } expr* mk_eq(unsigned sz, rational const* weights, expr* const* args, rational const& w) { if (w.is_neg()) { DEBUG_CODE(for (unsigned i = 0; i < sz; ++i) SASSERT(weights[i].is_nonneg()); ); return m.mk_false(); } if (m_compile_equality) { return m_pb.mk_eq(sz, weights, args, w); } else { return m.mk_and(mk_ge(sz, weights, args, w), mk_le(sz, weights, args, w)); } } expr* mk_ge(unsigned sz, rational const* weights, expr* const* args, rational const& w) { if (sz == 0) { return w.is_pos()?m.mk_false():m.mk_true(); } if (sz == 1 && weights[0].is_one() && w.is_one()) { return args[0]; } if (sz == 1 && weights[0].is_one() && w.is_zero()) { return m.mk_not(args[0]); } if (w.is_neg()) { DEBUG_CODE(for (unsigned i = 0; i < sz; ++i) SASSERT(weights[i].is_nonneg()); ); return m.mk_true(); } return m_pb.mk_ge(sz, weights, args, w); } bool get_pb_sum(expr* x, rational const& mul, expr_ref_vector& args, vector& coeffs, rational& coeff) { expr_ref_vector conds(m); return get_sum(x, mul, conds, args, coeffs, coeff); } bool get_sum(expr* x, rational const& mul, expr_ref_vector& conds, expr_ref_vector& args, vector& coeffs, rational& coeff) { checkpoint(); expr *y, *z, *u; rational r, q; if (!is_app(x)) return false; app* f = to_app(x); bool ok = true; if (a.is_add(x)) { for (unsigned i = 0; ok && i < f->get_num_args(); ++i) { ok = get_sum(f->get_arg(i), mul, conds, args, coeffs, coeff); } } else if (a.is_sub(x, y, z)) { ok = get_sum(y, mul, conds, args, coeffs, coeff); ok = ok && get_sum(z, -mul, conds, args, coeffs, coeff); } else if (a.is_uminus(x, y)) { ok = get_sum(y, -mul, conds, args, coeffs, coeff); } else if (a.is_mul(x, y, z) && is_numeral(y, r)) { ok = get_sum(z, r*mul, conds, args, coeffs, coeff); } else if (a.is_mul(x, z, y) && is_numeral(y, r)) { ok = get_sum(z, r*mul, conds, args, coeffs, coeff); } else if (a.is_to_real(x, y)) { ok = get_sum(y, mul, conds, args, coeffs, coeff); } else if (m.is_ite(x, y, z, u)) { conds.push_back(y); ok = get_sum(z, mul, conds, args, coeffs, coeff); conds.pop_back(); conds.push_back(m.mk_not(y)); ok &= get_sum(u, mul, conds, args, coeffs, coeff); conds.pop_back(); } else if (is_numeral(x, r)) { insert_arg(mul*r, conds, m.mk_true(), args, coeffs, coeff); } else { TRACE("pb", tout << "Can't handle " << mk_pp(x, m) << "\n";); ok = false; } return ok; } expr_ref add_conds(expr_ref_vector& es, expr* e) { expr_ref result(m); if (!m.is_true(e)) { es.push_back(e); } result = mk_and(m, es.size(), es.c_ptr()); if (!m.is_true(e)) { es.pop_back(); } return result; } bool is_numeral(expr* e, rational& r) { if (a.is_uminus(e, e) && is_numeral(e, r)) { r.neg(); return true; } if (a.is_to_real(e, e)) { return is_numeral(e, r); } return a.is_numeral(e, r); } void insert_arg( rational const& p, expr_ref_vector& conds, expr* x, expr_ref_vector& args, vector& coeffs, rational& coeff) { expr_ref cond = add_conds(conds, x); if (m.is_true(cond)) { coeff += p; } else if (p.is_neg()) { // -p*x = p*(1-x) - p args.push_back(m.mk_not(cond)); coeffs.push_back(-p); coeff += p; } else if (p.is_pos()) { args.push_back(cond); coeffs.push_back(p); } } tactic * translate(ast_manager & m) override { return alloc(lia2card_tactic, m, m_params); } void collect_param_descrs(param_descrs & r) override { r.insert("compile_equality", CPK_BOOL, "(default:false) compile equalities into pseudo-Boolean equality"); } void cleanup() override { ptr_vector* todo = alloc(ptr_vector); std::swap(m_todo, todo); dealloc(todo); m_bounds.reset(); } }; tactic * mk_lia2card_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(lia2card_tactic, m, p)); } bool get_pb_sum(expr* term, expr_ref_vector& args, vector& coeffs, rational& coeff) { params_ref p; ast_manager& m = args.get_manager(); lia2card_tactic tac(m, p); return tac.get_pb_sum(term, rational::one(), args, coeffs, coeff); } z3-z3-4.8.7/src/tactic/arith/lia2card_tactic.h000066400000000000000000000012471356505360400207610ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: lia2card_tactic.h Abstract: Extract 0-1 integer variables used in cardinality constraints and replace them by Booleans. Author: Nikolaj Bjorner (nbjorner) 2013-11-5 Notes: --*/ #ifndef LIA2CARD_TACTIC_H_ #define LIA2CARD_TACTIC_H_ #include "util/params.h" class ast_manager; class tactic; tactic * mk_lia2card_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("lia2card", "introduce cardinality constraints from 0-1 integer.", "mk_lia2card_tactic(m, p)") */ bool get_pb_sum(expr* term, expr_ref_vector& args, vector& coeffs, rational& coeff); #endif z3-z3-4.8.7/src/tactic/arith/lia2pb_tactic.cpp000066400000000000000000000265201356505360400210050ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: lia2pb_tactic.cpp Abstract: Reduce bounded LIA benchmark into 0-1 LIA benchmark. Author: Leonardo de Moura (leonardo) 2012-02-07. Revision History: --*/ #include "tactic/tactical.h" #include "tactic/arith/bound_manager.h" #include "ast/rewriter/th_rewriter.h" #include "ast/for_each_expr.h" #include "tactic/generic_model_converter.h" #include "ast/arith_decl_plugin.h" #include "ast/expr_substitution.h" #include "ast/ast_smt2_pp.h" class lia2pb_tactic : public tactic { struct imp { ast_manager & m; bound_manager m_bm; arith_util m_util; expr_dependency_ref_vector m_new_deps; th_rewriter m_rw; bool m_produce_models; bool m_produce_unsat_cores; bool m_partial_lia2pb; unsigned m_max_bits; unsigned m_total_bits; imp(ast_manager & _m, params_ref const & p): m(_m), m_bm(m), m_util(m), m_new_deps(m), m_rw(m, p) { updt_params(p); } void updt_params_core(params_ref const & p) { m_partial_lia2pb = p.get_bool("lia2pb_partial", false); m_max_bits = p.get_uint("lia2pb_max_bits", 32); m_total_bits = p.get_uint("lia2pb_total_bits", 2048); } void updt_params(params_ref const & p) { m_rw.updt_params(p); updt_params_core(p); } bool is_target_core(expr * n, rational & u) { if (!is_uninterp_const(n)) return false; rational l; bool s; if (m_bm.has_lower(n, l, s) && m_bm.has_upper(n, u, s) && l.is_zero() && !u.is_neg() && u.get_num_bits() <= m_max_bits) { return true; } return false; } bool is_bounded(expr * n) { rational u; return is_target_core(n, u); } bool is_target(expr * n) { rational u; return is_target_core(n, u) && u > rational(1); } struct failed {}; struct visitor { imp & m_owner; visitor(imp & o):m_owner(o) {} void throw_failed(expr * n) { TRACE("lia2pb", tout << "Failed at:\n" << mk_ismt2_pp(n, m_owner.m) << "\n";); throw failed(); } void operator()(var * n) { throw_failed(n); } void operator()(app * n) { family_id fid = n->get_family_id(); if (fid == m_owner.m.get_basic_family_id()) { // all basic family ops are OK } else if (fid == m_owner.m_util.get_family_id()) { // check if linear switch (n->get_decl_kind()) { case OP_LE: case OP_GE: case OP_LT: case OP_GT: case OP_ADD: case OP_NUM: return; case OP_MUL: if (n->get_num_args() != 2) throw_failed(n); if (!m_owner.m_util.is_numeral(n->get_arg(0))) throw_failed(n); return; default: throw_failed(n); } } else if (is_uninterp_const(n)) { if (m_owner.m_util.is_real(n)) { if (!m_owner.m_partial_lia2pb) throw_failed(n); } else if (m_owner.m_util.is_int(n)) { if (!m_owner.m_partial_lia2pb && !m_owner.is_bounded(n)) throw_failed(n); } } else { sort * s = m_owner.m.get_sort(n); if (s->get_family_id() == m_owner.m_util.get_family_id()) throw_failed(n); } } void operator()(quantifier * n) { throw_failed(n); } }; bool check(goal const & g) { try { expr_fast_mark1 visited; visitor proc(*this); unsigned sz = g.size(); for (unsigned i = 0; i < sz; i++) { expr * f = g.form(i); for_each_expr_core(proc, visited, f); } return true; } catch (const failed &) { return false; } } bool has_target() { for (expr * x : m_bm) { if (is_target(x)) return true; } return false; } bool check_num_bits() { unsigned num_bits = 0; rational u; for (expr * x : m_bm) { if (is_target_core(x, u) && u > rational(1)) { num_bits += u.get_num_bits(); if (num_bits > m_total_bits) return false; } } return true; } void operator()(goal_ref const & g, goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); fail_if_proof_generation("lia2pb", g); m_produce_models = g->models_enabled(); m_produce_unsat_cores = g->unsat_core_enabled(); result.reset(); tactic_report report("lia2pb", *g); m_bm.reset(); m_rw.reset(); m_new_deps.reset(); if (g->inconsistent()) { result.push_back(g.get()); return; } m_bm(*g); TRACE("lia2pb", m_bm.display(tout);); // check if there is some variable to be converted if (!has_target()) { // nothing to be done g->inc_depth(); result.push_back(g.get()); return; } if (!check(*g)) throw tactic_exception("goal is in a fragment unsupported by lia2pb"); if (!check_num_bits()) throw tactic_exception("lia2pb failed, number of necessary bits exceeds specified threshold (use option :lia2pb-total-bits to increase threshold)"); ref gmc; if (m_produce_models) { gmc = alloc(generic_model_converter, m, "lia2pb"); } expr_ref zero(m); expr_ref one(m); zero = m_util.mk_numeral(rational(0), true); one = m_util.mk_numeral(rational(1), true); unsigned num_converted = 0; expr_substitution subst(m, m_produce_unsat_cores, false); rational u; ptr_buffer def_args; for (expr * x : m_bm) { if (is_target_core(x, u) && u > rational(1)) { num_converted++; def_args.reset(); rational a(1); unsigned num_bits = u.get_num_bits(); for (unsigned i = 0; i < num_bits; i++) { app * x_prime = m.mk_fresh_const(nullptr, m_util.mk_int()); g->assert_expr(m_util.mk_le(zero, x_prime)); g->assert_expr(m_util.mk_le(x_prime, one)); if (a.is_one()) def_args.push_back(x_prime); else def_args.push_back(m_util.mk_mul(m_util.mk_numeral(a, true), x_prime)); if (m_produce_models) gmc->hide(x_prime->get_decl()); a *= rational(2); } SASSERT(def_args.size() > 1); expr * def = m_util.mk_add(def_args.size(), def_args.c_ptr()); expr_dependency * dep = nullptr; if (m_produce_unsat_cores) { dep = m.mk_join(m_bm.lower_dep(x), m_bm.upper_dep(x)); if (dep != nullptr) m_new_deps.push_back(dep); } TRACE("lia2pb", tout << mk_ismt2_pp(x, m) << " -> " << dep << "\n";); subst.insert(x, def, nullptr, dep); if (m_produce_models) { gmc->add(x, def); } } } report_tactic_progress(":converted-lia2pb", num_converted); m_rw.set_substitution(&subst); 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); expr_dependency * dep = nullptr; m_rw(curr, new_curr, new_pr); if (m_produce_unsat_cores) { dep = m.mk_join(m_rw.get_used_dependencies(), g->dep(idx)); m_rw.reset_used_dependencies(); } if (m.proofs_enabled()) { new_pr = m.mk_modus_ponens(g->pr(idx), new_pr); } g->update(idx, new_curr, new_pr, dep); } g->inc_depth(); g->add(gmc.get()); result.push_back(g.get()); TRACE("lia2pb", g->display(tout);); SASSERT(g->is_well_sorted()); } }; imp * m_imp; params_ref m_params; public: lia2pb_tactic(ast_manager & m, params_ref const & p): m_params(p) { m_imp = alloc(imp, m, p); } tactic * translate(ast_manager & m) override { return alloc(lia2pb_tactic, m, m_params); } ~lia2pb_tactic() override { dealloc(m_imp); } void updt_params(params_ref const & p) override { m_params = p; m_imp->updt_params(p); } void collect_param_descrs(param_descrs & r) override { r.insert("lia2pb_partial", CPK_BOOL, "(default: false) partial lia2pb conversion."); r.insert("lia2pb_max_bits", CPK_UINT, "(default: 32) maximum number of bits to be used (per variable) in lia2pb."); r.insert("lia2pb_total_bits", CPK_UINT, "(default: 2048) total number of bits to be used (per problem) in lia2pb."); } void operator()(goal_ref const & in, goal_ref_buffer & result) override { try { (*m_imp)(in, result); } catch (rewriter_exception & ex) { throw tactic_exception(ex.msg()); } } void cleanup() override { imp * d = alloc(imp, m_imp->m, m_params); std::swap(d, m_imp); dealloc(d); } }; tactic * mk_lia2pb_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(lia2pb_tactic, m, p)); } z3-z3-4.8.7/src/tactic/arith/lia2pb_tactic.h000066400000000000000000000010531356505360400204440ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: lia2pb_tactic.h Abstract: Reduce bounded LIA benchmark into 0-1 LIA benchmark. Author: Leonardo de Moura (leonardo) 2012-02-07. Revision History: --*/ #ifndef LIA2PB_TACTIC_H_ #define LIA2PB_TACTIC_H_ #include "util/params.h" class ast_manager; class tactic; tactic * mk_lia2pb_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("lia2pb", "convert bounded integer variables into a sequence of 0-1 variables.", "mk_lia2pb_tactic(m, p)") */ #endif z3-z3-4.8.7/src/tactic/arith/linear_equation.cpp000066400000000000000000000177471356505360400214770ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: linear_equation.cpp Abstract: Basic infrastructure for managing linear equations of the form: a_1 * x_1 + ... + a_n * x_n = 0 Author: Leonardo de Moura (leonardo) 2011-06-28 Revision History: --*/ #include "tactic/arith/linear_equation.h" /** \brief Return the position of variable x_i in the linear equation. Return UINT_MAX, if the variable is not in the linear_equation. */ unsigned linear_equation::pos(unsigned x_i) const { int low = 0; int high = m_size - 1; while (true) { int mid = low + ((high - low) / 2); var x_mid = m_xs[mid]; if (x_i > x_mid) { low = mid + 1; if (low > high) return UINT_MAX; } else if (x_i < x_mid) { high = mid - 1; if (low > high) return UINT_MAX; } else { return mid; } } } void linear_equation_manager::display(std::ostream & out, linear_equation const & eq) const { unsigned sz = eq.m_size; for (unsigned i = 0; i < sz; i++) { if (i > 0) out << " + "; out << m.to_string(eq.m_as[i]) << "*x" << eq.m_xs[i]; } out << " = 0"; } linear_equation * linear_equation_manager::mk(unsigned sz, mpq * as, var * xs, bool normalized) { SASSERT(sz > 1); // compute lcm of the denominators mpz l; mpz r; m.set(l, as[0].denominator()); for (unsigned i = 1; i < sz; i++) { m.set(r, as[i].denominator()); m.lcm(r, l, l); } TRACE("linear_equation_mk", tout << "lcm: " << m.to_string(l) << "\n";); // copy l * as to m_int_buffer. m_int_buffer.reset(); for (unsigned i = 0; i < sz; i++) { TRACE("linear_equation_mk", tout << "before as[" << i << "]: " << m.to_string(as[i]) << "\n";); m.mul(l, as[i], as[i]); TRACE("linear_equation_mk", tout << "after as[" << i << "]: " << m.to_string(as[i]) << "\n";); SASSERT(m.is_int(as[i])); m_int_buffer.push_back(as[i].numerator()); } linear_equation * new_eq = mk(sz, m_int_buffer.c_ptr(), xs, normalized); m.del(r); m.del(l); return new_eq; } linear_equation * linear_equation_manager::mk_core(unsigned sz, mpz * as, var * xs) { SASSERT(sz > 0); DEBUG_CODE({ for (unsigned i = 1; i < sz; i++) { SASSERT(xs[i-1] < xs[i]); } }); TRACE("linear_equation_bug", for (unsigned i = 0; i < sz; i++) tout << m.to_string(as[i]) << "*x" << xs[i] << " "; tout << "\n";); mpz g; m.set(g, as[0]); for (unsigned i = 1; i < sz; i++) { if (m.is_one(g)) break; if (m.is_neg(as[i])) { m.neg(as[i]); m.gcd(g, as[i], g); m.neg(as[i]); } else { m.gcd(g, as[i], g); } } if (!m.is_one(g)) { for (unsigned i = 0; i < sz; i++) { m.div(as[i], g, as[i]); } } TRACE("linear_equation_bug", tout << "g: " << m.to_string(g) << "\n"; for (unsigned i = 0; i < sz; i++) tout << m.to_string(as[i]) << "*x" << xs[i] << " "; tout << "\n";); m.del(g); unsigned obj_sz = linear_equation::get_obj_size(sz); void * mem = m_allocator.allocate(obj_sz); linear_equation * new_eq = new (mem) linear_equation(); mpz * new_as = reinterpret_cast(reinterpret_cast(new_eq) + sizeof(linear_equation)); double * new_app_as = reinterpret_cast(reinterpret_cast(new_as) + sz * sizeof(mpz)); var * new_xs = reinterpret_cast(reinterpret_cast(new_app_as) + sz * sizeof(double)); for (unsigned i = 0; i < sz; i++) { new (new_as + i) mpz(); m.set(new_as[i], as[i]); new_app_as[i] = m.get_double(as[i]); var x_i = xs[i]; new_xs[i] = x_i; } new_eq->m_size = sz; new_eq->m_as = new_as; new_eq->m_approx_as = new_app_as; new_eq->m_xs = new_xs; return new_eq; } linear_equation * linear_equation_manager::mk(unsigned sz, mpz * as, var * xs, bool normalized) { if (!normalized) { for (unsigned i = 0; i < sz; i++) { var x = xs[i]; m_mark.reserve(x+1, false); m_val_buffer.reserve(x+1); if (m_mark[x]) { m.add(m_val_buffer[x], as[i], m_val_buffer[x]); } else { m.set(m_val_buffer[x], as[i]); m_mark[x] = true; } } unsigned j = 0; for (unsigned i = 0; i < sz; i++) { var x = xs[i]; if (m_mark[x]) { if (!m.is_zero(m_val_buffer[x])) { xs[j] = xs[i]; m.set(as[j], m_val_buffer[x]); j++; } m_mark[x] = false; } } sz = j; if (sz <= 1) return nullptr; } else { DEBUG_CODE({ for (unsigned i = 0; i < sz; i++) { var x = xs[i]; m_mark.reserve(x+1, false); SASSERT(!m_mark[x]); m_mark[x] = true; } for (unsigned i = 0; i < sz; i++) { var x = xs[i]; m_mark[x] = false; } }); } for (unsigned i = 0; i < sz; i++) { var x = xs[i]; m_val_buffer.reserve(x+1); m.swap(m_val_buffer[x], as[i]); } std::sort(xs, xs+sz); for (unsigned i = 0; i < sz; i++) { var x = xs[i]; m.swap(as[i], m_val_buffer[x]); } return mk_core(sz, as, xs); } linear_equation * linear_equation_manager::mk(mpz const & b1, linear_equation const & eq1, mpz const & b2, linear_equation const & eq2) { SASSERT(!m.is_zero(b1)); SASSERT(!m.is_zero(b2)); mpz tmp, new_a; m_int_buffer.reset(); m_var_buffer.reset(); unsigned sz1 = eq1.size(); unsigned sz2 = eq2.size(); unsigned i1 = 0; unsigned i2 = 0; while (true) { if (i1 == sz1) { // copy remaining entries from eq2 while (i2 < sz2) { m_int_buffer.push_back(eq2.a(i2)); m.mul(m_int_buffer.back(), b2, m_int_buffer.back()); m_var_buffer.push_back(eq2.x(i2)); i2++; } break; } if (i2 == sz2) { // copy remaining entries from eq1 while (i1 < sz1) { m_int_buffer.push_back(eq1.a(i1)); m.mul(m_int_buffer.back(), b1, m_int_buffer.back()); m_var_buffer.push_back(eq1.x(i1)); i1++; } break; } var x1 = eq1.x(i1); var x2 = eq2.x(i2); if (x1 < x2) { m_int_buffer.push_back(eq1.a(i1)); m.mul(m_int_buffer.back(), b1, m_int_buffer.back()); m_var_buffer.push_back(eq1.x(i1)); i1++; } else if (x1 > x2) { m_int_buffer.push_back(eq2.a(i2)); m.mul(m_int_buffer.back(), b2, m_int_buffer.back()); m_var_buffer.push_back(eq2.x(i2)); i2++; } else { m.mul(eq1.a(i1), b1, tmp); m.addmul(tmp, b2, eq2.a(i2), new_a); if (!m.is_zero(new_a)) { m_int_buffer.push_back(new_a); m_var_buffer.push_back(eq1.x(i1)); } i1++; i2++; } } m.del(tmp); m.del(new_a); SASSERT(m_int_buffer.size() == m_var_buffer.size()); if (m_int_buffer.empty()) return nullptr; return mk_core(m_int_buffer.size(), m_int_buffer.c_ptr(), m_var_buffer.c_ptr()); } void linear_equation_manager::del(linear_equation * eq) { for (unsigned i = 0; i < eq->m_size; i++) { m.del(eq->m_as[i]); } unsigned obj_sz = linear_equation::get_obj_size(eq->m_size); m_allocator.deallocate(obj_sz, eq); } z3-z3-4.8.7/src/tactic/arith/linear_equation.h000066400000000000000000000053661356505360400211360ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: linear_equation.h Abstract: Basic infrastructure for managing linear equations of the form: a_1 * x_1 + ... + a_n * x_n = 0 Author: Leonardo de Moura (leonardo) 2011-06-28 Revision History: --*/ #ifndef LINEAR_EQUATION_H_ #define LINEAR_EQUATION_H_ #include "util/mpq.h" #include "util/small_object_allocator.h" #include "util/numeral_buffer.h" #include "util/double_manager.h" class linear_equation { public: typedef unsigned var; private: static unsigned get_obj_size(unsigned sz) { return sizeof(linear_equation) + sz * (sizeof(mpz) + sizeof(double) + sizeof(var)); } friend class linear_equation_manager; unsigned m_size; mpz * m_as; // precise coefficients double * m_approx_as; // approximated coefficients var * m_xs; // var ids linear_equation() {} public: unsigned size() const { return m_size; } mpz const & a(unsigned idx) const { SASSERT(idx < m_size); return m_as[idx]; } double approx_a(unsigned idx) const { SASSERT(idx < m_size); return m_approx_as[idx]; } var x(unsigned idx) const { SASSERT(idx < m_size); return m_xs[idx]; } unsigned pos(unsigned x_i) const; void get_a(double_manager & m, unsigned idx, double & r) const { r = m_approx_as[idx]; } template void get_a(NumManager & m, unsigned idx, mpq & r) const { m.set(r, m_as[idx]); } template void get_a(NumManager & m, unsigned idx, mpz & r) const { m.set(r, m_as[idx]); } }; class linear_equation_manager { public: typedef unsynch_mpq_manager numeral_manager; typedef linear_equation::var var; typedef numeral_buffer mpz_buffer; private: typedef svector var_buffer; small_object_allocator & m_allocator; numeral_manager & m; mpz_buffer m_int_buffer; mpz_buffer m_val_buffer; char_vector m_mark; var_buffer m_var_buffer; linear_equation * mk_core(unsigned sz, mpz * as, var * xs); public: linear_equation_manager(numeral_manager & _m, small_object_allocator & a):m_allocator(a), m(_m), m_int_buffer(m), m_val_buffer(m) {} ~linear_equation_manager() {} linear_equation * mk(unsigned sz, mpq * as, var * xs, bool normalized = false); linear_equation * mk(unsigned sz, mpz * as, var * xs, bool normalized = false); void del(linear_equation * eq); // Return b1 * eq1 + b2 * eq2 // return 0 if the b1 * eq1 + b2 * eq2 == 0 linear_equation * mk(mpz const & b1, linear_equation const & eq1, mpz const & b2, linear_equation const & eq2); void display(std::ostream & out, linear_equation const & eq) const; }; #endif z3-z3-4.8.7/src/tactic/arith/nla2bv_tactic.cpp000066400000000000000000000414241356505360400210200ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: nla2bv_tactic.cpp Abstract: Convert quantified NIA problems to bounded bit-vector arithmetic problems. Author: Nikolaj (nbjorner) 2011-05-3 Notes: Ported to tactic framework on 2012-02-28 The original file was called qfnla2bv.cpp --*/ #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" #include "tactic/arith/bv2int_rewriter.h" #include "tactic/arith/bv2real_rewriter.h" #include "tactic/generic_model_converter.h" #include "tactic/arith/bound_manager.h" #include "util/obj_pair_hashtable.h" #include "ast/ast_smt2_pp.h" // // // 1. for each variable, determine bounds (s.t., non-negative variables // have unsigned bit-vectors). // // 2. replace uninterpreted variables of sort int by // expressions of the form +- bv2int(b) +- k // where k is a slack. // // 3. simplify resulting assertion set to reduce occurrences of bv2int. // class nla2bv_tactic : public tactic { class imp { typedef rational numeral; ast_manager & m_manager; bool m_is_sat_preserving; arith_util m_arith; bv_util m_bv; bv2real_util m_bv2real; bv2int_rewriter_ctx m_bv2int_ctx; bound_manager m_bounds; expr_substitution m_subst; func_decl_ref_vector m_vars; expr_ref_vector m_defs; expr_ref_vector m_trail; unsigned m_num_bits; unsigned m_default_bv_size; generic_model_converter_ref m_fmc; public: imp(ast_manager & m, params_ref const& p): m_manager(m), m_is_sat_preserving(true), m_arith(m), m_bv(m), m_bv2real(m, rational(p.get_uint("nla2bv_root",2)), rational(p.get_uint("nla2bv_divisor",2)), p.get_uint("nla2bv_max_bv_size", UINT_MAX)), m_bv2int_ctx(m, p, p.get_uint("nla2bv_max_bv_size", UINT_MAX)), m_bounds(m), m_subst(m), m_vars(m), m_defs(m), m_trail(m), m_fmc(nullptr) { m_default_bv_size = m_num_bits = p.get_uint("nla2bv_bv_size", 4); } ~imp() {} void updt_params(params_ref const& p) { } void operator()(goal & g, model_converter_ref & mc) { 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); 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; } substitute_vars(g); TRACE("nla2bv", g.display(tout << "substitute vars\n");); reduce_bv2int(g); reduce_bv2real(g); TRACE("nla2bv", g.display(tout << "after reduce\n");); mc = m_fmc.get(); for (unsigned i = 0; i < m_vars.size(); ++i) { m_fmc->add(m_vars[i].get(), m_defs[i].get()); } for (unsigned i = 0; i < m_bv2real.num_aux_decls(); ++i) { m_fmc->hide(m_bv2real.get_aux_decl(i)); } IF_VERBOSE(TACTIC_VERBOSITY_LVL, verbose_stream() << "(nla->bv :sat-preserving " << m_is_sat_preserving << ")\n";); TRACE("nla2bv_verbose", g.display(tout);); TRACE("nla2bv", tout << "Muls: " << count_mul(g) << "\n";); g.inc_depth(); if (!is_sat_preserving()) g.updt_prec(goal::UNDER); } bool const& is_sat_preserving() const { return m_is_sat_preserving; } private: void set_satisfiability_preserving(bool f) { m_is_sat_preserving = f; } void collect_power2(goal & g) { m_bv2int_ctx.collect_power2(g); obj_map const& p2 = m_bv2int_ctx.power2(); if (p2.empty()) return; obj_map::iterator it = p2.begin(), end = p2.end(); for (; it != end; ++it) { expr* v = it->m_value; unsigned num_bits = m_bv.get_bv_size(v); expr* w = m_bv.mk_bv2int(m_bv.mk_bv_shl(m_bv.mk_numeral(1, num_bits), v)); m_trail.push_back(w); m_subst.insert(it->m_key, w); TRACE("nla2bv", tout << mk_ismt2_pp(it->m_key, m_manager) << " " << mk_ismt2_pp(w, m_manager) << "\n";); } // eliminate the variables that are power of two. substitute_vars(g); m_subst.reset(); } // eliminate bv2int from formula void reduce_bv2int(goal & g) { bv2int_rewriter_star reduce(m_manager, m_bv2int_ctx); expr_ref r(m_manager); for (unsigned i = 0; i < g.size(); ++i) { reduce(g.form(i), r); g.update(i, r); } assert_side_conditions(g, m_bv2int_ctx.num_side_conditions(), m_bv2int_ctx.side_conditions()); } // eliminate bv2real from formula void reduce_bv2real(goal & g) { bv2real_rewriter_star reduce(m_manager, m_bv2real); expr_ref r(m_manager); for (unsigned i = 0; i < g.size(); ++i) { reduce(g.form(i), r); if (m_bv2real.contains_bv2real(r)) { throw tactic_exception("nla2bv could not eliminate reals"); } g.update(i, r); } assert_side_conditions(g, m_bv2real.num_side_conditions(), m_bv2real.side_conditions()); } void assert_side_conditions(goal & g, unsigned sz, expr * const * conditions) { for (unsigned i = 0; i < sz; ++i) { g.assert_expr(conditions[i]); set_satisfiability_preserving(false); } TRACE("nla2bv", for (unsigned i = 0; i < sz; ++i) { tout << mk_ismt2_pp(conditions[i], m_manager) << "\n"; }); } // substitute variables by bit-vectors void substitute_vars(goal & g) { scoped_ptr er = mk_default_expr_replacer(m_manager); er->set_substitution(&m_subst); expr_ref r(m_manager); for (unsigned i = 0; i < g.size(); ++i) { (*er)(g.form(i), r); g.update(i, r); } } // ----------------- // collect uninterpreted variables in problem. // create a substitution from the variables to // bit-vector terms. // void add_var(app* n) { if (m_arith.is_int(n)) { add_int_var(n); } else { SASSERT(m_arith.is_real(n)); add_real_var(n); } } void add_int_var(app* n) { expr_ref s_bv(m_manager); sort_ref bv_sort(m_manager); optional low, up; numeral tmp; bool is_strict; if (m_bounds.has_lower(n, tmp, is_strict)) { SASSERT(!is_strict); low = tmp; } if (m_bounds.has_upper(n, tmp, is_strict)) { SASSERT(!is_strict); up = tmp; } // // [low .. up] // num_bits = log2(1 + |up - low|) or m_num_bits // unsigned num_bits = m_num_bits; if (up && low) { num_bits = log2(abs(*up - *low)+numeral(1)); } else { TRACE("nla2bv", tout << "no bounds for " << mk_ismt2_pp(n, m_manager) << "\n";); set_satisfiability_preserving(false); } bv_sort = m_bv.mk_sort(num_bits); s_bv = m_manager.mk_fresh_const(n->get_decl()->get_name(), bv_sort); m_fmc->hide(s_bv); s_bv = m_bv.mk_bv2int(s_bv); if (low) { if (!(*low).is_zero()) { // low <= s_bv // ~> // replace s_bv by s_bv + low // add 'low' to model for n. // s_bv = m_arith.mk_add(s_bv, m_arith.mk_numeral(*low, true)); } } else if (up) { // s_bv <= up // ~> // replace s_bv by up - s_bv // s_bv = m_arith.mk_sub(m_arith.mk_numeral(*up, true), s_bv); } else { s_bv = m_arith.mk_sub(s_bv, m_arith.mk_numeral(rational::power_of_two(num_bits-1), true)); } m_trail.push_back(s_bv); m_subst.insert(n, s_bv); m_vars.push_back(n->get_decl()); m_defs.push_back(s_bv); } void add_real_var(app* n) { expr_ref s_bv(m_manager), s_bvr(m_manager), s(m_manager), t(m_manager); sort_ref bv_sort(m_manager); bv_sort = m_bv.mk_sort(m_num_bits); set_satisfiability_preserving(false); std::string name = n->get_decl()->get_name().str(); s = m_manager.mk_fresh_const(name, bv_sort); name += "_r"; t = m_manager.mk_fresh_const(name, bv_sort); m_fmc->hide(s); m_fmc->hide(t); s_bv = m_bv2real.mk_bv2real(s, t); m_trail.push_back(s_bv); m_subst.insert(n, s_bv); m_vars.push_back(n->get_decl()); // use version without bv2real function. m_bv2real.mk_bv2real_reduced(s, t, s_bvr); m_defs.push_back(s_bvr); } // update number of bits based on the largest constant used. void update_num_bits(app* n) { bool is_int; numeral nm; if (m_arith.is_numeral(n, nm, is_int) && is_int) { nm = abs(nm); unsigned l = log2(nm); if (m_num_bits <= l) { m_num_bits = l+1; } } } unsigned log2(rational const& n) { rational pow(1), two(2); unsigned sz = 0; while (pow < n) { ++sz; pow *= two; } if (sz == 0) sz = 1; return sz; } 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), 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) { if (a.is_int(n) && is_uninterp_const(n)) { m_vars.push_back(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 (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); } void operator()(quantifier* q) { m_in_supported_fragment = false; } bool is_supported() const { return m_in_supported_fragment; } }; 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]); } 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 { imp& m_imp; unsigned m_count; public: count_mul_proc(imp& s): m_imp(s), m_count(0) {} unsigned count() const { return m_count; } void operator()(var * n) {} void operator()(app* n) { if (m_imp.m_arith.is_mul(n)) { m_count += n->get_num_args()-1; } if (m_imp.m_bv.is_bv_mul(n)) { unsigned num_vars = 0; for (unsigned j = 0; j < n->get_num_args(); ++j) { if (!m_imp.m_bv.is_numeral(n->get_arg(j))) { ++num_vars; } } if (num_vars > 1) { m_count += num_vars - 1; } } } void operator()(quantifier* q) {} }; unsigned count_mul(goal const & g) { count_mul_proc c(*this); for_each_expr_at(c, g); return c.count(); } }; params_ref m_params; imp * m_imp; struct scoped_set_imp { nla2bv_tactic & m_owner; scoped_set_imp(nla2bv_tactic & o, imp & i): m_owner(o) { m_owner.m_imp = &i; } ~scoped_set_imp() { m_owner.m_imp = nullptr; } }; public: nla2bv_tactic(params_ref const & p): m_params(p), m_imp(nullptr) { } tactic * translate(ast_manager & m) override { return alloc(nla2bv_tactic, m_params); } ~nla2bv_tactic() override { } void updt_params(params_ref const & p) override { m_params.append(p); } void collect_param_descrs(param_descrs & r) override { r.insert("nla2bv_max_bv_size", CPK_UINT, "(default: inf) maximum bit-vector size used by nla2bv tactic"); r.insert("nla2bv_bv_size", CPK_UINT, "(default: 4) default bit-vector size used by nla2bv tactic."); r.insert("nla2bv_root", CPK_UINT, "(default: 2) nla2bv tactic encodes reals into bit-vectors using expressions of the form a+b*sqrt(c), this parameter sets the value of c used in the encoding."); r.insert("nla2bv_divisor", CPK_UINT, "(default: 2) nla2bv tactic parameter."); } /** \brief Modify a goal to use bounded bit-vector arithmetic in place of non-linear integer arithmetic. \return false if transformation is not possible. */ void operator()(goal_ref const & g, goal_ref_buffer & result) override { SASSERT(g->is_well_sorted()); fail_if_proof_generation("nla2bv", g); fail_if_unsat_core_generation("nla2bv", g); result.reset(); imp proc(g->m(), m_params); scoped_set_imp setter(*this, proc); model_converter_ref mc; proc(*(g.get()), mc); g->add(mc.get()); result.push_back(g.get()); SASSERT(g->is_well_sorted()); } void cleanup() override { } }; tactic * mk_nla2bv_tactic(ast_manager & m, params_ref const & p) { return alloc(nla2bv_tactic, p); } z3-z3-4.8.7/src/tactic/arith/nla2bv_tactic.h000066400000000000000000000012641356505360400204630ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: nla2bv_tactic.cpp Abstract: Convert quantified NIA problems to bounded bit-vector arithmetic problems. Author: Nikolaj (nbjorner) 2011-05-3 Notes: Ported to tactic framework on 2012-02-28 --*/ #ifndef NLA2BV_TACTIC_H_ #define NLA2BV_TACTIC_H_ #include "util/params.h" class ast_manager; class tactic; tactic * mk_nla2bv_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("nla2bv", "convert a nonlinear arithmetic problem into a bit-vector problem, in most cases the resultant goal is an under approximation and is useul for finding models.", "mk_nla2bv_tactic(m, p)") */ #endif z3-z3-4.8.7/src/tactic/arith/normalize_bounds_tactic.cpp000066400000000000000000000131001356505360400231740ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: normalize_bounds_tactic.cpp Abstract: Replace x with x' + l, when l <= x where x' is a fresh variable. Note that, after the transformation 0 <= x'. Author: Leonardo de Moura (leonardo) 2011-10-21. Revision History: --*/ #include "tactic/tactical.h" #include "tactic/arith/bound_manager.h" #include "ast/rewriter/th_rewriter.h" #include "tactic/generic_model_converter.h" #include "ast/arith_decl_plugin.h" #include "ast/expr_substitution.h" #include "ast/ast_smt2_pp.h" class normalize_bounds_tactic : public tactic { struct imp { ast_manager & m; bound_manager m_bm; arith_util m_util; th_rewriter m_rw; bool m_normalize_int_only; imp(ast_manager & _m, params_ref const & p): m(_m), m_bm(m), m_util(m), m_rw(m, p) { updt_params(p); } void updt_params_core(params_ref const & p) { m_normalize_int_only = p.get_bool("norm_int_only", true); } void updt_params(params_ref const & p) { m_rw.updt_params(p); updt_params_core(p); } bool is_target(expr * var, rational & val) { bool strict; return is_uninterp_const(var) && (!m_normalize_int_only || m_util.is_int(var)) && m_bm.has_lower(var, val, strict) && !val.is_zero(); } bool is_target(expr * var) { rational val; return is_target(var, val); } bool has_lowers() { bound_manager::iterator it = m_bm.begin(); bound_manager::iterator end = m_bm.end(); for (; it != end; ++it) { TRACE("normalize_bounds_tactic", rational val; bool strict; tout << mk_ismt2_pp(*it, m) << " has_lower: " << m_bm.has_lower(*it, val, strict) << " val: " << val << "\n";); if (is_target(*it)) return true; } return false; } void operator()(goal_ref const & in, goal_ref_buffer & result) { bool produce_models = in->models_enabled(); bool produce_proofs = in->proofs_enabled(); tactic_report report("normalize-bounds", *in); m_bm(*in); if (!has_lowers()) { result.push_back(in.get()); // did not increase depth since it didn't do anything. return; } generic_model_converter * gmc = nullptr; if (produce_models) { gmc = alloc(generic_model_converter, m, "normalize_bounds"); in->add(gmc); } unsigned num_norm_bounds = 0; expr_substitution subst(m); rational val; for (expr * x : m_bm) { if (is_target(x, val)) { num_norm_bounds++; sort * s = m.get_sort(x); app * x_prime = m.mk_fresh_const(nullptr, s); expr * def = m_util.mk_add(x_prime, m_util.mk_numeral(val, s)); subst.insert(x, def); if (produce_models) { gmc->hide(x_prime->get_decl()); gmc->add(to_app(x)->get_decl(), def); } } } report_tactic_progress(":normalized-bounds", num_norm_bounds); m_rw.set_substitution(&subst); expr_ref new_curr(m); proof_ref new_pr(m); unsigned size = in->size(); for (unsigned idx = 0; idx < size; idx++) { expr * curr = in->form(idx); m_rw(curr, new_curr, new_pr); if (produce_proofs) { proof * pr = in->pr(idx); new_pr = m.mk_modus_ponens(pr, new_pr); } in->update(idx, new_curr, new_pr, in->dep(idx)); } TRACE("normalize_bounds_tactic", in->display(tout);); in->inc_depth(); result.push_back(in.get()); } }; imp * m_imp; params_ref m_params; public: normalize_bounds_tactic(ast_manager & m, params_ref const & p): m_params(p) { m_imp = alloc(imp, m, p); } tactic * translate(ast_manager & m) override { return alloc(normalize_bounds_tactic, m, m_params); } ~normalize_bounds_tactic() override { dealloc(m_imp); } void updt_params(params_ref const & p) override { m_imp->updt_params(p); } void collect_param_descrs(param_descrs & r) override { insert_produce_models(r); r.insert("norm_int_only", CPK_BOOL, "(default: true) normalize only the bounds of integer constants."); } void operator()(goal_ref const & in, goal_ref_buffer & result) override { try { (*m_imp)(in, result); } catch (rewriter_exception & ex) { throw tactic_exception(ex.msg()); } } void cleanup() override { ast_manager & m = m_imp->m; imp * d = alloc(imp, m, m_params); std::swap(d, m_imp); dealloc(d); } }; tactic * mk_normalize_bounds_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(normalize_bounds_tactic, m, p)); } z3-z3-4.8.7/src/tactic/arith/normalize_bounds_tactic.h000066400000000000000000000012431356505360400226460ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: normalize_bounds_tactic.h Abstract: Replace x with x' + l, when l <= x where x' is a fresh variable. Note that, after the transformation 0 <= x'. Author: Leonardo de Moura (leonardo) 2011-10-21. Revision History: --*/ #ifndef NORMALIZE_BOUNDS_TACTIC_H_ #define NORMALIZE_BOUNDS_TACTIC_H_ #include "util/params.h" class ast_manager; class tactic; tactic * mk_normalize_bounds_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("normalize-bounds", "replace a variable x with lower bound k <= x with x' = x - k.", "mk_normalize_bounds_tactic(m, p)") */ #endif z3-z3-4.8.7/src/tactic/arith/pb2bv_model_converter.cpp000066400000000000000000000056311356505360400225670ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: pb2bv_model_converter.cpp Abstract: Model converter for the pb2bv tactic. Author: Christoph (cwinter) 2012-02-15 Notes: --*/ #include "util/trace.h" #include "ast/arith_decl_plugin.h" #include "model/model_v2_pp.h" #include "tactic/arith/pb2bv_model_converter.h" pb2bv_model_converter::pb2bv_model_converter(ast_manager & _m) : m(_m) { } pb2bv_model_converter::pb2bv_model_converter(ast_manager & _m, obj_map const & c2bit, bound_manager const & bm): m(_m) { for (auto const& kv : c2bit) { m_c2bit.push_back(func_decl_pair(kv.m_key, to_app(kv.m_value)->get_decl())); m.inc_ref(kv.m_key); m.inc_ref(to_app(kv.m_value)->get_decl()); } for (expr* c : bm) { SASSERT(is_uninterp_const(c)); func_decl * d = to_app(c)->get_decl(); if (!c2bit.contains(d)) { SASSERT(d->get_arity() == 0); m.inc_ref(d); m_c2bit.push_back(func_decl_pair(d, static_cast(nullptr))); } } } pb2bv_model_converter::~pb2bv_model_converter() { for (auto const& kv : m_c2bit) { m.dec_ref(kv.first); m.dec_ref(kv.second); } } void pb2bv_model_converter::get_units(obj_map& units) { if (!m_c2bit.empty()) units.reset(); } void pb2bv_model_converter::operator()(model_ref & md) { TRACE("pb2bv", tout << "converting model:\n"; model_v2_pp(tout, *md); display(tout);); arith_util a_util(m); for (auto const& kv : m_c2bit) { if (kv.second) { expr * val = md->get_const_interp(kv.second); if (val == nullptr || m.is_false(val)) { /* false's and don't cares get the integer 0 solution*/ md->register_decl(kv.first, a_util.mk_numeral(rational(0), true)); } else { md->register_decl(kv.first, a_util.mk_numeral(rational(1), true)); } } else { // kv.first is a don't care. md->register_decl(kv.first, a_util.mk_numeral(rational(0), true)); } } } void pb2bv_model_converter::display(std::ostream & out) { out << "(pb2bv-model-converter"; for (auto const& kv : m_c2bit) { out << "\n (" << kv.first->get_name() << " "; if (kv.second == 0) out << "0"; else out << kv.second->get_name(); out << ")"; } out << ")\n"; } model_converter * pb2bv_model_converter::translate(ast_translation & translator) { ast_manager & to = translator.to(); pb2bv_model_converter * res = alloc(pb2bv_model_converter, to); for (auto const& kv : m_c2bit) { func_decl * f1 = translator(kv.first); func_decl * f2 = translator(kv.second); res->m_c2bit.push_back(func_decl_pair(f1, f2)); to.inc_ref(f1); to.inc_ref(f2); } return res; } z3-z3-4.8.7/src/tactic/arith/pb2bv_model_converter.h000066400000000000000000000017271356505360400222360ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: pb2bv_model_converter.h Abstract: Model converter for the pb2bv tactic. Author: Christoph (cwinter) 2012-02-15 Notes: --*/ #ifndef PB2BV_MODEL_CONVERTER_H_ #define PB2BV_MODEL_CONVERTER_H_ #include "tactic/model_converter.h" #include "tactic/arith/bound_manager.h" class pb2bv_model_converter : public model_converter { typedef std::pair func_decl_pair; ast_manager & m; svector m_c2bit; public: pb2bv_model_converter(ast_manager & _m); pb2bv_model_converter(ast_manager & _m, obj_map const & c2bit, bound_manager const & bm); ~pb2bv_model_converter() override; void operator()(model_ref & md) override; void display(std::ostream & out) override; void get_units(obj_map& units) override; model_converter * translate(ast_translation & translator) override; }; #endif z3-z3-4.8.7/src/tactic/arith/pb2bv_tactic.cpp000066400000000000000000001105441356505360400206470ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: pb2bv_tactic.cpp Abstract: Tactic for converting Pseudo-Boolean constraints to BV Author: Christoph (cwinter) 2012-02-15 Notes: --*/ #include "tactic/tactical.h" #include "tactic/arith/bound_manager.h" #include "ast/rewriter/bool_rewriter.h" #include "ast/rewriter/rewriter_def.h" #include "util/ref_util.h" #include "ast/arith_decl_plugin.h" #include "util/trace.h" #include "ast/ast_smt2_pp.h" #include "ast/expr_substitution.h" #include "tactic/generic_model_converter.h" #include "tactic/arith/pb2bv_model_converter.h" #include "tactic/arith/pb2bv_tactic.h" #include "ast/ast_pp.h" class pb2bv_tactic : public tactic { public: struct non_pb { expr* e; non_pb(expr* e) : e(e) {}}; struct only_01_visitor { typedef rational numeral; ast_manager & m; arith_util & m_util; bound_manager & m_bm; only_01_visitor(arith_util & u, bound_manager & bm): m(u.get_manager()), m_util(u), m_bm(bm) { } void throw_non_pb(expr * n) { TRACE("pb2bv", tout << "Not pseudo-Boolean: " << mk_ismt2_pp(n, m) << "\n";); throw non_pb(n); } void operator()(var * n) { throw_non_pb(n); } void operator()(app * n) { family_id fid = n->get_family_id(); if (fid == m.get_basic_family_id()) { // all basic family ops (but term-ite and distinct) are OK if (m.is_term_ite(n) || m.is_distinct(n)) throw_non_pb(n); return; } if (fid == m_util.get_family_id()) { // check if linear switch (n->get_decl_kind()) { case OP_LE: case OP_GE: case OP_LT: case OP_GT: case OP_ADD: case OP_NUM: return; case OP_MUL: if (n->get_num_args() != 2) throw_non_pb(n); if (!m_util.is_numeral(n->get_arg(0))) throw_non_pb(n); return; default: throw_non_pb(n); } } if (is_uninterp_const(n)) { if (m.is_bool(n)) return; // boolean variables are ok if (m_util.is_int(n)) { numeral l, u; bool s; if (m_bm.has_lower(n, l, s) && m_bm.has_upper(n, u, s) && (l.is_zero() || l.is_one()) && (u.is_zero() || u.is_one())) return; } } throw_non_pb(n); } void operator()(quantifier * n) { throw_non_pb(n); } }; private: struct imp { typedef rational numeral; ast_manager & m; bound_manager m_bm; bool_rewriter m_b_rw; arith_util m_arith_util; bv_util m_bv_util; expr_dependency_ref_vector m_new_deps; bool m_produce_models; bool m_produce_unsat_cores; unsigned m_all_clauses_limit; unsigned m_cardinality_limit; unsigned long long m_max_memory; // m_const2bit should be a map, since we want constant time access to it, and avoid quadratic behavior. // It is ok to use a vector at the model converter because we don't need to search that vector. obj_map m_const2bit; obj_map m_not_const2bit; expr_ref_vector m_temporary_ints; expr_dependency_ref m_used_dependencies; struct lit { expr * m_v; public: lit(expr * v, bool sign = false):m_v(TAG(expr*, v, sign)) {} bool sign() const { return GET_TAG(m_v) == 1; } expr * var() const { return UNTAG(expr*, m_v); } void neg() { #ifdef Z3DEBUG bool s = sign(); #endif m_v = TAG(expr*, UNTAG(expr*, m_v), !sign()); SASSERT(s == !sign()); } }; struct monomial { numeral m_a; lit m_lit; monomial(lit l):m_a(1), m_lit(l) {} monomial(numeral const & a, lit l):m_a(a), m_lit(l) {} }; typedef vector polynomial; struct monomial_lt { bool operator()(monomial const & m1, monomial const & m2) const { return m1.m_a > m2.m_a; } }; enum constraint_kind { EQ, GE, LE }; struct failed {}; struct visitor { imp & m_owner; visitor(imp & o):m_owner(o) {} void throw_failed(expr * n) { throw failed(); } void operator()(var * n) { throw_failed(n); } void operator()(app * n) { } void operator()(quantifier * n) { throw_failed(n); } }; void checkpoint() { if (memory::get_allocation_size() > m_max_memory) throw tactic_exception(TACTIC_MAX_MEMORY_MSG); } void quick_pb_check(goal_ref const & g) { expr_fast_mark1 visited; only_01_visitor proc(m_arith_util, m_bm); unsigned sz = g->size(); for (unsigned i = 0; i < sz; i++) { expr * f = g->form(i); for_each_expr_core(proc, visited, f); } } struct rw_cfg : public default_rewriter_cfg { ast_manager & m; imp & owner; expr_ref m_saved_res; rw_cfg(imp & o): m(o.m), owner(o), m_saved_res(m) { } bool max_steps_exceeded(unsigned num_steps) const { if (memory::get_allocation_size() > owner.m_max_memory) throw tactic_exception(TACTIC_MAX_MEMORY_MSG); return false; } bool get_subst(expr * s, expr * & t, proof * & t_pr) { t_pr = nullptr; if (owner.is_constraint_core(s)) { owner.convert(to_app(s), m_saved_res, true, false); t = m_saved_res; TRACE("pb2bv_convert", tout << mk_ismt2_pp(s, m) << "\n-->\n" << mk_ismt2_pp(t, m) << "\n";); return true; } return false; } }; struct rw : public rewriter_tpl { rw_cfg m_cfg; rw(imp & o): rewriter_tpl(o.m, false, m_cfg), m_cfg(o) { } }; rw m_rw; struct pb2bv_all_clauses { imp & m_owner; ast_manager & m; unsigned m_size; vector m_sums; expr_ref_vector m_lits; ptr_vector m_cls; polynomial const * m_pol; expr_ref_vector m_result; pb2bv_all_clauses(imp & owner): m_owner(owner), m(m_owner.m), m_lits(m), m_result(m) { } void init_lits(polynomial const & p) { polynomial::const_iterator it = p.begin(); polynomial::const_iterator end = p.end(); for (; it != end; ++it) m_lits.push_back(m_owner.mon_lit2lit(it->m_lit)); } void init_sums(polynomial const & p) { SASSERT(m_sums.empty()); m_size = p.size(); m_sums.resize(m_size); unsigned i = m_size; while (i > 0) { --i; if (i == m_size - 1) m_sums[i] = p[i].m_a; else m_sums[i] = p[i].m_a + m_sums[i+1]; } } void process(unsigned idx, numeral c) { if (c.is_nonpos()) return; if (idx == m_size || m_sums[idx] < c) { SASSERT(c.is_pos()); // conflict 0 >= c > 0 switch (m_cls.size()) { case 0: m_result.push_back(m.mk_false()); break; case 1: m_result.push_back(m_cls[0]); break; default: m_result.push_back(m.mk_or(m_cls.size(), m_cls.c_ptr())); } return; } m_owner.checkpoint(); m_cls.push_back(m_lits.get(idx)); process(idx+1, c); m_cls.pop_back(); process(idx+1, c - (*m_pol)[idx].m_a); } void operator()(polynomial const & m_p, numeral const & m_c, expr_ref & r) { m_pol = &(m_p); init_sums(m_p); init_lits(m_p); process(0, m_c); m_owner.m_b_rw.mk_and(m_result.size(), m_result.c_ptr(), r); } }; void display(std::ostream & out, polynomial const & m_p, numeral const & m_c) const { polynomial::const_iterator it = m_p.begin(); polynomial::const_iterator end = m_p.end(); for (bool first = true; it != end; ++it) { if (!first) out << " + "; first = false; if (!it->m_a.is_one()) out << it->m_a << "*"; if (it->m_lit.sign()) out << "~"; out << mk_ismt2_pp(it->m_lit.var(), m); } out << " >= " << m_c << "\n"; } expr * int2lit(app * x, bool sign = false) { func_decl * fd = x->get_decl(); obj_map & const2lit = sign ? m_not_const2bit : m_const2bit; expr * r = nullptr; const2lit.find(fd, r); if (r != nullptr) return r; r = m.mk_fresh_const(nullptr, m.mk_bool_sort()); expr * not_r = m.mk_not(r); m_const2bit.insert(fd, r); m_not_const2bit.insert(fd, not_r); m.inc_ref(fd); m.inc_ref(r); m.inc_ref(not_r); return sign ? not_r : r; } expr * mon_lit2lit(monomial const & mo) { return int2lit(to_app(mo.m_lit.var()), mo.m_lit.sign()); } expr * mk_unit(expr * t, bool sign) { return mon_lit2lit(lit(t, sign)); } static bool is_cardinality(polynomial const & m_p, numeral const & m_c) { for (unsigned i = 0; i < m_p.size(); i++) { if (!m_p[i].m_a.is_one()) return false; } return true; } void bitblast_pbc(polynomial & m_p, numeral const & m_c, expr_ref & r) { bool is_card = is_cardinality(m_p, m_c); if (is_card && numeral(m_p.size()) < m_c) { r = m.mk_false(); return; } if (is_card && m_c.is_one()) { ptr_buffer args; for (unsigned i = 0; i < m_p.size(); i++) { args.push_back(mon_lit2lit(m_p[i])); } r = m.mk_or(args.size(), args.c_ptr()); return; } if (is_card && m_c == numeral(m_p.size())) { ptr_buffer args; for (unsigned i = 0; i < m_p.size(); i++) { args.push_back(mon_lit2lit(m_p[i])); } m_b_rw.mk_and(args.size(), args.c_ptr(), r); return; } if (m_p.size() <= m_all_clauses_limit) { pb2bv_all_clauses proc(*this); proc(m_p, m_c, r); return; } if (is_card) { SASSERT(m_c < numeral(m_p.size())); // After normalization, this should be true. SASSERT(m_c.is_unsigned()); // Otherwise this is not going to fit into memory... unsigned n = m_p.size(); unsigned k = m_c.get_unsigned(); unsigned rowsz = n - k + 1; unsigned long long cost = k * rowsz; if (cost <= static_cast(m_cardinality_limit)) { SASSERT(rowsz > 0); expr_ref_vector tmp(m); tmp.resize(rowsz, m.mk_true()); for (unsigned i = 0; i < k; i++) { for (unsigned j = 0; j < rowsz; j++) { expr_ref new_ite(m); m_b_rw.mk_ite(mon_lit2lit(m_p[i + j]), tmp.get(j), j == 0 ? m.mk_false() : tmp.get(j-1), new_ite); tmp.set(j, new_ite.get()); } } TRACE("pb2bv_bv", tout << "BV Cardinality: " << mk_ismt2_pp(tmp.back(), m) << std::endl;); r = tmp.back(); return; } } TRACE("pb2bv_bv_detail", tout << "encoding:\n"; display(tout, m_p, m_c);); // [Leo] improving number of bits needed. // using (sum-of-coeffs).get_num_bits() numeral sum; for (unsigned i = 0; i < m_p.size(); i++) { monomial const & mo = m_p[i]; SASSERT(mo.m_a.is_pos()); sum += mo.m_a; } if (sum < m_c) { // trivially false. r = m.mk_false(); return; } unsigned bits = sum.get_num_bits(); TRACE("num_bits_bug", tout << "bits: " << bits << " sum: " << sum << " size: " << m_p.size() << "\n";); // [Leo]: The following assertion should hold, right? // I mean, the constraints are normalized, then mo.m_a <= m_c for every monomial in cnstr. // [Christoph]: I agree and never saw it violated so far! SASSERT(m_c.get_num_bits() <= bits); ptr_buffer lhs_args; for (unsigned i = 0; i < m_p.size(); i++) { monomial const & mo = m_p[i]; // encode using if-then-else expr * bv_monom = m.mk_ite(mon_lit2lit(mo.m_lit), m_bv_util.mk_numeral(mo.m_a, bits), m_bv_util.mk_numeral(numeral(0), bits)); lhs_args.push_back(bv_monom); } expr * lhs = m.mk_app(m_bv_util.get_family_id(), OP_BADD, lhs_args.size(), lhs_args.c_ptr()); expr * rhs = m_bv_util.mk_numeral(m_c, bits); r = m_bv_util.mk_ule(rhs, lhs); } void split(polynomial & m_p, numeral & m_c, polynomial & m_clause) { if (m_p.size() <= 2 || m_c.is_one()) return; if (m_p[0].m_a != m_c || m_p[1].m_a != m_c) return; // nothing to do. unsigned sz = m_p.size(); unsigned i; for (i = 2; i < sz; i++) { if (m_p[i].m_a != m_c) break; } if (i >= sz) { // [Christoph]: In this case, all the m_a are equal to m_c. return; } // copy lits [0, i) to m_clause for (unsigned j = 0; j < i; j++) m_clause.push_back(monomial(numeral(1), m_p[j].m_lit)); app * new_var = m.mk_fresh_const(nullptr, m_arith_util.mk_int()); m_temporary_ints.push_back(new_var); m_clause.push_back(monomial(numeral(1), lit(new_var, true))); // remove monomials [0, i) from m_p and add new_var in the beginning for (unsigned j = i; j < sz; j++) { m_p[j - i + 1] = m_p[j]; } m_p.shrink(sz - i + 1); m_p[0] = monomial(m_c, lit(new_var, false)); } void mk_pbc(polynomial & m_p, numeral & m_c, expr_ref & r, bool enable_split) { TRACE("mk_pbc", display(tout, m_p, m_c); ); if (m_c.is_nonpos()) { // constraint is equivalent to true. r = m.mk_true(); return; } polynomial::iterator it = m_p.begin(); polynomial::iterator end = m_p.end(); numeral a_gcd = (it->m_a > m_c) ? m_c : it->m_a; for (; it != end; ++it) { if (it->m_a > m_c) it->m_a = m_c; // trimming coefficients a_gcd = gcd(a_gcd, it->m_a); } SASSERT(a_gcd.is_pos()); if (!a_gcd.is_one()) { it = m_p.begin(); for (; it != end; ++it) it->m_a /= a_gcd; m_c = ceil(m_c/a_gcd); } TRACE("mk_pbc", tout << "GCD = " << a_gcd << "; Normalized: "; display(tout, m_p, m_c); tout << "\n"; ); it = m_p.begin(); numeral a_sum; for (; it != end; ++it) { a_sum += m_c; if (a_sum >= m_c) break; } if (a_sum < m_c) { // constraint is equivalent to false. r = m.mk_false(); return; } polynomial clause; TRACE("split_bug", display(tout, m_p, m_c);); if (enable_split) split(m_p, m_c, clause); TRACE("split_bug", display(tout, m_p, m_c); display(tout, clause, rational(1));); if (clause.empty()) { bitblast_pbc(m_p, m_c, r); } else { expr_ref r1(m); expr_ref r2(m); bitblast_pbc(m_p, m_c, r1); bitblast_pbc(clause, numeral(1), r2); TRACE("split_bug", tout << mk_ismt2_pp(r1, m) << "\nAND\n" << mk_ismt2_pp(r2, m) << "\n";); m_b_rw.mk_and(r1, r2, r); } } void adjust(bool & pos, constraint_kind & k, numeral & c) { if (!pos) { if (k == LE) { // not (lhs <= c) --> lhs > c --> lhs >= c+1 pos = true; k = GE; c++; } else if (k == GE) { // not (lhs >= c) --> lhs < c --> lhs <= c-1 pos = true; k = LE; c--; } } SASSERT(pos || k == EQ); } void throw_non_pb(expr * n) { TRACE("pb2bv", tout << "Not pseudo-Boolean: " << mk_ismt2_pp(n, m) << "\n";); throw non_pb(n); } // check if polynomial is encoding // a_0*x_0 + a_0*~y_0 + ... + a_{n-1}*x_{n - 1} + a_{n - 1}*~y_{n - 1} = c // x_0 = y_0, ..., x_{n - 1} = y_{n - 1} bool is_eq_vector(polynomial const & p, numeral const & c) { TRACE("is_eq_vector", display(tout, p, c);); unsigned sz = p.size(); if (sz % 2 == 1) return false; // size must be even // I implemented only the easy (and very common) case, where a_i = 2^{n-i-1} and c = 2^n - 1 unsigned n = sz/2; if (c != rational::power_of_two(n) - numeral(1)) return false; for (unsigned i = 0; i < n; i++) { monomial const & m1 = p[i*2]; monomial const & m2 = p[i*2+1]; if (m1.m_lit.sign() == m2.m_lit.sign()) return false; if (m1.m_a != m2.m_a) return false; if (m1.m_a != rational::power_of_two(n - i - 1)) return false; } return true; } void add_bounds_dependencies(expr * a) { m_used_dependencies = m.mk_join(m_used_dependencies, m_bm.lower_dep(a)); m_used_dependencies = m.mk_join(m_used_dependencies, m_bm.upper_dep(a)); } void convert(app * t, expr_ref & r, bool pos, bool root) { constraint_kind k; expr * lhs, * rhs; if (m.is_eq(t, lhs, rhs)) { if (is_uninterp_const(lhs) && is_uninterp_const(rhs)) { add_bounds_dependencies(lhs); add_bounds_dependencies(rhs); r = m.mk_iff(mon_lit2lit(lit(lhs, false)), mon_lit2lit(lit(rhs, !pos))); return; } k = EQ; } else if (m_arith_util.is_le(t, lhs, rhs)) { k = LE; } else if (m_arith_util.is_ge(t, lhs, rhs)) { k = GE; } else { throw_non_pb(t); } numeral c; bool is_int; if (m_arith_util.is_numeral(lhs, c)) { adjust(pos, k, c); if (is_uninterp_const(rhs)) { add_bounds_dependencies(rhs); if (k == EQ) { bool sign = c.is_zero(); if (!pos) sign = !sign; r = mk_unit(rhs, sign); } else if ((c.is_zero() && k == LE) || (c.is_one() && k == GE)) { // redundant 0 <= x, 1 >= x TRACE("pb2bv", tout << "discarding:\n" << mk_ismt2_pp(t, m) << "\n";); SASSERT(pos); r = m.mk_true(); } else { SASSERT((c.is_zero() && k == GE) || (c.is_one() && k == LE)); // unit 0 >= x, 1 <= x SASSERT(pos); r = mk_unit(rhs, k == GE); } return; } throw_non_pb(t); } if (!m_arith_util.is_numeral(rhs, c, is_int) || !is_int) throw_non_pb(t); adjust(pos, k, c); if (is_uninterp_const(lhs)) { add_bounds_dependencies(lhs); if (k == EQ) { TRACE("pb2bv_bug", tout << "c: " << c << "\n";); if (!c.is_zero() && !c.is_one()) { // x = k --> true where k is not 0 or 1 r = pos ? m.mk_false() : m.mk_true(); } else { bool sign = c.is_zero(); if (!pos) sign = !sign; r = mk_unit(lhs, sign); } return; } else { // Our atom is of the form: (<= lhs c) or (>= lhs c) // c may be different from 0,1. if (k == LE) { // x <= c >= 1 if (c >= numeral(1)) { TRACE("pb2bv", tout << "discarding:\n" << mk_ismt2_pp(t, m) << "\n";); r = m.mk_true(); return; } else if (c.is_neg()) { // x <= c < 0 r = m.mk_false(); return; } SASSERT(c.is_zero()); } else if (k == GE) { if (c.is_nonpos()) { TRACE("pb2bv", tout << "discarding:\n" << mk_ismt2_pp(t, m) << "\n";); // x >= a <= 0 r = m.mk_true(); return; } else if (c > numeral(1)) { // x >= a > 1 r = m.mk_false(); return; } SASSERT(c.is_one()); } CTRACE("pb2bv", !(c.is_zero() || c.is_one()), tout << "BUG: " << mk_ismt2_pp(t, m) << "\nk: " << k << " " << c << "\n";); SASSERT(c.is_zero() || c.is_one()); SASSERT(!((c.is_zero() && k == GE) || (c.is_one() && k == LE))); CTRACE("pb2bv_bug", !((c.is_zero() && k == LE) || (c.is_one() && k == GE)), tout << "c: " << c << ", k: " << k << "\n"; tout << "t: " << mk_ismt2_pp(t, m) << "\n";); SASSERT((c.is_zero() && k == LE) || (c.is_one() && k == GE)); // x <= 0, x >= 1 SASSERT(pos); r = mk_unit(lhs, k == LE); return; } } if (!m_arith_util.is_add(lhs)) throw_non_pb(t); unsigned sz = to_app(lhs)->get_num_args(); expr * const * ms = to_app(lhs)->get_args(); expr * a, * x; for (unsigned i = 0; i < sz; i++) { expr * m = ms[i]; if (is_uninterp_const(m)) continue; if (m_arith_util.is_mul(m, a, x) && m_arith_util.is_numeral(a) && is_uninterp_const(x)) continue; throw_non_pb(t); } // is pb constraint. numeral a_val; polynomial m_p; numeral m_c; m_c = c; for (unsigned i = 0; i < sz; i++) { expr * m = ms[i]; if (is_uninterp_const(m)) { add_bounds_dependencies(m); m_p.push_back(monomial(lit(m))); } else if (m_arith_util.is_mul(m, a, x) && m_arith_util.is_numeral(a, a_val)) { add_bounds_dependencies(x); if (a_val.is_neg()) { a_val.neg(); // -a x --> -a(1-!x) ==> -a + a!x, m_c += a_val; m_p.push_back(monomial(a_val, lit(x, true))); } else { m_p.push_back(monomial(a_val, lit(x))); } } else { UNREACHABLE(); } } std::stable_sort(m_p.begin(), m_p.end(), monomial_lt()); if (k == GE) { mk_pbc(m_p, m_c, r, root); } else if (k == LE) { m_c.neg(); for (unsigned i = 0; i < sz; i++) { monomial & m = m_p[i]; SASSERT(m.m_a.is_nonneg()); m_c += m.m_a; m.m_lit.neg(); } mk_pbc(m_p, m_c, r, root); } else { SASSERT(k == EQ); if (is_eq_vector(m_p, m_c)) { TRACE("is_eq_vector", tout << "found eq vector\n";); unsigned sz = m_p.size(); expr_ref_vector eqs(m); for (unsigned i = 0; i < sz; i += 2) { app * x_i = to_app(m_p[i].m_lit.var()); app * y_i = to_app(m_p[i+1].m_lit.var()); eqs.push_back(m.mk_eq(int2lit(x_i), int2lit(y_i))); } m_b_rw.mk_and(eqs.size(), eqs.c_ptr(), r); if (!pos) m_b_rw.mk_not(r, r); return; } polynomial m_p2; numeral m_c2 = m_c; m_c2.neg(); for (unsigned i = 0; i < sz; i++) { monomial m = m_p[i]; SASSERT(m.m_a.is_nonneg()); m_c2 += m.m_a; m.m_lit.neg(); m_p2.push_back(m); } expr_ref r1(m); expr_ref r2(m); mk_pbc(m_p, m_c, r1, false); mk_pbc(m_p2, m_c2, r2, false); TRACE("pb2bv_convert", tout << mk_ismt2_pp(t, m) << "\n"; display(tout, m_p, m_c); display(tout, m_p2, m_c2); tout << "--->\n" << mk_ismt2_pp(r1, m) << "\nAND\n" << mk_ismt2_pp(r2, m) << "\n";); m_b_rw.mk_and(r1, r2, r); if (!pos) m_b_rw.mk_not(r, r); } } bool is_constraint_core(expr * n) { return (m.is_eq(n) && m_arith_util.is_int(to_app(n)->get_arg(0))) || m_arith_util.is_le(n) || m_arith_util.is_ge(n); } bool is_constraint(expr * n, expr * & atom, bool & pos) { pos = true; while (m.is_not(n)) { n = to_app(n)->get_arg(0); pos = !pos; } atom = n; return is_constraint_core(n); } imp(ast_manager & _m, params_ref const & p): m(_m), m_bm(m), m_b_rw(m, p), m_arith_util(m), m_bv_util(m), m_new_deps(m), m_temporary_ints(m), m_used_dependencies(m), m_rw(*this) { updt_params(p); m_b_rw.set_flat(false); // no flattening otherwise will blowup the memory m_b_rw.set_elim_and(true); } ~imp() { dec_ref_map_key_values(m, m_const2bit); dec_ref_map_values(m, m_not_const2bit); m_rw.reset(); m_bm.reset(); m_temporary_ints.reset(); } void updt_params(params_ref const & p) { m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); m_all_clauses_limit = p.get_uint("pb2bv_all_clauses_limit", 8); m_cardinality_limit = p.get_uint("pb2bv_cardinality_limit", UINT_MAX); m_b_rw.updt_params(p); } void collect_param_descrs(param_descrs & r) { insert_max_memory(r); r.insert("pb2bv_all_clauses_limit", CPK_UINT, "(default: 8) maximum number of literals for using equivalent CNF encoding of PB constraint."); r.insert("pb2bv_cardinality_limit", CPK_UINT, "(default: inf) limit for using arc-consistent cardinality constraint encoding."); m_b_rw.get_param_descrs(r); r.erase("flat"); r.erase("elim_and"); } void operator()(goal_ref const & g, goal_ref_buffer & result) { TRACE("pb2bv", g->display(tout);); SASSERT(g->is_well_sorted()); fail_if_proof_generation("pb2bv", g); m_produce_models = g->models_enabled(); m_produce_unsat_cores = g->unsat_core_enabled(); result.reset(); tactic_report report("pb2bv", *g); m_bm.reset(); m_rw.reset(); m_new_deps.reset(); if (g->inconsistent()) { result.push_back(g.get()); return; } m_bm(*g); TRACE("pb2bv", m_bm.display(tout);); try { quick_pb_check(g); } catch (non_pb& p) { throw_tactic(p.e); } unsigned size = g->size(); expr_ref_vector new_exprs(m); expr_dependency_ref_vector new_deps(m); try { expr_ref new_curr(m); proof_ref new_pr(m); expr_ref new_f(m); for (unsigned idx = 0; idx < size; idx++) { expr * curr = g->form(idx); expr * atom; bool pos; if (is_constraint(curr, atom, pos)) { convert(to_app(atom), new_f, pos, true); TRACE("pb2bv_convert", tout << "pos: " << pos << "\n" << mk_ismt2_pp(atom, m) << "\n--->\n" << mk_ismt2_pp(new_f, m) << "\n";); } else { m_rw(curr, new_f); } if (m_produce_unsat_cores) { new_deps.push_back(m.mk_join(m_used_dependencies, g->dep(idx))); m_used_dependencies.reset(); } new_exprs.push_back(new_f); } } catch (non_pb& p) { throw_tactic(p.e); } for (unsigned idx = 0; idx < size; idx++) g->update(idx, new_exprs[idx].get(), nullptr, (m_produce_unsat_cores) ? new_deps[idx].get() : g->dep(idx)); if (m_produce_models) { model_converter_ref mc; generic_model_converter * mc1 = alloc(generic_model_converter, m, "pb2bv"); for (auto const& kv : m_const2bit) mc1->hide(kv.m_value); // store temp int constants in the filter unsigned num_temps = m_temporary_ints.size(); for (unsigned i = 0; i < num_temps; i++) mc1->hide(m_temporary_ints.get(i)); pb2bv_model_converter * mc2 = alloc(pb2bv_model_converter, m, m_const2bit, m_bm); mc = concat(mc1, mc2); g->add(mc.get()); } g->inc_depth(); result.push_back(g.get()); TRACE("pb2bv", g->display(tout);); SASSERT(g->is_well_sorted()); } void throw_tactic(expr* e) { std::stringstream strm; strm << "goal is in a fragment unsupported by pb2bv. Offending expression: " << mk_pp(e, m); throw tactic_exception(strm.str()); } }; imp * m_imp; params_ref m_params; public: pb2bv_tactic(ast_manager & m, params_ref const & p): m_params(p) { m_imp = alloc(imp, m, p); } tactic * translate(ast_manager & m) override { return alloc(pb2bv_tactic, m, m_params); } ~pb2bv_tactic() override { dealloc(m_imp); } void updt_params(params_ref const & p) override { m_params = p; m_imp->updt_params(p); } void collect_param_descrs(param_descrs & r) override { m_imp->collect_param_descrs(r); } void operator()(goal_ref const & in, goal_ref_buffer & result) override { (*m_imp)(in, result); } void cleanup() override { ast_manager & m = m_imp->m; imp * d = alloc(imp, m, m_params); std::swap(d, m_imp); dealloc(d); } }; tactic * mk_pb2bv_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(pb2bv_tactic, m, p)); } struct is_pb_probe : public probe { result operator()(goal const & g) override { try { ast_manager & m = g.m(); bound_manager bm(m); bm(g); arith_util a_util(m); expr_fast_mark1 visited; pb2bv_tactic::only_01_visitor proc(a_util, bm); unsigned sz = g.size(); for (unsigned i = 0; i < sz; i++) { expr * f = g.form(i); for_each_expr_core(proc, visited, f); } return true; } catch (const pb2bv_tactic::non_pb &) { return false; } } }; probe * mk_is_pb_probe() { return alloc(is_pb_probe); } z3-z3-4.8.7/src/tactic/arith/pb2bv_tactic.h000066400000000000000000000011771356505360400203150ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: pb2bv_tactic.cpp Abstract: Tactic for converting Pseudo-Boolean constraints to BV Author: Christoph (cwinter) 2012-02-15 Notes: --*/ #ifndef PB2BV_TACTIC_H_ #define PB2BV_TACTIC_H_ #include "util/params.h" class ast_manager; class tactic; tactic * mk_pb2bv_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("pb2bv", "convert pseudo-boolean constraints to bit-vectors.", "mk_pb2bv_tactic(m, p)") */ probe * mk_is_pb_probe(); /* ADD_PROBE("is-pb", "true if the goal is a pseudo-boolean problem.", "mk_is_pb_probe()") */ #endif z3-z3-4.8.7/src/tactic/arith/probe_arith.cpp000066400000000000000000000446011356505360400206030ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: probe_arith.cpp Abstract: Some probes for arithmetic problems. Author: Leonardo de Moura (leonardo) 2012-03-01. Revision History: --*/ #include "tactic/probe.h" #include "ast/expr2polynomial.h" #include "ast/for_each_expr.h" #include "ast/arith_decl_plugin.h" #include "tactic/goal_util.h" class arith_degree_probe : public probe { struct proc { ast_manager & m; unsynch_mpq_manager m_qm; polynomial::manager m_pm; default_expr2polynomial m_expr2poly; arith_util m_util; unsigned m_max_degree; unsigned long long m_acc_degree; unsigned m_counter; proc(ast_manager & _m):m(_m), m_pm(m.limit(), m_qm), m_expr2poly(m, m_pm), m_util(m) { m_max_degree = 0; m_acc_degree = 0; m_counter = 0; } void updt_degree(polynomial_ref const & p) { unsigned deg = m_pm.total_degree(p); if (deg > m_max_degree) m_max_degree = deg; m_acc_degree += deg; m_counter++; } void process(app * n) { expr * lhs = n->get_arg(0); expr * rhs = n->get_arg(1); polynomial_ref p1(m_pm); polynomial_ref p2(m_pm); scoped_mpz d1(m_qm); scoped_mpz d2(m_qm); m_expr2poly.to_polynomial(lhs, p1, d1); m_expr2poly.to_polynomial(rhs, p2, d2); updt_degree(p1); updt_degree(p2); } void operator()(var * n) {} void operator()(quantifier * n) {} void operator()(app * n) { if (m_util.is_le(n) || m_util.is_lt(n) || m_util.is_gt(n) || m_util.is_ge(n)) process(n); if (m.is_eq(n) && m_util.is_int_real(n->get_arg(0))) process(n); } }; bool m_avg; public: arith_degree_probe(bool avg):m_avg(avg) {} result operator()(goal const & g) override { proc p(g.m()); for_each_expr_at(p, g); if (m_avg) return p.m_counter == 0 ? 0.0 : static_cast(p.m_acc_degree)/static_cast(p.m_counter); else return p.m_max_degree; } }; class arith_bw_probe : public probe { struct proc { ast_manager & m; arith_util m_util; unsigned m_max_bw; unsigned long long m_acc_bw; unsigned m_counter; proc(ast_manager & _m):m(_m), m_util(m) { m_max_bw = 0; m_acc_bw = 0; m_counter = 0; } void operator()(var * n) {} void operator()(quantifier * n) {} void operator()(app * n) { rational val; if (m_util.is_numeral(n, val)) { unsigned bw = val.bitsize(); if (bw > m_max_bw) m_max_bw = bw; m_acc_bw += bw; m_counter++; } } }; bool m_avg; public: arith_bw_probe(bool avg):m_avg(avg) {} result operator()(goal const & g) override { proc p(g.m()); for_each_expr_at(p, g); if (m_avg) return p.m_counter == 0 ? 0.0 : static_cast(p.m_acc_bw)/static_cast(p.m_counter); else return p.m_max_bw; } }; struct has_nlmul { struct found {}; ast_manager& m; arith_util a; has_nlmul(ast_manager& m):m(m), a(m) {} void throw_found(expr* e) { TRACE("probe", tout << expr_ref(e, m) << ": " << sort_ref(m.get_sort(e), m) << "\n";); throw found(); } void operator()(var *) { } void operator()(quantifier *) { } void operator()(app * n) { family_id fid = n->get_family_id(); if (fid == a.get_family_id()) { switch (n->get_decl_kind()) { case OP_MUL: if (n->get_num_args() != 2 || !a.is_numeral(n->get_arg(0))) throw_found(n); break; case OP_IDIV: case OP_DIV: case OP_REM: case OP_MOD: if (!a.is_numeral(n->get_arg(1))) throw_found(n); break; case OP_POWER: throw_found(n); default: break; } } } }; probe * mk_arith_avg_degree_probe() { return alloc(arith_degree_probe, true); } probe * mk_arith_max_degree_probe() { return alloc(arith_degree_probe, false); } probe * mk_arith_avg_bw_probe() { return alloc(arith_bw_probe, true); } probe * mk_arith_max_bw_probe() { return alloc(arith_bw_probe, false); } struct is_non_qflira_functor { struct found {}; ast_manager & m; arith_util u; bool m_int; bool m_real; is_non_qflira_functor(ast_manager & _m, bool _int, bool _real):m(_m), u(m), m_int(_int), m_real(_real) {} void operator()(var *) { throw found(); } void operator()(quantifier *) { throw found(); } bool compatible_sort(app * n) const { if (m.is_bool(n)) return true; if (m_int && u.is_int(n)) return true; if (m_real && u.is_real(n)) return true; return false; } void operator()(app * n) { if (!compatible_sort(n)) throw found(); family_id fid = n->get_family_id(); if (fid == m.get_basic_family_id()) return; if (fid == u.get_family_id()) { switch (n->get_decl_kind()) { case OP_LE: case OP_GE: case OP_LT: case OP_GT: case OP_ADD: case OP_NUM: return; case OP_MUL: if (n->get_num_args() != 2) throw found(); if (!u.is_numeral(n->get_arg(0))) throw found(); return; case OP_TO_REAL: if (!m_real) throw found(); break; default: throw found(); } return; } if (is_uninterp_const(n)) return; throw found(); } }; struct is_non_qfauflira_functor { struct found {}; ast_manager & m; arith_util m_arith_util; array_util m_array_util; bool m_int; bool m_real; is_non_qfauflira_functor(ast_manager & _m, bool _int, bool _real) : m(_m), m_arith_util(_m), m_array_util(_m), m_int(_int), m_real(_real) {} void operator()(var *) { throw found(); } void operator()(quantifier *) { throw found(); } bool compatible_sort(app * n) const { if (m.is_bool(n)) return true; if (m_int && m_arith_util.is_int(n)) return true; if (m_real && m_arith_util.is_real(n)) return true; if (m_array_util.is_array(n)) return true; return false; } void operator()(app * n) { if (!compatible_sort(n)) throw found(); family_id fid = n->get_family_id(); if (fid == m.get_basic_family_id()) return; if (fid == m_arith_util.get_family_id()) { switch (n->get_decl_kind()) { case OP_LE: case OP_GE: case OP_LT: case OP_GT: case OP_ADD: case OP_NUM: return; case OP_MUL: if (n->get_num_args() != 2) throw found(); if (!m_arith_util.is_numeral(n->get_arg(0))) throw found(); return; case OP_TO_REAL: if (!m_real) throw found(); break; default: throw found(); } return; } if (is_uninterp(n)) return; throw found(); } }; static bool is_qflia(goal const & g) { is_non_qflira_functor p(g.m(), true, false); return !test(g, p); } static bool is_qfauflia(goal const & g) { is_non_qfauflira_functor p(g.m(), true, false); return !test(g, p); } class is_qflia_probe : public probe { public: result operator()(goal const & g) override { return is_qflia(g); } }; class is_qfauflia_probe : public probe { public: result operator()(goal const & g) override { return is_qfauflia(g); } }; static bool is_qflra(goal const & g) { is_non_qflira_functor p(g.m(), false, true); return !test(g, p); } class is_qflra_probe : public probe { public: result operator()(goal const & g) override { return is_qflra(g); } }; static bool is_qflira(goal const & g) { is_non_qflira_functor p(g.m(), true, true); return !test(g, p); } class is_qflira_probe : public probe { public: result operator()(goal const & g) override { return is_qflira(g); } }; static bool is_lp(goal const & g) { ast_manager & m = g.m(); arith_util u(m); unsigned sz = g.size(); for (unsigned i = 0; i < sz; i++) { expr * f = g.form(i); bool sign = false; while (m.is_not(f, f)) sign = !sign; if (m.is_eq(f) && !sign) { if (m.get_sort(to_app(f)->get_arg(0))->get_family_id() != u.get_family_id()) return false; continue; } if (u.is_le(f) || u.is_ge(f) || u.is_lt(f) || u.is_gt(f)) continue; return false; } return true; } static bool is_ilp(goal const & g) { if (!is_qflia(g)) return false; if (has_term_ite(g)) return false; return is_lp(g); } static bool is_mip(goal const & g) { if (!is_qflira(g)) return false; if (has_term_ite(g)) return false; return is_lp(g); } class is_ilp_probe : public probe { public: result operator()(goal const & g) override { return is_ilp(g); } }; class is_mip_probe : public probe { public: result operator()(goal const & g) override { return is_mip(g); } }; probe * mk_is_qflia_probe() { return alloc(is_qflia_probe); } probe * mk_is_qfauflia_probe() { return alloc(is_qfauflia_probe); } probe * mk_is_qflra_probe() { return alloc(is_qflra_probe); } probe * mk_is_qflira_probe() { return alloc(is_qflira_probe); } probe * mk_is_ilp_probe() { return alloc(is_ilp_probe); } probe * mk_is_mip_probe() { return alloc(is_mip_probe); } struct is_non_nira_functor { struct found {}; ast_manager & m; arith_util u; bool m_int; bool m_real; bool m_quant; bool m_linear; is_non_nira_functor(ast_manager & _m, bool _int, bool _real, bool _quant, bool linear):m(_m), u(m), m_int(_int), m_real(_real), m_quant(_quant), m_linear(linear) {} void throw_found(expr* e) { TRACE("probe", tout << expr_ref(e, m) << ": " << sort_ref(m.get_sort(e), m) << "\n";); throw found(); } void operator()(var * x) { if (!m_quant) throw_found(x); sort * s = x->get_sort(); if (m_int && u.is_int(s)) return; if (m_real && u.is_real(s)) return; if (m.is_bool(s)) return; throw_found(x); } void operator()(quantifier * q) { if (!m_quant) throw_found(q); } bool compatible_sort(app * n) const { if (m.is_bool(n)) return true; if (m_int && u.is_int(n)) return true; if (m_real && u.is_real(n)) return true; return false; } void operator()(app * n) { if (!compatible_sort(n)) throw_found(n); family_id fid = n->get_family_id(); rational r; if (fid == m.get_basic_family_id()) return; if (fid == u.get_family_id()) { switch (n->get_decl_kind()) { case OP_LE: case OP_GE: case OP_LT: case OP_GT: case OP_ADD: case OP_UMINUS: case OP_SUB: case OP_ABS: case OP_NUM: return; case OP_MUL: if (m_linear) { if (n->get_num_args() != 2) throw_found(n); if (!u.is_numeral(n->get_arg(0)) && !u.is_numeral(n->get_arg(1))) throw_found(n); } return; case OP_IDIV: case OP_DIV: case OP_REM: case OP_MOD: if (m_linear && !u.is_numeral(n->get_arg(1))) throw_found(n); if (m_linear && u.is_numeral(n->get_arg(1), r) && r.is_zero()) throw_found(n); if (!is_ground(n->get_arg(0)) || !is_ground(n->get_arg(1))) throw_found(n); return; case OP_IS_INT: if (m_real) throw_found(n); return; case OP_TO_INT: case OP_TO_REAL: return; case OP_POWER: if (m_linear) throw_found(n); return; case OP_IRRATIONAL_ALGEBRAIC_NUM: if (m_linear || !m_real) throw_found(n); return; default: throw_found(n); } return; } if (is_uninterp_const(n)) return; throw_found(n); } }; static bool is_qfnia(goal const & g) { is_non_nira_functor p(g.m(), true, false, false, false); return !test(g, p) && test(g); } static bool is_qfnra(goal const & g) { is_non_nira_functor p(g.m(), false, true, false, false); return !test(g, p) && test(g); } static bool is_nia(goal const & g) { is_non_nira_functor p(g.m(), true, false, true, false); return !test(g, p) && test(g); } static bool is_nra(goal const & g) { is_non_nira_functor p(g.m(), false, true, true, false); return !test(g, p) && test(g); } static bool is_nira(goal const & g) { is_non_nira_functor p(g.m(), true, true, true, false); return !test(g, p) && test(g); } static bool is_lra(goal const & g) { is_non_nira_functor p(g.m(), false, true, true, true); return !test(g, p); } static bool is_lia(goal const & g) { is_non_nira_functor p(g.m(), true, false, true, true); return !test(g, p); } static bool is_lira(goal const & g) { is_non_nira_functor p(g.m(), true, true, true, true); return !test(g, p); } struct is_non_qfufnra_functor { struct found {}; ast_manager & m; arith_util u; bool m_has_nonlinear; is_non_qfufnra_functor(ast_manager & _m): m(_m), u(m), m_has_nonlinear(false) {} void throw_found() { throw found(); } bool has_nonlinear() const { return m_has_nonlinear; } void operator()(var * x) { throw_found(); } void operator()(quantifier *) { throw_found(); } void operator()(app * n) { family_id fid = n->get_family_id(); if (fid == m.get_basic_family_id()) return; if (fid == u.get_family_id()) { switch (n->get_decl_kind()) { case OP_LE: case OP_GE: case OP_LT: case OP_GT: case OP_ADD: case OP_UMINUS: case OP_SUB: case OP_ABS: case OP_NUM: case OP_IRRATIONAL_ALGEBRAIC_NUM: return; case OP_MUL: if (n->get_num_args() == 2 && u.is_real(n->get_arg(0)) && !u.is_numeral(n->get_arg(0)) && !u.is_numeral(n->get_arg(1))) { m_has_nonlinear = true; } return; case OP_IDIV: case OP_DIV: case OP_REM: case OP_MOD: if (!u.is_numeral(n->get_arg(1))) { TRACE("arith", tout << "non-linear " << expr_ref(n, m) << "\n";); throw_found(); } return; case OP_POWER: if (!u.is_numeral(n->get_arg(1))) { TRACE("arith", tout << "non-linear " << expr_ref(n, m) << "\n";); throw_found(); } m_has_nonlinear = true; return; case OP_IS_INT: case OP_TO_INT: case OP_TO_REAL: throw_found(); return; default: TRACE("arith", tout << "non-linear " << expr_ref(n, m) << "\n";); throw_found(); } } } }; class is_qfnia_probe : public probe { public: result operator()(goal const & g) override { return is_qfnia(g); } }; class is_qfnra_probe : public probe { public: result operator()(goal const & g) override { return is_qfnra(g); } }; class is_nia_probe : public probe { public: result operator()(goal const & g) override { return is_nia(g); } }; class is_nra_probe : public probe { public: result operator()(goal const & g) override { return is_nra(g); } }; class is_nira_probe : public probe { public: result operator()(goal const & g) override { return is_nira(g); } }; class is_lia_probe : public probe { public: result operator()(goal const & g) override { return is_lia(g); } }; class is_lra_probe : public probe { public: result operator()(goal const & g) override { return is_lra(g); } }; class is_lira_probe : public probe { public: result operator()(goal const & g) override { return is_lira(g); } }; static bool is_qfufnra(goal const& g) { is_non_qfufnra_functor p(g.m()); return !g.proofs_enabled() && !g.unsat_core_enabled() && !test(g, p) && p.has_nonlinear(); } class is_qfufnra_probe : public probe { public: result operator()(goal const & g) override { return is_qfufnra(g); } }; probe * mk_is_qfnia_probe() { return alloc(is_qfnia_probe); } probe * mk_is_qfnra_probe() { return alloc(is_qfnra_probe); } probe * mk_is_nia_probe() { return alloc(is_nia_probe); } probe * mk_is_nra_probe() { return alloc(is_nra_probe); } probe * mk_is_nira_probe() { return alloc(is_nira_probe); } probe * mk_is_lia_probe() { return alloc(is_lia_probe); } probe * mk_is_lra_probe() { return alloc(is_lra_probe); } probe * mk_is_lira_probe() { return alloc(is_lira_probe); } probe* mk_is_qfufnra_probe() { return alloc(is_qfufnra_probe); } z3-z3-4.8.7/src/tactic/arith/probe_arith.h000066400000000000000000000055161356505360400202520ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: probe_arith.h Abstract: Some probes for arithmetic problems. Author: Leonardo de Moura (leonardo) 2012-03-01. Revision History: --*/ #ifndef PROBE_ARITH_H_ #define PROBE_ARITH_H_ class probe; probe * mk_arith_avg_bw_probe(); probe * mk_arith_max_bw_probe(); probe * mk_arith_avg_degree_probe(); probe * mk_arith_max_degree_probe(); /* ADD_PROBE("arith-max-deg", "max polynomial total degree of an arithmetic atom.", "mk_arith_max_degree_probe()") ADD_PROBE("arith-avg-deg", "avg polynomial total degree of an arithmetic atom.", "mk_arith_avg_degree_probe()") ADD_PROBE("arith-max-bw", "max coefficient bit width.", "mk_arith_max_bw_probe()") ADD_PROBE("arith-avg-bw", "avg coefficient bit width.", "mk_arith_avg_bw_probe()") */ probe * mk_is_qflia_probe(); probe * mk_is_qfauflia_probe(); probe * mk_is_qflra_probe(); probe * mk_is_qflira_probe(); probe * mk_is_ilp_probe(); probe * mk_is_mip_probe(); /* ADD_PROBE("is-qflia", "true if the goal is in QF_LIA.", "mk_is_qflia_probe()") ADD_PROBE("is-qfauflia", "true if the goal is in QF_AUFLIA.", "mk_is_qfauflia_probe()") ADD_PROBE("is-qflra", "true if the goal is in QF_LRA.", "mk_is_qflra_probe()") ADD_PROBE("is-qflira", "true if the goal is in QF_LIRA.", "mk_is_qflira_probe()") ADD_PROBE("is-ilp", "true if the goal is ILP.", "mk_is_ilp_probe()") */ probe * mk_is_qfnia_probe(); probe * mk_is_qfnra_probe(); probe * mk_is_nia_probe(); probe * mk_is_nra_probe(); probe * mk_is_nira_probe(); probe * mk_is_lia_probe(); probe * mk_is_lra_probe(); probe * mk_is_lira_probe(); probe * mk_is_qfufnra_probe(); /* ADD_PROBE("is-qfnia", "true if the goal is in QF_NIA (quantifier-free nonlinear integer arithmetic).", "mk_is_qfnia_probe()") ADD_PROBE("is-qfnra", "true if the goal is in QF_NRA (quantifier-free nonlinear real arithmetic).", "mk_is_qfnra_probe()") ADD_PROBE("is-nia", "true if the goal is in NIA (nonlinear integer arithmetic, formula may have quantifiers).", "mk_is_nia_probe()") ADD_PROBE("is-nra", "true if the goal is in NRA (nonlinear real arithmetic, formula may have quantifiers).", "mk_is_nra_probe()") ADD_PROBE("is-nira", "true if the goal is in NIRA (nonlinear integer and real arithmetic, formula may have quantifiers).", "mk_is_nira_probe()") ADD_PROBE("is-lia", "true if the goal is in LIA (linear integer arithmetic, formula may have quantifiers).", "mk_is_lia_probe()") ADD_PROBE("is-lra", "true if the goal is in LRA (linear real arithmetic, formula may have quantifiers).", "mk_is_lra_probe()") ADD_PROBE("is-lira", "true if the goal is in LIRA (linear integer and real arithmetic, formula may have quantifiers).", "mk_is_lira_probe()") ADD_PROBE("is-qfufnra", "true if the goal is QF_UFNRA (quantifier-free nonlinear real arithmetic with other theories).", "mk_is_qfufnra_probe()") */ #endif z3-z3-4.8.7/src/tactic/arith/propagate_ineqs_tactic.cpp000066400000000000000000000413611356505360400230150ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: propagate_ineqs_tactic.h Abstract: This tactic performs the following tasks: - Propagate bounds using the bound_propagator. - Eliminate subsumed inequalities. For example: x - y >= 3 can be replaced with true if we know that x >= 3 and y <= 0 - Convert inequalities of the form p <= k and p >= k into p = k, where p is a polynomial and k is a constant. This strategy assumes the input is in arith LHS mode. This can be achieved by using option :arith-lhs true in the simplifier. Author: Leonardo (leonardo) 2012-02-19 Notes: --*/ #include "tactic/tactical.h" #include "tactic/arith/bound_propagator.h" #include "ast/arith_decl_plugin.h" #include "tactic/core/simplify_tactic.h" #include "ast/ast_smt2_pp.h" class propagate_ineqs_tactic : public tactic { struct imp; imp * m_imp; params_ref m_params; public: propagate_ineqs_tactic(ast_manager & m, params_ref const & p); tactic * translate(ast_manager & m) override { return alloc(propagate_ineqs_tactic, m, m_params); } ~propagate_ineqs_tactic() override; void updt_params(params_ref const & p) override; void collect_param_descrs(param_descrs & r) override {} void operator()(goal_ref const & g, goal_ref_buffer & result) override; void cleanup() override; }; tactic * mk_propagate_ineqs_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(propagate_ineqs_tactic, m, p)); } struct propagate_ineqs_tactic::imp { ast_manager & m; unsynch_mpq_manager nm; small_object_allocator m_allocator; bound_propagator bp; arith_util m_util; typedef bound_propagator::var a_var; obj_map m_expr2var; expr_ref_vector m_var2expr; typedef numeral_buffer mpq_buffer; typedef svector var_buffer; mpq_buffer m_num_buffer; var_buffer m_var_buffer; goal_ref m_new_goal; imp(ast_manager & _m, params_ref const & p): m(_m), m_allocator("ineq-simplifier"), bp(nm, m_allocator, p), m_util(m), m_var2expr(m), m_num_buffer(nm) { updt_params_core(p); } void updt_params_core(params_ref const & p) { } void updt_params(params_ref const & p) { updt_params_core(p); bp.updt_params(p); } void display_bounds(std::ostream & out) { unsigned sz = m_var2expr.size(); mpq k; bool strict; unsigned ts; for (unsigned x = 0; x < sz; x++) { if (bp.lower(x, k, strict, ts)) out << nm.to_string(k) << " " << (strict ? "<" : "<="); else out << "-oo <"; out << " " << mk_ismt2_pp(m_var2expr.get(x), m) << " "; if (bp.upper(x, k, strict, ts)) out << (strict ? "<" : "<=") << " " << nm.to_string(k); else out << "< oo"; out << "\n"; } nm.del(k); } a_var mk_var(expr * t) { if (m_util.is_to_real(t)) t = to_app(t)->get_arg(0); a_var x; if (m_expr2var.find(t, x)) return x; x = m_var2expr.size(); bp.mk_var(x, m_util.is_int(t)); m_var2expr.push_back(t); m_expr2var.insert(t, x); return x; } void expr2linear_pol(expr * t, mpq_buffer & as, var_buffer & xs) { mpq c_mpq_val; if (m_util.is_add(t)) { rational c_val; unsigned num = to_app(t)->get_num_args(); for (unsigned i = 0; i < num; i++) { expr * mon = to_app(t)->get_arg(i); expr * c, * x; if (m_util.is_mul(mon, c, x) && m_util.is_numeral(c, c_val)) { nm.set(c_mpq_val, c_val.to_mpq()); as.push_back(c_mpq_val); xs.push_back(mk_var(x)); } else { as.push_back(mpq(1)); xs.push_back(mk_var(mon)); } } } else { as.push_back(mpq(1)); xs.push_back(mk_var(t)); } nm.del(c_mpq_val); } a_var mk_linear_pol(expr * t) { a_var x; if (m_expr2var.find(t, x)) return x; x = mk_var(t); if (m_util.is_add(t)) { m_num_buffer.reset(); m_var_buffer.reset(); expr2linear_pol(t, m_num_buffer, m_var_buffer); m_num_buffer.push_back(mpq(-1)); m_var_buffer.push_back(x); bp.mk_eq(m_num_buffer.size(), m_num_buffer.c_ptr(), m_var_buffer.c_ptr()); } return x; } enum kind { EQ, LE, GE }; bool process(expr * t) { bool sign = false; while (m.is_not(t, t)) sign = !sign; bool strict = false; kind k; if (m.is_eq(t)) { if (sign) return false; k = EQ; } else if (m_util.is_le(t)) { if (sign) { k = GE; strict = true; } else { k = LE; } } else if (m_util.is_ge(t)) { if (sign) { k = LE; strict = true; } else { k = GE; } } else { return false; } expr * lhs = to_app(t)->get_arg(0); expr * rhs = to_app(t)->get_arg(1); if (m_util.is_numeral(lhs)) { std::swap(lhs, rhs); if (k == LE) k = GE; else if (k == GE) k = LE; } rational c; if (!m_util.is_numeral(rhs, c)) return false; a_var x = mk_linear_pol(lhs); mpq c_prime; nm.set(c_prime, c.to_mpq()); if (k == EQ) { SASSERT(!strict); bp.assert_lower(x, c_prime, false); bp.assert_upper(x, c_prime, false); } else if (k == LE) { bp.assert_upper(x, c_prime, strict); } else { SASSERT(k == GE); bp.assert_lower(x, c_prime, strict); } nm.del(c_prime); return true; } bool collect_bounds(goal const & g) { bool found = false; unsigned sz = g.size(); for (unsigned i = 0; i < sz; i++) { expr * t = g.form(i); if (process(t)) found = true; else m_new_goal->assert_expr(t); // save non-bounds here } return found; } bool lower_subsumed(expr * p, mpq const & k, bool strict) { if (!m_util.is_add(p)) return false; m_num_buffer.reset(); m_var_buffer.reset(); expr2linear_pol(p, m_num_buffer, m_var_buffer); mpq implied_k; bool implied_strict; bool result = bp.lower(m_var_buffer.size(), m_num_buffer.c_ptr(), m_var_buffer.c_ptr(), implied_k, implied_strict) && (nm.gt(implied_k, k) || (nm.eq(implied_k, k) && (!strict || implied_strict))); nm.del(implied_k); return result; } bool upper_subsumed(expr * p, mpq const & k, bool strict) { if (!m_util.is_add(p)) return false; m_num_buffer.reset(); m_var_buffer.reset(); expr2linear_pol(p, m_num_buffer, m_var_buffer); mpq implied_k; bool implied_strict; bool result = bp.upper(m_var_buffer.size(), m_num_buffer.c_ptr(), m_var_buffer.c_ptr(), implied_k, implied_strict) && (nm.lt(implied_k, k) || (nm.eq(implied_k, k) && (!strict || implied_strict))); nm.del(implied_k); return result; } void restore_bounds() { mpq l, u; bool strict_l, strict_u, has_l, has_u; unsigned ts; unsigned sz = m_var2expr.size(); for (unsigned x = 0; x < sz; x++) { expr * p = m_var2expr.get(x); has_l = bp.lower(x, l, strict_l, ts); has_u = bp.upper(x, u, strict_u, ts); if (!has_l && !has_u) continue; if (has_l && has_u && nm.eq(l, u) && !strict_l && !strict_u) { // l <= p <= l --> p = l m_new_goal->assert_expr(m.mk_eq(p, m_util.mk_numeral(rational(l), m_util.is_int(p)))); continue; } if (has_l && !lower_subsumed(p, l, strict_l)) { if (strict_l) m_new_goal->assert_expr(m.mk_not(m_util.mk_le(p, m_util.mk_numeral(rational(l), m_util.is_int(p))))); else m_new_goal->assert_expr(m_util.mk_ge(p, m_util.mk_numeral(rational(l), m_util.is_int(p)))); } if (has_u && !upper_subsumed(p, u, strict_u)) { if (strict_u) m_new_goal->assert_expr(m.mk_not(m_util.mk_ge(p, m_util.mk_numeral(rational(u), m_util.is_int(p))))); else m_new_goal->assert_expr(m_util.mk_le(p, m_util.mk_numeral(rational(u), m_util.is_int(p)))); } } nm.del(l); nm.del(u); } bool is_x_minus_y_eq_0(expr * t, expr * & x, expr * & y) { expr * lhs, * rhs, * m1, * m2; if (m.is_eq(t, lhs, rhs) && m_util.is_zero(rhs) && m_util.is_add(lhs, m1, m2)) { if (m_util.is_times_minus_one(m2, y) && is_uninterp_const(m1)) { x = m1; return true; } if (m_util.is_times_minus_one(m1, y) && is_uninterp_const(m2)) { x = m2; return true; } } return false; } bool is_unbounded(expr * t) { a_var x; if (m_expr2var.find(t, x)) return !bp.has_lower(x) && !bp.has_upper(x); return true; } bool lower(expr * t, mpq & k, bool & strict) { unsigned ts; a_var x; if (m_expr2var.find(t, x)) return bp.lower(x, k, strict, ts); return false; } bool upper(expr * t, mpq & k, bool & strict) { unsigned ts; a_var x; if (m_expr2var.find(t, x)) return bp.upper(x, k, strict, ts); return false; } void find_ite_bounds(expr * root) { TRACE("find_ite_bounds_bug", display_bounds(tout);); expr * n = root; expr * target = nullptr; expr * c, * t, * e; expr * x, * y; bool has_l, has_u; mpq l_min, u_max; bool l_strict, u_strict; mpq curr; bool curr_strict; while (true) { TRACE("find_ite_bounds_bug", tout << mk_ismt2_pp(n, m) << "\n";); if (m.is_ite(n, c, t, e)) { if (is_x_minus_y_eq_0(t, x, y)) n = e; else if (is_x_minus_y_eq_0(e, x, y)) n = t; else break; } else if (is_x_minus_y_eq_0(n, x, y)) { n = nullptr; } else { break; } TRACE("find_ite_bounds_bug", tout << "x: " << mk_ismt2_pp(x, m) << ", y: " << mk_ismt2_pp(y, m) << "\n"; if (target) { tout << "target: " << mk_ismt2_pp(target, m) << "\n"; tout << "has_l: " << has_l << " " << nm.to_string(l_min) << " has_u: " << has_u << " " << nm.to_string(u_max) << "\n"; }); if (is_unbounded(y)) std::swap(x, y); if (!is_unbounded(x)) { TRACE("find_ite_bounds_bug", tout << "x is already bounded\n";); break; } if (target == nullptr) { target = x; if (lower(y, curr, curr_strict)) { has_l = true; nm.set(l_min, curr); l_strict = curr_strict; } else { has_l = false; TRACE("find_ite_bounds_bug", tout << "y does not have lower\n";); } if (upper(y, curr, curr_strict)) { has_u = true; nm.set(u_max, curr); u_strict = curr_strict; } else { has_u = false; TRACE("find_ite_bounds_bug", tout << "y does not have upper\n";); } } else if (target == x) { if (has_l) { if (lower(y, curr, curr_strict)) { if (nm.lt(curr, l_min) || (!curr_strict && l_strict && nm.eq(curr, l_min))) { nm.set(l_min, curr); l_strict = curr_strict; } } else { has_l = false; TRACE("find_ite_bounds_bug", tout << "y does not have lower\n";); } } if (has_u) { if (upper(y, curr, curr_strict)) { if (nm.gt(curr, u_max) || (curr_strict && !u_strict && nm.eq(curr, u_max))) { nm.set(u_max, curr); u_strict = curr_strict; } } else { has_u = false; TRACE("find_ite_bounds_bug", tout << "y does not have upper\n";); } } } else { break; } if (!has_l && !has_u) break; if (n == nullptr) { TRACE("find_ite_bounds", tout << "found bounds for: " << mk_ismt2_pp(target, m) << "\n"; tout << "has_l: " << has_l << " " << nm.to_string(l_min) << " l_strict: " << l_strict << "\n"; tout << "has_u: " << has_u << " " << nm.to_string(u_max) << " u_strict: " << u_strict << "\n"; tout << "root:\n" << mk_ismt2_pp(root, m) << "\n";); a_var x = mk_var(target); if (has_l) bp.assert_lower(x, l_min, l_strict); if (has_u) bp.assert_upper(x, u_max, u_strict); break; } } nm.del(l_min); nm.del(u_max); nm.del(curr); } void find_ite_bounds() { unsigned sz = m_new_goal->size(); for (unsigned i = 0; i < sz; i++) { expr * f = m_new_goal->form(i); if (m.is_ite(f)) find_ite_bounds(to_app(f)); } bp.propagate(); TRACE("find_ite_bounds", display_bounds(tout);); } void operator()(goal * g, goal_ref & r) { tactic_report report("propagate-ineqs", *g); m_new_goal = alloc(goal, *g, true); m_new_goal->inc_depth(); r = m_new_goal.get(); if (!collect_bounds(*g)) { m_new_goal = nullptr; r = g; return; // nothing to be done } TRACE("propagate_ineqs_tactic", g->display(tout); display_bounds(tout); tout << "bound propagator:\n"; bp.display(tout);); bp.propagate(); report_tactic_progress(":bound-propagations", bp.get_num_propagations()); report_tactic_progress(":bound-false-alarms", bp.get_num_false_alarms()); if (bp.inconsistent()) { r->reset(); r->assert_expr(m.mk_false()); return; } // find_ite_bounds(); // did not help restore_bounds(); TRACE("propagate_ineqs_tactic", tout << "after propagation:\n"; display_bounds(tout); bp.display(tout);); TRACE("propagate_ineqs_tactic", r->display(tout);); } }; propagate_ineqs_tactic::propagate_ineqs_tactic(ast_manager & m, params_ref const & p): m_params(p) { m_imp = alloc(imp, m, p); } propagate_ineqs_tactic::~propagate_ineqs_tactic() { dealloc(m_imp); } void propagate_ineqs_tactic::updt_params(params_ref const & p) { m_params = p; m_imp->updt_params(p); } void propagate_ineqs_tactic::operator()(goal_ref const & g, goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); fail_if_proof_generation("propagate-ineqs", g); fail_if_unsat_core_generation("propagate-ineqs", g); result.reset(); goal_ref r; (*m_imp)(g.get(), r); result.push_back(r.get()); SASSERT(r->is_well_sorted()); } void propagate_ineqs_tactic::cleanup() { imp * d = alloc(imp, m_imp->m, m_params); std::swap(d, m_imp); dealloc(d); } z3-z3-4.8.7/src/tactic/arith/propagate_ineqs_tactic.h000066400000000000000000000020421356505360400224530ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: propagate_ineqs_tactic.h Abstract: This tactic performs the following tasks: - Propagate bounds using the bound_propagator. - Eliminate subsumed inequalities. For example: x - y >= 3 can be replaced with true if we know that x >= 3 and y <= 0 - Convert inequalities of the form p <= k and p >= k into p = k, where p is a polynomial and k is a constant. This strategy assumes the input is in arith LHS mode. This can be achieved by using option :arith-lhs true in the simplifier. Author: Leonardo (leonardo) 2012-02-19 Notes: --*/ #ifndef PROPAGATE_INEQS_TACTIC_H_ #define PROPAGATE_INEQS_TACTIC_H_ #include "util/params.h" class ast_manager; class tactic; tactic * mk_propagate_ineqs_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("propagate-ineqs", "propagate ineqs/bounds, remove subsumed inequalities.", "mk_propagate_ineqs_tactic(m, p)") */ #endif z3-z3-4.8.7/src/tactic/arith/purify_arith_tactic.cpp000066400000000000000000001035641356505360400223450ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: purify_arith_tactic.h Abstract: Tactic for eliminating arithmetic operators: DIV, IDIV, MOD, TO_INT, and optionally (OP_IRRATIONAL_ALGEBRAIC_NUM). This tactic uses the simplifier for also eliminating: OP_SUB, OP_UMINUS, OP_POWER (optionally), OP_REM, OP_IS_INT. Author: Leonardo de Moura (leonardo) 2011-12-30. Revision History: --*/ #include "tactic/tactical.h" #include "ast/rewriter/rewriter_def.h" #include "ast/arith_decl_plugin.h" #include "math/polynomial/algebraic_numbers.h" #include "tactic/core/nnf_tactic.h" #include "tactic/core/simplify_tactic.h" #include "ast/rewriter/th_rewriter.h" #include "tactic/generic_model_converter.h" #include "ast/ast_smt2_pp.h" #include "ast/rewriter/expr_replacer.h" /* ---- Some of the rules needed in the conversion are implemented in arith_rewriter.cpp. Here is a summary of these rules: (^ t (/ p q)) --> (^ (^ t (/ 1 q)) p) (^ t n) --> t*...*t when integer power expansion is requested (is-int t) --> t = (to-real (to-int t)) (rem t1 t2) --> ite(t2 >= 0, (mod t1 t2), -(mod t1 t2)) ---- The tactic implements a set of transformation rules. These rules create fresh constants or (existential) variables, and add new constraints to the context. The context is the set of asserted formulas or a quantifier. A rule is represented as: From --> To | C It means, any expression that matches From is replaced by To, and the constraints C are added to the context. For clarity reasons, I write the constraints using ad-hoc notation. Rules (^ t 0) --> k | t != 0 implies k = 1, t = 0 implies k = 0^0 where k is fresh 0^0 is a constant used to capture the meaning of (^ 0 0). (^ t (/ 1 n)) --> k | t = k^n when n is odd where k is fresh (^ t (/ 1 n)) --> k | t >= 0 implies t = k^n, t < 0 implies t = neg-root(t, n) when n is even where k is fresh neg-root is a function symbol used to capture the meaning of a negative root (root-obj p(x) i) --> k | p(k) = 0, l < k < u when root object elimination is requested where k is fresh (l, u) is an isolating interval for the i-th root of p. (to-int t) --> k | 0 <= to-real(k) - t < 1 where k is a fresh integer constant/variable (/ t1 t2) --> k | t2 != 0 implies k*t2 = t1, t2 = 0 implies k = div-0(t1) where k is fresh div-0 is a function symbol used to capture the meaning of division by 0. Remark: If it can be shown that t2 != 0, then the div-0(t1) function application vanishes from the formula. (div t1 t2) --> k1 | t2 = 0 \/ t1 = k1 * t2 + k2, t2 = 0 \/ 0 <= k2, t2 = 0 \/ k2 < |t2|, t2 != 0 \/ k1 = idiv-0(t1), t2 != 0 \/ k2 = mod-0(t1) k1 is a fresh name for (div t1 t2) k2 is a fresh name for (mod t1 t2) (mod t1 t2) --> k2 | same constraints as above */ struct purify_arith_proc { arith_util & m_util; goal & m_goal; bool m_produce_proofs; bool m_elim_root_objs; bool m_elim_inverses; bool m_complete; ast_mark m_unsafe_exprs; bool m_unsafe_found; obj_map > m_sin_cos; expr_ref_vector m_pinned; purify_arith_proc(goal & g, arith_util & u, bool produce_proofs, bool elim_root_objs, bool elim_inverses, bool complete): m_util(u), m_goal(g), m_produce_proofs(produce_proofs), m_elim_root_objs(elim_root_objs), m_elim_inverses(elim_inverses), m_complete(complete), m_unsafe_found(false), m_pinned(m()) { } arith_util & u() { return m_util; } ast_manager & m() { return u().get_manager(); } struct find_unsafe_proc { purify_arith_proc& m_owner; find_unsafe_proc(purify_arith_proc& o) : m_owner(o) {} void operator()(app* a) { if (!m_owner.u().is_sin(a) && !m_owner.u().is_cos(a)) { for (unsigned i = 0; i < a->get_num_args(); ++i) { m_owner.m_unsafe_exprs.mark(a->get_arg(i), true); } } } void operator()(quantifier *q) {} void operator()(var* v) {} }; void find_unsafe() { if (m_unsafe_found) return; find_unsafe_proc proc(*this); expr_fast_mark1 visited; unsigned sz = m_goal.size(); for (unsigned i = 0; i < sz; i++) { expr * curr = m_goal.form(i); for_each_expr_core(proc, visited, curr); } m_unsafe_found = true; } bool convert_basis(expr* theta, expr*& x, expr*& y) { if (!is_uninterp_const(theta)) { return false; } find_unsafe(); if (m_unsafe_exprs.is_marked(theta)) { return false; } std::pair pair; if (!m_sin_cos.find(to_app(theta), pair)) { pair.first = m().mk_fresh_const(nullptr, u().mk_real()); pair.second = m().mk_fresh_const(nullptr, u().mk_real()); m_sin_cos.insert(to_app(theta), pair); m_pinned.push_back(pair.first); m_pinned.push_back(pair.second); // TBD for model conversion } x = pair.first; y = pair.second; return true; } struct div_def { expr* x, *y, *d; div_def(expr* x, expr* y, expr* d): x(x), y(y), d(d) {} }; struct rw_cfg : public default_rewriter_cfg { purify_arith_proc & m_owner; obj_map m_app2fresh; obj_map m_app2pr; expr_ref_vector m_pinned; expr_ref_vector m_new_cnstrs; proof_ref_vector m_new_cnstr_prs; svector m_divs; expr_ref m_subst; proof_ref m_subst_pr; expr_ref_vector m_new_vars; rw_cfg(purify_arith_proc & o): m_owner(o), m_pinned(o.m()), m_new_cnstrs(o.m()), m_new_cnstr_prs(o.m()), m_subst(o.m()), m_subst_pr(o.m()), m_new_vars(o.m()) { } ast_manager & m() { return m_owner.m(); } arith_util & u() { return m_owner.u(); } bool produce_proofs() const { return m_owner.m_produce_proofs; } bool complete() const { return m_owner.m_complete; } bool elim_root_objs() const { return m_owner.m_elim_root_objs; } bool elim_inverses() const { return m_owner.m_elim_inverses; } expr * mk_fresh_var(bool is_int) { expr * r = m().mk_fresh_const(nullptr, is_int ? u().mk_int() : u().mk_real()); m_new_vars.push_back(r); return r; } expr * mk_fresh_real_var() { return mk_fresh_var(false); } expr * mk_fresh_int_var() { return mk_fresh_var(true); } expr * mk_int_zero() { return u().mk_numeral(rational(0), true); } expr * mk_real_zero() { return u().mk_numeral(rational(0), false); } expr * mk_real_one() { return u().mk_numeral(rational(1), false); } bool already_processed(app * t, expr_ref & result, proof_ref & result_pr) { expr * r; if (m_app2fresh.find(t, r)) { result = r; if (produce_proofs()) result_pr = m_app2pr.find(t); return true; } return false; } void mk_def_proof(expr * k, expr * def, proof_ref & result_pr) { result_pr = nullptr; if (produce_proofs()) { expr * eq = m().mk_eq(k, def); proof * pr1 = m().mk_def_intro(eq); result_pr = m().mk_apply_def(k, def, pr1); } } void push_cnstr_pr(proof * def_pr) { if (produce_proofs()) m_new_cnstr_prs.push_back(m().mk_th_lemma(u().get_family_id(), m_new_cnstrs.back(), 1, &def_pr)); } void push_cnstr_pr(proof * def_pr1, proof * def_pr2) { if (produce_proofs()) { proof * prs[2] = { def_pr1, def_pr2 }; m_new_cnstr_prs.push_back(m().mk_th_lemma(u().get_family_id(), m_new_cnstrs.back(), 2, prs)); } } void push_cnstr(expr * cnstr) { m_new_cnstrs.push_back(cnstr); } void cache_result(app * t, expr * r, proof * pr) { m_app2fresh.insert(t, r); m_pinned.push_back(t); m_pinned.push_back(r); if (produce_proofs()) { m_app2pr.insert(t, pr); m_pinned.push_back(pr); } } expr * OR(expr * arg1, expr * arg2) { return m().mk_or(arg1, arg2); } expr * AND(expr * arg1, expr * arg2) { return m().mk_and(arg1, arg2); } expr * EQ(expr * lhs, expr * rhs) { return m().mk_eq(lhs, rhs); } expr * NOT(expr * arg) { return m().mk_not(arg); } void process_div(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { app_ref t(m()); t = m().mk_app(f, num, args); if (already_processed(t, result, result_pr)) return; expr * k = mk_fresh_real_var(); result = k; mk_def_proof(k, t, result_pr); cache_result(t, result, result_pr); expr * x = args[0]; expr * y = args[1]; // y = 0 \/ y*k = x push_cnstr(OR(EQ(y, mk_real_zero()), EQ(u().mk_mul(y, k), x))); push_cnstr_pr(result_pr); rational r; if (complete()) { // y != 0 \/ k = div-0(x) push_cnstr(OR(NOT(EQ(y, mk_real_zero())), EQ(k, u().mk_div(x, mk_real_zero())))); push_cnstr_pr(result_pr); } m_divs.push_back(div_def(x, y, k)); } void process_idiv(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { app_ref div_app(m()); div_app = m().mk_app(f, num, args); if (already_processed(div_app, result, result_pr)) return; expr * k1 = mk_fresh_int_var(); result = k1; mk_def_proof(k1, div_app, result_pr); cache_result(div_app, result, result_pr); expr * k2 = mk_fresh_int_var(); app_ref mod_app(m()); proof_ref mod_pr(m()); mod_app = u().mk_mod(args[0], args[1]); mk_def_proof(k2, mod_app, mod_pr); cache_result(mod_app, k2, mod_pr); expr * x = args[0]; expr * y = args[1]; // (div x y) --> k1 | y = 0 \/ x = k1 * y + k2, // y = 0 \/ 0 <= k2, // y = 0 \/ k2 < |y|, // y != 0 \/ k1 = idiv-0(x), // y != 0 \/ k2 = mod-0(x) // We can write y = 0 \/ k2 < |y| as: // y > 0 implies k2 < y ---> y <= 0 \/ k2 < y // y < 0 implies k2 < -y ---> y >= 0 \/ k2 < -y // expr * zero = mk_int_zero(); push_cnstr(OR(EQ(y, zero), EQ(x, u().mk_add(u().mk_mul(k1, y), k2)))); push_cnstr_pr(result_pr, mod_pr); push_cnstr(OR(EQ(y, zero), u().mk_le(zero, k2))); push_cnstr_pr(mod_pr); push_cnstr(OR(u().mk_le(y, zero), u().mk_lt(k2, y))); push_cnstr_pr(mod_pr); push_cnstr(OR(u().mk_ge(y, zero), u().mk_lt(k2, u().mk_mul(u().mk_numeral(rational(-1), true), y)))); push_cnstr_pr(mod_pr); rational r; if (complete() && (!u().is_numeral(y, r) || r.is_zero())) { push_cnstr(OR(NOT(EQ(y, zero)), EQ(k1, u().mk_idiv(x, zero)))); push_cnstr_pr(result_pr); push_cnstr(OR(NOT(EQ(y, zero)), EQ(k2, u().mk_mod(x, zero)))); push_cnstr_pr(mod_pr); } } void process_mod(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { app_ref t(m()); t = m().mk_app(f, num, args); if (already_processed(t, result, result_pr)) return; process_idiv(f, num, args, result, result_pr); // it will create mod VERIFY(already_processed(t, result, result_pr)); } void process_to_int(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { app_ref t(m()); t = m().mk_app(f, num, args); if (already_processed(t, result, result_pr)) return; expr * k = mk_fresh_int_var(); result = k; mk_def_proof(k, t, result_pr); cache_result(t, result, result_pr); expr * x = args[0]; // x - to-real(k) >= 0 expr * diff = u().mk_add(x, u().mk_mul(u().mk_numeral(rational(-1), false), u().mk_to_real(k))); push_cnstr(u().mk_ge(diff, mk_real_zero())); push_cnstr_pr(result_pr); // not(x - to-real(k) >= 1) push_cnstr(NOT(u().mk_ge(diff, u().mk_numeral(rational(1), false)))); push_cnstr_pr(result_pr); } br_status process_power(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { rational y; if (!u().is_numeral(args[1], y)) return BR_FAILED; if (y.is_int() && !y.is_zero()) return BR_FAILED; app_ref t(m()); t = m().mk_app(f, num, args); if (already_processed(t, result, result_pr)) return BR_DONE; bool is_int = u().is_int(args[0]); expr * x = args[0]; rational xr; if (u().is_numeral(x, xr) && xr.is_zero()) return BR_FAILED; expr * k = mk_fresh_var(is_int); result = k; mk_def_proof(k, t, result_pr); cache_result(t, result, result_pr); expr_ref zero(u().mk_numeral(rational(0), is_int), m()); expr_ref one(u().mk_numeral(rational(1), is_int), m()); if (y.is_zero()) { // (^ x 0) --> k | x != 0 implies k = 1, x = 0 implies k = 0^0 push_cnstr(OR(EQ(x, zero), EQ(k, one))); push_cnstr_pr(result_pr); push_cnstr(OR(NOT(EQ(x, zero)), EQ(k, u().mk_power(zero, zero)))); push_cnstr_pr(result_pr); } else if (!is_int) { SASSERT(!y.is_int()); SASSERT(numerator(y).is_one()); rational n = denominator(y); if (!n.is_even()) { // (^ x (/ 1 n)) --> k | x = k^n // when n is odd push_cnstr(EQ(x, u().mk_power(k, u().mk_numeral(n, false)))); push_cnstr_pr(result_pr); } else { SASSERT(n.is_even()); // (^ x (/ 1 n)) --> k | x >= 0 implies (x = k^n and k >= 0), x < 0 implies k = neg-root(x, n) // when n is even push_cnstr(OR(NOT(u().mk_ge(x, zero)), AND(EQ(x, u().mk_power(k, u().mk_numeral(n, false))), u().mk_ge(k, zero)))); push_cnstr_pr(result_pr); push_cnstr(OR(u().mk_ge(x, zero), EQ(k, u().mk_neg_root(x, u().mk_numeral(n, false))))); push_cnstr_pr(result_pr); } // else { // return BR_FAILED; // } } else { // root not supported for integers. SASSERT(is_int); SASSERT(!y.is_int()); return BR_FAILED; } return BR_DONE; } void process_irrat(app * s, expr_ref & result, proof_ref & result_pr) { if (already_processed(s, result, result_pr)) return; expr * k = mk_fresh_real_var(); result = k; mk_def_proof(k, s, result_pr); cache_result(s, result, result_pr); anum_manager & am = u().am(); anum const & a = u().to_irrational_algebraic_numeral(s); scoped_mpz_vector p(am.qm()); am.get_polynomial(a, p); rational lower, upper; am.get_lower(a, lower); am.get_upper(a, upper); unsigned sz = p.size(); SASSERT(sz > 2); ptr_buffer args; for (unsigned i = 0; i < sz; i++) { if (am.qm().is_zero(p[i])) continue; rational coeff = rational(p[i]); if (i == 0) { args.push_back(u().mk_numeral(coeff, false)); } else { expr * m; if (i == 1) m = k; else m = u().mk_power(k, u().mk_numeral(rational(i), false)); args.push_back(u().mk_mul(u().mk_numeral(coeff, false), m)); } } SASSERT(args.size() >= 2); push_cnstr(EQ(u().mk_add(args.size(), args.c_ptr()), mk_real_zero())); push_cnstr_pr(result_pr); push_cnstr(u().mk_lt(u().mk_numeral(lower, false), k)); push_cnstr_pr(result_pr); push_cnstr(u().mk_lt(k, u().mk_numeral(upper, false))); push_cnstr_pr(result_pr); } br_status process_sin_cos(bool first, func_decl *f, expr * theta, expr_ref & result, proof_ref & result_pr) { expr* x, *y; if (m_owner.convert_basis(theta, x, y)) { result = first ? x : y; app_ref t(m().mk_app(f, theta), m()); mk_def_proof(result, t, result_pr); cache_result(t, result, result_pr); push_cnstr(EQ(mk_real_one(), u().mk_add(u().mk_mul(x, x), u().mk_mul(y, y)))); push_cnstr_pr(result_pr); 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; } } br_status process_sin(func_decl *f, expr * theta, expr_ref & result, proof_ref & result_pr) { return process_sin_cos(true, f, theta, result, result_pr); } br_status process_cos(func_decl *f, expr * theta, expr_ref & result, proof_ref & result_pr) { return process_sin_cos(false, f, theta, result, result_pr); } br_status process_asin(func_decl * f, expr * x, expr_ref & result, proof_ref & result_pr) { if (!elim_inverses()) return BR_FAILED; app_ref t(m()); t = m().mk_app(f, x); if (already_processed(t, result, result_pr)) return BR_DONE; expr * k = mk_fresh_var(false); result = k; mk_def_proof(k, t, result_pr); cache_result(t, result, result_pr); // Constraints: // -1 <= x <= 1 implies sin(k) = x, -pi/2 <= k <= pi/2 // If complete() // x < -1 implies k = asin_u(x) // x > 1 implies k = asin_u(x) expr * one = u().mk_numeral(rational(1), false); expr * mone = u().mk_numeral(rational(-1), false); expr * pi2 = u().mk_mul(u().mk_numeral(rational(1,2), false), u().mk_pi()); expr * mpi2 = u().mk_mul(u().mk_numeral(rational(-1,2), false), u().mk_pi()); // -1 <= x <= 1 implies sin(k) = x, -pi/2 <= k <= pi/2 push_cnstr(OR(OR(NOT(u().mk_ge(x, mone)), NOT(u().mk_le(x, one))), AND(EQ(x, u().mk_sin(k)), AND(u().mk_ge(k, mpi2), u().mk_le(k, pi2))))); push_cnstr_pr(result_pr); if (complete()) { // x < -1 implies k = asin_u(x) // x > 1 implies k = asin_u(x) push_cnstr(OR(u().mk_ge(x, mone), EQ(k, u().mk_u_asin(x)))); push_cnstr_pr(result_pr); push_cnstr(OR(u().mk_le(x, one), EQ(k, u().mk_u_asin(x)))); push_cnstr_pr(result_pr); } return BR_DONE; } br_status process_acos(func_decl * f, expr * x, expr_ref & result, proof_ref & result_pr) { if (!elim_inverses()) return BR_FAILED; app_ref t(m()); t = m().mk_app(f, x); if (already_processed(t, result, result_pr)) return BR_DONE; expr * k = mk_fresh_var(false); result = k; mk_def_proof(k, t, result_pr); cache_result(t, result, result_pr); // Constraints: // -1 <= x <= 1 implies cos(k) = x, 0 <= k <= pi // If complete() // x < -1 implies k = acos_u(x) // x > 1 implies k = acos_u(x) expr * one = u().mk_numeral(rational(1), false); expr * mone = u().mk_numeral(rational(-1), false); expr * pi = u().mk_pi(); expr * zero = u().mk_numeral(rational(0), false); // -1 <= x <= 1 implies cos(k) = x, 0 <= k <= pi push_cnstr(OR(OR(NOT(u().mk_ge(x, mone)), NOT(u().mk_le(x, one))), AND(EQ(x, u().mk_cos(k)), AND(u().mk_ge(k, zero), u().mk_le(k, pi))))); push_cnstr_pr(result_pr); if (complete()) { // x < -1 implies k = acos_u(x) // x > 1 implies k = acos_u(x) push_cnstr(OR(u().mk_ge(x, mone), EQ(k, u().mk_u_acos(x)))); push_cnstr_pr(result_pr); push_cnstr(OR(u().mk_le(x, one), EQ(k, u().mk_u_acos(x)))); push_cnstr_pr(result_pr); } return BR_DONE; } br_status process_atan(func_decl * f, expr * x, expr_ref & result, proof_ref & result_pr) { if (!elim_inverses()) return BR_FAILED; app_ref t(m()); t = m().mk_app(f, x); if (already_processed(t, result, result_pr)) return BR_DONE; expr * k = mk_fresh_var(false); result = k; mk_def_proof(k, t, result_pr); cache_result(t, result, result_pr); // Constraints: // tan(k) = x, -pi/2 < k < pi/2 expr * pi2 = u().mk_mul(u().mk_numeral(rational(1,2), false), u().mk_pi()); expr * mpi2 = u().mk_mul(u().mk_numeral(rational(-1,2), false), u().mk_pi()); push_cnstr(AND(EQ(x, u().mk_tan(k)), AND(u().mk_gt(k, mpi2), u().mk_lt(k, pi2)))); push_cnstr_pr(result_pr); return BR_DONE; } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { if (f->get_family_id() != u().get_family_id()) return BR_FAILED; switch (f->get_decl_kind()) { case OP_DIV: process_div(f, num, args, result, result_pr); return BR_DONE; case OP_IDIV: process_idiv(f, num, args, result, result_pr); return BR_DONE; case OP_MOD: process_mod(f, num, args, result, result_pr); return BR_DONE; case OP_TO_INT: process_to_int(f, num, args, result, result_pr); return BR_DONE; case OP_POWER: return process_power(f, num, args, result, result_pr); case OP_ASIN: return process_asin(f, args[0], result, result_pr); case OP_ACOS: return process_acos(f, args[0], result, result_pr); case OP_SIN: return process_sin(f, args[0], result, result_pr); case OP_COS: return process_cos(f, args[0], result, result_pr); case OP_ATAN: return process_atan(f, args[0], result, result_pr); default: return BR_FAILED; } } bool get_subst(expr * s, expr * & t, proof * & t_pr) { if (is_quantifier(s)) { m_owner.process_quantifier(to_quantifier(s), m_subst, m_subst_pr); t = m_subst.get(); t_pr = m_subst_pr.get(); return true; } else if (u().is_irrational_algebraic_numeral(s) && elim_root_objs()) { process_irrat(to_app(s), m_subst, m_subst_pr); t = m_subst.get(); t_pr = m_subst_pr.get(); return true; } return false; } }; expr * mk_real_zero() { return u().mk_numeral(rational(0), false); } struct rw : public rewriter_tpl { rw_cfg m_cfg; rw(purify_arith_proc & o): rewriter_tpl(o.m(), o.m_produce_proofs, m_cfg), m_cfg(o) { } }; void process_quantifier(quantifier * q, expr_ref & result, proof_ref & result_pr) { result_pr = nullptr; rw r(*this); expr_ref new_body(m()); proof_ref new_body_pr(m()); r(q->get_expr(), new_body, new_body_pr); unsigned num_vars = r.cfg().m_new_vars.size(); TRACE("purify_arith", tout << "num_vars: " << num_vars << "\n"; tout << "body: " << mk_ismt2_pp(q->get_expr(), m()) << "\nnew_body: " << mk_ismt2_pp(new_body, m()) << "\n";); if (num_vars == 0) { SASSERT(r.cfg().m_new_cnstrs.empty()); result = m().update_quantifier(q, new_body); if (m_produce_proofs) result_pr = m().mk_quant_intro(q, to_quantifier(result.get()), result_pr); } else { // Add new constraints expr_ref_vector & cnstrs = r.cfg().m_new_cnstrs; cnstrs.push_back(new_body); new_body = m().mk_and(cnstrs.size(), cnstrs.c_ptr()); // Open space for new variables var_shifter shifter(m()); shifter(new_body, num_vars, new_body); // Rename fresh constants in r.cfg().m_new_vars to variables ptr_buffer sorts; buffer names; expr_substitution subst(m(), false, false); for (unsigned i = 0; i < num_vars; i++) { expr * c = r.cfg().m_new_vars.get(i); sort * s = get_sort(c); sorts.push_back(s); names.push_back(m().mk_fresh_var_name("x")); unsigned idx = num_vars - i - 1; subst.insert(c, m().mk_var(idx, s)); } scoped_ptr replacer = mk_default_expr_replacer(m()); replacer->set_substitution(&subst); (*replacer)(new_body, new_body); new_body = m().mk_exists(num_vars, sorts.c_ptr(), names.c_ptr(), new_body, q->get_weight()); result = m().update_quantifier(q, new_body); if (m_produce_proofs) { proof_ref_vector & cnstr_prs = r.cfg().m_new_cnstr_prs; cnstr_prs.push_back(result_pr); // TODO: improve proof result_pr = m().mk_quant_intro(q, to_quantifier(result.get()), m().mk_rewrite_star(q->get_expr(), new_body, cnstr_prs.size(), cnstr_prs.c_ptr())); } } } void operator()(model_converter_ref & mc, bool produce_models) { rw r(*this); // purify expr_ref new_curr(m()); proof_ref new_pr(m()); unsigned sz = m_goal.size(); for (unsigned i = 0; i < sz; i++) { expr * curr = m_goal.form(i); r(curr, new_curr, new_pr); if (m_produce_proofs) { proof * pr = m_goal.pr(i); new_pr = m().mk_modus_ponens(pr, new_pr); } m_goal.update(i, new_curr, new_pr, m_goal.dep(i)); } // add cnstraints sz = r.cfg().m_new_cnstrs.size(); TRACE("purify_arith", tout << r.cfg().m_new_cnstrs << "\n";); for (unsigned i = 0; i < sz; i++) { m_goal.assert_expr(r.cfg().m_new_cnstrs.get(i), m_produce_proofs ? r.cfg().m_new_cnstr_prs.get(i) : nullptr, nullptr); } auto const& divs = r.cfg().m_divs; for (unsigned i = 0; i < divs.size(); ++i) { auto const& p1 = divs[i]; for (unsigned j = i + 1; j < divs.size(); ++j) { auto const& p2 = divs[j]; m_goal.assert_expr(m().mk_implies( m().mk_and(m().mk_eq(p1.x, p2.x), m().mk_eq(p1.y, p2.y)), m().mk_eq(p1.d, p2.d))); } } // add generic_model_converter to eliminate auxiliary variables from model if (produce_models) { generic_model_converter * fmc = alloc(generic_model_converter, m(), "purify"); mc = fmc; obj_map & f2v = r.cfg().m_app2fresh; for (auto const& kv : f2v) { app * v = to_app(kv.m_value); SASSERT(is_uninterp_const(v)); fmc->hide(v->get_decl()); } if (!divs.empty()) { expr_ref body(u().mk_real(0), m()); expr_ref v0(m().mk_var(0, u().mk_real()), m()); expr_ref v1(m().mk_var(1, u().mk_real()), m()); for (auto const& p : divs) { body = m().mk_ite(m().mk_and(m().mk_eq(v0, p.x), m().mk_eq(v1, p.y)), p.d, body); } fmc->add(u().mk_div0(), body); } } if (produce_models && !m_sin_cos.empty()) { generic_model_converter* emc = alloc(generic_model_converter, m(), "purify_sin_cos"); mc = concat(mc.get(), emc); 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()))); } } } }; class purify_arith_tactic : public tactic { arith_util m_util; params_ref m_params; public: purify_arith_tactic(ast_manager & m, params_ref const & p): m_util(m), m_params(p) { } tactic * translate(ast_manager & m) override { return alloc(purify_arith_tactic, m, m_params); } ~purify_arith_tactic() override { } void updt_params(params_ref const & p) override { m_params = p; } void collect_param_descrs(param_descrs & r) override { r.insert("complete", CPK_BOOL, "(default: true) add constraints to make sure that any interpretation of a underspecified arithmetic operators is a function. The result will include additional uninterpreted functions/constants: /0, div0, mod0, 0^0, neg-root"); r.insert("elim_root_objects", CPK_BOOL, "(default: true) eliminate root objects."); r.insert("elim_inverses", CPK_BOOL, "(default: true) eliminate inverse trigonometric functions (asin, acos, atan)."); th_rewriter::get_param_descrs(r); } void operator()(goal_ref const & g, goal_ref_buffer & result) override { try { SASSERT(g->is_well_sorted()); tactic_report report("purify-arith", *g); TRACE("purify_arith", g->display(tout);); bool produce_proofs = g->proofs_enabled(); bool produce_models = g->models_enabled(); bool elim_root_objs = m_params.get_bool("elim_root_objects", true); bool elim_inverses = m_params.get_bool("elim_inverses", true); bool complete = m_params.get_bool("complete", true); purify_arith_proc proc(*(g.get()), m_util, produce_proofs, elim_root_objs, elim_inverses, complete); model_converter_ref mc; proc(mc, produce_models); g->add(mc.get()); g->inc_depth(); result.push_back(g.get()); TRACE("purify_arith", g->display(tout);); SASSERT(g->is_well_sorted()); } catch (rewriter_exception & ex) { throw tactic_exception(ex.msg()); } } void cleanup() override { } }; tactic * mk_purify_arith_tactic(ast_manager & m, params_ref const & p) { params_ref elim_rem_p = p; elim_rem_p.set_bool("elim_rem", true); params_ref skolemize_p; skolemize_p.set_bool("skolemize", false); return and_then(using_params(mk_snf_tactic(m, skolemize_p), skolemize_p), using_params(mk_simplify_tactic(m, elim_rem_p), elim_rem_p), alloc(purify_arith_tactic, m, p), mk_simplify_tactic(m, p)); } z3-z3-4.8.7/src/tactic/arith/purify_arith_tactic.h000066400000000000000000000035671356505360400220140ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: purify_arith_tactic.h Abstract: Tactic for eliminating arithmetic operators: DIV, IDIV, MOD, TO_INT, and optionally (OP_IRRATIONAL_ALGEBRAIC_NUM). This tactic uses the simplifier for also eliminating: OP_SUB, OP_UMINUS, OP_POWER (optionally), OP_REM, OP_IS_INT. Remarks: - The semantics of division by zero is not specified. Thus, uninterpreted functions are used. An ExRCF procedure may treat the uninterpreted function applications as fresh constants. Then, in any model produced by this procedure, the interpretation for division by zero must be checked. - POWER operator can only be handled if the second argument is a rational value. The tactic has an option for preserving POWER operator where the second argument is an integer. - The semantics of (^ t (/ 1 k)) is not specified when t < 0 and k is even. Similarly to the division by zero case, uninterpreted function symbols are created. - The semantics of (^ t 0) is not specified if t == 0. Thus, uninterpreted function symbols are created. - TO_REAL is not really outside of the RCF language since it is only used for "casting". - All quantifiers must occur with positive polarity. The tactic snf (with skolemization disabled) is applied to enforce that. Author: Leonardo de Moura (leonardo) 2011-12-30. Revision History: --*/ #ifndef PURIFY_ARITH_TACTIC_H_ #define PURIFY_ARITH_TACTIC_H_ #include "util/params.h" class ast_manager; class tactic; tactic * mk_purify_arith_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("purify-arith", "eliminate unnecessary operators: -, /, div, mod, rem, is-int, to-int, ^, root-objects.", "mk_purify_arith_tactic(m, p)") */ #endif z3-z3-4.8.7/src/tactic/arith/recover_01_tactic.cpp000066400000000000000000000344101356505360400215760ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: recover_01_tactic.cpp Abstract: Recover 01 variables Search for clauses of the form p or q or x = 0 ~p or q or x = k1 p or ~q or x = k2 ~p or ~q or x = k1+k2 Then, replaces x with k1*y1 + k2*y2 p with y1=1 q with y2=1 where y1 and y2 are fresh 01 variables The clauses are also removed. Author: Leonardo de Moura (leonardo) 2012-02-17. Revision History: --*/ #include "tactic/tactical.h" #include "ast/rewriter/th_rewriter.h" #include "tactic/generic_model_converter.h" #include "ast/arith_decl_plugin.h" #include "ast/expr_substitution.h" #include "util/dec_ref_util.h" #include "ast/ast_smt2_pp.h" class recover_01_tactic : public tactic { struct imp { typedef obj_map > var2clauses; ast_manager & m; var2clauses m_var2clauses; arith_util m_util; th_rewriter m_rw; bool m_produce_models; unsigned m_cls_max_size; imp(ast_manager & _m, params_ref const & p): m(_m), m_util(m), m_rw(m, p) { updt_params_core(p); } void updt_params_core(params_ref const & p) { m_cls_max_size = p.get_uint("recover_01_max_bits", 10); } void updt_params(params_ref const & p) { m_rw.updt_params(p); updt_params_core(p); } bool save_clause(expr * c) { if (!m.is_or(c)) return false; func_decl * x = nullptr; app * cls = to_app(c); if (cls->get_num_args() <= 1 || cls->get_num_args() >= m_cls_max_size) return false; unsigned sz = cls->get_num_args(); for (unsigned i = 0; i < sz; i++) { expr * lit = cls->get_arg(i); expr * lhs, * rhs, * arg; if (is_uninterp_const(lit)) { // positive literal } else if (m.is_not(lit, arg) && is_uninterp_const(arg)) { // negative literal } else if (x == nullptr && m.is_eq(lit, lhs, rhs)) { // x = k literal if (is_uninterp_const(lhs) && m_util.is_numeral(rhs)) { x = to_app(lhs)->get_decl(); } else if (is_uninterp_const(rhs) && m_util.is_numeral(lhs)) { x = to_app(rhs)->get_decl(); } else { return false; } } else { return false; } } if (x != nullptr) { var2clauses::obj_map_entry * entry = m_var2clauses.insert_if_not_there2(x, ptr_vector()); if (entry->get_data().m_value.empty() || entry->get_data().m_value.back()->get_num_args() == cls->get_num_args()) { entry->get_data().m_value.push_back(cls); return true; } } return false; } // temporary fields used by operator() and process generic_model_converter * gmc; expr_substitution * subst; goal_ref new_goal; obj_map bool2int; app * find_zero_cls(func_decl * x, ptr_vector & clauses) { ptr_vector::iterator it = clauses.begin(); ptr_vector::iterator end = clauses.end(); for (; it != end; ++it) { app * cls = *it; unsigned num = cls->get_num_args(); for (unsigned i = 0; i < num; i++) { expr * lhs, * rhs; if (m.is_eq(cls->get_arg(i), lhs, rhs)) { if (is_uninterp_const(lhs) && m_util.is_zero(rhs)) return cls; if (is_uninterp_const(rhs) && m_util.is_zero(lhs)) return cls; } } } return nullptr; } // Find coeff (the k of literal (x = k)) of clause cls. // Store in idx the bit-vector representing the literals. // Example: idx = 101 if cls has three boolean literals p1, p2, p3 // where p1 = ~q1, p2 = q2, p3 = ~q3 // and q1 q2 q3 are the corresponding literals in the // zero clause. // Return false, if the boolean literals of cls cannot be matched with the literals // of zero_cls bool find_coeff(app * cls, app * zero_cls, unsigned & idx, rational & k) { unsigned num = zero_cls->get_num_args(); if (cls->get_num_args() != num) return false; idx = 0; unsigned val = 1; for (unsigned i = 0; i < num; i++) { expr * lit = zero_cls->get_arg(i); if (m.is_eq(lit)) continue; // search for lit or ~lit in cls unsigned j; for (j = 0; j < num; j++) { expr * lit2 = cls->get_arg(j); if (m.is_eq(lit2)) continue; if (lit2 == lit) break; if (m.is_complement(lit2, lit)) { idx += val; break; } } if (j == num) return false; // cls does not contain literal lit val *= 2; } // find k unsigned i; for (i = 0; i < num; i++) { expr * lhs, * rhs; if (m.is_eq(cls->get_arg(i), lhs, rhs) && (m_util.is_numeral(lhs, k) || m_util.is_numeral(rhs, k))) break; } if (i == num) return false; return true; } void mk_ivar(expr * lit, expr_ref & def, bool real_ctx) { expr * atom; bool sign; if (m.is_not(lit, atom)) { sign = true; } else { atom = lit; sign = false; } SASSERT(is_uninterp_const(atom)); expr * var; if (!bool2int.find(atom, var)) { var = m.mk_fresh_const(nullptr, m_util.mk_int()); new_goal->assert_expr(m_util.mk_le(m_util.mk_numeral(rational(0), true), var)); new_goal->assert_expr(m_util.mk_le(var, m_util.mk_numeral(rational(1), true))); expr * bool_def = m.mk_eq(var, m_util.mk_numeral(rational(1), true)); subst->insert(atom, bool_def); if (m_produce_models) { gmc->hide(var); gmc->add(to_app(atom)->get_decl(), bool_def); } m.inc_ref(atom); m.inc_ref(var); bool2int.insert(atom, var); } expr * norm_var = real_ctx ? m_util.mk_to_real(var) : var; if (sign) def = m_util.mk_sub(m_util.mk_numeral(rational(1), !real_ctx), norm_var); else def = norm_var; } bool process(func_decl * x, ptr_vector & clauses) { unsigned cls_size = clauses.back()->get_num_args(); unsigned expected_num_clauses = 1 << (cls_size - 1); if (clauses.size() < expected_num_clauses) // using < instead of != because we tolerate duplicates return false; app * zero_cls = find_zero_cls(x, clauses); if (zero_cls == nullptr) return false; buffer found; // marks which idx were found buffer idx2coeff; found.resize(expected_num_clauses, false); idx2coeff.resize(expected_num_clauses); ptr_vector::iterator it = clauses.begin(); ptr_vector::iterator end = clauses.end(); for (; it != end; ++it) { app * cls = *it; unsigned idx; rational k; if (!find_coeff(cls, zero_cls, idx, k)) return false; SASSERT(idx < expected_num_clauses); if (found[idx] && k != idx2coeff[idx]) return false; found[idx] = true; idx2coeff[idx] = k; } unsigned num_bits = cls_size - 1; // check if idxs are consistent for (unsigned idx = 0; idx < expected_num_clauses; idx++) { if (!found[idx]) return false; // case is missing rational expected_k; unsigned idx_aux = idx; unsigned idx_bit = 1; for (unsigned j = 0; j < num_bits; j++) { if (idx_aux % 2 == 1) { expected_k += idx2coeff[idx_bit]; } idx_aux /= 2; idx_bit *= 2; } if (idx2coeff[idx] != expected_k) return false; } expr_ref_buffer def_args(m); expr_ref def(m); bool real_ctx = m_util.is_real(x->get_range()); unsigned idx_bit = 1; for (unsigned i = 0; i < cls_size; i++) { expr * lit = zero_cls->get_arg(i); if (m.is_eq(lit)) continue; mk_ivar(lit, def, real_ctx); def_args.push_back(m_util.mk_mul(m_util.mk_numeral(idx2coeff[idx_bit], !real_ctx), def)); idx_bit *= 2; } expr * x_def; if (def_args.size() == 1) x_def = def_args[0]; else x_def = m_util.mk_add(def_args.size(), def_args.c_ptr()); TRACE("recover_01", tout << x->get_name() << " --> " << mk_ismt2_pp(x_def, m) << "\n";); subst->insert(m.mk_const(x), x_def); if (m_produce_models) { gmc->add(x, x_def); } return true; } void operator()(goal_ref const & g, goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); fail_if_proof_generation("recover-01", g); fail_if_unsat_core_generation("recover-01", g); m_produce_models = g->models_enabled(); result.reset(); tactic_report report("recover-01", *g); bool saved = false; new_goal = alloc(goal, *g, true); SASSERT(new_goal->depth() == g->depth()); SASSERT(new_goal->prec() == g->prec()); new_goal->inc_depth(); new_goal->add(g->mc()); new_goal->add(g->pc()); unsigned sz = g->size(); for (unsigned i = 0; i < sz; i++) { expr * f = g->form(i); if (save_clause(f)) { saved = true; } else { new_goal->assert_expr(f); } } if (!saved) { result.push_back(g.get()); return; } if (m_produce_models) { gmc = alloc(generic_model_converter, m, "recover_01"); new_goal->add(gmc); } dec_ref_key_values(m, bool2int); unsigned counter = 0; bool recovered = false; expr_substitution _subst(m); subst = &_subst; for (auto& kv : m_var2clauses) { if (process(kv.m_key, kv.m_value)) { recovered = true; counter++; } else { for (app* a : kv.m_value) { new_goal->assert_expr(a); } } } if (!recovered) { result.push_back(g.get()); return; } report_tactic_progress(":recovered-01-vars", counter); m_rw.set_substitution(subst); expr_ref new_curr(m); proof_ref new_pr(m); unsigned size = new_goal->size(); for (unsigned idx = 0; idx < size; idx++) { expr * curr = new_goal->form(idx); m_rw(curr, new_curr); new_goal->update(idx, new_curr); } result.push_back(new_goal.get()); TRACE("recover_01", new_goal->display(tout);); SASSERT(new_goal->is_well_sorted()); } ~imp() { dec_ref_key_values(m, bool2int); } }; imp * m_imp; params_ref m_params; public: recover_01_tactic(ast_manager & m, params_ref const & p): m_params(p) { m_imp = alloc(imp, m, p); } tactic * translate(ast_manager & m) override { return alloc(recover_01_tactic, m, m_params); } ~recover_01_tactic() override { dealloc(m_imp); } void updt_params(params_ref const & p) override { m_params = p; m_imp->updt_params(p); } void collect_param_descrs(param_descrs & r) override { th_rewriter::get_param_descrs(r); r.insert("recover_01_max_bits", CPK_UINT, "(default: 10) maximum number of bits to consider in a clause."); } void operator()(goal_ref const & g, goal_ref_buffer & result) override { try { (*m_imp)(g, result); } catch (rewriter_exception & ex) { throw tactic_exception(ex.msg()); } } void cleanup() override { imp * d = alloc(imp, m_imp->m, m_params); std::swap(d, m_imp); dealloc(d); } }; tactic * mk_recover_01_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(recover_01_tactic, m, p)); } z3-z3-4.8.7/src/tactic/arith/recover_01_tactic.h000066400000000000000000000014601356505360400212420ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: recover_01_tactic.h Abstract: Recover 01 variables Search for clauses of the form p or q or x = 0 ~p or q or x = k1 p or ~q or x = k2 ~p or ~q or x = k1+k2 Then, replaces x with k1*y1 + k2*y2 p with y1=1 q with y2=1 where y1 and y2 are fresh 01 variables The clauses are also removed. Author: Leonardo de Moura (leonardo) 2012-02-17. Revision History: --*/ #ifndef RECOVER_01_TACTIC_H_ #define RECOVER_01_TACTIC_H_ #include "util/params.h" class ast_manager; class tactic; tactic * mk_recover_01_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("recover-01", "recover 0-1 variables hidden as Boolean variables.", "mk_recover_01_tactic(m, p)") */ #endif z3-z3-4.8.7/src/tactic/bv/000077500000000000000000000000001356505360400150745ustar00rootroot00000000000000z3-z3-4.8.7/src/tactic/bv/CMakeLists.txt000066400000000000000000000012301356505360400176300ustar00rootroot00000000000000z3_add_component(bv_tactics SOURCES bit_blaster_model_converter.cpp bit_blaster_tactic.cpp bv1_blaster_tactic.cpp bvarray2uf_rewriter.cpp bvarray2uf_tactic.cpp bv_bound_chk_tactic.cpp bv_bounds_tactic.cpp bv_size_reduction_tactic.cpp dt2bv_tactic.cpp elim_small_bv_tactic.cpp max_bv_sharing_tactic.cpp COMPONENT_DEPENDENCIES bit_blaster core_tactics tactic TACTIC_HEADERS bit_blaster_tactic.h bv1_blaster_tactic.h bv_bound_chk_tactic.h bv_bounds_tactic.h bv_size_reduction_tactic.h bvarray2uf_tactic.h dt2bv_tactic.h elim_small_bv_tactic.h max_bv_sharing_tactic.h ) z3-z3-4.8.7/src/tactic/bv/bit_blaster_model_converter.cpp000066400000000000000000000207661356505360400233540ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: bit_blaster_model_convert.cpp Abstract: Model converter for bit-blasting tactics. Author: Leonardo (leonardo) 2011-05-09 Notes: --*/ #include "model/model.h" #include "model/model_pp.h" #include "tactic/model_converter.h" #include "ast/bv_decl_plugin.h" #include "ast/ast_smt2_pp.h" #include "ast/ast_util.h" /** If TO_BOOL == true, then bit-vectors of size n were blasted into n-tuples of Booleans. If TO_BOOL == false, then bit-vectors of size n were blasted into n-tuples of bit-vectors of size 1. */ template struct bit_blaster_model_converter : public model_converter { func_decl_ref_vector m_vars; expr_ref_vector m_bits; func_decl_ref_vector m_newbits; ast_manager & m() const { return m_vars.get_manager(); } bit_blaster_model_converter( ast_manager & m, obj_map const & const2bits, ptr_vector const& newbits): m_vars(m), m_bits(m), m_newbits(m) { for (auto const& kv : const2bits) { func_decl * v = kv.m_key; expr * bits = kv.m_value; SASSERT(!TO_BOOL || is_app_of(bits, m.get_family_id("bv"), OP_MKBV)); SASSERT(TO_BOOL || is_app_of(bits, m.get_family_id("bv"), OP_CONCAT)); m_vars.push_back(v); m_bits.push_back(bits); } for (func_decl* f : newbits) m_newbits.push_back(f); } ~bit_blaster_model_converter() override { } void collect_bits(obj_hashtable & bits) { unsigned sz = m_bits.size(); for (unsigned i = 0; i < sz; i++) { expr * bs = m_bits.get(i); SASSERT(!TO_BOOL || is_app_of(bs, m().get_family_id("bv"), OP_MKBV)); SASSERT(TO_BOOL || is_app_of(bs, m().get_family_id("bv"), OP_CONCAT)); unsigned num_args = to_app(bs)->get_num_args(); for (unsigned j = 0; j < num_args; j++) { expr * bit = to_app(bs)->get_arg(j); SASSERT(!TO_BOOL || m().is_bool(bit)); SASSERT(TO_BOOL || is_sort_of(m().get_sort(bit), m().get_family_id("bv"), BV_SORT)); SASSERT(is_uninterp_const(bit)); bits.insert(to_app(bit)->get_decl()); } } TRACE("blaster_mc", tout << "bits that should not be included in the model:\n"; for (func_decl* f : bits) { tout << f->get_name() << " "; } tout << "\n";); } void copy_non_bits(obj_hashtable & bits, model * old_model, model * new_model) { unsigned num = old_model->get_num_constants(); for (unsigned i = 0; i < num; i++) { func_decl * f = old_model->get_constant(i); if (bits.contains(f)) continue; TRACE("blaster_mc", tout << "non-bit: " << f->get_name() << "\n";); expr * fi = old_model->get_const_interp(f); new_model->register_decl(f, fi); } TRACE("blaster_mc", tout << "after copy non bits:\n"; model_pp(tout, *new_model);); new_model->copy_func_interps(*old_model); new_model->copy_usort_interps(*old_model); TRACE("blaster_mc", tout << "after copying functions and sorts:\n"; model_pp(tout, *new_model);); } void mk_bvs(model * old_model, model * new_model) { bv_util util(m()); rational val; rational two(2); SASSERT(m_vars.size() == m_bits.size()); unsigned sz = m_vars.size(); for (unsigned i = 0; i < sz; i++) { expr* new_val = old_model->get_const_interp(m_vars.get(i)); if (new_val) { new_model->register_decl(m_vars.get(i), new_val); continue; } expr * bs = m_bits.get(i); val.reset(); unsigned bv_sz = to_app(bs)->get_num_args(); if (TO_BOOL) { SASSERT(is_app_of(bs, m().get_family_id("bv"), OP_MKBV)); unsigned j = bv_sz; while (j > 0) { --j; val *= two; expr * bit = to_app(bs)->get_arg(j); SASSERT(m().is_bool(bit)); SASSERT(is_uninterp_const(bit)); func_decl * bit_decl = to_app(bit)->get_decl(); expr * bit_val = old_model->get_const_interp(bit_decl); if (bit_val != nullptr && m().is_true(bit_val)) val++; } } else { SASSERT(is_app_of(bs, m().get_family_id("bv"), OP_CONCAT)); for (unsigned j = 0; j < bv_sz; j++) { val *= two; expr * bit = to_app(bs)->get_arg(j); SASSERT(util.is_bv(bit)); SASSERT(util.get_bv_size(bit) == 1); SASSERT(is_uninterp_const(bit)); func_decl * bit_decl = to_app(bit)->get_decl(); expr * bit_val = old_model->get_const_interp(bit_decl); // remark: if old_model does not assign bit_val, then assume it is false. if (bit_val != nullptr && !util.is_zero(bit_val)) val++; } } new_val = util.mk_numeral(val, bv_sz); new_model->register_decl(m_vars.get(i), new_val); } } app_ref mk_bv(expr* bs, model& old_model) { bv_util util(m()); unsigned bv_sz = to_app(bs)->get_num_args(); expr_ref_vector args(m()); app_ref result(m()); for (expr * bit : *to_app(bs)) { SASSERT(is_uninterp_const(bit)); func_decl * bit_decl = to_app(bit)->get_decl(); expr * bit_val = old_model.get_const_interp(bit_decl); args.push_back(bit_val ? bit_val : bit); } if (TO_BOOL) { SASSERT(is_app_of(bs, m().get_family_id("bv"), OP_MKBV)); result = util.mk_bv(bv_sz, args.c_ptr()); } else { SASSERT(is_app_of(bs, m().get_family_id("bv"), OP_CONCAT)); result = util.mk_concat(bv_sz, args.c_ptr()); } return result; } void operator()(model_ref & md) override { model * new_model = alloc(model, m()); obj_hashtable bits; collect_bits(bits); copy_non_bits(bits, md.get(), new_model); mk_bvs(md.get(), new_model); md = new_model; } /** \brief simplisic expansion operator for formulas. It just adds back bit-vector definitions to the formula whether they are used or not. */ void operator()(expr_ref& fml) override { unsigned sz = m_vars.size(); if (sz == 0) return; expr_ref_vector fmls(m()); fmls.push_back(fml); for (unsigned i = 0; i < sz; i++) { fmls.push_back(m().mk_eq(m().mk_const(m_vars.get(i)), m_bits.get(i))); } m_vars.reset(); m_bits.reset(); fml = mk_and(fmls); } void display(std::ostream & out) override { for (func_decl * f : m_newbits) display_del(out, f); unsigned sz = m_vars.size(); for (unsigned i = 0; i < sz; i++) display_add(out, m(), m_vars.get(i), m_bits.get(i)); } void get_units(obj_map& units) override { // no-op } protected: bit_blaster_model_converter(ast_manager & m):m_vars(m), m_bits(m), m_newbits(m) { } public: model_converter * translate(ast_translation & translator) override { bit_blaster_model_converter * res = alloc(bit_blaster_model_converter, translator.to()); for (func_decl * v : m_vars) res->m_vars.push_back(translator(v)); for (expr* b : m_bits) res->m_bits.push_back(translator(b)); for (func_decl* f : m_newbits) res->m_newbits.push_back(translator(f)); return res; } }; model_converter * mk_bit_blaster_model_converter(ast_manager & m, obj_map const & const2bits, ptr_vector const& newbits) { return const2bits.empty() ? nullptr : alloc(bit_blaster_model_converter, m, const2bits, newbits); } model_converter * mk_bv1_blaster_model_converter(ast_manager & m, obj_map const & const2bits, ptr_vector const& newbits) { return const2bits.empty() ? nullptr : alloc(bit_blaster_model_converter, m, const2bits, newbits); } z3-z3-4.8.7/src/tactic/bv/bit_blaster_model_converter.h000066400000000000000000000011741356505360400230110ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: bit_blaster_model_convert.h Abstract: Model converter for bit-blasting tactics. Author: Leonardo (leonardo) 2011-05-09 Notes: --*/ #ifndef BIT_BLASTER_MODEL_CONVERTER_H_ #define BIT_BLASTER_MODEL_CONVERTER_H_ #include "tactic/model_converter.h" model_converter * mk_bit_blaster_model_converter(ast_manager & m, obj_map const & const2bits, ptr_vector const& newbits); model_converter * mk_bv1_blaster_model_converter(ast_manager & m, obj_map const & const2bits, ptr_vector const& newbits); #endif z3-z3-4.8.7/src/tactic/bv/bit_blaster_tactic.cpp000066400000000000000000000123361356505360400214260ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: bit_blaster_tactic.cpp Abstract: Apply bit-blasting to a given goal Author: Leonardo (leonardo) 2011-10-25 Notes: --*/ #include "tactic/tactical.h" #include "tactic/bv/bit_blaster_model_converter.h" #include "ast/rewriter/bit_blaster/bit_blaster_rewriter.h" #include "ast/ast_pp.h" #include "model/model_pp.h" #include "ast/rewriter/rewriter_types.h" class bit_blaster_tactic : public tactic { struct imp { bit_blaster_rewriter m_base_rewriter; bit_blaster_rewriter* m_rewriter; unsigned m_num_steps; bool m_blast_quant; imp(ast_manager & m, bit_blaster_rewriter* rw, params_ref const & p): m_base_rewriter(m, p), m_rewriter(rw?rw:&m_base_rewriter) { updt_params(p); } void updt_params_core(params_ref const & p) { m_blast_quant = p.get_bool("blast_quant", false); } void updt_params(params_ref const & p) { m_rewriter->updt_params(p); updt_params_core(p); } ast_manager & m() const { return m_rewriter->m(); } void operator()(goal_ref const & g, goal_ref_buffer & result) { bool proofs_enabled = g->proofs_enabled(); if (proofs_enabled && m_blast_quant) throw tactic_exception("quantified variable blasting does not support proof generation"); tactic_report report("bit-blaster", *g); TRACE("before_bit_blaster", g->display(tout);); m_num_steps = 0; m_rewriter->start_rewrite(); expr_ref new_curr(m()); proof_ref new_pr(m()); unsigned size = g->size(); bool change = false; for (unsigned idx = 0; idx < size; idx++) { if (g->inconsistent()) break; expr * curr = g->form(idx); (*m_rewriter)(curr, new_curr, new_pr); m_num_steps += m_rewriter->get_num_steps(); if (proofs_enabled) { proof * pr = g->pr(idx); new_pr = m().mk_modus_ponens(pr, new_pr); } if (curr != new_curr) { change = true; TRACE("bit_blaster", tout << mk_pp(curr, m()) << " -> " << new_curr << "\n";); g->update(idx, new_curr, new_pr, g->dep(idx)); } } if (change && g->models_enabled()) { obj_map const2bits; ptr_vector newbits; m_rewriter->end_rewrite(const2bits, newbits); g->add(mk_bit_blaster_model_converter(m(), const2bits, newbits)); } g->inc_depth(); result.push_back(g.get()); TRACE("after_bit_blaster", g->display(tout); if (g->mc()) g->mc()->display(tout); tout << "\n";); m_rewriter->cleanup(); } unsigned get_num_steps() const { return m_num_steps; } }; imp * m_imp; bit_blaster_rewriter* m_rewriter; params_ref m_params; public: bit_blaster_tactic(ast_manager & m, bit_blaster_rewriter* rw, params_ref const & p): m_rewriter(rw), m_params(p) { m_imp = alloc(imp, m, m_rewriter, p); } tactic * translate(ast_manager & m) override { SASSERT(!m_rewriter); // assume translation isn't used where rewriter is external. return alloc(bit_blaster_tactic, m, nullptr, m_params); } ~bit_blaster_tactic() override { dealloc(m_imp); } void updt_params(params_ref const & p) override { m_params = p; m_imp->updt_params(p); } void collect_param_descrs(param_descrs & r) override { insert_max_memory(r); insert_max_steps(r); r.insert("blast_mul", CPK_BOOL, "(default: true) bit-blast multipliers (and dividers, remainders)."); r.insert("blast_add", CPK_BOOL, "(default: true) bit-blast adders."); r.insert("blast_quant", CPK_BOOL, "(default: false) bit-blast quantified variables."); r.insert("blast_full", CPK_BOOL, "(default: false) bit-blast any term with bit-vector sort, this option will make E-matching ineffective in any pattern containing bit-vector terms."); } void operator()(goal_ref const & g, goal_ref_buffer & result) override { try { (*m_imp)(g, result); } catch (rewriter_exception & ex) { throw tactic_exception(ex.msg()); } } void cleanup() override { imp * d = alloc(imp, m_imp->m(), m_rewriter, m_params); std::swap(d, m_imp); dealloc(d); } unsigned get_num_steps() const { return m_imp->get_num_steps(); } }; tactic * mk_bit_blaster_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(bit_blaster_tactic, m, nullptr, p)); } tactic * mk_bit_blaster_tactic(ast_manager & m, bit_blaster_rewriter* rw, params_ref const & p) { return clean(alloc(bit_blaster_tactic, m, rw, p)); } z3-z3-4.8.7/src/tactic/bv/bit_blaster_tactic.h000066400000000000000000000013051356505360400210650ustar00rootroot00000000000000 /*++ Copyright (c) 2011 Microsoft Corporation Module Name: bit_blaster_tactic.h Abstract: Apply bit-blasting to a given goal. Author: Leonardo (leonardo) 2011-10-25 Notes: --*/ #ifndef BIT_BLASTER_TACTIC_H_ #define BIT_BLASTER_TACTIC_H_ #include "util/params.h" #include "ast/rewriter/bit_blaster/bit_blaster_rewriter.h" class ast_manager; class tactic; tactic * mk_bit_blaster_tactic(ast_manager & m, params_ref const & p = params_ref()); tactic * mk_bit_blaster_tactic(ast_manager & m, bit_blaster_rewriter* rw, params_ref const & p = params_ref()); /* ADD_TACTIC("bit-blast", "reduce bit-vector expressions into SAT.", "mk_bit_blaster_tactic(m, p)") */ #endif z3-z3-4.8.7/src/tactic/bv/bv1_blaster_tactic.cpp000066400000000000000000000407121356505360400213370ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: bv1_blaster_tactic.cpp Abstract: Rewriter for "blasting" bit-vectors of size n into bit-vectors of size 1. This rewriter only supports concat and extract operators. This transformation is useful for handling benchmarks that contain many BV equalities. Remark: other operators can be mapped into concat/extract by using the simplifiers. Author: Leonardo (leonardo) 2011-10-25 Notes: --*/ #include "tactic/tactical.h" #include "tactic/bv/bit_blaster_model_converter.h" #include "ast/bv_decl_plugin.h" #include "ast/rewriter/rewriter_def.h" #include "ast/for_each_expr.h" #include "ast/rewriter/bv_rewriter.h" class bv1_blaster_tactic : public tactic { struct rw_cfg : public default_rewriter_cfg { ast_manager & m_manager; bv_util m_util; obj_map m_const2bits; ptr_vector m_newbits; expr_ref_vector m_saved; expr_ref m_bit1; expr_ref m_bit0; unsigned long long m_max_memory; // in bytes unsigned m_max_steps; bool m_produce_models; ast_manager & m() const { return m_manager; } bv_util & butil() { return m_util; } bv_util const & butil() const { return m_util; } void cleanup_buffers() { m_saved.finalize(); } rw_cfg(ast_manager & m, params_ref const & p): m_manager(m), m_util(m), m_saved(m), m_bit1(m), m_bit0(m) { m_bit1 = butil().mk_numeral(rational(1), 1); m_bit0 = butil().mk_numeral(rational(0), 1); 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); m_produce_models = p.get_bool("produce_models", false); } bool rewrite_patterns() const { UNREACHABLE(); return false; } bool max_steps_exceeded(unsigned num_steps) const { if (memory::get_allocation_size() > m_max_memory) throw tactic_exception(TACTIC_MAX_MEMORY_MSG); return num_steps > m_max_steps; } typedef ptr_buffer bit_buffer; void get_bits(expr * arg, bit_buffer & bits) { SASSERT(butil().is_concat(arg) || butil().get_bv_size(arg) == 1); if (butil().is_concat(arg)) bits.append(to_app(arg)->get_num_args(), to_app(arg)->get_args()); else bits.push_back(arg); } void mk_const(func_decl * f, expr_ref & result) { SASSERT(f->get_family_id() == null_family_id); SASSERT(f->get_arity() == 0); expr * r; if (m_const2bits.find(f, r)) { result = r; return; } sort * s = f->get_range(); SASSERT(butil().is_bv_sort(s)); unsigned bv_size = butil().get_bv_size(s); if (bv_size == 1) { result = m().mk_const(f); return; } sort * b = butil().mk_sort(1); ptr_buffer bits; for (unsigned i = 0; i < bv_size; i++) { bits.push_back(m().mk_fresh_const(nullptr, b)); m_newbits.push_back(to_app(bits.back())->get_decl()); } r = butil().mk_concat(bits.size(), bits.c_ptr()); m_saved.push_back(r); m_const2bits.insert(f, r); result = r; } void blast_bv_term(expr * t, expr_ref & result) { bit_buffer bits; unsigned bv_size = butil().get_bv_size(t); if (bv_size == 1) { result = t; return; } unsigned i = bv_size; while (i > 0) { --i; bits.push_back(butil().mk_extract(i, i, t)); } result = butil().mk_concat(bits.size(), bits.c_ptr()); } void reduce_eq(expr * arg1, expr * arg2, expr_ref & result) { bit_buffer bits1; bit_buffer bits2; get_bits(arg1, bits1); get_bits(arg2, bits2); SASSERT(bits1.size() == bits2.size()); bit_buffer new_eqs; unsigned i = bits1.size(); while (i > 0) { --i; new_eqs.push_back(m().mk_eq(bits1[i], bits2[i])); } result = m().mk_and(new_eqs.size(), new_eqs.c_ptr()); } void reduce_ite(expr * c, expr * t, expr * e, expr_ref & result) { bit_buffer t_bits; bit_buffer e_bits; get_bits(t, t_bits); get_bits(e, e_bits); SASSERT(t_bits.size() == e_bits.size()); bit_buffer new_ites; unsigned num = t_bits.size(); for (unsigned i = 0; i < num; i++) new_ites.push_back(m().mk_ite(c, t_bits[i], e_bits[i])); result = butil().mk_concat(new_ites.size(), new_ites.c_ptr()); } void reduce_num(func_decl * f, expr_ref & result) { SASSERT(f->get_num_parameters() == 2); SASSERT(f->get_parameter(0).is_rational()); SASSERT(f->get_parameter(1).is_int()); bit_buffer bits; rational v = f->get_parameter(0).get_rational(); rational two(2); unsigned sz = f->get_parameter(1).get_int(); for (unsigned i = 0; i < sz; i++) { if ((v % two).is_zero()) bits.push_back(m_bit0); else bits.push_back(m_bit1); v = div(v, two); } std::reverse(bits.begin(), bits.end()); result = butil().mk_concat(bits.size(), bits.c_ptr()); } void reduce_extract(func_decl * f, expr * arg, expr_ref & result) { bit_buffer arg_bits; get_bits(arg, arg_bits); SASSERT(arg_bits.size() == butil().get_bv_size(arg)); unsigned high = butil().get_extract_high(f); unsigned low = butil().get_extract_low(f); unsigned sz = arg_bits.size(); unsigned start = sz - 1 - high; unsigned end = sz - 1 - low; bit_buffer bits; for (unsigned i = start; i <= end; i++) { bits.push_back(arg_bits[i]); } result = butil().mk_concat(bits.size(), bits.c_ptr()); } void reduce_concat(unsigned num, expr * const * args, expr_ref & result) { bit_buffer bits; bit_buffer arg_bits; for (unsigned i = 0; i < num; i++) { expr * arg = args[i]; arg_bits.reset(); get_bits(arg, arg_bits); bits.append(arg_bits.size(), arg_bits.c_ptr()); } result = butil().mk_concat(bits.size(), bits.c_ptr()); } void reduce_bin_xor(expr * arg1, expr * arg2, expr_ref & result) { bit_buffer bits1; bit_buffer bits2; get_bits(arg1, bits1); get_bits(arg2, bits2); SASSERT(bits1.size() == bits2.size()); bit_buffer new_bits; unsigned num = bits1.size(); for (unsigned i = 0; i < num; i++) { new_bits.push_back(m().mk_ite(m().mk_eq(bits1[i], bits2[i]), m_bit0, m_bit1)); } result = butil().mk_concat(new_bits.size(), new_bits.c_ptr()); } void reduce_xor(unsigned num_args, expr * const * args, expr_ref & result) { SASSERT(num_args > 0); #if 1 if (num_args == 1) { result = args[0]; return; } reduce_bin_xor(args[0], args[1], result); for (unsigned i = 2; i < num_args; i++) { reduce_bin_xor(result, args[i], result); } #else ptr_buffer args_bits; for (unsigned i = 0; i < num_args; i++) { bit_buffer * buff_i = alloc(bit_buffer); get_bits(args[i], *buff_i); args_bits.push_back(buff_i); } bit_buffer new_bits; unsigned sz = butil().get_bv_size(args[0]); for (unsigned i = 0; i < sz; i++) { ptr_buffer eqs; for (unsigned j = 0; j < num_args; j++) { bit_buffer * buff_j = args_bits[j]; eqs.push_back(m().mk_eq(buff_j->get(i), m_bit1)); } expr * cond = m().mk_xor(eqs.size(), eqs.c_ptr()); new_bits.push_back(m().mk_ite(cond, m_bit1, m_bit0)); } result = butil().mk_concat(new_bits.size(), new_bits.c_ptr()); std::for_each(args_bits.begin(), args_bits.end(), delete_proc()); #endif } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { result_pr = nullptr; if (num == 0 && f->get_family_id() == null_family_id && butil().is_bv_sort(f->get_range())) { mk_const(f, result); return BR_DONE; } if (m().is_eq(f)) { SASSERT(num == 2); if (butil().is_bv(args[0])) { reduce_eq(args[0], args[1], result); return BR_DONE; } return BR_FAILED; } if (m().is_ite(f)) { SASSERT(num == 3); if (butil().is_bv(args[1])) { reduce_ite(args[0], args[1], args[2], result); return BR_DONE; } return BR_FAILED; } if (f->get_family_id() == butil().get_family_id()) { switch (f->get_decl_kind()) { case OP_BV_NUM: reduce_num(f, result); return BR_DONE; case OP_EXTRACT: SASSERT(num == 1); reduce_extract(f, args[0], result); return BR_DONE; case OP_CONCAT: reduce_concat(num, args, result); return BR_DONE; case OP_BXOR: reduce_xor(num, args, result); return BR_DONE; default: UNREACHABLE(); return BR_FAILED; } } if (butil().is_bv_sort(f->get_range())) { blast_bv_term(m().mk_app(f, num, args), result); return BR_DONE; } return BR_FAILED; } 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) { UNREACHABLE(); return false; } }; struct rw : public rewriter_tpl { rw_cfg m_cfg; rw(ast_manager & m, params_ref const & p): rewriter_tpl(m, m.proofs_enabled(), m_cfg), m_cfg(m, p) { } }; struct imp { rw m_rw; unsigned m_num_steps; imp(ast_manager & m, params_ref const & p): m_rw(m, p) { } struct not_target {}; struct visitor { family_id m_bv_fid; visitor(family_id bv_fid):m_bv_fid(bv_fid) {} void operator()(var const * n) { throw not_target(); } void operator()(app const * n) { if (n->get_family_id() == m_bv_fid) { switch (n->get_decl_kind()) { case OP_BV_NUM: case OP_EXTRACT: case OP_CONCAT: return; case OP_BXOR: // it doesn't payoff to do the reduction in this case. throw not_target(); default: throw not_target(); } } } void operator()(quantifier const * n) { throw not_target(); } }; bool is_target(goal const & g) const { expr_fast_mark1 visited; unsigned sz = g.size(); visitor proc(m_rw.cfg().butil().get_family_id()); try { for (unsigned i = 0; i < sz; i++) { expr * f = g.form(i); for_each_expr_core(proc, visited, f); } } catch (const not_target &) { return false; } return true; } ast_manager & m() const { return m_rw.m(); } void operator()(goal_ref const & g, goal_ref_buffer & result) { if (!is_target(*g)) throw tactic_exception("bv1 blaster cannot be applied to goal"); tactic_report report("bv1-blaster", *g); m_num_steps = 0; bool proofs_enabled = g->proofs_enabled(); expr_ref new_curr(m()); proof_ref new_pr(m()); unsigned size = g->size(); for (unsigned idx = 0; idx < size; idx++) { if (g->inconsistent()) break; expr * curr = g->form(idx); m_rw(curr, new_curr, new_pr); m_num_steps += m_rw.get_num_steps(); if (proofs_enabled) { proof * pr = g->pr(idx); new_pr = m().mk_modus_ponens(pr, new_pr); } g->update(idx, new_curr, new_pr, g->dep(idx)); } if (g->models_enabled()) g->add(mk_bv1_blaster_model_converter(m(), m_rw.cfg().m_const2bits, m_rw.cfg().m_newbits)); g->inc_depth(); result.push_back(g.get()); m_rw.cfg().cleanup(); } unsigned get_num_steps() const { return m_num_steps; } }; imp * m_imp; params_ref m_params; public: bv1_blaster_tactic(ast_manager & m, params_ref const & p = params_ref()): m_params(p) { m_imp = alloc(imp, m, p); } tactic * translate(ast_manager & m) override { return alloc(bv1_blaster_tactic, m, m_params); } ~bv1_blaster_tactic() override { dealloc(m_imp); } void updt_params(params_ref const & p) override { m_params = p; m_imp->m_rw.cfg().updt_params(p); } void collect_param_descrs(param_descrs & r) override { insert_max_memory(r); insert_max_steps(r); } bool is_target(goal const & g) const { return m_imp->is_target(g); } /** \brief "Blast" bit-vectors of size n in s into bit-vectors of size 1. If s contains other bit-vectors operators different from concat/extract, then this is method is a NO-OP. It also does not support quantifiers. Return a model_converter that converts any model for the updated set into a model for the old set. */ void operator()(goal_ref const & g, goal_ref_buffer & result) override { (*m_imp)(g, result); } void cleanup() override { imp * d = alloc(imp, m_imp->m(), m_params); std::swap(d, m_imp); dealloc(d); } unsigned get_num_steps() const { return m_imp->get_num_steps(); } }; tactic * mk_bv1_blaster_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(bv1_blaster_tactic, m, p)); } class is_qfbv_eq_probe : public probe { public: result operator()(goal const & g) override { bv1_blaster_tactic t(g.m()); return t.is_target(g); } }; probe * mk_is_qfbv_eq_probe() { return alloc(is_qfbv_eq_probe); } z3-z3-4.8.7/src/tactic/bv/bv1_blaster_tactic.h000066400000000000000000000020241356505360400207760ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: bv1_blaster_tactic.h Abstract: Rewriter for "blasting" bit-vectors of size n into bit-vectors of size 1. This rewriter only supports concat and extract operators. This transformation is useful for handling benchmarks that contain many BV equalities. Remark: other operators can be mapped into concat/extract by using the simplifiers. Author: Leonardo (leonardo) 2011-10-25 Notes: --*/ #ifndef BV1_BLASTER_TACTIC_H_ #define BV1_BLASTER_TACTIC_H_ #include "util/params.h" class ast_manager; class tactic; tactic * mk_bv1_blaster_tactic(ast_manager & m, params_ref const & p = params_ref()); probe * mk_is_qfbv_eq_probe(); /* ADD_TACTIC("bv1-blast", "reduce bit-vector expressions into bit-vectors of size 1 (notes: only equality, extract and concat are supported).", "mk_bv1_blaster_tactic(m, p)") ADD_PROBE("is-qfbv-eq", "true if the goal is in a fragment of QF_BV which uses only =, extract, concat.", "mk_is_qfbv_eq_probe()") */ #endif z3-z3-4.8.7/src/tactic/bv/bv_bound_chk_tactic.cpp000066400000000000000000000154301356505360400215550ustar00rootroot00000000000000/*++ Copyright (c) 2016 Microsoft Corporation Module Name: bv_bound_chk_tactic.cpp Abstract: Author: Mikolas Janota Revision History: --*/ #include "tactic/bv/bv_bound_chk_tactic.h" #include "ast/ast.h" #include "ast/rewriter/rewriter.h" #include "ast/rewriter/rewriter_def.h" #include "ast/rewriter/bv_bounds.h" #include "ast/rewriter/rewriter_params.hpp" #include "ast/rewriter/bool_rewriter.h" struct bv_bound_chk_stats { unsigned m_unsats; unsigned m_singletons; unsigned m_reduces; bv_bound_chk_stats() : m_unsats(0), m_singletons(0), m_reduces(0) {}; }; struct bv_bound_chk_rewriter_cfg : public default_rewriter_cfg { ast_manager & m_m; unsigned m_bv_ineq_consistency_test_max; bool_rewriter m_b_rw; unsigned long long m_max_steps; unsigned long long m_max_memory; // in bytes bv_bound_chk_stats& m_stats; bv_bound_chk_rewriter_cfg(ast_manager & m, bv_bound_chk_stats& stats) : m_m(m), m_b_rw(m), m_stats(stats) {} ~bv_bound_chk_rewriter_cfg() {} void updt_params(params_ref const & _p) { rewriter_params p(_p); m_bv_ineq_consistency_test_max = p.bv_ineq_consistency_test_max(); m_max_memory = p.max_memory(); m_max_steps = p.max_steps(); } ast_manager & m() const { return m_m; } bool rewrite_patterns() const { return false; } bool flat_assoc(func_decl * f) const { return true; } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { const br_status st = reduce_app_core(f, num, args, result, result_pr); CTRACE("bv_bound_chk_step", st != BR_FAILED, tout << f->get_name() << "\n"; for (unsigned i = 0; i < num; i++) tout << mk_ismt2_pp(args[i], m()) << "\n"; tout << "---------->\n" << mk_ismt2_pp(result, m()) << "\n";); return st; } br_status reduce_app_core(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { result_pr = nullptr; const family_id fid = f->get_family_id(); if (fid != m_b_rw.get_fid()) return BR_FAILED; 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().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; } bool max_steps_exceeded(unsigned long long num_steps) const { if (num_steps > m_max_steps) return true; if (memory::get_allocation_size() > m_max_memory) throw tactic_exception(TACTIC_MAX_MEMORY_MSG); return false; } void reset_statistics() { m_stats.m_unsats = 0; m_stats.m_singletons = 0; m_stats.m_reduces = 0; } void collect_statistics(statistics & st) const { st.update("unsat bv bounds", m_stats.m_unsats); st.update("bv singletons", m_stats.m_singletons); st.update("bv reduces", m_stats.m_reduces); } }; struct bv_bound_chk_rewriter : public rewriter_tpl { bv_bound_chk_rewriter_cfg m_cfg; bv_bound_chk_rewriter(ast_manager & m, params_ref const & p, bv_bound_chk_stats& stats) : rewriter_tpl(m, false, m_cfg) , m_cfg(m, stats) { updt_params(p); } ~bv_bound_chk_rewriter() override {} void updt_params(params_ref const & _p) { m_cfg.updt_params(_p); } void collect_statistics(statistics & st) const { m_cfg.collect_statistics(st); } void reset_statistics() { m_cfg.reset_statistics(); } }; class bv_bound_chk_tactic : public tactic { class imp; imp * m_imp; params_ref m_params; bv_bound_chk_stats m_stats; public: bv_bound_chk_tactic(ast_manager & m, params_ref const & p); void operator()(goal_ref const & g, goal_ref_buffer & result) override; ~bv_bound_chk_tactic() override; tactic * translate(ast_manager & m) override; void updt_params(params_ref const & p) override; void cleanup() override; void collect_statistics(statistics & st) const override; void reset_statistics() override; }; class bv_bound_chk_tactic::imp { bv_bound_chk_rewriter m_rw; public: imp(ast_manager & m, params_ref const & p, bv_bound_chk_stats& stats) : m_rw(m, p, stats) { } virtual ~imp() { } ast_manager& m() { return m_rw.m(); } void operator()(goal_ref const & g) { SASSERT(g->is_well_sorted()); tactic_report report("bv-bound-chk", *g); ast_manager& m(g->m()); expr_ref new_curr(m); const unsigned size = g->size(); for (unsigned idx = 0; idx < size; idx++) { if (g->inconsistent()) break; expr * curr = g->form(idx); m_rw(curr, new_curr); g->update(idx, new_curr); } m_rw.m_cfg.cleanup(); } virtual void updt_params(params_ref const & p) { m_rw.updt_params(p); } void collect_statistics(statistics & st) const { m_rw.collect_statistics(st); } void reset_statistics() { m_rw.reset_statistics(); } }; bv_bound_chk_tactic::bv_bound_chk_tactic(ast_manager & m, params_ref const & p) : m_params(p) { m_imp = alloc(imp, m, p, m_stats); } bv_bound_chk_tactic::~bv_bound_chk_tactic() { dealloc(m_imp); } void bv_bound_chk_tactic::operator()(goal_ref const & g, goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); fail_if_proof_generation("bv-bound-chk", g); fail_if_unsat_core_generation("bv-bound-chk", g); TRACE("bv-bound-chk", g->display(tout << "before:"); tout << std::endl;); result.reset(); m_imp->operator()(g); g->inc_depth(); result.push_back(g.get()); TRACE("bv-bound-chk", g->display(tout << "after:");); SASSERT(g->is_well_sorted()); } tactic * bv_bound_chk_tactic::translate(ast_manager & m) { return alloc(bv_bound_chk_tactic, m, m_params); } void bv_bound_chk_tactic::updt_params(params_ref const & p) { m_params = p; m_imp->updt_params(p); } void bv_bound_chk_tactic::cleanup() { imp * d = alloc(imp, m_imp->m(), m_params, m_stats); std::swap(d, m_imp); dealloc(d); } void bv_bound_chk_tactic::collect_statistics(statistics & st) const { m_imp->collect_statistics(st); } void bv_bound_chk_tactic::reset_statistics() { m_imp->reset_statistics(); } tactic* mk_bv_bound_chk_tactic(ast_manager & m, params_ref const & p) { return alloc(bv_bound_chk_tactic, m, p); } z3-z3-4.8.7/src/tactic/bv/bv_bound_chk_tactic.h000066400000000000000000000010461356505360400212200ustar00rootroot00000000000000 /*++ Copyright (c) 2016 Microsoft Corporation Module Name: bv_bound_chk_tactic.h Abstract: Author: Mikolas Janota Revision History: --*/ #ifndef BV_BOUND_CHK_TACTIC_H_ #define BV_BOUND_CHK_TACTIC_H_ #include "tactic/tactical.h" #include "util/params.h" #include "ast/ast.h" tactic* mk_bv_bound_chk_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("bv_bound_chk", "attempts to detect inconsistencies of bounds on bv expressions.", "mk_bv_bound_chk_tactic(m, p)") */ #endif /* BV_BOUND_CHK_TACTIC_H_*/ z3-z3-4.8.7/src/tactic/bv/bv_bounds_tactic.cpp000066400000000000000000000626331356505360400211220ustar00rootroot00000000000000/*++ Copyright (c) 2016 Microsoft Corporation Module Name: bv_bounds_tactic.cpp Abstract: Contextual bounds simplification tactic. Author: Nuno Lopes (nlopes) 2016-2-12 Nikolaj Bjorner (nbjorner) --*/ #include "tactic/bv/bv_bounds_tactic.h" #include "tactic/core/ctx_simplify_tactic.h" #include "tactic/core/dom_simplify_tactic.h" #include "ast/bv_decl_plugin.h" #include "ast/ast_pp.h" #include static uint64_t uMaxInt(unsigned sz) { SASSERT(sz <= 64); return ULLONG_MAX >> (64u - sz); } namespace { struct interval { // l < h: [l, h] // l > h: [0, h] U [l, UMAX_INT] uint64_t l, h; unsigned sz; bool tight; interval() {} interval(uint64_t l, uint64_t h, unsigned sz, bool tight = false) : l(l), h(h), sz(sz), tight(tight) { // canonicalize full set if (is_wrapped() && l == h + 1) { this->l = 0; this->h = uMaxInt(sz); } SASSERT(invariant()); } bool invariant() const { return l <= uMaxInt(sz) && h <= uMaxInt(sz) && (!is_wrapped() || l != h+1); } bool is_full() const { return l == 0 && h == uMaxInt(sz); } bool is_wrapped() const { return l > h; } bool is_singleton() const { return l == h; } bool operator==(const interval& b) const { SASSERT(sz == b.sz); return l == b.l && h == b.h && tight == b.tight; } bool operator!=(const interval& b) const { return !(*this == b); } bool implies(const interval& b) const { if (b.is_full()) return true; if (is_full()) return false; if (is_wrapped()) { // l >= b.l >= b.h >= h return b.is_wrapped() && h <= b.h && l >= b.l; } else if (b.is_wrapped()) { // b.l > b.h >= h >= l // h >= l >= b.l > b.h return h <= b.h || l >= b.l; } else { // return l >= b.l && h <= b.h; } } /// return false if intersection is unsat bool intersect(const interval& b, interval& result) const { if (is_full() || *this == b) { result = b; return true; } if (b.is_full()) { result = *this; return true; } if (is_wrapped()) { if (b.is_wrapped()) { if (h >= b.l) { result = b; } else if (b.h >= l) { result = *this; } else { result = interval(std::max(l, b.l), std::min(h, b.h), sz); } } else { return b.intersect(*this, result); } } else if (b.is_wrapped()) { // ... b.h ... l ... h ... b.l .. if (h < b.l && l > b.h) { return false; } // ... l ... b.l ... h ... if (h >= b.l && l <= b.h) { result = b; } else if (h >= b.l) { result = interval(b.l, h, sz); } else { // ... l .. b.h .. h .. b.l ... SASSERT(l <= b.h); result = interval(l, std::min(h, b.h), sz); } } else { if (l > b.h || h < b.l) return false; // 0 .. l.. l' ... h ... h' result = interval(std::max(l, b.l), std::min(h, b.h), sz, tight && b.tight); } return true; } /// return false if negation is empty bool negate(interval& result) const { if (!tight) { result = interval(0, uMaxInt(sz), true); return true; } if (is_full()) return false; if (l == 0) { result = interval(h + 1, uMaxInt(sz), sz); } else if (uMaxInt(sz) == h) { result = interval(0, l - 1, sz); } else { result = interval(h + 1, l - 1, sz); } return true; } }; #ifdef _TRACE std::ostream& operator<<(std::ostream& o, const interval& I) { o << "[" << I.l << ", " << I.h << "]"; return o; } #endif struct undo_bound { expr* e; interval b; bool fresh; undo_bound(expr* e, const interval& b, bool fresh) : e(e), b(b), fresh(fresh) {} }; class bv_bounds_simplifier : public ctx_simplify_tactic::simplifier { typedef obj_map map; typedef obj_map expr_set; typedef obj_map expr_cnt; ast_manager& m; params_ref m_params; bool m_propagate_eq; bv_util m_bv; vector m_scopes; map m_bound; svector m_expr_vars; svector m_bound_exprs; bool is_number(expr *e, uint64_t& n, unsigned& sz) const { rational r; if (m_bv.is_numeral(e, r, sz) && sz <= 64) { n = r.get_uint64(); return true; } return false; } bool is_bound(expr *e, expr*& v, interval& b) const { uint64_t n; expr *lhs = nullptr, *rhs = nullptr; unsigned sz; if (m_bv.is_bv_ule(e, lhs, rhs)) { if (is_number(lhs, n, sz)) { // C ule x <=> x uge C if (m_bv.is_numeral(rhs)) return false; b = interval(n, uMaxInt(sz), sz, true); v = rhs; return true; } if (is_number(rhs, n, sz)) { // x ule C b = interval(0, n, sz, true); v = lhs; return true; } } else if (m_bv.is_bv_sle(e, lhs, rhs)) { if (is_number(lhs, n, sz)) { // C sle x <=> x sge C if (m_bv.is_numeral(rhs)) return false; b = interval(n, (1ull << (sz-1)) - 1, sz, true); v = rhs; return true; } if (is_number(rhs, n, sz)) { // x sle C b = interval(1ull << (sz-1), n, sz, true); v = lhs; return true; } } else if (m.is_eq(e, lhs, rhs)) { if (is_number(lhs, n, sz)) { if (m_bv.is_numeral(rhs)) return false; b = interval(n, n, sz, true); v = rhs; return true; } if (is_number(rhs, n, sz)) { b = interval(n, n, sz, true); v = lhs; return true; } } return false; } #if 0 expr_set* get_expr_vars(expr* t) { unsigned id = t->get_id(); m_expr_vars.reserve(id + 1); expr_set*& entry = m_expr_vars[id]; if (entry) return entry; expr_set* set = alloc(expr_set); entry = set; if (!m_bv.is_numeral(t)) set->insert(t, true); if (!is_app(t)) return set; app* a = to_app(t); for (unsigned i = 0; i < a->get_num_args(); ++i) { expr_set* set_arg = get_expr_vars(a->get_arg(i)); for (expr_set::iterator I = set_arg->begin(), E = set_arg->end(); I != E; ++I) { set->insert(I->m_key, true); } } return set; } #endif #if 0 expr_cnt* get_expr_bounds(expr* t) { unsigned id = t->get_id(); m_bound_exprs.reserve(id + 1); expr_cnt*& entry = m_bound_exprs[id]; if (entry) return entry; expr_cnt* set = alloc(expr_cnt); entry = set; if (!is_app(t)) return set; interval b; expr* e; if (is_bound(t, e, b)) { set->insert_if_not_there2(e, 0)->get_data().m_value++; } app* a = to_app(t); for (unsigned i = 0; i < a->get_num_args(); ++i) { expr_cnt* set_arg = get_expr_bounds(a->get_arg(i)); for (expr_cnt::iterator I = set_arg->begin(), E = set_arg->end(); I != E; ++I) { set->insert_if_not_there2(I->m_key, 0)->get_data().m_value += I->m_value; } } return set; } #endif public: bv_bounds_simplifier(ast_manager& m, params_ref const& p) : m(m), m_params(p), m_bv(m) { updt_params(p); } void updt_params(params_ref const & p) override { m_propagate_eq = p.get_bool("propagate_eq", false); } static void get_param_descrs(param_descrs& r) { r.insert("propagate-eq", CPK_BOOL, "(default: false) propagate equalities from inequalities"); } ~bv_bounds_simplifier() override { for (unsigned i = 0, e = m_expr_vars.size(); i < e; ++i) { dealloc(m_expr_vars[i]); } for (unsigned i = 0, e = m_bound_exprs.size(); i < e; ++i) { dealloc(m_bound_exprs[i]); } } bool assert_expr(expr * t, bool sign) override { while (m.is_not(t, t)) { sign = !sign; } interval b; expr* t1; if (is_bound(t, t1, b)) { SASSERT(!m_bv.is_numeral(t1)); if (sign) VERIFY(b.negate(b)); TRACE("bv", tout << (sign?"(not ":"") << mk_pp(t, m) << (sign ? ")" : "") << ": " << mk_pp(t1, m) << " in " << b << "\n";); map::obj_map_entry* e = m_bound.find_core(t1); if (e) { interval& old = e->get_data().m_value; interval intr; if (!old.intersect(b, intr)) return false; if (old == intr) return true; m_scopes.insert(undo_bound(t1, old, false)); old = intr; } else { m_bound.insert(t1, b); m_scopes.insert(undo_bound(t1, interval(), true)); } } return true; } bool simplify(expr* t, expr_ref& result) override { expr* t1; interval b; if (m_bound.find(t, b) && b.is_singleton()) { result = m_bv.mk_numeral(b.l, m_bv.get_bv_size(t)); return true; } if (!m.is_bool(t)) return false; bool sign = false; while (m.is_not(t, t)) { sign = !sign; } if (!is_bound(t, t1, b)) return false; if (sign && b.tight) { sign = false; if (!b.negate(b)) { result = m.mk_false(); return true; } } interval ctx, intr; result = nullptr; if (b.is_full() && b.tight) { result = m.mk_true(); } else if (m_bound.find(t1, ctx)) { if (ctx.implies(b)) { result = m.mk_true(); } else if (!b.intersect(ctx, intr)) { result = m.mk_false(); } else if (m_propagate_eq && intr.is_singleton()) { result = m.mk_eq(t1, m_bv.mk_numeral(rational(intr.l, rational::ui64()), m.get_sort(t1))); } } CTRACE("bv", result != 0, tout << mk_pp(t, m) << " " << b << " (ctx: " << ctx << ") (intr: " << intr << "): " << result << "\n";); if (sign && result != 0) result = m.mk_not(result); return result != 0; } // check if t contains v ptr_vector todo; bool contains(expr* t, expr* v) { ast_fast_mark1 mark; todo.push_back(t); while (!todo.empty()) { t = todo.back(); todo.pop_back(); if (mark.is_marked(t)) { continue; } if (t == v) { todo.reset(); return true; } mark.mark(t); if (!is_app(t)) { continue; } app* a = to_app(t); todo.append(a->get_num_args(), a->get_args()); } return false; } bool contains_bound(expr* t) { ast_fast_mark1 mark1; ast_fast_mark2 mark2; todo.push_back(t); while (!todo.empty()) { t = todo.back(); todo.pop_back(); if (mark1.is_marked(t)) { continue; } mark1.mark(t); if (!is_app(t)) { continue; } interval b; expr* e; if (is_bound(t, e, b)) { if (mark2.is_marked(e)) { todo.reset(); return true; } mark2.mark(e); if (m_bound.contains(e)) { todo.reset(); return true; } } app* a = to_app(t); todo.append(a->get_num_args(), a->get_args()); } return false; } bool may_simplify(expr* t) override { if (m_bv.is_numeral(t)) return false; while (m.is_not(t, t)); for (auto & v : m_bound) { if (contains(t, v.m_key)) return true; } #if 0 expr_set* used_exprs = get_expr_vars(t); for (map::iterator I = m_bound.begin(), E = m_bound.end(); I != E; ++I) { if (contains(t, I->m_key)) return true; if (I->m_value.is_singleton() && used_exprs->contains(I->m_key)) return true; } #endif expr* t1; interval b; // skip common case: single bound constraint without any context for simplification if (is_bound(t, t1, b)) { return b.is_full() || m_bound.contains(t1); } if (contains_bound(t)) { return true; } #if 0 expr_cnt* bounds = get_expr_bounds(t); for (expr_cnt::iterator I = bounds->begin(), E = bounds->end(); I != E; ++I) { if (I->m_value > 1 || m_bound.contains(I->m_key)) return true; } #endif return false; } void pop(unsigned num_scopes) override { TRACE("bv", tout << "pop: " << num_scopes << "\n";); if (m_scopes.empty()) return; unsigned target = m_scopes.size() - num_scopes; if (target == 0) { m_bound.reset(); m_scopes.reset(); return; } for (unsigned i = m_scopes.size()-1; i >= target; --i) { undo_bound& undo = m_scopes[i]; SASSERT(m_bound.contains(undo.e)); if (undo.fresh) { m_bound.erase(undo.e); } else { m_bound.insert(undo.e, undo.b); } } m_scopes.shrink(target); } simplifier * translate(ast_manager & m) override { return alloc(bv_bounds_simplifier, m, m_params); } unsigned scope_level() const override { return m_scopes.size(); } }; class dom_bv_bounds_simplifier : public dom_simplifier { typedef obj_map map; typedef obj_map expr_set; typedef obj_map expr_cnt; ast_manager& m; params_ref m_params; bool m_propagate_eq; bv_util m_bv; vector m_scopes; map m_bound; svector m_expr_vars; svector m_bound_exprs; bool is_number(expr *e, uint64_t& n, unsigned& sz) const { rational r; if (m_bv.is_numeral(e, r, sz) && sz <= 64) { n = r.get_uint64(); return true; } return false; } bool is_bound(expr *e, expr*& v, interval& b) const { uint64_t n; expr *lhs = nullptr, *rhs = nullptr; unsigned sz = 0; if (m_bv.is_bv_ule(e, lhs, rhs)) { if (is_number(lhs, n, sz)) { // C ule x <=> x uge C if (m_bv.is_numeral(rhs)) return false; b = interval(n, uMaxInt(sz), sz, true); v = rhs; return true; } if (is_number(rhs, n, sz)) { // x ule C b = interval(0, n, sz, true); v = lhs; return true; } } else if (m_bv.is_bv_sle(e, lhs, rhs)) { if (is_number(lhs, n, sz)) { // C sle x <=> x sge C if (m_bv.is_numeral(rhs)) return false; b = interval(n, (1ull << (sz-1)) - 1, sz, true); v = rhs; return true; } if (is_number(rhs, n, sz)) { // x sle C b = interval(1ull << (sz-1), n, sz, true); v = lhs; return true; } } else if (m.is_eq(e, lhs, rhs)) { if (is_number(lhs, n, sz)) { if (m_bv.is_numeral(rhs)) return false; b = interval(n, n, sz, true); v = rhs; return true; } if (is_number(rhs, n, sz)) { b = interval(n, n, sz, true); v = lhs; return true; } } return false; } public: dom_bv_bounds_simplifier(ast_manager& m, params_ref const& p) : m(m), m_params(p), m_bv(m) { updt_params(p); } virtual void updt_params(params_ref const & p) { m_propagate_eq = p.get_bool("propagate_eq", false); } static void get_param_descrs(param_descrs& r) { r.insert("propagate-eq", CPK_BOOL, "(default: false) propagate equalities from inequalities"); } ~dom_bv_bounds_simplifier() override { for (unsigned i = 0, e = m_expr_vars.size(); i < e; ++i) { dealloc(m_expr_vars[i]); } for (unsigned i = 0, e = m_bound_exprs.size(); i < e; ++i) { dealloc(m_bound_exprs[i]); } } bool assert_expr(expr * t, bool sign) override { while (m.is_not(t, t)) { sign = !sign; } interval b; expr* t1; if (is_bound(t, t1, b)) { SASSERT(!m_bv.is_numeral(t1)); if (sign) VERIFY(b.negate(b)); TRACE("bv", tout << (sign?"(not ":"") << mk_pp(t, m) << (sign ? ")" : "") << ": " << mk_pp(t1, m) << " in " << b << "\n";); map::obj_map_entry* e = m_bound.find_core(t1); if (e) { interval& old = e->get_data().m_value; interval intr; if (!old.intersect(b, intr)) return false; if (old == intr) return true; m_scopes.push_back(undo_bound(t1, old, false)); old = intr; } else { m_bound.insert(t1, b); m_scopes.push_back(undo_bound(t1, interval(), true)); } } return true; } void operator()(expr_ref& r) override { expr* t1, * t = r; interval b; if (m_bound.find(t, b) && b.is_singleton()) { r = m_bv.mk_numeral(b.l, m_bv.get_bv_size(t)); return; } if (!m.is_bool(t)) return; bool sign = false; while (m.is_not(t, t)) { sign = !sign; } if (!is_bound(t, t1, b)) return; if (sign && b.tight) { sign = false; if (!b.negate(b)) { r = m.mk_false(); return; } } interval ctx, intr; bool was_updated = true; if (b.is_full() && b.tight) { r = m.mk_true(); } else if (m_bound.find(t1, ctx)) { if (ctx.implies(b)) { r = m.mk_true(); } else if (!b.intersect(ctx, intr)) { r = m.mk_false(); } else if (m_propagate_eq && intr.is_singleton()) { r = m.mk_eq(t1, m_bv.mk_numeral(rational(intr.l, rational::ui64()), m.get_sort(t1))); } else { was_updated = false; } } else { was_updated = false; } TRACE("bv", tout << mk_pp(t, m) << " " << b << " (ctx: " << ctx << ") (intr: " << intr << "): " << r << "\n";); if (sign && was_updated) r = m.mk_not(r); } // check if t contains v ptr_vector todo; bool contains(expr* t, expr* v) { ast_fast_mark1 mark; todo.push_back(t); while (!todo.empty()) { t = todo.back(); todo.pop_back(); if (mark.is_marked(t)) { continue; } if (t == v) { todo.reset(); return true; } mark.mark(t); if (!is_app(t)) { continue; } app* a = to_app(t); todo.append(a->get_num_args(), a->get_args()); } return false; } bool contains_bound(expr* t) { ast_fast_mark1 mark1; ast_fast_mark2 mark2; todo.push_back(t); while (!todo.empty()) { t = todo.back(); todo.pop_back(); if (mark1.is_marked(t)) { continue; } mark1.mark(t); if (!is_app(t)) { continue; } interval b; expr* e; if (is_bound(t, e, b)) { if (mark2.is_marked(e)) { todo.reset(); return true; } mark2.mark(e); if (m_bound.contains(e)) { todo.reset(); return true; } } app* a = to_app(t); todo.append(a->get_num_args(), a->get_args()); } return false; } void pop(unsigned num_scopes) override { TRACE("bv", tout << "pop: " << num_scopes << "\n";); if (m_scopes.empty()) return; unsigned target = m_scopes.size() - num_scopes; if (target == 0) { m_bound.reset(); m_scopes.reset(); return; } for (unsigned i = m_scopes.size()-1; i >= target; --i) { undo_bound& undo = m_scopes[i]; SASSERT(m_bound.contains(undo.e)); if (undo.fresh) { m_bound.erase(undo.e); } else { m_bound.insert(undo.e, undo.b); } } m_scopes.shrink(target); } dom_simplifier * translate(ast_manager & m) override { return alloc(dom_bv_bounds_simplifier, m, m_params); } unsigned scope_level() const override { return m_scopes.size(); } }; } tactic * mk_bv_bounds_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(ctx_simplify_tactic, m, alloc(bv_bounds_simplifier, m, p), p)); } tactic * mk_dom_bv_bounds_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(dom_simplify_tactic, m, alloc(dom_bv_bounds_simplifier, m, p), p)); } z3-z3-4.8.7/src/tactic/bv/bv_bounds_tactic.h000066400000000000000000000014331356505360400205560ustar00rootroot00000000000000/*++ Copyright (c) 2016 Microsoft Corporation Module Name: bv_bounds_tactic.h Abstract: Contextual bounds simplification tactic. Author: Nuno Lopes (nlopes) 2016-2-12 Nikolaj Bjorner (nbjorner) --*/ #ifndef BV_BOUNDS_TACTIC_H_ #define BV_BOUNDS_TACTIC_H_ #include "tactic/tactic.h" tactic * mk_bv_bounds_tactic(ast_manager & m, params_ref const & p = params_ref()); tactic * mk_dom_bv_bounds_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("propagate-bv-bounds", "propagate bit-vector bounds by simplifying implied or contradictory bounds.", "mk_bv_bounds_tactic(m, p)") ADD_TACTIC("propagate-bv-bounds-new", "propagate bit-vector bounds by simplifying implied or contradictory bounds.", "mk_dom_bv_bounds_tactic(m, p)") */ #endif z3-z3-4.8.7/src/tactic/bv/bv_size_reduction_tactic.cpp000066400000000000000000000412411356505360400226460ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: bv_size_reduction_tactic.cpp Abstract: Reduce the number of bits used to encode constants, by using signed bounds. Example: suppose x is a bit-vector of size 8, and we have signed bounds for x such that: -2 <= x <= 2 Then, x can be replaced by ((sign-extend 5) k) where k is a fresh bit-vector constant of size 3. Author: Leonardo (leonardo) 2012-02-19 Notes: --*/ #include "tactic/tactical.h" #include "ast/bv_decl_plugin.h" #include "ast/rewriter/expr_replacer.h" #include "tactic/generic_model_converter.h" #include "ast/ast_smt2_pp.h" namespace { class bv_size_reduction_tactic : public tactic { typedef rational numeral; typedef generic_model_converter bv_size_reduction_mc; ast_manager & m; bv_util m_util; obj_map m_signed_lowers; obj_map m_signed_uppers; obj_map m_unsigned_lowers; obj_map m_unsigned_uppers; ref m_mc; generic_model_converter_ref m_fmc; scoped_ptr m_replacer; bool m_produce_models; 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); if (entry->get_data().m_value < k) { // improve bound entry->get_data().m_value = k; } } void update_signed_upper(app * v, numeral const & k) { // v <= k obj_map::obj_map_entry * entry = m_signed_uppers.insert_if_not_there2(v, k); if (k < entry->get_data().m_value) { // improve bound entry->get_data().m_value = k; } } void update_unsigned_lower(app * v, numeral const & k) { SASSERT(k > numeral(0)); // k <= v obj_map::obj_map_entry * entry = m_unsigned_lowers.insert_if_not_there2(v, k); if (entry->get_data().m_value < k) { // improve bound entry->get_data().m_value = k; } } void update_unsigned_upper(app * v, numeral const & k) { SASSERT(k > numeral(0)); // v <= k obj_map::obj_map_entry * entry = m_unsigned_uppers.insert_if_not_there2(v, k); if (k < entry->get_data().m_value) { // improve bound entry->get_data().m_value = k; } } void collect_bounds(goal const & g) { unsigned sz = g.size(); numeral val; unsigned bv_sz; expr * f, * lhs, * rhs; for (unsigned i = 0; i < sz; i++) { bool negated = false; f = g.form(i); if (m.is_not(f)) { negated = true; f = to_app(f)->get_arg(0); } if (m_util.is_bv_sle(f, lhs, rhs)) { bv_sz = m_util.get_bv_size(lhs); if (is_uninterp_const(lhs) && m_util.is_numeral(rhs, val, bv_sz)) { TRACE("bv_size_reduction", tout << (negated?"not ":"") << mk_ismt2_pp(f, m) << std::endl; ); // v <= k val = m_util.norm(val, bv_sz, true); if (negated) { val += numeral(1); if (m_util.norm(val, bv_sz, true) != val) { // bound is infeasible. } else { update_signed_lower(to_app(lhs), val); } } else update_signed_upper(to_app(lhs), val); } else if (is_uninterp_const(rhs) && m_util.is_numeral(lhs, val, bv_sz)) { TRACE("bv_size_reduction", tout << (negated?"not ":"") << mk_ismt2_pp(f, m) << std::endl; ); // k <= v val = m_util.norm(val, bv_sz, true); if (negated) { val -= numeral(1); if (m_util.norm(val, bv_sz, true) != val) { // bound is infeasible. } else { update_signed_upper(to_app(rhs), val); } } else update_signed_lower(to_app(rhs), val); } } #if 0 else if (m_util.is_bv_ule(f, lhs, rhs)) { if (is_uninterp_const(lhs) && m_util.is_numeral(rhs, val, bv_sz)) { TRACE("bv_size_reduction", tout << (negated?"not ":"") << mk_ismt2_pp(f, m) << std::endl; ); // v <= k if (negated) update_unsigned_lower(to_app(lhs), val+numeral(1)); else update_unsigned_upper(to_app(lhs), val); } else if (is_uninterp_const(rhs) && m_util.is_numeral(lhs, val, bv_sz)) { TRACE("bv_size_reduction", tout << (negated?"not ":"") << mk_ismt2_pp(f, m) << std::endl; ); // k <= v if (negated) update_unsigned_upper(to_app(rhs), val-numeral(1)); else update_unsigned_lower(to_app(rhs), val); } } #endif } } void checkpoint() { if (m.canceled()) throw tactic_exception(m.limit().get_cancel_msg()); } void run(goal & g, model_converter_ref & mc) { if (g.inconsistent()) return; TRACE("before_bv_size_reduction", g.display(tout);); m_produce_models = g.models_enabled(); mc = nullptr; m_mc = nullptr; unsigned num_reduced = 0; { tactic_report report("bv-size-reduction", g); collect_bounds(g); // create substitution expr_substitution subst(m); if (!(m_signed_lowers.empty() || m_signed_uppers.empty())) { TRACE("bv_size_reduction", tout << "m_signed_lowers: " << std::endl; for (obj_map::iterator it = m_signed_lowers.begin(); it != m_signed_lowers.end(); it++) tout << mk_ismt2_pp(it->m_key, m) << " >= " << it->m_value.to_string() << std::endl; tout << "m_signed_uppers: " << std::endl; for (obj_map::iterator it = m_signed_uppers.begin(); it != m_signed_uppers.end(); it++) tout << mk_ismt2_pp(it->m_key, m) << " <= " << it->m_value.to_string() << std::endl; ); obj_map::iterator it = m_signed_lowers.begin(); obj_map::iterator end = m_signed_lowers.end(); for (; it != end; ++it) { app * v = it->m_key; unsigned bv_sz = m_util.get_bv_size(v); numeral l = m_util.norm(it->m_value, bv_sz, true); obj_map::obj_map_entry * entry = m_signed_uppers.find_core(v); if (entry != nullptr) { numeral u = m_util.norm(entry->get_data().m_value, bv_sz, true); TRACE("bv_size_reduction", tout << l << " <= " << v->get_decl()->get_name() << " <= " << u << "\n";); expr * new_def = nullptr; app * new_const = nullptr; if (l > u) { g.assert_expr(m.mk_false()); return; } else if (l == u) { new_def = m_util.mk_numeral(l, m.get_sort(v)); } else { // l < u if (l.is_neg()) { unsigned l_nb = (-l).get_num_bits(); unsigned v_nb = m_util.get_bv_size(v); if (u.is_neg()) { // l <= v <= u <= 0 unsigned i_nb = l_nb; TRACE("bv_size_reduction", tout << " l <= " << v->get_decl()->get_name() << " <= u <= 0 " << " --> " << i_nb << " bits\n";); if (i_nb < v_nb) { new_const = m.mk_fresh_const(nullptr, m_util.mk_sort(i_nb)); new_def = m_util.mk_concat(m_util.mk_numeral(numeral(-1), v_nb - i_nb), new_const); } } else { // l <= v <= 0 <= u unsigned u_nb = u.get_num_bits(); unsigned i_nb = ((l_nb > u_nb) ? l_nb : u_nb) + 1; TRACE("bv_size_reduction", tout << " l <= " << v->get_decl()->get_name() << " <= 0 <= u " << " --> " << i_nb << " bits\n";); if (i_nb < v_nb) { new_const = m.mk_fresh_const(nullptr, m_util.mk_sort(i_nb)); new_def = m_util.mk_sign_extend(v_nb - i_nb, new_const); } } } else { // 0 <= l <= v <= u unsigned u_nb = u.get_num_bits(); unsigned v_nb = m_util.get_bv_size(v); TRACE("bv_size_reduction", tout << l << " <= " << v->get_decl()->get_name() << " <= " << u << " --> " << u_nb << " bits\n";); if (u_nb < v_nb) { new_const = m.mk_fresh_const(nullptr, m_util.mk_sort(u_nb)); new_def = m_util.mk_concat(m_util.mk_numeral(numeral(0), v_nb - u_nb), new_const); } } } if (new_def) { subst.insert(v, new_def); if (m_produce_models) { if (!m_mc) m_mc = alloc(bv_size_reduction_mc, m, "bv_size_reduction"); m_mc->add(v, new_def); if (!m_fmc && new_const) m_fmc = alloc(generic_model_converter, m, "bv_size_reduction"); if (new_const) m_fmc->hide(new_const); } num_reduced++; } } } } #if 0 if (!(m_unsigned_lowers.empty() && m_unsigned_uppers.empty())) { TRACE("bv_size_reduction", tout << "m_unsigned_lowers: " << std::endl; for (obj_map::iterator it = m_unsigned_lowers.begin(); it != m_unsigned_lowers.end(); it++) tout << mk_ismt2_pp(it->m_key, m) << " >= " << it->m_value.to_string() << std::endl; tout << "m_unsigned_uppers: " << std::endl; for (obj_map::iterator it = m_unsigned_uppers.begin(); it != m_unsigned_uppers.end(); it++) tout << mk_ismt2_pp(it->m_key, m) << " <= " << it->m_value.to_string() << std::endl; ); obj_map::iterator it = m_unsigned_uppers.begin(); obj_map::iterator end = m_unsigned_uppers.end(); for (; it != end; ++it) { app * v = it->m_key; unsigned bv_sz = m_util.get_bv_size(v); numeral u = m_util.norm(it->m_value, bv_sz, false); obj_map::obj_map_entry * entry = m_signed_lowers.find_core(v); numeral l = (entry != 0) ? m_util.norm(entry->get_data().m_value, bv_sz, false) : numeral(0); obj_map::obj_map_entry * lse = m_signed_lowers.find_core(v); obj_map::obj_map_entry * use = m_signed_uppers.find_core(v); if ((lse != 0 && lse->get_data().m_value > l) && (use != 0 && use->get_data().m_value < u)) continue; // Skip, we had better signed bounds. if (lse != 0 && lse->get_data().m_value > l) l = lse->get_data().m_value; if (use != 0 && use->get_data().m_value < u) u = use->get_data().m_value; TRACE("bv_size_reduction", tout << l << " <= " << v->get_decl()->get_name() << " <= " << u << "\n";); expr * new_def = 0; app * new_const = 0; if (l > u) { g.assert_expr(m.mk_false()); return; } else if (l == u) { new_def = m_util.mk_numeral(l, m.get_sort(v)); } else { // 0 <= l <= v <= u unsigned u_nb = u.get_num_bits(); unsigned v_nb = m_util.get_bv_size(v); if (u_nb < v_nb) { new_def = m_util.mk_concat(m_util.mk_numeral(numeral(0), v_nb - u_nb), new_const); new_const = m.mk_fresh_const(0, m_util.mk_sort(u_nb)); } } if (new_def) { subst.insert(v, new_def); if (m_produce_models) { if (!m_mc) m_mc = alloc(bv_size_reduction_mc, m); m_mc->insert(v->get_decl(), new_def); if (!m_fmc && new_const) m_fmc = alloc(generic_model_converter, m, "bv_size_reduction"); if (new_const) m_fmc->hide(new_const); } num_reduced++; TRACE("bv_size_reduction", tout << "New definition = " << mk_ismt2_pp(new_def, m) << "\n";); } } } #endif if (subst.empty()) return; m_replacer->set_substitution(&subst); unsigned sz = g.size(); expr * f; expr_ref new_f(m); for (unsigned i = 0; i < sz; i++) { if (g.inconsistent()) return; f = g.form(i); (*m_replacer)(f, new_f); g.update(i, new_f); } mc = m_mc.get(); if (m_fmc) { mc = concat(m_fmc.get(), mc.get()); } m_mc = nullptr; m_fmc = nullptr; } report_tactic_progress(":bv-reduced", num_reduced); TRACE("after_bv_size_reduction", g.display(tout); if (m_mc) m_mc->display(tout);); } }; void bv_size_reduction_tactic::operator()(goal_ref const & g, goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); fail_if_proof_generation("bv-size-reduction", g); fail_if_unsat_core_generation("bv-size-reduction", g); result.reset(); model_converter_ref mc; run(*(g.get()), mc); g->inc_depth(); g->add(mc.get()); result.push_back(g.get()); SASSERT(g->is_well_sorted()); } } tactic * mk_bv_size_reduction_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(bv_size_reduction_tactic, m)); } z3-z3-4.8.7/src/tactic/bv/bv_size_reduction_tactic.h000066400000000000000000000013611356505360400223120ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: bv_size_reduction.h Abstract: Reduce the number of bits used to encode constants, by using signed bounds. Example: suppose x is a bit-vector of size 8, and we have signed bounds for x such that: -2 <= x <= 2 Then, x can be replaced by ((sign-extend 5) k) where k is a fresh bit-vector constant of size 3. Author: Leonardo (leonardo) 2012-02-19 Notes: --*/ #pragma once #include "util/params.h" class ast_manager; class tactic; tactic * mk_bv_size_reduction_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("reduce-bv-size", "try to reduce bit-vector sizes using inequalities.", "mk_bv_size_reduction_tactic(m, p)") */ z3-z3-4.8.7/src/tactic/bv/bvarray2uf_rewriter.cpp000066400000000000000000000365521356505360400216210ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: bvarray2uf_rewriter.cpp Abstract: Rewriter that rewrites bit-vector arrays into bit-vector (uninterpreted) functions. Author: Christoph (cwinter) 2015-11-04 Notes: --*/ #include "ast/bv_decl_plugin.h" #include "ast/array_decl_plugin.h" #include "util/params.h" #include "ast/ast_pp.h" #include "tactic/bv/bvarray2uf_rewriter.h" #include "ast/rewriter/rewriter_def.h" #include "util/ref_util.h" // [1] C. M. Wintersteiger, Y. Hamadi, and L. de Moura: Efficiently Solving // Quantified Bit-Vector Formulas, in Formal Methods in System Design, // vol. 42, no. 1, pp. 3-23, Springer, 2013. bvarray2uf_rewriter_cfg::bvarray2uf_rewriter_cfg(ast_manager & m, params_ref const & p) : m_manager(m), m_out(m), m_bindings(m), m_bv_util(m), m_array_util(m), m_fmc(nullptr), extra_assertions(m) { updt_params(p); // We need to make sure that the manager has the BV and array plugins loaded. symbol s_bv("bv"); if (!m_manager.has_plugin(s_bv)) m_manager.register_plugin(s_bv, alloc(bv_decl_plugin)); symbol s_array("array"); if (!m_manager.has_plugin(s_array)) m_manager.register_plugin(s_array, alloc(array_decl_plugin)); } bvarray2uf_rewriter_cfg::~bvarray2uf_rewriter_cfg() { dec_ref_map_key_values(m_manager, m_arrays_fs); } void bvarray2uf_rewriter_cfg::reset() {} sort * bvarray2uf_rewriter_cfg::get_index_sort(expr * e) { return get_index_sort(m_manager.get_sort(e)); } sort * bvarray2uf_rewriter_cfg::get_index_sort(sort * s) { SASSERT(s->get_num_parameters() >= 2); unsigned total_width = 0; for (unsigned i = 0; i < s->get_num_parameters() - 1; i++) { parameter const & p = s->get_parameter(i); SASSERT(p.is_ast() && is_sort(to_sort(p.get_ast()))); SASSERT(m_bv_util.is_bv_sort(to_sort(p.get_ast()))); total_width += m_bv_util.get_bv_size(to_sort(p.get_ast())); } return m_bv_util.mk_sort(total_width); } sort * bvarray2uf_rewriter_cfg::get_value_sort(expr * e) { return get_value_sort(m_manager.get_sort(e)); } sort * bvarray2uf_rewriter_cfg::get_value_sort(sort * s) { SASSERT(s->get_num_parameters() >= 2); parameter const & p = s->get_parameter(s->get_num_parameters() - 1); SASSERT(p.is_ast() && is_sort(to_sort(p.get_ast()))); return to_sort(p.get_ast()); } bool bvarray2uf_rewriter_cfg::is_bv_array(expr * e) { return is_bv_array(m_manager.get_sort(e)); } bool bvarray2uf_rewriter_cfg::is_bv_array(sort * s) { if (!m_array_util.is_array(s)) return false; SASSERT(s->get_num_parameters() >= 2); for (unsigned i = 0; i < s->get_num_parameters(); i++) { parameter const & p = s->get_parameter(i); if (!p.is_ast() || !is_sort(to_sort(p.get_ast())) || !m_bv_util.is_bv_sort(to_sort(p.get_ast()))) return false; } return true; } func_decl_ref bvarray2uf_rewriter_cfg::mk_uf_for_array(expr * e) { SASSERT(is_bv_array(e)); if (m_array_util.is_as_array(e)) return func_decl_ref(static_cast(to_app(e)->get_decl()->get_parameter(0).get_ast()), m_manager); else { func_decl * bv_f = nullptr; if (!m_arrays_fs.find(e, bv_f)) { sort * domain = get_index_sort(e); sort * range = get_value_sort(e); bv_f = m_manager.mk_fresh_func_decl("f_t", "", 1, &domain, range); TRACE("bvarray2uf_rw", tout << "for " << mk_ismt2_pp(e, m_manager) << " new func_decl is " << mk_ismt2_pp(bv_f, m_manager) << std::endl; ); if (is_uninterp_const(e)) { if (m_fmc) m_fmc->add(e, m_array_util.mk_as_array(bv_f)); } else if (m_fmc) m_fmc->hide(bv_f); m_arrays_fs.insert(e, bv_f); m_manager.inc_ref(e); m_manager.inc_ref(bv_f); } else { TRACE("bvarray2uf_rw", tout << "for " << mk_ismt2_pp(e, m_manager) << " found " << mk_ismt2_pp(bv_f, m_manager) << std::endl; ); } return func_decl_ref(bv_f, m_manager); } } br_status bvarray2uf_rewriter_cfg::reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { br_status res = BR_FAILED; if (m_manager.is_eq(f) && is_bv_array(f->get_domain()[0])) { SASSERT(num == 2); // From [1]: Finally, we replace equations of the form t = s, // where t and s are arrays, with \forall x . f_t(x) = f_s(x). if (m_manager.are_equal(args[0], args[1])) { result = m_manager.mk_true(); res = BR_DONE; } else { func_decl_ref f_t(mk_uf_for_array(args[0]), m_manager); func_decl_ref f_s(mk_uf_for_array(args[1]), m_manager); sort * sorts[1] = { get_index_sort(m_manager.get_sort(args[0])) }; symbol names[1] = { symbol("x") }; var_ref x(m_manager.mk_var(0, sorts[0]), m_manager); expr_ref body(m_manager); body = m_manager.mk_eq(m_manager.mk_app(f_t, x.get()), m_manager.mk_app(f_s, x.get())); result = m_manager.mk_forall(1, sorts, names, body); res = BR_DONE; } } else if (m_manager.is_distinct(f) && is_bv_array(f->get_domain()[0])) { result = m_manager.mk_distinct_expanded(num, args); res = BR_REWRITE1; } else if (m_manager.is_term_ite(f) && is_bv_array(f->get_range())) { expr_ref c(args[0], m_manager); func_decl_ref f_t(mk_uf_for_array(args[1]), m_manager); func_decl_ref f_f(mk_uf_for_array(args[2]), m_manager); TRACE("bvarray2uf_rw", tout << "(ite " << c << ", " << f_t->get_name() << ", " << f_f->get_name() << ")" << std::endl;); sort * sorts[1] = { get_index_sort(m_manager.get_sort(args[1])) }; symbol names[1] = { symbol("x") }; var_ref x(m_manager.mk_var(0, sorts[0]), m_manager); app_ref f_a(m_manager), f_ta(m_manager), f_fa(m_manager); f_a = m_manager.mk_app(f, num, args); f_ta = m_manager.mk_app(f_t, x.get()); f_fa = m_manager.mk_app(f_f, x.get()); app_ref e(m_manager); func_decl_ref itefd(m_manager); e = m_manager.mk_ite(c, f_ta, f_fa); func_decl * bv_f = nullptr; if (!m_arrays_fs.find(f_a, bv_f)) { sort * domain = get_index_sort(args[1]); sort * range = get_value_sort(args[1]); bv_f = m_manager.mk_fresh_func_decl("f_t", "", 1, &domain, range); TRACE("bvarray2uf_rw", tout << mk_ismt2_pp(e, m_manager) << " -> " << bv_f->get_name() << std::endl; ); if (is_uninterp_const(e)) { if (m_fmc) m_fmc->add(e, m_array_util.mk_as_array(bv_f)); } else if (m_fmc) m_fmc->hide(bv_f); m_arrays_fs.insert(e, bv_f); m_manager.inc_ref(e); m_manager.inc_ref(bv_f); } expr_ref q(m_manager), body(m_manager); body = m_manager.mk_eq(m_manager.mk_app(bv_f, x), e); q = m_manager.mk_forall(1, sorts, names, body); extra_assertions.push_back(q); result = m_array_util.mk_as_array(bv_f); TRACE("bvarray2uf_rw", tout << "result: " << mk_ismt2_pp(result, m_manager) << ")" << std::endl;); res = BR_DONE; } else if (f->get_family_id() == m_manager.get_basic_family_id() && is_bv_array(f->get_range())) { NOT_IMPLEMENTED_YET(); } else if (f->get_family_id() == null_family_id) { TRACE("bvarray2uf_rw", tout << "UF APP: " << f->get_name() << std::endl; ); bool has_bv_arrays = false; func_decl_ref f_t(m_manager); for (unsigned i = 0; i < num; i++) { if (is_bv_array(args[i])) { SASSERT(m_array_util.is_as_array(args[i])); has_bv_arrays = true; } } expr_ref t(m_manager); t = m_manager.mk_app(f, num, args); if (is_bv_array(t)) { // From [1]: For every array term t we create a fresh uninterpreted function f_t. f_t = mk_uf_for_array(t); result = m_array_util.mk_as_array(f_t); res = BR_DONE; } else if (has_bv_arrays) { result = t; res = BR_DONE; } else res = BR_FAILED; } else if (m_array_util.get_family_id() == f->get_family_id()) { TRACE("bvarray2uf_rw", tout << "APP: " << f->get_name() << std::endl; ); if (m_array_util.is_select(f)) { SASSERT(num == 2); expr * t = args[0]; expr * i = args[1]; TRACE("bvarray2uf_rw", tout << "select; array: " << mk_ismt2_pp(t, m()) << " index: " << mk_ismt2_pp(i, m()) << std::endl;); if (!is_bv_array(t)) res = BR_FAILED; else { // From [1]: Then, we replace terms of the form select(t, i) with f_t(i). func_decl_ref f_t(mk_uf_for_array(t), m_manager); result = m_manager.mk_app(f_t, i); res = BR_DONE; } } else { if (!is_bv_array(f->get_range())) res = BR_FAILED; else { if (m_array_util.is_const(f)) { SASSERT(num == 1); expr_ref t(m_manager.mk_app(f, num, args), m_manager); expr * v = args[0]; func_decl_ref f_t(mk_uf_for_array(t), m_manager); result = m_array_util.mk_as_array(f_t); res = BR_DONE; // Add \forall x . f_t(x) = v sort * sorts[1] = { get_index_sort(f->get_range()) }; symbol names[1] = { symbol("x") }; var_ref x(m_manager.mk_var(0, sorts[0]), m_manager); expr_ref body(m_manager); body = m_manager.mk_eq(m_manager.mk_app(f_t, x.get()), v); expr_ref frllx(m_manager.mk_forall(1, sorts, names, body), m_manager); extra_assertions.push_back(frllx); } else if (m_array_util.is_as_array(f)) { res = BR_FAILED; } else if (m_array_util.is_map(f)) { SASSERT(f->get_num_parameters() == 1); SASSERT(f->get_parameter(0).is_ast()); expr_ref t(m_manager.mk_app(f, num, args), m_manager); func_decl_ref f_t(mk_uf_for_array(t), m_manager); func_decl_ref map_f(to_func_decl(f->get_parameter(0).get_ast()), m_manager); func_decl_ref_vector ss(m_manager); for (unsigned i = 0; i < num; i++) { SASSERT(m_array_util.is_array(args[i])); func_decl_ref fd(mk_uf_for_array(args[i]), m_manager); ss.push_back(fd); } // Add \forall x . f_t(x) = map_f(a[x], b[x], ...) sort * sorts[1] = { get_index_sort(f->get_range()) }; symbol names[1] = { symbol("x") }; var_ref x(m_manager.mk_var(0, sorts[0]), m_manager); expr_ref_vector new_args(m_manager); for (unsigned i = 0; i < num; i++) new_args.push_back(m_manager.mk_app(ss[i].get(), x.get())); expr_ref body(m_manager); body = m_manager.mk_eq(m_manager.mk_app(f_t, x.get()), m_manager.mk_app(map_f, num, new_args.c_ptr())); expr_ref frllx(m_manager.mk_forall(1, sorts, names, body), m_manager); extra_assertions.push_back(frllx); result = m_array_util.mk_as_array(f_t); res = BR_DONE; } else if (m_array_util.is_store(f)) { SASSERT(num == 3); expr * s = args[0]; expr * i = args[1]; expr * v = args[2]; TRACE("bvarray2uf_rw", tout << "store; array: " << mk_ismt2_pp(s, m()) << " index: " << mk_ismt2_pp(i, m()) << " value: " << mk_ismt2_pp(v, m()) << std::endl;); if (!is_bv_array(s)) res = BR_FAILED; else { expr_ref t(m_manager.mk_app(f, num, args), m_manager); // From [1]: For every term t of the form store(s, i, v), we add the universal // formula \forall x . x = i \vee f_t(x) = f_s(x), and the ground atom f_t(i) = v. func_decl_ref f_s(mk_uf_for_array(s), m_manager); func_decl_ref f_t(mk_uf_for_array(t), m_manager); result = m_array_util.mk_as_array(f_t); res = BR_DONE; sort * sorts[1] = { get_index_sort(f->get_range()) }; symbol names[1] = { symbol("x") }; var_ref x(m_manager.mk_var(0, sorts[0]), m_manager); expr_ref body(m_manager); body = m_manager.mk_or(m_manager.mk_eq(x, i), m_manager.mk_eq(m_manager.mk_app(f_t, x.get()), m_manager.mk_app(f_s, x.get()))); expr_ref frllx(m_manager.mk_forall(1, sorts, names, body), m_manager); extra_assertions.push_back(frllx); expr_ref ground_atom(m_manager); ground_atom = m_manager.mk_eq(m_manager.mk_app(f_t, i), v); extra_assertions.push_back(ground_atom); TRACE("bvarray2uf_rw", tout << "ground atom: " << mk_ismt2_pp(ground_atom, m()) << std::endl; ); } } else { NOT_IMPLEMENTED_YET(); res = BR_FAILED; } } } } CTRACE("bvarray2uf_rw", res == BR_DONE, tout << "result: " << mk_ismt2_pp(result, m()) << std::endl; ); return res; } bool bvarray2uf_rewriter_cfg::pre_visit(expr * t) { TRACE("bvarray2uf_rw_q", tout << "pre_visit: " << mk_ismt2_pp(t, m()) << std::endl;); if (is_quantifier(t)) { quantifier * q = to_quantifier(t); TRACE("bvarray2uf_rw_q", tout << "pre_visit quantifier [" << q->get_id() << "]: " << mk_ismt2_pp(q->get_expr(), m()) << std::endl;); sort_ref_vector new_bindings(m_manager); for (unsigned i = 0; i < q->get_num_decls(); i++) new_bindings.push_back(q->get_decl_sort(i)); SASSERT(new_bindings.size() == q->get_num_decls()); m_bindings.append(new_bindings); } return true; } bool bvarray2uf_rewriter_cfg::reduce_quantifier(quantifier * old_q, expr * new_body, expr * const * new_patterns, expr * const * new_no_patterns, expr_ref & result, proof_ref & result_pr) { NOT_IMPLEMENTED_YET(); return true; } bool bvarray2uf_rewriter_cfg::reduce_var(var * t, expr_ref & result, proof_ref & result_pr) { if (t->get_idx() >= m_bindings.size()) return false; NOT_IMPLEMENTED_YET(); return true; } template class rewriter_tpl; z3-z3-4.8.7/src/tactic/bv/bvarray2uf_rewriter.h000066400000000000000000000042401356505360400212530ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: bvarray2uf_rewriter.h Abstract: Rewriter that rewrites bit-vector arrays into bit-vector (uninterpreted) functions. Author: Christoph (cwinter) 2015-11-04 Notes: --*/ #ifndef BVARRAY2UF_REWRITER_H_ #define BVARRAY2UF_REWRITER_H_ #include "ast/rewriter/rewriter.h" #include "tactic/generic_model_converter.h" class bvarray2uf_rewriter_cfg : public default_rewriter_cfg { ast_manager & m_manager; expr_ref_vector m_out; sort_ref_vector m_bindings; bv_util m_bv_util; array_util m_array_util; generic_model_converter * m_fmc; obj_map m_arrays_fs; public: bvarray2uf_rewriter_cfg(ast_manager & m, params_ref const & p); ~bvarray2uf_rewriter_cfg(); ast_manager & m() const { return m_manager; } void updt_params(params_ref const & p) {} void reset(); bool pre_visit(expr * t); br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr); 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); bool reduce_var(var * t, expr_ref & result, proof_ref & result_pr); expr_ref_vector extra_assertions; void set_mcs(generic_model_converter * fmc) { m_fmc = fmc; } protected: sort * get_index_sort(expr * e); sort * get_index_sort(sort * s); sort * get_value_sort(expr * e); sort * get_value_sort(sort * s); bool is_bv_array(expr * e); bool is_bv_array(sort * e); func_decl_ref mk_uf_for_array(expr * e); }; struct bvarray2uf_rewriter : public rewriter_tpl { bvarray2uf_rewriter_cfg m_cfg; bvarray2uf_rewriter(ast_manager & m, params_ref const & p) : rewriter_tpl(m, m.proofs_enabled(), m_cfg), m_cfg(m, p) { } void set_mcs(generic_model_converter * fmc) { m_cfg.set_mcs(fmc); } }; #endif z3-z3-4.8.7/src/tactic/bv/bvarray2uf_tactic.cpp000066400000000000000000000076161356505360400212240ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: bvarray2uf_tactic.cpp Abstract: Tactic that rewrites bit-vector arrays into bit-vector (uninterpreted) functions. Author: Christoph (cwinter) 2015-11-04 Notes: --*/ #include "tactic/tactical.h" #include "ast/bv_decl_plugin.h" #include "ast/rewriter/expr_replacer.h" #include "tactic/generic_model_converter.h" #include "ast/ast_smt2_pp.h" #include "tactic/bv/bvarray2uf_tactic.h" #include "tactic/bv/bvarray2uf_rewriter.h" class bvarray2uf_tactic : public tactic { struct imp { ast_manager & m_manager; bool m_produce_models; bool m_produce_proofs; bool m_produce_cores; bvarray2uf_rewriter m_rw; ast_manager & m() { return m_manager; } imp(ast_manager & m, params_ref const & p) : m_manager(m), m_produce_models(false), m_produce_proofs(false), m_produce_cores(false), m_rw(m, p) { updt_params(p); } void checkpoint() { if (m_manager.canceled()) throw tactic_exception(m_manager.limit().get_cancel_msg()); } void operator()(goal_ref const & g, goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); tactic_report report("bvarray2uf", *g); result.reset(); fail_if_unsat_core_generation("bvarray2uf", g); TRACE("bvarray2uf", tout << "Before: " << std::endl; g->display(tout); ); m_produce_models = g->models_enabled(); model_converter_ref mc; if (m_produce_models) { generic_model_converter * fmc = alloc(generic_model_converter, m_manager, "bvarray2uf"); mc = fmc; m_rw.set_mcs(fmc); } m_rw.reset(); expr_ref new_curr(m_manager); proof_ref new_pr(m_manager); unsigned size = g->size(); for (unsigned idx = 0; idx < size; idx++) { if (g->inconsistent()) break; expr * curr = g->form(idx); m_rw(curr, new_curr, new_pr); if (m_produce_proofs) { proof * pr = g->pr(idx); new_pr = m_manager.mk_modus_ponens(pr, new_pr); } g->update(idx, new_curr, new_pr, g->dep(idx)); } for (unsigned i = 0; i < m_rw.m_cfg.extra_assertions.size(); i++) g->assert_expr(m_rw.m_cfg.extra_assertions[i].get()); g->inc_depth(); g->add(mc.get()); result.push_back(g.get()); TRACE("bvarray2uf", tout << "After: " << std::endl; g->display(tout);); SASSERT(g->is_well_sorted()); } void updt_params(params_ref const & p) { } }; imp * m_imp; params_ref m_params; public: bvarray2uf_tactic(ast_manager & m, params_ref const & p) : m_params(p) { m_imp = alloc(imp, m, p); } tactic * translate(ast_manager & m) override { return alloc(bvarray2uf_tactic, m, m_params); } ~bvarray2uf_tactic() override { dealloc(m_imp); } void updt_params(params_ref const & p) override { m_params = p; m_imp->updt_params(p); } void collect_param_descrs(param_descrs & r) override { insert_produce_models(r); } void operator()(goal_ref const & in, goal_ref_buffer & result) override { (*m_imp)(in, result); } void cleanup() override { ast_manager & m = m_imp->m(); imp * d = alloc(imp, m, m_params); std::swap(d, m_imp); dealloc(d); } }; tactic * mk_bvarray2uf_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(bvarray2uf_tactic, m, p)); } z3-z3-4.8.7/src/tactic/bv/bvarray2uf_tactic.h000066400000000000000000000011371356505360400206610ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: bvarray2ufbvarray2uf_tactic.h Abstract: Tactic that rewrites bit-vector arrays into bit-vector (uninterpreted) functions. Author: Christoph (cwinter) 2015-11-04 Notes: --*/ #ifndef BV_ARRAY2UF_TACTIC_H_ #define BV_ARRAY2UF_TACTIC_H_ #include "util/params.h" class ast_manager; class tactic; tactic * mk_bvarray2uf_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("bvarray2uf", "Rewrite bit-vector arrays into bit-vector (uninterpreted) functions.", "mk_bvarray2uf_tactic(m, p)") */ #endif z3-z3-4.8.7/src/tactic/bv/dt2bv_tactic.cpp000066400000000000000000000113501356505360400201500ustar00rootroot00000000000000/*++ Copyright (c) 2016 Microsoft Corporation Module Name: dt2bv_tactic.cpp Abstract: Tactic that eliminates finite domain data-types. Author: nbjorner 2016-07-22 Revision History: Possible extension is to handle tuple types over finite domains. --*/ #include "tactic/bv/dt2bv_tactic.h" #include "tactic/tactical.h" #include "tactic/generic_model_converter.h" #include "ast/datatype_decl_plugin.h" #include "ast/bv_decl_plugin.h" #include "ast/rewriter/rewriter_def.h" #include "ast/rewriter/var_subst.h" #include "ast/ast_util.h" #include "ast/rewriter/enum2bv_rewriter.h" #include "ast/ast_pp.h" class dt2bv_tactic : public tactic { ast_manager& m; params_ref m_params; datatype_util m_dt; bv_util m_bv; obj_hashtable m_fd_sorts; obj_hashtable m_non_fd_sorts; bool is_fd(expr* a) { return is_fd(get_sort(a)); } bool is_fd(sort* a) { return m_dt.is_enum_sort(a); } struct check_fd { dt2bv_tactic& m_t; ast_manager& m; check_fd(dt2bv_tactic& t): m_t(t), m(t.m) {} void operator()(app* a) { if (m.is_eq(a)) { // no-op } else if (m.is_distinct(a)) { // no-op } else if (m_t.m_dt.is_recognizer(a->get_decl()) && m_t.is_fd(a->get_arg(0))) { m_t.m_fd_sorts.insert(get_sort(a->get_arg(0))); } else if (m_t.is_fd(a) && a->get_num_args() > 0) { m_t.m_non_fd_sorts.insert(get_sort(a)); args_cannot_be_fd(a); } else if (m_t.is_fd(a)) { m_t.m_fd_sorts.insert(get_sort(a)); } else { args_cannot_be_fd(a); } } void args_cannot_be_fd(app* a) { for (expr* arg : *a) { if (m_t.is_fd(arg)) { m_t.m_non_fd_sorts.insert(get_sort(arg)); } } } void operator()(var * v) { if (m_t.is_fd(v)) { m_t.m_fd_sorts.insert(get_sort(v)); } } void operator()(quantifier* q) {} }; struct sort_pred : public i_sort_pred { dt2bv_tactic& m_t; sort_pred(dt2bv_tactic& t): m_t(t) {} ~sort_pred() override {} bool operator()(sort* s) override { return m_t.m_fd_sorts.contains(s); } }; sort_pred m_is_fd; public: dt2bv_tactic(ast_manager& m, params_ref const& p): m(m), m_params(p), m_dt(m), m_bv(m), m_is_fd(*this) {} tactic * translate(ast_manager & m) override { return alloc(dt2bv_tactic, m, m_params); } void updt_params(params_ref const & p) override { } void collect_param_descrs(param_descrs & r) override { } void operator()(goal_ref const & g, goal_ref_buffer & result) override { bool produce_proofs = g->proofs_enabled(); tactic_report report("dt2bv", *g); unsigned size = g->size(); expr_fast_mark1 visited; check_fd proc(*this); for (unsigned i = 0; i < size; ++i) { quick_for_each_expr(proc, visited, g->form(i)); } for (sort* s : m_non_fd_sorts) m_fd_sorts.remove(s); if (!m_fd_sorts.empty()) { ref filter = alloc(generic_model_converter, m, "dt2bv"); enum2bv_rewriter rw(m, m_params); rw.set_is_fd(&m_is_fd); expr_ref new_curr(m); proof_ref new_pr(m); for (unsigned idx = 0; idx < size; idx++) { rw(g->form(idx), 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)); } expr_ref_vector bounds(m); rw.flush_side_constraints(bounds); for (expr* b : bounds) g->assert_expr(b); for (auto const& kv : rw.enum2bv()) filter->hide(kv.m_value); for (auto const& kv : rw.enum2def()) filter->add(kv.m_key, kv.m_value); g->add(filter.get()); report_tactic_progress(":fd-num-translated", rw.num_translated()); } g->inc_depth(); result.push_back(g.get()); TRACE("dt2bv", g->display(tout);); SASSERT(g->is_well_sorted()); } void cleanup() override { m_fd_sorts.reset(); m_non_fd_sorts.reset(); } }; tactic * mk_dt2bv_tactic(ast_manager & m, params_ref const & p) { return alloc(dt2bv_tactic, m, p); } z3-z3-4.8.7/src/tactic/bv/dt2bv_tactic.h000066400000000000000000000010471356505360400176170ustar00rootroot00000000000000/*++ Copyright (c) 2016 Microsoft Corporation Module Name: dt2bv_tactic.h Abstract: Tactic that eliminates finite domain data-types. Author: nbjorner 2016-07-22 Revision History: --*/ #ifndef DT2BV_TACTIC_H_ #define DT2BV_TACTIC_H_ #include "util/params.h" #include "util/obj_hashtable.h" class ast_manager; class tactic; tactic * mk_dt2bv_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("dt2bv", "eliminate finite domain data-types. Replace by bit-vectors.", "mk_dt2bv_tactic(m, p)") */ #endif z3-z3-4.8.7/src/tactic/bv/elim_small_bv_tactic.cpp000066400000000000000000000237461356505360400217500ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: elim_small_bv.h Abstract: Tactic that eliminates small, quantified bit-vectors. Author: Christoph (cwinter) 2015-11-09 Revision History: --*/ #include "tactic/tactical.h" #include "ast/rewriter/rewriter_def.h" #include "tactic/generic_model_converter.h" #include "ast/bv_decl_plugin.h" #include "ast/used_vars.h" #include "ast/well_sorted.h" #include "ast/rewriter/var_subst.h" #include "ast/rewriter/th_rewriter.h" #include "tactic/bv/elim_small_bv_tactic.h" namespace { class elim_small_bv_tactic : public tactic { struct rw_cfg : public default_rewriter_cfg { ast_manager & m; params_ref m_params; bv_util m_util; th_rewriter m_simp; ref m_mc; unsigned m_max_bits; unsigned long long m_max_steps; unsigned long long m_max_memory; // in bytes bool m_produce_models; sort_ref_vector m_bindings; unsigned long m_num_eliminated; rw_cfg(ast_manager & _m, params_ref const & p) : m(_m), m_params(p), m_util(_m), m_simp(_m), m_bindings(_m), m_num_eliminated(0) { updt_params(p); m_max_steps = UINT_MAX; } bool max_steps_exceeded(unsigned long long num_steps) const { if (num_steps > m_max_steps) return true; if (memory::get_allocation_size() > m_max_memory) throw tactic_exception(TACTIC_MAX_MEMORY_MSG); return false; } bool is_small_bv(sort * s) { return m_util.is_bv_sort(s) && m_util.get_bv_size(s) <= m_max_bits; } expr_ref replace_var(used_vars & uv, unsigned num_decls, unsigned max_var_idx_p1, unsigned idx, sort * s, expr * e, expr * replacement) { 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); ptr_vector substitution; substitution.resize(num_decls, nullptr); substitution[num_decls - idx - 1] = replacement; // (VAR 0) is in the first position of substitution; (VAR num_decls-1) is in the last position. for (unsigned i = 0; i < max_var_idx_p1; i++) substitution.push_back(nullptr); // (VAR num_decls) ... (VAR num_decls+sz-1); are in positions num_decls .. num_decls+sz-1 std::reverse(substitution.c_ptr(), substitution.c_ptr() + substitution.size()); // (VAR 0) should be in the last position of substitution. TRACE("elim_small_bv", tout << "substitution: " << std::endl; for (unsigned k = 0; k < substitution.size(); k++) { expr * se = substitution[k]; tout << k << " = "; if (se == 0) tout << "0"; else tout << mk_ismt2_pp(se, m); tout << std::endl; }); var_subst vsbst(m); res = vsbst(e, substitution.size(), substitution.c_ptr()); SASSERT(is_well_sorted(m, res)); proof_ref pr(m); m_simp(res, res, pr); TRACE("elim_small_bv", tout << "replace done: " << mk_ismt2_pp(res, m) << std::endl;); return res; } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { TRACE("elim_small_bv_app", expr_ref tmp(m.mk_app(f, num, args), m); tout << "reduce " << tmp << std::endl; ); return BR_FAILED; } bool reduce_quantifier( quantifier * q, expr * old_body, expr * const * new_patterns, expr * const * new_no_patterns, expr_ref & result, proof_ref & result_pr) { if (is_lambda(q)) { return false; } TRACE("elim_small_bv", tout << "reduce_quantifier " << mk_ismt2_pp(q, m) << std::endl; ); unsigned long long num_steps = 0; unsigned curr_sz = m_bindings.size(); SASSERT(q->get_num_decls() <= curr_sz); unsigned num_decls = q->get_num_decls(); unsigned old_sz = curr_sz - num_decls; used_vars uv; uv(q); SASSERT(is_well_sorted(m, q)); unsigned max_var_idx_p1 = uv.get_max_found_var_idx_plus_1(); expr_ref body(old_body, m); for (unsigned i = num_decls-1; i != ((unsigned)-1) && !max_steps_exceeded(num_steps); i--) { sort * s = q->get_decl_sort(i); unsigned bv_sz = m_util.get_bv_size(s); if (is_small_bv(s) && !max_steps_exceeded(num_steps)) { TRACE("elim_small_bv", tout << "eliminating " << q->get_decl_name(i) << "; sort = " << mk_ismt2_pp(s, m) << "; body = " << mk_ismt2_pp(body, m) << std::endl;); 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); new_bodies.push_back(replace_var(uv, num_decls, max_var_idx_p1, i, s, body, n)); num_steps++; } TRACE("elim_small_bv", tout << "new bodies: " << std::endl; for (unsigned k = 0; k < new_bodies.size(); k++) tout << mk_ismt2_pp(new_bodies[k].get(), m) << std::endl; ); body = is_forall(q) ? m.mk_and(new_bodies.size(), new_bodies.c_ptr()) : m.mk_or(new_bodies.size(), new_bodies.c_ptr()); SASSERT(is_well_sorted(m, body)); proof_ref pr(m); m_simp(body, body, pr); m_num_eliminated++; } } quantifier_ref new_q(m); new_q = m.update_quantifier(q, body); unused_vars_eliminator el(m, m_params); result = el(new_q); TRACE("elim_small_bv", tout << "elimination result: " << mk_ismt2_pp(result, m) << std::endl; ); result_pr = nullptr; // proofs NIY m_bindings.shrink(old_sz); return true; } bool pre_visit(expr * t) { TRACE("elim_small_bv_pre", tout << "pre_visit: " << mk_ismt2_pp(t, m) << std::endl;); if (is_quantifier(t)) { quantifier * q = to_quantifier(t); TRACE("elim_small_bv", tout << "pre_visit quantifier [" << q->get_id() << "]: " << mk_ismt2_pp(q->get_expr(), m) << std::endl;); sort_ref_vector new_bindings(m); for (unsigned i = 0; i < q->get_num_decls(); i++) new_bindings.push_back(q->get_decl_sort(i)); SASSERT(new_bindings.size() == q->get_num_decls()); m_bindings.append(new_bindings); } return true; } void updt_params(params_ref const & p) { m_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); m_max_bits = p.get_uint("max_bits", 4); } }; struct rw : public rewriter_tpl { rw_cfg m_cfg; rw(ast_manager & m, params_ref const & p) : rewriter_tpl(m, m.proofs_enabled(), m_cfg), m_cfg(m, p) { } }; ast_manager & m; rw m_rw; 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) { } tactic * translate(ast_manager & m) override { return alloc(elim_small_bv_tactic, m, m_params); } void updt_params(params_ref const & p) override { m_params = p; m_rw.cfg().updt_params(p); } void collect_param_descrs(param_descrs & r) override { insert_max_memory(r); insert_max_steps(r); r.insert("max_bits", CPK_UINT, "(default: 4) maximum bit-vector size of quantified bit-vectors to be eliminated."); } void operator()(goal_ref const & g, goal_ref_buffer & result) override { 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 { 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)); } z3-z3-4.8.7/src/tactic/bv/elim_small_bv_tactic.h000066400000000000000000000007771356505360400214140ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: elim_small_bv.h Abstract: Tactic that eliminates small, quantified bit-vectors. Author: Christoph (cwinter) 2015-11-09 Revision History: --*/ #pragma once #include "util/params.h" class ast_manager; class tactic; tactic * mk_elim_small_bv_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("elim-small-bv", "eliminate small, quantified bit-vectors by expansion.", "mk_elim_small_bv_tactic(m, p)") */ z3-z3-4.8.7/src/tactic/bv/max_bv_sharing_tactic.cpp000066400000000000000000000247111356505360400221230ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: max_bv_sharing_tactic.cpp Abstract: Rewriter for "maximing" the number of shared terms. The idea is to rewrite AC terms to maximize sharing. This rewriter is particularly useful for reducing the number of Adders and Multipliers before "bit-blasting". Author: Leonardo de Moura (leonardo) 2011-12-29. Revision History: --*/ #include "tactic/tactical.h" #include "ast/bv_decl_plugin.h" #include "ast/rewriter/rewriter_def.h" #include "util/obj_pair_hashtable.h" #include "ast/ast_lt.h" class max_bv_sharing_tactic : public tactic { struct rw_cfg : public default_rewriter_cfg { typedef std::pair expr_pair; typedef obj_pair_hashtable set; bv_util m_util; set m_add_apps; set m_mul_apps; set m_xor_apps; set m_or_apps; unsigned long long m_max_memory; unsigned m_max_steps; unsigned m_max_args; ast_manager & m() const { return m_util.get_manager(); } rw_cfg(ast_manager & m, params_ref const & p): m_util(m) { updt_params(p); } void cleanup() { m_add_apps.finalize(); m_mul_apps.finalize(); m_or_apps.finalize(); m_xor_apps.finalize(); } 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); m_max_args = p.get_uint("max_args", 128); } bool max_steps_exceeded(unsigned num_steps) const { if (memory::get_allocation_size() > m_max_memory) throw tactic_exception(TACTIC_MAX_MEMORY_MSG); return num_steps > m_max_steps; } set & f2set(func_decl * f) { switch (f->get_decl_kind()) { case OP_BADD: return m_add_apps; case OP_BMUL: return m_mul_apps; case OP_BXOR: return m_xor_apps; case OP_BOR: return m_or_apps; default: UNREACHABLE(); return m_or_apps; // avoid compilation error } } expr * reuse(set & s, func_decl * f, expr * arg1, expr * arg2) { if (s.contains(expr_pair(arg1, arg2))) return m().mk_app(f, arg1, arg2); if (s.contains(expr_pair(arg2, arg1))) return m().mk_app(f, arg2, arg1); return nullptr; } struct ref_count_lt { bool operator()(expr * t1, expr * t2) const { if (t1->get_ref_count() < t2->get_ref_count()) return true; return (t1->get_ref_count() == t2->get_ref_count()) && lt(t1, t2); } }; br_status reduce_ac_app(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { set & s = f2set(f); if (num_args == 2) { if (!m_util.is_numeral(args[0]) && !m_util.is_numeral(args[1])) s.insert(expr_pair(args[0], args[1])); return BR_FAILED; } ptr_buffer _args; bool first = false; expr * num = nullptr; for (unsigned i = 0; i < num_args; i++) { expr * arg = args[i]; if (num == nullptr && m_util.is_numeral(arg)) { if (i == 0) first = true; num = arg; } else { _args.push_back(arg); } } num_args = _args.size(); // std::sort(_args.begin(), _args.end(), ref_count_lt()); // std::sort(_args.begin(), _args.end(), ast_to_lt()); try_to_reuse: if (num_args > 1 && num_args < m_max_args) { for (unsigned i = 0; i < num_args - 1; i++) { for (unsigned j = i + 1; j < num_args; j++) { expr * r = reuse(s, f, _args[i], _args[j]); if (r != nullptr) { TRACE("bv_sharing_detail", tout << "reusing args: " << i << " " << j << "\n";); _args[i] = r; SASSERT(num_args > 1); for (unsigned w = j; w < num_args - 1; w++) { _args[w] = _args[w+1]; } num_args--; goto try_to_reuse; } } } } // TODO: // some benchmarks are more efficiently solved using a tree-like structure (better sharing) // other benchmarks are more efficiently solved using a chain-like structure (better propagation for arguments "closer to the output"). // // One possible solution is to do a global analysis that finds a good order that increases sharing without affecting // propagation. // // Another cheap trick is to create an option, and try both for a small amount of time. #if 0 SASSERT(num_args > 0); if (num_args == 1) { result = _args[0]; } else { // ref_count_lt is not a total order on expr's std::stable_sort(_args.c_ptr(), _args.c_ptr() + num_args, ref_count_lt()); result = m().mk_app(f, _args[0], _args[1]); for (unsigned i = 2; i < num_args; i++) { result = m().mk_app(f, result.get(), _args[i]); } } if (num != 0) { if (first) result = m().mk_app(f, num, result); else result = m().mk_app(f, result, num); } return BR_DONE; #else // Create "tree-like circuit" while (true) { TRACE("bv_sharing_detail", tout << "tree-loop: num_args: " << num_args << "\n";); unsigned j = 0; for (unsigned i = 0; i < num_args; i += 2, j++) { if (i == num_args - 1) { _args[j] = _args[i]; } else { s.insert(expr_pair(_args[i], _args[i+1])); _args[j] = m().mk_app(f, _args[i], _args[i+1]); } } num_args = j; if (num_args == 1) { if (num == nullptr) { result = _args[0]; } else { if (first) result = m().mk_app(f, num, _args[0]); else result = m().mk_app(f, _args[0], num); } return BR_DONE; } } #endif } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { if (f->get_family_id() != m_util.get_family_id()) return BR_FAILED; switch (f->get_decl_kind()) { case OP_BADD: case OP_BMUL: case OP_BOR: case OP_BXOR: result_pr = nullptr; return reduce_ac_app(f, num, args, result); default: return BR_FAILED; } } }; struct rw : public rewriter_tpl { rw_cfg m_cfg; rw(ast_manager & m, params_ref const & p): rewriter_tpl(m, m.proofs_enabled(), m_cfg), m_cfg(m, p) { } }; struct imp { rw m_rw; unsigned m_num_steps; imp(ast_manager & m, params_ref const & p): m_rw(m, p) { } ast_manager & m() const { return m_rw.m(); } void operator()(goal_ref const & g, goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); tactic_report report("max-bv-sharing", *g); bool produce_proofs = g->proofs_enabled(); expr_ref new_curr(m()); proof_ref new_pr(m()); unsigned size = g->size(); for (unsigned idx = 0; idx < size; idx++) { if (g->inconsistent()) break; expr * curr = g->form(idx); m_rw(curr, new_curr, new_pr); m_num_steps += m_rw.get_num_steps(); 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)); } m_rw.cfg().cleanup(); g->inc_depth(); result.push_back(g.get()); TRACE("max_bv_sharing", g->display(tout);); SASSERT(g->is_well_sorted()); } }; imp * m_imp; params_ref m_params; public: max_bv_sharing_tactic(ast_manager & m, params_ref const & p): m_params(p) { m_imp = alloc(imp, m, p); } tactic * translate(ast_manager & m) override { return alloc(max_bv_sharing_tactic, m, m_params); } ~max_bv_sharing_tactic() override { dealloc(m_imp); } void updt_params(params_ref const & p) override { m_params = p; m_imp->m_rw.cfg().updt_params(p); } 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."); } void operator()(goal_ref const & in, goal_ref_buffer & result) override { (*m_imp)(in, result); } 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); } }; tactic * mk_max_bv_sharing_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(max_bv_sharing_tactic, m, p)); } z3-z3-4.8.7/src/tactic/bv/max_bv_sharing_tactic.h000066400000000000000000000014471356505360400215710ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: max_bv_sharing_tactic.h Abstract: Rewriter for "maximing" the number of shared terms. The idea is to rewrite AC terms to maximize sharing. This rewriter is particularly useful for reducing the number of Adders and Multipliers before "bit-blasting". Author: Leonardo de Moura (leonardo) 2011-12-29. Revision History: --*/ #ifndef MAX_BV_SHARING_TACTIC_H_ #define MAX_BV_SHARING_TACTIC_H_ #include "util/params.h" class ast_manager; class tactic; tactic * mk_max_bv_sharing_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("max-bv-sharing", "use heuristics to maximize the sharing of bit-vector expressions such as adders and multipliers.", "mk_max_bv_sharing_tactic(m, p)") */ #endif z3-z3-4.8.7/src/tactic/converter.h000066400000000000000000000051641356505360400166530ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: converter.h Abstract: Abstract class and templates for proof and model converters. Author: Leonardo (leonardo) 2011-11-14 Notes: --*/ #ifndef CONVERTER_H_ #define CONVERTER_H_ #include "util/vector.h" #include "util/ref.h" #include "ast/ast_translation.h" class converter { unsigned m_ref_count; public: converter():m_ref_count(0) {} virtual ~converter() {} void inc_ref() { ++m_ref_count; } void dec_ref() { --m_ref_count; if (m_ref_count == 0) { dealloc(this); } } virtual void cancel() {} virtual void display(std::ostream & out) = 0; }; template class concat_converter : public T { protected: ref m_c1; ref m_c2; template T * translate_core(ast_translation & translator) { T * t1 = m_c1->translate(translator); T * t2 = m_c2->translate(translator); return alloc(T2, t1, t2); } public: concat_converter(T * c1, T * c2):m_c1(c1), m_c2(c2) {} ~concat_converter() override {} void cancel() override { m_c2->cancel(); m_c1->cancel(); } virtual char const * get_name() const = 0; void display(std::ostream & out) override { m_c1->display(out); m_c2->display(out); } }; template class concat_star_converter : public T { protected: ref m_c1; ptr_vector m_c2s; unsigned_vector m_szs; template T * translate_core(ast_translation & translator) { T * t1 = m_c1 ? m_c1->translate(translator) : nullptr; ptr_buffer t2s; for (T* c : m_c2s) t2s.push_back(c ? c->translate(translator) : nullptr); return alloc(T2, t1, m_c2s.size(), t2s.c_ptr(), m_szs.c_ptr()); } public: concat_star_converter(T * c1, unsigned num, T * const * c2s, unsigned * szs): m_c1(c1) { for (unsigned i = 0; i < num; i++) { T * c2 = c2s[i]; if (c2) c2->inc_ref(); m_c2s.push_back(c2); m_szs.push_back(szs[i]); } } ~concat_star_converter() override { for (T* c : m_c2s) if (c) c->dec_ref(); } void cancel() override { if (m_c1) m_c1->cancel(); for (T* c : m_c2s) if (c) c->cancel(); } virtual char const * get_name() const = 0; void display(std::ostream & out) override { if (m_c1) m_c1->display(out); for (T* c : m_c2s) if (c) c->display(out); } }; #endif z3-z3-4.8.7/src/tactic/core/000077500000000000000000000000001356505360400154155ustar00rootroot00000000000000z3-z3-4.8.7/src/tactic/core/CMakeLists.txt000066400000000000000000000025301356505360400201550ustar00rootroot00000000000000z3_add_component(core_tactics SOURCES blast_term_ite_tactic.cpp cofactor_elim_term_ite.cpp cofactor_term_ite_tactic.cpp collect_statistics_tactic.cpp ctx_simplify_tactic.cpp der_tactic.cpp distribute_forall_tactic.cpp dom_simplify_tactic.cpp elim_term_ite_tactic.cpp elim_uncnstr_tactic.cpp injectivity_tactic.cpp nnf_tactic.cpp occf_tactic.cpp pb_preprocess_tactic.cpp propagate_values_tactic.cpp reduce_args_tactic.cpp reduce_invertible_tactic.cpp simplify_tactic.cpp solve_eqs_tactic.cpp special_relations_tactic.cpp split_clause_tactic.cpp symmetry_reduce_tactic.cpp tseitin_cnf_tactic.cpp collect_occs.cpp COMPONENT_DEPENDENCIES normal_forms rewriter tactic TACTIC_HEADERS blast_term_ite_tactic.h cofactor_term_ite_tactic.h collect_statistics_tactic.h ctx_simplify_tactic.h der_tactic.h distribute_forall_tactic.h dom_simplify_tactic.h elim_term_ite_tactic.h elim_uncnstr_tactic.h injectivity_tactic.h nnf_tactic.h occf_tactic.h pb_preprocess_tactic.h propagate_values_tactic.h reduce_args_tactic.h reduce_invertible_tactic.h simplify_tactic.h solve_eqs_tactic.h special_relations_tactic.h split_clause_tactic.h symmetry_reduce_tactic.h tseitin_cnf_tactic.h ) z3-z3-4.8.7/src/tactic/core/blast_term_ite_tactic.cpp000066400000000000000000000156541356505360400224600ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: blast_term_ite_tactic.cpp Abstract: Blast term if-then-else by hoisting them up. Author: Nikolaj Bjorner (nbjorner) 2013-11-4 Notes: --*/ #include "ast/normal_forms/defined_names.h" #include "ast/rewriter/rewriter_def.h" #include "ast/scoped_proof.h" #include "tactic/tactical.h" #include "tactic/tactic_params.hpp" // // (f (if c1 (if c2 e1 e2) e3) b c) -> // (if c1 (if c2 (f e1 b c) // class blast_term_ite_tactic : public tactic { struct rw_cfg : public default_rewriter_cfg { ast_manager& m; unsigned long long m_max_memory; // in bytes unsigned m_num_fresh; // number of expansions unsigned m_max_steps; unsigned m_max_inflation; unsigned m_init_term_size; rw_cfg(ast_manager & _m, params_ref const & p): m(_m), m_num_fresh(0), m_max_steps(UINT_MAX), m_max_inflation(UINT_MAX), m_init_term_size(0) { updt_params(p); } 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", 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. } bool max_steps_exceeded(unsigned num_steps) const { // if (memory::get_allocation_size() > m_max_memory) // throw tactic_exception(TACTIC_MAX_MEMORY_MSG); return num_steps >= m_max_steps; } br_status mk_app_core(func_decl* f, unsigned num_args, expr* const* args, expr_ref& result) { if (m.is_ite(f)) { return BR_FAILED; } if (m_max_inflation < UINT_MAX && m_init_term_size > 0 && m_max_inflation * m_init_term_size < m_num_fresh) return BR_FAILED; for (unsigned i = 0; i < num_args; ++i) { expr* c, *t, *e; if (!m.is_bool(args[i]) && m.is_ite(args[i], c, t, e)) { TRACE("blast_term_ite", result = m.mk_app(f, num_args, args); tout << result << "\n";); expr_ref e1(m), e2(m); ptr_vector args1(num_args, args); args1[i] = t; e1 = m.mk_app(f, num_args, args1.c_ptr()); if (m.are_equal(t, e)) { result = e1; return BR_REWRITE1; } else { args1[i] = e; e2 = m.mk_app(f, num_args, args1.c_ptr()); result = m.mk_ite(c, e1, e2); ++m_num_fresh; return BR_REWRITE3; } } } return BR_FAILED; } 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) { return mk_app_core(f, num, args, result); } }; struct rw : public rewriter_tpl { rw_cfg m_cfg; rw(ast_manager & m, params_ref const & p): rewriter_tpl(m, m.proofs_enabled(), m_cfg), m_cfg(m, p) { } }; struct imp { ast_manager & m; rw m_rw; imp(ast_manager & _m, params_ref const & p): m(_m), m_rw(m, p) { } void updt_params(params_ref const & p) { m_rw.m_cfg.updt_params(p); } void operator()(goal_ref const & g, goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); tactic_report report("blast-term-ite", *g); bool produce_proofs = g->proofs_enabled(); expr_ref new_curr(m); proof_ref new_pr(m); unsigned size = g->size(); unsigned num_fresh = 0; for (unsigned idx = 0; idx < size; idx++) { expr * curr = g->form(idx); if (m_rw.m_cfg.m_max_inflation < UINT_MAX) { m_rw.m_cfg.m_init_term_size = get_num_exprs(curr); num_fresh += m_rw.m_cfg.m_num_fresh; m_rw.m_cfg.m_num_fresh = 0; } 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)); } report_tactic_progress(":blast-term-ite-consts", m_rw.m_cfg.m_num_fresh + num_fresh); g->inc_depth(); result.push_back(g.get()); TRACE("blast_term_ite", g->display(tout);); SASSERT(g->is_well_sorted()); } }; imp * m_imp; params_ref m_params; public: blast_term_ite_tactic(ast_manager & m, params_ref const & p): m_params(p) { m_imp = alloc(imp, m, p); } tactic * translate(ast_manager & m) override { return alloc(blast_term_ite_tactic, m, m_params); } ~blast_term_ite_tactic() override { dealloc(m_imp); } void updt_params(params_ref const & p) override { m_params = p; m_imp->m_rw.m_cfg.updt_params(p); } void collect_param_descrs(param_descrs & r) override { insert_max_memory(r); insert_max_steps(r); 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 { (*m_imp)(in, result); } void cleanup() override { ast_manager & m = m_imp->m; dealloc(m_imp); m_imp = alloc(imp, m, m_params); } static void blast_term_ite(expr_ref& fml, unsigned max_inflation) { ast_manager& m = fml.get_manager(); scoped_no_proof _sp(m); params_ref p; rw ite_rw(m, p); ite_rw.m_cfg.m_max_inflation = max_inflation; if (max_inflation < UINT_MAX) { ite_rw.m_cfg.m_init_term_size = get_num_exprs(fml); } try { expr_ref tmp(m); ite_rw(fml, tmp); fml = tmp; } catch (z3_exception &) { // max steps exceeded. } } }; tactic * mk_blast_term_ite_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(blast_term_ite_tactic, m, p)); } void blast_term_ite(expr_ref& fml, unsigned max_inflation) { blast_term_ite_tactic::blast_term_ite(fml, max_inflation); } z3-z3-4.8.7/src/tactic/core/blast_term_ite_tactic.h000066400000000000000000000014161356505360400221140ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: blast_term_ite_tactic.h Abstract: Blast term if-then-else by hoisting them up. This is expensive but useful in some cases, such as for enforcing constraints being in difference logic. Use elim-term-ite elsewhere when possible. Author: Nikolaj Bjorner (nbjorner) 2013-11-4 Notes: --*/ #ifndef BLAST_TERM_ITE_TACTIC_H_ #define BLAST_TERM_ITE_TACTIC_H_ #include "util/params.h" class ast_manager; class tactic; tactic * mk_blast_term_ite_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("blast-term-ite", "blast term if-then-else by hoisting them.", "mk_blast_term_ite_tactic(m, p)") */ void blast_term_ite(expr_ref& fml, unsigned max_inflation); #endif z3-z3-4.8.7/src/tactic/core/cofactor_elim_term_ite.cpp000066400000000000000000000577701356505360400226370ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: cofactor_elim_term_ite.cpp Abstract: Eliminate term if-then-else's using cofactors. Author: Leonardo de Moura (leonardo) 2011-06-05. Revision History: --*/ #include "tactic/core/cofactor_elim_term_ite.h" #include "ast/rewriter/mk_simplified_app.h" #include "ast/rewriter/rewriter_def.h" #include "ast/for_each_expr.h" #include "ast/ast_smt2_pp.h" #include "ast/ast_ll_pp.h" #include "tactic/tactic.h" struct cofactor_elim_term_ite::imp { ast_manager & m; params_ref m_params; unsigned long long m_max_memory; bool m_cofactor_equalities; void checkpoint() { if (memory::get_allocation_size() > m_max_memory) throw tactic_exception(TACTIC_MAX_MEMORY_MSG); if (m.canceled()) throw tactic_exception(m.limit().get_cancel_msg()); } // Collect atoms that contain term if-then-else struct analyzer { struct frame { expr * m_t; unsigned m_first:1; unsigned m_form_ctx:1; frame(expr * t, bool form_ctx):m_t(t), m_first(true), m_form_ctx(form_ctx) {} }; ast_manager & m; imp & m_owner; obj_hashtable m_candidates; expr_fast_mark1 m_processed; expr_fast_mark2 m_has_term_ite; svector m_frame_stack; analyzer(ast_manager & _m, imp & owner):m(_m), m_owner(owner) {} void push_frame(expr * t, bool form_ctx) { m_frame_stack.push_back(frame(t, form_ctx && m.is_bool(t))); } void visit(expr * t, bool form_ctx, bool & visited) { if (!m_processed.is_marked(t)) { visited = false; push_frame(t, form_ctx); } } void save_candidate(expr * t, bool form_ctx) { if (!form_ctx) return; if (!m.is_bool(t)) return; if (!m_has_term_ite.is_marked(t)) return; if (!is_app(t)) return; if (to_app(t)->get_family_id() == m.get_basic_family_id()) { switch (to_app(t)->get_decl_kind()) { case OP_OR: case OP_AND: case OP_NOT: case OP_XOR: case OP_IMPLIES: case OP_TRUE: case OP_FALSE: case OP_ITE: return; case OP_EQ: case OP_DISTINCT: if (m.is_bool(to_app(t)->get_arg(0))) return; break; default: break; } } // it is an atom in a formula context (i.e., it is not nested inside a term), // and it contains a term if-then-else. m_candidates.insert(t); } void operator()(expr * t) { SASSERT(m.is_bool(t)); push_frame(t, true); SASSERT(!m_frame_stack.empty()); while (!m_frame_stack.empty()) { frame & fr = m_frame_stack.back(); expr * t = fr.m_t; bool form_ctx = fr.m_form_ctx; TRACE("cofactor", tout << "processing, form_ctx: " << form_ctx << "\n" << mk_bounded_pp(t, m) << "\n";); m_owner.checkpoint(); if (m_processed.is_marked(t)) { save_candidate(t, form_ctx); m_frame_stack.pop_back(); continue; } if (m.is_term_ite(t)) { m_has_term_ite.mark(t); m_processed.mark(t); m_frame_stack.pop_back(); continue; } if (fr.m_first) { fr.m_first = false; bool visited = true; if (is_app(t)) { unsigned num_args = to_app(t)->get_num_args(); for (unsigned i = 0; i < num_args; i++) visit(to_app(t)->get_arg(i), form_ctx, visited); } // ignoring quantifiers if (!visited) continue; } if (is_app(t)) { unsigned num_args = to_app(t)->get_num_args(); unsigned i; for (i = 0; i < num_args; i++) { if (m_has_term_ite.is_marked(to_app(t)->get_arg(i))) break; } if (i < num_args) { m_has_term_ite.mark(t); TRACE("cofactor", tout << "saving candidate: " << form_ctx << "\n" << mk_bounded_pp(t, m) << "\n";); save_candidate(t, form_ctx); } } else { SASSERT(is_quantifier(t) || is_var(t)); // ignoring quantifiers... they are treated as black boxes. } m_processed.mark(t); m_frame_stack.pop_back(); } m_processed.reset(); m_has_term_ite.reset(); } }; expr * get_first(expr * t) { TRACE("cofactor", tout << mk_ismt2_pp(t, m) << "\n";); typedef std::pair frame; expr_fast_mark1 visited; sbuffer stack; stack.push_back(frame(t, 0)); while (!stack.empty()) { start: checkpoint(); frame & fr = stack.back(); expr * curr = fr.first; if (m.is_term_ite(curr)) return to_app(curr)->get_arg(0); switch (curr->get_kind()) { case AST_VAR: case AST_QUANTIFIER: // ignore quantifiers stack.pop_back(); break; case AST_APP: { unsigned num_args = to_app(curr)->get_num_args(); while (fr.second < num_args) { expr * arg = to_app(curr)->get_arg(fr.second); fr.second++; if (arg->get_ref_count() > 1) { if (visited.is_marked(arg)) continue; visited.mark(arg); } switch (arg->get_kind()) { case AST_VAR: case AST_QUANTIFIER: // ignore quantifiers break; case AST_APP: if (to_app(arg)->get_num_args() > 0) { stack.push_back(frame(arg, 0)); goto start; } break; default: UNREACHABLE(); break; } } stack.pop_back(); break; } default: UNREACHABLE(); break; } } return nullptr; } /** \brief Fuctor for selecting the term if-then-else condition with the most number of occurrences. */ expr * get_best(expr * t) { TRACE("cofactor", tout << mk_ismt2_pp(t, m) << "\n";); typedef std::pair frame; obj_map occs; expr_fast_mark1 visited; sbuffer stack; stack.push_back(frame(t, 0)); while (!stack.empty()) { start: checkpoint(); frame & fr = stack.back(); expr * curr = fr.first; switch (curr->get_kind()) { case AST_VAR: case AST_QUANTIFIER: // ignore quantifiers stack.pop_back(); break; case AST_APP: { unsigned num_args = to_app(curr)->get_num_args(); bool is_term_ite = m.is_term_ite(curr); while (fr.second < num_args) { expr * arg = to_app(curr)->get_arg(fr.second); if (fr.second == 0 && is_term_ite) { unsigned num = 0; if (occs.find(arg, num)) occs.insert(arg, num+1); else occs.insert(arg, 1); } fr.second++; if (arg->get_ref_count() > 1) { if (visited.is_marked(arg)) continue; visited.mark(arg); } switch (arg->get_kind()) { case AST_VAR: case AST_QUANTIFIER: // ignore quantifiers break; case AST_APP: if (to_app(arg)->get_num_args() > 0) { stack.push_back(frame(arg, 0)); goto start; } break; default: UNREACHABLE(); break; } } stack.pop_back(); break; } default: UNREACHABLE(); break; } } expr * best = nullptr; unsigned best_occs = 0; obj_map::iterator it = occs.begin(); obj_map::iterator end = occs.end(); for (; it != end; ++it) { if ((!best) || (get_depth(it->m_key) < get_depth(best)) || (get_depth(it->m_key) == get_depth(best) && it->m_value > best_occs) || // break ties by giving preference to equalities (get_depth(it->m_key) == get_depth(best) && it->m_value == best_occs && m.is_eq(it->m_key) && !m.is_eq(best))) { best = it->m_key; best_occs = it->m_value; } } visited.reset(); CTRACE("cofactor", best != 0, tout << "best num-occs: " << best_occs << "\n" << mk_ismt2_pp(best, m) << "\n";); return best; } void updt_params(params_ref const & p) { m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); m_cofactor_equalities = p.get_bool("cofactor_equalities", true); } void collect_param_descrs(param_descrs & r) { r.insert("cofactor_equalities", CPK_BOOL, "(default: true) use equalities to rewrite bodies of ite-expressions. This is potentially expensive."); } struct cofactor_rw_cfg : public default_rewriter_cfg { ast_manager & m; imp & m_owner; obj_hashtable * m_has_term_ite; mk_simplified_app m_mk_app; expr * m_atom; bool m_sign; expr * m_term; app * m_value; bool m_strict_lower; app * m_lower; bool m_strict_upper; app * m_upper; cofactor_rw_cfg(ast_manager & _m, imp & owner, obj_hashtable * has_term_ite = nullptr): m(_m), m_owner(owner), m_has_term_ite(has_term_ite), m_mk_app(m, owner.m_params) { } bool max_steps_exceeded(unsigned num_steps) const { m_owner.checkpoint(); return false; } bool pre_visit(expr * t) { return true; } void set_cofactor_atom(expr * t) { if (m.is_not(t)) { m_atom = to_app(t)->get_arg(0); m_sign = true; m_term = nullptr; // TODO: bounds } else { m_atom = t; m_sign = false; m_term = nullptr; expr * lhs; expr * rhs; if (m_owner.m_cofactor_equalities && m.is_eq(t, lhs, rhs)) { if (m.is_unique_value(lhs)) { m_term = rhs; m_value = to_app(lhs); TRACE("cofactor", tout << "term:\n" << mk_ismt2_pp(m_term, m) << "\nvalue: " << mk_ismt2_pp(m_value, m) << "\n";); } else if (m.is_unique_value(rhs)) { m_term = lhs; m_value = to_app(rhs); TRACE("cofactor", tout << "term:\n" << mk_ismt2_pp(m_term, m) << "\nvalue: " << mk_ismt2_pp(m_value, m) << "\n";); } } // TODO: bounds } } 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; return m_mk_app.mk_core(f, num, args, result); } bool get_subst(expr * s, expr * & t, proof * & pr) { pr = nullptr; if (s == m_atom) { t = m_sign ? m.mk_false() : m.mk_true(); return true; } if (s == m_term && m_value != nullptr) { t = m_value; return true; } // TODO: handle simple bounds // Example: s is of the form (<= s 10) and m_term == s, and m_upper is 9 // then rewrite to true. return false; } }; struct cofactor_rw : rewriter_tpl { cofactor_rw_cfg m_cfg; public: cofactor_rw(ast_manager & m, imp & owner, obj_hashtable * has_term_ite = nullptr): rewriter_tpl(m, false, m_cfg), m_cfg(m, owner, has_term_ite) { } void set_cofactor_atom(expr * t) { m_cfg.set_cofactor_atom(t); reset(); } }; struct main_rw_cfg : public default_rewriter_cfg { ast_manager & m; imp & m_owner; cofactor_rw m_cofactor; obj_hashtable const & m_candidates; obj_map m_cache; expr_ref_vector m_cache_domain; main_rw_cfg(ast_manager & _m, imp & owner, obj_hashtable & candidates): m(_m), m_owner(owner), m_cofactor(m, m_owner), m_candidates(candidates), m_cache_domain(_m) { } bool max_steps_exceeded(unsigned num_steps) const { m_owner.checkpoint(); return false; } bool pre_visit(expr * t) { return m.is_bool(t) && !is_quantifier(t); } bool get_subst(expr * s, expr * & t, proof * & t_pr) { if (m_candidates.contains(s)) { t_pr = nullptr; if (m_cache.find(s, t)) return true; unsigned step = 0; TRACE("cofactor_ite", tout << "cofactor target:\n" << mk_ismt2_pp(s, m) << "\n";); expr_ref curr(m); curr = s; while (true) { // expr * c = m_owner.get_best(curr); expr * c = m_owner.get_first(curr); if (c == nullptr) { m_cache.insert(s, curr); m_cache_domain.push_back(curr); t = curr.get(); return true; } step++; expr_ref pos_cofactor(m); expr_ref neg_cofactor(m); m_cofactor.set_cofactor_atom(c); m_cofactor(curr, pos_cofactor); expr_ref neg_c(m); neg_c = m.is_not(c) ? to_app(c)->get_arg(0) : m.mk_not(c); m_cofactor.set_cofactor_atom(neg_c); m_cofactor(curr, neg_cofactor); curr = m.mk_ite(c, pos_cofactor, neg_cofactor); TRACE("cofactor", tout << "cofactor_ite step: " << step << "\n" << mk_ismt2_pp(curr, m) << "\n";); } } return false; } }; struct main_rw : rewriter_tpl { main_rw_cfg m_cfg; public: main_rw(ast_manager & m, imp & owner, obj_hashtable & candidates): rewriter_tpl(m, false, m_cfg), m_cfg(m, owner, candidates) { } }; struct bottom_up_elim { typedef std::pair frame; ast_manager & m; imp & m_owner; obj_map m_cache; expr_ref_vector m_cache_domain; obj_hashtable m_has_term_ite; svector m_frames; cofactor_rw m_cofactor; bottom_up_elim(ast_manager & _m, imp & owner): m(_m), m_owner(owner), m_cache_domain(m), m_cofactor(m, owner, &m_has_term_ite) { } bool is_atom(expr * t) const { if (!m.is_bool(t)) return false; if (!is_app(t)) return false; if (to_app(t)->get_family_id() == m.get_basic_family_id()) { switch (to_app(t)->get_decl_kind()) { case OP_EQ: case OP_DISTINCT: if (m.is_bool(to_app(t)->get_arg(0))) return false; else return true; default: return false; } } return true; } void cofactor(expr * t, expr_ref & r) { unsigned step = 0; TRACE("cofactor", tout << "cofactor target:\n" << mk_ismt2_pp(t, m) << "\n";); expr_ref curr(m); curr = t; while (true) { expr * c = m_owner.get_best(curr); // expr * c = m_owner.get_first(curr); if (c == nullptr) { r = curr.get(); return; } step++; expr_ref pos_cofactor(m); expr_ref neg_cofactor(m); m_cofactor.set_cofactor_atom(c); m_cofactor(curr, pos_cofactor); expr_ref neg_c(m); neg_c = m.is_not(c) ? to_app(c)->get_arg(0) : m.mk_not(c); m_cofactor.set_cofactor_atom(neg_c); m_cofactor(curr, neg_cofactor); if (pos_cofactor == neg_cofactor) { curr = pos_cofactor; } else if (m.is_true(pos_cofactor) && m.is_false(neg_cofactor)) { curr = c; } else if (m.is_false(pos_cofactor) && m.is_true(neg_cofactor)) { curr = neg_c; } else { curr = m.mk_ite(c, pos_cofactor, neg_cofactor); } TRACE("cofactor", tout << "cofactor_ite step: " << step << "\n"; tout << "cofactor: " << mk_ismt2_pp(c, m) << "\n"; tout << mk_ismt2_pp(curr, m) << "\n";); } } void visit(expr * t, bool & visited) { if (!m_cache.contains(t)) { m_frames.push_back(frame(t, true)); visited = false; } } void operator()(expr * t, expr_ref & r) { ptr_vector new_args; SASSERT(m_frames.empty()); m_frames.push_back(frame(t, true)); while (!m_frames.empty()) { m_owner.checkpoint(); frame & fr = m_frames.back(); expr * t = fr.first; TRACE("cofactor_bug", tout << "processing: " << t->get_id() << " :first " << fr.second << "\n";); if (!is_app(t)) { m_cache.insert(t, t); m_frames.pop_back(); continue; } if (m_cache.contains(t)) { m_frames.pop_back(); continue; } if (fr.second) { fr.second = false; bool visited = true; unsigned i = to_app(t)->get_num_args(); while (i > 0) { --i; expr * arg = to_app(t)->get_arg(i); visit(arg, visited); } if (!visited) continue; } new_args.reset(); bool has_new_args = false; bool has_term_ite = false; unsigned num = to_app(t)->get_num_args(); for (unsigned i = 0; i < num; i++) { expr * arg = to_app(t)->get_arg(i); expr * new_arg = nullptr; TRACE("cofactor_bug", tout << "collecting child: " << arg->get_id() << "\n";); m_cache.find(arg, new_arg); SASSERT(new_arg != 0); if (new_arg != arg) has_new_args = true; if (m_has_term_ite.contains(new_arg)) has_term_ite = true; new_args.push_back(new_arg); } if (m.is_term_ite(t)) has_term_ite = true; expr_ref new_t(m); if (has_new_args) new_t = m.mk_app(to_app(t)->get_decl(), num, new_args.c_ptr()); else new_t = t; if (has_term_ite && is_atom(new_t)) { // update new_t expr_ref new_new_t(m); m_has_term_ite.insert(new_t); cofactor(new_t, new_new_t); m_has_term_ite.erase(new_t); new_t = new_new_t; has_term_ite = false; } if (has_term_ite) m_has_term_ite.insert(new_t); SASSERT(new_t.get() != 0); TRACE("cofactor_bug", tout << "caching: " << t->get_id() << "\n";); #if 0 counter ++; verbose_stream() << counter << "\n"; #endif m_cache.insert(t, new_t); m_cache_domain.push_back(new_t); m_frames.pop_back(); } expr * result = nullptr; m_cache.find(t, result); r = result; } }; imp(ast_manager & _m, params_ref const & p): m(_m), m_params(p), m_cofactor_equalities(true) { updt_params(p); } void operator()(expr * t, expr_ref & r) { #if 0 analyzer proc(m, *this); proc(t); main_rw rw(m, *this, proc.m_candidates); rw(t, r); #else bottom_up_elim proc(m, *this); proc(t, r); #endif } }; cofactor_elim_term_ite::cofactor_elim_term_ite(ast_manager & m, params_ref const & p): m_imp(alloc(imp, m, p)), m_params(p) { } cofactor_elim_term_ite::~cofactor_elim_term_ite() { dealloc(m_imp); } void cofactor_elim_term_ite::updt_params(params_ref const & p) { m_imp->updt_params(p); } void cofactor_elim_term_ite::collect_param_descrs(param_descrs & r) { m_imp->collect_param_descrs(r); } void cofactor_elim_term_ite::operator()(expr * t, expr_ref & r) { m_imp->operator()(t, r); } void cofactor_elim_term_ite::cleanup() { ast_manager & m = m_imp->m; imp * d = alloc(imp, m, m_params); std::swap(d, m_imp); dealloc(d); } z3-z3-4.8.7/src/tactic/core/cofactor_elim_term_ite.h000066400000000000000000000013721356505360400222670ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: cofactor_elim_term_ite.h Abstract: Eliminate (ground) term if-then-else's using cofactors. Author: Leonardo de Moura (leonardo) 2011-06-05. Revision History: --*/ #ifndef COFACTOR_ELIM_TERM_ITE_H_ #define COFACTOR_ELIM_TERM_ITE_H_ #include "ast/ast.h" #include "util/params.h" class cofactor_elim_term_ite { struct imp; imp * m_imp; params_ref m_params; public: cofactor_elim_term_ite(ast_manager & m, params_ref const & p = params_ref()); virtual ~cofactor_elim_term_ite(); void updt_params(params_ref const & p); void collect_param_descrs(param_descrs & r); void operator()(expr * t, expr_ref & r); void cleanup(); }; #endif z3-z3-4.8.7/src/tactic/core/cofactor_term_ite_tactic.cpp000066400000000000000000000041101356505360400231340ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: cofactor_term_ite_tactic.cpp Abstract: Wrap cofactor_elim_term_ite as a tactic. Eliminate (ground) term if-then-else's using cofactors. Author: Leonardo de Moura (leonardo) 2012-02-20. Revision History: --*/ #include "tactic/tactical.h" #include "tactic/core/cofactor_elim_term_ite.h" /** \brief Wrapper for applying cofactor_elim_term_ite in an assertion set. */ class cofactor_term_ite_tactic : public tactic { params_ref m_params; cofactor_elim_term_ite m_elim_ite; void process(goal & g) { ast_manager & m = g.m(); unsigned sz = g.size(); for (unsigned i = 0; i < sz; i++) { if (g.inconsistent()) break; expr * f = g.form(i); expr_ref new_f(m); m_elim_ite(f, new_f); g.update(i, new_f, nullptr, g.dep(i)); } } public: cofactor_term_ite_tactic(ast_manager & m, params_ref const & p): m_params(p), m_elim_ite(m, p) { } tactic * translate(ast_manager & m) override { return alloc(cofactor_term_ite_tactic, m, m_params); } ~cofactor_term_ite_tactic() override {} void updt_params(params_ref const & p) override { m_params = p; m_elim_ite.updt_params(p); } void collect_param_descrs(param_descrs & r) override { m_elim_ite.collect_param_descrs(r); } void operator()(goal_ref const & g, goal_ref_buffer& result) override { SASSERT(g->is_well_sorted()); fail_if_proof_generation("cofactor-term-ite", g); fail_if_unsat_core_generation("cofactor-term-ite", g); tactic_report report("cofactor-term-ite", *g); process(*(g.get())); g->inc_depth(); result.push_back(g.get()); TRACE("cofactor-term-ite", g->display(tout);); SASSERT(g->is_well_sorted()); } void cleanup() override { return m_elim_ite.cleanup(); } }; tactic * mk_cofactor_term_ite_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(cofactor_term_ite_tactic, m, p)); } z3-z3-4.8.7/src/tactic/core/cofactor_term_ite_tactic.h000066400000000000000000000012051356505360400226030ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: cofactor_term_ite_tactic.h Abstract: Wrap cofactor_elim_term_ite as a tactic. Eliminate (ground) term if-then-else's using cofactors. Author: Leonardo de Moura (leonardo) 2012-02-20. Revision History: --*/ #ifndef COFACTOR_TERM_ITE_TACTIC_H_ #define COFACTOR_TERM_ITE_TACTIC_H_ #include "util/params.h" class ast_manager; class tactic; tactic * mk_cofactor_term_ite_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("cofactor-term-ite", "eliminate term if-the-else using cofactors.", "mk_cofactor_term_ite_tactic(m, p)") */ #endif z3-z3-4.8.7/src/tactic/core/collect_occs.cpp000066400000000000000000000040561356505360400205620ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: collect_cccs.cpp Abstract: Collect variables by occurrences. Author: Leonardo (leonardo) 2011-10-22 Notes: --*/ #include "ast/ast.h" #include "tactic/goal.h" #include "util/hashtable.h" #include "tactic/core/collect_occs.h" bool collect_occs::visit(expr * t) { if (m_visited.is_marked(t)) { if (is_uninterp_const(t)) m_more_than_once.mark(t); return true; } m_visited.mark(t); if (is_uninterp_const(t)) { m_vars.push_back(to_app(t)); return true; } if (is_var(t)) return true; if (is_app(t) && to_app(t)->get_num_args() == 0) return true; m_stack.push_back(frame(t, 0)); return false; } void collect_occs::process(expr * t) { SASSERT(m_stack.empty()); if (visit(t)) return; SASSERT(!m_stack.empty()); unsigned num; expr * child; while (!m_stack.empty()) { start: frame & fr = m_stack.back(); expr * t = fr.first; switch (t->get_kind()) { case AST_APP: num = to_app(t)->get_num_args(); while (fr.second < num) { child = to_app(t)->get_arg(fr.second); fr.second++; if (!visit(child)) goto start; } m_stack.pop_back(); break; case AST_QUANTIFIER: // don't need to visit patterns child = to_quantifier(t)->get_expr(); fr.second++; if (!visit(child)) goto start; m_stack.pop_back(); break; default: UNREACHABLE(); } } } void collect_occs::operator()(goal const & g, obj_hashtable & r) { unsigned sz = g.size(); for (unsigned i = 0; i < sz; i++) { expr * t = g.form(i); process(t); } for (expr* e : m_vars) { if (!m_more_than_once.is_marked(e)) r.insert(e); } m_visited.reset(); m_more_than_once.reset(); } z3-z3-4.8.7/src/tactic/core/collect_occs.h000066400000000000000000000011131356505360400202160ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: collect_cccs.h Abstract: Collect variables by occurrences. Author: Leonardo (leonardo) 2011-10-22 Notes: --*/ #ifndef COLLECT_OCCS_H_ #define COLLECT_OCCS_H_ class collect_occs { expr_fast_mark1 m_visited; expr_fast_mark2 m_more_than_once; typedef std::pair frame; svector m_stack; ptr_vector m_vars; bool visit(expr * t); void process(expr * t); public: void operator()(goal const & g, obj_hashtable & r); }; #endif z3-z3-4.8.7/src/tactic/core/collect_statistics_tactic.cpp000066400000000000000000000140521356505360400233510ustar00rootroot00000000000000/*++ Copyright (c) 2016 Microsoft Corporation Module Name: collect_statistics_tactic.cpp Abstract: A tactic for collection of various statistics. Author: Mikolas Janota (mikjan) 2016-06-03 Christoph (cwinter) 2016-06-03 Notes: --*/ #include #include #include "ast/ast.h" #include "util/params.h" #include "ast/arith_decl_plugin.h" #include "ast/array_decl_plugin.h" #include "ast/bv_decl_plugin.h" #include "ast/datatype_decl_plugin.h" #include "ast/fpa_decl_plugin.h" #include "tactic/tactical.h" #include "util/stats.h" #include "tactic/core/collect_statistics_tactic.h" class collect_statistics_tactic : public tactic { ast_manager & m; params_ref m_params; basic_decl_plugin m_basic_pi; arith_decl_plugin m_arith_pi; array_decl_plugin m_array_pi; bv_decl_plugin m_bv_pi; datatype_decl_plugin m_datatype_pi; fpa_decl_plugin m_fpa_pi; typedef std::map stats_type; stats_type m_stats; public: collect_statistics_tactic(ast_manager & m, params_ref const & p) : m(m), m_params(p) { } ~collect_statistics_tactic() override {} tactic * translate(ast_manager & m_) override { return alloc(collect_statistics_tactic, m_, m_params); } void updt_params(params_ref const & p) override { m_params = p; } void collect_param_descrs(param_descrs & r) override {} void operator()(goal_ref const & g, goal_ref_buffer & result) override { tactic_report report("collect-statistics", *g); collect_proc cp(m, m_stats); expr_mark visited; const unsigned sz = g->size(); for (unsigned i = 0; i < sz; i++) for_each_expr(cp, visited, g->form(i)); std::cout << "(" << std::endl; for (auto const& kv : m_stats) std::cout << " :" << kv.first << " " << kv.second << std::endl; std::cout << ")" << std::endl; g->inc_depth(); result.push_back(g.get()); } void cleanup() override {} void collect_statistics(statistics & st) const override { } void reset_statistics() override { reset(); } void reset() override { cleanup(); } protected: class collect_proc { public: ast_manager & m; stats_type & m_stats; obj_hashtable m_seen_sorts; obj_hashtable m_seen_func_decls; unsigned m_qdepth; collect_proc(ast_manager & m, stats_type & s) : m(m), m_stats(s), m_qdepth(0) {} void operator()(var * v) { m_stats["bound-variables"]++; this->operator()(v->get_sort()); } void operator()(quantifier * q) { m_stats["quantifiers"]++; SASSERT(is_app(q->get_expr())); app * body = to_app(q->get_expr()); switch (q->get_kind()) { case forall_k: m_stats["forall-variables"] += q->get_num_decls(); break; case exists_k: m_stats["exists-variables"] += q->get_num_decls(); break; case lambda_k: m_stats["lambda-variables"] += q->get_num_decls(); break; } m_stats["patterns"] += q->get_num_patterns(); m_stats["no-patterns"] += q->get_num_no_patterns(); m_qdepth++; if (m_stats.find("max-quantification-depth") == m_stats.end() || m_stats["max-quantification-depth"] < m_qdepth) m_stats["max-quantification-depth"] = m_qdepth; this->operator()(body); m_qdepth--; } void operator()(app * n) { m_stats["function-applications"]++; this->operator()(n->get_decl()); } void operator()(sort * s) { if (m.is_uninterp(s)) { if (!m_seen_sorts.contains(s)) { m_stats["uninterpreted-sorts"]++; m_seen_sorts.insert(s); } m_stats["uninterpreted-sort-occurrences"]++; } else { params_ref prms; prms.set_bool("pp.single_line", true); std::stringstream ss; ss << "(declare-sort " << mk_ismt2_pp(s, m, prms) << ")"; m_stats[ss.str()]++; if (s->get_info()->get_num_parameters() > 0) { std::stringstream ssname; ssname << "(declare-sort (_ " << s->get_name() << " *))"; m_stats[ssname.str()]++; } } } void operator()(func_decl * f) { for (unsigned i = 0; i < f->get_arity(); i++) this->operator()(f->get_domain()[i]); this->operator()(f->get_range()); if (f->get_family_id() == null_family_id) { if (!m_seen_func_decls.contains(f)) { if (f->get_arity() == 0) m_stats["uninterpreted-constants"]++; else m_stats["uninterpreted-functions"]++; m_seen_func_decls.insert(f); } if (f->get_arity() > 0) m_stats["uninterpreted-function-occurrences"]++; } else { params_ref prms; prms.set_bool("pp.single_line", true); std::stringstream ss; ss << mk_ismt2_pp(f, m, prms); m_stats[ss.str()]++; std::stringstream ssfname; if (f->get_num_parameters() > 0) ssfname << "(declare-fun (_ " << f->get_name() << " *) *)"; else ssfname << "(declare-fun " << f->get_name() << " *)"; m_stats[ssfname.str()]++; } m_stats["function-applications"]++; } }; }; tactic * mk_collect_statistics_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(collect_statistics_tactic, m, p)); } z3-z3-4.8.7/src/tactic/core/collect_statistics_tactic.h000066400000000000000000000011141356505360400230110ustar00rootroot00000000000000/*++ Copyright (c) 2016 Microsoft Corporation Module Name: collect_statistics_tactic.h Abstract: A tactic for collection of various statistics. Author: Mikolas Janota (mikjan) 2016-06-03 Christoph (cwinter) 2016-06-03 Notes: --*/ #ifndef COLLECT_STATISTICS_H_ #define COLLECT_STATISTICS_H_ #include "util/params.h" class ast_manager; class tactic; tactic * mk_collect_statistics_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("collect-statistics", "Collects various statistics.", "mk_collect_statistics_tactic(m, p)") */ #endif z3-z3-4.8.7/src/tactic/core/ctx_simplify_tactic.cpp000066400000000000000000000501201356505360400221600ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: ctx_simplify_tactic.cpp Abstract: Simple context simplifier for propagating constants. Author: Leonardo (leonardo) 2011-10-26 Notes: --*/ #include "tactic/core/ctx_simplify_tactic.h" #include "ast/rewriter/mk_simplified_app.h" #include "ast/ast_ll_pp.h" #include "ast/ast_pp.h" class ctx_propagate_assertions : public ctx_simplify_tactic::simplifier { ast_manager& m; obj_map m_assertions; expr_ref_vector m_trail; unsigned_vector m_scopes; void assert_eq_val(expr * t, app * val, bool mk_scope); void assert_eq_core(expr * t, app * val); public: ctx_propagate_assertions(ast_manager& m); ~ctx_propagate_assertions() override {} bool assert_expr(expr * t, bool sign) override; bool simplify(expr* t, expr_ref& result) override; void push(); void pop(unsigned num_scopes) override; unsigned scope_level() const override { return m_scopes.size(); } simplifier * translate(ast_manager & m) override; }; ctx_propagate_assertions::ctx_propagate_assertions(ast_manager& m): m(m), m_trail(m) {} bool ctx_propagate_assertions::assert_expr(expr * t, bool sign) { expr * p = t; while (m.is_not(t, t)) { sign = !sign; } bool mk_scope = true; if (shared(t) || shared(p)) { push(); mk_scope = false; assert_eq_core(t, sign ? m.mk_false() : m.mk_true()); } expr * lhs, * rhs; if (!sign && m.is_eq(t, lhs, rhs)) { if (m.is_value(rhs)) assert_eq_val(lhs, to_app(rhs), mk_scope); else if (m.is_value(lhs)) assert_eq_val(rhs, to_app(lhs), mk_scope); } return true; } void ctx_propagate_assertions::assert_eq_val(expr * t, app * val, bool mk_scope) { if (shared(t)) { if (mk_scope) push(); assert_eq_core(t, val); } } void ctx_propagate_assertions::assert_eq_core(expr * t, app * val) { if (m_assertions.contains(t)) { // This branch can only happen when m_max_depth was reached. // It can happen when m_assertions contains an entry t->val', // but (= t val) was not simplified to (= val' val) // because the simplifier stopped at depth m_max_depth return; } CTRACE("assert_eq_bug", m_assertions.contains(t), tout << "t:\n" << mk_ismt2_pp(t, m) << "\nval:\n" << mk_ismt2_pp(val, m) << "\n"; expr * old_val = 0; m_assertions.find(t, old_val); tout << "old_val:\n" << mk_ismt2_pp(old_val, m) << "\n";); m_assertions.insert(t, val); m_trail.push_back(t); } bool ctx_propagate_assertions::simplify(expr* t, expr_ref& result) { expr* r; return m_assertions.find(t, r) && (result = r, true); } void ctx_propagate_assertions::push() { m_scopes.push_back(m_trail.size()); } void ctx_propagate_assertions::pop(unsigned num_scopes) { unsigned scope_lvl = m_scopes.size(); unsigned old_trail_size = m_scopes[scope_lvl - num_scopes]; unsigned i = m_trail.size(); while (i > old_trail_size) { --i; expr * key = m_trail.back(); m_assertions.erase(key); m_trail.pop_back(); } SASSERT(m_trail.size() == old_trail_size); m_scopes.shrink(scope_lvl - num_scopes); } bool ctx_simplify_tactic::simplifier::shared(expr * t) const { SASSERT(m_occs); return t->get_ref_count() > 1 && m_occs->get_num_occs(t) > 1; } ctx_simplify_tactic::simplifier * ctx_propagate_assertions::translate(ast_manager & m) { return alloc(ctx_propagate_assertions, m); } tactic * mk_ctx_simplify_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(ctx_simplify_tactic, m, alloc(ctx_propagate_assertions, m), p)); } struct ctx_simplify_tactic::imp { struct cached_result { expr * m_to; unsigned m_lvl; cached_result * m_next; cached_result(expr * t, unsigned lvl, cached_result * next): m_to(t), m_lvl(lvl), m_next(next) { } }; struct cache_cell { expr * m_from; cached_result * m_result; cache_cell():m_from(nullptr), m_result(nullptr) {} }; ast_manager & m; simplifier* m_simp; small_object_allocator m_allocator; svector m_cache; vector > m_cache_undo; unsigned m_depth; unsigned m_num_steps; goal_num_occurs m_occs; mk_simplified_app m_mk_app; unsigned long long m_max_memory; unsigned m_max_depth; unsigned m_max_steps; bool m_bail_on_blowup; imp(ast_manager & _m, simplifier* simp, params_ref const & p): m(_m), m_simp(simp), m_allocator("context-simplifier"), m_occs(true, true), m_mk_app(m, p) { updt_params(p); m_simp->set_occs(m_occs); } ~imp() { pop(scope_level()); SASSERT(scope_level() == 0); restore_cache(0); dealloc(m_simp); DEBUG_CODE({ for (unsigned i = 0; i < m_cache.size(); i++) { CTRACE("ctx_simplify_tactic_bug", m_cache[i].m_from, tout << "i: " << i << "\n" << mk_ismt2_pp(m_cache[i].m_from, m) << "\n"; tout << "m_result: " << m_cache[i].m_result << "\n"; if (m_cache[i].m_result) tout << "lvl: " << m_cache[i].m_result->m_lvl << "\n";); SASSERT(m_cache[i].m_from == 0); SASSERT(m_cache[i].m_result == 0); } }); } 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); m_max_depth = p.get_uint("max_depth", 1024); m_bail_on_blowup = p.get_bool("bail_on_blowup", false); m_simp->updt_params(p); } void checkpoint() { if (memory::get_allocation_size() > m_max_memory) throw tactic_exception(TACTIC_MAX_MEMORY_MSG); if (m.canceled()) throw tactic_exception(m.limit().get_cancel_msg()); } bool shared(expr * t) const { TRACE("ctx_simplify_tactic_bug", tout << mk_pp(t, m) << "\n";); return t->get_ref_count() > 1 && m_occs.get_num_occs(t) > 1; } bool check_cache() { for (unsigned i = 0; i < m_cache.size(); i++) { cache_cell & cell = m_cache[i]; if (cell.m_from != nullptr) { SASSERT(cell.m_result != 0); cached_result * curr = cell.m_result; while (curr) { SASSERT(curr->m_lvl <= scope_level()); curr = curr->m_next; } } } return true; } void cache_core(expr * from, expr * to) { unsigned id = from->get_id(); TRACE("ctx_simplify_tactic_cache", tout << "caching " << id << " @ " << scope_level() << "\n" << mk_ismt2_pp(from, m) << "\n--->\n" << mk_ismt2_pp(to, m) << "\n";); m_cache.reserve(id+1); cache_cell & cell = m_cache[id]; void * mem = m_allocator.allocate(sizeof(cached_result)); if (cell.m_from == nullptr) { // new_entry cell.m_from = from; cell.m_result = new (mem) cached_result(to, scope_level(), nullptr); m.inc_ref(from); m.inc_ref(to); } else { // update cell.m_result = new (mem) cached_result(to, scope_level(), cell.m_result); m.inc_ref(to); } m_cache_undo.reserve(scope_level()+1); m_cache_undo[scope_level()].push_back(from); } void cache(expr * from, expr * to) { if (shared(from)) cache_core(from, to); } unsigned scope_level() const { return m_simp->scope_level(); } void restore_cache(unsigned lvl) { if (lvl >= m_cache_undo.size()) return; ptr_vector & keys = m_cache_undo[lvl]; ptr_vector::iterator it = keys.end(); ptr_vector::iterator begin = keys.begin(); while (it != begin) { --it; expr * key = *it; unsigned key_id = key->get_id(); cache_cell & cell = m_cache[key_id]; SASSERT(cell.m_from == key); SASSERT(cell.m_result != 0); m.dec_ref(cell.m_result->m_to); cached_result * to_delete = cell.m_result; SASSERT(to_delete->m_lvl == lvl); TRACE("ctx_simplify_tactic_cache", tout << "uncaching: " << to_delete->m_lvl << "\n" << mk_ismt2_pp(key, m) << "\n--->\n" << mk_ismt2_pp(to_delete->m_to, m) << "\nrestoring:\n"; if (to_delete->m_next) tout << mk_ismt2_pp(to_delete->m_next->m_to, m); else tout << ""; tout << "\n";); cell.m_result = to_delete->m_next; if (cell.m_result == nullptr) { m.dec_ref(cell.m_from); cell.m_from = nullptr; } m_allocator.deallocate(sizeof(cached_result), to_delete); } keys.reset(); } void pop(unsigned num_scopes) { if (num_scopes == 0) return; SASSERT(num_scopes <= scope_level()); unsigned lvl = scope_level(); m_simp->pop(num_scopes); // restore cache for (unsigned i = 0; i < num_scopes; i++) { restore_cache(lvl); lvl--; } CASSERT("ctx_simplify_tactic", check_cache()); } bool assert_expr(expr * t, bool sign) { return m_simp->assert_expr(t, sign); } bool is_cached(expr * t, expr_ref & r) { unsigned id = t->get_id(); if (id >= m_cache.size()) return false; cache_cell & cell = m_cache[id]; SASSERT(cell.m_result == 0 || cell.m_result->m_lvl <= scope_level()); if (cell.m_result != nullptr && cell.m_result->m_lvl == scope_level()) { SASSERT(cell.m_from == t); SASSERT(cell.m_result->m_to != 0); r = cell.m_result->m_to; return true; } return false; } void simplify(expr * t, expr_ref & r) { r = nullptr; if (m_depth >= m_max_depth || m_num_steps >= m_max_steps || !is_app(t) || !m_simp->may_simplify(t)) { r = t; return; } checkpoint(); TRACE("ctx_simplify_tactic_detail", tout << "processing: " << mk_bounded_pp(t, m) << "\n";); if (is_cached(t, r) || m_simp->simplify(t, r)) { SASSERT(r.get() != 0); return; } m_num_steps++; m_depth++; if (m.is_or(t)) simplify_or_and(to_app(t), r); else if (m.is_and(t)) simplify_or_and(to_app(t), r); else if (m.is_ite(t)) simplify_ite(to_app(t), r); else simplify_app(to_app(t), r); m_depth--; SASSERT(r.get() != 0); TRACE("ctx_simplify_tactic_detail", tout << "result:\n" << mk_bounded_pp(t, m) << "\n---->\n" << mk_bounded_pp(r, m) << "\n";); } template void simplify_or_and(app * t, expr_ref & r) { // go forwards expr_ref_buffer new_args(m); unsigned old_lvl = scope_level(); bool modified = false; unsigned num_args = t->get_num_args(); for (unsigned i = 0; i < num_args; i++) { expr * arg = t->get_arg(i); expr_ref new_arg(m); simplify(arg, new_arg); if (new_arg != arg) modified = true; if (i < num_args - 1 && !m.is_true(new_arg) && !m.is_false(new_arg) && !assert_expr(new_arg, OR)) new_arg = OR ? m.mk_true() : m.mk_false(); if ((OR && m.is_false(new_arg)) || (!OR && m.is_true(new_arg))) { modified = true; continue; } if ((OR && m.is_true(new_arg)) || (!OR && m.is_false(new_arg))) { r = new_arg; pop(scope_level() - old_lvl); cache(t, r); return; } new_args.push_back(new_arg); } pop(scope_level() - old_lvl); // go backwards expr_ref_buffer new_new_args(m); unsigned i = new_args.size(); while (i > 0) { --i; expr * arg = new_args[i]; expr_ref new_arg(m); simplify(arg, new_arg); if (new_arg != arg) modified = true; if (i > 0 && !m.is_true(new_arg) && !m.is_false(new_arg) && !assert_expr(new_arg, OR)) new_arg = OR ? m.mk_true() : m.mk_false(); if ((OR && m.is_false(new_arg)) || (!OR && m.is_true(new_arg))) { modified = true; continue; } if ((OR && m.is_true(new_arg)) || (!OR && m.is_false(new_arg))) { r = new_arg; pop(scope_level() - old_lvl); cache(t, r); return; } new_new_args.push_back(new_arg); } pop(scope_level() - old_lvl); if (!modified) { r = t; } else if (new_new_args.empty()) { r = OR?m.mk_false():m.mk_true(); } else if (new_new_args.size() == 1) { r = new_new_args[0]; } else { std::reverse(new_new_args.c_ptr(), new_new_args.c_ptr() + new_new_args.size()); m_mk_app(t->get_decl(), new_new_args.size(), new_new_args.c_ptr(), r); } cache(t, r); } void simplify_ite(app * ite, expr_ref & r) { expr * c = ite->get_arg(0); expr * t = ite->get_arg(1); expr * e = ite->get_arg(2); expr_ref new_c(m); unsigned old_lvl = scope_level(); simplify(c, new_c); if (m.is_true(new_c)) { simplify(t, r); } else if (m.is_false(new_c)) { simplify(e, r); } else { expr_ref new_t(m); expr_ref new_e(m); if (!assert_expr(new_c, false)) { simplify(e, r); cache(ite, r); return; } simplify(t, new_t); pop(scope_level() - old_lvl); if (!assert_expr(new_c, true)) { r = new_t; cache(ite, r); return; } simplify(e, new_e); pop(scope_level() - old_lvl); if (c == new_c && t == new_t && e == new_e) { r = ite; } else if (new_t == new_e) { r = new_t; } else { expr * args[3] = { new_c.get(), new_t.get(), new_e.get() }; TRACE("ctx_simplify_tactic_ite_bug", tout << "mk_ite\n" << mk_ismt2_pp(new_c.get(), m) << "\n" << mk_ismt2_pp(new_t.get(), m) << "\n" << mk_ismt2_pp(new_e.get(), m) << "\n";); m_mk_app(ite->get_decl(), 3, args, r); } } cache(ite, r); } void simplify_app(app * t, expr_ref & r) { if (t->get_num_args() == 0) { r = t; return; } expr_ref_buffer new_args(m); bool modified = false; unsigned num_args = t->get_num_args(); for (unsigned i = 0; i < num_args; i++) { expr * arg = t->get_arg(i); expr_ref new_arg(m); simplify(arg, new_arg); CTRACE("ctx_simplify_tactic_bug", new_arg.get() == 0, tout << mk_ismt2_pp(arg, m) << "\n";); SASSERT(new_arg); if (new_arg != arg) modified = true; new_args.push_back(new_arg); } if (!modified) { r = t; } else { m_mk_app(t->get_decl(), new_args.size(), new_args.c_ptr(), r); } } unsigned expr_size(expr* s) { ast_mark visit; unsigned sz = 0; ptr_vector todo; todo.push_back(s); while (!todo.empty()) { s = todo.back(); todo.pop_back(); if (visit.is_marked(s)) { continue; } visit.mark(s, true); ++sz; for (unsigned i = 0; is_app(s) && i < to_app(s)->get_num_args(); ++i) { todo.push_back(to_app(s)->get_arg(i)); } } return sz; } void process_goal(goal & g) { SASSERT(scope_level() == 0); // go forwards unsigned old_lvl = scope_level(); unsigned sz = g.size(); expr_ref r(m); for (unsigned i = 0; !g.inconsistent() && i < sz; ++i) { m_depth = 0; simplify(g.form(i), r); if (i < sz - 1 && !m.is_true(r) && !m.is_false(r) && !g.dep(i) && !assert_expr(r, false)) { r = m.mk_false(); } g.update(i, r, nullptr, g.dep(i)); } pop(scope_level() - old_lvl); m_occs(g); // go backwards sz = g.size(); for (unsigned i = sz; !g.inconsistent() && i > 0; ) { m_depth = 0; --i; simplify(g.form(i), r); if (i > 0 && !m.is_true(r) && !m.is_false(r) && !g.dep(i) && !assert_expr(r, false)) { r = m.mk_false(); } g.update(i, r, nullptr, g.dep(i)); } pop(scope_level() - old_lvl); SASSERT(scope_level() == 0); } void process(expr * s, expr_ref & r) { TRACE("ctx_simplify_tactic", tout << "simplifying:\n" << mk_ismt2_pp(s, m) << "\n";); SASSERT(scope_level() == 0); m_depth = 0; simplify(s, r); SASSERT(scope_level() == 0); SASSERT(m_depth == 0); SASSERT(r.get() != 0); TRACE("ctx_simplify_tactic", tout << "result\n" << mk_ismt2_pp(r, m) << " :num-steps " << m_num_steps << "\n"; tout << "old size: " << expr_size(s) << " new size: " << expr_size(r) << "\n";); if (m_bail_on_blowup && expr_size(s) < expr_size(r)) { r = s; } } void operator()(goal & g) { SASSERT(g.is_well_sorted()); m_occs.reset(); m_occs(g); m_num_steps = 0; tactic_report report("ctx-simplify", g); if (g.proofs_enabled()) { expr_ref r(m); unsigned sz = g.size(); for (unsigned i = 0; !g.inconsistent() && i < sz; ++i) { expr * t = g.form(i); process(t, r); proof* new_pr = m.mk_modus_ponens(g.pr(i), m.mk_rewrite(t, r)); g.update(i, r, new_pr, g.dep(i)); } } else { process_goal(g); } IF_VERBOSE(TACTIC_VERBOSITY_LVL, verbose_stream() << "(ctx-simplify :num-steps " << m_num_steps << ")\n";); SASSERT(g.is_well_sorted()); } }; ctx_simplify_tactic::ctx_simplify_tactic(ast_manager & m, simplifier* simp, params_ref const & p): m_imp(alloc(imp, m, simp, p)), m_params(p) { } tactic * ctx_simplify_tactic::translate(ast_manager & m) { return alloc(ctx_simplify_tactic, m, m_imp->m_simp->translate(m), m_params); } ctx_simplify_tactic::~ctx_simplify_tactic() { dealloc(m_imp); } void ctx_simplify_tactic::updt_params(params_ref const & p) { m_params = p; m_imp->updt_params(p); } void ctx_simplify_tactic::get_param_descrs(param_descrs & r) { insert_max_memory(r); insert_max_steps(r); r.insert("max_depth", CPK_UINT, "(default: 1024) maximum term depth."); r.insert("propagate_eq", CPK_BOOL, "(default: false) enable equality propagation from bounds."); } void ctx_simplify_tactic::operator()(goal_ref const & in, goal_ref_buffer & result) { (*m_imp)(*(in.get())); in->inc_depth(); result.push_back(in.get()); } void ctx_simplify_tactic::cleanup() { ast_manager & m = m_imp->m; imp * d = alloc(imp, m, m_imp->m_simp->translate(m), m_params); std::swap(d, m_imp); dealloc(d); } z3-z3-4.8.7/src/tactic/core/ctx_simplify_tactic.h000066400000000000000000000034051356505360400216310ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: ctx_simplify_tactic.h Abstract: Simple context simplifier for propagating constants. Author: Leonardo (leonardo) 2011-10-26 Notes: --*/ #ifndef CTX_SIMPLIFY_TACTIC_H_ #define CTX_SIMPLIFY_TACTIC_H_ #include "tactic/tactical.h" #include "tactic/goal_num_occurs.h" class ctx_simplify_tactic : public tactic { public: class simplifier { goal_num_occurs* m_occs; public: virtual ~simplifier() {} virtual bool assert_expr(expr * t, bool sign) = 0; virtual bool simplify(expr* t, expr_ref& result) = 0; virtual bool may_simplify(expr* t) { return true; } virtual void pop(unsigned num_scopes) = 0; virtual simplifier * translate(ast_manager & m) = 0; virtual unsigned scope_level() const = 0; virtual void updt_params(params_ref const & p) {} void set_occs(goal_num_occurs& occs) { m_occs = &occs; }; bool shared(expr* t) const; }; protected: struct imp; imp * m_imp; params_ref m_params; public: ctx_simplify_tactic(ast_manager & m, simplifier* simp, params_ref const & p = params_ref()); tactic * translate(ast_manager & m) override; ~ctx_simplify_tactic() override; void updt_params(params_ref const & p) override; static void get_param_descrs(param_descrs & r); void collect_param_descrs(param_descrs & r) override { get_param_descrs(r); } void operator()(goal_ref const & in, goal_ref_buffer & result) override; void cleanup() override; }; tactic * mk_ctx_simplify_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("ctx-simplify", "apply contextual simplification rules.", "mk_ctx_simplify_tactic(m, p)") */ #endif z3-z3-4.8.7/src/tactic/core/der_tactic.cpp000066400000000000000000000042641356505360400202300ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: der_tactic.cpp Abstract: DER tactic Author: Leonardo de Moura (leonardo) 2012-10-20 --*/ #include "ast/rewriter/der.h" #include "tactic/tactical.h" class der_tactic : public tactic { struct imp { ast_manager & m_manager; der_rewriter m_r; imp(ast_manager & m): m_manager(m), m_r(m) { } ast_manager & m() const { return m_manager; } void reset() { m_r.reset(); } void operator()(goal & g) { SASSERT(g.is_well_sorted()); bool proofs_enabled = g.proofs_enabled(); tactic_report report("der", g); TRACE("before_der", g.display(tout);); expr_ref new_curr(m()); proof_ref new_pr(m()); unsigned size = g.size(); for (unsigned idx = 0; idx < size; idx++) { if (g.inconsistent()) break; expr * curr = g.form(idx); m_r(curr, new_curr, new_pr); if (proofs_enabled) { 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.elim_redundancies(); TRACE("after_der", g.display(tout);); SASSERT(g.is_well_sorted()); } }; imp * m_imp; public: der_tactic(ast_manager & m) { m_imp = alloc(imp, m); } tactic * translate(ast_manager & m) override { return alloc(der_tactic, m); } ~der_tactic() override { dealloc(m_imp); } void operator()(goal_ref const & in, goal_ref_buffer & result) override { (*m_imp)(*(in.get())); in->inc_depth(); result.push_back(in.get()); } void cleanup() override { ast_manager & m = m_imp->m(); imp * d = alloc(imp, m); std::swap(d, m_imp); dealloc(d); } }; tactic * mk_der_tactic(ast_manager & m) { return alloc(der_tactic, m); } z3-z3-4.8.7/src/tactic/core/der_tactic.h000066400000000000000000000006141356505360400176700ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: der_tactic.h Abstract: DER tactic Author: Leonardo de Moura (leonardo) 2012-10-20 --*/ #ifndef DER_TACTIC_H_ #define DER_TACTIC_H_ class ast_manager; class tactic; tactic * mk_der_tactic(ast_manager & m); /* ADD_TACTIC("der", "destructive equality resolution.", "mk_der_tactic(m)") */ #endif /* DER_TACTIC_H_ */ z3-z3-4.8.7/src/tactic/core/distribute_forall_tactic.cpp000066400000000000000000000101741356505360400231700ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: distribute_forall_tactic.cpp Abstract: Author: Leonardo de Moura (leonardo) 2012-02-18. --*/ #include "tactic/tactical.h" #include "ast/rewriter/rewriter_def.h" #include "ast/rewriter/var_subst.h" class distribute_forall_tactic : public tactic { struct rw_cfg : public default_rewriter_cfg { ast_manager & m; rw_cfg(ast_manager & _m):m(_m) {} 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) { if (!is_forall(old_q)) { return false; } if (m.is_not(new_body) && m.is_or(to_app(new_body)->get_arg(0))) { // (forall X (not (or F1 ... Fn))) // --> // (and (forall X (not F1)) // ... // (forall X (not Fn))) app * or_e = to_app(to_app(new_body)->get_arg(0)); unsigned num_args = or_e->get_num_args(); expr_ref_buffer new_args(m); for (unsigned i = 0; i < num_args; i++) { expr * arg = or_e->get_arg(i); expr * not_arg = m.mk_not(arg); quantifier_ref tmp_q(m); tmp_q = m.update_quantifier(old_q, not_arg); 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; } if (m.is_and(new_body)) { // (forall X (and F1 ... Fn)) // --> // (and (forall X F1) // ... // (forall X Fn) unsigned num_args = to_app(new_body)->get_num_args(); expr_ref_buffer new_args(m); for (unsigned i = 0; i < num_args; i++) { expr * arg = to_app(new_body)->get_arg(i); quantifier_ref tmp_q(m); tmp_q = m.update_quantifier(old_q, arg); 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; } return false; } }; struct rw : public rewriter_tpl { rw_cfg m_cfg; rw(ast_manager & m, bool proofs_enabled): rewriter_tpl(m, proofs_enabled, m_cfg), m_cfg(m) { } }; rw * m_rw; public: distribute_forall_tactic():m_rw(nullptr) {} tactic * translate(ast_manager & m) override { return alloc(distribute_forall_tactic); } void operator()(goal_ref const & g, goal_ref_buffer & result) override { SASSERT(g->is_well_sorted()); ast_manager & m = g->m(); bool produce_proofs = g->proofs_enabled(); rw r(m, produce_proofs); m_rw = &r; result.reset(); tactic_report report("distribute-forall", *g); expr_ref new_curr(m); proof_ref new_pr(m); unsigned size = g->size(); for (unsigned idx = 0; idx < size; idx++) { if (g->inconsistent()) break; expr * curr = g->form(idx); r(curr, new_curr, new_pr); if (g->proofs_enabled()) { 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->inc_depth(); result.push_back(g.get()); TRACE("distribute-forall", g->display(tout);); SASSERT(g->is_well_sorted()); m_rw = nullptr; } void cleanup() override {} }; tactic * mk_distribute_forall_tactic(ast_manager & m, params_ref const & p) { return alloc(distribute_forall_tactic); } z3-z3-4.8.7/src/tactic/core/distribute_forall_tactic.h000066400000000000000000000010031356505360400226240ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: distribute_forall_tactic.h Abstract: Author: Leonardo de Moura (leonardo) 2012-02-18. --*/ #ifndef DISTRIBUTE_FORALL_TACTIC_H_ #define DISTRIBUTE_FORALL_TACTIC_H_ #include "util/params.h" class ast_manager; class tactic; tactic * mk_distribute_forall_tactic(ast_manager & m, params_ref const & p); /* ADD_TACTIC("distribute-forall", "distribute forall over conjunctions.", "mk_distribute_forall_tactic(m, p)") */ #endif z3-z3-4.8.7/src/tactic/core/dom_simplify_tactic.cpp000066400000000000000000000363461356505360400221570ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: dom_simplify_tactic.cpp Abstract: Dominator-based context simplifer. Author: Nikolaj and Nuno Notes: --*/ #include "ast/ast_util.h" #include "ast/ast_pp.h" #include "tactic/core/dom_simplify_tactic.h" /** \brief compute a post-order traversal for e. Also populate the set of parents */ void expr_dominators::compute_post_order() { unsigned post_num = 0; SASSERT(m_post2expr.empty()); SASSERT(m_expr2post.empty()); ast_mark mark; ptr_vector todo; todo.push_back(m_root); while (!todo.empty()) { expr* e = todo.back(); if (mark.is_marked(e)) { todo.pop_back(); continue; } if (is_app(e)) { app* a = to_app(e); bool done = true; for (expr* arg : *a) { if (!mark.is_marked(arg)) { todo.push_back(arg); done = false; } } if (done) { mark.mark(e, true); m_expr2post.insert(e, post_num++); m_post2expr.push_back(e); todo.pop_back(); for (expr* arg : *a) { add_edge(m_parents, arg, a); } } } else { mark.mark(e, true); todo.pop_back(); } } } expr* expr_dominators::intersect(expr* x, expr * y) { unsigned n1 = m_expr2post[x]; unsigned n2 = m_expr2post[y]; while (n1 != n2) { if (n1 < n2) { x = m_doms[x]; n1 = m_expr2post[x]; } else if (n1 > n2) { y = m_doms[y]; n2 = m_expr2post[y]; } } SASSERT(x == y); return x; } bool expr_dominators::compute_dominators() { expr * e = m_root; SASSERT(m_doms.empty()); m_doms.insert(e, e); bool change = true; unsigned iterations = 1; while (change) { change = false; TRACE("simplify", for (auto & kv : m_doms) { tout << expr_ref(kv.m_key, m) << " |-> " << expr_ref(kv.m_value, m) << "\n"; }); SASSERT(m_post2expr.empty() || m_post2expr.back() == e); for (unsigned i = 0; i + 1 < m_post2expr.size(); ++i) { expr * child = m_post2expr[i]; ptr_vector const& p = m_parents[child]; expr * new_idom = nullptr, *idom2 = nullptr; for (expr * pred : p) { if (m_doms.contains(pred)) { new_idom = !new_idom ? pred : intersect(new_idom, pred); } } if (!new_idom) { m_doms.insert(child, p[0]); change = true; } else if (!m_doms.find(child, idom2) || idom2 != new_idom) { m_doms.insert(child, new_idom); change = true; } } iterations *= 2; if (change && iterations > m_post2expr.size()) { return false; } } return true; } void expr_dominators::extract_tree() { for (auto const& kv : m_doms) { add_edge(m_tree, kv.m_value, kv.m_key); } } bool expr_dominators::compile(expr * e) { reset(); m_root = e; compute_post_order(); if (!compute_dominators()) return false; extract_tree(); TRACE("simplify", display(tout);); return true; } bool expr_dominators::compile(unsigned sz, expr * const* es) { expr_ref e(m.mk_and(sz, es), m); return compile(e); } void expr_dominators::reset() { m_expr2post.reset(); m_post2expr.reset(); m_parents.reset(); m_doms.reset(); m_tree.reset(); m_root.reset(); } std::ostream& expr_dominators::display(std::ostream& out) { return display(out, 0, m_root); } std::ostream& expr_dominators::display(std::ostream& out, unsigned indent, expr* r) { for (unsigned i = 0; i < indent; ++i) out << " "; out << expr_ref(r, m); if (m_tree.contains(r)) { for (expr* child : m_tree[r]) { if (child != r) display(out, indent + 1, child); } } out << "\n"; return out; } // ----------------------- // dom_simplify_tactic dom_simplify_tactic::~dom_simplify_tactic() { dealloc(m_simplifier); } tactic * dom_simplify_tactic::translate(ast_manager & m) { return alloc(dom_simplify_tactic, m, m_simplifier->translate(m), m_params); } void dom_simplify_tactic::operator()(goal_ref const & in, goal_ref_buffer & result) { tactic_report report("dom-simplify", *in.get()); simplify_goal(*(in.get())); in->inc_depth(); result.push_back(in.get()); } void dom_simplify_tactic::cleanup() { m_trail.reset(); m_args.reset(); m_result.reset(); m_dominators.reset(); } expr_ref dom_simplify_tactic::simplify_ite(app * ite) { expr_ref r(m); expr * c = nullptr, *t = nullptr, *e = nullptr; VERIFY(m.is_ite(ite, c, t, e)); unsigned old_lvl = scope_level(); expr_ref new_c = simplify_arg(c); if (m.is_true(new_c)) { r = simplify_arg(t); } else if (m.is_false(new_c) || !assert_expr(new_c, false)) { r = simplify_arg(e); } else { for (expr * child : tree(ite)) { if (is_subexpr(child, t) && !is_subexpr(child, e)) { simplify_rec(child); } } pop(scope_level() - old_lvl); expr_ref new_t = simplify_arg(t); if (!assert_expr(new_c, true)) { return new_t; } for (expr * child : tree(ite)) { if (is_subexpr(child, e) && !is_subexpr(child, t)) { simplify_rec(child); } } pop(scope_level() - old_lvl); expr_ref new_e = simplify_arg(e); if (c == new_c && t == new_t && e == new_e) { r = ite; } else if (new_t == new_e) { r = new_t; } else { TRACE("simplify", tout << new_c << "\n" << new_t << "\n" << new_e << "\n";); r = m.mk_ite(new_c, new_t, new_e); } } return r; } expr_ref dom_simplify_tactic::simplify_arg(expr * e) { expr_ref r(m); r = get_cached(e); (*m_simplifier)(r); TRACE("simplify", tout << "depth: " << m_depth << " " << mk_pp(e, m) << " -> " << r << "\n";); return r; } /** \brief simplify e recursively. */ expr_ref dom_simplify_tactic::simplify_rec(expr * e0) { expr_ref r(m); expr* e = nullptr; TRACE("simplify", tout << "depth: " << m_depth << " " << mk_pp(e0, m) << "\n";); if (!m_result.find(e0, e)) { e = e0; } ++m_depth; if (m_depth > m_max_depth) { r = e; } else if (m.is_ite(e)) { r = simplify_ite(to_app(e)); } else if (m.is_and(e)) { r = simplify_and(to_app(e)); } else if (m.is_or(e)) { r = simplify_or(to_app(e)); } else { for (expr * child : tree(e)) { simplify_rec(child); } if (is_app(e)) { m_args.reset(); for (expr* arg : *to_app(e)) { m_args.push_back(simplify_arg(arg)); } r = m.mk_app(to_app(e)->get_decl(), m_args.size(), m_args.c_ptr()); } else { r = e; } } (*m_simplifier)(r); cache(e0, r); TRACE("simplify", tout << "depth: " << m_depth << " " << mk_pp(e0, m) << " -> " << r << "\n";); --m_depth; m_subexpr_cache.reset(); return r; } expr_ref dom_simplify_tactic::simplify_and_or(bool is_and, app * e) { expr_ref r(m); unsigned old_lvl = scope_level(); auto is_subexpr_arg = [&](expr * child, expr * except) { if (!is_subexpr(child, except)) return false; for (expr * arg : *e) { if (arg != except && is_subexpr(child, arg)) return false; } return true; }; expr_ref_vector args(m); if (m_forward) { for (expr * arg : *e) { #define _SIMP_ARG(arg) \ for (expr * child : tree(arg)) { \ if (is_subexpr_arg(child, arg)) { \ simplify_rec(child); \ } \ } \ r = simplify_arg(arg); \ args.push_back(r); \ if (!assert_expr(r, !is_and)) { \ r = is_and ? m.mk_false() : m.mk_true(); \ return r; \ } _SIMP_ARG(arg); } } else { for (unsigned i = e->get_num_args(); i > 0; ) { --i; expr* arg = e->get_arg(i); _SIMP_ARG(arg); } args.reverse(); } pop(scope_level() - old_lvl); r = is_and ? mk_and(args) : mk_or(args); return r; } bool dom_simplify_tactic::init(goal& g) { expr_ref_vector args(m); unsigned sz = g.size(); for (unsigned i = 0; i < sz; ++i) args.push_back(g.form(i)); expr_ref fml = mk_and(args); m_result.reset(); m_trail.reset(); return m_dominators.compile(fml); } void dom_simplify_tactic::simplify_goal(goal& g) { SASSERT(scope_level() == 0); bool change = true; m_depth = 0; while (change) { change = false; // go forwards m_forward = true; if (!init(g)) return; unsigned sz = g.size(); for (unsigned i = 0; !g.inconsistent() && i < sz; ++i) { expr_ref r = simplify_rec(g.form(i)); if (i < sz - 1 && !m.is_true(r) && !m.is_false(r) && !g.dep(i) && !g.proofs_enabled() && !assert_expr(r, false)) { r = m.mk_false(); } CTRACE("simplify", r != g.form(i), tout << r << " " << mk_pp(g.form(i), m) << "\n";); change |= r != g.form(i); proof* new_pr = nullptr; if (g.proofs_enabled()) { new_pr = m.mk_modus_ponens(g.pr(i), m.mk_rewrite(g.form(i), r)); } g.update(i, r, new_pr, g.dep(i)); } pop(scope_level()); // go backwards m_forward = false; if (!init(g)) return; sz = g.size(); for (unsigned i = sz; !g.inconsistent() && i > 0; ) { --i; expr_ref r = simplify_rec(g.form(i)); if (i > 0 && !m.is_true(r) && !m.is_false(r) && !g.dep(i) && !g.proofs_enabled() && !assert_expr(r, false)) { r = m.mk_false(); } change |= r != g.form(i); CTRACE("simplify", r != g.form(i), tout << r << " " << mk_pp(g.form(i), m) << "\n";); proof* new_pr = nullptr; if (g.proofs_enabled()) { new_pr = m.mk_modus_ponens(g.pr(i), m.mk_rewrite(g.form(i), r)); } g.update(i, r, new_pr, g.dep(i)); } pop(scope_level()); } SASSERT(scope_level() == 0); } /** \brief determine if a is dominated by b. Walk the immediate dominators of a upwards until hitting b or a term that is deeper than b. Save intermediary results in a cache to avoid recomputations. */ bool dom_simplify_tactic::is_subexpr(expr * a, expr * b) { if (a == b) return true; bool r; if (m_subexpr_cache.find(a, b, r)) return r; if (get_depth(a) >= get_depth(b)) { return false; } SASSERT(a != idom(a) && get_depth(idom(a)) > get_depth(a)); r = is_subexpr(idom(a), b); m_subexpr_cache.insert(a, b, r); return r; } ptr_vector const & dom_simplify_tactic::tree(expr * e) { if (auto p = m_dominators.get_tree().find_core(e)) return p->get_data().get_value(); return m_empty; } // --------------------- // expr_substitution_simplifier bool expr_substitution_simplifier::assert_expr(expr * t, bool sign) { m_scoped_substitution.push(); expr* tt; if (!sign) { update_substitution(t, nullptr); } else if (m.is_not(t, tt)) { update_substitution(tt, nullptr); } else { expr_ref nt(m.mk_not(t), m); update_substitution(nt, nullptr); } return true; } bool expr_substitution_simplifier::is_gt(expr* lhs, expr* rhs) { if (lhs == rhs) { return false; } if (m.is_value(rhs)) { return true; } SASSERT(is_ground(lhs) && is_ground(rhs)); if (depth(lhs) > depth(rhs)) { return true; } if (depth(lhs) == depth(rhs) && is_app(lhs) && is_app(rhs)) { app* l = to_app(lhs); app* r = to_app(rhs); if (l->get_decl()->get_id() != r->get_decl()->get_id()) { return l->get_decl()->get_id() > r->get_decl()->get_id(); } if (l->get_num_args() != r->get_num_args()) { return l->get_num_args() > r->get_num_args(); } for (unsigned i = 0; i < l->get_num_args(); ++i) { if (l->get_arg(i) != r->get_arg(i)) { return is_gt(l->get_arg(i), r->get_arg(i)); } } UNREACHABLE(); } return false; } void expr_substitution_simplifier::update_substitution(expr* n, proof* pr) { expr* lhs, *rhs, *n1; if (is_ground(n) && m.is_eq(n, lhs, rhs)) { compute_depth(lhs); compute_depth(rhs); m_trail.push_back(lhs); m_trail.push_back(rhs); if (is_gt(lhs, rhs)) { TRACE("propagate_values", tout << "insert " << mk_pp(lhs, m) << " -> " << mk_pp(rhs, m) << "\n";); m_scoped_substitution.insert(lhs, rhs, pr); return; } if (is_gt(rhs, lhs)) { TRACE("propagate_values", tout << "insert " << mk_pp(rhs, m) << " -> " << mk_pp(lhs, m) << "\n";); m_scoped_substitution.insert(rhs, lhs, m.mk_symmetry(pr)); return; } TRACE("propagate_values", tout << "incompatible " << mk_pp(n, m) << "\n";); } if (m.is_not(n, n1)) { m_scoped_substitution.insert(n1, m.mk_false(), m.mk_iff_false(pr)); } else { m_scoped_substitution.insert(n, m.mk_true(), m.mk_iff_true(pr)); } } void expr_substitution_simplifier::compute_depth(expr* e) { ptr_vector todo; todo.push_back(e); while (!todo.empty()) { e = todo.back(); unsigned d = 0; if (m_expr2depth.contains(e)) { todo.pop_back(); continue; } if (is_app(e)) { app* a = to_app(e); bool visited = true; for (expr* arg : *a) { unsigned d1 = 0; if (m_expr2depth.find(arg, d1)) { d = std::max(d, d1); } else { visited = false; todo.push_back(arg); } } if (!visited) { continue; } } todo.pop_back(); m_expr2depth.insert(e, d + 1); } } tactic * mk_dom_simplify_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(dom_simplify_tactic, m, alloc(expr_substitution_simplifier, m), p)); } z3-z3-4.8.7/src/tactic/core/dom_simplify_tactic.h000066400000000000000000000125131356505360400216120ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: dom_simplify_tactic.cpp Abstract: Dominator-based context simplifer. Author: Nikolaj and Nuno Notes: --*/ #ifndef DOM_SIMPLIFY_TACTIC_H_ #define DOM_SIMPLIFY_TACTIC_H_ #include "ast/ast.h" #include "ast/expr_substitution.h" #include "tactic/tactic.h" #include "tactic/tactical.h" #include "util/obj_pair_hashtable.h" class expr_dominators { public: typedef obj_map> tree_t; private: ast_manager& m; expr_ref m_root; obj_map m_expr2post; // reverse post-order number ptr_vector m_post2expr; tree_t m_parents; obj_map m_doms; tree_t m_tree; void add_edge(tree_t& tree, expr * src, expr* dst) { tree.insert_if_not_there2(src, ptr_vector())->get_data().m_value.push_back(dst); } void compute_post_order(); expr* intersect(expr* x, expr * y); bool compute_dominators(); void extract_tree(); std::ostream& display(std::ostream& out, unsigned indent, expr* r); public: expr_dominators(ast_manager& m): m(m), m_root(m) {} bool compile(expr * e); bool compile(unsigned sz, expr * const* es); tree_t const& get_tree() { return m_tree; } void reset(); expr* idom(expr *e) const { return m_doms[e]; } std::ostream& display(std::ostream& out); }; class dom_simplifier { public: dom_simplifier() {} virtual ~dom_simplifier() {} /** \brief assert_expr performs an implicit push */ virtual bool assert_expr(expr * t, bool sign) = 0; /** \brief apply simplification. */ virtual void operator()(expr_ref& r) = 0; /** \brief pop scopes accumulated from assertions. */ virtual void pop(unsigned num_scopes) = 0; virtual dom_simplifier * translate(ast_manager & m) = 0; virtual unsigned scope_level() const = 0; }; class dom_simplify_tactic : public tactic { ast_manager& m; dom_simplifier* m_simplifier; params_ref m_params; expr_ref_vector m_trail, m_args; obj_map m_result; expr_dominators m_dominators; unsigned m_depth; unsigned m_max_depth; ptr_vector m_empty; obj_pair_map m_subexpr_cache; bool m_forward; expr_ref simplify_rec(expr* t); expr_ref simplify_arg(expr* t); expr_ref simplify_ite(app * ite); expr_ref simplify_and(app * ite) { return simplify_and_or(true, ite); } expr_ref simplify_or(app * ite) { return simplify_and_or(false, ite); } expr_ref simplify_and_or(bool is_and, app * ite); void simplify_goal(goal& g); bool is_subexpr(expr * a, expr * b); expr_ref get_cached(expr* t) { expr* r = nullptr; if (!m_result.find(t, r)) r = t; return expr_ref(r, m); } void cache(expr *t, expr* r) { m_result.insert(t, r); m_trail.push_back(r); } ptr_vector const & tree(expr * e); expr* idom(expr *e) const { return m_dominators.idom(e); } unsigned scope_level() { return m_simplifier->scope_level(); } void pop(unsigned n) { SASSERT(n <= m_simplifier->scope_level()); m_simplifier->pop(n); } bool assert_expr(expr* f, bool sign) { return m_simplifier->assert_expr(f, sign); } bool init(goal& g); public: dom_simplify_tactic(ast_manager & m, dom_simplifier* s, params_ref const & p = params_ref()): m(m), m_simplifier(s), m_params(p), m_trail(m), m_args(m), m_dominators(m), m_depth(0), m_max_depth(1024), m_forward(true) {} ~dom_simplify_tactic() override; tactic * translate(ast_manager & m) override; void updt_params(params_ref const & p) override {} static void get_param_descrs(param_descrs & r) {} void collect_param_descrs(param_descrs & r) override { get_param_descrs(r); } void operator()(goal_ref const & in, goal_ref_buffer & result) override; void cleanup() override; }; class expr_substitution_simplifier : public dom_simplifier { ast_manager& m; expr_substitution m_subst; scoped_expr_substitution m_scoped_substitution; obj_map m_expr2depth; expr_ref_vector m_trail; // move from asserted_formulas to here.. void compute_depth(expr* e); bool is_gt(expr* lhs, expr* rhs); unsigned depth(expr* e) { return m_expr2depth[e]; } public: expr_substitution_simplifier(ast_manager& m): m(m), m_subst(m), m_scoped_substitution(m_subst), m_trail(m) {} ~expr_substitution_simplifier() override {} bool assert_expr(expr * t, bool sign) override; void update_substitution(expr* n, proof* pr); void operator()(expr_ref& r) override { r = m_scoped_substitution.find(r); } void pop(unsigned num_scopes) override { m_scoped_substitution.pop(num_scopes); } unsigned scope_level() const override { return m_scoped_substitution.scope_level(); } dom_simplifier * translate(ast_manager & m) override { SASSERT(m_subst.empty()); return alloc(expr_substitution_simplifier, m); } }; tactic * mk_dom_simplify_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("dom-simplify", "apply dominator simplification rules.", "mk_dom_simplify_tactic(m, p)") */ #endif z3-z3-4.8.7/src/tactic/core/elim_term_ite_tactic.cpp000066400000000000000000000123361356505360400222730ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: elim_term_ite_tactic.cpp Abstract: Eliminate term if-then-else by adding new fresh auxiliary variables. Author: Leonardo (leonardo) 2011-12-29 Notes: --*/ #include "tactic/tactical.h" #include "ast/normal_forms/defined_names.h" #include "ast/rewriter/rewriter_def.h" #include "tactic/generic_model_converter.h" class elim_term_ite_tactic : public tactic { struct rw_cfg : public default_rewriter_cfg { ast_manager & m; defined_names m_defined_names; ref m_mc; goal * m_goal; unsigned long long m_max_memory; // in bytes bool m_produce_models; unsigned m_num_fresh; bool max_steps_exceeded(unsigned num_steps) const { if (memory::get_allocation_size() > m_max_memory) throw tactic_exception(TACTIC_MAX_MEMORY_MSG); return false; } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { if (!m.is_term_ite(f)) return BR_FAILED; expr_ref new_ite(m); new_ite = m.mk_app(f, num, args); expr_ref new_def(m); proof_ref new_def_pr(m); app_ref _result(m); if (m_defined_names.mk_name(new_ite, new_def, new_def_pr, _result, result_pr)) { m_goal->assert_expr(new_def, new_def_pr, nullptr); m_num_fresh++; if (m_produce_models) { if (!m_mc) m_mc = alloc(generic_model_converter, m, "elim_term_ite"); m_mc->hide(_result->get_decl()); } } result = _result.get(); return BR_DONE; } rw_cfg(ast_manager & _m, params_ref const & p): m(_m), m_defined_names(m, nullptr /* don't use prefix */) { updt_params(p); m_goal = nullptr; m_num_fresh = 0; } void updt_params(params_ref const & p) { m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); } }; struct rw : public rewriter_tpl { rw_cfg m_cfg; rw(ast_manager & m, params_ref const & p): rewriter_tpl(m, m.proofs_enabled(), m_cfg), m_cfg(m, p) { } }; struct imp { ast_manager & m; rw m_rw; 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-term-ite", *g); bool produce_proofs = g->proofs_enabled(); m_rw.cfg().m_produce_models = g->models_enabled(); m_rw.m_cfg.m_num_fresh = 0; 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-term-ite-consts", m_rw.m_cfg.m_num_fresh); g->inc_depth(); result.push_back(g.get()); TRACE("elim_term_ite", g->display(tout);); SASSERT(g->is_well_sorted()); } }; imp * m_imp; params_ref m_params; public: elim_term_ite_tactic(ast_manager & m, params_ref const & p): m_params(p) { m_imp = alloc(imp, m, p); } ~elim_term_ite_tactic() override { dealloc(m_imp); } tactic * translate(ast_manager & m) override { return alloc(elim_term_ite_tactic, m, m_params); } void updt_params(params_ref const & p) override { m_params = p; m_imp->m_rw.cfg().updt_params(p); } 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."); } void operator()(goal_ref const & in, goal_ref_buffer & result) override { (*m_imp)(in, result); } void cleanup() override { ast_manager & m = m_imp->m; m_imp->~imp(); m_imp = new (m_imp) imp(m, m_params); } }; tactic * mk_elim_term_ite_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(elim_term_ite_tactic, m, p)); } z3-z3-4.8.7/src/tactic/core/elim_term_ite_tactic.h000066400000000000000000000011301356505360400217260ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: elim_term_ite_tactic.h Abstract: Eliminate term if-then-else by adding new fresh auxiliary variables. Author: Leonardo (leonardo) 2011-12-29 Notes: --*/ #ifndef ELIM_TERM_ITE_TACTIC_H_ #define ELIM_TERM_ITE_TACTIC_H_ #include "util/params.h" class ast_manager; class tactic; tactic * mk_elim_term_ite_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("elim-term-ite", "eliminate term if-then-else by adding fresh auxiliary declarations.", "mk_elim_term_ite_tactic(m, p)") */ #endif z3-z3-4.8.7/src/tactic/core/elim_uncnstr_tactic.cpp000066400000000000000000001007411356505360400221550ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: elim_uncnstr_vars.cpp Abstract: Eliminated unconstrained variables. Author: Leonardo (leonardo) 2011-10-22 Notes: --*/ #include "tactic/tactical.h" #include "tactic/generic_model_converter.h" #include "ast/rewriter/rewriter_def.h" #include "ast/arith_decl_plugin.h" #include "ast/bv_decl_plugin.h" #include "ast/array_decl_plugin.h" #include "ast/datatype_decl_plugin.h" #include "tactic/core/collect_occs.h" #include "ast/ast_smt2_pp.h" #include "ast/ast_ll_pp.h" namespace { class elim_uncnstr_tactic : public tactic { // unconstrained vars collector 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 { 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; } 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_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; 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; 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)); } 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; } 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; } } 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; } } 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; } 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; } 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_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; } } 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 = 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", *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_manager(m), m_params(p) { updt_params(p); } tactic * translate(ast_manager & m) override { return alloc(elim_uncnstr_tactic, m, m_params); } void updt_params(params_ref const & p) override { m_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 { insert_max_memory(r); insert_max_steps(r); } void operator()(goal_ref const & g, goal_ref_buffer & result) override { run(g, result); report_tactic_progress(":num-elim-apps", m_num_elim_apps); } void cleanup() override { m_mc = nullptr; m_rw = nullptr; m_vars.reset(); } void collect_statistics(statistics & st) const override { st.update("eliminated applications", m_num_elim_apps); } void reset_statistics() override { 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)); } z3-z3-4.8.7/src/tactic/core/elim_uncnstr_tactic.h000066400000000000000000000010001356505360400216060ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: elim_uncnstr_tactic.h Abstract: Eliminated applications containing unconstrained variables. Author: Leonardo (leonardo) 2011-10-22 Notes: --*/ #pragma once #include "util/params.h" class tactic; class ast_manager; tactic * mk_elim_uncnstr_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("elim-uncnstr", "eliminate application containing unconstrained variables.", "mk_elim_uncnstr_tactic(m, p)") */ z3-z3-4.8.7/src/tactic/core/injectivity_tactic.cpp000066400000000000000000000211631356505360400220140ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: injectivity_tactic.cpp Abstract: Injectivity tactics - Discover axioms of the form `forall x. (= (g (f x)) x` Mark `f` as injective - Rewrite (sub)terms of the form `(= (f x) (f y))` to `(= x y)` whenever `f` is injective. Author: Nicolas Braud-Santoni (t-nibrau) 2017-08-10 Notes: --*/ #include #include #include "tactic/tactical.h" #include "ast/rewriter/rewriter_def.h" #include "tactic/core/injectivity_tactic.h" #include "util/dec_ref_util.h" class injectivity_tactic : public tactic { struct InjHelper : public obj_map*> { ast_manager & m_manager; void insert(func_decl* const f, func_decl* const g) { obj_hashtable *m; if (! obj_map::find(f, m)) { m_manager.inc_ref(f); m = alloc(obj_hashtable); // TODO: Check we don't leak memory obj_map::insert(f, m); } if (!m->contains(g)) { m_manager.inc_ref(g); m->insert(g); } } bool find(func_decl* const f, func_decl* const g) const { obj_hashtable *m; if(! obj_map::find(f, m)) return false; return m->contains(g); } InjHelper(ast_manager& m) : obj_map*>(), m_manager(m) {} ~InjHelper() { for(auto m : *this) { for (func_decl* f : *m.get_value()) m_manager.dec_ref(f); m_manager.dec_ref(m.m_key); dealloc(m.m_value); } } }; struct finder { ast_manager & m_manager; InjHelper & inj_map; finder(ast_manager & m, InjHelper & map, params_ref const & p) : m_manager(m), inj_map(map) { updt_params(p); } ast_manager & m() const { return m_manager; } bool is_axiom(expr* n, func_decl* &f, func_decl* &g) { if (!is_forall(n)) return false; quantifier* const q = to_quantifier(n); if (q->get_num_decls() != 1) return false; const expr * const body = q->get_expr(); // n ~= forall x. body if (!m().is_eq(body)) return false; const app * const body_a = to_app(body); if (body_a->get_num_args() != 2) return false; const expr* a = body_a->get_arg(0); const expr* b = body_a->get_arg(1); // n ~= forall x. (= a b) if (is_app(a) && is_var(b)) { // Do nothing } else if (is_app(b) && is_var(a)) { std::swap(a, b); } else return false; const app* const a_app = to_app(a); const var* const b_var = to_var(b); if (b_var->get_idx() != 0) // idx is the De Bruijn's index return false; if (a_app->get_num_args() != 1) return false; g = a_app->get_decl(); const expr* const a_body = a_app->get_arg(0); // n ~= forall x. (= (g a_body) x) if (!is_app(a_body)) return false; const app* const a_body_app = to_app(a_body); if (a_body_app->get_num_args() != 1) // Maybe TODO: support multi-argument functions return false; f = a_body_app->get_decl(); const expr* const a_body_body = a_body_app->get_arg(0); // n ~= forall x. (= (g (f a_body_body)) x) if (a_body_body != b_var) return false; // n ~= forall x. (= (g (f x)) x) return true; } void operator()(goal_ref const & goal, goal_ref_buffer & result) { SASSERT(goal->is_well_sorted()); tactic_report report("injectivity", *goal); fail_if_unsat_core_generation("injectivity", goal); // TODO: Support UNSAT cores fail_if_proof_generation("injectivity", goal); for (unsigned i = 0; i < goal->size(); ++i) { func_decl *f, *g; if (!is_axiom(goal->form(i), f, g)) continue; TRACE("injectivity", tout << "Marking " << f->get_name() << " as injective" << std::endl;); inj_map.insert(f, g); // TODO: Record that g is f's pseudoinverse } } void updt_params(params_ref const & p) {} }; struct rewriter_eq_cfg : public default_rewriter_cfg { ast_manager & m_manager; InjHelper & inj_map; // expr_ref_vector m_out; // sort_ref_vector m_bindings; ast_manager & m() const { return m_manager; } rewriter_eq_cfg(ast_manager & m, InjHelper & map, params_ref const & p) : m_manager(m), inj_map(map) { } ~rewriter_eq_cfg() { } void cleanup_buffers() { // m_out.finalize(); } void reset() { } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { if(num != 2) return BR_FAILED; if (!m().is_eq(f)) return BR_FAILED; // We are rewriting (= a b) if (!is_app(args[0]) || !is_app(args[1])) return BR_FAILED; const app* const a = to_app(args[0]); const app* const b = to_app(args[1]); // a and b are applications of the same function if (a->get_decl() != b->get_decl()) return BR_FAILED; // Maybe TODO: Generalize to multi-parameter functions ? if (a->get_num_args() != 1 || b->get_num_args() != 1) return BR_FAILED; if (!inj_map.contains(a->get_decl())) return BR_FAILED; SASSERT(m().get_sort(a->get_arg(0)) == m().get_sort(b->get_arg(0))); TRACE("injectivity", tout << "Rewriting (= " << mk_ismt2_pp(args[0], m()) << " " << mk_ismt2_pp(args[1], m()) << ")" << std::endl;); result = m().mk_eq(a->get_arg(0), b->get_arg(0)); result_pr = nullptr; return BR_DONE; } }; struct rewriter_eq : public rewriter_tpl { rewriter_eq_cfg m_cfg; rewriter_eq(ast_manager & m, InjHelper & map, params_ref const & p) : rewriter_tpl(m, m.proofs_enabled(), m_cfg), m_cfg(m, map, p) { } }; struct rewriter_inverse { }; finder * m_finder; rewriter_eq * m_eq; InjHelper * m_map; // rewriter_inverse * m_inverse; params_ref m_params; ast_manager & m_manager; public: injectivity_tactic(ast_manager & m, params_ref const & p): m_params(p), m_manager(m) { TRACE("injectivity", tout << "constructed new tactic" << std::endl;); m_map = alloc(InjHelper, m); m_finder = alloc(finder, m, *m_map, p); m_eq = alloc(rewriter_eq, m, *m_map, p); } tactic * translate(ast_manager & m) override { return alloc(injectivity_tactic, m, m_params); } ~injectivity_tactic() override { dealloc(m_finder); dealloc(m_eq); dealloc(m_map); } void updt_params(params_ref const & p) override { m_params = p; m_finder->updt_params(p); } void collect_param_descrs(param_descrs & r) override { insert_max_memory(r); insert_produce_models(r); } void operator()(goal_ref const & g, goal_ref_buffer & result) override { (*m_finder)(g, result); for (unsigned i = 0; i < g->size(); ++i) { expr* curr = g->form(i); expr_ref rw(m_manager); proof_ref pr(m_manager); (*m_eq)(curr, rw, pr); g->update(i, rw, pr, g->dep(i)); } result.push_back(g.get()); } void cleanup() override { InjHelper * m = alloc(InjHelper, m_manager); finder * f = alloc(finder, m_manager, *m, m_params); rewriter_eq * r = alloc(rewriter_eq, m_manager, *m, m_params); std::swap(m, m_map), std::swap(f, m_finder), std::swap(r, m_eq); dealloc(m), dealloc(f), dealloc(r); } }; tactic * mk_injectivity_tactic(ast_manager & m, params_ref const & p) { return alloc(injectivity_tactic, m, p); } z3-z3-4.8.7/src/tactic/core/injectivity_tactic.h000066400000000000000000000010111356505360400214470ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: injectivity_tactic.h Abstract: Injectivity tactics Author: Nicolas Braud-Santoni (t-nibrau) 2017-08-10 Notes: --*/ #ifndef INJECTIVITY_TACTIC_H_ #define INJECTIVITY_TACTIC_H_ #include "util/params.h" class ast_manager; class tactic; tactic * mk_injectivity_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("injectivity", "Identifies and applies injectivity axioms.", "mk_injectivity_tactic(m, p)") */ #endif z3-z3-4.8.7/src/tactic/core/nnf_tactic.cpp000066400000000000000000000062421356505360400202350ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: nnf_tactic.cpp Abstract: NNF tactic Author: Leonardo de Moura (leonardo) 2011-12-28. Revision History: --*/ #include "ast/normal_forms/nnf.h" #include "tactic/tactical.h" #include "tactic/generic_model_converter.h" class nnf_tactic : public tactic { params_ref m_params; nnf * m_nnf; struct set_nnf { nnf_tactic & m_owner; set_nnf(nnf_tactic & owner, nnf & n): m_owner(owner) { m_owner.m_nnf = &n; } ~set_nnf() { m_owner.m_nnf = nullptr; } }; public: nnf_tactic(params_ref const & p): m_params(p), m_nnf(nullptr) { TRACE("nnf", tout << "nnf_tactic constructor: " << p << "\n";); } tactic * translate(ast_manager & m) override { return alloc(nnf_tactic, m_params); } ~nnf_tactic() override {} void updt_params(params_ref const & p) override { m_params = p; } void collect_param_descrs(param_descrs & r) override { nnf::get_param_descrs(r); } void operator()(goal_ref const & g, goal_ref_buffer & result) override { TRACE("nnf", tout << "params: " << m_params << "\n"; g->display(tout);); SASSERT(g->is_well_sorted()); tactic_report report("nnf", *g); bool produce_proofs = g->proofs_enabled(); ast_manager & m = g->m(); defined_names dnames(m); nnf local_nnf(m, dnames, m_params); set_nnf setter(*this, local_nnf); expr_ref_vector defs(m); proof_ref_vector def_prs(m); 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); local_nnf(curr, defs, def_prs, new_curr, new_pr); if (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)); } sz = defs.size(); for (unsigned i = 0; i < sz; i++) { if (produce_proofs) g->assert_expr(defs.get(i), def_prs.get(i), nullptr); else g->assert_expr(defs.get(i), nullptr, nullptr); } g->inc_depth(); result.push_back(g.get()); unsigned num_extra_names = dnames.get_num_names(); if (num_extra_names > 0) { generic_model_converter * fmc = alloc(generic_model_converter, m, "nnf"); g->add(fmc); for (unsigned i = 0; i < num_extra_names; i++) fmc->hide(dnames.get_name_decl(i)); } TRACE("nnf", g->display(tout);); SASSERT(g->is_well_sorted()); } void cleanup() override {} }; tactic * mk_snf_tactic(ast_manager & m, params_ref const & p) { return alloc(nnf_tactic, p); } tactic * mk_nnf_tactic(ast_manager & m, params_ref const & p) { params_ref new_p(p); new_p.set_sym("mode", symbol("full")); TRACE("nnf", tout << "mk_nnf: " << new_p << "\n";); return using_params(mk_snf_tactic(m, p), new_p); } z3-z3-4.8.7/src/tactic/core/nnf_tactic.h000066400000000000000000000011531356505360400176760ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: nnf_tactic.h Abstract: NNF tactic Author: Leonardo de Moura (leonardo) 2011-12-28. Revision History: --*/ #ifndef NNF_TACTIC_H_ #define NNF_TACTIC_H_ #include "util/params.h" class ast_manager; class tactic; tactic * mk_snf_tactic(ast_manager & m, params_ref const & p = params_ref()); tactic * mk_nnf_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("snf", "put goal in skolem normal form.", "mk_snf_tactic(m, p)") ADD_TACTIC("nnf", "put goal in negation normal form.", "mk_nnf_tactic(m, p)") */ #endif z3-z3-4.8.7/src/tactic/core/occf_tactic.cpp000066400000000000000000000153761356505360400203760ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: occf_tactic.cpp Abstract: Put clauses in the assertion set in OOC (one constraint per clause) form. Constraints occurring in formulas that are not clauses are ignored. The formula can be put into CNF by using mk_sat_preprocessor strategy. Author: Leonardo de Moura (leonardo) 2011-12-28. Revision History: --*/ #include "tactic/tactical.h" #include "tactic/core/occf_tactic.h" #include "tactic/generic_model_converter.h" class occf_tactic : public tactic { struct imp { ast_manager & m; generic_model_converter * m_mc; imp(ast_manager & _m): m(_m) { } void checkpoint() { if (m.canceled()) throw tactic_exception(TACTIC_CANCELED_MSG); } bool is_literal(expr * t) const { expr * atom; return is_uninterp_const(t) || (m.is_not(t, atom) && is_uninterp_const(atom)); } bool is_constraint(expr * t) const { return !is_literal(t); } bool is_target(app * cls) { SASSERT(m.is_or(cls)); bool found = false; unsigned num = cls->get_num_args(); for (unsigned i = 0; i < num; i++) { if (is_constraint(cls->get_arg(i))) { if (found) return true; found = true; } } return false; } struct bvar_info { expr * m_bvar; unsigned m_gen_pos:1; unsigned m_gen_neg:1; bvar_info():m_bvar(nullptr), m_gen_pos(false), m_gen_neg(false) {} bvar_info(expr * var, bool sign): m_bvar(var), m_gen_pos(!sign), m_gen_neg(sign) { } }; typedef obj_map cnstr2bvar; expr * get_aux_lit(cnstr2bvar & c2b, expr * cnstr, goal_ref const & g) { bool sign = false; while (m.is_not(cnstr)) { cnstr = to_app(cnstr)->get_arg(0); sign = !sign; } cnstr2bvar::obj_map_entry * entry = c2b.find_core(cnstr); if (entry == nullptr) return nullptr; bvar_info & info = entry->get_data().m_value; if (sign) { if (!info.m_gen_neg) { info.m_gen_neg = true; g->assert_expr(m.mk_or(info.m_bvar, m.mk_not(cnstr)), nullptr, nullptr); } return m.mk_not(info.m_bvar); } else { if (!info.m_gen_pos) { info.m_gen_pos = true; g->assert_expr(m.mk_or(m.mk_not(info.m_bvar), cnstr), nullptr, nullptr); } return info.m_bvar; } } expr * mk_aux_lit(cnstr2bvar & c2b, expr * cnstr, bool produce_models, goal_ref const & g) { bool sign = false; while (m.is_not(cnstr)) { cnstr = to_app(cnstr)->get_arg(0); sign = !sign; } SASSERT(!c2b.contains(cnstr)); expr * bvar = m.mk_fresh_const(nullptr, m.mk_bool_sort()); if (produce_models) m_mc->hide(to_app(bvar)->get_decl()); c2b.insert(cnstr, bvar_info(bvar, sign)); if (sign) { g->assert_expr(m.mk_or(bvar, m.mk_not(cnstr)), nullptr, nullptr); return m.mk_not(bvar); } else { g->assert_expr(m.mk_or(m.mk_not(bvar), cnstr), nullptr, nullptr); return bvar; } } void operator()(goal_ref const & g, goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); fail_if_proof_generation("occf", g); bool produce_models = g->models_enabled(); tactic_report report("occf", *g); m_mc = nullptr; ptr_vector new_lits; cnstr2bvar c2b; unsigned sz = g->size(); for (unsigned i = 0; i < sz; i++) { checkpoint(); expr * f = g->form(i); expr_dependency * d = g->dep(i); if (!m.is_or(f)) continue; app * cls = to_app(f); if (!is_target(cls)) continue; if (produce_models && !m_mc) { m_mc = alloc(generic_model_converter, m, "occf"); g->add(m_mc); } expr * keep = nullptr; new_lits.reset(); unsigned num = cls->get_num_args(); for (unsigned j = 0; j < num; j++) { expr * l = cls->get_arg(j); if (is_constraint(l)) { expr * new_l = get_aux_lit(c2b, l, g); if (new_l != nullptr) { new_lits.push_back(new_l); } else if (keep == nullptr) { keep = l; } else { new_l = mk_aux_lit(c2b, l, produce_models, g); new_lits.push_back(new_l); } } else { new_lits.push_back(l); } } if (keep != nullptr) new_lits.push_back(keep); g->update(i, m.mk_or(new_lits.size(), new_lits.c_ptr()), nullptr, d); } g->inc_depth(); result.push_back(g.get()); TRACE("occf", g->display(tout);); SASSERT(g->is_well_sorted()); } }; imp * m_imp; public: occf_tactic(ast_manager & m) { m_imp = alloc(imp, m); } tactic * translate(ast_manager & m) override { return alloc(occf_tactic, m); } ~occf_tactic() override { dealloc(m_imp); } void updt_params(params_ref const & p) override {} void collect_param_descrs(param_descrs & r) override {} void operator()(goal_ref const & in, goal_ref_buffer & result) override { (*m_imp)(in, result); } void cleanup() override { imp * d = alloc(imp, m_imp->m); std::swap(d, m_imp); dealloc(d); } }; tactic * mk_occf_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(occf_tactic, m)); } z3-z3-4.8.7/src/tactic/core/occf_tactic.h000066400000000000000000000014171356505360400200320ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: occf_tactic.h Abstract: Put clauses in the assertion set in OOC (one constraint per clause) form. Constraints occurring in formulas that are not clauses are ignored. The formula can be put into CNF by using mk_sat_preprocessor strategy. Author: Leonardo de Moura (leonardo) 2011-12-28. Revision History: --*/ #ifndef OCCF_TACTIC_H_ #define OCCF_TACTIC_H_ #include "util/params.h" class ast_manager; class tactic; tactic * mk_occf_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("occf", "put goal in one constraint per clause normal form (notes: fails if proof generation is enabled; only clauses are considered).", "mk_occf_tactic(m, p)") */ #endif z3-z3-4.8.7/src/tactic/core/pb_preprocess_tactic.cpp000066400000000000000000000513521356505360400223240ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: pb_preprocess_tactic.cpp Abstract: Pre-process pseudo-Boolean inequalities using generalized Davis Putnam (resolution) to eliminate variables. Author: Nikolaj Bjorner (nbjorner) 2013-12-23 Notes: Resolution for PB constraints require the implicit inequalities that each variable ranges over [0,1] so not all resolvents produce smaller sets of clauses. We here implement subsumption resolution. x + y >= 1 A~x + B~y + Cz >= k --------------------- Cz >= k - B where A <= B, x, y do not occur elsewhere. --*/ #include "tactic/core/pb_preprocess_tactic.h" #include "tactic/tactical.h" #include "tactic/generic_model_converter.h" #include "ast/for_each_expr.h" #include "ast/pb_decl_plugin.h" #include "ast/rewriter/th_rewriter.h" #include "ast/expr_substitution.h" #include "ast/ast_pp.h" class pb_preprocess_tactic : public tactic { struct rec { unsigned_vector pos, neg; rec() { } }; typedef obj_map var_map; ast_manager& m; pb_util pb; var_map m_vars; unsigned_vector m_ge; unsigned_vector m_other; bool m_progress; th_rewriter m_r; struct declassifier { var_map& m_vars; declassifier(var_map& v): m_vars(v) {} void operator()(app* e) { if (m_vars.contains(e)) { m_vars.remove(e); } } void operator()(var*) {} void operator()(quantifier*) {} }; void display_annotation(std::ostream& out, goal_ref const& g) { for (unsigned i = 0; i < m_ge.size(); ++i) { out << "ge " << m_ge[i] << ": " << mk_pp(g->form(m_ge[i]), m) << "\n"; } for (unsigned i = 0; i < m_other.size(); ++i) { out << "ot " << m_other[i] << ": " << mk_pp(g->form(m_other[i]), m) << "\n"; } for (auto const& kv : m_vars) { app* e = kv.m_key; unsigned_vector const& pos = kv.m_value.pos; unsigned_vector const& neg = kv.m_value.neg; out << mk_pp(e, m) << ": "; for (unsigned p : pos) { out << "p: " << p << " "; } for (unsigned n : neg) { out << "n: " << n << " "; } out << "\n"; } } void set_value(generic_model_converter& mc, expr* e, bool p) { while (m.is_not(e, e)) { p = !p; } SASSERT(is_app(e)); mc.add(to_app(e), p?m.mk_true():m.mk_false()); } public: pb_preprocess_tactic(ast_manager& m, params_ref const& p = params_ref()): m(m), pb(m), m_r(m) {} ~pb_preprocess_tactic() override {} tactic * translate(ast_manager & m) override { return alloc(pb_preprocess_tactic, m); } void operator()( goal_ref const & g, goal_ref_buffer & result) override { SASSERT(g->is_well_sorted()); if (g->proofs_enabled()) { throw tactic_exception("pb-preprocess does not support proofs"); } generic_model_converter* pp = alloc(generic_model_converter, m, "pb-preprocess"); g->add(pp); g->inc_depth(); result.push_back(g.get()); while (simplify(g, *pp)); // decompose(g); } bool simplify(goal_ref const& g, generic_model_converter& mc) { reset(); normalize(g); if (g->inconsistent()) { return false; } for (unsigned i = 0; i < g->size(); ++i) { process_vars(i, g); } if (m_ge.empty()) { return false; } for (unsigned i = 0; i < m_ge.size(); ++i) { classify_vars(i, to_app(g->form(m_ge[i]))); } declassifier dcl(m_vars); expr_mark visited; for (unsigned i = 0; !m_vars.empty() && i < m_other.size(); ++i) { for_each_expr(dcl, visited, g->form(m_other[i])); } if (m_vars.empty()) { return false; } // display_annotation(tout, g); m_progress = false; // first eliminate variables var_map::iterator it = next_resolvent(m_vars.begin()); while (it != m_vars.end()) { app * e = it->m_key; rec const& r = it->m_value; TRACE("pb", tout << mk_pp(e, m) << " " << r.pos.size() << " " << r.neg.size() << "\n";); if (r.pos.empty()) { replace(r.neg, e, m.mk_false(), g); set_value(mc, e, false); } else if (r.neg.empty()) { replace(r.pos, e, m.mk_true(), g); set_value(mc, e, true); } if (g->inconsistent()) return false; ++it; it = next_resolvent(it); } // now resolve clauses. it = next_resolvent(m_vars.begin()); while (it != m_vars.end()) { app * e = it->m_key; SASSERT(is_uninterp_const(e)); rec const& r = it->m_value; if (r.pos.size() == 1 && !r.neg.empty()) { resolve(mc, r.pos[0], r.neg, e, true, g); } else if (r.neg.size() == 1 && !r.pos.empty()) { resolve(mc, r.neg[0], r.pos, e, false, g); } if (g->inconsistent()) return false; ++it; it = next_resolvent(it); } // now check for subsumption. for (unsigned i = 0; i < m_ge.size(); ++i) { expr_ref_vector args1(m), args2(m); vector coeffs1, coeffs2; rational k1, k2; expr* fml = g->form(m_ge[i]); if (!to_ge(fml, args1, coeffs1, k1)) continue; if (args1.empty()) continue; expr* arg = args1[0].get(); bool neg = m.is_not(arg, arg); if (!is_uninterp_const(arg)) continue; if (!m_vars.contains(to_app(arg))) continue; rec const& r = m_vars.find(to_app(arg)); unsigned_vector const& pos = neg?r.neg:r.pos; for (unsigned j = 0; j < pos.size(); ++j) { unsigned k = pos[j]; if (k == m_ge[i]) continue; if (!to_ge(g->form(k), args2, coeffs2, k2)) continue; if (subsumes(args1, coeffs1, k1, args2, coeffs2, k2)) { IF_VERBOSE(3, verbose_stream() << "replace " << mk_pp(g->form(k), m) << "\n";); g->update(k, m.mk_true(), nullptr, m.mk_join(g->dep(m_ge[i]), g->dep(k))); m_progress = true; } } } g->elim_true(); return m_progress; } void updt_params(params_ref const & p) override { } void cleanup() override { } private: void reset() override { m_ge.reset(); m_other.reset(); m_vars.reset(); } expr* negate(expr* e) { if (m.is_not(e, e)) return e; return m.mk_not(e); } void normalize(goal_ref const& g) { expr* r; expr_ref tmp(m); for (unsigned i = 0; !g->inconsistent() && i < g->size(); ++i) { expr* e = g->form(i); if (m.is_not(e, r) && pb.is_ge(r)) { rational k = pb.get_k(r); rational sum(0); expr_ref_vector args(m); vector coeffs; for (unsigned j = 0; j < to_app(r)->get_num_args(); ++j) { sum += pb.get_coeff(r, j); coeffs.push_back(pb.get_coeff(r, j)); args.push_back(negate(to_app(r)->get_arg(j))); } tmp = pb.mk_ge(args.size(), coeffs.c_ptr(), args.c_ptr(), sum - k + rational::one()); g->update(i, tmp, g->pr(i), g->dep(i)); } } } unsigned log2ceil(unsigned n) { unsigned p = 1; while (n > 0) { n /= 2; ++p; } return p; } /** \brief decompose large sums into smaller sums by introducing auxiliary variables. */ void decompose(goal_ref const& g) { expr_ref fml1(m), fml2(m); for (unsigned i = 0; !g->inconsistent() && i < g->size(); ++i) { expr* e = g->form(i); unsigned_vector cuts; if (cut(e, cuts)) { app* a = to_app(e); expr_ref_vector cut_args(m); vector cut_coeffs; if (cuts.size() < 2) continue; unsigned start = 0; for (unsigned j = 0; j < cuts.size(); ++j) { unsigned end = cuts[j]; fml1 = decompose_cut(a, start, end, cut_args, cut_coeffs); g->assert_expr(fml1, nullptr, g->dep(i)); start = end; TRACE("pb", tout << fml1 << "\n";); } fml2 = pb.mk_ge(cut_args.size(), cut_coeffs.c_ptr(), cut_args.c_ptr(), pb.get_k(e)); g->update(i, fml2, nullptr, g->dep(i)); TRACE("pb", tout << fml2 << "\n";); } } } bool cut(expr* e, unsigned_vector& cuts) { if (!pb.is_ge(e)) return false; if (to_app(e)->get_num_args() <= 20) return false; unsigned n = 0, cut = 0; unsigned sz = to_app(e)->get_num_args(); for (unsigned i = 0; i < sz; ++i) { rational r = pb.get_coeff(e, i); if (!r.is_unsigned()) { return false; } n += r.get_unsigned(); if (2*log2ceil(n) < cut) { cuts.push_back(i+1); n = 0; cut = 0; } else { ++cut; } } if (!cuts.empty() && cuts.back() + 20 >= sz) { cuts.pop_back(); } cuts.push_back(sz); return true; } expr_ref decompose_cut(app* e, unsigned start, unsigned end, expr_ref_vector& cut_args, vector& cut_coeffs) { unsigned n = 0, j = 1; vector coeffs; expr_ref_vector args(m); app_ref z(m); expr_ref fml1(m), fml(m); for (unsigned i = start; i < end; ++i) { rational r = pb.get_coeff(e, i); n += r.get_unsigned(); args.push_back(e->get_arg(i)); coeffs.push_back(r); } while (j <= n) { z = m.mk_fresh_const("z", m.mk_bool_sort()); coeffs.push_back(-rational(j)); args.push_back(z); cut_coeffs.push_back(rational(j)); cut_args.push_back(z); j <<= 1; } fml1 = pb.mk_ge(args.size(), coeffs.c_ptr(), args.c_ptr(), rational(0)); m_r(fml1, fml); return fml; } void process_vars(unsigned i, goal_ref const& g) { expr* r, *e; e = g->form(i); if (is_uninterp_const(e)) { m_ge.push_back(i); } else if (pb.is_ge(e) && pure_args(to_app(e))) { m_ge.push_back(i); } else if (m.is_or(e) && pure_args(to_app(e))) { m_ge.push_back(i); } else if (m.is_not(e, r) && is_uninterp_const(r)) { m_ge.push_back(i); } else { m_other.push_back(i); } } void classify_vars(unsigned idx, app* e) { expr* r; if (m.is_not(e, r) && is_uninterp_const(r)) { insert(idx, to_app(r), false); return; } if (is_uninterp_const(e)) { insert(idx, e, true); return; } for (unsigned i = 0; i < e->get_num_args(); ++i) { expr* arg = e->get_arg(i); if (m.is_true(arg) || m.is_false(arg)) { // no-op } else if (m.is_not(arg, r)) { SASSERT(is_uninterp_const(r)); insert(idx, to_app(r), false); } else { SASSERT(is_uninterp_const(arg)); insert(idx, to_app(arg), true); } } } void insert(unsigned i, app* e, bool pos) { SASSERT(is_uninterp_const(e)); if (!m_vars.contains(e)) { m_vars.insert(e, rec()); } if (pos) { m_vars.find(e).pos.push_back(i); } else { m_vars.find(e).neg.push_back(i); } } bool pure_args(app* a) const { for (unsigned i = 0; i < a->get_num_args(); ++i) { expr* e = a->get_arg(i); m.is_not(e, e); if (!is_uninterp_const(e) && !m.is_true(e) && !m.is_false(e)) { return false; } } return true; } var_map::iterator next_resolvent(var_map::iterator it) { if (it == m_vars.end()) { return it; } while (it != m_vars.end() && it->m_value.pos.size() > 1 && it->m_value.neg.size() > 1) { ++it; } return it; } rational get_coeff(unsigned num_args, expr* const* args, rational const* coeffs, expr* e) { for (unsigned i = 0; i < num_args; ++i) { if (args[i] == e) return coeffs[i]; } return rational::zero(); } // // one of the formulas are replaced by T after resolution // so if there is a pointer into that formula, we can no // longer assume variables have unique occurrences. // bool is_valid(unsigned_vector const& positions, goal_ref const& g) const { for (unsigned i = 0; i < positions.size(); ++i) { unsigned idx = positions[i]; if (m.is_true(g->form(idx))) return false; } return true; } bool is_reduction(unsigned_vector const& pos, app* fml, goal_ref const& g) { unsigned sz = fml->get_num_args(); for (unsigned i = 0; i < pos.size(); ++i) { if (!is_app(g->form(pos[i]))) return false; if (to_app(g->form(pos[i]))->get_num_args() < sz) return false; } return true; } // Implement very special case of resolution. void resolve(generic_model_converter& mc, unsigned idx1, unsigned_vector const& positions, app* e, bool pos, goal_ref const& g) { if (positions.size() != 1) return; unsigned idx2 = positions[0]; expr_ref tmp1(m), tmp2(m); expr* fml1 = g->form(idx1); expr* fml2 = g->form(idx2); TRACE("pb", tout << mk_pp(fml1, m) << " " << mk_pp(fml2, m) << "\n";); expr_ref_vector args1(m), args2(m); vector coeffs1, coeffs2; rational k1, k2; if (!to_ge(fml1, args1, coeffs1, k1)) return; if (!k1.is_one()) return; if (!to_ge(g->form(idx2), args2, coeffs2, k2)) return; // check that each variable in idx1 occurs only in idx2 unsigned min_index = 0; rational min_coeff(0); unsigned_vector indices; for (unsigned i = 0; i < args1.size(); ++i) { expr* x = args1[i].get(); m.is_not(x, x); if (!is_app(x)) return; if (!m_vars.contains(to_app(x))) return; TRACE("pb", tout << mk_pp(x, m) << "\n";); rec const& r = m_vars.find(to_app(x)); if (r.pos.size() != 1 || r.neg.size() != 1) return; if (r.pos[0] != idx2 && r.neg[0] != idx2) return; for (unsigned j = 0; j < args2.size(); ++j) { if (is_complement(args1[i].get(), args2[j].get())) { if (i == 0) { min_coeff = coeffs2[j]; } else if (min_coeff > coeffs2[j]) { min_coeff = coeffs2[j]; min_index = j; } indices.push_back(j); } } } for (unsigned i = 0; i < indices.size(); ++i) { unsigned j = indices[i]; expr* arg = args2[j].get(); if (j == min_index) { args2[j] = m.mk_false(); } else { args2[j] = m.mk_true(); } set_value(mc, arg, j != min_index); } tmp1 = pb.mk_ge(args2.size(), coeffs2.c_ptr(), args2.c_ptr(), k2); IF_VERBOSE(3, verbose_stream() << " " << tmp1 << "\n"; for (unsigned i = 0; i < args2.size(); ++i) { verbose_stream() << mk_pp(args2[i].get(), m) << " "; } verbose_stream() << "\n"; ); m_r(tmp1, tmp2); if (pb.is_ge(tmp2) && pb.get_k(to_app(tmp2)).is_one()) { tmp2 = m.mk_or(to_app(tmp2)->get_num_args(), to_app(tmp2)->get_args()); } IF_VERBOSE(3, verbose_stream() << "resolve: " << mk_pp(fml1, m) << "\n" << mk_pp(fml2, m) << "\n" << tmp1 << "\n"; verbose_stream() << "to\n" << mk_pp(fml2, m) << " -> " << tmp2 << "\n";); TRACE("pb", tout << "resolve: " << mk_pp(fml1, m) << "\n" << mk_pp(fml2, m) << "\n" << tmp1 << "\n"; tout << "to\n" << mk_pp(fml2, m) << " -> " << tmp2 << "\n";); g->update(idx2, tmp2, nullptr, m.mk_join(g->dep(idx1), g->dep(idx2))); g->update(idx1, m.mk_true(), nullptr, nullptr); m_progress = true; //IF_VERBOSE(0, if (!g->inconsistent()) display_annotation(verbose_stream(), g);); } bool is_complement(expr* x, expr* y) const { if (m.is_not(x,x)) return x == y; if (m.is_not(y,y)) return x == y; return false; } bool to_ge(expr* e, expr_ref_vector& args, vector& coeffs, rational& k) { expr* r; if (is_uninterp_const(e)) { args.push_back(e); coeffs.push_back(rational::one()); k = rational::one(); } else if (m.is_not(e, r) && is_uninterp_const(r)) { args.push_back(e); coeffs.push_back(rational::one()); k = rational::one(); } else if (pb.is_ge(e)) { app* a = to_app(e); SASSERT(pure_args(a)); for (unsigned i = 0; i < a->get_num_args(); ++i) { args.push_back(a->get_arg(i)); coeffs.push_back(pb.get_coeff(a, i)); } k = pb.get_k(e); } else if (m.is_or(e)) { app* a = to_app(e); SASSERT(pure_args(a)); for (unsigned i = 0; i < a->get_num_args(); ++i) { args.push_back(a->get_arg(i)); coeffs.push_back(rational::one()); } k = rational::one(); } else { return false; } return true; } void replace(unsigned_vector const& positions, expr* e, expr* v, goal_ref const& g) { if (!is_valid(positions, g)) return; expr_substitution sub(m); sub.insert(e, v); expr_ref tmp(m); m_r.set_substitution(&sub); for (unsigned i = 0; i < positions.size(); ++i) { unsigned idx = positions[i]; expr_ref f(m); proof_ref new_pr(m); f = g->form(idx); if (!m.is_true(f)) { m_r(f, tmp, new_pr); if (tmp != f) { TRACE("pb", tout << mk_pp(f, m) << " -> " << tmp << " by " << mk_pp(e, m) << " |-> " << mk_pp(v, m) << "\n";); IF_VERBOSE(3, verbose_stream() << "replace " << mk_pp(f, m) << " -> " << tmp << "\n";); if (g->proofs_enabled()) { new_pr = m.mk_modus_ponens(g->pr(idx), new_pr); } g->update(idx, tmp, new_pr, g->dep(idx)); m_progress = true; } } } m_r.set_substitution(nullptr); } bool subsumes(expr_ref_vector const& args1, vector const& coeffs1, rational const& k1, expr_ref_vector const& args2, vector const& coeffs2, rational const& k2) { if (k2 > k1) return false; for (unsigned i = 0; i < args1.size(); ++i) { bool found = false; for (unsigned j = 0; !found && j < args2.size(); ++j) { if (args1[i] == args2[j]) { if (coeffs1[i] > coeffs2[j]) return false; found = true; } } if (!found) return false; } return true; } }; tactic * mk_pb_preprocess_tactic(ast_manager & m, params_ref const & p) { return alloc(pb_preprocess_tactic, m); } z3-z3-4.8.7/src/tactic/core/pb_preprocess_tactic.h000066400000000000000000000011751356505360400217670ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: pb_preprocess_tactic.h Abstract: Pre-process pseudo-Boolean inequalities using generalized Davis Putnam (resolution) to eliminate variables. Author: Nikolaj Bjorner (nbjorner) 2013-12-23 Notes: --*/ #ifndef PB_PREPROCESS_TACTIC_H_ #define PB_PREPROCESS_TACTIC_H_ #include "util/params.h" class ast_manager; class tactic; tactic * mk_pb_preprocess_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("pb-preprocess", "pre-process pseudo-Boolean constraints a la Davis Putnam.", "mk_pb_preprocess_tactic(m, p)") */ #endif z3-z3-4.8.7/src/tactic/core/propagate_values_tactic.cpp000066400000000000000000000164341356505360400230210ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: propagate_values_tactic.cpp Abstract: Propagate values using equalities of the form (= t v) where v is a value, and atoms t and (not t) Author: Leonardo de Moura (leonardo) 2011-12-28. Revision History: --*/ #include "tactic/tactical.h" #include "tactic/core/propagate_values_tactic.h" #include "ast/rewriter/th_rewriter.h" #include "ast/ast_smt2_pp.h" #include "ast/expr_substitution.h" #include "tactic/goal_shared_occs.h" #include "tactic/tactic_params.hpp" namespace { class propagate_values_tactic : public tactic { 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; 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()); } 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_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); } 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; } 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 { 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";); } 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; } 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) { updt_params_core(p); } tactic * translate(ast_manager & m) override { return alloc(propagate_values_tactic, m, m_params); } void updt_params(params_ref const & p) override { m_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: 4) maximum number of rounds."); } void operator()(goal_ref const & in, goal_ref_buffer & result) override { try { run(in, result); } catch (rewriter_exception & ex) { throw tactic_exception(ex.msg()); } } void cleanup() override { 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); } z3-z3-4.8.7/src/tactic/core/propagate_values_tactic.h000066400000000000000000000010511356505360400224530ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: propagate_values_tactic.h Abstract: Propagate values using equalities of the form (= t v) where v is a value, and atoms t and (not t) Author: Leonardo de Moura (leonardo) 2011-12-28. Revision History: --*/ #pragma once #include "util/params.h" class ast_manager; class tactic; tactic * mk_propagate_values_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("propagate-values", "propagate constants.", "mk_propagate_values_tactic(m, p)") */ z3-z3-4.8.7/src/tactic/core/reduce_args_tactic.cpp000066400000000000000000000445211356505360400217410ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: reduce_args_tactic.cpp Abstract: Reduce the number of arguments in function applications. Author: Leonardo (leonardo) 2012-02-19 Notes: --*/ #include "tactic/tactical.h" #include "ast/ast_smt2_pp.h" #include "ast/has_free_vars.h" #include "util/map.h" #include "ast/rewriter/rewriter_def.h" #include "tactic/generic_model_converter.h" /** \brief Reduce the number of arguments in function applications. Example, suppose we have a function f with 2 arguments. There are 1000 applications of this function, but the first argument is always "a", "b" or "c". Thus, we replace the f(t1, t2) with f_a(t2) if t1 = a f_b(t2) if t2 = b f_c(t2) if t2 = c Since f_a, f_b, f_c are new symbols, satisfiability is preserved. This transformation is very similar in spirit to the Ackermman's reduction. This transformation should work in the following way: 1- Create a mapping decl2arg_map from declarations to tuples of booleans, an entry [f -> (true, false, true)] means that f is a declaration with 3 arguments where the first and third arguments are always values. 2- Traverse the formula and populate the mapping. For each function application f(t1, ..., tn) do a) Create a boolean tuple (is_value(t1), ..., is_value(tn)) and do the logical-and with the tuple that is already in the mapping. If there is no such tuple in the mapping, we just add a new entry. If all entries are false-tuples, then there is nothing to be done. The transformation is not applicable. Now, we create a mapping decl2new_decl from (decl, val_1, ..., val_n) to decls. Note that, n may be different for each entry, but it is the same for the same declaration. For example, suppose we have [f -> (true, false, true)] in decl2arg_map, and applications f(1, a, 2), f(1, b, 2), f(1, b, 3), f(2, b, 3), f(2, c, 3) in the formula. Then, decl2arg_map would contain (f, 1, 2) -> f_1_2 (f, 1, 3) -> f_1_3 (f, 2, 3) -> f_2_3 where f_1_2, f_1_3 and f_2_3 are new function symbols. Using the new map, we can replace the occurrences of f. */ class reduce_args_tactic : public tactic { struct imp; imp * m_imp; public: reduce_args_tactic(ast_manager & m); tactic * translate(ast_manager & m) override { return alloc(reduce_args_tactic, m); } ~reduce_args_tactic() override; void operator()(goal_ref const & g, goal_ref_buffer & result) override; void cleanup() override; }; tactic * mk_reduce_args_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(reduce_args_tactic, m)); } struct reduce_args_tactic::imp { ast_manager & m_manager; bv_util m_bv; ast_manager & m() const { return m_manager; } imp(ast_manager & m): m_manager(m), m_bv(m) { } static bool is_var_plus_offset(ast_manager& m, bv_util& bv, expr* e, expr*& base) { expr *lhs, *rhs; if (bv.is_bv_add(e, lhs, rhs) && bv.is_numeral(lhs)) { base = rhs; } else { base = e; } return !has_free_vars(base); } static bool may_be_unique(ast_manager& m, bv_util& bv, expr* e, expr*& base) { base = nullptr; return m.is_unique_value(e) || is_var_plus_offset(m, bv, e, base); } static bool may_be_unique(ast_manager& m, bv_util& bv, expr* e) { expr* base; return may_be_unique(m, bv, e, base); } void checkpoint() { if (m_manager.canceled()) throw tactic_exception(m_manager.limit().get_cancel_msg()); } struct find_non_candidates_proc { ast_manager & m_manager; bv_util & m_bv; obj_hashtable & m_non_cadidates; find_non_candidates_proc(ast_manager & m, bv_util & bv, obj_hashtable & non_cadidates): m_manager(m), m_bv(bv), m_non_cadidates(non_cadidates) { } void operator()(var * n) {} void operator()(quantifier * n) {} void operator()(app * n) { if (n->get_num_args() == 0) return; // ignore constants func_decl * d = n->get_decl(); if (d->get_family_id() != null_family_id) return; // ignore interpreted symbols if (m_non_cadidates.contains(d)) return; // it is already in the set. unsigned j = n->get_num_args(); while (j > 0) { --j; if (may_be_unique(m_manager, m_bv, n->get_arg(j))) return; } m_non_cadidates.insert(d); } }; /** \brief Populate the table non_cadidates with function declarations \c f such that there is a function application (f t1 ... tn) where t1 ... tn are not values. */ void find_non_candidates(goal const & g, obj_hashtable & non_candidates) { non_candidates.reset(); find_non_candidates_proc proc(m_manager, m_bv, non_candidates); expr_fast_mark1 visited; unsigned sz = g.size(); for (unsigned i = 0; i < sz; i++) { checkpoint(); quick_for_each_expr(proc, visited, g.form(i)); } TRACE("reduce_args", tout << "non_candidates:\n"; obj_hashtable::iterator it = non_candidates.begin(); obj_hashtable::iterator end = non_candidates.end(); for (; it != end; ++it) { func_decl * d = *it; tout << d->get_name() << "\n"; }); } struct populate_decl2args_proc { ast_manager & m_manager; bv_util & m_bv; obj_hashtable & m_non_cadidates; obj_map & m_decl2args; obj_map > m_decl2base; // for args = base + offset populate_decl2args_proc(ast_manager & m, bv_util & bv, obj_hashtable & nc, obj_map & d): m_manager(m), m_bv(bv), m_non_cadidates(nc), m_decl2args(d) {} void operator()(var * n) {} void operator()(quantifier * n) {} void operator()(app * n) { if (n->get_num_args() == 0) return; // ignore constants func_decl * d = n->get_decl(); if (d->get_family_id() != null_family_id) return; // ignore interpreted symbols if (m_non_cadidates.contains(d)) return; // declaration is not a candidate unsigned j = n->get_num_args(); obj_map::iterator it = m_decl2args.find_iterator(d); expr* base; if (it == m_decl2args.end()) { m_decl2args.insert(d, bit_vector()); svector& bases = m_decl2base.insert_if_not_there2(d, svector())->get_data().m_value; bases.resize(j); it = m_decl2args.find_iterator(d); SASSERT(it != m_decl2args.end()); it->m_value.reserve(j); while (j > 0) { --j; it->m_value.set(j, may_be_unique(m_manager, m_bv, n->get_arg(j), base)); bases[j] = base; } } else { svector& bases = m_decl2base[d]; SASSERT(j == it->m_value.size()); while (j > 0) { --j; it->m_value.set(j, it->m_value.get(j) && may_be_unique(m_manager, m_bv, n->get_arg(j), base) && bases[j] == base); } } } }; void populate_decl2args(goal const & g, obj_hashtable & non_candidates, obj_map & decl2args) { expr_fast_mark1 visited; decl2args.reset(); populate_decl2args_proc proc(m_manager, m_bv, non_candidates, decl2args); unsigned sz = g.size(); for (unsigned i = 0; i < sz; i++) { checkpoint(); quick_for_each_expr(proc, visited, g.form(i)); } // Remove all cases where the simplification is not applicable. ptr_buffer bad_decls; obj_map::iterator it = decl2args.begin(); obj_map::iterator end = decl2args.end(); for (; it != end; it++) { bool is_zero = true; for (unsigned i = 0; i < it->m_value.size() && is_zero ; i++) { if (it->m_value.get(i)) is_zero = false; } if (is_zero) bad_decls.push_back(it->m_key); } ptr_buffer::iterator it2 = bad_decls.begin(); ptr_buffer::iterator end2 = bad_decls.end(); for (; it2 != end2; ++it2) decl2args.erase(*it2); TRACE("reduce_args", tout << "decl2args:" << std::endl; for (obj_map::iterator it = decl2args.begin() ; it != decl2args.end() ; it++) { tout << it->m_key->get_name() << ": "; for (unsigned i = 0 ; i < it->m_value.size() ; i++) tout << (it->m_value.get(i) ? "1" : "0"); tout << std::endl; }); } struct arg2func_hash_proc { bit_vector const & m_bv; arg2func_hash_proc(bit_vector const & bv):m_bv(bv) {} unsigned operator()(app const * n) const { // compute the hash-code using only the arguments where m_bv is true. unsigned a = 0x9e3779b9; unsigned num_args = n->get_num_args(); for (unsigned i = 0; i < num_args; i++) { if (!m_bv.get(i)) continue; // ignore argument a = hash_u_u(a, n->get_arg(i)->get_id()); } return a; } }; struct arg2func_eq_proc { bit_vector const & m_bv; arg2func_eq_proc(bit_vector const & bv):m_bv(bv) {} bool operator()(app const * n1, app const * n2) const { // compare only the arguments where m_bv is true SASSERT(n1->get_num_args() == n2->get_num_args()); unsigned num_args = n1->get_num_args(); for (unsigned i = 0; i < num_args; i++) { if (!m_bv.get(i)) continue; // ignore argument if (n1->get_arg(i) != n2->get_arg(i)) return false; } return true; } }; typedef map arg2func; typedef obj_map decl2arg2func_map; struct reduce_args_ctx { ast_manager & m_manager; decl2arg2func_map m_decl2arg2funcs; reduce_args_ctx(ast_manager & m): m_manager(m) { } ~reduce_args_ctx() { obj_map::iterator it = m_decl2arg2funcs.begin(); obj_map::iterator end = m_decl2arg2funcs.end(); for (; it != end; ++it) { arg2func * map = it->m_value; arg2func::iterator it2 = map->begin(); arg2func::iterator end2 = map->end(); for (; it2 != end2; ++it2) { m_manager.dec_ref(it2->m_key); m_manager.dec_ref(it2->m_value); } dealloc(map); } } }; struct reduce_args_rw_cfg : public default_rewriter_cfg { ast_manager & m; imp & m_owner; obj_map & m_decl2args; decl2arg2func_map & m_decl2arg2funcs; reduce_args_rw_cfg(imp & owner, obj_map & decl2args, decl2arg2func_map & decl2arg2funcs): m(owner.m_manager), m_owner(owner), m_decl2args(decl2args), m_decl2arg2funcs(decl2arg2funcs) { } bool max_steps_exceeded(unsigned num_steps) const { m_owner.checkpoint(); 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_arity() == 0) return BR_FAILED; // ignore constants if (f->get_family_id() != null_family_id) return BR_FAILED; // ignore interpreted symbols obj_map::iterator it = m_decl2args.find_iterator(f); if (it == m_decl2args.end()) return BR_FAILED; bit_vector & bv = it->m_value; arg2func *& map = m_decl2arg2funcs.insert_if_not_there2(f, 0)->get_data().m_value; if (!map) { map = alloc(arg2func, arg2func_hash_proc(bv), arg2func_eq_proc(bv)); } app_ref tmp(m.mk_app(f, num, args), m); func_decl *& new_f = map->insert_if_not_there2(tmp, nullptr)->get_data().m_value; if (!new_f) { // create fresh symbol ptr_buffer domain; unsigned arity = f->get_arity(); for (unsigned i = 0; i < arity; ++i) { if (!bv.get(i)) domain.push_back(f->get_domain(i)); } new_f = m.mk_fresh_func_decl(f->get_name(), symbol::null, domain.size(), domain.c_ptr(), f->get_range()); m.inc_ref(tmp); m.inc_ref(new_f); } ptr_buffer new_args; for (unsigned i = 0; i < num; i++) { if (!bv.get(i)) new_args.push_back(args[i]); } result = m.mk_app(new_f, new_args.size(), new_args.c_ptr()); return BR_DONE; } }; struct reduce_args_rw : rewriter_tpl { reduce_args_rw_cfg m_cfg; public: reduce_args_rw(imp & owner, obj_map & decl2args, decl2arg2func_map & decl2arg2funcs): rewriter_tpl(owner.m_manager, false, m_cfg), m_cfg(owner, decl2args, decl2arg2funcs) { } }; model_converter * mk_mc(obj_map & decl2args, decl2arg2func_map & decl2arg2funcs) { ptr_buffer new_args; var_ref_vector new_vars(m_manager); ptr_buffer new_eqs; generic_model_converter * f_mc = alloc(generic_model_converter, m_manager, "reduce_args"); for (auto const& kv : decl2arg2funcs) { func_decl * f = kv.m_key; arg2func * map = kv.m_value; expr * def = nullptr; SASSERT(decl2args.contains(f)); bit_vector & bv = decl2args.find(f); new_vars.reset(); new_args.reset(); for (unsigned i = 0; i < f->get_arity(); i++) { new_vars.push_back(m_manager.mk_var(i, f->get_domain(i))); if (!bv.get(i)) new_args.push_back(new_vars.back()); } arg2func::iterator it2 = map->begin(); arg2func::iterator end2 = map->end(); for (; it2 != end2; ++it2) { app * t = it2->m_key; func_decl * new_def = it2->m_value; f_mc->hide(new_def); SASSERT(new_def->get_arity() == new_args.size()); app * new_t = m_manager.mk_app(new_def, new_args.size(), new_args.c_ptr()); if (def == nullptr) { def = new_t; } else { new_eqs.reset(); for (unsigned i = 0; i < f->get_arity(); i++) { if (bv.get(i)) new_eqs.push_back(m_manager.mk_eq(new_vars.get(i), t->get_arg(i))); } SASSERT(new_eqs.size() > 0); expr * cond; if (new_eqs.size() == 1) cond = new_eqs[0]; else cond = m_manager.mk_and(new_eqs.size(), new_eqs.c_ptr()); def = m_manager.mk_ite(cond, new_t, def); } } SASSERT(def); f_mc->add(f, def); } return f_mc; } void operator()(goal & g) { if (g.inconsistent()) return; TRACE("reduce_args", g.display(tout);); tactic_report report("reduce-args", g); obj_hashtable non_candidates; obj_map decl2args; find_non_candidates(g, non_candidates); populate_decl2args(g, non_candidates, decl2args); if (decl2args.empty()) return; reduce_args_ctx ctx(m_manager); reduce_args_rw rw(*this, decl2args, ctx.m_decl2arg2funcs); unsigned sz = g.size(); for (unsigned i = 0; i < sz; i++) { if (g.inconsistent()) break; expr * f = g.form(i); expr_ref new_f(m_manager); rw(f, new_f); g.update(i, new_f); } report_tactic_progress(":reduced-funcs", decl2args.size()); if (g.models_enabled()) g.add(mk_mc(decl2args, ctx.m_decl2arg2funcs)); TRACE("reduce_args", g.display(tout); if (g.mc()) g.mc()->display(tout);); } }; reduce_args_tactic::reduce_args_tactic(ast_manager & m) { m_imp = alloc(imp, m); } reduce_args_tactic::~reduce_args_tactic() { dealloc(m_imp); } void reduce_args_tactic::operator()(goal_ref const & g, goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); fail_if_proof_generation("reduce-args", g); fail_if_unsat_core_generation("reduce-args", g); result.reset(); m_imp->operator()(*(g.get())); g->inc_depth(); result.push_back(g.get()); SASSERT(g->is_well_sorted()); } void reduce_args_tactic::cleanup() { ast_manager & m = m_imp->m(); m_imp->~imp(); m_imp = new (m_imp) imp(m); } z3-z3-4.8.7/src/tactic/core/reduce_args_tactic.h000066400000000000000000000011531356505360400214000ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: reduce_args_tactic.h Abstract: Reduce the number of arguments in function applications. Author: Leonardo (leonardo) 2012-02-19 Notes: --*/ #ifndef REDUCE_ARGS_TACTIC_H_ #define REDUCE_ARGS_TACTIC_H_ #include "util/params.h" class ast_manager; class tactic; tactic * mk_reduce_args_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("reduce-args", "reduce the number of arguments of function applications, when for all occurrences of a function f the i-th is a value.", "mk_reduce_args_tactic(m, p)") */ #endif z3-z3-4.8.7/src/tactic/core/reduce_invertible_tactic.cpp000066400000000000000000000372041356505360400231500ustar00rootroot00000000000000/*++ Copyright (c) 2018 Microsoft Corporation Module Name: reduce_invertible_tactic.cpp Abstract: Reduce invertible variables. Author: Nuno Lopes (nlopes) 2018-6-30 Nikolaj Bjorner (nbjorner) Notes: 1. Walk through top-level uninterpreted constants. --*/ #include "ast/bv_decl_plugin.h" #include "ast/arith_decl_plugin.h" #include "ast/ast_pp.h" #include "ast/rewriter/expr_safe_replace.h" #include "ast/rewriter/rewriter_def.h" #include "tactic/tactic.h" #include "tactic/core/reduce_invertible_tactic.h" #include "tactic/core/collect_occs.h" #include "tactic/generic_model_converter.h" #include namespace { class reduce_invertible_tactic : public tactic { ast_manager& m; bv_util m_bv; arith_util m_arith; public: reduce_invertible_tactic(ast_manager & m): m(m), m_bv(m), m_arith(m) {} ~reduce_invertible_tactic() override { } tactic * translate(ast_manager & m) override { return alloc(reduce_invertible_tactic, m); } void operator()(goal_ref const & g, goal_ref_buffer & result) override { TRACE("reduce_invertible", g->display(tout);); tactic_report report("reduce-invertible", *g); m_inverted.reset(); m_parents.reset(); collect_parents(g); collect_occs occs; obj_hashtable vars; generic_model_converter_ref mc; occs(*g, vars); expr_safe_replace sub(m); expr_ref new_v(m); expr * p; for (expr* v : vars) { if (is_invertible(v, p, new_v, &mc)) { mark_inverted(p); sub.insert(p, new_v); } } reduce_q_rw rw(*this); unsigned sz = g->size(); for (unsigned idx = 0; idx < sz; idx++) { checkpoint(); expr* f = g->form(idx); expr_ref f_new(m); sub(f, f_new); rw(f_new, f_new); if (f == f_new) continue; proof_ref new_pr(m); if (g->proofs_enabled()) { proof * pr = g->pr(idx); new_pr = m.mk_modus_ponens(pr, new_pr); } g->update(idx, f_new, new_pr, g->dep(idx)); } if (mc) g->add(mc.get()); result.push_back(g.get()); g->inc_depth(); } void cleanup() override {} private: void checkpoint() { if (m.canceled()) throw tactic_exception(m.limit().get_cancel_msg()); } expr_mark m_inverted; void mark_inverted(expr *p) { ptr_buffer todo; todo.push_back(p); while (!todo.empty()) { p = todo.back(); todo.pop_back(); if (!m_inverted.is_marked(p)) { m_inverted.mark(p, true); if (is_app(p)) { for (expr* arg : *to_app(p)) { todo.push_back(arg); } } else if (is_quantifier(p)) { todo.push_back(to_quantifier(p)->get_expr()); } } } } // store one parent of expression, or null if multiple struct parents { parents(): m_p(0) {} uintptr_t m_p; void set(expr * e) { SASSERT((uintptr_t)e != 1); if (!m_p) m_p = (uintptr_t)e; else m_p = 1; } expr * get() const { return m_p == 1 ? nullptr : (expr*)m_p; } }; svector m_parents; struct parent_collector { reduce_invertible_tactic& c; parent_collector(reduce_invertible_tactic& c):c(c) {} void operator()(app* n) { for (expr* arg : *n) { c.m_parents.reserve(arg->get_id() + 1); c.m_parents[arg->get_id()].set(n); } } void operator()(var* v) { c.m_parents.reserve(v->get_id() + 1); } void operator()(quantifier* q) {} }; void collect_parents(goal_ref const& g) { parent_collector proc(*this); expr_fast_mark1 visited; unsigned sz = g->size(); for (unsigned i = 0; i < sz; i++) { checkpoint(); quick_for_each_expr(proc, visited, g->form(i)); } } void ensure_mc(generic_model_converter_ref* mc) { if (mc && !(*mc)) *mc = alloc(generic_model_converter, m, "reduce-invertible"); } bool is_full_domain_var(expr* v, rational& model) { auto f = is_app(v) ? to_app(v)->get_decl() : nullptr; if (!f || f->get_family_id() != m_bv.get_family_id() || f->get_arity() == 0) return false; switch (f->get_decl_kind()) { case OP_BADD: case OP_BSUB: case OP_BSHL: case OP_BASHR: case OP_BLSHR: case OP_BOR: model = rational::zero(); return true; case OP_BAND: model = rational::power_of_two(m_bv.get_bv_size(v)) - rational::one(); return true; case OP_BMUL: case OP_BSDIV: case OP_BSDIV0: case OP_BSDIV_I: case OP_BUDIV: case OP_BUDIV0: case OP_BUDIV_I: model = rational::one(); return true; default: return false; } } bool rewrite_unconstr(expr* v, expr_ref& new_v, generic_model_converter_ref* mc, unsigned max_var) { rational mdl; if (!is_full_domain_var(v, mdl)) return false; rational r; app* a = to_app(v); expr* fst_arg = a->get_arg(0); bool fst_is_var = is_var(fst_arg); for (unsigned i = 0, n = a->get_num_args(); i != n; ++i) { auto arg = a->get_arg(i); if (!m_parents[arg->get_id()].get() || is_var(arg) != fst_is_var) return false; if (fst_is_var && to_var(arg)->get_idx() >= max_var) return false; if (m_bv.is_numeral(arg, r) && r != mdl) return false; } if (mc) { ensure_mc(mc); expr_ref num(m_bv.mk_numeral(mdl, get_sort(fst_arg)), m); for (unsigned i = 1, n = a->get_num_args(); i != n; ++i) { (*mc)->add(a->get_arg(i), num); } } new_v = fst_arg; return true; } // TBD: could be made to be recursive, by walking multiple layers of parents. bool is_invertible(expr* v, expr*& p, expr_ref& new_v, generic_model_converter_ref* mc, unsigned max_var = 0) { p = m_parents[v->get_id()].get(); if (!p) return false; if (m_inverted.is_marked(p)) return false; if (mc && !is_ground(p)) return false; if (m_bv.is_bv_xor(p) || m_bv.is_bv_not(p) || m_bv.is_bv_neg(p)) { if (mc) { ensure_mc(mc); (*mc)->add(v, p); } new_v = v; return true; } if (rewrite_unconstr(p, new_v, mc, max_var)) return true; if (m_bv.is_bv_add(p)) { if (mc) { ensure_mc(mc); // if we solve for v' := v + t // then the value for v is v' - t expr_ref def(v, m); for (expr* arg : *to_app(p)) { if (arg != v) def = m_bv.mk_bv_sub(def, arg); } (*mc)->add(v, def); } new_v = v; return true; } if (m_bv.is_bv_mul(p)) { expr_ref rest(m); for (expr* arg : *to_app(p)) { if (arg != v) { if (rest) rest = m_bv.mk_bv_mul(rest, arg); else rest = arg; } } if (!rest) return false; // create case split on // divisbility of 2 // v * t -> // if t = 0, set v' := 0 and the solution for v is 0. // otherwise, // let i be the position of the least bit of t set to 1 // then extract[sz-1:i](v) ++ zero[i-1:0] is the invertible of v * t // thus // extract[i+1:0](t) = 1 ++ zero[i-1:0] -> extract[sz-1:i](v) ++ zero[i-1:0] // to reproduce the original v from t // solve for v*t = extract[sz-1:i](v') ++ zero[i-1:0] // using values for t and v' // thus // // udiv(extract[sz-1:i](v') ++ zero[i-1:0], t) // // TBD: this argument is flawed. Unit test breaks with either // the above or udiv(v, t) unsigned sz = m_bv.get_bv_size(p); expr_ref bit1(m_bv.mk_numeral(1, 1), m); new_v = m_bv.mk_numeral(0, sz); for (unsigned i = sz; i-- > 0; ) { expr_ref extr_i(m_bv.mk_extract(i, i, rest), m); expr_ref cond(m.mk_eq(extr_i, bit1), m); expr_ref part(v, m); if (i > 0) { part = m_bv.mk_concat(m_bv.mk_extract(sz-1, i, v), m_bv.mk_numeral(0, i)); } new_v = m.mk_ite(cond, part, new_v); } if (mc) { ensure_mc(mc); expr_ref div(m), def(m); div = m.mk_app(m_bv.get_fid(), OP_BUDIV_I, v, rest); def = m_bv.mk_numeral(0, sz); def = m.mk_ite(m.mk_eq(def, rest), def, div); (*mc)->add(v, def); TRACE("invertible_tactic", tout << def << "\n";); } return true; } if (m_bv.is_bv_sub(p)) { // TBD } if (m_bv.is_bv_udivi(p)) { // TBD } // sdivi, sremi, uremi, smodi // TBD if (m_arith.is_mul(p) && m_arith.is_real(p)) { expr_ref rest(m); for (expr* arg : *to_app(p)) { if (arg != v) { if (rest) rest = m_arith.mk_mul(rest, arg); else rest = arg; } } if (!rest) return false; expr_ref zero(m_arith.mk_real(0), m); new_v = m.mk_ite(m.mk_eq(zero, rest), zero, v); if (mc) { ensure_mc(mc); expr_ref def(m_arith.mk_div(v, rest), m); (*mc)->add(v, def); } return true; } expr* e1 = nullptr, *e2 = nullptr; // t / v -> if t = 0 then 0 else v // inverse: t = 0 then 1 else v / t if (m_arith.is_div(p, e1, e2) && e2 == v) { expr_ref zero(m_arith.mk_real(0), m); new_v = m.mk_ite(m.mk_eq(zero, e1), zero, v); if (mc) { ensure_mc(mc); expr_ref def(m.mk_ite(m.mk_eq(zero, e1), m_arith.mk_real(1), m_arith.mk_div(e1, v)), m); (*mc)->add(v, def); } return true; } // v / t unless t != 0 if (m_arith.is_div(p, e1, e2) && e1 == v) { return false; } if (m.is_eq(p, e1, e2)) { if (mc && has_diagonal(e1)) { ensure_mc(mc); new_v = m.mk_fresh_const("eq", m.mk_bool_sort()); SASSERT(v == e1 || v == e2); expr* other = (v == e1) ? e2 : e1; (*mc)->hide(new_v); (*mc)->add(v, m.mk_ite(new_v, other, mk_diagonal(other))); return true; } else if (mc) { // diagonal functions for other types depend on theory. return false; } else if (is_var(v) && is_non_singleton_sort(m.get_sort(v))) { new_v = m.mk_var(to_var(v)->get_idx(), m.mk_bool_sort()); return true; } } return false; } bool has_diagonal(expr* e) { return m_bv.is_bv(e) || m.is_bool(e) || m_arith.is_int_real(e); } expr * mk_diagonal(expr* e) { if (m_bv.is_bv(e)) return m_bv.mk_bv_neg(e); if (m.is_bool(e)) return m.mk_not(e); if (m_arith.is_int(e)) return m_arith.mk_add(m_arith.mk_int(1), e); if (m_arith.is_real(e)) return m_arith.mk_add(m_arith.mk_real(1), e); UNREACHABLE(); return e; } bool is_non_singleton_sort(sort* s) { if (m.is_uninterp(s)) return false; sort_size sz = s->get_num_elements(); if (sz.is_finite() && sz.size() == 1) return false; return true; } struct reduce_q_rw_cfg : public default_rewriter_cfg { ast_manager& m; reduce_invertible_tactic& t; reduce_q_rw_cfg(reduce_invertible_tactic& t): m(t.m), t(t) {} 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) { if (is_lambda(old_q)) return false; if (has_quantifiers(new_body)) return false; ref_buffer vars(m); ptr_buffer new_sorts; unsigned n = old_q->get_num_decls(); for (unsigned i = 0; i < n; ++i) { sort* srt = old_q->get_decl_sort(i); vars.push_back(m.mk_var(n - i - 1, srt)); new_sorts.push_back(srt); } // for each variable, collect parents, // ensure they are in unique location and not under other quantifiers. // if they are invertible, then produce inverting expression. // expr_safe_replace sub(m); t.m_parents.reset(); t.m_inverted.reset(); expr_ref new_v(m); expr * p; { parent_collector proc(t); expr_fast_mark1 visited; quick_for_each_expr(proc, visited, new_body); } bool has_new_var = false; for (unsigned i = 0; i < vars.size(); ++i) { var* v = vars[i]; if (!occurs_under_nested_q(v, new_body) && t.is_invertible(v, p, new_v, nullptr, vars.size())) { t.mark_inverted(p); sub.insert(p, new_v); new_sorts[i] = m.get_sort(new_v); has_new_var |= new_v != v; } } if (has_new_var) { sub(new_body, result); result = m.mk_quantifier(old_q->get_kind(), new_sorts.size(), new_sorts.c_ptr(), old_q->get_decl_names(), result, old_q->get_weight()); result_pr = nullptr; return true; } if (!sub.empty()) { sub(new_body, result); result = m.update_quantifier(old_q, old_q->get_num_patterns(), new_patterns, old_q->get_num_no_patterns(), new_no_patterns, result); result_pr = nullptr; return true; } return false; } bool occurs_under_nested_q(var* v, expr* body) { return has_quantifiers(body); } }; struct reduce_q_rw : rewriter_tpl { reduce_q_rw_cfg m_cfg; public: reduce_q_rw(reduce_invertible_tactic& t): rewriter_tpl(t.m, false, m_cfg), m_cfg(t) {} }; }; } tactic * mk_reduce_invertible_tactic(ast_manager & m, params_ref const &) { return alloc(reduce_invertible_tactic, m); } z3-z3-4.8.7/src/tactic/core/reduce_invertible_tactic.h000066400000000000000000000010121356505360400226010ustar00rootroot00000000000000/*++ Copyright (c) 2018 Microsoft Corporation Module Name: reduce_invertible_tactic.h Abstract: Reduce invertible variables. Author: Nuno Lopes (nlopes) 2018-6-30 Nikolaj Bjorner (nbjorner) Notes: --*/ #pragma once #include "util/params.h" class tactic; class ast_manager; tactic * mk_reduce_invertible_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("reduce-invertible", "reduce invertible variable occurrences.", "mk_reduce_invertible_tactic(m, p)") */ z3-z3-4.8.7/src/tactic/core/simplify_tactic.cpp000066400000000000000000000060101356505360400213010ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: simplify_tactic.cpp Abstract: Apply simplification and rewriting rules. Author: Leonardo (leonardo) 2011-11-20 Notes: --*/ #include "tactic/core/simplify_tactic.h" #include "ast/rewriter/th_rewriter.h" #include "ast/ast_pp.h" struct simplify_tactic::imp { ast_manager & m_manager; th_rewriter m_r; unsigned m_num_steps; imp(ast_manager & m, params_ref const & p): m_manager(m), m_r(m, p), m_num_steps(0) { } ~imp() { } ast_manager & m() const { return m_manager; } void reset() { m_r.reset(); m_num_steps = 0; } void operator()(goal & g) { SASSERT(g.is_well_sorted()); tactic_report report("simplifier", g); TRACE("before_simplifier", g.display(tout);); m_num_steps = 0; if (g.inconsistent()) return; expr_ref new_curr(m()); proof_ref new_pr(m()); unsigned size = g.size(); for (unsigned idx = 0; idx < size; idx++) { if (g.inconsistent()) break; expr * curr = g.form(idx); m_r(curr, new_curr, new_pr); m_num_steps += m_r.get_num_steps(); if (g.proofs_enabled()) { proof * pr = g.pr(idx); new_pr = m().mk_modus_ponens(pr, new_pr); } g.update(idx, new_curr, new_pr, g.dep(idx)); } TRACE("after_simplifier_bug", g.display(tout);); g.elim_redundancies(); TRACE("after_simplifier", g.display(tout);); TRACE("after_simplifier_detail", g.display_with_dependencies(tout);); SASSERT(g.is_well_sorted()); } unsigned get_num_steps() const { return m_num_steps; } }; simplify_tactic::simplify_tactic(ast_manager & m, params_ref const & p): m_params(p) { m_imp = alloc(imp, m, p); } simplify_tactic::~simplify_tactic() { dealloc(m_imp); } void simplify_tactic::updt_params(params_ref const & p) { m_params = p; m_imp->m_r.updt_params(p); } void simplify_tactic::get_param_descrs(param_descrs & r) { th_rewriter::get_param_descrs(r); } void simplify_tactic::operator()(goal_ref const & in, goal_ref_buffer & result) { try { (*m_imp)(*(in.get())); in->inc_depth(); result.push_back(in.get()); } catch (rewriter_exception & ex) { throw tactic_exception(ex.msg()); } } void simplify_tactic::cleanup() { ast_manager & m = m_imp->m(); params_ref p = std::move(m_params); m_imp->~imp(); new (m_imp) imp(m, p); } unsigned simplify_tactic::get_num_steps() const { return m_imp->get_num_steps(); } tactic * mk_simplify_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(simplify_tactic, m, p)); } tactic * mk_elim_and_tactic(ast_manager & m, params_ref const & p) { params_ref xp = p; xp.set_bool("elim_and", true); return using_params(mk_simplify_tactic(m, xp), xp); } z3-z3-4.8.7/src/tactic/core/simplify_tactic.h000066400000000000000000000025171356505360400207560ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: simplify_tactic.h Abstract: Apply simplification and rewriting rules. Author: Leonardo (leonardo) 2011-11-20 Notes: --*/ #ifndef SIMPLIFY_TACTIC_H_ #define SIMPLIFY_TACTIC_H_ #include "tactic/tactic.h" #include "tactic/tactical.h" class simplify_tactic : public tactic { struct imp; imp * m_imp; params_ref m_params; public: simplify_tactic(ast_manager & m, params_ref const & ref = params_ref()); ~simplify_tactic() override; void updt_params(params_ref const & p) override; static void get_param_descrs(param_descrs & r); void collect_param_descrs(param_descrs & r) override { get_param_descrs(r); } void operator()(goal_ref const & in, goal_ref_buffer & result) override; void cleanup() override; unsigned get_num_steps() const; tactic * translate(ast_manager & m) override { return alloc(simplify_tactic, m, m_params); } }; tactic * mk_simplify_tactic(ast_manager & m, params_ref const & p = params_ref()); tactic * mk_elim_and_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("simplify", "apply simplification rules.", "mk_simplify_tactic(m, p)") ADD_TACTIC("elim-and", "convert (and a b) into (not (or (not a) (not b))).", "mk_elim_and_tactic(m, p)") */ #endif z3-z3-4.8.7/src/tactic/core/solve_eqs_tactic.cpp000066400000000000000000001260201356505360400214510ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: solve_eqs_tactic.cpp Abstract: Tactic for solving equations and performing gaussian elimination. Author: Leonardo de Moura (leonardo) 2011-12-29. Revision History: --*/ #include "ast/rewriter/expr_replacer.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 { typedef generic_model_converter gmc; ast_manager & m_manager; expr_replacer * m_r; bool m_r_owner; arith_util m_a_util; obj_map m_num_occs; unsigned m_num_steps; unsigned m_num_eliminated_vars; 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; expr_sparse_mark m_candidate_set; ptr_vector m_candidates; ptr_vector m_vars; expr_sparse_mark m_nonzero; ptr_vector m_ordered_vars; bool m_produce_proofs; bool m_produce_unsat_cores; bool m_produce_models; imp(ast_manager & m, params_ref const & p, expr_replacer * r, bool owner): m_manager(m), m_r(r), m_r_owner(r == nullptr || owner), m_a_util(m), m_num_steps(0), m_num_eliminated_vars(0) { updt_params(p); if (m_r == nullptr) m_r = mk_default_expr_replacer(m); } ~imp() { if (m_r_owner) dealloc(m_r); } ast_manager & m() const { return m_manager; } void updt_params(params_ref const & p) { 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() { if (m().canceled()) throw tactic_exception(m().limit().get_cancel_msg()); } // Check if the number of occurrences of t is below the specified threshold :solve-eqs-max-occs bool check_occs(expr * t) const { if (m_max_occs == UINT_MAX) return true; unsigned num = 0; m_num_occs.find(t, num); TRACE("solve_eqs_check_occs", tout << mk_ismt2_pp(t, m_manager) << " num_occs: " << num << " max: " << m_max_occs << "\n";); return num <= m_max_occs; } // Use: (= x def) and (= def x) bool trivial_solve1(expr * lhs, expr * rhs, app_ref & var, expr_ref & def, proof_ref & pr) { if (is_uninterp_const(lhs) && !m_candidate_vars.is_marked(lhs) && !occurs(lhs, rhs) && check_occs(lhs)) { var = to_app(lhs); def = rhs; pr = nullptr; return true; } else { return false; } } bool trivial_solve(expr * lhs, expr * rhs, app_ref & var, expr_ref & def, proof_ref & pr) { if (trivial_solve1(lhs, rhs, var, def, pr)) return true; if (trivial_solve1(rhs, lhs, var, def, pr)) { if (m_produce_proofs) { pr = m().mk_commutativity(m().mk_eq(lhs, rhs)); } return true; } return false; } // (ite c (= x t1) (= x t2)) --> (= x (ite c t1 t2)) bool solve_ite_core(app * ite, expr * lhs1, expr * rhs1, expr * lhs2, expr * rhs2, app_ref & var, expr_ref & def, proof_ref & pr) { if (lhs1 != lhs2) return false; if (!is_uninterp_const(lhs1) || m_candidate_vars.is_marked(lhs1)) return false; if (occurs(lhs1, ite->get_arg(0)) || occurs(lhs1, rhs1) || occurs(lhs1, rhs2)) return false; if (!check_occs(lhs1)) return false; var = to_app(lhs1); def = m().mk_ite(ite->get_arg(0), rhs1, rhs2); if (m_produce_proofs) pr = m().mk_rewrite(ite, m().mk_eq(var, def)); return true; } // (ite c (= x t1) (= x t2)) --> (= x (ite c t1 t2)) bool solve_ite(app * ite, app_ref & var, expr_ref & def, proof_ref & pr) { expr * t = ite->get_arg(1); expr * e = ite->get_arg(2); if (!m().is_eq(t) || !m().is_eq(e)) return false; expr * lhs1 = to_app(t)->get_arg(0); expr * rhs1 = to_app(t)->get_arg(1); expr * lhs2 = to_app(e)->get_arg(0); expr * rhs2 = to_app(e)->get_arg(1); return solve_ite_core(ite, lhs1, rhs1, lhs2, rhs2, var, def, pr) || solve_ite_core(ite, rhs1, lhs1, lhs2, rhs2, var, def, pr) || solve_ite_core(ite, lhs1, rhs1, rhs2, lhs2, var, def, pr) || solve_ite_core(ite, rhs1, lhs1, rhs2, lhs2, var, def, pr); } bool is_pos_literal(expr * n) { return is_app(n) && to_app(n)->get_num_args() == 0 && to_app(n)->get_family_id() == null_family_id; } bool is_neg_literal(expr * n) { if (m_manager.is_not(n)) return is_pos_literal(to_app(n)->get_arg(0)); return false; } #if 0 bool not_bool_eq(expr * f, app_ref & var, expr_ref & def, proof_ref & pr) { if (!m().is_not(f)) return false; expr * eq = to_app(f)->get_arg(0); if (!m().is_eq(f)) return false; } #endif /** \brief Given t of the form (f s_0 ... s_n), return true if x occurs in some s_j for j != i */ bool occurs_except(expr * x, app * t, unsigned i) { unsigned num = t->get_num_args(); for (unsigned j = 0; j < num; j++) { if (i != j && occurs(x, t->get_arg(j))) return true; } return false; } void add_pos(expr* f) { expr* lhs = nullptr, *rhs = nullptr; rational val; if (m_a_util.is_le(f, lhs, rhs) && m_a_util.is_numeral(rhs, val) && val.is_neg()) { m_nonzero.mark(lhs); } else if (m_a_util.is_ge(f, lhs, rhs) && m_a_util.is_numeral(rhs, val) && val.is_pos()) { m_nonzero.mark(lhs); } else if (m().is_not(f, f)) { if (m_a_util.is_le(f, lhs, rhs) && m_a_util.is_numeral(rhs, val) && !val.is_neg()) { m_nonzero.mark(lhs); } else if (m_a_util.is_ge(f, lhs, rhs) && m_a_util.is_numeral(rhs, val) && !val.is_pos()) { m_nonzero.mark(lhs); } else if (m().is_eq(f, lhs, rhs) && m_a_util.is_numeral(rhs, val) && val.is_zero()) { m_nonzero.mark(lhs); } } } bool is_nonzero(expr* e) { return m_nonzero.is_marked(e); } bool isolate_var(app* arg, app_ref& var, expr_ref& div, unsigned i, app* lhs, expr* rhs) { if (!m_a_util.is_mul(arg)) return false; unsigned n = arg->get_num_args(); for (unsigned j = 0; j < n; ++j) { expr* e = arg->get_arg(j); bool ok = is_uninterp_const(e) && check_occs(e) && !occurs(e, rhs) && !occurs_except(e, lhs, i); if (!ok) continue; var = to_app(e); for (unsigned k = 0; ok && k < n; ++k) { expr* arg_k = arg->get_arg(k); ok = k == j || (!occurs(var, arg_k) && is_nonzero(arg_k)); } if (!ok) continue; ptr_vector args; for (unsigned k = 0; k < n; ++k) { if (k != j) args.push_back(arg->get_arg(k)); } div = m_a_util.mk_mul(args.size(), args.c_ptr()); return true; } return false; } bool solve_nl(app * lhs, expr * rhs, expr* eq, app_ref& var, expr_ref & def, proof_ref & pr) { SASSERT(m_a_util.is_add(lhs)); if (m_a_util.is_int(lhs)) return false; unsigned num = lhs->get_num_args(); expr_ref div(m()); for (unsigned i = 0; i < num; i++) { expr * arg = lhs->get_arg(i); if (is_app(arg) && isolate_var(to_app(arg), var, div, i, lhs, rhs)) { ptr_vector args; for (unsigned k = 0; k < num; ++k) { if (k != i) args.push_back(lhs->get_arg(k)); } def = m_a_util.mk_sub(rhs, m_a_util.mk_add(args.size(), args.c_ptr())); def = m_a_util.mk_div(def, div); if (m_produce_proofs) pr = m().mk_rewrite(eq, m().mk_eq(var, def)); return true; } } return false; } bool solve_arith_core(app * lhs, expr * rhs, expr * eq, app_ref & var, expr_ref & def, proof_ref & pr) { SASSERT(m_a_util.is_add(lhs)); bool is_int = m_a_util.is_int(lhs); expr * a = nullptr; expr * v = nullptr; rational a_val; unsigned num = lhs->get_num_args(); unsigned i; for (i = 0; i < num; i++) { expr * arg = lhs->get_arg(i); if (is_uninterp_const(arg) && !m_candidate_vars.is_marked(arg) && check_occs(arg) && !occurs(arg, rhs) && !occurs_except(arg, lhs, i)) { a_val = rational(1); v = arg; break; } else if (m_a_util.is_mul(arg, a, v) && is_uninterp_const(v) && !m_candidate_vars.is_marked(v) && m_a_util.is_numeral(a, a_val) && !a_val.is_zero() && (!is_int || a_val.is_minus_one()) && check_occs(v) && !occurs(v, rhs) && !occurs_except(v, lhs, i)) { break; } } if (i == num) return false; var = to_app(v); expr_ref inv_a(m()); if (!a_val.is_one()) { inv_a = m_a_util.mk_numeral(rational(1)/a_val, is_int); rhs = m_a_util.mk_mul(inv_a, rhs); } ptr_buffer other_args; for (unsigned j = 0; j < num; j++) { if (i != j) { if (inv_a) other_args.push_back(m_a_util.mk_mul(inv_a, lhs->get_arg(j))); else other_args.push_back(lhs->get_arg(j)); } } switch (other_args.size()) { case 0: def = rhs; break; case 1: def = m_a_util.mk_sub(rhs, other_args[0]); break; default: def = m_a_util.mk_sub(rhs, m_a_util.mk_add(other_args.size(), other_args.c_ptr())); break; } if (m_produce_proofs) pr = m().mk_rewrite(eq, m().mk_eq(var, def)); return true; } bool solve_arith(expr * lhs, expr * rhs, expr * eq, app_ref & var, expr_ref & def, proof_ref & pr) { return (m_a_util.is_add(lhs) && solve_arith_core(to_app(lhs), rhs, eq, var, def, pr)) || (m_a_util.is_add(rhs) && solve_arith_core(to_app(rhs), lhs, eq, var, def, pr)); #if 0 // better done inside of nlsat (m_a_util.is_add(lhs) && solve_nl(to_app(lhs), rhs, eq, var, def, pr)) || (m_a_util.is_add(rhs) && solve_nl(to_app(rhs), lhs, eq, var, def, pr)); #endif } bool solve(expr * f, app_ref & var, expr_ref & def, proof_ref & pr) { expr* arg1 = nullptr, *arg2 = nullptr; if (m().is_eq(f, arg1, arg2)) { if (trivial_solve(arg1, arg2, var, def, pr)) return true; if (m_theory_solver) { if (solve_arith(arg1, arg2, f, var, def, pr)) return true; } return false; } #if 0 if (not_bool_eq(f, var, def, pr)) return true; #endif if (m_ite_solver && m().is_ite(f)) return solve_ite(to_app(f), var, def, pr); if (is_pos_literal(f)) { if (m_candidate_vars.is_marked(f)) return false; var = to_app(f); def = m().mk_true(); if (m_produce_proofs) { // [rewrite]: (iff (iff l true) l) // [symmetry T1]: (iff l (iff l true)) pr = m().mk_rewrite(m().mk_eq(var, def), var); pr = m().mk_symmetry(pr); } TRACE("solve_eqs_bug2", tout << "eliminating: " << mk_ismt2_pp(f, m()) << "\n";); return true; } if (is_neg_literal(f)) { var = to_app(to_app(f)->get_arg(0)); if (m_candidate_vars.is_marked(var)) return false; def = m().mk_false(); if (m_produce_proofs) { // [rewrite]: (iff (iff l false) ~l) // [symmetry T1]: (iff ~l (iff l false)) pr = m().mk_rewrite(m().mk_eq(var, def), f); pr = m().mk_symmetry(pr); } return true; } 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 */ void collect(goal const & g) { m_subst->reset(); m_norm_subst->reset(); m_r->set_substitution(nullptr); m_candidate_vars.reset(); m_candidate_set.reset(); m_candidates.reset(); m_vars.reset(); m_nonzero.reset(); app_ref var(m()); expr_ref def(m()); proof_ref pr(m()); unsigned size = g.size(); for (unsigned idx = 0; idx < size; idx++) { add_pos(g.form(idx)); } for (unsigned idx = 0; idx < size; idx++) { checkpoint(); expr * f = g.form(idx); if (solve(f, var, def, pr)) { insert_solution(g, idx, f, var, def, pr); } m_num_steps++; } TRACE("solve_eqs", tout << "candidate vars:\n"; for (app* v : m_vars) { tout << mk_ismt2_pp(v, m()) << " "; } 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) {} }; ptr_vector m_todo; void mark_occurs(expr_mark& occ, goal const& g, expr* v) { expr_fast_mark2 visited; occ.mark(v, true); visited.mark(v, true); for (unsigned j = 0; j < g.size(); ++j) { m_todo.push_back(g.form(j)); } while (!m_todo.empty()) { expr* e = m_todo.back(); if (visited.is_marked(e)) { m_todo.pop_back(); continue; } if (is_app(e)) { bool does_occur = false; bool all_visited = true; for (expr* arg : *to_app(e)) { if (!visited.is_marked(arg)) { m_todo.push_back(arg); all_visited = false; } else { does_occur |= occ.is_marked(arg); } } if (all_visited) { occ.mark(e, does_occur); visited.mark(e, true); m_todo.pop_back(); } } else if (is_quantifier(e)) { expr* body = to_quantifier(e)->get_expr(); if (visited.is_marked(body)) { visited.mark(e, true); occ.mark(e, occ.is_marked(body)); m_todo.pop_back(); } } else { visited.mark(e, true); m_todo.pop_back(); } } } bool is_compatible(goal const& g, unsigned idx, vector const & path, expr* v, expr* eq) { expr_mark occ; svector cache; mark_occurs(occ, g, v); return is_goal_compatible(g, occ, cache, idx, v, eq) && is_path_compatible(occ, cache, path, v, eq); } bool is_goal_compatible(goal const& g, expr_mark& occ, svector& cache, unsigned idx, expr* v, expr* eq) { bool all_e = false; for (unsigned j = 0; j < g.size(); ++j) { if (j != idx && !check_eq_compat_rec(occ, cache, 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(expr_mark& occ, svector& cache, 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 && occ.is_marked(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_rec(occ, cache, 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_rec(expr_mark& occ, svector& cache, expr* f, expr* v, expr* eq, bool& all) { expr_ref_vector args(m()); expr* f1 = nullptr; if (!occ.is_marked(f)) { all = false; return true; } unsigned idx = f->get_id(); if (cache.size() > idx && cache[idx] != l_undef) { return cache[idx] == l_true; } if (m().is_not(f, f1) && m().is_or(f1)) { flatten_and(f, args); for (expr* arg : args) { if (arg == eq) { cache.reserve(idx+1, l_undef); cache[idx] = l_true; return true; } } } else if (m().is_or(f)) { flatten_or(f, args); } else { return false; } for (expr* arg : args) { if (!check_eq_compat_rec(occ, cache, arg, v, eq, all)) { cache.reserve(idx+1, l_undef); cache[idx] = l_false; return false; } } cache.reserve(idx+1, l_undef); cache[idx] = l_true; 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()); TRACE("solve_eqs", g.display(tout);); for (unsigned idx = 0; idx < size; idx++) { checkpoint(); if (g.is_decided_unsat()) break; expr* f = g.form(idx); thrw(f, tmp); rw(tmp, tmp2); TRACE("solve_eqs", tout << mk_pp(f, m()) << " " << tmp2 << "\n";); g.update(idx, tmp2, g.pr(idx), g.dep(idx)); } } void sort_vars() { SASSERT(m_candidates.size() == m_vars.size()); TRACE("solve_eqs_bug", tout << "sorting vars...\n";); m_ordered_vars.reset(); // The variables (and its definitions) in m_subst must remain alive until the end of this procedure. // Reason: they are scheduled for unmarking in visiting/done. // They should remain alive while they are on the stack. // To make sure this is the case, whenever a variable (and its definition) is removed from m_subst, // I add them to the saved vector. expr_ref_vector saved(m()); expr_fast_mark1 visiting; expr_fast_mark2 done; typedef std::pair frame; svector todo; unsigned num = 0; for (app* v : m_vars) { checkpoint(); if (!m_candidate_vars.is_marked(v)) continue; todo.push_back(frame(v, 0)); while (!todo.empty()) { start: frame & fr = todo.back(); expr * t = fr.first; m_num_steps++; TRACE("solve_eqs_bug", tout << "processing:\n" << mk_ismt2_pp(t, m()) << "\n";); if (t->get_ref_count() > 1 && done.is_marked(t)) { todo.pop_back(); continue; } switch (t->get_kind()) { case AST_VAR: todo.pop_back(); break; case AST_QUANTIFIER: num = to_quantifier(t)->get_num_children(); while (fr.second < num) { expr * c = to_quantifier(t)->get_child(fr.second); fr.second++; if (c->get_ref_count() > 1 && done.is_marked(c)) continue; todo.push_back(frame(c, 0)); goto start; } if (t->get_ref_count() > 1) done.mark(t); todo.pop_back(); break; case AST_APP: num = to_app(t)->get_num_args(); if (num == 0) { if (fr.second == 0) { if (m_candidate_vars.is_marked(t)) { if (visiting.is_marked(t)) { // cycle detected: remove t visiting.reset_mark(t); m_candidate_vars.mark(t, false); SASSERT(!m_candidate_vars.is_marked(t)); // Must save t and its definition. // See comment in the beginning of the function expr * def = nullptr; proof * pr; expr_dependency * dep; m_subst->find(to_app(t), def, pr, dep); SASSERT(def != 0); saved.push_back(t); saved.push_back(def); // m_subst->erase(t); } else { visiting.mark(t); fr.second = 1; expr * def = nullptr; proof * pr; expr_dependency * dep; m_subst->find(to_app(t), def, pr, dep); SASSERT(def != 0); todo.push_back(frame(def, 0)); goto start; } } } else { SASSERT(fr.second == 1); if (m_candidate_vars.is_marked(t)) { visiting.reset_mark(t); m_ordered_vars.push_back(to_app(t)); } else { // var was removed from the list of candidate vars to elim cycle // do nothing } } } else { while (fr.second < num) { expr * arg = to_app(t)->get_arg(fr.second); fr.second++; if (arg->get_ref_count() > 1 && done.is_marked(arg)) continue; todo.push_back(frame(arg, 0)); goto start; } } if (t->get_ref_count() > 1) done.mark(t); todo.pop_back(); break; default: UNREACHABLE(); todo.pop_back(); break; } } } // cleanup unsigned idx = 0; for (expr* v : m_vars) { if (!m_candidate_vars.is_marked(v)) { m_candidate_set.mark(m_candidates[idx], false); } ++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) { SASSERT(m_candidate_vars.is_marked(v)); tout << mk_ismt2_pp(v, m()) << " "; } tout << "\n";); m_candidate_vars.reset(); } void normalize() { m_norm_subst->reset(); m_r->set_substitution(m_norm_subst.get()); expr_ref new_def(m()); proof_ref new_pr(m()); expr_dependency_ref new_dep(m()); for (app * v : m_ordered_vars) { checkpoint(); expr * def = nullptr; proof * pr = nullptr; expr_dependency * dep = nullptr; m_subst->find(v, def, pr, dep); SASSERT(def != 0); m_r->operator()(def, new_def, new_pr, new_dep); m_num_steps += m_r->get_num_steps() + 1; if (m_produce_proofs) new_pr = m().mk_transitivity(pr, new_pr); new_dep = m().mk_join(dep, new_dep); m_norm_subst->insert(v, new_def, new_pr, new_dep); // we updated the substituting, but we don't need to reset m_r // because all cached values there do not depend on v. } m_subst->reset(); TRACE("solve_eqs", tout << "after normalizing variables\n"; for (expr * v : m_ordered_vars) { expr * def = 0; proof * pr = 0; expr_dependency * dep = 0; m_norm_subst->find(v, def, pr, dep); tout << mk_ismt2_pp(v, m()) << "\n----->\n" << mk_ismt2_pp(def, m()) << "\n\n"; }); #if 0 DEBUG_CODE({ for (expr * v : m_ordered_vars) { expr * def = 0; proof * pr = 0; expr_dependency * dep = 0; m_norm_subst->find(v, def, pr, dep); SASSERT(def != 0); CASSERT("solve_eqs_bug", !occurs(v, def)); } }); #endif } void substitute(goal & g) { // force the cache of m_r to be reset. m_r->set_substitution(m_norm_subst.get()); expr_ref new_f(m()); proof_ref new_pr(m()); expr_dependency_ref new_dep(m()); unsigned size = g.size(); for (unsigned idx = 0; idx < size; idx++) { checkpoint(); expr * f = g.form(idx); TRACE("gaussian_leak", tout << "processing:\n" << mk_ismt2_pp(f, m()) << "\n";); if (m_candidate_set.is_marked(f)) { // f may be deleted after the following update. // so, we must remove the mark before doing the update m_candidate_set.mark(f, false); SASSERT(!m_candidate_set.is_marked(f)); g.update(idx, m().mk_true(), m().mk_true_proof(), nullptr); m_num_steps ++; continue; } m_r->operator()(f, new_f, new_pr, new_dep); 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; if (m_produce_proofs) new_pr = m().mk_modus_ponens(g.pr(idx), new_pr); if (m_produce_unsat_cores) new_dep = m().mk_join(g.dep(idx), new_dep); g.update(idx, new_f, new_pr, new_dep); if (g.inconsistent()) return; } g.elim_true(); TRACE("solve_eqs", g.display(tout << "after applying substitution\n");); #if 0 DEBUG_CODE({ for (expr* v : m_ordered_vars) { for (unsigned j = 0; j < g.size(); j++) { CASSERT("solve_eqs_bug", !occurs(v, g.form(j))); } }}); #endif } void save_elim_vars(model_converter_ref & mc) { IF_VERBOSE(100, if (!m_ordered_vars.empty()) verbose_stream() << "num. eliminated vars: " << m_ordered_vars.size() << "\n";); m_num_eliminated_vars += m_ordered_vars.size(); if (m_produce_models) { if (!mc.get()) mc = alloc(gmc, m(), "solve-eqs"); for (app* v : m_ordered_vars) { expr * def = nullptr; proof * pr; expr_dependency * dep = nullptr; m_norm_subst->find(v, def, pr, dep); SASSERT(def); static_cast(mc.get())->add(v, def); } } } void collect_num_occs(expr * t, expr_fast_mark1 & visited) { ptr_buffer stack; #define VISIT(ARG) { \ if (is_uninterp_const(ARG)) { \ obj_map::obj_map_entry * entry = m_num_occs.insert_if_not_there2(ARG, 0); \ entry->get_data().m_value++; \ } \ if (!visited.is_marked(ARG)) { \ visited.mark(ARG, true); \ stack.push_back(ARG); \ } \ } VISIT(t); while (!stack.empty()) { expr * t = stack.back(); stack.pop_back(); if (!is_app(t)) continue; unsigned j = to_app(t)->get_num_args(); while (j > 0) { --j; expr * arg = to_app(t)->get_arg(j); VISIT(arg); } } } void collect_num_occs(goal const & g) { if (m_max_occs == UINT_MAX) return; // no need to compute num occs m_num_occs.reset(); expr_fast_mark1 visited; unsigned sz = g.size(); for (unsigned i = 0; i < sz; i++) collect_num_occs(g.form(i), visited); } unsigned get_num_steps() const { return m_num_steps; } unsigned get_num_eliminated_vars() const { return m_num_eliminated_vars; } void operator()(goal_ref const & g, goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); model_converter_ref mc; tactic_report report("solve_eqs", *g); m_produce_models = g->models_enabled(); m_produce_proofs = g->proofs_enabled(); m_produce_unsat_cores = g->unsat_core_enabled(); if (!g->inconsistent()) { 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(); if (m_ordered_vars.empty()) { break; } normalize(); substitute(*(g.get())); if (g->inconsistent()) { break; } save_elim_vars(mc); TRACE("solve_eqs_round", g->display(tout); if (mc) mc->display(tout);); } } g->inc_depth(); g->add(mc.get()); result.push_back(g.get()); TRACE("solve_eqs", g->display(tout);); SASSERT(g->is_well_sorted()); } }; imp * m_imp; params_ref m_params; public: solve_eqs_tactic(ast_manager & m, params_ref const & p, expr_replacer * r, bool owner): m_params(p) { m_imp = alloc(imp, m, p, r, owner); } tactic * translate(ast_manager & m) override { return alloc(solve_eqs_tactic, m, m_params, mk_expr_simp_replacer(m, m_params), true); } ~solve_eqs_tactic() override { dealloc(m_imp); } void updt_params(params_ref const & p) override { m_params = p; m_imp->updt_params(p); } 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, goal_ref_buffer & result) override { (*m_imp)(in, result); report_tactic_progress(":num-elim-vars", m_imp->get_num_eliminated_vars()); } void cleanup() override { unsigned num_elim_vars = m_imp->m_num_eliminated_vars; ast_manager & m = m_imp->m(); expr_replacer * r = m_imp->m_r; if (r) r->set_substitution(nullptr); bool owner = m_imp->m_r_owner; m_imp->m_r_owner = false; // stole replacer imp * d = alloc(imp, m, m_params, r, owner); d->m_num_eliminated_vars = num_elim_vars; std::swap(d, m_imp); dealloc(d); } void collect_statistics(statistics & st) const override { st.update("eliminated vars", m_imp->get_num_eliminated_vars()); } void reset_statistics() override { m_imp->m_num_eliminated_vars = 0; } }; tactic * mk_solve_eqs_tactic(ast_manager & m, params_ref const & p, expr_replacer * r) { if (r == nullptr) return clean(alloc(solve_eqs_tactic, m, p, mk_expr_simp_replacer(m, p), true)); else return clean(alloc(solve_eqs_tactic, m, p, r, false)); } z3-z3-4.8.7/src/tactic/core/solve_eqs_tactic.h000066400000000000000000000011441356505360400211150ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: solve_eqs_tactic.h Abstract: Tactic for solving equations and performing gaussian elimination. Author: Leonardo de Moura (leonardo) 2011-12-29. Revision History: --*/ #ifndef SOLVE_EQS_TACTIC_H_ #define SOLVE_EQS_TACTIC_H_ #include "util/params.h" class ast_manager; class tactic; class expr_replacer; tactic * mk_solve_eqs_tactic(ast_manager & m, params_ref const & p = params_ref(), expr_replacer * r = nullptr); /* ADD_TACTIC("solve-eqs", "eliminate variables by solving equations.", "mk_solve_eqs_tactic(m, p)") */ #endif z3-z3-4.8.7/src/tactic/core/special_relations_tactic.cpp000066400000000000000000000144641356505360400231610ustar00rootroot00000000000000/*++ Copyright (c) 2019 Microsoft Corporation Module Name: special_relations_tactic.cpp Abstract: Detect special relations in an axiomatization, rewrite goal using special relations. Author: Nikolaj Bjorner (nbjorner) 2019-03-28 Notes: --*/ #include "tactic/core/special_relations_tactic.h" #include "ast/rewriter/func_decl_replace.h" #include "ast/ast_util.h" #include "ast/ast_pp.h" void special_relations_tactic::collect_feature(goal const& g, unsigned idx, obj_map& goal_features) { expr* f = g.form(idx); func_decl_ref p(m); if (!is_quantifier(f)) return; unsigned index = 0; app_ref_vector patterns(m); bool is_match = m_pm.match_quantifier_index(to_quantifier(f), patterns, index); TRACE("special_relations", tout << "check " << is_match << " " << mk_pp(f, m) << "\n"; if (is_match) tout << patterns << " " << index << "\n";); if (is_match) { p = to_app(patterns.get(0)->get_arg(0))->get_decl(); insert(goal_features, p, idx, m_properties[index]); } } void special_relations_tactic::insert(obj_map& goal_features, func_decl* f, unsigned idx, sr_property p) { sp_axioms ax; goal_features.find(f, ax); ax.m_goal_indices.push_back(idx); ax.m_sp_features = (sr_property)(p | ax.m_sp_features); goal_features.insert(f, ax); } void special_relations_tactic::initialize() { if (!m_properties.empty()) return; sort_ref A(m.mk_uninterpreted_sort(symbol("A")), m); func_decl_ref R(m.mk_func_decl(symbol("?R"), A, A, m.mk_bool_sort()), m); var_ref x(m.mk_var(0, A), m); var_ref y(m.mk_var(1, A), m); var_ref z(m.mk_var(2, A), m); expr* _x = x, *_y = y, *_z = z; expr_ref Rxy(m.mk_app(R, _x, y), m); expr_ref Ryz(m.mk_app(R, _y, z), m); expr_ref Rxz(m.mk_app(R, _x, z), m); expr_ref Rxx(m.mk_app(R, _x, x), m); expr_ref Ryx(m.mk_app(R, _y, x), m); expr_ref Rzy(m.mk_app(R, _z, y), m); expr_ref Rzx(m.mk_app(R, _z, x), m); expr_ref nRxy(m.mk_not(Rxy), m); expr_ref nRyx(m.mk_not(Ryx), m); expr_ref nRzx(m.mk_not(Rzx), m); expr_ref nRxz(m.mk_not(Rxz), m); sort* As[3] = { A, A, A}; symbol xyz[3] = { symbol("x"), symbol("y"), symbol("z") }; expr_ref fml(m); quantifier_ref q(m); expr_ref pat(m.mk_pattern(to_app(Rxy)), m); expr_ref pat0(m.mk_pattern(to_app(Rxx)), m); expr* pats[1] = { pat }; expr* pats0[1] = { pat0 }; fml = m.mk_or(m.mk_not(Rxy), m.mk_not(Ryz), Rxz); q = m.mk_forall(3, As, xyz, fml, 0, symbol::null, symbol::null, 1, pats); register_pattern(m_pm.initialize(q), sr_transitive); fml = m.mk_or(mk_not(Rxy & Ryz), Rxz); q = m.mk_forall(3, As, xyz, fml, 0, symbol::null, symbol::null, 1, pats); register_pattern(m_pm.initialize(q), sr_transitive); fml = Rxx; q = m.mk_forall(1, As, xyz, fml, 0, symbol::null, symbol::null, 1, pats0); register_pattern(m_pm.initialize(q), sr_reflexive); fml = m.mk_or(nRxy, nRyx, m.mk_eq(x, y)); q = m.mk_forall(2, As, xyz, fml, 0, symbol::null, symbol::null, 1, pats); register_pattern(m_pm.initialize(q), sr_antisymmetric); fml = m.mk_or(mk_not(Rxy & Ryx), m.mk_eq(x, y)); q = m.mk_forall(2, As, xyz, fml, 0, symbol::null, symbol::null, 1, pats); register_pattern(m_pm.initialize(q), sr_antisymmetric); fml = m.mk_or(nRyx, nRzx, Ryz, Rzy); q = m.mk_forall(3, As, xyz, fml, 0, symbol::null, symbol::null, 1, pats); register_pattern(m_pm.initialize(q), sr_lefttree); fml = m.mk_or(mk_not (Ryx & Rzx), Ryz, Rzy); q = m.mk_forall(3, As, xyz, fml, 0, symbol::null, symbol::null, 1, pats); register_pattern(m_pm.initialize(q), sr_lefttree); fml = m.mk_or(nRxy, nRxz, Ryz, Rzy); q = m.mk_forall(3, As, xyz, fml, 0, symbol::null, symbol::null, 1, pats); register_pattern(m_pm.initialize(q), sr_righttree); fml = m.mk_or(mk_not(Rxy & Rxz), Ryz, Rzy); q = m.mk_forall(3, As, xyz, fml, 0, symbol::null, symbol::null, 1, pats); register_pattern(m_pm.initialize(q), sr_righttree); fml = m.mk_or(Rxy, Ryx); q = m.mk_forall(2, As, xyz, fml, 0, symbol::null, symbol::null, 1, pats); register_pattern(m_pm.initialize(q), sr_total); TRACE("special_relations", m_pm.display(tout);); } void special_relations_tactic::register_pattern(unsigned index, sr_property p) { SASSERT(index == m_properties.size()); m_properties.push_back(p); } void special_relations_tactic::operator()(goal_ref const & g, goal_ref_buffer & result) { tactic_report report("special_relations", *g); initialize(); obj_map goal_features; unsigned size = g->size(); for (unsigned idx = 0; idx < size; idx++) { collect_feature(*g, idx, goal_features); } special_relations_util u(m); func_decl_replace replace(m); unsigned_vector to_delete; for(auto const& kv : goal_features) { sr_property feature = kv.m_value.m_sp_features; switch (feature) { case sr_po: replace.insert(kv.m_key, u.mk_po_decl(kv.m_key)); to_delete.append(kv.m_value.m_goal_indices); break; case sr_to: replace.insert(kv.m_key, u.mk_to_decl(kv.m_key)); to_delete.append(kv.m_value.m_goal_indices); break; case sr_plo: replace.insert(kv.m_key, u.mk_plo_decl(kv.m_key)); to_delete.append(kv.m_value.m_goal_indices); break; case sr_lo: replace.insert(kv.m_key, u.mk_lo_decl(kv.m_key)); to_delete.append(kv.m_value.m_goal_indices); break; default: TRACE("special_relations", tout << "unprocessed feature " << feature << "\n";); break; } } if (!replace.empty()) { for (unsigned idx = 0; idx < size; idx++) { if (to_delete.contains(idx)) { g->update(idx, m.mk_true()); } else { expr_ref new_f = replace(g->form(idx)); g->update(idx, new_f); } } g->elim_true(); } g->inc_depth(); result.push_back(g.get()); } tactic * mk_special_relations_tactic(ast_manager & m, params_ref const & p) { return alloc(special_relations_tactic, m, p); } z3-z3-4.8.7/src/tactic/core/special_relations_tactic.h000066400000000000000000000035131356505360400226170ustar00rootroot00000000000000/*++ Copyright (c) 2019 Microsoft Corporation Module Name: special_relations_tactic.h Abstract: Detect special relations in an axiomatization, rewrite goal using special relations. Author: Nikolaj Bjorner (nbjorner) 2019-03-28 Notes: --*/ #ifndef SPECIAL_RELATIONS_TACTIC_H_ #define SPECIAL_RELATIONS_TACTIC_H_ #include "tactic/tactic.h" #include "tactic/tactical.h" #include "ast/special_relations_decl_plugin.h" #include "ast/pattern/expr_pattern_match.h" class special_relations_tactic : public tactic { ast_manager& m; params_ref m_params; expr_pattern_match m_pm; svector m_properties; struct sp_axioms { unsigned_vector m_goal_indices; sr_property m_sp_features; sp_axioms():m_sp_features(sr_none) {} }; void collect_feature(goal const& g, unsigned idx, obj_map& goal_features); void insert(obj_map& goal_features, func_decl* f, unsigned idx, sr_property p); void initialize(); void register_pattern(unsigned index, sr_property); public: special_relations_tactic(ast_manager & m, params_ref const & ref = params_ref()): m(m), m_params(ref), m_pm(m) {} ~special_relations_tactic() override {} void updt_params(params_ref const & p) override { m_params = p; } void collect_param_descrs(param_descrs & r) override { } void operator()(goal_ref const & in, goal_ref_buffer & result) override; void cleanup() override {} tactic * translate(ast_manager & m) override { return alloc(special_relations_tactic, m, m_params); } }; tactic * mk_special_relations_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("special-relations", "detect and replace by special relations.", "mk_special_relations_tactic(m, p)") */ #endif z3-z3-4.8.7/src/tactic/core/split_clause_tactic.cpp000066400000000000000000000114051356505360400221400ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: split_clause_tactic.cpp Abstract: Tactic that creates a subgoal for each literal in a clause (l_1 or ... or l_n). The tactic fails if the main goal does not contain any clause. Author: Leonardo (leonardo) 2011-11-21 Notes: --*/ #include "tactic/tactical.h" #include "tactic/core/split_clause_tactic.h" class split_clause_tactic : public tactic { bool m_largest_clause; unsigned select_clause(ast_manager & m, goal_ref const & in) { unsigned result_idx = UINT_MAX; unsigned len = 0; unsigned sz = in->size(); for (unsigned i = 0; i < sz; i++) { expr * f = in->form(i); if (m.is_or(f)) { unsigned curr_len = to_app(f)->get_num_args(); if (curr_len >= 2) { // consider only non unit clauses if (!m_largest_clause) return i; if (curr_len > len) { result_idx = i; len = curr_len; } } } } return result_idx; } class split_pc : public proof_converter { app_ref m_clause; proof_ref m_clause_pr; public: split_pc(ast_manager & m, app * cls, proof * pr):m_clause(cls, m), m_clause_pr(pr, m) { } ~split_pc() override { } proof_ref operator()(ast_manager & m, unsigned num_source, proof * const * source) override { // Let m_clause be of the form (l_0 or ... or l_{num_source - 1}) // Each source[i] proof is a proof for "false" using l_i as a hypothesis // So, I use lemma for producing a proof for (not l_i) that does not contain the hypothesis, // and unit_resolution for building a proof for the goal. SASSERT(num_source == m_clause->get_num_args()); proof_ref_buffer prs(m); prs.push_back(m_clause_pr); for (unsigned i = 0; i < num_source; i++) { proof * pr_i = source[i]; expr * not_li = m.mk_not(m_clause->get_arg(i)); prs.push_back(m.mk_lemma(pr_i, not_li)); } return proof_ref(m.mk_unit_resolution(prs.size(), prs.c_ptr()), m); } proof_converter * translate(ast_translation & translator) override { return alloc(split_pc, translator.to(), translator(m_clause.get()), translator(m_clause_pr.get())); } void display(std::ostream & out) override { out << "(split-clause-pc)\n"; } }; public: split_clause_tactic(params_ref const & ref = params_ref()) { updt_params(ref); } tactic * translate(ast_manager & m) override { split_clause_tactic * t = alloc(split_clause_tactic); t->m_largest_clause = m_largest_clause; return t; } ~split_clause_tactic() override { } void updt_params(params_ref const & p) override { m_largest_clause = p.get_bool("split_largest_clause", false); } void collect_param_descrs(param_descrs & r) override { r.insert("split_largest_clause", CPK_BOOL, "(default: false) split the largest clause in the goal."); } void operator()(goal_ref const & in, goal_ref_buffer & result) override { SASSERT(in->is_well_sorted()); tactic_report report("split-clause", *in); TRACE("before_split_clause", in->display(tout);); ast_manager & m = in->m(); unsigned cls_pos = select_clause(m, in); if (cls_pos == UINT_MAX) { throw tactic_exception("split-clause tactic failed, goal does not contain any clause"); } bool produce_proofs = in->proofs_enabled(); app * cls = to_app(in->form(cls_pos)); expr_dependency * cls_dep = in->dep(cls_pos); if (produce_proofs) in->set(alloc(split_pc, m, cls, in->pr(cls_pos))); report_tactic_progress(":num-new-branches", cls->get_num_args()); for (expr* lit_i : *cls) { goal * subgoal_i = alloc(goal, *in); subgoal_i->set(in->mc()); proof * pr_i = nullptr; if (produce_proofs) pr_i = m.mk_hypothesis(lit_i); subgoal_i->update(cls_pos, lit_i, pr_i, cls_dep); subgoal_i->inc_depth(); result.push_back(subgoal_i); } in->set(concat(in->pc(), result.size(), result.c_ptr())); in->add(dependency_converter::concat(result.size(), result.c_ptr())); } void cleanup() override { // do nothing this tactic is too simple } }; tactic * mk_split_clause_tactic(params_ref const & p) { return clean(alloc(split_clause_tactic, p)); } z3-z3-4.8.7/src/tactic/core/split_clause_tactic.h000066400000000000000000000011171356505360400216040ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: split_clause_tactic.h Abstract: Tactic that creates a subgoal for each literal in a clause (l_1 or ... or l_n). The tactic fails if the main goal does not contain any clause. Author: Leonardo (leonardo) 2011-11-21 Notes: --*/ #ifndef SPLIT_CLAUSE_TACTIC_H_ #define SPLIT_CLAUSE_TACTIC_H_ #include "util/params.h" class tactic; tactic * mk_split_clause_tactic(params_ref const & p = params_ref()); /* ADD_TACTIC("split-clause", "split a clause in many subgoals.", "mk_split_clause_tactic(p)") */ #endif z3-z3-4.8.7/src/tactic/core/symmetry_reduce_tactic.cpp000066400000000000000000000516361356505360400227030ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: symmetry_reduce.cpp Abstract: Add symmetry breaking predicates to goals. Author: Nikolaj (nbjorner) 2011-05-31 Notes: This is a straight-forward and literal adaption of the algorithms proposed for veriT. --*/ #include "tactic/tactical.h" #include "ast/for_each_expr.h" #include "util/map.h" #include "ast/rewriter/expr_replacer.h" #include "ast/rewriter/rewriter_def.h" #include "ast/ast_pp.h" class symmetry_reduce_tactic : public tactic { class imp; imp * m_imp; public: symmetry_reduce_tactic(ast_manager & m); tactic * translate(ast_manager & m) override { return alloc(symmetry_reduce_tactic, m); } ~symmetry_reduce_tactic() override; void operator()(goal_ref const & g, goal_ref_buffer & result) override; void cleanup() override; }; class ac_rewriter { ast_manager& m_manager; public: ac_rewriter(ast_manager& m): m_manager(m) {} br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { if ((f->is_associative() && f->is_commutative()) || m_manager.is_distinct(f)) { ptr_buffer buffer; buffer.append(num_args, args); std::sort(buffer.begin(), buffer.end(), ast_lt_proc()); bool change = false; for (unsigned i = 0; !change && i < num_args; ++i) { change = (args[i] != buffer[i]); } if (change) { result = m().mk_app(f, num_args, buffer.begin()); return BR_DONE; } } else if (f->is_commutative() && num_args == 2 && args[0]->get_id() > args[1]->get_id()) { expr* args2[2] = { args[1], args[0] }; result = m().mk_app(f, num_args, args2); return BR_DONE; } return BR_FAILED; } void mk_app(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { if (mk_app_core(f, num_args, args, result) == BR_FAILED) result = m().mk_app(f, num_args, args); } private: ast_manager& m() const { return m_manager; } }; struct ac_rewriter_cfg : public default_rewriter_cfg { ac_rewriter m_r; bool rewrite_patterns() const { return false; } bool flat_assoc(func_decl * f) 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; return m_r.mk_app_core(f, num, args, result); } ac_rewriter_cfg(ast_manager & m):m_r(m) {} }; class ac_rewriter_star : public rewriter_tpl { ac_rewriter_cfg m_cfg; public: ac_rewriter_star(ast_manager & m): rewriter_tpl(m, false, m_cfg), m_cfg(m) {} }; template class rewriter_tpl; class symmetry_reduce_tactic::imp { typedef ptr_vector permutation; typedef vector permutations; typedef ptr_vector term_set; typedef obj_map app_map; typedef u_map > inv_app_map; ast_manager& m_manager; ac_rewriter_star m_rewriter; scoped_ptr m_replace; ast_manager& m() const { return m_manager; } public: imp(ast_manager& m) : m_manager(m), m_rewriter(m) { m_replace = mk_default_expr_replacer(m); } ~imp() {} void operator()(goal & g) { if (g.inconsistent()) return; tactic_report report("symmetry-reduce", g); vector > P; expr_ref fml(m()); to_formula(g, fml); app_map occs; compute_occurrences(fml, occs); find_candidate_permutations(fml, occs, P); if (P.empty()) { return; } term_set T, cts; unsigned num_sym_break_preds = 0; for (unsigned i = 0; i < P.size(); ++i) { term_set& consts = P[i]; if (invariant_by_permutations(fml, consts)) { cts.reset(); select_terms(fml, consts, T); while (!T.empty() && cts.size() < consts.size()) { app* t = select_most_promising_term(fml, T, cts, consts, occs); T.erase(t); compute_used_in(t, cts, consts); app* c = select_const(consts, cts); if (!c) break; cts.push_back(c); expr* mem = mk_member(t, cts); g.assert_expr(mem); num_sym_break_preds++; TRACE("symmetry_reduce", tout << "member predicate: " << mk_pp(mem, m()) << "\n";); fml = m().mk_and(fml.get(), mem); normalize(fml); } } } report_tactic_progress(":num-symmetry-breaking ", num_sym_break_preds); } private: void to_formula(goal const & g, expr_ref& fml) { ptr_vector conjs; for (unsigned i = 0; i < g.size(); ++i) { conjs.push_back(g.form(i)); } fml = m().mk_and(conjs.size(), conjs.c_ptr()); normalize(fml); } // find candidate permutations void find_candidate_permutations(expr* fml, app_map const& occs, permutations& P) { app_map coloring; app_map depth; inv_app_map inv_color; unsigned num_occs = 0; compute_sort_colors(fml, coloring); compute_max_depth(fml, depth); merge_colors(occs, coloring); merge_colors(depth, coloring); // compute_siblings(fml, coloring); compute_inv_app(coloring, inv_color); inv_app_map::iterator it = inv_color.begin(), end = inv_color.end(); for (; it != end; ++it) { if (it->m_value.size() < 2) { continue; } VERIFY(occs.find(it->m_value[0], num_occs)); if (num_occs < 2) { continue; } bool is_const = true; for (unsigned j = 0; is_const && j < it->m_value.size(); ++j) { is_const = it->m_value[j]->get_num_args() == 0; } if (!is_const) { continue; } P.push_back(it->m_value); TRACE("symmetry_reduce", for (unsigned i = 0; i < it->m_value.size(); ++i) { tout << mk_pp(it->m_value[i], m()) << " "; } tout << "\n";); } } // // refine coloring by taking most specific generalization. // a |-> c1, b |-> c2 |-> c // struct u_pair { unsigned m_first; unsigned m_second; u_pair(unsigned f, unsigned s) : m_first(f), m_second(s) {} u_pair(): m_first(0), m_second(0) {} struct hash { unsigned operator()(u_pair const& p) const { return mk_mix(p.m_first, p.m_second, 23); } }; struct eq { bool operator()(u_pair const& p, u_pair const& q) const { return p.m_first == q.m_first && p.m_second == q.m_second; } }; }; typedef map pair_map; bool merge_colors(app_map const& colors1, app_map& colors2) { pair_map recolor; unsigned num_colors = 0, v1 = 0, v2 = 0, w = 0, old_max = 0; app_map::iterator it = colors2.begin(), end = colors2.end(); for (; it != end; ++it) { app* a = it->m_key; v1 = it->m_value; VERIFY(colors1.find(a, v2)); if (recolor.find(u_pair(v1, v2), w)) { it->m_value = w; } else { it->m_value = num_colors; recolor.insert(u_pair(v1, v2), num_colors++); } if (v1 > old_max) old_max = v1; } return num_colors > old_max + 1; } class sort_colors { ast_manager& m_manager; app_map& m_app2sortid; obj_map m_sort2id; unsigned m_max_id; public: sort_colors(ast_manager& m, app_map& app2sort): m_manager(m), m_app2sortid(app2sort), m_max_id(0) {} void operator()(app* n) { sort* s = m_manager.get_sort(n); unsigned id; if (!m_sort2id.find(s, id)) { id = m_max_id++; m_sort2id.insert(s, id); } m_app2sortid.insert(n, id); } void operator()(quantifier * n) {} void operator()(var * n) {} }; void compute_sort_colors(expr* fml, app_map& app2sortId) { app2sortId.reset(); sort_colors sc(m(), app2sortId); for_each_expr(sc, fml); } void compute_inv_app(app_map const& map, inv_app_map& inv_map) { app_map::iterator it = map.begin(), end = map.end(); for (; it != end; ++it) { app* t = it->m_key; unsigned n = it->m_value; if (is_uninterpreted(t)) { inv_app_map::entry* e = inv_map.insert_if_not_there2(n, ptr_vector()); e->get_data().m_value.push_back(t); } } } bool is_uninterpreted(app* t) const { return t->get_family_id() == null_family_id; } // compute maximal depth of terms. void compute_max_depth(expr* e, app_map& depth) { ptr_vector todo; unsigned_vector depths; unsigned d, d1; todo.push_back(e); depths.push_back(0); while (!todo.empty()) { e = todo.back(); d = depths.back(); todo.pop_back(); depths.pop_back(); if (is_var(e)) { // nothing } else if (is_quantifier(e)) { todo.push_back(to_quantifier(e)->get_expr()); depths.push_back(d+1); } else if (is_app(e)) { app* a = to_app(e); if (depth.find(a, d1) && d <= d1) { continue; } depth.insert(a, d); ++d; for (unsigned i = 0; i < a->get_num_args(); ++i) { todo.push_back(a->get_arg(i)); depths.push_back(d); } } else { UNREACHABLE(); } } } // color nodes according to the function symbols they appear in typedef obj_hashtable fun_set; typedef obj_map app_parents; class parents { app_parents m_use_funs; public: parents() {} app_parents const& get_parents() { return m_use_funs; } void operator()(app* n) { func_decl* f; unsigned sz = n->get_num_args(); for (unsigned i = 0; i < sz; ++i) { expr* e = n->get_arg(i); if (is_app(e)) { app_parents::obj_map_entry* entry = m_use_funs.insert_if_not_there2(to_app(e), 0); if (!entry->get_data().m_value) entry->get_data().m_value = alloc(fun_set); entry->get_data().m_value->insert(f); } } } void operator()(quantifier *n) {} void operator()(var* n) {} }; void compute_parents(expr* e, app_map& parents) { } typedef hashtable uint_set; typedef obj_map app_siblings;; class siblings { app_map const& m_colors; app_siblings m_sibs; public: siblings(app_map const& colors): m_colors(colors) {} app_siblings const& get() { return m_sibs; } void operator()(app* n) { unsigned sz = n->get_num_args(); for (unsigned i = 0; i < sz; ++i) { expr* e = n->get_arg(i); if (!is_app(e)) continue; app_siblings::obj_map_entry* entry = m_sibs.insert_if_not_there2(to_app(e), 0); if (!entry->get_data().get_value()) entry->get_data().m_value = alloc(uint_set); for (unsigned j = 0; j < sz; ++j) { expr* f = n->get_arg(j); if (is_app(f) && i != j) { unsigned c1 = 0; m_colors.find(to_app(f), c1); entry->get_data().m_value->insert(c1); } } } } void operator()(quantifier *n) {} void operator()(var* n) {} }; // refine coloring by taking colors of siblings into account. bool compute_siblings_rec(expr* e, app_map& colors) { siblings sibs(colors); app_map colors1; for_each_expr(sibs, e); app_siblings const& s = sibs.get(); app_siblings::iterator it = s.begin(), end = s.end(); for (; it != end; ++it) { app* a = it->m_key; uint_set* set = it->m_value; uint_set::iterator it2 = set->begin(), end2 = set->end(); unsigned c = 0; for(; it2 != end2; ++it2) { c += 1 + *it2; } colors1.insert(a, c); dealloc(set); } if (is_app(e)) { colors1.insert(to_app(e), 0); } return merge_colors(colors1, colors); } void compute_siblings(expr* fml, app_map& colors) { while(compute_siblings_rec(fml, colors)); } // check if assertion set is invariant under the current permutation bool invariant_by_permutations(expr* fml, permutation& p) { SASSERT(p.size() >= 2); bool result = check_swap(fml, p[0], p[1]) && check_cycle(fml, p); TRACE("symmetry_reduce", if (result) { tout << "Symmetric: "; } else { tout << "Not symmetric: "; } for (unsigned i = 0; i < p.size(); ++i) { tout << mk_pp(p[i], m()) << " "; } tout << "\n";); return result; } bool check_swap(expr* fml, app* t1, app* t2) { expr_substitution sub(m()); sub.insert(t1, t2); sub.insert(t2, t1); m_replace->set_substitution(&sub); return check_substitution(fml); } bool check_cycle(expr* fml, permutation& p) { expr_substitution sub(m()); for (unsigned i = 0; i + 1 < p.size(); ++i) { sub.insert(p[i], p[i+1]); } sub.insert(p[p.size()-1], p[0]); m_replace->set_substitution(&sub); return check_substitution(fml); } bool check_substitution(expr* t) { expr_ref r(m()); (*m_replace)(t, r); normalize(r); return t == r.get(); } void normalize(expr_ref& r) { proof_ref pr(m()); expr_ref result(m()); m_rewriter(r.get(), result, pr); r = result; } // select terms that are range restricted by set p. void select_terms(expr* fml, term_set const& p, term_set& T) { T.reset(); ptr_vector todo; todo.push_back(fml); app* t = nullptr; while (!todo.empty()) { fml = todo.back(); todo.pop_back(); if (m().is_and(fml)) { todo.append(to_app(fml)->get_num_args(), to_app(fml)->get_args()); } else if (is_range_restriction(fml, p, t)) { T.push_back(t); } } } bool is_range_restriction(expr* form, term_set const& C, app*& t) { if (!m().is_or(form)) return false; unsigned sz = to_app(form)->get_num_args(); t = nullptr; for (unsigned i = 0; i < sz; ++i) { expr* e = to_app(form)->get_arg(i); expr* e1, *e2; if (!m().is_eq(e, e1, e2)) return false; if (!is_app(e1) || !is_app(e2)) return false; app* a1 = to_app(e1), *a2 = to_app(e2); if (C.contains(a1) && (t == nullptr || t == a2)) { t = a2; } else if (C.contains(a2) && (t == nullptr || t == a1)) { t = a1; } else { return false; } } return t != nullptr; } // select the most promising term among T. // terms with the largest number of occurrences have higher weight. // terms that have fewest terms among C as subterms are preferred. class num_occurrences { app_map& m_occs; public: num_occurrences(app_map& occs): m_occs(occs) {} void operator()(app* n) { app_map::obj_map_entry* e; m_occs.insert_if_not_there2(n, 0); unsigned sz = n->get_num_args(); for (unsigned i = 0; i < sz; ++i) { expr* arg = n->get_arg(i); if (is_app(arg)) { e = m_occs.insert_if_not_there2(to_app(arg), 0); e->get_data().m_value++; } } } void operator()(quantifier * n) {} void operator()(var * n) {} }; void compute_occurrences(expr* fml, app_map& occs) { occs.reset(); num_occurrences num_occ(occs); for_each_expr(num_occ, fml); } app* select_most_promising_term( expr* fml, term_set const& T, term_set& cts, term_set const& consts, app_map const& occs) { SASSERT(!T.empty()); app* t = T[0]; unsigned weight = 0, weight1 = 0; VERIFY(occs.find(t, weight)); unsigned cts_delta = compute_cts_delta(t, cts, consts); TRACE("symmetry_reduce", tout << mk_pp(t, m()) << " " << weight << " " << cts_delta << "\n";); for (unsigned i = 1; i < T.size(); ++i) { app* t1 = T[i]; VERIFY(occs.find(t1, weight1)); if (weight1 < weight && t->get_num_args() <= t1->get_num_args()) { continue; } unsigned cts_delta1 = compute_cts_delta(t1, cts, consts); TRACE("symmetry_reduce", tout << mk_pp(t1, m()) << " " << weight1 << " " << cts_delta1 << "\n";); if ((t->get_num_args() == t1->get_num_args() && (weight1 > weight || cts_delta1 < cts_delta)) || t->get_num_args() > t1->get_num_args()) { cts_delta = cts_delta1; weight = weight1; t = t1; } } return t; } // add to cts subterms of t that are members of consts. class member_of { term_set const& m_S; term_set& m_r; public: member_of(term_set const& S, term_set& r) : m_S(S), m_r(r) {} void operator()(app* n) { if (m_S.contains(n) && !m_r.contains(n)) { m_r.push_back(n); } } void operator()(quantifier * n) {} void operator()(var * n) {} }; void compute_used_in(app* t, term_set& cts, term_set const& consts) { member_of mem(consts, cts); for_each_expr(mem, t); TRACE("symmetry_reduce", tout << "Term: " << mk_pp(t, m()) << "\n"; tout << "Support set: "; for (unsigned i = 0; i < consts.size(); ++i) { tout << mk_pp(consts[i], m()) << " "; } tout << "\n"; tout << "Constants: "; for (unsigned i = 0; i < cts.size(); ++i) { tout << mk_pp(cts[i], m()) << " "; } tout << "\n"; ); } unsigned compute_cts_delta(app* t, term_set& cts, term_set const& consts) { unsigned cts_size = cts.size(); if (cts_size == consts.size()) { return 0; } compute_used_in(t, cts, consts); unsigned cts_delta = cts.size() - cts_size; cts.resize(cts_size); return cts_delta; } // select element in A not in B app* select_const(term_set const& A, term_set const& B) { unsigned j; for (j = 0; j < A.size() && B.contains(A[j]); ++j); return (j == A.size())?0:A[j]; } app* mk_member(app* t, term_set const& C) { expr_ref_vector eqs(m()); for (unsigned i = 0; i < C.size(); ++i) { eqs.push_back(m().mk_eq(t, C[i])); } return m().mk_or(eqs.size(), eqs.c_ptr()); } }; symmetry_reduce_tactic::symmetry_reduce_tactic(ast_manager & m) { m_imp = alloc(imp, m); } symmetry_reduce_tactic::~symmetry_reduce_tactic() { dealloc(m_imp); } void symmetry_reduce_tactic::operator()(goal_ref const & g, goal_ref_buffer & result) { fail_if_proof_generation("symmetry_reduce", g); fail_if_unsat_core_generation("symmetry_reduce", g); result.reset(); (*m_imp)(*(g.get())); g->inc_depth(); result.push_back(g.get()); } void symmetry_reduce_tactic::cleanup() { // no-op. } tactic * mk_symmetry_reduce_tactic(ast_manager & m, params_ref const & p) { return alloc(symmetry_reduce_tactic, m); } z3-z3-4.8.7/src/tactic/core/symmetry_reduce_tactic.h000066400000000000000000000010131356505360400223300ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: symmetry_reduce.h Abstract: Add symmetry breaking predicates to assertion sets. Author: Nikolaj (nbjorner) 2011-05-31 Notes: --*/ #ifndef SYMMETRY_REDUCE_TACTIC_H_ #define SYMMETRY_REDUCE_TACTIC_H_ #include "util/params.h" class ast_manager; class tactic; tactic * mk_symmetry_reduce_tactic(ast_manager & m, params_ref const & p); /* ADD_TACTIC("symmetry-reduce", "apply symmetry reduction.", "mk_symmetry_reduce_tactic(m, p)") */ #endif z3-z3-4.8.7/src/tactic/core/tseitin_cnf_tactic.cpp000066400000000000000000001006401356505360400217560ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: tseitin_cnf_tactic.cpp Abstract: Puts an assertion set in CNF. Auxiliary variables are used to avoid blowup. Features: - Efficient encoding is used for commonly used patterns such as: (iff a (iff b c)) (or (not (or a b)) (not (or a c)) (not (or b c))) - Efficient encoding is used for chains of if-then-elses - Distributivity is applied to non-shared nodes if the blowup is acceptable. - The features above can be disabled/enabled using parameters. - The assertion-set is only modified if the resultant set of clauses is "acceptable". Notes: - Term-if-then-else expressions are not handled by this strategy. This kind of expression should be processed by other strategies. - Quantifiers are treated as "theory" atoms. They are viewed as propositional variables by this strategy. - The assertion set may contain free variables. - This strategy assumes the assertion_set_rewriter was used before invoking it. In particular, it is more effective when "and" operators were eliminated. TODO: add proof production Author: Leonardo (leonardo) 2011-12-29 Notes: --*/ #include "ast/ast_pp.h" #include "tactic/tactical.h" #include "tactic/goal_shared_occs.h" #include "tactic/generic_model_converter.h" #include "ast/rewriter/bool_rewriter.h" #include "tactic/core/simplify_tactic.h" static void swap_if_gt(expr * & n1, expr * & n2) { if (n1->get_id() > n2->get_id()) std::swap(n1, n2); } /** \brief Return true if n is of the form (= a b) */ static bool is_iff(ast_manager & m, expr * n, expr * & a, expr * & b) { if (m.is_iff(n, a, b)) return true; if (m.is_eq(n, a, b) && m.is_bool(a)) return true; return false; } class tseitin_cnf_tactic : public tactic { struct imp { struct frame { app * m_t; bool m_first; frame(app * n):m_t(n), m_first(true) {} }; typedef generic_model_converter mc; ast_manager & m; svector m_frame_stack; obj_map m_cache; expr_ref_vector m_cache_domain; goal_shared_occs m_occs; expr_ref_vector m_fresh_vars; ref m_mc; expr_ref_vector m_clauses; expr_dependency_ref_vector m_deps; bool_rewriter m_rw; expr_dependency * m_curr_dep; bool m_produce_models; bool m_produce_unsat_cores; // parameters bool m_common_patterns; bool m_distributivity; unsigned m_distributivity_blowup; bool m_ite_chains; bool m_ite_extra; unsigned long long m_max_memory; unsigned m_num_aux_vars; imp(ast_manager & _m, params_ref const & p): m(_m), m_cache_domain(_m), m_occs(m, false /* don't track atoms */, false /* do not visit quantifiers */), m_fresh_vars(_m), m_clauses(_m), m_deps(_m), m_rw(_m), m_num_aux_vars(0) { updt_params(p); m_rw.set_flat(false); } void updt_params(params_ref const & p) { m_common_patterns = p.get_bool("common_patterns", true); m_distributivity = p.get_bool("distributivity", true); m_distributivity_blowup = p.get_uint("distributivity_blowup", 32); m_ite_chains = p.get_bool("ite_chains", true); m_ite_extra = p.get_bool("ite_extra", true); m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); } void push_frame(app * n) { m_frame_stack.push_back(frame(n)); } void throw_op_not_handled() { throw tactic_exception("operator not supported, apply simplifier before invoking this strategy"); } void inv(expr * n, expr_ref & r) { if (m.is_true(n)) { r = m.mk_false(); return; } if (m.is_false(n)) { r = m.mk_true(); return; } if (m.is_not(n)) { r = to_app(n)->get_arg(0); return; } r = m.mk_not(n); } void mk_lit(expr * n, bool sign, expr_ref & r) { if (sign) r = m.mk_not(n); else r = n; } void get_lit(expr * n, bool sign, expr_ref & r) { start: if (!is_app(n) || to_app(n)->get_num_args() == 0) { mk_lit(n, sign, r); return; } func_decl * f = to_app(n)->get_decl(); if (f->get_family_id() != m.get_basic_family_id()) { mk_lit(n, sign, r); return; } app * l; switch (f->get_decl_kind()) { case OP_NOT: n = to_app(n)->get_arg(0); sign = !sign; goto start; case OP_OR: l = nullptr; m_cache.find(to_app(n), l); SASSERT(l != 0); mk_lit(l, sign, r); return; case OP_ITE: case OP_EQ: if (m.is_bool(to_app(n)->get_arg(1))) { l = nullptr; m_cache.find(to_app(n), l); SASSERT(l != 0); mk_lit(l, sign, r); return; } mk_lit(n, sign, r); return; default: TRACE("tseitin_cnf_bug", tout << f->get_name() << "\n";); UNREACHABLE(); return; } } void visit(expr * n, bool & visited, bool root = false) { start: if (!is_app(n)) return; if (m_cache.contains(to_app(n))) return; if (to_app(n)->get_num_args() == 0) return; func_decl * f = to_app(n)->get_decl(); if (f->get_family_id() != m.get_basic_family_id()) return; switch (f->get_decl_kind()) { case OP_NOT: if (root) { visited = false; push_frame(to_app(n)); return; } else { n = to_app(n)->get_arg(0); goto start; } case OP_OR: visited = false; push_frame(to_app(n)); return; case OP_ITE: case OP_EQ: if (m.is_bool(to_app(n)->get_arg(1))) { visited = false; push_frame(to_app(n)); } return; case OP_AND: case OP_XOR: case OP_IMPLIES: case OP_DISTINCT: throw_op_not_handled(); default: return; } } bool is_shared(expr * t) { return m_occs.is_shared(t); } /** \brief Return true if n is of the form (or (not (or a b)) (not (or a c)) (not (or b c))) \remark This pattern is found in the following "circuits": - carry - less-than (signed and unsigned) */ bool is_or_3and(expr * n, expr * & a, expr * & b, expr * & c) { expr * a1, * a2, * b1, * b2, * c1, * c2; if (!m.is_or(n, a1, b1, c1) || !m.is_not(a1, a1) || is_shared(a1) || !m.is_not(b1, b1) || is_shared(b1) || !m.is_not(c1, c1) || is_shared(c1) || !m.is_or(a1, a1, a2) || !m.is_or(b1, b1, b2) || !m.is_or(c1, c1, c2)) return false; SASSERT(to_app(n)->get_num_args() == 3); swap_if_gt(a1, a2); swap_if_gt(b1, b2); swap_if_gt(c1, c2); if ((a1 == b1 && a2 == c1 && b2 == c2) || (a1 == b1 && a2 == c2 && b2 == c1) || (a1 == c1 && a2 == b1 && b2 == c2)) { a = a1; b = a2; c = b2; return true; } if ((a1 == b2 && a2 == c2 && b1 == c1) || (a1 == c1 && a2 == b2 && b1 == c2) || (a1 == c2 && a2 == b2 && b1 == c1)) { a = a1; b = a2; c = b1; return true; } return false; } /** \brief Return true if n is of the form (iff a (iff b c)) */ bool is_iff3(expr * n, expr * & a, expr * & b, expr * & c) { expr * l1, * l2; if (!is_iff(m, n, l1, l2)) return false; if (!is_shared(l1) && is_iff(m, l1, a, b)) { c = l2; return true; } if (!is_shared(l2) && is_iff(m, l2, b, c)) { a = l1; return true; } return false; } void mk_clause(unsigned num, expr * const * ls) { expr_ref cls(m); m_rw.mk_or(num, ls, cls); m_clauses.push_back(cls); if (m_produce_unsat_cores) m_deps.push_back(m_curr_dep); } void mk_clause(expr * l1) { return mk_clause(1, &l1); } void mk_clause(expr * l1, expr * l2) { expr * ls[2] = { l1, l2 }; mk_clause(2, ls); } void mk_clause(expr * l1, expr * l2, expr * l3) { expr * ls[3] = { l1, l2, l3 }; mk_clause(3, ls); } void mk_clause(expr * l1, expr * l2, expr * l3, expr * l4) { expr * ls[4] = { l1, l2, l3, l4 }; mk_clause(4, ls); } app * mk_fresh() { m_num_aux_vars++; app * v = m.mk_fresh_const(nullptr, m.mk_bool_sort()); m_fresh_vars.push_back(v); if (m_mc) m_mc->hide(v->get_decl()); return v; } void cache_result(app * t, app * r) { m_cache.insert(t, r); m_cache_domain.push_back(t); } enum mres { NO, // did not match CONT, // matched but the children need to be processed DONE // matched }; mres match_not(app * t, bool first, bool root) { expr * a; if (m.is_not(t, a)) { if (first) { bool visited = true; visit(a, visited); if (!visited) return CONT; } expr_ref nla(m); get_lit(a, true, nla); if (root) { mk_clause(nla); } // Remark: don't need to do anything if it is not a root. return DONE; } return NO; } mres match_or_3and(app * t, bool first, bool root) { if (!m_common_patterns) return NO; expr * a, * b, * c; if (is_or_3and(t, a, b, c)) { if (first) { bool visited = true; visit(a, visited); visit(b, visited); visit(c, visited); if (!visited) return CONT; } expr_ref nla(m), nlb(m), nlc(m); get_lit(a, true, nla); get_lit(b, true, nlb); get_lit(c, true, nlc); if (root) { mk_clause(nla, nlb); mk_clause(nla, nlc); mk_clause(nlb, nlc); } else { app_ref k(m), nk(m); k = mk_fresh(); nk = m.mk_not(k); mk_clause(nk, nla, nlb); mk_clause(nk, nla, nlc); mk_clause(nk, nlb, nlc); expr_ref la(m), lb(m), lc(m); inv(nla, la); inv(nlb, lb); inv(nlc, lc); mk_clause(k, la, lb); mk_clause(k, la, lc); mk_clause(k, lb, lc); cache_result(t, k); } return DONE; } return NO; } mres match_iff3(app * t, bool first, bool root) { if (!m_common_patterns) return NO; expr * a, * b, * c; if (is_iff3(t, a, b, c)) { if (first) { bool visited = true; visit(a, visited); visit(b, visited); visit(c, visited); if (!visited) return CONT; } expr_ref la(m), lb(m), lc(m); expr_ref nla(m), nlb(m), nlc(m); get_lit(a, false, la); get_lit(b, false, lb); get_lit(c, false, lc); inv(la, nla); inv(lb, nlb); inv(lc, nlc); if (root) { mk_clause(la, lb, lc); mk_clause(la, nlb, nlc); mk_clause(nla, lb, nlc); mk_clause(nla, nlb, lc); } else { app_ref k(m), nk(m); k = mk_fresh(); nk = m.mk_not(k); mk_clause(nk, la, lb, lc); mk_clause(nk, la, nlb, nlc); mk_clause(nk, nla, lb, nlc); mk_clause(nk, nla, nlb, lc); mk_clause(k, nla, nlb, nlc); mk_clause(k, nla, lb, lc); mk_clause(k, la, nlb, lc); mk_clause(k, la, lb, nlc); cache_result(t, k); } return DONE; } return NO; } mres match_iff_or(app * t, bool first, bool root) { expr * a = nullptr, * _b = nullptr; if (!root) return NO; if (!is_iff(m, t, a, _b)) return NO; bool sign = m.is_not(_b, _b); if (!m.is_or(_b)) return NO; app* b = to_app(_b); if (first) { bool visited = true; visit(a, visited); for (expr* arg : *b) { visit(arg, visited); } if (!visited) return CONT; } expr_ref la(m), nla(m), nlb(m), lb(m); get_lit(a, sign, la); inv(la, nla); expr_ref_buffer lits(m); lits.push_back(nla); for (expr* arg : *b) { get_lit(arg, false, lb); lits.push_back(lb); inv(lb, nlb); mk_clause(la, nlb); } mk_clause(lits.size(), lits.c_ptr()); return DONE; } mres match_iff(app * t, bool first, bool root) { expr * a, * b; if (is_iff(m, t, a, b)) { if (first) { bool visited = true; visit(a, visited); visit(b, visited); if (!visited) return CONT; } expr_ref la(m), lb(m); expr_ref nla(m), nlb(m); get_lit(a, false, la); get_lit(b, false, lb); inv(la, nla); inv(lb, nlb); if (root) { mk_clause(la, nlb); mk_clause(nla, lb); } else { app_ref k(m), nk(m); k = mk_fresh(); nk = m.mk_not(k); mk_clause(nk, la, nlb); mk_clause(nk, nla, lb); mk_clause(k, nla, nlb); mk_clause(k, la, lb); cache_result(t, k); } return DONE; } return NO; } mres match_ite(app * t, bool first, bool root) { if (!m.is_ite(t)) return NO; if (first) { bool visited = true; app * ite = t; while (true) { visit(ite->get_arg(0), visited); if (m_ite_chains && m.is_ite(ite->get_arg(1)) && !is_shared(ite->get_arg(1))) { visit(ite->get_arg(2), visited); ite = to_app(ite->get_arg(1)); continue; } if (m_ite_chains && m.is_ite(ite->get_arg(2)) && !is_shared(ite->get_arg(2))) { visit(ite->get_arg(1), visited); ite = to_app(ite->get_arg(2)); continue; } visit(ite->get_arg(1), visited); visit(ite->get_arg(2), visited); break; } if (!visited) return CONT; } expr_ref_buffer ctx(m); expr_ref_buffer ex_pos_ctx(m); // for extra ite clauses expr_ref_buffer ex_neg_ctx(m); // for extra ite clauses expr_ref la(m), lb(m), lc(m); expr_ref nla(m), nlb(m), nlc(m); app_ref k(m), nk(m); if (!root) { k = mk_fresh(); nk = m.mk_not(k); cache_result(t, k); } #define MK_ITE_ROOT_CLS(L1, L2) { \ ctx.push_back(L1); ctx.push_back(L2); \ mk_clause(ctx.size(), ctx.c_ptr()); \ ctx.pop_back(); ctx.pop_back(); \ } #define MK_ITE_CLS(L1, L2, L3) { \ ctx.push_back(L1); ctx.push_back(L2); ctx.push_back(L3); \ mk_clause(ctx.size(), ctx.c_ptr()); \ ctx.pop_back(); ctx.pop_back(); ctx.pop_back(); \ } app * ite = t; while (true) { get_lit(ite->get_arg(0), false, la); inv(la, nla); if (m_ite_chains && m.is_ite(ite->get_arg(1)) && !is_shared(ite->get_arg(1))) { get_lit(ite->get_arg(2), false, lc); if (root) { MK_ITE_ROOT_CLS(la, lc); } else { inv(lc, nlc); MK_ITE_CLS(la, lc, nk); MK_ITE_CLS(la, nlc, k); if (m_ite_extra) { ex_neg_ctx.push_back(lc); ex_pos_ctx.push_back(nlc); } } ctx.push_back(nla); ite = to_app(ite->get_arg(1)); continue; } if (m_ite_chains && m.is_ite(ite->get_arg(2)) && !is_shared(ite->get_arg(2))) { get_lit(ite->get_arg(1), false, lb); if (root) { MK_ITE_ROOT_CLS(nla, lb); } else { inv(lb, nlb); MK_ITE_CLS(nla, lb, nk); MK_ITE_CLS(nla, nlb, k); if (m_ite_extra) { ex_neg_ctx.push_back(lb); ex_pos_ctx.push_back(nlb); } } ctx.push_back(la); ite = to_app(ite->get_arg(2)); continue; } get_lit(ite->get_arg(1), false, lb); get_lit(ite->get_arg(2), false, lc); if (root) { MK_ITE_ROOT_CLS(nla, lb); MK_ITE_ROOT_CLS(la, lc); } else { inv(lb, nlb); inv(lc, nlc); MK_ITE_CLS(nla, lb, nk); MK_ITE_CLS(la, lc, nk); MK_ITE_CLS(nla, nlb, k); MK_ITE_CLS(la, nlc, k); if (m_ite_extra) { MK_ITE_CLS(lb, lc, nk); MK_ITE_CLS(nlb, nlc, k); ex_neg_ctx.push_back(lb); ex_neg_ctx.push_back(lc); ex_neg_ctx.push_back(nk); mk_clause(ex_neg_ctx.size(), ex_neg_ctx.c_ptr()); ex_pos_ctx.push_back(nlb); ex_pos_ctx.push_back(nlc); ex_pos_ctx.push_back(k); mk_clause(ex_pos_ctx.size(), ex_pos_ctx.c_ptr()); } } break; } return DONE; } mres match_or(app * t, bool first, bool root) { if (!m.is_or(t)) return NO; if (first) { bool visited = true; unsigned num = t->get_num_args(); unsigned blowup = 1; for (unsigned i = 0; i < num; i++) { expr * a = t->get_arg(i); expr * a0; if (m_distributivity && m.is_not(a, a0) && m.is_or(a0) && !is_shared(a0)) { unsigned num2 = to_app(a0)->get_num_args(); if (num2 < m_distributivity_blowup && blowup * num2 < m_distributivity_blowup && blowup < blowup * num2) { blowup *= num2; for (unsigned j = 0; j < num2; j++) visit(to_app(a0)->get_arg(j), visited); continue; } } visit(a, visited); } if (!visited) return CONT; } app_ref k(m), nk(m); if (!root) { k = mk_fresh(); nk = m.mk_not(k); cache_result(t, k); } unsigned num = t->get_num_args(); bool distributivity = false; if (m_distributivity) { // check if need to apply distributivity for (unsigned i = 0; i < num; i++) { expr * a = t->get_arg(i); expr * a0; if (m.is_not(a, a0) && m.is_or(a0) && !is_shared(a0) && to_app(a0)->get_num_args() < m_distributivity_blowup) { distributivity = true; break; } } } if (!distributivity) { // easy case expr_ref_buffer lits(m); expr_ref l(m); for (unsigned i = 0; i < num; i++) { get_lit(t->get_arg(i), false, l); lits.push_back(l); } if (root) { mk_clause(lits.size(), lits.c_ptr()); } else { for (unsigned i = 0; i < num; i++) { inv(lits[i], l); mk_clause(l, k); } lits.push_back(nk); mk_clause(lits.size(), lits.c_ptr()); } } else { expr_ref_buffer buffer(m); expr_ref l(m), nl(m); sbuffer szs; sbuffer it; sbuffer offsets; unsigned blowup = 1; for (unsigned i = 0; i < num; i++) { it.push_back(0); offsets.push_back(buffer.size()); expr * a = t->get_arg(i); expr * a0; if (m.is_not(a, a0) && m.is_or(a0) && !is_shared(a0)) { unsigned num2 = to_app(a0)->get_num_args(); if (num2 < m_distributivity_blowup && blowup * num2 < m_distributivity_blowup && blowup < blowup * num2) { szs.push_back(num2); blowup *= num2; expr_ref_buffer lits(m); for (unsigned j = 0; j < num2; j++) { get_lit(to_app(a0)->get_arg(j), true, nl); buffer.push_back(nl); if (!root) { inv(nl, l); lits.push_back(l); } } if (!root) { lits.push_back(k); mk_clause(lits.size(), lits.c_ptr()); } continue; } } szs.push_back(1); get_lit(a, false, l); buffer.push_back(l); if (!root) { inv(l, nl); mk_clause(nl, k); } } SASSERT(offsets.size() == num); sbuffer arg_lits; ptr_buffer lits; expr ** buffer_ptr = buffer.c_ptr(); for (unsigned i = 0; i < num; i++) { arg_lits.push_back(buffer_ptr + offsets[i]); } do { lits.reset(); for (unsigned i = 0; i < num; i++) { lits.push_back(arg_lits[i][it[i]]); } if (!root) lits.push_back(nk); mk_clause(lits.size(), lits.c_ptr()); } while (product_iterator_next(szs.size(), szs.c_ptr(), it.c_ptr())); } return DONE; } #define TRY(_MATCHER_) \ r = _MATCHER_(t, first, t == root); \ if (r == CONT) goto loop; \ if (r == DONE) { m_frame_stack.pop_back(); continue; } void checkpoint() { if (m.canceled()) throw tactic_exception(TACTIC_CANCELED_MSG); if (memory::get_allocation_size() > m_max_memory) throw tactic_exception(TACTIC_MAX_MEMORY_MSG); } void process(expr * n, expr_dependency * dep) { m_curr_dep = dep; bool visited = true; visit(n, visited, true); if (visited) { expr_ref l(m); get_lit(n, false, l); mk_clause(l); return; } expr * root = n; app * t; bool first; mres r; while (!m_frame_stack.empty()) { loop: checkpoint(); frame & fr = m_frame_stack.back(); t = fr.m_t; first = fr.m_first; fr.m_first = false; TRY(match_or_3and); TRY(match_or); TRY(match_iff3); // TRY(match_iff_or); TRY(match_iff); TRY(match_ite); TRY(match_not); UNREACHABLE(); } } void reset_cache() { m_cache.reset(); m_cache_domain.reset(); } void operator()(goal_ref const & g, goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); tactic_report report("tseitin-cnf", *g); fail_if_proof_generation("tseitin-cnf", g); m_produce_models = g->models_enabled(); m_produce_unsat_cores = g->unsat_core_enabled(); TRACE("tseitin_cnf", g->display(tout);); m_occs(*g); reset_cache(); m_deps.reset(); m_fresh_vars.reset(); m_frame_stack.reset(); m_clauses.reset(); if (m_produce_models) m_mc = alloc(generic_model_converter, m, "tseitin"); else m_mc = nullptr; unsigned size = g->size(); for (unsigned idx = 0; idx < size; idx++) { process(g->form(idx), g->dep(idx)); g->update(idx, m.mk_true(), nullptr, nullptr); // to save memory } SASSERT(!m_produce_unsat_cores || m_clauses.size() == m_deps.size()); g->reset(); unsigned sz = m_clauses.size(); expr_fast_mark1 added; for (unsigned i = 0; i < sz; i++) { expr * cls = m_clauses.get(i); if (added.is_marked(cls)) continue; added.mark(cls); if (m_produce_unsat_cores) g->assert_expr(cls, nullptr, m_deps.get(i)); else g->assert_expr(cls); } if (m_produce_models && !m_fresh_vars.empty()) g->add(m_mc.get()); g->inc_depth(); result.push_back(g.get()); TRACE("tseitin_cnf", g->display(tout);); SASSERT(g->is_well_sorted()); } }; imp * m_imp; params_ref m_params; public: tseitin_cnf_tactic(ast_manager & m, params_ref const & p): m_params(p) { m_imp = alloc(imp, m, p); } tactic * translate(ast_manager & m) override { return alloc(tseitin_cnf_tactic, m, m_params); } ~tseitin_cnf_tactic() override { dealloc(m_imp); } void updt_params(params_ref const & p) override { m_params = p; m_imp->updt_params(p); } void collect_param_descrs(param_descrs & r) override { insert_max_memory(r); r.insert("common_patterns", CPK_BOOL, "(default: true) minimize the number of auxiliary variables during CNF encoding by identifing commonly used patterns"); r.insert("distributivity", CPK_BOOL, "(default: true) minimize the number of auxiliary variables during CNF encoding by applying distributivity over unshared subformulas"); r.insert("distributivity_blowup", CPK_UINT, "(default: 32) maximum overhead for applying distributivity during CNF encoding"); r.insert("ite_chaing", CPK_BOOL, "(default: true) minimize the number of auxiliary variables during CNF encoding by identifing if-then-else chains"); r.insert("ite_extra", CPK_BOOL, "(default: true) add redundant clauses (that improve unit propagation) when encoding if-then-else formulas"); } void operator()(goal_ref const & in, goal_ref_buffer & result) override { (*m_imp)(in, result); report_tactic_progress(":cnf-aux-vars", m_imp->m_num_aux_vars); } void cleanup() override { ast_manager & m = m_imp->m; imp * d = alloc(imp, m, m_params); d->m_num_aux_vars = m_imp->m_num_aux_vars; std::swap(d, m_imp); dealloc(d); } void collect_statistics(statistics & st) const override { st.update("cnf encoding aux vars", m_imp->m_num_aux_vars); } void reset_statistics() override { m_imp->m_num_aux_vars = 0; } }; tactic * mk_tseitin_cnf_core_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(tseitin_cnf_tactic, m, p)); } tactic * mk_tseitin_cnf_tactic(ast_manager & m, params_ref const & p) { params_ref simp_p = p; simp_p.set_bool("elim_and", true); simp_p.set_bool("blast_distinct", true); return or_else(mk_tseitin_cnf_core_tactic(m, p), and_then(using_params(mk_simplify_tactic(m, p), simp_p), mk_tseitin_cnf_core_tactic(m, p))); } z3-z3-4.8.7/src/tactic/core/tseitin_cnf_tactic.h000066400000000000000000000016711356505360400214270ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: tseitin_cnf_tactic.h Abstract: Puts an assertion set in CNF. Auxiliary variables are used to avoid blowup. Author: Leonardo (leonardo) 2011-12-29 Notes: --*/ #ifndef TSEITIN_CNF_TACTIC_H_ #define TSEITIN_CNF_TACTIC_H_ #include "util/params.h" class ast_manager; class tactic; tactic * mk_tseitin_cnf_core_tactic(ast_manager & m, params_ref const & p = params_ref()); tactic * mk_tseitin_cnf_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("tseitin-cnf", "convert goal into CNF using tseitin-like encoding (note: quantifiers are ignored).", "mk_tseitin_cnf_tactic(m, p)") ADD_TACTIC("tseitin-cnf-core", "convert goal into CNF using tseitin-like encoding (note: quantifiers are ignored). This tactic does not apply required simplifications to the input goal like the tseitin-cnf tactic.", "mk_tseitin_cnf_core_tactic(m, p)") */ #endif z3-z3-4.8.7/src/tactic/dependency_converter.cpp000066400000000000000000000061421356505360400214010ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: dependency_converter.cpp Abstract: Utility for converting dependencies across subgoals. Author: Nikolaj Bjorner (nbjorner) 2017-11-19 Notes: --*/ #include "tactic/dependency_converter.h" #include "tactic/goal.h" class unit_dependency_converter : public dependency_converter { expr_dependency_ref m_dep; public: unit_dependency_converter(expr_dependency_ref& d) : m_dep(d) {} expr_dependency_ref operator()() override { return m_dep; } dependency_converter * translate(ast_translation & translator) override { expr_dependency_translation tr(translator); expr_dependency_ref d(tr(m_dep), translator.to()); return alloc(unit_dependency_converter, d); } void display(std::ostream& out) override { out << m_dep.get() << "\n"; } }; class concat_dependency_converter : public dependency_converter { dependency_converter_ref m_dc1; dependency_converter_ref m_dc2; public: concat_dependency_converter(dependency_converter* c1, dependency_converter* c2) : m_dc1(c1), m_dc2(c2) {} expr_dependency_ref operator()() override { expr_dependency_ref d1 = (*m_dc1)(); expr_dependency_ref d2 = (*m_dc2)(); ast_manager& m = d1.get_manager(); return expr_dependency_ref(m.mk_join(d1, d2), m); } dependency_converter * translate(ast_translation & translator) override { return alloc(concat_dependency_converter, m_dc1->translate(translator), m_dc2->translate(translator)); } void display(std::ostream& out) override { m_dc1->display(out); m_dc2->display(out); } }; class goal_dependency_converter : public dependency_converter { ast_manager& m; goal_ref_buffer m_goals; public: goal_dependency_converter(unsigned n, goal * const* goals) : m(goals[0]->m()) { for (unsigned i = 0; i < n; ++i) m_goals.push_back(goals[i]); } expr_dependency_ref operator()() override { expr_dependency_ref result(m.mk_empty_dependencies(), m); for (goal_ref g : m_goals) { dependency_converter_ref dc = g->dc(); if (dc) result = m.mk_join(result, (*dc)()); } return result; } dependency_converter * translate(ast_translation & translator) override { goal_ref_buffer goals; for (goal_ref g : m_goals) goals.push_back(g->translate(translator)); return alloc(goal_dependency_converter, goals.size(), goals.c_ptr()); } void display(std::ostream& out) override { out << "goal-dep\n"; } }; dependency_converter * dependency_converter::concat(dependency_converter * mc1, dependency_converter * mc2) { if (!mc1) return mc2; if (!mc2) return mc1; return alloc(concat_dependency_converter, mc1, mc2); } dependency_converter* dependency_converter::unit(expr_dependency_ref& d) { return alloc(unit_dependency_converter, d); } dependency_converter * dependency_converter::concat(unsigned n, goal * const* goals) { if (n == 0) return nullptr; return alloc(goal_dependency_converter, n, goals); } z3-z3-4.8.7/src/tactic/dependency_converter.h000066400000000000000000000020471356505360400210460ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: dependency_converter.h Abstract: Utility for converting dependencies across subgoals. Author: Nikolaj Bjorner (nbjorner) 2017-11-19 Notes: --*/ #ifndef DEPENDENCY_CONVERTER_H_ #define DEPENDENCY_CONVERTER_H_ #include "util/ref.h" #include "ast/ast_pp_util.h" #include "model/model.h" #include "tactic/converter.h" class goal; class dependency_converter : public converter { public: static dependency_converter* unit(expr_dependency_ref& d); static dependency_converter* concat(dependency_converter * dc1, dependency_converter * dc2); static dependency_converter* concat(unsigned n, goal * const* goals); virtual expr_dependency_ref operator()() = 0; virtual dependency_converter * translate(ast_translation & translator) = 0; }; typedef ref dependency_converter_ref; typedef sref_vector dependency_converter_ref_vector; typedef sref_buffer dependency_converter_ref_buffer; #endif z3-z3-4.8.7/src/tactic/equiv_proof_converter.cpp000066400000000000000000000013551356505360400216220ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: equiv_proof_converter.cpp Abstract: Proof converter that applies equivalence rule to leaves. Author: Nikolaj Bjorner (nbjorner) 2012-11-23 Revision History: --*/ #include "tactic/equiv_proof_converter.h" #include "ast/ast_pp.h" #include "ast/scoped_proof.h" void equiv_proof_converter::insert(expr* fml1, expr* fml2) { if (fml1 != fml2) { scoped_proof _sp(m); proof_ref p1(m), p2(m), p3(m); p1 = m.mk_asserted(fml1); p2 = m.mk_rewrite(fml1, fml2); p3 = m.mk_modus_ponens(p1, p2); TRACE("proof_converter", tout << mk_pp(p3.get(), m) << "\n";); SASSERT(m.has_fact(p3)); m_replace.insert(p3); } } z3-z3-4.8.7/src/tactic/equiv_proof_converter.h000066400000000000000000000022231356505360400212620ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: equiv_proof_converter.h Abstract: Proof converter that applies equivalence rule to leaves. Given a proof P with occurrences of [asserted fml] replace [asserted fml] by a proof of the form [mp [asserted fml'] [~ fml fml']] Author: Nikolaj Bjorner (nbjorner) 2012-11-23 Revision History: --*/ #ifndef EQUIV_PROOF_CONVERTER_H_ #define EQUIV_PROOF_CONVERTER_H_ #include "tactic/replace_proof_converter.h" class equiv_proof_converter : public proof_converter { ast_manager& m; replace_proof_converter m_replace; public: equiv_proof_converter(ast_manager& m): m(m), m_replace(m) {} ~equiv_proof_converter() override {} proof_ref operator()(ast_manager & m, unsigned num_source, proof * const * source) override { return m_replace(m, num_source, source); } proof_converter * translate(ast_translation & translator) override { return m_replace.translate(translator); } void insert(expr* fml1, expr* fml2); ast_manager& get_manager() { return m; } void display(std::ostream & out) override {} }; #endif z3-z3-4.8.7/src/tactic/fd_solver/000077500000000000000000000000001356505360400164505ustar00rootroot00000000000000z3-z3-4.8.7/src/tactic/fd_solver/CMakeLists.txt000066400000000000000000000003641356505360400212130ustar00rootroot00000000000000z3_add_component(fd_solver SOURCES bounded_int2bv_solver.cpp enum2bv_solver.cpp fd_solver.cpp pb2bv_solver.cpp smtfd_solver.cpp COMPONENT_DEPENDENCIES sat_solver TACTIC_HEADERS fd_solver.h smtfd_solver.h ) z3-z3-4.8.7/src/tactic/fd_solver/bounded_int2bv_solver.cpp000066400000000000000000000322351356505360400234570ustar00rootroot00000000000000/*++ Copyright (c) 2016 Microsoft Corporation Module Name: bounded_int2bv_solver.cpp Abstract: This solver identifies bounded integers and rewrites them to bit-vectors. Author: Nikolaj Bjorner (nbjorner) 2016-10-23 Notes: --*/ #include "tactic/fd_solver/bounded_int2bv_solver.h" #include "solver/solver_na2as.h" #include "tactic/tactic.h" #include "ast/rewriter/pb2bv_rewriter.h" #include "tactic/generic_model_converter.h" #include "ast/ast_pp.h" #include "model/model_smt2_pp.h" #include "tactic/arith/bound_manager.h" #include "tactic/arith/bv2int_rewriter.h" #include "ast/rewriter/expr_safe_replace.h" #include "ast/bv_decl_plugin.h" #include "ast/arith_decl_plugin.h" class bounded_int2bv_solver : public solver_na2as { ast_manager& m; mutable bv_util m_bv; mutable arith_util m_arith; mutable expr_ref_vector m_assertions; ref m_solver; mutable ptr_vector m_bounds; mutable func_decl_ref_vector m_bv_fns; mutable func_decl_ref_vector m_int_fns; unsigned_vector m_bv_fns_lim; mutable obj_map m_int2bv; mutable obj_map m_bv2int; mutable obj_map m_bv2offset; mutable bv2int_rewriter_ctx m_rewriter_ctx; mutable bv2int_rewriter_star m_rewriter; mutable bool m_flushed; public: bounded_int2bv_solver(ast_manager& m, params_ref const& p, solver* s): solver_na2as(m), m(m), m_bv(m), m_arith(m), m_assertions(m), m_solver(s), m_bv_fns(m), m_int_fns(m), m_rewriter_ctx(m, p, p.get_uint("max_bv_size", UINT_MAX)), m_rewriter(m, m_rewriter_ctx), m_flushed(false) { solver::updt_params(p); m_bounds.push_back(alloc(bound_manager, m)); } ~bounded_int2bv_solver() override { while (!m_bounds.empty()) { dealloc(m_bounds.back()); m_bounds.pop_back(); } } solver* translate(ast_manager& dst_m, params_ref const& p) override { flush_assertions(); bounded_int2bv_solver* result = alloc(bounded_int2bv_solver, dst_m, p, m_solver->translate(dst_m, p)); ast_translation tr(m, dst_m); for (auto& kv : m_int2bv) result->m_int2bv.insert(tr(kv.m_key), tr(kv.m_value)); for (auto& kv : m_bv2int) result->m_bv2int.insert(tr(kv.m_key), tr(kv.m_value)); for (auto& kv : m_bv2offset) result->m_bv2offset.insert(tr(kv.m_key), kv.m_value); for (func_decl* f : m_bv_fns) result->m_bv_fns.push_back(tr(f)); for (func_decl* f : m_int_fns) result->m_int_fns.push_back(tr(f)); for (bound_manager* b : m_bounds) result->m_bounds.push_back(b->translate(dst_m)); model_converter_ref mc = external_model_converter(); if (mc) { ast_translation tr(m, dst_m); result->set_model_converter(mc->translate(tr)); } return result; } void assert_expr_core(expr * t) override { unsigned i = m_assertions.size(); m_assertions.push_back(t); while (i < m_assertions.size()) { t = m_assertions[i].get(); if (m.is_and(t)) { m_assertions.append(to_app(t)->get_num_args(), to_app(t)->get_args()); m_assertions[i] = m_assertions.back(); m_assertions.pop_back(); } else { ++i; } } } void push_core() override { flush_assertions(); m_solver->push(); m_bv_fns_lim.push_back(m_bv_fns.size()); m_bounds.push_back(alloc(bound_manager, m)); } void pop_core(unsigned n) override { m_assertions.reset(); m_solver->pop(n); if (n > 0) { SASSERT(n <= m_bv_fns_lim.size()); unsigned new_sz = m_bv_fns_lim.size() - n; unsigned lim = m_bv_fns_lim[new_sz]; for (unsigned i = m_int_fns.size(); i > lim; ) { --i; m_int2bv.erase(m_int_fns[i].get()); m_bv2int.erase(m_bv_fns[i].get()); m_bv2offset.erase(m_bv_fns[i].get()); } m_bv_fns_lim.resize(new_sz); m_bv_fns.resize(lim); m_int_fns.resize(lim); } while (n > 0) { dealloc(m_bounds.back()); m_bounds.pop_back(); --n; } } lbool check_sat_core2(unsigned num_assumptions, expr * const * assumptions) override { flush_assertions(); 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); } void collect_param_descrs(param_descrs & r) override { m_solver->collect_param_descrs(r); } void set_produce_models(bool f) override { m_solver->set_produce_models(f); } void set_progress_callback(progress_callback * callback) override { m_solver->set_progress_callback(callback); } void collect_statistics(statistics & st) const override { m_solver->collect_statistics(st); } void get_unsat_core(expr_ref_vector & r) override { m_solver->get_unsat_core(r); } void get_model_core(model_ref & mdl) override { m_solver->get_model(mdl); if (mdl) { model_converter_ref mc = local_model_converter(); 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(); } model_converter* external_model_converter() const { return concat(mc0(), local_model_converter()); } model_converter* local_model_converter() const { if (m_int2bv.empty() && m_bv_fns.empty()) return nullptr; generic_model_converter* mc = alloc(generic_model_converter, m, "bounded_int2bv"); for (func_decl* f : m_bv_fns) mc->hide(f); for (auto const& kv : m_int2bv) { rational offset; VERIFY (m_bv2offset.find(kv.m_value, offset)); expr_ref value(m_bv.mk_bv2int(m.mk_const(kv.m_value)), m); if (!offset.is_zero()) { value = m_arith.mk_add(value, m_arith.mk_numeral(offset, true)); } TRACE("int2bv", tout << mk_pp(kv.m_key, m) << " " << value << "\n";); mc->add(kv.m_key, value); } return mc; } 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()); return mc; } proof * get_proof() override { return m_solver->get_proof(); } std::string reason_unknown() const override { return m_solver->reason_unknown(); } void set_reason_unknown(char const* msg) override { m_solver->set_reason_unknown(msg); } void get_labels(svector & r) override { m_solver->get_labels(r); } ast_manager& get_manager() const override { return m; } expr_ref_vector cube(expr_ref_vector& vars, unsigned backtrack_level) override { flush_assertions(); return m_solver->cube(vars, backtrack_level); } lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes) override { return m_solver->find_mutexes(vars, mutexes); } lbool get_consequences_core(expr_ref_vector const& asms, expr_ref_vector const& vars, expr_ref_vector& consequences) override { flush_assertions(); expr_ref_vector bvars(m); for (unsigned i = 0; i < vars.size(); ++i) { expr* v = vars[i]; func_decl* f; rational offset; if (is_app(v) && is_uninterp_const(v) && m_int2bv.find(to_app(v)->get_decl(), f)) { bvars.push_back(m.mk_const(f)); } else { bvars.push_back(v); } } lbool r = m_solver->get_consequences(asms, bvars, consequences); // translate bit-vector consequences back to integer values for (unsigned i = 0; i < consequences.size(); ++i) { expr* a = nullptr, *b = nullptr, *u = nullptr, *v = nullptr; func_decl* f; rational num; unsigned bvsize; rational offset; VERIFY(m.is_implies(consequences[i].get(), a, b)); if (m.is_eq(b, u, v) && is_uninterp_const(u) && m_bv2int.find(to_app(u)->get_decl(), f) && m_bv.is_numeral(v, num, bvsize)) { SASSERT(num.is_unsigned()); expr_ref head(m); VERIFY (m_bv2offset.find(to_app(u)->get_decl(), offset)); // f + offset == num // f == num - offset head = m.mk_eq(m.mk_const(f), m_arith.mk_numeral(num + offset, true)); consequences[i] = m.mk_implies(a, head); } } return r; } private: void accumulate_sub(expr_safe_replace& sub) const { for (unsigned i = 0; i < m_bounds.size(); ++i) { accumulate_sub(sub, *m_bounds[i]); } } void accumulate_sub(expr_safe_replace& sub, bound_manager& bm) const { bound_manager::iterator it = bm.begin(), end = bm.end(); for (; it != end; ++it) { expr* e = *it; rational lo, hi; bool s1 = false, s2 = false; SASSERT(is_uninterp_const(e)); func_decl* f = to_app(e)->get_decl(); if (bm.has_lower(e, lo, s1) && bm.has_upper(e, hi, s2) && lo <= hi && !s1 && !s2) { func_decl* fbv; rational offset; if (!m_int2bv.find(f, fbv)) { rational n = hi - lo + rational::one(); unsigned num_bits = get_num_bits(n); expr_ref b(m); b = m.mk_fresh_const("b", m_bv.mk_sort(num_bits)); fbv = to_app(b)->get_decl(); offset = lo; m_int2bv.insert(f, fbv); m_bv2int.insert(fbv, f); m_bv2offset.insert(fbv, offset); m_bv_fns.push_back(fbv); m_int_fns.push_back(f); unsigned shift; if (!offset.is_zero() && !n.is_power_of_two(shift)) { m_assertions.push_back(m_bv.mk_ule(b, m_bv.mk_numeral(n-rational::one(), num_bits))); } } else { VERIFY(m_bv2offset.find(fbv, offset)); } expr_ref t(m.mk_const(fbv), m); t = m_bv.mk_bv2int(t); if (!offset.is_zero()) { t = m_arith.mk_add(t, m_arith.mk_numeral(offset, true)); } TRACE("pb", tout << lo << " <= " << hi << " offset: " << offset << "\n"; tout << mk_pp(e, m) << " |-> " << t << "\n";); sub.insert(e, t); } else { TRACE("pb", tout << "unprocessed entry: " << mk_pp(e, m) << "\n"; if (bm.has_lower(e, lo, s1)) { tout << "lower: " << lo << " " << s1 << "\n"; } if (bm.has_upper(e, hi, s2)) { tout << "upper: " << hi << " " << s2 << "\n"; }); } } } unsigned get_num_bits(rational const& k) const { SASSERT(!k.is_neg()); SASSERT(k.is_int()); rational two(2); rational bound(1); unsigned num_bits = 1; while (bound <= k) { ++num_bits; bound *= two; } return num_bits; } void flush_assertions() const { if (m_assertions.empty()) return; m_flushed = true; bound_manager& bm = *m_bounds.back(); for (expr* a : m_assertions) { bm(a); } TRACE("int2bv", bm.display(tout);); expr_safe_replace sub(m); accumulate_sub(sub); proof_ref proof(m); expr_ref fml1(m), fml2(m); if (sub.empty()) { m_solver->assert_expr(m_assertions); } else { for (expr* a : m_assertions) { sub(a, fml1); m_rewriter(fml1, fml2, proof); if (m.canceled()) { m_rewriter.reset(); return; } m_solver->assert_expr(fml2); TRACE("int2bv", tout << fml2 << "\n";); } } m_assertions.reset(); m_rewriter.reset(); } unsigned get_num_assertions() const override { if (m_flushed) { flush_assertions(); return m_solver->get_num_assertions(); } else { return m_assertions.size(); } } expr * get_assertion(unsigned idx) const override { if (m_flushed) { flush_assertions(); return m_solver->get_assertion(idx); } else { return m_assertions.get(idx); } } }; solver * mk_bounded_int2bv_solver(ast_manager & m, params_ref const & p, solver* s) { return alloc(bounded_int2bv_solver, m, p, s); } z3-z3-4.8.7/src/tactic/fd_solver/bounded_int2bv_solver.h000066400000000000000000000006421356505360400231210ustar00rootroot00000000000000/*++ Copyright (c) 2016 Microsoft Corporation Module Name: bounded_int2bv_solver.h Abstract: Finite domain solver. Author: Nikolaj Bjorner (nbjorner) 2016-10-23 Notes: --*/ #ifndef BOUNDED_INT2BV_SOLVER_H_ #define BOUNDED_INT2BV_SOLVER_H_ #include "ast/ast.h" #include "util/params.h" class solver; solver * mk_bounded_int2bv_solver(ast_manager & m, params_ref const & p, solver* s); #endif z3-z3-4.8.7/src/tactic/fd_solver/enum2bv_solver.cpp000066400000000000000000000156361356505360400221370ustar00rootroot00000000000000/*++ Copyright (c) 2016 Microsoft Corporation Module Name: enum2bv_solver.cpp Abstract: Finite domain solver. Enumeration data-types are translated into bit-vectors, and then the incremental sat-solver is applied to the resulting assertions. Author: Nikolaj Bjorner (nbjorner) 2016-10-17 Notes: --*/ #include "ast/bv_decl_plugin.h" #include "ast/datatype_decl_plugin.h" #include "ast/ast_pp.h" #include "model/model_smt2_pp.h" #include "tactic/tactic.h" #include "tactic/generic_model_converter.h" #include "solver/solver_na2as.h" #include "ast/rewriter/enum2bv_rewriter.h" #include "tactic/fd_solver/enum2bv_solver.h" class enum2bv_solver : public solver_na2as { ast_manager& m; ref m_solver; enum2bv_rewriter m_rewriter; public: enum2bv_solver(ast_manager& m, params_ref const& p, solver* s): solver_na2as(m), m(m), m_solver(s), m_rewriter(m, p) { solver::updt_params(p); } ~enum2bv_solver() override {} solver* translate(ast_manager& dst_m, params_ref const& p) override { solver* result = alloc(enum2bv_solver, dst_m, p, m_solver->translate(dst_m, p)); model_converter_ref mc = external_model_converter(); if (mc) { ast_translation tr(m, dst_m); result->set_model_converter(mc->translate(tr)); } return result; } void assert_expr_core(expr * t) override { expr_ref tmp(t, m); expr_ref_vector bounds(m); proof_ref tmp_proof(m); m_rewriter(t, tmp, tmp_proof); m_solver->assert_expr(tmp); m_rewriter.flush_side_constraints(bounds); m_solver->assert_expr(bounds); } void push_core() override { m_rewriter.push(); m_solver->push(); } void pop_core(unsigned n) override { m_solver->pop(n); m_rewriter.pop(n); } lbool check_sat_core2(unsigned num_assumptions, expr * const * assumptions) override { m_solver->updt_params(get_params()); 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); } void collect_param_descrs(param_descrs & r) override { m_solver->collect_param_descrs(r); } void set_produce_models(bool f) override { m_solver->set_produce_models(f); } void set_progress_callback(progress_callback * callback) override { m_solver->set_progress_callback(callback); } void collect_statistics(statistics & st) const override { m_solver->collect_statistics(st); } void get_unsat_core(expr_ref_vector & r) override { m_solver->get_unsat_core(r); } void get_model_core(model_ref & mdl) override { m_solver->get_model(mdl); if (mdl) { model_converter_ref mc = local_model_converter(); if (mc) (*mc)(mdl); } } model_converter* local_model_converter() const { if (m_rewriter.enum2def().empty() && m_rewriter.enum2bv().empty()) { return nullptr; } generic_model_converter* mc = alloc(generic_model_converter, m, "enum2bv"); for (auto const& kv : m_rewriter.enum2bv()) mc->hide(kv.m_value); for (auto const& kv : m_rewriter.enum2def()) mc->add(kv.m_key, kv.m_value); return mc; } 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()); return mc; } proof * get_proof() override { return m_solver->get_proof(); } std::string reason_unknown() const override { return m_solver->reason_unknown(); } void set_reason_unknown(char const* msg) override { m_solver->set_reason_unknown(msg); } void get_labels(svector & r) override { m_solver->get_labels(r); } ast_manager& get_manager() const override { return m; } lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes) override { return m_solver->find_mutexes(vars, mutexes); } expr_ref_vector cube(expr_ref_vector& vars, unsigned backtrack_level) override { return m_solver->cube(vars, backtrack_level); } lbool get_consequences_core(expr_ref_vector const& asms, expr_ref_vector const& vars, expr_ref_vector& consequences) override { datatype_util dt(m); bv_util bv(m); expr_ref_vector bvars(m), conseq(m), bounds(m); // ensure that enumeration variables that // don't occur in the constraints // are also internalized. for (expr* v : vars) { expr_ref tmp(m.mk_eq(v, v), m); proof_ref proof(m); m_rewriter(tmp, tmp, proof); } m_rewriter.flush_side_constraints(bounds); m_solver->assert_expr(bounds); // translate enumeration constants to bit-vectors. for (expr* v : vars) { func_decl* f = nullptr; if (is_app(v) && is_uninterp_const(v) && m_rewriter.enum2bv().find(to_app(v)->get_decl(), f)) { bvars.push_back(m.mk_const(f)); } else { bvars.push_back(v); } } lbool r = m_solver->get_consequences(asms, bvars, consequences); // translate bit-vector consequences back to enumeration types for (unsigned i = 0; i < consequences.size(); ++i) { expr* a = nullptr, *b = nullptr, *u = nullptr, *v = nullptr; func_decl* f; rational num; unsigned bvsize; VERIFY(m.is_implies(consequences[i].get(), a, b)); if (m.is_eq(b, u, v) && is_uninterp_const(u) && m_rewriter.bv2enum().find(to_app(u)->get_decl(), f) && bv.is_numeral(v, num, bvsize)) { SASSERT(num.is_unsigned()); expr_ref head(m); ptr_vector const& enums = *dt.get_datatype_constructors(f->get_range()); if (enums.size() > num.get_unsigned()) { head = m.mk_eq(m.mk_const(f), m.mk_const(enums[num.get_unsigned()])); consequences[i] = m.mk_implies(a, head); } } } 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(); } unsigned get_num_assertions() const override { return m_solver->get_num_assertions(); } expr * get_assertion(unsigned idx) const override { return m_solver->get_assertion(idx); } }; solver * mk_enum2bv_solver(ast_manager & m, params_ref const & p, solver* s) { return alloc(enum2bv_solver, m, p, s); } z3-z3-4.8.7/src/tactic/fd_solver/enum2bv_solver.h000066400000000000000000000006061356505360400215730ustar00rootroot00000000000000/*++ Copyright (c) 2016 Microsoft Corporation Module Name: enum2bv_solver.h Abstract: Finite domain solver. Author: Nikolaj Bjorner (nbjorner) 2016-10-17 Notes: --*/ #ifndef ENUM2BV_SOLVER_H_ #define ENUM2BV_SOLVER_H_ #include "ast/ast.h" #include "util/params.h" class solver; solver * mk_enum2bv_solver(ast_manager & m, params_ref const & p, solver* s); #endif z3-z3-4.8.7/src/tactic/fd_solver/fd_solver.cpp000066400000000000000000000024011356505360400211340ustar00rootroot00000000000000/*++ Copyright (c) 2016 Microsoft Corporation Module Name: fd_solver.cpp Abstract: Finite domain solver. Author: Nikolaj Bjorner (nbjorner) 2016-10-17 Notes: --*/ #include "tactic/fd_solver/fd_solver.h" #include "tactic/tactic.h" #include "sat/sat_solver/inc_sat_solver.h" #include "tactic/fd_solver/enum2bv_solver.h" #include "tactic/fd_solver/pb2bv_solver.h" #include "tactic/fd_solver/bounded_int2bv_solver.h" #include "solver/solver2tactic.h" #include "solver/parallel_tactic.h" #include "solver/parallel_params.hpp" solver * mk_fd_solver(ast_manager & m, params_ref const & p, bool incremental_mode) { solver* s = mk_inc_sat_solver(m, p, incremental_mode); s = mk_enum2bv_solver(m, p, s); s = mk_pb2bv_solver(m, p, s); s = mk_bounded_int2bv_solver(m, p, s); return s; } static tactic * mk_seq_fd_tactic(ast_manager & m, params_ref const& p) { return mk_solver2tactic(mk_fd_solver(m, p, false)); } tactic * mk_parallel_qffd_tactic(ast_manager& m, params_ref const& p) { return mk_parallel_tactic(mk_fd_solver(m, p), p); } tactic * mk_fd_tactic(ast_manager & m, params_ref const& _p) { parallel_params pp(_p); params_ref p = _p; return pp.enable() ? mk_parallel_qffd_tactic(m, p) : mk_seq_fd_tactic(m, p); } z3-z3-4.8.7/src/tactic/fd_solver/fd_solver.h000066400000000000000000000013611356505360400206050ustar00rootroot00000000000000/*++ Copyright (c) 2016 Microsoft Corporation Module Name: fd_solver.h Abstract: Finite domain solver. Author: Nikolaj Bjorner (nbjorner) 2016-10-17 Notes: --*/ #ifndef FD_SOLVER_H_ #define FD_SOLVER_H_ #include "ast/ast.h" #include "util/params.h" class solver; class tactic; solver * mk_fd_solver(ast_manager & m, params_ref const & p, bool incremental_mode = true); tactic * mk_fd_tactic(ast_manager & m, params_ref const & p); tactic * mk_parallel_qffd_tactic(ast_manager& m, params_ref const& p); /* ADD_TACTIC("qffd", "builtin strategy for solving QF_FD problems.", "mk_fd_tactic(m, p)") ADD_TACTIC("pqffd", "builtin strategy for solving QF_FD problems in parallel.", "mk_parallel_qffd_tactic(m, p)") */ #endif z3-z3-4.8.7/src/tactic/fd_solver/pb2bv_solver.cpp000066400000000000000000000131061356505360400215620ustar00rootroot00000000000000/*++ Copyright (c) 2016 Microsoft Corporation Module Name: pb2bv_solver.cpp Abstract: Author: Nikolaj Bjorner (nbjorner) 2016-10-23 Notes: --*/ #include "util/statistics.h" #include "ast/ast_pp.h" #include "ast/rewriter/pb2bv_rewriter.h" #include "ast/rewriter/th_rewriter.h" #include "model/model_smt2_pp.h" #include "tactic/tactic.h" #include "tactic/generic_model_converter.h" #include "solver/solver_na2as.h" #include "tactic/fd_solver/pb2bv_solver.h" class pb2bv_solver : public solver_na2as { ast_manager& m; mutable expr_ref_vector m_assertions; mutable ref m_solver; mutable th_rewriter m_th_rewriter; mutable pb2bv_rewriter m_rewriter; public: pb2bv_solver(ast_manager& m, params_ref const& p, solver* s): solver_na2as(m), m(m), m_assertions(m), m_solver(s), m_th_rewriter(m, p), m_rewriter(m, p) { solver::updt_params(p); } ~pb2bv_solver() override {} solver* translate(ast_manager& dst_m, params_ref const& p) override { flush_assertions(); solver* result = alloc(pb2bv_solver, dst_m, p, m_solver->translate(dst_m, p)); model_converter_ref mc = external_model_converter(); if (mc.get()) { ast_translation tr(m, dst_m); result->set_model_converter(mc->translate(tr)); } return result; } void assert_expr_core(expr * t) override { m_assertions.push_back(t); } void push_core() override { flush_assertions(); m_rewriter.push(); m_solver->push(); } void pop_core(unsigned n) override { m_assertions.reset(); m_solver->pop(n); m_rewriter.pop(n); } lbool check_sat_core2(unsigned num_assumptions, expr * const * assumptions) override { flush_assertions(); 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); } void collect_param_descrs(param_descrs & r) override { m_solver->collect_param_descrs(r); m_rewriter.collect_param_descrs(r);} void set_produce_models(bool f) override { m_solver->set_produce_models(f); } void set_progress_callback(progress_callback * callback) override { m_solver->set_progress_callback(callback); } void collect_statistics(statistics & st) const override { m_rewriter.collect_statistics(st); m_solver->collect_statistics(st); } void get_unsat_core(expr_ref_vector & r) override { m_solver->get_unsat_core(r); } void get_model_core(model_ref & mdl) override { m_solver->get_model(mdl); if (mdl) { model_converter_ref mc = local_model_converter(); 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(); } 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()); return mc; } proof * get_proof() override { return m_solver->get_proof(); } std::string reason_unknown() const override { return m_solver->reason_unknown(); } void set_reason_unknown(char const* msg) override { m_solver->set_reason_unknown(msg); } void get_labels(svector & r) override { m_solver->get_labels(r); } ast_manager& get_manager() const override { return m; } expr_ref_vector cube(expr_ref_vector& vars, unsigned backtrack_level) override { flush_assertions(); return m_solver->cube(vars, backtrack_level); } lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes) override { return m_solver->find_mutexes(vars, mutexes); } lbool get_consequences_core(expr_ref_vector const& asms, expr_ref_vector const& vars, expr_ref_vector& consequences) override { flush_assertions(); return m_solver->get_consequences(asms, vars, consequences); } model_converter* local_model_converter() const { if (m_rewriter.fresh_constants().empty()) { return nullptr; } generic_model_converter* filter = alloc(generic_model_converter, m, "pb2bv"); func_decl_ref_vector const& fns = m_rewriter.fresh_constants(); for (func_decl* f : fns) { filter->hide(f); } return filter; } unsigned get_num_assertions() const override { flush_assertions(); return m_solver->get_num_assertions(); } expr * get_assertion(unsigned idx) const override { flush_assertions(); return m_solver->get_assertion(idx); } private: void flush_assertions() const { if (m_assertions.empty()) return; m_rewriter.updt_params(get_params()); proof_ref proof(m); expr_ref fml1(m), fml(m); expr_ref_vector fmls(m); for (expr* a : m_assertions) { m_th_rewriter(a, fml1, proof); m_rewriter(false, fml1, fml, proof); m_solver->assert_expr(fml); } m_rewriter.flush_side_constraints(fmls); m_solver->assert_expr(fmls); m_assertions.reset(); } }; solver * mk_pb2bv_solver(ast_manager & m, params_ref const & p, solver* s) { return alloc(pb2bv_solver, m, p, s); } z3-z3-4.8.7/src/tactic/fd_solver/pb2bv_solver.h000066400000000000000000000006151356505360400212300ustar00rootroot00000000000000/*++ Copyright (c) 2016 Microsoft Corporation Module Name: pb2bv_solver.h Abstract: Pseudo-Boolean to bit-vector solver. Author: Nikolaj Bjorner (nbjorner) 2016-10-23 Notes: --*/ #ifndef PB2BV_SOLVER_H_ #define PB2BV_SOLVER_H_ #include "ast/ast.h" #include "util/params.h" class solver; solver * mk_pb2bv_solver(ast_manager & m, params_ref const & p, solver* s); #endif z3-z3-4.8.7/src/tactic/fd_solver/smtfd_solver.cpp000066400000000000000000002223611356505360400216710ustar00rootroot00000000000000/** F1, F2, .., -> Fa1, Fa2, ... assert incrementally: t1 <-> Fa1 t2 <-> Fa2 & t1 .... abstraction replaces subterms that are not bv constants by atomic formulas or a term of the form: xor(random_bv, fresh_bv_variable) of enough (24) bits Then default assignment to the fresh bv variable (to 0) is hashed to a value that is likely different form other variables of the same type, so two variables of the same type are then most likely equal in a model if they have to be. atoms := list of atomic formulas (including equalities over bit-vectors) while True: r = check_sat([t_k]) if r != sat: return r M = current model literals := evaluation of atoms under M r = check_sat([!t_k] + literals) if r != unsat: return unknown core = rep(some unsat_core excluding !t_k) if core is SAT modulo A + UF + other theories: return SAT optionally apply a step of superposition (reduce congruence and equality diamonds): let t1 = t2, core1 := core, where t1 in core1 - t1 is uninterpreted constant core := replace t1 by t2 in core1 - t1 = f(args): core := replace all occurrences of t1' by t2 in core1 where M(abs(t1)) = M(abs(t1')). - t1 = select(A,args)? when is it safe to reduce t1? for t in subterms(core) where t is f(args): v1 := M(abs(t)) v_args = M(abs(args)) v2, t2 := table[f][v_args] if v2 != null and v1 != v2: lemmas += (args = t2.args => t = t2) else: table[f][v_args] := v1, t for t in subterms((core) where t is select(A, args): vA := M(abs(A)) v_args = M(abs(args)) v2, args2, t2 := table[vA][v_args] if v2 != null and v1 != v2: lemmas += (args1 = args2 => t = t2) else: table[vA][v_args] := v1, args, t for t in subterms(core) where t is store(A, args, b): vT := M(abs(t)) vA := M(abs(A)) vB := M(abs(b)) v2, args2, t2 := table[vT][v_args] if v2 != null and vB != v2: lemmas += (select(t, args) = b) if v2 = null: table[vT][v_args] := vB, args, t for v_args2 |-> v2, args2, t2 in table[vA]: check table[vT][v_args2] for compatibility with v2 if not compatible: lemmas += (args2 != args => select(t, args2) = select(A, args2)) for v_args2 |-> v2, args2, t2 in table[tA]: check table[vA][v_args2] for compatibility with v2 if not compatible: lemmas += (args2 != args => select(t, args2) = select(A, args2)) for t in subterms(core) where t is (lambda x . M), t is ground: vT := M(abs(t)) for v_args2 |-> v2, args2, t2 in table[vT]: v1 := M(abs(M[args2/x])) if v1 != v2: lemmas += (select(t2, args2) = M[args2/x]) for t in subterms(core) where t is map(f, A, B, C), t is const: similar to lambda for A, B in array_terms(core): lemmas += (select(A, delta(A,B)) = select(B, delta(A,B)) => A = B) if AUF solver returned unsat: add abs(!core) to solver add abs(lemmas) to solver Note: - hint to SMT solver using FD model (add equalities from FD model) - abstractions for multiplication and other BV operations: - add ackerman reductions for BV - commutativity? - fix most bits using model, blast specialization. Z = X * Y X[range] = k1, Y[range] = k2 => Z = (k1++X') * (k2 ++ Y') - abstract also equality - add triangle lemmas whenever using equality chaining in lemmas. - add equality resolution lemmas For core: v = t & phi(v) and v = t occurs in several cores set core := phi(t/v) - do something about arithmetic? */ #include "util/scoped_ptr_vector.h" #include "util/obj_hashtable.h" #include "util/obj_pair_hashtable.h" #include "ast/ast_util.h" #include "ast/ast_pp.h" #include "ast/ast_ll_pp.h" #include "ast/for_each_expr.h" #include "ast/pb_decl_plugin.h" #include "ast/rewriter/th_rewriter.h" #include "ast/rewriter/var_subst.h" #include "tactic/tactic_exception.h" #include "tactic/fd_solver/fd_solver.h" #include "solver/solver.h" #include "solver/solver_na2as.h" #include "solver/solver2tactic.h" #include "smt/smt_solver.h" namespace smtfd { class smtfd_abs { ast_manager& m; expr_ref_vector m_abs, m_rep, m_atoms, m_atom_defs; // abstraction and representation maps array_util m_autil; bv_util m_butil; pb_util m_pb; ptr_vector m_args, m_todo; unsigned m_nv; unsigned_vector m_abs_trail, m_rep_trail, m_nv_trail; unsigned_vector m_abs_lim, m_rep_lim, m_atoms_lim; random_gen m_rand; void pop(unsigned n, expr_ref_vector& v, unsigned_vector& trail, unsigned_vector& lim) { SASSERT(n > 0); unsigned sz = lim[lim.size() - n]; for (unsigned i = trail.size(); i-- > sz;) { v[trail[i]] = nullptr; } trail.shrink(sz); lim.shrink(lim.size() - n); } expr* try_abs(expr* e) { return m_abs.get(e->get_id(), nullptr); } expr* try_rep(expr* e) { return m_rep.get(e->get_id(), nullptr); } expr* fresh_var(expr* t) { symbol name = is_app(t) ? to_app(t)->get_name() : (is_quantifier(t) ? symbol("Q") : symbol("X")); if (m.is_bool(t)) { return m.mk_fresh_const(name, m.mk_bool_sort()); } else if (m_butil.is_bv(t)) { return m.mk_fresh_const(name, m.get_sort(t)); } else { ++m_nv; unsigned bw = log2(m_nv) + 1; if (bw >= 24) { throw default_exception("number of allowed bits for variables exceeded"); } unsigned n = (m_rand() << 16) | m_rand(); expr* num = m_butil.mk_numeral(n, bw); expr* es[2] = { num, m.mk_fresh_const(name, m_butil.mk_sort(bw)) }; expr* e = m_butil.mk_bv_xor(2, es); return m_butil.mk_concat(e, m_butil.mk_numeral(0, 24 - bw)); } } void push_trail(expr_ref_vector& map, unsigned_vector& trail, expr* t, expr* r) { unsigned idx = t->get_id(); map.reserve(idx + 1); map.set(idx, r); trail.push_back(idx); } bool is_atom(expr* r) { if (!m.is_bool(r)) { return false; } if (m.is_eq(r) && !m.is_bool(to_app(r)->get_arg(0))) { return true; } return !is_app(r) || to_app(r)->get_family_id() != m.get_basic_family_id(); } bool is_uninterp_atom(expr* a) { return is_app(a) && to_app(a)->get_num_args() == 0 && to_app(a)->get_family_id() == null_family_id; } public: smtfd_abs(ast_manager& m): m(m), m_abs(m), m_rep(m), m_atoms(m), m_atom_defs(m), m_autil(m), m_butil(m), m_pb(m), m_nv(0) { abs(m.mk_true()); abs(m.mk_false()); } expr_ref_vector const& atoms() { return m_atoms; } expr_ref_vector const& atom_defs() { return m_atom_defs; } void reset_atom_defs() { m_atom_defs.reset(); } void push() { m_abs_lim.push_back(m_abs_trail.size()); m_rep_lim.push_back(m_rep_trail.size()); m_atoms_lim.push_back(m_atoms.size()); m_nv_trail.push_back(m_nv); } void pop(unsigned n) { pop(n, m_abs, m_abs_trail, m_abs_lim); pop(n, m_rep, m_rep_trail, m_rep_lim); m_atoms.shrink(m_atoms_lim[m_atoms_lim.size() - n]); m_atoms_lim.shrink(m_atoms_lim.size() - n); m_nv = m_nv_trail[m_nv_trail.size() - n]; m_nv_trail.shrink(m_nv_trail.size() - n); } std::ostream& display(std::ostream& out) { out << "abs: " << m_atoms.size() << "\n"; for (expr* a : m_atoms) { out << mk_pp(a, m) << ": "; out << mk_bounded_pp(rep(a), m, 2) << "\n"; } return out; } expr* abs_assumption(expr* e) { expr* a = abs(e), *b = nullptr; if (is_uninterp_atom(a) || (m.is_not(a, b) && is_uninterp_atom(b))) { return a; } expr* f = fresh_var(e); push_trail(m_abs, m_abs_trail, e, f); push_trail(m_rep, m_rep_trail, f, e); m_atoms.push_back(f); m_atom_defs.push_back(m.mk_iff(f, a)); return f; } expr* abs(expr* e) { expr* r = try_abs(e); if (r) return r; m_todo.push_back(e); family_id bvfid = m_butil.get_fid(); family_id bfid = m.get_basic_family_id(); family_id pbfid = m_pb.get_family_id(); while (!m_todo.empty()) { expr* t = m_todo.back(); r = try_abs(t); if (r) { m_todo.pop_back(); continue; } if (is_app(t)) { app* a = to_app(t); m_args.reset(); for (expr* arg : *a) { r = try_abs(arg); if (r) { m_args.push_back(r); } else { m_todo.push_back(arg); } } if (m_args.size() != a->get_num_args()) { continue; } family_id fid = a->get_family_id(); if (m.is_eq(a)) { r = m.mk_eq(m_args.get(0), m_args.get(1)); } else if (m.is_distinct(a)) { r = m.mk_distinct(m_args.size(), m_args.c_ptr()); } else if (m.is_ite(a)) { r = m.mk_ite(m_args.get(0), m_args.get(1), m_args.get(2)); } else if (bvfid == fid || bfid == fid || pbfid == fid) { r = m.mk_app(a->get_decl(), m_args.size(), m_args.c_ptr()); } else if (is_uninterp_const(t) && m.is_bool(t)) { r = t; } else if (is_uninterp_const(t) && m_butil.is_bv(t)) { r = t; } else { r = fresh_var(t); } } else { r = fresh_var(t); } if (is_atom(r) && !is_uninterp_const(r)) { expr* rr = fresh_var(r); m_atom_defs.push_back(m.mk_iff(rr, r)); r = rr; } push_trail(m_abs, m_abs_trail, t, r); push_trail(m_rep, m_rep_trail, r, t); if (t != r) { push_trail(m_abs, m_abs_trail, r, r); } if (is_atom(r)) { m_atoms.push_back(r); } } return try_abs(e); } expr* rep(expr* e) { expr* r = try_rep(e); if (r) return r; VERIFY(m.is_not(e, r)); r = try_rep(r); r = m.mk_not(r); abs(r); return r; } }; struct f_app { ast* m_f; app* m_t; sort* m_s; unsigned m_val_offset; }; class theory_plugin; class plugin_context { ast_manager& m; smtfd_abs& m_abs; expr_ref_vector m_lemmas; unsigned m_max_lemmas; th_rewriter m_rewriter; ptr_vector m_plugins; model_ref m_model; public: plugin_context(smtfd_abs& a, ast_manager& m): m(m), m_abs(a), m_lemmas(m), m_rewriter(m) { (void)m; } void set_max_lemmas(unsigned max) { m_max_lemmas = max; } unsigned get_max_lemmas() const { return m_max_lemmas; } smtfd_abs& get_abs() { return m_abs; } void add(expr* f, char const* msg) { m_lemmas.push_back(f); TRACE("smtfd", tout << msg << " " << mk_bounded_pp(f, m, 2) << "\n";); } ast_manager& get_manager() { return m_lemmas.get_manager(); } bool at_max() const { return m_lemmas.size() >= m_max_lemmas; } model& get_model() { return *m_model; } expr_ref_vector::iterator begin() { return m_lemmas.begin(); } expr_ref_vector::iterator end() { return m_lemmas.end(); } unsigned size() const { return m_lemmas.size(); } bool empty() const { return m_lemmas.empty(); } void reset_lemmas() { m_lemmas.reset(); } void add_plugin(theory_plugin* p) { m_plugins.push_back(p); } expr_ref model_value(expr* t); expr_ref model_value(sort* s); bool term_covered(expr* t); bool sort_covered(sort* s); void reset(model_ref& mdl); void rewrite(expr_ref& r) { m_rewriter(r); } /** * \brief use propositional model to create a model of uninterpreted functions */ void populate_model(model_ref& mdl, expr_ref_vector const& terms); /** * \brief check consistency properties that can only be achived using a global analysis of terms */ void global_check(expr_ref_vector const& core); /** * \brief add theory axioms that are violdated in the current model * the round indicator is used to prioritize "cheap" axioms before * expensive axiom instantiation. */ bool add_theory_axioms(expr_ref_vector const& core, unsigned round); std::ostream& display(std::ostream& out); }; struct f_app_eq { theory_plugin& p; f_app_eq(theory_plugin& p):p(p) {} bool operator()(f_app const& a, f_app const& b) const; }; struct f_app_hash { theory_plugin& p; f_app_hash(theory_plugin& p):p(p) {} unsigned operator()(f_app const& a) const; unsigned operator()(expr* const* args) const { return 14; } unsigned operator()(expr* const* args, unsigned idx) const { return args[idx]->hash(); } }; class theory_plugin { protected: typedef hashtable table; ast_manager& m; smtfd_abs& m_abs; plugin_context& m_context; expr_ref_vector m_values; ast_ref_vector m_pinned; expr_ref_vector m_args, m_vargs; f_app_eq m_eq; f_app_hash m_hash; scoped_ptr_vector m_tables; obj_pair_map m_ast2table; f_app mk_app(ast* f, app* t, sort* s) { f_app r; r.m_f = f; r.m_val_offset = m_values.size(); r.m_t = t; r.m_s = s; for (expr* arg : *t) { m_values.push_back(eval_abs(arg)); } m_values.push_back(eval_abs(t)); return r; } f_app const& insert(f_app const& f) { return ast2table(f.m_f, f.m_s).insert_if_not_there(f); } public: theory_plugin(plugin_context& context) : m(context.get_manager()), m_abs(context.get_abs()), m_context(context), m_values(m), m_pinned(m), m_args(m), m_vargs(m), m_eq(*this), m_hash(*this) { m_context.add_plugin(this); } table& ast2table(ast* f, sort* s) { unsigned idx = 0; if (!m_ast2table.find(f, s, idx)) { idx = m_tables.size(); m_tables.push_back(alloc(table, DEFAULT_HASHTABLE_INITIAL_CAPACITY, m_hash, m_eq)); m_ast2table.insert(f, s, idx); m_pinned.push_back(f); } return *m_tables[idx]; } expr_ref_vector const& values() const { return m_values; } ast_manager& get_manager() { return m; } expr_ref eval_abs(expr* t) { return m_context.get_model()(m_abs.abs(t)); } bool is_true_abs(expr* t) { return m_context.get_model().is_true(m_abs.abs(t)); } expr* value_of(f_app const& f) const { return m_values[f.m_val_offset + f.m_t->get_num_args()]; } bool check_congruence(ast* f, app* t, sort* s) { f_app f1 = mk_app(f, t, s); f_app const& f2 = insert(f1); if (f2.m_val_offset == f1.m_val_offset) { return true; } bool eq = value_of(f1) == value_of(f2); m_values.shrink(f1.m_val_offset); return eq; } void enforce_congruence(ast* f, app* t, sort* s) { f_app f1 = mk_app(f, t, s); f_app const& f2 = insert(f1); if (f2.m_val_offset == f1.m_val_offset) { TRACE("smtfd_verbose", tout << "fresh: " << mk_pp(f, m, 2) << "\n";); return; } bool eq = value_of(f1) == value_of(f2); m_values.shrink(f1.m_val_offset); if (eq) { TRACE("smtfd_verbose", tout << "eq: " << " " << mk_bounded_pp(t, m, 2) << " " << mk_bounded_pp(f2.m_t, m, 2) << "\n";); return; } m_args.reset(); SASSERT(t->get_num_args() == f1.m_t->get_num_args()); SASSERT(t->get_num_args() == f2.m_t->get_num_args()); for (unsigned i = 0; i < t->get_num_args(); ++i) { expr* e1 = f1.m_t->get_arg(i); expr* e2 = f2.m_t->get_arg(i); if (e1 != e2) m_args.push_back(m.mk_eq(e1, e2)); } TRACE("smtfd_verbose", tout << "diff: " << mk_bounded_pp(f1.m_t, m, 2) << " " << mk_bounded_pp(f2.m_t, m, 2) << "\n";); m_context.add(m.mk_implies(mk_and(m_args), m.mk_eq(f1.m_t, f2.m_t)), __FUNCTION__); } std::ostream& display(std::ostream& out) { for (table* tb : m_tables) { display(out, *tb); } return out; } std::ostream& display(std::ostream& out, table& t) { out << "table\n"; for (auto const& k : t) { out << "key: " << mk_bounded_pp(k.m_f, m, 2) << "\nterm: " << mk_bounded_pp(k.m_t, m, 2) << "\n"; out << "args:\n"; for (unsigned i = 0; i <= k.m_t->get_num_args(); ++i) { out << mk_bounded_pp(m_values.get(k.m_val_offset + i), m, 3) << "\n"; } out << "\n"; } return out; } expr_ref model_value(expr* t) { return m_context.model_value(t); } expr_ref model_value(sort* s) { return m_context.model_value(s); } virtual void global_check(expr_ref_vector const& core) {} virtual void check_term(expr* t, unsigned round) = 0; virtual expr_ref model_value_core(expr* t) = 0; virtual expr_ref model_value_core(sort* s) = 0; virtual bool term_covered(expr* t) = 0; virtual bool sort_covered(sort* s) = 0; virtual unsigned max_rounds() = 0; virtual void populate_model(model_ref& mdl, expr_ref_vector const& terms) {} virtual void reset() { m_pinned.reset(); m_tables.reset(); m_ast2table.reset(); m_values.reset(); } }; void plugin_context::global_check(expr_ref_vector const& core) { for (theory_plugin* p : m_plugins) { p->global_check(core); } } bool plugin_context::add_theory_axioms(expr_ref_vector const& core, unsigned round) { unsigned max_rounds = 0; for (theory_plugin* p : m_plugins) { max_rounds = std::max(max_rounds, p->max_rounds()); } if (max_rounds < round) { return false; } else if (round < max_rounds) { for (expr* t : subterms(core)) { for (theory_plugin* p : m_plugins) { p->check_term(t, round); } } } else if (round == max_rounds) { global_check(core); } return true; } expr_ref plugin_context::model_value(expr* t) { expr_ref r(get_manager()); for (theory_plugin* p : m_plugins) { r = p->model_value_core(t); if (r) break; } return r; } expr_ref plugin_context::model_value(sort* s) { expr_ref r(get_manager()); for (theory_plugin* p : m_plugins) { r = p->model_value_core(s); if (r) break; } return r; } void plugin_context::reset(model_ref& mdl) { m_lemmas.reset(); m_model = mdl; for (theory_plugin* p : m_plugins) { p->reset(); } } bool plugin_context::sort_covered(sort* s) { for (theory_plugin* p : m_plugins) { if (p->sort_covered(s)) return true; } return false; } bool plugin_context::term_covered(expr* t) { for (theory_plugin* p : m_plugins) { if (p->term_covered(t)) return true; } return false; } std::ostream& plugin_context::display(std::ostream& out) { for (theory_plugin* p : m_plugins) { p->display(out); } return out; } void plugin_context::populate_model(model_ref& mdl, expr_ref_vector const& terms) { for (theory_plugin* p : m_plugins) { p->populate_model(mdl, terms); } } bool f_app_eq::operator()(f_app const& a, f_app const& b) const { if (a.m_f != b.m_f) return false; for (unsigned i = 0; i < a.m_t->get_num_args(); ++i) { if (p.values().get(a.m_val_offset+i) != p.values().get(b.m_val_offset+i)) return false; if (p.get_manager().get_sort(a.m_t->get_arg(i)) != p.get_manager().get_sort(b.m_t->get_arg(i))) return false; } return true; } unsigned f_app_hash::operator()(f_app const& a) const { return get_composite_hash(p.values().c_ptr() + a.m_val_offset, a.m_t->get_num_args(), *this, *this); } class basic_plugin : public theory_plugin { public: basic_plugin(plugin_context& context): theory_plugin(context) {} void check_term(expr* t, unsigned round) override { } bool term_covered(expr* t) override { return is_app(t) && to_app(t)->get_family_id() == m.get_basic_family_id(); } bool sort_covered(sort* s) override { return m.is_bool(s); } unsigned max_rounds() override { return 0; } void populate_model(model_ref& mdl, expr_ref_vector const& terms) override { } expr_ref model_value_core(expr* t) override { return m.is_bool(t) ? m_context.get_model()(m_abs.abs(t)) : expr_ref(m); } expr_ref model_value_core(sort* s) override { return m.is_bool(s) ? expr_ref(m.mk_false(), m) : expr_ref(m); } }; class pb_plugin : public theory_plugin { pb_util m_pb; public: pb_plugin(plugin_context& context): theory_plugin(context), m_pb(m) {} void check_term(expr* t, unsigned round) override { } bool term_covered(expr* t) override { return is_app(t) && to_app(t)->get_family_id() == m_pb.get_family_id(); } bool sort_covered(sort* s) override { return m.is_bool(s); } unsigned max_rounds() override { return 0; } void populate_model(model_ref& mdl, expr_ref_vector const& terms) override { } expr_ref model_value_core(expr* t) override { return expr_ref(m); } expr_ref model_value_core(sort* s) override { return expr_ref(m); } }; class bv_plugin : public theory_plugin { bv_util m_butil; public: bv_plugin(plugin_context& context): theory_plugin(context), m_butil(m) {} void check_term(expr* t, unsigned round) override { } bool term_covered(expr* t) override { return is_app(t) && to_app(t)->get_family_id() == m_butil.get_family_id(); } bool sort_covered(sort* s) override { return m_butil.is_bv_sort(s); } unsigned max_rounds() override { return 0; } void populate_model(model_ref& mdl, expr_ref_vector const& terms) override { } expr_ref model_value_core(expr* t) override { return m_butil.is_bv(t) ? m_context.get_model()(m_abs.abs(t)) : expr_ref(m); } expr_ref model_value_core(sort* s) override { return m_butil.is_bv_sort(s) ? expr_ref(m_butil.mk_numeral(rational(0), s), m) : expr_ref(m); } }; class uf_plugin : public theory_plugin { obj_map m_sort2idx; typedef obj_map val2elem_t; scoped_ptr_vector m_val2elem; val2elem_t& get_table(sort* s) { unsigned idx = 0; if (!m_sort2idx.find(s, idx)) { idx = m_val2elem.size(); m_sort2idx.insert(s, idx); m_val2elem.push_back(alloc(val2elem_t)); } return *m_val2elem[idx]; } bool is_uf(expr* t) { return is_app(t) && to_app(t)->get_family_id() == null_family_id && to_app(t)->get_num_args() > 0; } val2elem_t& ensure_table(sort* s) { val2elem_t& v2e = get_table(s); if (v2e.empty()) { v2e.insert(m.mk_true(), nullptr); } ptr_vector keys, values; for (auto const& kv : v2e) { if (kv.m_value) return v2e; keys.push_back(kv.m_key); values.push_back(m.mk_model_value(values.size(), s)); m_pinned.push_back(values.back()); } m_context.get_model().register_usort(s, values.size(), values.c_ptr()); for (unsigned i = 0; i < keys.size(); ++i) { v2e.insert(keys[i], values[i]); } return v2e; } public: uf_plugin(plugin_context& context): theory_plugin(context) {} void check_term(expr* t, unsigned round) override { sort* s = m.get_sort(t); if (round == 0 && is_uf(t)) { TRACE("smtfd_verbose", tout << "check-term: " << mk_bounded_pp(t, m, 2) << "\n";); enforce_congruence(to_app(t)->get_decl(), to_app(t), s); } else if (round == 1 && sort_covered(s) && m.is_value(t)) { expr_ref v = eval_abs(t); val2elem_t& v2e = get_table(s); expr* e; if (v2e.find(v, e)) { if (e != t) { m_context.add(m.mk_not(m.mk_eq(e, t)), __FUNCTION__); } } else { m_pinned.push_back(v); v2e.insert(v, t); } } if (m.is_value(t)) { // std::cout << mk_bounded_pp(t, m, 2) << " " << eval_abs(t) << " " << mk_pp(s, m) << "\n"; } } bool term_covered(expr* t) override { sort* s = m.get_sort(t); if (sort_covered(s)) { val2elem_t& v2e = get_table(s); expr_ref v = eval_abs(t); if (!v2e.contains(v)) { m_pinned.push_back(v); v2e.insert(v, nullptr); } } check_term(t, 0); return is_uf(t) || is_uninterp_const(t) || sort_covered(s); } bool sort_covered(sort* s) override { return s->get_family_id() == m.get_user_sort_family_id(); } void reset() override { theory_plugin::reset(); for (auto& v2e : m_val2elem) { v2e->reset(); } } unsigned max_rounds() override { return 1; } void populate_model(model_ref& mdl, expr_ref_vector const& terms) override { expr_ref_vector args(m); for (table* tb : m_tables) { func_interp* fi = nullptr; func_decl* fn = nullptr; for (f_app const& f : *tb) { fn = to_func_decl(f.m_f); unsigned arity = fn->get_arity(); if (!fi) { fi = alloc(func_interp, m, arity); } args.reset(); for (expr* arg : *f.m_t) { args.push_back(model_value(arg)); } expr_ref val = model_value(f.m_t); TRACE("smtfd_verbose", tout << mk_bounded_pp(f.m_t, m, 2) << " := " << val << "\n";); fi->insert_new_entry(args.c_ptr(), val); } mdl->register_decl(fn, fi); } for (expr* t : subterms(terms)) { if (is_uninterp_const(t) && sort_covered(m.get_sort(t))) { expr_ref val = model_value(t); mdl->register_decl(to_app(t)->get_decl(), val); } } } expr_ref model_value_core(expr* t) override { sort* s = m.get_sort(t); if (sort_covered(s)) { auto& v2e = ensure_table(s); return expr_ref(v2e[eval_abs(t)], m); } return expr_ref(m); } expr_ref model_value_core(sort* s) override { if (sort_covered(s)) { auto& v2e = ensure_table(s); return expr_ref(v2e.begin()->m_value, m); } return expr_ref(m); } }; class ar_plugin : public theory_plugin { array_util m_autil; unsigned_vector m_num_stores; // count number of equivalent stores void update_lambda(expr* t) { if (m_autil.is_store(t)) { expr_ref tV = eval_abs(t); inc_lambda(tV); } } unsigned get_lambda(expr* tV) { unsigned id = tV->get_id(); if (id >= m_num_stores.size()) { m_num_stores.resize(id + 1, 0); } return m_num_stores[id]; } void inc_lambda(expr* tV) { unsigned id = tV->get_id(); if (id >= m_num_stores.size()) { m_num_stores.resize(id + 1, 0); } if (0 == m_num_stores[id]++) { m_pinned.push_back(tV); } } void insert_select(app* t) { expr* a = t->get_arg(0); expr_ref vA = eval_abs(a); check_congruence(vA, t, m.get_sort(a)); } void check_select(app* t) { expr* a = t->get_arg(0); expr_ref vA = eval_abs(a); enforce_congruence(vA, t, m.get_sort(a)); } // check that (select(t, t.args) = t.value) void check_store0(app * t) { SASSERT(m_autil.is_store(t)); m_args.reset(); m_args.push_back(t); for (unsigned i = 1; i + 1 < t->get_num_args(); ++i) { m_args.push_back(t->get_arg(i)); } app_ref sel(m_autil.mk_select(m_args), m); expr* stored_value = t->get_arg(t->get_num_args()-1); expr_ref val1 = eval_abs(sel); expr_ref val2 = eval_abs(stored_value); // A[i] = v if (val1 != val2) { TRACE("smtfd", tout << "select/store: " << mk_bounded_pp(t, m, 2) << "\n";); m_context.add(m.mk_eq(sel, stored_value), __FUNCTION__); m_pinned.push_back(sel); insert_select(sel); } } // store(A, i, v)[j] = A[i] or i = j void check_select_store(app * t) { SASSERT(m_autil.is_select(t)); if (!m_autil.is_store(t->get_arg(0))) { return; } app* store = to_app(t->get_arg(0)); expr* val = store->get_arg(store->get_num_args()-1); expr* a = store->get_arg(0); expr_ref_vector eqs(m); m_args.reset(); m_args.push_back(a); for (unsigned i = 1; i < t->get_num_args(); ++i) { expr* arg1 = t->get_arg(i); expr* arg2 = store->get_arg(i); if (arg1 == arg2) continue; expr_ref v1 = eval_abs(arg1); expr_ref v2 = eval_abs(arg2); m_args.push_back(arg1); eqs.push_back(m.mk_eq(arg1, arg2)); } if (eqs.empty()) return; expr_ref eq = mk_and(eqs); expr_ref eqV = eval_abs(eq); expr_ref val1 = eval_abs(t); expr_ref val2 = eval_abs(val); if (val1 != val2 && !m.is_false(eqV)) { m_context.add(m.mk_implies(mk_and(eqs), m.mk_eq(t, val)), __FUNCTION__); } app_ref sel(m_autil.mk_select(m_args), m); val2 = eval_abs(sel); if (val1 != val2 && !m.is_true(eqV)) { TRACE("smtfd", tout << "select/store: " << mk_bounded_pp(t, m, 2) << "\n";); m_context.add(m.mk_or(m.mk_eq(sel, t), mk_and(eqs)), __FUNCTION__); m_pinned.push_back(sel); insert_select(sel); } } /** every t and a must agree with select values that are different from updates in t. let t := store(B, i, v) add axioms of the form: i = j or A != B or store(B,i,v)[j] = A[j] where j is in tableA and value equal to some index in tableT */ void check_store2(app* t) { SASSERT(m_autil.is_store(t)); expr* arg = t->get_arg(0); expr_ref vT = eval_abs(t); expr_ref vA = eval_abs(arg); table& tT = ast2table(vT, m.get_sort(t)); // select table of t table& tA = ast2table(vA, m.get_sort(arg)); // select table of arg if (vT == vA) { return; } m_vargs.reset(); for (unsigned i = 0; i + 1 < t->get_num_args(); ++i) { m_vargs.push_back(eval_abs(t->get_arg(i))); } reconcile_stores(t, vT, tT, vA, tA); } // // T = store(A, i, v) // T[j] = w: i = j or A[j] = T[j] // A[j] = w: i = j or T[j] = A[j] // void reconcile_stores(app* t, expr* vT, table& tT, expr* vA, table& tA) { unsigned r = 0; if (get_lambda(vA) <= 1) { return; } inc_lambda(vT); for (auto& fA : tA) { f_app fT; if (m_context.at_max()) { break; } if (m.get_sort(t) != m.get_sort(fA.m_t->get_arg(0))) { continue; } if (!tT.find(fA, fT) || (value_of(fA) != value_of(fT) && !eq(m_vargs, fA))) { add_select_store_axiom(t, fA); ++r; } } #if 0 // only up-propagation really needed. for (auto& fT : tT) { f_app fA; if (m_context.at_max()) { break; } if (!tA.find(fT, fA) && m.get_sort(t) == m.get_sort(fT.m_t->get_arg(0))) { TRACE("smtfd", tout << "not found\n";); add_select_store_axiom(t, fT); ++r; } } #endif } void add_select_store_axiom(app* t, f_app& f) { SASSERT(m_autil.is_store(t)); expr* a = t->get_arg(0); m_args.reset(); for (expr* arg : *f.m_t) { m_args.push_back(arg); } SASSERT(m.get_sort(t) == m.get_sort(a)); TRACE("smtfd", tout << mk_bounded_pp(t, m, 2) << " " << mk_bounded_pp(f.m_t, m, 2) << "\n";); expr_ref eq = mk_eq_idxs(t, f.m_t); m_args[0] = t; expr_ref sel1(m_autil.mk_select(m_args), m); m_args[0] = a; expr_ref sel2(m_autil.mk_select(m_args), m); expr_ref fml(m.mk_or(eq, m.mk_eq(sel1, sel2)), m); if (!is_true_abs(fml)) { m_context.add(fml, __FUNCTION__); } } bool same_array_sort(f_app const& fA, f_app const& fT) const { return m.get_sort(fA.m_t->get_arg(0)) == m.get_sort(fT.m_t->get_arg(0)); } /** Enforce M[x] == rewrite(M[x]) for every A[x] such that M = A under current model. */ void beta_reduce(expr* t) { if (m_autil.is_map(t) || m_autil.is_const(t) || is_lambda(t)) { expr_ref vT = eval_abs(t); table& tT = ast2table(vT, m.get_sort(t)); for (f_app & f : tT) { if (m.get_sort(t) != m.get_sort(f.m_t->get_arg(0))) continue; if (m_context.at_max()) break; m_args.reset(); m_args.append(f.m_t->get_num_args(), f.m_t->get_args()); m_args[0] = t; expr_ref sel(m_autil.mk_select(m_args), m); expr_ref selr = sel; m_context.rewrite(selr); expr_ref vS = eval_abs(sel); expr_ref vR = eval_abs(selr); if (vS != vR) { m_context.add(m.mk_eq(sel, selr), __FUNCTION__); } } } } // arguments, except for array variable are equal. bool eq(expr_ref_vector const& args, f_app const& f) { SASSERT(args.size() == f.m_t->get_num_args()); for (unsigned i = args.size(); i-- > 1; ) { if (args.get(i) != m_values.get(f.m_val_offset + i)) return false; } return true; } expr_ref mk_eq_idxs(app* t, app* s) { SASSERT(m_autil.is_store(t)); SASSERT(m_autil.is_select(s)); expr_ref_vector r(m); for (unsigned i = 1; i < s->get_num_args(); ++i) { r.push_back(m.mk_eq(t->get_arg(i), s->get_arg(i))); } return mk_and(r); } bool same_table(table const& t1, table const& t2) { if (t1.size() != t2.size()) { return false; } for (f_app const& f1 : t1) { f_app f2; if (!t2.find(f1, f2) || value_of(f1) != value_of(f2)) { return false; } } return true; } bool same_table(expr* v1, sort* s1, expr* v2, sort* s2) { return same_table(ast2table(v1, s1), ast2table(v2, s2)); } void enforce_extensionality(expr* a, expr* b) { sort* s = m.get_sort(a); unsigned arity = get_array_arity(s); expr_ref_vector args(m); args.push_back(a); for (unsigned i = 0; i < arity; ++i) { args.push_back(m.mk_app(m_autil.mk_array_ext(s, i), a, b)); } expr_ref a1(m_autil.mk_select(args), m); args[0] = b; expr_ref b1(m_autil.mk_select(args), m); expr_ref ext(m.mk_iff(m.mk_eq(a1, b1), m.mk_eq(a, b)), m); if (!m.is_true(eval_abs(ext))) { TRACE("smtfd", tout << mk_bounded_pp(a, m, 2) << " " << mk_bounded_pp(b, m, 2) << "\n";); m_context.add(ext, __FUNCTION__); } } expr_ref mk_array_value(table& t) { expr_ref value(m), default_value(m); SASSERT(!t.empty()); expr_ref_vector args(m); for (f_app const& f : t) { SASSERT(m_autil.is_select(f.m_t)); expr_ref v = model_value(f.m_t); if (!value) { sort* s = m.get_sort(f.m_t->get_arg(0)); default_value = v; value = m_autil.mk_const_array(s, default_value); } else if (v != default_value) { args.reset(); args.push_back(value); for (unsigned i = 1; i < f.m_t->get_num_args(); ++i) { args.push_back(model_value(f.m_t->get_arg(i))); } args.push_back(v); value = m_autil.mk_store(args); } } return value; } public: ar_plugin(plugin_context& context): theory_plugin(context), m_autil(m) {} void reset() override { theory_plugin::reset(); m_num_stores.reset(); } void check_term(expr* t, unsigned round) override { switch (round) { case 0: if (m_autil.is_select(t)) { insert_select(to_app(t)); } else if (m_autil.is_store(t)) { update_lambda(t); check_store0(to_app(t)); } break; case 1: if (m_autil.is_select(t)) { check_select(to_app(t)); } else { beta_reduce(t); } break; case 2: if (m_autil.is_store(t)) { check_store2(to_app(t)); } else if (m_autil.is_select(t)) { check_select_store(to_app(t)); } break; default: break; } } bool term_covered(expr* t) override { // populate congruence table for model building if (m_autil.is_select(t)) { expr* a = to_app(t)->get_arg(0); expr_ref vA = eval_abs(a); insert(mk_app(vA, to_app(t), m.get_sort(a))); } return m_autil.is_store(t) || m_autil.is_select(t) || m_autil.is_map(t) || m_autil.is_ext(t) || is_lambda(t) || m_autil.is_const(t); } bool sort_covered(sort* s) override { if (!m_autil.is_array(s)) { return false; } if (!m_context.sort_covered(get_array_range(s))) { return false; } for (unsigned i = 0; i < get_array_arity(s); ++i) { if (!m_context.sort_covered(get_array_domain(s, i))) return false; } return true; } expr_ref model_value_core(expr* t) override { if (m_autil.is_array(t)) { expr_ref vT = eval_abs(t); table& tb = ast2table(vT, m.get_sort(t)); if (tb.empty()) { return model_value_core(m.get_sort(t)); } else { return mk_array_value(tb); } } return expr_ref(m); } expr_ref model_value_core(sort* s) override { if (m_autil.is_array(s)) { return expr_ref(m_autil.mk_const_array(s, model_value(get_array_range(s))), m); } return expr_ref(m); } void populate_model(model_ref& mdl, expr_ref_vector const& terms) override { for (expr* t : subterms(terms)) { if (is_uninterp_const(t) && m_autil.is_array(t)) { mdl->register_decl(to_app(t)->get_decl(), model_value_core(t)); } } } unsigned max_rounds() override { return 3; } void global_check(expr_ref_vector const& core) override { expr_mark seen; expr_ref_vector shared(m), sharedvals(m); for (expr* t : subterms(core)) { if (!is_app(t)) continue; app* a = to_app(t); unsigned offset = 0; if (m_autil.is_select(t) || m_autil.is_store(t)) { offset = 1; } else if (m_autil.is_map(t)) { continue; } for (unsigned i = a->get_num_args(); i-- > offset; ) { expr* arg = a->get_arg(i); if (m_autil.is_array(arg) && !seen.is_marked(arg)) { shared.push_back(arg); seen.mark(arg, true); } } } for (expr* s : shared) { sharedvals.push_back(eval_abs(s)); } for (unsigned i = 0; !m_context.at_max() && i < shared.size(); ++i) { expr* s1 = shared.get(i); expr* v1 = sharedvals.get(i); for (unsigned j = i + 1; !m_context.at_max() && j < shared.size(); ++j) { expr* s2 = shared.get(j); expr* v2 = sharedvals.get(j); if (v1 != v2 && m.get_sort(s1) == m.get_sort(s2) && same_table(v1, m.get_sort(s1), v2, m.get_sort(s2))) { enforce_extensionality(s1, s2); } } } } }; class mbqi { ast_manager& m; plugin_context& m_context; obj_hashtable& m_enforced; model_ref m_model; ref<::solver> m_solver; expr_ref_vector m_pinned; obj_pair_map m_val2term; expr* abs(expr* e) { return m_context.get_abs().abs(e); } expr_ref eval_abs(expr* t) { return (*m_model)(abs(t)); } void restrict_to_universe(expr * sk, ptr_vector const & universe) { SASSERT(!universe.empty()); expr_ref_vector eqs(m); for (expr * e : universe) { eqs.push_back(m.mk_eq(sk, e)); } expr_ref fml = mk_or(eqs); m_solver->assert_expr(fml); } // !Ex P(x) => !P(t) // Ax P(x) => P(t) // l_true: new instance // l_false: no new instance // l_undef unresolved lbool check_forall(quantifier* q) { expr_ref tmp(m); unsigned sz = q->get_num_decls(); if (!m_model->eval_expr(q->get_expr(), tmp, true)) { return l_undef; } if (is_forall(q) && m.is_true(tmp)) { return l_false; } if (is_exists(q) && m.is_false(tmp)) { return l_false; } TRACE("smtfd", tout << mk_pp(q, m) << "\n"; /*tout << *m_model << "\n"; */ tout << "eval: " << tmp << "\n";); m_solver->push(); expr_ref_vector vars(m), vals(m); vars.resize(sz, nullptr); vals.resize(sz, nullptr); for (unsigned i = 0; i < sz; ++i) { sort* s = q->get_decl_sort(i); vars[i] = m.mk_fresh_const(q->get_decl_name(i), s, false); if (m_model->has_uninterpreted_sort(s)) { restrict_to_universe(vars.get(i), m_model->get_universe(s)); } } var_subst subst(m); expr_ref body = subst(tmp, vars.size(), vars.c_ptr()); if (is_forall(q)) { body = m.mk_not(body); } m_solver->assert_expr(body); lbool r = m_solver->check_sat(0, nullptr); model_ref mdl; TRACE("smtfd", tout << "check: " << r << "\n";); if (r == l_true) { expr_ref qq(q->get_expr(), m); for (expr* t : subterms(qq)) { if (is_ground(t)) { expr_ref v = eval_abs(t); m_pinned.push_back(v); m_val2term.insert(v, m.get_sort(t), t); } } m_solver->get_model(mdl); TRACE("smtfd", tout << *mdl << "\n";); for (unsigned i = 0; i < sz; ++i) { app* v = to_app(vars.get(i)); func_decl* f = v->get_decl(); expr_ref val(mdl->get_some_const_interp(f), m); if (!val) { r = l_undef; break; } expr* t = nullptr; if (m_val2term.find(val, m.get_sort(v), t)) { val = t; } vals[i] = val; } } if (r == l_true) { body = subst(q->get_expr(), vals.size(), vals.c_ptr()); m_context.rewrite(body); TRACE("smtfd", tout << "vals: " << vals << "\n" << body << "\n";); if (is_forall(q)) { body = m.mk_implies(q, body); } else { body = m.mk_implies(body, q); } m_context.add(body, __FUNCTION__); } m_solver->pop(1); return r; } lbool check_exists(quantifier* q) { if (m_enforced.contains(q)) { return l_true; } expr_ref tmp(m); expr_ref_vector vars(m); unsigned sz = q->get_num_decls(); vars.resize(sz, nullptr); for (unsigned i = 0; i < sz; ++i) { vars[i] = m.mk_fresh_const(q->get_decl_name(i), q->get_decl_sort(i)); } var_subst subst(m); expr_ref body = subst(q->get_expr(), vars.size(), vars.c_ptr()); if (is_exists(q)) { body = m.mk_implies(q, body); } else { body = m.mk_implies(body, q); } m_enforced.insert(q); m_context.add(body, __FUNCTION__); return l_true; } void init_val2term(expr_ref_vector const& core) { for (expr* t : subterms(core)) { if (!m.is_bool(t) && is_ground(t)) { expr_ref v = eval_abs(t); m_pinned.push_back(v); m_val2term.insert(v, m.get_sort(t), t); } } } public: mbqi(::solver* s, plugin_context& c, obj_hashtable& enforced, model_ref& mdl): m(s->get_manager()), m_context(c), m_enforced(enforced), m_model(mdl), m_solver(s), m_pinned(m) {} bool check_quantifiers(expr_ref_vector const& core) { bool result = true; init_val2term(core); IF_VERBOSE(9, for (expr* c : core) { verbose_stream() << "core: " << mk_bounded_pp(c, m, 2) << "\n"; }); for (expr* c : core) { lbool r = l_false; if (is_forall(c)) { r = check_forall(to_quantifier(c)); } else if (is_exists(c)) { r = check_exists(to_quantifier(c)); } else if (m.is_not(c, c)) { if (is_forall(c)) { r = check_exists(to_quantifier(c)); } else if (is_exists(c)) { r = check_forall(to_quantifier(c)); } } if (r == l_undef) { result = false; } } return result; } }; struct stats { unsigned m_num_lemmas; unsigned m_num_rounds; unsigned m_num_mbqi; stats() { memset(this, 0, sizeof(stats)); } }; class solver : public solver_na2as { ast_manager& m; mutable smtfd_abs m_abs; plugin_context m_context; uf_plugin m_uf; ar_plugin m_ar; bv_plugin m_bv; basic_plugin m_bs; pb_plugin m_pb; ref<::solver> m_fd_sat_solver; ref<::solver> m_fd_core_solver; ref<::solver> m_smt_solver; ref m_mbqi_solver; expr_ref_vector m_assertions; unsigned_vector m_assertions_lim; unsigned m_assertions_qhead; expr_ref_vector m_axioms; expr_ref_vector m_toggles; unsigned_vector m_toggles_lim; model_ref m_model; std::string m_reason_unknown; stats m_stats; unsigned m_max_conflicts; obj_hashtable m_enforced_quantifier; void set_delay_simplify() { params_ref p; p.set_uint("simplify.delay", 10000); m_fd_sat_solver->updt_params(p); m_fd_core_solver->updt_params(p); } expr* add_toggle(expr* toggle) { m_toggles.push_back(abs(toggle)); return toggle; } bool is_toggle(expr* e) { return m_toggles.contains(e); } void flush_assertions() { SASSERT(m_assertions_qhead <= m_assertions.size()); unsigned sz = m_assertions.size() - m_assertions_qhead; if (sz > 0) { m_assertions.push_back(m_toggles.back()); expr_ref fml(m.mk_and(sz + 1, m_assertions.c_ptr() + m_assertions_qhead), m); m_assertions.pop_back(); expr* toggle = add_toggle(m.mk_fresh_const("toggle", m.mk_bool_sort())); m_assertions_qhead = m_assertions.size(); TRACE("smtfd", tout << "flush: " << m_assertions_qhead << " " << mk_bounded_pp(fml, m, 3) << "\n";); fml = abs(fml); m_fd_sat_solver->assert_expr(fml); fml = m.mk_not(m.mk_and(toggle, fml)); m_fd_core_solver->assert_expr(fml); flush_atom_defs(); } } lbool check_abs(unsigned num_assumptions, expr * const * assumptions) { expr_ref_vector asms(m); init_assumptions(num_assumptions, assumptions, asms); TRACE("smtfd", for (unsigned i = 0; i < num_assumptions; ++i) { tout << mk_bounded_pp(assumptions[i], m, 3) << "\n"; } display(tout << asms << "\n");); TRACE("smtfd_verbose", m_fd_sat_solver->display(tout);); lbool r = m_fd_sat_solver->check_sat(asms); update_reason_unknown(r, m_fd_sat_solver); set_delay_simplify(); return r; } // not necessarily prime lbool get_prime_implicate(unsigned num_assumptions, expr * const * assumptions, expr_ref_vector& core) { expr_ref_vector asms(m); m_fd_sat_solver->get_model(m_model); m_model->set_model_completion(true); init_model_assumptions(num_assumptions, assumptions, asms); TRACE("smtfd", display(tout << asms << "\n" << *m_model << "\n");); lbool r = m_fd_core_solver->check_sat(asms); update_reason_unknown(r, m_fd_core_solver); if (r == l_false) { m_fd_core_solver->get_unsat_core(core); TRACE("smtfd", display(tout << core << "\n");); SASSERT(asms.contains(m_toggles.back())); SASSERT(core.contains(m_toggles.back())); core.erase(m_toggles.back()); rep(core); } return r; } lbool check_smt(expr_ref_vector& core) { IF_VERBOSE(10, verbose_stream() << "core: " << core.size() << "\n"); params_ref p; p.set_uint("max_conflicts", m_max_conflicts); m_smt_solver->updt_params(p); lbool r = m_smt_solver->check_sat(core); TRACE("smtfd", tout << "core: " << core << "\nresult: " << r << "\n";); update_reason_unknown(r, m_smt_solver); switch (r) { case l_false: { unsigned sz0 = core.size(); m_smt_solver->get_unsat_core(core); TRACE("smtfd", display(tout << core);); unsigned sz1 = core.size(); if (sz1 < sz0) { m_max_conflicts += 10; } else { if (m_max_conflicts > 20) m_max_conflicts -= 5; } break; } case l_undef: if (m_max_conflicts > 20) m_max_conflicts -= 5; break; case l_true: m_smt_solver->get_model(m_model); break; } return r; } bool add_theory_axioms(expr_ref_vector const& core) { m_context.reset(m_model); expr_ref_vector terms(core); terms.append(m_axioms); for (unsigned round = 0; !m_context.at_max() && m_context.add_theory_axioms(terms, round); ++round) {} TRACE("smtfd", m_context.display(tout);); for (expr* f : m_context) { assert_fd(f); } m_stats.m_num_lemmas += m_context.size(); if (m_context.at_max()) { m_context.set_max_lemmas(3*m_context.get_max_lemmas()/2); } return !m_context.empty(); } lbool is_decided_sat(expr_ref_vector const& core) { bool has_q = false; lbool is_decided = l_true; m_context.reset(m_model); expr_ref_vector terms(core); terms.append(m_axioms); for (expr* t : subterms(core)) { if (is_forall(t) || is_exists(t)) { has_q = true; } } for (expr* t : subterms(terms)) { if (!is_forall(t) && !is_exists(t) && (!m_context.term_covered(t) || !m_context.sort_covered(m.get_sort(t)))) { is_decided = l_false; } } m_context.populate_model(m_model, terms); TRACE("smtfd", tout << "axioms: " << m_axioms << "\n"; for (expr* a : subterms(terms)) { expr_ref val0 = (*m_model)(a); expr_ref val1 = (*m_model)(abs(a)); if (val0 != val1 && m.get_sort(val0) == m.get_sort(val1)) { tout << mk_bounded_pp(a, m, 2) << " := " << val0 << " " << val1 << "\n"; } if (!is_forall(a) && !is_exists(a) && (!m_context.term_covered(a) || !m_context.sort_covered(m.get_sort(a)))) { tout << "not covered: " << mk_pp(a, m) << " " << mk_pp(m.get_sort(a), m) << " "; tout << m_context.term_covered(a) << " " << m_context.sort_covered(m.get_sort(a)) << "\n"; } } tout << "has quantifier: " << has_q << "\n" << core << "\n";); if (!has_q) { return is_decided; } if (!m_mbqi_solver) { m_mbqi_solver = alloc(solver, m, get_params()); } mbqi mb(m_mbqi_solver.get(), m_context, m_enforced_quantifier, m_model); if (!mb.check_quantifiers(core) && m_context.empty()) { return l_false; } for (expr* f : m_context) { IF_VERBOSE(10, verbose_stream() << "lemma: " << f->get_id() << ": " << expr_ref(f, m) << "\n"); assert_fd(f); } m_stats.m_num_mbqi += m_context.size(); IF_VERBOSE(10, verbose_stream() << "context size: " << m_context.size() << "\n"); return m_context.empty() ? is_decided : l_undef; } void init_assumptions(unsigned sz, expr* const* user_asms, expr_ref_vector& asms) { asms.reset(); for (unsigned i = 0; i < sz; ++i) { asms.push_back(abs_assumption(user_asms[i])); } flush_atom_defs(); } void init_model_assumptions(unsigned sz, expr* const* user_asms, expr_ref_vector& asms) { asms.reset(); asms.push_back(m_toggles.back()); for (unsigned i = 0; i < sz; ++i) { asms.push_back(abs(user_asms[i])); } for (expr* a : m_abs.atoms()) { if (is_toggle(a)) { } else if (m_model->is_true(a)) { asms.push_back(a); } else { asms.push_back(m.mk_not(a)); } } } void checkpoint() { if (m.canceled()) { throw tactic_exception(m.limit().get_cancel_msg()); } } expr* rep(expr* e) { return m_abs.rep(e); } expr* abs(expr* e) { return m_abs.abs(e); } expr* abs_assumption(expr* e) { return m_abs.abs_assumption(e); } expr_ref_vector& rep(expr_ref_vector& v) { for (unsigned i = v.size(); i-- > 0; ) v[i] = rep(v.get(i)); return v; } expr_ref_vector& abs(expr_ref_vector& v) { for (unsigned i = v.size(); i-- > 0; ) v[i] = abs(v.get(i)); return v; } void init() { m_axioms.reset(); if (!m_fd_sat_solver) { m_fd_sat_solver = mk_fd_solver(m, get_params()); m_fd_core_solver = mk_fd_solver(m, get_params()); m_smt_solver = mk_smt_solver(m, get_params(), symbol::null); m_smt_solver->updt_params(get_params()); } } std::ostream& display(std::ostream& out, unsigned n = 0, expr * const * assumptions = nullptr) const override { if (!m_fd_sat_solver) return out; // m_fd_sat_solver->display(out); // out << m_assumptions << "\n"; m_abs.display(out); return out; } void update_reason_unknown(lbool r, ::solver_ref& s) { if (r == l_undef) m_reason_unknown = s->reason_unknown(); } public: solver(ast_manager& m, params_ref const& p): solver_na2as(m), m(m), m_abs(m), m_context(m_abs, m), m_uf(m_context), m_ar(m_context), m_bv(m_context), m_bs(m_context), m_pb(m_context), m_assertions(m), m_assertions_qhead(0), m_axioms(m), m_toggles(m), m_max_conflicts(50) { updt_params(p); add_toggle(m.mk_true()); } ~solver() override {} ::solver* translate(ast_manager& dst_m, params_ref const& p) override { solver* result = alloc(solver, dst_m, p); if (m_smt_solver) result->m_smt_solver = m_smt_solver->translate(dst_m, p); if (m_fd_sat_solver) result->m_fd_sat_solver = m_fd_sat_solver->translate(dst_m, p); if (m_fd_core_solver) result->m_fd_core_solver = m_fd_core_solver->translate(dst_m, p); return result; } void assert_expr_core(expr* t) override { m_assertions.push_back(t); } void push_core() override { init(); flush_assertions(); m_abs.push(); m_fd_sat_solver->push(); m_fd_core_solver->push(); m_smt_solver->push(); m_assertions_lim.push_back(m_assertions.size()); m_toggles_lim.push_back(m_toggles.size()); } void pop_core(unsigned n) override { m_fd_sat_solver->pop(n); m_fd_core_solver->pop(n); m_smt_solver->pop(n); m_abs.pop(n); unsigned sz = m_toggles_lim[m_toggles_lim.size() - n]; m_toggles.shrink(sz); m_toggles_lim.shrink(m_toggles_lim.size() - n); m_assertions.shrink(m_assertions_lim[m_assertions_lim.size() - n]); m_assertions_lim.shrink(m_assertions_lim.size() - n); m_assertions_qhead = m_assertions.size(); } void flush_atom_defs() { CTRACE("smtfd", !m_abs.atom_defs().empty(), for (expr* f : m_abs.atom_defs()) tout << mk_bounded_pp(f, m, 4) << "\n";); for (expr* f : m_abs.atom_defs()) { m_fd_sat_solver->assert_expr(f); m_fd_core_solver->assert_expr(f); } m_abs.reset_atom_defs(); } void assert_fd(expr* fml) { expr_ref _fml(fml, m); TRACE("smtfd", tout << mk_bounded_pp(fml, m, 3) << "\n";); SASSERT(!m_axioms.contains(fml)); m_axioms.push_back(fml); _fml = abs(fml); TRACE("smtfd", tout << mk_bounded_pp(_fml, m, 3) << "\n";); m_fd_sat_solver->assert_expr(_fml); m_fd_core_solver->assert_expr(_fml); flush_atom_defs(); } void block_core(expr_ref_vector const& core) { expr_ref fml(m.mk_not(mk_and(core)), m); TRACE("smtfd", tout << "block:\n" << mk_bounded_pp(fml, m, 3) << "\n" << mk_bounded_pp(abs(fml), m, 3) << "\n";); assert_fd(fml); } lbool check_sat_core2(unsigned num_assumptions, expr * const * assumptions) override { init(); flush_assertions(); lbool r = l_undef; expr_ref_vector core(m), axioms(m); while (true) { IF_VERBOSE(1, verbose_stream() << "(smtfd-check-sat :rounds " << m_stats.m_num_rounds << " :lemmas " << m_stats.m_num_lemmas << " :qi " << m_stats.m_num_mbqi << ")\n"); m_stats.m_num_rounds++; checkpoint(); // phase 1: check sat of abs r = check_abs(num_assumptions, assumptions); if (r != l_true) { break; } // phase 2: find prime implicate over FD (abstraction) r = get_prime_implicate(num_assumptions, assumptions, core); if (r != l_false) { break; } // phase 3: check if prime implicate is really valid, or add theory lemmas until there is a theory core r = refine_core(core); switch (r) { case l_true: switch (is_decided_sat(core)) { case l_true: return l_true; case l_undef: break; case l_false: break; // disable for now r = check_smt(core); switch (r) { case l_true: return r; case l_false: block_core(core); break; case l_undef: break; } } break; case l_false: block_core(core); break; case l_undef: return r; } } return r; } lbool refine_core(expr_ref_vector & core) { lbool r = l_true; unsigned round = 0; m_context.reset(m_model); while (true) { expr_ref_vector terms(core); terms.append(m_axioms); if (!m_context.add_theory_axioms(terms, round)) { break; } if (m_context.empty()) { ++round; continue; } IF_VERBOSE(1, verbose_stream() << "(smtfd-round :round " << round << " :lemmas " << m_context.size() << ")\n"); round = 0; TRACE("smtfd_verbose", for (expr* f : m_context) tout << "refine " << mk_bounded_pp(f, m, 3) << "\n"; m_context.display(tout);); for (expr* f : m_context) { assert_fd(f); } m_stats.m_num_lemmas += m_context.size(); m_context.reset(m_model); r = check_abs(core.size(), core.c_ptr()); update_reason_unknown(r, m_fd_sat_solver); switch (r) { case l_false: m_fd_sat_solver->get_unsat_core(core); rep(core); return r; case l_true: m_fd_sat_solver->get_model(m_model); m_model->set_model_completion(true); m_context.reset(m_model); break; default: return r; } } // context is satisfiable SASSERT(r == l_true); return r; } void updt_params(params_ref const & p) override { ::solver::updt_params(p); if (m_fd_sat_solver) { m_fd_sat_solver->updt_params(p); m_fd_core_solver->updt_params(p); m_smt_solver->updt_params(p); } m_context.set_max_lemmas(UINT_MAX); // p.get_uint("max-lemmas", 100)); } void collect_param_descrs(param_descrs & r) override { init(); m_smt_solver->collect_param_descrs(r); m_fd_sat_solver->collect_param_descrs(r); r.insert("max-lemmas", CPK_UINT, "maximal number of lemmas per round", "10"); } void set_produce_models(bool f) override { } void set_progress_callback(progress_callback * callback) override { } void collect_statistics(statistics & st) const override { if (m_fd_sat_solver) { m_fd_sat_solver->collect_statistics(st); m_fd_core_solver->collect_statistics(st); m_smt_solver->collect_statistics(st); } st.update("smtfd-num-lemmas", m_stats.m_num_lemmas); st.update("smtfd-num-rounds", m_stats.m_num_rounds); st.update("smtfd-num-mbqi", m_stats.m_num_mbqi); } void get_unsat_core(expr_ref_vector & r) override { m_fd_sat_solver->get_unsat_core(r); rep(r); } void get_model_core(model_ref & mdl) override { mdl = m_model; } model_converter_ref get_model_converter() const override { SASSERT(m_smt_solver); return m_smt_solver->get_model_converter(); } proof * get_proof() override { return nullptr; } std::string reason_unknown() const override { return m_reason_unknown; } void set_reason_unknown(char const* msg) override { m_reason_unknown = msg; } void get_labels(svector & r) override { init(); m_smt_solver->get_labels(r); } ast_manager& get_manager() const override { return m; } lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes) override { return l_undef; } expr_ref_vector cube(expr_ref_vector& vars, unsigned backtrack_level) override { return expr_ref_vector(m); } lbool get_consequences_core(expr_ref_vector const& asms, expr_ref_vector const& vars, expr_ref_vector& consequences) override { return l_undef; } void get_levels(ptr_vector const& vars, unsigned_vector& depth) override { init(); m_smt_solver->get_levels(vars, depth); } expr_ref_vector get_trail() override { init(); return m_smt_solver->get_trail(); } unsigned get_num_assertions() const override { return m_assertions.size(); } expr * get_assertion(unsigned idx) const override { return m_assertions.get(idx); } }; } solver * mk_smtfd_solver(ast_manager & m, params_ref const & p) { return alloc(smtfd::solver, m, p); } tactic * mk_smtfd_tactic(ast_manager & m, params_ref const & p) { return mk_solver2tactic(mk_smtfd_solver(m, p)); } z3-z3-4.8.7/src/tactic/fd_solver/smtfd_solver.h000066400000000000000000000011141356505360400213250ustar00rootroot00000000000000/*++ Copyright (c) 2019 Microsoft Corporation Module Name: smtfd_solver.h Abstract: SMT reduction to Finite domain solver. Author: Nikolaj Bjorner (nbjorner) 2019-09-03 Notes: --*/ #ifndef SMTFD_SOLVER_H_ #define SMTFD_SOLVER_H_ #include "ast/ast.h" #include "util/params.h" class solver; class tactic; solver * mk_smtfd_solver(ast_manager & m, params_ref const & p); tactic * mk_smtfd_tactic(ast_manager & m, params_ref const & p); /* ADD_TACTIC("smtfd", "builtin strategy for solving SMT problems by reduction to FD.", "mk_smtfd_tactic(m, p)") */ #endif z3-z3-4.8.7/src/tactic/filter_model_converter.h000066400000000000000000000021121356505360400213660ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: filter_model_converter.h Abstract: Filter decls from a model Author: Leonardo (leonardo) 2011-05-06 Notes: --*/ #ifndef FILTER_MODEL_CONVERTER_H_ #define FILTER_MODEL_CONVERTER_H_ #include "tactic/model_converter.h" class filter_model_converter : public model_converter { func_decl_ref_vector m_decls; public: filter_model_converter(ast_manager & m):m_decls(m) {} ~filter_model_converter() override; ast_manager & m() const { return m_decls.get_manager(); } void operator()(model_ref & md, unsigned goal_idx) override; virtual void operator()(svector & labels, unsigned goal_idx); void operator()(model_ref & md) override { operator()(md, 0); } // TODO: delete void cancel() override {} void display(std::ostream & out) override; void insert(func_decl * d) { m_decls.push_back(d); } model_converter * translate(ast_translation & translator) override; }; typedef ref filter_model_converter_ref; #endif z3-z3-4.8.7/src/tactic/fpa/000077500000000000000000000000001356505360400152335ustar00rootroot00000000000000z3-z3-4.8.7/src/tactic/fpa/CMakeLists.txt000066400000000000000000000005331356505360400177740ustar00rootroot00000000000000z3_add_component(fpa_tactics SOURCES fpa2bv_model_converter.cpp fpa2bv_tactic.cpp qffp_tactic.cpp qffplra_tactic.cpp COMPONENT_DEPENDENCIES arith_tactics bv_tactics core_tactics fpa sat_tactic smtlogic_tactics smt_tactic TACTIC_HEADERS fpa2bv_tactic.h qffp_tactic.h qffplra_tactic.h ) z3-z3-4.8.7/src/tactic/fpa/fpa2bv_model_converter.cpp000066400000000000000000000057321356505360400223750ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: fpa2bv_model_converter.h Abstract: Model conversion for fpa2bv_converter Author: Christoph (cwinter) 2012-02-09 Notes: --*/ #include "ast/ast_smt2_pp.h" #include "ast/rewriter/fpa_rewriter.h" #include "tactic/fpa/fpa2bv_model_converter.h" void fpa2bv_model_converter::display(std::ostream & out) { out << "(fpa2bv-model-converter"; m_bv2fp->display(out); out << ")"; } model_converter * fpa2bv_model_converter::translate(ast_translation & translator) { fpa2bv_model_converter * res = alloc(fpa2bv_model_converter, translator.to()); res->m_bv2fp = m_bv2fp->translate(translator); return res; } void fpa2bv_model_converter::convert(model_core * mc, model * float_mdl) { TRACE("fpa2bv_mc", tout << "BV Model: " << std::endl; for (unsigned i = 0; i < mc->get_num_constants(); i++) tout << mc->get_constant(i)->get_name() << " --> " << mk_ismt2_pp(mc->get_const_interp(mc->get_constant(i)), m) << std::endl; for (unsigned i = 0; i < mc->get_num_functions(); i++) { func_decl * f = mc->get_function(i); tout << f->get_name() << "(...) := " << std::endl; func_interp * fi = mc->get_func_interp(f); for (unsigned j = 0; j < fi->num_entries(); j++) { func_entry const * fe = fi->get_entry(j); for (unsigned k = 0; k < f->get_arity(); k++) tout << mk_ismt2_pp(fe->get_arg(k), m) << " "; tout << "--> " << mk_ismt2_pp(fe->get_result(), m) << std::endl; } tout << "else " << mk_ismt2_pp(fi->get_else(), m) << std::endl; }); obj_hashtable seen; m_bv2fp->convert_consts(mc, float_mdl, seen); m_bv2fp->convert_rm_consts(mc, float_mdl, seen); m_bv2fp->convert_min_max_specials(mc, float_mdl, seen); m_bv2fp->convert_uf2bvuf(mc, float_mdl, seen); // Keep all the non-float constants. unsigned sz = mc->get_num_constants(); for (unsigned i = 0; i < sz; i++) { func_decl * c = mc->get_constant(i); if (!seen.contains(c)) float_mdl->register_decl(c, mc->get_const_interp(c)); } // And keep everything else sz = mc->get_num_functions(); for (unsigned i = 0; i < sz; i++) { func_decl * f = mc->get_function(i); if (!seen.contains(f)) { TRACE("fpa2bv_mc", tout << "Keeping: " << mk_ismt2_pp(f, m) << std::endl;); func_interp * val = mc->get_func_interp(f)->copy(); float_mdl->register_decl(f, val); } } sz = mc->get_num_uninterpreted_sorts(); for (unsigned i = 0; i < sz; i++) { sort * s = mc->get_uninterpreted_sort(i); ptr_vector u = mc->get_universe(s); float_mdl->register_usort(s, u.size(), u.c_ptr()); } } model_converter * mk_fpa2bv_model_converter(ast_manager & m, fpa2bv_converter & conv) { return alloc(fpa2bv_model_converter, m, conv); } z3-z3-4.8.7/src/tactic/fpa/fpa2bv_model_converter.h000066400000000000000000000023501356505360400220330ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: fpa2bv_model_converter.h Abstract: Model conversion for fpa2bv_converter Author: Christoph (cwinter) 2012-02-09 Notes: --*/ #ifndef FPA2BV_MODEL_CONVERTER_H_ #define FPA2BV_MODEL_CONVERTER_H_ #include "ast/fpa/fpa2bv_converter.h" #include "tactic/model_converter.h" #include "ast/fpa/bv2fpa_converter.h" class fpa2bv_model_converter : public model_converter { ast_manager & m; bv2fpa_converter * m_bv2fp; public: fpa2bv_model_converter(ast_manager & m, fpa2bv_converter & conv): m(m), m_bv2fp(alloc(bv2fpa_converter, m, conv)) { } ~fpa2bv_model_converter() override { dealloc(m_bv2fp); } void operator()(model_ref & md) override { model * new_model = alloc(model, m); convert(md.get(), new_model); md = new_model; } void display(std::ostream & out) override; model_converter * translate(ast_translation & translator) override; protected: fpa2bv_model_converter(ast_manager & m) : m(m), m_bv2fp(nullptr) {} void convert(model_core * mc, model * float_mdl); }; model_converter * mk_fpa2bv_model_converter(ast_manager & m, fpa2bv_converter & conv); #endif z3-z3-4.8.7/src/tactic/fpa/fpa2bv_tactic.cpp000066400000000000000000000115301356505360400204460ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: fpa2bv_tactic.cpp Abstract: Tactic that converts floating points to bit-vectors Author: Christoph (cwinter) 2012-02-09 Notes: --*/ #include "tactic/tactical.h" #include "ast/fpa/fpa2bv_rewriter.h" #include "tactic/core/simplify_tactic.h" #include "tactic/fpa/fpa2bv_tactic.h" #include "tactic/fpa/fpa2bv_model_converter.h" class fpa2bv_tactic : public tactic { struct imp { ast_manager & m; fpa2bv_converter m_conv; fpa2bv_rewriter m_rw; unsigned m_num_steps; bool m_proofs_enabled; bool m_produce_models; bool m_produce_unsat_cores; imp(ast_manager & _m, params_ref const & p): m(_m), m_conv(m), m_rw(m, m_conv, p), m_proofs_enabled(false), m_produce_models(false), m_produce_unsat_cores(false) { } 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()); m_proofs_enabled = g->proofs_enabled(); m_produce_models = g->models_enabled(); m_produce_unsat_cores = g->unsat_core_enabled(); result.reset(); tactic_report report("fpa2bv", *g); m_rw.reset(); TRACE("fpa2bv", tout << "BEFORE: " << std::endl; g->display(tout);); if (g->inconsistent()) { result.push_back(g.get()); return; } m_num_steps = 0; expr_ref new_curr(m); proof_ref new_pr(m); unsigned size = g->size(); for (unsigned idx = 0; idx < size; idx++) { if (g->inconsistent()) break; expr * curr = g->form(idx); m_rw(curr, new_curr, new_pr); m_num_steps += m_rw.get_num_steps(); if (m_proofs_enabled) { proof * pr = g->pr(idx); new_pr = m.mk_modus_ponens(pr, new_pr); } g->update(idx, new_curr, new_pr, g->dep(idx)); if (is_app(new_curr)) { const app * a = to_app(new_curr.get()); if (a->get_family_id() == m_conv.fu().get_family_id() && a->get_decl_kind() == OP_FPA_IS_NAN) { // Inject auxiliary lemmas that fix e to the one and only NaN value, // that is (= e (fp #b0 #b1...1 #b0...01)), so that the value propagation // has a value to propagate. expr_ref sgn(m), sig(m), exp(m); m_conv.split_fp(new_curr, sgn, exp, sig); result.back()->assert_expr(m.mk_eq(sgn, m_conv.bu().mk_numeral(0, 1))); result.back()->assert_expr(m.mk_eq(exp, m_conv.bu().mk_bv_neg(m_conv.bu().mk_numeral(1, m_conv.bu().get_bv_size(exp))))); result.back()->assert_expr(m.mk_eq(sig, m_conv.bu().mk_numeral(1, m_conv.bu().get_bv_size(sig)))); } } } if (g->models_enabled()) g->add(mk_fpa2bv_model_converter(m, m_conv)); g->inc_depth(); result.push_back(g.get()); for (unsigned i = 0; i < m_conv.m_extra_assertions.size(); i++) result.back()->assert_expr(m_conv.m_extra_assertions[i].get()); SASSERT(g->is_well_sorted()); TRACE("fpa2bv", tout << "AFTER: " << std::endl; g->display(tout); if (g->mc()) g->mc()->display(tout); tout << std::endl; ); } }; imp * m_imp; params_ref m_params; public: fpa2bv_tactic(ast_manager & m, params_ref const & p): m_params(p) { m_imp = alloc(imp, m, p); } tactic * translate(ast_manager & m) override { return alloc(fpa2bv_tactic, m, m_params); } ~fpa2bv_tactic() override { dealloc(m_imp); } void updt_params(params_ref const & p) override { m_params = p; m_imp->updt_params(p); } void collect_param_descrs(param_descrs & r) override { } void operator()(goal_ref const & in, goal_ref_buffer & result) override { try { (*m_imp)(in, result); } catch (rewriter_exception & ex) { throw tactic_exception(ex.msg()); } } void cleanup() override { imp * d = alloc(imp, m_imp->m, m_params); std::swap(d, m_imp); dealloc(d); } }; tactic * mk_fpa2bv_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(fpa2bv_tactic, m, p)); } z3-z3-4.8.7/src/tactic/fpa/fpa2bv_tactic.h000066400000000000000000000010001356505360400201020ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: fpa2bv_tactic.h Abstract: Tactic that converts floating points to bit-vectors Author: Christoph (cwinter) 2012-02-09 Notes: --*/ #ifndef FPA2BV_TACTIC_H_ #define FPA2BV_TACTIC_H_ #include "util/params.h" class ast_manager; class tactic; tactic * mk_fpa2bv_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("fpa2bv", "convert floating point numbers to bit-vectors.", "mk_fpa2bv_tactic(m, p)") */ #endif z3-z3-4.8.7/src/tactic/fpa/qffp_tactic.cpp000066400000000000000000000106101356505360400202200ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qffpa_tactic.cpp Abstract: Tactic for QF_FP benchmarks. Author: Christoph (cwinter) 2012-01-16 Notes: --*/ #include "tactic/tactical.h" #include "tactic/fpa/fpa2bv_tactic.h" #include "tactic/core/simplify_tactic.h" #include "tactic/core/propagate_values_tactic.h" #include "tactic/arith/probe_arith.h" #include "tactic/bv/bit_blaster_tactic.h" #include "tactic/smtlogics/qfnra_tactic.h" #include "sat/tactic/sat_tactic.h" #include "sat/sat_solver/inc_sat_solver.h" #include "smt/tactic/smt_tactic.h" #include "ackermannization/ackermannize_bv_tactic.h" #include "tactic/fpa/qffp_tactic.h" struct is_non_fp_qfnra_predicate { struct found {}; ast_manager & m; bv_util bu; fpa_util fu; arith_util au; is_non_fp_qfnra_predicate(ast_manager & _m) : m(_m), bu(m), fu(m), au(m) {} void operator()(var *) { throw found(); } void operator()(quantifier *) { throw found(); } void operator()(app * n) { family_id fid = n->get_family_id(); if (fid != null_family_id && fid != fu.get_family_id()) throw found(); sort * s = get_sort(n); if (fid == fu.get_family_id()) { if (!fu.is_float(s) && !fu.is_rm(s) && to_app(n)->get_decl_kind() != OP_FPA_TO_REAL) throw found(); } else if (fid == null_family_id) { if (!fu.is_float(s) && !fu.is_rm(s) && !m.is_bool(s)) throw found(); } else if (fid == m.get_basic_family_id()) return; else throw found(); } }; class is_fp_qfnra_probe : public probe { public: result operator()(goal const & g) override { return !test(g); } ~is_fp_qfnra_probe() override {} }; probe * mk_is_fp_qfnra_probe() { return alloc(is_fp_qfnra_probe); } tactic * mk_qffp_tactic(ast_manager & m, params_ref const & p) { params_ref simp_p = p; simp_p.set_bool("arith_lhs", true); simp_p.set_bool("elim_and", true); tactic * preamble = and_then(mk_simplify_tactic(m, simp_p), mk_propagate_values_tactic(m, p), mk_fpa2bv_tactic(m, p), mk_propagate_values_tactic(m, p), using_params(mk_simplify_tactic(m, p), simp_p), if_no_proofs(if_no_unsat_cores(mk_ackermannize_bv_tactic(m, p)))); tactic * st = and_then(preamble, mk_bit_blaster_tactic(m, p), using_params(mk_simplify_tactic(m, p), simp_p), cond(mk_is_propositional_probe(), cond(mk_produce_proofs_probe(), mk_smt_tactic(m, p), // `sat' does not support proofs. mk_psat_tactic(m, p)), cond(mk_is_fp_qfnra_probe(), mk_qfnra_tactic(m, p), mk_smt_tactic(m, p)))); st->updt_params(p); return st; } tactic * mk_qffpbv_tactic(ast_manager & m, params_ref const & p) { return mk_qffp_tactic(m, p); } struct is_non_qffp_predicate { struct found {}; ast_manager & m; bv_util bu; fpa_util fu; arith_util au; is_non_qffp_predicate(ast_manager & _m) : m(_m), bu(m), fu(m), au(m) {} void operator()(var *) { throw found(); } void operator()(quantifier *) { throw found(); } void operator()(app * n) { sort * s = get_sort(n); if (!m.is_bool(s) && !fu.is_float(s) && !fu.is_rm(s) && !bu.is_bv_sort(s) && !au.is_real(s)) throw found(); family_id fid = n->get_family_id(); if (fid == m.get_basic_family_id()) return; if (fid == fu.get_family_id() || fid == bu.get_family_id()) return; if (is_uninterp_const(n)) return; if (au.is_real(s) && au.is_numeral(n)) return; throw found(); } }; class is_qffp_probe : public probe { public: result operator()(goal const & g) override { return !test(g); } ~is_qffp_probe() override {} }; probe * mk_is_qffp_probe() { return alloc(is_qffp_probe); } probe * mk_is_qffpbv_probe() { return alloc(is_qffp_probe); } z3-z3-4.8.7/src/tactic/fpa/qffp_tactic.h000066400000000000000000000016541356505360400176750ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qffp_tactic.h Abstract: Tactic for QF_FP benchmarks. Author: Christoph (cwinter) 2012-01-16 Notes: --*/ #ifndef QFFP_TACTIC_H_ #define QFFP_TACTIC_H_ #include "util/params.h" class ast_manager; class tactic; tactic * mk_qffp_tactic(ast_manager & m, params_ref const & p = params_ref()); tactic * mk_qffpbv_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("qffp", "(try to) solve goal using the tactic for QF_FP.", "mk_qffp_tactic(m, p)") ADD_TACTIC("qffpbv", "(try to) solve goal using the tactic for QF_FPBV (floats+bit-vectors).", "mk_qffpbv_tactic(m, p)") */ probe * mk_is_qffp_probe(); probe * mk_is_qffpbv_probe(); /* ADD_PROBE("is-qffp", "true if the goal is in QF_FP (floats).", "mk_is_qffp_probe()") ADD_PROBE("is-qffpbv", "true if the goal is in QF_FPBV (floats+bit-vectors).", "mk_is_qffpbv_probe()") */ #endif z3-z3-4.8.7/src/tactic/fpa/qffplra_tactic.cpp000066400000000000000000000037101356505360400207220ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qffpalra_tactic.cpp Abstract: Tactic for QF_FPLRA benchmarks. Author: Christoph (cwinter) 2018-04-24 Notes: --*/ #include "tactic/tactical.h" #include "tactic/fpa/qffp_tactic.h" #include "tactic/fpa/qffplra_tactic.h" tactic * mk_qffplra_tactic(ast_manager & m, params_ref const & p) { tactic * st = mk_qffp_tactic(m, p); st->updt_params(p); return st; } struct is_fpa_function { struct found {}; ast_manager & m; fpa_util fu; is_fpa_function(ast_manager & _m) : m(_m), fu(m) {} void operator()(var *) { } void operator()(quantifier *) { } void operator()(app * n) { if (n->get_family_id() == fu.get_family_id()) throw found(); } }; struct is_non_qffplra_predicate { struct found {}; ast_manager & m; bv_util bu; fpa_util fu; arith_util au; is_non_qffplra_predicate(ast_manager & _m) : m(_m), bu(m), fu(m), au(m) {} void operator()(var *) { throw found(); } void operator()(quantifier *) { throw found(); } void operator()(app * n) { sort * s = get_sort(n); if (!m.is_bool(s) && !fu.is_float(s) && !fu.is_rm(s) && !bu.is_bv_sort(s) && !au.is_real(s)) throw found(); family_id fid = n->get_family_id(); if (fid == m.get_basic_family_id() || fid == fu.get_family_id() || fid == bu.get_family_id() || fid == au.get_family_id()) return; if (is_uninterp_const(n)) return; if (au.is_real(s)) return; throw found(); } }; class is_qffplra_probe : public probe { public: result operator()(goal const & g) override { return test(g) && !test(g); } ~is_qffplra_probe() override {} }; probe * mk_is_qffplra_probe() { return alloc(is_qffplra_probe); } z3-z3-4.8.7/src/tactic/fpa/qffplra_tactic.h000066400000000000000000000011751356505360400203720ustar00rootroot00000000000000#pragma once /*++ Copyright (c) 2012 Microsoft Corporation Module Name: qffplra_tactic.h Abstract: Tactic for QF_FPLRA benchmarks. Author: Christoph (cwinter) 2018-04-24 Notes: --*/ #ifndef QFFPLRA_TACTIC_H_ #define QFFPLRA_TACTIC_H_ #include "util/params.h" class ast_manager; class tactic; tactic * mk_qffplra_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("qffplra", "(try to) solve goal using the tactic for QF_FPLRA.", "mk_qffplra_tactic(m, p)") */ probe * mk_is_qffplra_probe(); /* ADD_PROBE("is-qffplra", "true if the goal is in QF_FPLRA.", "mk_is_qffplra_probe()") */ #endif z3-z3-4.8.7/src/tactic/generic_model_converter.cpp000066400000000000000000000200721356505360400220550ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: generic_model_converter.cpp Abstract: Generic model converter that hides and adds entries. It subsumes filter_model_converter and extension_model_converter. Author: Nikolaj Bjorner (nbjorner) 2017-10-29 Notes: --*/ #include "ast/ast_pp.h" #include "ast/for_each_expr.h" #include "ast/ast_util.h" #include "ast/occurs.h" #include "ast/rewriter/expr_safe_replace.h" #include "ast/rewriter/th_rewriter.h" #include "tactic/generic_model_converter.h" #include "model/model_v2_pp.h" #include "model/model_evaluator.h" generic_model_converter::~generic_model_converter() { } void generic_model_converter::add(func_decl * d, expr* e) { VERIFY(e); VERIFY(d->get_range() == m.get_sort(e)); m_first_idx.insert_if_not_there(d, m_entries.size()); m_entries.push_back(entry(d, e, m, ADD)); } void generic_model_converter::operator()(model_ref & md) { TRACE("model_converter", tout << "before generic_model_converter\n"; model_v2_pp(tout, *md); display(tout);); model_evaluator ev(*(md.get())); ev.set_model_completion(true); ev.set_expand_array_equalities(false); expr_ref val(m); unsigned arity; bool reset_ev = false; for (unsigned i = m_entries.size(); i-- > 0; ) { entry const& e = m_entries[i]; switch (e.m_instruction) { case instruction::HIDE: md->unregister_decl(e.m_f); break; case instruction::ADD: ev(e.m_def, val); TRACE("model_converter", tout << e.m_f->get_name() << " ->\n" << e.m_def << "\n==>\n" << val << "\n";); arity = e.m_f->get_arity(); reset_ev = false; if (arity == 0) { expr* old_val = md->get_const_interp(e.m_f); if (old_val && old_val == val) { // skip } else { reset_ev = old_val != nullptr; md->register_decl(e.m_f, val); } } else { func_interp * old_val = md->get_func_interp(e.m_f); if (old_val && old_val->get_else() == val) { // skip } else { reset_ev = old_val != nullptr; func_interp * new_fi = alloc(func_interp, m, arity); new_fi->set_else(val); md->register_decl(e.m_f, new_fi); } } if (reset_ev) { ev.reset(); ev.set_model_completion(true); ev.set_expand_array_equalities(false); } break; } } TRACE("model_converter", tout << "after generic_model_converter\n"; model_v2_pp(tout, *md);); } void generic_model_converter::display(std::ostream & out) { for (entry const& e : m_entries) { switch (e.m_instruction) { case instruction::HIDE: display_del(out, e.m_f); break; case instruction::ADD: display_add(out, m, e.m_f, e.m_def); break; } } } model_converter * generic_model_converter::translate(ast_translation & translator) { ast_manager& to = translator.to(); generic_model_converter * res = alloc(generic_model_converter, to, m_orig.c_str()); for (entry const& e : m_entries) { res->m_entries.push_back(entry(translator(e.m_f.get()), translator(e.m_def.get()), to, e.m_instruction)); } return res; } void generic_model_converter::set_env(ast_pp_util* visitor) { if (!visitor) { m_env = nullptr; } else { m_env = &visitor->env(); for (entry const& e : m_entries) { visitor->coll.visit_func(e.m_f); if (e.m_def) visitor->coll.visit(e.m_def); } } } struct min_app_idx_proc { unsigned m_min; obj_map& m_idxs; min_app_idx_proc(obj_map& idxs) : m_min(UINT_MAX), m_idxs(idxs) {} void operator()(app * n) { unsigned idx; if (m_idxs.find(n->get_decl(), idx)) { m_min = std::min(m_min, idx); } } void operator()(var * n) {} void operator()(quantifier * n) {} }; void generic_model_converter::operator()(expr_ref& fml) { min_app_idx_proc min_proc(m_first_idx); for_each_expr(min_proc, fml); unsigned min_idx = min_proc.m_min; if (min_idx == UINT_MAX) return; expr_ref_vector fmls(m); fmls.push_back(fml); for (unsigned i = m_entries.size(); i-- > min_idx;) { entry const& e = m_entries[i]; if (e.m_instruction != instruction::ADD) { continue; } unsigned arity = e.m_f->get_arity(); if (arity == 0) { fmls.push_back(simplify_def(e)); } else { expr_ref_vector args(m); sort_ref_vector sorts(m); svector names; for (unsigned i = 0; i < arity; ++i) { sorts.push_back(e.m_f->get_domain(i)); names.push_back(symbol(i)); args.push_back(m.mk_var(i, sorts.back())); } // TBD: check if order is correct with respect to quantifier binding ordering expr_ref lhs(m.mk_app(e.m_f, arity, args.c_ptr()), m); expr_ref body(m.mk_eq(lhs, e.m_def), m); fmls.push_back(m.mk_forall(arity, sorts.c_ptr(), names.c_ptr(), body)); } if (m_first_idx[e.m_f] == i) { m_first_idx.remove(e.m_f); } } unsigned j = min_idx; for (unsigned i = min_idx; i < m_entries.size(); ++i) { entry& e = m_entries[i]; if (e.m_instruction == instruction::HIDE) { if (i != j) { m_entries[j] = e; } ++j; } } m_entries.shrink(j); fml = mk_and(fmls); } void generic_model_converter::get_units(obj_map& units) { th_rewriter rw(m); expr_safe_replace rep(m); expr_ref tmp(m); for (auto const& kv : units) { rep.insert(kv.m_key, kv.m_value ? m.mk_true() : m.mk_false()); } for (unsigned i = m_entries.size(); i-- > 0;) { entry const& e = m_entries[i]; switch (e.m_instruction) { case HIDE: tmp = m.mk_const(e.m_f); if (units.contains(tmp)) { m.dec_ref(tmp); units.remove(tmp); } break; case ADD: if (e.m_f->get_arity() == 0 && m.is_bool(e.m_f->get_range())) { tmp = m.mk_const(e.m_f); if (units.contains(tmp)) { break; } tmp = e.m_def; rep(tmp); rw(tmp); if (m.is_true(tmp)) { tmp = m.mk_const(e.m_f); m.inc_ref(tmp); units.insert(tmp, true); rep.insert(tmp, m.mk_true()); } else if (m.is_false(tmp)) { tmp = m.mk_const(e.m_f); m.inc_ref(tmp); units.insert(tmp, false); rep.insert(tmp, m.mk_false()); } } break; } } } /* \brief simplify definition expansion from model converter in the case they come from blocked clauses. In this case the definitions are of the form: x <=> x or not (C) or dually, x <=> not (not x or not C) in either case the definitions simplify to x or C */ expr_ref generic_model_converter::simplify_def(entry const& e) { expr_ref c(m.mk_const(e.m_f), m); if (m.is_bool(c) && occurs(c, e.m_def)) { expr_safe_replace rep(m); expr_ref result1 = e.m_def; expr_ref result2 = e.m_def; rep.apply_substitution(c, m.mk_true(), result1); rep.apply_substitution(c, m.mk_false(), result2); th_rewriter rw(m); expr_ref result(m.mk_and(m.mk_implies(result2, c), m.mk_implies(c, result1)), m); rw(result); return result; } else { return expr_ref(m.mk_eq(c, e.m_def), m); } } z3-z3-4.8.7/src/tactic/generic_model_converter.h000066400000000000000000000035541356505360400215300ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: generic_model_converter.h Abstract: Generic model converter that hides and adds entries. It subsumes filter_model_converter and extension_model_converter. Author: Nikolaj Bjorner (nbjorner) 2017-10-29 Notes: --*/ #ifndef GENERIC_MODEL_CONVERTER_H_ #define GENERIC_MODEL_CONVERTER_H_ #include "tactic/model_converter.h" class generic_model_converter : public model_converter { enum instruction { HIDE, ADD }; struct entry { func_decl_ref m_f; expr_ref m_def; instruction m_instruction; entry(func_decl* f, expr* d, ast_manager& m, instruction i): m_f(f, m), m_def(d, m), m_instruction(i) {} }; ast_manager& m; std::string m_orig; vector m_entries; obj_map m_first_idx; expr_ref simplify_def(entry const& e); public: generic_model_converter(ast_manager & m, char const* orig) : m(m), m_orig(orig) {} ~generic_model_converter() override; void hide(expr* e) { SASSERT(is_app(e) && to_app(e)->get_num_args() == 0); hide(to_app(e)->get_decl()); } void hide(func_decl * f) { m_entries.push_back(entry(f, nullptr, m, HIDE)); } void add(func_decl * d, expr* e); void add(expr * d, expr* e) { SASSERT(is_app(d) && to_app(d)->get_num_args() == 0); add(to_app(d)->get_decl(), e); } void operator()(labels_vec & labels) override {} void operator()(model_ref & md) override; void cancel() override {} void display(std::ostream & out) override; model_converter * translate(ast_translation & translator) override; void set_env(ast_pp_util* visitor) override; void operator()(expr_ref& fml) override; void get_units(obj_map& units) override; }; typedef ref generic_model_converter_ref; #endif z3-z3-4.8.7/src/tactic/goal.cpp000066400000000000000000000456401356505360400161240ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: goal.cpp Abstract: Proof / Model finding Goals Author: Leonardo de Moura (leonardo) 2011-10-12 Revision History: --*/ #include "ast/ast_ll_pp.h" #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) { if (p1 == PRECISE) return p2; if (p2 == PRECISE) return p1; if (p1 != p2) return UNDER_OVER; return p1; } std::ostream & operator<<(std::ostream & out, goal::precision p) { switch (p) { case goal::PRECISE: out << "precise"; break; case goal::UNDER: out << "under"; break; case goal::OVER: out << "over"; break; case goal::UNDER_OVER: out << "under-over"; break; } return out; } goal::goal(ast_manager & m, bool models_enabled, bool core_enabled): m_manager(m), m_ref_count(0), m_depth(0), m_models_enabled(models_enabled), m_proofs_enabled(m.proofs_enabled()), m_core_enabled(core_enabled), m_inconsistent(false), m_precision(PRECISE) { } goal::goal(ast_manager & m, bool proofs_enabled, bool models_enabled, bool core_enabled): m_manager(m), m_ref_count(0), m_depth(0), m_models_enabled(models_enabled), m_proofs_enabled(proofs_enabled), m_core_enabled(core_enabled), m_inconsistent(false), m_precision(PRECISE) { SASSERT(!proofs_enabled || m.proofs_enabled()); } goal::goal(goal const & src): m_manager(src.m()), m_ref_count(0), m_depth(0), m_models_enabled(src.models_enabled()), m_proofs_enabled(src.proofs_enabled()), m_core_enabled(src.unsat_core_enabled()), m_inconsistent(false), m_precision(PRECISE) { copy_from(src); } // Copy configuration: depth, models/proofs/cores flags, and precision from src. // The assertions are not copied goal::goal(goal const & src, bool): m_manager(src.m()), m_ref_count(0), m_depth(src.m_depth), m_models_enabled(src.models_enabled()), m_proofs_enabled(src.proofs_enabled()), m_core_enabled(src.unsat_core_enabled()), m_inconsistent(false), m_precision(src.m_precision) { m_mc = src.m_mc.get(); m_pc = src.m_pc.get(); m_dc = src.m_dc.get(); } goal::~goal() { reset_core(); } void goal::copy_to(goal & target) const { SASSERT(&m_manager == &(target.m_manager)); if (this == &target) return; m().copy(m_forms, target.m_forms); m().copy(m_proofs, target.m_proofs); m().copy(m_dependencies, target.m_dependencies); target.m_depth = std::max(m_depth, target.m_depth); SASSERT(target.m_proofs_enabled == m_proofs_enabled); SASSERT(target.m_core_enabled == m_core_enabled); target.m_inconsistent = m_inconsistent; target.m_precision = mk_union(prec(), target.prec()); target.m_mc = m_mc.get(); target.m_pc = m_pc.get(); target.m_dc = m_dc.get(); } void goal::push_back(expr * f, proof * pr, expr_dependency * d) { if (m().is_true(f)) return; if (m().is_false(f)) { // Make sure pr and d are not deleted by the m().del(...) statements. proof_ref saved_pr(m()); expr_dependency_ref saved_d(m()); saved_pr = pr; saved_d = d; m().del(m_forms); m().del(m_proofs); m().del(m_dependencies); m_inconsistent = true; m().push_back(m_forms, m().mk_false()); m().push_back(m_proofs, saved_pr); if (unsat_core_enabled()) m().push_back(m_dependencies, saved_d); } else { SASSERT(!m_inconsistent); m().push_back(m_forms, f); m().push_back(m_proofs, pr); if (unsat_core_enabled()) m().push_back(m_dependencies, d); } } void goal::quick_process(bool save_first, expr_ref& f, expr_dependency * d) { expr* g = nullptr; if (!m().is_and(f) && !(m().is_not(f, g) && m().is_or(g))) { if (!save_first) { push_back(f, nullptr, d); } return; } typedef std::pair expr_pol; sbuffer todo; expr_ref_vector tmp_exprs(m()); todo.push_back(expr_pol(f, true)); while (!todo.empty()) { if (m_inconsistent) return; expr_pol p = todo.back(); expr * curr = p.first; bool pol = p.second; todo.pop_back(); if (pol && m().is_and(curr)) { app * t = to_app(curr); unsigned i = t->get_num_args(); while (i > 0) { --i; todo.push_back(expr_pol(t->get_arg(i), true)); } } else if (!pol && m().is_or(curr)) { app * t = to_app(curr); unsigned i = t->get_num_args(); while (i > 0) { --i; todo.push_back(expr_pol(t->get_arg(i), false)); } } else if (m().is_not(curr, g)) { todo.push_back(expr_pol(g, !pol)); } else { if (!pol) { curr = m().mk_not(curr); tmp_exprs.push_back(curr); } if (save_first) { f = curr; save_first = false; } else { push_back(curr, nullptr, d); } } } } void goal::process_and(bool save_first, app * f, proof * pr, expr_dependency * d, expr_ref & out_f, proof_ref & out_pr) { unsigned num = f->get_num_args(); for (unsigned i = 0; i < num; i++) { if (m_inconsistent) return; slow_process(save_first && i == 0, f->get_arg(i), m().mk_and_elim(pr, i), d, out_f, out_pr); } } void goal::process_not_or(bool save_first, app * f, proof * pr, expr_dependency * d, expr_ref & out_f, proof_ref & out_pr) { unsigned num = f->get_num_args(); for (unsigned i = 0; i < num; i++) { if (m_inconsistent) return; expr * child = f->get_arg(i); if (m().is_not(child)) { expr * not_child = to_app(child)->get_arg(0); slow_process(save_first && i == 0, not_child, m().mk_not_or_elim(pr, i), d, out_f, out_pr); } else { expr_ref not_child(m()); not_child = m().mk_not(child); slow_process(save_first && i == 0, not_child, m().mk_not_or_elim(pr, i), d, out_f, out_pr); } } } void goal::slow_process(bool save_first, expr * f, proof * pr, expr_dependency * d, expr_ref & out_f, proof_ref & out_pr) { if (m().is_and(f)) process_and(save_first, to_app(f), pr, d, out_f, out_pr); else if (m().is_not(f) && m().is_or(to_app(f)->get_arg(0))) process_not_or(save_first, to_app(to_app(f)->get_arg(0)), pr, d, out_f, out_pr); else if (save_first) { out_f = f; out_pr = pr; } else { push_back(f, pr, d); } } void goal::slow_process(expr * f, proof * pr, expr_dependency * d) { expr_ref out_f(m()); proof_ref out_pr(m()); slow_process(false, f, pr, d, out_f, out_pr); } void goal::assert_expr(expr * f, proof * pr, expr_dependency * d) { expr_ref _f(f, m()); proof_ref _pr(pr, m()); expr_dependency_ref _d(d, m()); if (m_inconsistent) { return; } if (pr) { slow_process(f, pr, d); } else { expr_ref fr(f, m()); quick_process(false, fr, d); } } 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) const { unsigned sz = size(); for (unsigned i = 0; i < sz; i++) { result.push_back(form(i)); } } void goal::get_formulas(expr_ref_vector & result) const { unsigned sz = size(); for (unsigned i = 0; i < sz; i++) { result.push_back(form(i)); } } void goal::update(unsigned i, expr * f, proof * pr, expr_dependency * d) { if (m_inconsistent) return; if (pr) { expr_ref out_f(m()); proof_ref out_pr(m()); slow_process(true, f, pr, d, out_f, out_pr); if (!m_inconsistent) { if (m().is_false(out_f)) { push_back(out_f, out_pr, d); } else { m().set(m_forms, i, out_f); m().set(m_proofs, i, out_pr); if (unsat_core_enabled()) m().set(m_dependencies, i, d); } } } else { expr_ref fr(f, m()); quick_process(true, fr, d); if (!m_inconsistent) { if (m().is_false(fr)) { push_back(f, nullptr, d); } else { m().set(m_forms, i, fr); if (unsat_core_enabled()) m().set(m_dependencies, i, d); } } } } void goal::reset_core() { m().del(m_forms); m().del(m_proofs); m().del(m_dependencies); } void goal::reset_all() { reset_core(); m_depth = 0; m_inconsistent = false; m_precision = PRECISE; } void goal::reset() { reset_core(); m_inconsistent = false; } void goal::display(ast_printer & prn, std::ostream & out) const { out << "(goal"; unsigned sz = size(); for (unsigned i = 0; i < sz; i++) { out << "\n "; prn.display(out, form(i), 2); } out << "\n :precision " << prec() << " :depth " << depth() << ")" << std::endl; } void goal::display_with_dependencies(ast_printer & prn, std::ostream & out) const { ptr_vector deps; obj_hashtable to_pp; out << "(goal"; unsigned sz = size(); for (unsigned i = 0; i < sz; i++) { out << "\n |-"; deps.reset(); m().linearize(dep(i), deps); for (expr * d : deps) { if (is_uninterp_const(d)) { out << " " << mk_ismt2_pp(d, m()); } else { out << " #" << d->get_id(); to_pp.insert(d); } } out << "\n "; prn.display(out, form(i), 2); } if (!to_pp.empty()) { out << "\n :dependencies-definitions ("; for (expr* d : to_pp) { out << "\n (#" << d->get_id() << "\n "; prn.display(out, d, 2); out << ")"; } out << ")"; } out << "\n :precision " << prec() << " :depth " << depth() << ")" << std::endl; } void goal::display_with_dependencies(std::ostream & out) const { ptr_vector deps; out << "(goal"; unsigned sz = size(); for (unsigned i = 0; i < sz; i++) { out << "\n |-"; deps.reset(); m().linearize(dep(i), deps); for (expr * d : deps) { if (is_uninterp_const(d)) { out << " " << mk_ismt2_pp(d, m()); } else { out << " #" << d->get_id(); } } out << "\n " << mk_ismt2_pp(form(i), m(), 2); } out << "\n :precision " << prec() << " :depth " << depth() << ")" << std::endl; } void goal::display(ast_printer_context & ctx) const { display(ctx, ctx.regular_stream()); } void goal::display_with_dependencies(ast_printer_context & ctx) const { display_with_dependencies(ctx, ctx.regular_stream()); } void goal::display(std::ostream & out) const { out << "(goal"; unsigned sz = size(); for (unsigned i = 0; i < sz; i++) { out << "\n "; out << mk_ismt2_pp(form(i), m(), 2); } out << ")" << std::endl; } void goal::display_as_and(std::ostream & out) const { ptr_buffer args; unsigned sz = size(); for (unsigned i = 0; i < sz; i++) args.push_back(form(i)); expr_ref tmp(m()); tmp = m().mk_and(args.size(), args.c_ptr()); out << mk_ismt2_pp(tmp, m()) << "\n"; } void goal::display_ll(std::ostream & out) const { unsigned sz = size(); for (unsigned i = 0; i < sz; i++) { out << mk_ll_pp(form(i), m()) << "\n"; } } /** \brief Assumes that the formula is already in CNF. */ void goal::display_dimacs(std::ostream & out) const { expr_ref_vector fmls(m()); get_formulas(fmls); ::display_dimacs(out, fmls); } unsigned goal::num_exprs() const { expr_fast_mark1 visited; unsigned sz = size(); unsigned r = 0; for (unsigned i = 0; i < sz; i++) { r += get_num_exprs(form(i), visited); } return r; } void goal::shrink(unsigned j) { SASSERT(j <= size()); unsigned sz = size(); for (unsigned i = j; i < sz; i++) m().pop_back(m_forms); for (unsigned i = j; i < sz; i++) m().pop_back(m_proofs); if (unsat_core_enabled()) for (unsigned i = j; i < sz; i++) m().pop_back(m_dependencies); } /** \brief Eliminate true formulas. */ void goal::elim_true() { unsigned sz = size(); unsigned j = 0; for (unsigned i = 0; i < sz; i++) { expr * f = form(i); if (m().is_true(f)) continue; if (i == j) { j++; continue; } m().set(m_forms, j, f); m().set(m_proofs, j, m().get(m_proofs, i)); if (unsat_core_enabled()) m().set(m_dependencies, j, m().get(m_dependencies, i)); j++; } shrink(j); } /** \brief Return the position of formula f in the goal. Return UINT_MAX if f is not in the goal */ unsigned goal::get_idx(expr * f) const { unsigned sz = size(); for (unsigned j = 0; j < sz; j++) { if (form(j) == f) return j; } return UINT_MAX; } /** \brief Return the position of formula (not f) in the goal. Return UINT_MAX if (not f) is not in the goal */ unsigned goal::get_not_idx(expr * f) const { expr * atom; unsigned sz = size(); for (unsigned j = 0; j < sz; j++) { if (m().is_not(form(j), atom) && atom == f) return j; } return UINT_MAX; } void goal::elim_redundancies() { if (inconsistent()) return; expr_ref_fast_mark1 neg_lits(m()); expr_ref_fast_mark2 pos_lits(m()); unsigned sz = size(); unsigned j = 0; for (unsigned i = 0; i < sz; i++) { expr * f = form(i); if (m().is_true(f)) continue; if (m().is_not(f)) { expr * atom = to_app(f)->get_arg(0); if (neg_lits.is_marked(atom)) continue; if (pos_lits.is_marked(atom)) { proof * p = nullptr; if (proofs_enabled()) { proof * prs[2] = { pr(get_idx(atom)), pr(i) }; p = m().mk_unit_resolution(2, prs); } expr_dependency_ref d(m()); if (unsat_core_enabled()) d = m().mk_join(dep(get_idx(atom)), dep(i)); push_back(m().mk_false(), p, d); return; } neg_lits.mark(atom); } else { if (pos_lits.is_marked(f)) continue; if (neg_lits.is_marked(f)) { proof * p = nullptr; if (proofs_enabled()) { proof * prs[2] = { pr(get_not_idx(f)), pr(i) }; p = m().mk_unit_resolution(2, prs); } expr_dependency_ref d(m()); if (unsat_core_enabled()) d = m().mk_join(dep(get_not_idx(f)), dep(i)); push_back(m().mk_false(), p, d); return; } pos_lits.mark(f); } if (i == j) { j++; continue; } m().set(m_forms, j, f); m().set(m_proofs, j, pr(i)); if (unsat_core_enabled()) m().set(m_dependencies, j, dep(i)); j++; } shrink(j); } bool goal::is_well_sorted() const { unsigned sz = size(); for (unsigned i = 0; i < sz; i++) { expr * t = form(i); if (!::is_well_sorted(m(), t)) return false; } return true; } /** \brief Translate the assertion set to a new one that uses a different ast_manager. */ goal * goal::translate(ast_translation & translator) const { expr_dependency_translation dep_translator(translator); ast_manager & m_to = translator.to(); goal * res = alloc(goal, m_to, m_to.proofs_enabled() && proofs_enabled(), models_enabled(), unsat_core_enabled()); unsigned sz = m().size(m_forms); for (unsigned i = 0; i < sz; i++) { res->m().push_back(res->m_forms, translator(m().get(m_forms, i))); res->m().push_back(res->m_proofs, translator(m().get(m_proofs, i))); if (res->unsat_core_enabled()) res->m().push_back(res->m_dependencies, dep_translator(m().get(m_dependencies, i))); } res->m_inconsistent = m_inconsistent; res->m_depth = m_depth; res->m_precision = m_precision; res->m_pc = m_pc ? m_pc->translate(translator) : nullptr; res->m_mc = m_mc ? m_mc->translate(translator) : nullptr; res->m_dc = m_dc ? m_dc->translate(translator) : nullptr; return res; } bool goal::sat_preserved() const { return prec() == PRECISE || prec() == UNDER; } bool goal::unsat_preserved() const { return prec() == PRECISE || prec() == OVER; } bool goal::is_decided_sat() const { return size() == 0 && sat_preserved(); } bool goal::is_decided_unsat() const { return inconsistent() && unsat_preserved(); } bool goal::is_decided() const { return is_decided_sat() || is_decided_unsat(); } bool is_equal(goal const & s1, goal const & s2) { if (s1.size() != s2.size()) return false; unsigned num1 = 0; // num unique ASTs in s1 unsigned num2 = 0; // num unique ASTs in s2 expr_fast_mark1 visited1; expr_fast_mark2 visited2; unsigned sz = s1.size(); for (unsigned i = 0; i < sz; i++) { expr * f1 = s1.form(i); if (visited1.is_marked(f1)) continue; num1++; visited1.mark(f1); } SASSERT(num1 <= sz); SASSERT(0 <= num1); for (unsigned i = 0; i < sz; i++) { expr * f2 = s2.form(i); if (visited2.is_marked(f2)) continue; num2++; visited2.mark(f2); if (!visited1.is_marked(f2)) return false; } SASSERT(num2 <= sz); SASSERT(0 <= num2); SASSERT(num1 >= num2); return num1 == num2; } bool goal::is_cnf() const { for (unsigned i = 0; i < size(); i++) { expr * f = form(i); if (m_manager.is_or(f)) { for (expr* lit : *to_app(f)) { if (!is_literal(lit)) { return false; } } return true; } if (!is_literal(f)) { return false; } } return true; } bool goal::is_literal(expr* f) const { m_manager.is_not(f, f); if (!is_app(f)) return false; if (to_app(f)->get_family_id() == m_manager.get_basic_family_id()) { for (expr* arg : *to_app(f)) if (m_manager.is_bool(arg)) { return false; } } return true; } z3-z3-4.8.7/src/tactic/goal.h000066400000000000000000000202131356505360400155560ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: goal.h Abstract: A goal is essentially a set of formulas. Tactics are used to build proof and model finding procedures for these sets. Remark: In a previous version of Z3, goals were called assertion_sets. Here is a summary of the main changes: - Goals track whether they are the result of applying over/under approximation steps. This prevent users from creating unsound strategies (e.g., user uses nia2sat, but does not check the sat_preserving flag). - Goals track dependencies (aka light proofs) for unsat core extraction, and building multi-tier solvers. This kind of dependency tracking is more powerful than the one used in the current Z3, since it does not prevent the use of preprocessing steps such as "Gaussian Elimination". Author: Leonardo de Moura (leonardo) 2011-10-12 Revision History: --*/ #ifndef GOAL_H_ #define GOAL_H_ #include "ast/ast.h" #include "ast/ast_translation.h" #include "ast/ast_printer.h" #include "ast/for_each_expr.h" #include "util/ref.h" #include "util/ref_vector.h" #include "util/ref_buffer.h" #include "tactic/model_converter.h" #include "tactic/proof_converter.h" #include "tactic/dependency_converter.h" class goal { public: enum precision { PRECISE, UNDER, // goal is the product of an under-approximation OVER, // goal is the product of an over-approximation UNDER_OVER // goal is garbage: the produce of combined under and over approximation steps. }; static precision mk_union(precision p1, precision p2); protected: ast_manager & m_manager; model_converter_ref m_mc; proof_converter_ref m_pc; dependency_converter_ref m_dc; unsigned m_ref_count; expr_array m_forms; expr_array m_proofs; expr_dependency_array m_dependencies; // attributes unsigned m_depth:26; // depth of the goal in the goal tree. unsigned m_models_enabled:1; // model generation is enabled. unsigned m_proofs_enabled:1; // proof production is enabled. m_manager.proofs_enabled() must be true if m_proofs_enabled == true unsigned m_core_enabled:1; // unsat core extraction is enabled. unsigned m_inconsistent:1; // true if the goal is known to be inconsistent. unsigned m_precision:2; // PRECISE, UNDER, OVER. void push_back(expr * f, proof * pr, expr_dependency * d); void quick_process(bool save_first, expr_ref & f, expr_dependency * d); void process_and(bool save_first, app * f, proof * pr, expr_dependency * d, expr_ref & out_f, proof_ref & out_pr); void process_not_or(bool save_first, app * f, proof * pr, expr_dependency * d, expr_ref & out_f, proof_ref & out_pr); void slow_process(bool save_first, expr * f, proof * pr, expr_dependency * d, expr_ref & out_f, proof_ref & out_pr); void slow_process(expr * f, proof * pr, expr_dependency * d); unsigned get_idx(expr * f) const; unsigned get_not_idx(expr * f) const; void shrink(unsigned j); void reset_core(); bool is_literal(expr* f) const; public: goal(ast_manager & m, bool models_enabled = true, bool core_enabled = false); goal(ast_manager & m, bool proofs_enabled, bool models_enabled, bool core_enabled); goal(goal const & src); // Copy configuration: depth, models/proofs/cores flags, and precision from src. // The assertions are not copied goal(goal const & src, bool); ~goal(); void inc_ref() { ++m_ref_count; } void dec_ref() { --m_ref_count; if (m_ref_count == 0) dealloc(this); } ast_manager & m() const { return m_manager; } unsigned depth() const { return m_depth; } bool models_enabled() const { return m_models_enabled; } bool proofs_enabled() const { return m_proofs_enabled; } bool unsat_core_enabled() const { return m_core_enabled; } bool inconsistent() const { return m_inconsistent; } precision prec() const { return static_cast(m_precision); } void set_depth(unsigned d) { m_depth = d; } void inc_depth() { m_depth++; } void set_prec(precision d) { m_precision = d; } void updt_prec(precision d) { m_precision = mk_union(prec(), d); } void reset_all(); // reset goal and precision and depth attributes. void reset(); // reset goal but preserve precision and depth attributes. void copy_to(goal & target) const; void copy_from(goal const & src) { src.copy_to(*this); } void assert_expr(expr * f, proof * pr, expr_dependency * d); void assert_expr(expr * f, expr_dependency * d); void assert_expr(expr * f, expr * d) { assert_expr(f, m().mk_leaf(d)); } void assert_expr(expr * f) { assert_expr(f, static_cast(nullptr)); } unsigned size() const { return m().size(m_forms); } unsigned num_exprs() const; expr * form(unsigned i) const { return m().get(m_forms, i); } proof * pr(unsigned i) const { return m().size(m_proofs) > i ? static_cast(m().get(m_proofs, i)) : nullptr; } expr_dependency * dep(unsigned i) const { return unsat_core_enabled() ? m().get(m_dependencies, i) : nullptr; } void update(unsigned i, expr * f, proof * pr = nullptr, expr_dependency * dep = nullptr); void get_formulas(ptr_vector & result) const; void get_formulas(expr_ref_vector & result) const; void elim_true(); void elim_redundancies(); void display(ast_printer & prn, std::ostream & out) const; void display(ast_printer_context & ctx) const; void display(std::ostream & out) const; void display_ll(std::ostream & out) const; void display_as_and(std::ostream & out) const; void display_dimacs(std::ostream & out) const; void display_with_dependencies(ast_printer & prn, std::ostream & out) const; void display_with_dependencies(ast_printer_context & ctx) const; void display_with_dependencies(std::ostream & out) const; bool sat_preserved() const; bool unsat_preserved() const; bool is_decided_sat() const; bool is_decided_unsat() const; bool is_decided() const; bool is_well_sorted() const; dependency_converter* dc() { return m_dc.get(); } model_converter* mc() const { return m_mc.get(); } proof_converter* pc() const { return inconsistent() ? proof2proof_converter(m(), pr(0)) : m_pc.get(); } void add(dependency_converter* d) { m_dc = dependency_converter::concat(m_dc.get(), d); } void add(model_converter* m) { m_mc = concat(m_mc.get(), m); } void add(proof_converter* p) { m_pc = concat(m_pc.get(), p); } void set(dependency_converter* d) { m_dc = d; } void set(model_converter* m) { m_mc = m; } void set(proof_converter* p) { m_pc = p; } bool is_cnf() const; goal * translate(ast_translation & translator) const; }; std::ostream & operator<<(std::ostream & out, goal::precision p); typedef ref goal_ref; typedef sref_vector goal_ref_vector; typedef sref_buffer goal_ref_buffer; template inline bool is_decided(GoalCollection const & c) { return c.size() == 1 && c[0]->is_decided(); } template inline bool is_decided_sat(GoalCollection const & c) { return c.size() == 1 && c[0]->is_decided_sat(); } template inline bool is_decided_unsat(GoalCollection const & c) { return c.size() == 1 && c[0]->is_decided_unsat(); } template void for_each_expr_at(ForEachProc& proc, goal const & s) { expr_mark visited; for (unsigned i = 0; i < s.size(); ++i) { for_each_expr(proc, visited, s.form(i)); } } bool is_equal(goal const & g1, goal const & g2); template bool test(goal const & g, Predicate & proc) { expr_fast_mark1 visited; try { unsigned sz = g.size(); for (unsigned i = 0; i < sz; i++) quick_for_each_expr(proc, visited, g.form(i)); } catch (const typename Predicate::found &) { return true; } return false; } template bool test(goal const & g) { Predicate proc(g.m()); return test(g, proc); } #endif z3-z3-4.8.7/src/tactic/goal_num_occurs.cpp000066400000000000000000000006621356505360400203540ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: goal_num_occurs.cpp Abstract: Author: Leonardo de Moura (leonardo) 2012-10-20. Revision History: --*/ #include "tactic/goal_num_occurs.h" #include "tactic/goal.h" void goal_num_occurs::operator()(goal const & g) { expr_fast_mark1 visited; unsigned sz = g.size(); for (unsigned i = 0; i < sz; i++) { process(g.form(i), visited); } } z3-z3-4.8.7/src/tactic/goal_num_occurs.h000066400000000000000000000010171356505360400200140ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: goal_num_occurs.h Abstract: Author: Leonardo de Moura (leonardo) 2012-10-20. Revision History: --*/ #ifndef GOAL_NUM_OCCURS_H_ #define GOAL_NUM_OCCURS_H_ #include "ast/num_occurs.h" class goal; class goal_num_occurs : public num_occurs { public: goal_num_occurs(bool ignore_ref_count1 = false, bool ignore_quantifiers = false): num_occurs(ignore_ref_count1, ignore_quantifiers) { } void operator()(goal const & s); }; #endif z3-z3-4.8.7/src/tactic/goal_shared_occs.cpp000066400000000000000000000010061356505360400204450ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: goal_shared_occs.cpp Abstract: Functor for computing the set of shared occurrences in a goal. Author: Leonardo de Moura (leonardo) 2011-12-28 Revision History: --*/ #include "tactic/goal_shared_occs.h" void goal_shared_occs::operator()(goal const & g) { m_occs.reset(); shared_occs_mark visited; unsigned sz = g.size(); for (unsigned i = 0; i < sz; i++) { expr * t = g.form(i); m_occs(t, visited); } } z3-z3-4.8.7/src/tactic/goal_shared_occs.h000066400000000000000000000021221356505360400201120ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: goal_shared_occs.h Abstract: Functor for computing the set of shared occurrences in a goal. Author: Leonardo de Moura (leonardo) 2011-12-28 Revision History: --*/ #ifndef GOAL_SHARED_OCCS_H_ #define GOAL_SHARED_OCCS_H_ #include "tactic/goal.h" #include "ast/shared_occs.h" /** \brief Functor for computing the set of shared occurrences in a goal. It is essentially a wrapper for shared_occs functor. */ class goal_shared_occs { shared_occs m_occs; public: goal_shared_occs(ast_manager & m, bool track_atomic = false, bool visit_quantifiers = true, bool visit_patterns = false): m_occs(m, track_atomic, visit_quantifiers, visit_patterns) { } void operator()(goal const & s); bool is_shared(expr * t) { return m_occs.is_shared(t); } unsigned num_shared() const { return m_occs.num_shared(); } void reset() { return m_occs.reset(); } void cleanup() { return m_occs.cleanup(); } void display(std::ostream & out, ast_manager & m) const { m_occs.display(out, m); } }; #endif z3-z3-4.8.7/src/tactic/goal_util.cpp000066400000000000000000000011161356505360400171470ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: goal_util.cpp Abstract: goal goodies. Author: Leonardo de Moura (leonardo) 2012-01-03. Revision History: --*/ #include "tactic/goal_util.h" #include "tactic/goal.h" struct has_term_ite_functor { struct found {}; ast_manager & m; has_term_ite_functor(ast_manager & _m):m(_m) {} void operator()(var *) {} void operator()(quantifier *) {} void operator()(app * n) { if (m.is_term_ite(n)) throw found(); } }; bool has_term_ite(goal const & g) { return test(g); } z3-z3-4.8.7/src/tactic/goal_util.h000066400000000000000000000004351356505360400166170ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: goal_util.h Abstract: goal goodies. Author: Leonardo de Moura (leonardo) 2012-01-03. Revision History: --*/ #ifndef GOAL_UTIL_H_ #define GOAL_UTIL_H_ class goal; bool has_term_ite(goal const & g); #endif z3-z3-4.8.7/src/tactic/horn_subsume_model_converter.cpp000066400000000000000000000143421356505360400231550ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: horn_subsume_model_converter.cpp Abstract: Model converter for redundant Horn clauses. Author: Nikolaj Bjorner (nbjorner) 2012-9-16 Revision History: --*/ #include "tactic/horn_subsume_model_converter.h" #include "ast/rewriter/var_subst.h" #include "ast/ast_pp.h" #include "model/model_smt2_pp.h" #include "ast/rewriter/bool_rewriter.h" #include "ast/rewriter/th_rewriter.h" #include "ast/for_each_expr.h" #include "ast/well_sorted.h" void horn_subsume_model_converter::insert(app* head, expr* body) { m_delay_head.push_back(head); m_delay_body.push_back(body); } void horn_subsume_model_converter::insert(app* head, unsigned sz, expr* const* body) { expr_ref b(m); bool_rewriter(m).mk_and(sz, body, b); insert(head, b.get()); } bool horn_subsume_model_converter::mk_horn( app* head, expr* body, func_decl_ref& pred, expr_ref& body_res) { expr_ref_vector conjs(m), subst(m); ptr_vector sorts2; var_subst vs(m, false); if (!is_uninterp(head)) { return false; } pred = head->get_decl(); unsigned arity = head->get_num_args(); expr_free_vars fv; fv(head); fv.accumulate(body); if (arity == 0 && fv.empty()) { body_res = body; return true; } fv.set_default_sort(m.mk_bool_sort()); svector names; for (unsigned i = 0; i < fv.size(); ++i) { names.push_back(symbol(i)); } names.reverse(); fv.reverse(); conjs.push_back(body); for (unsigned i = 0; i < arity; ++i) { expr* arg = head->get_arg(i); var_ref v(m); v = m.mk_var(fv.size()+i, m.get_sort(arg)); if (is_var(arg)) { unsigned w = to_var(arg)->get_idx(); if (w >= subst.size()) { subst.resize(w+1); } if (subst[w].get()) { conjs.push_back(m.mk_eq(v, subst[w].get())); } else { subst[w] = v; } } else { conjs.push_back(m.mk_eq(v, arg)); } } expr_ref body_expr(m); body_expr = m.mk_and(conjs.size(), conjs.c_ptr()); // substitute variables directly. if (!subst.empty()) { body_expr = vs(body_expr, subst.size(), subst.c_ptr()); } if (fv.empty()) { SASSERT(subst.empty()); body_res = body_expr; } else { body_res = m.mk_exists(fv.size(), fv.c_ptr(), names.c_ptr(), body_expr.get()); m_rewrite(body_res); } TRACE("mc", tout << mk_pp(head, m) << " :- " << mk_pp(body, m) << "\n"; tout << pred->get_name() << " :- " << mk_pp(body_res.get(), m) << "\n";); return true; } bool horn_subsume_model_converter::mk_horn( expr* clause, func_decl_ref& pred, expr_ref& body) { // formula is closed. DEBUG_CODE(expr_free_vars fv; fv(clause); SASSERT(fv.empty());); while (is_quantifier(clause) && to_quantifier(clause)->get_kind() == forall_k) { quantifier* q = to_quantifier(clause); clause = q->get_expr(); } expr* e1, *e2; if (m.is_implies(clause, e1, e2)) { if (!is_uninterp(e2)) { return false; } return mk_horn(to_app(e2), e1, pred, body); } else if (m.is_or(clause)) { // todo? return false; } else { return false; } } void horn_subsume_model_converter::add_default_proc::operator()(app* n) { // // predicates that have not been assigned values // in the Horn model are assumed false. // if (m.is_bool(n) && !m_md->has_interpretation(n->get_decl()) && (n->get_family_id() == null_family_id)) { TRACE("mc", tout << "adding: " << n->get_decl()->get_name() << "\n";); if (n->get_decl()->get_arity() == 0) { m_md->register_decl(n->get_decl(), m.mk_false()); } else { func_interp* fi = alloc(func_interp, m, n->get_decl()->get_arity()); fi->set_else(m.mk_false()); m_md->register_decl(n->get_decl(), fi); } } } void horn_subsume_model_converter::add_default_false_interpretation(expr* e, model_ref& md) { add_default_proc proc(m, md); for_each_expr(proc, e); } void horn_subsume_model_converter::operator()(expr_ref& fml) { NOT_IMPLEMENTED_YET(); } void horn_subsume_model_converter::operator()(model_ref& mr) { func_decl_ref pred(m); expr_ref body_res(m); for (unsigned i = 0; i < m_delay_head.size(); ++i) { VERIFY(mk_horn(m_delay_head[i].get(), m_delay_body[i].get(), pred, body_res)); insert(pred.get(), body_res.get()); } m_delay_head.reset(); m_delay_body.reset(); TRACE("mc", tout << m_funcs.size() << "\n"; model_smt2_pp(tout, m, *mr, 0);); for (unsigned i = m_funcs.size(); i > 0; ) { --i; func_decl* h = m_funcs[i].get(); expr_ref body(m_bodies[i].get(), m); unsigned arity = h->get_arity(); add_default_false_interpretation(body, mr); SASSERT(m.is_bool(body)); TRACE("mc", tout << "eval: " << h->get_name() << "\n" << body << "\n";); body = (*mr)(body); TRACE("mc", tout << "to:\n" << body << "\n";); if (arity == 0) { expr* e = mr->get_const_interp(h); if (e) { body = m.mk_or(e, body); } m_rewrite(body); mr->register_decl(h, body); } else { func_interp* f = mr->get_func_interp(h); if (f) { expr* e = f->get_else(); body = m.mk_or(e, body); } else { f = alloc(func_interp, m, arity); mr->register_decl(h, f); } m_rewrite(body); f->set_else(body); } } } model_converter* horn_subsume_model_converter::translate(ast_translation & translator) { horn_subsume_model_converter* mc = alloc(horn_subsume_model_converter, translator.to()); for (unsigned i = 0; i < m_funcs.size(); ++i) { mc->insert(translator(m_funcs[i].get()), translator(m_bodies[i].get())); } return mc; } z3-z3-4.8.7/src/tactic/horn_subsume_model_converter.h000066400000000000000000000037511356505360400226240ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: horn_subsume_model_converter.h Abstract: Model converter for redundant Horn clauses. Author: Nikolaj Bjorner (nbjorner) 2012-9-16 Revision History: Notes: Subsumption transformation (remove Horn clause): P(x) :- Body(x,y) Rules ---------------------------- Rules Model converter: P(x) := P(x) or (exists y. Body(x,y)) --*/ #ifndef HORN_SUBSUME_MODEL_CONVERTER_H_ #define HORN_SUBSUME_MODEL_CONVERTER_H_ #include "tactic/model_converter.h" #include "ast/rewriter/th_rewriter.h" class horn_subsume_model_converter : public model_converter { ast_manager& m; func_decl_ref_vector m_funcs; expr_ref_vector m_bodies; th_rewriter m_rewrite; app_ref_vector m_delay_head; expr_ref_vector m_delay_body; void add_default_false_interpretation(expr* e, model_ref& md); struct add_default_proc { ast_manager& m; model_ref& m_md; add_default_proc(ast_manager& m, model_ref& md): m(m), m_md(md) {} void operator()(app* n); void operator()(expr* n) {} }; public: horn_subsume_model_converter(ast_manager& m): m(m), m_funcs(m), m_bodies(m), m_rewrite(m), m_delay_head(m), m_delay_body(m) {} bool mk_horn(expr* clause, func_decl_ref& pred, expr_ref& body); bool mk_horn(app* head, expr* body, func_decl_ref& pred, expr_ref& body_res); void insert(app* head, expr* body); void insert(app* head, unsigned sz, expr* const* body); void insert(func_decl* p, expr* body) { m_funcs.push_back(p); m_bodies.push_back(body); } void operator()(model_ref& _m) override; void operator()(expr_ref& fml) override; model_converter * translate(ast_translation & translator) override; ast_manager& get_manager() { return m; } void display(std::ostream & out) override {} void get_units(obj_map& units) override { units.reset(); } }; #endif z3-z3-4.8.7/src/tactic/model_converter.cpp000066400000000000000000000110131356505360400203540ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: model_converter.h Abstract: Abstract interface for converting models. Author: Leonardo (leonardo) 2011-04-21 Notes: --*/ #include "tactic/model_converter.h" #include "model/model_v2_pp.h" #include "ast/ast_smt2_pp.h" /* * Add or overwrite value in model. */ void model_converter::display_add(std::ostream& out, ast_manager& m, func_decl* f, expr* e) const { VERIFY(e); smt2_pp_environment_dbg env(m); smt2_pp_environment* _env = m_env ? m_env : &env; VERIFY(f->get_range() == m.get_sort(e)); ast_smt2_pp(out, f, e, *_env, params_ref(), 0, "model-add") << "\n"; } /* * A value is removed from the model. */ void model_converter::display_del(std::ostream& out, func_decl* f) const { if (m_env) { ast_smt2_pp(out << "(model-del ", f->get_name(), f->is_skolem(), *m_env) << ")\n"; } else { out << "(model-del " << f->get_name() << ")\n"; } } void model_converter::set_env(ast_pp_util* visitor) { if (visitor) { m_env = &visitor->env(); } else { m_env = nullptr; } } void model_converter::display_add(std::ostream& out, ast_manager& m) { // default printer for converter that adds entries model_ref mdl = alloc(model, m); (*this)(mdl); for (unsigned i = 0, sz = mdl->get_num_constants(); i < sz; ++i) { func_decl* f = mdl->get_constant(i); display_add(out, m, f, mdl->get_const_interp(f)); } for (unsigned i = 0, sz = mdl->get_num_functions(); i < sz; ++i) { func_decl* f = mdl->get_function(i); func_interp* fi = mdl->get_func_interp(f); display_add(out, m, f, fi->get_interp()); } } class concat_model_converter : public concat_converter { public: concat_model_converter(model_converter * mc1, model_converter * mc2): concat_converter(mc1, mc2) { VERIFY(m_c1 && m_c2); } void operator()(model_ref & m) override { this->m_c2->operator()(m); this->m_c1->operator()(m); } void operator()(expr_ref & fml) override { this->m_c2->operator()(fml); this->m_c1->operator()(fml); } void operator()(labels_vec & r) override { this->m_c2->operator()(r); this->m_c1->operator()(r); } void get_units(obj_map& fmls) override { m_c2->get_units(fmls); m_c1->get_units(fmls); } char const * get_name() const override { return "concat-model-converter"; } model_converter * translate(ast_translation & translator) override { return this->translate_core(translator); } void set_env(ast_pp_util* visitor) override { this->m_c1->set_env(visitor); this->m_c2->set_env(visitor); } }; model_converter * concat(model_converter * mc1, model_converter * mc2) { if (mc1 == nullptr) return mc2; if (mc2 == nullptr) return mc1; return alloc(concat_model_converter, mc1, mc2); } class model2mc : public model_converter { model_ref m_model; labels_vec m_labels; public: model2mc(model * m):m_model(m) {} model2mc(model * m, labels_vec const & r):m_model(m), m_labels(r) {} ~model2mc() override {} void operator()(model_ref & m) override { m = m_model; } void operator()(labels_vec & r) override { r.append(m_labels.size(), m_labels.c_ptr()); } void operator()(expr_ref& fml) override { model::scoped_model_completion _scm(m_model, false); fml = (*m_model)(fml); } void get_units(obj_map& fmls) override { // no-op } void cancel() override { } void display(std::ostream & out) override { out << "(rmodel->model-converter-wrapper\n"; model_v2_pp(out, *m_model); out << ")\n"; } model_converter * translate(ast_translation & translator) override { model * m = m_model->translate(translator); return alloc(model2mc, m, m_labels); } }; model_converter * model2model_converter(model * m) { if (!m) return nullptr; return alloc(model2mc, m); } model_converter * model_and_labels2model_converter(model * m, labels_vec const & r) { if (!m) return nullptr; return alloc(model2mc, m, r); } void model_converter2model(ast_manager & mng, model_converter * mc, model_ref & m) { if (mc) { m = alloc(model, mng); (*mc)(m); } } void apply(model_converter_ref & mc, model_ref & m) { if (mc) { (*mc)(m); } } z3-z3-4.8.7/src/tactic/model_converter.h000066400000000000000000000064041356505360400200310ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: model_converter.h Abstract: Abstract interface for converting models. Author: Leonardo (leonardo) 2011-04-21 Notes: A model converter, mc, can be used to convert a model for one of a generated subgoal into a model for an initial goal or solver state. For a goal or solver state that is decided, a model converter can be a simple wrapper around a model. Logically, given a formula F and subgoal formula F_s a model converter mc for F_s relative to F has the property: m |= F_s iff mc(m) |= F for every model m For the evaluator associated with models, m, we expect eval(m)(F_s) <=> eval(mc(m))(F) This property holds for both eval, that decides on a fixed value for constants that have no interpretation in m and for 'peval' (partial eval) that returns just the constants that are unfixed. (in the model evaluator one can control this behavior using a configuration flag) and more generally over the eval method have: G => F_s iff peval(mc(e))(G) => F for every formula G where e is the empty model (a model that does not evaluate any When a model converter supports application to a formula it satisfies the following property: mc(G) & F_s is SAT iff G & F is SAT For a model converter that is a sequence of definitions and removals of functions we can obtain mc(G) by adding back or expanding definitions that are required to interpret G fully in the context of F_s. --*/ #ifndef MODEL_CONVERTER_H_ #define MODEL_CONVERTER_H_ #include "util/ref.h" #include "ast/ast_pp_util.h" #include "model/model.h" #include "tactic/converter.h" class labels_vec : public svector {}; class smt2_pp_environment; class model_converter : public converter { protected: smt2_pp_environment* m_env; void display_add(std::ostream& out, ast_manager& m, func_decl* f, expr* e) const; void display_del(std::ostream& out, func_decl* f) const; void display_add(std::ostream& out, ast_manager& m); public: model_converter(): m_env(nullptr) {} virtual void operator()(model_ref & m) = 0; virtual void operator()(labels_vec & r) {} virtual model_converter * translate(ast_translation & translator) = 0; virtual void set_env(ast_pp_util* visitor); /** \brief we are adding a formula to the context of the model converter. The operator has as side effect of adding definitions as assertions to the formula and removing these definitions from the model converter. */ virtual void operator()(expr_ref& formula) { UNREACHABLE(); } virtual void get_units(obj_map& fmls) { UNREACHABLE(); } }; typedef ref model_converter_ref; typedef sref_vector model_converter_ref_vector; typedef sref_buffer model_converter_ref_buffer; model_converter * concat(model_converter * mc1, model_converter * mc2); model_converter * model2model_converter(model * m); model_converter * model_and_labels2model_converter(model * m, labels_vec const &r); void model_converter2model(ast_manager & mng, model_converter * mc, model_ref & m); void apply(model_converter_ref & mc, model_ref & m); #endif z3-z3-4.8.7/src/tactic/portfolio/000077500000000000000000000000001356505360400165025ustar00rootroot00000000000000z3-z3-4.8.7/src/tactic/portfolio/CMakeLists.txt000066400000000000000000000005061356505360400212430ustar00rootroot00000000000000z3_add_component(portfolio SOURCES default_tactic.cpp smt_strategic_solver.cpp solver2lookahead.cpp COMPONENT_DEPENDENCIES aig_tactic fp fpa_tactics qe sat_solver sls_tactic smtlogic_tactics subpaving_tactic ufbv_tactic fd_solver TACTIC_HEADERS default_tactic.h ) z3-z3-4.8.7/src/tactic/portfolio/default_tactic.cpp000066400000000000000000000045171356505360400221700ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: default_tactic.cpp Abstract: General purpose tactic for the Z3 logic (when the logic is not specified). Author: Leonardo (leonardo) 2012-02-22 Notes: --*/ #include "tactic/portfolio/default_tactic.h" #include "tactic/core/simplify_tactic.h" #include "tactic/smtlogics/qfbv_tactic.h" #include "smt/tactic/smt_tactic.h" #include "tactic/smtlogics/qflia_tactic.h" #include "tactic/smtlogics/qflra_tactic.h" #include "tactic/smtlogics/qfnia_tactic.h" #include "tactic/smtlogics/qfnra_tactic.h" #include "tactic/smtlogics/nra_tactic.h" #include "tactic/arith/probe_arith.h" #include "tactic/smtlogics/quant_tactics.h" #include "tactic/fpa/qffp_tactic.h" #include "tactic/fpa/qffplra_tactic.h" #include "tactic/smtlogics/qfaufbv_tactic.h" #include "tactic/smtlogics/qfauflia_tactic.h" #include "tactic/fd_solver/fd_solver.h" tactic * mk_default_tactic(ast_manager & m, params_ref const & p) { tactic * st = using_params(and_then(mk_simplify_tactic(m), 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), cond(mk_is_qfauflia_probe(), mk_qfauflia_tactic(m), cond(mk_is_qflra_probe(), mk_qflra_tactic(m), cond(mk_is_qfnra_probe(), mk_qfnra_tactic(m), cond(mk_is_qfnia_probe(), mk_qfnia_tactic(m), cond(mk_is_lira_probe(), mk_lira_tactic(m, p), cond(mk_is_nra_probe(), mk_nra_tactic(m), cond(mk_is_qffp_probe(), mk_qffp_tactic(m, p), cond(mk_is_qffplra_probe(), mk_qffplra_tactic(m, p), //cond(mk_is_qfufnra_probe(), mk_qfufnra_tactic(m, p), and_then(mk_preamble_tactic(m), mk_smt_tactic(m))))))))))))))), p); return st; } z3-z3-4.8.7/src/tactic/portfolio/default_tactic.h000066400000000000000000000010371356505360400216270ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: default_tactic.h Abstract: General purpose tactic for the Z3 logic (when the logic is not specified). Author: Leonardo (leonardo) 2012-02-22 Notes: --*/ #ifndef DEFAULT_TACTIC_H_ #define DEFAULT_TACTIC_H_ #include "util/params.h" class ast_manager; class tactic; tactic * mk_default_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("default", "default strategy used when no logic is specified.", "mk_default_tactic(m, p)") */ #endif z3-z3-4.8.7/src/tactic/portfolio/smt_strategic_solver.cpp000066400000000000000000000125361356505360400234570ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: smt_strategic_solver.cpp Abstract: Create a strategic solver with tactic for all main logics used in SMT. Author: Leonardo (leonardo) 2012-02-19 Notes: --*/ #include "cmd_context/cmd_context.h" #include "solver/combined_solver.h" #include "solver/tactic2solver.h" #include "tactic/smtlogics/qfbv_tactic.h" #include "tactic/smtlogics/qflia_tactic.h" #include "tactic/smtlogics/qfnia_tactic.h" #include "tactic/smtlogics/qfnra_tactic.h" #include "tactic/smtlogics/qfuf_tactic.h" #include "tactic/smtlogics/qflra_tactic.h" #include "tactic/smtlogics/quant_tactics.h" #include "tactic/smtlogics/qfauflia_tactic.h" #include "tactic/smtlogics/qfaufbv_tactic.h" #include "tactic/smtlogics/qfufbv_tactic.h" #include "tactic/smtlogics/qfidl_tactic.h" #include "tactic/smtlogics/nra_tactic.h" #include "tactic/portfolio/default_tactic.h" #include "tactic/fd_solver/fd_solver.h" #include "tactic/ufbv/ufbv_tactic.h" #include "tactic/fpa/qffp_tactic.h" #include "muz/fp/horn_tactic.h" #include "smt/smt_solver.h" #include "sat/sat_solver/inc_sat_solver.h" #include "ast/rewriter/bv_rewriter.h" #include "solver/solver2tactic.h" #include "solver/parallel_tactic.h" #include "solver/parallel_params.hpp" #include "tactic/tactic_params.hpp" #include "parsers/smt2/smt2parser.h" tactic * mk_tactic_for_logic(ast_manager & m, params_ref const & p, symbol const & logic) { if (logic=="QF_UF") return mk_qfuf_tactic(m, p); else if (logic=="QF_BV") return mk_qfbv_tactic(m, p); else if (logic=="QF_IDL") return mk_qfidl_tactic(m, p); else if (logic=="QF_LIA") return mk_qflia_tactic(m, p); else if (logic=="QF_LRA") return mk_qflra_tactic(m, p); else if (logic=="QF_NIA") return mk_qfnia_tactic(m, p); else if (logic=="QF_NRA") return mk_qfnra_tactic(m, p); else if (logic=="QF_AUFLIA") return mk_qfauflia_tactic(m, p); else if (logic=="QF_AUFBV") return mk_qfaufbv_tactic(m, p); else if (logic=="QF_ABV") return mk_qfaufbv_tactic(m, p); else if (logic=="QF_UFBV") return mk_qfufbv_tactic(m, p); else if (logic=="AUFLIA") return mk_auflia_tactic(m, p); else if (logic=="AUFLIRA") return mk_auflira_tactic(m, p); else if (logic=="AUFNIRA") return mk_aufnira_tactic(m, p); else if (logic=="UFNIA") return mk_ufnia_tactic(m, p); else if (logic=="UFLRA") return mk_uflra_tactic(m, p); else if (logic=="LRA") return mk_lra_tactic(m, p); else if (logic=="NRA") return mk_nra_tactic(m, p); else if (logic=="LIA") return mk_lia_tactic(m, p); else if (logic=="UFBV") return mk_ufbv_tactic(m, p); else if (logic=="BV") return mk_ufbv_tactic(m, p); else if (logic=="QF_FP") return mk_qffp_tactic(m, p); else if (logic == "QF_FPBV" || logic == "QF_BVFP") return mk_qffpbv_tactic(m, p); else if (logic=="HORN") return mk_horn_tactic(m, p); else if ((logic == "QF_FD" || logic == "SAT") && !m.proofs_enabled()) return mk_fd_tactic(m, p); else return mk_default_tactic(m, p); } static solver* mk_special_solver_for_logic(ast_manager & m, params_ref const & p, symbol const& logic) { parallel_params pp(p); if ((logic == "QF_FD" || logic == "SAT") && !m.proofs_enabled() && !pp.enable()) return mk_fd_solver(m, p); return nullptr; } static solver* mk_solver_for_logic(ast_manager & m, params_ref const & p, symbol const& logic) { bv_rewriter rw(m); solver* s = mk_special_solver_for_logic(m, p, logic); if (!s && logic == "QF_BV" && rw.hi_div0()) s = mk_inc_sat_solver(m, p); if (!s) s = mk_smt_solver(m, p, logic); return s; } class smt_strategic_solver_factory : public solver_factory { symbol m_logic; public: smt_strategic_solver_factory(symbol const & logic):m_logic(logic) {} ~smt_strategic_solver_factory() override {} solver * operator()(ast_manager & m, params_ref const & p, bool proofs_enabled, bool models_enabled, bool unsat_core_enabled, symbol const & logic) override { symbol l; if (m_logic != symbol::null) l = m_logic; else l = logic; tactic_params tp; tactic_ref t; if (tp.default_tactic() != symbol::null && !tp.default_tactic().is_numerical() && tp.default_tactic().bare_str() && tp.default_tactic().bare_str()[0]) { cmd_context ctx(false, &m, l); std::istringstream is(tp.default_tactic().bare_str()); char const* file_name = ""; sexpr_ref se = parse_sexpr(ctx, is, p, file_name); if (se) { t = sexpr2tactic(ctx, se.get()); } } if (!t) { solver* s = mk_special_solver_for_logic(m, p, l); if (s) return s; } if (!t) { t = mk_tactic_for_logic(m, p, l); } return mk_combined_solver(mk_tactic2solver(m, t.get(), p, proofs_enabled, models_enabled, unsat_core_enabled, l), mk_solver_for_logic(m, p, l), p); } }; solver_factory * mk_smt_strategic_solver_factory(symbol const & logic) { return alloc(smt_strategic_solver_factory, logic); } z3-z3-4.8.7/src/tactic/portfolio/solver2lookahead.cpp000066400000000000000000000005311356505360400224510ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: solver2lookahead.cpp Abstract: Lookahead wrapper for arbitrary solver. Author: Nikolaj Bjorner (nbjorner) 2017-10-9 Notes: --*/ #include "sat/sat_solver/inc_sat_solver.h" #include "solver/solver.h" solver * mk_solver2lookahead(solver* s) { return nullptr; } z3-z3-4.8.7/src/tactic/portfolio/solver2lookahead.h000066400000000000000000000005111356505360400221140ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: solver2lookahead.h Abstract: Lookahead wrapper for arbitrary solver. Author: Nikolaj Bjorner (nbjorner) 2017-10-9 Notes: --*/ #ifndef SOLVER2LOOKAHEAD_H_ #define SOLVER2LOOKAHEAD_H_ class solver; solver * mk_solver2lookahead(solver* s); #endif z3-z3-4.8.7/src/tactic/probe.cpp000066400000000000000000000324171356505360400163070ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: probe.cpp Abstract: Evaluates/Probes a goal. A probe is used to build tactics (aka strategies) that makes decisions based on the structure of a goal. Author: Leonardo de Moura (leonardo) 2011-10-13. Revision History: --*/ #include "tactic/probe.h" #include "ast/arith_decl_plugin.h" #include "ast/bv_decl_plugin.h" #include "tactic/goal_util.h" #include "ast/rewriter/bv_rewriter.h" class memory_probe : public probe { public: result operator()(goal const & g) override { return result(static_cast(memory::get_allocation_size())/static_cast(1024*1024)); } }; probe * mk_memory_probe() { return alloc(memory_probe); } class depth_probe : public probe { public: result operator()(goal const & g) override { return result(g.depth()); } }; class size_probe : public probe { public: result operator()(goal const & g) override { return result(g.size()); } }; class num_exprs_probe : public probe { public: result operator()(goal const & g) override { return result(g.num_exprs()); } }; probe * mk_depth_probe() { return alloc(depth_probe); } probe * mk_size_probe() { return alloc(size_probe); } probe * mk_num_exprs_probe() { return alloc(num_exprs_probe); } class unary_probe : public probe { protected: probe * m_p; public: unary_probe(probe * p): m_p(p) { SASSERT(p); p->inc_ref(); } ~unary_probe() override { m_p->dec_ref(); } }; class bin_probe : public probe { protected: probe * m_p1; probe * m_p2; public: bin_probe(probe * p1, probe * p2): m_p1(p1), m_p2(p2) { SASSERT(p1); SASSERT(p2); p1->inc_ref(); p2->inc_ref(); } ~bin_probe() override { m_p1->dec_ref(); m_p2->dec_ref(); } }; class not_probe : public unary_probe { public: not_probe(probe * p):unary_probe(p) {} result operator()(goal const & g) override { return result(!m_p->operator()(g).is_true()); } }; class and_probe : public bin_probe { public: and_probe(probe * p1, probe * p2):bin_probe(p1, p2) {} result operator()(goal const & g) override { return result(m_p1->operator()(g).is_true() && m_p2->operator()(g).is_true()); } }; class or_probe : public bin_probe { public: or_probe(probe * p1, probe * p2):bin_probe(p1, p2) {} result operator()(goal const & g) override { return result(m_p1->operator()(g).is_true() || m_p2->operator()(g).is_true()); } }; class eq_probe : public bin_probe { public: eq_probe(probe * p1, probe * p2):bin_probe(p1, p2) {} result operator()(goal const & g) override { return result(m_p1->operator()(g).get_value() == m_p2->operator()(g).get_value()); } }; class le_probe : public bin_probe { public: le_probe(probe * p1, probe * p2):bin_probe(p1, p2) {} result operator()(goal const & g) override { return result(m_p1->operator()(g).get_value() <= m_p2->operator()(g).get_value()); } }; class add_probe : public bin_probe { public: add_probe(probe * p1, probe * p2):bin_probe(p1, p2) {} result operator()(goal const & g) override { return result(m_p1->operator()(g).get_value() + m_p2->operator()(g).get_value()); } }; class sub_probe : public bin_probe { public: sub_probe(probe * p1, probe * p2):bin_probe(p1, p2) {} result operator()(goal const & g) override { return result(m_p1->operator()(g).get_value() - m_p2->operator()(g).get_value()); } }; class mul_probe : public bin_probe { public: mul_probe(probe * p1, probe * p2):bin_probe(p1, p2) {} result operator()(goal const & g) override { return result(m_p1->operator()(g).get_value() * m_p2->operator()(g).get_value()); } }; class div_probe : public bin_probe { public: div_probe(probe * p1, probe * p2):bin_probe(p1, p2) {} result operator()(goal const & g) override { return result(m_p1->operator()(g).get_value() / m_p2->operator()(g).get_value()); } }; class const_probe : public probe { double m_val; public: const_probe(double v):m_val(v) {} result operator()(goal const & g) override { return result(m_val); } }; probe * mk_const_probe(double v) { return alloc(const_probe, v); } probe * mk_not(probe * p) { return alloc(not_probe, p); } probe * mk_and(probe * p1, probe * p2) { return alloc(and_probe, p1, p2); } probe * mk_or(probe * p1, probe * p2) { return alloc(or_probe, p1, p2); } probe * mk_implies(probe * p1, probe * p2) { return mk_or(mk_not(p1), p2); } probe * mk_eq(probe * p1, probe * p2) { return alloc(eq_probe, p1, p2); } probe * mk_neq(probe * p1, probe * p2) { return mk_not(mk_eq(p1, p2)); } probe * mk_le(probe * p1, probe * p2) { return alloc(le_probe, p1, p2); } probe * mk_ge(probe * p1, probe * p2) { return mk_le(p2, p1); } probe * mk_lt(probe * p1, probe * p2) { return mk_not(mk_ge(p1, p2)); } probe * mk_gt(probe * p1, probe * p2) { return mk_lt(p2, p1); } probe * mk_add(probe * p1, probe * p2) { return alloc(add_probe, p1, p2); } probe * mk_mul(probe * p1, probe * p2) { return alloc(mul_probe, p1, p2); } probe * mk_sub(probe * p1, probe * p2) { return alloc(sub_probe, p1, p2); } probe * mk_div(probe * p1, probe * p2) { return alloc(div_probe, p1, p2); } struct is_non_propositional_predicate { struct found {}; ast_manager & m; is_non_propositional_predicate(ast_manager & _m):m(_m) {} void operator()(var *) { throw found(); } void operator()(quantifier *) { throw found(); } void operator()(app * n) { if (!m.is_bool(n)) throw found(); family_id fid = n->get_family_id(); if (fid == m.get_basic_family_id()) return; if (is_uninterp_const(n)) return; throw found(); } }; struct is_non_qfbv_predicate { struct found {}; ast_manager & m; bv_util u; is_non_qfbv_predicate(ast_manager & _m):m(_m), u(m) {} void operator()(var *) { throw found(); } void operator()(quantifier *) { throw found(); } void operator()(app * n) { if (!m.is_bool(n) && !u.is_bv(n)) throw found(); family_id fid = n->get_family_id(); if (fid == m.get_basic_family_id()) return; if (fid == u.get_family_id()) { if (n->get_decl_kind() == OP_BSDIV0 || n->get_decl_kind() == OP_BUDIV0 || n->get_decl_kind() == OP_BSREM0 || n->get_decl_kind() == OP_BUREM0 || n->get_decl_kind() == OP_BSMOD0) throw found(); return; } if (is_uninterp_const(n)) return; throw found(); } }; class is_propositional_probe : public probe { public: result operator()(goal const & g) override { return !test(g); } }; class is_qfbv_probe : public probe { public: result operator()(goal const & g) override { return !test(g); } }; probe * mk_is_propositional_probe() { return alloc(is_propositional_probe); } probe * mk_is_qfbv_probe() { return alloc(is_qfbv_probe); } struct is_non_qfaufbv_predicate { struct found {}; ast_manager & m; bv_util m_bv_util; array_util m_array_util; is_non_qfaufbv_predicate(ast_manager & _m) : m(_m), m_bv_util(_m), m_array_util(_m) {} void operator()(var *) { throw found(); } void operator()(quantifier *) { throw found(); } void operator()(app * n) { if (!m.is_bool(n) && !m_bv_util.is_bv(n) && !m_array_util.is_array(n)) throw found(); family_id fid = n->get_family_id(); if (fid == m.get_basic_family_id()) return; if (fid == m_bv_util.get_family_id() || fid == m_array_util.get_family_id()) return; if (is_uninterp(n)) return; throw found(); } }; class is_qfaufbv_probe : public probe { public: result operator()(goal const & g) override { return !test(g); } }; probe * mk_is_qfaufbv_probe() { return alloc(is_qfaufbv_probe); } struct is_non_qfufbv_predicate { struct found {}; ast_manager & m; bv_util m_bv_util; is_non_qfufbv_predicate(ast_manager & _m) : m(_m), m_bv_util(_m) {} void operator()(var *) { throw found(); } void operator()(quantifier *) { throw found(); } void operator()(app * n) { if (!m.is_bool(n) && !m_bv_util.is_bv(n)) throw found(); family_id fid = n->get_family_id(); if (fid == m.get_basic_family_id()) return; if (fid == m_bv_util.get_family_id()) return; if (is_uninterp(n)) return; throw found(); } }; class is_qfufbv_probe : public probe { public: result operator()(goal const & g) override { return !test(g); } }; probe * mk_is_qfufbv_probe() { return alloc(is_qfufbv_probe); } class num_consts_probe : public probe { bool m_bool; // If true, track only boolean constants. Otherwise, track only non boolean constants. char const * m_family; // (Ignored if m_bool == true), if != 0 and m_bool == true, then track only constants of the given family. struct proc { ast_manager & m; bool m_bool; family_id m_fid; unsigned m_counter; proc(ast_manager & _m, bool b, char const * family):m(_m), m_bool(b), m_counter(0) { if (family != nullptr) m_fid = m.mk_family_id(family); else m_fid = null_family_id; } void operator()(quantifier *) {} void operator()(var *) {} void operator()(app * n) { if (n->get_num_args() == 0 && !m.is_value(n)) { if (m_bool) { if (m.is_bool(n)) m_counter++; } else { if (m_fid == null_family_id) { if (!m.is_bool(n)) m_counter++; } else { if (m.get_sort(n)->get_family_id() == m_fid) m_counter++; } } } } }; public: num_consts_probe(bool b, char const * f): m_bool(b), m_family(f) { } result operator()(goal const & g) override { proc p(g.m(), m_bool, m_family); unsigned sz = g.size(); expr_fast_mark1 visited; for (unsigned i = 0; i < sz; i++) { for_each_expr_core(p, visited, g.form(i)); } return result(p.m_counter); } }; probe * mk_num_consts_probe() { return alloc(num_consts_probe, false, nullptr); } probe * mk_num_bool_consts_probe() { return alloc(num_consts_probe, true, nullptr); } probe * mk_num_arith_consts_probe() { return alloc(num_consts_probe, false, "arith"); } probe * mk_num_bv_consts_probe() { return alloc(num_consts_probe, false, "bv"); } class produce_proofs_probe : public probe { public: result operator()(goal const & g) override { return g.proofs_enabled(); } }; class produce_models_probe : public probe { public: result operator()(goal const & g) override { return g.models_enabled(); } }; class produce_unsat_cores_probe : public probe { public: result operator()(goal const & g) override { return g.unsat_core_enabled(); } }; probe * mk_produce_proofs_probe() { return alloc(produce_proofs_probe); } probe * mk_produce_models_probe() { return alloc(produce_models_probe); } probe * mk_produce_unsat_cores_probe() { return alloc(produce_unsat_cores_probe); } struct has_pattern_probe : public probe { struct found {}; struct proc { void operator()(var * n) {} void operator()(app * n) {} void operator()(quantifier * n) { if (n->get_num_patterns() > 0 || n->get_num_no_patterns() > 0) throw found(); } }; public: result operator()(goal const & g) override { try { expr_fast_mark1 visited; proc p; unsigned sz = g.size(); for (unsigned i = 0; i < sz; i++) { quick_for_each_expr(p, visited, g.form(i)); } return false; } catch (const found &) { return true; } } }; probe * mk_has_pattern_probe() { return alloc(has_pattern_probe); } struct has_quantifier_probe : public probe { struct found {}; struct proc { void operator()(var * n) {} void operator()(app * n) {} void operator()(quantifier * n) { throw found(); } }; public: result operator()(goal const & g) override { try { expr_fast_mark1 visited; proc p; unsigned sz = g.size(); for (unsigned i = 0; i < sz; i++) { quick_for_each_expr(p, visited, g.form(i)); } return false; } catch (const found &) { return true; } } }; probe * mk_has_quantifier_probe() { return alloc(has_quantifier_probe); } z3-z3-4.8.7/src/tactic/probe.h000066400000000000000000000076341356505360400157570ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: probe.h Abstract: Evaluates/Probes a goal. A probe is used to build tactics (aka strategies) that makes decisions based on the structure of a goal. The current implementation is very simple. Author: Leonardo de Moura (leonardo) 2011-10-13. Revision History: --*/ #ifndef PROBE_H_ #define PROBE_H_ #include "tactic/goal.h" class probe { public: class result { double m_value; public: result(double v = 0.0):m_value(v) {} result(unsigned v):m_value(static_cast(v)) {} result(int v):m_value(static_cast(v)) {} result(bool b):m_value(b ? 1.0 : 0.0) {} bool is_true() const { return m_value != 0.0; } double get_value() const { return m_value; } }; private: unsigned m_ref_count; public: probe():m_ref_count(0) {} virtual ~probe() {} void inc_ref() { ++m_ref_count; } void dec_ref() { SASSERT(m_ref_count > 0); --m_ref_count; if (m_ref_count == 0) dealloc(this); } virtual result operator()(goal const & g) = 0; }; typedef ref probe_ref; probe * mk_const_probe(double val); probe * mk_memory_probe(); probe * mk_depth_probe(); probe * mk_size_probe(); /* ADD_PROBE("memory", "amount of used memory in megabytes.", "mk_memory_probe()") ADD_PROBE("depth", "depth of the input goal.", "mk_depth_probe()") ADD_PROBE("size", "number of assertions in the given goal.", "mk_size_probe()") */ probe * mk_num_exprs_probe(); probe * mk_num_consts_probe(); probe * mk_num_bool_consts_probe(); probe * mk_num_arith_consts_probe(); probe * mk_num_bv_consts_probe(); /* ADD_PROBE("num-exprs", "number of expressions/terms in the given goal.", "mk_num_exprs_probe()") ADD_PROBE("num-consts", "number of non Boolean constants in the given goal.", "mk_num_consts_probe()") ADD_PROBE("num-bool-consts", "number of Boolean constants in the given goal.", "mk_num_bool_consts_probe()") ADD_PROBE("num-arith-consts", "number of arithmetic constants in the given goal.", "mk_num_arith_consts_probe()") ADD_PROBE("num-bv-consts", "number of bit-vector constants in the given goal.", "mk_num_bv_consts_probe()") */ probe * mk_produce_proofs_probe(); probe * mk_produce_models_probe(); probe * mk_produce_unsat_cores_probe(); /* ADD_PROBE("produce-proofs", "true if proof generation is enabled for the given goal.", "mk_produce_proofs_probe()") ADD_PROBE("produce-model", "true if model generation is enabled for the given goal.", "mk_produce_models_probe()") ADD_PROBE("produce-unsat-cores", "true if unsat-core generation is enabled for the given goal.", "mk_produce_unsat_cores_probe()") */ probe * mk_has_quantifier_probe(); probe * mk_has_pattern_probe(); /* ADD_PROBE("has-quantifiers", "true if the goal contains quantifiers.", "mk_has_quantifier_probe()") ADD_PROBE("has-patterns", "true if the goal contains quantifiers with patterns.", "mk_has_pattern_probe()") */ // Some basic combinators for probes probe * mk_not(probe * p1); probe * mk_and(probe * p1, probe * p2); probe * mk_or(probe * p1, probe * p2); probe * mk_implies(probe * p1, probe * p2); probe * mk_eq(probe * p1, probe * p2); probe * mk_neq(probe * p1, probe * p2); probe * mk_le(probe * p1, probe * p2); probe * mk_lt(probe * p1, probe * p2); probe * mk_ge(probe * p1, probe * p2); probe * mk_gt(probe * p1, probe * p2); probe * mk_add(probe * p1, probe * p2); probe * mk_sub(probe * p1, probe * p2); probe * mk_mul(probe * p1, probe * p2); probe * mk_div(probe * p1, probe * p2); probe * mk_is_propositional_probe(); probe * mk_is_qfbv_probe(); probe * mk_is_qfaufbv_probe(); probe * mk_is_qfufbv_probe(); /* ADD_PROBE("is-propositional", "true if the goal is in propositional logic.", "mk_is_propositional_probe()") ADD_PROBE("is-qfbv", "true if the goal is in QF_BV.", "mk_is_qfbv_probe()") ADD_PROBE("is-qfaufbv", "true if the goal is in QF_AUFBV.", "mk_is_qfaufbv_probe()") */ #endif z3-z3-4.8.7/src/tactic/proof_converter.cpp000066400000000000000000000101111356505360400203770ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: proof_converter.cpp Abstract: Abstract interface for converting proofs, and basic combinators Author: Leonardo (leonardo) 2011-11-14 Notes: --*/ #include "tactic/proof_converter.h" #include "tactic/goal.h" #include "ast/ast_smt2_pp.h" class concat_proof_converter : public concat_converter { public: concat_proof_converter(proof_converter * pc1, proof_converter * pc2):concat_converter(pc1, pc2) {} char const * get_name() const override { return "concat-proof-converter"; } proof_ref operator()(ast_manager & m, unsigned num_source, proof * const * source) override { proof_ref tmp(m); tmp = this->m_c2->operator()(m, num_source, source); proof * new_source = tmp.get(); return this->m_c1->operator()(m, 1, &new_source); } proof_converter * translate(ast_translation & translator) override { return this->translate_core(translator); } }; proof_converter * concat(proof_converter * pc1, proof_converter * pc2) { if (pc1 == nullptr) return pc2; if (pc2 == nullptr) return pc1; return alloc(concat_proof_converter, pc1, pc2); } class subgoal_proof_converter : public proof_converter { proof_converter_ref m_pc; goal_ref_buffer m_goals; public: subgoal_proof_converter(proof_converter* pc, unsigned n, goal * const* goals): m_pc(pc) { for (unsigned i = 0; i < n; ++i) m_goals.push_back(goals[i]); } proof_ref operator()(ast_manager & m, unsigned num_source, proof * const * source) override { // ignore the proofs from the arguments, instead obtain the proofs fromt he subgoals. SASSERT(num_source == 0); proof_converter_ref_buffer pc_buffer; for (goal_ref g : m_goals) { pc_buffer.push_back(g->pc()); } return apply(m, m_pc, pc_buffer); } proof_converter* translate(ast_translation& tr) override { proof_converter_ref pc1 = m_pc->translate(tr); goal_ref_buffer goals; for (goal_ref g : m_goals) goals.push_back(g->translate(tr)); return alloc(subgoal_proof_converter, pc1.get(), goals.size(), goals.c_ptr()); } void display(std::ostream& out) override {} }; proof_converter * concat(proof_converter *pc, unsigned n, goal* const* goals) { return alloc(subgoal_proof_converter, pc, n, goals); } class proof2pc : public proof_converter { proof_ref m_pr; public: proof2pc(ast_manager & m, proof * pr):m_pr(pr, m) {} ~proof2pc() override {} proof_ref operator()(ast_manager & m, unsigned num_source, proof * const * source) override { SASSERT(num_source == 0); return m_pr; } proof_converter * translate(ast_translation & translator) override { return alloc(proof2pc, translator.to(), translator(m_pr.get())); } void display(std::ostream & out) override { out << "(proof->proof-converter-wrapper\n" << mk_ismt2_pp(m_pr.get(), m_pr.get_manager()) << ")\n"; } }; proof_converter * proof2proof_converter(ast_manager & m, proof * pr) { if (pr == nullptr) return nullptr; return alloc(proof2pc, m, pr); } void apply(ast_manager & m, proof_converter * pc, proof_ref & pr) { if (pc) { proof * _pr = pr.get(); pr = (*pc)(m, 1, &_pr); } } /** Let pc2s be a buffer of proof converters that are wrappers for proofs. That is, they are functors of the form: unit -> Proof Then, this function applies pc1 to the proofs produced by pc2s's and store the resultant proof in result. pc1 and pc2s must be different from 0. */ proof_ref apply(ast_manager & m, proof_converter_ref & pc1, proof_converter_ref_buffer & pc2s) { SASSERT(pc1); proof_ref_buffer prs(m); unsigned sz = pc2s.size(); for (unsigned i = 0; i < sz; i++) { proof_ref pr(m); SASSERT(pc2s[i]); // proof production is enabled pr = pc2s[i]->operator()(m, 0, nullptr); prs.push_back(pr); } return (*pc1)(m, sz, prs.c_ptr()); } z3-z3-4.8.7/src/tactic/proof_converter.h000066400000000000000000000025161356505360400200560ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: proof_converter.h Abstract: Abstract interface for converting proofs, and basic combinators. Author: Leonardo (leonardo) 2011-04-26 Notes: --*/ #ifndef PROOF_CONVERTER_H_ #define PROOF_CONVERTER_H_ #include "ast/ast.h" #include "util/ref.h" #include "tactic/converter.h" class goal; class proof_converter : public converter { public: ~proof_converter() override { } virtual proof_ref operator()(ast_manager & m, unsigned num_source, proof * const * source) = 0; virtual proof_converter * translate(ast_translation & translator) = 0; }; typedef ref proof_converter_ref; typedef sref_vector proof_converter_ref_vector; typedef sref_buffer proof_converter_ref_buffer; proof_converter * concat(proof_converter * pc1, proof_converter * pc2); /** \brief create a proof converter that takes a set of subgoals and converts their proofs to a proof of the goal they were derived from. */ proof_converter * concat(proof_converter *pc1, unsigned n, goal* const* goals); proof_converter * proof2proof_converter(ast_manager & m, proof * pr); void apply(ast_manager & m, proof_converter * pc, proof_ref & pr); proof_ref apply(ast_manager & m, proof_converter_ref & pc1, proof_converter_ref_buffer & pc2s); #endif z3-z3-4.8.7/src/tactic/replace_proof_converter.cpp000066400000000000000000000044561356505360400221110ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: replace_proof_converter.cpp Abstract: Proof converter that replaces asserted by sub-proof. Author: Nikolaj Bjorner (nbjorner) 2012-9-16 Revision History: --*/ #include "tactic/replace_proof_converter.h" #include "ast/expr_functors.h" #include "ast/ast_pp.h" #include "ast/for_each_expr.h" /** \brief Replace expressions by other expressions. replace_map is caching, so inserting src |-> dst has no effect if src is a sub-expression of something that has already been visited. The assumption is that proof replacements are inserted into the replace_proof_converter in the order that they are introduced, so there are no such clashes. map_proc is used as expr_replacer behaves differently when proof mode is turned on. */ class replace_map : public map_proc { public: replace_map(ast_manager& m): map_proc(m) {} void insert(expr* src, expr* dst) { m_map.insert(src, dst, nullptr); } void operator()(var* v) { visit(v); } void operator()(app* a) { if (!get_expr(a)) { reconstruct(a); } } void operator()(quantifier* q) { visit(q); } void apply(expr_ref& e) { for_each_expr(*this, e); e = get_expr(e); } }; proof_ref replace_proof_converter::operator()(ast_manager & m, unsigned num_source, proof * const * source) { SASSERT(num_source == 1); replace_map replace(m); proof_ref p(m); expr_ref tmp(source[0], m), e(m), f(m); // apply the substitution to the prefix before inserting it. for (unsigned i = 0; i < m_proofs.size(); ++i) { p = m_proofs[i].get(); e = p; replace.apply(e); f = m.mk_asserted(m.get_fact(p)); replace.insert(f, e); TRACE("proof_converter", tout << f->get_id() << " " << mk_pp(f, m) << "\n|-> " << mk_pp(e, m) << "\n";); } replace.apply(tmp); TRACE("proof_converter", tout << mk_pp(source[0], m) << "\n"; tout << mk_pp(tmp.get(), m) << "\n";); return proof_ref(to_app(tmp), m); } proof_converter * replace_proof_converter::translate(ast_translation & translator) { replace_proof_converter* rp = alloc(replace_proof_converter, m); for (proof* p : m_proofs) rp->insert(translator(p)); return rp; } z3-z3-4.8.7/src/tactic/replace_proof_converter.h000066400000000000000000000021521356505360400215450ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: replace_proof_converter.h Abstract: Proof converter to replace asserted leaves by proofs. Given a proof P with occurrences of [asserted fml] Replace [asserted fml] by proofs whose conclusions are fml. Author: Nikolaj Bjorner (nbjorner) 2012-9-16 Revision History: --*/ #ifndef REPLACE_PROOF_CONVERTER_H_ #define REPLACE_PROOF_CONVERTER_H_ #include "tactic/proof_converter.h" class replace_proof_converter : public proof_converter { ast_manager& m; proof_ref_vector m_proofs; public: replace_proof_converter(ast_manager& _m): m(_m), m_proofs(m) {} ~replace_proof_converter() override {} proof_ref operator()(ast_manager & _m, unsigned num_source, proof * const * source) override; proof_converter * translate(ast_translation & translator) override; void insert(proof* p) { m_proofs.push_back(p); } ast_manager& get_manager() { return m; } // run the replacements the inverse direction. void invert() { m_proofs.reverse(); } void display(std::ostream & out) override {} }; #endif z3-z3-4.8.7/src/tactic/sine_filter.cpp000066400000000000000000000171051356505360400175000ustar00rootroot00000000000000/*++ Copyright (c) 2016 Microsoft Corporation Module Name: sine_filter.cpp Abstract: Tactic that performs Sine Qua Non premise selection Author: Doug Woos Revision History: --*/ #include "tactic/sine_filter.h" #include "tactic/tactical.h" #include "tactic/generic_model_converter.h" #include "ast/datatype_decl_plugin.h" #include "ast/rewriter/rewriter_def.h" #include "ast/rewriter/var_subst.h" #include "ast/ast_util.h" #include "util/obj_pair_hashtable.h" #include "ast/ast_pp.h" class sine_tactic : public tactic { ast_manager& m; params_ref m_params; public: sine_tactic(ast_manager& m, params_ref const& p): m(m), m_params(p) {} tactic * translate(ast_manager & m) override { return alloc(sine_tactic, m, m_params); } void updt_params(params_ref const & p) override { } void collect_param_descrs(param_descrs & r) override { } void operator()(goal_ref const & g, goal_ref_buffer& result) override { TRACE("sine", g->display(tout);); TRACE("sine", tout << g->size();); ptr_vector new_forms; filter_expressions(g, new_forms); TRACE("sine", tout << new_forms.size();); g->reset(); for (unsigned i = 0; i < new_forms.size(); i++) { g->assert_expr(new_forms.get(i), nullptr, nullptr); } g->inc_depth(); g->updt_prec(goal::OVER); result.push_back(g.get()); TRACE("sine", result[0]->display(tout);); SASSERT(g->is_well_sorted()); } void cleanup() override { } private: typedef std::pair t_work_item; t_work_item work_item(expr * e, expr * root) { return std::pair(e, root); } void find_constants(expr * e, obj_hashtable &consts) { ptr_vector stack; stack.push_back(e); expr * curr; while (!stack.empty()) { curr = stack.back(); stack.pop_back(); if (is_app(curr) && is_uninterp(curr)) { app *a = to_app(curr); func_decl *f = a->get_decl(); consts.insert_if_not_there(f); } } } bool quantifier_matches(quantifier * q, obj_hashtable const & consts, ptr_vector & next_consts) { TRACE("sine", tout << "size of consts is "; tout << consts.size(); tout << "\n"; for (func_decl* f : consts) tout << f->get_name() << "\n";); bool matched = false; for (unsigned i = 0; i < q->get_num_patterns(); i++) { bool p_matched = true; ptr_vector stack; expr * curr; // patterns are wrapped with "pattern" if (!m.is_pattern(q->get_pattern(i), stack)) continue; while (!stack.empty()) { curr = stack.back(); stack.pop_back(); if (is_app(curr)) { app * a = to_app(curr); func_decl * f = a->get_decl(); if (!consts.contains(f)) { TRACE("sine", tout << mk_pp(f, m) << "\n";); p_matched = false; next_consts.push_back(f); break; } for (unsigned j = 0; j < a->get_num_args(); j++) stack.push_back(a->get_arg(j)); } } if (p_matched) { matched = true; break; } } return matched; } void filter_expressions(goal_ref const & g, ptr_vector & new_exprs) { obj_map > const2exp; obj_map > exp2const; obj_map > const2quantifier; obj_hashtable consts; vector stack; t_work_item curr; for (unsigned i = 0; i < g->size(); i++) stack.push_back(work_item(g->form(i), g->form(i))); while (!stack.empty()) { curr = stack.back(); stack.pop_back(); if (is_app(curr.first) && is_uninterp(curr.first)) { app * a = to_app(curr.first); func_decl * f = a->get_decl(); if (!consts.contains(f)) { consts.insert(f); if (const2quantifier.contains(f)) { for (auto const& p : const2quantifier[f]) stack.push_back(p); const2quantifier.remove(f); } } if (!const2exp.contains(f)) { const2exp.insert(f, obj_hashtable()); } if (!const2exp[f].contains(curr.second)) { const2exp[f].insert(curr.second); } if (!exp2const.contains(curr.second)) { exp2const.insert(curr.second, obj_hashtable()); } if (!exp2const[curr.second].contains(f)) { exp2const[curr.second].insert(f); } for (unsigned i = 0; i < a->get_num_args(); i++) { stack.push_back(work_item(a->get_arg(i), curr.second)); } } else if (is_quantifier(curr.first)) { quantifier *q = to_quantifier(curr.first); if (is_forall(q)) { if (q->has_patterns()) { ptr_vector next_consts; if (quantifier_matches(q, consts, next_consts)) { stack.push_back(work_item(q->get_expr(), curr.second)); } else { for (unsigned i = 0; i < next_consts.size(); i++) { func_decl *c = next_consts.get(i); if (!const2quantifier.contains(c)) { const2quantifier.insert(c, obj_pair_hashtable()); } if (!const2quantifier[c].contains(curr)) { const2quantifier[c].insert(curr); } } } } else { stack.push_back(work_item(q->get_expr(), curr.second)); } } else if (is_exists(q)) { stack.push_back(work_item(q->get_expr(), curr.second)); } } } // ok, now we just need to find the connected component of the last term obj_hashtable visited; ptr_vector to_visit; to_visit.push_back(g->form(g->size() - 1)); expr * visiting; while (!to_visit.empty()) { visiting = to_visit.back(); to_visit.pop_back(); visited.insert(visiting); for (func_decl* f : exp2const[visiting]) for (expr* e : const2exp[f]) { if (!visited.contains(e)) to_visit.push_back(e); } } for (unsigned i = 0; i < g->size(); i++) { if (visited.contains(g->form(i))) new_exprs.push_back(g->form(i)); } } }; tactic * mk_sine_tactic(ast_manager & m, params_ref const & p) { return alloc(sine_tactic, m, p); } z3-z3-4.8.7/src/tactic/sine_filter.h000066400000000000000000000007531356505360400171460ustar00rootroot00000000000000/*++ Copyright (c) 2016 Microsoft Corporation Module Name: sine_filter.h Abstract: Tactic that performs Sine Qua Non premise selection Author: Doug Woos Revision History: --*/ #ifndef SINE_TACTIC_H_ #define SINE_TACTIC_H_ #include "util/params.h" class ast_manager; class tactic; tactic * mk_sine_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("sine-filter", "eliminate premises using Sine Qua Non", "mk_sine_tactic(m, p)") */ #endif z3-z3-4.8.7/src/tactic/sls/000077500000000000000000000000001356505360400152665ustar00rootroot00000000000000z3-z3-4.8.7/src/tactic/sls/CMakeLists.txt000066400000000000000000000003751356505360400200330ustar00rootroot00000000000000z3_add_component(sls_tactic SOURCES bvsls_opt_engine.cpp sls_engine.cpp sls_tactic.cpp COMPONENT_DEPENDENCIES bv_tactics core_tactics normal_forms tactic PYG_FILES sls_params.pyg TACTIC_HEADERS sls_tactic.h ) z3-z3-4.8.7/src/tactic/sls/bvsls_opt_engine.cpp000066400000000000000000000301731356505360400213360ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: bvsls_opt_engine.cpp Abstract: Optimization extensions to bvsls Author: Christoph (cwinter) 2014-03-28 Notes: --*/ #include "ast/normal_forms/nnf.h" #include "tactic/sls/bvsls_opt_engine.h" bvsls_opt_engine::bvsls_opt_engine(ast_manager & m, params_ref const & p) : sls_engine(m, p), m_hard_tracker(sls_engine::m_tracker), m_obj_tracker(m, m_bv_util, m_mpz_manager, m_powers), m_obj_evaluator(m, m_bv_util, m_obj_tracker, m_mpz_manager, m_powers) { m_best_model = alloc(model, m); } bvsls_opt_engine::~bvsls_opt_engine() { } bvsls_opt_engine::optimization_result bvsls_opt_engine::optimize( expr_ref const & objective, model_ref initial_model, bool _maximize) { SASSERT(m_bv_util.is_bv(objective)); TRACE("sls_opt", tout << "objective: " << (_maximize?"maximize":"minimize") << " " << mk_ismt2_pp(objective, m()) << std::endl;); m_hard_tracker.initialize(m_assertions); setup_opt_tracker(objective, _maximize); if (initial_model.get() != nullptr) { TRACE("sls_opt", tout << "Initial model provided: " << std::endl; for (unsigned i = 0; i < initial_model->get_num_constants(); i++) { func_decl * fd = initial_model->get_constant(i); expr * val = initial_model->get_const_interp(fd); tout << fd->get_name() << " := " << mk_ismt2_pp(val, m()) << std::endl; }); m_hard_tracker.set_model(initial_model); m_evaluator.update_all(); } optimization_result res(m_manager); lbool is_sat = m_hard_tracker.is_sat() ? l_true : l_undef; TRACE("sls_opt", tout << "initial model is sat? " << is_sat << std::endl;); for (m_stats.m_restarts = 0; m_stats.m_restarts < m_max_restarts; m_stats.m_restarts++) { mpz old_best; m_mpz_manager.set(old_best, m_best_model_score); if (is_sat != l_true) { do { checkpoint(); IF_VERBOSE(1, verbose_stream() << "Satisfying... restarts left:" << (m_max_restarts - m_stats.m_restarts) << std::endl;); is_sat = search(); if (is_sat == l_undef) m_hard_tracker.randomize(m_assertions); } while (is_sat != l_true && m_stats.m_restarts++ < m_max_restarts); } if (is_sat == l_true) { IF_VERBOSE(1, verbose_stream() << "Optimizing... restarts left:" << (m_max_restarts - m_stats.m_restarts) << std::endl;); res.is_sat = l_true; m_obj_tracker.set_model(m_hard_tracker.get_model()); m_obj_evaluator.update_all(); expr_ref local_best = maximize(); if ((_maximize && m_mpz_manager.gt(m_best_model_score, old_best)) || (!_maximize && m_mpz_manager.lt(m_best_model_score, old_best))) { res.optimum = local_best; } } m_hard_tracker.randomize(m_assertions); m_evaluator.update_all(); is_sat = m_hard_tracker.is_sat() ? l_true : l_undef; } TRACE("sls_opt", tout << "sat: " << res.is_sat << "; optimum: " << mk_ismt2_pp(res.optimum, m()) << std::endl;); return res; } void bvsls_opt_engine::setup_opt_tracker(expr_ref const & objective, bool _max) { expr_ref obj(m_manager); obj = objective; if (!_max) obj = m_bv_util.mk_bv_neg(objective); m_obj_e = obj.get(); m_obj_bv_sz = m_bv_util.get_bv_size(m_obj_e); ptr_vector objs; objs.push_back(m_obj_e); m_obj_tracker.initialize(objs); } expr_ref bvsls_opt_engine::maximize() { SASSERT(m_hard_tracker.is_sat()); TRACE("sls_opt", tout << "Initial opt model:" << std::endl; m_obj_tracker.show_model(tout);); mpz score, old_score, max_score, new_value; unsigned new_const = (unsigned)-1, new_bit = 0; ptr_vector consts = m_obj_tracker.get_constants(); move_type move; m_mpz_manager.set(score, top_score()); m_mpz_manager.set(max_score, m_powers(m_obj_bv_sz)); m_mpz_manager.dec(max_score); IF_VERBOSE(10, verbose_stream() << "Initial score: " << m_mpz_manager.to_string(score) << std::endl;); save_model(score); while (m_mpz_manager.lt(score, max_score) && check_restart(m_stats.m_moves)) { checkpoint(); m_stats.m_moves++; m_mpz_manager.set(old_score, score); new_const = (unsigned)-1; mpz score(0); m_mpz_manager.set(score, find_best_move(consts, score, new_const, new_value, new_bit, move, max_score, m_obj_e)); if (new_const == static_cast(-1)) { m_mpz_manager.set(score, old_score); if (m_mpz_manager.gt(score, m_best_model_score)) save_model(score); if (!randomize_wrt_hard()) { // Can't improve and can't randomize; can't do anything other than bail out. TRACE("sls_opt", tout << "Got stuck; bailing out." << std::endl;); IF_VERBOSE(10, verbose_stream() << "No local improvements possible." << std::endl;); goto bailout; } m_mpz_manager.set(score, top_score()); } else { m_stats.m_moves++; TRACE("sls_opt", tout << "New optimum: " << m_mpz_manager.to_string(score) << std::endl;); IF_VERBOSE(10, verbose_stream() << "New optimum: " << m_mpz_manager.to_string(score) << std::endl;); func_decl * fd = consts[new_const]; incremental_score(fd, new_value); m_obj_evaluator.update(fd, new_value); m_mpz_manager.set(score, top_score()); } } bailout: m_mpz_manager.del(new_value); expr_ref res(m_manager); res = m_bv_util.mk_numeral(m_best_model_score, m_obj_bv_sz); return res; } void bvsls_opt_engine::save_model(mpz const & score) { model_ref mdl = m_hard_tracker.get_model(); model_ref obj_mdl = m_obj_tracker.get_model(); for (unsigned i = 0; i < obj_mdl->get_num_constants(); i++) { func_decl * fd = obj_mdl->get_constant(i); expr * val = obj_mdl->get_const_interp(fd); if (mdl->has_interpretation(fd)) { if (mdl->get_const_interp(fd) != val) TRACE("sls_opt", tout << "model disagreement on " << fd->get_name() << ": " << mk_ismt2_pp(val, m()) << " != " << mk_ismt2_pp(mdl->get_const_interp(fd), m()) << std::endl;); SASSERT(mdl->get_const_interp(fd) == val); } else mdl->register_decl(fd, val); } m_best_model = mdl; m_mpz_manager.set(m_best_model_score, score); } // checks whether the score outcome of a given move is better than the previous score bool bvsls_opt_engine::what_if( func_decl * fd, const unsigned & fd_inx, const mpz & temp, mpz & best_score, unsigned & best_const, mpz & best_value) { #if _EARLY_PRUNE_ double r = incremental_score_prune(fd, temp); #else double r = incremental_score(fd, temp); #endif if (r >= 1.0 && m_hard_tracker.is_sat()) { m_obj_evaluator.update(fd, temp); mpz cur_best(0); m_mpz_manager.set(cur_best, top_score()); TRACE("sls_whatif", tout << "WHAT IF " << fd->get_name() << " WERE " << m_mpz_manager.to_string(temp) << " --> " << r << "; score=" << m_mpz_manager.to_string(cur_best) << std::endl;); if (m_mpz_manager.gt(cur_best, best_score)) { m_mpz_manager.set(best_score, cur_best); best_const = fd_inx; m_mpz_manager.set(best_value, temp); return true; } } else { TRACE("sls_whatif_failed", tout << "WHAT IF " << fd->get_name() << " WERE " << m_mpz_manager.to_string(temp) << " --> unsatisfied hard constraints" << std::endl;); } return false; } mpz bvsls_opt_engine::find_best_move( ptr_vector & to_evaluate, mpz & score, unsigned & best_const, mpz & best_value, unsigned & new_bit, move_type & move, mpz const & max_score, expr * objective) { mpz old_value, temp; #if _USE_MUL3_ || _USE_UNARY_MINUS_ mpz temp2; #endif unsigned bv_sz; mpz new_score; m_mpz_manager.set(new_score, score); for (unsigned i = 0; i < to_evaluate.size() && m_mpz_manager.lt(new_score, max_score); i++) { func_decl * fd = to_evaluate[i]; sort * srt = fd->get_range(); bv_sz = (m_manager.is_bool(srt)) ? 1 : m_bv_util.get_bv_size(srt); m_mpz_manager.set(old_value, m_obj_tracker.get_value(fd)); // first try to flip every bit for (unsigned j = 0; j < bv_sz && m_mpz_manager.lt(new_score, max_score); j++) { // What would happen if we flipped bit #i ? mk_flip(srt, old_value, j, temp); if (what_if(fd, i, temp, new_score, best_const, best_value)) { new_bit = j; move = MV_FLIP; } } if (m_bv_util.is_bv_sort(srt) && bv_sz > 1) { #if _USE_ADDSUB_ if (!m_mpz_manager.is_even(old_value)) { // for odd values, try +1 mk_inc(bv_sz, old_value, temp); if (what_if(fd, i, temp, new_score, best_const, best_value)) move = MV_INC; } else { // for even values, try -1 mk_dec(bv_sz, old_value, temp); if (what_if(fd, i, temp, new_score, best_const, best_value)) move = MV_DEC; } #endif // try inverting mk_inv(bv_sz, old_value, temp); if (what_if(fd, i, temp, new_score, best_const, best_value)) move = MV_INV; #if _USE_UNARY_MINUS_ mk_inc(bv_sz, temp, temp2); if (what_if(fd, i, temp2, new_score, best_const, best_value)) move = MV_UMIN; #endif #if _USE_MUL2DIV2_ // try multiplication by 2 mk_mul2(bv_sz, old_value, temp); if (what_if(fd, i, temp, new_score, best_const, best_value)) move = MV_MUL2; #if _USE_MUL3_ // try multiplication by 3 mk_add(bv_sz, old_value, temp, temp2); if (what_if(fd, i, temp2, new_score, best_const, best_value)) move = MV_MUL3; #endif // try division by 2 mk_div2(bv_sz, old_value, temp); if (what_if(fd, i, temp, new_score, best_const, best_value)) move = MV_DIV2; #endif } // reset to what it was before //double check = incremental_score(fd, old_value); m_obj_evaluator.update(fd, old_value); } m_mpz_manager.del(old_value); m_mpz_manager.del(temp); #if _USE_MUL3_ m_mpz_manager.del(temp2); #endif return new_score; } bool bvsls_opt_engine::randomize_wrt_hard() { ptr_vector consts = m_obj_tracker.get_constants(); unsigned csz = consts.size(); unsigned retry_count = csz; while (retry_count-- > 0) { unsigned ri = (m_obj_tracker.get_random_uint((csz < 16) ? 4 : (csz < 256) ? 8 : (csz < 4096) ? 12 : (csz < 65536) ? 16 : 32)) % csz; func_decl * random_fd = consts[ri]; // Random constant mpz random_val; // random value. m_mpz_manager.set(random_val, m_obj_tracker.get_random(random_fd->get_range())); mpz old_value; m_mpz_manager.set(old_value, m_obj_tracker.get_value(random_fd)); if (!m_mpz_manager.eq(random_val, old_value)) { m_evaluator.update(random_fd, random_val); if (m_hard_tracker.is_sat()) { TRACE("sls_opt", tout << "Randomizing " << random_fd->get_name() << " to " << m_mpz_manager.to_string(random_val) << std::endl;); m_obj_evaluator.update(random_fd, random_val); return true; } else m_evaluator.update(random_fd, old_value); } } return false; } z3-z3-4.8.7/src/tactic/sls/bvsls_opt_engine.h000066400000000000000000000043641356505360400210060ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: bvsls_opt_engine.h Abstract: Optimization extensions to bvsls Author: Christoph (cwinter) 2014-03-28 Notes: --*/ #ifndef BVSLS_OPT_ENGINE_H_ #define BVSLS_OPT_ENGINE_H_ #include "tactic/sls/sls_engine.h" class bvsls_opt_engine : public sls_engine { sls_tracker & m_hard_tracker; sls_tracker m_obj_tracker; sls_evaluator m_obj_evaluator; model_ref m_best_model; mpz m_best_model_score; unsigned m_obj_bv_sz; expr * m_obj_e; public: bvsls_opt_engine(ast_manager & m, params_ref const & p); ~bvsls_opt_engine(); class optimization_result { public: lbool is_sat; expr_ref optimum; optimization_result(ast_manager & m) : is_sat(l_undef), optimum(m) {} optimization_result& operator=(optimization_result const& other) { is_sat = other.is_sat; optimum = other.optimum; return *this; } optimization_result(optimization_result const& other): is_sat(other.is_sat), optimum(other.optimum) { } }; optimization_result optimize(expr_ref const & objective, model_ref initial_model = model_ref(), bool maximize=true); void get_model(model_ref & result) { result = m_best_model; } protected: void setup_opt_tracker(expr_ref const & objective, bool _max); expr_ref maximize(); bool what_if(func_decl * fd, const unsigned & fd_inx, const mpz & temp, mpz & best_score, unsigned & best_const, mpz & best_value); mpz find_best_move(ptr_vector & to_evaluate, mpz & score, unsigned & best_const, mpz & best_value, unsigned & new_bit, move_type & move, mpz const & max_score, expr * objective); mpz top_score() { mpz res(0); obj_hashtable const & top_exprs = m_obj_tracker.get_top_exprs(); for (obj_hashtable::iterator it = top_exprs.begin(); it != top_exprs.end(); it++) m_mpz_manager.add(res, m_obj_tracker.get_value(*it), res); return res; } void save_model(mpz const & score); bool randomize_wrt_hard(); }; #endif z3-z3-4.8.7/src/tactic/sls/sls_engine.cpp000066400000000000000000000512751356505360400201320ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: sls_engine.cpp Abstract: A Stochastic Local Search (SLS) engine Author: Christoph (cwinter) 2014-03-19 Notes: --*/ #include // Need DBL_MAX #include "util/map.h" #include "ast/ast_smt2_pp.h" #include "ast/ast_pp.h" #include "ast/rewriter/var_subst.h" #include "model/model_pp.h" #include "tactic/tactic.h" #include "util/luby.h" #include "tactic/sls/sls_params.hpp" #include "tactic/sls/sls_engine.h" sls_engine::sls_engine(ast_manager & m, params_ref const & p) : m_manager(m), m_powers(m_mpz_manager), m_zero(m_mpz_manager.mk_z(0)), m_one(m_mpz_manager.mk_z(1)), m_two(m_mpz_manager.mk_z(2)), m_bv_util(m), m_tracker(m, m_bv_util, m_mpz_manager, m_powers), m_evaluator(m, m_bv_util, m_tracker, m_mpz_manager, m_powers) { updt_params(p); m_tracker.updt_params(p); } sls_engine::~sls_engine() { m_mpz_manager.del(m_zero); m_mpz_manager.del(m_one); m_mpz_manager.del(m_two); } void sls_engine::updt_params(params_ref const & _p) { sls_params p(_p); m_produce_models = _p.get_bool("model", false); m_max_restarts = p.max_restarts(); m_tracker.set_random_seed(p.random_seed()); m_walksat = p.walksat(); m_walksat_repick = p.walksat_repick(); m_paws_sp = p.paws_sp(); m_paws = m_paws_sp < 1024; m_wp = p.wp(); m_vns_mc = p.vns_mc(); m_vns_repick = p.vns_repick(); m_restart_base = p.restart_base(); m_restart_next = m_restart_base; m_restart_init = p.restart_init(); m_early_prune = p.early_prune(); m_random_offset = p.random_offset(); m_rescore = p.rescore(); // Andreas: Would cause trouble because repick requires an assertion being picked before which is not the case in GSAT. if (m_walksat_repick && !m_walksat) NOT_IMPLEMENTED_YET(); if (m_vns_repick && !m_walksat) NOT_IMPLEMENTED_YET(); } void sls_engine::collect_statistics(statistics& st) const { double seconds = m_stats.m_stopwatch.get_current_seconds(); st.update("sls restarts", m_stats.m_restarts); st.update("sls full evals", m_stats.m_full_evals); st.update("sls incr evals", m_stats.m_incr_evals); st.update("sls incr evals/sec", m_stats.m_incr_evals / seconds); st.update("sls FLIP moves", m_stats.m_flips); st.update("sls INC moves", m_stats.m_incs); st.update("sls DEC moves", m_stats.m_decs); st.update("sls INV moves", m_stats.m_invs); st.update("sls moves", m_stats.m_moves); st.update("sls moves/sec", m_stats.m_moves / seconds); } void sls_engine::checkpoint() { if (m_manager.canceled()) throw tactic_exception(m_manager.limit().get_cancel_msg()); } bool sls_engine::full_eval(model & mdl) { model::scoped_model_completion _scm(mdl, true); for (expr* a : m_assertions) { checkpoint(); if (!mdl.is_true(a)) { TRACE("sls", tout << "Evaluation: false\n";); return false; } } return true; } double sls_engine::top_score() { double top_sum = 0.0; for (expr* e : m_assertions) { top_sum += m_tracker.get_score(e); } TRACE("sls_top", tout << "Score distribution:"; for (expr* e : m_assertions) tout << " " << m_tracker.get_score(e); tout << " AVG: " << top_sum / (double)m_assertions.size() << std::endl;); m_tracker.set_top_sum(top_sum); return top_sum; } double sls_engine::rescore() { m_evaluator.update_all(); m_stats.m_full_evals++; return top_score(); } double sls_engine::serious_score(func_decl * fd, const mpz & new_value) { m_evaluator.serious_update(fd, new_value); m_stats.m_incr_evals++; return m_tracker.get_top_sum(); } double sls_engine::incremental_score(func_decl * fd, const mpz & new_value) { m_evaluator.update(fd, new_value); m_stats.m_incr_evals++; return m_tracker.get_top_sum(); } double sls_engine::incremental_score_prune(func_decl * fd, const mpz & new_value) { m_stats.m_incr_evals++; if (m_evaluator.update_prune(fd, new_value)) return m_tracker.get_top_sum(); else return -DBL_MAX; } // checks whether the score outcome of a given move is better than the previous score bool sls_engine::what_if( func_decl * fd, const unsigned & fd_inx, const mpz & temp, double & best_score, unsigned & best_const, mpz & best_value) { #ifdef Z3DEBUG mpz old_value; m_mpz_manager.set(old_value, m_tracker.get_value(fd)); #endif double r; if (m_early_prune) r = incremental_score_prune(fd, temp); else r = incremental_score(fd, temp); #ifdef Z3DEBUG TRACE("sls_whatif", tout << "WHAT IF " << fd->get_name() << " WERE " << m_mpz_manager.to_string(temp) << " --> " << r << std::endl;); m_mpz_manager.del(old_value); #endif // Andreas: Had this idea on my last day. Maybe we could add a noise here similar to the one that worked so well for ucb assertion selection. // r += 0.0001 * m_tracker.get_random_uint(8); // Andreas: For some reason it is important to use > here instead of >=. Probably related to preferring the LSB. if (r > best_score) { best_score = r; best_const = fd_inx; m_mpz_manager.set(best_value, temp); return true; } return false; } void sls_engine::mk_add(unsigned bv_sz, const mpz & old_value, mpz & add_value, mpz & result) { mpz temp, mask, mask2; m_mpz_manager.add(old_value, add_value, temp); m_mpz_manager.set(mask, m_powers(bv_sz)); m_mpz_manager.bitwise_not(bv_sz, mask, mask2); m_mpz_manager.bitwise_and(temp, mask2, result); m_mpz_manager.del(temp); m_mpz_manager.del(mask); m_mpz_manager.del(mask2); } void sls_engine::mk_inc(unsigned bv_sz, const mpz & old_value, mpz & incremented) { unsigned shift; m_mpz_manager.add(old_value, m_one, incremented); if (m_mpz_manager.is_power_of_two(incremented, shift) && shift == bv_sz) m_mpz_manager.set(incremented, m_zero); } void sls_engine::mk_dec(unsigned bv_sz, const mpz & old_value, mpz & decremented) { if (m_mpz_manager.is_zero(old_value)) { m_mpz_manager.set(decremented, m_powers(bv_sz)); m_mpz_manager.dec(decremented); } else m_mpz_manager.sub(old_value, m_one, decremented); } void sls_engine::mk_inv(unsigned bv_sz, const mpz & old_value, mpz & inverted) { m_mpz_manager.bitwise_not(bv_sz, old_value, inverted); } void sls_engine::mk_flip(sort * s, const mpz & old_value, unsigned bit, mpz & flipped) { m_mpz_manager.set(flipped, m_zero); if (m_bv_util.is_bv_sort(s)) { mpz mask; m_mpz_manager.set(mask, m_powers(bit)); m_mpz_manager.bitwise_xor(old_value, mask, flipped); m_mpz_manager.del(mask); } else if (m_manager.is_bool(s)) m_mpz_manager.set(flipped, (m_mpz_manager.is_zero(old_value)) ? m_one : m_zero); else NOT_IMPLEMENTED_YET(); } void sls_engine::mk_random_move(ptr_vector & unsat_constants) { unsigned rnd_mv = 0; unsigned ucc = unsat_constants.size(); unsigned rc = (m_tracker.get_random_uint((ucc < 16) ? 4 : (ucc < 256) ? 8 : (ucc < 4096) ? 12 : (ucc < 65536) ? 16 : 32)) % ucc; func_decl * fd = unsat_constants[rc]; mpz new_value; sort * srt = fd->get_range(); if (m_manager.is_bool(srt)) m_mpz_manager.set(new_value, (m_mpz_manager.is_zero(m_tracker.get_value(fd))) ? m_one : m_zero); else { if (m_mpz_manager.is_one(m_tracker.get_random_bool())) rnd_mv = 2; if (m_mpz_manager.is_one(m_tracker.get_random_bool())) rnd_mv++; // Andreas: The other option would be to scale the probability for flips according to the bit-width. /* unsigned bv_sz2 = m_bv_util.get_bv_size(srt); rnd_mv = m_tracker.get_random_uint(16) % (bv_sz2 + 3); if (rnd_mv > 3) rnd_mv = 0; */ move_type mt = (move_type)rnd_mv; // Andreas: Christoph claimed inversion doesn't make sense, let's do a flip instead. Is this really true? if (mt == MV_INV) mt = MV_FLIP; unsigned bit = 0; switch (mt) { case MV_FLIP: { unsigned bv_sz = m_bv_util.get_bv_size(srt); bit = (m_tracker.get_random_uint((bv_sz < 16) ? 4 : (bv_sz < 256) ? 8 : (bv_sz < 4096) ? 12 : (bv_sz < 65536) ? 16 : 32)) % bv_sz; mk_flip(fd->get_range(), m_tracker.get_value(fd), bit, new_value); break; } case MV_INC: mk_inc(m_bv_util.get_bv_size(fd->get_range()), m_tracker.get_value(fd), new_value); break; case MV_DEC: mk_dec(m_bv_util.get_bv_size(fd->get_range()), m_tracker.get_value(fd), new_value); break; case MV_INV: mk_inv(m_bv_util.get_bv_size(fd->get_range()), m_tracker.get_value(fd), new_value); break; default: NOT_IMPLEMENTED_YET(); } TRACE("sls", tout << "Randomization candidates: "; for (unsigned i = 0; i < unsat_constants.size(); i++) tout << unsat_constants[i]->get_name() << ", "; tout << std::endl; tout << "Random move: "; switch (mt) { case MV_FLIP: tout << "Flip #" << bit << " in " << fd->get_name() << std::endl; break; case MV_INC: tout << "+1 for " << fd->get_name() << std::endl; break; case MV_DEC: tout << "-1 for " << fd->get_name() << std::endl; break; case MV_INV: tout << "NEG for " << fd->get_name() << std::endl; break; } tout << "Locally randomized model: " << std::endl; m_tracker.show_model(tout);); } m_evaluator.serious_update(fd, new_value); m_mpz_manager.del(new_value); } // finds the move that increased score the most. returns best_const = -1, if no increasing move exists. double sls_engine::find_best_move( ptr_vector & to_evaluate, double score, unsigned & best_const, mpz & best_value, unsigned & new_bit, move_type & move) { mpz old_value, temp; unsigned bv_sz; double new_score = score; // Andreas: Introducting a bit of randomization by using a random offset and a random direction to go through the candidate list. unsigned sz = to_evaluate.size(); unsigned offset = (m_random_offset) ? m_tracker.get_random_uint(16) % sz : 0; for (unsigned j = 0; j < sz; j++) { unsigned i = j + offset; if (i >= sz) i -= sz; //for (unsigned i = 0; i < to_evaluate.size(); i++) { func_decl * fd = to_evaluate[i]; sort * srt = fd->get_range(); bv_sz = (m_manager.is_bool(srt)) ? 1 : m_bv_util.get_bv_size(srt); m_mpz_manager.set(old_value, m_tracker.get_value(fd)); // first try to flip every bit for (unsigned j = 0; j < bv_sz; j++) { // What would happen if we flipped bit #i ? mk_flip(srt, old_value, j, temp); if (what_if(fd, i, temp, new_score, best_const, best_value)) { new_bit = j; move = MV_FLIP; } } if (m_bv_util.is_bv_sort(srt) && bv_sz > 1) { if (!m_mpz_manager.is_even(old_value)) { // for odd values, try +1 mk_inc(bv_sz, old_value, temp); if (what_if(fd, i, temp, new_score, best_const, best_value)) move = MV_INC; } else { // for even values, try -1 mk_dec(bv_sz, old_value, temp); if (what_if(fd, i, temp, new_score, best_const, best_value)) move = MV_DEC; } // try inverting mk_inv(bv_sz, old_value, temp); if (what_if(fd, i, temp, new_score, best_const, best_value)) move = MV_INV; } // reset to what it was before incremental_score(fd, old_value); } m_mpz_manager.del(old_value); m_mpz_manager.del(temp); return new_score; } // finds the move that increased score the most. returns best_const = -1, if no increasing move exists. double sls_engine::find_best_move_mc(ptr_vector & to_evaluate, double score, unsigned & best_const, mpz & best_value) { mpz old_value, temp, temp2; unsigned bv_sz; double new_score = score; // Andreas: Introducting a bit of randomization by using a random offset and a random direction to go through the candidate list. unsigned sz = to_evaluate.size(); unsigned offset = (m_random_offset) ? m_tracker.get_random_uint(16) % sz : 0; for (unsigned j = 0; j < sz; j++) { unsigned i = j + offset; if (i >= sz) i -= sz; //for (unsigned i = 0; i < to_evaluate.size(); i++) { func_decl * fd = to_evaluate[i]; sort * srt = fd->get_range(); bv_sz = (m_manager.is_bool(srt)) ? 1 : m_bv_util.get_bv_size(srt); m_mpz_manager.set(old_value, m_tracker.get_value(fd)); if (m_bv_util.is_bv_sort(srt) && bv_sz > 2) { for (unsigned j = 0; j < bv_sz; j++) { mk_flip(srt, old_value, j, temp); for (unsigned l = 0; l < m_vns_mc && l < bv_sz / 2; l++) { unsigned k = m_tracker.get_random_uint(16) % bv_sz; while (k == j) k = m_tracker.get_random_uint(16) % bv_sz; mk_flip(srt, temp, k, temp2); what_if(fd, i, temp2, new_score, best_const, best_value); } } } // reset to what it was before incremental_score(fd, old_value); } m_mpz_manager.del(old_value); m_mpz_manager.del(temp); m_mpz_manager.del(temp2); return new_score; } // main search loop lbool sls_engine::search() { lbool res = l_undef; double score = 0.0, old_score = 0.0; unsigned new_const = (unsigned)-1, new_bit; mpz new_value; move_type move; score = rescore(); unsigned sz = m_assertions.size(); while (check_restart(m_stats.m_moves)) { checkpoint(); m_stats.m_moves++; // Andreas: Every base restart interval ... if (m_stats.m_moves % m_restart_base == 0) { // ... potentially smooth the touched counters ... m_tracker.ucb_forget(m_assertions); // ... or normalize the top-level score. if (m_rescore) score = rescore(); } // get candidate variables ptr_vector & to_evaluate = m_tracker.get_unsat_constants(m_assertions); if (to_evaluate.empty()) { res = l_true; goto bailout; } // random walk with probability wp / 1024 if (m_wp && m_tracker.get_random_uint(10) < m_wp) { mk_random_move(to_evaluate); score = m_tracker.get_top_sum(); continue; } old_score = score; new_const = (unsigned)-1; // find best increasing move score = find_best_move(to_evaluate, score, new_const, new_value, new_bit, move); // use Monte Carlo 2-bit-flip sampling if no increasing move was found previously if (m_vns_mc && (new_const == static_cast(-1))) score = find_best_move_mc(to_evaluate, score, new_const, new_value); // repick assertion if no increasing move was found previously if (m_vns_repick && (new_const == static_cast(-1))) { expr * q = m_tracker.get_new_unsat_assertion(m_assertions); // only apply if another unsatisfied assertion actually exists if (q) { ptr_vector & to_evaluate2 = m_tracker.get_unsat_constants_walksat(q); score = find_best_move(to_evaluate2, score, new_const, new_value, new_bit, move); if (new_const != static_cast(-1)) { func_decl * fd = to_evaluate2[new_const]; score = serious_score(fd, new_value); continue; } } } // randomize if no increasing move was found if (new_const == static_cast(-1)) { score = old_score; if (m_walksat_repick) m_evaluator.randomize_local(m_assertions); else m_evaluator.randomize_local(to_evaluate); score = m_tracker.get_top_sum(); // update assertion weights if a weighting is enabled (sp < 1024) if (m_paws) { for (unsigned i = 0; i < sz; i++) { expr * q = m_assertions[i]; // smooth weights with probability sp / 1024 if (m_tracker.get_random_uint(10) < m_paws_sp) { if (m_mpz_manager.eq(m_tracker.get_value(q),m_one)) m_tracker.decrease_weight(q); } // increase weights otherwise else { if (m_mpz_manager.eq(m_tracker.get_value(q),m_zero)) m_tracker.increase_weight(q); } } } } // otherwise, apply most increasing move else { func_decl * fd = to_evaluate[new_const]; score = serious_score(fd, new_value); } } bailout: m_mpz_manager.del(new_value); return res; } void sls_engine::operator()(goal_ref const & g, model_converter_ref & mc) { if (g->inconsistent()) { mc = nullptr; return; } m_produce_models = g->models_enabled(); for (unsigned i = 0; i < g->size(); i++) assert_expr(g->form(i)); lbool res = operator()(); if (res == l_true) { report_tactic_progress("Number of flips:", m_stats.m_moves); for (unsigned i = 0; i < g->size(); i++) if (!m_mpz_manager.is_one(m_tracker.get_value(g->form(i)))) { verbose_stream() << "Terminated before all assertions were SAT!" << std::endl; NOT_IMPLEMENTED_YET(); } if (m_produce_models) { model_ref mdl = m_tracker.get_model(); mc = model2model_converter(mdl.get()); TRACE("sls_model", mc->display(tout);); } g->reset(); } else mc = nullptr; } lbool sls_engine::operator()() { m_tracker.initialize(m_assertions); m_tracker.reset(m_assertions); if (m_restart_init) m_tracker.randomize(m_assertions); lbool res = l_undef; do { checkpoint(); report_tactic_progress("Searching... restarts left:", m_max_restarts - m_stats.m_restarts); res = search(); if (res == l_undef) { if (m_restart_init) m_tracker.randomize(m_assertions); else m_tracker.reset(m_assertions); } } while (res != l_true && m_stats.m_restarts++ < m_max_restarts); verbose_stream() << "(restarts: " << m_stats.m_restarts << " flips: " << m_stats.m_moves << " fps: " << (m_stats.m_moves / m_stats.m_stopwatch.get_current_seconds()) << ")" << std::endl; return res; } /* Andreas: Needed for Armin's restart scheme if we don't want to use loops. double sls_engine::get_restart_armin(unsigned cnt_restarts) { unsigned outer_id = (unsigned)(0.5 + sqrt(0.25 + 2 * cnt_restarts)); unsigned inner_id = cnt_restarts - (outer_id - 1) * outer_id / 2; return pow((double) _RESTART_CONST_ARMIN_, (int) inner_id + 1); } */ unsigned sls_engine::check_restart(unsigned curr_value) { if (curr_value > m_restart_next) { /* Andreas: My own scheme (= 1) seems to work best. Other schemes are disabled so that we save one parameter. I leave the other versions as comments in case you want to try it again somewhen. #if _RESTART_SCHEME_ == 5 m_restart_next += (unsigned)(m_restart_base * pow(_RESTART_CONST_ARMIN_, m_stats.m_restarts)); #elif _RESTART_SCHEME_ == 4 m_restart_next += (m_stats.m_restarts & (m_stats.m_restarts + 1)) ? m_restart_base : (m_restart_base * m_stats.m_restarts + 1); #elif _RESTART_SCHEME_ == 3 m_restart_next += (unsigned)get_restart_armin(m_stats.m_restarts + 1) * m_restart_base; #elif _RESTART_SCHEME_ == 2 m_restart_next += get_luby(m_stats.m_restarts + 1) * m_restart_base; #elif _RESTART_SCHEME_ == 1 if (m_stats.m_restarts & 1) m_restart_next += m_restart_base; else m_restart_next += (2 << (m_stats.m_restarts >> 1)) * m_restart_base; #else m_restart_limit += m_restart_base; #endif */ if (m_stats.m_restarts & 1) m_restart_next += m_restart_base; else m_restart_next += (2 << (m_stats.m_restarts >> 1)) * m_restart_base; return 0; } return 1; } z3-z3-4.8.7/src/tactic/sls/sls_engine.h000066400000000000000000000076341356505360400175770ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: sls_engine.h Abstract: A Stochastic Local Search (SLS) engine Author: Christoph (cwinter) 2014-03-19 Notes: --*/ #ifndef SLS_ENGINE_H_ #define SLS_ENGINE_H_ #include "util/stopwatch.h" #include "util/lbool.h" #include "tactic/model_converter.h" #include "tactic/goal.h" #include "tactic/sls/sls_tracker.h" #include "tactic/sls/sls_evaluator.h" #include "util/statistics.h" class sls_engine { public: class stats { public: unsigned m_restarts; stopwatch m_stopwatch; unsigned m_full_evals; unsigned m_incr_evals; unsigned m_moves, m_flips, m_incs, m_decs, m_invs; stats() : m_restarts(0), m_full_evals(0), m_incr_evals(0), m_moves(0), m_flips(0), m_incs(0), m_decs(0), m_invs(0) { m_stopwatch.reset(); m_stopwatch.start(); } void reset() { m_full_evals = m_flips = m_incr_evals = 0; m_stopwatch.reset(); m_stopwatch.start(); } }; protected: ast_manager & m_manager; stats m_stats; unsynch_mpz_manager m_mpz_manager; powers m_powers; mpz m_zero, m_one, m_two; bool m_produce_models; bv_util m_bv_util; sls_tracker m_tracker; sls_evaluator m_evaluator; ptr_vector m_assertions; unsigned m_max_restarts; unsigned m_walksat; unsigned m_walksat_repick; unsigned m_wp; unsigned m_vns_mc; unsigned m_vns_repick; unsigned m_paws; unsigned m_paws_sp; unsigned m_restart_base; unsigned m_restart_next; unsigned m_restart_init; unsigned m_early_prune; unsigned m_random_offset; unsigned m_rescore; typedef enum { MV_FLIP = 0, MV_INC, MV_DEC, MV_INV } move_type; public: sls_engine(ast_manager & m, params_ref const & p); ~sls_engine(); ast_manager & m() const { return m_manager; } void updt_params(params_ref const & _p); void assert_expr(expr * e) { m_assertions.push_back(e); } // stats const & get_stats(void) { return m_stats; } void collect_statistics(statistics & st) const; void reset_statistics() { m_stats.reset(); } bool full_eval(model & mdl); void mk_add(unsigned bv_sz, const mpz & old_value, mpz & add_value, mpz & result); void mk_inc(unsigned bv_sz, const mpz & old_value, mpz & incremented); void mk_dec(unsigned bv_sz, const mpz & old_value, mpz & decremented); void mk_inv(unsigned bv_sz, const mpz & old_value, mpz & inverted); void mk_flip(sort * s, const mpz & old_value, unsigned bit, mpz & flipped); lbool search(); lbool operator()(); void operator()(goal_ref const & g, model_converter_ref & mc); protected: void checkpoint(); bool what_if(func_decl * fd, const unsigned & fd_inx, const mpz & temp, double & best_score, unsigned & best_const, mpz & best_value); double top_score(); double rescore(); double serious_score(func_decl * fd, const mpz & new_value); double incremental_score(func_decl * fd, const mpz & new_value); double incremental_score_prune(func_decl * fd, const mpz & new_value); double find_best_move(ptr_vector & to_evaluate, double score, unsigned & best_const, mpz & best_value, unsigned & new_bit, move_type & move); double find_best_move_mc(ptr_vector & to_evaluate, double score, unsigned & best_const, mpz & best_value); void mk_random_move(ptr_vector & unsat_constants); //double get_restart_armin(unsigned cnt_restarts); unsigned check_restart(unsigned curr_value); }; #endif z3-z3-4.8.7/src/tactic/sls/sls_evaluator.h000066400000000000000000001003201356505360400203160ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: sls_evaluator.h Abstract: SLS Evaluator Author: Christoph (cwinter) 2012-02-29 Notes: --*/ #ifndef SLS_EVALUATOR_H_ #define SLS_EVALUATOR_H_ #include "model/model_evaluator.h" #include "tactic/sls/sls_powers.h" #include "tactic/sls/sls_tracker.h" class sls_evaluator { ast_manager & m_manager; bv_util & m_bv_util; family_id m_basic_fid; family_id m_bv_fid; sls_tracker & m_tracker; unsynch_mpz_manager & m_mpz_manager; mpz m_zero, m_one, m_two; powers & m_powers; expr_ref_buffer m_temp_exprs; vector > m_traversal_stack; vector > m_traversal_stack_bool; public: sls_evaluator(ast_manager & m, bv_util & bvu, sls_tracker & t, unsynch_mpz_manager & mm, powers & p) : m_manager(m), m_bv_util(bvu), m_tracker(t), m_mpz_manager(mm), m_zero(m_mpz_manager.mk_z(0)), m_one(m_mpz_manager.mk_z(1)), m_two(m_mpz_manager.mk_z(2)), m_powers(p), m_temp_exprs(m) { m_bv_fid = m_bv_util.get_family_id(); m_basic_fid = m_manager.get_basic_family_id(); } ~sls_evaluator() { m_mpz_manager.del(m_zero); m_mpz_manager.del(m_one); m_mpz_manager.del(m_two); } void operator()(app * n, mpz & result) { family_id nfid = n->get_family_id(); func_decl * fd = n->get_decl(); unsigned n_args = n->get_num_args(); if (n_args == 0) { m_mpz_manager.set(result, m_tracker.get_value(n)); return; } expr * const * args = n->get_args(); m_mpz_manager.set(result, m_zero); if (nfid == m_basic_fid) { switch (n->get_decl_kind()) { case OP_AND: { m_mpz_manager.set(result, m_one); for (unsigned i = 0; i < n_args; i++) if (m_mpz_manager.neq(m_tracker.get_value(args[i]), result)) { m_mpz_manager.set(result, m_zero); break; } break; } case OP_OR: { for (unsigned i = 0; i < n_args; i++) if (m_mpz_manager.neq(m_tracker.get_value(args[i]), result)) { m_mpz_manager.set(result, m_one); break; } break; } case OP_NOT: { SASSERT(n_args == 1); const mpz & child = m_tracker.get_value(args[0]); SASSERT(m_mpz_manager.is_one(child) || m_mpz_manager.is_zero(child)); m_mpz_manager.set(result, (m_mpz_manager.is_zero(child)) ? m_one : m_zero); break; } case OP_EQ: { SASSERT(n_args >= 2); m_mpz_manager.set(result, m_one); const mpz & first = m_tracker.get_value(args[0]); for (unsigned i = 1; i < n_args; i++) if (m_mpz_manager.neq(m_tracker.get_value(args[i]), first)) { m_mpz_manager.set(result, m_zero); break; } break; } case OP_DISTINCT: { m_mpz_manager.set(result, m_one); for (unsigned i = 0; i < n_args && m_mpz_manager.is_one(result); i++) { for (unsigned j = i+1; j < n_args && m_mpz_manager.is_one(result); j++) { if (m_mpz_manager.eq(m_tracker.get_value(args[i]), m_tracker.get_value(args[j]))) m_mpz_manager.set(result, m_zero); } } break; } case OP_ITE: { SASSERT(n_args = 3); if (m_mpz_manager.is_one(m_tracker.get_value(args[0]))) m_mpz_manager.set(result, m_tracker.get_value(args[1])); else m_mpz_manager.set(result, m_tracker.get_value(args[2])); break; } default: NOT_IMPLEMENTED_YET(); } } else if (nfid == m_bv_fid) { bv_op_kind k = static_cast(fd->get_decl_kind()); switch(k) { case OP_CONCAT: { SASSERT(n_args >= 2); for (unsigned i = 0; i < n_args; i++) { if (i != 0) { const mpz & p = m_powers(m_bv_util.get_bv_size(args[i])); m_mpz_manager.mul(result, p, result); } m_mpz_manager.add(result, m_tracker.get_value(args[i]), result); } break; } case OP_EXTRACT: { SASSERT(n_args == 1); const mpz & child = m_tracker.get_value(args[0]); unsigned h = m_bv_util.get_extract_high(n); unsigned l = m_bv_util.get_extract_low(n); m_mpz_manager.rem(child, m_powers(h+1), result); // result = [h:0] of child m_mpz_manager.machine_div2k(result, l, result); break; } case OP_BADD: { SASSERT(n_args >= 2); for (unsigned i = 0; i < n_args; i++) { const mpz & next = m_tracker.get_value(args[i]); m_mpz_manager.add(result, next, result); } const mpz & p = m_powers(m_bv_util.get_bv_size(n)); m_mpz_manager.rem(result, p, result); break; } case OP_BSUB: { SASSERT(n_args == 2); const mpz & p = m_powers(m_bv_util.get_bv_size(n)); mpz temp; m_mpz_manager.sub(m_tracker.get_value(args[0]), m_tracker.get_value(args[1]), temp); m_mpz_manager.mod(temp, p, result); m_mpz_manager.del(temp); break; } case OP_BMUL: { SASSERT(n_args >= 2); m_mpz_manager.set(result, m_tracker.get_value(args[0])); for (unsigned i = 1; i < n_args; i++) { const mpz & next = m_tracker.get_value(args[i]); m_mpz_manager.mul(result, next, result); } const mpz & p = m_powers(m_bv_util.get_bv_size(n)); m_mpz_manager.rem(result, p, result); break; } case OP_BNEG: { // 2's complement unary minus SASSERT(n_args == 1); const mpz & child = m_tracker.get_value(args[0]); if (m_mpz_manager.is_zero(child)) { m_mpz_manager.set(result, m_zero); } else { unsigned bv_sz = m_bv_util.get_bv_size(n); m_mpz_manager.bitwise_not(bv_sz, child, result); m_mpz_manager.inc(result); // can't overflow } break; } case OP_BSDIV: case OP_BSDIV0: case OP_BSDIV_I: { SASSERT(n_args == 2); mpz x; m_mpz_manager.set(x, m_tracker.get_value(args[0])); mpz y; m_mpz_manager.set(y, m_tracker.get_value(args[1])); SASSERT(m_mpz_manager.is_nonneg(x) && m_mpz_manager.is_nonneg(y)); unsigned bv_sz = m_bv_util.get_bv_size(args[0]); const mpz & p = m_powers(bv_sz); const mpz & p_half = m_powers(bv_sz-1); if (x >= p_half) { m_mpz_manager.sub(x, p, x); } if (y >= p_half) { m_mpz_manager.sub(y, p, y); } if (m_mpz_manager.is_zero(y)) { if (m_mpz_manager.is_neg(x)) m_mpz_manager.set(result, m_one); else { m_mpz_manager.set(result, m_powers(m_bv_util.get_bv_size(n))); m_mpz_manager.dec(result); } } else { m_mpz_manager.machine_div(x, y, result); } if (m_mpz_manager.is_neg(result)) m_mpz_manager.add(result, p, result); m_mpz_manager.del(x); m_mpz_manager.del(y); break; } case OP_BUDIV: case OP_BUDIV0: case OP_BUDIV_I: { SASSERT(n_args == 2); mpz x; m_mpz_manager.set(x, m_tracker.get_value(args[0])); mpz y; m_mpz_manager.set(y, m_tracker.get_value(args[1])); if (m_mpz_manager.is_zero(y)) { m_mpz_manager.set(result, m_powers(m_bv_util.get_bv_size(n))); m_mpz_manager.dec(result); } else { m_mpz_manager.machine_div(x, y, result); } m_mpz_manager.del(x); m_mpz_manager.del(y); break; } case OP_BSREM: case OP_BSREM0: case OP_BSREM_I: { SASSERT(n_args == 2); mpz x; m_mpz_manager.set(x, m_tracker.get_value(args[0])); mpz y; m_mpz_manager.set(y, m_tracker.get_value(args[1])); unsigned bv_sz = m_bv_util.get_bv_size(args[0]); const mpz & p = m_powers(bv_sz); const mpz & p_half = m_powers(bv_sz-1); if (x >= p_half) { m_mpz_manager.sub(x, p, x); } if (y >= p_half) { m_mpz_manager.sub(y, p, y); } if (m_mpz_manager.is_zero(y)) { m_mpz_manager.set(result, x); } else { m_mpz_manager.rem(x, y, result); } if (m_mpz_manager.is_neg(result)) m_mpz_manager.add(result, p, result); m_mpz_manager.del(x); m_mpz_manager.del(y); break; } case OP_BUREM: case OP_BUREM0: case OP_BUREM_I: { SASSERT(n_args == 2); mpz x; m_mpz_manager.set(x, m_tracker.get_value(args[0])); mpz y; m_mpz_manager.set(y, m_tracker.get_value(args[1])); if (m_mpz_manager.is_zero(y)) { m_mpz_manager.set(result, x); } else { m_mpz_manager.mod(x, y, result); } m_mpz_manager.del(x); m_mpz_manager.del(y); break; } case OP_BSMOD: case OP_BSMOD0: case OP_BSMOD_I:{ SASSERT(n_args == 2); mpz x; m_mpz_manager.set(x, m_tracker.get_value(args[0])); mpz y; m_mpz_manager.set(y, m_tracker.get_value(args[1])); unsigned bv_sz = m_bv_util.get_bv_size(args[0]); const mpz & p = m_powers(bv_sz); const mpz & p_half = m_powers(bv_sz-1); if (x >= p_half) { m_mpz_manager.sub(x, p, x); } if (y >= p_half) { m_mpz_manager.sub(y, p, y); } if (m_mpz_manager.is_zero(y)) m_mpz_manager.set(result, x); else { bool neg_x = m_mpz_manager.is_neg(x); bool neg_y = m_mpz_manager.is_neg(y); mpz abs_x, abs_y; m_mpz_manager.set(abs_x, x); m_mpz_manager.set(abs_y, y); if (neg_x) m_mpz_manager.neg(abs_x); if (neg_y) m_mpz_manager.neg(abs_y); SASSERT(m_mpz_manager.is_nonneg(abs_x) && m_mpz_manager.is_nonneg(abs_y)); m_mpz_manager.mod(abs_x, abs_y, result); if (m_mpz_manager.is_zero(result) || (!neg_x && !neg_y)) { /* Nothing */ } else if (neg_x && !neg_y) { m_mpz_manager.neg(result); m_mpz_manager.add(result, y, result); } else if (!neg_x && neg_y) { m_mpz_manager.add(result, y, result); } else { m_mpz_manager.neg(result); } m_mpz_manager.del(abs_x); m_mpz_manager.del(abs_y); } if (m_mpz_manager.is_neg(result)) m_mpz_manager.add(result, p, result); m_mpz_manager.del(x); m_mpz_manager.del(y); break; } case OP_BAND: { SASSERT(n_args >= 2); m_mpz_manager.set(result, m_tracker.get_value(args[0])); for (unsigned i = 1; i < n_args; i++) m_mpz_manager.bitwise_and(result, m_tracker.get_value(args[i]), result); break; } case OP_BOR: { SASSERT(n_args >= 2); m_mpz_manager.set(result, m_tracker.get_value(args[0])); for (unsigned i = 1; i < n_args; i++) { m_mpz_manager.bitwise_or(result, m_tracker.get_value(args[i]), result); } break; } case OP_BXOR: { SASSERT(n_args >= 2); m_mpz_manager.set(result, m_tracker.get_value(args[0])); for (unsigned i = 1; i < n_args; i++) m_mpz_manager.bitwise_xor(result, m_tracker.get_value(args[i]), result); break; } case OP_BNAND: { SASSERT(n_args >= 2); mpz temp; unsigned bv_sz = m_bv_util.get_bv_size(n); m_mpz_manager.set(result, m_tracker.get_value(args[0])); for (unsigned i = 1; i < n_args; i++) { m_mpz_manager.bitwise_and(result, m_tracker.get_value(args[i]), temp); m_mpz_manager.bitwise_not(bv_sz, temp, result); } m_mpz_manager.del(temp); break; } case OP_BNOR: { SASSERT(n_args >= 2); mpz temp; unsigned bv_sz = m_bv_util.get_bv_size(n); m_mpz_manager.set(result, m_tracker.get_value(args[0])); for (unsigned i = 1; i < n_args; i++) { m_mpz_manager.bitwise_or(result, m_tracker.get_value(args[i]), temp); m_mpz_manager.bitwise_not(bv_sz, temp, result); } m_mpz_manager.del(temp); break; } case OP_BNOT: { SASSERT(n_args == 1); m_mpz_manager.bitwise_not(m_bv_util.get_bv_size(args[0]), m_tracker.get_value(args[0]), result); break; } case OP_ULT: case OP_ULEQ: case OP_UGT: case OP_UGEQ: { SASSERT(n_args == 2); const mpz & x = m_tracker.get_value(args[0]); const mpz & y = m_tracker.get_value(args[1]); if ((k == OP_ULT && m_mpz_manager.lt(x, y)) || (k == OP_ULEQ && m_mpz_manager.le(x, y)) || (k == OP_UGT && m_mpz_manager.gt(x, y)) || (k == OP_UGEQ && m_mpz_manager.ge(x, y))) m_mpz_manager.set(result, m_one); break; } case OP_SLT: case OP_SLEQ: case OP_SGT: case OP_SGEQ: { SASSERT(n_args == 2); mpz x; m_mpz_manager.set(x, m_tracker.get_value(args[0])); mpz y; m_mpz_manager.set(y, m_tracker.get_value(args[1])); unsigned bv_sz = m_bv_util.get_bv_size(args[0]); const mpz & p = m_powers(bv_sz); const mpz & p_half = m_powers(bv_sz-1); if (x >= p_half) { m_mpz_manager.sub(x, p, x); } if (y >= p_half) { m_mpz_manager.sub(y, p, y); } if ((k == OP_SLT && m_mpz_manager.lt(x, y)) || (k == OP_SLEQ && m_mpz_manager.le(x, y)) || (k == OP_SGT && m_mpz_manager.gt(x, y)) || (k == OP_SGEQ && m_mpz_manager.ge(x, y))) m_mpz_manager.set(result, m_one); m_mpz_manager.del(x); m_mpz_manager.del(y); break; } case OP_BIT2BOOL: { SASSERT(n_args == 1); const mpz & child = m_tracker.get_value(args[0]); m_mpz_manager.set(result, child); break; } case OP_BASHR: { SASSERT(n_args == 2); m_mpz_manager.set(result, m_tracker.get_value(args[0])); mpz first; const mpz & p = m_powers(m_bv_util.get_bv_size(args[0])-1); m_mpz_manager.bitwise_and(result, p, first); mpz shift; m_mpz_manager.set(shift, m_tracker.get_value(args[1])); mpz temp; while (!m_mpz_manager.is_zero(shift)) { m_mpz_manager.machine_div(result, m_two, temp); m_mpz_manager.add(temp, first, result); m_mpz_manager.dec(shift); } m_mpz_manager.del(first); m_mpz_manager.del(shift); m_mpz_manager.del(temp); break; } case OP_BLSHR: { SASSERT(n_args == 2); m_mpz_manager.set(result, m_tracker.get_value(args[0])); mpz shift; m_mpz_manager.set(shift, m_tracker.get_value(args[1])); while (!m_mpz_manager.is_zero(shift)) { m_mpz_manager.machine_div(result, m_two, result); m_mpz_manager.dec(shift); } m_mpz_manager.del(shift); break; } case OP_BSHL: { SASSERT(n_args == 2); m_mpz_manager.set(result, m_tracker.get_value(args[0])); mpz shift; m_mpz_manager.set(shift, m_tracker.get_value(args[1])); while (!m_mpz_manager.is_zero(shift)) { m_mpz_manager.mul(result, m_two, result); m_mpz_manager.dec(shift); } const mpz & p = m_powers(m_bv_util.get_bv_size(n)); m_mpz_manager.rem(result, p, result); m_mpz_manager.del(shift); break; } case OP_SIGN_EXT: { SASSERT(n_args == 1); m_mpz_manager.set(result, m_tracker.get_value(args[0])); break; } default: NOT_IMPLEMENTED_YET(); } } else { NOT_IMPLEMENTED_YET(); } TRACE("sls_eval", tout << "(" << fd->get_name(); for (unsigned i = 0; i < n_args; i++) tout << " " << m_mpz_manager.to_string(m_tracker.get_value(args[i])); tout << ") ---> " << m_mpz_manager.to_string(result); if (m_manager.is_bool(fd->get_range())) tout << " [Boolean]"; else tout << " [vector size: " << m_bv_util.get_bv_size(fd->get_range()) << "]"; tout << std::endl; ); SASSERT(m_mpz_manager.is_nonneg(result)); } void eval_checked(expr * n, mpz & result) { switch(n->get_kind()) { case AST_APP: { app * a = to_app(n); (*this)(a, result); unsigned n_args = a->get_num_args(); m_temp_exprs.reset(); for (unsigned i = 0; i < n_args; i++) { expr * arg = a->get_arg(i); const mpz & v = m_tracker.get_value(arg); m_temp_exprs.push_back(m_tracker.mpz2value(m_manager.get_sort(arg), v)); } expr_ref q(m_manager), temp(m_manager); q = m_manager.mk_app(a->get_decl(), m_temp_exprs.size(), m_temp_exprs.c_ptr()); model dummy_model(m_manager); model_evaluator evaluator(dummy_model); evaluator(q, temp); mpz check_res; m_tracker.value2mpz(temp, check_res); CTRACE("sls", !m_mpz_manager.eq(check_res, result), tout << "EVAL BUG: IS " << m_mpz_manager.to_string(result) << " SHOULD BE " << m_mpz_manager.to_string(check_res) << std::endl; ); SASSERT(m_mpz_manager.eq(check_res, result)); m_mpz_manager.del(check_res); break; } default: NOT_IMPLEMENTED_YET(); } } void run_serious_update(unsigned cur_depth) { // precondition: m_traversal_stack contains the entry point(s) expr_fast_mark1 visited; mpz new_value; double new_score; SASSERT(cur_depth < m_traversal_stack.size()); while (cur_depth != static_cast(-1)) { ptr_vector & cur_depth_exprs = m_traversal_stack[cur_depth]; for (unsigned i = 0; i < cur_depth_exprs.size(); i++) { expr * cur = cur_depth_exprs[i]; (*this)(to_app(cur), new_value); m_tracker.set_value(cur, new_value); new_score = m_tracker.score(cur); if (m_tracker.is_top_expr(cur)) { m_tracker.adapt_top_sum(cur, new_score, m_tracker.get_score(cur)); if (m_mpz_manager.eq(new_value,m_one)) m_tracker.make_assertion(cur); else m_tracker.break_assertion(cur); } m_tracker.set_score(cur, new_score); m_tracker.set_score_prune(cur, new_score); if (m_tracker.has_uplinks(cur)) { ptr_vector & ups = m_tracker.get_uplinks(cur); for (unsigned j = 0; j < ups.size(); j++) { expr * next = ups[j]; unsigned next_d = m_tracker.get_distance(next); SASSERT(next_d < cur_depth); if (!visited.is_marked(next)) { m_traversal_stack[next_d].push_back(next); visited.mark(next); } } } } cur_depth_exprs.reset(); cur_depth--; } m_mpz_manager.del(new_value); } void run_update(unsigned cur_depth) { // precondition: m_traversal_stack contains the entry point(s) expr_fast_mark1 visited; mpz new_value; double new_score; SASSERT(cur_depth < m_traversal_stack.size()); while (cur_depth != static_cast(-1)) { ptr_vector & cur_depth_exprs = m_traversal_stack[cur_depth]; for (unsigned i = 0; i < cur_depth_exprs.size(); i++) { expr * cur = cur_depth_exprs[i]; (*this)(to_app(cur), new_value); m_tracker.set_value(cur, new_value); new_score = m_tracker.score(cur); if (m_tracker.is_top_expr(cur)) m_tracker.adapt_top_sum(cur, new_score, m_tracker.get_score(cur)); m_tracker.set_score(cur, new_score); if (m_tracker.has_uplinks(cur)) { ptr_vector & ups = m_tracker.get_uplinks(cur); for (unsigned j = 0; j < ups.size(); j++) { expr * next = ups[j]; unsigned next_d = m_tracker.get_distance(next); SASSERT(next_d < cur_depth); if (!visited.is_marked(next)) { m_traversal_stack[next_d].push_back(next); visited.mark(next); } } } } cur_depth_exprs.reset(); cur_depth--; } m_mpz_manager.del(new_value); } void update_all() { unsigned max_depth = 0; sls_tracker::entry_point_type::iterator start = m_tracker.get_entry_points().begin(); sls_tracker::entry_point_type::iterator end = m_tracker.get_entry_points().end(); for (sls_tracker::entry_point_type::iterator it = start; it != end; it++) { expr * ep = m_tracker.get_entry_point(it->m_key); unsigned cur_depth = m_tracker.get_distance(ep); if (m_traversal_stack.size() <= cur_depth) m_traversal_stack.resize(cur_depth+1); m_traversal_stack[cur_depth].push_back(ep); if (cur_depth > max_depth) max_depth = cur_depth; } run_serious_update(max_depth); } void update(func_decl * fd, const mpz & new_value) { m_tracker.set_value(fd, new_value); expr * ep = m_tracker.get_entry_point(fd); unsigned cur_depth = m_tracker.get_distance(ep); if (m_traversal_stack.size() <= cur_depth) m_traversal_stack.resize(cur_depth+1); m_traversal_stack[cur_depth].push_back(ep); run_update(cur_depth); } void serious_update(func_decl * fd, const mpz & new_value) { m_tracker.set_value(fd, new_value); expr * ep = m_tracker.get_entry_point(fd); unsigned cur_depth = m_tracker.get_distance(ep); if (m_traversal_stack.size() <= cur_depth) m_traversal_stack.resize(cur_depth+1); m_traversal_stack[cur_depth].push_back(ep); run_serious_update(cur_depth); } unsigned run_update_bool_prune(unsigned cur_depth) { expr_fast_mark1 visited; double prune_score, new_score; unsigned pot_benefits = 0; SASSERT(cur_depth < m_traversal_stack_bool.size()); ptr_vector & cur_depth_exprs = m_traversal_stack_bool[cur_depth]; for (unsigned i = 0; i < cur_depth_exprs.size(); i++) { expr * cur = cur_depth_exprs[i]; new_score = m_tracker.score(cur); if (m_tracker.is_top_expr(cur)) m_tracker.adapt_top_sum(cur, new_score, m_tracker.get_score(cur)); prune_score = m_tracker.get_score_prune(cur); m_tracker.set_score(cur, new_score); if ((new_score > prune_score) && (m_tracker.has_pos_occ(cur))) pot_benefits = 1; if ((new_score <= prune_score) && (m_tracker.has_neg_occ(cur))) pot_benefits = 1; if (m_tracker.has_uplinks(cur)) { ptr_vector & ups = m_tracker.get_uplinks(cur); for (unsigned j = 0; j < ups.size(); j++) { expr * next = ups[j]; unsigned next_d = m_tracker.get_distance(next); SASSERT(next_d < cur_depth); if (!visited.is_marked(next)) { m_traversal_stack_bool[next_d].push_back(next); visited.mark(next); } } } } cur_depth_exprs.reset(); cur_depth--; while (cur_depth != static_cast(-1)) { ptr_vector & cur_depth_exprs = m_traversal_stack_bool[cur_depth]; if (pot_benefits) { unsigned cur_size = cur_depth_exprs.size(); for (unsigned i = 0; i < cur_size; i++) { expr * cur = cur_depth_exprs[i]; new_score = m_tracker.score(cur); if (m_tracker.is_top_expr(cur)) m_tracker.adapt_top_sum(cur, new_score, m_tracker.get_score(cur)); m_tracker.set_score(cur, new_score); if (m_tracker.has_uplinks(cur)) { ptr_vector & ups = m_tracker.get_uplinks(cur); for (unsigned j = 0; j < ups.size(); j++) { expr * next = ups[j]; unsigned next_d = m_tracker.get_distance(next); SASSERT(next_d < cur_depth); if (!visited.is_marked(next)) { m_traversal_stack_bool[next_d].push_back(next); visited.mark(next); } } } } } cur_depth_exprs.reset(); cur_depth--; } return pot_benefits; } void run_update_prune(unsigned max_depth) { // precondition: m_traversal_stack contains the entry point(s) expr_fast_mark1 visited; mpz new_value; unsigned cur_depth = max_depth; SASSERT(cur_depth < m_traversal_stack.size()); while (cur_depth != static_cast(-1)) { ptr_vector & cur_depth_exprs = m_traversal_stack[cur_depth]; for (unsigned i = 0; i < cur_depth_exprs.size(); i++) { expr * cur = cur_depth_exprs[i]; (*this)(to_app(cur), new_value); m_tracker.set_value(cur, new_value); // Andreas: Should actually always have uplinks ... if (m_tracker.has_uplinks(cur)) { ptr_vector & ups = m_tracker.get_uplinks(cur); for (unsigned j = 0; j < ups.size(); j++) { expr * next = ups[j]; unsigned next_d = m_tracker.get_distance(next); SASSERT(next_d < cur_depth); if (!visited.is_marked(next)) { if (m_manager.is_bool(next)) m_traversal_stack_bool[max_depth].push_back(next); else m_traversal_stack[next_d].push_back(next); visited.mark(next); } } } } cur_depth_exprs.reset(); cur_depth--; } m_mpz_manager.del(new_value); } unsigned update_prune(func_decl * fd, const mpz & new_value) { m_tracker.set_value(fd, new_value); expr * ep = m_tracker.get_entry_point(fd); unsigned cur_depth = m_tracker.get_distance(ep); if (m_traversal_stack_bool.size() <= cur_depth) m_traversal_stack_bool.resize(cur_depth+1); if (m_traversal_stack.size() <= cur_depth) m_traversal_stack.resize(cur_depth+1); if (m_manager.is_bool(ep)) m_traversal_stack_bool[cur_depth].push_back(ep); else { m_traversal_stack[cur_depth].push_back(ep); run_update_prune(cur_depth); } return run_update_bool_prune(cur_depth); } void randomize_local(ptr_vector & unsat_constants) { // Randomize _one_ candidate: unsigned r = m_tracker.get_random_uint(16) % unsat_constants.size(); func_decl * fd = unsat_constants[r]; mpz temp = m_tracker.get_random(fd->get_range()); serious_update(fd, temp); m_mpz_manager.del(temp); TRACE("sls", tout << "Randomization candidate: " << unsat_constants[r]->get_name() << std::endl; tout << "Locally randomized model: " << std::endl; m_tracker.show_model(tout); ); } void randomize_local(expr * e) { randomize_local(m_tracker.get_constants(e)); } void randomize_local(ptr_vector const & as) { randomize_local(m_tracker.get_unsat_constants(as)); } }; #endif z3-z3-4.8.7/src/tactic/sls/sls_params.pyg000066400000000000000000000046111356505360400201550ustar00rootroot00000000000000def_module_params('sls', export=True, description='Experimental Stochastic Local Search Solver (for QFBV only).', params=(max_memory_param(), ('max_restarts', UINT, UINT_MAX, 'maximum number of restarts'), ('walksat', BOOL, 1, 'use walksat assertion selection (instead of gsat)'), ('walksat_ucb', BOOL, 1, 'use bandit heuristic for walksat assertion selection (instead of random)'), ('walksat_ucb_constant', DOUBLE, 20.0, 'the ucb constant c in the term score + c * f(touched)'), ('walksat_ucb_init', BOOL, 0, 'initialize total ucb touched to formula size'), ('walksat_ucb_forget', DOUBLE, 1.0, 'scale touched by this factor every base restart interval'), ('walksat_ucb_noise', DOUBLE, 0.0002, 'add noise 0 <= 256 * ucb_noise to ucb score for assertion selection'), ('walksat_repick', BOOL, 1, 'repick assertion if randomizing in local minima'), ('scale_unsat', DOUBLE, 0.5, 'scale score of unsat expressions by this factor'), ('paws_init', UINT, 40, 'initial/minimum assertion weights'), ('paws_sp', UINT, 52, 'smooth assertion weights with probability paws_sp / 1024'), ('wp', UINT, 100, 'random walk with probability wp / 1024'), ('vns_mc', UINT, 0, 'in local minima, try Monte Carlo sampling vns_mc many 2-bit-flips per bit'), ('vns_repick', BOOL, 0, 'in local minima, try picking a different assertion (only for walksat)'), ('restart_base', UINT, 100, 'base restart interval given by moves per run'), ('restart_init', BOOL, 0, 'initialize to 0 or random value (= 1) after restart'), ('early_prune', BOOL, 1, 'use early pruning for score prediction'), ('random_offset', BOOL, 1, 'use random offset for candidate evaluation'), ('rescore', BOOL, 1, 'rescore/normalize top-level score every base restart interval'), ('track_unsat', BOOL, 0, 'keep a list of unsat assertions as done in SAT - currently disabled internally'), ('random_seed', UINT, 0, 'random seed') )) z3-z3-4.8.7/src/tactic/sls/sls_powers.h000066400000000000000000000015721356505360400176440ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: sls_powers.h Abstract: Power-of-2 module for SLS Author: Christoph (cwinter) 2012-02-29 Notes: --*/ #ifndef SLS_POWERS_H_ #define SLS_POWERS_H_ #include "util/mpz.h" class powers : public u_map { unsynch_mpz_manager & m; public: powers(unsynch_mpz_manager & m) : m(m) {} ~powers() { for (iterator it = begin(); it != end(); it++) { m.del(*it->m_value); dealloc(it->m_value); } } const mpz & operator()(unsigned n) { u_map::iterator it = find_iterator(n); if (it != end()) return *it->m_value; else { mpz * new_obj = alloc(mpz); m.mul2k(m.mk_z(1), n, *new_obj); insert(n, new_obj); return *new_obj; } } }; #endif z3-z3-4.8.7/src/tactic/sls/sls_tactic.cpp000066400000000000000000000101511356505360400201200ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: sls_tactic.h Abstract: A Stochastic Local Search (SLS) tactic Author: Christoph (cwinter) 2012-02-29 Notes: --*/ #include "ast/normal_forms/nnf.h" #include "tactic/core/solve_eqs_tactic.h" #include "tactic/bv/bv_size_reduction_tactic.h" #include "tactic/bv/max_bv_sharing_tactic.h" #include "tactic/core/simplify_tactic.h" #include "tactic/core/propagate_values_tactic.h" #include "tactic/core/ctx_simplify_tactic.h" #include "tactic/core/elim_uncnstr_tactic.h" #include "tactic/core/nnf_tactic.h" #include "util/stopwatch.h" #include "tactic/sls/sls_tactic.h" #include "tactic/sls/sls_params.hpp" #include "tactic/sls/sls_engine.h" class sls_tactic : public tactic { ast_manager & m; params_ref m_params; sls_engine * m_engine; public: sls_tactic(ast_manager & _m, params_ref const & p): m(_m), m_params(p) { m_engine = alloc(sls_engine, m, p); } tactic * translate(ast_manager & m) override { return alloc(sls_tactic, m, m_params); } ~sls_tactic() override { dealloc(m_engine); } void updt_params(params_ref const & p) override { m_params = p; m_engine->updt_params(p); } void collect_param_descrs(param_descrs & r) override { sls_params::collect_param_descrs(r); } void operator()(goal_ref const & g, goal_ref_buffer & result) override { SASSERT(g->is_well_sorted()); result.reset(); TRACE("sls", g->display(tout);); tactic_report report("sls", *g); model_converter_ref mc; m_engine->operator()(g, mc); g->add(mc.get()); g->inc_depth(); result.push_back(g.get()); TRACE("sls", g->display(tout);); SASSERT(g->is_well_sorted()); } void cleanup() override { sls_engine * d = alloc(sls_engine, m, m_params); std::swap(d, m_engine); dealloc(d); } void collect_statistics(statistics & st) const override { m_engine->collect_statistics(st); } void reset_statistics() override { m_engine->reset_statistics(); } }; static tactic * mk_sls_tactic(ast_manager & m, params_ref const & p) { return and_then(fail_if_not(mk_is_qfbv_probe()), // Currently only QF_BV is supported. clean(alloc(sls_tactic, m, p))); } static tactic * mk_preamble(ast_manager & m, params_ref const & p) { params_ref main_p; main_p.set_bool("elim_and", true); // main_p.set_bool("pull_cheap_ite", true); main_p.set_bool("push_ite_bv", true); main_p.set_bool("blast_distinct", true); // main_p.set_bool("udiv2mul", true); main_p.set_bool("hi_div0", true); params_ref simp2_p = p; simp2_p.set_bool("som", true); simp2_p.set_bool("pull_cheap_ite", true); simp2_p.set_bool("push_ite_bv", false); simp2_p.set_bool("local_ctx", true); simp2_p.set_uint("local_ctx_limit", 10000000); params_ref hoist_p; hoist_p.set_bool("hoist_mul", true); // hoist_p.set_bool("hoist_cmul", true); hoist_p.set_bool("som", false); params_ref gaussian_p; // conservative gaussian elimination. gaussian_p.set_uint("gaussian_max_occs", 2); params_ref ctx_p; ctx_p.set_uint("max_depth", 32); ctx_p.set_uint("max_steps", 5000000); return and_then(and_then(mk_simplify_tactic(m), mk_propagate_values_tactic(m), using_params(mk_solve_eqs_tactic(m), gaussian_p), mk_elim_uncnstr_tactic(m), mk_bv_size_reduction_tactic(m), using_params(mk_simplify_tactic(m), simp2_p)), using_params(mk_simplify_tactic(m), hoist_p), mk_max_bv_sharing_tactic(m), mk_nnf_tactic(m, p)); } tactic * mk_qfbv_sls_tactic(ast_manager & m, params_ref const & p) { tactic * t = and_then(mk_preamble(m, p), mk_sls_tactic(m, p)); t->updt_params(p); return t; } z3-z3-4.8.7/src/tactic/sls/sls_tactic.h000066400000000000000000000007731356505360400175760ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: sls_tactic.h Abstract: A Stochastic Local Search (SLS) tactic Author: Christoph (cwinter) 2012-02-29 Notes: --*/ #ifndef SLS_TACTIC_H_ #define SLS_TACTIC_H_ #include "util/params.h" class ast_manager; class tactic; tactic * mk_qfbv_sls_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("qfbv-sls", "(try to) solve using stochastic local search for QF_BV.", "mk_qfbv_sls_tactic(m, p)") */ #endif z3-z3-4.8.7/src/tactic/sls/sls_tracker.h000066400000000000000000001114711356505360400177600ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: sls_score_tracker.h Abstract: Score and value tracking module for SLS Author: Christoph (cwinter) 2012-02-29 Notes: --*/ #ifndef SLS_TRACKER_H_ #define SLS_TRACKER_H_ #include #include "ast/for_each_expr.h" #include "ast/ast_smt2_pp.h" #include "ast/bv_decl_plugin.h" #include "model/model.h" #include "tactic/sls/sls_params.hpp" #include "tactic/sls/sls_powers.h" class sls_tracker { ast_manager & m_manager; unsynch_mpz_manager & m_mpz_manager; bv_util & m_bv_util; powers & m_powers; random_gen m_rng; unsigned m_random_bits; unsigned m_random_bits_cnt; mpz m_zero, m_one, m_two; struct value_score { value_score() : m(nullptr), value(unsynch_mpz_manager::mk_z(0)), score(0.0), score_prune(0.0), has_pos_occ(0), has_neg_occ(0), distance(0), touched(1) {}; value_score(value_score && other) : m(other.m), value(std::move(other.value)), score(other.score), score_prune(other.score_prune), has_pos_occ(other.has_pos_occ), has_neg_occ(other.has_neg_occ), distance(other.distance), touched(other.touched) {} ~value_score() { if (m) m->del(value); } void operator=(value_score && other) { this->~value_score(); new (this) value_score(std::move(other)); } value_score& operator=(value_score& other) { UNREACHABLE(); return *this; } unsynch_mpz_manager * m; mpz value; double score; double score_prune; unsigned has_pos_occ; unsigned has_neg_occ; unsigned distance; // max distance from any root unsigned touched; }; public: typedef obj_map entry_point_type; private: typedef obj_map scores_type; typedef obj_map > uplinks_type; typedef obj_map > occ_type; obj_hashtable m_top_expr; scores_type m_scores; uplinks_type m_uplinks; entry_point_type m_entry_points; ptr_vector m_constants; ptr_vector m_temp_constants; occ_type m_constants_occ; unsigned m_last_pos; unsigned m_walksat; unsigned m_ucb; double m_ucb_constant; unsigned m_ucb_init; double m_ucb_forget; double m_ucb_noise; unsigned m_touched; double m_scale_unsat; unsigned m_paws_init; obj_map m_where_false; expr** m_list_false; unsigned m_track_unsat; obj_map m_weights; double m_top_sum; obj_hashtable m_temp_seen; public: sls_tracker(ast_manager & m, bv_util & bvu, unsynch_mpz_manager & mm, powers & p) : m_manager(m), m_mpz_manager(mm), m_bv_util(bvu), m_powers(p), m_random_bits_cnt(0), m_zero(m_mpz_manager.mk_z(0)), m_one(m_mpz_manager.mk_z(1)), m_two(m_mpz_manager.mk_z(2)) { } ~sls_tracker() { m_mpz_manager.del(m_zero); m_mpz_manager.del(m_one); m_mpz_manager.del(m_two); } void updt_params(params_ref const & _p) { sls_params p(_p); m_walksat = p.walksat(); m_ucb = p.walksat_ucb(); m_ucb_constant = p.walksat_ucb_constant(); m_ucb_init = p.walksat_ucb_init(); m_ucb_forget = p.walksat_ucb_forget(); m_ucb_noise = p.walksat_ucb_noise(); m_scale_unsat = p.scale_unsat(); m_paws_init = p.paws_init(); // Andreas: track_unsat is currently disabled because I cannot guarantee that it is not buggy. // If you want to use it, you will also need to change comments in the assertion selection. m_track_unsat = 0;//p.track_unsat(); } /* Andreas: Tried to give some measure for the formula size by the following two methods but both are not used currently. unsigned get_formula_size() { return m_scores.size(); } double get_avg_bw(goal_ref const & g) { double sum = 0.0; unsigned count = 0; for (unsigned i = 0; i < g->size(); i++) { m_temp_constants.reset(); ptr_vector const & this_decls = m_constants_occ.find(g->form(i)); unsigned sz = this_decls.size(); for (unsigned i = 0; i < sz; i++) { func_decl * fd = this_decls[i]; m_temp_constants.push_back(fd); sort * srt = fd->get_range(); sum += (m_manager.is_bool(srt)) ? 1 : m_bv_util.get_bv_size(srt); count++; } } return sum / count; }*/ inline void adapt_top_sum(expr * e, double add, double sub) { m_top_sum += m_weights.find(e) * (add - sub); } inline void set_top_sum(double new_score) { m_top_sum = new_score; } inline double get_top_sum() { return m_top_sum; } inline obj_hashtable const & get_top_exprs() { return m_top_expr; } inline bool is_sat() { for (obj_hashtable::iterator it = m_top_expr.begin(); it != m_top_expr.end(); it++) if (!m_mpz_manager.is_one(get_value(*it))) return false; return true; } inline void set_value(expr * n, const mpz & r) { SASSERT(m_scores.contains(n)); m_mpz_manager.set(m_scores.find(n).value, r); } inline void set_value(func_decl * fd, const mpz & r) { SASSERT(m_entry_points.contains(fd)); expr * ep = get_entry_point(fd); set_value(ep, r); } inline mpz & get_value(expr * n) { SASSERT(m_scores.contains(n)); return m_scores.find(n).value; } inline mpz & get_value(func_decl * fd) { SASSERT(m_entry_points.contains(fd)); expr * ep = get_entry_point(fd); return get_value(ep); } inline void set_score(expr * n, double score) { SASSERT(m_scores.contains(n)); m_scores.find(n).score = score; } inline void set_score(func_decl * fd, double score) { SASSERT(m_entry_points.contains(fd)); expr * ep = get_entry_point(fd); set_score(ep, score); } inline double & get_score(expr * n) { SASSERT(m_scores.contains(n)); return m_scores.find(n).score; } inline double & get_score(func_decl * fd) { SASSERT(m_entry_points.contains(fd)); expr * ep = get_entry_point(fd); return get_score(ep); } inline void set_score_prune(expr * n, double score) { SASSERT(m_scores.contains(n)); m_scores.find(n).score_prune = score; } inline double & get_score_prune(expr * n) { SASSERT(m_scores.contains(n)); return m_scores.find(n).score_prune; } inline unsigned has_pos_occ(expr * n) { SASSERT(m_scores.contains(n)); return m_scores.find(n).has_pos_occ; } inline unsigned has_neg_occ(expr * n) { SASSERT(m_scores.contains(n)); return m_scores.find(n).has_neg_occ; } inline unsigned get_distance(expr * n) { SASSERT(m_scores.contains(n)); return m_scores.find(n).distance; } inline void set_distance(expr * n, unsigned d) { SASSERT(m_scores.contains(n)); m_scores.find(n).distance = d; } inline expr * get_entry_point(func_decl * fd) { SASSERT(m_entry_points.contains(fd)); return m_entry_points.find(fd); } inline entry_point_type const & get_entry_points() { return m_entry_points; } inline bool has_uplinks(expr * n) { return m_uplinks.contains(n); } inline bool is_top_expr(expr * n) { return m_top_expr.contains(n); } inline ptr_vector & get_uplinks(expr * n) { SASSERT(m_uplinks.contains(n)); return m_uplinks.find(n); } inline void ucb_forget(ptr_vector & as) { if (m_ucb_forget < 1.0) { expr * e; unsigned touched_old, touched_new; for (unsigned i = 0; i < as.size(); i++) { e = as[i]; touched_old = m_scores.find(e).touched; touched_new = (unsigned)((touched_old - 1) * m_ucb_forget + 1); m_scores.find(e).touched = touched_new; m_touched += touched_new - touched_old; } } } void initialize(app * n) { // Build score table if (!m_scores.contains(n)) { value_score vs; vs.m = & m_mpz_manager; m_scores.insert(n, std::move(vs)); } // Update uplinks unsigned na = n->get_num_args(); for (unsigned i = 0; i < na; i++) { expr * c = n->get_arg(i); uplinks_type::obj_map_entry * entry = m_uplinks.insert_if_not_there2(c, ptr_vector()); entry->get_data().m_value.push_back(n); } func_decl * d = n->get_decl(); if (n->get_num_args() == 0) { if (d->get_family_id() != null_family_id) { // Interpreted constant mpz t; value2mpz(n, t); set_value(n, t); m_mpz_manager.del(t); } else { // Uninterpreted constant m_entry_points.insert_if_not_there(d, n); m_constants.push_back(d); } } } struct init_proc { ast_manager & m_manager; sls_tracker & m_tracker; init_proc(ast_manager & m, sls_tracker & tracker): m_manager(m), m_tracker(tracker) { } void operator()(var * n) {} void operator()(quantifier * n) {} void operator()(app * n) { m_tracker.initialize(n); } }; struct find_func_decls_proc { ast_manager & m_manager; ptr_vector & m_occs; find_func_decls_proc (ast_manager & m, ptr_vector & occs): m_manager(m), m_occs(occs) { } void operator()(var * n) {} void operator()(quantifier * n) {} void operator()(app * n) { if (n->get_num_args() != 0) return; func_decl * d = n->get_decl(); if (d->get_family_id() != null_family_id) return; m_occs.push_back(d); } }; void calculate_expr_distances(ptr_vector const & as) { // precondition: m_scores is set up. unsigned sz = as.size(); ptr_vector stack; for (unsigned i = 0; i < sz; i++) stack.push_back(to_app(as[i])); while (!stack.empty()) { app * cur = stack.back(); stack.pop_back(); unsigned d = get_distance(cur); for (unsigned i = 0; i < cur->get_num_args(); i++) { app * child = to_app(cur->get_arg(i)); unsigned d_child = get_distance(child); if (d >= d_child) { set_distance(child, d+1); stack.push_back(child); } } } } /* Andreas: Used this at some point to have values for the non-top-level expressions. However, it did not give better performance but even cause some additional m/o - is not used currently. void initialize_recursive(init_proc proc, expr_mark visited, expr * e) { if (m_manager.is_and(e) || m_manager.is_or(e)) { app * a = to_app(e); expr * const * args = a->get_args(); unsigned int sz = a->get_num_args(); for (unsigned int i = 0; i < sz; i++) { expr * q = args[i]; initialize_recursive(proc, visited, q); } } for_each_expr(proc, visited, e); } void initialize_recursive(expr * e) { if (m_manager.is_and(e) || m_manager.is_or(e)) { app * a = to_app(e); expr * const * args = a->get_args(); unsigned int sz = a->get_num_args(); for (unsigned int i = 0; i < sz; i++) { expr * q = args[i]; initialize_recursive(q); } } ptr_vector t; m_constants_occ.insert_if_not_there(e, t); find_func_decls_proc ffd_proc(m_manager, m_constants_occ.find(e)); expr_fast_mark1 visited; quick_for_each_expr(ffd_proc, visited, e); }*/ void initialize(ptr_vector const & as) { init_proc proc(m_manager, *this); expr_mark visited; unsigned sz = as.size(); for (unsigned i = 0; i < sz; i++) { expr * e = as[i]; if (!m_top_expr.contains(e)) m_top_expr.insert(e); for_each_expr(proc, visited, e); } visited.reset(); for (unsigned i = 0; i < sz; i++) { expr * e = as[i]; ptr_vector t; m_constants_occ.insert_if_not_there(e, t); find_func_decls_proc ffd_proc(m_manager, m_constants_occ.find(e)); expr_fast_mark1 visited; quick_for_each_expr(ffd_proc, visited, e); } calculate_expr_distances(as); TRACE("sls", tout << "Initial model:" << std::endl; show_model(tout); ); if (m_track_unsat) { m_list_false = new expr*[sz]; for (unsigned i = 0; i < sz; i++) { if (m_mpz_manager.eq(get_value(as[i]), m_zero)) break_assertion(as[i]); } } m_temp_seen.reset(); for (unsigned i = 0; i < sz; i++) { expr * e = as[i]; // initialize weights if (!m_weights.contains(e)) m_weights.insert(e, m_paws_init); // positive/negative occurrences used for early pruning setup_occs(as[i]); } // initialize ucb total touched value (individual ones are always initialized to 1) m_touched = m_ucb_init ? as.size() : 1; } void increase_weight(expr * e) { m_weights.find(e)++; } void decrease_weight(expr * e) { unsigned old_weight = m_weights.find(e); m_weights.find(e) = old_weight > m_paws_init ? old_weight - 1 : m_paws_init; } unsigned get_weight(expr * e) { return m_weights.find(e); } void make_assertion(expr * e) { if (m_track_unsat) { if (m_where_false.contains(e)) { unsigned pos = m_where_false.find(e); m_where_false.erase(e); if (pos != m_where_false.size()) { expr * q = m_list_false[m_where_false.size()]; m_list_false[pos] = q; m_where_false.find(q) = pos; } } } } void break_assertion(expr * e) { if (m_track_unsat) { if (!m_where_false.contains(e)) { unsigned pos = m_where_false.size(); m_list_false[pos] = e; m_where_false.insert(e, pos); } } } void show_model(std::ostream & out) { unsigned sz = get_num_constants(); for (unsigned i = 0; i < sz; i++) { func_decl * fd = get_constant(i); out << fd->get_name() << " = " << m_mpz_manager.to_string(get_value(fd)) << std::endl; } } void set_model(model_ref const & mdl) { for (unsigned i = 0; i < mdl->get_num_constants(); i++) { func_decl * fd = mdl->get_constant(i); expr * val = mdl->get_const_interp(fd); if (m_entry_points.contains(fd)) { if (m_manager.is_bool(val)) { set_value(fd, m_manager.is_true(val) ? m_mpz_manager.mk_z(1) : m_mpz_manager.mk_z(0)); } else if (m_bv_util.is_numeral(val)) { rational r_val; unsigned bv_sz; m_bv_util.is_numeral(val, r_val, bv_sz); const mpq& q = r_val.to_mpq(); SASSERT(m_mpz_manager.is_one(q.denominator())); set_value(fd, q.numerator()); } else NOT_IMPLEMENTED_YET(); } } } model_ref get_model() { model_ref res = alloc(model, m_manager); unsigned sz = get_num_constants(); for (unsigned i = 0; i < sz; i++) { func_decl * fd = get_constant(i); res->register_decl(fd, mpz2value(fd->get_range(), get_value(fd))); } return res; } unsigned get_num_constants() { return m_constants.size(); } ptr_vector & get_constants() { return m_constants; } func_decl * get_constant(unsigned i) { return m_constants[i]; } void set_random_seed(unsigned s) { m_rng.set_seed(s); } mpz get_random_bv(sort * s) { SASSERT(m_bv_util.is_bv_sort(s)); unsigned bv_size = m_bv_util.get_bv_size(s); mpz r; m_mpz_manager.set(r, 0); mpz temp; do { m_mpz_manager.mul(r, m_two, temp); m_mpz_manager.add(temp, get_random_bool(), r); } while (--bv_size > 0); m_mpz_manager.del(temp); return r; } mpz & get_random_bool() { if (m_random_bits_cnt == 0) { m_random_bits = m_rng(); m_random_bits_cnt = 15; // random_gen produces 15 bits of randomness. } bool val = (m_random_bits & 0x01) != 0; m_random_bits = m_random_bits >> 1; m_random_bits_cnt--; return (val) ? m_one : m_zero; } unsigned get_random_uint(unsigned bits) { if (m_random_bits_cnt == 0) { m_random_bits = m_rng(); m_random_bits_cnt = 15; // random_gen produces 15 bits of randomness. } unsigned val = 0; while (bits-- > 0) { if ((m_random_bits & 0x01) != 0) val++; val <<= 1; m_random_bits >>= 1; m_random_bits_cnt--; if (m_random_bits_cnt == 0) { m_random_bits = m_rng(); m_random_bits_cnt = 15; // random_gen produces 15 bits of randomness. } } return val; } mpz get_random(sort * s) { if (m_bv_util.is_bv_sort(s)) return get_random_bv(s); else if (m_manager.is_bool(s)) return m_mpz_manager.dup(get_random_bool()); else NOT_IMPLEMENTED_YET(); // This only works for bit-vectors for now. } void randomize(ptr_vector const & as) { TRACE("sls", tout << "Abandoned model:" << std::endl; show_model(tout); ); for (entry_point_type::iterator it = m_entry_points.begin(); it != m_entry_points.end(); it++) { func_decl * fd = it->m_key; sort * s = fd->get_range(); mpz temp = get_random(s); set_value(it->m_value, temp); m_mpz_manager.del(temp); } TRACE("sls", tout << "Randomized model:" << std::endl; show_model(tout); ); } void reset(ptr_vector const & as) { TRACE("sls", tout << "Abandoned model:" << std::endl; show_model(tout); ); for (entry_point_type::iterator it = m_entry_points.begin(); it != m_entry_points.end(); it++) { set_value(it->m_value, m_zero); } } void setup_occs(expr * n, bool negated = false) { if (m_manager.is_bool(n)) { if (m_manager.is_and(n) || m_manager.is_or(n)) { SASSERT(!negated); app * a = to_app(n); expr * const * args = a->get_args(); for (unsigned i = 0; i < a->get_num_args(); i++) { expr * child = args[i]; if (!m_temp_seen.contains(child)) { setup_occs(child, false); m_temp_seen.insert(child); } } } else if (m_manager.is_not(n)) { SASSERT(!negated); app * a = to_app(n); SASSERT(a->get_num_args() == 1); expr * child = a->get_arg(0); SASSERT(!m_manager.is_and(child) && !m_manager.is_or(child)); setup_occs(child, true); } else { if (negated) m_scores.find(n).has_neg_occ = 1; else m_scores.find(n).has_pos_occ = 1; } } else if (m_bv_util.is_bv(n)) { /* CMW: I need this for optimization. Safe to ignore? */ } else NOT_IMPLEMENTED_YET(); } double score_bool(expr * n, bool negated = false) { TRACE("sls_score", tout << ((negated)?"NEG ":"") << "BOOL: " << mk_ismt2_pp(n, m_manager) << std::endl; ); double res = 0.0; if (is_uninterp_const(n)) { const mpz & r = get_value(n); if (negated) res = (m_mpz_manager.is_one(r)) ? 0.0 : 1.0; else res = (m_mpz_manager.is_one(r)) ? 1.0 : 0.0; } else if (m_manager.is_and(n)) { SASSERT(!negated); app * a = to_app(n); expr * const * args = a->get_args(); /* Andreas: Seems to have no effect. But maybe you want to try it again at some point. double sum = 0.0; for (unsigned i = 0; i < a->get_num_args(); i++) sum += get_score(args[i]); res = sum / (double) a->get_num_args(); */ double min = 1.0; for (unsigned i = 0; i < a->get_num_args(); i++) { double cur = get_score(args[i]); if (cur < min) min = cur; } res = min; } else if (m_manager.is_or(n)) { SASSERT(!negated); app * a = to_app(n); expr * const * args = a->get_args(); double max = 0.0; for (unsigned i = 0; i < a->get_num_args(); i++) { double cur = get_score(args[i]); if (cur > max) max = cur; } res = max; } else if (m_manager.is_ite(n)) { SASSERT(!negated); app * a = to_app(n); SASSERT(a->get_num_args() == 3); const mpz & cond = get_value(a->get_arg(0)); double s_t = get_score(a->get_arg(1)); double s_f = get_score(a->get_arg(2)); res = (m_mpz_manager.is_one(cond)) ? s_t : s_f; } else if (m_manager.is_eq(n) || m_manager.is_iff(n)) { app * a = to_app(n); SASSERT(a->get_num_args() == 2); expr * arg0 = a->get_arg(0); expr * arg1 = a->get_arg(1); const mpz & v0 = get_value(arg0); const mpz & v1 = get_value(arg1); if (negated) { res = (m_mpz_manager.eq(v0, v1)) ? 0.0 : 1.0; TRACE("sls_score", tout << "V0 = " << m_mpz_manager.to_string(v0) << " ; V1 = " << m_mpz_manager.to_string(v1) << std::endl; ); } else if (m_manager.is_bool(arg0)) { res = m_mpz_manager.eq(v0, v1) ? 1.0 : 0.0; TRACE("sls_score", tout << "V0 = " << m_mpz_manager.to_string(v0) << " ; V1 = " << m_mpz_manager.to_string(v1) << std::endl; ); } else if (m_bv_util.is_bv(arg0)) { mpz diff, diff_m1; m_mpz_manager.bitwise_xor(v0, v1, diff); unsigned hamming_distance = 0; unsigned bv_sz = m_bv_util.get_bv_size(arg0); // unweighted hamming distance while (!m_mpz_manager.is_zero(diff)) { if (!m_mpz_manager.is_even(diff)) { hamming_distance++; } m_mpz_manager.machine_div(diff, m_two, diff); } res = 1.0 - (hamming_distance / (double) bv_sz); TRACE("sls_score", tout << "V0 = " << m_mpz_manager.to_string(v0) << " ; V1 = " << m_mpz_manager.to_string(v1) << " ; HD = " << hamming_distance << " ; SZ = " << bv_sz << std::endl; ); m_mpz_manager.del(diff); m_mpz_manager.del(diff_m1); } else NOT_IMPLEMENTED_YET(); } else if (m_bv_util.is_bv_ule(n)) { // x <= y app * a = to_app(n); SASSERT(a->get_num_args() == 2); const mpz & x = get_value(a->get_arg(0)); const mpz & y = get_value(a->get_arg(1)); int bv_sz = m_bv_util.get_bv_size(a->get_decl()->get_domain()[0]); if (negated) { if (m_mpz_manager.gt(x, y)) res = 1.0; else { mpz diff; m_mpz_manager.sub(y, x, diff); m_mpz_manager.inc(diff); rational n(diff); n /= rational(m_powers(bv_sz)); double dbl = n.get_double(); // In extreme cases, n is 0.9999 but to_double returns something > 1.0 res = (dbl > 1.0) ? 0.0 : (dbl < 0.0) ? 1.0 : 1.0 - dbl; m_mpz_manager.del(diff); } } else { if (m_mpz_manager.le(x, y)) res = 1.0; else { mpz diff; m_mpz_manager.sub(x, y, diff); rational n(diff); n /= rational(m_powers(bv_sz)); double dbl = n.get_double(); res = (dbl > 1.0) ? 0.0 : (dbl < 0.0) ? 1.0 : 1.0 - dbl; m_mpz_manager.del(diff); } } TRACE("sls_score", tout << "x = " << m_mpz_manager.to_string(x) << " ; y = " << m_mpz_manager.to_string(y) << " ; SZ = " << bv_sz << std::endl; ); } else if (m_bv_util.is_bv_sle(n)) { // x <= y app * a = to_app(n); SASSERT(a->get_num_args() == 2); mpz x; m_mpz_manager.set(x, get_value(a->get_arg(0))); mpz y; m_mpz_manager.set(y, get_value(a->get_arg(1))); unsigned bv_sz = m_bv_util.get_bv_size(a->get_decl()->get_domain()[0]); const mpz & p = m_powers(bv_sz); const mpz & p_half = m_powers(bv_sz-1); if (x >= p_half) { m_mpz_manager.sub(x, p, x); } if (y >= p_half) { m_mpz_manager.sub(y, p, y); } if (negated) { if (x > y) res = 1.0; else { mpz diff; m_mpz_manager.sub(y, x, diff); m_mpz_manager.inc(diff); rational n(diff); n /= p; double dbl = n.get_double(); res = (dbl > 1.0) ? 0.0 : (dbl < 0.0) ? 1.0 : 1.0 - dbl; m_mpz_manager.del(diff); } TRACE("sls_score", tout << "x = " << m_mpz_manager.to_string(x) << " ; y = " << m_mpz_manager.to_string(y) << " ; SZ = " << bv_sz << std::endl; ); } else { if (x <= y) res = 1.0; else { mpz diff; m_mpz_manager.sub(x, y, diff); SASSERT(!m_mpz_manager.is_neg(diff)); rational n(diff); n /= p; double dbl = n.get_double(); res = (dbl > 1.0) ? 0.0 : (dbl < 0.0) ? 1.0 : 1.0 - dbl; m_mpz_manager.del(diff); } TRACE("sls_score", tout << "x = " << m_mpz_manager.to_string(x) << " ; y = " << m_mpz_manager.to_string(y) << " ; SZ = " << bv_sz << std::endl; ); } m_mpz_manager.del(x); m_mpz_manager.del(y); } else if (m_manager.is_not(n)) { SASSERT(!negated); app * a = to_app(n); SASSERT(a->get_num_args() == 1); expr * child = a->get_arg(0); // Precondition: Assertion set is in NNF. // Also: careful about the unsat assertion scaling further down. if (m_manager.is_and(child) || m_manager.is_or(child)) NOT_IMPLEMENTED_YET(); res = score_bool(child, true); } else if (m_manager.is_distinct(n)) { app * a = to_app(n); unsigned pairs = 0, distinct_pairs = 0; unsigned sz = a->get_num_args(); for (unsigned i = 0; i < sz; i++) { for (unsigned j = i+1; j < sz; j++) { // pair i/j const mpz & v0 = get_value(a->get_arg(0)); const mpz & v1 = get_value(a->get_arg(1)); pairs++; if (v0 != v1) distinct_pairs++; } } res = (distinct_pairs/(double)pairs); if (negated) res = 1.0 - res; } else NOT_IMPLEMENTED_YET(); SASSERT(res >= 0.0 && res <= 1.0); app * a = to_app(n); family_id afid = a->get_family_id(); if (afid == m_bv_util.get_family_id()) if (res < 1.0) res *= m_scale_unsat; TRACE("sls_score", tout << "SCORE = " << res << std::endl; ); return res; } double score_bv(expr * n) { return 0.0; // a bv-expr is always scored as 0.0; we won't use those scores. } void value2mpz(expr * n, mpz & result) { m_mpz_manager.set(result, m_zero); if (m_manager.is_bool(n)) { m_mpz_manager.set(result, m_manager.is_true(n) ? m_one : m_zero); } else if (m_bv_util.is_bv(n)) { unsigned bv_sz = m_bv_util.get_bv_size(n); rational q; if (!m_bv_util.is_numeral(n, q, bv_sz)) NOT_IMPLEMENTED_YET(); const mpq& temp = q.to_mpq(); SASSERT(m_mpz_manager.is_one(temp.denominator())); m_mpz_manager.set(result, temp.numerator()); } else NOT_IMPLEMENTED_YET(); } expr_ref mpz2value(sort * s, const mpz & r) { expr_ref res(m_manager); if (m_manager.is_bool(s)) res = (m_mpz_manager.is_zero(r)) ? m_manager.mk_false() : m_manager.mk_true(); else if (m_bv_util.is_bv_sort(s)) { rational rat(r); res = m_bv_util.mk_numeral(rat, s); } else NOT_IMPLEMENTED_YET(); return res; } double score(expr * n) { if (m_manager.is_bool(n)) return score_bool(n); else if (m_bv_util.is_bv(n)) return score_bv(n); else NOT_IMPLEMENTED_YET(); } ptr_vector & get_constants(expr * e) { ptr_vector const & this_decls = m_constants_occ.find(e); unsigned sz = this_decls.size(); for (unsigned i = 0; i < sz; i++) { func_decl * fd = this_decls[i]; if (!m_temp_constants.contains(fd)) m_temp_constants.push_back(fd); } return m_temp_constants; } ptr_vector & get_unsat_constants_gsat(ptr_vector const & as) { unsigned sz = as.size(); if (sz == 1) { if (m_mpz_manager.neq(get_value(as[0]), m_one)) return get_constants(); } m_temp_constants.reset(); for (unsigned i = 0; i < sz; i++) { expr * q = as[i]; if (m_mpz_manager.eq(get_value(q), m_one)) continue; ptr_vector const & this_decls = m_constants_occ.find(q); unsigned sz2 = this_decls.size(); for (unsigned j = 0; j < sz2; j++) { func_decl * fd = this_decls[j]; if (!m_temp_constants.contains(fd)) m_temp_constants.push_back(fd); } } return m_temp_constants; } ptr_vector & get_unsat_constants_walksat(expr * e) { 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(); for (unsigned j = 0; j < sz; j++) { func_decl * fd = this_decls[j]; if (!m_temp_constants.contains(fd)) m_temp_constants.push_back(fd); } return m_temp_constants; } ptr_vector & get_unsat_constants(ptr_vector const & as) { if (m_walksat) { expr * e = get_unsat_assertion(as); if (!e) { m_temp_constants.reset(); return m_temp_constants; } return get_unsat_constants_walksat(e); } else return get_unsat_constants_gsat(as); } expr * get_unsat_assertion(ptr_vector const & as) { unsigned sz = as.size(); if (sz == 1) { if (m_mpz_manager.neq(get_value(as[0]), m_one)) return as[0]; else return nullptr; } m_temp_constants.reset(); unsigned pos = -1; if (m_ucb) { double max = -1.0; // Andreas: Commented things here might be used for track_unsat data structures as done in SLS for SAT. But seems to have no benefit. /* for (unsigned i = 0; i < m_where_false.size(); i++) { expr * e = m_list_false[i]; */ for (unsigned i = 0; i < sz; i++) { expr * e = as[i]; if (m_mpz_manager.neq(get_value(e), m_one)) { value_score & vscore = m_scores.find(e); // Andreas: Select the assertion with the greatest ucb score. Potentially add some noise. // double q = vscore.score + m_ucb_constant * sqrt(log((double)m_touched) / vscore.touched); double q = vscore.score + m_ucb_constant * sqrt(log((double)m_touched) / vscore.touched) + m_ucb_noise * get_random_uint(8); if (q > max) { max = q; pos = i; } } } if (pos == static_cast(-1)) return nullptr; m_touched++; m_scores.find(as[pos]).touched++; // Andreas: Also part of track_unsat data structures. Additionally disable the previous line! /* m_last_pos = pos; m_scores.find(m_list_false[pos]).touched++; return m_list_false[pos]; */ } else { // Andreas: The track_unsat data structures for random assertion selection. /* sz = m_where_false.size(); if (sz == 0) return 0; return m_list_false[get_random_uint(16) % sz]; */ unsigned cnt_unsat = 0; for (unsigned i = 0; i < sz; i++) if (m_mpz_manager.neq(get_value(as[i]), m_one) && (get_random_uint(16) % ++cnt_unsat == 0)) pos = i; if (pos == static_cast(-1)) return nullptr; } m_last_pos = pos; return as[pos]; } expr * get_new_unsat_assertion(ptr_vector const & as) { unsigned sz = as.size(); if (sz == 1) return nullptr; m_temp_constants.reset(); unsigned cnt_unsat = 0, pos = -1; for (unsigned i = 0; i < sz; i++) if ((i != m_last_pos) && m_mpz_manager.neq(get_value(as[i]), m_one) && (get_random_uint(16) % ++cnt_unsat == 0)) pos = i; if (pos == static_cast(-1)) return nullptr; return as[pos]; } }; #endif z3-z3-4.8.7/src/tactic/smtlogics/000077500000000000000000000000001356505360400164715ustar00rootroot00000000000000z3-z3-4.8.7/src/tactic/smtlogics/CMakeLists.txt000066400000000000000000000014251356505360400212330ustar00rootroot00000000000000z3_add_component(smtlogic_tactics SOURCES nra_tactic.cpp qfaufbv_tactic.cpp qfauflia_tactic.cpp qfbv_tactic.cpp qfidl_tactic.cpp qflia_tactic.cpp qflra_tactic.cpp qfnia_tactic.cpp qfnra_tactic.cpp qfufbv_ackr_model_converter.cpp qfufbv_tactic.cpp qfuf_tactic.cpp quant_tactics.cpp COMPONENT_DEPENDENCIES ackermannization aig_tactic arith_tactics bv_tactics fp muz nlsat_tactic qe sat_solver smt_tactic PYG_FILES qfufbv_tactic_params.pyg TACTIC_HEADERS nra_tactic.h qfaufbv_tactic.h qfauflia_tactic.h qfbv_tactic.h qfidl_tactic.h qflia_tactic.h qflra_tactic.h qfnia_tactic.h qfnra_tactic.h qfuf_tactic.h qfufbv_tactic.h quant_tactics.h ) z3-z3-4.8.7/src/tactic/smtlogics/nra_tactic.cpp000066400000000000000000000023301356505360400213020ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: nra_tactic.cpp Abstract: Tactic for NRA Author: Leonardo (leonardo) 2012-03-13 Notes: --*/ #include "tactic/tactical.h" #include "tactic/core/simplify_tactic.h" #include "tactic/core/propagate_values_tactic.h" #include "tactic/core/nnf_tactic.h" #include "tactic/arith/probe_arith.h" #include "smt/tactic/smt_tactic.h" #include "qe/qe_tactic.h" #include "qe/nlqsat.h" #include "qe/qe_lite.h" #include "nlsat/tactic/qfnra_nlsat_tactic.h" tactic * mk_nra_tactic(ast_manager & m, params_ref const& p) { params_ref p1 = p; p1.set_uint("seed", 11); p1.set_bool("factor", false); params_ref p2 = p; p2.set_uint("seed", 13); p2.set_bool("factor", false); return and_then( mk_simplify_tactic(m, p), mk_propagate_values_tactic(m, p), mk_qe_lite_tactic(m), mk_simplify_tactic(m, p), cond(mk_is_qfnra_probe(), or_else(try_for(mk_qfnra_nlsat_tactic(m, p), 5000), try_for(mk_qfnra_nlsat_tactic(m, p1), 10000), mk_qfnra_nlsat_tactic(m, p2)), or_else(mk_nlqsat_tactic(m, p), mk_smt_tactic(m, p)) )); } z3-z3-4.8.7/src/tactic/smtlogics/nra_tactic.h000066400000000000000000000006111356505360400207470ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: nra_tactic.h Abstract: Tactic for NRA Author: Leonardo (leonardo) 2012-03-13 Notes: --*/ #ifndef NRA_TACTIC_H_ #define NRA_TACTIC_H_ tactic * mk_nra_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("nra", "builtin strategy for solving NRA problems.", "mk_nra_tactic(m, p)") */ #endif z3-z3-4.8.7/src/tactic/smtlogics/qfaufbv_tactic.cpp000066400000000000000000000037461356505360400221700ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qfaufbv_tactic.cpp Abstract: Tactic for QF_AUFBV benchmarks. Author: Leonardo (leonardo) 2012-02-23 Notes: --*/ #include "tactic/core/solve_eqs_tactic.h" #include "tactic/core/simplify_tactic.h" #include "tactic/core/propagate_values_tactic.h" #include "tactic/bv/bit_blaster_tactic.h" #include "tactic/core/elim_uncnstr_tactic.h" #include "tactic/bv/max_bv_sharing_tactic.h" #include "tactic/bv/bv_size_reduction_tactic.h" #include "tactic/core/ctx_simplify_tactic.h" #include "tactic/smtlogics/qfbv_tactic.h" #include "ackermannization/ackermannize_bv_tactic.h" #include "smt/tactic/smt_tactic.h" static tactic * mk_qfaufbv_preamble(ast_manager & m, params_ref const & p) { params_ref simp2_p = p; simp2_p.set_bool("som", true); simp2_p.set_bool("pull_cheap_ite", true); simp2_p.set_bool("push_ite_bv", false); simp2_p.set_bool("local_ctx", true); simp2_p.set_uint("local_ctx_limit", 10000000); 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 * 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 * preamble_st = mk_qfaufbv_preamble(m, p); tactic * st = using_params( and_then(preamble_st, cond(mk_is_qfbv_probe(), mk_qfbv_tactic(m), mk_smt_tactic(m))), main_p); st->updt_params(p); return st; } z3-z3-4.8.7/src/tactic/smtlogics/qfaufbv_tactic.h000066400000000000000000000007511356505360400216260ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qfaufbv_tactic.h Abstract: Tactic for QF_AUFBV Author: Leonardo (leonardo) 2012-02-23 Notes: --*/ #ifndef QFAUFBV_TACTIC_H_ #define QFAUFBV_TACTIC_H_ #include "util/params.h" class ast_manager; class tactic; tactic * mk_qfaufbv_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("qfaufbv", "builtin strategy for solving QF_AUFBV problems.", "mk_qfaufbv_tactic(m, p)") */ #endif z3-z3-4.8.7/src/tactic/smtlogics/qfauflia_tactic.cpp000066400000000000000000000024761356505360400223250ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qfauflia_tactic.cpp Abstract: Tactic for QF_AUFLIA Author: Leonardo (leonardo) 2012-02-21 Notes: --*/ #include "tactic/tactical.h" #include "tactic/core/simplify_tactic.h" #include "tactic/core/propagate_values_tactic.h" #include "tactic/arith/propagate_ineqs_tactic.h" #include "tactic/core/solve_eqs_tactic.h" #include "tactic/core/elim_uncnstr_tactic.h" #include "smt/tactic/smt_tactic.h" tactic * mk_qfauflia_tactic(ast_manager & m, params_ref const & p) { params_ref main_p; main_p.set_bool("elim_and", true); main_p.set_bool("som", true); main_p.set_bool("sort_store", true); params_ref solver_p; solver_p.set_bool("array.simplify", false); // disable array simplifications at old_simplify module tactic * preamble_st = and_then(mk_simplify_tactic(m), mk_propagate_values_tactic(m), mk_solve_eqs_tactic(m), mk_elim_uncnstr_tactic(m), mk_simplify_tactic(m) ); tactic * st = and_then(using_params(preamble_st, main_p), using_params(mk_smt_tactic(m), solver_p)); st->updt_params(p); return st; } z3-z3-4.8.7/src/tactic/smtlogics/qfauflia_tactic.h000066400000000000000000000007611356505360400217650ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qfauflia_tactic.h Abstract: Tactic for QF_AUFLIA Author: Leonardo (leonardo) 2012-02-21 Notes: --*/ #ifndef QFAUFLIA_TACTIC_H_ #define QFAUFLIA_TACTIC_H_ #include "util/params.h" class ast_manager; class tactic; tactic * mk_qfauflia_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("qfauflia", "builtin strategy for solving QF_AUFLIA problems.", "mk_qfauflia_tactic(m, p)") */ #endif z3-z3-4.8.7/src/tactic/smtlogics/qfbv_tactic.cpp000066400000000000000000000116311356505360400214640ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qfbv_tactic.cpp Abstract: Tactic for QF_BV based on bit-blasting Author: Leonardo (leonardo) 2012-02-22 Notes: --*/ #include "tactic/tactical.h" #include "tactic/core/simplify_tactic.h" #include "tactic/core/propagate_values_tactic.h" #include "tactic/core/solve_eqs_tactic.h" #include "tactic/core/elim_uncnstr_tactic.h" #include "smt/tactic/smt_tactic.h" #include "tactic/bv/bit_blaster_tactic.h" #include "tactic/bv/bv1_blaster_tactic.h" #include "tactic/bv/max_bv_sharing_tactic.h" #include "tactic/bv/bv_size_reduction_tactic.h" #include "tactic/aig/aig_tactic.h" #include "sat/tactic/sat_tactic.h" #include "sat/sat_solver/inc_sat_solver.h" #include "ackermannization/ackermannize_bv_tactic.h" #define MEMLIMIT 300 static tactic * mk_qfbv_preamble(ast_manager& m, params_ref const& p) { params_ref solve_eq_p; // conservative gaussian elimination. solve_eq_p.set_uint("solve_eqs_max_occs", 2); params_ref simp2_p = p; simp2_p.set_bool("som", true); simp2_p.set_bool("pull_cheap_ite", true); simp2_p.set_bool("push_ite_bv", false); simp2_p.set_bool("local_ctx", true); simp2_p.set_uint("local_ctx_limit", 10000000); simp2_p.set_bool("flat", true); // required by som simp2_p.set_bool("hoist_mul", false); // required by som params_ref hoist_p; hoist_p.set_bool("hoist_mul", true); hoist_p.set_bool("som", false); return and_then( mk_simplify_tactic(m), mk_propagate_values_tactic(m), using_params(mk_solve_eqs_tactic(m), solve_eq_p), 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), // // Z3 can solve a couple of extra benchmarks by using hoist_mul // but the timeout in SMT-COMP is too small. // Moreover, it impacted negatively some easy benchmarks. // We should decide later, if we keep it or not. // using_params(mk_simplify_tactic(m), hoist_p), mk_max_bv_sharing_tactic(m), if_no_proofs(if_no_unsat_cores(mk_ackermannize_bv_tactic(m,p))) ); } static tactic * main_p(tactic* t) { params_ref p; p.set_bool("elim_and", true); p.set_bool("push_ite_bv", true); p.set_bool("blast_distinct", true); return using_params(t, p); } static tactic * mk_qfbv_tactic(ast_manager& m, params_ref const & p, tactic* sat, tactic* smt) { params_ref local_ctx_p = p; local_ctx_p.set_bool("local_ctx", true); params_ref solver_p; solver_p.set_bool("preprocess", false); // preprocessor of smt::context is not needed. params_ref big_aig_p; big_aig_p.set_bool("aig_per_assertion", false); tactic* preamble_st = mk_qfbv_preamble(m, p); tactic * st = main_p(and_then(preamble_st, // If the user sets HI_DIV0=false, then the formula may contain uninterpreted function // symbols. In this case, we should not use the `sat', but instead `smt'. Alternatively, // the UFs can be eliminated by eager ackermannization in the preamble. cond(mk_is_qfbv_eq_probe(), and_then(mk_bv1_blaster_tactic(m), using_params(smt, solver_p)), cond(mk_is_qfbv_probe(), and_then(mk_bit_blaster_tactic(m), when(mk_lt(mk_memory_probe(), mk_const_probe(MEMLIMIT)), and_then(using_params(and_then(mk_simplify_tactic(m), mk_solve_eqs_tactic(m)), local_ctx_p), if_no_proofs(cond(mk_produce_unsat_cores_probe(), mk_aig_tactic(), using_params(mk_aig_tactic(), big_aig_p))))), sat), smt)))); st->updt_params(p); return st; } tactic * mk_qfbv_tactic(ast_manager & m, params_ref const & p) { tactic * new_sat = cond(mk_produce_proofs_probe(), and_then(mk_simplify_tactic(m), mk_smt_tactic(m)), mk_psat_tactic(m, p)); return mk_qfbv_tactic(m, p, new_sat, mk_smt_tactic(m, p)); } z3-z3-4.8.7/src/tactic/smtlogics/qfbv_tactic.h000066400000000000000000000012031356505360400211230ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qfbv_tactic.h Abstract: Tactic for QF_BV based on bit-blasting Author: Leonardo (leonardo) 2012-02-22 Notes: --*/ #ifndef QFBV_TACTIC_H_ #define QFBV_TACTIC_H_ #include "util/params.h" class ast_manager; class tactic; tactic * mk_qfbv_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("qfbv", "builtin strategy for solving QF_BV problems.", "mk_qfbv_tactic(m, p)") */ tactic * mk_qfbv_preamble(ast_manager& m, params_ref const& p); tactic * mk_qfbv_tactic(ast_manager & m, params_ref const & p, tactic* sat, tactic* smt); #endif z3-z3-4.8.7/src/tactic/smtlogics/qfidl_tactic.cpp000066400000000000000000000101411356505360400216200ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qfidl_tactic.cpp Abstract: Tactic for QF_IDL Author: Leonardo (leonardo) 2012-02-21 Notes: --*/ #include "tactic/tactical.h" #include "tactic/core/simplify_tactic.h" #include "tactic/core/propagate_values_tactic.h" #include "tactic/arith/propagate_ineqs_tactic.h" #include "tactic/core/solve_eqs_tactic.h" #include "tactic/core/elim_uncnstr_tactic.h" #include "tactic/arith/normalize_bounds_tactic.h" #include "tactic/arith/fix_dl_var_tactic.h" #include "smt/tactic/smt_tactic.h" #include "tactic/arith/lia2pb_tactic.h" #include "tactic/arith/pb2bv_tactic.h" #include "tactic/arith/diff_neq_tactic.h" #include "tactic/bv/bit_blaster_tactic.h" #include "tactic/bv/max_bv_sharing_tactic.h" #include "tactic/aig/aig_tactic.h" #include "sat/tactic/sat_tactic.h" #define BIG_PROBLEM 5000 tactic * mk_qfidl_tactic(ast_manager & m, params_ref const & p) { params_ref main_p; main_p.set_bool("elim_and", true); main_p.set_bool("blast_distinct", true); main_p.set_bool("som", true); params_ref lhs_p; lhs_p.set_bool("arith_lhs", true); params_ref lia2pb_p; lia2pb_p.set_uint("lia2pb_max_bits", 4); params_ref pb2bv_p; pb2bv_p.set_uint("pb2bv_all_clauses_limit", 8); params_ref pull_ite_p; pull_ite_p.set_bool("pull_cheap_ite", true); pull_ite_p.set_bool("local_ctx", true); pull_ite_p.set_uint("local_ctx_limit", 10000000); tactic * preamble_st = and_then(and_then(mk_simplify_tactic(m), mk_fix_dl_var_tactic(m), mk_propagate_values_tactic(m), mk_elim_uncnstr_tactic(m) ), and_then(mk_solve_eqs_tactic(m), using_params(mk_simplify_tactic(m), lhs_p), mk_propagate_values_tactic(m), mk_normalize_bounds_tactic(m), mk_solve_eqs_tactic(m))); params_ref bv_solver_p; // The cardinality constraint encoding generates a lot of shared if-then-else's that can be flattened. // Several of them are simplified to and/or. If we flat them, we increase a lot the memory consumption. bv_solver_p.set_bool("flat", false); bv_solver_p.set_bool("som", false); // dynamic psm seems to work well. bv_solver_p.set_sym("gc", symbol("dyn_psm")); tactic * bv_solver = using_params(and_then(mk_simplify_tactic(m), mk_propagate_values_tactic(m), mk_solve_eqs_tactic(m), mk_max_bv_sharing_tactic(m), mk_bit_blaster_tactic(m), mk_aig_tactic(), mk_sat_tactic(m)), bv_solver_p); tactic * try2bv = and_then(using_params(mk_lia2pb_tactic(m), lia2pb_p), mk_propagate_ineqs_tactic(m), using_params(mk_pb2bv_tactic(m), pb2bv_p), fail_if(mk_not(mk_is_qfbv_probe())), bv_solver); params_ref diff_neq_p; diff_neq_p.set_uint("diff_neq_max_k", 25); tactic * st = cond(mk_and(mk_lt(mk_num_consts_probe(), mk_const_probe(static_cast(BIG_PROBLEM))), mk_and(mk_not(mk_produce_proofs_probe()), mk_not(mk_produce_unsat_cores_probe()))), using_params(and_then(preamble_st, or_else(using_params(mk_diff_neq_tactic(m), diff_neq_p), try2bv, mk_smt_tactic(m))), main_p), mk_smt_tactic(m)); st->updt_params(p); return st; } z3-z3-4.8.7/src/tactic/smtlogics/qfidl_tactic.h000066400000000000000000000007321356505360400212720ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qfidl_tactic.h Abstract: Tactic for QF_IDL Author: Leonardo (leonardo) 2012-02-21 Notes: --*/ #ifndef QFIDL_TACTIC_H_ #define QFIDL_TACTIC_H_ #include "util/params.h" class ast_manager; class tactic; tactic * mk_qfidl_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("qfidl", "builtin strategy for solving QF_IDL problems.", "mk_qfidl_tactic(m, p)") */ #endif z3-z3-4.8.7/src/tactic/smtlogics/qflia_tactic.cpp000066400000000000000000000216751356505360400216330ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qflia_tactic.cpp Abstract: Tactic for QF_LIA Author: Leonardo (leonardo) 2012-02-26 Notes: --*/ #include "tactic/tactical.h" #include "tactic/core/simplify_tactic.h" #include "tactic/core/propagate_values_tactic.h" #include "tactic/arith/propagate_ineqs_tactic.h" #include "tactic/arith/normalize_bounds_tactic.h" #include "tactic/core/solve_eqs_tactic.h" #include "tactic/core/elim_uncnstr_tactic.h" #include "smt/tactic/smt_tactic.h" #include "tactic/arith/add_bounds_tactic.h" #include "tactic/arith/pb2bv_tactic.h" #include "tactic/arith/lia2pb_tactic.h" #include "tactic/core/ctx_simplify_tactic.h" #include "tactic/bv/bit_blaster_tactic.h" #include "tactic/bv/max_bv_sharing_tactic.h" #include "tactic/aig/aig_tactic.h" #include "sat/tactic/sat_tactic.h" #include "tactic/arith/bound_manager.h" #include "tactic/arith/probe_arith.h" struct quasi_pb_probe : public probe { result operator()(goal const & g) override { bool found_non_01 = false; bound_manager bm(g.m()); bm(g); rational l, u; bool st; for (expr * t : bm) { if (bm.has_lower(t, l, st) && bm.has_upper(t, u, st) && (l.is_zero() || l.is_one()) && (u.is_zero() || u.is_one())) continue; if (found_non_01) return false; found_non_01 = true; } return true; } }; probe * mk_is_quasi_pb_probe() { return mk_and(mk_not(mk_is_unbounded_probe()), alloc(quasi_pb_probe)); } // Create SMT solver that does not use cuts static tactic * mk_no_cut_smt_tactic(ast_manager& m, unsigned rs) { params_ref solver_p; solver_p.set_sym(symbol("smt.logic"), symbol("QF_LIA")); // force smt_setup to use the new solver solver_p.set_uint("arith.branch_cut_ratio", 10000000); solver_p.set_uint("random_seed", rs); return annotate_tactic("no-cut-smt-tactic", using_params(mk_smt_tactic_using(m, false), solver_p)); } // Create SMT solver that does not use cuts static tactic * mk_no_cut_no_relevancy_smt_tactic(ast_manager& m, unsigned rs) { params_ref solver_p; solver_p.set_uint("arith.branch_cut_ratio", 10000000); solver_p.set_uint("random_seed", rs); solver_p.set_uint("relevancy", 0); return annotate_tactic("no-cut-relevancy-tactic", using_params(mk_smt_tactic_using(m, false), solver_p)); } static tactic * mk_bv2sat_tactic(ast_manager & m) { params_ref solver_p; // The cardinality constraint encoding generates a lot of shared if-then-else's that can be flattened. // Several of them are simplified to and/or. If we flat them, we increase a lot the memory consumption. solver_p.set_bool("flat", false); solver_p.set_bool("som", false); // dynamic psm seems to work well. solver_p.set_sym("gc", symbol("dyn_psm")); return using_params(and_then(mk_simplify_tactic(m), mk_propagate_values_tactic(m), mk_solve_eqs_tactic(m), mk_max_bv_sharing_tactic(m), mk_bit_blaster_tactic(m), mk_aig_tactic(), mk_sat_tactic(m, solver_p)), solver_p); } #define SMALL_SIZE 80000 static tactic * mk_pb_tactic(ast_manager & m) { params_ref pb2bv_p; pb2bv_p.set_uint("pb2bv_all_clauses_limit", 8); params_ref bv2sat_p; bv2sat_p.set_bool("ite_extra", true); return annotate_tactic( "pb-tactic", and_then(fail_if_not(mk_is_pb_probe()), fail_if(mk_produce_proofs_probe()), fail_if(mk_produce_unsat_cores_probe()), or_else(and_then(fail_if(mk_ge(mk_num_exprs_probe(), mk_const_probe(SMALL_SIZE))), fail_if_not(mk_is_ilp_probe()), // try_for(mk_mip_tactic(m), 8000), mk_fail_if_undecided_tactic()), and_then(using_params(mk_pb2bv_tactic(m), pb2bv_p), fail_if_not(mk_is_qfbv_probe()), using_params(mk_bv2sat_tactic(m), bv2sat_p))))); } static tactic * mk_lia2sat_tactic(ast_manager & m) { params_ref pb2bv_p; pb2bv_p.set_uint("pb2bv_all_clauses_limit", 8); params_ref bv2sat_p; bv2sat_p.set_bool("ite_extra", true); return annotate_tactic( "lia2sat-tactic", and_then(fail_if(mk_is_unbounded_probe()), fail_if(mk_produce_proofs_probe()), fail_if(mk_produce_unsat_cores_probe()), mk_propagate_ineqs_tactic(m), mk_normalize_bounds_tactic(m), mk_lia2pb_tactic(m), using_params(mk_pb2bv_tactic(m), pb2bv_p), fail_if_not(mk_is_qfbv_probe()), using_params(mk_bv2sat_tactic(m), bv2sat_p))); } // Try to find a model for an unbounded ILP problem. // Fails if the problem is no ILP. static tactic * mk_ilp_model_finder_tactic(ast_manager & m) { params_ref add_bounds_p1; add_bounds_p1.set_rat("add_bound_lower", rational(-16)); add_bounds_p1.set_rat("add_bound_upper", rational(15)); params_ref add_bounds_p2; add_bounds_p2.set_rat("add_bound_lower", rational(-32)); add_bounds_p2.set_rat("add_bound_upper", rational(31)); return annotate_tactic( "ilp-model-finder-tactic", and_then(fail_if_not(mk_and(mk_is_ilp_probe(), mk_is_unbounded_probe())), fail_if(mk_produce_proofs_probe()), fail_if(mk_produce_unsat_cores_probe()), mk_propagate_ineqs_tactic(m), or_else(// try_for(mk_mip_tactic(m), 5000), try_for(mk_no_cut_smt_tactic(m, 100), 2000), and_then(using_params(mk_add_bounds_tactic(m), add_bounds_p1), try_for(mk_lia2sat_tactic(m), 5000)), try_for(mk_no_cut_smt_tactic(m, 200), 5000), and_then(using_params(mk_add_bounds_tactic(m), add_bounds_p2), try_for(mk_lia2sat_tactic(m), 10000)) // , mk_mip_tactic(m) ), mk_fail_if_undecided_tactic())); } static tactic * mk_bounded_tactic(ast_manager & m) { return annotate_tactic( "bounded-tactic", and_then(fail_if(mk_is_unbounded_probe()), or_else(try_for(mk_no_cut_smt_tactic(m, 100), 5000), try_for(mk_no_cut_no_relevancy_smt_tactic(m, 200), 5000), try_for(mk_no_cut_smt_tactic(m, 300), 15000) ), mk_fail_if_undecided_tactic())); } tactic * mk_preamble_tactic(ast_manager& m) { params_ref pull_ite_p; pull_ite_p.set_bool("pull_cheap_ite", true); pull_ite_p.set_bool("push_ite_arith", false); pull_ite_p.set_bool("local_ctx", true); pull_ite_p.set_uint("local_ctx_limit", 10000000); pull_ite_p.set_bool("hoist_ite", true); params_ref ctx_simp_p; ctx_simp_p.set_uint("max_depth", 30); ctx_simp_p.set_uint("max_steps", 5000000); params_ref lhs_p; lhs_p.set_bool("arith_lhs", true); tactic * preamble_st = and_then(mk_simplify_tactic(m), mk_propagate_values_tactic(m), using_params(mk_ctx_simplify_tactic(m), ctx_simp_p), using_params(mk_simplify_tactic(m), pull_ite_p) ); preamble_st = and_then(preamble_st, mk_solve_eqs_tactic(m), mk_elim_uncnstr_tactic(m), using_params(mk_simplify_tactic(m), lhs_p) ); return preamble_st; } tactic * mk_qflia_tactic(ast_manager & m, params_ref const & p) { params_ref main_p; main_p.set_bool("elim_and", true); main_p.set_bool("som", true); main_p.set_bool("blast_distinct", true); main_p.set_uint("blast_distinct_threshold", 128); // main_p.set_bool("push_ite_arith", true); params_ref quasi_pb_p; quasi_pb_p.set_uint("lia2pb_max_bits", 64); tactic * preamble_st = mk_preamble_tactic(m); tactic * st = using_params(and_then(preamble_st, or_else(mk_ilp_model_finder_tactic(m), mk_pb_tactic(m), and_then(fail_if_not(mk_is_quasi_pb_probe()), using_params(mk_lia2sat_tactic(m), quasi_pb_p), mk_fail_if_undecided_tactic()), mk_bounded_tactic(m), mk_smt_tactic(m))), main_p); st->updt_params(p); return st; } z3-z3-4.8.7/src/tactic/smtlogics/qflia_tactic.h000066400000000000000000000012041356505360400212620ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qflia_tactic.h Abstract: Tactic for QF_LRA Author: Leonardo (leonardo) 2012-02-26 Notes: --*/ #ifndef QFLIA_TACTIC_H_ #define QFLIA_TACTIC_H_ #include "util/params.h" class ast_manager; class tactic; tactic * mk_preamble_tactic(ast_manager& m); tactic * mk_qflia_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("qflia", "builtin strategy for solving QF_LIA problems.", "mk_qflia_tactic(m, p)") */ probe * mk_is_quasi_pb_probe(); /* ADD_PROBE("is-quasi-pb", "true if the goal is quasi-pb.", "mk_is_quasi_pb_probe()") */ #endif z3-z3-4.8.7/src/tactic/smtlogics/qflra_tactic.cpp000066400000000000000000000055001356505360400216310ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qflra_tactic.cpp Abstract: Tactic for QF_LRA Author: Leonardo (leonardo) 2012-02-26 Notes: --*/ #include "tactic/tactical.h" #include "tactic/core/simplify_tactic.h" #include "tactic/core/propagate_values_tactic.h" #include "tactic/core/solve_eqs_tactic.h" #include "tactic/core/elim_uncnstr_tactic.h" #include "smt/tactic/smt_tactic.h" #include "tactic/arith/recover_01_tactic.h" #include "tactic/core/ctx_simplify_tactic.h" #include "tactic/arith/probe_arith.h" tactic * mk_qflra_tactic(ast_manager & m, params_ref const & p) { params_ref pivot_p; pivot_p.set_bool("arith.greatest_error_pivot", true); params_ref main_p = p; main_p.set_bool("elim_and", true); main_p.set_bool("som", true); main_p.set_bool("blast_distinct", true); params_ref ctx_simp_p; ctx_simp_p.set_uint("max_depth", 30); ctx_simp_p.set_uint("max_steps", 5000000); params_ref lhs_p; lhs_p.set_bool("arith_lhs", true); lhs_p.set_bool("eq2ineq", true); params_ref elim_to_real_p; elim_to_real_p.set_bool("elim_to_real", true); #if 0 tactic * mip = and_then(fail_if(mk_produce_proofs_probe()), fail_if(mk_produce_unsat_cores_probe()), using_params(and_then(and_then(mk_simplify_tactic(m), mk_recover_01_tactic(m), using_params(mk_simplify_tactic(m), elim_to_real_p), mk_propagate_values_tactic(m)), using_params(mk_ctx_simplify_tactic(m), ctx_simp_p), mk_elim_uncnstr_tactic(m), mk_solve_eqs_tactic(m), using_params(mk_simplify_tactic(m), lhs_p), using_params(mk_simplify_tactic(m), elim_to_real_p) ), main_p), fail_if(mk_not(mk_is_mip_probe())), try_for(mk_mip_tactic(m), 30000), mk_fail_if_undecided_tactic()); #endif // return using_params(or_else(mip, // using_params(mk_smt_tactic(m), pivot_p)), // p); #if 0 params_ref simplex_0, simplex_1, simplex_2; simplex_0.set_uint("lp.simplex_strategy", 0); simplex_1.set_uint("lp.simplex_strategy", 1); simplex_2.set_uint("lp.simplex_strategy", 2); return par(using_params(mk_smt_tactic(), simplex_0), using_params(mk_smt_tactic(), simplex_1), using_params(mk_smt_tactic(), simplex_2)); #else return using_params(using_params(mk_smt_tactic(m), pivot_p), p); #endif } z3-z3-4.8.7/src/tactic/smtlogics/qflra_tactic.h000066400000000000000000000007271356505360400213040ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qflra_tactic.h Abstract: Tactic for QF_LRA Author: Leonardo (leonardo) 2012-02-26 Notes: --*/ #ifndef QFLRA_TACTIC_H_ #define QFLRA_TACTIC_H_ #include "util/params.h" class ast_manager; class tactic; tactic * mk_qflra_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("qflra", "builtin strategy for solving QF_LRA problems.", "mk_qflra_tactic(m, p)") */ #endif z3-z3-4.8.7/src/tactic/smtlogics/qfnia_tactic.cpp000066400000000000000000000102041356505360400216170ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qflia_tactic.cpp Abstract: Tactic for QF_NIA Author: Leonardo (leonardo) 2012-02-28 Notes: --*/ #include "tactic/tactical.h" #include "tactic/core/simplify_tactic.h" #include "tactic/core/propagate_values_tactic.h" #include "tactic/core/solve_eqs_tactic.h" #include "tactic/core/elim_uncnstr_tactic.h" #include "smt/tactic/smt_tactic.h" #include "tactic/bv/bit_blaster_tactic.h" #include "tactic/bv/max_bv_sharing_tactic.h" #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" static tactic * mk_qfnia_bv_solver(ast_manager & m, params_ref const & p_ref) { params_ref p = p_ref; p.set_bool("flat", false); p.set_bool("hi_div0", true); p.set_bool("elim_and", true); p.set_bool("blast_distinct", true); params_ref simp2_p = p; simp2_p.set_bool("local_ctx", true); simp2_p.set_uint("local_ctx_limit", 10000000); tactic * r = using_params(and_then(mk_simplify_tactic(m), mk_propagate_values_tactic(m), using_params(mk_simplify_tactic(m), simp2_p), mk_max_bv_sharing_tactic(m), mk_bit_blaster_tactic(m), mk_sat_tactic(m)), p); return r; } static tactic * mk_qfnia_premable(ast_manager & m, params_ref const & p_ref) { params_ref pull_ite_p = p_ref; pull_ite_p.set_bool("pull_cheap_ite", true); pull_ite_p.set_bool("local_ctx", true); pull_ite_p.set_uint("local_ctx_limit", 10000000); params_ref ctx_simp_p = p_ref; ctx_simp_p.set_uint("max_depth", 30); ctx_simp_p.set_uint("max_steps", 5000000); params_ref elim_p = p_ref; elim_p.set_uint("max_memory",20); return and_then(mk_simplify_tactic(m), mk_propagate_values_tactic(m), 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_card2bv_tactic(m, p_ref), skip_if_failed(using_params(mk_cofactor_term_ite_tactic(m), elim_p))); } static tactic * mk_qfnia_sat_solver(ast_manager & m, params_ref const & p) { params_ref nia2sat_p = p; nia2sat_p.set_uint("nla2bv_max_bv_size", 64); params_ref simp_p = p; simp_p.set_bool("hoist_mul", true); // hoist multipliers to create smaller circuits. return and_then(using_params(mk_simplify_tactic(m), simp_p), mk_nla2bv_tactic(m, nia2sat_p), skip_if_failed(mk_qfnia_bv_solver(m, p)), mk_fail_if_undecided_tactic()); } static tactic * mk_qfnia_nlsat_solver(ast_manager & m, params_ref const & p) { params_ref nia2sat_p = p; nia2sat_p.set_uint("nla2bv_max_bv_size", 64); params_ref simp_p = p; simp_p.set_bool("som", true); // expand into sums of monomials simp_p.set_bool("factor", false); return and_then(using_params(mk_simplify_tactic(m), simp_p), try_for(mk_qfnra_nlsat_tactic(m, simp_p), 3000), mk_fail_if_undecided_tactic()); } 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)); } 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), mk_qfnia_smt_solver(m, p)) ); } z3-z3-4.8.7/src/tactic/smtlogics/qfnia_tactic.h000066400000000000000000000007271356505360400212750ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qfnia_tactic.h Abstract: Tactic for QF_NIA Author: Leonardo (leonardo) 2012-02-28 Notes: --*/ #ifndef QFNIA_TACTIC_H_ #define QFNIA_TACTIC_H_ #include "util/params.h" class ast_manager; class tactic; tactic * mk_qfnia_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("qfnia", "builtin strategy for solving QF_NIA problems.", "mk_qfnia_tactic(m, p)") */ #endif z3-z3-4.8.7/src/tactic/smtlogics/qfnra_tactic.cpp000066400000000000000000000031151356505360400216330ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qfnra_tactic.cpp Abstract: Tactic for QF_NRA Author: Leonardo (leonardo) 2012-02-28 Notes: --*/ #include "tactic/tactical.h" #include "tactic/core/simplify_tactic.h" #include "tactic/core/propagate_values_tactic.h" #include "tactic/arith/nla2bv_tactic.h" #include "smt/tactic/smt_tactic.h" #include "nlsat/tactic/qfnra_nlsat_tactic.h" static tactic * mk_qfnra_sat_solver(ast_manager& m, params_ref const& p, unsigned bv_size) { params_ref nra2sat_p = p; nra2sat_p.set_uint("nla2bv_max_bv_size", p.get_uint("nla2bv_max_bv_size", bv_size)); return and_then(mk_nla2bv_tactic(m, nra2sat_p), mk_smt_tactic(m), mk_fail_if_undecided_tactic()); } tactic * mk_qfnra_tactic(ast_manager & m, params_ref const& p) { params_ref p0 = p; p0.set_bool("inline_vars", true); params_ref p1 = p; p1.set_uint("seed", 11); p1.set_bool("factor", false); params_ref p2 = p; p2.set_uint("seed", 13); p2.set_bool("factor", false); return and_then(mk_simplify_tactic(m, p), mk_propagate_values_tactic(m, p), or_else(try_for(mk_qfnra_nlsat_tactic(m, p0), 5000), try_for(mk_qfnra_nlsat_tactic(m, p1), 10000), mk_qfnra_sat_solver(m, p, 4), and_then(try_for(mk_smt_tactic(m), 5000), mk_fail_if_undecided_tactic()), mk_qfnra_sat_solver(m, p, 6), mk_qfnra_nlsat_tactic(m, p2))); } z3-z3-4.8.7/src/tactic/smtlogics/qfnra_tactic.h000066400000000000000000000007271356505360400213060ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qfnra_tactic.h Abstract: Tactic for QF_NRA Author: Leonardo (leonardo) 2012-02-28 Notes: --*/ #ifndef QFNRA_TACTIC_H_ #define QFNRA_TACTIC_H_ #include "util/params.h" class ast_manager; class tactic; tactic * mk_qfnra_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("qfnra", "builtin strategy for solving QF_NRA problems.", "mk_qfnra_tactic(m, p)") */ #endif z3-z3-4.8.7/src/tactic/smtlogics/qfuf_tactic.cpp000066400000000000000000000017761356505360400215000ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qfuf_tactic.cpp Abstract: Tactic for QF_QFUF benchmarks. Author: Leonardo de Moura (leonardo) 2012-02-21 Notes: --*/ #include "tactic/tactical.h" #include "tactic/core/simplify_tactic.h" #include "tactic/core/symmetry_reduce_tactic.h" #include "tactic/core/solve_eqs_tactic.h" #include "tactic/core/propagate_values_tactic.h" #include "smt/tactic/smt_tactic.h" tactic * mk_qfuf_tactic(ast_manager & m, params_ref const & p) { params_ref s2_p; s2_p.set_bool("pull_cheap_ite", true); s2_p.set_bool("local_ctx", true); s2_p.set_uint("local_ctx_limit", 10000000); return and_then(mk_simplify_tactic(m, p), mk_propagate_values_tactic(m, p), mk_solve_eqs_tactic(m, p), using_params(mk_simplify_tactic(m, p), s2_p), if_no_proofs(if_no_unsat_cores(mk_symmetry_reduce_tactic(m, p))), mk_smt_tactic(m, p)); } z3-z3-4.8.7/src/tactic/smtlogics/qfuf_tactic.h000066400000000000000000000007311356505360400211330ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qfuf_tactic.h Abstract: Tactic for QF_QFUF benchmarks. Author: Leonardo de Moura (leonardo) 2012-02-21 Notes: --*/ #ifndef QFUF_TACTIC_H_ #define QFUF_TACTIC_H_ #include "util/params.h" class ast_manager; class tactic; tactic * mk_qfuf_tactic(ast_manager & m, params_ref const & p); /* ADD_TACTIC("qfuf", "builtin strategy for solving QF_UF problems.", "mk_qfuf_tactic(m, p)") */ #endif z3-z3-4.8.7/src/tactic/smtlogics/qfufbv_ackr_model_converter.cpp000066400000000000000000000007221356505360400247360ustar00rootroot00000000000000/*++ Copyright (c) 2016 Microsoft Corporation Module Name: qfufbv_ackr_model_converter.cpp Abstract: Author: Mikolas Janota (MikolasJanota) Revision History: --*/ #include "tactic/smtlogics/qfufbv_ackr_model_converter.h" #include "ackermannization/ackr_model_converter.h" model_converter * mk_qfufbv_ackr_model_converter(ast_manager & m, const ackr_info_ref& info, model_ref& abstr_model) { return mk_ackr_model_converter(m, info, abstr_model); } z3-z3-4.8.7/src/tactic/smtlogics/qfufbv_ackr_model_converter.h000066400000000000000000000007611356505360400244060ustar00rootroot00000000000000 /*++ Copyright (c) 2016 Microsoft Corporation Module Name: qfufbv_ackr_model_converter.h Abstract: Author: Mikolas Janota (MikolasJanota) Revision History: --*/ #ifndef QFUFBV_ACKR_MODEL_CONVERTER_H_ #define QFUFBV_ACKR_MODEL_CONVERTER_H_ #include "tactic/model_converter.h" #include "ackermannization/ackr_info.h" model_converter * mk_qfufbv_ackr_model_converter(ast_manager & m, const ackr_info_ref& info, model_ref& abstr_model); #endif /* QFUFBV_ACKR_MODEL_CONVERTER_H_ */ z3-z3-4.8.7/src/tactic/smtlogics/qfufbv_tactic.cpp000066400000000000000000000141201356505360400220130ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qfufbv_tactic.cpp Abstract: Tactic for QF_UFBV Author: Leonardo (leonardo) 2012-02-27 Mikolas Janota Notes: --*/ #include "tactic/tactical.h" #include "tactic/core/simplify_tactic.h" #include "tactic/core/propagate_values_tactic.h" #include "tactic/core/solve_eqs_tactic.h" #include "tactic/core/elim_uncnstr_tactic.h" #include "smt/tactic/smt_tactic.h" #include "tactic/bv/max_bv_sharing_tactic.h" #include "tactic/bv/bv_size_reduction_tactic.h" #include "tactic/core/reduce_args_tactic.h" #include "tactic/smtlogics/qfbv_tactic.h" #include "tactic/smtlogics/qfufbv_tactic_params.hpp" /////////////// #include "model/model_smt2_pp.h" #include "ackermannization/lackr.h" #include "ackermannization/ackermannization_params.hpp" #include "tactic/smtlogics/qfufbv_ackr_model_converter.h" /////////////// #include "sat/sat_solver/inc_sat_solver.h" #include "tactic/smtlogics/qfaufbv_tactic.h" #include "tactic/smtlogics/qfbv_tactic.h" #include "solver/tactic2solver.h" #include "tactic/bv/bv_bound_chk_tactic.h" #include "ackermannization/ackermannize_bv_tactic.h" /////////////// class qfufbv_ackr_tactic : public tactic { public: qfufbv_ackr_tactic(ast_manager& m, params_ref const& p) : m_m(m) , m_p(p) , m_use_sat(false) , m_inc_use_sat(false) {} ~qfufbv_ackr_tactic() override { } void operator()(goal_ref const & g, goal_ref_buffer & result) override { ast_manager& m(g->m()); tactic_report report("qfufbv_ackr", *g); fail_if_unsat_core_generation("qfufbv_ackr", g); fail_if_proof_generation("qfufbv_ackr", g); TRACE("qfufbv_ackr_tactic", g->display(tout << "goal:\n");); // running implementation ptr_vector flas; const unsigned sz = g->size(); for (unsigned i = 0; i < sz; i++) flas.push_back(g->form(i)); scoped_ptr uffree_solver = setup_sat(); lackr imp(m, m_p, m_st, flas, uffree_solver.get()); const lbool o = imp.operator()(); flas.reset(); // report result goal_ref resg(alloc(goal, *g, true)); if (o == l_false) resg->assert_expr(m.mk_false()); if (o != l_undef) result.push_back(resg.get()); // report model if (g->models_enabled() && (o == l_true)) { model_ref abstr_model = imp.get_model(); g->add(mk_qfufbv_ackr_model_converter(m, imp.get_info(), abstr_model)); } } void updt_params(params_ref const & _p) override { qfufbv_tactic_params p(_p); m_use_sat = p.sat_backend(); m_inc_use_sat = p.inc_sat_backend(); } void collect_statistics(statistics & st) const override { ackermannization_params p(m_p); if (!p.eager()) st.update("lackr-its", m_st.m_it); st.update("ackr-constraints", m_st.m_ackrs_sz); } void reset_statistics() override { m_st.reset(); } void cleanup() override { } tactic* translate(ast_manager& m) override { return alloc(qfufbv_ackr_tactic, m, m_p); } private: ast_manager& m_m; params_ref m_p; lackr_stats m_st; bool m_use_sat; bool m_inc_use_sat; solver* setup_sat() { solver * sat(nullptr); if (m_use_sat) { if (m_inc_use_sat) { sat = mk_inc_sat_solver(m_m, m_p); } else { tactic_ref t = mk_qfbv_tactic(m_m, m_p); sat = mk_tactic2solver(m_m, t.get(), m_p); } } else { tactic_ref t = mk_qfaufbv_tactic(m_m, m_p); sat = mk_tactic2solver(m_m, t.get(), m_p); } SASSERT(sat != nullptr); sat->set_produce_models(true); return sat; } }; static tactic * mk_qfufbv_preamble1(ast_manager & m, params_ref const & p) { params_ref simp2_p = p; simp2_p.set_bool("pull_cheap_ite", true); simp2_p.set_bool("push_ite_bv", false); simp2_p.set_bool("local_ctx", true); simp2_p.set_uint("local_ctx_limit", 10000000); simp2_p.set_bool("ite_extra_rules", true); simp2_p.set_bool("mul2concat", true); params_ref ctx_simp_p; ctx_simp_p.set_uint("max_depth", 32); ctx_simp_p.set_uint("max_steps", 5000000); return and_then( mk_simplify_tactic(m), mk_propagate_values_tactic(m), if_no_proofs(if_no_unsat_cores(mk_bv_bound_chk_tactic(m))), //using_params(mk_ctx_simplify_tactic(m_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))), mk_max_bv_sharing_tactic(m), using_params(mk_simplify_tactic(m), simp2_p) ); } static tactic * mk_qfufbv_preamble(ast_manager & m, params_ref const & p) { return and_then(mk_simplify_tactic(m), mk_propagate_values_tactic(m), mk_solve_eqs_tactic(m), mk_elim_uncnstr_tactic(m), 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))), mk_max_bv_sharing_tactic(m), if_no_proofs(if_no_unsat_cores(mk_ackermannize_bv_tactic(m,p))) ); } tactic * mk_qfufbv_tactic(ast_manager & m, params_ref const & p) { params_ref main_p; main_p.set_bool("elim_and", true); main_p.set_bool("blast_distinct", true); tactic * const preamble_st = mk_qfufbv_preamble(m, p); tactic * st = using_params(and_then(preamble_st, cond(mk_is_qfbv_probe(), mk_qfbv_tactic(m), mk_smt_tactic(m))), main_p); st->updt_params(p); return st; } tactic * mk_qfufbv_ackr_tactic(ast_manager & m, params_ref const & p) { tactic * const preamble_t = mk_qfufbv_preamble1(m, p); tactic * const actual_tactic = alloc(qfufbv_ackr_tactic, m, p); return and_then(preamble_t, cond(mk_is_qfufbv_probe(), actual_tactic, mk_smt_tactic(m))); } z3-z3-4.8.7/src/tactic/smtlogics/qfufbv_tactic.h000066400000000000000000000012361356505360400214640ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: qfufbv_tactic.h Abstract: Tactic for QF_UFBV Author: Leonardo (leonardo) 2012-02-27 Notes: --*/ #ifndef QFUFBV_TACTIC_H_ #define QFUFBV_TACTIC_H_ #include "util/params.h" class ast_manager; class tactic; tactic * mk_qfufbv_tactic(ast_manager & m, params_ref const & p = params_ref()); tactic * mk_qfufbv_ackr_tactic(ast_manager & m, params_ref const & p); /* ADD_TACTIC("qfufbv", "builtin strategy for solving QF_UFBV problems.", "mk_qfufbv_tactic(m, p)") ADD_TACTIC("qfufbv_ackr", "A tactic for solving QF_UFBV based on Ackermannization.", "mk_qfufbv_ackr_tactic(m, p)") */ #endif z3-z3-4.8.7/src/tactic/smtlogics/qfufbv_tactic_params.pyg000066400000000000000000000007411356505360400233770ustar00rootroot00000000000000def_module_params('ackermannization', description='tactics based on solving UF-theories via ackermannization (see also ackr module)', class_name='qfufbv_tactic_params', export=True, params=( ('sat_backend', BOOL, False, 'use SAT rather than SMT in qfufbv_ackr_tactic'), ('inc_sat_backend', BOOL, False, 'use incremental SAT'), )) z3-z3-4.8.7/src/tactic/smtlogics/quant_tactics.cpp000066400000000000000000000101061356505360400220350ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: quant_tactics.cpp Abstract: Tactics for benchmarks containing quantifiers. Author: Leonardo de Moura (leonardo) 2012-02-21. Revision History: --*/ #include "tactic/tactical.h" #include "tactic/core/simplify_tactic.h" #include "tactic/core/propagate_values_tactic.h" #include "tactic/core/solve_eqs_tactic.h" #include "tactic/core/elim_uncnstr_tactic.h" #include "qe/qe_tactic.h" #include "qe/qe_lite.h" #include "qe/qsat.h" #include "tactic/core/ctx_simplify_tactic.h" #include "smt/tactic/smt_tactic.h" #include "tactic/core/elim_term_ite_tactic.h" #include "tactic/arith/probe_arith.h" static tactic * mk_quant_preprocessor(ast_manager & m, bool disable_gaussian = false) { params_ref pull_ite_p; pull_ite_p.set_bool("pull_cheap_ite", true); pull_ite_p.set_bool("local_ctx", true); pull_ite_p.set_uint("local_ctx_limit", 10000000); params_ref ctx_simp_p; ctx_simp_p.set_uint("max_depth", 30); ctx_simp_p.set_uint("max_steps", 5000000); tactic * solve_eqs; if (disable_gaussian) { solve_eqs = mk_skip_tactic(); } else { solve_eqs = when(mk_not(mk_has_pattern_probe()), mk_solve_eqs_tactic(m)); } // remark: investigate if gaussian elimination is useful when patterns are not provided. return and_then(mk_simplify_tactic(m), mk_propagate_values_tactic(m), using_params(mk_ctx_simplify_tactic(m), ctx_simp_p), using_params(mk_simplify_tactic(m), pull_ite_p), solve_eqs, mk_elim_uncnstr_tactic(m), mk_simplify_tactic(m)); } static tactic * mk_no_solve_eq_preprocessor(ast_manager & m) { return mk_quant_preprocessor(m, true); } tactic * mk_ufnia_tactic(ast_manager & m, params_ref const & p) { tactic * st = and_then(mk_no_solve_eq_preprocessor(m), mk_qe_lite_tactic(m, p), mk_smt_tactic(m)); st->updt_params(p); return st; } tactic * mk_uflra_tactic(ast_manager & m, params_ref const & p) { tactic * st = and_then(mk_quant_preprocessor(m), mk_smt_tactic(m)); st->updt_params(p); return st; } tactic * mk_auflia_tactic(ast_manager & m, params_ref const & p) { params_ref qi_p; qi_p.set_str("qi.cost", "0"); TRACE("qi_cost", qi_p.display(tout); tout << "\n" << qi_p.get_str("qi.cost", "") << "\n";); tactic * st = and_then(mk_no_solve_eq_preprocessor(m), or_else(and_then(fail_if(mk_gt(mk_num_exprs_probe(), mk_const_probe(static_cast(128)))), using_params(mk_smt_tactic(m), qi_p), mk_fail_if_undecided_tactic()), mk_smt_tactic(m))); st->updt_params(p); return st; } tactic * mk_auflira_tactic(ast_manager & m, params_ref const & p) { tactic * st = and_then(mk_quant_preprocessor(m), mk_smt_tactic(m)); st->updt_params(p); return st; } tactic * mk_aufnira_tactic(ast_manager & m, params_ref const & p) { tactic * st = and_then(mk_quant_preprocessor(m), mk_smt_tactic(m)); st->updt_params(p); return st; } tactic * mk_lra_tactic(ast_manager & m, params_ref const & p) { tactic * st = and_then(mk_quant_preprocessor(m), mk_qe_lite_tactic(m, p), cond(mk_has_quantifier_probe(), cond(mk_is_lira_probe(), or_else(mk_qsat_tactic(m, p), and_then(mk_qe_tactic(m), mk_smt_tactic(m))), mk_smt_tactic(m)), mk_smt_tactic(m))); st->updt_params(p); return st; } tactic * mk_lia_tactic(ast_manager & m, params_ref const & p) { return mk_lra_tactic(m, p); } tactic * mk_lira_tactic(ast_manager & m, params_ref const & p) { return mk_lra_tactic(m, p); } z3-z3-4.8.7/src/tactic/smtlogics/quant_tactics.h000066400000000000000000000031431356505360400215050ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: quant_tactics.h Abstract: Tactics for benchmarks containing quantifiers. Author: Leonardo de Moura (leonardo) 2012-02-21. Revision History: --*/ #ifndef QUANT_TACTICS_H_ #define QUANT_TACTICS_H_ #include "util/params.h" class ast_manager; class tactic; tactic * mk_ufnia_tactic(ast_manager & m, params_ref const & p); tactic * mk_uflra_tactic(ast_manager & m, params_ref const & p); tactic * mk_auflia_tactic(ast_manager & m, params_ref const & p); tactic * mk_auflira_tactic(ast_manager & m, params_ref const & p); tactic * mk_aufnira_tactic(ast_manager & m, params_ref const & p); tactic * mk_lra_tactic(ast_manager & m, params_ref const & p); tactic * mk_lia_tactic(ast_manager & m, params_ref const & p); tactic * mk_lira_tactic(ast_manager & m, params_ref const & p); /* ADD_TACTIC("ufnia", "builtin strategy for solving UFNIA problems.", "mk_ufnia_tactic(m, p)") ADD_TACTIC("uflra", "builtin strategy for solving UFLRA problems.", "mk_uflra_tactic(m, p)") ADD_TACTIC("auflia", "builtin strategy for solving AUFLIA problems.", "mk_auflia_tactic(m, p)") ADD_TACTIC("auflira", "builtin strategy for solving AUFLIRA problems.", "mk_auflira_tactic(m, p)") ADD_TACTIC("aufnira", "builtin strategy for solving AUFNIRA problems.", "mk_aufnira_tactic(m, p)") ADD_TACTIC("lra", "builtin strategy for solving LRA problems.", "mk_lra_tactic(m, p)") ADD_TACTIC("lia", "builtin strategy for solving LIA problems.", "mk_lia_tactic(m, p)") ADD_TACTIC("lira", "builtin strategy for solving LIRA problems.", "mk_lira_tactic(m, p)") */ #endif z3-z3-4.8.7/src/tactic/tactic.cpp000066400000000000000000000145131356505360400164440ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: tactic.h Abstract: Abstract tactic object. Author: Leonardo (leonardo) 2011-10-13 Notes: --*/ #include #include "tactic/tactic.h" #include "tactic/probe.h" #include "util/stopwatch.h" #include "model/model_v2_pp.h" struct tactic_report::imp { char const * m_id; goal const & m_goal; stopwatch m_watch; double m_start_memory; imp(char const * id, goal const & g): m_id(id), m_goal(g), m_start_memory(static_cast(memory::get_allocation_size())/static_cast(1024*1024)) { m_watch.start(); } ~imp() { m_watch.stop(); double end_memory = static_cast(memory::get_allocation_size())/static_cast(1024*1024); 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); } }; tactic_report::tactic_report(char const * id, goal const & g) { if (get_verbosity_level() >= TACTIC_VERBOSITY_LVL) m_imp = alloc(imp, id, g); else m_imp = nullptr; } tactic_report::~tactic_report() { if (m_imp) dealloc(m_imp); } void report_tactic_progress(char const * id, unsigned val) { if (val > 0) { IF_VERBOSE(TACTIC_VERBOSITY_LVL, verbose_stream() << "(" << id << " " << val << ")" << std::endl;); } } void skip_tactic::operator()(goal_ref const & in, goal_ref_buffer& result) { result.push_back(in.get()); } tactic * mk_skip_tactic() { return alloc(skip_tactic); } class fail_tactic : public tactic { public: void operator()(goal_ref const & in, goal_ref_buffer & result) override { throw tactic_exception("fail tactic"); } void cleanup() override {} tactic * translate(ast_manager & m) override { return this; } }; tactic * mk_fail_tactic() { return alloc(fail_tactic); } class report_verbose_tactic : public skip_tactic { char const * m_msg; unsigned m_lvl; public: report_verbose_tactic(char const * msg, unsigned lvl) : m_msg(msg), m_lvl(lvl) {} void operator()(goal_ref const & in, goal_ref_buffer& result) override { IF_VERBOSE(m_lvl, verbose_stream() << m_msg << "\n";); skip_tactic::operator()(in, result); } }; tactic * mk_report_verbose_tactic(char const * msg, unsigned lvl) { return alloc(report_verbose_tactic, msg, lvl); } class trace_tactic : public skip_tactic { char const * m_tag; public: trace_tactic(char const * tag): m_tag(tag) {} void operator()(goal_ref const & in, goal_ref_buffer& result) override { TRACE(m_tag, in->display(tout);); (void)m_tag; skip_tactic::operator()(in, result); } }; tactic * mk_trace_tactic(char const * tag) { return alloc(trace_tactic, tag); } class fail_if_undecided_tactic : public skip_tactic { public: fail_if_undecided_tactic() {} void operator()(goal_ref const & in, goal_ref_buffer& result) override { if (!in->is_decided()) throw tactic_exception("undecided"); skip_tactic::operator()(in, result); } }; tactic * mk_fail_if_undecided_tactic() { return alloc(fail_if_undecided_tactic); } void exec(tactic & t, goal_ref const & in, goal_ref_buffer & result) { t.reset_statistics(); try { t(in, result); t.cleanup(); } catch (tactic_exception & ex) { IF_VERBOSE(TACTIC_VERBOSITY_LVL, verbose_stream() << "(tactic-exception \"" << escaped(ex.msg()) << "\")" << std::endl;); t.cleanup(); throw ex; } } lbool check_sat(tactic & t, goal_ref & g, model_ref & md, labels_vec & labels, proof_ref & pr, expr_dependency_ref & core, std::string & reason_unknown) { bool models_enabled = g->models_enabled(); bool cores_enabled = g->unsat_core_enabled(); md = nullptr; pr = nullptr; core = nullptr; ast_manager & m = g->m(); goal_ref_buffer r; try { exec(t, g, r); } catch (z3_exception & ex) { reason_unknown = ex.msg(); if (r.size() > 0) pr = r[0]->pr(0); return l_undef; } TRACE("tactic", tout << "r.size(): " << r.size() << "\n"; for (unsigned i = 0; i < r.size(); i++) r[i]->display_with_dependencies(tout);); if (r.size() > 0) { pr = r[0]->pr(0); TRACE("tactic", tout << pr << "\n";); } if (is_decided_sat(r)) { model_converter_ref mc = r[0]->mc(); if (mc.get()) { (*mc)(labels); model_converter2model(m, mc.get(), md); } if (!md) { // create empty model. md = alloc(model, m); } return l_true; } else if (is_decided_unsat(r)) { goal * final = r[0]; SASSERT(m.is_false(final->form(0))); pr = final->pr(0); if (cores_enabled) core = final->dep(0); return l_false; } else { if (models_enabled && !r.empty()) { model_converter_ref mc = r[0]->mc(); model_converter2model(m, mc.get(), md); if (mc) (*mc)(labels); } reason_unknown = "incomplete"; return l_undef; } } void fail_if_proof_generation(char const * tactic_name, goal_ref const & in) { if (in->proofs_enabled()) { std::string msg = tactic_name; msg += " does not support proof production"; throw tactic_exception(std::move(msg)); } } void fail_if_unsat_core_generation(char const * tactic_name, goal_ref const & in) { if (in->unsat_core_enabled()) { std::string msg = tactic_name; msg += " does not support unsat core production"; throw tactic_exception(std::move(msg)); } } void fail_if_model_generation(char const * tactic_name, goal_ref const & in) { if (in->models_enabled()) { std::string msg = tactic_name; msg += " does not generate models"; throw tactic_exception(std::move(msg)); } } z3-z3-4.8.7/src/tactic/tactic.h000066400000000000000000000077021356505360400161130ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: tactic.h Abstract: Abstract tactic object. It used to be called assertion_set_strategy. The main improvement is the support for multiple subgoals. Author: Leonardo (leonardo) 2011-10-13 Notes: --*/ #ifndef TACTIC_H_ #define TACTIC_H_ #include "tactic/goal.h" #include "util/params.h" #include "util/statistics.h" #include "tactic/tactic_exception.h" #include "util/lbool.h" class progress_callback; typedef ptr_buffer goal_buffer; class tactic { unsigned m_ref_count; public: tactic():m_ref_count(0) {} virtual ~tactic() {} void inc_ref() { m_ref_count++; } void dec_ref() { SASSERT(m_ref_count > 0); m_ref_count--; if (m_ref_count == 0) dealloc(this); } virtual void updt_params(params_ref const & p) {} virtual void collect_param_descrs(param_descrs & r) {} /** \brief Apply tactic to goal \c in. The list of resultant subgoals is stored in \c result. The content of \c in may be destroyed during the operation. The output parameter \c core is used to accumulate the unsat core of closed subgoals. It must be 0 if dependency tracking is disabled, or the result is decided unsat, or no tagged assertions were used to close any subgoal. Note that, this signature is not compatible with the one described in the paper: "The Strategy Challenge in SMT Solving". The approach in the paper is conceptually simpler, but (for historical reasons) it would require a lot of re-engineering in the Z3 code. In Z3, we keep a proof/justification for every formula in a goal. Therefore, in most cases, pc == 0 and core == 0 for non-branching tactics. */ virtual void operator()(goal_ref const & in, goal_ref_buffer& result) = 0; virtual void collect_statistics(statistics & st) const {} virtual void reset_statistics() {} virtual void cleanup() = 0; virtual void reset() { cleanup(); } // for backward compatibility virtual void set_logic(symbol const & l) {} virtual void set_progress_callback(progress_callback * callback) {} // translate tactic to the given manager virtual tactic * translate(ast_manager & m) = 0; protected: friend class nary_tactical; friend class binary_tactical; friend class unary_tactical; }; typedef ref tactic_ref; typedef sref_vector tactic_ref_vector; typedef sref_buffer tactic_ref_buffer; // minimum verbosity level for tactics #define TACTIC_VERBOSITY_LVL 10 class tactic_report { struct imp; imp * m_imp; public: tactic_report(char const * id, goal const & g); ~tactic_report(); }; void report_tactic_progress(char const * id, unsigned val); class skip_tactic : public tactic { public: void operator()(goal_ref const & in, goal_ref_buffer& result) override; void cleanup() override {} tactic * translate(ast_manager & m) override { return this; } }; tactic * mk_skip_tactic(); tactic * mk_fail_tactic(); tactic * mk_fail_if_undecided_tactic(); /* ADD_TACTIC("skip", "do nothing tactic.", "mk_skip_tactic()") ADD_TACTIC("fail", "always fail tactic.", "mk_fail_tactic()") ADD_TACTIC("fail-if-undecided", "fail if goal is undecided.", "mk_fail_if_undecided_tactic()") */ tactic * mk_report_verbose_tactic(char const * msg, unsigned lvl); tactic * mk_trace_tactic(char const * tag); void exec(tactic & t, goal_ref const & in, goal_ref_buffer & result); lbool check_sat(tactic & t, goal_ref & g, model_ref & md, labels_vec & labels, proof_ref & pr, expr_dependency_ref & core, std::string & reason_unknown); // Throws an exception if goal \c in requires proof generation. void fail_if_proof_generation(char const * tactic_name, goal_ref const & in); void fail_if_unsat_core_generation(char const * tactic_name, goal_ref const & in); void fail_if_model_generation(char const * tactic_name, goal_ref const & in); #endif z3-z3-4.8.7/src/tactic/tactic_exception.h000066400000000000000000000015551356505360400201710ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: tactic_exception.h Abstract: Tactic exception object. Author: Leonardo (leonardo) 2012-08-15 Notes: --*/ #ifndef TACTIC_EXCEPTION_H_ #define TACTIC_EXCEPTION_H_ #include "util/z3_exception.h" #include "util/common_msgs.h" class tactic_exception : public z3_exception { protected: std::string m_msg; public: tactic_exception(std::string && msg) : m_msg(std::move(msg)) {} ~tactic_exception() override {} char const * msg() const override { return m_msg.c_str(); } }; #define TACTIC_CANCELED_MSG Z3_CANCELED_MSG #define TACTIC_MAX_MEMORY_MSG Z3_MAX_MEMORY_MSG #define TACTIC_MAX_SCOPES_MSG Z3_MAX_SCOPES_MSG #define TACTIC_MAX_STEPS_MSG Z3_MAX_STEPS_MSG #define TACTIC_MAX_FRAMES_MSG Z3_MAX_FRAMES_MSG #define TACTIC_NO_PROOF_GEN_MSG Z3_NO_PROOF_GEN_MSG #endif z3-z3-4.8.7/src/tactic/tactic_params.pyg000066400000000000000000000034431356505360400200240ustar00rootroot00000000000000 def_module_params('tactic', description='tactic parameters', export=True, params=(('solve_eqs.context_solve', BOOL, True, "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."), ('default_tactic', SYMBOL, '', "overwrite default tactic in strategic solver"), # ('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 )) z3-z3-4.8.7/src/tactic/tactical.cpp000066400000000000000000001166061356505360400167670ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: tactical.h Abstract: Basic combinators Author: Leonardo (leonardo) 2011-10-13 Notes: --*/ #include "util/scoped_timer.h" #include "util/cancel_eh.h" #include "util/scoped_ptr_vector.h" #include "tactic/tactical.h" #include #include class binary_tactical : public tactic { protected: tactic_ref m_t1; tactic_ref m_t2; public: binary_tactical(tactic * t1, tactic * t2): m_t1(t1), m_t2(t2) { SASSERT(m_t1); SASSERT(m_t2); } ~binary_tactical() override { } void updt_params(params_ref const & p) override { m_t1->updt_params(p); m_t2->updt_params(p); } void collect_param_descrs(param_descrs & r) override { m_t1->collect_param_descrs(r); m_t2->collect_param_descrs(r); } void collect_statistics(statistics & st) const override { m_t1->collect_statistics(st); m_t2->collect_statistics(st); } void reset_statistics() override { m_t1->reset_statistics(); m_t2->reset_statistics(); } void cleanup() override { m_t1->cleanup(); m_t2->cleanup(); } void reset() override { m_t1->reset(); m_t2->reset(); } void set_logic(symbol const & l) override { m_t1->set_logic(l); m_t2->set_logic(l); } void set_progress_callback(progress_callback * callback) override { m_t1->set_progress_callback(callback); m_t2->set_progress_callback(callback); } protected: template tactic * translate_core(ast_manager & m) { tactic * new_t1 = m_t1->translate(m); tactic * new_t2 = m_t2->translate(m); return alloc(T, new_t1, new_t2); } }; struct false_pred { bool operator()(goal * g) { return false; } }; class and_then_tactical : public binary_tactical { public: and_then_tactical(tactic * t1, tactic * t2):binary_tactical(t1, t2) {} ~and_then_tactical() override {} void operator()(goal_ref const & in, goal_ref_buffer& result) override { bool proofs_enabled = in->proofs_enabled(); bool cores_enabled = in->unsat_core_enabled(); ast_manager & m = in->m(); goal_ref_buffer r1; m_t1->operator()(in, r1); unsigned r1_size = r1.size(); SASSERT(r1_size > 0); if (r1_size == 1) { if (r1[0]->is_decided()) { result.push_back(r1[0]); return; } goal_ref r1_0 = r1[0]; m_t2->operator()(r1_0, result); } else { goal_ref_buffer r2; for (unsigned i = 0; i < r1_size; i++) { goal_ref g = r1[i]; r2.reset(); m_t2->operator()(g, r2); if (is_decided(r2)) { SASSERT(r2.size() == 1); if (is_decided_sat(r2)) { // found solution... result.reset(); result.push_back(r2[0]); return; } else { SASSERT(is_decided_unsat(r2)); } } else { result.append(r2.size(), r2.c_ptr()); } } if (result.empty()) { // all subgoals were shown to be unsat. // create an decided_unsat goal with the proof in->reset_all(); proof_ref pr(m); expr_dependency_ref core(m); if (proofs_enabled) { apply(m, in->pc(), pr); } dependency_converter* dc = in->dc(); if (cores_enabled && dc) { core = (*dc)(); } in->assert_expr(m.mk_false(), pr, core); result.push_back(in.get()); } } } tactic * translate(ast_manager & m) override { return translate_core(m); } }; tactic * and_then(tactic * t1, tactic * t2) { return alloc(and_then_tactical, t1, t2); } tactic * and_then(tactic * t1, tactic * t2, tactic * t3) { return and_then(t1, and_then(t2, t3)); } tactic * and_then(tactic * t1, tactic * t2, tactic * t3, tactic * t4) { return and_then(t1, and_then(t2, t3, t4)); } tactic * and_then(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5) { return and_then(t1, and_then(t2, t3, t4, t5)); } tactic * and_then(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5, tactic * t6) { return and_then(t1, and_then(t2, t3, t4, t5, t6)); } tactic * and_then(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5, tactic * t6, tactic * t7) { return and_then(t1, and_then(t2, t3, t4, t5, t6, t7)); } tactic * and_then(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5, tactic * t6, tactic * t7, tactic * t8) { return and_then(t1, and_then(t2, t3, t4, t5, t6, t7, t8)); } tactic * and_then(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5, tactic * t6, tactic * t7, tactic * t8, tactic * t9) { return and_then(t1, and_then(t2, t3, t4, t5, t6, t7, t8, t9)); } tactic * and_then(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5, tactic * t6, tactic * t7, tactic * t8, tactic * t9, tactic * t10) { return and_then(t1, and_then(t2, t3, t4, t5, t6, t7, t8, t9, t10)); } tactic * and_then(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5, tactic * t6, tactic * t7, tactic * t8, tactic * t9, tactic * t10, tactic * t11) { return and_then(t1, and_then(t2, t3, t4, t5, t6, t7, t8, t9, t10, t11)); } tactic * and_then(unsigned num, tactic * const * ts) { SASSERT(num > 0); unsigned i = num - 1; tactic * r = ts[i]; while (i > 0) { --i; r = and_then(ts[i], r); } return r; } class nary_tactical : public tactic { protected: sref_vector m_ts; public: nary_tactical(unsigned num, tactic * const * ts) { for (unsigned i = 0; i < num; i++) { SASSERT(ts[i]); m_ts.push_back(ts[i]); } } ~nary_tactical() override { } void updt_params(params_ref const & p) override { TRACE("nary_tactical_updt_params", tout << "updt_params: " << p << "\n";); for (tactic* t : m_ts) t->updt_params(p); } void collect_param_descrs(param_descrs & r) override { for (tactic* t : m_ts) t->collect_param_descrs(r); } void collect_statistics(statistics & st) const override { for (tactic const* t : m_ts) t->collect_statistics(st); } void reset_statistics() override { for (tactic* t : m_ts) t->reset_statistics(); } void cleanup() override { for (tactic* t : m_ts) t->cleanup(); } void reset() override { for (tactic* t : m_ts) t->reset(); } void set_logic(symbol const & l) override { for (tactic* t : m_ts) t->set_logic(l); } void set_progress_callback(progress_callback * callback) override { for (tactic* t : m_ts) t->set_progress_callback(callback); } protected: template tactic * translate_core(ast_manager & m) { sref_vector new_ts; for (tactic* curr : m_ts) { new_ts.push_back(curr->translate(m)); } return alloc(T, new_ts.size(), new_ts.c_ptr()); } }; class or_else_tactical : public nary_tactical { public: or_else_tactical(unsigned num, tactic * const * ts):nary_tactical(num, ts) { SASSERT(num > 0); } ~or_else_tactical() override {} void operator()(goal_ref const & in, goal_ref_buffer& result) override { goal orig(*(in.get())); unsigned sz = m_ts.size(); unsigned i; for (i = 0; i < sz; i++) { tactic * t = m_ts[i]; SASSERT(sz > 0); if (i < sz - 1) { try { t->operator()(in, result); return; } catch (tactic_exception &) { result.reset(); } } else { t->operator()(in, result); return; } in->reset_all(); in->copy_from(orig); } } tactic * translate(ast_manager & m) override { return translate_core(m); } }; tactic * or_else(unsigned num, tactic * const * ts) { return alloc(or_else_tactical, num, ts); } tactic * or_else(tactic * t1, tactic * t2) { tactic * ts[2] = { t1, t2 }; return or_else(2, ts); } tactic * or_else(tactic * t1, tactic * t2, tactic * t3) { tactic * ts[3] = { t1, t2, t3 }; return or_else(3, ts); } tactic * or_else(tactic * t1, tactic * t2, tactic * t3, tactic * t4) { tactic * ts[4] = { t1, t2, t3, t4 }; return or_else(4, ts); } tactic * or_else(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5) { tactic * ts[5] = { t1, t2, t3, t4, t5 }; return or_else(5, ts); } tactic * or_else(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5, tactic * t6) { tactic * ts[6] = { t1, t2, t3, t4, t5, t6 }; return or_else(6, ts); } tactic * or_else(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5, tactic * t6, tactic * t7) { tactic * ts[7] = { t1, t2, t3, t4, t5, t6, t7 }; return or_else(7, ts); } tactic * or_else(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5, tactic * t6, tactic * t7, tactic * t8) { tactic * ts[8] = { t1, t2, t3, t4, t5, t6, t7, t8 }; return or_else(8, ts); } tactic * or_else(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5, tactic * t6, tactic * t7, tactic * t8, tactic * t9) { tactic * ts[9] = { t1, t2, t3, t4, t5, t6, t7, t8, t9 }; return or_else(9, ts); } tactic * or_else(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5, tactic * t6, tactic * t7, tactic * t8, tactic * t9, tactic * t10) { tactic * ts[10] = { t1, t2, t3, t4, t5, t6, t7, t8, t9, t10 }; return or_else(10, ts); } enum par_exception_kind { TACTIC_EX, DEFAULT_EX, ERROR_EX }; class par_tactical : public or_else_tactical { std::string ex_msg; unsigned error_code; public: par_tactical(unsigned num, tactic * const * ts):or_else_tactical(num, ts) { error_code = 0; } ~par_tactical() override {} void operator()(goal_ref const & in, goal_ref_buffer& result) override { bool use_seq; use_seq = false; if (use_seq) { // execute tasks sequentially or_else_tactical::operator()(in, result); return; } ast_manager & m = in->m(); scoped_ptr_vector managers; scoped_limits scl(m.limit()); goal_ref_vector in_copies; tactic_ref_vector ts; unsigned sz = m_ts.size(); for (unsigned i = 0; i < sz; i++) { ast_manager * new_m = alloc(ast_manager, m, !m.proof_mode()); managers.push_back(new_m); ast_translation translator(m, *new_m); in_copies.push_back(in->translate(translator)); ts.push_back(m_ts.get(i)->translate(*new_m)); scl.push_child(&new_m->limit()); } unsigned finished_id = UINT_MAX; par_exception_kind ex_kind = DEFAULT_EX; std::mutex mux; auto worker_thread = [&](unsigned i) { goal_ref_buffer _result; goal_ref in_copy = in_copies[i]; tactic & t = *(ts.get(i)); try { t(in_copy, _result); bool first = false; { std::lock_guard lock(mux); if (finished_id == UINT_MAX) { finished_id = i; first = true; } } if (first) { for (unsigned j = 0; j < sz; j++) { if (i != j) { managers[j]->limit().cancel(); } } ast_translation translator(*(managers[i]), m, false); for (goal* g : _result) { result.push_back(g->translate(translator)); } goal_ref in2(in_copy->translate(translator)); in->copy_from(*(in2.get())); } } catch (tactic_exception & ex) { if (i == 0) { ex_kind = TACTIC_EX; ex_msg = ex.msg(); } } catch (z3_error & err) { if (i == 0) { ex_kind = ERROR_EX; error_code = err.error_code(); } } catch (z3_exception & z3_ex) { if (i == 0) { ex_kind = DEFAULT_EX; ex_msg = z3_ex.msg(); } } }; vector threads(sz); for (unsigned i = 0; i < sz; ++i) { threads[i] = std::thread([&, i]() { worker_thread(i); }); } for (unsigned i = 0; i < sz; ++i) { threads[i].join(); } if (finished_id == UINT_MAX) { switch (ex_kind) { case ERROR_EX: throw z3_error(error_code); case TACTIC_EX: throw tactic_exception(std::move(ex_msg)); default: throw default_exception(std::move(ex_msg)); } } } tactic * translate(ast_manager & m) override { return translate_core(m); } }; tactic * par(unsigned num, tactic * const * ts) { return alloc(par_tactical, num, ts); } tactic * par(tactic * t1, tactic * t2) { tactic * ts[2] = { t1, t2 }; return par(2, ts); } tactic * par(tactic * t1, tactic * t2, tactic * t3) { tactic * ts[3] = { t1, t2, t3 }; return par(3, ts); } tactic * par(tactic * t1, tactic * t2, tactic * t3, tactic * t4) { tactic * ts[4] = { t1, t2, t3, t4 }; return par(4, ts); } class par_and_then_tactical : public and_then_tactical { public: par_and_then_tactical(tactic * t1, tactic * t2):and_then_tactical(t1, t2) {} ~par_and_then_tactical() override {} void operator()(goal_ref const & in, goal_ref_buffer& result) override { bool use_seq; use_seq = false; if (use_seq) { // execute tasks sequentially and_then_tactical::operator()(in, result); return; } // enabling proofs is possible, but requires translating subgoals back. fail_if_proof_generation("par_and_then", in); bool proofs_enabled = in->proofs_enabled(); bool cores_enabled = in->unsat_core_enabled(); ast_manager & m = in->m(); goal_ref_buffer r1; m_t1->operator()(in, r1); unsigned r1_size = r1.size(); SASSERT(r1_size > 0); if (r1_size == 1) { // Only one subgoal created... no need for parallelism if (r1[0]->is_decided()) { result.push_back(r1[0]); return; } goal_ref r1_0 = r1[0]; m_t2->operator()(r1_0, result); } else { scoped_ptr_vector managers; tactic_ref_vector ts2; goal_ref_vector g_copies; for (unsigned i = 0; i < r1_size; i++) { ast_manager * new_m = alloc(ast_manager, m, !m.proof_mode()); managers.push_back(new_m); ast_translation translator(m, *new_m); g_copies.push_back(r1[i]->translate(translator)); ts2.push_back(m_t2->translate(*new_m)); } scoped_ptr_vector core_buffer; scoped_ptr_vector goals_vect; core_buffer.resize(r1_size); goals_vect.resize(r1_size); bool found_solution = false; bool failed = false; par_exception_kind ex_kind = DEFAULT_EX; unsigned error_code = 0; std::string ex_msg; std::mutex mux; auto worker_thread = [&](unsigned i) { ast_manager & new_m = *(managers[i]); goal_ref new_g = g_copies[i]; goal_ref_buffer r2; bool curr_failed = false; try { ts2[i]->operator()(new_g, r2); } catch (tactic_exception & ex) { { std::lock_guard lock(mux); if (!failed && !found_solution) { curr_failed = true; failed = true; ex_kind = TACTIC_EX; ex_msg = ex.msg(); } } } catch (z3_error & err) { { std::lock_guard lock(mux); if (!failed && !found_solution) { curr_failed = true; failed = true; ex_kind = ERROR_EX; error_code = err.error_code(); } } } catch (z3_exception & z3_ex) { { std::lock_guard lock(mux); if (!failed && !found_solution) { curr_failed = true; failed = true; ex_kind = DEFAULT_EX; ex_msg = z3_ex.msg(); } } } if (curr_failed) { for (unsigned j = 0; j < r1_size; j++) { if (static_cast(i) != j) { managers[j]->limit().cancel(); } } } else { if (is_decided(r2)) { SASSERT(r2.size() == 1); if (is_decided_sat(r2)) { // found solution... bool first = false; { std::lock_guard lock(mux); if (!found_solution) { failed = false; found_solution = true; first = true; } } if (first) { for (unsigned j = 0; j < r1_size; j++) { if (static_cast(i) != j) { managers[j]->limit().cancel(); } } ast_translation translator(new_m, m, false); SASSERT(r2.size() == 1); result.push_back(r2[0]->translate(translator)); } } else { SASSERT(is_decided_unsat(r2)); if (cores_enabled && r2[0]->dep(0) != nullptr) { expr_dependency_ref * new_dep = alloc(expr_dependency_ref, new_m); *new_dep = r2[0]->dep(0); core_buffer.set(i, new_dep); } } } else { goal_ref_buffer * new_r2 = alloc(goal_ref_buffer); goals_vect.set(i, new_r2); new_r2->append(r2.size(), r2.c_ptr()); dependency_converter* dc = r1[i]->dc(); if (cores_enabled && dc) { expr_dependency_ref * new_dep = alloc(expr_dependency_ref, new_m); *new_dep = (*dc)(); core_buffer.set(i, new_dep); } } } }; vector threads(r1_size); for (unsigned i = 0; i < r1_size; ++i) { threads[i] = std::thread([&, i]() { worker_thread(i); }); } for (unsigned i = 0; i < r1_size; ++i) { threads[i].join(); } if (failed) { switch (ex_kind) { case ERROR_EX: throw z3_error(error_code); case TACTIC_EX: throw tactic_exception(std::move(ex_msg)); default: throw default_exception(std::move(ex_msg)); } } if (found_solution) return; expr_dependency_ref core(m); for (unsigned i = 0; i < r1_size; i++) { ast_translation translator(*(managers[i]), m, false); goal_ref_buffer * r = goals_vect[i]; unsigned j = result.size(); if (r != nullptr) { for (unsigned k = 0; k < r->size(); k++) { result.push_back((*r)[k]->translate(translator)); } } if (proofs_enabled) { // update proof converter of r1[i] r1[i]->set(concat(r1[i]->pc(), result.size() - j, result.c_ptr() + j)); } expr_dependency_translation td(translator); if (core_buffer[i] != nullptr) { expr_dependency_ref curr_core(m); curr_core = td(*(core_buffer[i])); core = m.mk_join(curr_core, core); } } if (core) { in->add(dependency_converter::unit(core)); } if (result.empty()) { // all subgoals were shown to be unsat. // create an decided_unsat goal with the proof in->reset_all(); proof_ref pr(m); if (proofs_enabled) { apply(m, in->pc(), pr); } dependency_converter* dc = in->dc(); if (cores_enabled && dc) { core = (*dc)(); } in->assert_expr(m.mk_false(), pr, core); result.push_back(in.get()); } } } tactic * translate(ast_manager & m) override { return translate_core(m); } }; // Similar to and_then combinator, but t2 is applied in parallel to all subgoals produced by t1 tactic * par_and_then(tactic * t1, tactic * t2) { return alloc(par_and_then_tactical, t1, t2); } tactic * par_and_then(unsigned num, tactic * const * ts) { unsigned i = num - 1; tactic * r = ts[i]; while (i > 0) { --i; r = par_and_then(ts[i], r); } return r; } class unary_tactical : public tactic { protected: tactic_ref m_t; public: unary_tactical(tactic * t): m_t(t) { SASSERT(t); } ~unary_tactical() override { } void operator()(goal_ref const & in, goal_ref_buffer& result) override { m_t->operator()(in, result); } void cleanup(void) override { m_t->cleanup(); } void collect_statistics(statistics & st) const override { m_t->collect_statistics(st); } void reset_statistics() override { m_t->reset_statistics(); } void updt_params(params_ref const & p) override { m_t->updt_params(p); } void collect_param_descrs(param_descrs & r) override { m_t->collect_param_descrs(r); } void reset() override { m_t->reset(); } void set_logic(symbol const& l) override { m_t->set_logic(l); } void set_progress_callback(progress_callback * callback) override { m_t->set_progress_callback(callback); } protected: template tactic * translate_core(ast_manager & m) { tactic * new_t = m_t->translate(m); return alloc(T, new_t); } }; class repeat_tactical : public unary_tactical { unsigned m_max_depth; void operator()(unsigned depth, goal_ref const & in, goal_ref_buffer& result) { // TODO: implement a non-recursive version. if (depth > m_max_depth) { result.push_back(in.get()); return; } bool models_enabled = in->models_enabled(); bool proofs_enabled = in->proofs_enabled(); bool cores_enabled = in->unsat_core_enabled(); ast_manager & m = in->m(); goal_ref_buffer r1; result.reset(); { goal orig_in(in->m(), proofs_enabled, models_enabled, cores_enabled); orig_in.copy_from(*(in.get())); m_t->operator()(in, r1); if (r1.size() == 1 && is_equal(orig_in, *(r1[0]))) { result.push_back(r1[0]); return; } } unsigned r1_size = r1.size(); SASSERT(r1_size > 0); if (r1_size == 1) { if (r1[0]->is_decided()) { result.push_back(r1[0]); return; } goal_ref r1_0 = r1[0]; operator()(depth+1, r1_0, result); } else { goal_ref_buffer r2; for (unsigned i = 0; i < r1_size; i++) { goal_ref g = r1[i]; r2.reset(); operator()(depth+1, g, r2); if (is_decided(r2)) { SASSERT(r2.size() == 1); if (is_decided_sat(r2)) { // found solution... result.push_back(r2[0]); return; } else { SASSERT(is_decided_unsat(r2)); } } else { result.append(r2.size(), r2.c_ptr()); } } if (result.empty()) { // all subgoals were shown to be unsat. // create an decided_unsat goal with the proof in->reset_all(); proof_ref pr(m); expr_dependency_ref core(m); if (proofs_enabled) { apply(m, in->pc(), pr); } if (cores_enabled && in->dc()) { core = (*in->dc())(); } in->assert_expr(m.mk_false(), pr, core); result.push_back(in.get()); } } } public: repeat_tactical(tactic * t, unsigned max_depth): unary_tactical(t), m_max_depth(max_depth) { } void operator()(goal_ref const & in, goal_ref_buffer& result) override { operator()(0, in, result); } tactic * translate(ast_manager & m) override { tactic * new_t = m_t->translate(m); return alloc(repeat_tactical, new_t, m_max_depth); } }; tactic * repeat(tactic * t, unsigned max) { return alloc(repeat_tactical, t, max); } class fail_if_branching_tactical : public unary_tactical { unsigned m_threshold; public: fail_if_branching_tactical(tactic * t, unsigned threshold):unary_tactical(t), m_threshold(threshold) {} void operator()(goal_ref const & in, goal_ref_buffer& result) override { m_t->operator()(in, result); if (result.size() > m_threshold) { result.reset(); // assumes in is not strenthened to one of the branches throw tactic_exception("failed-if-branching tactical"); } }; tactic * translate(ast_manager & m) override { tactic * new_t = m_t->translate(m); return alloc(fail_if_branching_tactical, new_t, m_threshold); } }; tactic * fail_if_branching(tactic * t, unsigned threshold) { return alloc(fail_if_branching_tactical, t, threshold); } class cleanup_tactical : public unary_tactical { public: cleanup_tactical(tactic * t):unary_tactical(t) {} void operator()(goal_ref const & in, goal_ref_buffer& result) override { m_t->operator()(in, result); m_t->cleanup(); } tactic * translate(ast_manager & m) override { tactic * new_t = m_t->translate(m); return alloc(cleanup_tactical, new_t); } }; tactic * clean(tactic * t) { return alloc(cleanup_tactical, t); } class try_for_tactical : public unary_tactical { unsigned m_timeout; public: try_for_tactical(tactic * t, unsigned ts):unary_tactical(t), m_timeout(ts) {} void operator()(goal_ref const & in, goal_ref_buffer& result) override { cancel_eh eh(in->m().limit()); { // Warning: scoped_timer is not thread safe in Linux. scoped_timer timer(m_timeout, &eh); m_t->operator()(in, result); } } tactic * translate(ast_manager & m) override { tactic * new_t = m_t->translate(m); return alloc(try_for_tactical, new_t, m_timeout); } }; tactic * try_for(tactic * t, unsigned msecs) { return alloc(try_for_tactical, t, msecs); } class using_params_tactical : public unary_tactical { params_ref m_params; public: using_params_tactical(tactic * t, params_ref const & p):unary_tactical(t), m_params(p) { t->updt_params(p); } void updt_params(params_ref const & p) override { TRACE("using_params", tout << "before p: " << p << "\n"; tout << "m_params: " << m_params << "\n";); params_ref new_p = p; new_p.append(m_params); unary_tactical::updt_params(new_p); TRACE("using_params", tout << "after p: " << p << "\n"; tout << "m_params: " << m_params << "\n"; tout << "new_p: " << new_p << "\n";); } tactic * translate(ast_manager & m) override { tactic * new_t = m_t->translate(m); return alloc(using_params_tactical, new_t, m_params); } }; tactic * using_params(tactic * t, params_ref const & p) { return alloc(using_params_tactical, t, p); } class annotate_tactical : public unary_tactical { std::string m_name; struct scope { std::string m_name; scope(std::string const& name) : m_name(name) { IF_VERBOSE(TACTIC_VERBOSITY_LVL, verbose_stream() << "(" << m_name << " start)\n";); } ~scope() { IF_VERBOSE(TACTIC_VERBOSITY_LVL, verbose_stream() << "(" << m_name << " done)\n";); } }; public: annotate_tactical(char const* name, tactic* t): unary_tactical(t), m_name(name) {} void operator()(goal_ref const & in, goal_ref_buffer& result) override { scope _scope(m_name); m_t->operator()(in, result); } tactic * translate(ast_manager & m) override { tactic * new_t = m_t->translate(m); return alloc(annotate_tactical, m_name.c_str(), new_t); } }; tactic * annotate_tactic(char const* name, tactic * t) { return alloc(annotate_tactical, name, t); } class cond_tactical : public binary_tactical { probe_ref m_p; public: cond_tactical(probe * p, tactic * t1, tactic * t2): binary_tactical(t1, t2), m_p(p) { SASSERT(m_p); } ~cond_tactical() override {} void operator()(goal_ref const & in, goal_ref_buffer & result) override { if (m_p->operator()(*(in.get())).is_true()) m_t1->operator()(in, result); else m_t2->operator()(in, result); } tactic * translate(ast_manager & m) override { tactic * new_t1 = m_t1->translate(m); tactic * new_t2 = m_t2->translate(m); return alloc(cond_tactical, m_p.get(), new_t1, new_t2); } }; tactic * cond(probe * p, tactic * t1, tactic * t2) { return alloc(cond_tactical, p, t1, t2); } tactic * when(probe * p, tactic * t) { return cond(p, t, mk_skip_tactic()); } class fail_if_tactic : public tactic { probe_ref m_p; public: fail_if_tactic(probe * p): m_p(p) { SASSERT(m_p); } ~fail_if_tactic() override {} void cleanup() override {} void operator()(goal_ref const & in, goal_ref_buffer& result) override { if (m_p->operator()(*(in.get())).is_true()) { throw tactic_exception("fail-if tactic"); } result.push_back(in.get()); } tactic * translate(ast_manager & m) override { return this; } }; tactic * fail_if(probe * p) { return alloc(fail_if_tactic, p); } tactic * fail_if_not(probe * p) { return fail_if(mk_not(p)); } class if_no_proofs_tactical : public unary_tactical { public: if_no_proofs_tactical(tactic * t):unary_tactical(t) {} void operator()(goal_ref const & in, goal_ref_buffer & result) override { if (in->proofs_enabled()) { result.push_back(in.get()); } else { m_t->operator()(in, result); } } tactic * translate(ast_manager & m) override { return translate_core(m); } }; class if_no_unsat_cores_tactical : public unary_tactical { public: if_no_unsat_cores_tactical(tactic * t):unary_tactical(t) {} void operator()(goal_ref const & in, goal_ref_buffer& result) override { if (in->unsat_core_enabled()) { result.push_back(in.get()); } else { m_t->operator()(in, result); } } tactic * translate(ast_manager & m) override { return translate_core(m); } }; class if_no_models_tactical : public unary_tactical { public: if_no_models_tactical(tactic * t):unary_tactical(t) {} void operator()(goal_ref const & in, goal_ref_buffer& result) override { if (in->models_enabled()) { result.push_back(in.get()); } else { m_t->operator()(in, result); } } tactic * translate(ast_manager & m) override { return translate_core(m); } }; tactic * if_no_proofs(tactic * t) { return alloc(if_no_proofs_tactical, t); } tactic * if_no_unsat_cores(tactic * t) { return alloc(if_no_unsat_cores_tactical, t); } tactic * if_no_models(tactic * t) { return alloc(if_no_models_tactical, t); } tactic * skip_if_failed(tactic * t) { return or_else(t, mk_skip_tactic()); } z3-z3-4.8.7/src/tactic/tactical.h000066400000000000000000000070561356505360400164320ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: tactical.h Abstract: Basic combinators Author: Leonardo (leonardo) 2011-10-13 Notes: --*/ #ifndef TACTICAL_H_ #define TACTICAL_H_ #include "tactic/tactic.h" #include "tactic/probe.h" tactic * and_then(unsigned num, tactic * const * ts); tactic * and_then(tactic * t1, tactic * t2); tactic * and_then(tactic * t1, tactic * t2, tactic * t3); tactic * and_then(tactic * t1, tactic * t2, tactic * t3, tactic * t4); tactic * and_then(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5); tactic * and_then(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5, tactic * t6); tactic * and_then(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5, tactic * t6, tactic * t7); tactic * and_then(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5, tactic * t6, tactic * t7, tactic * t8); tactic * and_then(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5, tactic * t6, tactic * t7, tactic * t8, tactic * t9); tactic * and_then(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5, tactic * t6, tactic * t7, tactic * t8, tactic * t9, tactic * t10); tactic * and_then(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5, tactic * t6, tactic * t7, tactic * t8, tactic * t9, tactic * t10, tactic * t11); tactic * or_else(unsigned num, tactic * const * ts); tactic * or_else(tactic * t1, tactic * t2); tactic * or_else(tactic * t1, tactic * t2, tactic * t3); tactic * or_else(tactic * t1, tactic * t2, tactic * t3, tactic * t4); tactic * or_else(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5); tactic * or_else(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5, tactic * t6); tactic * or_else(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5, tactic * t6, tactic * t7); tactic * or_else(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5, tactic * t6, tactic * t7, tactic * t8); tactic * or_else(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5, tactic * t6, tactic * t7, tactic * t8, tactic * t9); tactic * or_else(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5, tactic * t6, tactic * t7, tactic * t8, tactic * t9, tactic * t10); tactic * repeat(tactic * t, unsigned max = UINT_MAX); /** \brief Fails if \c t produces more than \c threshold subgoals. Otherwise, it behaves like \c t. */ tactic * fail_if_branching(tactic * t, unsigned threshold = 1); tactic * par(unsigned num, tactic * const * ts); tactic * par(tactic * t1, tactic * t2); tactic * par(tactic * t1, tactic * t2, tactic * t3); tactic * par(tactic * t1, tactic * t2, tactic * t3, tactic * t4); tactic * par_and_then(unsigned num, tactic * const * ts); tactic * par_and_then(tactic * t1, tactic * t2); tactic * try_for(tactic * t, unsigned msecs); tactic * clean(tactic * t); tactic * using_params(tactic * t, params_ref const & p); tactic * annotate_tactic(char const* name, tactic * t); // Create a tactic that fails if the result returned by probe p is true. tactic * fail_if(probe * p); tactic * fail_if_not(probe * p); // Execute t1 if p returns true, and t2 otherwise tactic * cond(probe * p, tactic * t1, tactic * t2); // Alias for cond(p, t, mk_skip_tactic()) tactic * when(probe * p, tactic * t); // alias for (or-else t skip) tactic * skip_if_failed(tactic * t); // Execute the given tactic only if proof production is not enabled. // If proof production is enabled it is a skip tactic * if_no_proofs(tactic * t); tactic * if_no_unsat_cores(tactic * t); tactic * if_no_models(tactic * t); #endif z3-z3-4.8.7/src/tactic/ufbv/000077500000000000000000000000001356505360400154275ustar00rootroot00000000000000z3-z3-4.8.7/src/tactic/ufbv/CMakeLists.txt000066400000000000000000000005741356505360400201750ustar00rootroot00000000000000z3_add_component(ufbv_tactic SOURCES macro_finder_tactic.cpp quasi_macros_tactic.cpp ufbv_rewriter.cpp ufbv_rewriter_tactic.cpp ufbv_tactic.cpp COMPONENT_DEPENDENCIES core_tactics macros normal_forms rewriter smt_tactic TACTIC_HEADERS macro_finder_tactic.h quasi_macros_tactic.h ufbv_rewriter_tactic.h ufbv_tactic.h ) z3-z3-4.8.7/src/tactic/ufbv/macro_finder_tactic.cpp000066400000000000000000000074331356505360400221210ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: macro_finder_tactic.cpp Abstract: Macro finder Author: Christoph (cwinter) 2012-10-26 Notes: --*/ #include "tactic/tactical.h" #include "ast/macros/macro_manager.h" #include "ast/macros/macro_finder.h" #include "tactic/generic_model_converter.h" #include "tactic/ufbv/macro_finder_tactic.h" class macro_finder_tactic : public tactic { struct imp { ast_manager & m_manager; bool m_elim_and; imp(ast_manager & m, params_ref const & p) : m_manager(m), m_elim_and(false) { updt_params(p); } ast_manager & m() const { return m_manager; } void operator()(goal_ref const & g, goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); tactic_report report("macro-finder", *g); bool produce_proofs = g->proofs_enabled(); bool unsat_core_enabled = g->unsat_core_enabled(); macro_manager mm(m_manager); macro_finder mf(m_manager, mm); expr_ref_vector forms(m_manager), new_forms(m_manager); proof_ref_vector proofs(m_manager), new_proofs(m_manager); expr_dependency_ref_vector deps(m_manager), new_deps(m_manager); unsigned size = g->size(); for (unsigned idx = 0; idx < size; idx++) { forms.push_back(g->form(idx)); proofs.push_back(g->pr(idx)); deps.push_back(g->dep(idx)); } mf(forms.size(), forms.c_ptr(), proofs.c_ptr(), deps.c_ptr(), new_forms, new_proofs, new_deps); g->reset(); for (unsigned i = 0; i < new_forms.size(); i++) g->assert_expr(new_forms.get(i), produce_proofs ? new_proofs.get(i) : nullptr, unsat_core_enabled ? new_deps.get(i) : nullptr); generic_model_converter * evmc = alloc(generic_model_converter, mm.get_manager(), "macro_finder"); unsigned num = mm.get_num_macros(); for (unsigned i = 0; i < num; i++) { expr_ref f_interp(mm.get_manager()); func_decl * f = mm.get_macro_interpretation(i, f_interp); evmc->add(f, f_interp); } g->add(evmc); g->inc_depth(); result.push_back(g.get()); TRACE("macro-finder", g->display(tout);); SASSERT(g->is_well_sorted()); } void updt_params(params_ref const & p) { m_elim_and = p.get_bool("elim_and", false); } }; imp * m_imp; params_ref m_params; public: macro_finder_tactic(ast_manager & m, params_ref const & p): m_params(p) { m_imp = alloc(imp, m, p); } tactic * translate(ast_manager & m) override { return alloc(macro_finder_tactic, m, m_params); } ~macro_finder_tactic() override { dealloc(m_imp); } void updt_params(params_ref const & p) override { m_params = p; m_imp->updt_params(p); } void collect_param_descrs(param_descrs & r) override { insert_max_memory(r); insert_produce_models(r); insert_produce_proofs(r); r.insert("elim_and", CPK_BOOL, "(default: false) eliminate conjunctions during (internal) calls to the simplifier."); } void operator()(goal_ref const & in, goal_ref_buffer & result) override { (*m_imp)(in, result); } void cleanup() override { ast_manager & m = m_imp->m(); imp * d = alloc(imp, m, m_params); std::swap(d, m_imp); dealloc(d); } }; tactic * mk_macro_finder_tactic(ast_manager & m, params_ref const & p) { return alloc(macro_finder_tactic, m, p); } z3-z3-4.8.7/src/tactic/ufbv/macro_finder_tactic.h000066400000000000000000000007571356505360400215700ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: macro_finder_tactic.h Abstract: Macro finder Author: Christoph (cwinter) 2012-10-26 Notes: --*/ #ifndef MACRO_FINDER_TACTIC_H_ #define MACRO_FINDER_TACTIC_H_ #include "util/params.h" class ast_manager; class tactic; tactic * mk_macro_finder_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("macro-finder", "Identifies and applies macros.", "mk_macro_finder_tactic(m, p)") */ #endif z3-z3-4.8.7/src/tactic/ufbv/quasi_macros_tactic.cpp000066400000000000000000000100071356505360400221460ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: quasi_macros_tactic.cpp Abstract: Quasi-Macros Author: Christoph (cwinter) 2012-10-26 Notes: --*/ #include "tactic/tactical.h" #include "tactic/generic_model_converter.h" #include "ast/macros/macro_manager.h" #include "ast/macros/macro_finder.h" #include "ast/macros/quasi_macros.h" #include "tactic/ufbv/quasi_macros_tactic.h" class quasi_macros_tactic : public tactic { struct imp { ast_manager & m_manager; imp(ast_manager & m, params_ref const & p) : m_manager(m) { updt_params(p); } ast_manager & m() const { return m_manager; } void operator()(goal_ref const & g, goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); tactic_report report("quasi-macros", *g); bool produce_proofs = g->proofs_enabled(); bool produce_unsat_cores = g->unsat_core_enabled(); macro_manager mm(m_manager); quasi_macros qm(m_manager, mm); bool more = true; expr_ref_vector forms(m_manager), new_forms(m_manager); proof_ref_vector proofs(m_manager), new_proofs(m_manager); expr_dependency_ref_vector deps(m_manager), new_deps(m_manager); unsigned size = g->size(); for (unsigned i = 0; i < size; i++) { forms.push_back(g->form(i)); proofs.push_back(g->pr(i)); deps.push_back(g->dep(i)); } while (more) { // CMW: use repeat(...) ? if (m().canceled()) throw tactic_exception(m().limit().get_cancel_msg()); new_forms.reset(); new_proofs.reset(); new_deps.reset(); more = qm(forms.size(), forms.c_ptr(), proofs.c_ptr(), deps.c_ptr(), new_forms, new_proofs, new_deps); forms.swap(new_forms); proofs.swap(new_proofs); deps.swap(new_deps); } g->reset(); for (unsigned i = 0; i < new_forms.size(); i++) g->assert_expr(forms.get(i), produce_proofs ? proofs.get(i) : nullptr, produce_unsat_cores ? deps.get(i) : nullptr); generic_model_converter * evmc = alloc(generic_model_converter, mm.get_manager(), "quasi_macros"); unsigned num = mm.get_num_macros(); for (unsigned i = 0; i < num; i++) { expr_ref f_interp(mm.get_manager()); func_decl * f = mm.get_macro_interpretation(i, f_interp); evmc->add(f, f_interp); } g->add(evmc); g->inc_depth(); result.push_back(g.get()); TRACE("quasi-macros", g->display(tout);); SASSERT(g->is_well_sorted()); } void updt_params(params_ref const & p) { } }; imp * m_imp; params_ref m_params; public: quasi_macros_tactic(ast_manager & m, params_ref const & p): m_params(p) { m_imp = alloc(imp, m, p); } tactic * translate(ast_manager & m) override { return alloc(quasi_macros_tactic, m, m_params); } ~quasi_macros_tactic() override { dealloc(m_imp); } void updt_params(params_ref const & p) override { m_params = p; m_imp->updt_params(p); } void collect_param_descrs(param_descrs & r) override { insert_max_memory(r); insert_produce_models(r); insert_produce_proofs(r); } void operator()(goal_ref const & in, goal_ref_buffer & result) override { (*m_imp)(in, result); } void cleanup() override { ast_manager & m = m_imp->m(); imp * d = alloc(imp, m, m_params); std::swap(d, m_imp); dealloc(d); } }; tactic * mk_quasi_macros_tactic(ast_manager & m, params_ref const & p) { return alloc(quasi_macros_tactic, m, p); } z3-z3-4.8.7/src/tactic/ufbv/quasi_macros_tactic.h000066400000000000000000000007651356505360400216250ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: quasi_macros_tactic.h Abstract: Quasi-Macros Author: Christoph (cwinter) 2012-10-26 Notes: --*/ #ifndef QUASI_MACROS_TACTIC_H_ #define QUASI_MACROS_TACTIC_H_ #include "util/params.h" class ast_manager; class tactic; tactic * mk_quasi_macros_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("quasi-macros", "Identifies and applies quasi-macros.", "mk_quasi_macros_tactic(m, p)") */ #endif z3-z3-4.8.7/src/tactic/ufbv/ufbv_rewriter.cpp000066400000000000000000000764751356505360400210430ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: demodulator.cpp Abstract: Author: Leonardo de Moura (leonardo) 2010-04-12. Revision History: Christoph M. Wintersteiger (cwinter) 2010-04-21: Implementation Christoph M. Wintersteiger (cwinter) 2012-10-24: Moved from demodulator.h to ufbv_rewriter.h --*/ #include "util/uint_set.h" #include "ast/ast_pp.h" #include "ast/for_each_expr.h" #include "ast/rewriter/var_subst.h" #include "tactic/ufbv/ufbv_rewriter.h" ufbv_rewriter::ufbv_rewriter(ast_manager & m): m_manager(m), m_match_subst(m), m_bsimp(m), m_todo(m), m_rewrite_todo(m), m_rewrite_cache(m), m_new_exprs(m) { params_ref p; p.set_bool("elim_and", true); m_bsimp.updt_params(p); } ufbv_rewriter::~ufbv_rewriter() { reset_dealloc_values(m_fwd_idx); reset_dealloc_values(m_back_idx); for (demodulator2lhs_rhs::iterator it = m_demodulator2lhs_rhs.begin(); it != m_demodulator2lhs_rhs.end(); it++) { m_manager.dec_ref(it->m_key); m_manager.dec_ref(it->m_value.first); m_manager.dec_ref(it->m_value.second); } } bool ufbv_rewriter::is_demodulator(expr * e, expr_ref & large, expr_ref & small) const { if (e->get_kind() == AST_QUANTIFIER) { quantifier * q = to_quantifier(e); if (is_forall(q)) { expr * qe = q->get_expr(); if ((m_manager.is_eq(qe) || m_manager.is_iff(qe))) { app * eq = to_app(q->get_expr()); expr * lhs = eq->get_arg(0); expr * rhs = eq->get_arg(1); int subset = is_subset(lhs, rhs); int smaller = is_smaller(lhs, rhs); TRACE("demodulator", tout << "testing is_demodulator:\n" << mk_pp(lhs, m_manager) << "\n" << mk_pp(rhs, m_manager) << "\n" << "subset: " << subset << ", smaller: " << smaller << "\n";); // We only track uninterpreted functions, everything else is likely too expensive. if ((subset == +1 || subset == +2) && smaller == +1) { if (is_uninterp(rhs)) { large = rhs; small = lhs; return true; } #if 1 // lhs = (not rhs) --> (not lhs) = rhs expr * not_rhs; if (m_manager.is_not(rhs, not_rhs) && is_uninterp(not_rhs)) { large = not_rhs; small = m_manager.mk_not(lhs); return true; } #endif } if ((subset == -1 || subset == +2) && smaller == -1) { if (is_uninterp(lhs)) { large = lhs; small = rhs; return true; } #if 1 // (not lhs) = rhs --> lhs = (not rhs) expr * not_lhs; if (m_manager.is_not(lhs, not_lhs) && is_uninterp(not_lhs)) { large = not_lhs; small = m_manager.mk_not(rhs); return true; } #endif } } else if (m_manager.is_not(qe) && is_uninterp(to_app(qe)->get_arg(0))) { // this is like (not (f ... )) --> (= (f ...) false) large = to_app(qe)->get_arg(0); small = m_manager.mk_false(); return true; } else if (is_uninterp(qe)) { // this is like (f ... ) --> (= (f ...) true) large = to_app(qe); small = m_manager.mk_true(); return true; } } } return false; } class var_set_proc { uint_set & m_set; public: var_set_proc(uint_set &s):m_set(s) {} void operator()(var * n) { m_set.insert(n->get_idx()); } void operator()(quantifier * n) {} void operator()(app * n) {} }; int ufbv_rewriter::is_subset(expr * e1, expr * e2) const { uint_set ev1, ev2; if (m_manager.is_value(e1)) return 1; // values are always a subset! var_set_proc proc1(ev1); for_each_expr(proc1, e1); var_set_proc proc2(ev2); for_each_expr(proc2, e2); return (ev1==ev2 ) ? +2 : // We return +2 if the sets are equal. (ev1.subset_of(ev2)) ? +1 : (ev2.subset_of(ev1)) ? -1 : 0 ; } int ufbv_rewriter::is_smaller(expr * e1, expr * e2) const { unsigned sz1 = 0, sz2 = 0; // values are always smaller! if (m_manager.is_value(e1)) return +1; else if (m_manager.is_value(e2)) return -1; // interpreted stuff is always better than uninterpreted. if (!is_uninterp(e1) && is_uninterp(e2)) return +1; else if (is_uninterp(e1) && !is_uninterp(e2)) return -1; // two uninterpreted functions are ordered first by the number of // arguments, then by their id. if (is_uninterp(e1) && is_uninterp(e2)) { if (to_app(e1)->get_num_args() < to_app(e2)->get_num_args()) return +1; else if (to_app(e1)->get_num_args() > to_app(e2)->get_num_args()) return -1; else { unsigned a = to_app(e1)->get_decl()->get_id(); unsigned b = to_app(e2)->get_decl()->get_id(); if (a < b) return +1; else if (a > b) return -1; } } switch (e1->get_kind()) { case AST_VAR: sz1 = 1; break; case AST_QUANTIFIER: sz1 = to_quantifier(e1)->get_depth(); break; case AST_APP: sz1 = to_app(e1)->get_depth(); break; default: UNREACHABLE(); } switch (e2->get_kind()) { case AST_VAR: sz2 = 1; break; case AST_QUANTIFIER: sz2 = to_quantifier(e2)->get_depth(); break; case AST_APP: sz2 = to_app(e2)->get_depth(); break; default: UNREACHABLE(); } return (sz1 == sz2) ? 0 : (sz1 < sz2) ? +1 : -1 ; } class max_var_id_proc { unsigned m_max_var_id; public: max_var_id_proc():m_max_var_id(0) {} void operator()(var * n) { if(n->get_idx() > m_max_var_id) m_max_var_id = n->get_idx(); } void operator()(quantifier * n) {} void operator()(app * n) {} unsigned get_max() { return m_max_var_id; } }; unsigned ufbv_rewriter::max_var_id(expr * e) { max_var_id_proc proc; for_each_expr(proc, e); return proc.get_max(); } void ufbv_rewriter::insert_fwd_idx(expr * large, expr * small, quantifier * demodulator) { SASSERT(large->get_kind() == AST_APP); SASSERT(demodulator); SASSERT(large && small); TRACE("demodulator_fwd", tout << "INSERT: " << mk_pp(demodulator, m_manager) << std::endl; ); func_decl * fd = to_app(large)->get_decl(); fwd_idx_map::iterator it = m_fwd_idx.find_iterator(fd); if (it == m_fwd_idx.end()) { quantifier_set * qs = alloc(quantifier_set, 1); m_fwd_idx.insert(fd, qs); it = m_fwd_idx.find_iterator(fd); } SASSERT(it->m_value); it->m_value->insert(demodulator); m_manager.inc_ref(demodulator); m_manager.inc_ref(large); m_manager.inc_ref(small); m_demodulator2lhs_rhs.insert(demodulator, expr_pair(large, small)); } void ufbv_rewriter::remove_fwd_idx(func_decl * f, quantifier * demodulator) { TRACE("demodulator_fwd", tout << "REMOVE: " << std::hex << (size_t)demodulator << std::endl; ); fwd_idx_map::iterator it = m_fwd_idx.find_iterator(f); if (it != m_fwd_idx.end()) { demodulator2lhs_rhs::iterator fit = m_demodulator2lhs_rhs.find_iterator(demodulator); m_manager.dec_ref(fit->m_value.first); m_manager.dec_ref(fit->m_value.second); m_manager.dec_ref(demodulator); m_demodulator2lhs_rhs.erase(demodulator); it->m_value->erase(demodulator); } else { SASSERT(m_demodulator2lhs_rhs.contains(demodulator)); } } bool ufbv_rewriter::check_fwd_idx_consistency() { for (fwd_idx_map::iterator it = m_fwd_idx.begin(); it != m_fwd_idx.end() ; it++ ) { quantifier_set * set = it->m_value; SASSERT(set); for (quantifier_set::iterator sit = set->begin(); sit != set->end(); sit++) { if (!m_demodulator2lhs_rhs.contains(*sit)) return false; } } return true; } void ufbv_rewriter::show_fwd_idx(std::ostream & out) { for (fwd_idx_map::iterator it = m_fwd_idx.begin(); it != m_fwd_idx.end() ; it++ ) { quantifier_set * set = it->m_value; SASSERT(!set); out << it->m_key->get_name() << ": " << std::endl; for (quantifier_set::iterator sit = set->begin(); sit != set->end(); sit++) { out << std::hex << (size_t)*sit << std::endl; } } out << "D2LR: " << std::endl; for (demodulator2lhs_rhs::iterator it = m_demodulator2lhs_rhs.begin(); it != m_demodulator2lhs_rhs.end() ; it++) { out << (size_t) it->m_key << std::endl; } } bool ufbv_rewriter::rewrite1(func_decl * f, ptr_vector & m_new_args, expr_ref & np) { fwd_idx_map::iterator it = m_fwd_idx.find_iterator(f); if (it != m_fwd_idx.end()) { TRACE("demodulator_bug", tout << "trying to rewrite: " << f->get_name() << " args:\n"; for (unsigned i = 0; i < m_new_args.size(); i++) { tout << mk_pp(m_new_args[i], m_manager) << "\n"; }); quantifier_set::iterator dit = it->m_value->begin(); quantifier_set::iterator dend = it->m_value->end(); for ( ; dit != dend ; dit++ ) { quantifier * d = *dit; SASSERT(m_demodulator2lhs_rhs.contains(d)); expr_pair l_s; m_demodulator2lhs_rhs.find(d, l_s); app * large = to_app(l_s.first); if (large->get_num_args() != m_new_args.size()) continue; TRACE("demodulator_bug", tout << "Matching with demodulator: " << mk_pp(d, m_manager) << std::endl; ); SASSERT(large->get_decl() == f); if (m_match_subst(large, l_s.second, m_new_args.c_ptr(), np)) { TRACE("demodulator_bug", tout << "succeeded...\n" << mk_pp(l_s.second, m_manager) << "\n===>\n" << mk_pp(np, m_manager) << "\n";); return true; } } } return false; } bool ufbv_rewriter::rewrite_visit_children(app * a) { bool res=true; unsigned j = a->get_num_args(); while (j > 0) { expr * e = a->get_arg(--j); if (!m_rewrite_cache.contains(e) || !m_rewrite_cache.get(e).second) { bool recursive = false; unsigned sz = m_rewrite_todo.size(); expr * v = e; if (m_rewrite_cache.contains(e)) { expr_bool_pair const & ebp = m_rewrite_cache.get(e); if (ebp.second) v = ebp.first; } for (unsigned i = sz; i > 0; i--) { if (m_rewrite_todo[i - 1] == v) { recursive = true; TRACE("demodulator", tout << "Detected demodulator cycle: " << mk_pp(a, m_manager) << " --> " << mk_pp(v, m_manager) << std::endl;); m_rewrite_cache.insert(e, expr_bool_pair(v, true)); break; } } if (!recursive) { m_rewrite_todo.push_back(e); res = false; } } } return res; } void ufbv_rewriter::rewrite_cache(expr * e, expr * new_e, bool done) { m_rewrite_cache.insert(e, expr_bool_pair(new_e, done)); } expr * ufbv_rewriter::rewrite(expr * n) { if (m_fwd_idx.empty()) return n; TRACE("demodulator", tout << "rewrite: " << mk_pp(n, m_manager) << std::endl; ); app * a; SASSERT(m_rewrite_todo.empty()); m_rewrite_cache.reset(); m_rewrite_todo.push_back(n); while (!m_rewrite_todo.empty()) { TRACE("demodulator_stack", tout << "STACK: " << std::endl; for ( unsigned i = 0; i\n"; tout << "na:\n " << mk_pp(na, m_manager) << "\n";); rewrite_cache(e, na, true); } m_rewrite_todo.pop_back(); } } break; case AST_QUANTIFIER: { expr * body = to_quantifier(actual)->get_expr(); if (m_rewrite_cache.contains(body)) { const expr_bool_pair ebp = m_rewrite_cache.get(body); SASSERT(ebp.second); expr * new_body = ebp.first; quantifier_ref q(m_manager); q = m_manager.update_quantifier(to_quantifier(actual), new_body); m_new_exprs.push_back(q); expr_ref new_q = elim_unused_vars(m_manager, q, params_ref()); m_new_exprs.push_back(new_q); rewrite_cache(e, new_q, true); m_rewrite_todo.pop_back(); } else { m_rewrite_todo.push_back(body); } break; } default: UNREACHABLE(); } } SASSERT(m_rewrite_cache.contains(n)); const expr_bool_pair & ebp = m_rewrite_cache.get(n); SASSERT(ebp.second); expr * r = ebp.first; TRACE("demodulator", tout << "rewrite result: " << mk_pp(r, m_manager) << std::endl; ); return r; } class ufbv_rewriter::add_back_idx_proc { back_idx_map & m_back_idx; expr * m_expr; public: add_back_idx_proc(back_idx_map & bi, expr * e):m_back_idx(bi),m_expr(e) {} void operator()(var * n) {} void operator()(quantifier * n) {} void operator()(app * n) { // We track only uninterpreted and constant functions. if (n->get_num_args()==0) return; SASSERT(m_expr && m_expr != (expr*) 0x00000003); func_decl * d=n->get_decl(); if (d->get_family_id() == null_family_id) { back_idx_map::iterator it = m_back_idx.find_iterator(d); if (it != m_back_idx.end()) { SASSERT(it->m_value); it->m_value->insert(m_expr); } else { expr_set * e = alloc(expr_set); e->insert(m_expr); m_back_idx.insert(d, e); } } } }; class ufbv_rewriter::remove_back_idx_proc { back_idx_map & m_back_idx; expr * m_expr; public: remove_back_idx_proc(back_idx_map & bi, expr * e):m_back_idx(bi),m_expr(e) {} void operator()(var * n) {} void operator()(quantifier * n) {} void operator()(app * n) { // We track only uninterpreted and constant functions. if (n->get_num_args()==0) return; func_decl * d=n->get_decl(); if (d->get_family_id() == null_family_id) { back_idx_map::iterator it = m_back_idx.find_iterator(d); if (it != m_back_idx.end()) { SASSERT(it->m_value); it->m_value->remove(m_expr); } } } }; void ufbv_rewriter::reschedule_processed(func_decl * f) { //use m_back_idx to find all formulas p in m_processed that contains f { back_idx_map::iterator it = m_back_idx.find_iterator(f); if (it != m_back_idx.end()) { SASSERT(it->m_value); expr_set temp; expr_set::iterator sit = it->m_value->begin(); expr_set::iterator send = it->m_value->end(); for ( ; sit != send ; sit++ ) { expr * p = *sit; if (m_processed.contains(p)) temp.insert(p); } sit = temp.begin(); send = temp.end(); for ( ; sit != send; sit++) { expr * p = *sit; // remove p from m_processed and m_back_idx m_processed.remove(p); remove_back_idx_proc proc(m_back_idx, p); // this could change it->m_value, thus we need the `temp' set. for_each_expr(proc, p); // insert p into m_todo m_todo.push_back(p); } } } bool ufbv_rewriter::can_rewrite(expr * n, expr * lhs) { // this is a quick check, we just traverse d and check if there is an expression in d that is an instance of lhs of n'. // we cannot use the trick used for m_processed, since the main loop would not terminate. ptr_vector stack; expr * curr; expr_mark visited; stack.push_back(n); while (!stack.empty()) { curr = stack.back(); if (visited.is_marked(curr)) { stack.pop_back(); continue; } switch(curr->get_kind()) { case AST_VAR: visited.mark(curr, true); stack.pop_back(); break; case AST_APP: if (for_each_expr_args(stack, visited, to_app(curr)->get_num_args(), to_app(curr)->get_args())) { if (m_match_subst(lhs, curr)) return true; visited.mark(curr, true); stack.pop_back(); } break; case AST_QUANTIFIER: if (!for_each_expr_args(stack, visited, to_quantifier(curr)->get_num_patterns(), to_quantifier(curr)->get_patterns())) { break; } if (!for_each_expr_args(stack, visited, to_quantifier(curr)->get_num_no_patterns(), to_quantifier(curr)->get_no_patterns())) { break; } if (!visited.is_marked(to_quantifier(curr)->get_expr())) { stack.push_back(to_quantifier(curr)->get_expr()); break; } stack.pop_back(); break; default: UNREACHABLE(); } } return false; } void ufbv_rewriter::reschedule_demodulators(func_decl * f, expr * lhs) { // use m_back_idx to find all demodulators d in m_fwd_idx that contains f { //ptr_vector to_remove; back_idx_map::iterator it = m_back_idx.find_iterator(f); if (it != m_back_idx.end()) { SASSERT(it->m_value); expr_set all_occurrences; expr_ref l(m_manager); expr_set::iterator esit = it->m_value->begin(); expr_set::iterator esend = it->m_value->end(); for ( ; esit != esend ; esit++) all_occurrences.insert(*esit); // Run over all f-demodulators esit = all_occurrences.begin(); esend = all_occurrences.end(); for ( ; esit != esend ; esit++ ) { expr * occ = *esit; if (!is_quantifier(occ)) continue; // Use the fwd idx to find out whether this is a demodulator. demodulator2lhs_rhs::iterator d2lr_it = m_demodulator2lhs_rhs.find_iterator(to_quantifier(occ)); if (d2lr_it != m_demodulator2lhs_rhs.end()) { l = d2lr_it->m_value.first; quantifier_ref d(m_manager); func_decl_ref df(m_manager); d = to_quantifier(occ); df = to_app(l)->get_decl(); // Now we know there is an occurrence of f in d // if n' can rewrite d { if (can_rewrite(d, lhs)) { TRACE("demodulator", tout << "Rescheduling: " << std::endl << mk_pp(d, m_manager) << std::endl; ); // remove d from m_fwd_idx remove_fwd_idx(df, d); // remove d from m_back_idx // just remember it here, because otherwise it and/or esit might become invalid? // to_remove.insert(d); remove_back_idx_proc proc(m_back_idx, d); for_each_expr(proc, d); // insert d into m_todo m_todo.push_back(d); } } } } //for (ptr_vector::iterator it = to_remove.begin(); it != to_remove.end(); it++) { // expr * d = *it; // remove_back_idx_proc proc(m_manager, m_back_idx, d); // for_each_expr(proc, d); //} } void ufbv_rewriter::operator()(unsigned n, expr * const * exprs, proof * const * prs, expr_ref_vector & new_exprs, proof_ref_vector & new_prs) { if (m_manager.proofs_enabled()) { // Let us not waste time with proof production warning_msg("PRE_DEMODULATOR=true is not supported when proofs are enabled."); new_exprs.append(n, exprs); new_prs.append(n, prs); return; } TRACE("demodulator", tout << "before demodulator:\n"; for ( unsigned i = 0 ; i < n ; i++ ) tout << mk_pp(exprs[i], m_manager) << std::endl; ); // Initially, m_todo contains all formulas. That is, it contains the argument exprs. m_fwd_idx, m_processed, m_back_idx are empty. unsigned max_vid = 0; for ( unsigned i = 0 ; i < n ; i++ ) { m_todo.push_back(exprs[i]); max_vid = std::max(max_vid, max_var_id(exprs[i])); } m_match_subst.reserve(max_vid); while (!m_todo.empty()) { // let n be the next formula in m_todo. expr_ref cur(m_manager); cur = m_todo.back(); m_todo.pop_back(); // rewrite cur using m_fwd_idx, and let n' be the result. expr * np = rewrite(cur); // at this point, it should be the case that there is no demodulator in m_fwd_idx that can rewrite n'. SASSERT(rewrite(np)==np); // if (n' is not a demodulator) { expr_ref large(m_manager), small(m_manager); if (!is_demodulator(np, large, small)) { // insert n' into m_processed m_processed.insert(np); // update m_back_idx (traverse n' and for each uninterpreted function declaration f in n' add the entry f->n' to m_back_idx) add_back_idx_proc proc(m_back_idx, np); for_each_expr(proc, np); } else { // np is a demodulator that allows us to replace 'large' with 'small'. TRACE("demodulator", tout << "Found demodulator: " << std::endl; tout << mk_pp(large.get(), m_manager) << std::endl << " ---> " << std::endl << mk_pp(small.get(), m_manager) << std::endl; ); TRACE("demodulator_s", tout << "Found demodulator: " << std::endl; tout << to_app(large)->get_decl()->get_name() << "[" << to_app(large)->get_depth() << "]" << " ---> "; if (is_app(small)) tout << to_app(small)->get_decl()->get_name() << "[" << to_app(small)->get_depth() << "]" << std::endl; else tout << mk_pp(small.get(), m_manager) << std::endl; ); // let f be the top symbol of n' SASSERT(is_app(large)); func_decl * f = to_app(large)->get_decl(); reschedule_processed(f); reschedule_demodulators(f, large); // insert n' into m_fwd_idx insert_fwd_idx(large, small, to_quantifier(np)); // update m_back_idx add_back_idx_proc proc(m_back_idx, np); for_each_expr(proc, np); } } // the result is the contents of m_processed + all demodulators in m_fwd_idx. obj_hashtable::iterator pit = m_processed.begin(); obj_hashtable::iterator pend = m_processed.end(); for ( ; pit != pend ; pit++ ) { new_exprs.push_back(*pit); TRACE("demodulator", tout << mk_pp(*pit, m_manager) << std::endl; ); } fwd_idx_map::iterator fit = m_fwd_idx.begin(); fwd_idx_map::iterator fend = m_fwd_idx.end(); for ( ; fit != fend ; fit++ ) { if (fit->m_value) { quantifier_set::iterator dit = fit->m_value->begin(); quantifier_set::iterator dend = fit->m_value->end(); for ( ; dit != dend ; dit++ ) { expr * e = *dit; new_exprs.push_back(e); TRACE("demodulator", tout << mk_pp(*dit, m_manager) << std::endl; ); } } } TRACE("demodulator", tout << "after demodulator:\n"; for ( unsigned i = 0 ; i < new_exprs.size() ; i++ ) tout << mk_pp(new_exprs[i].get(), m_manager) << std::endl; ); } ufbv_rewriter::match_subst::match_subst(ast_manager & m): m_manager(m), m_subst(m) { } /** \brief Auxiliary functor used to implement optimization in match_args. See comment there. */ struct match_args_aux_proc { substitution & m_subst; struct no_match {}; match_args_aux_proc(substitution & s):m_subst(s) {} void operator()(var * n) { expr_offset r; if (m_subst.find(n, 0, r)) { if (r.get_expr() != n) { SASSERT(r.get_offset() == 1); throw no_match(); } else { m_subst.insert(n, 0, expr_offset(n, 1)); } } } void operator()(quantifier * n) { throw no_match(); } void operator()(app * n) {} }; bool ufbv_rewriter::match_subst::match_args(app * lhs, expr * const * args) { m_cache.reset(); m_todo.reset(); // fill todo-list, and perform quick success/failure tests m_all_args_eq = true; unsigned num_args = lhs->get_num_args(); for (unsigned i = 0; i < num_args; i++) { expr * t_arg = lhs->get_arg(i); expr * i_arg = args[i]; if (t_arg != i_arg) m_all_args_eq = false; if (is_app(t_arg) && is_app(i_arg) && to_app(t_arg)->get_decl() != to_app(i_arg)->get_decl()) { // quick failure... return false; } m_todo.push_back(expr_pair(t_arg, i_arg)); } if (m_all_args_eq) { // quick success worked... return true; } m_subst.reset(); while (!m_todo.empty()) { expr_pair const & p = m_todo.back(); if (is_var(p.first)) { expr_offset r; if (m_subst.find(to_var(p.first), 0, r)) { if (r.get_expr() != p.second) return false; } else { m_subst.insert(to_var(p.first), 0, expr_offset(p.second, 1)); } m_todo.pop_back(); continue; } if (is_var(p.second)) return false; // we may have nested quantifiers. if (is_quantifier(p.first) || is_quantifier(p.second)) return false; SASSERT(is_app(p.first) && is_app(p.second)); if (to_app(p.first)->is_ground() && !to_app(p.second)->is_ground()) return false; if (p.first == p.second && to_app(p.first)->is_ground()) { SASSERT(to_app(p.second)->is_ground()); m_todo.pop_back(); continue; } if (m_cache.contains(p)) { m_todo.pop_back(); continue; } if (p.first == p.second) { // p.first and p.second is not ground... // Traverse p.first and check whether every variable X:0 in p.first // 1) is unbounded (then we bind X:0 -> X:1) // 2) or, is already bounded to X:1 // If that is, the case, we execute: // m_todo.pop_back(); // m_cache.insert(p); // continue; // Otherwise // return false; match_args_aux_proc proc(m_subst); try { for_each_expr(proc, p.first); // succeeded m_todo.pop_back(); m_cache.insert(p); continue; } catch (const match_args_aux_proc::no_match &) { return false; } } app * n1 = to_app(p.first); app * n2 = to_app(p.second); if (n1->get_decl() != n2->get_decl()) return false; unsigned num_args1 = n1->get_num_args(); if (num_args1 != n2->get_num_args()) return false; m_todo.pop_back(); if (num_args1 == 0) continue; m_cache.insert(p); unsigned j = num_args1; while (j > 0) { --j; m_todo.push_back(expr_pair(n1->get_arg(j), n2->get_arg(j))); } } return true; } bool ufbv_rewriter::match_subst::operator()(app * lhs, expr * rhs, expr * const * args, expr_ref & new_rhs) { if (match_args(lhs, args)) { if (m_all_args_eq) { // quick success... new_rhs = rhs; return true; } unsigned deltas[2] = { 0, 0 }; m_subst.apply(2, deltas, expr_offset(rhs, 0), new_rhs); return true; } return false; } bool ufbv_rewriter::match_subst::operator()(expr * t, expr * i) { m_cache.reset(); m_todo.reset(); if (is_var(t)) return true; if (is_app(t) && is_app(i) && to_app(t)->get_decl() == to_app(i)->get_decl() && to_app(t)->get_num_args() == to_app(i)->get_num_args()) { return match_args(to_app(t), to_app(i)->get_args()); } return false; } z3-z3-4.8.7/src/tactic/ufbv/ufbv_rewriter.h000066400000000000000000000264141356505360400204740ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: demodulator.h Abstract: Author: Leonardo de Moura (leonardo) 2010-04-12. Revision History: Christoph M. Wintersteiger (cwinter) 2012-10-24: Moved from demodulator.h to ufbv_rewriter.h --*/ #ifndef UFBV_REWRITER_H_ #define UFBV_REWRITER_H_ #include "ast/ast.h" #include "ast/substitution/substitution.h" #include "ast/rewriter/bool_rewriter.h" #include "util/obj_hashtable.h" #include "util/obj_pair_hashtable.h" #include "util/array_map.h" /** \brief Apply demodulators as a preprocessing technique. In first-order theorem proving (FOTP), a demodulator is a universally quantified formula of the form: Forall X1, ..., Xn. L[X1, ..., Xn] = R[X1, ..., Xn] Where L[X1, ..., Xn] contains all variables in R[X1, ..., Xn], and L[X1, ..., Xn] is "bigger" than R[X1, ...,Xn]. The idea is to replace something big L[X1, ..., Xn] with something smaller R[X1, ..., Xn]. In FOTP, they use term orderings to decide what does it mean to be smaller. We are using demodulators in a different context (pre-processing). So, I suggest we have a virtual method is_smaller for comparing expressions. The default implementation just compares the size of the expressions. Similarly, in our context, formulas using iff are also demodulators. Forall X1, ..., Xn. L[X1, ..., Xn] iff R[X1, ..., Xn] After selecting the demodulators, we traverse the rest of the formula looking for instances of L[X1, ..., Xn]. Whenever we find an instance, we replace it with the associated instance of R[X1, ..., Xn]. For example, suppose we have Forall x, y. f(x+y, y) = y and f(g(b) + h(c), h(c)) <= 0 The term f(g(b) + h(c), h(c)) is an instance of f(x+y, y) if we replace x <- g(b) and y <- h(c). So, we can replace it with "y" which is bound to h(c) in this example. So, the result of the transformation is: Forall x, y. f(x+y, y) = y and h(c) <= 0 In the first implementation, let us ignore theory matching. That is, for us the term f(a+1) is not an instance of f(1+x), because the matcher doesn't know + is commutative. Observe the demodulator is *not* copied to the macro manager in this case. Another complication is when we are looking for instances inside other universally quantified formulas. The problem is that both formulas (demodular) and target are reusing variables names (ids). To avoid renaming, we use offsets. The idea is to represent renames implicitly. In this case, each offset is a different "variable bank". A pair (expr, offset) is essentially an expression where every variable in expr is assumed to be from the "bank" offset. The class substitution (in substitution.h) manages offsets for us. The class matcher (in matcher.h) can be used to test whether an expression is an instance of another one. Finally, there is the problem when we have N demodulators (where N is big), and a big formula, and we want to traverse the formula only once looking for opportunities for applying these N demodulators. We want to efficiently find the applicable demodulars. We can start with a simple optimization that given a func_decl it returns the set of demodulators that start with this declaration. For example, suppose we have the demodulators. forall x, f(x, g(0)) = 10 forall x, f(g(h(x)), g(1)) = 20 Then, in our "index" f would map to these two demodulators. As a final optimization, we should adapt the code to use substitution-trees. The current implementation in Z3 is not efficient, and I think it is buggy. So, it would be great to replace it with a new one. The code in spc_rewriter.* does something like that. We cannot reuse this code directly since it is meant for the superposion engine in Z3, but we can adapt it for our needs in the preprocessor. */ class ufbv_rewriter { class rewrite_proc; class add_back_idx_proc; class remove_back_idx_proc; class can_rewrite_proc; typedef std::pair expr_bool_pair; class plugin { ast_manager& m_manager; public: plugin(ast_manager& m): m_manager(m) { } void ins_eh(expr* k, expr_bool_pair v) { m_manager.inc_ref(k); m_manager.inc_ref(v.first); } void del_eh(expr* k, expr_bool_pair v) { m_manager.dec_ref(k); m_manager.dec_ref(v.first); } static unsigned to_int(expr const * k) { return k->get_id(); } }; typedef array_map expr_map; typedef std::pair expr_pair; typedef obj_hashtable expr_set; typedef obj_map back_idx_map; typedef obj_hashtable quantifier_set; typedef obj_map fwd_idx_map; typedef obj_map demodulator2lhs_rhs; typedef expr_map rewrite_cache_map; /** \brief Custom matcher & substitution application */ class match_subst { typedef std::pair expr_pair; typedef obj_pair_hashtable cache; void reset(); ast_manager & m_manager; substitution m_subst; cache m_cache; svector m_todo; bool m_all_args_eq; bool match_args(app * t, expr * const * args); public: match_subst(ast_manager & m); void reserve(unsigned max_vid) { m_subst.reserve(2, max_vid+1); } /** \brief Let f be the top symbol of lhs. If (f args) is an instance of lhs, that is, there is a substitution s s.t. s[lhs] = (f args), then return true and store s[rhs] into new_rhs. Where s[t] represents the application of the substitution s into t. Assumptions, the variables in lhs and (f args) are assumed to be distinct. So, (f x y) matches (f y x). Moreover, the result should be in terms of the variables in (f args). */ bool operator()(app * lhs, expr * rhs, expr * const * args, expr_ref & new_rhs); /** \brief Return true if \c i is an instance of \c t. */ bool operator()(expr * t, expr * i); }; ast_manager & m_manager; match_subst m_match_subst; bool_rewriter m_bsimp; fwd_idx_map m_fwd_idx; back_idx_map m_back_idx; demodulator2lhs_rhs m_demodulator2lhs_rhs; expr_ref_buffer m_todo; obj_hashtable m_processed; ptr_vector m_new_args; expr_ref_buffer m_rewrite_todo; rewrite_cache_map m_rewrite_cache; expr_ref_buffer m_new_exprs; void insert_fwd_idx(expr * large, expr * small, quantifier * demodulator); void remove_fwd_idx(func_decl * f, quantifier * demodulator); bool check_fwd_idx_consistency(); void show_fwd_idx(std::ostream & out); bool is_demodulator(expr * e, expr_ref & large, expr_ref & small) const; bool can_rewrite(expr * n, expr * lhs); expr * rewrite(expr * n); bool rewrite1(func_decl * f, ptr_vector & m_new_args, expr_ref & np); bool rewrite_visit_children(app * a); void rewrite_cache(expr * e, expr * new_e, bool done); void reschedule_processed(func_decl * f); void reschedule_demodulators(func_decl * f, expr * np); unsigned max_var_id(expr * e); protected: // is_smaller returns -1 for e1e2. virtual int is_smaller(expr * e1, expr * e2) const; // is_subset returns -1 for e1 subset e2, +1 for e2 subset e1, 0 else. virtual int is_subset(expr * e1, expr * e2) const; public: ufbv_rewriter(ast_manager & m); virtual ~ufbv_rewriter(); void operator()(unsigned n, expr * const * exprs, proof * const * prs, expr_ref_vector & new_exprs, proof_ref_vector & new_prs); /** Given a demodulator (aka rewrite rule) of the form Forall X. L[X] = R[X] We say the top symbol is the first symbol in L. For example: f is the top symbol in Forall x, f(h(x)) = x + 1 The rewrite engine main loop is based on the DISCOUNT loop used in first-order theorem provers. Main structures: - m_todo: The todo-stack of formulas to be processed. - m_fwd_idx: "Forward index" for finding efficiently which demodulators can be used to rewrite an expression. We organize this set as a mapping from func_decl to a set of demodulators which start with the same top symbol. - m_processed: The set of already processed formulas. We can represent it using a hashtable. - m_back_idx: "Backward index" we use it to find efficiently which already processed expressions and demodulators may be rewritten by a new demodulator. Again, we use a very simple index, for each uninterpreted function symbol (ignore constants) f, store the expressions in m_processed and the demodulators that contain f. If you prefer, you may use two m_back_idxs (one for formulas in m_processed and another for demodulators in m_fwd_idx). Initially, m_todo contains all formulas. That is, it contains the argument exprs. m_fwd_idx, m_processed, m_back_idx are empty. while (m_todo is not empty) { let n be the next formula in m_todo. rewrite n using m_fwd_idx, and let n' be the result. // at this point, it should be the case that there is no demodulator in m_fwd_idx that can rewrite n'. if (n' is not a demodulator) { insert n' into m_processed update m_back_idx (traverse n' and for each uninterpreted function declaration f in n' add the entry f->n' to m_back_idx) } else { let f be the top symbol of n' use m_back_idx to find all formulas p in m_processed that contains f { remove p from m_processed remove p from m_back_idx insert p into m_todo } use m_back_idx to find all demodulators d in m_fwd_idx that contains f { if n' can rewrite d { // this is a quick check, we just traverse d and check if there is an expression in d that is an instance of lhs of n'. // we cannot use the trick used for m_processed, since the main loop would not terminate. remove d from m_fwd_idx remode d from m_back_idx insert p into m_todo } } insert n' into m_fwd_idx update m_back_idx } } the result is the contents of m_processed + all demodulators in m_fwd_idx. Note: to remove p from m_back_idx, we need to traverse p, and for every function declartion f in p, we should remove the entry f->p from m_back_idx. Note: we can implement m_back_idx for formulas as: typedef obj_hashtable expr_set; obj_map m_back_idx; we should represent the sets as hashtables because we want to be able to efficiently remove elements from these sets. ptr_vector m_expr_set_to_delete; // same trick we used in macro_manager. we can use a similar structure for m_back_idx and m_fwd_idx for demodulators. Note: m_processed should be obj_hashtable since we want to remove elements from there efficiently. */ }; #endif /* UFBV_REWRITER_H_ */ z3-z3-4.8.7/src/tactic/ufbv/ufbv_rewriter_tactic.cpp000066400000000000000000000042121356505360400223460ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: ufbv_rewriter_tactic.cpp Abstract: UFBV Rewriter (demodulator) Author: Christoph (cwinter) 2012-10-26 Notes: --*/ #include "tactic/tactical.h" #include "tactic/ufbv/ufbv_rewriter.h" #include "tactic/ufbv/ufbv_rewriter_tactic.h" class ufbv_rewriter_tactic : public tactic { ast_manager & m_manager; params_ref m_params; public: ufbv_rewriter_tactic(ast_manager & m, params_ref const & p): m_manager(m), m_params(p) {} tactic * translate(ast_manager & m) override { return alloc(ufbv_rewriter_tactic, m, m_params); } void updt_params(params_ref const & p) override { m_params = p; } void collect_param_descrs(param_descrs & r) override { insert_max_memory(r); insert_produce_models(r); insert_produce_proofs(r); } void operator()(goal_ref const & g, goal_ref_buffer & result) override { SASSERT(g->is_well_sorted()); tactic_report report("ufbv-rewriter", *g); fail_if_unsat_core_generation("ufbv-rewriter", g); bool produce_proofs = g->proofs_enabled(); ufbv_rewriter dem(m_manager); expr_ref_vector forms(m_manager), new_forms(m_manager); proof_ref_vector proofs(m_manager), new_proofs(m_manager); unsigned size = g->size(); for (unsigned i = 0; i < size; i++) { forms.push_back(g->form(i)); proofs.push_back(g->pr(i)); } dem(forms.size(), forms.c_ptr(), proofs.c_ptr(), new_forms, new_proofs); g->reset(); for (unsigned i = 0; i < new_forms.size(); i++) g->assert_expr(new_forms.get(i), produce_proofs ? new_proofs.get(i) : nullptr, nullptr); // CMW: Remark: The demodulator could potentially // remove all references to a variable. g->inc_depth(); result.push_back(g.get()); TRACE("ufbv-rewriter", g->display(tout);); SASSERT(g->is_well_sorted()); } void cleanup() override {} }; tactic * mk_ufbv_rewriter_tactic(ast_manager & m, params_ref const & p) { return alloc(ufbv_rewriter_tactic, m, p); } z3-z3-4.8.7/src/tactic/ufbv/ufbv_rewriter_tactic.h000066400000000000000000000010421356505360400220110ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: ufbv_rewriter_tactic.cpp Abstract: UFBV Rewriter (demodulator) Author: Christoph (cwinter) 2012-10-26 Notes: --*/ #ifndef UFBV_REWRITER_TACTIC_H_ #define UFBV_REWRITER_TACTIC_H_ #include "util/params.h" class ast_manager; class tactic; tactic * mk_ufbv_rewriter_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("ufbv-rewriter", "Applies UFBV-specific rewriting rules, mainly demodulation.", "mk_quasi_macros_tactic(m, p)") */ #endif z3-z3-4.8.7/src/tactic/ufbv/ufbv_tactic.cpp000066400000000000000000000051361356505360400204310ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: ufbv_tactic.cpp Abstract: General purpose tactic for UFBV benchmarks. Author: Christoph (cwinter) 2012-10-24 Notes: --*/ #include "tactic/tactical.h" #include "tactic/core/simplify_tactic.h" #include "tactic/core/propagate_values_tactic.h" #include "tactic/core/solve_eqs_tactic.h" #include "tactic/core/distribute_forall_tactic.h" #include "tactic/core/der_tactic.h" #include "tactic/core/reduce_args_tactic.h" #include "smt/tactic/smt_tactic.h" #include "tactic/core/nnf_tactic.h" #include "tactic/ufbv/macro_finder_tactic.h" #include "tactic/ufbv/ufbv_rewriter_tactic.h" #include "tactic/ufbv/quasi_macros_tactic.h" #include "tactic/ufbv/ufbv_tactic.h" static tactic * mk_der_fp_tactic(ast_manager & m, params_ref const & p) { return repeat(and_then(mk_der_tactic(m), mk_simplify_tactic(m, p))); } static tactic * mk_ufbv_preprocessor_tactic(ast_manager & m, params_ref const & p) { params_ref no_elim_and(p); no_elim_and.set_bool("elim_and", false); return and_then( mk_trace_tactic("ufbv_pre"), and_then(mk_simplify_tactic(m, p), mk_propagate_values_tactic(m, p), and_then(if_no_proofs(if_no_unsat_cores(using_params(mk_macro_finder_tactic(m, no_elim_and), no_elim_and))), mk_simplify_tactic(m, p)), and_then(mk_snf_tactic(m, p), mk_simplify_tactic(m, p)), mk_elim_and_tactic(m, p), mk_solve_eqs_tactic(m, p), and_then(mk_der_fp_tactic(m, p), mk_simplify_tactic(m, p)), and_then(mk_distribute_forall_tactic(m, p), mk_simplify_tactic(m, p))), if_no_unsat_cores( and_then(and_then(mk_reduce_args_tactic(m, p), mk_simplify_tactic(m, p)), and_then(mk_macro_finder_tactic(m, p), mk_simplify_tactic(m, p)), and_then(mk_ufbv_rewriter_tactic(m, p), mk_simplify_tactic(m, p)), and_then(mk_quasi_macros_tactic(m, p), mk_simplify_tactic(m, p)))), and_then(mk_der_fp_tactic(m, p), mk_simplify_tactic(m, p)), mk_simplify_tactic(m, p), mk_trace_tactic("ufbv_post")); } tactic * mk_ufbv_tactic(ast_manager & m, params_ref const & p) { params_ref main_p(p); main_p.set_bool("mbqi", true); main_p.set_uint("mbqi.max_iterations", UINT_MAX); main_p.set_bool("elim_and", true); tactic * t = and_then(repeat(mk_ufbv_preprocessor_tactic(m, main_p), 2), mk_smt_tactic_using(m, false, main_p)); t->updt_params(p); return t; } z3-z3-4.8.7/src/tactic/ufbv/ufbv_tactic.h000066400000000000000000000011521356505360400200700ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: ufbv_tactic.h Abstract: General purpose tactic for UFBV benchmarks. Author: Christoph (cwinter) 2012-10-24 Notes: --*/ #ifndef UFBV_TACTIC_H_ #define UFBV_TACTIC_H_ #include "util/params.h" class ast_manager; class tactic; tactic * mk_ufbv_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("bv", "builtin strategy for solving BV problems (with quantifiers).", "mk_ufbv_tactic(m, p)") ADD_TACTIC("ufbv", "builtin strategy for solving UFBV problems (with quantifiers).", "mk_ufbv_tactic(m, p)") */ #endif z3-z3-4.8.7/src/test/000077500000000000000000000000001356505360400141755ustar00rootroot00000000000000z3-z3-4.8.7/src/test/CMakeLists.txt000066400000000000000000000060461356505360400167430ustar00rootroot00000000000000add_subdirectory(fuzzing) add_subdirectory(lp) ################################################################################ # z3-test executable ################################################################################ set(z3_test_deps api fuzzing simplex) z3_expand_dependencies(z3_test_expanded_deps ${z3_test_deps}) set (z3_test_extra_object_files "") foreach (component ${z3_test_expanded_deps}) list(APPEND z3_test_extra_object_files $) endforeach() add_executable(test-z3 EXCLUDE_FROM_ALL algebraic.cpp api_bug.cpp api.cpp arith_rewriter.cpp arith_simplifier_plugin.cpp ast.cpp bdd.cpp bit_blaster.cpp bits.cpp bit_vector.cpp buffer.cpp chashtable.cpp check_assumptions.cpp cnf_backbones.cpp cube_clause.cpp datalog_parser.cpp ddnf.cpp diff_logic.cpp dl_context.cpp dl_product_relation.cpp dl_query.cpp dl_relation.cpp dl_table.cpp dl_util.cpp doc.cpp escaped.cpp ex.cpp expr_rand.cpp expr_substitution.cpp ext_numeral.cpp f2n.cpp factor_rewriter.cpp fixed_bit_vector.cpp for_each_file.cpp get_consequences.cpp get_implied_equalities.cpp "${CMAKE_CURRENT_BINARY_DIR}/gparams_register_modules.cpp" hashtable.cpp heap.cpp heap_trie.cpp hilbert_basis.cpp horn_subsume_model_converter.cpp hwf.cpp inf_rational.cpp "${CMAKE_CURRENT_BINARY_DIR}/install_tactic.cpp" interval.cpp karr.cpp list.cpp main.cpp map.cpp matcher.cpp "${CMAKE_CURRENT_BINARY_DIR}/mem_initializer.cpp" memory.cpp model2expr.cpp model_based_opt.cpp model_evaluator.cpp model_retrieval.cpp mpbq.cpp mpf.cpp mpff.cpp mpfx.cpp mpq.cpp mpz.cpp nlarith_util.cpp nlsat.cpp no_overflow.cpp object_allocator.cpp old_interval.cpp optional.cpp parray.cpp pb2bv.cpp permutation.cpp polynomial.cpp polynorm.cpp prime_generator.cpp proof_checker.cpp qe_arith.cpp quant_elim.cpp quant_solve.cpp random.cpp rational.cpp rcf.cpp region.cpp sat_local_search.cpp sat_lookahead.cpp sat_user_scope.cpp simple_parser.cpp simplex.cpp simplifier.cpp small_object_allocator.cpp smt2print_parse.cpp smt_context.cpp solver_pool.cpp sorting_network.cpp stack.cpp string_buffer.cpp substitution.cpp symbol.cpp symbol_table.cpp tbv.cpp theory_dl.cpp theory_pb.cpp timeout.cpp total_order.cpp trigo.cpp udoc_relation.cpp uint_set.cpp upolynomial.cpp var_subst.cpp vector.cpp lp/lp.cpp ${z3_test_extra_object_files} ) z3_add_install_tactic_rule(${z3_test_deps}) z3_add_memory_initializer_rule(${z3_test_deps}) z3_add_gparams_register_modules_rule(${z3_test_deps}) target_compile_definitions(test-z3 PRIVATE ${Z3_COMPONENT_CXX_DEFINES}) target_compile_options(test-z3 PRIVATE ${Z3_COMPONENT_CXX_FLAGS}) target_link_libraries(test-z3 PRIVATE ${Z3_DEPENDENT_LIBS}) target_include_directories(test-z3 PRIVATE ${Z3_COMPONENT_EXTRA_INCLUDE_DIRS}) z3_append_linker_flag_list_to_target(test-z3 ${Z3_DEPENDENT_EXTRA_CXX_LINK_FLAGS}) z3_add_component_dependencies_to_target(test-z3 ${z3_test_expanded_deps}) z3-z3-4.8.7/src/test/algebraic.cpp000066400000000000000000000444461356505360400166260ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: algebraic.cpp Abstract: Test Algebraic Numbers Author: Leonardo (leonardo) 2011-11-22 Notes: --*/ #include "math/polynomial/algebraic_numbers.h" #include "math/polynomial/polynomial_var2value.h" #include "util/mpbq.h" #include "util/rlimit.h" static void display_anums(std::ostream & out, scoped_anum_vector const & rs) { out << "numbers in decimal:\n"; algebraic_numbers::manager & m = rs.m(); for (unsigned i = 0; i < rs.size(); i++) { m.display_decimal(out, rs[i], 10); out << "\n"; } out << "numbers as root objects\n"; for (unsigned i = 0; i < rs.size(); i++) { m.display_root(out, rs[i]); out << "\n"; } out << "numbers as intervals\n"; for (unsigned i = 0; i < rs.size(); i++) { m.display_interval(out, rs[i]); out << "\n"; } } static void tst1() { reslimit rl; unsynch_mpq_manager nm; polynomial::manager m(rl, nm); polynomial_ref x(m); x = m.mk_polynomial(m.mk_var()); polynomial_ref p(m); p = 3*x - 2; algebraic_numbers::manager am(rl, nm); scoped_anum_vector rs1(am); std::cout << "p: " << p << "\n"; am.isolate_roots(p, rs1); display_anums(std::cout, rs1); ENSURE(rs1.size() == 1); std::cout.flush(); p = (x^2) - 2; std::cout << "p: " << p << "\n"; rs1.reset(); am.isolate_roots(p, rs1); display_anums(std::cout, rs1); ENSURE(rs1.size() == 2); scoped_anum sqrt2(am); am.set(sqrt2, rs1[1]); scoped_mpq q(nm); nm.set(q, 1, 3); scoped_anum aq(am); am.set(aq, q); // create algebraic number representing 1/3 am.add(sqrt2, aq, aq); std::cout << "sqrt(2) + 1/3: "; am.display_decimal(std::cout, aq, 10); std::cout << " "; am.display_interval(std::cout, aq); std::cout << " "; am.display_root(std::cout, aq); std::cout << "\n"; am.set(aq, q); am.add(rs1[0], aq, aq); std::cout << "-sqrt(2) + 1/3: "; am.display_decimal(std::cout, aq, 10); std::cout << " "; am.display_interval(std::cout, aq); std::cout << " "; am.display_root(std::cout, aq); std::cout << "\n"; p = ((x^5) - x - 1)*(x-1)*(x-2); std::cout << "p: " << p << "\n"; rs1.reset(); am.isolate_roots(p, rs1); display_anums(std::cout, rs1); ENSURE(rs1.size() == 3); scoped_anum gauss(am); am.set(gauss, rs1[1]); std::cout << "compare(" << sqrt2 << ", " << gauss << "): " << am.compare(sqrt2, gauss) << "\n"; statistics st; am.collect_statistics(st); st.display_smt2(std::cout); p = ((x^2) - 2)*((x^2) - 3); std::cout << "p: " << p << "\n"; rs1.reset(); am.isolate_roots(p, rs1); display_anums(std::cout, rs1); ENSURE(rs1.size() == 4); scoped_anum hidden_sqrt2(am); am.set(hidden_sqrt2, rs1[2]); std::cout << "compare(" << sqrt2 << ", " << hidden_sqrt2 << "): " << am.compare(sqrt2, hidden_sqrt2) << "\n"; st.reset(); am.collect_statistics(st); st.display_smt2(std::cout); std::cout << "sqrt(2)^4: " << (sqrt2^4) << "\n"; ENSURE(is_int(power(sqrt2, 4))); ENSURE(power(sqrt2, 4) == 4); scoped_anum sqrt2_gauss(am); am.add(sqrt2, gauss, sqrt2_gauss); std::cout << "sqrt2 + gauss: " << sqrt2_gauss << " "; am.display_root(std::cout, sqrt2_gauss); std::cout << "\n"; std::cout << "sqrt2*sqrt2: " << sqrt2*sqrt2 << "\n"; std::cout << "sqrt2*sqrt2 == 2: " << (sqrt2*sqrt2 == 2) << std::endl; scoped_anum three(am); am.set(three, -3); std::cout << "(-3)^(1/5): " << root(three, 5) << "\n"; std::cout << "sqrt(2)^(1/3): " << root(sqrt2, 3) << "\n"; std::cout << "as-root-object(sqrt(2)^(1/3)): " << root_obj_pp(root(sqrt2, 3)) << "\n"; std::cout << "(sqrt(2) + 1)^(1/3): " << root(sqrt2 + 1, 3) << "\n"; std::cout << "as-root-object((sqrt(2) + 1)^(1/3)): " << root_obj_pp(root(sqrt2 + 1, 3)) << "\n"; std::cout << "(sqrt(2) + gauss)^(1/5): " << root(sqrt2 + gauss, 5) << "\n"; std::cout << "as-root-object(sqrt(2) + gauss)^(1/5): " << root_obj_pp(root(sqrt2 + gauss, 5)) << "\n"; std::cout << "(sqrt(2) / sqrt(2)): " << sqrt2 / hidden_sqrt2 << "\n"; std::cout << "(sqrt(2) / gauss): " << sqrt2 / gauss << "\n"; std::cout << "(sqrt(2) / gauss) 30 digits: " << decimal_pp(sqrt2 / gauss, 30) << "\n"; std::cout << "as-root-object(sqrt(2) / gauss): " << root_obj_pp(sqrt2 / gauss) << "\n"; std::cout << "is_int(sqrt(2)^(1/3)): " << am.is_int(root(sqrt2, 3)) << "\n"; scoped_anum tmp(am); scoped_anum four(am); am.set(four, 4); am.set(tmp, sqrt2); am.inv(tmp); std::cout << "1/sqrt(2): " << tmp << "\n"; am.mul(tmp, four, tmp); std::cout << "4*1/sqrt(2): " << tmp << " " << root_obj_pp(tmp) << "\n"; am.mul(tmp, sqrt2, tmp); std::cout << "sqrt(2)*4*(1/sqrt2): " << tmp << " " << root_obj_pp(tmp) << "\n"; std::cout << "is_int(sqrt(2)*4*(1/sqrt2)): " << am.is_int(tmp) << ", after is-int: " << tmp << "\n"; p = (998*x - 1414)*((x^2) - 15); std::cout << "p: " << p << "\n"; rs1.reset(); am.isolate_roots(p, rs1); std::cout << "is-rational(sqrt2): " << am.is_rational(sqrt2) << "\n"; scoped_anum qr(am); am.set(qr, rs1[1]); std::cout << "qr: " << root_obj_pp(qr); std::cout << ", is-rational: " << am.is_rational(qr) << ", val: " << root_obj_pp(qr) << "\n"; return; std::cout << "compare(" << sqrt2 << ", " << gauss << "): " << am.compare(sqrt2, gauss) << "\n"; p = (x^16) - 136*(x^14) + 6476*(x^12) - 141912*(x^10) + 1513334*(x^8) - 7453176*(x^6) + 13950764*(x^4) - 5596840*(x^2) + 46225; std::cout << "p: " << p << "\n"; rs1.reset(); am.isolate_roots(p, rs1); display_anums(std::cout, rs1); } void tst_refine_mpbq(int n, int d) { unsynch_mpq_manager qm; mpbq_manager bqm(qm); scoped_mpq q1(qm); qm.set(q1, n, d); scoped_mpbq l(bqm); scoped_mpbq u(bqm); std::cout << "using refine upper...\n"; bqm.to_mpbq(q1, l); bqm.set(u, l); bqm.mul2(u); for (unsigned i = 0; i < 20; i++) { std::cout << l << " < " << q1 << " < " << u << "\n"; bqm.display_decimal(std::cout, l, 20); std::cout << " < "; qm.display_decimal(std::cout, q1, 20); std::cout << " < "; bqm.display_decimal(std::cout, u, 20); std::cout << std::endl; bqm.refine_upper(q1, l, u); } std::cout << "using refine lower...\n"; bqm.to_mpbq(q1, l); bqm.set(u, l); bqm.mul2(u); for (unsigned i = 0; i < 20; i++) { std::cout << l << " < " << q1 << " < " << u << "\n"; bqm.display_decimal(std::cout, l, 20); std::cout << " < "; qm.display_decimal(std::cout, q1, 20); std::cout << " < "; bqm.display_decimal(std::cout, u, 20); std::cout << std::endl; bqm.refine_lower(q1, l, u); } } void tst_refine_mpbq() { tst_refine_mpbq(5, 7); } void tst_mpbq_root() { unsynch_mpq_manager qm; mpbq_manager bqm(qm); // scoped_mpbq q(bqm); // q.set(q1, 1.4142135 , 7); } static void tst_wilkinson() { // Test Wilkinson Polynomial reslimit rl; unsynch_mpq_manager nm; polynomial::manager m(rl, nm); polynomial_ref x(m); x = m.mk_polynomial(m.mk_var()); polynomial_ref p(m); for (int i = 1; i <= 20; i++) { if (i > 1) p = p*(x - i); else p = (x - i); } std::cout << "Wilkinson's polynomial: " << p << "\n"; algebraic_numbers::manager am(rl, nm); scoped_anum_vector rs1(am); std::cout << "p: " << p << "\n"; am.isolate_roots(p, rs1); display_anums(std::cout, rs1); ENSURE(rs1.size() == 20); for (unsigned i = 0; i < rs1.size(); i++) { ENSURE(am.is_int(rs1[i])); } } static void tst_dejan() { reslimit rl; unsynch_mpq_manager qm; algebraic_numbers::manager am(rl, qm); scoped_anum two101(am); am.set(two101, 2); am.root(two101, 11, two101); scoped_anum two103(am); am.set(two103, 2); am.root(two103, 7, two103); std::cout << "two101: " << two101 << " " << root_obj_pp(two101) << std::endl; std::cout << "two103: " << two103 << " " << root_obj_pp(two103) << std::endl; scoped_anum sum1(am); am.add(two103, two101, sum1); std::cout << "sum1: " << sum1 << " " << root_obj_pp(sum1) << "\n"; } static void tst_select_small(mpbq_manager & m, scoped_mpbq const & l, scoped_mpbq const & u, bool expected) { scoped_mpbq r(m); std::cout << "----------\n"; std::cout << "lower: " << l << " as decimal: "; m.display_decimal(std::cout, l); std::cout << std::endl; std::cout << "upper: " << u << " as decimal: "; m.display_decimal(std::cout, u); std::cout << std::endl; VERIFY(m.select_small(l, u, r) == expected); std::cout << "choice: " << r << " as decimal: "; m.display_decimal(std::cout, r); std::cout << std::endl; } static void tst_select_small(mpbq_manager & m, int64_t n1, unsigned k1, int64_t n2, unsigned k2, bool expected) { scoped_mpbq l(m); scoped_mpbq u(m); m.set(l, n1, k1); m.set(u, n2, k2); tst_select_small(m, l, u, expected); } static void tst_select_small() { unsynch_mpz_manager m; mpbq_manager bqm(m); tst_select_small(bqm, 1, 3, 3, 2, true); tst_select_small(bqm, 10000000000000ll, 40, 11000, 10, true); tst_select_small(bqm, 10000000000000ll, 40, 10001, 10, true); tst_select_small(bqm, 1, 0, 1, 0, true); tst_select_small(bqm, 1, 0, 2, 0, true); tst_select_small(bqm, -1, 0, -1, 0, true); tst_select_small(bqm, -2, 0, -1, 0, true); tst_select_small(bqm, 0, 0, 1100, 10, true); tst_select_small(bqm, 7, 3, 1001, 10, true); tst_select_small(bqm, 1000, 10, 1001, 10, true); scoped_mpbq l1(bqm); l1 = 11; bqm.power(l1, 64, l1); scoped_mpbq l2(bqm); l2 = l1 + 1; bqm.div2k(l1, 64*3); bqm.div2k(l2, 64*3); tst_select_small(bqm, l1, l2, true); l1 = 11; bqm.power(l1, 64, l1); l2 = l1 + 256; bqm.div2k(l1, 64*3); bqm.div2k(l2, 64*3); tst_select_small(bqm, l1, l2, true); } static void tst_eval_sign(polynomial_ref const & p, anum_manager & am, polynomial::var x0, anum const & v0, polynomial::var x1, anum const & v1, polynomial::var x2, anum const & v2, int expected) { polynomial::simple_var2value x2v(am); x2v.push_back(x0, v0); x2v.push_back(x1, v1); x2v.push_back(x2, v2); std::cout << "--------------\n"; std::cout << "p: " << p << "\n"; std::cout << "x0 -> "; am.display_root(std::cout, v0); std::cout << "\n"; std::cout << "x1 -> "; am.display_root(std::cout, v1); std::cout << "\n"; std::cout << "x2 -> "; am.display_root(std::cout, v2); std::cout << "\n"; int s = am.eval_sign_at(p, x2v); ENSURE((s == 0) == (expected == 0)); ENSURE((s < 0) == (expected < 0)); ENSURE((s > 0) == (expected > 0)); std::cout << "sign: " << s << "\n"; } static void tst_eval_sign() { enable_trace("anum_eval_sign"); reslimit rl; unsynch_mpq_manager qm; polynomial::manager pm(rl, qm); algebraic_numbers::manager am(rl, qm); polynomial_ref x0(pm); polynomial_ref x1(pm); polynomial_ref x2(pm); x0 = pm.mk_polynomial(pm.mk_var()); x1 = pm.mk_polynomial(pm.mk_var()); x2 = pm.mk_polynomial(pm.mk_var()); polynomial_ref p(pm); p = x0*x1 + (x1^2) + x2 + 2; scoped_anum v0(am), v1(am), v2(am); tst_eval_sign(p, am, 0, v0, 1, v1, 2, v2, 1); am.set(v2, -2); tst_eval_sign(p, am, 0, v0, 1, v1, 2, v2, 0); am.set(v1, 1); am.set(v0, -3); tst_eval_sign(p, am, 0, v0, 1, v1, 2, v2, -1); am.set(v0, 2); am.root(v0, 2, v0); am.set(v1, 0); tst_eval_sign(p, am, 0, v0, 1, v1, 2, v2, 0); am.set(v2, 1); tst_eval_sign(p, am, 0, v0, 1, v1, 2, v2, 1); am.set(v2, -3); tst_eval_sign(p, am, 0, v0, 1, v1, 2, v2, -1); am.set(v1, 1); tst_eval_sign(p, am, 0, v0, 1, v1, 2, v2, 1); am.set(v2, -4); tst_eval_sign(p, am, 0, v0, 1, v1, 2, v2, 1); am.set(v2, -5); tst_eval_sign(p, am, 0, v0, 1, v1, 2, v2, -1); am.set(v2, -2); am.set(v1, v0); am.neg(v1); tst_eval_sign(p, am, 0, v0, 1, v1, 2, v2, 0); am.set(v2, -3); tst_eval_sign(p, am, 0, v0, 1, v1, 2, v2, -1); p = x0*x1 + (x1^2) - x2 + 2; tst_eval_sign(p, am, 0, v0, 1, v1, 2, v2, 1); } static void tst_isolate_roots(polynomial_ref const & p, anum_manager & am, polynomial::var x0, anum const & v0, polynomial::var x1, anum const & v1, polynomial::var x2, anum const & v2) { polynomial::simple_var2value x2v(am); x2v.push_back(x0, v0); x2v.push_back(x1, v1); x2v.push_back(x2, v2); std::cout << "--------------\n"; std::cout << "p: " << p << "\n"; std::cout << "x0 -> "; am.display_root(std::cout, v0); std::cout << "\n"; std::cout << "x1 -> "; am.display_root(std::cout, v1); std::cout << "\n"; std::cout << "x2 -> "; am.display_root(std::cout, v2); std::cout << "\n"; scoped_anum_vector roots(am); svector signs; am.isolate_roots(p, x2v, roots, signs); ENSURE(roots.size() + 1 == signs.size()); std::cout << "roots:\n"; for (unsigned i = 0; i < roots.size(); i++) { am.display_root(std::cout, roots[i]); std::cout << " "; am.display_decimal(std::cout, roots[i]); std::cout << "\n"; } std::cout << "signs:\n"; for (unsigned i = 0; i < signs.size(); i++) { if (i > 0) std::cout << " 0 "; if (signs[i] < 0) std::cout << "-"; else if (signs[i] == 0) std::cout << "0"; else std::cout << "+"; } std::cout << "\n"; } static void tst_isolate_roots() { enable_trace("isolate_roots"); reslimit rl; unsynch_mpq_manager qm; polynomial::manager pm(rl, qm); algebraic_numbers::manager am(rl, qm); polynomial_ref x0(pm); polynomial_ref x1(pm); polynomial_ref x2(pm); polynomial_ref x3(pm); x0 = pm.mk_polynomial(pm.mk_var()); x1 = pm.mk_polynomial(pm.mk_var()); x2 = pm.mk_polynomial(pm.mk_var()); x3 = pm.mk_polynomial(pm.mk_var()); polynomial_ref p(pm); p = x3*x1 + 1; scoped_anum v0(am), v1(am), v2(am); tst_isolate_roots(p, am, 0, v0, 1, v1, 2, v2); am.set(v1, 1); tst_isolate_roots(p, am, 0, v0, 1, v1, 2, v2); am.set(v1, 2); am.root(v1, 2, v1); tst_isolate_roots(p, am, 0, v0, 1, v1, 2, v2); p = (x1 + x2)*x3 + 1; am.set(v2, v1); tst_isolate_roots(p, am, 0, v0, 1, v1, 2, v2); p = (x1 + x2)*x3 + x1*x2 + 2; tst_isolate_roots(p, am, 0, v0, 1, v1, 2, v2); p = (x1 + x2)*(x3^3) + x1*x2 + 2; tst_isolate_roots(p, am, 0, v0, 1, v1, 2, v2); p = (x1 + x2)*(x3^2) - x1*x2 - 2; tst_isolate_roots(p, am, 0, v0, 1, v1, 2, v2); p = x0*(x1 + x2)*(x3^2) - x0*x1*x2 - 2; tst_isolate_roots(p, am, 0, v0, 1, v1, 2, v2); p = (x1 - x2)*x3 + x1*x2 - 2; tst_isolate_roots(p, am, 0, v0, 1, v1, 2, v2); p = (x1 - x2)*(x3^3) + x1*x2 - 2; tst_isolate_roots(p, am, 0, v0, 1, v1, 2, v2); p = (x3 - x0)*(x3 - x0 - x1); am.set(v0, 2); am.root(v0, 2, v0); // x2 -> sqrt(2) am.set(v1, 3); am.root(v1, 2, v1); // x1 -> sqrt(3) am.reset(v2); tst_isolate_roots(p, am, 0, v0, 1, v1, 2, v2); p = (x3 - x0)*((x3 - x0 - x1)^2); tst_isolate_roots(p, am, 0, v0, 1, v1, 2, v2); p = (x3 - x0)*(x3 - 2)*((x3 - 1)^2)*(x3 - x1); tst_isolate_roots(p, am, 0, v0, 1, v1, 2, v2); } static void pp(polynomial_ref const & p, polynomial::var x) { unsigned d = degree(p, x); for (unsigned i = 0; i <= d; i++) { std::cout << "(" << coeff(p, x, i) << ") "; } std::cout << "\n"; } static void ex1() { unsynch_mpq_manager qm; reslimit rl; polynomial::manager pm(rl, qm); polynomial_ref x(pm); polynomial_ref a(pm); polynomial_ref b(pm); polynomial_ref c(pm); x = pm.mk_polynomial(pm.mk_var()); a = pm.mk_polynomial(pm.mk_var()); b = pm.mk_polynomial(pm.mk_var()); c = pm.mk_polynomial(pm.mk_var()); polynomial_ref p(pm); p = (a + 2*b)*(x^3) + x*a + (b^2); polynomial_ref p1(pm); p1 = derivative(p, 0); polynomial_ref h2(pm); unsigned d; h2 = pseudo_remainder(p, p1, 0, d); std::cout << "d: " << d << "\n"; std::cout << "p: "; pp(p, 0); std::cout << "\np': "; pp(p1, 0); std::cout << "\nh2: "; pp(h2, 0); std::cout << "\n"; polynomial_ref h3(pm); h3 = pseudo_remainder(p1, h2, 0, d); std::cout << "d: " << d << "\n"; std::cout << "h3: "; pp(h3, 0); std::cout << "\n"; algebraic_numbers::manager am(rl, qm); scoped_anum v1(am), v2(am); am.set(v1, 2); am.root(v1, 3, v1); am.set(v2, 3); am.root(v2, 3, v2); polynomial::simple_var2value x2v(am); x2v.push_back(1, v1); x2v.push_back(2, v2); std::cout << "sign(h3(v1,v2)): " << am.eval_sign_at(h3, x2v) << "\n"; scoped_anum v0(am); am.set(v0, -1); x2v.push_back(0, v0); std::cout << "sign(h2(v1,v2)): " << am.eval_sign_at(h2, x2v) << "\n"; std::cout << "sign(p'(v1,v2)): " << am.eval_sign_at(p1, x2v) << "\n"; std::cout << "sign(p(v1,v2)): " << am.eval_sign_at(p, x2v) << "\n"; polynomial::simple_var2value x2v2(am); x2v2.push_back(1, v1); x2v2.push_back(2, v2); scoped_mpq tmp(qm); qm.set(tmp, -1); qm.div(tmp, mpz(2), tmp); std::cout << "tmp: "; qm.display(std::cout, tmp); std::cout << " "; qm.display_decimal(std::cout, tmp, 10); std::cout << "\n"; am.set(v0, tmp); x2v2.push_back(0, v0); std::cout << "v0: " << v0 << "\n"; std::cout << "sign(h2(v1,v2)): " << am.eval_sign_at(h2, x2v2) << "\n"; std::cout << "sign(p'(v1,v2)): " << am.eval_sign_at(p1, x2v2) << "\n"; std::cout << "sign(p(v1,v2)): " << am.eval_sign_at(p, x2v2) << "\n"; } static void tst_root() { reslimit rl; unsynch_mpq_manager qm; algebraic_numbers::manager am(rl, qm); scoped_anum v1(am), v2(am); am.set(v1, 4); am.root(v1, 2, v2); std::cout << "root: " << v2 << "\n"; am.set(v1, 4); am.root(v1, 4, v2); std::cout << "root: " << root_obj_pp(v2) << "\n"; } void tst_algebraic() { // enable_trace("resultant_bug"); // enable_trace("poly_sign"); disable_trace("algebraic"); // enable_trace("mpbq_bug"); // enable_trace("mpz_mul2k"); // enable_trace("mpz_gcd"); tst_root(); tst_isolate_roots(); ex1(); tst_eval_sign(); tst_select_small(); tst_dejan(); tst_wilkinson(); tst1(); tst_refine_mpbq(); } z3-z3-4.8.7/src/test/api.cpp000066400000000000000000000066071356505360400154630ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #ifdef _WINDOWS #include "api/z3.h" #include "api/z3_private.h" #include #include "util/util.h" #include "util/trace.h" #include #include "util/trace.h" void test_apps() { Z3_config cfg = Z3_mk_config(); Z3_set_param_value(cfg,"MODEL","true"); Z3_context ctx = Z3_mk_context(cfg); Z3_solver s = Z3_mk_solver(ctx); Z3_solver_inc_ref(ctx, s); Z3_symbol A = Z3_mk_string_symbol(ctx, "A"); Z3_symbol F = Z3_mk_string_symbol(ctx, "f"); Z3_sort SA = Z3_mk_uninterpreted_sort(ctx, A); Z3_func_decl f = Z3_mk_func_decl(ctx, F, 1, &SA, SA); Z3_symbol X = Z3_mk_string_symbol(ctx, "x"); Z3_ast x = Z3_mk_const(ctx, X, SA); Z3_ast fx = Z3_mk_app(ctx, f, 1, &x); Z3_ast ffx = Z3_mk_app(ctx, f, 1, &fx); Z3_ast fffx = Z3_mk_app(ctx, f, 1, &ffx); Z3_ast ffffx = Z3_mk_app(ctx, f, 1, &fffx); Z3_ast fffffx = Z3_mk_app(ctx, f, 1, &ffffx); Z3_ast fml = Z3_mk_not(ctx, Z3_mk_eq(ctx, x, fffffx)); Z3_solver_assert(ctx, s, fml); Z3_lbool r = Z3_solver_check(ctx, s); std::cout << r << "\n"; Z3_solver_dec_ref(ctx, s); Z3_del_config(cfg); Z3_del_context(ctx); } void test_bvneg() { Z3_config cfg = Z3_mk_config(); Z3_set_param_value(cfg,"MODEL","true"); Z3_context ctx = Z3_mk_context(cfg); Z3_solver s = Z3_mk_solver(ctx); Z3_solver_inc_ref(ctx, s); { Z3_sort bv30 = Z3_mk_bv_sort(ctx, 30); Z3_ast x30 = Z3_mk_fresh_const(ctx, "x", bv30); Z3_ast fml = Z3_mk_eq(ctx, Z3_mk_int(ctx, -1, bv30), Z3_mk_bvadd(ctx, Z3_mk_int(ctx, 0, bv30), x30)); Z3_solver_assert(ctx, s, fml); Z3_lbool r = Z3_solver_check(ctx, s); std::cout << r << "\n"; } { Z3_sort bv31 = Z3_mk_bv_sort(ctx, 31); Z3_ast x31 = Z3_mk_fresh_const(ctx, "x", bv31); Z3_ast fml = Z3_mk_eq(ctx, Z3_mk_int(ctx, -1, bv31), Z3_mk_bvadd(ctx, Z3_mk_int(ctx, 0, bv31), x31)); Z3_solver_assert(ctx, s, fml); Z3_lbool r = Z3_solver_check(ctx, s); std::cout << r << "\n"; } { Z3_sort bv32 = Z3_mk_bv_sort(ctx, 32); Z3_ast x32 = Z3_mk_fresh_const(ctx, "x", bv32); Z3_ast fml = Z3_mk_eq(ctx, Z3_mk_int(ctx,-1, bv32), Z3_mk_bvadd(ctx, Z3_mk_int(ctx, 0, bv32), x32)); Z3_solver_assert(ctx, s, fml); Z3_lbool r = Z3_solver_check(ctx, s); std::cout << r << "\n"; } Z3_solver_dec_ref(ctx, s); Z3_del_config(cfg); Z3_del_context(ctx); } static bool cb_called = false; static void my_cb(Z3_context, Z3_error_code) { cb_called = true; } static void test_mk_distinct() { Z3_config cfg = Z3_mk_config(); Z3_context ctx = Z3_mk_context(cfg); Z3_set_error_handler(ctx, my_cb); Z3_sort bv8 = Z3_mk_bv_sort(ctx, 8); Z3_sort bv32 = Z3_mk_bv_sort(ctx, 32); Z3_ast args[] = { Z3_mk_int64(ctx, 0, bv8), Z3_mk_int64(ctx, 0, bv32) }; Z3_ast d = Z3_mk_distinct(ctx, 2, args); ENSURE(cb_called); Z3_del_config(cfg); Z3_del_context(ctx); } void tst_api() { test_apps(); test_bvneg(); test_mk_distinct(); } #else void tst_api() { } #endif z3-z3-4.8.7/src/test/api_bug.cpp000066400000000000000000000025741356505360400163170ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include #include "api/z3.h" void tst_api_bug() { unsigned vmajor, vminor, vbuild, vrevision; Z3_get_version(&vmajor, &vminor, &vbuild, &vrevision); printf("Using Z3 Version %u.%u (build %u, revision %u)\n", vmajor, vminor, vbuild, vrevision); Z3_config cfg = Z3_mk_config(); Z3_set_param_value(cfg, "MODEL", "true"); Z3_context ctx = Z3_mk_context(cfg); Z3_solver s = Z3_mk_solver(ctx); Z3_solver_inc_ref(ctx, s); Z3_sort is = Z3_mk_int_sort(ctx); Z3_sort ss = Z3_mk_set_sort(ctx, is); Z3_ast e = Z3_mk_empty_set(ctx, is); // { 42 } Z3_ast fortytwo = Z3_mk_set_add(ctx, e, Z3_mk_int(ctx, 42, is)); // { 42, 43 } Z3_ast fortythree = Z3_mk_set_add(ctx, fortytwo, Z3_mk_int(ctx, 43, is)); // { 42 } U { 42, 43 } Z3_ast uargs[2] = { fortytwo, fortythree }; Z3_ast u = Z3_mk_set_union(ctx, 2, uargs); Z3_symbol sym = Z3_mk_string_symbol(ctx, "mySet"); Z3_ast sc = Z3_mk_const(ctx, sym, ss); Z3_ast c = Z3_mk_eq(ctx, sc, u); Z3_solver_push(ctx, s); Z3_solver_assert(ctx, s, c); printf("result %d\n", Z3_solver_check(ctx, s)); Z3_model m = Z3_solver_get_model(ctx, s); Z3_string ms = Z3_model_to_string(ctx, m); printf("model : %s\n", ms); Z3_solver_dec_ref(ctx, s); Z3_del_config(cfg); Z3_del_context(ctx); } z3-z3-4.8.7/src/test/arith_rewriter.cpp000066400000000000000000000031261356505360400177350ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "ast/rewriter/arith_rewriter.h" #include "ast/bv_decl_plugin.h" #include "ast/ast_pp.h" #include "ast/reg_decl_plugins.h" #include "ast/rewriter/th_rewriter.h" #include "model/model.h" #include "parsers/smt2/smt2parser.h" static expr_ref parse_fml(ast_manager& m, char const* str) { expr_ref result(m); cmd_context ctx(false, &m); ctx.set_ignore_check(true); std::ostringstream buffer; buffer << "(declare-const x Real)\n" << "(declare-const y Real)\n" << "(declare-const z Real)\n" << "(declare-const a Real)\n" << "(declare-const b Real)\n" << "(assert " << str << ")\n"; std::istringstream is(buffer.str()); VERIFY(parse_smt2_commands(ctx, is)); ENSURE(!ctx.assertions().empty()); result = ctx.assertions().get(0); return result; } static char const* example1 = "(<= (+ (* 1.3 x y) (* 2.3 y y) (* (- 1.1 x x))) 2.2)"; static char const* example2 = "(= (+ 4 3 (- (* 3 x x) (* 5 y)) y) 0)"; void tst_arith_rewriter() { ast_manager m; reg_decl_plugins(m); arith_rewriter ar(m); arith_util au(m); expr_ref t1(m), t2(m), result(m); t1 = au.mk_numeral(rational(0),false); t2 = au.mk_numeral(rational(-3),false); expr* args[2] = { t1, t2 }; ar.mk_mul(2, args, result); std::cout << mk_pp(result, m) << "\n"; th_rewriter rw(m); expr_ref fml = parse_fml(m, example1); rw(fml); std::cout << mk_pp(fml, m) << "\n"; fml = parse_fml(m, example2); rw(fml); std::cout << mk_pp(fml, m) << "\n"; } z3-z3-4.8.7/src/test/arith_simplifier_plugin.cpp000066400000000000000000000027121356505360400216130ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "smt/arith_eq_solver.h" #include "smt/params/smt_params.h" typedef rational numeral; typedef vector row; static void test_solve_integer_equations( arith_eq_solver& asimp, vector& rows ) { row r_unsat; if (asimp.solve_integer_equations(rows, r_unsat)) { std::cout << "solved\n"; } else { std::cout << "not solved\n"; for (unsigned i = 0; i < r_unsat.size(); ++i) { std::cout << " " << r_unsat[i]; } std::cout << "\n"; } } void tst_arith_simplifier_plugin() { smt_params params; ast_manager m; arith_eq_solver asimp(m); row r1; row r2; r1.push_back(numeral(1)); r1.push_back(numeral(2)); r1.push_back(numeral(1)); r1.push_back(numeral(2)); r2.push_back(numeral(1)); r2.push_back(numeral(2)); r2.push_back(numeral(1)); r2.push_back(numeral(2)); vector rows; rows.push_back(r1); rows.push_back(r2); #if 0 test_solve_integer_equations(asimp, rows); rows[1][3] = numeral(3); test_solve_integer_equations(asimp, rows); #endif rows[0][0] = numeral(1); rows[0][1] = numeral(3); rows[0][2] = numeral(0); rows[0][3] = numeral(0); rows[1][0] = numeral(1); rows[1][1] = numeral(0); rows[1][2] = numeral(3); rows[1][3] = numeral(1); test_solve_integer_equations(asimp, rows); } z3-z3-4.8.7/src/test/ast.cpp000066400000000000000000000105561356505360400154770ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: tst_ast.cpp Abstract: Test AST module Author: Leonardo de Moura (leonardo) 2006-09-29. Revision History: --*/ #include "ast/ast.h" static void tst1() { ast_manager m; family_id fid = m.get_basic_family_id(); sort_ref b(m.mk_bool_sort(), m); expr_ref a(m.mk_const(symbol("a"), b.get()), m); expr_ref c(m.mk_const(symbol("c"), b.get()), m); expr_ref i1(m.mk_app(fid, OP_AND, a.get(), c.get()), m); expr_ref i2(m.mk_app(fid, OP_AND, a.get(), c.get()), m); expr_ref i3(m.mk_app(fid, OP_OR, a.get(), c.get()), m); ENSURE(i1.get() == i2.get()); ENSURE(i1.get() != i3.get()); // TODO use smart pointers to track references // ast_manager m; // ast_ref n1(m.mk_numeral(rational(2,3)), m); // ast_ref n2(m.mk_numeral(rational(2,3)), m); // ENSURE(n1 == n2); // ast_ref n3(m.mk_numeral(rational(1,2)), m); // ENSURE(n1 != n3); // ast_ref v1 (m.mk_var(1), m); // ast_ref v2 (m.mk_var(2), m); // ast_ref v3 (m.mk_var(1), m); // ENSURE(v1 != v2); // ENSURE(v1 == v3); // TRACE("ast", tout << "resetting v1\n";); // v1.reset(); // TRACE("ast", tout << "overwriting v3\n";); // v3 = v2; // ast_ref t1(m.mk_type_decl(symbol("int"), 0), m); // ast_ref i(m.mk_type(t1.get(), 0, 0), m); // ast_ref foo_decl(m.mk_const_decl(symbol("foo"), i.get(), i.get()), m); // ast_ref x_decl(m.mk_const_decl(symbol("x"), i.get()), m); // ast_ref x(m.mk_const(x_decl.get()), m); // ast_ref foo_x(m.mk_const(foo_decl.get(), x.get()), m); // ast_ref foo_foo_x(m.mk_const(foo_decl.get(), foo_x.get()), m); // ast_ref foo_foo_x2(m.mk_const(foo_decl.get(), m.mk_const(foo_decl.get(), m.mk_const(x_decl.get()))), m); // ENSURE(foo_foo_x2 == foo_foo_x); } static void tst2() { // ast_manager m; // ast_vector m_nodes(m); // m_nodes.push_back(m.mk_var(1)); // m_nodes.push_back(m.mk_numeral(rational(1,2))); // m_nodes.push_back(m.mk_var(2)); // m_nodes[1] = m.mk_var(3); // ENSURE(m_nodes[1]->kind() == AST_VAR); // ENSURE(m_nodes.get(1)->kind() == AST_VAR); // m_nodes.pop_back(); // ENSURE(m_nodes.size() == 2); // ENSURE(!m_nodes.empty()); // m_nodes.set(1, m.mk_var(4)); // ENSURE(&(m_nodes.get_manager()) == &m); } static void tst3() { // ast_manager m; // ast_ref<> n(m.mk_var(1), m); // n = m.mk_var(1); // TRACE("ast", tout << n->get_id() << "\n";); } static void tst4() { // ast_manager m; // ast_ref<> n1(m.mk_var(1), m); // ast_ref<> n2(m.mk_var(2), m); // ast_ref<> n3(m.mk_var(3), m); // weak_memoize wm1; // #ifdef Z3DEBUG // int r; // #endif // ENSURE(!wm1.find(n1, r)); // wm1.insert(n2, 10); // ENSURE(!wm1.find(n1, r)); // ENSURE(wm1.find(n2, r) && r == 10); // wm1.insert(n2, 20); // ENSURE(!wm1.find(n1, r)); // ENSURE(wm1.find(n2, r) && r == 20); // wm1.insert(n1, 0); // ENSURE(wm1.find(n1, r) && r == 0); // ENSURE(wm1.find(n2, r) && r == 20); } static void tst5() { ast_manager m; sort_ref b(m.mk_bool_sort(), m); expr_ref a1(m.mk_const(symbol("a1"), b.get()), m); expr_ref a2(m.mk_const(symbol("a2"), b.get()), m); expr_array arr1; expr_array arr2; expr_array arr3; m.push_back(arr1, a1); m.push_back(arr1, a2); m.pop_back(arr1, arr2); m.set(arr2, 0, a2, arr3); ENSURE(m.size(arr1) == 2); ENSURE(m.size(arr2) == 1); ENSURE(m.size(arr3) == 1); ENSURE(m.get(arr1, 0) == a1); ENSURE(m.get(arr1, 1) == a2); ENSURE(m.get(arr2, 0) == a1); ENSURE(m.get(arr3, 0) == a2); m.del(arr1); m.del(arr2); m.del(arr3); } struct foo { unsigned m_id; unsigned short m_ref_count; unsigned char m_kind; unsigned char m_arity; bool m_val1:1; bool m_val2:1; }; void tst_ast() { TRACE("ast", tout << "sizeof(ast): " << sizeof(ast) << "\n"; tout << "sizeof(expr): " << sizeof(expr) << "\n"; tout << "sizeof(app): " << sizeof(app) << "\n"; ); TRACE("ast", tout << "sizeof(foo): " << sizeof(foo) << "\n";); tst1(); tst2(); tst3(); tst4(); tst5(); } z3-z3-4.8.7/src/test/bdd.cpp000066400000000000000000000046201356505360400154340ustar00rootroot00000000000000#include "sat/sat_bdd.h" namespace sat { static void test1() { bdd_manager m(20); bdd v0 = m.mk_var(0); bdd v1 = m.mk_var(1); bdd v2 = m.mk_var(2); bdd c1 = v0 && v1 && v2; bdd c2 = v2 && v0 && v1; std::cout << c1 << "\n"; SASSERT(c1 == c2); std::cout << "cnf size: " << c1.cnf_size() << "\n"; c1 = v0 || v1 || v2; c2 = v2 || v1 || v0; std::cout << c1 << "\n"; SASSERT(c1 == c2); std::cout << "cnf size: " << c1.cnf_size() << "\n"; } static void test2() { bdd_manager m(20); bdd v0 = m.mk_var(0); bdd v1 = m.mk_var(1); bdd v2 = m.mk_var(2); SASSERT(m.mk_ite(v0,v0,v1) == (v0 || v1)); SASSERT(m.mk_ite(v0,v1,v1) == v1); SASSERT(m.mk_ite(v1,v0,v1) == (v0 && v1)); SASSERT(m.mk_ite(v1,v0,v0) == v0); SASSERT(m.mk_ite(v0,!v0,v1) == (!v0 && v1)); SASSERT(m.mk_ite(v0,v1,!v0) == (!v0 || v1)); SASSERT((!(v0 && v1)) == (!v0 || !v1)); SASSERT((!(v0 || v1)) == (!v0 && !v1)); } static void test3() { bdd_manager m(20); bdd v0 = m.mk_var(0); bdd v1 = m.mk_var(1); bdd v2 = m.mk_var(2); bdd c1 = (v0 && v1) || v2; bdd c2 = m.mk_exists(0, c1); std::cout << c1 << "\n"; std::cout << c2 << "\n"; SASSERT(c2 == (v1 || v2)); c2 = m.mk_exists(1, c1); SASSERT(c2 == (v0 || v2)); c2 = m.mk_exists(2, c1); SASSERT(c2.is_true()); SASSERT(m.mk_exists(3, c1) == c1); c1 = (v0 && v1) || (v1 && v2) || (!v0 && !v2); c2 = m.mk_exists(0, c1); SASSERT(c2 == (v1 || (v1 && v2) || !v2)); c2 = m.mk_exists(1, c1); SASSERT(c2 == (v0 || v2 || (!v0 && !v2))); c2 = m.mk_exists(2, c1); SASSERT(c2 == ((v0 && v1) || v1 || !v0)); } void test4() { bdd_manager m(3); bdd v0 = m.mk_var(0); bdd v1 = m.mk_var(1); bdd v2 = m.mk_var(2); bdd c1 = (v0 && v2) || v1; std::cout << "before reorder:\n"; std::cout << c1 << "\n"; std::cout << c1.bdd_size() << "\n"; m.gc(); m.try_reorder(); std::cout << "after reorder:\n"; std::cout << c1 << "\n"; std::cout << c1.bdd_size() << "\n"; } } void tst_bdd() { sat::test1(); sat::test2(); sat::test3(); sat::test4(); } z3-z3-4.8.7/src/test/bit_blaster.cpp000066400000000000000000000064061356505360400172010ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: bit_blaster.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-06-05. Revision History: --*/ #include "ast/rewriter/bit_blaster/bit_blaster.h" #include "ast/ast_pp.h" #include "ast/ast_ll_pp.h" void mk_bits(ast_manager & m, char const * prefix, unsigned sz, expr_ref_vector & r) { sort_ref b(m); b = m.mk_bool_sort(); for (unsigned i = 0; i < sz; ++i) { char buffer[128]; #ifdef _WINDOWS sprintf_s(buffer, Z3_ARRAYSIZE(buffer), "%s%d.smt", prefix, i); #else sprintf(buffer, "%s%d.smt", prefix, i); #endif r.push_back(m.mk_const(symbol(buffer), b)); } } void display(std::ostream & out, expr_ref_vector & r, bool ll=true) { ast_mark v; for (unsigned i = 0; i < r.size(); i++) { if (ll) ast_ll_pp(out, r.get_manager(), r.get(i), v); else out << mk_pp(r.get(i), r.get_manager()); out << "\n"; } } void tst_adder(ast_manager & m, unsigned sz) { // expr_ref_vector a(m); // expr_ref_vector b(m); // expr_ref_vector c(m); // mk_bits(m, "a", sz, a); // mk_bits(m, "b", sz, b); // bool t = true; // bit_blaster blaster(m, t); // blaster.mk_adder(sz, a.c_ptr(), b.c_ptr(), c); // TRACE("bit_blaster", display(tout, c);); } void tst_multiplier(ast_manager & m, unsigned sz) { // expr_ref_vector a(m); // expr_ref_vector b(m); // expr_ref_vector c(m); // mk_bits(m, "a", sz, a); // mk_bits(m, "b", sz, b); // bool t = true; // bit_blaster blaster(m, t); // blaster.mk_multiplier(sz, a.c_ptr(), b.c_ptr(), c); // TRACE("bit_blaster", display(tout, c);); } void tst_le(ast_manager & m, unsigned sz) { // expr_ref_vector a(m); // expr_ref_vector b(m); // expr_ref out(m); // mk_bits(m, "a", sz, a); // mk_bits(m, "b", sz, b); // bool t = true; // bit_blaster blaster(m, t); // blaster.mk_ule(sz, a.c_ptr(), b.c_ptr(), out); // TRACE("bit_blaster", tout << mk_pp(out, m) << "\n";); // blaster.mk_sle(sz, a.c_ptr(), b.c_ptr(), out); // TRACE("bit_blaster", tout << mk_pp(out, m) << "\n";); } void tst_eqs(ast_manager & m, unsigned sz) { // expr_ref_vector a(m); // expr_ref_vector b(m); // expr_ref out(m); // mk_bits(m, "a", sz, a); // bool t = true; // bit_blaster blaster(m, t); // blaster.mk_eqs(sz, a.c_ptr(), b); // TRACE("bit_blaster", display(tout, b, false);); } void tst_sh(ast_manager & m, unsigned sz) { // expr_ref_vector a(m); // expr_ref_vector b(m); // expr_ref_vector c(m); // mk_bits(m, "a", sz, a); // mk_bits(m, "b", sz, b); // bool t = true; // bit_blaster blaster(m, t); // blaster.mk_shl(sz, a.c_ptr(), b.c_ptr(), c); // TRACE("bit_blaster", tout << "shl\n"; display(tout, c);); // c.reset(); // blaster.mk_lshr(sz, a.c_ptr(), b.c_ptr(), c); // TRACE("bit_blaster", tout << "lshr\n"; display(tout, c);); // c.reset(); // blaster.mk_ashr(sz, a.c_ptr(), b.c_ptr(), c); // TRACE("bit_blaster", tout << "ashr " << c.size() << "\n"; display(tout, c, false);); } void tst_bit_blaster() { ast_manager m; tst_adder(m, 4); tst_multiplier(m, 4); tst_le(m, 4); tst_eqs(m, 8); tst_sh(m, 4); } z3-z3-4.8.7/src/test/bit_vector.cpp000066400000000000000000000150361356505360400170460ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: tst_bitvector.cpp Abstract: Test bitvector module Author: Leonardo de Moura (leonardo) 2006-10-03. Revision History: --*/ #include #include #include "util/bit_vector.h" #include "util/vector.h" static void tst1() { bit_vector v1; svector v2; unsigned n = rand()%10000; for (unsigned i = 0; i < n; i++) { int op = rand()%6; if (op <= 1) { bool val = (rand()%2) != 0; v1.push_back(val); v2.push_back(val); ENSURE(v1.size() == v2.size()); } else if (op <= 3) { ENSURE(v1.size() == v2.size()); if (!v1.empty()) { bool val = (rand()%2) != 0; unsigned idx = rand()%v1.size(); ENSURE(v1.get(idx) == v2[idx]); v1.set(idx, val); v2[idx] = val; ENSURE(v1.get(idx) == v2[idx]); } } else if (op <= 4) { ENSURE(v1.size() == v2.size()); if (!v1.empty()) { unsigned idx = rand()%v1.size(); VERIFY(v1.get(idx) == v2[idx]); } } else if (op <= 5) { ENSURE(v1.size() == v2.size()); for (unsigned j = 0; j < v1.size(); j++) { ENSURE(v1.get(j) == v2[j]); } } } } static void tst2() { bit_vector b; b.push_back(true); b.push_back(false); b.push_back(true); b.resize(30); ENSURE(b.get(0) == true); ENSURE(b.get(1) == false); ENSURE(b.get(2) == true); ENSURE(b.get(3) == false); ENSURE(b.get(29) == false); } static void tst_shift() { bit_vector b; b.resize(111); b.set(105); b.set(99); b.set(98); b.set(90); b.set(80); b.set(75); b.set(33); b.set(32); b.set(31); b.set(30); b.set(10); std::cout << "b: " << b << "\n"; b.shift_right(16); std::cout << "b: " << b << "\n"; b.shift_right(1); std::cout << "b: " << b << "\n"; b.shift_right(32); std::cout << "b: " << b << "\n"; b.shift_right(42); std::cout << "b: " << b << "\n"; b.shift_right(16); std::cout << "b: " << b << "\n"; b.shift_right(63); std::cout << "b: " << b << "\n"; } static void tst_or() { { bit_vector b1; bit_vector b2; b1.resize(5); b2.resize(10); b1.set(4); b2.set(8); b2.set(3); b2.set(2); b2.set(1); std::cout << b1 << "\n"; std::cout << b2 << "\n"; b1 |= b2; ENSURE(b1.size() == 10); std::cout << b1 << "\n"; ENSURE(b1 != b2); ENSURE(b1 != b2); b1.unset(4); ENSURE(b1 == b2); b1.unset(3); ENSURE(b1 != b2); } { bit_vector b1; bit_vector b2; b1.resize(2); b2.resize(5); b1.set(0); b2.set(4); b1 |= b2; ENSURE(b1 != b2); b2.set(0); ENSURE(b1 == b2); std::cout << "-----\n"; std::cout << b1 << "\n"; } { bit_vector b1; bit_vector b2; b1.resize(10); b2.resize(10); b1.set(5); b2.set(8); b2.set(3); b2.resize(5); b1 |= b2; ENSURE(!b1.get(8)); ENSURE(b1.get(5)); ENSURE(b1.get(3)); } { bit_vector b1; bit_vector b2; b1.resize(123); b2.resize(126); b2.set(124); b1.set(122); b1.set(100); b2.set(100); b1.set(80); b2.set(80); b1.set(4); b2.set(4); ENSURE(b1!=b2); b2.resize(123); ENSURE(b1!=b2); b1.resize(120); b2.resize(120); ENSURE(b1==b2); b1.unset(80); b1.unset(100); ENSURE(b1!=b2); b1 |= b2; ENSURE(b1 == b2); } { bit_vector b1; bit_vector b2; b1.resize(5); b2.resize(10); b2.set(8); b1.set(4); b2.set(1); b1.set(0); b1 |= b2; ENSURE(b1.size() == 10); ENSURE(b1.get(8) && b1.get(4) && b1.get(1) && b1.get(0) && !b1.get(9)); } { bit_vector b1; bit_vector b2; b1.resize(32); b2.resize(32); b1.set(1); b1.set(5); b2.set(8); b2.set(0); b1 |= b2; ENSURE(b1.get(1) && b1.get(5) && b1.get(8) && b1.get(0) && !b1.get(11)); std::cout << "b1(size32): " << b1 << "\n"; } } static void tst_and() { { bit_vector b1; bit_vector b2; b1.resize(5); b2.resize(3); b2.set(2); b1.set(2); b1.set(4); std::cout << "------\nb1: " << b1 << "\n"; b1 &= b2; std::cout << "------\nb1: " << b1 << "\n"; ENSURE(!b1.get(4)); ENSURE(b1.get(2)); } { bit_vector b1; bit_vector b2; b1.resize(241); b2.resize(128); b1.set(240); b1.set(232); b1.set(127); b1.set(128); b1.set(8); b2.set(127); b2.set(5); b1 &= b2; ENSURE(!b1.get(240) && !b1.get(232) && !b1.get(128) && b1.get(127) && !b1.get(8) && !b1.get(5)); } } static void tst_crash() { { bit_vector b; b.push_back(true); b.resize(64); ENSURE(!b.get(63)); ENSURE(b.get(0)); ENSURE(!b.get(1)); } { bit_vector b; b.push_back(false); b.resize(64, true); ENSURE(b.get(63)); ENSURE(!b.get(0)); ENSURE(b.get(1)); } } static void tst_bv_reset() { bit_vector b; bool bit = true; for (unsigned sz = 1; sz < 84; ++sz) { b.reset(); b.resize(sz, bit); for (unsigned i = 0; i < sz; ++i) { ENSURE(bit == b.get(i)); } for (unsigned sz2 = sz; sz2 < sz+10; ++sz2) { b.resize(sz2, !bit); for (unsigned i = sz; i < sz2; ++i) { ENSURE(bit != b.get(i)); } } bit = !bit; } } static void tst_eq() { bit_vector b1, b2, b3; b1.resize(32); b2.resize(32); b3.resize(32); b1.set(3, true); ENSURE(b1 != b2); ENSURE(!(b1 == b2)); ENSURE(b2 == b3); b3.set(3, true); ENSURE(b1 == b3); ENSURE(!(b1 != b3)); b2.set(31, true); b3.set(31); b3.unset(3); ENSURE(b2 == b3); ENSURE(!(b2 != b3)); } void tst_bit_vector() { tst_crash(); tst_shift(); tst_or(); tst_and(); tst_bv_reset(); tst_eq(); return; tst2(); for (unsigned i = 0; i < 20; i++) { std::cerr << i << std::endl; tst1(); } } z3-z3-4.8.7/src/test/bits.cpp000066400000000000000000000152051356505360400156450ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ // Test some bit hacks #include "util/util.h" #include "util/debug.h" #include "util/vector.h" #include "util/mpz.h" #include "util/bit_util.h" static void tst_shl(unsigned src_sz, unsigned const * src, unsigned k, unsigned dst_sz, unsigned const * dst, bool trace = true) { if (trace) { std::cout << "shl({"; for (unsigned i = 0; i < src_sz; i++) { if (i > 0) std::cout << ", "; std::cout << src[i]; } std::cout << "}, " << k << ")" << std::endl; } svector actual_dst; actual_dst.resize(dst_sz, 0xAAAAAAAA); for (unsigned sz = 1; sz <= dst_sz; sz++) { if (trace) std::cout << " for sz = " << sz << std::endl; shl(src_sz, src, k, sz, actual_dst.c_ptr()); ENSURE(!has_one_at_first_k_bits(sz, actual_dst.c_ptr(), k)); for (unsigned i = 0; i < sz; i++) { if (trace && dst[i] != actual_dst[i]) std::cout << "UNEXPECTED RESULT at [" << i << "]: " << actual_dst[i] << ", expected: " << dst[i] << "\n"; ENSURE(dst[i] == actual_dst[i]); } if (sz == src_sz) { unsigned nz1 = nlz(sz, src); if (nz1 >= k && !is_zero(sz, src)) { unsigned nz2 = nlz(sz, actual_dst.c_ptr()); if (nz1 - k != nz2) { if (trace) std::cout << "nlz BUG, nlz1: " << nz1 << ", k: " << k << ", nlz2: " << nz2 << std::endl; UNREACHABLE(); } } } if (sz >= src_sz + (k/32) + 1) { svector new_src; new_src.resize(sz, 0xAAAAAAAA); shr(sz, actual_dst.c_ptr(), k, new_src.c_ptr()); for (unsigned i = 0; i < src_sz; i++) { if (trace && src[i] != new_src[i]) { std::cout << "shr BUG, inverting shl, at bit[" << i << "], " << new_src[i] << ", expected: " << src[i] << std::endl; } ENSURE(src[i] == new_src[i]); } } } if (trace) std::cout << " shift by 1, k times" << std::endl; copy(src_sz, src, dst_sz, actual_dst.c_ptr()); for (unsigned i = 0; i < k; i++) { shl(dst_sz, actual_dst.c_ptr(), 1, dst_sz, actual_dst.c_ptr()); } for (unsigned i = 0; i < dst_sz; i++) { if (trace && dst[i] != actual_dst[i]) std::cout << "UNEXPECTED RESULT at [" << i << "]: " << actual_dst[i] << ", expected: " << dst[i] << "\n"; ENSURE(dst[i] == actual_dst[i]); } if (src_sz <= dst_sz) { if (trace) std::cout << " self-shl" << std::endl; shl(src_sz, src, k, src_sz, const_cast(src)); for (unsigned i = 0; i < src_sz; i++) { if (trace && src[i] != dst[i]) std::cout << "UNEXPECTED RESULT at [" << i << "]: " << src[i] << ", expected: " << dst[i] << "\n"; ENSURE(src[i] == actual_dst[i]); } } } static void tst_shl() { { unsigned src[2] = {0, 2}; unsigned dst[2] = {0, 2<<10}; tst_shl(2, src, 10, 2, dst); } { unsigned src[2] = {2, 0}; unsigned dst[2] = {0, 2<<10}; tst_shl(2, src, 42, 2, dst); } { unsigned src[2] = {0, 0}; unsigned dst[3] = {0, 0, 0}; tst_shl(2, src, 1, 3, dst); } { unsigned src[2] = {0x80000009, 5}; unsigned dst[2] = {18, 11}; tst_shl(2, src, 1, 2, dst); } { unsigned src[2] = {0x80000009, 0x80000005}; unsigned dst[2] = {18, 11}; tst_shl(2, src, 1, 2, dst); } { unsigned src[2] = {0x80000009, 0x80000005}; unsigned dst[3] = {18, 11, 1}; tst_shl(2, src, 1, 3, dst); } { unsigned src[2] = {0x80000009, 0x80000005}; unsigned dst[3] = {0, 18, 11}; tst_shl(2, src, 33, 3, dst); } { unsigned src[2] = {0x80000009, 0x80000005}; unsigned dst[4] = {0, 18, 11, 1}; tst_shl(2, src, 33, 4, dst); } { unsigned src[2] = {0xFFFFFFFF, 0xFFFFFFFF}; unsigned dst[2] = {0xFFFFFFF0, 0xFFFFFFFF}; tst_shl(2, src, 4, 2, dst); } } static void tst_shr(unsigned src_sz, unsigned const * src, unsigned k, unsigned const * dst, bool trace = true) { if (trace) { std::cout << "shr({"; for (unsigned i = 0; i < src_sz; i++) { if (i > 0) std::cout << ", "; std::cout << src[i]; } std::cout << "}, " << k << ")" << std::endl; } svector actual_dst; actual_dst.resize(src_sz, 0xAAAAAAAA); shr(src_sz, src, k, actual_dst.c_ptr()); for (unsigned i = 0; i < src_sz; i++) { if (trace && dst[i] != actual_dst[i]) std::cout << "UNEXPECTED RESULT at [" << i << "]: " << actual_dst[i] << ", expected: " << dst[i] << "\n"; ENSURE(dst[i] == actual_dst[i]); } } static void tst_shr() { { unsigned src[2] = {0, 0}; unsigned dst[2] = {0, 0}; tst_shr(2, src, 1, dst); } } static void tst_shl_rand(unsynch_mpz_manager & m, unsigned sz, unsigned k, bool trace = true) { // create a random bitvector of of size sz svector src; for (unsigned i = 0; i < sz; i++) { src.push_back(rand()); } // convert src into a mpz number scoped_mpz _src(m); scoped_mpz tmp(m); unsigned i = sz; while (i > 0) { --i; m.mul2k(_src, 32); m.set(tmp, src[i]); m.add(_src, tmp, _src); } // shift left by multiplying by 2^k scoped_mpz _dst(m); m.set(_dst, _src); m.mul2k(_dst, k); // convert _dst into a vector of unsigned values svector dst; scoped_mpz max(m); m.set(max, 1); m.mul2k(max, 32); while (!m.is_zero(_dst)) { m.mod(_dst, max, tmp); ENSURE(m.is_uint64(tmp) && m.get_uint64(tmp) < UINT_MAX); dst.push_back(static_cast(m.get_uint64(tmp))); m.div(_dst, max, _dst); } while (dst.size() < src.size()) dst.push_back(0); dst.push_back(0); unsigned word_shift = (k / 32); for (unsigned i = 0; i < word_shift; i++) dst.push_back(0); tst_shl(src.size(), src.c_ptr(), k, dst.size(), dst.c_ptr(), trace); } static void tst_shl_rand(unsigned N, unsigned sz, unsigned k, bool trace = false) { unsynch_mpz_manager m; for (unsigned i = 0; i < N; i++) { unsigned _sz = rand() % sz; if (_sz == 0) _sz = 1; unsigned _k = rand() % k; if (_k == 0) _k = 1; tst_shl_rand(m, _sz, _k, trace); } } void tst_bits() { tst_shr(); tst_shl(); tst_shl_rand(100000, 4, 100); } z3-z3-4.8.7/src/test/buffer.cpp000066400000000000000000000015661356505360400161620ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: buffer.cpp Abstract: Test buffers. Author: Leonardo de Moura (leonardo) 2011-03-03. Revision History: --*/ #include "util/ptr_scoped_buffer.h" typedef std::pair point; template class ptr_scoped_buffer; static void tst1() { ptr_scoped_buffer b; ENSURE(b.empty()); b.push_back(alloc(point, 10, 20)); ENSURE(!b.empty()); point * p1 = alloc(point, 30, 20); b.push_back(p1); ENSURE(b.get(1) == p1); b.push_back(alloc(point, 40, 20)); ENSURE(b.size() == 3); b.pop_back(); ENSURE(b.get(0) != p1); ENSURE(b.get(1) == p1); point * p2 = alloc(point, 30, 20); ENSURE(b.get(0) != p2); b.set(0, p2); ENSURE(b.get(0) == p2); ENSURE(b.size() == 2); b.push_back(alloc(point, 40, 40)); } void tst_buffer() { tst1(); } z3-z3-4.8.7/src/test/chashtable.cpp000066400000000000000000000100621356505360400167760ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: chashtable.cpp Abstract: Hashtable with chaining. Author: Leonardo de Moura (leonardo) 2011-04-14. Revision History: --*/ #include "util/chashtable.h" #include "util/hashtable.h" #include "util/hash.h" #include "util/util.h" typedef chashtable > int_table; typedef cmap > int_map; template class chashtable >; template class cmap >; template static void display(T const & beg, T const & end) { for (T it = beg; it != end; ++it) std::cout << *it << " "; std::cout << "\n"; } static void tst1() { int_table t; t.insert(10); ENSURE(t.contains(10)); t.insert(20); ENSURE(t.contains(20)); t.insert(30); ENSURE(t.contains(30)); ENSURE(t.size() == 3); display(t.begin(), t.end()); t.erase(20); ENSURE(!t.contains(20)); ENSURE(t.size() == 2); } struct dummy_hash { unsigned operator()(int v) const { return v % 2; } }; typedef chashtable > dint_table; template class chashtable >; static void tst2() { dint_table t; t.insert(10); t.insert(12); ENSURE(t.used_slots() == 1); display(t.begin(), t.end()); t.insert(13); display(t.begin(), t.end()); ENSURE(t.used_slots() == 2); t.insert(14); ENSURE(t.used_slots() == 2); ENSURE(t.size() == 4); display(t.begin(), t.end()); t.erase(12); ENSURE(!t.contains(12)); ENSURE(t.size() == 3); ENSURE(t.contains(10)); ENSURE(!t.contains(12)); ENSURE(t.contains(14)); ENSURE(t.contains(13)); t.insert(16); ENSURE(t.size() == 4); t.insert(18); ENSURE(t.size() == 5); ENSURE(t.used_slots() == 2); display(t.begin(), t.end()); t.erase(10); display(t.begin(), t.end()); ENSURE(!t.contains(10)); ENSURE(!t.contains(12)); ENSURE(t.contains(14)); ENSURE(t.contains(13)); ENSURE(t.contains(16)); ENSURE(t.contains(18)); } static void tst3() { dint_table t; t.insert(10); t.insert(12); ENSURE(t.used_slots() == 1); ENSURE(t.contains(10)); ENSURE(t.contains(12)); t.erase(12); t.erase(10); ENSURE(t.empty()); ENSURE(t.empty()); ENSURE(t.used_slots() == 0); t.insert(10); ENSURE(t.used_slots() == 1); ENSURE(t.contains(10)); ENSURE(t.size() == 1); } typedef int_hashtable > int_set; template static void tst4(unsigned num, unsigned N) { int_set s; T t; for (unsigned i = 0; i < num; i++) { int v = rand() % N; if (rand() % 3 == 2) { TRACE("chashtable", tout << "erase " << v << "\n";); s.erase(v); t.erase(v); ENSURE(!t.contains(v)); } else { TRACE("chashtable", tout << "insert " << v << "\n";); s.insert(v); t.insert(v); ENSURE(t.contains(v)); } ENSURE(s.size() == t.size()); ENSURE(s.empty() == t.empty()); } std::cout << "size: " << s.size() << " " << t.size() << "\n"; int_set::iterator it1 = s.begin(); int_set::iterator end1 = s.end(); for(; it1 != end1; ++it1) { ENSURE(t.contains(*it1)); } typename T::iterator it2 = t.begin(); typename T::iterator end2 = t.end(); for(; it2 != end2; ++it2) { ENSURE(s.contains(*it2)); ENSURE(t.contains(*it2)); } } static void tst5() { dint_table t; t.insert(4); t.insert(9); t.insert(8); t.insert(1); t.erase(1); t.insert(7); t.insert(1); t.insert(2); } static void tst6() { int_map m; m.insert(10, 4); ENSURE(m.contains(10)); DEBUG_CODE({ int r; ENSURE(m.find(10, r) && r == 4); }); } void tst_chashtable() { tst1(); tst2(); tst3(); tst6(); tst4(1000,10); tst4(10000,10); tst4(50000,1000); tst5(); } z3-z3-4.8.7/src/test/check_assumptions.cpp000066400000000000000000000026341356505360400204300ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "util/memory_manager.h" #include "smt/params/smt_params.h" #include "ast/ast.h" #include "ast/arith_decl_plugin.h" #include "ast/bv_decl_plugin.h" #include "smt/smt_context.h" #include "ast/reg_decl_plugins.h" void tst_check_assumptions() { memory::initialize(0); smt_params params; ast_manager mgr; reg_decl_plugins(mgr); sort_ref b(mgr.mk_bool_sort(), mgr); func_decl_ref pPred(mgr.mk_func_decl(symbol("p"), 0, static_cast(nullptr), b), mgr); func_decl_ref qPred(mgr.mk_func_decl(symbol("q"), 0, static_cast(nullptr), b), mgr); func_decl_ref rPred(mgr.mk_func_decl(symbol("r"), 0, static_cast(nullptr), b), mgr); app_ref p(mgr.mk_app(pPred,0,static_cast(nullptr)), mgr); app_ref q(mgr.mk_app(qPred,0,static_cast(nullptr)), mgr); app_ref r(mgr.mk_app(rPred,0,static_cast(nullptr)), mgr); app_ref pOqOr(mgr.mk_or(p,q,r), mgr); app_ref np(mgr.mk_not(p), mgr); app_ref nq(mgr.mk_not(q), mgr); app_ref nr(mgr.mk_not(r), mgr); smt::context ctx(mgr, params); ctx.assert_expr(pOqOr); expr * npE = np.get(); lbool res1 = ctx.check(1, &npE); VERIFY(res1 == l_true); ctx.assert_expr(npE); expr * assumpt[] = { nq.get(), nr.get() }; //here it should crash ctx.check(2, assumpt); } z3-z3-4.8.7/src/test/cnf_backbones.cpp000066400000000000000000000220711356505360400174600ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation --*/ #include #include #include #include "util/timeout.h" #include "util/rlimit.h" #include "sat/dimacs.h" #include "sat/sat_solver.h" #include "util/gparams.h" static sat::solver * g_solver = nullptr; static clock_t g_start_time; static void display_statistics() { clock_t end_time = clock(); if (g_solver) { 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); } } static void on_timeout() { display_statistics(); exit(0); } static void STD_CALL on_ctrl_c(int) { signal (SIGINT, SIG_DFL); display_statistics(); raise(SIGINT); } #if 0 static void display_model(sat::solver const & s) { sat::model const & m = s.get_model(); for (unsigned i = 1; i < m.size(); i++) { switch (m[i]) { case l_false: std::cout << "-" << i << " "; break; case l_undef: break; case l_true: std::cout << i << " "; break; } } std::cout << "\n"; } #endif static void display_status(lbool r) { switch (r) { case l_true: std::cout << "sat\n"; break; case l_undef: std::cout << "unknown\n"; break; case l_false: std::cout << "unsat\n"; break; } } static void track_clause(sat::solver& dst, sat::literal_vector& lits, sat::literal_vector& assumptions, vector& tracking_clauses) { sat::literal lit = sat::literal(dst.mk_var(true, false), false); tracking_clauses.set(lit.var(), lits); lits.push_back(~lit); dst.mk_clause(lits.size(), lits.c_ptr()); assumptions.push_back(lit); } static void track_clauses(sat::solver const& src, sat::solver& dst, sat::literal_vector& assumptions, vector& tracking_clauses) { for (sat::bool_var v = 0; v < src.num_vars(); ++v) { dst.mk_var(false, true); } sat::literal_vector lits; sat::literal lit; sat::clause * const * it = src.begin_clauses(); sat::clause * const * end = src.end_clauses(); svector bin_clauses; 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) { if (src.value(v) != l_undef) { bool sign = src.value(v) == l_false; lits.reset(); lits.push_back(sat::literal(v, sign)); track_clause(dst, lits, assumptions, tracking_clauses); } } for (; it != end; ++it) { lits.reset(); sat::clause& cls = *(*it); lits.append(static_cast(cls.end()-cls.begin()), cls.begin()); track_clause(dst, lits, assumptions, tracking_clauses); } for (unsigned i = 0; i < bin_clauses.size(); ++i) { lits.reset(); lits.push_back(bin_clauses[i].first); lits.push_back(bin_clauses[i].second); track_clause(dst, lits, assumptions, tracking_clauses); } } static void prune_unfixed(sat::literal_vector& lambda, sat::model const& m) { for (unsigned i = 0; i < lambda.size(); ++i) { if ((m[lambda[i].var()] == l_false) != lambda[i].sign()) { lambda[i] = lambda.back(); lambda.pop_back(); --i; } } } // Algorithm 7: Corebased Algorithm with Chunking static void back_remove(sat::literal_vector& lits, sat::literal l) { for (unsigned i = lits.size(); i > 0; ) { --i; if (lits[i] == l) { lits[i] = lits.back(); lits.pop_back(); return; } } std::cout << "UNREACHABLE\n"; } static void brute_force_consequences(sat::solver& s, sat::literal_vector const& asms, sat::literal_vector const& gamma, sat::literal_vector& backbones) { for (unsigned i = 0; i < gamma.size(); ++i) { sat::literal nlit = ~gamma[i]; sat::literal_vector asms1(asms); asms1.push_back(nlit); lbool r = s.check(asms1.size(), asms1.c_ptr()); if (r == l_false) { backbones.push_back(gamma[i]); } } } static lbool core_chunking(sat::solver& s, sat::bool_var_vector& vars, sat::literal_vector const& asms, vector& conseq, unsigned K) { lbool r = s.check(asms.size(), asms.c_ptr()); if (r != l_true) { return r; } sat::model const & m = s.get_model(); sat::literal_vector lambda, backbones; for (unsigned i = 0; i < vars.size(); i++) { lambda.push_back(sat::literal(vars[i], m[vars[i]] == l_false)); } while (!lambda.empty()) { IF_VERBOSE(1, verbose_stream() << "(sat-backbone-core " << lambda.size() << " " << backbones.size() << ")\n";); unsigned k = std::min(K, lambda.size()); sat::literal_vector gamma, omegaN; for (unsigned i = 0; i < k; ++i) { sat::literal l = lambda[lambda.size() - i - 1]; gamma.push_back(l); omegaN.push_back(~l); } while (true) { sat::literal_vector asms1(asms); asms1.append(omegaN); r = s.check(asms1.size(), asms1.c_ptr()); if (r == l_true) { IF_VERBOSE(1, verbose_stream() << "(sat) " << omegaN << "\n";); prune_unfixed(lambda, s.get_model()); break; } sat::literal_vector const& core = s.get_core(); sat::literal_vector occurs; IF_VERBOSE(1, verbose_stream() << "(core " << core.size() << ")\n";); for (unsigned i = 0; i < omegaN.size(); ++i) { if (core.contains(omegaN[i])) { occurs.push_back(omegaN[i]); } } if (occurs.size() == 1) { sat::literal lit = occurs.back(); sat::literal nlit = ~lit; backbones.push_back(~lit); back_remove(lambda, ~lit); back_remove(gamma, ~lit); s.mk_clause(1, &nlit); } for (unsigned i = 0; i < omegaN.size(); ++i) { if (occurs.contains(omegaN[i])) { omegaN[i] = omegaN.back(); omegaN.pop_back(); --i; } } if (omegaN.empty() && occurs.size() > 1) { brute_force_consequences(s, asms, gamma, backbones); for (unsigned i = 0; i < gamma.size(); ++i) { back_remove(lambda, gamma[i]); } break; } } } for (unsigned i = 0; i < backbones.size(); ++i) { sat::literal_vector cons; cons.push_back(backbones[i]); conseq.push_back(cons); } return l_true; } static void cnf_backbones(bool use_chunk, 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"); p.set_bool("produce_models", true); reslimit limit; sat::solver solver(p, limit); sat::solver solver2(p, limit); g_solver = &solver; if (file_name) { std::ifstream in(file_name); if (in.bad() || in.fail()) { std::cerr << "(error \"failed to open file '" << file_name << "'\")" << std::endl; exit(ERR_OPEN_FILE); } if (!parse_dimacs(in, std::cerr, solver)) return; } else { if (!parse_dimacs(std::cin, std::cerr, solver)) return; } IF_VERBOSE(20, solver.display_status(verbose_stream());); vector conseq; sat::bool_var_vector vars; sat::literal_vector assumptions; unsigned num_vars = solver.num_vars(); if (p.get_bool("dimacs.core", false)) { g_solver = &solver2; vector tracking_clauses; track_clauses(solver, solver2, assumptions, tracking_clauses); } // remove this line to limit variables to exclude assumptions num_vars = g_solver->num_vars(); for (unsigned i = 1; i < num_vars; ++i) { vars.push_back(i); g_solver->set_external(i); } lbool r; if (use_chunk) { r = core_chunking(*g_solver, vars, assumptions, conseq, 100); } else { r = g_solver->get_consequences(assumptions, vars, conseq); } std::cout << vars.size() << " " << conseq.size() << "\n"; display_status(r); display_statistics(); } void tst_cnf_backbones(char ** argv, int argc, int& i) { bool use_chunk = i + 1 < argc && argv[i + 1] == std::string("chunk"); if (use_chunk) ++i; char const* file = ""; if (i + 1 < argc) { file = argv[i + 1]; } else { file = argv[1]; } cnf_backbones(use_chunk, file); ++i; } z3-z3-4.8.7/src/test/cube_clause.cpp000066400000000000000000000040361356505360400171560ustar00rootroot00000000000000#include "ast/reg_decl_plugins.h" #include "solver/solver_pool.h" #include "smt/smt_solver.h" void tst_cube_clause() { ast_manager m; reg_decl_plugins(m); params_ref p; lbool r; ref solver = mk_smt_solver(m, p, symbol::null); expr_ref a(m.mk_const(symbol("a"), m.mk_bool_sort()), m); expr_ref b(m.mk_const(symbol("b"), m.mk_bool_sort()), m); expr_ref c(m.mk_const(symbol("c"), m.mk_bool_sort()), m); expr_ref d(m.mk_const(symbol("d"), m.mk_bool_sort()), m); expr_ref e(m.mk_const(symbol("e"), m.mk_bool_sort()), m); expr_ref f(m.mk_const(symbol("f"), m.mk_bool_sort()), m); expr_ref g(m.mk_const(symbol("g"), m.mk_bool_sort()), m); expr_ref fml(m); fml = m.mk_not(m.mk_and(a, b)); solver->assert_expr(fml); fml = m.mk_not(m.mk_and(c, d)); solver->assert_expr(fml); fml = m.mk_not(m.mk_and(e, f)); solver->assert_expr(fml); expr_ref_vector cube(m), clause(m), core(m); r = solver->check_sat(cube); std::cout << r << "\n"; cube.push_back(a); r = solver->check_sat(cube); std::cout << r << "\n"; cube.push_back(c); cube.push_back(e); r = solver->check_sat(cube); std::cout << r << "\n"; clause.push_back(b); vector clauses; clauses.push_back(clause); r = solver->check_sat_cc(cube, clauses); std::cout << r << "\n"; core.reset(); solver->get_unsat_core(core); std::cout << core << "\n"; clause.push_back(d); clauses.reset(); clauses.push_back(clause); r = solver->check_sat_cc(cube, clauses); std::cout << r << "\n"; core.reset(); solver->get_unsat_core(core); std::cout << core << "\n"; clause.push_back(f); clauses.reset(); clauses.push_back(clause); r = solver->check_sat_cc(cube, clauses); std::cout << r << "\n"; core.reset(); solver->get_unsat_core(core); std::cout << core << "\n"; clause.push_back(g); clauses.reset(); clauses.push_back(clause); r = solver->check_sat_cc(cube, clauses); std::cout << r << "\n"; } z3-z3-4.8.7/src/test/datalog_parser.cpp000066400000000000000000000031761356505360400176770ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "muz/fp/datalog_parser.h" #include "ast/ast_pp.h" #include "ast/arith_decl_plugin.h" #include "muz/base/dl_context.h" #include "muz/fp/dl_register_engine.h" #include "smt/params/smt_params.h" #include "ast/reg_decl_plugins.h" using namespace datalog; static void dparse_string(char const* str) { ast_manager m; smt_params params; reg_decl_plugins(m); register_engine re; context ctx(m, re, params); parser* p = parser::create(ctx,m); bool res = p->parse_string(str); if (res) { std::cout << "Parsed\n"<parse_file(file)) { std::cout << "Failed to parse file\n"; } dealloc(p); } void tst_datalog_parser() { dparse_string("\nH :- C1(X,a,b), C2(Y,a,X) ."); dparse_string("N 128\n\nH :- C1(X,a,b), C2(Y,a,X) ."); dparse_string("N 128\nI 128\n\nC1(x : N, y : N, z : I)\nC2(x : N, y : N, z : N)\nH :- C1(X,a,b), C2(Y,a,X) ."); dparse_string("\nH :- C1(X,a,b), nC2(Y,a,X) ."); dparse_string("\nH :- C1(X,a,b),nC2(Y,a,X)."); dparse_string("\nH :- C1(X,a,b),\\\nC2(Y,a,X)."); dparse_string("\nH :- C1(X,a\\,\\b), C2(Y,a,X) ."); } void tst_datalog_parser_file(char** argv, int argc, int & i) { if (i + 1 < argc) { dparse_file(argv[i+1]); i++; } } z3-z3-4.8.7/src/test/ddnf.cpp000066400000000000000000000141661356505360400156240ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "muz/ddnf/ddnf.h" #include "muz/rel/tbv.h" #include #include #include #include #include #include #include #include /* TBD: count number of nodes, number of operations across all insertions */ void read_nums(std::istream& is, unsigned & x, unsigned& y) { x = 0; y = 0; is >> x; is >> y; std::string line; std::getline(is, line); } static char const* g_file = nullptr; void create_forwarding( char const* file, datalog::ddnf_core& ddnf, ptr_vector& tbvs, vector& fwd_indices) { IF_VERBOSE(1, verbose_stream() << "creating (and forgetting) forwarding index\n";); std::ifstream is(file); if (is.bad() || is.fail()) { std::cout << "could not load " << file << "\n"; exit(0); } std::string line; unsigned W, M; read_nums(is, W, M); tbv_manager& tbvm = ddnf.get_tbv_manager(); tbv* tX = tbvm.allocateX(); unsigned_vector forwarding_set; for (unsigned r = 0; r < M; ++r) { unsigned P, K; read_nums(is, K, P); ddnf.reset_accumulate(); unsigned p; fwd_indices.push_back(unsigned_vector()); unsigned_vector& forwarding_index = fwd_indices.back(); forwarding_index.resize(ddnf.size()); for (unsigned g = 0; g < K; ++g) { is >> p; std::getline(is, line); tbv* t = tbvm.allocate(line.c_str()); if (p > P) { std::cout << "port number " << p << " too big " << P << "\n"; tbvm.display(std::cout, *t) << " " << line << "\n"; exit(0); } forwarding_set.reset(); ddnf.accumulate(*t, forwarding_set); for (unsigned i = 0; i < forwarding_set.size(); ++i) { forwarding_index[forwarding_set[i]] = p; } tbvs.push_back(t); if (p == 0 && tbvm.equals(*t, *tX)) break; } } tbvm.deallocate(tX); } datalog::ddnf_core* populate_ddnf(char const* file, ptr_vector& tbvs) { IF_VERBOSE(1, verbose_stream() << "populate ddnf\n";); std::ifstream is(file); if (is.bad() || is.fail()) { std::cout << "could not load " << file << "\n"; exit(0); } std::string line; unsigned W, M; read_nums(is, W, M); datalog::ddnf_core* ddnf = alloc(datalog::ddnf_core, W); tbv_manager& tbvm = ddnf->get_tbv_manager(); tbv* tX = tbvm.allocateX(); for (unsigned r = 0; r < M; ++r) { unsigned P, K; read_nums(is, K, P); IF_VERBOSE(1, verbose_stream() << K << " " << P << "\n";); unsigned p; for (unsigned g = 0; g < K; ++g) { is >> p; std::getline(is, line); tbv* t = tbvm.allocate(line.c_str()); ddnf->insert(*t); IF_VERBOSE(2, tbvm.display(verbose_stream() << line << " ", *t) << "\n";); tbvs.push_back(t); if (p > P) { std::cout << "port number " << p << " too big " << P << "\n"; tbvm.display(std::cout, *t) << " " << line << "\n"; exit(0); } if (p == 0 && tbvm.equals(*t, *tX)) break; // std::cout << ddnf->well_formed() << "\n"; } } tbvm.deallocate(tX); return ddnf; } static void read_args(char ** argv, int argc, int& i) { if (argc == i + 2) { g_file = argv[i + 1]; ++i; return; } if (!g_file) { std::cout << "Need routing table file as argument. Arguments provided: "; for (int j = i; j < argc; ++j) { std::cout << argv[j] << " "; } std::cout << "\n"; exit(0); } } typedef std::pair u_pair; struct uu_eq { bool operator()(u_pair u1, u_pair u2) const { return u1 == u2; } }; typedef map, uu_eq > pair_table; static unsigned refine_forwarding( unsigned_vector& p, unsigned_vector const& q) { unsigned sz = p.size(); unsigned n = 0, m = 0; pair_table tbl; for (unsigned i = 0; i < sz; ++i) { u_pair pr = std::make_pair(p[i], q[i]); if (tbl.find(pr, m)) { p[i] = m; } else { p[i] = n; tbl.insert(pr, n++); } } return n; } static void refine_forwarding( datalog::ddnf_core& ddnf, vector const& fwd_indices) { unsigned_vector roots; roots.resize(ddnf.size()); for (unsigned i = 0; i < roots.size(); ++i) { roots[i] = 0; } unsigned max_class = 1; for (unsigned i = 0; i < fwd_indices.size(); ++i) { unsigned_vector const& fwd = fwd_indices[i]; max_class = refine_forwarding(roots, fwd); } std::cout << "num classes: " << max_class << "\n"; } void tst_ddnf(char ** argv, int argc, int& i) { read_args(argv, argc, i); ptr_vector tbvs; datalog::ddnf_core* ddnf = populate_ddnf(g_file, tbvs); IF_VERBOSE(1, ddnf->display(verbose_stream());); vector fwd_indices; create_forwarding(g_file, *ddnf, tbvs, fwd_indices); refine_forwarding(*ddnf, fwd_indices); std::cout << "resulting size: " << ddnf->size() << "\n"; ddnf->display_statistics(std::cout); IF_VERBOSE(1, ddnf->display(verbose_stream()); verbose_stream() << ddnf->well_formed() << "\n";); tbv_manager& tbvm = ddnf->get_tbv_manager(); for (unsigned i = 0; i < tbvs.size(); ++i) { tbvm.deallocate(tbvs[i]); } dealloc(ddnf); } void tst_ddnf1() { enable_trace("ddnf"); unsigned W = 2; datalog::ddnf_core ddnf(W); tbv_manager& tbvm = ddnf.get_tbv_manager(); tbv* tXX = tbvm.allocate("xx"); tbv* t1X = tbvm.allocate("1x"); tbv* tX1 = tbvm.allocate("x1"); tbv* t11 = tbvm.allocate("11"); ddnf.insert(*tXX); ddnf.insert(*t11); ddnf.insert(*tX1); ddnf.insert(*t1X); ddnf.display(std::cout); tbvm.deallocate(tXX); tbvm.deallocate(t1X); tbvm.deallocate(tX1); tbvm.deallocate(t11); } z3-z3-4.8.7/src/test/diff_logic.cpp000066400000000000000000000103341356505360400167670ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: diff_logic.cpp Abstract: Unit tests for difference logic Author: Leonardo de Moura (leonardo) 2006-11-22. Revision History: --*/ #ifdef _WINDOWS #include "util/rational.h" #include "smt/diff_logic.h" #include "smt/smt_literal.h" #include "util/util.h" #include "util/debug.h" struct diff_logic_ext { typedef rational numeral; typedef smt::literal explanation; }; template class dl_graph; typedef dl_graph dlg; struct tst_dl_functor { smt::literal_vector m_literals; void operator()(smt::literal l) { m_literals.push_back(l); } }; static void tst1() { dlg g; smt::literal l; g.init_var(1); g.init_var(2); g.init_var(3); g.enable_edge(g.add_edge(1, 2, rational(1), l)); g.enable_edge(g.add_edge(2, 3, rational(2), l)); g.push(); g.enable_edge(g.add_edge(1, 3, rational(4), l)); g.init_var(4); g.enable_edge(g.add_edge(1, 4, rational(5), l)); g.enable_edge(g.add_edge(4, 2, rational(0), l)); g.pop(1); } static void tst2() { dlg g; rational w; smt::literal l1(1); smt::literal l2(2); smt::literal l3(3); smt::literal l4(4); smt::literal l5(5); smt::literal l6(6); g.init_var(0); g.init_var(1); g.init_var(2); g.init_var(3); g.init_var(4); smt::literal d; ENSURE(g.enable_edge(g.add_edge(1, 2, rational(-1), l1))); ENSURE(g.get_edge_weight(1, 2, w, d) && w == rational(-1)); ENSURE(!g.get_edge_weight(2, 3, w, d)); ENSURE(g.enable_edge(g.add_edge(2, 3, rational(-2), l2))); ENSURE(g.enable_edge(g.add_edge(1, 4, rational(1), l3))); ENSURE(g.get_edge_weight(1, 2, w, d) && w == rational(-1)); ENSURE(g.get_edge_weight(1, 4, w, d) && w == rational(1)); ENSURE(!g.get_edge_weight(1, 3, w, d)); ENSURE(g.enable_edge(g.add_edge(2, 4, rational(10), l6))); ENSURE(g.is_feasible()); g.push(); ENSURE(g.enable_edge(g.add_edge(3, 0, rational(2), l4))); ENSURE(!g.enable_edge(g.add_edge(0, 1, rational(-1), l5))); ENSURE(!g.is_feasible()); TRACE("diff_logic", g.display(tout);); struct proc { svector found; proc(): found(7, false) { } void operator()(smt::literal l) { found[l.var()] = true; } }; proc p; g.traverse_neg_cycle(true, p); ENSURE(p.found[0] == false); ENSURE(p.found[1] == true); ENSURE(p.found[2] == true); ENSURE(p.found[3] == false); ENSURE(p.found[4] == true); ENSURE(p.found[5] == true); ENSURE(p.found[6] == false); g.pop(1); ENSURE(g.is_feasible()); TRACE("diff_logic", g.display(tout);); } static int add_edge(dlg& g, dl_var src, dl_var dst, int weight, unsigned lit) { int id = g.add_edge(src, dst, rational(weight), smt::literal(lit)); bool ok = g.enable_edge(id); ENSURE(ok); return id; } static void tst3() { dlg g; for (unsigned i = 1; i <= 10; ++i) { g.init_var(i); } add_edge(g, 1, 2, 1, 12); add_edge(g, 1, 3, 1, 13); add_edge(g, 1, 4, 1, 14); add_edge(g, 2, 5, 1, 25); add_edge(g, 2, 6, 1, 26); add_edge(g, 3, 5, 1, 35); add_edge(g, 4, 5, 1, 45); add_edge(g, 4, 6, 1, 46); int xy = add_edge(g, 5, 6, 1, 56); add_edge(g, 5, 7, 1, 57); add_edge(g, 5, 9, 1, 59); add_edge(g, 6, 7, 1, 67); add_edge(g, 6, 8, 1, 68); add_edge(g, 6, 9, 1, 69); add_edge(g, 6, 10, 1, 610); add_edge(g, 8, 10, 1, 810); add_edge(g, 9, 10, 1, 910); TRACE("diff_logic", g.display(tout);); int e38 = g.add_edge(3, 8, rational(3), smt::literal(38)); std::cout << "Edge: " << e38 << "\n"; svector subsumed; g.find_subsumed(xy, subsumed); for (unsigned i = 0; i < subsumed.size(); ++i) { std::cout << "subsumed: " << subsumed[i] << "\n"; ENSURE(e38 == subsumed[i]); tst_dl_functor tst_fn; g.explain_subsumed_lazy(xy, subsumed[i], tst_fn); for (unsigned j = 0; j < tst_fn.m_literals.size(); ++j) { std::cout << tst_fn.m_literals[j] << " "; } std::cout << "\n"; } } void tst_diff_logic() { //tst1(); //tst2(); //tst3(); } #else void tst_diff_logic() { } #endif z3-z3-4.8.7/src/test/dl_context.cpp000066400000000000000000000057311356505360400170520ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "muz/fp/datalog_parser.h" #include "ast/ast_pp.h" #include "ast/arith_decl_plugin.h" #include "muz/base/dl_context.h" #include "smt/params/smt_params.h" #include "muz/fp/dl_register_engine.h" using namespace datalog; void tst_dl_context() { return; #if 0 symbol relations[] = { symbol("tr_skip"), symbol("tr_sparse"), symbol("tr_hashtable"), symbol("smt_relation2") }; const unsigned rel_cnt = sizeof(relations)/sizeof(symbol); const char * test_file = "c:\\tvm\\src\\benchmarks\\datalog\\t0.datalog"; params_ref params; for(unsigned rel_index=0; rel_index=0; eager_checking--) { params.set_bool("eager_emptiness_checking", eager_checking!=0); std::cerr << "Testing " << relations[rel_index] << "\n"; std::cerr << "Eager emptiness checking " << (eager_checking!=0 ? "on" : "off") << "\n"; dl_context_simple_query_test(params); dl_context_saturate_file(params, test_file); } } #endif } #if 0 static lbool dl_context_eval_unary_predicate(ast_manager & m, context & ctx, char const* problem_text, const char * pred_name) { parser* p = parser::create(ctx,m); TRUSTME( p->parse_string(problem_text) ); dealloc(p); func_decl * pred = ctx.try_get_predicate_decl(symbol(pred_name)); ENSURE(pred); ENSURE(pred->get_arity()==1); app_ref query_app(m.mk_app(pred, m.mk_var(0, pred->get_domain()[0])), m); lbool status = ctx.query(query_app); ENSURE(status != l_undef); return status; } static void dl_context_simple_query_test(params_ref & params) { ast_manager m; dl_decl_util decl_util(m); register_engine re; smt_params fparams; context ctx(m, re, fparams); ctx.updt_params(params); /* lbool status = */ dl_context_eval_unary_predicate(m, ctx, "Z 64\n\nP(x:Z)\nP(\"a\").", "P"); #if 0 // TBD: //zero corresponds to the first constant the datalog parser encountered, in our case "a" app_ref c_0(decl_util.mk_constant(0, res1->get_signature()[0]), m); app_ref c_1(decl_util.mk_constant(1, res1->get_signature()[0]), m); relation_fact f(m); f.push_back(c_0); ENSURE(res1->contains_fact(f)); f[0]=c_1; ENSURE(!res1->contains_fact(f)); #endif } void dl_context_saturate_file(params_ref & params, const char * f) { ast_manager m; dl_decl_util decl_util(m); smt_params fparams; register_engine re; context ctx(m, re, fparams); ctx.updt_params(params); datalog::parser * parser = datalog::parser::create(ctx, m); if (!parser || !parser->parse_file(f)) { warning_msg("ERROR: failed to parse file"); dealloc(parser); return; } dealloc(parser); std::cerr << "Saturating...\n"; ctx.get_rel_context()->saturate(); std::cerr << "Done\n"; } #endif z3-z3-4.8.7/src/test/dl_product_relation.cpp000066400000000000000000000271771356505360400207530ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #ifdef _WINDOWS #include "muz/base/dl_context.h" #include "muz/fp/dl_register_engine.h" #include "muz/rel/dl_finite_product_relation.h" #include "muz/rel/dl_sparse_table.h" #include "muz/rel/rel_context.h" namespace datalog { typedef scoped_rel table_aptr; class collector_of_reduced : public table_row_pair_reduce_fn { idx_set & m_acc; public: collector_of_reduced(idx_set & accumulator) : m_acc(accumulator) {} virtual void operator()(table_element * func_columns, const table_element * merged_func_columns) { m_acc.insert(static_cast(merged_func_columns[0])); } }; void test_functional_columns(smt_params fparams, params_ref& params) { ast_manager m; register_engine re; context ctx(m, re, fparams); rel_context_base& rctx = *ctx.get_rel_context(); ctx.updt_params(params); relation_manager & rmgr(rctx.get_rmanager()); sparse_table_plugin & plugin = static_cast(*rctx.get_rmanager().get_table_plugin(symbol("sparse"))); ENSURE(&plugin); table_signature sig2; sig2.push_back(2); sig2.push_back(2); sig2.set_functional_columns(1); ENSURE(plugin.can_handle_signature(sig2)); table_fact f00; f00.push_back(0); f00.push_back(0); table_fact f01; f01.push_back(0); f01.push_back(1); table_fact f11; f11.push_back(1); f11.push_back(1); { table_aptr t0 = plugin.mk_empty(sig2); ENSURE(t0->empty()); t0->add_fact(f00); ENSURE(!t0->empty()); ENSURE(t0->get_size_estimate_rows()==1); t0->add_fact(f01); ENSURE(t0->get_size_estimate_rows()==1); t0->add_fact(f11); ENSURE(t0->get_size_estimate_rows()==2); unsigned rem_cols0[]={0}; scoped_ptr project0 = rmgr.mk_project_fn(*t0, 1, rem_cols0); table_aptr t1 = (*project0)(*t0); ENSURE(t1->get_size_estimate_rows()==2); ENSURE(t1->get_signature().functional_columns()==0); //project on non-functional column cancels functional unsigned rem_cols1[]={1}; scoped_ptr project1 = rmgr.mk_project_fn(*t0, 1, rem_cols1); table_aptr t2 = (*project1)(*t0); ENSURE(t2->get_size_estimate_rows()==2); idx_set acc; collector_of_reduced * reducer = alloc(collector_of_reduced, acc); scoped_ptr rproject = rmgr.mk_project_with_reduce_fn(*t0, 1, rem_cols0, reducer); table_aptr rt = (*rproject)(*t0); ENSURE(acc.num_elems()==1); ENSURE(rt->get_size_estimate_rows()==1); } { table_aptr t0 = plugin.mk_empty(sig2); t0->add_fact(f01); unsigned join_cols[]={1}; scoped_ptr join0 = rmgr.mk_join_fn(*t0, *t0, 1, join_cols, join_cols); table_aptr t1 = (*join0)(*t0, *t0); ENSURE(t1->get_signature().size()==4); ENSURE(t1->get_signature().functional_columns()==2); table_fact f0011; f0011.push_back(0); f0011.push_back(0); f0011.push_back(1); f0011.push_back(1); ENSURE(t1->contains_fact(f0011)); table_fact f0111 = f0011; f0111[1] = 1; ENSURE(!t1->contains_fact(f0111)); } { table_aptr t0 = plugin.mk_empty(sig2); t0->display(std::cout<<"0:"); ENSURE(t0->get_signature().functional_columns()==1); table_fact aux_fact; aux_fact = f01; TRUSTME( t0->suggest_fact(aux_fact) ); t0->display(std::cout<<"1:"); ENSURE(t0->contains_fact(f01)); ENSURE(aux_fact[1]==1); aux_fact = f00; TRUSTME( !t0->suggest_fact(aux_fact) ); t0->display(std::cout<<"2:"); ENSURE(t0->contains_fact(f01)); ENSURE(!t0->contains_fact(f00)); ENSURE(aux_fact[1]==1); t0->ensure_fact(f00); t0->display(std::cout<<"3:"); ENSURE(t0->contains_fact(f00)); ENSURE(!t0->contains_fact(f01)); } } void test_finite_product_relation(smt_params fparams, params_ref& params) { ast_manager m; register_engine re; context ctx(m, re, fparams); ctx.updt_params(params); dl_decl_util dl_util(m); relation_manager & rmgr = ctx.get_rel_context()->get_rmanager(); relation_plugin & rel_plugin = *rmgr.get_relation_plugin(params.get_sym("default_relation", symbol("sparse"))); ENSURE(&rel_plugin); finite_product_relation_plugin plg(rel_plugin, rmgr); sort_ref byte_srt_ref(dl_util.mk_sort(symbol("BYTE"), 256), m); relation_sort byte_srt = byte_srt_ref; relation_signature sig2; sig2.push_back(byte_srt); sig2.push_back(byte_srt); relation_signature sig3(sig2); sig3.push_back(byte_srt); relation_signature sig4(sig3); sig4.push_back(byte_srt); app_ref seven_ref(dl_util.mk_numeral(7, byte_srt), m); app_ref nine_ref(dl_util.mk_numeral(9, byte_srt), m); relation_element seven = seven_ref; relation_element nine = nine_ref; relation_fact f7(m); f7.push_back(seven); relation_fact f9(m); f9.push_back(nine); relation_fact f77(f7); f77.push_back(seven); relation_fact f79(f7); f79.push_back(nine); relation_fact f97(f9); f97.push_back(seven); relation_fact f99(f9); f99.push_back(nine); relation_fact f779(f77); f779.push_back(nine); relation_fact f799(f79); f799.push_back(nine); relation_fact f977(f97); f977.push_back(seven); relation_fact f7797(f779); f7797.push_back(seven); relation_fact f7997(f799); f7997.push_back(seven); bool table_cols2[] = { true, false }; bool table_cols3[] = { true, false, false }; bool table_cols4[] = { true, true, false, false }; scoped_rel r1 = plg.mk_empty(sig2, table_cols2); scoped_rel r2 = r1->clone(); scoped_rel r3 = r2->clone(); ENSURE(!r1->contains_fact(f77)); r1->add_fact(f77); ENSURE(r1->contains_fact(f77)); r2->add_fact(f79); r3->add_fact(f99); r2->display( std::cout << "r2 0\n"); scoped_rel r4 = r2->clone(); r2->display( std::cout << "r2 1\n"); r4->display( std::cout << "r4 0\n"); ENSURE(!r4->contains_fact(f77)); ENSURE(r4->contains_fact(f79)); r4->add_fact(f77); r4->display( std::cout << "r4 1\n"); ENSURE(r4->contains_fact(f77)); ENSURE(r4->contains_fact(f79)); r4->add_fact(f99); r4->display( std::cout << "r4 2\n"); ENSURE(r4->contains_fact(f99)); std::cout << "------ testing union ------\n"; r2->display( std::cout << "r2\n"); scoped_ptr union_op = rmgr.mk_union_fn(*r1, *r2, r3.get()); ENSURE(union_op); (*union_op)(*r1, *r2, r3.get()); r1->display( std::cout << "r1\n"); r2->display( std::cout << "r2\n"); r3->display( std::cout << "r3\n"); ENSURE(r1->contains_fact(f77)); ENSURE(r1->contains_fact(f79)); ENSURE(!r1->contains_fact(f99)); ENSURE(!r3->contains_fact(f77)); ENSURE(r3->contains_fact(f79)); ENSURE(r3->contains_fact(f99)); std::cout << "------ testing join ------\n"; r1->reset(); r1->add_fact(f77); r1->add_fact(f79); r1->add_fact(f97); r2->reset(); r2->add_fact(f97); r2->add_fact(f99); unsigned col0[] = { 0 }; unsigned col1[] = { 1 }; scoped_ptr join_tt = rmgr.mk_join_fn(*r1, *r2, 1, col0, col0); scoped_ptr join_tr = rmgr.mk_join_fn(*r1, *r2, 1, col0, col1); scoped_ptr join_rr = rmgr.mk_join_fn(*r1, *r2, 1, col1, col1); r1->display( std::cout << "r1\n"); r2->display( std::cout << "r2\n"); scoped_rel jr_tt = (*join_tt)(*r1, *r2); scoped_rel jr_tr = (*join_tr)(*r1, *r2); scoped_rel jr_rr = (*join_rr)(*r1, *r2); jr_tt->display( std::cout << "tt\n"); jr_tr->display( std::cout << "tr\n"); jr_rr->display( std::cout << "rr\n"); ENSURE(!jr_tt->contains_fact(f7797)); ENSURE(jr_tr->contains_fact(f7797)); ENSURE(jr_rr->contains_fact(f7797)); std::cout << "------ testing project ------\n"; scoped_rel r31 = plg.mk_empty(sig3, table_cols3); r31->add_fact(f779); r31->add_fact(f977); r31->add_fact(f799); unsigned rem_1_rel[] = { 1 }; unsigned rem_2_rel[] = { 1, 2 }; unsigned rem_1_table[] = { 0 }; scoped_ptr proj_1r = rmgr.mk_project_fn(*r31, 1, rem_1_rel); scoped_ptr proj_2r = rmgr.mk_project_fn(*r31, 2, rem_2_rel); scoped_ptr proj_1t = rmgr.mk_project_fn(*r31, 1, rem_1_table); scoped_rel sr_1r = (*proj_1r)(*r31); scoped_rel sr_2r = (*proj_2r)(*r31); scoped_rel sr_1t = (*proj_1t)(*r31); ENSURE(sr_1r->contains_fact(f79)); ENSURE(sr_1r->contains_fact(f97)); ENSURE(!sr_1r->contains_fact(f77)); ENSURE(sr_2r->contains_fact(f7)); ENSURE(sr_2r->contains_fact(f9)); ENSURE(sr_1t->contains_fact(f79)); ENSURE(!sr_1t->contains_fact(f97)); ENSURE(sr_1t->contains_fact(f77)); ENSURE(sr_1t->contains_fact(f99)); std::cout << "------ testing filter_interpreted ------\n"; scoped_rel r41 = plg.mk_empty(sig4, table_cols4); r41->add_fact(f7797); r41->add_fact(f7997); app_ref cond(m.mk_and( m.mk_not(m.mk_eq(m.mk_var(1,byte_srt), m.mk_var(2,byte_srt))), //#1!=#2 m.mk_not(m.mk_eq(m.mk_var(3,byte_srt), m.mk_var(2,byte_srt))) //#3!=#2 ), m); scoped_ptr i_filter = rmgr.mk_filter_interpreted_fn(*r41, cond); (*i_filter)(*r41); ENSURE(r41->contains_fact(f7797)); ENSURE(!r41->contains_fact(f7997)); std::cout << "------ testing filter_by_negation ------\n"; r31->reset(); r31->add_fact(f779); r31->add_fact(f977); r31->add_fact(f799); r1->reset(); r1->add_fact(f77); r1->add_fact(f79); unsigned nf_r31_cols[] = {1, 0, 1}; unsigned nf_r1_cols[] = {0, 0, 1}; scoped_ptr neg_filter = rmgr.mk_filter_by_negation_fn(*r31, *r1, 3, nf_r31_cols, nf_r1_cols); (*neg_filter)(*r31, *r1); ENSURE(!r31->contains_fact(f779)); ENSURE(r31->contains_fact(f977)); ENSURE(r31->contains_fact(f799)); } } using namespace datalog; void tst_dl_product_relation() { smt_params fparams; params_ref params; test_functional_columns(fparams, params); params.set_sym("default_relation", symbol("tr_sparse")); test_finite_product_relation(fparams, params); } #else void tst_dl_product_relation() { } #endif z3-z3-4.8.7/src/test/dl_query.cpp000066400000000000000000000226341356505360400165340ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "muz/fp/datalog_parser.h" #include "ast/ast_pp.h" #include "muz/rel/dl_table_relation.h" #include "muz/base/dl_context.h" #include "muz/fp/dl_register_engine.h" #include "smt/params/smt_params.h" #include "util/stopwatch.h" #include "ast/reg_decl_plugins.h" #include "muz/rel/dl_relation_manager.h" using namespace datalog; void dl_query_ask_ground_query(context & ctx, func_decl * pred, relation_fact & f, bool should_be_successful) { expr * const * q_args = reinterpret_cast(f.c_ptr()); app * query = ctx.get_manager().mk_app(pred, q_args); lbool is_sat = ctx.query(query); std::cerr << "@@ query should succeed: " << should_be_successful << "\n"; ENSURE(is_sat != l_undef); if((is_sat != l_true) == should_be_successful) { std::cerr<<"wrong ground query answer!\n"; UNREACHABLE(); } } void dl_query_ask_for_last_arg(context & ctx, func_decl * pred, relation_fact & f, bool should_be_successful) { ast_manager & m = ctx.get_manager(); expr_ref_vector query_args(m); push_into_vector(query_args, f); query_args.pop_back(); query_args.push_back(m.mk_var(0, pred->get_domain(query_args.size()))); app * query = ctx.get_manager().mk_app(pred, query_args.c_ptr()); lbool is_sat = ctx.query(query); std::cerr << "@@ last arg query should succeed: " << should_be_successful << "\n"; VERIFY(is_sat != l_undef); relation_fact res_fact(m); res_fact.push_back(f.back()); if(ctx.result_contains_fact(res_fact)!=should_be_successful) { std::cerr<<"wrong arg query answer!\n"; UNREACHABLE(); } } void dl_query_test(ast_manager & m, smt_params & fparams, params_ref& params, context & ctx_b, char const* problem_file, unsigned test_count, bool use_magic_sets) { dl_decl_util decl_util(m); random_gen ran(0); register_engine re; context ctx_q(m, re, fparams); params.set_bool("magic_sets_for_queries", use_magic_sets); ctx_q.updt_params(params); { parser* p = parser::create(ctx_q,m); bool ok = p && p->parse_file(problem_file); dealloc(p); if (!ok) { std::cout << "Could not parse: " << problem_file << "\n"; return; } } relation_manager & rel_mgr_q = ctx_b.get_rel_context()->get_rmanager(); decl_set out_preds = ctx_b.get_rules().get_output_predicates(); decl_set::iterator it = out_preds.begin(); decl_set::iterator end = out_preds.end(); for(; it!=end; ++it) { func_decl * pred_b = *it; std::cerr << "Checking queries on relation " << pred_b->get_name() << "\n"; func_decl * pred_q = ctx_q.try_get_predicate_decl(symbol(pred_b->get_name().bare_str())); ENSURE(pred_q); relation_base & rel_b = ctx_b.get_rel_context()->get_relation(pred_b); relation_signature sig_b = rel_b.get_signature(); relation_signature sig_q = ctx_q.get_rel_context()->get_relation(pred_q).get_signature(); ENSURE(sig_b.size()==sig_q.size()); std::cerr << "Queries on random facts...\n"; relation_fact f_b(m); relation_fact f_q(m); for(unsigned attempt=0; attempt(rel_b); table_base & table_b = tr_b.get_table(); table_fact tf; unsigned table_sz = table_b.get_size_estimate_rows(); table_base::iterator fit = table_b.begin(); table_base::iterator fend = table_b.end(); for(; fit!=fend; ++fit) { if(ran()%std::max(1u,table_sz/test_count)!=0) { continue; } fit->get_fact(tf); rel_mgr_q.table_fact_to_relation(sig_q, tf, f_q); dl_query_ask_ground_query(ctx_q, pred_q, f_q, true); dl_query_ask_for_last_arg(ctx_q, pred_q, f_q, true); } std::cerr << "Done.\n"; } } void dl_query_test_wpa(smt_params & fparams, params_ref& params) { params.set_bool("magic_sets_for_queries", true); ast_manager m; random_gen ran(0); reg_decl_plugins(m); arith_util arith(m); const char * problem_dir = "C:\\tvm\\src\\z3_2\\debug\\test\\w0.datalog"; dl_decl_util dl_util(m); std::cerr << "Testing queries on " << problem_dir <<"\n"; register_engine re; context ctx(m, re, fparams); ctx.updt_params(params); { wpa_parser* p = wpa_parser::create(ctx, m); bool ok = p->parse_directory(problem_dir); dealloc(p); if (!ok) { std::cout << "Could not parse: " << problem_dir << "\n"; return; } } const unsigned attempts = 10; func_decl * v_pred = ctx.try_get_predicate_decl(symbol("V")); ENSURE(v_pred); sort * var_sort = v_pred->get_domain(0); uint64_t var_sz; TRUSTME( ctx.try_get_sort_constant_count(var_sort, var_sz) ); for(unsigned attempt=0; attemptparse_file(problem_file); dealloc(p); if (!ok) { std::cout << "Could not parse: " << problem_file << "\n"; return; } } ctx_base.get_rel_context()->saturate(); for(unsigned use_restarts=0; use_restarts<=1; use_restarts++) { params.set_uint("initial_restart_timeout", use_restarts ? 100 : 0); for(unsigned use_similar=0; use_similar<=1; use_similar++) { params.set_uint("similarity_compressor", use_similar != 0); for(unsigned use_magic_sets=0; use_magic_sets<=1; use_magic_sets++) { stopwatch watch; if (!(use_restarts == 1 && use_similar == 0 && use_magic_sets == 1)) { continue; } watch.start(); std::cerr << "------- " << (use_restarts ? "With" : "Without") << " restarts -------\n"; std::cerr << "------- " << (use_similar ? "With" : "Without") << " similar compressor -------\n"; std::cerr << "------- " << (use_magic_sets ? "With" : "Without") << " magic sets -------\n"; dl_query_test(m, fparams, params, ctx_base, problem_file, 1, use_magic_sets!=0); watch.stop(); std::cout << (use_restarts ? "With" : "Without") << " restarts\n"; std::cout << (use_similar ? "With" : "Without") << " similar compressor\n"; std::cout << (use_magic_sets ? "With" : "Without") << " magic sets\n"; std::cout << "Time: " << watch.get_current_seconds() << "\n\n"; std::cerr << "Time: " << watch.get_current_seconds() << "\n"; } } } } z3-z3-4.8.7/src/test/dl_relation.cpp000066400000000000000000000264421356505360400172050ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #ifdef _WINDOWS #include "muz/base/dl_context.h" #include "muz/fp/dl_register_engine.h" #include "muz/rel/dl_relation_manager.h" #include "muz/rel/dl_interval_relation.h" #include "muz/rel/dl_bound_relation.h" #include "muz/rel/dl_product_relation.h" #include "util/util.h" namespace datalog { static void test_interval_relation() { smt_params params; ast_manager ast_m; register_engine re; context ctx(ast_m, re, params); arith_util autil(ast_m); relation_manager & m = ctx.get_rel_context()->get_rmanager(); m.register_plugin(alloc(interval_relation_plugin, m)); interval_relation_plugin& ip = dynamic_cast(*m.get_relation_plugin(symbol("interval_relation"))); ENSURE(&ip); relation_signature sig; sort* int_sort = autil.mk_int(); sig.push_back(int_sort); sig.push_back(int_sort); sig.push_back(int_sort); sig.push_back(int_sort); interval_relation& i1 = dynamic_cast(*ip.mk_empty(sig)); interval_relation& i2 = dynamic_cast(*ip.mk_full(0, sig)); i1.display(std::cout); i2.display(std::cout); ENSURE(i1.empty()); ENSURE(!i2.empty()); app_ref cond1(ast_m), cond2(ast_m), cond3(ast_m); app_ref cond4(ast_m), cond5(ast_m), cond6(ast_m); app_ref num1(ast_m); cond1 = autil.mk_le(ast_m.mk_var(0, int_sort), autil.mk_numeral(rational(0), true)); cond2 = autil.mk_le(ast_m.mk_var(1, int_sort), autil.mk_numeral(rational(1), true)); cond3 = autil.mk_le(ast_m.mk_var(2, int_sort), autil.mk_numeral(rational(2), true)); cond4 = autil.mk_ge(ast_m.mk_var(0, int_sort), autil.mk_numeral(rational(0), true)); cond5 = autil.mk_ge(ast_m.mk_var(1, int_sort), autil.mk_numeral(rational(0), true)); cond6 = autil.mk_ge(ast_m.mk_var(2, int_sort), autil.mk_numeral(rational(5), true)); num1 = autil.mk_numeral(rational(4), true); i2.filter_interpreted(cond1); i2.display(std::cout); // x0 <= 0 unsigned cols1[2] = { 1, 2}; unsigned cols2[2] = { 2, 3}; relation_join_fn* join1 = ip.mk_join_fn(i1, i2, 2, cols1, cols2); relation_transformer_fn* proj1 = ip.mk_project_fn(i1, 2, cols2); relation_transformer_fn* ren1 = ip.mk_rename_fn(i1, 2, cols2); relation_union_fn* union1 = ip.mk_union_fn(i1, i2, &i1); relation_mutator_fn* filterId1 = ip.mk_filter_identical_fn(i1, 2, cols1); relation_mutator_fn* filterEq1 = ip.mk_filter_equal_fn(i1, num1, 2); relation_mutator_fn* filterCond1 = ip.mk_filter_interpreted_fn(i1, cond2); relation_base* i3 = (*join1)(i2, i2); i3->display(std::cout); relation_transformer_fn* proj2 = ip.mk_project_fn(*i3, 2, cols2); (*filterEq1)(i2); i2.display(std::cout); // x0 <= 0 // x2 = 4 (*filterId1)(i2); i2.display(std::cout); // x0 <= 0 // x1 = x2 = 4 relation_fact fact1(ast_m); fact1.push_back(autil.mk_numeral(rational(0), true)); fact1.push_back(autil.mk_numeral(rational(4), true)); fact1.push_back(autil.mk_numeral(rational(4), true)); fact1.push_back(autil.mk_numeral(rational(5), true)); ENSURE(i2.contains_fact(fact1)); fact1[0] = autil.mk_numeral(rational(-1), true); ENSURE(i2.contains_fact(fact1)); fact1[0] = autil.mk_numeral(rational(1), true); ENSURE(!i2.contains_fact(fact1)); relation_base* i5 = (*ren1)(i2); i2.display(std::cout << "Orig\n"); i5->display(std::cout << "renamed 2 |-> 3 |-> 2\n"); (*filterCond1)(i2); i2.display(std::cout); // empty ENSURE(i2.empty()); relation_base* i4 = (*proj2)(*i3); i4->display(std::cout); i1.deallocate(); i2.deallocate(); i3->deallocate(); i4->deallocate(); i5->deallocate(); dealloc(join1); dealloc(proj1); dealloc(ren1); dealloc(union1); dealloc(filterId1); dealloc(filterEq1); dealloc(filterCond1); } static void test_bound_relation() { std::cout << "bound relation\n"; smt_params params; ast_manager ast_m; register_engine re; context ctx(ast_m, re, params); arith_util autil(ast_m); relation_manager & m = ctx.get_rel_context()->get_rmanager(); m.register_plugin(alloc(bound_relation_plugin, m)); bound_relation_plugin& br = dynamic_cast(*m.get_relation_plugin(symbol("bound_relation"))); ENSURE(&br); relation_signature sig; sort* int_sort = autil.mk_int(); sig.push_back(int_sort); sig.push_back(int_sort); sig.push_back(int_sort); sig.push_back(int_sort); bound_relation& i1 = dynamic_cast(*br.mk_empty(sig)); bound_relation& i2 = dynamic_cast(*br.mk_full(0, sig)); i1.display(std::cout << "empty:\n"); i2.display(std::cout << "full:\n"); ENSURE(i1.empty()); ENSURE(!i2.empty()); app_ref cond1(ast_m), cond2(ast_m), cond3(ast_m); app_ref cond4(ast_m), cond5(ast_m), cond6(ast_m); app_ref num1(ast_m); cond1 = autil.mk_lt(ast_m.mk_var(0, int_sort), autil.mk_numeral(rational(0), true)); cond2 = autil.mk_lt(ast_m.mk_var(1, int_sort), autil.mk_numeral(rational(1), true)); cond3 = autil.mk_lt(ast_m.mk_var(2, int_sort), ast_m.mk_var(3, int_sort)); cond4 = autil.mk_ge(ast_m.mk_var(0, int_sort), autil.mk_numeral(rational(0), true)); cond5 = autil.mk_ge(ast_m.mk_var(1, int_sort), autil.mk_numeral(rational(0), true)); cond6 = autil.mk_ge(ast_m.mk_var(2, int_sort), autil.mk_numeral(rational(5), true)); app_ref lt_x0x1(ast_m), lt_x1x2(ast_m), lt_x0x3(ast_m), lt_x0x2(ast_m); lt_x0x1 = autil.mk_lt(ast_m.mk_var(0, int_sort), ast_m.mk_var(1, int_sort)); lt_x1x2 = autil.mk_lt(ast_m.mk_var(1, int_sort), ast_m.mk_var(2, int_sort)); lt_x0x2 = autil.mk_lt(ast_m.mk_var(0, int_sort), ast_m.mk_var(2, int_sort)); lt_x0x3 = autil.mk_lt(ast_m.mk_var(0, int_sort), ast_m.mk_var(3, int_sort)); num1 = autil.mk_numeral(rational(4), true); unsigned cols1[2] = { 1, 2}; unsigned cols2[2] = { 2, 3}; unsigned cols3[3] = { 0, 2, 3 }; relation_join_fn* join1 = br.mk_join_fn(i1, i2, 2, cols1, cols2); relation_transformer_fn* proj1 = br.mk_project_fn(i1, 2, cols2); relation_transformer_fn* ren1 = br.mk_rename_fn(i1, 3, cols3); relation_union_fn* union1 = br.mk_union_fn(i1, i2, &i1); relation_mutator_fn* filterId1 = br.mk_filter_identical_fn(i1, 2, cols1); relation_mutator_fn* filterEq1 = br.mk_filter_equal_fn(i1, num1, 2); relation_mutator_fn* filterCond1 = br.mk_filter_interpreted_fn(i1, cond3); relation_base* i3 = (*join1)(i2, i2); i3->display(std::cout); relation_transformer_fn* proj2 = br.mk_project_fn(*i3, 2, cols2); (*filterEq1)(i2); i2.display(std::cout << "no-op still full\n"); // no-op (*filterCond1)(i2); i2.display(std::cout << "x2 < x3\n"); // x2 < x3 (*filterId1)(i2); i2.display(std::cout << "id\n"); // x1 = x2 < x3 relation_fact fact1(ast_m); i2.display(std::cout << "Orig\n"); std::cout << "renamed "; for (unsigned i = 0; i < 3; ++i) { std::cout << cols3[i] << " "; } std::cout << "\n"; relation_base* i5 = (*ren1)(i2); i5->display(std::cout); //ENSURE(i2.empty()); relation_base* i4 = (*proj2)(*i3); i4->display(std::cout); // test that equivalence classes are expanded. // { x1 = x3, x0 < x1 x1 < x2} u { x2 = x3, x0 < x3 } = { x0 < x3 } { relation_base* b1 = br.mk_full(0, sig); relation_base* b2 = br.mk_full(0, sig); unsigned x1x3[2] = { 1, 3 }; unsigned x2x3[2] = { 2, 3 }; scoped_ptr id1 = br.mk_filter_identical_fn(*b1, 2, x1x3); scoped_ptr ltx0x1 = br.mk_filter_interpreted_fn(*b1, lt_x0x1); scoped_ptr ltx1x2 = br.mk_filter_interpreted_fn(*b1, lt_x1x2); scoped_ptr ltx0x3 = br.mk_filter_interpreted_fn(*b2, lt_x0x3); scoped_ptr id2 = br.mk_filter_identical_fn(*b2, 2, x2x3); (*id1)(*b1); (*ltx0x1)(*b1); (*ltx1x2)(*b1); b2->display(std::cout << "b2:\n"); (*id2)(*b2); b2->display(std::cout << "b2:\n"); (*ltx0x3)(*b2); b2->display(std::cout << "b2:\n"); scoped_ptr u = br.mk_union_fn(*b1, *b2, 0); b1->display(std::cout << "b1:\n"); b2->display(std::cout << "b2:\n"); (*u)(*b1, *b2, 0); b1->display(std::cout << "b1 u b2:\n"); // TBD check property; b1->deallocate(); b2->deallocate(); } // test that equivalence classes are expanded. // { x1 = x2 = x3, x0 < x1} u { x1 = x3, x0 < x3, x0 < x2 } = { x0 < x2, x0 < x3 } { relation_base* b1 = br.mk_full(0, sig); relation_base* b2 = br.mk_full(0, sig); unsigned x0x3[2] = { 0, 3 }; unsigned x1x3[2] = { 1, 3 }; unsigned x2x3[2] = { 2, 3 }; scoped_ptr id1 = br.mk_filter_identical_fn(*b1, 2, x1x3); scoped_ptr id2 = br.mk_filter_identical_fn(*b1, 2, x2x3); scoped_ptr ltx0x1 = br.mk_filter_interpreted_fn(*b1, lt_x0x1); scoped_ptr ltx0x2 = br.mk_filter_interpreted_fn(*b2, lt_x0x2); scoped_ptr ltx0x3 = br.mk_filter_interpreted_fn(*b2, lt_x0x3); scoped_ptr id3 = br.mk_filter_identical_fn(*b2, 2, x1x3); (*id1)(*b1); (*id2)(*b1); (*ltx0x1)(*b1); (*id3)(*b2); (*ltx0x2)(*b2); (*ltx0x3)(*b2); scoped_ptr u = br.mk_union_fn(*b1, *b2, 0); b1->display(std::cout << "b1:\n"); b2->display(std::cout << "b2:\n"); (*u)(*b1, *b2, 0); b1->display(std::cout << "b1 u b2:\n"); // TBD check property; b1->deallocate(); b2->deallocate(); } i1.deallocate(); i2.deallocate(); i3->deallocate(); i4->deallocate(); i5->deallocate(); dealloc(join1); dealloc(proj1); dealloc(ren1); dealloc(union1); dealloc(filterId1); dealloc(filterEq1); dealloc(filterCond1); } } void tst_dl_relation() { datalog::test_interval_relation(); datalog::test_bound_relation(); } #else void tst_dl_relation() { } #endif z3-z3-4.8.7/src/test/dl_table.cpp000066400000000000000000000052231356505360400164510ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "muz/base/dl_context.h" #include "muz/rel/dl_table.h" #include "muz/fp/dl_register_engine.h" #include "muz/rel/dl_relation_manager.h" typedef datalog::table_base* (*mk_table_fn)(datalog::relation_manager& m, datalog::table_signature& sig); static datalog::table_base* mk_bv_table(datalog::relation_manager& m, datalog::table_signature& sig) { datalog::table_plugin * p = m.get_table_plugin(symbol("bitvector")); ENSURE(p); return p->mk_empty(sig); } static void test_table(mk_table_fn mk_table) { datalog::table_signature sig; sig.push_back(2); sig.push_back(4); sig.push_back(8); sig.push_back(4); smt_params params; ast_manager ast_m; datalog::register_engine re; datalog::context ctx(ast_m, re, params); datalog::relation_manager & m = ctx.get_rel_context()->get_rmanager(); m.register_plugin(alloc(datalog::bitvector_table_plugin, m)); datalog::table_base* _tbl = mk_table(m, sig); datalog::table_base& table = *_tbl; datalog::table_fact row, row1, row2, row3; row.push_back(1); row.push_back(3); row.push_back(7); row.push_back(2); row1 = row; row[3] = 3; row2 = row; row[0] = 0; row[3] = 1; row3 = row; table.add_fact(row1); table.add_fact(row2); table.display(std::cout); datalog::table_base::iterator it = table.begin(); datalog::table_base::iterator end = table.end(); for (; it != end; ++it) { it->get_fact(row); for (unsigned j = 0; j < row.size(); ++j) { std::cout << row[j] << " "; } std::cout << "\n"; } ENSURE(table.contains_fact(row1)); ENSURE(table.contains_fact(row2)); ENSURE(!table.contains_fact(row3)); #if 0 table.remove_facts(1, &row1); ENSURE(!table.contains_fact(row1)); #endif table.add_fact(row1); datalog::table_base* _tbl2 = mk_table(m, sig); datalog::table_base& table2 = *_tbl2; table2.add_fact(row2); table2.add_fact(row3); unsigned cols1[1] = { 1 }; unsigned cols2[1] = { 3 }; datalog::table_join_fn * j1 = m.mk_join_fn(table2, table, 1, cols1, cols2); datalog::table_base* _tbl3 = (*j1)(table2,table); _tbl3->display(std::cout); datalog::table_join_fn * j2 = m.mk_join_fn(table2, table, 1, cols1, cols1); datalog::table_base* _tbl4 = (*j2)(table2,table); _tbl4->display(std::cout); dealloc(j1); dealloc(j2); _tbl->deallocate(); (_tbl2->deallocate()); (_tbl3->deallocate()); (_tbl4->deallocate()); } void test_dl_bitvector_table() { test_table(mk_bv_table); } void tst_dl_table() { test_dl_bitvector_table(); } z3-z3-4.8.7/src/test/dl_util.cpp000066400000000000000000000024151356505360400163370ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "muz/base/dl_util.h" using namespace datalog; void dl_util_two_array_sort() { const unsigned num = 97; unsigned a1[num]; unsigned a2[num]; for(unsigned i=0; i result; // VERIFY(!m.intersect(*d1,*d0, result)); // m.subtract(*d1,*d0, result); ENSURE(result.empty()); dX = m.allocateX(); m.display(std::cout, *d0) << "\n"; m.display(std::cout, *dX) << "\n"; ENSURE(m.contains(*dX,*d1)); ENSURE(m.contains(*dX,*d0)); ENSURE(!m.contains(*d0,*d1)); ENSURE(!m.contains(*d1,*d0)); d1->neg().push_back(m.tbvm().allocate0()); m.display(std::cout, *d1) << " -> "; VERIFY(m.fold_neg(*d1)); m.display(std::cout, *d1) << "\n"; bit_vector to_delete; to_delete.resize(n, false); to_delete.set(1); to_delete.set(3); doc_manager m1(n-2); doc_ref d1_1(m1, m.project(m1, to_delete, *d1)); doc_ref d1_2(m1, m1.allocate1()); m.display(std::cout, *d1) << " -> "; m1.display(std::cout, *d1_1) << "\n"; ENSURE(m1.equals(*d1_1,*d1_2)); m.set(*d1,2,BIT_x); m.set(*d1,4,BIT_x); d1_1 = m.project(m1, to_delete, *d1); m.display(std::cout, *d1) << " -> "; m1.display(std::cout, *d1_1) << "\n"; d1->neg().push_back(m.tbvm().allocate1()); ENSURE(m.well_formed(*d1)); d1_1 = m.project(m1, to_delete, *d1); m.display(std::cout, *d1) << " -> "; m1.display(std::cout, *d1_1) << "\n"; } // generate "all" clauses over num_vars // create XXXX \ clauses // project 0, 1, 2, 3 variables // check that result is the same as QE over those clauses. class test_doc_cls { random_gen m_ran; ast_manager m; doc_manager dm; expr_ref_vector m_vars; tbit choose_tbit() { switch (m_ran(3)) { case 0: return BIT_0; case 1: return BIT_1; default : return BIT_x; } } tbv* mk_rand_tbv() { tbv* result = dm.tbvm().allocate(); for (unsigned i = 0; i < dm.num_tbits(); ++i) { dm.tbvm().set(*result, i, choose_tbit()); } return result; } tbv* mk_rand_tbv(tbv const& pos) { tbv* result = dm.tbvm().allocate(); for (unsigned i = 0; i < dm.num_tbits(); ++i) { if (pos[i] == BIT_x) { dm.tbvm().set(*result, i, choose_tbit()); } else { dm.tbvm().set(*result, i, pos[i]); } } return result; } doc* mk_rand_doc(unsigned num_diff) { tbv_ref t(dm.tbvm()); t = mk_rand_tbv(); doc* result = dm.allocate(*t); ENSURE(dm.tbvm().equals(*t, result->pos())); for (unsigned i = 0; i < num_diff; ++i) { result->neg().push_back(mk_rand_tbv(result->pos())); } ENSURE(dm.well_formed(*result)); return result; } void mk_rand_udoc(unsigned num_elems, unsigned num_diff, udoc& result) { result.reset(dm); for (unsigned i = 0; i < num_elems; ++i) { result.push_back(mk_rand_doc(num_diff)); } } expr_ref mk_conj(tbv& t) { expr_ref result(m); expr_ref_vector conjs(m); for (unsigned i = 0; i < m_vars.size(); ++i) { tbit b = choose_tbit(); dm.tbvm().set(t, i, b); switch (b) { case BIT_1: conjs.push_back(m_vars[i].get()); break; case BIT_0: conjs.push_back(m.mk_not(m_vars[i].get())); break; default: break; } } result = mk_and(m, conjs.size(), conjs.c_ptr()); return result; } expr_ref to_formula(tbv const& t, doc_manager& m2) { expr_ref result(m); expr_ref_vector conjs(m); unsigned n = m2.num_tbits(); ENSURE(n <= m_vars.size()); for (unsigned i = 0; i < n; ++i) { switch (t[i]) { case BIT_x: break; case BIT_1: conjs.push_back(m_vars[i].get()); break; case BIT_0: conjs.push_back(m.mk_not(m_vars[i].get())); break; default: UNREACHABLE(); break; } } result = mk_and(m, conjs.size(), conjs.c_ptr()); return result; } expr_ref to_formula(doc const& d, doc_manager& m2) { expr_ref result(m); expr_ref_vector conjs(m); conjs.push_back(to_formula(d.pos(), m2)); for (unsigned i = 0; i < d.neg().size(); ++i) { conjs.push_back(m.mk_not(to_formula(d.neg()[i], m2))); } result = mk_and(m, conjs.size(), conjs.c_ptr()); return result; } expr_ref to_formula(udoc const& ud, doc_manager& m2) { expr_ref result(m); expr_ref_vector disjs(m); for (unsigned i = 0; i < ud.size(); ++i) { disjs.push_back(to_formula(ud[i], m2)); } result = mk_or(m, disjs.size(), disjs.c_ptr()); return result; } void project(doc const& d, doc_manager& m2, const bit_vector& to_delete, doc_ref& result) { result = dm.project(m2, to_delete, d); TRACE("doc", for (unsigned i = 0; i < m_vars.size(); ++i) { tout << (to_delete.get(i)?"0":"1"); } tout << " "; dm.display(tout, d) << " -> "; m2.display(tout, *result) << "\n"; ); } void test_project(unsigned num_clauses) { doc_ref d(dm); d = mk_rand_doc(3); expr_ref fml1(m), fml2(m), fml3(m), tmp1(m), tmp2(m), fml(m); fml1 = to_formula(*d, dm); bit_vector to_delete; to_delete.reserve(m_vars.size(), false); unsigned num_bits = 1; for (unsigned i = 1; i < to_delete.size(); ++i) { to_delete.set(i, m_ran(2) == 0); if (!to_delete.get(i)) ++num_bits; } doc_manager m2(num_bits); doc_ref result(m2); project(*d, m2, to_delete, result); TRACE("doc", dm.display(tout, *d) << "\n"; m2.display(tout, *result) << "\n";); fml2 = to_formula(*result, m2); project_expand(fml1, to_delete); project_rename(fml2, to_delete); check_equiv(fml1, fml2); } void project_expand(expr_ref& fml, bit_vector const& to_delete) { expr_ref tmp1(m), tmp2(m); for (unsigned i = 0; i < m_vars.size(); ++i) { if (to_delete.get(i)) { expr_safe_replace rep1(m), rep2(m); rep1.insert(m_vars[i].get(), m.mk_true()); rep1(fml, tmp1); rep2.insert(m_vars[i].get(), m.mk_false()); rep2(fml, tmp2); if (tmp1 == tmp2) { fml = tmp1; } else { fml = m.mk_or(tmp1, tmp2); } } } } void project_rename(expr_ref& fml, bit_vector const& to_delete) { expr_safe_replace rep(m); for (unsigned i = 0, j = 0; i < m_vars.size(); ++i) { if (!to_delete.get(i)) { rep.insert(m_vars[j].get(), m_vars[i].get()); ++j; } } rep(fml); } void test_merge(unsigned num_clauses) { doc_ref d(dm, dm.allocateX()); expr_ref_vector fmls(m), eqs(m); unsigned N = m_vars.size(); expr_ref fml1(m), fml2(m), fml3(m), tmp1(m), tmp2(m), fml(m); for (unsigned i = 0; i < num_clauses; ++i) { tbv* t = dm.tbvm().allocate(); fmls.push_back(m.mk_not(mk_conj(*t))); d->neg().push_back(t); } fml1 = mk_and(m, fmls.size(), fmls.c_ptr()); svector to_merge(N, false); bit_vector discard_cols; discard_cols.resize(N, false); unsigned num_bits = 1; union_find_default_ctx union_ctx; subset_ints equalities(union_ctx); unsigned lo = N; equalities.mk_var(); for (unsigned i = 1; i < N; ++i) { to_merge[i] = (m_ran(2) == 0); if (!to_merge[i]) ++num_bits; else lo = i; equalities.mk_var(); } if (lo == N) return; for (unsigned i = 0; i < N; ++i) { if (to_merge[i] && i != lo) { equalities.merge(i, lo); eqs.push_back(m.mk_eq(m_vars[i].get(), m_vars[lo].get())); } } eqs.push_back(to_formula(*d, dm)); fml1 = mk_and(m, eqs.size(), eqs.c_ptr()); if (dm.merge(*d, lo, 1, equalities, discard_cols)) { fml2 = to_formula(*d, dm); } else { fml2 = m.mk_false(); } check_equiv(fml1, fml2); } void check_equiv(expr_ref& fml1, expr_ref& fml2) { th_rewriter rw(m); rw(fml1); rw(fml2); smt_params fp; smt::kernel solver(m, fp); expr_ref fml(m); fml = m.mk_not(m.mk_eq(fml1, fml2)); solver.assert_expr(fml); lbool res = solver.check(); if (res != l_false) { TRACE("doc", tout << mk_pp(fml1, m) << "\n"; tout << mk_pp(fml2, m) << "\n"; ); } ENSURE(res == l_false); } public: test_doc_cls(unsigned num_vars): dm(num_vars), m_vars(m) { reg_decl_plugins(m); for (unsigned i = 0; i < num_vars; ++i) { m_vars.push_back(m.mk_fresh_const("b", m.mk_bool_sort())); } } void test_project(unsigned num_rounds, unsigned num_clauses) { for (unsigned i = 0; i < num_rounds; ++i) { test_project(num_clauses); } } void test_merge(unsigned num_rounds, unsigned num_clauses) { for (unsigned i = 0; i < num_rounds; ++i) { test_merge(num_clauses); } } void test_project1() { expr_ref fml1(m), fml2(m); doc_ref d(dm, dm.allocateX()); tbv_ref t(dm.tbvm(), dm.tbvm().allocateX()); dm.tbvm().set(*t, 0, BIT_0); d->neg().push_back(t.detach()); unsigned num_bits = dm.num_tbits(); bit_vector to_delete; to_delete.reserve(num_bits, false); fml1 = to_formula(*d, dm); to_delete.set(0, true); doc_manager m2(num_bits-1); doc_ref result(m2); project(*d, m2, to_delete, result); dm.display(std::cout, *d) << "\n"; m2.display(std::cout, *result) << "\n"; fml2 = to_formula(*result, m2); project_rename(fml2, to_delete); project_expand(fml1, to_delete); std::cout << fml1 << " " << fml2 << "\n"; check_equiv(fml1, fml2); } void test_subtract() { doc_ref d1(dm); doc_ref d2(dm); doc_ref d3(dm); udoc ds1, ds2; d1 = dm.allocateX(); d2 = dm.allocateX(); d3 = dm.allocateX(); dm.set(*d1, 0, BIT_1); dm.set(*d1, 1, BIT_0); dm.set(*d2, 0, BIT_0); dm.set(*d2, 1, BIT_1); //ds1.push_back(d1.detach()); ds1.push_back(d2.detach()); // ds1 = {10x, 01x} d1 = dm.allocateX(); tbv_ref t1(dm.tbvm()); tbv_ref t2(dm.tbvm()); t1 = dm.tbvm().allocateX(); t2 = dm.tbvm().allocateX(); dm.tbvm().set(*t1, 0, BIT_1); dm.tbvm().set(*t1, 2, BIT_0); dm.tbvm().set(*t2, 0, BIT_0); dm.tbvm().set(*t2, 2, BIT_1); d1->neg().push_back(t1.detach()); d1->neg().push_back(t2.detach()); ds2.push_back(d1.detach()); ds1.display(dm, std::cout) << "\n"; ds2.display(dm, std::cout) << "\n"; expr_ref fml1 = to_formula(ds1, dm); expr_ref fml2 = to_formula(ds2, dm); ds1.subtract(dm, ds2); ds1.display(dm, std::cout) << "\n"; expr_ref fml3 = to_formula(ds1, dm); fml1 = m.mk_and(fml1, m.mk_not(fml2)); check_equiv(fml1, fml3); ds1.reset(dm); ds2.reset(dm); //sub:{xxx \ {1x0, 0x1}} //result:{100} for (unsigned i = 0; i < 1000; ++i) { udoc d1, d2; mk_rand_udoc(3, 3, d1); mk_rand_udoc(3, 3, d2); fml1 = to_formula(d1, dm); fml2 = to_formula(d2, dm); d1.subtract(dm, d2); fml3 = to_formula(d1, dm); fml1 = m.mk_and(fml1, m.mk_not(fml2)); check_equiv(fml1, fml3); d1.reset(dm); d2.reset(dm); } } void test_intersect() { expr_ref fml1(m), fml2(m), fml3(m); for (unsigned i = 0; i < 10000; ++i) { udoc d1, d2; mk_rand_udoc(3, 3, d1); mk_rand_udoc(3, 3, d2); fml1 = to_formula(d1, dm); fml2 = to_formula(d2, dm); TRACE("doc", d1.display(dm, tout) << "\n"; d2.display(dm, tout) << "\n";); d1.intersect(dm, d2); TRACE("doc", d1.display(dm, tout) << "\n";); ENSURE(d1.well_formed(dm)); fml3 = to_formula(d1, dm); fml1 = m.mk_and(fml1, fml2); check_equiv(fml1, fml3); d1.reset(dm); d2.reset(dm); } } }; void tst_doc() { test_doc_cls tp(4); tp.test_project1(); tp.test_project(200,7); tp.test_intersect(); tp.test_subtract(); tp.test_merge(200,7); tst_doc1(5); tst_doc1(10); tst_doc1(70); } z3-z3-4.8.7/src/test/escaped.cpp000066400000000000000000000017661356505360400163170ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: escaped_str.cpp Abstract: Escaped strings Author: Leonardo de Moura (leonardo) 2011-03-01. Revision History: --*/ #include "util/util.h" void tst_escaped() { std::cout << "[" << escaped("\"hello\"\"world\"\n\n") << "]\n"; std::cout << "[" << escaped("\"hello\"\nworld\"\n\n\n", true) << "]\n"; std::cout << "[" << escaped("\"hello\"\nworld\"\n", true) << "]\n"; std::cout << "[" << escaped("\"hello\"\nworld\"", true) << "]\n"; std::cout << "[" << escaped("\"hello\"\n\"world\"\n\n") << "]\n"; std::cout << "[" << escaped("\n\n\n", true) << "]\n"; std::cout << "[" << escaped("\n\n\n") << "]\n"; std::cout << "[" << escaped("\n", true) << "]\n"; std::cout << "[" << escaped("\n") << "]\n"; std::cout << "[" << escaped("", true) << "]\n"; std::cout << "[" << escaped("") << "]\n"; std::cout << "[" << escaped(nullptr, true) << "]\n"; std::cout << "[" << escaped(nullptr) << "]\n"; } z3-z3-4.8.7/src/test/ex.cpp000066400000000000000000000020321356505360400153120ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: ex.cpp Abstract: Author: Leonardo de Moura (leonardo) 2011-04-28 Revision History: --*/ #include #include "util/z3_exception.h" class ex { public: virtual ~ex() {} virtual char const * msg() const = 0; }; class ex1 : public ex { char const * m_msg; public: ex1(char const * m):m_msg(m) {} char const * msg() const override { return m_msg; } }; class ex2 : public ex { std::string m_msg; public: ex2(char const * m):m_msg(m) {} char const * msg() const override { return m_msg.c_str(); } }; static void th() { throw ex2("testing exception"); } static void tst1() { try { th(); } catch (ex & e) { std::cerr << e.msg() << "\n"; } } static void tst2() { try { throw default_exception(default_exception::fmt(), "Format %d %s", 12, "twelve"); } catch (z3_exception& ex) { std::cerr << ex.msg() << "\n"; } } void tst_ex() { tst1(); tst2(); } z3-z3-4.8.7/src/test/expr_rand.cpp000066400000000000000000000060121356505360400166620ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "test/fuzzing/expr_rand.h" #include "ast/ast_pp.h" #include "ast/bv_decl_plugin.h" #include "ast/array_decl_plugin.h" #include "ast/arith_decl_plugin.h" #include "ast/ast_smt_pp.h" #include #include #include "ast/reg_decl_plugins.h" static unsigned rand_seed = 1; void tst_expr_arith(unsigned num_files) { ast_manager m; reg_decl_plugins(m); expr_rand er(m); er.seed(rand_seed); er.initialize_arith(20); family_id fid = m.mk_family_id("arith"); sort* int_ty = m.mk_sort(fid, INT_SORT, 0, nullptr); sort* real_ty = m.mk_sort(fid, REAL_SORT, 0, nullptr); er.initialize_array(3, int_ty, int_ty); er.initialize_array(3, int_ty, real_ty); er.initialize_basic(20); for (unsigned i = 0; i < num_files; ++i) { expr_ref e(m); er.get_next(m.mk_bool_sort(), e); ast_smt_pp pp(m); pp.set_logic(symbol("QF_AUFLIA")); std::ostringstream buffer; buffer << "random_arith_" << i << ".smt2"; std::cout << buffer.str() << "\n"; std::ofstream file(buffer.str().c_str()); pp.display_smt2(file, e.get()); file.close(); } } void tst_expr_rand(unsigned num_files) { ast_manager m; m.register_plugin(symbol("bv"), alloc(bv_decl_plugin)); m.register_plugin(symbol("array"), alloc(array_decl_plugin)); expr_rand er(m); er.initialize_bv(20); er.seed(rand_seed); parameter p1(1); parameter p2(2); parameter p8(8); parameter p32(32); family_id bvfid = m.mk_family_id("bv"); sort* bv1 = m.mk_sort(bvfid, BV_SORT, 1, &p1); sort* bv2 = m.mk_sort(bvfid, BV_SORT, 1, &p2); sort* bv8 = m.mk_sort(bvfid, BV_SORT, 1, &p8); sort* bv32 = m.mk_sort(bvfid, BV_SORT, 1, &p32); er.initialize_array(3, bv8, bv32); er.initialize_array(3, bv1, bv1); er.initialize_array(3, bv1, bv2); er.initialize_array(3, bv2, bv1); er.initialize_array(3, bv2, bv2); er.initialize_array(3, bv8, bv8); er.initialize_array(3, bv32, bv32); er.initialize_basic(20); for (unsigned i = 0; i < num_files; ++i) { expr_ref e(m); er.get_next(m.mk_bool_sort(), e); ast_smt_pp pp(m); pp.set_logic(symbol("QF_AUFBV")); std::ostringstream buffer; buffer << "random_bv_" << i << ".smt2"; std::cout << buffer.str() << "\n"; std::ofstream file(buffer.str().c_str()); pp.display_smt2(file, e.get()); file.close(); } } void tst_expr_rand(char** argv, int argc, int& i) { if (i + 1 < argc) { unsigned num_files = atol(argv[i+1]); i += 1; if (i + 1 < argc && 0 == strncmp(argv[i+1],"/rs:",3)) { rand_seed = atol(argv[i+1]+4); std::cout << "random seed:" << rand_seed << "\n"; i += 1; } if (i + 1 < argc && 0 == strcmp(argv[i+1],"/arith")) { tst_expr_arith(num_files); return; } tst_expr_rand(num_files); } } z3-z3-4.8.7/src/test/expr_substitution.cpp000066400000000000000000000031271356505360400205160ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "ast/expr_substitution.h" #include "smt/params/smt_params.h" #include "ast/substitution/substitution.h" #include "ast/substitution/unifier.h" #include "ast/bv_decl_plugin.h" #include "ast/ast_pp.h" #include "ast/arith_decl_plugin.h" #include "ast/reg_decl_plugins.h" #include "ast/rewriter/th_rewriter.h" expr* mk_bv_xor(bv_util& bv, expr* a, expr* b) { expr* args[2]; args[0] = a; args[1] = b; return bv.mk_bv_xor(2, args); } expr* mk_bv_and(bv_util& bv, expr* a, expr* b) { expr* args[2]; args[0] = a; args[1] = b; ast_manager& m = bv.get_manager(); return m.mk_app(bv.get_family_id(), OP_BAND, 2, args); } void tst_expr_substitution() { memory::initialize(0); ast_manager m; reg_decl_plugins(m); bv_util bv(m); expr_ref a(m), b(m), c(m), d(m); expr_ref x(m); expr_ref new_a(m); proof_ref pr(m); x = m.mk_const(symbol("x"), bv.mk_sort(8)); a = mk_bv_and(bv, mk_bv_xor(bv, x,bv.mk_numeral(8,8)), mk_bv_xor(bv,x,x)); b = x; c = bv.mk_bv_sub(x, bv.mk_numeral(4, 8)); expr_substitution subst(m); th_rewriter rw(m); std::cout << mk_pp(c, m) << "\n"; // normalizing c does not help. rw(c, d, pr); std::cout << mk_pp(d, m) << "\n"; // This portion diverges. It attempts to replace x by (bvadd #xfc x), which contains x. // subst.insert(b, d); // std::cout << mk_pp(a, m) << "\n"; // rw.set_substitution(&subst); // enable_trace("th_rewriter_step"); // rw(a, new_a, pr); // std::cout << mk_pp(new_a, m) << "\n"; } z3-z3-4.8.7/src/test/ext_numeral.cpp000066400000000000000000000436111356505360400172310ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: ext_numeral.cpp Abstract: Unit tests for ext_numeral template. Author: Leonardo (leonardo) 2012-07-18 Notes: --*/ #include #include "util/mpq.h" #include "util/ext_numeral.h" #define MK_TST_UNARY(NAME) \ static void tst_ ## NAME(int a, ext_numeral_kind ak, int expected_c, ext_numeral_kind expected_ck) { \ unsynch_mpq_manager m; \ scoped_mpq _a(m); \ m.set(_a, a); \ NAME(m, _a, ak); \ ENSURE(ak == expected_ck); \ if (expected_ck == EN_NUMERAL) { \ scoped_mpq _expected_c(m); \ m.set(_expected_c, expected_c); \ ENSURE(m.eq(_a, _expected_c)); \ } \ } MK_TST_UNARY(neg); MK_TST_UNARY(inv); #define MK_TST_BIN_CORE(FUN_NAME, OP_NAME) \ static void FUN_NAME(int a, ext_numeral_kind ak, int b, ext_numeral_kind bk, int expected_c, ext_numeral_kind expected_ck) { \ unsynch_mpq_manager m; \ scoped_mpq _a(m), _b(m), _c(m); \ m.set(_a, a); \ m.set(_b, b); \ ext_numeral_kind ck(EN_NUMERAL); \ OP_NAME(m, _a, ak, _b, bk, _c, ck); \ ENSURE(ck == expected_ck); \ if (expected_ck == EN_NUMERAL) { \ scoped_mpq _expected_c(m); \ m.set(_expected_c, expected_c); \ ENSURE(m.eq(_c, _expected_c)); \ } \ } #define MK_TST_BIN(NAME) MK_TST_BIN_CORE(tst_ ## NAME, NAME) #define MK_TST_COMM_BIN(NAME) \ MK_TST_BIN_CORE(tst_ ## NAME ## _core, NAME) \ static void tst_ ## NAME(int a, ext_numeral_kind ak, int b, ext_numeral_kind bk, int expected_c, ext_numeral_kind expected_ck) { \ tst_ ## NAME ## _core(a, ak, b, bk, expected_c, expected_ck); \ tst_ ## NAME ## _core(b, bk, a, ak, expected_c, expected_ck); \ } MK_TST_COMM_BIN(add); MK_TST_BIN(sub); MK_TST_COMM_BIN(mul); static void tst1() { tst_neg(0, EN_MINUS_INFINITY, 0, EN_PLUS_INFINITY); tst_neg(30, EN_MINUS_INFINITY, 10, EN_PLUS_INFINITY); tst_neg(0, EN_NUMERAL, 0, EN_NUMERAL); tst_neg(10, EN_NUMERAL, -10, EN_NUMERAL); tst_neg(-7, EN_NUMERAL, 7, EN_NUMERAL); tst_neg(0, EN_PLUS_INFINITY, 0, EN_MINUS_INFINITY); tst_neg(30, EN_PLUS_INFINITY, 0, EN_MINUS_INFINITY); tst_neg(-7, EN_PLUS_INFINITY, 0, EN_MINUS_INFINITY); tst_inv(0, EN_MINUS_INFINITY, 0, EN_NUMERAL); tst_inv(0, EN_PLUS_INFINITY, 0, EN_NUMERAL); tst_inv(1, EN_NUMERAL, 1, EN_NUMERAL); tst_inv(-1, EN_NUMERAL, -1, EN_NUMERAL); tst_add(0, EN_MINUS_INFINITY, 0, EN_MINUS_INFINITY, 0, EN_MINUS_INFINITY); tst_add(0, EN_MINUS_INFINITY, 0, EN_NUMERAL, 0, EN_MINUS_INFINITY); tst_add(0, EN_MINUS_INFINITY, -1, EN_NUMERAL, 0, EN_MINUS_INFINITY); tst_add(0, EN_MINUS_INFINITY, 1, EN_NUMERAL, 0, EN_MINUS_INFINITY); tst_add(1, EN_MINUS_INFINITY, -1, EN_NUMERAL, 0, EN_MINUS_INFINITY); tst_add(1, EN_NUMERAL, 0, EN_MINUS_INFINITY, 0, EN_MINUS_INFINITY); tst_add(-1, EN_NUMERAL, 0, EN_MINUS_INFINITY, 0, EN_MINUS_INFINITY); tst_add(0, EN_NUMERAL, 0, EN_MINUS_INFINITY, 0, EN_MINUS_INFINITY); tst_add(0, EN_NUMERAL, 2, EN_NUMERAL, 2, EN_NUMERAL); tst_add(-3, EN_NUMERAL, 4, EN_NUMERAL, 1, EN_NUMERAL); tst_add(-2, EN_NUMERAL, 0, EN_NUMERAL, -2, EN_NUMERAL); tst_add(3, EN_NUMERAL, 4, EN_NUMERAL, 7, EN_NUMERAL); tst_add(0, EN_PLUS_INFINITY, 0, EN_PLUS_INFINITY, 0, EN_PLUS_INFINITY); tst_add(0, EN_PLUS_INFINITY, 0, EN_NUMERAL, 0, EN_PLUS_INFINITY); tst_add(0, EN_PLUS_INFINITY, 1, EN_NUMERAL, 0, EN_PLUS_INFINITY); tst_add(0, EN_PLUS_INFINITY, -1, EN_NUMERAL, 0, EN_PLUS_INFINITY); tst_add(0, EN_NUMERAL, 0, EN_PLUS_INFINITY, 0, EN_PLUS_INFINITY); tst_add(-1, EN_NUMERAL, 0, EN_PLUS_INFINITY, 0, EN_PLUS_INFINITY); tst_add(1, EN_NUMERAL, 0, EN_PLUS_INFINITY, 0, EN_PLUS_INFINITY); tst_mul(0, EN_MINUS_INFINITY, 0, EN_MINUS_INFINITY, 0, EN_PLUS_INFINITY); tst_mul(0, EN_MINUS_INFINITY, 0, EN_PLUS_INFINITY, 0, EN_MINUS_INFINITY); tst_mul(0, EN_PLUS_INFINITY, 0, EN_MINUS_INFINITY, 0, EN_MINUS_INFINITY); tst_mul(0, EN_PLUS_INFINITY, 0, EN_PLUS_INFINITY, 0, EN_PLUS_INFINITY); tst_mul(0, EN_MINUS_INFINITY, 0, EN_NUMERAL, 0, EN_NUMERAL); tst_mul(0, EN_MINUS_INFINITY, 1, EN_NUMERAL, 0, EN_MINUS_INFINITY); tst_mul(0, EN_MINUS_INFINITY, 5, EN_NUMERAL, 0, EN_MINUS_INFINITY); tst_mul(0, EN_MINUS_INFINITY, -1, EN_NUMERAL, 0, EN_PLUS_INFINITY); tst_mul(0, EN_MINUS_INFINITY, -5, EN_NUMERAL, 0, EN_PLUS_INFINITY); tst_mul(0, EN_PLUS_INFINITY, 0, EN_NUMERAL, 0, EN_NUMERAL); tst_mul(0, EN_PLUS_INFINITY, 1, EN_NUMERAL, 0, EN_PLUS_INFINITY); tst_mul(0, EN_PLUS_INFINITY, 5, EN_NUMERAL, 0, EN_PLUS_INFINITY); tst_mul(0, EN_PLUS_INFINITY, -1, EN_NUMERAL, 0, EN_MINUS_INFINITY); tst_mul(0, EN_PLUS_INFINITY, -5, EN_NUMERAL, 0, EN_MINUS_INFINITY); tst_mul(0, EN_NUMERAL, 3, EN_NUMERAL, 0, EN_NUMERAL); tst_mul(2, EN_NUMERAL, 3, EN_NUMERAL, 6, EN_NUMERAL); tst_mul(-2, EN_NUMERAL, 3, EN_NUMERAL, -6, EN_NUMERAL); tst_sub(0, EN_PLUS_INFINITY, 0, EN_MINUS_INFINITY, 0, EN_PLUS_INFINITY); tst_sub(0, EN_PLUS_INFINITY, 0, EN_NUMERAL, 0, EN_PLUS_INFINITY); tst_sub(0, EN_PLUS_INFINITY, -10, EN_NUMERAL, 0, EN_PLUS_INFINITY); tst_sub(0, EN_PLUS_INFINITY, 10, EN_NUMERAL, 0, EN_PLUS_INFINITY); tst_sub(0, EN_MINUS_INFINITY, 0, EN_PLUS_INFINITY, 0, EN_MINUS_INFINITY); tst_sub(0, EN_MINUS_INFINITY, 0, EN_NUMERAL, 0, EN_MINUS_INFINITY); tst_sub(0, EN_MINUS_INFINITY, -10, EN_NUMERAL, 0, EN_MINUS_INFINITY); tst_sub(0, EN_MINUS_INFINITY, 10, EN_NUMERAL, 0, EN_MINUS_INFINITY); tst_sub(0, EN_MINUS_INFINITY, 0, EN_PLUS_INFINITY, 0, EN_MINUS_INFINITY); tst_sub(0, EN_MINUS_INFINITY, 0, EN_NUMERAL, 0, EN_MINUS_INFINITY); tst_sub(0, EN_MINUS_INFINITY, 3, EN_NUMERAL, 0, EN_MINUS_INFINITY); tst_sub(0, EN_MINUS_INFINITY, -3, EN_NUMERAL, 0, EN_MINUS_INFINITY); tst_sub(0, EN_PLUS_INFINITY, 0, EN_MINUS_INFINITY, 0, EN_PLUS_INFINITY); tst_sub(0, EN_PLUS_INFINITY, 0, EN_NUMERAL, 0, EN_PLUS_INFINITY); tst_sub(0, EN_PLUS_INFINITY, 3, EN_NUMERAL, 0, EN_PLUS_INFINITY); tst_sub(0, EN_PLUS_INFINITY, -3, EN_NUMERAL, 0, EN_PLUS_INFINITY); tst_sub(0, EN_NUMERAL, 2, EN_NUMERAL, -2, EN_NUMERAL); tst_sub(3, EN_NUMERAL, 2, EN_NUMERAL, 1, EN_NUMERAL); tst_sub(3, EN_NUMERAL, -3, EN_NUMERAL, 6, EN_NUMERAL); tst_sub(3, EN_NUMERAL, 3, EN_NUMERAL, 0, EN_NUMERAL); tst_sub(3, EN_NUMERAL, 0, EN_NUMERAL, 3, EN_NUMERAL); tst_sub(-3, EN_NUMERAL, -5, EN_NUMERAL, 2, EN_NUMERAL); } #define MK_TST_REL_CORE(FUN_NAME, OP_NAME) \ static void FUN_NAME(int a, ext_numeral_kind ak, int b, ext_numeral_kind bk, bool expected) { \ unsynch_mpq_manager m; \ scoped_mpq _a(m), _b(m); \ m.set(_a, a); \ m.set(_b, b); \ VERIFY(expected == OP_NAME(m, _a, ak, _b, bk)); \ } #define MK_TST_REL(NAME) MK_TST_REL_CORE(tst_ ## NAME, NAME) #define MK_TST_SYMM_REL(NAME) \ MK_TST_REL_CORE(tst_ ## NAME ## _core, NAME) \ static void tst_ ## NAME(int a, ext_numeral_kind ak, int b, ext_numeral_kind bk, bool expected) { \ tst_ ## NAME ## _core(a, ak, b, bk, expected); \ tst_ ## NAME ## _core(b, bk, a, ak, expected); \ } MK_TST_SYMM_REL(eq); MK_TST_SYMM_REL(neq); MK_TST_REL(lt); MK_TST_REL(gt); MK_TST_REL(le); MK_TST_REL(ge); static void tst2() { tst_eq(0, EN_NUMERAL, 0, EN_NUMERAL, true); tst_eq(0, EN_NUMERAL, 2, EN_NUMERAL, false); tst_eq(3, EN_NUMERAL, 0, EN_NUMERAL, false); tst_eq(0, EN_PLUS_INFINITY, 0, EN_NUMERAL, false); tst_eq(0, EN_PLUS_INFINITY, 3, EN_NUMERAL, false); tst_eq(0, EN_PLUS_INFINITY, -2, EN_NUMERAL, false); tst_eq(0, EN_PLUS_INFINITY, 0, EN_MINUS_INFINITY, false); tst_neq(0, EN_NUMERAL, 0, EN_NUMERAL, false); tst_neq(0, EN_NUMERAL, 2, EN_NUMERAL, true); tst_neq(3, EN_NUMERAL, 0, EN_NUMERAL, true); tst_neq(0, EN_PLUS_INFINITY, 0, EN_NUMERAL, true); tst_neq(0, EN_PLUS_INFINITY, 3, EN_NUMERAL, true); tst_neq(0, EN_PLUS_INFINITY, -2, EN_NUMERAL, true); tst_neq(0, EN_PLUS_INFINITY, 0, EN_MINUS_INFINITY, true); tst_lt(0, EN_MINUS_INFINITY, 10, EN_NUMERAL, true); tst_lt(0, EN_MINUS_INFINITY, 0, EN_NUMERAL, true); tst_lt(0, EN_MINUS_INFINITY, -3, EN_NUMERAL, true); tst_lt(30, EN_MINUS_INFINITY, 10, EN_NUMERAL, true); tst_lt(20, EN_MINUS_INFINITY, 0, EN_NUMERAL, true); tst_lt(-20, EN_MINUS_INFINITY, -3, EN_NUMERAL, true); tst_lt(0, EN_MINUS_INFINITY, 10, EN_PLUS_INFINITY, true); tst_lt(0, EN_MINUS_INFINITY, 0, EN_PLUS_INFINITY, true); tst_lt(10, EN_MINUS_INFINITY, -30, EN_PLUS_INFINITY, true); tst_lt(0, EN_PLUS_INFINITY, 10, EN_NUMERAL, false); tst_lt(0, EN_PLUS_INFINITY, 0, EN_NUMERAL, false); tst_lt(0, EN_PLUS_INFINITY, -3, EN_NUMERAL, false); tst_lt(30, EN_PLUS_INFINITY, 10, EN_NUMERAL, false); tst_lt(20, EN_PLUS_INFINITY, 0, EN_NUMERAL, false); tst_lt(-20, EN_PLUS_INFINITY, -3, EN_NUMERAL, false); tst_lt(0, EN_PLUS_INFINITY, 10, EN_MINUS_INFINITY, false); tst_lt(0, EN_PLUS_INFINITY, 0, EN_MINUS_INFINITY, false); tst_lt(10, EN_PLUS_INFINITY, -30, EN_MINUS_INFINITY, false); tst_lt(0, EN_NUMERAL, 0, EN_PLUS_INFINITY, true); tst_lt(20, EN_NUMERAL, 10, EN_PLUS_INFINITY, true); tst_lt(-20, EN_NUMERAL, -100, EN_PLUS_INFINITY, true); tst_lt(0, EN_NUMERAL, 10, EN_NUMERAL, true); tst_lt(0, EN_NUMERAL, 0, EN_NUMERAL, false); tst_lt(10, EN_NUMERAL, 10, EN_NUMERAL, false); tst_lt(0, EN_NUMERAL, -3, EN_NUMERAL, false); tst_lt(30, EN_NUMERAL, 10, EN_NUMERAL, false); tst_lt(30, EN_NUMERAL, 40, EN_NUMERAL, true); tst_lt(20, EN_NUMERAL, 0, EN_NUMERAL, false); tst_lt(-20, EN_NUMERAL, -3, EN_NUMERAL, true); tst_lt(0, EN_NUMERAL, 10, EN_MINUS_INFINITY, false); tst_lt(0, EN_NUMERAL, 0, EN_MINUS_INFINITY, false); tst_lt(10, EN_NUMERAL, -30, EN_MINUS_INFINITY, false); tst_le(0, EN_MINUS_INFINITY, 10, EN_NUMERAL, true); tst_le(0, EN_MINUS_INFINITY, 0, EN_NUMERAL, true); tst_le(0, EN_MINUS_INFINITY, -3, EN_NUMERAL, true); tst_le(30, EN_MINUS_INFINITY, 10, EN_NUMERAL, true); tst_le(20, EN_MINUS_INFINITY, 0, EN_NUMERAL, true); tst_le(-20, EN_MINUS_INFINITY, -3, EN_NUMERAL, true); tst_le(0, EN_MINUS_INFINITY, 10, EN_PLUS_INFINITY, true); tst_le(0, EN_MINUS_INFINITY, 0, EN_PLUS_INFINITY, true); tst_le(10, EN_MINUS_INFINITY, -30, EN_PLUS_INFINITY, true); tst_le(0, EN_PLUS_INFINITY, 10, EN_NUMERAL, false); tst_le(0, EN_PLUS_INFINITY, 0, EN_NUMERAL, false); tst_le(0, EN_PLUS_INFINITY, -3, EN_NUMERAL, false); tst_le(30, EN_PLUS_INFINITY, 10, EN_NUMERAL, false); tst_le(20, EN_PLUS_INFINITY, 0, EN_NUMERAL, false); tst_le(-20, EN_PLUS_INFINITY, -3, EN_NUMERAL, false); tst_le(0, EN_PLUS_INFINITY, 10, EN_MINUS_INFINITY, false); tst_le(0, EN_PLUS_INFINITY, 0, EN_MINUS_INFINITY, false); tst_le(10, EN_PLUS_INFINITY, -30, EN_MINUS_INFINITY, false); tst_le(0, EN_NUMERAL, 0, EN_PLUS_INFINITY, true); tst_le(20, EN_NUMERAL, 10, EN_PLUS_INFINITY, true); tst_le(-20, EN_NUMERAL, -100, EN_PLUS_INFINITY, true); tst_le(0, EN_NUMERAL, 10, EN_NUMERAL, true); tst_le(0, EN_NUMERAL, 0, EN_NUMERAL, true); tst_le(10, EN_NUMERAL, 10, EN_NUMERAL, true); tst_le(0, EN_NUMERAL, -3, EN_NUMERAL, false); tst_le(30, EN_NUMERAL, 10, EN_NUMERAL, false); tst_le(30, EN_NUMERAL, 40, EN_NUMERAL, true); tst_le(20, EN_NUMERAL, 0, EN_NUMERAL, false); tst_le(-20, EN_NUMERAL, -3, EN_NUMERAL, true); tst_le(0, EN_NUMERAL, 10, EN_MINUS_INFINITY, false); tst_le(0, EN_NUMERAL, 0, EN_MINUS_INFINITY, false); tst_le(10, EN_NUMERAL, -30, EN_MINUS_INFINITY, false); tst_ge(0, EN_MINUS_INFINITY, 10, EN_NUMERAL, false); tst_ge(0, EN_MINUS_INFINITY, 0, EN_NUMERAL, false); tst_ge(0, EN_MINUS_INFINITY, -3, EN_NUMERAL, false); tst_ge(30, EN_MINUS_INFINITY, 10, EN_NUMERAL, false); tst_ge(20, EN_MINUS_INFINITY, 0, EN_NUMERAL, false); tst_ge(-20, EN_MINUS_INFINITY, -3, EN_NUMERAL, false); tst_ge(0, EN_MINUS_INFINITY, 10, EN_PLUS_INFINITY, false); tst_ge(0, EN_MINUS_INFINITY, 0, EN_PLUS_INFINITY, false); tst_ge(10, EN_MINUS_INFINITY, -30, EN_PLUS_INFINITY, false); tst_ge(0, EN_PLUS_INFINITY, 10, EN_NUMERAL, true); tst_ge(0, EN_PLUS_INFINITY, 0, EN_NUMERAL, true); tst_ge(0, EN_PLUS_INFINITY, -3, EN_NUMERAL, true); tst_ge(30, EN_PLUS_INFINITY, 10, EN_NUMERAL, true); tst_ge(20, EN_PLUS_INFINITY, 0, EN_NUMERAL, true); tst_ge(-20, EN_PLUS_INFINITY, -3, EN_NUMERAL, true); tst_ge(0, EN_PLUS_INFINITY, 10, EN_MINUS_INFINITY, true); tst_ge(0, EN_PLUS_INFINITY, 0, EN_MINUS_INFINITY, true); tst_ge(10, EN_PLUS_INFINITY, -30, EN_MINUS_INFINITY, true); tst_ge(0, EN_NUMERAL, 0, EN_PLUS_INFINITY, false); tst_ge(20, EN_NUMERAL, 10, EN_PLUS_INFINITY, false); tst_ge(-20, EN_NUMERAL, -100, EN_PLUS_INFINITY, false); tst_ge(0, EN_NUMERAL, 10, EN_NUMERAL, false); tst_ge(0, EN_NUMERAL, 0, EN_NUMERAL, true); tst_ge(10, EN_NUMERAL, 10, EN_NUMERAL, true); tst_ge(0, EN_NUMERAL, -3, EN_NUMERAL, true); tst_ge(30, EN_NUMERAL, 10, EN_NUMERAL, true); tst_ge(30, EN_NUMERAL, 40, EN_NUMERAL, false); tst_ge(20, EN_NUMERAL, 0, EN_NUMERAL, true); tst_ge(-20, EN_NUMERAL, -3, EN_NUMERAL, false); tst_ge(0, EN_NUMERAL, 10, EN_MINUS_INFINITY, true); tst_ge(0, EN_NUMERAL, 0, EN_MINUS_INFINITY, true); tst_ge(10, EN_NUMERAL, -30, EN_MINUS_INFINITY, true); tst_gt(0, EN_MINUS_INFINITY, 10, EN_NUMERAL, false); tst_gt(0, EN_MINUS_INFINITY, 0, EN_NUMERAL, false); tst_gt(0, EN_MINUS_INFINITY, -3, EN_NUMERAL, false); tst_gt(30, EN_MINUS_INFINITY, 10, EN_NUMERAL, false); tst_gt(20, EN_MINUS_INFINITY, 0, EN_NUMERAL, false); tst_gt(-20, EN_MINUS_INFINITY, -3, EN_NUMERAL, false); tst_gt(0, EN_MINUS_INFINITY, 10, EN_PLUS_INFINITY, false); tst_gt(0, EN_MINUS_INFINITY, 0, EN_PLUS_INFINITY, false); tst_gt(10, EN_MINUS_INFINITY, -30, EN_PLUS_INFINITY, false); tst_gt(0, EN_PLUS_INFINITY, 10, EN_NUMERAL, true); tst_gt(0, EN_PLUS_INFINITY, 0, EN_NUMERAL, true); tst_gt(0, EN_PLUS_INFINITY, -3, EN_NUMERAL, true); tst_gt(30, EN_PLUS_INFINITY, 10, EN_NUMERAL, true); tst_gt(20, EN_PLUS_INFINITY, 0, EN_NUMERAL, true); tst_gt(-20, EN_PLUS_INFINITY, -3, EN_NUMERAL, true); tst_gt(0, EN_PLUS_INFINITY, 10, EN_MINUS_INFINITY, true); tst_gt(0, EN_PLUS_INFINITY, 0, EN_MINUS_INFINITY, true); tst_gt(10, EN_PLUS_INFINITY, -30, EN_MINUS_INFINITY, true); tst_gt(0, EN_NUMERAL, 0, EN_PLUS_INFINITY, false); tst_gt(20, EN_NUMERAL, 10, EN_PLUS_INFINITY, false); tst_gt(-20, EN_NUMERAL, -100, EN_PLUS_INFINITY, false); tst_gt(0, EN_NUMERAL, 10, EN_NUMERAL, false); tst_gt(0, EN_NUMERAL, 0, EN_NUMERAL, false); tst_gt(10, EN_NUMERAL, 10, EN_NUMERAL, false); tst_gt(0, EN_NUMERAL, -3, EN_NUMERAL, true); tst_gt(30, EN_NUMERAL, 10, EN_NUMERAL, true); tst_gt(30, EN_NUMERAL, 40, EN_NUMERAL, false); tst_gt(20, EN_NUMERAL, 0, EN_NUMERAL, true); tst_gt(-20, EN_NUMERAL, -3, EN_NUMERAL, false); tst_gt(0, EN_NUMERAL, 10, EN_MINUS_INFINITY, true); tst_gt(0, EN_NUMERAL, 0, EN_MINUS_INFINITY, true); tst_gt(10, EN_NUMERAL, -30, EN_MINUS_INFINITY, true); } static void tst3() { unsynch_mpq_manager m; scoped_mpq a(m); ENSURE(is_zero(m, a, EN_NUMERAL)); ENSURE(!is_zero(m, a, EN_PLUS_INFINITY)); ENSURE(!is_zero(m, a, EN_MINUS_INFINITY)); ENSURE(!is_pos(m, a, EN_NUMERAL)); ENSURE(is_pos(m, a, EN_PLUS_INFINITY)); ENSURE(!is_pos(m, a, EN_MINUS_INFINITY)); ENSURE(!is_infinite(EN_NUMERAL)); ENSURE(is_infinite(EN_PLUS_INFINITY)); ENSURE(is_infinite(EN_MINUS_INFINITY)); ENSURE(!is_neg(m, a, EN_NUMERAL)); ENSURE(!is_neg(m, a, EN_PLUS_INFINITY)); ENSURE(is_neg(m, a, EN_MINUS_INFINITY)); m.set(a, 10); ENSURE(!is_zero(m, a, EN_NUMERAL)); ENSURE(is_pos(m, a, EN_NUMERAL)); ENSURE(!is_neg(m, a, EN_NUMERAL)); ENSURE(!is_infinite(EN_NUMERAL)); m.set(a, -5); ENSURE(!is_zero(m, a, EN_NUMERAL)); ENSURE(!is_pos(m, a, EN_NUMERAL)); ENSURE(is_neg(m, a, EN_NUMERAL)); ENSURE(!is_infinite(EN_NUMERAL)); ext_numeral_kind ak; ak = EN_MINUS_INFINITY; reset(m, a, ak); ENSURE(is_zero(m, a, EN_NUMERAL)); { std::ostringstream buffer; display(buffer, m, a, ak); ENSURE(buffer.str() == "0"); } { std::ostringstream buffer; m.set(a, -10); display(buffer, m, a, ak); ENSURE(buffer.str() == "-10"); } { std::ostringstream buffer; display(buffer, m, a, EN_PLUS_INFINITY); ENSURE(buffer.str() == "+oo"); } { std::ostringstream buffer; display(buffer, m, a, EN_MINUS_INFINITY); ENSURE(buffer.str() == "-oo"); } } void tst_ext_numeral() { tst1(); tst2(); tst3(); } z3-z3-4.8.7/src/test/f2n.cpp000066400000000000000000000030371356505360400153710ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: f2n.cpp Abstract: Author: Leonardo de Moura (leonardo) 2012-08-17. Revision History: --*/ #include "util/f2n.h" #include "util/hwf.h" #include "util/mpf.h" static void tst1() { hwf_manager hm; f2n m(hm); hwf a, b; m.set(a, 11, 3); m.floor(a, b); std::cout << "floor(11/3): " << m.to_double(b) << "\n"; m.ceil(a, b); std::cout << "ceil(11/3): " << m.to_double(b) << "\n"; m.set(a, -11, 3); m.floor(a, b); std::cout << "floor(-11/3): " << m.to_double(b) << "\n"; m.ceil(a, b); std::cout << "ceil(-11/3): " << m.to_double(b) << "\n"; m.set(a, 11, 1); m.floor(a, b); std::cout << "floor(11): " << m.to_double(b) << "\n"; m.ceil(a, b); std::cout << "ceil(11): " << m.to_double(b) << "\n"; } static void tst2() { std::cout << "using mpf...\n"; mpf_manager fm; f2n m(fm); scoped_mpf a(fm), b(fm); m.set(a, 11, 3); m.floor(a, b); std::cout << "floor(11/3): " << m.to_double(b) << "\n"; m.ceil(a, b); std::cout << "ceil(11/3): " << m.to_double(b) << "\n"; m.set(a, -11, 3); m.floor(a, b); std::cout << "floor(-11/3): " << m.to_double(b) << "\n"; m.ceil(a, b); std::cout << "ceil(-11/3): " << m.to_double(b) << "\n"; m.set(a, 11, 1); m.floor(a, b); std::cout << "floor(11): " << m.to_double(b) << "\n"; m.ceil(a, b); std::cout << "ceil(11): " << m.to_double(b) << "\n"; } void tst_f2n() { tst1(); tst2(); } z3-z3-4.8.7/src/test/factor_rewriter.cpp000066400000000000000000000012171356505360400201030ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "ast/rewriter/factor_rewriter.h" #include "ast/bv_decl_plugin.h" #include "ast/ast_pp.h" #include "ast/reg_decl_plugins.h" void tst_factor_rewriter() { ast_manager m; reg_decl_plugins(m); factor_rewriter_star fw(m); arith_util a(m); expr_ref fml1(m), fml2(m); expr_ref z(m.mk_const(symbol("z"), a.mk_real()), m); expr_ref two(a.mk_numeral(rational(2),false),m); expr_ref zero(a.mk_numeral(rational(0),false),m); fml1 = a.mk_le(zero, a.mk_mul(two, z, z)); fw(fml1, fml2); std::cout << mk_pp(fml1, m) << " -> " << mk_pp(fml2, m) << "\n"; } z3-z3-4.8.7/src/test/fixed_bit_vector.cpp000066400000000000000000000045001356505360400202170ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: fixed_bit_vector.cpp Abstract: Test fixed-size bit vector module Author: Nikolaj Bjorner (nbjorner) 2014-9-15. Revision History: based on bit_vector.cpp --*/ #include #include #include "util/fixed_bit_vector.h" #include "util/vector.h" static void tst1() { fixed_bit_vector_manager m(30); fixed_bit_vector *b; b = m.allocate0(); m.set(*b, 0, true); m.set(*b, 1, false); m.set(*b, 2, true); ENSURE(b->get(0) == true); ENSURE(b->get(1) == false); ENSURE(b->get(2) == true); ENSURE(b->get(3) == false); ENSURE(b->get(29) == false); m.deallocate(b); } static void tst_or() { { fixed_bit_vector_manager m(10); fixed_bit_vector *b1, *b2; b1 = m.allocate0(); b2 = m.allocate0(); m.set(*b1, 4); m.set(*b2, 8); m.set(*b2, 3); m.set(*b2, 2); m.set(*b2, 1); m.display(std::cout, *b1) << "\n"; m.display(std::cout, *b2) << "\n"; m.set_or(*b1, *b2); m.display(std::cout, *b1) << "\n"; ENSURE(!m.equals(*b1, *b2)); m.unset(*b1, 4); ENSURE(m.equals(*b1, *b2)); m.unset(*b1, 3); ENSURE(!m.equals(*b1, *b2)); m.deallocate(b1); m.deallocate(b2); } } static void tst_and() { } static void tst_eq(unsigned num_bits) { fixed_bit_vector_manager m(num_bits); fixed_bit_vector* b1 = m.allocate0(); fixed_bit_vector* b2 = m.allocate0(); fixed_bit_vector* b3 = m.allocate0(); m.set(*b1, 3, true); ENSURE(!m.equals(*b1, *b2)); ENSURE(m.equals(*b2, *b3)); m.set(*b3, 3, true); ENSURE(m.equals(*b1, *b3)); m.set(*b2, num_bits-1, true); m.set(*b3, num_bits-1); m.unset(*b3, 3); ENSURE(m.equals(*b2, *b3)); m.fill0(*b1); m.set_neg(*b1); m.fill1(*b2); ENSURE(m.equals(*b1, *b2)); m.fill0(*b1); for (unsigned i = 0; i < num_bits; ++i) { m.set(*b1, i, true); } ENSURE(m.equals(*b1, *b2)); m.deallocate(b1); m.deallocate(b2); m.deallocate(b3); } void tst_fixed_bit_vector() { tst1(); tst_or(); tst_and(); tst_eq(15); tst_eq(16); tst_eq(17); tst_eq(31); tst_eq(32); tst_eq(33); tst_eq(63); tst_eq(64); tst_eq(65); } z3-z3-4.8.7/src/test/for_each_file.cpp000066400000000000000000000031111356505360400174420ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: foreach_file.cpp Abstract: Traverse files in a directory that match a given suffix. Apply a method to each of the files. Author: Nikolaj Bjorner (nbjorner) 2006-11-3. Revision History: --*/ #ifdef _WINDOWS #include #include #include #include "util/util.h" #include "test/for_each_file.h" bool for_each_file(for_each_file_proc& proc, const char* base, const char* suffix) { std::string pattern(base); pattern += "\\"; pattern += suffix; char buffer[MAX_PATH]; WIN32_FIND_DATAA data; HANDLE h = FindFirstFileA(pattern.c_str(),&data); while (h != INVALID_HANDLE_VALUE) { StringCchPrintfA(buffer, Z3_ARRAYSIZE(buffer), "%s\\%s", base, data.cFileName); if (!proc(buffer)) { return false; } if (!FindNextFileA(h,&data)) { break; } } // // Now recurse through sub-directories. // pattern = base; pattern += "\\*"; h = FindFirstFileA(pattern.c_str(),&data); while (h != INVALID_HANDLE_VALUE) { if ((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && data.cFileName[0] != '.' ){ std::string subdir(base); subdir += "\\"; subdir += data.cFileName; if (!for_each_file(proc, subdir.c_str(), suffix)) { return false; } } if (!FindNextFileA(h,&data)) { break; } } return true; }; #endif z3-z3-4.8.7/src/test/for_each_file.h000066400000000000000000000010411356505360400171070ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: foreach_file.h Abstract: Traverse files in a directory that match a given suffix. Apply a method to each of the files. Author: Nikolaj Bjorner (nbjorner) 2006-11-3. Revision History: --*/ #pragma once #ifndef FOR_EACH_FILE_H_ #define FOR_EACH_FILE_H_ struct for_each_file_proc { virtual bool operator()(const char* file_path) = 0; }; bool for_each_file(for_each_file_proc& proc, const char* base, const char* suffix); #endif /* FOR_EACH_FILE_H_ */ z3-z3-4.8.7/src/test/fuzzing/000077500000000000000000000000001356505360400156715ustar00rootroot00000000000000z3-z3-4.8.7/src/test/fuzzing/CMakeLists.txt000066400000000000000000000002511356505360400204270ustar00rootroot00000000000000z3_add_component(fuzzing NOT_LIBZ3_COMPONENT # Don't put this component inside libz3 SOURCES expr_delta.cpp expr_rand.cpp COMPONENT_DEPENDENCIES ast ) z3-z3-4.8.7/src/test/fuzzing/expr_delta.cpp000066400000000000000000000040421356505360400205240ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "test/fuzzing/expr_delta.h" #include "ast/ast_pp.h" expr_delta::expr_delta(ast_manager& m) : m_manager(m), m_exprs(m) {} void expr_delta::assert_cnstr(expr* n) { m_exprs.push_back(n); } bool expr_delta::delta_dfs(unsigned n, expr_ref_vector& result) { return delta_dfs(n, m_exprs.size(), m_exprs.c_ptr(), result); } bool expr_delta::delta_dfs(unsigned& n, unsigned sz, expr* const* exprs, expr_ref_vector& result) { expr_ref r(m_manager); for (unsigned i = 0; i < sz; ++i) { expr* e = exprs[i]; if (delta_dfs(n, e, r)) { result.push_back(r.get()); for (unsigned j = i+1; j < sz; ++j) { result.push_back(exprs[j]); } return true; } else { result.push_back(e); } } return false; } bool expr_delta::delta_dfs(unsigned& n, app* a, expr_ref& result) { expr_ref_vector args(m_manager); if (delta_dfs(n, a->get_num_args(), a->get_args(), args)) { result = m_manager.mk_app(a->get_decl(), args.size(), args.c_ptr()); return true; } else { return false; } } bool expr_delta::delta_dfs(unsigned& n, expr* e, expr_ref& result) { ast_manager& m = m_manager; if (m.is_true(e) || m.is_false(e)) { return false; } if (n == 0 && m.is_bool(e)) { result = m.mk_true(); return true; } else if (n == 1 && m.is_bool(e)) { result = m.mk_false(); return true; } else if (is_app(e)) { if (m.is_bool(e)) { SASSERT(n >= 2); n -= 2; } return delta_dfs(n, to_app(e), result); } else if (is_quantifier(e)) { SASSERT(n >= 2); n -= 2; quantifier* q = to_quantifier(e); if (delta_dfs(n, q->get_expr(), result)) { result = m.update_quantifier(q, result.get()); return true; } else { return false; } } return false; } z3-z3-4.8.7/src/test/fuzzing/expr_delta.h000066400000000000000000000016511356505360400201740ustar00rootroot00000000000000/*++ Copyright (c) 2008 Microsoft Corporation Module Name: expr_delta.h Abstract: Delta debugging support for specifications. A specification is a list of assumptions. Author: Nikolaj Bjorner (nbjorner) 2008-21-06 Revision History: --*/ #ifndef EXPR_DELTA_H_ #define EXPR_DELTA_H_ #include "ast/ast.h" class expr_delta { ast_manager& m_manager; expr_ref_vector m_exprs; public: expr_delta(ast_manager& m); // Assert a constraint. void assert_cnstr(expr* e); // // Create the n'th delta in dfs mode. // return 'true' if a delta was obtained. // bool delta_dfs(unsigned n, expr_ref_vector& result); private: // perform delta bool delta_dfs(unsigned& n, expr* e, expr_ref& result); bool delta_dfs(unsigned& n, app* a, expr_ref& result); bool delta_dfs(unsigned& n, unsigned sz, expr* const* exprs, expr_ref_vector& result); }; #endif z3-z3-4.8.7/src/test/fuzzing/expr_rand.cpp000066400000000000000000000253371356505360400203710ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "test/fuzzing/expr_rand.h" #include "ast/bv_decl_plugin.h" #include "ast/array_decl_plugin.h" #include "ast/arith_decl_plugin.h" #include "ast/ast_pp.h" expr_rand::expr_rand(ast_manager& m): m_manager(m), m_max_steps(10), m_funcs(m) {} expr_rand::~expr_rand() { map_t::iterator it = m_nodes.begin(); map_t::iterator end = m_nodes.end(); for (; it != end; ++it) { dealloc(it->m_value); } } void expr_rand::add_var(sort* s) { add_expr(m_manager.mk_fresh_const("x", s)); } void expr_rand::add_func_decl(func_decl* f) { m_funcs.push_back(f); } void expr_rand::add_expr(expr* t) { sort* s = m_manager.get_sort(t); expr_ref_vector* vals = nullptr; if (!m_nodes.find(s, vals)) { vals = alloc(expr_ref_vector, m_manager); m_nodes.insert(s, vals); } vals->push_back(t); } void expr_rand::get_next(sort* s, expr_ref& e) { walk(m_max_steps); e = choose_expr(s); } void expr_rand::walk() { func_decl* f = choose_func_decl(); unsigned arity = f->get_arity(); expr_ref_vector args(m_manager); for (unsigned i = 0; i < arity; ++i) { args.push_back(choose_expr(f->get_domain(i))); } expr* r = m_manager.mk_app(f, args.size(), args.c_ptr()); add_expr(r); } void expr_rand::walk(unsigned n) { for (unsigned i = 0; i < n; ++i) { walk(); } } func_decl* expr_rand::choose_func_decl() { unsigned idx = m_random(m_funcs.size()); return m_funcs[idx].get(); } expr* expr_rand::choose_expr(sort* s) { expr_ref_vector* vals = nullptr; if (!m_nodes.find(s, vals)) { add_var(s); if (!m_nodes.find(s, vals)) { UNREACHABLE(); } SASSERT(vals); } unsigned idx = m_random(vals->size()); return (*vals)[idx].get(); } void expr_rand::initialize_arith(unsigned num_vars) { arith_util u(m_manager); family_id afid = m_manager.mk_family_id("arith"); sort* i_ty = m_manager.mk_sort(afid, INT_SORT, 0, nullptr); for(unsigned i = 0; i < num_vars; ++i) { add_var(i_ty); } sort* is[2] = { i_ty, i_ty }; decl_kind kinds[7] = {OP_ADD, OP_MUL, OP_SUB, OP_LE, OP_LT, OP_GE, OP_GT }; for (unsigned i = 0; i < 7; ++i) { add_func_decl(m_manager.mk_func_decl(afid, kinds[i], 0, nullptr, 2, is)); } add_expr(u.mk_numeral(rational(0), true)); add_expr(u.mk_numeral(rational(1), true)); add_expr(u.mk_numeral(rational(2), true)); add_expr(u.mk_numeral(rational(3), true)); add_expr(u.mk_numeral(rational(6), true)); add_expr(u.mk_numeral(rational(7), true)); add_expr(u.mk_numeral(rational(-1), true)); add_expr(u.mk_numeral(rational(-2), true)); } void expr_rand::initialize_bv(unsigned num_vars) { bv_util u(m_manager); family_id bfid = m_manager.get_basic_family_id(); family_id bvfid = m_manager.mk_family_id("bv"); const unsigned num_sizes = 6; unsigned sizes[num_sizes] = { 1, 2, 8, 16, 24, 32 }; parameter p1(1), p2(2), p3(3), p4(4), p8(8), p16(16), p24(24), p32(32); for (unsigned i = 0; i < num_sizes; ++i) { add_expr(u.mk_numeral(rational(0), sizes[i])); add_expr(u.mk_numeral(rational(1), sizes[i])); } add_expr(u.mk_numeral(rational(2), 2)); add_expr(u.mk_numeral(rational(3), 2)); add_expr(u.mk_numeral(rational(6), 8)); add_expr(u.mk_numeral(rational(7), 8)); add_expr(u.mk_numeral(rational(static_cast(-2)), 32)); add_expr(u.mk_numeral(rational(static_cast(-1)), 32)); for (unsigned i = 0; num_vars > 0; ++i, --num_vars) { i = i % num_sizes; parameter param(sizes[i]); add_var(m_manager.mk_sort(bvfid, BV_SORT, 1, ¶m)); } for (unsigned i = 0; i < num_sizes; ++i) { parameter param(sizes[i]); sort* s = m_manager.mk_sort(bvfid, BV_SORT, 1, ¶m); sort* ss[3] = { s, s, s }; add_func_decl(m_manager.mk_func_decl(bvfid, OP_BNEG, 0, nullptr, 1, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_BADD, 0, nullptr, 2, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_BSUB, 0, nullptr, 2, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_BMUL, 0, nullptr, 2, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_BSDIV, 0, nullptr, 2, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_BUDIV, 0, nullptr, 2, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_BSREM, 0, nullptr, 2, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_BUREM, 0, nullptr, 2, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_BSMOD, 0, nullptr, 2, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_ULEQ, 0, nullptr, 2, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_SLEQ, 0, nullptr, 2, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_UGEQ, 0, nullptr, 2, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_SGEQ, 0, nullptr, 2, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_ULT, 0, nullptr, 2, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_SLT, 0, nullptr, 2, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_UGT, 0, nullptr, 2, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_SGT, 0, nullptr, 2, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_BAND, 0, nullptr, 2, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_BOR, 0, nullptr, 2, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_BNOT, 0, nullptr, 1, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_BXOR, 0, nullptr, 2, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_BXNOR, 0, nullptr, 2, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_BNAND, 0, nullptr, 2, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_BCOMP, 0, nullptr, 2, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_BREDAND, 0, nullptr, 1, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_BREDOR, 0, nullptr, 1, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_BSHL, 0, nullptr, 2, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_BLSHR, 0, nullptr, 2, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_BASHR, 0, nullptr, 2, ss)); add_func_decl(m_manager.mk_func_decl(bfid, OP_EQ, 0, nullptr, 2, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_ROTATE_LEFT, 1, &p1, 1, ss)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_ROTATE_RIGHT, 1, &p1, 1, ss)); } sort* b8 = m_manager.mk_sort(bvfid, BV_SORT, 1, &p8); sort* b16 = m_manager.mk_sort(bvfid, BV_SORT, 1, &p16); sort* b24 = m_manager.mk_sort(bvfid, BV_SORT, 1, &p24); sort* b32 = m_manager.mk_sort(bvfid, BV_SORT, 1, &p32); // OP_CONCAT: { sort* ss[2] = { b8, b8 }; add_func_decl(m_manager.mk_func_decl(bvfid, OP_CONCAT, 0, nullptr, 2, ss)); ss[0] = b16; ss[1] = b8; add_func_decl(m_manager.mk_func_decl(bvfid, OP_CONCAT, 0, nullptr, 2, ss)); ss[0] = b8; ss[1] = b16; add_func_decl(m_manager.mk_func_decl(bvfid, OP_CONCAT, 0, nullptr, 2, ss)); ss[0] = b16; ss[1] = b16; add_func_decl(m_manager.mk_func_decl(bvfid, OP_CONCAT, 0, nullptr, 2, ss)); ss[0] = b24; ss[1] = b8; add_func_decl(m_manager.mk_func_decl(bvfid, OP_CONCAT, 0, nullptr, 2, ss)); ss[0] = b8; ss[1] = b24; add_func_decl(m_manager.mk_func_decl(bvfid, OP_CONCAT, 0, nullptr, 2, ss)); } add_func_decl(m_manager.mk_func_decl(bvfid, OP_SIGN_EXT, 1, &p8, 1, &b8)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_SIGN_EXT, 1, &p8, 1, &b16)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_SIGN_EXT, 1, &p16, 1, &b8)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_SIGN_EXT, 1, &p16, 1, &b16)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_SIGN_EXT, 1, &p8, 1, &b16)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_SIGN_EXT, 1, &p16, 1, &b8)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_SIGN_EXT, 1, &p8, 1, &b24)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_ZERO_EXT, 1, &p8, 1, &b8)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_ZERO_EXT, 1, &p8, 1, &b16)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_ZERO_EXT, 1, &p16, 1, &b8)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_ZERO_EXT, 1, &p16, 1, &b16)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_ZERO_EXT, 1, &p8, 1, &b16)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_ZERO_EXT, 1, &p16, 1, &b8)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_ZERO_EXT, 1, &p8, 1, &b24)); parameter bounds[2] = { parameter(7), parameter(0) }; add_func_decl(m_manager.mk_func_decl(bvfid, OP_EXTRACT, 2, bounds, 1, &b32)); bounds[0] = parameter(15); add_func_decl(m_manager.mk_func_decl(bvfid, OP_EXTRACT, 2, bounds, 1, &b32)); bounds[0] = parameter(23); add_func_decl(m_manager.mk_func_decl(bvfid, OP_EXTRACT, 2, bounds, 1, &b32)); bounds[1] = parameter(8); add_func_decl(m_manager.mk_func_decl(bvfid, OP_EXTRACT, 2, bounds, 1, &b32)); bounds[1] = parameter(16); add_func_decl(m_manager.mk_func_decl(bvfid, OP_EXTRACT, 2, bounds, 1, &b32)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_REPEAT, 1, &p4, 1, &b8)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_REPEAT, 1, &p3, 1, &b8)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_REPEAT, 1, &p2, 1, &b8)); add_func_decl(m_manager.mk_func_decl(bvfid, OP_REPEAT, 1, &p1, 1, &b8)); /* OP_ROTATE_LEFT, OP_ROTATE_RIGHT, */ } void expr_rand::initialize_array(unsigned num_vars, sort* dom, sort* rng) { family_id afid = m_manager.mk_family_id("array"); parameter p1(dom), p2(rng); parameter ps[2] = { p1, p2 }; sort* a = m_manager.mk_sort(afid, ARRAY_SORT, 2, ps); sort* ss[3] = { a, dom, rng }; add_func_decl(m_manager.mk_func_decl(afid, OP_STORE, 0, nullptr, 3, ss)); add_func_decl(m_manager.mk_func_decl(afid, OP_SELECT, 0, nullptr, 2, ss)); for (unsigned i = 0; i < num_vars; ++i) { add_var(a); } } void expr_rand::initialize_basic(unsigned amplification) { family_id bfid = m_manager.get_basic_family_id(); sort* bools[2] = { m_manager.mk_bool_sort(), m_manager.mk_bool_sort() }; for (unsigned i = 0; i < amplification; ++i) { add_func_decl(m_manager.mk_func_decl(bfid, OP_OR, 0, nullptr, 2, bools)); add_func_decl(m_manager.mk_func_decl(bfid, OP_NOT, 0, nullptr, 1, bools)); } map_t::iterator it = m_nodes.begin(); map_t::iterator end = m_nodes.end(); for (; it != end; ++it) { sort* s = it->m_key; sort* ites[3] = { bools[0], s, s }; add_func_decl(m_manager.mk_func_decl(bfid, OP_ITE, 0, nullptr, 3, ites)); } } z3-z3-4.8.7/src/test/fuzzing/expr_rand.h000066400000000000000000000021501356505360400200220ustar00rootroot00000000000000/*++ Copyright (c) 2008 Microsoft Corporation Module Name: expr_rand.h Abstract: Generator of random ASTs. Author: Nikolaj Bjorner 2008-04-10. Revision History: --*/ #ifndef EXPR_RAND_H_ #define EXPR_RAND_H_ #include "ast/ast.h" #include "util/obj_hashtable.h" class expr_rand { ast_manager& m_manager; unsigned m_max_steps; random_gen m_random; typedef obj_map map_t; func_decl_ref_vector m_funcs; map_t m_nodes; public: expr_rand(ast_manager& m); ~expr_rand(); void add_var(sort*); void add_func_decl(func_decl*); void add_expr(expr* t); void get_next(sort* s, expr_ref& e); void initialize_bv(unsigned num_vars); void initialize_arith(unsigned num_vars); void initialize_array(unsigned num_vars, sort* dom, sort* rng); void initialize_basic(unsigned amplification); void seed(unsigned n) { m_random = random_gen(n); } private: void walk(); void walk(unsigned n); func_decl* choose_func_decl(); expr* choose_expr(sort*); }; #endif z3-z3-4.8.7/src/test/get_consequences.cpp000066400000000000000000000070711356505360400202400ustar00rootroot00000000000000/*++ Copyright (c) 2016 Microsoft Corporation --*/ #include "sat/sat_solver/inc_sat_solver.h" #include "ast/bv_decl_plugin.h" #include "ast/datatype_decl_plugin.h" #include "ast/reg_decl_plugins.h" #include "ast/ast_pp.h" #include "tactic/bv/dt2bv_tactic.h" #include "tactic/tactic.h" #include "model/model_smt2_pp.h" #include "tactic/fd_solver/fd_solver.h" static expr_ref mk_const(ast_manager& m, char const* name, sort* s) { return expr_ref(m.mk_const(symbol(name), s), m); } static expr_ref mk_bool(ast_manager& m, char const* name) { return expr_ref(m.mk_const(symbol(name), m.mk_bool_sort()), m); } static expr_ref mk_bv(ast_manager& m, char const* name, unsigned sz) { bv_util bv(m); return expr_ref(m.mk_const(symbol(name), bv.mk_sort(sz)), m); } static void test1() { ast_manager m; reg_decl_plugins(m); bv_util bv(m); params_ref p; ref solver = mk_inc_sat_solver(m, p); expr_ref a = mk_bool(m, "a"), b = mk_bool(m, "b"), c = mk_bool(m, "c"); expr_ref ba = mk_bv(m, "ba", 3), bb = mk_bv(m, "bb", 3), bc = mk_bv(m, "bc", 3); solver->assert_expr(m.mk_implies(a, b)); solver->assert_expr(m.mk_implies(b, c)); expr_ref_vector asms(m), vars(m), conseq(m); asms.push_back(a); vars.push_back(b); vars.push_back(c); vars.push_back(bb); vars.push_back(bc); solver->assert_expr(m.mk_eq(ba, bc)); solver->assert_expr(m.mk_eq(bv.mk_numeral(2, 3), ba)); solver->get_consequences(asms, vars, conseq); std::cout << conseq << "\n"; } void test2() { ast_manager m; reg_decl_plugins(m); bv_util bv(m); datatype_util dtutil(m); params_ref p; datatype_decl_plugin & dt = *(static_cast(m.get_plugin(m.get_family_id("datatype")))); sort_ref_vector new_sorts(m); constructor_decl* R = mk_constructor_decl(symbol("R"), symbol("is-R"), 0, nullptr); constructor_decl* G = mk_constructor_decl(symbol("G"), symbol("is-G"), 0, nullptr); constructor_decl* B = mk_constructor_decl(symbol("B"), symbol("is-B"), 0, nullptr); constructor_decl* constrs[3] = { R, G, B }; datatype_decl * enum_sort = mk_datatype_decl(dtutil, symbol("RGB"), 0, nullptr, 3, constrs); VERIFY(dt.mk_datatypes(1, &enum_sort, 0, nullptr, new_sorts)); sort* rgb = new_sorts[0].get(); expr_ref x = mk_const(m, "x", rgb), y = mk_const(m, "y", rgb), z = mk_const(m, "z", rgb); ptr_vector const& enums = *dtutil.get_datatype_constructors(rgb); expr_ref r = expr_ref(m.mk_const(enums[0]), m); expr_ref g = expr_ref(m.mk_const(enums[1]), m); expr_ref b = expr_ref(m.mk_const(enums[2]), m); ref fd_solver = mk_fd_solver(m, p); fd_solver->assert_expr(m.mk_not(m.mk_eq(x, r))); fd_solver->assert_expr(m.mk_not(m.mk_eq(x, b))); expr_ref_vector asms(m), vars(m), conseq(m); vars.push_back(x); vars.push_back(y); VERIFY(l_true == fd_solver->get_consequences(asms, vars, conseq)); std::cout << conseq << "\n"; conseq.reset(); fd_solver->push(); fd_solver->assert_expr(m.mk_not(m.mk_eq(x, g))); VERIFY(l_false == fd_solver->check_sat(0,nullptr)); fd_solver->pop(1); VERIFY(l_true == fd_solver->get_consequences(asms, vars, conseq)); std::cout << conseq << "\n"; conseq.reset(); model_ref mr; fd_solver->get_model(mr); model_smt2_pp(std::cout << "model:\n", m, *mr.get(), 0); VERIFY(l_true == fd_solver->check_sat(0,nullptr)); fd_solver->get_model(mr); ENSURE(mr.get()); model_smt2_pp(std::cout, m, *mr.get(), 0); } void tst_get_consequences() { test1(); test2(); } z3-z3-4.8.7/src/test/get_implied_equalities.cpp000066400000000000000000000103021356505360400214040ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "api/z3.h" #include "util/trace.h" #include "util/debug.h" static Z3_ast mk_var(Z3_context ctx, char const* name, Z3_sort s) { return Z3_mk_const(ctx, Z3_mk_string_symbol(ctx, name), s); } static Z3_ast mk_int_var(Z3_context ctx, char const* name) { return mk_var(ctx, name, Z3_mk_int_sort(ctx)); } static void tst_get_implied_equalities1() { Z3_config cfg = Z3_mk_config(); Z3_context ctx = Z3_mk_context(cfg); Z3_del_config(cfg); Z3_sort int_ty = Z3_mk_int_sort(ctx); Z3_ast a = mk_int_var(ctx,"a"); Z3_ast b = mk_int_var(ctx,"b"); Z3_ast c = mk_int_var(ctx,"c"); Z3_ast d = mk_int_var(ctx,"d"); Z3_func_decl f = Z3_mk_func_decl(ctx, Z3_mk_string_symbol(ctx,"f"), 1, &int_ty, int_ty); Z3_ast fa = Z3_mk_app(ctx, f, 1, &a); Z3_ast fb = Z3_mk_app(ctx, f, 1, &b); Z3_ast fc = Z3_mk_app(ctx, f, 1, &c); unsigned const num_terms = 7; unsigned i; Z3_ast terms[7] = { a, b, c, d, fa, fb, fc }; unsigned class_ids[7] = { 0, 0, 0, 0, 0, 0, 0 }; Z3_solver solver = Z3_mk_simple_solver(ctx); Z3_solver_inc_ref(ctx, solver); Z3_solver_assert(ctx, solver, Z3_mk_eq(ctx, a, b)); Z3_solver_assert(ctx, solver, Z3_mk_eq(ctx, b, d)); Z3_solver_assert(ctx, solver, Z3_mk_le(ctx, fa, fc)); Z3_solver_assert(ctx, solver, Z3_mk_le(ctx, fc, d)); Z3_get_implied_equalities(ctx, solver, num_terms, terms, class_ids); for (i = 0; i < num_terms; ++i) { printf("Class %s |-> %d\n", Z3_ast_to_string(ctx, terms[i]), class_ids[i]); } ENSURE(class_ids[1] == class_ids[0]); ENSURE(class_ids[2] != class_ids[0]); ENSURE(class_ids[3] == class_ids[0]); ENSURE(class_ids[4] != class_ids[0]); ENSURE(class_ids[5] != class_ids[0]); ENSURE(class_ids[6] != class_ids[0]); ENSURE(class_ids[4] == class_ids[5]); printf("asserting b <= f(a)\n"); Z3_solver_assert(ctx, solver, Z3_mk_le(ctx, b, fa)); Z3_get_implied_equalities(ctx, solver, num_terms, terms, class_ids); for (i = 0; i < num_terms; ++i) { printf("Class %s |-> %d\n", Z3_ast_to_string(ctx, terms[i]), class_ids[i]); } ENSURE(class_ids[1] == class_ids[0]); ENSURE(class_ids[2] != class_ids[0]); ENSURE(class_ids[3] == class_ids[0]); ENSURE(class_ids[4] == class_ids[0]); ENSURE(class_ids[5] == class_ids[0]); ENSURE(class_ids[6] == class_ids[0]); Z3_solver_dec_ref(ctx, solver); /* delete logical context */ Z3_del_context(ctx); } static void tst_get_implied_equalities2() { //enable_trace("after_search"); //enable_trace("get_implied_equalities"); //enable_trace("implied_equalities"); Z3_config cfg = Z3_mk_config(); Z3_context ctx = Z3_mk_context(cfg); Z3_del_config(cfg); Z3_solver solver = Z3_mk_simple_solver(ctx); Z3_solver_inc_ref(ctx, solver); Z3_sort int_ty = Z3_mk_int_sort(ctx); Z3_ast a = mk_int_var(ctx,"a"); Z3_ast b = mk_int_var(ctx,"b"); Z3_ast one = Z3_mk_numeral(ctx, "1", int_ty); Z3_ast two = Z3_mk_numeral(ctx, "2", int_ty); Z3_ast x = Z3_mk_const_array(ctx, int_ty, one); Z3_ast y = Z3_mk_store(ctx, x, one, a); Z3_ast z = Z3_mk_store(ctx, y, two , b); Z3_ast u = Z3_mk_store(ctx, x, two , b); Z3_ast v = Z3_mk_store(ctx, u, one , a); unsigned const num_terms = 5; unsigned i; Z3_ast terms[5] = { x, y, z, u, v}; unsigned class_ids[5] = { 0, 0, 0, 0, 0}; Z3_get_implied_equalities(ctx, solver, num_terms, terms, class_ids); for (i = 0; i < num_terms; ++i) { printf("Class %s |-> %d\n", Z3_ast_to_string(ctx, terms[i]), class_ids[i]); } ENSURE(class_ids[1] != class_ids[0]); ENSURE(class_ids[2] != class_ids[0]); ENSURE(class_ids[3] != class_ids[0]); ENSURE(class_ids[4] != class_ids[0]); ENSURE(class_ids[4] == class_ids[2]); ENSURE(class_ids[2] != class_ids[1]); ENSURE(class_ids[3] != class_ids[1]); ENSURE(class_ids[4] != class_ids[1]); ENSURE(class_ids[3] != class_ids[2]); /* delete logical context */ Z3_solver_dec_ref(ctx, solver); Z3_del_context(ctx); } void tst_get_implied_equalities() { tst_get_implied_equalities1(); tst_get_implied_equalities2(); } z3-z3-4.8.7/src/test/hashtable.cpp000066400000000000000000000055561356505360400166470ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: tst_hashtable.cpp Abstract: Test hashtable module Author: Leonardo de Moura (leonardo) 2006-09-12. Revision History: --*/ #ifdef _WINDOWS #include #include #include #include "util/hashtable.h" struct int_hash_proc { unsigned operator()(int x) const { return x * 3; } }; typedef int_hashtable > int_set; typedef std::unordered_set > > safe_int_set; // typedef safe_int_set int_set; inline bool contains(int_set & h, int i) { // return h.find(i) != h.end(); return h.contains(i); } const int N = 10000; int vals[N]; static void tst1() { int_set h1; int size = 0; for (int i = 1; i < N; i ++) { int v = rand() % (N / 2); h1.insert(v); vals[i] = v; ENSURE(contains(h1, v)); } std::cout << "step1\n"; std::cout.flush(); for (int i = 1; i < N; i ++) { ENSURE(contains(h1, vals[i])); } std::cout << "step2\n"; std::cout.flush(); for (int i = 1; i < N; i += 2) { h1.erase(vals[i]); ENSURE(!contains(h1, vals[i])); } std::cout << "step3\n"; std::cout.flush(); for (int i = 1; i < N; i += 2) { h1.insert(vals[i]); } std::cout << "step4\n"; std::cout.flush(); for (int i = 1; i < N; i ++) { ENSURE(contains(h1, vals[i])); } } static void tst2() { int_set h1; safe_int_set h2; int N = rand() % 1000; for (int i = 0; i < N; i++) { int v = rand()%1000; if (rand() % 3 == 2) { h1.erase(v); h2.erase(v); ENSURE(!contains(h1, v)); } else { h1.insert(v); h2.insert(v); ENSURE(contains(h1, v)); } } { safe_int_set::iterator it = h2.begin(); safe_int_set::iterator end = h2.end(); for(; it != end; ++it) { ENSURE(contains(h1, *it)); } } { int_set::iterator it = h1.begin(); int_set::iterator end = h1.end(); int n = 0; for (; it != end; ++it) { ENSURE(contains(h1, *it)); n++; } ENSURE(n == h1.size()); } ENSURE(h1.size() == h2.size()); // std::cout << "size: " << h1.size() << ", capacity: " << h1.capacity() << "\n"; std::cout.flush(); } static void tst3() { int_set h1; h1.insert(10); h1.insert(20); h1.insert(30); h1.erase(20); int_set h2(h1); ENSURE(h1.contains(10)); ENSURE(!h1.contains(20)); ENSURE(h1.contains(30)); ENSURE(h2.contains(10)); ENSURE(!h2.contains(20)); ENSURE(h2.contains(30)); ENSURE(h2.size() == 2); } void tst_hashtable() { tst3(); for (int i = 0; i < 100; i++) tst2(); tst1(); } #else void tst_hashtable() { } #endif z3-z3-4.8.7/src/test/heap.cpp000066400000000000000000000076321356505360400156260ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: tst_heap.cpp Abstract: Test heap template. Author: Leonardo de Moura (leonardo) 2006-09-18. Revision History: --*/ #include #include "util/util.h" #include "util/heap.h" #include "util/trace.h" #include "util/uint_set.h" // include "util/hashtable.h" struct lt_proc { bool operator()(int v1, int v2) const { return v1 < v2; } }; //struct int_hash_proc { unsigned operator()(int v) const { std::cout << "hash " << v << "\n"; VERIFY(v >= 0); return v; }}; //typedef int_hashtable > int_set; typedef heap int_heap; #define N 10000 static random_gen heap_rand(1); static void tst1() { int_heap h(N); // int_set t; uint_set t; for (int i = 0; i < N * 3; i++) { int val = heap_rand() % N; if (!h.contains(val)) { ENSURE(!t.contains(val)); h.insert(val); t.insert(val); } else { if (!t.contains(val)) { for (int v : t) std::cout << v << "\n"; } ENSURE(t.contains(val)); } } ENSURE(h.check_invariant()); for (int v : t) { ENSURE(h.contains(v)); } while (!h.empty()) { int m1 = h.min_value(); int m2 = h.erase_min(); (void)m1; (void)m2; ENSURE(m1 == m2); ENSURE(-1 < m2); } } int g_value[N]; struct lt_proc2 { bool operator()(int v1, int v2) const { ENSURE(v1 < N && v2 < N); return g_value[v1] < g_value[v2]; } }; typedef heap int_heap2; static void init_values() { for (unsigned i = 0; i < N; i++) g_value[i] = heap_rand(); } #ifdef _TRACE static void dump_heap(const int_heap2 & h, std::ostream & out) { // int_heap2::const_iterator it = h.begin(); // int_heap2::const_iterator end = h.end(); // for (; it != end; ++it) { // out << *it << ":" << g_value[*it] << " "; // } // out << "\n"; } #endif 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 % 1000 == 0) std::cout << "i: " << i << std::endl; int cmd = heap_rand() % 10; if (cmd <= 3) { // insert int val = heap_rand() % N; if (!h.contains(val)) { TRACE("heap", tout << "inserting: " << val << "\n";); h.insert(val); TRACE("heap", dump_heap(h, tout);); ENSURE(h.contains(val)); } } else if (cmd <= 6) { int val = heap_rand() % N; if (h.contains(val)) { TRACE("heap", tout << "removing: " << val << "\n";); h.erase(val); TRACE("heap", dump_heap(h, tout);); ENSURE(!h.contains(val)); } } else if (cmd <= 8) { // increased & decreased int val = heap_rand() % N; int old_v = g_value[val]; int new_v = heap_rand(); if (h.contains(val)) { g_value[val] = new_v; if (old_v < new_v) { TRACE("heap", tout << "value of: " << val << " increased old: " << old_v << " new: " << new_v << "\n";); h.increased(val); } else { TRACE("heap", tout << "value of: " << val << " decreased old: " << old_v << " new: " << new_v << "\n";); h.decreased(val); } } } else { ENSURE(h.check_invariant()); } } ENSURE(h.check_invariant()); } void tst_heap() { // enable_debug("heap"); enable_trace("heap"); unsigned i = 0; while (i < 3) { IF_VERBOSE(1, verbose_stream() << "test\n";); heap_rand.set_seed(i++); tst1(); init_values(); tst2(); } } z3-z3-4.8.7/src/test/heap_trie.cpp000066400000000000000000000026611356505360400166460ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "math/hilbert/heap_trie.h" struct unsigned_le { static bool le(unsigned i, unsigned j) { return i <= j; } }; typedef heap_trie heap_trie_t; static void find_le(heap_trie_t& ht, unsigned num_keys, unsigned const* keys) { statistics st; vector vals; ht.find_all_le(keys, vals); std::cout << "find_le: "; for (unsigned i = 0; i < num_keys; ++i) { std::cout << keys[i] << " "; } std::cout << " |-> "; for (unsigned i = 0; i < vals.size(); ++i) { std::cout << vals[i] << " "; } std::cout << "\n"; ht.collect_statistics(st); st.display(std::cout); } void tst_heap_trie() { unsigned_le le; heap_trie_t ht(le); ht.reset(3); unsigned keys1[3] = { 1, 2, 3}; ht.insert(keys1, 1); unsigned keys2[3] = { 2, 2, 3}; ht.insert(keys2, 2); unsigned keys3[3] = { 1, 1, 3}; ht.insert(keys3, 3); unsigned keys4[3] = { 2, 1, 3}; unsigned keys5[3] = { 2, 3, 3}; unsigned val; VERIFY (ht.find_eq(keys1, val) && val == 1); VERIFY (ht.find_eq(keys2, val) && val == 2); VERIFY (ht.find_eq(keys3, val) && val == 3); VERIFY (!ht.find_eq(keys4, val)); find_le(ht, 3, keys1); find_le(ht, 3, keys2); find_le(ht, 3, keys3); find_le(ht, 3, keys4); find_le(ht, 3, keys5); ht.display(std::cout); } z3-z3-4.8.7/src/test/hilbert_basis.cpp000066400000000000000000000403121356505360400175130ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "math/hilbert/hilbert_basis.h" #include "ast/ast_pp.h" #include "ast/reg_decl_plugins.h" #include "ast/arith_decl_plugin.h" #include "tactic/smtlogics/quant_tactics.h" #include "tactic/tactic.h" #include "solver/tactic2solver.h" #include "solver/solver.h" #include "util/rlimit.h" #include #include #include static bool g_use_ordered_support = false; static bool g_use_ordered_subsumption = false; static bool g_use_support = false; class hilbert_basis_validate { ast_manager& m; void validate_solution(hilbert_basis& hb, vector const& v, bool is_initial); rational eval_ineq(hilbert_basis& hb, unsigned idx, vector const& v, bool& is_eq) { vector w; rational bound; hb.get_ge(idx, w, bound, is_eq); rational sum(0); for (unsigned j = 0; j < v.size(); ++j) { sum += w[j]*v[j]; } sum -= bound; return sum; } public: hilbert_basis_validate(ast_manager& m); expr_ref mk_validate(hilbert_basis& hb); }; hilbert_basis_validate::hilbert_basis_validate(ast_manager& m): m(m) { } void hilbert_basis_validate::validate_solution(hilbert_basis& hb, vector const& v, bool is_initial) { unsigned sz = hb.get_num_ineqs(); rational bound; for (unsigned i = 0; i < sz; ++i) { bool is_eq; vector w; rational bound; hb.get_ge(i, w, bound, is_eq); rational sum(0); for (unsigned j = 0; j < v.size(); ++j) { sum += w[j]*v[j]; } if (sum >= bound && !is_eq) { continue; } if (sum == bound && is_eq) { continue; } // homogeneous solutions should be non-negative. if (!is_initial && sum.is_nonneg()) { continue; } // validation failed. std::cout << "validation failed\n"; std::cout << "constraint: "; for (unsigned j = 0; j < v.size(); ++j) { std::cout << v[j] << " "; } std::cout << (is_eq?" = ":" >= ") << bound << "\n"; std::cout << "vector: "; for (unsigned j = 0; j < w.size(); ++j) { std::cout << w[j] << " "; } std::cout << "\n"; std::cout << "sum: " << sum << "\n"; } } expr_ref hilbert_basis_validate::mk_validate(hilbert_basis& hb) { arith_util a(m); unsigned sz = hb.get_basis_size(); vector v; // check that claimed solution really satisfies inequalities: for (unsigned i = 0; i < sz; ++i) { bool is_initial; hb.get_basis_solution(i, v, is_initial); validate_solution(hb, v, is_initial); } // check that solutions satisfying inequalities are in solution. // build a formula that says solutions to linear inequalities // coincide with linear combinations of basis. vector offsets, increments; expr_ref_vector xs(m), vars(m); expr_ref var(m); svector names; sort_ref_vector sorts(m); #define mk_mul(_r,_x) (_r.is_one()?((expr*)_x):((expr*)a.mk_mul(a.mk_numeral(_r,true),_x))) for (unsigned i = 0; i < sz; ++i) { bool is_initial; hb.get_basis_solution(i, v, is_initial); for (unsigned j = 0; xs.size() < v.size(); ++j) { xs.push_back(m.mk_fresh_const("x", a.mk_int())); } if (is_initial) { expr_ref_vector tmp(m); for (unsigned j = 0; j < v.size(); ++j) { tmp.push_back(a.mk_numeral(v[j], true)); } offsets.push_back(tmp); } else { var = m.mk_var(vars.size(), a.mk_int()); expr_ref_vector tmp(m); for (unsigned j = 0; j < v.size(); ++j) { tmp.push_back(mk_mul(v[j], var)); } std::stringstream name; name << "u" << i; increments.push_back(tmp); vars.push_back(var); names.push_back(symbol(name.str().c_str())); sorts.push_back(a.mk_int()); } } expr_ref_vector bounds(m); for (unsigned i = 0; i < vars.size(); ++i) { bounds.push_back(a.mk_ge(vars[i].get(), a.mk_numeral(rational(0), true))); } expr_ref_vector fmls(m); expr_ref fml(m), fml1(m), fml2(m); for (unsigned i = 0; i < offsets.size(); ++i) { expr_ref_vector eqs(m); eqs.append(bounds); for (unsigned j = 0; j < xs.size(); ++j) { expr_ref_vector sum(m); sum.push_back(offsets[i][j].get()); for (unsigned k = 0; k < increments.size(); ++k) { sum.push_back(increments[k][j].get()); } eqs.push_back(m.mk_eq(xs[j].get(), a.mk_add(sum.size(), sum.c_ptr()))); } fml = m.mk_and(eqs.size(), eqs.c_ptr()); if (!names.empty()) { fml = m.mk_exists(names.size(), sorts.c_ptr(), names.c_ptr(), fml); } fmls.push_back(fml); } fml1 = m.mk_or(fmls.size(), fmls.c_ptr()); fmls.reset(); sz = hb.get_num_ineqs(); for (unsigned i = 0; i < sz; ++i) { bool is_eq; vector w; rational bound; hb.get_ge(i, w, bound, is_eq); expr_ref_vector sum(m); for (unsigned j = 0; j < w.size(); ++j) { if (!w[j].is_zero()) { sum.push_back(mk_mul(w[j], xs[j].get())); } } expr_ref lhs(m), rhs(m); lhs = a.mk_add(sum.size(), sum.c_ptr()); rhs = a.mk_numeral(bound, true); if (is_eq) { fmls.push_back(a.mk_eq(lhs, rhs)); } else { fmls.push_back(a.mk_ge(lhs, rhs)); } } fml2 = m.mk_and(fmls.size(), fmls.c_ptr()); fml = m.mk_eq(fml1, fml2); bounds.reset(); for (unsigned i = 0; i < xs.size(); ++i) { if (!hb.get_is_int(i)) { bounds.push_back(a.mk_ge(xs[i].get(), a.mk_numeral(rational(0), true))); } } if (!bounds.empty()) { fml = m.mk_implies(m.mk_and(bounds.size(), bounds.c_ptr()), fml); } return fml; } hilbert_basis* g_hb = nullptr; static double g_start_time; static void display_statistics(hilbert_basis& hb) { double time = static_cast(clock()) - g_start_time; statistics st; hb.collect_statistics(st); st.display(std::cout); std::cout << "time: " << (time / CLOCKS_PER_SEC) << " secs\n"; } static void on_ctrl_c(int) { signal (SIGINT, SIG_DFL); display_statistics(*g_hb); raise(SIGINT); } #if 0 static void validate_sat(hilbert_basis& hb) { ast_manager m; reg_decl_plugins(m); hilbert_basis_validate val(m); expr_ref fml = val.mk_validate(hb); return; std::cout << mk_pp(fml, m) << "\n"; fml = m.mk_not(fml); params_ref p; tactic_ref tac = mk_lra_tactic(m, p); ref sol = mk_tactic2solver(m, tac.get(), p); sol->assert_expr(fml); lbool r = sol->check_sat(0,0); std::cout << r << "\n"; } #endif static void saturate_basis(hilbert_basis& hb) { signal(SIGINT, on_ctrl_c); g_hb = &hb; g_start_time = static_cast(clock()); hb.set_use_ordered_support(g_use_ordered_support); hb.set_use_support(g_use_support); hb.set_use_ordered_subsumption(g_use_ordered_subsumption); lbool is_sat = hb.saturate(); switch(is_sat) { case l_true: std::cout << "sat\n"; hb.display(std::cout); //validate_sat(hb); break; case l_false: std::cout << "unsat\n"; break; case l_undef: std::cout << "undef\n"; break; } display_statistics(hb); } /** n - number of variables. k - subset of variables to be non-zero bound - numeric value of upper and lower bound num_ineqs - number of inequalities to create */ static void gorrila_test(unsigned seed, unsigned n, unsigned k, unsigned bound, unsigned num_ineqs) { std::cout << "Gorrila test\n"; random_gen rand(seed); reslimit rl; hilbert_basis hb(rl); ENSURE(0 < bound); ENSURE(k <= n); int ibound = static_cast(bound); for (unsigned i = 0; i < num_ineqs; ++i) { vector nv; nv.resize(n); rational a0; unsigned num_selected = 0; while (num_selected < k) { unsigned s = rand(n); if (nv[s].is_zero()) { nv[s] = rational(ibound - static_cast(rand(2*bound+1))); if (!nv[s].is_zero()) { ++num_selected; } } } a0 = rational(ibound - static_cast(rand(2*bound+1))); hb.add_ge(nv, a0); } hb.display(std::cout << "Saturate\n"); saturate_basis(hb); } static vector vec(int i, int j) { vector nv; nv.resize(2); nv[0] = rational(i); nv[1] = rational(j); return nv; } static vector vec(int i, int j, int k) { vector nv; nv.resize(3); nv[0] = rational(i); nv[1] = rational(j); nv[2] = rational(k); return nv; } static vector vec(int i, int j, int k, int l) { vector nv; nv.resize(4); nv[0] = rational(i); nv[1] = rational(j); nv[2] = rational(k); nv[3] = rational(l); return nv; } static vector vec(int i, int j, int k, int l, int m) { vector nv; nv.resize(5); nv[0] = rational(i); nv[1] = rational(j); nv[2] = rational(k); nv[3] = rational(l); nv[4] = rational(m); return nv; } static vector vec(int i, int j, int k, int l, int x, int y, int z) { vector nv; nv.resize(7); nv[0] = rational(i); nv[1] = rational(j); nv[2] = rational(k); nv[3] = rational(l); nv[4] = rational(x); nv[5] = rational(y); nv[6] = rational(z); return nv; } // example 9, Ajili, Contenjean // x + y - 2z = 0 // x - z = 0 // -y + z <= 0 static void tst1() { reslimit rl; hilbert_basis hb(rl); hb.add_eq(vec(1,1,-2)); hb.add_eq(vec(1,0,-1)); hb.add_le(vec(0,1,-1)); saturate_basis(hb); } // example 10, Ajili, Contenjean // 23x - 12y - 9z <= 0 // x - 8y - 8z <= 0 void tst2() { reslimit rl; hilbert_basis hb(rl); hb.add_eq(vec(-23,12,9)); hb.add_eq(vec(-1,8,8)); saturate_basis(hb); } // example 6, Ajili, Contenjean // 3x + 2y - z - 2u <= 0 static void tst3() { reslimit rl; hilbert_basis hb(rl); hb.add_le(vec(3,2,-1,-2)); saturate_basis(hb); } #define R rational // Sigma_1, table 1, Ajili, Contejean static void tst4() { reslimit rl; hilbert_basis hb(rl); hb.add_le(vec( 0,-2, 1, 3, 2,-2, 3), R(3)); hb.add_le(vec(-1, 7, 0, 1, 3, 5,-4), R(2)); hb.add_le(vec( 0,-1, 1,-1,-1, 0, 0), R(2)); hb.add_le(vec(-2, 0, 1, 4, 0, 0,-2), R(1)); hb.add_le(vec(-3, 2,-2, 2,-4,-1, 0), R(8)); hb.add_le(vec( 3,-2, 2,-2, 4, 1, 0), R(3)); hb.add_le(vec( 1, 0, 0,-1, 0, 1, 0), R(4)); hb.add_le(vec( 1,-2, 0, 0, 0, 0, 0), R(2)); hb.add_le(vec( 1, 1, 0, 0,-1, 0, 1), R(4)); hb.add_le(vec( 1, 0, 0, 0,-1, 0, 0), R(9)); saturate_basis(hb); } // Sigma_2 table 1, Ajili, Contejean static void tst5() { reslimit rl; hilbert_basis hb(rl); hb.add_le(vec( 1, 2,-1, 1), R(3)); hb.add_le(vec( 2, 4, 1, 2), R(12)); hb.add_le(vec( 1, 4, 2, 1), R(9)); hb.add_le(vec( 1, 1, 0,-1), R(10)); hb.add_le(vec( 1, 1,-1, 0), R(6)); hb.add_le(vec( 1,-1, 0, 0), R(0)); hb.add_le(vec( 0, 0, 1,-1), R(2)); saturate_basis(hb); } // Sigma_3 table 1, Ajili, Contejean static void tst6() { reslimit rl; hilbert_basis hb(rl); hb.add_le(vec( 4, 3, 0), R(6)); hb.add_le(vec(-3,-4, 0), R(-1)); hb.add_le(vec( 4, 0,-3), R(3)); hb.add_le(vec(-3, 0, 4), R(7)); hb.add_le(vec( 4, 0,-3), R(23)); hb.add_le(vec( 0,-3, 4), R(11)); saturate_basis(hb); } // Sigma_4 table 1, Ajili, Contejean static void tst7() { reslimit rl; hilbert_basis hb(rl); hb.add_eq(vec( 1, 1, 1, 0), R(5)); hb.add_le(vec( 2, 1, 0, 1), R(6)); hb.add_le(vec( 1, 2, 1, 1), R(7)); hb.add_le(vec( 1, 3,-1, 2), R(8)); hb.add_le(vec( 1, 2,-9,-12), R(-11)); hb.add_le(vec( 0, 0,-1, 3), R(10)); saturate_basis(hb); } // Sigma_5 table 1, Ajili, Contejean static void tst8() { reslimit rl; hilbert_basis hb(rl); hb.add_le(vec( 2, 1, 1), R(2)); hb.add_le(vec( 1, 2, 3), R(5)); hb.add_le(vec( 2, 2, 3), R(6)); hb.add_le(vec( 1,-1,-3), R(-2)); saturate_basis(hb); } // Sigma_6 table 1, Ajili, Contejean static void tst9() { reslimit rl; hilbert_basis hb(rl); hb.add_le(vec( 1, 2, 3), R(11)); hb.add_le(vec( 2, 2, 5), R(13)); hb.add_le(vec( 1,-1,-11), R(3)); saturate_basis(hb); } // Sigma_7 table 1, Ajili, Contejean static void tst10() { reslimit rl; hilbert_basis hb(rl); hb.add_le(vec( 1,-1,-1,-3), R(2)); hb.add_le(vec(-2, 3, 3,-5), R(3)); saturate_basis(hb); } // Sigma_8 table 1, Ajili, Contejean static void tst11() { reslimit rl; hilbert_basis hb(rl); hb.add_le(vec( 7,-2,11, 3, -5), R(5)); saturate_basis(hb); } // Sigma_9 table 1, Ajili, Contejean static void tst12() { reslimit rl; hilbert_basis hb(rl); hb.add_eq(vec( 1,-2,-3,4), R(0)); hb.add_le(vec(100,45,-78,-67), R(0)); saturate_basis(hb); } // Sigma_10 table 1, Ajili, Contejean static void tst13() { reslimit rl; hilbert_basis hb(rl); hb.add_le(vec( 23, -56, -34, 12, 11), R(0)); saturate_basis(hb); } // Sigma_11 table 1, Ajili, Contejean static void tst14() { reslimit rl; hilbert_basis hb(rl); hb.add_eq(vec(1, 0, -4, 8), R(2)); hb.add_le(vec(12,19,-11,-7), R(-7)); saturate_basis(hb); } static void tst15() { reslimit rl; hilbert_basis hb(rl); hb.add_le(vec(1, 0), R(1)); hb.add_le(vec(0, 1), R(1)); saturate_basis(hb); } static void tst16() { reslimit rl; hilbert_basis hb(rl); hb.add_le(vec(1, 0), R(100)); saturate_basis(hb); } static void tst17() { reslimit rl; hilbert_basis hb(rl); hb.add_eq(vec(1, 0), R(0)); hb.add_eq(vec(-1, 0), R(0)); hb.add_eq(vec(0, 2), R(0)); hb.add_eq(vec(0, -2), R(0)); saturate_basis(hb); } static void tst18() { reslimit rl; hilbert_basis hb(rl); hb.add_eq(vec(0, 1), R(0)); hb.add_eq(vec(1, -1), R(2)); saturate_basis(hb); } static void tst19() { reslimit rl; hilbert_basis hb(rl); hb.add_eq(vec(0, 1, 0), R(0)); hb.add_eq(vec(1, -1, 0), R(2)); saturate_basis(hb); } static void test_A_5_5_3() { reslimit rl; hilbert_basis hb(rl); for (unsigned i = 0; i < 15; ++i) { vector v; for (unsigned j = 0; j < 5; ++j) { for (unsigned k = 0; k < 15; ++k) { v.push_back(rational(k == i)); } } hb.add_ge(v, R(0)); } for (unsigned i = 1; i <= 15; ++i) { vector v; for (unsigned k = 1; k <= 5; ++k) { for (unsigned l = 1; l <= 5; ++l) { for (unsigned j = 1; j <= 3; ++j) { bool one = ((j*k <= i) && (((i - j) % 3) == 0)); // fixme v.push_back(rational(one)); } } } hb.add_ge(v, R(0)); } // etc. saturate_basis(hb); } void tst_hilbert_basis() { std::cout << "hilbert basis test\n"; // tst3(); // return; g_use_ordered_support = true; test_A_5_5_3(); return; tst18(); return; tst19(); return; tst17(); if (true) { tst1(); tst2(); tst3(); tst4(); tst4(); tst4(); tst4(); tst4(); tst4(); tst5(); tst6(); tst7(); tst8(); tst9(); tst10(); tst11(); tst12(); tst13(); tst14(); tst15(); tst16(); gorrila_test(0, 4, 3, 20, 5); gorrila_test(1, 4, 3, 20, 5); //gorrila_test(2, 4, 3, 20, 5); //gorrila_test(0, 4, 2, 20, 5); //gorrila_test(0, 4, 2, 20, 5); } else { gorrila_test(0, 10, 7, 20, 11); } return; std::cout << "ordered support\n"; g_use_ordered_support = true; tst4(); std::cout << "non-ordered support\n"; g_use_ordered_support = false; tst4(); } z3-z3-4.8.7/src/test/horn_subsume_model_converter.cpp000066400000000000000000000041121356505360400226570ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "tactic/horn_subsume_model_converter.h" #include "ast/arith_decl_plugin.h" #include "model/model_smt2_pp.h" #include "ast/reg_decl_plugins.h" void tst_horn_subsume_model_converter() { ast_manager m; reg_decl_plugins(m); arith_util a(m); ptr_vector ints; ints.push_back(a.mk_int()); ints.push_back(a.mk_int()); ints.push_back(a.mk_int()); func_decl_ref p(m), q(m), r(m); p = m.mk_func_decl(symbol("p"), 2, ints.c_ptr(), m.mk_bool_sort()); q = m.mk_func_decl(symbol("q"), 2, ints.c_ptr(), m.mk_bool_sort()); r = m.mk_func_decl(symbol("r"), 2, ints.c_ptr(), m.mk_bool_sort()); ref mc = alloc(horn_subsume_model_converter,m); model_ref mr = alloc(model, m); mc->insert(p, m.mk_app(q, a.mk_numeral(rational(1), true), a.mk_numeral(rational(2), true))); model_converter_ref mcr = mc.get(); apply(mcr, mr); model_smt2_pp(std::cout, m, *mr.get(), 0); mr = alloc(model, m); mc->insert(p, m.mk_app(q, a.mk_numeral(rational(3), true), a.mk_numeral(rational(5), true))); apply(mcr, mr); model_smt2_pp(std::cout, m, *mr.get(), 0); mr = alloc(model, m); mc->insert(p, m.mk_app(r, m.mk_var(0,a.mk_int()), m.mk_var(1, a.mk_int()))); apply(mcr, mr); model_smt2_pp(std::cout, m, *mr.get(), 0); mr = alloc(model, m); app_ref head1(m); expr_ref body1(m), body2(m); func_decl_ref pred(m); head1 = m.mk_app(p, m.mk_var(0, a.mk_int()), m.mk_var(0, a.mk_int())); body1 = m.mk_app(q, m.mk_var(1, a.mk_int()), m.mk_var(2, a.mk_int())); VERIFY(mc->mk_horn(head1, body1, pred, body2)); mc->insert(pred, body2); apply(mcr, mr); model_smt2_pp(std::cout, m, *mr.get(), 0); mr = alloc(model, m); head1 = m.mk_app(p, m.mk_var(0, a.mk_int()), m.mk_var(0, a.mk_int())); body1 = m.mk_app(q, m.mk_var(1, a.mk_int()), m.mk_var(0, a.mk_int())); VERIFY(mc->mk_horn(head1, body1, pred, body2)); mc->insert(pred, body2); apply(mcr, mr); model_smt2_pp(std::cout, m, *mr.get(), 0); } z3-z3-4.8.7/src/test/hwf.cpp000066400000000000000000000041361356505360400154710ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: hwf.cpp Abstract: hwf repros... Author: Leonardo de Moura (leonardo) 2012-08-23. Revision History: --*/ #include "util/hwf.h" #include "util/f2n.h" #include "util/rational.h" static void bug_set_double() { hwf_manager m; hwf a; m.set(a, 0.1); ENSURE(m.is_regular(a)); m.set(a, 1.1); ENSURE(m.is_regular(a)); m.set(a, 11.3); ENSURE(m.is_regular(a)); m.set(a, 0.0); ENSURE(m.is_regular(a)); } static void bug_to_rational() { hwf_manager m; hwf a; unsynch_mpq_manager mq; scoped_mpq r(mq); double ad = 0, rd = 0; m.set(a, 0.0); m.to_rational(a, r); ad = m.to_double(a); rd = mq.get_double(r); VERIFY(ad == rd); m.set(a, 1.0); m.to_rational(a, r); ad = m.to_double(a); rd = mq.get_double(r); VERIFY(ad == rd); m.set(a, 1.5); m.to_rational(a, r); ad = m.to_double(a); rd = mq.get_double(r); ENSURE(ad == rd); m.set(a, 0.875); m.to_rational(a, r); ad = m.to_double(a); rd = mq.get_double(r); ENSURE(ad == rd); m.set(a, -1.0); m.to_rational(a, r); ad = m.to_double(a); rd = mq.get_double(r); ENSURE(ad == rd); m.set(a, -1.5); m.to_rational(a, r); ad = m.to_double(a); rd = mq.get_double(r); ENSURE(ad == rd); m.set(a, -0.875); m.to_rational(a, r); ad = m.to_double(a); rd = mq.get_double(r); ENSURE(ad == rd); m.set(a, 0.1); m.to_rational(a, r); ad = m.to_double(a); rd = mq.get_double(r); #ifdef _WINDOWS // CMW: This one depends on the rounding mode, // which is implicit in both hwf::set and in mpq::to_double. double diff = (ad-rd); ENSURE(diff >= -DBL_EPSILON && diff <= DBL_EPSILON); #endif } static void bug_is_int() { unsigned raw_val[2] = { 2147483648u, 1077720461u }; double val = *(double*)(raw_val); std::cout << val << "\n"; hwf_manager m; hwf a; m.set(a, val); ENSURE(!m.is_int(a)); } void tst_hwf() { bug_is_int(); bug_set_double(); bug_to_rational(); } z3-z3-4.8.7/src/test/im_float_config.h000066400000000000000000000043301356505360400174650ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: im_float_config.h Abstract: Auxiliary class for testing intervals with software floats end points. Author: Leonardo de Moura (leonardo) 2012-08-21. Revision History: --*/ #ifndef IM_FLOAT_CONFIG_H_ #define IM_FLOAT_CONFIG_H_ #include "util/f2n.h" #include "util/mpf.h" #include "util/hwf.h" template class im_float_config { f2n m_manager; public: typedef f2n numeral_manager; typedef typename f_manager::numeral numeral; im_float_config(f_manager & m, unsigned ebits = 11, unsigned sbits = 53):m_manager(m, ebits, sbits) {} struct interval { numeral m_lower; numeral m_upper; }; void round_to_minus_inf() { m_manager.round_to_minus_inf(); } void round_to_plus_inf() { m_manager.round_to_plus_inf(); } void set_rounding(bool up) { m_manager.set_rounding(up); } // Getters numeral const & lower(interval const & a) const { return a.m_lower; } numeral const & upper(interval const & a) const { return a.m_upper; } numeral & lower(interval & a) { return a.m_lower; } numeral & upper(interval & a) { return a.m_upper; } bool lower_is_inf(interval const & a) const { return m_manager.m().is_ninf(a.m_lower); } bool upper_is_inf(interval const & a) const { return m_manager.m().is_pinf(a.m_upper); } bool lower_is_open(interval const & a) const { return lower_is_inf(a); } bool upper_is_open(interval const & a) const { return upper_is_inf(a); } // Setters void set_lower(interval & a, numeral const & n) { m_manager.set(a.m_lower, n); } void set_upper(interval & a, numeral const & n) { m_manager.set(a.m_upper, n); } void set_lower_is_open(interval & a, bool v) {} void set_upper_is_open(interval & a, bool v) {} void set_lower_is_inf(interval & a, bool v) { if (v) m_manager.m().mk_ninf(m_manager.ebits(), m_manager.sbits(), a.m_lower); } void set_upper_is_inf(interval & a, bool v) { if (v) m_manager.m().mk_pinf(m_manager.ebits(), m_manager.sbits(), a.m_upper); } // Reference to numeral manager numeral_manager & m() const { return const_cast(m_manager); } }; #endif z3-z3-4.8.7/src/test/inf_rational.cpp000066400000000000000000000122351356505360400173510ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: tst_inf_rational.cpp Abstract: Test for Rational numbers with infinitesimals Author: Leonardo de Moura (leonardo) 2006-09-18. Nikolaj Bjorner (nbjorner) 2006-10-24. Revision History: --*/ #include "util/inf_rational.h" static void tst0() { inf_rational n(rational(0), false); TRACE("inf_rational", tout << n << "\n";); ENSURE(n < inf_rational::zero()); ENSURE(!(n >= inf_rational::zero())); } void test_inc_dec( inf_rational& r, inf_rational const & b_8_5, inf_rational const & b_7_5, inf_rational const & b_7_10, inf_rational const & b_17_10 ) { r += rational(1,5); ENSURE (r == b_8_5); r -= rational(1,5); ENSURE (r == b_7_5); r += inf_rational(1,5); ENSURE (r == b_8_5); r -= inf_rational(1,5); ENSURE (r == b_7_5); r /= rational(2,1); ENSURE (r == b_7_10); inf_rational r_pre = r++; ENSURE (r_pre == b_7_10); ENSURE (r == b_17_10); inf_rational r_post = --r; ENSURE (r_post == b_7_10); ENSURE (r == b_7_10); r_post = ++r; ENSURE (r_post == b_17_10); ENSURE (r == b_17_10); r_pre = r--; ENSURE (r_pre == b_17_10); ENSURE (r == b_7_10); r_pre = r; r_pre += inf_rational(1,2); r_post = r_pre; r_post -= inf_rational(1,2); ENSURE(r == r_post); ENSURE(r + inf_rational(1,2) == r_pre); r_pre = r; r_pre /= rational(2,1); r_post = r_pre; r_post /= rational(1,2); ENSURE(r == r_post); ENSURE(rational(1,2) * r == r_pre); ENSURE(r == r_pre / rational(1,2)); } void tst_inf_rational() { tst0(); inf_rational r1; inf_rational r2(r1); ENSURE (r1 == r2); inf_rational r3(1); inf_rational r4(0); ENSURE (r4 == r1); ENSURE (r3 != r4); inf_rational r5(0,1); inf_rational r6(1,1); inf_rational r7(2,2); inf_rational r8(7,5); ENSURE (r1 == r5); ENSURE (r6 == r3); ENSURE (r7 == r3); inf_rational r9(rational(7,5)); ENSURE (r8 == r9); r9.reset(); ENSURE (r1 == r9); ENSURE (r1.is_int()); ENSURE (!r8.is_int()); ENSURE (0 == r1.get_int64()); r9 = r8; ENSURE (r8 == r9); inf_rational n = numerator(r7); inf_rational d = denominator(r7); { inf_rational b_8_5 = inf_rational(8,5); inf_rational b_7_5 = inf_rational(7,5); inf_rational b_7_10 = inf_rational(7,10); inf_rational b_17_10 = inf_rational(17,10); inf_rational r = r9; test_inc_dec(r, b_8_5, b_7_5, b_7_10, b_17_10); } { inf_rational b_8_5 = inf_rational(rational(8,5),true); inf_rational b_7_5 = inf_rational(rational(7,5),true); inf_rational b_7_10 = inf_rational(rational(7,5),true) / rational(2); inf_rational b_17_10 = b_7_10 + inf_rational(1); inf_rational r (rational(7,5),true); test_inc_dec(r, b_8_5, b_7_5, b_7_10, b_17_10); } ENSURE(inf_rational(rational(1,2),true) > inf_rational(rational(1,2))); ENSURE(inf_rational(rational(1,2),false) < inf_rational(rational(1,2))); ENSURE(inf_rational(rational(1,2),true) >= inf_rational(rational(1,2))); ENSURE(inf_rational(rational(1,2)) >= inf_rational(rational(1,2),false)); ENSURE(inf_rational(rational(1,2),false) != inf_rational(rational(1,2))); ENSURE(inf_rational(rational(1,2),true) != inf_rational(rational(1,2))); ENSURE(inf_rational(rational(1,2),false) != inf_rational(rational(1,2),true)); inf_rational h_neg(rational(1,2),false); inf_rational h_pos(rational(1,2),true); h_neg.neg(); ENSURE(h_neg == -inf_rational(rational(1,2),false)); h_neg.neg(); ENSURE(h_neg == inf_rational(rational(1,2),false)); ENSURE(r1.is_zero() && !r1.is_one() && !r1.is_neg() && r1.is_nonneg() && r1.is_nonpos() && !r1.is_pos()); ENSURE(!r3.is_zero() && r3.is_one() && !r3.is_neg() && r3.is_nonneg() && !r3.is_nonpos() && r3.is_pos()); ENSURE(floor(inf_rational(rational(1,2),false)) == rational()); ENSURE(floor(inf_rational(rational(1,2))) == rational()); ENSURE(floor(inf_rational(rational(),false)) == rational(-1)); ENSURE(floor(inf_rational(rational())) == rational()); ENSURE(floor(inf_rational(rational(),true)) == rational()); ENSURE(floor(inf_rational(rational(1),false)) == rational()); ENSURE(floor(inf_rational(rational(1))) == rational(1)); ENSURE(floor(inf_rational(rational(1),true)) == rational(1)); ENSURE(ceil(inf_rational(rational(1,2),false)) == rational(1)); ENSURE(ceil(inf_rational(rational(1,2))) == rational(1)); ENSURE(ceil(inf_rational(rational(),false)) == rational()); ENSURE(ceil(inf_rational(rational())) == rational()); ENSURE(ceil(inf_rational(rational(),true)) == rational(1)); ENSURE(ceil(inf_rational(rational(1),false)) == rational(1)); ENSURE(ceil(inf_rational(rational(1))) == rational(1)); ENSURE(ceil(inf_rational(rational(1),true)) == rational(2)); inf_rational x(rational(1,2),true); inf_rational y(1,2); x.swap(y); ENSURE (x == inf_rational(1,2)); ENSURE (y == inf_rational(rational(1,2),true)); ENSURE(inf_rational(1,2) == abs(-inf_rational(1,2))); } z3-z3-4.8.7/src/test/interval.cpp000066400000000000000000000342151356505360400165320ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: interval.h Abstract: Goodies/Templates for interval arithmetic Author: Leonardo de Moura (leonardo) 2012-07-19. Revision History: --*/ #include #include "math/interval/interval_def.h" #include "util/dependency.h" #include "util/mpq.h" #include "ast/ast.h" #include "util/debug.h" #include "util/rlimit.h" template class interval_manager; typedef im_default_config::interval interval; static void display_preamble(std::ostream & out) { out << "(set-info :status unsat)\n"; out << "(set-option :auto-config true)\n"; out << "(set-option :numeral-as-real true)\n"; out << "(declare-const a Real)\n"; out << "(declare-const b Real)\n"; } static void display_smt2_pos_numeral(std::ostream & out, unsynch_mpq_manager & m, mpq const & n) { if (m.is_int(n)) { m.display(out, n); } else { out << "(/ "; m.display(out, n.numerator()); out << " "; m.display(out, n.denominator()); out << ")"; } } static void display_smt2_numeral(std::ostream & out, unsynch_mpq_manager & m, mpq const & n) { if (m.is_neg(n)) { scoped_mpq n_copy(m); n_copy = n; out << "(- "; n_copy.neg(); display_smt2_pos_numeral(out, m, n_copy); out << ")"; } else { display_smt2_pos_numeral(out, m, n); } } static void display_constraint(std::ostream & out, unsynch_mpq_manager & m, char const * a, interval const & i, bool include_lower = true, bool include_upper = true) { out << "(and true"; if (!i.m_lower_inf && include_lower) { out << " (" << (i.m_lower_open ? "<" : "<=") << " "; display_smt2_numeral(out, m, i.m_lower); out << " " << a << ")"; } if (!i.m_upper_inf && include_upper) { out << " (" << (i.m_upper_open ? "<" : "<=") << " " << a << " "; display_smt2_numeral(out, m, i.m_upper); out << ")"; } out << ")"; } static void assert_hyp(std::ostream & out, unsynch_mpq_manager & m, char const * a, interval const & i, bool include_lower = true, bool include_upper = true) { out << "(assert "; display_constraint(out, m, a, i, include_lower, include_upper); out << ")\n"; } static void assert_conj(std::ostream & out, unsynch_mpq_manager & m, char const * a, interval const & i, bool include_lower = true, bool include_upper = true) { out << "(assert (not "; display_constraint(out, m, a, i, include_lower, include_upper); out << "))\n"; } #if 0 static bool mk_interval(im_default_config & cfg, interval & a, bool l_inf, bool l_open, int l_val, bool u_inf, bool u_open, int u_val) { if (!l_inf && !u_inf) { if (l_val > u_val) return false; if (l_val == u_val && (l_open || u_open)) return false; } if (l_inf) { a.m_lower_open = true; a.m_lower_inf = true; } else { a.m_lower_open = l_open; a.m_lower_inf = false; cfg.m().set(a.m_lower, l_val); } if (u_inf) { a.m_upper_open = true; a.m_upper_inf = true; } else { a.m_upper_open = u_open; a.m_upper_inf = false; cfg.m().set(a.m_upper, u_val); } return true; } #endif template static void mk_random_interval(T & cfg, interval & a, unsigned magnitude) { switch (rand()%3) { case 0: // Neg, Neg if (rand()%4 == 0) { a.m_lower_open = true; a.m_lower_inf = true; a.m_upper_open = (rand()%2 == 0); a.m_upper_inf = false; cfg.m().set(a.m_upper, -static_cast((rand()%magnitude))); } else { a.m_upper_open = (rand()%2 == 0); a.m_upper_inf = false; int upper = -static_cast((rand()%magnitude)); cfg.m().set(a.m_upper, upper); a.m_lower_open = (rand()%2 == 0); a.m_lower_inf = false; cfg.m().set(a.m_lower, upper - static_cast(rand()%magnitude) - (a.m_lower_open || a.m_upper_open ? 1 : 0)); } break; case 1: // Neg, Pos if (rand()%4 == 0) { a.m_lower_open = true; a.m_lower_inf = true; } else { a.m_lower_open = (rand()%2 == 0); a.m_lower_inf = false; cfg.m().set(a.m_lower, -static_cast((rand()%magnitude)) - 1); } if (rand()%4 == 0) { a.m_upper_open = true; a.m_upper_inf = true; } else { a.m_upper_open = (rand()%2 == 0); a.m_upper_inf = false; cfg.m().set(a.m_upper, rand()%magnitude + 1); } break; default: // Neg, Neg if (rand()%4 == 0) { a.m_upper_open = true; a.m_upper_inf = true; a.m_lower_open = (rand()%2 == 0); a.m_lower_inf = false; cfg.m().set(a.m_lower, (rand()%magnitude)); } else { a.m_lower_open = (rand()%2 == 0); a.m_lower_inf = false; int lower = (rand()%magnitude); cfg.m().set(a.m_lower, lower); a.m_upper_open = (rand()%2 == 0); a.m_upper_inf = false; cfg.m().set(a.m_upper, lower + rand()%magnitude + (a.m_lower_open || a.m_upper_open ? 1 : 0)); } break; } } #define BUFFER_SZ 256 static int g_problem_id = 0; static char g_buffer[BUFFER_SZ]; char const * get_next_file_name() { #ifdef _WINDOWS sprintf_s(g_buffer, BUFFER_SZ, "interval_lemma_%d.smt2", g_problem_id); #else sprintf(g_buffer, "interval_lemma_%d.smt2", g_problem_id); #endif g_problem_id++; return g_buffer; } static void display_lemmas(unsynch_mpq_manager & nm, char const * result_term, interval const & a, interval const & b, interval const & r, interval_deps const & deps) { { std::ofstream out(get_next_file_name()); display_preamble(out); assert_hyp(out, nm, "a", a, dep_in_lower1(deps.m_lower_deps), dep_in_upper1(deps.m_lower_deps)); assert_hyp(out, nm, "b", b, dep_in_lower2(deps.m_lower_deps), dep_in_upper2(deps.m_lower_deps)); assert_conj(out, nm, result_term, r, true, false); out << "(check-sat)\n"; } { std::ofstream out(get_next_file_name()); display_preamble(out); assert_hyp(out, nm, "a", a, dep_in_lower1(deps.m_upper_deps), dep_in_upper1(deps.m_upper_deps)); assert_hyp(out, nm, "b", b, dep_in_lower2(deps.m_upper_deps), dep_in_upper2(deps.m_upper_deps)); assert_conj(out, nm, result_term, r, false, true); out << "(check-sat)\n"; } } #define MK_BINARY(NAME, RES_TERM) \ static void tst_ ## NAME(unsigned N, unsigned magnitude) { \ reslimit rl; \ unsynch_mpq_manager nm; \ interval_manager im(rl, nm); \ interval a, b, r; \ \ for (unsigned i = 0; i < N; i++) { \ mk_random_interval(im, a, magnitude); \ mk_random_interval(im, b, magnitude); \ interval_deps deps; \ im.NAME(a, b, r, deps); \ \ display_lemmas(nm, RES_TERM, a, b, r, deps); \ } \ im.del(a); im.del(b); im.del(r); \ } MK_BINARY(mul, "(* a b)"); MK_BINARY(add, "(+ a b)"); MK_BINARY(sub, "(- a b)"); static void tst_neg(unsigned N, unsigned magnitude) { reslimit rl; unsynch_mpq_manager nm; interval_manager im(rl, nm); interval a, b, r; for (unsigned i = 0; i < N; i++) { mk_random_interval(im, a, magnitude); interval_deps deps; im.neg(a, r, deps); display_lemmas(nm, "(- a)", a, b, r, deps); } im.del(a); im.del(b); im.del(r); } static void tst_pw_2(unsigned N, unsigned magnitude) { reslimit rl; unsynch_mpq_manager nm; interval_manager im(rl, nm); interval a, b, r; for (unsigned i = 0; i < N; i++) { mk_random_interval(im, a, magnitude); interval_deps deps; im.power(a, 2, r, deps); display_lemmas(nm, "(* a a)", a, b, r, deps); } im.del(a); im.del(b); im.del(r); } static void tst_pw_3(unsigned N, unsigned magnitude) { reslimit rl; unsynch_mpq_manager nm; interval_manager im(rl, nm); interval a, b, r; for (unsigned i = 0; i < N; i++) { mk_random_interval(im, a, magnitude); interval_deps deps; im.power(a, 3, r, deps); display_lemmas(nm, "(* a a a)", a, b, r, deps); } im.del(a); im.del(b); im.del(r); } static void tst_root_2(unsigned N, unsigned magnitude, unsigned precision) { reslimit rl; unsynch_mpq_manager nm; interval_manager im(rl, nm); interval a, b, r; scoped_mpq p(nm); p = precision; nm.inv(p); unsigned i = 0; while (i < N) { mk_random_interval(im, a, magnitude); if (!im.lower_is_neg(a)) { i++; interval_deps deps; im.nth_root(a, 2, p, r, deps); display_lemmas(nm, "(^ a (/ 1.0 2.0))", a, b, r, deps); } } im.del(a); im.del(b); im.del(r); } static void tst_root_3(unsigned N, unsigned magnitude, unsigned precision) { reslimit rl; unsynch_mpq_manager nm; interval_manager im(rl, nm); interval a, b, r; scoped_mpq p(nm); p = precision; nm.inv(p); unsigned i = 0; while (i < N) { mk_random_interval(im, a, magnitude); i++; interval_deps deps; im.nth_root(a, 3, p, r, deps); display_lemmas(nm, "(^ a (/ 1.0 3.0))", a, b, r, deps); } im.del(a); im.del(b); im.del(r); } static void tst_inv(unsigned N, unsigned magnitude) { reslimit rl; unsynch_mpq_manager nm; interval_manager im(rl, nm); interval a, b, r; for (unsigned i = 0; i < N; i++) { while (true) { mk_random_interval(im, a, magnitude); if (!im.contains_zero(a)) break; } interval_deps deps; im.inv(a, r, deps); display_lemmas(nm, "(/ 1 a)", a, b, r, deps); } im.del(a); im.del(b); im.del(r); } static void tst_div(unsigned N, unsigned magnitude) { reslimit rl; unsynch_mpq_manager nm; interval_manager im(rl, nm); interval a, b, r; for (unsigned i = 0; i < N; i++) { mk_random_interval(im, a, magnitude); while (true) { mk_random_interval(im, b, magnitude); if (!im.contains_zero(b)) break; } interval_deps deps; im.div(a, b, r, deps); display_lemmas(nm, "(/ a b)", a, b, r, deps); } im.del(a); im.del(b); im.del(r); } #include "test/im_float_config.h" #if 0 static void tst_float() { unsynch_mpq_manager qm; mpf_manager fm; interval_manager > im(fm); im_float_config::interval a, b, c; scoped_mpq minus_one_third(qm), one_third(qm), two_third(qm), minus_two_third(qm); qm.set(minus_one_third, -1, 3); qm.set(one_third, 1, 3); qm.set(two_third, 2, 3); qm.set(minus_two_third, -2, 3); ifc.round_to_minus_inf(); ifc.m().set(a.m_lower, minus_one_third); ifc.round_to_plus_inf(); ifc.m().set(a.m_upper, two_third); ifc.round_to_minus_inf(); ifc.m().set(b.m_lower, minus_two_third); ifc.round_to_plus_inf(); ifc.m().set(b.m_upper, one_third); im.display(std::cout, a); std::cout << "\n"; im.display(std::cout, b); std::cout << "\n"; interval_deps deps; im.add(a, b, c, deps); im.display(std::cout, c); std::cout << "\n"; im.del(a); im.del(b); im.del(r); } #endif void tst_pi() { reslimit rl; unsynch_mpq_manager nm; interval_manager im(rl, nm); interval r; for (unsigned i = 0; i < 8; i++) { im.pi(i, r); nm.display_decimal(std::cout, im.lower(r), 32); std::cout << " "; nm.display_decimal(std::cout, im.upper(r), 32); std::cout << "\n"; ENSURE(nm.lt(im.lower(r), im.upper(r))); } im.del(r); } #if 0 static void tst_pi_float() { std::cout << "pi float...\n"; reslimit rl; unsynch_mpq_manager qm; mpf_manager fm; im_float_config ifc(fm, 22, 106); interval_manager > im(rl, ifc); scoped_mpq q(qm); im_float_config::interval r; for (unsigned i = 0; i < 8; i++) { im.pi(i, r); fm.to_rational(im.lower(r), q); qm.display_decimal(std::cout, q, 32); std::cout << " "; fm.to_rational(im.upper(r), q); qm.display_decimal(std::cout, q, 32); std::cout << "\n"; } del_f_interval(ifc, r); } #endif #define NUM_TESTS 1000 #define SMALL_MAG 3 #define MID_MAG 10 void tst_interval() { // enable_trace("interval_bug"); // tst_float(); // return; // enable_trace("interval_nth_root"); // tst_pi(); // tst_pi_float(); tst_root_2(NUM_TESTS, MID_MAG, 100); tst_root_3(NUM_TESTS, MID_MAG, 100); tst_div(NUM_TESTS, SMALL_MAG); tst_inv(NUM_TESTS, SMALL_MAG); tst_pw_2(NUM_TESTS, SMALL_MAG); tst_pw_3(NUM_TESTS, SMALL_MAG); tst_neg(NUM_TESTS, SMALL_MAG); tst_sub(NUM_TESTS, SMALL_MAG); tst_mul(NUM_TESTS, SMALL_MAG); tst_add(NUM_TESTS, SMALL_MAG); } z3-z3-4.8.7/src/test/karr.cpp000066400000000000000000000174021356505360400156440ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "util/rlimit.h" #include "math/hilbert/hilbert_basis.h" /* Test generation of linear congruences a la Karr. */ namespace karr { struct matrix { vector > A; vector b; unsigned size() const { return A.size(); } void reset() { A.reset(); b.reset(); } matrix& operator=(matrix const& other) { reset(); append(other); return *this; } void append(matrix const& other) { A.append(other.A); b.append(other.b); } void display(std::ostream& out) { for (unsigned i = 0; i < A.size(); ++i) { for (unsigned j = 0; j < A[i].size(); ++j) { out << A[i][j] << " "; } out << " = " << -b[i] << "\n"; } } }; // treat src as a homogeneous matrix. void dualizeH(matrix& dst, matrix const& src) { reslimit rl; hilbert_basis hb(rl); for (unsigned i = 0; i < src.size(); ++i) { vector v(src.A[i]); v.push_back(src.b[i]); hb.add_eq(v, rational(0)); } for (unsigned i = 0; i < 1 + src.A[0].size(); ++i) { hb.set_is_int(i); } lbool is_sat = hb.saturate(); hb.display(std::cout); VERIFY(is_sat == l_true); dst.reset(); unsigned basis_size = hb.get_basis_size(); for (unsigned i = 0; i < basis_size; ++i) { bool is_initial; vector soln; hb.get_basis_solution(i, soln, is_initial); if (!is_initial) { dst.b.push_back(soln.back()); soln.pop_back(); dst.A.push_back(soln); } } } // treat src as an inhomegeneous matrix. void dualizeI(matrix& dst, matrix const& src) { reslimit rl; hilbert_basis hb(rl); for (unsigned i = 0; i < src.size(); ++i) { hb.add_eq(src.A[i], -src.b[i]); } for (unsigned i = 0; i < src.A[0].size(); ++i) { hb.set_is_int(i); } lbool is_sat = hb.saturate(); hb.display(std::cout); VERIFY(is_sat == l_true); dst.reset(); unsigned basis_size = hb.get_basis_size(); bool first_initial = true; for (unsigned i = 0; i < basis_size; ++i) { bool is_initial; vector soln; hb.get_basis_solution(i, soln, is_initial); if (is_initial && first_initial) { dst.A.push_back(soln); dst.b.push_back(rational(1)); first_initial = false; } else if (!is_initial) { dst.A.push_back(soln); dst.b.push_back(rational(0)); } } } void juxtapose(matrix& dst, matrix const& M, matrix const& N) { dst = M; dst.append(N); } void join(matrix& dst, matrix const& M, matrix const& N) { matrix MD, ND, dstD; dualizeI(MD, M); dualizeI(ND, N); juxtapose(dstD, MD, ND); dualizeH(dst, dstD); } void joinD(matrix& dst, matrix const& MD, matrix const& ND) { matrix dstD; juxtapose(dstD, MD, ND); dualizeH(dst, dstD); } void transition( matrix& dst, matrix const& src, matrix const& Ab) { matrix T; // length of rows in Ab are twice as long as // length of rows in src. ENSURE(2*src.A[0].size() == Ab.A[0].size()); vector zeros; for (unsigned i = 0; i < src.A[0].size(); ++i) { zeros.push_back(rational(0)); } for (unsigned i = 0; i < src.size(); ++i) { T.A.push_back(src.A[i]); T.A.back().append(zeros); } T.b.append(src.b); T.append(Ab); T.display(std::cout << "T:\n"); matrix TD; dualizeI(TD, T); TD.display(std::cout << "TD:\n"); for (unsigned i = 0; i < TD.size(); ++i) { vector v; v.append(src.size(), TD.A[i].c_ptr() + src.size()); dst.A.push_back(v); dst.b.push_back(TD.b[i]); } dst.display(std::cout << "dst\n"); } static vector V(int i, int j) { vector v; v.push_back(rational(i)); v.push_back(rational(j)); return v; } static vector V(int i, int j, int k, int l) { vector v; v.push_back(rational(i)); v.push_back(rational(j)); v.push_back(rational(k)); v.push_back(rational(l)); return v; } #if 0 static vector V(int i, int j, int k, int l, int m) { vector v; v.push_back(rational(i)); v.push_back(rational(j)); v.push_back(rational(k)); v.push_back(rational(l)); v.push_back(rational(m)); return v; } #endif static vector V(int i, int j, int k, int l, int x, int y, int z) { vector v; v.push_back(rational(i)); v.push_back(rational(j)); v.push_back(rational(k)); v.push_back(rational(l)); v.push_back(rational(x)); v.push_back(rational(y)); v.push_back(rational(z)); return v; } #define R(_x_) rational(_x_) static void tst1() { matrix Theta; matrix Ab; // Theta.A.push_back(V(1, 0)); Theta.b.push_back(R(0)); Theta.A.push_back(V(0, 1)); Theta.b.push_back(R(-2)); Theta.display(std::cout << "Theta\n"); Ab.A.push_back(V(-1, 0, 1, 0)); Ab.b.push_back(R(1)); Ab.A.push_back(V(-1, -2, 0, 1)); Ab.b.push_back(R(1)); Ab.display(std::cout << "Ab\n"); matrix ThetaD; dualizeI(ThetaD, Theta); ThetaD.display(std::cout); matrix t1D, e1; transition(t1D, Theta, Ab); joinD(e1, t1D, ThetaD); t1D.display(std::cout << "t1D\n"); e1.display(std::cout << "e1\n"); matrix t2D, e2; transition(t2D, e1, Ab); joinD(e2, t2D, ThetaD); t2D.display(std::cout << "t2D\n"); e2.display(std::cout << "e2\n"); } void tst2() { /** 0 0 0 0 0 0 0 = 0 0 0 0 0 0 0 0 = 0 0 0 0 0 0 0 0 = 0 0 0 0 0 0 0 0 = 0 0 0 0 0 1 0 0 = 0 0 0 0 0 -1 0 0 = 0 0 1 0 0 0 0 0 = 0 0 -1 0 0 0 0 0 = 0 0 0 0 2 0 0 0 = 0 0 0 0 -2 0 0 0 = 0 */ matrix ND; ND.A.push_back(V(0,0,0,0,1,0,0)); ND.b.push_back(R(0)); ND.A.push_back(V(0,0,0,0,-1,0,0)); ND.b.push_back(R(0)); ND.A.push_back(V(0,1,0,0,0,0,0)); ND.b.push_back(R(0)); ND.A.push_back(V(0,-1,0,0,0,0,0)); ND.b.push_back(R(0)); ND.A.push_back(V(0,0,0,2,0,0,0)); ND.b.push_back(R(0)); ND.A.push_back(V(0,0,0,-2,0,0,0)); ND.b.push_back(R(0)); ND.display(std::cout << "ND\n"); matrix N; dualizeH(N, ND); N.display(std::cout << "N\n"); } void tst3() { /** 0 0 0 0 1 0 0 = 0 0 0 0 0 -1 0 0 = 0 0 1 0 0 0 0 0 = 0 0 -1 0 0 0 0 0 = 0 0 0 0 2 0 0 0 = 0 0 0 0 -2 0 0 0 = 0 */ matrix ND; ND.A.push_back(V(1,0)); ND.b.push_back(R(0)); ND.A.push_back(V(0,2)); ND.b.push_back(R(0)); ND.display(std::cout << "ND\n"); matrix N; dualizeH(N, ND); N.display(std::cout << "N\n"); } }; void tst_karr() { karr::tst3(); return; karr::tst1(); } z3-z3-4.8.7/src/test/list.cpp000066400000000000000000000022021356505360400156500ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: list.cpp Abstract: Author: Leonardo de Moura (leonardo) 2007-07-10. Revision History: --*/ #include "util/trace.h" #include "util/util.h" #include "util/region.h" #include "util/list.h" static void tst1() { region r; list * l1 = new (r) list(10); list * l2 = new (r) list(20, l1); list * l3 = new (r) list(30); list * l4 = new (r) list(40, l3); ENSURE(append(r, l1, static_cast *>(nullptr)) == l1); ENSURE(append(r, l2, static_cast *>(nullptr)) == l2); ENSURE(append(r, static_cast *>(nullptr), l2) == l2); ENSURE(append(r, static_cast *>(nullptr), static_cast *>(nullptr)) == nullptr); TRACE("list", display(tout, l2->begin(), l2->end()); tout << "\n";); list * l5 = append(r, l4, l2); TRACE("list", display(tout, l5->begin(), l5->end()); tout << "\n";); list * l6 = append(r, l5, l5); (void) l6; TRACE("list", display(tout, l6->begin(), l6->end()); tout << "\n";); } void tst_list() { tst1(); } z3-z3-4.8.7/src/test/lp/000077500000000000000000000000001356505360400146105ustar00rootroot00000000000000z3-z3-4.8.7/src/test/lp/CMakeLists.txt000066400000000000000000000010011356505360400173400ustar00rootroot00000000000000add_executable(lp_tst EXCLUDE_FROM_ALL lp_main.cpp lp.cpp $ $ $ $ ) target_compile_definitions(lp_tst PRIVATE ${Z3_COMPONENT_CXX_DEFINES}) target_compile_options(lp_tst PRIVATE ${Z3_COMPONENT_CXX_FLAGS}) target_include_directories(lp_tst PRIVATE ${Z3_COMPONENT_EXTRA_INCLUDE_DIRS}) target_link_libraries(lp_tst PRIVATE ${Z3_DEPENDENT_LIBS}) z3_append_linker_flag_list_to_target(lp_tst ${Z3_DEPENDENT_EXTRA_CXX_LINK_FLAGS}) z3-z3-4.8.7/src/test/lp/argument_parser.h000066400000000000000000000112151356505360400201570ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #include #include #include #include #include namespace lp { class argument_parser { std::unordered_map m_options; std::unordered_map m_options_with_after_string; std::set m_used_options; std::unordered_map m_used_options_with_after_string; std::vector m_free_args; std::vector m_args; public: std::string m_error_message; argument_parser(unsigned argn, char * const* args) { for (unsigned i = 0; i < argn; i++) { m_args.push_back(std::string(args[i])); } } void add_option(std::string s) { add_option_with_help_string(s, ""); } void add_option_with_help_string(std::string s, std::string help_string) { m_options[s]=help_string; } void add_option_with_after_string(std::string s) { add_option_with_after_string_with_help(s, ""); } void add_option_with_after_string_with_help(std::string s, std::string help_string) { m_options_with_after_string[s]=help_string; } bool parse() { bool status_is_ok = true; for (unsigned i = 0; i < m_args.size(); i++) { std::string ar = m_args[i]; if (m_options.find(ar) != m_options.end() ) m_used_options.insert(ar); else if (m_options_with_after_string.find(ar) != m_options_with_after_string.end()) { if (i == m_args.size() - 1) { m_error_message = "Argument is missing after "+ar; return false; } i++; m_used_options_with_after_string[ar] = m_args[i]; } else { if (starts_with(ar, "-") || starts_with(ar, "//")) status_is_ok = false; m_free_args.push_back(ar); } } return status_is_ok; } bool contains(std::unordered_map & m, std::string s) { return m.find(s) != m.end(); } bool contains(std::set & m, std::string s) { return m.find(s) != m.end(); } bool option_is_used(std::string option) { return contains(m_used_options, option) || contains(m_used_options_with_after_string, option); } std::string get_option_value(std::string option) { auto t = m_used_options_with_after_string.find(option); if (t != m_used_options_with_after_string.end()){ return t->second; } return std::string(); } bool starts_with(std::string s, char const * prefix) { return starts_with(s, std::string(prefix)); } bool starts_with(std::string s, std::string prefix) { return s.substr(0, prefix.size()) == prefix; } std::string usage_string() { std::string ret = ""; std::vector unknown_options; for (const auto & t : m_free_args) { if (starts_with(t, "-") || starts_with(t, "\\")) { unknown_options.push_back(t); } } if (!unknown_options.empty()) { ret = "Unknown options:"; } for (const auto & unknownOption : unknown_options) { ret += unknownOption; ret += ","; } ret += "\n"; ret += "Usage:\n"; for (auto allowed_option : m_options) 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.empty()? " \"option value\"":("\""+ s.second+"\"")) + "\n"; } return ret; } void print() { 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; } std::cout << "options are: " << std::endl; for (const std::string & s : m_used_options) { std::cout << s << std::endl; } for (auto & t : m_used_options_with_after_string) { std::cout << t.first << " " << t.second << std::endl; } if (!m_free_args.empty()) { std::cout << "free arguments are: " << std::endl; for (auto & t : m_free_args) { std::cout << t << " " << std::endl; } } } }; } z3-z3-4.8.7/src/test/lp/gomory_test.h000066400000000000000000000217641356505360400173460ustar00rootroot00000000000000namespace lp { #include "util/lp/lp_utils.h" struct gomory_test { gomory_test( std::function name_function_p, std::function get_value_p, std::function at_low_p, std::function at_upper_p, std::function lower_bound_p, std::function upper_bound_p, std::function column_lower_bound_constraint_p, std::function column_upper_bound_constraint_p ) : m_name_function(name_function_p), get_value(get_value_p), at_low(at_low_p), at_upper(at_upper_p), lower_bound(lower_bound_p), upper_bound(upper_bound_p), column_lower_bound_constraint(column_lower_bound_constraint_p), column_upper_bound_constraint(column_upper_bound_constraint_p) {} std::function m_name_function; std::function get_value; std::function at_low; std::function at_upper; std::function lower_bound; std::function upper_bound; std::function column_lower_bound_constraint; std::function column_upper_bound_constraint; bool is_real(unsigned) { return false; } // todo: test real case void print_row(std::ostream& out, vector> & row ) { bool first = true; for (const auto & it : row) { auto val = it.first; if (first) { first = false; } else { if (numeric_traits::is_pos(val)) { out << " + "; } else { out << " - "; val = -val; } } if (val == -numeric_traits::one()) out << " - "; else if (val != numeric_traits::one()) out << T_to_string(val); out << m_name_function(it.second); } } void real_case_in_gomory_cut(const mpq & a, unsigned x_j, mpq & k, lar_term& pol, explanation & expl, const mpq& f_0, const mpq& one_minus_f_0) { TRACE("gomory_cut_detail_real", tout << "real\n";); mpq new_a; if (at_low(x_j)) { if (a.is_pos()) { new_a = a / (1 - f_0); } else { new_a = a / f_0; new_a.neg(); } k.addmul(new_a, lower_bound(x_j).x); // is it a faster operation than // k += lower_bound(x_j).x * new_a; expl.push_justification(column_lower_bound_constraint(x_j), new_a); } else { lp_assert(at_upper(x_j)); if (a.is_pos()) { new_a = a / f_0; new_a.neg(); // the upper terms are inverted. } else { new_a = a / (mpq(1) - f_0); } k.addmul(new_a, upper_bound(x_j).x); // k += upper_bound(x_j).x * new_a; expl.push_justification(column_upper_bound_constraint(x_j), new_a); } TRACE("gomory_cut_detail_real", tout << a << "*v" << x_j << " k: " << k << "\n";); pol.add_monomial(new_a, x_j); } void int_case_in_gomory_cut(const mpq & a, unsigned x_j, mpq & k, lar_term & t, explanation& expl, mpq & lcm_den, const mpq& f_0, const mpq& one_minus_f_0) { lp_assert(is_integer(x_j)); lp_assert(!a.is_int()); lp_assert(f_0 > zero_of_type() && f_0 < one_of_type()); mpq f_j = fractional_part(a); TRACE("gomory_cut_detail", tout << a << " x_j = " << x_j << ", k = " << k << "\n"; tout << "f_j: " << f_j << "\n"; tout << "f_0: " << f_0 << "\n"; tout << "1 - f_0: " << one_minus_f_0 << "\n"; tout << "at_low(" << x_j << ") = " << at_low(x_j) << std::endl; ); lp_assert (!f_j.is_zero()); mpq new_a; if (at_low(x_j)) { if (f_j <= one_minus_f_0) { new_a = f_j / one_minus_f_0; } else { new_a = (1 - f_j) / f_0; } k.addmul(new_a, lower_bound(x_j).x); expl.push_justification(column_lower_bound_constraint(x_j), new_a); } else { lp_assert(at_upper(x_j)); if (f_j <= f_0) { new_a = f_j / f_0; } else { new_a = (mpq(1) - f_j) / (one_minus_f_0); } new_a.neg(); // the upper terms are inverted k.addmul(new_a, upper_bound(x_j).x); expl.push_justification(column_upper_bound_constraint(x_j), new_a); } TRACE("gomory_cut_detail", tout << "new_a: " << new_a << " k: " << k << "\n";); t.add_monomial(new_a, x_j); lcm_den = lcm(lcm_den, denominator(new_a)); } void report_conflict_from_gomory_cut(mpq &k) { lp_assert(false); } void adjust_term_and_k_for_some_ints_case_gomory(lar_term& t, mpq& k, mpq &lcm_den) { lp_assert(!t.is_empty()); auto pol = t.coeffs_as_vector(); t.clear(); if (pol.size() == 1) { TRACE("gomory_cut_detail", tout << "pol.size() is 1" << std::endl;); unsigned v = pol[0].second; lp_assert(is_integer(v)); const mpq& a = pol[0].first; k /= a; if (a.is_pos()) { // we have av >= k if (!k.is_int()) k = ceil(k); // switch size t.add_monomial(- mpq(1), v); k.neg(); } else { if (!k.is_int()) k = floor(k); t.add_monomial(mpq(1), v); } } else { TRACE("gomory_cut_detail", tout << "pol.size() > 1" << std::endl;); lcm_den = lcm(lcm_den, denominator(k)); TRACE("gomory_cut_detail", tout << "k: " << k << " lcm_den: " << lcm_den << "\n"; for (unsigned i = 0; i < pol.size(); i++) { tout << pol[i].first << " " << pol[i].second << "\n"; } tout << "k: " << k << "\n";); lp_assert(lcm_den.is_pos()); if (!lcm_den.is_one()) { // normalize coefficients of integer parameters to be integers. for (auto & pi: pol) { pi.first *= lcm_den; SASSERT(!is_integer(pi.second) || pi.first.is_int()); } k *= lcm_den; } TRACE("gomory_cut_detail", tout << "after *lcm\n"; for (unsigned i = 0; i < pol.size(); i++) { tout << pol[i].first << " * v" << pol[i].second << "\n"; } tout << "k: " << k << "\n";); // negate everything to return -pol <= -k for (const auto & pi: pol) t.add_monomial(-pi.first, pi.second); k.neg(); } TRACE("gomory_cut_detail", tout << "k = " << k << std::endl;); lp_assert(k.is_int()); } void print_term(lar_term & t, std::ostream & out) { vector> row; for (auto p : t.m_coeffs) row.push_back(std::make_pair(p.second, p.first)); print_row(out, row); } void mk_gomory_cut(lar_term& t, mpq& k, explanation & expl, unsigned inf_col, vector> & row) { enable_trace("gomory_cut"); enable_trace("gomory_cut_detail"); TRACE("gomory_cut", tout << "applying cut at:\n"; print_row(tout, row); tout << std::endl << "inf_col = " << inf_col << std::endl; ); // gomory will be t >= k k = 1; mpq lcm_den(1); unsigned x_j; mpq a; bool some_int_columns = false; mpq f_0 = fractional_part(get_value(inf_col)); mpq one_min_f_0 = 1 - f_0; for ( auto pp : row) { a = pp.first; x_j = pp.second; if (x_j == inf_col) continue; // make the format compatible with the format used in: Integrating Simplex with DPLL(T) a.neg(); if (is_real(x_j)) real_case_in_gomory_cut(a, x_j, k, t, expl, f_0, one_min_f_0); else { if (a.is_int()) continue; // f_j will be zero and no monomial will be added some_int_columns = true; int_case_in_gomory_cut(a, x_j, k, t, expl, lcm_den, f_0, one_min_f_0); } } if (t.is_empty()) return report_conflict_from_gomory_cut(k); if (some_int_columns) adjust_term_and_k_for_some_ints_case_gomory(t, k, lcm_den); TRACE("gomory_cut", tout<<"new cut :"; print_term(t, tout); tout << " >= " << k << std::endl;); } }; } z3-z3-4.8.7/src/test/lp/lp.cpp000066400000000000000000003703411356505360400157370ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #include #if _LINUX_ #include #endif #include #include #include #include #include #include #include #include #include #include #include "util/lp/lp_utils.h" #include "util/lp/lp_primal_simplex.h" #include "util/lp/mps_reader.h" #include "test/lp/smt_reader.h" #include "util/lp/binary_heap_priority_queue.h" #include "test/lp/argument_parser.h" #include "test/lp/test_file_reader.h" #include "util/lp/indexed_value.h" #include "util/lp/lar_solver.h" #include "util/lp/numeric_pair.h" #include "util/lp/binary_heap_upair_queue.h" #include "util/lp/stacked_value.h" #include "util/lp/int_set.h" #include "util/stopwatch.h" #include #include "test/lp/gomory_test.h" #include "util/lp/matrix.h" #include "util/lp/hnf.h" #include "util/lp/square_sparse_matrix_def.h" #include "util/lp/lu_def.h" #include "util/lp/general_matrix.h" #include "util/lp/bound_propagator.h" namespace lp { unsigned seed = 1; class my_bound_propagator : public bound_propagator { public: my_bound_propagator(lar_solver & ls): bound_propagator(ls) {} void consume(mpq const& v, lp::constraint_index j) override {} }; random_gen g_rand; static unsigned my_random() { return g_rand(); } struct simple_column_namer:public column_namer { std::string get_column_name(unsigned j) const override { return std::string("x") + T_to_string(j); } }; template void test_matrix(square_sparse_matrix & a) { auto m = a.dimension(); // copy a to b in the reversed order square_sparse_matrix b(m, m); std::cout << "copy b to a"<< std::endl; for (int row = m - 1; row >= 0; row--) for (int col = m - 1; col >= 0; col --) { b(row, col) = (T const&) a(row, col); } std::cout << "zeroing b in the reverse order"<< std::endl; for (int row = m - 1; row >= 0; row--) for (int col = m - 1; col >= 0; col --) b.set(row, col, T(0)); for (unsigned row = 0; row < m; row ++) for (unsigned col = 0; col < m; col ++) a.set(row, col, T(0)); unsigned i = my_random() % m; unsigned j = my_random() % m; auto t = T(1); a.set(i, j, t); lp_assert(a.get(i, j) == t); unsigned j1; if (j < m - 1) { j1 = m - 1; a.set(i, j1, T(2)); } } void tst1() { std::cout << "testing the minimal matrix with 1 row and 1 column" << std::endl; square_sparse_matrix m0(1, 1); m0.set(0, 0, 1); // print_matrix(m0); m0.set(0, 0, 0); // print_matrix(m0); test_matrix(m0); unsigned rows = 2; square_sparse_matrix m(rows, rows); std::cout << "setting m(0,1)=" << std::endl; m.set(0, 1, 11); m.set(0, 0, 12); // print_matrix(m); test_matrix(m); square_sparse_matrix m1(2, 2); m1.set(0, 0, 2); m1.set(1, 0, 3); // print_matrix(m1); std::cout << " zeroing matrix 2 by 2" << std::endl; m1.set(0, 0, 0); m1.set(1, 0, 0); // print_matrix(m1); test_matrix(m1); std::cout << "printing zero matrix 3 by 1" << std::endl; square_sparse_matrix m2(3, 3); // print_matrix(m2); m2.set(0, 0, 1); m2.set(2, 0, 2); std::cout << "printing matrix 3 by 1 with a gap" << std::endl; // print_matrix(m2); test_matrix(m2); square_sparse_matrix m10by9(10, 10); m10by9.set(0, 1, 1); m10by9(0, 1) = 4; double test = m10by9(0, 1); std::cout << "got " << test << std::endl; m10by9.set(0, 8, 8); m10by9.set(3, 4, 7); m10by9.set(3, 2, 5); m10by9.set(3, 8, 99); m10by9.set(3, 2, 6); m10by9.set(1, 8, 9); m10by9.set(4, 0, 40); m10by9.set(0, 0, 10); std::cout << "printing matrix 10 by 9" << std::endl; // print_matrix(m10by9); test_matrix(m10by9); std::cout <<"zeroing m10by9\n"; #ifdef Z3DEBUG for (unsigned int i = 0; i < m10by9.dimension(); i++) for (unsigned int j = 0; j < m10by9.column_count(); j++) m10by9.set(i, j, 0); #endif // print_matrix(m10by9); } vector allocate_basis_heading(unsigned count) { // the rest of initialization will be handled by lu_QR vector basis_heading(count, -1); return basis_heading; } void init_basic_part_of_basis_heading(vector & basis, vector & basis_heading) { lp_assert(basis_heading.size() >= basis.size()); unsigned m = basis.size(); for (unsigned i = 0; i < m; i++) { unsigned column = basis[i]; basis_heading[column] = i; } } void init_non_basic_part_of_basis_heading(vector & basis_heading, vector & non_basic_columns) { non_basic_columns.clear(); for (int j = basis_heading.size(); j--;){ if (basis_heading[j] < 0) { non_basic_columns.push_back(j); // the index of column j in m_nbasis is (- basis_heading[j] - 1) basis_heading[j] = - static_cast(non_basic_columns.size()); } } } void init_basis_heading_and_non_basic_columns_vector(vector & basis, vector & basis_heading, vector & non_basic_columns) { init_basic_part_of_basis_heading(basis, basis_heading); init_non_basic_part_of_basis_heading(basis_heading, non_basic_columns); } void change_basis(unsigned entering, unsigned leaving, vector& basis, vector& nbasis, vector & basis_heading) { int place_in_basis = basis_heading[leaving]; int place_in_non_basis = - basis_heading[entering] - 1; basis_heading[entering] = place_in_basis; basis_heading[leaving] = -place_in_non_basis - 1; basis[place_in_basis] = entering; nbasis[place_in_non_basis] = leaving; } #ifdef Z3DEBUG void test_small_lu(lp_settings & settings) { std::cout << " test_small_lu" << std::endl; static_matrix m(3, 6); vector basis(3); basis[0] = 0; basis[1] = 1; basis[2] = 3; m(0, 0) = 1; m(0, 2)= 3.9; m(2, 3) = 11; m(0, 5) = -3; m(1, 1) = 4; m(1, 4) = 7; m(2, 0) = 1.8; m(2, 2) = 5; m(2, 4) = 2; m(2, 5) = 8; #ifdef Z3DEBUG print_matrix(m, std::cout); #endif vector heading = allocate_basis_heading(m.column_count()); vector non_basic_columns; init_basis_heading_and_non_basic_columns_vector(basis, heading, non_basic_columns); lu> l(m, basis, settings); lp_assert(l.is_correct(basis)); indexed_vector w(m.row_count()); std::cout << "entering 2, leaving 0" << std::endl; l.prepare_entering(2, w); // to init vector w l.replace_column(0, w, heading[0]); change_basis(2, 0, basis, non_basic_columns, heading); // #ifdef Z3DEBUG // std::cout << "we were factoring " << std::endl; // print_matrix(get_B(l)); // #endif lp_assert(l.is_correct(basis)); std::cout << "entering 4, leaving 3" << std::endl; l.prepare_entering(4, w); // to init vector w l.replace_column(0, w, heading[3]); change_basis(4, 3, basis, non_basic_columns, heading); std::cout << "we were factoring " << std::endl; #ifdef Z3DEBUG { auto bl = get_B(l, basis); print_matrix(&bl, std::cout); } #endif lp_assert(l.is_correct(basis)); std::cout << "entering 5, leaving 1" << std::endl; l.prepare_entering(5, w); // to init vector w l.replace_column(0, w, heading[1]); change_basis(5, 1, basis, non_basic_columns, heading); std::cout << "we were factoring " << std::endl; #ifdef Z3DEBUG { auto bl = get_B(l, basis); print_matrix(&bl, std::cout); } #endif lp_assert(l.is_correct(basis)); std::cout << "entering 3, leaving 2" << std::endl; l.prepare_entering(3, w); // to init vector w l.replace_column(0, w, heading[2]); change_basis(3, 2, basis, non_basic_columns, heading); std::cout << "we were factoring " << std::endl; #ifdef Z3DEBUG { auto bl = get_B(l, basis); print_matrix(&bl, std::cout); } #endif lp_assert(l.is_correct(basis)); m.add_row(); m.add_column(); m.add_row(); m.add_column(); for (unsigned i = 0; i < m.column_count(); i++) { m(3, i) = i; m(4, i) = i * i; // to make the rows linearly independent } unsigned j = m.column_count() ; basis.push_back(j-2); heading.push_back(basis.size() - 1); basis.push_back(j-1); heading.push_back(basis.size() - 1); auto columns_to_replace = l.get_set_of_columns_to_replace_for_add_last_rows(heading); l.add_last_rows_to_B(heading, columns_to_replace); lp_assert(l.is_correct(basis)); } #endif void fill_long_row(square_sparse_matrix &m, int i) { int n = m.dimension(); for (int j = 0; j < n; j ++) { m (i, (j + i) % n) = j * j; } } void fill_long_row(static_matrix &m, int i) { int n = m.column_count(); for (int j = 0; j < n; j ++) { m (i, (j + i) % n) = j * j; } } void fill_long_row_exp(square_sparse_matrix &m, int i) { int n = m.dimension(); for (int j = 0; j < n; j ++) { m(i, j) = my_random() % 20; } } void fill_long_row_exp(static_matrix &m, int i) { int n = m.column_count(); for (int j = 0; j < n; j ++) { m(i, j) = my_random() % 20; } } void fill_larger_square_sparse_matrix_exp(square_sparse_matrix & m){ for ( unsigned i = 0; i < m.dimension(); i++ ) fill_long_row_exp(m, i); } void fill_larger_square_sparse_matrix_exp(static_matrix & m){ for ( unsigned i = 0; i < m.row_count(); i++ ) fill_long_row_exp(m, i); } void fill_larger_square_sparse_matrix(square_sparse_matrix & m){ for ( unsigned i = 0; i < m.dimension(); i++ ) fill_long_row(m, i); } void fill_larger_square_sparse_matrix(static_matrix & m){ for ( unsigned i = 0; i < m.row_count(); i++ ) fill_long_row(m, i); } int perm_id = 0; #ifdef Z3DEBUG void test_larger_lu_exp(lp_settings & settings) { std::cout << " test_larger_lu_exp" << std::endl; static_matrix m(6, 12); vector basis(6); basis[0] = 1; basis[1] = 3; basis[2] = 0; basis[3] = 4; basis[4] = 5; basis[5] = 6; fill_larger_square_sparse_matrix_exp(m); // print_matrix(m); vector heading = allocate_basis_heading(m.column_count()); vector non_basic_columns; init_basis_heading_and_non_basic_columns_vector(basis, heading, non_basic_columns); lu> l(m, basis, settings); dense_matrix left_side = l.get_left_side(basis); dense_matrix right_side = l.get_right_side(); lp_assert(left_side == right_side); int leaving = 3; int entering = 8; for (unsigned i = 0; i < m.row_count(); i++) { std::cout << static_cast(m(i, entering)) << std::endl; } indexed_vector w(m.row_count()); l.prepare_entering(entering, w); l.replace_column(0, w, heading[leaving]); change_basis(entering, leaving, basis, non_basic_columns, heading); lp_assert(l.is_correct(basis)); l.prepare_entering(11, w); // to init vector w l.replace_column(0, w, heading[0]); change_basis(11, 0, basis, non_basic_columns, heading); lp_assert(l.is_correct(basis)); } void test_larger_lu_with_holes(lp_settings & settings) { std::cout << " test_larger_lu_with_holes" << std::endl; static_matrix m(8, 9); vector basis(8); for (unsigned i = 0; i < m.row_count(); i++) { basis[i] = i; } m(0, 0) = 1; m(0, 1) = 2; m(0, 2) = 3; m(0, 3) = 4; m(0, 4) = 5; m(0, 8) = 99; /* */ m(1, 1) =- 6; m(1, 2) = 7; m(1, 3) = 8; m(1, 4) = 9; /* */ m(2, 2) = 10; /* */ m(3, 2) = 11; m(3, 3) = -12; /* */ m(4, 2) = 13; m(4, 3) = 14; m(4, 4) = 15; // the rest of the matrix is denser m(5, 4) = 28; m(5, 5) = -18; m(5, 6) = 19; m(5, 7) = 25; /* */ m(6, 5) = 20; m(6, 6) = -21; /* */ m(7, 5) = 22; m(7, 6) = 23; m(7, 7) = 24; m(7, 8) = 88; print_matrix(m, std::cout); vector heading = allocate_basis_heading(m.column_count()); vector non_basic_columns; init_basis_heading_and_non_basic_columns_vector(basis, heading, non_basic_columns); lu> l(m, basis, settings); std::cout << "printing factorization" << std::endl; for (int i = l.tail_size() - 1; i >=0; i--) { auto lp = l.get_lp_matrix(i); lp->set_number_of_columns(m.row_count()); lp->set_number_of_rows(m.row_count()); print_matrix( *lp, std::cout); } dense_matrix left_side = l.get_left_side(basis); dense_matrix right_side = l.get_right_side(); if (!(left_side == right_side)) { std::cout << "different sides" << std::endl; } indexed_vector w(m.row_count()); l.prepare_entering(8, w); // to init vector w l.replace_column(0, w, heading[0]); change_basis(8, 0, basis, non_basic_columns, heading); lp_assert(l.is_correct(basis)); } void test_larger_lu(lp_settings& settings) { std::cout << " test_larger_lu" << std::endl; static_matrix m(6, 12); vector basis(6); basis[0] = 1; basis[1] = 3; basis[2] = 0; basis[3] = 4; basis[4] = 5; basis[5] = 6; fill_larger_square_sparse_matrix(m); print_matrix(m, std::cout); vector heading = allocate_basis_heading(m.column_count()); vector non_basic_columns; init_basis_heading_and_non_basic_columns_vector(basis, heading, non_basic_columns); auto l = lu> (m, basis, settings); // std::cout << "printing factorization" << std::endl; // for (int i = lu.tail_size() - 1; i >=0; i--) { // auto lp = lu.get_lp_matrix(i); // lp->set_number_of_columns(m.row_count()); // lp->set_number_of_rows(m.row_count()); // print_matrix(* lp); // } dense_matrix left_side = l.get_left_side(basis); dense_matrix right_side = l.get_right_side(); if (!(left_side == right_side)) { std::cout << "left side" << std::endl; print_matrix(&left_side, std::cout); std::cout << "right side" << std::endl; print_matrix(&right_side, std::cout); std::cout << "different sides" << std::endl; std::cout << "initial factorization is incorrect" << std::endl; exit(1); } indexed_vector w(m.row_count()); l.prepare_entering(9, w); // to init vector w l.replace_column(0, w, heading[0]); change_basis(9, 0, basis, non_basic_columns, heading); lp_assert(l.is_correct(basis)); } void test_lu(lp_settings & settings) { test_small_lu(settings); test_larger_lu(settings); test_larger_lu_with_holes(settings); test_larger_lu_exp(settings); } #endif void init_b(vector & b, square_sparse_matrix & m, vector& x) { for (unsigned i = 0; i < m.dimension(); i++) { b.push_back(m.dot_product_with_row(i, x)); } } void init_b(vector & b, static_matrix & m, vector & x) { for (unsigned i = 0; i < m.row_count(); i++) { b.push_back(m.dot_product_with_row(i, x)); } } void test_lp_0() { std::cout << " test_lp_0 " << std::endl; static_matrix m_(3, 7); m_(0, 0) = 3; m_(0, 1) = 2; m_(0, 2) = 1; m_(0, 3) = 2; m_(0, 4) = 1; m_(1, 0) = 1; m_(1, 1) = 1; m_(1, 2) = 1; m_(1, 3) = 1; m_(1, 5) = 1; m_(2, 0) = 4; m_(2, 1) = 3; m_(2, 2) = 3; m_(2, 3) = 4; m_(2, 6) = 1; vector x_star(7); x_star[0] = 225; x_star[1] = 117; x_star[2] = 420; x_star[3] = x_star[4] = x_star[5] = x_star[6] = 0; vector b; init_b(b, m_, x_star); vector basis(3); basis[0] = 0; basis[1] = 1; basis[2] = 2; vector costs(7); costs[0] = 19; costs[1] = 13; costs[2] = 12; costs[3] = 17; costs[4] = 0; costs[5] = 0; costs[6] = 0; vector column_types(7, column_type::lower_bound); vector upper_bound_values; lp_settings settings; simple_column_namer cn; vector nbasis; vector heading; lp_primal_core_solver lpsolver(m_, b, x_star, basis, nbasis, heading, costs, column_types, upper_bound_values, settings, cn); lpsolver.solve(); } void test_lp_1() { std::cout << " test_lp_1 " << std::endl; static_matrix m(4, 7); m(0, 0) = 1; m(0, 1) = 3; m(0, 2) = 1; m(0, 3) = 1; m(1, 0) = -1; m(1, 2) = 3; m(1, 4) = 1; m(2, 0) = 2; m(2, 1) = -1; m(2, 2) = 2; m(2, 5) = 1; m(3, 0) = 2; m(3, 1) = 3; m(3, 2) = -1; m(3, 6) = 1; #ifdef Z3DEBUG print_matrix(m, std::cout); #endif vector x_star(7); x_star[0] = 0; x_star[1] = 0; x_star[2] = 0; x_star[3] = 3; x_star[4] = 2; x_star[5] = 4; x_star[6] = 2; vector basis(4); basis[0] = 3; basis[1] = 4; basis[2] = 5; basis[3] = 6; vector b; b.push_back(3); b.push_back(2); b.push_back(4); b.push_back(2); vector costs(7); costs[0] = 5; costs[1] = 5; costs[2] = 3; costs[3] = 0; costs[4] = 0; costs[5] = 0; costs[6] = 0; vector column_types(7, column_type::lower_bound); vector upper_bound_values; std::cout << "calling lp\n"; lp_settings settings; simple_column_namer cn; vector nbasis; vector heading; lp_primal_core_solver lpsolver(m, b, x_star, basis, nbasis, heading, costs, column_types, upper_bound_values, settings, cn); lpsolver.solve(); } void test_lp_primal_core_solver() { test_lp_0(); test_lp_1(); } #ifdef Z3DEBUG template void test_swap_rows_with_permutation(square_sparse_matrix& m){ std::cout << "testing swaps" << std::endl; unsigned dim = m.row_count(); dense_matrix original(&m); permutation_matrix q(dim); print_matrix(m, std::cout); lp_assert(original == q * m); for (int i = 0; i < 100; i++) { unsigned row1 = my_random() % dim; unsigned row2 = my_random() % dim; if (row1 == row2) continue; std::cout << "swap " << row1 << " " << row2 << std::endl; m.swap_rows(row1, row2); q.transpose_from_left(row1, row2); lp_assert(original == q * m); print_matrix(m, std::cout); std::cout << std::endl; } } #endif template void fill_matrix(square_sparse_matrix& m); // forward definition #ifdef Z3DEBUG template void test_swap_cols_with_permutation(square_sparse_matrix& m){ std::cout << "testing swaps" << std::endl; unsigned dim = m.row_count(); dense_matrix original(&m); permutation_matrix q(dim); print_matrix(m, std::cout); lp_assert(original == q * m); for (int i = 0; i < 100; i++) { unsigned row1 = my_random() % dim; unsigned row2 = my_random() % dim; if (row1 == row2) continue; std::cout << "swap " << row1 << " " << row2 << std::endl; m.swap_rows(row1, row2); q.transpose_from_right(row1, row2); lp_assert(original == q * m); print_matrix(m, std::cout); std::cout << std::endl; } } template void test_swap_rows(square_sparse_matrix& m, unsigned i0, unsigned i1){ std::cout << "test_swap_rows(" << i0 << "," << i1 << ")" << std::endl; square_sparse_matrix mcopy(m.dimension(), 0); for (unsigned i = 0; i < m.dimension(); i++) for (unsigned j = 0; j < m.dimension(); j++) { mcopy(i, j)= m(i, j); } std::cout << "swapping rows "<< i0 << "," << i1 << std::endl; m.swap_rows(i0, i1); for (unsigned j = 0; j < m.dimension(); j++) { lp_assert(mcopy(i0, j) == m(i1, j)); lp_assert(mcopy(i1, j) == m(i0, j)); } } template void test_swap_columns(square_sparse_matrix& m, unsigned i0, unsigned i1){ std::cout << "test_swap_columns(" << i0 << "," << i1 << ")" << std::endl; square_sparse_matrix mcopy(m.dimension(), 0); // the second argument does not matter for (unsigned i = 0; i < m.dimension(); i++) for (unsigned j = 0; j < m.dimension(); j++) { mcopy(i, j)= m(i, j); } m.swap_columns(i0, i1); for (unsigned j = 0; j < m.dimension(); j++) { lp_assert(mcopy(j, i0) == m(j, i1)); lp_assert(mcopy(j, i1) == m(j, i0)); } for (unsigned i = 0; i < m.dimension(); i++) { if (i == i0 || i == i1) continue; for (unsigned j = 0; j < m.dimension(); j++) { lp_assert(mcopy(j, i)== m(j, i)); } } } #endif template void fill_matrix(square_sparse_matrix& m){ int v = 0; for (int i = m.dimension() - 1; i >= 0; i--) { for (int j = m.dimension() - 1; j >=0; j--){ m(i, j) = v++; } } } void test_pivot_like_swaps_and_pivot(){ square_sparse_matrix m(10, 10); fill_matrix(m); // print_matrix(m); // pivot at 2,7 m.swap_columns(0, 7); // print_matrix(m); m.swap_rows(2, 0); // print_matrix(m); for (unsigned i = 1; i < m.dimension(); i++) { m(i, 0) = 0; } // print_matrix(m); // say pivot at 3,4 m.swap_columns(1, 4); // print_matrix(m); m.swap_rows(1, 3); // print_matrix(m); vector row; double alpha = 2.33; unsigned pivot_row = 1; unsigned target_row = 2; unsigned pivot_row_0 = 3; double beta = 3.1; m(target_row, 3) = 0; m(target_row, 5) = 0; m(pivot_row, 6) = 0; #ifdef Z3DEBUG print_matrix(m, std::cout); #endif for (unsigned j = 0; j < m.dimension(); j++) { row.push_back(m(target_row, j) + alpha * m(pivot_row, j) + beta * m(pivot_row_0, j)); } for (auto & t : row) { std::cout << t << ","; } std::cout << std::endl; lp_settings settings; m.pivot_row_to_row(pivot_row, alpha, target_row, settings); m.pivot_row_to_row(pivot_row_0, beta, target_row, settings); // print_matrix(m); for (unsigned j = 0; j < m.dimension(); j++) { lp_assert(abs(row[j] - m(target_row, j)) < 0.00000001); } } #ifdef Z3DEBUG void test_swap_rows() { square_sparse_matrix m(10, 10); fill_matrix(m); // print_matrix(m); test_swap_rows(m, 3, 5); test_swap_rows(m, 1, 3); test_swap_rows(m, 1, 3); test_swap_rows(m, 1, 7); test_swap_rows(m, 3, 7); test_swap_rows(m, 0, 7); m(0, 4) = 1; // print_matrix(m); test_swap_rows(m, 0, 7); // go over some corner cases square_sparse_matrix m0(2, 2); test_swap_rows(m0, 0, 1); m0(0, 0) = 3; test_swap_rows(m0, 0, 1); m0(1, 0) = 3; test_swap_rows(m0, 0, 1); square_sparse_matrix m1(10, 10); test_swap_rows(m1, 0, 1); m1(0, 0) = 3; test_swap_rows(m1, 0, 1); m1(1, 0) = 3; m1(0, 3) = 5; m1(1, 3) = 4; m1(1, 8) = 8; m1(1, 9) = 8; test_swap_rows(m1, 0, 1); square_sparse_matrix m2(3, 3); test_swap_rows(m2, 0, 1); m2(0, 0) = 3; test_swap_rows(m2, 0, 1); m2(2, 0) = 3; test_swap_rows(m2, 0, 2); } void fill_uniformly(square_sparse_matrix & m, unsigned dim) { int v = 0; for (unsigned i = 0; i < dim; i++) { for (unsigned j = 0; j < dim; j++) { m(i, j) = v++; } } } void fill_uniformly(dense_matrix & m, unsigned dim) { int v = 0; for (unsigned i = 0; i < dim; i++) { for (unsigned j = 0; j < dim; j++) { m.set_elem(i, j, v++); } } } void square_sparse_matrix_with_permutations_test() { unsigned dim = 4; square_sparse_matrix m(dim, dim); fill_uniformly(m, dim); dense_matrix dm(dim, dim); fill_uniformly(dm, dim); dense_matrix dm0(dim, dim); fill_uniformly(dm0, dim); permutation_matrix q0(dim); q0[0] = 1; q0[1] = 0; q0[2] = 3; q0[3] = 2; permutation_matrix q1(dim); q1[0] = 1; q1[1] = 2; q1[2] = 3; q1[3] = 0; permutation_matrix p0(dim); p0[0] = 1; p0[1] = 0; p0[2] = 3; p0[3] = 2; permutation_matrix p1(dim); p1[0] = 1; p1[1] = 2; p1[2] = 3; p1[3] = 0; m.multiply_from_left(q0); for (unsigned i = 0; i < dim; i++) { for (unsigned j = 0; j < dim; j++) { lp_assert(m(i, j) == dm0.get_elem(q0[i], j)); } } auto q0_dm = q0 * dm; lp_assert(m == q0_dm); m.multiply_from_left(q1); for (unsigned i = 0; i < dim; i++) { for (unsigned j = 0; j < dim; j++) { lp_assert(m(i, j) == dm0.get_elem(q0[q1[i]], j)); } } auto q1_q0_dm = q1 * q0_dm; lp_assert(m == q1_q0_dm); m.multiply_from_right(p0); for (unsigned i = 0; i < dim; i++) { for (unsigned j = 0; j < dim; j++) { lp_assert(m(i, j) == dm0.get_elem(q0[q1[i]], p0[j])); } } auto q1_q0_dm_p0 = q1_q0_dm * p0; lp_assert(m == q1_q0_dm_p0); m.multiply_from_right(p1); for (unsigned i = 0; i < dim; i++) { for (unsigned j = 0; j < dim; j++) { lp_assert(m(i, j) == dm0.get_elem(q0[q1[i]], p1[p0[j]])); } } auto q1_q0_dm_p0_p1 = q1_q0_dm_p0 * p1; lp_assert(m == q1_q0_dm_p0_p1); m.multiply_from_right(p1); for (unsigned i = 0; i < dim; i++) { for (unsigned j = 0; j < dim; j++) { lp_assert(m(i, j) == dm0.get_elem(q0[q1[i]], p1[p1[p0[j]]])); } } auto q1_q0_dm_p0_p1_p1 = q1_q0_dm_p0_p1 * p1; lp_assert(m == q1_q0_dm_p0_p1_p1); } void test_swap_columns() { square_sparse_matrix m(10, 10); fill_matrix(m); // print_matrix(m); test_swap_columns(m, 3, 5); test_swap_columns(m, 1, 3); test_swap_columns(m, 1, 3); // print_matrix(m); test_swap_columns(m, 1, 7); test_swap_columns(m, 3, 7); test_swap_columns(m, 0, 7); test_swap_columns(m, 0, 7); // go over some corner cases square_sparse_matrix m0(2, 2); test_swap_columns(m0, 0, 1); m0(0, 0) = 3; test_swap_columns(m0, 0, 1); m0(0, 1) = 3; test_swap_columns(m0, 0, 1); square_sparse_matrix m1(10, 10); test_swap_columns(m1, 0, 1); m1(0, 0) = 3; test_swap_columns(m1, 0, 1); m1(0, 1) = 3; m1(3, 0) = 5; m1(3, 1) = 4; m1(8, 1) = 8; m1(9, 1) = 8; test_swap_columns(m1, 0, 1); square_sparse_matrix m2(3, 3); test_swap_columns(m2, 0, 1); m2(0, 0) = 3; test_swap_columns(m2, 0, 1); m2(0, 2) = 3; test_swap_columns(m2, 0, 2); } void test_swap_operations() { test_swap_rows(); test_swap_columns(); } void test_dense_matrix() { dense_matrix d(3, 2); d.set_elem(0, 0, 1); d.set_elem(1, 1, 2); d.set_elem(2, 0, 3); // print_matrix(d); dense_matrix unit(2, 2); d.set_elem(0, 0, 1); d.set_elem(1, 1, 1); dense_matrix c = d * unit; // print_matrix(d); dense_matrix perm(3, 3); perm.set_elem(0, 1, 1); perm.set_elem(1, 0, 1); perm.set_elem(2, 2, 1); auto c1 = perm * d; // print_matrix(c1); dense_matrix p2(2, 2); p2.set_elem(0, 1, 1); p2.set_elem(1, 0, 1); auto c2 = d * p2; } #endif vector> vector_of_permutations() { vector> ret; { permutation_matrix p0(5); p0[0] = 1; p0[1] = 2; p0[2] = 3; p0[3] = 4; p0[4] = 0; ret.push_back(p0); } { permutation_matrix p0(5); p0[0] = 2; p0[1] = 0; p0[2] = 1; p0[3] = 4; p0[4] = 3; ret.push_back(p0); } return ret; } void test_apply_reverse_from_right_to_perm(permutation_matrix & l) { permutation_matrix p(5); p[0] = 4; p[1] = 2; p[2] = 0; p[3] = 3; p[4] = 1; permutation_matrix pclone(5); pclone[0] = 4; pclone[1] = 2; pclone[2] = 0; pclone[3] = 3; pclone[4] = 1; p.multiply_by_reverse_from_right(l); #ifdef Z3DEBUG auto rev = l.get_inverse(); auto rs = pclone * rev; lp_assert(p == rs) #endif } void test_apply_reverse_from_right() { auto vec = vector_of_permutations(); for (unsigned i = 0; i < vec.size(); i++) { test_apply_reverse_from_right_to_perm(vec[i]); } } void test_permutations() { std::cout << "test permutations" << std::endl; test_apply_reverse_from_right(); vector v; v.resize(5, 0); v[1] = 1; v[3] = 3; permutation_matrix p(5); p[0] = 4; p[1] = 2; p[2] = 0; p[3] = 3; p[4] = 1; indexed_vector vi(5); vi.set_value(1, 1); vi.set_value(3, 3); p.apply_reverse_from_right_to_T(v); p.apply_reverse_from_right_to_T(vi); lp_assert(vectors_are_equal(v, vi.m_data)); lp_assert(vi.is_OK()); } void lp_solver_test() { // lp_revised_solver lp_revised; // lp_revised.get_minimal_solution(); } 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.empty()) { n = atoi(s.c_str()); return true; } return false; } 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.empty()) { n = atof(s.c_str()); return true; } return false; } void update_settings(argument_parser & args_parser, lp_settings& settings) { unsigned n; settings.m_simplex_strategy = simplex_strategy_enum::lu; if (get_int_from_args_parser("--rep_frq", args_parser, n)) settings.report_frequency = n; else settings.report_frequency = args_parser.option_is_used("--mpq")? 80: 1000; settings.print_statistics = true; if (get_int_from_args_parser("--percent_for_enter", args_parser, n)) settings.percent_of_entering_to_check = n; if (get_int_from_args_parser("--partial_pivot", args_parser, n)) { std::cout << "setting partial pivot constant to " << n << std::endl; settings.c_partial_pivoting = n; } if (get_int_from_args_parser("--density", args_parser, n)) { double density = static_cast(n) / 100.0; std::cout << "setting density to " << density << std::endl; settings.density_threshold = density; } if (get_int_from_args_parser("--maxng", args_parser, n)) settings.max_number_of_iterations_with_no_improvements = n; double d; if (get_double_from_args_parser("--harris_toler", args_parser, d)) { std::cout << "setting harris_feasibility_tolerance to " << d << std::endl; settings.harris_feasibility_tolerance = d; } if (get_int_from_args_parser("--random_seed", args_parser, n)) { settings.set_random_seed(n); } if (get_int_from_args_parser("--simplex_strategy", args_parser, n)) { settings.simplex_strategy() = static_cast(n); } } template void setup_solver(unsigned max_iterations, unsigned time_limit, bool look_for_min, argument_parser & args_parser, lp_solver * solver) { if (max_iterations > 0) solver->set_max_iterations_per_stage(max_iterations); if (time_limit > 0) solver->set_time_limit(time_limit); if (look_for_min) solver->flip_costs(); update_settings(args_parser, solver->settings()); } bool values_are_one_percent_close(double a, double b); void print_x(mps_reader & reader, lp_solver * solver) { for (const auto & name : reader.column_names()) { std::cout << name << "=" << solver->get_column_value_by_name(name) << ' '; } std::cout << std::endl; } void compare_solutions(mps_reader & reader, lp_solver * solver, lp_solver * solver0) { for (const auto & name : reader.column_names()) { double a = solver->get_column_value_by_name(name); double b = solver0->get_column_value_by_name(name); if (!values_are_one_percent_close(a, b)) { std::cout << "different values for " << name << ":" << a << " and " << b << std::endl; } } } void solve_mps_double(std::string file_name, bool look_for_min, unsigned max_iterations, unsigned time_limit, bool dual, bool compare_with_primal, argument_parser & args_parser) { mps_reader reader(file_name); reader.read(); if (!reader.is_ok()) { std::cout << "cannot process " << file_name << std::endl; return; } lp_solver * solver = reader.create_solver(dual); setup_solver(max_iterations, time_limit, look_for_min, args_parser, solver); stopwatch sw; sw.start(); if (dual) { std::cout << "solving for dual" << std::endl; } solver->find_maximal_solution(); sw.stop(); double span = sw.get_seconds(); std::cout << "Status: " << lp_status_to_string(solver->get_status()) << std::endl; if (solver->get_status() == lp_status::OPTIMAL) { if (reader.column_names().size() < 20) { print_x(reader, solver); } double cost = solver->get_current_cost(); if (look_for_min) { cost = -cost; } std::cout << "cost = " << cost << std::endl; } std::cout << "processed in " << span / 1000.0 << " seconds, running for " << solver->m_total_iterations << " iterations" << " one iter for " << (double)span/solver->m_total_iterations << " ms" << std::endl; if (compare_with_primal) { auto * primal_solver = reader.create_solver(false); setup_solver(max_iterations, time_limit, look_for_min, args_parser, primal_solver); primal_solver->find_maximal_solution(); if (solver->get_status() != primal_solver->get_status()) { std::cout << "statuses are different: dual " << lp_status_to_string(solver->get_status()) << " primal = " << lp_status_to_string(primal_solver->get_status()) << std::endl; } else { if (solver->get_status() == lp_status::OPTIMAL) { double cost = solver->get_current_cost(); if (look_for_min) { cost = -cost; } double primal_cost = primal_solver->get_current_cost(); if (look_for_min) { primal_cost = -primal_cost; } std::cout << "primal cost = " << primal_cost << std::endl; if (!values_are_one_percent_close(cost, primal_cost)) { compare_solutions(reader, primal_solver, solver); print_x(reader, primal_solver); std::cout << "dual cost is " << cost << ", but primal cost is " << primal_cost << std::endl; lp_assert(false); } } } delete primal_solver; } delete solver; } void solve_mps_rational(std::string file_name, bool look_for_min, unsigned max_iterations, unsigned time_limit, bool dual, argument_parser & args_parser) { mps_reader reader(file_name); reader.read(); if (reader.is_ok()) { auto * solver = reader.create_solver(dual); setup_solver(max_iterations, time_limit, look_for_min, args_parser, solver); stopwatch sw; sw.start(); solver->find_maximal_solution(); std::cout << "Status: " << lp_status_to_string(solver->get_status()) << std::endl; if (solver->get_status() == lp_status::OPTIMAL) { // for (auto name: reader.column_names()) { // std::cout << name << "=" << solver->get_column_value_by_name(name) << ' '; // } lp::mpq cost = solver->get_current_cost(); if (look_for_min) { cost = -cost; } std::cout << "cost = " << cost.get_double() << std::endl; } std::cout << "processed in " << sw.get_current_seconds() / 1000.0 << " seconds, running for " << solver->m_total_iterations << " iterations" << std::endl; delete solver; } else { std::cout << "cannot process " << file_name << std::endl; } } void get_time_limit_and_max_iters_from_parser(argument_parser & args_parser, unsigned & time_limit, unsigned & max_iters); // forward definition void solve_mps(std::string file_name, bool look_for_min, unsigned max_iterations, unsigned time_limit, bool solve_for_rational, bool dual, bool compare_with_primal, argument_parser & args_parser) { if (!solve_for_rational) { std::cout << "solving " << file_name << std::endl; solve_mps_double(file_name, look_for_min, max_iterations, time_limit, dual, compare_with_primal, args_parser); } else { std::cout << "solving " << file_name << " in rationals " << std::endl; solve_mps_rational(file_name, look_for_min, max_iterations, time_limit, dual, args_parser); } } void solve_mps(std::string file_name, argument_parser & args_parser) { bool look_for_min = args_parser.option_is_used("--min"); unsigned max_iterations, time_limit; bool solve_for_rational = args_parser.option_is_used("--mpq"); bool dual = args_parser.option_is_used("--dual"); bool compare_with_primal = args_parser.option_is_used("--compare_with_primal"); get_time_limit_and_max_iters_from_parser(args_parser, time_limit, max_iterations); solve_mps(file_name, look_for_min, max_iterations, time_limit, solve_for_rational, dual, compare_with_primal, args_parser); } void solve_mps_in_rational(std::string file_name, bool dual, argument_parser & /*args_parser*/) { std::cout << "solving " << file_name << std::endl; mps_reader reader(file_name); reader.read(); if (reader.is_ok()) { auto * solver = reader.create_solver(dual); solver->find_maximal_solution(); std::cout << "status is " << lp_status_to_string(solver->get_status()) << std::endl; if (solver->get_status() == lp_status::OPTIMAL) { if (reader.column_names().size() < 20) { for (const auto & name : reader.column_names()) { std::cout << name << "=" << solver->get_column_value_by_name(name).get_double() << ' '; } } std::cout << std::endl << "cost = " << numeric_traits::get_double(solver->get_current_cost()) << std::endl; } delete solver; } else { std::cout << "cannot process " << file_name << std::endl; } } void test_upair_queue() { int n = 10; binary_heap_upair_queue q(2); std::unordered_map m; for (int k = 0; k < 100; k++) { int i = my_random()%n; int j = my_random()%n; q.enqueue(i, j, my_random()%n); } q.remove(5, 5); while (!q.is_empty()) { unsigned i, j; q.dequeue(i, j); } } void test_binary_priority_queue() { std::cout << "testing binary_heap_priority_queue..."; auto q = binary_heap_priority_queue(10); q.enqueue(2, 2); q.enqueue(1, 1); q.enqueue(9, 9); q.enqueue(8, 8); q.enqueue(5, 25); q.enqueue(3, 3); q.enqueue(4, 4); q.enqueue(7, 30); q.enqueue(6, 6); q.enqueue(0, 0); q.enqueue(5, 5); q.enqueue(7, 7); for (unsigned i = 0; i < 10; i++) { unsigned de = q.dequeue(); lp_assert(i == de); std::cout << de << std::endl; } q.enqueue(2, 2); q.enqueue(1, 1); q.enqueue(9, 9); q.enqueue(8, 8); q.enqueue(5, 5); q.enqueue(3, 3); q.enqueue(4, 4); q.enqueue(7, 2); q.enqueue(0, 1); q.enqueue(6, 6); q.enqueue(7, 7); q.enqueue(33, 1000); q.enqueue(20, 0); q.dequeue(); q.remove(33); q.enqueue(0, 0); #ifdef Z3DEBUG unsigned t = 0; while (q.size() > 0) { unsigned d =q.dequeue(); lp_assert(t++ == d); std::cout << d << std::endl; } #endif test_upair_queue(); std::cout << " done" << std::endl; } bool solution_is_feasible(std::string file_name, const std::unordered_map & solution) { mps_reader reader(file_name); reader.read(); if (reader.is_ok()) { lp_primal_simplex * solver = static_cast *>(reader.create_solver(false)); return solver->solution_is_feasible(solution); } return false; } void solve_mps_with_known_solution(std::string file_name, std::unordered_map * solution, lp_status status, bool dual) { std::cout << "solving " << file_name << std::endl; mps_reader reader(file_name); reader.read(); if (reader.is_ok()) { auto * solver = reader.create_solver(dual); solver->find_maximal_solution(); std::cout << "status is " << lp_status_to_string(solver->get_status()) << std::endl; if (status != solver->get_status()){ std::cout << "status should be " << lp_status_to_string(status) << std::endl; lp_assert(status == solver->get_status()); throw "status is wrong"; } if (solver->get_status() == lp_status::OPTIMAL) { std::cout << "cost = " << solver->get_current_cost() << std::endl; if (solution != nullptr) { for (auto it : *solution) { if (fabs(it.second - solver->get_column_value_by_name(it.first)) >= 0.000001) { std::cout << "expected:" << it.first << "=" << it.second <<", got " << solver->get_column_value_by_name(it.first) << std::endl; } lp_assert(fabs(it.second - solver->get_column_value_by_name(it.first)) < 0.000001); } } if (reader.column_names().size() < 20) { for (const auto & name : reader.column_names()) { std::cout << name << "=" << solver->get_column_value_by_name(name) << ' '; } std::cout << std::endl; } } delete solver; } else { std::cout << "cannot process " << file_name << std::endl; } } int get_random_rows() { return 5 + my_random() % 2; } int get_random_columns() { return 5 + my_random() % 3; } int get_random_int() { return -1 + my_random() % 2; // (1.0 + RAND_MAX); } void add_random_row(lp_primal_simplex * solver, int cols, int row) { solver->add_constraint(lp_relation::Greater_or_equal, 1, row); for (int i = 0; i < cols; i++) { solver->set_row_column_coefficient(row, i, get_random_int()); } } void add_random_cost(lp_primal_simplex * solver, int cols) { for (int i = 0; i < cols; i++) { solver->set_cost_for_column(i, get_random_int()); } } lp_primal_simplex * generate_random_solver() { int rows = get_random_rows(); int cols = get_random_columns(); auto * solver = new lp_primal_simplex(); for (int i = 0; i < rows; i++) { add_random_row(solver, cols, i); } add_random_cost(solver, cols); return solver; } void random_test_on_i(unsigned i) { if (i % 1000 == 0) { std::cout << "."; } srand(i); auto *solver = generate_random_solver(); solver->find_maximal_solution(); // std::cout << lp_status_to_string(solver->get_status()) << std::endl; delete solver; } void random_test() { for (unsigned i = 0; i < std::numeric_limits::max(); i++) { try { random_test_on_i(i); } catch (const char * error) { std::cout << "i = " << i << ", throwing at ' " << error << "'" << std::endl; break; } } } #if _LINUX_ void fill_file_names(vector &file_names, std::set & minimums) { char *home_dir = getenv("HOME"); if (home_dir == nullptr) { std::cout << "cannot find home directory, don't know how to find the files"; return; } std::string home_dir_str(home_dir); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/l0redund.mps"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/l1.mps"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/l2.mps"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/l3.mps"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/l4.mps"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/l4fix.mps"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/plan.mps"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/samp2.mps"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/murtagh.mps"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/l0.mps"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/AFIRO.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SC50B.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SC50A.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/KB2.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SC105.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/STOCFOR1.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/ADLITTLE.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/BLEND.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SCAGR7.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SC205.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SHARE2B.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/RECIPELP.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/LOTFI.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/VTP-BASE.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SHARE1B.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/BOEING2.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/BORE3D.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SCORPION.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/CAPRI.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/BRANDY.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SCAGR25.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SCTAP1.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/ISRAEL.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SCFXM1.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/BANDM.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/E226.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/AGG.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/GROW7.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/ETAMACRO.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/FINNIS.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SCSD1.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/STANDATA.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/STANDGUB.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/BEACONFD.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/STAIR.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/STANDMPS.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/GFRD-PNC.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SCRS8.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/BOEING1.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/MODSZK1.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/DEGEN2.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/FORPLAN.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/AGG2.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/AGG3.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SCFXM2.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SHELL.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/PILOT4.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SCSD6.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SHIP04S.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SEBA.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/GROW15.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/FFFFF800.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/BNL1.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/PEROLD.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/QAP8.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SCFXM3.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SHIP04L.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/GANGES.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SCTAP2.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/GROW22.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SHIP08S.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/PILOT-WE.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/MAROS.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/STOCFOR2.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/25FV47.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SHIP12S.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SCSD8.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/FIT1P.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SCTAP3.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SIERRA.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/PILOTNOV.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/CZPROB.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/FIT1D.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/PILOT-JA.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SHIP08L.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/BNL2.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/NESM.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/CYCLE.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/acc-tight5.mps"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SHIP12L.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/DEGEN3.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/GREENBEA.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/GREENBEB.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/80BAU3B.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/TRUSS.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/D2Q06C.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/WOODW.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/QAP12.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/D6CUBE.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/PILOT.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/DFL001.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/WOOD1P.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/FIT2P.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/PILOT87.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/STOCFOR3.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/QAP15.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/FIT2D.SIF"); file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/MAROS-R7.SIF"); minimums.insert("/projects/lp/src/tests/util/lp/test_files/netlib/FIT2P.SIF"); minimums.insert("/projects/lp/src/tests/util/lp/test_files/netlib/DFL001.SIF"); minimums.insert("/projects/lp/src/tests/util/lp/test_files/netlib/D2Q06C.SIF"); minimums.insert("/projects/lp/src/tests/util/lp/test_files/netlib/80BAU3B.SIF"); minimums.insert("/projects/lp/src/tests/util/lp/test_files/netlib/GREENBEB.SIF"); minimums.insert("/projects/lp/src/tests/util/lp/test_files/netlib/GREENBEA.SIF"); minimums.insert("/projects/lp/src/tests/util/lp/test_files/netlib/BNL2.SIF"); minimums.insert("/projects/lp/src/tests/util/lp/test_files/netlib/SHIP08L.SIF"); minimums.insert("/projects/lp/src/tests/util/lp/test_files/netlib/FIT1D.SIF"); minimums.insert("/projects/lp/src/tests/util/lp/test_files/netlib/SCTAP3.SIF"); minimums.insert("/projects/lp/src/tests/util/lp/test_files/netlib/SCSD8.SIF"); minimums.insert("/projects/lp/src/tests/util/lp/test_files/netlib/SCSD6.SIF"); minimums.insert("/projects/lp/src/tests/util/lp/test_files/netlib/MAROS-R7.SIF"); } void test_out_dir(std::string out_dir) { auto *out_dir_p = opendir(out_dir.c_str()); if (out_dir_p == nullptr) { std::cout << "creating directory " << out_dir << std::endl; #ifdef LEAN_WINDOWS int res = mkdir(out_dir.c_str()); #else int res = mkdir(out_dir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); #endif if (res) { std::cout << "Cannot open output directory \"" << out_dir << "\"" << std::endl; } return; } closedir(out_dir_p); } void find_dir_and_file_name(std::string a, std::string & dir, std::string& fn) { // todo: make it system independent size_t last_slash_pos = a.find_last_of("/"); if (last_slash_pos >= a.size()) { std::cout << "cannot find file name in " << a << std::endl; throw; } dir = a.substr(0, last_slash_pos); // std::cout << "dir = " << dir << std::endl; fn = a.substr(last_slash_pos + 1); // std::cout << "fn = " << fn << std::endl; } void process_test_file(std::string test_dir, std::string test_file_name, argument_parser & args_parser, std::string out_dir, unsigned max_iters, unsigned time_limit, unsigned & successes, unsigned & failures, unsigned & inconclusives); void solve_some_mps(argument_parser & args_parser) { unsigned max_iters, time_limit; get_time_limit_and_max_iters_from_parser(args_parser, time_limit, max_iters); unsigned successes = 0; unsigned failures = 0; unsigned inconclusives = 0; std::set minimums; vector file_names; fill_file_names(file_names, minimums); bool solve_for_rational = args_parser.option_is_used("--mpq"); bool dual = args_parser.option_is_used("--dual"); bool compare_with_primal = args_parser.option_is_used("--compare_with_primal"); bool compare_with_glpk = args_parser.option_is_used("--compare_with_glpk"); if (compare_with_glpk) { std::string out_dir = args_parser.get_option_value("--out_dir"); if (out_dir.size() == 0) { out_dir = "/tmp/test"; } test_out_dir(out_dir); for (auto& a : file_names) { try { std::string file_dir; std::string file_name; find_dir_and_file_name(a, file_dir, file_name); process_test_file(file_dir, file_name, args_parser, out_dir, max_iters, time_limit, successes, failures, inconclusives); } catch(const char *s){ std::cout<< "exception: "<< s << std::endl; } } std::cout << "comparing with glpk: successes " << successes << ", failures " << failures << ", inconclusives " << inconclusives << std::endl; return; } if (!solve_for_rational) { solve_mps(file_names[6], false, 0, time_limit, false, dual, compare_with_primal, args_parser); solve_mps_with_known_solution(file_names[3], nullptr, lp_status::INFEASIBLE, dual); // chvatal: 135(d) std::unordered_map sol; sol["X1"] = 0; sol["X2"] = 6; sol["X3"] = 0; sol["X4"] = 15; sol["X5"] = 2; sol["X6"] = 1; sol["X7"] = 1; sol["X8"] = 0; solve_mps_with_known_solution(file_names[9], &sol, lp_status::OPTIMAL, dual); solve_mps_with_known_solution(file_names[0], &sol, lp_status::OPTIMAL, dual); sol.clear(); sol["X1"] = 25.0/14.0; // sol["X2"] = 0; // sol["X3"] = 0; // sol["X4"] = 0; // sol["X5"] = 0; // sol["X6"] = 0; // sol["X7"] = 9.0/14.0; solve_mps_with_known_solution(file_names[5], &sol, lp_status::OPTIMAL, dual); // chvatal: 135(e) solve_mps_with_known_solution(file_names[4], &sol, lp_status::OPTIMAL, dual); // chvatal: 135(e) solve_mps_with_known_solution(file_names[2], nullptr, lp_status::UNBOUNDED, dual); // chvatal: 135(c) solve_mps_with_known_solution(file_names[1], nullptr, lp_status::UNBOUNDED, dual); // chvatal: 135(b) solve_mps(file_names[8], false, 0, time_limit, false, dual, compare_with_primal, args_parser); // return; for (auto& s : file_names) { try { solve_mps(s, minimums.find(s) != minimums.end(), max_iters, time_limit, false, dual, compare_with_primal, args_parser); } catch(const char *s){ std::cout<< "exception: "<< s << std::endl; } } } else { // unsigned i = 0; for (auto& s : file_names) { // if (i++ > 9) return; try { solve_mps_in_rational(s, dual, args_parser); } catch(const char *s){ std::cout<< "exception: "<< s << std::endl; } } } } #endif void solve_rational() { lp_primal_simplex solver; solver.add_constraint(lp_relation::Equal, lp::mpq(7), 0); solver.add_constraint(lp_relation::Equal, lp::mpq(-3), 1); // setting the cost int cost[] = {-3, -1, -1, 2, -1, 1, 1, -4}; std::string var_names[8] = {"x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8"}; for (unsigned i = 0; i < 8; i++) { solver.set_cost_for_column(i, lp::mpq(cost[i])); solver.give_symbolic_name_to_column(var_names[i], i); } int row0[] = {1, 0, 3, 1, -5, -2 , 4, -6}; for (unsigned i = 0; i < 8; i++) { solver.set_row_column_coefficient(0, i, lp::mpq(row0[i])); } int row1[] = {0, 1, -2, -1, 4, 1, -3, 5}; for (unsigned i = 0; i < 8; i++) { solver.set_row_column_coefficient(1, i, lp::mpq(row1[i])); } int bounds[] = {8, 6, 4, 15, 2, 10, 10, 3}; for (unsigned i = 0; i < 8; i++) { solver.set_lower_bound(i, lp::mpq(0)); solver.set_upper_bound(i, lp::mpq(bounds[i])); } std::unordered_map expected_sol; expected_sol["x1"] = lp::mpq(0); expected_sol["x2"] = lp::mpq(6); expected_sol["x3"] = lp::mpq(0); expected_sol["x4"] = lp::mpq(15); expected_sol["x5"] = lp::mpq(2); expected_sol["x6"] = lp::mpq(1); expected_sol["x7"] = lp::mpq(1); expected_sol["x8"] = lp::mpq(0); solver.find_maximal_solution(); lp_assert(solver.get_status() == lp_status::OPTIMAL); for (const auto & it : expected_sol) { (void)it; lp_assert(it.second == solver.get_column_value_by_name(it.first)); } } std::string read_line(bool & end, std::ifstream & file) { std::string s; if (!getline(file, s)) { end = true; return std::string(); } end = false; return s; } bool contains(std::string const & s, char const * pattern) { return s.find(pattern) != std::string::npos; } std::unordered_map * get_solution_from_glpsol_output(std::string & file_name) { std::ifstream file(file_name); if (!file.is_open()){ std::cerr << "cannot open " << file_name << std::endl; return nullptr; } std::string s; bool end; do { s = read_line(end, file); if (end) { std::cerr << "unexpected file end " << file_name << std::endl; return nullptr; } if (contains(s, "Column name")){ break; } } while (true); read_line(end, file); if (end) { std::cerr << "unexpected file end " << file_name << std::endl; return nullptr; } auto ret = new std::unordered_map(); do { s = read_line(end, file); if (end) { std::cerr << "unexpected file end " << file_name << std::endl; return nullptr; } auto split = string_split(s, " \t", false); if (split.empty()) { return ret; } lp_assert(split.size() > 3); (*ret)[split[1]] = atof(split[3].c_str()); } while (true); } void test_init_U() { static_matrix m(3, 7); m(0, 0) = 10; m(0, 1) = 11; m(0, 2) = 12; m(0, 3) = 13; m(0, 4) = 14; m(1, 0) = 20; m(1, 1) = 21; m(1, 2) = 22; m(1, 3) = 23; m(1, 5) = 24; m(2, 0) = 30; m(2, 1) = 31; m(2, 2) = 32; m(2, 3) = 33; m(2, 6) = 34; #ifdef Z3DEBUG print_matrix(m, std::cout); #endif vector basis(3); basis[0] = 1; basis[1] = 2; basis[2] = 4; square_sparse_matrix u(m, basis); for (unsigned i = 0; i < 3; i++) { for (unsigned j = 0; j < 3; j ++) { lp_assert(m(i, basis[j]) == u(i, j)); } } // print_matrix(m); // print_matrix(u); } void test_replace_column() { square_sparse_matrix m(10, 10); fill_matrix(m); m.swap_columns(0, 7); m.swap_columns(6, 3); m.swap_rows(2, 0); for (unsigned i = 1; i < m.dimension(); i++) { m(i, 0) = 0; } indexed_vector w(m.dimension()); for (unsigned i = 0; i < m.dimension(); i++) { w.set_value(i % 3, i); } lp_settings settings; for (unsigned column_to_replace = 0; column_to_replace < m.dimension(); column_to_replace ++) { m.replace_column(column_to_replace, w, settings); for (unsigned i = 0; i < m.dimension(); i++) { lp_assert(abs(w[i] - m(i, column_to_replace)) < 0.00000001); } } } void setup_args_parser(argument_parser & parser) { parser.add_option_with_help_string("-hnf", "test hermite normal form"); parser.add_option_with_help_string("-gomory", "gomory"); parser.add_option_with_help_string("-intd", "test integer_domain"); parser.add_option_with_help_string("-xyz_sample", "run a small interactive scenario"); parser.add_option_with_after_string_with_help("--density", "the percentage of non-zeroes in the matrix below which it is not dense"); parser.add_option_with_after_string_with_help("--harris_toler", "harris tolerance"); parser.add_option_with_help_string("--test_swaps", "test row swaps with a permutation"); parser.add_option_with_help_string("--test_perm", "test permutations"); parser.add_option_with_after_string_with_help("--checklu", "the file name for lu checking"); parser.add_option_with_after_string_with_help("--partial_pivot", "the partial pivot constant, a number somewhere between 10 and 100"); parser.add_option_with_after_string_with_help("--percent_for_enter", "which percent of columns check for entering column"); parser.add_option_with_help_string("--totalinf", "minimizes the total infeasibility instead of diminishing infeasibility of the rows"); parser.add_option_with_after_string_with_help("--rep_frq", "the report frequency, in how many iterations print the cost and other info "); parser.add_option_with_help_string("--smt", "smt file format"); parser.add_option_with_after_string_with_help("--filelist", "the file containing the list of files"); parser.add_option_with_after_string_with_help("--file", "the input file name"); parser.add_option_with_after_string_with_help("--random_seed", "random seed"); parser.add_option_with_help_string("--bp", "bound propagation"); parser.add_option_with_help_string("--min", "will look for the minimum for the given file if --file is used; the default is looking for the max"); parser.add_option_with_help_string("--max", "will look for the maximum for the given file if --file is used; it is the default behavior"); parser.add_option_with_after_string_with_help("--max_iters", "maximum total iterations in a core solver stage"); parser.add_option_with_after_string_with_help("--time_limit", "time limit in seconds"); parser.add_option_with_help_string("--mpq", "solve for rational numbers"); parser.add_option_with_after_string_with_help("--simplex_strategy", "sets simplex strategy for rational number"); parser.add_option_with_help_string("--test_lu", "test the work of the factorization"); parser.add_option_with_help_string("--test_small_lu", "test the work of the factorization on a smallish matrix"); parser.add_option_with_help_string("--test_larger_lu", "test the work of the factorization"); parser.add_option_with_help_string("--test_larger_lu_with_holes", "test the work of the factorization"); parser.add_option_with_help_string("--test_lp_0", "solve a small lp"); parser.add_option_with_help_string("--solve_some_mps", "solves a list of mps problems"); parser.add_option_with_after_string_with_help("--test_file_directory", "loads files from the directory for testing"); parser.add_option_with_help_string("--compare_with_glpk", "compares the results by running glpsol"); parser.add_option_with_after_string_with_help("--out_dir", "setting the output directory for tests, if not set /tmp is used"); parser.add_option_with_help_string("--dual", "using the dual simplex solver"); parser.add_option_with_help_string("--compare_with_primal", "using the primal simplex solver for comparison"); parser.add_option_with_help_string("--lar", "test lar_solver"); parser.add_option_with_after_string_with_help("--maxng", "max iterations without progress"); parser.add_option_with_help_string("-tbq", "test binary queue"); parser.add_option_with_help_string("--randomize_lar", "test randomize functionality"); parser.add_option_with_help_string("--smap", "test stacked_map"); parser.add_option_with_help_string("--term", "simple term test"); parser.add_option_with_help_string("--eti"," run a small evidence test for total infeasibility scenario"); parser.add_option_with_help_string("--row_inf", "forces row infeasibility search"); parser.add_option_with_help_string("-pd", "presolve with double solver"); parser.add_option_with_help_string("--test_int_set", "test int_set"); parser.add_option_with_help_string("--test_mpq", "test rationals"); parser.add_option_with_help_string("--test_mpq_np", "test rationals"); parser.add_option_with_help_string("--test_mpq_np_plus", "test rationals using plus instead of +="); parser.add_option_with_help_string("--maximize_term", "test maximize_term()"); } struct fff { int a; int b;}; void test_stacked_unsigned() { std::cout << "test stacked unsigned" << std::endl; stacked_value v(0); v = 1; v = 2; v.push(); v = 3; v = 4; v.pop(); lp_assert(v == 2); v ++; v++; std::cout << "before push v=" << v << std::endl; v.push(); v++; v.push(); v+=1; std::cout << "v = " << v << std::endl; v.pop(2); lp_assert(v == 4); const unsigned & rr = v; std::cout << rr << std:: endl; } void test_stacked_value() { test_stacked_unsigned(); } void test_stacked_vector() { std::cout << "test_stacked_vector" << std::endl; stacked_vector v; v.push(); v.push_back(0); v.push_back(1); v.push(); v[0] = 3; v[0] = 0; v.push_back(2); v.push_back(3); v.push_back(34); v.push(); v[1]=3; v[2] = 3; v.push(); v[0]= 7; v[1] = 9; v.pop(2); if (v.size()) v[v.size() -1 ] = 7; v.push(); v.push_back(33); v[0] = 13; v.pop(); } void test_stacked() { test_stacked_value(); test_stacked_vector(); } char * find_home_dir() { #ifdef _WINDOWS #else char * home_dir = getenv("HOME"); if (home_dir == nullptr) { std::cout << "cannot find home directory" << std::endl; return nullptr; } #endif return nullptr; } template void print_chunk(T * arr, unsigned len) { for (unsigned i = 0; i < len; i++) { std::cout << arr[i] << ", "; } std::cout << std::endl; } struct mem_cpy_place_holder { static void mem_copy_hook(int * destination, unsigned num) { if (destination == nullptr || num == 0) { throw "bad parameters"; } } }; void finalize(unsigned ret) { /* finalize_util_module(); finalize_numerics_module(); */ // return 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.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.empty()) { time_limit = atoi(time_limit_string.c_str()); } else { time_limit = 0; } } std::string create_output_file_name(bool minimize, std::string file_name, bool use_mpq) { std::string ret = file_name + "_lp_tst_" + (minimize?"min":"max"); if (use_mpq) return ret + "_mpq.out"; return ret + ".out"; } std::string create_output_file_name_for_glpsol(bool minimize, std::string file_name){ return file_name + (minimize?"_min":"_max") + "_glpk_out"; } int run_glpk(std::string file_name, std::string glpk_out_file_name, bool minimize, unsigned time_limit) { std::string minmax(minimize?"--min":"--max"); std::string tmlim = time_limit > 0 ? std::string(" --tmlim ") + std::to_string(time_limit)+ " ":std::string(); std::string command_line = std::string("glpsol --nointopt --nomip ") + minmax + tmlim + + " -o " + glpk_out_file_name +" " + file_name + " > /dev/null"; return system(command_line.c_str()); } std::string get_status(std::string file_name) { std::ifstream f(file_name); if (!f.is_open()) { std::cout << "cannot open " << file_name << std::endl; throw 0; } std::string str; while (getline(f, str)) { if (str.find("Status") != std::string::npos) { vector tokens = split_and_trim(str); if (tokens.size() != 2) { std::cout << "unexpected Status string " << str << std::endl; throw 0; } return tokens[1]; } } std::cout << "cannot find the status line in " << file_name << std::endl; throw 0; } // returns true if the costs should be compared too bool compare_statuses(std::string glpk_out_file_name, std::string lp_out_file_name, unsigned & successes, unsigned & failures) { std::string glpk_status = get_status(glpk_out_file_name); std::string lp_tst_status = get_status(lp_out_file_name); if (glpk_status != lp_tst_status) { if (glpk_status == "UNDEFINED" && (lp_tst_status == "UNBOUNDED" || lp_tst_status == "INFEASIBLE")) { successes++; return false; } else { std::cout << "glpsol and lp_tst disagree: glpsol status is " << glpk_status; std::cout << " but lp_tst status is " << lp_tst_status << std::endl; failures++; return false; } } return lp_tst_status == "OPTIMAL"; } double get_glpk_cost(std::string file_name) { std::ifstream f(file_name); if (!f.is_open()) { std::cout << "cannot open " << file_name << std::endl; throw 0; } std::string str; while (getline(f, str)) { if (str.find("Objective") != std::string::npos) { vector tokens = split_and_trim(str); if (tokens.size() != 5) { std::cout << "unexpected Objective std::string " << str << std::endl; throw 0; } return atof(tokens[3].c_str()); } } std::cout << "cannot find the Objective line in " << file_name << std::endl; throw 0; } double get_lp_tst_cost(std::string file_name) { std::ifstream f(file_name); if (!f.is_open()) { std::cout << "cannot open " << file_name << std::endl; throw 0; } std::string str; std::string cost_string; while (getline(f, str)) { if (str.find("cost") != std::string::npos) { cost_string = str; } } if (cost_string.empty()) { std::cout << "cannot find the cost line in " << file_name << std::endl; throw 0; } vector tokens = split_and_trim(cost_string); if (tokens.size() != 3) { std::cout << "unexpected cost string " << cost_string << std::endl; throw 0; } return atof(tokens[2].c_str()); } bool values_are_one_percent_close(double a, double b) { double maxval = std::max(fabs(a), fabs(b)); if (maxval < 0.000001) { return true; } double one_percent = maxval / 100; return fabs(a - b) <= one_percent; } // returns true if both are optimal void compare_costs(std::string glpk_out_file_name, std::string lp_out_file_name, unsigned & successes, unsigned & failures) { double a = get_glpk_cost(glpk_out_file_name); double b = get_lp_tst_cost(lp_out_file_name); if (values_are_one_percent_close(a, b)) { successes++; } else { failures++; std::cout << "glpsol cost is " << a << " lp_tst cost is " << b << std::endl; } } void compare_with_glpk(std::string glpk_out_file_name, std::string lp_out_file_name, unsigned & successes, unsigned & failures, std::string /*lp_file_name*/) { #ifdef CHECK_GLPK_SOLUTION std::unordered_map * solution_table = get_solution_from_glpsol_output(glpk_out_file_name); if (solution_is_feasible(lp_file_name, *solution_table)) { std::cout << "glpk solution is feasible" << std::endl; } else { std::cout << "glpk solution is infeasible" << std::endl; } delete solution_table; #endif if (compare_statuses(glpk_out_file_name, lp_out_file_name, successes, failures)) { compare_costs(glpk_out_file_name, lp_out_file_name, successes, failures); } } void test_lar_on_file(std::string file_name, argument_parser & args_parser); void process_test_file(std::string test_dir, std::string test_file_name, argument_parser & args_parser, std::string out_dir, unsigned max_iters, unsigned time_limit, unsigned & successes, unsigned & failures, unsigned & inconclusives) { bool use_mpq = args_parser.option_is_used("--mpq"); bool minimize = args_parser.option_is_used("--min"); std::string full_lp_tst_out_name = out_dir + "/" + create_output_file_name(minimize, test_file_name, use_mpq); std::string input_file_name = test_dir + "/" + test_file_name; if (input_file_name[input_file_name.size() - 1] == '~') { // std::cout << "ignoring " << input_file_name << std::endl; return; } std::cout <<"processing " << input_file_name << std::endl; std::ofstream out(full_lp_tst_out_name); if (!out.is_open()) { std::cout << "cannot open file " << full_lp_tst_out_name << std::endl; throw 0; } std::streambuf *coutbuf = std::cout.rdbuf(); // save old buffer std::cout.rdbuf(out.rdbuf()); // redirect std::cout to dir_entry->d_name! bool dual = args_parser.option_is_used("--dual"); try { if (args_parser.option_is_used("--lar")) test_lar_on_file(input_file_name, args_parser); else solve_mps(input_file_name, minimize, max_iters, time_limit, use_mpq, dual, false, args_parser); } catch(...) { std::cout << "catching the failure" << std::endl; failures++; std::cout.rdbuf(coutbuf); // reset to standard output again return; } std::cout.rdbuf(coutbuf); // reset to standard output again if (args_parser.option_is_used("--compare_with_glpk")) { std::string glpk_out_file_name = out_dir + "/" + create_output_file_name_for_glpsol(minimize, std::string(test_file_name)); int glpk_exit_code = run_glpk(input_file_name, glpk_out_file_name, minimize, time_limit); if (glpk_exit_code != 0) { std::cout << "glpk failed" << std::endl; inconclusives++; } else { compare_with_glpk(glpk_out_file_name, full_lp_tst_out_name, successes, failures, input_file_name); } } } /* int my_readdir(DIR *dirp, struct dirent * #ifndef LEAN_WINDOWS entry #endif , struct dirent **result) { #ifdef LEAN_WINDOWS *result = readdir(dirp); // NOLINT return *result != nullptr? 0 : 1; #else return readdir_r(dirp, entry, result); #endif } */ /* vector> get_file_list_of_dir(std::string test_file_dir) { DIR *dir; if ((dir = opendir(test_file_dir.c_str())) == nullptr) { std::cout << "Cannot open directory " << test_file_dir << std::endl; throw 0; } vector> ret; struct dirent entry; struct dirent* result; int return_code; for (return_code = my_readdir(dir, &entry, &result); #ifndef LEAN_WINDOWS result != nullptr && #endif return_code == 0; return_code = my_readdir(dir, &entry, &result)) { DIR *tmp_dp = opendir(result->d_name); struct stat file_record; if (tmp_dp == nullptr) { std::string s = test_file_dir+ "/" + result->d_name; int stat_ret = stat(s.c_str(), & file_record); if (stat_ret!= -1) { ret.push_back(make_pair(result->d_name, file_record.st_size)); } else { perror("stat"); exit(1); } } else { closedir(tmp_dp); } } closedir(dir); return ret; } */ /* struct file_size_comp { unordered_map& m_file_sizes; file_size_comp(unordered_map& fs) :m_file_sizes(fs) {} int operator()(std::string a, std::string b) { std::cout << m_file_sizes.size() << std::endl; std::cout << a << std::endl; std::cout << b << std::endl; auto ls = m_file_sizes.find(a); std::cout << "fa" << std::endl; auto rs = m_file_sizes.find(b); std::cout << "fb" << std::endl; if (ls != m_file_sizes.end() && rs != m_file_sizes.end()) { std::cout << "fc " << std::endl; int r = (*ls < *rs? -1: (*ls > *rs)? 1 : 0); std::cout << "calc r " << std::endl; return r; } else { std::cout << "sc " << std::endl; return 0; } } }; */ struct sort_pred { bool operator()(const std::pair &left, const std::pair &right) { return left.second < right.second; } }; void test_files_from_directory(std::string test_file_dir, argument_parser & args_parser) { /* std::cout << "loading files from directory \"" << test_file_dir << "\"" << std::endl; std::string out_dir = args_parser.get_option_value("--out_dir"); if (out_dir.size() == 0) { out_dir = "/tmp/test"; } DIR *out_dir_p = opendir(out_dir.c_str()); if (out_dir_p == nullptr) { std::cout << "Cannot open output directory \"" << out_dir << "\"" << std::endl; return; } closedir(out_dir_p); vector> files = get_file_list_of_dir(test_file_dir); std::sort(files.begin(), files.end(), sort_pred()); unsigned max_iters, time_limit; get_time_limit_and_max_iters_from_parser(args_parser, time_limit, max_iters); unsigned successes = 0, failures = 0, inconclusives = 0; for (auto & t : files) { process_test_file(test_file_dir, t.first, args_parser, out_dir, max_iters, time_limit, successes, failures, inconclusives); } std::cout << "comparing with glpk: successes " << successes << ", failures " << failures << ", inconclusives " << inconclusives << std::endl; */ } std::unordered_map get_solution_map(lp_solver * lps, mps_reader & reader) { std::unordered_map ret; for (const auto & it : reader.column_names()) { ret[it] = lps->get_column_value_by_name(it); } return ret; } void run_lar_solver(argument_parser & args_parser, lar_solver * solver, mps_reader * reader) { std::string maxng = args_parser.get_option_value("--maxng"); if (!maxng.empty()) { solver->settings().max_number_of_iterations_with_no_improvements = atoi(maxng.c_str()); } if (args_parser.option_is_used("-pd")){ solver->settings().presolve_with_double_solver_for_lar = true; } std::string iter = args_parser.get_option_value("--max_iters"); if (!iter.empty()) { solver->settings().max_total_number_of_iterations = atoi(iter.c_str()); } if (args_parser.option_is_used("--compare_with_primal")){ if (reader == nullptr) { std::cout << "cannot compare with primal, the reader is null " << std::endl; return; } auto * lps = reader->create_solver(false); lps->find_maximal_solution(); std::unordered_map sol = get_solution_map(lps, *reader); std::cout << "status = " << lp_status_to_string(solver->get_status()) << std::endl; return; } stopwatch sw; sw.start(); lp_status status = solver->solve(); std::cout << "status is " << lp_status_to_string(status) << ", processed for " << sw.get_current_seconds() <<" seconds, and " << solver->get_total_iterations() << " iterations" << std::endl; if (solver->get_status() == lp_status::INFEASIBLE) { vector> evidence; solver->get_infeasibility_explanation(evidence); } if (args_parser.option_is_used("--randomize_lar")) { if (solver->get_status() != lp_status::OPTIMAL) { std::cout << "cannot check randomize on an infeazible problem" << std::endl; return; } std::cout << "checking randomize" << std::endl; vector all_vars = solver->get_list_of_all_var_indices(); unsigned m = all_vars.size(); if (m > 100) m = 100; var_index *vars = new var_index[m]; for (unsigned i = 0; i < m; i++) vars[i]=all_vars[i]; solver->random_update(m, vars); delete []vars; } } lar_solver * create_lar_solver_from_file(std::string file_name, argument_parser & args_parser) { if (args_parser.option_is_used("--smt")) { smt_reader reader(file_name); reader.read(); if (!reader.is_ok()){ std::cout << "cannot process " << file_name << std::endl; return nullptr; } return reader.create_lar_solver(); } mps_reader reader(file_name); reader.read(); if (!reader.is_ok()) { std::cout << "cannot process " << file_name << std::endl; return nullptr; } return reader.create_lar_solver(); } void test_lar_on_file(std::string file_name, argument_parser & args_parser) { lar_solver * solver = create_lar_solver_from_file(file_name, args_parser); mps_reader reader(file_name); mps_reader * mps_reader = nullptr; reader.read(); if (reader.is_ok()) { mps_reader = & reader; run_lar_solver(args_parser, solver, mps_reader); } delete solver; } vector get_file_names_from_file_list(std::string filelist) { std::ifstream file(filelist); if (!file.is_open()) { std::cout << "cannot open " << filelist << std::endl; return vector(); } vector ret; bool end; do { std::string s = read_line(end, file); if (end) break; if (s.empty()) break; ret.push_back(s); } while (true); return ret; } void test_lar_solver(argument_parser & args_parser) { std::string file_name = args_parser.get_option_value("--file"); 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.empty()) { for (const std::string & fn : get_file_names_from_file_list(file_list)) test_lar_on_file(fn, args_parser); return; } std::cout << "give option --file or --filelist to test_lar_solver\n"; } void test_numeric_pair() { numeric_pair a; numeric_pair b(2, lp::mpq(6, 2)); a = b; numeric_pair c(0.1, 0.5); a += 2*c; a -= c; lp_assert (a == b + c); numeric_pair d = a * 2; std::cout << a << std::endl; lp_assert(b == b); lp_assert(b < a); lp_assert(b <= a); lp_assert(a > b); lp_assert(a != b); lp_assert(a >= b); lp_assert(-a < b); lp_assert(a < 2 * b); lp_assert(b + b > a); lp_assert(lp::mpq(2.1) * b + b > a); lp_assert(-b * lp::mpq(2.1) - b < lp::mpq(0.99) * a); std::cout << - b * lp::mpq(2.1) - b << std::endl; lp_assert(-b *(lp::mpq(2.1) + 1) == - b * lp::mpq(2.1) - b); } void get_matrix_dimensions(std::ifstream & f, unsigned & m, unsigned & n) { std::string line; getline(f, line); getline(f, line); vector r = split_and_trim(line); m = atoi(r[1].c_str()); getline(f, line); r = split_and_trim(line); n = atoi(r[1].c_str()); } void read_row_cols(unsigned i, static_matrix& A, std::ifstream & f) { do { std::string line; getline(f, line); if (line== "row_end") break; auto r = split_and_trim(line); lp_assert(r.size() == 4); unsigned j = atoi(r[1].c_str()); double v = atof(r[3].c_str()); A.set(i, j, v); } while (true); } bool read_row(static_matrix & A, std::ifstream & f) { std::string line; getline(f, line); if (static_cast(line.find("row")) == -1) return false; auto r = split_and_trim(line); if (r[0] != "row") std::cout << "wrong row line" << line << std::endl; unsigned i = atoi(r[1].c_str()); read_row_cols(i, A, f); return true; } void read_rows(static_matrix& A, std::ifstream & f) { while (read_row(A, f)) {} } void read_basis(vector & basis, std::ifstream & f) { std::cout << "reading basis" << std::endl; std::string line; getline(f, line); lp_assert(line == "basis_start"); do { getline(f, line); if (line == "basis_end") break; unsigned j = atoi(line.c_str()); basis.push_back(j); } while (true); } void read_indexed_vector(indexed_vector & v, std::ifstream & f) { std::string line; getline(f, line); lp_assert(line == "vector_start"); do { getline(f, line); if (line == "vector_end") break; auto r = split_and_trim(line); unsigned i = atoi(r[0].c_str()); double val = atof(r[1].c_str()); v.set_value(val, i); std::cout << "setting value " << i << " = " << val << std::endl; } while (true); } void check_lu_from_file(std::string lufile_name) { std::ifstream f(lufile_name); if (!f.is_open()) { std::cout << "cannot open file " << lufile_name << std::endl; } unsigned m, n; get_matrix_dimensions(f, m, n); std::cout << "init matrix " << m << " by " << n << std::endl; static_matrix A(m, n); read_rows(A, f); vector basis; read_basis(basis, f); indexed_vector v(m); // read_indexed_vector(v, f); f.close(); vector basis_heading; lp_settings settings; vector non_basic_columns; lu> lsuhl(A, basis, settings); indexed_vector d(A.row_count()); unsigned entering = 26; lsuhl.solve_Bd(entering, d, v); #ifdef Z3DEBUG auto B = get_B(lsuhl, basis); vector a(m); A.copy_column_to_vector(entering, a); indexed_vector cd(d); B.apply_from_left(cd.m_data, settings); lp_assert(vectors_are_equal(cd.m_data , a)); #endif } void test_square_dense_submatrix() { std::cout << "testing square_dense_submatrix" << std::endl; unsigned parent_dim = 7; square_sparse_matrix parent(parent_dim, 0); fill_matrix(parent); unsigned index_start = 3; square_dense_submatrix d; d.init(&parent, index_start); for (unsigned i = index_start; i < parent_dim; i++) for (unsigned j = index_start; j < parent_dim; j++) d[i][j] = i*3+j*2; #ifdef Z3DEBUG unsigned dim = parent_dim - index_start; dense_matrix m(dim, dim); for (unsigned i = index_start; i < parent_dim; i++) for (unsigned j = index_start; j < parent_dim; j++) m[i-index_start][j-index_start] = d[i][j]; print_matrix(&m, std::cout); #endif for (unsigned i = index_start; i < parent_dim; i++) for (unsigned j = index_start; j < parent_dim; j++) d[i][j] = d[j][i]; #ifdef Z3DEBUG for (unsigned i = index_start; i < parent_dim; i++) for (unsigned j = index_start; j < parent_dim; j++) m[i-index_start][j-index_start] = d[i][j]; print_matrix(&m, std::cout); std::cout << std::endl; #endif } void print_st(lp_status status) { std::cout << lp_status_to_string(status) << std::endl; } void test_term() { lar_solver solver; unsigned _x = 0; unsigned _y = 1; unsigned _one = 2; var_index x = solver.add_var(_x, false); var_index y = solver.add_var(_y, false); var_index one = solver.add_var(_one, false); vector> term_one; term_one.push_back(std::make_pair(mpq(1), one)); solver.add_constraint(term_one, lconstraint_kind::EQ, mpq(0)); vector> term_ls; term_ls.push_back(std::pair(mpq(1), x)); term_ls.push_back(std::pair(mpq(1), y)); term_ls.push_back(std::make_pair(mpq(3), one)); var_index z = solver.add_term(term_ls); vector> ls; ls.push_back(std::pair(mpq(1), x)); ls.push_back(std::pair(mpq(1), y)); ls.push_back(std::pair(mpq(1), z)); solver.add_constraint(ls, lconstraint_kind::EQ, mpq(0)); ls.clear(); ls.push_back(std::pair(mpq(1), x)); solver.add_constraint(ls, lconstraint_kind::LT, mpq(0)); ls.push_back(std::pair(mpq(2), y)); solver.add_constraint(ls, lconstraint_kind::GT, mpq(0)); auto status = solver.solve(); std::cout << lp_status_to_string(status) << std::endl; std::unordered_map model; if (status != lp_status::OPTIMAL) return; solver.get_model(model); for (auto & t : model) { std::cout << solver.get_variable_name(t.first) << " = " << t.second.get_double() << ","; } std::cout << std::endl; } void test_evidence_for_total_inf_simple(argument_parser & args_parser) { lar_solver solver; var_index x = solver.add_var(0, false); var_index y = solver.add_var(1, false); solver.add_var_bound(x, LE, mpq(-1)); solver.add_var_bound(y, GE, mpq(0)); vector> ls; ls.push_back(std::pair(mpq(1), x)); ls.push_back(std::pair(mpq(1), y)); solver.add_constraint(ls, GE, mpq(1)); ls.pop_back(); ls.push_back(std::pair(- mpq(1), y)); solver.add_constraint(ls, lconstraint_kind::GE, mpq(0)); auto status = solver.solve(); std::cout << lp_status_to_string(status) << std::endl; std::unordered_map model; lp_assert(solver.get_status() == lp_status::INFEASIBLE); } void test_bound_propagation_one_small_sample1() { /* (<= (+ a (* (- 1.0) b)) 0.0) (<= (+ b (* (- 1.0) x_13)) 0.0) --> (<= (+ a (* (- 1.0) c)) 0.0) the inequality on (<= a c) is obtained from a triangle inequality (<= a b) (<= b c). If b becomes basic variable, then it is likely the old solver ends up with a row that implies (<= a c). a - b <= 0.0 b - c <= 0.0 got to get a <= c */ std::function bound_is_relevant = [&](unsigned j, bool is_lower_bound, bool strict, const rational& bound_val) { return true; }; lar_solver ls; unsigned a = ls.add_var(0, false); unsigned b = ls.add_var(1, false); unsigned c = ls.add_var(2, false); vector> coeffs; coeffs.push_back(std::pair(mpq(1), a)); coeffs.push_back(std::pair(mpq(-1), c)); ls.add_term(coeffs); coeffs.pop_back(); coeffs.push_back(std::pair(mpq(-1), b)); ls.add_term(coeffs); coeffs.clear(); coeffs.push_back(std::pair(mpq(1), a)); coeffs.push_back(std::pair(mpq(-1), b)); ls.add_constraint(coeffs, LE, zero_of_type()); coeffs.clear(); coeffs.push_back(std::pair(mpq(1), b)); coeffs.push_back(std::pair(mpq(-1), c)); ls.add_constraint(coeffs, LE, zero_of_type()); vector ev; ls.add_var_bound(a, LE, mpq(1)); ls.solve(); my_bound_propagator bp(ls); ls.propagate_bounds_for_touched_rows(bp); std::cout << " bound ev from test_bound_propagation_one_small_sample1" << std::endl; for (auto & be : bp.m_ibounds) { std::cout << "bound\n"; ls.print_implied_bound(be, std::cout); } } void test_bound_propagation_one_small_samples() { test_bound_propagation_one_small_sample1(); /* (>= x_46 0.0) (<= x_29 0.0) (not (<= x_68 0.0)) (<= (+ (* (/ 1001.0 1998.0) x_10) (* (- 1.0) x_151) x_68) (- (/ 1001.0 999.0))) (<= (+ (* (/ 1001.0 999.0) x_9) (* (- 1.0) x_152) (* (/ 1001.0 999.0) x_151) (* (/ 1001.0 999.0) x_68)) (- (/ 1502501.0 999000.0))) (not (<= (+ (* (/ 999.0 2.0) x_10) (* (- 1.0) x_152) (* (- (/ 999.0 2.0)) x_151)) (/ 1001.0 2.0))) (not (<= x_153 0.0))z (>= (+ x_9 (* (- (/ 1001.0 999.0)) x_10) (* (- 1.0) x_153) (* (- 1.0) x_68)) (/ 5003.0 1998.0)) --> (not (<= (+ x_10 x_46 (* (- 1.0) x_29)) 0.0)) and (<= (+ a (* (- 1.0) b)) 0.0) (<= (+ b (* (- 1.0) x_13)) 0.0) --> (<= (+ a (* (- 1.0) x_13)) 0.0) In the first case, there typically are no atomic formulas for bounding x_10. So there is never some basic lemma of the form (>= x46 0), (<= x29 0), (>= x10 0) -> (not (<= (+ x10 x46 (- x29)) 0)). Instead the bound on x_10 falls out from a bigger blob of constraints. In the second case, the inequality on (<= x19 x13) is obtained from a triangle inequality (<= x19 x9) (<= x9 x13). If x9 becomes basic variable, then it is likely the old solver ends up with a row that implies (<= x19 x13). */ } void test_bound_propagation_one_row() { lar_solver ls; unsigned x0 = ls.add_var(0, false); unsigned x1 = ls.add_var(1, false); vector> c; c.push_back(std::pair(mpq(1), x0)); c.push_back(std::pair(mpq(-1), x1)); ls.add_constraint(c, EQ, one_of_type()); vector ev; ls.add_var_bound(x0, LE, mpq(1)); ls.solve(); my_bound_propagator bp(ls); ls.propagate_bounds_for_touched_rows(bp); } void test_bound_propagation_one_row_with_bounded_vars() { lar_solver ls; unsigned x0 = ls.add_var(0, false); unsigned x1 = ls.add_var(1, false); vector> c; c.push_back(std::pair(mpq(1), x0)); c.push_back(std::pair(mpq(-1), x1)); ls.add_constraint(c, EQ, one_of_type()); vector ev; ls.add_var_bound(x0, GE, mpq(-3)); ls.add_var_bound(x0, LE, mpq(3)); ls.add_var_bound(x0, LE, mpq(1)); ls.solve(); my_bound_propagator bp(ls); ls.propagate_bounds_for_touched_rows(bp); } void test_bound_propagation_one_row_mixed() { lar_solver ls; unsigned x0 = ls.add_var(0, false); unsigned x1 = ls.add_var(1, false); vector> c; c.push_back(std::pair(mpq(1), x0)); c.push_back(std::pair(mpq(-1), x1)); ls.add_constraint(c, EQ, one_of_type()); vector ev; ls.add_var_bound(x1, LE, mpq(1)); ls.solve(); my_bound_propagator bp(ls); ls.propagate_bounds_for_touched_rows(bp); } void test_bound_propagation_two_rows() { lar_solver ls; unsigned x = ls.add_var(0, false); unsigned y = ls.add_var(1, false); unsigned z = ls.add_var(2, false); vector> c; c.push_back(std::pair(mpq(1), x)); c.push_back(std::pair(mpq(2), y)); c.push_back(std::pair(mpq(3), z)); ls.add_constraint(c, GE, one_of_type()); c.clear(); c.push_back(std::pair(mpq(3), x)); c.push_back(std::pair(mpq(2), y)); c.push_back(std::pair(mpq(y), z)); ls.add_constraint(c, GE, one_of_type()); ls.add_var_bound(x, LE, mpq(2)); vector ev; ls.add_var_bound(y, LE, mpq(1)); ls.solve(); my_bound_propagator bp(ls); ls.propagate_bounds_for_touched_rows(bp); } void test_total_case_u() { std::cout << "test_total_case_u\n"; lar_solver ls; unsigned x = ls.add_var(0, false); unsigned y = ls.add_var(1, false); unsigned z = ls.add_var(2, false); vector> c; c.push_back(std::pair(mpq(1), x)); c.push_back(std::pair(mpq(2), y)); c.push_back(std::pair(mpq(3), z)); ls.add_constraint(c, LE, one_of_type()); ls.add_var_bound(x, GE, zero_of_type()); ls.add_var_bound(y, GE, zero_of_type()); vector ev; ls.add_var_bound(z, GE, zero_of_type()); ls.solve(); my_bound_propagator bp(ls); ls.propagate_bounds_for_touched_rows(bp); } bool contains_j_kind(unsigned j, lconstraint_kind kind, const mpq & rs, const vector & ev) { for (auto & e : ev) { if (e.m_j == j && e.m_bound == rs && e.kind() == kind) return true; } return false; } void test_total_case_l(){ std::cout << "test_total_case_l\n"; lar_solver ls; unsigned x = ls.add_var(0, false); unsigned y = ls.add_var(1, false); unsigned z = ls.add_var(2, false); vector> c; c.push_back(std::pair(mpq(1), x)); c.push_back(std::pair(mpq(2), y)); c.push_back(std::pair(mpq(3), z)); ls.add_constraint(c, GE, one_of_type()); ls.add_var_bound(x, LE, one_of_type()); ls.add_var_bound(y, LE, one_of_type()); ls.settings().presolve_with_double_solver_for_lar = true; vector ev; ls.add_var_bound(z, LE, zero_of_type()); ls.solve(); my_bound_propagator bp(ls); ls.propagate_bounds_for_touched_rows(bp); lp_assert(ev.size() == 4); lp_assert(contains_j_kind(x, GE, - one_of_type(), ev)); } void test_bound_propagation() { test_total_case_u(); test_bound_propagation_one_small_samples(); test_bound_propagation_one_row(); test_bound_propagation_one_row_with_bounded_vars(); test_bound_propagation_two_rows(); test_bound_propagation_one_row_mixed(); test_total_case_l(); } void test_int_set() { int_set s(4); s.insert(2); s.print(std::cout); s.insert(1); s.insert(2); s.print(std::cout); lp_assert(s.contains(2)); lp_assert(s.size() == 2); s.erase(2); lp_assert(s.size() == 1); s.erase(2); lp_assert(s.size() == 1); s.print(std::cout); s.insert(3); s.insert(2); s.clear(); lp_assert(s.size() == 0); } void test_rationals_no_numeric_pairs() { stopwatch sw; vector c; for (unsigned j = 0; j < 10; j ++) c.push_back(mpq(my_random()%100, 1 + my_random()%100 )); vector x; for (unsigned j = 0; j < 10; j ++) x.push_back(mpq(my_random()%100, 1 + my_random()%100 )); unsigned k = 500000; mpq r=zero_of_type(); sw.start(); for (unsigned j = 0; j < k; j++){ mpq val = zero_of_type(); for (unsigned j=0;j< c.size(); j++){ val += c[j]*x[j]; } r += val; } sw.stop(); std::cout << "operation with rationals no pairs " << sw.get_seconds() << std::endl; std::cout << T_to_string(r) << std::endl; } void test_rationals_no_numeric_pairs_plus() { stopwatch sw; vector c; for (unsigned j = 0; j < 10; j ++) c.push_back(mpq(my_random()%100, 1 + my_random()%100 )); vector x; for (unsigned j = 0; j < 10; j ++) x.push_back(mpq(my_random()%100, 1 + my_random()%100 )); unsigned k = 500000; mpq r=zero_of_type(); sw.start(); for (unsigned j = 0; j < k; j++){ mpq val = zero_of_type(); for (unsigned j=0;j< c.size(); j++){ val = val + c[j]*x[j]; } r = r + val; } sw.stop(); std::cout << "operation with rationals no pairs " << sw.get_seconds() << std::endl; std::cout << T_to_string(r) << std::endl; } void test_rationals() { stopwatch sw; vector c; for (unsigned j = 0; j < 10; j ++) c.push_back(mpq(my_random()%100, 1 + my_random()%100)); vector> x; for (unsigned j = 0; j < 10; j ++) x.push_back(mpq(my_random()%100, 1 + my_random()%100 )); std::cout << "x = "; print_vector(x, std::cout); unsigned k = 1000000; numeric_pair r=zero_of_type>(); sw.start(); for (unsigned j = 0; j < k; j++) { for (unsigned i = 0; i < c.size(); i++) { r+= c[i] * x[i]; } } sw.stop(); std::cout << "operation with rationals " << sw.get_seconds() << std::endl; std::cout << T_to_string(r) << std::endl; } void get_random_interval(bool& neg_inf, bool& pos_inf, int& x, int &y) { int i = my_random() % 10; if (i == 0) { neg_inf = true; } else { neg_inf = false; x = my_random() % 100; } i = my_random() % 10; if (i == 0) { pos_inf = true; } else { pos_inf = false; if (!neg_inf) { y = x + my_random() % (101 - x); lp_assert(y >= x); } else { y = my_random() % 100; } } lp_assert((neg_inf || (0 <= x && x <= 100)) && (pos_inf || (0 <= y && y <= 100))); } void test_gomory_cut_0() { gomory_test g( [](unsigned j) { return "v" + T_to_string(j);} // name_function_p , [](unsigned j) { //get_value_p if (j == 1) return mpq(2730, 1727); if (j == 2) return zero_of_type(); if (j == 3) return mpq(3); lp_assert(false); return zero_of_type(); }, [](unsigned j) { // at_low_p if (j == 1) return false; if (j == 2) return true; if (j == 3) return true; lp_assert(false); return false; }, [](unsigned j) { // at_upper if (j == 1) return false; if (j == 2) return true; if (j == 3) return false; lp_assert(false); return false; }, [](unsigned j) { // lower_bound if (j == 1) { lp_assert(false); //unlimited from below return 0; } if (j == 2) return 0; if (j == 3) return 3; lp_assert(false); return 0; }, [](unsigned j) { // upper if (j == 1) { lp_assert(false); //unlimited from above return 0; } if (j == 2) return 0; if (j == 3) return 10; lp_assert(false); return 0; }, [] (unsigned) { return 0; }, [] (unsigned) { return 0; } ); lar_term t; mpq k; explanation expl; unsigned inf_col = 1; vector> row; row.push_back(std::make_pair(mpq(1), 1)); row.push_back(std::make_pair(mpq(2731, 1727), 2)); row.push_back(std::make_pair(mpq(-910, 1727), 3)); g.mk_gomory_cut(t, k, expl, inf_col, row); } void test_gomory_cut_1() { gomory_test g( [](unsigned j) { return "v" + T_to_string(j);} // name_function_p , [](unsigned j) { //get_value_p if (j == 1) return mpq(-2); if (j == 2) return mpq(4363334, 2730001); if (j == 3) return mpq(1); lp_assert(false); return zero_of_type(); }, [](unsigned j) { // at_low_p if (j == 1) return false; if (j == 2) return false; if (j == 3) return true; lp_assert(false); return false; }, [](unsigned j) { // at_upper if (j == 1) return true; if (j == 2) return false; if (j == 3) return true; lp_assert(false); return false; }, [](unsigned j) { // lower_bound if (j == 1) { lp_assert(false); //unlimited from below return 0; } if (j == 2) return 1; if (j == 3) return 1; lp_assert(false); return 0; }, [](unsigned j) { // upper if (j == 1) { return -2; } if (j == 2) return 3333; if (j == 3) return 10000; lp_assert(false); return 0; }, [] (unsigned) { return 0; }, [] (unsigned) { return 0; } ); lar_term t; mpq k; explanation expl; unsigned inf_col = 2; vector> row; row.push_back(std::make_pair(mpq(1726667, 2730001), 1)); row.push_back(std::make_pair(mpq(-910000, 2730001), 3)); row.push_back(std::make_pair(mpq(1), 2)); g.mk_gomory_cut(t, k, expl, inf_col, row); } void call_hnf(general_matrix & A); void test_hnf_m_less_than_n() { #ifdef Z3DEBUG general_matrix A; vector v; // example 4.3 from Nemhauser, Wolsey v.push_back(mpq(2)); v.push_back(mpq(6)); v.push_back(mpq(1)); v.push_back(mpq(3)); A.push_row(v); v.clear(); v.push_back(mpq(4)); v.push_back(mpq(7)); v.push_back(mpq(7)); v.push_back(mpq(3)); A.push_row(v); v.clear(); v.push_back(mpq(0)); v.push_back(mpq(0)); v.push_back(mpq(1)); v.push_back(mpq(5)); A.push_row(v); call_hnf(A); #endif } void test_hnf_m_greater_than_n() { #ifdef Z3DEBUG general_matrix A; vector v; v.push_back(mpq(2)); v.push_back(mpq(6)); A.push_row(v); v.clear(); v.push_back(mpq(4)); v.push_back(mpq(7)); A.push_row(v); v.clear(); v.push_back(mpq(0)); v.push_back(mpq(0)); A.push_row(v); v.clear(); v.push_back(mpq(12)); v.push_back(mpq(55)); A.push_row(v); call_hnf(A); #endif } void cutting_the_mix_example_1() { mpq sev(7); mpq nine(9); mpq d, u, vv; hnf_calc::extended_gcd_minimal_uv(sev, nine, d, u, vv); std::cout << "d = " << d << ", u = " << u << ", vv = " << vv << std::endl; hnf_calc::extended_gcd_minimal_uv(sev, -nine, d, u, vv); std::cout << "d = " << d << ", u = " << u << ", vv = " << vv << std::endl; hnf_calc::extended_gcd_minimal_uv(-nine, -nine, d, u, vv); std::cout << "d = " << d << ", u = " << u << ", vv = " << vv << std::endl; hnf_calc::extended_gcd_minimal_uv(-sev*2, sev, d, u, vv); std::cout << "d = " << d << ", u = " << u << ", vv = " << vv << std::endl; hnf_calc::extended_gcd_minimal_uv(mpq(24), mpq(-7), d, u, vv); std::cout << "d = " << d << ", u = " << u << ", vv = " << vv << std::endl; hnf_calc::extended_gcd_minimal_uv(-mpq(24), mpq(7), d, u, vv); std::cout << "d = " << d << ", u = " << u << ", vv = " << vv << std::endl; hnf_calc::extended_gcd_minimal_uv(mpq(24), mpq(7), d, u, vv); std::cout << "d = " << d << ", u = " << u << ", vv = " << vv << std::endl; hnf_calc::extended_gcd_minimal_uv(-mpq(21), mpq(7), d, u, vv); std::cout << "d = " << d << ", u = " << u << ", vv = " << vv << std::endl; hnf_calc::extended_gcd_minimal_uv(mpq(21), -mpq(7), d, u, vv); std::cout << "d = " << d << ", u = " << u << ", vv = " << vv << std::endl; } #ifdef Z3DEBUG void fill_general_matrix(general_matrix & M) { unsigned m = M.row_count(); unsigned n = M.column_count(); for (unsigned i = 0; i < m; i++) for (unsigned j = 0; j < n; j++) M[i][j] = mpq(static_cast(my_random() % 13) - 6); } void call_hnf(general_matrix& A) { svector r; mpq d = hnf_calc::determinant_of_rectangular_matrix(A, r, mpq((int)1000000000)); A.shrink_to_rank(r); hnf h(A, d); } void test_hnf_for_dim(int m) { general_matrix M(m, m + my_random() % m); fill_general_matrix(M); call_hnf(M); } void test_hnf_1_2() { std::cout << "test_hnf_1_2" << std::endl; general_matrix A; vector v; v.push_back(mpq(5)); v.push_back(mpq(26)); A.push_row(v); call_hnf(A); std::cout << "test_hnf_1_2 passed" << std::endl; } void test_hnf_2_2() { std::cout << "test_hnf_2_2" << std::endl; general_matrix A; vector v; v.push_back(mpq(5)); v.push_back(mpq(26)); A.push_row(v); v.clear(); v.push_back(mpq(2)); v.push_back(mpq(11)); A.push_row(v); call_hnf(A); std::cout << "test_hnf_2_2 passed" << std::endl; } void test_hnf_3_3() { std::cout << "test_hnf_3_3" << std::endl; general_matrix A; vector v; v.push_back(mpq(-3)); v.push_back(mpq(0)); v.push_back(mpq(-1)); A.push_row(v); v.clear(); v.push_back(mpq(-1)); v.push_back(mpq(0)); v.push_back(mpq(-6)); A.push_row(v); v.clear(); v.push_back(mpq(-2)); v.push_back(mpq(-4)); v.push_back(mpq(-3)); A.push_row(v); call_hnf(A); std::cout << "test_hnf_3_3 passed" << std::endl; } void test_hnf_4_4() { std::cout << "test_hnf_4_4" << std::endl; general_matrix A; vector v; v.push_back(mpq(4)); v.push_back(mpq(3)); v.push_back(mpq(-5)); v.push_back(mpq(6)); A.push_row(v); v.clear(); v.push_back(mpq(1)); v.push_back(mpq(-3)); v.push_back(mpq(1)); v.push_back(mpq(-4)); A.push_row(v); v.clear(); v.push_back(mpq(4)); v.push_back(mpq(4)); v.push_back(mpq(4)); v.push_back(mpq(4)); A.push_row(v); v.clear(); v.push_back(mpq(2)); v.push_back(mpq(-2)); v.push_back(mpq(-5)); v.push_back(mpq(6)); A.push_row(v); call_hnf(A); std::cout << "test_hnf_4_4 passed" << std::endl; } void test_hnf_5_5() { std::cout << "test_hnf_5_5" << std::endl; general_matrix A; vector v; v.push_back(mpq(-4)); v.push_back(mpq(5)); v.push_back(mpq(-5)); v.push_back(mpq(1)); v.push_back(mpq(-3)); A.push_row(v); v.clear(); v.push_back(mpq(3)); v.push_back(mpq(-1)); v.push_back(mpq(2)); v.push_back(mpq(3)); v.push_back(mpq(-5)); A.push_row(v); v.clear(); v.push_back(mpq(0)); v.push_back(mpq(6)); v.push_back(mpq(-5)); v.push_back(mpq(-6)); v.push_back(mpq(-2)); A.push_row(v); v.clear(); v.push_back(mpq(1)); v.push_back(mpq(0)); v.push_back(mpq(-4)); v.push_back(mpq(-4)); v.push_back(mpq(4)); A.push_row(v); v.clear(); v.push_back(mpq(-2)); v.push_back(mpq(3)); v.push_back(mpq(6)); v.push_back(mpq(-5)); v.push_back(mpq(-1)); A.push_row(v); call_hnf(A); std::cout << "test_hnf_5_5 passed" << std::endl; } void test_small_generated_hnf() { std::cout << "test_small_rank_hnf" << std::endl; general_matrix A; vector v; v.push_back(mpq(5)); v.push_back(mpq(26)); A.push_row(v); v.clear(); v.push_back(zero_of_type()); v.push_back(zero_of_type()); A.push_row(v); call_hnf(A); std::cout << "test_small_rank_hnf passed" << std::endl; } void test_larger_generated_hnf() { std::cout << "test_larger_generated_rank_hnf" << std::endl; general_matrix A; vector v; v.clear(); v.push_back(mpq(5)); v.push_back(mpq(6)); v.push_back(mpq(3)); v.push_back(mpq(1)); A.push_row(v); v.clear(); v.push_back(mpq(5)); v.push_back(mpq(2)); v.push_back(mpq(3)); v.push_back(mpq(7)); A.push_row(v); v.clear(); v.push_back(mpq(5)); v.push_back(mpq(6)); v.push_back(mpq(3)); v.push_back(mpq(1)); A.push_row(v); v.clear(); v.push_back(mpq(5)); v.push_back(mpq(2)); v.push_back(mpq(3)); v.push_back(mpq(7)); A.push_row(v); call_hnf(A); std::cout << "test_larger_generated_rank_hnf passed" << std::endl; } #endif void test_maximize_term() { std::cout << "test_maximize_term\n"; lar_solver solver; int_solver i_solver(&solver); // have to create it too unsigned _x = 0; unsigned _y = 1; var_index x = solver.add_var(_x, false); var_index y = solver.add_var(_y, true); vector> term_ls; term_ls.push_back(std::pair(mpq(1), x)); term_ls.push_back(std::pair(mpq(-1), y)); unsigned term_x_min_y = solver.add_term(term_ls); term_ls.clear(); term_ls.push_back(std::pair(mpq(2), x)); term_ls.push_back(std::pair(mpq(2), y)); unsigned term_2x_pl_2y = solver.add_term(term_ls); solver.add_var_bound(term_x_min_y, LE, zero_of_type()); solver.add_var_bound(term_2x_pl_2y, LE, mpq(5)); solver.find_feasible_solution(); lp_assert(solver.get_status() == lp_status::OPTIMAL); solver.print_constraints(std::cout); std::unordered_map model; solver.get_model(model); for (auto p : model) { std::cout<< "v[" << p.first << "] = " << p.second << std::endl; } std::cout << "calling int_solver\n"; lia_move lm = i_solver.check(); VERIFY(lm == lia_move::sat); impq term_max; lp_status st = solver.maximize_term(term_2x_pl_2y, term_max); std::cout << "status = " << lp_status_to_string(st) << std::endl; std::cout << "term_max = " << term_max << std::endl; solver.get_model(model); for (auto p : model) { std::cout<< "v[" << p.first << "] = " << p.second << std::endl; } } #ifdef Z3DEBUG void test_hnf() { test_larger_generated_hnf(); test_small_generated_hnf(); test_hnf_1_2(); test_hnf_3_3(); test_hnf_4_4(); test_hnf_5_5(); test_hnf_2_2(); for (unsigned k=1000; k>0; k--) for (int i = 1; i < 8; i++) test_hnf_for_dim(i); cutting_the_mix_example_1(); // test_hnf_m_less_than_n(); // test_hnf_m_greater_than_n(); } #endif void test_gomory_cut() { test_gomory_cut_0(); test_gomory_cut_1(); } void test_lp_local(int argn, char**argv) { // initialize_util_module(); // initialize_numerics_module(); int ret; argument_parser args_parser(argn, argv); setup_args_parser(args_parser); if (!args_parser.parse()) { std::cout << args_parser.m_error_message << std::endl; std::cout << args_parser.usage_string(); ret = 1; return finalize(ret); } args_parser.print(); if (args_parser.option_is_used("-hnf")) { #ifdef Z3DEBUG test_hnf(); #endif return finalize(0); } if (args_parser.option_is_used("-gomory")) { test_gomory_cut(); return finalize(0); } if (args_parser.option_is_used("--test_mpq")) { test_rationals(); return finalize(0); } if (args_parser.option_is_used("--test_mpq_np")) { test_rationals_no_numeric_pairs(); return finalize(0); } if (args_parser.option_is_used("--test_mpq_np_plus")) { test_rationals_no_numeric_pairs_plus(); return finalize(0); } if (args_parser.option_is_used("--test_int_set")) { test_int_set(); return finalize(0); } if (args_parser.option_is_used("--bp")) { test_bound_propagation(); return finalize(0); } std::string lufile = args_parser.get_option_value("--checklu"); if (!lufile.empty()) { check_lu_from_file(lufile); return finalize(0); } #ifdef Z3DEBUG if (args_parser.option_is_used("--test_swaps")) { square_sparse_matrix m(10, 0); fill_matrix(m); test_swap_rows_with_permutation(m); test_swap_cols_with_permutation(m); return finalize(0); } #endif if (args_parser.option_is_used("--test_perm")) { test_permutations(); return finalize(0); } if (args_parser.option_is_used("--test_file_directory")) { test_files_from_directory(args_parser.get_option_value("--test_file_directory"), args_parser); return finalize(0); } std::string file_list = args_parser.get_option_value("--filelist"); 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); } if (args_parser.option_is_used("-tbq")) { test_binary_priority_queue(); ret = 0; return finalize(ret); } #ifdef Z3DEBUG lp_settings settings; update_settings(args_parser, settings); if (args_parser.option_is_used("--test_lu")) { test_lu(settings); ret = 0; return finalize(ret); } if (args_parser.option_is_used("--test_small_lu")) { test_small_lu(settings); ret = 0; return finalize(ret); } if (args_parser.option_is_used("--lar")){ std::cout <<"calling test_lar_solver" << std::endl; test_lar_solver(args_parser); return finalize(0); } if (args_parser.option_is_used("--test_larger_lu")) { test_larger_lu(settings); ret = 0; return finalize(ret); } if (args_parser.option_is_used("--test_larger_lu_with_holes")) { test_larger_lu_with_holes(settings); ret = 0; return finalize(ret); } #endif if (args_parser.option_is_used("--eti")) { test_evidence_for_total_inf_simple(args_parser); ret = 0; return finalize(ret); } if (args_parser.option_is_used("--maximize_term")) { test_maximize_term(); ret = 0; return finalize(ret); } if (args_parser.option_is_used("--test_lp_0")) { test_lp_0(); ret = 0; return finalize(ret); } if (args_parser.option_is_used("--smap")) { test_stacked(); ret = 0; return finalize(ret); } if (args_parser.option_is_used("--term")) { test_term(); ret = 0; return finalize(ret); } unsigned max_iters; unsigned time_limit; get_time_limit_and_max_iters_from_parser(args_parser, time_limit, max_iters); 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.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); } if (args_parser.option_is_used("--solve_some_mps")) { #if _LINUX_ solve_some_mps(args_parser); #endif ret = 0; return finalize(ret); } // lp::ccc = 0; return finalize(0); test_init_U(); test_replace_column(); #ifdef Z3DEBUG square_sparse_matrix_with_permutations_test(); test_dense_matrix(); test_swap_operations(); test_permutations(); test_pivot_like_swaps_and_pivot(); #endif tst1(); std::cout << "done with LP tests\n"; return finalize(0); // has_violations() ? 1 : 0); } } void tst_lp(char ** argv, int argc, int& i) { lp::test_lp_local(argc - 2, argv + 2); } #ifdef Z3DEBUG namespace lp { template void print_matrix(general_matrix&, std::ostream&); } #endif z3-z3-4.8.7/src/test/lp/lp_main.cpp000066400000000000000000000004711356505360400167350ustar00rootroot00000000000000void gparams_register_modules(){} void mem_initialize() {} void mem_finalize() {} #include "util/rational.h" namespace lp { void test_lp_local(int argc, char**argv); } int main(int argn, char**argv){ rational::initialize(); lp::test_lp_local(argn, argv); rational::finalize(); return 0; } z3-z3-4.8.7/src/test/lp/smt_reader.h000066400000000000000000000317471356505360400171220ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #pragma once // reads an MPS file reperesenting a Mixed Integer Program #include #include #include #include "util/lp/lp_primal_simplex.h" #include "util/lp/lp_dual_simplex.h" #include "util/lp/lar_solver.h" #include #include #include #include #include "util/lp/mps_reader.h" #include "util/lp/ul_pair.h" #include "util/lp/lar_constraints.h" #include #include namespace lp { template T from_string(const std::string& str) { std::istringstream ss(str); T ret; ss >> ret; return ret; } class smt_reader { public: struct lisp_elem { std::string m_head; std::vector m_elems; void print() { if (!m_elems.empty()) { std::cout << '('; std::cout << m_head << ' '; for (auto & el : m_elems) el.print(); std::cout << ')'; } else { std::cout << " " << m_head; } } unsigned size() const { return static_cast(m_elems.size()); } bool is_simple() const { return size() == 0; } }; struct formula_constraint { lconstraint_kind m_kind; std::vector> m_coeffs; mpq m_right_side; void add_pair(mpq c, std::string name) { m_coeffs.push_back(make_pair(c, name)); } formula_constraint() : m_right_side(numeric_traits::zero()) {} }; lisp_elem m_formula_lisp_elem; std::unordered_map m_name_to_var_index; std::vector m_constraints; bool m_is_OK; unsigned m_line_number; std::string m_file_name; std::ifstream m_file_stream; std::string m_line; smt_reader(std::string file_name): m_is_OK(true), m_line_number(0), m_file_name(file_name), m_file_stream(file_name) { } void set_error() { std::cout << "setting error" << std::endl; m_is_OK = false; } bool is_ok() { return m_is_OK; } bool prefix(const char * pr) { return m_line.find(pr) == 0; } int first_separator() { unsigned blank_pos = static_cast(m_line.find(' ')); unsigned br_pos = static_cast(m_line.find('(')); unsigned reverse_br_pos = static_cast(m_line.find(')')); return std::min(blank_pos, std::min(br_pos, reverse_br_pos)); } void fill_lisp_elem(lisp_elem & lm) { if (m_line[0] == '(') fill_nested_elem(lm); else fill_simple_elem(lm); } void fill_simple_elem(lisp_elem & lm) { int separator = first_separator(); lp_assert(-1 != separator && separator != 0); lm.m_head = m_line.substr(0, separator); m_line = m_line.substr(separator); } void fill_nested_elem(lisp_elem & lm) { lp_assert(m_line[0] == '('); m_line = m_line.substr(1); int separator = first_separator(); lm.m_head = m_line.substr(0, separator); m_line = m_line.substr(lm.m_head.size()); eat_blanks(); while (!m_line.empty()) { if (m_line[0] == '(') { lisp_elem el; fill_nested_elem(el); lm.m_elems.push_back(el); } else { if (m_line[0] == ')') { m_line = m_line.substr(1); break; } lisp_elem el; fill_simple_elem(el); lm.m_elems.push_back(el); } eat_blanks(); } } void eat_blanks() { while (!m_line.empty()) { if (m_line[0] == ' ') m_line = m_line.substr(1); else break; } } void fill_formula_elem() { fill_lisp_elem(m_formula_lisp_elem); } void parse_line() { if (m_line.find(":formula") == 0) { int first_br = static_cast(m_line.find('(')); if (first_br == -1) { std::cout << "empty formula" << std::endl; return; } m_line = m_line.substr(first_br); fill_formula_elem(); } } void set_constraint_kind(formula_constraint & c, lisp_elem & el) { if (el.m_head == "=") { c.m_kind = EQ; } else if (el.m_head == ">=") { c.m_kind = GE; } else if (el.m_head == "<=") { c.m_kind = LE; } else if (el.m_head == ">") { c.m_kind = GT; } else if (el.m_head == "<") { c.m_kind = LT; } else { std::cout << "kind " << el.m_head << " is not supported " << std::endl; set_error(); } } 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_right_side(c, el.m_elems[1]); } bool is_integer(std::string & s) { if (s.empty()) return false; return atoi(s.c_str()) != 0 || isdigit(s.c_str()[0]); } void add_complex_sum_elem(formula_constraint & c, lisp_elem & el) { if (el.m_head == "*") { add_mult_elem(c, el.m_elems); } else if (el.m_head == "~") { lisp_elem & minel = el.m_elems[0]; lp_assert(minel.is_simple()); c.m_right_side += mpq(str_to_int(minel.m_head)); } else { std::cout << "unexpected input " << el.m_head << std::endl; set_error(); return; } } std::string get_name(lisp_elem & name) { lp_assert(name.is_simple()); lp_assert(!is_integer(name.m_head)); return name.m_head; } void add_mult_elem(formula_constraint & c, std::vector & els) { lp_assert(els.size() == 2); mpq coeff = get_coeff(els[0]); std::string col_name = get_name(els[1]); c.add_pair(coeff, col_name); } mpq get_coeff(lisp_elem & le) { if (le.is_simple()) { return mpq(str_to_int(le.m_head)); } else { lp_assert(le.m_head == "~"); lp_assert(le.size() == 1); lisp_elem & el = le.m_elems[0]; lp_assert(el.is_simple()); return -mpq(str_to_int(el.m_head)); } } int str_to_int(std::string & s) { lp_assert(is_integer(s)); return atoi(s.c_str()); } void add_sum_elem(formula_constraint & c, lisp_elem & el) { if (el.size()) { add_complex_sum_elem(c, el); } else { lp_assert(is_integer(el.m_head)); int v = atoi(el.m_head.c_str()); mpq vr(v); c.m_right_side -= vr; } } void add_sum(formula_constraint & c, std::vector & sum_els) { for (auto & el : sum_els) add_sum_elem(c, el); } void set_constraint_coeffs_on_coeff_element(formula_constraint & c, lisp_elem & el) { if (el.m_head == "*") { add_mult_elem(c, el.m_elems); } else if (el.m_head == "+") { add_sum(c, el.m_elems); } else { lp_assert(false); // unexpected input } } void create_constraint(lisp_elem & el) { formula_constraint c; set_constraint_kind(c, el); set_constraint_coeffs(c, el); m_constraints.push_back(c); } void fill_constraints() { if (m_formula_lisp_elem.m_head != "and") { std::cout << "unexpected top element " << m_formula_lisp_elem.m_head << std::endl; set_error(); return; } for (auto & el : m_formula_lisp_elem.m_elems) create_constraint(el); } void read() { if (!m_file_stream.is_open()){ std::cout << "cannot open file " << m_file_name << std::endl; set_error(); return; } while (m_is_OK && getline(m_file_stream, m_line)) { parse_line(); m_line_number++; } m_file_stream.close(); fill_constraints(); } /* void fill_lar_solver_on_row(row * row, lar_solver *solver) { if (row->m_name != m_cost_row_name) { lar_constraint c(get_lar_relation_from_row(row->m_type), row->m_right_side); for (auto s : row->m_row_columns) { var_index i = solver->add_var(s.first); c.add_variable_to_constraint(i, s.second); } solver->add_constraint(&c); } else { // ignore the cost row } } void fill_lar_solver_on_rows(lar_solver * solver) { for (auto row_it : m_rows) { fill_lar_solver_on_row(row_it.second, solver); } } void create_low_constraint_for_var(column* col, bound * b, lar_solver *solver) { lar_constraint c(GE, b->m_low); var_index i = solver->add_var(col->m_name); c.add_variable_to_constraint(i, numeric_traits::one()); solver->add_constraint(&c); } void create_upper_constraint_for_var(column* col, bound * b, lar_solver *solver) { lar_constraint c(LE, b->m_upper); var_index i = solver->add_var(col->m_name); c.add_variable_to_constraint(i, numeric_traits::one()); solver->add_constraint(&c); } void create_equality_contraint_for_var(column* col, bound * b, lar_solver *solver) { lar_constraint c(EQ, b->m_fixed_value); var_index i = solver->add_var(col->m_name); c.add_variable_to_constraint(i, numeric_traits::one()); solver->add_constraint(&c); } void fill_lar_solver_on_columns(lar_solver * solver) { for (auto s : m_columns) { mps_reader::column * col = s.second; solver->add_var(col->m_name); auto b = col->m_bound; if (b == nullptr) return; if (b->m_free) continue; if (b->m_low_is_set) { create_low_constraint_for_var(col, b, solver); } if (b->m_upper_is_set) { create_upper_constraint_for_var(col, b, solver); } if (b->m_value_is_fixed) { create_equality_contraint_for_var(col, b, solver); } } } */ unsigned register_name(std::string s) { auto it = m_name_to_var_index.find(s); if (it!= m_name_to_var_index.end()) return it->second; unsigned ret = static_cast(m_name_to_var_index.size()); m_name_to_var_index[s] = ret; return ret; } void add_constraint_to_solver(lar_solver * solver, formula_constraint & fc) { vector> ls; for (auto & it : fc.m_coeffs) { ls.push_back(std::make_pair(it.first, solver->add_var(register_name(it.second), false))); } solver->add_constraint(ls, fc.m_kind, fc.m_right_side); } void fill_lar_solver(lar_solver * solver) { for (formula_constraint & fc : m_constraints) add_constraint_to_solver(solver, fc); } lar_solver * create_lar_solver() { lar_solver * ls = new lar_solver(); fill_lar_solver(ls); return ls; } }; } z3-z3-4.8.7/src/test/lp/test_file_reader.h000066400000000000000000000033431356505360400202640ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #pragma once // reads a text file #include #include #include #include #include #include "util/lp/lp_utils.h" #include "util/lp/lp_solver.h" namespace lp { template struct test_result { lp_status m_status; T m_cost; std::unordered_map column_values; }; template class test_file_reader { struct raw_blob { std::vector m_unparsed_strings; std::vector m_blobs; }; struct test_file_blob { std::string m_name; std::string m_content; std::unordered_map m_table; std::unordered_map m_blobs; test_result * get_test_result() { test_result * tr = new test_result(); throw "not impl"; return tr; } }; std::ifstream m_file_stream; public: // constructor test_file_reader(std::string file_name) : m_file_stream(file_name) { if (!m_file_stream.is_open()) { std::cout << "cannot open file " << "\'" << file_name << "\'" << std::endl; } } raw_blob scan_to_row_blob() { } test_file_blob scan_row_blob_to_test_file_blob(raw_blob /* rblob */) { } test_result * get_test_result() { if (!m_file_stream.is_open()) { return nullptr; } raw_blob rblob = scan_to_row_blob(); test_file_blob tblob = scan_row_blob_to_test_file_blob(rblob); return tblob.get_test_result(); } }; } z3-z3-4.8.7/src/test/main.cpp000066400000000000000000000166261356505360400156400ustar00rootroot00000000000000#include #include #include #include #include "util/util.h" #include "util/trace.h" #include "util/debug.h" #include "util/timeit.h" #include "util/warning.h" #include "util/memory_manager.h" #include "util/gparams.h" // // Unit tests fail by asserting. // If they return, we assume the unit test succeeds // and print "PASS" to indicate success. // #define TST(MODULE) { \ std::string s("test "); \ s += #MODULE; \ void tst_##MODULE(); \ if (do_display_usage) \ std::cout << " " << #MODULE << "\n"; \ for (int i = 0; i < argc; i++) \ if (test_all || strcmp(argv[i], #MODULE) == 0) { \ enable_trace(#MODULE); \ enable_debug(#MODULE); \ timeit timeit(true, s.c_str()); \ tst_##MODULE(); \ std::cout << "PASS" << std::endl; \ } \ } #define TST_ARGV(MODULE) { \ std::string s("test "); \ s += #MODULE; \ void tst_##MODULE(char** argv, int argc, int& i); \ if (do_display_usage) \ std::cout << " " << #MODULE << "(...)\n"; \ for (int i = 0; i < argc; i++) \ if (strcmp(argv[i], #MODULE) == 0) { \ enable_trace(#MODULE); \ enable_debug(#MODULE); \ timeit timeit(true, s.c_str()); \ tst_##MODULE(argv, argc, i); \ std::cout << "PASS" << std::endl; \ } \ } void error(const char * msg) { std::cerr << "Error: " << msg << "\n"; std::cerr << "For usage information: test /h\n"; exit(1); } void display_usage() { std::cout << "Z3 unit tests [version 1.0]. (C) Copyright 2006 Microsoft Corp.\n"; std::cout << "Usage: test [options] [module names]\n"; std::cout << "\nMisc.:\n"; std::cout << " /h prints this message.\n"; std::cout << " /v:level be verbose, where is the verbosity level.\n"; std::cout << " /w enable warning messages.\n"; std::cout << " /a run all unit tests that don't require arguments.\n"; #if defined(Z3DEBUG) || defined(_TRACE) std::cout << "\nDebugging support:\n"; #endif #ifdef _TRACE std::cout << " /tr:tag enable trace messages tagged with .\n"; #endif #ifdef Z3DEBUG std::cout << " /dbg:tag enable assertions tagged with .\n"; #endif std::cout << "\nModule names:\n"; } void parse_cmd_line_args(int argc, char ** argv, bool& do_display_usage, bool& test_all) { int i = 1; if (argc == 1) { display_usage(); do_display_usage = true; } while (i < argc) { char * arg = argv[i]; char * eq_pos = nullptr; if (arg[0] == '-' || arg[0] == '/') { char * opt_name = arg + 1; char * opt_arg = nullptr; char * colon = strchr(arg, ':'); if (colon) { opt_arg = colon + 1; *colon = 0; } if (strcmp(opt_name, "h") == 0 || strcmp(opt_name, "?") == 0) { display_usage(); do_display_usage = true; return; } else if (strcmp(opt_name, "v") == 0) { if (!opt_arg) error("option argument (/v:level) is missing."); long lvl = strtol(opt_arg, nullptr, 10); set_verbosity_level(lvl); } else if (strcmp(opt_name, "w") == 0) { enable_warning_messages(true); } else if (strcmp(opt_name, "a") == 0) { test_all = true; } #ifdef _TRACE else if (strcmp(opt_name, "tr") == 0) { if (!opt_arg) error("option argument (/tr:tag) is missing."); enable_trace(opt_arg); } #endif #ifdef Z3DEBUG else if (strcmp(opt_name, "dbg") == 0) { if (!opt_arg) error("option argument (/dbg:tag) is missing."); enable_debug(opt_arg); } #endif } else if (arg[0] != '"' && (eq_pos = strchr(arg, '='))) { char * key = arg; *eq_pos = 0; char * value = eq_pos+1; try { gparams::set(key, value); } catch (z3_exception& ex) { std::cerr << ex.msg() << "\n"; } } i++; } } int main(int argc, char ** argv) { memory::initialize(0); bool do_display_usage = false; bool test_all = false; parse_cmd_line_args(argc, argv, do_display_usage, test_all); TST(random); TST(symbol_table); TST(region); TST(symbol); TST(heap); TST(hashtable); TST(rational); TST(inf_rational); TST(ast); TST(optional); TST(bit_vector); TST(fixed_bit_vector); TST(tbv); TST(doc); TST(udoc_relation); TST(string_buffer); TST(map); TST(diff_logic); TST(uint_set); TST_ARGV(expr_rand); TST(list); TST(small_object_allocator); TST(timeout); TST(proof_checker); TST(simplifier); TST(bit_blaster); TST(var_subst); TST(simple_parser); TST(api); TST(cube_clause); TST(old_interval); TST(get_implied_equalities); TST(arith_simplifier_plugin); TST(matcher); TST(object_allocator); TST(mpz); TST(mpq); TST(mpf); TST(total_order); TST(dl_table); TST(dl_context); TST(dl_util); TST(dl_product_relation); TST(dl_relation); TST(parray); TST(stack); TST(escaped); TST(buffer); TST(chashtable); TST(ex); TST(nlarith_util); TST(api_bug); TST(arith_rewriter); TST(check_assumptions); TST(smt_context); TST(theory_dl); TST(model_retrieval); TST(model_based_opt); TST(factor_rewriter); TST(smt2print_parse); TST(substitution); TST(polynomial); TST(upolynomial); TST(algebraic); TST(prime_generator); TST(permutation); TST(nlsat); if (test_all) return 0; TST(ext_numeral); TST(interval); TST(vector); TST(f2n); TST(hwf); TST(trigo); TST(bits); TST(mpbq); TST(mpfx); TST(mpff); TST(horn_subsume_model_converter); TST(model2expr); TST(hilbert_basis); TST(heap_trie); TST(karr); TST(no_overflow); // TST(memory); TST(datalog_parser); TST_ARGV(datalog_parser_file); TST(dl_query); TST(quant_solve); TST(rcf); TST(polynorm); TST(qe_arith); TST(expr_substitution); TST(sorting_network); TST(theory_pb); TST(simplex); TST(sat_user_scope); TST_ARGV(ddnf); TST(ddnf1); TST(model_evaluator); TST(get_consequences); TST(pb2bv); TST_ARGV(sat_lookahead); TST_ARGV(sat_local_search); TST_ARGV(cnf_backbones); TST(bdd); TST(solver_pool); //TST_ARGV(hs); } z3-z3-4.8.7/src/test/map.cpp000066400000000000000000000020501356505360400154530ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: tst_map.cpp Abstract: Test simple mapping. Author: Leonardo de Moura (leonardo) 2006-10-14. Revision History: --*/ #include "util/map.h" #include "util/str_hashtable.h" static void tst1() { map str2int; str2int.insert("foo", 35); ENSURE(str2int.contains("foo")); ENSURE(str2int.find_iterator("foo") != str2int.end()); ENSURE((*(str2int.find_iterator("foo"))).m_value == 35); ENSURE(str2int.size() == 1); str2int.insert("boo", 32); ENSURE(str2int.contains("foo")); ENSURE(str2int.find_iterator("foo") != str2int.end()); ENSURE((*(str2int.find_iterator("foo"))).m_value == 35); ENSURE(str2int.contains("boo")); ENSURE(str2int.find_iterator("boo") != str2int.end()); ENSURE((*(str2int.find_iterator("boo"))).m_value == 32); ENSURE(str2int.size() == 2); str2int.remove("boo"); ENSURE(str2int.size() == 1); ENSURE(!str2int.contains("boo")); ENSURE(str2int.contains("foo")); } void tst_map() { tst1(); } z3-z3-4.8.7/src/test/matcher.cpp000066400000000000000000000065641356505360400163370ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: matcher.cpp Abstract: Author: Leonardo de Moura (leonardo) 2010-04-16. Revision History: --*/ #ifdef _WINDOWS #include "ast/substitution/matcher.h" #include "ast/ast_pp.h" #include "ast/reg_decl_plugins.h" void tst_match(ast_manager & m, app * t, app * i) { substitution s(m); s.reserve(2, 10); // reserving a big number of variables to be safe. matcher match; std::cout << "Is " << mk_pp(i, m) << " an instance of " << mk_pp(t, m) << "\n"; if (match(t, i, s)) { std::cout << "yes\n"; s.display(std::cout); } else { std::cout << "no\n"; } s.reset(); if (t->get_decl() == i->get_decl()) { // trying to match the arguments of t and i std::cout << "Are the arguments of " << mk_pp(i, m) << " an instance of the arguments of " << mk_pp(t, m) << "\n"; unsigned num_args = t->get_num_args(); unsigned j; for (j = 0; j < num_args; j++) { if (!match(t->get_arg(j), i->get_arg(j), s)) break; } if (j == num_args) { std::cout << "yes\n"; s.display(std::cout); // create some dummy term to test for applying the substitution. sort_ref S( m.mk_uninterpreted_sort(symbol("S")), m); sort * domain[3] = {S, S, S}; func_decl_ref r( m.mk_func_decl(symbol("r"), 3, domain, S), m); expr_ref x1( m.mk_var(0, S), m); expr_ref x2( m.mk_var(1, S), m); expr_ref x3( m.mk_var(2, S), m); app_ref rxyzw( m.mk_app(r, x1.get(), x2.get(), x3.get()), m); expr_ref result(m); unsigned deltas[2] = {0,0}; s.apply(2, deltas, expr_offset(rxyzw, 0), result); std::cout << "applying substitution to\n" << mk_pp(rxyzw,m) << "\nresult:\n" << mk_pp(result,m) << "\n"; } else { std::cout << "no\n"; } } std::cout << "\n"; } void tst1() { ast_manager m; reg_decl_plugins(m); sort_ref s( m.mk_uninterpreted_sort(symbol("S")), m); func_decl_ref g( m.mk_func_decl(symbol("g"), s, s), m); func_decl_ref h( m.mk_func_decl(symbol("h"), s, s), m); sort * domain[2] = {s, s}; func_decl_ref f( m.mk_func_decl(symbol("f"), 2, domain, s), m); app_ref a( m.mk_const(symbol("a"), s), m); 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.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); app_ref fgha_ha( m.mk_app(f, gha.get(), ha.get()), m); tst_match(m, fgx_x, fgha_ha); app_ref fgha_gha( m.mk_app(f, gha.get(), gha.get()), m); tst_match(m, fgx_x, fgha_gha); app_ref fxy( m.mk_app(f, x.get(), y.get()), m); app_ref fyx( m.mk_app(f, y.get(), x.get()), m); tst_match(m, fxy, fyx); app_ref fygx( m.mk_app(f, y.get(), gx.get()), m); tst_match(m, fxy, fygx); tst_match(m, fygx, fxy); } void tst_matcher() { tst1(); } #else void tst_matcher() { } #endif z3-z3-4.8.7/src/test/memory.cpp000066400000000000000000000022211356505360400162060ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #ifdef _WINDOWS #include "api/z3.h" #include "api/z3_private.h" #include #include "util/util.h" #include "util/trace.h" static bool oom = false; static void err_handler(Z3_context c, Z3_error_code e) { oom = true; throw std::bad_alloc(); } static void hit_me(char const* wm) { Z3_config cfg; Z3_context ctx; oom = false; cfg = Z3_mk_config(); if (!cfg) { return; } Z3_global_param_set("MEMORY_MAX_SIZE", wm); ctx = Z3_mk_context(cfg); if (ctx) { Z3_set_error_handler(ctx, &err_handler); unsigned i; for (i = 1; !oom ; ++i) { try { Z3_mk_bv_sort(ctx,i); } catch (std::bad_alloc) { std::cout << "caught\n"; } } std::cout << "oom " << i << "\n"; Z3_del_context(ctx); } Z3_del_config(cfg); } void tst_memory() { hit_me("10"); Z3_reset_memory(); hit_me("20"); Z3_reset_memory(); hit_me("30"); Z3_reset_memory(); } #else void tst_memory() { } #endif z3-z3-4.8.7/src/test/model2expr.cpp000066400000000000000000000027701356505360400167700ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "model/model2expr.h" #include "ast/ast_pp.h" #include "ast/arith_decl_plugin.h" #include "model/model_smt2_pp.h" #include "ast/reg_decl_plugins.h" void tst_model2expr() { ast_manager m; reg_decl_plugins(m); arith_util a(m); ptr_vector ints; ints.push_back(a.mk_int()); ints.push_back(a.mk_int()); ints.push_back(a.mk_int()); func_decl_ref p(m), q(m), x(m); p = m.mk_func_decl(symbol("p"), 2, ints.c_ptr(), a.mk_int()); q = m.mk_func_decl(symbol("q"), 2, ints.c_ptr(), a.mk_int()); x = m.mk_const_decl(symbol("x"), a.mk_int()); expr_ref n0(m), n1(m), n2(m); n0 = a.mk_numeral(rational(0), true); n1 = a.mk_numeral(rational(1), true); n2 = a.mk_numeral(rational(2), true); model_ref md = alloc(model, m); func_interp* fip = alloc(func_interp, m, 2); func_interp* fiq = alloc(func_interp, m, 2); expr_ref_vector args(m); args.push_back(n1); args.push_back(n2); fip->insert_entry(args.c_ptr(), n1); fiq->insert_entry(args.c_ptr(), n1); args[0] = n0; args[1] = n1; fip->insert_entry(args.c_ptr(), n2); fiq->insert_entry(args.c_ptr(), n2); fip->set_else(n0); md->register_decl(x, a.mk_numeral(rational(0), true)); md->register_decl(p, fip); // full md->register_decl(q, fiq); // partial expr_ref result(m); model2expr(md, result); model_smt2_pp(std::cout, m, *md, 0); std::cout << mk_pp(result, m) << "\n"; } z3-z3-4.8.7/src/test/model_based_opt.cpp000066400000000000000000000301701356505360400200220ustar00rootroot00000000000000#include "math/simplex/model_based_opt.h" #include "util/util.h" #include "util/uint_set.h" typedef opt::model_based_opt::var var; static void add_ineq(opt::model_based_opt& mbo, unsigned x, int a, int k, opt::ineq_type rel) { vector vars; vars.push_back(var(x, rational(a))); mbo.add_constraint(vars, rational(k), rel); } static void add_ineq(opt::model_based_opt& mbo, unsigned x, int a, unsigned y, int b, int k, opt::ineq_type rel) { vector vars; vars.push_back(var(x, rational(a))); vars.push_back(var(y, rational(b))); mbo.add_constraint(vars, rational(k), rel); } #if 0 static void add_ineq(opt::model_based_opt& mbo, unsigned x, int a, unsigned y, int b, unsigned z, int c, int k, opt::ineq_type rel) { vector vars; vars.push_back(var(x, rational(a))); vars.push_back(var(y, rational(b))); vars.push_back(var(z, rational(c))); mbo.add_constraint(vars, rational(k), rel); } #endif static void add_random_ineq(opt::model_based_opt& mbo, random_gen& r, svector const& values, unsigned max_vars, unsigned max_coeff) { unsigned num_vars = values.size(); uint_set used_vars; vector vars; int value = 0; for (unsigned i = 0; i < max_vars; ++i) { unsigned x = r(num_vars); if (used_vars.contains(x)) { continue; } used_vars.insert(x); int coeff = r(max_coeff + 1); if (coeff == 0) { continue; } unsigned sign = r(2); coeff = sign == 0 ? coeff : -coeff; vars.push_back(var(x, rational(coeff))); value += coeff*values[x]; } unsigned abs_value = value < 0 ? - value : value; // value + k <= 0 // k <= - value // range for k is 2*|value| // k <= - value - range opt::ineq_type rel = opt::t_le; int coeff = 0; if (r(4) == 0) { rel = opt::t_eq; coeff = -value; } else { if (abs_value > 0) { coeff = -value - r(2*abs_value); } else { coeff = 0; } if (coeff != -value && r(3) == 0) { rel = opt::t_lt; } } mbo.add_constraint(vars, rational(coeff), rel); } static void check_random_ineqs(random_gen& r, unsigned num_vars, unsigned max_value, unsigned num_ineqs, unsigned max_vars, unsigned max_coeff) { opt::model_based_opt mbo; svector values; for (unsigned i = 0; i < num_vars; ++i) { values.push_back(r(max_value + 1)); mbo.add_var(rational(values.back())); } for (unsigned i = 0; i < num_ineqs; ++i) { add_random_ineq(mbo, r, values, max_vars, max_coeff); } vector vars; vars.reset(); vars.push_back(var(0, rational(2))); vars.push_back(var(1, rational(-2))); mbo.set_objective(vars, rational(0)); mbo.display(std::cout); opt::inf_eps value = mbo.maximize(); std::cout << "optimal: " << value << "\n"; mbo.display(std::cout); for (unsigned i = 0; i < values.size(); ++i) { std::cout << i << ": " << values[i] << " -> " << mbo.get_value(i) << "\n"; } } static void check_random_ineqs() { random_gen r(1); for (unsigned i = 0; i < 1009; ++i) { check_random_ineqs(r, 4, 5, 5, 3, 6); } } // test with upper bounds static void test1() { opt::model_based_opt mbo; vector vars; unsigned x = mbo.add_var(rational(2)); unsigned y = mbo.add_var(rational(3)); unsigned z = mbo.add_var(rational(4)); unsigned u = mbo.add_var(rational(5)); add_ineq(mbo, x, 1, y, -1, 0, opt::t_le); add_ineq(mbo, x, 1, z, -1, 0, opt::t_le); add_ineq(mbo, y, 1, u, -1, 0, opt::t_le); add_ineq(mbo, z, 1, u, -1, 1, opt::t_le); add_ineq(mbo, u, 1, -6, opt::t_le); vars.reset(); vars.push_back(var(x, rational(2))); mbo.set_objective(vars, rational(0)); opt::inf_eps value = mbo.maximize(); std::cout << value << "\n"; std::cout << "x: " << mbo.get_value(x) << "\n"; std::cout << "y: " << mbo.get_value(y) << "\n"; std::cout << "z: " << mbo.get_value(z) << "\n"; std::cout << "u: " << mbo.get_value(u) << "\n"; } // test with lower bounds static void test2() { opt::model_based_opt mbo; vector vars; unsigned x = mbo.add_var(rational(5)); unsigned y = mbo.add_var(rational(4)); unsigned z = mbo.add_var(rational(3)); unsigned u = mbo.add_var(rational(2)); add_ineq(mbo, x, -1, y, 1, 0, opt::t_le); add_ineq(mbo, x, -1, z, 1, 0, opt::t_le); add_ineq(mbo, y, -1, u, 1, 0, opt::t_le); add_ineq(mbo, z, -1, u, 1, 1, opt::t_le); add_ineq(mbo, u, -1, -6, opt::t_le); vars.reset(); vars.push_back(var(x, rational(-2))); mbo.set_objective(vars, rational(0)); opt::inf_eps value = mbo.maximize(); std::cout << value << "\n"; } // test unbounded static void test3() { opt::model_based_opt mbo; vector vars; unsigned x = mbo.add_var(rational(2)); unsigned y = mbo.add_var(rational(3)); unsigned z = mbo.add_var(rational(4)); unsigned u = mbo.add_var(rational(5)); add_ineq(mbo, x, 1, y, -1, 0, opt::t_le); add_ineq(mbo, x, 1, z, -1, 0, opt::t_le); add_ineq(mbo, y, 1, u, -1, 0, opt::t_le); add_ineq(mbo, z, 1, u, -1, 1, opt::t_le); vars.reset(); vars.push_back(var(x, rational(2))); mbo.set_objective(vars, rational(0)); opt::inf_eps value = mbo.maximize(); std::cout << value << "\n"; } // test strict static void test4() { opt::model_based_opt mbo; vector vars; unsigned x = mbo.add_var(rational(2)); unsigned y = mbo.add_var(rational(3)); unsigned z = mbo.add_var(rational(4)); unsigned u = mbo.add_var(rational(5)); add_ineq(mbo, x, 1, y, -1, 0, opt::t_lt); add_ineq(mbo, x, 1, z, -1, 0, opt::t_lt); add_ineq(mbo, y, 1, u, -1, 0, opt::t_le); add_ineq(mbo, z, 1, u, -1, 1, opt::t_le); add_ineq(mbo, u, 1, -6, opt::t_le); vars.reset(); vars.push_back(var(x, rational(2))); mbo.set_objective(vars, rational(0)); opt::inf_eps value = mbo.maximize(); std::cout << value << "\n"; std::cout << "x: " << mbo.get_value(x) << "\n"; std::cout << "y: " << mbo.get_value(y) << "\n"; std::cout << "z: " << mbo.get_value(z) << "\n"; std::cout << "u: " << mbo.get_value(u) << "\n"; } static void display_project(vector const& defs) { for (auto const& d : defs) { std::cout << d << "\n"; } } static void test5() { opt::model_based_opt mbo; unsigned x = mbo.add_var(rational(2)); unsigned y = mbo.add_var(rational(3)); unsigned z = mbo.add_var(rational(4)); unsigned u = mbo.add_var(rational(5)); add_ineq(mbo, x, 1, y, -1, 0, opt::t_le); add_ineq(mbo, x, 1, z, -1, 0, opt::t_le); add_ineq(mbo, y, 1, u, -1, 0, opt::t_le); add_ineq(mbo, z, 1, u, -1, 1, opt::t_le); unsigned vars[2] = { y, z }; display_project(mbo.project(1, vars, true)); mbo.display(std::cout); display_project(mbo.project(1, vars, true)); mbo.display(std::cout); display_project(mbo.project(1, vars+1, true)); mbo.display(std::cout); vector rows; mbo.get_live_rows(rows); } static void test6() { opt::model_based_opt mbo; unsigned x0 = mbo.add_var(rational(1)); unsigned x = mbo.add_var(rational(2)); unsigned y = mbo.add_var(rational(3)); unsigned z = mbo.add_var(rational(4)); unsigned u = mbo.add_var(rational(5)); unsigned v = mbo.add_var(rational(6)); unsigned w = mbo.add_var(rational(6)); add_ineq(mbo, x0, 1, y, -1, 0, opt::t_le); add_ineq(mbo, x, 1, y, -1, 0, opt::t_le); add_ineq(mbo, y, 1, u, -1, 0, opt::t_le); add_ineq(mbo, y, 1, z, -1, 1, opt::t_le); add_ineq(mbo, y, 1, v, -1, 1, opt::t_le); add_ineq(mbo, y, 1, w, -1, 1, opt::t_le); mbo.display(std::cout); display_project(mbo.project(1, &y, true)); mbo.display(std::cout); } static void test7() { opt::model_based_opt mbo; unsigned x0 = mbo.add_var(rational(2)); unsigned x = mbo.add_var(rational(1)); unsigned y = mbo.add_var(rational(3)); unsigned z = mbo.add_var(rational(4)); unsigned u = mbo.add_var(rational(5)); unsigned v = mbo.add_var(rational(6)); unsigned w = mbo.add_var(rational(6)); add_ineq(mbo, x0, 1, y, -1, 0, opt::t_le); add_ineq(mbo, x, 1, y, -1, 0, opt::t_lt); add_ineq(mbo, y, 1, u, -1, 0, opt::t_le); add_ineq(mbo, y, 1, z, -1, 1, opt::t_le); add_ineq(mbo, y, 1, v, -1, 1, opt::t_le); add_ineq(mbo, y, 1, w, -1, 1, opt::t_lt); mbo.display(std::cout); display_project(mbo.project(1, &y, true)); mbo.display(std::cout); } static void test8() { opt::model_based_opt mbo; unsigned x0 = mbo.add_var(rational(2)); unsigned x = mbo.add_var(rational(1)); unsigned y = mbo.add_var(rational(3)); unsigned z = mbo.add_var(rational(4)); unsigned u = mbo.add_var(rational(5)); unsigned v = mbo.add_var(rational(6)); // unsigned w = mbo.add_var(rational(6)); add_ineq(mbo, x0, 1, y, -1, 0, opt::t_le); add_ineq(mbo, x, 1, y, -1, 0, opt::t_lt); add_ineq(mbo, y, 1, u, -1, 0, opt::t_le); add_ineq(mbo, y, 1, z, -1, 1, opt::t_le); add_ineq(mbo, y, 1, v, -1, 1, opt::t_le); mbo.display(std::cout); display_project(mbo.project(1, &y, true)); mbo.display(std::cout); } static void test9() { opt::model_based_opt mbo; unsigned x0 = mbo.add_var(rational(2), true); unsigned x = mbo.add_var(rational(1), true); unsigned y = mbo.add_var(rational(3), true); unsigned z = mbo.add_var(rational(4), true); unsigned u = mbo.add_var(rational(5), true); unsigned v = mbo.add_var(rational(6), true); add_ineq(mbo, x0, 1, y, -1, 0, opt::t_le); add_ineq(mbo, x, 1, y, -1, 0, opt::t_lt); add_ineq(mbo, y, 1, u, -1, 0, opt::t_le); add_ineq(mbo, y, 1, z, -1, 1, opt::t_le); add_ineq(mbo, y, 1, v, -1, 1, opt::t_le); mbo.display(std::cout); display_project(mbo.project(1, &y, true)); mbo.display(std::cout); } static void test10() { opt::model_based_opt mbo; unsigned x0 = mbo.add_var(rational(2), true); unsigned x = mbo.add_var(rational(1), true); unsigned y = mbo.add_var(rational(3), true); unsigned z = mbo.add_var(rational(4), true); unsigned u = mbo.add_var(rational(5), true); unsigned v = mbo.add_var(rational(6), true); add_ineq(mbo, x0, 1, y, -2, 0, opt::t_le); add_ineq(mbo, x, 1, y, -2, 0, opt::t_lt); add_ineq(mbo, y, 3, u, -4, 0, opt::t_le); add_ineq(mbo, y, 3, z, -5, 1, opt::t_le); add_ineq(mbo, y, 3, v, -6, 1, opt::t_le); mbo.display(std::cout); display_project(mbo.project(1, &y, true)); mbo.display(std::cout); display_project(mbo.project(1, &x0, true)); mbo.display(std::cout); } static void test11() { { opt::model_based_opt mbo; unsigned s = mbo.add_var(rational(2), true); unsigned a = mbo.add_var(rational(1), true); unsigned t = mbo.add_var(rational(3), true); add_ineq(mbo, s, 1, a, -2, 0, opt::t_le); // s - 2a <= 0 add_ineq(mbo, a, 2, t, -1, 0, opt::t_le); // 2a - t <= 0 mbo.display(std::cout); display_project(mbo.project(1, &a, true)); mbo.display(std::cout); } { opt::model_based_opt mbo; unsigned s = mbo.add_var(rational(3), true); unsigned a = mbo.add_var(rational(2), true); unsigned t = mbo.add_var(rational(4), true); add_ineq(mbo, s, 1, a, -2, 0, opt::t_le); // s - 2a <= 0 add_ineq(mbo, a, 2, t, -1, 0, opt::t_le); // 2a - t <= 0 mbo.display(std::cout); display_project(mbo.project(1, &a, true)); mbo.display(std::cout); } } // test with mix of upper and lower bounds void tst_model_based_opt() { test10(); check_random_ineqs(); test1(); test2(); test3(); test4(); test5(); test6(); test7(); test8(); test9(); test11(); } z3-z3-4.8.7/src/test/model_evaluator.cpp000066400000000000000000000043621356505360400200700ustar00rootroot00000000000000#include "model/model.h" #include "model/model_evaluator.h" #include "model/model_pp.h" #include "ast/arith_decl_plugin.h" #include "ast/reg_decl_plugins.h" #include "ast/ast_pp.h" void tst_model_evaluator() { ast_manager m; reg_decl_plugins(m); arith_util a(m); model mdl(m); sort* sI = a.mk_int(); sort* dom[2] = { sI, m.mk_bool_sort() }; func_decl_ref f(m.mk_func_decl(symbol("f"), 2, dom, sI), m); func_decl_ref g(m.mk_func_decl(symbol("g"), 2, dom, sI), m); func_decl_ref h(m.mk_func_decl(symbol("h"), 2, dom, sI), m); func_decl_ref F(m.mk_func_decl(symbol("F"), 2, dom, sI), m); func_decl_ref G(m.mk_func_decl(symbol("G"), 2, dom, sI), m); expr_ref vI0(m.mk_var(0, sI), m); expr_ref vI1(m.mk_var(1, sI), m); expr_ref vB0(m.mk_var(0, m.mk_bool_sort()), m); expr_ref vB1(m.mk_var(1, m.mk_bool_sort()), m); expr_ref vB2(m.mk_var(2, m.mk_bool_sort()), m); expr* vI0p = vI0.get(); expr* vI1p = vI1.get(); expr* vB1p = vB1.get(); expr* vB2p = vB2.get(); expr_ref f01(m.mk_app(f, vI0p, vB1p), m); expr_ref g01(m.mk_app(g, vI0p, vB1p), m); expr_ref h01(m.mk_app(h, vI0p, vB1p), m); func_interp* fi = alloc(func_interp, m, 2); func_interp* gi = alloc(func_interp, m, 2); func_interp* hi = alloc(func_interp, m, 2); hi->set_else(m.mk_ite(vB1p, m.mk_app(f, vI0p, vB1p), m.mk_app(g, vI0p, vB1p))); mdl.register_decl(h, hi); model_evaluator eval(mdl); expr_ref e(m), v(m); { symbol nI("N"); fi->set_else(m.mk_ite(m.mk_exists(1, &sI, &nI, a.mk_le(vI0p, m.mk_app(F, vI1p, vB2p))), vI0p, a.mk_int(1))); gi->set_else(m.mk_ite(m.mk_exists(1, &sI, &nI, a.mk_le(vI0p, m.mk_app(G, vI1p, vB2p))), a.mk_int(2), vI0p)); mdl.register_decl(g, gi); mdl.register_decl(f, fi); model_pp(std::cout, mdl); e = m.mk_app(h, vI0p, vB1p); eval(e, v); std::cout << e << " " << v << "\n"; } { fi->set_else(m.mk_app(F, vI0p, vB1p)); gi->set_else(m.mk_app(G, vI0p, vB1p)); mdl.register_decl(g, gi); mdl.register_decl(h, hi); model_pp(std::cout, mdl); e = m.mk_app(h, vI0p, vB1p); eval(e, v); std::cout << e << " " << v << "\n"; } } z3-z3-4.8.7/src/test/model_retrieval.cpp000066400000000000000000000037531356505360400200660ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "ast/ast.h" #include "smt/params/smt_params.h" #include "smt/smt_context.h" #include "ast/arith_decl_plugin.h" #include "ast/bv_decl_plugin.h" #include "ast/array_decl_plugin.h" #include "model/model_v2_pp.h" #include "ast/reg_decl_plugins.h" void tst_model_retrieval() { memory::initialize(0); smt_params params; params.m_model = true; ast_manager m; reg_decl_plugins(m); family_id array_fid = m.mk_family_id(symbol("array")); array_util au(m); // arr_s and select_fn creation copy-pasted from z3.cpp parameter sparams[2] = { parameter(to_sort(m.mk_bool_sort())), parameter(to_sort(m.mk_bool_sort())) }; sort_ref arr_s(m.mk_sort(array_fid, ARRAY_SORT, 2, sparams), m); sort * domain2[2] = {arr_s, m.mk_bool_sort()}; func_decl_ref select_fn( m.mk_func_decl(array_fid, OP_SELECT, 2, arr_s->get_parameters(), 2, domain2), m); app_ref a1(m.mk_const(symbol("a1"), arr_s), m); app_ref a2(m.mk_const(symbol("a2"), arr_s), m); // (= true (select a1 true)) app_ref fml(m.mk_eq(m.mk_true(), m.mk_app(select_fn.get(), a1, m.mk_true())), m); smt::context ctx(m, params); ctx.assert_expr(fml); lbool check_result = ctx.check(); std::cout<<((check_result==l_true) ? "satisfiable" : (check_result==l_false) ? "unsatisfiable" : "unknown")<<"\n"; ref model; ctx.get_model(model); model_v2_pp(std::cout, *model, false); expr_ref a1_val(model->get_const_interp(a1->get_decl()), m); app_ref fml2(m.mk_eq(a2, a1_val), m); ctx.assert_expr(fml2); std::cout<<"--------------------------\n"; ctx.display(std::cout); std::cout<<"--------------------------\n"; check_result = ctx.check(); ctx.display(std::cout); std::cout<<"--------------------------\n"; std::cout<<((check_result==l_true) ? "satisfiable" : (check_result==l_false) ? "unsatisfiable" : "unknown")<<"\n"; } z3-z3-4.8.7/src/test/mpbq.cpp000066400000000000000000000017371356505360400156500ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: mpbq.cpp Abstract: mpbq tests... Author: Leonardo de Moura (leonardo) 2012-09-20 Revision History: --*/ #include "util/mpbq.h" static void tst1() { unsynch_mpz_manager zm; mpbq_manager m(zm); scoped_mpbq a(m), b(m); m.set(a, INT_MAX); a = a + 1; a = a * a; a = a*3 - 1; a = a * a - 5; a = a * a - 7; m.div2k(a, 67); std::cout << a << "\n"; b = a; m.approx(b, 32, true); std::cout << b << "\n"; b = a; m.approx(b, 32, false); std::cout << b << "\n"; b = a; m.neg(b); m.approx(b, 32, true); std::cout << b << "\n"; b = a; m.neg(b); m.approx(b, 32, false); std::cout << b << "\n"; } static void tst2() { unsynch_mpz_manager zm; mpbq_manager m(zm); scoped_mpbq a(m), b(m); m.set(a, 5); m.set(b, 3); m.approx_div(a, b, a, 128); std::cout << a << "\n"; } void tst_mpbq() { tst1(); tst2(); } z3-z3-4.8.7/src/test/mpf.cpp000066400000000000000000000034171356505360400154700ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: mpf.cpp Abstract: mpf repros... Author: Leonardo de Moura (leonardo) 2012-08-21. Revision History: --*/ #include "util/mpf.h" #include "util/f2n.h" static void bug_set_int() { mpf_manager fm; scoped_mpf a(fm); fm.set(a, 11, 53, 3); ENSURE(fm.to_double(a) == 3.0); fm.set(a, 11, 53, 0); ENSURE(fm.to_double(a) == 0.0); fm.set(a, 11, 53, -1); ENSURE(fm.to_double(a) == -1.0); fm.set(a, 11, 53, INT_MAX); ENSURE(fm.to_double(a) == (double)INT_MAX); fm.set(a, 11, 53, INT_MIN); ENSURE(fm.to_double(a) == (double)INT_MIN); fm.set(a, 8, 24, 3); ENSURE(fm.to_float(a) == 3.0); ENSURE(fm.to_double(a) == 3.0); fm.set(a, 8, 24, 0); ENSURE(fm.to_float(a) == 0.0); ENSURE(fm.to_double(a) == 0.0); fm.set(a, 8, 24, -1); ENSURE(fm.to_float(a) == -1.0); ENSURE(fm.to_double(a) == -1.0); fm.set(a, 8, 24, INT_MIN); ENSURE(fm.to_float(a) == (float)INT_MIN); // CMW: This one depends on the rounding mode, but fm.set(..., int) doesn't have one. // fm.set(a, 8, 24, INT_MAX); // ENSURE(fm.to_float(a) == (float)INT_MAX); } static void bug_set_double() { mpf_manager fm; scoped_mpf a(fm); fm.set(a, 11, 53, 2.5); ENSURE(fm.to_double(a) == 2.5); fm.set(a, 11, 53, -42.25); ENSURE(fm.to_double(a) == -42.25); fm.set(a, 8, 24, (double)2.5); ENSURE(fm.to_double(a) == 2.5); fm.set(a, 8, 24, (double)-42.25); ENSURE(fm.to_double(a) == -42.25); fm.set(a, 8, 24, (float)2.5); ENSURE(fm.to_float(a) == 2.5); fm.set(a, 8, 24, (float)-42.25); ENSURE(fm.to_float(a) == -42.25); } void tst_mpf() { enable_trace("mpf_mul_bug"); bug_set_int(); bug_set_double(); } z3-z3-4.8.7/src/test/mpff.cpp000066400000000000000000000456321356505360400156430ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: mpff.cpp Abstract: mpff tests... Author: Leonardo de Moura (leonardo) 2012-09-12. Revision History: --*/ #include #include "util/mpff.h" #include "util/mpz.h" #include "util/mpq.h" static void tst1() { try { mpff_manager m; // m.round_to_minus_inf(); scoped_mpff a(m), b(m); m.set(a, 100); m.set(b, -33); std::cout << "a: " << a << ", b: " << b << "\n"; std::cout << "a*b: " << a*b << "\n"; for (unsigned i = 0; i < 100; i++) { a = a*a; std::cout << i << ": " << a << "\n"; } } catch (const z3_exception & ex) { std::cout << ex.msg() << "\n"; } } static void tst2() { mpff_manager m; scoped_mpff a(m), b(m); m.set(a, static_cast(100)); m.set(b, static_cast(-100)); std::cout << "[test2], a: " << a << ", b: " << b << "\n"; } static void tst3() { mpff_manager m; scoped_mpff a(m), b(m), c(m); m.set(a, 1); m.set(b, 3); m.div(a, b, c); std::cout << "[div] c: " << c << "\n"; m.round_to_plus_inf(); m.reset(c); m.div(a, b, c); std::cout << "[div] c: " << c << "\n"; } static void tst4() { unsynch_mpz_manager zm; mpff_manager m; scoped_mpz a(zm); scoped_mpff b(m); zm.set(a, 2); zm.power(a, 512, a); m.set(b, zm, a); std::cout << "[mpz->mpff] a: " << a << ", b: " << b << "\n"; } static void tst5() { mpff_manager m; scoped_mpff a(m), b(m); m.set(a, static_cast(1) << 63); m.display_raw(std::cout, a); std::cout << "\n"; ENSURE(m.is_zero(b)); ENSURE(m.lt(b, a)); m.set(b, -1); ENSURE(m.lt(b, a)); } static void tst6() { mpff_manager m; scoped_mpff a(m), b(m), one(m); m.set(a, 1, 3); std::cout << "mpff(1/3) " << a << "\n"; b = a; m.next(b); ENSURE(m.lt(a, b)); std::cout << "b: " << b << "\n"; m.prev(b); ENSURE(m.eq(a, b)); m.ceil(b); std::cout << "b: " << b << "\n"; m.set(b, 4, 3); std::cout << "b: " << b << "\n"; m.ceil(b); std::cout << "b: " << b << "\n"; } static void tst7() { mpff_manager m; scoped_mpff a(m); m.set(a, 2); m.display_smt2(std::cout, a); std::cout << "\n"; m.set(a, -2); m.display_smt2(std::cout, a); std::cout << "\n"; m.set(a, 1, 3); m.display_smt2(std::cout, a); std::cout << "\n"; } // if (!qm.le(qa, qt)) { TRACE("mpff_bug", tout << fa << "\n" << qa << "\n" << qt << "\n";); UNREACHABLE(); } #define MK_BIN_OP(OP) \ static void tst_ ## OP ## _core(int64_t n1, uint64_t d1, int64_t n2, uint64_t d2, unsigned precision = 2, unsigned exp = 0) { \ TRACE("mpff_bug", tout << n1 << "/" << d1 << ", " << n2 << "/" << d2 << "\n";); \ unsynch_mpq_manager qm; \ scoped_mpq qa(qm), qb(qm), qc(qm), qt(qm); \ \ mpff_manager fm(precision); \ scoped_mpff fa(fm), fb(fm), fc1(fm), fc2(fm); \ fm.set(fa, n1, d1); \ if (exp != 0) { int _exp = rand() % exp; if (rand() % 2 == 0) _exp = -_exp; fm.set_exponent(fa, _exp); } \ fm.to_mpq(fa, qm, qa); \ fm.set(fb, n2, d2); \ if (exp != 0) { int _exp = rand() % exp; if (rand() % 2 == 0) _exp = -_exp; fm.set_exponent(fb, _exp); } \ fm.to_mpq(fb, qm, qb); \ qm.OP(qa, qb, qc); \ { \ fm.round_to_plus_inf(); \ fm.OP(fa, fb, fc1); \ fm.to_mpq(fc1, qm, qt); \ ENSURE(qm.le(qc, qt)); \ } \ { \ fm.round_to_minus_inf(); \ fm.OP(fa, fb, fc2); \ fm.to_mpq(fc2, qm, qt); \ ENSURE(qm.le(qt, qc)); \ } \ ENSURE(fm.le(fc2, fc1)); \ } MK_BIN_OP(add); MK_BIN_OP(sub); MK_BIN_OP(mul); MK_BIN_OP(div); #define MK_BIN_RANDOM_TST(OP) \ static void tst_ ## OP(unsigned N, unsigned max, unsigned prec = 2, bool is_div = false) { \ for (unsigned i = 0; i < N; i++) { \ int n1 = rand() % max; \ int d1 = rand() % max + 1; \ int n2 = rand() % max; \ int d2 = rand() % max + 1; \ if (rand () % 2 == 0) \ n1 = -n1; \ if (rand () % 2 == 0) \ n2 = -n2; \ if (is_div && n2 == 0) n2 = 1; \ tst_ ## OP ## _core(n1, d1, n2, d2, prec); \ tst_ ## OP ## _core(n1, d1, n2, d2, prec, 512); \ } \ } MK_BIN_RANDOM_TST(add) MK_BIN_RANDOM_TST(sub) MK_BIN_RANDOM_TST(mul) MK_BIN_RANDOM_TST(div) static void tst_bug() { unsynch_mpq_manager qm; mpff_manager fm; fm.round_to_plus_inf(); scoped_mpff a(fm); fm.set(a, 41, 36); scoped_mpq b(qm), c(qm); qm.set(b, 41, 36); fm.to_mpq(a, qm, c); ENSURE(qm.le(b, c)); } static void tst_bug2() { mpff_manager fm(4); scoped_mpff a(fm), b(fm); fm.set(b, 1); fm.sub(a, b, b); fm.set(a, -1); ENSURE(fm.eq(a, b)); fm.set(a, 1); fm.set(b, 0); fm.sub(a, b, a); fm.set(b, 1); ENSURE(fm.eq(a, b)); fm.set(a, 1); fm.set(b, 1); fm.sub(a, b, a); ENSURE(fm.is_zero(a)); } static void tst_set64(unsigned N, unsigned prec) { mpff_manager fm(prec); scoped_mpff a(fm); fm.set(a, static_cast(INT64_MAX)); ENSURE(fm.is_int64(a)); ENSURE(fm.is_uint64(a)); fm.inc(a); ENSURE(!fm.is_int64(a)); ENSURE(fm.is_uint64(a)); ENSURE(fm.is_int(a)); fm.dec(a); ENSURE(fm.is_int64(a)); ENSURE(fm.is_uint64(a)); fm.dec(a); ENSURE(fm.is_int64(a)); ENSURE(fm.is_uint64(a)); fm.set(a, static_cast(INT64_MIN)); ENSURE(fm.is_int64(a)); ENSURE(!fm.is_uint64(a)); fm.dec(a); ENSURE(!fm.is_int64(a)); ENSURE(!fm.is_uint64(a)); ENSURE(fm.is_int(a)); fm.inc(a); ENSURE(fm.is_int64(a)); ENSURE(!fm.is_uint64(a)); fm.inc(a); ENSURE(fm.is_int64(a)); ENSURE(!fm.is_uint64(a)); fm.set(a, static_cast(UINT64_MAX)); ENSURE(fm.is_uint64(a)); ENSURE(!fm.is_int64(a)); fm.inc(a); ENSURE(!fm.is_uint64(a)); ENSURE(!fm.is_int64(a)); fm.dec(a); ENSURE(fm.is_uint64(a)); ENSURE(!fm.is_int64(a)); fm.dec(a); ENSURE(fm.is_uint64(a)); ENSURE(!fm.is_int64(a)); for (unsigned i = 0; i < N; i++) { { uint64_t v = (static_cast(rand()) << 32) + static_cast(rand()); fm.set(a, v); ENSURE(fm.is_uint64(a)); v = (static_cast(rand() % 3) << 32) + static_cast(rand()); fm.set(a, v); ENSURE(fm.is_uint64(a)); } { int64_t v = (static_cast(rand() % INT_MAX) << 32) + static_cast(rand()); if (rand()%2 == 0) v = -v; fm.set(a, v); ENSURE(fm.is_int64(a)); v = (static_cast(rand() % 3) << 32) + static_cast(rand()); if (rand()%2 == 0) v = -v; fm.set(a, v); ENSURE(fm.is_int64(a)); } } } static void tst_capacity(unsigned prec = 2) { mpff_manager m(prec); scoped_mpff_vector v(m); scoped_mpff a(m); for (unsigned i = 0; i < 50000; i++) { m.set(a, i); v.push_back(a); ENSURE(m.is_int(v.back())); ENSURE(m.is_int64(v.back())); ENSURE(m.is_uint64(v.back())); } for (unsigned i = 0; i < 50000; i++) { ENSURE(m.get_int64(v[i]) == i); } } static void tst_power(unsigned prec = 2) { mpff_manager m(prec); scoped_mpff a(m), b(m); // 0^k == 0 ENSURE(m.is_zero(a)); m.power(a, 10, a); ENSURE(m.is_zero(a)); // a != 0 ==> a^0 == 1 m.set(a, 33); m.power(a, 0, a); ENSURE(m.is_one(a)); m.set(a, -33); m.power(a, 0, a); ENSURE(m.is_one(a)); // a^1 == a m.set(a, 33); m.power(a, 1, b); ENSURE(m.eq(a, b)); m.set(a, -33); m.power(a, 1, b); ENSURE(m.eq(a, b)); // checking special support for powers of 2 unsigned k; m.set(a, 1); ENSURE(m.is_power_of_two(a, k) && k == 0); m.set(a, 2); ENSURE(m.is_power_of_two(a, k) && k == 1); m.set(a, 3); ENSURE(!m.is_power_of_two(a, k)); m.set(a, 4); ENSURE(m.is_power_of_two(a, k) && k == 2); m.set(a, -4); ENSURE(!m.is_power_of_two(a, k)); m.set(a, 8); ENSURE(m.is_power_of_two(a, k) && k == 3); m.set(a, 0); ENSURE(!m.is_power_of_two(a)); m.set(a, UINT_MAX); m.inc(a); ENSURE(m.is_power_of_two(a, k) && k == 32); ENSURE(m.get_uint64(a) == static_cast(UINT_MAX) + 1); m.power(a, 2, a); ENSURE(m.is_power_of_two(a, k) && k == 64); m.power(a, 4, a); ENSURE(m.is_power_of_two(a, k) && k == 256); m.round_to_plus_inf(); m.inc(a); ENSURE(!m.is_power_of_two(a, k)); m.set(a, -4); m.power(a, 3, a); m.set(b, -64); ENSURE(m.eq(a, b)); m.set(a, -4); m.power(a, 4, a); m.set(b, 256); ENSURE(m.eq(a, b)); // additional tests m.set(a, 5); m.power(a, 3, a); m.set(b, 5*5*5); ENSURE(m.eq(a,b)); m.set(a, -5); m.power(a, 3, a); m.set(b, -5*5*5); ENSURE(m.eq(a,b)); } static void tst_sgn(unsigned prec) { mpff_manager m(prec); scoped_mpff a(m), b(m); ENSURE(m.is_zero(a) && !m.is_pos(a) && !m.is_neg(a) && m.is_nonpos(a) && m.is_nonneg(a)); m.set(a, 3); ENSURE(!m.is_zero(a) && m.is_pos(a) && !m.is_neg(a) && !m.is_nonpos(a) && m.is_nonneg(a)); m.set(a, -3); ENSURE(!m.is_zero(a) && !m.is_pos(a) && m.is_neg(a) && m.is_nonpos(a) && !m.is_nonneg(a)); m.set(a, 8); m.power(a, 256, a); ENSURE(!m.is_zero(a) && m.is_pos(a) && !m.is_neg(a) && !m.is_nonpos(a) && m.is_nonneg(a)); b = a; m.neg(a); ENSURE(m.neq(a, b)); ENSURE(!m.is_zero(a) && !m.is_pos(a) && m.is_neg(a) && m.is_nonpos(a) && !m.is_nonneg(a)); m.neg(a); ENSURE(m.eq(a, b)); m.set(a, 1); ENSURE(m.is_one(a) && !m.is_zero(a) && !m.is_minus_one(a) && m.is_abs_one(a)); m.neg(a); ENSURE(!m.is_one(a) && !m.is_zero(a) && m.is_minus_one(a) && m.is_abs_one(a)); m.set(a, 3); ENSURE(!m.is_one(a) && !m.is_zero(a) && !m.is_minus_one(a)); m.set(a, 3); b = a; m.abs(a); ENSURE(m.eq(a, b)); m.set(a, -3); b = a; m.abs(a); ENSURE(!m.eq(a,b) && m.is_pos(a)); m.set(a, 1); m.swap(a, a); ENSURE(m.is_one(a)); m.set(b, -1); m.swap(a, b); ENSURE(m.is_one(b) && m.is_minus_one(a)); m.neg(a); ENSURE(m.eq(a, b)); } static void tst_limits(unsigned prec) { mpff_manager m(prec); scoped_mpff a(m), b(m), two(m); m.set_max(a); ENSURE(m.is_pos(a)); m.set_min(b); ENSURE(m.is_neg(b)); m.neg(a); ENSURE(m.eq(a, b)); m.set_max(a); m.set_max(b); m.round_to_minus_inf(); m.inc(a); ENSURE(m.eq(a, b)); m.dec(a); ENSURE(m.lt(a, b)); m.set_max(a); m.round_to_plus_inf(); bool overflow = false; try { m.inc(a); } catch (const mpff_manager::overflow_exception &) { overflow = true; } VERIFY(overflow); m.set_max(a); m.dec(a); ENSURE(m.eq(a, b)); m.set_min(a); m.set_min(b); m.round_to_minus_inf(); m.inc(a); ENSURE(m.eq(a, b)); overflow = true; try { m.dec(a); } catch (const mpff_manager::overflow_exception &) { overflow = true; } ENSURE(overflow); m.round_to_plus_inf(); m.set_min(a); m.inc(a); ENSURE(m.gt(a,b)); m.set_min(a); m.dec(a); ENSURE(m.eq(a,b)); m.set_plus_epsilon(a); m.set_plus_epsilon(b); ENSURE(!m.is_zero(a) && m.is_pos(a)); m.set(two, 2); m.round_to_plus_inf(); m.div(a, two, a); ENSURE(m.eq(a, b)); m.round_to_minus_inf(); m.div(a, two, a); ENSURE(m.is_zero(a)); m.round_to_plus_inf(); m.set_plus_epsilon(a); m.add(a, a, a); ENSURE(m.gt(a, b)); m.round_to_minus_inf(); m.set_plus_epsilon(a); m.add(a, a, a); ENSURE(m.gt(a, b)); m.set_plus_epsilon(a); m.sub(a, a, a); ENSURE(m.is_zero(a)); m.set_plus_epsilon(a); ENSURE(m.is_plus_epsilon(a)); ENSURE(!m.is_minus_epsilon(a)); m.neg(a); ENSURE(!m.is_plus_epsilon(a)); ENSURE(m.is_minus_epsilon(a)); for (unsigned i = 0; i < 2; i++) { m.set_rounding(i == 0); m.set_plus_epsilon(a); m.floor(a); ENSURE(m.is_zero(a)); m.set_plus_epsilon(a); m.ceil(a); ENSURE(m.is_one(a)); m.set_minus_epsilon(a); m.floor(a); ENSURE(m.is_minus_one(a)); m.set_minus_epsilon(a); m.ceil(a); ENSURE(m.is_zero(a)); } m.set_minus_epsilon(a); m.set_minus_epsilon(b); ENSURE(!m.is_zero(a) && m.is_neg(a)); m.set(two, 2); m.round_to_minus_inf(); m.div(a, two, a); ENSURE(m.eq(a, b)); m.round_to_plus_inf(); m.div(a, two, a); ENSURE(m.is_zero(a)); m.round_to_plus_inf(); m.set_minus_epsilon(a); m.add(a, a, a); ENSURE(m.lt(a, b)); m.round_to_minus_inf(); m.set_minus_epsilon(a); m.add(a, a, a); ENSURE(m.lt(a, b)); m.set_minus_epsilon(a); m.sub(a, a, a); ENSURE(m.is_zero(a)); m.set_minus_epsilon(a); ENSURE(!m.is_plus_epsilon(a)); ENSURE(m.is_minus_epsilon(a)); m.neg(a); ENSURE(m.is_plus_epsilon(a)); ENSURE(!m.is_minus_epsilon(a)); } #if 0 static void tst_add_corner(unsigned prec) { mpff_manager m(prec); scoped_mpff a(m), b(m); } #endif static void tst_decimal(int64_t n, uint64_t d, bool to_plus_inf, unsigned prec, char const * expected, unsigned decimal_places = UINT_MAX) { mpff_manager m(prec); scoped_mpff a(m); m.set_rounding(to_plus_inf); m.set(a, n, d); m.display(std::cout, a); std::cout << std::endl; m.display_decimal(std::cout, a, decimal_places); std::cout << std::endl; std::ostringstream buffer; m.display_decimal(buffer, a, decimal_places); ENSURE(strcmp(expected, buffer.str().c_str()) == 0); } static void tst_decimal() { tst_decimal(1, 3, false, 2, "0.3333333333333333333152632971252415927665424533188343048095703125"); tst_decimal(1, 3, false, 4, "0.33333333333333333333333333333333333333235375470764809374335938621898146193515111203602326039874270691143465228378772735595703125"); tst_decimal(-1, 3, true, 2, "-0.3333333333333333333152632971252415927665424533188343048095703125"); tst_decimal(-1, 3, false, 2, "-0.33333333333333333334236835143737920361672877334058284759521484375"); tst_decimal(0, 1, false, 2, "0"); tst_decimal(2, 1, false, 2, "2"); tst_decimal(-3, 1, false, 2, "-3"); tst_decimal(INT64_MAX, 1, false, 2, "9223372036854775807"); tst_decimal(4, 5, false, 2, "0.79999999999999999995663191310057982263970188796520233154296875"); tst_decimal(4, 5, false, 2, "0.7999999999?", 10); tst_decimal(32, 5, true, 2, "6.4000000000000000000867361737988403547205962240695953369140625"); tst_decimal(32, 5, false, 2, "6.39999999999999999965305530480463858111761510372161865234375"); tst_decimal(-32, 5, false, 2, "-6.4000000000000000000867361737988403547205962240695953369140625"); tst_decimal(-32, 5, true, 2, "-6.39999999999999999965305530480463858111761510372161865234375"); } static void tst_prev_power_2(int64_t n, uint64_t d, unsigned expected) { mpff_manager m; scoped_mpff a(m); m.set(a, n, d); ENSURE(m.prev_power_of_two(a) == expected); } static void tst_prev_power_2() { tst_prev_power_2(-10, 1, 0); tst_prev_power_2(0, 1, 0); tst_prev_power_2(1, 1, 0); tst_prev_power_2(2, 1, 1); tst_prev_power_2(3, 1, 1); tst_prev_power_2(4, 1, 2); tst_prev_power_2(5, 1, 2); tst_prev_power_2(8, 1, 3); tst_prev_power_2(9, 1, 3); tst_prev_power_2(9, 2, 2); tst_prev_power_2(9, 4, 1); tst_prev_power_2(9, 5, 0); tst_prev_power_2((1ll << 60) + 1, 1, 60); tst_prev_power_2((1ll << 60), 1, 60); tst_prev_power_2((1ll << 60) - 1, 1, 59); tst_prev_power_2((1ll << 60), 3, 58); } static void tst_div(unsigned prec) { mpff_manager m(prec); scoped_mpff a(m), b(m), c(m); m.round_to_plus_inf(); m.set(a, 1); m.set(b, static_cast(UINT64_MAX)); m.div(a, b, c); m.display_raw(std::cout, a); std::cout << "\n"; m.display_raw(std::cout, b); std::cout << "\n"; std::cout << a << "/" << b << " <= " << c << "\n"; // 10...0 0...0 // 11111 } void tst_mpff() { disable_trace("mpff"); enable_trace("mpff_trace"); // enable_trace("mpff_bug"); // enable_trace("mpff_to_mpq"); // tst_div(2); tst_prev_power_2(); tst_decimal(); tst_div_core(679, 396, 279, 756, 2, 0); tst_limits(2); tst_limits(4); tst_sgn(2); tst_sgn(4); tst_sgn(8); tst_power(2); tst_power(4); tst_power(18); tst_capacity(2); tst_capacity(4); tst_capacity(8); tst_capacity(16); tst_set64(1000, 2); tst_set64(1000, 4); tst_set64(1000, 6); tst_bug2(); tst_sub(1000, 1024, 2); tst_sub(1000, 1024, 4); tst_div(1000, 1024, 2, true); tst_div(1000, 1024, 4, true); tst_mul(1000, 1024, 2); tst_mul(1000, 1024, 4); tst_add(1000, 1024, 2); tst_add(1000, 1024, 4); tst_sub(1000, UINT_MAX, 2); tst_sub(1000, UINT_MAX, 4); tst_div(1000, UINT_MAX, 2, true); tst_div(1000, UINT_MAX, 4, true); tst_mul(1000, UINT_MAX, 2); tst_mul(1000, UINT_MAX, 4); tst_add(1000, UINT_MAX, 2); tst_add(1000, UINT_MAX, 4); tst_bug2(); tst_bug(); tst_add_core(1,1, 1,1); tst_add_core(1,3, 2,3); tst1(); tst2(); tst3(); tst4(); tst5(); tst6(); tst7(); } z3-z3-4.8.7/src/test/mpfx.cpp000066400000000000000000000031351356505360400156550ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: mpfx.cpp Abstract: Multi precision fixed point numbers. Author: Leonardo de Moura (leonardo) 2012-09-19 Revision History: --*/ #include "util/mpfx.h" static void tst1() { mpfx_manager m; scoped_mpfx a(m), b(m), c(m); m.set(a, 1); m.set(b, 2); std::cout << a << " + " << b << " == " << (a+b) << "\n"; m.set(a, 5); m.set(c, 3); m.display_raw(std::cout, (a*a*b)/c); std::cout << "\n"; m.display_decimal(std::cout, (a*a*b)/c); std::cout << "\n"; m.display_decimal(std::cout, (a*a*b)/c, 10); std::cout << "\n"; m.round_to_plus_inf(); m.display_decimal(std::cout, (a*a*b)/c); std::cout << "\n"; m.set(a, -1, 4); m.display_decimal(std::cout, a); std::cout << "\n"; } static void tst_prev_power_2(int64_t n, uint64_t d, unsigned expected) { mpfx_manager m; scoped_mpfx a(m); m.set(a, n, d); ENSURE(m.prev_power_of_two(a) == expected); } static void tst_prev_power_2() { tst_prev_power_2(-10, 1, 0); tst_prev_power_2(0, 1, 0); tst_prev_power_2(1, 1, 0); tst_prev_power_2(2, 1, 1); tst_prev_power_2(3, 1, 1); tst_prev_power_2(4, 1, 2); tst_prev_power_2(5, 1, 2); tst_prev_power_2(8, 1, 3); tst_prev_power_2(9, 1, 3); tst_prev_power_2(9, 2, 2); tst_prev_power_2(9, 4, 1); tst_prev_power_2(9, 5, 0); tst_prev_power_2((1ll << 60) + 1, 1, 60); tst_prev_power_2((1ll << 60), 1, 60); tst_prev_power_2((1ll << 60) - 1, 1, 59); tst_prev_power_2((1ll << 60), 3, 58); } void tst_mpfx() { tst_prev_power_2(); tst1(); } z3-z3-4.8.7/src/test/mpq.cpp000066400000000000000000000066451356505360400155110ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: mpq.cpp Abstract: Author: Leonardo de Moura (leonardo) 2010-06-21. Revision History: --*/ #include "util/mpq.h" #include "util/rational.h" #include "util/timeit.h" static void tst0() { synch_mpq_manager m; mpq a, b; m.set(a, 2, 3); m.set(b, 4, 3); m.div(a, b, b); ENSURE(m.eq(b, m.mk_q(1, 2))); } static void tst1() { synch_mpq_manager m; char const * str = "1002034040050606089383838288182"; mpz v; m.set(v, str); std::cout << str << "\n" << m.to_string(v) << "\n"; mpz v2, v3; m.mul(v, m.mk_z(-2), v2); std::cout << "*-2 = \n" << m.to_string(v2) << "\n"; m.add(v, v2, v3); m.neg(v3); ENSURE(m.eq(v, v3)); ENSURE(m.le(v, v3)); ENSURE(m.ge(v, v3)); ENSURE(m.lt(v2, v)); ENSURE(m.le(v2, v)); ENSURE(m.gt(v, v2)); ENSURE(m.ge(v, v2)); ENSURE(m.neq(v, v2)); ENSURE(!m.neq(v, v3)); m.del(v); m.del(v2); m.del(v3); } #if 0 static void mk_random_num_str(unsigned buffer_sz, char * buffer) { unsigned div_pos; unsigned sz = (rand() % (buffer_sz-2)) + 1; if (rand() % 2 == 0) { // integer div_pos = sz + 1; } else { div_pos = rand() % sz; if (div_pos == 0) div_pos++; } ENSURE(sz < buffer_sz); for (unsigned i = 0; i < sz-1; i++) { if (i == div_pos && i < sz-2) { buffer[i] = '/'; i++; buffer[i] = '1' + (rand() % 9); } else { buffer[i] = '0' + (rand() % 10); } } buffer[sz-1] = 0; } #endif static void bug1() { synch_mpq_manager m; mpq a; mpq b; m.set(a, 2); m.set(b, 1, 2); m.inv(a, a); ENSURE(m.eq(a, b)); } static void bug2() { synch_mpq_manager m; mpq a; mpq b; m.set(a, -2); m.set(b, -1, 2); m.inv(a, a); ENSURE(m.eq(a, b)); } static void tst2() { unsynch_mpq_manager m; scoped_mpq a(m); m.set(a, 1, 3); std::cout << "1/3: "; m.display_decimal(std::cout, a, 10); std::cout << "\n1/4: "; m.set(a, 1, 4); m.display_decimal(std::cout, a, 10); std::cout << "\n"; } static void set_str_bug() { unsynch_mpq_manager m; scoped_mpq a(m); scoped_mpq b(m); m.set(a, "1.0"); std::cout << a << "\n"; m.set(b, 1); ENSURE(a == b); m.set(a, "1.1"); std::cout << a << "\n"; m.set(b, 11, 10); ENSURE(a == b); m.set(a, "1/3"); m.set(b, 1, 3); std::cout << a << "\n"; ENSURE(a == b); } static void tst_prev_power_2(int64_t n, uint64_t d, unsigned expected) { unsynch_mpq_manager m; scoped_mpq a(m); m.set(a, n, d); ENSURE(m.prev_power_of_two(a) == expected); } static void tst_prev_power_2() { tst_prev_power_2(-10, 1, 0); tst_prev_power_2(0, 1, 0); tst_prev_power_2(1, 1, 0); tst_prev_power_2(2, 1, 1); tst_prev_power_2(3, 1, 1); tst_prev_power_2(4, 1, 2); tst_prev_power_2(5, 1, 2); tst_prev_power_2(8, 1, 3); tst_prev_power_2(9, 1, 3); tst_prev_power_2(9, 2, 2); tst_prev_power_2(9, 4, 1); tst_prev_power_2(9, 5, 0); tst_prev_power_2((1ll << 60) + 1, 1, 60); tst_prev_power_2((1ll << 60), 1, 60); tst_prev_power_2((1ll << 60) - 1, 1, 59); tst_prev_power_2((1ll << 60), 3, 58); } void tst_mpq() { tst_prev_power_2(); set_str_bug(); bug2(); bug1(); tst0(); tst1(); tst2(); } z3-z3-4.8.7/src/test/mpz.cpp000066400000000000000000000406011356505360400155100ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: mpz.cpp Abstract: Author: Leonardo de Moura (leonardo) 2010-06-18. Revision History: --*/ #include "util/mpz.h" #include "util/rational.h" #include "util/timeit.h" #include "util/scoped_numeral.h" static void tst1() { synch_mpz_manager m; char const * str = "1002034040050606089383838288182"; mpz v; m.set(v, str); std::cout << str << "\n" << m.to_string(v) << "\n"; mpz v2, v3; m.mul(v, m.mk_z(-2), v2); std::cout << "*-2 = \n" << m.to_string(v2) << "\n"; m.add(v, v2, v3); m.neg(v3); ENSURE(m.eq(v, v3)); ENSURE(m.le(v, v3)); ENSURE(m.ge(v, v3)); ENSURE(m.lt(v2, v)); ENSURE(m.le(v2, v)); ENSURE(m.gt(v, v2)); ENSURE(m.ge(v, v2)); ENSURE(m.neq(v, v2)); ENSURE(!m.neq(v, v3)); m.del(v); m.del(v2); m.del(v3); } static void tst2() { synch_mpz_manager m; mpz v1, v2, v3; m.set(v1, static_cast(UINT_MAX)); m.add(v1, m.mk_z(1), v2); m.mul(v2, v2, v3); std::cout << "v2:\n" << m.to_string(v2) << "\n"; std::cout << "v2*v2:\n" << m.to_string(v3) << "\n"; m.del(v1); m.del(v2); m.del(v3); } static void tst2b() { synch_mpz_manager m; mpz v1, v2, v3; m.set(v1, static_cast(UINT_MAX)); m.add(v1, m.mk_z(1), v2); m.mul(v2, v2, v3); std::cout << "v2:\n" << m.to_string(v2) << "\n"; std::cout << "v2*v2:\n" << m.to_string(v3) << "\n"; m.mul(v2, v2, v2); m.mul(v2, v2, v2); m.mul(v2, v2, v2); std::cout << "v2: " << m.to_string(v2) << "\n"; m.del(v1); m.del(v2); m.del(v3); } #if 0 static void mk_random_num_str(unsigned buffer_sz, char * buffer) { unsigned sz = (rand() % (buffer_sz-2)) + 1; ENSURE(sz < buffer_sz); for (unsigned i = 0; i < sz-1; i++) { buffer[i] = '0' + (rand() % 10); } if (rand() % 2 == 0) buffer[0] = '-'; buffer[sz-1] = 0; } #endif static void bug1() { synch_mpz_manager m; mpz v1; m.set(v1, "1002043949858757875676767675747473"); mpz v2; m.sub(v1, v1, v2); ENSURE(m.is_zero(v2)); m.del(v1); m.del(v2); } static void bug2() { synch_mpz_manager m; mpz v1, v2, vout; m.set(v1, "78160958900072552148646898355155840547214990303507678364419557473408815232466629049311995556059463490539128818602490221544425042127795"); m.set(v2, "403412298227858394469856607272647132163832860126054679347881638761723785858733108109249157334220127"); m.sub(v1, v2, vout); m.del(v1); m.del(v2); m.del(vout); } static void bug3() { synch_mpz_manager m; mpz v1, v2; m.set(v1, INT_MIN); m.set(v2, INT_MAX); m.add(v2, m.mk_z(1), v2); m.neg(v1); ENSURE(m.eq(v1, v2)); m.del(v1); m.del(v2); } static void bug4() { synch_mpz_manager m; mpz x, y; m.set(y, static_cast(4294967295ull)); m.set(x, static_cast(4026531839ull)); mpz result1; m.bitwise_or(x, y, result1); mpz result2; m.set(result2, x); m.bitwise_or(result2, y, result2); std::cout << m.to_string(result1) << " " << m.to_string(result2) << "\n"; ENSURE(m.eq(result1, result2)); m.del(x); m.del(y); m.del(result1); m.del(result2); } void tst_div2k(synch_mpz_manager & m, mpz const & v, unsigned k) { mpz x, y, two(2), pw; m.machine_div2k(v, k, x); m.power(two, k, pw); m.machine_div(v, pw, y); bool is_eq = m.eq(x, y); (void)is_eq; CTRACE("mpz_2k", !is_eq, tout << "div: " << m.to_string(v) << ", k: " << k << " r: " << m.to_string(x) << ", expected: " << m.to_string(y) << "\n";); ENSURE(is_eq); m.del(x); m.del(y); m.del(pw); } void tst_div2k(synch_mpz_manager & m, int v, unsigned k) { mpz x; m.set(x, v); tst_div2k(m, x, k); m.del(x); } void tst_div2k(synch_mpz_manager & m, char const * v, unsigned k) { mpz x; m.set(x, v); tst_div2k(m, x, k); m.del(x); } void tst_mul2k(synch_mpz_manager & m, mpz const & v, unsigned k) { mpz x, y, two(2), pw; m.mul2k(v, k, x); m.power(two, k, pw); m.mul(v, pw, y); bool is_eq = m.eq(x, y); (void)is_eq; CTRACE("mpz_2k", !is_eq, tout << "mul: " << m.to_string(v) << ", k: " << k << " r: " << m.to_string(x) << ", expected: " << m.to_string(y) << "\n";); ENSURE(is_eq); m.del(x); m.del(y); m.del(pw); } void tst_mul2k(synch_mpz_manager & m, int v, unsigned k) { mpz x; m.set(x, v); tst_mul2k(m, x, k); m.del(x); } void tst_mul2k(synch_mpz_manager & m, char const * v, unsigned k) { mpz x; m.set(x, v); tst_mul2k(m, x, k); m.del(x); } static void tst_2k() { synch_mpz_manager m; tst_mul2k(m, 120, 32); tst_mul2k(m, "102938484858483282832717616263643648481827437292943727163646457588332211", 22); tst_div2k(m, "102938484858483282832717616263643648481827437292943727163646457588332211", 22); tst_div2k(m, 3, 1); tst_div2k(m, 3, 2); tst_div2k(m, 3, 0); tst_div2k(m, 120, 32); tst_div2k(m, 120, 0); tst_div2k(m, 81, 2); tst_div2k(m, -3, 1); tst_div2k(m, -3, 2); tst_div2k(m, -3, 0); tst_div2k(m, -102, 4); tst_div2k(m, 0, 3); tst_div2k(m, 0, 1000); tst_div2k(m, 7, 10000); tst_div2k(m, -7, 1000); tst_div2k(m, -7, 2); tst_div2k(m, "1029384848584832828327176162636436484", 4); tst_div2k(m, "1029384848584832828327176162636436484", 100); tst_div2k(m, "102938484858483282832717616263643648481827437292943727163646457588332211", 1); tst_div2k(m, "102938484858483282832717616263643648481827437292943727163646457588332211", 0); tst_div2k(m, "102938484858483282832717616263643648481827437292943727163646457588332211", 4); tst_div2k(m, "102938484858483282832717616263643648481827437292943727163646457588332211", 7); tst_div2k(m, "102938484858483282832717616263643648433838737661626264364583983298239291919", 100); tst_div2k(m, "11579208923731619542357098500868790785326998466564056403945758400793872761761638458372762", 100); tst_div2k(m, "11579208923731619542357098500868790785326998466564056403945758400793872761761638458372762", 177); tst_div2k(m, "11579208923731619542357098500868790785326998466564056403945758400793872761761638458372762", 77); tst_div2k(m, "11579208923731619542357098500868790785326998466564056403945758400793872761761638458372762", 32); tst_div2k(m, "11579208923731619542357098500868790785326998466564056403945758400793872761761638458372762", 64); tst_div2k(m, "-11579208923731619542357098500868790785326998466564056403945758400793872761761638458372762", 64); tst_div2k(m, "-11579208923731619542357098500868790785326998466564056403945758400793872761761638458372762", 128); tst_div2k(m, "-1092983874757371817626399990000000", 100); tst_div2k(m, "-109298387475737181762639999000000231", 8); tst_div2k(m, "-109298387475737181762639999000000231", 16); tst_div2k(m, "-109298387475737181762639999000000231", 17); tst_div2k(m, "-109298387475737181762639999000000231", 32); tst_mul2k(m, 3, 1); tst_mul2k(m, 3, 2); tst_mul2k(m, 3, 0); tst_mul2k(m, 120, 32); tst_mul2k(m, 120, 0); tst_mul2k(m, 81, 2); tst_mul2k(m, -3, 1); tst_mul2k(m, -3, 2); tst_mul2k(m, -3, 0); tst_mul2k(m, -102, 4); tst_mul2k(m, 0, 3); tst_mul2k(m, 0, 1000); tst_mul2k(m, 7, 10000); tst_mul2k(m, -7, 1000000); tst_mul2k(m, -7, 2); tst_mul2k(m, "1029384848584832828327176162636436484", 4); tst_mul2k(m, "1029384848584832828327176162636436484", 100); tst_mul2k(m, "102938484858483282832717616263643648481827437292943727163646457588332211", 1); tst_mul2k(m, "102938484858483282832717616263643648481827437292943727163646457588332211", 0); tst_mul2k(m, "102938484858483282832717616263643648481827437292943727163646457588332211", 4); tst_mul2k(m, "102938484858483282832717616263643648481827437292943727163646457588332211", 22); tst_mul2k(m, "102938484858483282832717616263643648481827437292943727163646457588332211", 7); tst_mul2k(m, "102938484858483282832717616263643648433838737661626264364583983298239291919", 100); tst_mul2k(m, "11579208923731619542357098500868790785326998466564056403945758400793872761761638458372762", 100); tst_mul2k(m, "11579208923731619542357098500868790785326998466564056403945758400793872761761638458372762", 177); tst_mul2k(m, "11579208923731619542357098500868790785326998466564056403945758400793872761761638458372762", 77); tst_mul2k(m, "11579208923731619542357098500868790785326998466564056403945758400793872761761638458372762", 32); tst_mul2k(m, "11579208923731619542357098500868790785326998466564056403945758400793872761761638458372762", 64); tst_mul2k(m, "-11579208923731619542357098500868790785326998466564056403945758400793872761761638458372762", 64); tst_mul2k(m, "-11579208923731619542357098500868790785326998466564056403945758400793872761761638458372762", 128); tst_mul2k(m, "-1092983874757371817626399990000000", 100); tst_mul2k(m, "-109298387475737181762639999000000231", 8); tst_mul2k(m, "-109298387475737181762639999000000231", 16); tst_mul2k(m, "-109298387475737181762639999000000231", 17); tst_mul2k(m, "-109298387475737181762639999000000231", 32); } void tst_int_min_bug() { synch_mpz_manager m; mpz intmin(INT_MIN); mpz big; mpz expected; mpz r; m.set(big, static_cast(UINT64_MAX)); m.set(expected, "18446744075857035263"); m.sub(big, intmin, r); std::cout << "r: " << m.to_string(r) << "\nexpected: " << m.to_string(expected) << "\n"; ENSURE(m.eq(r, expected)); m.del(intmin); m.del(big); m.del(expected); m.del(r); } void tst_int64_min_bug() { synch_mpz_manager m; mpz intmin; mpz test; m.set(test, "-9223372036854775808"); m.set(intmin, std::numeric_limits::min()); std::cout << "minint: " << m.to_string(intmin) << "\n"; ENSURE(m.eq(test, intmin)); m.del(intmin); m.del(test); } void tst_scoped() { synch_mpz_manager m; scoped_synch_mpz a(m); scoped_synch_mpz b(m); m.set(a, 1000231); m.set(b, "102928187172727273"); std::cout << "a: " << m.to_string(a) << "\n"; std::cout << "b: " << m.to_string(b) << "\n"; m.mul(a, b, b); std::cout << "b: " << m.to_string(b) << "\n"; } #define NUM_PRIMES 168 unsigned g_primes[NUM_PRIMES] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997 }; // Return a big number by multipling powers of the first NUM_PRIMES. // - ratio: rand() % ratio == 0 is used to decide whether a specific prime will be included or not. // - max_pw: if condition above is satisfied, then we use (rand() % max_pw) + 1 as the power. void mk_big_num(unsynch_mpz_manager & m, unsigned ratio, unsigned max_pw, mpz & r) { scoped_mpz tmp(m); m.set(r, 1); for (unsigned i = 0; i < NUM_PRIMES; i++) { if ((rand() % ratio) == 0) { m.power(mpz(g_primes[i]), (rand() % max_pw) + 1, tmp); m.mul(r, tmp, r); } } if ((rand() % 2) == 0) m.neg(r); } void slow_gcd(unsynch_mpz_manager & m, mpz const & a, mpz const & b, mpz & c) { scoped_mpz tmp1(m); scoped_mpz tmp2(m); scoped_mpz aux(m); m.set(tmp1, a); m.set(tmp2, b); m.abs(tmp1); m.abs(tmp2); if (m.le(tmp1, tmp2)) m.swap(tmp1, tmp2); if (m.is_zero(tmp2)) { m.set(c, tmp1); return; } for(;;) { m.rem(tmp1, tmp2, aux); if (m.is_zero(aux)) { m.set(c, tmp2); return; } m.set(tmp1, tmp2); m.set(tmp2, aux); } } void rand_tst_gcd(unsigned num, unsigned ratio, unsigned pw) { unsynch_mpz_manager m; scoped_mpz a(m); scoped_mpz b(m); scoped_mpz g1(m); scoped_mpz g2(m); for (unsigned i = 0; i < num; i++) { mk_big_num(m, ratio, pw, a); mk_big_num(m, ratio, pw, b); slow_gcd(m, a, b, g1); m.gcd(a, b, g2); std::cout << "a: " << m.to_string(a) << "\n"; std::cout << "b: " << m.to_string(b) << "\n"; std::cout << "g1: " << m.to_string(g1) << "\n"; std::cout << "g2: " << m.to_string(g2) << "\n"; std::cout << "\n"; if (!m.eq(g1, g2)) { std::cout << "\n\nBUG\n\n"; UNREACHABLE(); } } } void tst_gcd() { unsynch_mpz_manager m; scoped_mpz a(m); scoped_mpz b(m); scoped_mpz g(m); m.set(a, "125141154046000416200229049397707776"); m.set(b, "67062117506072642958648904906464"); m.gcd(a, b, g); std::cout << "g: " << m.to_string(g) << "\n"; m.set(a, "664877781119188360263909568610284290708591605105963082581413244598320881431041311468785283029437655134762231312337924555674674176"); m.set(b, "21691055098083293041646678174999125628463716392747356050705870375852789453851926624107939885328471215366825649627326658281728580399051770334114658498352848410853519374962852431831492868108719406669605254329669417322836882756478295264"); for (unsigned i = 0; i < 50000; i++) { m.del(g); m.gcd(a, b, g); // slow_gcd(m, a, b, g); } std::cout << "g: " << m.to_string(g) << "\n"; } void tst_log2(unsynch_mpz_manager & m, mpz const & a) { scoped_mpz b(m); unsigned k = m.log2(a); m.power(mpz(2), k, b); ENSURE(m.is_zero(a) || m.le(b, a)); m.power(mpz(2), k+1, b); ENSURE(m.le(a, b)); scoped_mpz neg_a(m); m.set(neg_a, a); m.neg(neg_a); k = m.mlog2(neg_a); m.power(mpz(2), k, b); m.neg(b); ENSURE(m.is_zero(neg_a) || m.le(neg_a, b)); m.power(mpz(2), k+1, b); m.neg(b); ENSURE(m.le(b, neg_a)); } void tst_log2() { unsynch_mpz_manager m; for (unsigned i = 0; i <= 64; i++) std::cout << "log2(" << i << "): " << m.log2(mpz(i)) << "\n"; for (unsigned i = 0; i < 1000; i++) tst_log2(m, mpz(i)); scoped_mpz a(m); m.set(a, "1029489380487098723984579237"); for (unsigned i = 0; i < 1000; i++) { m.inc(a); tst_log2(m, a); } } void tst_root() { unsynch_mpz_manager m; scoped_mpz a(m); m.set(a, 213); VERIFY(!m.root(a, 5)); std::cout << "213^{1/5}: " << a << "\n"; ENSURE(m.eq(a, mpz(3))); m.set(a, -213); VERIFY(!m.root(a, 5)); std::cout << "-213^{1/5}: " << a << "\n"; ENSURE(m.eq(a, mpz(-2))); m.set(a, 0); VERIFY(m.root(a, 3)); ENSURE(m.is_zero(a)); m.set(a, 8); VERIFY(m.root(a, 3)); ENSURE(m.eq(a, mpz(2))); m.set(a, -8); VERIFY(m.root(a, 3)); ENSURE(m.eq(a, mpz(-2))); } void tst_gcd_bug() { unsynch_mpz_manager m; scoped_mpz a(m), b(m), g(m); m.set(a, INT_MIN); m.set(b, INT_MIN); m.gcd(a, b, g); std::cout << "g: " << g << "\n"; } void tst_div2k_bug() { unsynch_mpz_manager m; scoped_mpz a(m), b(m), g(m); m.set(a, UINT_MAX); m.machine_div2k(a, 32, b); std::cout << "a: " << a << ", b: " << b << "\n"; } static void tst5() { unsynch_mpz_manager m; scoped_mpz a(m); a = 1; std::cout << "1 -> " << m.log2(a) << "\n"; a = 5; std::cout << "5 -> " << m.log2(a) << "\n"; a = 16; std::cout << "16 -> " << m.log2(a) << "\n"; a = INT_MAX; std::cout << "INT_MAX -> " << m.log2(a) << "\n"; a = INT_MAX/4; std::cout << "INT_MAX/4 -> " << m.log2(a) << "\n"; } static void tst_pw2() { unsynch_mpz_manager m; scoped_mpz a(m); for (unsigned i = 0; i < 128; i++) { m.power(mpz(2), i, a); std::cout << "i: " << i << ", a: " << a << std::endl; } } void tst_mpz() { disable_trace("mpz"); enable_trace("mpz_2k"); tst_pw2(); tst5(); tst_div2k_bug(); rand_tst_gcd(50, 3, 2); tst_2k(); tst_gcd_bug(); tst_root(); tst_log2(); // tst_gcd(); tst_scoped(); tst_int_min_bug(); tst_int64_min_bug(); bug4(); bug3(); bug1(); bug2(); tst1(); tst2(); tst2b(); } z3-z3-4.8.7/src/test/nlarith_util.cpp000066400000000000000000000027721356505360400174070ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "qe/nlarith_util.h" #include "ast/arith_decl_plugin.h" #include "ast/ast_pp.h" #include "ast/reg_decl_plugins.h" void tst_nlarith_util() { ast_manager M; reg_decl_plugins(M); arith_util A(M); sort_ref R(A.mk_real(), M); app_ref one(A.mk_numeral(rational(1), false), M); app_ref two(A.mk_numeral(rational(2), false), M); app_ref ten(A.mk_numeral(rational(10), false), M); app_ref x(M.mk_const(symbol("x"), R), M); app_ref y(M.mk_const(symbol("y"), R), M); app_ref z(M.mk_const(symbol("z"), R), M); expr_ref p1(A.mk_le(A.mk_add(A.mk_mul(x,x,y), y), one), M); expr_ref p2(A.mk_le(A.mk_add(A.mk_mul(x,x,y), z), one), M); enable_trace("nlarith"); nlarith::util u(M); nlarith::branch_conditions bc(M); expr_ref_vector preds(M), branches(M); vector substs; expr* lits[2] = { p1.get(), p2.get() }; if (!u.create_branches(x.get(), 2, lits, bc)) { std::cout << "Failed to create branches\n"; return; } for (unsigned i = 0; i < preds.size(); ++i) { std::cout << "Pred: " << mk_pp(preds[i].get(), M) << "\n"; } for (unsigned i = 0; i < branches.size(); ++i) { std::cout << "Branch:\n" << mk_pp(branches[i].get(), M) << "\n"; for (unsigned j = 0; j < substs[i].size(); ++j) { std::cout << mk_pp(preds[j].get(), M) << " |-> " << mk_pp(substs[i][j].get(), M) << "\n"; } } } z3-z3-4.8.7/src/test/nlsat.cpp000066400000000000000000000611641356505360400160320ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: nlsat.cpp Abstract: nlsat procedure Author: Leonardo (leonardo) 2012-01-09 Notes: --*/ #include "nlsat/nlsat_assignment.h" #include "nlsat/nlsat_interval_set.h" #include "nlsat/nlsat_evaluator.h" #include "nlsat/nlsat_solver.h" #include "util/util.h" #include "nlsat/nlsat_explain.h" #include "math/polynomial/polynomial_cache.h" #include "util/rlimit.h" nlsat::interval_set_ref tst_interval(nlsat::interval_set_ref const & s1, nlsat::interval_set_ref const & s2, unsigned expected_num_intervals, bool check_num_intervals = true) { nlsat::interval_set_manager & ism = s1.m(); nlsat::interval_set_ref r(ism); std::cout << "s1: " << s1 << "\n"; std::cout << "s2: " << s2 << "\n"; r = ism.mk_union(s1, s2); std::cout << "union(s1, s2): " << r << std::endl; ENSURE(!check_num_intervals || ism.num_intervals(r) == expected_num_intervals); ENSURE(ism.subset(s1, r)); ENSURE(ism.subset(s2, r)); if (ism.set_eq(s1, s2)) { ENSURE(ism.set_eq(s1, r)); ENSURE(ism.set_eq(s2, r)); } else { ENSURE(ism.subset(s1, s2) || !ism.subset(r, s2)); ENSURE(ism.subset(s2, s1) || !ism.subset(r, s1)); } nlsat::interval_set_ref r2(ism); r2 = ism.mk_union(s2, s1); ENSURE(ism.set_eq(r, r2)); anum zero; nlsat::interval_set_ref full(ism); nlsat::literal dummy(131, false); full = ism.mk(true, true, zero, true, true, zero, dummy, nullptr); ENSURE(ism.set_eq(r, full) == ism.is_full(r)); return r; } static void tst3() { enable_trace("nlsat_interval"); reslimit rl; unsynch_mpq_manager qm; anum_manager am(rl, qm); small_object_allocator allocator; nlsat::interval_set_manager ism(am, allocator); scoped_anum sqrt2(am), m_sqrt2(am), two(am), m_two(am), three(am), one(am), zero(am); am.set(two, 2); am.set(m_two, -2); am.set(one, 1); am.root(two, 2, sqrt2); am.set(m_sqrt2, sqrt2); am.neg(m_sqrt2); am.set(three, 3); nlsat::literal p1(1, false); nlsat::literal p2(2, false); nlsat::literal p3(3, false); nlsat::literal p4(4, false); nlsat::literal np2(2, true); nlsat::interval_set_ref s1(ism), s2(ism), s3(ism), s4(ism); s1 = ism.mk_empty(); std::cout << "s1: " << s1 << "\n"; s2 = ism.mk(true, true, zero, false, false, sqrt2, np2, nullptr); std::cout << "s2: " << s2 << "\n"; s3 = ism.mk(false, false, zero, false, false, two, p1, nullptr); std::cout << "s3: " << s3 << "\n"; s4 = ism.mk_union(s2, s3); std::cout << "s4: " << s4 << "\n"; // Case // s1: [ ... ] // s2: [ ... ] s1 = ism.mk(false, false, zero, false, false, two, p1, nullptr); s2 = ism.mk(false, false, zero, false, false, two, p2, nullptr); tst_interval(s1, s2, 1); // Case // s1: [ ... ] // s2: [ ... ] s1 = ism.mk(false, false, zero, false, false, two, p1, nullptr); s2 = ism.mk(false, false, m_sqrt2, false, false, one, p2, nullptr); s3 = ism.mk_union(s1, s2); tst_interval(s1, s2, 2); // Case // s1: [ ... ] // s2: [ ... ] s1 = ism.mk(false, false, m_sqrt2, false, false, one, p1, nullptr); s2 = ism.mk(false, false, zero, false, false, two, p2, nullptr); tst_interval(s1, s2, 2); // Case // s1: [ ... ] // s2: [ ... ] s1 = ism.mk(false, false, m_sqrt2, false, false, one, p1, nullptr); s2 = ism.mk(false, false, two, false, false, three, p2, nullptr); tst_interval(s1, s2, 2); // Case // s1: [ ... ] // s2: [ ... ] s1 = ism.mk(false, false, m_sqrt2, false, false, three, p1, nullptr); s2 = ism.mk(false, false, zero, false, false, two, p2, nullptr); tst_interval(s1, s2, 1); // Case // s1: [ ... ] // s2: [ ... ] [ ... ] s1 = ism.mk(false, false, m_two, false, false, two, p1, nullptr); s2 = ism.mk(false, false, m_sqrt2, false, false, zero, p2, nullptr); s3 = ism.mk(false, false, one, false, false, three, p2, nullptr); s2 = ism.mk_union(s2, s3); tst_interval(s1, s2, 2); // Case // s1: [ ... ] // s2: [ ... ] s1 = ism.mk(false, false, m_two, false, false, two, p1, nullptr); s2 = ism.mk(false, false, two, false, false, three, p2, nullptr); tst_interval(s1, s2, 2); s2 = ism.mk(true, false, two, false, false, three, p2, nullptr); tst_interval(s1, s2, 2); s2 = ism.mk(true, false, two, false, false, three, p1, nullptr); tst_interval(s1, s2, 1); s1 = ism.mk(false, false, m_two, true, false, two, p1, nullptr); tst_interval(s1, s2, 2); s1 = ism.mk(false, false, two, false, false, two, p1, nullptr); s2 = ism.mk(false, false, two, false, false, three, p2, nullptr); tst_interval(s1, s2, 1); // Case // s1: [ ... ] [ ... ] // s2: [ .. ] [ ... ] [ ... ] s1 = ism.mk(false, false, m_two, false, false, zero, p1, nullptr); s3 = ism.mk(false, false, one, false, false, three, p1, nullptr); s1 = ism.mk_union(s1, s3); s2 = ism.mk(true, true, zero, false, false, m_sqrt2, p2, nullptr); tst_interval(s1, s2, 3); s3 = ism.mk(false, false, one, false, false, sqrt2, p2, nullptr); s2 = ism.mk_union(s2, s3); s3 = ism.mk(false, false, two, true, true, zero, p2, nullptr); s2 = ism.mk_union(s2, s3); tst_interval(s1, s2, 4); // Case s1 = ism.mk(true, true, zero, false, false, one, p1, nullptr); s2 = ism.mk(true, false, one, true, true, zero, p2, nullptr); tst_interval(s1, s2, 2); s2 = ism.mk(true, false, one, false, false, two, p2, nullptr); s3 = ism.mk(false, false, two, true, true, zero, p1, nullptr); s2 = ism.mk_union(s2, s3); tst_interval(s1, s2, 3); } static nlsat::interval_set_ref mk_random(nlsat::interval_set_manager & ism, anum_manager & am, int range, int space, int tries, bool minus_inf, bool plus_inf, nlsat::literal lit) { static random_gen gen; ENSURE(range > 0); ENSURE(space > 0); nlsat::interval_set_ref r(ism), curr(ism); scoped_anum lower(am); scoped_anum upper(am); int prev = -range + (gen() % (space*4)); if (gen() % 3 == 0 && minus_inf) { int next = prev + (gen() % space); bool open = gen() % 2 == 0; am.set(upper, next); r = ism.mk(true, true, lower, open, false, upper, lit, nullptr); prev = next; } for (int i = 0; i < tries; i++) { int l = prev + (gen() % space); int u = l + (gen() % space); bool lower_open = gen() % 2 == 0; bool upper_open = gen() % 2 == 0; if ((lower_open || upper_open) && l == u) u++; am.set(lower, l); am.set(upper, u); curr = ism.mk(lower_open, false, lower, upper_open, false, upper, lit, nullptr); r = ism.mk_union(r, curr); prev = u; } if (gen() % 3 == 0 && plus_inf) { int next = prev + (gen() % space); bool open = gen() % 2 == 0; am.set(lower, next); curr = ism.mk(open, false, lower, true, true, upper, lit, nullptr); r = ism.mk_union(r, curr); } return r; } static void check_subset_result(nlsat::interval_set_ref const & s1, nlsat::interval_set_ref const & s2, nlsat::interval_set_ref const & r, nlsat::literal l1, nlsat::literal l2) { nlsat::interval_set_manager ism(s1.m()); nlsat::interval_set_ref tmp(ism); unsigned num = ism.num_intervals(r); nlsat::literal_vector lits; ptr_vector clauses; ism.get_justifications(r, lits, clauses); ENSURE(lits.size() <= 2); for (unsigned i = 0; i < num; i++) { tmp = ism.get_interval(r, i); ism.get_justifications(tmp, lits, clauses); ENSURE(lits.size() == 1); if (lits[0] == l1) { ENSURE(ism.subset(tmp, s1)); } else { ENSURE(lits[0] == l2); ENSURE(ism.subset(tmp, s2)); } } } static void tst4() { enable_trace("nlsat_interval"); reslimit rl; unsynch_mpq_manager qm; anum_manager am(rl, qm); small_object_allocator allocator; nlsat::interval_set_manager ism(am, allocator); nlsat::interval_set_ref s1(ism), s2(ism), r(ism); nlsat::literal l1(1, false); nlsat::literal l2(2, false); for (unsigned i = 0; i < 100; i++) { s1 = mk_random(ism, am, 20, 3, 10, true, true, l1); s2 = mk_random(ism, am, 20, 3, 10, true, true, l2); r = tst_interval(s1, s2, 0, false); check_subset_result(s1, s2, r, l1, l2); } for (unsigned i = 0; i < 100; i++) { s1 = mk_random(ism, am, 200, 100, 20, true, true, l1); s2 = mk_random(ism, am, 200, 100, 20, true, true, l2); r = tst_interval(s1, s2, 0, false); check_subset_result(s1, s2, r, l1, l2); } } static void tst5() { params_ref ps; reslimit rlim; nlsat::solver s(rlim, ps, false); anum_manager & am = s.am(); nlsat::pmanager & pm = s.pm(); nlsat::assignment as(am); small_object_allocator allocator; nlsat::interval_set_manager ism(am, allocator); nlsat::evaluator ev(s, as, pm, allocator); nlsat::var x0, x1; x0 = pm.mk_var(); x1 = pm.mk_var(); polynomial_ref p(pm); polynomial_ref _x0(pm), _x1(pm); _x0 = pm.mk_polynomial(x0); _x1 = pm.mk_polynomial(x1); p = (_x0^2) + (_x1^2) - 2; nlsat::poly * _p[1] = { p.get() }; bool is_even[1] = { false }; nlsat::bool_var b = s.mk_ineq_atom(nlsat::atom::GT, 1, _p, is_even); nlsat::atom * a = s.bool_var2atom(b); ENSURE(a != nullptr); scoped_anum zero(am); am.set(zero, 0); as.set(0, zero); auto i = ev.infeasible_intervals(a, true, nullptr); std::cout << "1) " << i << "\n"; as.set(1, zero); auto i2 = ev.infeasible_intervals(a, true, nullptr); std::cout << "2) " << i2 << "\n"; } static void project(nlsat::solver& s, nlsat::explain& ex, nlsat::var x, unsigned num, nlsat::literal const* lits) { std::cout << "Project "; s.display(std::cout, num, lits); nlsat::scoped_literal_vector result(s); ex.project(x, num, lits, result); s.display(std::cout << "\n==>\n", result.size(), result.c_ptr()); std::cout << "\n"; } static void project_fa(nlsat::solver& s, nlsat::explain& ex, nlsat::var x, unsigned num, nlsat::literal const* lits) { std::cout << "Project "; nlsat::scoped_literal_vector result(s); ex(num, lits, result); std::cout << "(or"; for (auto l : result) { s.display(std::cout << " ", l); } for (unsigned i = 0; i < num; ++i) { s.display(std::cout << " ", ~lits[i]); } std::cout << ")\n"; } static nlsat::literal mk_gt(nlsat::solver& s, nlsat::poly* p) { nlsat::poly * _p[1] = { p }; bool is_even[1] = { false }; return s.mk_ineq_literal(nlsat::atom::GT, 1, _p, is_even); } static nlsat::literal mk_lt(nlsat::solver& s, nlsat::poly* p) { nlsat::poly * _p[1] = { p }; bool is_even[1] = { false }; return s.mk_ineq_literal(nlsat::atom::LT, 1, _p, is_even); } static nlsat::literal mk_eq(nlsat::solver& s, nlsat::poly* p) { nlsat::poly * _p[1] = { p }; bool is_even[1] = { false }; return s.mk_ineq_literal(nlsat::atom::EQ, 1, _p, is_even); } static void tst6() { params_ref ps; reslimit rlim; nlsat::solver s(rlim, ps, false); anum_manager & am = s.am(); nlsat::pmanager & pm = s.pm(); nlsat::assignment as(am); nlsat::explain& ex = s.get_explain(); nlsat::var x0, x1, x2, a, b, c, d; a = s.mk_var(false); b = s.mk_var(false); c = s.mk_var(false); d = s.mk_var(false); x0 = s.mk_var(false); x1 = s.mk_var(false); x2 = s.mk_var(false); polynomial_ref p1(pm), p2(pm), p3(pm), p4(pm), p5(pm); polynomial_ref _x0(pm), _x1(pm), _x2(pm); polynomial_ref _a(pm), _b(pm), _c(pm), _d(pm); _x0 = pm.mk_polynomial(x0); _x1 = pm.mk_polynomial(x1); _x2 = pm.mk_polynomial(x2); _a = pm.mk_polynomial(a); _b = pm.mk_polynomial(b); _c = pm.mk_polynomial(c); _d = pm.mk_polynomial(d); p1 = (_a*(_x0^2)) + _x2 + 2; p2 = (_b*_x1) - (2*_x2) - _x0 + 8; nlsat::scoped_literal_vector lits(s); lits.push_back(mk_gt(s, p1)); lits.push_back(mk_gt(s, p2)); lits.push_back(mk_gt(s, (_c*_x0) + _x2 + 1)); lits.push_back(mk_gt(s, (_d*_x0) - _x1 + 5*_x2)); scoped_anum zero(am), one(am), two(am); am.set(zero, 0); am.set(one, 1); am.set(two, 2); as.set(0, one); as.set(1, one); as.set(2, two); as.set(3, two); as.set(4, two); as.set(5, one); as.set(6, one); s.set_rvalues(as); project(s, ex, x0, 2, lits.c_ptr()); project(s, ex, x1, 3, lits.c_ptr()); project(s, ex, x2, 3, lits.c_ptr()); project(s, ex, x2, 2, lits.c_ptr()); project(s, ex, x2, 4, lits.c_ptr()); project(s, ex, x2, 3, lits.c_ptr()+1); } static void tst7() { params_ref ps; reslimit rlim; nlsat::solver s(rlim, ps, false); nlsat::pmanager & pm = s.pm(); nlsat::var x0, x1, x2, a, b, c, d; a = s.mk_var(false); b = s.mk_var(false); c = s.mk_var(false); d = s.mk_var(false); x0 = s.mk_var(false); x1 = s.mk_var(false); x2 = s.mk_var(false); polynomial_ref p1(pm), p2(pm), p3(pm), p4(pm), p5(pm); polynomial_ref _x0(pm), _x1(pm), _x2(pm); polynomial_ref _a(pm), _b(pm), _c(pm), _d(pm); _x0 = pm.mk_polynomial(x0); _x1 = pm.mk_polynomial(x1); _x2 = pm.mk_polynomial(x2); _a = pm.mk_polynomial(a); _b = pm.mk_polynomial(b); _c = pm.mk_polynomial(c); _d = pm.mk_polynomial(d); p1 = _x0 + _x1; p2 = _x2 - _x0; p3 = (-1*_x0) - _x1; nlsat::scoped_literal_vector lits(s); lits.push_back(mk_gt(s, p1)); lits.push_back(mk_gt(s, p2)); lits.push_back(mk_gt(s, p3)); nlsat::literal_vector litsv(lits.size(), lits.c_ptr()); lbool res = s.check(litsv); VERIFY(res == l_false); for (unsigned i = 0; i < litsv.size(); ++i) { s.display(std::cout, litsv[i]); std::cout << " "; } std::cout << "\n"; litsv.reset(); litsv.append(2, lits.c_ptr()); res = s.check(litsv); ENSURE(res == l_true); s.display(std::cout); s.am().display(std::cout, s.value(x0)); std::cout << "\n"; s.am().display(std::cout, s.value(x1)); std::cout << "\n"; s.am().display(std::cout, s.value(x2)); std::cout << "\n"; } static void tst8() { params_ref ps; reslimit rlim; nlsat::solver s(rlim, ps, false); anum_manager & am = s.am(); nlsat::pmanager & pm = s.pm(); nlsat::assignment as(am); nlsat::explain& ex = s.get_explain(); nlsat::var x0, x1, x2, a, b, c, d; a = s.mk_var(false); b = s.mk_var(false); c = s.mk_var(false); d = s.mk_var(false); x0 = s.mk_var(false); x1 = s.mk_var(false); x2 = s.mk_var(false); polynomial_ref p1(pm), p2(pm), p3(pm), p4(pm), p5(pm); polynomial_ref _x0(pm), _x1(pm), _x2(pm); polynomial_ref _a(pm), _b(pm), _c(pm), _d(pm); _x0 = pm.mk_polynomial(x0); _x1 = pm.mk_polynomial(x1); _x2 = pm.mk_polynomial(x2); _a = pm.mk_polynomial(a); _b = pm.mk_polynomial(b); _c = pm.mk_polynomial(c); _d = pm.mk_polynomial(d); scoped_anum zero(am), one(am), two(am), six(am); am.set(zero, 0); am.set(one, 1); am.set(two, 2); am.set(six, 6); as.set(0, two); // a as.set(1, one); // b as.set(2, six); // c as.set(3, zero); // d as.set(4, zero); // x0 as.set(5, zero); // x1 as.set(6, two); // x2 s.set_rvalues(as); nlsat::scoped_literal_vector lits(s); lits.push_back(mk_eq(s, (_a*_x2*_x2) - (_b*_x2) - _c)); project(s, ex, x2, 1, lits.c_ptr()); } static void tst9() { params_ref ps; reslimit rlim; nlsat::solver s(rlim, ps, false); anum_manager & am = s.am(); nlsat::pmanager & pm = s.pm(); nlsat::assignment as(am); nlsat::explain& ex = s.get_explain(); int num_lo = 4; int num_hi = 5; svector los, his; for (int i = 0; i < num_lo; ++i) { los.push_back(s.mk_var(false)); scoped_anum num(am); am.set(num, - i - 1); as.set(i, num); } for (int i = 0; i < num_hi; ++i) { his.push_back(s.mk_var(false)); scoped_anum num(am); am.set(num, i + 1); as.set(num_lo + i, num); } nlsat::var _z = s.mk_var(false); nlsat::var _x = s.mk_var(false); polynomial_ref x(pm), z(pm); x = pm.mk_polynomial(_x); scoped_anum val(am); am.set(val, 0); as.set(num_lo + num_hi, val); as.set(num_lo + num_hi + 1, val); s.set_rvalues(as); nlsat::scoped_literal_vector lits(s); for (int i = 0; i < num_lo; ++i) { polynomial_ref y(pm); y = pm.mk_polynomial(los[i]); lits.push_back(mk_gt(s, x - y)); } for (int i = 0; i < num_hi; ++i) { polynomial_ref y(pm); y = pm.mk_polynomial(his[i]); lits.push_back(mk_gt(s, y - x)); } z = pm.mk_polynomial(_z); lits.push_back(mk_eq(s, x - z)); #define TEST_ON_OFF() \ std::cout << "Off "; \ ex.set_signed_project(false); \ project(s, ex, _x, lits.size()-1, lits.c_ptr()); \ std::cout << "On "; \ ex.set_signed_project(true); \ project(s, ex, _x, lits.size()-1, lits.c_ptr()); \ std::cout << "Off "; \ ex.set_signed_project(false); \ project(s, ex, _x, lits.size(), lits.c_ptr()); \ std::cout << "On "; \ ex.set_signed_project(true); \ project(s, ex, _x, lits.size(), lits.c_ptr()) \ TEST_ON_OFF(); lits.reset(); polynomial_ref u(pm); u = pm.mk_polynomial(his[1]); for (int i = 0; i < num_lo; ++i) { polynomial_ref y(pm); y = pm.mk_polynomial(los[i]); lits.push_back(mk_gt(s, u*x - y)); } for (int i = 0; i < num_hi; ++i) { polynomial_ref y(pm); y = pm.mk_polynomial(his[i]); lits.push_back(mk_gt(s, y - u*x)); } z = pm.mk_polynomial(_z); lits.push_back(mk_eq(s, u*x - z)); TEST_ON_OFF(); lits.reset(); u = pm.mk_polynomial(los[1]); for (int i = 0; i < num_lo; ++i) { polynomial_ref y(pm); y = pm.mk_polynomial(los[i]); lits.push_back(mk_gt(s, u*x - y)); } for (int i = 0; i < num_hi; ++i) { polynomial_ref y(pm); y = pm.mk_polynomial(his[i]); lits.push_back(mk_gt(s, y - u*x)); } z = pm.mk_polynomial(_z); lits.push_back(mk_eq(s, x - z)); TEST_ON_OFF(); } #if 0 #endif static void test_root_literal(nlsat::solver& s, nlsat::explain& ex, nlsat::var x, nlsat::atom::kind k, unsigned i, nlsat::poly* p) { nlsat::scoped_literal_vector result(s); ex.test_root_literal(k, x, 1, p, result); nlsat::bool_var b = s.mk_root_atom(k, x, i, p); s.display(std::cout, nlsat::literal(b, false)); s.display(std::cout << " ==> ", result.size(), result.c_ptr()); std::cout << "\n"; } static bool satisfies_root(nlsat::solver& s, nlsat::atom::kind k, nlsat::poly* p) { nlsat::pmanager & pm = s.pm(); anum_manager & am = s.am(); nlsat::assignment as(am); s.get_rvalues(as); polynomial_ref pr(p, pm); switch (k) { case nlsat::atom::ROOT_EQ: return am.eval_sign_at(pr, as) == 0; case nlsat::atom::ROOT_LE: return am.eval_sign_at(pr, as) <= 0; case nlsat::atom::ROOT_LT: return am.eval_sign_at(pr, as) < 0; case nlsat::atom::ROOT_GE: return am.eval_sign_at(pr, as) >= 0; case nlsat::atom::ROOT_GT: return am.eval_sign_at(pr, as) > 0; default: UNREACHABLE(); return false; } } static void tst10() { params_ref ps; reslimit rlim; nlsat::solver s(rlim, ps, false); anum_manager & am = s.am(); nlsat::pmanager & pm = s.pm(); nlsat::assignment as(am); nlsat::explain& ex = s.get_explain(); nlsat::var _a = s.mk_var(false); nlsat::var _b = s.mk_var(false); nlsat::var _c = s.mk_var(false); nlsat::var _x = s.mk_var(false); polynomial_ref x(pm), a(pm), b(pm), c(pm), p(pm); x = pm.mk_polynomial(_x); a = pm.mk_polynomial(_a); b = pm.mk_polynomial(_b); c = pm.mk_polynomial(_c); p = a*x*x + b*x + c; scoped_anum one(am), two(am), three(am), mone(am), mtwo(am), mthree(am), zero(am), one_a_half(am); am.set(zero, 0); am.set(one, 1); am.set(two, 2); am.set(three, 3); am.set(mone, -1); am.set(mtwo, -2); am.set(mthree, -3); rational oah(1,2); am.set(one_a_half, oah.to_mpq()); scoped_anum_vector nums(am); nums.push_back(one); nums.push_back(two); nums.push_back(one_a_half); nums.push_back(mone); nums.push_back(three); // a = 1, b = -3, c = 2: // has roots x = 2, x = 1: // 2^2 - 3*2 + 2 = 0 // 1 - 3 + 2 = 0 as.set(_a, one); as.set(_b, mthree); as.set(_c, two); for (unsigned i = 0; i < nums.size(); ++i) { as.set(_x, nums[i]); s.set_rvalues(as); std::cout << p << "\n"; as.display(std::cout); for (unsigned k = nlsat::atom::ROOT_EQ; k <= nlsat::atom::ROOT_GE; ++k) { if (satisfies_root(s, (nlsat::atom::kind) k, p)) { test_root_literal(s, ex, _x, (nlsat::atom::kind) k, 1, p); } } } as.set(_a, mone); as.set(_b, three); as.set(_c, mtwo); for (unsigned i = 0; i < nums.size(); ++i) { as.set(_x, nums[i]); s.set_rvalues(as); std::cout << p << "\n"; as.display(std::cout); for (unsigned k = nlsat::atom::ROOT_EQ; k <= nlsat::atom::ROOT_GE; ++k) { if (satisfies_root(s, (nlsat::atom::kind) k, p)) { test_root_literal(s, ex, _x, (nlsat::atom::kind) k, 1, p); } } } std::cout << "\n"; } static void tst11() { params_ref ps; reslimit rlim; nlsat::solver s(rlim, ps, false); anum_manager & am = s.am(); nlsat::pmanager & pm = s.pm(); nlsat::assignment as(am); nlsat::explain& ex = s.get_explain(); nlsat::var x, y, z; y = s.mk_var(false); z = s.mk_var(false); x = s.mk_var(false); polynomial_ref p1(pm), p2(pm), _x(pm), _y(pm), _z(pm); _x = pm.mk_polynomial(x); _y = pm.mk_polynomial(y); _z = pm.mk_polynomial(z); nlsat::scoped_literal_vector lits(s); scoped_anum zero(am), one(am), five(am); am.set(zero, 0); am.set(one, 1); am.set(five, 5); as.set(z, zero); as.set(y, five); as.set(x, five); s.set_rvalues(as); p1 = (_x - _y); p2 = ((_x*_x) - (_x*_y) - _z); lits.reset(); lits.push_back(mk_gt(s, p1)); lits.push_back(mk_eq(s, p2)); project_fa(s, ex, x, 2, lits.c_ptr()); // return; p1 = ((_x * _x) - (2 * _y * _x) - _z + (_y *_y)); p2 = _x + _y; as.set(_x, one); as.set(_y, zero); as.set(_z, one); lits.reset(); lits.push_back(mk_lt(s, p1)); lits.push_back(mk_eq(s, p2)); project_fa(s, ex, x, 2, lits.c_ptr()); return; as.set(z, zero); as.set(y, five); as.set(x, five); p1 = (_x - _y); p2 = ((_x*_x) - (_x*_y)); lits.reset(); lits.push_back(mk_gt(s, p1)); lits.push_back(mk_eq(s, p2)); project_fa(s, ex, x, 2, lits.c_ptr()); #if 0 !(x5^4 - 2 x3^2 x5^2 - 2 x1^2 x5^2 + 4 x0 x1 x5^2 - 2 x0^2 x5^2 + x3^4 - 2 x1^2 x3^2 + 4 x0 x1 x3^2 - 2 x0^2 x3^2 + x1^4 - 4 x0 x1^3 + 6 x0^2 x1^2 - 4 x0^3 x1 + x0^4 = 0) or !(x5 < 0) or !(x4 > root[1](x1 x4 - x0 x4 + x3)) or !(x3 + x1 - x0 > 0) or !(x1 - x0 < 0) or !(x7 > root[1](x1^2 x7 - 2 x0 x1 x7 + x0^2 x7 + x1 x3 - x0 x3)) or x7 - x4 = 0 or !(x1 x3 x7^2 - x0 x3 x7^2 - x5^2 x7 + x3^2 x7 + x1^2 x7 - 2 x0 x1 x7 + x0^2 x7 + x1 x3 - x0 x3 = 0) x0 := -1 x1 := -21.25 x2 := 0.0470588235? x3 := 2 x4 := -0.03125 x5 := -18.25 x6 := -0.5 x7 := 1 #endif } void tst_nlsat() { tst11(); std::cout << "------------------\n"; return; tst10(); std::cout << "------------------\n"; tst9(); std::cout << "------------------\n"; tst8(); std::cout << "------------------\n"; tst7(); std::cout << "------------------\n"; tst6(); std::cout << "------------------\n"; tst5(); std::cout << "------------------\n"; tst4(); std::cout << "------------------\n"; tst3(); } z3-z3-4.8.7/src/test/no_overflow.cpp000066400000000000000000000615521356505360400172510ustar00rootroot00000000000000/*++ Copyright (c) 2009 Microsoft Corporation Module Name: no_overflow.cpp Abstract: Test non-overflowing checks for arithmetic. Author: Yannick Moy (t-yanmoy) 2009-02-17. Revision History: --*/ #ifdef _WINDOWS #include "api/z3.h" #include "util/trace.h" #include "util/rational.h" #define TEST(TEST_NAME, TEST_OUTCOME, NEG_TEST_OUTCOME) \ do { \ if (TEST_NAME != NULL) \ { \ Z3_solver_push(ctx, s); \ Z3_solver_assert(ctx, s, TEST_NAME); \ ENSURE(Z3_solver_check(ctx, s) == TEST_OUTCOME); \ Z3_solver_pop(ctx, s, 1); \ \ Z3_solver_push(ctx, s); \ Z3_solver_assert(ctx, s, Z3_mk_not(ctx, TEST_NAME)); \ ENSURE(Z3_solver_check(ctx, s) == NEG_TEST_OUTCOME); \ Z3_solver_pop(ctx, s, 1); \ } \ } while (0) #define TEST_NO_OVERFLOW TEST(test_ovfl, Z3_L_TRUE, Z3_L_FALSE) #define TEST_OVERFLOW TEST(test_ovfl, Z3_L_FALSE, Z3_L_TRUE) #define TEST_NO_OVERFLOW_IFF(COND) \ do { \ if (COND) { \ TEST_NO_OVERFLOW; \ } \ else { \ TEST_OVERFLOW; \ } \ } while (0) #define TEST_NO_UNDERFLOW TEST(test_udfl, Z3_L_TRUE, Z3_L_FALSE) #define TEST_UNDERFLOW TEST(test_udfl, Z3_L_FALSE, Z3_L_TRUE) #define TEST_NO_UNDERFLOW_IFF(COND) \ do { \ if (COND) { \ TEST_NO_UNDERFLOW; \ } \ else { \ TEST_UNDERFLOW; \ } \ } while (0) #define TEST_ANY(TEST_NAME) \ do { \ Z3_solver_push(ctx, s); \ Z3_solver_assert(ctx, s, TEST_NAME); \ Z3_solver_check(ctx, s); /* ignore result of check */ \ Z3_solver_pop(ctx, s, 1); \ } while (0) #define TEST_ANY_OVERFLOW TEST_ANY(test_ovfl) #define TEST_ANY_UNDERFLOW TEST_ANY(test_udfl) Z3_ast mk_min(Z3_context ctx, Z3_sort bv, bool is_signed) { unsigned bvsize = Z3_get_bv_sort_size(ctx, bv); if (! is_signed) return Z3_mk_numeral(ctx, "0", bv); unsigned sz = bvsize - 1; rational min_bound = power(rational(2), sz); min_bound.neg(); return Z3_mk_numeral(ctx, min_bound.to_string().c_str(), bv); } Z3_ast mk_max(Z3_context ctx, Z3_sort bv, bool is_signed) { unsigned bvsize = Z3_get_bv_sort_size(ctx, bv); unsigned sz = is_signed ? bvsize - 1 : bvsize; rational max_bound = power(rational(2), sz); --max_bound; return Z3_mk_numeral(ctx, max_bound.to_string().c_str(), bv); } void test_add(unsigned bvsize, bool is_signed) { TRACE("no_overflow", tout << "test_add: bvsize = " << bvsize << ", is_signed = " << is_signed << "\n";); Z3_config cfg = Z3_mk_config(); Z3_context ctx = Z3_mk_context(cfg); Z3_solver s = Z3_mk_solver(ctx); Z3_solver_inc_ref(ctx, s); Z3_sort bv = Z3_mk_bv_sort(ctx, bvsize); Z3_ast min = mk_min(ctx, bv, is_signed); Z3_ast max = mk_max(ctx, bv, is_signed); Z3_ast t1; Z3_ast t2; Z3_ast test_ovfl; Z3_ast test_udfl; t1 = Z3_mk_const(ctx, Z3_mk_string_symbol(ctx,"x"), bv); t2 = Z3_mk_const(ctx, Z3_mk_string_symbol(ctx,"y"), bv); test_ovfl = Z3_mk_bvadd_no_overflow(ctx, t1, t2, is_signed); test_udfl = is_signed ? Z3_mk_bvadd_no_underflow(ctx, t1, t2) : NULL; Z3_solver_push(ctx, s); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t1, Z3_mk_numeral(ctx, "0", bv))); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t2, Z3_mk_numeral(ctx, "1", bv))); TEST_NO_OVERFLOW; TEST_NO_UNDERFLOW; Z3_solver_pop(ctx, s, 1); Z3_solver_push(ctx, s); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t1, Z3_mk_numeral(ctx, "0", bv))); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t2, max)); TEST_NO_OVERFLOW; TEST_NO_UNDERFLOW; Z3_solver_pop(ctx, s, 1); Z3_solver_push(ctx, s); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t1, Z3_mk_numeral(ctx, "1", bv))); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t2, max)); TEST_NO_OVERFLOW_IFF(bvsize == 1 && is_signed); TEST_NO_UNDERFLOW; Z3_solver_pop(ctx, s, 1); Z3_solver_push(ctx, s); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t1, Z3_mk_numeral(ctx, "1", bv))); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t2, min)); TEST_NO_OVERFLOW; TEST_NO_UNDERFLOW_IFF(! (bvsize == 1 && is_signed)); Z3_solver_pop(ctx, s, 1); if (is_signed) { Z3_solver_push(ctx, s); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t1, Z3_mk_numeral(ctx, "-1", bv))); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t2, min)); TEST_NO_OVERFLOW; TEST_UNDERFLOW; Z3_solver_pop(ctx, s, 1); } Z3_solver_push(ctx, s); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t1, min)); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t2, min)); TEST_NO_OVERFLOW; TEST_NO_UNDERFLOW_IFF(! is_signed); Z3_solver_pop(ctx, s, 1); Z3_solver_push(ctx, s); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t1, min)); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t2, max)); TEST_NO_OVERFLOW; TEST_NO_UNDERFLOW; Z3_solver_pop(ctx, s, 1); Z3_solver_push(ctx, s); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t1, max)); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t2, max)); TEST_NO_OVERFLOW_IFF(bvsize == 1 && is_signed); TEST_NO_UNDERFLOW; Z3_solver_pop(ctx, s, 1); Z3_solver_dec_ref(ctx, s); Z3_del_config(cfg); Z3_del_context(ctx); } void test_sub(unsigned bvsize, bool is_signed) { TRACE("no_overflow", tout << "test_sub: bvsize = " << bvsize << ", is_signed = " << is_signed << "\n";); Z3_config cfg = Z3_mk_config(); Z3_context ctx = Z3_mk_context(cfg); Z3_sort bv = Z3_mk_bv_sort(ctx, bvsize); Z3_solver s = Z3_mk_solver(ctx); Z3_solver_inc_ref(ctx, s); Z3_ast min = mk_min(ctx, bv, is_signed); Z3_ast max = mk_max(ctx, bv, is_signed); Z3_ast t1; Z3_ast t2; Z3_ast test_ovfl; Z3_ast test_udfl; t1 = Z3_mk_const(ctx, Z3_mk_string_symbol(ctx,"x"), bv); t2 = Z3_mk_const(ctx, Z3_mk_string_symbol(ctx,"y"), bv); test_ovfl = is_signed ? Z3_mk_bvsub_no_overflow(ctx, t1, t2) : NULL; test_udfl = Z3_mk_bvsub_no_underflow(ctx, t1, t2, is_signed); Z3_solver_push(ctx, s); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t1, Z3_mk_numeral(ctx, "0", bv))); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t2, Z3_mk_numeral(ctx, "1", bv))); TEST_NO_OVERFLOW_IFF(! (bvsize == 1 && is_signed)); TEST_NO_UNDERFLOW_IFF(is_signed); Z3_solver_pop(ctx, s, 1); Z3_solver_push(ctx, s); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t1, Z3_mk_numeral(ctx, "0", bv))); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t2, max)); TEST_NO_OVERFLOW; TEST_NO_UNDERFLOW_IFF(is_signed); Z3_solver_pop(ctx, s, 1); Z3_solver_push(ctx, s); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t1, Z3_mk_numeral(ctx, "1", bv))); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t2, max)); TEST_NO_OVERFLOW; TEST_NO_UNDERFLOW_IFF(bvsize == 1 || is_signed); Z3_solver_pop(ctx, s, 1); Z3_solver_push(ctx, s); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t1, max)); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t2, Z3_mk_numeral(ctx, "1", bv))); TEST_NO_OVERFLOW_IFF(! (bvsize == 1 && is_signed)); TEST_NO_UNDERFLOW; Z3_solver_pop(ctx, s, 1); Z3_solver_push(ctx, s); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t1, Z3_mk_numeral(ctx, "1", bv))); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t2, min)); TEST_NO_OVERFLOW_IFF(! (bvsize != 1 && is_signed)); TEST_NO_UNDERFLOW; Z3_solver_pop(ctx, s, 1); Z3_solver_push(ctx, s); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t1, min)); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t2, Z3_mk_numeral(ctx, "1", bv))); TEST_NO_OVERFLOW; TEST_NO_UNDERFLOW_IFF(bvsize == 1 && is_signed); Z3_solver_pop(ctx, s, 1); if (is_signed) { Z3_solver_push(ctx, s); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t1, Z3_mk_numeral(ctx, "-1", bv))); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t2, min)); TEST_NO_OVERFLOW; TEST_NO_UNDERFLOW; Z3_solver_pop(ctx, s, 1); Z3_solver_push(ctx, s); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t1, min)); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t2, Z3_mk_numeral(ctx, "-1", bv))); TEST_NO_OVERFLOW; TEST_NO_UNDERFLOW; Z3_solver_pop(ctx, s, 1); } Z3_solver_push(ctx, s); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t1, min)); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t2, min)); TEST_NO_OVERFLOW; TEST_NO_UNDERFLOW; Z3_solver_pop(ctx, s, 1); Z3_solver_push(ctx, s); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t1, min)); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t2, max)); TEST_NO_OVERFLOW; TEST_NO_UNDERFLOW_IFF(bvsize == 1 && is_signed); Z3_solver_pop(ctx, s, 1); Z3_solver_push(ctx, s); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t1, max)); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t2, min)); TEST_NO_OVERFLOW_IFF(! is_signed); TEST_NO_UNDERFLOW; Z3_solver_pop(ctx, s, 1); Z3_solver_push(ctx, s); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t1, max)); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t2, max)); TEST_NO_OVERFLOW; TEST_NO_UNDERFLOW; Z3_solver_pop(ctx, s, 1); Z3_solver_dec_ref(ctx, s); Z3_del_config(cfg); Z3_del_context(ctx); } void test_neg(unsigned bvsize) { TRACE("no_overflow", tout << "test_neg: bvsize = " << bvsize << "\n";); Z3_config cfg = Z3_mk_config(); Z3_context ctx = Z3_mk_context(cfg); Z3_sort bv = Z3_mk_bv_sort(ctx, bvsize); Z3_solver s = Z3_mk_solver(ctx); Z3_solver_inc_ref(ctx, s); Z3_ast min = mk_min(ctx, bv, /* is_signed = */ true); Z3_ast max = mk_max(ctx, bv, /* is_signed = */ true); Z3_ast t1; Z3_ast test_ovfl; t1 = Z3_mk_const(ctx, Z3_mk_string_symbol(ctx,"x"), bv); test_ovfl = Z3_mk_bvneg_no_overflow(ctx, t1); Z3_solver_push(ctx, s); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t1, Z3_mk_numeral(ctx, "0", bv))); TEST_NO_OVERFLOW; Z3_solver_pop(ctx, s, 1); Z3_solver_push(ctx, s); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t1, Z3_mk_numeral(ctx, "1", bv))); TEST_NO_OVERFLOW_IFF(bvsize != 1); Z3_solver_pop(ctx, s, 1); Z3_solver_push(ctx, s); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t1, Z3_mk_numeral(ctx, "-1", bv))); TEST_NO_OVERFLOW_IFF(bvsize != 1); Z3_solver_pop(ctx, s, 1); Z3_solver_push(ctx, s); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t1, max)); TEST_NO_OVERFLOW; Z3_solver_pop(ctx, s, 1); Z3_solver_push(ctx, s); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t1, min)); TEST_OVERFLOW; Z3_solver_pop(ctx, s, 1); Z3_solver_dec_ref(ctx, s); Z3_del_config(cfg); Z3_del_context(ctx); } void test_mul(unsigned bvsize, bool is_signed) { TRACE("no_overflow", tout << "test_mul: bvsize = " << bvsize << ", is_signed = " << is_signed << "\n";); Z3_config cfg = Z3_mk_config(); Z3_context ctx = Z3_mk_context(cfg); Z3_sort bv = Z3_mk_bv_sort(ctx, bvsize); Z3_solver s = Z3_mk_solver(ctx); Z3_solver_inc_ref(ctx, s); Z3_ast min = mk_min(ctx, bv, is_signed); Z3_ast max = mk_max(ctx, bv, is_signed); Z3_ast t1; Z3_ast t2; Z3_ast test_ovfl; Z3_ast test_udfl; t1 = Z3_mk_const(ctx, Z3_mk_string_symbol(ctx,"x"), bv); t2 = Z3_mk_const(ctx, Z3_mk_string_symbol(ctx,"y"), bv); test_ovfl = Z3_mk_bvmul_no_overflow(ctx, t1, t2, is_signed); test_udfl = is_signed ? Z3_mk_bvmul_no_underflow(ctx, t1, t2) : NULL; Z3_solver_push(ctx, s); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t1, Z3_mk_numeral(ctx, "1", bv))); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t2, Z3_mk_numeral(ctx, "1", bv))); TEST_NO_OVERFLOW_IFF(! (bvsize == 1 && is_signed)); TEST_NO_UNDERFLOW; Z3_solver_pop(ctx, s, 1); Z3_solver_push(ctx, s); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t1, Z3_mk_numeral(ctx, "0", bv))); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t2, max)); TEST_NO_OVERFLOW; TEST_NO_UNDERFLOW; Z3_solver_pop(ctx, s, 1); Z3_solver_push(ctx, s); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t1, min)); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t2, Z3_mk_numeral(ctx, "0", bv))); TEST_NO_OVERFLOW; TEST_NO_UNDERFLOW; Z3_solver_pop(ctx, s, 1); Z3_solver_push(ctx, s); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t1, max)); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t2, Z3_mk_numeral(ctx, "1", bv))); TEST_NO_OVERFLOW; TEST_NO_UNDERFLOW; Z3_solver_pop(ctx, s, 1); Z3_solver_push(ctx, s); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t1, Z3_mk_numeral(ctx, "1", bv))); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t2, min)); TEST_NO_OVERFLOW_IFF(! (bvsize == 1 && is_signed)); TEST_NO_UNDERFLOW; Z3_solver_pop(ctx, s, 1); if (is_signed) { Z3_solver_push(ctx, s); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t1, Z3_mk_numeral(ctx, "-1", bv))); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t2, min)); TEST_OVERFLOW; TEST_NO_UNDERFLOW; Z3_solver_pop(ctx, s, 1); Z3_solver_push(ctx, s); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t1, min)); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t2, Z3_mk_numeral(ctx, "-1", bv))); TEST_OVERFLOW; TEST_NO_UNDERFLOW; Z3_solver_pop(ctx, s, 1); Z3_solver_push(ctx, s); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t1, Z3_mk_numeral(ctx, "-1", bv))); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t2, max)); TEST_NO_OVERFLOW; TEST_NO_UNDERFLOW; Z3_solver_pop(ctx, s, 1); } Z3_solver_push(ctx, s); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t1, min)); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t2, min)); TEST_NO_OVERFLOW_IFF(! is_signed); TEST_NO_UNDERFLOW; Z3_solver_pop(ctx, s, 1); Z3_solver_push(ctx, s); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t1, min)); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t2, max)); TEST_NO_OVERFLOW; TEST_NO_UNDERFLOW_IFF(! (bvsize != 1 && is_signed)); Z3_solver_pop(ctx, s, 1); Z3_solver_push(ctx, s); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t1, max)); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t2, max)); TEST_NO_OVERFLOW_IFF(bvsize == 1); TEST_NO_UNDERFLOW; Z3_solver_pop(ctx, s, 1); Z3_solver_dec_ref(ctx, s); Z3_del_config(cfg); Z3_del_context(ctx); } void test_div(unsigned bvsize) { TRACE("no_overflow", tout << "test_div: bvsize = " << bvsize << "\n";); Z3_config cfg = Z3_mk_config(); Z3_context ctx = Z3_mk_context(cfg); Z3_sort bv = Z3_mk_bv_sort(ctx, bvsize); Z3_solver s = Z3_mk_solver(ctx); Z3_solver_inc_ref(ctx, s); Z3_ast min = mk_min(ctx, bv, /* is_signed = */ true); Z3_ast max = mk_max(ctx, bv, /* is_signed = */ true); Z3_ast t1; Z3_ast t2; Z3_ast test_ovfl; t1 = Z3_mk_const(ctx, Z3_mk_string_symbol(ctx,"x"), bv); t2 = Z3_mk_const(ctx, Z3_mk_string_symbol(ctx,"y"), bv); test_ovfl = Z3_mk_bvsdiv_no_overflow(ctx, t1, t2); Z3_solver_push(ctx, s); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t1, Z3_mk_numeral(ctx, "1", bv))); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t2, Z3_mk_numeral(ctx, "1", bv))); TEST_NO_OVERFLOW_IFF(bvsize != 1); Z3_solver_pop(ctx, s, 1); Z3_solver_push(ctx, s); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t1, Z3_mk_numeral(ctx, "0", bv))); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t2, Z3_mk_numeral(ctx, "1", bv))); TEST_NO_OVERFLOW; Z3_solver_pop(ctx, s, 1); Z3_solver_push(ctx, s); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t1, Z3_mk_numeral(ctx, "0", bv))); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t2, Z3_mk_numeral(ctx, "-1", bv))); TEST_NO_OVERFLOW; Z3_solver_pop(ctx, s, 1); Z3_solver_push(ctx, s); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t1, Z3_mk_numeral(ctx, "1", bv))); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t2, Z3_mk_numeral(ctx, "0", bv))); TEST_ANY_OVERFLOW; Z3_solver_pop(ctx, s, 1); Z3_solver_push(ctx, s); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t1, Z3_mk_numeral(ctx, "-1", bv))); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t2, Z3_mk_numeral(ctx, "0", bv))); TEST_ANY_OVERFLOW; Z3_solver_pop(ctx, s, 1); Z3_solver_push(ctx, s); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t1, Z3_mk_numeral(ctx, "0", bv))); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t2, Z3_mk_numeral(ctx, "0", bv))); TEST_ANY_OVERFLOW; Z3_solver_pop(ctx, s, 1); Z3_solver_push(ctx, s); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t1, min)); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t2, Z3_mk_numeral(ctx, "-1", bv))); TEST_OVERFLOW; Z3_solver_pop(ctx, s, 1); Z3_solver_push(ctx, s); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t1, min)); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t2, Z3_mk_numeral(ctx, "1", bv))); TEST_NO_OVERFLOW_IFF(bvsize != 1); Z3_solver_pop(ctx, s, 1); Z3_solver_push(ctx, s); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t1, min)); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t2, min)); TEST_NO_OVERFLOW_IFF(bvsize != 1); Z3_solver_pop(ctx, s, 1); Z3_solver_push(ctx, s); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t1, max)); Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t2, min)); TEST_NO_OVERFLOW; Z3_solver_pop(ctx, s, 1); Z3_solver_dec_ref(ctx, s); Z3_del_config(cfg); Z3_del_context(ctx); } typedef Z3_ast (Z3_API *NO_OVFL_ARITH_FUNC)(Z3_context ctx, Z3_ast t1, Z3_ast t2, bool is_signed); typedef Z3_ast (Z3_API *ARITH_FUNC)(Z3_context ctx, Z3_ast t1, Z3_ast t2); typedef enum { OVFL_FUNC, UDFL_FUNC } overflow_type; typedef struct { std::string name; NO_OVFL_ARITH_FUNC no_overflow_func; ARITH_FUNC func; overflow_type type; int extsize; // negative size indicates size of arguments should be doubled bool do_unsigned; bool non_zero; // second argument should not be null (for division) bool sign_compar; // whether signed comparison should be used even for unsigned operation } Equivalence_params; Z3_ast Z3_API Z3_mk_bvsdiv_no_overflow_wrapper(Z3_context ctx, Z3_ast t1, Z3_ast t2, bool is_signed) { return Z3_mk_bvsdiv_no_overflow(ctx, t1, t2); } Z3_ast Z3_API Z3_mk_bvneg_no_overflow_wrapper(Z3_context ctx, Z3_ast t1, Z3_ast t2, bool is_signed) { return Z3_mk_bvneg_no_overflow(ctx, t1); } Z3_ast Z3_API Z3_mk_bvneg_wrapper(Z3_context ctx, Z3_ast t1, Z3_ast t2) { return Z3_mk_bvneg(ctx, t1); } Z3_ast Z3_API Z3_mk_bvadd_no_underflow_wrapper(Z3_context ctx, Z3_ast t1, Z3_ast t2, bool is_signed) { return Z3_mk_bvadd_no_underflow(ctx, t1, t2); } Z3_ast Z3_API Z3_mk_bvsub_no_overflow_wrapper(Z3_context ctx, Z3_ast t1, Z3_ast t2, bool is_signed) { return Z3_mk_bvsub_no_overflow(ctx, t1, t2); } Z3_ast Z3_API Z3_mk_bvmul_no_underflow_wrapper(Z3_context ctx, Z3_ast t1, Z3_ast t2, bool is_signed) { return Z3_mk_bvmul_no_underflow(ctx, t1, t2); } void test_equiv(Equivalence_params params, unsigned bvsize, bool is_signed) { TRACE("no_overflow", tout << "test_" << params.name << "_equiv: bvsize = " << bvsize << ", is_signed = " << is_signed << "\n";); Z3_config cfg = Z3_mk_config(); Z3_context ctx = Z3_mk_context(cfg); Z3_sort bv = Z3_mk_bv_sort(ctx, bvsize); Z3_solver s = Z3_mk_solver(ctx); Z3_solver_inc_ref(ctx, s); Z3_ast min = mk_min(ctx, bv, is_signed); Z3_ast max = mk_max(ctx, bv, is_signed); Z3_ast t1 = Z3_mk_const(ctx, Z3_mk_string_symbol(ctx,"x"), bv); Z3_ast t2 = Z3_mk_const(ctx, Z3_mk_string_symbol(ctx,"y"), bv); Z3_ast real_test = (*params.no_overflow_func)(ctx, t1, t2, is_signed); Z3_ast cond = NULL; if (params.non_zero) { cond = Z3_mk_not(ctx, Z3_mk_eq(ctx, t2, Z3_mk_int(ctx, 0, bv))); } unsigned extsize = params.extsize < 0 ? bvsize : params.extsize; if (is_signed) { min = Z3_mk_sign_ext(ctx, extsize, min); max = Z3_mk_sign_ext(ctx, extsize, max); t1 = Z3_mk_sign_ext(ctx, extsize, t1); t2 = Z3_mk_sign_ext(ctx, extsize, t2); } else { min = Z3_mk_zero_ext(ctx, extsize, min); max = Z3_mk_zero_ext(ctx, extsize, max); t1 = Z3_mk_zero_ext(ctx, extsize, t1); t2 = Z3_mk_zero_ext(ctx, extsize, t2); } Z3_ast r = (*params.func)(ctx, t1, t2); Z3_ast check; if (is_signed) { check = (params.type == UDFL_FUNC) ? Z3_mk_bvsle(ctx, min, r) : Z3_mk_bvsle(ctx, r, max); } else { if (params.sign_compar) { // check with signed comparison for subtraction of unsigned check = (params.type == UDFL_FUNC) ? Z3_mk_bvsle(ctx, min, r) : Z3_mk_bvsle(ctx, r, max); } else { check = (params.type == UDFL_FUNC) ? Z3_mk_bvule(ctx, min, r) : Z3_mk_bvule(ctx, r, max); } } Z3_solver_push(ctx, s); Z3_ast equiv = Z3_mk_iff(ctx, real_test, check); if (cond != NULL) { equiv = Z3_mk_implies(ctx, cond, equiv); } Z3_solver_assert(ctx, s, Z3_mk_not(ctx, equiv)); ENSURE(Z3_solver_check(ctx, s) == Z3_L_FALSE); Z3_solver_pop(ctx, s, 1); Z3_solver_dec_ref(ctx, s); Z3_del_config(cfg); Z3_del_context(ctx); } //void debug_mul() { // // unsigned bvsize = 2; // bool is_signed = true; // // Z3_config cfg = Z3_mk_config(); // Z3_context ctx = Z3_mk_context(cfg); // Z3_sort bv = Z3_mk_bv_sort(ctx, bvsize); // // Z3_ast min = mk_min(ctx, bv, is_signed); // Z3_ast max = mk_max(ctx, bv, is_signed); // Z3_ast t1; // Z3_ast t2; // Z3_ast test_udfl; // // t1 = Z3_mk_const(ctx, Z3_mk_string_symbol(ctx,"x"), bv); // t2 = Z3_mk_const(ctx, Z3_mk_string_symbol(ctx,"y"), bv); // test_udfl = Z3_mk_bvmul_no_underflow(ctx, t1, t2); // // Z3_solver_push(ctx, s); // Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t1, Z3_mk_numeral(ctx, "1", bv))); // Z3_solver_assert(ctx, s, Z3_mk_eq(ctx, t2, Z3_mk_numeral(ctx, "1", bv))); // //TEST_NO_UNDERFLOW; // Z3_solver_assert(ctx, s, test_udfl); // ENSURE(Z3_check(ctx) == true); // Z3_solver_pop(ctx, s, 1); // // Z3_del_config(cfg); // Z3_del_context(ctx); //} #define BVSIZES 4 #define TESTNUM 3 #define EQUIV_BVSIZES 4 #define EQUIV_TESTNUM 8 typedef void (*TESTFUN)(unsigned bvsize, bool is_signed); void tst_no_overflow() { disable_debug("heap"); unsigned bvsizes[BVSIZES] = { 1, 16, 32, 42 }; TESTFUN tests[TESTNUM] = { test_add, test_sub, test_mul }; for (int i = 0; i < BVSIZES; ++i) { for (int j = 0; j < TESTNUM; ++j) { tests[j](bvsizes[i], /* is_signed = */ true); tests[j](bvsizes[i], /* is_signed = */ false); } test_neg(bvsizes[i]); test_div(bvsizes[i]); } unsigned equiv_bvsizes[EQUIV_BVSIZES] = { 1, 2, 7, 16 }; // before performing the bound test, arguments are extended by a few bits to prevent overflow: // * 1 is the default // * 2 is used for subtraction, so that 1 bit is used for the sign event for unsigned subtraction // * -1 to indicate that the bitsize should be doubled for multiplication Equivalence_params equiv_tests[EQUIV_TESTNUM] = { { "ovfl_add", Z3_mk_bvadd_no_overflow, Z3_mk_bvadd, OVFL_FUNC, 1, /* do_unsigned = */ true, /* non_zero = */ false, /* sign_compar = */ false }, { "udfl_add", Z3_mk_bvadd_no_underflow_wrapper, Z3_mk_bvadd, UDFL_FUNC, 1, /* do_unsigned = */ false, /* non_zero = */ false, /* sign_compar = */ false }, { "ovfl_sub", Z3_mk_bvsub_no_overflow_wrapper, Z3_mk_bvsub, OVFL_FUNC, 1, /* do_unsigned = */ false, /* non_zero = */ false, /* sign_compar = */ false }, { "udfl_sub", Z3_mk_bvsub_no_underflow, Z3_mk_bvsub, UDFL_FUNC, 2, /* do_unsigned = */ true, /* non_zero = */ false, /* sign_compar = */ true }, { "ovfl_mul", Z3_mk_bvmul_no_overflow, Z3_mk_bvmul, OVFL_FUNC, -1, /* do_unsigned = */ true, /* non_zero = */ false, /* sign_compar = */ false }, { "udfl_mul", Z3_mk_bvmul_no_underflow_wrapper, Z3_mk_bvmul, UDFL_FUNC, -1, /* do_unsigned = */ false, /* non_zero = */ false, /* sign_compar = */ false }, { "ovfl_div", Z3_mk_bvsdiv_no_overflow_wrapper, Z3_mk_bvsdiv, OVFL_FUNC, 1, /* do_unsigned = */ false, /* non_zero = */ true, /* sign_compar = */ false }, { "ovfl_neg", Z3_mk_bvneg_no_overflow_wrapper, Z3_mk_bvneg_wrapper, OVFL_FUNC, 1, /* do_unsigned = */ false, /* non_zero = */ false, /* sign_compar = */ false }, }; for (int i = 0; i < EQUIV_BVSIZES; ++i) { for (int j = 0; j < EQUIV_TESTNUM; ++j) { test_equiv(equiv_tests[j], equiv_bvsizes[i], /* is_signed = */ true); if (equiv_tests[j].do_unsigned) { test_equiv(equiv_tests[j], equiv_bvsizes[i], /* is_signed = */ false); } } } } #else void tst_no_overflow() { } #endif z3-z3-4.8.7/src/test/object_allocator.cpp000066400000000000000000000063701356505360400202150ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: object_allocator.cpp Abstract: Author: Leonardo de Moura (leonardo) 2010-06-09. Revision History: --*/ #include "util/rational.h" #include "util/object_allocator.h" struct cell { rational m_coeff; unsigned m_i; unsigned m_j; cell * m_next_row; cell * m_next_col; public: static unsigned g_num_allocated_cells; static unsigned g_num_deallocated_cells; static unsigned g_num_recycled_cells; cell() { g_num_allocated_cells++; } ~cell() { g_num_deallocated_cells++; } void reset() { m_coeff.reset(); g_num_recycled_cells++; } }; unsigned cell::g_num_allocated_cells = 0; unsigned cell::g_num_deallocated_cells = 0; unsigned cell::g_num_recycled_cells = 0; typedef object_allocator > cell_allocator; static void tst1() { cell_allocator m; cell * c1 = m.allocate(); /* cell * c2 = */ m.allocate(); c1->m_coeff = rational(10); m.recycle(c1); cell * c3 = m.allocate(); (void)c3; ENSURE(c3->m_coeff.is_zero()); } static void tst2() { cell_allocator m; ENSURE(m.capacity() >= 2); cell_allocator::worker_object_allocator m1 = m.get_worker_allocator(0); cell_allocator::worker_object_allocator m2 = m.get_worker_allocator(1); m.enable_concurrent(true); vector > object_coeff_pairs; unsigned num_resets = 0; for (unsigned i = 0; i < 100000; i++) { unsigned idx = rand() % 6; if (idx < 4) { cell * c; if (idx < 2) c = m1.allocate(); else c = m2.allocate(); ENSURE(c->m_coeff.is_zero()); int val = rand(); c->m_coeff = rational(val); object_coeff_pairs.push_back(std::make_pair(c, val)); } else { if (!object_coeff_pairs.empty()) { unsigned idx = rand() % object_coeff_pairs.size(); cell * c = object_coeff_pairs[idx].first; CTRACE("object_allocator", c->m_coeff != rational(object_coeff_pairs[idx].second), tout << c->m_coeff << " != " << rational(object_coeff_pairs[idx].second) << "\n";); ENSURE(c->m_coeff == rational(object_coeff_pairs[idx].second)); if (idx < 5) m1.recycle(c); else m2.recycle(c); object_coeff_pairs.erase(object_coeff_pairs.begin() + idx); } } if (rand() % 5000 == 0) { m.enable_concurrent(false); m.reset(); object_coeff_pairs.reset(); m.enable_concurrent(true); num_resets++; } } TRACE("object_allocator", tout << "num. resets: " << num_resets << "\n";); } void tst_object_allocator() { tst1(); tst2(); TRACE("object_allocator", tout << "num. allocated cells: " << cell::g_num_allocated_cells << "\nnum. deallocated cells: " << cell::g_num_deallocated_cells << "\nnum. recycled cells: " << cell::g_num_recycled_cells << "\n";); ENSURE(cell::g_num_allocated_cells == cell::g_num_deallocated_cells); } z3-z3-4.8.7/src/test/old_interval.cpp000066400000000000000000000154631356505360400173740ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: interval.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-12-10. Revision History: --*/ #include "smt/old_interval.h" static void tst1() { ext_numeral inf(true); ext_numeral minus_inf(false); ext_numeral zero(0); ENSURE(ext_numeral(10) + ext_numeral(3) == ext_numeral(13)); ENSURE(inf + zero == inf); ENSURE(minus_inf + zero == minus_inf); ENSURE(minus_inf + ext_numeral(3) == minus_inf); ENSURE(inf + inf == inf); ENSURE(minus_inf + minus_inf == minus_inf); ENSURE(minus_inf + ext_numeral(10) == minus_inf); ENSURE(minus_inf + ext_numeral(-10) == minus_inf); ENSURE(inf + ext_numeral(10) == inf); ENSURE(inf + ext_numeral(-10) == inf); ENSURE(ext_numeral(10) - ext_numeral(3) == ext_numeral(7)); ENSURE(inf - zero == inf); ENSURE(minus_inf - zero == minus_inf); ENSURE(minus_inf - ext_numeral(3) == minus_inf); ENSURE(inf - minus_inf == inf); ENSURE(minus_inf - inf == minus_inf); ENSURE(zero - minus_inf == inf); ENSURE(zero - inf == minus_inf); ENSURE(ext_numeral(-10) - minus_inf == inf); ENSURE(ext_numeral(10) - minus_inf == inf); ENSURE(ext_numeral(-10) - inf == minus_inf); ENSURE(ext_numeral(10) - inf == minus_inf); ENSURE(ext_numeral(10) * inf == inf); ENSURE(ext_numeral(-10) * inf == minus_inf); ENSURE(zero * inf == zero); ENSURE(zero * minus_inf == zero); ENSURE(zero * ext_numeral(10) == zero); ENSURE(ext_numeral(10) * ext_numeral(-20) == ext_numeral(-200)); ENSURE(ext_numeral(3) * ext_numeral(2) == ext_numeral(6)); ENSURE(inf * inf == inf); ENSURE(inf * minus_inf == minus_inf); ENSURE(minus_inf * minus_inf == inf); ENSURE(minus_inf * inf == minus_inf); ENSURE(minus_inf * ext_numeral(10) == minus_inf); ENSURE(minus_inf * ext_numeral(-10) == inf); ENSURE(minus_inf < inf); ENSURE(!(inf < minus_inf)); ENSURE(minus_inf < ext_numeral(10)); ENSURE(ext_numeral(-3) < inf); ENSURE(ext_numeral(-10) < ext_numeral(4)); ENSURE(ext_numeral(2) < ext_numeral(10)); ENSURE(!(inf < ext_numeral(30))); ENSURE(!(ext_numeral(10) < minus_inf)); ENSURE(!(inf < inf)); ENSURE(!(minus_inf < minus_inf)); ENSURE(!(zero < zero)); ENSURE(!(ext_numeral(10) < ext_numeral(10))); ENSURE(inf > minus_inf); ENSURE(inf > zero); ENSURE(inf > ext_numeral(10)); ENSURE(ext_numeral(10) > minus_inf); ENSURE(zero > minus_inf); ENSURE(!(zero > inf)); ENSURE(!(minus_inf > inf)); ENSURE(inf >= minus_inf); ENSURE(inf >= inf); ENSURE(minus_inf >= minus_inf); ENSURE(inf >= zero); ENSURE(zero >= minus_inf); ENSURE(inf <= inf); ENSURE(minus_inf <= minus_inf); ENSURE(zero <= inf); ENSURE(minus_inf <= zero); ext_numeral val(10); val.neg(); ENSURE(val == ext_numeral(-10)); val = inf; val.neg(); ENSURE(val == minus_inf); val.neg(); ENSURE(val == inf); ENSURE(minus_inf.sign()); ENSURE(!zero.sign()); ENSURE(!inf.sign()); ENSURE(ext_numeral(-10).sign()); ENSURE(!ext_numeral(10).sign()); ENSURE(inf.is_infinite()); ENSURE(minus_inf.is_infinite()); ENSURE(!zero.is_infinite()); ENSURE(!ext_numeral(10).is_infinite()); ENSURE(!inf.is_zero()); ENSURE(!minus_inf.is_zero()); ENSURE(zero.is_zero()); ENSURE(!ext_numeral(10).is_zero()); } class interval_tester { v_dependency_manager m; interval singleton(int i) { return interval(m, rational(i)); } interval all() { return interval(m); } interval l(int i, bool o = false, size_t idx = 0) { return interval(m, rational(i), o, true, idx == 0 ? nullptr : m.mk_leaf(reinterpret_cast(idx))); } interval r(int i, bool o = false, size_t idx = 0) { return interval(m, rational(i), o, false, idx == 0 ? nullptr : m.mk_leaf(reinterpret_cast(idx))); } interval b(int l, int u, bool lo = false, bool uo = false, size_t idx_l = 0, size_t idx_u = 0) { return interval(m, rational(l), lo, idx_l == 0 ? nullptr : m.mk_leaf(reinterpret_cast(idx_l)), rational(u), uo, idx_u == 0 ? nullptr : m.mk_leaf(reinterpret_cast(idx_u))); } void bugs() { interval r1 = l(0); interval r2 = b(-1, 0, false, true); r1 *= r2; } void tst1() { std::cerr << singleton(10) << "\n"; std::cerr << all() << "\n"; std::cerr << l(-10) << "\n"; std::cerr << r(10) << "\n"; std::cerr << l(-10, true) << "\n"; std::cerr << r(10, true) << "\n"; std::cerr << b(2, 10) << "\n"; std::cerr << wd(b(-5, 5, true, false, 1, 2) * b(-5, 5, false, true, 3, 4)) << "\n"; std::cerr << wd(l(2, false, 1) / b(2, 6, false, false, 2, 3)) << "\n"; std::cerr << wd(expt(b(-2, 3, true, false, 1, 2), 2)) << "\n"; std::cerr << wd(expt(b(-4, 3, true, false, 1, 2), 2)) << "\n"; std::cerr << wd(expt(b(2, 4, true, false, 1, 2), 2)) << "\n"; std::cerr << wd(expt(b(0, 3, true, false, 1, 2), 2)) << "\n"; std::cerr << wd(expt(b(-4, -2, true, false, 1, 2), 2)) << "\n"; std::cerr << b(2, 10, false, false, 1, 2) << " * " << l(10, false, 3).inv() << " = " << wd(b(2, 10, false, false, 1, 2) / l(10, false, 3)) << "\n"; std::cerr << b(-2, -1, false, true) << " * " << b(-3,0) << " = "; std::cerr.flush(); std::cerr << (b(-2, -1, false, true) * b(-3,0)) << "\n"; std::cerr << b(1, 2, true, false) << " * " << b(0,3) << " = "; std::cerr.flush(); std::cerr << (b(1, 2, true, false) * b(0,3)) << "\n"; std::cerr << b(1, 2, true, true) << " * " << b(-3,0) << " = "; std::cerr.flush(); std::cerr << (b(1, 2, true, true) * b(-3,0)) << "\n"; std::cerr << b(10,20) << " / " << b(0,1,true,false) << " = "; std::cerr.flush(); std::cerr << (b(10,20)/b(0,1,true,false)) << "\n"; std::cerr << (b(10,20)/b(0,2,true,false)) << "\n"; } public: void run() { bugs(); tst1(); } }; #include "util/basic_interval.h" #include "util/mpz.h" #include "util/scoped_numeral.h" static void tst2() { typedef basic_interval_manager mpzi_manager; typedef mpzi_manager::scoped_interval scoped_mpzi; unsynch_mpz_manager nm; mpzi_manager m(nm); scoped_mpzi x(m), y(m), z(m); m.set(x, mpz(1), mpz(2)); m.set(y, mpz(-2), mpz(3)); m.add(x, y, z); std::cout << "x: " << x << ", y: " << y << ", z: " << z << "\n"; ENSURE(nm.eq(z.lower(), mpz(-1))); ENSURE(nm.eq(z.upper(), mpz(5))); m.mul(x, y, z); std::cout << "x: " << x << ", y: " << y << ", z: " << z << "\n"; ENSURE(nm.eq(z.lower(), mpz(-4))); ENSURE(nm.eq(z.upper(), mpz(6))); } void tst_old_interval() { tst2(); enable_trace("interval_bug"); interval_tester tester; tester.run(); tst1(); } z3-z3-4.8.7/src/test/optional.cpp000066400000000000000000000024411356505360400165270ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: tst_optional.cpp Abstract: Test optional module Author: Leonardo de Moura (leonardo) 2006-09-29. Revision History: --*/ #include "util/trace.h" #include "util/debug.h" #include "util/memory_manager.h" #include "util/optional.h" static void tst1() { optional v; ENSURE(!v); ENSURE(v == false); v = 10; ENSURE(v); ENSURE(*v == 10); TRACE("optional", tout << sizeof(v) << "\n";); } struct OptFoo { int m_x; int m_y; OptFoo(int x, int y):m_x(x), m_y(y) { TRACE("optional", tout << "OptFoo created: " << m_x << " : " << m_y << "\n";); } ~OptFoo() { TRACE("optional", tout << "OptFoo deleted: " << m_x << " : " << m_y << "\n";); } }; static void tst2() { optional v; ENSURE(!v); v = OptFoo(10, 20); ENSURE(v->m_x == 10); ENSURE(v->m_y == 20); v = OptFoo(200, 300); ENSURE(v->m_x == 200); ENSURE(v->m_y == 300); TRACE("optional", tout << sizeof(v) << "\n";); } static void tst3() { optional v; ENSURE(!v); int x = 10; v = &x; ENSURE(v); ENSURE(*v == &x); TRACE("optional", tout << sizeof(v) << "\n";); ENSURE(*(*v) == 10); } void tst_optional() { tst1(); tst2(); tst3(); } z3-z3-4.8.7/src/test/parray.cpp000066400000000000000000000217271356505360400162100ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: parray.cpp Abstract: Test persistent arrays. Author: Leonardo de Moura (leonardo) 2011-02-23. Revision History: --*/ #include "util/parray.h" #include "util/small_object_allocator.h" #include "ast/ast.h" template struct int_parray_config { typedef int value; typedef dummy_value_manager value_manager; typedef small_object_allocator allocator; static const bool ref_count = false; static const bool preserve_roots = PRESERVE_ROOTS; static const unsigned max_trail_sz = 8; static const unsigned factor = 2; }; template static void tst1() { typedef parray_manager > int_parray_manager; typedef typename int_parray_manager::ref int_array; dummy_value_manager vm; small_object_allocator a; int_parray_manager m(vm, a); int_array a1; int_array a2; int_array a3; m.mk(a1); ENSURE(m.size(a1) == 0); m.push_back(a1, 10, a2); TRACE("parray", m.display_info(tout, a1); tout << "\n"; m.display_info(tout, a2); tout << "\n";); ENSURE(m.size(a1) == 0); ENSURE(m.size(a2) == 1); m.push_back(a1, 20, a1); m.push_back(a1, 30, a1); TRACE("parray", m.display_info(tout, a1); tout << "\n"; m.display_info(tout, a2); tout << "\n";); ENSURE(m.get(a1, 0) == 20); ENSURE(m.get(a1, 1) == 30); ENSURE(m.get(a2, 0) == 10); ENSURE(m.size(a1) == 2); ENSURE(m.size(a2) == 1); ENSURE(m.size(a3) == 0); m.push_back(a2, 100, a3); ENSURE(m.size(a3) == 2); ENSURE(m.get(a3, 0) == 10); ENSURE(m.get(a3, 1) == 100); TRACE("parray", m.display_info(tout, a1); tout << "\n"; m.display_info(tout, a2); tout << "\n"; m.display_info(tout, a3); tout << "\n";); m.push_back(a2, 50); ENSURE(m.get(a2, 0) == 10); ENSURE(m.get(a2, 1) == 50); ENSURE(m.size(a2) == 2); TRACE("parray", m.display_info(tout, a1); tout << "\n"; m.display_info(tout, a2); tout << "\n"; m.display_info(tout, a3); tout << "\n";); m.del(a1); m.del(a2); m.del(a3); } template static void tst2() { typedef parray_manager > int_parray_manager; typedef typename int_parray_manager::ref int_array; TRACE("parray", tout << "tst2\n";); dummy_value_manager vm; small_object_allocator a; int_parray_manager m(vm, a); int_array a1; int_array a2; for (unsigned i = 0; i < 100; i++) m.push_back(a1, i); ENSURE(m.size(a1) == 100); m.push_back(a1, 100, a2); for (unsigned i = 0; i < 10; i++) m.push_back(a2, i+101); TRACE("parray", m.display_info(tout, a1); tout << "\n"; m.display_info(tout, a2); tout << "\n";); ENSURE(m.get(a1, 0) == 0); TRACE("parray", m.display_info(tout, a1); tout << "\n"; m.display_info(tout, a2); tout << "\n";); for (unsigned i = 0; i < m.size(a1); i++) { ENSURE(static_cast(m.get(a1, i)) == i); } for (unsigned i = 0; i < m.size(a2); i++) { ENSURE(static_cast(m.get(a2, i)) == i); } TRACE("parray", m.display_info(tout, a1); tout << "\n"; m.display_info(tout, a2); tout << "\n";); m.unshare(a1); TRACE("parray", m.display_info(tout, a1); tout << "\n"; m.display_info(tout, a2); tout << "\n";); m.del(a1); m.del(a2); } template static void tst3() { typedef parray_manager > int_parray_manager; typedef typename int_parray_manager::ref int_array; TRACE("parray", tout << "tst3\n";); dummy_value_manager vm; small_object_allocator a; int_parray_manager m(vm, a); int_array a1; int_array a2; int_array a3; int_array a4; for (unsigned i = 0; i < 20; i++) m.push_back(a1, i); ENSURE(m.size(a1) == 20); m.set(a1, 0, 1, a2); for (unsigned i = 1; i < 20; i++) { if (i == 6) { m.copy(a2, a3); m.pop_back(a3); m.pop_back(a3); m.push_back(a3, 40); } m.set(a2, i, i+1); } m.pop_back(a2, a4); m.pop_back(a4); m.push_back(a4, 30); for (unsigned i = 0; i < 20; i++) { ENSURE(static_cast(m.get(a2, i)) == i+1); } TRACE("parray", m.display_info(tout, a1); tout << "\n"; m.display_info(tout, a2); tout << "\n"; m.display_info(tout, a3); tout << "\n"; m.display_info(tout, a4); tout << "\n"; ); ENSURE(m.get(a1, 10) == 10); TRACE("parray", tout << "after rerooting...\n"; m.display_info(tout, a1); tout << "\n"; m.display_info(tout, a2); tout << "\n"; m.display_info(tout, a3); tout << "\n"; m.display_info(tout, a4); tout << "\n"; ); ENSURE(m.size(a1) == 20); ENSURE(m.size(a2) == 20); ENSURE(m.size(a3) == 19); ENSURE(m.size(a4) == 19); for (unsigned i = 0; i < 20; i++) { ENSURE(static_cast(m.get(a1, i)) == i); ENSURE(static_cast(m.get(a2, i)) == i+1); ENSURE(i >= 18 || static_cast(m.get(a4, i)) == i+1); ENSURE(i >= 6 || static_cast(m.get(a3, i)) == i+1); ENSURE(!(6 <= i && i <= 17) || static_cast(m.get(a3, i)) == i); } ENSURE(m.get(a4, 18) == 30); ENSURE(m.get(a3, 18) == 40); TRACE("parray", tout << "after many gets...\n"; m.display_info(tout, a1); tout << "\n"; m.display_info(tout, a2); tout << "\n"; m.display_info(tout, a3); tout << "\n"; m.display_info(tout, a4); tout << "\n"; ); m.unshare(a1); TRACE("parray", tout << "after unshare...\n"; m.display_info(tout, a1); tout << "\n"; m.display_info(tout, a2); tout << "\n"; m.display_info(tout, a3); tout << "\n"; m.display_info(tout, a4); tout << "\n"; ); m.reroot(a4); TRACE("parray", tout << "after reroot...\n"; m.display_info(tout, a1); tout << "\n"; m.display_info(tout, a2); tout << "\n"; m.display_info(tout, a3); tout << "\n"; m.display_info(tout, a4); tout << "\n"; ); m.unshare(a2); TRACE("parray", tout << "after second unshare...\n"; m.display_info(tout, a1); tout << "\n"; m.display_info(tout, a2); tout << "\n"; m.display_info(tout, a3); tout << "\n"; m.display_info(tout, a4); tout << "\n"; ); m.del(a1); m.del(a2); m.del(a3); m.del(a4); } #if 0 // Moved to ast.cpp struct expr_array_config { typedef expr * value; typedef ast_manager value_manager; typedef small_object_allocator allocator; static const bool ref_count = true; static const bool preserve_roots = true; static const unsigned max_trail_sz = 8; static const unsigned factor = 2; }; typedef parray_manager expr_array_manager; typedef expr_array_manager::ref expr_array; static void tst4() { TRACE("parray", tout << "tst4\n";); ast_manager m; expr_array_manager m2(m, m.get_allocator()); expr_array a1; expr_array a2; expr * v0 = m.mk_var(0, m.mk_bool_sort()); expr * v1 = m.mk_var(1, m.mk_bool_sort()); expr * v2 = m.mk_var(2, m.mk_bool_sort()); expr * v3 = m.mk_var(3, m.mk_bool_sort()); m2.push_back(a1, v0); m2.push_back(a1, v1); m2.push_back(a1, v2, a2); m2.push_back(a1, v3); m2.push_back(a2, v2); m2.pop_back(a1); TRACE("parray", m2.display_info(tout, a1); tout << "\n"; m2.display_info(tout, a2); tout << "\n"; ); m2.reroot(a1); TRACE("parray", m2.display_info(tout, a1); tout << "\n"; m2.display_info(tout, a2); tout << "\n"; ); m2.del(a1); m2.del(a2); } #endif static void tst5() { ast_manager m; expr_array a1; expr_array a2; m.mk(a1); for (unsigned i = 0; i < 100; i++) { m.push_back(a1, m.mk_var(i, m.mk_bool_sort())); } unsigned long long mem = memory::get_max_used_memory(); std::cout << "max. heap size: " << static_cast(mem)/static_cast(1024*1024) << " Mbytes\n"; m.copy(a1, a2); for (unsigned i = 0; i < 1000000; i++) { m.set(a1, i % 100, m.mk_var(rand() % 100, m.mk_bool_sort())); } mem = memory::get_max_used_memory(); std::cout << "max. heap size: " << static_cast(mem)/static_cast(1024*1024) << " Mbytes\n"; m.del(a2); m.del(a1); } void tst_parray() { // enable_trace("parray_mem"); tst1(); tst2(); tst3(); tst1(); tst2(); tst3(); // tst4(); tst5(); } z3-z3-4.8.7/src/test/pb2bv.cpp000066400000000000000000000170211356505360400157150ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "util/trace.h" #include "util/vector.h" #include "ast/ast.h" #include "ast/ast_pp.h" #include "util/statistics.h" #include "ast/reg_decl_plugins.h" #include "ast/rewriter/pb2bv_rewriter.h" #include "smt/smt_kernel.h" #include "model/model_smt2_pp.h" #include "smt/params/smt_params.h" #include "ast/ast_util.h" #include "ast/pb_decl_plugin.h" #include "ast/rewriter/th_rewriter.h" #include "tactic/fd_solver/fd_solver.h" #include "solver/solver.h" #include "ast/arith_decl_plugin.h" static void test1() { ast_manager m; reg_decl_plugins(m); pb_util pb(m); params_ref p; pb2bv_rewriter rw(m, p); expr_ref_vector vars(m); unsigned N = 5; for (unsigned i = 0; i < N; ++i) { std::stringstream strm; strm << "b" << i; vars.push_back(m.mk_const(symbol(strm.str().c_str()), m.mk_bool_sort())); } for (unsigned k = 1; k <= N; ++k) { expr_ref fml(m), result(m); proof_ref proof(m); fml = pb.mk_at_least_k(vars.size(), vars.c_ptr(), k); rw(true, fml, result, proof); std::cout << fml << " |-> " << result << "\n"; } expr_ref_vector lemmas(m); rw.flush_side_constraints(lemmas); std::cout << lemmas << "\n"; } static void test_semantics(ast_manager& m, expr_ref_vector const& vars, vector const& coeffs, unsigned k, unsigned kind) { pb_util pb(m); params_ref p; pb2bv_rewriter rw(m, p); unsigned N = vars.size(); expr_ref fml1(m), fml2(m), result1(m), result2(m); proof_ref proof(m); expr_ref_vector lemmas(m); th_rewriter th_rw(m); switch (kind) { case 0: fml1 = pb.mk_ge(vars.size(), coeffs.c_ptr(), vars.c_ptr(), rational(k)); break; case 1: fml1 = pb.mk_le(vars.size(), coeffs.c_ptr(), vars.c_ptr(), rational(k)); break; default: fml1 = pb.mk_eq(vars.size(), coeffs.c_ptr(), vars.c_ptr(), rational(k)); break; } rw(true, fml1, result1, proof); rw.flush_side_constraints(lemmas); std::cout << "lemmas: " << lemmas << "\n"; std::cout << "simplified: " << result1 << "\n"; for (unsigned values = 0; values < static_cast(1 << N); ++values) { smt_params fp; smt::kernel solver(m, fp); expr_ref_vector tf(m); for (unsigned i = 0; i < N; ++i) { bool is_true = 0 != (values & (1 << i)); tf.push_back(is_true ? m.mk_true() : m.mk_false()); solver.assert_expr(is_true ? vars[i] : m.mk_not(vars[i])); } solver.assert_expr(lemmas); switch (kind) { case 0: fml2 = pb.mk_ge(tf.size(), coeffs.c_ptr(), tf.c_ptr(), rational(k)); break; case 1: fml2 = pb.mk_le(tf.size(), coeffs.c_ptr(), tf.c_ptr(), rational(k)); break; default: fml2 = pb.mk_eq(tf.size(), coeffs.c_ptr(), tf.c_ptr(), rational(k)); break; } std::cout << fml1 << " " << fml2 << "\n"; th_rw(fml2, result2, proof); ENSURE(m.is_true(result2) || m.is_false(result2)); lbool res = solver.check(); VERIFY(res == l_true); solver.assert_expr(m.is_true(result2) ? m.mk_not(result1) : result1.get()); res = solver.check(); if (res != l_false) { IF_VERBOSE(0, solver.display(verbose_stream()); verbose_stream() << vars << " k: " << k << " kind: " << kind << "\n"; for (auto const& c : coeffs) verbose_stream() << c << "\n"; ); } VERIFY(res == l_false); } } static void test_semantics(ast_manager& m, expr_ref_vector const& vars, vector const& coeffs, unsigned k) { test_semantics(m, vars, coeffs, k, 0); test_semantics(m, vars, coeffs, k, 1); test_semantics(m, vars, coeffs, k, 2); } static void test2() { ast_manager m; reg_decl_plugins(m); expr_ref_vector vars(m); unsigned N = 4; for (unsigned i = 0; i < N; ++i) { std::stringstream strm; strm << "b" << i; vars.push_back(m.mk_const(symbol(strm.str().c_str()), m.mk_bool_sort())); } for (unsigned coeff = 0; coeff < static_cast(1 << N); ++coeff) { vector coeffs; for (unsigned i = 0; i < N; ++i) { bool is_one = 0 != (coeff & (1 << i)); coeffs.push_back(is_one ? rational(1) : rational(2)); } for (unsigned i = 0; i <= N; ++i) { test_semantics(m, vars, coeffs, i); } } } static void test_solver_semantics(ast_manager& m, expr_ref_vector const& vars, vector const& coeffs, unsigned k, unsigned kind) { pb_util pb(m); params_ref p; unsigned N = vars.size(); expr_ref fml1(m), fml2(m), result1(m), result2(m); proof_ref proof(m); th_rewriter th_rw(m); switch (kind) { case 0: fml1 = pb.mk_ge(vars.size(), coeffs.c_ptr(), vars.c_ptr(), rational(k)); break; case 1: fml1 = pb.mk_le(vars.size(), coeffs.c_ptr(), vars.c_ptr(), rational(k)); break; default: fml1 = pb.mk_eq(vars.size(), coeffs.c_ptr(), vars.c_ptr(), rational(k)); break; } result1 = m.mk_fresh_const("xx", m.mk_bool_sort()); for (unsigned values = 0; values < static_cast(1 << N); ++values) { ref slv = mk_fd_solver(m, p); expr_ref_vector tf(m); for (unsigned i = 0; i < N; ++i) { bool is_true = 0 != (values & (1 << i)); tf.push_back(is_true ? m.mk_true() : m.mk_false()); slv->assert_expr(is_true ? vars[i] : m.mk_not(vars[i])); } slv->assert_expr(m.mk_eq(result1, fml1)); switch (kind) { case 0: fml2 = pb.mk_ge(tf.size(), coeffs.c_ptr(), tf.c_ptr(), rational(k)); break; case 1: fml2 = pb.mk_le(tf.size(), coeffs.c_ptr(), tf.c_ptr(), rational(k)); break; default: fml2 = pb.mk_eq(tf.size(), coeffs.c_ptr(), tf.c_ptr(), rational(k)); break; } std::cout << fml1 << " " << fml2 << "\n"; th_rw(fml2, result2, proof); ENSURE(m.is_true(result2) || m.is_false(result2)); lbool res = slv->check_sat(0,nullptr); VERIFY(res == l_true); slv->assert_expr(m.is_true(result2) ? m.mk_not(result1) : result1.get()); res = slv->check_sat(0,nullptr); VERIFY(res == l_false); } } static void test_solver_semantics(ast_manager& m, expr_ref_vector const& vars, vector const& coeffs, unsigned k) { test_solver_semantics(m, vars, coeffs, k, 0); test_solver_semantics(m, vars, coeffs, k, 1); test_solver_semantics(m, vars, coeffs, k, 2); } static void test3() { ast_manager m; reg_decl_plugins(m); expr_ref_vector vars(m); unsigned N = 4; for (unsigned i = 0; i < N; ++i) { std::stringstream strm; strm << "b" << i; vars.push_back(m.mk_const(symbol(strm.str().c_str()), m.mk_bool_sort())); } for (unsigned coeff = 0; coeff < static_cast(1 << N); ++coeff) { vector coeffs; for (unsigned i = 0; i < N; ++i) { bool is_one = 0 != (coeff & (1 << i)); coeffs.push_back(is_one ? rational(1) : rational(2)); } for (unsigned i = 0; i <= N; ++i) { test_solver_semantics(m, vars, coeffs, i); } } } static void test4() { ast_manager m; reg_decl_plugins(m); arith_util arith(m); expr_ref a(m.mk_const(symbol("a"), arith.mk_int()), m); expr_ref b(m.mk_const(symbol("b"), arith.mk_int()), m); expr_ref eq(m.mk_eq(a,b), m); std::cout << "is_atom: " << is_atom(m, eq) << "\n"; } void tst_pb2bv() { test1(); test2(); test3(); test4(); } z3-z3-4.8.7/src/test/permutation.cpp000066400000000000000000000043511356505360400172530ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: permutation.cpp Abstract: Simple abstraction for managing permutations. Author: Leonardo de Moura (leonardo) 2012-01-04 Revision History: --*/ #include "util/permutation.h" #include "util/util.h" #include "util/vector.h" void apply_permutation_copy(unsigned sz, unsigned const * src, unsigned const * p, unsigned * target) { for (unsigned i = 0; i < sz; i++) { target[i] = src[p[i]]; } } static void tst1(unsigned sz, unsigned num_tries, unsigned max = UINT_MAX) { #if 0 unsigned_vector data; unsigned_vector p; unsigned_vector new_data; data.resize(sz); p.resize(sz); new_data.resize(sz); random_gen g; for (unsigned i = 0; i < sz; i++) p[i] = i; // fill data with random numbers for (unsigned i = 0; i < sz; i++) data[i] = g() % max; for (unsigned k = 0; k < num_tries; k ++) { shuffle(p.size(), p.c_ptr(), g); // std::cout << "p: "; display(std::cout, p.begin(), p.end()); std::cout << "\n"; // std::cout << "data: "; display(std::cout, data.begin(), data.end()); std::cout << "\n"; apply_permutation_copy(sz, data.c_ptr(), p.c_ptr(), new_data.c_ptr()); apply_permutation(sz, data.c_ptr(), p.c_ptr()); // std::cout << "data: "; display(std::cout, data.begin(), data.end()); std::cout << "\n"; for (unsigned i = 0; i < 0; i++) ENSURE(data[i] == new_data[i]); } #endif } void tst_permutation() { tst1(10, 1000, 5); tst1(10, 1000, 1000); tst1(10, 1000, UINT_MAX); tst1(100, 1000, 33); tst1(100, 1000, 1000); tst1(100, 1000, UINT_MAX); tst1(1000, 1000, 121); tst1(1000, 1000, 1000); tst1(1000, 1000, UINT_MAX); tst1(33, 1000, 121); tst1(33, 1000, 1000); tst1(33, 1000, UINT_MAX); tst1(121, 1000, 121); tst1(121, 1000, 1000); tst1(121, 1000, UINT_MAX); for (unsigned i = 0; i < 1000; i++) { tst1(1000, 2, 333); tst1(1000, 2, 10000); tst1(1000, 2, UINT_MAX); } random_gen g; for (unsigned i = 0; i < 100000; i++) { unsigned sz = (g() % 131) + 1; tst1(sz, 1, sz*2); tst1(sz, 1, UINT_MAX); tst1(sz, 1, sz/2 + 1); } } z3-z3-4.8.7/src/test/polynomial.cpp000066400000000000000000002347051356505360400170770ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: polynomial.cpp Abstract: Goodies for creating and handling polynomials. Author: Leonardo (leonardo) 2011-11-15 Notes: --*/ #if !defined(__clang__) #include "math/polynomial/polynomial.h" #include "math/polynomial/polynomial_var2value.h" #include "math/polynomial/polynomial_cache.h" #include "math/polynomial/linear_eq_solver.h" #include "util/rlimit.h" static void tst1() { std::cout << "\n----- Basic testing -------\n"; reslimit rl; polynomial::numeral_manager nm; polynomial::manager m(rl, nm); polynomial_ref x0(m); polynomial_ref x1(m); polynomial_ref x2(m); x0 = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); x2 = m.mk_polynomial(m.mk_var()); std::cout << x0 << " " << x1 << " " << x2 << "\n"; polynomial_ref p(m); p = (x0^3) + x1*x0 + 2; std::cout << p << "\n"; std::cout << "max_var(p): " << max_var(p) << "\n"; ENSURE(max_var(p) == 1); std::cout << (2*x2 - x1*x0) << "\n"; std::cout << (p + (2*x2 - x1*x0)) << "\n"; std::cout << (p*p + 2*x2) << "\n"; std::cout << derivative(p*p + 2*x2, 0) << "\n"; polynomial_ref q(m); q = (x0^4) + x0 + 1; std::cout << "q(x): " << q << "\n"; std::cout << "q(y): " << compose_y(q, 2) << "\n"; std::cout << "q(x-y): " << compose_x_minus_y(q, 2) << "\n"; q = (x0 - 1)*(x0 - 2)*(x0 - 1)*(x0 + 2); std::cout << "q: " << q << "\n"; polynomial_ref s(m); s = (x0 - 1)*((x0 + 3)^2); std::cout << "s: " << s << "\n"; } static void tst_pseudo_div(polynomial_ref const & A, polynomial_ref const & B, polynomial::var x) { reslimit rl; polynomial::manager & m = A.m(); std::cout << "---- Pseudo-division test ----\n"; std::cout << "A: " << A << "\n"; std::cout << "B: " << B << "\n"; std::cout << "x: " << x << "\n"; polynomial_ref Q(m); polynomial_ref R(m); unsigned d; Q = pseudo_division(A, B, x, d, R); std::cout << "d: " << d << "\n"; std::cout << "Q: " << Q << "\n"; std::cout << "R: " << R << "\n"; polynomial_ref l_B(m); l_B = coeff(B, x, degree(B, x)); std::cout << "l_B: " << l_B << "\n"; polynomial_ref l_B_d(m); l_B_d = l_B^d; polynomial_ref t(m); std::cout << "l_B^d: " << l_B_d << "\n"; std::cout << "Q * B + R: " << Q * B + R << "\n"; std::cout << "l_B_d * A: " << l_B_d * A << "\n"; ENSURE(eq((Q * B + R), (l_B_d * A))); } static void tst2() { reslimit rl; polynomial::numeral_manager nm; polynomial::manager m(rl, nm); polynomial_ref x0(m); polynomial_ref x1(m); polynomial_ref x2(m); x0 = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); x2 = m.mk_polynomial(m.mk_var()); polynomial_ref p(m); polynomial_ref q(m); p = ((x0 - 1)^2)*x2 + (x1^2)*((x2 - 2)^2) + 1; q = (x0 - 1)*x2 + (x1^3)*(x2 - 2) + (x0 - 2)*(x1 - 2) + 10; tst_pseudo_div(p, q, 0); } static void tst3() { reslimit rl; polynomial::numeral_manager nm; polynomial::manager m(rl, nm); polynomial_ref x0(m); polynomial_ref x1(m); x0 = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); polynomial_ref p(m); polynomial_ref q(m); p = (x1^2) + (x0^2) - 1; q = (x1*x0) - 1; tst_pseudo_div(p, q, 1); } static void tst4() { std::cout << "---- Testing renaming/reordering ----\n"; reslimit rl; polynomial::numeral_manager nm; polynomial::manager m(rl, nm); polynomial_ref x0(m); polynomial_ref x1(m); polynomial_ref x2(m); x0 = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); x2 = m.mk_polynomial(m.mk_var()); polynomial_ref p1 = x2 + ((x0 - 1)^2)*x1 + (x2^3) + 10; polynomial_ref p2 = x0*x1*x2 + x1*(x2^3) + ((x0 - 2)^2); std::cout << "p1: " << p1 << "\n"; std::cout << "p2: " << p2 << "\n"; polynomial::var new_order[3] = { 2, 0, 1 }; m.rename(3, new_order); std::cout << "----- x0 -> x2, x1 -> x0, x2 -> x1 \n"; std::cout << "p1: " << p1 << "\n"; std::cout << "p2: " << p2 << "\n"; } static void tst_quasi_resultant(polynomial_ref const & p, polynomial_ref const & q, polynomial::var x) { std::cout << "---- Testing quasi-resultants ---- \n"; std::cout << "p : " << p << "\n"; std::cout << "q : " << q << "\n"; std::cout << "x : " << x << "\n--->\n"; std::cout << quasi_resultant(p, q, x) << "\n"; } static void tst5() { reslimit rl; polynomial::numeral_manager nm; polynomial::manager m(rl, nm); polynomial_ref x0(m); polynomial_ref x1(m); polynomial_ref x2(m); x0 = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); x2 = m.mk_polynomial(m.mk_var()); polynomial_ref p(m); polynomial_ref q(m); p = ((x0 - x1)^2) - 2; q = (x1^2) - 3; // sqrt(2) + sqrt(3) must be a root of the quasi-resultant tst_quasi_resultant(p, q, 1); } static void tst6() { reslimit rl; polynomial::numeral_manager nm; polynomial::manager m(rl, nm); polynomial_ref x0(m); polynomial_ref x1(m); polynomial_ref x2(m); x0 = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); polynomial_ref p(m); polynomial_ref q(m); p = (x0 - 2)*(x0 - 3)*(x0 + 2); std::cout << "p(x0): " << p << "\n"; std::cout << "p(-x0): " << compose_minus_x(p) << "\n"; std::cout << "x^3*p(1/x0): " << compose_1_div_x(p) << "\n"; std::cout << "p(x0 - x1): " << compose_x_minus_y(p, 1) << "\n"; std::cout << "x1^3*p(x0/x1): " << compose_x_div_y(p, 1) << "\n"; } static void tst7() { reslimit rl; polynomial::numeral_manager nm; polynomial::manager m(rl, nm); polynomial_ref x0(m); polynomial_ref x1(m); polynomial_ref x2(m); x0 = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); x2 = m.mk_polynomial(m.mk_var()); polynomial_ref p(m); polynomial_ref q1(m); polynomial_ref q2(m); p = (x0 - x1)*(x2 - 1); q1 = (x0^2) - 2; q2 = (x1^2) - 2; polynomial_ref r(m); r = quasi_resultant(p, q1, 0); std::cout << "1) r: " << r << "\n"; r = quasi_resultant(r, q2, 1); std::cout << "2) r: " << r << "\n"; } static void tst8() { reslimit rl; polynomial::numeral_manager nm; polynomial::manager m(rl, nm); polynomial_ref x0(m); polynomial_ref x1(m); polynomial_ref x2(m); x0 = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); x2 = m.mk_polynomial(m.mk_var()); polynomial_ref p(m); polynomial_ref sqrt2(m); polynomial_ref sqrt3(m); p = x2 - (x0*x1 - (x0^2) + 1); sqrt3 = (x0^2) - 3; sqrt2 = (x1^2) - 2; polynomial_ref r(m); r = quasi_resultant(p, sqrt2, 1); r = quasi_resultant(r, sqrt3, 0); std::cout << "r: " << r << "\n"; } static void tst9() { reslimit rl; polynomial::numeral_manager nm; polynomial::manager m(rl, nm); polynomial_ref x0(m); polynomial_ref x1(m); polynomial_ref x2(m); x0 = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); x2 = m.mk_polynomial(m.mk_var()); polynomial_ref p(m); polynomial_ref sqrt2(m); p = ((x0^3) - 1)*(x1^2) - 1; sqrt2 = ((x0^2) - 2)*(x0 - 2); // added garbage to polynomial // sqrt2 = (x0^2) - 2; // added garbage to polynomial // p(sqrt(2), x1) has the roots -0.7395 and 0.7395 polynomial_ref r(m); r = quasi_resultant(p, sqrt2, 0); std::cout << "p: " << p << "\n"; std::cout << "sqrt2: " << sqrt2 << "\n"; std::cout << "r: " << r << "\n"; // r contains the roots -0.7395 and 0.7395, plus garbage roots: 0, -0.3779, 0.3779 polynomial_ref q(m); q = x2 - (((x0^3) - 1)*(x1^2) - 1); std::cout << "q: " << q << "\n"; polynomial_ref r2(m); TRACE("polynomial", tout << "QUASI_RESULTANT: q, sqrt2.....\n";); r2 = quasi_resultant(q, sqrt2, 0); // TRACE("polynomial", tout << "QUASI_RESULTANT: sqrt2, q.....\n";); // std::cout << "r2: " << r2 << "\n"; // r2 = quasi_resultant(sqrt2, q, 0); // std::cout << "r2: " << r2 << "\n"; // return; std::cout << "r2: " << r2 << "\n"; r2 = normalize(quasi_resultant(r2, r, 1)); std::cout << "r2: " << r2 << "\n"; polynomial_ref_vector seq(m); r2 = normalize(quasi_resultant(sqrt2, q, 0)); // sturm_seq(r2, seq); std::cout << "r2:\n" << r2 << "\n"; } static void tst10() { reslimit rl; polynomial::numeral_manager nm; polynomial::manager m(rl, nm); polynomial_ref x0(m); polynomial_ref x1(m); polynomial_ref x2(m); x0 = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); x2 = m.mk_polynomial(m.mk_var()); polynomial_ref A(m); polynomial_ref B(m); polynomial_ref g(m); polynomial_ref h(m); polynomial_ref R(m); A = x2 - (((x0^3) - 1)*(x1^2) - 1); B = ((x0^2) - 2)*(x0 - 2); std::cout << "A: " << A << "\nB: " << B << "\n"; unsigned d; R = pseudo_remainder(A, B, 0, d); std::cout << "R: " << R << "\n"; // second iteration std::cout << "second iteration...\n"; A = B; B = R; g = coeff(A, 0, degree(A, 0)); std::cout << "A: " << A << "\nB: " << B << "\ng: " << g << "\n"; R = pseudo_remainder(A, B, 0, d); std::cout << "R: " << R << "\n"; // third iteration std::cout << "third iteration...\n"; A = B; B = R; g = coeff(A, 0, degree(A, 0)); std::cout << "A: " << A << "\nB: " << B << "\ng: " << g << "\n"; R = pseudo_remainder(A, B, 0, d); std::cout << "R: " << R << "\n"; } static void tst11() { reslimit rl; polynomial::numeral_manager nm; polynomial::manager m(rl, nm); polynomial_ref x0(m); polynomial_ref x1(m); polynomial_ref x2(m); polynomial_ref x3(m); x0 = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); x2 = m.mk_polynomial(m.mk_var()); x3 = m.mk_polynomial(m.mk_var()); polynomial_ref p(m); polynomial_ref q(m); q = ((x1^2) + 1)*(x2 + 1); p = (x3 + 1)*q; polynomial_ref d(m); d = exact_div(p, q); std::cout << "p: " << p << "\nq: " << q << "\nd: " << d << "\n"; ENSURE(eq(q * d, p)); q = ((x1^3) + x1 + 1)*((x2^2) + x2 + x2 + 1)*((x3^2) + 2); p = (x1 + (x3^2) + x3 + x2 + (x2^2) + 1)*((x1^3) + x1 + 1)*((x2^2) + x2 + x2 + 1)*((x3^2) + 2); d = exact_div(p, q); std::cout << "p: " << p << "\nq: " << q << "\nd: " << d << "\n"; ENSURE(eq(q * d, p)); } static void tst_discriminant(polynomial_ref const & p, polynomial::var x, polynomial_ref const & expected) { polynomial::manager & m = p.m(); polynomial_ref r(m); r = discriminant(p, x); std::cout << "r: " << r << "\n"; std::cout << "expected: " << expected << "\n"; ENSURE(eq(r, expected)); m.lex_sort(r); std::cout << "r (sorted): " << r << "\n"; } static void tst_discriminant(polynomial_ref const & p, polynomial_ref const & expected) { tst_discriminant(p, max_var(p), expected); } static void tst_discriminant() { reslimit rl; polynomial::numeral_manager nm; polynomial::manager m(rl, nm); polynomial_ref a(m); polynomial_ref b(m); polynomial_ref c(m); polynomial_ref d(m); polynomial_ref x(m); a = m.mk_polynomial(m.mk_var()); b = m.mk_polynomial(m.mk_var()); c = m.mk_polynomial(m.mk_var()); d = m.mk_polynomial(m.mk_var()); x = m.mk_polynomial(m.mk_var()); tst_discriminant(a*(x^2) + b*x + c, (b^2) - 4*a*c); tst_discriminant(a*(x^3) + b*(x^2) + c*x + d, (b^2)*(c^2) - 4*a*(c^3) - 4*(b^3)*d + 18*a*b*c*d - 27*(a^2)*(d^2)); tst_discriminant(a*(x^3) + b*(x^2) + c*(x^2) + d, -4*(b^3)*d - 12*(b^2)*c*d - 12*b*(c^2)*d - 4*(c^3)*d - 27*(a^2)*(d^2)); tst_discriminant(a*(x^3) + b*(x^2) + c*(x^2) + d, -4*(b^3)*d - 12*(b^2)*c*d - 12*b*(c^2)*d - 4*(c^3)*d - 27*(a^2)*(d^2)); tst_discriminant(a*(x^3) + (b^2)*d*(x^2) + c*(x^2) + d, -4*(b^6)*(d^4) - 12*(b^4)*c*(d^3) - 12*(b^2)*(c^2)*(d^2) - 4*(c^3)*d - 27*(a^2)*(d^2)); tst_discriminant(a*(x^4) + b*(x^2) + c, 16*a*(b^4)*c - 128*(a^2)*(b^2)*(c^2) + 256*(a^3)*(c^3)); polynomial_ref one(m); one = m.mk_const(rational(1)); tst_discriminant(x, one); tst_discriminant(3*x, one); polynomial_ref zero(m); zero = m.mk_zero(); tst_discriminant(one, zero); tst_discriminant(a*(x^7) + b, -823543*(a^6)*(b^6)); tst_discriminant(((a^2)+(b^2)+c)*(x^4) + (d + a*b)*x + a, -27*(a^8)*(b^4) - 54*(a^6)*(b^6) - 27*(a^4)*(b^8) - 54*(a^6)*(b^4)*c - 54*(a^4)*(b^6)*c - 108*(a^7)*(b^3)*d - 216*(a^5)*(b^5)*d - 108*(a^3)*(b^7)*d - 27*(a^4)*(b^4)*(c^2) - 216*(a^5)*(b^3)*c*d - 216*(a^3)*(b^5)*c*d - 162*(a^6)*(b^2)*(d^2) - 324*(a^4)*(b^4)*(d^2) - 162*(a^2)*(b^6)*(d^2) + 256*(a^9) + 768*(a^7)*(b^2) + 768*(a^5)*(b^4) + 256*(a^3)*(b^6) - 108*(a^3)*(b^3)*(c^2)*d - 324*(a^4)*(b^2)*c*(d^2) - 324*(a^2)*(b^4)*c*(d^2) - 108*(a^5)*b*(d^3) - 216*(a^3)*(b^3)*(d^3) - 108*a*(b^5)*(d^3) + 768*(a^7)*c + 1536*(a^5)*(b^2)*c + 768*(a^3)*(b^4)*c - 162*(a^2)*(b^2)*(c^2)*(d^2) - 216*(a^3)*b*c*(d^3) - 216*a*(b^3)*c*(d^3) - 27*(a^4)*(d^4) - 54*(a^2)*(b^2)*(d^4) - 27*(b^4)*(d^4) + 768*(a^5)*(c^2) + 768*(a^3)*(b^2)*(c^2) - 108*a*b*(c^2)*(d^3) - 54*(a^2)*c*(d^4) - 54*(b^2)*c*(d^4) + 256*(a^3)*(c^3) - 27*(c^2)*(d^4)); tst_discriminant((x^5) + a*(x^2) + a, 108*(a^6) + 3125*(a^4)); tst_discriminant((x^5) + (a*b)*(x^2) + a, 108*(a^6)*(b^5) + 3125*(a^4)); tst_discriminant((x^5) + (a*b*c)*(x^2) + a, 108*(a^6)*(b^5)*(c^5) + 3125*(a^4)); tst_discriminant((x^5) + (a*b*c + d)*(x^2) + a, 108*(a^6)*(b^5)*(c^5) + 540*(a^5)*(b^4)*(c^4)*d + 1080*(a^4)*(b^3)*(c^3)*(d^2) + 1080*(a^3)*(b^2)*(c^2)*(d^3) + 540*(a^2)*b*c*(d^4) + 108*a*(d^5) + 3125*(a^4)); tst_discriminant((x^4) + a*(x^2) + (a + c)*x + (c^2), 16*(a^4)*(c^2) - 128*(a^2)*(c^4) + 256*(c^6) - 4*(a^5) - 8*(a^4)*c + 140*(a^3)*(c^2) + 288*(a^2)*(c^3) + 144*a*(c^4) - 27*(a^4) - 108*(a^3)*c - 162*(a^2)*(c^2) - 108*a*(c^3) - 27*(c^4)); tst_discriminant((x^4) + (a + b)*(x^2) + (a + c)*x, -4*(a^5) - 12*(a^4)*b - 12*(a^3)*(b^2) - 4*(a^2)*(b^3) - 8*(a^4)*c - 24*(a^3)*b*c - 24*(a^2)*(b^2)*c - 8*a*(b^3)*c - 4*(a^3)*(c^2) - 12*(a^2)*b*(c^2) - 12*a*(b^2)*(c^2) - 4*(b^3)*(c^2) - 27*(a^4) - 108*(a^3)*c - 162*(a^2)*(c^2) - 108*a*(c^3) - 27*(c^4)); tst_discriminant((x^4) + (a + c)*x + (c^2), 256*(c^6) - 27*(a^4) - 108*(a^3)*c - 162*(a^2)*(c^2) - 108*a*(c^3) - 27*(c^4) ); tst_discriminant((x^4) + (a + b)*(x^2) + (a + c)*x + (c^2), 16*(a^4)*(c^2) + 64*(a^3)*b*(c^2) + 96*(a^2)*(b^2)*(c^2) + 64*a*(b^3)*(c^2) + 16*(b^4)*(c^2) - 128*(a^2)*(c^4) - 256*a*b*(c^4) - 128*(b^2)*(c^4) + 256*(c^6) - 4*(a^5) - 12*(a^4)*b - 12*(a^3)*(b^2) - 4*(a^2)*(b^3) - 8*(a^4)*c - 24*(a^3)*b*c - 24*(a^2)*(b^2)*c - 8*a*(b^3)*c + 140*(a^3)*(c^2) + 132*(a^2)*b*(c^2) - 12*a*(b^2)*(c^2) - 4*(b^3)*(c^2) + 288*(a^2)*(c^3) + 288*a*b*(c^3) + 144*a*(c^4) + 144*b*(c^4) - 27*(a^4) - 108*(a^3)*c - 162*(a^2)*(c^2) - 108*a*(c^3) - 27*(c^4)); tst_discriminant((a + c)*(x^3) + (a + b)*(x^2) + (a + c)*x + (c^2), -27*(a^2)*(c^4) - 54*a*(c^5) - 27*(c^6) + 14*(a^3)*(c^2) + 6*(a^2)*b*(c^2) - 12*a*(b^2)*(c^2) - 4*(b^3)*(c^2) + 36*(a^2)*(c^3) + 36*a*b*(c^3) + 18*a*(c^4) + 18*b*(c^4) - 3*(a^4) + 2*(a^3)*b + (a^2)*(b^2) - 14*(a^3)*c + 4*(a^2)*b*c + 2*a*(b^2)*c - 23*(a^2)*(c^2) + 2*a*b*(c^2) + (b^2)*(c^2) - 16*a*(c^3) - 4*(c^4)); tst_discriminant((a^4) - 2*(a^3) + (a^2) - 3*(b^2)*a + 2*(b^4), max_var(a), 2048*(b^12) - 4608*(b^10) + 37*(b^8) + 12*(b^6)); tst_discriminant((a^4) - 2*(a^3) + (a^2) - 3*(b^2)*a + 2*(b^4), max_var(b), 2048*(a^12) - 12288*(a^11) + 26112*(a^10) - 22528*(a^9) + 5664*(a^8) + 960*(a^7) + 32*(a^6)); tst_discriminant((x^4) + a*(x^2) + b*x + c, -4*(a^3)*(b^2) + 16*(a^4)*c - 27*(b^4) + 144*a*(b^2)*c - 128*(a^2)*(c^2) + 256*(c^3)); tst_discriminant((((a-1)^2) + a*b + ((b-1)^2) - 1)*(x^3) + (a*b)*(x^2) + ((a^2) - (b^2))*x + c*a, -4*(a^8) - 4*(a^7)*b + 9*(a^6)*(b^2) + 12*(a^5)*(b^3) - 2*(a^4)*(b^4) - 12*(a^3)*(b^5) - 7*(a^2)*(b^6) + 4*a*(b^7) + 4*(b^8) + 18*(a^6)*b*c + 18*(a^5)*(b^2)*c - 4*(a^4)*(b^3)*c - 18*(a^3)*(b^4)*c - 18*(a^2)*(b^5)*c - 27*(a^6)*(c^2) - 54*(a^5)*b*(c^2) - 81*(a^4)*(b^2)*(c^2) - 54*(a^3)*(b^3)*(c^2) - 27*(a^2)*(b^4)*(c^2) + 8*(a^7) + 8*(a^6)*b - 24*(a^5)*(b^2) - 24*(a^4)*(b^3) + 24*(a^3)*(b^4) + 24*(a^2)*(b^5) - 8*a*(b^6) - 8*(b^7) - 36*(a^5)*b*c - 36*(a^4)*(b^2)*c + 36*(a^3)*(b^3)*c + 36*(a^2)*(b^4)*c + 108*(a^5)*(c^2) + 216*(a^4)*b*(c^2) + 216*(a^3)*(b^2)*(c^2) + 108*(a^2)*(b^3)*(c^2) - 4*(a^6) + 12*(a^4)*(b^2) - 12*(a^2)*(b^4) + 4*(b^6) + 18*(a^4)*b*c - 18*(a^2)*(b^3)*c - 162*(a^4)*(c^2) - 270*(a^3)*b*(c^2) - 162*(a^2)*(b^2)*(c^2) + 108*(a^3)*(c^2) + 108*(a^2)*b*(c^2) - 27*(a^2)*(c^2)); } static void tst_resultant(polynomial_ref const & p, polynomial_ref const & q, polynomial::var x, polynomial_ref const & expected) { polynomial::manager & m = p.m(); polynomial_ref r(m); std::cout << "----------------\n"; std::cout << "p: " << p << "\n"; std::cout << "q: " << q << std::endl; r = resultant(p, q, x); std::cout << "r: " << r << "\n"; std::cout << "expected: " << expected << "\n"; if (degree(p, x) > 0 && degree(q, x) > 0) std::cout << "quasi-resultant: " << quasi_resultant(p, q, x) << "\n"; ENSURE(eq(r, expected)); m.lex_sort(r); std::cout << "r (sorted): " << r << "\n"; } static void tst_resultant(polynomial_ref const & p, polynomial_ref const & q, polynomial_ref const & expected) { tst_resultant(p, q, max_var(p), expected); } static void tst_resultant() { reslimit rl; polynomial::numeral_manager nm; polynomial::manager m(rl, nm); polynomial_ref a(m); polynomial_ref b(m); polynomial_ref c(m); polynomial_ref d(m); polynomial_ref x(m); a = m.mk_polynomial(m.mk_var()); b = m.mk_polynomial(m.mk_var()); c = m.mk_polynomial(m.mk_var()); d = m.mk_polynomial(m.mk_var()); x = m.mk_polynomial(m.mk_var()); tst_resultant((((a-1)^2) + a*b + ((b-1)^2) - 1)*(x^3) + (a*b)*(x^2) + ((a^2) - (b^2))*x + c*a, a*b*(x^2) - (a^2) - (b^2), -4*(a^9)*b - (a^10) - 9*(a^8)*(b^2) - 11*(a^7)*(b^3) - 14*(a^6)*(b^4) - 10*(a^5)*(b^5) - 10*(a^4)*(b^6) - 3*(a^3)*(b^7) - 5*(a^2)*(b^8) - (b^10) + 2*(a^6)*(b^3)*c + 2*(a^4)*(b^5)*c + (a^5)*(b^3)*(c^2) + 4*(a^9) + 12*(a^8)*b + 24*(a^7)*(b^2) + 32*(a^6)*(b^3) + 40*(a^5)*(b^4) + 32*(a^4)*(b^5) + 24*(a^3)*(b^6) + 16*(a^2)*(b^7) + 4*a*(b^8) + 4*(b^9) - 6*(a^8) - 12*(a^7)*b - 24*(a^6)*(b^2) - 32*(a^5)*(b^3) - 36*(a^4)*(b^4) - 28*(a^3)*(b^5) - 24*(a^2)*(b^6) - 8*a*(b^7) - 6*(b^8) + 4*(a^7) + 4*(a^6)*b + 12*(a^5)*(b^2) + 12*(a^4)*(b^3) + 12*(a^3)*(b^4) + 12*(a^2)*(b^5) + 4*a*(b^6) + 4*(b^7) - (a^6) - 3*(a^4)*(b^2) - 3*(a^2)*(b^4) - (b^6)); tst_resultant(a*(x^5) + b, c*x + d, a*(d^5) - b*(c^5)); tst_resultant(a*(x^5) + 3*(c + d)*(x^2) + 2*b, c*x + d, -2*b*(c^5) - 3*(c^4)*(d^2) - 3*(c^3)*(d^3) + a*(d^5)); tst_resultant(c*x + d, a*(x^5) + 3*(c + d)*(x^2) + 2*b, 2*b*(c^5) + 3*(c^4)*(d^2) + 3*(c^3)*(d^3) - a*(d^5)); tst_resultant((x^2) - (a^3)*(x^2) + b + 1, -49*(x^10) + 21*(x^8) + 5*(x^6) - (x^4), (a^18)*(b^4) + 4*(a^18)*(b^3) + 6*(a^18)*(b^2) - 10*(a^15)*(b^5) + 4*(a^18)*b - 56*(a^15)*(b^4) + (a^18) - 124*(a^15)*(b^3) - 17*(a^12)*(b^6) - 136*(a^15)*(b^2) - 52*(a^12)*(b^5) - 74*(a^15)*b + 10*(a^12)*(b^4) + 308*(a^9)*(b^7) - 16*(a^15) + 220*(a^12)*(b^3) + 2224*(a^9)*(b^6) + 335*(a^12)*(b^2) + 6776*(a^9)*(b^5) - 49*(a^6)*(b^8) + 208*(a^12)*b + 11280*(a^9)*(b^4) - 1316*(a^6)*(b^7) + 48*(a^12) + 11060*(a^9)*(b^3) - 7942*(a^6)*(b^6) - 2058*(a^3)*(b^9) + 6368*(a^9)*(b^2) - 22660*(a^6)*(b^5) - 18424*(a^3)*(b^8) + 1984*(a^9)*b - 36785*(a^6)*(b^4) - 72380*(a^3)*(b^7) + 2401*(b^10) + 256*(a^9) - 36064*(a^6)*(b^3) - 163592*(a^3)*(b^6) + 26068*(b^9) - 21216*(a^6)*(b^2) - 234058*(a^3)*(b^5) + 126518*(b^8) - 6912*(a^6)*b - 219344*(a^3)*(b^4) + 361508*(b^7) - 960*(a^6) - 134208*(a^3)*(b^3) + 673537*(b^6) - 51456*(a^3)*(b^2) + 855056*(b^5) - 11136*(a^3)*b + 749104*(b^4) - 1024*(a^3) + 447232*(b^3) + 174144*(b^2) + 39936*b + 4096); tst_resultant(((a - x)^2) + 2, (x^5) - x - 1, (a^10) + 10*(a^8) + 38*(a^6) - 2*(a^5) + 100*(a^4) + 40*(a^3) + 121*(a^2) - 38*a + 19); tst_resultant(c - (((a^3) - 1)*(b^2) - 1), ((a^2) - 2)*(a - 2), max_var(a), -49*(b^6) + 21*(b^4)*c + 21*(b^4) + 5*(b^2)*(c^2) + 10*(b^2)*c - (c^3) + 5*(b^2) - 3*(c^2) - 3*c - 1); tst_resultant(-49*(b^6) + 21*(b^4)*c + 21*(b^4) + 5*(b^2)*(c^2) + 10*(b^2)*c - (c^3) + 5*(b^2) - 3*(c^2) - 3*c - 1, (7*(b^4) - 2*(b^2) - 1), max_var(b), 117649*(c^12) + 1075648*(c^11) + 1651888*(c^10) - 12293120*(c^9) - 46560192*(c^8) - 9834496*(c^7) + 186855424*(c^6) + 314703872*(c^5) + 157351936*(c^4)); tst_resultant(144*(b^2) + 96*(a^2)*b + 9*(a^4) + 105*(a^2) + 70*a - 98, a*(b^2) + 6*a*b + (a^3) + 9*a, max_var(b), 81*(a^10) + 3330*(a^8) + 1260*(a^7) - 37395*(a^6) - 45780*(a^5) - 32096*(a^4) + 167720*(a^3) + 1435204*(a^2)); tst_resultant(144*(b^2) + 96*(a^2)*b + 9*(a^4) + 105*(a^2) + 70*a - 98, a*(b^2) + 6*a*b + (a^3) + 9*a, max_var(a), 11664*(b^10) + 31104*(b^9) - 119394*(b^8) - 1550448*(b^7) - 2167524*(b^6) + 7622712*(b^5) + 46082070*(b^4) + 46959720*(b^3) - 9774152*(b^2) - 35007168*b - 13984208); polynomial_ref n1(m); polynomial_ref n2(m); polynomial_ref one(m); n1 = m.mk_const(rational(10)); n2 = m.mk_const(rational(100)); one = m.mk_const(rational(1)); tst_resultant(n1, (x^2) + 2*x + 1, max_var(x), n2); tst_resultant(n1, 2*x + 1, max_var(x), n1); tst_resultant(n1, n2, 0, one); tst_resultant((x^2) + 2*x + 1, n1, max_var(x), n2); tst_resultant(2*x + 1, n1, max_var(x), n1); tst_resultant((x^2) + 8*x + 1, n1, max_var(x), n2); } static void tst_compose() { reslimit rl; polynomial::numeral_manager nm; polynomial::manager m(rl, nm); polynomial_ref x0(m); polynomial_ref x1(m); x0 = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); polynomial_ref p(m); p = (x0^3) - x0 + 3; std::cout << "p: " << p << "\np(x - y): " << compose_x_minus_y(p, 1) << "\np(x + y): " << compose_x_plus_y(p, 1) << "\np(x - x): " << compose_x_minus_y(p, 0) << "\np(x + x): " << compose_x_plus_y(p, 0) << "\n"; ENSURE(eq(compose_x_minus_y(p, 1), (x0^3) - 3*(x0^2)*x1 + 3*x0*(x1^2) - (x1^3) - x0 + x1 + 3)); ENSURE(eq(compose_x_plus_y(p, 1), (x0^3) + 3*(x0^2)*x1 + 3*x0*(x1^2) + (x1^3) - x0 - x1 + 3)); } void tst_prem() { reslimit rl; polynomial::numeral_manager nm; polynomial::manager m(rl, nm); polynomial_ref x(m); polynomial_ref y(m); x = m.mk_polynomial(m.mk_var()); y = m.mk_polynomial(m.mk_var()); polynomial_ref p(m); polynomial_ref q(m); p = (x^2) - 2; q = y*(x^3); std::cout << "p: " << p << "\n"; std::cout << "q: " << q << "\n"; // unsigned d; std::cout << "srem: " << exact_pseudo_remainder(q, p, 0) << "\n"; } void tst_sqrt() { reslimit rl; polynomial::numeral_manager nm; polynomial::manager m(rl, nm); polynomial_ref x(m); polynomial_ref y(m); x = m.mk_polynomial(m.mk_var()); y = m.mk_polynomial(m.mk_var()); polynomial_ref p(m); p = (4*x*y + 3*(x^2)*y + (y^2) + 3)^4; polynomial_ref q(m); VERIFY(sqrt(p, q)); ENSURE(eq(p, q*q)); std::cout << "p: " << p << "\n"; std::cout << "q: " << q << "\n"; p = p - 1; ENSURE(!sqrt(p, q)); } static void tst_content(polynomial_ref const & p, polynomial::var x, polynomial_ref const & expected) { std::cout << "---------------\n"; std::cout << "p: " << p << std::endl; std::cout << "content(p): " << content(p, x) << std::endl; std::cout << "expected: " << expected << std::endl; ENSURE(eq(content(p, x), expected)); } static void tst_primitive(polynomial_ref const & p, polynomial::var x, polynomial_ref const & expected) { std::cout << "---------------\n"; std::cout << "p: " << p << std::endl; std::cout << "primitive(p): " << primitive(p, x) << std::endl; std::cout << "expected: " << expected << std::endl; ENSURE(eq(primitive(p, x), expected)); } static void tst_gcd(polynomial_ref const & p, polynomial_ref const & q, polynomial_ref const & expected) { std::cout << "---------------\n"; std::cout << "p: " << p << std::endl; std::cout << "q: " << q << std::endl; polynomial_ref r(p.m()); r = gcd(p, q); std::cout << "gcd(p, q): " << r << std::endl; std::cout << "expected: " << expected << std::endl; ENSURE(eq(r, expected)); } static void tst_gcd() { reslimit rl; polynomial::numeral_manager nm; polynomial::manager m(rl, nm); polynomial_ref x0(m); polynomial_ref x1(m); polynomial_ref x2(m); polynomial_ref x3(m); x0 = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); x2 = m.mk_polynomial(m.mk_var()); x3 = m.mk_polynomial(m.mk_var()); polynomial_ref three(m); three = m.mk_const(mpz(3)); std::cout << "tst_gcd\n======================\n"; tst_gcd(((x0^2) + x0*x1 + 1)*(x2*x2 + x3 + 2)*(x3*x1 + 2)*(x3*x1*x1 + x1*x2 + 1), ((x0^2) + x0*x1 + 1)*(x3*x1*x1 + x1*x2 + 1)*(x3*x1 + x1*x2 + 17), ((x0^2) + x0*x1 + 1)*(x3*x1*x1 + x1*x2 + 1)); tst_gcd((-1)*((x0^2) + x0*x1 + 1)*(x2*x2 + x3 + 2)*(x3*x1 + 2)*(x3*x1*x1 + x1*x2 + 1), ((x0^2) + x0*x1 + 1)*(x3*x1*x1 + x1*x2 + 1)*(x3*x1 + x1*x2 + 17), ((x0^2) + x0*x1 + 1)*(x3*x1*x1 + x1*x2 + 1)); tst_gcd(((x0^2) + x0*x1 + 1)*(x2*x2 + x3 + 2)*(x3*x1 + 2)*(x3*x1*x1 + x1*x2 + 1), (-1)*((x0^2) + x0*x1 + 1)*(x3*x1*x1 + x1*x2 + 1)*(x3*x1 + x1*x2 + 17), ((x0^2) + x0*x1 + 1)*(x3*x1*x1 + x1*x2 + 1)); tst_gcd((-1)*((x0^2) + x0*x1 + 1)*(x2*x2 + x3 + 2)*(x3*x1 + 2)*(x3*x1*x1 + x1*x2 + 1), (-1)*((x0^2) + x0*x1 + 1)*(x3*x1*x1 + x1*x2 + 1)*(x3*x1 + x1*x2 + 17), ((x0^2) + x0*x1 + 1)*(x3*x1*x1 + x1*x2 + 1)); tst_gcd(21*(x0 + 1), 6*x0^2, three); tst_content(x0*x1 + x0, 1, x0); tst_primitive(x0*x1 + x0, 1, x1 + 1); tst_primitive((x1^2) + x0*x1 + x0, 1, (x1^2) + x0*x1 + x0); tst_primitive((x0 + 1)*(2*x1) + 1, 1, (x0 + 1)*(2*x1) + 1); tst_primitive((x0 + 1)*(2*x1) + (x0^2)*(x0 + 1), 1, 2*x1 + (x0^2)); tst_primitive((x0 + 1)*(x2 + 1)*(x2^2)*(x0 + 1)*(x1^2) + (x0 + 1)*(x2^2)*x1 + (x0+1)*(x0+1), 1, (x2 + 1)*(x2^2)*(x0 + 1)*(x1^2) + (x2^2)*x1 + (x0+1)); tst_primitive((x0 + (x3^2))*(x2 + x3 + 1)*(x2^2)*(x1^2) + (x0 + (x3^2))*(x2 + x3 + 1)*x1 + (x0 + (x3^2))*(x2 + x3 + 1)*(x3^2), 1, (x2^2)*(x1^2) + x1 + (x3^2)); tst_content((x0 + (x3^2))*(x2 + x3 + 1)*(x2^2)*(x1^2) + (x0 + (x3^2))*(x2 + x3 + 1)*x1 + (x0 + (x3^2))*(x2 + x3 + 1)*(x3^2), 1, (x0 + (x3^2))*(x2 + x3 + 1)); tst_primitive(4*(x0 + (x3^2))*(x2 + x3 + 1)*(x2^2)*(x1^2) + 2*(x0 + (x3^2))*(x2 + x3 + 1)*x1 + 4*(x0 + (x3^2))*(x2 + x3 + 1)*(x3^2), 1, 2*(x2^2)*(x1^2) + x1 + 2*(x3^2)); tst_gcd(63*((x0^2) + x0*x1 + 1)*(x2*x2 + x3 + 2)*(x3*x1 + 2)*(x3*x1*x1 + x1*x2 + 1), 14*((x0^2) + x0*x1 + 1)*(x3*x1*x1 + x1*x2 + 1)*(x3*x1 + x1*x2 + 17), 7*((x0^2) + x0*x1 + 1)*(x3*x1*x1 + x1*x2 + 1)); } static void tst_psc(polynomial_ref const & p, polynomial_ref const & q, polynomial::var x, polynomial_ref const & first, polynomial_ref const & second) { polynomial::manager & m = p.m(); polynomial_ref_vector S(m); std::cout << "---------" << std::endl; std::cout << "p: " << p << std::endl; std::cout << "q: " << q << std::endl; m.psc_chain(p, q, x, S); unsigned sz = S.size(); for (unsigned i = 0; i < sz; i++) { std::cout << "S_" << i << ": " << polynomial_ref(S.get(i), m) << std::endl; } if (sz > 0) { ENSURE(m.eq(S.get(0), first) || m.eq(S.get(0), neg(first))); } if (sz > 1) { ENSURE(m.eq(S.get(1), second) || m.eq(S.get(1), neg(second))); } if (sz > 0) { polynomial_ref Res(m); Res = resultant(p, q, x); ENSURE(m.eq(Res, S.get(0)) || m.eq(S.get(0), neg(Res))); } } #if 0 static void tst_psc_perf(polynomial_ref const & p, polynomial_ref const & q, polynomial::var x) { polynomial::manager & m = p.m(); polynomial_ref_vector S(m); std::cout << "---------" << std::endl; std::cout << "p: " << p << std::endl; std::cout << "q: " << q << std::endl; m.psc_chain(p, q, x, S); unsigned sz = S.size(); for (unsigned i = 0; i < sz; i++) { std::cout << "S_" << i << ": " << m.size(S.get(i)) << std::endl; // polynomial_ref(S.get(i), m) << std::endl; } } #endif static void tst_psc() { reslimit rl; polynomial::numeral_manager nm; polynomial::manager m(rl, nm); polynomial_ref x0(m); polynomial_ref x1(m); polynomial_ref x2(m); polynomial_ref x3(m); polynomial_ref x4(m); polynomial_ref x5(m), x6(m), x7(m), x8(m), x9(m), x10(m); x0 = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); x2 = m.mk_polynomial(m.mk_var()); x3 = m.mk_polynomial(m.mk_var()); x4 = m.mk_polynomial(m.mk_var()); x5 = m.mk_polynomial(m.mk_var()); x6 = m.mk_polynomial(m.mk_var()); x7 = m.mk_polynomial(m.mk_var()); x8 = m.mk_polynomial(m.mk_var()); x9 = m.mk_polynomial(m.mk_var()); x10 = m.mk_polynomial(m.mk_var()); tst_psc(x0*(x1^2) + (x0 + 1)*x1 + 2, x0*x1 + 3, 1, 6*x0 - (x0^2), x0); tst_psc(x0*(x1^4) + (x0 + 1)*(x1^3) + 2, x0*(x1^3) + 3, 1, 72*(x0^3) - (x0^4) - 27*(x0^2) - 27*(x0), 9*(x0^3)); polynomial_ref & a = x0; polynomial_ref & b = x1; polynomial_ref & c = x2; polynomial_ref & d = x3; polynomial_ref & e = x4; polynomial_ref & f = x5; polynomial_ref & x = x9; tst_psc((x^4) + a*(x^2) + b*x + c, 4*(x^3) + 2*a*x + b, 9, 16*(a^4)*c - 4*(a^3)*(b^2) - 128*(a^2)*(c^2) + 144*a*(b^2)*c - 27*(b^4) + 256*(c^3), 8*(a^3) - 32*a*c + 36*(b^2)); polynomial_ref & y = x10; tst_psc(((y^2) + 6)*(x - 1) - y*((x^2) + 1), ((x^2) + 6)*(y - 1) - x*((y^2) + 1), 10, 2*(x^6) - 22*(x^5) + 102*(x^4) - 274*(x^3) + 488*(x^2) - 552*x + 288, 5*x - (x^2) - 6 ); tst_psc(((y^3) + 6)*(x - 1) - y*((x^3) + 1), ((x^3) + 6)*(y - 1) - x*((y^3) + 1), 10, 3*(x^11) - 3*(x^10) - 37*(x^9) + 99*(x^8) + 51*(x^7) - 621*(x^6) + 1089*(x^5) - 39*(x^4) - 3106*(x^3) + 5868*(x^2) - 4968*x + 1728, (x^6) - 10*(x^4) + 12*(x^3) + 25*(x^2) - 60*x + 36); polynomial_ref p = (x^6) + a * (x^3) + b; polynomial_ref q = (x^6) + c * (x^3) + d; tst_psc(p, q, 9, (b^6) - 3*a*(b^5)*c + 3*(a^2)*(b^4)*(c^2) + 3*(b^5)*(c^2) - (a^3)*(b^3)*(c^3) - 6*a*(b^4)*(c^3) + 3*(a^2)*(b^3)*(c^4) + 3*(b^4)*(c^4) - 3*a*(b^3)*(c^5) + (b^3)*(c^6) + 3*(a^2)*(b^4)*d - 6*(b^5)*d - 6*(a^3)*(b^3)*c*d + 9*a*(b^4)*c*d + 3*(a^4)*(b^2)*(c^2)*d + 6*(a^2)*(b^3)*(c^2)*d - 12*(b^4)*(c^2)*d - 9*(a^3)*(b^2)*(c^3)*d + 6*a*(b^3)*(c^3)*d + 9*(a^2)*(b^2)*(c^4)*d - 6*(b^3)*(c^4)*d - 3*a*(b^2)*(c^5)*d + 3*(a^4)*(b^2)*(d^2) - 12*(a^2)*(b^3)*(d^2) + 15*(b^4)*(d^2) - 3*(a^5)*b*c*(d^2) + 6*(a^3)*(b^2)*c*(d^2) - 6*a*(b^3)*c*(d^2) + 9*(a^4)*b*(c^2)*(d^2) - 18*(a^2)*(b^2)*(c^2)*(d^2) + 18*(b^3)*(c^2)*(d^2) - 9*(a^3)*b*(c^3)*(d^2) + 6*a*(b^2)*(c^3)*(d^2) + 3*(a^2)*b*(c^4)*(d^2) + 3*(b^2)*(c^4)*(d^2) + (a^6)*(d^3) - 6*(a^4)*b*(d^3) + 18*(a^2)*(b^2)*(d^3) - 20*(b^3)*(d^3) - 3*(a^5)*c*(d^3) + 6*(a^3)*b*c*(d^3) - 6*a*(b^2)*c*(d^3) + 3*(a^4)*(c^2)*(d^3) + 6*(a^2)*b*(c^2)*(d^3) - 12*(b^2)*(c^2)*(d^3) - (a^3)*(c^3)*(d^3) - 6*a*b*(c^3)*(d^3) + 3*(a^4)*(d^4) - 12*(a^2)*b*(d^4) + 15*(b^2)*(d^4) - 6*(a^3)*c*(d^4) + 9*a*b*c*(d^4) + 3*(a^2)*(c^2)*(d^4) + 3*b*(c^2)*(d^4) + 3*(a^2)*(d^5) - 6*b*(d^5) - 3*a*c*(d^5) + (d^6), 3*(a^2)*c - (a^3) - 3*a*(c^2) + (c^3) ); tst_psc(x, a * x + b * c + d - e, 9, b*c + d - e, a); polynomial_ref zero(m); zero = m.mk_zero(); tst_psc( a*d*x + a*c*f + a*e - b*a, d*x + c*f + e - b, 9, zero, zero); #if 0 tst_psc_perf((x^7) + a*(x^3) + b*(x^2) + c*x + d, (x^7) + e*(x^3) + f*(x^2) + g*x + h, 9); tst_psc_perf((x^15) + a * (x^10) + b, (x^15) + c * (x^10) + d, 9); tst_psc_perf((y^5) + a * (y^4) + b * (y^3) + c * (y^2) + d * y + e, (y^5) + f * (y^4) + g * (y^3) + h * (y^2) + i * y + x, 10); #endif } static void tst_vars(polynomial_ref const & p, unsigned sz, polynomial::var * xs) { polynomial::var_vector r; p.m().vars(p, r); std::cout << "---------------\n"; std::cout << "p: " << p << "\nvars: "; for (unsigned i = 0; i < r.size(); i++) { std::cout << r[i] << " "; } std::cout << std::endl; ENSURE(r.size() == sz); std::sort(r.begin(), r.end()); std::sort(xs, xs + sz); for (unsigned i = 0; i < r.size(); i++) { ENSURE(r[i] == xs[i]); } } static void tst_vars() { polynomial::numeral_manager nm; reslimit rl; polynomial::manager m(rl, nm); polynomial_ref x0(m); polynomial_ref x1(m); polynomial_ref x2(m); polynomial_ref x3(m); polynomial_ref x4(m); x0 = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); x2 = m.mk_polynomial(m.mk_var()); x3 = m.mk_polynomial(m.mk_var()); x4 = m.mk_polynomial(m.mk_var()); polynomial::var s023[3] = {0, 2, 3}; polynomial::var s14[2] = {1, 4}; polynomial::var s012[3] = {0, 1, 2}; polynomial::var s3[1] = {3}; polynomial::var s01234[5] = {0, 1, 2, 3, 4}; tst_vars((x0 + 1)*((x0^2) + (x3^2))*(x2*x3), 3, s023); tst_vars((x0 + x2)*((x0^2) + (x3^2))*(x2*x3), 3, s023); tst_vars(((x1 + x4 + 1)^5), 2, s14); tst_vars(((x1 + x4*x2 + 1)^4) + x0 + (x3^2), 5, s01234); tst_vars((x3 + 1)^5, 1, s3); tst_vars(x0*x1*x2, 3, s012); tst_vars(x0*x1*x2 + 1, 3, s012); } static void tst_sqf(polynomial_ref const & p, polynomial_ref const & expected) { polynomial_ref r(p.m()); std::cout << "---------------\n"; std::cout << "p: " << p << std::endl; r = square_free(p); std::cout << "sqf(p): " << r << std::endl; std::cout << "expected: " << expected << std::endl; ENSURE(is_square_free(r)); ENSURE(!eq(r, p) || is_square_free(p)); ENSURE(eq(expected, r)); } static void tst_sqf() { polynomial::numeral_manager nm; reslimit rl; polynomial::manager m(rl, nm); polynomial_ref x0(m); polynomial_ref x1(m); polynomial_ref x2(m); polynomial_ref x3(m); x0 = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); x2 = m.mk_polynomial(m.mk_var()); x3 = m.mk_polynomial(m.mk_var()); tst_sqf(((x0 + x1)^2)*((x0^2) - 3)*((x2*x2 + x3 + 1)^3), (x0 + x1)*((x0^2) - 3)*(x2*x2 + x3 + 1)); tst_sqf((x0 + x1)*(x0 - x1), (x0 + x1)*(x0 - x1)); tst_sqf(((x0 + x1)^3)*(x0 - x1), (x0 + x1)*(x0 - x1)); polynomial_ref c1(m); c1 = m.mk_const(rational(3)); tst_sqf(c1, c1); polynomial_ref z(m); z = m.mk_zero(); tst_sqf(z, z); tst_sqf((x0 + x1 + x2 + x3)^5, (x0 + x1 + x2 + x3)); tst_sqf(((x0 + x1 + x2 + x3)^5) + 1, ((x0 + x1 + x2 + x3)^5) + 1); } static void tst_substitute(polynomial_ref const & p, polynomial::var x1, mpz const & v1, polynomial::var x2, mpz const & v2, polynomial_ref const & expected) { polynomial::numeral_manager & nm = p.m().m(); polynomial::var xs[2] = { x1, x2 }; scoped_mpz_vector vs(nm); vs.push_back(v1); vs.push_back(v2); std::cout << "---------------\n"; std::cout << "p: " << p << std::endl; polynomial_ref r(p.m()); r = p.m().substitute(p, 2, xs, vs.c_ptr()); std::cout << "r: " << r << std::endl; std::cout << "expected: " << expected << std::endl; ENSURE(eq(r, expected)); p.m().lex_sort(r); std::cout << "r (sorted): " << r << std::endl; } static void tst_substitute() { polynomial::numeral_manager nm; reslimit rl; polynomial::manager m(rl, nm); polynomial_ref x0(m); polynomial_ref x1(m); polynomial_ref x2(m); polynomial_ref x3(m); x0 = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); x2 = m.mk_polynomial(m.mk_var()); x3 = m.mk_polynomial(m.mk_var()); tst_substitute(x0 + x1*x0 + x3, 1, mpz(1), 3, mpz(2), 2*x0 + 2); tst_substitute((x0^2) + x1*x0 + x3, 0, mpz(2), 3, mpz(2), 2*x1 + 6); tst_substitute((x0 + x1 + x2)^3, 0, mpz(2), 2, mpz(3), (x1 + 5)^3); tst_substitute(((x0 + x1 + x2)^3) + ((x0*x1 + x3)^2), 0, mpz(2), 2, mpz(3), ((x1 + 5)^3) + ((2*x1 + x3)^2)); tst_substitute((x0 + x1 + 1)^5, 2, mpz(2), 3, mpz(3), (x0 + x1 + 1)^5); polynomial_ref zero(m); zero = m.mk_zero(); tst_substitute(zero, 2, mpz(2), 3, mpz(3), zero); } static void tst_qsubstitute(polynomial_ref const & p, unsynch_mpq_manager & qm, polynomial::var x1, rational const & v1, polynomial::var x2, rational const & v2, polynomial_ref const & expected) { polynomial::var xs[2] = { x1, x2 }; scoped_mpq_vector vs(qm); vs.push_back(v1.to_mpq()); vs.push_back(v2.to_mpq()); std::cout << "---------------\n"; std::cout << "p: " << p << std::endl; polynomial_ref r(p.m()); r = p.m().substitute(p, 2, xs, vs.c_ptr()); std::cout << "r: " << r << std::endl; std::cout << "expected (modulo a constant): " << expected << std::endl; ENSURE(eq(r, normalize(expected))); p.m().lex_sort(r); std::cout << "r (sorted): " << r << std::endl; } static void tst_qsubstitute() { unsynch_mpq_manager qm; reslimit rl; polynomial::manager m(rl, qm); polynomial_ref x0(m); polynomial_ref x1(m); polynomial_ref x2(m); polynomial_ref x3(m); x0 = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); x2 = m.mk_polynomial(m.mk_var()); x3 = m.mk_polynomial(m.mk_var()); tst_qsubstitute(x0 + x1 + x2, qm, 0, rational(1)/rational(2), 1, rational(3), 2*x2 + 2*3 + 1); tst_qsubstitute((x0^2)*x2 + x1 + x2*x0, qm, 0, rational(3)/rational(2), 1, rational(3), 9*x2 + (4*3) + (2*3)*x2); tst_qsubstitute(x0*x1*x2*(x3^2) + (x0^3)*x3 + (x1^2)*x0, qm, 0, rational(5)/rational(2), 1, rational(7)/rational(3), (2*2*3*7*5)*x2*(x3^2) + (5*5*5*3*3)*x3 + (7*7*5*2*2)); tst_qsubstitute((x2 + x3)^3, qm, 0, rational(5)/rational(2), 1, rational(7)/rational(3), (x2 + x3)^3); tst_qsubstitute((x0 + x1 + x2 + x3)^3, qm, 0, rational(5)/rational(2), 2, rational(7)/rational(3), (6*x1 + 6*x3 + 15 + 14)^3); tst_qsubstitute((x0 + 2*x1 + 11*(x2^2)*x3 + x2 + (x3^2))^3, qm, 0, rational(5)/rational(2), 3, rational(7)/rational(3), ((2*3*3*2)*x1 + (11*2*3*7)*(x2^2) + (2*3*3)*x2 + (5*9 + 7*7*2))^3); polynomial_ref zero(m); zero = m.mk_zero(); tst_qsubstitute(zero, qm, 0, rational(5)/rational(2), 3, rational(7)/rational(3), zero); } void tst_mfact(polynomial_ref const & p, unsigned num_distinct_factors) { std::cout << "---------------\n"; std::cout << "p: " << p << std::endl; polynomial::factors fs(p.m()); factor(p, fs); std::cout << "factors:\n"; std::cout << p.m().m().to_string(fs.get_constant()) << "\n"; for (unsigned i = 0; i < fs.distinct_factors(); i++) { std::cout << "*(" << fs[i] << ")^" << fs.get_degree(i) << std::endl; } ENSURE(fs.distinct_factors() == num_distinct_factors); polynomial_ref p2(p.m()); fs.multiply(p2); ENSURE(eq(p, p2)); } static void tst_mfact() { polynomial::numeral_manager nm; reslimit rl; polynomial::manager m(rl, nm); polynomial_ref x0(m); polynomial_ref x1(m); polynomial_ref x2(m); polynomial_ref x3(m); polynomial_ref x4(m); polynomial_ref x5(m); x0 = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); x2 = m.mk_polynomial(m.mk_var()); x3 = m.mk_polynomial(m.mk_var()); x4 = m.mk_polynomial(m.mk_var()); x5 = m.mk_polynomial(m.mk_var()); polynomial_ref & x = x0; tst_mfact((x0 - (x1^3))*(x0 - ((x2^3) - 2)), 2); tst_mfact((((x3^3) + 1)*x0 - (x1^3))*(x0 - ((x2^3) - 2)), 2); tst_mfact((((x3^3) + 1)*x0 - (x1^3))*((x3 - 1)*x0 - ((x2^3) - 2)), 2); tst_mfact((((x3^3) + 1)*x0 - (x1^3))*((-1)*x0 - ((x2^3) - 2)), 2); tst_mfact((-1)*(x0 - (x1^3))*(x0 - ((x2^3) - 2)), 2); tst_mfact((-1)*(x0 - (x1^3) + (x1^2) + 2)*(x0 - ((x2^3) - 2)), 2); tst_mfact(((x0 - (x1^3))*(x0 - ((x2^3) - 2)))^2, 2); tst_mfact(((((x3^3) + 1)*x0 - (x1^3))*(x0 - ((x2^3) - 2)))^2, 2); tst_mfact(((((x3^3) + 1)*x0 - (x1^3))*((x3 - 1)*x0 - ((x2^3) - 2)))^2, 2); tst_mfact(((((x3^3) + 1)*x0 - (x1^3))*((-1)*x0 - ((x2^3) - 2)))^2, 2); tst_mfact(((-1)*(x0 - (x1^3))*(x0 - ((x2^3) - 2)))^2, 2); tst_mfact(((-1)*(x0 - (x1^3) + (x1^2) + 2)*(x0 - ((x2^3) - 2)))^2, 2); tst_mfact(((x0 - (x1^3))*(x0 - ((x2^3) - 2)))^3, 2); tst_mfact(((((x3^3) + 1)*x0 - (x1^3))*(x0 - ((x2^3) - 2)))^3, 2); tst_mfact(((((x3^3) + 1)*x0 - (x1^3))*((x3 - 1)*x0 - ((x2^3) - 2)))^3, 2); tst_mfact(((((x3^3) + 1)*x0 - (x1^3))*((-1)*x0 - ((x2^3) - 2)))^3, 2); tst_mfact(((-1)*(x0 - (x1^3))*(x0 - ((x2^3) - 2)))^3, 2); tst_mfact(((-1)*(x0 - (x1^3) + (x1^2) + 2)*(x0 - ((x2^3) - 2)))^3, 2); tst_mfact(((x0 - (x1^3))*(x0 - ((x2^3) - 2)))^4, 2); tst_mfact(((((x3^3) + 1)*x0 - (x1^3))*(x0 - ((x2^3) - 2)))^4, 2); tst_mfact(((((x3^3) + 1)*x0 - (x1^3))*((x3 - 1)*x0 - ((x2^3) - 2)))^4, 2); tst_mfact(((((x3^3) + 1)*x0 - (x1^3))*((-1)*x0 - ((x2^3) - 2)))^4, 2); tst_mfact(((-1)*(x0 - (x1^3))*(x0 - ((x2^3) - 2)))^4, 2); tst_mfact(((-1)*(x0 - (x1^3) + (x1^2) + 2)*(x0 - ((x2^3) - 2)))^4, 2); tst_mfact(((x^5) - (x^2) + 1)*((-1)*x + 1)*((x^2) - 2*x + 3), 3); tst_mfact(11*((x^5) - (x^2) + 1)*((-1)*x + 1)*((x^2) - 2*x + 3), 3); tst_mfact(11*(7*(x^5) - (x^2) + 1)*((-1)*(x^2) + 1)*((x^2) - 2*x + 3), 4); tst_mfact(11*(7*(x^5) - (x^2) + 1)*((-1)*(x^2) + 1)*((x^2) - 2*x + 3)*((x^7) - x +2), 5); tst_mfact((7*(x^5) - (x^2) + 1)*((-1)*(x^2) + 1)*((x^2) - 2*x + 3)*((x^7) - x +2)*((x^3) - x + 1), 6); tst_mfact((7*(x^5) - (x^2) + 1)*((-1)*(x^2) + 1)*((x^2) - 2*x + 3)*((x^7) - x +2)*((x^3) - x + 1)*((x^7) - (x^5) + (x^3) + (x^2) + x + 3), 7); tst_mfact((7*(x^5) - (x^2) + 1)*((-1)*(x^2) + 1)*((x^2) - 2*x + 3)*((x^7) - x +2)* ((x^3) - x + 1)*((x^7) - (x^5) + (x^3) + (x^2) + x + 3)*(x - (x^3) + 11)* (x - 10)*(x - 9)*(33*x + 12)*((x^5) - x + 1), 12); tst_mfact((x^4) + (x^2) - 20, 3); tst_mfact((-11)*((x^5) - (x^2) + 1)*((-1)*x + 1)*((x^2) - 2*x + 3), 3); tst_mfact(x0 - 2*(x0^2) + 1, 2); tst_mfact((x0 + 1)*(x0 - 1)*(x0 + 2)*(((x1^5) - x1 - 1)^2), 4); tst_mfact((x0 + 1)*((x1 + 2)^2), 2); tst_mfact(7*(x0 + 1)*((x1 + 2)^2), 2); tst_mfact(11*(x0 + 1)*((x1 + 2)^2)*((x1 - x3)^4), 3); tst_mfact(11*(x0 + 1)*((x1 + 2)^2)*((x1 - x3)^4)*(((x0*(x2^2) + (x0 + x1)*x2 + x1))^3), 5); tst_mfact((11*(x0 + 1)*((x1 + 2)^2))^3, 2); tst_mfact((3*(x0 + 1)*(x1 + 2))^3, 2); tst_mfact((3*(x0 + 1)*(2*x1 + 2))^3, 2); tst_mfact(3*(2*(x0^2) + 4*(x1^2))*x2, 2); tst_mfact(13*((x0 - x2)^6)*((x1 - x2)^5)*((x0 - x3)^7), 3); tst_mfact((x0+1)^100, 1); tst_mfact((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); tst_mfact(((x0^4) - 8*(x0^2)), 2); tst_mfact((x0^5) - 2*(x0^3) + x0 - 1, 1); tst_mfact( (x0^25) - 4*(x0^21) - 5*(x0^20) + 6*(x0^17) + 11*(x0^16) + 10*(x0^15) - 4*(x0^13) - 7*(x0^12) - 9*(x0^11) - 10*(x0^10) + (x0^9) + (x0^8) + (x0^7) + (x0^6) + 3*(x0^5) + x0 - 1, 2); tst_mfact( (x0^25) - 10*(x0^21) - 10*(x0^20) - 95*(x0^17) - 470*(x0^16) - 585*(x0^15) - 40*(x0^13) - 1280*(x0^12) - 4190*(x0^11) - 3830*(x0^10) + 400*(x0^9)+ 1760*(x0^8) + 760*(x0^7) - 2280*(x0^6) + 449*(x0^5) + 640*(x0^3) - 640*(x0^2) + 240*x0 - 32, 2); tst_mfact( x0^10, 1); polynomial_ref c(m); c = m.mk_zero(); tst_mfact(c, 0); c = m.mk_const(mpz(3)); tst_mfact(c, 0); tst_mfact(x0, 1); tst_mfact(x0 + x1, 1); tst_mfact(x0 - x1, 1); tst_mfact( (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_mfact( (x0^50) - 10*(x0^40) + 38*(x0^30) - 2*(x0^25) - 100*(x0^20) - 40*(x0^15) + 121*(x0^10) - 38*(x0^5) - 17, 1); polynomial_ref & y = x0; tst_mfact( (((y^5) + 5*(y^4) + 10*(y^3) + 10*(y^2) + 5*y)^10) + 10*(((y^5) + 5*(y^4) + 10*(y^3) + 10*(y^2) + 5*y)^9) + 35*(((y^5) + 5*(y^4) + 10*(y^3) + 10*(y^2) + 5*y)^8) + 40*(((y^5) + 5*(y^4) + 10*(y^3) + 10*(y^2) + 5*y)^7) - 32*(((y^5) + 5*(y^4) + 10*(y^3) + 10*(y^2) + 5*y)^6) - 82*(((y^5) + 5*(y^4) + 10*(y^3) + 10*(y^2) + 5*y)^5) - 30*(((y^5) + 5*(y^4) + 10*(y^3) + 10*(y^2) + 5*y)^4) - 140*(((y^5) + 5*(y^4) + 10*(y^3) + 10*(y^2) + 5*y)^3) - 284*(((y^5) + 5*(y^4) + 10*(y^3) + 10*(y^2) + 5*y)^2) - 168*((y^5) + 5*(y^4) + 10*(y^3) + 10*(y^2) + 5*y) - 47, 1); tst_mfact( (y^4) - 404*(y^2) + 39204, 2); tst_mfact( ((y^5) - 15552)* ((y^20)- 15708*(y^15) + rational("138771724")*(y^10)- rational("432104148432")*(y^5) + rational("614198284585616")), 2); tst_mfact( (y^25) - rational("3125")*(y^21) - rational("15630")*(y^20) + rational("3888750")*(y^17) + rational("38684375")*(y^16) + rational("95765635")*(y^15) - rational("2489846500")*(y^13) - rational("37650481875")*(y^12) - rational("190548065625")*(y^11) - rational("323785250010")*(y^10) + rational("750249453025")*(y^9) + rational("14962295699875")*(y^8) + rational("111775113235000")*(y^7) + rational("370399286731250")*(y^6) + rational("362903064503129")*(y^5) - rational("2387239013984400")*(y^4) - rational("23872390139844000")*(y^3) - rational("119361950699220000")*(y^2) - rational("298404876748050000")*y - rational("298500366308609376"), 2); tst_mfact( rational("54")*(y^24) - (y^27) - 324*(y^21) + rational("17496")*(y^18) - 34992*(y^15)+ rational("1889568")*(y^12)- 1259712*(y^9) + rational("68024448")*(y^6), 3); tst_mfact( ((y^3)- 432)*(((y^3)+54)^2)*((y^6)+108)*((y^6)+6912)*((y^6)- 324*(y^3)+37044), 5); tst_mfact( ((y^6)- 6*(y^4) - 864*(y^3) + 12*(y^2) - 5184*y + 186616)* (((y^6) - 6*(y^4) + 108*(y^3) + 12*(y^2) + 648*y + 2908)^2)* ((y^12) - 12*(y^10) + 60*(y^8) + 56*(y^6) + 6720*(y^4) + 12768*(y^2) + 13456)* ((y^12) - 12*(y^10) + 60*(y^8) + 13664*(y^6) + 414960*(y^4) + 829248*(y^2) + 47886400)* ((y^12) - 12*(y^10) - 648*(y^9)+ 60*(y^8) + 178904*(y^6) + 15552*(y^5) + 1593024*(y^4) - 24045984*(y^3) + 5704800*(y^2) - 143995968*y + 1372010896), 5); { polynomial_ref q1(m); polynomial_ref q2(m); polynomial_ref q3(m); polynomial_ref q4(m); polynomial_ref q5(m); polynomial_ref p(m); q1 = (x0^3) - 2; q2 = (x1^3) - 2; q3 = (x2^3) - 2; q4 = (x3^2) - 2; q5 = (x4^7) - x4 + 3; p = x5 - x0 - 2*x1 /* - 3*x2 - x3 */ + x4; p = resultant(p, q1, 0); std::cout << "finished resultant 1... size: " << size(p) << std::endl; p = resultant(p, q2, 1); std::cout << "finished resultant 2... size: " << size(p) << std::endl; // p = resultant(p, q3, 2); std::cout << "finished resultant 3... size: " << size(p) << std::endl; // p = resultant(p, q4, 3); std::cout << "finished resultant 4... size: " << size(p) << std::endl; p = resultant(p, q5, 4); tst_mfact(p, 2); } } static void tst_zp() { unsynch_mpz_manager z; reslimit rl; polynomial::manager pm(rl, z); polynomial_ref x(pm); polynomial_ref y(pm); x = pm.mk_polynomial(pm.mk_var()); y = pm.mk_polynomial(pm.mk_var()); polynomial_ref p(pm); polynomial_ref q(pm); p = (x^4) + 2*(x^3) + 2*(x^2) + x; q = (x^3) + x + 1; std::cout << "p: " << p << "\n"; std::cout << "q: " << q << "\n"; std::cout << "gcd: " << gcd(p, q) << "\n"; { polynomial::scoped_set_zp setZ3(pm, 3); polynomial_ref p3(pm); polynomial_ref q3(pm); p3 = normalize(p); q3 = normalize(q); std::cout << "p[Z_3]: " << p3 << "\n"; std::cout << "q[Z_3]: " << q3 << "\n"; std::cout << "gcd[Z_3]: " << gcd(p3, q3) << "\n"; } std::cout << "back into Z[x,y]\ngcd: " << gcd(p, q) << "\n"; p = 5*(x^2)*(y^2) + 3*(x^3) + 7*(y^3) + 3; { polynomial::scoped_set_zp setZ11(pm, 11); polynomial_ref p11(pm); std::cout << "---------------\n"; p11 = normalize(p); std::cout << "p[Z_11]: " << p11 << "\n"; p11 = pm.mk_glex_monic(p11); std::cout << "monic p[Z_11]: " << p11 << "\n"; } std::cout << "back into Z[x,y]\n"; std::cout << "p: " << p << "\n"; std::cout << "gcd: " << gcd(p, q) << "\n"; } static void tst_translate(polynomial_ref const & p, polynomial::var x0, int v0, polynomial::var x1, int v1, polynomial::var x2, int v2, polynomial_ref const & expected) { std::cout << "---------------\n"; std::cout << "p: " << p << std::endl; polynomial::var xs[3] = { x0, x1, x2 }; mpz vs[3] = { mpz(v0), mpz(v1), mpz(v2) }; polynomial_ref r(p.m()); p.m().translate(p, 3, xs, vs, r); std::cout << "r: " << r << std::endl; ENSURE(eq(expected, r)); } static void tst_translate() { unsynch_mpq_manager qm; reslimit rl; polynomial::manager m(rl, qm); polynomial_ref x0(m); polynomial_ref x1(m); polynomial_ref x2(m); polynomial_ref x3(m); x0 = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); x2 = m.mk_polynomial(m.mk_var()); x3 = m.mk_polynomial(m.mk_var()); tst_translate((x0^2) + x1 + 1, 0, 1, 1, 2, 3, 0, (x0^2) + 2*x0 + x1 + 4 ); tst_translate(x3 + 1, 0, 1, 1, 2, 2, 3, x3 + 1 ); tst_translate(x3 + 1, 0, 1, 1, 2, 3, 0, x3 + 1 ); tst_translate(x3 + 1, 0, 1, 1, 2, 3, 10, x3 + 11 ); tst_translate((x0^3)*(x1^2) + (x0^2)*(x1^3) + 10, 0, -3, 1, -2, 3, 0, (x0^3)*(x1^2) + (x0^2)*(x1^3) - 4*(x0^3)*x1 - 15*(x0^2)*(x1^2) - 6*x0*(x1^3) + 4*(x0^3) + 48*(x0^2)*x1 + 63*x0*(x1^2) + 9*(x1^3) - 44*(x0^2) - 180*x0*x1 - 81*(x1^2) + 156*x0 + 216*x1 - 170 ); } #if 0 static void tst_p25() { unsynch_mpq_manager qm; reslimit rl; polynomial::manager m(rl, qm); polynomial_ref x0(m); polynomial_ref x1(m); polynomial_ref x2(m); polynomial_ref x3(m); polynomial_ref x4(m); polynomial_ref x5(m); polynomial_ref x6(m); x0 = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); x2 = m.mk_polynomial(m.mk_var()); x3 = m.mk_polynomial(m.mk_var()); x4 = m.mk_polynomial(m.mk_var()); x5 = m.mk_polynomial(m.mk_var()); x6 = m.mk_polynomial(m.mk_var()); polynomial_ref p(m); p = (x0 + x1 + x2 + x3 + x4 + x5 + x6)^25; std::cout << "size(p): " << size(p) << "\n"; } #endif static void tst_mm() { unsynch_mpq_manager qm; // pm1 and pm2 share the same monomial manager reslimit rl; polynomial::manager * pm1_ptr = alloc(polynomial::manager, rl, qm); polynomial::manager & pm1 = *pm1_ptr; polynomial::manager pm2(rl, qm, &pm1.mm()); polynomial::manager pm3(rl, qm); // pm3 has its own manager polynomial_ref p2(pm2); { polynomial_ref x0(pm1); polynomial_ref x1(pm1); polynomial_ref x2(pm1); x0 = pm1.mk_polynomial(pm1.mk_var()); x1 = pm1.mk_polynomial(pm1.mk_var()); x2 = pm1.mk_polynomial(pm1.mk_var()); polynomial_ref p1(pm1); p1 = (x0 + x1 + x2)^2; std::cout << "p1: " << p1 << "\n"; p2 = convert(pm1, p1, pm2); std::cout << "p2: " << p2 << "\n"; ENSURE(pm1.get_monomial(p1, 0) == pm2.get_monomial(p2, 0)); polynomial_ref p3(pm3); p3 = convert(pm1, p1, pm3); ENSURE(pm1.get_monomial(p1, 0) != pm3.get_monomial(p3, 0)); } dealloc(pm1_ptr); // p2 is still ok std::cout << "p2: " << p2 << "\n"; } static void tst_eval(polynomial_ref const & p, polynomial::var x0, rational v0, polynomial::var x1, rational v1, polynomial::var x2, rational v2, rational expected) { TRACE("eval_bug", tout << "tst_eval, " << p << "\n";); std::cout << "p: " << p << "\nx" << x0 << " -> " << v0 << "\nx" << x1 << " -> " << v1 << "\nx" << x2 << " -> " << v2 << "\n"; unsynch_mpq_manager qm; polynomial::simple_var2value x2v(qm); x2v.push_back(x0, v0.to_mpq()); x2v.push_back(x1, v1.to_mpq()); x2v.push_back(x2, v2.to_mpq()); scoped_mpq r(qm); p.m().eval(p, x2v, r); std::cout << "r: " << r << "\nexpected: " << expected << "\n"; scoped_mpq ex(qm); qm.set(ex, expected.to_mpq()); ENSURE(qm.eq(r, ex)); } static void tst_eval() { polynomial::numeral_manager nm; reslimit rl; polynomial::manager m(rl, nm); polynomial_ref x0(m); polynomial_ref x1(m); polynomial_ref x2(m); x0 = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); x2 = m.mk_polynomial(m.mk_var()); tst_eval(2000*x1 - x0, 0, rational(1), 1, rational(2), 2, rational(3), rational(3999)); tst_eval(x0 + 1, 0, rational(1), 1, rational(2), 2, rational(3), rational(2)); tst_eval((x0^3) + x0 + 1, 0, rational(2), 1, rational(2), 2, rational(3), rational(11)); tst_eval((x0^3) - 2*x0 + 1, 0, rational(2), 1, rational(2), 2, rational(3), rational(5)); tst_eval((x0^3) - 2*x0 + 1, 0, rational(-2), 1, rational(2), 2, rational(3), rational(-3)); tst_eval((x0^4) - 2*x0 + x1 + 1, 0, rational(-2), 1, rational(10), 2, rational(3), rational(31)); tst_eval((x0^4) - 2*x0 + ((x0^3) + 1)*x1 + 1, 0, rational(-2), 1, rational(10), 2, rational(3), rational(-49)); tst_eval(((x0^4) - 2*x0)*(x1^2) + ((x0^3) + 1)*x1 + (x0^2) + 1, 0, rational(-2), 1, rational(10), 2, rational(3), rational(1935)); tst_eval(((x0^4) - 2*x0)*(x1^2)*(x2^3) + ((x0^3) + 1)*x1 + (x0^2) + 1, 0, rational(-2), 1, rational(10), 2, rational(0), rational(-65)); tst_eval(((x0^4) - 2*x0)*((x1^2) + 1)*(x2^3) + ((x0^3) + 1)*x1 + (x0^2) + 1, 0, rational(-2), 1, rational(10), 2, rational(1, 2), rational(375, 2)); tst_eval(x0*x1*x2, 0, rational(2), 1, rational(3), 2, rational(1), rational(6)); tst_eval(x0*x1*x2 + 1, 0, rational(2), 1, rational(3), 2, rational(1), rational(7)); polynomial_ref one(m); one = x0 - x0 + 1; tst_eval(one, 0, rational(2), 1, rational(3), 2, rational(1), rational(1)); tst_eval(x0*(x1^2)*x2 + 1, 0, rational(2), 1, rational(3), 2, rational(1), rational(19)); tst_eval(x0*(x1^2)*x2 + x1 + 1, 0, rational(2), 1, rational(3), 2, rational(1), rational(22)); tst_eval(x0*(x1^2)*x2 + x1 + 1 + (x2^2)*(2*x1 - 1), 0, rational(2), 1, rational(3), 2, rational(1), rational(27)); tst_eval((x0^5) + 1, 0, rational(2), 1, rational(3), 2, rational(1), rational(33)); tst_eval((x0^5) + x0*x1 + 1, 0, rational(2), 1, rational(1), 2, rational(5), rational(35)); tst_eval((x1^5) + x0*x1 + 1, 0, rational(2), 1, rational(1), 2, rational(5), rational(4)); tst_eval((x1^5) + x0*(x1^2) + 1, 0, rational(2), 1, rational(-2), 2, rational(5), rational(-23)); } static void tst_mk_unique() { polynomial::numeral_manager nm; reslimit rl; polynomial::manager m(rl, nm); polynomial_ref x0(m); polynomial_ref x1(m); polynomial_ref x2(m); x0 = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); x2 = m.mk_polynomial(m.mk_var()); polynomial::cache uniq(m); polynomial_ref p(m); polynomial_ref q(m); polynomial_ref r(m); p = (x0^3) + (x2^5) + x0*x1 + x0*x1*x1 + 3*x0*x0 + 5; q = x0*x1*x1 + (x0^3) + 3*x0*x0 + (x2^5) + 5 + x0*x1; r = x0*x1*x1 + (x0^3) + 3*x0*x0 + (x2^5) + 6 + x0*x1; std::cout << "p: " << p << "\n"; std::cout << "q: " << q << "\n"; std::cout << "r: " << r << "\n"; ENSURE(m.eq(p, q)); ENSURE(!m.eq(p, r)); ENSURE(p.get() != q.get()); q = uniq.mk_unique(q); p = uniq.mk_unique(p); r = uniq.mk_unique(r); std::cout << "after mk_unique\np: " << p << "\n"; std::cout << "q: " << q << "\n"; std::cout << "r: " << r << "\n"; ENSURE(m.eq(p, q)); ENSURE(!m.eq(r, q)); ENSURE(p.get() == q.get()); } struct dummy_del_eh : public polynomial::manager::del_eh { unsigned m_counter; dummy_del_eh():m_counter(0) {} virtual void operator()(polynomial::polynomial * p) { m_counter++; } }; static void tst_del_eh() { dummy_del_eh eh1; dummy_del_eh eh2; polynomial::numeral_manager nm; reslimit rl; polynomial::manager m(rl, nm); polynomial_ref x0(m); polynomial_ref x1(m); x0 = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); m.add_del_eh(&eh1); x1 = 0; ENSURE(eh1.m_counter == 1); m.add_del_eh(&eh2); x1 = m.mk_polynomial(m.mk_var()); x1 = 0; ENSURE(eh1.m_counter == 2); ENSURE(eh2.m_counter == 1); m.remove_del_eh(&eh1); x0 = 0; x1 = m.mk_polynomial(m.mk_var()); x1 = 0; ENSURE(eh1.m_counter == 2); ENSURE(eh2.m_counter == 3); m.remove_del_eh(&eh2); x1 = m.mk_polynomial(m.mk_var()); x1 = 0; ENSURE(eh1.m_counter == 2); ENSURE(eh2.m_counter == 3); } static void tst_const_coeff() { polynomial::numeral_manager nm; reslimit rl; polynomial::manager m(rl, nm); polynomial_ref x0(m); polynomial_ref x1(m); x0 = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); scoped_mpz c(nm); polynomial_ref p(m); p = (x0^2)*x1 + 3*x0 + x1; ENSURE(!m.const_coeff(p, 0, 2, c)); ENSURE(m.const_coeff(p, 0, 1, c) && c == 3); ENSURE(!m.const_coeff(p, 0, 0, c)); p = (x0^2)*x1 + 3*x0 + x1 + 1; ENSURE(!m.const_coeff(p, 0, 2, c)); ENSURE(m.const_coeff(p, 0, 1, c) && c == 3); ENSURE(!m.const_coeff(p, 0, 0, c)); p = (x0^2)*x1 + 3*x0 + 1; ENSURE(!m.const_coeff(p, 0, 2, c)); ENSURE(m.const_coeff(p, 0, 1, c) && c == 3); ENSURE(m.const_coeff(p, 0, 0, c) && c == 1); p = x1 + 3*x0 + 1; ENSURE(m.const_coeff(p, 0, 2, c) && c == 0); ENSURE(m.const_coeff(p, 0, 1, c) && c == 3); ENSURE(!m.const_coeff(p, 0, 0, c)); p = 5*(x0^2) + 3*x0 + 7; ENSURE(m.const_coeff(p, 0, 5, c) && c == 0); ENSURE(m.const_coeff(p, 0, 2, c) && c == 5); ENSURE(m.const_coeff(p, 0, 1, c) && c == 3); ENSURE(m.const_coeff(p, 0, 0, c) && c == 7); p = 5*(x0^2) + 3*x0; ENSURE(m.const_coeff(p, 0, 0, c) && c == 0); p = - x0*x1 - x1 + 1; ENSURE(!m.const_coeff(p, 0, 1, c)); } static void tst_gcd2() { // enable_trace("mgcd"); polynomial::numeral_manager nm; reslimit rl; polynomial::manager m(rl, nm); polynomial_ref x0(m); polynomial_ref x1(m); polynomial_ref x2(m); polynomial_ref x3(m); x0 = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); x2 = m.mk_polynomial(m.mk_var()); x3 = m.mk_polynomial(m.mk_var()); polynomial_ref p(m); polynomial_ref one(m); one = m.mk_const(mpz(1)); tst_gcd(one, one, one); tst_gcd((5*x0 + 3*x1)*(x1 + 2)*(x2 + 3), (10*x0 + 6*x1)*(x1 + 2)*(x2 + 5), (5*x0 + 3*x1)*(x1 + 2)); tst_gcd((x0 + 1)*(x1 + 2)*(x2 + 3), (x0 + 1)*(x1 + 2)*(x2 + 5), (x0 + 1)*(x1 + 2)); tst_gcd(((x1^2) + 2*(x2^2) + 3*(x3^3))*(x1 + x2 + x3 + 1), ((x1^2) + 2*(x2^2) + 3*(x3^3))*(x1 + x2 + x3 + 2), ((x1^2) + 2*(x2^2) + 3*(x3^3))); tst_gcd(5*(x1^3) + 11 + 7*(x0^2), 5*(x1^3) + 13 + 7*(x0^2), one); tst_gcd((5*3*(x1^2) + 5*6*(x2^2) + 5*21*(x3^3))*(5*(x1^3) + 7*(x0^2) + 11), (7*3*(x1^2) + 7*6*(x2^2) + 7*21*(x3^3))*(5*(x1^3) + 7*(x0^2) + 13), (3*(x1^2) + 6*(x2^2) + 21*(x3^3))); tst_gcd((x2^6)*(x3^6) - 4*(x2^3)*(x3^6) + 2*(x2^6)*(x3^3) - 8*(x2^3)*(x3^3) + 4*(x1^3)*(x2^3)*(x3^3) - 8*(x1^3)*(x3^3) + 4*(x3^6) + 8*(x3^3) + (x2^6) - 4*(x2^3) + 4*(x1^3)*(x2^3) - 8*(x1^3) + 4 + (x1^6), (-2)*(x2^3)*(x3^6) - 4*(x2^3)*(x3^3) + 4*(x3^6) + 8*(x3^3) - 2*(x1^3)*(x3^3) - 2*(x2^3) + 4 - 2*(x1^3), one); tst_gcd((x1^2) - 2*x0 + 1 + (x0^2) + x0*x1 - 2*x1, x0*x1, one); tst_gcd((5*3*(x1^2) + 5*6*(x2^2) + 5*21*(x3^3))*(x1 + x2 + x3 + 1)*(5*(x1^3) + 7*(x0^2) + 11), (7*3*(x1^2) + 7*6*(x2^2) + 7*21*(x3^3))*(x1 + x2 + x3 + 2)*(5*(x1^3) + 7*(x0^2) + 13), (3*(x1^2) + 6*(x2^2) + 21*(x3^3))); p = 169*(x1^12)*(x2^16) - 468*x0*(x1^11)*(x2^16) + 428*(x0^2)*(x1^10)*(x2^16) - 92*(x0^3)*(x1^9)*(x2^16) - 82*(x0^4)*(x1^8)*(x2^16) + 52*(x0^5)*(x1^7)*(x2^16) - 4*(x0^6)*(x1^6)*(x2^16) - 4*(x0^7)*(x1^5)*(x2^16) + (x0^8)*(x1^4)*(x2^16) - 581*(x1^14)*(x2^14) + 1828*x0*(x1^13)*(x2^14) - 2452*(x0^2)*(x1^12)*(x2^14) + 548*(x0^3)*(x1^11)*(x2^14) + 1002*(x0^4)*(x1^10)*(x2^14) - 756*(x0^5)*(x1^9)*(x2^14) + 124*(x0^6)*(x1^8)*(x2^14) + 44*(x0^7)*(x1^7)*(x2^14) - 13*(x0^8)*(x1^6)*(x2^14) + 895*(x1^16)*(x2^12) - 1556*x0*(x1^15)*(x2^12) + 2864*(x0^2)*(x1^14)*(x2^12); tst_gcd(p, derivative(p, 2), (x1^4)*(x2^11)); tst_gcd((11*5*3)*((x0^2) + 1)*(x1 + 3), (11*5*7)*((x0^2) + 1)*(x1 + 5), (11*5)*((x0^2) + 1)); p = (x0^4)*(x3^8) - 2*(x0^4)*(x3^7) - 2*(x0^3)*(x2^3)*(x3^7) + 4*(x0^3)*(x3^7) + 2*(x0^4)*(x3^5) - 4*(x0^4)*(x3^4) - 4*(x0^3)*(x2^3)*(x3^4) + 8*(x0^3)*(x3^4) - 2*(x0^3)*(x1^3)*(x3^5) + 4*(x0^3)*(x1^3)*(x3^4) + 4*(x0^2)*(x1^3)*(x2^3)*(x3^4) - 8*(x0^2)*(x1^3)*(x3^4) + (x0^4)*(x3^6) + 2*(x0^3)*(x2^3)*(x3^6) - 4*(x0^3)*(x3^6) + 2*(x0^4)*(x3^3) + 4*(x0^3)*(x2^3)*(x3^3) - 8*(x0^3)*(x3^3) - 2*(x0^3)*(x1^3)*(x3^3) - 4*(x0^2)*(x1^3)*(x2^3)*(x3^3) + 8*(x0^2)*(x1^3)*(x3^3) + (x0^2)*(x2^6)*(x3^6) - 4*(x0^2)*(x2^3)*(x3^6) + 2*(x0^2)*(x2^6)*(x3^3) - 8*(x0^2)*(x2^3)*(x3^3) - 2*x0*(x1^3)*(x2^6)*(x3^3) + 8*x0*(x1^3)*(x2^3)*(x3^3) + 4*(x0^2)*(x3^6) + 8*(x0^2)*(x3^3) - 8*x0*(x1^3)*(x3^3) + (x0^4)*(x3^2) - 2*(x0^4)*x3 - 2*(x0^3)*(x2^3)*x3 + 4*(x0^3)*x3 - 2*(x0^3)*(x1^3)*(x3^2) + 4*(x0^3)*(x1^3)*x3 + 4*(x0^2)*(x1^3)*(x2^3)*x3 - 8*(x0^2)*(x1^3)*x3 + (x0^4) + 2*(x0^3)*(x2^3) - 4*(x0^3) - 2*(x0^3)*(x1^3) - 4*(x0^2)*(x1^3)*(x2^3) + 8*(x0^2)*(x1^3) + (x0^2)*(x2^6) - 4*(x0^2)*(x2^3) - 2*x0*(x1^3)*(x2^6) + 8*x0*(x1^3)*(x2^3) + 4*(x0^2) - 8*x0*(x1^3) + (x0^2)*(x1^6)*(x3^2) - 2*(x0^2)*(x1^6)*x3 - 2*x0*(x1^6)*(x2^3)*x3 + 4*x0*(x1^6)*x3 + (x0^2)*(x1^6) + 2*x0*(x1^6)*(x2^3) - 4*x0*(x1^6) + (x1^6)*(x2^6) - 4*(x1^6)*(x2^3) + 4*(x1^6); // polynomial_ref p1(m); // p1 = derivative(p, 0); // polynomial_ref g(m); // for (unsigned i = 0; i < 50; i++) // g = gcd(p, p1); // return; tst_gcd(p, derivative(p, 1), x0*(x2^6)*(x3^3) - (x1^3)*(x2^6) - 2*(x0^2)*(x2^3)*(x3^4) + 2*x0*(x1^3)*(x2^3)*x3 + 2*(x0^2)*(x2^3)*(x3^3) + (x0^3)*(x3^5) - 2*x0*(x1^3)*(x2^3) + x0*(x2^6) - (x0^2)*(x1^3)*(x3^2) - 4*x0*(x2^3)*(x3^3) - 2*(x0^3)*(x3^4) + 4*(x1^3)*(x2^3) + 2*(x0^2)*(x1^3)*x3 - 2*(x0^2)*(x2^3)*x3 + (x0^3)*(x3^3) + 4*(x0^2)*(x3^4) - (x0^2)*(x1^3) + 2*(x0^2)*(x2^3) - 4*x0*(x1^3)*x3 + (x0^3)*(x3^2) - 4*(x0^2)*(x3^3) + 4*x0*(x1^3) - 4*x0*(x2^3) - 2*(x0^3)*x3 + 4*x0*(x3^3) + (x0^3) - 4*(x1^3) + 4*(x0^2)*x3 - 4*(x0^2) + 4*x0 ); tst_gcd(p, derivative(p, 0), neg((-1)*x0*(x2^3)*(x3^3) + (x1^3)*(x2^3) + (x0^2)*(x3^4) - x0*(x1^3)*x3 - (x0^2)*(x3^3) + x0*(x1^3) - x0*(x2^3) + 2*x0*(x3^3) - 2*(x1^3) + (x0^2)*x3 - (x0^2) + 2*x0)); tst_gcd(p, derivative(p, 2), neg((-1)*(x0^2)*(x2^3)*(x3^6) + 2*x0*(x1^3)*(x2^3)*(x3^3) + (x0^3)*(x3^7) - (x1^6)*(x2^3) - 2*(x0^2)*(x1^3)*(x3^4) - (x0^3)*(x3^6) + x0*(x1^6)*x3 + 2*(x0^2)*(x1^3)*(x3^3) - 2*(x0^2)*(x2^3)*(x3^3) + 2*(x0^2)*(x3^6) - x0*(x1^6) + 2*x0*(x1^3)*(x2^3) - 4*x0*(x1^3)*(x3^3) + 2*(x0^3)*(x3^4) + 2*(x1^6) - 2*(x0^2)*(x1^3)*x3 - 2*(x0^3)*(x3^3) + 2*(x0^2)*(x1^3) - (x0^2)*(x2^3) + 4*(x0^2)*(x3^3) - 4*x0*(x1^3) + (x0^3)*x3 - (x0^3) + 2*(x0^2)) ); tst_gcd(((11*5*3)*(x0^2) + 1)*(x1 + 3), ((11*5*3)*(x0^2) + 1)*(x1 + 5), ((11*5*3)*(x0^2) + 1)); return; p = 169*(x1^12)*(x2^16) - 468*x0*(x1^11)*(x2^16) + 428*(x0^2)*(x1^10)*(x2^16) - 92*(x0^3)*(x1^9)*(x2^16) - 82*(x0^4)*(x1^8)*(x2^16) + 52*(x0^5)*(x1^7)*(x2^16) - 4*(x0^6)*(x1^6)*(x2^16) - 4*(x0^7)*(x1^5)*(x2^16) + (x0^8)*(x1^4)*(x2^16) - 581*(x1^14)*(x2^14) + 1828*x0*(x1^13)*(x2^14) - 2452*(x0^2)*(x1^12)*(x2^14) + 548*(x0^3)*(x1^11)*(x2^14) + 1002*(x0^4)*(x1^10)*(x2^14) - 756*(x0^5)*(x1^9)*(x2^14) + 124*(x0^6)*(x1^8)*(x2^14) + 44*(x0^7)*(x1^7)*(x2^14) - 13*(x0^8)*(x1^6)*(x2^14) + 895*(x1^16)*(x2^12) - 1556*x0*(x1^15)*(x2^12) + 2864*(x0^2)*(x1^14)*(x2^12) + 520*(x0^3)*(x1^13)*(x2^12) - 5402*(x0^4)*(x1^12)*(x2^12) + 3592*(x0^5)*(x1^11)*(x2^12) - 156*(x0^6)*(x1^10)*(x2^12) - 680*(x0^7)*(x1^9)*(x2^12) + 171*(x0^8)*(x1^8)*(x2^12) + 12*(x0^9)*(x1^7)*(x2^12) - 4*(x0^10)*(x1^6)*(x2^12) - 957*(x1^18)*(x2^10) - 1132*x0*(x1^17)*(x2^10) + 206*(x0^2)*(x1^16)*(x2^10) + 588*(x0^3)*(x1^15)*(x2^10) + 6861*(x0^4)*(x1^14)*(x2^10) - 5016*(x0^5)*(x1^13)*(x2^10) - 2756*(x0^6)*(x1^12)*(x2^10) + 3952*(x0^7)*(x1^11)*(x2^10) - 1143*(x0^8)*(x1^10)*(x2^10) - 124*(x0^9)*(x1^9)*(x2^10) + 30*(x0^10)*(x1^8)*(x2^10) + 4*(x0^11)*(x1^7)*(x2^10) - (x0^12)*(x1^6)*(x2^10) + 1404*(x1^20)*(x2^8) + 684*x0*(x1^19)*(x2^8) - 1224*(x0^2)*(x1^18)*(x2^8) - 4412*(x0^3)*(x1^17)*(x2^8) - 1442*(x0^4)*(x1^16)*(x2^8) + 4164*(x0^5)*(x1^15)*(x2^8) + 4116*(x0^6)*(x1^14)*(x2^8) - 5308*(x0^7)*(x1^13)*(x2^8) + 392*(x0^8)*(x1^12)*(x2^8) + 1600*(x0^9)*(x1^11)*(x2^8) - 468*(x0^10)*(x1^10)*(x2^8) - 24*(x0^11)*(x1^9)*(x2^8) + 6*(x0^12)*(x1^8)*(x2^8) - 594*(x1^22)*(x2^6) - 324*x0*(x1^21)*(x2^6) + 1980*(x0^2)*(x1^20)*(x2^6) + 1136*(x0^3)*(x1^19)*(x2^6) + 405*(x0^4)*(x1^18)*(x2^6) - 3916*(x0^5)*(x1^17)*(x2^6) - 396*(x0^6)*(x1^16)*(x2^6) + 1876*(x0^7)*(x1^15)*(x2^6) + 1108*(x0^8)*(x1^14)*(x2^6) - 2064*(x0^9)*(x1^13)*(x2^6) + 248*(x0^10)*(x1^12)*(x2^6) + 380*(x0^11)*(x1^11)*(x2^6) - 95*(x0^12)*(x1^10)*(x2^6) + 81*(x1^24)*(x2^4) + 108*x0*(x1^23)*(x2^4) - 432*(x0^2)*(x1^22)*(x2^4) - 276*(x0^3)*(x1^21)*(x2^4) + 481*(x0^4)*(x1^20)*(x2^4) + 144*(x0^5)*(x1^19)*(x2^4) + 788*(x0^6)*(x1^18)*(x2^4) - 1152*(x0^7)*(x1^17)*(x2^4) + 231*(x0^8)*(x1^16)*(x2^4) + 244*(x0^9)*(x1^15)*(x2^4) + 396*(x0^10)*(x1^14)*(x2^4) - 476*(x0^11)*(x1^13)*(x2^4) + 119*(x0^12)*(x1^12)*(x2^4) + 72*(x0^4)*(x1^22)*(x2^2) - 96*(x0^5)*(x1^21)*(x2^2) - 40*(x0^6)*(x1^20)*(x2^2) - 32*(x0^7)*(x1^19)*(x2^2) + 340*(x0^8)*(x1^18)*(x2^2) - 368*(x0^9)*(x1^17)*(x2^2) + 112*(x0^10)*(x1^16)*(x2^2) + 16*(x0^11)*(x1^15)*(x2^2) - 4*(x0^12)*(x1^14)*(x2^2) + 16*(x0^8)*(x1^20) - 64*(x0^9)*(x1^19) + 96*(x0^10)*(x1^18) - 64*(x0^11)*(x1^17) + 16*(x0^12)*(x1^16); polynomial_ref p_prime(m); p_prime = derivative(p, 2); tst_gcd(p, p_prime, x1^4); } #if 0 static void tst_gcd3() { enable_trace("polynomial_gcd"); enable_trace("polynomial_gcd_detail"); enable_trace("mpzzp"); polynomial::numeral_manager nm; reslimit rl; polynomial::manager m(rl, nm); polynomial_ref x(m); x = m.mk_polynomial(m.mk_var()); polynomial_ref p(m); polynomial_ref q(m); p = (x^8) + (x^6) + (x^4) + (x^3) + (x^2) + 1; q = (x^6) + (x^4) + x + 1; { polynomial::scoped_set_zp setZ2(m, 2); std::cout << "Z_p: " << nm.to_string(m.p()) << "\n"; tst_gcd(normalize(p), normalize(q), x + 1); } { polynomial::scoped_set_zp setZ3(m, 3); std::cout << "Z_p: " << nm.to_string(m.p()) << "\n"; polynomial_ref one(m); one = m.mk_const(mpz(1)); tst_gcd(normalize(p), normalize(q), one); } } static void tst_gcd4() { enable_trace("mgcd"); // enable_trace("CRA"); polynomial::numeral_manager nm; reslimit rl; polynomial::manager m(rl, nm); polynomial_ref x(m); x = m.mk_polynomial(m.mk_var()); polynomial_ref p(m); polynomial_ref q(m); p = (15*x + 15)*((x + 2)^8)*(10000*x + 1)*(x + 3); q = (6*x + 6)*((x + 20)^8)*(10000*x + 3)*(x + 30); tst_gcd(p, q, 3*x + 3); p = (3*x + 2)*((x + 2)^8)*(10000*x + 1)*(x + 3); q = (3*x + 2)*((x + 20)^8)*(10000*x + 3)*(x + 30); tst_gcd(p, q, 3*x + 2); p = ((3*x + 2)*((x + 2)^8)*(10000*x + 1)*(x + 3))^3; q = ((3*x + 2)*((x + 20)^8)*(10000*x + 3)*(x + 30))^3; tst_gcd(p, q, (3*x + 2)^3); p = ((x + 3)^10)*((x^5) - x - 1)*(x + 1)*(x + 2)*(x + 4)*(10000*x + 33)*(x + 6)*(x + 11)*(x+33)* ((x^16) - 136*(x^14) + 6476*(x^12) - 141912*(x^10) + 1513334*(x^8) - 7453176*(x^6) + 13950764*(x^4) - 5596840*(x^2) + 46225)* (1000000*x + 1)*(333333333*x + 1)*(77777777*x + 1)*(11111111*x + 1)*(x + 128384747)*(x + 82837437)*(x + 22848481); tst_gcd(p, derivative(p, 0), (x + 3)^9); } #endif static void tst_newton_interpolation() { // enable_trace("newton"); polynomial::numeral_manager nm; reslimit rl; polynomial::manager m(rl, nm); polynomial_ref x(m); polynomial_ref y(m); x = m.mk_polynomial(m.mk_var()); y = m.mk_polynomial(m.mk_var()); polynomial_ref p1(m), p2(m), p3(m); p1 = (-9)*y - 21; p2 = (-3)*y + 20; p3 = 5*y - 36; scoped_mpz_vector ins(nm); ins.push_back(mpz(0)); ins.push_back(mpz(1)); ins.push_back(mpz(2)); polynomial::polynomial * outs[3] = { p1.get(), p2.get(), p3.get() }; polynomial_ref r(m); { polynomial::scoped_set_zp setZ97(m, 97); m.newton_interpolation(0, 2, ins.c_ptr(), outs, r); } std::cout << "interpolation result: " << r << "\n"; ENSURE(m.eq((x^2)*y + 5*x*y + 41*x - 9*y - 21, r)); } static void tst_slow_mod_gcd() { polynomial::numeral_manager nm; reslimit rl; polynomial::manager m(rl, nm); polynomial_ref x0(m), x1(m), x2(m), x3(m), x4(m), x5(m); x0 = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); x2 = m.mk_polynomial(m.mk_var()); x3 = m.mk_polynomial(m.mk_var()); x4 = m.mk_polynomial(m.mk_var()); x5 = m.mk_polynomial(m.mk_var()); polynomial_ref p(m), q(m), b(m); polynomial_ref p_prime(m); p = ((x0^3)*x1*x2*x3*x4*x5 + x0*x1*x2*x3*(x4^3)*x5 + x0*x1*x2*x3*x4*(x5^3) - x0*x1*x2*x3*x4*x5 - 2)^2; q = derivative(p, 0); tst_gcd(p, q, (x0^3)*x1*x2*x3*x4*x5 + x0*x1*x2*x3*(x4^3)*x5 + x0*x1*x2*x3*x4*(x5^3) - x0*x1*x2*x3*x4*x5 - 2); b = (x0^10) + (x1^10) + (x2^10) + (x3^10); p = b*(x0 + 1); q = b*(x0 + 2); tst_gcd(p, q, b); return; p = (x0^8) * (((x0^3)*x1*x2*x3*x4*x5 + x0*(x1^3)*x2*x3*x4*x5 + x0*x1*(x2^3)*x3*x4*x5 + x0*x1*x2*(x3^3)*x4*x5 + x0*x1*x2*x3*(x4^3)*x5 + x0*x1*x2*x3*x4*(x5^3) - x0*x1*x2*x3*x4*x5 - 2)^2) * (((x0^3)*x1*x2*x3*x4*x5 + x0*(x1^3)*x2*x3*x4*x5 + x0*x1*(x2^3)*x3*x4*x5 + x0*x1*x2*(x3^3)*x4*x5 + x0*x1*x2*x3*(x4^3)*x5 + x0*x1*x2*x3*x4*(x5^3) - x0*x1*x2*x3*x4*x5 + 2)^2); p_prime = derivative(p, 0); tst_gcd(p, p_prime, (x0^7) * ((x0^3)*x1*x2*x3*x4*x5 + x0*(x1^3)*x2*x3*x4*x5 + x0*x1*(x2^3)*x3*x4*x5 + x0*x1*x2*(x3^3)*x4*x5 + x0*x1*x2*x3*(x4^3)*x5 + x0*x1*x2*x3*x4*(x5^3) - x0*x1*x2*x3*x4*x5 - 2) * ((x0^3)*x1*x2*x3*x4*x5 + x0*(x1^3)*x2*x3*x4*x5 + x0*x1*(x2^3)*x3*x4*x5 + x0*x1*x2*(x3^3)*x4*x5 + x0*x1*x2*x3*(x4^3)*x5 + x0*x1*x2*x3*x4*(x5^3) - x0*x1*x2*x3*x4*x5 + 2)); } void tst_linear_solver() { unsynch_mpq_manager qm; scoped_mpq_vector as(qm); scoped_mpq b(qm); scoped_mpq_vector xs(qm); linear_eq_solver solver(qm); solver.resize(3); xs.resize(3); as.reset(); as.push_back(mpq(2)); as.push_back(mpq(1)); as.push_back(mpq(-1)); qm.set(b, 8); solver.add(0, as.c_ptr(), b); as.reset(); as.push_back(mpq(-3)); as.push_back(mpq(-1)); as.push_back(mpq(2)); qm.set(b, -11); solver.add(1, as.c_ptr(), b); as.reset(); as.push_back(mpq(-2)); as.push_back(mpq(1)); as.push_back(mpq(2)); qm.set(b, -3); solver.add(2, as.c_ptr(), b); VERIFY(solver.solve(xs.c_ptr())); ENSURE(qm.eq(xs[0], mpq(2))); ENSURE(qm.eq(xs[1], mpq(3))); ENSURE(qm.eq(xs[2], mpq(-1))); } static void tst_lex(polynomial_ref const & p1, polynomial_ref const & p2, int lex_expected, polynomial::var min, int lex2_expected) { polynomial::manager & m = p1.m(); std::cout << "compare "; m.display(std::cout, m.get_monomial(p1, 0)); std::cout << " "; m.display(std::cout, m.get_monomial(p2, 0)); std::cout << " "; std::cout.flush(); int r1 = lex_compare(m.get_monomial(p1, 0), m.get_monomial(p2, 0)); int r2 = lex_compare2(m.get_monomial(p1, 0), m.get_monomial(p2, 0), min); ENSURE(r1 == lex_expected); ENSURE(r2 == lex2_expected); std::cout << r1 << " " << r2 << "\n"; ENSURE(lex_compare(m.get_monomial(p2, 0), m.get_monomial(p1, 0)) == -r1); ENSURE(lex_compare2(m.get_monomial(p2, 0), m.get_monomial(p1, 0), min) == -r2); } static void tst_lex() { polynomial::numeral_manager nm; reslimit rl; polynomial::manager m(rl, nm); polynomial_ref x0(m), x1(m), x2(m), x3(m), x4(m), x5(m); x0 = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); x2 = m.mk_polynomial(m.mk_var()); x3 = m.mk_polynomial(m.mk_var()); x4 = m.mk_polynomial(m.mk_var()); x5 = m.mk_polynomial(m.mk_var()); polynomial_ref one(m); one = m.mk_const(mpz(1)); tst_lex(x0*x4*x1, (x0^10)*(x1^3), 1, 4, -1); tst_lex(x0*x3*(x1^2)*x4, x0*(x3^2)*(x1^2)*x4, -1, 3, -1); tst_lex((x0^2)*x3*(x1^2)*x4, x0*(x3^2)*(x1^2)*x4, -1, 3, 1); tst_lex(x0*x3*(x1^2)*x4, x0*x3*(x1^2)*x4, 0, 3, 0); tst_lex(x0*(x3^2)*(x1^2)*x4, x0*x3*(x1^2)*x4, 1, 3, 1); tst_lex((x1^2)*x4, x0*x2*x3*x4*x5, -1, 1, -1); tst_lex((x1^2)*x3*x4, x0*x1, 1, 1, 1); tst_lex(x1*x3*x4, x2*x3*x4, -1, 2, 1); tst_lex(x1*x3*x4, x2*x3*x4, -1, 1, -1); tst_lex(x1*x3*x4, x0*x2*x3*x4, -1, 1, -1); tst_lex(x3, x4, -1, 1, -1); tst_lex(x3, x4, -1, 4, 1); tst_lex(x2*x3, x4, -1, 1, -1); tst_lex(x2*x3, x4, -1, 4, 1); tst_lex(x3, x2*x4, -1, 1, -1); tst_lex(x3, x2*x4, -1, 4, 1); tst_lex(x3, x2*x4, -1, 4, 1); tst_lex(one, x3, -1, 1, -1); tst_lex(one, x3, -1, 3, -1); tst_lex(x3, one, 1, 3, 1); tst_lex(x4*x5, (x4^3)*x5, -1, 4, -1); } static void tst_divides() { polynomial::numeral_manager nm; reslimit rl; polynomial::manager m(rl, nm); polynomial_ref x0(m); x0 = m.mk_polynomial(m.mk_var()); polynomial_ref q(m); polynomial_ref p(m); q = 16*(x0^27) - 1984*(x0^26) + 1762*(x0^25) + 17351*(x0^24) - 14165*(x0^23) + 16460*(x0^22) + 2919*(x0^21) - 16823*(x0^20) + 1530*(x0^19) + 10646*(x0^18) + 19217*(x0^17); p = 16*(x0^39) - 3648*(x0^38) + 338136*(x0^37) - 16037936*(x0^36) + 392334357*(x0^35) - rational("3851617443")*(x0^34) - rational("14636221526")*(x0^33) + rational("377151717618")*(x0^32) + rational("677140776981")*(x0^31) - rational("4308280094419")*(x0^30) + rational("312708087606")*(x0^29) + rational("8205543533730")*(x0^28) + rational("3331586202704")*(x0^27) - rational("15291636627072")*(x0^26) + rational("433482645282")*(x0^25) + rational("7397104817486")*(x0^24) + rational("1021197979053")*(x0^23) - rational("1373737505247")*(x0^22) - rational("639394669026")*(x0^21) - rational("118513560618")*(x0^20) - rational("10405319535")*(x0^19) - rational("358722675")*(x0^18); std::cout << "----------------------\n"; std::cout << "q: " << q << "\n"; std::cout << "p: " << p << std::endl; std::cout << "divides(q, p): " << m.divides(q, p) << "\n"; } void tst_polynomial() { set_verbosity_level(1000); // enable_trace("factor"); // enable_trace("poly_bug"); // enable_trace("factor_bug"); disable_trace("polynomial"); enable_trace("psc_chain_classic"); enable_trace("Lazard"); // enable_trace("eval_bug"); // enable_trace("mgcd"); tst_psc(); return; tst_eval(); tst_divides(); tst_gcd2(); tst_slow_mod_gcd(); tst_gcd(); tst_lex(); tst_linear_solver(); tst_newton_interpolation(); tst_resultant(); // // tst_gcd4(); // tst_gcd3(); tst_zp(); tst_const_coeff(); tst_psc(); tst_del_eh(); tst_mk_unique(); tst_qsubstitute(); tst_substitute(); tst_discriminant(); tst_mfact(); tst_mm(); // tst_p25(); // return; tst_translate(); // enable_trace("mpz_gcd"); tst_vars(); tst_sqf(); enable_trace("resultant"); enable_trace("psc"); disable_trace("polynomial"); enable_trace("pseudo_remainder"); enable_trace("resultant_bug"); tst_sqrt(); tst_prem(); tst_compose(); tst11(); tst10(); tst9(); tst8(); tst7(); tst6(); tst5(); tst3(); tst2(); tst1(); tst4(); } #else void tst_polynomial() { // it takes forever to compiler these regressions using clang++ } #endif z3-z3-4.8.7/src/test/polynorm.cpp000066400000000000000000000147531356505360400165720ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "ast/rewriter/th_rewriter.h" #include "parsers/smt2/smt2parser.h" #include "ast/arith_decl_plugin.h" #include "ast/reg_decl_plugins.h" #include "ast/rewriter/arith_rewriter.h" #include "ast/ast_pp.h" static expr_ref parse_fml(ast_manager& m, char const* str) { expr_ref result(m); cmd_context ctx(false, &m); ctx.set_ignore_check(true); std::ostringstream buffer; buffer << "(declare-const x Int)\n" << "(declare-const y Int)\n" << "(declare-const z Int)\n" << "(declare-const a Int)\n" << "(declare-const b Int)\n" << "(assert " << str << ")\n"; std::istringstream is(buffer.str()); VERIFY(parse_smt2_commands(ctx, is)); result = ctx.assertions().get(0); return result; } static char const* example1 = "(= (+ (- (* x x) (* 2 y)) y) 0)"; static char const* example2 = "(= (+ 4 3 (- (* x x) (* 2 y)) y) 0)"; class poly_nf { expr_ref m_coefficient; expr_ref_vector m_coefficients; expr_ref_vector m_factors; public: poly_nf(ast_manager& m): m_coefficient(m), m_coefficients(m), m_factors(m) {} expr_ref& coefficient() { return m_coefficient; } expr_ref_vector& coefficients() { return m_coefficients; } expr_ref_vector& factors() { return m_factors; } void reset() { m_coefficient.reset(); m_coefficients.reset(); m_factors.reset(); } }; class polynorm { ast_manager& m; arith_util m_arith; arith_rewriter m_arith_rw; th_rewriter m_rw; public: polynorm(ast_manager& m): m(m), m_arith(m), m_arith_rw(m), m_rw(m) {} private: expr_ref_vector mk_fresh_constants(unsigned num, sort* s) { expr_ref_vector result(m); for (unsigned i = 0; i < num; ++i) { result.push_back(m.mk_fresh_const("fresh", s)); } return result; } expr_ref_vector mk_fresh_reals(unsigned num) { return mk_fresh_constants(num, m_arith.mk_real()); } expr_ref mk_mul(unsigned num_args, expr* const* args) { expr_ref result(m); m_arith_rw.mk_mul(num_args, args, result); return result; } void nf(expr_ref& term, obj_hashtable& constants, poly_nf& poly) { expr_ref_vector& factors = poly.factors(); expr_ref_vector& coefficients = poly.coefficients(); expr_ref& coefficient = poly.coefficient(); (void) coefficient; (void) coefficients; m_rw(term); if (m_arith.is_add(term)) { factors.append(to_app(term)->get_num_args(), to_app(term)->get_args()); } else { factors.push_back(term); } for (unsigned i = 0; i < factors.size(); ++i) { expr* f = factors[i].get(); unsigned num_args = 1; expr* const* args = &f; if (m_arith.is_mul(f)) { num_args = to_app(f)->get_num_args(); args = to_app(f)->get_args(); } for (unsigned j = 0; j < num_args; ++j) { if (m_arith.is_numeral(args[j]) || constants.contains(args[j])) { //consts.push_back(args[j]); } else { // vars.push_back(args[j]); } // deal with the relevant corner cases. } #if 0 rational r; if (m_arith.is_mul(f) && m_arith.is_numeral(to_app(f)->get_arg(0), r)) { coefficients.push_back(r); factors[i] = mk_mul(to_app(f)->get_num_args()-1, to_app(f)->get_args()+1); } else if (m_arith.is_numeral(f, r)) { factors[i] = factors.back(); factors.pop_back(); ENSURE(coefficient.is_zero()); ENSURE(!r.is_zero()); coefficient = r; --i; // repeat examining 'i' } else { coefficients.push_back(rational(1)); } #endif } TRACE("polynorm", tout << mk_pp(coefficient, m) << "\n"; for (unsigned i = 0; i < factors.size(); ++i) { tout << mk_pp(factors[i].get(), m) << " * " << mk_pp(coefficients[i].get(), m) << "\n"; }); } }; // ast /// sort : ast /// func_decl : ast /// expr : ast /// app : expr /// quantifier : expr /// var : expr /// static expr_ref mk_mul(arith_util& arith, unsigned num_args, expr* const* args) { ast_manager& m = arith.get_manager(); expr_ref result(m); switch (num_args) { case 0: UNREACHABLE(); break; case 1: result = args[0]; break; default: result = arith.mk_mul(num_args, args); break; } return result; } static void nf(expr_ref& term) { ast_manager& m = term.get_manager(); expr *e1 = nullptr, *e2 = nullptr; th_rewriter rw(m); arith_util arith(m); VERIFY(m.is_eq(term, e1, e2)); term = e1; rw(term); std::cout << mk_pp(term, m) << "\n"; std::cout << arith.is_add(term) << "\n"; expr_ref_vector factors(m); vector coefficients; rational coefficient(0); if (arith.is_add(term)) { factors.append(to_app(term)->get_num_args(), to_app(term)->get_args()); } else { factors.push_back(term); } for (unsigned i = 0; i < factors.size(); ++i) { expr* f = factors[i].get(); rational r; if (arith.is_mul(f) && arith.is_numeral(to_app(f)->get_arg(0), r)) { coefficients.push_back(r); factors[i] = mk_mul(arith, to_app(f)->get_num_args()-1, to_app(f)->get_args()+1); } else if (arith.is_numeral(f, r)) { factors[i] = factors.back(); factors.pop_back(); ENSURE(coefficient.is_zero()); ENSURE(!r.is_zero()); coefficient = r; --i; // repeat examining 'i' } else { coefficients.push_back(rational(1)); } } std::cout << coefficient << "\n"; for (unsigned i = 0; i < factors.size(); ++i) { std::cout << mk_pp(factors[i].get(), m) << " * " << coefficients[i] << "\n"; } } void tst_polynorm() { ast_manager m; reg_decl_plugins(m); expr_ref fml(m); fml = parse_fml(m, example1); std::cout << mk_pp(fml, m) << "\n"; nf(fml); fml = parse_fml(m, example2); std::cout << mk_pp(fml, m) << "\n"; nf(fml); } z3-z3-4.8.7/src/test/prime_generator.cpp000066400000000000000000000014621356505360400200660ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: prime_generator.cpp Abstract: Prime generator Author: Leonardo (leonardo) 2011-12-23 Notes: --*/ #include "util/mpz.h" #include "util/prime_generator.h" void tst_prime_generator() { unsynch_mpz_manager m; scoped_mpz sqrt_p(m); prime_generator gen; gen.initialize(); for (unsigned i = 0; i < 10000; i++) { uint64_t p = gen(i); std::cout << p << ", "; if (i % 11 == 0) std::cout << "\n"; std::cout.flush(); if (p == 2) continue; m.set(sqrt_p, p); m.root(sqrt_p, 2); uint64_t k = m.get_uint64(sqrt_p); for (uint64_t i = 2; i <= k; i++) { ENSURE(p % i != 0); } } gen.finalize(); std::cout << std::endl; } z3-z3-4.8.7/src/test/proof_checker.cpp000066400000000000000000000016041356505360400175130ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "ast/proofs/proof_checker.h" #include "ast/ast_ll_pp.h" void tst_checker1() { ast_manager m(PGM_ENABLED); expr_ref a(m); proof_ref p1(m), p2(m), p3(m), p4(m); expr_ref_vector side_conditions(m); a = m.mk_const(symbol("a"), m.mk_bool_sort()); p1 = m.mk_hypothesis(a.get()); p2 = m.mk_hypothesis(m.mk_not(a.get())); ast_ll_pp(std::cout, m, p1.get()); ast_ll_pp(std::cout, m, p2.get()); proof* proofs[2] = { p1.get(), p2.get() }; p3 = m.mk_unit_resolution(2, proofs); p4 = m.mk_lemma(p3.get(), a.get()); ast_ll_pp(std::cout, m, p4.get()); proof_checker checker(m); p4 = m.mk_lemma(p3.get(), m.mk_or(a.get(), m.mk_not(a.get()))); ast_ll_pp(std::cout, m, p4.get()); VERIFY(checker.check(p4.get(), side_conditions)); } void tst_proof_checker() { tst_checker1(); } z3-z3-4.8.7/src/test/qe_arith.cpp000066400000000000000000000431601356505360400165010ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "qe/qe_arith.h" #include "qe/qe.h" #include "ast/rewriter/th_rewriter.h" #include "parsers/smt2/smt2parser.h" #include "ast/arith_decl_plugin.h" #include "ast/reg_decl_plugins.h" #include "ast/rewriter/arith_rewriter.h" #include "ast/ast_pp.h" #include "smt/smt_context.h" #include "ast/expr_abstract.h" #include "ast/rewriter/expr_safe_replace.h" static expr_ref parse_fml(ast_manager& m, char const* str) { expr_ref result(m); cmd_context ctx(false, &m); ctx.set_ignore_check(true); std::ostringstream buffer; buffer << "(declare-const x Real)\n" << "(declare-const y Real)\n" << "(declare-const z Real)\n" << "(declare-const u Real)\n" << "(declare-const v Real)\n" << "(declare-const t Real)\n" << "(declare-const a Real)\n" << "(declare-const b Real)\n" << "(declare-const i Int)\n" << "(declare-const j Int)\n" << "(declare-const k Int)\n" << "(declare-const l Int)\n" << "(declare-const m Int)\n" << "(assert " << str << ")\n"; std::istringstream is(buffer.str()); VERIFY(parse_smt2_commands(ctx, is)); result = ctx.assertions().get(0); return result; } static char const* example1 = "(and (<= x 3.0) (<= (* 3.0 x) y) (<= z y))"; static char const* example2 = "(and (<= z x) (<= x 3.0) (<= (* 3.0 x) y) (<= z y))"; static char const* example3 = "(and (<= z x) (<= x 3.0) (< (* 3.0 x) y) (<= z y))"; static char const* example4 = "(and (<= z x) (<= x 3.0) (not (>= (* 3.0 x) y)) (<= z y))"; static char const* example5 = "(and (<= y x) (<= z x) (<= x u) (<= x v) (<= x t))"; static char const* example7 = "(and (<= x y) (<= x z) (<= u x) (< v x))"; static char const* example8 = "(and (<= (* 2 i) k) (<= j (* 3 i)))"; static char const* example6 = "(and (<= 0 (+ x z))\ (>= y x) \ (<= y x)\ (<= (- u y) 0.0)\ (>= x (+ v z))\ (>= x 0.0)\ (<= x 1.0))"; // phi[M] => result => E x . phi[x] static void test(app* var, expr_ref& fml) { ast_manager& m = fml.get_manager(); smt_params params; params.m_model = true; symbol x_name(var->get_decl()->get_name()); sort* x_sort = m.get_sort(var); expr_ref pr(m); expr_ref_vector lits(m); flatten_and(fml, lits); model_ref md; { smt::context ctx(m, params); ctx.assert_expr(fml); lbool result = ctx.check(); if (result != l_true) return; ctx.get_model(md); } VERIFY(qe::arith_project(*md, var, lits)); pr = mk_and(lits); std::cout << "original: " << mk_pp(fml, m) << "\n"; std::cout << "projected: " << mk_pp(pr, m) << "\n"; // projection is consistent with model. VERIFY(md->is_true(pr)); // projection implies E x. fml { qe::expr_quant_elim qelim(m, params); expr_ref result(m), efml(m); expr* x = var; expr_abstract(m, 0, 1, &x, fml, efml); efml = m.mk_exists(1, &x_sort, &x_name, efml); qelim(m.mk_true(), efml, result); smt::context ctx(m, params); ctx.assert_expr(pr); ctx.assert_expr(m.mk_not(result)); std::cout << "exists: " << pr << " =>\n" << result << "\n"; VERIFY(l_false == ctx.check()); } std::cout << "\n"; } static void testR(char const *ex) { ast_manager m; reg_decl_plugins(m); arith_util a(m); expr_ref fml = parse_fml(m, ex); symbol x_name("x"); sort_ref x_sort(a.mk_real(), m); app_ref var(m.mk_const(x_name, x_sort), m); test(var, fml); } static void testI(char const *ex) { ast_manager m; reg_decl_plugins(m); arith_util a(m); expr_ref fml = parse_fml(m, ex); symbol x_name("i"); sort_ref x_sort(a.mk_int(), m); app_ref var(m.mk_const(x_name, x_sort), m); test(var, fml); } static expr_ref_vector mk_ineqs(app* x, app* y, app_ref_vector const& nums) { ast_manager& m = nums.get_manager(); arith_util a(m); expr_ref_vector result(m); for (unsigned i = 0; i < nums.size(); ++i) { expr_ref ax(a.mk_mul(nums[i], x), m); result.push_back(a.mk_le(ax, y)); result.push_back(m.mk_not(a.mk_ge(ax, y))); result.push_back(m.mk_not(a.mk_gt(y, ax))); result.push_back(a.mk_lt(y, ax)); } return result; } static app_ref generate_ineqs(ast_manager& m, sort* s, vector& cs, bool mods_too) { arith_util a(m); app_ref_vector vars(m), nums(m); vars.push_back(m.mk_const(symbol("x"), s)); vars.push_back(m.mk_const(symbol("y"), s)); vars.push_back(m.mk_const(symbol("z"), s)); vars.push_back(m.mk_const(symbol("u"), s)); vars.push_back(m.mk_const(symbol("v"), s)); vars.push_back(m.mk_const(symbol("w"), s)); nums.push_back(a.mk_numeral(rational(1), s)); nums.push_back(a.mk_numeral(rational(2), s)); nums.push_back(a.mk_numeral(rational(3), s)); app* x = vars[0].get(); app* y = vars[1].get(); // app* z = vars[2].get(); // // ax <= by, ax < by, not (ax >= by), not (ax > by) // cs.push_back(mk_ineqs(x, vars[1].get(), nums)); cs.push_back(mk_ineqs(x, vars[2].get(), nums)); cs.push_back(mk_ineqs(x, vars[3].get(), nums)); cs.push_back(mk_ineqs(x, vars[4].get(), nums)); cs.push_back(mk_ineqs(x, vars[5].get(), nums)); if (mods_too) { expr_ref_vector mods(m); expr_ref zero(a.mk_numeral(rational(0), s), m); mods.push_back(m.mk_true()); for (unsigned j = 0; j < nums.size(); ++j) { mods.push_back(m.mk_eq(a.mk_mod(a.mk_add(a.mk_mul(nums[j].get(),x), y), nums[1].get()), zero)); } cs.push_back(mods); mods.resize(1); for (unsigned j = 0; j < nums.size(); ++j) { mods.push_back(m.mk_eq(a.mk_mod(a.mk_add(a.mk_mul(nums[j].get(),x), y), nums[2].get()), zero)); } cs.push_back(mods); } return app_ref(x, m); } static void test_c(app* x, expr_ref_vector const& c) { ast_manager& m = c.get_manager(); expr_ref fml(m); fml = m.mk_and(c.size(), c.c_ptr()); test(x, fml); } static void test_cs(app* x, expr_ref_vector& c, vector const& cs) { if (c.size() == cs.size()) { test_c(x, c); return; } expr_ref_vector const& c1 = cs[c.size()]; for (unsigned i = 0; i < c1.size(); ++i) { c.push_back(c1[i]); test_cs(x, c, cs); c.pop_back(); } } static void test_ineqs(ast_manager& m, sort* s, bool mods_too) { vector ineqs; app_ref x = generate_ineqs(m, s, ineqs, mods_too); expr_ref_vector cs(m); test_cs(x, cs, ineqs); } static void test_ineqs() { ast_manager m; reg_decl_plugins(m); arith_util a(m); sort* s_int = a.mk_int(); sort* s_real = a.mk_real(); test_ineqs(m, s_int, true); test_ineqs(m, s_real, false); } static void test2(char const *ex) { smt_params params; params.m_model = true; ast_manager m; reg_decl_plugins(m); arith_util a(m); expr_ref fml = parse_fml(m, ex); app_ref_vector vars(m); expr_ref_vector lits(m); vars.push_back(m.mk_const(symbol("x"), a.mk_real())); vars.push_back(m.mk_const(symbol("y"), a.mk_real())); vars.push_back(m.mk_const(symbol("z"), a.mk_real())); flatten_and(fml, lits); smt::context ctx(m, params); ctx.push(); ctx.assert_expr(fml); lbool result = ctx.check(); VERIFY(result == l_true); ref md; ctx.get_model(md); ctx.pop(1); std::cout << mk_pp(fml, m) << "\n"; expr_ref pr1(m), pr2(m), fml2(m); expr_ref_vector bound(m); ptr_vector sorts; svector names; for (unsigned i = 0; i < vars.size(); ++i) { bound.push_back(vars[i].get()); names.push_back(vars[i]->get_decl()->get_name()); sorts.push_back(m.get_sort(vars[i].get())); } expr_abstract(m, 0, bound.size(), bound.c_ptr(), fml, fml2); fml2 = m.mk_exists(bound.size(), sorts.c_ptr(), names.c_ptr(), fml2); qe::expr_quant_elim qe(m, params); for (unsigned i = 0; i < vars.size(); ++i) { VERIFY(qe::arith_project(*md, vars[i].get(), lits)); } pr1 = mk_and(lits); qe(m.mk_true(), fml2, pr2); std::cout << mk_pp(pr1, m) << "\n"; std::cout << mk_pp(pr2, m) << "\n"; expr_ref npr2(m); npr2 = m.mk_not(pr2); ctx.push(); ctx.assert_expr(pr1); ctx.assert_expr(npr2); VERIFY(l_false == ctx.check()); ctx.pop(1); } typedef opt::model_based_opt::var var_t; static void mk_var(unsigned x, app_ref& v) { ast_manager& m = v.get_manager(); arith_util a(m); std::ostringstream strm; strm << "v" << x; v = m.mk_const(symbol(strm.str().c_str()), a.mk_real()); } static void mk_term(vector const& vars, rational const& coeff, app_ref& term) { ast_manager& m = term.get_manager(); expr_ref_vector ts(m); arith_util a(m); for (unsigned i = 0; i < vars.size(); ++i) { app_ref var(m); mk_var(vars[i].m_id, var); rational coeff = vars[i].m_coeff; ts.push_back(a.mk_mul(a.mk_numeral(coeff, false), var)); } ts.push_back(a.mk_numeral(coeff, a.mk_real())); term = a.mk_add(ts.size(), ts.c_ptr()); } static void add_random_ineq( expr_ref_vector& fmls, opt::model_based_opt& mbo, random_gen& r, svector const& values, unsigned max_vars, unsigned max_coeff) { ast_manager& m = fmls.get_manager(); arith_util a(m); unsigned num_vars = values.size(); uint_set used_vars; vector vars; int value = 0; for (unsigned i = 0; i < max_vars; ++i) { unsigned x = r(num_vars); if (used_vars.contains(x)) { continue; } used_vars.insert(x); int coeff = r(max_coeff + 1); if (coeff == 0) { continue; } unsigned sign = r(2); coeff = sign == 0 ? coeff : -coeff; vars.push_back(var_t(x, rational(coeff))); value += coeff*values[x]; } unsigned abs_value = value < 0 ? - value : value; // value + k <= 0 // k <= - value // range for k is 2*|value| // k <= - value - range opt::ineq_type rel = opt::t_le; int coeff = 0; if (r(4) == 0) { rel = opt::t_eq; coeff = -value; } else { if (abs_value > 0) { coeff = -value - r(2*abs_value); } else { coeff = 0; } if (coeff != -value && r(3) == 0) { rel = opt::t_lt; } } expr_ref fml(m); app_ref t1(m); app_ref t2(a.mk_numeral(rational(0), a.mk_real()), m); mk_term(vars, rational(coeff), t1); switch (rel) { case opt::t_eq: fml = m.mk_eq(t1, t2); break; case opt::t_lt: fml = a.mk_lt(t1, t2); break; case opt::t_le: fml = a.mk_le(t1, t2); break; case opt::t_mod: NOT_IMPLEMENTED_YET(); break; } fmls.push_back(fml); mbo.add_constraint(vars, rational(coeff), rel); } static void test_maximize(opt::model_based_opt& mbo, ast_manager& m, unsigned num_vars, expr_ref_vector const& fmls, app* t) { qe::arith_project_plugin plugin(m); model mdl(m); arith_util a(m); for (unsigned i = 0; i < num_vars; ++i) { app_ref var(m); mk_var(i, var); rational val = mbo.get_value(i); mdl.register_decl(var->get_decl(), a.mk_numeral(val, false)); } expr_ref ge(m), gt(m); opt::inf_eps value1 = plugin.maximize(fmls, mdl, t, ge, gt); opt::inf_eps value2 = mbo.maximize(); std::cout << "optimal: " << value1 << " " << value2 << "\n"; mbo.display(std::cout); } static void check_random_ineqs(random_gen& r, ast_manager& m, unsigned num_vars, unsigned max_value, unsigned num_ineqs, unsigned max_vars, unsigned max_coeff) { opt::model_based_opt mbo; expr_ref_vector fmls(m); svector values; for (unsigned i = 0; i < num_vars; ++i) { values.push_back(r(max_value + 1)); mbo.add_var(rational(values.back())); } for (unsigned i = 0; i < num_ineqs; ++i) { add_random_ineq(fmls, mbo, r, values, max_vars, max_coeff); } vector vars; vars.reset(); vars.push_back(var_t(0, rational(2))); vars.push_back(var_t(1, rational(-2))); mbo.set_objective(vars, rational(0)); mbo.display(std::cout); app_ref t(m); mk_term(vars, rational(0), t); test_maximize(mbo, m, num_vars, fmls, t); for (unsigned i = 0; i < values.size(); ++i) { std::cout << i << ": " << values[i] << " -> " << mbo.get_value(i) << "\n"; } } static void check_random_ineqs() { random_gen r(1); ast_manager m; reg_decl_plugins(m); for (unsigned i = 0; i < 100; ++i) { check_random_ineqs(r, m, 4, 5, 5, 3, 6); } } static void test_project() { ast_manager m; reg_decl_plugins(m); qe::arith_project_plugin plugin(m); arith_util a(m); app_ref_vector vars(m); expr_ref_vector lits(m), ds(m); model mdl(m); app_ref x(m), y(m), z(m), u(m); x = m.mk_const(symbol("x"), a.mk_int()); y = m.mk_const(symbol("y"), a.mk_int()); z = m.mk_const(symbol("z"), a.mk_int()); u = m.mk_const(symbol("u"), a.mk_int()); func_decl_ref f(m); sort* int_sort = a.mk_int(); f = m.mk_func_decl(symbol("f"), 1, &int_sort, int_sort); // test non-projection mdl.register_decl(x->get_decl(), a.mk_int(0)); mdl.register_decl(y->get_decl(), a.mk_int(1)); mdl.register_decl(z->get_decl(), a.mk_int(2)); mdl.register_decl(u->get_decl(), a.mk_int(3)); func_interp* fi = alloc(func_interp, m, 1); expr_ref_vector nums(m); nums.push_back(a.mk_int(0)); nums.push_back(a.mk_int(1)); nums.push_back(a.mk_int(2)); fi->insert_new_entry(nums.c_ptr(), a.mk_int(1)); fi->insert_new_entry(nums.c_ptr()+1, a.mk_int(2)); fi->insert_new_entry(nums.c_ptr()+2, a.mk_int(3)); fi->set_else(a.mk_int(10)); mdl.register_decl(f, fi); vars.reset(); lits.reset(); vars.push_back(x); lits.push_back(x <= app_ref(m.mk_app(f, (expr*)x), m)); lits.push_back(x < y); plugin(mdl, vars, lits); std::cout << lits << "\n"; // test not-equals vars.reset(); lits.reset(); vars.push_back(x); lits.push_back(m.mk_not(m.mk_eq(x, y))); plugin(mdl, vars, lits); std::cout << lits << "\n"; // test negation of distinct using bound variables mdl.register_decl(x->get_decl(), a.mk_int(0)); mdl.register_decl(y->get_decl(), a.mk_int(1)); mdl.register_decl(z->get_decl(), a.mk_int(0)); mdl.register_decl(u->get_decl(), a.mk_int(6)); vars.reset(); lits.reset(); ds.reset(); vars.push_back(x); vars.push_back(y); ds.push_back(x); ds.push_back(y); ds.push_back(z + 2); ds.push_back(u); ds.push_back(z); lits.push_back(m.mk_not(m.mk_distinct(ds.size(), ds.c_ptr()))); plugin(mdl, vars, lits); std::cout << lits << "\n"; // test negation of distinct, not using bound variables mdl.register_decl(x->get_decl(), a.mk_int(0)); mdl.register_decl(y->get_decl(), a.mk_int(1)); mdl.register_decl(z->get_decl(), a.mk_int(0)); mdl.register_decl(u->get_decl(), a.mk_int(6)); vars.reset(); lits.reset(); ds.reset(); vars.push_back(x); vars.push_back(y); ds.push_back(x); ds.push_back(y); ds.push_back(z + 2); ds.push_back(u); ds.push_back(z + 10); ds.push_back(u + 4); lits.push_back(m.mk_not(m.mk_distinct(ds.size(), ds.c_ptr()))); plugin(mdl, vars, lits); std::cout << lits << "\n"; // test distinct mdl.register_decl(x->get_decl(), a.mk_int(0)); mdl.register_decl(y->get_decl(), a.mk_int(1)); mdl.register_decl(z->get_decl(), a.mk_int(0)); mdl.register_decl(u->get_decl(), a.mk_int(6)); vars.reset(); lits.reset(); ds.reset(); vars.push_back(x); vars.push_back(y); ds.push_back(x); ds.push_back(y); ds.push_back(z + 2); ds.push_back(u); lits.push_back(m.mk_distinct(ds.size(), ds.c_ptr())); plugin(mdl, vars, lits); std::cout << lits << "\n"; // equality over modulus mdl.register_decl(y->get_decl(), a.mk_int(4)); mdl.register_decl(z->get_decl(), a.mk_int(8)); lits.reset(); vars.reset(); vars.push_back(y); lits.push_back(m.mk_eq(a.mk_mod(y, a.mk_int(3)), a.mk_int(1))); lits.push_back(m.mk_eq(2*y, z)); plugin(mdl, vars, lits); std::cout << lits << "\n"; // inequality test mdl.register_decl(x->get_decl(), a.mk_int(0)); mdl.register_decl(y->get_decl(), a.mk_int(1)); mdl.register_decl(z->get_decl(), a.mk_int(0)); mdl.register_decl(u->get_decl(), a.mk_int(6)); vars.reset(); lits.reset(); vars.push_back(x); vars.push_back(y); lits.push_back(z <= (x + (2*y))); lits.push_back(2*x < u + 3); lits.push_back(2*y <= u); plugin(mdl, vars, lits); std::cout << lits << "\n"; // non-unit equalities mdl.register_decl(x->get_decl(), a.mk_int(1)); mdl.register_decl(z->get_decl(), a.mk_int(2)); mdl.register_decl(u->get_decl(), a.mk_int(3)); mdl.register_decl(y->get_decl(), a.mk_int(4)); lits.reset(); vars.reset(); vars.push_back(x); lits.push_back(m.mk_eq(2*x, z)); lits.push_back(m.mk_eq(3*x, u)); plugin(mdl, vars, lits); std::cout << lits << "\n"; } void tst_qe_arith() { test_project(); return; check_random_ineqs(); return; // enable_trace("qe"); testI(example8); testR(example7); test_ineqs(); return; testR(example1); testR(example2); testR(example3); testR(example4); testR(example5); return; test2(example6); return; } z3-z3-4.8.7/src/test/quant_elim.cpp000066400000000000000000000454751356505360400170560ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "ast/ast.h" #include "smt/params/smt_params.h" #include "qe/qe.h" #include "ast/ast_pp.h" #include "util/lbool.h" #include #include "ast/reg_decl_plugins.h" #if 0 static void test_qe(ast_manager& m, lbool expected_outcome, expr* fml, char const* option) { // enable_trace("bit2int"); //enable_trace("gomory_cut"); enable_trace("final_check_arith"); enable_trace("arith_final_check"); //enable_trace("arith_branching"); enable_trace("theory_arith_int"); enable_trace("presburger"); enable_trace("quant_elim"); // enable_trace("arith_simplifier_plugin"); // enable_trace("non_linear"); // enable_trace("gomory_cut_detail"); // enable_trace("arith"); // enable_trace("bv"); // enable_trace("after_search"); // enable_trace("bv_bit_prop"); smt_params params; // params.m_quant_elim = true; std::cout << mk_pp(fml, m) << "\n"; qe::expr_quant_elim qe(m, params); expr_ref result(m); qe(m.mk_true(), fml, result); std::cout << " -> " << mk_pp(result, m) << " " << expected_outcome << "\n"; if (expected_outcome == l_true && !m.is_true(result)) { std::cout << "ERROR: expected true, instead got " << mk_pp(result, m) << "\n"; //exit(-1); } if (expected_outcome == l_false && !m.is_false(result)) { std::cout << "ERROR: expected false, instead got " << mk_pp(result, m) << "\n"; //exit(-1); } } #endif static void test_formula(lbool expected_outcome, char const* fml) { ast_manager m; reg_decl_plugins(m); // No-op requires SMTLIB2 #if 0 scoped_ptr parser = smtlib::parser::create(m); parser->initialize_smtlib(); std::ostringstream buffer; buffer << "(benchmark presburger :status unknown :logic AUFLIA :extrapreds ((p1) (p2) (p3)) " << ":extrafuns ((a Int) (b Int))\n" << ":extrapreds ((p) (q) (r))\n" << ":datatypes ((list (nil) (cons (hd Int) (tl list))))\n" << ":datatypes ((cell (cnil) (ccons (car cell) (cdr cell))))\n" << ":extrasorts (U)\n" << ":extrafuns ((f U U))\n" << ":formula " << fml << ")"; parser->parse_string(buffer.str().c_str()); smtlib::benchmark* b = parser->get_benchmark(); smtlib::theory::expr_iterator it = b->begin_formulas(); smtlib::theory::expr_iterator end = b->end_formulas(); for (; it != end; ++it) { test_qe(m, expected_outcome, *it, 0); } #endif } void tst_quant_elim() { disable_debug("heap"); test_formula(l_undef, "(exists ((p1 Bool) (q1 Bool) (r1 Bool))\ (and (or (not p1) (not q1) r1)\ (or (and (not p) (not q) (not p1) q1)\ (and (not p) q p1 (not q1))\ (and p (not q) p1 q1)\ (and p q p1 q1))\ (or (and (not r) (not r1))\ (and (= p p1) (= q q1) r r1)\ (and (not (and (= p p1) (= q q1))) (not (= r r1))))))"); test_formula(l_false,"(forall (x Int) (y Int) (or (= x 0) (< (* 5 y) (* 6 x)) (> (* 5 y) (* 6 x))))"); test_formula(l_false, "(forall (a Int) (b Int) (exists (x Int) (and (< a (* 20 x)) (< (* 20 x) b))))"); test_formula(l_undef, "(exists (u U) (= (f u) u))"); test_formula(l_true, "(exists (l Int) (forall (x Int) (implies (>= x l) " " (exists (u Int) (v Int) (and (>= u 0) (>= v 0) (= x (+ (* 3 u) (* 7 v))))))))"); test_formula(l_true, "(forall (x Int) (y Int) (implies (= (* 6 x) (* 5 y)) (exists (d Int) (= y (* 3 d)))))"); test_formula(l_undef, "(exists (x Int) (= (- a (mod x 4)) 0))"); // return; // test_formula(l_true, "(exists (x Int) (y Int) (= 1 (+ (* 5 x) (* 3 y))))"); test_formula(l_undef, "(exists (a Bool) (b Bool) (or (and p1 a) (and p2 (not b))))"); test_formula(l_false, "(forall (x Int) (q1 Int) (q2 Int) (r1 Int) (r2 Int) " " (implies " " (and (< x 4699) " " (= (* 2622 x) (+ (* 65536 q1) r1)) " " (<= 0 q1) " " (<= 0 r1) " " (< r1 65536) " " (= x (+ (* 100 q2) r2)) " " (<= 0 q2) " " (<= 0 r2) " " (< r2 100)) " " (= q1 q2)))"); test_formula(l_undef, "(forall (l list) (or (= l nil) (exists (x Int) (ll list) (= l (cons x ll)))))"); test_formula(l_false, "(exists (x Real) (forall (y Real) (>= x y)))"); test_formula(l_false, "(exists (x Real) (forall (y Real) (> x y)))"); test_formula(l_false, "(exists (x Real) (forall (y Real) (< x y)))"); test_formula(l_false, "(exists (x Real) (forall (y Real) (<= x y)))"); test_formula(l_true, "(exists (x Real) (exists (y Real) (< x y)))"); test_formula(l_true, "(exists (x Real) (exists (y Real) (<= x y)))"); test_formula(l_true, "(exists (x Real) (exists (y Real) (>= x y)))"); test_formula(l_true, "(exists (x Real) (exists (y Real) (> x y)))"); test_formula(l_true, "(forall (x Real) (exists (y Real) (< x y)))"); test_formula(l_true, "(forall (x Real) (exists (y Real) (<= x y)))"); test_formula(l_true, "(forall (x Real) (exists (y Real) (>= x y)))"); test_formula(l_true, "(forall (x Real) (exists (y Real) (> x y)))"); test_formula(l_false, "(forall (x Real) (forall (y Real) (< x y)))"); test_formula(l_false, "(forall (x Real) (forall (y Real) (<= x y)))"); test_formula(l_false, "(forall (x Real) (forall (y Real) (>= x y)))"); test_formula(l_false, "(forall (x Real) (forall (y Real) (> x y)))"); test_formula(l_true, "(exists (l Int) (forall (x Int) (implies (>= x l) " " (exists (u Int) (v Int) (and (>= u 0) (>= v 0) (= x (+ (* 3 u) (* 5 v))))))))"); test_formula(l_false, "(forall (d Int) (implies (>= d 0) (exists (x Int) (y Int) (and (>= x 0) (>= y 0) (= d (+ (* 3 x) (* 5 y)))))))"); test_formula(l_true, "(forall (y Int) (implies (exists (d Int) (= y (* 6 d))) (exists (d Int) (= y (* 2 d)))))"); test_formula(l_true, "(forall (y Int) (implies (exists (d Int) (= y (* 65 d))) (exists (d Int) (= y (* 5 d)))))"); test_formula(l_true, "(exists (z Int) (forall (w Int) (exists (x Int) (y Int) " " (or (and (< (+ (* 3 x) w) 2) (< 1 (- (+ (* 2 x) z) w))) " " (and (< z (* 2 y)) (> z y))))))"); test_formula(l_true, "(exists (x Int) (y Int) (and (> x 0) (>= y 0) (= 1 (- (* 3 x) (* 5 y)))))"); test_formula(l_true, "(exists (a Int) (b Int) " " (and (not (= a 1)) (= a b) (or (= a (* 2 b)) (= (* 2 b) (+ 1 (* 3 a))))))"); test_formula(l_true, "(forall (x Int) (iff (and (not (= 0 (mod x 2))) (= 0 (mod (- x 1) 3))) " " (or (= 0 (mod (- x 1) 12)) (= 0 (mod (- x 7) 12)))))"); test_formula(l_false, "(exists (x Int) (and (< (* 3 x) 2) (< 1 (* 2 x))))"); test_formula(l_true, "(forall (x Int) (y Int) (or (= 0 (mod x 5)) (not (= (* 6 x) (* 5 y)))))"); test_formula(l_false, "(forall (x Int) (exists (y Int) (= x (* 2 y))))"); test_formula(l_false, "(forall (x Int) " " (implies (not (= 0 (mod x 2))) " " (or (= 0 (mod (- x 1) 4)) " " (= 0 (mod (- x 1) 8)) " " (= 0 (mod (- x 3) 8)) " " (= 0 (mod (- x 1) 6)) " " (= 0 (mod (- x 1) 14)) " " (= 0 (mod (- x 9) 14)) " " (= 0 (mod (- x 11) 14)) " " (= 0 (mod (- x 5) 24)) " " (= 0 (mod (- x 11) 24))))) "); test_formula(l_true, "(forall (x Int) (iff (and (not (= 0 (mod x 2))) (= 0 (mod (- x 1) 3))) " " (or (= 0 (mod (- x 1) 12)) (= 0 (mod (- x 7) 12)))))"); test_formula(l_false, "(forall (d Int) (c Int) (b Int) " " (and (= c 0) (= d (* b c)) (= d 0)))"); //return; test_formula(l_undef, "(exists (k!12 Int) (k!11 Int) (and (= (ite (= k!11 0) 0 k!11) k!11) (not (= (ite (= k!12 (+ 1)) 1 0) 0))))"); //return; test_formula(l_false, "(forall (a Int) (b Int) (x Int) (y Int) (z Int) " " (implies (and (= (+ a 2) b) (= x (+ 1 (- b a))) (= y (- b 2)) (= z 3)) false))"); test_formula(l_false, "(exists (a Int) (b Int) " " (and (> a 1) (> b 1) (= a b) (or (= a (* 2 b)) (= (* 2 b) (+ 1 (* 3 a))))))"); test_formula(l_true, "(forall (d Int) (implies true (exists (x Int) (y Int) (and true true (= d (+ (* 3 x) (* 5 y)))))))"); // This one takes forever without bit-vectors test_formula(l_true, "(forall (d Int) (implies (>= d 8) (exists (x Int) (y Int) (and (>= x 0) (>= y 0) (= d (+ (* 3 x) (* 5 y)))))))"); test_formula(l_true, "(forall (d Int) (implies (>= d 0) (exists (x Int) (y Int) (and (>= x 0) (>= y 0) (= d (- (* 3 x) (* 5 y)))))))"); test_formula(l_false, "(exists (x Int) (y Int) (z Int) (= 1 (- (* 4 x) (* 6 y))))"); //return; test_formula(l_true, "(exists (l Int) (forall (x Int) (implies (>= x l) " " (exists (u Int) (v Int) (and (>= u 0) (>= v 0) (= x (+ (* 3 u) (* 8 v))))))))"); test_formula(l_true, "(exists (l Int) (forall (x Int) (implies (>= x l) " " (exists (u Int) (v Int) (and (>= u 0) (>= v 0) (= x (+ (* 3 u) (* 8 v))))))))"); #if 0 // too slow. test_formula(l_true, "(exists (l Int) (forall (x Int) (implies (>= x l) " " (exists (u Int) (v Int) (and (>= u 0) (>= v 0) (= x (+ (* 7 u) (* 8 v))))))))"); #endif test_formula(l_true, "(forall (x Int) (exists (y Int) (and (<= (* 2 y) x) (< x (* 2 (+ y 1))))))"); test_formula(l_false, "(exists (x Int) (y Int) (and (> y 0) (> y (* 2 x)) (< y (+ x 2)) (= 0 (mod y 2))))"); test_formula(l_false, "(exists (x Int) (and (< (* 3 x) 3) (< 1 (* 2 x))))"); test_formula(l_true, "(exists (x Int) (and (< (* 3 x) 4) (< 1 (* 2 x))))"); test_formula(l_false, "(exists (x Int) (and (< (+ (* 3 x) 1) 10) (> (- (* 7 x) 6) 7) (= 0 (mod x 3))))"); test_formula(l_false, "(exists (x Int) (y Int) (and (< (- 1 (* 5 y)) x) (< (+ 1 y) (* 13 x)) (< (+ x 2) 0) (> y 0)))"); test_formula(l_false, "(exists (x Int) (y Int) (and (< (- 1 (* 5 y)) x) (< (+ 1 y) (* 13 x)) (< x -2)))"); test_formula(l_true, "(exists (w Int) (z Int) (y Int) (x Int) (and (< (- 1 (* 5 y)) (+ x (* 2 z))) (< (+ 1 y w (* -4 z)) (* 13 x)) (< x -2) (> z 0)))"); test_formula(l_true, "(forall (w Int) " " (exists (z Int) (y Int) (x Int) " " (and (< (- 1 (* 5 y)) (+ x (* 2 z))) " " (< (- (+ 1 y) (* 4 z)) (* 13 x)) " " (< x -2) (> z 0) (< x 10)))) "); test_formula(l_false, "(forall (d Int) (c Int) (b Int) " " (and (= c 0) (= d (* b c)) (= d 4)))"); test_formula(l_undef, "(exists (d Int) (c Int) (b Int) " " (and (= c 0) (= d (* b c)) (= d 0)))"); test_formula(l_undef, "(exists (d Int) (c Int) (b Int) " " (and (= c 0) (= d (* b c)) (= d 4)))"); // Tests from Harrison's HOL-light version of Cooper. test_formula(l_true, "(forall (x Int) (y Int) (not (= (+ 1 (* 2 x)) (* 2 y))))"); test_formula(l_false, "(exists (x Int) (y Int) (= 1 (- (* 4 x) (* 6 y))))"); // "(forall (x Int) (implies (< b x) (<= a x)))" // "(forall (x Int) (implies (< b x) (< a x)))" test_formula(l_false, "(forall (d Int) (implies (>= d 0) (exists (x Int) (y Int) (and (>= x 0) (>= y 0) (= d (+ (* 3 x) (* 5 y)))))))"); test_formula(l_true, "(forall (d Int) (implies true (exists (x Int) (y Int) (and true true (= d (+ (* 3 x) (* 5 y)))))))"); // This one takes forever without bit-vectors test_formula(l_true, "(forall (d Int) (implies (>= d 8) (exists (x Int) (y Int) (and (>= x 0) (>= y 0) (= d (+ (* 3 x) (* 5 y)))))))"); test_formula(l_true, "(forall (d Int) (implies (>= d 0) (exists (x Int) (y Int) (and (>= x 0) (>= y 0) (= d (- (* 3 x) (* 5 y)))))))"); test_formula(l_true, "(exists (x Int) (y Int) (and (> x 0) (>= y 0) (= 1 (- (* 3 x) (* 5 y)))))"); test_formula(l_false, "(exists (x Int) (y Int) (z Int) (= 1 (- (* 4 x) (* 6 y))))"); // "(forall (x Int) (implies (< b (* 3 x)) (a < (* 3 x))))" test_formula(l_false, "(forall (x Int) (y Int) (implies (<= x y) (< (+ 1 (* 2 x)) (* 2 y))))"); test_formula(l_true, "(forall (x Int) (y Int) (z Int) (implies (= (+ 1 (* 2 x)) (* 2 y)) (> (+ x y z) 129)))"); // Formula examples from Cooper's paper. test_formula(l_true, "(forall (a Int) (exists (b Int) (or (< a (+ (* 4 b) (* 3 a))) (and (not (< a b)) (> a (+ b 1))))))"); test_formula(l_false, "(exists (y Int) (forall (x Int) (and (> (+ x (* 5 y)) 1) (> (- (* 13 x) y) 1) (< (+ x 2) 0))))"); // Harrison's formulas: test_formula(l_false, "(forall (x Int) (y Int) (implies (and (>= x 0) (>= y 0)) (or (< (- (* 12 x) (* 8 y)) 0) (> (- (* 12 x) (* 8 y)) 2))))"); // test_formula(l_true, "(exists (x Int) (y Int) (= 1 (+ (* 5 x) (* 3 y))))"); test_formula(l_false, "(exists (x Int) (y Int) (= 1 (+ (* 5 x) (* 10 y))))"); test_formula(l_true, "(exists (x Int) (y Int) (and (>= x 0) (>= y 0) (= 1 (- (* 5 x) (* 6 y)))))"); test_formula(l_true, "(exists (x Int) (y Int) (z Int) (w Int) (= 1 (+ (* 2 w) (* 3 x) (* 4 y) (* 5 z))))"); test_formula(l_true, "(exists (x Int) (y Int) (and (>= x 0) (>= y 0) (= 1 (- (* 5 x) (* 3 y)))))"); test_formula(l_true, "(exists (x Int) (y Int) (and (>= x 0) (>= y 0) (= 1 (- (* 3 x) (* 5 y)))))"); test_formula(l_false,"(exists (x Int) (y Int) (and (>= x 0) (>= y 0) (= 1 (- (* 6 x) (* 3 y)))))"); test_formula(l_true, "(forall (x Int) (y Int) (or (= 0 (mod x 5)) (= 0 (mod y 6)) (not (= (* 6 x) (* 5 y)))))"); test_formula(l_false,"(forall (x Int) (y Int) (or (not (= (* 6 x) (* 5 y)))))"); // Positive variant of the Bezout theorem (see the exercise). *) test_formula(l_true, "(forall (z Int) (implies (> z 7) (exists (x Int) (y Int) (and (>= x 0) (>= y 0) (= (+ (* 3 x) (* 5 y)) z)))))"); test_formula(l_false,"(forall (z Int) (implies (> z 2) (exists (x Int) (y Int) (and (>= x 0) (>= y 0) (= (+ (* 3 x) (* 5 y)) z)))))"); test_formula(l_true, "(forall (z Int) (implies (<= z 7) " " (iff (exists (x Int) (y Int) (and (>= x 0) (>= y 0) (= z (+ (* 3 x) (* 5 y))))) " " (not (exists (x Int) (y Int) (and (>= x 0) (>= y 0) (= (- 7 z) (+ (* 3 x) (* 5 y))))))))) "); // Basic result about congruences. test_formula(l_true, "(forall (x Int) " " (iff (and (not (exists (m Int) (= x (* 2 m)))) (exists (m Int) (= x (+ (* 3 m) 1)))) " " (or (exists (m Int) (= x (+ (* 12 m) 1))) (exists (m Int) (= x (+ (* 12 m) 7))))))"); // Inspired by the Collatz conjecture. test_formula(l_false, "(forall (a Int) (b Int) (x Int) (y Int) (z Int) " " (implies (and (= (+ a 2) b) (= x (+ 1 (- b a))) (= y (- b 2)) (= z 3)) false))"); test_formula(l_true, "(exists (a Int) (b Int) " " (and (not (= a 1)) (= a b) (or (= a (* 2 b)) (= (* 2 b) (+ 1 (* 3 a))))))"); test_formula(l_false, "(exists (a Int) (b Int) " " (and (> a 1) (> b 1) (= a b) (or (= a (* 2 b)) (= (* 2 b) (+ 1 (* 3 a))))))"); test_formula(l_false, "(exists (a Int) (b Int) " " (and (> a 1) (> b 1) " " (or (= a (* 2 b)) (= (* 2 b) (+ 1 (* 3 a)))) " " (or (= b (* 2 a)) (= (* 2 a) (+ 1 (* 3 b))))))"); #if 0 // Bob Constable's "stamp problem". test_formula(l_true, "(forall (x Int) (implies (>= x 8) " " (exists (u Int) (v Int) (and (>= u 0) (>= v 0) (= x (+ (* 3 u) (* 5 v)))))))"); test_formula(l_true, "(exists (l Int) (forall (x Int) (implies (>= x l) " " (exists (u Int) (v Int) (and (>= u 0) (>= v 0) (= x (+ (* 3 u) (* 5 v))))))))"); test_formula(l_true, "(exists (l Int) (forall (x Int) (implies (>= x l) " " (exists (u Int) (v Int) (and (>= u 0) (>= v 0) (= x (+ (* 3 u) (* 7 v))))))))"); test_formula(l_true, "(exists (l Int) (forall (x Int) (implies (>= x l) " " (exists (u Int) (v Int) (and (>= u 0) (>= v 0) (= x (+ (* 3 u) (* 8 v))))))))"); test_formula(l_true, "(exists (l Int) (forall (x Int) (implies (>= x l) " " (exists (u Int) (v Int) (and (>= u 0) (>= v 0) (= x (+ (* 7 u) (* 8 v))))))))"); #endif // Example from reciprocal mult: (2622 * x)>>16 = x/100 within a range. test_formula(l_true, "(forall (x Int) (y Int) " " (iff (exists (d Int) (= (+ x y) (* 2 d))) " " (iff (exists (d Int) (= x (* 2 d))) (exists (d Int) (= y (* 2 d))))))"); test_formula(l_true, "(forall (n Int) " " (implies (and (< 0 n) (< n 2400)) " " (or (and (<= n 2) (<= 2 (* 2 n))) " " (and (<= n 3) (<= 3 (* 2 n))) " " (and (<= n 5) (<= 5 (* 2 n))) " " (and (<= n 7) (<= 7 (* 2 n))) " " (and (<= n 13) (<= 13 (* 2 n))) " " (and (<= n 23) (<= 23 (* 2 n))) " " (and (<= n 43) (<= 43 (* 2 n))) " " (and (<= n 83) (<= 83 (* 2 n))) " " (and (<= n 163) (<= 163 (* 2 n))) " " (and (<= n 317) (<= 317 (* 2 n))) " " (and (<= n 631) (<= 631 (* 2 n))) " " (and (<= n 1259) (<= 1259 (* 2 n))) " " (and (<= n 2503) (<= 2503 (* 2 n)))))) "); memory::finalize(); #ifdef _WINDOWS _CrtDumpMemoryLeaks(); #endif exit(0); } z3-z3-4.8.7/src/test/quant_solve.cpp000066400000000000000000000226401356505360400172450ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "ast/ast.h" #include "smt/params/smt_params.h" #include "qe/qe.h" #include "ast/arith_decl_plugin.h" #include "ast/ast_pp.h" #include "util/lbool.h" #include #include "ast/rewriter/expr_replacer.h" #include "smt/smt_kernel.h" #include "ast/reg_decl_plugins.h" #include "ast/expr_abstract.h" #include "model/model_smt2_pp.h" #include "parsers/smt2/smt2parser.h" #include "ast/rewriter/var_subst.h" static void validate_quant_solution(ast_manager& m, expr* fml, expr* guard, qe::def_vector const& defs) { // verify: // new_fml => fml[t/x] scoped_ptr rep = mk_expr_simp_replacer(m); app_ref_vector xs(m); expr_substitution sub(m); for (unsigned i = 0; i < defs.size(); ++i) { xs.push_back(m.mk_const(defs.var(i))); sub.insert(xs.back(), defs.def(i)); } rep->set_substitution(&sub); expr_ref fml1(fml, m); (*rep)(fml1); expr_ref tmp(m); tmp = m.mk_not(m.mk_implies(guard, fml1)); std::cout << "validating: " << mk_pp(tmp, m) << "\n"; smt_params fp; smt::kernel solver(m, fp); solver.assert_expr(tmp); lbool res = solver.check(); //ENSURE(res == l_false); if (res != l_false) { std::cout << "Validation failed: " << res << "\n"; std::cout << mk_pp(tmp, m) << "\n"; model_ref model; solver.get_model(model); model_smt2_pp(std::cout, m, *model, 0); fatal_error(0); } } #if 0 static void validate_quant_solutions(app* x, expr* fml, expr_ref_vector& guards) { return; // quant_elim option got removed... // verify: // fml <=> guard_1 \/ guard_2 \/ ... ast_manager& m = guards.get_manager(); expr_ref tmp(m), fml2(m); tmp = m.mk_or(guards.size(), guards.c_ptr()); expr* _x = x; std::cout << mk_pp(fml, m) << "\n"; expr_abstract(m, 0, 1, &_x, fml, fml2); std::cout << mk_pp(fml2, m) << "\n"; symbol name(x->get_decl()->get_name()); sort* s = m.get_sort(x); fml2 = m.mk_exists(1, &s, &name, fml2); std::cout << mk_pp(fml2, m) << "\n"; tmp = m.mk_not(m.mk_iff(fml2, tmp)); std::cout << mk_pp(tmp, m) << "\n"; smt_params fp; smt::kernel solver(m, fp); solver.assert_expr(tmp); lbool res = solver.check(); std::cout << "checked\n"; ENSURE(res == l_false); if (res != l_false) { std::cout << res << "\n"; fatal_error(0); } } #endif static void test_quant_solver(ast_manager& m, unsigned sz, app*const* xs, expr* fml, bool validate) { smt_params params; qe::expr_quant_elim qe(m, params); qe::guarded_defs defs(m); bool success = qe.solve_for_vars(sz, xs, fml, defs); std::cout << "------------------------\n"; std::cout << mk_pp(fml, m) << "\n"; if (success) { defs.display(std::cout); for (unsigned i = 0; validate && i < defs.size(); ++i) { validate_quant_solution(m, fml, defs.guard(i), defs.defs(i)); } } else { std::cout << "failed\n"; } } static expr_ref parse_fml(ast_manager& m, char const* str) { expr_ref result(m); cmd_context ctx(false, &m); ctx.set_ignore_check(true); std::ostringstream buffer; buffer << "(declare-const x Int)\n" << "(declare-const y Int)\n" << "(declare-const z Int)\n" << "(declare-const a Int)\n" << "(declare-const b Int)\n" << "(declare-const P Bool)\n" << "(declare-const Q Bool)\n" << "(declare-const r1 Real)\n" << "(declare-const r2 Real)\n" << "(declare-datatypes () ((IList (nil) (cons (car Int) (cdr IList)))))\n" << "(declare-const l1 IList)\n" << "(declare-const l2 IList)\n" << "(declare-datatypes () ((Cell (null) (cell (car Cell) (cdr Cell)))))\n" << "(declare-const c1 Cell)\n" << "(declare-const c2 Cell)\n" << "(declare-const c3 Cell)\n" << "(declare-datatypes () ((Tuple (tuple (first Int) (second Bool) (third Real)))))\n" << "(declare-const t1 Tuple)\n" << "(declare-const t2 Tuple)\n" << "(declare-const t3 Tuple)\n" << "(assert " << str << ")\n"; std::istringstream is(buffer.str()); VERIFY(parse_smt2_commands(ctx, is)); result = ctx.assertions().get(0); return result; } static void parse_fml(char const* str, app_ref_vector& vars, expr_ref& fml) { ast_manager& m = fml.get_manager(); fml = parse_fml(m, str); if (is_exists(fml)) { quantifier* q = to_quantifier(fml); for (unsigned i = 0; i < q->get_num_decls(); ++i) { vars.push_back(m.mk_const(q->get_decl_name(i), q->get_decl_sort(i))); } fml = q->get_expr(); var_subst vs(m, true); fml = vs(fml, vars.size(), (expr*const*)vars.c_ptr()); } } static void test_quant_solver(ast_manager& m, app* x, char const* str, bool validate = true) { expr_ref fml = parse_fml(m, str); test_quant_solver(m, 1, &x, fml, validate); } static void test_quant_solver(ast_manager& m, unsigned sz, app*const* xs, char const* str, bool validate = true) { expr_ref fml = parse_fml(m, str); test_quant_solver(m, sz, xs, fml, validate); } static void test_quant_solver(ast_manager& m, char const* str, bool validate = true) { expr_ref fml(m); app_ref_vector vars(m); parse_fml(str, vars, fml); test_quant_solver(m, vars.size(), vars.c_ptr(), fml, validate); } static void test_quant_solve1() { ast_manager m; arith_util ar(m); reg_decl_plugins(m); sort* i = ar.mk_int(); app_ref xr(m.mk_const(symbol("x"),i), m); app_ref yr(m.mk_const(symbol("y"),i), m); app* x = xr.get(); app* y = yr.get(); app* xy[2] = { x, y }; test_quant_solver(m, x, "(and (<= (* 2 x) y) (>= x z) (= (mod x 2) 0))"); test_quant_solver(m, x, "(and (<= x y) (= (mod x 2) 0))"); test_quant_solver(m, x, "(and (<= (* 2 x) y) (= (mod x 2) 0))"); test_quant_solver(m, x, "(and (>= x y) (= (mod x 2) 0))"); test_quant_solver(m, x, "(and (>= (* 2 x) y) (= (mod x 2) 0))"); test_quant_solver(m, x, "(and (<= (* 2 x) y) (>= (* 3 x) z) (= (mod x 2) 0))"); test_quant_solver(m, x, "(>= (* 2 x) a)"); test_quant_solver(m, x, "(<= (* 2 x) a)"); test_quant_solver(m, x, "(< (* 2 x) a)"); test_quant_solver(m, x, "(= (* 2 x) a)"); test_quant_solver(m, x, "(< (* 2 x) a)"); test_quant_solver(m, x, "(> (* 2 x) a)"); test_quant_solver(m, x, "(and (<= a x) (<= (* 2 x) b))"); test_quant_solver(m, x, "(and (<= a x) (<= x b))"); test_quant_solver(m, x, "(and (<= (* 2 a) x) (<= x b))"); test_quant_solver(m, x, "(and (<= (* 2 a) x) (<= (* 2 x) b))"); test_quant_solver(m, x, "(and (<= a x) (<= (* 3 x) b))"); test_quant_solver(m, x, "(and (<= (* 3 a) x) (<= x b))"); test_quant_solver(m, x, "(and (<= (* 3 a) x) (<= (* 3 x) b))"); test_quant_solver(m, x, "(and (< a (* 3 x)) (< (* 3 x) b))"); test_quant_solver(m, x, "(< (* 3 x) a)"); test_quant_solver(m, x, "(= (* 3 x) a)"); test_quant_solver(m, x, "(< (* 3 x) a)"); test_quant_solver(m, x, "(> (* 3 x) a)"); test_quant_solver(m, x, "(<= (* 3 x) a)"); test_quant_solver(m, x, "(>= (* 3 x) a)"); test_quant_solver(m, x, "(<= (* 2 x) a)"); test_quant_solver(m, x, "(or (= (* 2 x) y) (= (+ (* 2 x) 1) y))"); test_quant_solver(m, x, "(= x a)"); test_quant_solver(m, x, "(< x a)"); test_quant_solver(m, x, "(> x a)"); test_quant_solver(m, x, "(and (> x a) (< x b))"); test_quant_solver(m, x, "(and (> x a) (< x b))"); test_quant_solver(m, x, "(<= x a)"); test_quant_solver(m, x, "(>= x a)"); test_quant_solver(m, x, "(and (<= (* 2 x) y) (= (mod x 2) 0))"); test_quant_solver(m, x, "(= (* 2 x) y)"); test_quant_solver(m, x, "(or (< x 0) (> x 1))"); test_quant_solver(m, x, "(or (< x y) (> x y))"); test_quant_solver(m, x, "(= x y)"); test_quant_solver(m, x, "(<= x y)"); test_quant_solver(m, x, "(>= x y)"); test_quant_solver(m, x, "(and (<= (+ x y) 0) (<= (+ x z) 0))"); test_quant_solver(m, x, "(and (<= (+ x y) 0) (<= (+ (* 2 x) z) 0))"); test_quant_solver(m, x, "(and (<= (+ (* 3 x) y) 0) (<= (+ (* 2 x) z) 0))"); test_quant_solver(m, x, "(and (>= x y) (>= x z))"); test_quant_solver(m, x, "(< x y)"); test_quant_solver(m, x, "(> x y)"); test_quant_solver(m, 2, xy, "(and (<= (- (* 2 y) b) (+ (* 3 x) a)) (<= (- (* 2 x) a) (+ (* 4 y) b)))"); test_quant_solver(m, "(exists ((c Cell)) (= c null))"); test_quant_solver(m, "(exists ((c Cell)) (= c (cell null c1)))"); test_quant_solver(m, "(exists ((c Cell)) (not (= c null)))", false); test_quant_solver(m, "(exists ((c Cell)) (= (cell c c) c1))", false); test_quant_solver(m, "(exists ((c Cell)) (= (cell c (cdr c1)) c1))", false); test_quant_solver(m, "(exists ((t Tuple)) (= (tuple a P r1) t))"); test_quant_solver(m, "(exists ((t Tuple)) (= a (first t)))"); test_quant_solver(m, "(exists ((t Tuple)) (= P (second t)))"); test_quant_solver(m, "(exists ((t Tuple)) (= r2 (third t)))"); test_quant_solver(m, "(exists ((t Tuple)) (not (= a (first t))))"); test_quant_solver(m, "(exists ((t Tuple)) (not (= P (second t))))"); test_quant_solver(m, "(exists ((t Tuple)) (not (= r2 (third t))))"); } void tst_quant_solve() { disable_debug("heap"); test_quant_solve1(); #if 0 memory::finalize(); #ifdef _WINDOWS _CrtDumpMemoryLeaks(); #endif exit(0); #endif } z3-z3-4.8.7/src/test/random.cpp000066400000000000000000000006571356505360400161710ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: random.cpp Abstract: Author: Leonardo de Moura (leonardo) 2007-08-24. Revision History: --*/ #include "util/util.h" #include "util/trace.h" static void tst1() { random_gen r(0); TRACE("random", for (unsigned i = 0; i < 1000; i++) { tout << r() << "\n"; }); } void tst_random() { tst1(); } z3-z3-4.8.7/src/test/rational.cpp000066400000000000000000000371261356505360400165230ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: tst_rational.cpp Abstract: Test rationals Author: Leonardo de Moura (leonardo) 2006-09-26. Revision History: --*/ #include #include "util/vector.h" #include "util/rational.h" #include "util/trace.h" #include "util/ext_gcd.h" #include "util/timeit.h" static void tst1() { rational r1(1); rational r2(1,2); rational r3(2,4); ENSURE(r2 == r3); ENSURE(r1 != r2); ENSURE(r2 + r3 == r1); ENSURE(r1.is_pos()); ENSURE((r2 - r1).is_neg()); ENSURE((r2 - r3).is_zero()); ENSURE(floor(r2).is_zero()); ENSURE(ceil(r2).is_one()); // std::cout << "-r2: " << (-r2) << ", floor(-r2):" << floor(-r2) << "\n"; ENSURE(floor(-r2).is_minus_one()); ENSURE(ceil(-r2).is_zero()); ENSURE(floor(r1) == r1); ENSURE(ceil(r1) == r1); rational r4(3,5); ENSURE(r3 * r4 == rational(3, 10)); ENSURE(r3 / r4 == rational(5, 6)); rational r5(2,3); ENSURE(r4 * r5 == rational(2, 5)); --r2; ENSURE(r2 == -r3); r2.neg(); ENSURE(r2 == r3); --r2; r2 = abs(r2); ENSURE(r2 == r3); --r2; ++r2; ENSURE(r2 == r3); ENSURE(r2 == abs(r2)); ENSURE(r4 * rational(1) == r4); ENSURE((r4 * rational(0)).is_zero()); ENSURE(r4 * rational(-1) == -r4); ENSURE(rational(1) * r4 == r4); ENSURE((rational(0) * r4).is_zero()); ENSURE(rational(-1) * r4 == -r4); ENSURE(r4 + rational(0) == r4); ENSURE(ceil(r4).is_one()); // std::cout << "r3: " << r3 << ", r4: " << r4 << ", -r4: " << -r4 << ", r3 / (-r4): " << (r3 / (-r4)) << "\n"; ENSURE(r3 / (-r4) == rational(5,-6)); ENSURE(div(rational(7), rational(2)) == rational(3)); ENSURE(rational(7) % rational(4) == rational(3)); ENSURE(div(rational(7), rational(-2)) == rational(-3)); ENSURE(rational(3) + rational(5) == rational(8)); ENSURE(rational("13/10") + rational("7/10") == rational(2)); ENSURE(rational("100/20") == rational(5)); ENSURE(gcd(rational(12), rational(8)) == rational(4)); ENSURE(ceil(rational(-3,2)) == rational(-1)); ENSURE(floor(rational(-3,2)) == rational(-2)); ENSURE(ceil(rational(3,2)) == rational(2)); ENSURE(floor(rational(3,2)) == rational(1)); ENSURE(rational(3).is_pos()); ENSURE(rational(0).is_nonneg()); ENSURE(rational(3).is_pos()); ENSURE(rational(3).is_nonneg()); ENSURE(rational(0).is_nonneg()); ENSURE(!rational(3).is_zero()); ENSURE(!rational(-3).is_zero()); ENSURE(rational(0).is_zero()); ENSURE(rational(1).is_one()); ENSURE(!rational(2).is_one()); ENSURE(rational(3,4) >= rational(2,8)); ENSURE(rational(3,4) <= rational(7,8)); ENSURE(rational(3,4) <= rational(3,4)); ENSURE(rational(3,4) >= rational(3,4)); ENSURE(rational(3,4) > rational(2,8)); ENSURE(rational(3,4) < rational(7,8)); TRACE("rational", tout << rational(3,4) << "\n";); TRACE("rational", tout << rational(7,9) << "\n";); TRACE("rational", tout << rational(-3,7) << "\n";); TRACE("rational", tout << rational(5,8) << "\n";); TRACE("rational", tout << rational(4,2) << "\n";); ENSURE(rational(3) + rational(2) == rational(5)); ENSURE(rational(3) - rational(2) == rational(1)); ENSURE(rational(3) * rational(2) == rational(6)); ENSURE(rational(6) / rational(2) == rational(3)); ENSURE(rational(6) % rational(4) == rational(2)); ENSURE(power(rational(2),0) == rational(1)); ENSURE(power(rational(2),1) == rational(2)); ENSURE(power(rational(2),3) == rational(8)); } static void tst2() { rational r1("10000000000000000000000000000000000"); rational r2("10000000000000000000000000000000000/3"); rational r3("20000000000000000000000000000000000/6"); TRACE("rational", tout << r1 << std::endl;); TRACE("rational", tout << r2 << std::endl;); TRACE("rational", tout << r3 << std::endl;); ENSURE(r2 == r3); ENSURE(r1 != r2); ENSURE(rational(2)*r2 + r3 == r1); ENSURE(r1.is_pos()); ENSURE((r2 - r1).is_neg()); ENSURE((r2 - r3).is_zero()); // std::cout << "===> " << floor(r2) << "\n"; { rational r0("1/3000000000000000000000000"); ENSURE(ceil(r0).is_one()); ENSURE(floor(-r0).is_minus_one()); ENSURE(ceil(-r0).is_zero()); } ENSURE(floor(r1) == r1); ENSURE(ceil(r1) == r1); rational r4("300000000/5"); ENSURE(rational(1,2) * r4 == rational("300000000/10")); ENSURE(rational(1,2) / r4 == rational("5/600000000")); rational r5(2,3); ENSURE(r4 * r5 == rational("200000000/5")); rational r6("10000000000000000000000000000000003/3"); --r6; ENSURE(r6 == r2); r6.neg(); ENSURE(r6 != r2); ENSURE(abs(r6) == r2); --r2; ++r2; r2.neg(); ENSURE(r2 == r6); ENSURE(r6 * rational(1) == r6); ENSURE((r6 * rational(0)).is_zero()); ENSURE(r6 * rational(-1) == -r6); ENSURE(rational(1) * r6 == r6); ENSURE((rational(0) * r6).is_zero()); ENSURE(rational(-1) * r6 == -r6); ENSURE(r6 + rational(0) == r6); ENSURE(rational("300000000000000").is_pos()); ENSURE(rational("0000000000000000000").is_nonneg()); ENSURE(rational("0000000000000000000").is_nonpos()); ENSURE(rational("3000000000000000000/2").is_pos()); ENSURE(rational("3000000000000000000/2").is_nonneg()); ENSURE((-rational("3000000000000000000/2")).is_neg()); ENSURE(!rational("3000000000000000000/2").is_neg()); ENSURE(!rational("3000000000000000000/2").is_zero()); ENSURE(!rational("3000000000000000000/2").is_one()); ENSURE(rational("99999999999/2") >= rational("23/2")); ENSURE(rational("99999999999/2") > rational("23/2")); ENSURE(rational("23/2") <= rational("99999999999/2")); ENSURE(rational("23/2") < rational("99999999999/2")); ENSURE(!(rational("99999999999/2") < rational("23/2"))); rational int64_max("9223372036854775807"); rational int64_min((-int64_max) - rational(1)); // is_int64 ENSURE(int64_max.is_int64()); ENSURE(int64_min.is_int64()); ENSURE(rational(0).is_int64()); ENSURE(rational(1).is_int64()); ENSURE(rational(-1).is_int64()); ENSURE(!(int64_max + rational(1)).is_int64()); ENSURE(!(int64_min - rational(1)).is_int64()); // is_uint64 ENSURE(int64_max.is_uint64()); ENSURE(!int64_min.is_uint64()); ENSURE(rational(0).is_uint64()); ENSURE(rational(1).is_uint64()); ENSURE(!rational(-1).is_uint64()); ENSURE((int64_max + rational(1)).is_uint64()); ENSURE(!(int64_min - rational(1)).is_uint64()); rational uint64_max(rational(1) + (rational(2) * int64_max)); ENSURE(uint64_max.is_uint64()); // get_int64, get_uint64 uint64_t u1 = uint64_max.get_uint64(); uint64_t u2 = UINT64_MAX; VERIFY(u1 == u2); std::cout << "int64_max: " << int64_max << ", INT64_MAX: " << INT64_MAX << ", int64_max.get_int64(): " << int64_max.get_int64() << ", int64_max.get_uint64(): " << int64_max.get_uint64() << "\n"; ENSURE(int64_max.get_int64() == INT64_MAX); ENSURE(int64_min.get_int64() == INT64_MIN); // extended Euclid: } void tst3() { rational n1 = power(rational(2), 32); TRACE("rational", tout << "n1: " << n1 << "\n";); rational n2 = div(n1, rational(2)); rational n3 = div(rational(2), n2); TRACE("rational", tout << "n1: " << n1 << "\n"; tout << "n2: " << n2 << "\n"; tout << "n3: " << n3 << "\n";); rational n4 = n1 - rational(3); rational n5 = div(n4, rational(2)); TRACE("rational", tout << "n4: " << n4 << "\n"; tout << "n5: " << n5 << "\n";); ENSURE(n5 == rational("2147483646")); } void tst4() { rational n1("4294967293"); TRACE("rational", tout << "n1: " << n1 << "\n";); rational n2 = div(n1, rational(2)); } void tst5() { rational n1(1); n1.neg(); rational n2("4294967295"); n1 /= n2; TRACE("rational", tout << n1 << " " << n2 << " " << n1.is_big() << " " << n2.is_big() << "\n";); n1 *= n2; TRACE("rational", tout << "after: " << n1 << " " << n2 << "\n";); ENSURE(n1.is_minus_one()); } void tst6() { rational t1(5); rational t2(3); rational a, b, g; g = gcd(t1, t2, a, b); t1 = rational(15); t2 = rational(25); g = gcd(t1, t2, a, b); t1.neg(); g = gcd(t1, t2, a, b); t2.neg(); g = gcd(t1, t2, a, b); t1.neg(); g = gcd(t1, t2, a, b); std::swap(t1, t2); g = gcd(t1, t2, a, b); t1.neg(); g = gcd(t1, t2, a, b); t2.neg(); g = gcd(t1, t2, a, b); t1.neg(); g = gcd(t1, t2, a, b); } class rational_tester { public: static void tst1() { rational n1(-1); rational n2(8); ENSURE((n1 % n2).is_minus_one()); ENSURE(mod(n1, n2) == rational(7)); } static void tst_hash(int val) { rational n1(val); rational n2("10203939394995449949494394932929"); rational n3(val); n2 = n3; ENSURE(n1.hash() == n2.hash()); } static void tst2() { tst_hash(0); for (int i = 0; i <= 10000; i++) { int r = rand() % INT_MAX; if (rand()%2 == 1) r = -r; tst_hash(r); } } }; static void tst7() { rational p; p = power(rational(2), 32); for (unsigned i = 1; i < 1000; i++) { rational n(i); rational x; rational y; rational gcd; extended_gcd(n, p, gcd, x, y); TRACE("gcd", tout << n << " " << p << ": " << gcd << " " << x << " " << y << "\n";); ENSURE(!mod(n, rational(2)).is_one() || mod(n * x, p).is_one()); } } static void tst8() { rational r; ENSURE(!rational(-4).is_int_perfect_square(r) && r.is_zero()); ENSURE(!rational(-3).is_int_perfect_square(r) && r.is_zero()); ENSURE(!rational(-2).is_int_perfect_square(r) && r.is_zero()); ENSURE(!rational(-1).is_int_perfect_square(r) && r.is_zero()); ENSURE(rational(0).is_int_perfect_square(r) && r.is_zero()); ENSURE(rational(1).is_int_perfect_square(r) && r.is_one()); ENSURE(!rational(2).is_int_perfect_square(r) && r == rational(2)); ENSURE(!rational(3).is_int_perfect_square(r) && r == rational(2)); ENSURE(rational(4).is_int_perfect_square(r) && r == rational(2)); ENSURE(!rational(5).is_int_perfect_square(r) && r == rational(3)); ENSURE(!rational(6).is_int_perfect_square(r) && r == rational(3)); ENSURE(!rational(7).is_int_perfect_square(r) && r == rational(3)); ENSURE(!rational(8).is_int_perfect_square(r) && r == rational(3)); ENSURE(rational(9).is_int_perfect_square(r) && r == rational(3)); ENSURE(!rational(10).is_int_perfect_square(r) && r == rational(4)); ENSURE(!rational(11).is_int_perfect_square(r) && r == rational(4)); ENSURE(!rational(12).is_int_perfect_square(r) && r == rational(4)); ENSURE(!rational(13).is_int_perfect_square(r) && r == rational(4)); ENSURE(!rational(14).is_int_perfect_square(r) && r == rational(4)); ENSURE(!rational(15).is_int_perfect_square(r) && r == rational(4)); ENSURE(rational(16).is_int_perfect_square(r) && r == rational(4)); ENSURE(!rational(17).is_int_perfect_square(r) && r == rational(5)); ENSURE(!rational(18).is_int_perfect_square(r) && r == rational(5)); ENSURE(!rational(19).is_int_perfect_square(r) && r == rational(5)); ENSURE(!rational(20).is_int_perfect_square(r) && r == rational(5)); ENSURE(!rational(21).is_int_perfect_square(r) && r == rational(5)); ENSURE(!rational(22).is_int_perfect_square(r) && r == rational(5)); ENSURE(!rational(23).is_int_perfect_square(r) && r == rational(5)); ENSURE(!rational(24).is_int_perfect_square(r) && r == rational(5)); ENSURE(rational(25).is_int_perfect_square(r) && r == rational(5)); ENSURE(!rational(26).is_int_perfect_square(r) && r == rational(6)); ENSURE(rational(36).is_int_perfect_square(r) && r == rational(6)); ENSURE(rational(1,9).is_perfect_square(r) && r == rational(1,3)); ENSURE(rational(4,9).is_perfect_square(r) && r == rational(2,3)); } static void tstmod(rational const& m, rational const& n) { // // (=> (distinct n 0) // (let ((q (div m n)) (r (mod m n))) // (and (= m (+ (* n q) r)) // (<= 0 r (- (abs n) 1)))))) // rational q = div(m,n); rational r = mod(m,n); std::cout << m << " " << n << " " << q << " " << r << "\n"; std::cout << m << " == " << n*q+r << "\n"; ENSURE(m == (n * q) + r); ENSURE(rational::zero() <= r); ENSURE(r < abs(n)); } static void tst9() { // record semantics of rational div/mod. tstmod(rational("41000000000000"),rational("-7000000000000")); tstmod(rational("-41000000000000"),rational("-7000000000000")); tstmod(rational("-41000000000000"),rational("7000000000000")); tstmod(rational("41000000000000"),rational("7000000000000")); tstmod(rational(41),rational(-7)); tstmod(rational(-41),rational(-7)); tstmod(rational(-41),rational(7)); tstmod(rational(41),rational(7)); } #define NUM_RATIONALS 1000000 #define MAGNITUDE 10000 static void tst10(bool use_ints) { if (use_ints) std::cout << "Testing multiplication performance using small ints\n"; else std::cout << "Testing multiplication performance using small rationals\n"; vector vals; vector vals2; vector fvals; vals.resize(NUM_RATIONALS); vals2.resize(NUM_RATIONALS); fvals.resize(NUM_RATIONALS); for (unsigned i = 0; i < NUM_RATIONALS; i++) { int r1 = rand() % MAGNITUDE; int r2 = use_ints ? 1 : rand() % MAGNITUDE; if (r2 == 0) r2 = 1; if (rand() % 2 == 0) r1 = -r1; vals[i] = rational(r1, r2); vals2[i] = rational(r1, r2); fvals[i] = ((float)r1) / ((float)r2); } { timeit t(true, "multiplication with rationals"); for (unsigned i = 0; i < NUM_RATIONALS - 1; i++) { vals[i] *= vals[i+1]; } } { timeit t(true, "multiplication with floats: "); for (unsigned i = 0; i < NUM_RATIONALS - 1; i++) { fvals[i] *= fvals[i+1]; } } std::cout << "\n"; } #define NUM_RATIONALS2 10000 #define MAGNITUDE2 100000000 static void tst11(bool use_ints) { vector vals; vector fvals; vals.resize(NUM_RATIONALS2); fvals.resize(NUM_RATIONALS2); for (unsigned i = 0; i < NUM_RATIONALS2; i++) { int r1 = rand() % MAGNITUDE2; int r2 = use_ints ? 1 : rand() % MAGNITUDE2; if (r2 == 0) r2 = 1; if (rand() % 2 == 0) r1 = -r1; vals[i] = rational(r1, r2); fvals[i] = ((float)r1) / ((float)r2); } { timeit t(true, "multiplication with big rationals"); for (unsigned j = 0; j < 10; j++) for (unsigned i = 0; i < NUM_RATIONALS2-1; i++) { vals[i] *= vals[i+1]; } } { timeit t(true, "multiplication with floats: "); for (unsigned j = 0; j < 10; j++) for (unsigned i = 0; i < NUM_RATIONALS2-1; i++) { fvals[i] *= fvals[i+1]; } } std::cout << "\n"; } void tst_rational() { TRACE("rational", tout << "starting rational test...\n";); std::cout << "sizeof(rational): " << sizeof(rational) << "\n"; rational r1("10000000000000000000000000000000001"); r1.hash(); tst1(); tst2(); tst3(); tst4(); tst5(); std::cout << "running tst6" << std::endl; tst6(); std::cout << "running tst7" << std::endl; tst7(); std::cout << "running tst8" << std::endl; tst8(); std::cout << "running tst9" << std::endl; tst9(); std::cout << "running rational_tester::tst1" << std::endl; rational_tester::tst1(); rational_tester::tst2(); tst11(true); tst10(true); tst10(false); } z3-z3-4.8.7/src/test/rcf.cpp000066400000000000000000000117541356505360400154630ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: rcf.cpp Abstract: Testing RCF module Author: Leonardo (leonardo) 2013-01-04 Notes: --*/ #include "math/realclosure/realclosure.h" #include "math/realclosure/mpz_matrix.h" #include "util/rlimit.h" static void tst1() { unsynch_mpq_manager qm; reslimit rl; rcmanager m(rl, qm); scoped_rcnumeral a(m); #if 0 a = 10; std::cout << sym_pp(a) << std::endl; std::cout << sym_pp(eps) << std::endl; std::cout << interval_pp(a) << std::endl; std::cout << interval_pp(eps) << std::endl; #endif scoped_rcnumeral eps(m); m.mk_infinitesimal(eps); mpq aux; qm.set(aux, 1, 3); m.set(a, aux); #if 0 std::cout << interval_pp(a) << std::endl; std::cout << decimal_pp(eps, 4) << std::endl; std::cout << decimal_pp(a) << std::endl; std::cout << a + eps << std::endl; std::cout << a * eps << std::endl; std::cout << (a + eps)*eps - eps << std::endl; #endif std::cout << interval_pp(a - eps*2) << std::endl; std::cout << interval_pp(eps + 1) << std::endl; scoped_rcnumeral t(m); t = (a - eps*2) / (eps + 1); std::cout << t << std::endl; std::cout << t * (eps + 1) << std::endl; a = 10; std::cout << (a + eps > a) << std::endl; scoped_rcnumeral pi(m); m.mk_pi(pi); std::cout << pi + 1 << std::endl; std::cout << decimal_pp(pi) << std::endl; std::cout << decimal_pp(pi + 1) << std::endl; scoped_rcnumeral e(m); m.mk_e(e); t = e + (pi + 1)*2; std::cout << t << std::endl; std::cout << decimal_pp(t, 10) << std::endl; std::cout << (eps + 1 > 1) << std::endl; std::cout << interval_pp((a + eps)/(a - eps)) << std::endl; } static void tst2() { enable_trace("mpz_matrix"); unsynch_mpq_manager nm; small_object_allocator allocator; mpz_matrix_manager mm(nm, allocator); scoped_mpz_matrix A(mm); mm.mk(3, 3, A); // Matrix // 1 1 1 // 0 1 -1 // 0 1 1 A.set(0, 0, 1); A.set(0, 1, 1); A.set(0, 2, 1); A.set(1, 0, 0); A.set(1, 1, 1); A.set(1, 2, -1); A.set(2, 0, 0); A.set(2, 1, 1); A.set(2, 2, 1); std::cout << A; { int b[3]; int c[3] = { 10, -2, 8 }; std::cout << "solve: " << mm.solve(A, b, c) << "\n"; for (unsigned i = 0; i < 3; i++) std::cout << b[i] << " "; std::cout << "\n"; } scoped_mpz_matrix A2(mm); mm.tensor_product(A, A, A2); std::cout << A2; scoped_mpz_matrix B(mm); unsigned cols[] = { 1, 3, 7, 8 }; mm.filter_cols(A2, 4, cols, B); std::cout << B; scoped_mpz_matrix C(mm); unsigned perm[] = { 8, 7, 6, 5, 4, 3, 2, 1, 0 }; mm.permute_rows(B, perm, C); std::cout << C; } static void tst_solve(unsigned n, int _A[], int _b[], int _c[], bool solved) { unsynch_mpq_manager nm; small_object_allocator allocator; mpz_matrix_manager mm(nm, allocator); scoped_mpz_matrix A(mm); mm.mk(n, n, A); for (unsigned i = 0; i < n; i++) for (unsigned j = 0; j < n; j++) A.set(i, j, _A[i*n + j]); svector b; b.resize(n, 0); if (mm.solve(A, b.c_ptr(), _c)) { ENSURE(solved); for (unsigned i = 0; i < n; i++) { ENSURE(b[i] == _b[i]); } } else { ENSURE(!solved); } } static void tst_lin_indep(unsigned m, unsigned n, int _A[], unsigned ex_sz, unsigned ex_r[]) { unsynch_mpq_manager nm; small_object_allocator allocator; mpz_matrix_manager mm(nm, allocator); scoped_mpz_matrix A(mm); mm.mk(m, n, A); for (unsigned i = 0; i < m; i++) for (unsigned j = 0; j < n; j++) A.set(i, j, _A[i*n + j]); unsigned_vector r; r.resize(A.n()); scoped_mpz_matrix B(mm); mm.linear_independent_rows(A, r.c_ptr(), B); for (unsigned i = 0; i < ex_sz; i++) { ENSURE(r[i] == ex_r[i]); } } static void tst_denominators() { reslimit rl; unsynch_mpq_manager qm; rcmanager m(rl, qm); scoped_rcnumeral a(m); scoped_rcnumeral t(m); scoped_rcnumeral eps(m); m.mk_pi(a); m.inv(a); m.mk_infinitesimal(eps); t = (a - eps*2) / (a*eps + 1); // t = t + a * 2; scoped_rcnumeral n(m), d(m); std::cout << t << "\n"; m.clean_denominators(t, n, d); std::cout << "---->\n" << n << "\n" << d << "\n"; } void tst_rcf() { enable_trace("rcf_clean"); enable_trace("rcf_clean_bug"); tst_denominators(); tst1(); tst2(); { int A[] = {0, 1, 1, 1, 0, 1, 1, 1, -1}; int c[] = {10, 4, -4}; int b[] = {-2, 4, 6}; tst_solve(3, A, b, c, true); } { int A[] = {1, 1, 1, 0, 1, 1, 0, 1, 1}; int c[] = {3, 2, 2}; int b[] = {1, 1, 1}; tst_solve(3, A, b, c, false); } { int A[] = {1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, -1}; unsigned r[] = {0, 1, 4}; tst_lin_indep(5, 3, A, 3, r); } { int A[] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, -1}; unsigned r[] = {0, 4}; tst_lin_indep(5, 3, A, 2, r); } { int A[] = {1, 1, 1, 1, 1, 1, 1, 0, 1, 2, 1, 2, 3, 1, 3}; unsigned r[] = {0, 2}; tst_lin_indep(5, 3, A, 2, r); } } z3-z3-4.8.7/src/test/region.cpp000066400000000000000000000005021356505360400161610ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: tst_region.cpp Abstract: Test region memory allocator. Author: Leonardo de Moura (leonardo) 2006-09-14. Revision History: --*/ #include #include "util/region.h" static void tst1() { // TODO } void tst_region() { tst1(); } z3-z3-4.8.7/src/test/sat_local_search.cpp000066400000000000000000000067431356505360400202010ustar00rootroot00000000000000#include "sat/sat_local_search.h" #include "sat/sat_solver.h" #include "util/cancel_eh.h" #include "util/scoped_ctrl_c.h" #include "util/scoped_timer.h" static bool build_instance(char const * filename, sat::solver& s, sat::local_search& local_search) { char line[16383]; // for temporary storage std::ifstream infile(filename); //if (infile == NULL) //linux if (!infile) { std::cout << "File not found " << filename << "\n"; return false; } infile.getline(line, 16383); #ifdef _WINDOWS int cur_term; int num_vars = 0, num_constraints = 0; sscanf_s(line, "%d %d", &num_vars, &num_constraints); //std::cout << "number of variables: " << num_vars << '\n'; //std::cout << "number of constraints: " << num_constraints << '\n'; unsigned_vector coefficients; sat::literal_vector lits; // process objective function: // read coefficients infile >> cur_term; while (cur_term != 0) { coefficients.push_back(cur_term); infile >> cur_term; } // read variables infile >> cur_term; while (cur_term != 0) { lits.push_back(sat::literal(abs(cur_term), cur_term < 0)); infile >> cur_term; } if (lits.size() != coefficients.size()) { std::cout << "Objective function format error. They have different lengths.\n"; return false; } // read the constraints, one at a time int k; for (int c = 0; c < num_constraints; ++c) { lits.reset(); infile >> cur_term; while (cur_term != 0) { lits.push_back(sat::literal(abs(cur_term), cur_term > 0)); infile >> cur_term; } infile >> k; //local_search.add_cardinality(lits.size(), lits.c_ptr(), static_cast(lits.size() - k)); local_search.add_cardinality(lits.size(), lits.c_ptr(), static_cast(k)); } infile.close(); return true; #else return false; #endif } void tst_sat_local_search(char ** argv, int argc, int& i) { if (argc < i + 2) { std::cout << "require dimacs file name\n"; return; } reslimit limit; params_ref params; sat::solver solver(params, limit); sat::local_search local_search; local_search.import(solver, true); char const* file_name = argv[i + 1]; ++i; int cutoff_time = 1; int v; while (i + 1 < argc) { std::cout << argv[i + 1] << "\n"; // set other ad hoc parameters. if (argv[i + 1][0] == '-' && i + 2 < argc) { switch (argv[i + 1][1]) { case 's': // seed v = atoi(argv[i + 2]); local_search.config().set_random_seed(v); break; case 't': // cutoff_time v = atoi(argv[i + 2]); cutoff_time = v; break; case 'b': // best_known_value v = atoi(argv[i + 2]); local_search.config().set_best_known_value(v); break; default: ++i; v = -1; break; } } ++i; } if (!build_instance(file_name, solver, local_search)) { return; } //std::cout << "local instance built\n"; // set up cancellation/timeout environment. cancel_eh eh(local_search.rlimit()); scoped_ctrl_c ctrlc(eh, false, true); scoped_timer timer(cutoff_time*1000, &eh); local_search.check(0, nullptr, nullptr); } z3-z3-4.8.7/src/test/sat_lookahead.cpp000066400000000000000000000025331356505360400175020ustar00rootroot00000000000000#include "sat/sat_solver.h" #include "sat/sat_watched.h" #include "util/statistics.h" #include "sat/sat_lookahead.h" #include "sat/dimacs.h" static void display_model(sat::model const & m) { for (unsigned i = 1; i < m.size(); i++) { switch (m[i]) { case l_false: std::cout << "-" << i << " "; break; case l_undef: break; case l_true: std::cout << i << " "; break; } } std::cout << "\n"; } void tst_sat_lookahead(char ** argv, int argc, int& i) { if (argc != i + 2) { std::cout << "require dimacs file name\n"; return; } // enable_trace("sat"); reslimit limit; params_ref params; sat::solver solver(params, limit); char const* file_name = argv[i + 1]; ++i; { std::ifstream in(file_name); if (in.bad() || in.fail()) { std::cerr << "(error \"failed to open file '" << file_name << "'\")" << std::endl; exit(ERR_OPEN_FILE); } if (!parse_dimacs(in, std::cerr, solver)) return; } sat::lookahead lh(solver); IF_VERBOSE(20, solver.display_status(verbose_stream());); lbool is_sat = lh.check(); std::cout << is_sat << "\n"; statistics st; lh.collect_statistics(st); st.display(std::cout); if (is_sat == l_true) { display_model(lh.get_model()); } } z3-z3-4.8.7/src/test/sat_user_scope.cpp000066400000000000000000000052501356505360400177210ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "sat/sat_solver.h" #include "util/util.h" typedef sat::literal_vector clause_t; typedef vector clauses_t; typedef vector trail_t; // [ [c1, c2, ..], [ ...] ] static unsigned s_num_vars = 6; static unsigned s_num_clauses_per_frame = 8; static unsigned s_num_frames = 7; static void add_literal(random_gen& r, clause_t& c) { c.push_back(sat::literal(r(s_num_vars) + 1, r(2) == 0)); } static clause_t& last_clause(trail_t& t) { return t.back().back(); } static void add_clause(sat::solver& s, random_gen& r, trail_t& t) { t.back().push_back(sat::literal_vector()); clause_t& cls = last_clause(t); for (unsigned i = 0; i < 3; ++i) { add_literal(r, cls); } s.mk_clause(cls.size(), cls.c_ptr()); } static void pop_user_scope(sat::solver& s, trail_t& t) { std::cout << "pop\n"; s.user_pop(1); t.pop_back(); } static void push_user_scope(sat::solver& s, trail_t& t) { std::cout << "push\n"; s.user_push(); t.push_back(clauses_t()); } static void init_vars(sat::solver& s) { for (unsigned i = 0; i <= s_num_vars; ++i) { s.mk_var(); } } static void check_coherence(sat::solver& s1, trail_t& t) { params_ref p; reslimit rlim; sat::solver s2(p, rlim); init_vars(s2); sat::literal_vector cls; for (unsigned i = 0; i < t.size(); ++i) { clauses_t& clss = t[i]; for (unsigned j = 0; j < clss.size(); ++j) { cls.reset(); cls.append(clss[j]); s2.mk_clause(cls.size(), cls.c_ptr()); } } lbool is_sat1 = s1.check(); lbool is_sat2 = s2.check(); if (is_sat1 != is_sat2) { s1.display(std::cout); s2.display(std::cout); } std::cout << is_sat1 << "\n"; ENSURE(is_sat1 == is_sat2); } void tst_sat_user_scope() { random_gen r(0); trail_t trail; params_ref p; reslimit rlim; sat::solver s(p, rlim); // incremental solver init_vars(s); while (true) { for (unsigned i = 0; i < s_num_frames; ++i) { // push 3 frames, pop 2 for (unsigned k = 0; k < 3; ++k) { push_user_scope(s, trail); for (unsigned j = 0; j < s_num_clauses_per_frame; ++j) { add_clause(s, r, trail); } check_coherence(s, trail); } for (unsigned k = 0; k < 2; ++k) { pop_user_scope(s, trail); check_coherence(s, trail); } } for (unsigned i = 0; i < s_num_frames; ++i) { pop_user_scope(s, trail); check_coherence(s, trail); } } } z3-z3-4.8.7/src/test/simple_parser.cpp000066400000000000000000000033321356505360400175470ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: simple_parser.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-06-14. Revision History: --*/ #include "parsers/util/cost_parser.h" #include "smt/cost_evaluator.h" #include "ast/arith_decl_plugin.h" #include "ast/ast_pp.h" #include "ast/well_sorted.h" #include "util/warning.h" #include "ast/reg_decl_plugins.h" void tst_simple_parser() { ast_manager m; reg_decl_plugins(m); arith_util m_util(m); cost_parser p(m); var_ref_vector vs(m); cost_evaluator eval(m); p.add_var("x"); p.add_var("y"); expr_ref r(m); p.parse_string("(+ x (* y x))", r); TRACE("simple_parser", tout << mk_pp(r, m) << "\n";); p.parse_string("(+ x (* y x) x)", r); float vals[2] = { 2.0f, 3.0f }; (void)vals; TRACE("simple_parser", tout << mk_pp(r, m) << "\n"; tout << "val: " << eval(r, 2, vals) << "\n";); p.parse_string("(+ x (* y x) x", r); // << error p.parse_string("(x)", r); // << error p.parse_string("(+ x))", r); // <<< this is accepted TRACE("simple_parser", tout << mk_pp(r, m) << "\n";); p.parse_string(")x)", r); // error p.parse_string("(+ x (* 10 y) 2)", r); TRACE("simple_parser", tout << mk_pp(r, m) << "\n"; tout << "val: " << eval(r, 2, vals) << "\n";); p.parse_string("(ite (and (> x 3) (<= y 4)) 2 10)", r); TRACE("simple_parser", tout << mk_pp(r, m) << "\n"; tout << "val: " << eval(r, 2, vals) << "\n";); p.parse_string("(ite (or (> x 3) (<= y 4)) 2 10)", r); TRACE("simple_parser", tout << mk_pp(r, m) << "\n"; tout << "val: " << eval(r, 2, vals) << "\n";); } z3-z3-4.8.7/src/test/simplex.cpp000066400000000000000000000100311356505360400163550ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "math/simplex/sparse_matrix_def.h" #include "math/simplex/simplex.h" #include "math/simplex/simplex_def.h" #include "util/mpq_inf.h" #include "util/vector.h" #include "util/rational.h" #include "util/rlimit.h" #define R rational typedef simplex::simplex Simplex; typedef simplex::sparse_matrix sparse_matrix; static vector vec(int i, int j) { vector nv; nv.resize(2); nv[0] = R(i); nv[1] = R(j); return nv; } // static vector vec(int i, int j, int k) { // vector nv = vec(i, j); // nv.push_back(R(k)); // return nv; // } // static vector vec(int i, int j, int k, int l) { // vector nv = vec(i, j, k); // nv.push_back(R(l)); // return nv; // } /// static vector vec(int i, int j, int k, int l, int x) { /// vector nv = vec(i, j, k, l); /// nv.push_back(R(x)); /// return nv; /// } // static vector vec(int i, int j, int k, int l, int x, int y) { // vector nv = vec(i, j, k, l, x); // nv.push_back(R(y)); // return nv; // } // static vector vec(int i, int j, int k, int l, int x, int y, int z) { // vector nv = vec(i, j, k, l, x, y); // nv.push_back(R(z)); // return nv; // } void add_row(Simplex& S, vector const& _v, R const& _b, bool is_eq = false) { unsynch_mpz_manager m; unsigned_vector vars; vector v(_v); R b(_b); R l(denominator(b)); scoped_mpz_vector coeffs(m); for (unsigned i = 0; i < v.size(); ++i) { l = lcm(l, denominator(v[i])); vars.push_back(i); S.ensure_var(i); } b *= l; b.neg(); for (unsigned i = 0; i < v.size(); ++i) { v[i] *= l; coeffs.push_back(v[i].to_mpq().numerator()); } unsigned nv = S.get_num_vars(); vars.push_back(nv); vars.push_back(nv+1); S.ensure_var(nv); S.ensure_var(nv+1); coeffs.push_back(mpz(-1)); coeffs.push_back(b.to_mpq().numerator()); mpq_inf one(mpq(1),mpq(0)); mpq_inf zero(mpq(0),mpq(0)); ENSURE(vars.size() == coeffs.size()); S.set_lower(nv, zero); if (is_eq) S.set_upper(nv, zero); S.set_lower(nv+1, one); S.set_upper(nv+1, one); S.add_row(nv, coeffs.size(), vars.c_ptr(), coeffs.c_ptr()); } static void feas(Simplex& S) { S.display(std::cout); lbool is_sat = S.make_feasible(); std::cout << "feasible: " << is_sat << "\n"; S.display(std::cout); } static void test1() { reslimit rl; Simplex S(rl); add_row(S, vec(1,0), R(1)); add_row(S, vec(0,1), R(1)); add_row(S, vec(1,1), R(1)); feas(S); } static void test2() { reslimit rl; Simplex S(rl); add_row(S, vec(1, 0), R(1)); add_row(S, vec(0, 1), R(1)); add_row(S, vec(1, 1), R(1), true); feas(S); } static void test3() { reslimit rl; Simplex S(rl); add_row(S, vec(-1, 0), R(-1)); add_row(S, vec(0, -1), R(-1)); add_row(S, vec(1, 1), R(1), true); feas(S); } static void test4() { reslimit rl; Simplex S(rl); add_row(S, vec(1, 0), R(1)); add_row(S, vec(0, -1), R(-1)); add_row(S, vec(1, 1), R(1), true); feas(S); } void tst_simplex() { reslimit rl; Simplex S(rl); std::cout << "simplex\n"; lbool is_sat = S.make_feasible(); std::cout << "feasible: " << is_sat << "\n"; unsynch_mpz_manager m; unsynch_mpq_inf_manager em; scoped_mpz_vector coeffs(m); svector vars; for (unsigned i = 0; i < 5; ++i) { S.ensure_var(i); vars.push_back(i); coeffs.push_back(mpz(i+1)); } // Simplex::row r = S.add_row(1, coeffs.size(), vars.c_ptr(), coeffs.c_ptr()); is_sat = S.make_feasible(); std::cout << "feasible: " << is_sat << "\n"; S.display(std::cout); _scoped_numeral num(em); num = std::make_pair(mpq(1), mpq(0)); S.set_lower(0, num); S.set_upper(0, num); is_sat = S.make_feasible(); std::cout << "feasible: " << is_sat << "\n"; S.display(std::cout); test1(); test2(); test3(); test4(); } z3-z3-4.8.7/src/test/simplifier.cpp000066400000000000000000000171271356505360400170540ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #ifdef _WINDOWS #include "api/z3.h" #include "api/z3_private.h" #include #include "util/util.h" #include "util/trace.h" static void ev_const(Z3_context ctx, Z3_ast e) { Z3_ast r = Z3_simplify(ctx, e); TRACE("simplifier", tout << Z3_ast_to_string(ctx, e) << " -> "; tout << Z3_ast_to_string(ctx, r) << "\n";); Z3_ast_kind k = Z3_get_ast_kind(ctx, r); ENSURE(k == Z3_NUMERAL_AST || (k == Z3_APP_AST && (Z3_OP_TRUE == Z3_get_decl_kind(ctx,Z3_get_app_decl(ctx, Z3_to_app(ctx, r))) || Z3_OP_FALSE == Z3_get_decl_kind(ctx,Z3_get_app_decl(ctx, Z3_to_app(ctx, r)))))); } static void test_bv() { Z3_config cfg = Z3_mk_config(); Z3_context ctx = Z3_mk_context(cfg); Z3_sort bv1 = Z3_mk_bv_sort(ctx,1); Z3_sort bv2 = Z3_mk_bv_sort(ctx,2); Z3_sort bv72 = Z3_mk_bv_sort(ctx,72); Z3_ast bit1_1 = Z3_mk_numeral(ctx, "1", bv1); Z3_ast bit3_2 = Z3_mk_numeral(ctx, "3", bv2); Z3_ast e = Z3_mk_eq(ctx, bit3_2, Z3_mk_sign_ext(ctx, 1, bit1_1)); ENSURE(Z3_simplify(ctx, e) == Z3_mk_true(ctx)); TRACE("simplifier", tout << Z3_ast_to_string(ctx, e) << "\n";); Z3_ast b12 = Z3_mk_numeral(ctx, "12", bv72); Z3_ast b13 = Z3_mk_numeral(ctx, "13", bv72); ev_const(ctx, Z3_mk_bvnot(ctx,b12)); ev_const(ctx, Z3_mk_bvnot(ctx,Z3_simplify(ctx, Z3_mk_bvnot(ctx, b12)))); ev_const(ctx, Z3_mk_bvand(ctx,b12,b13)); ev_const(ctx, Z3_mk_bvor(ctx,b12,b13)); ev_const(ctx, Z3_mk_bvxor(ctx,b12,b13)); ev_const(ctx, Z3_mk_bvnand(ctx,b12,b13)); ev_const(ctx, Z3_mk_bvnor(ctx,b12,b13)); ev_const(ctx, Z3_mk_bvxnor(ctx,b12,b13)); ev_const(ctx, Z3_mk_bvneg(ctx,b12)); ev_const(ctx, Z3_mk_bvadd(ctx,b12,b13)); ev_const(ctx, Z3_mk_bvsub(ctx,b12,b13)); ev_const(ctx, Z3_mk_bvmul(ctx,b12,b13)); ev_const(ctx, Z3_mk_bvudiv(ctx,b12,b13)); ev_const(ctx, Z3_mk_bvsdiv(ctx,b12,b13)); ev_const(ctx, Z3_mk_bvsrem(ctx,b12,b13)); ev_const(ctx, Z3_mk_bvuge(ctx,b12,b13)); ev_const(ctx, Z3_mk_bvsge(ctx,b12,b13)); ev_const(ctx, Z3_mk_bvugt(ctx,b12,b13)); ev_const(ctx, Z3_mk_bvsgt(ctx,b12,b13)); ev_const(ctx, Z3_mk_bvule(ctx,b12,b13)); ev_const(ctx, Z3_mk_bvult(ctx,b12,b13)); ev_const(ctx, Z3_mk_bvsle(ctx,b12,b13)); ev_const(ctx, Z3_mk_bvslt(ctx,b12,b13)); ev_const(ctx, Z3_mk_concat(ctx,b12,b13)); ev_const(ctx, Z3_mk_extract(ctx,43,1,b13)); ev_const(ctx, Z3_mk_sign_ext(ctx,33,b13)); ev_const(ctx, Z3_mk_zero_ext(ctx,33,b13)); ev_const(ctx, Z3_mk_bvshl(ctx,b12,b13)); ev_const(ctx, Z3_mk_bvshl(ctx,b12,b13)); ev_const(ctx, Z3_mk_bvlshr(ctx,b12,b13)); ev_const(ctx, Z3_mk_bvashr(ctx,b12,b13)); ev_const(ctx, Z3_mk_rotate_left(ctx,21,b13)); ev_const(ctx, Z3_mk_rotate_right(ctx,21,b13)); Z3_del_config(cfg); Z3_del_context(ctx); } static void test_datatypes() { Z3_config cfg = Z3_mk_config(); Z3_context ctx = Z3_mk_context(cfg); Z3_sort int_ty, int_list; Z3_func_decl nil_decl, is_nil_decl, cons_decl, is_cons_decl, head_decl, tail_decl; Z3_ast nil, l1; int_ty = Z3_mk_int_sort(ctx); int_list = Z3_mk_list_sort(ctx, Z3_mk_string_symbol(ctx, "int_list"), int_ty, &nil_decl, &is_nil_decl, &cons_decl, &is_cons_decl, &head_decl, &tail_decl); nil = Z3_mk_app(ctx, nil_decl, 0, 0); Z3_ast a = Z3_simplify(ctx, Z3_mk_app(ctx, is_nil_decl, 1, &nil)); ENSURE(a == Z3_mk_true(ctx)); a = Z3_simplify(ctx, Z3_mk_app(ctx, is_cons_decl, 1, &nil)); ENSURE(a == Z3_mk_false(ctx)); Z3_ast one = Z3_mk_numeral(ctx, "1", int_ty); Z3_ast args[2] = { one, nil }; l1 = Z3_mk_app(ctx, cons_decl, 2, args); ENSURE(nil == Z3_simplify(ctx, Z3_mk_app(ctx, tail_decl, 1, &l1))); ENSURE(one == Z3_simplify(ctx, Z3_mk_app(ctx, head_decl, 1, &l1))); ENSURE(Z3_mk_false(ctx) == Z3_simplify(ctx, Z3_mk_eq(ctx, nil, l1))); Z3_del_config(cfg); Z3_del_context(ctx); } static void test_skolemize_bug() { Z3_config cfg = Z3_mk_config(); Z3_set_param_value(cfg, "MODEL", "true"); Z3_context ctx = Z3_mk_context(cfg); Z3_del_config(cfg); Z3_sort Real = Z3_mk_real_sort(ctx); Z3_ast x = Z3_mk_bound(ctx, 0, Real); Z3_symbol x_name = Z3_mk_string_symbol(ctx, "x"); Z3_ast y = Z3_mk_const(ctx, Z3_mk_string_symbol(ctx, "y"), Real); Z3_ast xp = Z3_mk_const(ctx, Z3_mk_string_symbol(ctx, "xp"), Real); Z3_ast n0 = Z3_mk_numeral(ctx, "0", Real); Z3_ast n1 = Z3_mk_numeral(ctx, "1", Real); Z3_ast args1[2] = { x, n1 }; Z3_ast args2[2] = { x, y }; Z3_ast args[2] = { Z3_mk_eq(ctx, Z3_mk_add(ctx, 2, args1), xp), Z3_mk_ge(ctx, Z3_mk_add(ctx, 2, args2), n0) }; Z3_ast f = Z3_mk_and(ctx, 2, args); Z3_ast f2 = Z3_mk_exists(ctx, 0, 0, 0, 1, &Real, &x_name, f); std::cout << Z3_ast_to_string(ctx, f2) << "\n"; Z3_ast f3 = Z3_simplify(ctx, f2); std::cout << Z3_ast_to_string(ctx, f3) << "\n"; } static void test_bool() { Z3_config cfg = Z3_mk_config(); Z3_context ctx = Z3_mk_context(cfg); Z3_ast a = Z3_simplify(ctx, Z3_mk_not(ctx, Z3_mk_eq(ctx, Z3_mk_false(ctx), Z3_mk_true(ctx)))); Z3_ast b = Z3_simplify(ctx, Z3_mk_not(ctx, Z3_mk_iff(ctx, Z3_mk_false(ctx), Z3_mk_true(ctx)))); ENSURE(Z3_mk_true(ctx) == a); ENSURE(Z3_mk_true(ctx) == b); TRACE("simplifier", tout << Z3_ast_to_string(ctx, a) << "\n";); TRACE("simplifier", tout << Z3_ast_to_string(ctx, b) << "\n";); Z3_del_config(cfg); Z3_del_context(ctx); } static void test_array() { Z3_config cfg = Z3_mk_config(); Z3_context ctx = Z3_mk_context(cfg); Z3_sort i = Z3_mk_int_sort(ctx); Z3_ast n1 = Z3_mk_numeral(ctx, "1", i); Z3_ast n2 = Z3_mk_numeral(ctx, "2", i); Z3_ast n3 = Z3_mk_numeral(ctx, "3", i); Z3_ast n4 = Z3_mk_numeral(ctx, "4", i); Z3_ast s1 = Z3_mk_const(ctx, Z3_mk_string_symbol(ctx,"s1"), i); Z3_ast s2 = Z3_mk_const(ctx, Z3_mk_string_symbol(ctx,"s2"), i); Z3_ast c1 = Z3_mk_const_array(ctx, i, n1); Z3_ast x1 = Z3_mk_store(ctx, Z3_mk_store(ctx, c1, n2, n3), n1, n4); Z3_ast x2 = Z3_mk_store(ctx, Z3_mk_store(ctx, c1, n1, n4), n2, n3); Z3_ast x3 = Z3_mk_store(ctx, Z3_mk_store(ctx, c1, s1, n1), n2, n3); Z3_ast x4 = Z3_mk_store(ctx, Z3_mk_store(ctx, Z3_mk_store(ctx, c1, n2, n3), n1, n4), n2, n3); Z3_ast xs[4] = { x1, x2, x3, x4}; Z3_ast exy = Z3_mk_eq(ctx, x2, x1); Z3_ast rxy = Z3_simplify(ctx, exy); TRACE("simplifier", tout << Z3_ast_to_string(ctx, rxy) << "\n";); TRACE("simplifier", tout << Z3_ast_to_string(ctx, Z3_simplify(ctx, Z3_mk_eq(ctx, x2, x3))) << "\n";); // ENSURE(rxy == Z3_mk_true(ctx)); // ENSURE(Z3_simplify(ctx, Z3_mk_eq(ctx, x2, x3)) == Z3_mk_false(ctx)); for (unsigned i = 0; i < 4; ++i) { for (unsigned j = 0; j < 4; ++j) { exy = Z3_mk_eq(ctx, xs[i], xs[j]); rxy = Z3_simplify(ctx, exy); TRACE("simplifier", tout << Z3_ast_to_string(ctx, exy); tout << " -> " << Z3_ast_to_string(ctx, rxy) << "\n"; ); } } Z3_ast sel1 = Z3_mk_select(ctx, x1, n1); Z3_ast sel2 = Z3_mk_select(ctx, x1, n4); TRACE("simplifier", tout << Z3_ast_to_string(ctx, Z3_simplify(ctx, sel1)) << "\n"; tout << Z3_ast_to_string(ctx, Z3_simplify(ctx, sel2)) << "\n"; ); Z3_del_config(cfg); Z3_del_context(ctx); } void tst_simplifier() { test_array(); test_bv(); test_datatypes(); test_bool(); test_skolemize_bug(); } #else void tst_simplifier() { } #endif z3-z3-4.8.7/src/test/small_object_allocator.cpp000066400000000000000000000027741356505360400214110ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include #include "util/util.h" #include "util/trace.h" #include "util/small_object_allocator.h" void tst_small_object_allocator() { small_object_allocator soa; char * p1 = new (soa) char[13]; char * q1 = new (soa) char[14]; char * p2 = new (soa) char[13]; TRACE("small_object_allocator", tout << "p1: " << (void*)p1 << " q1: " << (void*)q1 << " p2: " << (void*)p2 << "\n";); soa.deallocate(13,p1); soa.deallocate(14,q1); soa.deallocate(13,p2); char * p3 = new (soa) char[13]; TRACE("small_object_allocator", tout << "p3: " << (void*)p3 << "\n";); soa.deallocate(13,p3); char * r1 = new (soa) char[1]; char * r2 = new (soa) char[1]; char * r3 = new (soa) char[1]; char * r4 = new (soa) char[1]; TRACE("small_object_allocator", tout << "r1: " << (void*)r1 << " r2: " << (void*)r2 << " r3: " << (void*)r3 << " r4: " << (void*)r4 << "\n";); soa.deallocate(1,r1); soa.deallocate(1,r3); r1 = new (soa) char[1]; soa.deallocate(1,r4); r4 = new (soa) char[1]; r3 = new (soa) char[1]; TRACE("small_object_allocator", tout << "r1: " << (void*)r1 << " r2: " << (void*)r2 << " r3: " << (void*)r3 << " r4: " << (void*)r4 << "\n";); soa.deallocate(1,r1); soa.deallocate(1,r2); soa.deallocate(1,r3); soa.deallocate(1,r4); (void)r1; (void)r2; (void)r3; (void)r4; (void)q1; (void)p1; (void)p2; (void)p3; } z3-z3-4.8.7/src/test/smt2print_parse.cpp000066400000000000000000000076551356505360400200520ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ // This is to test the print-parse facilities over the API // for SMT-LIB2. #include "api/z3.h" #include void test_print(Z3_context ctx, Z3_ast_vector av) { Z3_set_ast_print_mode(ctx, Z3_PRINT_SMTLIB2_COMPLIANT); Z3_ast* args = new Z3_ast[Z3_ast_vector_size(ctx, av)]; for (unsigned i = 0; i < Z3_ast_vector_size(ctx, av); ++i) { args[i] = Z3_ast_vector_get(ctx, av, i); } Z3_ast a = Z3_mk_and(ctx, Z3_ast_vector_size(ctx, av), args); Z3_inc_ref(ctx, a); delete[] args; char const* spec1 = Z3_benchmark_to_smtlib_string(ctx, "test", nullptr, nullptr, nullptr, 0, nullptr, a); Z3_dec_ref(ctx, a); std::cout << "spec1: benchmark->string\n" << spec1 << "\n"; std::cout << "attempting to parse spec1...\n"; Z3_ast_vector b = Z3_parse_smtlib2_string(ctx, spec1, 0, nullptr, nullptr, 0, nullptr, nullptr); std::cout << "parse successful, converting ast->string\n"; Z3_ast_vector_inc_ref(ctx, b); char const* spec2 = Z3_ast_vector_to_string(ctx, b); std::cout << "spec2: string->ast->string\n" << spec2 << "\n"; Z3_ast_vector_dec_ref(ctx, b); } void test_parseprint(char const* spec) { Z3_context ctx = Z3_mk_context(nullptr); std::cout << "spec:\n" << spec << "\n"; Z3_ast_vector a = Z3_parse_smtlib2_string(ctx, spec, 0, nullptr, nullptr, 0, nullptr, nullptr); std::cout << "done parsing\n"; Z3_ast_vector_inc_ref(ctx, a); test_print(ctx, a); std::cout << "done printing\n"; Z3_ast_vector_dec_ref(ctx, a); Z3_del_context(ctx); } void tst_smt2print_parse() { // test basic datatypes char const* spec1 = "(declare-datatypes (T) ((list (nil) (cons (car T) (cdr list)))))\n" "(declare-const x Int)\n" "(declare-const l (list Int))\n" "(declare-fun f ((list Int)) Bool)\n" "(assert (f (cons x l)))\n"; test_parseprint(spec1); // test basic arrays char const* spec2 = "(declare-const x Int)\n" "(declare-const a (Array Int Int))\n" "(declare-const b (Array (Array Int Int) Bool))\n" "(assert (select b a))\n" "(assert (= b ((as const (Array (Array Int Int) Bool)) true)))\n" "(assert (= b (store b a true)))\n" "(declare-const b1 (Array Bool Bool))\n" "(declare-const b2 (Array Bool Bool))\n" "(assert (= ((as const (Array Bool Bool)) false) ((_ map and) b1 b2)))\n"; // TBD: const, map, store test_parseprint(spec2); // Test mutually recursive datatypes char const* spec3 = "(declare-datatypes () ((list (nil) (cons (car tree) (cdr list))) (tree (leaf) (node (n list)))))\n" "(declare-const x tree)\n" "(declare-const l list)\n" "(declare-fun f (list) Bool)\n" "(assert (f (cons x l)))\n"; test_parseprint(spec3); // Test arithmetic char const* spec4 = "(declare-const x Real)\n" "(declare-const y Int)\n" "(assert (= x 0.0))\n" "(assert (= y 6))\n" "(assert (> (/ x 1.4) (to_real y)))"; test_parseprint(spec4); // Test bit-vectors char const* spec5 = "(declare-const x (_ BitVec 4))\n" "(declare-const y (_ BitVec 4))\n" "(assert (bvule x (bvmul y (concat ((_ extract 2 0) x) ((_ extract 3 3) #xf0)))))"; test_parseprint(spec5); // Test strings char const* spec6 = "(assert (= \"abc\" \"abc\"))"; test_parseprint(spec6); // Test ? } z3-z3-4.8.7/src/test/smt_context.cpp000066400000000000000000000013601356505360400172500ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "smt/smt_context.h" #include "ast/reg_decl_plugins.h" void tst_smt_context() { smt_params params; ast_manager m; reg_decl_plugins(m); smt::context ctx(m, params); app_ref a1(m.mk_const(symbol("a"), m.mk_bool_sort()), m); app_ref b1(m.mk_const(symbol("b"), m.mk_bool_sort()), m); app_ref c1(m.mk_const(symbol("c"), m.mk_bool_sort()), m); app_ref na1(m.mk_not(a1), m); ctx.assert_expr(na1); ctx.assert_expr(m.mk_or(c1.get(), b1.get())); { app_ref nc(m.mk_not(c1), m); ptr_vector assumptions; assumptions.push_back(nc.get()); ctx.check(assumptions.size(), assumptions.c_ptr()); } ctx.check(); } z3-z3-4.8.7/src/test/solver_pool.cpp000066400000000000000000000024051356505360400172450ustar00rootroot00000000000000#include "ast/reg_decl_plugins.h" #include "solver/solver_pool.h" #include "smt/smt_solver.h" void tst_solver_pool() { ast_manager m; reg_decl_plugins(m); params_ref p; ref base = mk_smt_solver(m, p, symbol::null); expr_ref a(m.mk_const(symbol("a"), m.mk_bool_sort()), m); expr_ref b(m.mk_const(symbol("b"), m.mk_bool_sort()), m); expr_ref c(m.mk_const(symbol("c"), m.mk_bool_sort()), m); expr_ref d(m.mk_const(symbol("d"), m.mk_bool_sort()), m); expr_ref fml(m); fml = m.mk_or(a, b); base->assert_expr(fml); solver_pool pool(base.get(), 3); // base solver is cloned so any assertions // added to base after mk_solver() are ignored. ref s1 = pool.mk_solver(); ref s2 = pool.mk_solver(); ref s3 = pool.mk_solver(); ref s4 = pool.mk_solver(); fml = m.mk_and(b, c); s1->assert_expr(fml); fml = m.mk_and(a, b); s2->assert_expr(fml); expr_ref_vector asms(m); asms.push_back(m.mk_not(a)); std::cout << base->check_sat(asms) << "\n"; std::cout << s1->check_sat(asms) << "\n"; std::cout << s2->check_sat(asms) << "\n"; std::cout << s3->check_sat(asms) << "\n"; std::cout << *s1; std::cout << *s2; std::cout << *base; } z3-z3-4.8.7/src/test/sorting_network.cpp000066400000000000000000000453551356505360400201530ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "util/trace.h" #include "util/vector.h" #include "util/sorting_network.h" #include "ast/ast.h" #include "ast/ast_pp.h" #include "ast/reg_decl_plugins.h" #include "ast/ast_util.h" #include "model/model_smt2_pp.h" #include "smt/smt_kernel.h" #include "smt/params/smt_params.h" struct ast_ext { ast_manager& m; ast_ext(ast_manager& m):m(m) {} typedef expr* T; typedef expr_ref_vector vector; T mk_ite(T a, T b, T c) { return m.mk_ite(a, b, c); } T mk_le(T a, T b) { if (m.is_bool(a)) { return m.mk_implies(a, b); } UNREACHABLE(); return nullptr; } T mk_default() { return m.mk_false(); } }; struct unsigned_ext { unsigned_ext() {} typedef unsigned T; typedef svector vector; T mk_ite(T a, T b, T c) { return (a==1)?b:c; } T mk_le(T a, T b) { return (a <= b)?1:0; } T mk_default() { return 0; } }; static void is_sorted(svector const& v) { for (unsigned i = 0; i + 1 < v.size(); ++i) { ENSURE(v[i] <= v[i+1]); } } static void test_sorting1() { svector in, out; unsigned_ext uext; sorting_network sn(uext); in.push_back(0); in.push_back(1); in.push_back(0); in.push_back(1); in.push_back(1); in.push_back(0); sn(in, out); is_sorted(out); for (unsigned i = 0; i < out.size(); ++i) { std::cout << out[i]; } std::cout << "\n"; } static void test_sorting2() { svector in, out; unsigned_ext uext; sorting_network sn(uext); in.push_back(0); in.push_back(1); in.push_back(2); in.push_back(1); in.push_back(1); in.push_back(3); sn(in, out); is_sorted(out); for (unsigned i = 0; i < out.size(); ++i) { std::cout << out[i]; } std::cout << "\n"; } static void test_sorting4_r(unsigned i, svector& in) { if (i == in.size()) { svector out; unsigned_ext uext; sorting_network sn(uext); sn(in, out); is_sorted(out); std::cout << "sorted\n"; } else { in[i] = 0; test_sorting4_r(i+1, in); in[i] = 1; test_sorting4_r(i+1, in); } } static void test_sorting4() { svector in; in.resize(5); test_sorting4_r(0, in); in.resize(8); test_sorting4_r(0, in); } void test_sorting3() { ast_manager m; reg_decl_plugins(m); expr_ref_vector in(m), out(m); for (unsigned i = 0; i < 7; ++i) { in.push_back(m.mk_fresh_const("a",m.mk_bool_sort())); } for (expr* e : in) std::cout << mk_pp(e, m) << "\n"; ast_ext aext(m); sorting_network sn(aext); sn(in, out); std::cout << "size: " << out.size() << "\n"; for (expr* e : out) std::cout << mk_pp(e, m) << "\n"; } struct ast_ext2 { ast_manager& m; expr_ref_vector m_clauses; expr_ref_vector m_trail; ast_ext2(ast_manager& m):m(m), m_clauses(m), m_trail(m) {} typedef expr* pliteral; typedef ptr_vector pliteral_vector; expr* trail(expr* e) { m_trail.push_back(e); return e; } pliteral mk_false() { return m.mk_false(); } pliteral mk_true() { return m.mk_true(); } pliteral mk_max(unsigned n, pliteral const* lits) { return trail(m.mk_or(n, lits)); } pliteral mk_min(unsigned n, pliteral const* lits) { return trail(m.mk_and(n, lits)); } pliteral mk_not(pliteral a) { if (m.is_not(a,a)) return a; return trail(m.mk_not(a)); } std::ostream& pp(std::ostream& out, pliteral lit) { return out << mk_pp(lit, m); } pliteral fresh(char const* n) { return trail(m.mk_fresh_const(n, m.mk_bool_sort())); } void mk_clause(unsigned n, pliteral const* lits) { m_clauses.push_back(mk_or(m, n, lits)); } }; static void test_eq1(unsigned n, sorting_network_encoding enc) { //std::cout << "test eq1 " << n << " for encoding: " << enc << "\n"; ast_manager m; reg_decl_plugins(m); ast_ext2 ext(m); expr_ref_vector in(m), out(m); for (unsigned i = 0; i < n; ++i) { in.push_back(m.mk_fresh_const("a",m.mk_bool_sort())); } smt_params fp; smt::kernel solver(m, fp); psort_nw sn(ext); sn.cfg().m_encoding = enc; expr_ref result1(m), result2(m); // equality: solver.push(); result1 = sn.eq(true, 1, in.size(), in.c_ptr()); for (expr* cls : ext.m_clauses) { solver.assert_expr(cls); } expr_ref_vector ors(m); for (unsigned i = 0; i < n; ++i) { expr_ref_vector ands(m); for (unsigned j = 0; j < n; ++j) { ands.push_back(j == i ? in[j].get() : m.mk_not(in[j].get())); } ors.push_back(mk_and(ands)); } result2 = mk_or(ors); solver.assert_expr(m.mk_not(m.mk_eq(result1, result2))); //std::cout << ext.m_clauses << "\n"; //std::cout << result1 << "\n"; //std::cout << result2 << "\n"; lbool res = solver.check(); if (res == l_true) { model_ref model; solver.get_model(model); model_smt2_pp(std::cout, m, *model, 0); TRACE("pb", model_smt2_pp(tout, m, *model, 0);); } ENSURE(l_false == res); ext.m_clauses.reset(); } static void test_sorting_eq(unsigned n, unsigned k, sorting_network_encoding enc) { ENSURE(k < n); ast_manager m; reg_decl_plugins(m); ast_ext2 ext(m); expr_ref_vector in(m), out(m); for (unsigned i = 0; i < n; ++i) { in.push_back(m.mk_fresh_const("a",m.mk_bool_sort())); } smt_params fp; smt::kernel solver(m, fp); psort_nw sn(ext); sn.cfg().m_encoding = enc; expr_ref result(m); // equality: std::cout << "eq " << k << " out of " << n << " for encoding " << enc << "\n"; solver.push(); result = sn.eq(false, k, in.size(), in.c_ptr()); solver.assert_expr(result); for (expr* cl : ext.m_clauses) { solver.assert_expr(cl); } lbool res = solver.check(); if (res != l_true) { std::cout << res << "\n"; solver.display(std::cout); } ENSURE(res == l_true); solver.push(); for (unsigned i = 0; i < k; ++i) { solver.assert_expr(in[i].get()); } res = solver.check(); if (res != l_true) { std::cout << result << "\n" << ext.m_clauses << "\n"; } ENSURE(res == l_true); solver.assert_expr(in[k].get()); res = solver.check(); if (res == l_true) { TRACE("pb", unsigned sz = solver.size(); for (unsigned i = 0; i < sz; ++i) { tout << mk_pp(solver.get_formula(i), m) << "\n"; }); model_ref model; solver.get_model(model); model_smt2_pp(std::cout, m, *model, 0); TRACE("pb", model_smt2_pp(tout, m, *model, 0);); } ENSURE(res == l_false); solver.pop(1); ext.m_clauses.reset(); } static void test_sorting_le(unsigned n, unsigned k, sorting_network_encoding enc) { ast_manager m; reg_decl_plugins(m); ast_ext2 ext(m); expr_ref_vector in(m), out(m); for (unsigned i = 0; i < n; ++i) { in.push_back(m.mk_fresh_const("a",m.mk_bool_sort())); } smt_params fp; smt::kernel solver(m, fp); psort_nw sn(ext); sn.cfg().m_encoding = enc; expr_ref result(m); // B <= k std::cout << "le " << k << "\n"; solver.push(); result = sn.le(false, k, in.size(), in.c_ptr()); solver.assert_expr(result); for (expr* cls : ext.m_clauses) { solver.assert_expr(cls); } lbool res = solver.check(); if (res != l_true) { std::cout << res << "\n"; solver.display(std::cout); std::cout << "clauses: " << ext.m_clauses << "\n"; std::cout << "result: " << result << "\n"; } ENSURE(res == l_true); for (unsigned i = 0; i < k; ++i) { solver.assert_expr(in[i].get()); } res = solver.check(); if (res != l_true) { std::cout << res << "\n"; solver.display(std::cout); } ENSURE(res == l_true); solver.assert_expr(in[k].get()); res = solver.check(); if (res == l_true) { TRACE("pb", unsigned sz = solver.size(); for (unsigned i = 0; i < sz; ++i) { tout << mk_pp(solver.get_formula(i), m) << "\n"; }); model_ref model; solver.get_model(model); model_smt2_pp(std::cout, m, *model, 0); TRACE("pb", model_smt2_pp(tout, m, *model, 0);); } ENSURE(res == l_false); solver.pop(1); ext.m_clauses.reset(); } void test_sorting_ge(unsigned n, unsigned k, sorting_network_encoding enc) { ast_manager m; reg_decl_plugins(m); ast_ext2 ext(m); expr_ref_vector in(m), out(m); for (unsigned i = 0; i < n; ++i) { in.push_back(m.mk_fresh_const("a",m.mk_bool_sort())); } smt_params fp; smt::kernel solver(m, fp); psort_nw sn(ext); sn.cfg().m_encoding = enc; expr_ref result(m); // k <= B std::cout << "ge " << k << "\n"; solver.push(); result = sn.ge(false, k, in.size(), in.c_ptr()); solver.assert_expr(result); for (expr* cls : ext.m_clauses) { solver.assert_expr(cls); } lbool res = solver.check(); ENSURE(res == l_true); solver.push(); for (unsigned i = 0; i < n - k; ++i) { solver.assert_expr(m.mk_not(in[i].get())); } res = solver.check(); ENSURE(res == l_true); solver.assert_expr(m.mk_not(in[n - k].get())); res = solver.check(); if (res == l_true) { TRACE("pb", unsigned sz = solver.size(); for (unsigned i = 0; i < sz; ++i) { tout << mk_pp(solver.get_formula(i), m) << "\n"; }); model_ref model; solver.get_model(model); model_smt2_pp(std::cout, m, *model, 0); TRACE("pb", model_smt2_pp(tout, m, *model, 0);); } ENSURE(res == l_false); solver.pop(1); } void test_sorting5(unsigned n, unsigned k, sorting_network_encoding enc) { std::cout << "n: " << n << " k: " << k << "\n"; test_sorting_le(n, k, enc); test_sorting_eq(n, k, enc); test_sorting_ge(n, k, enc); } expr_ref naive_at_most1(expr_ref_vector const& xs) { ast_manager& m = xs.get_manager(); expr_ref_vector clauses(m); for (unsigned i = 0; i < xs.size(); ++i) { for (unsigned j = i + 1; j < xs.size(); ++j) { clauses.push_back(m.mk_not(m.mk_and(xs[i], xs[j]))); } } return mk_and(clauses); } void test_at_most_1(unsigned n, bool full, sorting_network_encoding enc) { ast_manager m; reg_decl_plugins(m); expr_ref_vector in(m), out(m); for (unsigned i = 0; i < n; ++i) { in.push_back(m.mk_fresh_const("a",m.mk_bool_sort())); } ast_ext2 ext(m); psort_nw sn(ext); sn.cfg().m_encoding = enc; expr_ref result1(m), result2(m); result1 = sn.le(full, 1, in.size(), in.c_ptr()); result2 = naive_at_most1(in); std::cout << "clauses: " << ext.m_clauses << "\n-----\n"; //std::cout << "encoded: " << result1 << "\n"; //std::cout << "naive: " << result2 << "\n"; smt_params fp; smt::kernel solver(m, fp); for (expr* cls : ext.m_clauses) { solver.assert_expr(cls); } if (full) { solver.push(); solver.assert_expr(m.mk_not(m.mk_eq(result1, result2))); std::cout << result1 << "\n"; lbool res = solver.check(); if (res == l_true) { model_ref model; solver.get_model(model); model_smt2_pp(std::cout, m, *model, 0); } VERIFY(l_false == res); solver.pop(1); } if (n >= 9) return; if (n <= 1) return; for (unsigned i = 0; i < static_cast(1 << n); ++i) { std::cout << "checking n: " << n << " bits: "; for (unsigned j = 0; j < n; ++j) { bool is_true = (i & (1 << j)) != 0; std::cout << (is_true?"1":"0"); } std::cout << "\n"; solver.push(); unsigned k = 0; for (unsigned j = 0; j < n; ++j) { bool is_true = (i & (1 << j)) != 0; expr_ref atom(m); atom = is_true ? in[j].get() : m.mk_not(in[j].get()); solver.assert_expr(atom); std::cout << atom << "\n"; if (is_true) ++k; } if (k > 1) { solver.assert_expr(result1); } else if (!full) { solver.pop(1); continue; } else { solver.assert_expr(m.mk_not(result1)); } VERIFY(l_false == solver.check()); solver.pop(1); } } static void test_at_most1(sorting_network_encoding enc) { ast_manager m; reg_decl_plugins(m); expr_ref_vector in(m), out(m); for (unsigned i = 0; i < 5; ++i) { in.push_back(m.mk_fresh_const("a",m.mk_bool_sort())); } in[4] = in[3].get(); ast_ext2 ext(m); psort_nw sn(ext); sn.cfg().m_encoding = enc; expr_ref result(m); result = sn.le(true, 1, in.size(), in.c_ptr()); //std::cout << result << "\n"; //std::cout << ext.m_clauses << "\n"; } static void test_sorting5(sorting_network_encoding enc) { test_sorting_eq(11,7, enc); for (unsigned n = 3; n < 20; n += 2) { for (unsigned k = 1; k < n; ++k) { test_sorting5(n, k, enc); } } } static void tst_sorting_network(sorting_network_encoding enc) { for (unsigned i = 1; i < 17; ++i) { test_at_most_1(i, true, enc); test_at_most_1(i, false, enc); } for (unsigned n = 2; n < 20; ++n) { std::cout << "verify eq-1 out of " << n << "\n"; test_sorting_eq(n, 1, enc); test_eq1(n, enc); } test_at_most1(enc); test_sorting5(enc); } static void test_pb(unsigned max_w, unsigned sz, unsigned_vector& ws) { if (ws.empty()) { for (unsigned w = 1; w <= max_w; ++w) { ws.push_back(w); test_pb(max_w, sz, ws); ws.pop_back(); } } else if (ws.size() < sz) { for (unsigned w = ws.back(); w <= max_w; ++w) { ws.push_back(w); test_pb(max_w, sz, ws); ws.pop_back(); } } else { SASSERT(ws.size() == sz); ast_manager m; reg_decl_plugins(m); expr_ref_vector xs(m), nxs(m); expr_ref ge(m), eq(m); smt_params fp; smt::kernel solver(m, fp); for (unsigned i = 0; i < sz; ++i) { xs.push_back(m.mk_const(symbol(i), m.mk_bool_sort())); nxs.push_back(m.mk_not(xs.back())); } std::cout << ws << " " << "\n"; for (unsigned k = max_w + 1; k < ws.size()*max_w; ++k) { ast_ext2 ext(m); psort_nw sn(ext); solver.push(); //std::cout << "bound: " << k << "\n"; //std::cout << ws << " " << xs << "\n"; ge = sn.ge(k, sz, ws.c_ptr(), xs.c_ptr()); //std::cout << "ge: " << ge << "\n"; for (expr* cls : ext.m_clauses) { solver.assert_expr(cls); } // solver.display(std::cout); // for each truth assignment to xs, validate // that circuit computes the right value for ge for (unsigned i = 0; i < (1ul << sz); ++i) { solver.push(); unsigned sum = 0; for (unsigned j = 0; j < sz; ++j) { if (0 == ((1 << j) & i)) { solver.assert_expr(xs.get(j)); sum += ws[j]; } else { solver.assert_expr(nxs.get(j)); } } // std::cout << "bound: " << k << "\n"; // std::cout << ws << " " << xs << "\n"; // std::cout << sum << " >= " << k << " : " << (sum >= k) << " "; solver.push(); if (sum < k) { solver.assert_expr(m.mk_not(ge)); } else { solver.assert_expr(ge); } // solver.display(std::cout) << "\n"; VERIFY(solver.check() == l_true); solver.pop(1); solver.push(); if (sum >= k) { solver.assert_expr(m.mk_not(ge)); } else { solver.assert_expr(ge); } // solver.display(std::cout) << "\n"; VERIFY(l_false == solver.check()); solver.pop(1); solver.pop(1); } solver.pop(1); solver.push(); eq = sn.eq(k, sz, ws.c_ptr(), xs.c_ptr()); for (expr* cls : ext.m_clauses) { solver.assert_expr(cls); } // for each truth assignment to xs, validate // that circuit computes the right value for ge for (unsigned i = 0; i < (1ul << sz); ++i) { solver.push(); unsigned sum = 0; for (unsigned j = 0; j < sz; ++j) { if (0 == ((1 << j) & i)) { solver.assert_expr(xs.get(j)); sum += ws[j]; } else { solver.assert_expr(nxs.get(j)); } } solver.push(); if (sum != k) { solver.assert_expr(m.mk_not(eq)); } else { solver.assert_expr(eq); } // solver.display(std::cout) << "\n"; VERIFY(solver.check() == l_true); solver.pop(1); solver.push(); if (sum == k) { solver.assert_expr(m.mk_not(eq)); } else { solver.assert_expr(eq); } VERIFY(l_false == solver.check()); solver.pop(1); solver.pop(1); } solver.pop(1); } } } static void tst_pb() { unsigned_vector ws; test_pb(3, 3, ws); } void tst_sorting_network() { tst_pb(); tst_sorting_network(sorting_network_encoding::unate_at_most); tst_sorting_network(sorting_network_encoding::circuit_at_most); tst_sorting_network(sorting_network_encoding::ordered_at_most); tst_sorting_network(sorting_network_encoding::grouped_at_most); tst_sorting_network(sorting_network_encoding::bimander_at_most); test_sorting1(); test_sorting2(); test_sorting3(); test_sorting4(); } z3-z3-4.8.7/src/test/stack.cpp000066400000000000000000000031601356505360400160060ustar00rootroot00000000000000/*++ Copyright (c) 2007 Microsoft Corporation Module Name: stack.cpp Abstract: Low level stack (aka stack-based allocator). Author: Leonardo (leonardo) 2011-02-27 Notes: --*/ #include #include "util/stack.h" #include "util/vector.h" typedef std::pair point; static void tst1() { stack s; point * p1 = new (s) point(10, 20); point * p2 = new (s) point(30, 40); void * ptr = s.allocate(16000); ENSURE(p2->first == 30 && p2->second == 40); ENSURE(p1->first == 10 && p1->second == 20); s.deallocate(static_cast(ptr)); s.deallocate(p2); s.deallocate(p1); } static void tst2(unsigned num, unsigned del_rate) { ptr_vector ptrs; stack s; for (unsigned i = 0; i < num; i++) { ENSURE(ptrs.empty() == s.empty()); ENSURE(s.empty() || ptrs.back() == s.top()); if (!ptrs.empty() && rand() % del_rate == 0) { s.deallocate(); ptrs.pop_back(); } else { unsigned size; if (rand()%10 == 0) { size = 8192 + rand()%800; } else { size = rand()%100; } char * ptr = static_cast(s.allocate(size)); ptrs.push_back(ptr); } } while (s.empty()) { ENSURE(ptrs.empty() == s.empty()); ENSURE(s.empty() || ptrs.back() == s.top()); s.deallocate(); ptrs.pop_back(); } } void tst_stack() { tst1(); tst2(1000, 10); tst2(2000, 2); tst2(100000, 10); tst2(300000, 5); tst2(300000, 2); tst2(300000, 7); } z3-z3-4.8.7/src/test/string_buffer.cpp000066400000000000000000000015521356505360400175430ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: tst_string_buffer.cpp Abstract: Test string buffer Author: Leonardo de Moura (leonardo) 2006-10-14. Revision History: --*/ #include #include "util/debug.h" #include "util/string_buffer.h" #include "util/trace.h" static void tst1() { string_buffer<> b; b << "Testing" << 10 << true; ENSURE(strcmp(b.c_str(), "Testing10true") == 0); } static void tst2() { string_buffer<> b; for (unsigned i = 0; i < 10000; i++) { int r = rand() % 10; b << r; } TRACE("string_buffer", tout << b.c_str() << "\n";); ENSURE(strlen(b.c_str()) == 10000); } static void tst3() { string_buffer<32> b; string_buffer<128> b2; b2 << "World"; b << "Hello" << " " << b2; ENSURE(strcmp(b.c_str(), "Hello World") == 0); } void tst_string_buffer() { tst1(); tst2(); tst3(); } z3-z3-4.8.7/src/test/substitution.cpp000066400000000000000000000030561356505360400174610ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "ast/expr_substitution.h" #include "smt/params/smt_params.h" #include "ast/substitution/substitution.h" #include "ast/substitution/unifier.h" #include "ast/bv_decl_plugin.h" #include "ast/ast_pp.h" #include "ast/arith_decl_plugin.h" #include "ast/reg_decl_plugins.h" void tst_substitution() { memory::initialize(0); smt_params params; params.m_model = true; enable_trace("subst_bug"); ast_manager m; reg_decl_plugins(m); var_ref v1(m.mk_var(0, m.mk_bool_sort()), m); var_ref v2(m.mk_var(1, m.mk_bool_sort()), m); var_ref v3(m.mk_var(2, m.mk_bool_sort()), m); var_ref v4(m.mk_var(3, m.mk_bool_sort()), m); substitution subst(m); subst.reserve(1,4); unifier unif(m); bool ok1 = unif(v1.get(), v2.get(), subst, false); bool ok2 = unif(v2.get(), v1.get(), subst, false); (void)ok1; (void)ok2; expr_ref res(m); TRACE("substitution", subst.display(tout);); TRACE("substitution", tout << ok1 << " " << ok2 << "\n";); subst.display(std::cout); subst.apply(v1.get(), res); TRACE("substitution", tout << mk_pp(res, m) << "\n";); expr_ref q(m), body(m); sort_ref_vector sorts(m); svector names; sorts.push_back(m.mk_bool_sort()); names.push_back(symbol("dude")); body = m.mk_and(m.mk_eq(v1,v2), m.mk_eq(v3,v4)); q = m.mk_forall(sorts.size(), sorts.c_ptr(), names.c_ptr(), body); subst.apply(q, res); TRACE("substitution", tout << mk_pp(q, m) << "\n->\n" << mk_pp(res, m) << "\n";); } z3-z3-4.8.7/src/test/symbol.cpp000066400000000000000000000023361356505360400162120ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: tst_symbol.cpp Abstract: Test symbol class. Author: Leonardo de Moura (leonardo) 2006-09-13. Revision History: --*/ #include #include "util/symbol.h" #include "util/debug.h" static void tst1() { symbol s1("foo"); symbol s2("boo"); symbol s3("foo"); ENSURE(s1 != s2); ENSURE(s1 == s3); std::cout << s1 << " " << s2 << " " << s3 << "\n"; ENSURE(s1 == "foo"); ENSURE(s1 != "boo"); ENSURE(s2 != "foo"); ENSURE(s3 == "foo"); ENSURE(s2 == "boo"); ENSURE(lt(s2, s1)); ENSURE(!lt(s1, s2)); ENSURE(!lt(s1, s3)); ENSURE(lt(symbol("abcc"), symbol("abcd"))); ENSURE(!lt(symbol("abcd"), symbol("abcc"))); ENSURE(lt(symbol("abc"), symbol("abcc"))); ENSURE(!lt(symbol("abcd"), symbol("abc"))); ENSURE(lt(symbol(10), s1)); ENSURE(!lt(s1, symbol(10))); ENSURE(lt(symbol(10), symbol(20))); ENSURE(!lt(symbol(20), symbol(10))); ENSURE(!lt(symbol(10), symbol(10))); ENSURE(lt(symbol("a"), symbol("b"))); ENSURE(!lt(symbol("z"), symbol("b"))); ENSURE(!lt(symbol("zzz"), symbol("b"))); ENSURE(lt(symbol("zzz"), symbol("zzzb"))); } void tst_symbol() { tst1(); } z3-z3-4.8.7/src/test/symbol_table.cpp000066400000000000000000000016661356505360400173660ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: tst_symbol_table.cpp Abstract: Test symbol table module. Author: Leonardo de Moura (leonardo) 2006-09-19. Revision History: --*/ #include "util/symbol_table.h" static void tst1() { symbol_table t; t.insert(symbol("foo"), 35); ENSURE(t.contains(symbol("foo"))); ENSURE(!t.contains(symbol("boo"))); t.begin_scope(); t.insert(symbol("boo"), 20); ENSURE(t.contains(symbol("boo"))); int tmp; ENSURE(t.find(symbol("boo"), tmp) && tmp == 20); ENSURE(t.find(symbol("foo"), tmp) && tmp == 35); t.insert(symbol("foo"), 100); ENSURE(t.find(symbol("foo"), tmp) && tmp == 100); t.end_scope(); ENSURE(t.find(symbol("foo"), tmp) && tmp == 35); ENSURE(!t.contains(symbol("boo"))); t.reset(); ENSURE(!t.contains(symbol("boo"))); ENSURE(!t.contains(symbol("foo"))); } void tst_symbol_table() { tst1(); } z3-z3-4.8.7/src/test/tbv.cpp000066400000000000000000000040371356505360400155000ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "muz/rel/tbv.h" static void tst1(unsigned num_bits) { tbv_manager m(num_bits); tbv* b1 = m.allocate1(); tbv* b0 = m.allocate0(); tbv* bX = m.allocateX(); tbv* bN = m.allocate(31); m.display(std::cout, *b0) << "\n"; m.display(std::cout, *b1) << "\n"; m.display(std::cout, *bX) << "\n"; m.display(std::cout, *bN) << "\n"; ENSURE(!m.equals(*b1,*b0)); ENSURE(!m.equals(*b1,*bX)); ENSURE(!m.equals(*b0,*bX)); m.set_and(*bX,*b0); ENSURE(m.equals(*b0,*bX)); ENSURE(!m.equals(*b1,*bX)); m.copy(*bX,*b1); ENSURE(m.equals(*b1,*bX)); ENSURE(!m.equals(*b0,*bX)); m.fillX(*bX); VERIFY(m.intersect(*bX,*b0,*bN)); ENSURE(m.equals(*b0, *bN)); VERIFY(!m.intersect(*b0,*b1,*bN)); m.fill1(*b1); bit_vector to_delete; to_delete.reserve(num_bits, false); tbv_manager m2(num_bits-2); to_delete.set(1); to_delete.set(3); m.set(*b1, 2, BIT_0); m.set(*b1, 4, BIT_x); tbv_ref b2(m2, m2.project(to_delete, *b1)); m.display(std::cout, *b1) << " -> "; m2.display(std::cout, *b2) << "\n"; m.deallocate(b0); m.deallocate(b1); m.deallocate(bX); m.deallocate(bN); } static void tst0() { tbv_manager m(0); tbv_ref t1(m), t2(m), t3(m); t1 = m.allocate1(); t2 = m.allocate0(); t3 = m.allocateX(); m.display(std::cout, *t1) << "\n"; m.display(std::cout, *t2) << "\n"; m.display(std::cout, *t3) << "\n"; ENSURE(m.equals(*t1, *t2)); ENSURE(m.equals(*t1, *t3)); } static void tst2(unsigned num_bits) { tbv_manager m(num_bits); tbv_ref t(m), t2(m); for (unsigned i = 0; i < 55; ++i) { t = m.allocate(i); ENSURE(m.is_well_formed(*t)); t2 = m.allocate(i+1); VERIFY(!m.set_and(*t2, *t)); ENSURE(!m.is_well_formed(*t2)); } } void tst_tbv() { tst0(); tst1(31); tst1(11); tst1(15); tst1(16); tst1(17); tst2(31); tst2(11); tst2(15); tst2(16); tst2(17); } z3-z3-4.8.7/src/test/test_util.h000066400000000000000000000035171356505360400163700ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #pragma once #include "util/stopwatch.h" struct test_context { bool test_ok; unsigned test_fails; unsigned fails; double test_time; stopwatch test_timer; test_context() : fails(0) {} }; #undef min #undef max #define TEST_CLASS(context, CLASS_NAME, TYPE, CALL_DESTRUCTORS) \ context.test_fails = 0; \ cout << "" << #CLASS_NAME << "<" << #TYPE << ">" << endl; \ CLASS_NAME ## _test< TYPE, CALL_DESTRUCTORS >::run_tests(context); \ context.fails += context.test_fails; #define TEST_METHOD(context, METHOD) \ cout << "\t" << #METHOD << "... "; \ context.test_timer.reset(); \ context.test_ok = test_ ## METHOD; \ context.test_time = context.test_timer.get_seconds(); \ if (context.test_ok) { \ cout << "PASS"; \ if (context.test_time > 0) { \ cout << "(" << context.test_time << "s)"; \ } \ cout << endl; \ } \ else { \ cout << "FAIL"; \ if (context.test_time > 0) { \ cout << "(" << context.test_time << "s)"; \ } \ cout << endl; \ ++ context.test_fails; \ } \ z3-z3-4.8.7/src/test/theory_dl.cpp000066400000000000000000000016201356505360400166710ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "smt/smt_context.h" #include "ast/dl_decl_plugin.h" #include "ast/ast_pp.h" #include "model/model_v2_pp.h" #include "ast/reg_decl_plugins.h" void tst_theory_dl() { ast_manager m; smt_params params; params.m_model = true; datalog::dl_decl_util u(m); smt::context ctx(m, params); reg_decl_plugins(m); expr_ref a(m), b(m), c(m); sort_ref s(m); s = u.mk_sort(symbol("S"),111); a = m.mk_const(symbol("a"),s); b = m.mk_const(symbol("b"),s); ctx.assert_expr(u.mk_lt(a, b)); ctx.check(); ref md; ctx.get_model(md); model_v2_pp(std::cout, *md.get()); c = m.mk_const(symbol("c"),s); ctx.assert_expr(u.mk_lt(b, c)); ctx.check(); ctx.get_model(md); model_v2_pp(std::cout, *md.get()); ctx.assert_expr(u.mk_lt(c, a)); std::cout << ctx.check() << "\n"; } z3-z3-4.8.7/src/test/theory_pb.cpp000066400000000000000000000107471356505360400167050ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "smt/smt_context.h" #include "ast/ast_pp.h" #include "model/model_v2_pp.h" #include "ast/reg_decl_plugins.h" #include "smt/theory_pb.h" #include "ast/rewriter/th_rewriter.h" 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) { if (k & (1 << i)) { lits[i] = smt::true_literal; t++; } else { lits[i] = smt::false_literal; } } return t; } class pb_fuzzer { ast_manager& m; random_gen rand; smt_params params; smt::context ctx; expr_ref_vector vars; public: pb_fuzzer(ast_manager& m): m(m), rand(0), ctx(m, params), vars(m) { params.m_model = true; unsigned N = 3; for (unsigned i = 0; i < N; ++i) { std::stringstream strm; strm << "b" << i; vars.push_back(m.mk_const(symbol(strm.str().c_str()), m.mk_bool_sort())); std::cout << "(declare-const " << strm.str() << " Bool)\n"; } } void fuzz() { enable_trace("pb"); unsigned nr = 0; for (unsigned i = 0; i < 100000; ++i) { fuzz_round(nr, 2); } } private: void add_ineq() { pb_util pb(m); expr_ref fml(m), tmp(m); th_rewriter rw(m); vector coeffs(vars.size()); expr_ref_vector args(vars); while (true) { rational k(rand(6)); for (unsigned i = 0; i < coeffs.size(); ++i) { int v = 3 - rand(5); coeffs[i] = rational(v); if (coeffs[i].is_neg()) { args[i] = m.mk_not(args[i].get()); coeffs[i].neg(); k += coeffs[i]; } } fml = pb.mk_ge(args.size(), coeffs.c_ptr(), args.c_ptr(), k); rw(fml, tmp); rw(tmp, tmp); if (pb.is_ge(tmp)) { fml = tmp; break; } } std::cout << "(assert " << fml << ")\n"; ctx.assert_expr(fml); std::cout << ";asserted\n"; } void fuzz_round(unsigned& num_rounds, unsigned lvl) { unsigned num_rounds2 = 0; lbool is_sat = l_true; std::cout << "(push)\n"; ctx.push(); unsigned r = 0; while (is_sat == l_true && r <= num_rounds + 1) { add_ineq(); std::cout << "(check-sat)\n"; is_sat = ctx.check(); if (lvl > 0 && is_sat == l_true) { fuzz_round(num_rounds2, lvl-1); } ++r; } num_rounds = r; std::cout << "; number of rounds: " << num_rounds << " level: " << lvl << "\n"; ctx.pop(1); std::cout << "(pop)\n"; } }; static void fuzz_pb() { ast_manager m; reg_decl_plugins(m); pb_fuzzer fuzzer(m); fuzzer.fuzz(); } void tst_theory_pb() { fuzz_pb(); ast_manager m; smt_params params; params.m_model = true; reg_decl_plugins(m); expr_ref tmp(m); enable_trace("pb"); for (unsigned N = 4; N < 11; ++N) { for (unsigned i = 0; i < (1u << N); ++i) { smt::literal_vector lits(N, smt::false_literal); unsigned k = populate_literals(i, lits); std::cout << "k:" << k << " " << N << "\n"; std::cout.flush(); TRACE("pb", tout << "k " << k << ": " << lits << "\n";); { smt::context ctx(m, params); ctx.push(); smt::literal l = smt::theory_pb::assert_ge(ctx, k+1, lits.size(), lits.c_ptr()); if (l != smt::false_literal) { ctx.assign(l, nullptr, false); TRACE("pb", tout << "assign: " << l << "\n"; ctx.display(tout);); VERIFY(l_false == ctx.check()); } ctx.pop(1); } { smt::context ctx(m, params); ctx.push(); smt::literal l = smt::theory_pb::assert_ge(ctx, k, lits.size(), lits.c_ptr()); ENSURE(l != smt::false_literal); ctx.assign(l, nullptr, false); TRACE("pb", ctx.display(tout);); VERIFY(l_true == ctx.check()); ctx.pop(1); } } } } z3-z3-4.8.7/src/test/timeout.cpp000066400000000000000000000003161356505360400163670ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "util/timeout.h" #include "util/trace.h" #ifdef _WINDOWS #include void tst_timeout() { } #else void tst_timeout() { } #endif z3-z3-4.8.7/src/test/total_order.cpp000066400000000000000000000073741356505360400172320ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: total_order.cpp Abstract: Author: Leonardo de Moura (leonardo) 2010-07-01. Revision History: --*/ #include "util/total_order.h" #include "util/timeit.h" static void tst1() { uint_total_order to; to.insert(1); to.insert_after(1, 2); to.insert_after(1, 3); ENSURE(to.lt(1, 2)); ENSURE(to.lt(3, 2)); ENSURE(to.lt(1, 3)); ENSURE(!to.lt(2, 3)); ENSURE(!to.lt(3, 1)); ENSURE(!to.lt(2, 2)); std::cout << to << "\n"; } static void tst2() { uint_total_order to; to.insert(1); to.insert_after(1, 2); to.insert_after(2, 3); for (unsigned i = 0; i < 1000; i++) { to.move_after(3, 1); to.move_after(1, 2); to.move_after(2, 3); ENSURE(to.lt(1,2)); ENSURE(to.lt(2,3)); } } static void tst3(unsigned sz, unsigned num_rounds) { uint_total_order to; to.insert(0); for (unsigned i = 0; i < sz; i++) { to.insert_after(i, i+1); } for (unsigned i = 0; i < num_rounds; i++) { unsigned v1 = rand() % sz; unsigned v2 = rand() % sz; if (v1 != v2) to.move_after(v1, v2); if (i % 1000 == 0) { std::cout << "*"; std::cout.flush(); } } std::cout << std::endl; } void move_after(unsigned_vector & v, unsigned_vector & inv_v, unsigned a, unsigned b) { if (a == b) return; // std::cout << std::endl; // display(std::cout, v.begin(), v.end()); std::cout << std::endl; // std::cout << "move_after(" << a << ", " << b << ")\n"; unsigned pos_a = inv_v[a]; unsigned pos_b = inv_v[b]; ENSURE(pos_a != pos_b); if (pos_b < pos_a) { for (unsigned i = pos_b; i < pos_a; i++) { v[i] = v[i+1]; inv_v[v[i+1]] = i; } v[pos_a] = b; inv_v[b] = pos_a; ENSURE(inv_v[b] == inv_v[a] + 1); } else { ENSURE(pos_b > pos_a); for (unsigned i = pos_b; i > pos_a + 1; i--) { v[i] = v[i-1]; inv_v[v[i-1]] = i; } v[pos_a+1] = b; inv_v[b] = pos_a+1; ENSURE(inv_v[b] == inv_v[a] + 1); } // display(std::cout, v.begin(), v.end()); std::cout << std::endl; } static void tst4(unsigned sz, unsigned num_rounds) { uint_total_order to; unsigned_vector v; unsigned_vector inv_v; to.insert(0); v.push_back(0); inv_v.push_back(0); for (unsigned i = 0; i < sz; i++) { to.insert_after(i, i+1); v.push_back(i+1); inv_v.push_back(i+1); } for (unsigned i = 0; i < num_rounds; i++) { unsigned v1 = rand() % sz; unsigned v2 = rand() % sz; if (v1 != v2) { to.move_after(v1, v2); move_after(v, inv_v, v1, v2); } for (unsigned k = 0; k < sz - 1; k++) { ENSURE(inv_v[v[k]] == k); ENSURE(to.lt(v[k], v[k+1])); } if (i % 1000 == 0) { std::cout << "*"; std::cout.flush(); } } std::cout << std::endl; } static void bad_case(unsigned sz, unsigned num_rounds) { uint_total_order to; to.insert(0); for (unsigned i = 0; i < sz; i++) { to.insert_after(i, i+1); } for (unsigned i = 0; i < num_rounds; i++) { to.move_after(sz, 0); for (unsigned j = 0; j < sz; j++) { to.move_after(j, j+1); } if (i % 10 == 0) { std::cout << "*"; std::cout.flush(); } } std::cout << std::endl; } void tst_total_order() { bad_case(100, 1000); tst1(); tst2(); tst4(3, 1000000); tst4(100, 100000); tst4(512, 100000); tst4(1000, 100000); tst3(100, 100000); tst3(1000, 100000); } z3-z3-4.8.7/src/test/trigo.cpp000066400000000000000000000123751356505360400160350ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: trigo.cpp Abstract: Test trigonometric primitives in the interval class. Author: Leonardo de Moura (leonardo) 2012-08-20 Revision History: --*/ #include #include "math/interval/interval_def.h" #include "util/dependency.h" #include "util/mpq.h" #include "ast/ast.h" #include "util/debug.h" #include "test/im_float_config.h" #include "util/rlimit.h" #define PREC 100000 static void tst_sine_core(std::ostream & out, unsynch_mpq_manager & nm, interval_manager & im, mpq & a, unsigned k) { scoped_mpq lo(nm), hi(nm); im.sine(a, k, lo, hi); nm.display(out, lo); out << " <= Sin["; nm.display(out, a); out << "]\n"; out << "Sin["; nm.display(out, a); out << "] <= "; nm.display(out, hi); out << "\n"; } static void tst_sine(std::ostream & out, unsigned N, unsigned k) { unsynch_mpq_manager nm; reslimit rl; interval_manager im(rl, nm); scoped_mpq a(nm); nm.set(a, 0); tst_sine_core(out, nm, im, a, 1); for (unsigned i = 0; i < N; i++) { nm.set(a, 4 * (rand() % PREC), PREC); if (rand() % 2 == 0) nm.neg(a); tst_sine_core(out, nm, im, a, k); } } static void tst_cosine_core(std::ostream & out, unsynch_mpq_manager & nm, interval_manager & im, mpq & a, unsigned k) { scoped_mpq lo(nm), hi(nm); im.cosine(a, k, lo, hi); nm.display(out, lo); out << " <= Cos["; nm.display(out, a); out << "]\n"; out << "Cos["; nm.display(out, a); out << "] <= "; nm.display(out, hi); out << "\n"; } static void tst_cosine(std::ostream & out, unsigned N, unsigned k) { reslimit rl; unsynch_mpq_manager nm; interval_manager im(rl, nm); scoped_mpq a(nm); nm.set(a, 0); tst_cosine_core(out, nm, im, a, 1); for (unsigned i = 0; i < N; i++) { nm.set(a, 4 * (rand() % PREC), PREC); if (rand() % 2 == 0) nm.neg(a); tst_cosine_core(out, nm, im, a, k); } } template static void tst_float_sine_core(std::ostream & out, fmanager & fm, interval_manager > & im, typename fmanager::numeral & a, unsigned k) { _scoped_numeral lo(fm), hi(fm); im.sine(a, k, lo, hi); out << fm.to_rational_string(lo) << " <= Sin[" << fm.to_rational_string(a) << "]\n"; out << "Sin[" << fm.to_rational_string(a) << "] <= " << fm.to_rational_string(hi) << "\n"; } const unsigned EBITS = 11; const unsigned SBITS = 53; template static void tst_float_sine(std::ostream & out, unsigned N, unsigned k) { reslimit rl; fmanager fm; interval_manager > im(rl, im_float_config(fm, EBITS, SBITS)); _scoped_numeral a(fm); fm.set(a, EBITS, SBITS, static_cast(0)); tst_float_sine_core(out, fm, im, a, 1); // fm.set(a, EBITS, SBITS, MPF_ROUND_TOWARD_POSITIVE, 25336, 100000); // tst_float_sine_core(out, fm, im, a, k); // return; for (unsigned i = 0; i < N; i++) { unsigned n = 4 * (rand() % PREC); unsigned d = PREC; TRACE("sine", tout << "next-val : " << n << "/" << d << "\n";); fm.set(a, EBITS, SBITS, MPF_ROUND_TOWARD_POSITIVE, n, d); if (rand() % 2 == 0) fm.neg(a); tst_float_sine_core(out, fm, im, a, k); } } #if 0 static void tst_mpf_bug() { mpf_manager fm; scoped_mpf a(fm), b(fm), c(fm); fm.set(a, EBITS, SBITS, 2); fm.set(b, EBITS, SBITS, 3); std::cout << "a: " << fm.to_double(a) << "\n"; std::cout << "b: " << fm.to_double(b) << "\n"; fm.mul(MPF_ROUND_TOWARD_NEGATIVE, a, b, c); std::cout << "c: " << fm.to_double(c) << "\n"; } #endif static void tst_e(std::ostream & out) { reslimit rl; unsynch_mpq_manager nm; interval_manager im(rl, nm); im_default_config::interval r; for (unsigned i = 0; i < 64; i++) { im.e(i, r); out << nm.to_string(im.lower(r)) << " <= E\n"; out << "E <= " << nm.to_string(im.upper(r)) << "\n"; } im.del(r); } static void tst_e_float(std::ostream & out) { std::cout << "e float...\n"; reslimit rl; unsynch_mpq_manager qm; mpf_manager fm; interval_manager > im(rl, fm); scoped_mpq q(qm); im_float_config::interval r; for (unsigned i = 0; i < 64; i++) { im.e(i, r); out << fm.to_rational_string(im.lower(r)) << " <= E\n"; out << "E <= " << fm.to_rational_string(im.upper(r)) << "\n"; } im.del(r); } void tst_trigo() { // enable_trace("sine"); // enable_trace("sine_bug"); // enable_trace("mpf_mul_bug"); std::ofstream out("trigo-lemmas.math"); tst_e_float(out); tst_e(out); tst_float_sine(out, 100, 5); tst_float_sine(out, 100, 7); tst_sine(out, 200, 3); tst_sine(out, 200, 5); tst_sine(out, 200, 9); tst_cosine(out, 200, 3); tst_cosine(out, 200, 5); tst_cosine(out, 200, 9); } z3-z3-4.8.7/src/test/udoc_relation.cpp000066400000000000000000000727261356505360400175460ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include "muz/rel/udoc_relation.h" #include "util/trace.h" #include "util/vector.h" #include "ast/ast.h" #include "ast/ast_pp.h" #include "ast/reg_decl_plugins.h" #include "util/sorting_network.h" #include "smt/smt_kernel.h" #include "model/model_smt2_pp.h" #include "smt/params/smt_params.h" #include "ast/ast_util.h" #include "ast/rewriter/expr_safe_replace.h" #include "ast/rewriter/th_rewriter.h" #include "muz/rel/dl_relation_manager.h" #include "muz/fp/dl_register_engine.h" #include "muz/rel/rel_context.h" #include "ast/bv_decl_plugin.h" #include "muz/rel/check_relation.h" class udoc_tester { typedef datalog::relation_base relation_base; typedef datalog::udoc_relation udoc_relation; typedef datalog::udoc_plugin udoc_plugin; typedef datalog::relation_signature relation_signature; typedef datalog::relation_fact relation_fact; typedef scoped_ptr rel_mut; typedef scoped_ptr rel_union; struct init { init(ast_manager& m) { reg_decl_plugins(m); } }; random_gen m_rand; ast_manager m; init m_init; bv_util bv; expr_ref_vector m_vars; smt_params m_smt_params; datalog::register_engine m_reg; datalog::context m_ctx; datalog::rel_context rc; udoc_plugin& p; datalog::check_relation_plugin& cr; tbit choose_tbit() { switch (m_rand(3)) { case 0: return BIT_0; case 1: return BIT_1; default : return BIT_x; } } tbv* mk_rand_tbv(doc_manager& dm) { tbv* result = dm.tbvm().allocate(); for (unsigned i = 0; i < dm.num_tbits(); ++i) { dm.tbvm().set(*result, i, choose_tbit()); } return result; } tbv* mk_rand_tbv(doc_manager& dm, tbv const& pos) { tbv* result = dm.tbvm().allocate(); for (unsigned i = 0; i < dm.num_tbits(); ++i) { if (pos[i] == BIT_x) { dm.tbvm().set(*result, i, choose_tbit()); } else { dm.tbvm().set(*result, i, pos[i]); } } return result; } doc* mk_rand_doc(doc_manager& dm, unsigned num_diff) { tbv_ref t(dm.tbvm()); doc_ref result(dm); t = mk_rand_tbv(dm); result = dm.allocate(*t); ENSURE(dm.tbvm().equals(*t, result->pos())); for (unsigned i = 0; i < num_diff; ++i) { t = mk_rand_tbv(dm, result->pos()); if (dm.tbvm().equals(*t, result->pos())) { return nullptr; } if (!result->neg().is_empty() && dm.tbvm().equals(*t, result->neg()[0])) { continue; } result->neg().push_back(t.detach()); } ENSURE(dm.well_formed(*result)); return result.detach(); } void mk_rand_udoc(doc_manager& dm, unsigned num_elems, unsigned num_diff, udoc& result) { result.reset(dm); for (unsigned i = 0; i < num_elems; ++i) { doc* d = mk_rand_doc(dm, num_diff); if (d) { result.push_back(d); } } } public: udoc_tester(): m_init(m), bv(m), m_vars(m), m_ctx(m, m_reg, m_smt_params), rc(m_ctx), p(dynamic_cast(*rc.get_rmanager().get_relation_plugin(symbol("doc")))), cr(dynamic_cast(*rc.get_rmanager().get_relation_plugin(symbol("check_relation")))) { cr.set_plugin(&p); } udoc_relation* mk_empty(relation_signature const& sig) { ENSURE(p.can_handle_signature(sig)); relation_base* empty = p.mk_empty(sig); return dynamic_cast(empty); } udoc_relation* mk_full(relation_signature const& sig) { func_decl_ref fn(m); fn = m.mk_func_decl(symbol("full"), sig.size(), sig.c_ptr(), m.mk_bool_sort()); relation_base* full = p.mk_full(fn, sig); return dynamic_cast(full); } void test1() { datalog::relation_signature sig; sig.push_back(bv.mk_sort(12)); sig.push_back(bv.mk_sort(6)); sig.push_back(bv.mk_sort(12)); datalog::relation_fact fact1(m), fact2(m), fact3(m); fact1.push_back(bv.mk_numeral(rational(1), 12)); fact1.push_back(bv.mk_numeral(rational(6), 6)); fact1.push_back(bv.mk_numeral(rational(56), 12)); fact2.push_back(bv.mk_numeral(rational(8), 12)); fact2.push_back(bv.mk_numeral(rational(16), 6)); fact2.push_back(bv.mk_numeral(rational(32), 12)); fact3.push_back(bv.mk_numeral(rational(32), 12)); fact3.push_back(bv.mk_numeral(rational(16), 6)); fact3.push_back(bv.mk_numeral(rational(4), 12)); relation_signature sig2; sig2.push_back(bv.mk_sort(3)); sig2.push_back(bv.mk_sort(6)); sig2.push_back(bv.mk_sort(3)); sig2.push_back(bv.mk_sort(3)); sig2.push_back(bv.mk_sort(3)); relation_base* t; udoc_relation* t1, *t2, *t3; expr_ref fml(m); test_filter_neg2(); test_join_project(); test_join_project2(); test_join_project3(); test_filter_neg4(false); test_filter_neg4(true); test_filter_neg5(false); test_filter_neg5(true); test_filter_neg(); test_filter_neg3(); test_join(1000); test_rename(); // empty { std::cout << "empty\n"; t = mk_empty(sig); t->display(std::cout); std::cout << "\n"; t->to_formula(fml); std::cout << fml << "\n"; t->deallocate(); } // full { std::cout << "full\n"; t = mk_full(sig); t->display(std::cout); std::cout << "\n"; t->to_formula(fml); std::cout << fml << "\n"; t->deallocate(); } // join { t1 = mk_full(sig); t2 = mk_full(sig); t3 = mk_empty(sig); unsigned_vector jc1, jc2; jc1.push_back(1); jc2.push_back(1); datalog::relation_join_fn* join_fn = p.mk_join_fn(*t1, *t2, jc1.size(), jc1.c_ptr(), jc2.c_ptr()); ENSURE(join_fn); t = (*join_fn)(*t1, *t2); cr.verify_join(*t1, *t2, *t, jc1, jc2); t->display(std::cout); std::cout << "\n"; t->deallocate(); t = (*join_fn)(*t1, *t3); cr.verify_join(*t1, *t3, *t, jc1, jc2); ENSURE(t->empty()); t->display(std::cout); std::cout << "\n"; t->deallocate(); t = (*join_fn)(*t3, *t3); cr.verify_join(*t3, *t3, *t, jc1, jc2); ENSURE(t->empty()); t->display(std::cout); std::cout << "\n"; t->deallocate(); dealloc(join_fn); t1->deallocate(); t2->deallocate(); t3->deallocate(); } // project { std::cout << "project\n"; t1 = mk_full(sig); unsigned_vector pc; pc.push_back(0); datalog::relation_transformer_fn* proj_fn = p.mk_project_fn(*t1, pc.size(), pc.c_ptr()); t = (*proj_fn)(*t1); cr.verify_project(*t1, *t, pc); t->display(std::cout); std::cout << "\n"; t->deallocate(); t1->reset(); t = (*proj_fn)(*t1); cr.verify_project(*t1, *t, pc); t->display(std::cout); std::cout << "\n"; t->deallocate(); t1->add_fact(fact1); t1->add_fact(fact2); t1->add_fact(fact3); t = (*proj_fn)(*t1); cr.verify_project(*t1, *t, pc); t1->display(std::cout); std::cout << "\n"; t->display(std::cout); std::cout << "\n"; t->deallocate(); dealloc(proj_fn); t1->deallocate(); } // union { t1 = mk_empty(sig); t2 = mk_empty(sig); udoc_relation* delta = mk_full(sig); t2->add_fact(fact1); t2->add_fact(fact2); t1->add_fact(fact3); expr_ref t10(m); t1->to_formula(t10); expr_ref delta0(m); delta->to_formula(delta0); rel_union union_fn = p.mk_union_fn(*t1, *t2, nullptr); t1->display(std::cout << "t1 before:"); std::cout << "\n"; (*union_fn)(*t1, *t2, delta); cr.verify_union(t10, *t2, *t1, delta0, delta); t1->display(std::cout << "t1 after:"); std::cout << "\n"; delta->display(std::cout << "delta:"); std::cout << "\n"; t1->deallocate(); t2->deallocate(); delta->deallocate(); } // filter_identical { t1 = mk_empty(sig2); unsigned_vector id; id.push_back(0); id.push_back(2); id.push_back(4); rel_mut filter_id = p.mk_filter_identical_fn(*t1, id.size(), id.c_ptr()); relation_fact f1(m); f1.push_back(bv.mk_numeral(rational(1),3)); f1.push_back(bv.mk_numeral(rational(1),6)); f1.push_back(bv.mk_numeral(rational(1),3)); f1.push_back(bv.mk_numeral(rational(1),3)); f1.push_back(bv.mk_numeral(rational(1),3)); t1->add_fact(f1); f1[4] = bv.mk_numeral(rational(2),3); t1->add_fact(f1); t1->display(std::cout); std::cout << "\n"; (*filter_id)(*t1); t1->display(std::cout); std::cout << "\n"; t1->deallocate(); } // tbv_manager::debug_alloc(); { relation_signature sig3; sig3.push_back(m.mk_bool_sort()); sig3.push_back(m.mk_bool_sort()); sig3.push_back(m.mk_bool_sort()); var_ref v0(m.mk_var(0, m.mk_bool_sort()),m); var_ref v1(m.mk_var(1, m.mk_bool_sort()),m); var_ref v2(m.mk_var(2, m.mk_bool_sort()),m); app_ref cond1(m); t1 = mk_full(sig3); cond1 = m.mk_eq(v0,v1); apply_filter(*t1, cond1); t1->deallocate(); } { relation_signature sig3; sig3.push_back(m.mk_bool_sort()); sig3.push_back(m.mk_bool_sort()); sig3.push_back(m.mk_bool_sort()); var_ref v0(m.mk_var(0, m.mk_bool_sort()),m); var_ref v1(m.mk_var(1, m.mk_bool_sort()),m); var_ref v2(m.mk_var(2, m.mk_bool_sort()),m); app_ref cond1(m); t1 = mk_full(sig3); cond1 = m.mk_or(m.mk_eq(v0,v1),m.mk_eq(v0,v2)); apply_filter(*t1, cond1); t1->deallocate(); } { relation_signature sig3; sig3.push_back(bv.mk_sort(1)); sig3.push_back(bv.mk_sort(1)); sig3.push_back(bv.mk_sort(1)); var_ref v0(m.mk_var(0, bv.mk_sort(1)),m); var_ref v1(m.mk_var(1, bv.mk_sort(1)),m); var_ref v2(m.mk_var(2, bv.mk_sort(1)),m); app_ref cond1(m); cond1 = m.mk_or(m.mk_eq(v0,v1),m.mk_eq(v0,v2)); t1 = mk_full(sig3); apply_filter(*t1, cond1); t1->deallocate(); } app_ref_vector conds(m); app_ref cond1(m); var_ref v0(m.mk_var(0, bv.mk_sort(3)),m); var_ref v1(m.mk_var(1, bv.mk_sort(6)),m); var_ref v2(m.mk_var(2, bv.mk_sort(3)),m); var_ref v3(m.mk_var(3, bv.mk_sort(3)),m); var_ref v4(m.mk_var(4, bv.mk_sort(3)),m); conds.push_back(m.mk_true()); conds.push_back(m.mk_false()); conds.push_back(m.mk_eq(v0, v2)); conds.push_back(m.mk_not(m.mk_eq(v0, v2))); conds.push_back(m.mk_eq(v0, bv.mk_numeral(rational(2), 3))); cond1 = m.mk_eq(ex(2,1,v0),bv.mk_numeral(rational(3),2)); conds.push_back(cond1); conds.push_back(m.mk_or(cond1,m.mk_eq(v3,v4))); conds.push_back(m.mk_eq(ex(2,1,v3),ex(1,0,v4))); conds.push_back(m.mk_or(cond1,m.mk_eq(ex(2,1,v3),ex(1,0,v4)))); conds.push_back(m.mk_or(m.mk_eq(v0,v2),m.mk_eq(v0,v4))); conds.push_back(m.mk_or(m.mk_eq(v0,v2),m.mk_eq(v3,v4))); conds.push_back(m.mk_or(m.mk_eq(ex(2,1,v0),ex(1,0,v2)),m.mk_eq(v3,v4))); conds.push_back(m.mk_or(m.mk_eq(ex(2,1,v0),bv.mk_numeral(rational(3),2)), m.mk_eq(v3,v4))); conds.push_back(m.mk_or(m.mk_eq(ex(2,1,v0),bv.mk_numeral(rational(3),2)), m.mk_eq(v3,bv.mk_numeral(rational(3),3)))); conds.push_back(m.mk_or(m.mk_eq(v0,bv.mk_numeral(rational(5),3)), m.mk_eq(v3,bv.mk_numeral(rational(5),3)))); conds.push_back(m.mk_or(m.mk_eq(v0,bv.mk_numeral(rational(7),3)), m.mk_eq(v3,bv.mk_numeral(rational(7),3)))); conds.push_back(m.mk_not(m.mk_or(m.mk_eq(v0,v2),m.mk_eq(v3,v4)))); // filter_interpreted { std::cout << "filter interpreted\n"; t1 = mk_full(sig2); for (unsigned i = 0; i < conds.size(); ++i) { apply_filter(*t1, conds[i].get()); } t1->deallocate(); } // filter_interpreted_project { unsigned_vector remove; remove.push_back(0); remove.push_back(2); t1 = mk_full(sig2); apply_filter(*t1, conds[2].get()); apply_filter_project(*t1, remove, conds[2].get()); apply_filter_project(*t1, remove, conds[3].get()); t1->deallocate(); t1 = mk_full(sig2); apply_filter(*t1, conds[3].get()); apply_filter_project(*t1, remove, conds[2].get()); apply_filter_project(*t1, remove, conds[3].get()); t1->deallocate(); for (unsigned i = 0; i < conds.size(); ++i) { t1 = mk_full(sig2); apply_filter_project(*t1, remove, conds[i].get()); t1->deallocate(); } remove[1] = 1; for (unsigned i = 0; i < conds.size(); ++i) { t1 = mk_full(sig2); apply_filter_project(*t1, remove, conds[i].get()); t1->deallocate(); } } } // {11xx \ {111x}, x011 \ {x011}, 0111} // {xx11 \ {0011, 1111, x111}} // 0111 // {1x1x \ {1x1x}, 1111 \ {1111}, x1x1 \ {x1x1}} void test_join_project() { datalog::relation_signature sig; sig.push_back(bv.mk_sort(2)); sig.push_back(bv.mk_sort(2)); //sig.push_back(bv.mk_sort(3)); unsigned_vector jc1, jc2, pc; jc1.push_back(0); jc2.push_back(0); pc.push_back(1); pc.push_back(3); //pc.push_back(4); udoc_relation* t1, *t2; relation_base* t; scoped_ptr join_project_fn; for (unsigned i = 0; i < 200; ++i) { t1 = mk_rand(sig); t2 = mk_rand(sig); t1->display(std::cout); t2->display(std::cout); join_project_fn = p.mk_join_project_fn(*t1, *t2, jc1.size(), jc1.c_ptr(), jc2.c_ptr(), pc.size(), pc.c_ptr()); t = (*join_project_fn)(*t1, *t2); t->display(std::cout); cr.verify_join_project(*t1, *t2, *t, jc1, jc2, pc); t->deallocate(); t1->deallocate(); t2->deallocate(); } } void test_join_project2() { relation_signature sig3; sig3.push_back(bv.mk_sort(1)); sig3.push_back(bv.mk_sort(1)); sig3.push_back(bv.mk_sort(1)); /// xxx \ x11 udoc_relation *t1 = mk_full(sig3); { udoc_relation *neg = mk_full(sig3); doc& n = neg->get_udoc()[0]; neg->get_dm().set(n, 1, BIT_1); neg->get_dm().set(n, 2, BIT_1); unsigned_vector allcols; allcols.push_back(0); allcols.push_back(1); allcols.push_back(2); apply_filter_neg(*t1, *neg, allcols, allcols); neg->deallocate(); } // 11x udoc_relation *t2 = mk_full(sig3); { doc& n = t2->get_udoc()[0]; t2->get_dm().set(n, 0, BIT_1); t2->get_dm().set(n, 1, BIT_1); } unsigned_vector jc1, jc2, pc; jc1.push_back(1); jc1.push_back(2); jc2.push_back(0); jc2.push_back(1); pc.push_back(1); pc.push_back(2); scoped_ptr join_project_fn; join_project_fn = p.mk_join_project_fn(*t1, *t2, jc1.size(), jc1.c_ptr(), jc2.c_ptr(), pc.size(), pc.c_ptr()); relation_base *t = (*join_project_fn)(*t1, *t2); cr.verify_join_project(*t1, *t2, *t, jc1, jc2, pc); t->deallocate(); t1->deallocate(); t2->deallocate(); } void test_join_project3() { datalog::relation_signature sig; sig.push_back(bv.mk_sort(2)); sig.push_back(bv.mk_sort(2)); unsigned_vector jc1, jc2, pc; jc1.push_back(0); jc1.push_back(1); jc2.push_back(1); jc2.push_back(0); pc.push_back(0); pc.push_back(1); scoped_ptr join_project_fn; udoc_relation* t1 = mk_empty(sig); { datalog::relation_fact fact1(m); fact1.push_back(bv.mk_numeral(rational(3), 2)); fact1.push_back(bv.mk_numeral(rational(1), 2)); t1->add_fact(fact1); } udoc_relation *t2 = mk_empty(sig); { datalog::relation_fact fact1(m); fact1.push_back(bv.mk_numeral(rational(0), 2)); fact1.push_back(bv.mk_numeral(rational(3), 2)); t2->add_fact(fact1); fact1.reset(); fact1.push_back(bv.mk_numeral(rational(1), 2)); fact1.push_back(bv.mk_numeral(rational(3), 2)); t2->add_fact(fact1); } t1->display(std::cout << "t1:"); t2->display(std::cout << "t2:"); join_project_fn = p.mk_join_project_fn(*t1, *t2, jc1.size(), jc1.c_ptr(), jc2.c_ptr(), pc.size(), pc.c_ptr()); relation_base* t; t = (*join_project_fn)(*t1, *t2); t->display(std::cout << "t:"); cr.verify_join_project(*t1, *t2, *t, jc1, jc2, pc); t->deallocate(); t1->deallocate(); t2->deallocate(); } void test_rename() { udoc_relation* t1; // rename datalog::relation_signature sig; sig.push_back(bv.mk_sort(12)); sig.push_back(bv.mk_sort(6)); sig.push_back(bv.mk_sort(2)); datalog::relation_fact fact1(m); fact1.push_back(bv.mk_numeral(rational(1), 12)); fact1.push_back(bv.mk_numeral(rational(6), 6)); fact1.push_back(bv.mk_numeral(rational(3), 2)); t1 = mk_empty(sig); t1->add_fact(fact1); unsigned_vector cycle; cycle.push_back(0); cycle.push_back(2); check_permutation(t1, cycle); sig.reset(); sig.push_back(bv.mk_sort(2)); sig.push_back(bv.mk_sort(6)); sig.push_back(bv.mk_sort(12)); fact1.reset(); fact1.push_back(bv.mk_numeral(rational(3), 2)); fact1.push_back(bv.mk_numeral(rational(6), 6)); fact1.push_back(bv.mk_numeral(rational(1), 12)); t1 = mk_empty(sig); t1->add_fact(fact1); cycle.reset(); cycle.push_back(0); cycle.push_back(2); check_permutation(t1, cycle); t1 = mk_empty(sig); t1->add_fact(fact1); cycle.reset(); cycle.push_back(0); cycle.push_back(1); cycle.push_back(2); check_permutation(t1, cycle); } void test_join(unsigned num_rounds) { for (unsigned i = 0; i < num_rounds; ++i) { test_join(); } } void test_join() { relation_signature sig; sig.push_back(bv.mk_sort(2)); sig.push_back(bv.mk_sort(3)); udoc_relation* t1, *t2; relation_base* t; t1 = mk_rand(sig); t2 = mk_rand(sig); unsigned_vector jc1, jc2; jc1.push_back(0); jc2.push_back(0); scoped_ptr join_fn; join_fn = p.mk_join_fn(*t1, *t2, jc1.size(), jc1.c_ptr(), jc2.c_ptr()); t = (*join_fn)(*t1, *t2); cr.verify_join(*t1, *t2, *t, jc1, jc2); t1->display(std::cout); t2->display(std::cout); t->display(std::cout); std::cout << "\n"; t1->deallocate(); t2->deallocate(); t->deallocate(); } udoc_relation* mk_rand(relation_signature const& sig) { udoc_relation* t = mk_empty(sig); mk_rand_udoc(t->get_dm(), 3, 3, t->get_udoc()); return t; } void check_permutation(relation_base* t1, unsigned_vector const& cycle) { scoped_ptr rename; rename = p.mk_rename_fn(*t1, cycle.size(), cycle.c_ptr()); relation_base* t = (*rename)(*t1); cr.verify_permutation(*t1,*t, cycle); t1->display(std::cout); std::cout << "\n"; t->display(std::cout); std::cout << "\n"; t->deallocate(); t1->deallocate(); } /* The filter_by_negation postcondition: filter_by_negation(tgt, neg, columns in tgt: c1,...,cN, corresponding columns in neg: d1,...,dN): tgt_1:={x: x\in tgt_0 && ! \exists y: ( y \in neg & pi_c1(x)= pi_d1(y) & ... & pi_cN(x)= pi_dN(y) ) } */ void test_filter_neg() { // filter_by_negation relation_signature sig4; sig4.push_back(bv.mk_sort(1)); sig4.push_back(bv.mk_sort(1)); sig4.push_back(bv.mk_sort(1)); udoc_relation* t1 = mk_empty(sig4); udoc_relation* t2 = mk_empty(sig4); unsigned_vector cols1, cols2; unsigned num_bits = t1->get_dm().num_tbits(); cols1.push_back(0); cols2.push_back(1); for (unsigned i = 0; i < 100; ++i) { set_random(*t1, 2*num_bits/3); set_random(*t2, 2*num_bits/3); apply_filter_neg(*t1,*t2, cols1, cols2); } cols1.push_back(1); cols2.push_back(2); for (unsigned i = 0; i < 200; ++i) { set_random(*t1, 2*num_bits/3); set_random(*t2, 2*num_bits/3); apply_filter_neg(*t1,*t2, cols1, cols2); } t1->deallocate(); t2->deallocate(); } void test_filter_neg2() { // filter_by_negation relation_signature sig4; sig4.push_back(bv.mk_sort(1)); sig4.push_back(bv.mk_sort(1)); sig4.push_back(bv.mk_sort(1)); unsigned_vector cols, allcols; cols.push_back(0); cols.push_back(2); allcols.push_back(0); allcols.push_back(1); allcols.push_back(2); /// xxx \ 1x0 udoc_relation* t1 = mk_full(sig4); { udoc_relation* neg = mk_full(sig4); doc& n = neg->get_udoc()[0]; neg->get_dm().set(n, 0, BIT_1); neg->get_dm().set(n, 2, BIT_0); apply_filter_neg(*t1, *neg, allcols, allcols); neg->deallocate(); } /// xxx \ (1x1 u 0x0) udoc_relation* t2 = mk_full(sig4); { udoc_relation* neg = mk_full(sig4); doc& n = neg->get_udoc()[0]; neg->get_dm().set(n, 0, BIT_0); neg->get_dm().set(n, 2, BIT_0); apply_filter_neg(*t2, *neg, allcols, allcols); neg->deallocate(); } { udoc_relation* neg = mk_full(sig4); doc& n = neg->get_udoc()[0]; neg->get_dm().set(n, 0, BIT_1); neg->get_dm().set(n, 2, BIT_1); apply_filter_neg(*t2, *neg, allcols, allcols); neg->deallocate(); } t1->display(std::cout); t2->display(std::cout); apply_filter_neg(*t2, *t1, cols, cols); t1->deallocate(); t2->deallocate(); } void test_filter_neg3() { // filter_by_negation relation_signature sig; sig.push_back(bv.mk_sort(1)); sig.push_back(bv.mk_sort(1)); sig.push_back(bv.mk_sort(1)); unsigned_vector cols1, cols2; cols1.push_back(0); cols1.push_back(0); cols2.push_back(0); cols2.push_back(1); /// 1xx udoc_relation* t1 = mk_full(sig); { doc& d = t1->get_udoc()[0]; t1->get_dm().set(d, 0, BIT_1); } /// 10x udoc_relation* t2 = mk_full(sig); { doc& d = t2->get_udoc()[0]; t1->get_dm().set(d, 0, BIT_1); t1->get_dm().set(d, 1, BIT_0); } apply_filter_neg(*t1, *t2, cols1, cols2); t1->deallocate(); t2->deallocate(); } void test_filter_neg4(bool disable_fast) { relation_signature sig1, sig2; sig1.push_back(bv.mk_sort(2)); sig1.push_back(bv.mk_sort(2)); sig2.push_back(bv.mk_sort(2)); unsigned_vector cols1, cols2; cols1.push_back(0); cols1.push_back(1); cols2.push_back(0); cols2.push_back(0); udoc_relation* tgt = mk_full(sig1); udoc_relation* neg = mk_full(sig2); if (disable_fast) p.disable_fast_pass(); apply_filter_neg(*tgt, *neg, cols1, cols2); tgt->deallocate(); tgt = mk_full(sig1); apply_filter_neg(*neg, *tgt, cols2, cols1); tgt->deallocate(); neg->deallocate(); } void test_filter_neg5(bool disable_fast) { relation_signature sig1, sig2; sig1.push_back(bv.mk_sort(2)); sig1.push_back(bv.mk_sort(2)); sig2.push_back(bv.mk_sort(2)); sig2.push_back(bv.mk_sort(2)); sig2.push_back(bv.mk_sort(2)); unsigned_vector cols1, cols2, cols3; cols1.push_back(0); cols1.push_back(1); cols2.push_back(0); cols2.push_back(2); cols3.push_back(0); cols3.push_back(1); udoc_relation* tgt = mk_full(sig1); udoc_relation* neg = mk_full(sig2); rel_mut filter_id = p.mk_filter_identical_fn(*tgt, cols3.size(), cols3.c_ptr()); (*filter_id)(*tgt); if (disable_fast) p.disable_fast_pass(); apply_filter_neg(*tgt, *neg, cols1, cols2); tgt->deallocate(); neg->deallocate(); } void set_random(udoc_relation& r, unsigned num_vals) { unsigned num_bits = r.get_dm().num_tbits(); udoc_relation* full = mk_full(r.get_signature()); rel_union union_fn = p.mk_union_fn(r, r, nullptr); (*union_fn)(r, *full); doc_manager& dm = r.get_dm(); ENSURE(r.get_udoc().size() == 1); doc& d0 = r.get_udoc()[0]; ENSURE(dm.is_full(d0)); for (unsigned i = 0; i < num_vals; ++i) { unsigned idx = m_rand(num_bits); unsigned val = m_rand(2); tbit b = (val == 0)?BIT_0:BIT_1; dm.set(d0, idx, b); } full->deallocate(); } void apply_filter_neg(udoc_relation& dst, udoc_relation& neg, unsigned_vector const& cols1, unsigned_vector const& cols2) { scoped_ptr negf; negf = p.mk_filter_by_negation_fn(dst, neg, cols1.size(), cols1.c_ptr(), cols2.c_ptr()); expr_ref dst0(m); dst.to_formula(dst0); (*negf)(dst, neg); cr.verify_filter_by_negation(dst0, dst, neg, cols1, cols2); /* tgt_1:={ x: x\in tgt_0 && ! \exists y: ( y \in neg & pi_c1(x)= pi_d1(y) & ... & pi_cN(x)= pi_dN(y) ) } */ } expr_ref ex(unsigned hi, unsigned lo, expr* e) { expr_ref result(m); result = bv.mk_extract(hi, lo, e); return result; } void apply_filter_project(udoc_relation& t, unsigned_vector const& rm, app* cond) { scoped_ptr rt; rt = p.mk_filter_interpreted_and_project_fn(t, cond, rm.size(), rm.c_ptr()); datalog::relation_base* result = (*rt)(t); cr.verify_filter_project(t, *result, cond, rm); result->deallocate(); } void project_var(unsigned i, sort* s, expr_ref& fml) { var_ref v(m); v = m.mk_var(i, s); unsigned num_bits = bv.get_bv_size(s); unsigned p = 1 << num_bits; expr_ref_vector disj(m); expr_ref tmp(m); for (unsigned i = 0; i < p; ++i) { expr_safe_replace repl(m); repl.insert(v, bv.mk_numeral(rational(i), s)); tmp = fml; repl(tmp); disj.push_back(tmp); } fml = mk_or(m, disj.size(), disj.c_ptr()); } void apply_filter(udoc_relation& t, app* cond) { udoc_relation* full = mk_full(t.get_signature()); rel_union union_fn = p.mk_union_fn(t, *full, nullptr); (*union_fn)(t, *full, nullptr); expr_ref fml0(m); t.to_formula(fml0); rel_mut fint = p.mk_filter_interpreted_fn(t, cond); (*fint)(t); t.display(std::cout << "filter: " << mk_pp(cond, m) << " "); std::cout << "\n"; cr.verify_filter(fml0, t, cond); full->deallocate(); } }; void tst_udoc_relation() { udoc_tester tester; try { tester.test1(); } catch (z3_exception& ex) { std::cout << ex.msg() << "\n"; } } z3-z3-4.8.7/src/test/uint_set.cpp000066400000000000000000000074711356505360400165440ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: uint_set.cpp Abstract: Test sets of unsigned integers. Author: Leonardo de Moura (leonardo) 2006-12-07. Revision History: --*/ #include "util/uint_set.h" #include "util/vector.h" static void tst1(unsigned n) { uint_set s1; svector s2(n, false); unsigned size = 0; unsigned num_op = rand()%1000; for (unsigned i = 0; i < num_op; i++) { unsigned op = rand()%3; if (op < 2) { unsigned idx = rand() % n; if (!s2[idx]) { size++; } s2[idx] = true; s1.insert(idx); } else { unsigned idx = rand() % n; if (s2[idx]) { size--; } s2[idx] = false; s1.remove(idx); } ENSURE(s1.num_elems() == size); ENSURE((size == 0) == s1.empty()); for (unsigned idx = 0; idx < n; idx++) { ENSURE(s2[idx] == s1.contains(idx)); } } } static void tst2(unsigned n) { uint_set s; ENSURE(s.empty()); unsigned val = rand()%n; s.insert(val); ENSURE(!s.empty()); ENSURE(s.num_elems() == 1); for (unsigned i = 0; i < 100; i++) { unsigned val2 = rand()%n; if (val != val2) { ENSURE(!s.contains(val2)); } } s.remove(val); ENSURE(s.num_elems() == 0); ENSURE(s.empty()); } static void tst3(unsigned n) { ENSURE(n > 10); uint_set s1; uint_set s2; ENSURE(s1 == s2); s1.insert(3); ENSURE(s1.num_elems() == 1); ENSURE(s2.num_elems() == 0); ENSURE(s1 != s2); s2.insert(5); ENSURE(s2.num_elems() == 1); ENSURE(s1 != s2); ENSURE(!s1.subset_of(s2)); s2 |= s1; ENSURE(s1.subset_of(s2)); ENSURE(s2.num_elems() == 2); ENSURE(s1 != s2); s1 |= s2; ENSURE(s1.subset_of(s2)); ENSURE(s2.subset_of(s1)); ENSURE(s1.num_elems() == 2); ENSURE(s2.num_elems() == 2); ENSURE(s1 == s2); s1.insert(9); ENSURE(s1.num_elems() == 3); ENSURE(s2.num_elems() == 2); s1.insert(9); ENSURE(s1.num_elems() == 3); ENSURE(s2.num_elems() == 2); ENSURE(s2.subset_of(s1)); ENSURE(!s1.subset_of(s2)); ENSURE(s1 != s2); uint_set s3(s1); ENSURE(s1 == s3); ENSURE(s1.subset_of(s3)); ENSURE(s3.subset_of(s1)); ENSURE(s2 != s3); uint_set s4(s2); ENSURE(s2 == s4); ENSURE(s2.subset_of(s4)); ENSURE(s4.subset_of(s2)); ENSURE(s2 != s3); for (unsigned i = 0; i < n; i++) { uint_set s5; s5.insert(i); ENSURE(s1.contains(i) == s5.subset_of(s1)); } } static void tst4() { uint_set s; s.insert(32); ENSURE(s.contains(32)); ENSURE(!s.contains(31)); ENSURE(!s.contains(0)); s.remove(32); ENSURE(!s.contains(32)); ENSURE(!s.contains(31)); ENSURE(!s.contains(0)); } #include "util/map.h" template struct uint_map : public map {}; static void tst5() { uint_set s; std::cout << s.get_hash() << "\n"; s.insert(1); std::cout << s.get_hash() << "\n"; s.insert(2); std::cout << s.get_hash() << "\n"; s.insert(44); std::cout << s.get_hash() << "\n"; uint_map m; m.insert(s, 1); s.insert(4); m.insert(s, 3); uint_map::iterator it = m.begin(), end = m.end(); for (; it != end; ++it) { std::cout << it->m_key << " : " << it->m_value << "\n"; } } void tst_uint_set() { for (unsigned i = 0; i < 100; i++) { tst1(1 + rand()%31); tst1(1 + rand()%100); } for (unsigned i = 0; i < 1000; i++) { tst2(1); tst2(10); tst2(31); tst2(32); tst2(100); } tst3(12); tst3(100); tst4(); tst5(); } z3-z3-4.8.7/src/test/upolynomial.cpp000066400000000000000000001242761356505360400172650ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: upolynomial.cpp Abstract: Goodies for creating and handling univariate polynomials. Author: Leonardo (leonardo) 2011-12-01 Notes: --*/ #include "math/polynomial/upolynomial.h" #include "util/timeit.h" #include "util/rlimit.h" static void tst1() { reslimit rl; polynomial::numeral_manager nm; polynomial::manager m(rl, nm); upolynomial::manager um(rl, nm); polynomial_ref x(m); x = m.mk_polynomial(m.mk_var()); // create univariate polynomial using multivariate polynomial package polynomial_ref p(m); p = (x - 1)*(x + 1)*(x + 2)*(x + 3)*(x - 3); std::cout << "p: " << p << "\n"; // convert p into an univariate polynomial upolynomial::scoped_numeral_vector q(um); um.to_numeral_vector(p, q); std::cout << "q: "; um.display(std::cout, q); std::cout << "\n"; std::cout << "degree(q): " << um.degree(q) << "\n"; // display coefficients of q std::cout << "expanded q: "; for (unsigned i = 0; i < q.size(); i++) std::cout << nm.to_string(q[i]) << " "; std::cout << "\n"; // traverse coefficients of q adding 1 for (unsigned i = 0; i < q.size(); i++) { nm.add(q[i], mpz(1), q[i]); } // All operations in upolynomial::manager assume the leading coefficient of q is not zero. // So, if we perform destructive operations on these coefficients, we must execute the "trim" operation // before invoking another operation of upolynomial::manager um.trim(q); // q after adding 1 to all coefficients std::cout << "new q: "; um.display(std::cout, q); std::cout << "\n"; // q^2 um.mul(q.size(), q.c_ptr(), q.size(), q.c_ptr(), q); std::cout << "new q^2: "; um.display(std::cout, q); std::cout << "\n"; // using pw for (q^2)^3 um.pw(q.size(), q.c_ptr(), 3, q); std::cout << "new (q^2)^3: "; um.display(std::cout, q); std::cout << "\n"; } static void tst_isolate_roots(polynomial_ref const & p, unsigned prec, mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers) { reslimit rl; upolynomial::manager um(rl, p.m().m()); upolynomial::scoped_numeral_vector q(um); um.to_numeral_vector(p, q); std::cout << "isolating roots of: "; um.display(std::cout, q); std::cout << "\n"; { timeit timer(true, "isolate time"); um.isolate_roots(q.size(), q.c_ptr(), bqm, roots, lowers, uppers); } upolynomial::scoped_upolynomial_sequence sseq(um), fseq(um); { timeit timer(true, "sturm time"); um.sturm_seq(q.size(), q.c_ptr(), sseq); // um.display(std::cout, sseq); std::cout << "\n"; } // Fourier sequence is sensitive to repeated roots... we remove them by taking the square free component. upolynomial::scoped_numeral_vector q_sqf(um); { timeit timer(true, "sqf time"); um.square_free(q.size(), q.c_ptr(), q_sqf); std::cout << "square free part: "; um.display(std::cout, q_sqf); std::cout << "\n"; } { timeit timer(true, "fourier time"); um.fourier_seq(q_sqf.size(), q_sqf.c_ptr(), fseq); } // um.display(std::cout, fseq); std::cout << "num. roots: " << roots.size() + lowers.size() << "\n"; std::cout << "sign var(-oo): " << um.sign_variations_at_minus_inf(sseq) << "\n"; std::cout << "sign var(+oo): " << um.sign_variations_at_plus_inf(sseq) << "\n"; ENSURE(roots.size() + lowers.size() == um.sign_variations_at_minus_inf(sseq) - um.sign_variations_at_plus_inf(sseq)); std::cout << "roots:"; for (unsigned i = 0; i < roots.size(); i++) { ENSURE(um.eval_sign_at(q.size(), q.c_ptr(), roots[i]) == 0); std::cout << " "; bqm.display_decimal(std::cout, roots[i], prec); } { timeit timer(true, "interval check"); std::cout << "\n"; std::cout << "intervals:"; for (unsigned i = 0; i < lowers.size(); i++) { std::cout << " ("; bqm.display_decimal(std::cout, lowers[i], prec); std::cout << ", "; bqm.display_decimal(std::cout, uppers[i], prec); std::cout << ")"; // Check interval with Sturm sequence. Small detail: Sturm sequence is for close intervals. ENSURE(um.eval_sign_at(q.size(), q.c_ptr(), lowers[i]) == 0 || um.eval_sign_at(q.size(), q.c_ptr(), uppers[i]) == 0 || um.sign_variations_at(sseq, lowers[i]) - um.sign_variations_at(sseq, uppers[i]) == 1); // Fourier sequence may also be used to check if the interval is isolating TRACE("upolynomial", tout << "lowers[i]: " << bqm.to_string(lowers[i]) << "\n"; tout << "uppers[i]: " << bqm.to_string(uppers[i]) << "\n"; tout << "fourier lower: " << um.sign_variations_at(fseq, lowers[i]) << "\n"; tout << "fourier upper: " << um.sign_variations_at(fseq, uppers[i]) << "\n";); unsigned fsv_lower = um.sign_variations_at(fseq, lowers[i]); unsigned fsv_upper = um.sign_variations_at(fseq, uppers[i]); VERIFY(um.eval_sign_at(q.size(), q.c_ptr(), lowers[i]) == 0 || um.eval_sign_at(q.size(), q.c_ptr(), uppers[i]) == 0 || // fsv_lower - fsv_upper is an upper bound for the number of roots in the interval // fsv_upper - fsv_upper - num_roots is even // Recall that num_roots == 1 in the interval. (fsv_lower - fsv_upper >= 1 && (fsv_lower - fsv_upper - 1) % 2 == 0)); // Double checking using Descartes bounds for the interval // Must use square free component. unsigned dab = um.descartes_bound_a_b(q_sqf.size(), q_sqf.c_ptr(), bqm, lowers[i], uppers[i]); TRACE("upolynomial", tout << "Descartes bound: " << dab << "\n";); VERIFY(dab == 1); } } std::cout << "\n"; } static void tst_isolate_roots(polynomial_ref const & p, unsigned prec = 5) { mpbq_manager bqm(p.m().m()); scoped_mpbq_vector roots(bqm); scoped_mpbq_vector lowers(bqm); scoped_mpbq_vector uppers(bqm); tst_isolate_roots(p, prec, bqm, roots, lowers, uppers); } static void check_roots(mpbq_vector const & roots, mpbq_vector const & lowers, mpbq_vector const & uppers, unsigned expected_sz, rational const * expected_roots) { ENSURE(expected_sz == roots.size() + lowers.size()); svector visited; visited.resize(expected_sz, false); for (unsigned i = 0; i < expected_sz; i++) { rational const & r = expected_roots[i]; bool found = false; for (unsigned j = 0; j < roots.size(); j++) { if (to_rational(roots[j]) == r) { ENSURE(!visited[j]); VERIFY(!found); found = true; visited[j] = true; } } for (unsigned j = 0; j < lowers.size(); j++) { unsigned j_prime = j + roots.size(); if (to_rational(lowers[j]) < r && r < to_rational(uppers[j])) { VERIFY(!found); ENSURE(!visited[j_prime]); found = true; visited[j_prime] = true; } } ENSURE(found); } } static void tst_isolate_roots(polynomial_ref const & p, unsigned expected_sz, rational const * expected_roots, unsigned prec = 5) { mpbq_manager bqm(p.m().m()); scoped_mpbq_vector roots(bqm); scoped_mpbq_vector lowers(bqm); scoped_mpbq_vector uppers(bqm); tst_isolate_roots(p, prec, bqm, roots, lowers, uppers); check_roots(roots, lowers, uppers, expected_sz, expected_roots); } static void tst_isolate_roots() { reslimit rl; polynomial::numeral_manager nm; polynomial::manager m(rl, nm); polynomial_ref x(m); x = m.mk_polynomial(m.mk_var()); // create univariate polynomial using multivariate polynomial package polynomial_ref p(m); p = (x-1)*(x-2); { rational ex[2] = { rational(1), rational(2) }; tst_isolate_roots(p, 2, ex); } p = (x-1)*(x-1)*x*x*x; { rational ex[2] = { rational(1), rational(0) }; tst_isolate_roots(p, 2, ex); } p = (x^5) - x - 1; { rational ex[1] = { rational(11673039, 10000000) }; // approximated root tst_isolate_roots(p, 1, ex); } p = (x - 1)*(x + 1)*(x + 2)*(x + 3)*((x - 3)^2); { rational ex[5] = { rational(1), rational(-1), rational(-2), rational(-3), rational(3) }; tst_isolate_roots(p, 5, ex); } p = (10000*x - 31)*(10000*x - 32); { rational ex[2] = { rational(31, 10000), rational(32, 10000) }; tst_isolate_roots(p, 2, ex, 10); } p = (10000*x - 31)*(10000*x - 32)*(10000*x - 33); { rational ex[3] = { rational(31, 10000), rational(32, 10000), rational(33, 10000) }; tst_isolate_roots(p, 3, ex, 10); } p = ((x^5) - x - 1)*((x^5) + x - 1)*(1000*x - 1167); { rational ex[3] = { rational(11673039, 10000000), // approximated rational(75487766, 100000000), // approximated rational(1167, 1000) }; tst_isolate_roots(p, 3, ex, 10); } p = (x - 2)*(x - 4)*(x - 8)*(x - 16)*(x - 32)*(x - 64)*(2*x - 1)*(4*x - 1)*(8*x - 1)*(16*x - 1)*(32*x - 1); { rational ex[11] = { rational(2), rational(4), rational(8), rational(16), rational(32), rational(64), rational(1, 2), rational(1, 4), rational(1, 8), rational(1, 16), rational(1, 32) }; tst_isolate_roots(p, 11, ex, 10); } p = ((x^5) - x - 1)*((x^5) + x - 1)*(1000*x - 1167)*((x^5) - x - 1)*((x^5) + x - 1)*(1000*x - 1167); { rational ex[3] = { rational(11673039, 10000000), // approximated rational(75487766, 100000000), // approximated rational(1167, 1000) }; tst_isolate_roots(p, 3, ex, 10); } p = (x^17) + 5*(x^16) + 3*(x^15) + 10*(x^13) + 13*(x^10) + (x^9) + 8*(x^5) + 3*(x^2) + 7; { rational ex[3] = { rational(-413582, 100000), // approximated rational(-170309, 100000), // approximated rational(-109968, 100000), // approximated }; tst_isolate_roots(p, 3, ex, 10); } p = ((x^17) + 5*(x^16) + 3*(x^15) + 10*(x^13) + 13*(x^10) + (x^9) + 8*(x^5) + 3*(x^2) + 7)*(((x^5) - x - 1)^2)*(((x^3) - 2)^2); { rational ex[5] = { rational(-413582, 100000), // approximated rational(-170309, 100000), // approximated rational(-109968, 100000), // approximated rational(11673039, 10000000), // approximated rational(125992, 100000) // approximated }; tst_isolate_roots(p, 5, ex, 10); } p = (((x^5) - 1000000000)^3)*((3*x - 10000000)^2)*((10*x - 632)^2); { rational ex[3] = { rational(630957, 10000), // approximated rational(10000000, 3), rational(632, 10) }; tst_isolate_roots(p, 3, ex, 10); } } static void tst_remove_one_half() { reslimit rl; polynomial::numeral_manager nm; polynomial::manager m(rl, nm); polynomial_ref x(m); x = m.mk_polynomial(m.mk_var()); // create univariate polynomial using multivariate polynomial package polynomial_ref p(m), r(m); p = 4*(x^3) - 12*(x^2) - x + 3; r = 16*(x^2) - 40*x - 24; upolynomial::manager um(rl, nm); upolynomial::scoped_numeral_vector _p(um), _q(um), _r(um); um.to_numeral_vector(p, _p); um.to_numeral_vector(r, _r); ENSURE(um.has_one_half_root(_p.size(), _p.c_ptr())); um.remove_one_half_root(_p.size(), _p.c_ptr(), _q); ENSURE(!um.has_one_half_root(_q.size(), _q.c_ptr())); std::cout << "_p: "; um.display(std::cout, _p); std::cout << "\n"; std::cout << "_r: "; um.display(std::cout, _r); std::cout << "\n"; std::cout << "_q: "; um.display(std::cout, _q); std::cout << "\n"; ENSURE(um.eq(_q, _r)); p = (((x^5) - 1000000000)^3)*((3*x - 10000000)^2)*((10*x - 632)^2); um.to_numeral_vector(p, _p); ENSURE(!um.has_one_half_root(_p.size(), _p.c_ptr())); p = (x - 2)*(x - 4)*(x - 8)*(x - 16)*(x - 32)*(x - 64)*(2*x - 1)*(4*x - 1)*(8*x - 1)*(16*x - 1)*(32*x - 1); um.to_numeral_vector(p, _p); ENSURE(um.has_one_half_root(_p.size(), _p.c_ptr())); } template static void tst_gcd(polynomial_ref const & p, polynomial_ref const & q, pmanager & um) { typename pmanager::scoped_numeral_vector _p(um.m()), _q(um.m()), _r(um.m()); um.to_numeral_vector(p, _p); um.to_numeral_vector(q, _q); std::cout << "_p: "; um.display(std::cout, _p); std::cout << "\n"; std::cout << "_q: "; um.display(std::cout, _q); std::cout << std::endl; um.gcd(_p.size(), _p.c_ptr(), _q.size(), _q.c_ptr(), _r); std::cout << "gcd: "; um.display(std::cout, _r); std::cout << "\n"; um.subresultant_gcd(_p.size(), _p.c_ptr(), _q.size(), _q.c_ptr(), _r); std::cout << "_p: "; um.display(std::cout, _p); std::cout << "\n"; std::cout << "_q: "; um.display(std::cout, _q); std::cout << "\n"; std::cout << "subresultant_gcd: "; um.display(std::cout, _r); std::cout << "\n"; } static void tst_gcd() { std::cout << "\n\nTesting GCD\n"; reslimit rl; polynomial::numeral_manager nm; polynomial::manager m(rl, nm); polynomial_ref x(m); x = m.mk_polynomial(m.mk_var()); // create univariate polynomial using multivariate polynomial package polynomial_ref p(m); polynomial_ref q(m); upolynomial::manager um(rl, nm); p = 13*((x - 3)^6)*((x - 5)^5)*((x - 11)^7); q = derivative(p, 0); tst_gcd(p, q, um); return; p = (x^8) + (x^6) - 3*(x^4) - 3*(x^3) + 8*(x^2) + 2*x - 5; q = 3*(x^6) + 5*(x^4) - 4*(x^2) - 9*x + 21; tst_gcd(p, q, um); p = ((x - 1)^2)*(x - 3)*(x + 2)*((x - 5)^3); q = (x + 1)*(x-1)*((x-3)^2)*(x + 3)*(x - 5); tst_gcd(p, q, um); std::cout << "expected: " << ((x - 1)*(x-3)*(x-5)) << "\n"; } static void tst_zp() { std::cout << "\n\nTesting Z_p\n"; reslimit rl; polynomial::numeral_manager nm; polynomial::manager m(rl, nm); polynomial_ref x(m); x = m.mk_polynomial(m.mk_var()); // create univariate polynomial using multivariate polynomial package polynomial_ref p(m); polynomial_ref q(m); p = (x^4) + 2*(x^3) + 2*(x^2) + x; q = (x^3) + x + 1; // Computing GCD of p an q in Z[x] std::cout << "GCD in Z[x]\n"; upolynomial::manager um(rl, nm); tst_gcd(p, q, um); // Computing GCD of p an q in Z_3[x] std::cout << "GCD in Z_3[x]\n"; upolynomial::zp_manager um3(rl, nm); um3.set_zp(3); tst_gcd(p, q, um3); } static void tst_zp2() { std::cout << "\n\nTesting Z_p\n"; reslimit rl; polynomial::numeral_manager nm; polynomial::manager m(rl, nm); polynomial_ref x(m); x = m.mk_polynomial(m.mk_var()); // create univariate polynomial using multivariate polynomial package polynomial_ref u(m); polynomial_ref v(m); v = (x^6) + 5*(x^5) + 9*(x^4) + 5*(x^2) + 5*x; u = (x^8) + (x^6) + 10*(x^4) + 10*(x^3) + 8*(x^2) + 2*x + 8; // Computing GCD of p an q in Z[x] std::cout << "GCD in Z[x]\n"; upolynomial::manager um(rl, nm); tst_gcd(u, v, um); // Computing GCD of p an q in Z_3[x] std::cout << "GCD in Z_13[x]\n"; upolynomial::zp_manager um13(rl, nm); um13.set_zp(13); tst_gcd(u, v, um13); } static void tst_ext_gcd() { std::cout << "\nExtended GCD\n"; reslimit rl; polynomial::numeral_manager nm; polynomial::manager m(rl, nm); polynomial_ref x(m); x = m.mk_polynomial(m.mk_var()); // create univariate polynomial using multivariate polynomial package polynomial_ref a(m); polynomial_ref b(m); a = (x^6) + 5*(x^5) + 9*(x^4) + 5*(x^2) + 5*x; b = (x^8) + (x^6) + 10*(x^4) + 10*(x^3) + 8*(x^2) + 2*x + 8; // Computing GCD of p an q in Z_3[x] std::cout << "GCD in Z_13[x]\n"; upolynomial::zp_manager um(rl, nm); um.set_zp(13); mpzzp_manager & z13 = um.m(); upolynomial::zp_manager::scoped_numeral_vector A(z13), B(z13), U(z13), V(z13), D(z13); um.to_numeral_vector(a, A); um.to_numeral_vector(b, B); um.ext_gcd(A.size(), A.c_ptr(), B.size(), B.c_ptr(), U, V, D); std::cout << "A: "; um.display(std::cout, A); std::cout << "\n"; std::cout << "B: "; um.display(std::cout, B); std::cout << "\n"; std::cout << "U: "; um.display(std::cout, U); std::cout << "\n"; std::cout << "V: "; um.display(std::cout, V); std::cout << "\n"; std::cout << "D: "; um.display(std::cout, D); std::cout << "\n"; } static void tst_ext_gcd_z7() { std::cout << "\nExtended GCD in Z_7\n"; reslimit rl; polynomial::numeral_manager nm; polynomial::manager m(rl, nm); polynomial_ref x(m); x = m.mk_polynomial(m.mk_var()); // create univariate polynomial using multivariate polynomial package polynomial_ref a(m); polynomial_ref b(m); a = (x^3) + 2; b = 6*(x^2) + 6; // Computing GCD of a and b in Z_3[x] // expecting: D = 1, U = 3*x + 6, V = 3*x^2 + 6*x + 4 std::cout << "GCD in Z_7[x]\n"; upolynomial::zp_manager um(rl, nm); um.set_zp(7); mpzzp_manager & z7 = um.m(); upolynomial::zp_manager::scoped_numeral_vector A(z7), B(z7), U(z7), V(z7), D(z7); um.to_numeral_vector(a, A); um.to_numeral_vector(b, B); um.ext_gcd(A.size(), A.c_ptr(), B.size(), B.c_ptr(), U, V, D); std::cout << "A: "; um.display(std::cout, A); std::cout << "\n"; std::cout << "B: "; um.display(std::cout, B); std::cout << "\n"; std::cout << "U: "; um.display(std::cout, U); std::cout << "\n"; std::cout << "V: "; um.display(std::cout, V); std::cout << "\n"; std::cout << "D: "; um.display(std::cout, D); std::cout << "\n"; } static void tst_sturm() { std::cout << "\nSturm Seq\n"; reslimit rl; polynomial::numeral_manager nm; polynomial::manager m(rl, nm); polynomial_ref x(m); x = m.mk_polynomial(m.mk_var()); // create univariate polynomial using multivariate polynomial package polynomial_ref p(m); p = 7*(x^10) + 3*(x^9) + (x^8) + (x^6) + 10*(x^4) + 10*(x^3) + 8*(x^2) + 2*x + 8; // p = ((x^17) + 5*(x^16) + 3*(x^15) + 10*(x^13) + 13*(x^10) + (x^9) + 8*(x^5) + 3*(x^2) + 7)*(((x^5) - x - 1)^2)*(((x^3) - 2)^2); // p = ((x^17) + 5*(x^16) + 3*(x^15) + 10*(x^13) + 13*(x^10) + (x^9) + 8*(x^5) + 3*(x^2) + 7)*(((x^5) - x - 1))*(((x^3) - 2)); upolynomial::manager um(rl, nm); upolynomial::scoped_numeral_vector _p(um); upolynomial::scoped_upolynomial_sequence seq2(um); um.to_numeral_vector(p, _p); um.sturm_seq(_p.size(), _p.c_ptr(), seq2); std::cout << "upolynomial sturm seq...\n"; um.display(std::cout, seq2); } static void tst_refinable(polynomial_ref const & p, mpbq_manager & bqm, mpbq & a, mpbq & b) { reslimit rl; upolynomial::manager um(rl, p.m().m()); upolynomial::scoped_numeral_vector _p(um); um.to_numeral_vector(p, _p); std::cout << "before (" << bqm.to_string(a) << ", " << bqm.to_string(b) << ")\n"; bool r = um.isolating2refinable(_p.size(), _p.c_ptr(), bqm, a, b); if (r) { std::cout << "new (" << bqm.to_string(a) << ", " << bqm.to_string(b) << ")\n"; int sign_a = um.eval_sign_at(_p.size(), _p.c_ptr(), a); int sign_b = um.eval_sign_at(_p.size(), _p.c_ptr(), b); VERIFY(sign_a != 0 && sign_b != 0 && sign_a == -sign_b); } else { std::cout << "new root: " << bqm.to_string(a) << "\n"; ENSURE(um.eval_sign_at(_p.size(), _p.c_ptr(), a) == 0); } } static void tst_refinable() { std::cout << "\nRefinable intervals\n"; reslimit rl; polynomial::numeral_manager nm; polynomial::manager m(rl, nm); polynomial_ref x(m); x = m.mk_polynomial(m.mk_var()); // create univariate polynomial using multivariate polynomial package polynomial_ref p(m); p = (x - 1)*(4*x - 11)*(x - 3); std::cout << "p: " << p << "\n"; mpbq_manager bqm(nm); mpbq a(1); mpbq b(3); tst_refinable(p, bqm, a, b); bqm.set(a, 2); bqm.set(b, 3); tst_refinable(p, bqm, a, b); bqm.set(a, 5, 1); bqm.set(b, 3); tst_refinable(p, bqm, a, b); p = (x - 1)*(5*x - 11)*(x - 3); std::cout << "p: " << p << "\n"; bqm.set(a, 1); bqm.set(b, 3); tst_refinable(p, bqm, a, b); bqm.set(a, 2); bqm.set(b, 3); tst_refinable(p, bqm, a, b); bqm.set(a, 3, 1); bqm.set(b, 3); tst_refinable(p, bqm, a, b); bqm.set(a, 1); bqm.set(b, 5, 1); tst_refinable(p, bqm, a, b); bqm.set(a, 3, 1); bqm.set(b, 5, 1); tst_refinable(p, bqm, a, b); p = (x - 1)*(x - 2)*(x - 3); std::cout << "p: " << p << "\n"; bqm.set(a, 1); bqm.set(b, 3); tst_refinable(p, bqm, a, b); bqm.del(a); bqm.del(b); } static void tst_refine(polynomial_ref const & p, mpbq_manager & bqm, mpbq & a, mpbq & b, unsigned prec_k=100) { reslimit rl; upolynomial::manager um(rl, p.m().m()); upolynomial::scoped_numeral_vector _p(um); um.to_numeral_vector(p, _p); std::cout << "before (" << bqm.to_string(a) << ", " << bqm.to_string(b) << ")\n"; bool r = um.refine(_p.size(), _p.c_ptr(), bqm, a, b, prec_k); if (r) { std::cout << "new (" << bqm.to_string(a) << ", " << bqm.to_string(b) << ")\n"; std::cout << "as decimal: "; bqm.display_decimal(std::cout, a, prec_k); std::cout << "\n"; } else { std::cout << "new root: " << bqm.to_string(a) << "\n"; std::cout << "as decimal: "; bqm.display_decimal(std::cout, a, prec_k); std::cout << "\n"; } } static void tst_refine() { std::cout << "\nRefining intervals\n"; reslimit rl; polynomial::numeral_manager nm; polynomial::manager m(rl, nm); polynomial_ref x(m); x = m.mk_polynomial(m.mk_var()); // create univariate polynomial using multivariate polynomial package polynomial_ref p(m); p = (x^5) - x - 1; std::cout << "p: " << p << "\n"; mpbq_manager bqm(nm); scoped_mpbq a(bqm), b(bqm); a = 1; b = 2; tst_refine(p, bqm, a, b, 20); p = (x^2) - 2; std::cout << "p: " << p << "\n"; a = 1; b = 2; tst_refine(p, bqm, a, b, 200); } static void tst_translate_q() { reslimit rl; unsynch_mpq_manager nm; polynomial::manager m(rl, nm); polynomial_ref x(m); x = m.mk_polynomial(m.mk_var()); // create univariate polynomial using multivariate polynomial package polynomial_ref p(m); p = (x-1)*(x-2)*(x-3)*(x-4); upolynomial::manager um(rl, nm); upolynomial::scoped_numeral_vector _p(um), _q(um); um.to_numeral_vector(p, _p); ENSURE(um.eval_sign_at(_p.size(), _p.c_ptr(), mpq(1)) == 0); ENSURE(um.eval_sign_at(_p.size(), _p.c_ptr(), mpq(2)) == 0); ENSURE(um.eval_sign_at(_p.size(), _p.c_ptr(), mpq(3)) == 0); ENSURE(um.eval_sign_at(_p.size(), _p.c_ptr(), mpq(4)) == 0); ENSURE(um.eval_sign_at(_p.size(), _p.c_ptr(), mpq(-1)) != 0); ENSURE(um.eval_sign_at(_p.size(), _p.c_ptr(), mpq(5)) != 0); ENSURE(um.eval_sign_at(_p.size(), _p.c_ptr(), mpq(-2)) != 0); scoped_mpq c(nm); nm.set(c, 1, 3); scoped_mpq r1(nm); r1 = 1; r1 -= c; scoped_mpq r2(nm); r2 = 3; r2 -= c; ENSURE(um.eval_sign_at(_p.size(), _p.c_ptr(), r1) != 0); ENSURE(um.eval_sign_at(_p.size(), _p.c_ptr(), r2) != 0); std::cout << "p: "; um.display(std::cout, _p); std::cout << "\n"; um.translate_q(_p.size(), _p.c_ptr(), c, _q); std::cout << "q: "; um.display(std::cout, _q); std::cout << "\n"; ENSURE(um.eval_sign_at(_q.size(), _q.c_ptr(), mpq(1)) != 0); ENSURE(um.eval_sign_at(_q.size(), _q.c_ptr(), mpq(2)) != 0); ENSURE(um.eval_sign_at(_q.size(), _q.c_ptr(), mpq(3)) != 0); ENSURE(um.eval_sign_at(_q.size(), _q.c_ptr(), mpq(4)) != 0); ENSURE(um.eval_sign_at(_q.size(), _q.c_ptr(), mpq(-1)) != 0); ENSURE(um.eval_sign_at(_q.size(), _q.c_ptr(), mpq(5)) != 0); ENSURE(um.eval_sign_at(_q.size(), _q.c_ptr(), mpq(-2)) != 0); ENSURE(um.eval_sign_at(_q.size(), _q.c_ptr(), r1) == 0); ENSURE(um.eval_sign_at(_q.size(), _q.c_ptr(), r2) == 0); um.p_1_div_x(_p.size(), _p.c_ptr()); std::cout << "p: "; um.display(std::cout, _p); std::cout << "\n"; ENSURE(um.eval_sign_at(_p.size(), _p.c_ptr(), mpq(1)) == 0); nm.set(c, 1, 2); ENSURE(um.eval_sign_at(_p.size(), _p.c_ptr(), c) == 0); nm.set(c, 1, 3); ENSURE(um.eval_sign_at(_p.size(), _p.c_ptr(), c) == 0); nm.set(c, 1, 4); ENSURE(um.eval_sign_at(_p.size(), _p.c_ptr(), c) == 0); ENSURE(um.eval_sign_at(_p.size(), _p.c_ptr(), mpq(2)) != 0); ENSURE(um.eval_sign_at(_p.size(), _p.c_ptr(), mpq(3)) != 0); ENSURE(um.eval_sign_at(_p.size(), _p.c_ptr(), mpq(4)) != 0); } static void tst_convert_q2bq(unsynch_mpq_manager & m, polynomial_ref const & p, mpq const & a, mpq const & b) { reslimit rl; upolynomial::manager um(rl, m); upolynomial::scoped_numeral_vector _p(um); um.to_numeral_vector(p, _p); std::cout << "\np: "; um.display(std::cout, _p); std::cout << "\n"; std::cout << "before (" << m.to_string(a) << ", " << m.to_string(b) << ") ("; m.display_decimal(std::cout, a, 10); std::cout << ", "; m.display_decimal(std::cout, b, 10); std::cout << ")\n"; mpbq_manager bqm(m); scoped_mpbq c(bqm); scoped_mpbq d(bqm); if (!um.convert_q2bq_interval(_p.size(), _p.c_ptr(), a, b, bqm, c, d)) { std::cout << "found root: " << c << "\n"; } else { std::cout << "after (" << c << ", " << d << ")" << " ("; bqm.display_decimal(std::cout, c, 10); std::cout << ", "; bqm.display_decimal(std::cout, d, 10); std::cout << ")\n"; } } static void tst_convert_q2bq() { reslimit rl; unsynch_mpq_manager nm; polynomial::manager m(rl, nm); polynomial_ref x(m); x = m.mk_polynomial(m.mk_var()); // create univariate polynomial using multivariate polynomial package polynomial_ref p(m); p = (x-1)*(x-2)*(x-3)*(x-4); scoped_mpq a(nm), b(nm); nm.set(a, 1, 3); nm.set(b, 7, 5); tst_convert_q2bq(nm, p, a, b); nm.set(a, 1, 2); nm.set(b, 7, 5); tst_convert_q2bq(nm, p, a, b); nm.set(a, 3, 7); nm.set(b, 3, 2); tst_convert_q2bq(nm, p, a, b); nm.set(a, 0); nm.set(b, 3, 2); tst_convert_q2bq(nm, p, a, b); nm.set(a, 0); nm.set(b, 23, 21); tst_convert_q2bq(nm, p, a, b); nm.set(a, 7, 2); nm.set(b, 5); tst_convert_q2bq(nm, p, a, b); nm.set(a, 999, 1000); nm.set(b, 1001, 1000); tst_convert_q2bq(nm, p, a, b); nm.set(a, 9999, 10000); nm.set(b, 10001, 10000); tst_convert_q2bq(nm, p, a, b); nm.set(a, 39999, 10000); nm.set(b, 40001, 10000); tst_convert_q2bq(nm, p, a, b); } static void tst_sturm2() { reslimit rl; polynomial::numeral_manager nm; polynomial::manager m(rl, nm); polynomial_ref x(m); x = m.mk_polynomial(m.mk_var()); // create univariate polynomial using multivariate polynomial package polynomial_ref p(m); polynomial_ref q(m); p = (x^16) - 136*(x^14) + 6476*(x^12) - 141912*(x^10) + 1513334*(x^8) - 7453176*(x^6) + 13950764*(x^4) - 5596840*(x^2) + 46225; q = ((x^8) - 40*(x^6) + 352*(x^4) - 960*(x^2) + 576)^2; upolynomial::manager um(rl, nm); upolynomial::scoped_numeral_vector _p(um), _q(um); upolynomial::scoped_upolynomial_sequence seq2(um); um.to_numeral_vector(p, _p); um.to_numeral_vector(q, _q); um.sturm_tarski_seq(_p.size(), _p.c_ptr(), _q.size(), _q.c_ptr(), seq2); std::cout << "upolynomial sturm seq...\n"; um.display(std::cout, seq2); } #if 0 static void tst_isolate_roots2() { polynomial::numeral_manager nm; polynomial::manager m(nm); polynomial_ref x(m); x = m.mk_polynomial(m.mk_var()); // create univariate polynomial using multivariate polynomial package polynomial_ref p(m); p = (2*x - 1)*(x - 21)*(x + 12)*(x - 19)*(x + 11)*(x + 34)*(x - 9)*(x - 72)*(10000*x - 4999)*((x^5) - x - 1)*((x^2) - 2)*((x^2) - 3)*((x^7) - 3)*((x^101) - 3); { tst_isolate_roots(p, 10); } } static void tst_isolate_roots3() { polynomial::numeral_manager nm; polynomial::manager m(nm); polynomial_ref x1(m), x2(m), x3(m), x4(m), x5(m), x6(m), x(m); x = m.mk_polynomial(m.mk_var()); x1 = m.mk_polynomial(m.mk_var()); x2 = m.mk_polynomial(m.mk_var()); x3 = m.mk_polynomial(m.mk_var()); x4 = m.mk_polynomial(m.mk_var()); x5 = m.mk_polynomial(m.mk_var()); x6 = m.mk_polynomial(m.mk_var()); // create univariate polynomial using multivariate polynomial package polynomial_ref p1(m); polynomial_ref p2(m); polynomial_ref p3(m); polynomial_ref p4(m); polynomial_ref p5(m); polynomial_ref p6(m); polynomial_ref q(m); polynomial_ref r(m); p1 = ((x1^2) - 2); p2 = ((x2^2) - 3); p3 = ((x3^2) - 5); p4 = ((x4^2) - 7); p5 = ((x5^2) - 11); p6 = ((x6^2) - 13); q = (x - x1 - x2 - x3 - x4 - x5 - x6); r = resultant(resultant(resultant(resultant(resultant(resultant(q, p1, 1), p2, 2), p3, 3), p4, 4), p5, 5), p6, 6); std::cout << "r: " << r << "\n"; { timeit timer(true, "isolate"); tst_isolate_roots(r, 10); } } static void tst_gcd2() { polynomial::numeral_manager nm; polynomial::manager m(nm); polynomial_ref x(m); x = m.mk_polynomial(m.mk_var()); // create univariate polynomial using multivariate polynomial package polynomial_ref p(m); p = ((x^1000) - x + 1)^5; reslimit rl; upolynomial::manager um(rl, nm); upolynomial::scoped_numeral_vector _p(um); upolynomial::scoped_numeral_vector _p_sqf(um); um.to_numeral_vector(p, _p); { timeit timer(true, "gcd"); um.square_free(_p.size(), _p.c_ptr(), _p_sqf); } um.display(std::cout, _p_sqf.size(), _p_sqf.c_ptr()); std::cout << "\n"; } #endif static void tst_isolate_roots5() { reslimit rl; polynomial::numeral_manager nm; polynomial::manager m(rl, nm); polynomial_ref x(m); x = m.mk_polynomial(m.mk_var()); // create univariate polynomial using multivariate polynomial package polynomial_ref p(m); p = (x^70) - 6*(x^65) - (x^60) + 60*(x^55) - 54*(x^50) - 230*(x^45) + 274*(x^40) + 542*(x^35) - 615*(x^30) - 1120*(x^25) + 1500*(x^20) - 160*(x^15) - 395*(x^10) + 76*(x^5) + 34; { tst_isolate_roots(p, 10); } } static void tst_exact_div(polynomial_ref const & p1, polynomial_ref const & p2, bool expected, polynomial_ref const & expected_q) { reslimit rl; upolynomial::manager um(rl, p1.m().m()); upolynomial::scoped_numeral_vector _p1(um), _p2(um), _q(um), _r(um); um.to_numeral_vector(p1, _p1); um.to_numeral_vector(p2, _p2); if (expected) um.to_numeral_vector(expected_q, _q); std::cout << "------\n"; std::cout << "p1: "; um.display(std::cout, _p1); std::cout << "\n"; std::cout << "p2: "; um.display(std::cout, _p2); std::cout << std::endl; bool res = um.exact_div(_p1.size(), _p1.c_ptr(), _p2.size(), _p2.c_ptr(), _r); if (res) { std::cout << "r: "; um.display(std::cout, _r); std::cout << "\n"; } if (expected) { std::cout << "expected: "; um.display(std::cout, _q); std::cout << "\n"; } std::cout.flush(); ENSURE(res == expected); ENSURE(expected == um.divides(_p1.size(), _p1.c_ptr(), _p2.size(), _p2.c_ptr())); ENSURE(!expected || um.eq(_r, _q)); } static void tst_exact_div() { reslimit rl; polynomial::numeral_manager nm; polynomial::manager m(rl, nm); polynomial_ref x(m); x = m.mk_polynomial(m.mk_var()); // create univariate polynomial using multivariate polynomial package polynomial_ref p(m); tst_exact_div((x - 1)*(x - 2)*(x - 3), (x-2)*(x-1), true, (x-3)); tst_exact_div((x - 1)*(2*x - 4)*(x - 3), (x-2)*(x-1), true, (2*x-6)); tst_exact_div((x - 1)*(2*x - 4)*(x - 3), (x-2)*(x-1)*(x-4), false, x); tst_exact_div((x - 3), (x-1), false, x); polynomial_ref z(m); z = m.mk_const(rational(0)); tst_exact_div(z, (x-2)*(x-1)*(x-4), true, z); tst_exact_div((x-2)*(x-1)*(x-4), z, false, z); tst_exact_div(z, z, false, z); polynomial_ref two(m); two = m.mk_const(rational(2)); tst_exact_div((2*x - 2), (x-1), true, two); tst_exact_div((2*x - 2), (4*x-4), false, two); tst_exact_div((6*x - 4), two, true, (3*x - 2)); } static void tst_fact(polynomial_ref const & p, unsigned num_distinct_factors, upolynomial::factor_params const & params = upolynomial::factor_params()) { ENSURE(is_univariate(p)); std::cout << "---------------\n"; std::cout << "p: " << p << std::endl; reslimit rl; upolynomial::manager um(rl, p.m().m()); upolynomial::scoped_numeral_vector _p(um); upolynomial::factors fs(um); um.to_numeral_vector(p, _p); um.factor(_p, fs, params); std::cout << "factors:\n"; std::cout << um.m().to_string(fs.get_constant()) << "\n"; 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); TRACE("upolynomial", tout << "_r: "; um.display(tout, _r); tout << "\n_p: "; um.display(tout, _p); tout << "\n";); ENSURE(um.eq(_p, _r)); } static void tst_fact() { reslimit rl; polynomial::numeral_manager nm; polynomial::manager m(rl, nm); polynomial_ref x0(m); 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, 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, 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); tst_fact((x0^4) + (x0^2) - 20, 3); tst_fact((-11)*((x0^5) - (x0^2) + 1)*((-1)*x0 + 1)*((x0^2) - 2*x0 + 3), 3); tst_fact(x0 - 2*(x0^2) + 1, 2); tst_fact(13*((x0 - 3)^6)*((x0 - 5)^5)*((x0 - 11)^7), 3); tst_fact((x0+1)^30, 1); 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); tst_fact(((x0^4) - 8*(x0^2)), 2); tst_fact((x0^5) - 2*(x0^3) + x0 - 1, 1); tst_fact( (x0^25) - 4*(x0^21) - 5*(x0^20) + 6*(x0^17) + 11*(x0^16) + 10*(x0^15) - 4*(x0^13) - 7*(x0^12) - 9*(x0^11) - 10*(x0^10) + (x0^9) + (x0^8) + (x0^7) + (x0^6) + 3*(x0^5) + x0 - 1, 2); tst_fact( (x0^25) - 10*(x0^21) - 10*(x0^20) - 95*(x0^17) - 470*(x0^16) - 585*(x0^15) - 40*(x0^13) - 1280*(x0^12) - 4190*(x0^11) - 3830*(x0^10) + 400*(x0^9)+ 1760*(x0^8) + 760*(x0^7) - 2280*(x0^6) + 449*(x0^5) + 640*(x0^3) - 640*(x0^2) + 240*x0 - 32, 2); tst_fact( x0^10, 1); tst_fact( (x0^2) - 1, 2); tst_fact( (-2)*(x0^2) + 2, 2); polynomial_ref zero(m); polynomial_ref three(m); zero = m.mk_zero(); three = m.mk_const(rational(3)); tst_fact(zero, 0); tst_fact(three, 0); tst_fact(x0 + 1, 1); tst_fact(x0 - 1, 1); tst_fact((-1)*x0 - 1, 1); tst_fact((-1)*x0 + 1, 1); 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^50) - 10*(x0^40) + 38*(x0^30) - 2*(x0^25) - 100*(x0^20) - 40*(x0^15) + 121*(x0^10) - 38*(x0^5) - 17, 1); tst_fact( (((x0^5) + 5*(x0^4) + 10*(x0^3) + 10*(x0^2) + 5*x0)^10) + 10*(((x0^5) + 5*(x0^4) + 10*(x0^3) + 10*(x0^2) + 5*x0)^9) + 35*(((x0^5) + 5*(x0^4) + 10*(x0^3) + 10*(x0^2) + 5*x0)^8) + 40*(((x0^5) + 5*(x0^4) + 10*(x0^3) + 10*(x0^2) + 5*x0)^7) - 32*(((x0^5) + 5*(x0^4) + 10*(x0^3) + 10*(x0^2) + 5*x0)^6) - 82*(((x0^5) + 5*(x0^4) + 10*(x0^3) + 10*(x0^2) + 5*x0)^5) - 30*(((x0^5) + 5*(x0^4) + 10*(x0^3) + 10*(x0^2) + 5*x0)^4) - 140*(((x0^5) + 5*(x0^4) + 10*(x0^3) + 10*(x0^2) + 5*x0)^3) - 284*(((x0^5) + 5*(x0^4) + 10*(x0^3) + 10*(x0^2) + 5*x0)^2) - 168*((x0^5) + 5*(x0^4) + 10*(x0^3) + 10*(x0^2) + 5*x0) - 47, 1); tst_fact( (x0^4) - 404*(x0^2) + 39204, 2); tst_fact( ((x0^5) - 15552)* ((x0^20)- 15708*(x0^15) + rational("138771724")*(x0^10)- rational("432104148432")*(x0^5) + rational("614198284585616")), 2); tst_fact( (x0^25) - rational("3125")*(x0^21) - rational("15630")*(x0^20) + rational("3888750")*(x0^17) + rational("38684375")*(x0^16) + rational("95765635")*(x0^15) - rational("2489846500")*(x0^13) - rational("37650481875")*(x0^12) - rational("190548065625")*(x0^11) - rational("323785250010")*(x0^10) + rational("750249453025")*(x0^9) + rational("14962295699875")*(x0^8) + rational("111775113235000")*(x0^7) + rational("370399286731250")*(x0^6) + rational("362903064503129")*(x0^5) - rational("2387239013984400")*(x0^4) - rational("23872390139844000")*(x0^3) - rational("119361950699220000")*(x0^2) - rational("298404876748050000")*x0 - rational("298500366308609376"), 2); tst_fact( rational("54")*(x0^24) - (x0^27) - 324*(x0^21) + rational("17496")*(x0^18) - 34992*(x0^15)+ rational("1889568")*(x0^12)- 1259712*(x0^9) + rational("68024448")*(x0^6), 3); tst_fact( ((x0^3)- 432)*(((x0^3)+54)^2)*((x0^6)+108)*((x0^6)+6912)*((x0^6)- 324*(x0^3)+37044), 5); tst_fact( ((x0^6)- 6*(x0^4) - 864*(x0^3) + 12*(x0^2) - 5184*x0 + 186616)* (((x0^6) - 6*(x0^4) + 108*(x0^3) + 12*(x0^2) + 648*x0 + 2908)^2)* ((x0^12) - 12*(x0^10) + 60*(x0^8) + 56*(x0^6) + 6720*(x0^4) + 12768*(x0^2) + 13456)* ((x0^12) - 12*(x0^10) + 60*(x0^8) + 13664*(x0^6) + 414960*(x0^4) + 829248*(x0^2) + 47886400)* ((x0^12) - 12*(x0^10) - 648*(x0^9)+ 60*(x0^8) + 178904*(x0^6) + 15552*(x0^5) + 1593024*(x0^4) - 24045984*(x0^3) + 5704800*(x0^2) - 143995968*x0 + 1372010896), 5); } static void tst_rem(polynomial_ref const & p, polynomial_ref const & q, polynomial_ref const & expected) { ENSURE(is_univariate(p)); ENSURE(is_univariate(q)); std::cout << "---------------\n"; std::cout << "p: " << p << std::endl; std::cout << "q: " << q << std::endl; reslimit rl; upolynomial::manager um(rl, p.m().m()); upolynomial::scoped_numeral_vector _p(um), _q(um), _r(um); um.to_numeral_vector(p, _p); um.to_numeral_vector(q, _q); um.rem(_p.size(), _p.c_ptr(), _q.size(), _q.c_ptr(), _r); polynomial_ref r(p.m()); r = p.m().to_polynomial(_r.size(), _r.c_ptr(), 0); std::cout << "r: " << r << std::endl; ENSURE(eq(expected, r)); } static void tst_rem() { reslimit rl; polynomial::numeral_manager nm; polynomial::manager m(rl, nm); polynomial_ref x0(m), zero(m), one(m); x0 = m.mk_polynomial(m.mk_var()); zero = m.mk_zero(); one = m.mk_const(rational(1)); tst_rem((x0^2) + x0, x0, zero); tst_rem((x0^2) + x0 + 1, x0, one); tst_rem((x0^2) + 2*x0 + 1, 2*x0 + 2, zero); } static void tst_lower_bound(polynomial_ref const & p) { ENSURE(is_univariate(p)); std::cout << "---------------\n"; std::cout << "p: " << p << std::endl; reslimit rl; upolynomial::manager um(rl, p.m().m()); upolynomial::scoped_numeral_vector _p(um); um.to_numeral_vector(p, _p); std::cout << "_p: "; um.display(std::cout, _p); std::cout << "\n"; unsigned k = um.nonzero_root_lower_bound(_p.size(), _p.c_ptr()); std::cout << "_p: "; um.display(std::cout, _p); std::cout << "\n"; std::cout << "k: " << k << "\n"; } static void tst_lower_bound() { reslimit rl; polynomial::numeral_manager nm; polynomial::manager m(rl, nm); polynomial_ref x(m), zero(m), one(m); x = m.mk_polynomial(m.mk_var()); zero = m.mk_zero(); one = m.mk_const(rational(1)); tst_lower_bound((x^2) - 2); tst_lower_bound((x^5)); tst_lower_bound((x - 1)*(2*x - 1)*(4*x - 1)*(8*x - 1)); tst_lower_bound((x - 1)*(2*x - 1)*(4*x - 1)*(8*x - 1)*(16*x - 1)); tst_lower_bound((x - 1)*(2*x - 1)*(4*x - 1)*(8*x - 1)*(16*x - 1)*(x^3)); tst_lower_bound((x^5) - x - 1); tst_lower_bound((1000*x - 1)*(x - 1)); tst_lower_bound((x + 1)*(2*x - 1)*(4*x + 1)*(8*x - 1)*(16*x + 1)); tst_lower_bound((x + 1)*(2*x + 1)*(4*x + 1)*(8*x + 1)*(16*x + 1)); tst_lower_bound((x^10) - 10*(x^8) + 38*(x^6) - 2*(x^5) - 100*(x^4) - 40*(x^3) + 121*(x^2) - 38*x - 17); tst_lower_bound(((x^17) + 5*(x^16) + 3*(x^15) + 10*(x^13) + 13*(x^10) + (x^9) + 8*(x^5) + 3*(x^2) + 7)*(((x^5) - x - 1)^2)*(((x^3) - 2)^2)); tst_lower_bound((((x^5) - 1000000000)^3)*((3*x - 10000000)^2)*((10*x - 632)^2)); } void tst_upolynomial() { set_verbosity_level(1000); enable_trace("mpz_gcd"); enable_trace("normalize_bug"); enable_trace("factor_bug"); enable_trace("factor"); // enable_trace("mpzp_inv_bug"); // enable_trace("mpz"); tst_gcd(); tst_lower_bound(); tst_fact(); tst_rem(); tst_exact_div(); tst_isolate_roots5(); // tst_gcd2(); // tst_isolate_roots4(); // tst_isolate_roots3(); // tst_isolate_roots2(); // return; tst_isolate_roots(); tst_sturm2(); tst_convert_q2bq(); enable_trace("div_bug"); enable_trace("mpbq_bug"); tst_translate_q(); tst_refine(); tst_refinable(); tst_sturm(); tst_remove_one_half(); tst_isolate_roots(); tst1(); tst_zp(); tst_zp2(); tst_ext_gcd(); tst_ext_gcd_z7(); } z3-z3-4.8.7/src/test/var_subst.cpp000066400000000000000000000055071356505360400167200ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: var_subst.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-06-12. Revision History: --*/ #include "ast/rewriter/var_subst.h" #include "ast/ast_pp.h" #include "ast/arith_decl_plugin.h" #include "ast/bv_decl_plugin.h" #include "ast/array_decl_plugin.h" #include "ast/for_each_expr.h" #include "ast/reg_decl_plugins.h" namespace find_q { struct proc { quantifier * m_q; proc():m_q(nullptr) {} void operator()(var * n) {} void operator()(app * n) {} void operator()(quantifier * n) { m_q = n; } }; }; quantifier * find_quantifier(expr * n) { find_q::proc p; for_each_expr(p, n); return p.m_q; } void tst_instantiate(ast_manager & m, expr * f) { if (is_quantifier(f)) { tst_instantiate(m, to_quantifier(f)->get_expr()); return; } quantifier * q = find_quantifier(f); if (q) { expr_ref_vector cnsts(m); for (unsigned i = 0; i < q->get_num_decls(); i++) cnsts.push_back(m.mk_fresh_const("a", q->get_decl_sort(i))); expr_ref r = instantiate(m, q, cnsts.c_ptr()); TRACE("var_subst", tout << "quantifier:\n" << mk_pp(q, m) << "\nresult:\n" << mk_pp(r, m) << "\n";); } } void tst_subst(ast_manager& m) { func_decl_ref p(m); sort_ref s(m); obj_ref x(m), y(m), z(m), u(m), v(m); expr_ref e1(m), e2(m), e3(m); expr_ref t1(m), t2(m), t3(m); s = m.mk_uninterpreted_sort(symbol("S")); sort* ss[2] = { s.get(), s.get() }; symbol names[2] = { symbol("y"), symbol("x") }; p = m.mk_func_decl(symbol("p"), 2, ss, m.mk_bool_sort()); x = m.mk_var(0, s); y = m.mk_var(1, s); z = m.mk_var(2, s); u = m.mk_var(3, s); v = m.mk_var(4, s); e1 = m.mk_and(m.mk_app(p, x.get(), y.get()), m.mk_app(p, z.get(), u.get())); e2 = m.mk_forall(1, ss, names, e1); t1 = m.mk_forall(1, ss, names, m.mk_and(m.mk_app(p, x.get(), z.get()), m.mk_app(p, y.get(), u.get()))); t2 = m.mk_forall(2, ss, names, m.mk_and(m.mk_app(p, x.get(), y.get()), m.mk_app(p, u.get(), z.get()))); var_subst subst(m); expr_ref_vector sub1(m); sub1.push_back(x); sub1.push_back(y); // replace #1 -> #2, #2 -> #1 e3 = subst(e2, 2, sub1.c_ptr()); std::cout << mk_pp(e2, m) << "\n"; std::cout << mk_pp(e3, m) << "\n"; std::cout << mk_pp(t1, m) << "\n"; ENSURE(e3.get() == t1.get()); // replace #2 -> #3, #3 -> #2 e2 = m.mk_forall(2, ss, names, e1); e3 = subst(e2, 2, sub1.c_ptr()); std::cout << mk_pp(e2, m) << "\n"; std::cout << mk_pp(e3, m) << "\n"; std::cout << mk_pp(t2, m) << "\n"; ENSURE(e3.get() == t2.get()); } void tst_var_subst() { ast_manager m; reg_decl_plugins(m); tst_subst(m); } z3-z3-4.8.7/src/test/vector.cpp000066400000000000000000000024431356505360400162060ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: tst_vector.cpp Abstract: Test my vector template. Author: Leonardo de Moura (leonardo) 2006-09-11. Revision History: --*/ #include "util/vector.h" static void tst1() { svector v1; ENSURE(v1.empty()); for (unsigned i = 0; i < 1000; i++) { v1.push_back(i + 3); ENSURE(static_cast(v1[i]) == i + 3); ENSURE(v1.capacity() >= v1.size()); ENSURE(!v1.empty()); } for (unsigned i = 0; i < 1000; i++) { ENSURE(static_cast(v1[i]) == i + 3); } svector::iterator it = v1.begin(); svector::iterator end = v1.end(); for (int i = 0; it != end; ++it, ++i) { ENSURE(*it == i + 3); } for (unsigned i = 0; i < 1000; i++) { ENSURE(static_cast(v1.back()) == 1000 - i - 1 + 3); ENSURE(v1.size() == 1000 - i); v1.pop_back(); } ENSURE(v1.empty()); ENSURE(v1.empty()); unsigned i = 1000000000; while (true) { std::cout << "resize " << i << "\n"; try { v1.resize(i); } catch (z3_exception& e) { std::cout << e.msg() << "\n"; break; } i *= 2; } } void tst_vector() { tst1(); } z3-z3-4.8.7/src/util/000077500000000000000000000000001356505360400141735ustar00rootroot00000000000000z3-z3-4.8.7/src/util/CMakeLists.txt000066400000000000000000000025201356505360400167320ustar00rootroot00000000000000if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/z3_version.h") message(FATAL_ERROR "\"${CMAKE_CURRENT_SOURCE_DIR}/z3_version.h\"" ${z3_polluted_tree_msg} ) endif() set(Z3_FULL_VERSION "\"${Z3_FULL_VERSION_STR}\"") configure_file(z3_version.h.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/z3_version.h) z3_add_component(util SOURCES approx_nat.cpp approx_set.cpp bit_util.cpp bit_vector.cpp cmd_context_types.cpp common_msgs.cpp debug.cpp env_params.cpp fixed_bit_vector.cpp gparams.cpp hash.cpp hwf.cpp inf_int_rational.cpp inf_rational.cpp inf_s_integer.cpp lbool.cpp luby.cpp memory_manager.cpp min_cut.cpp mpbq.cpp mpf.cpp mpff.cpp mpfx.cpp mpn.cpp mpq.cpp mpq_inf.cpp mpz.cpp page.cpp params.cpp permutation.cpp prime_generator.cpp rational.cpp region.cpp rlimit.cpp scoped_ctrl_c.cpp scoped_timer.cpp sexpr.cpp s_integer.cpp small_object_allocator.cpp smt2_util.cpp stack.cpp statistics.cpp symbol.cpp timeit.cpp timeout.cpp trace.cpp util.cpp warning.cpp z3_exception.cpp EXTRA_REGISTER_MODULE_HEADERS env_params.h MEMORY_INIT_FINALIZER_HEADERS debug.h gparams.h prime_generator.h rational.h rlimit.h symbol.h trace.h ) z3-z3-4.8.7/src/util/approx_nat.cpp000066400000000000000000000024411356505360400170530ustar00rootroot00000000000000/*++ Copyright (c) 2007 Microsoft Corporation Module Name: approx_nat.cpp Abstract: Approximated natural numbers. It performs operations on the set [0, ..., 2^{n-2}, huge]. Where huge represents all numbers greater than 2^{n-2}. Author: Leonardo (leonardo) 2008-01-11 Notes: --*/ #include "util/approx_nat.h" approx_nat::approx_nat(unsigned val) { m_value = val > m_limit ? UINT_MAX : val; } approx_nat & approx_nat::operator=(unsigned val) { m_value = val > m_limit ? UINT_MAX : val; return *this; } approx_nat & approx_nat::operator+=(unsigned w) { if (is_huge()) return *this; if (w > m_limit) { m_value = UINT_MAX; return *this; } m_value += w; if (m_value > m_limit) m_value = UINT_MAX; return *this; } approx_nat & approx_nat::operator*=(unsigned w) { if (is_huge()) return *this; unsigned long long r = static_cast(m_value) * static_cast(w); if (r > m_limit) m_value = UINT_MAX; else m_value = static_cast(r); return *this; } std::ostream & operator<<(std::ostream & target, approx_nat const & w) { if (w.is_huge()) target << "[huge]"; else target << w.get_value(); return target; } z3-z3-4.8.7/src/util/approx_nat.h000066400000000000000000000023321356505360400165170ustar00rootroot00000000000000/*++ Copyright (c) 2007 Microsoft Corporation Module Name: approx_nat.h Abstract: Approximated natural numbers. It performs operations on the set [0, ..., 2^{n-2}, huge]. Where huge represents all numbers greater than 2^{n-2}. Author: Leonardo (leonardo) 2008-01-11 Notes: --*/ #ifndef APPROX_NAT_H_ #define APPROX_NAT_H_ #include #include class approx_nat { unsigned m_value; static const unsigned m_limit = UINT_MAX >> 2; public: approx_nat():m_value(0) {} explicit approx_nat(unsigned val); bool is_huge() const { return m_value == UINT_MAX; } unsigned get_value() const { return m_value; } approx_nat & operator=(unsigned w); approx_nat & operator+=(unsigned w); approx_nat & operator+=(approx_nat const & w) { return operator+=(w.m_value); } approx_nat & operator*=(unsigned w); approx_nat & operator*=(approx_nat const & w) { return operator*=(w.m_value); } bool operator<(unsigned w) const { return !is_huge() && m_value < w; } bool operator<(approx_nat const & w) const { return !is_huge() && !w.is_huge() && m_value < w.m_value; } }; std::ostream & operator<<(std::ostream & target, approx_nat const & w); #endif /* APPROX_NAT_H_ */ z3-z3-4.8.7/src/util/approx_set.cpp000066400000000000000000000015751356505360400170730ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: approx_set.cpp Abstract: Approximated sets. Author: Leonardo de Moura (leonardo) 2007-03-02. Revision History: --*/ #include "util/approx_set.h" void approx_set::display(std::ostream & out) const { out << "{"; bool first = true; unsigned long long s = m_set; for (unsigned i = 0; i < approx_set_traits::capacity; i++) { if ((s & 1) != 0) { if (first) { first = false; } else { out << ", "; } out << i; } s = s >> 1; } out << "}"; } unsigned approx_set::size() const { unsigned long long tmp = m_set; unsigned r = 0; while (tmp > 0) { if ((tmp & 1) != 0) { r++; } tmp = tmp >> 1; } return r; } z3-z3-4.8.7/src/util/approx_set.h000066400000000000000000000141261356505360400165340ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: approx_set.h Abstract: Approximated sets. Author: Leonardo de Moura (leonardo) 2007-03-02. Revision History: --*/ #ifndef APPROX_SET_H_ #define APPROX_SET_H_ #include #include "util/debug.h" template class approx_set_traits; template <> class approx_set_traits { public: static const unsigned capacity = 64; static const unsigned long long zero = 0ull; static const unsigned long long one = 1ull; }; static_assert(sizeof(unsigned long long) == 8, ""); template <> class approx_set_traits { public: static const unsigned capacity = 32; static const unsigned zero = 0; static const unsigned one = 1; }; static_assert(sizeof(unsigned) == 4, "unsigned are 4 bytes"); template class approx_set_tpl : private T2U_Proc { protected: R m_set; unsigned e2u(T const & e) const { return T2U_Proc::operator()(e); } R u2s(unsigned u) const { return (approx_set_traits::one << (u & (approx_set_traits::capacity - 1))); } R e2s(T const & e) const { return u2s(e2u(e)); } static approx_set_tpl r2s(R const & s) { approx_set_tpl r; r.m_set = s; return r; } public: approx_set_tpl(): m_set(approx_set_traits::zero) { } explicit approx_set_tpl(T const & e): m_set(e2s(e)) { } approx_set_tpl(unsigned sz, T const * es): m_set(approx_set_traits::zero) { for (unsigned i = 0; i < sz; i++) insert(es[i]); } approx_set_tpl(approx_set_tpl const & s): m_set(s.m_set) { } void insert(T const & e) { m_set |= e2s(e); } bool may_contain(T const & e) const { return (m_set & e2s(e)) != approx_set_traits::zero; } bool must_not_contain(T const & e) const { return !may_contain(e); } friend inline approx_set_tpl mk_union(approx_set_tpl const & s1, approx_set_tpl const & s2) { return r2s(s1.m_set | s2.m_set); } friend inline approx_set_tpl mk_intersection(approx_set_tpl const & s1, approx_set_tpl const & s2) { return r2s(s1.m_set & s2.m_set); } void operator|=(approx_set_tpl const & other) { m_set |= other.m_set; } void operator&=(approx_set_tpl const & other) { m_set &= other.m_set; } void operator-=(approx_set_tpl const & other) { m_set &= ~(other.m_set); } bool empty() const { return m_set == approx_set_traits::zero; } friend inline bool empty(approx_set_tpl const & s) { return s.empty(); } bool must_not_subset(approx_set_tpl const & s2) const { return (m_set & ~(s2.m_set)) != approx_set_traits::zero; } friend inline bool must_not_subset(approx_set_tpl const & s1, approx_set_tpl const & s2) { return s1.must_not_subset(s2); } bool must_not_subsume(approx_set_tpl const & s2) const { return must_not_subset(s2); } friend inline bool must_not_subsume(approx_set_tpl const & s1, approx_set_tpl const & s2) { return s1.must_not_subset(s2); } friend inline bool must_not_eq(approx_set_tpl const & s1, approx_set_tpl const & s2) { return s1.m_set != s2.m_set; } friend inline bool may_eq(approx_set_tpl const & s1, approx_set_tpl const & s2) { return s1.m_set == s2.m_set; } /** \brief Return if s1 and s2 are the same approximated set. */ bool equiv(approx_set_tpl const & s2) const { return m_set == s2.m_set; } friend inline bool equiv(approx_set_tpl const & s1, approx_set_tpl const & s2) { return s1.m_set == s2.m_set; } /** \brief Return true if the approximation of s1 is a subset of the approximation of s2. */ friend inline bool approx_subset(approx_set_tpl const & s1, approx_set_tpl const & s2) { return s2.equiv(mk_union(s1, s2)); } void reset() { m_set = approx_set_traits::zero; } bool empty_intersection(approx_set_tpl const & other) const { return mk_intersection(*this, other).empty(); } }; struct u2u { unsigned operator()(unsigned u) const { return u; } }; typedef approx_set_tpl u_approx_set; #define APPROX_SET_CAPACITY (approx_set_traits::capacity) class approx_set : public u_approx_set { public: approx_set():u_approx_set() {} approx_set(unsigned e):u_approx_set(e) {} class iterator { unsigned long long m_set; unsigned m_val; void move_to_next() { // TODO: this code can be optimized in platforms with special // instructions to count leading (trailing) zeros in a word. while (m_set > 0) { if ((m_set & 1ull) != 0) { return; } m_val ++; m_set = m_set >> 1; } } public: iterator(unsigned long long s): m_set(s), m_val(0) { move_to_next(); } unsigned operator*() const { return m_val; } iterator & operator++() { m_val++; m_set = m_set >> 1; move_to_next(); return *this; } iterator operator++(int) { iterator tmp = *this; ++*this; return tmp; } bool operator==(iterator const & it) const { return m_set == it.m_set; } bool operator!=(iterator const & it) const { return m_set != it.m_set; } }; iterator begin() const { return iterator(m_set); } static iterator end() { return iterator(0); } void display(std::ostream & out) const; unsigned size() const; // for backward compatibility friend inline bool operator==(approx_set const & s1, approx_set const & s2) { return may_eq(s1, s2); } }; inline std::ostream & operator<<(std::ostream & out, approx_set const & s) { s.display(out); return out; } #endif /* APPROX_SET_H_ */ z3-z3-4.8.7/src/util/array.h000066400000000000000000000126711356505360400154710ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: array.h Abstract: Fixed size arrays Author: Leonardo de Moura (leonardo) 2011-01-26. Revision History: --*/ #ifndef ARRAY_H_ #define ARRAY_H_ template class array { public: // Return the space needed to store an array of size sz. static size_t space(size_t sz) { return sizeof(T)*sz + sizeof(size_t); } private: #define ARRAY_SIZE_IDX -1 T * m_data; void destroy_elements() { iterator it = begin(); iterator e = end(); for (; it != e; ++it) { it->~T(); } } char * raw_ptr() const { return reinterpret_cast(reinterpret_cast(m_data) - 1); } array & operator=(array const & source); void set_data(void * mem, unsigned sz) { size_t * _mem = static_cast(mem); *_mem = sz; _mem ++; m_data = reinterpret_cast(_mem); } template void allocate(Allocator & a, unsigned sz) { size_t * mem = reinterpret_cast(a.allocate(space(sz))); set_data(mem, sz); } void init(T const & v) { iterator it = begin(); iterator e = end(); for (; it != e; ++it) { new (it) T(v); } } void init(T const * vs) { iterator it = begin(); iterator e = end(); for (; it != e; ++it, ++vs) { new (it) T(*vs); } } public: typedef T data; typedef T * iterator; typedef const T * const_iterator; array():m_data(nullptr) {} /** \brief Store the array in the given chunk of memory (mem). This chunck should be big enough to store space(sz) bytes. */ array(void * mem, unsigned sz, T const * vs) { DEBUG_CODE(m_data = 0;); set(mem, sz, vs); } // WARNING: the memory allocated will not be automatically freed. array(void * mem, unsigned sz, bool init_mem) { DEBUG_CODE(m_data = 0;); set_data(mem, sz); if (init_mem) init(); } // WARNING: the memory allocated will not be automatically freed. template array(Allocator & a, unsigned sz, T const * vs) { DEBUG_CODE(m_data = 0;); set(a, sz, vs); } // WARNING: the memory allocated will not be automatically freed. template array(Allocator & a, unsigned sz, bool init_mem) { DEBUG_CODE(m_data = 0;); allocate(a, sz); if (init_mem) init(); } // WARNING: this does not free the memory used to store the array. // You must free it yourself, or use finalize. ~array() { if (m_data && CallDestructors) destroy_elements(); } // Free the memory used to store the array. template void finalize(Allocator & a) { if (m_data) { if (CallDestructors) destroy_elements(); a.deallocate(space(size()), raw_ptr()); m_data = nullptr; } } void set(void * mem, unsigned sz, T const * vs) { SASSERT(m_data == 0); set_data(mem, sz); init(vs); } template void set(Allocator & a, unsigned sz, T const * vs) { SASSERT(m_data == 0); allocate(a, sz); init(vs); } template void set(Allocator & a, unsigned sz, T const & v = T()) { SASSERT(m_data == 0); allocate(a, sz); init(v); } unsigned size() const { if (m_data == nullptr) { return 0; } return static_cast(reinterpret_cast(m_data)[ARRAY_SIZE_IDX]); } bool empty() const { return m_data == nullptr; } T & operator[](unsigned idx) { SASSERT(idx < size()); return m_data[idx]; } T const & operator[](unsigned idx) const { SASSERT(idx < size()); return m_data[idx]; } iterator begin() { return m_data; } iterator end() { return m_data + size(); } const_iterator begin() const { return m_data; } const_iterator end() const { return m_data + size(); } T const * c_ptr() const { return m_data; } T * c_ptr() { return m_data; } void swap(array & other) { std::swap(m_data, other.m_data); } }; template class ptr_array : public array { public: ptr_array() {} ptr_array(void * mem, unsigned sz, T * const * vs):array(mem, sz, vs) {} template ptr_array(Allocator & a, unsigned sz, T * const * vs):array(a, sz, vs) {} ptr_array(void * mem, unsigned sz, bool init_mem):array(mem, sz, init_mem) {} template ptr_array(Allocator & a, unsigned sz, bool init_mem):array(a, sz, init_mem) {} }; template class sarray : public array { public: sarray() {} sarray(void * mem, unsigned sz, T const * vs):array(mem, sz, vs) {} template sarray(Allocator & a, unsigned sz, T const * vs):array(a, sz, vs) {} sarray(void * mem, unsigned sz, bool init_mem):array(mem, sz, init_mem) {} template sarray(Allocator & a, unsigned sz, bool init_mem):array(a, sz, init_mem) {} }; #endif z3-z3-4.8.7/src/util/array_map.h000066400000000000000000000073431356505360400163260ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: array_map.h Abstract: A mapping for keys that can be mapped to unsigned integers. Author: Leonardo de Moura (leonardo) 2008-01-03. Revision History: --*/ #ifndef ARRAY_MAP_H_ #define ARRAY_MAP_H_ #include "util/vector.h" #include "util/optional.h" /** \brief Implements a mapping from Key to Data. Plugin must provide the following functions: - void ins_eh(Key const & k, Data const & d); - void del_eh(Key const & k, Data const & d); - unsigned to_int(Key const & k); */ template class array_map { struct entry { Key m_key; Data m_data; unsigned m_timestamp; entry(Key const & k, Data const & d, unsigned t): m_key(k), m_data(d), m_timestamp(t) {} }; unsigned m_timestamp; unsigned m_garbage; unsigned m_non_garbage; static const unsigned m_gc_threshold = 10000; vector, CallDestructors > m_map; Plugin m_plugin; bool is_current(optional const& e) const { return e->m_timestamp == m_timestamp; } optional const & get_core(Key const & k) const { unsigned id = m_plugin.to_int(k); if (id < m_map.size()) { optional const & e = m_map[id]; if (e && is_current(e)) { return e; } } return optional::undef(); } void really_flush() { for (optional & e : m_map) { if (e) { m_plugin.del_eh(e->m_key, e->m_data); e.set_invalid(); } } m_garbage = 0; m_non_garbage = 0; } public: array_map(Plugin const & p = Plugin()):m_timestamp(0), m_garbage(0), m_non_garbage(0), m_plugin(p) {} ~array_map() { really_flush(); } bool contains(Key const & k) const { return get_core(k); } Data const & get(Key const & k) const { optional const & e = get_core(k); SASSERT(e); return e->m_data; } void reset() { if (m_timestamp < UINT_MAX) { m_timestamp++; } else { really_flush(); m_timestamp = 0; } } void insert(Key const & k, Data const & d) { unsigned id = m_plugin.to_int(k); if (id >= m_map.size()) { m_map.resize(id + 1, optional::undef()); } m_plugin.ins_eh(k, d); optional & e = m_map[id]; if (e) { if (!is_current(e)) { --m_garbage; ++m_non_garbage; } m_plugin.del_eh(e->m_key, e->m_data); } else { ++m_non_garbage; } e = entry(k, d, m_timestamp); } void erase(Key const & k) { unsigned id = m_plugin.to_int(k); if (id < m_map.size()) { optional & e = m_map[id]; if (e) { m_plugin.del_eh(e->m_key, e->m_data); if (is_current(e)) { SASSERT(m_non_garbage > 0); --m_non_garbage; } else { SASSERT(m_garbage > 0); --m_garbage; } e.set_invalid(); } } } void flush() { m_garbage += m_non_garbage; m_non_garbage = 0; if (m_garbage > m_gc_threshold) { really_flush(); } else { reset(); } } void finalize() { really_flush(); } }; #endif /* ARRAY_MAP_H_ */ z3-z3-4.8.7/src/util/backtrackable_set.h000066400000000000000000000046221356505360400177740ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: backtrackable_set.h Abstract: Quick hack for support backtrackable sets. Author: Leonardo de Moura (leonardo) 2011-01-08. Revision History: --*/ #ifndef BACKTRACKABLE_SET_H_ #define BACKTRACKABLE_SET_H_ #include "util/vector.h" template struct default_eh { void operator()(T const & e, bool ins) {} }; // quick hack for having backtrackable sets. // // EV is a big hack, it should be used with care. // template > class backtrackable_set : private EV { enum trail_kind { DEL, INS }; typedef std::pair trail_obj; Set m_set; svector m_trail; svector m_scopes; public: typedef typename Set::iterator iterator; backtrackable_set(EV const & ev = EV()): EV(ev) { } void insert(T const & e) { if (m_scopes.empty()) { m_set.insert(e); } else if (!m_set.contains(e)) { m_set.insert(e); m_trail.push_back(std::make_pair(INS, e)); } } void erase(T const & e) { if (m_scopes.empty()) { m_set.insert(e); } else if (m_set.contains(e)) { m_set.erase(e); m_trail.push_back(std::make_pair(DEL, e)); } } bool contains(T const & e) const { return m_set.contains(e); } bool empty() const { return m_set.empty(); } void push_scope() { m_scopes.push_back(m_trail.size()); } void pop_scope() { unsigned old_sz = m_scopes.back(); m_scopes.pop_back(); SASSERT(old_sz <= m_trail.size()); while (m_trail.size() > old_sz) { trail_obj & t = m_trail.back(); if (t.first == INS) { this->operator()(t.second, true); m_set.erase(t.second); } else { SASSERT(t.first == DEL); this->operator()(t.second, false); m_set.insert(t.second); } m_trail.pop_back(); } } void reset() { m_scopes.reset(); m_trail.reset(); m_set.reset(); } iterator begin() const { return m_set.begin(); } iterator end() const { return m_set.end(); } }; #endif z3-z3-4.8.7/src/util/basic_interval.h000066400000000000000000000245411356505360400173370ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: basic_interval.h Abstract: Basic interval arithmetic template for precise numerals: mpz, mpq, mpbq. Only basic support is provided. There is no support for: - minus and plus infinity bounds. - mixed open/closed intervals such as (2, 3] The main customer of this package is the algebraic_number module. Author: Leonardo de Moura (leonardo) 2012-12-04. Revision History: --*/ #ifndef BASIC_INTERVAL_H_ #define BASIC_INTERVAL_H_ template class basic_interval_manager { public: typedef typename numeral_manager::numeral bound; class interval { friend class basic_interval_manager; bound m_lower; bound m_upper; public: interval() {} bound const & lower() const { return m_lower; } bound const & upper() const { return m_upper; } bound & lower() { return m_lower; } bound & upper() { return m_upper; } }; class scoped_interval { basic_interval_manager & m_manager; interval m_interval; public: scoped_interval(basic_interval_manager & m):m_manager(m) {} ~scoped_interval() { m_manager.del(m_interval); } basic_interval_manager & m() const { return m_manager; } operator interval const &() const { return m_interval; } operator interval&() { return m_interval; } interval const & get() const { return m_interval; } interval & get() { return m_interval; } void reset() { m().reset(m_interval); } void swap(scoped_interval & a) { m().swap(m_interval, a.m_interval); } void swap(interval & a) { m().swap(m_interval, a); } bound const & lower() const { return m_interval.lower(); } bound const & upper() const { return m_interval.upper(); } bound & lower() { return m_interval.lower(); } bound & upper() { return m_interval.upper(); } friend std::ostream & operator<<(std::ostream & out, scoped_interval const & a) { a.m().display(out, a.get()); return out; } }; protected: numeral_manager & m_manager; bound m_mul_curr; bound m_mul_max; bound m_mul_min; public: typedef interval numeral; // allow intervals to be used by algorithms parameterized by numeral_manager basic_interval_manager(numeral_manager & m): m_manager(m) { } ~basic_interval_manager() { m().del(m_mul_curr); m().del(m_mul_max); m().del(m_mul_min); } numeral_manager & m() const { return m_manager; } /** \brief Delete interval */ void del(interval & a) { m().del(a.m_lower); m().del(a.m_upper); } /** \brief Delete and reset lower and upper bounds to 0 */ void reset(interval & a) { m().reset(a.m_lower); m().reset(a.m_upper); } bound const & lower(interval const & a) { return a.lower(); } bound const & upper(interval const & a) { return a.upper(); } /** \brief a <- (lower, upper) */ void set(interval & a, bound const & lower, bound const & upper) { SASSERT(m().le(lower, upper)); m().set(a.m_lower, lower); m().set(a.m_upper, upper); } /** \brief a <- b */ void set(interval & a, interval const & b) { set(a, b.m_lower, b.m_upper); } /** \brief a <- (n, n) Manager must be configured for closed intervals. */ void set(interval & a, bound const & n) { m().set(a.m_lower, n); m().set(a.m_upper, n); } void set_lower(interval & a, bound const & n) { SASSERT(m().le(n, a.m_upper)); m().set(a.m_lower, n); } void set_upper(interval & a, bound const & n) { SASSERT(m().le(a.m_lower, n)); m().set(a.m_upper, n); } void swap(interval & a, interval & b) { m().swap(a.m_lower, b.m_lower); m().swap(a.m_upper, b.m_upper); } /** \brief a <- -a */ void neg(interval & a) { m().neg(a.m_lower); m().neg(a.m_upper); m().swap(a.m_lower, a.m_upper); } /** \brief Return true if a does not contain any value. We can only have empty intervals if the manager is configured to used open intervals. */ bool is_empty(interval const & a) { return !closed && m().eq(a.m_lower, a.m_upper); } /** \brief Return true if all values in the given interval are positive. */ bool is_pos(interval const & a) { return (closed && m().is_pos(a.m_lower)) || (!closed && m().is_nonneg(a.m_lower)); } /** \brief Return true if all values in the given interval are negative. */ bool is_neg(interval const & a) { return (closed && m().is_neg(a.m_upper)) || (!closed && m().is_nonpos(a.m_upper)); } /** \brief Return true if 0 is in the interval. */ bool contains_zero(interval const & a) { return (closed && m().is_nonpos(a.m_lower) && m().is_nonneg(a.m_upper)) || (!closed && m().is_neg(a.m_lower) && m().is_pos(a.m_upper)); } /** \brief Return true if all values in interval a are in interval b. */ bool is_subset(interval const & a, interval const & b) { return m().le(b.m_lower, a.m_lower) && m().le(a.m_upper, b.m_upper); } /** \brief Return true if there is no value v s.t. v \in a and v \in b. */ bool disjoint(interval const & a, interval const & b) { return (closed && (m().lt(a.m_upper, b.m_lower) || m().lt(b.m_upper, a.m_lower))) || (!closed && (m().le(a.m_upper, b.m_upper) || m().le(b.m_upper, a.m_lower))); } /** \brief Return true if all elements in a are smaller than all elements in b. */ bool precedes(interval const & a, interval const & b) { return (closed && (m().lt(a.m_upper, b.m_lower))) || (!closed && (m().le(a.m_upper, b.m_lower))); } /** \brief Return true if all elements in a are smaller than b. */ bool precedes(interval const & a, bound const & b) { return (closed && (m().lt(a.m_upper, b))) || (!closed && (m().le(a.m_upper, b))); } /** \brief Return true if a is smaller than all elements in b. */ bool precedes(bound const & a, interval const & b) { return (closed && (m().lt(a, b.m_lower))) || (!closed && (m().le(a, b.m_lower))); } /** \brief a <- 1/a \pre a.m_lower and m_upper must not be 0. \pre bound must be a field. */ void inv(interval & a) { SASSERT(numeral_manager::field()); SASSERT(!contains_zero(a)); SASSERT(!m().is_zero(a.m_lower) && !m().is_zero(a.m_upper)); m().inv(a.m_lower); m().inv(a.m_upper); m().swap(a.m_lower, a.m_upper); } /** \brief c <- a + b */ void add(interval const & a, interval const & b, interval & c) { m().add(a.m_lower, b.m_lower, c.m_lower); m().add(a.m_upper, b.m_upper, c.m_upper); } /** \brief c <- a - b */ void sub(interval const & a, interval const & b, interval & c) { m().sub(a.m_lower, b.m_upper, c.m_lower); m().sub(a.m_upper, b.m_lower, c.m_upper); } private: /** \brief Init the value of m_mul_max and m_mul_min using m_mul_curr */ void init_mul_max_min() { m().set(m_mul_min, m_mul_curr); m().swap(m_mul_max, m_mul_curr); } /** \brief Update the value of m_mul_max and m_mul_min using m_mul_curr */ void update_mul_max_min() { if (m().lt(m_mul_curr, m_mul_min)) m().set(m_mul_min, m_mul_curr); if (m().gt(m_mul_curr, m_mul_max)) m().swap(m_mul_max, m_mul_curr); } public: /** \brief c <- a * b */ void mul(interval const & a, interval const & b, interval & c) { m().mul(a.m_lower, b.m_lower, m_mul_curr); init_mul_max_min(); m().mul(a.m_lower, b.m_upper, m_mul_curr); update_mul_max_min(); m().mul(a.m_upper, b.m_lower, m_mul_curr); update_mul_max_min(); m().mul(a.m_upper, b.m_upper, m_mul_curr); update_mul_max_min(); m().swap(c.m_lower, m_mul_min); m().swap(c.m_upper, m_mul_max); } /** \brief c <- a/b \pre b m_lower and m_upper must not be 0 \pre bound must be a field. */ void div(interval const & a, interval const & b, interval & c) { SASSERT(numeral_manager::field()); SASSERT(!contains_zero(b)); SASSERT(!m().is_zero(b.m_lower) && !m().is_zero(b.m_upper)); m().div(a.m_lower, b.m_lower, m_mul_curr); init_mul_max_min(); m().div(a.m_lower, b.m_upper, m_mul_curr); update_mul_max_min(); m().div(a.m_upper, b.m_lower, m_mul_curr); update_mul_max_min(); m().div(a.m_upper, b.m_upper, m_mul_curr); update_mul_max_min(); m().swap(c.m_lower, m_mul_min); m().swap(c.m_upper, m_mul_max); } /** \brief c <- a^n */ void power(interval const & a, unsigned n, interval & c) { // Let a be of the form (l, u) if (n % 2 == 1) { // n is odd // c <- (l^n, u^n) m().power(a.m_lower, n, c.m_lower); m().power(a.m_upper, n, c.m_upper); } else { SASSERT(n % 2 == 0); m().power(a.m_lower, n, c.m_lower); m().power(a.m_upper, n, c.m_upper); if (m().is_nonneg(a.m_lower)) { // n is even and l >= 0 // c <- (l^n, u^n) return; } if (m().is_neg(a.m_upper)) { // n is even and u < 0 // c <- (u^n, l^n) m().swap(c.m_lower, c.m_upper); return; } // c <- (0, max(l^n, u^n)) if (m().gt(c.m_lower, c.m_upper)) m().swap(c.m_lower, c.m_upper); m().reset(c.m_lower); } } void display(std::ostream & out, interval const & a) { out << (closed ? "[" : "(") << m().to_string(a.m_lower) << ", " << m().to_string(a.m_upper) << (closed ? "]" : ")"); } }; #endif z3-z3-4.8.7/src/util/bit_util.cpp000066400000000000000000000234401356505360400165150ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: bit_util.cpp Abstract: Bit hacking utilities. Author: Leonardo de Moura (leonardo) 2012-09-11. Revision History: --*/ #include "util/bit_util.h" #include "util/util.h" #include "util/debug.h" #include /** \brief (Debugging version) Return the position of the most significant (set) bit of a nonzero unsigned integer. */ #ifdef Z3DEBUG unsigned slow_msb_pos(unsigned v) { SASSERT(v != 0); unsigned r = 0; while (v != 1) { v = v >> 1; r++; } return r; } #endif /** \brief Return the position of the most significant (set) bit of a nonzero unsigned integer. */ unsigned msb_pos(unsigned v) { SASSERT(v != 0); #ifdef Z3DEBUG unsigned expected = slow_msb_pos(v); #endif unsigned r, shift; r = (v > 0xFFFF) << 4; v >>= r; shift = (v > 0xFF) << 3; v >>= shift; r |= shift; shift = (v > 0xF) << 2; v >>= shift; r |= shift; shift = (v > 0x3) << 1; v >>= shift; r |= shift; r |= (v >> 1); SASSERT(r == expected); return r; } /** \brief Return the number of leading zeros bits in a nonzero unsigned integer. */ unsigned nlz_core(unsigned x) { SASSERT(x != 0); #ifdef __GNUC__ return __builtin_clz(x); #else return 31 - msb_pos(x); #endif } /** \brief Return the number of leading zero bits in data (a number of sz words). */ unsigned nlz(unsigned sz, unsigned const * data) { unsigned r = 0; unsigned i = sz; while (i > 0) { --i; unsigned d = data[i]; if (d == 0) r += 32; else return r + nlz_core(d); } return r; } /** \brief Return the number of trailing zeros in a nonzero unsigned number. */ unsigned ntz_core(unsigned x) { SASSERT(x != 0); #ifdef __GNUC__ return __builtin_ctz(x); #else float f = static_cast(x & static_cast(-static_cast(x))); unsigned u; SASSERT(sizeof(u) == sizeof(f)); memcpy(&u, &f, sizeof(u)); return (u >> 23) - 0x7f; #endif } /** \brief Return the number of trailing zero bits in data (a number of sz words). */ unsigned ntz(unsigned sz, unsigned const * data) { unsigned r = 0; for (unsigned i = 0; i < sz; i++) { unsigned d = data[i]; if (d == 0) r += 32; else return r + ntz_core(d); } return r; } /** \brief dst <- src Truncate if src_sz > dst_sz. Fill range [src_sz, dst_sz) of dst with zeros if dst_sz > src_sz. */ void copy(unsigned src_sz, unsigned const * src, unsigned dst_sz, unsigned * dst) { if (dst_sz >= src_sz) { unsigned i; for (i = 0; i < src_sz; i++) dst[i] = src[i]; for (; i < dst_sz; i++) dst[i] = 0; } else { SASSERT(dst_sz < src_sz); for (unsigned i = 0; i < dst_sz; i++) dst[i] = src[i]; } } /** \brief Return true if all words of data are zero. */ bool is_zero(unsigned sz, unsigned const * data) { for (unsigned i = 0; i < sz; i++) if (data[i]) return false; return true; } /** \brief Set all words of data to zero. */ void reset(unsigned sz, unsigned * data) { for (unsigned i = 0; i < sz; i++) data[i] = 0; } /** \brief dst <- src << k Store in dst the result of shifting src k bits to the left. The result is truncated by dst_sz. \pre src_sz != 0 \pre dst_sz != 0 */ void shl(unsigned src_sz, unsigned const * src, unsigned k, unsigned dst_sz, unsigned * dst) { SASSERT(src_sz != 0); SASSERT(dst_sz != 0); SASSERT(k != 0); unsigned word_shift = k / (8 * sizeof(unsigned)); unsigned bit_shift = k % (8 * sizeof(unsigned)); if (word_shift > 0) { unsigned j = src_sz; unsigned i = src_sz + word_shift; if (i > dst_sz) { if (j >= i - dst_sz) j -= (i - dst_sz); else j = 0; i = dst_sz; } else if (i < dst_sz) { for (unsigned r = i; r < dst_sz; r++) dst[r] = 0; } while (j > 0) { --j; --i; dst[i] = src[j]; } while (i > 0) { --i; dst[i] = 0; } if (bit_shift > 0) { unsigned comp_shift = (8 * sizeof(unsigned)) - bit_shift; unsigned prev = 0; for (unsigned i = word_shift; i < dst_sz; i++) { unsigned new_prev = (dst[i] >> comp_shift); dst[i] <<= bit_shift; dst[i] |= prev; prev = new_prev; } } } else { unsigned comp_shift = (8 * sizeof(unsigned)) - bit_shift; unsigned prev = 0; if (src_sz > dst_sz) src_sz = dst_sz; for (unsigned i = 0; i < src_sz; i++) { unsigned new_prev = (src[i] >> comp_shift); dst[i] = src[i]; dst[i] <<= bit_shift; dst[i] |= prev; prev = new_prev; } if (dst_sz > src_sz) { dst[src_sz] = prev; for (unsigned i = src_sz+1; i < dst_sz; i++) dst[i] = 0; } } } /** \brief dst <- src >> k Store in dst the result of shifting src k bits to the right. \pre dst must have size sz. \pre src_sz != 0 \pre dst_sz != 0 */ void shr(unsigned sz, unsigned const * src, unsigned k, unsigned * dst) { unsigned digit_shift = k / (8 * sizeof(unsigned)); if (digit_shift >= sz) { reset(sz, dst); return; } unsigned bit_shift = k % (8 * sizeof(unsigned)); unsigned comp_shift = (8 * sizeof(unsigned)) - bit_shift; unsigned new_sz = sz - digit_shift; if (new_sz < sz) { unsigned i = 0; unsigned j = digit_shift; if (bit_shift != 0) { for (; i < new_sz - 1; i++, j++) { dst[i] = src[j]; dst[i] >>= bit_shift; dst[i] |= (src[j+1] << comp_shift); } dst[i] = src[j]; dst[i] >>= bit_shift; } else { for (; i < new_sz; i++, j++) { dst[i] = src[j]; } } for (unsigned i = new_sz; i < sz; i++) dst[i] = 0; } else { SASSERT(new_sz == sz); SASSERT(bit_shift != 0); unsigned i = 0; for (; i < new_sz - 1; i++) { dst[i] = src[i]; dst[i] >>= bit_shift; dst[i] |= (src[i+1] << comp_shift); } dst[i] = src[i]; dst[i] >>= bit_shift; } } void shr(unsigned src_sz, unsigned const * src, unsigned k, unsigned dst_sz, unsigned * dst) { unsigned digit_shift = k / (8 * sizeof(unsigned)); if (digit_shift >= src_sz) { reset(dst_sz, dst); return; } unsigned bit_shift = k % (8 * sizeof(unsigned)); unsigned comp_shift = (8 * sizeof(unsigned)) - bit_shift; unsigned new_sz = src_sz - digit_shift; if (digit_shift > 0) { unsigned i = 0; unsigned j = digit_shift; if (bit_shift != 0) { unsigned sz = new_sz; if (new_sz > dst_sz) sz = dst_sz; for (; i < sz - 1; i++, j++) { dst[i] = src[j]; dst[i] >>= bit_shift; dst[i] |= (src[j+1] << comp_shift); } dst[i] = src[j]; dst[i] >>= bit_shift; if (new_sz > dst_sz) dst[i] |= (src[j+1] << comp_shift); } else { if (new_sz > dst_sz) new_sz = dst_sz; for (; i < new_sz; i++, j++) { dst[i] = src[j]; } } } else { SASSERT(new_sz == src_sz); SASSERT(bit_shift != 0); unsigned sz = new_sz; if (new_sz > dst_sz) sz = dst_sz; unsigned i = 0; for (; i < sz - 1; i++) { dst[i] = src[i]; dst[i] >>= bit_shift; dst[i] |= (src[i+1] << comp_shift); } dst[i] = src[i]; dst[i] >>= bit_shift; if (new_sz > dst_sz) dst[i] |= (src[i+1] << comp_shift); } for (unsigned i = new_sz; i < dst_sz; i++) dst[i] = 0; } /** \brief Return true if one of the first k bits of src is not zero. */ bool has_one_at_first_k_bits(unsigned sz, unsigned const * data, unsigned k) { SASSERT(sz != 0); unsigned word_sz = k / (8 * sizeof(unsigned)); if (word_sz > sz) word_sz = sz; for (unsigned i = 0; i < word_sz; i++) { if (data[i] != 0) return true; } if (word_sz < sz) { unsigned bit_sz = k % (8 * sizeof(unsigned)); unsigned mask = (1u << bit_sz) - 1; return (data[word_sz] & mask) != 0; } return false; } bool inc(unsigned sz, unsigned * data) { for (unsigned i = 0; i < sz; i++) { data[i]++; if (data[i] != 0) return true; // no overflow } return false; // overflow } bool dec(unsigned sz, unsigned * data) { for (unsigned i = 0; i < sz; i++) { data[i]--; if (data[i] != UINT_MAX) return true; // no underflow } return false; // underflow } bool lt(unsigned sz, unsigned * data1, unsigned * data2) { unsigned i = sz; while (i > 0) { --i; if (data1[i] < data2[i]) return true; if (data1[i] > data2[i]) return false; } return false; } bool add(unsigned sz, unsigned const * a, unsigned const * b, unsigned * c) { unsigned k = 0; for (unsigned j = 0; j < sz; j++) { unsigned r = a[j] + b[j]; bool c1 = r < a[j]; c[j] = r + k; bool c2 = c[j] < r; k = c1 | c2; } return k == 0; } z3-z3-4.8.7/src/util/bit_util.h000066400000000000000000000055301356505360400161620ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: bit_util.h Abstract: Bit hacking utilities. Author: Leonardo de Moura (leonardo) 2012-09-11. Revision History: --*/ #ifndef BIT_UTIL_H_ #define BIT_UTIL_H_ /** \brief Return the position of the most significant (set) bit of a nonzero unsigned integer. */ unsigned msb_pos(unsigned v); /** \brief Return the number of leading zeros bits in a nonzero unsigned integer. */ unsigned nlz_core(unsigned x); /** \brief Return the number of leading zero bits in data (a number of sz words). */ unsigned nlz(unsigned sz, unsigned const * data); /** \brief Return the number of trailing zeros in a nonzero unsigned number. */ unsigned ntz_core(unsigned x); /** \brief Return the number of trailing zero bits in data (a number of sz words). */ unsigned ntz(unsigned sz, unsigned const * data); /** \brief dst <- src Truncate if src_sz > dst_sz. Fill range [src_sz, dst_sz) of dst with zeros if dst_sz > src_sz. */ void copy(unsigned src_sz, unsigned const * src, unsigned dst_sz, unsigned * dst); /** \brief Return true if all words of data are zero. */ bool is_zero(unsigned sz, unsigned const * data); /** \brief Set all words of data to zero. */ void reset(unsigned sz, unsigned * data); /** \brief dst <- src << k Store in dst the result of shifting src k bits to the left. The result is truncated by dst_sz. \pre src_sz != 0 \pre dst_sz != 0 */ void shl(unsigned src_sz, unsigned const * src, unsigned k, unsigned dst_sz, unsigned * dst); /** \brief dst <- src >> k Store in dst the result of shifting src k bits to the right. \pre dst must have size sz. \pre src_sz != 0 \pre dst_sz != 0 */ void shr(unsigned sz, unsigned const * src, unsigned k, unsigned * dst); /** \brief dst <- src >> k Store in dst the result of shifting src k bits to the right. Truncate if src_sz > dst_sz. Fill range [src_sz, dst_sz) of dst with zeros if dst_sz > src_sz. \pre src_sz != 0 \pre dst_sz != 0 */ void shr(unsigned src_sz, unsigned const * src, unsigned k, unsigned dst_sz, unsigned * dst); /** \brief Return true if one of the first k bits of src is not zero. */ bool has_one_at_first_k_bits(unsigned sz, unsigned const * data, unsigned k); /** \brief data <- data + 1 Return true if no overflow occurred. */ bool inc(unsigned sz, unsigned * data); /** \brief data <- data - 1 Return true if no underflow occurred. */ bool dec(unsigned sz, unsigned * data); /** \brief Return true if data1 < data2. Both must have the same size. */ bool lt(unsigned sz, unsigned * data1, unsigned * data2); /** \brief Store in c the a+b. This procedure assumes that a,b,c are vectors of size sz. Return false if a+b overflows. */ bool add(unsigned sz, unsigned const * a, unsigned const * b, unsigned * c); #endif z3-z3-4.8.7/src/util/bit_vector.cpp000066400000000000000000000146341356505360400170470ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: bitvector.cpp Abstract: Simple bitvector implementation Author: Leonardo de Moura (leonardo) 2006-10-03. Revision History: --*/ #include #include "util/bit_vector.h" #include "util/trace.h" #define DEFAULT_CAPACITY 2 #define MK_MASK(_num_bits_) ((1U << _num_bits_) - 1) void bit_vector::expand_to(unsigned new_capacity) { if (m_data) { m_data = (unsigned*)memory::reallocate(m_data, new_capacity * sizeof(unsigned)); } else { m_data = alloc_svect(unsigned, new_capacity); } memset(m_data + m_capacity, 0, (new_capacity - m_capacity) * sizeof(unsigned)); m_capacity = new_capacity; } void bit_vector::resize(unsigned new_size, bool val) { if (new_size <= m_num_bits) { m_num_bits = new_size; return; } TRACE("bit_vector", tout << "expanding: " << new_size << " capacity: " << m_capacity << " num words: " << num_words(new_size) << "\n";); if (num_words(new_size) > m_capacity) { expand_to((num_words(new_size) * 3 + 1) >> 1); } unsigned bwidx = m_num_bits/32; unsigned ewidx = num_words(new_size); unsigned * begin = m_data + bwidx; unsigned pos = m_num_bits % 32; unsigned mask = MK_MASK(pos); int cval; if (val) { *begin |= ~mask; cval = ~0; } else { *begin &= mask; cval = 0; } TRACE("bit_vector", tout << "num_bits: " << m_num_bits << "\n"; tout << "bwidx: " << bwidx << "\n"; tout << "ewidx: " << ewidx << "\n"; tout << "pos: " << pos << "\n"; tout << "mask: " << std::hex << mask << "\n" << std::dec; tout << "cval: " << cval << "\n";); if (bwidx < ewidx) { memset(begin + 1, cval, (ewidx - bwidx - 1) * sizeof(unsigned)); } m_num_bits = new_size; } void bit_vector::shift_right(unsigned k) { if (k == 0) return; unsigned new_num_bits = m_num_bits + k; unsigned old_num_words = num_words(m_num_bits); unsigned new_num_words = num_words(new_num_bits); resize(m_num_bits + k, false); unsigned bit_shift = k % (8 * sizeof(unsigned)); unsigned word_shift = k / (8 * sizeof(unsigned)); if (word_shift > 0) { unsigned j = old_num_words; unsigned i = old_num_words + word_shift; while (j > 0) { --j; --i; m_data[i] = m_data[j]; } while (i > 0) { --i; m_data[i] = 0; } } if (bit_shift > 0) { DEBUG_CODE({ for (unsigned i = 0; i < word_shift; i++) { SASSERT(m_data[i] == 0); } }); unsigned comp_shift = (8 * sizeof(unsigned)) - bit_shift; unsigned prev = 0; for (unsigned i = word_shift; i < new_num_words; i++) { unsigned new_prev = (m_data[i] >> comp_shift); m_data[i] <<= bit_shift; m_data[i] |= prev; prev = new_prev; } } } bool bit_vector::operator==(bit_vector const & source) const { if (m_num_bits != source.m_num_bits) return false; unsigned n = num_words(); if (n == 0) return true; unsigned i; for (i = 0; i < n - 1; i++) { if (m_data[i] != source.m_data[i]) return false; } unsigned bit_rest = source.m_num_bits % 32; unsigned mask = MK_MASK(bit_rest); if (mask == 0) mask = UINT_MAX; return (m_data[i] & mask) == (source.m_data[i] & mask); } bit_vector & bit_vector::operator|=(bit_vector const & source) { if (size() < source.size()) resize(source.size(), false); unsigned n2 = source.num_words(); SASSERT(n2 <= num_words()); unsigned bit_rest = source.m_num_bits % 32; if (bit_rest == 0) { unsigned i = 0; for (i = 0; i < n2; i++) m_data[i] |= source.m_data[i]; } else { unsigned i = 0; for (i = 0; i < n2 - 1; i++) m_data[i] |= source.m_data[i]; unsigned mask = MK_MASK(bit_rest); m_data[i] |= source.m_data[i] & mask; } return *this; } bit_vector & bit_vector::operator&=(bit_vector const & source) { unsigned n1 = num_words(); unsigned n2 = source.num_words(); if (n1 == 0) return *this; if (n2 > n1) { for (unsigned i = 0; i < n1; i++) m_data[i] &= source.m_data[i]; } else { SASSERT(n2 <= n1); unsigned bit_rest = source.m_num_bits % 32; unsigned i = 0; if (bit_rest == 0) { for (i = 0; i < n2; i++) m_data[i] &= source.m_data[i]; } else { for (i = 0; i < n2 - 1; i++) m_data[i] &= source.m_data[i]; unsigned mask = MK_MASK(bit_rest); m_data[i] &= (source.m_data[i] & mask); } for (i = n2; i < n1; i++) m_data[i] = 0; } return *this; } void bit_vector::display(std::ostream & out) const { #if 1 unsigned i = m_num_bits; while (i > 0) { --i; if (get(i)) out << "1"; else out << "0"; } #else for (unsigned i = 0; i < m_num_bits; i++) { if (get(i)) out << "1"; else out << "0"; if ((i + 1) % 32 == 0) out << "\n"; } #endif } bool bit_vector::contains(bit_vector const& other) const { unsigned n = num_words(); if (n == 0) return true; for (unsigned i = 0; i < n - 1; ++i) { if ((m_data[i] & other.m_data[i]) != other.m_data[i]) return false; } unsigned bit_rest = m_num_bits % 32; unsigned mask = (1U << bit_rest) - 1; if (mask == 0) mask = UINT_MAX; unsigned other_data = other.m_data[n-1] & mask; return (m_data[n-1] & other_data) == other_data; } unsigned bit_vector::get_hash() const { return string_hash(reinterpret_cast(m_data), size()/8, 0); } bit_vector& bit_vector::neg() { unsigned n = num_words(); for (unsigned i = 0; i < n; ++i) { m_data[i] = ~m_data[i]; } return *this; } void fr_bit_vector::reset() { unsigned sz = size(); unsigned_vector::const_iterator it = m_one_idxs.begin(); unsigned_vector::const_iterator end = m_one_idxs.end(); for (; it != end; ++it) { unsigned idx = *it; if (idx < sz) unset(idx); } m_one_idxs.reset(); } z3-z3-4.8.7/src/util/bit_vector.h000066400000000000000000000136621356505360400165140ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: bit_vector.h Abstract: Simple bitvector implementation. Author: Leonardo de Moura (leonardo) 2006-10-03. Revision History: --*/ #ifndef BIT_VECTOR_H_ #define BIT_VECTOR_H_ #include #include "util/debug.h" #include "util/vector.h" #include "util/memory_manager.h" static_assert(sizeof(unsigned) == 4, "unsigned are 4 bytes"); #define BV_DEFAULT_CAPACITY 2 class bit_vector { protected: unsigned m_num_bits; unsigned m_capacity; //!< in words unsigned * m_data; static unsigned get_pos_mask(unsigned bit_idx) { return 1 << (bit_idx % 32); } static unsigned num_words(unsigned num_bits) { // return (num_bits % 32) == 0 ? (num_bits / 32) : ((num_bits / 32) + 1); return (num_bits + 31) / 32; } void expand_to(unsigned new_capacity); void expand() { expand_to(m_capacity == 0 ? BV_DEFAULT_CAPACITY : ((m_capacity * 3 + 1) >> 1)); } unsigned get_bit_word(unsigned bit_idx) const { SASSERT(bit_idx < size()); return m_data[bit_idx / 32]; } unsigned & get_bit_word(unsigned bit_idx) { SASSERT(bit_idx < size()); return m_data[bit_idx / 32]; } public: bit_vector(): m_num_bits(0), m_capacity(0), m_data(nullptr) { } bit_vector(unsigned reserve_num_bits) : m_num_bits(0), m_capacity(num_words(reserve_num_bits)), m_data(alloc_svect(unsigned, m_capacity)) { memset(m_data, 0, m_capacity * sizeof(unsigned)); } bit_vector(bit_vector const & source): m_num_bits(source.m_num_bits), m_capacity(source.m_capacity), m_data(nullptr) { if (source.m_data) { m_data = alloc_svect(unsigned, m_capacity); memcpy(m_data, source.m_data, m_capacity * sizeof(unsigned)); } } bit_vector(unsigned const * source, int num_bits): m_num_bits(num_bits), m_capacity(num_words(num_bits)), m_data(alloc_svect(unsigned, m_capacity)) { memcpy(m_data, source, m_capacity * sizeof(unsigned)); } ~bit_vector() { dealloc_svect(m_data); } void reset() { if (m_data) memset(m_data, 0, m_capacity * sizeof(unsigned)); m_num_bits = 0; } void swap(bit_vector & other) { std::swap(m_data, other.m_data); std::swap(m_num_bits, other.m_num_bits); std::swap(m_capacity, other.m_capacity); } // Increase the size of the bit_vector by k 0-bits. void shift_right(unsigned k); void fill0() { memset(m_data, 0, m_capacity * sizeof(unsigned)); } unsigned size() const { return m_num_bits; } bool empty() const { return m_num_bits == 0; } unsigned num_words() const { return num_words(m_num_bits); } unsigned get_word(unsigned word_idx) const { return m_data[word_idx]; } unsigned get_hash() const; bool get(unsigned bit_idx) const { SASSERT(bit_idx < size()); bool r = (get_bit_word(bit_idx) & get_pos_mask(bit_idx)) != 0; return r; } void set(unsigned bit_idx) { SASSERT(bit_idx < size()); get_bit_word(bit_idx) |= get_pos_mask(bit_idx); } void unset(unsigned bit_idx) { SASSERT(bit_idx < size()); get_bit_word(bit_idx) &= ~get_pos_mask(bit_idx); } void set(unsigned bit_idx, bool val) { SASSERT(bit_idx < size()); int _val = static_cast(val); get_bit_word(bit_idx) ^= (-_val ^ get_bit_word(bit_idx)) & get_pos_mask(bit_idx); } void push_back(bool val) { unsigned idx = m_num_bits; m_num_bits++; if (num_words(m_num_bits) > m_capacity) { expand(); } set(idx, val); } void pop_back() { SASSERT(m_num_bits > 0); m_num_bits--; } bool back() const { SASSERT(!empty()); bool r = get(m_num_bits - 1); return r; } void shrink(unsigned new_size) { SASSERT(new_size <= m_num_bits); m_num_bits = new_size; } void resize(unsigned new_size, bool val = false); void reserve(unsigned sz, bool val = false) { if (sz > size()) resize(sz, val); } bool operator==(bit_vector const & other) const; bool operator!=(bit_vector const & other) const { return !operator==(other); } bit_vector & operator=(bit_vector const & source) { m_num_bits = source.m_num_bits; if (!source.m_data) return *this; if (m_capacity < source.m_capacity) { dealloc_svect(m_data); m_data = alloc_svect(unsigned, source.m_capacity); m_capacity = source.m_capacity; } memcpy(m_data, source.m_data, source.m_capacity * sizeof(unsigned)); return *this; } bit_vector & operator|=(bit_vector const & source); bit_vector & operator&=(bit_vector const & source); bit_vector & neg(); void display(std::ostream & out) const; bool contains(const bit_vector & other) const; }; inline std::ostream & operator<<(std::ostream & out, bit_vector const & b) { b.display(out); return out; } /** \brief Bitvector class with fast reset. This class should be used if the reset is frequently called. */ class fr_bit_vector : private bit_vector { unsigned_vector m_one_idxs; public: void reset(); void fill0() { bit_vector::fill0(); m_one_idxs.reset(); } void set(unsigned idx) { m_one_idxs.push_back(idx); bit_vector::set(idx); } void set(unsigned idx, bool val) { if (val) m_one_idxs.push_back(idx); bit_vector::set(idx, val); } void push_back(bool val) { if (val) m_one_idxs.push_back(size()); bit_vector::push_back(val); } }; #endif /* BIT_VECTOR_H_ */ z3-z3-4.8.7/src/util/buffer.h000066400000000000000000000013331356505360400156150ustar00rootroot00000000000000/*++ Copyright (c) 2019 Microsoft Corporation Module Name: buffer.h Author: Daniel Schemmel 2019-2-23 --*/ #ifndef BUFFER_H_ #define BUFFER_H_ #include "old_buffer.h" template using buffer = old_buffer; // note that the append added in the old_ptr_buffer is actually not an addition over its base class old_buffer, // which already has an append function with the same signature and implementation template using ptr_buffer = old_ptr_buffer; template using sbuffer = old_sbuffer; #endif /* BUFFER_H_ */ z3-z3-4.8.7/src/util/cancel_eh.h000066400000000000000000000016431356505360400162510ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: cancel_eh.h Abstract: Template for implementing simple event handler that just invokes cancel method. Author: Leonardo de Moura (leonardo) 2011-04-27. Revision History: --*/ #ifndef CANCEL_EH_H_ #define CANCEL_EH_H_ #include "util/event_handler.h" /** \brief Generic event handler for invoking cancel method. */ template class cancel_eh : public event_handler { bool m_canceled; T & m_obj; public: cancel_eh(T & o): m_canceled(false), m_obj(o) {} ~cancel_eh() override { if (m_canceled) m_obj.dec_cancel(); } void operator()(event_handler_caller_t caller_id) override { if (!m_canceled) { m_caller_id = caller_id; m_canceled = true; m_obj.inc_cancel(); } } bool canceled() const { return m_canceled; } void reset() { m_canceled = false; } }; #endif z3-z3-4.8.7/src/util/chashtable.h000066400000000000000000000516151356505360400164520ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: chashtable.h Abstract: Hashtable with chaining. The performance of the hashtable in hashtable.h deteriorates if there is a huge number of deletions. In this case, the hashtable starts to contain many cells marked as deleted, and insertion/deletion start to suffer. The hashtable defined in this class addresses this problem by using chaining. Of course, there is the cost of storing the link to the next cell. Author: Leonardo de Moura (leonardo) 2011-04-14. Revision History: --*/ #ifndef CHASHTABLE_H_ #define CHASHTABLE_H_ #include "util/memory_manager.h" #include "util/debug.h" #include "util/trace.h" #include "util/tptr.h" #include "util/util.h" #ifdef Z3DEBUG #include "util/hashtable.h" #endif #define CH_STATISTICS #ifdef CH_STATISTICS #define CHS_CODE(CODE) { CODE } #else #define CHS_CODE(CODE) #endif template class chashtable : private HashProc, private EqProc { public: static const unsigned default_init_slots = 8; static const unsigned default_init_cellar = 2; protected: struct cell { cell * m_next; T m_data; cell():m_next(reinterpret_cast(1)) {} bool is_free() const { return GET_TAG(m_next) == 1; } void mark_free() { m_next = TAG(cell*, m_next, 1); } void unmark_free() { m_next = UNTAG(cell*, m_next); } }; cell * m_table; // array of cells. unsigned m_capacity; // size of the array of cells. unsigned m_init_slots; unsigned m_init_cellar; unsigned m_slots; // m_slots < m_capacity, and m_slots is a power of two, the cells [m_slots, m_capacity) are used for chaining. unsigned m_used_slots; // m_used_slots <= m_slots (number of used slots). unsigned m_size; // number of occupied cells. #ifdef CH_STATISTICS unsigned m_collisions; #endif cell * m_next_cell; cell * m_free_cell; cell * m_tofree_cell; unsigned get_hash(T const & d) const { return HashProc::operator()(d); } bool equals(T const & e1, T const & e2) const { return EqProc::operator()(e1, e2); } static cell * alloc_table(unsigned sz) { return alloc_vect(sz); } void delete_table() { dealloc_vect(m_table, m_capacity); } // Return the next free cell in the cellar, and the number of used slots // Return 0 if the cellar is too small (unlikely but it might happen with a bad hash) cell * copy_table(cell * source, unsigned source_slots, unsigned source_capacity, cell * target, unsigned target_slots, unsigned target_capacity, unsigned & used_slots) { TRACE("chashtable", tout << "copy_table...\n";); SASSERT(target_slots >= source_slots); SASSERT(target_capacity >= source_capacity); unsigned target_mask = target_slots - 1; used_slots = 0; cell * source_end = source + source_slots; cell * target_cellar = target + target_slots; cell * target_end = target + target_capacity; for (cell * source_it = source; source_it != source_end; ++source_it) { if (!source_it->is_free()) { cell * list_it = source_it; do { unsigned h = get_hash(list_it->m_data); unsigned idx = h & target_mask; cell * target_it = target + idx; SASSERT(target_it >= target); SASSERT(target_it < target + target_slots); if (target_it->is_free()) { target_it->m_data = list_it->m_data; target_it->m_next = nullptr; used_slots++; } else { SASSERT((get_hash(target_it->m_data) & target_mask) == idx); if (target_cellar == target_end) return nullptr; // the cellar is too small... SASSERT(target_cellar >= target + target_slots); SASSERT(target_cellar < target_end); *target_cellar = *target_it; target_it->m_data = list_it->m_data; target_it->m_next = target_cellar; target_cellar++; } SASSERT(!target_it->is_free()); list_it = list_it->m_next; } while (list_it != nullptr); } } #if 0 TRACE("chashtable", for (unsigned i = 0; i < source_capacity; i++) { tout << i << ":["; if (source[i].m_next == 0) tout << "null"; else if (source[i].m_next == reinterpret_cast(1)) tout << "X"; else tout << (source[i].m_next - source); tout << ", " << source[i].m_data << "]\n"; } tout << "\n"; for (unsigned i = 0; i < target_capacity; i++) { tout << i << ":["; if (target[i].m_next == 0) tout << "null"; else if (target[i].m_next == reinterpret_cast(1)) tout << "X"; else tout << (target[i].m_next - target); tout << ", " << target[i].m_data << "]\n"; } tout << "\n";); #endif return target_cellar; } void expand_table() { unsigned curr_cellar = (m_capacity - m_slots); unsigned new_slots = m_slots * 2; unsigned new_cellar = curr_cellar * 2; while (true) { unsigned new_capacity = new_slots + new_cellar; cell * new_table = alloc_table(new_capacity); cell * next_cell = copy_table(m_table, m_slots, m_capacity, new_table, new_slots, new_capacity, m_used_slots); if (next_cell != nullptr) { delete_table(); m_table = new_table; m_capacity = new_capacity; m_slots = new_slots; m_next_cell = next_cell; m_free_cell = nullptr; m_tofree_cell = nullptr; CASSERT("chashtable", check_invariant()); return; } dealloc_vect(new_table, new_capacity); new_cellar *= 2; } } bool has_free_cells() const { return m_free_cell != nullptr || m_next_cell < m_table + m_capacity; } cell * get_free_cell() { if (m_free_cell != nullptr) { cell * c = m_free_cell; m_free_cell = c->m_next; return c; } else { cell * c = m_next_cell; m_next_cell++; return c; } } void recycle_cell(cell * c) { // c is in the cellar SASSERT(c >= m_table + m_slots); SASSERT(c < m_table + m_capacity); c->m_next = m_free_cell; m_free_cell = c; } void push_recycle_cell(cell* c) { SASSERT(c < m_table + m_capacity); c->m_next = m_tofree_cell; m_tofree_cell = c; } void init(unsigned slots, unsigned cellar) { m_capacity = slots + cellar; m_table = alloc_table(m_capacity); m_slots = slots; m_used_slots = 0; m_size = 0; m_next_cell = m_table + slots; m_free_cell = nullptr; m_tofree_cell = nullptr; } public: chashtable(HashProc const & h = HashProc(), EqProc const & e = EqProc(), unsigned init_slots = default_init_slots, unsigned init_cellar = default_init_cellar): HashProc(h), EqProc(e) { SASSERT(is_power_of_two(init_slots)); SASSERT(init_cellar > 0); m_init_slots = init_slots; m_init_cellar = init_cellar; init(m_init_slots, m_init_cellar); CHS_CODE(m_collisions = 0;); } ~chashtable() { #if 0 cell * it = m_table; cell * end = m_table + m_slots; verbose_stream() << "[chashtable] free slots: "; for (; it != end; ++it) { if (it->is_free()) verbose_stream() << (it - m_table) << " "; } verbose_stream() << "\n"; #endif delete_table(); } void reset() { if (m_size == 0) return; finalize(); } void finalize() { delete_table(); init(m_init_slots, m_init_cellar); } bool empty() const { return m_size == 0; } unsigned size() const { return m_size; } unsigned capacity() const { return m_capacity; } unsigned used_slots() const { return m_used_slots; } void insert_fresh(T const& d) { if (!has_free_cells()) { expand_table(); } unsigned mask = m_slots - 1; unsigned h = get_hash(d); unsigned idx = h & mask; cell * c = m_table + idx; cell * new_c = nullptr; m_size++; if (c->is_free()) { m_used_slots++; } else { new_c = get_free_cell(); *new_c = *c; } c->m_next = new_c; c->m_data = d; CASSERT("chashtable_bug", check_invariant()); } void insert(T const & d) { if (!has_free_cells()) expand_table(); unsigned mask = m_slots - 1; unsigned h = get_hash(d); unsigned idx = h & mask; cell * c = m_table + idx; if (c->is_free()) { m_size++; m_used_slots++; c->m_data = d; c->m_next = nullptr; CASSERT("chashtable_bug", check_invariant()); return; } else { cell * it = c; do { if (equals(it->m_data, d)) { // already there it->m_data = d; CASSERT("chashtable_bug", check_invariant()); return; } CHS_CODE(m_collisions++;); it = it->m_next; } while (it != nullptr); // d is not in the table. m_size++; cell * new_c = get_free_cell(); *new_c = *c; c->m_data = d; c->m_next = new_c; CASSERT("chashtable_bug", check_invariant()); return; } } T & insert_if_not_there(T const & d) { if (!has_free_cells()) expand_table(); unsigned mask = m_slots - 1; unsigned h = get_hash(d); unsigned idx = h & mask; cell * c = m_table + idx; if (c->is_free()) { m_size++; m_used_slots++; c->m_data = d; c->m_next = nullptr; CASSERT("chashtable_bug", check_invariant()); return c->m_data; } else { cell * it = c; do { if (equals(it->m_data, d)) { // already there CASSERT("chashtable_bug", check_invariant()); return it->m_data; } CHS_CODE(m_collisions++;); it = it->m_next; } while (it != nullptr); // d is not in the table. m_size++; cell * new_c = get_free_cell(); *new_c = *c; c->m_data = d; c->m_next = new_c; CASSERT("chashtable_bug", check_invariant()); return c->m_data; } } bool insert_if_not_there2(T const & d) { if (!has_free_cells()) expand_table(); unsigned mask = m_slots - 1; unsigned h = get_hash(d); unsigned idx = h & mask; cell * c = m_table + idx; if (c->is_free()) { m_size++; m_used_slots++; c->m_data = d; c->m_next = nullptr; CASSERT("chashtable_bug", check_invariant()); return true; } else { cell * it = c; do { if (equals(it->m_data, d)) { // already there CASSERT("chashtable_bug", check_invariant()); return false; } CHS_CODE(m_collisions++;); it = it->m_next; } while (it != nullptr); // d is not in the table. m_size++; cell * new_c = get_free_cell(); *new_c = *c; c->m_data = d; c->m_next = new_c; CASSERT("chashtable_bug", check_invariant()); return true; } } bool contains(T const & d) const { unsigned mask = m_slots - 1; unsigned h = get_hash(d); unsigned idx = h & mask; cell * c = m_table + idx; if (c->is_free()) return false; do { if (equals(c->m_data, d)) { return true; } CHS_CODE(const_cast(this)->m_collisions++;); c = c->m_next; } while (c != nullptr); return false; } T * find_core(T const & d) const { unsigned mask = m_slots - 1; unsigned h = get_hash(d); unsigned idx = h & mask; cell * c = m_table + idx; if (c->is_free()) return nullptr; do { if (equals(c->m_data, d)) { return &(c->m_data); } CHS_CODE(const_cast(this)->m_collisions++;); c = c->m_next; } while (c != nullptr); return nullptr; } bool find(T const & d, T & r) { unsigned mask = m_slots - 1; unsigned h = get_hash(d); unsigned idx = h & mask; cell * c = m_table + idx; if (c->is_free()) return false; do { if (equals(c->m_data, d)) { r = c->m_data; return true; } CHS_CODE(const_cast(this)->m_collisions++;); c = c->m_next; } while (c != nullptr); return false; } void erase(T const & d) { unsigned mask = m_slots - 1; unsigned h = get_hash(d); unsigned idx = h & mask; cell * c = m_table + idx; if (c->is_free()) return; cell * prev = nullptr; do { if (equals(c->m_data, d)) { m_size--; if (prev == nullptr) { cell * next = c->m_next; if (next == nullptr) { m_used_slots--; c->mark_free(); SASSERT(c->is_free()); } else { *c = *next; recycle_cell(next); } } else { prev->m_next = c->m_next; recycle_cell(c); } CASSERT("chashtable_bug", check_invariant()); return; } CHS_CODE(m_collisions++;); prev = c; c = c->m_next; } while (c != nullptr); } class iterator { cell * m_it; cell * m_end; cell * m_list_it; void move_to_used() { while (m_it != m_end) { if (!m_it->is_free()) { m_list_it = m_it; return; } m_it++; } m_list_it = nullptr; } public: iterator(cell * start, cell * end): m_it(start), m_end(end) { move_to_used(); } iterator():m_it(nullptr), m_end(nullptr), m_list_it(nullptr) {} T & operator*() { return m_list_it->m_data; } T const & operator*() const { return m_list_it->m_data; } T const * operator->() const { return &(operator*()); } T * operator->() { return &(operator*()); } iterator & operator++() { m_list_it = m_list_it->m_next; if (m_list_it == nullptr) { m_it++; move_to_used(); } return *this; } iterator operator++(int) { iterator tmp = *this; ++*this; return tmp; } bool operator==(iterator const & it) const { return m_list_it == it.m_list_it; } bool operator!=(iterator const & it) const { return m_list_it != it.m_list_it; } }; iterator begin() const { return iterator(m_table, m_table + m_slots); } iterator end() const { return iterator(); } void swap(chashtable & other) { std::swap(m_table, other.m_table); std::swap(m_capacity, other.m_capacity); std::swap(m_init_slots, other.m_init_slots); std::swap(m_init_cellar, other.m_init_cellar); std::swap(m_slots, other.m_slots); std::swap(m_used_slots, other.m_used_slots); std::swap(m_size, other.m_size); #ifdef CH_STATISTICS std::swap(m_collisions, other.m_collisions); #endif std::swap(m_next_cell, other.m_next_cell); std::swap(m_free_cell, other.m_free_cell); } unsigned collisions() const { #ifdef CH_STATISTICS return m_collisions; #else return 0; #endif } #ifdef Z3DEBUG bool check_invariant() const { ptr_addr_hashtable visited; unsigned sz = 0; cell * _end = m_table + m_slots; for (cell * it = m_table; it != _end; ++it) { if (!it->is_free()) { cell * list_it = it; while (list_it != 0) { sz++; SASSERT(!visited.contains(list_it)); visited.insert(list_it); list_it = list_it->m_next; } } } SASSERT(m_size == sz); return true; } #endif }; template class cmap { public: struct key_value { Key m_key; Value m_value; key_value() {} key_value(Key const & k):m_key(k) {} key_value(Key const & k, Value const & v):m_key(k), m_value(v) {} }; protected: struct key_value_hash_proc : private HashProc { key_value_hash_proc(HashProc const & p):HashProc(p) {} unsigned operator()(key_value const & d) const { return HashProc::operator()(d.m_key); } }; struct key_value_eq_proc : private EqProc { key_value_eq_proc(EqProc const & p):EqProc(p) {} bool operator()(key_value const & d1, key_value const & d2) const { return EqProc::operator()(d1.m_key, d2.m_key); } }; typedef chashtable table; table m_table; public: cmap(HashProc const & h = HashProc(), EqProc const & e = EqProc(), unsigned init_slots = table::default_init_slots, unsigned init_cellar = table::default_init_cellar): m_table(key_value_hash_proc(h), key_value_eq_proc(e), init_slots, init_cellar) { } typedef typename table::iterator iterator; void reset() { m_table.reset(); } void finalize() { m_table.finalize(); } bool empty() const { return m_table.empty(); } unsigned size() const { return m_table.size(); } unsigned capacity() const { return m_table.capacity(); } unsigned used_slots() const { return m_table.used_slots(); } unsigned collisions() const { return m_table.collisions(); } iterator begin() const { return m_table.begin(); } iterator end() const { return m_table.end(); } void insert(Key const & k, Value const & v) { return m_table.insert(key_value(k, v)); } key_value & insert_if_not_there(Key const & k, Value const & v) { return m_table.insert_if_not_there(key_value(k, v)); } bool contains(Key const & k) const { return m_table.contains(key_value(k)); } key_value * find_core(Key const & k) const { return m_table.find_core(key_value(k)); } bool find(Key const & k, Value & v) const { key_value * e = m_table.find_core(key_value(k)); if (e == nullptr) return false; v = e->m_value; return true; } void erase(Key const & k) { m_table.erase(key_value(k)); } }; #endif z3-z3-4.8.7/src/util/checked_int64.h000066400000000000000000000147771356505360400167760ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: checked_int64.h Abstract: A class for wrapping checked (and unchecked) int64_t operations. Note: the mpfx class defines a more general class of fixed-point operations. A tradeoff is that it relies on a manager. This class several of the most common operations from rational, so it can be swapped for rational. Author: Nikolaj Bjorner (nbjorner) 2013-03-25. Revision History: --*/ #ifndef CHECKED_INT64_H_ #define CHECKED_INT64_H_ #include "util/z3_exception.h" #include "util/rational.h" template class checked_int64 { int64_t m_value; typedef checked_int64 ci; rational r64(int64_t i) { return rational(i, rational::i64()); } public: checked_int64(): m_value(0) {} checked_int64(int64_t v): m_value(v) {} checked_int64(checked_int64 const& other) { m_value = other.m_value; } class overflow_exception : public z3_exception { char const * msg() const override { return "checked_int64 overflow/underflow";} }; bool is_zero() const { return m_value == 0; } bool is_pos() const { return m_value > 0; } bool is_neg() const { return m_value < 0; } bool is_one() const { return m_value == 1; } bool is_minus_one() const { return m_value == -1; } bool is_nonneg() const { return m_value >= 0; } bool is_nonpos() const { return m_value <= 0; } bool is_even() const { return 0 == (m_value ^ 0x1); } static checked_int64 zero() { return ci(0); } static checked_int64 one() { return ci(1); } static checked_int64 minus_one() { return ci(-1);} int64_t get_int64() const { return m_value; } checked_int64 abs() const { if (m_value >= 0) { return *this; } if (CHECK && m_value == INT64_MIN) { throw overflow_exception(); } return ci(-m_value); } checked_int64& neg() { if (CHECK && m_value == INT64_MIN) { throw overflow_exception(); } m_value = -m_value; return *this; } unsigned hash() const { return static_cast(m_value); } struct hash_proc { unsigned operator()(checked_int64 const& r) const { return r.hash(); } }; struct eq_proc { bool operator()(checked_int64 const& r1, checked_int64 const& r2) const { return r1 == r2; } }; friend inline std::ostream& operator<<(std::ostream& out, checked_int64 const& i) { return out << i.m_value; } friend inline bool operator==(checked_int64 const& a, checked_int64 const& b) { return a.m_value == b.m_value; } friend inline bool operator<(checked_int64 const& a, checked_int64 const& b) { return a.m_value < b.m_value; } checked_int64 & operator++() { if (CHECK && INT64_MAX == m_value) { throw overflow_exception(); } ++m_value; return *this; } const checked_int64 operator++(int) { checked_int64 tmp(*this); ++(*this); return tmp; } checked_int64 & operator--() { if (CHECK && m_value == INT64_MIN) { throw overflow_exception(); } --m_value; return *this; } const checked_int64 operator--(int) { checked_int64 tmp(*this); --(*this); return tmp; } checked_int64& operator+=(checked_int64 const& other) { if (CHECK) { uint64_t x = static_cast(m_value); uint64_t y = static_cast(other.m_value); int64_t r = static_cast(x + y); if (m_value > 0 && other.m_value > 0 && r <= 0) throw overflow_exception(); if (m_value < 0 && other.m_value < 0 && r >= 0) throw overflow_exception(); m_value = r; } else { m_value += other.m_value; } return *this; } checked_int64& operator-=(checked_int64 const& other) { if (CHECK) { uint64_t x = static_cast(m_value); uint64_t y = static_cast(other.m_value); int64_t r = static_cast(x - y); if (m_value > 0 && other.m_value < 0 && r <= 0) throw overflow_exception(); if (m_value < 0 && other.m_value > 0 && r >= 0) throw overflow_exception(); m_value = r; } else { m_value -= other.m_value; } return *this; } checked_int64& operator*=(checked_int64 const& other) { if (CHECK) { if (INT_MIN < m_value && m_value <= INT_MAX && INT_MIN < other.m_value && other.m_value <= INT_MAX) { m_value *= other.m_value; } // TBD: could be tuned by using known techniques or 128-bit arithmetic. else { rational r(r64(m_value) * r64(other.m_value)); if (!r.is_int64()) { throw overflow_exception(); } m_value = r.get_int64(); } } else { m_value *= other.m_value; } return *this; } friend inline checked_int64 abs(checked_int64 const& i) { return i.abs(); } }; template inline bool operator!=(checked_int64 const & i1, checked_int64 const & i2) { return !operator==(i1, i2); } template inline bool operator>(checked_int64 const & i1, checked_int64 const & i2) { return operator<(i2, i1); } template inline bool operator<=(checked_int64 const & i1, checked_int64 const & i2) { return !operator>(i1, i2); } template inline bool operator>=(checked_int64 const & i1, checked_int64 const & i2) { return !operator<(i1, i2); } template inline checked_int64 operator-(checked_int64 const& i) { checked_int64 result(i); return result.neg(); } template inline checked_int64 operator+(checked_int64 const& a, checked_int64 const& b) { checked_int64 result(a); result += b; return result; } template inline checked_int64 operator-(checked_int64 const& a, checked_int64 const& b) { checked_int64 result(a); result -= b; return result; } template inline checked_int64 operator*(checked_int64 const& a, checked_int64 const& b) { checked_int64 result(a); result *= b; return result; } #endif z3-z3-4.8.7/src/util/cmd_context_types.cpp000066400000000000000000000024551356505360400204400ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: cmd_context_types.h Abstract: Author: Leonardo (leonardo) 2011-04-22 Notes: --*/ #include #include "util/cmd_context_types.h" std::ostream & operator<<(std::ostream & out, cmd_arg_kind k) { switch (k) { case CPK_UINT: out << "unsigned int"; break; case CPK_BOOL: out << "bool"; break; case CPK_DOUBLE: out << "double"; break; case CPK_NUMERAL: out << "rational"; break; case CPK_DECIMAL: out << "rational"; break; case CPK_STRING: out << "string"; break; case CPK_OPTION_VALUE: out << "optional-value"; break; case CPK_KEYWORD: out << "keyword"; break; case CPK_SYMBOL: out << "symbol"; break; case CPK_SYMBOL_LIST: out << "symbol-list"; break; case CPK_SORT: out << "sort"; break; case CPK_SORT_LIST: out << "sort-list"; break; case CPK_EXPR: out << "expression"; break; case CPK_EXPR_LIST: out << "expression-list"; break; case CPK_FUNC_DECL: out << "declaration"; break; case CPK_FUNC_DECL_LIST: out << "declaration-list"; break; case CPK_SORTED_VAR: out << "sorted-variable"; break; case CPK_SORTED_VAR_LIST: out << "sorted-variable-list"; break; case CPK_SEXPR: out << "s-expression"; break; default: out << "unknown"; break; } return out; } z3-z3-4.8.7/src/util/cmd_context_types.h000066400000000000000000000104471356505360400201050ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: cmd_context_types.h Abstract: Author: Leonardo (leonardo) 2011-04-22 Notes: --*/ #ifndef CMD_CONTEXT_TYPES_H_ #define CMD_CONTEXT_TYPES_H_ #include "util/symbol.h" #include "util/z3_exception.h" #include class rational; class expr; class sort; class func_decl; class sexpr; class cmd_context; enum cmd_arg_kind { CPK_UINT, CPK_BOOL, CPK_DOUBLE, CPK_NUMERAL, CPK_DECIMAL, CPK_STRING, CPK_OPTION_VALUE, CPK_KEYWORD, CPK_SYMBOL, CPK_SYMBOL_LIST, CPK_SORT, CPK_SORT_LIST, CPK_EXPR, CPK_EXPR_LIST, CPK_FUNC_DECL, CPK_FUNC_DECL_LIST, CPK_SORTED_VAR, CPK_SORTED_VAR_LIST, CPK_SEXPR, CPK_INVALID }; std::ostream & operator<<(std::ostream & out, cmd_arg_kind k); typedef cmd_arg_kind param_kind; class cmd_exception : public default_exception { int m_line; int m_pos; std::string compose(char const* msg, symbol const& s) { std::stringstream stm; stm << msg << s; return stm.str(); } public: cmd_exception(char const * msg):default_exception(msg), m_line(-1), m_pos(-1) {} cmd_exception(std::string && msg):default_exception(std::move(msg)), m_line(-1), m_pos(-1) {} cmd_exception(std::string && msg, int line, int pos):default_exception(std::move(msg)), m_line(line), m_pos(pos) {} cmd_exception(char const * msg, symbol const & s): default_exception(compose(msg,s)),m_line(-1),m_pos(-1) {} cmd_exception(char const * msg, symbol const & s, int line, int pos): default_exception(compose(msg,s)),m_line(line),m_pos(pos) {} bool has_pos() const { return m_line >= 0; } int line() const { SASSERT(has_pos()); return m_line; } int pos() const { SASSERT(has_pos()); return m_pos; } }; class stop_parser_exception { }; typedef std::pair sorted_var; // A command may have a variable number of arguments. #define VAR_ARITY UINT_MAX /** \brief Command abstract class. Commands may have variable number of argumets. */ class cmd { symbol m_name; protected: int m_line; int m_pos; public: cmd(char const * n):m_name(n), m_line(0), m_pos(0) {} virtual ~cmd() {} virtual void reset(cmd_context & ctx) {} virtual void finalize(cmd_context & ctx) {} virtual symbol get_name() const { return m_name; } virtual char const * get_usage() const { return nullptr; } virtual char const * get_descr(cmd_context & ctx) const { return nullptr; } virtual unsigned get_arity() const { return 0; } // command invocation void set_line_pos(int line, int pos) { m_line = line; m_pos = pos; } virtual void prepare(cmd_context & ctx) {} virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { UNREACHABLE(); return CPK_UINT; } virtual void set_next_arg(cmd_context & ctx, unsigned val) { UNREACHABLE(); } virtual void set_next_arg(cmd_context & ctx, bool val) { UNREACHABLE(); } virtual void set_next_arg(cmd_context & ctx, rational const & val) { UNREACHABLE(); } virtual void set_next_arg(cmd_context & ctx, double val) { UNREACHABLE(); } virtual void set_next_arg(cmd_context & ctx, char const * val) { UNREACHABLE(); } virtual void set_next_arg(cmd_context & ctx, symbol const & s) { UNREACHABLE(); } virtual void set_next_arg(cmd_context & ctx, unsigned num, symbol const * slist) { UNREACHABLE(); } virtual void set_next_arg(cmd_context & ctx, sort * s) { UNREACHABLE(); } virtual void set_next_arg(cmd_context & ctx, unsigned num, sort * const * slist) { UNREACHABLE(); } virtual void set_next_arg(cmd_context & ctx, expr * t) { UNREACHABLE(); } virtual void set_next_arg(cmd_context & ctx, unsigned num, expr * const * tlist) { UNREACHABLE(); } virtual void set_next_arg(cmd_context & ctx, sorted_var const & sv) { UNREACHABLE(); } virtual void set_next_arg(cmd_context & ctx, unsigned num, sorted_var const * svlist) { UNREACHABLE(); } virtual void set_next_arg(cmd_context & ctx, func_decl * f) { UNREACHABLE(); } virtual void set_next_arg(cmd_context & ctx, unsigned num, func_decl * const * flist) { UNREACHABLE(); } virtual void set_next_arg(cmd_context & ctx, sexpr * n) { UNREACHABLE(); } virtual void failure_cleanup(cmd_context & ctx) {} virtual void execute(cmd_context & ctx) {} }; #endif z3-z3-4.8.7/src/util/common_msgs.cpp000066400000000000000000000013161356505360400172210ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: common_msgs.cpp Abstract: Common messages used in Z3. Author: Leonardo (leonardo) 2012-10-25 Notes: --*/ #include "util/common_msgs.h" char const * common_msgs::g_canceled_msg = "canceled"; char const * common_msgs::g_max_memory_msg = "max. memory exceeded"; char const * common_msgs::g_max_scopes_msg = "max. scopes exceeded"; char const * common_msgs::g_max_steps_msg = "max. steps exceeded"; char const * common_msgs::g_max_frames_msg = "max. frames exceeded"; char const * common_msgs::g_no_proofs_msg = "component does not support proof generation"; char const * common_msgs::g_max_resource_msg = "max. resource limit exceeded"; z3-z3-4.8.7/src/util/common_msgs.h000066400000000000000000000017141356505360400166700ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: common_msgs.h Abstract: Common messages used in Z3. Author: Leonardo (leonardo) 2012-10-25 Notes: --*/ #ifndef COMMON_MSGS_H_ #define COMMON_MSGS_H_ class common_msgs { public: static char const * g_canceled_msg; static char const * g_max_memory_msg; static char const * g_max_scopes_msg; static char const * g_max_steps_msg; static char const * g_max_frames_msg; static char const * g_no_proofs_msg; static char const * g_max_resource_msg; }; #define Z3_CANCELED_MSG common_msgs::g_canceled_msg #define Z3_MAX_MEMORY_MSG common_msgs::g_max_memory_msg #define Z3_MAX_SCOPES_MSG common_msgs::g_max_scopes_msg #define Z3_MAX_STEPS_MSG common_msgs::g_max_steps_msg #define Z3_MAX_FRAMES_MSG common_msgs::g_max_frames_msg #define Z3_NO_PROOF_GEN_MSG common_msgs::g_no_proofs_msg #define Z3_MAX_RESOURCE_MSG common_msgs::g_max_resource_msg #endif z3-z3-4.8.7/src/util/container_util.h000066400000000000000000000053371356505360400173730ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: container_util.h Abstract: Useful functions for containers Author: Krystof Hoder, Nikolaj Bjorner 2017-10-24 Revision History: Extracted from dl_util.h --*/ #ifndef CONTAINER_UTIL_H_ #define CONTAINER_UTIL_H_ // ----------------------------------- // // container functions // // ----------------------------------- template void set_intersection(Set1 & tgt, const Set2 & src) { svector to_remove; for (auto const& itm : tgt) if (!src.contains(itm)) to_remove.push_back(itm); while (!to_remove.empty()) { tgt.remove(to_remove.back()); to_remove.pop_back(); } } template void set_difference(Set & tgt, const Set & to_remove) { for (auto const& itm : to_remove) tgt.remove(itm); } template void set_union(Set1 & tgt, const Set2 & to_add) { for (auto const& itm : to_add) tgt.insert(itm); } template void unite_disjoint_maps(T & tgt, const T & src) { for (auto const& kv : src) { SASSERT(!tgt.contains(kv.m_key)); tgt.insert(kv.m_key, kv.m_value); } } template void collect_map_range(T & acc, const U & map) { for (auto const& kv : map) acc.push_back(kv.m_value); } template void print_container(const T & begin, const T & end, std::ostream & out) { T it = begin; out << "("; bool first = true; for(; it!=end; ++it) { if(first) { first = false; } else { out << ","; } out << (*it); } out << ")"; } template void print_container(const T & cont, std::ostream & out) { print_container(cont.begin(), cont.end(), out); } template void print_container(const ref_vector & cont, std::ostream & out) { print_container(cont.c_ptr(), cont.c_ptr() + cont.size(), out); } template void print_map(const T & cont, std::ostream & out) { out << "("; bool first = true; for (auto const& kv : cont) { if (first) { first = false; } else { out << ","; } out << kv.m_key << "->" << kv.m_value; } out << ")"; } template unsigned find_index(const It & begin, const It & end, const V & val) { It it = begin; for (unsigned idx = 0; it != end; it++, idx++) { if (*it == val) { return idx; } } return UINT_MAX; } template bool containers_equal(const T & begin1, const T & end1, const U & begin2, const U & end2) { T it1 = begin1; U it2 = begin2; for (; it1 != end1 && it2 != end2 && *it1 == *it2; ++it1, ++it2) {}; return it1 == end1 && it2 == end2; } #endif z3-z3-4.8.7/src/util/debug.cpp000066400000000000000000000051541356505360400157720ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: debug.cpp Abstract: Basic debugging support. Author: Leonardo de Moura (leonardo) 2006-09-11. Revision History: --*/ #include #ifndef _WINDOWS #include #endif #include #include "util/str_hashtable.h" #include "util/z3_exception.h" static volatile bool g_enable_assertions = true; void enable_assertions(bool f) { g_enable_assertions = f; } bool assertions_enabled() { return g_enable_assertions; } void notify_assertion_violation(const char * fileName, int line, const char * condition) { std::cerr << "ASSERTION VIOLATION\n"; std::cerr << "File: " << fileName << "\n"; std::cerr << "Line: " << line << "\n"; std::cerr << condition << "\n"; } static str_hashtable* g_enabled_debug_tags = nullptr; static void init_debug_table() { if (!g_enabled_debug_tags) { g_enabled_debug_tags = alloc(str_hashtable); } } void finalize_debug() { dealloc(g_enabled_debug_tags); g_enabled_debug_tags = nullptr; } void enable_debug(const char * tag) { init_debug_table(); g_enabled_debug_tags->insert(tag); } void disable_debug(const char * tag) { init_debug_table(); g_enabled_debug_tags->erase(tag); } bool is_debug_enabled(const char * tag) { init_debug_table(); return g_enabled_debug_tags->contains(tag); } #if !defined(_WINDOWS) && !defined(NO_Z3_DEBUGGER) void invoke_gdb() { char buffer[1024]; int * x = nullptr; for (;;) { std::cerr << "(C)ontinue, (A)bort, (S)top, (T)hrow exception, Invoke (G)DB\n"; char result; bool ok = bool(std::cin >> result); if (!ok) exit(ERR_INTERNAL_FATAL); // happens if std::cin is eof or unattached. switch(result) { case 'C': case 'c': return; case 'A': case 'a': exit(1); case 'S': case 's': // force seg fault... *x = 0; return; case 't': case 'T': throw default_exception("assertion violation"); case 'G': case 'g': sprintf(buffer, "gdb -nw /proc/%d/exe %d", getpid(), getpid()); std::cerr << "invoking GDB...\n"; if (system(buffer) == 0) { std::cerr << "continuing the execution...\n"; } else { std::cerr << "error starting GDB...\n"; // forcing seg fault. int * x = nullptr; *x = 0; } return; default: std::cerr << "INVALID COMMAND\n"; } } } #endif z3-z3-4.8.7/src/util/debug.h000066400000000000000000000057451356505360400154450ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: debug.h Abstract: Basic debugging support. Author: Leonardo de Moura (leonardo) 2006-09-11. Revision History: --*/ #ifndef DEBUG_H_ #define DEBUG_H_ #include void enable_assertions(bool f); bool assertions_enabled(); #if 0 #define _CRTDBG_MAP_ALLOC #include #include #include #endif #ifndef __has_builtin # define __has_builtin(x) 0 #endif #include "util/error_codes.h" #include "util/warning.h" #ifdef Z3DEBUG #define DEBUG_CODE(CODE) { CODE } ((void) 0) #else #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 #ifdef _WINDOWS #define INVOKE_DEBUGGER() __debugbreak() #else void invoke_gdb(); #define INVOKE_DEBUGGER() invoke_gdb() #endif #endif void notify_assertion_violation(const char * file_name, int line, const char * condition); void enable_debug(const char * tag); void disable_debug(const char * tag); bool is_debug_enabled(const char * tag); #define SASSERT(COND) DEBUG_CODE(if (assertions_enabled() && !(COND)) { notify_assertion_violation(__FILE__, __LINE__, #COND); INVOKE_DEBUGGER(); }) #define CASSERT(TAG, COND) DEBUG_CODE(if (assertions_enabled() && is_debug_enabled(TAG) && !(COND)) { notify_assertion_violation(__FILE__, __LINE__, #COND); INVOKE_DEBUGGER(); }) #define XASSERT(COND, EXTRA_CODE) DEBUG_CODE(if (assertions_enabled() && !(COND)) { notify_assertion_violation(__FILE__, __LINE__, #COND); { EXTRA_CODE } INVOKE_DEBUGGER(); }) #ifdef Z3DEBUG # define UNREACHABLE() DEBUG_CODE(notify_assertion_violation(__FILE__, __LINE__, "UNREACHABLE CODE WAS REACHED."); INVOKE_DEBUGGER();) #else #if (defined(__GNUC__) && ((__GNUC__ * 100 + __GNUC_MINOR__) >= 405)) || __has_builtin(__builtin_unreachable) // only available in gcc >= 4.5 and in newer versions of clang # define UNREACHABLE() __builtin_unreachable() #elif defined(_MSC_VER) # define UNREACHABLE() __assume(0) #else #define UNREACHABLE() DEBUG_CODE(notify_assertion_violation(__FILE__, __LINE__, "UNREACHABLE CODE WAS REACHED."); INVOKE_DEBUGGER();) #endif #endif #define NOT_IMPLEMENTED_YET() { std::cerr << "NOT IMPLEMENTED YET!\n"; UNREACHABLE(); exit(ERR_NOT_IMPLEMENTED_YET); } ((void) 0) #define VERIFY(_x_) if (!(_x_)) { \ std::cerr << "Failed to verify: " << #_x_ << "\n"; \ UNREACHABLE(); \ } #define ENSURE(_x_) \ if (!(_x_)) { \ std::cerr << "Failed to verify: " << #_x_ << "\n"; \ exit(-1); \ } void finalize_debug(); /* ADD_FINALIZER('finalize_debug();') */ #endif /* DEBUG_H_ */ z3-z3-4.8.7/src/util/dec_ref_util.h000066400000000000000000000025551356505360400167770ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: map_util.h Abstract: Some goodies for managing reference counters stored in maps. Author: Leonardo (leonardo) 2011-06-07 Notes: --*/ #ifndef MAP_UTIL_H_ #define MAP_UTIL_H_ /** \brief Decrement the reference counter of the keys and values stored in the map, then reset the map. */ template void dec_ref_key_values(Mng & m, Map & map) { typename Map::iterator it = map.begin(); typename Map::iterator end = map.end(); for (; it != end; ++it) { m.dec_ref(it->m_key); m.dec_ref(it->m_value); } map.reset(); } /** \brief Decrement the reference counter of the keys stored in the map, then reset the map. */ template void dec_ref_keys(Mng & m, Map & map) { typename Map::iterator it = map.begin(); typename Map::iterator end = map.end(); for (; it != end; ++it) { m.dec_ref(it->m_key); } map.reset(); } /** \brief Decrement the reference counter of the values stored in the map, then reset the map. */ template void dec_ref_values(Mng & m, Map & map) { typename Map::iterator it = map.begin(); typename Map::iterator end = map.end(); for (; it != end; ++it) { m.dec_ref(it->m_value); } map.reset(); } #endif z3-z3-4.8.7/src/util/dependency.h000066400000000000000000000204421356505360400164640ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dependency.h Abstract: Author: Leonardo de Moura (leonardo) 2008-12-10. Revision History: --*/ #ifndef DEPENDENCY_H_ #define DEPENDENCY_H_ #include "util/vector.h" #include "util/region.h" template class dependency_manager { public: typedef typename C::value value; typedef typename C::value_manager value_manager; typedef typename C::allocator allocator; class dependency { unsigned m_ref_count:30; unsigned m_mark:1; unsigned m_leaf:1; friend class dependency_manager; dependency(bool leaf): m_ref_count(0), m_mark(false), m_leaf(leaf) { } bool is_marked() const { return m_mark == 1; } void mark() { m_mark = true; } void unmark() { m_mark = false; } public: unsigned get_ref_count() const { return m_ref_count; } bool is_leaf() const { return m_leaf == 1; } }; private: struct join : public dependency { dependency * m_children[2]; join(dependency * d1, dependency * d2): dependency(false) { m_children[0] = d1; m_children[1] = d2; } }; struct leaf : public dependency { value m_value; leaf(value const & v): dependency(true), m_value(v) { } }; static join * to_join(dependency * d) { SASSERT(!d->is_leaf()); return static_cast(d); } static leaf * to_leaf(dependency * d) { SASSERT(d->is_leaf()); return static_cast(d); } value_manager & m_vmanager; allocator & m_allocator; ptr_vector m_todo; void inc_ref(value const & v) { if (C::ref_count) m_vmanager.inc_ref(v); } void dec_ref(value const & v) { if (C::ref_count) m_vmanager.dec_ref(v); } void del(dependency * d) { SASSERT(d); m_todo.push_back(d); while (!m_todo.empty()) { d = m_todo.back(); m_todo.pop_back(); if (d->is_leaf()) { dec_ref(to_leaf(d)->m_value); to_leaf(d)->~leaf(); m_allocator.deallocate(sizeof(leaf), to_leaf(d)); } else { for (unsigned i = 0; i < 2; i++) { dependency * c = to_join(d)->m_children[i]; SASSERT(c->m_ref_count > 0); c->m_ref_count--; if (c->m_ref_count == 0) m_todo.push_back(c); } to_join(d)->~join(); m_allocator.deallocate(sizeof(join), to_join(d)); } } } void unmark_todo() { typename ptr_vector::iterator it = m_todo.begin(); typename ptr_vector::iterator end = m_todo.end(); for (; it != end; ++it) { (*it)->unmark(); } m_todo.reset(); } public: dependency_manager(value_manager & m, allocator & a): m_vmanager(m), m_allocator(a) { } void inc_ref(dependency * d) { if (d) d->m_ref_count++; } void dec_ref(dependency * d) { if (d) { SASSERT(d->m_ref_count > 0); d->m_ref_count--; if (d->m_ref_count == 0) del(d); } } dependency * mk_empty() { return nullptr; } dependency * mk_leaf(value const & v) { void * mem = m_allocator.allocate(sizeof(leaf)); inc_ref(v); return new (mem) leaf(v); } dependency * mk_join(dependency * d1, dependency * d2) { if (d1 == nullptr) { return d2; } else if (d2 == nullptr) { return d1; } else if (d1 == d2) { return d1; } else { void * mem = m_allocator.allocate(sizeof(join)); inc_ref(d1); inc_ref(d2); return new (mem) join(d1, d2); } } bool contains(dependency * d, value const & v) { if (d) { m_todo.reset(); d->mark(); m_todo.push_back(d); unsigned qhead = 0; while (qhead < m_todo.size()) { d = m_todo[qhead]; qhead++; if (d->is_leaf()) { if (to_leaf(d)->m_value == v) { unmark_todo(); return true; } } else { for (unsigned i = 0; i < 2; i++) { dependency * child = to_join(d)->m_children[i]; if (!child->is_marked()) { m_todo.push_back(child); child->mark(); } } } } unmark_todo(); } return false; } void linearize(dependency * d, vector & vs) { if (d) { m_todo.reset(); d->mark(); m_todo.push_back(d); unsigned qhead = 0; while (qhead < m_todo.size()) { d = m_todo[qhead]; qhead++; if (d->is_leaf()) { vs.push_back(to_leaf(d)->m_value); } else { for (unsigned i = 0; i < 2; i++) { dependency * child = to_join(d)->m_children[i]; if (!child->is_marked()) { m_todo.push_back(child); child->mark(); } } } } unmark_todo(); } } }; /** \brief Version of the dependency_manager where memory management is scoped (i.e., reference counting is ignored), and push_scope/pop_scope are used instead. Value must be a primitive type such as an integer or pointer. */ template class scoped_dependency_manager { class config { public: static const bool ref_count = true; typedef Value value; class value_manager { public: void inc_ref(value const & v) { } void dec_ref(value const & v) { } }; class allocator { region m_region; public: void * allocate(size_t sz) { return m_region.allocate(sz); } void deallocate(size_t sz, void * mem) { } void push_scope() { m_region.push_scope(); } void pop_scope(unsigned num) { m_region.pop_scope(num); } void reset() { m_region.reset(); } }; }; typedef dependency_manager dep_manager; public: typedef typename dep_manager::dependency dependency; typedef Value value; private: typename config::value_manager m_vmanager; typename config::allocator m_allocator; dep_manager m_dep_manager; public: scoped_dependency_manager(): m_dep_manager(m_vmanager, m_allocator) { } dependency * mk_empty() { return m_dep_manager.mk_empty(); } dependency * mk_leaf(value const & v) { return m_dep_manager.mk_leaf(v); } dependency * mk_join(dependency * d1, dependency * d2) { return m_dep_manager.mk_join(d1, d2); } bool contains(dependency * d, value const & v) { return m_dep_manager.contains(d, v); } void linearize(dependency * d, vector & vs) { return m_dep_manager.linearize(d, vs); } void reset() { m_allocator.reset(); } void push_scope() { m_allocator.push_scope(); } void pop_scope(unsigned num_scopes) { m_allocator.pop_scope(num_scopes); } }; // Implement old dependency manager used by interval and groebner typedef scoped_dependency_manager v_dependency_manager; typedef scoped_dependency_manager::dependency v_dependency; #endif /* DEPENDENCY_H_ */ z3-z3-4.8.7/src/util/dictionary.h000066400000000000000000000005721356505360400165150ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: dictionary.h Abstract: Author: Leonardo (leonardo) 2011-03-01 Notes: --*/ #ifndef DICTIONARY_H_ #define DICTIONARY_H_ #include "util/map.h" #include "util/symbol.h" template class dictionary : public map { public: dictionary() {} }; #endif z3-z3-4.8.7/src/util/dlist.h000066400000000000000000000064261356505360400154730ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: dlist.h Abstract: Templates for manipulating doubly linked lists. Author: Leonardo de Moura (leonardo) 2011-01-25. Revision History: --*/ #ifndef DLIST_H_ #define DLIST_H_ /** Add element \c elem to the list headed by \c head. NextProc and PrevProc must have the methods: T * & operator()(T *); T * & operator()(T *); They should return the next and prev fields of the given object. */ template void dlist_add(T * & head, T * elem, NextProc const & next = NextProc(), PrevProc const & prev = PrevProc()) { SASSERT(prev(elem) == 0); SASSERT(next(elem) == 0); if (head == 0) { head = elem; } else { next(elem) = head; prev(head) = elem; head = elem; } } template void dlist_del(T * & head, T * elem, NextProc const & next = NextProc(), PrevProc const & prev = PrevProc()) { T * & prev_elem = prev(elem); T * & next_elem = next(elem); if (head == elem) { SASSERT(prev_elem == 0); if (next_elem != 0) prev(next_elem) = 0; head = next_elem; } else { SASSERT(prev_elem != 0); next(prev_elem) = next_elem; if (next_elem != 0) prev(next_elem) = prev_elem; } prev_elem = 0; next_elem = 0; } /** \brief Remove the head of the list. Return the old head. */ template T * dlist_pop(T * & head, NextProc const & next = NextProc(), PrevProc const & prev = PrevProc()) { if (head == 0) { return 0; } else { SASSERT(prev(head) == 0); T * r = head; head = next(head); if (head) prev(head) = 0; next(r) = 0; return r; } } /** \brief Insert new element after elem. */ template void dlist_insert_after(T * elem, T * new_elem, NextProc const & next = NextProc(), PrevProc const & prev = PrevProc()) { SASSERT(elem); SASSERT(new_elem); T * & old_next_elem = next(elem); prev(new_elem) = elem; next(new_elem) = old_next_elem; if (old_next_elem) prev(old_next_elem) = new_elem; // next(elem) = new_elem; old_next_elem = new_elem; } template bool dlist_contains(T * head, T const * elem, NextProc const & next = NextProc()) { T * curr = head; while (curr != 0) { if (curr == elem) return true; curr = next(curr); } return false; } template unsigned dlist_length(T * head, NextProc const & next = NextProc()) { unsigned r = 0; T * curr = head; while (curr != 0) { r++; curr = next(curr); } return r; } template bool dlist_check_invariant(T * head, NextProc const & next = NextProc(), PrevProc const & prev = PrevProc()) { if (head == 0) return true; SASSERT(prev(head) == 0); T * old = head; T * curr = next(head); while (curr != 0) { SASSERT(prev(curr) == old); old = curr; curr = next(curr); } return true; } #endif z3-z3-4.8.7/src/util/double_manager.h000066400000000000000000000072451356505360400173200ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: double_manager.h Abstract: Simulates mpq interface using doubles Author: Leonardo de Moura (leonardo) 2011-06-09. Revision History: --*/ #ifndef DOUBLE_MANAGER_H_ #define DOUBLE_MANAGER_H_ #include #include #include #include #include "util/util.h" #include "util/debug.h" #include "util/hash.h" #include "util/params.h" /** \brief Create an interface for manipulating double numbers compatible with the one for mpq. */ class double_manager { double m_zero_tolerance; public: typedef double numeral; static bool precise() { return false; } double_manager(params_ref const & p = params_ref()) { updt_params(p); } void updt_params(params_ref const & p) { m_zero_tolerance = p.get_double("zero_tolerance", 0.00000001); } static void reset(double & a) { a = 0.0; } static void del(double & a) { /* do nothing */ } static void add(double a, double b, double & c) { c = a + b; } // d <- a + b*c static void addmul(double a, double b, double c, double & d) { d = a + b*c; } // d <- a - b*c static void submul(double a, double b, double c, double & d) { d = a - b*c; } static void sub(double a, double b, double & c) { c = a - b; } static void mul(double a, double b, double & c) { c = a * b; } static void div(double a, double b, double & c) { c = a / b; } static void inv(double a, double & b) { b = 1 / a; } static void inv(double & a) { a = 1 / a; } static void neg(double & a) { a = -a; } static void abs(double & a) { if (a < 0.0) neg(a); } static void power(double a, unsigned p, double & b) { SASSERT(p <= INT_MAX); b = ::pow(a, static_cast(p)); } static void floor(double a, double & b) { b = ::floor(a); } static void ceil(double a, double & b) { b = ::ceil(a); } bool eq(double a, double b) const { return is_zero(a - b); } bool neq(double a, double b) const { return !eq(a, b); } static bool lt(double a, double b) { return a < b; } static bool le(double a, double b) { return a <= b; } static bool gt(double a, double b) { return a > b; } static bool ge(double a, double b) { return a >= b; } static void set(double & a, int n, int d) { a = static_cast(n)/static_cast(d); } static void set(double & a, double val) { a = val; } static void set(double & a, char const * val) { a = atof(val); } static void set(double & a, int val) { a = static_cast(val); } static void set(double & a, unsigned val) { a = static_cast(val); } static void set(double & a, int64_t val) { a = static_cast(val); } static void set(double & a, uint64_t val) { a = static_cast(val); } static void swap(double & a, double & b) { std::swap(a, b); } bool is_pos(double a) const { return a > m_zero_tolerance; } bool is_neg(double a) const { return a < m_zero_tolerance; } bool is_zero(double a) const { return -m_zero_tolerance <= a && a <= m_zero_tolerance; } bool is_nonpos(double a) const { return !is_pos(a); } bool is_nonneg(double a) const { return !is_neg(a); } static bool is_one(double a) { return a == 1.0; } static bool is_minus_one(double a) { return a == -1.0; } static bool is_int(double a) { return a == ::floor(a); } static std::string to_string(double a) { std::ostringstream sstream; sstream << std::setprecision(12) << a; return sstream.str(); } static unsigned hash(double a) { return hash_ull(static_cast(a)); } }; static_assert(sizeof(uint64_t) == sizeof(double), ""); #endif /* DOUBLE_MANAGER_H_ */ z3-z3-4.8.7/src/util/ema.h000066400000000000000000000024141356505360400151070ustar00rootroot00000000000000/*++ Copyright (c) 2018 Microsoft Corporation Module Name: ema.h Abstract: Exponential moving average based on CaDiCal. The exponential scheme used to adjust beta to alpha is described in Biere & Froelich, POS (Pragmatics of SAT) 2016. Author: Nikolaj Bjorner (nbjorner) 2018-05-03 Revision History: --*/ #ifndef EMA_H_ #define EMA_H_ class ema { double m_alpha, m_beta, m_value; unsigned m_period, m_wait; bool invariant() const { return 0 <= m_alpha && m_alpha <= m_beta && m_beta <= 1; } public: ema(): m_alpha(0), m_beta(1), m_value(0), m_period(0), m_wait(0) { SASSERT(invariant()); } ema(double alpha): m_alpha(alpha), m_beta(1), m_value(0), m_period(0), m_wait(0) { SASSERT(invariant()); } void set_alpha(double alpha) { m_alpha = alpha; SASSERT(invariant()); } operator double () const { return m_value; } void update(double x) { SASSERT(invariant()); m_value += m_beta * (x - m_value); if (m_beta <= m_alpha || m_wait--) return; m_wait = m_period = 2*(m_period + 1) - 1; m_beta *= 0.5; if (m_beta < m_alpha) m_beta = m_alpha; } void set(double x) { m_value = x; } }; #endif z3-z3-4.8.7/src/util/env_params.cpp000066400000000000000000000026131356505360400170340ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: env_params.cpp Abstract: Goodies for updating environment parameters. Author: Leonardo (leonardo) 2012-12-01 Notes: --*/ #include "util/env_params.h" #include "util/params.h" #include "util/gparams.h" #include "util/util.h" #include "util/memory_manager.h" void env_params::updt_params() { params_ref const& p = gparams::get_ref(); set_verbosity_level(p.get_uint("verbose", get_verbosity_level())); enable_warning_messages(p.get_bool("warning", true)); memory::set_max_size(megabytes_to_bytes(p.get_uint("memory_max_size", 0))); memory::set_max_alloc_count(p.get_uint("memory_max_alloc_count", 0)); memory::set_high_watermark(p.get_uint("memory_high_watermark", 0)); } void env_params::collect_param_descrs(param_descrs & d) { d.insert("verbose", CPK_UINT, "be verbose, where the value is the verbosity level", "0"); d.insert("warning", CPK_BOOL, "enable/disable warning messages", "true"); d.insert("memory_max_size", CPK_UINT, "set hard upper limit for memory consumption (in megabytes), if 0 then there is no limit", "0"); d.insert("memory_max_alloc_count", CPK_UINT, "set hard upper limit for memory allocations, if 0 then there is no limit", "0"); d.insert("memory_high_watermark", CPK_UINT, "set high watermark for memory consumption (in megabytes), if 0 then there is no limit", "0"); } z3-z3-4.8.7/src/util/env_params.h000066400000000000000000000007011356505360400164750ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: env_params.h Abstract: Goodies for updating environment parameters. Author: Leonardo (leonardo) 2012-12-01 Notes: --*/ #ifndef ENV_PARAMS_H_ #define ENV_PARAMS_H_ class param_descrs; struct env_params { static void updt_params(); static void collect_param_descrs(param_descrs & p); /* REG_PARAMS('env_params::collect_param_descrs') */ }; #endif z3-z3-4.8.7/src/util/error_codes.h000066400000000000000000000014201356505360400166470ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: error_codes.h Abstract: Error codes produced by Z3. Author: Leonardo de Moura (leonardo) 2007-09-04. Revision History: --*/ #ifndef ERROR_CODES_H_ #define ERROR_CODES_H_ #define ERR_OK 0 #define ERR_MEMOUT 101 #define ERR_TIMEOUT 102 #define ERR_PARSER 103 #define ERR_UNSOUNDNESS 104 #define ERR_INCOMPLETENESS 105 #define ERR_INI_FILE 106 #define ERR_NOT_IMPLEMENTED_YET 107 #define ERR_OPEN_FILE 108 #define ERR_CMD_LINE 109 #define ERR_INTERNAL_FATAL 110 #define ERR_TYPE_CHECK 111 #define ERR_UNKNOWN_RESULT 112 #define ERR_ALLOC_EXCEEDED 113 #endif /* ERROR_CODES_H_ */ z3-z3-4.8.7/src/util/event_handler.h000066400000000000000000000013151356505360400171620ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: event_handler.h Abstract: Abstract event handler. Author: Leonardo de Moura (leonardo) 2011-04-26. Revision History: --*/ #ifndef EVENT_HANDLER_H_ #define EVENT_HANDLER_H_ enum event_handler_caller_t { UNSET_EH_CALLER, CTRL_C_EH_CALLER, TIMEOUT_EH_CALLER, RESLIMIT_EH_CALLER, API_INTERRUPT_EH_CALLER }; class event_handler { protected: event_handler_caller_t m_caller_id; public: event_handler(): m_caller_id(UNSET_EH_CALLER) {} virtual ~event_handler() {} virtual void operator()(event_handler_caller_t caller_id) = 0; event_handler_caller_t caller_id() const { return m_caller_id; } }; #endif z3-z3-4.8.7/src/util/ext_gcd.h000066400000000000000000000016441356505360400157660ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: ext_gcd.h Abstract: Author: Leonardo de Moura (leonardo) 2007-06-09. Revision History: --*/ #ifndef EXT_GCD_H_ #define EXT_GCD_H_ template void extended_gcd(const numeral & in_a, const numeral & in_b, numeral & gcd, numeral & x, numeral & y) { numeral a = in_a; numeral b = in_b; x = numeral(0); y = numeral(1); numeral lastx(1); numeral lasty(0); numeral tmp; numeral quotient; while (!b.is_zero()) { tmp = b; quotient = div(a, b); b = mod(a, b); a = tmp; tmp = x; x = lastx - (quotient * x); lastx = tmp; tmp = y; y = lasty - (quotient * y); lasty = tmp; } gcd = a; x = lastx; y = lasty; } #endif /* EXT_GCD_H_ */ z3-z3-4.8.7/src/util/ext_numeral.h000066400000000000000000000217711356505360400166770ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: ext_numeral.h Abstract: Goodies for handling extended numerals such as R union { -oo, +oo }. We can have extended sets of mpq, mpz, mpbq, mpf, etc. Author: Leonardo de Moura (leonardo) 2011-12-04. Revision History: --*/ #ifndef EXT_NUMERAL_H_ #define EXT_NUMERAL_H_ #include #include "util/debug.h" enum ext_numeral_kind { EN_MINUS_INFINITY, EN_NUMERAL, EN_PLUS_INFINITY }; template bool is_zero(numeral_manager & m, typename numeral_manager::numeral const & a, ext_numeral_kind ak) { return ak == EN_NUMERAL && m.is_zero(a); } template bool is_pos(numeral_manager & m, typename numeral_manager::numeral const & a, ext_numeral_kind ak) { return ak == EN_PLUS_INFINITY || (ak == EN_NUMERAL && m.is_pos(a)); } template bool is_neg(numeral_manager & m, typename numeral_manager::numeral const & a, ext_numeral_kind ak) { return ak == EN_MINUS_INFINITY || (ak == EN_NUMERAL && m.is_neg(a)); } inline bool is_infinite(ext_numeral_kind ak) { return ak != EN_NUMERAL; } template void set(numeral_manager & m, typename numeral_manager::numeral & a, ext_numeral_kind & ak, typename numeral_manager::numeral const & b, ext_numeral_kind bk) { m.set(a, b); ak = bk; } template void reset(numeral_manager & m, typename numeral_manager::numeral & a, ext_numeral_kind & ak) { m.reset(a); ak = EN_NUMERAL; } template void neg(numeral_manager & m, typename numeral_manager::numeral & a, ext_numeral_kind & ak) { switch (ak) { case EN_MINUS_INFINITY: ak = EN_PLUS_INFINITY; break; case EN_NUMERAL: m.neg(a); break; case EN_PLUS_INFINITY: ak = EN_MINUS_INFINITY; break; } } template void inv(numeral_manager & m, typename numeral_manager::numeral & a, ext_numeral_kind & ak) { SASSERT(numeral_manager::field()); switch (ak) { case EN_MINUS_INFINITY: ak = EN_NUMERAL; m.reset(a); break; case EN_NUMERAL: SASSERT(!m.is_zero(a)); m.inv(a); break; case EN_PLUS_INFINITY: ak = EN_NUMERAL; m.reset(a); break; } } template void add(numeral_manager & m, typename numeral_manager::numeral const & a, ext_numeral_kind ak, typename numeral_manager::numeral const & b, ext_numeral_kind bk, typename numeral_manager::numeral & c, ext_numeral_kind & ck) { SASSERT(!(ak == EN_MINUS_INFINITY && bk == EN_PLUS_INFINITY)); // result is undefined SASSERT(!(ak == EN_PLUS_INFINITY && bk == EN_MINUS_INFINITY)); // result is undefined if (ak != EN_NUMERAL) { m.reset(c); ck = ak; } else if (bk != EN_NUMERAL) { m.reset(c); ck = bk; } else { m.add(a, b, c); ck = EN_NUMERAL; } } template void sub(numeral_manager & m, typename numeral_manager::numeral const & a, ext_numeral_kind ak, typename numeral_manager::numeral const & b, ext_numeral_kind bk, typename numeral_manager::numeral & c, ext_numeral_kind & ck) { SASSERT(!(ak == EN_MINUS_INFINITY && bk == EN_MINUS_INFINITY)); // result is undefined SASSERT(!(ak == EN_PLUS_INFINITY && bk == EN_PLUS_INFINITY)); // result is undefined if (ak != EN_NUMERAL) { SASSERT(bk != ak); m.reset(c); ck = ak; } else { switch (bk) { case EN_MINUS_INFINITY: m.reset(c); ck = EN_PLUS_INFINITY; break; case EN_NUMERAL: m.sub(a, b, c); ck = EN_NUMERAL; break; case EN_PLUS_INFINITY: m.reset(c); ck = EN_MINUS_INFINITY; break; } } } template void mul(numeral_manager & m, typename numeral_manager::numeral const & a, ext_numeral_kind ak, typename numeral_manager::numeral const & b, ext_numeral_kind bk, typename numeral_manager::numeral & c, ext_numeral_kind & ck) { if (is_zero(m, a, ak) || is_zero(m, b, bk)) { m.reset(c); ck = EN_NUMERAL; } else if (is_infinite(ak) || is_infinite(bk)) { if (is_pos(m, a, ak) == is_pos(m, b, bk)) ck = EN_PLUS_INFINITY; else ck = EN_MINUS_INFINITY; m.reset(c); } else { ck = EN_NUMERAL; m.mul(a, b, c); } } template void div(numeral_manager & m, typename numeral_manager::numeral const & a, ext_numeral_kind ak, typename numeral_manager::numeral const & b, ext_numeral_kind bk, typename numeral_manager::numeral & c, ext_numeral_kind & ck) { SASSERT(!is_zero(m, b, bk)); if (is_zero(m, a, ak)) { SASSERT(!is_zero(m, b, bk)); m.reset(c); ck = EN_NUMERAL; } else if (is_infinite(ak)) { SASSERT(!is_infinite(bk)); if (is_pos(m, a, ak) == is_pos(m, b, bk)) ck = EN_PLUS_INFINITY; else ck = EN_MINUS_INFINITY; m.reset(c); } else if (is_infinite(bk)) { SASSERT(!is_infinite(ak)); m.reset(c); ck = EN_NUMERAL; } else { ck = EN_NUMERAL; m.div(a, b, c); } } template void power(numeral_manager & m, typename numeral_manager::numeral & a, ext_numeral_kind & ak, unsigned n) { switch (ak) { case EN_MINUS_INFINITY: if (n % 2 == 0) ak = EN_PLUS_INFINITY; break; case EN_NUMERAL: m.power(a, n, a); break; case EN_PLUS_INFINITY: break; // do nothing } } /** \brief Return true if (a,ak) == (b,bk). */ template bool eq(numeral_manager & m, typename numeral_manager::numeral const & a, ext_numeral_kind ak, typename numeral_manager::numeral const & b, ext_numeral_kind bk) { if (ak == EN_NUMERAL) { return bk == EN_NUMERAL && m.eq(a, b); } else { return ak == bk; } } template bool neq(numeral_manager & m, typename numeral_manager::numeral const & a, ext_numeral_kind ak, typename numeral_manager::numeral const & b, ext_numeral_kind bk) { return !eq(m, a, ak, b, bk); } template bool lt(numeral_manager & m, typename numeral_manager::numeral const & a, ext_numeral_kind ak, typename numeral_manager::numeral const & b, ext_numeral_kind bk) { switch (ak) { case EN_MINUS_INFINITY: return bk != EN_MINUS_INFINITY; case EN_NUMERAL: switch (bk) { case EN_MINUS_INFINITY: return false; case EN_NUMERAL: return m.lt(a, b); case EN_PLUS_INFINITY: return true; default: UNREACHABLE(); return false; } case EN_PLUS_INFINITY: return false; default: UNREACHABLE(); return false; } } template bool gt(numeral_manager & m, typename numeral_manager::numeral const & a, ext_numeral_kind ak, typename numeral_manager::numeral const & b, ext_numeral_kind bk) { return lt(m, b, bk, a, ak); } template bool le(numeral_manager & m, typename numeral_manager::numeral const & a, ext_numeral_kind ak, typename numeral_manager::numeral const & b, ext_numeral_kind bk) { return !gt(m, a, ak, b, bk); } template bool ge(numeral_manager & m, typename numeral_manager::numeral const & a, ext_numeral_kind ak, typename numeral_manager::numeral const & b, ext_numeral_kind bk) { return !lt(m, a, ak, b, bk); } template void display(std::ostream & out, numeral_manager & m, typename numeral_manager::numeral const & a, ext_numeral_kind ak) { switch (ak) { case EN_MINUS_INFINITY: out << "-oo"; break; case EN_NUMERAL: m.display(out, a); break; case EN_PLUS_INFINITY: out << "+oo"; break; } } template void display_pp(std::ostream & out, numeral_manager & m, typename numeral_manager::numeral const & a, ext_numeral_kind ak) { switch (ak) { case EN_MINUS_INFINITY: out << "-∞"; break; case EN_NUMERAL: m.display_pp(out, a); break; case EN_PLUS_INFINITY: out << "+∞"; break; } } #endif z3-z3-4.8.7/src/util/f2n.h000066400000000000000000000155371356505360400150440ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: f2n.h Abstract: Template for wrapping a float-like API as a numeral-like API. The basic idea is to have the rounding mode as an implicit argument. Author: Leonardo de Moura (leonardo) 2012-07-30. Revision History: --*/ #ifndef F2N_H_ #define F2N_H_ #include "util/mpf.h" template class f2n { public: typedef typename fmanager::numeral numeral; struct exception {}; private: fmanager & m_manager; mpf_rounding_mode m_mode; unsigned m_ebits; unsigned m_sbits; numeral m_tmp1; numeral m_one; void check(numeral const & n) { if (!m().is_regular(n)) throw exception(); } public: static bool field() { return true; } static bool precise() { return false; } f2n(fmanager & m, unsigned ebits = 11, unsigned sbits = 53):m_manager(m), m_mode(MPF_ROUND_TOWARD_POSITIVE), m_ebits(ebits), m_sbits(sbits) { m_manager.set(m_one, ebits, sbits, 1); } f2n(f2n && other) : m_manager(other.m_manager), m_mode(other.m_mode), m_ebits(other.m_ebits), m_sbits(other.m_sbits), m_tmp1(std::move(other.m_tmp1)), m_one(std::move(other.m_one)) {} ~f2n() { m().del(m_tmp1); m().del(m_one); } void set_rounding_mode(mpf_rounding_mode m) { m_mode = m; } mpf_rounding_mode rounding_mode() const { return m_mode; } void round_to_plus_inf() { m_mode = MPF_ROUND_TOWARD_POSITIVE; } void round_to_minus_inf() { m_mode = MPF_ROUND_TOWARD_NEGATIVE; } void set_rounding(bool to_plus_inf) { if (to_plus_inf) round_to_plus_inf(); else round_to_minus_inf(); } unsigned ebits() const { return m_ebits; } unsigned sbits() const { return m_sbits; } fmanager & m() const { return m_manager; } double to_double(numeral & x) const { return m().to_double(x); } void del(numeral & x) { m().del(x); } void abs(numeral & o) { m().abs(o); } void abs(numeral const & x, numeral & o) { m().abs(x, o); } void neg(numeral & o) { m().neg(o); } void neg(numeral const & x, numeral & o) { m().neg(x, o); } bool is_zero(numeral const & x) { return m().is_zero(x); } bool is_neg(numeral const & x) { return m().is_neg(x) && !m().is_zero(x); /* it is not clear whether actual hardware returns true for is_neg(0-) */ } bool is_pos(numeral const & x) { return m().is_pos(x) && !m().is_zero(x); } bool is_nonneg(numeral const & x) { return !is_neg(x); } bool is_nonpos(numeral const & x) { return !is_pos(x); } void set(numeral & o, int value) { m().set(o, m_ebits, m_sbits, value); check(o); } void set(numeral & o, int n, int d) { m().set(o, m_ebits, m_sbits, m_mode, n, d); check(o); } void set(numeral & o, double x) { m().set(o, m_ebits, m_sbits, x); check(o); } void set(numeral & o, unsigned value) { m().set(o, m_ebits, m_sbits, (double)value); check(o); } void set(numeral & o, numeral const & x) { m().set(o, x); check(o); } void set(numeral & o, mpq const & x) { m().set(o, m_ebits, m_sbits, m_mode, x); check(o); } void reset(numeral & o) { m().reset(o, m_ebits, m_sbits); } static void swap(numeral & x, numeral & y) { x.swap(y); } void add(numeral const & x, numeral const & y, numeral & o) { m().add(m_mode, x, y, o); check(o); } void sub(numeral const & x, numeral const & y, numeral & o) { m().sub(m_mode, x, y, o); check(o); } void mul(numeral const & x, numeral const & y, numeral & o) { m().mul(m_mode, x, y, o); check(o); } void div(numeral const & x, numeral const & y, numeral & o) { m().div(m_mode, x, y, o); check(o); } void inv(numeral & o) { numeral a; set(a, 1); div(a, o, o); del(a); check(o); } void inv(numeral const & x, numeral & o) { set(o, x); inv(o); } void inc(numeral & x) { add(x, m_one, x); } void dec(numeral & x) { sub(x, m_one, x); } void power(numeral const & a, unsigned p, numeral & b) { unsigned mask = 1; numeral power; set(power, a); set(b, 1); while (mask <= p) { if (mask & p) mul(b, power, b); mul(power, power, power); mask = mask << 1; } del(power); check(b); } // Store the floor of a into b. Return true if a is an integer. // Throws an exception if the result cannot be computed precisely. void floor(numeral const & a, numeral & b) { SASSERT(m().is_regular(a)); // Claim: If a is a regular float, then floor(a) is an integer that can be precisely represented. // Justification: (for the case a is nonnegative) // If 0 <= a > 2^sbits(), then a is an integer, and floor(a) == a // If 0 <= a <= 2^sbits(), then floor(a) is representable since every integer less than 2^sbit m().round_to_integral(MPF_ROUND_TOWARD_NEGATIVE, a, m_tmp1); SASSERT(m().is_regular(m_tmp1)); if (m().le(m_tmp1, a)) { m().set(b, m_tmp1); } else { // the rounding mode doesn't matter for the following operation. m().sub(MPF_ROUND_TOWARD_NEGATIVE, m_tmp1, m_one, b); } SASSERT(m().is_regular(b)); } void ceil(numeral const & a, numeral & b) { SASSERT(m().is_regular(a)); // See comment in floor m().round_to_integral(MPF_ROUND_TOWARD_POSITIVE, a, m_tmp1); SASSERT(m().is_regular(m_tmp1)); if (m().ge(m_tmp1, a)) { m().set(b, m_tmp1); } else { // the rounding mode doesn't matter for the following operation. m().add(MPF_ROUND_TOWARD_NEGATIVE, m_tmp1, m_one, b); } SASSERT(m().is_regular(b)); } unsigned prev_power_of_two(numeral const & a) { return m().prev_power_of_two(a); } bool eq(numeral const & x, numeral const & y) { return m().eq(x, y); } bool lt(numeral const & x, numeral const & y) { return m().lt(x, y); } bool le(numeral const & x, numeral const & y) { return m().le(x, y); } bool gt(numeral const & x, numeral const & y) { return m().gt(x, y); } bool ge(numeral const & x, numeral const & y) { return m().ge(x, y); } bool is_int(numeral const & x) { return m().is_int(x); } bool is_one(numeral const & x) { return m().is_one(x); } bool is_minus_one(numeral const & x) { numeral & _x = const_cast(x); m().neg(_x); bool r = m().is_one(_x); m().neg(_x); return r; } std::string to_string(numeral const & a) { return m().to_string(a); } std::string to_rational_string(numeral const & a) { return m().to_rational_string(a); } void display(std::ostream & out, numeral const & a) { out << to_string(a); } void display_decimal(std::ostream & out, numeral const & a, unsigned k) { m().display_decimal(out, a, k); } void display_smt2(std::ostream & out, numeral const & a, bool decimal) { m().display_smt2(out, a, decimal); } }; #endif z3-z3-4.8.7/src/util/file_path.h000066400000000000000000000011731356505360400163010ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: file_path.h Abstract: File path functions. Author: Nikolaj Bjorner (nbjorner) 2017-11-19 Revision History: --*/ #ifndef FILE_PATH_H_ #define FILE_PATH_H_ #include inline char const * get_extension(char const * file_name) { if (file_name == nullptr) return nullptr; char const * last_dot = nullptr; for (;;) { char const * tmp = strchr(file_name, '.'); if (tmp == nullptr) { return last_dot; } last_dot = tmp + 1; file_name = last_dot; } } #endif /* FILE_PATH_H_ */ z3-z3-4.8.7/src/util/fixed_bit_vector.cpp000066400000000000000000000105531356505360400202220ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: fixed_bit_vector.cpp Abstract: Simple bitvector implementation for fixed size bit-vectors. Author: Nikolaj Bjorner (nbjorner) 2014-9-15. Leonardo de Moura (leonardo) 2006-10-03. Revision History: Based on bit_vector.cpp --*/ #include #include "util/fixed_bit_vector.h" #include "util/trace.h" #include "util/hash.h" void fixed_bit_vector::set(fixed_bit_vector const& other, unsigned hi, unsigned lo) { if ((lo % 32) == 0) { unsigned sz32 = (hi-lo+1)/32; unsigned lo32 = lo/32; for (unsigned i = 0; i < sz32; ++i) { m_data[lo32 + i] = other.m_data[i]; } for (unsigned i = sz32*32; i < hi - lo + 1; ++i) { set(lo + i, other.get(i)); } return; } for (unsigned i = 0; i < hi - lo + 1; ++i) { set(lo + i, other.get(i)); } } fixed_bit_vector_manager::fixed_bit_vector_manager(unsigned num_bits): m_alloc("fixed_bit_vector") { m_num_bits = num_bits; m_num_words = num_words(num_bits); m_num_bytes = m_num_words * sizeof(unsigned); unsigned bit_rest = m_num_bits % 32; m_mask = (1U << bit_rest) - 1; if (m_mask == 0) m_mask = UINT_MAX; } fixed_bit_vector* fixed_bit_vector_manager::allocate() { if (m_num_bytes == 0) return &m_0; return static_cast(m_alloc.allocate(m_num_bytes)); } fixed_bit_vector* fixed_bit_vector_manager::allocate0() { fixed_bit_vector* result = allocate(); fill0(*result); return result; } fixed_bit_vector* fixed_bit_vector_manager::allocate1() { fixed_bit_vector* result = allocate(); fill1(*result); return result; } fixed_bit_vector* fixed_bit_vector_manager::allocate(fixed_bit_vector const& bv) { fixed_bit_vector* result = allocate(); copy(*result, bv); return result; } void fixed_bit_vector_manager::deallocate(fixed_bit_vector* bv) { if (m_num_bytes > 0) m_alloc.deallocate(m_num_bytes, bv); } void fixed_bit_vector_manager::copy(fixed_bit_vector& dst, fixed_bit_vector const& src) const { memcpy(dst.m_data, src.m_data, num_bytes()); } fixed_bit_vector& fixed_bit_vector_manager::fill0(fixed_bit_vector& bv) const { memset(bv.m_data, 0, num_bytes()); return bv; } fixed_bit_vector& fixed_bit_vector_manager::fill1(fixed_bit_vector& bv) const { memset(bv.m_data, 0xFF, num_bytes()); return bv; } fixed_bit_vector& fixed_bit_vector_manager::set_and(fixed_bit_vector& dst, fixed_bit_vector const& src) const { for (unsigned i = 0; i < m_num_words; i++) dst.m_data[i] &= src.m_data[i]; return dst; } fixed_bit_vector& fixed_bit_vector_manager::set_or(fixed_bit_vector& dst, fixed_bit_vector const& src) const { for (unsigned i = 0; i < m_num_words; i++) dst.m_data[i] |= src.m_data[i]; return dst; } fixed_bit_vector& fixed_bit_vector_manager::set_neg(fixed_bit_vector& dst) const { for (unsigned i = 0; i < m_num_words; i++) dst.m_data[i] = ~dst.m_data[i]; return dst; } unsigned fixed_bit_vector_manager::last_word(fixed_bit_vector const& bv) const { unsigned n = num_words(); if (n == 0) return 0; return bv.m_data[n-1] & m_mask; } bool fixed_bit_vector_manager::equals(fixed_bit_vector const& a, fixed_bit_vector const& b) const { if (&a == &b) return true; unsigned n = num_words(); if (n == 0) return true; for (unsigned i = 0; i < n - 1; i++) { if (a.m_data[i] != b.m_data[i]) return false; } return last_word(a) == last_word(b); } unsigned fixed_bit_vector_manager::hash(fixed_bit_vector const& src) const { return string_hash(reinterpret_cast(src.m_data), num_bits()/8, num_bits()); } bool fixed_bit_vector_manager::contains(fixed_bit_vector const& a, fixed_bit_vector const& b) const { unsigned n = num_words(); if (n == 0) return true; for (unsigned i = 0; i < n - 1; ++i) { if ((a.m_data[i] & b.m_data[i]) != b.m_data[i]) return false; } unsigned b_data = last_word(b); return (last_word(a) & b_data) == b_data; } std::ostream& fixed_bit_vector_manager::display(std::ostream& out, fixed_bit_vector const& b) const { unsigned i = num_bits(); while (i > 0) { --i; if (b.get(i)) out << "1"; else out << "0"; } return out; } z3-z3-4.8.7/src/util/fixed_bit_vector.h000066400000000000000000000100731356505360400176640ustar00rootroot00000000000000/*++ Copyright (c) 2014 Microsoft Corporation Module Name: fixed_bit_vector.h Abstract: Simple bitvector implementation for fixed size bit-vectors. Author: Nikolaj Bjorner (nbjorner) 2014-9-15. Revision History: Related to bit_vector, but is based on a manager. --*/ #ifndef FIXED_BIT_VECTOR_H_ #define FIXED_BIT_VECTOR_H_ #include #include "util/debug.h" #include "util/small_object_allocator.h" class fixed_bit_vector { friend class fixed_bit_vector_manager; friend class tbv_manager; unsigned m_data[1]; static unsigned get_pos_mask(unsigned bit_idx) { return 1 << (bit_idx % 32); } unsigned get_bit_word(unsigned bit_idx) const { return m_data[bit_idx / 32]; } unsigned & get_bit_word(unsigned bit_idx) { return m_data[bit_idx / 32]; } public: fixed_bit_vector() {} ~fixed_bit_vector() {} unsigned get_word(unsigned word_idx) const { return m_data[word_idx]; } bool operator[](unsigned bit_idx) const { return get(bit_idx); } bool get(unsigned bit_idx) const { return (get_bit_word(bit_idx) & get_pos_mask(bit_idx)) != 0; } private: void set(unsigned bit_idx) { get_bit_word(bit_idx) |= get_pos_mask(bit_idx); } void unset(unsigned bit_idx) { get_bit_word(bit_idx) &= ~get_pos_mask(bit_idx); } void set(unsigned bit_idx, bool val) { int _val = static_cast(val); get_bit_word(bit_idx) ^= (-_val ^ get_bit_word(bit_idx)) & get_pos_mask(bit_idx); } // assign bits this[lo:hi] := other[0:hi-lo+1] void set(fixed_bit_vector const& other, unsigned hi, unsigned lo); }; class fixed_bit_vector_manager { friend class fixed_bit_vector; small_object_allocator m_alloc; unsigned m_num_bits; unsigned m_num_bytes; unsigned m_num_words; unsigned m_mask; fixed_bit_vector m_0; static unsigned num_words(unsigned num_bits) { return (num_bits + 31) / 32; } public: fixed_bit_vector_manager(unsigned num_bits); void reset() { m_alloc.reset(); } fixed_bit_vector* allocate(); fixed_bit_vector* allocate1(); fixed_bit_vector* allocate0(); fixed_bit_vector* allocate(fixed_bit_vector const& bv); void deallocate(fixed_bit_vector* bv); void copy(fixed_bit_vector& dst, fixed_bit_vector const& src) const; unsigned num_words() const { return m_num_words; } unsigned num_bytes() const { return m_num_bytes; } unsigned num_bits() const { return m_num_bits; } fixed_bit_vector& reset(fixed_bit_vector& bv) const { return fill0(bv); } fixed_bit_vector& fill0(fixed_bit_vector& bv) const; fixed_bit_vector& fill1(fixed_bit_vector& bv) const; fixed_bit_vector& set_and(fixed_bit_vector& dst, fixed_bit_vector const& src) const; fixed_bit_vector& set_or(fixed_bit_vector& dst, fixed_bit_vector const& src) const; fixed_bit_vector& set_neg(fixed_bit_vector& dst) const; unsigned last_word(fixed_bit_vector const& bv) const; unsigned get_mask() const { return m_mask; } bool equals(fixed_bit_vector const& a, fixed_bit_vector const& b) const; unsigned hash(fixed_bit_vector const& src) const; bool contains(fixed_bit_vector const& a, fixed_bit_vector const& b) const; std::ostream& display(std::ostream& out, fixed_bit_vector const& b) const; void set(fixed_bit_vector& dst, unsigned bit_idx) { SASSERT(bit_idx < num_bits()); dst.set(bit_idx); } void unset(fixed_bit_vector& dst, unsigned bit_idx) { SASSERT(bit_idx < num_bits()); dst.unset(bit_idx); } void set(fixed_bit_vector& dst, unsigned bit_idx, bool val) { SASSERT(bit_idx < num_bits()); dst.set(bit_idx, val); } // assign bits this[lo:hi] := other[0:hi-lo+1] void set(fixed_bit_vector& dst, fixed_bit_vector const& other, unsigned hi, unsigned lo) { SASSERT(lo <= hi && hi < num_bits()); dst.set(other, hi, lo); } }; #endif /* FIXED_BIT_VECTOR_H_ */ z3-z3-4.8.7/src/util/gparams.cpp000066400000000000000000000567031356505360400163440ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: gparams.cpp Abstract: Global parameter management. Author: Leonardo (leonardo) 2012-11-29 Notes: --*/ #include "util/gparams.h" #include "util/dictionary.h" #include "util/trace.h" #include "util/mutex.h" static DECLARE_MUTEX(gparams_mux); extern void gparams_register_modules(); static char const * g_old_params_names[] = { "arith_adaptive","arith_adaptive_assertion_threshold","arith_adaptive_gcd","arith_adaptive_propagation_threshold","arith_add_binary_bounds","arith_blands_rule_threshold","arith_branch_cut_ratio","arith_dump_lemmas","arith_eager_eq_axioms","arith_eager_gcd","arith_eq_bounds","arith_euclidean_solver","arith_expand_eqs","arith_force_simplex","arith_gcd_test","arith_ignore_int","arith_lazy_adapter","arith_lazy_pivoting","arith_max_lemma_size","arith_process_all_eqs","arith_propagate_eqs","arith_propagation_mode","arith_propagation_threshold","arith_prop_strategy","arith_random_initial_value","arith_random_lower","arith_random_seed","arith_random_upper","arith_reflect","arith_skip_big_coeffs","arith_small_lemma_size","arith_solver","arith_stronger_lemmas","array_always_prop_upward","array_canonize","array_cg","array_delay_exp_axiom","array_extensional","array_laziness","array_lazy_ieq","array_lazy_ieq_delay","array_solver","array_weak","async_commands","at_labels_cex","auto_config","bb_eager","bb_ext_gates","bb_quantifiers","bin_clauses","bit2int","bv2int_distribute","bv_blast_max_size","bv_cc","bv_enable_int2bv_propagation","bv_lazy_le","bv_max_sharing","bv_reflect","bv_solver","case_split","check_at_labels","check_proof","cnf_factor","cnf_mode","context_simplifier","dack","dack_eq","dack_factor","dack_gc","dack_gc_inv_decay","dack_threshold","default_qid","default_table","default_table_checked","delay_units","delay_units_threshold","der","display_config","display_dot_proof","display_error_for_visual_studio","display_features","display_proof","display_unsat_core","distribute_forall","dt_lazy_splits","dump_goal_as_smt","elim_and","elim_bounds","elim_nlarith_quantifiers","elim_quantifiers","elim_term_ite","ematching","engine","eq_propagation","hi_div0","ignore_bad_patterns","ignore_setparameter","instruction_max","inst_gen","interactive","internalizer_nnf","lemma_gc_factor","lemma_gc_half","lemma_gc_initial","lemma_gc_new_clause_activity","lemma_gc_new_clause_relevancy","lemma_gc_new_old_ratio","lemma_gc_old_clause_activity","lemma_gc_old_clause_relevancy","lemma_gc_strategy","lift_ite","lookahead_diseq","macro_finder","max_conflicts","max_counterexamples","mbqi","mbqi_force_template","mbqi_max_cexs","mbqi_max_cexs_incr","mbqi_max_iterations","mbqi_trace","minimize_lemmas","model","model_compact","model_completion","model_display_arg_sort","model_hide_unused_partitions","model_on_final_check","model_on_timeout","model_partial","model_v1","model_v2","model_validate","new_core2th_eq","ng_lift_ite","nl_arith","nl_arith_branching","nl_arith_gb","nl_arith_gb_eqs","nl_arith_gb_perturbate","nl_arith_gb_threshold","nl_arith_max_degree","nl_arith_rounds","nnf_factor","nnf_ignore_labels","nnf_mode","nnf_sk_hack","order","order_var_weight","order_weights","phase_selection","pi_arith","pi_arith_weight","pi_avoid_skolems","pi_block_looop_patterns","pi_max_multi_patterns","pi_non_nested_arith_weight","pi_nopat_weight","pi_pull_quantifiers","pi_use_database","pi_warnings","pp_bounded","pp_bv_literals","pp_bv_neg","pp_decimal","pp_decimal_precision","pp_fixed_indent","pp_flat_assoc","pp_max_depth","pp_max_indent","pp_max_num_lines","pp_max_ribbon","pp_max_width","pp_min_alias_size","pp_simplify_implies","pp_single_line","precedence","precedence_gen","pre_demodulator","pre_simplifier","pre_simplify_expr","profile_res_sub","progress_sampling_freq","proof_mode","propagate_booleans","propagate_values","pull_cheap_ite_trees","pull_nested_quantifiers","qi_conservative_final_check","qi_cost","qi_eager_threshold","qi_lazy_instantiation","qi_lazy_quick_checker","qi_lazy_threshold","qi_max_eager_multi_patterns","qi_max_instances","qi_max_lazy_multi_pattern_matching","qi_new_gen","qi_profile","qi_profile_freq","qi_promote_unsat","qi_quick_checker","quasi_macros","random_case_split_freq","random_initial_activity","random_seed","recent_lemma_threshold","reduce_args","refine_inj_axiom","relevancy","relevancy_lemma","rel_case_split_order","restart_adaptive","restart_agility_threshold","restart_factor","restart_initial","restart_strategy","restricted_quasi_macros","simplify_clauses","smtlib2_compliant","smtlib_category","smtlib_dump_lemmas","smtlib_logic","smtlib_source_info","smtlib_trace_path","soft_timeout","solver","spc_bs","spc_es","spc_factor_subsumption_index_opt","spc_initial_subsumption_index_opt","spc_max_subsumption_index_features","spc_min_func_freq_subsumption_index","spc_num_iterations","spc_trace","statistics","strong_context_simplifier","tick","trace","trace_file_name","type_check","user_theory_persist_axioms","user_theory_preprocess_axioms","verbose","warning","well_sorted_check","z3_solver_ll_pp","z3_solver_smt_pp", nullptr }; static bool is_old_param_name(symbol const & name) { char const * const * it = g_old_params_names; while (*it) { if (name == *it) return true; it++; } return false; } static char const * g_params_renames[] = { "proof_mode", "proof", "soft_timeout", "timeout", "mbqi", "smt.mbqi", "relevancy", "smt.relevancy", "ematching", "smt.ematching", "macro_finder", "smt.macro_finder", "delay_units", "smt.delay_units", "case_split", "smt.case_split", "phase_selection", "smt.phase_selection", "restart_strategy", "smt.restart_strategy", "restart_factor", "smt.restart_factor", "arith_random_initial_value", "smt.arith.random_initial_value", "bv_reflect", "smt.bv.reflect", "bv_enable_int2bv_propagation", "smt.bv.enable_int2bv", "qi_cost", "smt.qi.cost", "qi_eager_threshold", "smt.qi.eager_threshold", "nl_arith", "smt.arith.nl", "pull_nested_quantifiers", "smt.pull_nested_quantifiers", "nnf_sk_hack", "nnf.sk_hack", "model_v2", "model.v2", "pi_non_nested_arith_weight", "pi.non_nested_arith_weight", "pi_warnings", "pi.warnings", "pp_decimal", "pp.decimal", "pp_decimal", "pp.decimal_precision", "pp_bv_literals", "pp.bv_literals", "pp_bv_neg", "pp.bv_neg", "pp_max_depth", "pp.max_depth", "pp_min_alias_size", "pp.min_alias_size", nullptr }; static char const * get_new_param_name(symbol const & p) { char const * const * it = g_params_renames; while (*it) { if (p == *it) { it++; return *it; } it += 2; } return nullptr; } struct gparams::imp { bool m_modules_registered; dictionary m_module_param_descrs; dictionary m_module_descrs; param_descrs m_param_descrs; dictionary m_module_params; params_ref m_params; void check_registered() { if (m_modules_registered) return; m_modules_registered = true; gparams_register_modules(); } dictionary & get_module_param_descrs() { check_registered(); return m_module_param_descrs; } dictionary & get_module_descrs() { check_registered(); return m_module_descrs; } param_descrs & get_param_descrs() { check_registered(); return m_param_descrs; } public: imp(): m_modules_registered(false) { } ~imp() { reset(); for (auto & kv : m_module_param_descrs) { dealloc(kv.m_value); } } void reset() { lock_guard lock(*gparams_mux); m_params.reset(); for (auto & kv : m_module_params) { dealloc(kv.m_value); } m_module_params.reset(); } // ----------------------------------------------- // // Module registration routines. // They are invoked when descriptions are initialized // // ----------------------------------------------- void register_global(param_descrs & d) { // Don't need synchronization here, this method // is invoked from check_registered that is already protected. m_param_descrs.copy(d); } void register_module(char const * module_name, param_descrs * d) { // Don't need synchronization here, this method // is invoked from check_registered that is already protected. symbol s(module_name); param_descrs * old_d; if (m_module_param_descrs.find(s, old_d)) { old_d->copy(*d); dealloc(d); } else { m_module_param_descrs.insert(s, d); } } void register_module_descr(char const * module_name, char const * descr) { // Don't need synchronization here, this method // is invoked from check_registered that is already protected. m_module_descrs.insert(symbol(module_name), descr); } // ----------------------------------------------- // // Parameter setting & retrieval // // ----------------------------------------------- void normalize(char const * name, /* out */ symbol & mod_name, /* out */ symbol & param_name) { if (*name == ':') name++; std::string tmp = name; unsigned n = static_cast(tmp.size()); for (unsigned i = 0; i < n; i++) { if (tmp[i] >= 'A' && tmp[i] <= 'Z') tmp[i] = tmp[i] - 'A' + 'a'; else if (tmp[i] == '-') tmp[i] = '_'; } for (unsigned i = 0; i < n; i++) { if (tmp[i] == '.') { param_name = tmp.c_str() + i + 1; tmp.resize(i); mod_name = tmp.c_str(); return; } } param_name = tmp.c_str(); mod_name = symbol::null; } params_ref & get_params(symbol const & mod_name) { if (mod_name == symbol::null) { return m_params; } else { params_ref * p = nullptr; if (!m_module_params.find(mod_name, p)) { p = alloc(params_ref); m_module_params.insert(mod_name, p); } SASSERT(p != 0); return *p; } } void throw_unknown_parameter(symbol const & param_name, param_descrs const& d, symbol const & mod_name) { if (mod_name == symbol::null) { char const * new_name = get_new_param_name(param_name); if (new_name) { std::stringstream strm; strm << "the parameter '" << param_name << "', invoke 'z3 -p' to obtain the new parameter list, and 'z3 -pp:" << new_name << "' for the full description of the parameter"; throw exception(strm.str()); } else if (is_old_param_name(param_name)) { std::stringstream strm; strm << "unknown parameter '" << param_name << "', this is an old parameter name, invoke 'z3 -p' to obtain the new parameter list"; throw default_exception(strm.str()); } else { std::stringstream strm; strm << "unknown parameter '" << param_name << "'\n"; strm << "Legal parameters are:\n"; d.display(strm, 2, false, false); throw default_exception(strm.str()); } } else { std::stringstream strm; strm << "unknown parameter '" << param_name << "' "; strm << "at module '" << mod_name << "'\n"; strm << "Legal parameters are:\n"; d.display(strm, 2, false, false); throw default_exception(strm.str()); } } void validate_type(symbol const& name, char const* value, param_descrs const& d) { param_kind k = d.get_kind(name); std::stringstream strm; char const* _value = value; switch (k) { case CPK_UINT: for (; *value; ++value) { if (!('0' <= *value && *value <= '9')) { strm << "Expected values for parameter " << name << " is an unsigned integer. It was given argument '" << _value << "'"; throw default_exception(strm.str()); } } break; case CPK_DOUBLE: for (; *value; ++value) { if (!('0' <= *value && *value <= '9') && *value != '.' && *value != '-' && *value != '/') { strm << "Expected values for parameter " << name << " is a double. It was given argument '" << _value << "'"; throw default_exception(strm.str()); } } break; case CPK_BOOL: if (strcmp(value, "true") != 0 && strcmp(value, "false") != 0) { strm << "Expected values for parameter " << name << " are 'true' or 'false'. It was given argument '" << value << "'"; throw default_exception(strm.str()); } break; default: break; } } void set(param_descrs const & d, symbol const & param_name, char const * value, symbol const & mod_name) { param_kind k = d.get_kind(param_name); params_ref & ps = get_params(mod_name); if (k == CPK_INVALID) { throw_unknown_parameter(param_name, d, mod_name); } else if (k == CPK_UINT) { long val = strtol(value, nullptr, 10); ps.set_uint(param_name, static_cast(val)); } else if (k == CPK_DOUBLE) { char * aux; double val = strtod(value, &aux); ps.set_double(param_name, val); } else if (k == CPK_BOOL) { if (strcmp(value, "true") == 0) { ps.set_bool(param_name, true); } else if (strcmp(value, "false") == 0) { ps.set_bool(param_name, false); } else { std::stringstream strm; strm << "invalid value '" << value << "' for Boolean parameter '" << param_name << "'"; if (mod_name == symbol::null) { strm << " at module '" << mod_name << "'"; } throw default_exception(strm.str()); } } else if (k == CPK_SYMBOL) { ps.set_sym(param_name, symbol(value)); } else if (k == CPK_STRING) { // There is no guarantee that (external) callers will not delete value after invoking gparams::set. // I see two solutions: // 1) Modify params_ref to create a copy of set_str parameters. // This solution is not nice since we create copies and move the params_ref around. // We would have to keep copying the strings. // Moreover, when we use params_ref internally, the value is usually a static value. // So, we would be paying this price for nothing. // 2) "Copy" value by transforming it into a symbol. // I'm using this solution for now. ps.set_str(param_name, symbol(value).bare_str()); } else { std::stringstream strm; strm << "unsupported parameter type '" << param_name << "'"; if (mod_name == symbol::null) { strm << " at module '" << mod_name << "'"; } throw exception(strm.str()); } } void set(char const * name, char const * value) { lock_guard lock(*gparams_mux); symbol m, p; normalize(name, m, p); if (m == symbol::null) { validate_type(p, value, get_param_descrs()); set(get_param_descrs(), p, value, m); } else { param_descrs * d; if (get_module_param_descrs().find(m, d)) { validate_type(p, value, *d); set(*d, p, value, m); } else { std::stringstream strm; strm << "invalid parameter, unknown module '" << m << "'"; throw exception(strm.str()); } } } std::string get_value(params_ref const & ps, symbol const & p) { std::ostringstream buffer; ps.display(buffer, p); return buffer.str(); } std::string get_default(param_descrs const & d, symbol const & p, symbol const & m) { if (!d.contains(p)) { throw_unknown_parameter(p, d, m); } char const * r = d.get_default(p); if (r == nullptr) return "default"; return r; } std::string get_value(char const * name) { lock_guard lock(*gparams_mux); symbol m, p; normalize(name, m, p); if (m == symbol::null) { if (m_params.contains(p)) { return get_value(m_params, p); } else { return get_default(get_param_descrs(), p, m); } } else { params_ref * ps = nullptr; if (m_module_params.find(m, ps) && ps->contains(p)) { return get_value(*ps, p); } else { param_descrs * d; if (get_module_param_descrs().find(m, d)) { return get_default(*d, p, m); } } } std::stringstream strm; strm << "unknown module '" << m << "'"; throw exception(strm.str()); } // unfortunately, params_ref is not thread safe // so better create a local copy of the parameters. params_ref get_module(symbol const & module_name) { params_ref result; params_ref * ps = nullptr; { lock_guard lock(*gparams_mux); if (m_module_params.find(module_name, ps)) { result.copy(*ps); } } return result; } params_ref const& get_ref() { return m_params; } // ----------------------------------------------- // // Pretty printing // // ----------------------------------------------- void display(std::ostream & out, unsigned indent, bool smt2_style, bool include_descr) { lock_guard lock(*gparams_mux); out << "Global parameters\n"; get_param_descrs().display(out, indent + 4, smt2_style, include_descr); out << "\n"; if (!smt2_style) { out << "To set a module parameter, use .=value\n"; out << "Example: pp.decimal=true\n"; out << "\n"; } for (auto & kv : get_module_param_descrs()) { out << "[module] " << kv.m_key; char const * descr = nullptr; if (get_module_descrs().find(kv.m_key, descr)) { out << ", description: " << descr; } out << "\n"; kv.m_value->display(out, indent + 4, smt2_style, include_descr); } } void display_modules(std::ostream & out) { lock_guard lock(*gparams_mux); for (auto & kv : get_module_param_descrs()) { out << "[module] " << kv.m_key; char const * descr = nullptr; if (get_module_descrs().find(kv.m_key, descr)) { out << ", description: " << descr; } out << "\n"; } } void display_module(std::ostream & out, symbol const & module_name) { lock_guard lock(*gparams_mux); param_descrs * d = nullptr; if (!get_module_param_descrs().find(module_name, d)) { std::stringstream strm; strm << "unknown module '" << module_name << "'"; throw exception(strm.str()); } out << "[module] " << module_name; char const * descr = nullptr; if (get_module_descrs().find(module_name, descr)) { out << ", description: " << descr; } out << "\n"; d->display(out, 4, false); } void display_parameter(std::ostream & out, char const * name) { lock_guard lock(*gparams_mux); symbol m, p; normalize(name, m, p); out << name << " " << m << " " << p << "\n"; param_descrs * d; if (m == symbol::null) { d = &get_param_descrs(); } else { if (!get_module_param_descrs().find(m, d)) { std::stringstream strm; strm << "unknown module '" << m << "'"; throw exception(strm.str()); } } if (!d->contains(p)) throw_unknown_parameter(p, *d, m); out << " name: " << p << "\n"; if (m != symbol::null) { out << " module: " << m << "\n"; out << " qualified name: " << m << "." << p << "\n"; } out << " type: " << d->get_kind(p) << "\n"; out << " description: " << d->get_descr(p) << "\n"; out << " default value: " << d->get_default(p) << "\n"; } }; gparams::imp * gparams::g_imp = nullptr; void gparams::reset() { SASSERT(g_imp != 0); g_imp->reset(); } void gparams::set(char const * name, char const * value) { TRACE("gparams", tout << "setting [" << name << "] <- '" << value << "'\n";); SASSERT(g_imp != 0); g_imp->set(name, value); } void gparams::set(symbol const & name, char const * value) { SASSERT(g_imp != 0); g_imp->set(name.bare_str(), value); } std::string gparams::get_value(char const * name) { SASSERT(g_imp != 0); return g_imp->get_value(name); } std::string gparams::get_value(symbol const & name) { SASSERT(g_imp != 0); return g_imp->get_value(name.bare_str()); } void gparams::register_global(param_descrs & d) { SASSERT(g_imp != 0); g_imp->register_global(d); } void gparams::register_module(char const * module_name, param_descrs * d) { SASSERT(g_imp != 0); g_imp->register_module(module_name, d); } void gparams::register_module_descr(char const * module_name, char const * descr) { SASSERT(g_imp != 0); g_imp->register_module_descr(module_name, descr); } params_ref gparams::get_module(char const * module_name) { return get_module(symbol(module_name)); } params_ref gparams::get_module(symbol const & module_name) { SASSERT(g_imp != 0); return g_imp->get_module(module_name); } params_ref const& gparams::get_ref() { TRACE("gparams", tout << "gparams::get_ref()\n";); SASSERT(g_imp != 0); return g_imp->get_ref(); } void gparams::display(std::ostream & out, unsigned indent, bool smt2_style, bool include_descr) { SASSERT(g_imp != 0); g_imp->display(out, indent, smt2_style, include_descr); } void gparams::display_modules(std::ostream & out) { SASSERT(g_imp != 0); g_imp->display_modules(out); } void gparams::display_module(std::ostream & out, char const * module_name) { SASSERT(g_imp != 0); g_imp->display_module(out, symbol(module_name)); } void gparams::display_parameter(std::ostream & out, char const * name) { SASSERT(g_imp != 0); g_imp->display_parameter(out, name); } void gparams::init() { TRACE("gparams", tout << "gparams::init()\n";); ALLOC_MUTEX(gparams_mux); g_imp = alloc(imp); } void gparams::finalize() { TRACE("gparams", tout << "gparams::finalize()\n";); dealloc(g_imp); DEALLOC_MUTEX(gparams_mux); } z3-z3-4.8.7/src/util/gparams.h000066400000000000000000000111351356505360400157770ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: gparams.h Abstract: Global parameter management. Author: Leonardo (leonardo) 2012-11-29 Notes: --*/ #ifndef GPARAMS_H_ #define GPARAMS_H_ #include "util/params.h" class gparams { struct imp; static imp * g_imp; public: typedef default_exception exception; /** \brief Reset all global and module parameters. */ static void reset(); /** \brief Set a global parameter \c name with \c value. The name of parameter can be composed of characters [a-z][A-Z], digits [0-9], '-' and '_'. The character '.' is a delimiter (more later). The parameter names are case-insensitive. The character '-' should be viewed as an "alias" for '_'. Thus, the following parameter names are considered equivalent: "auto-config" and "AUTO_CONFIG". This function can be used to set parameters for a specific Z3 module. This can be done by using .. For example: set_global_param('pp.decimal', 'true') will set the parameter "decimal" in the module "pp" to true. An exception is thrown if the parameter name is unknown, or if the value is incorrect. */ static void set(char const * name, char const * value); static void set(symbol const & name, char const * value); /** \brief Auxiliary method used to implement get-option in SMT 2.0 front-end. If the parameter is not set, then it just returns 'default'. An exception is thrown if the parameter name is unknown. */ static std::string get_value(char const * name); static std::string get_value(symbol const & name); /** \brief Register additional global parameters This is an auxiliary function used by our automatic code generator. Example: the directive REG_PARAMS('collect_param_descrs') "tells" the automatic code generator how to register the additional global parameters. */ static void register_global(param_descrs & d); /** \brief Register parameter descriptions for a Z3 module. The parameters of a given Z3 module can only be set using #set_global_param if they are registered in this module using this function. This is an auxiliary function used by our automatic code generator. Each module will contain directives (in comments) such as Example: the directive REG_MODULE_PARAMS('nlsat', 'nlsat::solver::collect_param_descrs') "tells" the automatic code generator how to register the parameters for the given module. */ static void register_module(char const * module_name, param_descrs * d); /** \brief Add a (small) description to the given module. */ static void register_module_descr(char const * module_name, char const * descr); /** \brief Retrieves the parameters associated with the given module. Example: // The following command sets the parameter "decimal" in the module "pp" to true. set_global_param("pp.decimal", "true"); ... // The following command will return the global parameters that were set for the module "pp". // In this example "p" will contain "decimal" -> true after executing this function. params_ref const & p = get_module_params("pp") */ static params_ref get_module(char const * module_name); static params_ref get_module(symbol const & module_name); /** \brief Return the global parameter set (i.e., parameters that are not associated with any particular module). */ static params_ref const& get_ref(); /** \brief Dump information about available parameters in the given output stream. */ static void display(std::ostream & out, unsigned indent = 0, bool smt2_style=false, bool include_descr=true); // Auxiliary APIs for better command line support static void display_modules(std::ostream & out); static void display_module(std::ostream & out, char const * module_name); static void display_parameter(std::ostream & out, char const * name); /** \brief Initialize the global parameter management module. Remark: I set a priority in the initialization, because this module must be initialized after the core modules such as symbol. ADD_INITIALIZER('gparams::init();', 1) */ static void init(); /** \brief Finalize the global parameter management module. ADD_FINALIZER('gparams::finalize();'); */ static void finalize(); }; #endif z3-z3-4.8.7/src/util/hash.cpp000066400000000000000000000042031356505360400156210ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: hash.cpp Abstract: Basic hash computation support. Author: Leonardo de Moura (leonardo) 2006-09-11. Revision History: --*/ #include "util/debug.h" #include "util/hash.h" #include static unsigned read_unsigned(const char *s) { unsigned n; memcpy(&n, s, sizeof(unsigned)); return n; } // I'm using Bob Jenkin's hash function. // http://burtleburtle.net/bob/hash/doobs.html unsigned string_hash(const char * str, unsigned length, unsigned init_value) { unsigned a, b, c, len; /* Set up the internal state */ len = length; a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */ c = init_value; /* the previous hash value */ /*---------------------------------------- handle most of the key */ SASSERT(sizeof(unsigned) == 4); while (len >= 12) { a += read_unsigned(str); b += read_unsigned(str+4); c += read_unsigned(str+8); mix(a,b,c); str += 12; len -= 12; } /*------------------------------------- handle the last 11 bytes */ c += length; switch(len) { /* all the case statements fall through */ case 11: c+=((unsigned)str[10]<<24); Z3_fallthrough; case 10: c+=((unsigned)str[9]<<16); Z3_fallthrough; case 9 : c+=((unsigned)str[8]<<8); Z3_fallthrough; /* the first byte of c is reserved for the length */ case 8 : b+=((unsigned)str[7]<<24); Z3_fallthrough; case 7 : b+=((unsigned)str[6]<<16); Z3_fallthrough; case 6 : b+=((unsigned)str[5]<<8); Z3_fallthrough; case 5 : b+=str[4]; Z3_fallthrough; case 4 : a+=((unsigned)str[3]<<24); Z3_fallthrough; case 3 : a+=((unsigned)str[2]<<16); Z3_fallthrough; case 2 : a+=((unsigned)str[1]<<8); Z3_fallthrough; case 1 : a+=str[0]; /* case 0: nothing left to add */ break; } mix(a,b,c); /*-------------------------------------------- report the result */ return c; } z3-z3-4.8.7/src/util/hash.h000066400000000000000000000140261356505360400152720ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: hash.h Abstract: Basic hash computation support. Author: Leonardo de Moura (leonardo) 2006-09-11. Revision History: --*/ #ifndef HASH_H_ #define HASH_H_ #include #include "util/util.h" #define mix(a,b,c) \ { \ a -= b; a -= c; a ^= (c>>13); \ b -= c; b -= a; b ^= (a<<8); \ c -= a; c -= b; c ^= (b>>13); \ a -= b; a -= c; a ^= (c>>12); \ b -= c; b -= a; b ^= (a<<16); \ c -= a; c -= b; c ^= (b>>5); \ a -= b; a -= c; a ^= (c>>3); \ b -= c; b -= a; b ^= (a<<10); \ c -= a; c -= b; c ^= (b>>15); \ } inline unsigned hash_u(unsigned a) { a = (a+0x7ed55d16) + (a<<12); a = (a^0xc761c23c) ^ (a>>19); a = (a+0x165667b1) + (a<<5); a = (a+0xd3a2646c) ^ (a<<9); a = (a+0xfd7046c5) + (a<<3); a = (a^0xb55a4f09) ^ (a>>16); return a; } inline unsigned hash_ull(unsigned long long a) { a = (~a) + (a << 18); a ^= (a >> 31); a += (a << 2) + (a << 4); a ^= (a >> 11); a += (a << 6); a ^= (a >> 22); return static_cast(a); } inline unsigned combine_hash(unsigned h1, unsigned h2) { h2 -= h1; h2 ^= (h1 << 8); h1 -= h2; h2 ^= (h1 << 16); h2 -= h1; h2 ^= (h1 << 10); return h2; } inline unsigned hash_u_u(unsigned a, unsigned b) { return combine_hash(hash_u(a), hash_u(b)); } unsigned string_hash(const char * str, unsigned len, unsigned init_value); template unsigned get_composite_hash(Composite app, unsigned n, GetKindHashProc const & khasher = GetKindHashProc(), GetChildHashProc const & chasher = GetChildHashProc()) { unsigned a, b, c; SASSERT(n > 0); unsigned kind_hash = khasher(app); a = b = 0x9e3779b9; c = 11; switch (n) { case 1: a += kind_hash; b = chasher(app, 0); mix(a, b, c); return c; case 2: a += kind_hash; b += chasher(app, 0); c += chasher(app, 1); mix(a, b, c); return c; case 3: a += chasher(app, 0); b += chasher(app, 1); c += chasher(app, 2); mix(a, b, c); a += kind_hash; mix(a, b, c); return c; default: while (n >= 3) { n--; a += chasher(app, n); n--; b += chasher(app, n); n--; c += chasher(app, n); mix(a, b, c); } a += kind_hash; switch (n) { case 2: b += chasher(app, 1); Z3_fallthrough; case 1: c += chasher(app, 0); } mix(a, b, c); return c; } } template struct default_kind_hash_proc { unsigned operator()(Composite const & c) const { return 17; } }; struct int_hash { typedef int data; unsigned operator()(int x) const { return static_cast(x); } }; struct unsigned_hash { typedef unsigned data; unsigned operator()(unsigned x) const { return x; } }; struct size_t_hash { typedef size_t data; unsigned operator()(size_t x) const { return static_cast(x); } }; struct uint64_hash { typedef uint64_t data; unsigned operator()(uint64_t x) const { return static_cast(x); } }; struct bool_hash { typedef bool data; unsigned operator()(bool x) const { return static_cast(x); } }; template struct obj_hash { typedef T data; unsigned operator()(const T & e) const { return e.hash(); } }; template struct obj_ptr_hash { typedef T * data; unsigned operator()(T * a) const { return a->hash(); } }; template struct obj_ptr_pair_hash { typedef std::pair data; unsigned operator()(data const & d) const { return combine_hash(d.first->hash(), d.second->hash()); } }; template struct triple { T1 first; T2 second; T3 third; triple(): first(T1()), second(T2()), third(T3()) {} triple(T1 f, T2 s, T3 t): first(f), second(s), third(t) {} bool operator==(triple const& other) const { return first == other.first && second == other.second && third == other.third; } }; template struct triple_hash : private Hash1 { Hash2 m_hash2; Hash3 m_hash3; typedef triple data; triple_hash(Hash1 const & h1 = Hash1(), Hash2 const & h2 = Hash2(), Hash3 const & h3 = Hash3()): Hash1(h1), m_hash2(h2), m_hash3(h3) { } unsigned operator()(std::pair const & p) const { return combine_hash(combine_hash(Hash1::operator()(p.first), m_hash2.operator()(p.second)), m_hash3.operator()(p.third)); } }; template struct obj_ptr_triple_hash { typedef triple data; unsigned operator()(data const & d) const { return combine_hash(combine_hash(d.first->hash(), d.second->hash()), d.third->hash()); } }; template struct pair_hash : private Hash1 { Hash2 m_hash2; typedef std::pair data; pair_hash(Hash1 const & h1 = Hash1(), Hash2 const & h2 = Hash2()): Hash1(h1), m_hash2(h2) { } unsigned operator()(std::pair const & p) const { return combine_hash(Hash1::operator()(p.first), m_hash2.operator()(p.second)); } }; template inline unsigned get_ptr_hash(T * ptr) { return static_cast(reinterpret_cast(ptr)); } template struct ptr_hash { typedef T * data; unsigned operator()(T * ptr) const { return get_ptr_hash(ptr); } }; inline unsigned mk_mix(unsigned a, unsigned b, unsigned c) { mix(a, b, c); return c; } #endif /* HASH_H_ */ z3-z3-4.8.7/src/util/hashtable.h000066400000000000000000000667511356505360400163160ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: hashtable.h Abstract: Hashtable without buckets. Author: Leonardo de Moura (leonardo) 2006-09-11. Revision History: --*/ #ifndef HASHTABLE_H_ #define HASHTABLE_H_ #include "util/debug.h" #include #include "util/util.h" #include #include "util/memory_manager.h" #include "util/hash.h" #include "util/vector.h" #define DEFAULT_HASHTABLE_INITIAL_CAPACITY 8 #define SMALL_TABLE_CAPACITY 64 // #define HASHTABLE_STATISTICS #ifdef HASHTABLE_STATISTICS #define HS_CODE(CODE) { CODE } #else #define HS_CODE(CODE) #endif typedef enum { HT_FREE, HT_DELETED, HT_USED } hash_entry_state; template class default_hash_entry { unsigned m_hash; //!< cached hash code hash_entry_state m_state; T m_data; public: typedef T data; default_hash_entry():m_state(HT_FREE) {} unsigned get_hash() const { return m_hash; } bool is_free() const { return m_state == HT_FREE; } bool is_deleted() const { return m_state == HT_DELETED; } bool is_used() const { return m_state == HT_USED; } T & get_data() { return m_data; } const T & get_data() const { return m_data; } void set_data(T && d) { m_data = std::move(d); m_state = HT_USED; } void set_hash(unsigned h) { m_hash = h; } void mark_as_deleted() { m_state = HT_DELETED; } void mark_as_free() { m_state = HT_FREE; } }; /** \brief Special entry for a hashtable of integers. This entry "steals" two values for representing HT_FREE and HT_DELETED. */ template class int_hash_entry { unsigned m_hash; //!< cached hash code int m_data; public: typedef int data; int_hash_entry():m_data(Free) {} unsigned get_hash() const { return m_hash; } bool is_free() const { return m_data == Free; } bool is_deleted() const { return m_data == Deleted; } bool is_used() const { return m_data != Free && m_data != Deleted; } int get_data() const { return m_data; } int & get_data() { return m_data; } void set_data(int d) { m_data = d; } void set_hash(unsigned h) { m_hash = h; } void mark_as_deleted() { m_data = Deleted; } void mark_as_free() { m_data = Free; } }; /** \brief Special entry for a hashtable of pointers. This entry uses 0x0 and 0x1 to represent HT_FREE and HT_DELETED. */ template class ptr_hash_entry { unsigned m_hash; //!< cached hash code T * m_ptr; public: typedef T * data; ptr_hash_entry():m_ptr(nullptr) {} unsigned get_hash() const { return m_hash; } bool is_free() const { return m_ptr == nullptr; } bool is_deleted() const { return m_ptr == reinterpret_cast(1); } bool is_used() const { return m_ptr != reinterpret_cast(0) && m_ptr != reinterpret_cast(1); } T * get_data() const { return m_ptr; } T * & get_data() { return m_ptr; } void set_data(T * d) { m_ptr = d; } void set_hash(unsigned h) { m_hash = h; } void mark_as_deleted() { m_ptr = reinterpret_cast(1); } void mark_as_free() { m_ptr = nullptr; } }; /** \brief Special entry for a hashtable of pointers which uses the pointer itself as the hashcode. This entry uses 0x0 and 0x1 to represent HT_FREE and HT_DELETED. */ template class ptr_addr_hash_entry : public ptr_hash_entry { T * m_ptr; public: typedef T * data; ptr_addr_hash_entry():m_ptr(nullptr) {} unsigned get_hash() const { return get_ptr_hash(m_ptr); } bool is_free() const { return m_ptr == nullptr; } bool is_deleted() const { return m_ptr == reinterpret_cast(1); } bool is_used() const { return m_ptr != reinterpret_cast(0) && m_ptr != reinterpret_cast(1); } T * get_data() const { return m_ptr; } T * & get_data() { return m_ptr; } void set_data(T * d) { m_ptr = d; } void set_hash(unsigned h) { SASSERT(h == get_ptr_hash(m_ptr)); /* do nothing */ } void mark_as_deleted() { m_ptr = reinterpret_cast(1); } void mark_as_free() { m_ptr = 0; } }; template class core_hashtable : private HashProc, private EqProc { protected: Entry * m_table; unsigned m_capacity; unsigned m_size; unsigned m_num_deleted; #ifdef HASHTABLE_STATISTICS unsigned long long m_st_collision; #endif Entry* alloc_table(unsigned size) { Entry* entries = alloc_vect(size); return entries; } void delete_table() { dealloc_vect(m_table, m_capacity); m_table = nullptr; } public: typedef typename Entry::data data; typedef Entry entry; protected: unsigned get_hash(data const & e) const { return HashProc::operator()(e); } bool equals(data const & e1, data const & e2) const { return EqProc::operator()(e1, e2); } static void copy_table(entry * source, unsigned source_capacity, entry * target, unsigned target_capacity) { SASSERT(target_capacity >= source_capacity); unsigned target_mask = target_capacity - 1; entry * source_end = source + source_capacity; entry * target_end = target + target_capacity; for (entry * source_curr = source; source_curr != source_end; ++source_curr) { if (source_curr->is_used()) { unsigned hash = source_curr->get_hash(); unsigned idx = hash & target_mask; entry * target_begin = target + idx; entry * target_curr = target_begin; for (; target_curr != target_end; ++target_curr) { SASSERT(!target_curr->is_deleted()); if (target_curr->is_free()) { *target_curr = *source_curr; goto end; } } for (target_curr = target; target_curr != target_begin; ++target_curr) { SASSERT(!target_curr->is_deleted()); if (target_curr->is_free()) { *target_curr = *source_curr; goto end; } } UNREACHABLE(); end: ; } } } static void move_table(entry * source, unsigned source_capacity, entry * target, unsigned target_capacity) { SASSERT(target_capacity >= source_capacity); unsigned target_mask = target_capacity - 1; entry * source_end = source + source_capacity; entry * target_end = target + target_capacity; for (entry * source_curr = source; source_curr != source_end; ++source_curr) { if (source_curr->is_used()) { unsigned hash = source_curr->get_hash(); unsigned idx = hash & target_mask; entry * target_begin = target + idx; entry * target_curr = target_begin; for (; target_curr != target_end; ++target_curr) { SASSERT(!target_curr->is_deleted()); if (target_curr->is_free()) { *target_curr = std::move(*source_curr); goto end; } } for (target_curr = target; target_curr != target_begin; ++target_curr) { SASSERT(!target_curr->is_deleted()); if (target_curr->is_free()) { *target_curr = std::move(*source_curr); goto end; } } UNREACHABLE(); end: ; } } } void expand_table() { unsigned new_capacity = m_capacity << 1; entry * new_table = alloc_table(new_capacity); move_table(m_table, m_capacity, new_table, new_capacity); delete_table(); m_table = new_table; m_capacity = new_capacity; m_num_deleted = 0; } void remove_deleted_entries() { if (memory::is_out_of_memory()) return; entry * new_table = alloc_table(m_capacity); move_table(m_table, m_capacity, new_table, m_capacity); delete_table(); m_table = new_table; m_num_deleted = 0; } public: core_hashtable(unsigned initial_capacity = DEFAULT_HASHTABLE_INITIAL_CAPACITY, HashProc const & h = HashProc(), EqProc const & e = EqProc()): HashProc(h), EqProc(e) { SASSERT(is_power_of_two(initial_capacity)); m_table = alloc_table(initial_capacity); m_capacity = initial_capacity; m_size = 0; m_num_deleted = 0; HS_CODE({ m_st_collision = 0; }); } core_hashtable(const core_hashtable & source): HashProc(source), EqProc(source) { m_capacity = source.m_capacity; m_table = alloc_table(m_capacity); copy_table(source.m_table, m_capacity, m_table, m_capacity); m_size = source.m_size; m_num_deleted = 0; HS_CODE({ m_st_collision = 0; }); } ~core_hashtable() { delete_table(); } void swap(core_hashtable & source) { std::swap(m_table, source.m_table); std::swap(m_capacity, source.m_capacity); std::swap(m_size, source.m_size); std::swap(m_num_deleted, source.m_num_deleted); HS_CODE({ std::swap(m_st_collision, source.m_st_collision); }); } void reset() { if (m_size == 0 && m_num_deleted == 0) return; unsigned overhead = 0; entry * curr = m_table; entry * end = m_table + m_capacity; for (; curr != end; ++curr) { if (!curr->is_free()) curr->mark_as_free(); else overhead++; } if (m_capacity > 16 && overhead << 2 > (m_capacity * 3)) { delete_table(); SASSERT(m_capacity > 16); SASSERT(is_power_of_two(m_capacity)); m_capacity = (m_capacity >> 1); SASSERT(is_power_of_two(m_capacity)); m_table = alloc_table(m_capacity); } m_size = 0; m_num_deleted = 0; } void finalize() { if (m_capacity > SMALL_TABLE_CAPACITY) { delete_table(); m_table = alloc_table(SMALL_TABLE_CAPACITY); m_capacity = SMALL_TABLE_CAPACITY; m_size = 0; m_num_deleted = 0; } else { reset(); } } class iterator { entry * m_curr; entry * m_end; void move_to_used() { while (m_curr != m_end && !m_curr->is_used()) { m_curr++; } } public: iterator(entry * start, entry * end): m_curr(start), m_end(end) { move_to_used(); } data & operator*() { return m_curr->get_data(); } data const & operator*() const { return m_curr->get_data(); } data const * operator->() const { return &(operator*()); } data * operator->() { return &(operator*()); } iterator & operator++() { ++m_curr; move_to_used(); return *this; } iterator operator++(int) { iterator tmp = *this; ++*this; return tmp; } bool operator==(iterator const & it) const { return m_curr == it.m_curr; } bool operator!=(iterator const & it) const { return m_curr != it.m_curr; } }; bool empty() const { return m_size == 0; } unsigned size() const { return m_size; } unsigned capacity() const { return m_capacity; } iterator begin() const { return iterator(m_table, m_table + m_capacity); } iterator end() const { return iterator(m_table + m_capacity, m_table + m_capacity); } #define INSERT_LOOP_BODY() { \ if (curr->is_used()) { \ if (curr->get_hash() == hash && equals(curr->get_data(), e)) { \ curr->set_data(std::move(e)); \ return; \ } \ HS_CODE(m_st_collision++;); \ } \ else if (curr->is_free()) { \ entry * new_entry; \ if (del_entry) { new_entry = del_entry; m_num_deleted--; } \ else { new_entry = curr; } \ new_entry->set_data(std::move(e)); \ new_entry->set_hash(hash); \ m_size++; \ return; \ } \ else { \ SASSERT(curr->is_deleted()); \ del_entry = curr; \ HS_CODE(m_st_collision++;); \ } \ } ((void) 0) void insert(data && e) { if (((m_size + m_num_deleted) << 2) > (m_capacity * 3)) { expand_table(); } unsigned hash = get_hash(e); unsigned mask = m_capacity - 1; unsigned idx = hash & mask; entry * begin = m_table + idx; entry * end = m_table + m_capacity; entry * curr = begin; entry * del_entry = nullptr; for (; curr != end; ++curr) { INSERT_LOOP_BODY(); } for (curr = m_table; curr != begin; ++curr) { INSERT_LOOP_BODY(); } UNREACHABLE(); } void insert(const data & e) { data tmp(e); insert(std::move(tmp)); } #define INSERT_LOOP_CORE_BODY() { \ if (curr->is_used()) { \ if (curr->get_hash() == hash && equals(curr->get_data(), e)) { \ et = curr; \ return false; \ } \ HS_CODE(m_st_collision++;); \ } \ else if (curr->is_free()) { \ entry * new_entry; \ if (del_entry) { new_entry = del_entry; m_num_deleted--; } \ else { new_entry = curr; } \ new_entry->set_data(std::move(e)); \ new_entry->set_hash(hash); \ m_size++; \ et = new_entry; \ return true; \ } \ else { \ SASSERT(curr->is_deleted()); \ del_entry = curr; \ HS_CODE(m_st_collision++;); \ } \ } ((void) 0) /** \brief Insert the element e if it is not in the table. Return true if it is a new element, and false otherwise. Store the entry/slot of the table in et. */ bool insert_if_not_there_core(data && e, entry * & et) { if ((m_size + m_num_deleted) << 2 > (m_capacity * 3)) { // if ((m_size + m_num_deleted) * 2 > (m_capacity)) { expand_table(); } unsigned hash = get_hash(e); unsigned mask = m_capacity - 1; unsigned idx = hash & mask; entry * begin = m_table + idx; entry * end = m_table + m_capacity; entry * curr = begin; entry * del_entry = nullptr; for (; curr != end; ++curr) { INSERT_LOOP_CORE_BODY(); } for (curr = m_table; curr != begin; ++curr) { INSERT_LOOP_CORE_BODY(); } UNREACHABLE(); return false; } bool insert_if_not_there_core(const data & e, entry * & et) { data temp(e); return insert_if_not_there_core(std::move(temp), et); } /** \brief Insert the element e if it is not in the table. Return a reference to e or to an object identical to e that was already in the table. */ data const & insert_if_not_there(data const & e) { entry * et; insert_if_not_there_core(e, et); return et->get_data(); } /** \brief Insert the element e if it is not in the table. Return the entry that contains e. */ entry * insert_if_not_there2(data const & e) { entry * et; insert_if_not_there_core(e, et); return et; } #define FIND_LOOP_BODY() { \ if (curr->is_used()) { \ if (curr->get_hash() == hash && equals(curr->get_data(), e)) { \ return curr; \ } \ HS_CODE(const_cast(this)->m_st_collision++;); \ } \ else if (curr->is_free()) { \ return 0; \ } \ else { \ HS_CODE(const_cast(this)->m_st_collision++;); \ } \ } ((void) 0) entry * find_core(data const & e) const { unsigned hash = get_hash(e); unsigned mask = m_capacity - 1; unsigned idx = hash & mask; entry * begin = m_table + idx; entry * end = m_table + m_capacity; entry * curr = begin; for (; curr != end; ++curr) { FIND_LOOP_BODY(); } for (curr = m_table; curr != begin; ++curr) { FIND_LOOP_BODY(); } return nullptr; } bool find(data const & k, data & r) const { entry * e = find_core(k); if (e != nullptr) { r = e->get_data(); return true; } return false; } bool contains(data const & e) const { return find_core(e) != nullptr; } iterator find(data const & e) const { entry * r = find_core(e); if (r) { return iterator(r, m_table + m_capacity); } else { return end(); } } #define REMOVE_LOOP_BODY() { \ if (curr->is_used()) { \ if (curr->get_hash() == hash && equals(curr->get_data(), e)) { \ goto end_remove; \ } \ HS_CODE(m_st_collision++;); \ } \ else if (curr->is_free()) { \ return; \ } \ HS_CODE(m_st_collision++;); \ } ((void) 0) void remove(data const & e) { unsigned hash = get_hash(e); unsigned mask = m_capacity - 1; unsigned idx = hash & mask; entry * begin = m_table + idx; entry * end = m_table + m_capacity; entry * curr = begin; for (; curr != end; ++curr) { REMOVE_LOOP_BODY(); } for (curr = m_table; curr != begin; ++curr) { REMOVE_LOOP_BODY(); } SASSERT(!contains(e)); return; // node is not in the table end_remove: entry * next = curr + 1; if (next == end) { next = m_table; } if (next->is_free()) { curr->mark_as_free(); m_size--; } else { curr->mark_as_deleted(); m_num_deleted++; m_size--; if (m_num_deleted > m_size && m_num_deleted > SMALL_TABLE_CAPACITY) { remove_deleted_entries(); } } } void erase(data const & e) { remove(e); } void dump(std::ostream & out) { entry * curr = m_table; entry * end = m_table + m_capacity; out << "["; bool first = true; for (; curr != end; ++curr) { if (curr->is_used()) { if (first) { first = false; } else { out << " "; } out << curr->get_data(); } } out << "]"; } core_hashtable& operator|=(core_hashtable const& other) { if (this == &other) return *this; for (const data& d : other) { insert(d); } return *this; } core_hashtable& operator&=(core_hashtable const& other) { if (this == &other) return *this; core_hashtable copy(*this); for (const data& d : copy) { if (!other.contains(d)) { remove(d); } } return *this; } core_hashtable& operator=(core_hashtable const& other) { if (this == &other) return *this; reset(); for (const data& d : other) { insert(d); } return *this; } #ifdef Z3DEBUG bool check_invariant() { entry * curr = m_table; entry * end = m_table + m_capacity; unsigned num_deleted = 0; unsigned num_used = 0; for (; curr != end; ++curr) { if (curr->is_deleted()) { num_deleted ++; } if (curr->is_used()) { num_used++; } } SASSERT(num_deleted == m_num_deleted); SASSERT(num_used == m_size); return true; } #endif #ifdef HASHTABLE_STATISTICS unsigned long long get_num_collision() const { return m_st_collision; } #else unsigned long long get_num_collision() const { return 0; } #endif #define COLL_LOOP_BODY() { \ if (curr->is_used()) { \ if (curr->get_hash() == hash && equals(curr->get_data(), e)) return; \ collisions.push_back(curr->get_data()); \ continue; \ } \ else if (curr->is_free()) { \ continue; \ } \ collisions.push_back(curr->get_data()); \ } ((void) 0); void get_collisions(data const& e, vector& collisions) { unsigned hash = get_hash(e); unsigned mask = m_capacity - 1; unsigned idx = hash & mask; entry * begin = m_table + idx; entry * end = m_table + m_capacity; entry * curr = begin; for (; curr != end; ++curr) { COLL_LOOP_BODY(); } for (curr = m_table; curr != begin; ++curr) { COLL_LOOP_BODY(); } } }; template class hashtable : public core_hashtable, HashProc, EqProc> { public: hashtable(unsigned initial_capacity = DEFAULT_HASHTABLE_INITIAL_CAPACITY, HashProc const & h = HashProc(), EqProc const & e = EqProc()): core_hashtable, HashProc, EqProc>(initial_capacity, h, e) {} }; template class ptr_hashtable : public core_hashtable, HashProc, EqProc> { public: ptr_hashtable(unsigned initial_capacity = DEFAULT_HASHTABLE_INITIAL_CAPACITY, HashProc const & h = HashProc(), EqProc const & e = EqProc()): core_hashtable, HashProc, EqProc>(initial_capacity, h, e) {} }; /** \brief Hashtable of pointers which use the pointer as the hash-code. */ template class ptr_addr_hashtable : public core_hashtable, ptr_hash, ptr_eq > { public: typedef typename core_hashtable, ptr_hash, ptr_eq >::iterator iterator; ptr_addr_hashtable(unsigned initial_capacity = DEFAULT_HASHTABLE_INITIAL_CAPACITY): core_hashtable, ptr_hash, ptr_eq >(initial_capacity) {} iterator begin() const { UNREACHABLE(); } iterator end() const { UNREACHABLE(); } // NB. Using iterators to traverse the elements of this kind of hashtable will produce non-determinism. }; /** \brief Simple int_hashtable. The values INT_MIN and INT_MIN + 1 are used to mark deleted and free slots. So, these values cannot be stored in the table. Use core_hashtable template to avoid this limitation. */ template class int_hashtable : public core_hashtable, HashProc, EqProc> { public: int_hashtable(unsigned initial_capacity = DEFAULT_HASHTABLE_INITIAL_CAPACITY, HashProc const & h = HashProc(), EqProc const & e = EqProc()): core_hashtable, HashProc, EqProc>(initial_capacity, h, e) {} }; #endif /* HASHTABLE_H_ */ z3-z3-4.8.7/src/util/heap.h000066400000000000000000000175151356505360400152720ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: heap.h Abstract: A heap of integers. Author: Leonardo de Moura (leonardo) 2006-09-14. Revision History: --*/ #ifndef HEAP_H_ #define HEAP_H_ #include "util/vector.h" #include "util/debug.h" template class heap : private LT { int_vector m_values; int_vector m_value2indices; static int left(int i) { return i << 1; } static int right(int i) { return (i << 1) + 1; } static int parent(int i) { return i >> 1; } void display(std::ostream& out, unsigned indent, int idx) const { if (idx < static_cast(m_values.size())) { for (unsigned i = 0; i < indent; ++i) out << " "; out << m_values[idx] << "\n"; display(out, indent + 1, left(idx)); display(out, indent + 1, right(idx)); } } // Return true if the value can be inserted in the heap. That is, the vector m_value2indices is big enough to store this value. bool is_valid_value(int v) const { SASSERT(v >= 0 && v < static_cast(m_value2indices.size())); return true; } bool check_invariant_core(int idx) const { if (idx < static_cast(m_values.size())) { SASSERT(m_value2indices[m_values[idx]] == idx); SASSERT(parent(idx) == 0 || !less_than(m_values[idx], m_values[parent(idx)])); SASSERT(check_invariant_core(left(idx))); SASSERT(check_invariant_core(right(idx))); } return true; } public: bool check_invariant() const { return check_invariant_core(1); } private: void move_up(int idx) { int val = m_values[idx]; while (true) { int parent_idx = parent(idx); if (parent_idx == 0 || !less_than(val, m_values[parent_idx])) { break; } m_values[idx] = m_values[parent_idx]; m_value2indices[m_values[idx]] = idx; idx = parent_idx; } m_values[idx] = val; m_value2indices[val] = idx; CASSERT("heap", check_invariant()); } void move_down(int idx) { int val = m_values[idx]; int sz = static_cast(m_values.size()); while (true) { int left_idx = left(idx); if (left_idx >= sz) { break; } int right_idx = right(idx); int min_idx = right_idx < sz && less_than(m_values[right_idx], m_values[left_idx]) ? right_idx : left_idx; SASSERT(parent(min_idx) == idx); int min_value = m_values[min_idx]; if (!less_than(min_value, val)) { break; } m_values[idx] = min_value; m_value2indices[min_value] = idx; idx = min_idx; } m_values[idx] = val; m_value2indices[val] = idx; CASSERT("heap", check_invariant()); } public: typedef int * iterator; typedef const int * const_iterator; heap(int s, const LT & lt = LT()):LT(lt) { m_values.push_back(-1); set_bounds(s); CASSERT("heap", check_invariant()); } bool less_than(int v1, int v2) const { return LT::operator()(v1, v2); } bool empty() const { return m_values.size() == 1; } bool contains(int val) const { return val < static_cast(m_value2indices.size()) && m_value2indices[val] != 0; } void reset() { CASSERT("heap", check_invariant()); if (empty()) { return; } memset(m_value2indices.begin(), 0, sizeof(int) * m_value2indices.size()); m_values.reset(); m_values.push_back(-1); CASSERT("heap", check_invariant()); } void clear() { reset(); } void set_bounds(int s) { m_value2indices.resize(s, 0); CASSERT("heap", check_invariant()); } unsigned get_bounds() const { return m_value2indices.size(); } void reserve(int s) { CASSERT("heap", check_invariant()); if (s > static_cast(m_value2indices.size())) set_bounds(s); CASSERT("heap", check_invariant()); } int min_value() const { SASSERT(!empty()); return m_values[1]; } int erase_min() { CASSERT("heap", check_invariant()); SASSERT(!empty()); SASSERT(m_values.size() >= 2); int result = m_values[1]; if (m_values.size() == 2) { m_value2indices[result] = 0; m_values.pop_back(); SASSERT(empty()); } else { int last_val = m_values.back(); m_values[1] = last_val; m_value2indices[last_val] = 1; m_value2indices[result] = 0; m_values.pop_back(); move_down(1); } CASSERT("heap", check_invariant()); return result; } void erase(int val) { CASSERT("heap", check_invariant()); SASSERT(contains(val)); int idx = m_value2indices[val]; if (idx == static_cast(m_values.size()) - 1) { m_value2indices[val] = 0; m_values.pop_back(); } else { int last_val = m_values.back(); m_values[idx] = last_val; m_value2indices[last_val] = idx; m_value2indices[val] = 0; m_values.pop_back(); int parent_idx = parent(idx); if (parent_idx != 0 && less_than(last_val, m_values[parent(idx)])) { move_up(idx); } else { move_down(idx); } } CASSERT("heap", check_invariant()); } void decreased(int val) { SASSERT(contains(val)); move_up(m_value2indices[val]); } void increased(int val) { SASSERT(contains(val)); move_down(m_value2indices[val]); } void insert(int val) { CASSERT("heap", check_invariant()); CASSERT("heap", !contains(val)); SASSERT(is_valid_value(val)); int idx = static_cast(m_values.size()); m_value2indices[val] = idx; m_values.push_back(val); SASSERT(idx == static_cast(m_values.size()) - 1); move_up(idx); CASSERT("heap", check_invariant()); } iterator begin() { return m_values.begin() + 1; } iterator end() { return m_values.end(); } const_iterator begin() const { return m_values.begin() + 1; } const_iterator end() const { return m_values.end(); } void swap(heap & other) { if (this != &other) { CASSERT("heap", other.check_invariant()); CASSERT("heap", check_invariant()); m_values.swap(other.m_values); m_value2indices.swap(other.m_value2indices); CASSERT("heap", other.check_invariant()); CASSERT("heap", check_invariant()); } } /** \brief return set of values in heap that are less or equal to val. */ void find_le(int val, int_vector& result) { int_vector todo; todo.push_back(1); while (!todo.empty()) { int index = todo.back(); todo.pop_back(); if (index < static_cast(m_values.size()) && !less_than(val, m_values[index])) { result.push_back(m_values[index]); todo.push_back(left(index)); todo.push_back(right(index)); } } } void display(std::ostream& out) const { display(out, 0, 1); } }; #endif /* HEAP_H_ */ z3-z3-4.8.7/src/util/hwf.cpp000066400000000000000000000421101356505360400154610ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: hwf.cpp Abstract: Hardware Floating Point Numbers Author: Christoph Wintersteiger (cwinter) 2012-07-30. Revision History: --*/ #include #include #include #ifdef _WINDOWS #if defined(_MSC_VER) #pragma float_control( except, on ) // exception semantics; this does _not_ mean that exceptions are enabled (we want them off!) #pragma float_control( precise, on ) // precise semantics (no guessing!) #pragma fp_contract(off) // contractions off (`contraction' means x*y+z is turned into a fused-mul-add). #pragma fenv_access(on) // fpu environment sensitivity (needed to be allowed to make FPU mode changes). #endif #else #include #endif #if defined(__x86_64__) || defined(_M_X64) || \ defined(__i386) || defined(_M_IX86) #define USE_INTRINSICS #endif #include "util/hwf.h" // Note: // Which FPU will be used is determined by compiler settings. On x64 it's always SSE2, // on x86 we have to chose SSE2 by enabling /arch:SSE2 (otherwise the x87 FPU will be used). // Christoph has decided that we don't want to use the x87; this makes everything a lot easier. // For SSE2, it is best to use compiler intrinsics because this makes it completely // clear to the compiler what instructions should be used. E.g., for sqrt(), the Windows compiler selects // the x87 FPU, even when /arch:SSE2 is on. // Luckily, these are kind of standardized, at least for Windows/Linux/macOS. #if defined(__clang__) || defined(_M_ARM) && defined(_M_ARM64) #undef USE_INTRINSICS #endif #ifdef USE_INTRINSICS #include #if defined(_MSC_VER) || defined(__SSE4_1__) #include #endif #endif hwf_manager::hwf_manager() : m_mpz_manager(m_mpq_manager) { #ifdef _WINDOWS #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 // (the x64/SSE2 FPU has separate instructions for different precisions). #else // Setting the precision should only be required on the x87, but it won't hurt to do it anyways. // _PC_53 means double precision (53 significand bits). For extended precision use _PC_64. #ifndef USE_INTRINSICS __control87_2(_PC_53, _MCW_PC, &x86_state, &sse2_state); #endif #endif #else // macOS/Linux: Nothing. #endif // We only set the precision of the FPU here in the constructor. At the moment, there are no // other parts of the code that could overwrite this, and Windows takes care of context switches. // CMW: I'm not sure what happens on CPUs with hyper-threading (since the FPU is shared). // I have yet to discover whether Linux and macOS save the FPU state when switching context. // As long as we stick to using the SSE2 FPU though, there shouldn't be any problems with respect // to the precision (not sure about the rounding modes though). } hwf_manager::~hwf_manager() { } uint64_t RAW(double X) { uint64_t tmp; memcpy(&tmp, &(X), sizeof(uint64_t)); return tmp; } double DBL(uint64_t X) { double tmp; memcpy(&tmp, &(X), sizeof(double)); return tmp; } void hwf_manager::set(hwf & o, int value) { o.value = (double) value; } void hwf_manager::set(hwf & o, mpf_rounding_mode rm, int n, int d) { set_rounding_mode(rm); o.value = ((double) n)/((double) d); } void hwf_manager::set(hwf & o, double value) { o.value = value; } void hwf_manager::set(hwf & o, float value) { o.value = (double)value; } void hwf_manager::set(hwf & o, mpf_rounding_mode rm, mpq const & value) { set_rounding_mode(rm); o.value = m_mpq_manager.get_double(value); } void hwf_manager::set(hwf & o, mpf_rounding_mode rm, char const * value) { // We expect [i].[f]P[e], where P means that the exponent is interpreted as 2^e instead of 10^e. std::string v(value); size_t e_pos = v.find('p'); if (e_pos == std::string::npos) e_pos = v.find('P'); std::string f, e; f = (e_pos != std::string::npos) ? v.substr(0, e_pos) : v; e = (e_pos != std::string::npos) ? v.substr(e_pos+1) : "0"; TRACE("mpf_dbg", tout << " f = " << f << " e = " << e << std::endl;); mpq q; m_mpq_manager.set(q, f.c_str()); mpz ex; m_mpz_manager.set(ex, e.c_str()); set(o, rm, q, ex); TRACE("mpf_dbg", tout << "set: res = " << to_string(o) << std::endl;); } void hwf_manager::set(hwf & o, mpf_rounding_mode rm, mpq const & significand, mpz const & exponent) { // Assumption: this represents significand * 2^exponent. set_rounding_mode(rm); mpq sig; m_mpq_manager.set(sig, significand); int64_t exp = m_mpz_manager.get_int64(exponent); if (m_mpq_manager.is_zero(significand)) o.value = 0.0; else { while (m_mpq_manager.lt(sig, 1)) { m_mpq_manager.mul(sig, 2, sig); exp--; } hwf s; s.value = m_mpq_manager.get_double(sig); uint64_t r = (RAW(s.value) & 0x800FFFFFFFFFFFFFull) | ((exp + 1023) << 52); o.value = DBL(r); } } void hwf_manager::set(hwf & o, bool sign, uint64_t significand, int exponent) { // Assumption: this represents (sign * -1) * (significand/2^sbits) * 2^exponent. SASSERT(significand <= 0x000FFFFFFFFFFFFFull); SASSERT(-1022 <= exponent && exponent <= 1023); uint64_t raw = (sign?0x8000000000000000ull:0); raw |= (((uint64_t)exponent) + 1023) << 52; raw |= significand; memcpy(&o.value, &raw, sizeof(double)); } void hwf_manager::set(hwf & o, hwf const & x) { o.value = x.value; } void hwf_manager::abs(hwf & o) { o.value = fabs(o.value); } void hwf_manager::abs(hwf const & x, hwf & o) { o.value = fabs(x.value); } void hwf_manager::neg(hwf & o) { o.value = -o.value; } void hwf_manager::neg(hwf const & x, hwf & o) { o.value = -x.value; } bool hwf_manager::eq(hwf const & x, hwf const & y) { return (x.value == y.value); } bool hwf_manager::lt(hwf const & x, hwf const & y) { return (x.value < y.value); } bool hwf_manager::lte(hwf const & x, hwf const & y) { return (x.value <= y.value); } bool hwf_manager::gt(hwf const & x, hwf const & y) { return (x.value > y.value); } bool hwf_manager::gte(hwf const & x, hwf const & y) { return (x.value >= y.value); } void hwf_manager::add(mpf_rounding_mode rm, hwf const & x, hwf const & y, hwf & o) { set_rounding_mode(rm); #ifdef USE_INTRINSICS _mm_store_sd(&o.value, _mm_add_sd(_mm_set_sd(x.value), _mm_set_sd(y.value))); #else o.value = x.value + y.value; #endif } void hwf_manager::sub(mpf_rounding_mode rm, hwf const & x, hwf const & y, hwf & o) { set_rounding_mode(rm); #ifdef USE_INTRINSICS _mm_store_sd(&o.value, _mm_sub_sd(_mm_set_sd(x.value), _mm_set_sd(y.value))); #else o.value = x.value - y.value; #endif } #define DBL_SCALE 15360 void hwf_manager::mul(mpf_rounding_mode rm, hwf const & x, hwf const & y, hwf & o) { set_rounding_mode(rm); #ifdef USE_INTRINSICS _mm_store_sd(&o.value, _mm_mul_sd(_mm_set_sd(x.value), _mm_set_sd(y.value))); #else o.value = x.value * y.value; #endif } void hwf_manager::div(mpf_rounding_mode rm, hwf const & x, hwf const & y, hwf & o) { set_rounding_mode(rm); #ifdef USE_INTRINSICS _mm_store_sd(&o.value, _mm_div_sd(_mm_set_sd(x.value), _mm_set_sd(y.value))); #else o.value = x.value / y.value; #endif } void hwf_manager::fma(mpf_rounding_mode rm, hwf const & x, hwf const & y, hwf const &z, hwf & o) { set_rounding_mode(rm); o.value = ::fma(x.value, y.value, z.value); } void hwf_manager::sqrt(mpf_rounding_mode rm, hwf const & x, hwf & o) { set_rounding_mode(rm); #ifdef USE_INTRINSICS _mm_store_sd(&o.value, _mm_sqrt_pd(_mm_set_sd(x.value))); #else o.value = ::sqrt(x.value); #endif } void hwf_manager::round_to_integral(mpf_rounding_mode rm, hwf const & x, hwf & o) { set_rounding_mode(rm); // CMW: modf is not the right function here. // modf(x.value, &o.value); // 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. #if defined(_WINDOWS) && !defined(_M_ARM) && !defined(_M_ARM64) #if defined( __MINGW32__ ) && ( defined( __GNUG__ ) || defined( __clang__ ) ) o.value = nearbyint(x.value); #else #if defined(USE_INTRINSICS) && \ (defined(_WINDOWS) && (defined(__AVX__) || defined(_M_X64))) || \ (!defined(_WINDOWS) && defined(__SSE4_1__)) switch (rm) { case 0: _mm_store_sd(&o.value, _mm_round_pd(_mm_set_sd(x.value), _MM_FROUND_TO_NEAREST_INT)); break; case 2: _mm_store_sd(&o.value, _mm_round_pd(_mm_set_sd(x.value), _MM_FROUND_TO_POS_INF)); break; case 3: _mm_store_sd(&o.value, _mm_round_pd(_mm_set_sd(x.value), _MM_FROUND_TO_NEG_INF)); break; case 4: _mm_store_sd(&o.value, _mm_round_pd(_mm_set_sd(x.value), _MM_FROUND_TO_ZERO)); break; case 1: UNREACHABLE(); // Note: MPF_ROUND_NEAREST_TAWAY is not supported by the hardware! break; default: UNREACHABLE(); // Unknown rounding mode. } #else double xv = x.value; double & ov = o.value; __asm { fld xv frndint fstp ov // Store result away. } #endif #endif #else // Linux, macOS. o.value = nearbyint(x.value); #endif } void hwf_manager::rem(hwf const & x, hwf const & y, hwf & o) { #if defined(_WINDOWS) && _MSC_VER <= 1700 o.value = fmod(x.value, y.value); if (o.value >= (y.value/2.0)) o.value -= y.value; #else o.value = remainder(x.value, y.value); #endif } void hwf_manager::maximum(hwf const & x, hwf const & y, hwf & o) { #ifdef USE_INTRINSICS _mm_store_sd(&o.value, _mm_max_sd(_mm_set_sd(x.value), _mm_set_sd(y.value))); #else // use __max ? if (is_nan(x)) o.value = y.value; else if (is_nan(y)) o.value = x.value; else if (lt(x, y)) o.value = y.value; else o.value = x.value; #endif } void hwf_manager::minimum(hwf const & x, hwf const & y, hwf & o) { #ifdef USE_INTRINSICS _mm_store_sd(&o.value, _mm_min_sd(_mm_set_sd(x.value), _mm_set_sd(y.value))); #else // use __min ? if (is_nan(x) || is_nan(x)) o.value = y.value; else if (is_nan(y)) o.value = x.value; else if (lt(x, y)) o.value = x.value; else o.value = y.value; #endif } std::string hwf_manager::to_string(hwf const & x) { std::stringstream ss(""); ss << std::scientific << x.value; return ss.str(); } std::string hwf_manager::to_rational_string(hwf const & a) { // temporary hack unsynch_mpq_manager qm; scoped_mpq q(qm); to_rational(a, q); return qm.to_string(q); } void hwf_manager::display_decimal(std::ostream & out, hwf const & a, unsigned k) { // temporary hack unsynch_mpq_manager qm; scoped_mpq q(qm); to_rational(a, q); qm.display_decimal(out, q, k); } void hwf_manager::display_smt2(std::ostream & out, hwf const & a, bool decimal) { // temporary hack unsynch_mpq_manager qm; scoped_mpq q(qm); to_rational(a, q); qm.display_smt2(out, q, decimal); } void hwf_manager::to_rational(hwf const & x, unsynch_mpq_manager & qm, mpq & o) { SASSERT(is_normal(x) || is_denormal(x) || is_zero(x)); scoped_mpz n(qm), d(qm); if (is_normal(x)) qm.set(n, (uint64_t)(sig(x) | 0x0010000000000000ull)); else qm.set(n, sig(x)); if (sgn(x)) qm.neg(n); qm.set(d, (uint64_t)0x0010000000000000ull); int e = exp(x); if (e >= 0) qm.mul2k(n, (unsigned)e); else qm.mul2k(d, (unsigned)-e); qm.set(o, n, d); } bool hwf_manager::is_zero(hwf const & x) { uint64_t t = RAW(x.value) & 0x7FFFFFFFFFFFFFFFull; return (t == 0x0ull); // CMW: I tried, and these are slower: // return (t != 0x0ull) ? false : true; // return (x.value == 0.0 || x.value == -0.0); // [uses SSE2]. } bool hwf_manager::is_neg(hwf const & x) { // [Leo]: I added !is_nan(x) return sgn(x) && !is_nan(x); } bool hwf_manager::is_pos(hwf const & x) { return !sgn(x) && !is_nan(x); } bool hwf_manager::is_nzero(hwf const & x) { return RAW(x.value) == 0x8000000000000000ull; } bool hwf_manager::is_pzero(hwf const & x) { return RAW(x.value) == 0x0000000000000000ull; } bool hwf_manager::is_one(hwf const & x) { return RAW(x.value) == 0x3FF0000000000000ull; } bool hwf_manager::is_nan(hwf const & x) { bool r = ((RAW(x.value) & 0x7FF0000000000000ull) == 0x7FF0000000000000ull) && ((RAW(x.value) & 0x000FFFFFFFFFFFFFull) != 0x0); #ifdef _WINDOWS SASSERT( !r || (_fpclass(x.value) == _FPCLASS_SNAN || _fpclass(x.value) == _FPCLASS_QNAN)); #endif return r; } bool hwf_manager::is_inf(hwf const & x) { bool r = ((RAW(x.value) & 0x7FF0000000000000ull) == 0x7FF0000000000000ull) && ((RAW(x.value) & 0x000FFFFFFFFFFFFFull) == 0x0); #ifdef _WINDOWS SASSERT( !r || (_fpclass(x.value) == _FPCLASS_NINF || _fpclass(x.value) == _FPCLASS_PINF)); #endif return r; } bool hwf_manager::is_pinf(hwf const & x) { return !sgn(x) && is_inf(x); } bool hwf_manager::is_ninf(hwf const & x) { return sgn(x) && is_inf(x); } bool hwf_manager::is_normal(hwf const & x) { uint64_t t = RAW(x.value) & 0x7FF0000000000000ull; return (t != 0x0ull && t != 0x7FF0000000000000ull); } bool hwf_manager::is_denormal(hwf const & x) { uint64_t t = RAW(x.value); return ((t & 0x7FF0000000000000ull) == 0x0 && (t & 0x000FFFFFFFFFFFFFull) != 0x0); } bool hwf_manager::is_regular(hwf const & x) { // Everything that doesn't have the top-exponent is considered regular. // Note that +-0.0 and denormal numbers have exponent==0; these are regular. // All normal numbers are also regular. What remains is +-Inf and NaN, they are // not regular and they are the only numbers that have exponent 7FF. uint64_t e = RAW(x.value) & 0x7FF0000000000000ull; // the exponent return (e != 0x7FF0000000000000ull); } bool hwf_manager::is_int(hwf const & x) { if (!is_normal(x)) return false; const int e = exp(x); if (e >= 52) return true; else if (e < 0) return false; else { uint64_t t = sig(x); unsigned shift = 52 - ((unsigned)e); uint64_t mask = (0x1ull << shift) - 1; return (t & mask) == 0; } } void hwf_manager::mk_nzero(hwf & o) { uint64_t raw = 0x8000000000000000ull; o.value = DBL(raw); } void hwf_manager::mk_pzero(hwf & o) { o.value = 0; } void hwf_manager::mk_zero(bool sign, hwf & o) { if (sign) mk_nzero(o); else mk_pzero(o); } void hwf_manager::mk_nan(hwf & o) { uint64_t raw = 0x7FF0000000000001ull; o.value = DBL(raw); } void hwf_manager::mk_inf(bool sign, hwf & o) { uint64_t raw = (sign) ? 0xFFF0000000000000ull : 0x7FF0000000000000ull; o.value = DBL(raw); } void hwf_manager::mk_pinf(hwf & o) { uint64_t raw = 0x7FF0000000000000ull; o.value = DBL(raw); } void hwf_manager::mk_ninf(hwf & o) { uint64_t raw = 0xFFF0000000000000ull; o.value = DBL(raw); } #ifdef _WINDOWS #if defined(_WIN64) #ifdef USE_INTRINSICS #define SETRM(RM) _MM_SET_ROUNDING_MODE(RM) #else #define SETRM(RM) _controlfp_s(&sse2_state, RM, _MCW_RC); #endif #else #ifdef USE_INTRINSICS #define SETRM(RM) _MM_SET_ROUNDING_MODE(RM) #else #define SETRM(RM) __control87_2(RM, _MCW_RC, &x86_state, &sse2_state) #endif #endif #else #define SETRM(RM) fesetround(RM) #endif unsigned hwf_manager::prev_power_of_two(hwf const & a) { SASSERT(!is_nan(a) && !is_pinf(a) && !is_ninf(a)); if (!is_pos(a)) return 0; if (exp(a) <= -52) return 0; return 51 + exp(a); } void hwf_manager::set_rounding_mode(mpf_rounding_mode rm) { #ifdef _WINDOWS #ifdef USE_INTRINSICS switch (rm) { case MPF_ROUND_NEAREST_TEVEN: SETRM(_MM_ROUND_NEAREST); break; case MPF_ROUND_TOWARD_POSITIVE: SETRM(_MM_ROUND_UP); break; case MPF_ROUND_TOWARD_NEGATIVE: SETRM(_MM_ROUND_DOWN); break; case MPF_ROUND_TOWARD_ZERO: SETRM(_MM_ROUND_TOWARD_ZERO); break; case MPF_ROUND_NEAREST_TAWAY: default: UNREACHABLE(); // Note: MPF_ROUND_NEAREST_TAWAY is not supported by the hardware! } #else switch (rm) { case MPF_ROUND_NEAREST_TEVEN: SETRM(_RC_NEAR); break; case MPF_ROUND_TOWARD_POSITIVE: SETRM(_RC_UP); break; case MPF_ROUND_TOWARD_NEGATIVE: SETRM(_RC_DOWN); break; case MPF_ROUND_TOWARD_ZERO: SETRM(_RC_CHOP); break; case MPF_ROUND_NEAREST_TAWAY: default: UNREACHABLE(); // Note: MPF_ROUND_NEAREST_TAWAY is not supported by the hardware! } #endif #else // macOS/Linux switch (rm) { case MPF_ROUND_NEAREST_TEVEN: SETRM(FE_TONEAREST); break; case MPF_ROUND_TOWARD_POSITIVE: SETRM(FE_UPWARD); break; case MPF_ROUND_TOWARD_NEGATIVE: SETRM(FE_DOWNWARD); break; case MPF_ROUND_TOWARD_ZERO: SETRM(FE_TOWARDZERO); break; case MPF_ROUND_NEAREST_TAWAY: default: UNREACHABLE(); // Note: MPF_ROUND_NEAREST_TAWAY is not supported by the hardware! } #endif } z3-z3-4.8.7/src/util/hwf.h000066400000000000000000000124271356505360400151360ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: hwf.h Abstract: Hardware Floating Point Numbers Author: Christoph Wintersteiger (cwinter) 2012-07-30. Revision History: --*/ #ifndef HWF_H_ #define HWF_H_ #include #include "util/mpz.h" #include "util/mpq.h" #include "util/mpf.h" class hwf { friend class hwf_manager; double value; hwf & operator=(hwf const & other) { UNREACHABLE(); return *this; } uint64_t get_raw() const { uint64_t n; SASSERT(sizeof(n) == sizeof(value)); memcpy(&n, &value, sizeof(value)); return n; } public: hwf() {} hwf(hwf const & other) { this->value = other.value; } ~hwf() {} void swap(hwf & other) { double t = value; value = other.value; other.value = t; } }; class hwf_manager { unsynch_mpq_manager m_mpq_manager; unsynch_mpz_manager & m_mpz_manager; // A mpq_manager is a mpz_manager, reusing it. public: typedef hwf numeral; hwf_manager(); ~hwf_manager(); void reset(hwf & o) { set(o, 0); } void set(hwf & o, int value); void set(hwf & o, mpf_rounding_mode rm, int n, int d); void set(hwf & o, float value); void set(hwf & o, double value); void set(hwf & o, mpf_rounding_mode rm, mpq const & value); void set(hwf & o, mpf_rounding_mode rm, char const * value); void set(hwf & o, mpf_rounding_mode rm, mpq const & significand, mpz const & exponent); void set(hwf & o, bool sign, uint64_t significand, int exponent); void set(hwf & o, hwf const & x); // auxiliary methods to make the interface compatible with mpf void reset(hwf & o, unsigned ebits, unsigned sbits) { set(o, 0); } void set(hwf & o, unsigned ebits, unsigned sbits, mpf_rounding_mode rm, mpq const & value) { set(o, rm, value); } void set(hwf & o, unsigned ebits, unsigned sbits, int value) { set(o, value); } void set(hwf & o, unsigned ebits, unsigned sbits, mpf_rounding_mode rm, int n, int d) { set(o, rm, n, d); } void set(hwf & o, unsigned ebits, unsigned sbits, float value) { set(o, value); } void set(hwf & o, unsigned ebits, unsigned sbits, double value) { set(o, value); } void del(hwf & x) {} void abs(hwf & o); void abs(hwf const & x, hwf & o); void neg(hwf & o); void neg(hwf const & x, hwf & o); bool is_zero(hwf const & x); bool is_neg(hwf const & x); bool is_pos(hwf const & x); bool is_nzero(hwf const & x); bool is_pzero(hwf const & x); bool is_one(hwf const & x); bool eq(hwf const & x, hwf const & y); bool lt(hwf const & x, hwf const & y); bool lte(hwf const & x, hwf const & y); bool le(hwf const & x, hwf const & y) { return lte(x, y); } bool gt(hwf const & x, hwf const & y); bool gte(hwf const & x, hwf const & y); bool ge(hwf const & x, hwf const & y) { return gte(x, y); } void add(mpf_rounding_mode rm, hwf const & x, hwf const & y, hwf & o); void sub(mpf_rounding_mode rm, hwf const & x, hwf const & y, hwf & o); void mul(mpf_rounding_mode rm, hwf const & x, hwf const & y, hwf & o); void div(mpf_rounding_mode rm, hwf const & x, hwf const & y, hwf & o); void fma(mpf_rounding_mode rm, hwf const & x, hwf const & y, hwf const &z, hwf & o); void sqrt(mpf_rounding_mode rm, hwf const & x, hwf & o); void round_to_integral(mpf_rounding_mode rm, hwf const & x, hwf & o); void rem(hwf const & x, hwf const & y, hwf & o); void maximum(hwf const & x, hwf const & y, hwf & o); void minimum(hwf const & x, hwf const & y, hwf & o); std::string to_string(hwf const & a); std::string to_rational_string(hwf const & a); void display_decimal(std::ostream & out, hwf const & a, unsigned k); void display_smt2(std::ostream & out, hwf const & a, bool decimal); double to_double(hwf const & x) { return x.value; } float to_float(hwf const & x) { return (float) x.value; } void to_rational(hwf const & x, unsynch_mpq_manager & qm, mpq & o); void to_rational(hwf const & x, scoped_mpq & o) { to_rational(x, o.m(), o); } bool sgn(hwf const & x) const { return (x.get_raw() & 0x8000000000000000ull) != 0; } uint64_t sig(hwf const & x) const { return x.get_raw() & 0x000FFFFFFFFFFFFFull; } int exp(hwf const & x) const { return ((x.get_raw() & 0x7FF0000000000000ull) >> 52) - 1023; } bool is_nan(hwf const & x); bool is_inf(hwf const & x); bool is_pinf(hwf const & x); bool is_ninf(hwf const & x); bool is_normal(hwf const & x); bool is_denormal(hwf const & x); bool is_regular(hwf const & x); bool is_int(hwf const & x); void mk_zero(bool sign, hwf & o); void mk_nzero(hwf & o); void mk_pzero(hwf & o); void mk_nan(hwf & o); void mk_inf(bool sign, hwf & o); void mk_pinf(hwf & o); void mk_ninf(hwf & o); unsigned hash(hwf const & a) { return hash_ull(a.get_raw()); } inline void set_rounding_mode(mpf_rounding_mode rm); /** \brief Return the biggest k s.t. 2^k <= a. \remark Return 0 if a is not positive. */ unsigned prev_power_of_two(hwf const & a); protected: #ifdef _WINDOWS unsigned x86_state, sse2_state; #endif }; typedef _scoped_numeral scoped_hwf; typedef _scoped_numeral_vector scoped_hwf_vector; #endif z3-z3-4.8.7/src/util/id_gen.h000066400000000000000000000036411356505360400155750ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: id_gen.h Abstract: Basic support for generating & recycling ids. Author: Leonardo de Moura (leonardo) 2008-02-02. Revision History: --*/ #ifndef ID_GEN_H_ #define ID_GEN_H_ #include "util/vector.h" #include "util/util.h" class id_gen { unsigned m_next_id; unsigned_vector m_free_ids; public: id_gen(unsigned start = 0):m_next_id(start) {} unsigned mk() { unsigned r; if (m_free_ids.empty()) { r = m_next_id; m_next_id++; } else { r = m_free_ids.back(); m_free_ids.pop_back(); } return r; } void recycle(unsigned id) { if (memory::is_out_of_memory()) return; m_free_ids.push_back(id); } void reset(unsigned start = 0) { m_next_id = start; m_free_ids.reset(); } void cleanup(unsigned start = 0) { m_next_id = start; m_free_ids.finalize(); } unsigned show_hash(){ unsigned h = string_hash((char *)&m_free_ids[0],m_free_ids.size()*sizeof(unsigned),17); return hash_u_u(h,m_next_id); } /** \brief Return N if the range of ids generated by this module is in the set [0..N) */ unsigned get_id_range() const { return m_next_id; } /** \brief Debugging support method: set m_next_id to the least value id' s.t. id' >= id and id' is not in m_free_ids. This method is only used to create small repros that exposes bugs in Z3. */ unsigned set_next_id(unsigned id) { m_next_id = id; while (std::find(m_free_ids.begin(), m_free_ids.end(), m_next_id) != m_free_ids.end()) m_next_id++; return m_next_id; } void display_free_ids(std::ostream & out) { ::display(out, m_free_ids.begin(), m_free_ids.end()); } }; #endif /* ID_GEN_H_ */ z3-z3-4.8.7/src/util/inf_eps_rational.h000066400000000000000000000247321356505360400176700ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: inf_eps_rational.h Abstract: Rational numbers with infinity and epsilon. Author: Nikolaj Bjorner (nbjorner) 2013-4-23. Revision History: --*/ #ifndef INF_EPS_RATIONAL_H_ #define INF_EPS_RATIONAL_H_ #include #include #include "util/debug.h" #include "util/vector.h" #include "util/rational.h" #include "util/inf_rational.h" template class inf_eps_rational { rational m_infty; Numeral m_r; public: unsigned hash() const { return m_infty.hash() ^ m_r.hash(); } struct hash_proc { unsigned operator()(inf_eps_rational const& r) const { return r.hash(); } }; struct eq_proc { bool operator()(inf_eps_rational const& r1, inf_eps_rational const& r2) const { return r1 == r2; } }; void swap(inf_eps_rational & n) { m_infty.swap(n.m_infty); m_r.swap(n.m_r); } std::string to_string() const { if (m_infty.is_zero()) { return m_r.to_string(); } std::string si; if (m_infty.is_one()) { si = "oo"; } else if (m_infty.is_minus_one()) { si = "-oo"; } else { si = m_infty.to_string() += "*oo"; } if (m_r.is_zero()) { return si; } std::string s = "("; s += si; s += " + "; s += m_r.to_string(); s += ")"; return s; } inf_eps_rational(): m_infty(), m_r() {} inf_eps_rational(const inf_eps_rational & r): m_infty(r.m_infty), m_r(r.m_r) {} explicit inf_eps_rational(int n): m_infty(), m_r(n) {} explicit inf_eps_rational(Numeral const& r): m_infty(), m_r(r) {} explicit inf_eps_rational(rational const& i, Numeral const& r): m_infty(i), m_r(r) { } ~inf_eps_rational() {} /** \brief Set inf_eps_rational to 0. */ void reset() { m_infty.reset(); m_r.reset(); } bool is_int() const { return m_infty.is_zero() && m_r.is_int(); } bool is_int64() const { return m_infty.is_zero() && m_r.is_int64(); } bool is_uint64() const { return m_infty.is_zero() && m_r.is_uint64(); } bool is_rational() const { return m_infty.is_zero() && m_r.is_rational(); } int64_t get_int64() const { SASSERT(is_int64()); return m_r.get_int64(); } uint64_t get_uint64() const { SASSERT(is_uint64()); return m_r.get_uint64(); } Numeral const& get_numeral() const { return m_r; } rational const& get_rational() const { return m_r.get_rational(); } rational const& get_infinitesimal() const { return m_r.get_infinitesimal(); } rational const& get_infinity() const { return m_infty; } bool is_finite() const { return m_infty.is_zero(); } static inf_eps_rational zero() { return inf_eps_rational(Numeral::zero()); } static inf_eps_rational one() { return inf_eps_rational(Numeral::one()); } static inf_eps_rational minus_one() { return inf_eps_rational(Numeral::minus_one()); } static inf_eps_rational infinity() { return inf_eps_rational(rational::one(), Numeral::zero()); } inf_eps_rational & operator=(const inf_eps_rational & r) { m_infty = r.m_infty; m_r = r.m_r; return *this; } inf_eps_rational & operator=(const Numeral & r) { m_infty.reset(); m_r = r; return *this; } inf_eps_rational & operator+=(const inf_eps_rational & r) { m_infty += r.m_infty; m_r += r.m_r; return *this; } inf_eps_rational & operator-=(const inf_eps_rational & r) { m_infty -= r.m_infty; m_r -= r.m_r; return *this; } inf_eps_rational & operator-=(const inf_rational & r) { m_r -= r; return *this; } inf_eps_rational & operator+=(const inf_rational & r) { m_r += r; return *this; } inf_eps_rational & operator+=(const rational & r) { m_r += r; return *this; } inf_eps_rational & operator-=(const rational & r) { m_r -= r; return *this; } inf_eps_rational & operator*=(const rational & r1) { m_infty *= r1; m_r *= r1; return *this; } inf_eps_rational & operator/=(const rational & r) { m_infty /= r; m_r /= r; return *this; } inf_eps_rational & operator++() { ++m_r; return *this; } const inf_eps_rational operator++(int) { inf_eps_rational tmp(*this); ++(*this); return tmp; } inf_eps_rational & operator--() { --m_r; return *this; } const inf_eps_rational operator--(int) { inf_eps_rational tmp(*this); --(*this); return tmp; } friend inline bool operator==(const inf_eps_rational & r1, const inf_eps_rational & r2) { return r1.m_infty == r2.m_infty && r1.m_r == r2.m_r; } friend inline bool operator==(const rational & r1, const inf_eps_rational & r2) { return r1 == r2.m_infty && r2.m_r.is_zero(); } friend inline bool operator==(const inf_eps_rational & r1, const rational & r2) { return r1.m_infty == r2 && r1.m_r.is_zero(); } friend inline bool operator<(const inf_eps_rational & r1, const inf_eps_rational & r2) { return (r1.m_infty < r2.m_infty) || (r1.m_infty == r2.m_infty && r1.m_r < r2.m_r); } friend inline bool operator<(const rational & r1, const inf_eps_rational & r2) { return r2.m_infty.is_pos() || (r2.m_infty.is_zero() && r1 < r2.m_r); } friend inline bool operator<(const inf_eps_rational & r1, const rational & r2) { return r1.m_infty.is_neg() || (r1.m_infty.is_zero() && r1.m_r < r2); } void neg() { m_infty.neg(); m_r.neg(); } bool is_zero() const { return m_infty.is_zero() && m_r.is_zero(); } bool is_one() const { return m_infty.is_zero() && m_r.is_one(); } bool is_minus_one() const { return m_infty.is_zero() && m_r.is_minus_one(); } bool is_neg() const { return m_infty.is_neg() || (m_infty.is_zero() && m_r.is_neg()); } bool is_pos() const { return m_infty.is_pos() || (m_infty.is_zero() && m_r.is_pos()); } bool is_nonneg() const { return m_infty.is_pos() || (m_infty.is_zero() && m_r.is_nonneg()); } bool is_nonpos() const { return m_infty.is_neg() || (m_infty.is_zero() && m_r.is_nonpos()); } friend inline rational floor(const inf_eps_rational & r) { // SASSERT(r.m_infty.is_zero()); return floor(r.m_r); } friend inline rational ceil(const inf_eps_rational & r) { // SASSERT(r.m_infty.is_zero()); return ceil(r.m_r); } // Perform: this += c * k void addmul(const rational & c, const inf_eps_rational & k) { m_infty.addmul(c, k.m_infty); m_r.addmul(c, k.m_r); } // Perform: this += c * k void submul(const rational & c, const inf_eps_rational & k) { m_infty.submul(c, k.m_infty); m_r.submul(c, k.m_r); } }; template inline bool operator!=(const inf_eps_rational & r1, const inf_eps_rational & r2) { return !operator==(r1, r2); } template inline bool operator!=(const rational & r1, const inf_eps_rational & r2) { return !operator==(r1, r2); } template inline bool operator!=(const inf_eps_rational & r1, const rational & r2) { return !operator==(r1, r2); } template inline bool operator>(const inf_eps_rational & r1, const inf_eps_rational & r2) { return operator<(r2, r1); } template inline bool operator>(const inf_eps_rational & r1, const rational & r2) { return operator<(r2, r1); } template inline bool operator>(const rational & r1, const inf_eps_rational & r2) { return operator<(r2, r1); } template inline bool operator<=(const inf_eps_rational & r1, const inf_eps_rational & r2) { return !operator>(r1, r2); } template inline bool operator<=(const rational & r1, const inf_eps_rational & r2) { return !operator>(r1, r2); } template inline bool operator<=(const inf_eps_rational & r1, const rational & r2) { return !operator>(r1, r2); } template inline bool operator>=(const inf_eps_rational & r1, const inf_eps_rational & r2) { return !operator<(r1, r2); } template inline bool operator>=(const rational & r1, const inf_eps_rational & r2) { return !operator<(r1, r2); } template inline bool operator>=(const inf_eps_rational & r1, const rational & r2) { return !operator<(r1, r2); } template inline inf_eps_rational operator+(const inf_eps_rational & r1, const inf_eps_rational & r2) { return inf_eps_rational(r1) += r2; } template inline inf_eps_rational operator-(const inf_eps_rational & r1, const inf_eps_rational & r2) { return inf_eps_rational(r1) -= r2; } template inline inf_eps_rational operator-(const inf_eps_rational & r) { inf_eps_rational result(r); result.neg(); return result; } template inline inf_eps_rational operator*(const rational & r1, const inf_eps_rational & r2) { inf_eps_rational result(r2); result *= r1; return result; } template inline inf_eps_rational operator*(const inf_eps_rational & r1, const rational & r2) { return r2 * r1; } template inline inf_eps_rational operator/(const inf_eps_rational & r1, const rational & r2) { inf_eps_rational result(r1); result /= r2; return result; } template inline std::ostream & operator<<(std::ostream & target, const inf_eps_rational & r) { target << r.to_string(); return target; } template inline inf_eps_rational abs(const inf_eps_rational & r) { inf_eps_rational result(r); if (result.is_neg()) { result.neg(); } return result; } #endif /* INF_EPS_RATIONAL_H_ */ z3-z3-4.8.7/src/util/inf_int_rational.cpp000066400000000000000000000022171356505360400202200ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: inf_int_rational.cpp Abstract: Rational numbers with infenitesimals Author: Nikolaj Bjorner (nbjorner) 2006-12-05. Revision History: --*/ #include #include "util/inf_int_rational.h" inf_int_rational inf_int_rational::m_zero; inf_int_rational inf_int_rational::m_one; inf_int_rational inf_int_rational::m_minus_one; std::string inf_int_rational::to_string() const { if (m_second == 0) { return m_first.to_string(); } std::ostringstream s; s << "(" << m_first.to_string(); if (m_second < 0) { s << " -e*" << (-m_second) << ")"; } else { s << " +e*" << m_second << ")"; } return s.str(); } void initialize_inf_int_rational() { inf_int_rational::init(); } void inf_int_rational::init() { m_zero.m_first = rational::zero(); m_one.m_first = rational::one(); m_minus_one.m_first = rational::minus_one(); } void finalize_inf_int_rational() { inf_int_rational::finalize(); } void inf_int_rational::finalize() { m_zero.~inf_int_rational(); m_one.~inf_int_rational(); m_minus_one.~inf_int_rational(); } z3-z3-4.8.7/src/util/inf_int_rational.h000066400000000000000000000227231356505360400176710ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: inf_int_rational.h Abstract: Rational numbers with infenitesimals Author: Leonardo de Moura (leonardo) 2006-09-18. Nikolaj Bjorner (nbjorner) 2006-10-24. Revision History: --*/ #ifndef INF_INT_RATIONAL_H_ #define INF_INT_RATIONAL_H_ #include #include #include "util/debug.h" #include "util/vector.h" #include "util/rational.h" class inf_int_rational { static inf_int_rational m_zero; static inf_int_rational m_one; static inf_int_rational m_minus_one; rational m_first; int m_second; public: static void init(); // called from rational::initialize() only static void finalize(); // called from rational::finalize() only unsigned hash() const { return m_first.hash() ^ (static_cast(m_second) + 1); } struct hash_proc { unsigned operator()(inf_int_rational const& r) const { return r.hash(); } }; struct eq_proc { bool operator()(inf_int_rational const& r1, inf_int_rational const& r2) const { return r1 == r2; } }; void swap(inf_int_rational & n) { m_first.swap(n.m_first); std::swap(m_second, n.m_second); } std::string to_string() const; inf_int_rational(): m_first(rational()), m_second(0) {} inf_int_rational(const inf_int_rational & r): m_first(r.m_first), m_second(r.m_second) {} explicit inf_int_rational(int n): m_first(rational(n)), m_second(0) {} explicit inf_int_rational(int n, int d): m_first(rational(n,d)), m_second(0) {} explicit inf_int_rational(rational const& r, bool pos_inf): m_first(r), m_second(pos_inf?1:-1) {} explicit inf_int_rational(rational const& r): m_first(r), m_second(0) {} inf_int_rational(rational const& r, int i): m_first(r), m_second(i) { } ~inf_int_rational() {} /** \brief Set inf_int_rational to 0. */ void reset() { m_first.reset(); m_second = 0; } bool is_int() const { return m_first.is_int() && m_second == 0; } bool is_int64() const { return m_first.is_int64() && m_second == 0; } bool is_uint64() const { return m_first.is_uint64() && m_second == 0; } bool is_rational() const { return m_second == 0; } int64_t get_int64() const { SASSERT(is_int64()); return m_first.get_int64(); } uint64_t get_uint64() const { SASSERT(is_uint64()); return m_first.get_uint64(); } rational const& get_rational() const { return m_first; } rational get_infinitesimal() const { return rational(m_second); } rational const & get_first() const { return m_first; } inf_int_rational & operator=(const inf_int_rational & r) { m_first = r.m_first; m_second = r.m_second; return *this; } inf_int_rational & operator=(const rational & r) { m_first = r; m_second = 0; return *this; } friend inline inf_int_rational numerator(const inf_int_rational & r) { SASSERT(r.m_second == 0); return inf_int_rational(numerator(r.m_first)); } friend inline inf_int_rational denominator(const inf_int_rational & r) { SASSERT(r.m_second == 0); return inf_int_rational(denominator(r.m_first)); } inf_int_rational & operator+=(const inf_int_rational & r) { m_first += r.m_first; m_second += r.m_second; return *this; } inf_int_rational & operator*=(const rational & r) { if (!r.is_int32()) { throw default_exception("multiplication with large rational is not possible"); } m_first *= r; m_second *= r.get_int32(); return *this; } inf_int_rational & operator-=(const inf_int_rational & r) { m_first -= r.m_first; m_second -= r.m_second; return *this; } inf_int_rational & operator+=(const rational & r) { m_first += r; return *this; } inf_int_rational & operator-=(const rational & r) { m_first -= r; return *this; } inf_int_rational & operator++() { ++m_first; return *this; } const inf_int_rational operator++(int) { inf_int_rational tmp(*this); ++(*this); return tmp; } inf_int_rational & operator--() { --m_first; return *this; } const inf_int_rational operator--(int) { inf_int_rational tmp(*this); --(*this); return tmp; } friend inline bool operator==(const inf_int_rational & r1, const inf_int_rational & r2) { return r1.m_first == r2.m_first && r1.m_second == r2.m_second; } friend inline bool operator==(const rational & r1, const inf_int_rational & r2) { return r1 == r2.m_first && r2.m_second == 0; } friend inline bool operator==(const inf_int_rational & r1, const rational & r2) { return r1.m_first == r2 && r1.m_second == 0; } friend inline bool operator<(const inf_int_rational & r1, const inf_int_rational & r2) { return (r1.m_first < r2.m_first) || (r1.m_first == r2.m_first && r1.m_second < r2.m_second); } friend inline bool operator<(const rational & r1, const inf_int_rational & r2) { return (r1 < r2.m_first) || (r1 == r2.m_first && r2.m_second > 0); } friend inline bool operator<(const inf_int_rational & r1, const rational & r2) { return (r1.m_first < r2) || (r1.m_first == r2 && r1.m_second < 0); } void neg() { m_first.neg(); m_second = -m_second; } bool is_zero() const { return m_first.is_zero() && m_second == 0; } bool is_one() const { return m_first.is_one() && m_second == 0; } bool is_minus_one() const { return m_first.is_minus_one() && m_second == 0; } bool is_neg() const { return m_first.is_neg() || (m_first.is_zero() && m_second < 0); } bool is_pos() const { return m_first.is_pos() || (m_first.is_zero() && m_second > 0); } bool is_nonneg() const { return m_first.is_pos() || (m_first.is_zero() && m_second >= 0); } bool is_nonpos() const { return m_first.is_neg() || (m_first.is_zero() && m_second <= 0); } friend inline rational floor(const inf_int_rational & r) { if (r.m_first.is_int()) { if (r.m_second >= 0) { return r.m_first; } return r.m_first - rational::one(); } return floor(r.m_first); } friend inline rational ceil(const inf_int_rational & r) { if (r.m_first.is_int()) { if (r.m_second <= 0) { return r.m_first; } return r.m_first + rational::one(); } return ceil(r.m_first); } static const inf_int_rational & zero() { return m_zero; } static const inf_int_rational & one() { return m_one; } static const inf_int_rational & minus_one() { return m_minus_one; } }; inline bool operator!=(const inf_int_rational & r1, const inf_int_rational & r2) { return !operator==(r1, r2); } inline bool operator!=(const rational & r1, const inf_int_rational & r2) { return !operator==(r1, r2); } inline bool operator!=(const inf_int_rational & r1, const rational & r2) { return !operator==(r1, r2); } inline bool operator>(const inf_int_rational & r1, const inf_int_rational & r2) { return operator<(r2, r1); } inline bool operator>(const inf_int_rational & r1, const rational & r2) { return operator<(r2, r1); } inline bool operator>(const rational & r1, const inf_int_rational & r2) { return operator<(r2, r1); } inline bool operator<=(const inf_int_rational & r1, const inf_int_rational & r2) { return !operator>(r1, r2); } inline bool operator<=(const rational & r1, const inf_int_rational & r2) { return !operator>(r1, r2); } inline bool operator<=(const inf_int_rational & r1, const rational & r2) { return !operator>(r1, r2); } inline bool operator>=(const inf_int_rational & r1, const inf_int_rational & r2) { return !operator<(r1, r2); } inline bool operator>=(const rational & r1, const inf_int_rational & r2) { return !operator<(r1, r2); } inline bool operator>=(const inf_int_rational & r1, const rational & r2) { return !operator<(r1, r2); } inline inf_int_rational operator+(const inf_int_rational & r1, const inf_int_rational & r2) { return inf_int_rational(r1) += r2; } inline inf_int_rational operator*(const rational & r1, const inf_int_rational & r2) { return inf_int_rational(r2) *= r1; } inline inf_int_rational operator-(const inf_int_rational & r1, const inf_int_rational & r2) { return inf_int_rational(r1) -= r2; } inline inf_int_rational operator-(const inf_int_rational & r) { inf_int_rational result(r); result.neg(); return result; } inline std::ostream & operator<<(std::ostream & target, const inf_int_rational & r) { target << r.to_string(); return target; } inline inf_int_rational abs(const inf_int_rational & r) { inf_int_rational result(r); if (result.is_neg()) { result.neg(); } return result; } #endif /* INF_INT_RATIONAL_H_ */ z3-z3-4.8.7/src/util/inf_rational.cpp000066400000000000000000000115311356505360400173450ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: inf_rational.cpp Abstract: Rational numbers with infenitesimals Author: Nikolaj Bjorner (nbjorner) 2006-12-05. Revision History: --*/ #include "util/inf_rational.h" inf_rational inf_rational::m_zero; inf_rational inf_rational::m_one; inf_rational inf_rational::m_minus_one; inf_rational inf_mult(inf_rational const& r1, inf_rational const& r2) { inf_rational result; result.m_first = r1.m_first * r2.m_first; result.m_second = (r1.m_first * r2.m_second) + (r1.m_second * r2.m_first); if (r1.m_second.is_pos() && r2.m_second.is_neg()) { --result.m_second; } else if (r1.m_second.is_neg() && r2.m_second.is_pos()) { --result.m_second; } return result; } inf_rational sup_mult(inf_rational const& r1, inf_rational const& r2) { inf_rational result; result.m_first = r1.m_first * r2.m_first; result.m_second = (r1.m_first * r2.m_second) + (r1.m_second * r2.m_first); if (r1.m_second.is_pos() && r2.m_second.is_pos()) { ++result.m_second; } else if (r1.m_second.is_neg() && r2.m_second.is_neg()) { ++result.m_second; } return result; } // // Find rationals c, x, such that c + epsilon*x <= r1/r2 // // let r1 = a + d_1 // let r2 = b + d_2 // // suppose b != 0: // // r1/b <= r1/r2 // <=> { if b > 0, then r2 > 0, and cross multiplication does not change the sign } // { if b < 0, then r2 < 0, and cross multiplication changes sign twice } // r1 * r2 <= b * r1 // <=> // r1 * (b + d_2) <= r1 * b // <=> // r1 * d_2 <= 0 // // if r1 * d_2 > 0, then r1/(b + sign_of(r1)*1/2*|b|) <= r1/r2 // // Not handled here: // if b = 0, then d_2 != 0 // if r1 * d_2 = 0 then it's 0. // if r1 * d_2 > 0, then result is +oo // if r1 * d_2 < 0, then result is -oo // inf_rational inf_div(inf_rational const& r1, inf_rational const& r2) { SASSERT(!r2.m_first.is_zero()); inf_rational result; if (r2.m_second.is_neg() && r1.is_neg()) { result = r1 / (r2.m_first - (abs(r2.m_first)/rational(2))); } else if (r2.m_second.is_pos() && r1.is_pos()) { result = r1 / (r2.m_first + (abs(r2.m_first)/rational(2))); } else { result = r1 / r2.m_first; } return result; } inf_rational sup_div(inf_rational const& r1, inf_rational const& r2) { SASSERT(!r2.m_first.is_zero()); inf_rational result; if (r2.m_second.is_pos() && r1.is_neg()) { result = r1 / (r2.m_first + (abs(r2.m_first)/rational(2))); } else if (r2.m_second.is_neg() && r1.is_pos()) { result = r1 / (r2.m_first - (abs(r2.m_first)/rational(2))); } else { result = r1 / r2.m_first; } return result; } inf_rational inf_power(inf_rational const& r, unsigned n) { bool is_even = (0 == (n & 0x1)); inf_rational result; if (n == 1) { result = r; } else if ((r.m_second.is_zero()) || (r.m_first.is_pos() && r.m_second.is_pos()) || (r.m_first.is_neg() && r.m_second.is_neg() && is_even)) { result.m_first = r.m_first.expt(n); } else if (is_even) { // 0 will work. } else if (r.m_first.is_zero()) { result.m_first = rational::minus_one(); } else if (r.m_first.is_pos()) { result.m_first = rational(r.m_first - r.m_first/rational(2)).expt(n); } else { result.m_first = rational(r.m_first + r.m_first/rational(2)).expt(n); } return result; } inf_rational sup_power(inf_rational const& r, unsigned n) { bool is_even = (0 == (n & 0x1)); inf_rational result; if (n == 1) { result = r; } else if (r.m_second.is_zero() || (r.m_first.is_pos() && r.m_second.is_neg()) || (r.m_first.is_neg() && r.m_second.is_pos() && is_even)) { result.m_first = r.m_first.expt(n); } else if (r.m_first.is_zero() || (n == 0)) { result.m_first = rational::one(); } else if (r.m_first.is_pos() || is_even) { result.m_first = rational(r.m_first + r.m_first/rational(2)).expt(n); } else { // r (r.m_first) is negative, n is odd. result.m_first = rational(r.m_first - r.m_first/rational(2)).expt(n); } return result; } inf_rational inf_root(inf_rational const& r, unsigned n) { SASSERT(!r.is_neg()); // use 0 return inf_rational(); } inf_rational sup_root(inf_rational const& r, unsigned n) { SASSERT(!r.is_neg()); // use r. return r; } void initialize_inf_rational() { inf_rational::init(); } void inf_rational::init() { m_zero.m_first = rational::zero(); m_one.m_first = rational::one(); m_minus_one.m_first = rational::minus_one(); } void finalize_inf_rational() { inf_rational::finalize(); } void inf_rational::finalize() { m_zero.~inf_rational(); m_one.~inf_rational(); m_minus_one.~inf_rational(); } z3-z3-4.8.7/src/util/inf_rational.h000066400000000000000000000306441356505360400170200ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: inf_rational.h Abstract: Rational numbers with infenitesimals Author: Leonardo de Moura (leonardo) 2006-09-18. Nikolaj Bjorner (nbjorner) 2006-10-24. Revision History: --*/ #ifndef INF_RATIONAL_H_ #define INF_RATIONAL_H_ #include #include #include "util/debug.h" #include "util/vector.h" #include "util/rational.h" class inf_rational { static inf_rational m_zero; static inf_rational m_one; static inf_rational m_minus_one; rational m_first; rational m_second; public: static void init(); // called from rational::initialize() only static void finalize(); // called from rational::finalize() only unsigned hash() const { return m_first.hash() ^ (m_second.hash()+1); } struct hash_proc { unsigned operator()(inf_rational const& r) const { return r.hash(); } }; struct eq_proc { bool operator()(inf_rational const& r1, inf_rational const& r2) const { return r1 == r2; } }; void swap(inf_rational & n) { m_first.swap(n.m_first); m_second.swap(n.m_second); } std::string to_string() const { if (m_second.is_zero()) { return m_first.to_string(); } std::string s = "("; s += m_first.to_string(); if (m_second.is_neg()) { s += " -e*"; } else { s += " +e*"; } s += abs(m_second).to_string(); s += ")"; return s; } inf_rational() {} inf_rational(const inf_rational & r): m_first(r.m_first), m_second(r.m_second) {} explicit inf_rational(int n): m_first(rational(n)), m_second(rational()) {} explicit inf_rational(int n, int d): m_first(rational(n,d)), m_second(rational()) {} explicit inf_rational(rational const& r, bool pos_inf): m_first(r), m_second(pos_inf ? rational::one() : rational::minus_one()) {} inf_rational(rational const& r): m_first(r) { m_second.reset(); } inf_rational(rational const& r, rational const& i): m_first(r), m_second(i) { } ~inf_rational() {} /** \brief Set inf_rational to 0. */ void reset() { m_first.reset(); m_second.reset(); } bool is_int() const { return m_first.is_int() && m_second.is_zero(); } bool is_int64() const { return m_first.is_int64() && m_second.is_zero(); } bool is_uint64() const { return m_first.is_uint64() && m_second.is_zero(); } bool is_rational() const { return m_second.is_zero(); } int64_t get_int64() const { SASSERT(is_int64()); return m_first.get_int64(); } uint64_t get_uint64() const { SASSERT(is_uint64()); return m_first.get_uint64(); } rational const& get_rational() const { return m_first; } rational const& get_infinitesimal() const { return m_second; } rational const & get_first() const { return m_first; } inf_rational & operator=(const inf_rational & r) { m_first = r.m_first; m_second = r.m_second; return *this; } inf_rational & operator=(const rational & r) { m_first = r; m_second.reset(); return *this; } friend inline inf_rational numerator(const inf_rational & r) { SASSERT(r.m_second.is_zero()); return inf_rational(numerator(r.m_first)); } friend inline inf_rational denominator(const inf_rational & r) { SASSERT(r.m_second.is_zero()); return inf_rational(denominator(r.m_first)); } inf_rational & operator+=(const inf_rational & r) { m_first += r.m_first; m_second += r.m_second; return *this; } inf_rational & operator-=(const inf_rational & r) { m_first -= r.m_first; m_second -= r.m_second; return *this; } inf_rational & operator+=(const rational & r) { m_first += r; return *this; } inf_rational & operator-=(const rational & r) { m_first -= r; return *this; } inf_rational & operator*=(const rational & r1) { m_first *= r1; m_second *= r1; return *this; } // // These operations get us out of the realm of inf_rational: // (r1 + e*k1)*(r2 + e*k2) = (r1*r2 + (r1*k2 + r2*k1)*e) // // inf_rational & operator*=(const inf_rational & r) // inf_rational & operator/=(const inf_rational & r) // inf_rational & operator%=(const inf_rational & r) // friend inline inf_rational div(const inf_rational & r1, const inf_rational & r2) // inf_rational expt(int n) // instead, we define operators that approximate some of these operations from above and below. friend inf_rational inf_mult(inf_rational const& r1, inf_rational const& r2); friend inf_rational sup_mult(inf_rational const& r1, inf_rational const& r2); friend inf_rational inf_div(inf_rational const& r1, inf_rational const& r2); friend inf_rational sup_div(inf_rational const& r1, inf_rational const& r2); friend inf_rational inf_power(inf_rational const& r1, unsigned n); friend inf_rational sup_power(inf_rational const& r1, unsigned n); friend inf_rational inf_root(inf_rational const& r1, unsigned n); friend inf_rational sup_root(inf_rational const& r1, unsigned n); inf_rational & operator/=(const rational & r) { m_first /= r; m_second /= r; return *this; } friend inline inf_rational operator*(const rational & r1, const inf_rational & r2); friend inline inf_rational operator*(const inf_rational & r1, const rational & r2); friend inline inf_rational operator/(const inf_rational & r1, const rational & r2); inf_rational & operator++() { ++m_first; return *this; } const inf_rational operator++(int) { inf_rational tmp(*this); ++(*this); return tmp; } inf_rational & operator--() { --m_first; return *this; } const inf_rational operator--(int) { inf_rational tmp(*this); --(*this); return tmp; } friend inline bool operator==(const inf_rational & r1, const inf_rational & r2) { return r1.m_first == r2.m_first && r1.m_second == r2.m_second; } friend inline bool operator==(const rational & r1, const inf_rational & r2) { return r1 == r2.m_first && r2.m_second.is_zero(); } friend inline bool operator==(const inf_rational & r1, const rational & r2) { return r1.m_first == r2 && r1.m_second.is_zero(); } friend inline bool operator<(const inf_rational & r1, const inf_rational & r2) { return (r1.m_first < r2.m_first) || (r1.m_first == r2.m_first && r1.m_second < r2.m_second); } friend inline bool operator<(const rational & r1, const inf_rational & r2) { return (r1 < r2.m_first) || (r1 == r2.m_first && r2.m_second.is_pos()); } friend inline bool operator<(const inf_rational & r1, const rational & r2) { return (r1.m_first < r2) || (r1.m_first == r2 && r1.m_second.is_neg()); } void neg() { m_first.neg(); m_second.neg(); } bool is_zero() const { return m_first.is_zero() && m_second.is_zero(); } bool is_one() const { return m_first.is_one() && m_second.is_zero(); } bool is_minus_one() const { return m_first.is_minus_one() && m_second.is_zero(); } bool is_neg() const { return m_first.is_neg() || (m_first.is_zero() && m_second.is_neg()); } bool is_pos() const { return m_first.is_pos() || (m_first.is_zero() && m_second.is_pos()); } bool is_nonneg() const { return m_first.is_pos() || (m_first.is_zero() && m_second.is_nonneg()); } bool is_nonpos() const { return m_first.is_neg() || (m_first.is_zero() && m_second.is_nonpos()); } friend inline rational floor(const inf_rational & r) { if (r.m_first.is_int()) { if (r.m_second.is_nonneg()) { return r.m_first; } return r.m_first - rational::one(); } return floor(r.m_first); } friend inline rational ceil(const inf_rational & r) { if (r.m_first.is_int()) { if (r.m_second.is_nonpos()) { return r.m_first; } return r.m_first + rational::one(); } return ceil(r.m_first); } static const inf_rational & zero() { return m_zero; } static const inf_rational & one() { return m_one; } static const inf_rational & minus_one() { return m_minus_one; } // Perform: this += c * k void addmul(const rational & c, const inf_rational & k) { m_first.addmul(c, k.m_first); m_second.addmul(c, k.m_second); } // Perform: this += c * k void submul(const rational & c, const inf_rational & k) { m_first.submul(c, k.m_first); m_second.submul(c, k.m_second); } }; inline bool operator!=(const inf_rational & r1, const inf_rational & r2) { return !operator==(r1, r2); } inline bool operator!=(const rational & r1, const inf_rational & r2) { return !operator==(r1, r2); } inline bool operator!=(const inf_rational & r1, const rational & r2) { return !operator==(r1, r2); } inline bool operator>(const inf_rational & r1, const inf_rational & r2) { return operator<(r2, r1); } inline bool operator>(const inf_rational & r1, const rational & r2) { return operator<(r2, r1); } inline bool operator>(const rational & r1, const inf_rational & r2) { return operator<(r2, r1); } inline bool operator<=(const inf_rational & r1, const inf_rational & r2) { return !operator>(r1, r2); } inline bool operator<=(const rational & r1, const inf_rational & r2) { return !operator>(r1, r2); } inline bool operator<=(const inf_rational & r1, const rational & r2) { return !operator>(r1, r2); } inline bool operator>=(const inf_rational & r1, const inf_rational & r2) { return !operator<(r1, r2); } inline bool operator>=(const rational & r1, const inf_rational & r2) { return !operator<(r1, r2); } inline bool operator>=(const inf_rational & r1, const rational & r2) { return !operator<(r1, r2); } inline inf_rational operator+(const inf_rational & r1, const inf_rational & r2) { return inf_rational(r1) += r2; } inline inf_rational operator-(const inf_rational & r1, const inf_rational & r2) { return inf_rational(r1) -= r2; } inline inf_rational operator-(const inf_rational & r) { inf_rational result(r); result.neg(); return result; } inline inf_rational operator*(const rational & r1, const inf_rational & r2) { inf_rational result(r2); result.m_first *= r1; result.m_second *= r1; return result; } inline inf_rational operator*(const inf_rational & r1, const rational & r2) { return r2 * r1; } inline inf_rational operator/(const inf_rational & r1, const rational & r2) { inf_rational result(r1); result.m_first /= r2; result.m_second /= r2; return result; } #if 0 inf_rational inf_mult(inf_rational const& r1, inf_rational const& r2); inf_rational sup_mult(inf_rational const& r1, inf_rational const& r2); inf_rational inf_div(inf_rational const& r1, inf_rational const& r2); inf_rational sup_div(inf_rational const& r1, inf_rational const& r2); inf_rational inf_power(inf_rational const& r1, unsigned n); inf_rational sup_power(inf_rational const& r1, unsigned n); inf_rational inf_root(inf_rational const& r1, unsigned n); inf_rational sup_root(inf_rational const& r1, unsigned n); #endif // // inline inf_rational operator/(const inf_rational & r1, const inf_rational & r2) // inline inf_rational operator%(const inf_rational & r1, const inf_rational & r2) // inf_rational gcd(const inf_rational & r1, const inf_rational & r2); // inf_rational lcm(const inf_rational & r1, const inf_rational & r2); inline std::ostream & operator<<(std::ostream & target, const inf_rational & r) { target << r.to_string(); return target; } inline inf_rational abs(const inf_rational & r) { inf_rational result(r); if (result.is_neg()) { result.neg(); } return result; } #endif /* INF_RATIONAL_H_ */ z3-z3-4.8.7/src/util/inf_s_integer.cpp000066400000000000000000000005361356505360400175160ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: inf_s_integer.cpp Abstract: Author: Leonardo de Moura (leonardo) 2007-06-16. Revision History: --*/ #include "util/inf_s_integer.h" inf_s_integer inf_s_integer::m_zero(0); inf_s_integer inf_s_integer::m_one(1); inf_s_integer inf_s_integer::m_minus_one(-1); z3-z3-4.8.7/src/util/inf_s_integer.h000066400000000000000000000242651356505360400171700ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: inf_s_integer.h Abstract: Author: Leonardo de Moura (leonardo) 2007-06-11. Revision History: --*/ #ifndef INF_S_INTEGER_H_ #define INF_S_INTEGER_H_ #include "util/s_integer.h" #include "util/rational.h" class inf_s_integer { static inf_s_integer m_zero; static inf_s_integer m_one; static inf_s_integer m_minus_one; int m_first; int m_second; public: unsigned hash() const { return m_first ^ (m_second + 1); } struct hash_proc { unsigned operator()(inf_s_integer const& r) const { return r.hash(); } }; struct eq_proc { bool operator()(inf_s_integer const& r1, inf_s_integer const& r2) const { return r1 == r2; } }; void swap(inf_s_integer & n) { std::swap(m_first, n.m_first); std::swap(m_second, n.m_second); } std::string to_string() const; inf_s_integer():m_first(0), m_second(0) {} inf_s_integer(const inf_s_integer & r):m_first(r.m_first), m_second(r.m_second) {} explicit inf_s_integer(int n):m_first(n), m_second(0) {} explicit inf_s_integer(int n, int d): m_first(n), m_second(0) { SASSERT(d == 1); } explicit inf_s_integer(s_integer const& r, bool pos_inf):m_first(r.get_int()), m_second(pos_inf ? 1 : -1) {} explicit inf_s_integer(s_integer const& r):m_first(r.get_int()), m_second(0) {} explicit inf_s_integer(rational const& r):m_first(static_cast(r.get_int64())), m_second(0) {} inf_s_integer(s_integer const& r, s_integer const& i):m_first(r.get_int()), m_second(i.get_int()) {} void reset() { m_first = 0; m_second = 0; } bool is_int() const { return m_second == 0; } bool is_int64() const { return m_second == 0; } bool is_uint64() const { return m_second == 0; } bool is_rational() const { return m_second == 0; } int64_t get_int64() const { return m_first; } uint64_t get_uint64() const { return m_first; } s_integer get_rational() const { return s_integer(m_first); } s_integer get_infinitesimal() const { return s_integer(m_second); } inf_s_integer & operator=(const inf_s_integer & r) { m_first = r.m_first; m_second = r.m_second; return *this; } inf_s_integer & operator=(const rational & r) { m_first = static_cast(r.get_int64()); m_second = 0; return *this; } inf_s_integer & operator=(const s_integer & r) { m_first = r.get_int(); m_second = 0; return *this; } friend inline inf_s_integer numerator(const inf_s_integer & r) { SASSERT(r.m_second == 0); return inf_s_integer(r.m_first); } friend inline inf_s_integer denominator(const inf_s_integer & r) { SASSERT(r.m_second == 0); return inf_s_integer(1); } inf_s_integer & operator+=(const inf_s_integer & r) { m_first += r.m_first; m_second += r.m_second; return *this; } inf_s_integer & operator-=(const inf_s_integer & r) { m_first -= r.m_first; m_second -= r.m_second; return *this; } inf_s_integer & operator+=(const s_integer & r) { m_first += r.get_int(); return *this; } inf_s_integer & operator-=(const s_integer & r) { m_first -= r.get_int(); return *this; } inf_s_integer & operator*=(const s_integer & r1) { m_first *= r1.get_int(); m_second *= r1.get_int(); return *this; } // friend inf_s_integer inf_mult(inf_s_integer const& r1, inf_s_integer const& r2); // friend inf_s_integer sup_mult(inf_s_integer const& r1, inf_s_integer const& r2); // friend inf_s_integer inf_div(inf_s_integer const& r1, inf_s_integer const& r2); // friend inf_s_integer sup_div(inf_s_integer const& r1, inf_s_integer const& r2); // friend inf_s_integer inf_power(inf_s_integer const& r1, unsigned n); // friend inf_s_integer sup_power(inf_s_integer const& r1, unsigned n); // friend inf_s_integer inf_root(inf_s_integer const& r1, unsigned n); // friend inf_s_integer sup_root(inf_s_integer const& r1, unsigned n); inf_s_integer & operator/=(const s_integer & r) { m_first /= r.get_int(); m_second /= r.get_int(); return *this; } friend inline inf_s_integer operator*(const s_integer & r1, const inf_s_integer & r2); friend inline inf_s_integer operator/(const inf_s_integer & r1, const s_integer & r2); inf_s_integer & operator++() { ++m_first; return *this; } const inf_s_integer operator++(int) { inf_s_integer tmp(*this); ++(*this); return tmp; } inf_s_integer & operator--() { --m_first; return *this; } const inf_s_integer operator--(int) { inf_s_integer tmp(*this); --(*this); return tmp; } friend inline bool operator==(const inf_s_integer & r1, const inf_s_integer & r2) { return r1.m_first == r2.m_first && r1.m_second == r2.m_second; } friend inline bool operator==(const s_integer & r1, const inf_s_integer & r2) { return r1.get_int() == r2.m_first && r2.m_second == 0; } friend inline bool operator==(const inf_s_integer & r1, const s_integer & r2) { return r1.m_first == r2.get_int() && r1.m_second == 0; } friend inline bool operator<(const inf_s_integer & r1, const inf_s_integer & r2) { return (r1.m_first < r2.m_first) || (r1.m_first == r2.m_first && r1.m_second < r2.m_second); } friend inline bool operator<(const s_integer & r1, const inf_s_integer & r2) { return (r1.get_int() < r2.m_first) || (r1.get_int() == r2.m_first && r2.m_second > 0); } friend inline bool operator<(const inf_s_integer & r1, const s_integer & r2) { return (r1.m_first < r2.get_int()) || (r1.m_first == r2.get_int() && r1.m_second < 0); } void neg() { m_first = -m_first; m_second = -m_second; } bool is_zero() const { return m_first == 0 && m_second == 0; } bool is_one() const { return m_first == 1 && m_second == 0; } bool is_minus_one() const { return m_first == -1 && m_second == 0; } bool is_neg() const { return m_first < 0 || (m_first == 0 && m_second < 0); } bool is_pos() const { return m_first > 0 || (m_first == 0 && m_second > 0); } bool is_nonneg() const { return m_first > 0 || (m_first == 0 && m_second >= 0); } bool is_nonpos() const { return m_first < 0 || (m_first == 0 && m_second <= 0); } friend inline s_integer floor(const inf_s_integer & r) { if (r.m_second >= 0) { return s_integer(r.m_first); } return s_integer(r.m_first - 1); } friend inline s_integer ceil(const inf_s_integer & r) { if (r.m_second <= 0) { return s_integer(r.m_first); } return s_integer(r.m_first + 1); } static const inf_s_integer & zero() { return m_zero; } static const inf_s_integer & one() { return m_one; } static const inf_s_integer & minus_one() { return m_minus_one; } // Perform: this += c * k void addmul(const s_integer & c, const inf_s_integer & k) { m_first += c.get_int() * k.m_first; m_second += c.get_int() * k.m_second; } // Perform: this += c * k void submul(const s_integer & c, const inf_s_integer & k) { m_first -= c.get_int() * k.m_first; m_second -= c.get_int() * k.m_second; } friend inline std::ostream & operator<<(std::ostream & target, const inf_s_integer & r) { if (r.m_second == 0) { target << r.m_first; } else if (r.m_second < 0) { target << "(" << r.m_first << " -e*" << r.m_second << ")"; } else { target << "(" << r.m_first << " +e*" << r.m_second << ")"; } return target; } }; inline bool operator!=(const inf_s_integer & r1, const inf_s_integer & r2) { return !operator==(r1, r2); } inline bool operator!=(const s_integer & r1, const inf_s_integer & r2) { return !operator==(r1, r2); } inline bool operator!=(const inf_s_integer & r1, const s_integer & r2) { return !operator==(r1, r2); } inline bool operator>(const inf_s_integer & r1, const inf_s_integer & r2) { return operator<(r2, r1); } inline bool operator>(const inf_s_integer & r1, const s_integer & r2) { return operator<(r2, r1); } inline bool operator>(const s_integer & r1, const inf_s_integer & r2) { return operator<(r2, r1); } inline bool operator<=(const inf_s_integer & r1, const inf_s_integer & r2) { return !operator>(r1, r2); } inline bool operator<=(const s_integer & r1, const inf_s_integer & r2) { return !operator>(r1, r2); } inline bool operator<=(const inf_s_integer & r1, const s_integer & r2) { return !operator>(r1, r2); } inline bool operator>=(const inf_s_integer & r1, const inf_s_integer & r2) { return !operator<(r1, r2); } inline bool operator>=(const s_integer & r1, const inf_s_integer & r2) { return !operator<(r1, r2); } inline bool operator>=(const inf_s_integer & r1, const s_integer & r2) { return !operator<(r1, r2); } inline inf_s_integer operator+(const inf_s_integer & r1, const inf_s_integer & r2) { return inf_s_integer(r1) += r2; } inline inf_s_integer operator-(const inf_s_integer & r1, const inf_s_integer & r2) { return inf_s_integer(r1) -= r2; } inline inf_s_integer operator-(const inf_s_integer & r) { inf_s_integer result(r); result.neg(); return result; } inline inf_s_integer operator*(const s_integer & r1, const inf_s_integer & r2) { inf_s_integer result(r2); result.m_first *= r1.get_int(); result.m_second *= r1.get_int(); return result; } inline inf_s_integer operator/(const inf_s_integer & r1, const s_integer & r2) { inf_s_integer result(r1); result.m_first /= r2.get_int(); result.m_second /= r2.get_int(); return result; } inline inf_s_integer abs(const inf_s_integer & r) { inf_s_integer result(r); if (result.is_neg()) { result.neg(); } return result; } #endif /* INF_S_INTEGER_H_ */ z3-z3-4.8.7/src/util/lbool.cpp000066400000000000000000000011241356505360400160040ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: lbool.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-02-08. Revision History: --*/ #include "util/lbool.h" std::ostream & operator<<(std::ostream & out, lbool b) { switch(b) { case l_false: return out << "l_false"; case l_true: return out << "l_true"; default: return out << "l_undef"; } } char const * to_sat_str(lbool l) { switch (l) { case l_false: return "unsatisfiable"; case l_true: return "satisfiable"; default: return "unknown"; } } z3-z3-4.8.7/src/util/lbool.h000066400000000000000000000012661356505360400154600ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: lbool.h Abstract: Lifted boolean Author: Leonardo de Moura (leonardo) 2008-02-08. Revision History: --*/ #ifndef LBOOL_H_ #define LBOOL_H_ #include "util/util.h" typedef enum { l_false = -1, l_undef, l_true } lbool; inline lbool operator~(lbool lb) { return static_cast(-static_cast(lb)); } inline lbool to_lbool(bool b) { return static_cast(static_cast(b)*2-1); } std::ostream & operator<<(std::ostream & out, lbool b); /** \brief Convert l_true -> satisfiable, l_false -> unsatisfiable, and l_undef -> unknown. */ char const * to_sat_str(lbool l); #endif /* LBOOL_H_ */ z3-z3-4.8.7/src/util/list.h000066400000000000000000000042261356505360400153230ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: list.h Abstract: Simple list data structure. It is meant to be used with region allocators. Author: Leonardo de Moura (leonardo) 2007-07-10. Revision History: --*/ #ifndef LIST_H_ #define LIST_H_ #include "util/buffer.h" #include "util/region.h" template class list { T m_head; list * m_tail; public: list(T const & h, list * t = nullptr): m_head(h), m_tail(t) { } T const & head() const { return m_head; } list * tail() const { return m_tail; } T & head() { return m_head; } list * & tail() { return m_tail; } class iterator { list const * m_curr; public: iterator(list const * c = 0):m_curr(c) {} T const & operator*() const { return m_curr->head(); } iterator & operator++() { m_curr = m_curr->tail(); return *this; } iterator operator++(int) { iterator tmp = *this; ++*this; return tmp; } bool operator==(iterator const & it) { return m_curr == it.m_curr; } bool operator!=(iterator const & it) { return m_curr != it.m_curr; } }; typedef iterator const_iterator; iterator begin() const { return iterator(this); } iterator end() const { return iterator(0); } }; /** \brief Return the list length. */ template unsigned length(list * l) { unsigned r = 0; while(l) { l = l->tail(); r++; } return r; } /** \brief Non destructive append operation. The new nodes are allocated using the given region allocator. */ template list * append(region & r, list * l1, list * l2) { if (l2 == nullptr) { return l1; } ptr_buffer > buffer; while (l1) { buffer.push_back(l1); l1 = l1->tail(); } list * result = l2; typename ptr_buffer >::const_iterator it = buffer.end(); typename ptr_buffer >::const_iterator begin = buffer.begin(); while (it != begin) { --it; list * curr = *it; result = new (r) list(curr->head(), result); } return result; } #endif /* LIST_H_ */ z3-z3-4.8.7/src/util/lp/000077500000000000000000000000001356505360400146065ustar00rootroot00000000000000z3-z3-4.8.7/src/util/lp/CMakeLists.txt000066400000000000000000000015131356505360400173460ustar00rootroot00000000000000z3_add_component(lp SOURCES binary_heap_priority_queue.cpp binary_heap_upair_queue.cpp lp_bound_propagator.cpp core_solver_pretty_printer.cpp dense_matrix.cpp eta_matrix.cpp gomory.cpp indexed_vector.cpp int_solver.cpp lar_solver.cpp lar_core_solver.cpp lp_core_solver_base.cpp lp_dual_core_solver.cpp lp_dual_simplex.cpp lp_primal_core_solver.cpp lp_primal_simplex.cpp lp_settings.cpp lp_solver.cpp lu.cpp lp_utils.cpp matrix.cpp nra_solver.cpp permutation_matrix.cpp random_updater.cpp row_eta_matrix.cpp scaler.cpp square_dense_submatrix.cpp square_sparse_matrix.cpp static_matrix.cpp COMPONENT_DEPENDENCIES util polynomial nlsat PYG_FILES lp_params.pyg ) include_directories(${src_SOURCE_DIR}) z3-z3-4.8.7/src/util/lp/active_set.h000066400000000000000000000034731356505360400171140ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Nikolaj Bjorner (nbjorner) Lev Nachmanson (levnach) Revision History: --*/ #pragma once #include "util/lp/binary_heap_priority_queue.h" namespace lp { class active_set { std::unordered_set m_cs; binary_heap_priority_queue m_q; std::unordered_map m_id_to_constraint; public: std::unordered_set cs() const { return m_cs;} bool contains(const constraint* c) const { return m_id_to_constraint.find(c->id()) != m_id_to_constraint.end(); } bool is_empty() const { return m_cs.size() == 0; } // low priority will be dequeued first void add_constraint(constraint* c, int priority) { if (contains(c)) return; m_cs.insert(c); m_id_to_constraint[c->id()] = c; m_q.enqueue(c->id(), priority); } void clear() { m_cs.clear(); m_id_to_constraint.clear(); m_q.clear(); } constraint* remove_constraint() { if (m_cs.size() == 0) return nullptr; unsigned id = m_q.dequeue(); auto it = m_id_to_constraint.find(id); lp_assert(it != m_id_to_constraint.end()); constraint* c = it->second; m_cs.erase(c); m_id_to_constraint.erase(it); return c; } unsigned size() const { return static_cast(m_cs.size()); } void remove_constraint(constraint * c) { if (! contains(c)) return; m_cs.erase(c); m_id_to_constraint.erase(c->id()); m_q.remove(c->id()); } }; } z3-z3-4.8.7/src/util/lp/binary_heap_priority_queue.cpp000066400000000000000000000032421356505360400227410ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #include "util/lp/numeric_pair.h" #include "util/lp/binary_heap_priority_queue_def.h" namespace lp { template binary_heap_priority_queue::binary_heap_priority_queue(unsigned int); template unsigned binary_heap_priority_queue::dequeue(); template void binary_heap_priority_queue::enqueue(unsigned int, int const&); template void binary_heap_priority_queue::enqueue(unsigned int, double const&); template void binary_heap_priority_queue::enqueue(unsigned int, mpq const&); template void binary_heap_priority_queue::remove(unsigned int); template unsigned binary_heap_priority_queue >::dequeue(); template unsigned binary_heap_priority_queue::dequeue(); template unsigned binary_heap_priority_queue::dequeue(); template void binary_heap_priority_queue >::enqueue(unsigned int, numeric_pair const&); template void binary_heap_priority_queue >::resize(unsigned int); template void lp::binary_heap_priority_queue::resize(unsigned int); template binary_heap_priority_queue::binary_heap_priority_queue(unsigned int); template void binary_heap_priority_queue::resize(unsigned int); template unsigned binary_heap_priority_queue::dequeue(); template void binary_heap_priority_queue::enqueue(unsigned int, unsigned int const&); template void binary_heap_priority_queue::remove(unsigned int); template void lp::binary_heap_priority_queue::resize(unsigned int); } z3-z3-4.8.7/src/util/lp/binary_heap_priority_queue.h000066400000000000000000000044341356505360400224120ustar00rootroot00000000000000 /*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #pragma once #include "util/vector.h" #include "util/debug.h" #include "util/lp/lp_utils.h" namespace lp { // the elements with the smallest priority are dequeued first template class binary_heap_priority_queue { vector m_priorities; // indexing for A starts from 1 vector m_heap; // keeps the elements of the queue vector m_heap_inverse; // o = m_heap[m_heap_inverse[o]] unsigned m_heap_size; // is is the child place in heap void swap_with_parent(unsigned i); void put_at(unsigned i, unsigned h); void decrease_priority(unsigned o, T newPriority); public: #ifdef Z3DEBUG bool is_consistent() const; #endif public: void remove(unsigned o); unsigned size() const { return m_heap_size; } binary_heap_priority_queue(): m_heap(1), m_heap_size(0) {} // the empty constructror // n is the initial queue capacity. // The capacity will be enlarged each time twice if needed binary_heap_priority_queue(unsigned n); void clear() { for (unsigned i = 0; i < m_heap_size; i++) { unsigned o = m_heap[i+1]; m_heap_inverse[o] = -1; } m_heap_size = 0; } void resize(unsigned n); void put_to_heap(unsigned i, unsigned o); void enqueue_new(unsigned o, const T& priority); // This method can work with an element that is already in the queue. // In this case the priority will be changed and the queue adjusted. void enqueue(unsigned o, const T & priority); void change_priority_for_existing(unsigned o, const T & priority); T get_priority(unsigned o) const { return m_priorities[o]; } bool is_empty() const { return m_heap_size == 0; } /// return the first element of the queue and removes it from the queue unsigned dequeue_and_get_priority(T & priority); void fix_heap_under(unsigned i); void put_the_last_at_the_top_and_fix_the_heap(); /// return the first element of the queue and removes it from the queue unsigned dequeue(); unsigned peek() const { lp_assert(m_heap_size > 0); return m_heap[1]; } void print(std::ostream & out); }; } z3-z3-4.8.7/src/util/lp/binary_heap_priority_queue_def.h000066400000000000000000000143621356505360400232310ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #include "util/vector.h" #include "util/lp/binary_heap_priority_queue.h" namespace lp { // "i" is the child's place in the heap template void binary_heap_priority_queue::swap_with_parent(unsigned i) { unsigned parent = m_heap[i >> 1]; put_at(i >> 1, m_heap[i]); put_at(i, parent); } template void binary_heap_priority_queue::put_at(unsigned i, unsigned h) { m_heap[i] = h; m_heap_inverse[h] = i; } template void binary_heap_priority_queue::decrease_priority(unsigned o, T newPriority) { m_priorities[o] = newPriority; int i = m_heap_inverse[o]; while (i > 1) { if (m_priorities[m_heap[i]] < m_priorities[m_heap[i >> 1]]) swap_with_parent(i); else break; i >>= 1; } } #ifdef Z3DEBUG template bool binary_heap_priority_queue::is_consistent() const { for (int i = 0; i < m_heap_inverse.size(); i++) { int i_index = m_heap_inverse[i]; lp_assert(i_index <= static_cast(m_heap_size)); lp_assert(i_index == -1 || m_heap[i_index] == i); } for (unsigned i = 1; i < m_heap_size; i++) { unsigned ch = i << 1; for (int k = 0; k < 2; k++) { if (ch > m_heap_size) break; if (!(m_priorities[m_heap[i]] <= m_priorities[m_heap[ch]])){ return false; } ch++; } } return true; } #endif template void binary_heap_priority_queue::remove(unsigned o) { T priority_of_o = m_priorities[o]; int o_in_heap = m_heap_inverse[o]; if (o_in_heap == -1) { return; // nothing to do } lp_assert(static_cast(o_in_heap) <= m_heap_size); if (static_cast(o_in_heap) < m_heap_size) { put_at(o_in_heap, m_heap[m_heap_size--]); if (m_priorities[m_heap[o_in_heap]] > priority_of_o) { fix_heap_under(o_in_heap); } else { // we need to propagate the m_heap[o_in_heap] up unsigned i = o_in_heap; while (i > 1) { unsigned ip = i >> 1; if (m_priorities[m_heap[i]] < m_priorities[m_heap[ip]]) swap_with_parent(i); else break; i = ip; } } } else { lp_assert(static_cast(o_in_heap) == m_heap_size); m_heap_size--; } m_heap_inverse[o] = -1; // lp_assert(is_consistent()); } // n is the initial queue capacity. // The capacity will be enlarged two times automatically if needed template binary_heap_priority_queue::binary_heap_priority_queue(unsigned n) : m_priorities(n), m_heap(n + 1), // because the indexing for A starts from 1 m_heap_inverse(n, -1), m_heap_size(0) { } template void binary_heap_priority_queue::resize(unsigned n) { m_priorities.resize(n); m_heap.resize(n + 1); m_heap_inverse.resize(n, -1); } template void binary_heap_priority_queue::put_to_heap(unsigned i, unsigned o) { m_heap[i] = o; m_heap_inverse[o] = i; } template void binary_heap_priority_queue::enqueue_new(unsigned o, const T& priority) { m_heap_size++; int i = m_heap_size; lp_assert(o < m_priorities.size()); m_priorities[o] = priority; put_at(i, o); while (i > 1 && m_priorities[m_heap[i >> 1]] > priority) { swap_with_parent(i); i >>= 1; } } // This method can work with an element that is already in the queue. // In this case the priority will be changed and the queue adjusted. template void binary_heap_priority_queue::enqueue(unsigned o, const T & priority) { if (o >= m_priorities.size()) { if (o == 0) resize(2); else resize(o << 1); // make the size twice larger } if (m_heap_inverse[o] == -1) enqueue_new(o, priority); else change_priority_for_existing(o, priority); } template void binary_heap_priority_queue::change_priority_for_existing(unsigned o, const T & priority) { if (m_priorities[o] > priority) { decrease_priority(o, priority); } else { m_priorities[o] = priority; fix_heap_under(m_heap_inverse[o]); } } /// return the first element of the queue and removes it from the queue template unsigned binary_heap_priority_queue::dequeue_and_get_priority(T & priority) { lp_assert(m_heap_size != 0); int ret = m_heap[1]; priority = m_priorities[ret]; put_the_last_at_the_top_and_fix_the_heap(); return ret; } template void binary_heap_priority_queue::fix_heap_under(unsigned i) { while (true) { unsigned smallest = i; unsigned l = i << 1; if (l <= m_heap_size && m_priorities[m_heap[l]] < m_priorities[m_heap[i]]) smallest = l; unsigned r = l + 1; if (r <= m_heap_size && m_priorities[m_heap[r]] < m_priorities[m_heap[smallest]]) smallest = r; if (smallest != i) swap_with_parent(smallest); else break; i = smallest; } } template void binary_heap_priority_queue::put_the_last_at_the_top_and_fix_the_heap() { if (m_heap_size > 1) { put_at(1, m_heap[m_heap_size--]); fix_heap_under(1); } else { m_heap_size--; } } /// return the first element of the queue and removes it from the queue template unsigned binary_heap_priority_queue::dequeue() { lp_assert(m_heap_size > 0); int ret = m_heap[1]; put_the_last_at_the_top_and_fix_the_heap(); m_heap_inverse[ret] = -1; return ret; } template void binary_heap_priority_queue::print(std::ostream & out) { vector index; vector prs; while (size()) { T prior; int j = dequeue_and_get_priority(prior); index.push_back(j); prs.push_back(prior); out << "(" << j << ", " << prior << ")"; } out << std::endl; // restore the queue for (int i = 0; i < index.size(); i++) enqueue(index[i], prs[i]); } } z3-z3-4.8.7/src/util/lp/binary_heap_upair_queue.cpp000066400000000000000000000021141356505360400221750ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #include "util/lp/binary_heap_upair_queue_def.h" namespace lp { template binary_heap_upair_queue::binary_heap_upair_queue(unsigned int); template binary_heap_upair_queue::binary_heap_upair_queue(unsigned int); template unsigned binary_heap_upair_queue::dequeue_available_spot(); template unsigned binary_heap_upair_queue::dequeue_available_spot(); template void binary_heap_upair_queue::enqueue(unsigned int, unsigned int, int const&); template void binary_heap_upair_queue::remove(unsigned int, unsigned int); template void binary_heap_upair_queue::remove(unsigned int, unsigned int); template void binary_heap_upair_queue::dequeue(unsigned int&, unsigned int&); template void binary_heap_upair_queue::enqueue(unsigned int, unsigned int, unsigned int const&); template void binary_heap_upair_queue::dequeue(unsigned int&, unsigned int&); } z3-z3-4.8.7/src/util/lp/binary_heap_upair_queue.h000066400000000000000000000030131356505360400216410ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #pragma once #include #include #include #include "util/vector.h" #include #include #include "util/lp/binary_heap_priority_queue.h" typedef std::pair upair; namespace lp { template class binary_heap_upair_queue { binary_heap_priority_queue m_q; std::unordered_map m_pairs_to_index; svector m_pairs; // inverse to index svector m_available_spots; public: binary_heap_upair_queue(unsigned size); unsigned dequeue_available_spot(); bool is_empty() const { return m_q.is_empty(); } unsigned size() const {return m_q.size(); } bool contains(unsigned i, unsigned j) const { return m_pairs_to_index.find(std::make_pair(i, j)) != m_pairs_to_index.end(); } void remove(unsigned i, unsigned j); bool ij_index_is_new(unsigned ij_index) const; void enqueue(unsigned i, unsigned j, const T & priority); void dequeue(unsigned & i, unsigned &j); T get_priority(unsigned i, unsigned j) const; #ifdef Z3DEBUG bool pair_to_index_is_a_bijection() const; bool available_spots_are_correct() const; bool is_correct() const { return m_q.is_consistent() && pair_to_index_is_a_bijection() && available_spots_are_correct(); } #endif void resize(unsigned size) { m_q.resize(size); } }; } z3-z3-4.8.7/src/util/lp/binary_heap_upair_queue_def.h000066400000000000000000000066621356505360400224740ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #include #include "util/lp/lp_utils.h" #include "util/lp/binary_heap_upair_queue.h" namespace lp { template binary_heap_upair_queue::binary_heap_upair_queue(unsigned size) : m_q(size), m_pairs(size) { for (unsigned i = 0; i < size; i++) m_available_spots.push_back(i); } template unsigned binary_heap_upair_queue::dequeue_available_spot() { lp_assert(m_available_spots.empty() == false); unsigned ret = m_available_spots.back(); m_available_spots.pop_back(); return ret; } template void binary_heap_upair_queue::remove(unsigned i, unsigned j) { upair p(i, j); auto it = m_pairs_to_index.find(p); if (it == m_pairs_to_index.end()) return; // nothing to do m_q.remove(it->second); m_available_spots.push_back(it->second); m_pairs_to_index.erase(it); } template bool binary_heap_upair_queue::ij_index_is_new(unsigned ij_index) const { for (auto it : m_pairs_to_index) { if (it.second == ij_index) return false; } return true; } template void binary_heap_upair_queue::enqueue(unsigned i, unsigned j, const T & priority) { upair p(i, j); auto it = m_pairs_to_index.find(p); unsigned ij_index; if (it == m_pairs_to_index.end()) { // it is a new pair, let us find a spot for it if (m_available_spots.empty()) { // we ran out of empty spots unsigned size_was = static_cast(m_pairs.size()); unsigned new_size = size_was << 1; for (unsigned i = size_was; i < new_size; i++) m_available_spots.push_back(i); m_pairs.resize(new_size); } ij_index = dequeue_available_spot(); // lp_assert(ij_indexsecond; } m_q.enqueue(ij_index, priority); } template void binary_heap_upair_queue::dequeue(unsigned & i, unsigned &j) { lp_assert(!m_q.is_empty()); unsigned ij_index = m_q.dequeue(); upair & p = m_pairs[ij_index]; i = p.first; j = p.second; m_available_spots.push_back(ij_index); m_pairs_to_index.erase(p); } template T binary_heap_upair_queue::get_priority(unsigned i, unsigned j) const { auto it = m_pairs_to_index.find(std::make_pair(i, j)); if (it == m_pairs_to_index.end()) return T(0xFFFFFF); // big number return m_q.get_priority(it->second); } #ifdef Z3DEBUG template bool binary_heap_upair_queue::pair_to_index_is_a_bijection() const { std::set tmp; for (auto p : m_pairs_to_index) { unsigned j = p.second; unsigned size = tmp.size(); tmp.insert(j); if (tmp.size() == size) return false; } return true; } template bool binary_heap_upair_queue::available_spots_are_correct() const { std::set tmp; for (auto p : m_available_spots){ tmp.insert(p); } if (tmp.size() != m_available_spots.size()) return false; for (auto it : m_pairs_to_index) if (tmp.find(it.second) != tmp.end()) return false; return true; } #endif } z3-z3-4.8.7/src/util/lp/bound_analyzer_on_row.h000066400000000000000000000240661356505360400213660ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #pragma once #include "util/vector.h" #include "implied_bound.h" #include "test_bound_analyzer.h" #include "util/lp/bound_propagator.h" // We have an equality : sum by j of row[j]*x[j] = rs // We try to pin a var by pushing the total by using the variable bounds // In a loop we drive the partial sum down, denoting the variables of this process by _u. // In the same loop trying to pin variables by pushing the partial sum up, denoting the variable related to it by _l namespace lp { template // C plays a role of a container class bound_analyzer_on_row { const C& m_row; bound_propagator & m_bp; unsigned m_row_or_term_index; int m_column_of_u; // index of an unlimited from above monoid // -1 means that such a value is not found, -2 means that at least two of such monoids were found int m_column_of_l; // index of an unlimited from below monoid impq m_rs; public : // constructor bound_analyzer_on_row( const C & it, unsigned bj, // basis column for the row const numeric_pair& rs, unsigned row_or_term_index, bound_propagator & bp ) : m_row(it), m_bp(bp), m_row_or_term_index(row_or_term_index), m_column_of_u(-1), m_column_of_l(-1), m_rs(rs) {} unsigned j; void analyze() { for (const auto & c : m_row) { if ((m_column_of_l == -2) && (m_column_of_u == -2)) break; analyze_bound_on_var_on_coeff(c.var(), c.coeff()); } if (m_column_of_u >= 0) limit_monoid_u_from_below(); else if (m_column_of_u == -1) limit_all_monoids_from_below(); if (m_column_of_l >= 0) limit_monoid_l_from_above(); else if (m_column_of_l == -1) limit_all_monoids_from_above(); } bool bound_is_available(unsigned j, bool lower_bound) { return (lower_bound && lower_bound_is_available(j)) || (!lower_bound && upper_bound_is_available(j)); } bool upper_bound_is_available(unsigned j) const { switch (m_bp.get_column_type(j)) { case column_type::fixed: case column_type::boxed: case column_type::upper_bound: return true; default: return false; } } bool lower_bound_is_available(unsigned j) const { switch (m_bp.get_column_type(j)) { case column_type::fixed: case column_type::boxed: case column_type::lower_bound: return true; default: return false; } } const impq & ub(unsigned j) const { lp_assert(upper_bound_is_available(j)); return m_bp.get_upper_bound(j); } const impq & lb(unsigned j) const { lp_assert(lower_bound_is_available(j)); return m_bp.get_lower_bound(j); } const mpq & monoid_max_no_mult(bool a_is_pos, unsigned j, bool & strict) const { if (a_is_pos) { strict = !is_zero(ub(j).y); return ub(j).x; } strict = !is_zero(lb(j).y); return lb(j).x; } mpq monoid_max(const mpq & a, unsigned j) const { if (is_pos(a)) { return a * ub(j).x; } return a * lb(j).x; } mpq monoid_max(const mpq & a, unsigned j, bool & strict) const { if (is_pos(a)) { strict = !is_zero(ub(j).y); return a * ub(j).x; } strict = !is_zero(lb(j).y); return a * lb(j).x; } const mpq & monoid_min_no_mult(bool a_is_pos, unsigned j, bool & strict) const { if (!a_is_pos) { strict = !is_zero(ub(j).y); return ub(j).x; } strict = !is_zero(lb(j).y); return lb(j).x; } mpq monoid_min(const mpq & a, unsigned j, bool& strict) const { if (is_neg(a)) { strict = !is_zero(ub(j).y); return a * ub(j).x; } strict = !is_zero(lb(j).y); return a * lb(j).x; } mpq monoid_min(const mpq & a, unsigned j) const { if (is_neg(a)) { return a * ub(j).x; } return a * lb(j).x; } void limit_all_monoids_from_above() { int strict = 0; mpq total; lp_assert(is_zero(total)); for (const auto& p : m_row) { bool str; total -= monoid_min(p.coeff(), p.var(), str); if (str) strict++; } mpq bound; for (const auto &p : m_row) { bool str; bool a_is_pos = is_pos(p.coeff()); bound = total; bound /= p.coeff(); bound += monoid_min_no_mult(a_is_pos, p.var(), str); if (a_is_pos) { limit_j(p.var(), bound, true, false, strict - static_cast(str) > 0); } else { limit_j(p.var(), bound, false, true, strict - static_cast(str) > 0); } } } void limit_all_monoids_from_below() { int strict = 0; mpq total; lp_assert(is_zero(total)); for (const auto &p : m_row) { bool str; total -= monoid_max(p.coeff(), p.var(), str); if (str) strict++; } for (const auto& p : m_row) { bool str; bool a_is_pos = is_pos(p.coeff()); mpq bound = total / p.coeff() + monoid_max_no_mult(a_is_pos, p.var(), str); bool astrict = strict - static_cast(str) > 0; if (a_is_pos) { limit_j(p.var(), bound, true, true, astrict); } else { limit_j(p.var(), bound, false, false, astrict); } } } void limit_monoid_u_from_below() { // we are going to limit from below the monoid m_column_of_u, // every other monoid is impossible to limit from below mpq u_coeff; unsigned j; mpq bound = -m_rs.x; bool strict = false; for (const auto& p : m_row) { j = p.var(); if (j == static_cast(m_column_of_u)) { u_coeff = p.coeff(); continue; } bool str; bound -= monoid_max(p.coeff(), j, str); if (str) strict = true; } bound /= u_coeff; if (numeric_traits::is_pos(u_coeff)) { limit_j(m_column_of_u, bound, true, true, strict); } else { limit_j(m_column_of_u, bound, false, false, strict); } } void limit_monoid_l_from_above() { // we are going to limit from above the monoid m_column_of_l, // every other monoid is impossible to limit from above mpq l_coeff; unsigned j; mpq bound = -m_rs.x; bool strict = false; for (const auto &p : m_row) { j = p.var(); if (j == static_cast(m_column_of_l)) { l_coeff = p.coeff(); continue; } bool str; bound -= monoid_min(p.coeff(), j, str); if (str) strict = true; } bound /= l_coeff; if (is_pos(l_coeff)) { limit_j(m_column_of_l, bound, true, false, strict); } else { limit_j(m_column_of_l, bound, false, true, strict); } } // // it is the coefficient before the bounded column // void provide_evidence(bool coeff_is_pos) { // /* // auto & be = m_ibounds.back(); // bool lower_bound = be.m_lower_bound; // if (!coeff_is_pos) // lower_bound = !lower_bound; // auto it = m_row.clone(); // mpq a; unsigned j; // while (it->next(a, j)) { // if (be.m_j == j) continue; // lp_assert(bound_is_available(j, is_neg(a) ? lower_bound : !lower_bound)); // be.m_vector_of_bound_signatures.emplace_back(a, j, numeric_traits:: // is_neg(a)? lower_bound: !lower_bound); // } // delete it; // */ // } void limit_j(unsigned j, const mpq& u, bool coeff_before_j_is_pos, bool is_lower_bound, bool strict){ m_bp.try_add_bound(u, j, is_lower_bound, coeff_before_j_is_pos, m_row_or_term_index, strict); } void advance_u(unsigned j) { if (m_column_of_u == -1) m_column_of_u = j; else m_column_of_u = -2; } void advance_l(unsigned j) { if (m_column_of_l == -1) m_column_of_l = j; else m_column_of_l = -2; } void analyze_bound_on_var_on_coeff(int j, const mpq &a) { switch (m_bp.get_column_type(j)) { case column_type::lower_bound: if (numeric_traits::is_pos(a)) advance_u(j); else advance_l(j); break; case column_type::upper_bound: if(numeric_traits::is_neg(a)) advance_u(j); else advance_l(j); break; case column_type::free_column: advance_u(j); advance_l(j); break; default: break; } } static void analyze_row(const C & row, unsigned bj, // basis column for the row const numeric_pair& rs, unsigned row_or_term_index, bound_propagator & bp ) { bound_analyzer_on_row a(row, bj, rs, row_or_term_index, bp); a.analyze(); } }; } z3-z3-4.8.7/src/util/lp/bound_propagator.h000066400000000000000000000021411356505360400203220ustar00rootroot00000000000000/* Copyright (c) 2017 Microsoft Corporation Author: Lev Nachmanson */ #pragma once #include "util/lp/lp_settings.h" namespace lp { class lar_solver; class bound_propagator { std::unordered_map m_improved_lower_bounds; // these maps map a column index to the corresponding index in ibounds std::unordered_map m_improved_upper_bounds; lar_solver & m_lar_solver; public: vector m_ibounds; public: bound_propagator(lar_solver & ls); column_type get_column_type(unsigned) const; const impq & get_lower_bound(unsigned) const; const impq & get_upper_bound(unsigned) const; void try_add_bound(mpq v, unsigned j, bool is_low, bool coeff_before_j_is_pos, unsigned row_or_term_index, bool strict); virtual bool bound_is_interesting(unsigned vi, lp::lconstraint_kind kind, const rational & bval) {return true;} unsigned number_of_found_bounds() const { return m_ibounds.size(); } virtual void consume(mpq const& v, lp::constraint_index j) = 0; }; } z3-z3-4.8.7/src/util/lp/breakpoint.h000066400000000000000000000007711356505360400171220ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #pragma once namespace lp { enum breakpoint_type { low_break, upper_break, fixed_break }; template struct breakpoint { unsigned m_j; // the basic column breakpoint_type m_type; X m_delta; breakpoint(){} breakpoint(unsigned j, X delta, breakpoint_type type):m_j(j), m_type(type), m_delta(delta) {} }; } z3-z3-4.8.7/src/util/lp/column_info.h000066400000000000000000000154061356505360400172750ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #pragma once #include "util/vector.h" #include #include #include #include "util/lp/lp_settings.h" namespace lp { inline bool is_valid(unsigned j) { return static_cast(j) >= 0;} template class column_info { std::string m_name; bool m_lower_bound_is_set; bool m_lower_bound_is_strict; bool m_upper_bound_is_set; bool m_upper_bound_is_strict; T m_lower_bound; T m_upper_bound; T m_fixed_value; bool m_is_fixed; T m_cost; unsigned m_column_index; public: bool operator==(const column_info & c) const { return m_name == c.m_name && m_lower_bound_is_set == c.m_lower_bound_is_set && m_lower_bound_is_strict == c.m_lower_bound_is_strict && m_upper_bound_is_set == c.m_upper_bound_is_set&& m_upper_bound_is_strict == c.m_upper_bound_is_strict&& (!m_lower_bound_is_set || m_lower_bound == c.m_low_bound) && (!m_upper_bound_is_set || m_upper_bound == c.m_upper_bound) && m_cost == c.m_cost && m_is_fixed == c.m_is_fixed && (!m_is_fixed || m_fixed_value == c.m_fixed_value) && m_column_index == c.m_column_index; } bool operator!=(const column_info & c) const { return !((*this) == c); } void set_column_index(unsigned j) { m_column_index = j; } // the default constructor column_info(): m_lower_bound_is_set(false), m_lower_bound_is_strict(false), m_upper_bound_is_set (false), m_upper_bound_is_strict (false), m_is_fixed(false), m_cost(numeric_traits::zero()), m_column_index(static_cast(-1)) {} column_info(const column_info & ci) { m_name = ci.m_name; m_lower_bound_is_set = ci.m_lower_bound_is_set; m_lower_bound_is_strict = ci.m_lower_bound_is_strict; m_upper_bound_is_set = ci.m_upper_bound_is_set; m_upper_bound_is_strict = ci.m_upper_bound_is_strict; m_lower_bound = ci.m_lower_bound; m_upper_bound = ci.m_upper_bound; m_cost = ci.m_cost; m_fixed_value = ci.m_fixed_value; m_is_fixed = ci.m_is_fixed; m_column_index = ci.m_column_index; } unsigned get_column_index() const { return m_column_index; } column_type get_column_type() const { return m_is_fixed? column_type::fixed : (m_lower_bound_is_set? (m_upper_bound_is_set? column_type::boxed : column_type::lower_bound) : (m_upper_bound_is_set? column_type::upper_bound: column_type::free_column)); } column_type get_column_type_no_flipping() const { if (m_is_fixed) { return column_type::fixed; } if (m_lower_bound_is_set) { return m_upper_bound_is_set? column_type::boxed: column_type::lower_bound; } // we are flipping the bounds! return m_upper_bound_is_set? column_type::upper_bound : column_type::free_column; } T get_lower_bound() const { lp_assert(m_lower_bound_is_set); return m_lower_bound; } T get_upper_bound() const { lp_assert(m_upper_bound_is_set); return m_upper_bound; } bool lower_bound_is_set() const { return m_lower_bound_is_set; } bool upper_bound_is_set() const { return m_upper_bound_is_set; } T get_shift() { if (is_fixed()) { return m_fixed_value; } if (is_flipped()){ return m_upper_bound; } return m_lower_bound_is_set? m_lower_bound : numeric_traits::zero(); } bool is_flipped() { return m_upper_bound_is_set && !m_lower_bound_is_set; } bool adjusted_lower_bound_is_set() { return !is_flipped()? lower_bound_is_set(): upper_bound_is_set(); } bool adjusted_upper_bound_is_set() { return !is_flipped()? upper_bound_is_set(): lower_bound_is_set(); } T get_adjusted_upper_bound() { return get_upper_bound() - get_lower_bound(); } bool is_fixed() const { return m_is_fixed; } bool is_free() { return !m_lower_bound_is_set && !m_upper_bound_is_set; } void set_fixed_value(T v) { m_is_fixed = true; m_fixed_value = v; } T get_fixed_value() const { lp_assert(m_is_fixed); return m_fixed_value; } T get_cost() const { return m_cost; } void set_cost(T const & cost) { m_cost = cost; } void set_name(std::string const & s) { m_name = s; } std::string get_name() const { return m_name; } void set_lower_bound(T const & l) { m_lower_bound = l; m_lower_bound_is_set = true; } void set_upper_bound(T const & l) { m_upper_bound = l; m_upper_bound_is_set = true; } void unset_lower_bound() { m_lower_bound_is_set = false; } void unset_upper_bound() { m_upper_bound_is_set = false; } void unset_fixed() { m_is_fixed = false; } bool lower_bound_holds(T v) { return !lower_bound_is_set() || v >= m_lower_bound -T(0.0000001); } bool upper_bound_holds(T v) { return !upper_bound_is_set() || v <= m_upper_bound + T(0.000001); } bool bounds_hold(T v) { return lower_bound_holds(v) && upper_bound_holds(v); } bool adjusted_bounds_hold(T v) { return adjusted_lower_bound_holds(v) && adjusted_upper_bound_holds(v); } bool adjusted_lower_bound_holds(T v) { return !adjusted_lower_bound_is_set() || v >= -T(0.0000001); } bool adjusted_upper_bound_holds(T v) { return !adjusted_upper_bound_is_set() || v <= get_adjusted_upper_bound() + T(0.000001); } bool is_infeasible() { if ((!upper_bound_is_set()) || (!lower_bound_is_set())) return false; // ok, both bounds are set bool at_least_one_is_strict = upper_bound_is_strict() || lower_bound_is_strict(); if (!at_least_one_is_strict) return get_upper_bound() < get_lower_bound(); // at least on bound is strict return get_upper_bound() <= get_lower_bound(); // the equality is impossible } bool lower_bound_is_strict() const { return m_lower_bound_is_strict; } void set_lower_bound_strict(bool val) { m_lower_bound_is_strict = val; } bool upper_bound_is_strict() const { return m_upper_bound_is_strict; } void set_upper_bound_strict(bool val) { m_upper_bound_is_strict = val; } }; } z3-z3-4.8.7/src/util/lp/column_namer.h000066400000000000000000000042671356505360400174470ustar00rootroot00000000000000#pragma once /*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #include #include "util/lp/static_matrix.h" namespace lp { class column_namer { public: virtual std::string get_column_name(unsigned j) const = 0; template void print_row(const row_strip & row, std::ostream & out) const { vector> coeff; for (auto & p : row) { coeff.push_back(std::make_pair(p.coeff(), p.var())); } print_linear_combination_of_column_indices(coeff, out); } template void print_linear_combination_of_column_indices_std(const vector> & coeffs, std::ostream & out) const { bool first = true; for (const auto & it : coeffs) { auto val = it.first; if (first) { first = false; } else { if (numeric_traits::is_pos(val)) { out << " + "; } else { out << " - "; val = -val; } } if (val == -numeric_traits::one()) out << " - "; else if (val != numeric_traits::one()) out << val; out << get_column_name(it.second); } } template void print_linear_combination_of_column_indices(const vector> & coeffs, std::ostream & out) const { bool first = true; for (const auto & it : coeffs) { auto val = it.first; if (first) { first = false; } else { if (numeric_traits::is_pos(val)) { out << " + "; } else { out << " - "; val = -val; } } if (val == -numeric_traits::one()) out << " - "; else if (val != numeric_traits::one()) out << val; out << get_column_name(it.second); } } }; } z3-z3-4.8.7/src/util/lp/constraint.h000066400000000000000000000053121356505360400171440ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Nikolaj Bjorner (nbjorner) Lev Nachmanson (levnach) Revision History: --*/ #pragma once namespace lp { class constraint; // forward definition struct constraint_hash { size_t operator() (const constraint* c) const; }; struct constraint_equal { bool operator() (const constraint * a, const constraint * b) const; }; class constraint { // we only have less or equal for the inequality sign, which is enough for integral variables int m_id; bool m_is_ineq; polynomial m_poly; mpq m_d; // the divider for the case of a divisibility constraint std::unordered_set m_assert_origins; // these indices come from the client and get collected during tightening public : unsigned id() const { return m_id; } const polynomial & poly() const { return m_poly; } polynomial & poly() { return m_poly; } std::unordered_set & assert_origins() { return m_assert_origins;} const std::unordered_set & assert_origins() const { return m_assert_origins;} bool is_lemma() const { return !is_assert(); } bool is_assert() const { return m_assert_origins.size() == 1; } bool is_ineq() const { return m_is_ineq; } const mpq & divider() const { return m_d; } public: constraint( unsigned id, constraint_index assert_origin, const polynomial & p, bool is_ineq): m_id(id), m_is_ineq(is_ineq), m_poly(p) { // creates an assert m_assert_origins.insert(assert_origin); } constraint( unsigned id, const std::unordered_set& origins, const polynomial & p, bool is_ineq): m_id(id), m_is_ineq(is_ineq), m_poly(p), m_assert_origins(origins) {} constraint( unsigned id, const polynomial & p, bool is_ineq): m_id(id), m_is_ineq(is_ineq), m_poly(p) { // creates a lemma } public: constraint() {} const mpq & coeff(var_index j) const { return m_poly.coeff(j); } const vector& coeffs() const { return m_poly.m_coeffs;} bool is_tight(unsigned j) const { const mpq & a = m_poly.coeff(j); return a == 1 || a == -1; } void add_predecessor(const constraint* p) { lp_assert(p != nullptr); for (auto m : p->assert_origins()) m_assert_origins.insert(m); } }; } z3-z3-4.8.7/src/util/lp/conversion_helper.h000066400000000000000000000027051356505360400205070ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #pragma once namespace lp { template struct conversion_helper { static V get_lower_bound(const column_info & ci) { return V(ci.get_lower_bound(), ci.lower_bound_is_strict()? 1 : 0); } static V get_upper_bound(const column_info & ci) { return V(ci.get_upper_bound(), ci.upper_bound_is_strict()? -1 : 0); } }; template<> struct conversion_helper { static double get_upper_bound(const column_info & ci) { if (!ci.upper_bound_is_strict()) return ci.get_upper_bound().get_double(); double eps = 0.00001; if (!ci.lower_bound_is_set()) return ci.get_upper_bound().get_double() - eps; eps = std::min((ci.get_upper_bound() - ci.get_lower_bound()).get_double() / 1000, eps); return ci.get_upper_bound().get_double() - eps; } static double get_lower_bound(const column_info & ci) { if (!ci.lower_bound_is_strict()) return ci.get_lower_bound().get_double(); double eps = 0.00001; if (!ci.upper_bound_is_set()) return ci.get_lower_bound().get_double() + eps; eps = std::min((ci.get_upper_bound() - ci.get_lower_bound()).get_double() / 1000, eps); return ci.get_lower_bound().get_double() + eps; } }; } z3-z3-4.8.7/src/util/lp/core_solver_pretty_printer.cpp000066400000000000000000000023571356505360400230150ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #include "util/lp/numeric_pair.h" #include "util/lp/core_solver_pretty_printer_def.h" template lp::core_solver_pretty_printer::core_solver_pretty_printer(lp::lp_core_solver_base &, std::ostream & out); template void lp::core_solver_pretty_printer::print(); template lp::core_solver_pretty_printer::~core_solver_pretty_printer(); template lp::core_solver_pretty_printer::core_solver_pretty_printer(lp::lp_core_solver_base &, std::ostream & out); template void lp::core_solver_pretty_printer::print(); template lp::core_solver_pretty_printer::~core_solver_pretty_printer(); template lp::core_solver_pretty_printer >::core_solver_pretty_printer(lp::lp_core_solver_base > &, std::ostream & out); template lp::core_solver_pretty_printer >::~core_solver_pretty_printer(); template void lp::core_solver_pretty_printer >::print(); z3-z3-4.8.7/src/util/lp/core_solver_pretty_printer.h000066400000000000000000000062461356505360400224630ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #pragma once #include #include #include #include "util/vector.h" #include #include "util/lp/lp_settings.h" #include "util/lp/indexed_vector.h" namespace lp { template class lp_core_solver_base; // forward definition template class core_solver_pretty_printer { std::ostream & m_out; typedef std::string string; lp_core_solver_base & m_core_solver; vector m_column_widths; vector> m_A; vector> m_signs; vector m_costs; vector m_cost_signs; vector m_lows; // low bounds vector m_upps; // upper bounds vector m_lows_signs; vector m_upps_signs; unsigned m_rs_width; vector m_rs; unsigned m_title_width; std::string m_cost_title; std::string m_basis_heading_title; std::string m_x_title; std::string m_lower_bounds_title; std::string m_upp_bounds_title; std::string m_exact_norm_title; std::string m_approx_norm_title; unsigned ncols() { return m_core_solver.m_A.column_count(); } unsigned nrows() { return m_core_solver.m_A.row_count(); } unsigned m_artificial_start; indexed_vector m_w_buff; indexed_vector m_ed_buff; vector m_exact_column_norms; public: core_solver_pretty_printer(lp_core_solver_base & core_solver, std::ostream & out); void init_costs(); ~core_solver_pretty_printer(); void init_rs_width(); T current_column_norm(); void init_m_A_and_signs(); void init_column_widths(); void adjust_width_with_lower_bound(unsigned column, unsigned & w); void adjust_width_with_upper_bound(unsigned column, unsigned & w); void adjust_width_with_bounds(unsigned column, unsigned & w); void adjust_width_with_basis_heading(unsigned column, unsigned & w) { w = std::max(w, (unsigned)T_to_string(m_core_solver.m_basis_heading[column]).size()); } unsigned get_column_width(unsigned column); unsigned regular_cell_width(unsigned row, unsigned column, const std::string & name) { return regular_cell_string(row, column, name).size(); } std::string regular_cell_string(unsigned row, unsigned column, std::string name); void set_coeff(vector& row, vector & row_signs, unsigned col, const T & t, string name); void print_x(); std::string get_lower_bound_string(unsigned j); std::string get_upp_bound_string(unsigned j); void print_lows(); void print_upps(); string get_exact_column_norm_string(unsigned col) { return T_to_string(m_exact_column_norms[col]); } void print_exact_norms(); void print_approx_norms(); void print(); void print_basis_heading(); void print_bottom_line() { m_out << "----------------------" << std::endl; } void print_cost(); void print_given_rows(vector & row, vector & signs, X rst); void print_row(unsigned i); }; } z3-z3-4.8.7/src/util/lp/core_solver_pretty_printer_def.h000066400000000000000000000335131356505360400232760ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #include #include #include #include "util/lp/lp_utils.h" #include "util/lp/lp_core_solver_base.h" #include "util/lp/core_solver_pretty_printer.h" #include "util/lp/numeric_pair.h" namespace lp { template core_solver_pretty_printer::core_solver_pretty_printer(lp_core_solver_base & core_solver, std::ostream & out): m_out(out), m_core_solver(core_solver), m_A(core_solver.m_A.row_count(), vector(core_solver.m_A.column_count(), "")), m_signs(core_solver.m_A.row_count(), vector(core_solver.m_A.column_count(), " ")), m_costs(ncols(), ""), m_cost_signs(ncols(), " "), m_rs(ncols(), zero_of_type()), m_w_buff(core_solver.m_w), m_ed_buff(core_solver.m_ed) { m_lower_bounds_title = "low"; m_upp_bounds_title = "upp"; m_exact_norm_title = "exact cn"; m_approx_norm_title = "approx cn"; m_artificial_start = std::numeric_limits::max(); m_column_widths.resize(core_solver.m_A.column_count(), 0), init_m_A_and_signs(); init_costs(); init_column_widths(); init_rs_width(); m_cost_title = "costs"; m_basis_heading_title = "heading"; m_x_title = "x*"; m_title_width = static_cast(std::max(std::max(m_cost_title.size(), std::max(m_basis_heading_title.size(), m_x_title.size())), m_approx_norm_title.size())); } template void core_solver_pretty_printer::init_costs() { if (!m_core_solver.use_tableau()) { vector local_y(m_core_solver.m_m()); m_core_solver.solve_yB(local_y); for (unsigned i = 0; i < ncols(); i++) { if (m_core_solver.m_basis_heading[i] < 0) { T t = m_core_solver.m_costs[i] - m_core_solver.m_A.dot_product_with_column(local_y, i); set_coeff(m_costs, m_cost_signs, i, t, m_core_solver.column_name(i)); } } } else { for (unsigned i = 0; i < ncols(); i++) { if (m_core_solver.m_basis_heading[i] < 0) { set_coeff(m_costs, m_cost_signs, i, m_core_solver.m_d[i], m_core_solver.column_name(i)); } } } } template core_solver_pretty_printer::~core_solver_pretty_printer() { m_core_solver.m_w = m_w_buff; m_core_solver.m_ed = m_ed_buff; } template void core_solver_pretty_printer::init_rs_width() { m_rs_width = static_cast(T_to_string(m_core_solver.get_cost()).size()); for (unsigned i = 0; i < nrows(); i++) { unsigned wt = static_cast(T_to_string(m_rs[i]).size()); if (wt > m_rs_width) { m_rs_width = wt; } } } template T core_solver_pretty_printer::current_column_norm() { T ret = zero_of_type(); for (auto i : m_core_solver.m_ed.m_index) ret += m_core_solver.m_ed[i] * m_core_solver.m_ed[i]; return ret; } template void core_solver_pretty_printer::init_m_A_and_signs() { if (numeric_traits::precise() && m_core_solver.m_settings.use_tableau()) { for (unsigned column = 0; column < ncols(); column++) { vector t(nrows(), zero_of_type()); for (const auto & c : m_core_solver.m_A.m_columns[column]){ t[c.var()] = m_core_solver.m_A.get_val(c); } string name = m_core_solver.column_name(column); for (unsigned row = 0; row < nrows(); row ++) { m_A[row].resize(ncols(), ""); m_signs[row].resize(ncols(),""); set_coeff( m_A[row], m_signs[row], column, t[row], name); m_rs[row] += t[row] * m_core_solver.m_x[column]; } } } else { for (unsigned column = 0; column < ncols(); column++) { m_core_solver.solve_Bd(column); // puts the result into m_core_solver.m_ed string name = m_core_solver.column_name(column); for (unsigned row = 0; row < nrows(); row ++) { set_coeff( m_A[row], m_signs[row], column, m_core_solver.m_ed[row], name); m_rs[row] += m_core_solver.m_ed[row] * m_core_solver.m_x[column]; } if (!m_core_solver.use_tableau()) m_exact_column_norms.push_back(current_column_norm() + T(1)); // a conversion missing 1 -> T } } } template void core_solver_pretty_printer::init_column_widths() { for (unsigned i = 0; i < ncols(); i++) { m_column_widths[i] = get_column_width(i); } } template void core_solver_pretty_printer::adjust_width_with_lower_bound(unsigned column, unsigned & w) { if (!m_core_solver.lower_bounds_are_set()) return; w = std::max(w, (unsigned)T_to_string(m_core_solver.lower_bound_value(column)).size()); } template void core_solver_pretty_printer::adjust_width_with_upper_bound(unsigned column, unsigned & w) { w = std::max(w, (unsigned)T_to_string(m_core_solver.upper_bound_value(column)).size()); } template void core_solver_pretty_printer::adjust_width_with_bounds(unsigned column, unsigned & w) { switch (m_core_solver.get_column_type(column)) { case column_type::fixed: case column_type::boxed: adjust_width_with_lower_bound(column, w); adjust_width_with_upper_bound(column, w); break; case column_type::lower_bound: adjust_width_with_lower_bound(column, w); break; case column_type::upper_bound: adjust_width_with_upper_bound(column, w); break; case column_type::free_column: break; default: lp_assert(false); break; } } template unsigned core_solver_pretty_printer:: get_column_width(unsigned column) { unsigned w = static_cast(std::max((size_t)m_costs[column].size(), T_to_string(m_core_solver.m_x[column]).size())); adjust_width_with_bounds(column, w); adjust_width_with_basis_heading(column, w); for (unsigned i = 0; i < nrows(); i++) { unsigned cellw = static_cast(m_A[i][column].size()); if (cellw > w) { w = cellw; } } 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.empty()) w = std::max(w, (unsigned)T_to_string(m_core_solver.m_column_norms[column]).size()); } return w; } template std::string core_solver_pretty_printer::regular_cell_string(unsigned row, unsigned /* column */, std::string name) { T t = fabs(m_core_solver.m_ed[row]); if ( t == 1) return name; return T_to_string(t) + name; } template void core_solver_pretty_printer::set_coeff(vector& row, vector & row_signs, unsigned col, const T & t, string name) { if (numeric_traits::is_zero(t)) { return; } if (col > 0) { if (t > 0) { row_signs[col] = "+"; row[col] = t != 1? T_to_string(t) + name : name; } else { row_signs[col] = "-"; row[col] = t != -1? T_to_string(-t) + name: name; } } else { // col == 0 if (t == -1) { row[col] = "-" + name; } else if (t == 1) { row[col] = name; } else { row[col] = T_to_string(t) + name; } } } template void core_solver_pretty_printer::print_x() { if (ncols() == 0) { return; } int blanks = m_title_width + 1 - static_cast(m_x_title.size()); m_out << m_x_title; print_blanks(blanks, m_out); auto bh = m_core_solver.m_x; for (unsigned i = 0; i < ncols(); i++) { string s = T_to_string(bh[i]); int blanks = m_column_widths[i] - static_cast(s.size()); print_blanks(blanks, m_out); m_out << s << " "; // the column interval } m_out << std::endl; } template std::string core_solver_pretty_printer::get_lower_bound_string(unsigned j) { switch (m_core_solver.get_column_type(j)){ case column_type::boxed: case column_type::lower_bound: case column_type::fixed: if (m_core_solver.lower_bounds_are_set()) return T_to_string(m_core_solver.lower_bound_value(j)); else return std::string("0"); break; default: return std::string(); } } template std::string core_solver_pretty_printer::get_upp_bound_string(unsigned j) { switch (m_core_solver.get_column_type(j)){ case column_type::boxed: case column_type::upper_bound: case column_type::fixed: return T_to_string(m_core_solver.upper_bound_value(j)); break; default: return std::string(); } } template void core_solver_pretty_printer::print_lows() { if (ncols() == 0) { return; } int blanks = m_title_width + 1 - static_cast(m_lower_bounds_title.size()); m_out << m_lower_bounds_title; print_blanks(blanks, m_out); for (unsigned i = 0; i < ncols(); i++) { string s = get_lower_bound_string(i); int blanks = m_column_widths[i] - static_cast(s.size()); print_blanks(blanks, m_out); m_out << s << " "; // the column interval } m_out << std::endl; } template void core_solver_pretty_printer::print_upps() { if (ncols() == 0) { return; } int blanks = m_title_width + 1 - static_cast(m_upp_bounds_title.size()); m_out << m_upp_bounds_title; print_blanks(blanks, m_out); for (unsigned i = 0; i < ncols(); i++) { string s = get_upp_bound_string(i); int blanks = m_column_widths[i] - static_cast(s.size()); print_blanks(blanks, m_out); m_out << s << " "; // the column interval } m_out << std::endl; } template void core_solver_pretty_printer::print_exact_norms() { if (m_core_solver.use_tableau()) return; int blanks = m_title_width + 1 - static_cast(m_exact_norm_title.size()); m_out << m_exact_norm_title; print_blanks(blanks, m_out); for (unsigned i = 0; i < ncols(); i++) { string s = get_exact_column_norm_string(i); int blanks = m_column_widths[i] - static_cast(s.size()); print_blanks(blanks, m_out); m_out << s << " "; } m_out << std::endl; } template void core_solver_pretty_printer::print_approx_norms() { if (m_core_solver.use_tableau()) return; int blanks = m_title_width + 1 - static_cast(m_approx_norm_title.size()); m_out << m_approx_norm_title; print_blanks(blanks, m_out); for (unsigned i = 0; i < ncols(); i++) { string s = T_to_string(m_core_solver.m_column_norms[i]); int blanks = m_column_widths[i] - static_cast(s.size()); print_blanks(blanks, m_out); m_out << s << " "; } m_out << std::endl; } template void core_solver_pretty_printer::print() { for (unsigned i = 0; i < nrows(); i++) { print_row(i); } print_bottom_line(); print_cost(); print_x(); print_basis_heading(); print_lows(); print_upps(); print_exact_norms(); if (!m_core_solver.m_column_norms.empty()) print_approx_norms(); m_out << std::endl; } template void core_solver_pretty_printer::print_basis_heading() { int blanks = m_title_width + 1 - static_cast(m_basis_heading_title.size()); m_out << m_basis_heading_title; print_blanks(blanks, m_out); if (ncols() == 0) { return; } auto bh = m_core_solver.m_basis_heading; for (unsigned i = 0; i < ncols(); i++) { string s = T_to_string(bh[i]); int blanks = m_column_widths[i] - static_cast(s.size()); print_blanks(blanks, m_out); m_out << s << " "; // the column interval } m_out << std::endl; } template void core_solver_pretty_printer::print_cost() { int blanks = m_title_width + 1 - static_cast(m_cost_title.size()); m_out << m_cost_title; print_blanks(blanks, m_out); print_given_rows(m_costs, m_cost_signs, m_core_solver.get_cost()); } template void core_solver_pretty_printer::print_given_rows(vector & row, vector & signs, X rst) { for (unsigned col = 0; col < row.size(); col++) { unsigned width = m_column_widths[col]; string s = row[col]; int number_of_blanks = width - static_cast(s.size()); lp_assert(number_of_blanks >= 0); print_blanks(number_of_blanks, m_out); m_out << s << ' '; if (col < row.size() - 1) { m_out << signs[col + 1] << ' '; } } m_out << '='; string rs = T_to_string(rst); int nb = m_rs_width - static_cast(rs.size()); lp_assert(nb >= 0); print_blanks(nb + 1, m_out); m_out << rs << std::endl; } template void core_solver_pretty_printer::print_row(unsigned i){ print_blanks(m_title_width + 1, m_out); auto row = m_A[i]; auto sign_row = m_signs[i]; auto rs = m_rs[i]; print_given_rows(row, sign_row, rs); } } z3-z3-4.8.7/src/util/lp/dense_matrix.cpp000066400000000000000000000042201356505360400177720ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #include "util/lp/lp_settings.h" #include "util/lp/dense_matrix_def.h" #ifdef Z3DEBUG #include "util/vector.h" template lp::dense_matrix lp::operator*(lp::matrix&, lp::matrix&); template void lp::dense_matrix::apply_from_left(vector &); template lp::dense_matrix::dense_matrix(lp::matrix const*); template lp::dense_matrix::dense_matrix(unsigned int, unsigned int); template lp::dense_matrix& lp::dense_matrix::operator=(lp::dense_matrix const&); template lp::dense_matrix::dense_matrix(unsigned int, unsigned int); template lp::dense_matrix >::dense_matrix(lp::matrix > const*); template void lp::dense_matrix >::apply_from_left(vector&); template lp::dense_matrix lp::operator*(lp::matrix&, lp::matrix&); template lp::dense_matrix & lp::dense_matrix::operator=(lp::dense_matrix const&); template lp::dense_matrix >::dense_matrix(unsigned int, unsigned int); template lp::dense_matrix >& lp::dense_matrix >::operator=(lp::dense_matrix > const&); template lp::dense_matrix > lp::operator* >(lp::matrix >&, lp::matrix >&); template void lp::dense_matrix >::apply_from_right( vector< lp::mpq> &); template void lp::dense_matrix::apply_from_right(vector &); template void lp::dense_matrix::apply_from_left(vector&); #endif z3-z3-4.8.7/src/util/lp/dense_matrix.h000066400000000000000000000056201356505360400174440ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #pragma once #ifdef Z3DEBUG #include "util/vector.h" #include "util/lp/matrix.h" namespace lp { // used for debugging purposes only template class dense_matrix: public matrix { public: struct ref { unsigned m_i; dense_matrix & m_s; ref(unsigned i, dense_matrix & s) :m_i(i * s.m_n), m_s(s){} T & operator[] (unsigned j) { return m_s.m_values[m_i + j]; } const T & operator[] (unsigned j) const { return m_s.m_v[m_i + j]; } }; ref operator[] (unsigned i) { return ref(i, *this); } unsigned m_m; // number of rows unsigned m_n; // number of const vector m_values; dense_matrix(unsigned m, unsigned n); dense_matrix operator*=(matrix const & a) { lp_assert(column_count() == a.row_count()); dense_matrix c(row_count(), a.column_count()); for (unsigned i = 0; i < row_count(); i++) { for (unsigned j = 0; j < a.column_count(); j++) { T v = numeric_traits::zero(); for (unsigned k = 0; k < a.column_count(); k++) { v += get_elem(i, k) * a(k, j); } c.set_elem(i, j, v); } } *this = c; return *this; } dense_matrix & operator=(matrix const & other); dense_matrix & operator=(dense_matrix const & other); dense_matrix(matrix const * other); void apply_from_right(T * w); void apply_from_right(vector & w); T * apply_from_left_with_different_dims(vector & w); void apply_from_left(vector & w , lp_settings & ) { apply_from_left(w); } void apply_from_left(vector & w); void apply_from_left(X * w, lp_settings & ); void apply_from_left_to_X(vector & w, lp_settings & ); void set_number_of_rows(unsigned /*m*/) override {} void set_number_of_columns(unsigned /*n*/) override {} #ifdef Z3DEBUG T get_elem(unsigned i, unsigned j) const override { return m_values[i * m_n + j]; } #endif unsigned row_count() const override { return m_m; } unsigned column_count() const override { return m_n; } void set_elem(unsigned i, unsigned j, const T& val) { m_values[i * m_n + j] = val; } // This method pivots row i to row i0 by muliplying row i by // alpha and adding it to row i0. void pivot_row_to_row(unsigned i, const T& alpha, unsigned i0, const double & pivot_epsilon); void swap_columns(unsigned a, unsigned b); void swap_rows(unsigned a, unsigned b); void multiply_row_by_constant(unsigned row, T & t); }; template dense_matrix operator* (matrix & a, matrix & b); } #endif z3-z3-4.8.7/src/util/lp/dense_matrix_def.h000066400000000000000000000130441356505360400202610ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #include "util/lp/lp_settings.h" #ifdef Z3DEBUG #include "util/vector.h" #include "util/lp/numeric_pair.h" #include "util/lp/dense_matrix.h" namespace lp { template dense_matrix::dense_matrix(unsigned m, unsigned n) : m_m(m), m_n(n), m_values(m * n, numeric_traits::zero()) { } template dense_matrix& dense_matrix::operator=(matrix const & other){ if ( this == & other) return *this; m_values = new T[m_m * m_n]; for (unsigned i = 0; i < m_m; i ++) for (unsigned j = 0; j < m_n; j++) m_values[i * m_n + j] = other.get_elem(i, j); return *this; } template dense_matrix& dense_matrix::operator=(dense_matrix const & other){ if ( this == & other) return *this; m_m = other.m_m; m_n = other.m_n; m_values.resize(m_m * m_n); for (unsigned i = 0; i < m_m; i ++) for (unsigned j = 0; j < m_n; j++) m_values[i * m_n + j] = other.get_elem(i, j); return *this; } template dense_matrix::dense_matrix(matrix const * other) : m_m(other->row_count()), m_n(other->column_count()) { m_values.resize(m_m*m_n); for (unsigned i = 0; i < m_m; i++) for (unsigned j = 0; j < m_n; j++) m_values[i * m_n + j] = other->get_elem(i, j); } template void dense_matrix::apply_from_right(T * w) { T * t = new T[m_m]; for (int i = 0; i < m_m; i ++) { T v = numeric_traits::zero(); for (int j = 0; j < m_m; j++) { v += w[j]* get_elem(j, i); } t[i] = v; } for (int i = 0; i < m_m; i++) { w[i] = t[i]; } delete [] t; } template void dense_matrix::apply_from_right(vector & w) { vector t(m_m, numeric_traits::zero()); for (unsigned i = 0; i < m_m; i ++) { auto & v = t[i]; for (unsigned j = 0; j < m_m; j++) v += w[j]* get_elem(j, i); } for (unsigned i = 0; i < m_m; i++) w[i] = t[i]; } template T* dense_matrix:: apply_from_left_with_different_dims(vector & w) { T * t = new T[m_m]; for (int i = 0; i < m_m; i ++) { T v = numeric_traits::zero(); for (int j = 0; j < m_n; j++) { v += w[j]* get_elem(i, j); } t[i] = v; } return t; } template void dense_matrix::apply_from_left(vector & w) { T * t = new T[m_m]; for (unsigned i = 0; i < m_m; i ++) { T v = numeric_traits::zero(); for (unsigned j = 0; j < m_m; j++) { v += w[j]* get_elem(i, j); } t[i] = v; } for (unsigned i = 0; i < m_m; i ++) { w[i] = t[i]; } delete [] t; } template void dense_matrix::apply_from_left(X * w, lp_settings & ) { T * t = new T[m_m]; for (int i = 0; i < m_m; i ++) { T v = numeric_traits::zero(); for (int j = 0; j < m_m; j++) { v += w[j]* get_elem(i, j); } t[i] = v; } for (int i = 0; i < m_m; i ++) { w[i] = t[i]; } delete [] t; } template void dense_matrix::apply_from_left_to_X(vector & w, lp_settings & ) { vector t(m_m); for (int i = 0; i < m_m; i ++) { X v = zero_of_type(); for (int j = 0; j < m_m; j++) { v += w[j]* get_elem(i, j); } t[i] = v; } for (int i = 0; i < m_m; i ++) { w[i] = t[i]; } } // This method pivots row i to row i0 by muliplying row i by // alpha and adding it to row i0. template void dense_matrix::pivot_row_to_row(unsigned i, const T& alpha, unsigned i0, const double & pivot_epsilon) { for (unsigned j = 0; j < m_n; j++) { m_values[i0 * m_n + j] += m_values[i * m_n + j] * alpha; if (fabs(m_values[i0 + m_n + j]) < pivot_epsilon) { m_values[i0 + m_n + j] = numeric_traits::zero();; } } } template void dense_matrix::swap_columns(unsigned a, unsigned b) { for (unsigned i = 0; i < m_m; i++) { T t = get_elem(i, a); set_elem(i, a, get_elem(i, b)); set_elem(i, b, t); } } template void dense_matrix::swap_rows(unsigned a, unsigned b) { for (unsigned i = 0; i < m_n; i++) { T t = get_elem(a, i); set_elem(a, i, get_elem(b, i)); set_elem(b, i, t); } } template void dense_matrix::multiply_row_by_constant(unsigned row, T & t) { for (unsigned i = 0; i < m_n; i++) { set_elem(row, i, t * get_elem(row, i)); } } template dense_matrix operator* (matrix & a, matrix & b){ lp_assert(a.column_count() == b.row_count()); dense_matrix ret(a.row_count(), b.column_count()); for (unsigned i = 0; i < ret.m_m; i++) for (unsigned j = 0; j< ret.m_n; j++) { T v = numeric_traits::zero(); for (unsigned k = 0; k < a.column_count(); k ++){ v += (a.get_elem(i, k) * b.get_elem(k, j)); } ret.set_elem(i, j, v); } return ret; } } #endif z3-z3-4.8.7/src/util/lp/eta_matrix.cpp000066400000000000000000000043661356505360400174600ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #include #include "util/vector.h" #include "util/lp/numeric_pair.h" #include "util/lp/eta_matrix_def.h" #ifdef Z3DEBUG template double lp::eta_matrix::get_elem(unsigned int, unsigned int) const; template lp::mpq lp::eta_matrix::get_elem(unsigned int, unsigned int) const; template lp::mpq lp::eta_matrix >::get_elem(unsigned int, unsigned int) const; #endif template void lp::eta_matrix::apply_from_left(vector&, lp::lp_settings&); template void lp::eta_matrix::apply_from_right(vector&); template void lp::eta_matrix::conjugate_by_permutation(lp::permutation_matrix&); template void lp::eta_matrix::apply_from_left(vector&, lp::lp_settings&); template void lp::eta_matrix::apply_from_right(vector&); template void lp::eta_matrix::conjugate_by_permutation(lp::permutation_matrix&); template void lp::eta_matrix >::apply_from_left(vector >&, lp::lp_settings&); template void lp::eta_matrix >::apply_from_right(vector&); template void lp::eta_matrix >::conjugate_by_permutation(lp::permutation_matrix >&); template void lp::eta_matrix::apply_from_left_local(lp::indexed_vector&, lp::lp_settings&); template void lp::eta_matrix::apply_from_left_local(lp::indexed_vector&, lp::lp_settings&); template void lp::eta_matrix >::apply_from_left_local(lp::indexed_vector&, lp::lp_settings&); template void lp::eta_matrix >::apply_from_right(lp::indexed_vector&); template void lp::eta_matrix::apply_from_right(lp::indexed_vector&); template void lp::eta_matrix::apply_from_right(lp::indexed_vector&); z3-z3-4.8.7/src/util/lp/eta_matrix.h000066400000000000000000000045311356505360400171170ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #pragma once #include "util/vector.h" #include "util/lp/tail_matrix.h" #include "util/lp/permutation_matrix.h" namespace lp { // This is the sum of a unit matrix and a one-column matrix template class eta_matrix : public tail_matrix { #ifdef Z3DEBUG unsigned m_length; #endif unsigned m_column_index; public: sparse_vector m_column_vector; T m_diagonal_element; #ifdef Z3DEBUG eta_matrix(unsigned column_index, unsigned length): #else eta_matrix(unsigned column_index): #endif #ifdef Z3DEBUG m_length(length), #endif m_column_index(column_index) {} bool is_dense() const override { return false; } void print(std::ostream & out) { print_matrix(*this, out); } bool is_unit() { return m_column_vector.size() == 0 && m_diagonal_element == 1; } bool set_diagonal_element(T const & diagonal_element) { m_diagonal_element = diagonal_element; return !lp_settings::is_eps_small_general(diagonal_element, 1e-12); } const T & get_diagonal_element() const { return m_diagonal_element; } void apply_from_left(vector & w, lp_settings & ) override; template void apply_from_left_local(indexed_vector & w, lp_settings & settings); void apply_from_left_to_T(indexed_vector & w, lp_settings & settings) override { apply_from_left_local(w, settings); } void push_back(unsigned row_index, T val ) { lp_assert(row_index != m_column_index); m_column_vector.push_back(row_index, val); } void apply_from_right(vector & w) override; void apply_from_right(indexed_vector & w) override; #ifdef Z3DEBUG T get_elem(unsigned i, unsigned j) const override; unsigned row_count() const override { return m_length; } unsigned column_count() const override { return m_length; } void set_number_of_rows(unsigned m) override { m_length = m; } void set_number_of_columns(unsigned n) override { m_length = n; } #endif void divide_by_diagonal_element() { m_column_vector.divide(m_diagonal_element); } void conjugate_by_permutation(permutation_matrix & p); }; } z3-z3-4.8.7/src/util/lp/eta_matrix_def.h000066400000000000000000000101101356505360400177230ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #pragma once #include "util/vector.h" #include "util/lp/eta_matrix.h" namespace lp { // This is the sum of a unit matrix and a one-column matrix template void eta_matrix::apply_from_left(vector & w, lp_settings & ) { auto & w_at_column_index = w[m_column_index]; for (auto & it : m_column_vector.m_data) { w[it.first] += w_at_column_index * it.second; } w_at_column_index /= m_diagonal_element; } template template void eta_matrix:: apply_from_left_local(indexed_vector & w, lp_settings & settings) { const L w_at_column_index = w[m_column_index]; if (is_zero(w_at_column_index)) return; if (settings.abs_val_is_smaller_than_drop_tolerance(w[m_column_index] /= m_diagonal_element)) { w[m_column_index] = zero_of_type(); w.erase_from_index(m_column_index); } for (auto & it : m_column_vector.m_data) { unsigned i = it.first; if (is_zero(w[i])) { L v = w[i] = w_at_column_index * it.second; if (settings.abs_val_is_smaller_than_drop_tolerance(v)) { w[i] = zero_of_type(); continue; } w.m_index.push_back(i); } else { L v = w[i] += w_at_column_index * it.second; if (settings.abs_val_is_smaller_than_drop_tolerance(v)) { w[i] = zero_of_type(); w.erase_from_index(i); } } } } template void eta_matrix::apply_from_right(vector & w) { #ifdef Z3DEBUG // dense_matrix deb(*this); // auto clone_w = clone_vector(w, get_number_of_rows()); // deb.apply_from_right(clone_w); #endif T t = w[m_column_index] / m_diagonal_element; for (auto & it : m_column_vector.m_data) { t += w[it.first] * it.second; } w[m_column_index] = t; #ifdef Z3DEBUG // lp_assert(vectors_are_equal(clone_w, w, get_number_of_rows())); // delete clone_w; #endif } template void eta_matrix::apply_from_right(indexed_vector & w) { if (w.m_index.empty()) return; #ifdef Z3DEBUG // vector wcopy(w.m_data); // apply_from_right(wcopy); #endif T & t = w[m_column_index]; t /= m_diagonal_element; bool was_in_index = (!numeric_traits::is_zero(t)); for (auto & it : m_column_vector.m_data) { t += w[it.first] * it.second; } if (numeric_traits::precise() ) { if (!numeric_traits::is_zero(t)) { if (!was_in_index) w.m_index.push_back(m_column_index); } else { if (was_in_index) w.erase_from_index(m_column_index); } } else { if (!lp_settings::is_eps_small_general(t, 1e-14)) { if (!was_in_index) w.m_index.push_back(m_column_index); } else { if (was_in_index) w.erase_from_index(m_column_index); t = zero_of_type(); } } #ifdef Z3DEBUG // lp_assert(w.is_OK()); // lp_assert(vectors_are_equal(wcopy, w.m_data)); #endif } #ifdef Z3DEBUG template T eta_matrix::get_elem(unsigned i, unsigned j) const { if (j == m_column_index){ if (i == j) { return 1 / m_diagonal_element; } return m_column_vector[i]; } return i == j ? numeric_traits::one() : numeric_traits::zero(); } #endif template void eta_matrix::conjugate_by_permutation(permutation_matrix & p) { // this = p * this * p(-1) #ifdef Z3DEBUG // auto rev = p.get_reverse(); // auto deb = ((*this) * rev); // deb = p * deb; #endif m_column_index = p.get_rev(m_column_index); for (auto & pair : m_column_vector.m_data) { pair.first = p.get_rev(pair.first); } #ifdef Z3DEBUG // lp_assert(deb == *this); #endif } } z3-z3-4.8.7/src/util/lp/explanation.h000066400000000000000000000011351356505360400173010ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Nikolaj Bjorner (nbjorner) Lev Nachmanson (levnach) Revision History: --*/ #pragma once namespace lp { struct explanation { void clear() { m_explanation.clear(); } vector> m_explanation; void push_justification(constraint_index j, const mpq& v) { m_explanation.push_back(std::make_pair(v, j)); } void push_justification(constraint_index j) { m_explanation.push_back(std::make_pair(one_of_type(), j)); } }; } z3-z3-4.8.7/src/util/lp/general_matrix.h000066400000000000000000000176421356505360400177720ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #pragma once #include namespace lp { class general_matrix { // fields permutation_matrix m_row_permutation; permutation_matrix m_column_permutation; vector> m_data; public: unsigned adjust_row(unsigned row) const{ return m_row_permutation[row]; } void push_row(vector & v) { m_data.push_back(v); m_row_permutation.resize(m_data.size()); m_column_permutation.resize(v.size()); } unsigned adjust_column(unsigned col) const{ return m_column_permutation.apply_reverse(col); } unsigned adjust_row_inverse(unsigned row) const{ return m_row_permutation.apply_reverse(row); } unsigned adjust_column_inverse(unsigned col) const{ return m_column_permutation[col]; } unsigned row_count() const { return m_data.size(); } unsigned column_count() const { return !m_data.empty()? m_data[0].size() : 0; } class ref_row { general_matrix& m_matrix; vector& m_row_data; public: ref_row(general_matrix& m, vector& row_data) : m_matrix(m), m_row_data(row_data) {} mpq & operator[](unsigned col) { return m_row_data[m_matrix.adjust_column(col)]; } }; class ref_row_const { const general_matrix& m_matrix; const vector& m_row_data; public: ref_row_const(const general_matrix& m, const vector& row_data) : m_matrix(m), m_row_data(row_data) {} const mpq& operator[](unsigned col) const { return m_row_data[m_matrix.adjust_column(col)]; } }; ref_row operator[](unsigned i) { return ref_row(*this, m_data[adjust_row(i)]); } ref_row_const operator[](unsigned i) const { return ref_row_const(*this, m_data[adjust_row(i)]); } void print(std::ostream & out, unsigned blanks = 0) const { unsigned m = row_count(); unsigned n = column_count(); general_matrix g(m, n); for (unsigned i = 0; i < m; i++) for (unsigned j = 0; j < n; j++) g[i][j] = (*this)[i][j]; print_matrix(g.m_data, out, blanks); } void print(std::ostream & out, const char * ss) const { std::string s(ss); out << s; print(out, static_cast(s.size())); } void print_submatrix(std::ostream & out, unsigned k, unsigned blanks = 0) const { general_matrix m(row_count() - k, column_count() - k); for (unsigned i = k; i < row_count(); i++) { for (unsigned j = k; j < column_count(); j++) m[i-k][j-k] = (*this)[i][j]; } print_matrix(m.m_data, out, blanks); } void clear() { m_data.clear(); } bool row_is_initialized_correctly(const vector& row) { lp_assert(row.size() == column_count()); for (unsigned j = 0; j < row.size(); j ++) lp_assert(is_zero(row[j])); return true; } template void init_row_from_container(int i, const T & c, std::function column_fix, const mpq& sign) { auto & row = m_data[adjust_row(i)]; lp_assert(row_is_initialized_correctly(row)); for (const auto & p : c) { unsigned j = adjust_column(column_fix(p.var())); row[j] = sign * p.coeff(); } } void copy_column_to_indexed_vector(unsigned entering, indexed_vector &w ) const { lp_assert(false); // not implemented } general_matrix operator*(const general_matrix & m) const { lp_assert(m.row_count() == column_count()); general_matrix ret(row_count(), m.column_count()); for (unsigned i = 0; i < row_count(); i ++) { for (unsigned j = 0; j < m.column_count(); j++) { mpq a(0); for (unsigned k = 0; k < column_count(); k++) a += ((*this)[i][k])*m[k][j]; ret[i][j] = a; } } return ret; } bool elements_are_equal(const general_matrix& m) const { for (unsigned i = 0; i < row_count(); i++) for (unsigned j = 0; j < column_count(); j++) if ( (*this)[i][j] != m[i][j]) return false; return true; } bool elements_are_equal_modulo(const general_matrix& m, const mpq & d) const { for (unsigned i = 0; i < row_count(); i++) for (unsigned j = 0; j < column_count(); j++) if (!is_zero(((*this)[i][j] - m[i][j]) % d)) return false; return true; } bool operator==(const general_matrix& m) const { return row_count() == m.row_count() && column_count() == m.column_count() && elements_are_equal(m); } bool operator!=(const general_matrix& m) const { return !(*this == m); } bool equal_modulo(const general_matrix& m, const mpq & d) const { return row_count() == m.row_count() && column_count() == m.column_count() && elements_are_equal_modulo(m, d); } vector operator*(const vector & x) const { vector r; lp_assert(x.size() == column_count()); for (unsigned i = 0; i < row_count(); i++) { mpq v(0); for (unsigned j = 0; j < column_count(); j++) { v += (*this)[i][j] * x[j]; } r.push_back(v); } return r; } // bool create_upper_triangle(general_matrix& m, vector& x) { // for (unsigned i = 1; i < m.row_count(); i++) { // lp_assert(false); // to be continued // } // } // bool solve_A_x_equal_b(const general_matrix& m, vector& x, const vector& b) const { // auto m_copy = m; // // for square matrices // lp_assert(row_count() == b.size()); // lp_assert(x.size() == column_count()); // lp_assert(row_count() == column_count()); // x = b; // create_upper_triangle(copy_of_m, x); // solve_on_triangle(copy_of_m, x); // } // void transpose_rows(unsigned i, unsigned l) { lp_assert(i != l); m_row_permutation.transpose_from_right(i, l); } void transpose_columns(unsigned j, unsigned k) { lp_assert(j != k); m_column_permutation.transpose_from_left(j, k); } general_matrix(){} general_matrix(unsigned n) : m_row_permutation(n), m_column_permutation(n), m_data(n) { for (auto& v : m_data){ v.resize(n); } } general_matrix(unsigned m, unsigned n) : m_row_permutation(m), m_column_permutation(n), m_data(m) { for (auto& v : m_data){ v.resize(n); } } void shrink_to_rank(const svector& basis_rows) { if (basis_rows.size() == row_count()) return; vector> data; // todo : not efficient code for (unsigned i : basis_rows) data.push_back(m_data[i]); m_data = data; } // used for debug only general_matrix take_first_n_columns(unsigned n) const { lp_assert(n <= column_count()); if (n == column_count()) return *this; general_matrix ret(row_count(), n); for (unsigned i = 0; i < row_count(); i++) for (unsigned j = 0; j < n; j++) ret[i][j] = (*this)[i][j]; return ret; } inline friend vector operator*(const vector & f, const general_matrix& a) { vector r(a.column_count()); for (unsigned j = 0; j < a.column_count(); j ++) { mpq t = zero_of_type(); for (unsigned i = 0; i < a.row_count(); i++) { t += f[i] * a[i][j]; } r[j] = t; } return r; } }; } z3-z3-4.8.7/src/util/lp/gomory.cpp000066400000000000000000000305111356505360400166260ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Nikolaj Bjorner (nbjorner) Lev Nachmanson (levnach) Revision History: --*/ #include "util/lp/gomory.h" #include "util/lp/int_solver.h" #include "util/lp/lar_solver.h" #include "util/lp/lp_utils.h" namespace lp { class gomory::imp { lar_term & m_t; // the term to return in the cut mpq & m_k; // the right side of the cut explanation& m_ex; // the conflict explanation unsigned m_inf_col; // a basis column which has to be an integer but has a non integral value const row_strip& m_row; const int_solver& m_int_solver; mpq m_lcm_den; mpq m_f; mpq m_one_minus_f; mpq m_fj; mpq m_one_minus_fj; const impq & get_value(unsigned j) const { return m_int_solver.get_value(j); } bool is_real(unsigned j) const { return m_int_solver.is_real(j); } bool at_lower(unsigned j) const { return m_int_solver.at_lower(j); } bool at_upper(unsigned j) const { return m_int_solver.at_upper(j); } const impq & lower_bound(unsigned j) const { return m_int_solver.lower_bound(j); } const impq & upper_bound(unsigned j) const { return m_int_solver.upper_bound(j); } constraint_index column_lower_bound_constraint(unsigned j) const { return m_int_solver.column_lower_bound_constraint(j); } constraint_index column_upper_bound_constraint(unsigned j) const { return m_int_solver.column_upper_bound_constraint(j); } bool column_is_fixed(unsigned j) const { return m_int_solver.m_lar_solver->column_is_fixed(j); } void int_case_in_gomory_cut(unsigned j) { lp_assert(m_int_solver.column_is_int(j) && m_fj.is_pos()); TRACE("gomory_cut_detail", tout << " k = " << m_k; tout << ", fj: " << m_fj << ", "; tout << (at_lower(j)?"at_lower":"at_upper")<< std::endl; ); mpq new_a; if (at_lower(j)) { new_a = m_fj <= m_one_minus_f ? m_fj / m_one_minus_f : ((1 - m_fj) / m_f); lp_assert(new_a.is_pos()); m_k.addmul(new_a, lower_bound(j).x); m_ex.push_justification(column_lower_bound_constraint(j)); } else { lp_assert(at_upper(j)); // the upper terms are inverted: therefore we have the minus new_a = - (m_fj <= m_f ? m_fj / m_f : ((1 - m_fj) / m_one_minus_f)); lp_assert(new_a.is_neg()); m_k.addmul(new_a, upper_bound(j).x); m_ex.push_justification(column_upper_bound_constraint(j)); } m_t.add_monomial(new_a, j); m_lcm_den = lcm(m_lcm_den, denominator(new_a)); TRACE("gomory_cut_detail", tout << "v" << j << " new_a = " << new_a << ", k = " << m_k << ", m_lcm_den = " << m_lcm_den << "\n";); } void real_case_in_gomory_cut(const mpq & a, unsigned j) { TRACE("gomory_cut_detail_real", tout << "real\n";); mpq new_a; if (at_lower(j)) { if (a.is_pos()) { new_a = a / m_one_minus_f; } else { new_a = - a / m_f; } m_k.addmul(new_a, lower_bound(j).x); // is it a faster operation than // k += lower_bound(j).x * new_a; m_ex.push_justification(column_lower_bound_constraint(j)); } else { lp_assert(at_upper(j)); if (a.is_pos()) { new_a = - a / m_f; } else { new_a = a / m_one_minus_f; } m_k.addmul(new_a, upper_bound(j).x); // k += upper_bound(j).x * new_a; m_ex.push_justification(column_upper_bound_constraint(j)); } TRACE("gomory_cut_detail_real", tout << a << "*v" << j << " k: " << m_k << "\n";); m_t.add_monomial(new_a, j); } lia_move report_conflict_from_gomory_cut() { lp_assert(m_k.is_pos()); // conflict 0 >= k where k is positive m_k.neg(); // returning 0 <= -k return lia_move::conflict; } void adjust_term_and_k_for_some_ints_case_gomory() { lp_assert(!m_t.is_empty()); // k = 1 + sum of m_t at bounds auto pol = m_t.coeffs_as_vector(); m_t.clear(); if (pol.size() == 1) { TRACE("gomory_cut_detail", tout << "pol.size() is 1" << std::endl;); unsigned v = pol[0].second; lp_assert(m_int_solver.column_is_int(v)); const mpq& a = pol[0].first; m_k /= a; if (a.is_pos()) { // we have av >= k if (!m_k.is_int()) m_k = ceil(m_k); // switch size m_t.add_monomial(- mpq(1), v); m_k.neg(); } else { if (!m_k.is_int()) m_k = floor(m_k); m_t.add_monomial(mpq(1), v); } } else { m_lcm_den = lcm(m_lcm_den, denominator(m_k)); lp_assert(m_lcm_den.is_pos()); TRACE("gomory_cut_detail", tout << "pol.size() > 1 den: " << m_lcm_den << std::endl;); if (!m_lcm_den.is_one()) { // normalize coefficients of integer parameters to be integers. for (auto & pi: pol) { pi.first *= m_lcm_den; SASSERT(!m_int_solver.column_is_int(pi.second) || pi.first.is_int()); } m_k *= m_lcm_den; } // negate everything to return -pol <= -m_k for (const auto & pi: pol) { TRACE("gomory_cut", tout << pi.first << "* " << "v" << pi.second << "\n";); m_t.add_monomial(-pi.first, pi.second); } m_k.neg(); } TRACE("gomory_cut_detail", tout << "k = " << m_k << std::endl;); lp_assert(m_k.is_int()); } std::string var_name(unsigned j) const { return std::string("x") + std::to_string(j); } std::ostream& dump_coeff_val(std::ostream & out, const mpq & a) const { if (a.is_int()) { out << a; } else if ( a >= zero_of_type()) out << "(/ " << numerator(a) << " " << denominator(a) << ")"; else { out << "(- ( / " << numerator(-a) << " " << denominator(-a) << "))"; } return out; } template void dump_coeff(std::ostream & out, const T& c) const { out << "( * "; dump_coeff_val(out, c.coeff()); out << " " << var_name(c.var()) << ")"; } std::ostream& dump_row_coefficients(std::ostream & out) const { mpq lc(1); for (const auto& p : m_row) { lc = lcm(lc, denominator(p.coeff())); } for (const auto& p : m_row) { dump_coeff_val(out << " (* ", p.coeff()*lc) << " " << var_name(p.var()) << ")"; } return out; } void dump_the_row(std::ostream& out) const { out << "; the row, excluding fixed vars\n"; out << "(assert ( = ( +"; dump_row_coefficients(out) << ") 0))\n"; } void dump_declaration(std::ostream& out, unsigned v) const { out << "(declare-const " << var_name(v) << (m_int_solver.column_is_int(v) ? " Int" : " Real") << ")\n"; } void dump_declarations(std::ostream& out) const { // for a column j the var name is vj for (const auto & p : m_row) { dump_declaration(out, p.var()); } for (const auto& p : m_t) { unsigned v = p.var(); if (m_int_solver.m_lar_solver->is_term(v)) { dump_declaration(out, v); } } } void dump_lower_bound_expl(std::ostream & out, unsigned j) const { out << "(assert (>= " << var_name(j) << " " << lower_bound(j).x << "))\n"; } void dump_upper_bound_expl(std::ostream & out, unsigned j) const { out << "(assert (<= " << var_name(j) << " " << upper_bound(j).x << "))\n"; } void dump_explanations(std::ostream& out) const { for (const auto & p : m_row) { unsigned j = p.var(); if (j == m_inf_col || (!is_real(j) && p.coeff().is_int())) { continue; } else if (at_lower(j)) { dump_lower_bound_expl(out, j); } else { lp_assert(at_upper(j)); dump_upper_bound_expl(out, j); } } } std::ostream& dump_term_coefficients(std::ostream & out) const { for (const auto& p : m_t) { dump_coeff(out, p); } return out; } std::ostream& dump_term_sum(std::ostream & out) const { return dump_term_coefficients(out << "(+ ") << ")"; } std::ostream& dump_term_le_k(std::ostream & out) const { return dump_term_sum(out << "(<= ") << " " << m_k << ")"; } void dump_the_cut_assert(std::ostream & out) const { dump_term_le_k(out << "(assert (not ") << "))\n"; } void dump_cut_and_constraints_as_smt_lemma(std::ostream& out) const { dump_declarations(out); dump_the_row(out); dump_explanations(out); dump_the_cut_assert(out); out << "(check-sat)\n"; } public: lia_move create_cut() { TRACE("gomory_cut", tout << "applying cut at:\n"; print_linear_combination_of_column_indices_only(m_row, tout); tout << std::endl; for (auto & p : m_row) { m_int_solver.m_lar_solver->m_mpq_lar_core_solver.m_r_solver.print_column_info(p.var(), tout); } tout << "inf_col = " << m_inf_col << std::endl; ); // gomory will be t <= k and the current solution has a property t > k m_k = 1; m_t.clear(); mpq m_lcm_den(1); bool some_int_columns = false; mpq m_f = fractional_part(get_value(m_inf_col)); TRACE("gomory_cut_detail", tout << "m_f: " << m_f << ", "; tout << "1 - m_f: " << 1 - m_f << ", get_value(m_inf_col).x - m_f = " << get_value(m_inf_col).x - m_f;); lp_assert(m_f.is_pos() && (get_value(m_inf_col).x - m_f).is_int()); mpq one_min_m_f = 1 - m_f; for (const auto & p : m_row) { unsigned j = p.var(); if (j == m_inf_col) { lp_assert(p.coeff() == one_of_type()); TRACE("gomory_cut_detail", tout << "seeing basic var";); continue; } // use -p.coeff() to make the format compatible with the format used in: Integrating Simplex with DPLL(T) if (is_real(j)) { real_case_in_gomory_cut(- p.coeff(), j); } else { if (p.coeff().is_int()) { // m_fj will be zero and no monomial will be added continue; } some_int_columns = true; m_fj = fractional_part(-p.coeff()); m_one_minus_fj = 1 - m_fj; int_case_in_gomory_cut(j); } } if (m_t.is_empty()) return report_conflict_from_gomory_cut(); if (some_int_columns) adjust_term_and_k_for_some_ints_case_gomory(); lp_assert(m_int_solver.current_solution_is_inf_on_cut()); TRACE("gomory_cut_detail", dump_cut_and_constraints_as_smt_lemma(tout);); m_int_solver.m_lar_solver->subs_term_columns(m_t); TRACE("gomory_cut", print_linear_combination_of_column_indices_only(m_t, tout << "gomory cut:"); tout << " <= " << m_k << std::endl;); return lia_move::cut; } imp(lar_term & t, mpq & k, explanation& ex, unsigned basic_inf_int_j, const row_strip& row, const int_solver& int_slv ) : m_t(t), m_k(k), m_ex(ex), m_inf_col(basic_inf_int_j), m_row(row), m_int_solver(int_slv), m_lcm_den(1), m_f(fractional_part(get_value(basic_inf_int_j).x)), m_one_minus_f(1 - m_f) {} }; lia_move gomory::create_cut() { return m_imp->create_cut(); } gomory::gomory(lar_term & t, mpq & k, explanation& ex, unsigned basic_inf_int_j, const row_strip& row, const int_solver& s) { m_imp = alloc(imp, t, k, ex, basic_inf_int_j, row, s); } gomory::~gomory() { dealloc(m_imp); } } z3-z3-4.8.7/src/util/lp/gomory.h000066400000000000000000000011441356505360400162730ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Nikolaj Bjorner (nbjorner) Lev Nachmanson (levnach) Revision History: --*/ #pragma once #include "util/lp/lar_term.h" #include "util/lp/lia_move.h" #include "util/lp/explanation.h" #include "util/lp/static_matrix.h" namespace lp { class int_solver; class gomory { class imp; imp *m_imp; public : gomory(lar_term & t, mpq & k, explanation& ex, unsigned basic_inf_int_j, const row_strip& row, const int_solver& s); lia_move create_cut(); ~gomory(); }; } z3-z3-4.8.7/src/util/lp/hnf.h000066400000000000000000000453221356505360400155400ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Creates the Hermite Normal Form of a matrix in place. We suppose that $A$ is an integral $m$ by $n$ matrix or rank $m$, where $n >= m$. The paragraph below is applicable to the usage of HNF. We have $H = AU$ where $H$ is in Hermite Normal Form and $U$ is a unimodular matrix. We do not have an explicit representation of $U$. For a given $i$ we need to find the $i$-th row of $U^{-1}$. Let $e_i$ be a vector of length $m$ with all elements equal to $0$ and $1$ at $i$-th position. Then we need to find the row vector $e_iU^{-1}=t$. Noticing that $U^{-1} = H^{-1}A$, we have $e_iH^{-1}A=t$. We find $e_iH^{-1} = f$ by solving $e_i = fH$ and then $fA$ gives us $t$. Author: Lev Nachmanson (levnach) Revision History: --*/ #pragma once #include "util/lp/numeric_pair.h" #include "util/ext_gcd.h" namespace lp { namespace hnf_calc { // d = u * a + v * b and the sum of abs(u) + abs(v) is minimal, d is positive inline void extended_gcd_minimal_uv(const mpq & a, const mpq & b, mpq & d, mpq & u, mpq & v) { if (is_zero(a)) { u = zero_of_type(); v = one_of_type(); d = b; return; } if (is_zero(b)) { u = one_of_type(); v = zero_of_type(); d = a; return; } #if 1 d = gcd(a, b, u, v); #else extended_gcd(a, b, d, u, v); #endif if (is_neg(d)) { d = -d; u = -u; v = -v; } if (d == a) { u = one_of_type(); v = zero_of_type(); return; } if (d == -a) { u = - one_of_type(); v = zero_of_type(); return; } mpq a_over_d = abs(a) / d; mpq r; mpq k = machine_div_rem(v, a_over_d, r); if (is_neg(r)) { r += a_over_d; k -= one_of_type(); } lp_assert(v == k * a_over_d + r); if (is_pos(b)) { v = r - a_over_d; // v -= (k + 1) * a_over_d; lp_assert(- a_over_d < v && v <= zero_of_type()); if (is_pos(a)) { u += (k + 1) * (b / d); lp_assert( one_of_type() <= u && u <= abs(b)/d); } else { u -= (k + 1) * (b / d); lp_assert( one_of_type() <= -u && -u <= abs(b)/d); } } else { v = r; // v -= k * a_over_d; lp_assert(- a_over_d < -v && -v <= zero_of_type()); if (is_pos(a)) { u += k * (b / d); lp_assert( one_of_type() <= u && u <= abs(b)/d); } else { u -= k * (b / d); lp_assert( one_of_type() <= -u && -u <= abs(b)/d); } } lp_assert(d == u * a + v * b); } template bool prepare_pivot_for_lower_triangle(M &m, unsigned r) { for (unsigned i = r; i < m.row_count(); i++) { for (unsigned j = r; j < m.column_count(); j++) { if (!is_zero(m[i][j])) { if (i != r) { m.transpose_rows(i, r); } if (j != r) { m.transpose_columns(j, r); } return true; } } } return false; } template void pivot_column_non_fractional(M &m, unsigned r, bool & overflow, const mpq & big_number) { lp_assert(!is_zero(m[r][r])); for (unsigned j = r + 1; j < m.column_count(); j++) { for (unsigned i = r + 1; i < m.row_count(); i++) { if ( (m[i][j] = (r > 0) ? (m[r][r]*m[i][j] - m[i][r]*m[r][j]) / m[r-1][r-1] : (m[r][r]*m[i][j] - m[i][r]*m[r][j])) >= big_number) { overflow = true; return; } lp_assert(is_integer(m[i][j])); } } } // returns the rank of the matrix template unsigned to_lower_triangle_non_fractional(M &m, bool & overflow, const mpq& big_number) { unsigned i = 0; for (; i < m.row_count(); i++) { if (!prepare_pivot_for_lower_triangle(m, i)) { return i; } pivot_column_non_fractional(m, i, overflow, big_number); if (overflow) return 0; } lp_assert(i == m.row_count()); return i; } // returns gcd of values below diagonal i,i template mpq gcd_of_row_starting_from_diagonal(const M& m, unsigned i) { mpq g = zero_of_type(); unsigned j = i; for (; j < m.column_count() && is_zero(g); j++) { const auto & t = m[i][j]; if (!is_zero(t)) g = abs(t); } lp_assert(!is_zero(g)); for (; j < m.column_count(); j++) { const auto & t = m[i][j]; if (!is_zero(t)) g = gcd(g, t); } return g; } // It fills "r" - the basic rows of m. // The plan is to transform m to the lower triangular form by using non-fractional Gaussian Elimination by columns. // Then the trailing after the diagonal elements of the following elements of the last non-zero row of the matrix, // namely, m[r-1][r-1], m[r-1][r], ..., m[r-1]m[m.column_count() - 1] give the determinants of all minors of rank r. // The gcd of these minors is the return value. template mpq determinant_of_rectangular_matrix(const M& m, svector & basis_rows, const mpq& big_number) { auto m_copy = m; bool overflow = false; unsigned rank = to_lower_triangle_non_fractional(m_copy, overflow, big_number); if (overflow) return big_number; if (rank == 0) return one_of_type(); for (unsigned i = 0; i < rank; i++) { basis_rows.push_back(m_copy.adjust_row(i)); } TRACE("hnf_calc", tout << "basis_rows = "; print_vector(basis_rows, tout); m_copy.print(tout, "m_copy = ");); return gcd_of_row_starting_from_diagonal(m_copy, rank - 1); } } // end of namespace hnf_calc template // M is the matrix type class hnf { // fields #ifdef Z3DEBUG M m_H; M m_U; M m_U_reverse; M m_A_orig; #endif M m_W; vector m_buffer; unsigned m_m; unsigned m_n; mpq m_d; // it is a positive number and a multiple of gcd of r-minors of m_A_orig, where r is the rank of m_A_orig // we suppose that the rank of m_A is equal to row_count(), and that row_count() <= column_count(), that is m_A has the full rank unsigned m_i; unsigned m_j; mpq m_R; mpq m_half_R; mpq mod_R_balanced(const mpq & a) const { mpq t = a % m_R; return t > m_half_R? t - m_R : (t < - m_half_R? t + m_R : t); } mpq mod_R(const mpq & a) const { mpq t = a % m_R; t = is_neg(t) ? t + m_R : t; CTRACE("hnf", is_neg(t), tout << "a=" << a << ", m_R= " << m_R << std::endl;); return t; } #ifdef Z3DEBUG void buffer_p_col_i_plus_q_col_j_H(const mpq & p, unsigned i, const mpq & q, unsigned j) { for (unsigned k = i; k < m_m; k++) { m_buffer[k] = p * m_H[k][i] + q * m_H[k][j]; } } #endif bool zeros_in_column_W_above(unsigned i) { for (unsigned k = 0; k < i; k++) if (!is_zero(m_W[k][i])) return false; return true; } void buffer_p_col_i_plus_q_col_j_W_modulo(const mpq & p, const mpq & q) { lp_assert(zeros_in_column_W_above(m_i)); for (unsigned k = m_i; k < m_m; k++) { m_buffer[k] = mod_R_balanced(mod_R_balanced(p * m_W[k][m_i]) + mod_R_balanced(q * m_W[k][m_j])); } } #ifdef Z3DEBUG void buffer_p_col_i_plus_q_col_j_U(const mpq & p, unsigned i, const mpq & q, unsigned j) { for (unsigned k = 0; k < m_n; k++) { m_buffer[k] = p * m_U[k][i] + q * m_U[k][j]; } } void pivot_column_i_to_column_j_H(mpq u, unsigned i, mpq v, unsigned j) { lp_assert(is_zero(u * m_H[i][i] + v * m_H[i][j])); m_H[i][j] = zero_of_type(); for (unsigned k = i + 1; k < m_m; k ++) m_H[k][j] = u * m_H[k][i] + v * m_H[k][j]; } #endif void pivot_column_i_to_column_j_W_modulo(mpq u, mpq v) { lp_assert(is_zero((u * m_W[m_i][m_i] + v * m_W[m_i][m_j]) % m_R)); m_W[m_i][m_j] = zero_of_type(); for (unsigned k = m_i + 1; k < m_m; k ++) m_W[k][m_j] = mod_R_balanced(mod_R_balanced(u * m_W[k][m_i]) + mod_R_balanced(v * m_W[k][m_j])); } #ifdef Z3DEBUG void pivot_column_i_to_column_j_U(mpq u, unsigned i, mpq v, unsigned j) { for (unsigned k = 0; k < m_n; k ++) m_U[k][j] = u * m_U[k][i] + v * m_U[k][j]; } void copy_buffer_to_col_i_H(unsigned i) { for (unsigned k = i; k < m_m; k++) { m_H[k][i] = m_buffer[k]; } } void copy_buffer_to_col_i_U(unsigned i) { for (unsigned k = 0; k < m_n; k++) m_U[k][i] = m_buffer[k]; } // multiply by (a, b) // (c, d) // from the left where i and j are the modified columns // the [i][i] = a, and [i][j] = b for the matrix we multiply by void multiply_U_reverse_from_left_by(unsigned i, unsigned j, const mpq & a, const mpq & b, const mpq & c, const mpq d) { // the new i-th row goes to the buffer for (unsigned k = 0; k < m_n; k++) { m_buffer[k] = a * m_U_reverse[i][k] + b * m_U_reverse[j][k]; } // calculate the new j-th row in place for (unsigned k = 0; k < m_n; k++) { m_U_reverse[j][k] = c * m_U_reverse[i][k] + d * m_U_reverse[j][k]; } // copy the buffer into i-th row for (unsigned k = 0; k < m_n; k++) { m_U_reverse[i][k] = m_buffer[k]; } } void handle_column_ij_in_row_i(unsigned i, unsigned j) { const mpq& aii = m_H[i][i]; const mpq& aij = m_H[i][j]; mpq p,q,r; extended_gcd(aii, aij, r, p, q); mpq aii_over_r = aii / r; mpq aij_over_r = aij / r; buffer_p_col_i_plus_q_col_j_H(p, i, q, j); pivot_column_i_to_column_j_H(- aij_over_r, i, aii_over_r, j); copy_buffer_to_col_i_H(i); buffer_p_col_i_plus_q_col_j_U(p, i, q, j); pivot_column_i_to_column_j_U(- aij_over_r, i, aii_over_r, j); copy_buffer_to_col_i_U(i); // U was multiplied from the right by (p, - aij_over_r) // (q, aii_over_r ) // We need to multiply U_reverse by (aii_over_r, aij_over_r) // (-q , p) // from the left multiply_U_reverse_from_left_by(i, j, aii_over_r, aij_over_r, -q, p); CASSERT("hnf_test", is_correct_modulo()); } void switch_sign_for_column(unsigned i) { for (unsigned k = i; k < m_m; k++) m_H[k][i].neg(); for (unsigned k = 0; k < m_n; k++) m_U[k][i].neg(); // switch sign for the i-th row in the reverse m_U_reverse for (unsigned k = 0; k < m_n; k++) m_U_reverse[i][k].neg(); } void process_row_column(unsigned i, unsigned j){ if (is_zero(m_H[i][j])) return; handle_column_ij_in_row_i(i, j); } void replace_column_j_by_j_minus_u_col_i_H(unsigned i, unsigned j, const mpq & u) { lp_assert(j < i); for (unsigned k = i; k < m_m; k++) { m_H[k][j] -= u * m_H[k][i]; } } void replace_column_j_by_j_minus_u_col_i_U(unsigned i, unsigned j, const mpq & u) { lp_assert(j < i); for (unsigned k = 0; k < m_n; k++) { m_U[k][j] -= u * m_U[k][i]; } // Here we multiply from m_U from the right by the matrix ( 1, 0) // ( -u, 1). // To adjust the reverse we multiply it from the left by (1, 0) // (u, 1) for (unsigned k = 0; k < m_n; k++) { m_U_reverse[i][k] += u * m_U_reverse[j][k]; } } void work_on_columns_less_than_i_in_the_triangle(unsigned i) { const mpq & mii = m_H[i][i]; if (is_zero(mii)) return; for (unsigned j = 0; j < i; j++) { const mpq & mij = m_H[i][j]; if (!is_pos(mij) && - mij < mii) continue; mpq u = ceil(mij / mii); replace_column_j_by_j_minus_u_col_i_H(i, j, u); replace_column_j_by_j_minus_u_col_i_U(i, j, u); } } void process_row(unsigned i) { for (unsigned j = i + 1; j < m_n; j++) { process_row_column(i, j); } if (i >= m_n) { lp_assert(m_H == m_A_orig * m_U); return; } if (is_neg(m_H[i][i])) switch_sign_for_column(i); work_on_columns_less_than_i_in_the_triangle(i); CASSERT("hnf_test", is_correct_modulo()); } void calculate() { for (unsigned i = 0; i < m_m; i++) { process_row(i); } } void prepare_U_and_U_reverse() { m_U = M(m_H.column_count()); for (unsigned i = 0; i < m_U.column_count(); i++) m_U[i][i] = 1; m_U_reverse = m_U; lp_assert(m_H == m_A_orig * m_U); } bool row_is_correct_form(unsigned i) const { if (i >= m_n) return true; const mpq& hii = m_H[i][i]; if (is_neg(hii)) return false; for (unsigned j = 0; j < i; j++) { const mpq & hij = m_H[i][j]; if (is_pos(hij)) return false; if (!is_zero(hii) && - hij >= hii) return false; } return true; } bool is_correct_form() const { for (unsigned i = 0; i < m_m; i++) if (!row_is_correct_form(i)) return false; return true; } bool is_correct() const { return m_H == m_A_orig * m_U && is_unit_matrix(m_U * m_U_reverse); } bool is_correct_modulo() const { return m_H.equal_modulo(m_A_orig * m_U, m_d) && is_unit_matrix(m_U * m_U_reverse); } bool is_correct_final() const { if (!is_correct()) { TRACE("hnf_calc", tout << "m_H = "; m_H.print(tout, 17); tout << "\nm_A_orig * m_U = "; (m_A_orig * m_U).print(tout, 17); tout << "is_correct() does not hold" << std::endl;); return false; } if (!is_correct_form()) { TRACE("hnf_calc", tout << "is_correct_form() does not hold" << std::endl;); return false; } return true; } public: const M& H() const { return m_H;} const M& U() const { return m_U;} const M& U_reverse() const { return m_U_reverse; } private: #endif void copy_buffer_to_col_i_W_modulo() { for (unsigned k = m_i; k < m_m; k++) { m_W[k][m_i] = m_buffer[k]; } } void replace_column_j_by_j_minus_u_col_i_W(unsigned j, const mpq & u) { lp_assert(j < m_i); for (unsigned k = m_i; k < m_m; k++) { m_W[k][j] -= u * m_W[k][m_i]; // m_W[k][j] = mod_R_balanced(m_W[k][j]); } } bool is_unit_matrix(const M& u) const { unsigned m = u.row_count(); unsigned n = u.column_count(); if (m != n) return false; for (unsigned i = 0; i < m; i ++) for (unsigned j = 0; j < n; j++) { if (i == j) { if (one_of_type() != u[i][j]) return false; } else { if (!is_zero(u[i][j])) return false; } } return true; } // follows Algorithm 2.4.8 of Henri Cohen's "A course on computational algebraic number theory", // with some changes related to that we create a low triangle matrix // with non-positive elements under the diagonal void process_column_in_row_modulo() { const mpq& aii = m_W[m_i][m_i]; const mpq& aij = m_W[m_i][m_j]; mpq d, p,q; hnf_calc::extended_gcd_minimal_uv(aii, aij, d, p, q); if (is_zero(d)) return; mpq aii_over_d = mod_R(aii / d); mpq aij_over_d = mod_R(aij / d); buffer_p_col_i_plus_q_col_j_W_modulo(p, q); pivot_column_i_to_column_j_W_modulo(- aij_over_d, aii_over_d); copy_buffer_to_col_i_W_modulo(); } void fix_row_under_diagonal_W_modulo() { mpq d, u, v; if (is_zero(m_W[m_i][m_i])) { m_W[m_i][m_i] = m_R; u = one_of_type(); d = m_R; } else { hnf_calc::extended_gcd_minimal_uv(m_W[m_i][m_i], m_R, d, u, v); } auto & mii = m_W[m_i][m_i]; mii *= u; mii = mod_R(mii); if (is_zero(mii)) mii = d; lp_assert(is_pos(mii)); // adjust column m_i for (unsigned k = m_i + 1; k < m_m; k++) { m_W[k][m_i] *= u; m_W[k][m_i] = mod_R_balanced(m_W[k][m_i]); } lp_assert(is_pos(mii)); for (unsigned j = 0; j < m_i; j++) { const mpq & mij = m_W[m_i][j]; if (!is_pos(mij) && - mij < mii) continue; mpq q = ceil(mij / mii); replace_column_j_by_j_minus_u_col_i_W(j, q); } } void process_row_modulo() { for (m_j = m_i + 1; m_j < m_n; m_j++) { process_column_in_row_modulo(); } fix_row_under_diagonal_W_modulo(); } void calculate_by_modulo() { for (m_i = 0; m_i < m_m; m_i ++) { process_row_modulo(); lp_assert(is_pos(m_W[m_i][m_i])); m_R /= m_W[m_i][m_i]; lp_assert(is_integer(m_R)); m_half_R = floor(m_R / 2); } } public: hnf(M & A, const mpq & d) : #ifdef Z3DEBUG m_H(A), m_A_orig(A), #endif m_W(A), m_buffer(std::max(A.row_count(), A.column_count())), m_m(A.row_count()), m_n(A.column_count()), m_d(d), m_R(m_d), m_half_R(floor(m_R / 2)) { if (m_m == 0 || m_n == 0 || is_zero(m_d)) return; #ifdef Z3DEBUG prepare_U_and_U_reverse(); calculate(); CASSERT("hnf_test", is_correct_final()); #endif calculate_by_modulo(); #ifdef Z3DEBUG CTRACE("hnf_calc", m_H != m_W, tout << "A = "; m_A_orig.print(tout, 4); tout << std::endl; tout << "H = "; m_H.print(tout, 4); tout << std::endl; tout << "W = "; m_W.print(tout, 4); tout << std::endl;); lp_assert (m_H == m_W); #endif } const M & W() const { return m_W; } }; } z3-z3-4.8.7/src/util/lp/hnf_cutter.h000066400000000000000000000163541356505360400171310ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #pragma once #include "util/lp/lar_term.h" #include "util/lp/hnf.h" #include "util/lp/general_matrix.h" #include "util/lp/var_register.h" #include "util/lp/lia_move.h" #include "util/lp/explanation.h" namespace lp { class hnf_cutter { var_register m_var_register; general_matrix m_A; vector m_terms; vector m_terms_upper; svector m_constraints_for_explanation; vector m_right_sides; lp_settings & m_settings; mpq m_abs_max; bool m_overflow; public: const mpq & abs_max() const { return m_abs_max; } hnf_cutter(lp_settings & settings) : m_settings(settings), m_abs_max(zero_of_type()) {} unsigned terms_count() const { return m_terms.size(); } const vector& terms() const { return m_terms; } const svector& constraints_for_explanation() const { return m_constraints_for_explanation; } const vector & right_sides() const { return m_right_sides; } void clear() { // m_A will be filled from scratch in init_matrix_A m_var_register.clear(); m_terms.clear(); m_terms_upper.clear(); m_constraints_for_explanation.clear(); m_right_sides.clear(); m_abs_max = zero_of_type(); m_overflow = false; } void add_term(const lar_term* t, const mpq &rs, constraint_index ci, bool upper_bound) { m_terms.push_back(t); m_terms_upper.push_back(upper_bound); if (upper_bound) m_right_sides.push_back(rs); else m_right_sides.push_back(-rs); m_constraints_for_explanation.push_back(ci); for (const auto &p : *t) { m_var_register.add_var(p.var()); mpq t = abs(ceil(p.coeff())); if (t > m_abs_max) m_abs_max = t; } } void print(std::ostream & out) { out << "terms = " << m_terms.size() << ", var = " << m_var_register.size() << std::endl; } void initialize_row(unsigned i) { mpq sign = m_terms_upper[i]? one_of_type(): - one_of_type(); m_A.init_row_from_container(i, * m_terms[i], [this](unsigned j) { return m_var_register.add_var(j);}, sign); } void init_matrix_A() { m_A = general_matrix(terms_count(), vars().size()); for (unsigned i = 0; i < terms_count(); i++) initialize_row(i); } // todo: as we need only one row i with non integral b[i] need to optimize later void find_h_minus_1_b(const general_matrix& H, vector & b) { // the solution will be put into b for (unsigned i = 0; i < H.row_count() ;i++) { for (unsigned j = 0; j < i; j++) { b[i] -= H[i][j]*b[j]; } b[i] /= H[i][i]; // consider return from here if b[i] is not an integer and return i } } vector create_b(const svector & basis_rows) { if (basis_rows.size() == m_right_sides.size()) return m_right_sides; vector b; for (unsigned i : basis_rows) { b.push_back(m_right_sides[i]); } return b; } int find_cut_row_index(const vector & b) { int ret = -1; int n = 0; for (int i = 0; i < static_cast(b.size()); i++) { if (is_integer(b[i])) continue; if (n == 0 ) { lp_assert(ret == -1); n = 1; ret = i; } else { if (m_settings.random_next() % (++n) == 0) { ret = i; } } } return ret; } // fills e_i*H_minus_1 void get_ei_H_minus_1(unsigned i, const general_matrix& H, vector & row) { // we solve x = ei * H_min_1 // or x * H = ei unsigned m = H.row_count(); for (unsigned k = i + 1; k < m; k++) { row[k] = zero_of_type(); } row[i] = one_of_type() / H[i][i]; for(int k = i - 1; k >= 0; k--) { mpq t = zero_of_type(); for (unsigned l = k + 1; l <= i; l++) { t += H[l][k]*row[l]; } row[k] = -t / H[k][k]; } // // test region // vector ei(H.row_count(), zero_of_type()); // ei[i] = one_of_type(); // vector pr = row * H; // pr.shrink(ei.size()); // lp_assert(ei == pr); // // end test region } void fill_term(const vector & row, lar_term& t) { for (unsigned j = 0; j < row.size(); j++) { if (!is_zero(row[j])) t.add_monomial(row[j], m_var_register.local_to_external(j)); } } #ifdef Z3DEBUG vector transform_to_local_columns(const vector & x) const { vector ret; for (unsigned j = 0; j < vars().size(); j++) { ret.push_back(x[m_var_register.local_to_external(j)].x); } return ret; } #endif void shrink_explanation(const svector& basis_rows) { svector new_expl; for (unsigned i : basis_rows) { new_expl.push_back(m_constraints_for_explanation[i]); } m_constraints_for_explanation = new_expl; } bool overflow() const { return m_overflow; } lia_move create_cut(lar_term& t, mpq& k, explanation& ex, bool & upper, const vector & x0) { // we suppose that x0 has at least one non integer element (void)x0; init_matrix_A(); svector basis_rows; mpq big_number = m_abs_max.expt(3); mpq d = hnf_calc::determinant_of_rectangular_matrix(m_A, basis_rows, big_number); // std::cout << "max = " << m_abs_max << ", d = " << d << ", d/max = " << ceil (d /m_abs_max) << std::endl; // std::cout << "max cube " << m_abs_max * m_abs_max * m_abs_max << std::endl; if (d >= big_number) { return lia_move::undef; } if (m_settings.get_cancel_flag()) { return lia_move::undef; } if (basis_rows.size() < m_A.row_count()) { m_A.shrink_to_rank(basis_rows); shrink_explanation(basis_rows); } hnf h(m_A, d); vector b = create_b(basis_rows); lp_assert(m_A * x0 == b); find_h_minus_1_b(h.W(), b); int cut_row = find_cut_row_index(b); if (cut_row == -1) { return lia_move::undef; } // the matrix is not square - we can get // all integers in b's projection vector row(m_A.column_count()); get_ei_H_minus_1(cut_row, h.W(), row); vector f = row * m_A; fill_term(f, t); k = floor(b[cut_row]); upper = true; return lia_move::cut; } svector vars() const { return m_var_register.vars(); } }; } z3-z3-4.8.7/src/util/lp/implied_bound.h000066400000000000000000000026451356505360400176000ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #pragma once #include "util/lp/lp_settings.h" #include "util/lp/lar_constraints.h" namespace lp { struct implied_bound { mpq m_bound; unsigned m_j; // the column for which the bound has been found bool m_is_lower_bound; bool m_coeff_before_j_is_pos; unsigned m_row_or_term_index; bool m_strict; lconstraint_kind kind() const { lconstraint_kind k = m_is_lower_bound? GE : LE; if (m_strict) k = static_cast(k / 2); return k; } bool operator==(const implied_bound & o) const { return m_j == o.m_j && m_is_lower_bound == o.m_is_lower_bound && m_bound == o.m_bound && m_coeff_before_j_is_pos == o.m_coeff_before_j_is_pos && m_row_or_term_index == o.m_row_or_term_index && m_strict == o.m_strict; } implied_bound(){} implied_bound(const mpq & a, unsigned j, bool lower_bound, bool coeff_before_j_is_pos, unsigned row_or_term_index, bool strict): m_bound(a), m_j(j), m_is_lower_bound(lower_bound), m_coeff_before_j_is_pos(coeff_before_j_is_pos), m_row_or_term_index(row_or_term_index), m_strict(strict) { } }; } z3-z3-4.8.7/src/util/lp/indexed_value.h000066400000000000000000000030771356505360400176020ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #pragma once namespace lp { template class indexed_value { public: T m_value; // the idea is that m_index for a row element gives its column, and for a column element its row unsigned m_index; // m_other point is the offset of the corresponding element in its vector : for a row element it point to the column element offset, // for a column element it points to the row element offset unsigned m_other; indexed_value() {} indexed_value(T v, unsigned i) : m_value(v), m_index(i) {} indexed_value(T v, unsigned i, unsigned other) : m_value(v), m_index(i), m_other(other) { } indexed_value(const indexed_value & iv) { m_value = iv.m_value; m_index = iv.m_index; m_other = iv.m_other; } indexed_value & operator=(const indexed_value & right_side) { m_value = right_side.m_value; m_index = right_side.m_index; m_other = right_side.m_other; return *this; } const T & value() const { return m_value; } void set_value(T val) { m_value = val; } }; #ifdef Z3DEBUG template bool check_vector_for_small_values(indexed_vector & w, lp_settings & settings) { for (unsigned i : w.m_index) { const X & v = w[i]; if ((!is_zero(v)) && settings.abs_val_is_smaller_than_drop_tolerance(v)) return false; } return true; } #endif } z3-z3-4.8.7/src/util/lp/indexed_vector.cpp000066400000000000000000000044361356505360400203230ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #include "util/vector.h" #include "util/lp/indexed_vector_def.h" namespace lp { template void indexed_vector::clear(); template void indexed_vector::clear_all(); template void indexed_vector::erase_from_index(unsigned int); template void indexed_vector::set_value(const double&, unsigned int); template void indexed_vector::clear(); template void indexed_vector::clear(); template void indexed_vector::clear_all(); template void indexed_vector::erase_from_index(unsigned int); template void indexed_vector::resize(unsigned int); template void indexed_vector::resize(unsigned int); template void indexed_vector::set_value(const mpq&, unsigned int); template void indexed_vector::set_value(const unsigned&, unsigned int); #ifdef Z3DEBUG template bool indexed_vector::is_OK() const; template bool indexed_vector::is_OK() const; template bool indexed_vector::is_OK() const; template bool indexed_vector >::is_OK() const; #endif template void lp::indexed_vector< lp::mpq>::print(std::basic_ostream > &); template void lp::indexed_vector::print(std::basic_ostream > &); template void lp::indexed_vector >::print(std::ostream&); } // template void lp::print_vector(vector const&, std::ostream&); // template void lp::print_vector(vector const&, std::ostream&); // template void lp::print_vector(vector const&, std::ostream&); // template void lp::print_vector >(vector> const&, std::ostream&); template void lp::indexed_vector::resize(unsigned int); // template void lp::print_vector< lp::mpq>(vector< lp::mpq> const &, std::basic_ostream > &); // template void lp::print_vector >(vector> const&, std::ostream&); template void lp::indexed_vector >::erase_from_index(unsigned int); z3-z3-4.8.7/src/util/lp/indexed_vector.h000066400000000000000000000127521356505360400177700ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #pragma once #include "util/vector.h" #include "util/debug.h" #include #include #include "util/lp/lp_utils.h" #include "util/lp/lp_settings.h" #include namespace lp { template void print_sparse_vector(const vector & t, std::ostream & out); void print_vector_as_doubles(const vector & t, std::ostream & out); template class indexed_vector { public: // m_index points to non-zero elements of m_data vector m_data; vector m_index; indexed_vector(unsigned data_size) { m_data.resize(data_size, numeric_traits::zero()); } indexed_vector& operator=(const indexed_vector& y) { for (unsigned i: m_index) m_data[i] = zero_of_type(); m_index = y.m_index; m_data.resize(y.data_size()); for (unsigned i : m_index) m_data[i] = y[i]; return *this; } bool operator==(const indexed_vector& y) const { std::unordered_set y_index; for (unsigned i : y.m_index) y_index.insert(i); std::unordered_set this_index; for (unsigned i : m_index) this_index.insert(i); for (unsigned i : y.m_index) { if (this_index.find(i) == this_index.end()) return false; } for (unsigned i : m_index) { if (y_index.find(i) == y_index.end()) return false; } return vectors_are_equal(m_data, m_data); } indexed_vector() {} void resize(unsigned data_size); unsigned data_size() const { return m_data.size(); } unsigned size() { return m_index.size(); } void set_value(const T& value, unsigned index); void clear(); void clear_all(); const T& operator[] (unsigned i) const { return m_data[i]; } T& operator[] (unsigned i) { return m_data[i]; } void clean_up() { #if 0==1 for (unsigned k = 0; k < m_index.size(); k++) { unsigned i = m_index[k]; T & v = m_data[i]; if (lp_settings::is_eps_small_general(v, 1e-14)) { v = zero_of_type(); m_index.erase(m_index.begin() + k--); } } #endif vector index_copy; for (unsigned i : m_index) { T & v = m_data[i]; if (!lp_settings::is_eps_small_general(v, 1e-14)) { index_copy.push_back(i); } else if (!numeric_traits::is_zero(v)) { v = zero_of_type(); } } m_index = index_copy; } void erase_from_index(unsigned j); void add_value_at_index_with_drop_tolerance(unsigned j, const T& val_to_add) { T & v = m_data[j]; bool was_zero = is_zero(v); v += val_to_add; if (lp_settings::is_eps_small_general(v, 1e-14)) { v = zero_of_type(); if (!was_zero) { erase_from_index(j); } } else { if (was_zero) m_index.push_back(j); } } void add_value_at_index(unsigned j, const T& val_to_add) { T & v = m_data[j]; bool was_zero = is_zero(v); v += val_to_add; if (is_zero(v)) { if (!was_zero) erase_from_index(j); } else { if (was_zero) m_index.push_back(j); } } void restore_index_and_clean_from_data() { m_index.resize(0); for (unsigned i = 0; i < m_data.size(); i++) { T & v = m_data[i]; if (lp_settings::is_eps_small_general(v, 1e-14)) { v = zero_of_type(); } else { m_index.push_back(i); } } } struct ival { unsigned m_var; const T & m_coeff; ival(unsigned var, const T & val) : m_var(var), m_coeff(val) { } unsigned var() const { return m_var;} const T & coeff() const { return m_coeff; } bool dead() const { return false; } }; struct const_iterator { // fields const unsigned *m_i; const indexed_vector& m_v; //typedefs typedef const_iterator self_type; typedef ival value_type; typedef const ival reference; // typedef const column_cell* pointer; typedef int difference_type; typedef std::forward_iterator_tag iterator_category; reference operator*() const { return ival(*m_i, m_v[*m_i]); } self_type operator++() { self_type i = *this; m_i++; return i; } self_type operator++(int) { m_i++; return *this; } const_iterator(const unsigned* it, const indexed_vector& v) : m_i(it), m_v(v) {} bool operator==(const self_type &other) const { return m_i == other.m_i; } bool operator!=(const self_type &other) const { return !(*this == other); } }; const_iterator begin() const { return const_iterator(m_index.begin(), *this); } const_iterator end() const { return const_iterator(m_index.end(), *this); } #ifdef Z3DEBUG bool is_OK() const; #endif void print(std::ostream & out); }; } z3-z3-4.8.7/src/util/lp/indexed_vector_def.h000066400000000000000000000051751356505360400206070ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #include "util/vector.h" #include "util/lp/indexed_vector.h" #include "util/lp/lp_settings.h" namespace lp { template void print_sparse_vector(const vector & t, std::ostream & out) { for (unsigned i = 0; i < t.size(); i++) { if (is_zero(t[i]))continue; out << "[" << i << "] = " << t[i] << ", "; } out << std::endl; } void print_vector_as_doubles(const vector & t, std::ostream & out) { for (unsigned i = 0; i < t.size(); i++) out << t[i].get_double() << std::setprecision(3) << " "; out << std::endl; } template void indexed_vector::resize(unsigned data_size) { clear(); m_data.resize(data_size, numeric_traits::zero()); lp_assert(is_OK()); } template void indexed_vector::set_value(const T& value, unsigned index) { m_data[index] = value; lp_assert(std::find(m_index.begin(), m_index.end(), index) == m_index.end()); m_index.push_back(index); } template void indexed_vector::clear() { for (unsigned i : m_index) m_data[i] = numeric_traits::zero(); m_index.resize(0); } template void indexed_vector::clear_all() { unsigned i = m_data.size(); while (i--) m_data[i] = numeric_traits::zero(); m_index.resize(0); } template void indexed_vector::erase_from_index(unsigned j) { auto it = std::find(m_index.begin(), m_index.end(), j); if (it != m_index.end()) m_index.erase(it); } #ifdef Z3DEBUG template bool indexed_vector::is_OK() const { return true; const double drop_eps = 1e-14; for (unsigned i = 0; i < m_data.size(); i++) { if (!is_zero(m_data[i]) && lp_settings::is_eps_small_general(m_data[i], drop_eps)) { return false; } if (lp_settings::is_eps_small_general(m_data[i], drop_eps) != (std::find(m_index.begin(), m_index.end(), i) == m_index.end())) { return false; } } std::unordered_set s; for (unsigned i : m_index) { //no duplicates!!! if (s.find(i) != s.end()) return false; s.insert(i); if (i >= m_data.size()) return false; } return true; } #endif template void indexed_vector::print(std::ostream & out) { out << "m_index " << std::endl; for (unsigned i = 0; i < m_index.size(); i++) { out << m_index[i] << " "; } out << std::endl; print_vector(m_data, out); } } z3-z3-4.8.7/src/util/lp/indexer_of_constraints.h000066400000000000000000000015071356505360400215330ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Nikolaj Bjorner (nbjorner) Lev Nachmanson (levnach) Revision History: --*/ #pragma once #include "util/lp/binary_heap_priority_queue.h" namespace lp { class indexer_of_constraints { binary_heap_priority_queue m_queue_of_released_indices; unsigned m_max; public: indexer_of_constraints() :m_max(0) {} unsigned get_new_index() { unsigned ret; if (m_queue_of_released_indices.is_empty()) { ret = m_max++; } else { ret = m_queue_of_released_indices.dequeue(); } return ret; }; void release_index(unsigned i) { m_queue_of_released_indices.enqueue(i, i); }; unsigned max() const { return m_max; } }; } z3-z3-4.8.7/src/util/lp/int_set.h000066400000000000000000000033511356505360400164260ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #pragma once #include "util/vector.h" #include "util/lp/indexed_vector.h" #include namespace lp { // serves at a set of non-negative integers smaller than the set size class int_set { vector m_data; public: vector m_index; int_set(unsigned size): m_data(size, -1) {} int_set() {} bool contains(unsigned j) const { if (j >= m_data.size()) return false; return m_data[j] >= 0; } void insert(unsigned j) { lp_assert(j < m_data.size()); if (contains(j)) return; m_data[j] = m_index.size(); m_index.push_back(j); } void erase(unsigned j) { if (!contains(j)) return; unsigned pos_j = m_data[j]; unsigned last_pos = m_index.size() - 1; int last_j = m_index[last_pos]; if (last_pos != pos_j) { // move last to j spot m_data[last_j] = pos_j; m_index[pos_j] = last_j; } m_index.pop_back(); m_data[j] = -1; } void resize(unsigned size) { m_data.resize(size, -1); } void increase_size_by_one() { resize(m_data.size() + 1); } unsigned data_size() const { return m_data.size(); } unsigned size() const { return m_index.size();} bool is_empty() const { return size() == 0; } void clear() { for (unsigned j : m_index) m_data[j] = -1; m_index.resize(0); } void print(std::ostream & out ) const { for (unsigned j : m_index) { out << j << " "; } out << std::endl; } }; } z3-z3-4.8.7/src/util/lp/int_solver.cpp000066400000000000000000000771111356505360400175050ustar00rootroot00000000000000/* Copyright (c) 2017 Microsoft Corporation Author: Lev Nachmanson */ #include "util/lp/int_solver.h" #include "util/lp/lar_solver.h" #include "util/lp/lp_utils.h" #include #include "util/lp/monomial.h" #include "util/lp/gomory.h" namespace lp { void int_solver::trace_inf_rows() const { TRACE("arith_int_rows", unsigned num = m_lar_solver->A_r().column_count(); for (unsigned v = 0; v < num; v++) { if (column_is_int(v) && !get_value(v).is_int()) { display_column(tout, v); } } num = 0; for (unsigned i = 0; i < m_lar_solver->A_r().row_count(); i++) { unsigned j = m_lar_solver->m_mpq_lar_core_solver.m_r_basis[i]; if (column_is_int_inf(j)) { num++; m_lar_solver->print_row(m_lar_solver->A_r().m_rows[i], tout); tout << "\n"; } } tout << "num of int infeasible: " << num << "\n"; ); } bool int_solver::has_inf_int() const { return m_lar_solver->has_inf_int(); } int int_solver::find_inf_int_base_column() { unsigned inf_int_count = 0; int j = find_inf_int_boxed_base_column_with_smallest_range(inf_int_count); if (j != -1) return j; if (inf_int_count == 0) return -1; unsigned k = random() % inf_int_count; return get_kth_inf_int(k); } int int_solver::get_kth_inf_int(unsigned k) const { for (unsigned j : m_lar_solver->r_basis()) if (column_is_int_inf(j) && k-- == 0) return j; lp_assert(false); return -1; } int int_solver::find_inf_int_nbasis_column() const { for (unsigned j : m_lar_solver->r_nbasis()) if (!column_is_int_inf(j)) return j; return -1; } int int_solver::find_inf_int_boxed_base_column_with_smallest_range(unsigned & inf_int_count) { inf_int_count = 0; int result = -1; mpq range; mpq new_range; mpq small_range_thresold(1024); unsigned n = 0; lar_core_solver & lcs = m_lar_solver->m_mpq_lar_core_solver; for (unsigned j : m_lar_solver->r_basis()) { if (!column_is_int_inf(j)) continue; inf_int_count++; if (!is_boxed(j)) continue; lp_assert(!is_fixed(j)); new_range = lcs.m_r_upper_bounds()[j].x - lcs.m_r_lower_bounds()[j].x; if (new_range > small_range_thresold) continue; if (result == -1 || new_range < range) { result = j; range = new_range; n = 1; } else if (new_range == range && settings().random_next() % (++n) == 0) { lp_assert(n > 1); result = j; } } return result; } bool int_solver::is_gomory_cut_target(const row_strip& row) { // All non base variables must be at their bounds and assigned to rationals (that is, infinitesimals are not allowed). unsigned j; for (const auto & p : row) { j = p.var(); if (!is_base(j) && (!at_bound(j) || !is_zero(get_value(j).y))) { TRACE("gomory_cut", tout << "row is not gomory cut target:\n"; display_column(tout, j); tout << "infinitesimal: " << !is_zero(get_value(j).y) << "\n";); return false; } } return true; } constraint_index int_solver::column_upper_bound_constraint(unsigned j) const { return m_lar_solver->get_column_upper_bound_witness(j); } constraint_index int_solver::column_lower_bound_constraint(unsigned j) const { return m_lar_solver->get_column_lower_bound_witness(j); } bool int_solver::current_solution_is_inf_on_cut() const { const auto & x = m_lar_solver->m_mpq_lar_core_solver.m_r_x; impq v = m_t.apply(x); mpq sign = m_upper ? one_of_type() : -one_of_type(); CTRACE("current_solution_is_inf_on_cut", v * sign <= m_k * sign, tout << "m_upper = " << m_upper << std::endl; tout << "v = " << v << ", k = " << m_k << std::endl; ); return v * sign > m_k * sign; } lia_move int_solver::mk_gomory_cut( unsigned inf_col, const row_strip & row) { lp_assert(column_is_int_inf(inf_col)); gomory gc(m_t, m_k, m_ex, inf_col, row, *this); return gc.create_cut(); } lia_move int_solver::proceed_with_gomory_cut(unsigned j) { const row_strip& row = m_lar_solver->get_row(row_of_basic_column(j)); if (!is_gomory_cut_target(row)) return create_branch_on_column(j); m_upper = true; return mk_gomory_cut(j, row); } unsigned int_solver::row_of_basic_column(unsigned j) const { return m_lar_solver->m_mpq_lar_core_solver.m_r_heading[j]; } // template // void int_solver::fill_cut_solver_for_constraint(constraint_index ci, cut_solver & cs) { // const lar_base_constraint* c = m_lar_solver->constraints()[ci]; // vector> coeffs; // T rs; // get_int_coeffs_from_constraint(c, coeffs, rs); // vector explanation; // explanation.push_back(ci); // cs.add_ineq(coeffs, -rs, explanation); // } typedef monomial mono; // this will allow to enable and disable tracking of the pivot rows struct check_return_helper { lar_solver * m_lar_solver; const lia_move & m_r; bool m_track_pivoted_rows; check_return_helper(lar_solver* ls, const lia_move& r) : m_lar_solver(ls), m_r(r), m_track_pivoted_rows(ls->get_track_pivoted_rows()) { TRACE("pivoted_rows", tout << "pivoted rows = " << ls->m_mpq_lar_core_solver.m_r_solver.m_pivoted_rows->size() << std::endl;); m_lar_solver->set_track_pivoted_rows(false); } ~check_return_helper() { TRACE("pivoted_rows", tout << "pivoted rows = " << m_lar_solver->m_mpq_lar_core_solver.m_r_solver.m_pivoted_rows->size() << std::endl;); m_lar_solver->set_track_pivoted_rows(m_track_pivoted_rows); } }; impq int_solver::get_cube_delta_for_term(const lar_term& t) const { if (t.size() == 2) { bool seen_minus = false; bool seen_plus = false; for(const auto & p : t) { if (!column_is_int(p.var())) goto usual_delta; const mpq & c = p.coeff(); if (c == one_of_type()) { seen_plus = true; } else if (c == -one_of_type()) { seen_minus = true; } else { goto usual_delta; } } if (seen_minus && seen_plus) return zero_of_type(); return impq(0, 1); } usual_delta: mpq delta = zero_of_type(); for (const auto & p : t) if (column_is_int(p.var())) delta += abs(p.coeff()); delta *= mpq(1, 2); return impq(delta); } bool int_solver::tighten_term_for_cube(unsigned i) { unsigned ti = i + m_lar_solver->terms_start_index(); if (!m_lar_solver->term_is_used_as_row(ti)) return true; const lar_term* t = m_lar_solver->terms()[i]; impq delta = get_cube_delta_for_term(*t); TRACE("cube", m_lar_solver->print_term_as_indices(*t, tout); tout << ", delta = " << delta;); if (is_zero(delta)) return true; return m_lar_solver->tighten_term_bounds_by_delta(i, delta); } bool int_solver::tighten_terms_for_cube() { for (unsigned i = 0; i < m_lar_solver->terms().size(); i++) if (!tighten_term_for_cube(i)) { TRACE("cube", tout << "cannot tighten";); return false; } return true; } lia_move int_solver::find_cube() { if (m_number_of_calls % settings().m_int_find_cube_period != 0) return lia_move::undef; settings().st().m_cube_calls++; TRACE("cube", for (unsigned j = 0; j < m_lar_solver->A_r().column_count(); j++) display_column(tout, j); m_lar_solver->print_terms(tout); ); lar_solver::scoped_push _sp(*m_lar_solver); if (!tighten_terms_for_cube()) { return lia_move::undef; } lp_status st = m_lar_solver->find_feasible_solution(); if (st != lp_status::FEASIBLE && st != lp_status::OPTIMAL) { TRACE("cube", tout << "cannot find a feasiblie solution";); _sp.pop(); m_lar_solver->move_non_basic_columns_to_bounds(); find_feasible_solution(); // it can happen that we found an integer solution here return !m_lar_solver->r_basis_has_inf_int()? lia_move::sat: lia_move::undef; } _sp.pop(); m_lar_solver->round_to_integer_solution(); settings().st().m_cube_success++; return lia_move::sat; } void int_solver::find_feasible_solution() { m_lar_solver->find_feasible_solution(); lp_assert(lp_status::OPTIMAL == m_lar_solver->get_status() || lp_status::FEASIBLE == m_lar_solver->get_status()); } lia_move int_solver::run_gcd_test() { if (settings().m_int_run_gcd_test) { settings().st().m_gcd_calls++; TRACE("int_solver", tout << "gcd-test " << settings().st().m_gcd_calls << "\n";); if (!gcd_test()) { settings().st().m_gcd_conflicts++; return lia_move::conflict; } } return lia_move::undef; } lia_move int_solver::gomory_cut() { TRACE("int_solver", tout << "gomory " << m_number_of_calls << "\n";); if ((m_number_of_calls) % settings().m_int_gomory_cut_period != 0) return lia_move::undef; if (m_lar_solver->move_non_basic_columns_to_bounds()) { #if Z3DEBUG lp_status st = #endif m_lar_solver->find_feasible_solution(); #if Z3DEBUG lp_assert(st == lp_status::FEASIBLE || st == lp_status::OPTIMAL); #endif } int j = find_inf_int_base_column(); if (j == -1) { j = find_inf_int_nbasis_column(); return j == -1? lia_move::sat : create_branch_on_column(j); } return proceed_with_gomory_cut(j); } void int_solver::try_add_term_to_A_for_hnf(unsigned i) { mpq rs; const lar_term* t = m_lar_solver->terms()[i]; constraint_index ci; bool upper_bound; if (!hnf_cutter_is_full() && m_lar_solver->get_equality_and_right_side_for_term_on_current_x(i, rs, ci, upper_bound)) { m_hnf_cutter.add_term(t, rs, ci, upper_bound); } } bool int_solver::hnf_cutter_is_full() const { return m_hnf_cutter.terms_count() >= settings().limit_on_rows_for_hnf_cutter || m_hnf_cutter.vars().size() >= settings().limit_on_columns_for_hnf_cutter; } lp_settings& int_solver::settings() { return m_lar_solver->settings(); } const lp_settings& int_solver::settings() const { return m_lar_solver->settings(); } bool int_solver::hnf_has_var_with_non_integral_value() const { for (unsigned j : m_hnf_cutter.vars()) if (get_value(j).is_int() == false) return true; return false; } bool int_solver::init_terms_for_hnf_cut() { m_hnf_cutter.clear(); for (unsigned i = 0; i < m_lar_solver->terms().size() && !hnf_cutter_is_full(); i++) { try_add_term_to_A_for_hnf(i); } return hnf_has_var_with_non_integral_value(); } lia_move int_solver::make_hnf_cut() { if (!init_terms_for_hnf_cut()) { return lia_move::undef; } settings().st().m_hnf_cutter_calls++; TRACE("hnf_cut", tout << "settings().st().m_hnf_cutter_calls = " << settings().st().m_hnf_cutter_calls << "\n"; for (unsigned i : m_hnf_cutter.constraints_for_explanation()) { m_lar_solver->print_constraint(i, tout); } m_lar_solver->print_constraints(tout); ); #ifdef Z3DEBUG vector x0 = m_hnf_cutter.transform_to_local_columns(m_lar_solver->m_mpq_lar_core_solver.m_r_x); #else vector x0; #endif lia_move r = m_hnf_cutter.create_cut(m_t, m_k, m_ex, m_upper, x0); if (r == lia_move::cut) { TRACE("hnf_cut", m_lar_solver->print_term(m_t, tout << "cut:"); tout << " <= " << m_k << std::endl; for (unsigned i : m_hnf_cutter.constraints_for_explanation()) { m_lar_solver->print_constraint(i, tout); } ); lp_assert(current_solution_is_inf_on_cut()); settings().st().m_hnf_cuts++; m_ex.clear(); for (unsigned i : m_hnf_cutter.constraints_for_explanation()) { m_ex.push_justification(i); } } return r; } lia_move int_solver::hnf_cut() { if (!settings().m_enable_hnf) { return lia_move::undef; } if ((m_number_of_calls) % settings().hnf_cut_period() == 0) { return make_hnf_cut(); } return lia_move::undef; } lia_move int_solver::check() { if (!has_inf_int()) return lia_move::sat; m_t.clear(); m_k.reset(); m_ex.clear(); m_upper = false; lia_move r = run_gcd_test(); if (r != lia_move::undef) return r; check_return_helper pc(m_lar_solver, r); if(settings().m_int_pivot_fixed_vars_from_basis) m_lar_solver->pivot_fixed_vars_from_basis(); r = patch_nbasic_columns(); if (r != lia_move::undef) return r; ++m_number_of_calls; r = find_cube(); if (r != lia_move::undef) return r; r = hnf_cut(); if (r != lia_move::undef) return r; r = gomory_cut(); return (r == lia_move::undef)? branch_or_sat() : r; } lia_move int_solver::branch_or_sat() { int j = find_any_inf_int_column_basis_first(); return j == -1? lia_move::sat : create_branch_on_column(j); } int int_solver::find_any_inf_int_column_basis_first() { int j = find_inf_int_base_column(); if (j != -1) return j; return find_inf_int_nbasis_column(); } void int_solver::set_value_for_nbasic_column_ignore_old_values(unsigned j, const impq & new_val) { lp_assert(!is_base(j)); auto & x = m_lar_solver->m_mpq_lar_core_solver.m_r_x[j]; auto delta = new_val - x; x = new_val; m_lar_solver->change_basic_columns_dependend_on_a_given_nb_column(j, delta); } void int_solver::patch_nbasic_column(unsigned j, bool patch_only_int_vals) { auto & lcs = m_lar_solver->m_mpq_lar_core_solver; impq & val = lcs.m_r_x[j]; bool val_is_int = val.is_int(); if (patch_only_int_vals && !val_is_int) return; bool inf_l, inf_u; impq l, u; mpq m; if (!get_freedom_interval_for_column(j, inf_l, l, inf_u, u, m)) { return; } bool m_is_one = m.is_one(); if (m.is_one() && val_is_int) return; // check whether value of j is already a multiple of m. if (val_is_int && (val.x / m).is_int()) return; TRACE("patch_int", tout << "TARGET j" << j << " -> ["; if (inf_l) tout << "-oo"; else tout << l; tout << ", "; if (inf_u) tout << "oo"; else tout << u; tout << "]"; tout << ", m: " << m << ", val: " << val << ", is_int: " << m_lar_solver->column_is_int(j) << "\n";); if (!inf_l) { l = m_is_one ? ceil(l) : m * ceil(l / m); if (inf_u || l <= u) { TRACE("patch_int", tout << "patching with l: " << l << '\n';); m_lar_solver->set_value_for_nbasic_column(j, l); } else { TRACE("patch_int", tout << "not patching " << l << "\n";); } } else if (!inf_u) { u = m_is_one ? floor(u) : m * floor(u / m); m_lar_solver->set_value_for_nbasic_column(j, u); TRACE("patch_int", tout << "patching with u: " << u << '\n';); } else { m_lar_solver->set_value_for_nbasic_column(j, impq(0)); TRACE("patch_int", tout << "patching with 0\n";); } } lia_move int_solver::patch_nbasic_columns() { settings().st().m_patches++; lp_assert(is_feasible()); for (unsigned j : m_lar_solver->m_mpq_lar_core_solver.m_r_nbasis) { patch_nbasic_column(j, settings().m_int_patch_only_integer_values); } lp_assert(is_feasible()); if (!has_inf_int()) { settings().st().m_patches_success++; return lia_move::sat; } return lia_move::undef; } mpq get_denominators_lcm(const row_strip & row) { mpq r(1); for (auto & c : row) { r = lcm(r, denominator(c.coeff())); } return r; } bool int_solver::gcd_test_for_row(static_matrix> & A, unsigned i) { mpq lcm_den = get_denominators_lcm(A.m_rows[i]); mpq consts(0); mpq gcds(0); mpq least_coeff(0); bool least_coeff_is_bounded = false; unsigned j; for (auto &c : A.m_rows[i]) { j = c.var(); const mpq& a = c.coeff(); if (m_lar_solver->column_is_fixed(j)) { mpq aux = lcm_den * a; consts += aux * m_lar_solver->column_lower_bound(j).x; } else if (m_lar_solver->column_is_real(j)) { return true; } else if (gcds.is_zero()) { gcds = abs(lcm_den * a); least_coeff = gcds; least_coeff_is_bounded = m_lar_solver->column_is_bounded(j); } else { mpq aux = abs(lcm_den * a); gcds = gcd(gcds, aux); if (aux < least_coeff) { least_coeff = aux; least_coeff_is_bounded = m_lar_solver->column_is_bounded(j); } else if (least_coeff_is_bounded && aux == least_coeff) { least_coeff_is_bounded = m_lar_solver->column_is_bounded(j); } } SASSERT(gcds.is_int()); SASSERT(least_coeff.is_int()); TRACE("gcd_test_bug", tout << "coeff: " << a << ", gcds: " << gcds << " least_coeff: " << least_coeff << " consts: " << consts << "\n";); } if (gcds.is_zero()) { // All variables are fixed. // This theory guarantees that the assignment satisfies each row, and // fixed integer variables are assigned to integer values. return true; } if (!(consts / gcds).is_int()) { TRACE("gcd_test", tout << "row failed the GCD test:\n"; display_row_info(tout, i);); fill_explanation_from_fixed_columns(A.m_rows[i]); return false; } if (least_coeff.is_one() && !least_coeff_is_bounded) { SASSERT(gcds.is_one()); return true; } if (least_coeff_is_bounded) { return ext_gcd_test(A.m_rows[i], least_coeff, lcm_den, consts); } return true; } void int_solver::add_to_explanation_from_fixed_or_boxed_column(unsigned j) { constraint_index lc, uc; m_lar_solver->get_bound_constraint_witnesses_for_column(j, lc, uc); m_ex.m_explanation.push_back(std::make_pair(mpq(1), lc)); m_ex.m_explanation.push_back(std::make_pair(mpq(1), uc)); } void int_solver::fill_explanation_from_fixed_columns(const row_strip & row) { for (const auto & c : row) { if (!m_lar_solver->column_is_fixed(c.var())) continue; add_to_explanation_from_fixed_or_boxed_column(c.var()); } } bool int_solver::gcd_test() { auto & A = m_lar_solver->A_r(); // getting the matrix for (unsigned i = 0; i < A.row_count(); i++) if (!gcd_test_for_row(A, i)) return false; return true; } bool int_solver::ext_gcd_test(const row_strip & row, mpq const & least_coeff, mpq const & lcm_den, mpq const & consts) { mpq gcds(0); mpq l(consts); mpq u(consts); mpq a; unsigned j; for (const auto & c : row) { j = c.var(); const mpq & a = c.coeff(); if (m_lar_solver->column_is_fixed(j)) continue; SASSERT(!m_lar_solver->column_is_real(j)); mpq ncoeff = lcm_den * a; SASSERT(ncoeff.is_int()); mpq abs_ncoeff = abs(ncoeff); if (abs_ncoeff == least_coeff) { SASSERT(m_lar_solver->column_is_bounded(j)); if (ncoeff.is_pos()) { // l += ncoeff * m_lar_solver->column_lower_bound(j).x; l.addmul(ncoeff, m_lar_solver->column_lower_bound(j).x); // u += ncoeff * m_lar_solver->column_upper_bound(j).x; u.addmul(ncoeff, m_lar_solver->column_upper_bound(j).x); } else { // l += ncoeff * upper_bound(j).get_rational(); l.addmul(ncoeff, m_lar_solver->column_upper_bound(j).x); // u += ncoeff * lower_bound(j).get_rational(); u.addmul(ncoeff, m_lar_solver->column_lower_bound(j).x); } add_to_explanation_from_fixed_or_boxed_column(j); } else if (gcds.is_zero()) { gcds = abs_ncoeff; } else { gcds = gcd(gcds, abs_ncoeff); } SASSERT(gcds.is_int()); } if (gcds.is_zero()) { return true; } mpq l1 = ceil(l/gcds); mpq u1 = floor(u/gcds); if (u1 < l1) { fill_explanation_from_fixed_columns(row); return false; } return true; } /* linear_combination_iterator * int_solver::get_column_iterator(unsigned j) { if (m_lar_solver->use_tableau()) return new iterator_on_column(m_lar_solver->A_r().m_columns[j], m_lar_solver->A_r()); return new iterator_on_indexed_vector(m_lar_solver->get_column_in_lu_mode(j)); } */ int_solver::int_solver(lar_solver* lar_slv) : m_lar_solver(lar_slv), m_number_of_calls(0), m_hnf_cutter(settings()) { m_lar_solver->set_int_solver(this); } bool int_solver::has_low(unsigned j) const { switch (m_lar_solver->m_mpq_lar_core_solver.m_column_types()[j]) { case column_type::fixed: case column_type::boxed: case column_type::lower_bound: return true; default: return false; } } bool int_solver::has_upper(unsigned j) const { switch (m_lar_solver->m_mpq_lar_core_solver.m_column_types()[j]) { case column_type::fixed: case column_type::boxed: case column_type::upper_bound: return true; default: return false; } } void set_lower(impq & l, bool & inf_l, impq const & v ) { if (inf_l || v > l) { l = v; inf_l = false; } } void set_upper(impq & u, bool & inf_u, impq const & v) { if (inf_u || v < u) { u = v; inf_u = false; } } bool int_solver::get_freedom_interval_for_column(unsigned j, bool & inf_l, impq & l, bool & inf_u, impq & u, mpq & m) { auto & lcs = m_lar_solver->m_mpq_lar_core_solver; if (lcs.m_r_heading[j] >= 0) // the basic var return false; impq const & xj = get_value(j); inf_l = true; inf_u = true; l = u = zero_of_type(); m = mpq(1); if (has_low(j)) { set_lower(l, inf_l, lower_bound(j)); } if (has_upper(j)) { set_upper(u, inf_u, upper_bound(j)); } mpq a; // the coefficient in the column unsigned row_index; lp_assert(settings().use_tableau()); const auto & A = m_lar_solver->A_r(); for (const auto &c : A.column(j)) { row_index = c.var(); const mpq & a = c.coeff(); unsigned i = lcs.m_r_basis[row_index]; impq const & xi = get_value(i); if (column_is_int(i) && column_is_int(j) && !a.is_int()) m = lcm(m, denominator(a)); if (a.is_neg()) { if (has_low(i)) set_lower(l, inf_l, xj + (xi - lcs.m_r_lower_bounds()[i]) / a); if (has_upper(i)) set_upper(u, inf_u, xj + (xi - lcs.m_r_upper_bounds()[i]) / a); } else { if (has_upper(i)) set_lower(l, inf_l, xj + (xi - lcs.m_r_upper_bounds()[i]) / a); if (has_low(i)) set_upper(u, inf_u, xj + (xi - lcs.m_r_lower_bounds()[i]) / a); } if (!inf_l && !inf_u && l >= u) break; } TRACE("freedom_interval", tout << "freedom variable for:\n"; tout << m_lar_solver->get_column_name(j); tout << "["; if (inf_l) tout << "-oo"; else tout << l; tout << "; "; if (inf_u) tout << "oo"; else tout << u; tout << "]\n"; tout << "val = " << get_value(j) << "\n"; tout << "return " << (inf_l || inf_u || l <= u); ); return (inf_l || inf_u || l <= u); } bool int_solver::column_is_int(unsigned j) const { return m_lar_solver->column_is_int(j); } bool int_solver::is_real(unsigned j) const { return !column_is_int(j); } bool int_solver::value_is_int(unsigned j) const { return m_lar_solver->column_value_is_int(j); } bool int_solver::is_feasible() const { auto & lcs = m_lar_solver->m_mpq_lar_core_solver; lp_assert( lcs.m_r_solver.calc_current_x_is_feasible_include_non_basis() == lcs.m_r_solver.current_x_is_feasible()); return lcs.m_r_solver.current_x_is_feasible(); } const impq & int_solver::get_value(unsigned j) const { return m_lar_solver->m_mpq_lar_core_solver.m_r_x[j]; } void int_solver::display_column(std::ostream & out, unsigned j) const { m_lar_solver->m_mpq_lar_core_solver.m_r_solver.print_column_info(j, out); } bool int_solver::column_is_int_inf(unsigned j) const { return column_is_int(j) && (!value_is_int(j)); } bool int_solver::is_base(unsigned j) const { return m_lar_solver->m_mpq_lar_core_solver.m_r_heading[j] >= 0; } bool int_solver::is_boxed(unsigned j) const { return m_lar_solver->m_mpq_lar_core_solver.m_column_types[j] == column_type::boxed; } bool int_solver::is_fixed(unsigned j) const { return m_lar_solver->m_mpq_lar_core_solver.m_column_types[j] == column_type::fixed; } bool int_solver::is_free(unsigned j) const { return m_lar_solver->m_mpq_lar_core_solver.m_column_types[j] == column_type::free_column; } bool int_solver::at_bound(unsigned j) const { auto & mpq_solver = m_lar_solver->m_mpq_lar_core_solver.m_r_solver; switch (mpq_solver.m_column_types[j] ) { case column_type::fixed: case column_type::boxed: return mpq_solver.m_lower_bounds[j] == get_value(j) || mpq_solver.m_upper_bounds[j] == get_value(j); case column_type::lower_bound: return mpq_solver.m_lower_bounds[j] == get_value(j); case column_type::upper_bound: return mpq_solver.m_upper_bounds[j] == get_value(j); default: return false; } } bool int_solver::at_lower(unsigned j) const { auto & mpq_solver = m_lar_solver->m_mpq_lar_core_solver.m_r_solver; switch (mpq_solver.m_column_types[j] ) { case column_type::fixed: case column_type::boxed: case column_type::lower_bound: return mpq_solver.m_lower_bounds[j] == get_value(j); default: return false; } } bool int_solver::at_upper(unsigned j) const { auto & mpq_solver = m_lar_solver->m_mpq_lar_core_solver.m_r_solver; switch (mpq_solver.m_column_types[j] ) { case column_type::fixed: case column_type::boxed: case column_type::upper_bound: return mpq_solver.m_upper_bounds[j] == get_value(j); default: return false; } } void int_solver::display_row_info(std::ostream & out, unsigned row_index) const { auto & rslv = m_lar_solver->m_mpq_lar_core_solver.m_r_solver; for (const auto &c: rslv.m_A.m_rows[row_index]) { if (numeric_traits::is_pos(c.coeff())) out << "+"; out << c.coeff() << rslv.column_name(c.var()) << " "; } for (const auto& c: rslv.m_A.m_rows[row_index]) { rslv.print_column_bound_info(c.var(), out); } rslv.print_column_bound_info(rslv.m_basis[row_index], out); } unsigned int_solver::random() { return m_lar_solver->get_core_solver().settings().random_next(); } bool int_solver::shift_var(unsigned j, unsigned range) { if (is_fixed(j) || is_base(j)) return false; bool inf_l, inf_u; impq l, u; mpq m; get_freedom_interval_for_column(j, inf_l, l, inf_u, u, m); if (inf_l && inf_u) { impq new_val = impq(random() % (range + 1)); set_value_for_nbasic_column_ignore_old_values(j, new_val); return true; } if (column_is_int(j)) { if (!inf_l) { l = ceil(l); if (!m.is_one()) l = m*ceil(l/m); } if (!inf_u) { u = floor(u); if (!m.is_one()) u = m*floor(u/m); } } if (!inf_l && !inf_u && l >= u) return false; if (inf_u) { SASSERT(!inf_l); impq delta = impq(random() % (range + 1)); impq new_val = l + m*delta; set_value_for_nbasic_column_ignore_old_values(j, new_val); return true; } if (inf_l) { SASSERT(!inf_u); impq delta = impq(random() % (range + 1)); impq new_val = u - m*delta; set_value_for_nbasic_column_ignore_old_values(j, new_val); return true; } if (!column_is_int(j)) { SASSERT(!inf_l && !inf_u); mpq delta = mpq(random() % (range + 1)); impq new_val = l + ((delta * (u - l)) / mpq(range)); set_value_for_nbasic_column_ignore_old_values(j, new_val); return true; } else { mpq r = (u.x - l.x) / m; if (r < mpq(range)) range = static_cast(r.get_uint64()); impq new_val = l + m * (impq(random() % (range + 1))); set_value_for_nbasic_column_ignore_old_values(j, new_val); return true; } } bool int_solver::non_basic_columns_are_at_bounds() const { auto & lcs = m_lar_solver->m_mpq_lar_core_solver; for (unsigned j :lcs.m_r_nbasis) { auto & val = lcs.m_r_x[j]; switch (lcs.m_column_types()[j]) { case column_type::boxed: if (val != lcs.m_r_lower_bounds()[j] && val != lcs.m_r_upper_bounds()[j]) return false; break; case column_type::lower_bound: if (val != lcs.m_r_lower_bounds()[j]) return false; break; case column_type::upper_bound: if (val != lcs.m_r_upper_bounds()[j]) return false; break; default: if (column_is_int(j) && !val.is_int()) { return false; } } } return true; } const impq& int_solver::lower_bound(unsigned j) const { return m_lar_solver->column_lower_bound(j); } lia_move int_solver::create_branch_on_column(int j) { TRACE("check_main_int", tout << "branching" << std::endl;); lp_assert(m_t.is_empty()); lp_assert(j != -1); m_t.add_monomial(mpq(1), m_lar_solver->adjust_column_index_to_term_index(j)); if (is_free(j)) { m_upper = true; m_k = mpq(0); } else { m_upper = left_branch_is_more_narrow_than_right(j); m_k = m_upper? floor(get_value(j)) : ceil(get_value(j)); } TRACE("int_solver", tout << "branching v" << j << " = " << get_value(j) << "\n"; display_column(tout, j); tout << "k = " << m_k << std::endl; ); return lia_move::branch; } bool int_solver::left_branch_is_more_narrow_than_right(unsigned j) { switch (m_lar_solver->m_mpq_lar_core_solver.m_r_solver.m_column_types[j] ) { case column_type::fixed: return false; case column_type::boxed: { auto k = floor(get_value(j)); return k - lower_bound(j).x < upper_bound(j).x - (k + mpq(1)); } case column_type::lower_bound: return true; case column_type::upper_bound: return false; default: return false; } } const impq& int_solver::upper_bound(unsigned j) const { return m_lar_solver->column_upper_bound(j); } bool int_solver::is_term(unsigned j) const { return m_lar_solver->column_corresponds_to_term(j); } unsigned int_solver::column_count() const { return m_lar_solver->column_count(); } } z3-z3-4.8.7/src/util/lp/int_solver.h000066400000000000000000000121661356505360400171510ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Nikolaj Bjorner (nbjorner) Lev Nachmanson (levnach) Revision History: --*/ #pragma once #include "util/lp/lp_settings.h" #include "util/lp/static_matrix.h" #include "util/lp/int_set.h" #include "util/lp/lar_term.h" #include "util/lp/lar_constraints.h" #include "util/lp/hnf_cutter.h" #include "util/lp/lia_move.h" #include "util/lp/explanation.h" namespace lp { class lar_solver; template struct lp_constraint; class int_solver { public: // fields lar_solver *m_lar_solver; unsigned m_number_of_calls; lar_term m_t; // the term to return in the cut mpq m_k; // the right side of the cut explanation m_ex; // the conflict explanation bool m_upper; // we have a cut m_t*x <= k if m_upper is true nad m_t*x >= k otherwise hnf_cutter m_hnf_cutter; // methods int_solver(lar_solver* lp); // main function to check that the solution provided by lar_solver is valid for integral values, // or provide a way of how it can be adjusted. lia_move check(); lar_term const& get_term() const { return m_t; } mpq const& get_offset() const { return m_k; } explanation const& get_explanation() const { return m_ex; } bool is_upper() const { return m_upper; } bool is_base(unsigned j) const; bool is_real(unsigned j) const; const impq & lower_bound(unsigned j) const; const impq & upper_bound(unsigned j) const; bool column_is_int(unsigned j) const; const impq & get_value(unsigned j) const; bool at_lower(unsigned j) const; bool at_upper(unsigned j) const; private: // how to tighten bounds for integer variables. bool gcd_test_for_row(static_matrix> & A, unsigned i); // gcd test // 5*x + 3*y + 6*z = 5 // suppose x is fixed at 2. // so we have 10 + 3(y + 2z) = 5 // 5 = -3(y + 2z) // this is unsolvable because 5/3 is not an integer. // so we create a lemma that rules out this condition. // bool gcd_test(); // returns false in case of failure. Creates a theory lemma in case of failure. bool branch(const lp_constraint & new_inequality); bool ext_gcd_test(const row_strip& row, mpq const & least_coeff, mpq const & lcm_den, mpq const & consts); void fill_explanation_from_fixed_columns(const row_strip & row); void add_to_explanation_from_fixed_or_boxed_column(unsigned j); lia_move patch_nbasic_columns(); bool get_freedom_interval_for_column(unsigned j, bool & inf_l, impq & l, bool & inf_u, impq & u, mpq & m); private: bool is_boxed(unsigned j) const; bool is_fixed(unsigned j) const; bool is_free(unsigned j) const; bool value_is_int(unsigned j) const; void set_value_for_nbasic_column_ignore_old_values(unsigned j, const impq & new_val); bool non_basic_columns_are_at_bounds() const; bool is_feasible() const; bool column_is_int_inf(unsigned j) const; void trace_inf_rows() const; lia_move branch_or_sat(); int find_any_inf_int_column_basis_first(); int find_inf_int_base_column(); int find_inf_int_boxed_base_column_with_smallest_range(unsigned&); int get_kth_inf_int(unsigned) const; lp_settings& settings(); const lp_settings& settings() const; void branch_infeasible_int_var(unsigned); lia_move mk_gomory_cut(unsigned inf_col, const row_strip& row); lia_move proceed_with_gomory_cut(unsigned j); bool is_gomory_cut_target(const row_strip&); bool at_bound(unsigned j) const; bool has_low(unsigned j) const; bool has_upper(unsigned j) const; unsigned row_of_basic_column(unsigned j) const; public: void display_column(std::ostream & out, unsigned j) const; constraint_index column_upper_bound_constraint(unsigned j) const; constraint_index column_lower_bound_constraint(unsigned j) const; bool current_solution_is_inf_on_cut() const; bool shift_var(unsigned j, unsigned range); private: void display_row_info(std::ostream & out, unsigned row_index) const; unsigned random(); bool has_inf_int() const; lia_move create_branch_on_column(int j); public: bool is_term(unsigned j) const; bool left_branch_is_more_narrow_than_right(unsigned); lia_move find_cube(); bool tighten_terms_for_cube(); bool tighten_term_for_cube(unsigned); unsigned column_count() const; bool all_columns_are_bounded() const; impq get_cube_delta_for_term(const lar_term&) const; void find_feasible_solution(); int find_inf_int_nbasis_column() const; lia_move run_gcd_test(); lia_move gomory_cut(); lia_move hnf_cut(); lia_move make_hnf_cut(); bool init_terms_for_hnf_cut(); bool hnf_matrix_is_empty() const; void try_add_term_to_A_for_hnf(unsigned term_index); bool hnf_has_var_with_non_integral_value() const; bool hnf_cutter_is_full() const; void patch_nbasic_column(unsigned j, bool patch_only_int_vals); }; } z3-z3-4.8.7/src/util/lp/lar_constraints.h000066400000000000000000000056161356505360400201740ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #pragma once #include "util/vector.h" #include #include #include #include #include "util/lp/lp_utils.h" #include "util/lp/ul_pair.h" #include "util/lp/lar_term.h" namespace lp { inline lconstraint_kind flip_kind(lconstraint_kind t) { return static_cast( - static_cast(t)); } inline std::string lconstraint_kind_string(lconstraint_kind t) { switch (t) { case LE: return std::string("<="); case LT: return std::string("<"); case GE: return std::string(">="); case GT: return std::string(">"); case EQ: return std::string("="); } lp_unreachable(); return std::string(); // it is unreachable } struct lar_base_constraint { lconstraint_kind m_kind; mpq m_right_side; virtual vector> get_left_side_coefficients() const = 0; lar_base_constraint() {} lar_base_constraint(lconstraint_kind kind, const mpq& right_side) :m_kind(kind), m_right_side(right_side) {} virtual unsigned size() const = 0; virtual ~lar_base_constraint(){} virtual mpq get_free_coeff_of_left_side() const { return zero_of_type();} }; struct lar_var_constraint: public lar_base_constraint { unsigned m_j; vector> get_left_side_coefficients() const override { vector> ret; ret.push_back(std::make_pair(one_of_type(), m_j)); return ret; } unsigned size() const override { return 1;} lar_var_constraint(unsigned j, lconstraint_kind kind, const mpq& right_side) : lar_base_constraint(kind, right_side), m_j(j) { } }; struct lar_term_constraint: public lar_base_constraint { const lar_term * m_term; vector> get_left_side_coefficients() const override { return m_term->coeffs_as_vector(); } unsigned size() const override { return m_term->size();} lar_term_constraint(const lar_term *t, lconstraint_kind kind, const mpq& right_side) : lar_base_constraint(kind, right_side), m_term(t) { } // mpq get_free_coeff_of_left_side() const override { return m_term->m_v;} }; class lar_constraint : public lar_base_constraint { public: vector> m_coeffs; lar_constraint(const vector> & left_side, lconstraint_kind kind, const mpq & right_side) : lar_base_constraint(kind, right_side), m_coeffs(left_side) {} lar_constraint(const lar_base_constraint & c) { lp_assert(false); // should not be called : todo! } unsigned size() const override { return static_cast(m_coeffs.size()); } vector> get_left_side_coefficients() const override { return m_coeffs; } }; } z3-z3-4.8.7/src/util/lp/lar_core_solver.cpp000066400000000000000000000004631356505360400204750ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #include #include #include #include "util/vector.h" #include #include "util/lp/lar_core_solver_def.h" z3-z3-4.8.7/src/util/lp/lar_core_solver.h000066400000000000000000000743321356505360400201500ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #pragma once #include "util/vector.h" #include #include #include "util/lp/lp_core_solver_base.h" #include #include "util/lp/indexed_vector.h" #include "util/lp/binary_heap_priority_queue.h" #include "util/lp/breakpoint.h" #include "util/lp/lp_primal_core_solver.h" #include "util/lp/stacked_vector.h" #include "util/lp/lar_solution_signature.h" #include "util/lp/stacked_value.h" namespace lp { class lar_core_solver { // m_sign_of_entering is set to 1 if the entering variable needs // to grow and is set to -1 otherwise int m_sign_of_entering_delta; vector> m_infeasible_linear_combination; int m_infeasible_sum_sign; // todo: get rid of this field vector> m_right_sides_dummy; vector m_costs_dummy; vector m_d_right_sides_dummy; vector m_d_costs_dummy; public: stacked_value m_stacked_simplex_strategy; stacked_vector m_column_types; // r - solver fields, for rational numbers vector> m_r_x; // the solution stacked_vector> m_r_lower_bounds; stacked_vector> m_r_upper_bounds; static_matrix> m_r_A; stacked_vector m_r_pushed_basis; vector m_r_basis; vector m_r_nbasis; vector m_r_heading; stacked_vector m_r_columns_nz; stacked_vector m_r_rows_nz; // d - solver fields, for doubles vector m_d_x; // the solution in doubles vector m_d_lower_bounds; vector m_d_upper_bounds; static_matrix m_d_A; stacked_vector m_d_pushed_basis; vector m_d_basis; vector m_d_nbasis; vector m_d_heading; lp_primal_core_solver> m_r_solver; // solver in rational numbers lp_primal_core_solver m_d_solver; // solver in doubles lar_core_solver( lp_settings & settings, const column_namer & column_names ); lp_settings & settings() { return m_r_solver.m_settings;} const lp_settings & settings() const { return m_r_solver.m_settings;} int get_infeasible_sum_sign() const { return m_infeasible_sum_sign; } const vector> & get_infeasibility_info(int & inf_sign) const { inf_sign = m_infeasible_sum_sign; return m_infeasible_linear_combination; } void fill_not_improvable_zero_sum_from_inf_row(); column_type get_column_type(unsigned j) { return m_column_types[j];} void init_costs(bool first_time); void init_cost_for_column(unsigned j); // returns m_sign_of_alpha_r int column_is_out_of_bounds(unsigned j); void calculate_pivot_row(unsigned i); void print_pivot_row(std::ostream & out, unsigned row_index) const { // remove later debug !!!! for (unsigned j : m_r_solver.m_pivot_row.m_index) { if (numeric_traits::is_pos(m_r_solver.m_pivot_row.m_data[j])) out << "+"; out << m_r_solver.m_pivot_row.m_data[j] << m_r_solver.column_name(j) << " "; } out << " +" << m_r_solver.column_name(m_r_solver.m_basis[row_index]) << std::endl; for (unsigned j : m_r_solver.m_pivot_row.m_index) { m_r_solver.print_column_bound_info(j, out); } m_r_solver.print_column_bound_info(m_r_solver.m_basis[row_index], out); } void advance_on_sorted_breakpoints(unsigned entering); void change_slope_on_breakpoint(unsigned entering, breakpoint> * b, mpq & slope_at_entering); bool row_is_infeasible(unsigned row); bool row_is_evidence(unsigned row); bool find_evidence_row(); void prefix_r(); void prefix_d(); unsigned m_m() const { return m_r_A.row_count(); } unsigned m_n() const { return m_r_A.column_count(); } bool is_tiny() const { return this->m_m() < 10 && this->m_n() < 20; } bool is_empty() const { return this->m_m() == 0 && this->m_n() == 0; } template int get_sign(const L & v) { return v > zero_of_type() ? 1 : (v < zero_of_type() ? -1 : 0); } void fill_evidence(unsigned row); unsigned get_number_of_non_ints() const; void solve(); bool lower_bounds_are_set() const { return true; } const indexed_vector & get_pivot_row() const { return m_r_solver.m_pivot_row; } void fill_not_improvable_zero_sum(); void pop_basis(unsigned k) { if (!settings().use_tableau()) { m_r_pushed_basis.pop(k); m_r_basis = m_r_pushed_basis(); m_r_solver.init_basis_heading_and_non_basic_columns_vector(); m_d_pushed_basis.pop(k); m_d_basis = m_d_pushed_basis(); m_d_solver.init_basis_heading_and_non_basic_columns_vector(); } else { m_d_basis = m_r_basis; m_d_nbasis = m_r_nbasis; m_d_heading = m_r_heading; } } void push() { lp_assert(m_r_solver.basis_heading_is_correct()); lp_assert(!need_to_presolve_with_double_solver() || m_d_solver.basis_heading_is_correct()); lp_assert(m_column_types.size() == m_r_A.column_count()); m_stacked_simplex_strategy = settings().simplex_strategy(); m_stacked_simplex_strategy.push(); m_column_types.push(); // rational if (!settings().use_tableau()) m_r_A.push(); m_r_lower_bounds.push(); m_r_upper_bounds.push(); if (!settings().use_tableau()) { push_vector(m_r_pushed_basis, m_r_basis); push_vector(m_r_columns_nz, m_r_solver.m_columns_nz); push_vector(m_r_rows_nz, m_r_solver.m_rows_nz); } m_d_A.push(); if (!settings().use_tableau()) push_vector(m_d_pushed_basis, m_d_basis); } template void push_vector(stacked_vector & pushed_vector, const vector & vector) { lp_assert(pushed_vector.size() <= vector.size()); for (unsigned i = 0; i < vector.size();i++) { if (i == pushed_vector.size()) { pushed_vector.push_back(vector[i]); } else { pushed_vector[i] = vector[i]; } } pushed_vector.push(); } void pop_markowitz_counts(unsigned k) { m_r_columns_nz.pop(k); m_r_rows_nz.pop(k); m_r_solver.m_columns_nz.resize(m_r_columns_nz.size()); m_r_solver.m_rows_nz.resize(m_r_rows_nz.size()); for (unsigned i = 0; i < m_r_columns_nz.size(); i++) m_r_solver.m_columns_nz[i] = m_r_columns_nz[i]; for (unsigned i = 0; i < m_r_rows_nz.size(); i++) m_r_solver.m_rows_nz[i] = m_r_rows_nz[i]; } void pop(unsigned k) { // rationals if (!settings().use_tableau()) m_r_A.pop(k); m_r_lower_bounds.pop(k); m_r_upper_bounds.pop(k); m_column_types.pop(k); delete m_r_solver.m_factorization; m_r_solver.m_factorization = nullptr; m_r_x.resize(m_r_A.column_count()); m_r_solver.m_costs.resize(m_r_A.column_count()); m_r_solver.m_d.resize(m_r_A.column_count()); if(!settings().use_tableau()) pop_markowitz_counts(k); m_d_A.pop(k); // doubles delete m_d_solver.m_factorization; m_d_solver.m_factorization = nullptr; m_d_x.resize(m_d_A.column_count()); pop_basis(k); m_stacked_simplex_strategy.pop(k); settings().simplex_strategy() = m_stacked_simplex_strategy; lp_assert(m_r_solver.basis_heading_is_correct()); lp_assert(!need_to_presolve_with_double_solver() || m_d_solver.basis_heading_is_correct()); } bool need_to_presolve_with_double_solver() const { return settings().simplex_strategy() == simplex_strategy_enum::lu; } template bool is_zero_vector(const vector & b) { for (const L & m: b) if (!is_zero(m)) return false; return true; } bool update_xj_and_get_delta(unsigned j, non_basic_column_value_position pos_type, numeric_pair & delta) { auto & x = m_r_x[j]; switch (pos_type) { case at_lower_bound: if (x == m_r_solver.m_lower_bounds[j]) return false; delta = m_r_solver.m_lower_bounds[j] - x; m_r_solver.m_x[j] = m_r_solver.m_lower_bounds[j]; break; case at_fixed: case at_upper_bound: if (x == m_r_solver.m_upper_bounds[j]) return false; delta = m_r_solver.m_upper_bounds[j] - x; x = m_r_solver.m_upper_bounds[j]; break; case free_of_bounds: { return false; } case not_at_bound: switch (m_column_types[j]) { case column_type::free_column: return false; case column_type::upper_bound: delta = m_r_solver.m_upper_bounds[j] - x; x = m_r_solver.m_upper_bounds[j]; break; case column_type::lower_bound: delta = m_r_solver.m_lower_bounds[j] - x; x = m_r_solver.m_lower_bounds[j]; break; case column_type::boxed: if (x > m_r_solver.m_upper_bounds[j]) { delta = m_r_solver.m_upper_bounds[j] - x; x += m_r_solver.m_upper_bounds[j]; } else { delta = m_r_solver.m_lower_bounds[j] - x; x = m_r_solver.m_lower_bounds[j]; } break; case column_type::fixed: delta = m_r_solver.m_lower_bounds[j] - x; x = m_r_solver.m_lower_bounds[j]; break; default: lp_assert(false); } break; default: lp_unreachable(); } m_r_solver.remove_column_from_inf_set(j); return true; } void prepare_solver_x_with_signature_tableau(const lar_solution_signature & signature) { lp_assert(m_r_solver.inf_set_is_correct()); for (auto &t : signature) { unsigned j = t.first; if (m_r_heading[j] >= 0) continue; auto pos_type = t.second; numeric_pair delta; if (!update_xj_and_get_delta(j, pos_type, delta)) continue; for (const auto & cc : m_r_solver.m_A.m_columns[j]){ unsigned i = cc.var(); unsigned jb = m_r_solver.m_basis[i]; m_r_solver.update_x_with_delta_and_track_feasibility(jb, - delta * m_r_solver.m_A.get_val(cc)); } CASSERT("A_off", m_r_solver.A_mult_x_is_off() == false); } lp_assert(m_r_solver.inf_set_is_correct()); } template void prepare_solver_x_with_signature(const lar_solution_signature & signature, lp_primal_core_solver & s) { for (auto &t : signature) { unsigned j = t.first; lp_assert(m_r_heading[j] < 0); auto pos_type = t.second; switch (pos_type) { case at_lower_bound: s.m_x[j] = s.m_lower_bounds[j]; break; case at_fixed: case at_upper_bound: s.m_x[j] = s.m_upper_bounds[j]; break; case free_of_bounds: { s.m_x[j] = zero_of_type(); continue; } case not_at_bound: switch (m_column_types[j]) { case column_type::free_column: lp_assert(false); // unreachable case column_type::upper_bound: s.m_x[j] = s.m_upper_bounds[j]; break; case column_type::lower_bound: s.m_x[j] = s.m_lower_bounds[j]; break; case column_type::boxed: if (settings().random_next() % 2) { s.m_x[j] = s.m_lower_bounds[j]; } else { s.m_x[j] = s.m_upper_bounds[j]; } break; case column_type::fixed: s.m_x[j] = s.m_lower_bounds[j]; break; default: lp_assert(false); } break; default: lp_unreachable(); } } lp_assert(is_zero_vector(s.m_b)); s.solve_Ax_eq_b(); } template void catch_up_in_lu_in_reverse(const vector & trace_of_basis_change, lp_primal_core_solver & cs) { // recover the previous working basis for (unsigned i = trace_of_basis_change.size(); i > 0; i-= 2) { unsigned entering = trace_of_basis_change[i-1]; unsigned leaving = trace_of_basis_change[i-2]; cs.change_basis_unconditionally(entering, leaving); } cs.init_lu(); } //basis_heading is the basis heading of the solver owning trace_of_basis_change // here we compact the trace as we go to avoid unnecessary column changes template void catch_up_in_lu(const vector & trace_of_basis_change, const vector & basis_heading, lp_primal_core_solver & cs) { if (cs.m_factorization == nullptr || cs.m_factorization->m_refactor_counter + trace_of_basis_change.size()/2 >= 200) { for (unsigned i = 0; i < trace_of_basis_change.size(); i+= 2) { unsigned entering = trace_of_basis_change[i]; unsigned leaving = trace_of_basis_change[i+1]; cs.change_basis_unconditionally(entering, leaving); } if (cs.m_factorization != nullptr) { delete cs.m_factorization; cs.m_factorization = nullptr; } } else { indexed_vector w(cs.m_A.row_count()); // the queues of delayed indices std::queue entr_q, leav_q; auto * l = cs.m_factorization; lp_assert(l->get_status() == LU_status::OK); for (unsigned i = 0; i < trace_of_basis_change.size(); i+= 2) { unsigned entering = trace_of_basis_change[i]; unsigned leaving = trace_of_basis_change[i+1]; bool good_e = basis_heading[entering] >= 0 && cs.m_basis_heading[entering] < 0; bool good_l = basis_heading[leaving] < 0 && cs.m_basis_heading[leaving] >= 0; if (!good_e && !good_l) continue; if (good_e && !good_l) { while (!leav_q.empty() && cs.m_basis_heading[leav_q.front()] < 0) leav_q.pop(); if (!leav_q.empty()) { leaving = leav_q.front(); leav_q.pop(); } else { entr_q.push(entering); continue; } } else if (!good_e && good_l) { while (!entr_q.empty() && cs.m_basis_heading[entr_q.front()] >= 0) entr_q.pop(); if (!entr_q.empty()) { entering = entr_q.front(); entr_q.pop(); } else { leav_q.push(leaving); continue; } } lp_assert(cs.m_basis_heading[entering] < 0); lp_assert(cs.m_basis_heading[leaving] >= 0); if (l->get_status() == LU_status::OK) { l->prepare_entering(entering, w); // to init vector w l->replace_column(zero_of_type(), w, cs.m_basis_heading[leaving]); } cs.change_basis_unconditionally(entering, leaving); } if (l->get_status() != LU_status::OK) { delete l; cs.m_factorization = nullptr; } } if (cs.m_factorization == nullptr) { if (numeric_traits::precise()) init_factorization(cs.m_factorization, cs.m_A, cs.m_basis, settings()); } } bool no_r_lu() const { return m_r_solver.m_factorization == nullptr || m_r_solver.m_factorization->get_status() == LU_status::Degenerated; } void solve_on_signature_tableau(const lar_solution_signature & signature, const vector & changes_of_basis) { r_basis_is_OK(); lp_assert(settings().use_tableau()); bool r = catch_up_in_lu_tableau(changes_of_basis, m_d_solver.m_basis_heading); if (!r) { // it is the case where m_d_solver gives a degenerated basis prepare_solver_x_with_signature_tableau(signature); // still are going to use the signature partially m_r_solver.find_feasible_solution(); m_d_basis = m_r_basis; m_d_heading = m_r_heading; m_d_nbasis = m_r_nbasis; delete m_d_solver.m_factorization; m_d_solver.m_factorization = nullptr; } else { prepare_solver_x_with_signature_tableau(signature); m_r_solver.start_tracing_basis_changes(); m_r_solver.find_feasible_solution(); if (settings().get_cancel_flag()) return; m_r_solver.stop_tracing_basis_changes(); // and now catch up in the double solver lp_assert(m_r_solver.total_iterations() >= m_r_solver.m_trace_of_basis_change_vector.size() /2); catch_up_in_lu(m_r_solver.m_trace_of_basis_change_vector, m_r_solver.m_basis_heading, m_d_solver); } lp_assert(r_basis_is_OK()); } bool adjust_x_of_column(unsigned j) { /* if (m_r_solver.m_basis_heading[j] >= 0) { return false; } if (m_r_solver.column_is_feasible(j)) { return false; } m_r_solver.snap_column_to_bound_tableau(j); lp_assert(m_r_solver.column_is_feasible(j)); m_r_solver.m_inf_set.erase(j); */ lp_assert(false); return true; } bool catch_up_in_lu_tableau(const vector & trace_of_basis_change, const vector & basis_heading) { lp_assert(r_basis_is_OK()); // the queues of delayed indices std::queue entr_q, leav_q; for (unsigned i = 0; i < trace_of_basis_change.size(); i+= 2) { unsigned entering = trace_of_basis_change[i]; unsigned leaving = trace_of_basis_change[i+1]; bool good_e = basis_heading[entering] >= 0 && m_r_solver.m_basis_heading[entering] < 0; bool good_l = basis_heading[leaving] < 0 && m_r_solver.m_basis_heading[leaving] >= 0; if (!good_e && !good_l) continue; if (good_e && !good_l) { while (!leav_q.empty() && m_r_solver.m_basis_heading[leav_q.front()] < 0) leav_q.pop(); if (!leav_q.empty()) { leaving = leav_q.front(); leav_q.pop(); } else { entr_q.push(entering); continue; } } else if (!good_e && good_l) { while (!entr_q.empty() && m_r_solver.m_basis_heading[entr_q.front()] >= 0) entr_q.pop(); if (!entr_q.empty()) { entering = entr_q.front(); entr_q.pop(); } else { leav_q.push(leaving); continue; } } lp_assert(m_r_solver.m_basis_heading[entering] < 0); lp_assert(m_r_solver.m_basis_heading[leaving] >= 0); m_r_solver.change_basis_unconditionally(entering, leaving); if(!m_r_solver.pivot_column_tableau(entering, m_r_solver.m_basis_heading[entering])) { // unroll the last step m_r_solver.change_basis_unconditionally(leaving, entering); #ifdef Z3DEBUG bool t = #endif m_r_solver.pivot_column_tableau(leaving, m_r_solver.m_basis_heading[leaving]); #ifdef Z3DEBUG lp_assert(t); #endif return false; } } lp_assert(r_basis_is_OK()); return true; } bool r_basis_is_OK() const { #ifdef Z3DEBUG if (!m_r_solver.m_settings.use_tableau()) return true; for (unsigned j : m_r_solver.m_basis) { lp_assert(m_r_solver.m_A.m_columns[j].size() == 1); } for (unsigned j =0; j < m_r_solver.m_basis_heading.size(); j++) { if (m_r_solver.m_basis_heading[j] >= 0) continue; if (m_r_solver.m_column_types[j] == column_type::fixed) continue; lp_assert(static_cast(- m_r_solver.m_basis_heading[j] - 1) < m_r_solver.m_column_types.size()); lp_assert( m_r_solver.m_basis_heading[j] <= -1); } #endif return true; } void solve_on_signature(const lar_solution_signature & signature, const vector & changes_of_basis) { SASSERT(!settings().use_tableau()); if (m_r_solver.m_factorization == nullptr) { for (unsigned j = 0; j < changes_of_basis.size(); j+=2) { unsigned entering = changes_of_basis[j]; unsigned leaving = changes_of_basis[j + 1]; m_r_solver.change_basis_unconditionally(entering, leaving); } init_factorization(m_r_solver.m_factorization, m_r_A, m_r_basis, settings()); } else { catch_up_in_lu(changes_of_basis, m_d_solver.m_basis_heading, m_r_solver); } if (no_r_lu()) { // it is the case where m_d_solver gives a degenerated basis, we need to roll back catch_up_in_lu_in_reverse(changes_of_basis, m_r_solver); m_r_solver.find_feasible_solution(); m_d_basis = m_r_basis; m_d_heading = m_r_heading; m_d_nbasis = m_r_nbasis; delete m_d_solver.m_factorization; m_d_solver.m_factorization = nullptr; } else { prepare_solver_x_with_signature(signature, m_r_solver); m_r_solver.start_tracing_basis_changes(); m_r_solver.find_feasible_solution(); if (settings().get_cancel_flag()) return; m_r_solver.stop_tracing_basis_changes(); // and now catch up in the double solver lp_assert(m_r_solver.total_iterations() >= m_r_solver.m_trace_of_basis_change_vector.size() /2); catch_up_in_lu(m_r_solver.m_trace_of_basis_change_vector, m_r_solver.m_basis_heading, m_d_solver); } } void create_double_matrix(static_matrix & A) { for (unsigned i = 0; i < m_r_A.row_count(); i++) { auto & row = m_r_A.m_rows[i]; for (row_cell & c : row) { A.add_new_element(i, c.var(), c.get_val().get_double()); } } } void fill_basis_d( vector& basis_d, vector& heading_d, vector& nbasis_d){ basis_d = m_r_basis; heading_d = m_r_heading; nbasis_d = m_r_nbasis; } template void extract_signature_from_lp_core_solver(const lp_primal_core_solver & solver, lar_solution_signature & signature) { signature.clear(); lp_assert(signature.size() == 0); for (unsigned j = 0; j < solver.m_basis_heading.size(); j++) { if (solver.m_basis_heading[j] < 0) { signature[j] = solver.get_non_basic_column_value_position(j); } } } void get_bounds_for_double_solver() { unsigned n = m_n(); m_d_lower_bounds.resize(n); m_d_upper_bounds.resize(n); double delta = find_delta_for_strict_boxed_bounds().get_double(); if (delta > 0.000001) delta = 0.000001; for (unsigned j = 0; j < n; j++) { if (lower_bound_is_set(j)) { const auto & lb = m_r_solver.m_lower_bounds[j]; m_d_lower_bounds[j] = lb.x.get_double() + delta * lb.y.get_double(); } if (upper_bound_is_set(j)) { const auto & ub = m_r_solver.m_upper_bounds[j]; m_d_upper_bounds[j] = ub.x.get_double() + delta * ub.y.get_double(); lp_assert(!lower_bound_is_set(j) || (m_d_upper_bounds[j] >= m_d_lower_bounds[j])); } } } void scale_problem_for_doubles( static_matrix& A, vector & lower_bounds, vector & upper_bounds) { vector column_scale_vector; vector right_side_vector(A.column_count()); settings().reps_in_scaler = 5; scaler scaler(right_side_vector, A, settings().scaling_minimum, settings().scaling_maximum, column_scale_vector, settings()); if (! scaler.scale()) { // the scale did not succeed, unscaling A.clear(); create_double_matrix(A); } else { for (unsigned j = 0; j < A.column_count(); j++) { if (m_r_solver.column_has_upper_bound(j)) { upper_bounds[j] /= column_scale_vector[j]; } if (m_r_solver.column_has_lower_bound(j)) { lower_bounds[j] /= column_scale_vector[j]; } } } } // returns the trace of basis changes vector find_solution_signature_with_doubles(lar_solution_signature & signature) { if (m_d_solver.m_factorization == nullptr || m_d_solver.m_factorization->get_status() != LU_status::OK) { vector ret; return ret; } get_bounds_for_double_solver(); extract_signature_from_lp_core_solver(m_r_solver, signature); prepare_solver_x_with_signature(signature, m_d_solver); m_d_solver.start_tracing_basis_changes(); m_d_solver.find_feasible_solution(); if (settings().get_cancel_flag()) return vector(); m_d_solver.stop_tracing_basis_changes(); extract_signature_from_lp_core_solver(m_d_solver, signature); return m_d_solver.m_trace_of_basis_change_vector; } bool lower_bound_is_set(unsigned j) const { switch (m_column_types[j]) { case column_type::free_column: case column_type::upper_bound: return false; case column_type::lower_bound: case column_type::boxed: case column_type::fixed: return true; default: lp_assert(false); } return false; } bool upper_bound_is_set(unsigned j) const { switch (m_column_types[j]) { case column_type::free_column: case column_type::lower_bound: return false; case column_type::upper_bound: case column_type::boxed: case column_type::fixed: return true; default: lp_assert(false); } return false; } void update_delta(mpq& delta, numeric_pair const& l, numeric_pair const& u) const { lp_assert(l <= u); if (l.x < u.x && l.y > u.y) { mpq delta1 = (u.x - l.x) / (l.y - u.y); if (delta1 < delta) { delta = delta1; } } lp_assert(l.x + delta * l.y <= u.x + delta * u.y); } mpq find_delta_for_strict_boxed_bounds() const{ mpq delta = numeric_traits::one(); for (unsigned j = 0; j < m_r_A.column_count(); j++ ) { if (m_column_types()[j] != column_type::boxed) continue; update_delta(delta, m_r_lower_bounds[j], m_r_upper_bounds[j]); } return delta; } mpq find_delta_for_strict_bounds(const mpq & initial_delta) const{ mpq delta = initial_delta; for (unsigned j = 0; j < m_r_A.column_count(); j++ ) { if (lower_bound_is_set(j)) update_delta(delta, m_r_lower_bounds[j], m_r_x[j]); if (upper_bound_is_set(j)) update_delta(delta, m_r_x[j], m_r_upper_bounds[j]); } return delta; } void init_column_row_nz_for_r_solver() { m_r_solver.init_column_row_non_zeroes(); } bool column_is_fixed(unsigned j) const { return m_column_types()[j] == column_type::fixed || ( m_column_types()[j] == column_type::boxed && m_r_solver.m_lower_bounds[j] == m_r_solver.m_upper_bounds[j]); } const impq & lower_bound(unsigned j) const { lp_assert(m_column_types()[j] == column_type::fixed || m_column_types()[j] == column_type::boxed || m_column_types()[j] == column_type::lower_bound); return m_r_lower_bounds[j]; } const impq & upper_bound(unsigned j) const { lp_assert(m_column_types()[j] == column_type::fixed || m_column_types()[j] == column_type::boxed || m_column_types()[j] == column_type::upper_bound); return m_r_upper_bounds[j]; } bool column_is_bounded(unsigned j) const { switch(m_column_types()[j]) { case column_type::fixed: case column_type::boxed: return true; default: return false; } } const vector& r_basis() const { return m_r_basis; } const vector& r_nbasis() const { return m_r_nbasis; } }; } z3-z3-4.8.7/src/util/lp/lar_core_solver_def.h000066400000000000000000000252461356505360400207660ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ /*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #include #include "util/vector.h" #include "util/lp/lar_core_solver.h" #include "util/lp/lar_solution_signature.h" namespace lp { lar_core_solver::lar_core_solver( lp_settings & settings, const column_namer & column_names ): m_infeasible_sum_sign(0), m_r_solver(m_r_A, m_right_sides_dummy, m_r_x, m_r_basis, m_r_nbasis, m_r_heading, m_costs_dummy, m_column_types(), m_r_lower_bounds(), m_r_upper_bounds(), settings, column_names), m_d_solver(m_d_A, m_d_right_sides_dummy, m_d_x, m_d_basis, m_d_nbasis, m_d_heading, m_d_costs_dummy, m_column_types(), m_d_lower_bounds, m_d_upper_bounds, settings, column_names){} void lar_core_solver::init_costs(bool first_time) { lp_assert(false); // should not be called // lp_assert(this->m_x.size() >= this->m_n()); // lp_assert(this->m_column_types.size() >= this->m_n()); // if (first_time) // this->m_costs.resize(this->m_n()); // X inf = this->m_infeasibility; // this->m_infeasibility = zero_of_type(); // for (unsigned j = this->m_n(); j--;) // init_cost_for_column(j); // if (!(first_time || inf >= this->m_infeasibility)) { // LP_OUT(this->m_settings, "iter = " << this->total_iterations() << std::endl); // LP_OUT(this->m_settings, "inf was " << T_to_string(inf) << " and now " << T_to_string(this->m_infeasibility) << std::endl); // lp_assert(false); // } // if (inf == this->m_infeasibility) // this->m_iters_with_no_cost_growing++; } void lar_core_solver::init_cost_for_column(unsigned j) { /* // If j is a breakpoint column, then we set the cost zero. // When anylyzing an entering column candidate we update the cost of the breakpoints columns to get the left or the right derivative if the infeasibility function const numeric_pair & x = this->m_x[j]; // set zero cost for each non-basis column if (this->m_basis_heading[j] < 0) { this->m_costs[j] = numeric_traits::zero(); return; } // j is a basis column switch (this->m_column_types[j]) { case fixed: case column_type::boxed: if (x > this->m_upper_bounds[j]) { this->m_costs[j] = 1; this->m_infeasibility += x - this->m_upper_bounds[j]; } else if (x < this->m_lower_bounds[j]) { this->m_infeasibility += this->m_lower_bounds[j] - x; this->m_costs[j] = -1; } else { this->m_costs[j] = numeric_traits::zero(); } break; case lower_bound: if (x < this->m_lower_bounds[j]) { this->m_costs[j] = -1; this->m_infeasibility += this->m_lower_bounds[j] - x; } else { this->m_costs[j] = numeric_traits::zero(); } break; case upper_bound: if (x > this->m_upper_bounds[j]) { this->m_costs[j] = 1; this->m_infeasibility += x - this->m_upper_bounds[j]; } else { this->m_costs[j] = numeric_traits::zero(); } break; case free_column: this->m_costs[j] = numeric_traits::zero(); break; default: lp_assert(false); break; }*/ } // returns m_sign_of_alpha_r int lar_core_solver::column_is_out_of_bounds(unsigned j) { /* switch (this->m_column_type[j]) { case fixed: case column_type::boxed: if (this->x_below_low_bound(j)) { return -1; } if (this->x_above_upper_bound(j)) { return 1; } return 0; case lower_bound: if (this->x_below_low_bound(j)) { return -1; } return 0; case upper_bound: if (this->x_above_upper_bound(j)) { return 1; } return 0; default: return 0; break; }*/ lp_assert(false); return true; } void lar_core_solver::calculate_pivot_row(unsigned i) { m_r_solver.calculate_pivot_row(i); } void lar_core_solver::prefix_r() { if (!m_r_solver.m_settings.use_tableau()) { m_r_solver.m_copy_of_xB.resize(m_r_solver.m_n()); m_r_solver.m_ed.resize(m_r_solver.m_m()); m_r_solver.m_pivot_row.resize(m_r_solver.m_n()); m_r_solver.m_pivot_row_of_B_1.resize(m_r_solver.m_m()); m_r_solver.m_w.resize(m_r_solver.m_m()); m_r_solver.m_y.resize(m_r_solver.m_m()); m_r_solver.m_rows_nz.resize(m_r_solver.m_m(), 0); m_r_solver.m_columns_nz.resize(m_r_solver.m_n(), 0); init_column_row_nz_for_r_solver(); } m_r_solver.m_b.resize(m_r_solver.m_m()); if (m_r_solver.m_settings.simplex_strategy() != simplex_strategy_enum::tableau_rows) { if(m_r_solver.m_settings.use_breakpoints_in_feasibility_search) m_r_solver.m_breakpoint_indices_queue.resize(m_r_solver.m_n()); m_r_solver.m_costs.resize(m_r_solver.m_n()); m_r_solver.m_d.resize(m_r_solver.m_n()); m_r_solver.m_using_infeas_costs = true; } } void lar_core_solver::prefix_d() { m_d_solver.m_b.resize(m_d_solver.m_m()); m_d_solver.m_breakpoint_indices_queue.resize(m_d_solver.m_n()); m_d_solver.m_copy_of_xB.resize(m_d_solver.m_n()); m_d_solver.m_costs.resize(m_d_solver.m_n()); m_d_solver.m_d.resize(m_d_solver.m_n()); m_d_solver.m_ed.resize(m_d_solver.m_m()); m_d_solver.m_pivot_row.resize(m_d_solver.m_n()); m_d_solver.m_pivot_row_of_B_1.resize(m_d_solver.m_m()); m_d_solver.m_w.resize(m_d_solver.m_m()); m_d_solver.m_y.resize(m_d_solver.m_m()); m_d_solver.m_steepest_edge_coefficients.resize(m_d_solver.m_n()); m_d_solver.m_column_norms.clear(); m_d_solver.m_column_norms.resize(m_d_solver.m_n(), 2); m_d_solver.m_inf_set.clear(); m_d_solver.m_inf_set.resize(m_d_solver.m_n()); } void lar_core_solver::fill_not_improvable_zero_sum_from_inf_row() { CASSERT("A_off", m_r_solver.A_mult_x_is_off() == false); unsigned bj = m_r_basis[m_r_solver.m_inf_row_index_for_tableau]; m_infeasible_sum_sign = m_r_solver.inf_sign_of_column(bj); m_infeasible_linear_combination.clear(); for (auto & rc : m_r_solver.m_A.m_rows[m_r_solver.m_inf_row_index_for_tableau]) { m_infeasible_linear_combination.push_back(std::make_pair( rc.get_val(), rc.var())); } } void lar_core_solver::fill_not_improvable_zero_sum() { if (m_r_solver.m_settings.simplex_strategy() == simplex_strategy_enum::tableau_rows) { fill_not_improvable_zero_sum_from_inf_row(); return; } // reusing the existing mechanism for row_feasibility_loop m_infeasible_sum_sign = m_r_solver.m_settings.use_breakpoints_in_feasibility_search? -1 : 1; m_infeasible_linear_combination.clear(); for (auto j : m_r_solver.m_basis) { const mpq & cost_j = m_r_solver.m_costs[j]; if (!numeric_traits::is_zero(cost_j)) { m_infeasible_linear_combination.push_back(std::make_pair(cost_j, j)); } } // m_costs are expressed by m_d ( additional costs), substructing the latter gives 0 for (unsigned j = 0; j < m_r_solver.m_n(); j++) { if (m_r_solver.m_basis_heading[j] >= 0) continue; const mpq & d_j = m_r_solver.m_d[j]; if (!numeric_traits::is_zero(d_j)) { m_infeasible_linear_combination.push_back(std::make_pair(-d_j, j)); } } } unsigned lar_core_solver::get_number_of_non_ints() const { unsigned n = 0; for (auto & x : m_r_solver.m_x) { if (x.is_int() == false) n++; } return n; } void lar_core_solver::solve() { TRACE("lar_solver", tout << m_r_solver.get_status() << "\n";); lp_assert(m_r_solver.non_basic_columns_are_set_correctly()); lp_assert(m_r_solver.inf_set_is_correct()); TRACE("find_feas_stats", tout << "infeasibles = " << m_r_solver.m_inf_set.size() << ", int_infs = " << get_number_of_non_ints() << std::endl;); if (m_r_solver.current_x_is_feasible() && m_r_solver.m_look_for_feasible_solution_only) { m_r_solver.set_status(lp_status::OPTIMAL); TRACE("lar_solver", tout << m_r_solver.get_status() << "\n";); return; } ++settings().st().m_need_to_solve_inf; CASSERT("A_off", !m_r_solver.A_mult_x_is_off()); lp_assert((!settings().use_tableau()) || r_basis_is_OK()); if (need_to_presolve_with_double_solver()) { TRACE("lar_solver", tout << "presolving\n";); prefix_d(); lar_solution_signature solution_signature; vector changes_of_basis = find_solution_signature_with_doubles(solution_signature); if (m_d_solver.get_status() == lp_status::TIME_EXHAUSTED) { m_r_solver.set_status(lp_status::TIME_EXHAUSTED); return; } if (settings().use_tableau()) solve_on_signature_tableau(solution_signature, changes_of_basis); else solve_on_signature(solution_signature, changes_of_basis); lp_assert(!settings().use_tableau() || r_basis_is_OK()); } else { if (!settings().use_tableau()) { TRACE("lar_solver", tout << "no tablau\n";); bool snapped = m_r_solver.snap_non_basic_x_to_bound(); lp_assert(m_r_solver.non_basic_columns_are_set_correctly()); if (snapped) m_r_solver.solve_Ax_eq_b(); } if (m_r_solver.m_look_for_feasible_solution_only) m_r_solver.find_feasible_solution(); else { TRACE("lar_solver", tout << "solve\n";); m_r_solver.solve(); } lp_assert(!settings().use_tableau() || r_basis_is_OK()); } if (m_r_solver.get_status() == lp_status::INFEASIBLE) { fill_not_improvable_zero_sum(); } else if (m_r_solver.get_status() != lp_status::UNBOUNDED) { m_r_solver.set_status(lp_status::OPTIMAL); } lp_assert(r_basis_is_OK()); lp_assert(m_r_solver.non_basic_columns_are_set_correctly()); lp_assert(m_r_solver.inf_set_is_correct()); TRACE("lar_solver", tout << m_r_solver.get_status() << "\n";); } } z3-z3-4.8.7/src/util/lp/lar_solution_signature.h000066400000000000000000000006141356505360400215530ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #pragma once #include "util/vector.h" #include "util/debug.h" #include "util/lp/lp_settings.h" #include namespace lp { typedef std::unordered_map lar_solution_signature; } z3-z3-4.8.7/src/util/lp/lar_solver.cpp000066400000000000000000002465021356505360400174730ustar00rootroot00000000000000#include "util/lp/lar_solver.h" /* Copyright (c) 2017 Microsoft Corporation Author: Nikolaj Bjorner, Lev Nachmanson */ namespace lp { unsigned lar_solver::constraint_count() const { return m_constraints.size(); } const lar_base_constraint& lar_solver::get_constraint(unsigned ci) const { return *(m_constraints[ci]); } ////////////////// methods //////////////////////////////// static_matrix> & lar_solver::A_r() { return m_mpq_lar_core_solver.m_r_A;} static_matrix> const & lar_solver::A_r() const { return m_mpq_lar_core_solver.m_r_A;} static_matrix & lar_solver::A_d() { return m_mpq_lar_core_solver.m_d_A;} static_matrix const & lar_solver::A_d() const { return m_mpq_lar_core_solver.m_d_A;} lp_settings & lar_solver::settings() { return m_settings;} lp_settings const & lar_solver::settings() const { return m_settings;} void clear() {lp_assert(false); // not implemented } lar_solver::lar_solver() : m_status(lp_status::UNKNOWN), m_infeasible_column_index(-1), m_terms_start_index(1000000), m_mpq_lar_core_solver(m_settings, *this), m_int_solver(nullptr) {} void lar_solver::set_track_pivoted_rows(bool v) { m_mpq_lar_core_solver.m_r_solver.m_pivoted_rows = v? (& m_rows_with_changed_bounds) : nullptr; } bool lar_solver::get_track_pivoted_rows() const { return m_mpq_lar_core_solver.m_r_solver.m_pivoted_rows != nullptr; } lar_solver::~lar_solver(){ for (auto c : m_constraints) delete c; for (auto t : m_terms) delete t; } bool lar_solver::is_term(var_index j) const { return j >= m_terms_start_index && j - m_terms_start_index < m_terms.size(); } unsigned lar_solver::adjust_term_index(unsigned j) const { lp_assert(is_term(j)); return j - m_terms_start_index; } bool lar_solver::use_lu() const { return m_settings.simplex_strategy() == simplex_strategy_enum::lu; } bool lar_solver::sizes_are_correct() const { lp_assert(strategy_is_undecided() || !m_mpq_lar_core_solver.need_to_presolve_with_double_solver() || A_r().column_count() == A_d().column_count()); lp_assert(A_r().column_count() == m_mpq_lar_core_solver.m_r_solver.m_column_types.size()); lp_assert(A_r().column_count() == m_mpq_lar_core_solver.m_r_solver.m_costs.size()); lp_assert(A_r().column_count() == m_mpq_lar_core_solver.m_r_x.size()); return true; } std::ostream& lar_solver::print_implied_bound(const implied_bound& be, std::ostream & out) const { out << "implied bound\n"; unsigned v = be.m_j; if (is_term(v)) { out << "it is a term number " << be.m_j << std::endl; print_term(*m_terms[be.m_j - m_terms_start_index], out); } else { out << get_column_name(v); } out << " " << lconstraint_kind_string(be.kind()) << " " << be.m_bound << std::endl; out << "end of implied bound" << std::endl; return out; } std::ostream& lar_solver::print_values(std::ostream& out) const { for (unsigned i = 0; i < m_mpq_lar_core_solver.m_r_x.size(); i++ ) { const numeric_pair & rp = m_mpq_lar_core_solver.m_r_x[i]; out << this->get_column_name(i) << " -> " << rp << "\n"; } return out; } bool lar_solver::implied_bound_is_correctly_explained(implied_bound const & be, const vector> & explanation) const { std::unordered_map coeff_map; auto rs_of_evidence = zero_of_type(); unsigned n_of_G = 0, n_of_L = 0; bool strict = false; for (auto & it : explanation) { mpq coeff = it.first; constraint_index con_ind = it.second; const auto & constr = *m_constraints[con_ind]; lconstraint_kind kind = coeff.is_pos() ? constr.m_kind : flip_kind(constr.m_kind); register_in_map(coeff_map, constr, coeff); if (kind == GT || kind == LT) strict = true; if (kind == GE || kind == GT) n_of_G++; else if (kind == LE || kind == LT) n_of_L++; rs_of_evidence += coeff*constr.m_right_side; } lp_assert(n_of_G == 0 || n_of_L == 0); lconstraint_kind kind = n_of_G ? GE : (n_of_L ? LE : EQ); if (strict) kind = static_cast((static_cast(kind) / 2)); if (!is_term(be.m_j)) { if (coeff_map.size() != 1) return false; auto it = coeff_map.find(be.m_j); if (it == coeff_map.end()) return false; mpq ratio = it->second; if (ratio < zero_of_type()) { kind = static_cast(-kind); } rs_of_evidence /= ratio; } else { const lar_term * t = m_terms[adjust_term_index(be.m_j)]; const auto first_coeff = *t->m_coeffs.begin(); unsigned j = first_coeff.first; auto it = coeff_map.find(j); if (it == coeff_map.end()) return false; mpq ratio = it->second / first_coeff.second; for (auto & p : t->m_coeffs) { it = coeff_map.find(p.first); if (it == coeff_map.end()) return false; if (p.second * ratio != it->second) return false; } if (ratio < zero_of_type()) { kind = static_cast(-kind); } rs_of_evidence /= ratio; // rs_of_evidence += t->m_v * ratio; } return kind == be.kind() && rs_of_evidence == be.m_bound; } void lar_solver::analyze_new_bounds_on_row( unsigned row_index, bound_propagator & bp) { lp_assert(!use_tableau()); unsigned j = m_mpq_lar_core_solver.m_r_basis[row_index]; // basis column for the row bound_analyzer_on_row> ra_pos(m_mpq_lar_core_solver.get_pivot_row(), j, zero_of_type>(), row_index, bp ); ra_pos.analyze(); } void lar_solver::analyze_new_bounds_on_row_tableau( unsigned row_index, bound_propagator & bp ) { if (A_r().m_rows[row_index].size() > settings().max_row_length_for_bound_propagation) return; lp_assert(use_tableau()); bound_analyzer_on_row>::analyze_row(A_r().m_rows[row_index], static_cast(-1), zero_of_type>(), row_index, bp ); } void lar_solver::substitute_basis_var_in_terms_for_row(unsigned i) { // todo : create a map from term basic vars to the rows where they are used unsigned basis_j = m_mpq_lar_core_solver.m_r_solver.m_basis[i]; for (unsigned k = 0; k < m_terms.size(); k++) { if (term_is_used_as_row(k)) continue; if (!m_terms[k]->contains(basis_j)) continue; m_terms[k]->subst(basis_j, m_mpq_lar_core_solver.m_r_solver.m_pivot_row); } } void lar_solver::calculate_implied_bounds_for_row(unsigned i, bound_propagator & bp) { if(use_tableau()) { analyze_new_bounds_on_row_tableau(i, bp); } else { m_mpq_lar_core_solver.calculate_pivot_row(i); substitute_basis_var_in_terms_for_row(i); analyze_new_bounds_on_row(i, bp); } } unsigned lar_solver::adjust_column_index_to_term_index(unsigned j) const { unsigned ext_var_or_term = m_var_register.local_to_external(j); return ext_var_or_term < m_terms_start_index ? j : ext_var_or_term; } void lar_solver::propagate_bounds_on_a_term(const lar_term& t, bound_propagator & bp, unsigned term_offset) { lp_assert(false); // not implemented } void lar_solver::explain_implied_bound(implied_bound & ib, bound_propagator & bp) { unsigned i = ib.m_row_or_term_index; int bound_sign = ib.m_is_lower_bound? 1: -1; int j_sign = (ib.m_coeff_before_j_is_pos ? 1 :-1) * bound_sign; unsigned bound_j = ib.m_j; if (is_term(bound_j)) { bound_j = m_var_register.external_to_local(bound_j); } for (auto const& r : A_r().m_rows[i]) { unsigned j = r.var(); if (j == bound_j) continue; mpq const& a = r.get_val(); int a_sign = is_pos(a)? 1: -1; int sign = j_sign * a_sign; const ul_pair & ul = m_columns_to_ul_pairs[j]; auto witness = sign > 0? ul.upper_bound_witness(): ul.lower_bound_witness(); lp_assert(is_valid(witness)); bp.consume(a, witness); } // lp_assert(implied_bound_is_correctly_explained(ib, explanation)); } bool lar_solver::term_is_used_as_row(unsigned term) const { lp_assert(is_term(term)); return m_var_register.external_is_used(term); } void lar_solver::propagate_bounds_on_terms(bound_propagator & bp) { for (unsigned i = 0; i < m_terms.size(); i++) { if (term_is_used_as_row(i + m_terms_start_index)) continue; // this term is used a left side of a constraint, // it was processed as a touched row if needed propagate_bounds_on_a_term(*m_terms[i], bp, i); } } // goes over touched rows and tries to induce bounds void lar_solver::propagate_bounds_for_touched_rows(bound_propagator & bp) { if (!use_tableau()) return; // todo: consider to remove the restriction for (unsigned i : m_rows_with_changed_bounds.m_index) { calculate_implied_bounds_for_row(i, bp); if (settings().get_cancel_flag()) return; } m_rows_with_changed_bounds.clear(); if (!use_tableau()) { propagate_bounds_on_terms(bp); } } lp_status lar_solver::get_status() const { return m_status; } void lar_solver::set_status(lp_status s) { m_status = s; } lp_status lar_solver::find_feasible_solution() { m_settings.st().m_make_feasible++; if (A_r().column_count() > m_settings.st().m_max_cols) m_settings.st().m_max_cols = A_r().column_count(); if (A_r().row_count() > m_settings.st().m_max_rows) m_settings.st().m_max_rows = A_r().row_count(); if (strategy_is_undecided()) decide_on_strategy_and_adjust_initial_state(); m_mpq_lar_core_solver.m_r_solver.m_look_for_feasible_solution_only = true; auto ret = solve(); return ret; } lp_status lar_solver::solve() { if (m_status == lp_status::INFEASIBLE) { return m_status; } solve_with_core_solver(); if (m_status != lp_status::INFEASIBLE) { if (m_settings.bound_propagation()) detect_rows_with_changed_bounds(); } m_columns_with_changed_bound.clear(); return m_status; } void lar_solver::fill_explanation_from_infeasible_column(vector> & evidence) const{ // this is the case when the lower bound is in conflict with the upper one const ul_pair & ul = m_columns_to_ul_pairs[m_infeasible_column_index]; evidence.push_back(std::make_pair(numeric_traits::one(), ul.upper_bound_witness())); evidence.push_back(std::make_pair(-numeric_traits::one(), ul.lower_bound_witness())); } unsigned lar_solver::get_total_iterations() const { return m_mpq_lar_core_solver.m_r_solver.total_iterations(); } vector lar_solver::get_list_of_all_var_indices() const { vector ret; for (unsigned j = 0; j < m_mpq_lar_core_solver.m_r_heading.size(); j++) ret.push_back(j); return ret; } void lar_solver::push() { m_simplex_strategy = m_settings.simplex_strategy(); m_simplex_strategy.push(); m_columns_to_ul_pairs.push(); m_infeasible_column_index.push(); m_mpq_lar_core_solver.push(); m_term_count = m_terms.size(); m_term_count.push(); m_constraint_count = m_constraints.size(); m_constraint_count.push(); } void lar_solver::clean_popped_elements(unsigned n, int_set& set) { vector to_remove; for (unsigned j: set.m_index) if (j >= n) to_remove.push_back(j); for (unsigned j : to_remove) set.erase(j); } void lar_solver::shrink_inf_set_after_pop(unsigned n, int_set & set) { clean_popped_elements(n, set); set.resize(n); } void lar_solver::pop(unsigned k) { TRACE("int_solver", tout << "pop" << std::endl;); TRACE("lar_solver", tout << "k = " << k << std::endl;); m_infeasible_column_index.pop(k); unsigned n = m_columns_to_ul_pairs.peek_size(k); m_var_register.shrink(n); if (m_settings.use_tableau()) { pop_tableau(); } lp_assert(A_r().column_count() == n); m_columns_to_ul_pairs.pop(k); m_mpq_lar_core_solver.pop(k); clean_popped_elements(n, m_columns_with_changed_bound); unsigned m = A_r().row_count(); clean_popped_elements(m, m_rows_with_changed_bounds); clean_inf_set_of_r_solver_after_pop(); lp_assert(m_settings.simplex_strategy() == simplex_strategy_enum::undecided || (!use_tableau()) || m_mpq_lar_core_solver.m_r_solver.reduced_costs_are_correct_tableau()); lp_assert(ax_is_correct()); m_constraint_count.pop(k); for (unsigned i = m_constraint_count; i < m_constraints.size(); i++) delete m_constraints[i]; m_constraints.resize(m_constraint_count); m_term_count.pop(k); for (unsigned i = m_term_count; i < m_terms.size(); i++) { #if Z3DEBUG_CHECK_UNIQUE_TERMS m_set_of_terms.erase(m_terms[i]); #endif delete m_terms[i]; } m_terms.resize(m_term_count); m_simplex_strategy.pop(k); m_settings.simplex_strategy() = m_simplex_strategy; lp_assert(sizes_are_correct()); lp_assert((!m_settings.use_tableau()) || m_mpq_lar_core_solver.m_r_solver.reduced_costs_are_correct_tableau()); set_status(lp_status::UNKNOWN); } vector lar_solver::get_all_constraint_indices() const { vector ret; constraint_index i = 0; while ( i < m_constraints.size()) ret.push_back(i++); return ret; } bool lar_solver::maximize_term_on_tableau(const lar_term & term, impq &term_max) { if (settings().simplex_strategy() == simplex_strategy_enum::undecided) decide_on_strategy_and_adjust_initial_state(); m_mpq_lar_core_solver.m_r_solver.set_status(lp_status::FEASIBLE); m_mpq_lar_core_solver.solve(); lp_status st = m_mpq_lar_core_solver.m_r_solver.get_status(); TRACE("lar_solver", tout << st << "\n";); if (st == lp_status::UNBOUNDED) { return false; } else { term_max = term.apply(m_mpq_lar_core_solver.m_r_x); return true; } } bool lar_solver::costs_are_zeros_for_r_solver() const { for (unsigned j = 0; j < m_mpq_lar_core_solver.m_r_solver.m_costs.size(); j++) { lp_assert(is_zero(m_mpq_lar_core_solver.m_r_solver.m_costs[j])); } return true; } bool lar_solver::reduced_costs_are_zeroes_for_r_solver() const { for (unsigned j = 0; j < m_mpq_lar_core_solver.m_r_solver.m_d.size(); j++) { lp_assert(is_zero(m_mpq_lar_core_solver.m_r_solver.m_d[j])); } return true; } void lar_solver::set_costs_to_zero(const lar_term& term) { auto & rslv = m_mpq_lar_core_solver.m_r_solver; auto & jset = m_mpq_lar_core_solver.m_r_solver.m_inf_set; // hijack this set that should be empty right now lp_assert(jset.m_index.size()==0); for (const auto & p : term) { unsigned j = p.var(); rslv.m_costs[j] = zero_of_type(); int i = rslv.m_basis_heading[j]; if (i < 0) jset.insert(j); else { for (const auto & rc : A_r().m_rows[i]) jset.insert(rc.var()); } } for (unsigned j : jset.m_index) rslv.m_d[j] = zero_of_type(); jset.clear(); lp_assert(reduced_costs_are_zeroes_for_r_solver()); lp_assert(costs_are_zeros_for_r_solver()); } void lar_solver::prepare_costs_for_r_solver(const lar_term & term) { TRACE("lar_solver", print_term(term, tout << "prepare: ") << "\n";); if (move_non_basic_columns_to_bounds()) find_feasible_solution(); auto & rslv = m_mpq_lar_core_solver.m_r_solver; rslv.m_using_infeas_costs = false; lp_assert(costs_are_zeros_for_r_solver()); lp_assert(reduced_costs_are_zeroes_for_r_solver()); rslv.m_costs.resize(A_r().column_count(), zero_of_type()); for (const auto & p : term) { unsigned j = p.var(); rslv.m_costs[j] = p.coeff(); if (rslv.m_basis_heading[j] < 0) rslv.m_d[j] += p.coeff(); else rslv.update_reduced_cost_for_basic_column_cost_change(- p.coeff(), j); } lp_assert(rslv.reduced_costs_are_correct_tableau()); } bool lar_solver::move_non_basic_columns_to_bounds() { auto & lcs = m_mpq_lar_core_solver; bool change = false; for (unsigned j : lcs.m_r_nbasis) { if (move_non_basic_column_to_bounds(j)) change = true; } if (settings().simplex_strategy() == simplex_strategy_enum::tableau_costs) update_x_and_inf_costs_for_columns_with_changed_bounds_tableau(); return change; } bool lar_solver::move_non_basic_column_to_bounds(unsigned j) { auto & lcs = m_mpq_lar_core_solver; auto & val = lcs.m_r_x[j]; switch (lcs.m_column_types()[j]) { case column_type::boxed: if (val != lcs.m_r_lower_bounds()[j] && val != lcs.m_r_upper_bounds()[j]) { if (m_settings.random_next() % 2 == 0) set_value_for_nbasic_column(j, lcs.m_r_lower_bounds()[j]); else set_value_for_nbasic_column(j, lcs.m_r_upper_bounds()[j]); return true; } break; case column_type::lower_bound: if (val != lcs.m_r_lower_bounds()[j]) { set_value_for_nbasic_column(j, lcs.m_r_lower_bounds()[j]); return true; } break; case column_type::upper_bound: if (val != lcs.m_r_upper_bounds()[j]) { set_value_for_nbasic_column(j, lcs.m_r_upper_bounds()[j]); return true; } break; default: if (column_is_int(j) && !val.is_int()) { set_value_for_nbasic_column(j, impq(floor(val))); return true; } break; } return false; } void lar_solver::set_value_for_nbasic_column(unsigned j, const impq & new_val) { lp_assert(!is_base(j)); auto & x = m_mpq_lar_core_solver.m_r_x[j]; auto delta = new_val - x; x = new_val; change_basic_columns_dependend_on_a_given_nb_column(j, delta); } bool lar_solver::maximize_term_on_corrected_r_solver(lar_term & term, impq &term_max) { settings().backup_costs = false; bool ret = false; TRACE("lar_solver", print_term(term, tout << "maximize: ") << "\n"; print_constraints(tout); tout << ", strategy = " << (int)settings().simplex_strategy() << "\n";); switch (settings().simplex_strategy()) { case simplex_strategy_enum::tableau_rows: prepare_costs_for_r_solver(term); settings().simplex_strategy() = simplex_strategy_enum::tableau_costs; ret = maximize_term_on_tableau(term, term_max); settings().simplex_strategy() = simplex_strategy_enum::tableau_rows; set_costs_to_zero(term); m_mpq_lar_core_solver.m_r_solver.set_status(lp_status::OPTIMAL); return ret; case simplex_strategy_enum::tableau_costs: prepare_costs_for_r_solver(term); ret = maximize_term_on_tableau(term, term_max); set_costs_to_zero(term); m_mpq_lar_core_solver.m_r_solver.set_status(lp_status::OPTIMAL); return ret; case simplex_strategy_enum::lu: lp_assert(false); // not implemented return false; default: lp_unreachable(); // wrong mode } return false; } bool lar_solver::remove_from_basis(unsigned j) { return m_mpq_lar_core_solver.m_r_solver.remove_from_basis(j); } lar_term lar_solver::get_term_to_maximize(unsigned j_or_term) const { if (is_term(j_or_term)) { return get_term(j_or_term); } if (j_or_term < m_mpq_lar_core_solver.m_r_x.size()) { lar_term r; r.add_monomial(one_of_type(), j_or_term); return r; } return lar_term(); // return an empty term } lp_status lar_solver::maximize_term(unsigned j_or_term, impq &term_max) { TRACE("lar_solver", print_values(tout);); bool was_feasible = m_mpq_lar_core_solver.m_r_solver.calc_current_x_is_feasible_include_non_basis(); impq prev_value; lar_term term = get_term_to_maximize(j_or_term); if (term.is_empty()) { return lp_status::UNBOUNDED; } auto backup = m_mpq_lar_core_solver.m_r_x; if (was_feasible) { prev_value = term.apply(m_mpq_lar_core_solver.m_r_x); } m_mpq_lar_core_solver.m_r_solver.m_look_for_feasible_solution_only = false; if (!maximize_term_on_corrected_r_solver(term, term_max)) { m_mpq_lar_core_solver.m_r_x = backup; return lp_status::UNBOUNDED; } impq opt_val = term_max; bool change = false; for (unsigned j = 0; j < m_mpq_lar_core_solver.m_r_x.size(); j++) { if (!column_is_int(j)) continue; if (column_value_is_integer(j)) continue; if (m_int_solver->is_base(j)) { if (!remove_from_basis(j)) { // consider a special version of remove_from_basis that would not remove inf_int columns m_mpq_lar_core_solver.m_r_x = backup; term_max = prev_value; return lp_status::FEASIBLE; // it should not happen } } m_int_solver->patch_nbasic_column(j, false); if (!column_value_is_integer(j)) { term_max = prev_value; m_mpq_lar_core_solver.m_r_x = backup; return lp_status::FEASIBLE; } change = true; } if (change) { term_max = term.apply(m_mpq_lar_core_solver.m_r_x); } if (was_feasible && term_max < prev_value) { term_max = prev_value; m_mpq_lar_core_solver.m_r_x = backup; } TRACE("lar_solver", print_values(tout);); if (term_max == opt_val) { set_status(lp_status::OPTIMAL); return lp_status::OPTIMAL; } return lp_status::FEASIBLE; } const lar_term & lar_solver::get_term(unsigned j) const { lp_assert(j >= m_terms_start_index); return *m_terms[j - m_terms_start_index]; } void lar_solver::pop_core_solver_params() { pop_core_solver_params(1); } void lar_solver::pop_core_solver_params(unsigned k) { A_r().pop(k); A_d().pop(k); } void lar_solver::set_upper_bound_witness(var_index j, constraint_index ci) { ul_pair ul = m_columns_to_ul_pairs[j]; ul.upper_bound_witness() = ci; m_columns_to_ul_pairs[j] = ul; } void lar_solver::set_lower_bound_witness(var_index j, constraint_index ci) { ul_pair ul = m_columns_to_ul_pairs[j]; ul.lower_bound_witness() = ci; m_columns_to_ul_pairs[j] = ul; } void lar_solver::register_monoid_in_map(std::unordered_map & coeffs, const mpq & a, unsigned j) { auto it = coeffs.find(j); if (it == coeffs.end()) { coeffs[j] = a; } else { it->second += a; } } void lar_solver::substitute_terms_in_linear_expression(const vector>& left_side_with_terms, vector> &left_side) const { std::unordered_map coeffs; for (auto & t : left_side_with_terms) { unsigned j = t.second; if (!is_term(j)) { register_monoid_in_map(coeffs, t.first, j); } else { const lar_term & term = * m_terms[adjust_term_index(t.second)]; for (auto & p : term.coeffs()){ register_monoid_in_map(coeffs, t.first * p.second , p.first); } } } for (auto & p : coeffs) if (!is_zero(p.second)) left_side.push_back(std::make_pair(p.second, p.first)); } void lar_solver::detect_rows_of_bound_change_column_for_nbasic_column(unsigned j) { if (A_r().row_count() != m_column_buffer.data_size()) m_column_buffer.resize(A_r().row_count()); else m_column_buffer.clear(); lp_assert(m_column_buffer.size() == 0 && m_column_buffer.is_OK()); m_mpq_lar_core_solver.m_r_solver.solve_Bd(j, m_column_buffer); for (unsigned i : m_column_buffer.m_index) m_rows_with_changed_bounds.insert(i); } void lar_solver::detect_rows_of_bound_change_column_for_nbasic_column_tableau(unsigned j) { for (auto & rc : m_mpq_lar_core_solver.m_r_A.m_columns[j]) m_rows_with_changed_bounds.insert(rc.var()); } bool lar_solver::use_tableau() const { return m_settings.use_tableau(); } bool lar_solver::use_tableau_costs() const { return m_settings.simplex_strategy() == simplex_strategy_enum::tableau_costs; } void lar_solver::detect_rows_of_column_with_bound_change(unsigned j) { if (m_mpq_lar_core_solver.m_r_heading[j] >= 0) { // it is a basic column // just mark the row at touched and exit m_rows_with_changed_bounds.insert(m_mpq_lar_core_solver.m_r_heading[j]); return; } if (use_tableau()) detect_rows_of_bound_change_column_for_nbasic_column_tableau(j); else detect_rows_of_bound_change_column_for_nbasic_column(j); } void lar_solver::adjust_x_of_column(unsigned j) { lp_assert(false); } bool lar_solver::row_is_correct(unsigned i) const { numeric_pair r = zero_of_type>(); for (const auto & c : A_r().m_rows[i]) { r += c.coeff() * m_mpq_lar_core_solver.m_r_x[c.var()]; } return is_zero(r); } bool lar_solver::ax_is_correct() const { for (unsigned i = 0; i < A_r().row_count(); i++) { if (!row_is_correct(i)) return false; } return true; } bool lar_solver::tableau_with_costs() const { return m_settings.simplex_strategy() == simplex_strategy_enum::tableau_costs; } bool lar_solver::costs_are_used() const { return m_settings.simplex_strategy() != simplex_strategy_enum::tableau_rows; } void lar_solver::change_basic_columns_dependend_on_a_given_nb_column(unsigned j, const numeric_pair & delta) { if (use_tableau()) { for (const auto & c : A_r().m_columns[j]) { unsigned bj = m_mpq_lar_core_solver.m_r_basis[c.var()]; if (tableau_with_costs()) { m_basic_columns_with_changed_cost.insert(bj); } m_mpq_lar_core_solver.m_r_solver.update_x_with_delta_and_track_feasibility(bj, - A_r().get_val(c) * delta); TRACE("change_x_del", tout << "changed basis column " << bj << ", it is " << ( m_mpq_lar_core_solver.m_r_solver.column_is_feasible(bj)? "feas":"inf") << std::endl;); } } else { m_column_buffer.clear(); m_column_buffer.resize(A_r().row_count()); m_mpq_lar_core_solver.m_r_solver.solve_Bd(j, m_column_buffer); for (unsigned i : m_column_buffer.m_index) { unsigned bj = m_mpq_lar_core_solver.m_r_basis[i]; m_mpq_lar_core_solver.m_r_solver.update_x_with_delta_and_track_feasibility(bj, -m_column_buffer[i] * delta); } } } void lar_solver::update_x_and_inf_costs_for_column_with_changed_bounds(unsigned j) { if (m_mpq_lar_core_solver.m_r_heading[j] >= 0) { if (costs_are_used()) { bool was_infeas = m_mpq_lar_core_solver.m_r_solver.m_inf_set.contains(j); m_mpq_lar_core_solver.m_r_solver.track_column_feasibility(j); if (was_infeas != m_mpq_lar_core_solver.m_r_solver.m_inf_set.contains(j)) m_basic_columns_with_changed_cost.insert(j); } else { m_mpq_lar_core_solver.m_r_solver.track_column_feasibility(j); } } else { numeric_pair delta; if (m_mpq_lar_core_solver.m_r_solver.make_column_feasible(j, delta)) change_basic_columns_dependend_on_a_given_nb_column(j, delta); } } void lar_solver::detect_rows_with_changed_bounds_for_column(unsigned j) { if (m_mpq_lar_core_solver.m_r_heading[j] >= 0) { m_rows_with_changed_bounds.insert(m_mpq_lar_core_solver.m_r_heading[j]); return; } if (use_tableau()) detect_rows_of_bound_change_column_for_nbasic_column_tableau(j); else detect_rows_of_bound_change_column_for_nbasic_column(j); } void lar_solver::detect_rows_with_changed_bounds() { for (auto j : m_columns_with_changed_bound.m_index) detect_rows_with_changed_bounds_for_column(j); } void lar_solver::update_x_and_inf_costs_for_columns_with_changed_bounds() { for (auto j : m_columns_with_changed_bound.m_index) update_x_and_inf_costs_for_column_with_changed_bounds(j); } void lar_solver::update_x_and_inf_costs_for_columns_with_changed_bounds_tableau() { for (auto j : m_columns_with_changed_bound.m_index) update_x_and_inf_costs_for_column_with_changed_bounds(j); if (tableau_with_costs()) { for (unsigned j : m_basic_columns_with_changed_cost.m_index) m_mpq_lar_core_solver.m_r_solver.update_inf_cost_for_column_tableau(j); lp_assert(m_mpq_lar_core_solver.m_r_solver.reduced_costs_are_correct_tableau()); } } void lar_solver::solve_with_core_solver() { if (!use_tableau()) add_last_rows_to_lu(m_mpq_lar_core_solver.m_r_solver); if (m_mpq_lar_core_solver.need_to_presolve_with_double_solver()) { add_last_rows_to_lu(m_mpq_lar_core_solver.m_d_solver); } m_mpq_lar_core_solver.prefix_r(); if (costs_are_used()) { m_basic_columns_with_changed_cost.clear(); m_basic_columns_with_changed_cost.resize(m_mpq_lar_core_solver.m_r_x.size()); } if (use_tableau()) update_x_and_inf_costs_for_columns_with_changed_bounds_tableau(); else update_x_and_inf_costs_for_columns_with_changed_bounds(); m_mpq_lar_core_solver.solve(); set_status(m_mpq_lar_core_solver.m_r_solver.get_status()); lp_assert((m_settings.random_next() % 100) != 0 || m_status != lp_status::OPTIMAL || all_constraints_hold()); } numeric_pair lar_solver::get_basic_var_value_from_row_directly(unsigned i) { numeric_pair r = zero_of_type>(); unsigned bj = m_mpq_lar_core_solver.m_r_solver.m_basis[i]; for (const auto & c: A_r().m_rows[i]) { if (c.var() == bj) continue; const auto & x = m_mpq_lar_core_solver.m_r_x[c.var()]; if (!is_zero(x)) r -= c.coeff() * x; } return r; } numeric_pair lar_solver::get_basic_var_value_from_row(unsigned i) { if (settings().use_tableau()) { return get_basic_var_value_from_row_directly(i); } numeric_pair r = zero_of_type>(); m_mpq_lar_core_solver.calculate_pivot_row(i); for (unsigned j : m_mpq_lar_core_solver.m_r_solver.m_pivot_row.m_index) { lp_assert(m_mpq_lar_core_solver.m_r_solver.m_basis_heading[j] < 0); r -= m_mpq_lar_core_solver.m_r_solver.m_pivot_row.m_data[j] * m_mpq_lar_core_solver.m_r_x[j]; } return r; } template void lar_solver::add_last_rows_to_lu(lp_primal_core_solver & s) { auto & f = s.m_factorization; if (f != nullptr) { auto columns_to_replace = f->get_set_of_columns_to_replace_for_add_last_rows(s.m_basis_heading); if (f->m_refactor_counter + columns_to_replace.size() >= 200 || f->has_dense_submatrix()) { delete f; f = nullptr; } else { f->add_last_rows_to_B(s.m_basis_heading, columns_to_replace); } } if (f == nullptr) { init_factorization(f, s.m_A, s.m_basis, m_settings); if (f->get_status() != LU_status::OK) { delete f; f = nullptr; } } } bool lar_solver::x_is_correct() const { if (m_mpq_lar_core_solver.m_r_x.size() != A_r().column_count()) { return false; } for (unsigned i = 0; i < A_r().row_count(); i++) { numeric_pair delta = A_r().dot_product_with_row(i, m_mpq_lar_core_solver.m_r_x); if (!delta.is_zero()) { return false; } } return true;; } bool lar_solver::var_is_registered(var_index vj) const { if (vj >= m_terms_start_index) { if (vj - m_terms_start_index >= m_terms.size()) return false; } else if ( vj >= A_r().column_count()) { return false; } return true; } unsigned lar_solver::constraint_stack_size() const { return m_constraint_count.stack_size(); } void lar_solver::fill_last_row_of_A_r(static_matrix> & A, const lar_term * ls) { lp_assert(A.row_count() > 0); lp_assert(A.column_count() > 0); unsigned last_row = A.row_count() - 1; lp_assert(A.m_rows[last_row].size() == 0); for (auto & t : ls->m_coeffs) { lp_assert(!is_zero(t.second)); var_index j = t.first; A.set(last_row, j, - t.second); } unsigned basis_j = A.column_count() - 1; A.set(last_row, basis_j, mpq(1)); } template void lar_solver::create_matrix_A(static_matrix & matr) { lp_assert(false); // not implemented /* unsigned m = number_or_nontrivial_left_sides(); unsigned n = m_vec_of_canonic_left_sides.size(); if (matr.row_count() == m && matr.column_count() == n) return; matr.init_empty_matrix(m, n); copy_from_mpq_matrix(matr); */ } template void lar_solver::copy_from_mpq_matrix(static_matrix & matr) { matr.m_rows.resize(A_r().row_count()); matr.m_columns.resize(A_r().column_count()); for (unsigned i = 0; i < matr.row_count(); i++) { for (auto & it : A_r().m_rows[i]) { matr.set(i, it.var(), convert_struct::convert(it.get_val())); } } } bool lar_solver::try_to_set_fixed(column_info & ci) { if (ci.upper_bound_is_set() && ci.lower_bound_is_set() && ci.get_upper_bound() == ci.get_lower_bound() && !ci.is_fixed()) { ci.set_fixed_value(ci.get_upper_bound()); return true; } return false; } column_type lar_solver::get_column_type(unsigned j) const{ return m_mpq_lar_core_solver.m_column_types[j]; } std::string lar_solver::get_column_name(unsigned j) const { if (j >= m_terms_start_index) return std::string("_t") + T_to_string(j); if (j >= m_var_register.size()) return std::string("_s") + T_to_string(j); return std::string("v") + T_to_string(m_var_register.local_to_external(j)); } bool lar_solver::all_constrained_variables_are_registered(const vector>& left_side) { for (auto it : left_side) { if (! var_is_registered(it.second)) return false; } return true; } bool lar_solver::all_constraints_hold() const { if (m_settings.get_cancel_flag()) return true; std::unordered_map var_map; get_model_do_not_care_about_diff_vars(var_map); for (unsigned i = 0; i < m_constraints.size(); i++) { if (!constraint_holds(*m_constraints[i], var_map)) { return false; } } return true; } bool lar_solver::constraint_holds(const lar_base_constraint & constr, std::unordered_map & var_map) const { mpq left_side_val = get_left_side_val(constr, var_map); switch (constr.m_kind) { case LE: return left_side_val <= constr.m_right_side; case LT: return left_side_val < constr.m_right_side; case GE: return left_side_val >= constr.m_right_side; case GT: return left_side_val > constr.m_right_side; case EQ: return left_side_val == constr.m_right_side; default: lp_unreachable(); } return false; // it is unreachable } bool lar_solver::the_relations_are_of_same_type(const vector> & evidence, lconstraint_kind & the_kind_of_sum) const { unsigned n_of_G = 0, n_of_L = 0; bool strict = false; for (auto & it : evidence) { mpq coeff = it.first; constraint_index con_ind = it.second; lconstraint_kind kind = coeff.is_pos() ? m_constraints[con_ind]->m_kind : flip_kind(m_constraints[con_ind]->m_kind); if (kind == GT || kind == LT) strict = true; if (kind == GE || kind == GT) n_of_G++; else if (kind == LE || kind == LT) n_of_L++; } the_kind_of_sum = n_of_G ? GE : (n_of_L ? LE : EQ); if (strict) the_kind_of_sum = static_cast((static_cast(the_kind_of_sum) / 2)); return n_of_G == 0 || n_of_L == 0; } void lar_solver::register_in_map(std::unordered_map & coeffs, const lar_base_constraint & cn, const mpq & a) { for (auto & it : cn.get_left_side_coefficients()) { unsigned j = it.second; auto p = coeffs.find(j); if (p == coeffs.end()) coeffs[j] = it.first * a; else { p->second += it.first * a; if (p->second.is_zero()) coeffs.erase(p); } } } bool lar_solver::the_left_sides_sum_to_zero(const vector> & evidence) const { std::unordered_map coeff_map; for (auto & it : evidence) { mpq coeff = it.first; constraint_index con_ind = it.second; lp_assert(con_ind < m_constraints.size()); register_in_map(coeff_map, *m_constraints[con_ind], coeff); } if (!coeff_map.empty()) { return false; } return true; } bool lar_solver::the_right_sides_do_not_sum_to_zero(const vector> & evidence) { mpq ret = numeric_traits::zero(); for (auto & it : evidence) { mpq coeff = it.first; constraint_index con_ind = it.second; lp_assert(con_ind < m_constraints.size()); const lar_constraint & constr = *m_constraints[con_ind]; ret += constr.m_right_side * coeff; } return !numeric_traits::is_zero(ret); } bool lar_solver::explanation_is_correct(const vector>& explanation) const { return true; #if 0 // disabled: kind is uninitialized #ifdef Z3DEBUG lconstraint_kind kind; lp_assert(the_left_sides_sum_to_zero(explanation)); mpq rs = sum_of_right_sides_of_explanation(explanation); switch (kind) { case LE: lp_assert(rs < zero_of_type()); break; case LT: lp_assert(rs <= zero_of_type()); break; case GE: lp_assert(rs > zero_of_type()); break; case GT: lp_assert(rs >= zero_of_type()); break; case EQ: lp_assert(rs != zero_of_type()); break; default: lp_assert(false); return false; } #endif #endif return true; } bool lar_solver::inf_explanation_is_correct() const { #ifdef Z3DEBUG vector> explanation; get_infeasibility_explanation(explanation); return explanation_is_correct(explanation); #endif return true; } mpq lar_solver::sum_of_right_sides_of_explanation(const vector> & explanation) const { mpq ret = numeric_traits::zero(); for (auto & it : explanation) { mpq coeff = it.first; constraint_index con_ind = it.second; lp_assert(con_ind < m_constraints.size()); ret += (m_constraints[con_ind]->m_right_side - m_constraints[con_ind]->get_free_coeff_of_left_side()) * coeff; } return ret; } bool lar_solver::has_lower_bound(var_index var, constraint_index& ci, mpq& value, bool& is_strict) const { if (var >= m_columns_to_ul_pairs.size()) { // TBD: bounds on terms could also be used, caller may have to track these. return false; } const ul_pair & ul = m_columns_to_ul_pairs[var]; ci = ul.lower_bound_witness(); if (ci != static_cast(-1)) { auto& p = m_mpq_lar_core_solver.m_r_lower_bounds()[var]; value = p.x; is_strict = p.y.is_pos(); return true; } else { return false; } } bool lar_solver::has_upper_bound(var_index var, constraint_index& ci, mpq& value, bool& is_strict) const { if (var >= m_columns_to_ul_pairs.size()) { // TBD: bounds on terms could also be used, caller may have to track these. return false; } const ul_pair & ul = m_columns_to_ul_pairs[var]; ci = ul.upper_bound_witness(); if (ci != static_cast(-1)) { auto& p = m_mpq_lar_core_solver.m_r_upper_bounds()[var]; value = p.x; is_strict = p.y.is_neg(); return true; } else { return false; } } bool lar_solver::has_value(var_index var, mpq& value) const { if (is_term(var)) { lar_term const& t = get_term(var); value = 0; for (auto const& cv : t) { impq const& r = get_column_value(cv.var()); if (!numeric_traits::is_zero(r.y)) return false; value += r.x * cv.coeff(); } return true; } else { impq const& r = get_column_value(var); value = r.x; return numeric_traits::is_zero(r.y); } } void lar_solver::get_infeasibility_explanation(vector> & explanation) const { explanation.clear(); if (m_infeasible_column_index != -1) { fill_explanation_from_infeasible_column(explanation); return; } if (m_mpq_lar_core_solver.get_infeasible_sum_sign() == 0) { return; } // the infeasibility sign int inf_sign; auto inf_row = m_mpq_lar_core_solver.get_infeasibility_info(inf_sign); get_infeasibility_explanation_for_inf_sign(explanation, inf_row, inf_sign); lp_assert(explanation_is_correct(explanation)); } void lar_solver::get_infeasibility_explanation_for_inf_sign( vector> & explanation, const vector> & inf_row, int inf_sign) const { for (auto & it : inf_row) { mpq coeff = it.first; unsigned j = it.second; int adj_sign = coeff.is_pos() ? inf_sign : -inf_sign; const ul_pair & ul = m_columns_to_ul_pairs[j]; constraint_index bound_constr_i = adj_sign < 0 ? ul.upper_bound_witness() : ul.lower_bound_witness(); lp_assert(bound_constr_i < m_constraints.size()); explanation.push_back(std::make_pair(coeff, bound_constr_i)); } } void lar_solver::get_model(std::unordered_map & variable_values) const { lp_assert(m_mpq_lar_core_solver.m_r_solver.calc_current_x_is_feasible_include_non_basis()); mpq delta = mpq(1, 2); // start from 0.5 to have less clashes unsigned i; do { // different pairs have to produce different singleton values std::unordered_set set_of_different_pairs; std::unordered_set set_of_different_singles; delta = m_mpq_lar_core_solver.find_delta_for_strict_bounds(delta); TRACE("get_model", tout << "delta=" << delta << "size = " << m_mpq_lar_core_solver.m_r_x.size() << std::endl;); for (i = 0; i < m_mpq_lar_core_solver.m_r_x.size(); i++ ) { const numeric_pair & rp = m_mpq_lar_core_solver.m_r_x[i]; set_of_different_pairs.insert(rp); mpq x = rp.x + delta * rp.y; set_of_different_singles.insert(x); if (set_of_different_pairs.size() != set_of_different_singles.size()) { delta /= mpq(2); break; } TRACE("get_model", tout << get_column_name(i) << " := " << x << "\n";); variable_values[i] = x; } } while (i != m_mpq_lar_core_solver.m_r_x.size()); } void lar_solver::get_model_do_not_care_about_diff_vars(std::unordered_map & variable_values) const { mpq delta = mpq(1); delta = m_mpq_lar_core_solver.find_delta_for_strict_bounds(delta); for (unsigned i = 0; i < m_mpq_lar_core_solver.m_r_x.size(); i++ ) { const impq & rp = m_mpq_lar_core_solver.m_r_x[i]; variable_values[i] = rp.x + delta * rp.y; } } std::string lar_solver::get_variable_name(var_index vi) const { return get_column_name(vi); } // ********** print region start std::ostream& lar_solver::print_constraint(constraint_index ci, std::ostream & out) const { if (ci >= m_constraints.size()) { out << "constraint " << T_to_string(ci) << " is not found"; out << std::endl; return out; } return print_constraint(m_constraints[ci], out); } std::ostream& lar_solver::print_constraints(std::ostream& out) const { out << "number of constraints = " << m_constraints.size() << std::endl; for (auto c : m_constraints) { print_constraint(c, out); } return out; } std::ostream& lar_solver::print_terms(std::ostream& out) const { for (auto it : m_terms) { print_term(*it, out) << "\n"; } return out; } std::ostream& lar_solver::print_left_side_of_constraint(const lar_base_constraint * c, std::ostream & out) const { print_linear_combination_of_column_indices(c->get_left_side_coefficients(), out); mpq free_coeff = c->get_free_coeff_of_left_side(); if (!is_zero(free_coeff)) out << " + " << free_coeff; return out; } std::ostream& lar_solver::print_term(lar_term const& term, std::ostream & out) const { bool first = true; for (const auto p : term) { mpq val = p.coeff(); if (first) { first = false; } else { if (is_pos(val)) { out << " + "; } else { out << " - "; val = -val; } } if (val == -numeric_traits::one()) out << " - "; else if (val != numeric_traits::one()) out << T_to_string(val); out << this->get_column_name(p.var()); } return out; } std::ostream& lar_solver::print_term_as_indices(lar_term const& term, std::ostream & out) const { print_linear_combination_of_column_indices_only(term, out); return out; } mpq lar_solver::get_left_side_val(const lar_base_constraint & cns, const std::unordered_map & var_map) const { mpq ret = cns.get_free_coeff_of_left_side(); for (auto & it : cns.get_left_side_coefficients()) { var_index j = it.second; auto vi = var_map.find(j); lp_assert(vi != var_map.end()); ret += it.first * vi->second; } return ret; } std::ostream& lar_solver::print_constraint(const lar_base_constraint * c, std::ostream & out) const { print_left_side_of_constraint(c, out); out << " " << lconstraint_kind_string(c->m_kind) << " " << c->m_right_side << std::endl; return out; } void lar_solver::fill_var_set_for_random_update(unsigned sz, var_index const * vars, vector& column_list) { for (unsigned i = 0; i < sz; i++) { var_index var = vars[i]; if (var >= m_terms_start_index) { // handle the term for (auto & it : m_terms[var - m_terms_start_index]->m_coeffs) { column_list.push_back(it.first); } } else { column_list.push_back(var); } } } void lar_solver::random_update(unsigned sz, var_index const * vars) { vector column_list; fill_var_set_for_random_update(sz, vars, column_list); random_updater ru(*this, column_list); ru.update(); } void lar_solver::pivot_fixed_vars_from_basis() { m_mpq_lar_core_solver.m_r_solver.pivot_fixed_vars_from_basis(); } void lar_solver::pop() { pop(1); } bool lar_solver::column_represents_row_in_tableau(unsigned j) { return m_columns_to_ul_pairs()[j].m_i != static_cast(-1); } void lar_solver::make_sure_that_the_bottom_right_elem_not_zero_in_tableau(unsigned i, unsigned j) { // i, j - is the indices of the bottom-right element of the tableau lp_assert(A_r().row_count() == i + 1 && A_r().column_count() == j + 1); auto & last_column = A_r().m_columns[j]; int non_zero_column_cell_index = -1; for (unsigned k = last_column.size(); k-- > 0;){ auto & cc = last_column[k]; if (cc.var() == i) return; non_zero_column_cell_index = k; } lp_assert(non_zero_column_cell_index != -1); lp_assert(static_cast(non_zero_column_cell_index) != i); m_mpq_lar_core_solver.m_r_solver.transpose_rows_tableau(last_column[non_zero_column_cell_index].var(), i); } void lar_solver::remove_last_row_and_column_from_tableau(unsigned j) { lp_assert(A_r().column_count() == m_mpq_lar_core_solver.m_r_solver.m_costs.size()); auto & slv = m_mpq_lar_core_solver.m_r_solver; unsigned i = A_r().row_count() - 1; //last row index make_sure_that_the_bottom_right_elem_not_zero_in_tableau(i, j); if (slv.m_basis_heading[j] < 0) { slv.pivot_column_tableau(j, i); } auto & last_row = A_r().m_rows[i]; mpq &cost_j = m_mpq_lar_core_solver.m_r_solver.m_costs[j]; bool cost_is_nz = !is_zero(cost_j); for (unsigned k = last_row.size(); k-- > 0;) { auto &rc = last_row[k]; if (cost_is_nz) { m_mpq_lar_core_solver.m_r_solver.m_d[rc.var()] += cost_j*rc.get_val(); } A_r().remove_element(last_row, rc); } lp_assert(last_row.size() == 0); lp_assert(A_r().m_columns[j].size() == 0); A_r().m_rows.pop_back(); A_r().m_columns.pop_back(); CASSERT("check_static_matrix", A_r().is_correct()); slv.m_b.pop_back(); } void lar_solver::remove_last_column_from_A() { // the last column has to be empty lp_assert(A_r().m_columns.back().size() == 0); A_r().m_columns.pop_back(); } void lar_solver::remove_last_column_from_basis_tableau(unsigned j) { auto& rslv = m_mpq_lar_core_solver.m_r_solver; int i = rslv.m_basis_heading[j]; if (i >= 0) { // j is a basic var int last_pos = static_cast(rslv.m_basis.size()) - 1; lp_assert(last_pos >= 0); if (i != last_pos) { unsigned j_at_last_pos = rslv.m_basis[last_pos]; rslv.m_basis[i] = j_at_last_pos; rslv.m_basis_heading[j_at_last_pos] = i; } rslv.m_basis.pop_back(); // remove j from the basis } else { int last_pos = static_cast(rslv.m_nbasis.size()) - 1; lp_assert(last_pos >= 0); i = - 1 - i; if (i != last_pos) { unsigned j_at_last_pos = rslv.m_nbasis[last_pos]; rslv.m_nbasis[i] = j_at_last_pos; rslv.m_basis_heading[j_at_last_pos] = - i - 1; } rslv.m_nbasis.pop_back(); // remove j from the basis } rslv.m_basis_heading.pop_back(); lp_assert(rslv.m_basis.size() == A_r().row_count()); lp_assert(rslv.basis_heading_is_correct()); } void lar_solver::remove_last_column_from_tableau() { auto& rslv = m_mpq_lar_core_solver.m_r_solver; unsigned j = A_r().column_count() - 1; lp_assert(A_r().column_count() == m_mpq_lar_core_solver.m_r_solver.m_costs.size()); if (column_represents_row_in_tableau(j)) { remove_last_row_and_column_from_tableau(j); if (rslv.m_basis_heading[j] < 0) rslv.change_basis_unconditionally(j, rslv.m_basis[A_r().row_count()]); // A_r().row_count() is the index of the last row in the basis still } else { remove_last_column_from_A(); } rslv.m_x.pop_back(); rslv.m_d.pop_back(); rslv.m_costs.pop_back(); remove_last_column_from_basis_tableau(j); lp_assert(m_mpq_lar_core_solver.r_basis_is_OK()); lp_assert(A_r().column_count() == m_mpq_lar_core_solver.m_r_solver.m_costs.size()); } void lar_solver::pop_tableau() { lp_assert(m_mpq_lar_core_solver.m_r_solver.m_costs.size() == A_r().column_count()); lp_assert(m_mpq_lar_core_solver.m_r_solver.m_basis.size() == A_r().row_count()); lp_assert(m_mpq_lar_core_solver.m_r_solver.basis_heading_is_correct()); // We remove last variables starting from m_column_names.size() to m_vec_of_canonic_left_sides.size(). // At this moment m_column_names is already popped unsigned size = m_var_register.size(); while (A_r().column_count() > size) remove_last_column_from_tableau(); lp_assert(m_mpq_lar_core_solver.m_r_solver.m_costs.size() == A_r().column_count()); lp_assert(m_mpq_lar_core_solver.m_r_solver.m_basis.size() == A_r().row_count()); lp_assert(m_mpq_lar_core_solver.m_r_solver.basis_heading_is_correct()); } void lar_solver::clean_inf_set_of_r_solver_after_pop() { vector became_feas; clean_popped_elements(A_r().column_count(), m_mpq_lar_core_solver.m_r_solver.m_inf_set); std::unordered_set basic_columns_with_changed_cost; auto inf_index_copy = m_mpq_lar_core_solver.m_r_solver.m_inf_set.m_index; for (auto j: inf_index_copy) { if (m_mpq_lar_core_solver.m_r_heading[j] >= 0) { continue; } // some basic columns might become non-basic - these columns need to be made feasible numeric_pair delta; if (m_mpq_lar_core_solver.m_r_solver.make_column_feasible(j, delta)) { change_basic_columns_dependend_on_a_given_nb_column(j, delta); } became_feas.push_back(j); } for (unsigned j : became_feas) { lp_assert(m_mpq_lar_core_solver.m_r_solver.m_basis_heading[j] < 0); m_mpq_lar_core_solver.m_r_solver.m_d[j] -= m_mpq_lar_core_solver.m_r_solver.m_costs[j]; m_mpq_lar_core_solver.m_r_solver.m_costs[j] = zero_of_type(); m_mpq_lar_core_solver.m_r_solver.m_inf_set.erase(j); } became_feas.clear(); for (unsigned j : m_mpq_lar_core_solver.m_r_solver.m_inf_set.m_index) { lp_assert(m_mpq_lar_core_solver.m_r_heading[j] >= 0); if (m_mpq_lar_core_solver.m_r_solver.column_is_feasible(j)) became_feas.push_back(j); } for (unsigned j : became_feas) m_mpq_lar_core_solver.m_r_solver.m_inf_set.erase(j); if (use_tableau_costs()) { for (unsigned j : became_feas) m_mpq_lar_core_solver.m_r_solver.update_inf_cost_for_column_tableau(j); for (unsigned j : basic_columns_with_changed_cost) m_mpq_lar_core_solver.m_r_solver.update_inf_cost_for_column_tableau(j); lp_assert(m_mpq_lar_core_solver.m_r_solver.reduced_costs_are_correct_tableau()); } } bool lar_solver::model_is_int_feasible() const { unsigned n = A_r().column_count(); for (unsigned j = 0; j < n; j++) { if (column_is_int(j) && !column_value_is_integer(j)) return false; } return true; } bool lar_solver::term_is_int(const lar_term * t) const { for (auto const & p : t->m_coeffs) if (! (column_is_int(p.first) && p.second.is_int())) return false; return true; } bool lar_solver::var_is_int(var_index v) const { if (is_term(v)) { lar_term const& t = get_term(v); return term_is_int(&t); } else { return column_is_int(v); } } bool lar_solver::column_is_int(unsigned j) const { return m_var_register.local_is_int(j); } bool lar_solver::column_is_fixed(unsigned j) const { return m_mpq_lar_core_solver.column_is_fixed(j); } // below is the initialization functionality of lar_solver bool lar_solver::strategy_is_undecided() const { return m_settings.simplex_strategy() == simplex_strategy_enum::undecided; } var_index lar_solver::add_var(unsigned ext_j, bool is_int) { TRACE("add_var", tout << "adding var " << ext_j << (is_int? " int" : " nonint") << std::endl;); var_index local_j; lp_assert(ext_j < m_terms_start_index); if (m_var_register.external_is_used(ext_j, local_j)) return local_j; lp_assert(m_columns_to_ul_pairs.size() == A_r().column_count()); local_j = A_r().column_count(); m_columns_to_ul_pairs.push_back(ul_pair(static_cast(-1))); add_non_basic_var_to_core_fields(ext_j, is_int); lp_assert(sizes_are_correct()); return local_j; } void lar_solver::register_new_ext_var_index(unsigned ext_v, bool is_int) { lp_assert(!m_var_register.external_is_used(ext_v)); m_var_register.add_var(ext_v, is_int); } void lar_solver::add_non_basic_var_to_core_fields(unsigned ext_j, bool is_int) { register_new_ext_var_index(ext_j, is_int); m_mpq_lar_core_solver.m_column_types.push_back(column_type::free_column); m_columns_with_changed_bound.increase_size_by_one(); add_new_var_to_core_fields_for_mpq(false); if (use_lu()) add_new_var_to_core_fields_for_doubles(false); } void lar_solver::add_new_var_to_core_fields_for_doubles(bool register_in_basis) { unsigned j = A_d().column_count(); A_d().add_column(); lp_assert(m_mpq_lar_core_solver.m_d_x.size() == j); // lp_assert(m_mpq_lar_core_solver.m_d_lower_bounds.size() == j && m_mpq_lar_core_solver.m_d_upper_bounds.size() == j); // restore later m_mpq_lar_core_solver.m_d_x.resize(j + 1); m_mpq_lar_core_solver.m_d_lower_bounds.resize(j + 1); m_mpq_lar_core_solver.m_d_upper_bounds.resize(j + 1); lp_assert(m_mpq_lar_core_solver.m_d_heading.size() == j); // as A().column_count() on the entry to the method if (register_in_basis) { A_d().add_row(); m_mpq_lar_core_solver.m_d_heading.push_back(m_mpq_lar_core_solver.m_d_basis.size()); m_mpq_lar_core_solver.m_d_basis.push_back(j); } else { m_mpq_lar_core_solver.m_d_heading.push_back(-static_cast(m_mpq_lar_core_solver.m_d_nbasis.size()) - 1); m_mpq_lar_core_solver.m_d_nbasis.push_back(j); } } void lar_solver::add_new_var_to_core_fields_for_mpq(bool register_in_basis) { unsigned j = A_r().column_count(); A_r().add_column(); lp_assert(m_mpq_lar_core_solver.m_r_x.size() == j); // lp_assert(m_mpq_lar_core_solver.m_r_lower_bounds.size() == j && m_mpq_lar_core_solver.m_r_upper_bounds.size() == j); // restore later m_mpq_lar_core_solver.m_r_x.resize(j + 1); m_mpq_lar_core_solver.m_r_lower_bounds.increase_size_by_one(); m_mpq_lar_core_solver.m_r_upper_bounds.increase_size_by_one(); m_mpq_lar_core_solver.m_r_solver.m_inf_set.increase_size_by_one(); m_mpq_lar_core_solver.m_r_solver.m_costs.resize(j + 1); m_mpq_lar_core_solver.m_r_solver.m_d.resize(j + 1); lp_assert(m_mpq_lar_core_solver.m_r_heading.size() == j); // as A().column_count() on the entry to the method if (register_in_basis) { A_r().add_row(); m_mpq_lar_core_solver.m_r_heading.push_back(m_mpq_lar_core_solver.m_r_basis.size()); m_mpq_lar_core_solver.m_r_basis.push_back(j); if (m_settings.bound_propagation()) m_rows_with_changed_bounds.insert(A_r().row_count() - 1); } else { m_mpq_lar_core_solver.m_r_heading.push_back(-static_cast(m_mpq_lar_core_solver.m_r_nbasis.size()) - 1); m_mpq_lar_core_solver.m_r_nbasis.push_back(j); } } var_index lar_solver::add_term_undecided(const vector> & coeffs) { push_and_register_term(new lar_term(coeffs)); return m_terms_start_index + m_terms.size() - 1; } #if Z3DEBUG_CHECK_UNIQUE_TERMS bool lar_solver::term_coeffs_are_ok(const vector> & coeffs) { for (const auto & p : coeffs) { if (column_is_real(p.second)) return true; } mpq g; bool g_is_set = false; for (const auto & p : coeffs) { if (!p.first.is_int()) { return false; } if (!g_is_set) { g_is_set = true; g = p.first; } else { g = gcd(g, p.first); } } if (g == one_of_type()) return true; return false; } #endif void lar_solver::push_and_register_term(lar_term* t) { #if Z3DEBUG_CHECK_UNIQUE_TERMS lp_assert(m_set_of_terms.find(t) == m_set_of_terms.end()); m_set_of_terms.insert(t); #endif m_terms.push_back(t); } // terms var_index lar_solver::add_term(const vector> & coeffs) { if (strategy_is_undecided()) return add_term_undecided(coeffs); push_and_register_term(new lar_term(coeffs)); unsigned adjusted_term_index = m_terms.size() - 1; var_index ret = m_terms_start_index + adjusted_term_index; if (use_tableau() && !coeffs.empty()) { add_row_from_term_no_constraint(m_terms.back(), ret); if (m_settings.bound_propagation()) m_rows_with_changed_bounds.insert(A_r().row_count() - 1); } lp_assert(m_var_register.size() == A_r().column_count()); return ret; } void lar_solver::add_row_from_term_no_constraint(const lar_term * term, unsigned term_ext_index) { TRACE("dump_terms", print_term(*term, tout) << std::endl;); register_new_ext_var_index(term_ext_index, term_is_int(term)); // j will be a new variable unsigned j = A_r().column_count(); ul_pair ul(j); m_columns_to_ul_pairs.push_back(ul); add_basic_var_to_core_fields(); if (use_tableau()) { A_r().fill_last_row_with_pivoting(*term, j, m_mpq_lar_core_solver.m_r_solver.m_basis_heading); m_mpq_lar_core_solver.m_r_solver.m_b.resize(A_r().column_count(), zero_of_type()); } else { fill_last_row_of_A_r(A_r(), term); } m_mpq_lar_core_solver.m_r_solver.update_x_and_call_tracker(j, get_basic_var_value_from_row_directly(A_r().row_count() - 1)); if (use_lu()) fill_last_row_of_A_d(A_d(), term); } void lar_solver::add_basic_var_to_core_fields() { bool use_lu = m_mpq_lar_core_solver.need_to_presolve_with_double_solver(); lp_assert(!use_lu || A_r().column_count() == A_d().column_count()); m_mpq_lar_core_solver.m_column_types.push_back(column_type::free_column); m_columns_with_changed_bound.increase_size_by_one(); m_rows_with_changed_bounds.increase_size_by_one(); add_new_var_to_core_fields_for_mpq(true); if (use_lu) add_new_var_to_core_fields_for_doubles(true); } bool lar_solver::bound_is_integer_for_integer_column(unsigned j, const mpq & right_side) const { if (!column_is_int(j)) return true; return right_side.is_int(); } constraint_index lar_solver::add_var_bound(var_index j, lconstraint_kind kind, const mpq & right_side) { TRACE("lar_solver", tout << "j = " << j << std::endl;); constraint_index ci = m_constraints.size(); if (!is_term(j)) { // j is a var lp_assert(bound_is_integer_for_integer_column(j, right_side)); auto vc = new lar_var_constraint(j, kind, right_side); m_constraints.push_back(vc); update_column_type_and_bound(j, kind, right_side, ci); } else { add_var_bound_on_constraint_for_term(j, kind, right_side, ci); } lp_assert(sizes_are_correct()); return ci; } bool lar_solver::compare_values(var_index j, lconstraint_kind k, const mpq & rhs) { if (is_term(j)) j = to_column(j); return compare_values(get_column_value(j), k, rhs); } bool lar_solver::compare_values(impq const& lhs, lconstraint_kind k, const mpq & rhs) { switch (k) { case LT: return lhs < rhs; case LE: return lhs <= rhs; case GT: return lhs > rhs; case GE: return lhs >= rhs; case EQ: return lhs == rhs; default: UNREACHABLE(); return true; } } void lar_solver::update_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index constr_index) { switch (m_mpq_lar_core_solver.m_column_types[j]) { case column_type::free_column: update_free_column_type_and_bound(j, kind, right_side, constr_index); break; case column_type::boxed: update_boxed_column_type_and_bound(j, kind, right_side, constr_index); break; case column_type::lower_bound: update_lower_bound_column_type_and_bound(j, kind, right_side, constr_index); break; case column_type::upper_bound: update_upper_bound_column_type_and_bound(j, kind, right_side, constr_index); break; case column_type::fixed: update_fixed_column_type_and_bound(j, kind, right_side, constr_index); break; default: lp_assert(false); // cannot be here } } void lar_solver::add_var_bound_on_constraint_for_term(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci) { lp_assert(is_term(j)); unsigned adjusted_term_index = adjust_term_index(j); // lp_assert(!term_is_int(m_terms[adjusted_term_index]) || right_side.is_int()); unsigned term_j; if (m_var_register.external_is_used(j, term_j)) { m_constraints.push_back(new lar_term_constraint(m_terms[adjusted_term_index], kind, right_side)); update_column_type_and_bound(term_j, kind, right_side, ci); } else { add_constraint_from_term_and_create_new_column_row(j, m_terms[adjusted_term_index], kind, right_side); } } constraint_index lar_solver::add_constraint(const vector>& left_side_with_terms, lconstraint_kind kind_par, const mpq& right_side_parm) { vector> left_side; substitute_terms_in_linear_expression(left_side_with_terms, left_side); unsigned term_index = add_term(left_side); constraint_index ci = m_constraints.size(); add_var_bound_on_constraint_for_term(term_index, kind_par, right_side_parm, ci); return ci; } void lar_solver::add_constraint_from_term_and_create_new_column_row(unsigned term_j, const lar_term* term, lconstraint_kind kind, const mpq & right_side) { add_row_from_term_no_constraint(term, term_j); unsigned j = A_r().column_count() - 1; update_column_type_and_bound(j, kind, right_side, m_constraints.size()); m_constraints.push_back(new lar_term_constraint(term, kind, right_side)); lp_assert(A_r().column_count() == m_mpq_lar_core_solver.m_r_solver.m_costs.size()); } void lar_solver::decide_on_strategy_and_adjust_initial_state() { lp_assert(strategy_is_undecided()); if (m_columns_to_ul_pairs.size() > m_settings.column_number_threshold_for_using_lu_in_lar_solver) { m_settings.simplex_strategy() = simplex_strategy_enum::lu; } else { m_settings.simplex_strategy() = simplex_strategy_enum::tableau_rows; // todo: when to switch to tableau_costs? } adjust_initial_state(); } void lar_solver::adjust_initial_state() { switch (m_settings.simplex_strategy()) { case simplex_strategy_enum::lu: adjust_initial_state_for_lu(); break; case simplex_strategy_enum::tableau_rows: adjust_initial_state_for_tableau_rows(); break; case simplex_strategy_enum::tableau_costs: lp_assert(false); // not implemented case simplex_strategy_enum::undecided: adjust_initial_state_for_tableau_rows(); break; } } void lar_solver::adjust_initial_state_for_lu() { copy_from_mpq_matrix(A_d()); unsigned n = A_d().column_count(); m_mpq_lar_core_solver.m_d_x.resize(n); m_mpq_lar_core_solver.m_d_lower_bounds.resize(n); m_mpq_lar_core_solver.m_d_upper_bounds.resize(n); m_mpq_lar_core_solver.m_d_heading = m_mpq_lar_core_solver.m_r_heading; m_mpq_lar_core_solver.m_d_basis = m_mpq_lar_core_solver.m_r_basis; /* unsigned j = A_d().column_count(); A_d().add_column(); lp_assert(m_mpq_lar_core_solver.m_d_x.size() == j); // lp_assert(m_mpq_lar_core_solver.m_d_lower_bounds.size() == j && m_mpq_lar_core_solver.m_d_upper_bounds.size() == j); // restore later m_mpq_lar_core_solver.m_d_x.resize(j + 1 ); m_mpq_lar_core_solver.m_d_lower_bounds.resize(j + 1); m_mpq_lar_core_solver.m_d_upper_bounds.resize(j + 1); lp_assert(m_mpq_lar_core_solver.m_d_heading.size() == j); // as A().column_count() on the entry to the method if (register_in_basis) { A_d().add_row(); m_mpq_lar_core_solver.m_d_heading.push_back(m_mpq_lar_core_solver.m_d_basis.size()); m_mpq_lar_core_solver.m_d_basis.push_back(j); }else { m_mpq_lar_core_solver.m_d_heading.push_back(- static_cast(m_mpq_lar_core_solver.m_d_nbasis.size()) - 1); m_mpq_lar_core_solver.m_d_nbasis.push_back(j); }*/ } void lar_solver::adjust_initial_state_for_tableau_rows() { for (unsigned i = 0; i < m_terms.size(); i++) { if (m_var_register.external_is_used(i + m_terms_start_index)) continue; add_row_from_term_no_constraint(m_terms[i], i + m_terms_start_index); } } // this fills the last row of A_d and sets the basis column: -1 in the last column of the row void lar_solver::fill_last_row_of_A_d(static_matrix & A, const lar_term* ls) { lp_assert(A.row_count() > 0); lp_assert(A.column_count() > 0); unsigned last_row = A.row_count() - 1; lp_assert(A.m_rows[last_row].empty()); for (auto & t : ls->m_coeffs) { lp_assert(!is_zero(t.second)); var_index j = t.first; A.set(last_row, j, -t.second.get_double()); } unsigned basis_j = A.column_count() - 1; A.set(last_row, basis_j, -1); lp_assert(A.is_correct()); } void lar_solver::update_free_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index constr_ind) { mpq y_of_bound(0); switch (kind) { case LT: y_of_bound = -1; case LE: m_mpq_lar_core_solver.m_column_types[j] = column_type::upper_bound; lp_assert(m_mpq_lar_core_solver.m_column_types()[j] == column_type::upper_bound); lp_assert(m_mpq_lar_core_solver.m_r_upper_bounds.size() > j); { auto up = numeric_pair(right_side, y_of_bound); m_mpq_lar_core_solver.m_r_upper_bounds[j] = up; } set_upper_bound_witness(j, constr_ind); break; case GT: y_of_bound = 1; case GE: m_mpq_lar_core_solver.m_column_types[j] = column_type::lower_bound; lp_assert(m_mpq_lar_core_solver.m_r_upper_bounds.size() > j); { auto low = numeric_pair(right_side, y_of_bound); m_mpq_lar_core_solver.m_r_lower_bounds[j] = low; } set_lower_bound_witness(j, constr_ind); break; case EQ: m_mpq_lar_core_solver.m_column_types[j] = column_type::fixed; m_mpq_lar_core_solver.m_r_lower_bounds[j] = m_mpq_lar_core_solver.m_r_upper_bounds[j] = numeric_pair(right_side, zero_of_type()); set_upper_bound_witness(j, constr_ind); set_lower_bound_witness(j, constr_ind); break; default: lp_unreachable(); } m_columns_with_changed_bound.insert(j); } void lar_solver::update_upper_bound_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci) { lp_assert(m_mpq_lar_core_solver.m_column_types()[j] == column_type::upper_bound); mpq y_of_bound(0); switch (kind) { case LT: y_of_bound = -1; case LE: { auto up = numeric_pair(right_side, y_of_bound); if (up < m_mpq_lar_core_solver.m_r_upper_bounds()[j]) { m_mpq_lar_core_solver.m_r_upper_bounds[j] = up; set_upper_bound_witness(j, ci); m_columns_with_changed_bound.insert(j); } } break; case GT: y_of_bound = 1; case GE: m_mpq_lar_core_solver.m_column_types[j] = column_type::boxed; { auto low = numeric_pair(right_side, y_of_bound); m_mpq_lar_core_solver.m_r_lower_bounds[j] = low; set_lower_bound_witness(j, ci); m_columns_with_changed_bound.insert(j); if (low > m_mpq_lar_core_solver.m_r_upper_bounds[j]) { m_status = lp_status::INFEASIBLE; m_infeasible_column_index = j; } else { m_mpq_lar_core_solver.m_column_types[j] = m_mpq_lar_core_solver.m_r_lower_bounds()[j] < m_mpq_lar_core_solver.m_r_upper_bounds()[j] ? column_type::boxed : column_type::fixed; } } break; case EQ: { auto v = numeric_pair(right_side, zero_of_type()); if (v > m_mpq_lar_core_solver.m_r_upper_bounds[j]) { m_status = lp_status::INFEASIBLE; set_lower_bound_witness(j, ci); m_infeasible_column_index = j; } else { m_mpq_lar_core_solver.m_r_lower_bounds[j] = m_mpq_lar_core_solver.m_r_upper_bounds[j] = v; m_columns_with_changed_bound.insert(j); set_lower_bound_witness(j, ci); set_upper_bound_witness(j, ci); m_mpq_lar_core_solver.m_column_types[j] = column_type::fixed; } break; } break; default: lp_unreachable(); } } void lar_solver::update_boxed_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci) { lp_assert(m_status == lp_status::INFEASIBLE || (m_mpq_lar_core_solver.m_column_types()[j] == column_type::boxed && m_mpq_lar_core_solver.m_r_lower_bounds()[j] < m_mpq_lar_core_solver.m_r_upper_bounds()[j])); mpq y_of_bound(0); switch (kind) { case LT: y_of_bound = -1; case LE: { auto up = numeric_pair(right_side, y_of_bound); if (up < m_mpq_lar_core_solver.m_r_upper_bounds[j]) { m_mpq_lar_core_solver.m_r_upper_bounds[j] = up; set_upper_bound_witness(j, ci); m_columns_with_changed_bound.insert(j); } if (up < m_mpq_lar_core_solver.m_r_lower_bounds[j]) { m_status = lp_status::INFEASIBLE; m_infeasible_column_index = j; } else { if (m_mpq_lar_core_solver.m_r_lower_bounds()[j] == m_mpq_lar_core_solver.m_r_upper_bounds()[j]) m_mpq_lar_core_solver.m_column_types[j] = column_type::fixed; } } break; case GT: y_of_bound = 1; case GE: { auto low = numeric_pair(right_side, y_of_bound); if (low > m_mpq_lar_core_solver.m_r_lower_bounds[j]) { m_mpq_lar_core_solver.m_r_lower_bounds[j] = low; m_columns_with_changed_bound.insert(j); set_lower_bound_witness(j, ci); } if (low > m_mpq_lar_core_solver.m_r_upper_bounds[j]) { m_status = lp_status::INFEASIBLE; m_infeasible_column_index = j; } else if (low == m_mpq_lar_core_solver.m_r_upper_bounds[j]) { m_mpq_lar_core_solver.m_column_types[j] = column_type::fixed; } } break; case EQ: { auto v = numeric_pair(right_side, zero_of_type()); if (v < m_mpq_lar_core_solver.m_r_lower_bounds[j]) { m_status = lp_status::INFEASIBLE; m_infeasible_column_index = j; set_upper_bound_witness(j, ci); } else if (v > m_mpq_lar_core_solver.m_r_upper_bounds[j]) { m_status = lp_status::INFEASIBLE; m_infeasible_column_index = j; set_lower_bound_witness(j, ci); } else { m_mpq_lar_core_solver.m_r_lower_bounds[j] = m_mpq_lar_core_solver.m_r_upper_bounds[j] = v; set_lower_bound_witness(j, ci); set_upper_bound_witness(j, ci); m_mpq_lar_core_solver.m_column_types[j] = column_type::fixed; m_columns_with_changed_bound.insert(j); } break; } default: lp_unreachable(); } } void lar_solver::update_lower_bound_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci) { lp_assert(m_mpq_lar_core_solver.m_column_types()[j] == column_type::lower_bound); mpq y_of_bound(0); switch (kind) { case LT: y_of_bound = -1; case LE: { auto up = numeric_pair(right_side, y_of_bound); m_mpq_lar_core_solver.m_r_upper_bounds[j] = up; set_upper_bound_witness(j, ci); m_columns_with_changed_bound.insert(j); if (up < m_mpq_lar_core_solver.m_r_lower_bounds[j]) { m_status = lp_status::INFEASIBLE; m_infeasible_column_index = j; } else { m_mpq_lar_core_solver.m_column_types[j] = m_mpq_lar_core_solver.m_r_lower_bounds()[j] < m_mpq_lar_core_solver.m_r_upper_bounds()[j] ? column_type::boxed : column_type::fixed; } } break; case GT: y_of_bound = 1; case GE: { auto low = numeric_pair(right_side, y_of_bound); if (low > m_mpq_lar_core_solver.m_r_lower_bounds[j]) { m_mpq_lar_core_solver.m_r_lower_bounds[j] = low; m_columns_with_changed_bound.insert(j); set_lower_bound_witness(j, ci); } } break; case EQ: { auto v = numeric_pair(right_side, zero_of_type()); if (v < m_mpq_lar_core_solver.m_r_lower_bounds[j]) { m_status = lp_status::INFEASIBLE; m_infeasible_column_index = j; set_upper_bound_witness(j, ci); } else { m_mpq_lar_core_solver.m_r_lower_bounds[j] = m_mpq_lar_core_solver.m_r_upper_bounds[j] = v; set_lower_bound_witness(j, ci); set_upper_bound_witness(j, ci); m_mpq_lar_core_solver.m_column_types[j] = column_type::fixed; } m_columns_with_changed_bound.insert(j); break; } default: lp_unreachable(); } } void lar_solver::update_fixed_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci) { lp_assert(m_status == lp_status::INFEASIBLE || (m_mpq_lar_core_solver.m_column_types()[j] == column_type::fixed && m_mpq_lar_core_solver.m_r_lower_bounds()[j] == m_mpq_lar_core_solver.m_r_upper_bounds()[j])); lp_assert(m_status == lp_status::INFEASIBLE || (m_mpq_lar_core_solver.m_r_lower_bounds()[j].y.is_zero() && m_mpq_lar_core_solver.m_r_upper_bounds()[j].y.is_zero())); auto v = numeric_pair(right_side, mpq(0)); mpq y_of_bound(0); switch (kind) { case LT: if (v <= m_mpq_lar_core_solver.m_r_lower_bounds[j]) { m_status = lp_status::INFEASIBLE; m_infeasible_column_index = j; set_upper_bound_witness(j, ci); } break; case LE: { if (v < m_mpq_lar_core_solver.m_r_lower_bounds[j]) { m_status = lp_status::INFEASIBLE; m_infeasible_column_index = j; set_upper_bound_witness(j, ci); } } break; case GT: { if (v >= m_mpq_lar_core_solver.m_r_upper_bounds[j]) { m_status = lp_status::INFEASIBLE; m_infeasible_column_index = j; set_lower_bound_witness(j, ci); } } break; case GE: { if (v > m_mpq_lar_core_solver.m_r_upper_bounds[j]) { m_status = lp_status::INFEASIBLE; m_infeasible_column_index = j; set_lower_bound_witness(j, ci); } } break; case EQ: { if (v < m_mpq_lar_core_solver.m_r_lower_bounds[j]) { m_status = lp_status::INFEASIBLE; m_infeasible_column_index = j; set_upper_bound_witness(j, ci); } else if (v > m_mpq_lar_core_solver.m_r_upper_bounds[j]) { m_status = lp_status::INFEASIBLE; m_infeasible_column_index = j; set_lower_bound_witness(j, ci); } break; } default: lp_unreachable(); } } bool lar_solver::column_corresponds_to_term(unsigned j) const { return m_var_register.local_to_external(j) >= m_terms_start_index; } var_index lar_solver::to_column(unsigned ext_j) const { return m_var_register.external_to_local(ext_j); } bool lar_solver::tighten_term_bounds_by_delta(unsigned term_index, const impq& delta) { unsigned tj = term_index + m_terms_start_index; unsigned j; if (m_var_register.external_is_used(tj, j) == false) return true; // the term is not a column so it has no bounds auto & slv = m_mpq_lar_core_solver.m_r_solver; TRACE("cube", tout << "delta = " << delta << std::endl; m_int_solver->display_column(tout, j); ); if (slv.column_has_upper_bound(j) && slv.column_has_lower_bound(j)) { if (slv.m_upper_bounds[j] - delta < slv.m_lower_bounds[j] + delta) { TRACE("cube", tout << "cannot tighten, delta = " << delta;); return false; } } TRACE("cube", tout << "can tighten";); if (slv.column_has_upper_bound(j)) { if (!is_zero(delta.y)) add_var_bound(tj, lconstraint_kind::LT, slv.m_upper_bounds[j].x - delta.x); else add_var_bound(tj, lconstraint_kind::LE, slv.m_upper_bounds[j].x - delta.x); } if (slv.column_has_lower_bound(j)) { if (!is_zero(delta.y)) add_var_bound(tj, lconstraint_kind::GT, slv.m_lower_bounds[j].x + delta.x); else add_var_bound(tj, lconstraint_kind::GE, slv.m_lower_bounds[j].x + delta.x); } return true; } void lar_solver::round_to_integer_solution() { for (unsigned j = 0; j < column_count(); j++) { if (!column_is_int(j)) continue; if (column_corresponds_to_term(j)) continue; TRACE("cube", m_int_solver->display_column(tout, j);); impq& v = m_mpq_lar_core_solver.m_r_x[j]; if (v.is_int()) continue; impq flv = floor(v); auto del = flv - v; // del is negative if (del < - mpq(1, 2)) { del = impq(one_of_type()) + del; v = ceil(v); } else { v = flv; } } } // return true if all y coords are zeroes bool lar_solver::sum_first_coords(const lar_term& t, mpq & val) const { val = zero_of_type(); for (const auto & c : t) { const auto & x = m_mpq_lar_core_solver.m_r_x[c.var()]; if (!is_zero(x.y)) return false; val += x.x * c.coeff(); } return true; } bool lar_solver::get_equality_and_right_side_for_term_on_current_x(unsigned term_index, mpq & rs, constraint_index& ci, bool &upper_bound) const { unsigned tj = term_index + m_terms_start_index; unsigned j; bool is_int; if (m_var_register.external_is_used(tj, j, is_int) == false) return false; // the term does not have a bound because it does not correspond to a column if (!is_int) // todo - allow for the next version of hnf return false; bool rs_is_calculated = false; mpq b; bool is_strict; const lar_term& t = *terms()[term_index]; if (has_upper_bound(j, ci, b, is_strict) && !is_strict) { lp_assert(b.is_int()); if (!sum_first_coords(t, rs)) return false; rs_is_calculated = true; if (rs == b) { upper_bound = true; return true; } } if (has_lower_bound(j, ci, b, is_strict) && !is_strict) { if (!rs_is_calculated){ if (!sum_first_coords(t, rs)) return false; } lp_assert(b.is_int()); if (rs == b) { upper_bound = false; return true; } } return false; } void lar_solver::set_cut_strategy(unsigned cut_frequency) { if (cut_frequency < 4) { settings().m_int_gomory_cut_period = 2; // do it often settings().set_hnf_cut_period(4); // also create hnf cuts } else if (cut_frequency == 4) { // enable all cuts and cube equally settings().m_int_gomory_cut_period = 4; settings().set_hnf_cut_period(4); } else { // disable all heuristics except cube settings().m_int_gomory_cut_period = 10000000; settings().set_hnf_cut_period(100000000); } } } // namespace lp z3-z3-4.8.7/src/util/lp/lar_solver.h000066400000000000000000000511521356505360400171330ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Nikolaj Bjorner (nbjorner) Lev Nachmanson (levnach) Revision History: --*/ #pragma once #include "util/vector.h" #include #include "util/debug.h" #include "util/buffer.h" #include #include #include #include "util/lp/lar_constraints.h" #include #include "util/lp/lar_core_solver.h" #include #include "util/lp/numeric_pair.h" #include "util/lp/scaler.h" #include "util/lp/lp_primal_core_solver.h" #include "util/lp/random_updater.h" #include #include "util/lp/stacked_value.h" #include "util/lp/stacked_vector.h" #include "util/lp/implied_bound.h" #include "util/lp/bound_analyzer_on_row.h" #include "util/lp/conversion_helper.h" #include "util/lp/int_solver.h" #include "util/lp/nra_solver.h" #include "util/lp/bound_propagator.h" namespace lp { class lar_solver : public column_namer { #if Z3DEBUG_CHECK_UNIQUE_TERMS struct term_hasher { std::size_t operator()(const lar_term *t) const { using std::size_t; using std::hash; using std::string; size_t seed = 0; for (const auto& p : t->m_coeffs) { hash_combine(seed, p); } return seed; } }; struct term_ls_comparer { bool operator()(const lar_term *a, const lar_term* b) const { // a is contained in b for (auto & p : a->m_coeffs) { auto t = b->m_coeffs.find(p.first); if (t == b->m_coeffs.end()) return false; if (p.second != t->second) return false; } // zz is contained in b for (auto & p : b->m_coeffs) { auto t = a->m_coeffs.find(p.first); if (t == a->m_coeffs.end()) return false; if (p.second != t->second) return false; } return true; } }; std::unordered_set m_set_of_terms; #endif //////////////////// fields ////////////////////////// lp_settings m_settings; lp_status m_status; stacked_value m_simplex_strategy; var_register m_var_register; stacked_vector m_columns_to_ul_pairs; vector m_constraints; stacked_value m_constraint_count; // the set of column indices j such that bounds have changed for j int_set m_columns_with_changed_bound; int_set m_rows_with_changed_bounds; int_set m_basic_columns_with_changed_cost; stacked_value m_infeasible_column_index; // such can be found at the initialization step stacked_value m_term_count; vector m_terms; const var_index m_terms_start_index; indexed_vector m_column_buffer; public: lar_core_solver m_mpq_lar_core_solver; private: int_solver * m_int_solver; public : unsigned terms_start_index() const { return m_terms_start_index; } const vector & terms() const { return m_terms; } const vector& constraints() const { return m_constraints; } void set_int_solver(int_solver * int_slv) { m_int_solver = int_slv; } int_solver * get_int_solver() { return m_int_solver; } unsigned constraint_count() const; const lar_base_constraint& get_constraint(unsigned ci) const; ////////////////// methods //////////////////////////////// static_matrix> & A_r(); static_matrix> const & A_r() const; static_matrix & A_d(); static_matrix const & A_d() const; static bool valid_index(unsigned j){ return static_cast(j) >= 0;} bool column_is_int(unsigned j) const; bool column_value_is_int(unsigned j) const { return m_mpq_lar_core_solver.m_r_x[j].is_int(); } const impq& get_column_value(unsigned j) const { return m_mpq_lar_core_solver.m_r_x[j]; } bool is_term(var_index j) const; bool column_is_fixed(unsigned j) const; public: // init region bool strategy_is_undecided() const; var_index add_var(unsigned ext_j, bool is_integer); void register_new_ext_var_index(unsigned ext_v, bool is_int); bool term_is_int(const lar_term * t) const; bool var_is_int(var_index v) const; void add_non_basic_var_to_core_fields(unsigned ext_j, bool is_int); void add_new_var_to_core_fields_for_doubles(bool register_in_basis); void add_new_var_to_core_fields_for_mpq(bool register_in_basis); // terms var_index add_term(const vector> & coeffs); var_index add_term_undecided(const vector> & coeffs); bool term_coeffs_are_ok(const vector> & coeffs); void push_and_register_term(lar_term* t); void add_row_for_term(const lar_term * term, unsigned term_ext_index); void add_row_from_term_no_constraint(const lar_term * term, unsigned term_ext_index); void add_basic_var_to_core_fields(); constraint_index add_var_bound(var_index j, lconstraint_kind kind, const mpq & right_side) ; bool compare_values(var_index j, lconstraint_kind kind, const mpq & right_side); bool compare_values(impq const& lhs, lconstraint_kind k, const mpq & rhs); void update_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index constr_index); void add_var_bound_on_constraint_for_term(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci); void add_constraint_from_term_and_create_new_column_row(unsigned term_j, const lar_term* term, lconstraint_kind kind, const mpq & right_side); void decide_on_strategy_and_adjust_initial_state(); void adjust_initial_state(); void adjust_initial_state_for_lu(); void adjust_initial_state_for_tableau_rows(); // this fills the last row of A_d and sets the basis column: -1 in the last column of the row void fill_last_row_of_A_d(static_matrix & A, const lar_term* ls); void update_free_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index constr_ind); void update_upper_bound_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci); void update_boxed_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci); void update_lower_bound_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci); void update_fixed_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci); //end of init region lp_settings & settings(); lp_settings const & settings() const; void clear(); lar_solver(); void set_track_pivoted_rows(bool v); bool get_track_pivoted_rows() const; virtual ~lar_solver(); unsigned adjust_term_index(unsigned j) const; bool use_lu() const; bool sizes_are_correct() const; bool implied_bound_is_correctly_explained(implied_bound const & be, const vector> & explanation) const; void analyze_new_bounds_on_row( unsigned row_index, bound_propagator & bp); void analyze_new_bounds_on_row_tableau( unsigned row_index, bound_propagator & bp); void substitute_basis_var_in_terms_for_row(unsigned i); void calculate_implied_bounds_for_row(unsigned i, bound_propagator & bp); unsigned adjust_column_index_to_term_index(unsigned j) const; var_index local2external(var_index idx) const { return m_var_register.local_to_external(idx); } void propagate_bounds_on_a_term(const lar_term& t, bound_propagator & bp, unsigned term_offset); void explain_implied_bound(implied_bound & ib, bound_propagator & bp); bool term_is_used_as_row(unsigned term) const; void propagate_bounds_on_terms(bound_propagator & bp); // goes over touched rows and tries to induce bounds void propagate_bounds_for_touched_rows(bound_propagator & bp); lp_status get_status() const; void set_status(lp_status s); lp_status find_feasible_solution(); lp_status solve(); void fill_explanation_from_infeasible_column(explanation_t & evidence) const; unsigned get_total_iterations() const; // see http://research.microsoft.com/projects/z3/smt07.pdf // This method searches for a feasible solution with as many different values of variables, reverenced in vars, as it can find // Attention, after a call to this method the non-basic variables don't necesserarly stick to their bounds anymore vector get_list_of_all_var_indices() const; void push(); static void clean_popped_elements(unsigned n, int_set& set); static void shrink_inf_set_after_pop(unsigned n, int_set & set); void pop(unsigned k); class scoped_push { lar_solver& m_solver; bool m_pop; public: scoped_push(lar_solver& s):m_solver(s), m_pop(true) { s.push(); } ~scoped_push() { if (m_pop) m_solver.pop(); } void pop() { SASSERT(m_pop); m_solver.pop(); m_pop = false; } }; vector get_all_constraint_indices() const; bool maximize_term_on_tableau(const lar_term & term, impq &term_max); bool costs_are_zeros_for_r_solver() const; bool reduced_costs_are_zeroes_for_r_solver() const; void set_costs_to_zero(const lar_term & term); void prepare_costs_for_r_solver(const lar_term & term); bool maximize_term_on_corrected_r_solver(lar_term & term, impq &term_max); // starting from a given feasible state look for the maximum of the term // return true if found and false if unbounded lp_status maximize_term(unsigned j_or_term, impq &term_max); const lar_term & get_term(unsigned j) const; void pop_core_solver_params(); void pop_core_solver_params(unsigned k); void set_upper_bound_witness(var_index j, constraint_index ci); void set_lower_bound_witness(var_index j, constraint_index ci); void substitute_terms_in_linear_expression( const vector>& left_side_with_terms, vector> &left_side) const; void detect_rows_of_bound_change_column_for_nbasic_column(unsigned j); void detect_rows_of_bound_change_column_for_nbasic_column_tableau(unsigned j); bool use_tableau() const; bool use_tableau_costs() const; void detect_rows_of_column_with_bound_change(unsigned j); void adjust_x_of_column(unsigned j); bool row_is_correct(unsigned i) const; bool ax_is_correct() const; bool tableau_with_costs() const; bool costs_are_used() const; void change_basic_columns_dependend_on_a_given_nb_column(unsigned j, const numeric_pair & delta); void update_x_and_inf_costs_for_column_with_changed_bounds(unsigned j); void detect_rows_with_changed_bounds_for_column(unsigned j); void detect_rows_with_changed_bounds(); inline bool is_base(unsigned j) const { return m_mpq_lar_core_solver.m_r_heading[j] >= 0; } bool move_non_basic_columns_to_bounds(); bool move_non_basic_column_to_bounds(unsigned j); void set_value_for_nbasic_column(unsigned j, const impq & new_val); void update_x_and_inf_costs_for_columns_with_changed_bounds(); void update_x_and_inf_costs_for_columns_with_changed_bounds_tableau(); void solve_with_core_solver(); numeric_pair get_basic_var_value_from_row_directly(unsigned i); numeric_pair get_basic_var_value_from_row(unsigned i); template void add_last_rows_to_lu(lp_primal_core_solver & s); bool x_is_correct() const; bool var_is_registered(var_index vj) const; unsigned constraint_stack_size() const; void fill_last_row_of_A_r(static_matrix> & A, const lar_term * ls); template void create_matrix_A(static_matrix & matr); template void copy_from_mpq_matrix(static_matrix & matr); bool try_to_set_fixed(column_info & ci); column_type get_column_type(unsigned j) const; std::string get_column_name(unsigned j) const override; bool all_constrained_variables_are_registered(const vector>& left_side); constraint_index add_constraint(const vector>& left_side_with_terms, lconstraint_kind kind_par, const mpq& right_side_parm); bool all_constraints_hold() const; bool constraint_holds(const lar_base_constraint & constr, std::unordered_map & var_map) const; bool the_relations_are_of_same_type(const vector> & evidence, lconstraint_kind & the_kind_of_sum) const; static void register_in_map(std::unordered_map & coeffs, const lar_base_constraint & cn, const mpq & a); static void register_monoid_in_map(std::unordered_map & coeffs, const mpq & a, unsigned j); bool the_left_sides_sum_to_zero(const vector> & evidence) const; bool the_right_sides_do_not_sum_to_zero(const vector> & evidence); bool explanation_is_correct(const vector>& explanation) const; bool inf_explanation_is_correct() const; mpq sum_of_right_sides_of_explanation(const vector> & explanation) const; bool has_lower_bound(var_index var, constraint_index& ci, mpq& value, bool& is_strict) const; bool has_upper_bound(var_index var, constraint_index& ci, mpq& value, bool& is_strict) const; bool has_value(var_index var, mpq& value) const; void get_infeasibility_explanation(vector> & explanation) const; void get_infeasibility_explanation_for_inf_sign( vector> & explanation, const vector> & inf_row, int inf_sign) const; void get_model(std::unordered_map & variable_values) const; void get_model_do_not_care_about_diff_vars(std::unordered_map & variable_values) const; std::string get_variable_name(var_index vi) const; // print utilities std::ostream& print_constraint(constraint_index ci, std::ostream & out) const; std::ostream& print_constraints(std::ostream& out) const ; std::ostream& print_terms(std::ostream& out) const; std::ostream& print_left_side_of_constraint(const lar_base_constraint * c, std::ostream & out) const; std::ostream& print_term(lar_term const& term, std::ostream & out) const; std::ostream& print_term_as_indices(lar_term const& term, std::ostream & out) const; std::ostream& print_constraint(const lar_base_constraint * c, std::ostream & out) const; std::ostream& print_implied_bound(const implied_bound& be, std::ostream & out) const; std::ostream& print_values(std::ostream& out) const; mpq get_left_side_val(const lar_base_constraint & cns, const std::unordered_map & var_map) const; void fill_var_set_for_random_update(unsigned sz, var_index const * vars, vector& column_list); void random_update(unsigned sz, var_index const * vars); void pivot_fixed_vars_from_basis(); void pop(); bool column_represents_row_in_tableau(unsigned j); void make_sure_that_the_bottom_right_elem_not_zero_in_tableau(unsigned i, unsigned j); void remove_last_row_and_column_from_tableau(unsigned j); void remove_last_column_from_A(); void remove_last_column_from_basis_tableau(unsigned j); void remove_last_column_from_tableau(); void pop_tableau(); void clean_inf_set_of_r_solver_after_pop(); void shrink_explanation_to_minimum(vector> & explanation) const; bool column_value_is_integer(unsigned j) const { return get_column_value(j).is_int(); } bool column_is_real(unsigned j) const { return !column_is_int(j); } bool model_is_int_feasible() const; const impq & column_lower_bound(unsigned j) const { return m_mpq_lar_core_solver.lower_bound(j); } const impq & column_upper_bound(unsigned j) const { return m_mpq_lar_core_solver.upper_bound(j); } bool column_is_bounded(unsigned j) const { return m_mpq_lar_core_solver.column_is_bounded(j); } void get_bound_constraint_witnesses_for_column(unsigned j, constraint_index & lc, constraint_index & uc) const { const ul_pair & ul = m_columns_to_ul_pairs[j]; lc = ul.lower_bound_witness(); uc = ul.upper_bound_witness(); } indexed_vector & get_column_in_lu_mode(unsigned j) { m_column_buffer.clear(); m_column_buffer.resize(A_r().row_count()); m_mpq_lar_core_solver.m_r_solver.solve_Bd(j, m_column_buffer); return m_column_buffer; } bool bound_is_integer_for_integer_column(unsigned j, const mpq & right_side) const; const row_strip & get_row(unsigned i) { return A_r().m_rows[i]; } unsigned get_base_column_in_row(unsigned row_index) const { return m_mpq_lar_core_solver.m_r_solver.get_base_column_in_row(row_index); } constraint_index get_column_upper_bound_witness(unsigned j) const { return m_columns_to_ul_pairs()[j].upper_bound_witness(); } constraint_index get_column_lower_bound_witness(unsigned j) const { return m_columns_to_ul_pairs()[j].lower_bound_witness(); } void subs_term_columns(lar_term& t) { vector> columns_to_subs; for (const auto & m : t.m_coeffs) { unsigned tj = adjust_column_index_to_term_index(m.first); if (tj == m.first) continue; columns_to_subs.push_back(std::make_pair(m.first, tj)); } for (const auto & p : columns_to_subs) { auto it = t.m_coeffs.find(p.first); lp_assert(it != t.m_coeffs.end()); mpq v = it->second; t.m_coeffs.erase(it); t.m_coeffs[p.second] = v; } } bool has_int_var() const; bool has_inf_int() const { for (unsigned j = 0; j < column_count(); j++) { if (column_is_int(j) && ! column_value_is_int(j)) return true; } return false; } bool r_basis_has_inf_int() const { for (unsigned j : r_basis()) { if (column_is_int(j) && ! column_value_is_int(j)) return true; } return false; } lar_core_solver & get_core_solver() { return m_mpq_lar_core_solver; } bool column_corresponds_to_term(unsigned) const; void catch_up_in_updating_int_solver(); var_index to_column(unsigned ext_j) const; bool tighten_term_bounds_by_delta(unsigned, const impq&); void round_to_integer_solution(); void update_delta_for_terms(const impq & delta, unsigned j, const vector&); void fill_vars_to_terms(vector> & vars_to_terms); unsigned column_count() const { return A_r().column_count(); } const vector & r_basis() const { return m_mpq_lar_core_solver.r_basis(); } const vector & r_nbasis() const { return m_mpq_lar_core_solver.r_nbasis(); } bool get_equality_and_right_side_for_term_on_current_x(unsigned i, mpq &rs, constraint_index& ci, bool &upper_bound) const; bool remove_from_basis(unsigned); lar_term get_term_to_maximize(unsigned ext_j) const; void set_cut_strategy(unsigned cut_frequency); bool sum_first_coords(const lar_term& t, mpq & val) const; }; } z3-z3-4.8.7/src/util/lp/lar_term.h000066400000000000000000000072571356505360400165770ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #pragma once #include "util/lp/indexed_vector.h" namespace lp { struct lar_term { // the term evaluates to sum of m_coeffs std::unordered_map m_coeffs; // mpq m_v; lar_term() {} void add_monomial(const mpq& c, unsigned j) { auto it = m_coeffs.find(j); if (it == m_coeffs.end()) { m_coeffs.emplace(j, c); } else { it->second += c; if (is_zero(it->second)) m_coeffs.erase(it); } } bool is_empty() const { return m_coeffs.empty(); // && is_zero(m_v); } unsigned size() const { return static_cast(m_coeffs.size()); } const std::unordered_map & coeffs() const { return m_coeffs; } lar_term(const vector>& coeffs) { for (const auto & p : coeffs) { add_monomial(p.first, p.second); } } bool operator==(const lar_term & a) const { return false; } // take care not to create identical terms bool operator!=(const lar_term & a) const { return ! (*this == a);} // some terms get used in add constraint // it is the same as the offset in the m_constraints vector> coeffs_as_vector() const { vector> ret; for (const auto & p : m_coeffs) { ret.push_back(std::make_pair(p.second, p.first)); } return ret; } // j is the basic variable to substitute void subst(unsigned j, indexed_vector & li) { auto it = m_coeffs.find(j); if (it == m_coeffs.end()) return; const mpq & b = it->second; for (unsigned it_j :li.m_index) { add_monomial(- b * li.m_data[it_j], it_j); } m_coeffs.erase(it); } bool contains(unsigned j) const { return m_coeffs.find(j) != m_coeffs.end(); } void negate() { for (auto & t : m_coeffs) t.second.neg(); } template T apply(const vector& x) const { T ret(0); for (const auto & t : m_coeffs) { ret += t.second * x[t.first]; } return ret; } void clear() { m_coeffs.clear(); } struct ival { unsigned m_var; const mpq & m_coeff; ival(unsigned var, const mpq & val) : m_var(var), m_coeff(val) { } unsigned var() const { return m_var;} const mpq & coeff() const { return m_coeff; } }; struct const_iterator { //fields std::unordered_map::const_iterator m_it; typedef const_iterator self_type; typedef ival value_type; typedef ival reference; // typedef std::pair* pointer; typedef int difference_type; typedef std::forward_iterator_tag iterator_category; reference operator*() const { return ival(m_it->first, m_it->second); } self_type operator++() { self_type i = *this; m_it++; return i; } self_type operator++(int) { m_it++; return *this; } const_iterator(std::unordered_map::const_iterator it) : m_it(it) {} bool operator==(const self_type &other) const { return m_it == other.m_it; } bool operator!=(const self_type &other) const { return !(*this == other); } }; const_iterator begin() const { return m_coeffs.begin();} const_iterator end() const { return m_coeffs.end(); } }; } z3-z3-4.8.7/src/util/lp/lia_move.h000066400000000000000000000015301356505360400165510ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Nikolaj Bjorner (nbjorner) Lev Nachmanson (levnach) Revision History: --*/ #pragma once namespace lp { enum class lia_move { sat, branch, cut, conflict, continue_with_check, undef, unsat }; inline std::string lia_move_to_string(lia_move m) { switch (m) { case lia_move::sat: return "sat"; case lia_move::branch: return "branch"; case lia_move::cut: return "cut"; case lia_move::conflict: return "conflict"; case lia_move::continue_with_check: return "continue_with_check"; case lia_move::undef: return "undef"; case lia_move::unsat: return "unsat"; default: lp_assert(false); }; return "strange"; } } z3-z3-4.8.7/src/util/lp/lp_bound_propagator.cpp000066400000000000000000000050331356505360400213530ustar00rootroot00000000000000/* Copyright (c) 2017 Microsoft Corporation Author: Lev Nachmanson */ #include "util/lp/lar_solver.h" namespace lp { bound_propagator::bound_propagator(lar_solver & ls): m_lar_solver(ls) {} column_type bound_propagator::get_column_type(unsigned j) const { return m_lar_solver.m_mpq_lar_core_solver.m_column_types()[j]; } const impq & bound_propagator::get_lower_bound(unsigned j) const { return m_lar_solver.m_mpq_lar_core_solver.m_r_lower_bounds()[j]; } const impq & bound_propagator::get_upper_bound(unsigned j) const { return m_lar_solver.m_mpq_lar_core_solver.m_r_upper_bounds()[j]; } void bound_propagator::try_add_bound(mpq v, unsigned j, bool is_low, bool coeff_before_j_is_pos, unsigned row_or_term_index, bool strict) { j = m_lar_solver.adjust_column_index_to_term_index(j); lconstraint_kind kind = is_low? GE : LE; if (strict) kind = static_cast(kind / 2); if (!bound_is_interesting(j, kind, v)) return; unsigned k; // index to ibounds if (is_low) { if (try_get_value(m_improved_lower_bounds, j, k)) { auto & found_bound = m_ibounds[k]; if (v > found_bound.m_bound || (v == found_bound.m_bound && found_bound.m_strict == false && strict)) { found_bound = implied_bound(v, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict); TRACE("try_add_bound", m_lar_solver.print_implied_bound(found_bound, tout);); } } else { m_improved_lower_bounds[j] = m_ibounds.size(); m_ibounds.push_back(implied_bound(v, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict)); TRACE("try_add_bound", m_lar_solver.print_implied_bound(m_ibounds.back(), tout);); } } else { // the upper bound case if (try_get_value(m_improved_upper_bounds, j, k)) { auto & found_bound = m_ibounds[k]; if (v < found_bound.m_bound || (v == found_bound.m_bound && found_bound.m_strict == false && strict)) { found_bound = implied_bound(v, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict); TRACE("try_add_bound", m_lar_solver.print_implied_bound(found_bound, tout);); } } else { m_improved_upper_bounds[j] = m_ibounds.size(); m_ibounds.push_back(implied_bound(v, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict)); TRACE("try_add_bound", m_lar_solver.print_implied_bound(m_ibounds.back(), tout);); } } } } z3-z3-4.8.7/src/util/lp/lp_core_solver_base.cpp000066400000000000000000000317131356505360400213260ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #include #include #include #include "util/vector.h" #include #include "util/lp/lp_core_solver_base_def.h" template bool lp::lp_core_solver_base::A_mult_x_is_off() const; template bool lp::lp_core_solver_base::A_mult_x_is_off_on_index(const vector &) const; template bool lp::lp_core_solver_base::basis_heading_is_correct() const; template void lp::lp_core_solver_base::calculate_pivot_row_of_B_1(unsigned int); template void lp::lp_core_solver_base::calculate_pivot_row_when_pivot_row_of_B1_is_ready(unsigned); template bool lp::lp_core_solver_base::column_is_dual_feasible(unsigned int) const; template void lp::lp_core_solver_base::fill_reduced_costs_from_m_y_by_rows(); template bool lp::lp_core_solver_base::find_x_by_solving(); template lp::non_basic_column_value_position lp::lp_core_solver_base::get_non_basic_column_value_position(unsigned int) const; template lp::non_basic_column_value_position lp::lp_core_solver_base >::get_non_basic_column_value_position(unsigned int) const; template lp::non_basic_column_value_position lp::lp_core_solver_base::get_non_basic_column_value_position(unsigned int) const; template void lp::lp_core_solver_base::init_reduced_costs_for_one_iteration(); template lp::lp_core_solver_base::lp_core_solver_base( lp::static_matrix&, vector&, vector&, vector &, vector &, vector&, vector&, lp::lp_settings&, const column_namer&, const vector&, const vector&, const vector&); template bool lp::lp_core_solver_base::print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(char const*, std::ostream &); template bool lp::lp_core_solver_base >::print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(char const*, std::ostream &); template void lp::lp_core_solver_base::restore_x(unsigned int, double const&); template void lp::lp_core_solver_base::set_non_basic_x_to_correct_bounds(); template void lp::lp_core_solver_base::snap_xN_to_bounds_and_free_columns_to_zeroes(); template void lp::lp_core_solver_base >::snap_xN_to_bounds_and_free_columns_to_zeroes(); template void lp::lp_core_solver_base::solve_Ax_eq_b(); template void lp::lp_core_solver_base::solve_Bd(unsigned int); template void lp::lp_core_solver_base>::solve_Bd(unsigned int, indexed_vector&); template void lp::lp_core_solver_base::solve_yB(vector&); template bool lp::lp_core_solver_base::update_basis_and_x(int, int, double const&); template void lp::lp_core_solver_base::update_x(unsigned int, const double&); template bool lp::lp_core_solver_base::A_mult_x_is_off() const; template bool lp::lp_core_solver_base::A_mult_x_is_off_on_index(const vector &) const; template bool lp::lp_core_solver_base::basis_heading_is_correct() const ; template void lp::lp_core_solver_base::calculate_pivot_row_of_B_1(unsigned int); template void lp::lp_core_solver_base::calculate_pivot_row_when_pivot_row_of_B1_is_ready(unsigned); template bool lp::lp_core_solver_base::column_is_dual_feasible(unsigned int) const; template void lp::lp_core_solver_base::fill_reduced_costs_from_m_y_by_rows(); template bool lp::lp_core_solver_base::find_x_by_solving(); template void lp::lp_core_solver_base::init_reduced_costs_for_one_iteration(); template bool lp::lp_core_solver_base::print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(char const*, std::ostream &); template void lp::lp_core_solver_base::restore_x(unsigned int, lp::mpq const&); template void lp::lp_core_solver_base::set_non_basic_x_to_correct_bounds(); template void lp::lp_core_solver_base::solve_Ax_eq_b(); template void lp::lp_core_solver_base::solve_Bd(unsigned int); template void lp::lp_core_solver_base::solve_yB(vector&); template bool lp::lp_core_solver_base::update_basis_and_x(int, int, lp::mpq const&); template void lp::lp_core_solver_base::update_x(unsigned int, const lp::mpq&); template void lp::lp_core_solver_base >::calculate_pivot_row_of_B_1(unsigned int); template void lp::lp_core_solver_base >::calculate_pivot_row_when_pivot_row_of_B1_is_ready(unsigned); template void lp::lp_core_solver_base >::init(); template void lp::lp_core_solver_base >::init_basis_heading_and_non_basic_columns_vector(); template void lp::lp_core_solver_base >::init_reduced_costs_for_one_iteration(); template lp::lp_core_solver_base >::lp_core_solver_base(lp::static_matrix >&, vector >&, vector&, vector &, vector &, vector >&, vector&, lp::lp_settings&, const column_namer&, const vector&, const vector >&, const vector >&); template bool lp::lp_core_solver_base >::print_statistics_with_cost_and_check_that_the_time_is_over(lp::numeric_pair, std::ostream&); template void lp::lp_core_solver_base >::snap_xN_to_bounds_and_fill_xB(); template void lp::lp_core_solver_base >::solve_Ax_eq_b(); template void lp::lp_core_solver_base >::solve_Bd(unsigned int); template bool lp::lp_core_solver_base >::update_basis_and_x(int, int, lp::numeric_pair const&); template void lp::lp_core_solver_base >::update_x(unsigned int, const lp::numeric_pair&); template lp::lp_core_solver_base::lp_core_solver_base( lp::static_matrix&, vector&, vector&, vector &, vector &, vector&, vector&, lp::lp_settings&, const column_namer&, const vector&, const vector&, const vector&); template bool lp::lp_core_solver_base >::print_statistics_with_iterations_and_check_that_the_time_is_over(std::ostream &); template std::string lp::lp_core_solver_base::column_name(unsigned int) const; template void lp::lp_core_solver_base::pretty_print(std::ostream & out); template void lp::lp_core_solver_base::restore_state(double*, double*); template void lp::lp_core_solver_base::save_state(double*, double*); template std::string lp::lp_core_solver_base::column_name(unsigned int) const; template void lp::lp_core_solver_base::pretty_print(std::ostream & out); template void lp::lp_core_solver_base::restore_state(lp::mpq*, lp::mpq*); template void lp::lp_core_solver_base::save_state(lp::mpq*, lp::mpq*); template std::string lp::lp_core_solver_base >::column_name(unsigned int) const; template void lp::lp_core_solver_base >::pretty_print(std::ostream & out); template void lp::lp_core_solver_base >::restore_state(lp::mpq*, lp::mpq*); template void lp::lp_core_solver_base >::save_state(lp::mpq*, lp::mpq*); template void lp::lp_core_solver_base >::solve_yB(vector&); template void lp::lp_core_solver_base::init_lu(); template void lp::lp_core_solver_base::init_lu(); template int lp::lp_core_solver_base::pivots_in_column_and_row_are_different(int, int) const; template int lp::lp_core_solver_base >::pivots_in_column_and_row_are_different(int, int) const; template int lp::lp_core_solver_base::pivots_in_column_and_row_are_different(int, int) const; template bool lp::lp_core_solver_base::calc_current_x_is_feasible_include_non_basis(void)const; template bool lp::lp_core_solver_base::calc_current_x_is_feasible_include_non_basis(void)const; template bool lp::lp_core_solver_base >::calc_current_x_is_feasible_include_non_basis() const; template void lp::lp_core_solver_base >::pivot_fixed_vars_from_basis(); template bool lp::lp_core_solver_base::column_is_feasible(unsigned int) const; template bool lp::lp_core_solver_base::column_is_feasible(unsigned int) const; // template void lp::lp_core_solver_base >::print_linear_combination_of_column_indices(vector, std::allocator > > const&, std::ostream&) const; template bool lp::lp_core_solver_base >::column_is_feasible(unsigned int) const; template bool lp::lp_core_solver_base >::snap_non_basic_x_to_bound(); template void lp::lp_core_solver_base >::init_lu(); template bool lp::lp_core_solver_base >::A_mult_x_is_off_on_index(vector const&) const; template bool lp::lp_core_solver_base >::find_x_by_solving(); template void lp::lp_core_solver_base >::restore_x(unsigned int, lp::numeric_pair const&); template bool lp::lp_core_solver_base::pivot_for_tableau_on_basis(); template bool lp::lp_core_solver_base::pivot_for_tableau_on_basis(); template bool lp::lp_core_solver_base>::pivot_for_tableau_on_basis(); template bool lp::lp_core_solver_base>::pivot_column_tableau(unsigned int, unsigned int); template bool lp::lp_core_solver_base::pivot_column_tableau(unsigned int, unsigned int); template bool lp::lp_core_solver_base::pivot_column_tableau(unsigned int, unsigned int); template void lp::lp_core_solver_base >::transpose_rows_tableau(unsigned int, unsigned int); template bool lp::lp_core_solver_base >::inf_set_is_correct() const; template bool lp::lp_core_solver_base::inf_set_is_correct() const; template bool lp::lp_core_solver_base::inf_set_is_correct() const; template bool lp::lp_core_solver_base >::infeasibility_costs_are_correct() const; template bool lp::lp_core_solver_base::infeasibility_costs_are_correct() const; template bool lp::lp_core_solver_base::infeasibility_costs_are_correct() const; template void lp::lp_core_solver_base >::calculate_pivot_row(unsigned int); template bool lp::lp_core_solver_base >::remove_from_basis(unsigned int); z3-z3-4.8.7/src/util/lp/lp_core_solver_base.h000066400000000000000000000563761356505360400210070ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #pragma once #include #include "util/vector.h" #include #include "util/lp/lp_utils.h" #include "util/lp/core_solver_pretty_printer.h" #include "util/lp/numeric_pair.h" #include "util/lp/static_matrix.h" #include "util/lp/lu.h" #include "util/lp/permutation_matrix.h" #include "util/lp/column_namer.h" namespace lp { template // X represents the type of the x variable and the bounds class lp_core_solver_base { unsigned m_total_iterations; unsigned m_iters_with_no_cost_growing; unsigned inc_total_iterations() { ++m_settings.st().m_total_iterations; return m_total_iterations++; } private: lp_status m_status; public: bool current_x_is_feasible() const { TRACE("feas", if (m_inf_set.size()) { tout << "column " << m_inf_set.m_index[0] << " is infeasible" << std::endl; print_column_info(m_inf_set.m_index[0], tout); } else { tout << "x is feasible\n"; } ); return m_inf_set.size() == 0; } bool current_x_is_infeasible() const { return m_inf_set.size() != 0; } int_set m_inf_set; bool m_using_infeas_costs; vector m_columns_nz; // m_columns_nz[i] keeps an approximate value of non zeroes the i-th column vector m_rows_nz; // m_rows_nz[i] keeps an approximate value of non zeroes in the i-th row indexed_vector m_pivot_row_of_B_1; // the pivot row of the reverse of B indexed_vector m_pivot_row; // this is the real pivot row of the simplex tableu static_matrix & m_A; // the matrix A vector & m_b; // the right side vector & m_basis; vector& m_nbasis; vector& m_basis_heading; vector & m_x; // a feasible solution, the fist time set in the constructor vector & m_costs; lp_settings & m_settings; vector m_y; // the buffer for yB = cb // a device that is able to solve Bx=c, xB=d, and change the basis lu> * m_factorization; const column_namer & m_column_names; indexed_vector m_w; // the vector featuring in 24.3 of the Chvatal book vector m_d; // the vector of reduced costs indexed_vector m_ed; // the solution of B*m_ed = a const vector & m_column_types; const vector & m_lower_bounds; const vector & m_upper_bounds; vector m_column_norms; // the approximate squares of column norms that help choosing a profitable column vector m_copy_of_xB; unsigned m_basis_sort_counter; vector m_steepest_edge_coefficients; vector m_trace_of_basis_change_vector; // the even positions are entering, the odd positions are leaving bool m_tracing_basis_changes; int_set* m_pivoted_rows; bool m_look_for_feasible_solution_only; void start_tracing_basis_changes() { m_trace_of_basis_change_vector.resize(0); m_tracing_basis_changes = true; } void stop_tracing_basis_changes() { m_tracing_basis_changes = false; } void trace_basis_change(unsigned entering, unsigned leaving) { unsigned size = m_trace_of_basis_change_vector.size(); if (size >= 2 && m_trace_of_basis_change_vector[size-2] == leaving && m_trace_of_basis_change_vector[size -1] == entering) { m_trace_of_basis_change_vector.pop_back(); m_trace_of_basis_change_vector.pop_back(); } else { m_trace_of_basis_change_vector.push_back(entering); m_trace_of_basis_change_vector.push_back(leaving); } } unsigned m_m() const { return m_A.row_count(); } // it is the length of basis. The matrix m_A has m_m rows and the dimension of the matrix A is m_m unsigned m_n() const { return m_A.column_count(); } // the number of columns in the matrix m_A lp_core_solver_base(static_matrix & A, vector & b, // the right side vector vector & basis, vector & nbasis, vector & heading, vector & x, vector & costs, lp_settings & settings, const column_namer& column_names, const vector & column_types, const vector & lower_bound_values, const vector & upper_bound_values); void allocate_basis_heading(); void init(); virtual ~lp_core_solver_base() { delete m_factorization; } vector & non_basis() { return m_nbasis; } const vector & non_basis() const { return m_nbasis; } void set_status(lp_status status) { m_status = status; } lp_status get_status() const{ return m_status; } void fill_cb(T * y); void fill_cb(vector & y); void solve_yB(vector & y); void solve_Bd(unsigned entering); void solve_Bd(unsigned entering, indexed_vector & column); void pretty_print(std::ostream & out); void save_state(T * w_buffer, T * d_buffer); void restore_state(T * w_buffer, T * d_buffer); X get_cost() { return dot_product(m_costs, m_x); } void copy_m_w(T * buffer); void restore_m_w(T * buffer); // needed for debugging void copy_m_ed(T * buffer); void restore_m_ed(T * buffer); bool A_mult_x_is_off() const; bool A_mult_x_is_off_on_index(const vector & index) const; // from page 182 of Istvan Maros's book void calculate_pivot_row_of_B_1(unsigned pivot_row); void calculate_pivot_row_when_pivot_row_of_B1_is_ready(unsigned pivot_row); void update_x(unsigned entering, const X & delta); const T & get_var_value(unsigned j) const { return m_x[j]; } void print_statistics(char const* str, X cost, std::ostream & message_stream); bool print_statistics_with_iterations_and_check_that_the_time_is_over(std::ostream & message_stream); bool print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(char const* str, std::ostream & message_stream); bool print_statistics_with_cost_and_check_that_the_time_is_over(X cost, std::ostream & message_stream); unsigned total_iterations() const { return m_total_iterations; } void set_total_iterations(unsigned s) { m_total_iterations = s; } void set_non_basic_x_to_correct_bounds(); bool at_bound(const X &x, const X & bound) const { return !below_bound(x, bound) && !above_bound(x, bound); } bool need_to_pivot_to_basis_tableau() const { unsigned m = m_A.row_count(); for (unsigned i = 0; i < m; i++) { unsigned bj = m_basis[i]; lp_assert(m_A.m_columns[bj].size() > 0); if (m_A.m_columns[bj].size() > 1) return true; for (const auto & c : m_A.m_columns[bj]) { if (m_A.get_val(c) != one_of_type()) return true; else break; } } return false; } bool reduced_costs_are_correct_tableau() const { if (m_settings.simplex_strategy() == simplex_strategy_enum::tableau_rows) return true; CASSERT("check_static_matrix", m_A.is_correct()); if (m_using_infeas_costs) { if (infeasibility_costs_are_correct() == false) { return false; } } unsigned n = m_A.column_count(); for (unsigned j = 0; j < n; j++) { if (m_basis_heading[j] >= 0) { if (!is_zero(m_d[j])) { return false; } } else { auto d = m_costs[j]; for (const auto & cc : this->m_A.m_columns[j]) { d -= this->m_costs[this->m_basis[cc.var()]] * this->m_A.get_val(cc); } if (m_d[j] != d) { return false; } } } return true; } bool below_bound(const X & x, const X & bound) const { if (precise()) return x < bound; return below_bound_numeric(x, bound, m_settings.primal_feasibility_tolerance); } bool above_bound(const X & x, const X & bound) const { if (precise()) return x > bound; return above_bound_numeric(x, bound, m_settings.primal_feasibility_tolerance); } bool x_below_low_bound(unsigned p) const { return below_bound(m_x[p], m_lower_bounds[p]); } bool infeasibility_costs_are_correct() const; bool infeasibility_cost_is_correct_for_column(unsigned j) const; bool x_above_lower_bound(unsigned p) const { return above_bound(m_x[p], m_lower_bounds[p]); } bool x_below_upper_bound(unsigned p) const { return below_bound(m_x[p], m_upper_bounds[p]); } bool x_above_upper_bound(unsigned p) const { return above_bound(m_x[p], m_upper_bounds[p]); } bool x_is_at_lower_bound(unsigned j) const { return at_bound(m_x[j], m_lower_bounds[j]); } bool x_is_at_upper_bound(unsigned j) const { return at_bound(m_x[j], m_upper_bounds[j]); } bool x_is_at_bound(unsigned j) const { return x_is_at_lower_bound(j) || x_is_at_upper_bound(j); } bool column_is_feasible(unsigned j) const; bool calc_current_x_is_feasible_include_non_basis() const; bool inf_set_is_correct() const; bool column_is_dual_feasible(unsigned j) const; bool d_is_not_negative(unsigned j) const; bool d_is_not_positive(unsigned j) const; bool time_is_over(); void rs_minus_Anx(vector & rs); bool find_x_by_solving(); bool update_basis_and_x(int entering, int leaving, X const & tt); bool basis_has_no_doubles() const; bool non_basis_has_no_doubles() const; bool basis_is_correctly_represented_in_heading() const ; bool non_basis_is_correctly_represented_in_heading() const ; bool basis_heading_is_correct() const; void restore_x_and_refactor(int entering, int leaving, X const & t); void restore_x(unsigned entering, X const & t); void fill_reduced_costs_from_m_y_by_rows(); void copy_rs_to_xB(vector & rs); virtual bool lower_bounds_are_set() const { return false; } X lower_bound_value(unsigned j) const { return m_lower_bounds[j]; } X upper_bound_value(unsigned j) const { return m_upper_bounds[j]; } column_type get_column_type(unsigned j) const {return m_column_types[j]; } bool pivot_row_element_is_too_small_for_ratio_test(unsigned j) { return m_settings.abs_val_is_smaller_than_pivot_tolerance(m_pivot_row[j]); } X bound_span(unsigned j) const { return m_upper_bounds[j] - m_lower_bounds[j]; } std::string column_name(unsigned column) const; void copy_right_side(vector & rs); void add_delta_to_xB(vector & del); void find_error_in_BxB(vector& rs); // recalculates the projection of x to B, such that Ax = b, whereab is the right side void solve_Ax_eq_b(); bool snap_non_basic_x_to_bound() { bool ret = false; for (unsigned j : non_basis()) ret = snap_column_to_bound(j) || ret; return ret; } bool snap_column_to_bound(unsigned j) { switch (m_column_types[j]) { case column_type::fixed: if (x_is_at_bound(j)) break; m_x[j] = m_lower_bounds[j]; return true; case column_type::boxed: if (x_is_at_bound(j)) break; // we should preserve x if possible // snap randomly if (m_settings.random_next() % 2 == 1) m_x[j] = m_lower_bounds[j]; else m_x[j] = m_upper_bounds[j]; return true; case column_type::lower_bound: if (x_is_at_lower_bound(j)) break; m_x[j] = m_lower_bounds[j]; return true; case column_type::upper_bound: if (x_is_at_upper_bound(j)) break; m_x[j] = m_upper_bounds[j]; return true; default: break; } return false; } bool make_column_feasible(unsigned j, numeric_pair & delta) { bool ret = false; lp_assert(m_basis_heading[j] < 0); auto & x = m_x[j]; switch (m_column_types[j]) { case column_type::fixed: lp_assert(m_lower_bounds[j] == m_upper_bounds[j]); if (x != m_lower_bounds[j]) { delta = m_lower_bounds[j] - x; ret = true;; } break; case column_type::boxed: if (x < m_lower_bounds[j]) { delta = m_lower_bounds[j] - x; ret = true;; } if (x > m_upper_bounds[j]) { delta = m_upper_bounds[j] - x; ret = true; } break; case column_type::lower_bound: if (x < m_lower_bounds[j]) { delta = m_lower_bounds[j] - x; ret = true; } break; case column_type::upper_bound: if (x > m_upper_bounds[j]) { delta = m_upper_bounds[j] - x; ret = true; } break; default: break; } if (ret) add_delta_to_x_and_call_tracker(j, delta); return ret; } void snap_non_basic_x_to_bound_and_free_to_zeroes(); void snap_xN_to_bounds_and_fill_xB(); void snap_xN_to_bounds_and_free_columns_to_zeroes(); void init_reduced_costs_for_one_iteration(); non_basic_column_value_position get_non_basic_column_value_position(unsigned j) const; void init_lu(); int pivots_in_column_and_row_are_different(int entering, int leaving) const; void pivot_fixed_vars_from_basis(); bool remove_from_basis(unsigned j); bool pivot_column_general(unsigned j, unsigned j_basic, indexed_vector & w); bool pivot_for_tableau_on_basis(); bool pivot_row_for_tableau_on_basis(unsigned row); void init_basic_part_of_basis_heading() { unsigned m = m_basis.size(); for (unsigned i = 0; i < m; i++) { unsigned column = m_basis[i]; m_basis_heading[column] = i; } } void init_non_basic_part_of_basis_heading() { this->m_nbasis.clear(); for (int j = m_basis_heading.size(); j--;){ if (m_basis_heading[j] < 0) { m_nbasis.push_back(j); // the index of column j in m_nbasis is (- basis_heading[j] - 1) m_basis_heading[j] = - static_cast(m_nbasis.size()); } } } void init_basis_heading_and_non_basic_columns_vector() { m_basis_heading.resize(0); m_basis_heading.resize(m_n(), -1); init_basic_part_of_basis_heading(); init_non_basic_part_of_basis_heading(); } void change_basis_unconditionally(unsigned entering, unsigned leaving) { lp_assert(m_basis_heading[entering] < 0); int place_in_non_basis = -1 - m_basis_heading[entering]; if (static_cast(place_in_non_basis) >= m_nbasis.size()) { // entering variable in not in m_nbasis, we need to put it back; m_basis_heading[entering] = place_in_non_basis = m_nbasis.size(); m_nbasis.push_back(entering); } int place_in_basis = m_basis_heading[leaving]; m_basis_heading[entering] = place_in_basis; m_basis[place_in_basis] = entering; m_basis_heading[leaving] = -place_in_non_basis - 1; m_nbasis[place_in_non_basis] = leaving; if (m_tracing_basis_changes) trace_basis_change(entering, leaving); } void change_basis(unsigned entering, unsigned leaving) { lp_assert(m_basis_heading[entering] < 0); lp_assert(m_basis_heading[leaving] >= 0); int place_in_basis = m_basis_heading[leaving]; int place_in_non_basis = - m_basis_heading[entering] - 1; m_basis_heading[entering] = place_in_basis; m_basis[place_in_basis] = entering; m_basis_heading[leaving] = -place_in_non_basis - 1; m_nbasis[place_in_non_basis] = leaving; if (m_tracing_basis_changes) trace_basis_change(entering, leaving); } void restore_basis_change(unsigned entering, unsigned leaving) { if (m_basis_heading[entering] < 0) { return; // the basis has not been changed } change_basis_unconditionally(leaving, entering); } bool non_basic_column_is_set_correctly(unsigned j) const { if (j >= this->m_n()) return false; switch (this->m_column_types[j]) { case column_type::fixed: case column_type::boxed: if (!this->x_is_at_bound(j)) return false; break; case column_type::lower_bound: if (!this->x_is_at_lower_bound(j)) return false; break; case column_type::upper_bound: if (!this->x_is_at_upper_bound(j)) return false; break; case column_type::free_column: break; default: lp_assert(false); break; } return true; } bool non_basic_columns_are_set_correctly() const { for (unsigned j : this->m_nbasis) if (!column_is_feasible(j)) { TRACE("lp_core", tout << "inf col "; print_column_info(j, tout) << "\n";); return false; } return true; } void print_column_bound_info(unsigned j, std::ostream & out) const { out << column_name(j) << " type = " << column_type_to_string(m_column_types[j]) << std::endl; switch (m_column_types[j]) { case column_type::fixed: case column_type::boxed: out << "(" << m_lower_bounds[j] << ", " << m_upper_bounds[j] << ")" << std::endl; break; case column_type::lower_bound: out << m_lower_bounds[j] << std::endl; break; case column_type::upper_bound: out << m_upper_bounds[j] << std::endl; break; default: break; } } std::ostream& print_column_info(unsigned j, std::ostream & out) const { out << "j = " << j << ",\tname = "<< column_name(j) << "\t"; switch (m_column_types[j]) { case column_type::fixed: case column_type::boxed: out << " [" << m_lower_bounds[j] << ", " << m_upper_bounds[j] << "]"; break; case column_type::lower_bound: out << " [" << m_lower_bounds[j] << "," << "oo" << "]"; break; case column_type::upper_bound: out << " [-oo, " << m_upper_bounds[j] << ']'; break; case column_type::free_column: out << " [-oo, oo]"; break; default: lp_assert(false); } // out << "basis heading = " << m_basis_heading[j] << std::endl; out << "\tx = " << m_x[j]; if (m_basis_heading[j] >= 0) out << " base\n"; else out << " \n"; return out; } bool column_is_free(unsigned j) const { return this->m_column_type[j] == free; } bool column_has_upper_bound(unsigned j) const { switch(m_column_types[j]) { case column_type::free_column: case column_type::lower_bound: return false; default: return true; } } bool bounds_for_boxed_are_set_correctly() const { for (unsigned j = 0; j < m_column_types.size(); j++) { if (m_column_types[j] != column_type::boxed) continue; if (m_lower_bounds[j] > m_upper_bounds[j]) return false; } return true; } bool column_has_lower_bound(unsigned j) const { switch(m_column_types[j]) { case column_type::free_column: case column_type::upper_bound: return false; default: return true; } } // only check for basic columns bool calc_current_x_is_feasible() const { unsigned i = this->m_m(); while (i--) { if (!column_is_feasible(m_basis[i])) return false; } return true; } void transpose_rows_tableau(unsigned i, unsigned ii); void pivot_to_reduced_costs_tableau(unsigned i, unsigned j); bool pivot_column_tableau(unsigned j, unsigned row_index); bool divide_row_by_pivot(unsigned pivot_row, unsigned pivot_col); bool precise() const { return numeric_traits::precise(); } simplex_strategy_enum simplex_strategy() const { return m_settings.simplex_strategy(); } bool use_tableau() const { return m_settings.use_tableau(); } template static void swap(vector &v, unsigned i, unsigned j) { auto t = v[i]; v[i] = v[j]; v[j] = t; } // called when transposing row i and ii void transpose_basis(unsigned i, unsigned ii) { swap(m_basis, i, ii); swap(m_basis_heading, m_basis[i], m_basis[ii]); } bool column_is_in_inf_set(unsigned j) const { return m_inf_set.contains(j); } void update_x_with_feasibility_tracking(unsigned j, const X & v) { m_x[j] = v; track_column_feasibility(j); } void update_x_with_delta_and_track_feasibility(unsigned j, const X & del) { m_x[j] += del; track_column_feasibility(j); } void update_x_and_call_tracker(unsigned j, const X & v) { m_x[j] = v; } void add_delta_to_x_and_call_tracker(unsigned j, const X & delta) { m_x[j] += delta; } void track_column_feasibility(unsigned j) { if (column_is_feasible(j)) remove_column_from_inf_set(j); else insert_column_into_inf_set(j); } void insert_column_into_inf_set(unsigned j) { m_inf_set.insert(j); lp_assert(!column_is_feasible(j)); } void remove_column_from_inf_set(unsigned j) { m_inf_set.erase(j); lp_assert(column_is_feasible(j)); } bool costs_on_nbasis_are_zeros() const { lp_assert(this->basis_heading_is_correct()); for (unsigned j = 0; j < this->m_n(); j++) { if (this->m_basis_heading[j] < 0) lp_assert(is_zero(this->m_costs[j])); } return true; } unsigned & iters_with_no_cost_growing() { return m_iters_with_no_cost_growing; } const unsigned & iters_with_no_cost_growing() const { return m_iters_with_no_cost_growing; } void calculate_pivot_row(unsigned i); unsigned get_base_column_in_row(unsigned row_index) const { return m_basis[row_index]; } }; } z3-z3-4.8.7/src/util/lp/lp_core_solver_base_def.h000066400000000000000000001023521356505360400216070ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #include #include #include "util/vector.h" #include "util/lp/lp_utils.h" #include "util/lp/lp_core_solver_base.h" namespace lp { template lp_core_solver_base:: lp_core_solver_base(static_matrix & A, vector & b, // the right side vector vector & basis, vector & nbasis, vector & heading, vector & x, vector & costs, lp_settings & settings, const column_namer& column_names, const vector & column_types, const vector & lower_bound_values, const vector & upper_bound_values): m_total_iterations(0), m_iters_with_no_cost_growing(0), m_status(lp_status::FEASIBLE), m_inf_set(A.column_count()), m_using_infeas_costs(false), m_pivot_row_of_B_1(A.row_count()), m_pivot_row(A.column_count()), m_A(A), m_b(b), m_basis(basis), m_nbasis(nbasis), m_basis_heading(heading), m_x(x), m_costs(costs), m_settings(settings), m_y(m_m()), m_factorization(nullptr), m_column_names(column_names), m_w(m_m()), m_d(m_n()), m_ed(m_m()), m_column_types(column_types), m_lower_bounds(lower_bound_values), m_upper_bounds(upper_bound_values), m_column_norms(m_n()), m_copy_of_xB(m_m()), m_basis_sort_counter(0), m_steepest_edge_coefficients(A.column_count()), m_tracing_basis_changes(false), m_pivoted_rows(nullptr), m_look_for_feasible_solution_only(false) { lp_assert(bounds_for_boxed_are_set_correctly()); init(); init_basis_heading_and_non_basic_columns_vector(); } template void lp_core_solver_base:: allocate_basis_heading() { // the rest of initialization will be handled by the factorization class init_basis_heading_and_non_basic_columns_vector(); lp_assert(basis_heading_is_correct()); } template void lp_core_solver_base:: init() { allocate_basis_heading(); if (m_settings.use_lu()) init_factorization(m_factorization, m_A, m_basis, m_settings); } template bool lp_core_solver_base:: pivot_for_tableau_on_basis() { m_d = m_costs; // we will be pivoting to m_d as well unsigned m = m_A.row_count(); for (unsigned i = 0; i < m; i++) if (!pivot_column_tableau(m_basis[i], i)) return false; return true; } // i is the pivot row, and j is the pivot column template void lp_core_solver_base:: pivot_to_reduced_costs_tableau(unsigned i, unsigned j) { if (j >= m_d.size()) return; T &a = m_d[j]; if (is_zero(a)) return; for (const row_cell & r: m_A.m_rows[i]){ if (r.var() != j) m_d[r.var()] -= a * r.get_val(); } a = zero_of_type(); // zero the pivot column's m_d finally } template void lp_core_solver_base:: fill_cb(T * y){ for (unsigned i = 0; i < m_m(); i++) { y[i] = m_costs[m_basis[i]]; } } template void lp_core_solver_base:: fill_cb(vector & y){ for (unsigned i = 0; i < m_m(); i++) { y[i] = m_costs[m_basis[i]]; } } template void lp_core_solver_base:: solve_yB(vector & y) { fill_cb(y); // now y = cB, that is the projection of costs to basis m_factorization->solve_yB_with_error_check(y, m_basis); } // template void lp_core_solver_base:: // update_index_of_ed() { // m_index_of_ed.clear(); // unsigned i = static_cast(m_ed.size()); // while (i--) { // if (!is_zero(m_ed[i])) // m_index_of_ed.push_back(i); // } // } template void lp_core_solver_base::solve_Bd(unsigned entering, indexed_vector & column) { lp_assert(!m_settings.use_tableau()); if (m_factorization == nullptr) { init_factorization(m_factorization, m_A, m_basis, m_settings); } m_factorization->solve_Bd_faster(entering, column); } template void lp_core_solver_base:: solve_Bd(unsigned entering) { lp_assert(m_ed.is_OK()); m_factorization->solve_Bd(entering, m_ed, m_w); if (this->precise()) m_columns_nz[entering] = m_ed.m_index.size(); lp_assert(m_ed.is_OK()); lp_assert(m_w.is_OK()); #ifdef Z3DEBUG // auto B = get_B(*m_factorization, m_basis); // vector a(m_m()); // m_A.copy_column_to_vector(entering, a); // vector cd(m_ed.m_data); // B.apply_from_left(cd, m_settings); // lp_assert(vectors_are_equal(cd , a)); #endif } template void lp_core_solver_base:: pretty_print(std::ostream & out) { core_solver_pretty_printer pp(*this, out); pp.print(); } template void lp_core_solver_base:: save_state(T * w_buffer, T * d_buffer) { copy_m_w(w_buffer); copy_m_ed(d_buffer); } template void lp_core_solver_base:: restore_state(T * w_buffer, T * d_buffer) { restore_m_w(w_buffer); restore_m_ed(d_buffer); } template void lp_core_solver_base:: copy_m_w(T * buffer) { unsigned i = m_m(); while (i --) { buffer[i] = m_w[i]; } } template void lp_core_solver_base:: restore_m_w(T * buffer) { m_w.m_index.clear(); unsigned i = m_m(); while (i--) { if (!is_zero(m_w[i] = buffer[i])) m_w.m_index.push_back(i); } } // needed for debugging template void lp_core_solver_base:: copy_m_ed(T * buffer) { unsigned i = m_m(); while (i --) { buffer[i] = m_ed[i]; } } template void lp_core_solver_base:: restore_m_ed(T * buffer) { unsigned i = m_m(); while (i --) { m_ed[i] = buffer[i]; } } template bool lp_core_solver_base:: A_mult_x_is_off() const { lp_assert(m_x.size() == m_A.column_count()); if (numeric_traits::precise()) { for (unsigned i = 0; i < m_m(); i++) { X delta = m_b[i] - m_A.dot_product_with_row(i, m_x); if (delta != numeric_traits::zero()) { return true; } } return false; } T feps = convert_struct::convert(m_settings.refactor_tolerance); X one = convert_struct::convert(1.0); for (unsigned i = 0; i < m_m(); i++) { X delta = abs(m_b[i] - m_A.dot_product_with_row(i, m_x)); X eps = feps * (one + T(0.1) * abs(m_b[i])); if (delta > eps) { #if 0 LP_OUT(m_settings, "x is off (" << "m_b[" << i << "] = " << m_b[i] << " " << "left side = " << m_A.dot_product_with_row(i, m_x) << ' ' << "delta = " << delta << ' ' << "iters = " << total_iterations() << ")" << std::endl); #endif return true; } } return false; } template bool lp_core_solver_base:: A_mult_x_is_off_on_index(const vector & index) const { lp_assert(m_x.size() == m_A.column_count()); if (numeric_traits::precise()) return false; #if RUN_A_MULT_X_IS_OFF_FOR_PRECESE for (unsigned i : index) { X delta = m_b[i] - m_A.dot_product_with_row(i, m_x); if (delta != numeric_traits::zero()) { return true; } } return false; #endif // todo(levnach) run on m_ed.m_index only !!!!! T feps = convert_struct::convert(m_settings.refactor_tolerance); X one = convert_struct::convert(1.0); for (unsigned i : index) { X delta = abs(m_b[i] - m_A.dot_product_with_row(i, m_x)); X eps = feps * (one + T(0.1) * abs(m_b[i])); if (delta > eps) { #if 0 LP_OUT(m_settings, "x is off (" << "m_b[" << i << "] = " << m_b[i] << " " << "left side = " << m_A.dot_product_with_row(i, m_x) << ' ' << "delta = " << delta << ' ' << "iters = " << total_iterations() << ")" << std::endl); #endif return true; } } return false; } // from page 182 of Istvan Maros's book template void lp_core_solver_base:: calculate_pivot_row_of_B_1(unsigned pivot_row) { lp_assert(! use_tableau()); lp_assert(m_pivot_row_of_B_1.is_OK()); m_pivot_row_of_B_1.clear(); m_pivot_row_of_B_1.set_value(numeric_traits::one(), pivot_row); lp_assert(m_pivot_row_of_B_1.is_OK()); m_factorization->solve_yB_with_error_check_indexed(m_pivot_row_of_B_1, m_basis_heading, m_basis, m_settings); lp_assert(m_pivot_row_of_B_1.is_OK()); } template void lp_core_solver_base:: calculate_pivot_row_when_pivot_row_of_B1_is_ready(unsigned pivot_row) { m_pivot_row.clear(); for (unsigned i : m_pivot_row_of_B_1.m_index) { const T & pi_1 = m_pivot_row_of_B_1[i]; if (numeric_traits::is_zero(pi_1)) { continue; } for (auto & c : m_A.m_rows[i]) { unsigned j = c.var(); if (m_basis_heading[j] < 0) { m_pivot_row.add_value_at_index_with_drop_tolerance(j, c.get_val() * pi_1); } } } if (precise()) { m_rows_nz[pivot_row] = m_pivot_row.m_index.size(); } } template void lp_core_solver_base:: update_x(unsigned entering, const X& delta) { m_x[entering] += delta; if (!use_tableau()) for (unsigned i : m_ed.m_index) { if (!numeric_traits::precise()) m_copy_of_xB[i] = m_x[m_basis[i]]; m_x[m_basis[i]] -= delta * m_ed[i]; } else for (const auto & c : m_A.m_columns[entering]) { unsigned i = c.var(); m_x[m_basis[i]] -= delta * m_A.get_val(c); } } template void lp_core_solver_base:: print_statistics(char const* str, X cost, std::ostream & out) { if (str!= nullptr) out << str << " "; out << "iterations = " << (total_iterations() - 1) << ", cost = " << T_to_string(cost) << ", nonzeros = " << (m_factorization != nullptr? m_factorization->get_number_of_nonzeroes() : m_A.number_of_non_zeroes()) << std::endl; } template bool lp_core_solver_base:: print_statistics_with_iterations_and_check_that_the_time_is_over(std::ostream & str) { unsigned total_iterations = inc_total_iterations(); if (m_settings.report_frequency != 0) { if (m_settings.print_statistics && (total_iterations % m_settings.report_frequency == 0)) { print_statistics("", X(), str); } } return time_is_over(); } template bool lp_core_solver_base:: print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(char const* str, std::ostream & out) { unsigned total_iterations = inc_total_iterations(); if (m_settings.report_frequency != 0) if (m_settings.print_statistics && (total_iterations % m_settings.report_frequency == 0)) { print_statistics(str, get_cost(), out); } return time_is_over(); } template bool lp_core_solver_base:: print_statistics_with_cost_and_check_that_the_time_is_over(X cost, std::ostream & out) { unsigned total_iterations = inc_total_iterations(); if (m_settings.report_frequency != 0) if (m_settings.print_statistics && (total_iterations % m_settings.report_frequency == 0)) { print_statistics("", cost, out); } return time_is_over(); } template void lp_core_solver_base:: set_non_basic_x_to_correct_bounds() { for (unsigned j : non_basis()) { switch (m_column_types[j]) { case column_type::boxed: m_x[j] = m_d[j] < 0? m_upper_bounds[j]: m_lower_bounds[j]; break; case column_type::lower_bound: m_x[j] = m_lower_bounds[j]; lp_assert(column_is_dual_feasible(j)); break; case column_type::upper_bound: m_x[j] = m_upper_bounds[j]; lp_assert(column_is_dual_feasible(j)); break; default: break; } } } template bool lp_core_solver_base:: column_is_dual_feasible(unsigned j) const { switch (m_column_types[j]) { case column_type::fixed: case column_type::boxed: return (x_is_at_lower_bound(j) && d_is_not_negative(j)) || (x_is_at_upper_bound(j) && d_is_not_positive(j)); case column_type::lower_bound: return x_is_at_lower_bound(j) && d_is_not_negative(j); case column_type::upper_bound: lp_assert(false); // impossible case case column_type::free_column: return numeric_traits::is_zero(m_d[j]); default: lp_unreachable(); } lp_unreachable(); return false; } template bool lp_core_solver_base:: d_is_not_negative(unsigned j) const { if (numeric_traits::precise()) { return m_d[j] >= numeric_traits::zero(); } return m_d[j] > -T(0.00001); } template bool lp_core_solver_base:: d_is_not_positive(unsigned j) const { if (numeric_traits::precise()) { return m_d[j] <= numeric_traits::zero(); } return m_d[j] < T(0.00001); } template bool lp_core_solver_base:: time_is_over() { if (m_settings.get_cancel_flag()) { m_status = lp_status::TIME_EXHAUSTED; return true; } else { return false; } } template void lp_core_solver_base:: rs_minus_Anx(vector & rs) { unsigned row = m_m(); while (row--) { auto &rsv = rs[row] = m_b[row]; for (auto & it : m_A.m_rows[row]) { unsigned j = it.var(); if (m_basis_heading[j] < 0) { rsv -= m_x[j] * it.get_val(); } } } } template bool lp_core_solver_base:: find_x_by_solving() { solve_Ax_eq_b(); bool ret= !A_mult_x_is_off(); return ret; } template bool lp_core_solver_base::column_is_feasible(unsigned j) const { const X& x = this->m_x[j]; switch (this->m_column_types[j]) { case column_type::fixed: case column_type::boxed: if (this->above_bound(x, this->m_upper_bounds[j])) { return false; } else if (this->below_bound(x, this->m_lower_bounds[j])) { return false; } else { return true; } break; case column_type::lower_bound: if (this->below_bound(x, this->m_lower_bounds[j])) { return false; } else { return true; } break; case column_type::upper_bound: if (this->above_bound(x, this->m_upper_bounds[j])) { return false; } else { return true; } break; case column_type::free_column: return true; break; default: lp_unreachable(); } return false; // it is unreachable } template bool lp_core_solver_base::calc_current_x_is_feasible_include_non_basis() const { unsigned j = this->m_n(); while (j--) { if (!column_is_feasible(j)) { return false; } } return true; } template bool lp_core_solver_base::inf_set_is_correct() const { for (unsigned j = 0; j < this->m_n(); j++) { bool belongs_to_set = m_inf_set.contains(j); bool is_feas = column_is_feasible(j); if (is_feas == belongs_to_set) { TRACE("lp_core", tout << "incorrectly set column in inf set "; print_column_info(j, tout) << "\n";); return false; } } return true; } template bool lp_core_solver_base:: update_basis_and_x(int entering, int leaving, X const & tt) { if (!is_zero(tt)) { update_x(entering, tt); if ((!numeric_traits::precise()) && A_mult_x_is_off_on_index(m_ed.m_index) && !find_x_by_solving()) { init_factorization(m_factorization, m_A, m_basis, m_settings); if (!find_x_by_solving()) { restore_x(entering, tt); if(A_mult_x_is_off()) { m_status = lp_status::FLOATING_POINT_ERROR; m_iters_with_no_cost_growing++; return false; } init_factorization(m_factorization, m_A, m_basis, m_settings); m_iters_with_no_cost_growing++; if (m_factorization->get_status() != LU_status::OK) { std::stringstream s; // s << "failing refactor on off_result for entering = " << entering << ", leaving = " << leaving << " total_iterations = " << total_iterations(); m_status = lp_status::FLOATING_POINT_ERROR; return false; } return false; } } } bool refactor = m_factorization->need_to_refactor(); if (!refactor) { const T & pivot = this->m_pivot_row[entering]; // m_ed[m_factorization->basis_heading(leaving)] is the same but the one that we are using is more precise m_factorization->replace_column(pivot, m_w, m_basis_heading[leaving]); if (m_factorization->get_status() == LU_status::OK) { change_basis(entering, leaving); return true; } } // need to refactor == true change_basis(entering, leaving); init_lu(); if (m_factorization->get_status() != LU_status::OK) { if (m_look_for_feasible_solution_only && !precise()) { m_status = lp_status::UNSTABLE; delete m_factorization; m_factorization = nullptr; return false; } // LP_OUT(m_settings, "failing refactor for entering = " << entering << ", leaving = " << leaving << " total_iterations = " << total_iterations() << std::endl); restore_x_and_refactor(entering, leaving, tt); if (m_status == lp_status::FLOATING_POINT_ERROR) return false; CASSERT("A_off", !A_mult_x_is_off()); m_iters_with_no_cost_growing++; // LP_OUT(m_settings, "rolled back after failing of init_factorization()" << std::endl); m_status = lp_status::UNSTABLE; return false; } return true; } template bool lp_core_solver_base:: divide_row_by_pivot(unsigned pivot_row, unsigned pivot_col) { lp_assert(numeric_traits::precise()); int pivot_index = -1; auto & row = m_A.m_rows[pivot_row]; unsigned size = row.size(); for (unsigned j = 0; j < size; j++) { auto & c = row[j]; if (c.var() == pivot_col) { pivot_index = static_cast(j); break; } } if (pivot_index == -1) return false; auto & pivot_cell = row[pivot_index]; T & coeff = pivot_cell.coeff(); if (is_zero(coeff)) return false; this->m_b[pivot_row] /= coeff; for (unsigned j = 0; j < size; j++) { auto & c = row[j]; if (c.var() != pivot_col) { c.coeff() /= coeff; } } coeff = one_of_type(); CASSERT("check_static_matrix", m_A.is_correct()); return true; } template bool lp_core_solver_base:: pivot_column_tableau(unsigned j, unsigned piv_row_index) { if (!divide_row_by_pivot(piv_row_index, j)) return false; auto &column = m_A.m_columns[j]; int pivot_col_cell_index = -1; for (unsigned k = 0; k < column.size(); k++) { if (column[k].var() == piv_row_index) { pivot_col_cell_index = k; break; } } if (pivot_col_cell_index < 0) return false; if (pivot_col_cell_index != 0) { lp_assert(column.size() > 1); // swap the pivot column cell with the head cell auto c = column[0]; column[0] = column[pivot_col_cell_index]; column[pivot_col_cell_index] = c; m_A.m_rows[piv_row_index][column[0].m_offset].m_offset = 0; m_A.m_rows[c.var()][c.m_offset].m_offset = pivot_col_cell_index; } while (column.size() > 1) { auto & c = column.back(); lp_assert(c.var() != piv_row_index); if(! m_A.pivot_row_to_row_given_cell(piv_row_index, c, j)) { return false; } if (m_pivoted_rows!= nullptr) m_pivoted_rows->insert(c.var()); } if (m_settings.simplex_strategy() == simplex_strategy_enum::tableau_costs) pivot_to_reduced_costs_tableau(piv_row_index, j); return true; } template bool lp_core_solver_base:: basis_has_no_doubles() const { std::set bm; for (unsigned i = 0; i < m_m(); i++) { bm.insert(m_basis[i]); } return bm.size() == m_m(); } template bool lp_core_solver_base:: non_basis_has_no_doubles() const { std::set bm; for (auto j : m_nbasis) { bm.insert(j); } return bm.size() == m_nbasis.size(); } template bool lp_core_solver_base:: basis_is_correctly_represented_in_heading() const { for (unsigned i = 0; i < m_m(); i++) { if (m_basis_heading[m_basis[i]] != static_cast(i)) return false; } return true; } template bool lp_core_solver_base:: non_basis_is_correctly_represented_in_heading() const { for (unsigned i = 0; i < m_nbasis.size(); i++) { if (m_basis_heading[m_nbasis[i]] != - static_cast(i) - 1) return false; } for (unsigned j = 0; j < m_A.column_count(); j++) { if (m_basis_heading[j] >= 0) { lp_assert(static_cast(m_basis_heading[j]) < m_A.row_count() && m_basis[m_basis_heading[j]] == j); } } return true; } template bool lp_core_solver_base:: basis_heading_is_correct() const { lp_assert(m_basis_heading.size() == m_A.column_count()); lp_assert(m_basis.size() == m_A.row_count()); lp_assert(m_nbasis.size() <= m_A.column_count() - m_A.row_count()); // for the dual the size of non basis can be smaller if (!basis_has_no_doubles()) { return false; } if (!non_basis_has_no_doubles()) { return false; } if (!basis_is_correctly_represented_in_heading()) { return false; } if (!non_basis_is_correctly_represented_in_heading()) { return false; } return true; } template void lp_core_solver_base:: restore_x_and_refactor(int entering, int leaving, X const & t) { this->restore_basis_change(entering, leaving); restore_x(entering, t); init_factorization(m_factorization, m_A, m_basis, m_settings); if (m_factorization->get_status() == LU_status::Degenerated) { LP_OUT(m_settings, "cannot refactor" << std::endl); m_status = lp_status::FLOATING_POINT_ERROR; return; } // solve_Ax_eq_b(); if (A_mult_x_is_off()) { LP_OUT(m_settings, "cannot restore solution" << std::endl); m_status = lp_status::FLOATING_POINT_ERROR; return; } } template void lp_core_solver_base:: restore_x(unsigned entering, X const & t) { if (is_zero(t)) return; m_x[entering] -= t; for (unsigned i : m_ed.m_index) { m_x[m_basis[i]] = m_copy_of_xB[i]; } } template void lp_core_solver_base:: fill_reduced_costs_from_m_y_by_rows() { unsigned j = m_n(); while (j--) { if (m_basis_heading[j] < 0) m_d[j] = m_costs[j]; else m_d[j] = numeric_traits::zero(); } unsigned i = m_m(); while (i--) { const T & y = m_y[i]; if (is_zero(y)) continue; for (row_cell & c : m_A.m_rows[i]) { j = c.var(); if (m_basis_heading[j] < 0) { m_d[j] -= y * c.get_val(); } } } } template void lp_core_solver_base:: copy_rs_to_xB(vector & rs) { unsigned j = m_m(); while (j--) { m_x[m_basis[j]] = rs[j]; } } template std::string lp_core_solver_base:: column_name(unsigned column) const { return m_column_names.get_column_name(column); } template void lp_core_solver_base:: copy_right_side(vector & rs) { unsigned i = m_m(); while (i --) { rs[i] = m_b[i]; } } template void lp_core_solver_base:: add_delta_to_xB(vector & del) { unsigned i = m_m(); while (i--) { this->m_x[this->m_basis[i]] -= del[i]; } } template void lp_core_solver_base:: find_error_in_BxB(vector& rs){ unsigned row = m_m(); while (row--) { auto &rsv = rs[row]; for (auto & it : m_A.m_rows[row]) { unsigned j = it.var(); if (m_basis_heading[j] >= 0) { rsv -= m_x[j] * it.get_val(); } } } } // recalculates the projection of x to B, such that Ax = b template void lp_core_solver_base:: solve_Ax_eq_b() { if (numeric_traits::precise()) { vector rs(m_m()); rs_minus_Anx(rs); m_factorization->solve_By(rs); copy_rs_to_xB(rs); } else { vector rs(m_m()); rs_minus_Anx(rs); vector rrs = rs; // another copy of rs m_factorization->solve_By(rs); copy_rs_to_xB(rs); find_error_in_BxB(rrs); m_factorization->solve_By(rrs); add_delta_to_xB(rrs); } } template void lp_core_solver_base:: snap_non_basic_x_to_bound_and_free_to_zeroes() { for (unsigned j : non_basis()) { lp_assert(j < m_x.size()); switch (m_column_types[j]) { case column_type::fixed: case column_type::boxed: case column_type::lower_bound: m_x[j] = m_lower_bounds[j]; break; case column_type::upper_bound: m_x[j] = m_upper_bounds[j]; break; default: m_x[j] = zero_of_type(); break; } } } template void lp_core_solver_base:: snap_xN_to_bounds_and_fill_xB() { snap_non_basic_x_to_bound(); solve_Ax_eq_b(); } template void lp_core_solver_base:: snap_xN_to_bounds_and_free_columns_to_zeroes() { snap_non_basic_x_to_bound_and_free_to_zeroes(); solve_Ax_eq_b(); } template void lp_core_solver_base:: init_reduced_costs_for_one_iteration() { solve_yB(m_y); fill_reduced_costs_from_m_y_by_rows(); } template non_basic_column_value_position lp_core_solver_base:: get_non_basic_column_value_position(unsigned j) const { switch (m_column_types[j]) { case column_type::fixed: return x_is_at_lower_bound(j)? at_fixed : not_at_bound; case column_type::free_column: return free_of_bounds; case column_type::boxed: return x_is_at_lower_bound(j)? at_lower_bound :( x_is_at_upper_bound(j)? at_upper_bound: not_at_bound ); case column_type::lower_bound: return x_is_at_lower_bound(j)? at_lower_bound : not_at_bound; case column_type::upper_bound: return x_is_at_upper_bound(j)? at_upper_bound : not_at_bound; default: lp_unreachable(); } lp_unreachable(); return at_lower_bound; } template void lp_core_solver_base::init_lu() { init_factorization(this->m_factorization, this->m_A, this->m_basis, this->m_settings); } template int lp_core_solver_base::pivots_in_column_and_row_are_different(int entering, int leaving) const { const T & column_p = this->m_ed[this->m_basis_heading[leaving]]; const T & row_p = this->m_pivot_row[entering]; if (is_zero(column_p) || is_zero(row_p)) return true; // pivots cannot be zero // the pivots have to have the same sign if (column_p < 0) { if (row_p > 0) return 2; } else { // column_p > 0 if (row_p < 0) return 2; } T diff_normalized = abs((column_p - row_p) / (numeric_traits::one() + abs(row_p))); if ( !this->m_settings.abs_val_is_smaller_than_harris_tolerance(diff_normalized / T(10))) return 1; return 0; } template void lp_core_solver_base::transpose_rows_tableau(unsigned i, unsigned j) { transpose_basis(i, j); m_A.transpose_rows(i, j); } // j is the new basic column, j_basic - the leaving column template bool lp_core_solver_base::pivot_column_general(unsigned j, unsigned j_basic, indexed_vector & w) { lp_assert(m_basis_heading[j] < 0); lp_assert(m_basis_heading[j_basic] >= 0); unsigned row_index = m_basis_heading[j_basic]; if (m_settings.m_simplex_strategy == simplex_strategy_enum::lu) { if (m_factorization->need_to_refactor()) { init_lu(); } else { m_factorization->prepare_entering(j, w); // to init vector w m_factorization->replace_column(zero_of_type(), w, row_index); } if (m_factorization->get_status() != LU_status::OK) { init_lu(); return false; } else { change_basis(j, j_basic); } } else { // the tableau case if (pivot_column_tableau(j, row_index)) change_basis(j, j_basic); else return false; } return true; } template void lp_core_solver_base::pivot_fixed_vars_from_basis() { // run over basis and non-basis at the same time indexed_vector w(m_basis.size()); // the buffer unsigned i = 0; // points to basis for (; i < m_basis.size(); i++) { unsigned basic_j = m_basis[i]; if (get_column_type(basic_j) != column_type::fixed) continue; T a; unsigned j; for (auto &c : m_A.m_rows[i]) { j = c.var(); if (j == basic_j) continue; if (get_column_type(j) != column_type::fixed) { if (pivot_column_general(j, basic_j, w)) break; } } } } template bool lp_core_solver_base::remove_from_basis(unsigned basic_j) { indexed_vector w(m_basis.size()); // the buffer unsigned i = m_basis_heading[basic_j]; for (auto &c : m_A.m_rows[i]) { if (c.var() == basic_j) continue; if (pivot_column_general(c.var(), basic_j, w)) return true; } return false; } template bool lp_core_solver_base::infeasibility_costs_are_correct() const { if (! this->m_using_infeas_costs) return true; lp_assert(costs_on_nbasis_are_zeros()); for (unsigned j :this->m_basis) { if (!infeasibility_cost_is_correct_for_column(j)) { return false; } if (!is_zero(m_d[j])) { return false; } } return true; } template bool lp_core_solver_base::infeasibility_cost_is_correct_for_column(unsigned j) const { T r = (!this->m_settings.use_breakpoints_in_feasibility_search)? -one_of_type(): one_of_type(); switch (this->m_column_types[j]) { case column_type::fixed: case column_type::boxed: if (this->x_above_upper_bound(j)) { return (this->m_costs[j] == r); } if (this->x_below_low_bound(j)) { return (this->m_costs[j] == -r); } return is_zero(this->m_costs[j]); case column_type::lower_bound: if (this->x_below_low_bound(j)) { return this->m_costs[j] == -r; } return is_zero(this->m_costs[j]); case column_type::upper_bound: if (this->x_above_upper_bound(j)) { return this->m_costs[j] == r; } return is_zero(this->m_costs[j]); case column_type::free_column: return is_zero(this->m_costs[j]); default: lp_assert(false); return true; } } template void lp_core_solver_base::calculate_pivot_row(unsigned i) { lp_assert(!use_tableau()); lp_assert(m_pivot_row.is_OK()); m_pivot_row_of_B_1.clear(); m_pivot_row_of_B_1.resize(m_m()); m_pivot_row.clear(); m_pivot_row.resize(m_n()); if (m_settings.use_tableau()) { unsigned basic_j = m_basis[i]; for (auto & c : m_A.m_rows[i]) { if (c.var() != basic_j) m_pivot_row.set_value(c.get_val(), c.var()); } return; } calculate_pivot_row_of_B_1(i); calculate_pivot_row_when_pivot_row_of_B1_is_ready(i); } } z3-z3-4.8.7/src/util/lp/lp_dual_core_solver.cpp000066400000000000000000000037651356505360400213470ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #include #include #include #include "util/vector.h" #include #include "util/lp/lp_dual_core_solver_def.h" template void lp::lp_dual_core_solver::start_with_initial_basis_and_make_it_dual_feasible(); template void lp::lp_dual_core_solver::solve(); template lp::lp_dual_core_solver::lp_dual_core_solver(lp::static_matrix&, vector&, vector&, vector&, vector&, vector &, vector &, vector&, vector&, vector&, vector&, lp::lp_settings&, const lp::column_namer&); template void lp::lp_dual_core_solver::start_with_initial_basis_and_make_it_dual_feasible(); template void lp::lp_dual_core_solver::solve(); template void lp::lp_dual_core_solver::restore_non_basis(); template void lp::lp_dual_core_solver::restore_non_basis(); template void lp::lp_dual_core_solver::revert_to_previous_basis(); template void lp::lp_dual_core_solver::revert_to_previous_basis(); z3-z3-4.8.7/src/util/lp/lp_dual_core_solver.h000066400000000000000000000140131356505360400210000ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #pragma once #include "util/lp/static_matrix.h" #include "util/lp/lp_core_solver_base.h" #include #include #include #include #include "util/vector.h" namespace lp { template class lp_dual_core_solver:public lp_core_solver_base { public: vector & m_can_enter_basis; int m_r; // the row of the leaving column int m_p; // leaving column; that is m_p = m_basis[m_r] T m_delta; // the offset of the leaving basis variable int m_sign_of_alpha_r; // see page 27 T m_theta_D; T m_theta_P; int m_q; // todo : replace by a vector later std::set m_breakpoint_set; // it is F in "Progress in the dual simplex method ..." std::set m_flipped_boxed; std::set m_tight_set; // it is the set of all breakpoints that become tight when m_q becomes tight vector m_a_wave; vector m_betas; // m_betas[i] is approximately a square of the norm of the i-th row of the reverse of B T m_harris_tolerance; std::set m_forbidden_rows; lp_dual_core_solver(static_matrix & A, vector & can_enter_basis, vector & b, // the right side vector vector & x, // the number of elements in x needs to be at least as large as the number of columns in A vector & basis, vector & nbasis, vector & heading, vector & costs, vector & column_type_array, vector & lower_bound_values, vector & upper_bound_values, lp_settings & settings, const column_namer & column_names): lp_core_solver_base(A, b, basis, nbasis, heading, x, costs, settings, column_names, column_type_array, lower_bound_values, upper_bound_values), m_can_enter_basis(can_enter_basis), m_a_wave(this->m_m()), m_betas(this->m_m()) { m_harris_tolerance = numeric_traits::precise()? numeric_traits::zero() : T(this->m_settings.harris_feasibility_tolerance); this->solve_yB(this->m_y); this->init_basic_part_of_basis_heading(); fill_non_basis_with_only_able_to_enter_columns(); } void init_a_wave_by_zeros(); void fill_non_basis_with_only_able_to_enter_columns() { auto & nb = this->m_nbasis; nb.reset(); unsigned j = this->m_n(); while (j--) { if (this->m_basis_heading[j] >= 0 || !m_can_enter_basis[j]) continue; nb.push_back(j); this->m_basis_heading[j] = - static_cast(nb.size()); } } void restore_non_basis(); bool update_basis(int entering, int leaving); void recalculate_xB_and_d(); void recalculate_d(); void init_betas(); void adjust_xb_for_changed_xn_and_init_betas(); void start_with_initial_basis_and_make_it_dual_feasible(); bool done(); T get_edge_steepness_for_lower_bound(unsigned p); T get_edge_steepness_for_upper_bound(unsigned p); T pricing_for_row(unsigned i); void pricing_loop(unsigned number_of_rows_to_try, unsigned offset_in_rows); bool advance_on_known_p(); int define_sign_of_alpha_r(); bool can_be_breakpoint(unsigned j); void fill_breakpoint_set(); void DSE_FTran(); T get_delta(); void restore_d(); bool d_is_correct(); void xb_minus_delta_p_pivot_column(); void update_betas(); void apply_flips(); void snap_xN_column_to_bounds(unsigned j); void snap_xN_to_bounds(); void init_beta_precisely(unsigned i); void init_betas_precisely(); // step 7 of the algorithm from Progress bool basis_change_and_update(); void revert_to_previous_basis(); non_basic_column_value_position m_entering_boundary_position; bool update_basis_and_x_local(int entering, int leaving, X const & tt); void recover_leaving(); bool problem_is_dual_feasible() const; bool snap_runaway_nonbasic_column(unsigned); bool snap_runaway_nonbasic_columns(); unsigned get_number_of_rows_to_try_for_leaving(); void update_a_wave(const T & del, unsigned j) { this->m_A.add_column_to_vector(del, j, & m_a_wave[0]); } bool delta_keeps_the_sign(int initial_delta_sign, const T & delta); void set_status_to_tentative_dual_unbounded_or_dual_unbounded(); // it is positive if going from low bound to upper bound and negative if going from upper bound to low bound T signed_span_of_boxed(unsigned j) { return this->x_is_at_lower_bound(j)? this->bound_span(j): - this->bound_span(j); } void add_tight_breakpoints_and_q_to_flipped_set(); T delta_lost_on_flips_of_tight_breakpoints(); bool tight_breakpoinst_are_all_boxed(); T calculate_harris_delta_on_breakpoint_set(); void fill_tight_set_on_harris_delta(const T & harris_delta ); void find_q_on_tight_set(); void find_q_and_tight_set(); void erase_tight_breakpoints_and_q_from_breakpoint_set(); bool ratio_test(); void process_flipped(); void update_d_and_xB(); void calculate_beta_r_precisely(); // see "Progress in the dual simplex method for large scale LP problems: practical dual phase 1 algorithms" void update_xb_after_bound_flips(); void one_iteration(); void solve(); bool lower_bounds_are_set() const override { return true; } }; } z3-z3-4.8.7/src/util/lp/lp_dual_core_solver_def.h000066400000000000000000000641231356505360400216250ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #include #include #include "util/vector.h" #include "util/lp/lp_dual_core_solver.h" namespace lp { template void lp_dual_core_solver::init_a_wave_by_zeros() { unsigned j = this->m_m(); while (j--) { m_a_wave[j] = numeric_traits::zero(); } } template void lp_dual_core_solver::restore_non_basis() { auto & nb = this->m_nbasis; nb.reset(); unsigned j = this->m_n(); while (j--) { if (this->m_basis_heading[j] >= 0 ) continue; if (m_can_enter_basis[j]) { lp_assert(std::find(nb.begin(), nb.end(), j) == nb.end()); nb.push_back(j); this->m_basis_heading[j] = - static_cast(nb.size()); } } } template bool lp_dual_core_solver::update_basis(int entering, int leaving) { // the second argument is the element of the entering column from the pivot row - its value should be equal to the low diagonal element of the bump after all pivoting is done if (this->m_refactor_counter++ < 200) { this->m_factorization->replace_column(this->m_ed[this->m_factorization->basis_heading(leaving)], this->m_w); if (this->m_factorization->get_status() == LU_status::OK) { this->m_factorization->change_basis(entering, leaving); return true; } } // need to refactor this->m_factorization->change_basis(entering, leaving); init_factorization(this->m_factorization, this->m_A, this->m_basis, this->m_basis_heading, this->m_settings); this->m_refactor_counter = 0; if (this->m_factorization->get_status() != LU_status::OK) { LP_OUT(this->m_settings, "failing refactor for entering = " << entering << ", leaving = " << leaving << " total_iterations = " << this->total_iterations() << std::endl); this->m_iters_with_no_cost_growing++; return false; } return true; } template void lp_dual_core_solver::recalculate_xB_and_d() { this->solve_Ax_eq_b(); recalculate_d(); } template void lp_dual_core_solver::recalculate_d() { this->solve_yB(this->m_y); this->fill_reduced_costs_from_m_y_by_rows(); } template void lp_dual_core_solver::init_betas() { // todo : look at page 194 of Progress in the dual simplex algorithm for solving large scale LP problems : techniques for a fast and stable implementation // the current implementation is not good enough: todo unsigned i = this->m_m(); while (i--) { m_betas[i] = 1; } } template void lp_dual_core_solver::adjust_xb_for_changed_xn_and_init_betas() { this->solve_Ax_eq_b(); init_betas(); } template void lp_dual_core_solver::start_with_initial_basis_and_make_it_dual_feasible() { this->set_non_basic_x_to_correct_bounds(); // It is not an efficient version, see 3.29, // however this version does not require that m_x is the solution of Ax = 0 beforehand adjust_xb_for_changed_xn_and_init_betas(); } template bool lp_dual_core_solver::done() { if (this->get_status() == lp_status::OPTIMAL) { return true; } if (this->total_iterations() > this->m_settings.max_total_number_of_iterations) { // debug !!!! this->set_status(lp_status::ITERATIONS_EXHAUSTED); return true; } return false; // todo, need to be more cases } template T lp_dual_core_solver::get_edge_steepness_for_lower_bound(unsigned p) { lp_assert(this->m_basis_heading[p] >= 0 && static_cast(this->m_basis_heading[p]) < this->m_m()); T del = this->m_x[p] - this->m_lower_bounds[p]; del *= del; return del / this->m_betas[this->m_basis_heading[p]]; } template T lp_dual_core_solver::get_edge_steepness_for_upper_bound(unsigned p) { lp_assert(this->m_basis_heading[p] >= 0 && static_cast(this->m_basis_heading[p]) < this->m_m()); T del = this->m_x[p] - this->m_upper_bounds[p]; del *= del; return del / this->m_betas[this->m_basis_heading[p]]; } template T lp_dual_core_solver::pricing_for_row(unsigned i) { unsigned p = this->m_basis[i]; switch (this->m_column_types[p]) { case column_type::fixed: case column_type::boxed: if (this->x_below_low_bound(p)) { T del = get_edge_steepness_for_lower_bound(p); return del; } if (this->x_above_upper_bound(p)) { T del = get_edge_steepness_for_upper_bound(p); return del; } return numeric_traits::zero(); case column_type::lower_bound: if (this->x_below_low_bound(p)) { T del = get_edge_steepness_for_lower_bound(p); return del; } return numeric_traits::zero(); break; case column_type::upper_bound: if (this->x_above_upper_bound(p)) { T del = get_edge_steepness_for_upper_bound(p); return del; } return numeric_traits::zero(); break; case column_type::free_column: lp_assert(numeric_traits::is_zero(this->m_d[p])); return numeric_traits::zero(); default: lp_unreachable(); } lp_unreachable(); return numeric_traits::zero(); } template void lp_dual_core_solver::pricing_loop(unsigned number_of_rows_to_try, unsigned offset_in_rows) { m_r = -1; T steepest_edge_max = numeric_traits::zero(); unsigned initial_offset_in_rows = offset_in_rows; unsigned i = offset_in_rows; unsigned rows_left = number_of_rows_to_try; do { if (m_forbidden_rows.find(i) != m_forbidden_rows.end()) { if (++i == this->m_m()) { i = 0; } continue; } T se = pricing_for_row(i); if (se > steepest_edge_max) { steepest_edge_max = se; m_r = i; if (rows_left > 0) { rows_left--; } } if (++i == this->m_m()) { i = 0; } } while (i != initial_offset_in_rows && rows_left); if (m_r == -1) { if (this->get_status() != lp_status::UNSTABLE) { this->set_status(lp_status::OPTIMAL); } } else { m_p = this->m_basis[m_r]; m_delta = get_delta(); if (advance_on_known_p()){ m_forbidden_rows.clear(); return; } // failure in advance_on_known_p if (this->get_status() == lp_status::FLOATING_POINT_ERROR) { return; } this->set_status(lp_status::UNSTABLE); m_forbidden_rows.insert(m_r); } } // this calculation is needed for the steepest edge update, // it hijackes m_pivot_row_of_B_1 for this purpose since we will need it anymore to the end of the cycle template void lp_dual_core_solver::DSE_FTran() { // todo, see algorithm 7 from page 35 this->m_factorization->solve_By_for_T_indexed_only(this->m_pivot_row_of_B_1, this->m_settings); } template bool lp_dual_core_solver::advance_on_known_p() { if (done()) { return true; } this->calculate_pivot_row_of_B_1(m_r); this->calculate_pivot_row_when_pivot_row_of_B1_is_ready(m_r); if (!ratio_test()) { return true; } calculate_beta_r_precisely(); this->solve_Bd(m_q); // FTRAN int pivot_compare_result = this->pivots_in_column_and_row_are_different(m_q, m_p); if (!pivot_compare_result){;} else if (pivot_compare_result == 2) { // the sign is changed, cannot continue lp_unreachable(); // not implemented yet } else { lp_assert(pivot_compare_result == 1); this->init_lu(); } DSE_FTran(); return basis_change_and_update(); } template int lp_dual_core_solver::define_sign_of_alpha_r() { switch (this->m_column_types[m_p]) { case column_type::boxed: case column_type::fixed: if (this->x_below_low_bound(m_p)) { return -1; } if (this->x_above_upper_bound(m_p)) { return 1; } lp_unreachable(); case column_type::lower_bound: if (this->x_below_low_bound(m_p)) { return -1; } lp_unreachable(); case column_type::upper_bound: if (this->x_above_upper_bound(m_p)) { return 1; } lp_unreachable(); default: lp_unreachable(); } lp_unreachable(); return 0; } template bool lp_dual_core_solver::can_be_breakpoint(unsigned j) { if (this->pivot_row_element_is_too_small_for_ratio_test(j)) return false; switch (this->m_column_types[j]) { case column_type::lower_bound: lp_assert(this->m_settings.abs_val_is_smaller_than_harris_tolerance(this->m_x[j] - this->m_lower_bounds[j])); return m_sign_of_alpha_r * this->m_pivot_row[j] > 0; case column_type::upper_bound: lp_assert(this->m_settings.abs_val_is_smaller_than_harris_tolerance(this->m_x[j] - this->m_upper_bounds[j])); return m_sign_of_alpha_r * this->m_pivot_row[j] < 0; case column_type::boxed: { bool lower_bound = this->x_is_at_lower_bound(j); bool grawing = m_sign_of_alpha_r * this->m_pivot_row[j] > 0; return lower_bound == grawing; } case column_type::fixed: // is always dual feasible so we ignore it return false; case column_type::free_column: return true; default: return false; } } template void lp_dual_core_solver::fill_breakpoint_set() { m_breakpoint_set.clear(); for (unsigned j : this->non_basis()) { if (can_be_breakpoint(j)) { m_breakpoint_set.insert(j); } } } // template void lp_dual_core_solver::FTran() { // this->solve_Bd(m_q); // } template T lp_dual_core_solver::get_delta() { switch (this->m_column_types[m_p]) { case column_type::boxed: if (this->x_below_low_bound(m_p)) { return this->m_x[m_p] - this->m_lower_bounds[m_p]; } if (this->x_above_upper_bound(m_p)) { return this->m_x[m_p] - this->m_upper_bounds[m_p]; } lp_unreachable(); case column_type::lower_bound: if (this->x_below_low_bound(m_p)) { return this->m_x[m_p] - this->m_lower_bounds[m_p]; } lp_unreachable(); case column_type::upper_bound: if (this->x_above_upper_bound(m_p)) { return get_edge_steepness_for_upper_bound(m_p); } lp_unreachable(); case column_type::fixed: return this->m_x[m_p] - this->m_upper_bounds[m_p]; default: lp_unreachable(); } lp_unreachable(); return zero_of_type(); } template void lp_dual_core_solver::restore_d() { this->m_d[m_p] = numeric_traits::zero(); for (auto j : this->non_basis()) { this->m_d[j] += m_theta_D * this->m_pivot_row[j]; } } template bool lp_dual_core_solver::d_is_correct() { this->solve_yB(this->m_y); for (auto j : this->non_basis()) { T d = this->m_costs[j] - this->m_A.dot_product_with_column(this->m_y, j); if (numeric_traits::get_double(abs(d - this->m_d[j])) >= 0.001) { LP_OUT(this->m_settings, "total_iterations = " << this->total_iterations() << std::endl << "d[" << j << "] = " << this->m_d[j] << " but should be " << d << std::endl); return false; } } return true; } template void lp_dual_core_solver::xb_minus_delta_p_pivot_column() { unsigned i = this->m_m(); while (i--) { this->m_x[this->m_basis[i]] -= m_theta_P * this->m_ed[i]; } } template void lp_dual_core_solver::update_betas() { // page 194 of Progress ... todo - once in a while betas have to be reinitialized T one_over_arq = numeric_traits::one() / this->m_pivot_row[m_q]; T beta_r = this->m_betas[m_r] = std::max(T(0.0001), (m_betas[m_r] * one_over_arq) * one_over_arq); T k = -2 * one_over_arq; unsigned i = this->m_m(); while (i--) { if (static_cast(i) == m_r) continue; T a = this->m_ed[i]; m_betas[i] += a * (a * beta_r + k * this->m_pivot_row_of_B_1[i]); if (m_betas[i] < T(0.0001)) m_betas[i] = T(0.0001); } } template void lp_dual_core_solver::apply_flips() { for (unsigned j : m_flipped_boxed) { lp_assert(this->x_is_at_bound(j)); if (this->x_is_at_lower_bound(j)) { this->m_x[j] = this->m_upper_bounds[j]; } else { this->m_x[j] = this->m_lower_bounds[j]; } } } template void lp_dual_core_solver::snap_xN_column_to_bounds(unsigned j) { switch (this->m_column_type[j]) { case column_type::fixed: this->m_x[j] = this->m_lower_bounds[j]; break; case column_type::boxed: if (this->x_is_at_lower_bound(j)) { this->m_x[j] = this->m_lower_bounds[j]; } else { this->m_x[j] = this->m_upper_bounds[j]; } break; case column_type::lower_bound: this->m_x[j] = this->m_lower_bounds[j]; break; case column_type::upper_bound: this->m_x[j] = this->m_upper_bounds[j]; break; case column_type::free_column: break; default: lp_unreachable(); } } template void lp_dual_core_solver::snap_xN_to_bounds() { for (auto j : this->non_basis()) { snap_xN_column_to_bounds(j); } } template void lp_dual_core_solver::init_beta_precisely(unsigned i) { vector vec(this->m_m(), numeric_traits::zero()); vec[i] = numeric_traits::one(); this->m_factorization->solve_yB_with_error_check(vec, this->m_basis); T beta = numeric_traits::zero(); for (T & v : vec) { beta += v * v; } this->m_betas[i] =beta; } template void lp_dual_core_solver::init_betas_precisely() { unsigned i = this->m_m(); while (i--) { init_beta_precisely(i); } } // step 7 of the algorithm from Progress template bool lp_dual_core_solver::basis_change_and_update() { update_betas(); update_d_and_xB(); // m_theta_P = m_delta / this->m_ed[m_r]; m_theta_P = m_delta / this->m_pivot_row[m_q]; // xb_minus_delta_p_pivot_column(); apply_flips(); if (!this->update_basis_and_x(m_q, m_p, m_theta_P)) { init_betas_precisely(); return false; } if (snap_runaway_nonbasic_column(m_p)) { if (!this->find_x_by_solving()) { revert_to_previous_basis(); this->iters_with_no_cost_growing()++; return false; } } if (!problem_is_dual_feasible()) { // todo : shift the costs!!!! revert_to_previous_basis(); this->iters_with_no_cost_growing()++; return false; } lp_assert(d_is_correct()); return true; } template void lp_dual_core_solver::recover_leaving() { switch (m_entering_boundary_position) { case at_lower_bound: case at_fixed: this->m_x[m_q] = this->m_lower_bounds[m_q]; break; case at_upper_bound: this->m_x[m_q] = this->m_upper_bounds[m_q]; break; case free_of_bounds: this->m_x[m_q] = zero_of_type(); default: lp_unreachable(); } } template void lp_dual_core_solver::revert_to_previous_basis() { LP_OUT(this->m_settings, "revert to previous basis on ( " << m_p << ", " << m_q << ")" << std::endl); this->change_basis_unconditionally(m_p, m_q); init_factorization(this->m_factorization, this->m_A, this->m_basis, this->m_settings); if (this->m_factorization->get_status() != LU_status::OK) { this->set_status(lp_status::FLOATING_POINT_ERROR); // complete failure return; } recover_leaving(); if (!this->find_x_by_solving()) { this->set_status(lp_status::FLOATING_POINT_ERROR); return; } recalculate_xB_and_d(); init_betas_precisely(); } // returns true if the column has been snapped template bool lp_dual_core_solver::snap_runaway_nonbasic_column(unsigned j) { switch (this->m_column_types[j]) { case column_type::fixed: case column_type::lower_bound: if (!this->x_is_at_lower_bound(j)) { this->m_x[j] = this->m_lower_bounds[j]; return true; } break; case column_type::boxed: { bool closer_to_lower_bound = abs(this->m_lower_bounds[j] - this->m_x[j]) < abs(this->m_upper_bounds[j] - this->m_x[j]); if (closer_to_lower_bound) { if (!this->x_is_at_lower_bound(j)) { this->m_x[j] = this->m_lower_bounds[j]; return true; } } else { if (!this->x_is_at_upper_bound(j)) { this->m_x[j] = this->m_lower_bounds[j]; return true; } } } break; case column_type::upper_bound: if (!this->x_is_at_upper_bound(j)) { this->m_x[j] = this->m_upper_bounds[j]; return true; } break; default: break; } return false; } template bool lp_dual_core_solver::problem_is_dual_feasible() const { for (unsigned j : this->non_basis()){ if (!this->column_is_dual_feasible(j)) { return false; } } return true; } template unsigned lp_dual_core_solver::get_number_of_rows_to_try_for_leaving() { unsigned s = this->m_m(); if (this->m_m() > 300) { s = (unsigned)((s / 100.0) * this->m_settings.percent_of_entering_to_check); } return this->m_settings.random_next() % s + 1; } template bool lp_dual_core_solver::delta_keeps_the_sign(int initial_delta_sign, const T & delta) { if (numeric_traits::precise()) return ((delta > numeric_traits::zero()) && (initial_delta_sign == 1)) || ((delta < numeric_traits::zero()) && (initial_delta_sign == -1)); double del = numeric_traits::get_double(delta); return ( (del > this->m_settings.zero_tolerance) && (initial_delta_sign == 1)) || ((del < - this->m_settings.zero_tolerance) && (initial_delta_sign == -1)); } template void lp_dual_core_solver::set_status_to_tentative_dual_unbounded_or_dual_unbounded() { if (this->get_status() == lp_status::TENTATIVE_DUAL_UNBOUNDED) { this->set_status(lp_status::DUAL_UNBOUNDED); } else { this->set_status(lp_status::TENTATIVE_DUAL_UNBOUNDED); } } template void lp_dual_core_solver::add_tight_breakpoints_and_q_to_flipped_set() { m_flipped_boxed.insert(m_q); for (auto j : m_tight_set) { m_flipped_boxed.insert(j); } } template T lp_dual_core_solver::delta_lost_on_flips_of_tight_breakpoints() { T ret = abs(this->bound_span(m_q) * this->m_pivot_row[m_q]); for (auto j : m_tight_set) { ret += abs(this->bound_span(j) * this->m_pivot_row[j]); } return ret; } template bool lp_dual_core_solver::tight_breakpoinst_are_all_boxed() { if (this->m_column_types[m_q] != column_type::boxed) return false; for (auto j : m_tight_set) { if (this->m_column_types[j] != column_type::boxed) return false; } return true; } template T lp_dual_core_solver::calculate_harris_delta_on_breakpoint_set() { bool first_time = true; T ret = zero_of_type(); lp_assert(m_breakpoint_set.size() > 0); for (auto j : m_breakpoint_set) { T t; if (this->x_is_at_lower_bound(j)) { t = abs((std::max(this->m_d[j], numeric_traits::zero()) + m_harris_tolerance) / this->m_pivot_row[j]); } else { t = abs((std::min(this->m_d[j], numeric_traits::zero()) - m_harris_tolerance) / this->m_pivot_row[j]); } if (first_time) { ret = t; first_time = false; } else if (t < ret) { ret = t; } } return ret; } template void lp_dual_core_solver::fill_tight_set_on_harris_delta(const T & harris_delta ){ m_tight_set.clear(); for (auto j : m_breakpoint_set) { if (this->x_is_at_lower_bound(j)) { if (abs(std::max(this->m_d[j], numeric_traits::zero()) / this->m_pivot_row[j]) <= harris_delta){ m_tight_set.insert(j); } } else { if (abs(std::min(this->m_d[j], numeric_traits::zero() ) / this->m_pivot_row[j]) <= harris_delta){ m_tight_set.insert(j); } } } } template void lp_dual_core_solver::find_q_on_tight_set() { m_q = -1; T max_pivot; for (auto j : m_tight_set) { T r = abs(this->m_pivot_row[j]); if (m_q != -1) { if (r > max_pivot) { max_pivot = r; m_q = j; } } else { max_pivot = r; m_q = j; } } m_tight_set.erase(m_q); lp_assert(m_q != -1); } template void lp_dual_core_solver::find_q_and_tight_set() { T harris_del = calculate_harris_delta_on_breakpoint_set(); fill_tight_set_on_harris_delta(harris_del); find_q_on_tight_set(); m_entering_boundary_position = this->get_non_basic_column_value_position(m_q); } template void lp_dual_core_solver::erase_tight_breakpoints_and_q_from_breakpoint_set() { m_breakpoint_set.erase(m_q); for (auto j : m_tight_set) { m_breakpoint_set.erase(j); } } template bool lp_dual_core_solver::ratio_test() { m_sign_of_alpha_r = define_sign_of_alpha_r(); fill_breakpoint_set(); m_flipped_boxed.clear(); int initial_delta_sign = m_delta >= numeric_traits::zero()? 1: -1; do { if (m_breakpoint_set.empty()) { set_status_to_tentative_dual_unbounded_or_dual_unbounded(); return false; } this->set_status(lp_status::FEASIBLE); find_q_and_tight_set(); if (!tight_breakpoinst_are_all_boxed()) break; T del = m_delta - delta_lost_on_flips_of_tight_breakpoints() * initial_delta_sign; if (!delta_keeps_the_sign(initial_delta_sign, del)) break; if (m_tight_set.size() + 1 == m_breakpoint_set.size()) { break; // deciding not to flip since we might get stuck without finding m_q, the column entering the basis } // we can flip m_q together with the tight set and look for another breakpoint candidate for m_q and another tight set add_tight_breakpoints_and_q_to_flipped_set(); m_delta = del; erase_tight_breakpoints_and_q_from_breakpoint_set(); } while (true); m_theta_D = this->m_d[m_q] / this->m_pivot_row[m_q]; return true; } template void lp_dual_core_solver::process_flipped() { init_a_wave_by_zeros(); for (auto j : m_flipped_boxed) { update_a_wave(signed_span_of_boxed(j), j); } } template void lp_dual_core_solver::update_d_and_xB() { for (auto j : this->non_basis()) { this->m_d[j] -= m_theta_D * this->m_pivot_row[j]; } this->m_d[m_p] = - m_theta_D; if (!m_flipped_boxed.empty()) { process_flipped(); update_xb_after_bound_flips(); } } template void lp_dual_core_solver::calculate_beta_r_precisely() { T t = numeric_traits::zero(); unsigned i = this->m_m(); while (i--) { T b = this->m_pivot_row_of_B_1[i]; t += b * b; } m_betas[m_r] = t; } // see "Progress in the dual simplex method for large scale LP problems: practical dual phase 1 algorithms" template void lp_dual_core_solver::update_xb_after_bound_flips() { this->m_factorization->solve_By(m_a_wave); unsigned i = this->m_m(); while (i--) { this->m_x[this->m_basis[i]] -= m_a_wave[i]; } } template void lp_dual_core_solver::one_iteration() { unsigned number_of_rows_to_try = get_number_of_rows_to_try_for_leaving(); unsigned offset_in_rows = this->m_settings.random_next() % this->m_m(); if (this->get_status() == lp_status::TENTATIVE_DUAL_UNBOUNDED) { number_of_rows_to_try = this->m_m(); } else { this->set_status(lp_status::FEASIBLE); } pricing_loop(number_of_rows_to_try, offset_in_rows); lp_assert(problem_is_dual_feasible()); } template void lp_dual_core_solver::solve() { // see the page 35 lp_assert(d_is_correct()); lp_assert(problem_is_dual_feasible()); lp_assert(this->basis_heading_is_correct()); this->set_total_iterations(0); this->iters_with_no_cost_growing() = 0; do { if (this->print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over("", *this->m_settings.get_message_ostream())){ return; } one_iteration(); } while (this->get_status() != lp_status::FLOATING_POINT_ERROR && this->get_status() != lp_status::DUAL_UNBOUNDED && this->get_status() != lp_status::OPTIMAL && this->iters_with_no_cost_growing() <= this->m_settings.max_number_of_iterations_with_no_improvements && this->total_iterations() <= this->m_settings.max_total_number_of_iterations); } } z3-z3-4.8.7/src/util/lp/lp_dual_simplex.cpp000066400000000000000000000010101356505360400204630ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #include "util/lp/lp_dual_simplex_def.h" template lp::mpq lp::lp_dual_simplex::get_current_cost() const; template void lp::lp_dual_simplex::find_maximal_solution(); template double lp::lp_dual_simplex::get_current_cost() const; template void lp::lp_dual_simplex::find_maximal_solution(); z3-z3-4.8.7/src/util/lp/lp_dual_simplex.h000066400000000000000000000045441356505360400201470ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #pragma once #include "util/vector.h" #include "util/lp/lp_utils.h" #include "util/lp/lp_solver.h" #include "util/lp/lp_dual_core_solver.h" namespace lp { template class lp_dual_simplex: public lp_solver { lp_dual_core_solver * m_core_solver; vector m_b_copy; vector m_lower_bounds; // We don't have a convention here that all low bounds are zeros. At least it does not hold for the first stage solver vector m_column_types_of_core_solver; vector m_column_types_of_logicals; vector m_can_enter_basis; public: ~lp_dual_simplex() override { delete m_core_solver; } lp_dual_simplex() : m_core_solver(nullptr) {} void decide_on_status_after_stage1(); void fix_logical_for_stage2(unsigned j); void fix_structural_for_stage2(unsigned j); void unmark_boxed_and_fixed_columns_and_fix_structural_costs(); void restore_right_sides(); void solve_for_stage2(); void fill_x_with_zeros(); void stage1(); void stage2(); void fill_first_stage_solver_fields(); column_type get_column_type(unsigned j); void fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_structural_column(unsigned j); void fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_logical_column(unsigned j); void fill_costs_and_bounds_and_column_types_for_the_first_stage_solver(); void set_type_for_logical(unsigned j, column_type col_type) { this->m_column_types_of_logicals[j - this->number_of_core_structurals()] = col_type; } void fill_first_stage_solver_fields_for_row_slack_and_artificial(unsigned row, unsigned & slack_var, unsigned & artificial); void augment_matrix_A_and_fill_x_and_allocate_some_fields(); void copy_m_b_aside_and_set_it_to_zeros(); void find_maximal_solution() override; T get_column_value(unsigned column) const override { return this->get_column_value_with_core_solver(column, m_core_solver); } T get_current_cost() const override; }; } z3-z3-4.8.7/src/util/lp/lp_dual_simplex_def.h000066400000000000000000000353321356505360400207640ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #include "util/lp/lp_dual_simplex.h" namespace lp{ template void lp_dual_simplex::decide_on_status_after_stage1() { switch (m_core_solver->get_status()) { case lp_status::OPTIMAL: if (this->m_settings.abs_val_is_smaller_than_artificial_tolerance(m_core_solver->get_cost())) { this->m_status = lp_status::FEASIBLE; } else { this->m_status = lp_status::UNBOUNDED; } break; case lp_status::DUAL_UNBOUNDED: lp_unreachable(); case lp_status::ITERATIONS_EXHAUSTED: this->m_status = lp_status::ITERATIONS_EXHAUSTED; break; case lp_status::TIME_EXHAUSTED: this->m_status = lp_status::TIME_EXHAUSTED; break; case lp_status::FLOATING_POINT_ERROR: this->m_status = lp_status::FLOATING_POINT_ERROR; break; default: lp_unreachable(); } } template void lp_dual_simplex::fix_logical_for_stage2(unsigned j) { lp_assert(j >= this->number_of_core_structurals()); switch (m_column_types_of_logicals[j - this->number_of_core_structurals()]) { case column_type::lower_bound: m_lower_bounds[j] = numeric_traits::zero(); m_column_types_of_core_solver[j] = column_type::lower_bound; m_can_enter_basis[j] = true; break; case column_type::fixed: this->m_upper_bounds[j] = m_lower_bounds[j] = numeric_traits::zero(); m_column_types_of_core_solver[j] = column_type::fixed; m_can_enter_basis[j] = false; break; default: lp_unreachable(); } } template void lp_dual_simplex::fix_structural_for_stage2(unsigned j) { column_info * ci = this->m_map_from_var_index_to_column_info[this->m_core_solver_columns_to_external_columns[j]]; switch (ci->get_column_type()) { case column_type::lower_bound: m_lower_bounds[j] = numeric_traits::zero(); m_column_types_of_core_solver[j] = column_type::lower_bound; m_can_enter_basis[j] = true; break; case column_type::fixed: case column_type::upper_bound: lp_unreachable(); case column_type::boxed: this->m_upper_bounds[j] = ci->get_adjusted_upper_bound() / this->m_column_scale[j]; m_lower_bounds[j] = numeric_traits::zero(); m_column_types_of_core_solver[j] = column_type::boxed; m_can_enter_basis[j] = true; break; case column_type::free_column: m_can_enter_basis[j] = true; m_column_types_of_core_solver[j] = column_type::free_column; break; default: lp_unreachable(); } // T cost_was = this->m_costs[j]; this->set_scaled_cost(j); } template void lp_dual_simplex::unmark_boxed_and_fixed_columns_and_fix_structural_costs() { unsigned j = this->m_A->column_count(); while (j-- > this->number_of_core_structurals()) { fix_logical_for_stage2(j); } j = this->number_of_core_structurals(); while (j--) { fix_structural_for_stage2(j); } } template void lp_dual_simplex::restore_right_sides() { unsigned i = this->m_A->row_count(); while (i--) { this->m_b[i] = m_b_copy[i]; } } template void lp_dual_simplex::solve_for_stage2() { m_core_solver->restore_non_basis(); m_core_solver->solve_yB(m_core_solver->m_y); m_core_solver->fill_reduced_costs_from_m_y_by_rows(); m_core_solver->start_with_initial_basis_and_make_it_dual_feasible(); m_core_solver->set_status(lp_status::FEASIBLE); m_core_solver->solve(); switch (m_core_solver->get_status()) { case lp_status::OPTIMAL: this->m_status = lp_status::OPTIMAL; break; case lp_status::DUAL_UNBOUNDED: this->m_status = lp_status::INFEASIBLE; break; case lp_status::TIME_EXHAUSTED: this->m_status = lp_status::TIME_EXHAUSTED; break; case lp_status::FLOATING_POINT_ERROR: this->m_status = lp_status::FLOATING_POINT_ERROR; break; default: lp_unreachable(); } this->m_second_stage_iterations = m_core_solver->total_iterations(); this->m_total_iterations = (this->m_first_stage_iterations + this->m_second_stage_iterations); } template void lp_dual_simplex::fill_x_with_zeros() { unsigned j = this->m_A->column_count(); while (j--) { this->m_x[j] = numeric_traits::zero(); } } template void lp_dual_simplex::stage1() { lp_assert(m_core_solver == nullptr); this->m_x.resize(this->m_A->column_count(), numeric_traits::zero()); if (this->m_settings.get_message_ostream() != nullptr) this->print_statistics_on_A(*this->m_settings.get_message_ostream()); m_core_solver = new lp_dual_core_solver( *this->m_A, m_can_enter_basis, this->m_b, // the right side vector this->m_x, this->m_basis, this->m_nbasis, this->m_heading, this->m_costs, this->m_column_types_of_core_solver, this->m_lower_bounds, this->m_upper_bounds, this->m_settings, *this); m_core_solver->fill_reduced_costs_from_m_y_by_rows(); m_core_solver->start_with_initial_basis_and_make_it_dual_feasible(); if (this->m_settings.abs_val_is_smaller_than_artificial_tolerance(m_core_solver->get_cost())) { // skipping stage 1 m_core_solver->set_status(lp_status::OPTIMAL); m_core_solver->set_total_iterations(0); } else { m_core_solver->solve(); } decide_on_status_after_stage1(); this->m_first_stage_iterations = m_core_solver->total_iterations(); } template void lp_dual_simplex::stage2() { unmark_boxed_and_fixed_columns_and_fix_structural_costs(); restore_right_sides(); solve_for_stage2(); } template void lp_dual_simplex::fill_first_stage_solver_fields() { unsigned slack_var = this->number_of_core_structurals(); unsigned artificial = this->number_of_core_structurals() + this->m_slacks; for (unsigned row = 0; row < this->row_count(); row++) { fill_first_stage_solver_fields_for_row_slack_and_artificial(row, slack_var, artificial); } fill_costs_and_bounds_and_column_types_for_the_first_stage_solver(); } template column_type lp_dual_simplex::get_column_type(unsigned j) { lp_assert(j < this->m_A->column_count()); if (j >= this->number_of_core_structurals()) { return m_column_types_of_logicals[j - this->number_of_core_structurals()]; } return this->m_map_from_var_index_to_column_info[this->m_core_solver_columns_to_external_columns[j]]->get_column_type(); } template void lp_dual_simplex::fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_structural_column(unsigned j) { // see 4.7 in the dissertation of Achim Koberstein lp_assert(this->m_core_solver_columns_to_external_columns.find(j) != this->m_core_solver_columns_to_external_columns.end()); T free_bound = T(1e4); // see 4.8 unsigned jj = this->m_core_solver_columns_to_external_columns[j]; lp_assert(this->m_map_from_var_index_to_column_info.find(jj) != this->m_map_from_var_index_to_column_info.end()); column_info * ci = this->m_map_from_var_index_to_column_info[jj]; switch (ci->get_column_type()) { case column_type::upper_bound: { std::stringstream s; s << "unexpected bound type " << j << " " << column_type_to_string(get_column_type(j)); throw_exception(s.str()); break; } case column_type::lower_bound: { m_can_enter_basis[j] = true; this->set_scaled_cost(j); this->m_lower_bounds[j] = numeric_traits::zero(); this->m_upper_bounds[j] =numeric_traits::one(); break; } case column_type::free_column: { m_can_enter_basis[j] = true; this->set_scaled_cost(j); this->m_upper_bounds[j] = free_bound; this->m_lower_bounds[j] = -free_bound; break; } case column_type::boxed: m_can_enter_basis[j] = false; this->m_costs[j] = numeric_traits::zero(); this->m_upper_bounds[j] = this->m_lower_bounds[j] = numeric_traits::zero(); // is it needed? break; default: lp_unreachable(); } m_column_types_of_core_solver[j] = column_type::boxed; } template void lp_dual_simplex::fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_logical_column(unsigned j) { this->m_costs[j] = 0; lp_assert(get_column_type(j) != column_type::upper_bound); if ((m_can_enter_basis[j] = (get_column_type(j) == column_type::lower_bound))) { m_column_types_of_core_solver[j] = column_type::boxed; this->m_lower_bounds[j] = numeric_traits::zero(); this->m_upper_bounds[j] = numeric_traits::one(); } else { m_column_types_of_core_solver[j] = column_type::fixed; this->m_lower_bounds[j] = numeric_traits::zero(); this->m_upper_bounds[j] = numeric_traits::zero(); } } template void lp_dual_simplex::fill_costs_and_bounds_and_column_types_for_the_first_stage_solver() { unsigned j = this->m_A->column_count(); while (j-- > this->number_of_core_structurals()) { // go over logicals here fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_logical_column(j); } j = this->number_of_core_structurals(); while (j--) { fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_structural_column(j); } } template void lp_dual_simplex::fill_first_stage_solver_fields_for_row_slack_and_artificial(unsigned row, unsigned & slack_var, unsigned & artificial) { lp_assert(row < this->row_count()); auto & constraint = this->m_constraints[this->m_core_solver_rows_to_external_rows[row]]; // we need to bring the program to the form Ax = b T rs = this->m_b[row]; switch (constraint.m_relation) { case Equal: // no slack variable here set_type_for_logical(artificial, column_type::fixed); this->m_basis[row] = artificial; this->m_costs[artificial] = numeric_traits::zero(); (*this->m_A)(row, artificial) = numeric_traits::one(); artificial++; break; case Greater_or_equal: set_type_for_logical(slack_var, column_type::lower_bound); (*this->m_A)(row, slack_var) = - numeric_traits::one(); if (rs > 0) { // adding one artificial set_type_for_logical(artificial, column_type::fixed); (*this->m_A)(row, artificial) = numeric_traits::one(); this->m_basis[row] = artificial; this->m_costs[artificial] = numeric_traits::zero(); artificial++; } else { // we can put a slack_var into the basis, and avoid adding an artificial variable this->m_basis[row] = slack_var; this->m_costs[slack_var] = numeric_traits::zero(); } slack_var++; break; case Less_or_equal: // introduce a non-negative slack variable set_type_for_logical(slack_var, column_type::lower_bound); (*this->m_A)(row, slack_var) = numeric_traits::one(); if (rs < 0) { // adding one artificial set_type_for_logical(artificial, column_type::fixed); (*this->m_A)(row, artificial) = - numeric_traits::one(); this->m_basis[row] = artificial; this->m_costs[artificial] = numeric_traits::zero(); artificial++; } else { // we can put slack_var into the basis, and avoid adding an artificial variable this->m_basis[row] = slack_var; this->m_costs[slack_var] = numeric_traits::zero(); } slack_var++; break; } } template void lp_dual_simplex::augment_matrix_A_and_fill_x_and_allocate_some_fields() { this->count_slacks_and_artificials(); this->m_A->add_columns_at_the_end(this->m_slacks + this->m_artificials); unsigned n = this->m_A->column_count(); this->m_column_types_of_core_solver.resize(n); m_column_types_of_logicals.resize(this->m_slacks + this->m_artificials); this->m_costs.resize(n); this->m_upper_bounds.resize(n); this->m_lower_bounds.resize(n); m_can_enter_basis.resize(n); this->m_basis.resize(this->m_A->row_count()); } template void lp_dual_simplex::copy_m_b_aside_and_set_it_to_zeros() { for (unsigned i = 0; i < this->m_b.size(); i++) { m_b_copy.push_back(this->m_b[i]); this->m_b[i] = numeric_traits::zero(); // preparing for the first stage } } template void lp_dual_simplex::find_maximal_solution(){ if (this->problem_is_empty()) { this->m_status = lp_status::EMPTY; return; } this->flip_costs(); // do it for now, todo ( remove the flipping) this->cleanup(); if (this->m_status == lp_status::INFEASIBLE) { return; } this->fill_matrix_A_and_init_right_side(); this->fill_m_b(); this->scale(); augment_matrix_A_and_fill_x_and_allocate_some_fields(); fill_first_stage_solver_fields(); copy_m_b_aside_and_set_it_to_zeros(); stage1(); if (this->m_status == lp_status::FEASIBLE) { stage2(); } } template T lp_dual_simplex::get_current_cost() const { T ret = numeric_traits::zero(); for (auto it : this->m_map_from_var_index_to_column_info) { ret += this->get_column_cost_value(it.first, it.second); } return -ret; // we flip costs for now } } z3-z3-4.8.7/src/util/lp/lp_params.pyg000066400000000000000000000012031356505360400173010ustar00rootroot00000000000000def_module_params('lp', export=True, params=( ('rep_freq', UINT, 0, 'the report frequency, in how many iterations print the cost and other info '), ('min', BOOL, False, 'minimize cost'), ('print_stats', BOOL, False, 'print statistic'), ('simplex_strategy', UINT, 0, 'simplex strategy for the solver'), ('enable_hnf', BOOL, True, 'enable hnf cuts'), ('bprop_on_pivoted_rows', BOOL, True, 'propagate bounds on rows changed by the pivot operation') )) z3-z3-4.8.7/src/util/lp/lp_primal_core_solver.cpp000066400000000000000000000024661356505360400217030ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #include #include #include #include "util/vector.h" #include #include "util/lp/lar_solver.h" #include "util/lp/lp_primal_core_solver_def.h" #include "util/lp/lp_primal_core_solver_tableau_def.h" namespace lp { template void lp_primal_core_solver::find_feasible_solution(); template void lp::lp_primal_core_solver >::find_feasible_solution(); template unsigned lp_primal_core_solver::solve(); template unsigned lp_primal_core_solver::solve_with_tableau(); template unsigned lp_primal_core_solver::solve(); template unsigned lp_primal_core_solver >::solve(); template void lp::lp_primal_core_solver::clear_breakpoints(); template bool lp::lp_primal_core_solver::update_basis_and_x_tableau(int, int, lp::mpq const&); template bool lp::lp_primal_core_solver::update_basis_and_x_tableau(int, int, double const&); template bool lp::lp_primal_core_solver >::update_basis_and_x_tableau(int, int, lp::numeric_pair const&); } z3-z3-4.8.7/src/util/lp/lp_primal_core_solver.h000066400000000000000000001125421356505360400213450ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #pragma once #include #include #include #include #include #include "util/vector.h" #include #include #include #include #include "util/lp/lu.h" #include "util/lp/lp_solver.h" #include "util/lp/static_matrix.h" #include "util/lp/core_solver_pretty_printer.h" #include "util/lp/lp_core_solver_base.h" #include "util/lp/breakpoint.h" #include "util/lp/binary_heap_priority_queue.h" #include "util/lp/int_set.h" namespace lp { // This core solver solves (Ax=b, lower_bound_values \leq x \leq upper_bound_values, maximize costs*x ) // The right side b is given implicitly by x and the basis template class lp_primal_core_solver:public lp_core_solver_base { public: // m_sign_of_entering is set to 1 if the entering variable needs // to grow and is set to -1 otherwise unsigned m_column_norm_update_counter; T m_enter_price_eps; int m_sign_of_entering_delta; vector> m_breakpoints; binary_heap_priority_queue m_breakpoint_indices_queue; indexed_vector m_beta; // see Swietanowski working vector beta for column norms T m_epsilon_of_reduced_cost; vector m_costs_backup; T m_converted_harris_eps; unsigned m_inf_row_index_for_tableau; bool m_bland_mode_tableau; int_set m_left_basis_tableau; unsigned m_bland_mode_threshold; unsigned m_left_basis_repeated; vector m_leaving_candidates; // T m_converted_harris_eps = convert_struct::convert(this->m_settings.harris_feasibility_tolerance); std::list m_non_basis_list; void sort_non_basis(); void sort_non_basis_rational(); int choose_entering_column(unsigned number_of_benefitial_columns_to_go_over); int choose_entering_column_tableau(); int choose_entering_column_presize(unsigned number_of_benefitial_columns_to_go_over); int find_leaving_and_t_with_breakpoints(unsigned entering, X & t); // int find_inf_row() { // // mimicing CLP : todo : use a heap // int j = -1; // for (unsigned k : this->m_inf_set.m_index) { // if (k < static_cast(j)) // j = static_cast(k); // } // if (j == -1) // return -1; // return this->m_basis_heading[j]; // #if 0 // vector choices; // unsigned len = 100000000; // for (unsigned j : this->m_inf_set.m_index) { // int i = this->m_basis_heading[j]; // lp_assert(i >= 0); // unsigned row_len = this->m_A.m_rows[i].size(); // if (row_len < len) { // choices.clear(); // choices.push_back(i); // len = row_len; // if (m_settings.random_next() % 10) break; // } else if (row_len == len) { // choices.push_back(i); // if (m_settings.random_next() % 10) break; // } // } // if (choices.size() == 0) // return -1; // if (choices.size() == 1) // return choices[0]; // unsigned k = this->m_settings.random_next() % choices.size(); // return choices[k]; // #endif // } bool column_is_benefitial_for_entering_basis_on_sign_row_strategy(unsigned j, int sign) const { // sign = 1 means the x of the basis column of the row has to grow to become feasible, when the coeff before j is neg, or x - has to diminish when the coeff is pos // we have xbj = -aj * xj lp_assert(this->m_basis_heading[j] < 0); lp_assert(this->column_is_feasible(j)); switch (this->m_column_types[j]) { case column_type::free_column: return true; case column_type::fixed: return false; case column_type::lower_bound: if (sign < 0) return true; return !this->x_is_at_lower_bound(j); case column_type::upper_bound: if (sign > 0) return true; return !this->x_is_at_upper_bound(j); case column_type::boxed: if (sign < 0) return !this->x_is_at_lower_bound(j); return !this->x_is_at_upper_bound(j); } lp_assert(false); // cannot be here return false; } bool needs_to_grow(unsigned bj) const { lp_assert(!this->column_is_feasible(bj)); switch(this->m_column_types[bj]) { case column_type::free_column: return false; case column_type::fixed: case column_type::lower_bound: case column_type::boxed: return this-> x_below_low_bound(bj); default: return false; } lp_assert(false); // unreachable return false; } int inf_sign_of_column(unsigned bj) const { lp_assert(!this->column_is_feasible(bj)); switch(this->m_column_types[bj]) { case column_type::free_column: return 0; case column_type::lower_bound: return 1; case column_type::fixed: case column_type::boxed: return this->x_above_upper_bound(bj)? -1: 1; default: return -1; } lp_assert(false); // unreachable return 0; } bool monoid_can_decrease(const row_cell & rc) const { unsigned j = rc.var(); lp_assert(this->column_is_feasible(j)); switch (this->m_column_types[j]) { case column_type::free_column: return true; case column_type::fixed: return false; case column_type::lower_bound: if (is_pos(rc.get_val())) { return this->x_above_lower_bound(j); } return true; case column_type::upper_bound: if (is_pos(rc.get_val())) { return true; } return this->x_below_upper_bound(j); case column_type::boxed: if (is_pos(rc.get_val())) { return this->x_above_lower_bound(j); } return this->x_below_upper_bound(j); default: return false; } lp_assert(false); // unreachable return false; } bool monoid_can_increase(const row_cell & rc) const { unsigned j = rc.var(); lp_assert(this->column_is_feasible(j)); switch (this->m_column_types[j]) { case column_type::free_column: return true; case column_type::fixed: return false; case column_type::lower_bound: if (is_neg(rc.get_val())) { return this->x_above_lower_bound(j); } return true; case column_type::upper_bound: if (is_neg(rc.get_val())) { return true; } return this->x_below_upper_bound(j); case column_type::boxed: if (is_neg(rc.get_val())) { return this->x_above_lower_bound(j); } return this->x_below_upper_bound(j); default: return false; } lp_assert(false); // unreachable return false; } unsigned get_number_of_basic_vars_that_might_become_inf(unsigned j) const { // consider looking at the signs here: todo unsigned r = 0; for (const auto & cc : this->m_A.m_columns[j]) { unsigned k = this->m_basis[cc.var()]; if (this->m_column_types[k] != column_type::free_column) r++; } return r; } int find_beneficial_column_in_row_tableau_rows_bland_mode(int i, T & a_ent) { int j = -1; unsigned bj = this->m_basis[i]; bool bj_needs_to_grow = needs_to_grow(bj); for (const row_cell& rc : this->m_A.m_rows[i]) { if (rc.var() == bj) continue; if (bj_needs_to_grow) { if (!monoid_can_decrease(rc)) continue; } else { if (!monoid_can_increase(rc)) continue; } if (rc.var() < static_cast(j) ) { j = rc.var(); a_ent = rc.coeff(); } } if (j == -1) { m_inf_row_index_for_tableau = i; } return j; } int find_beneficial_column_in_row_tableau_rows(int i, T & a_ent) { if (m_bland_mode_tableau) return find_beneficial_column_in_row_tableau_rows_bland_mode(i, a_ent); // a short row produces short infeasibility explanation and benefits at least one pivot operation int choice = -1; int nchoices = 0; unsigned num_of_non_free_basics = 1000000; unsigned len = 100000000; unsigned bj = this->m_basis[i]; bool bj_needs_to_grow = needs_to_grow(bj); for (unsigned k = 0; k < this->m_A.m_rows[i].size(); k++) { const row_cell& rc = this->m_A.m_rows[i][k]; unsigned j = rc.var(); if (j == bj) continue; if (bj_needs_to_grow) { if (!monoid_can_decrease(rc)) continue; } else { if (!monoid_can_increase(rc)) continue; } unsigned damage = get_number_of_basic_vars_that_might_become_inf(j); if (damage < num_of_non_free_basics) { num_of_non_free_basics = damage; len = this->m_A.m_columns[j].size(); choice = k; nchoices = 1; } else if (damage == num_of_non_free_basics && this->m_A.m_columns[j].size() <= len && (this->m_settings.random_next() % (++nchoices))) { choice = k; len = this->m_A.m_columns[j].size(); } } if (choice == -1) { m_inf_row_index_for_tableau = i; return -1; } const row_cell& rc = this->m_A.m_rows[i][choice]; a_ent = rc.coeff(); return rc.var(); } static X positive_infinity() { return convert_struct::convert(std::numeric_limits::max()); } bool get_harris_theta(X & theta); void restore_harris_eps() { m_converted_harris_eps = convert_struct::convert(this->m_settings.harris_feasibility_tolerance); } void zero_harris_eps() { m_converted_harris_eps = zero_of_type(); } int find_leaving_on_harris_theta(X const & harris_theta, X & t); bool try_jump_to_another_bound_on_entering(unsigned entering, const X & theta, X & t, bool & unlimited); bool try_jump_to_another_bound_on_entering_unlimited(unsigned entering, X & t); int find_leaving_and_t(unsigned entering, X & t); int find_leaving_and_t_precise(unsigned entering, X & t); int find_leaving_and_t_tableau(unsigned entering, X & t); void limit_theta(const X & lim, X & theta, bool & unlimited) { if (unlimited) { theta = lim; unlimited = false; } else { theta = std::min(lim, theta); } } void limit_theta_on_basis_column_for_inf_case_m_neg_upper_bound(unsigned j, const T & m, X & theta, bool & unlimited) { lp_assert(m < 0 && this->m_column_types[j] == column_type::upper_bound); limit_inf_on_upper_bound_m_neg(m, this->m_x[j], this->m_upper_bounds[j], theta, unlimited); } void limit_theta_on_basis_column_for_inf_case_m_neg_lower_bound(unsigned j, const T & m, X & theta, bool & unlimited) { lp_assert(m < 0 && this->m_column_types[j] == column_type::lower_bound); limit_inf_on_bound_m_neg(m, this->m_x[j], this->m_lower_bounds[j], theta, unlimited); } void limit_theta_on_basis_column_for_inf_case_m_pos_lower_bound(unsigned j, const T & m, X & theta, bool & unlimited) { lp_assert(m > 0 && this->m_column_types[j] == column_type::lower_bound); limit_inf_on_lower_bound_m_pos(m, this->m_x[j], this->m_lower_bounds[j], theta, unlimited); } void limit_theta_on_basis_column_for_inf_case_m_pos_upper_bound(unsigned j, const T & m, X & theta, bool & unlimited) { lp_assert(m > 0 && this->m_column_types[j] == column_type::upper_bound); limit_inf_on_bound_m_pos(m, this->m_x[j], this->m_upper_bounds[j], theta, unlimited); }; X harris_eps_for_bound(const X & bound) const { return ( convert_struct::convert(1) + abs(bound)/10) * m_converted_harris_eps/3; } void get_bound_on_variable_and_update_leaving_precisely(unsigned j, vector & leavings, T m, X & t, T & abs_of_d_of_leaving); vector m_lower_bounds_dummy; // needed for the base class only X get_max_bound(vector & b); #ifdef Z3DEBUG void check_Ax_equal_b(); void check_the_bounds(); void check_bound(unsigned i); void check_correctness(); #endif // from page 183 of Istvan Maros's book // the basis structures have not changed yet void update_reduced_costs_from_pivot_row(unsigned entering, unsigned leaving); // return 0 if the reduced cost at entering is close enough to the refreshed // 1 if it is way off, and 2 if it is unprofitable int refresh_reduced_cost_at_entering_and_check_that_it_is_off(unsigned entering); void backup_and_normalize_costs(); void init_run(); void calc_working_vector_beta_for_column_norms(); void advance_on_entering_and_leaving(int entering, int leaving, X & t); void advance_on_entering_and_leaving_tableau(int entering, int leaving, X & t); void advance_on_entering_equal_leaving(int entering, X & t); void advance_on_entering_equal_leaving_tableau(int entering, X & t); bool need_to_switch_costs() const { if (this->m_settings.simplex_strategy() == simplex_strategy_enum::tableau_rows) return false; // lp_assert(calc_current_x_is_feasible() == current_x_is_feasible()); return this->current_x_is_feasible() == this->m_using_infeas_costs; } void advance_on_entering(int entering); void advance_on_entering_tableau(int entering); void advance_on_entering_precise(int entering); void push_forward_offset_in_non_basis(unsigned & offset_in_nb); unsigned get_number_of_non_basic_column_to_try_for_enter(); void print_column_norms(std::ostream & out); // returns the number of iterations unsigned solve(); lu> * factorization() {return this->m_factorization;} void delete_factorization(); // according to Swietanowski, " A new steepest edge approximation for the simplex method for linear programming" void init_column_norms(); T calculate_column_norm_exactly(unsigned j); void update_or_init_column_norms(unsigned entering, unsigned leaving); // following Swietanowski - A new steepest ... void update_column_norms(unsigned entering, unsigned leaving); T calculate_norm_of_entering_exactly(); void find_feasible_solution(); // bool is_tiny() const {return this->m_m < 10 && this->m_n < 20;} void one_iteration(); void one_iteration_tableau(); void advance_on_entering_and_leaving_tableau_rows(int entering, int leaving, const X &theta ) { this->update_basis_and_x_tableau(entering, leaving, theta); this->track_column_feasibility(entering); } int find_leaving_tableau_rows(X & new_val_for_leaving) { int j = -1; for (unsigned k : this->m_inf_set.m_index) { if (k < static_cast(j)) j = static_cast(k); } if (j == -1) return -1; lp_assert(!this->column_is_feasible(j)); switch (this->m_column_types[j]) { case column_type::fixed: case column_type::upper_bound: new_val_for_leaving = this->m_upper_bounds[j]; break; case column_type::lower_bound: new_val_for_leaving = this->m_lower_bounds[j]; break; case column_type::boxed: if (this->x_above_upper_bound(j)) new_val_for_leaving = this->m_upper_bounds[j]; else new_val_for_leaving = this->m_lower_bounds[j]; break; default: lp_assert(false); new_val_for_leaving = numeric_traits::zero(); // does not matter } return j; } void one_iteration_tableau_rows() { X new_val_for_leaving; int leaving = find_leaving_tableau_rows(new_val_for_leaving); if (leaving == -1) { this->set_status(lp_status::OPTIMAL); return; } if (!m_bland_mode_tableau) { if (m_left_basis_tableau.contains(leaving)) { if (++m_left_basis_repeated > m_bland_mode_threshold) { m_bland_mode_tableau = true; } } else { m_left_basis_tableau.insert(leaving); } } T a_ent; int entering = find_beneficial_column_in_row_tableau_rows(this->m_basis_heading[leaving], a_ent); if (entering == -1) { this->set_status(lp_status::INFEASIBLE); return; } X theta = (this->m_x[leaving] - new_val_for_leaving) / a_ent; advance_on_entering_and_leaving_tableau_rows(entering, leaving, theta ); lp_assert(this->m_x[leaving] == new_val_for_leaving); if (this->current_x_is_feasible()) this->set_status(lp_status::OPTIMAL); } void fill_breakpoints_array(unsigned entering); void try_add_breakpoint_in_row(unsigned i); void clear_breakpoints(); void change_slope_on_breakpoint(unsigned entering, breakpoint * b, T & slope_at_entering); void advance_on_sorted_breakpoints(unsigned entering); void update_basis_and_x_with_comparison(unsigned entering, unsigned leaving, X delta); void decide_on_status_when_cannot_find_entering() { lp_assert(!need_to_switch_costs()); this->set_status(this->current_x_is_feasible()? lp_status::OPTIMAL: lp_status::INFEASIBLE); } // void limit_theta_on_basis_column_for_feas_case_m_neg(unsigned j, const T & m, X & theta) { // lp_assert(m < 0); // lp_assert(this->m_column_type[j] == lower_bound || this->m_column_type[j] == boxed); // const X & eps = harris_eps_for_bound(this->m_lower_bounds[j]); // if (this->above_bound(this->m_x[j], this->m_lower_bounds[j])) { // theta = std::min((this->m_lower_bounds[j] -this->m_x[j] - eps) / m, theta); // if (theta < zero_of_type()) theta = zero_of_type(); // } // } void limit_theta_on_basis_column_for_feas_case_m_neg_no_check(unsigned j, const T & m, X & theta, bool & unlimited) { lp_assert(m < 0); const X& eps = harris_eps_for_bound(this->m_lower_bounds[j]); limit_theta((this->m_lower_bounds[j] - this->m_x[j] - eps) / m, theta, unlimited); if (theta < zero_of_type()) theta = zero_of_type(); } bool limit_inf_on_bound_m_neg(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) { // x gets smaller lp_assert(m < 0); if (numeric_traits::precise()) { if (this->below_bound(x, bound)) return false; if (this->above_bound(x, bound)) { limit_theta((bound - x) / m, theta, unlimited); } else { theta = zero_of_type(); unlimited = false; } } else { const X& eps = harris_eps_for_bound(bound); if (this->below_bound(x, bound)) return false; if (this->above_bound(x, bound)) { limit_theta((bound - x - eps) / m, theta, unlimited); } else { theta = zero_of_type(); unlimited = false; } } return true; } bool limit_inf_on_bound_m_pos(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) { // x gets larger lp_assert(m > 0); if (numeric_traits::precise()) { if (this->above_bound(x, bound)) return false; if (this->below_bound(x, bound)) { limit_theta((bound - x) / m, theta, unlimited); } else { theta = zero_of_type(); unlimited = false; } } else { const X& eps = harris_eps_for_bound(bound); if (this->above_bound(x, bound)) return false; if (this->below_bound(x, bound)) { limit_theta((bound - x + eps) / m, theta, unlimited); } else { theta = zero_of_type(); unlimited = false; } } return true; } void limit_inf_on_lower_bound_m_pos(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) { if (numeric_traits::precise()) { // x gets larger lp_assert(m > 0); if (this->below_bound(x, bound)) { limit_theta((bound - x) / m, theta, unlimited); } } else { // x gets larger lp_assert(m > 0); const X& eps = harris_eps_for_bound(bound); if (this->below_bound(x, bound)) { limit_theta((bound - x + eps) / m, theta, unlimited); } } } void limit_inf_on_upper_bound_m_neg(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) { // x gets smaller lp_assert(m < 0); const X& eps = harris_eps_for_bound(bound); if (this->above_bound(x, bound)) { limit_theta((bound - x - eps) / m, theta, unlimited); } } void limit_theta_on_basis_column_for_inf_case_m_pos_boxed(unsigned j, const T & m, X & theta, bool & unlimited) { // lp_assert(m > 0 && this->m_column_type[j] == column_type::boxed); const X & x = this->m_x[j]; const X & lbound = this->m_lower_bounds[j]; if (this->below_bound(x, lbound)) { const X& eps = harris_eps_for_bound(this->m_upper_bounds[j]); limit_theta((lbound - x + eps) / m, theta, unlimited); } else { const X & ubound = this->m_upper_bounds[j]; if (this->below_bound(x, ubound)){ const X& eps = harris_eps_for_bound(ubound); limit_theta((ubound - x + eps) / m, theta, unlimited); } else if (!this->above_bound(x, ubound)) { theta = zero_of_type(); unlimited = false; } } } void limit_theta_on_basis_column_for_inf_case_m_neg_boxed(unsigned j, const T & m, X & theta, bool & unlimited) { // lp_assert(m < 0 && this->m_column_type[j] == column_type::boxed); const X & x = this->m_x[j]; const X & ubound = this->m_upper_bounds[j]; if (this->above_bound(x, ubound)) { const X& eps = harris_eps_for_bound(ubound); limit_theta((ubound - x - eps) / m, theta, unlimited); } else { const X & lbound = this->m_lower_bounds[j]; if (this->above_bound(x, lbound)){ const X& eps = harris_eps_for_bound(lbound); limit_theta((lbound - x - eps) / m, theta, unlimited); } else if (!this->below_bound(x, lbound)) { theta = zero_of_type(); unlimited = false; } } } void limit_theta_on_basis_column_for_feas_case_m_pos(unsigned j, const T & m, X & theta, bool & unlimited) { lp_assert(m > 0); const T& eps = harris_eps_for_bound(this->m_upper_bounds[j]); if (this->below_bound(this->m_x[j], this->m_upper_bounds[j])) { limit_theta((this->m_upper_bounds[j] - this->m_x[j] + eps) / m, theta, unlimited); if (theta < zero_of_type()) { theta = zero_of_type(); unlimited = false; } } } void limit_theta_on_basis_column_for_feas_case_m_pos_no_check(unsigned j, const T & m, X & theta, bool & unlimited ) { lp_assert(m > 0); const X& eps = harris_eps_for_bound(this->m_upper_bounds[j]); limit_theta( (this->m_upper_bounds[j] - this->m_x[j] + eps) / m, theta, unlimited); if (theta < zero_of_type()) { theta = zero_of_type(); } } // j is a basic column or the entering, in any case x[j] has to stay feasible. // m is the multiplier. updating t in a way that holds the following // x[j] + t * m >= this->m_lower_bounds[j]- harris_feasibility_tolerance ( if m < 0 ) // or // x[j] + t * m <= this->m_upper_bounds[j] + harris_feasibility_tolerance ( if m > 0) void limit_theta_on_basis_column(unsigned j, T m, X & theta, bool & unlimited) { switch (this->m_column_types[j]) { case column_type::free_column: break; case column_type::upper_bound: if (this->current_x_is_feasible()) { if (m > 0) limit_theta_on_basis_column_for_feas_case_m_pos_no_check(j, m, theta, unlimited); } else { // inside of feasibility_loop if (m > 0) limit_theta_on_basis_column_for_inf_case_m_pos_upper_bound(j, m, theta, unlimited); else limit_theta_on_basis_column_for_inf_case_m_neg_upper_bound(j, m, theta, unlimited); } break; case column_type::lower_bound: if (this->current_x_is_feasible()) { if (m < 0) limit_theta_on_basis_column_for_feas_case_m_neg_no_check(j, m, theta, unlimited); } else { if (m < 0) limit_theta_on_basis_column_for_inf_case_m_neg_lower_bound(j, m, theta, unlimited); else limit_theta_on_basis_column_for_inf_case_m_pos_lower_bound(j, m, theta, unlimited); } break; // case fixed: // if (get_this->current_x_is_feasible()) { // theta = zero_of_type(); // break; // } // if (m < 0) // limit_theta_on_basis_column_for_inf_case_m_neg_fixed(j, m, theta); // else // limit_theta_on_basis_column_for_inf_case_m_pos_fixed(j, m, theta); // break; case column_type::fixed: case column_type::boxed: if (this->current_x_is_feasible()) { if (m > 0) { limit_theta_on_basis_column_for_feas_case_m_pos_no_check(j, m, theta, unlimited); } else { limit_theta_on_basis_column_for_feas_case_m_neg_no_check(j, m, theta, unlimited); } } else { if (m > 0) { limit_theta_on_basis_column_for_inf_case_m_pos_boxed(j, m, theta, unlimited); } else { limit_theta_on_basis_column_for_inf_case_m_neg_boxed(j, m, theta, unlimited); } } break; default: lp_unreachable(); } if (!unlimited && theta < zero_of_type()) { theta = zero_of_type(); } } bool column_is_benefitial_for_entering_basis(unsigned j) const; bool column_is_benefitial_for_entering_basis_precise(unsigned j) const; bool column_is_benefitial_for_entering_on_breakpoints(unsigned j) const; bool can_enter_basis(unsigned j); bool done(); void init_infeasibility_costs(); void init_infeasibility_cost_for_column(unsigned j); T get_infeasibility_cost_for_column(unsigned j) const; void init_infeasibility_costs_for_changed_basis_only(); void print_column(unsigned j, std::ostream & out); void add_breakpoint(unsigned j, X delta, breakpoint_type type); // j is the basic column, x is the value at x[j] // d is the coefficient before m_entering in the row with j as the basis column void try_add_breakpoint(unsigned j, const X & x, const T & d, breakpoint_type break_type, const X & break_value); template bool same_sign_with_entering_delta(const L & a) { return (a > zero_of_type() && m_sign_of_entering_delta > 0) || (a < zero_of_type() && m_sign_of_entering_delta < 0); } void init_reduced_costs(); bool lower_bounds_are_set() const override { return true; } int advance_on_sorted_breakpoints(unsigned entering, X & t); std::string break_type_to_string(breakpoint_type type); void print_breakpoint(const breakpoint * b, std::ostream & out); void print_bound_info_and_x(unsigned j, std::ostream & out); void init_infeasibility_after_update_x_if_inf(unsigned leaving) { if (this->m_using_infeas_costs) { init_infeasibility_costs_for_changed_basis_only(); this->m_costs[leaving] = zero_of_type(); this->m_inf_set.erase(leaving); } } void init_inf_set() { this->m_inf_set.clear(); for (unsigned j = 0; j < this->m_n(); j++) { if (this->m_basis_heading[j] < 0) continue; if (!this->column_is_feasible(j)) this->insert_column_into_inf_set(j); } } int get_column_out_of_bounds_delta_sign(unsigned j) { switch (this->m_column_types[j]) { case column_type::fixed: case column_type::boxed: if (this->x_below_low_bound(j)) return -1; if (this->x_above_upper_bound(j)) return 1; break; case column_type::lower_bound: if (this->x_below_low_bound(j)) return -1; break; case column_type::upper_bound: if (this->x_above_upper_bound(j)) return 1; break; case column_type::free_column: return 0; default: lp_assert(false); } return 0; } void init_column_row_non_zeroes() { this->m_columns_nz.resize(this->m_A.column_count()); this->m_rows_nz.resize(this->m_A.row_count()); for (unsigned i = 0; i < this->m_A.column_count(); i++) { if (this->m_columns_nz[i] == 0) this->m_columns_nz[i] = this->m_A.m_columns[i].size(); } for (unsigned i = 0; i < this->m_A.row_count(); i++) { if (this->m_rows_nz[i] == 0) this->m_rows_nz[i] = this->m_A.m_rows[i].size(); } } int x_at_bound_sign(unsigned j) { switch (this->m_column_types[j]) { case column_type::fixed: return 0; case column_type::boxed: if (this->x_is_at_lower_bound(j)) return 1; return -1; break; case column_type::lower_bound: return 1; break; case column_type::upper_bound: return -1; break; default: lp_assert(false); } return 0; } unsigned solve_with_tableau(); bool basis_column_is_set_correctly(unsigned j) const { return this->m_A.m_columns[j].size() == 1; } bool basis_columns_are_set_correctly() const { for (unsigned j : this->m_basis) if(!basis_column_is_set_correctly(j)) return false; return this->m_basis_heading.size() == this->m_A.column_count() && this->m_basis.size() == this->m_A.row_count(); } void init_run_tableau(); void update_x_tableau(unsigned entering, const X & delta); void update_inf_cost_for_column_tableau(unsigned j); // the delta is between the old and the new cost (old - new) void update_reduced_cost_for_basic_column_cost_change(const T & delta, unsigned j) { lp_assert(this->m_basis_heading[j] >= 0); unsigned i = static_cast(this->m_basis_heading[j]); for (const row_cell & rc : this->m_A.m_rows[i]) { unsigned k = rc.var(); if (k == j) continue; this->m_d[k] += delta * rc.get_val(); } } bool update_basis_and_x_tableau(int entering, int leaving, X const & tt); void init_reduced_costs_tableau(); void init_tableau_rows() { m_bland_mode_tableau = false; m_left_basis_tableau.clear(); m_left_basis_tableau.resize(this->m_A.column_count()); m_left_basis_repeated = 0; } // stage1 constructor lp_primal_core_solver(static_matrix & A, vector & b, // the right side vector vector & x, // the number of elements in x needs to be at least as large as the number of columns in A vector & basis, vector & nbasis, vector & heading, vector & costs, const vector & column_type_array, const vector & lower_bound_values, const vector & upper_bound_values, lp_settings & settings, const column_namer& column_names): lp_core_solver_base(A, b, basis, nbasis, heading, x, costs, settings, column_names, column_type_array, lower_bound_values, upper_bound_values), m_beta(A.row_count()), m_epsilon_of_reduced_cost(T(1)/T(10000000)), m_bland_mode_threshold(1000) { if (!(numeric_traits::precise())) { m_converted_harris_eps = convert_struct::convert(this->m_settings.harris_feasibility_tolerance); } else { m_converted_harris_eps = zero_of_type(); } this->set_status(lp_status::UNKNOWN); } // constructor lp_primal_core_solver(static_matrix & A, vector & b, // the right side vector vector & x, // the number of elements in x needs to be at least as large as the number of columns in A vector & basis, vector & nbasis, vector & heading, vector & costs, const vector & column_type_array, const vector & upper_bound_values, lp_settings & settings, const column_namer& column_names): lp_core_solver_base(A, b, basis, nbasis, heading, x, costs, settings, column_names, column_type_array, m_lower_bounds_dummy, upper_bound_values), m_beta(A.row_count()), m_converted_harris_eps(convert_struct::convert(this->m_settings.harris_feasibility_tolerance)) { lp_assert(initial_x_is_correct()); m_lower_bounds_dummy.resize(A.column_count(), zero_of_type()); m_enter_price_eps = numeric_traits::precise() ? numeric_traits::zero() : T(1e-5); #ifdef Z3DEBUG // check_correctness(); #endif } bool initial_x_is_correct() { std::set basis_set; for (unsigned i = 0; i < this->m_A.row_count(); i++) { basis_set.insert(this->m_basis[i]); } for (unsigned j = 0; j < this->m_n(); j++) { if (this->column_has_lower_bound(j) && this->m_x[j] < numeric_traits::zero()) { LP_OUT(this->m_settings, "low bound for variable " << j << " does not hold: this->m_x[" << j << "] = " << this->m_x[j] << " is negative " << std::endl); return false; } if (this->column_has_upper_bound(j) && this->m_x[j] > this->m_upper_bounds[j]) { LP_OUT(this->m_settings, "upper bound for " << j << " does not hold: " << this->m_upper_bounds[j] << ">" << this->m_x[j] << std::endl); return false; } if (basis_set.find(j) != basis_set.end()) continue; if (this->m_column_types[j] == column_type::lower_bound) { if (numeric_traits::zero() != this->m_x[j]) { LP_OUT(this->m_settings, "only low bound is set for " << j << " but low bound value " << numeric_traits::zero() << " is not equal to " << this->m_x[j] << std::endl); return false; } } if (this->m_column_types[j] == column_type::boxed) { if (this->m_upper_bounds[j] != this->m_x[j] && !numeric_traits::is_zero(this->m_x[j])) { return false; } } } return true; } friend core_solver_pretty_printer; }; } z3-z3-4.8.7/src/util/lp/lp_primal_core_solver_def.h000066400000000000000000001473011356505360400221640ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #include #include "util/vector.h" #include #include #include #include #include "util/lp/lp_primal_core_solver.h" namespace lp { // This core solver solves (Ax=b, lower_bound_values \leq x \leq upper_bound_values, maximize costs*x ) // The right side b is given implicitly by x and the basis template void lp_primal_core_solver::sort_non_basis_rational() { lp_assert(numeric_traits::precise()); if (this->m_settings.use_tableau()) { std::sort(this->m_nbasis.begin(), this->m_nbasis.end(), [this](unsigned a, unsigned b) { unsigned ca = this->m_A.number_of_non_zeroes_in_column(a); unsigned cb = this->m_A.number_of_non_zeroes_in_column(b); if (ca == 0 && cb != 0) return false; return ca < cb; }); } else { std::sort(this->m_nbasis.begin(), this->m_nbasis.end(), [this](unsigned a, unsigned b) { unsigned ca = this->m_columns_nz[a]; unsigned cb = this->m_columns_nz[b]; if (ca == 0 && cb != 0) return false; return ca < cb; });} m_non_basis_list.clear(); // reinit m_basis_heading for (unsigned j = 0; j < this->m_nbasis.size(); j++) { unsigned col = this->m_nbasis[j]; this->m_basis_heading[col] = - static_cast(j) - 1; m_non_basis_list.push_back(col); } } template void lp_primal_core_solver::sort_non_basis() { if (numeric_traits::precise()) { sort_non_basis_rational(); return; } for (unsigned j : this->m_nbasis) { T const & da = this->m_d[j]; this->m_steepest_edge_coefficients[j] = da * da / this->m_column_norms[j]; } std::sort(this->m_nbasis.begin(), this->m_nbasis.end(), [this](unsigned a, unsigned b) { return this->m_steepest_edge_coefficients[a] > this->m_steepest_edge_coefficients[b]; }); m_non_basis_list.clear(); // reinit m_basis_heading for (unsigned j = 0; j < this->m_nbasis.size(); j++) { unsigned col = this->m_nbasis[j]; this->m_basis_heading[col] = - static_cast(j) - 1; m_non_basis_list.push_back(col); } } template bool lp_primal_core_solver::column_is_benefitial_for_entering_on_breakpoints(unsigned j) const { bool ret; const T & d = this->m_d[j]; switch (this->m_column_types[j]) { case column_type::lower_bound: lp_assert(this->x_is_at_lower_bound(j)); ret = d < -m_epsilon_of_reduced_cost; break; case column_type::upper_bound: lp_assert(this->x_is_at_upper_bound(j)); ret = d > m_epsilon_of_reduced_cost; break; case column_type::fixed: ret = false; break; case column_type::boxed: { bool lower_bound = this->x_is_at_lower_bound(j); lp_assert(lower_bound || this->x_is_at_upper_bound(j)); ret = (lower_bound && d < -m_epsilon_of_reduced_cost) || ((!lower_bound) && d > m_epsilon_of_reduced_cost); } break; case column_type::free_column: ret = d > m_epsilon_of_reduced_cost || d < - m_epsilon_of_reduced_cost; break; default: lp_unreachable(); ret = false; break; } return ret; } template bool lp_primal_core_solver::column_is_benefitial_for_entering_basis(unsigned j) const { if (numeric_traits::precise()) return column_is_benefitial_for_entering_basis_precise(j); if (this->m_using_infeas_costs && this->m_settings.use_breakpoints_in_feasibility_search) return column_is_benefitial_for_entering_on_breakpoints(j); const T& dj = this->m_d[j]; switch (this->m_column_types[j]) { case column_type::fixed: break; case column_type::free_column: if (dj > m_epsilon_of_reduced_cost || dj < -m_epsilon_of_reduced_cost) return true; break; case column_type::lower_bound: if (dj > m_epsilon_of_reduced_cost) return true;; break; case column_type::upper_bound: if (dj < -m_epsilon_of_reduced_cost) return true; break; case column_type::boxed: if (dj > m_epsilon_of_reduced_cost) { if (this->m_x[j] < this->m_upper_bounds[j] - this->bound_span(j)/2) return true; break; } else if (dj < - m_epsilon_of_reduced_cost) { if (this->m_x[j] > this->m_lower_bounds[j] + this->bound_span(j)/2) return true; } break; default: lp_unreachable(); break; } return false; } template bool lp_primal_core_solver::column_is_benefitial_for_entering_basis_precise(unsigned j) const { lp_assert (numeric_traits::precise()); if (this->m_using_infeas_costs && this->m_settings.use_breakpoints_in_feasibility_search) return column_is_benefitial_for_entering_on_breakpoints(j); const T& dj = this->m_d[j]; TRACE("lar_solver", tout << "dj=" << dj << "\n";); switch (this->m_column_types[j]) { case column_type::fixed: break; case column_type::free_column: if (!is_zero(dj)) return true; break; case column_type::lower_bound: if (dj > zero_of_type()) return true; if (dj < 0 && this->m_x[j] > this->m_lower_bounds[j]){ return true; } break; case column_type::upper_bound: if (dj < zero_of_type()) return true; if (dj > 0 && this->m_x[j] < this->m_upper_bounds[j]) { return true; } break; case column_type::boxed: if (dj > zero_of_type()) { if (this->m_x[j] < this->m_upper_bounds[j]) return true; break; } else if (dj < zero_of_type()) { if (this->m_x[j] > this->m_lower_bounds[j]) return true; } break; default: lp_unreachable(); break; } return false; } template int lp_primal_core_solver::choose_entering_column_presize(unsigned number_of_benefitial_columns_to_go_over) { // at this moment m_y = cB * B(-1) lp_assert(numeric_traits::precise()); if (number_of_benefitial_columns_to_go_over == 0) return -1; if (this->m_basis_sort_counter == 0) { sort_non_basis(); this->m_basis_sort_counter = 20; } else { this->m_basis_sort_counter--; } unsigned j_nz = this->m_m() + 1; // this number is greater than the max column size std::list::iterator entering_iter = m_non_basis_list.end(); for (auto non_basis_iter = m_non_basis_list.begin(); number_of_benefitial_columns_to_go_over && non_basis_iter != m_non_basis_list.end(); ++non_basis_iter) { unsigned j = *non_basis_iter; if (!column_is_benefitial_for_entering_basis(j)) continue; // if we are here then j is a candidate to enter the basis unsigned t = this->m_columns_nz[j]; if (t < j_nz) { j_nz = t; entering_iter = non_basis_iter; if (number_of_benefitial_columns_to_go_over) number_of_benefitial_columns_to_go_over--; } else if (t == j_nz && this->m_settings.random_next() % 2 == 0) { entering_iter = non_basis_iter; } }// while (number_of_benefitial_columns_to_go_over && initial_offset_in_non_basis != offset_in_nb); if (entering_iter == m_non_basis_list.end()) return -1; unsigned entering = *entering_iter; m_sign_of_entering_delta = this->m_d[entering] > 0 ? 1 : -1; if (this->m_using_infeas_costs && this->m_settings.use_breakpoints_in_feasibility_search) m_sign_of_entering_delta = -m_sign_of_entering_delta; m_non_basis_list.erase(entering_iter); m_non_basis_list.push_back(entering); return entering; } template int lp_primal_core_solver::choose_entering_column(unsigned number_of_benefitial_columns_to_go_over) { // at this moment m_y = cB * B(-1) if (numeric_traits::precise()) return choose_entering_column_presize(number_of_benefitial_columns_to_go_over); if (number_of_benefitial_columns_to_go_over == 0) return -1; if (this->m_basis_sort_counter == 0) { sort_non_basis(); this->m_basis_sort_counter = 20; } else { this->m_basis_sort_counter--; } T steepest_edge = zero_of_type(); std::list::iterator entering_iter = m_non_basis_list.end(); for (auto non_basis_iter= m_non_basis_list.begin(); number_of_benefitial_columns_to_go_over && non_basis_iter != m_non_basis_list.end(); ++non_basis_iter) { unsigned j = *non_basis_iter; if (!column_is_benefitial_for_entering_basis(j)) continue; // if we are here then j is a candidate to enter the basis T dj = this->m_d[j]; T t = dj * dj / this->m_column_norms[j]; if (t > steepest_edge) { steepest_edge = t; entering_iter = non_basis_iter; if (number_of_benefitial_columns_to_go_over) number_of_benefitial_columns_to_go_over--; } }// while (number_of_benefitial_columns_to_go_over && initial_offset_in_non_basis != offset_in_nb); if (entering_iter != m_non_basis_list.end()) { unsigned entering = *entering_iter; m_sign_of_entering_delta = this->m_d[entering] > 0? 1 : -1; if (this->m_using_infeas_costs && this->m_settings.use_breakpoints_in_feasibility_search) m_sign_of_entering_delta = - m_sign_of_entering_delta; m_non_basis_list.erase(entering_iter); m_non_basis_list.push_back(entering); return entering; } return -1; } template int lp_primal_core_solver::advance_on_sorted_breakpoints(unsigned entering, X &t) { T slope_at_entering = this->m_d[entering]; breakpoint * last_bp = nullptr; lp_assert(m_breakpoint_indices_queue.is_empty()==false); while (m_breakpoint_indices_queue.is_empty() == false) { unsigned bi = m_breakpoint_indices_queue.dequeue(); breakpoint *b = &m_breakpoints[bi]; change_slope_on_breakpoint(entering, b, slope_at_entering); last_bp = b; if (slope_at_entering * m_sign_of_entering_delta > - m_epsilon_of_reduced_cost) { // the slope started to increase infeasibility break; } else { if ((numeric_traits::precise() == false) || ( numeric_traits::is_zero(slope_at_entering) && this->m_settings.random_next() % 2 == 0)) { // it is not cost beneficial to advance the delta more, so just break to increase the randomness break; } } } lp_assert (last_bp != nullptr); t = last_bp->m_delta; return last_bp->m_j; } template int lp_primal_core_solver::find_leaving_and_t_with_breakpoints(unsigned entering, X & t){ lp_assert(this->precise() == false); fill_breakpoints_array(entering); return advance_on_sorted_breakpoints(entering, t); } template bool lp_primal_core_solver::get_harris_theta(X & theta) { lp_assert(this->m_ed.is_OK()); bool unlimited = true; for (unsigned i : this->m_ed.m_index) { if (this->m_settings.abs_val_is_smaller_than_pivot_tolerance(this->m_ed[i])) continue; limit_theta_on_basis_column(this->m_basis[i], - this->m_ed[i] * m_sign_of_entering_delta, theta, unlimited); if (!unlimited && is_zero(theta)) break; } return unlimited; } template int lp_primal_core_solver:: find_leaving_on_harris_theta(X const & harris_theta, X & t) { int leaving = -1; T pivot_abs_max = zero_of_type(); // we know already that there is no bound flip on entering // we also know that harris_theta is limited, so we will find a leaving zero_harris_eps(); unsigned steps = this->m_ed.m_index.size(); unsigned k = this->m_settings.random_next() % steps; unsigned initial_k = k; do { unsigned i = this->m_ed.m_index[k]; const T & ed = this->m_ed[i]; if (this->m_settings.abs_val_is_smaller_than_pivot_tolerance(ed)) { if (++k == steps) k = 0; continue; } X ratio; unsigned j = this->m_basis[i]; bool unlimited = true; limit_theta_on_basis_column(j, - ed * m_sign_of_entering_delta, ratio, unlimited); if ((!unlimited) && ratio <= harris_theta) { if (leaving == -1 || abs(ed) > pivot_abs_max) { t = ratio; leaving = j; pivot_abs_max = abs(ed); } } if (++k == steps) k = 0; } while (k != initial_k); if (!this->precise()) restore_harris_eps(); return leaving; } template bool lp_primal_core_solver::try_jump_to_another_bound_on_entering(unsigned entering, const X & theta, X & t, bool & unlimited) { switch(this->m_column_types[entering]){ case column_type::boxed: if (m_sign_of_entering_delta > 0) { t = this->m_upper_bounds[entering] - this->m_x[entering]; if (unlimited || t <= theta){ lp_assert(t >= zero_of_type()); return true; } } else { // m_sign_of_entering_delta == -1 t = this->m_x[entering] - this->m_lower_bounds[entering]; if (unlimited || t <= theta) { lp_assert(t >= zero_of_type()); return true; } } return false; case column_type::upper_bound: if (m_sign_of_entering_delta > 0) { t = this->m_upper_bounds[entering] - this->m_x[entering]; if (unlimited || t <= theta){ lp_assert(t >= zero_of_type()); return true; } } return false; case column_type::lower_bound: if (m_sign_of_entering_delta < 0) { t = this->m_x[entering] - this->m_lower_bounds[entering]; if (unlimited || t <= theta) { lp_assert(t >= zero_of_type()); return true; } } return false; default:return false; } return false; } template bool lp_primal_core_solver:: try_jump_to_another_bound_on_entering_unlimited(unsigned entering, X & t ) { if (this->m_column_types[entering] != column_type::boxed) return false; if (m_sign_of_entering_delta > 0) { t = this->m_upper_bounds[entering] - this->m_x[entering]; return true; } // m_sign_of_entering_delta == -1 t = this->m_x[entering] - this->m_lower_bounds[entering]; return true; } template int lp_primal_core_solver::find_leaving_and_t_precise(unsigned entering, X & t) { if (this->m_settings.use_breakpoints_in_feasibility_search && !this->current_x_is_feasible()) return find_leaving_and_t_with_breakpoints(entering, t); bool unlimited = true; unsigned steps = this->m_ed.m_index.size(); unsigned k = this->m_settings.random_next() % steps; unsigned initial_k = k; unsigned row_min_nz = this->m_n() + 1; m_leaving_candidates.clear(); do { unsigned i = this->m_ed.m_index[k]; const T & ed = this->m_ed[i]; lp_assert(!numeric_traits::is_zero(ed)); unsigned j = this->m_basis[i]; limit_theta_on_basis_column(j, - ed * m_sign_of_entering_delta, t, unlimited); if (!unlimited) { m_leaving_candidates.push_back(j); row_min_nz = this->m_rows_nz[i]; } if (++k == steps) k = 0; } while (unlimited && k != initial_k); if (unlimited) { if (try_jump_to_another_bound_on_entering_unlimited(entering, t)) return entering; return -1; } X ratio; while (k != initial_k) { unsigned i = this->m_ed.m_index[k]; const T & ed = this->m_ed[i]; lp_assert(!numeric_traits::is_zero(ed)); unsigned j = this->m_basis[i]; unlimited = true; limit_theta_on_basis_column(j, -ed * m_sign_of_entering_delta, ratio, unlimited); if (unlimited) { if (++k == steps) k = 0; continue; } unsigned i_nz = this->m_rows_nz[i]; if (ratio < t) { t = ratio; m_leaving_candidates.clear(); m_leaving_candidates.push_back(j); row_min_nz = this->m_rows_nz[i]; } else if (ratio == t && i_nz < row_min_nz) { m_leaving_candidates.clear(); m_leaving_candidates.push_back(j); row_min_nz = this->m_rows_nz[i]; } else if (ratio == t && i_nz == row_min_nz) { m_leaving_candidates.push_back(j); } if (++k == steps) k = 0; } ratio = t; unlimited = false; if (try_jump_to_another_bound_on_entering(entering, t, ratio, unlimited)) { t = ratio; return entering; } k = this->m_settings.random_next() % m_leaving_candidates.size(); return m_leaving_candidates[k]; } template int lp_primal_core_solver::find_leaving_and_t(unsigned entering, X & t) { if (this->m_settings.use_breakpoints_in_feasibility_search && !this->current_x_is_feasible()) return find_leaving_and_t_with_breakpoints(entering, t); X theta; bool unlimited = get_harris_theta(theta); lp_assert(unlimited || theta >= zero_of_type()); if (try_jump_to_another_bound_on_entering(entering, theta, t, unlimited)) return entering; if (unlimited) return -1; return find_leaving_on_harris_theta(theta, t); } // m is the multiplier. updating t in a way that holds the following // x[j] + t * m >= m_lower_bounds[j] ( if m < 0 ) // or // x[j] + t * m <= this->m_upper_bounds[j] ( if m > 0) template void lp_primal_core_solver::get_bound_on_variable_and_update_leaving_precisely(unsigned j, vector & leavings, T m, X & t, T & abs_of_d_of_leaving) { if (m > 0) { switch(this->m_column_types[j]) { // check that j has a low bound case column_type::free_column: case column_type::upper_bound: return; default:break; } X tt = - (this->m_lower_bounds[j] - this->m_x[j]) / m; if (numeric_traits::is_neg(tt)) tt = zero_of_type(); if (leavings.empty() || tt < t || (tt == t && m > abs_of_d_of_leaving)) { t = tt; abs_of_d_of_leaving = m; leavings.clear(); leavings.push_back(j); } else if (tt == t || m == abs_of_d_of_leaving) { leavings.push_back(j); } } else if (m < 0){ switch (this->m_column_types[j]) { // check that j has an upper bound case column_type::free_column: case column_type::lower_bound: return; default:break; } X tt = (this->m_upper_bounds[j] - this->m_x[j]) / m; if (numeric_traits::is_neg(tt)) tt = zero_of_type(); if (leavings.empty() || tt < t || (tt == t && - m > abs_of_d_of_leaving)) { t = tt; abs_of_d_of_leaving = - m; leavings.clear(); leavings.push_back(j); } else if (tt == t || m == abs_of_d_of_leaving) { leavings.push_back(j); } } } template X lp_primal_core_solver::get_max_bound(vector & b) { X ret = zero_of_type(); for (auto & v : b) { X a = abs(v); if (a > ret) ret = a; } return ret; } #ifdef Z3DEBUG template void lp_primal_core_solver::check_Ax_equal_b() { dense_matrix d(this->m_A); T * ls = d.apply_from_left_with_different_dims(this->m_x); lp_assert(vectors_are_equal(ls, this->m_b, this->m_m())); delete [] ls; } template void lp_primal_core_solver::check_the_bounds() { for (unsigned i = 0; i < this->m_n(); i++) { check_bound(i); } } template void lp_primal_core_solver::check_bound(unsigned i) { lp_assert (!(this->column_has_lower_bound(i) && (numeric_traits::zero() > this->m_x[i]))); lp_assert (!(this->column_has_upper_bound(i) && (this->m_upper_bounds[i] < this->m_x[i]))); } template void lp_primal_core_solver::check_correctness() { check_the_bounds(); check_Ax_equal_b(); } #endif // from page 183 of Istvan Maros's book // the basis structures have not changed yet template void lp_primal_core_solver::update_reduced_costs_from_pivot_row(unsigned entering, unsigned leaving) { // the basis heading has changed already #ifdef Z3DEBUG auto & basis_heading = this->m_basis_heading; lp_assert(basis_heading[entering] >= 0 && static_cast(basis_heading[entering]) < this->m_m()); lp_assert(basis_heading[leaving] < 0); #endif T pivot = this->m_pivot_row[entering]; T dq = this->m_d[entering]/pivot; for (auto j : this->m_pivot_row.m_index) { // for (auto j : this->m_nbasis) if (this->m_basis_heading[j] >= 0) continue; if (j != leaving) this->m_d[j] -= dq * this->m_pivot_row[j]; } this->m_d[leaving] = -dq; if (this->current_x_is_infeasible() && !this->m_settings.use_breakpoints_in_feasibility_search) { this->m_d[leaving] -= this->m_costs[leaving]; this->m_costs[leaving] = zero_of_type(); } this->m_d[entering] = numeric_traits::zero(); } // return 0 if the reduced cost at entering is close enough to the refreshed // 1 if it is way off, and 2 if it is unprofitable template int lp_primal_core_solver::refresh_reduced_cost_at_entering_and_check_that_it_is_off(unsigned entering) { if (numeric_traits::precise()) return 0; T reduced_at_entering_was = this->m_d[entering]; // can benefit from going over non-zeros of m_ed lp_assert(abs(reduced_at_entering_was) > m_epsilon_of_reduced_cost); T refreshed_cost = this->m_costs[entering]; unsigned i = this->m_m(); while (i--) refreshed_cost -= this->m_costs[this->m_basis[i]] * this->m_ed[i]; this->m_d[entering] = refreshed_cost; T delta = abs(reduced_at_entering_was - refreshed_cost); if (delta * 2 > abs(reduced_at_entering_was)) { // this->m_status = UNSTABLE; if (reduced_at_entering_was > m_epsilon_of_reduced_cost) { if (refreshed_cost <= zero_of_type()) return 2; // abort entering } else { if (refreshed_cost > -m_epsilon_of_reduced_cost) return 2; // abort entering } return 1; // go on with this entering } else { if (reduced_at_entering_was > m_epsilon_of_reduced_cost) { if (refreshed_cost <= zero_of_type()) return 2; // abort entering } else { if (refreshed_cost > -m_epsilon_of_reduced_cost) return 2; // abort entering } } return 0; } template void lp_primal_core_solver::backup_and_normalize_costs() { if (this->m_look_for_feasible_solution_only) return; // no need to backup cost, since we are going to use only feasibility costs if (numeric_traits::precise() ) { m_costs_backup = this->m_costs; } else { T cost_max = std::max(max_abs_in_vector(this->m_costs), T(1)); lp_assert(m_costs_backup.size() == 0); for (unsigned j = 0; j < this->m_costs.size(); j++) m_costs_backup.push_back(this->m_costs[j] /= cost_max); } } template void lp_primal_core_solver::init_run() { this->m_basis_sort_counter = 0; // to initiate the sort of the basis this->set_total_iterations(0); this->iters_with_no_cost_growing() = 0; init_inf_set(); if (this->current_x_is_feasible() && this->m_look_for_feasible_solution_only) return; this->m_using_infeas_costs = false; if (this->m_settings.backup_costs) backup_and_normalize_costs(); m_epsilon_of_reduced_cost = numeric_traits::precise()? zero_of_type(): T(1)/T(10000000); m_breakpoint_indices_queue.resize(this->m_n()); init_reduced_costs(); if (!numeric_traits::precise()) { this->m_column_norm_update_counter = 0; init_column_norms(); } else { if (this->m_columns_nz.size() != this->m_n()) init_column_row_non_zeroes(); } } template void lp_primal_core_solver::calc_working_vector_beta_for_column_norms(){ lp_assert(numeric_traits::precise() == false); lp_assert(this->m_ed.is_OK()); lp_assert(m_beta.is_OK()); m_beta = this->m_ed; this->m_factorization->solve_yB_with_error_check_indexed(m_beta, this->m_basis_heading, this->m_basis, this->m_settings); } template void lp_primal_core_solver::advance_on_entering_equal_leaving(int entering, X & t) { CASSERT("A_off", !this->A_mult_x_is_off() ); this->update_x(entering, t * m_sign_of_entering_delta); if (this->A_mult_x_is_off_on_index(this->m_ed.m_index) && !this->find_x_by_solving()) { this->init_lu(); if (!this->find_x_by_solving()) { this->restore_x(entering, t * m_sign_of_entering_delta); this->iters_with_no_cost_growing()++; LP_OUT(this->m_settings, "failing in advance_on_entering_equal_leaving for entering = " << entering << std::endl); return; } } if (this->m_using_infeas_costs) { lp_assert(is_zero(this->m_costs[entering])); init_infeasibility_costs_for_changed_basis_only(); } if (this->m_look_for_feasible_solution_only && this->current_x_is_feasible()) return; if (need_to_switch_costs() ||!this->current_x_is_feasible()) { init_reduced_costs(); } this->iters_with_no_cost_growing() = 0; } template void lp_primal_core_solver::advance_on_entering_and_leaving(int entering, int leaving, X & t) { lp_assert(entering >= 0 && m_non_basis_list.back() == static_cast(entering)); lp_assert(this->m_using_infeas_costs || t >= zero_of_type()); lp_assert(leaving >= 0 && entering >= 0); lp_assert(entering != leaving || !is_zero(t)); // otherwise nothing changes if (entering == leaving) { advance_on_entering_equal_leaving(entering, t); return; } unsigned pivot_row = this->m_basis_heading[leaving]; this->calculate_pivot_row_of_B_1(pivot_row); this->calculate_pivot_row_when_pivot_row_of_B1_is_ready(pivot_row); int pivot_compare_result = this->pivots_in_column_and_row_are_different(entering, leaving); if (!pivot_compare_result){;} else if (pivot_compare_result == 2) { // the sign is changed, cannot continue this->set_status(lp_status::UNSTABLE); this->iters_with_no_cost_growing()++; return; } else { lp_assert(pivot_compare_result == 1); this->init_lu(); if (this->m_factorization == nullptr || this->m_factorization->get_status() != LU_status::OK) { this->set_status(lp_status::UNSTABLE); this->iters_with_no_cost_growing()++; return; } } if (!numeric_traits::precise()) calc_working_vector_beta_for_column_norms(); if (this->current_x_is_feasible() || !this->m_settings.use_breakpoints_in_feasibility_search) { if (m_sign_of_entering_delta == -1) t = -t; } if (!this->update_basis_and_x(entering, leaving, t)) { if (this->get_status() == lp_status::FLOATING_POINT_ERROR) return; if (this->m_look_for_feasible_solution_only) { this->set_status(lp_status::FLOATING_POINT_ERROR); return; } init_reduced_costs(); return; } if (!is_zero(t)) { this->iters_with_no_cost_growing() = 0; init_infeasibility_after_update_x_if_inf(leaving); } if (this->current_x_is_feasible()) { this->set_status(lp_status::FEASIBLE); if (this->m_look_for_feasible_solution_only) return; } if (numeric_traits::precise() == false) update_or_init_column_norms(entering, leaving); if (need_to_switch_costs()) { init_reduced_costs(); } else { update_reduced_costs_from_pivot_row(entering, leaving); } lp_assert(!need_to_switch_costs()); std::list::iterator it = m_non_basis_list.end(); it--; * it = static_cast(leaving); } template void lp_primal_core_solver::advance_on_entering_precise(int entering) { lp_assert(numeric_traits::precise()); lp_assert(entering > -1); this->solve_Bd(entering); X t; int leaving = find_leaving_and_t_precise(entering, t); if (leaving == -1) { TRACE("lar_solver", tout << "non-leaving\n";); this->set_status(lp_status::UNBOUNDED); return; } advance_on_entering_and_leaving(entering, leaving, t); } template void lp_primal_core_solver::advance_on_entering(int entering) { if (numeric_traits::precise()) { advance_on_entering_precise(entering); return; } lp_assert(entering > -1); this->solve_Bd(entering); int refresh_result = refresh_reduced_cost_at_entering_and_check_that_it_is_off(entering); if (refresh_result) { if (this->m_look_for_feasible_solution_only) { this->set_status(lp_status::FLOATING_POINT_ERROR); return; } this->init_lu(); init_reduced_costs(); if (refresh_result == 2) { this->iters_with_no_cost_growing()++; return; } } X t; int leaving = find_leaving_and_t(entering, t); if (leaving == -1){ if (!this->current_x_is_feasible()) { lp_assert(!numeric_traits::precise()); // we cannot have unbounded with inf costs // if (m_look_for_feasible_solution_only) { // this->m_status = INFEASIBLE; // return; // } if (this->get_status() == lp_status::UNSTABLE) { this->set_status(lp_status::FLOATING_POINT_ERROR); return; } init_infeasibility_costs(); this->set_status(lp_status::UNSTABLE); return; } if (this->get_status() == lp_status::TENTATIVE_UNBOUNDED) { this->set_status(lp_status::UNBOUNDED); } else { this->set_status(lp_status::TENTATIVE_UNBOUNDED); } TRACE("lar_solver", tout << this->get_status() << "\n";); return; } advance_on_entering_and_leaving(entering, leaving, t); } template void lp_primal_core_solver::push_forward_offset_in_non_basis(unsigned & offset_in_nb) { if (++offset_in_nb == this->m_nbasis.size()) offset_in_nb = 0; } template unsigned lp_primal_core_solver::get_number_of_non_basic_column_to_try_for_enter() { unsigned ret = static_cast(this->m_nbasis.size()); if (this->get_status() == lp_status::TENTATIVE_UNBOUNDED) return ret; // we really need to find entering with a large reduced cost if (ret > 300) { ret = (unsigned)(ret * this->m_settings.percent_of_entering_to_check / 100); } if (ret == 0) { return 0; } return std::max(static_cast(this->m_settings.random_next() % ret), 1u); } template void lp_primal_core_solver::print_column_norms(std::ostream & out) { out << " column norms " << std::endl; for (unsigned j = 0; j < this->m_n(); j++) { out << this->m_column_norms[j] << " "; } out << std::endl; } // returns the number of iterations template unsigned lp_primal_core_solver::solve() { TRACE("lar_solver", tout << "solve " << this->get_status() << "\n";); if (numeric_traits::precise() && this->m_settings.use_tableau()) return solve_with_tableau(); init_run(); if (this->current_x_is_feasible() && this->m_look_for_feasible_solution_only) { this->set_status(lp_status::FEASIBLE); return 0; } if ((!numeric_traits::precise()) && this->A_mult_x_is_off()) { this->set_status(lp_status::FLOATING_POINT_ERROR); return 0; } do { if (this->print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over((this->m_using_infeas_costs? "inf" : "feas"), * this->m_settings.get_message_ostream())) { return this->total_iterations(); } one_iteration(); TRACE("lar_solver", tout << "one iteration: " << this->get_status() << "\n";); lp_assert(!this->m_using_infeas_costs || this->costs_on_nbasis_are_zeros()); switch (this->get_status()) { case lp_status::OPTIMAL: // double check that we are at optimum case lp_status::INFEASIBLE: if (this->m_look_for_feasible_solution_only && this->current_x_is_feasible()) break; if (!numeric_traits::precise()) { if(this->m_look_for_feasible_solution_only) break; this->init_lu(); if (this->m_factorization->get_status() != LU_status::OK) { this->set_status (lp_status::FLOATING_POINT_ERROR); break; } init_reduced_costs(); if (choose_entering_column(1) == -1) { decide_on_status_when_cannot_find_entering(); break; } this->set_status(lp_status::UNKNOWN); } else { // precise case if (this->m_look_for_feasible_solution_only) { // todo: keep the reduced costs correct all the time! init_reduced_costs(); if (choose_entering_column(1) == -1) { decide_on_status_when_cannot_find_entering(); break; } this->set_status(lp_status::UNKNOWN); } } break; case lp_status::TENTATIVE_UNBOUNDED: this->init_lu(); if (this->m_factorization->get_status() != LU_status::OK) { this->set_status(lp_status::FLOATING_POINT_ERROR); break; } init_reduced_costs(); break; case lp_status::UNBOUNDED: if (this->current_x_is_infeasible()) { init_reduced_costs(); this->set_status(lp_status::UNKNOWN); } break; case lp_status::UNSTABLE: lp_assert(! (numeric_traits::precise())); this->init_lu(); if (this->m_factorization->get_status() != LU_status::OK) { this->set_status(lp_status::FLOATING_POINT_ERROR); break; } init_reduced_costs(); break; default: break; // do nothing } } while (this->get_status() != lp_status::FLOATING_POINT_ERROR && this->get_status() != lp_status::UNBOUNDED && this->get_status() != lp_status::OPTIMAL && this->get_status() != lp_status::INFEASIBLE && this->iters_with_no_cost_growing() <= this->m_settings.max_number_of_iterations_with_no_improvements && this->total_iterations() <= this->m_settings.max_total_number_of_iterations && !(this->current_x_is_feasible() && this->m_look_for_feasible_solution_only)); lp_assert(this->get_status() == lp_status::FLOATING_POINT_ERROR || this->current_x_is_feasible() == false || this->calc_current_x_is_feasible_include_non_basis()); return this->total_iterations(); } template void lp_primal_core_solver::delete_factorization() { if (this->m_factorization != nullptr) { delete this->m_factorization; this->m_factorization = nullptr; } } // according to Swietanowski, " A new steepest edge approximation for the simplex method for linear programming" template void lp_primal_core_solver::init_column_norms() { lp_assert(numeric_traits::precise() == false); for (unsigned j = 0; j < this->m_n(); j++) { this->m_column_norms[j] = T(static_cast(this->m_A.m_columns[j].size() + 1)) + T(static_cast(this->m_settings.random_next() % 10000)) / T(100000); } } // debug only template T lp_primal_core_solver::calculate_column_norm_exactly(unsigned j) { lp_assert(numeric_traits::precise() == false); indexed_vector w(this->m_m()); this->m_A.copy_column_to_vector(j, w); vector d(this->m_m()); this->m_factorization->solve_Bd_when_w_is_ready(d, w); T ret = zero_of_type(); for (auto v : d) ret += v*v; return ret+1; } template void lp_primal_core_solver::update_or_init_column_norms(unsigned entering, unsigned leaving) { lp_assert(numeric_traits::precise() == false); lp_assert(m_column_norm_update_counter <= this->m_settings.column_norms_update_frequency); if (m_column_norm_update_counter == this->m_settings.column_norms_update_frequency) { m_column_norm_update_counter = 0; init_column_norms(); } else { m_column_norm_update_counter++; update_column_norms(entering, leaving); } } // following Swietanowski - A new steepest ... template void lp_primal_core_solver::update_column_norms(unsigned entering, unsigned leaving) { lp_assert(numeric_traits::precise() == false); T pivot = this->m_pivot_row[entering]; T g_ent = calculate_norm_of_entering_exactly() / pivot / pivot; if (!numeric_traits::precise()) { if (g_ent < T(0.000001)) g_ent = T(0.000001); } this->m_column_norms[leaving] = g_ent; for (unsigned j : this->m_pivot_row.m_index) { if (j == leaving) continue; const T & t = this->m_pivot_row[j]; T s = this->m_A.dot_product_with_column(m_beta.m_data, j); T k = -2 / pivot; T tp = t/pivot; if (this->m_column_types[j] != column_type::fixed) { // a fixed columns do not enter the basis, we don't use the norm of a fixed column this->m_column_norms[j] = std::max(this->m_column_norms[j] + t * (t * g_ent + k * s), // see Istvan Maros, page 196 1 + tp * tp); } } } template T lp_primal_core_solver::calculate_norm_of_entering_exactly() { T r = numeric_traits::one(); for (auto i : this->m_ed.m_index) { T t = this->m_ed[i]; r += t * t; } return r; } // calling it stage1 is too cryptic template void lp_primal_core_solver::find_feasible_solution() { this->m_look_for_feasible_solution_only = true; lp_assert(this->non_basic_columns_are_set_correctly()); this->set_status(lp_status::UNKNOWN); solve(); } template void lp_primal_core_solver::one_iteration() { unsigned number_of_benefitial_columns_to_go_over = get_number_of_non_basic_column_to_try_for_enter(); int entering = choose_entering_column(number_of_benefitial_columns_to_go_over); if (entering == -1) { decide_on_status_when_cannot_find_entering(); } else { advance_on_entering(entering); } } template void lp_primal_core_solver::update_basis_and_x_with_comparison(unsigned entering, unsigned leaving, X delta) { if (entering != leaving) this->update_basis_and_x(entering, leaving, delta); else this->update_x(entering, delta); } template void lp_primal_core_solver::clear_breakpoints() { m_breakpoints.clear(); m_breakpoint_indices_queue.clear(); } template void lp_primal_core_solver::fill_breakpoints_array(unsigned entering) { clear_breakpoints(); for (unsigned i : this->m_ed.m_index) try_add_breakpoint_in_row(i); if (this->m_column_types[entering] == column_type::boxed) { if (m_sign_of_entering_delta < 0) add_breakpoint(entering, - this->bound_span(entering), low_break); else add_breakpoint(entering, this->bound_span(entering), upper_break); } } template bool lp_primal_core_solver::done() { if (this->get_status() == lp_status::OPTIMAL || this->get_status() == lp_status::FLOATING_POINT_ERROR) return true; if (this->get_status() == lp_status::INFEASIBLE) { return true; } if (this->m_iters_with_no_cost_growing >= this->m_settings.max_number_of_iterations_with_no_improvements) { this->get_status() = lp_status::ITERATIONS_EXHAUSTED; return true; } if (this->total_iterations() >= this->m_settings.max_total_number_of_iterations) { this->get_status() = lp_status::ITERATIONS_EXHAUSTED; return true; } return false; } template void lp_primal_core_solver::init_infeasibility_costs_for_changed_basis_only() { for (unsigned i : this->m_ed.m_index) init_infeasibility_cost_for_column(this->m_basis[i]); this->m_using_infeas_costs = true; } template void lp_primal_core_solver::init_infeasibility_costs() { lp_assert(this->m_x.size() >= this->m_n()); lp_assert(this->m_column_types.size() >= this->m_n()); for (unsigned j = this->m_n(); j--;) init_infeasibility_cost_for_column(j); this->m_using_infeas_costs = true; } template T lp_primal_core_solver::get_infeasibility_cost_for_column(unsigned j) const { if (this->m_basis_heading[j] < 0) { return zero_of_type(); } T ret; // j is a basis column switch (this->m_column_types[j]) { case column_type::fixed: case column_type::boxed: if (this->x_above_upper_bound(j)) { ret = 1; } else if (this->x_below_low_bound(j)) { ret = -1; } else { ret = numeric_traits::zero(); } break; case column_type::lower_bound: if (this->x_below_low_bound(j)) { ret = -1; } else { ret = numeric_traits::zero(); } break; case column_type::upper_bound: if (this->x_above_upper_bound(j)) { ret = 1; } else { ret = numeric_traits::zero(); } break; case column_type::free_column: ret = numeric_traits::zero(); break; default: lp_assert(false); ret = numeric_traits::zero(); // does not matter break; } if (!this->m_settings.use_breakpoints_in_feasibility_search) { ret = - ret; } return ret; } // changed m_inf_set too! template void lp_primal_core_solver::init_infeasibility_cost_for_column(unsigned j) { // If j is a breakpoint column, then we set the cost zero. // When anylyzing an entering column candidate we update the cost of the breakpoints columns to get the left or the right derivative if the infeasibility function // set zero cost for each non-basis column if (this->m_basis_heading[j] < 0) { this->m_costs[j] = numeric_traits::zero(); this->m_inf_set.erase(j); return; } // j is a basis column switch (this->m_column_types[j]) { case column_type::fixed: case column_type::boxed: if (this->x_above_upper_bound(j)) { this->m_costs[j] = 1; } else if (this->x_below_low_bound(j)) { this->m_costs[j] = -1; } else { this->m_costs[j] = numeric_traits::zero(); } break; case column_type::lower_bound: if (this->x_below_low_bound(j)) { this->m_costs[j] = -1; } else { this->m_costs[j] = numeric_traits::zero(); } break; case column_type::upper_bound: if (this->x_above_upper_bound(j)) { this->m_costs[j] = 1; } else { this->m_costs[j] = numeric_traits::zero(); } break; case column_type::free_column: this->m_costs[j] = numeric_traits::zero(); break; default: lp_assert(false); break; } if (numeric_traits::is_zero(this->m_costs[j])) { this->remove_column_from_inf_set(j); } else { this->insert_column_into_inf_set(j); } if (!this->m_settings.use_breakpoints_in_feasibility_search) { this->m_costs[j] = - this->m_costs[j]; } } template void lp_primal_core_solver::print_column(unsigned j, std::ostream & out) { out << this->column_name(j) << " " << j << " " << column_type_to_string(this->m_column_type[j]) << " x = " << this->m_x[j] << " " << "c = " << this->m_costs[j];; switch (this->m_column_type[j]) { case column_type::fixed: case column_type::boxed: out << "( " << this->m_lower_bounds[j] << " " << this->m_x[j] << " " << this->m_upper_bounds[j] << ")" << std::endl; break; case column_type::upper_bound: out << "( _" << this->m_x[j] << " " << this->m_upper_bounds[j] << ")" << std::endl; break; case column_type::lower_bound: out << "( " << this->m_lower_bounds[j] << " " << this->m_x[j] << " " << "_ )" << std::endl; break; case column_type::free_column: out << "( _" << this->m_x[j] << "_)" << std::endl; break; default: lp_unreachable(); } } template void lp_primal_core_solver::add_breakpoint(unsigned j, X delta, breakpoint_type type) { m_breakpoints.push_back(breakpoint(j, delta, type)); m_breakpoint_indices_queue.enqueue(m_breakpoint_indices_queue.size(), abs(delta)); } // j is the basic column, x is the value at x[j] // d is the coefficient before m_entering in the row with j as the basis column template void lp_primal_core_solver::try_add_breakpoint(unsigned j, const X & x, const T & d, breakpoint_type break_type, const X & break_value) { X diff = x - break_value; if (is_zero(diff)) { switch (break_type) { case low_break: if (!same_sign_with_entering_delta(d)) return; // no breakpoint break; case upper_break: if (same_sign_with_entering_delta(d)) return; // no breakpoint break; default: break; } add_breakpoint(j, zero_of_type(), break_type); return; } auto delta_j = diff / d; if (same_sign_with_entering_delta(delta_j)) add_breakpoint(j, delta_j, break_type); } template std::string lp_primal_core_solver::break_type_to_string(breakpoint_type type) { switch (type){ case low_break: return "low_break"; case upper_break: return "upper_break"; case fixed_break: return "fixed_break"; default: lp_assert(false); break; } return "type is not found"; } template void lp_primal_core_solver::print_breakpoint(const breakpoint * b, std::ostream & out) { out << "(" << this->column_name(b->m_j) << "," << break_type_to_string(b->m_type) << "," << T_to_string(b->m_delta) << ")" << std::endl; print_bound_info_and_x(b->m_j, out); } template void lp_primal_core_solver::init_reduced_costs() { lp_assert(!this->use_tableau()); if (this->current_x_is_infeasible() && !this->m_using_infeas_costs) { init_infeasibility_costs(); } else if (this->current_x_is_feasible() && this->m_using_infeas_costs) { if (this->m_look_for_feasible_solution_only) return; this->m_costs = m_costs_backup; this->m_using_infeas_costs = false; } this->init_reduced_costs_for_one_iteration(); } template void lp_primal_core_solver::change_slope_on_breakpoint(unsigned entering, breakpoint * b, T & slope_at_entering) { if (b->m_j == entering) { lp_assert(b->m_type != fixed_break && (!is_zero(b->m_delta))); slope_at_entering += m_sign_of_entering_delta; return; } lp_assert(this->m_basis_heading[b->m_j] >= 0); unsigned i_row = this->m_basis_heading[b->m_j]; const T & d = - this->m_ed[i_row]; if (numeric_traits::is_zero(d)) return; T delta = m_sign_of_entering_delta * abs(d); switch (b->m_type) { case fixed_break: if (is_zero(b->m_delta)) { slope_at_entering += delta; } else { slope_at_entering += 2 * delta; } break; case low_break: case upper_break: slope_at_entering += delta; break; default: lp_assert(false); } } template void lp_primal_core_solver::try_add_breakpoint_in_row(unsigned i) { lp_assert(i < this->m_m()); const T & d = this->m_ed[i]; // the coefficient before m_entering in the i-th row if (d == 0) return; // the change of x[m_entering] will not change the corresponding basis x unsigned j = this->m_basis[i]; const X & x = this->m_x[j]; switch (this->m_column_types[j]) { case column_type::fixed: try_add_breakpoint(j, x, d, fixed_break, this->m_lower_bounds[j]); break; case column_type::boxed: try_add_breakpoint(j, x, d, low_break, this->m_lower_bounds[j]); try_add_breakpoint(j, x, d, upper_break, this->m_upper_bounds[j]); break; case column_type::lower_bound: try_add_breakpoint(j, x, d, low_break, this->m_lower_bounds[j]); break; case column_type::upper_bound: try_add_breakpoint(j, x, d, upper_break, this->m_upper_bounds[j]); break; case column_type::free_column: break; default: lp_assert(false); break; } } template void lp_primal_core_solver::print_bound_info_and_x(unsigned j, std::ostream & out) { out << "type of " << this->column_name(j) << " is " << column_type_to_string(this->m_column_types[j]) << std::endl; out << "x[" << this->column_name(j) << "] = " << this->m_x[j] << std::endl; switch (this->m_column_types[j]) { case column_type::fixed: case column_type::boxed: out << "[" << this->m_lower_bounds[j] << "," << this->m_upper_bounds[j] << "]" << std::endl; break; case column_type::lower_bound: out << "[" << this->m_lower_bounds[j] << ", inf" << std::endl; break; case column_type::upper_bound: out << "inf ," << this->m_upper_bounds[j] << "]" << std::endl; break; case column_type::free_column: out << "inf, inf" << std::endl; break; default: lp_assert(false); break; } } } z3-z3-4.8.7/src/util/lp/lp_primal_core_solver_tableau_def.h000066400000000000000000000374141356505360400236640ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ // this is a part of lp_primal_core_solver that deals with the tableau #include "util/lp/lp_primal_core_solver.h" namespace lp { template void lp_primal_core_solver::one_iteration_tableau() { int entering = choose_entering_column_tableau(); if (entering == -1) { decide_on_status_when_cannot_find_entering(); } else { advance_on_entering_tableau(entering); } lp_assert(this->inf_set_is_correct()); } template void lp_primal_core_solver::advance_on_entering_tableau(int entering) { X t; int leaving = find_leaving_and_t_tableau(entering, t); if (leaving == -1) { TRACE("lar_solver", tout << "nothing leaving " << entering << "\n";); this->set_status(lp_status::UNBOUNDED); return; } advance_on_entering_and_leaving_tableau(entering, leaving, t); } /* template int lp_primal_core_solver::choose_entering_column_tableau_rows() { int i = find_inf_row(); if (i == -1) return -1; return find_shortest_beneficial_column_in_row(i); } */ template int lp_primal_core_solver::choose_entering_column_tableau() { //this moment m_y = cB * B(-1) unsigned number_of_benefitial_columns_to_go_over = get_number_of_non_basic_column_to_try_for_enter(); lp_assert(numeric_traits::precise()); if (number_of_benefitial_columns_to_go_over == 0) return -1; if (this->m_basis_sort_counter == 0) { sort_non_basis(); this->m_basis_sort_counter = 20; } else { this->m_basis_sort_counter--; } unsigned j_nz = this->m_m() + 1; // this number is greater than the max column size std::list::iterator entering_iter = m_non_basis_list.end(); for (auto non_basis_iter = m_non_basis_list.begin(); number_of_benefitial_columns_to_go_over && non_basis_iter != m_non_basis_list.end(); ++non_basis_iter) { unsigned j = *non_basis_iter; if (!column_is_benefitial_for_entering_basis(j)) continue; // if we are here then j is a candidate to enter the basis unsigned t = this->m_A.number_of_non_zeroes_in_column(j); if (t < j_nz) { j_nz = t; entering_iter = non_basis_iter; if (number_of_benefitial_columns_to_go_over) number_of_benefitial_columns_to_go_over--; } else if (t == j_nz && this->m_settings.random_next() % 2 == 0) { entering_iter = non_basis_iter; } }// while (number_of_benefitial_columns_to_go_over && initial_offset_in_non_basis != offset_in_nb); if (entering_iter == m_non_basis_list.end()) return -1; unsigned entering = *entering_iter; m_sign_of_entering_delta = this->m_d[entering] > 0 ? 1 : -1; if (this->m_using_infeas_costs && this->m_settings.use_breakpoints_in_feasibility_search) m_sign_of_entering_delta = -m_sign_of_entering_delta; m_non_basis_list.erase(entering_iter); m_non_basis_list.push_back(entering); return entering; } template unsigned lp_primal_core_solver::solve_with_tableau() { init_run_tableau(); if (this->current_x_is_feasible() && this->m_look_for_feasible_solution_only) { this->set_status(lp_status::FEASIBLE); return 0; } if ((!numeric_traits::precise()) && this->A_mult_x_is_off()) { this->set_status(lp_status::FLOATING_POINT_ERROR); return 0; } do { if (this->print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over((this->m_using_infeas_costs? "inf t" : "feas t"), * this->m_settings.get_message_ostream())) { return this->total_iterations(); } if (this->m_settings.use_tableau_rows()) { one_iteration_tableau_rows(); } else one_iteration_tableau(); TRACE("lar_solver", tout << "one iteration tableau " << this->get_status() << "\n";); switch (this->get_status()) { case lp_status::OPTIMAL: // double check that we are at optimum case lp_status::INFEASIBLE: if (this->m_look_for_feasible_solution_only && this->current_x_is_feasible()) break; if (!numeric_traits::precise()) { if(this->m_look_for_feasible_solution_only) break; this->init_lu(); if (this->m_factorization->get_status() != LU_status::OK) { this->set_status(lp_status::FLOATING_POINT_ERROR); break; } init_reduced_costs(); if (choose_entering_column(1) == -1) { decide_on_status_when_cannot_find_entering(); break; } this->set_status(lp_status::UNKNOWN); } else { // precise case if ((!this->infeasibility_costs_are_correct())) { init_reduced_costs_tableau(); // forcing recalc if (choose_entering_column_tableau() == -1) { decide_on_status_when_cannot_find_entering(); break; } this->set_status(lp_status::UNKNOWN); } } break; case lp_status::TENTATIVE_UNBOUNDED: this->init_lu(); if (this->m_factorization->get_status() != LU_status::OK) { this->set_status(lp_status::FLOATING_POINT_ERROR); break; } init_reduced_costs(); break; case lp_status::UNBOUNDED: if (this->current_x_is_infeasible()) { init_reduced_costs(); this->set_status(lp_status::UNKNOWN); } break; case lp_status::UNSTABLE: lp_assert(! (numeric_traits::precise())); this->init_lu(); if (this->m_factorization->get_status() != LU_status::OK) { this->set_status(lp_status::FLOATING_POINT_ERROR); break; } init_reduced_costs(); break; default: break; // do nothing } } while (this->get_status() != lp_status::FLOATING_POINT_ERROR && this->get_status() != lp_status::UNBOUNDED && this->get_status() != lp_status::OPTIMAL && this->get_status() != lp_status::INFEASIBLE && this->iters_with_no_cost_growing() <= this->m_settings.max_number_of_iterations_with_no_improvements && this->total_iterations() <= this->m_settings.max_total_number_of_iterations && !(this->current_x_is_feasible() && this->m_look_for_feasible_solution_only) && this->m_settings.get_cancel_flag() == false); if (this->m_settings.get_cancel_flag()) { this->set_status(lp_status::CANCELLED); } lp_assert( this->get_status() == lp_status::FLOATING_POINT_ERROR || this->get_status() == lp_status::CANCELLED || this->current_x_is_feasible() == false || this->calc_current_x_is_feasible_include_non_basis()); return this->total_iterations(); } template void lp_primal_core_solver::advance_on_entering_and_leaving_tableau(int entering, int leaving, X & t) { CASSERT("A_off", this->A_mult_x_is_off() == false); lp_assert(leaving >= 0 && entering >= 0); lp_assert((this->m_settings.simplex_strategy() == simplex_strategy_enum::tableau_rows) || m_non_basis_list.back() == static_cast(entering)); lp_assert(this->m_using_infeas_costs || !is_neg(t)); lp_assert(entering != leaving || !is_zero(t)); // otherwise nothing changes if (entering == leaving) { advance_on_entering_equal_leaving_tableau(entering, t); return; } if (!is_zero(t)) { if (this->current_x_is_feasible() || !this->m_settings.use_breakpoints_in_feasibility_search ) { if (m_sign_of_entering_delta == -1) t = -t; } this->update_basis_and_x_tableau(entering, leaving, t); CASSERT("A_off", this->A_mult_x_is_off() == false); this->iters_with_no_cost_growing() = 0; } else { this->pivot_column_tableau(entering, this->m_basis_heading[leaving]); this->change_basis(entering, leaving); } if (this->m_look_for_feasible_solution_only && this->current_x_is_feasible()) return; if (this->m_settings.simplex_strategy() != simplex_strategy_enum::tableau_rows) { if (need_to_switch_costs()) { this->init_reduced_costs_tableau(); } lp_assert(!need_to_switch_costs()); std::list::iterator it = m_non_basis_list.end(); it--; * it = static_cast(leaving); } } template void lp_primal_core_solver::advance_on_entering_equal_leaving_tableau(int entering, X & t) { CASSERT("A_off", !this->A_mult_x_is_off() ); this->update_x_tableau(entering, t * m_sign_of_entering_delta); if (this->m_look_for_feasible_solution_only && this->current_x_is_feasible()) return; if (need_to_switch_costs()) { init_reduced_costs_tableau(); } this->iters_with_no_cost_growing() = 0; } template int lp_primal_core_solver::find_leaving_and_t_tableau(unsigned entering, X & t) { unsigned k = 0; bool unlimited = true; unsigned row_min_nz = this->m_n() + 1; m_leaving_candidates.clear(); auto & col = this->m_A.m_columns[entering]; unsigned col_size = col.size(); for (;k < col_size && unlimited; k++) { const column_cell & c = col[k]; unsigned i = c.var(); const T & ed = this->m_A.get_val(c); lp_assert(!numeric_traits::is_zero(ed)); unsigned j = this->m_basis[i]; limit_theta_on_basis_column(j, - ed * m_sign_of_entering_delta, t, unlimited); if (!unlimited) { m_leaving_candidates.push_back(j); row_min_nz = this->m_A.m_rows[i].size(); } } if (unlimited) { if (try_jump_to_another_bound_on_entering_unlimited(entering, t)) return entering; return -1; } X ratio; for (;k < col_size; k++) { const column_cell & c = col[k]; unsigned i = c.var(); const T & ed = this->m_A.get_val(c); lp_assert(!numeric_traits::is_zero(ed)); unsigned j = this->m_basis[i]; unlimited = true; limit_theta_on_basis_column(j, -ed * m_sign_of_entering_delta, ratio, unlimited); if (unlimited) continue; unsigned i_nz = this->m_A.m_rows[i].size(); if (ratio < t) { t = ratio; m_leaving_candidates.clear(); m_leaving_candidates.push_back(j); row_min_nz = i_nz; } else if (ratio == t && i_nz < row_min_nz) { m_leaving_candidates.clear(); m_leaving_candidates.push_back(j); row_min_nz = this->m_A.m_rows[i].size(); } else if (ratio == t && i_nz == row_min_nz) { m_leaving_candidates.push_back(j); } } ratio = t; unlimited = false; if (try_jump_to_another_bound_on_entering(entering, t, ratio, unlimited)) { t = ratio; return entering; } if (m_leaving_candidates.size() == 1) return m_leaving_candidates[0]; k = this->m_settings.random_next() % m_leaving_candidates.size(); return m_leaving_candidates[k]; } template void lp_primal_core_solver::init_run_tableau() { CASSERT("A_off", this->A_mult_x_is_off() == false); lp_assert(basis_columns_are_set_correctly()); this->m_basis_sort_counter = 0; // to initiate the sort of the basis this->set_total_iterations(0); this->iters_with_no_cost_growing() = 0; lp_assert(this->inf_set_is_correct()); if (this->current_x_is_feasible() && this->m_look_for_feasible_solution_only) return; if (this->m_settings.backup_costs) backup_and_normalize_costs(); m_epsilon_of_reduced_cost = numeric_traits::precise() ? zero_of_type() : T(1) / T(10000000); if (this->m_settings.use_breakpoints_in_feasibility_search) m_breakpoint_indices_queue.resize(this->m_n()); if (!numeric_traits::precise()) { this->m_column_norm_update_counter = 0; init_column_norms(); } if (this->m_settings.simplex_strategy() == simplex_strategy_enum::tableau_rows) init_tableau_rows(); lp_assert(this->reduced_costs_are_correct_tableau()); lp_assert(!this->need_to_pivot_to_basis_tableau()); } template bool lp_primal_core_solver:: update_basis_and_x_tableau(int entering, int leaving, X const & tt) { lp_assert(this->use_tableau()); lp_assert(entering != leaving); update_x_tableau(entering, tt); this->pivot_column_tableau(entering, this->m_basis_heading[leaving]); this->change_basis(entering, leaving); return true; } template void lp_primal_core_solver:: update_x_tableau(unsigned entering, const X& delta) { this->add_delta_to_x_and_call_tracker(entering, delta); if (!this->m_using_infeas_costs) { for (const auto & c : this->m_A.m_columns[entering]) { unsigned i = c.var(); this->update_x_with_delta_and_track_feasibility(this->m_basis[i], - delta * this->m_A.get_val(c)); } } else { // m_using_infeas_costs == true lp_assert(this->column_is_feasible(entering)); lp_assert(this->m_costs[entering] == zero_of_type()); // m_d[entering] can change because of the cost change for basic columns. for (const auto & c : this->m_A.m_columns[entering]) { unsigned i = c.var(); unsigned j = this->m_basis[i]; this->add_delta_to_x_and_call_tracker(j, -delta * this->m_A.get_val(c)); update_inf_cost_for_column_tableau(j); if (is_zero(this->m_costs[j])) this->remove_column_from_inf_set(j); else this->insert_column_into_inf_set(j); } } CASSERT("A_off", this->A_mult_x_is_off() == false); } template void lp_primal_core_solver:: update_inf_cost_for_column_tableau(unsigned j) { lp_assert(this->m_settings.simplex_strategy() != simplex_strategy_enum::tableau_rows); lp_assert(this->m_using_infeas_costs); T new_cost = get_infeasibility_cost_for_column(j); T delta = this->m_costs[j] - new_cost; if (is_zero(delta)) return; this->m_costs[j] = new_cost; update_reduced_cost_for_basic_column_cost_change(delta, j); } template void lp_primal_core_solver::init_reduced_costs_tableau() { if (this->current_x_is_infeasible() && !this->m_using_infeas_costs) { init_infeasibility_costs(); } else if (this->current_x_is_feasible() && this->m_using_infeas_costs) { if (this->m_look_for_feasible_solution_only) return; this->m_costs = m_costs_backup; this->m_using_infeas_costs = false; } unsigned size = this->m_basis_heading.size(); for (unsigned j = 0; j < size; j++) { if (this->m_basis_heading[j] >= 0) this->m_d[j] = zero_of_type(); else { T& d = this->m_d[j] = this->m_costs[j]; for (auto & cc : this->m_A.m_columns[j]) { d -= this->m_costs[this->m_basis[cc.var()]] * this->m_A.get_val(cc); } } } } } z3-z3-4.8.7/src/util/lp/lp_primal_simplex.cpp000066400000000000000000000026101356505360400210310ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #include #include #include #include "util/vector.h" #include #include "util/lp/lp_primal_simplex_def.h" template bool lp::lp_primal_simplex::bounds_hold(std::unordered_map, std::equal_to, std::allocator > > const&); template bool lp::lp_primal_simplex::row_constraints_hold(std::unordered_map, std::equal_to, std::allocator > > const&); template double lp::lp_primal_simplex::get_current_cost() const; template double lp::lp_primal_simplex::get_column_value(unsigned int) const; template lp::lp_primal_simplex::~lp_primal_simplex(); template lp::lp_primal_simplex::~lp_primal_simplex(); template lp::mpq lp::lp_primal_simplex::get_current_cost() const; template lp::mpq lp::lp_primal_simplex::get_column_value(unsigned int) const; template void lp::lp_primal_simplex::find_maximal_solution(); template void lp::lp_primal_simplex::find_maximal_solution(); z3-z3-4.8.7/src/util/lp/lp_primal_simplex.h000066400000000000000000000051441356505360400205030ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #pragma once #include "util/vector.h" #include #include #include #include "util/lp/lp_utils.h" #include "util/lp/column_info.h" #include "util/lp/lp_primal_core_solver.h" #include "util/lp/lp_solver.h" namespace lp { template class lp_primal_simplex: public lp_solver { lp_primal_core_solver * m_core_solver; vector m_lower_bounds; private: unsigned original_rows() { return this->m_external_rows_to_core_solver_rows.size(); } void fill_costs_and_x_for_first_stage_solver(unsigned original_number_of_columns); void init_buffer(unsigned k, vector & r); void refactor(); void set_scaled_costs(); public: lp_primal_simplex(): m_core_solver(nullptr) {} column_info * get_or_create_column_info(unsigned column); void set_status(lp_status status) { this->m_status = status; } lp_status get_status() { return this->m_status; } void fill_acceptable_values_for_x(); void set_zero_bound(bool * bound_is_set, T * bounds, unsigned i); void fill_costs_and_x_for_first_stage_solver_for_row( int row, unsigned & slack_var, unsigned & artificial); void set_core_solver_bounds(); void find_maximal_solution() override; void fill_A_x_and_basis_for_stage_one_total_inf(); void fill_A_x_and_basis_for_stage_one_total_inf_for_row(unsigned row); void solve_with_total_inf(); ~lp_primal_simplex() override; bool bounds_hold(std::unordered_map const & solution); T get_row_value(unsigned i, std::unordered_map const & solution, std::ostream * out); bool row_constraint_holds(unsigned i, std::unordered_map const & solution, std::ostream * out); bool row_constraints_hold(std::unordered_map const & solution); T * get_array_from_map(std::unordered_map const & solution); bool solution_is_feasible(std::unordered_map const & solution) { return bounds_hold(solution) && row_constraints_hold(solution); } T get_column_value(unsigned column) const override { return this->get_column_value_with_core_solver(column, m_core_solver); } T get_current_cost() const override; }; } z3-z3-4.8.7/src/util/lp/lp_primal_simplex_def.h000066400000000000000000000331511356505360400213200ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #include #include "util/vector.h" #include "util/lp/lp_primal_simplex.h" namespace lp { template void lp_primal_simplex::fill_costs_and_x_for_first_stage_solver(unsigned original_number_of_columns) { unsigned slack_var = original_number_of_columns; unsigned artificial = original_number_of_columns + this->m_slacks; for (unsigned row = 0; row < this->row_count(); row++) { fill_costs_and_x_for_first_stage_solver_for_row(row, slack_var, artificial); } } template void lp_primal_simplex::init_buffer(unsigned k, vector & r) { for (unsigned i = 0; i < k; i++) { r[i] = 0; } r[k] = 1; for (unsigned i = this->row_count() -1; i > k; i--) { r[i] = 0; } } template void lp_primal_simplex::refactor() { m_core_solver->init_lu(); if (m_core_solver->factorization()->get_status() != LU_status::OK) { throw_exception("cannot refactor"); } } template void lp_primal_simplex::set_scaled_costs() { unsigned j = this->number_of_core_structurals(); while (j-- > 0) { this->set_scaled_cost(j); } } template column_info * lp_primal_simplex::get_or_create_column_info(unsigned column) { auto it = this->m_columns.find(column); return (it == this->m_columns.end())? ( this->m_columns[column] = new column_info) : it->second; } template void lp_primal_simplex::fill_acceptable_values_for_x() { for (auto t : this->m_core_solver_columns_to_external_columns) { this->m_x[t.first] = numeric_traits::zero(); } } template void lp_primal_simplex::set_zero_bound(bool * bound_is_set, T * bounds, unsigned i) { bound_is_set[i] = true; bounds[i] = numeric_traits::zero(); } template void lp_primal_simplex::fill_costs_and_x_for_first_stage_solver_for_row( int row, unsigned & slack_var, unsigned & artificial) { lp_assert(row >= 0 && row < this->row_count()); auto & constraint = this->m_constraints[this->m_core_solver_rows_to_external_rows[row]]; // we need to bring the program to the form Ax = b T rs = this->m_b[row]; T artificial_cost = - numeric_traits::one(); switch (constraint.m_relation) { case Equal: // no slack variable here this->m_column_types[artificial] = column_type::lower_bound; this->m_costs[artificial] = artificial_cost; // we are maximizing, so the artificial, which is non-negatiive, will be pushed to zero this->m_basis[row] = artificial; if (rs >= 0) { (*this->m_A)(row, artificial) = numeric_traits::one(); this->m_x[artificial] = rs; } else { (*this->m_A)(row, artificial) = - numeric_traits::one(); this->m_x[artificial] = - rs; } artificial++; break; case Greater_or_equal: this->m_column_types[slack_var] = column_type::lower_bound; (*this->m_A)(row, slack_var) = - numeric_traits::one(); if (rs > 0) { lp_assert(numeric_traits::is_zero(this->m_x[slack_var])); // adding one artificial this->m_column_types[artificial] = column_type::lower_bound; (*this->m_A)(row, artificial) = numeric_traits::one(); this->m_costs[artificial] = artificial_cost; this->m_basis[row] = artificial; this->m_x[artificial] = rs; artificial++; } else { // we can put a slack_var into the basis, and atemplate void lp_primal_simplex::adding an artificial variable this->m_basis[row] = slack_var; this->m_x[slack_var] = - rs; } slack_var++; break; case Less_or_equal: // introduce a non-negative slack variable this->m_column_types[slack_var] = column_type::lower_bound; (*this->m_A)(row, slack_var) = numeric_traits::one(); if (rs < 0) { // adding one artificial lp_assert(numeric_traits::is_zero(this->m_x[slack_var])); this->m_column_types[artificial] = column_type::lower_bound; (*this->m_A)(row, artificial) = - numeric_traits::one(); this->m_costs[artificial] = artificial_cost; this->m_x[artificial] = - rs; this->m_basis[row] = artificial++; } else { // we can put slack_var into the basis, and atemplate void lp_primal_simplex::adding an artificial variable this->m_basis[row] = slack_var; this->m_x[slack_var] = rs; } slack_var++; break; } } template void lp_primal_simplex::set_core_solver_bounds() { unsigned total_vars = this->m_A->column_count() + this->m_slacks + this->m_artificials; this->m_column_types.resize(total_vars); this->m_upper_bounds.resize(total_vars); for (auto cit : this->m_map_from_var_index_to_column_info) { column_info * ci = cit.second; unsigned j = ci->get_column_index(); if (!is_valid(j)) continue; // the variable is not mapped to a column switch (this->m_column_types[j] = ci->get_column_type()){ case column_type::fixed: this->m_upper_bounds[j] = numeric_traits::zero(); break; case column_type::boxed: this->m_upper_bounds[j] = ci->get_adjusted_upper_bound() / this->m_column_scale[j]; break; default: break; // do nothing } } } template void lp_primal_simplex::find_maximal_solution() { if (this->problem_is_empty()) { this->m_status = lp_status::EMPTY; return; } this->cleanup(); this->fill_matrix_A_and_init_right_side(); if (this->m_status == lp_status::INFEASIBLE) { return; } this->m_x.resize(this->m_A->column_count()); this->fill_m_b(); this->scale(); fill_acceptable_values_for_x(); this->count_slacks_and_artificials(); set_core_solver_bounds(); solve_with_total_inf(); } template void lp_primal_simplex::fill_A_x_and_basis_for_stage_one_total_inf() { for (unsigned row = 0; row < this->row_count(); row++) fill_A_x_and_basis_for_stage_one_total_inf_for_row(row); } template void lp_primal_simplex::fill_A_x_and_basis_for_stage_one_total_inf_for_row(unsigned row) { lp_assert(row < this->row_count()); auto ext_row_it = this->m_core_solver_rows_to_external_rows.find(row); lp_assert(ext_row_it != this->m_core_solver_rows_to_external_rows.end()); unsigned ext_row = ext_row_it->second; auto constr_it = this->m_constraints.find(ext_row); lp_assert(constr_it != this->m_constraints.end()); auto & constraint = constr_it->second; unsigned j = this->m_A->column_count(); // j is a slack variable this->m_A->add_column(); // we need to bring the program to the form Ax = b this->m_basis[row] = j; switch (constraint.m_relation) { case Equal: this->m_x[j] = this->m_b[row]; (*this->m_A)(row, j) = numeric_traits::one(); this->m_column_types[j] = column_type::fixed; this->m_upper_bounds[j] = m_lower_bounds[j] = zero_of_type(); break; case Greater_or_equal: this->m_x[j] = - this->m_b[row]; (*this->m_A)(row, j) = - numeric_traits::one(); this->m_column_types[j] = column_type::lower_bound; this->m_upper_bounds[j] = zero_of_type(); break; case Less_or_equal: this->m_x[j] = this->m_b[row]; (*this->m_A)(row, j) = numeric_traits::one(); this->m_column_types[j] = column_type::lower_bound; this->m_upper_bounds[j] = m_lower_bounds[j] = zero_of_type(); break; default: lp_unreachable(); } } template void lp_primal_simplex::solve_with_total_inf() { int total_vars = this->m_A->column_count() + this->row_count(); if (total_vars == 0) { this->m_status = lp_status::OPTIMAL; return; } m_lower_bounds.clear(); m_lower_bounds.resize(total_vars, zero_of_type()); // low bounds are shifted ot zero this->m_x.resize(total_vars, numeric_traits::zero()); this->m_basis.resize(this->row_count()); this->m_costs.clear(); this->m_costs.resize(total_vars, zero_of_type()); fill_A_x_and_basis_for_stage_one_total_inf(); if (this->m_settings.get_message_ostream() != nullptr) this->print_statistics_on_A(*this->m_settings.get_message_ostream()); set_scaled_costs(); m_core_solver = new lp_primal_core_solver(*this->m_A, this->m_b, this->m_x, this->m_basis, this->m_nbasis, this->m_heading, this->m_costs, this->m_column_types, m_lower_bounds, this->m_upper_bounds, this->m_settings, *this); m_core_solver->solve(); this->set_status(m_core_solver->get_status()); this->m_total_iterations = m_core_solver->total_iterations(); } template lp_primal_simplex::~lp_primal_simplex() { delete m_core_solver; } template bool lp_primal_simplex::bounds_hold(std::unordered_map const & solution) { for (auto it : this->m_map_from_var_index_to_column_info) { auto sol_it = solution.find(it.second->get_name()); if (sol_it == solution.end()) { std::stringstream s; s << "cannot find column " << it.first << " in solution"; throw_exception(s.str() ); } if (!it.second->bounds_hold(sol_it->second)) { it.second->bounds_hold(sol_it->second); return false; } } return true; } template T lp_primal_simplex::get_row_value(unsigned i, std::unordered_map const & solution, std::ostream * out) { auto it = this->m_A_values.find(i); if (it == this->m_A_values.end()) { std::stringstream s; s << "cannot find row " << i; throw_exception(s.str() ); } T ret = numeric_traits::zero(); for (auto & pair : it->second) { auto cit = this->m_map_from_var_index_to_column_info.find(pair.first); lp_assert(cit != this->m_map_from_var_index_to_column_info.end()); column_info * ci = cit->second; auto sol_it = solution.find(ci->get_name()); lp_assert(sol_it != solution.end()); T column_val = sol_it->second; if (out != nullptr) { (*out) << pair.second << "(" << ci->get_name() << "=" << column_val << ") "; } ret += pair.second * column_val; } if (out != nullptr) { (*out) << " = " << ret << std::endl; } return ret; } template bool lp_primal_simplex::row_constraint_holds(unsigned i, std::unordered_map const & solution, std::ostream *out) { T row_val = get_row_value(i, solution, out); auto & constraint = this->m_constraints[i]; T rs = constraint.m_rs; bool print = out != nullptr; switch (constraint.m_relation) { case Equal: if (fabs(numeric_traits::get_double(row_val - rs)) > 0.00001) { if (print) { (*out) << "should be = " << rs << std::endl; } return false; } return true; case Greater_or_equal: if (numeric_traits::get_double(row_val - rs) < -0.00001) { if (print) { (*out) << "should be >= " << rs << std::endl; } return false; } return true;; case Less_or_equal: if (numeric_traits::get_double(row_val - rs) > 0.00001) { if (print) { (*out) << "should be <= " << rs << std::endl; } return false; } return true;; } lp_unreachable(); return false; // it is unreachable } template bool lp_primal_simplex::row_constraints_hold(std::unordered_map const & solution) { for (auto it : this->m_A_values) { if (!row_constraint_holds(it.first, solution, nullptr)) { row_constraint_holds(it.first, solution, nullptr); return false; } } return true; } template T lp_primal_simplex::get_current_cost() const { T ret = numeric_traits::zero(); for (auto it : this->m_map_from_var_index_to_column_info) { ret += this->get_column_cost_value(it.first, it.second); } return ret; } } z3-z3-4.8.7/src/util/lp/lp_settings.cpp000066400000000000000000000006571356505360400176550ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #include #include "util/vector.h" #include "util/lp/lp_settings_def.h" template bool lp::vectors_are_equal(vector const&, vector const&); template bool lp::vectors_are_equal(vector const&, vector const&); z3-z3-4.8.7/src/util/lp/lp_settings.h000066400000000000000000000354301356505360400173170ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #pragma once #include "util/vector.h" #include #include #include #include #include "util/lp/lp_utils.h" #include "util/stopwatch.h" namespace lp { typedef unsigned var_index; typedef unsigned constraint_index; typedef unsigned row_index; typedef vector> explanation_t; enum class column_type { free_column = 0, lower_bound = 1, upper_bound = 2, boxed = 3, fixed = 4 }; enum class simplex_strategy_enum { undecided = 3, tableau_rows = 0, tableau_costs = 1, lu = 2 }; std::string column_type_to_string(column_type t); enum class lp_status { UNKNOWN, INFEASIBLE, TENTATIVE_UNBOUNDED, UNBOUNDED, TENTATIVE_DUAL_UNBOUNDED, DUAL_UNBOUNDED, OPTIMAL, FEASIBLE, FLOATING_POINT_ERROR, TIME_EXHAUSTED, ITERATIONS_EXHAUSTED, EMPTY, UNSTABLE, CANCELLED }; // when the ratio of the vector length to domain size to is greater than the return value we switch to solve_By_for_T_indexed_only template unsigned ratio_of_index_size_to_all_size() { if (numeric_traits::precise()) return 10; return 120; } const char* lp_status_to_string(lp_status status); inline std::ostream& operator<<(std::ostream& out, lp_status status) { return out << lp_status_to_string(status); } lp_status lp_status_from_string(std::string status); enum non_basic_column_value_position { at_lower_bound, at_upper_bound, at_fixed, free_of_bounds, not_at_bound }; template bool is_epsilon_small(const X & v, const double& eps); // forward definition class lp_resource_limit { public: virtual bool get_cancel_flag() = 0; }; struct stats { unsigned m_make_feasible; unsigned m_total_iterations; unsigned m_iters_with_no_cost_growing; unsigned m_num_factorizations; unsigned m_num_of_implied_bounds; unsigned m_need_to_solve_inf; unsigned m_max_cols; unsigned m_max_rows; unsigned m_gcd_calls; unsigned m_gcd_conflicts; unsigned m_cube_calls; unsigned m_cube_success; unsigned m_patches; unsigned m_patches_success; unsigned m_hnf_cutter_calls; unsigned m_hnf_cuts; stats() { reset(); } void reset() { memset(this, 0, sizeof(*this)); } }; struct lp_settings { private: class default_lp_resource_limit : public lp_resource_limit { lp_settings& m_settings; stopwatch m_sw; public: default_lp_resource_limit(lp_settings& s): m_settings(s) { m_sw.start(); } bool get_cancel_flag() override { return (m_sw.get_current_seconds() > m_settings.time_limit); } }; default_lp_resource_limit m_default_resource_limit; lp_resource_limit* m_resource_limit; // used for debug output std::ostream* m_debug_out; // used for messages, for example, the computation progress messages std::ostream* m_message_out; stats m_stats; random_gen m_rand; public: unsigned reps_in_scaler; // when the absolute value of an element is less than pivot_epsilon // in pivoting, we treat it as a zero double pivot_epsilon; // see Chatal, page 115 double positive_price_epsilon; // a quotation "if some choice of the entering variable leads to an eta matrix // whose diagonal element in the eta column is less than e2 (entering_diag_epsilon) in magnitude, the this choice is rejected ... double entering_diag_epsilon; int c_partial_pivoting; // this is the constant c from page 410 unsigned depth_of_rook_search; bool using_partial_pivoting; // dissertation of Achim Koberstein // if Bx - b is different at any component more that refactor_epsilon then we refactor double refactor_tolerance; double pivot_tolerance; double zero_tolerance; double drop_tolerance; double tolerance_for_artificials; double can_be_taken_to_basis_tolerance; unsigned percent_of_entering_to_check; // we try to find a profitable column in a percentage of the columns bool use_scaling; double scaling_maximum; double scaling_minimum; double harris_feasibility_tolerance; // page 179 of Istvan Maros double ignore_epsilon_of_harris; unsigned max_number_of_iterations_with_no_improvements; unsigned max_total_number_of_iterations; double time_limit; // the maximum time limit of the total run time in seconds // dual section double dual_feasibility_tolerance; // // page 71 of the PhD thesis of Achim Koberstein double primal_feasibility_tolerance; // page 71 of the PhD thesis of Achim Koberstein double relative_primal_feasibility_tolerance; // page 71 of the PhD thesis of Achim Koberstein // end of dual section bool m_bound_propagation; bool presolve_with_double_solver_for_lar; simplex_strategy_enum m_simplex_strategy; int report_frequency; bool print_statistics; unsigned column_norms_update_frequency; bool scale_with_ratio; double density_threshold; bool use_breakpoints_in_feasibility_search; unsigned max_row_length_for_bound_propagation; bool backup_costs; unsigned column_number_threshold_for_using_lu_in_lar_solver; unsigned m_int_gomory_cut_period; unsigned m_int_find_cube_period; private: unsigned m_hnf_cut_period; public: bool m_int_run_gcd_test; bool m_int_pivot_fixed_vars_from_basis; bool m_int_patch_only_integer_values; unsigned limit_on_rows_for_hnf_cutter; unsigned limit_on_columns_for_hnf_cutter; bool m_enable_hnf; unsigned hnf_cut_period() const { return m_hnf_cut_period; } void set_hnf_cut_period(unsigned period) { m_hnf_cut_period = period; } unsigned random_next() { return m_rand(); } void set_random_seed(unsigned s) { m_rand.set_seed(s); } bool bound_progation() const { return m_bound_propagation; } bool& bound_propagation() { return m_bound_propagation; } lp_settings() : m_default_resource_limit(*this), m_resource_limit(&m_default_resource_limit), m_debug_out(&std::cout), m_message_out(&std::cout), reps_in_scaler(20), pivot_epsilon(0.00000001), positive_price_epsilon(1e-7), entering_diag_epsilon (1e-8), c_partial_pivoting (10), // this is the constant c from page 410 depth_of_rook_search (4), using_partial_pivoting (true), // dissertation of Achim Koberstein // if Bx - b is different at any component more that refactor_epsilon then we refactor refactor_tolerance ( 1e-4), pivot_tolerance ( 1e-6), zero_tolerance ( 1e-12), drop_tolerance ( 1e-14), tolerance_for_artificials ( 1e-4), can_be_taken_to_basis_tolerance ( 0.00001), percent_of_entering_to_check ( 5),// we try to find a profitable column in a percentage of the columns use_scaling ( true), scaling_maximum ( 1), scaling_minimum ( 0.5), harris_feasibility_tolerance ( 1e-7), // page 179 of Istvan Maros ignore_epsilon_of_harris ( 10e-5), max_number_of_iterations_with_no_improvements ( 2000000), max_total_number_of_iterations ( 20000000), time_limit ( std::numeric_limits::max()), // the maximum time limit of the total run time in seconds // dual section dual_feasibility_tolerance ( 1e-7), // // page 71 of the PhD thesis of Achim Koberstein primal_feasibility_tolerance ( 1e-7), // page 71 of the PhD thesis of Achim Koberstein relative_primal_feasibility_tolerance ( 1e-9), // page 71 of the PhD thesis of Achim Koberstein m_bound_propagation ( true), presolve_with_double_solver_for_lar(true), m_simplex_strategy(simplex_strategy_enum::tableau_rows), report_frequency(1000), print_statistics(false), column_norms_update_frequency(12000), scale_with_ratio(true), density_threshold(0.7), use_breakpoints_in_feasibility_search(false), max_row_length_for_bound_propagation(300), backup_costs(true), column_number_threshold_for_using_lu_in_lar_solver(4000), m_int_gomory_cut_period(4), m_int_find_cube_period(4), m_hnf_cut_period(4), m_int_run_gcd_test(true), m_int_pivot_fixed_vars_from_basis(false), m_int_patch_only_integer_values(true), limit_on_rows_for_hnf_cutter(75), limit_on_columns_for_hnf_cutter(150), m_enable_hnf(true) {} void set_resource_limit(lp_resource_limit& lim) { m_resource_limit = &lim; } bool get_cancel_flag() const { return m_resource_limit->get_cancel_flag(); } void set_debug_ostream(std::ostream* out) { m_debug_out = out; } void set_message_ostream(std::ostream* out) { m_message_out = out; } std::ostream* get_debug_ostream() { return m_debug_out; } std::ostream* get_message_ostream() { return m_message_out; } stats& st() { return m_stats; } stats const& st() const { return m_stats; } template static bool is_eps_small_general(const T & t, const double & eps) { return (!numeric_traits::precise())? is_epsilon_small(t, eps) : numeric_traits::is_zero(t); } template bool abs_val_is_smaller_than_dual_feasibility_tolerance(T const & t) { return is_eps_small_general(t, dual_feasibility_tolerance); } template bool abs_val_is_smaller_than_primal_feasibility_tolerance(T const & t) { return is_eps_small_general(t, primal_feasibility_tolerance); } template bool abs_val_is_smaller_than_can_be_taken_to_basis_tolerance(T const & t) { return is_eps_small_general(t, can_be_taken_to_basis_tolerance); } template bool abs_val_is_smaller_than_drop_tolerance(T const & t) const { return is_eps_small_general(t, drop_tolerance); } template bool abs_val_is_smaller_than_zero_tolerance(T const & t) { return is_eps_small_general(t, zero_tolerance); } template bool abs_val_is_smaller_than_refactor_tolerance(T const & t) { return is_eps_small_general(t, refactor_tolerance); } template bool abs_val_is_smaller_than_pivot_tolerance(T const & t) { return is_eps_small_general(t, pivot_tolerance); } template bool abs_val_is_smaller_than_harris_tolerance(T const & t) { return is_eps_small_general(t, harris_feasibility_tolerance); } template bool abs_val_is_smaller_than_ignore_epslilon_for_harris(T const & t) { return is_eps_small_general(t, ignore_epsilon_of_harris); } template bool abs_val_is_smaller_than_artificial_tolerance(T const & t) { return is_eps_small_general(t, tolerance_for_artificials); } // the method of lar solver to use simplex_strategy_enum simplex_strategy() const { return m_simplex_strategy; } simplex_strategy_enum & simplex_strategy() { return m_simplex_strategy; } bool use_lu() const { return m_simplex_strategy == simplex_strategy_enum::lu; } bool use_tableau() const { return m_simplex_strategy == simplex_strategy_enum::tableau_rows || m_simplex_strategy == simplex_strategy_enum::tableau_costs; } bool use_tableau_rows() const { return m_simplex_strategy == simplex_strategy_enum::tableau_rows; } #ifdef Z3DEBUG static unsigned ddd; // used for debugging #endif }; // end of lp_settings class #define LP_OUT(_settings_, _msg_) { if (_settings_.get_debug_ostream()) { *_settings_.get_debug_ostream() << _msg_; } } template std::string T_to_string(const T & t) { std::ostringstream strs; strs << t; return strs.str(); } inline std::string T_to_string(const numeric_pair & t) { std::ostringstream strs; double r = (t.x + t.y / mpq(1000)).get_double(); strs << r; return strs.str(); } inline std::string T_to_string(const mpq & t) { std::ostringstream strs; strs << t; return strs.str(); } template bool val_is_smaller_than_eps(T const & t, double const & eps) { if (!numeric_traits::precise()) { return numeric_traits::get_double(t) < eps; } return t <= numeric_traits::zero(); } template bool vectors_are_equal(T * a, vector &b, unsigned n); template bool vectors_are_equal(const vector & a, const buffer &b); template bool vectors_are_equal(const vector & a, const vector &b); template T abs (T const & v) { return v >= zero_of_type() ? v : -v; } template X max_abs_in_vector(vector& t){ X r(zero_of_type()); for (auto & v : t) r = std::max(abs(v) , r); return r; } inline void print_blanks(int n, std::ostream & out) { while (n--) {out << ' '; } } // after a push of the last element we ensure that the vector increases // we also suppose that before the last push the vector was increasing inline void ensure_increasing(vector & v) { lp_assert(v.size() > 0); unsigned j = v.size() - 1; for (; j > 0; j-- ) if (v[j] <= v[j - 1]) { // swap unsigned t = v[j]; v[j] = v[j-1]; v[j-1] = t; } else { break; } } inline static bool is_rational(const impq & n) { return is_zero(n.y); } inline static mpq fractional_part(const impq & n) { lp_assert(is_rational(n)); return n.x - floor(n.x); } inline static mpq fractional_part(const mpq & n) { return n - floor(n); } #if Z3DEBUG bool D(); #endif } z3-z3-4.8.7/src/util/lp/lp_settings_def.h000066400000000000000000000071161356505360400201350ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #include #include #include "util/vector.h" #include "util/lp/lp_settings.h" namespace lp { std::string column_type_to_string(column_type t) { switch (t) { case column_type::fixed: return "fixed"; case column_type::boxed: return "boxed"; case column_type::lower_bound: return "lower_bound"; case column_type::upper_bound: return "upper_bound"; case column_type::free_column: return "free_column"; default: lp_unreachable(); } return "unknown"; // it is unreachable } const char* lp_status_to_string(lp_status status) { switch (status) { case lp_status::UNKNOWN: return "UNKNOWN"; case lp_status::INFEASIBLE: return "INFEASIBLE"; case lp_status::UNBOUNDED: return "UNBOUNDED"; case lp_status::TENTATIVE_DUAL_UNBOUNDED: return "TENTATIVE_DUAL_UNBOUNDED"; case lp_status::DUAL_UNBOUNDED: return "DUAL_UNBOUNDED"; case lp_status::OPTIMAL: return "OPTIMAL"; case lp_status::FEASIBLE: return "FEASIBLE"; case lp_status::FLOATING_POINT_ERROR: return "FLOATING_POINT_ERROR"; case lp_status::TIME_EXHAUSTED: return "TIME_EXHAUSTED"; case lp_status::ITERATIONS_EXHAUSTED: return "ITERATIONS_EXHAUSTED"; case lp_status::EMPTY: return "EMPTY"; case lp_status::UNSTABLE: return "UNSTABLE"; default: lp_unreachable(); } return "UNKNOWN"; // it is unreachable } lp_status lp_status_from_string(std::string status) { if (status == "UNKNOWN") return lp_status::UNKNOWN; if (status == "INFEASIBLE") return lp_status::INFEASIBLE; if (status == "UNBOUNDED") return lp_status::UNBOUNDED; if (status == "OPTIMAL") return lp_status::OPTIMAL; if (status == "FEASIBLE") return lp_status::FEASIBLE; if (status == "FLOATING_POINT_ERROR") return lp_status::FLOATING_POINT_ERROR; if (status == "TIME_EXHAUSTED") return lp_status::TIME_EXHAUSTED; if (status == "ITERATIONS_EXHAUSTED") return lp_status::ITERATIONS_EXHAUSTED; if (status == "EMPTY") return lp_status::EMPTY; lp_unreachable(); return lp_status::UNKNOWN; // it is unreachable } template bool vectors_are_equal(T * a, vector &b, unsigned n) { if (numeric_traits::precise()) { for (unsigned i = 0; i < n; i ++){ if (!numeric_traits::is_zero(a[i] - b[i])) { return false; } } } else { for (unsigned i = 0; i < n; i ++){ if (std::abs(numeric_traits::get_double(a[i] - b[i])) > 0.000001) { return false; } } } return true; } template bool vectors_are_equal(const vector & a, const vector &b) { unsigned n = static_cast(a.size()); if (n != b.size()) return false; if (numeric_traits::precise()) { for (unsigned i = 0; i < n; i ++){ if (!numeric_traits::is_zero(a[i] - b[i])) { return false; } } } else { for (unsigned i = 0; i < n; i ++){ double da = numeric_traits::get_double(a[i]); double db = numeric_traits::get_double(b[i]); double amax = std::max(fabs(da), fabs(db)); if (amax > 1) { da /= amax; db /= amax; } if (fabs(da - db) > 0.000001) { return false; } } } return true; } #ifdef Z3DEBUG unsigned lp_settings::ddd = 0; #endif } z3-z3-4.8.7/src/util/lp/lp_solver.cpp000066400000000000000000000060251356505360400173220ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #include #include "util/lp/lp_solver_def.h" template void lp::lp_solver::add_constraint(lp::lp_relation, double, unsigned int); template void lp::lp_solver::cleanup(); template void lp::lp_solver::count_slacks_and_artificials(); template void lp::lp_solver::fill_m_b(); template void lp::lp_solver::fill_matrix_A_and_init_right_side(); template void lp::lp_solver::flip_costs(); template double lp::lp_solver::get_column_cost_value(unsigned int, lp::column_info*) const; template int lp::lp_solver::get_column_index_by_name(std::string) const; template double lp::lp_solver::get_column_value_with_core_solver(unsigned int, lp::lp_core_solver_base*) const; template lp::column_info* lp::lp_solver::get_or_create_column_info(unsigned int); template void lp::lp_solver::give_symbolic_name_to_column(std::string, unsigned int); template void lp::lp_solver::print_statistics_on_A(std::ostream & out); template bool lp::lp_solver::problem_is_empty(); template void lp::lp_solver::scale(); template void lp::lp_solver::set_scaled_cost(unsigned int); template lp::lp_solver::~lp_solver(); template void lp::lp_solver::add_constraint(lp::lp_relation, lp::mpq, unsigned int); template void lp::lp_solver::cleanup(); template void lp::lp_solver::count_slacks_and_artificials(); template void lp::lp_solver::fill_m_b(); template void lp::lp_solver::fill_matrix_A_and_init_right_side(); template void lp::lp_solver::flip_costs(); template lp::mpq lp::lp_solver::get_column_cost_value(unsigned int, lp::column_info*) const; template int lp::lp_solver::get_column_index_by_name(std::string) const; template lp::mpq lp::lp_solver::get_column_value_by_name(std::string) const; template lp::mpq lp::lp_solver::get_column_value_with_core_solver(unsigned int, lp::lp_core_solver_base*) const; template lp::column_info* lp::lp_solver::get_or_create_column_info(unsigned int); template void lp::lp_solver::give_symbolic_name_to_column(std::string, unsigned int); template void lp::lp_solver::print_statistics_on_A(std::ostream & out); template bool lp::lp_solver::problem_is_empty(); template void lp::lp_solver::scale(); template void lp::lp_solver::set_scaled_cost(unsigned int); template lp::lp_solver::~lp_solver(); template double lp::lp_solver::get_column_value_by_name(std::string) const; z3-z3-4.8.7/src/util/lp/lp_solver.h000066400000000000000000000176231356505360400167750ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #pragma once #include #include #include #include "util/vector.h" #include "util/lp/lp_settings.h" #include "util/lp/column_info.h" #include "util/lp/static_matrix.h" #include "util/lp/lp_core_solver_base.h" #include "util/lp/scaler.h" #include "util/lp/bound_analyzer_on_row.h" namespace lp { enum lp_relation { Less_or_equal, Equal, Greater_or_equal }; template struct lp_constraint { X m_rs; // right side of the constraint lp_relation m_relation; lp_constraint() {} // empty constructor lp_constraint(T rs, lp_relation relation): m_rs(rs), m_relation(relation) {} }; template class lp_solver : public column_namer { column_info * get_or_create_column_info(unsigned column); protected: T get_column_cost_value(unsigned j, column_info * ci) const; public: unsigned m_total_iterations; static_matrix* m_A; // this is the matrix of constraints vector m_b; // the right side vector unsigned m_first_stage_iterations; unsigned m_second_stage_iterations; std::unordered_map> m_constraints; std::unordered_map*> m_map_from_var_index_to_column_info; std::unordered_map > m_A_values; std::unordered_map m_names_to_columns; // don't have to use it std::unordered_map m_external_rows_to_core_solver_rows; std::unordered_map m_core_solver_rows_to_external_rows; std::unordered_map m_core_solver_columns_to_external_columns; vector m_column_scale; std::unordered_map m_name_map; unsigned m_artificials; unsigned m_slacks; vector m_column_types; vector m_costs; vector m_x; vector m_upper_bounds; vector m_basis; vector m_nbasis; vector m_heading; lp_status m_status; lp_settings m_settings; lp_solver(): m_A(nullptr), // this is the matrix of constraints m_first_stage_iterations (0), m_second_stage_iterations (0), m_artificials (0), m_slacks (0), m_status(lp_status::UNKNOWN) {} unsigned row_count() const { return this->m_A->row_count(); } void add_constraint(lp_relation relation, T right_side, unsigned row_index); void set_cost_for_column(unsigned column, T column_cost) { get_or_create_column_info(column)->set_cost(column_cost); } std::string get_column_name(unsigned j) const override; void set_row_column_coefficient(unsigned row, unsigned column, T const & val) { m_A_values[row][column] = val; } // returns the current cost virtual T get_current_cost() const = 0; // do not have to call it void give_symbolic_name_to_column(std::string name, unsigned column); virtual T get_column_value(unsigned column) const = 0; T get_column_value_by_name(std::string name) const; // returns -1 if not found virtual int get_column_index_by_name(std::string name) const; void set_lower_bound(unsigned i, T bound) { column_info *ci = get_or_create_column_info(i); ci->set_lower_bound(bound); } void set_upper_bound(unsigned i, T bound) { column_info *ci = get_or_create_column_info(i); ci->set_upper_bound(bound); } void unset_lower_bound(unsigned i) { get_or_create_column_info(i)->unset_lower_bound(); } void unset_upper_bound(unsigned i) { get_or_create_column_info(i)->unset_upper_bound(); } void set_fixed_value(unsigned i, T val) { column_info *ci = get_or_create_column_info(i); ci->set_fixed_value(val); } void unset_fixed_value(unsigned i) { get_or_create_column_info(i)->unset_fixed(); } lp_status get_status() const { return m_status; } void set_status(lp_status st) { m_status = st; } virtual ~lp_solver(); void flip_costs(); virtual void find_maximal_solution() = 0; void set_time_limit(unsigned time_limit_in_seconds) { m_settings.time_limit = time_limit_in_seconds; } void set_max_iterations_per_stage(unsigned max_iterations) { m_settings.max_total_number_of_iterations = max_iterations; } unsigned get_max_iterations_per_stage() const { return m_settings.max_total_number_of_iterations; } protected: bool problem_is_empty(); void scale(); void print_rows_scale_stats(std::ostream & out); void print_columns_scale_stats(std::ostream & out); void print_row_scale_stats(unsigned i, std::ostream & out); void print_column_scale_stats(unsigned j, std::ostream & out); void print_scale_stats(std::ostream & out); void get_max_abs_in_row(std::unordered_map & row_map); void pin_vars_down_on_row(std::unordered_map & row) { pin_vars_on_row_with_sign(row, - numeric_traits::one()); } void pin_vars_up_on_row(std::unordered_map & row) { pin_vars_on_row_with_sign(row, numeric_traits::one()); } void pin_vars_on_row_with_sign(std::unordered_map & row, T sign ); bool get_minimal_row_value(std::unordered_map & row, T & lower_bound); bool get_maximal_row_value(std::unordered_map & row, T & lower_bound); bool row_is_zero(std::unordered_map & row); bool row_e_is_obsolete(std::unordered_map & row, unsigned row_index); bool row_ge_is_obsolete(std::unordered_map & row, unsigned row_index); bool row_le_is_obsolete(std::unordered_map & row, unsigned row_index); // analyse possible max and min values that are derived from var boundaries // Let us say that the we have a "ge" constraint, and the min value is equal to the rs. // Then we know what values of the variables are. For each positive coeff of the row it has to be // the low boundary of the var and for a negative - the upper. // this routing also pins the variables to the boundaries bool row_is_obsolete(std::unordered_map & row, unsigned row_index ); void remove_fixed_or_zero_columns(); void remove_fixed_or_zero_columns_from_row(unsigned i, std::unordered_map & row); unsigned try_to_remove_some_rows(); void cleanup(); void map_external_rows_to_core_solver_rows(); void map_external_columns_to_core_solver_columns(); unsigned number_of_core_structurals() { return static_cast(m_core_solver_columns_to_external_columns.size()); } void restore_column_scales_to_one() { for (unsigned i = 0; i < m_column_scale.size(); i++) m_column_scale[i] = numeric_traits::one(); } void unscale(); void fill_A_from_A_values(); void fill_matrix_A_and_init_right_side(); void count_slacks_and_artificials(); void count_slacks_and_artificials_for_row(unsigned i); T lower_bound_shift_for_row(unsigned i); void fill_m_b(); T get_column_value_with_core_solver(unsigned column, lp_core_solver_base * core_solver) const; void set_scaled_cost(unsigned j); void print_statistics_on_A(std::ostream & out) { out << "extended A[" << this->m_A->row_count() << "," << this->m_A->column_count() << "]" << std::endl; } public: lp_settings & settings() { return m_settings;} void print_model(std::ostream & s) const { s << "objective = " << get_current_cost() << std::endl; s << "column values\n"; for (auto & it : m_names_to_columns) { s << it.first << " = " << get_column_value(it.second) << std::endl; } } }; } z3-z3-4.8.7/src/util/lp/lp_solver_def.h000066400000000000000000000460621356505360400176120ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #include #include #include "util/vector.h" #include "util/lp/lp_solver.h" namespace lp { template column_info * lp_solver::get_or_create_column_info(unsigned column) { auto it = m_map_from_var_index_to_column_info.find(column); return (it == m_map_from_var_index_to_column_info.end())? (m_map_from_var_index_to_column_info[column] = new column_info()) : it->second; } template std::string lp_solver::get_column_name(unsigned j) const { // j here is the core solver index auto it = this->m_core_solver_columns_to_external_columns.find(j); if (it == this->m_core_solver_columns_to_external_columns.end()) return std::string("x")+T_to_string(j); unsigned external_j = it->second; auto t = this->m_map_from_var_index_to_column_info.find(external_j); if (t == this->m_map_from_var_index_to_column_info.end()) { return std::string("x") +T_to_string(external_j); } return t->second->get_name(); } template T lp_solver::get_column_cost_value(unsigned j, column_info * ci) const { if (ci->is_fixed()) { return ci->get_cost() * ci->get_fixed_value(); } return ci->get_cost() * get_column_value(j); } template void lp_solver::add_constraint(lp_relation relation, T right_side, unsigned row_index) { lp_assert(m_constraints.find(row_index) == m_constraints.end()); lp_constraint cs(right_side, relation); m_constraints[row_index] = cs; } template void lp_solver::give_symbolic_name_to_column(std::string name, unsigned column) { auto it = m_map_from_var_index_to_column_info.find(column); column_info *ci; if (it == m_map_from_var_index_to_column_info.end()){ m_map_from_var_index_to_column_info[column] = ci = new column_info; } else { ci = it->second; } ci->set_name(name); m_names_to_columns[name] = column; } template T lp_solver::get_column_value_by_name(std::string name) const { auto it = m_names_to_columns.find(name); if (it == m_names_to_columns.end()) { std::stringstream s; s << "get_column_value_by_name " << name; throw_exception(s.str()); } return get_column_value(it -> second); } // returns -1 if not found template int lp_solver::get_column_index_by_name(std::string name) const { auto t = m_names_to_columns.find(name); if (t == m_names_to_columns.end()) { return -1; } return t->second; } template lp_solver::~lp_solver(){ delete m_A; for (auto t : m_map_from_var_index_to_column_info) { delete t.second; } } template void lp_solver::flip_costs() { for (auto t : m_map_from_var_index_to_column_info) { column_info *ci = t.second; ci->set_cost(-ci->get_cost()); } } template bool lp_solver::problem_is_empty() { for (auto & c : m_A_values) if (!c.second.empty()) return false; return true; } template void lp_solver::scale() { if (numeric_traits::precise() || m_settings.use_scaling == false) { m_column_scale.clear(); m_column_scale.resize(m_A->column_count(), one_of_type()); return; } T smin = T(m_settings.scaling_minimum); T smax = T(m_settings.scaling_maximum); scaler scaler(m_b, *m_A, smin, smax, m_column_scale, this->m_settings); if (!scaler.scale()) { unscale(); } } template void lp_solver::print_rows_scale_stats(std::ostream & out) { out << "rows max" << std::endl; for (unsigned i = 0; i < m_A->row_count(); i++) { print_row_scale_stats(i, out); } out << std::endl; } template void lp_solver::print_columns_scale_stats(std::ostream & out) { out << "columns max" << std::endl; for (unsigned i = 0; i < m_A->column_count(); i++) { print_column_scale_stats(i, out); } out << std::endl; } template void lp_solver::print_row_scale_stats(unsigned i, std::ostream & out) { out << "(" << std::min(m_A->get_min_abs_in_row(i), abs(m_b[i])) << " "; out << std::max(m_A->get_max_abs_in_row(i), abs(m_b[i])) << ")"; } template void lp_solver::print_column_scale_stats(unsigned j, std::ostream & out) { out << "(" << m_A->get_min_abs_in_row(j) << " "; out << m_A->get_max_abs_in_column(j) << ")"; } template void lp_solver::print_scale_stats(std::ostream & out) { print_rows_scale_stats(out); print_columns_scale_stats(out); } template void lp_solver::get_max_abs_in_row(std::unordered_map & row_map) { T ret = numeric_traits::zero(); for (auto jp : row_map) { T ac = numeric_traits::abs(jp->second); if (ac > ret) { ret = ac; } } return ret; } template void lp_solver::pin_vars_on_row_with_sign(std::unordered_map & row, T sign ) { for (auto t : row) { unsigned j = t.first; column_info * ci = m_map_from_var_index_to_column_info[j]; T a = t.second; if (a * sign > numeric_traits::zero()) { lp_assert(ci->upper_bound_is_set()); ci->set_fixed_value(ci->get_upper_bound()); } else { lp_assert(ci->lower_bound_is_set()); ci->set_fixed_value(ci->get_lower_bound()); } } } template bool lp_solver::get_minimal_row_value(std::unordered_map & row, T & lower_bound) { lower_bound = numeric_traits::zero(); for (auto & t : row) { T a = t.second; column_info * ci = m_map_from_var_index_to_column_info[t.first]; if (a > numeric_traits::zero()) { if (ci->lower_bound_is_set()) { lower_bound += ci->get_lower_bound() * a; } else { return false; } } else { if (ci->upper_bound_is_set()) { lower_bound += ci->get_upper_bound() * a; } else { return false; } } } return true; } template bool lp_solver::get_maximal_row_value(std::unordered_map & row, T & lower_bound) { lower_bound = numeric_traits::zero(); for (auto & t : row) { T a = t.second; column_info * ci = m_map_from_var_index_to_column_info[t.first]; if (a < numeric_traits::zero()) { if (ci->lower_bound_is_set()) { lower_bound += ci->get_lower_bound() * a; } else { return false; } } else { if (ci->upper_bound_is_set()) { lower_bound += ci->get_upper_bound() * a; } else { return false; } } } return true; } template bool lp_solver::row_is_zero(std::unordered_map & row) { for (auto & t : row) { if (!is_zero(t.second)) return false; } return true; } template bool lp_solver::row_e_is_obsolete(std::unordered_map & row, unsigned row_index) { T rs = m_constraints[row_index].m_rs; if (row_is_zero(row)) { if (!is_zero(rs)) m_status = lp_status::INFEASIBLE; return true; } T lower_bound; bool lb = get_minimal_row_value(row, lower_bound); if (lb) { T diff = lower_bound - rs; if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)){ // lower_bound > rs + m_settings.refactor_epsilon m_status = lp_status::INFEASIBLE; return true; } if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){ pin_vars_down_on_row(row); return true; } } T upper_bound; bool ub = get_maximal_row_value(row, upper_bound); if (ub) { T diff = rs - upper_bound; if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)) { // upper_bound < rs - m_settings.refactor_tolerance m_status = lp_status::INFEASIBLE; return true; } if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){ pin_vars_up_on_row(row); return true; } } return false; } template bool lp_solver::row_ge_is_obsolete(std::unordered_map & row, unsigned row_index) { T rs = m_constraints[row_index].m_rs; if (row_is_zero(row)) { if (rs > zero_of_type()) m_status = lp_status::INFEASIBLE; return true; } T upper_bound; if (get_maximal_row_value(row, upper_bound)) { T diff = rs - upper_bound; if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)) { // upper_bound < rs - m_settings.refactor_tolerance m_status = lp_status::INFEASIBLE; return true; } if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){ pin_vars_up_on_row(row); return true; } } return false; } template bool lp_solver::row_le_is_obsolete(std::unordered_map & row, unsigned row_index) { T lower_bound; T rs = m_constraints[row_index].m_rs; if (row_is_zero(row)) { if (rs < zero_of_type()) m_status = lp_status::INFEASIBLE; return true; } if (get_minimal_row_value(row, lower_bound)) { T diff = lower_bound - rs; if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)){ // lower_bound > rs + m_settings.refactor_tolerance m_status = lp_status::INFEASIBLE; return true; } if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){ pin_vars_down_on_row(row); return true; } } return false; } // analyse possible max and min values that are derived from var boundaries // Let us say that the we have a "ge" constraint, and the min value is equal to the rs. // Then we know what values of the variables are. For each positive coeff of the row it has to be // the low boundary of the var and for a negative - the upper. // this routing also pins the variables to the boundaries template bool lp_solver::row_is_obsolete(std::unordered_map & row, unsigned row_index ) { auto & constraint = m_constraints[row_index]; switch (constraint.m_relation) { case lp_relation::Equal: return row_e_is_obsolete(row, row_index); case lp_relation::Greater_or_equal: return row_ge_is_obsolete(row, row_index); case lp_relation::Less_or_equal: return row_le_is_obsolete(row, row_index); } lp_unreachable(); return false; // it is unreachable } template void lp_solver::remove_fixed_or_zero_columns() { for (auto & i_row : m_A_values) { remove_fixed_or_zero_columns_from_row(i_row.first, i_row.second); } } template void lp_solver::remove_fixed_or_zero_columns_from_row(unsigned i, std::unordered_map & row) { auto & constraint = m_constraints[i]; vector removed; for (auto & col : row) { unsigned j = col.first; lp_assert(m_map_from_var_index_to_column_info.find(j) != m_map_from_var_index_to_column_info.end()); column_info * ci = m_map_from_var_index_to_column_info[j]; if (ci->is_fixed()) { removed.push_back(j); T aj = col.second; constraint.m_rs -= aj * ci->get_fixed_value(); } else { if (numeric_traits::is_zero(col.second)){ removed.push_back(j); } } } for (auto j : removed) { row.erase(j); } } template unsigned lp_solver::try_to_remove_some_rows() { vector rows_to_delete; for (auto & t : m_A_values) { if (row_is_obsolete(t.second, t.first)) { rows_to_delete.push_back(t.first); } if (m_status == lp_status::INFEASIBLE) { return 0; } } if (!rows_to_delete.empty()) { for (unsigned k : rows_to_delete) { m_A_values.erase(k); } } remove_fixed_or_zero_columns(); return static_cast(rows_to_delete.size()); } template void lp_solver::cleanup() { int n = 0; // number of deleted rows int d; while ((d = try_to_remove_some_rows() > 0)) n += d; if (n == 1) { LP_OUT(m_settings, "deleted one row" << std::endl); } else if (n) { LP_OUT(m_settings, "deleted " << n << " rows" << std::endl); } } template void lp_solver::map_external_rows_to_core_solver_rows() { unsigned size = 0; for (auto & row : m_A_values) { m_external_rows_to_core_solver_rows[row.first] = size; m_core_solver_rows_to_external_rows[size] = row.first; size++; } } template void lp_solver::map_external_columns_to_core_solver_columns() { unsigned size = 0; for (auto & row : m_A_values) { for (auto & col : row.second) { if (col.second == numeric_traits::zero() || m_map_from_var_index_to_column_info[col.first]->is_fixed()) { throw_exception("found fixed column"); } unsigned j = col.first; auto column_info_it = m_map_from_var_index_to_column_info.find(j); lp_assert(column_info_it != m_map_from_var_index_to_column_info.end()); auto j_column = column_info_it->second->get_column_index(); if (!is_valid(j_column)) { // j is a newcomer m_map_from_var_index_to_column_info[j]->set_column_index(size); m_core_solver_columns_to_external_columns[size++] = j; } } } } template void lp_solver::unscale() { delete m_A; m_A = nullptr; fill_A_from_A_values(); restore_column_scales_to_one(); fill_m_b(); } template void lp_solver::fill_A_from_A_values() { m_A = new static_matrix(static_cast(m_A_values.size()), number_of_core_structurals()); for (auto & t : m_A_values) { auto row_it = m_external_rows_to_core_solver_rows.find(t.first); lp_assert(row_it != m_external_rows_to_core_solver_rows.end()); unsigned row = row_it->second; for (auto k : t.second) { auto column_info_it = m_map_from_var_index_to_column_info.find(k.first); lp_assert(column_info_it != m_map_from_var_index_to_column_info.end()); column_info *ci = column_info_it->second; unsigned col = ci->get_column_index(); lp_assert(is_valid(col)); bool col_is_flipped = m_map_from_var_index_to_column_info[k.first]->is_flipped(); if (!col_is_flipped) { (*m_A)(row, col) = k.second; } else { (*m_A)(row, col) = - k.second; } } } } template void lp_solver::fill_matrix_A_and_init_right_side() { map_external_rows_to_core_solver_rows(); map_external_columns_to_core_solver_columns(); lp_assert(m_A == nullptr); fill_A_from_A_values(); m_b.resize(m_A->row_count()); } template void lp_solver::count_slacks_and_artificials() { for (int i = row_count() - 1; i >= 0; i--) { count_slacks_and_artificials_for_row(i); } } template void lp_solver::count_slacks_and_artificials_for_row(unsigned i) { lp_assert(this->m_constraints.find(this->m_core_solver_rows_to_external_rows[i]) != this->m_constraints.end()); auto & constraint = this->m_constraints[this->m_core_solver_rows_to_external_rows[i]]; switch (constraint.m_relation) { case Equal: m_artificials++; break; case Greater_or_equal: m_slacks++; if (this->m_b[i] > 0) { m_artificials++; } break; case Less_or_equal: m_slacks++; if (this->m_b[i] < 0) { m_artificials++; } break; } } template T lp_solver::lower_bound_shift_for_row(unsigned i) { T ret = numeric_traits::zero(); auto row = this->m_A_values.find(i); if (row == this->m_A_values.end()) { throw_exception("cannot find row"); } for (auto col : row->second) { ret += col.second * this->m_map_from_var_index_to_column_info[col.first]->get_shift(); } return ret; } template void lp_solver::fill_m_b() { for (int i = this->row_count() - 1; i >= 0; i--) { lp_assert(this->m_constraints.find(this->m_core_solver_rows_to_external_rows[i]) != this->m_constraints.end()); unsigned external_i = this->m_core_solver_rows_to_external_rows[i]; auto & constraint = this->m_constraints[external_i]; this->m_b[i] = constraint.m_rs - lower_bound_shift_for_row(external_i); } } template T lp_solver::get_column_value_with_core_solver(unsigned column, lp_core_solver_base * core_solver) const { auto cit = this->m_map_from_var_index_to_column_info.find(column); if (cit == this->m_map_from_var_index_to_column_info.end()) { return numeric_traits::zero(); } column_info * ci = cit->second; if (ci->is_fixed()) { return ci->get_fixed_value(); } unsigned cj = ci->get_column_index(); if (cj != static_cast(-1)) { T v = core_solver->get_var_value(cj) * this->m_column_scale[cj]; if (ci->is_free()) { return v; } if (!ci->is_flipped()) { return v + ci->get_lower_bound(); } // the flipped case when there is only upper bound return -v + ci->get_upper_bound(); // } return numeric_traits::zero(); // returns zero for out of boundary columns } template void lp_solver::set_scaled_cost(unsigned j) { // grab original costs but modify it with the column scales lp_assert(j < this->m_column_scale.size()); column_info * ci = this->m_map_from_var_index_to_column_info[this->m_core_solver_columns_to_external_columns[j]]; T cost = ci->get_cost(); if (ci->is_flipped()){ cost *= T(-1); } lp_assert(ci->is_fixed() == false); this->m_costs[j] = cost * this->m_column_scale[j]; } } z3-z3-4.8.7/src/util/lp/lp_utils.cpp000066400000000000000000000005051356505360400171450ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #include "util/lp/lp_utils.h" #ifdef lp_for_z3 namespace lp { double numeric_traits::g_zero = 0.0; double numeric_traits::g_one = 1.0; } #endif z3-z3-4.8.7/src/util/lp/lp_utils.h000066400000000000000000000132651356505360400166210ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #pragma once #include #include "util/lp/numeric_pair.h" #include "util/debug.h" #include template void print_vector(const C & t, std::ostream & out) { for (const auto & p : t) out << p << " "; out << std::endl; } template bool try_get_value(const std::unordered_map & map, const A& key, B & val) { const auto it = map.find(key); if (it == map.end()) return false; val = it->second; return true; } template bool contains(const std::unordered_map & map, const A& key) { return map.find(key) != map.end(); } #ifdef lp_for_z3 #ifdef Z3DEBUG #define Z3DEBUG 1 #endif namespace lp { template void print_linear_combination_of_column_indices_only(const T & coeffs, std::ostream & out) { bool first = true; for (const auto & it : coeffs) { auto val = it.coeff(); if (first) { first = false; } else { if (val.is_pos()) { out << " + "; } else { out << " - "; val = -val; } } if (val == 1) out << " "; else out << T_to_string(val); out << "x" << it.var(); } } inline void throw_exception(std::string && str) { throw default_exception(std::move(str)); } typedef z3_exception exception; #define lp_assert(_x_) { SASSERT(_x_); } inline void lp_unreachable() { lp_assert(false); } template inline X zero_of_type() { return numeric_traits::zero(); } template inline X one_of_type() { return numeric_traits::one(); } template inline bool is_zero(const X & v) { return numeric_traits::is_zero(v); } template inline bool is_pos(const X & v) { return numeric_traits::is_pos(v); } template inline bool is_neg(const X & v) { return numeric_traits::is_neg(v); } template inline bool is_integer(const X & v) { return numeric_traits::is_int(v); } template inline X ceil_ratio(const X & a, const X & b) { return numeric_traits::ceil_ratio(a, b); } template inline X floor_ratio(const X & a, const X & b) { return numeric_traits::floor_ratio(a, b); } template inline bool precise() { return numeric_traits::precise(); } } namespace std { template<> struct hash { inline size_t operator()(const rational & v) const { return v.hash(); } }; } template inline void hash_combine(std::size_t & seed, const T & v) { seed ^= std::hash()(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); } namespace std { template struct hash> { inline size_t operator()(const pair & v) const { size_t seed = 0; hash_combine(seed, v.first); hash_combine(seed, v.second); return seed; } }; template<> struct hash> { inline size_t operator()(const lp::numeric_pair & v) const { size_t seed = 0; hash_combine(seed, v.x); hash_combine(seed, v.y); return seed; } }; } #else // else of #if lp_for_z3 #include #include //include "util/numerics/mpq.h" //include "util/numerics/numeric_traits.h" //include "util/numerics/double.h" #ifdef __CLANG__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wmismatched-tags" #endif namespace std { template<> struct hash { inline size_t operator()(const lp::mpq & v) const { return v.hash(); } }; } namespace lp { template inline bool precise() { return numeric_traits::precise();} template inline X one_of_type() { return numeric_traits::one(); } template inline bool is_zero(const X & v) { return numeric_traits::is_zero(v); } template inline bool is_pos(const X & v) { return numeric_traits::is_pos(v); } template inline bool is_int(const X & v) { return numeric_traits::is_int(v); } template inline X ceil_ratio(const X & a, const X & b) { return numeric_traits::ceil_ratio(a, b); } template inline X floor_ratio(const X & a, const X & b) { return numeric_traits::floor_ratio(v); } template inline double get_double(const X & v) { return numeric_traits::get_double(v); } template inline T zero_of_type() {return numeric_traits::zero();} inline void throw_exception(std::string str) { throw exception(str); } template inline T from_string(std::string const & ) { lp_unreachable();} template <> double inline from_string(std::string const & str) { return atof(str.c_str());} template <> mpq inline from_string(std::string const & str) { return mpq(atof(str.c_str())); } } // closing lp template inline void hash_combine(std::size_t & seed, const T & v) { seed ^= std::hash()(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); } namespace std { template struct hash> { inline size_t operator()(const pair & v) const { size_t seed = 0; hash_combine(seed, v.first); hash_combine(seed, v.second); return seed; } }; template<> struct hash> { inline size_t operator()(const lp::numeric_pair & v) const { size_t seed = 0; hash_combine(seed, v.x); hash_combine(seed, v.y); return seed; } }; } // std #ifdef __CLANG__ #pragma clang diagnostic pop #endif #endif z3-z3-4.8.7/src/util/lp/lu.cpp000066400000000000000000000126221356505360400157350ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #include #include #include #include "util/vector.h" #include "util/debug.h" #include "util/lp/lu_def.h" namespace lp { template double dot_product(vector const&, vector const&); template lu>::lu(static_matrix const&, vector&, lp_settings&); template void lu>::push_matrix_to_tail(tail_matrix*); template void lu>::replace_column(double, indexed_vector&, unsigned); template void lu>::solve_Bd(unsigned int, indexed_vector&, indexed_vector&); template lu>::~lu(); template void lu>::push_matrix_to_tail(tail_matrix*); template void lu>::solve_Bd(unsigned int, indexed_vector&, indexed_vector&); template lu>::~lu(); template void lu>::push_matrix_to_tail(tail_matrix*); template void lu>::solve_Bd(unsigned int, indexed_vector&, indexed_vector&); template lu>::~lu(); template mpq dot_product(vector const&, vector const&); template void init_factorization> (lu>*&, static_matrix&, vector&, lp_settings&); template void init_factorization> (lu>*&, static_matrix&, vector&, lp_settings&); template void init_factorization>(lu >*&, static_matrix&, vector&, lp_settings&); template void print_matrix>(square_sparse_matrix&, std::ostream & out); template void print_matrix>(static_matrix&, std::ostream&); template void print_matrix >(static_matrix&, std::ostream&); template void print_matrix>(static_matrix&, std::ostream & out); #ifdef Z3DEBUG template bool lu>::is_correct(const vector& basis); template bool lu>::is_correct( vector const &); template dense_matrix get_B>(lu>&, const vector& basis); template dense_matrix get_B>(lu>&, vector const&); #endif template bool lu>::pivot_the_row(int); // NOLINT template void lu>::init_vector_w(unsigned int, indexed_vector&); template void lu>::solve_By(vector&); template void lu>::solve_By_when_y_is_ready_for_X(vector&); template void lu>::solve_yB_with_error_check(vector&, const vector& basis); template void lu>::solve_yB_with_error_check_indexed(indexed_vector&, vector const&, const vector & basis, const lp_settings&); template void lu>::replace_column(mpq, indexed_vector&, unsigned); template void lu>::solve_By(vector&); template void lu>::solve_By_when_y_is_ready_for_X(vector&); template void lu>::solve_yB_with_error_check(vector&, const vector& basis); template void lu>::solve_yB_with_error_check_indexed(indexed_vector&, vector< int > const&, const vector & basis, const lp_settings&); template void lu >::solve_yB_with_error_check_indexed(indexed_vector&, vector< int > const&, const vector & basis, const lp_settings&); template void lu >::init_vector_w(unsigned int, indexed_vector&); template void lu >::replace_column(mpq, indexed_vector&, unsigned); template void lu >::solve_Bd_faster(unsigned int, indexed_vector&); template void lu >::solve_By(vector&); template void lu >::solve_By_when_y_is_ready_for_X(vector&); template void lu >::solve_yB_with_error_check(vector&, const vector& basis); template void lu>::solve_By(indexed_vector&); template void lu>::solve_By(indexed_vector&); template void lu>::solve_yB_indexed(indexed_vector&); template void lu >::solve_yB_indexed(indexed_vector&); template void lu>::solve_By_for_T_indexed_only(indexed_vector&, lp_settings const&); template void lu>::solve_By_for_T_indexed_only(indexed_vector&, lp_settings const&); #ifdef Z3DEBUG template void print_matrix>(tail_matrix&, std::ostream&); #endif } z3-z3-4.8.7/src/util/lp/lu.h000066400000000000000000000276051356505360400154110ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: for matrix B we have t0*...*tn-1*B = Q*U*R here ti are matrices corresponding to pivot operations, including columns and rows swaps, or a multiplication matrix row by a number Q, R - permutations and U is an upper triangular matrix Author: Lev Nachmanson (levnach) Revision History: --*/ #pragma once #include "util/vector.h" #include "util/debug.h" #include #include #include "util/lp/square_sparse_matrix.h" #include "util/lp/static_matrix.h" #include #include "util/lp/numeric_pair.h" #include #include #include "util/lp/row_eta_matrix.h" #include "util/lp/square_dense_submatrix.h" #include "util/lp/dense_matrix.h" namespace lp { template // print the nr x nc submatrix at the top left corner void print_submatrix(square_sparse_matrix & m, unsigned mr, unsigned nc); template void print_matrix(M &m, std::ostream & out); template X dot_product(const vector & a, const vector & b) { lp_assert(a.size() == b.size()); auto r = zero_of_type(); for (unsigned i = 0; i < a.size(); i++) { r += a[i] * b[i]; } return r; } template class one_elem_on_diag: public tail_matrix { unsigned m_i; T m_val; public: one_elem_on_diag(unsigned i, T val) : m_i(i), m_val(val) { #ifdef Z3DEBUG m_one_over_val = numeric_traits::one() / m_val; #endif } bool is_dense() const override { return false; } one_elem_on_diag(const one_elem_on_diag & o); #ifdef Z3DEBUG unsigned m_m; unsigned m_n; void set_number_of_rows(unsigned m) override { m_m = m; m_n = m; } void set_number_of_columns(unsigned n) override { m_m = n; m_n = n; } T m_one_over_val; T get_elem (unsigned i, unsigned j) const override; unsigned row_count() const override { return m_m; } // not defined } unsigned column_count() const override { return m_m; } // not defined } #endif void apply_from_left(vector & w, lp_settings &) override { w[m_i] /= m_val; } void apply_from_right(vector & w) override { w[m_i] /= m_val; } void apply_from_right(indexed_vector & w) override { if (is_zero(w.m_data[m_i])) return; auto & v = w.m_data[m_i] /= m_val; if (lp_settings::is_eps_small_general(v, 1e-14)) { w.erase_from_index(m_i); v = zero_of_type(); } } void apply_from_left_to_T(indexed_vector & w, lp_settings & settings) override; void conjugate_by_permutation(permutation_matrix & p) { // this = p * this * p(-1) #ifdef Z3DEBUG // auto rev = p.get_reverse(); // auto deb = ((*this) * rev); // deb = p * deb; #endif m_i = p.apply_reverse(m_i); #ifdef Z3DEBUG // lp_assert(*this == deb); #endif } }; // end of one_elem_on_diag enum class LU_status { OK, Degenerated}; // This class supports updates of the columns of B, and solves systems Bx=b,and yB=c // Using Suhl-Suhl method described in the dissertation of Achim Koberstein, Chapter 5 template class lu { LU_status m_status; public: typedef typename M::coefftype T; typedef typename M::argtype X; // the fields unsigned m_dim; const M & m_A; permutation_matrix m_Q; permutation_matrix m_R; permutation_matrix m_r_wave; square_sparse_matrix m_U; square_dense_submatrix* m_dense_LU; vector *> m_tail; lp_settings & m_settings; bool m_failure; indexed_vector m_row_eta_work_vector; indexed_vector m_w_for_extension; indexed_vector m_y_copy; indexed_vector m_ii; //to optimize the work with the m_index fields unsigned m_refactor_counter; // constructor // if A is an m by n matrix then basis has length m and values in [0,n); the values are all different // they represent the set of m columns lu(const M & A, vector& basis, lp_settings & settings); lu(const M & A, lp_settings&); void debug_test_of_basis(const M & A, vector & basis); void solve_Bd_when_w_is_ready(vector & d, indexed_vector& w ); void solve_By(indexed_vector & y); void solve_By(vector & y); void solve_By_for_T_indexed_only(indexed_vector& y, const lp_settings &); template void solve_By_when_y_is_ready(indexed_vector & y); void solve_By_when_y_is_ready_for_X(vector & y); void solve_By_when_y_is_ready_for_T(vector & y, vector & index); void print_indexed_vector(indexed_vector & w, std::ofstream & f); void print_matrix_compact(std::ostream & f); void print(indexed_vector & w, const vector& basis); void solve_Bd(unsigned a_column, vector & d, indexed_vector & w); void solve_Bd(unsigned a_column, indexed_vector & d, indexed_vector & w); void solve_Bd_faster(unsigned a_column, indexed_vector & d); // d is the right side on the input and the solution at the exit void solve_yB(vector& y); void solve_yB_indexed(indexed_vector& y); void add_delta_to_solution_indexed(indexed_vector& y); void add_delta_to_solution(const vector& yc, vector& y); void find_error_of_yB(vector& yc, const vector& y, const vector& basis); void find_error_of_yB_indexed(const indexed_vector& y, const vector& heading, const lp_settings& settings); void solve_yB_with_error_check(vector & y, const vector& basis); void solve_yB_with_error_check_indexed(indexed_vector & y, const vector& heading, const vector & basis, const lp_settings &); void apply_Q_R_to_U(permutation_matrix & r_wave); LU_status get_status() { return m_status; } void set_status(LU_status status) { m_status = status; } ~lu(); void init_vector_y(vector & y); void perform_transformations_on_w(indexed_vector& w); void init_vector_w(unsigned entering, indexed_vector & w); void apply_lp_list_to_w(indexed_vector & w); void apply_lp_list_to_y(vector& y); void swap_rows(int j, int k); void swap_columns(int j, int pivot_column); void push_matrix_to_tail(tail_matrix* tm) { m_tail.push_back(tm); } bool pivot_the_row(int row); eta_matrix * get_eta_matrix_for_pivot(unsigned j); // we're processing the column j now eta_matrix * get_eta_matrix_for_pivot(unsigned j, square_sparse_matrix& copy_of_U); // see page 407 of Chvatal unsigned transform_U_to_V_by_replacing_column(indexed_vector & w, unsigned leaving_column_of_U); #ifdef Z3DEBUG void check_vector_w(unsigned entering); void check_apply_matrix_to_vector(matrix *lp, T *w); void check_apply_lp_lists_to_w(T * w); // provide some access operators for testing permutation_matrix & Q() { return m_Q; } permutation_matrix & R() { return m_R; } matrix & U() { return m_U; } unsigned tail_size() { return m_tail.size(); } tail_matrix * get_lp_matrix(unsigned i) { return m_tail[i]; } T B_(unsigned i, unsigned j, const vector& basis) { return m_A[i][basis[j]]; } unsigned dimension() { return m_dim; } #endif unsigned get_number_of_nonzeroes() { return m_U.get_number_of_nonzeroes(); } void process_column(int j); bool is_correct(const vector& basis); bool is_correct(); #ifdef Z3DEBUG dense_matrix tail_product(); dense_matrix get_left_side(const vector& basis); dense_matrix get_left_side(); dense_matrix get_right_side(); #endif // needed for debugging purposes void copy_w(T *buffer, indexed_vector & w); // needed for debugging purposes void restore_w(T *buffer, indexed_vector & w); bool all_columns_and_rows_are_active(); bool too_dense(unsigned j) const; void pivot_in_dense_mode(unsigned i); void create_initial_factorization(); void calculate_r_wave_and_update_U(unsigned bump_start, unsigned bump_end, permutation_matrix & r_wave); void scan_last_row_to_work_vector(unsigned lowest_row_of_the_bump); bool diagonal_element_is_off(T /* diag_element */) { return false; } void pivot_and_solve_the_system(unsigned replaced_column, unsigned lowest_row_of_the_bump); // see Achim Koberstein's thesis page 58, but here we solve the system and pivot to the last // row at the same time row_eta_matrix *get_row_eta_matrix_and_set_row_vector(unsigned replaced_column, unsigned lowest_row_of_the_bump, const T & pivot_elem_for_checking); void replace_column(T pivot_elem, indexed_vector & w, unsigned leaving_column_of_U); void calculate_Lwave_Pwave_for_bump(unsigned replaced_column, unsigned lowest_row_of_the_bump); void calculate_Lwave_Pwave_for_last_row(unsigned lowest_row_of_the_bump, T diagonal_element); void prepare_entering(unsigned entering, indexed_vector & w) { init_vector_w(entering, w); } bool need_to_refactor() { return m_refactor_counter >= 200; } void adjust_dimension_with_matrix_A() { lp_assert(m_A.row_count() >= m_dim); m_dim = m_A.row_count(); m_U.resize(m_dim); m_Q.resize(m_dim); m_R.resize(m_dim); m_row_eta_work_vector.resize(m_dim); } std::unordered_set get_set_of_columns_to_replace_for_add_last_rows(const vector & heading) const { std::unordered_set columns_to_replace; unsigned m = m_A.row_count(); unsigned m_prev = m_U.dimension(); lp_assert(m_A.column_count() == heading.size()); for (unsigned i = m_prev; i < m; i++) { for (const row_cell & c : m_A.m_rows[i]) { int h = heading[c.var()]; if (h < 0) { continue; } columns_to_replace.insert(c.var()); } } return columns_to_replace; } void add_last_rows_to_B(const vector & heading, const std::unordered_set & columns_to_replace) { unsigned m = m_A.row_count(); lp_assert(m_A.column_count() == heading.size()); adjust_dimension_with_matrix_A(); m_w_for_extension.resize(m); // At this moment the LU is correct // for B extended by only by ones at the diagonal in the lower right corner for (unsigned j :columns_to_replace) { lp_assert(heading[j] >= 0); replace_column_with_only_change_at_last_rows(j, heading[j]); if (get_status() == LU_status::Degenerated) break; } } // column j is a basis column, and there is a change in the last rows void replace_column_with_only_change_at_last_rows(unsigned j, unsigned column_to_change_in_U) { init_vector_w(j, m_w_for_extension); replace_column(zero_of_type(), m_w_for_extension, column_to_change_in_U); } bool has_dense_submatrix() const { for (auto m : m_tail) if (m->is_dense()) return true; return false; } }; // end of lu template void init_factorization(lu* & factorization, M & m_A, vector & m_basis, lp_settings &m_settings); #ifdef Z3DEBUG template dense_matrix get_B(lu& f, const vector& basis); template dense_matrix get_B(lu& f); #endif } z3-z3-4.8.7/src/util/lp/lu_def.h000066400000000000000000000775521356505360400162350ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #include #include #include #include "util/vector.h" #include #include "util/debug.h" #include "util/lp/lu.h" namespace lp { template // print the nr x nc submatrix at the top left corner void print_submatrix(square_sparse_matrix & m, unsigned mr, unsigned nc, std::ostream & out) { vector> A; vector widths; for (unsigned i = 0; i < m.row_count() && i < mr ; i++) { A.push_back(vector()); for (unsigned j = 0; j < m.column_count() && j < nc; j++) { A[i].push_back(T_to_string(static_cast(m(i, j)))); } } for (unsigned j = 0; j < m.column_count() && j < nc; j++) { widths.push_back(get_width_of_column(j, A)); } print_matrix_with_widths(A, widths, out); } template void print_matrix(M &m, std::ostream & out) { vector> A; vector widths; for (unsigned i = 0; i < m.row_count(); i++) { A.push_back(vector()); for (unsigned j = 0; j < m.column_count(); j++) { A[i].push_back(T_to_string(m[i][j])); } } for (unsigned j = 0; j < m.column_count(); j++) { widths.push_back(get_width_of_column(j, A)); } print_matrix_with_widths(A, widths, out); } template one_elem_on_diag::one_elem_on_diag(const one_elem_on_diag & o) { m_i = o.m_i; m_val = o.m_val; #ifdef Z3DEBUG m_m = m_n = o.m_m; m_one_over_val = numeric_traits::one() / o.m_val; #endif } #ifdef Z3DEBUG template T one_elem_on_diag::get_elem(unsigned i, unsigned j) const { if (i == j){ if (j == m_i) { return m_one_over_val; } return numeric_traits::one(); } return numeric_traits::zero(); } #endif template void one_elem_on_diag::apply_from_left_to_T(indexed_vector & w, lp_settings & settings) { T & t = w[m_i]; if (numeric_traits::is_zero(t)) { return; } t /= m_val; if (numeric_traits::precise()) return; if (settings.abs_val_is_smaller_than_drop_tolerance(t)) { w.erase_from_index(m_i); t = numeric_traits::zero(); } } // This class supports updates of the columns of B, and solves systems Bx=b,and yB=c // Using Suhl-Suhl method described in the dissertation of Achim Koberstein, Chapter 5 template lu::lu(const M& A, vector& basis, lp_settings & settings): m_status(LU_status::OK), m_dim(A.row_count()), m_A(A), m_Q(m_dim), m_R(m_dim), m_r_wave(m_dim), m_U(A, basis), // create the square matrix that eventually will be factorized m_settings(settings), m_failure(false), m_row_eta_work_vector(A.row_count()), m_refactor_counter(0) { lp_assert(!(numeric_traits::precise() && settings.use_tableau())); #ifdef Z3DEBUG debug_test_of_basis(A, basis); #endif ++m_settings.st().m_num_factorizations; create_initial_factorization(); #ifdef Z3DEBUG // lp_assert(check_correctness()); #endif } template lu::lu(const M& A, lp_settings & settings): m_status(LU_status::OK), m_dim(A.row_count()), m_A(A), m_Q(m_dim), m_R(m_dim), m_r_wave(m_dim), m_U(A), // create the square matrix that eventually will be factorized m_settings(settings), m_failure(false), m_row_eta_work_vector(A.row_count()), m_refactor_counter(0) { lp_assert(A.row_count() == A.column_count()); create_initial_factorization(); #ifdef Z3DEBUG lp_assert(is_correct()); #endif } template void lu::debug_test_of_basis( M const & A, vector & basis) { std::set set; for (unsigned i = 0; i < A.row_count(); i++) { lp_assert(basis[i]< A.column_count()); set.insert(basis[i]); } lp_assert(set.size() == A.row_count()); } template void lu::solve_By(indexed_vector & y) { lp_assert(false); // not implemented // init_vector_y(y); // solve_By_when_y_is_ready(y); } template void lu::solve_By(vector & y) { init_vector_y(y); solve_By_when_y_is_ready_for_X(y); } template void lu::solve_By_when_y_is_ready_for_X(vector & y) { if (numeric_traits::precise()) { m_U.solve_U_y(y); m_R.apply_reverse_from_left_to_X(y); // see 24.3 from Chvatal return; } m_U.double_solve_U_y(y); m_R.apply_reverse_from_left_to_X(y); // see 24.3 from Chvatal unsigned i = m_dim; while (i--) { if (is_zero(y[i])) continue; if (m_settings.abs_val_is_smaller_than_drop_tolerance(y[i])){ y[i] = zero_of_type(); } } } template void lu::solve_By_when_y_is_ready_for_T(vector & y, vector & index) { if (numeric_traits::precise()) { m_U.solve_U_y(y); m_R.apply_reverse_from_left_to_T(y); // see 24.3 from Chvatal unsigned j = m_dim; while (j--) { if (!is_zero(y[j])) index.push_back(j); } return; } m_U.double_solve_U_y(y); m_R.apply_reverse_from_left_to_T(y); // see 24.3 from Chvatal unsigned i = m_dim; while (i--) { if (is_zero(y[i])) continue; if (m_settings.abs_val_is_smaller_than_drop_tolerance(y[i])){ y[i] = zero_of_type(); } else { index.push_back(i); } } } template void lu::solve_By_for_T_indexed_only(indexed_vector & y, const lp_settings & settings) { if (numeric_traits::precise()) { vector active_rows; m_U.solve_U_y_indexed_only(y, settings, active_rows); m_R.apply_reverse_from_left(y); // see 24.3 from Chvatal return; } m_U.double_solve_U_y(y, m_settings); m_R.apply_reverse_from_left(y); // see 24.3 from Chvatal } template void lu::print_matrix_compact(std::ostream & f) { f << "matrix_start" << std::endl; f << "nrows " << m_A.row_count() << std::endl; f << "ncolumns " << m_A.column_count() << std::endl; for (unsigned i = 0; i < m_A.row_count(); i++) { auto & row = m_A.m_rows[i]; f << "row " << i << std::endl; for (auto & t : row) { f << "column " << t.m_j << " value " << t.m_value << std::endl; } f << "row_end" << std::endl; } f << "matrix_end" << std::endl; } template void lu< M>::print(indexed_vector & w, const vector& basis) { std::string dump_file_name("/tmp/lu"); remove(dump_file_name.c_str()); std::ofstream f(dump_file_name); if (!f.is_open()) { LP_OUT(m_settings, "cannot open file " << dump_file_name << std::endl); return; } LP_OUT(m_settings, "writing lu dump to " << dump_file_name << std::endl); print_matrix_compact(f); print_vector(basis, f); print_indexed_vector(w, f); f.close(); } template void lu< M>::solve_Bd(unsigned a_column, indexed_vector & d, indexed_vector & w) { init_vector_w(a_column, w); if (w.m_index.size() * ratio_of_index_size_to_all_size() < d.m_data.size()) { // this const might need some tuning d = w; solve_By_for_T_indexed_only(d, m_settings); } else { d.m_data = w.m_data; d.m_index.clear(); solve_By_when_y_is_ready_for_T(d.m_data, d.m_index); } } template void lu< M>::solve_Bd_faster(unsigned a_column, indexed_vector & d) { // puts the a_column into d init_vector_w(a_column, d); solve_By_for_T_indexed_only(d, m_settings); } template void lu< M>::solve_yB(vector& y) { // first solve yU = cb*R(-1) m_R.apply_reverse_from_right_to_T(y); // got y = cb*R(-1) m_U.solve_y_U(y); // got y*U=cb*R(-1) m_Q.apply_reverse_from_right_to_T(y); // for (auto e = m_tail.rbegin(); e != m_tail.rend(); ++e) { #ifdef Z3DEBUG (*e)->set_number_of_columns(m_dim); #endif (*e)->apply_from_right(y); } } template void lu< M>::solve_yB_indexed(indexed_vector& y) { lp_assert(y.is_OK()); // first solve yU = cb*R(-1) m_R.apply_reverse_from_right_to_T(y); // got y = cb*R(-1) lp_assert(y.is_OK()); m_U.solve_y_U_indexed(y, m_settings); // got y*U=cb*R(-1) lp_assert(y.is_OK()); m_Q.apply_reverse_from_right_to_T(y); lp_assert(y.is_OK()); for (auto e = m_tail.rbegin(); e != m_tail.rend(); ++e) { #ifdef Z3DEBUG (*e)->set_number_of_columns(m_dim); #endif (*e)->apply_from_right(y); lp_assert(y.is_OK()); } } template void lu< M>::add_delta_to_solution(const vector& yc, vector& y){ unsigned i = static_cast(y.size()); while (i--) y[i]+=yc[i]; } template void lu< M>::add_delta_to_solution_indexed(indexed_vector& y) { // the delta sits in m_y_copy, put result into y lp_assert(y.is_OK()); lp_assert(m_y_copy.is_OK()); m_ii.clear(); m_ii.resize(y.data_size()); for (unsigned i : y.m_index) m_ii.set_value(1, i); for (unsigned i : m_y_copy.m_index) { y.m_data[i] += m_y_copy[i]; if (m_ii[i] == 0) m_ii.set_value(1, i); } lp_assert(m_ii.is_OK()); y.m_index.clear(); for (unsigned i : m_ii.m_index) { T & v = y.m_data[i]; if (!lp_settings::is_eps_small_general(v, 1e-14)) y.m_index.push_back(i); else if (!numeric_traits::is_zero(v)) v = zero_of_type(); } lp_assert(y.is_OK()); } template void lu< M>::find_error_of_yB(vector& yc, const vector& y, const vector& m_basis) { unsigned i = m_dim; while (i--) { yc[i] -= m_A.dot_product_with_column(y, m_basis[i]); } } template void lu< M>::find_error_of_yB_indexed(const indexed_vector& y, const vector& heading, const lp_settings& settings) { #if 0 == 1 // it is a non efficient version indexed_vector yc = m_y_copy; yc.m_index.clear(); lp_assert(!numeric_traits::precise()); { vector d_basis(y.m_data.size()); for (unsigned j = 0; j < heading.size(); j++) { if (heading[j] >= 0) { d_basis[heading[j]] = j; } } unsigned i = m_dim; while (i--) { T & v = yc.m_data[i] -= m_A.dot_product_with_column(y.m_data, d_basis[i]); if (settings.abs_val_is_smaller_than_drop_tolerance(v)) v = zero_of_type(); else yc.m_index.push_back(i); } } #endif lp_assert(m_ii.is_OK()); m_ii.clear(); m_ii.resize(y.data_size()); lp_assert(m_y_copy.is_OK()); // put the error into m_y_copy for (auto k : y.m_index) { auto & row = m_A.m_rows[k]; const T & y_k = y.m_data[k]; for (auto & c : row) { unsigned j = c.var(); int hj = heading[j]; if (hj < 0) continue; if (m_ii.m_data[hj] == 0) m_ii.set_value(1, hj); m_y_copy.m_data[hj] -= c.get_val() * y_k; } } // add the index of m_y_copy to m_ii for (unsigned i : m_y_copy.m_index) { if (m_ii.m_data[i] == 0) m_ii.set_value(1, i); } // there is no guarantee that m_y_copy is OK here, but its index // is contained in m_ii index m_y_copy.m_index.clear(); // setup the index of m_y_copy for (auto k : m_ii.m_index) { T& v = m_y_copy.m_data[k]; if (settings.abs_val_is_smaller_than_drop_tolerance(v)) v = zero_of_type(); else { m_y_copy.set_value(v, k); } } lp_assert(m_y_copy.is_OK()); } // solves y*B = y // y is the input template void lu< M>::solve_yB_with_error_check_indexed(indexed_vector & y, const vector& heading, const vector & basis, const lp_settings & settings) { if (numeric_traits::precise()) { if (y.m_index.size() * ratio_of_index_size_to_all_size() * 3 < m_A.column_count()) { solve_yB_indexed(y); } else { solve_yB(y.m_data); y.restore_index_and_clean_from_data(); } return; } lp_assert(m_y_copy.is_OK()); lp_assert(y.is_OK()); if (y.m_index.size() * ratio_of_index_size_to_all_size() < m_A.column_count()) { m_y_copy = y; solve_yB_indexed(y); lp_assert(y.is_OK()); if (y.m_index.size() * ratio_of_index_size_to_all_size() >= m_A.column_count()) { find_error_of_yB(m_y_copy.m_data, y.m_data, basis); solve_yB(m_y_copy.m_data); add_delta_to_solution(m_y_copy.m_data, y.m_data); y.restore_index_and_clean_from_data(); m_y_copy.clear_all(); } else { find_error_of_yB_indexed(y, heading, settings); // this works with m_y_copy solve_yB_indexed(m_y_copy); add_delta_to_solution_indexed(y); } lp_assert(m_y_copy.is_OK()); } else { solve_yB_with_error_check(y.m_data, basis); y.restore_index_and_clean_from_data(); } } // solves y*B = y // y is the input template void lu< M>::solve_yB_with_error_check(vector & y, const vector& basis) { if (numeric_traits::precise()) { solve_yB(y); return; } auto & yc = m_y_copy.m_data; yc =y; // copy y aside solve_yB(y); find_error_of_yB(yc, y, basis); solve_yB(yc); add_delta_to_solution(yc, y); m_y_copy.clear_all(); } template void lu< M>::apply_Q_R_to_U(permutation_matrix & r_wave) { m_U.multiply_from_right(r_wave); m_U.multiply_from_left_with_reverse(r_wave); } // Solving yB = cb to find the entering variable, // where cb is the cost vector projected to B. // The result is stored in cb. // solving Bd = a ( to find the column d of B^{-1} A_N corresponding to the entering // variable template lu< M>::~lu(){ for (auto t : m_tail) { delete t; } } template void lu< M>::init_vector_y(vector & y) { apply_lp_list_to_y(y); m_Q.apply_reverse_from_left_to_X(y); } template void lu< M>::perform_transformations_on_w(indexed_vector& w) { apply_lp_list_to_w(w); m_Q.apply_reverse_from_left(w); // TBD does not compile: lp_assert(numeric_traits::precise() || check_vector_for_small_values(w, m_settings)); } // see Chvatal 24.3 template void lu< M>::init_vector_w(unsigned entering, indexed_vector & w) { w.clear(); m_A.copy_column_to_indexed_vector(entering, w); // w = a, the column perform_transformations_on_w(w); } template void lu< M>::apply_lp_list_to_w(indexed_vector & w) { for (unsigned i = 0; i < m_tail.size(); i++) { m_tail[i]->apply_from_left_to_T(w, m_settings); // TBD does not compile: lp_assert(check_vector_for_small_values(w, m_settings)); } } template void lu< M>::apply_lp_list_to_y(vector& y) { for (unsigned i = 0; i < m_tail.size(); i++) { m_tail[i]->apply_from_left(y, m_settings); } } template void lu< M>::swap_rows(int j, int k) { if (j != k) { m_Q.transpose_from_left(j, k); m_U.swap_rows(j, k); } } template void lu< M>::swap_columns(int j, int pivot_column) { if (j == pivot_column) return; m_R.transpose_from_right(j, pivot_column); m_U.swap_columns(j, pivot_column); } template bool lu< M>::pivot_the_row(int row) { eta_matrix * eta_matrix = get_eta_matrix_for_pivot(row); if (get_status() != LU_status::OK) { return false; } if (eta_matrix == nullptr) { m_U.shorten_active_matrix(row, nullptr); return true; } if (!m_U.pivot_with_eta(row, eta_matrix, m_settings)) return false; eta_matrix->conjugate_by_permutation(m_Q); push_matrix_to_tail(eta_matrix); return true; } // we're processing the column j now template eta_matrix * lu< M>::get_eta_matrix_for_pivot(unsigned j) { eta_matrix *ret; if(!m_U.fill_eta_matrix(j, &ret)) { set_status(LU_status::Degenerated); } return ret; } // we're processing the column j now template eta_matrix * lu::get_eta_matrix_for_pivot(unsigned j, square_sparse_matrix& copy_of_U) { eta_matrix *ret; copy_of_U.fill_eta_matrix(j, &ret); return ret; } // see page 407 of Chvatal template unsigned lu::transform_U_to_V_by_replacing_column(indexed_vector & w, unsigned leaving_column) { unsigned column_to_replace = m_R.apply_reverse(leaving_column); m_U.replace_column(column_to_replace, w, m_settings); return column_to_replace; } #ifdef Z3DEBUG template void lu::check_vector_w(unsigned entering) { T * w = new T[m_dim]; m_A.copy_column_to_vector(entering, w); check_apply_lp_lists_to_w(w); delete [] w; } template void lu::check_apply_matrix_to_vector(matrix *lp, T *w) { if (lp != nullptr) { lp -> set_number_of_rows(m_dim); lp -> set_number_of_columns(m_dim); apply_to_vector(*lp, w); } } template void lu::check_apply_lp_lists_to_w(T * w) { for (unsigned i = 0; i < m_tail.size(); i++) { check_apply_matrix_to_vector(m_tail[i], w); } permutation_matrix qr = m_Q.get_reverse(); apply_to_vector(qr, w); for (int i = m_dim - 1; i >= 0; i--) { lp_assert(abs(w[i] - w[i]) < 0.0000001); } } #endif template void lu::process_column(int j) { unsigned pi, pj; bool success = m_U.get_pivot_for_column(pi, pj, m_settings.c_partial_pivoting, j); if (!success) { // LP_OUT(m_settings, "get_pivot returned false: cannot find the pivot for column " << j << std::endl); m_failure = true; return; } if (static_cast(pi) == -1) { // LP_OUT(m_settings, "cannot find the pivot for column " << j << std::endl); m_failure = true; return; } swap_columns(j, pj); swap_rows(j, pi); if (!pivot_the_row(j)) { // LP_OUT(m_settings, "pivot_the_row(" << j << ") failed" << std::endl); m_failure = true; } } template bool lu::is_correct(const vector& basis) { #ifdef Z3DEBUG if (get_status() != LU_status::OK) { return false; } dense_matrix left_side = get_left_side(basis); dense_matrix right_side = get_right_side(); return left_side == right_side; #else return true; #endif } template bool lu::is_correct() { #ifdef Z3DEBUG if (get_status() != LU_status::OK) { return false; } dense_matrix left_side = get_left_side(); dense_matrix right_side = get_right_side(); return left_side == right_side; #else return true; #endif } #ifdef Z3DEBUG template dense_matrix lu::tail_product() { lp_assert(tail_size() > 0); dense_matrix left_side = permutation_matrix(m_dim); for (unsigned i = 0; i < tail_size(); i++) { matrix* lp = get_lp_matrix(i); lp->set_number_of_rows(m_dim); lp->set_number_of_columns(m_dim); left_side = ((*lp) * left_side); } return left_side; } template dense_matrix lu::get_left_side(const vector& basis) { dense_matrix left_side = get_B(*this, basis); for (unsigned i = 0; i < tail_size(); i++) { matrix* lp = get_lp_matrix(i); lp->set_number_of_rows(m_dim); lp->set_number_of_columns(m_dim); left_side = ((*lp) * left_side); } return left_side; } template dense_matrix lu::get_left_side() { dense_matrix left_side = get_B(*this); for (unsigned i = 0; i < tail_size(); i++) { matrix* lp = get_lp_matrix(i); lp->set_number_of_rows(m_dim); lp->set_number_of_columns(m_dim); left_side = ((*lp) * left_side); } return left_side; } template dense_matrix lu::get_right_side() { auto ret = U() * R(); ret = Q() * ret; return ret; } #endif // needed for debugging purposes template void lu::copy_w(T *buffer, indexed_vector & w) { unsigned i = m_dim; while (i--) { buffer[i] = w[i]; } } // needed for debugging purposes template void lu::restore_w(T *buffer, indexed_vector & w) { unsigned i = m_dim; while (i--) { w[i] = buffer[i]; } } template bool lu::all_columns_and_rows_are_active() { unsigned i = m_dim; while (i--) { lp_assert(m_U.col_is_active(i)); lp_assert(m_U.row_is_active(i)); } return true; } template bool lu::too_dense(unsigned j) const { unsigned r = m_dim - j; if (r < 5) return false; // if (j * 5 < m_dim * 4) // start looking for dense only at the bottom of the rows // return false; // return r * r * m_settings.density_threshold <= m_U.get_number_of_nonzeroes_below_row(j); return r * r * m_settings.density_threshold <= m_U.get_n_of_active_elems(); } template void lu::pivot_in_dense_mode(unsigned i) { int j = m_dense_LU->find_pivot_column_in_row(i); if (j == -1) { m_failure = true; return; } if (i != static_cast(j)) { swap_columns(i, j); m_dense_LU->swap_columns(i, j); } m_dense_LU->pivot(i, m_settings); } template void lu::create_initial_factorization(){ m_U.prepare_for_factorization(); unsigned j; for (j = 0; j < m_dim; j++) { process_column(j); if (m_failure) { set_status(LU_status::Degenerated); return; } if (too_dense(j)) { break; } } if (j == m_dim) { // TBD does not compile: lp_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings)); // lp_assert(is_correct()); // lp_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings)); return; } j++; m_dense_LU = new square_dense_submatrix(&m_U, j); for (; j < m_dim; j++) { pivot_in_dense_mode(j); if (m_failure) { set_status(LU_status::Degenerated); return; } } m_dense_LU->update_parent_matrix(m_settings); lp_assert(m_dense_LU->is_L_matrix()); m_dense_LU->conjugate_by_permutation(m_Q); push_matrix_to_tail(m_dense_LU); m_refactor_counter = 0; // lp_assert(is_correct()); // lp_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings)); } template void lu::calculate_r_wave_and_update_U(unsigned bump_start, unsigned bump_end, permutation_matrix & r_wave) { if (bump_start > bump_end) { set_status(LU_status::Degenerated); return; } if (bump_start == bump_end) { return; } r_wave[bump_start] = bump_end; // sending the offensive column to the end of the bump for ( unsigned i = bump_start + 1 ; i <= bump_end; i++ ) { r_wave[i] = i - 1; } m_U.multiply_from_right(r_wave); m_U.multiply_from_left_with_reverse(r_wave); } template void lu::scan_last_row_to_work_vector(unsigned lowest_row_of_the_bump) { vector> & last_row_vec = m_U.get_row_values(m_U.adjust_row(lowest_row_of_the_bump)); for (auto & iv : last_row_vec) { if (is_zero(iv.m_value)) continue; lp_assert(!m_settings.abs_val_is_smaller_than_drop_tolerance(iv.m_value)); unsigned adjusted_col = m_U.adjust_column_inverse(iv.m_index); if (adjusted_col < lowest_row_of_the_bump) { m_row_eta_work_vector.set_value(-iv.m_value, adjusted_col); } else { m_row_eta_work_vector.set_value(iv.m_value, adjusted_col); // preparing to calculate the real value in the matrix } } } template void lu::pivot_and_solve_the_system(unsigned replaced_column, unsigned lowest_row_of_the_bump) { // we have the system right side at m_row_eta_work_vector now // solve the system column wise for (unsigned j = replaced_column; j < lowest_row_of_the_bump; j++) { T v = m_row_eta_work_vector[j]; if (numeric_traits::is_zero(v)) continue; // this column does not contribute to the solution unsigned aj = m_U.adjust_row(j); vector> & row = m_U.get_row_values(aj); for (auto & iv : row) { unsigned col = m_U.adjust_column_inverse(iv.m_index); lp_assert(col >= j || numeric_traits::is_zero(iv.m_value)); if (col == j) continue; if (numeric_traits::is_zero(iv.m_value)) { continue; } // the -v is for solving the system ( to zero the last row), and +v is for pivoting T delta = col < lowest_row_of_the_bump? -v * iv.m_value: v * iv.m_value; lp_assert(numeric_traits::is_zero(delta) == false); // m_row_eta_work_vector.add_value_at_index_with_drop_tolerance(col, delta); if (numeric_traits::is_zero(m_row_eta_work_vector[col])) { if (!m_settings.abs_val_is_smaller_than_drop_tolerance(delta)){ m_row_eta_work_vector.set_value(delta, col); } } else { T t = (m_row_eta_work_vector[col] += delta); if (m_settings.abs_val_is_smaller_than_drop_tolerance(t)){ m_row_eta_work_vector[col] = numeric_traits::zero(); auto it = std::find(m_row_eta_work_vector.m_index.begin(), m_row_eta_work_vector.m_index.end(), col); if (it != m_row_eta_work_vector.m_index.end()) m_row_eta_work_vector.m_index.erase(it); } } } } } // see Achim Koberstein's thesis page 58, but here we solve the system and pivot to the last // row at the same time template row_eta_matrix *lu::get_row_eta_matrix_and_set_row_vector(unsigned replaced_column, unsigned lowest_row_of_the_bump, const T & pivot_elem_for_checking) { if (replaced_column == lowest_row_of_the_bump) return nullptr; scan_last_row_to_work_vector(lowest_row_of_the_bump); pivot_and_solve_the_system(replaced_column, lowest_row_of_the_bump); if (numeric_traits::precise() == false && !is_zero(pivot_elem_for_checking)) { T denom = std::max(T(1), abs(pivot_elem_for_checking)); if ( !m_settings.abs_val_is_smaller_than_pivot_tolerance((m_row_eta_work_vector[lowest_row_of_the_bump] - pivot_elem_for_checking) / denom)) { set_status(LU_status::Degenerated); // LP_OUT(m_settings, "diagonal element is off" << std::endl); return nullptr; } } #ifdef Z3DEBUG auto ret = new row_eta_matrix(replaced_column, lowest_row_of_the_bump, m_dim); #else auto ret = new row_eta_matrix(replaced_column, lowest_row_of_the_bump); #endif for (auto j : m_row_eta_work_vector.m_index) { if (j < lowest_row_of_the_bump) { auto & v = m_row_eta_work_vector[j]; if (!is_zero(v)) { if (!m_settings.abs_val_is_smaller_than_drop_tolerance(v)){ ret->push_back(j, v); } v = numeric_traits::zero(); } } } // now the lowest_row_of_the_bump contains the rest of the row to the right of the bump with correct values return ret; } template void lu::replace_column(T pivot_elem_for_checking, indexed_vector & w, unsigned leaving_column_of_U){ m_refactor_counter++; unsigned replaced_column = transform_U_to_V_by_replacing_column( w, leaving_column_of_U); unsigned lowest_row_of_the_bump = m_U.lowest_row_in_column(replaced_column); m_r_wave.init(m_dim); calculate_r_wave_and_update_U(replaced_column, lowest_row_of_the_bump, m_r_wave); auto row_eta = get_row_eta_matrix_and_set_row_vector(replaced_column, lowest_row_of_the_bump, pivot_elem_for_checking); if (get_status() == LU_status::Degenerated) { m_row_eta_work_vector.clear_all(); return; } m_Q.multiply_by_permutation_from_right(m_r_wave); m_R.multiply_by_permutation_reverse_from_left(m_r_wave); if (row_eta != nullptr) { row_eta->conjugate_by_permutation(m_Q); push_matrix_to_tail(row_eta); } calculate_Lwave_Pwave_for_bump(replaced_column, lowest_row_of_the_bump); // lp_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings)); // lp_assert(w.is_OK() && m_row_eta_work_vector.is_OK()); } template void lu::calculate_Lwave_Pwave_for_bump(unsigned replaced_column, unsigned lowest_row_of_the_bump){ T diagonal_elem; if (replaced_column < lowest_row_of_the_bump) { diagonal_elem = m_row_eta_work_vector[lowest_row_of_the_bump]; // lp_assert(m_row_eta_work_vector.is_OK()); m_U.set_row_from_work_vector_and_clean_work_vector_not_adjusted(m_U.adjust_row(lowest_row_of_the_bump), m_row_eta_work_vector, m_settings); } else { diagonal_elem = m_U(lowest_row_of_the_bump, lowest_row_of_the_bump); // todo - get it more efficiently } if (m_settings.abs_val_is_smaller_than_pivot_tolerance(diagonal_elem)) { set_status(LU_status::Degenerated); return; } calculate_Lwave_Pwave_for_last_row(lowest_row_of_the_bump, diagonal_elem); // lp_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings)); } template void lu::calculate_Lwave_Pwave_for_last_row(unsigned lowest_row_of_the_bump, T diagonal_element) { auto l = new one_elem_on_diag(lowest_row_of_the_bump, diagonal_element); #ifdef Z3DEBUG l->set_number_of_columns(m_dim); #endif push_matrix_to_tail(l); m_U.divide_row_by_constant(lowest_row_of_the_bump, diagonal_element, m_settings); l->conjugate_by_permutation(m_Q); } template void init_factorization(lu* & factorization, M & m_A, vector & m_basis, lp_settings &m_settings) { if (factorization != nullptr) delete factorization; factorization = new lu(m_A, m_basis, m_settings); // if (factorization->get_status() != LU_status::OK) // LP_OUT(m_settings, "failing in init_factorization" << std::endl); } #ifdef Z3DEBUG template dense_matrix get_B(lu& f, const vector& basis) { lp_assert(basis.size() == f.dimension()); lp_assert(basis.size() == f.m_U.dimension()); dense_matrix B(f.dimension(), f.dimension()); for (unsigned i = 0; i < f.dimension(); i++) for (unsigned j = 0; j < f.dimension(); j++) B.set_elem(i, j, f.B_(i, j, basis)); return B; } template dense_matrix get_B(lu& f) { dense_matrix B(f.dimension(), f.dimension()); for (unsigned i = 0; i < f.dimension(); i++) for (unsigned j = 0; j < f.dimension(); j++) B.set_elem(i, j, f.m_A[i][j]); return B; } #endif } z3-z3-4.8.7/src/util/lp/matrix.cpp000066400000000000000000000017431356505360400166230ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #include "util/lp/lp_settings.h" #include "util/lp/matrix_def.h" #include "util/lp/static_matrix.h" #include #ifdef Z3DEBUG template bool lp::matrix::is_equal(lp::matrix const&); template bool lp::matrix >::is_equal(lp::matrix > const&); template bool lp::matrix::is_equal(lp::matrix const&); #endif template void lp::print_matrix(lp::matrix const*, std::ostream & out); template void lp::print_matrix >(lp::matrix > const *, std::basic_ostream > &); template void lp::print_matrix(lp::matrix const*, std::ostream&); z3-z3-4.8.7/src/util/lp/matrix.h000066400000000000000000000033341356505360400162660ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #pragma once #include "util/lp/numeric_pair.h" #include "util/vector.h" #include #include "util/lp/lp_settings.h" namespace lp { // used for debugging purposes only template class matrix { public: virtual T get_elem (unsigned i, unsigned j) const = 0; virtual unsigned row_count() const = 0; virtual unsigned column_count() const = 0; virtual void set_number_of_rows(unsigned m) = 0; virtual void set_number_of_columns(unsigned n) = 0; virtual ~matrix() {} bool is_equal(const matrix& other); bool operator == (matrix const & other) { return is_equal(other); } T operator()(unsigned i, unsigned j) const { return get_elem(i, j); } }; template void apply_to_vector(matrix & m, T * w); unsigned get_width_of_column(unsigned j, vector> & A); void print_matrix_with_widths(vector> & A, vector & ws, std::ostream & out, unsigned blanks = 0); void print_string_matrix(vector> & A, std::ostream &, unsigned blanks_in_front = 0); template void print_matrix(matrix const * m, std::ostream & out); template void print_matrix(const vector> & A, std::ostream & out, unsigned blanks_in_front = 0) { vector> s(A.size()); for (unsigned i = 0; i < A.size(); i++) { for (const auto & v : A[i]) { s[i].push_back(T_to_string(v)); } } print_string_matrix(s, out, blanks_in_front); } } z3-z3-4.8.7/src/util/lp/matrix_def.h000066400000000000000000000067761356505360400171210ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #include #include #include "util/lp/matrix.h" namespace lp { #ifdef Z3DEBUG template bool matrix::is_equal(const matrix& other) { if (other.row_count() != row_count() || other.column_count() != column_count()) return false; for (unsigned i = 0; i < row_count(); i++) { for (unsigned j = 0; j < column_count(); j++) { auto a = get_elem(i, j); auto b = other.get_elem(i, j); if (numeric_traits::precise()) { if (a != b) return false; } else if (fabs(numeric_traits::get_double(a - b)) > 0.000001) { // cout << "returning false from operator== of matrix comparison" << endl; // cout << "this matrix is " << endl; // print_matrix(*this); // cout << "other matrix is " << endl; // print_matrix(other); return false; } } } return true; } template void apply_to_vector(matrix & m, T * w) { // here m is a square matrix unsigned dim = m.row_count(); T * wc = new T[dim]; for (unsigned i = 0; i < dim; i++) { wc[i] = w[i]; } for (unsigned i = 0; i < dim; i++) { T t = numeric_traits::zero(); for (unsigned j = 0; j < dim; j++) { t += m(i, j) * wc[j]; } w[i] = t; } delete [] wc; } #endif unsigned get_width_of_column(unsigned j, vector> & A) { unsigned r = 0; for (unsigned i = 0; i < A.size(); i++) { vector & t = A[i]; std::string str = t[j]; unsigned s = static_cast(str.size()); if (r < s) { r = s; } } return r; } void print_matrix_with_widths(vector> & A, vector & ws, std::ostream & out, unsigned blanks_in_front) { for (unsigned i = 0; i < A.size(); i++) { for (unsigned j = 0; j < static_cast(A[i].size()); j++) { if (i != 0 && j == 0) print_blanks(blanks_in_front, out); print_blanks(ws[j] - static_cast(A[i][j].size()), out); out << A[i][j] << " "; } out << std::endl; } } void print_string_matrix(vector> & A, std::ostream & out, unsigned blanks_in_front) { vector widths; if (!A.empty()) for (unsigned j = 0; j < A[0].size(); j++) { widths.push_back(get_width_of_column(j, A)); } print_matrix_with_widths(A, widths, out, blanks_in_front); out << std::endl; } template void print_matrix(vector> & A, std::ostream & out, unsigned blanks_in_front = 0) { vector> s(A.size()); for (unsigned i = 0; i < A.size(); i++) { for (const auto & v : A[i]) { s[i].push_back(T_to_string(v)); } } print_string_matrix(s, out, blanks_in_front); } template void print_matrix(matrix const * m, std::ostream & out) { vector> A(m->row_count()); for (unsigned i = 0; i < m->row_count(); i++) { for (unsigned j = 0; j < m->column_count(); j++) { A[i].push_back(T_to_string(m->get_elem(i, j))); } } print_string_matrix(A, out); } } z3-z3-4.8.7/src/util/lp/monomial.h000066400000000000000000000013271356505360400165750ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Nikolaj Bjorner (nbjorner) Lev Nachmanson (levnach) Revision History: --*/ #pragma once namespace lp { struct monomial { mpq m_coeff; // the coefficient of the monomial var_index m_var; // the variable index public: monomial(const mpq& coeff, var_index var) : m_coeff(coeff), m_var(var) {} monomial(var_index var) : monomial(one_of_type(), var) {} const mpq & coeff() const { return m_coeff; } mpq & coeff() { return m_coeff; } var_index var() const { return m_var; } std::pair to_pair() const { return std::make_pair(coeff(), var());} }; } z3-z3-4.8.7/src/util/lp/mps_reader.h000066400000000000000000000723111356505360400171040ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #pragma once // reads an MPS file reperesenting a Mixed Integer Program #include #include #include #include "util/vector.h" #include #include #include #include #include "util/lp/lp_primal_simplex.h" #include "util/lp/lp_dual_simplex.h" #include "util/lp/lar_solver.h" #include "util/lp/lp_utils.h" #include "util/lp/lp_solver.h" namespace lp { inline bool my_white_space(const char & a) { return a == ' ' || a == '\t'; } inline size_t number_of_whites(const std::string & s) { size_t i = 0; for(;i < s.size(); i++) if (!my_white_space(s[i])) return i; return i; } inline size_t number_of_whites_from_end(const std::string & s) { size_t ret = 0; for(int i = static_cast(s.size()) - 1;i >= 0; i--) if (my_white_space(s[i])) ret++;else break; return ret; } // trim from start inline std::string <rim(std::string &s) { s.erase(0, number_of_whites(s)); return s; } // trim from end inline std::string &rtrim(std::string &s) { // s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun(std::isspace))).base(), s.end()); s.erase(s.end() - number_of_whites_from_end(s), s.end()); return s; } // trim from both ends inline std::string &trim(std::string &s) { return ltrim(rtrim(s)); } inline std::string trim(std::string const &r) { std::string s = r; return ltrim(rtrim(s)); } inline vector string_split(const std::string &source, const char *delimiter, bool keep_empty) { vector results; size_t prev = 0; size_t next = 0; while ((next = source.find_first_of(delimiter, prev)) != std::string::npos) { if (keep_empty || (next - prev != 0)) { results.push_back(source.substr(prev, next - prev)); } prev = next + 1; } if (prev < source.size()) { results.push_back(source.substr(prev)); } return results; } inline vector split_and_trim(const std::string &line) { auto split = string_split(line, " \t", false); vector ret; for (auto s : split) { ret.push_back(trim(s)); } return ret; } template class mps_reader { enum row_type { Cost, Less_or_equal, Greater_or_equal, Equal }; struct bound { T m_low; T m_upper; bool m_low_is_set; bool m_upper_is_set; bool m_value_is_fixed; T m_fixed_value; bool m_free; // constructor bound() : m_low(numeric_traits::zero()), m_low_is_set(true), m_upper_is_set(false), m_value_is_fixed(false), m_free(false) {} // it seems all mps files I have seen have the default low value 0 on a variable }; struct column { std::string m_name; bound * m_bound; unsigned m_index; column(const std::string &name, unsigned index): m_name(name), m_bound(nullptr), m_index(index) { } }; struct row { row_type m_type; std::string m_name; std::unordered_map m_row_columns; unsigned m_index; T m_right_side; T m_range; row(row_type type, const std::string &name, unsigned index) : m_type(type), m_name(name), m_index(index), m_right_side(zero_of_type()), m_range(zero_of_type()) { } }; bool m_is_OK; std::string m_file_name; std::unordered_map m_rows; std::unordered_map m_columns; std::unordered_map m_names_to_var_index; std::string m_line; std::string m_name; std::string m_cost_row_name; std::ifstream m_file_stream; // needed to adjust the index row unsigned m_cost_line_count; unsigned m_line_number; std::ostream * m_message_stream; void set_m_ok_to_false() { *m_message_stream << "setting m_is_OK to false" << std::endl; m_is_OK = false; } std::string get_string_from_position(unsigned offset) { unsigned i = offset; for (; i < m_line.size(); i++){ if (m_line[i] == ' ') break; } lp_assert(m_line.size() >= offset); lp_assert(m_line.size() >> i); lp_assert(i >= offset); return m_line.substr(offset, i - offset); } void set_boundary_for_column(unsigned col, bound * b, lp_solver * solver){ if (b == nullptr) { solver->set_lower_bound(col, numeric_traits::zero()); return; } if (b->m_free) { return; } if (b->m_low_is_set) { solver->set_lower_bound(col, b->m_low); } if (b->m_upper_is_set) { solver->set_upper_bound(col, b->m_upper); } if (b->m_value_is_fixed) { solver->set_fixed_value(col, b->m_fixed_value); } } bool all_white_space() { for (unsigned i = 0; i < m_line.size(); i++) { char c = m_line[i]; if (c != ' ' && c != '\t') { return false; } } return true; } void read_line() { while (m_is_OK) { if (!getline(m_file_stream, m_line)) { m_line_number++; set_m_ok_to_false(); *m_message_stream << "cannot read from file" << std::endl; } m_line_number++; if (!m_line.empty() && m_line[0] != '*' && !all_white_space()) break; } } void read_name() { do { read_line(); if (m_line.find("NAME") != 0) { continue; } m_line = m_line.substr(4); m_name = trim(m_line); break; } while (m_is_OK); } void read_rows() { // look for start of the rows read_line(); do { if (static_cast(m_line.find("ROWS")) >= 0) { break; } } while (m_is_OK); do { read_line(); if (m_line.find("COLUMNS") == 0) { break; } add_row(); } while (m_is_OK); } void read_column_by_columns(const std::string & column_name, std::string column_data) { // uph, let us try to work with columns if (column_data.size() >= 22) { std::string ss = column_data.substr(0, 8); std::string row_name = trim(ss); auto t = m_rows.find(row_name); if (t == m_rows.end()) { *m_message_stream << "cannot find " << row_name << std::endl; goto fail; } else { row * row = t->second; row->m_row_columns[column_name] = numeric_traits::from_string(column_data.substr(8)); if (column_data.size() > 24) { column_data = column_data.substr(25); if (column_data.size() >= 22) { read_column_by_columns(column_name, column_data); } } } } else { fail: set_m_ok_to_false(); *m_message_stream << "cannot understand this line" << std::endl; *m_message_stream << "line = " << m_line << ", line number is " << m_line_number << std::endl; return; } } void read_column(const std::string & column_name, const std::string & column_data){ auto tokens = split_and_trim(column_data); for (unsigned i = 0; i < tokens.size() - 1; i+= 2) { auto row_name = tokens[i]; if (row_name == "'MARKER'") return; // it is the integrality marker, no real data here auto t = m_rows.find(row_name); if (t == m_rows.end()) { read_column_by_columns(column_name, column_data); return; } row *r = t->second; r->m_row_columns[column_name] = numeric_traits::from_string(tokens[i + 1]); } } void read_columns(){ std::string column_name; do { read_line(); if (m_line.find("RHS") == 0) { break; } if (m_line.size() < 22) { (*m_message_stream) << "line is too short for a column" << std::endl; (*m_message_stream) << m_line << std::endl; (*m_message_stream) << "line number is " << m_line_number << std::endl; set_m_ok_to_false(); return; } std::string column_name_tmp = trim(m_line.substr(4, 8)); if (!column_name_tmp.empty()) { column_name = column_name_tmp; } auto col_it = m_columns.find(column_name); mps_reader::column * col; if (col_it == m_columns.end()) { col = new mps_reader::column(column_name, static_cast(m_columns.size())); m_columns[column_name] = col; // (*m_message_stream) << column_name << '[' << col->m_index << ']'<< std::endl; } else { col = col_it->second; } read_column(column_name, m_line.substr(14)); } while (m_is_OK); } void read_rhs() { do { read_line(); if (m_line.find("BOUNDS") == 0 || m_line.find("ENDATA") == 0 || m_line.find("RANGES") == 0) { break; } fill_rhs(); } while (m_is_OK); } void fill_rhs_by_columns(std::string rhsides) { // uph, let us try to work with columns if (rhsides.size() >= 22) { std::string ss = rhsides.substr(0, 8); std::string row_name = trim(ss); auto t = m_rows.find(row_name); if (t == m_rows.end()) { (*m_message_stream) << "cannot find " << row_name << std::endl; goto fail; } else { row * row = t->second; row->m_right_side = numeric_traits::from_string(rhsides.substr(8)); if (rhsides.size() > 24) { rhsides = rhsides.substr(25); if (rhsides.size() >= 22) { fill_rhs_by_columns(rhsides); } } } } else { fail: set_m_ok_to_false(); (*m_message_stream) << "cannot understand this line" << std::endl; (*m_message_stream) << "line = " << m_line << ", line number is " << m_line_number << std::endl; return; } } void fill_rhs() { if (m_line.size() < 14) { (*m_message_stream) << "line is too short" << std::endl; (*m_message_stream) << m_line << std::endl; (*m_message_stream) << "line number is " << m_line_number << std::endl; set_m_ok_to_false(); return; } std::string rhsides = m_line.substr(14); vector splitted_line = split_and_trim(rhsides); for (unsigned i = 0; i < splitted_line.size() - 1; i += 2) { auto t = m_rows.find(splitted_line[i]); if (t == m_rows.end()) { fill_rhs_by_columns(rhsides); return; } row * row = t->second; row->m_right_side = numeric_traits::from_string(splitted_line[i + 1]); } } void read_bounds() { if (m_line.find("BOUNDS") != 0) { return; } do { read_line(); if (m_line[0] != ' ') { break; } create_or_update_bound(); } while (m_is_OK); } void read_ranges() { if (m_line.find("RANGES") != 0) { return; } do { read_line(); auto sl = split_and_trim(m_line); if (sl.size() < 2) { break; } read_range(sl); } while (m_is_OK); } void read_bound_by_columns(const std::string & colstr) { if (colstr.size() < 14) { (*m_message_stream) << "line is too short" << std::endl; (*m_message_stream) << m_line << std::endl; (*m_message_stream) << "line number is " << m_line_number << std::endl; set_m_ok_to_false(); return; } // uph, let us try to work with columns if (colstr.size() >= 22) { std::string ss = colstr.substr(0, 8); std::string column_name = trim(ss); auto t = m_columns.find(column_name); if (t == m_columns.end()) { (*m_message_stream) << "cannot find " << column_name << std::endl; goto fail; } else { vector bound_string; bound_string.push_back(column_name); if (colstr.size() > 14) { bound_string.push_back(colstr.substr(14)); } mps_reader::column * col = t->second; bound * b = col->m_bound; if (b == nullptr) { col->m_bound = b = new bound(); } update_bound(b, bound_string); } } else { fail: set_m_ok_to_false(); (*m_message_stream) << "cannot understand this line" << std::endl; (*m_message_stream) << "line = " << m_line << ", line number is " << m_line_number << std::endl; return; } } void update_bound(bound * b, vector bound_string) { /* UP means an upper bound is applied to the variable. A bound of type LO means a lower bound is applied. A bound type of FX ("fixed") means that the variable has upper and lower bounds equal to a single value. A bound type of FR ("free") means the variable has neither lower nor upper bounds and so can take on negative values. A variation on that is MI for free negative, giving an upper bound of 0 but no lower bound. Bound type PL is for a free positive for zero to plus infinity, but as this is the normal default, it is seldom used. There are also bound types for use in MIP models - BV for binary, being 0 or 1. UI for upper integer and LI for lower integer. SC stands for semi-continuous and indicates that the variable may be zero, but if not must be equal to at least the value given. */ std::string bound_type = get_string_from_position(1); if (bound_type == "BV") { b->m_upper_is_set = true; b->m_upper = 1; return; } if (bound_type == "UP" || bound_type == "UI" || bound_type == "LIMITMAX") { if (bound_string.size() <= 1){ set_m_ok_to_false(); return; } b->m_upper_is_set = true; b->m_upper= numeric_traits::from_string(bound_string[1]); } else if (bound_type == "LO" || bound_type == "LI") { if (bound_string.size() <= 1){ set_m_ok_to_false(); return; } b->m_low_is_set = true; b->m_low = numeric_traits::from_string(bound_string[1]); } else if (bound_type == "FR") { b->m_free = true; } else if (bound_type == "FX") { if (bound_string.size() <= 1){ set_m_ok_to_false(); return; } b->m_value_is_fixed = true; b->m_fixed_value = numeric_traits::from_string(bound_string[1]); } else if (bound_type == "PL") { b->m_low_is_set = true; b->m_low = 0; } else if (bound_type == "MI") { b->m_upper_is_set = true; b->m_upper = 0; } else { (*m_message_stream) << "unexpected bound type " << bound_type << " at line " << m_line_number << std::endl; set_m_ok_to_false(); throw; } } void create_or_update_bound() { const unsigned name_offset = 14; lp_assert(m_line.size() >= 14); vector bound_string = split_and_trim(m_line.substr(name_offset, m_line.size())); if (bound_string.empty()) { set_m_ok_to_false(); (*m_message_stream) << "error at line " << m_line_number << std::endl; throw m_line; } std::string name = bound_string[0]; auto it = m_columns.find(name); if (it == m_columns.end()){ read_bound_by_columns(m_line.substr(14)); return; } mps_reader::column * col = it->second; bound * b = col->m_bound; if (b == nullptr) { col->m_bound = b = new bound(); } update_bound(b, bound_string); } void read_range_by_columns(std::string rhsides) { if (m_line.size() < 14) { (*m_message_stream) << "line is too short" << std::endl; (*m_message_stream) << m_line << std::endl; (*m_message_stream) << "line number is " << m_line_number << std::endl; set_m_ok_to_false(); return; } // uph, let us try to work with columns if (rhsides.size() >= 22) { std::string ss = rhsides.substr(0, 8); std::string row_name = trim(ss); auto t = m_rows.find(row_name); if (t == m_rows.end()) { (*m_message_stream) << "cannot find " << row_name << std::endl; goto fail; } else { row * row = t->second; row->m_range = numeric_traits::from_string(rhsides.substr(8)); maybe_modify_current_row_and_add_row_for_range(row); if (rhsides.size() > 24) { rhsides = rhsides.substr(25); if (rhsides.size() >= 22) { read_range_by_columns(rhsides); } } } } else { fail: set_m_ok_to_false(); (*m_message_stream) << "cannot understand this line" << std::endl; (*m_message_stream) << "line = " << m_line << ", line number is " << m_line_number << std::endl; return; } } void read_range(vector & splitted_line){ for (unsigned i = 1; i < splitted_line.size() - 1; i += 2) { auto it = m_rows.find(splitted_line[i]); if (it == m_rows.end()) { read_range_by_columns(m_line.substr(14)); return; } row * row = it->second; row->m_range = numeric_traits::from_string(splitted_line[i + 1]); maybe_modify_current_row_and_add_row_for_range(row); } } void maybe_modify_current_row_and_add_row_for_range(row * row_with_range) { unsigned index= static_cast(m_rows.size() - m_cost_line_count); std::string row_name = row_with_range->m_name + "_range"; row * other_bound_range_row; switch (row_with_range->m_type) { case row_type::Greater_or_equal: m_rows[row_name] = other_bound_range_row = new row(row_type::Less_or_equal, row_name, index); other_bound_range_row->m_right_side = row_with_range->m_right_side + abs(row_with_range->m_range); break; case row_type::Less_or_equal: m_rows[row_name] = other_bound_range_row = new row(row_type::Greater_or_equal, row_name, index); other_bound_range_row->m_right_side = row_with_range->m_right_side - abs(row_with_range->m_range); break; case row_type::Equal: if (row_with_range->m_range > 0) { row_with_range->m_type = row_type::Greater_or_equal; // the existing row type change m_rows[row_name] = other_bound_range_row = new row(row_type::Less_or_equal, row_name, index); } else { // row->m_range < 0; row_with_range->m_type = row_type::Less_or_equal; // the existing row type change m_rows[row_name] = other_bound_range_row = new row(row_type::Greater_or_equal, row_name, index); } other_bound_range_row->m_right_side = row_with_range->m_right_side + row_with_range->m_range; break; default: (*m_message_stream) << "unexpected bound type " << row_with_range->m_type << " at line " << m_line_number << std::endl; set_m_ok_to_false(); throw; } for (auto s : row_with_range->m_row_columns) { lp_assert(m_columns.find(s.first) != m_columns.end()); other_bound_range_row->m_row_columns[s.first] = s.second; } } void add_row() { if (m_line.length() < 2) { return; } m_line = trim(m_line); char c = m_line[0]; m_line = m_line.substr(1); m_line = trim(m_line); add_row(c); } void add_row(char c) { unsigned index= static_cast(m_rows.size() - m_cost_line_count); switch (c) { case 'E': m_rows[m_line] = new row(row_type::Equal, m_line, index); break; case 'L': m_rows[m_line] = new row(row_type::Less_or_equal, m_line, index); break; case 'G': m_rows[m_line] = new row(row_type::Greater_or_equal, m_line, index); break; case 'N': m_rows[m_line] = new row(row_type::Cost, m_line, index); m_cost_row_name = m_line; m_cost_line_count++; break; } } unsigned range_count() { unsigned ret = 0; for (auto s : m_rows) { if (s.second->m_range != 0) { ret++; } } return ret; } /* If rhs is a constraint's right-hand-side value and range is the constraint's range value, then the range interval is defined according to the following table: sense interval G [rhs, rhs + |range|] L [rhs - |range|, rhs] E [rhs, rhs + |range|] if range > 0, [rhs - |range|, rhs] if range < 0 where |range| is range's absolute value. */ lp_relation get_relation_from_row(row_type rt) { switch (rt) { case mps_reader::Less_or_equal: return lp_relation::Less_or_equal; case mps_reader::Greater_or_equal: return lp_relation::Greater_or_equal; case mps_reader::Equal: return lp_relation::Equal; default: (*m_message_stream) << "Unexpected rt " << rt << std::endl; set_m_ok_to_false(); throw; } } unsigned solver_row_count() { return m_rows.size() - m_cost_line_count + range_count(); } void fill_solver_on_row(row * row, lp_solver *solver) { if (row->m_name != m_cost_row_name) { solver->add_constraint(get_relation_from_row(row->m_type), row->m_right_side, row->m_index); for (auto s : row->m_row_columns) { lp_assert(m_columns.find(s.first) != m_columns.end()); solver->set_row_column_coefficient(row->m_index, m_columns[s.first]->m_index, s.second); } } else { set_solver_cost(row, solver); } } T abs(T & t) { return t < numeric_traits::zero() ? -t: t; } void fill_solver_on_rows(lp_solver * solver) { for (auto row_it : m_rows) { fill_solver_on_row(row_it.second, solver); } } void fill_solver_on_columns(lp_solver * solver){ for (auto s : m_columns) { mps_reader::column * col = s.second; unsigned index = col->m_index; set_boundary_for_column(index, col->m_bound, solver); // optional call solver->give_symbolic_name_to_column(col->m_name, col->m_index); } } void fill_solver(lp_solver *solver) { fill_solver_on_rows(solver); fill_solver_on_columns(solver); } void set_solver_cost(row * row, lp_solver *solver) { for (auto s : row->m_row_columns) { std::string name = s.first; lp_assert(m_columns.find(name) != m_columns.end()); mps_reader::column * col = m_columns[name]; solver->set_cost_for_column(col->m_index, s.second); } } public: void set_message_stream(std::ostream * o) { lp_assert(o != nullptr); m_message_stream = o; } vector column_names() { vector v; for (auto s : m_columns) { v.push_back(s.first); } return v; } ~mps_reader() { for (auto s : m_rows) { delete s.second; } for (auto s : m_columns) { auto col = s.second; delete col->m_bound; delete col; } } mps_reader(const std::string & file_name): m_is_OK(true), m_file_name(file_name), m_file_stream(file_name), m_cost_line_count(0), m_line_number(0), m_message_stream(& std::cout) {} void read() { if (!m_file_stream.is_open()){ set_m_ok_to_false(); return; } read_name(); read_rows(); read_columns(); read_rhs(); if (m_line.find("BOUNDS") == 0) { read_bounds(); read_ranges(); } else if (m_line.find("RANGES") == 0) { read_ranges(); read_bounds(); } } bool is_ok() { return m_is_OK; } lp_solver * create_solver(bool dual) { lp_solver * solver = dual? (lp_solver*)new lp_dual_simplex() : new lp_primal_simplex(); fill_solver(solver); return solver; } lconstraint_kind get_lar_relation_from_row(row_type rt) { switch (rt) { case Less_or_equal: return LE; case Greater_or_equal: return GE; case Equal: return EQ; default: (*m_message_stream) << "Unexpected rt " << rt << std::endl; set_m_ok_to_false(); throw; } } unsigned get_var_index(std::string s) { auto it = m_names_to_var_index.find(s); if (it != m_names_to_var_index.end()) return it->second; unsigned ret = static_cast(m_names_to_var_index.size()); m_names_to_var_index[s] = ret; return ret; } void fill_lar_solver_on_row(row * row, lar_solver *solver) { if (row->m_name != m_cost_row_name) { auto kind = get_lar_relation_from_row(row->m_type); vector> ls; for (auto s : row->m_row_columns) { var_index i = solver->add_var(get_var_index(s.first), false); ls.push_back(std::make_pair(s.second, i)); } solver->add_constraint(ls, kind, row->m_right_side); } else { // ignore the cost row } } void fill_lar_solver_on_rows(lar_solver * solver) { for (auto row_it : m_rows) { fill_lar_solver_on_row(row_it.second, solver); } } void create_low_constraint_for_var(column* col, bound * b, lar_solver *solver) { vector> ls; var_index i = solver->add_var(col->m_index, false); ls.push_back(std::make_pair(numeric_traits::one(), i)); solver->add_constraint(ls, GE, b->m_low); } void create_upper_constraint_for_var(column* col, bound * b, lar_solver *solver) { var_index i = solver->add_var(col->m_index, false); vector> ls; ls.push_back(std::make_pair(numeric_traits::one(), i)); solver->add_constraint(ls, LE, b->m_upper); } void create_equality_contraint_for_var(column* col, bound * b, lar_solver *solver) { var_index i = solver->add_var(col->m_index, false); vector> ls; ls.push_back(std::make_pair(numeric_traits::one(), i)); solver->add_constraint(ls, EQ, b->m_fixed_value); } void fill_lar_solver_on_columns(lar_solver * solver) { for (auto s : m_columns) { mps_reader::column * col = s.second; solver->add_var(col->m_index, false); auto b = col->m_bound; if (b == nullptr) return; if (b->m_free) continue; if (b->m_low_is_set) { create_low_constraint_for_var(col, b, solver); } if (b->m_upper_is_set) { create_upper_constraint_for_var(col, b, solver); } if (b->m_value_is_fixed) { create_equality_contraint_for_var(col, b, solver); } } } void fill_lar_solver(lar_solver * solver) { fill_lar_solver_on_columns(solver); fill_lar_solver_on_rows(solver); } lar_solver * create_lar_solver() { lar_solver * solver = new lar_solver(); fill_lar_solver(solver); return solver; } }; } z3-z3-4.8.7/src/util/lp/nra_solver.cpp000066400000000000000000000221601356505360400174650ustar00rootroot00000000000000/* Copyright (c) 2017 Microsoft Corporation Author: Nikolaj Bjorner */ #include "util/lp/lar_solver.h" #include "util/lp/nra_solver.h" #include "nlsat/nlsat_solver.h" #include "math/polynomial/polynomial.h" #include "math/polynomial/algebraic_numbers.h" #include "util/map.h" namespace nra { struct mon_eq { mon_eq(lp::var_index v, unsigned sz, lp::var_index const* vs): m_v(v), m_vs(sz, vs) {} lp::var_index m_v; svector m_vs; }; struct solver::imp { lp::lar_solver& s; reslimit& m_limit; params_ref m_params; u_map m_lp2nl; // map from lar_solver variables to nlsat::solver variables scoped_ptr m_nlsat; scoped_ptr m_zero; vector m_monomials; unsigned_vector m_monomials_lim; mutable std::unordered_map m_variable_values; // current model imp(lp::lar_solver& s, reslimit& lim, params_ref const& p): s(s), m_limit(lim), m_params(p) { } bool need_check() { return !m_monomials.empty() && !check_assignments(); } void add(lp::var_index v, unsigned sz, lp::var_index const* vs) { m_monomials.push_back(mon_eq(v, sz, vs)); } void push() { m_monomials_lim.push_back(m_monomials.size()); } void pop(unsigned n) { if (n == 0) return; m_monomials.shrink(m_monomials_lim[m_monomials_lim.size() - n]); m_monomials_lim.shrink(m_monomials_lim.size() - n); } /* \brief Check if polynomials are well defined. multiply values for vs and check if they are equal to value for v. epsilon has been computed. */ bool check_assignment(mon_eq const& m) const { rational r1 = m_variable_values[m.m_v]; rational r2(1); for (auto w : m.m_vs) { r2 *= m_variable_values[w]; } return r1 == r2; } bool check_assignments() const { s.get_model(m_variable_values); for (auto const& m : m_monomials) { if (!check_assignment(m)) return false; } return true; } /** \brief one-shot nlsat check. A one shot checker is the least functionality that can enable non-linear reasoning. In addition to checking satisfiability we would also need to identify equalities in the model that should be assumed with the remaining solver. TBD: use partial model from lra_solver to prime the state of nlsat_solver. TBD: explore more incremental ways of applying nlsat (using assumptions) */ lbool check(lp::explanation_t& ex) { SASSERT(need_check()); m_nlsat = alloc(nlsat::solver, m_limit, m_params, false); m_zero = alloc(scoped_anum, am()); m_lp2nl.reset(); vector core; // add linear inequalities from lra_solver for (unsigned i = 0; i < s.constraint_count(); ++i) { add_constraint(i); } // add polynomial definitions. for (auto const& m : m_monomials) { add_monomial_eq(m); } // TBD: add variable bounds? lbool r = l_undef; try { r = m_nlsat->check(); } catch (z3_exception&) { if (m_limit.get_cancel_flag()) { r = l_undef; } else { throw; } } TRACE("arith", display(tout); m_nlsat->display(tout << r << "\n");); switch (r) { case l_true: break; case l_false: ex.reset(); m_nlsat->get_core(core); for (auto c : core) { unsigned idx = static_cast(static_cast(c) - this); ex.push_back(std::pair(rational(1), idx)); TRACE("arith", tout << "ex: " << idx << "\n";); } break; case l_undef: break; } return r; } void add_monomial_eq(mon_eq const& m) { polynomial::manager& pm = m_nlsat->pm(); svector vars; for (auto v : m.m_vs) { vars.push_back(lp2nl(v)); } polynomial::monomial_ref m1(pm.mk_monomial(vars.size(), vars.c_ptr()), pm); polynomial::monomial_ref m2(pm.mk_monomial(lp2nl(m.m_v), 1), pm); polynomial::monomial* mls[2] = { m1, m2 }; polynomial::scoped_numeral_vector coeffs(pm.m()); coeffs.push_back(mpz(1)); coeffs.push_back(mpz(-1)); polynomial::polynomial_ref p(pm.mk_polynomial(2, coeffs.c_ptr(), mls), pm); polynomial::polynomial* ps[1] = { p }; bool even[1] = { false }; nlsat::literal lit = m_nlsat->mk_ineq_literal(nlsat::atom::kind::EQ, 1, ps, even); m_nlsat->mk_clause(1, &lit, nullptr); } void add_constraint(unsigned idx) { auto& c = s.get_constraint(idx); auto& pm = m_nlsat->pm(); auto k = c.m_kind; auto rhs = c.m_right_side; auto lhs = c.get_left_side_coefficients(); auto sz = lhs.size(); svector vars; rational den = denominator(rhs); for (auto kv : lhs) { vars.push_back(lp2nl(kv.second)); den = lcm(den, denominator(kv.first)); } vector coeffs; for (auto kv : lhs) { coeffs.push_back(den * kv.first); } rhs *= den; polynomial::polynomial_ref p(pm.mk_linear(sz, coeffs.c_ptr(), vars.c_ptr(), -rhs), pm); polynomial::polynomial* ps[1] = { p }; bool is_even[1] = { false }; nlsat::literal lit; nlsat::assumption a = this + idx; switch (k) { case lp::lconstraint_kind::LE: lit = ~m_nlsat->mk_ineq_literal(nlsat::atom::kind::GT, 1, ps, is_even); break; case lp::lconstraint_kind::GE: lit = ~m_nlsat->mk_ineq_literal(nlsat::atom::kind::LT, 1, ps, is_even); break; case lp::lconstraint_kind::LT: lit = m_nlsat->mk_ineq_literal(nlsat::atom::kind::LT, 1, ps, is_even); break; case lp::lconstraint_kind::GT: lit = m_nlsat->mk_ineq_literal(nlsat::atom::kind::GT, 1, ps, is_even); break; case lp::lconstraint_kind::EQ: lit = m_nlsat->mk_ineq_literal(nlsat::atom::kind::EQ, 1, ps, is_even); break; } m_nlsat->mk_clause(1, &lit, a); } bool is_int(lp::var_index v) { return s.var_is_int(v); } polynomial::var lp2nl(lp::var_index v) { polynomial::var r; if (!m_lp2nl.find(v, r)) { r = m_nlsat->mk_var(is_int(v)); m_lp2nl.insert(v, r); TRACE("arith", tout << "v" << v << " := x" << r << "\n";); } return r; } nlsat::anum const& value(lp::var_index v) const { polynomial::var pv; if (m_lp2nl.find(v, pv)) return m_nlsat->value(pv); else return *m_zero; } nlsat::anum_manager& am() { return m_nlsat->am(); } std::ostream& display(std::ostream& out) const { for (auto m : m_monomials) { out << "v" << m.m_v << " = "; for (auto v : m.m_vs) { out << "v" << v << " "; } out << "\n"; } return out; } }; solver::solver(lp::lar_solver& s, reslimit& lim, params_ref const& p) { m_imp = alloc(imp, s, lim, p); } solver::~solver() { dealloc(m_imp); } void solver::add_monomial(lp::var_index v, unsigned sz, lp::var_index const* vs) { m_imp->add(v, sz, vs); } lbool solver::check(lp::explanation_t& ex) { return m_imp->check(ex); } bool solver::need_check() { return m_imp->need_check(); } void solver::push() { m_imp->push(); } void solver::pop(unsigned n) { m_imp->pop(n); } std::ostream& solver::display(std::ostream& out) const { return m_imp->display(out); } nlsat::anum const& solver::value(lp::var_index v) const { return m_imp->value(v); } nlsat::anum_manager& solver::am() { return m_imp->am(); } } z3-z3-4.8.7/src/util/lp/nra_solver.h000066400000000000000000000026701356505360400171360ustar00rootroot00000000000000/* Copyright (c) 2017 Microsoft Corporation Author: Nikolaj Bjorner */ #pragma once #include "util/vector.h" #include "util/lp/lp_settings.h" #include "util/rlimit.h" #include "util/params.h" #include "nlsat/nlsat_solver.h" namespace lp { class lar_solver; } namespace nra { class solver { struct imp; imp* m_imp; public: solver(lp::lar_solver& s, reslimit& lim, params_ref const& p = params_ref()); ~solver(); /* \brief Add a definition v = vs[0]*vs[1]*...*vs[sz-1] The variable v is equal to the product of variables vs. */ void add_monomial(lp::var_index v, unsigned sz, lp::var_index const* vs); /* \brief Check feasiblity of linear constraints augmented by polynomial definitions that are added. */ lbool check(lp::explanation_t& ex); /* \brief determine whether nra check is needed. */ bool need_check(); /* \brief Access model. */ nlsat::anum const& value(lp::var_index v) const; nlsat::anum_manager& am(); /* \brief push and pop scope. Monomial definitions are retraced when popping scope. */ void push(); void pop(unsigned n); /* \brief display state */ std::ostream& display(std::ostream& out) const; }; } z3-z3-4.8.7/src/util/lp/numeric_pair.h000066400000000000000000000312171356505360400174400ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #pragma once #define lp_for_z3 #include #include #include #ifdef lp_for_z3 #include "../rational.h" #include "../sstream.h" #include "../z3_exception.h" #else // include "util/numerics/mpq.h" // include "util/numerics/numeric_traits.h" #endif namespace lp { #ifdef lp_for_z3 // rename rationals typedef rational mpq; #else typedef lp::mpq mpq; #endif template std::string T_to_string(const T & t); // forward definition #ifdef lp_for_z3 template class numeric_traits {}; template <> class numeric_traits { public: static bool precise() { return true; } static unsigned zero() { return 0; } static unsigned one() { return 1; } static bool is_zero(unsigned v) { return v == 0; } static double get_double(unsigned const & d) { return d; } static bool is_int(unsigned) {return true;} static bool is_pos(unsigned) {return true;} }; template <> class numeric_traits { public: static bool precise() { return true; } static int zero() { return 0; } static int one() { return 1; } static bool is_zero(int v) { return v == 0; } static double get_double(int const & d) { return d; } static bool is_int(int) {return true;} static bool is_pos(int j) {return j > 0;} static bool is_neg(int j) {return j < 0;} static int ceil_ratio(int a, int b) { return static_cast(ceil(mpq(a, b)).get_int32());} static int floor_ratio(int a, int b) { return static_cast(floor(mpq(a, b)).get_int32());} }; template <> class numeric_traits { public: static bool precise() { return false; } static double g_zero; static double const &zero() { return g_zero; } static double g_one; static double const &one() { return g_one; } static bool is_zero(double v) { return v == 0.0; } static double const & get_double(double const & d) { return d;} static double log(double const & d) { NOT_IMPLEMENTED_YET(); return d;} static double from_string(std::string const & str) { return atof(str.c_str()); } static bool is_pos(const double & d) {return d > 0.0;} static bool is_neg(const double & d) {return d < 0.0;} }; template<> class numeric_traits { public: static bool precise() { return true; } static rational const & zero() { return rational::zero(); } static rational const & one() { return rational::one(); } static bool is_zero(const rational & v) { return v.is_zero(); } static double get_double(const rational & d) { return d.get_double();} static rational log(rational const& r) { UNREACHABLE(); return r; } static rational from_string(std::string const & str) { return rational(str.c_str()); } static bool is_pos(const rational & d) {return d.is_pos();} static bool is_neg(const rational & d) {return d.is_neg();} static bool is_int(const rational & d) {return d.is_int();} static mpq ceil_ratio(const mpq & a, const mpq & b) { return ceil(a / b); } static mpq floor_ratio(const mpq & a, const mpq & b) { return floor(a / b); } }; #endif template struct convert_struct { static X convert(const Y & y){ return X(y);} static bool is_epsilon_small(const X & x, const double & y) { return std::abs(numeric_traits::get_double(x)) < y; } static bool below_bound_numeric(const X &, const X &, const Y &) { /*lp_unreachable();*/ return false;} static bool above_bound_numeric(const X &, const X &, const Y &) { /*lp_unreachable();*/ return false; } }; template <> struct convert_struct { static double convert(const mpq & q) {return q.get_double();} }; template <> struct convert_struct { static mpq convert(unsigned q) {return mpq(q);} }; template struct numeric_pair { T x; T y; // empty constructor numeric_pair() {} // another constructor numeric_pair(T xp, T yp) : x(xp), y(yp) {} template numeric_pair(const X & n) : x(n), y(0) { } numeric_pair(const numeric_pair & n) : x(n.x), y(n.y) {} template numeric_pair(X xp, Y yp) : x(convert_struct::convert(xp)), y(convert_struct::convert(yp)) {} bool operator<(const numeric_pair& a) const { return x < a.x || (x == a.x && y < a.y); } bool operator>(const numeric_pair& a) const { return x > a.x || (x == a.x && y > a.y); } bool operator==(const numeric_pair& a) const { return a.x == x && a.y == y; } bool operator!=(const numeric_pair& a) const { return !(*this == a); } bool operator<=(const numeric_pair& a) const { return *this < a || *this == a; } bool operator>=(const numeric_pair& a) const { return *this > a || a == *this; } numeric_pair operator*(const T & a) const { return numeric_pair(a * x, a * y); } numeric_pair operator/(const T & a) const { T a_as_T(a); return numeric_pair(x / a_as_T, y / a_as_T); } numeric_pair operator/(const numeric_pair &) const { // lp_unreachable(); } numeric_pair operator+(const numeric_pair & a) const { return numeric_pair(a.x + x, a.y + y); } numeric_pair operator*(const numeric_pair & /*a*/) const { // lp_unreachable(); } numeric_pair& operator+=(const numeric_pair & a) { x += a.x; y += a.y; return *this; } numeric_pair& operator-=(const numeric_pair & a) { x -= a.x; y -= a.y; return *this; } numeric_pair& operator/=(const T & a) { x /= a; y /= a; return *this; } numeric_pair& operator*=(const T & a) { x *= a; y *= a; return *this; } numeric_pair operator-(const numeric_pair & a) const { return numeric_pair(x - a.x, y - a.y); } numeric_pair operator-() const { return numeric_pair(-x, -y); } static bool precize() { return lp::numeric_traits::precize();} bool is_zero() const { return x.is_zero() && y.is_zero(); } bool is_pos() const { return x.is_pos() || (x.is_zero() && y.is_pos());} bool is_neg() const { return x.is_neg() || (x.is_zero() && y.is_neg());} std::string to_string() const { return std::string("(") + T_to_string(x) + ", " + T_to_string(y) + ")"; } bool is_int() const { return x.is_int() && y.is_zero(); } }; template std::ostream& operator<<(std::ostream& os, numeric_pair const & obj) { os << obj.to_string(); return os; } template numeric_pair operator*(const X & a, const numeric_pair & r) { return numeric_pair(a * r.x, a * r.y); } template numeric_pair operator*(const numeric_pair & r, const X & a) { return numeric_pair(a * r.x, a * r.y); } template numeric_pair operator/(const numeric_pair & r, const X & a) { return numeric_pair(r.x / a, r.y / a); } // template bool precise() { return numeric_traits::precise();} template double get_double(const lp::numeric_pair & ) { /* lp_unreachable(); */ return 0;} template class numeric_traits> { public: static bool precise() { return numeric_traits::precise();} static lp::numeric_pair zero() { return lp::numeric_pair(numeric_traits::zero(), numeric_traits::zero()); } static bool is_zero(const lp::numeric_pair & v) { return numeric_traits::is_zero(v.x) && numeric_traits::is_zero(v.y); } static double get_double(const lp::numeric_pair & v){ return numeric_traits::get_double(v.x); } // just return the double of the first coordinate static double one() { /*lp_unreachable();*/ return 0;} static bool is_pos(const numeric_pair &p) { return numeric_traits::is_pos(p.x) || (numeric_traits::is_zero(p.x) && numeric_traits::is_pos(p.y)); } static bool is_neg(const numeric_pair &p) { return numeric_traits::is_neg(p.x) || (numeric_traits::is_zero(p.x) && numeric_traits::is_neg(p.y)); } static bool is_int(const numeric_pair & p) { return numeric_traits::is_int(p.x) && numeric_traits::is_zero(p.y); } }; template <> struct convert_struct> { static double convert(const numeric_pair & q) {return q.x;} }; typedef numeric_pair impq; template bool is_epsilon_small(const X & v, const double& eps); // forward definition { return convert_struct::is_epsilon_small(v, eps);} template struct convert_struct, double> { static numeric_pair convert(const double & q) { return numeric_pair(convert_struct::convert(q), numeric_traits::zero()); } static bool is_epsilon_small(const numeric_pair & p, const double & eps) { return convert_struct::is_epsilon_small(p.x, eps) && convert_struct::is_epsilon_small(p.y, eps); } static bool below_bound_numeric(const numeric_pair &, const numeric_pair &, const double &) { // lp_unreachable(); return false; } static bool above_bound_numeric(const numeric_pair &, const numeric_pair &, const double &) { // lp_unreachable(); return false; } }; template <> struct convert_struct, double> { static numeric_pair convert(const double & q) { return numeric_pair(q, 0.0); } static bool is_epsilon_small(const numeric_pair & p, const double & eps) { return std::abs(p.x) < eps && std::abs(p.y) < eps; } static int compare_on_coord(const double & x, const double & bound, const double eps) { if (bound == 0) return (x < - eps)? -1: (x > eps? 1 : 0); // it is an important special case double relative = (bound > 0)? - eps: eps; return (x < bound * (1.0 + relative) - eps)? -1 : ((x > bound * (1.0 - relative) + eps)? 1 : 0); } static bool below_bound_numeric(const numeric_pair & x, const numeric_pair & bound, const double & eps) { int r = compare_on_coord(x.x, bound.x, eps); if (r == 1) return false; if (r == -1) return true; // the first coordinates are almost the same return compare_on_coord(x.y, bound.y, eps) == -1; } static bool above_bound_numeric(const numeric_pair & x, const numeric_pair & bound, const double & eps) { int r = compare_on_coord(x.x, bound.x, eps); if (r == -1) return false; if (r == 1) return true; // the first coordinates are almost the same return compare_on_coord(x.y, bound.y, eps) == 1; } }; template <> struct convert_struct { static bool is_epsilon_small(const double& x, const double & eps) { return x < eps && x > -eps; } static double convert(const double & y){ return y;} static bool below_bound_numeric(const double & x, const double & bound, const double & eps) { if (bound == 0) return x < - eps; double relative = (bound > 0)? - eps: eps; return x < bound * (1.0 + relative) - eps; } static bool above_bound_numeric(const double & x, const double & bound, const double & eps) { if (bound == 0) return x > eps; double relative = (bound > 0)? eps: - eps; return x > bound * (1.0 + relative) + eps; } }; template bool is_epsilon_small(const X & v, const double &eps) { return convert_struct::is_epsilon_small(v, eps);} template bool below_bound_numeric(const X & x, const X & bound, const double& eps) { return convert_struct::below_bound_numeric(x, bound, eps);} template bool above_bound_numeric(const X & x, const X & bound, const double& eps) { return convert_struct::above_bound_numeric(x, bound, eps);} template T floor(const numeric_pair & r) { if (r.x.is_int()) { if (r.y.is_nonneg()) { return r.x; } return r.x - mpq::one(); } return floor(r.x); } template T ceil(const numeric_pair & r) { if (r.x.is_int()) { if (r.y.is_nonpos()) { return r.x; } return r.x + mpq::one(); } return ceil(r.x); } } z3-z3-4.8.7/src/util/lp/permutation_matrix.cpp000066400000000000000000000130071356505360400212460ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #include #include "util/vector.h" #include "util/lp/permutation_matrix_def.h" #include "util/lp/numeric_pair.h" template void lp::permutation_matrix::apply_from_right(vector&); template void lp::permutation_matrix::init(unsigned int); template void lp::permutation_matrix::init(unsigned int); template void lp::permutation_matrix>::init(unsigned int); template bool lp::permutation_matrix::is_identity() const; template void lp::permutation_matrix::multiply_by_permutation_from_left(lp::permutation_matrix&); template void lp::permutation_matrix::multiply_by_permutation_reverse_from_left(lp::permutation_matrix&); template void lp::permutation_matrix::multiply_by_reverse_from_right(lp::permutation_matrix&); template lp::permutation_matrix::permutation_matrix(unsigned int, vector const&); template void lp::permutation_matrix::transpose_from_left(unsigned int, unsigned int); template void lp::permutation_matrix::apply_from_right(vector&); template bool lp::permutation_matrix::is_identity() const; template void lp::permutation_matrix::multiply_by_permutation_from_left(lp::permutation_matrix&); template void lp::permutation_matrix::multiply_by_permutation_from_right(lp::permutation_matrix&); template void lp::permutation_matrix::multiply_by_permutation_reverse_from_left(lp::permutation_matrix&); template void lp::permutation_matrix::multiply_by_reverse_from_right(lp::permutation_matrix&); template lp::permutation_matrix::permutation_matrix(unsigned int); template void lp::permutation_matrix::transpose_from_left(unsigned int, unsigned int); template void lp::permutation_matrix::transpose_from_right(unsigned int, unsigned int); template void lp::permutation_matrix >::apply_from_right(vector&); template bool lp::permutation_matrix >::is_identity() const; template void lp::permutation_matrix >::multiply_by_permutation_from_left(lp::permutation_matrix >&); template void lp::permutation_matrix >::multiply_by_permutation_from_right(lp::permutation_matrix >&); template void lp::permutation_matrix >::multiply_by_permutation_reverse_from_left(lp::permutation_matrix >&); template void lp::permutation_matrix >::multiply_by_reverse_from_right(lp::permutation_matrix >&); template lp::permutation_matrix >::permutation_matrix(unsigned int); template void lp::permutation_matrix >::transpose_from_left(unsigned int, unsigned int); template void lp::permutation_matrix >::transpose_from_right(unsigned int, unsigned int); template void lp::permutation_matrix::apply_reverse_from_left(lp::indexed_vector&); template void lp::permutation_matrix::apply_reverse_from_left_to_T(vector&); template void lp::permutation_matrix::apply_reverse_from_right_to_T(vector&); template void lp::permutation_matrix::transpose_from_right(unsigned int, unsigned int); template void lp::permutation_matrix::apply_reverse_from_left(lp::indexed_vector&); template void lp::permutation_matrix::apply_reverse_from_left_to_T(vector&); template void lp::permutation_matrix::apply_reverse_from_right_to_T(vector&); template void lp::permutation_matrix >::apply_reverse_from_left(lp::indexed_vector&); template void lp::permutation_matrix >::apply_reverse_from_left_to_T(vector&); template void lp::permutation_matrix >::apply_reverse_from_right_to_T(vector&); template void lp::permutation_matrix::multiply_by_permutation_from_right(lp::permutation_matrix&); template lp::permutation_matrix::permutation_matrix(unsigned int); template void lp::permutation_matrix::apply_reverse_from_left_to_X(vector &); template void lp::permutation_matrix< lp::mpq, lp::mpq>::apply_reverse_from_left_to_X(vector &); template void lp::permutation_matrix< lp::mpq, lp::numeric_pair< lp::mpq> >::apply_reverse_from_left_to_X(vector> &); template void lp::permutation_matrix::apply_reverse_from_right_to_T(lp::indexed_vector&); template void lp::permutation_matrix::apply_reverse_from_right_to_T(lp::indexed_vector&); template void lp::permutation_matrix >::apply_reverse_from_right_to_T(lp::indexed_vector&); z3-z3-4.8.7/src/util/lp/permutation_matrix.h000066400000000000000000000107431356505360400207170ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #pragma once #include "util/vector.h" #include #include "util/debug.h" #include #include "util/lp/sparse_vector.h" #include "util/lp/indexed_vector.h" #include "util/lp/lp_settings.h" #include "util/lp/matrix.h" #include "util/lp/tail_matrix.h" namespace lp { #ifdef Z3DEBUG inline bool is_even(int k) { return (k/2)*2 == k; } #endif template class permutation_matrix : public tail_matrix { vector m_permutation; vector m_rev; vector m_work_array; vector m_T_buffer; vector m_X_buffer; class ref { permutation_matrix & m_p; unsigned m_i; public: ref(permutation_matrix & m, unsigned i):m_p(m), m_i(i) {} ref & operator=(unsigned v) { m_p.set_val(m_i, v); return *this; } ref & operator=(ref const & v) { m_p.set_val(m_i, v.m_p.m_permutation[v.m_i]); return *this; } operator unsigned & () const { return m_p.m_permutation[m_i]; } }; public: permutation_matrix() {} permutation_matrix(unsigned length); permutation_matrix(unsigned length, vector const & values); // create a unit permutation of the given length void init(unsigned length); unsigned get_rev(unsigned i) { return m_rev[i]; } bool is_dense() const override { return false; } #ifdef Z3DEBUG permutation_matrix get_inverse() const { return permutation_matrix(size(), m_rev); } void print(std::ostream & out) const; #endif ref operator[](unsigned i) { return ref(*this, i); } unsigned operator[](unsigned i) const { return m_permutation[i]; } void apply_from_left(vector & w, lp_settings &) override; void apply_from_left_to_T(indexed_vector & w, lp_settings & settings) override; void apply_from_right(vector & w) override; void apply_from_right(indexed_vector & w) override; template void copy_aside(vector & t, vector & tmp_index, indexed_vector & w); template void clear_data(indexed_vector & w); template void apply_reverse_from_left(indexed_vector & w); void apply_reverse_from_left_to_T(vector & w); void apply_reverse_from_left_to_X(vector & w); void apply_reverse_from_right_to_T(vector & w); void apply_reverse_from_right_to_T(indexed_vector & w); void apply_reverse_from_right_to_X(vector & w); void set_val(unsigned i, unsigned pi) { lp_assert(i < size() && pi < size()); m_permutation[i] = pi; m_rev[pi] = i; } void transpose_from_left(unsigned i, unsigned j); unsigned apply_reverse(unsigned i) const { return m_rev[i]; } void transpose_from_right(unsigned i, unsigned j); #ifdef Z3DEBUG T get_elem(unsigned i, unsigned j) const override { return m_permutation[i] == j? numeric_traits::one() : numeric_traits::zero(); } unsigned row_count() const override { return size(); } unsigned column_count() const override { return size(); } void set_number_of_rows(unsigned /*m*/) override { } void set_number_of_columns(unsigned /*n*/) override { } #endif void multiply_by_permutation_from_left(permutation_matrix & p); // this is multiplication in the matrix sense void multiply_by_permutation_from_right(permutation_matrix & p); void multiply_by_reverse_from_right(permutation_matrix & q); void multiply_by_permutation_reverse_from_left(permutation_matrix & r); void shrink_by_one_identity(); bool is_identity() const; unsigned size() const { return static_cast(m_rev.size()); } void resize(unsigned size) { unsigned old_size = m_permutation.size(); m_permutation.resize(size); m_rev.resize(size); m_T_buffer.resize(size); m_X_buffer.resize(size); for (unsigned i = old_size; i < size; i++) { m_permutation[i] = m_rev[i] = i; } } }; // end of the permutation class } z3-z3-4.8.7/src/util/lp/permutation_matrix_def.h000066400000000000000000000234741356505360400215420ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #include "util/vector.h" #include "util/lp/permutation_matrix.h" namespace lp { template permutation_matrix::permutation_matrix(unsigned length): m_permutation(length), m_rev(length), m_T_buffer(length), m_X_buffer(length) { for (unsigned i = 0; i < length; i++) { // do not change the direction of the loop because of the vectorization bug in clang3.3 m_permutation[i] = m_rev[i] = i; } } template permutation_matrix::permutation_matrix(unsigned length, vector const & values): m_permutation(length), m_rev(length) , m_T_buffer(length), m_X_buffer(length) { for (unsigned i = 0; i < length; i++) { set_val(i, values[i]); } } // create a unit permutation of the given length template void permutation_matrix::init(unsigned length) { m_permutation.resize(length); m_rev.resize(length); m_T_buffer.resize(length); m_X_buffer.resize(length); for (unsigned i = 0; i < length; i++) { m_permutation[i] = m_rev[i] = i; } } #ifdef Z3DEBUG template void permutation_matrix::print(std::ostream & out) const { out << "["; for (unsigned i = 0; i < size(); i++) { out << m_permutation[i]; if (i < size() - 1) { out << ","; } else { out << "]"; } } out << std::endl; } #endif template void permutation_matrix::apply_from_left(vector & w, lp_settings & ) { #ifdef Z3DEBUG // dense_matrix deb(*this); // L * deb_w = clone_vector(w, row_count()); // deb.apply_from_left(deb_w); #endif lp_assert(m_X_buffer.size() == w.size()); unsigned i = size(); while (i-- > 0) { m_X_buffer[i] = w[m_permutation[i]]; } i = size(); while (i-- > 0) { w[i] = m_X_buffer[i]; } #ifdef Z3DEBUG // lp_assert(vectors_are_equal(deb_w, w, row_count())); // delete [] deb_w; #endif } template void permutation_matrix::apply_from_left_to_T(indexed_vector & w, lp_settings & ) { vector t(w.m_index.size()); vector tmp_index(w.m_index.size()); copy_aside(t, tmp_index, w); // todo: is it too much copying clear_data(w); // set the new values for (unsigned i = static_cast(t.size()); i > 0;) { i--; unsigned j = m_rev[tmp_index[i]]; w[j] = t[i]; w.m_index[i] = j; } } template void permutation_matrix::apply_from_right(vector & w) { #ifdef Z3DEBUG // dense_matrix deb(*this); // T * deb_w = clone_vector(w, row_count()); // deb.apply_from_right(deb_w); #endif lp_assert(m_T_buffer.size() == w.size()); for (unsigned i = 0; i < size(); i++) { m_T_buffer[i] = w[m_rev[i]]; } for (unsigned i = 0; i < size(); i++) { w[i] = m_T_buffer[i]; } #ifdef Z3DEBUG // lp_assert(vectors_are_equal(deb_w, w, row_count())); // delete [] deb_w; #endif } template void permutation_matrix::apply_from_right(indexed_vector & w) { #ifdef Z3DEBUG vector wcopy(w.m_data); apply_from_right(wcopy); #endif vector buffer(w.m_index.size()); vector index_copy(w.m_index); for (unsigned i = 0; i < w.m_index.size(); i++) { buffer[i] = w.m_data[w.m_index[i]]; } w.clear(); for (unsigned i = 0; i < index_copy.size(); i++) { unsigned j = index_copy[i]; unsigned pj = m_permutation[j]; w.set_value(buffer[i], pj); } lp_assert(w.is_OK()); #ifdef Z3DEBUG lp_assert(vectors_are_equal(wcopy, w.m_data)); #endif } template template void permutation_matrix::copy_aside(vector & t, vector & tmp_index, indexed_vector & w) { for (unsigned i = static_cast(t.size()); i > 0;) { i--; unsigned j = w.m_index[i]; t[i] = w[j]; // copy aside all non-zeroes tmp_index[i] = j; // and the indices too } } template template void permutation_matrix::clear_data(indexed_vector & w) { // clear old non-zeroes for (unsigned i = static_cast(w.m_index.size()); i > 0;) { i--; unsigned j = w.m_index[i]; w[j] = zero_of_type(); } } template template void permutation_matrix::apply_reverse_from_left(indexed_vector & w) { // the result will be w = p(-1) * w #ifdef Z3DEBUG // dense_matrix deb(get_reverse()); // L * deb_w = clone_vector(w.m_data, row_count()); // deb.apply_from_left(deb_w); #endif vector t(w.m_index.size()); vector tmp_index(w.m_index.size()); copy_aside(t, tmp_index, w); clear_data(w); // set the new values for (unsigned i = static_cast(t.size()); i > 0;) { i--; unsigned j = m_permutation[tmp_index[i]]; w[j] = t[i]; w.m_index[i] = j; } #ifdef Z3DEBUG // lp_assert(vectors_are_equal(deb_w, w.m_data, row_count())); // delete [] deb_w; #endif } template void permutation_matrix::apply_reverse_from_left_to_T(vector & w) { // the result will be w = p(-1) * w lp_assert(m_T_buffer.size() == w.size()); unsigned i = size(); while (i-- > 0) { m_T_buffer[m_permutation[i]] = w[i]; } i = size(); while (i-- > 0) { w[i] = m_T_buffer[i]; } } template void permutation_matrix::apply_reverse_from_left_to_X(vector & w) { // the result will be w = p(-1) * w lp_assert(m_X_buffer.size() == w.size()); unsigned i = size(); while (i-- > 0) { m_X_buffer[m_permutation[i]] = w[i]; } i = size(); while (i-- > 0) { w[i] = m_X_buffer[i]; } } template void permutation_matrix::apply_reverse_from_right_to_T(vector & w) { // the result will be w = w * p(-1) lp_assert(m_T_buffer.size() == w.size()); unsigned i = size(); while (i-- > 0) { m_T_buffer[i] = w[m_permutation[i]]; } i = size(); while (i-- > 0) { w[i] = m_T_buffer[i]; } } template void permutation_matrix::apply_reverse_from_right_to_T(indexed_vector & w) { // the result will be w = w * p(-1) #ifdef Z3DEBUG // vector wcopy(w.m_data); // apply_reverse_from_right_to_T(wcopy); #endif lp_assert(w.is_OK()); vector tmp; vector tmp_index(w.m_index); for (auto i : w.m_index) { tmp.push_back(w[i]); } w.clear(); for (unsigned k = 0; k < tmp_index.size(); k++) { unsigned j = tmp_index[k]; w.set_value(tmp[k], m_rev[j]); } // lp_assert(w.is_OK()); // lp_assert(vectors_are_equal(w.m_data, wcopy)); } template void permutation_matrix::apply_reverse_from_right_to_X(vector & w) { // the result will be w = w * p(-1) lp_assert(m_X_buffer.size() == w.size()); unsigned i = size(); while (i-- > 0) { m_X_buffer[i] = w[m_permutation[i]]; } i = size(); while (i-- > 0) { w[i] = m_X_buffer[i]; } } template void permutation_matrix::transpose_from_left(unsigned i, unsigned j) { // the result will be this = (i,j)*this lp_assert(i < size() && j < size() && i != j); auto pi = m_rev[i]; auto pj = m_rev[j]; set_val(pi, j); set_val(pj, i); } template void permutation_matrix::transpose_from_right(unsigned i, unsigned j) { // the result will be this = this * (i,j) lp_assert(i < size() && j < size() && i != j); auto pi = m_permutation[i]; auto pj = m_permutation[j]; set_val(i, pj); set_val(j, pi); } template void permutation_matrix::multiply_by_permutation_from_left(permutation_matrix & p) { m_work_array = m_permutation; lp_assert(p.size() == size()); unsigned i = size(); while (i-- > 0) { set_val(i, m_work_array[p[i]]); // we have m(P)*m(Q) = m(QP), where m is the matrix of the permutation } } // this is multiplication in the matrix sense template void permutation_matrix::multiply_by_permutation_from_right(permutation_matrix & p) { m_work_array = m_permutation; lp_assert(p.size() == size()); unsigned i = size(); while (i-- > 0) set_val(i, p[m_work_array[i]]); // we have m(P)*m(Q) = m(QP), where m is the matrix of the permutation } template void permutation_matrix::multiply_by_reverse_from_right(permutation_matrix & q){ // todo : condensed permutations ? lp_assert(q.size() == size()); m_work_array = m_permutation; // the result is this = this*q(-1) unsigned i = size(); while (i-- > 0) { set_val(i, q.m_rev[m_work_array[i]]); // we have m(P)*m(Q) = m(QP), where m is the matrix of the permutation } } template void permutation_matrix::multiply_by_permutation_reverse_from_left(permutation_matrix & r){ // todo : condensed permutations? // the result is this = r(-1)*this m_work_array = m_permutation; // the result is this = this*q(-1) unsigned i = size(); while (i-- > 0) { set_val(i, m_work_array[r.m_rev[i]]); } } template bool permutation_matrix::is_identity() const { unsigned i = size(); while (i-- > 0) { if (m_permutation[i] != i) { return false; } } return true; } } z3-z3-4.8.7/src/util/lp/polynomial.h000066400000000000000000000055641356505360400171540ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Nikolaj Bjorner (nbjorner) Lev Nachmanson (levnach) Revision History: --*/ #pragma once namespace lp { struct polynomial { static mpq m_local_zero; // the polynomial evaluates to m_coeffs + m_a vector m_coeffs; mpq m_a; // the free coefficient polynomial(const vector& p, const mpq & a) : m_coeffs(p), m_a(a) {} polynomial(const vector& p) : polynomial(p, zero_of_type()) {} polynomial(): m_a(zero_of_type()) {} polynomial(const polynomial & p) : m_coeffs(p.m_coeffs), m_a(p.m_a) {} const mpq & coeff(var_index j) const { for (const auto & t : m_coeffs) { if (j == t.var()) { return t.coeff(); } } return m_local_zero; } polynomial & operator+=(const polynomial & p) { m_a += p.m_a; for (const auto & t: p.m_coeffs) *this += monomial(t.coeff(), t.var()); return *this; } void add(const mpq & c, const polynomial &p) { m_a += p.m_a * c; for (const auto & t: p.m_coeffs) *this += monomial(c * t.coeff(), t.var()); } void clear() { m_coeffs.clear(); m_a = zero_of_type(); } bool is_empty() const { return m_coeffs.size() == 0 && numeric_traits::is_zero(m_a); } unsigned number_of_monomials() const { return m_coeffs.size();} void add(const monomial &m ){ if (is_zero(m.coeff())) return; for (unsigned k = 0; k < m_coeffs.size(); k++) { auto & l = m_coeffs[k]; if (m.var() == l.var()) { l.coeff() += m.coeff(); if (l.coeff() == 0) m_coeffs.erase(m_coeffs.begin() + k); return; } } m_coeffs.push_back(m); lp_assert(is_correct()); } polynomial & operator+=(const monomial &m ){ add(m); return *this; } polynomial & operator+=(const mpq &c ){ m_a += c; return *this; } bool is_correct() const { std::unordered_set s; for (auto & l : m_coeffs) { if (l.coeff() == 0) return false; s.insert(l.var()); } return m_coeffs.size() == s.size(); } bool var_coeff_is_unit(unsigned j) const { const mpq & a = coeff(j); return a == 1 || a == -1; } template // c plays a role of a map from indices to impq mpq value(const c& v) const { mpq r = m_a; for (auto & p : m_coeffs) r += v[p.var()].x * p.coeff(); return r; } const vector & coeffs() const { return m_coeffs; } }; } z3-z3-4.8.7/src/util/lp/random_updater.cpp000066400000000000000000000003151356505360400203150ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #include "util/lp/random_updater_def.h" z3-z3-4.8.7/src/util/lp/random_updater.h000066400000000000000000000021731356505360400177660ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #pragma once #include #include "util/vector.h" #include #include #include #include "util/lp/lp_settings.h" // see http://research.microsoft.com/projects/z3/smt07.pdf // The class searches for a feasible solution with as many different values of variables as it can find namespace lp { template struct numeric_pair; // forward definition class lar_solver; // forward definition class random_updater { std::set m_var_set; lar_solver & m_lar_solver; unsigned m_range; void add_column_to_sets(unsigned j); bool random_shift_var(unsigned j); std::unordered_map, unsigned> m_values; // it maps a value to the number of time it occurs bool shift_var(unsigned j); void add_value(const numeric_pair& v); void remove_value(const numeric_pair & v); public: random_updater(lar_solver & solver, const vector & column_list); void update(); }; } z3-z3-4.8.7/src/util/lp/random_updater_def.h000066400000000000000000000044751356505360400206130ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #include "util/lp/random_updater.h" #include "util/lp/static_matrix.h" #include "util/lp/lar_solver.h" #include "util/vector.h" namespace lp { random_updater::random_updater( lar_solver & lar_solver, const vector & column_indices) : m_lar_solver(lar_solver), m_range(100000) { for (unsigned j : column_indices) add_column_to_sets(j); } bool random_updater::shift_var(unsigned v) { return m_lar_solver.get_int_solver()->shift_var(v, m_range); } bool random_updater::random_shift_var(unsigned j) { if (m_lar_solver.A_r().m_columns.size() >= 50) { return false; } return shift_var(j); } void random_updater::update() { for (auto j : m_var_set) { if (m_var_set.size() <= m_values.size()) { break; // we are done } auto old_x = m_lar_solver.get_column_value(j); if (random_shift_var(j)) { remove_value(old_x); add_value(m_lar_solver.get_column_value(j)); } } } void random_updater::add_value(const numeric_pair& v) { auto it = m_values.find(v); if (it == m_values.end()) { m_values[v] = 1; } else { it->second++; } } void random_updater::remove_value(const numeric_pair& v) { std::unordered_map, unsigned>::iterator it = m_values.find(v); lp_assert(it != m_values.end()); it->second--; if (it->second == 0) m_values.erase((std::unordered_map, unsigned>::const_iterator)it); } void random_updater::add_column_to_sets(unsigned j) { if (m_lar_solver.get_core_solver().m_r_heading[j] < 0) { m_var_set.insert(j); add_value(m_lar_solver.get_core_solver().m_r_x[j]); } else { unsigned row = m_lar_solver.get_core_solver().m_r_heading[j]; for (auto & row_c : m_lar_solver.get_core_solver().m_r_A.m_rows[row]) { unsigned cj = row_c.var(); if (m_lar_solver.get_core_solver().m_r_heading[cj] < 0) { m_var_set.insert(cj); add_value(m_lar_solver.get_core_solver().m_r_x[cj]); } } } } } z3-z3-4.8.7/src/util/lp/row_eta_matrix.cpp000066400000000000000000000044771356505360400203520ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #include #include "util/vector.h" #include "util/lp/row_eta_matrix_def.h" #include "util/lp/lu.h" namespace lp { template void row_eta_matrix::conjugate_by_permutation(permutation_matrix&); template void row_eta_matrix >::conjugate_by_permutation(permutation_matrix >&); template void row_eta_matrix::conjugate_by_permutation(permutation_matrix&); #ifdef Z3DEBUG template mpq row_eta_matrix::get_elem(unsigned int, unsigned int) const; template mpq row_eta_matrix >::get_elem(unsigned int, unsigned int) const; template double row_eta_matrix::get_elem(unsigned int, unsigned int) const; #endif template void row_eta_matrix::apply_from_left(vector&, lp_settings&); template void row_eta_matrix::apply_from_right(vector&); template void row_eta_matrix::apply_from_right(indexed_vector&); template void row_eta_matrix >::apply_from_left(vector>&, lp_settings&); template void row_eta_matrix >::apply_from_right(vector&); template void row_eta_matrix >::apply_from_right(indexed_vector&); template void row_eta_matrix::apply_from_left(vector&, lp_settings&); template void row_eta_matrix::apply_from_right(vector&); template void row_eta_matrix::apply_from_right(indexed_vector&); template void row_eta_matrix::apply_from_left_to_T(indexed_vector&, lp_settings&); template void row_eta_matrix::apply_from_left_local_to_T(indexed_vector&, lp_settings&); template void row_eta_matrix >::apply_from_left_to_T(indexed_vector&, lp_settings&); template void row_eta_matrix >::apply_from_left_local_to_T(indexed_vector&, lp_settings&); template void row_eta_matrix::apply_from_left_to_T(indexed_vector&, lp_settings&); template void row_eta_matrix::apply_from_left_local_to_T(indexed_vector&, lp_settings&); } z3-z3-4.8.7/src/util/lp/row_eta_matrix.h000066400000000000000000000043351356505360400200100ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #pragma once #include "util/vector.h" #include "util/debug.h" #include #include "util/lp/sparse_vector.h" #include "util/lp/indexed_vector.h" #include "util/lp/permutation_matrix.h" namespace lp { // This is the sum of a unit matrix and a lower triangular matrix // with non-zero elements only in one row template class row_eta_matrix : public tail_matrix { #ifdef Z3DEBUG unsigned m_dimension; #endif unsigned m_row_start; unsigned m_row; sparse_vector m_row_vector; public: #ifdef Z3DEBUG row_eta_matrix(unsigned row_start, unsigned row, unsigned dim): #else row_eta_matrix(unsigned row_start, unsigned row): #endif #ifdef Z3DEBUG m_dimension(dim), #endif m_row_start(row_start), m_row(row) { } bool is_dense() const override { return false; } void print(std::ostream & out) { print_matrix(*this, out); } const T & get_diagonal_element() const { return m_row_vector.m_data[m_row]; } void apply_from_left(vector & w, lp_settings &) override; void apply_from_left_local_to_T(indexed_vector & w, lp_settings & settings); void apply_from_left_local_to_X(indexed_vector & w, lp_settings & settings); void apply_from_left_to_T(indexed_vector & w, lp_settings & settings) override { apply_from_left_local_to_T(w, settings); } void push_back(unsigned row_index, T val ) { lp_assert(row_index != m_row); m_row_vector.push_back(row_index, val); } void apply_from_right(vector & w) override; void apply_from_right(indexed_vector & w) override; void conjugate_by_permutation(permutation_matrix & p); #ifdef Z3DEBUG T get_elem(unsigned row, unsigned col) const override; unsigned row_count() const override { return m_dimension; } unsigned column_count() const override { return m_dimension; } void set_number_of_rows(unsigned m) override { m_dimension = m; } void set_number_of_columns(unsigned n) override { m_dimension = n; } #endif }; // end of row_eta_matrix } z3-z3-4.8.7/src/util/lp/row_eta_matrix_def.h000066400000000000000000000126211356505360400206230ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #include "util/vector.h" #include "util/lp/row_eta_matrix.h" namespace lp { template void row_eta_matrix::apply_from_left(vector & w, lp_settings &) { // #ifdef Z3DEBUG // dense_matrix deb(*this); // auto clone_w = clone_vector(w, m_dimension); // deb.apply_from_left(clone_w, settings); // #endif auto & w_at_row = w[m_row]; for (auto & it : m_row_vector.m_data) { w_at_row += w[it.first] * it.second; } // w[m_row] = w_at_row; // #ifdef Z3DEBUG // lp_assert(vectors_are_equal(clone_w, w, m_dimension)); // delete [] clone_w; // #endif } template void row_eta_matrix::apply_from_left_local_to_T(indexed_vector & w, lp_settings & settings) { auto w_at_row = w[m_row]; bool was_zero_at_m_row = is_zero(w_at_row); for (auto & it : m_row_vector.m_data) { w_at_row += w[it.first] * it.second; } if (!settings.abs_val_is_smaller_than_drop_tolerance(w_at_row)){ if (was_zero_at_m_row) { w.m_index.push_back(m_row); } w[m_row] = w_at_row; } else if (!was_zero_at_m_row){ w[m_row] = zero_of_type(); auto it = std::find(w.m_index.begin(), w.m_index.end(), m_row); w.m_index.erase(it); } // TBD: lp_assert(check_vector_for_small_values(w, settings)); } template void row_eta_matrix::apply_from_left_local_to_X(indexed_vector & w, lp_settings & settings) { auto w_at_row = w[m_row]; bool was_zero_at_m_row = is_zero(w_at_row); for (auto & it : m_row_vector.m_data) { w_at_row += w[it.first] * it.second; } if (!settings.abs_val_is_smaller_than_drop_tolerance(w_at_row)){ if (was_zero_at_m_row) { w.m_index.push_back(m_row); } w[m_row] = w_at_row; } else if (!was_zero_at_m_row){ w[m_row] = zero_of_type(); auto it = std::find(w.m_index.begin(), w.m_index.end(), m_row); w.m_index.erase(it); } // TBD: does not compile lp_assert(check_vector_for_small_values(w, settings)); } template void row_eta_matrix::apply_from_right(vector & w) { const T & w_row = w[m_row]; if (numeric_traits::is_zero(w_row)) return; #ifdef Z3DEBUG // dense_matrix deb(*this); // auto clone_w = clone_vector(w, m_dimension); // deb.apply_from_right(clone_w); #endif for (auto & it : m_row_vector.m_data) { w[it.first] += w_row * it.second; } #ifdef Z3DEBUG // lp_assert(vectors_are_equal(clone_w, w, m_dimension)); // delete clone_w; #endif } template void row_eta_matrix::apply_from_right(indexed_vector & w) { lp_assert(w.is_OK()); const T & w_row = w[m_row]; if (numeric_traits::is_zero(w_row)) return; #ifdef Z3DEBUG // vector wcopy(w.m_data); // apply_from_right(wcopy); #endif if (numeric_traits::precise()) { for (auto & it : m_row_vector.m_data) { unsigned j = it.first; bool was_zero = numeric_traits::is_zero(w[j]); const T & v = w[j] += w_row * it.second; if (was_zero) { if (!numeric_traits::is_zero(v)) w.m_index.push_back(j); } else { if (numeric_traits::is_zero(v)) w.erase_from_index(j); } } } else { // the non precise version const double drop_eps = 1e-14; for (auto & it : m_row_vector.m_data) { unsigned j = it.first; bool was_zero = numeric_traits::is_zero(w[j]); T & v = w[j] += w_row * it.second; if (was_zero) { if (!lp_settings::is_eps_small_general(v, drop_eps)) w.m_index.push_back(j); else v = zero_of_type(); } else { if (lp_settings::is_eps_small_general(v, drop_eps)) { w.erase_from_index(j); v = zero_of_type(); } } } } #ifdef Z3DEBUG // lp_assert(vectors_are_equal(wcopy, w.m_data)); #endif } template void row_eta_matrix::conjugate_by_permutation(permutation_matrix & p) { // this = p * this * p(-1) #ifdef Z3DEBUG // auto rev = p.get_reverse(); // auto deb = ((*this) * rev); // deb = p * deb; #endif m_row = p.apply_reverse(m_row); // copy aside the column indices vector columns; for (auto & it : m_row_vector.m_data) columns.push_back(it.first); for (unsigned i = static_cast(columns.size()); i-- > 0;) m_row_vector.m_data[i].first = p.get_rev(columns[i]); #ifdef Z3DEBUG // lp_assert(deb == *this); #endif } #ifdef Z3DEBUG template T row_eta_matrix::get_elem(unsigned row, unsigned col) const { if (row == m_row){ if (col == row) { return numeric_traits::one(); } return m_row_vector[col]; } return col == row ? numeric_traits::one() : numeric_traits::zero(); } #endif } z3-z3-4.8.7/src/util/lp/scaler.cpp000066400000000000000000000004541356505360400165660ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #include "util/lp/scaler_def.h" template bool lp::scaler::scale(); template bool lp::scaler::scale(); z3-z3-4.8.7/src/util/lp/scaler.h000066400000000000000000000037651356505360400162430ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #pragma once #include "util/vector.h" #include #include #include /* printf, fopen */ #include /* exit, EXIT_FAILURE */ #include "util/lp/lp_utils.h" #include "util/lp/static_matrix.h" namespace lp { // for scaling an LP template class scaler { vector & m_b; // right side static_matrix &m_A; // the constraint matrix const T & m_scaling_minimum; const T & m_scaling_maximum; vector& m_column_scale; lp_settings & m_settings; public: // constructor scaler(vector & b, static_matrix &A, const T & scaling_minimum, const T & scaling_maximum, vector & column_scale, lp_settings & settings): m_b(b), m_A(A), m_scaling_minimum(scaling_minimum), m_scaling_maximum(scaling_maximum), m_column_scale(column_scale), m_settings(settings) { lp_assert(m_column_scale.size() == 0); m_column_scale.resize(m_A.column_count(), numeric_traits::one()); } T right_side_balance(); T get_balance() { return m_A.get_balance(); } T A_min() const; T A_max() const; T get_A_ratio() const; T get_max_ratio_on_rows() const; T get_max_ratio_on_columns() const; void scale_rows_with_geometric_mean(); void scale_columns_with_geometric_mean(); void scale_once_for_ratio(); bool scale_with_ratio(); void bring_row_maximums_to_one(); void bring_column_maximums_to_one(); void bring_rows_and_columns_maximums_to_one(); bool scale_with_log_balance(); // Returns true if and only if the scaling was successful. // It is the caller responsibility to restore the matrix bool scale(); void scale_rows(); void scale_row(unsigned i); void scale_column(unsigned i); void scale_columns(); }; } z3-z3-4.8.7/src/util/lp/scaler_def.h000066400000000000000000000200031356505360400170410ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #include #include "util/lp/scaler.h" #include "util/lp/numeric_pair.h" namespace lp { // for scaling an LP template T scaler::right_side_balance() { T ret = zero_of_type(); unsigned i = m_A.row_count(); while (i--) { T rs = abs(convert_struct::convert(m_b[i])); if (!is_zero(rs)) { numeric_traits::log(rs); ret += rs * rs; } } return ret; } template T scaler::A_min() const { T min = zero_of_type(); for (unsigned i = 0; i < m_A.row_count(); i++) { T t = m_A.get_min_abs_in_row(i); min = i == 0 ? t : std::min(t, min); } return min; } template T scaler::A_max() const { T max = zero_of_type(); for (unsigned i = 0; i < m_A.row_count(); i++) { T t = m_A.get_max_abs_in_row(i); max = i == 0? t : std::max(t, max); } return max; } template T scaler::get_A_ratio() const { T min = A_min(); T max = A_max(); lp_assert(!m_settings.abs_val_is_smaller_than_zero_tolerance(min)); T ratio = max / min; return ratio; } template T scaler::get_max_ratio_on_rows() const { T ret = T(1); unsigned i = m_A.row_count(); while (i--) { T den = m_A.get_min_abs_in_row(i); lp_assert(!m_settings.abs_val_is_smaller_than_zero_tolerance(den)); T t = m_A.get_max_abs_in_row(i)/ den; if (t > ret) ret = t; } return ret; } template T scaler::get_max_ratio_on_columns() const { T ret = T(1); unsigned i = m_A.column_count(); while (i--) { T den = m_A.get_min_abs_in_column(i); if (m_settings.abs_val_is_smaller_than_zero_tolerance(den)) continue; // got a zero column T t = m_A.get_max_abs_in_column(i)/den; if (t > ret) ret = t; } return ret; } template void scaler::scale_rows_with_geometric_mean() { unsigned i = m_A.row_count(); while (i--) { T max = m_A.get_max_abs_in_row(i); T min = m_A.get_min_abs_in_row(i); lp_assert(max > zero_of_type() && min > zero_of_type()); if (is_zero(max) || is_zero(min)) continue; T gm = T(sqrt(numeric_traits::get_double(max*min))); if (m_settings.is_eps_small_general(gm, 0.01)) { continue; } m_A.multiply_row(i, one_of_type() / gm); m_b[i] /= gm; } } template void scaler::scale_columns_with_geometric_mean() { unsigned i = m_A.column_count(); while (i--) { T max = m_A.get_max_abs_in_column(i); T min = m_A.get_min_abs_in_column(i); T den = T(sqrt(numeric_traits::get_double(max*min))); if (m_settings.is_eps_small_general(den, 0.01)) continue; // got a zero column T gm = T(1)/ den; T cs = m_column_scale[i] * gm; if (m_settings.is_eps_small_general(cs, 0.1)) continue; m_A.multiply_column(i, gm); m_column_scale[i] = cs; } } template void scaler::scale_once_for_ratio() { T max_ratio_on_rows = get_max_ratio_on_rows(); T max_ratio_on_columns = get_max_ratio_on_columns(); bool scale_rows_first = max_ratio_on_rows > max_ratio_on_columns; // if max_ratio_on_columns is the largest then the rows are in worse shape than columns if (scale_rows_first) { scale_rows_with_geometric_mean(); scale_columns_with_geometric_mean(); } else { scale_columns_with_geometric_mean(); scale_rows_with_geometric_mean(); } } template bool scaler::scale_with_ratio() { T ratio = get_A_ratio(); // The ratio is greater than or equal to one. We would like to diminish it and bring it as close to 1 as possible unsigned reps = m_settings.reps_in_scaler; do { scale_once_for_ratio(); T new_r = get_A_ratio(); if (new_r >= T(0.9) * ratio) break; } while (reps--); bring_rows_and_columns_maximums_to_one(); return true; } template void scaler::bring_row_maximums_to_one() { unsigned i = m_A.row_count(); while (i--) { T t = m_A.get_max_abs_in_row(i); if (m_settings.abs_val_is_smaller_than_zero_tolerance(t)) continue; m_A.multiply_row(i, one_of_type() / t); m_b[i] /= t; } } template void scaler::bring_column_maximums_to_one() { unsigned i = m_A.column_count(); while (i--) { T max = m_A.get_max_abs_in_column(i); if (m_settings.abs_val_is_smaller_than_zero_tolerance(max)) continue; T t = T(1) / max; m_A.multiply_column(i, t); m_column_scale[i] *= t; } } template void scaler::bring_rows_and_columns_maximums_to_one() { if (get_max_ratio_on_rows() > get_max_ratio_on_columns()) { bring_row_maximums_to_one(); bring_column_maximums_to_one(); } else { bring_column_maximums_to_one(); bring_row_maximums_to_one(); } } template bool scaler::scale_with_log_balance() { T balance = get_balance(); T balance_before_scaling = balance; // todo : analyze the scale order : rows-columns, or columns-rows. Iterate if needed for (int i = 0; i < 10; i++) { scale_rows(); scale_columns(); T nb = get_balance(); if (nb < T(0.9) * balance) { balance = nb; } else { balance = nb; break; } } return balance <= balance_before_scaling; } // Returns true if and only if the scaling was successful. // It is the caller responsibility to restore the matrix template bool scaler::scale() { if (numeric_traits::precise()) return true; if (m_settings.scale_with_ratio) return scale_with_ratio(); return scale_with_log_balance(); } template void scaler::scale_rows() { for (unsigned i = 0; i < m_A.row_count(); i++) scale_row(i); } template void scaler::scale_row(unsigned i) { T row_max = std::max(m_A.get_max_abs_in_row(i), abs(convert_struct::convert(m_b[i]))); T alpha = numeric_traits::one(); if (numeric_traits::is_zero(row_max)) { return; } if (row_max < m_scaling_minimum) { do { alpha *= T(2); row_max *= T(2); } while (row_max < m_scaling_minimum); m_A.multiply_row(i, alpha); m_b[i] *= alpha; } else if (row_max > m_scaling_maximum) { do { alpha /= T(2); row_max /= T(2); } while (row_max > m_scaling_maximum); m_A.multiply_row(i, alpha); m_b[i] *= alpha; } } template void scaler::scale_column(unsigned i) { T column_max = m_A.get_max_abs_in_column(i); T alpha = numeric_traits::one(); if (numeric_traits::is_zero(column_max)){ return; // the column has zeros only } if (column_max < m_scaling_minimum) { do { alpha *= T(2); column_max *= T(2); } while (column_max < m_scaling_minimum); } else if (column_max > m_scaling_maximum) { do { alpha /= T(2); column_max /= T(2); } while (column_max > m_scaling_maximum); } else { return; } m_A.multiply_column(i, alpha); m_column_scale[i] = alpha; } template void scaler::scale_columns() { for (unsigned i = 0; i < m_A.column_count(); i++) { scale_column(i); } } } z3-z3-4.8.7/src/util/lp/signature_bound_evidence.h000066400000000000000000000012721356505360400220130ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #pragma once #include "util/lp/lp_settings.h" #include "util/lp/lar_constraints.h" namespace lp { struct bound_signature { unsigned m_i; bool m_at_low; bound_signature(unsigned i, bool at_low) :m_i(i), m_at_low(m_at_low) {} bool at_upper_bound() const { return !m_at_lower_bound;} bool at_lower_bound() const { return m_at_low;} }; template struct signature_bound_evidence { vector m_evidence; unsigned m_j; // found new bound bool m_lower_bound; X m_bound; }; } z3-z3-4.8.7/src/util/lp/sparse_vector.h000066400000000000000000000016631356505360400176440ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #pragma once #include "util/vector.h" #include #include "util/debug.h" #include "util/lp/lp_utils.h" #include "util/lp/lp_settings.h" namespace lp { template class sparse_vector { public: vector> m_data; void push_back(unsigned index, T val) { m_data.push_back(std::make_pair(index, val)); } #ifdef Z3DEBUG T operator[] (unsigned i) const { for (auto &t : m_data) { if (t.first == i) return t.second; } return numeric_traits::zero(); } #endif void divide(T const & a) { lp_assert(!lp_settings::is_eps_small_general(a, 1e-12)); for (auto & t : m_data) { t.second /= a; } } unsigned size() const { return m_data.size(); } }; } z3-z3-4.8.7/src/util/lp/square_dense_submatrix.cpp000066400000000000000000000055731356505360400221000ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #include #include "util/vector.h" #include "util/lp/square_dense_submatrix_def.h" template void lp::square_dense_submatrix::init(lp::square_sparse_matrix*, unsigned int); template lp::square_dense_submatrix::square_dense_submatrix(lp::square_sparse_matrix*, unsigned int); template void lp::square_dense_submatrix::update_parent_matrix(lp::lp_settings&); template bool lp::square_dense_submatrix::is_L_matrix() const; template void lp::square_dense_submatrix::conjugate_by_permutation(lp::permutation_matrix&); template int lp::square_dense_submatrix::find_pivot_column_in_row(unsigned int) const; template void lp::square_dense_submatrix::pivot(unsigned int, lp::lp_settings&); template lp::square_dense_submatrix >::square_dense_submatrix(lp::square_sparse_matrix >*, unsigned int); template void lp::square_dense_submatrix >::update_parent_matrix(lp::lp_settings&); template bool lp::square_dense_submatrix >::is_L_matrix() const; template void lp::square_dense_submatrix >::conjugate_by_permutation(lp::permutation_matrix >&); template int lp::square_dense_submatrix >::find_pivot_column_in_row(unsigned int) const; template void lp::square_dense_submatrix >::pivot(unsigned int, lp::lp_settings&); #ifdef Z3DEBUG template double lp::square_dense_submatrix::get_elem(unsigned int, unsigned int) const; #endif template void lp::square_dense_submatrix::apply_from_right(vector&); template void lp::square_dense_submatrix::apply_from_left_local(lp::indexed_vector&, lp::lp_settings&); template void lp::square_dense_submatrix::apply_from_left_to_vector(vector&); template lp::square_dense_submatrix::square_dense_submatrix(lp::square_sparse_matrix*, unsigned int); template void lp::square_dense_submatrix::update_parent_matrix(lp::lp_settings&); template bool lp::square_dense_submatrix::is_L_matrix() const; template void lp::square_dense_submatrix::conjugate_by_permutation(lp::permutation_matrix&); template int lp::square_dense_submatrix::find_pivot_column_in_row(unsigned int) const; template void lp::square_dense_submatrix::pivot(unsigned int, lp::lp_settings&); z3-z3-4.8.7/src/util/lp/square_dense_submatrix.h000066400000000000000000000157601356505360400215440ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #pragma once #include "util/vector.h" #include "util/lp/permutation_matrix.h" #include #include "util/lp/static_matrix.h" #include #include #include #include #include #include "util/lp/indexed_value.h" #include "util/lp/indexed_vector.h" #include #include "util/lp/lp_settings.h" #include "util/lp/eta_matrix.h" #include "util/lp/binary_heap_upair_queue.h" #include "util/lp/square_sparse_matrix.h" namespace lp { template class square_dense_submatrix : public tail_matrix { // the submatrix uses the permutations of the parent matrix to access the elements struct ref { unsigned m_i_offset; square_dense_submatrix & m_s; ref(unsigned i, square_dense_submatrix & s) : m_i_offset((i - s.m_index_start) * s.m_dim), m_s(s){} T & operator[] (unsigned j) { lp_assert(j >= m_s.m_index_start); return m_s.m_v[m_i_offset + m_s.adjust_column(j) - m_s.m_index_start]; } const T & operator[] (unsigned j) const { lp_assert(j >= m_s.m_index_start); return m_s.m_v[m_i_offset + m_s.adjust_column(j) - m_s.m_index_start]; } }; public: unsigned m_index_start; unsigned m_dim; vector m_v; square_sparse_matrix * m_parent; permutation_matrix m_row_permutation; indexed_vector m_work_vector; public: permutation_matrix m_column_permutation; bool is_active() const { return m_parent != nullptr; } square_dense_submatrix() {} square_dense_submatrix (square_sparse_matrix *parent_matrix, unsigned index_start); void init(square_sparse_matrix *parent_matrix, unsigned index_start); bool is_dense() const override { return true; } ref operator[] (unsigned i) { lp_assert(i >= m_index_start); lp_assert(i < m_parent->dimension()); return ref(i, *this); } int find_pivot_column_in_row(unsigned i) const; void swap_columns(unsigned i, unsigned j) { if (i != j) m_column_permutation.transpose_from_left(i, j); } unsigned adjust_column(unsigned col) const{ if (col >= m_column_permutation.size()) return col; return m_column_permutation.apply_reverse(col); } unsigned adjust_column_inverse(unsigned col) const{ if (col >= m_column_permutation.size()) return col; return m_column_permutation[col]; } unsigned adjust_row(unsigned row) const{ if (row >= m_row_permutation.size()) return row; return m_row_permutation[row]; } unsigned adjust_row_inverse(unsigned row) const{ if (row >= m_row_permutation.size()) return row; return m_row_permutation.apply_reverse(row); } void pivot(unsigned i, lp_settings & settings); void pivot_row_to_row(unsigned i, unsigned row, lp_settings & settings);; void divide_row_by_pivot(unsigned i); void update_parent_matrix(lp_settings & settings); void update_existing_or_delete_in_parent_matrix_for_row(unsigned i, lp_settings & settings); void push_new_elements_to_parent_matrix(lp_settings & settings); template L row_by_vector_product(unsigned i, const vector & v); template L column_by_vector_product(unsigned j, const vector & v); template L row_by_indexed_vector_product(unsigned i, const indexed_vector & v); template void apply_from_left_local(indexed_vector & w, lp_settings & settings); template void apply_from_left_to_vector(vector & w); bool is_L_matrix() const; void apply_from_left_to_T(indexed_vector & w, lp_settings & settings) override { apply_from_left_local(w, settings); } void apply_from_right(indexed_vector & w) override { #if 1==0 indexed_vector wcopy = w; apply_from_right(wcopy.m_data); wcopy.m_index.clear(); if (numeric_traits::precise()) { for (unsigned i = 0; i < m_parent->dimension(); i++) { if (!is_zero(wcopy.m_data[i])) wcopy.m_index.push_back(i); } } else { for (unsigned i = 0; i < m_parent->dimension(); i++) { T & v = wcopy.m_data[i]; if (!lp_settings::is_eps_small_general(v, 1e-14)){ wcopy.m_index.push_back(i); } else { v = zero_of_type(); } } } lp_assert(wcopy.is_OK()); apply_from_right(w.m_data); w.m_index.clear(); if (numeric_traits::precise()) { for (unsigned i = 0; i < m_parent->dimension(); i++) { if (!is_zero(w.m_data[i])) w.m_index.push_back(i); } } else { for (unsigned i = 0; i < m_parent->dimension(); i++) { T & v = w.m_data[i]; if (!lp_settings::is_eps_small_general(v, 1e-14)){ w.m_index.push_back(i); } else { v = zero_of_type(); } } } #else lp_assert(w.is_OK()); lp_assert(m_work_vector.is_OK()); m_work_vector.resize(w.data_size()); m_work_vector.clear(); lp_assert(m_work_vector.is_OK()); unsigned end = m_index_start + m_dim; for (unsigned k : w.m_index) { // find j such that k = adjust_row_inverse(j) unsigned j = adjust_row(k); if (j < m_index_start || j >= end) { m_work_vector.set_value(w[k], adjust_column_inverse(j)); } else { // j >= m_index_start and j < end unsigned offset = (j - m_index_start) * m_dim; // this is the row start const T& wv = w[k]; for (unsigned col = m_index_start; col < end; col++, offset ++) { unsigned adj_col = adjust_column_inverse(col); m_work_vector.add_value_at_index(adj_col, m_v[offset] * wv); } } } m_work_vector.clean_up(); lp_assert(m_work_vector.is_OK()); w = m_work_vector; #endif } void apply_from_left(vector & w, lp_settings & /*settings*/) override { apply_from_left_to_vector(w);// , settings); } void apply_from_right(vector & w) override; #ifdef Z3DEBUG T get_elem (unsigned i, unsigned j) const override; unsigned row_count() const override { return m_parent->row_count();} unsigned column_count() const override { return row_count();} void set_number_of_rows(unsigned) override {} void set_number_of_columns(unsigned) override {} #endif void conjugate_by_permutation(permutation_matrix & q); }; } z3-z3-4.8.7/src/util/lp/square_dense_submatrix_def.h000066400000000000000000000316261356505360400223610ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #include "util/vector.h" #include "util/lp/square_dense_submatrix.h" namespace lp { template square_dense_submatrix::square_dense_submatrix (square_sparse_matrix *parent_matrix, unsigned index_start) : m_index_start(index_start), m_dim(parent_matrix->dimension() - index_start), m_v(m_dim * m_dim), m_parent(parent_matrix), m_row_permutation(m_parent->dimension()), m_column_permutation(m_parent->dimension()) { int row_offset = - static_cast(m_index_start); for (unsigned i = index_start; i < parent_matrix->dimension(); i++) { unsigned row = parent_matrix->adjust_row(i); for (auto & iv : parent_matrix->get_row_values(row)) { unsigned j = parent_matrix->adjust_column_inverse(iv.m_index); lp_assert(j>= m_index_start); m_v[row_offset + j] = iv.m_value; } row_offset += m_dim; } } template void square_dense_submatrix::init(square_sparse_matrix *parent_matrix, unsigned index_start) { m_index_start = index_start; m_dim = parent_matrix->dimension() - index_start; m_v.resize(m_dim * m_dim); m_parent = parent_matrix; m_column_permutation.init(m_parent->dimension()); for (unsigned i = index_start; i < parent_matrix->dimension(); i++) { unsigned row = parent_matrix->adjust_row(i); for (auto & iv : parent_matrix->get_row_values(row)) { unsigned j = parent_matrix->adjust_column_inverse(iv.m_index); (*this)[i][j] = iv.m_value; } } } template int square_dense_submatrix::find_pivot_column_in_row(unsigned i) const { int j = -1; T max = zero_of_type(); lp_assert(i >= m_index_start); unsigned row_start = (i - m_index_start) * m_dim; for (unsigned k = i; k < m_parent->dimension(); k++) { unsigned col = adjust_column(k); // this is where the column is in the row unsigned offs = row_start + col - m_index_start; T t = abs(m_v[offs]); if (t > max) { j = k; max = t; } } return j; } template void square_dense_submatrix::pivot(unsigned i, lp_settings & settings) { divide_row_by_pivot(i); for (unsigned k = i + 1; k < m_parent->dimension(); k++) pivot_row_to_row(i, k, settings); } template void square_dense_submatrix::pivot_row_to_row(unsigned i, unsigned row, lp_settings & settings) { lp_assert(i < row); unsigned pj = adjust_column(i); // the pivot column unsigned pjd = pj - m_index_start; unsigned pivot_row_offset = (i-m_index_start)*m_dim; T pivot = m_v[pivot_row_offset + pjd]; unsigned row_offset= (row-m_index_start)*m_dim; T m = m_v[row_offset + pjd]; lp_assert(!is_zero(pivot)); m_v[row_offset + pjd] = -m * pivot; // creating L matrix for (unsigned j = m_index_start; j < m_parent->dimension(); j++) { if (j == pj) { pivot_row_offset++; row_offset++; continue; } auto t = m_v[row_offset] - m_v[pivot_row_offset] * m; if (settings.abs_val_is_smaller_than_drop_tolerance(t)) { m_v[row_offset] = zero_of_type(); } else { m_v[row_offset] = t; } row_offset++; pivot_row_offset++; // at the same time we pivot the L too } } template void square_dense_submatrix::divide_row_by_pivot(unsigned i) { unsigned pj = adjust_column(i); // the pivot column unsigned irow_offset = (i - m_index_start) * m_dim; T pivot = m_v[irow_offset + pj - m_index_start]; lp_assert(!is_zero(pivot)); for (unsigned k = m_index_start; k < m_parent->dimension(); k++) { if (k == pj){ m_v[irow_offset++] = one_of_type() / pivot; // creating the L matrix diagonal continue; } m_v[irow_offset++] /= pivot; } } template void square_dense_submatrix::update_parent_matrix(lp_settings & settings) { for (unsigned i = m_index_start; i < m_parent->dimension(); i++) update_existing_or_delete_in_parent_matrix_for_row(i, settings); push_new_elements_to_parent_matrix(settings); for (unsigned i = m_index_start; i < m_parent->dimension(); i++) m_parent->set_max_in_row(m_parent->adjust_row(i)); } template void square_dense_submatrix::update_existing_or_delete_in_parent_matrix_for_row(unsigned i, lp_settings & settings) { bool diag_updated = false; unsigned ai = m_parent->adjust_row(i); auto & row_vals = m_parent->get_row_values(ai); for (unsigned k = 0; k < row_vals.size(); k++) { auto & iv = row_vals[k]; unsigned j = m_parent->adjust_column_inverse(iv.m_index); if (j < i) { m_parent->remove_element(row_vals, iv); k--; } else if (i == j) { m_parent->m_columns[iv.m_index].m_values[iv.m_other].set_value(iv.m_value = one_of_type()); diag_updated = true; } else { // j > i T & v = (*this)[i][j]; if (settings.abs_val_is_smaller_than_drop_tolerance(v)) { m_parent->remove_element(row_vals, iv); k--; } else { m_parent->m_columns[iv.m_index].m_values[iv.m_other].set_value(iv.m_value = v); v = zero_of_type(); // only new elements are left above the diagonal } } } if (!diag_updated) { unsigned aj = m_parent->adjust_column(i); m_parent->add_new_element(ai, aj, one_of_type()); } } template void square_dense_submatrix::push_new_elements_to_parent_matrix(lp_settings & settings) { for (unsigned i = m_index_start; i < m_parent->dimension() - 1; i++) { unsigned ai = m_parent->adjust_row(i); for (unsigned j = i + 1; j < m_parent->dimension(); j++) { T & v = (*this)[i][j]; if (!settings.abs_val_is_smaller_than_drop_tolerance(v)) { unsigned aj = m_parent->adjust_column(j); m_parent->add_new_element(ai, aj, v); } v = zero_of_type(); // leave only L elements now } } } template template L square_dense_submatrix::row_by_vector_product(unsigned i, const vector & v) { lp_assert(i >= m_index_start); unsigned row_in_subm = i - m_index_start; unsigned row_offset = row_in_subm * m_dim; L r = zero_of_type(); for (unsigned j = 0; j < m_dim; j++) r += m_v[row_offset + j] * v[adjust_column_inverse(m_index_start + j)]; return r; } template template L square_dense_submatrix::column_by_vector_product(unsigned j, const vector & v) { lp_assert(j >= m_index_start); unsigned offset = j - m_index_start; L r = zero_of_type(); for (unsigned i = 0; i < m_dim; i++, offset += m_dim) r += m_v[offset] * v[adjust_row_inverse(m_index_start + i)]; return r; } template template L square_dense_submatrix::row_by_indexed_vector_product(unsigned i, const indexed_vector & v) { lp_assert(i >= m_index_start); unsigned row_in_subm = i - m_index_start; unsigned row_offset = row_in_subm * m_dim; L r = zero_of_type(); for (unsigned j = 0; j < m_dim; j++) r += m_v[row_offset + j] * v[adjust_column_inverse(m_index_start + j)]; return r; } template template void square_dense_submatrix::apply_from_left_local(indexed_vector & w, lp_settings & settings) { #ifdef Z3DEBUG // dense_matrix deb(*this); // vector deb_w(w.m_data.size()); // for (unsigned i = 0; i < w.m_data.size(); i++) // deb_w[i] = w[i]; // deb.apply_from_left(deb_w); #endif // use indexed vector here #ifndef DO_NOT_USE_INDEX vector t(m_parent->dimension(), zero_of_type()); for (auto k : w.m_index) { unsigned j = adjust_column(k); // k-th element will contribute only to column j if (j < m_index_start || j >= this->m_index_start + this->m_dim) { // it is a unit matrix outside t[adjust_row_inverse(j)] = w[k]; } else { const L & v = w[k]; for (unsigned i = 0; i < m_dim; i++) { unsigned row = adjust_row_inverse(m_index_start + i); unsigned offs = i * m_dim + j - m_index_start; t[row] += m_v[offs] * v; } } } w.m_index.clear(); for (unsigned i = 0; i < m_parent->dimension(); i++) { const L & v = t[i]; if (!settings.abs_val_is_smaller_than_drop_tolerance(v)){ w.m_index.push_back(i); w.m_data[i] = v; } else { w.m_data[i] = zero_of_type(); } } #else vector t(m_parent->dimension()); for (unsigned i = 0; i < m_index_start; i++) { t[adjust_row_inverse(i)] = w[adjust_column_inverse(i)]; } for (unsigned i = m_index_start; i < m_parent->dimension(); i++){ t[adjust_row_inverse(i)] = row_by_indexed_vector_product(i, w); } for (unsigned i = 0; i < m_parent->dimension(); i++) { w.set_value(t[i], i); } for (unsigned i = 0; i < m_parent->dimension(); i++) { const L & v = t[i]; if (!is_zero(v)) w.m_index.push_back(i); w.m_data[i] = v; } #endif #ifdef Z3DEBUG // cout << "w final" << endl; // print_vector(w.m_data); // lp_assert(vectors_are_equal(deb_w, w.m_data)); // lp_assert(w.is_OK()); #endif } template template void square_dense_submatrix::apply_from_left_to_vector(vector & w) { // lp_settings & settings) { // dense_matrix deb(*this); // vector deb_w(w); // deb.apply_from_left_to_X(deb_w, settings); // // cout << "deb" << endl; // // print_matrix(deb); // // cout << "w" << endl; // // print_vector(w.m_data); // // cout << "deb_w" << endl; // // print_vector(deb_w); vector t(m_parent->dimension()); for (unsigned i = 0; i < m_index_start; i++) { t[adjust_row_inverse(i)] = w[adjust_column_inverse(i)]; } for (unsigned i = m_index_start; i < m_parent->dimension(); i++){ t[adjust_row_inverse(i)] = row_by_vector_product(i, w); } for (unsigned i = 0; i < m_parent->dimension(); i++) { w[i] = t[i]; } #ifdef Z3DEBUG // cout << "w final" << endl; // print_vector(w.m_data); // lp_assert(vectors_are_equal(deb_w, w)); #endif } template bool square_dense_submatrix::is_L_matrix() const { #ifdef Z3DEBUG lp_assert(m_row_permutation.is_identity()); for (unsigned i = 0; i < m_parent->dimension(); i++) { if (i < m_index_start) { lp_assert(m_column_permutation[i] == i); continue; } unsigned row_offs = (i-m_index_start)*m_dim; for (unsigned k = 0; k < m_dim; k++) { unsigned j = m_index_start + k; unsigned jex = adjust_column_inverse(j); if (jex > i) { lp_assert(is_zero(m_v[row_offs + k])); } else if (jex == i) { lp_assert(!is_zero(m_v[row_offs + k])); } } } #endif return true; } template void square_dense_submatrix::apply_from_right(vector & w) { #ifdef Z3DEBUG // dense_matrix deb(*this); // vector deb_w(w); // deb.apply_from_right(deb_w); #endif vector t(w.size()); for (unsigned j = 0; j < m_index_start; j++) { t[adjust_column_inverse(j)] = w[adjust_row_inverse(j)]; } unsigned end = m_index_start + m_dim; for (unsigned j = end; j < m_parent->dimension(); j++) { t[adjust_column_inverse(j)] = w[adjust_row_inverse(j)]; } for (unsigned j = m_index_start; j < end; j++) { t[adjust_column_inverse(j)] = column_by_vector_product(j, w); } w = t; #ifdef Z3DEBUG // lp_assert(vector_are_equal(deb_w, w)); #endif } #ifdef Z3DEBUG template T square_dense_submatrix::get_elem (unsigned i, unsigned j) const { i = adjust_row(i); j = adjust_column(j); if (i < m_index_start || j < m_index_start) return i == j? one_of_type() : zero_of_type(); unsigned offs = (i - m_index_start)* m_dim + j - m_index_start; return m_v[offs]; } #endif template void square_dense_submatrix::conjugate_by_permutation(permutation_matrix & q) { m_row_permutation.multiply_by_permutation_from_left(q); m_column_permutation.multiply_by_reverse_from_right(q); } } z3-z3-4.8.7/src/util/lp/square_sparse_matrix.cpp000066400000000000000000000252361356505360400215630ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #include #include "util/vector.h" #include "util/lp/lp_settings.h" #include "util/lp/lu.h" #include "util/lp/square_sparse_matrix_def.h" #include "util/lp/dense_matrix.h" namespace lp { template double square_sparse_matrix::dot_product_with_row(unsigned int, vector const&) const; template void square_sparse_matrix::add_new_element(unsigned int, unsigned int, const double&); template void square_sparse_matrix::divide_row_by_constant(unsigned int, const double&, lp_settings&); template bool square_sparse_matrix::fill_eta_matrix(unsigned int, eta_matrix**); template const double & square_sparse_matrix::get(unsigned int, unsigned int) const; template unsigned square_sparse_matrix::get_number_of_nonzeroes() const; template bool square_sparse_matrix::get_pivot_for_column(unsigned int&, unsigned int&, int, unsigned int); template unsigned square_sparse_matrix::lowest_row_in_column(unsigned int); template bool square_sparse_matrix::pivot_row_to_row(unsigned int, const double&, unsigned int, lp_settings&); template bool square_sparse_matrix::pivot_with_eta(unsigned int, eta_matrix*, lp_settings&); template void square_sparse_matrix::prepare_for_factorization(); template void square_sparse_matrix::remove_element(vector >&, indexed_value&); template void square_sparse_matrix::replace_column(unsigned int, indexed_vector&, lp_settings&); template void square_sparse_matrix::set(unsigned int, unsigned int, double); template void square_sparse_matrix::set_max_in_row(vector >&); template bool square_sparse_matrix::set_row_from_work_vector_and_clean_work_vector_not_adjusted(unsigned int, indexed_vector&, lp_settings&); template bool square_sparse_matrix::shorten_active_matrix(unsigned int, eta_matrix*); template void square_sparse_matrix::solve_y_U(vector&) const; template square_sparse_matrix::square_sparse_matrix(unsigned int, unsigned); template void square_sparse_matrix::add_new_element(unsigned int, unsigned int, const mpq&); template void square_sparse_matrix::divide_row_by_constant(unsigned int, const mpq&, lp_settings&); template bool square_sparse_matrix::fill_eta_matrix(unsigned int, eta_matrix**); template mpq const & square_sparse_matrix::get(unsigned int, unsigned int) const; template unsigned square_sparse_matrix::get_number_of_nonzeroes() const; template bool square_sparse_matrix::get_pivot_for_column(unsigned int&, unsigned int&, int, unsigned int); template unsigned square_sparse_matrix::lowest_row_in_column(unsigned int); template bool square_sparse_matrix::pivot_with_eta(unsigned int, eta_matrix*, lp_settings&); template void square_sparse_matrix::prepare_for_factorization(); template void square_sparse_matrix::remove_element(vector> &, indexed_value&); template void square_sparse_matrix::replace_column(unsigned int, indexed_vector&, lp_settings&); template void square_sparse_matrix::set_max_in_row(vector>&); template bool square_sparse_matrix::set_row_from_work_vector_and_clean_work_vector_not_adjusted(unsigned int, indexed_vector&, lp_settings&); template bool square_sparse_matrix::shorten_active_matrix(unsigned int, eta_matrix*); template void square_sparse_matrix::solve_y_U(vector&) const; template void square_sparse_matrix>::add_new_element(unsigned int, unsigned int, const mpq&); template void square_sparse_matrix>::divide_row_by_constant(unsigned int, const mpq&, lp_settings&); template bool square_sparse_matrix>::fill_eta_matrix(unsigned int, eta_matrix >**); template const mpq & square_sparse_matrix>::get(unsigned int, unsigned int) const; template unsigned square_sparse_matrix>::get_number_of_nonzeroes() const; template bool square_sparse_matrix>::get_pivot_for_column(unsigned int&, unsigned int&, int, unsigned int); template unsigned square_sparse_matrix>::lowest_row_in_column(unsigned int); template bool square_sparse_matrix>::pivot_with_eta(unsigned int, eta_matrix >*, lp_settings&); template void square_sparse_matrix>::prepare_for_factorization(); template void square_sparse_matrix>::remove_element(vector>&, indexed_value&); template void square_sparse_matrix>::replace_column(unsigned int, indexed_vector&, lp_settings&); template void square_sparse_matrix>::set_max_in_row(vector>&); template bool square_sparse_matrix>::set_row_from_work_vector_and_clean_work_vector_not_adjusted(unsigned int, indexed_vector&, lp_settings&); template bool square_sparse_matrix>::shorten_active_matrix(unsigned int, eta_matrix >*); template void square_sparse_matrix>::solve_y_U(vector&) const; template void square_sparse_matrix::double_solve_U_y(indexed_vector&, const lp_settings &); template void square_sparse_matrix::double_solve_U_y(indexed_vector&, const lp_settings&); template void square_sparse_matrix>::double_solve_U_y(indexed_vector&, const lp_settings&); template void square_sparse_matrix >::double_solve_U_y >(indexed_vector>&, const lp_settings&); template void square_sparse_matrix::solve_U_y_indexed_only(indexed_vector&, const lp_settings&, vector &); template void square_sparse_matrix::solve_U_y_indexed_only(indexed_vector&, const lp_settings &, vector &); #ifdef Z3DEBUG template bool square_sparse_matrix::is_upper_triangular_and_maximums_are_set_correctly_in_rows(lp_settings&) const; template bool square_sparse_matrix::is_upper_triangular_and_maximums_are_set_correctly_in_rows(lp_settings&) const; template bool square_sparse_matrix >::is_upper_triangular_and_maximums_are_set_correctly_in_rows(lp_settings&) const; #endif template void square_sparse_matrix >::solve_U_y_indexed_only(indexed_vector&, const lp_settings &, vector &); template void square_sparse_matrix::solve_U_y(vector&); template void square_sparse_matrix::double_solve_U_y(vector&); template void square_sparse_matrix::solve_U_y(vector&); template void square_sparse_matrix::double_solve_U_y(vector&); template void square_sparse_matrix >::solve_U_y >(vector >&); template void square_sparse_matrix >::double_solve_U_y >(vector >&); template void square_sparse_matrix::find_error_in_solution_U_y_indexed(indexed_vector&, indexed_vector&, const vector &); template double square_sparse_matrix::dot_product_with_row(unsigned int, indexed_vector const&) const; template void square_sparse_matrix::find_error_in_solution_U_y_indexed(indexed_vector&, indexed_vector&, const vector &); template mpq square_sparse_matrix::dot_product_with_row(unsigned int, indexed_vector const&) const; template void square_sparse_matrix >::find_error_in_solution_U_y_indexed(indexed_vector&, indexed_vector&, const vector &); template mpq square_sparse_matrix >::dot_product_with_row(unsigned int, indexed_vector const&) const; template void square_sparse_matrix >::find_error_in_solution_U_y_indexed >(indexed_vector >&, indexed_vector >&, const vector &); template numeric_pair square_sparse_matrix >::dot_product_with_row >(unsigned int, indexed_vector > const&) const; template void square_sparse_matrix::extend_and_sort_active_rows(vector const&, vector&); template void square_sparse_matrix >::extend_and_sort_active_rows(vector const&, vector&); template void square_sparse_matrix >::solve_U_y(vector&); template void square_sparse_matrix >::double_solve_U_y(vector&); template void square_sparse_matrix< mpq,numeric_pair< mpq> >::set(unsigned int,unsigned int, mpq); template void square_sparse_matrix::solve_y_U_indexed(indexed_vector&, const lp_settings & ); template void square_sparse_matrix::solve_y_U_indexed(indexed_vector&, const lp_settings &); template void square_sparse_matrix >::solve_y_U_indexed(indexed_vector&, const lp_settings &); template square_sparse_matrix::square_sparse_matrix(static_matrix const&, vector&); template square_sparse_matrix::square_sparse_matrix (static_matrix const&, vector&); template square_sparse_matrix >::square_sparse_matrix(static_matrix > const&, vector&); } template void lp::square_sparse_matrix::copy_from_input_on_basis >(lp::static_matrix const&, vector&); template void lp::square_sparse_matrix::copy_from_input_on_basis >(lp::static_matrix const&, vector&); z3-z3-4.8.7/src/util/lp/square_sparse_matrix.h000066400000000000000000000361261356505360400212300ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #pragma once #include "util/vector.h" #include "util/lp/permutation_matrix.h" #include #include "util/lp/static_matrix.h" #include #include #include #include #include #include "util/lp/indexed_value.h" #include "util/lp/indexed_vector.h" #include #include "util/lp/lp_settings.h" #include "util/lp/eta_matrix.h" #include "util/lp/binary_heap_upair_queue.h" #include "util/lp/numeric_pair.h" #include "util/lp/int_set.h" namespace lp { // it is a square matrix template class square_sparse_matrix : public matrix { struct col_header { unsigned m_shortened_markovitz; vector> m_values; // the actual column values col_header(): m_shortened_markovitz(0) {} void shorten_markovich_by_one() { m_shortened_markovitz++; } void zero_shortened_markovitz() { m_shortened_markovitz = 0; } }; unsigned m_n_of_active_elems; binary_heap_upair_queue m_pivot_queue; public: vector>> m_rows; vector m_columns; permutation_matrix m_row_permutation; permutation_matrix m_column_permutation; // m_work_pivot_vector[j] = offset of elementh of j-th column in the row we are pivoting to // if the column is not present then m_work_pivot_vector[j] is -1 vector m_work_pivot_vector; vector m_processed; unsigned get_n_of_active_elems() const { return m_n_of_active_elems; } #ifdef Z3DEBUG // dense_matrix m_dense; #endif /* the rule is: row i is mapped to m_row_permutation[i] and column j is mapped to m_column_permutation.apply_reverse(j) */ unsigned adjust_row(unsigned row) const{ return m_row_permutation[row]; } unsigned adjust_column(unsigned col) const{ return m_column_permutation.apply_reverse(col); } unsigned adjust_row_inverse(unsigned row) const{ return m_row_permutation.apply_reverse(row); } unsigned adjust_column_inverse(unsigned col) const{ return m_column_permutation[col]; } template void copy_column_from_input(unsigned input_column, const M& A, unsigned j); template void copy_column_from_input_with_possible_zeros(const M& A, unsigned j); template void copy_from_input(const M& A); template void copy_from_input_on_basis(const M& A, vector & basis); public: // constructors template square_sparse_matrix(const M &A, vector& basis); template square_sparse_matrix(const M &A); square_sparse_matrix(unsigned dim, unsigned); // the second parameter is needed to distinguish this // constructor from the one above class ref_matrix_element { square_sparse_matrix & m_matrix; unsigned m_row; unsigned m_col; public: ref_matrix_element(square_sparse_matrix & m, unsigned row, unsigned col):m_matrix(m), m_row(row), m_col(col) {} ref_matrix_element & operator=(T const & v) { m_matrix.set( m_row, m_col, v); return *this; } ref_matrix_element & operator=(ref_matrix_element const & v) { m_matrix.set(m_row, m_col, v.m_matrix.get(v.m_row, v.m_col)); return *this; } operator T () const { return m_matrix.get(m_row, m_col); } }; class ref_row { square_sparse_matrix & m_matrix; unsigned m_row; public: ref_row(square_sparse_matrix & m, unsigned row) : m_matrix(m), m_row(row) {} ref_matrix_element operator[](unsigned col) const { return ref_matrix_element(m_matrix, m_row, col); } }; void set_with_no_adjusting_for_row(unsigned row, unsigned col, T val); void set_with_no_adjusting_for_col(unsigned row, unsigned col, T val); void set_with_no_adjusting(unsigned row, unsigned col, T val); void set(unsigned row, unsigned col, T val); T const & get_not_adjusted(unsigned row, unsigned col) const; T const & get(unsigned row, unsigned col) const; ref_row operator[](unsigned row) { return ref_row(*this, row); } ref_matrix_element operator()(unsigned row, unsigned col) { return ref_matrix_element(*this, row, col); } T operator() (unsigned row, unsigned col) const { return get(row, col); } vector> & get_row_values(unsigned row) { return m_rows[row]; } vector> const & get_row_values(unsigned row) const { return m_rows[row]; } vector> & get_column_values(unsigned col) { return m_columns[col].m_values; } vector> const & get_column_values(unsigned col) const { return m_columns[col].m_values; } unsigned dimension() const {return static_cast(m_row_permutation.size());} unsigned row_count() const override {return dimension();} unsigned column_count() const override {return dimension();} void init_row_headers(); void init_column_headers(); unsigned lowest_row_in_column(unsigned j); indexed_value & column_iv_other(indexed_value & iv) { return m_rows[iv.m_index][iv.m_other]; } indexed_value & row_iv_other(indexed_value & iv) { return m_columns[iv.m_index].m_values[iv.m_other]; } void remove_element(vector> & row_vals, unsigned row_offset, vector> & column_vals, unsigned column_offset); void remove_element(vector> & row_chunk, indexed_value & row_el_iv); void put_max_index_to_0(vector> & row_vals, unsigned max_index); void set_max_in_row(unsigned row) { set_max_in_row(m_rows[row]); } void set_max_in_row(vector> & row_vals); bool pivot_with_eta(unsigned i, eta_matrix *eta_matrix, lp_settings & settings); void scan_row_to_work_vector_and_remove_pivot_column(unsigned row, unsigned pivot_column); // This method pivots row i to row i0 by muliplying row i by // alpha and adding it to row i0. // After pivoting the row i0 has a max abs value set correctly at the beginning of m_start, // Returns false if the resulting row is all zeroes, and true otherwise bool pivot_row_to_row(unsigned i, const T& alpha, unsigned i0, lp_settings & settings ); // set the max val as well // returns false if the resulting row is all zeroes, and true otherwise bool set_row_from_work_vector_and_clean_work_vector_not_adjusted(unsigned i0, indexed_vector & work_vec, lp_settings & settings); // set the max val as well // returns false if the resulting row is all zeroes, and true otherwise bool set_row_from_work_vector_and_clean_work_vector(unsigned i0); void remove_zero_elements_and_set_data_on_existing_elements(unsigned row); // work_vec here has not adjusted column indices void remove_zero_elements_and_set_data_on_existing_elements_not_adjusted(unsigned row, indexed_vector & work_vec, lp_settings & settings); void multiply_from_right(permutation_matrix& p) { // m_dense = m_dense * p; m_column_permutation.multiply_by_permutation_from_right(p); // lp_assert(*this == m_dense); } void multiply_from_left(permutation_matrix& p) { // m_dense = p * m_dense; m_row_permutation.multiply_by_permutation_from_left(p); // lp_assert(*this == m_dense); } void multiply_from_left_with_reverse(permutation_matrix& p) { // m_dense = p * m_dense; m_row_permutation.multiply_by_permutation_reverse_from_left(p); // lp_assert(*this == m_dense); } // adding delta columns at the end of the matrix void add_columns_at_the_end(unsigned delta); void delete_column(int i); void swap_columns(unsigned a, unsigned b) { m_column_permutation.transpose_from_left(a, b); } void swap_rows(unsigned a, unsigned b) { m_row_permutation.transpose_from_right(a, b); // m_dense.swap_rows(a, b); // lp_assert(*this == m_dense); } void divide_row_by_constant(unsigned i, const T & t, lp_settings & settings); bool close(T a, T b) { return // (numeric_traits::precise() && numeric_traits::is_zero(a - b)) // || fabs(numeric_traits::get_double(a - b)) < 0.0000001; } // solving x * this = y, and putting the answer into y // the matrix here has to be upper triangular void solve_y_U(vector & y) const; // solving x * this = y, and putting the answer into y // the matrix here has to be upper triangular void solve_y_U_indexed(indexed_vector & y, const lp_settings &); // fills the indices for such that y[i] can be not a zero // sort them so the smaller indices come first void fill_reachable_indices(std::set & rset, T *y); template void find_error_in_solution_U_y(vector& y_orig, vector & y); template void find_error_in_solution_U_y_indexed(indexed_vector& y_orig, indexed_vector & y, const vector& sorted_active_rows); template void add_delta_to_solution(const vector& del, vector & y); template void add_delta_to_solution(const indexed_vector& del, indexed_vector & y); template void double_solve_U_y(indexed_vector& y, const lp_settings & settings); template void double_solve_U_y(vector& y); // solving this * x = y, and putting the answer into y // the matrix here has to be upper triangular template void solve_U_y(vector & y); // solving this * x = y, and putting the answer into y // the matrix here has to be upper triangular template void solve_U_y_indexed_only(indexed_vector & y, const lp_settings&, vector & sorted_active_rows ); T get_elem(unsigned i, unsigned j) const override { return get(i, j); } unsigned get_number_of_rows() const { return dimension(); } unsigned get_number_of_columns() const { return dimension(); } void set_number_of_rows(unsigned /*m*/) override { } void set_number_of_columns(unsigned /*n*/) override { } template L dot_product_with_row (unsigned row, const vector & y) const; template L dot_product_with_row (unsigned row, const indexed_vector & y) const; unsigned get_number_of_nonzeroes() const; bool get_non_zero_column_in_row(unsigned i, unsigned *j) const; void remove_element_that_is_not_in_w(vector> & column_vals, indexed_value & col_el_iv); // w contains the new column // the old column inside of the matrix has not been changed yet void remove_elements_that_are_not_in_w_and_update_common_elements(unsigned column_to_replace, indexed_vector & w); void add_new_element(unsigned row, unsigned col, const T& val); // w contains the "rest" of the new column; all common elements of w and the old column has been zeroed // the old column inside of the matrix has not been changed yet void add_new_elements_of_w_and_clear_w(unsigned column_to_replace, indexed_vector & w, lp_settings & settings); void replace_column(unsigned column_to_replace, indexed_vector & w, lp_settings &settings); unsigned pivot_score(unsigned i, unsigned j); void enqueue_domain_into_pivot_queue(); void set_max_in_rows(); void zero_shortened_markovitz_numbers(); void prepare_for_factorization(); void recover_pivot_queue(vector & rejected_pivots); int elem_is_too_small(unsigned i, unsigned j, int c_partial_pivoting); bool remove_row_from_active_pivots_and_shorten_columns(unsigned row); void remove_pivot_column(unsigned row); void update_active_pivots(unsigned row); bool shorten_active_matrix(unsigned row, eta_matrix *eta_matrix); unsigned pivot_score_without_shortened_counters(unsigned i, unsigned j, unsigned k); #ifdef Z3DEBUG bool can_improve_score_for_row(unsigned row, unsigned score, T const & c_partial_pivoting, unsigned k); bool really_best_pivot(unsigned i, unsigned j, T const & c_partial_pivoting, unsigned k); void print_active_matrix(unsigned k, std::ostream & out); #endif bool pivot_queue_is_correct_for_row(unsigned i, unsigned k); bool pivot_queue_is_correct_after_pivoting(int k); bool get_pivot_for_column(unsigned &i, unsigned &j, int c_partial_pivoting, unsigned k); bool elem_is_too_small(vector> & row_chunk, indexed_value & iv, int c_partial_pivoting); unsigned number_of_non_zeroes_in_row(unsigned row) const { return static_cast(m_rows[row].size()); } unsigned number_of_non_zeroes_in_column(unsigned col) const { return m_columns[col].m_values.size(); } bool shorten_columns_by_pivot_row(unsigned i, unsigned pivot_column); bool col_is_active(unsigned j, unsigned pivot) { return adjust_column_inverse(j) > pivot; } bool row_is_active(unsigned i, unsigned pivot) { return adjust_row_inverse(i) > pivot; } bool fill_eta_matrix(unsigned j, eta_matrix ** eta); #ifdef Z3DEBUG bool is_upper_triangular_and_maximums_are_set_correctly_in_rows(lp_settings & settings) const; bool is_upper_triangular_until(unsigned k) const; void check_column_vs_rows(unsigned col); void check_row_vs_columns(unsigned row); void check_rows_vs_columns(); void check_columns_vs_rows(); void check_matrix(); #endif void create_graph_G(const vector & active_rows, vector & sorted_active_rows); void process_column_recursively(unsigned i, vector & sorted_rows); void extend_and_sort_active_rows(const vector & active_rows, vector & sorted_active_rows); void process_index_recursively_for_y_U(unsigned j, vector & sorted_rows); void resize(unsigned new_dim) { unsigned old_dim = dimension(); lp_assert(new_dim >= old_dim); for (unsigned j = old_dim; j < new_dim; j++) { m_rows.push_back(vector>()); m_columns.push_back(col_header()); } m_pivot_queue.resize(new_dim); m_row_permutation.resize(new_dim); m_column_permutation.resize(new_dim); m_work_pivot_vector.resize(new_dim); m_processed.resize(new_dim); for (unsigned j = old_dim; j < new_dim; j++) { add_new_element(j, j, numeric_traits::one()); } } #ifdef Z3DEBUG vector get_full_row(unsigned i) const; #endif unsigned pivot_queue_size() const { return m_pivot_queue.size(); } }; }; z3-z3-4.8.7/src/util/lp/square_sparse_matrix_def.h000066400000000000000000001317301356505360400220430ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #include "util/vector.h" #include "util/lp/square_sparse_matrix.h" #include #include namespace lp { template template void square_sparse_matrix::copy_column_from_input(unsigned input_column, const M& A, unsigned j) { vector> & new_column_vector = m_columns[j].m_values; for (const auto & c : A.column(input_column)) { unsigned col_offset = static_cast(new_column_vector.size()); vector> & row_vector = m_rows[c.var()]; unsigned row_offset = static_cast(row_vector.size()); new_column_vector.push_back(indexed_value(c.coeff(), c.var(), row_offset)); row_vector.push_back(indexed_value(c.coeff(), j, col_offset)); m_n_of_active_elems++; } } template template void square_sparse_matrix::copy_column_from_input_with_possible_zeros(const M& A, unsigned j) { vector> & new_column_vector = m_columns[j].m_values; for (const auto & c : A.column(j)) { if (is_zero(c.coeff())) continue; unsigned col_offset = static_cast(new_column_vector.size()); vector> & row_vector = m_rows[c.var()]; unsigned row_offset = static_cast(row_vector.size()); new_column_vector.push_back(indexed_value(c.coeff(), c.var(), row_offset)); row_vector.push_back(indexed_value(c.coeff(), j, col_offset)); m_n_of_active_elems++; } } template template void square_sparse_matrix::copy_from_input_on_basis(const M & A, vector & basis) { unsigned m = A.row_count(); for (unsigned j = m; j-- > 0;) { copy_column_from_input(basis[j], A, j); } } template template void square_sparse_matrix::copy_from_input(const M & A) { unsigned m = A.row_count(); for (unsigned j = m; j-- > 0;) { copy_column_from_input_with_possible_zeros(A, j); } } // constructor that copies columns of the basis from A template template square_sparse_matrix::square_sparse_matrix(const M &A, vector & basis) : m_n_of_active_elems(0), m_pivot_queue(A.row_count()), m_row_permutation(A.row_count()), m_column_permutation(A.row_count()), m_work_pivot_vector(A.row_count(), -1), m_processed(A.row_count()) { init_row_headers(); init_column_headers(); copy_from_input_on_basis(A, basis); } template template square_sparse_matrix::square_sparse_matrix(const M &A) : m_n_of_active_elems(0), m_pivot_queue(A.row_count()), m_row_permutation(A.row_count()), m_column_permutation(A.row_count()), m_work_pivot_vector(A.row_count(), -1), m_processed(A.row_count()) { init_row_headers(); init_column_headers(); copy_from_input(A); } template void square_sparse_matrix::set_with_no_adjusting_for_row(unsigned row, unsigned col, T val) { // should not be used in efficient code vector> & row_vec = m_rows[row]; for (auto & iv : row_vec) { if (iv.m_index == col) { iv.set_value(val); return; } } // have not found the column between the indices row_vec.push_back(indexed_value(val, col)); // what about m_other ??? } template void square_sparse_matrix::set_with_no_adjusting_for_col(unsigned row, unsigned col, T val) { // should not be used in efficient code vector> & col_vec = m_columns[col].m_values; for (auto & iv : col_vec) { if (iv.m_index == row) { iv.set_value(val); return; } } // have not found the column between the indices col_vec.push_back(indexed_value(val, row)); // what about m_other ??? } template void square_sparse_matrix::set_with_no_adjusting(unsigned row, unsigned col, T val) { // should not be used in efficient code set_with_no_adjusting_for_row(row, col, val); set_with_no_adjusting_for_col(row, col, val); } template void square_sparse_matrix::set(unsigned row, unsigned col, T val) { // should not be used in efficient code lp_assert(row < dimension() && col < dimension()); // m_dense.set_elem(row, col, val); row = adjust_row(row); col = adjust_column(col); set_with_no_adjusting(row, col, val); // lp_assert(*this == m_dense); } template T const & square_sparse_matrix::get_not_adjusted(unsigned row, unsigned col) const { for (indexed_value const & iv : m_rows[row]) { if (iv.m_index == col) { return iv.m_value; } } return numeric_traits::zero(); } template T const & square_sparse_matrix::get(unsigned row, unsigned col) const { // should not be used in efficient code row = adjust_row(row); auto & row_chunk = m_rows[row]; col = adjust_column(col); for (indexed_value const & iv : row_chunk) { if (iv.m_index == col) { return iv.m_value; } } return numeric_traits::zero(); } // constructor creating a zero matrix of dim*dim template square_sparse_matrix::square_sparse_matrix(unsigned dim, unsigned ) : m_pivot_queue(dim), // dim will be the initial size of the queue m_row_permutation(dim), m_column_permutation(dim), m_work_pivot_vector(dim, -1), m_processed(dim) { init_row_headers(); init_column_headers(); } template void square_sparse_matrix::init_row_headers() { for (unsigned l = 0; l < m_row_permutation.size(); l++) { m_rows.push_back(vector>()); } } template void square_sparse_matrix::init_column_headers() { // we always have only square square_sparse_matrix for (unsigned l = 0; l < m_row_permutation.size(); l++) { m_columns.push_back(col_header()); } } template unsigned square_sparse_matrix::lowest_row_in_column(unsigned j) { auto & mc = get_column_values(adjust_column(j)); unsigned ret = 0; for (auto & iv : mc) { unsigned row = adjust_row_inverse(iv.m_index); if (row > ret) { ret = row; } } return ret; } template void square_sparse_matrix::remove_element(vector> & row_vals, unsigned row_offset, vector> & column_vals, unsigned column_offset) { if (column_offset != column_vals.size() - 1) { auto & column_iv = column_vals[column_offset] = column_vals.back(); // copy from the tail column_iv_other(column_iv).m_other = column_offset; if (row_offset != row_vals.size() - 1) { auto & row_iv = row_vals[row_offset] = row_vals.back(); // copy from the tail row_iv_other(row_iv).m_other = row_offset; } } else if (row_offset != row_vals.size() - 1) { auto & row_iv = row_vals[row_offset] = row_vals.back(); // copy from the tail row_iv_other(row_iv).m_other = row_offset; } // do nothing - just decrease the sizes column_vals.pop_back(); row_vals.pop_back(); m_n_of_active_elems--; // the value is correct only when refactoring } template void square_sparse_matrix::remove_element(vector> & row_chunk, indexed_value & row_el_iv) { auto & column_chunk = get_column_values(row_el_iv.m_index); indexed_value & col_el_iv = column_chunk[row_el_iv.m_other]; remove_element(row_chunk, col_el_iv.m_other, column_chunk, row_el_iv.m_other); } template void square_sparse_matrix::put_max_index_to_0(vector> & row_vals, unsigned max_index) { if (max_index == 0) return; indexed_value * max_iv = & row_vals[max_index]; indexed_value * start_iv = & row_vals[0]; // update the "other" columns elements which are bound to the start_iv and max_iv m_columns[max_iv->m_index].m_values[max_iv->m_other].m_other = 0; m_columns[start_iv->m_index].m_values[start_iv->m_other].m_other = max_index; // swap the elements indexed_value t = * max_iv; * max_iv = * start_iv; * start_iv = t; } template void square_sparse_matrix::set_max_in_row(vector> & row_vals) { if (row_vals.empty()) return; T max_val = abs(row_vals[0].m_value); unsigned max_index = 0; for (unsigned i = 1; i < row_vals.size(); i++) { T iabs = abs(row_vals[i].m_value); if (iabs > max_val) { max_val = iabs; max_index = i; } } put_max_index_to_0(row_vals, max_index); } template bool square_sparse_matrix::pivot_with_eta(unsigned i, eta_matrix *eta_matrix, lp_settings & settings) { const T& pivot = eta_matrix->get_diagonal_element(); for (auto & it : eta_matrix->m_column_vector.m_data) { if (!pivot_row_to_row(i, it.second, it.first, settings)) { return false; } } divide_row_by_constant(i, pivot, settings); if (!shorten_active_matrix(i, eta_matrix)) { return false; } return true; } // returns the offset of the pivot column in the row template void square_sparse_matrix::scan_row_to_work_vector_and_remove_pivot_column(unsigned row, unsigned pivot_column) { auto & rvals = m_rows[row]; unsigned size = rvals.size(); for (unsigned j = 0; j < size; j++) { auto & iv = rvals[j]; if (iv.m_index != pivot_column) { m_work_pivot_vector[iv.m_index] = j; } else { remove_element(rvals, iv); j--; size--; } } } #ifdef Z3DEBUG template vector square_sparse_matrix::get_full_row(unsigned i) const { vector r; for (unsigned j = 0; j < column_count(); j++) r.push_back(get(i, j)); return r; } #endif // This method pivots row i to row i0 by muliplying row i by // alpha and adding it to row i0. // After pivoting the row i0 has a max abs value set correctly at the beginning of m_start, // Returns false if the resulting row is all zeroes, and true otherwise template bool square_sparse_matrix::pivot_row_to_row(unsigned i, const T& alpha, unsigned i0, lp_settings & settings ) { lp_assert(i < dimension() && i0 < dimension()); lp_assert(i != i0); unsigned pivot_col = adjust_column(i); i = adjust_row(i); i0 = adjust_row(i0); vector became_zeros; // the offset of element of the pivot column in row i0 scan_row_to_work_vector_and_remove_pivot_column(i0, pivot_col); auto & i0_row_vals = m_rows[i0]; // run over the pivot row and update row i0 unsigned prev_size_i0 = i0_row_vals.size(); for (const auto & iv : m_rows[i]) { unsigned j = iv.m_index; if (j == pivot_col) continue; T alv = alpha * iv.m_value; int j_offs = m_work_pivot_vector[j]; if (j_offs == -1) { // it is a new element if (!settings.abs_val_is_smaller_than_drop_tolerance(alv)) { add_new_element(i0, j, alv); } } else { auto & row_el_iv = i0_row_vals[j_offs]; row_el_iv.m_value += alv; if (settings.abs_val_is_smaller_than_drop_tolerance(row_el_iv.m_value)) { became_zeros.push_back(j_offs); ensure_increasing(became_zeros); } else { m_columns[j].m_values[row_el_iv.m_other].set_value(row_el_iv.m_value); } } } // clean the work vector for (unsigned k = 0; k < prev_size_i0; k++) { m_work_pivot_vector[i0_row_vals[k].m_index] = -1; } for (unsigned k = became_zeros.size(); k-- > 0; ) { unsigned j = became_zeros[k]; remove_element(i0_row_vals, i0_row_vals[j]); if (i0_row_vals.empty()) return false; } if (numeric_traits::precise() == false) set_max_in_row(i0_row_vals); return !i0_row_vals.empty(); } // set the max val as well // returns false if the resulting row is all zeroes, and true otherwise template bool square_sparse_matrix::set_row_from_work_vector_and_clean_work_vector_not_adjusted(unsigned i0, indexed_vector & work_vec, lp_settings & settings) { remove_zero_elements_and_set_data_on_existing_elements_not_adjusted(i0, work_vec, settings); // all non-zero elements in m_work_pivot_vector are new for (unsigned j : work_vec.m_index) { if (numeric_traits::is_zero(work_vec[j])) { continue; } lp_assert(!settings.abs_val_is_smaller_than_drop_tolerance(work_vec[j])); add_new_element(i0, adjust_column(j), work_vec[j]); work_vec[j] = numeric_traits::zero(); } work_vec.m_index.clear(); auto & row_vals = m_rows[i0]; if (row_vals.empty()) { return false; } set_max_in_row(row_vals); // it helps to find larger pivots return true; } template void square_sparse_matrix::remove_zero_elements_and_set_data_on_existing_elements(unsigned row) { auto & row_vals = m_rows[row]; for (unsigned k = static_cast(row_vals.size()); k-- > 0;) { // we cannot simply run the iterator since we are removing // elements from row_vals auto & row_el_iv = row_vals[k]; unsigned j = row_el_iv.m_index; T & wj = m_work_pivot_vector[j]; if (is_zero(wj)) { remove_element(row_vals, row_el_iv); } else { m_columns[j].m_values[row_el_iv.m_other].set_value(wj); row_el_iv.set_value(wj); wj = zero_of_type(); } } } // work_vec here has not adjusted column indices template void square_sparse_matrix::remove_zero_elements_and_set_data_on_existing_elements_not_adjusted(unsigned row, indexed_vector & work_vec, lp_settings & settings) { auto & row_vals = m_rows[row]; for (unsigned k = static_cast(row_vals.size()); k-- > 0;) { // we cannot simply run the iterator since we are removing // elements from row_vals auto & row_el_iv = row_vals[k]; unsigned j = row_el_iv.m_index; unsigned rj = adjust_column_inverse(j); T val = work_vec[rj]; if (settings.abs_val_is_smaller_than_drop_tolerance(val)) { remove_element(row_vals, row_el_iv); lp_assert(numeric_traits::is_zero(val)); } else { m_columns[j].m_values[row_el_iv.m_other].set_value(row_el_iv.m_value = val); work_vec[rj] = numeric_traits::zero(); } } } // adding delta columns at the end of the matrix template void square_sparse_matrix::add_columns_at_the_end(unsigned delta) { for (unsigned i = 0; i < delta; i++) { col_header col_head; m_columns.push_back(col_head); } m_column_permutation.enlarge(delta); } template void square_sparse_matrix::delete_column(int i) { lp_assert(i < dimension()); for (auto cell = m_columns[i].m_head; cell != nullptr;) { auto next_cell = cell->m_down; kill_cell(cell); cell = next_cell; } } template void square_sparse_matrix::divide_row_by_constant(unsigned i, const T & t, lp_settings & settings) { lp_assert(!settings.abs_val_is_smaller_than_zero_tolerance(t)); i = adjust_row(i); for (auto & iv : m_rows[i]) { T &v = iv.m_value; v /= t; if (settings.abs_val_is_smaller_than_drop_tolerance(v)){ v = numeric_traits::zero(); } m_columns[iv.m_index].m_values[iv.m_other].set_value(v); } } // solving x * this = y, and putting the answer into y // the matrix here has to be upper triangular template void square_sparse_matrix::solve_y_U(vector & y) const { // works by rows #ifdef Z3DEBUG // T * rs = clone_vector(y, dimension()); #endif unsigned end = dimension(); for (unsigned i = 0; i + 1 < end; i++) { // all y[i] has correct values already const T & yv = y[i]; if (numeric_traits::is_zero(yv)) continue; auto & mc = get_row_values(adjust_row(i)); for (auto & c : mc) { unsigned col = adjust_column_inverse(c.m_index); if (col != i) { y[col] -= c.m_value * yv; } } } #ifdef Z3DEBUG // dense_matrix deb(*this); // T * clone_y = clone_vector(y, dimension()); // deb.apply_from_right(clone_y); // lp_assert(vectors_are_equal(rs, clone_y, dimension())); // delete [] clone_y; // delete [] rs; #endif } // solving x * this = y, and putting the answer into y // the matrix here has to be upper triangular template void square_sparse_matrix::solve_y_U_indexed(indexed_vector & y, const lp_settings & settings) { #if 0 && Z3DEBUG vector ycopy(y.m_data); if (numeric_traits::precise() == false) solve_y_U(ycopy); #endif vector sorted_active_columns; extend_and_sort_active_rows(y.m_index, sorted_active_columns); for (unsigned k = sorted_active_columns.size(); k-- > 0; ) { unsigned j = sorted_active_columns[k]; auto & yj = y[j]; for (auto & c: m_columns[adjust_column(j)].m_values) { unsigned i = adjust_row_inverse(c.m_index); if (i == j) continue; yj -= y[i] * c.m_value; } } y.m_index.clear(); for (auto j : sorted_active_columns) { if (!settings.abs_val_is_smaller_than_drop_tolerance(y[j])) y.m_index.push_back(j); else if (!numeric_traits::precise()) y.m_data[j] = zero_of_type(); } lp_assert(y.is_OK()); #if 0 && Z3DEBUG if (numeric_traits::precise() == false) lp_assert(vectors_are_equal(ycopy, y.m_data)); #endif } // fills the indices for such that y[i] can be not a zero // sort them so the smaller indices come first // void fill_reachable_indices(std::set & rset, T *y) { // std::queue q; // int m = dimension(); // for (int i = m - 1; i >= 0; i--) { // if (!numeric_traits::is_zero(y[i])){ // for (cell * c = m_columns[adjust_column(i)].m_head; c != nullptr; c = c->m_down) { // unsigned row = adjust_row_inverse(c->m_i); // q.push(row); // } // } // } // while (!q.empty()) { // unsigned i = q.front(); // q.pop(); // for (cell * c = m_columns[adjust_column(i)].m_head; c != nullptr; c = c->m_down) { // unsigned row = adjust_row_inverse(c->m_i); // if (rset.find(row) == rset.end()){ // rset.insert(row); // q.push(row); // } // } // } // } template template void square_sparse_matrix::find_error_in_solution_U_y(vector& y_orig, vector & y) { unsigned i = dimension(); while (i--) { y_orig[i] -= dot_product_with_row(i, y); } } template template void square_sparse_matrix::find_error_in_solution_U_y_indexed(indexed_vector& y_orig, indexed_vector & y, const vector& sorted_active_rows) { for (unsigned i: sorted_active_rows) y_orig.add_value_at_index(i, -dot_product_with_row(i, y)); // cannot round up here!!! // y_orig can contain very small values } template template void square_sparse_matrix::add_delta_to_solution(const vector& del, vector & y) { unsigned i = dimension(); while (i--) { y[i] += del[i]; } } template template void square_sparse_matrix::add_delta_to_solution(const indexed_vector& del, indexed_vector & y) { // lp_assert(del.is_OK()); // lp_assert(y.is_OK()); for (auto i : del.m_index) { y.add_value_at_index(i, del[i]); } } template template void square_sparse_matrix::double_solve_U_y(indexed_vector& y, const lp_settings & settings){ lp_assert(y.is_OK()); indexed_vector y_orig(y); // copy y aside vector active_rows; solve_U_y_indexed_only(y, settings, active_rows); lp_assert(y.is_OK()); find_error_in_solution_U_y_indexed(y_orig, y, active_rows); // y_orig contains the error now if (y_orig.m_index.size() * ratio_of_index_size_to_all_size() < 32 * dimension()) { active_rows.clear(); solve_U_y_indexed_only(y_orig, settings, active_rows); add_delta_to_solution(y_orig, y); y.clean_up(); } else { // the dense version solve_U_y(y_orig.m_data); add_delta_to_solution(y_orig.m_data, y.m_data); y.restore_index_and_clean_from_data(); } lp_assert(y.is_OK()); } template template void square_sparse_matrix::double_solve_U_y(vector& y){ vector y_orig(y); // copy y aside solve_U_y(y); find_error_in_solution_U_y(y_orig, y); // y_orig contains the error now solve_U_y(y_orig); add_delta_to_solution(y_orig, y); } // solving this * x = y, and putting the answer into y // the matrix here has to be upper triangular template template void square_sparse_matrix::solve_U_y(vector & y) { // it is a column wise version #ifdef Z3DEBUG // T * rs = clone_vector(y, dimension()); #endif for (unsigned j = dimension(); j--; ) { const L & yj = y[j]; if (is_zero(yj)) continue; for (const auto & iv : m_columns[adjust_column(j)].m_values) { unsigned i = adjust_row_inverse(iv.m_index); if (i != j) { y[i] -= iv.m_value * yj; } } } #ifdef Z3DEBUG // dense_matrix deb(*this); // T * clone_y = clone_vector(y, dimension()); // deb.apply_from_left(clone_y); // lp_assert(vectors_are_equal(rs, clone_y, dimension())); #endif } template void square_sparse_matrix::process_index_recursively_for_y_U(unsigned j, vector & sorted_active_rows) { lp_assert(m_processed[j] == false); m_processed[j]=true; auto & row = m_rows[adjust_row(j)]; for (auto & c : row) { unsigned i = adjust_column_inverse(c.m_index); if (i == j) continue; if (!m_processed[i]) { process_index_recursively_for_y_U(i, sorted_active_rows); } } sorted_active_rows.push_back(j); } template void square_sparse_matrix::process_column_recursively(unsigned j, vector & sorted_active_rows) { lp_assert(m_processed[j] == false); auto & mc = m_columns[adjust_column(j)].m_values; for (auto & iv : mc) { unsigned i = adjust_row_inverse(iv.m_index); if (i == j) continue; if (!m_processed[i]) { process_column_recursively(i, sorted_active_rows); } } m_processed[j]=true; sorted_active_rows.push_back(j); } template void square_sparse_matrix::create_graph_G(const vector & index_or_right_side, vector & sorted_active_rows) { for (auto i : index_or_right_side) { if (m_processed[i]) continue; process_column_recursively(i, sorted_active_rows); } for (auto i : sorted_active_rows) { m_processed[i] = false; } } template void square_sparse_matrix::extend_and_sort_active_rows(const vector & index_or_right_side, vector & sorted_active_rows) { for (auto i : index_or_right_side) { if (m_processed[i]) continue; process_index_recursively_for_y_U(i, sorted_active_rows); } for (auto i : sorted_active_rows) { m_processed[i] = false; } } template template void square_sparse_matrix::solve_U_y_indexed_only(indexed_vector & y, const lp_settings & settings, vector & sorted_active_rows) { // it is a column wise version create_graph_G(y.m_index, sorted_active_rows); for (auto k = sorted_active_rows.size(); k-- > 0;) { unsigned j = sorted_active_rows[k]; const L & yj = y[j]; if (is_zero(yj)) continue; auto & mc = m_columns[adjust_column(j)].m_values; for (auto & iv : mc) { unsigned i = adjust_row_inverse(iv.m_index); if (i != j) { y[i] -= iv.m_value * yj; } } } y.m_index.clear(); for (auto j : sorted_active_rows) { if (!settings.abs_val_is_smaller_than_drop_tolerance(y[j])) y.m_index.push_back(j); else if (!numeric_traits::precise()) y[j] = zero_of_type(); } lp_assert(y.is_OK()); #ifdef Z3DEBUG // dense_matrix deb(this); // vector clone_y(y.m_data); // deb.apply_from_left(clone_y); // lp_assert(vectors_are_equal(rs, clone_y)); #endif } template template L square_sparse_matrix::dot_product_with_row (unsigned row, const vector & y) const { L ret = zero_of_type(); auto & mc = get_row_values(adjust_row(row)); for (auto & c : mc) { unsigned col = m_column_permutation[c.m_index]; ret += c.m_value * y[col]; } return ret; } template template L square_sparse_matrix::dot_product_with_row (unsigned row, const indexed_vector & y) const { L ret = zero_of_type(); auto & mc = get_row_values(adjust_row(row)); for (auto & c : mc) { unsigned col = m_column_permutation[c.m_index]; ret += c.m_value * y[col]; } return ret; } template unsigned square_sparse_matrix::get_number_of_nonzeroes() const { unsigned ret = 0; for (unsigned i = dimension(); i--; ) { ret += number_of_non_zeroes_in_row(i); } return ret; } template bool square_sparse_matrix::get_non_zero_column_in_row(unsigned i, unsigned *j) const { // go over the i-th row auto & mc = get_row_values(adjust_row(i)); if (mc.size() > 0) { *j = m_column_permutation[mc[0].m_index]; return true; } return false; } template void square_sparse_matrix::remove_element_that_is_not_in_w(vector> & column_vals, indexed_value & col_el_iv) { auto & row_chunk = m_rows[col_el_iv.m_index]; indexed_value & row_el_iv = row_chunk[col_el_iv.m_other]; unsigned index_in_row = col_el_iv.m_other; remove_element(row_chunk, col_el_iv.m_other, column_vals, row_el_iv.m_other); if (index_in_row == 0) set_max_in_row(row_chunk); } // w contains the new column // the old column inside of the matrix has not been changed yet template void square_sparse_matrix::remove_elements_that_are_not_in_w_and_update_common_elements(unsigned column_to_replace, indexed_vector & w) { // -------------------------------- // column_vals represents the old column auto & column_vals = m_columns[column_to_replace].m_values; for (unsigned k = static_cast(column_vals.size()); k-- > 0;) { indexed_value & col_el_iv = column_vals[k]; unsigned i = col_el_iv.m_index; T &w_data_at_i = w[adjust_row_inverse(i)]; if (numeric_traits::is_zero(w_data_at_i)) { remove_element_that_is_not_in_w(column_vals, col_el_iv); } else { auto& row_chunk = m_rows[i]; unsigned index_in_row = col_el_iv.m_other; if (index_in_row == 0) { bool look_for_max = abs(w_data_at_i) < abs(row_chunk[0].m_value); row_chunk[0].set_value(col_el_iv.m_value = w_data_at_i); if (look_for_max) set_max_in_row(i); } else { row_chunk[index_in_row].set_value(col_el_iv.m_value = w_data_at_i); if (abs(w_data_at_i) > abs(row_chunk[0].m_value)) put_max_index_to_0(row_chunk, index_in_row); } w_data_at_i = numeric_traits::zero(); } } } template void square_sparse_matrix::add_new_element(unsigned row, unsigned col, const T& val) { auto & row_vals = m_rows[row]; auto & col_vals = m_columns[col].m_values; unsigned row_el_offs = static_cast(row_vals.size()); unsigned col_el_offs = static_cast(col_vals.size()); row_vals.push_back(indexed_value(val, col, col_el_offs)); col_vals.push_back(indexed_value(val, row, row_el_offs)); m_n_of_active_elems++; } // w contains the "rest" of the new column; all common elements of w and the old column has been zeroed // the old column inside of the matrix has not been changed yet template void square_sparse_matrix::add_new_elements_of_w_and_clear_w(unsigned column_to_replace, indexed_vector & w, lp_settings & settings) { for (unsigned i : w.m_index) { T w_at_i = w[i]; if (numeric_traits::is_zero(w_at_i)) continue; // was dealt with already if (!settings.abs_val_is_smaller_than_drop_tolerance(w_at_i)) { unsigned ai = adjust_row(i); add_new_element(ai, column_to_replace, w_at_i); auto & row_chunk = m_rows[ai]; lp_assert(row_chunk.size() > 0); if (abs(w_at_i) > abs(row_chunk[0].m_value)) put_max_index_to_0(row_chunk, static_cast(row_chunk.size()) - 1); } w[i] = numeric_traits::zero(); } w.m_index.clear(); } template void square_sparse_matrix::replace_column(unsigned column_to_replace, indexed_vector & w, lp_settings &settings) { column_to_replace = adjust_column(column_to_replace); remove_elements_that_are_not_in_w_and_update_common_elements(column_to_replace, w); add_new_elements_of_w_and_clear_w(column_to_replace, w, settings); } template unsigned square_sparse_matrix::pivot_score(unsigned i, unsigned j) { // It goes like this (rnz-1)(cnz-1) is the Markovitz number, that is the max number of // new non zeroes we can obtain after the pivoting. // In addition we will get another cnz - 1 elements in the eta matrix created for this pivot, // which gives rnz(cnz-1). For example, is 0 for a column singleton, but not for // a row singleton ( which is not a column singleton). auto col_header = m_columns[j]; return static_cast(get_row_values(i).size() * (col_header.m_values.size() - col_header.m_shortened_markovitz - 1)); } template void square_sparse_matrix::enqueue_domain_into_pivot_queue() { lp_assert(m_pivot_queue.size() == 0); for (unsigned i = 0; i < dimension(); i++) { auto & rh = m_rows[i]; unsigned rnz = static_cast(rh.size()); for (auto iv : rh) { unsigned j = iv.m_index; m_pivot_queue.enqueue(i, j, rnz * (static_cast(m_columns[j].m_values.size()) - 1)); } } } template void square_sparse_matrix::set_max_in_rows() { unsigned i = dimension(); while (i--) set_max_in_row(i); } template void square_sparse_matrix::zero_shortened_markovitz_numbers() { for (auto & ch : m_columns) ch.zero_shortened_markovitz(); } template void square_sparse_matrix::prepare_for_factorization() { zero_shortened_markovitz_numbers(); set_max_in_rows(); enqueue_domain_into_pivot_queue(); } template void square_sparse_matrix::recover_pivot_queue(vector & rejected_pivots) { for (auto p : rejected_pivots) { m_pivot_queue.enqueue(p.first, p.second, pivot_score(p.first, p.second)); } } template int square_sparse_matrix::elem_is_too_small(unsigned i, unsigned j, int c_partial_pivoting) { auto & row_chunk = m_rows[i]; if (j == row_chunk[0].m_index) { return 0; // the max element is at the head } T max = abs(row_chunk[0].m_value); for (unsigned k = 1; k < row_chunk.size(); k++) { auto &iv = row_chunk[k]; if (iv.m_index == j) return abs(iv.m_value) * c_partial_pivoting < max ? 1: 0; } return 2; // the element became zero but it still sits in the active pivots? } template bool square_sparse_matrix::remove_row_from_active_pivots_and_shorten_columns(unsigned row) { unsigned arow = adjust_row(row); for (auto & iv : m_rows[arow]) { m_pivot_queue.remove(arow, iv.m_index); m_n_of_active_elems--; // the value is correct only when refactoring if (adjust_column_inverse(iv.m_index) <= row) continue; // this column will be removed anyway auto & col = m_columns[iv.m_index]; col.shorten_markovich_by_one(); if (col.m_values.size() <= col.m_shortened_markovitz) return false; // got a zero column } return true; } template void square_sparse_matrix::remove_pivot_column(unsigned row) { unsigned acol = adjust_column(row); for (const auto & iv : m_columns[acol].m_values) if (adjust_row_inverse(iv.m_index) >= row) m_pivot_queue.remove(iv.m_index, acol); } template void square_sparse_matrix::update_active_pivots(unsigned row) { unsigned arow = adjust_row(row); for (const auto & iv : m_rows[arow]) { col_header & ch = m_columns[iv.m_index]; int cols = static_cast(ch.m_values.size()) - ch.m_shortened_markovitz - 1; lp_assert(cols >= 0); for (const auto &ivc : ch.m_values) { unsigned i = ivc.m_index; if (adjust_row_inverse(i) <= row) continue; // the i is not an active row m_pivot_queue.enqueue(i, iv.m_index, static_cast(m_rows[i].size())*cols); } } } template bool square_sparse_matrix::shorten_active_matrix(unsigned row, eta_matrix *eta_matrix) { if (!remove_row_from_active_pivots_and_shorten_columns(row)) return false; remove_pivot_column(row); // need to know the max priority of the queue here update_active_pivots(row); if (eta_matrix == nullptr) return true; // it looks like double work, but the pivot scores have changed for all rows // touched by eta_matrix for (auto & it : eta_matrix->m_column_vector.m_data) { unsigned row = adjust_row(it.first); const auto & row_values = m_rows[row]; unsigned rnz = static_cast(row_values.size()); for (auto & iv : row_values) { const col_header& ch = m_columns[iv.m_index]; int cnz = static_cast(ch.m_values.size()) - ch.m_shortened_markovitz - 1; lp_assert(cnz >= 0); m_pivot_queue.enqueue(row, iv.m_index, rnz * cnz); } } return true; } template unsigned square_sparse_matrix::pivot_score_without_shortened_counters(unsigned i, unsigned j, unsigned k) { auto &cols = m_columns[j].m_values; unsigned cnz = cols.size(); for (auto & iv : cols) { if (adjust_row_inverse(iv.m_index) < k) cnz--; } lp_assert(cnz > 0); return m_rows[i].m_values.size() * (cnz - 1); } #ifdef Z3DEBUG template bool square_sparse_matrix::can_improve_score_for_row(unsigned row, unsigned score, T const & c_partial_pivoting, unsigned k) { unsigned arow = adjust_row(row); auto & row_vals = m_rows[arow].m_values; auto & begin_iv = row_vals[0]; T row_max = abs(begin_iv.m_value); lp_assert(adjust_column_inverse(begin_iv.m_index) >= k); if (pivot_score_without_shortened_counters(arow, begin_iv.m_index, k) < score) { print_active_matrix(k); return true; } for (unsigned jj = 1; jj < row_vals.size(); jj++) { auto & iv = row_vals[jj]; lp_assert(adjust_column_inverse(iv.m_index) >= k); lp_assert(abs(iv.m_value) <= row_max); if (c_partial_pivoting * abs(iv.m_value) < row_max) continue; if (pivot_score_without_shortened_counters(arow, iv.m_index, k) < score) { print_active_matrix(k); return true; } } return false; } template bool square_sparse_matrix::really_best_pivot(unsigned i, unsigned j, T const & c_partial_pivoting, unsigned k) { unsigned queue_pivot_score = pivot_score_without_shortened_counters(i, j, k); for (unsigned ii = k; ii < dimension(); ii++) { lp_assert(!can_improve_score_for_row(ii, queue_pivot_score, c_partial_pivoting, k)); } return true; } template void square_sparse_matrix::print_active_matrix(unsigned k, std::ostream & out) { out << "active matrix for k = " << k << std::endl; if (k >= dimension()) { out << "empty" << std::endl; return; } unsigned dim = dimension() - k; dense_matrix b(dim, dim); for (unsigned i = 0; i < dim; i++) for (unsigned j = 0; j < dim; j++ ) b.set_elem(i, j, zero_of_type()); for (int i = k; i < dimension(); i++) { unsigned col = adjust_column(i); for (auto &iv : get_column_values(col)) { unsigned row = iv.m_index; unsigned row_ex = this->adjust_row_inverse(row); if (row_ex < k) continue; auto v = this->get_not_adjusted(row, col); b.set_elem(row_ex - k, i -k, v); } } print_matrix(b, out); } template bool square_sparse_matrix::pivot_queue_is_correct_for_row(unsigned i, unsigned k) { unsigned arow = adjust_row(i); for (auto & iv : m_rows[arow].m_values) { lp_assert(pivot_score_without_shortened_counters(arow, iv.m_index, k + 1) == m_pivot_queue.get_priority(arow, iv.m_index)); } return true; } template bool square_sparse_matrix::pivot_queue_is_correct_after_pivoting(int k) { for (unsigned i = k + 1; i < dimension(); i++ ) lp_assert(pivot_queue_is_correct_for_row(i, k)); lp_assert(m_pivot_queue.is_correct()); return true; } #endif template bool square_sparse_matrix::get_pivot_for_column(unsigned &i, unsigned &j, int c_partial_pivoting, unsigned k) { vector pivots_candidates_that_are_too_small; while (!m_pivot_queue.is_empty()) { m_pivot_queue.dequeue(i, j); unsigned i_inv = adjust_row_inverse(i); if (i_inv < k) continue; unsigned j_inv = adjust_column_inverse(j); if (j_inv < k) continue; int _small = elem_is_too_small(i, j, c_partial_pivoting); if (!_small) { #ifdef Z3DEBUG // if (!really_best_pivot(i, j, c_partial_pivoting, k)) { // print_active_matrix(k); // lp_assert(false); // } #endif recover_pivot_queue(pivots_candidates_that_are_too_small); i = i_inv; j = j_inv; return true; } if (_small != 2) { // 2 means that the pair is not in the matrix pivots_candidates_that_are_too_small.push_back(std::make_pair(i, j)); } } recover_pivot_queue(pivots_candidates_that_are_too_small); return false; } template bool square_sparse_matrix::elem_is_too_small(vector> & row_chunk, indexed_value & iv, int c_partial_pivoting) { if (&iv == &row_chunk[0]) { return false; // the max element is at the head } T val = abs(iv.m_value); T max = abs(row_chunk[0].m_value); return val * c_partial_pivoting < max; } template bool square_sparse_matrix::shorten_columns_by_pivot_row(unsigned i, unsigned pivot_column) { vector> & row_chunk = get_row_values(i); for (indexed_value & iv : row_chunk) { unsigned j = iv.m_index; if (j == pivot_column) { lp_assert(!col_is_active(j)); continue; } m_columns[j].shorten_markovich_by_one(); if (m_columns[j].m_shortened_markovitz >= get_column_values(j).size()) { // got the zero column under the row! return false; } } return true; } template bool square_sparse_matrix::fill_eta_matrix(unsigned j, eta_matrix ** eta) { const vector> & col_chunk = get_column_values(adjust_column(j)); bool is_unit = true; for (const auto & iv : col_chunk) { unsigned i = adjust_row_inverse(iv.m_index); if (i > j) { is_unit = false; break; } if (i == j && iv.m_value != 1) { is_unit = false; break; } } if (is_unit) { *eta = nullptr; return true; } #ifdef Z3DEBUG *eta = new eta_matrix(j, dimension()); #else *eta = new eta_matrix(j); #endif for (const auto & iv : col_chunk) { unsigned i = adjust_row_inverse(iv.m_index); if (i < j) { continue; } if (i > j) { (*eta)->push_back(i, - iv.m_value); } else { // i == j if ( !(*eta)->set_diagonal_element(iv.m_value)) { delete *eta; *eta = nullptr; return false; } } } (*eta)->divide_by_diagonal_element(); return true; } #ifdef Z3DEBUG template bool square_sparse_matrix::is_upper_triangular_and_maximums_are_set_correctly_in_rows(lp_settings & settings) const { for (unsigned i = 0; i < dimension(); i++) { vector> const & row_chunk = get_row_values(i); lp_assert(row_chunk.size()); T const & max = abs(row_chunk[0].m_value); unsigned ai = adjust_row_inverse(i); for (auto & iv : row_chunk) { lp_assert(abs(iv.m_value) <= max); unsigned aj = adjust_column_inverse(iv.m_index); if (!(ai <= aj || numeric_traits::is_zero(iv.m_value))) return false; if (aj == ai) { if (iv.m_value != 1) { return false; } } if (settings.abs_val_is_smaller_than_drop_tolerance(iv.m_value) && (!is_zero(iv.m_value))) return false; } } return true; } template bool square_sparse_matrix::is_upper_triangular_until(unsigned k) const { for (unsigned j = 0; j < dimension() && j < k; j++) { unsigned aj = adjust_column(j); auto & col = get_column_values(aj); for (auto & iv : col) { unsigned row = adjust_row_inverse(iv.m_index); if (row > j) return false; } } return true; } template void square_sparse_matrix::check_column_vs_rows(unsigned col) { auto & mc = get_column_values(col); for (indexed_value & column_iv : mc) { indexed_value & row_iv = column_iv_other(column_iv); if (row_iv.m_index != col) { lp_assert(false); } if (& row_iv_other(row_iv) != &column_iv) { lp_assert(false); } if (row_iv.m_value != column_iv.m_value) { lp_assert(false); } } } template void square_sparse_matrix::check_row_vs_columns(unsigned row) { auto & mc = get_row_values(row); for (indexed_value & row_iv : mc) { indexed_value & column_iv = row_iv_other(row_iv); if (column_iv.m_index != row) { lp_assert(false); } if (& row_iv != & column_iv_other(column_iv)) { lp_assert(false); } if (row_iv.m_value != column_iv.m_value) { lp_assert(false); } } } template void square_sparse_matrix::check_rows_vs_columns() { for (unsigned i = 0; i < dimension(); i++) { check_row_vs_columns(i); } } template void square_sparse_matrix::check_columns_vs_rows() { for (unsigned i = 0; i < dimension(); i++) { check_column_vs_rows(i); } } template void square_sparse_matrix::check_matrix() { check_rows_vs_columns(); check_columns_vs_rows(); } #endif } z3-z3-4.8.7/src/util/lp/stacked_value.h000066400000000000000000000024071356505360400175740ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #pragma once // add to value the stack semantics #include namespace lp { template class stacked_value { T m_value; std::stack m_stack; public: void push() { m_stack.push(m_value); } void clear() { m_stack.clear(); } unsigned stack_size() const { return static_cast(m_stack.size()); } void pop() { pop(1); } void pop(unsigned k) { while (k-- > 0) { if (m_stack.empty()) return; m_value = m_stack.top(); m_stack.pop(); } } stacked_value() {} stacked_value(const T& m) { m_value = m; } stacked_value(const T&& m) { m_value = std::move(m); } T& operator=(T arg) { // copy/move constructor m_value = arg; return m_value; } operator T&() { return m_value; } operator const T&() const { return m_value; } T & operator()() { return m_value; } const T & operator()() const { return m_value; } }; } z3-z3-4.8.7/src/util/lp/stacked_vector.h000066400000000000000000000117671356505360400177730ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #pragma once #include #include #include #include "util/vector.h" namespace lp { template < typename B> class stacked_vector { svector m_stack_of_vector_sizes; svector m_stack_of_change_sizes; vector> m_changes; vector m_vector; public: class ref { stacked_vector & m_vec; unsigned m_i; public: ref(stacked_vector &m, unsigned key) :m_vec(m), m_i(key) { lp_assert(key < m.size()); } ref & operator=(const B & b) { m_vec.emplace_replace(m_i, b); return *this; } ref & operator=(const ref & b) { m_vec.emplace_replace(m_i, b.m_vec.m_vector[b.m_i]); return *this; } operator const B&() const { return m_vec.m_vector[m_i]; } bool operator==(B const& other) const { return m_vec.m_vector[m_i] == other; } B& operator+=(B const &delta) { // not tracking the change here! return m_vec.m_vector[m_i] += delta; } B& operator-=(B const &delta) { // not tracking the change here! return m_vec.m_vector[m_i] -= delta; } }; class ref_const { const stacked_vector & m_vec; unsigned m_i; public: ref_const(const stacked_vector &m, unsigned key) :m_vec(m), m_i(key) { lp_assert(key < m.size()); } operator const B&() const { return m_vec.m_vector[m_i]; } }; private: void emplace_replace(unsigned i,const B & b) { if (m_vector[i] != b) { m_changes.push_back(std::make_pair(i, m_vector[i])); m_vector[i] = b; } } public: ref operator[] (unsigned a) { return ref(*this, a); } ref_const operator[] (unsigned a) const { return ref_const(*this, a); } /* const B & operator[](unsigned a) const { lp_assert(a < m_vector.size()); return m_vector[a]; } */ unsigned size() const { return m_vector.size(); } void push() { m_stack_of_change_sizes.push_back(m_changes.size()); m_stack_of_vector_sizes.push_back(m_vector.size()); } void pop() { pop(1); } template void pop_tail(svector & v, unsigned k) { lp_assert(v.size() >= k); v.resize(v.size() - k); } template void resize(vector & v, unsigned new_size) { v.resize(new_size); } void pop_back() { unsigned last = m_vector.size() - 1; m_changes.push_back(std::make_pair(last, m_vector[last])); m_vector.pop_back(); } void pop(unsigned k) { lp_assert(m_stack_of_vector_sizes.size() >= k); lp_assert(k > 0); resize(m_vector, m_stack_of_vector_sizes[m_stack_of_vector_sizes.size() - k]); pop_tail(m_stack_of_vector_sizes, k); unsigned first_change = m_stack_of_change_sizes[m_stack_of_change_sizes.size() - k]; pop_tail(m_stack_of_change_sizes, k); for (unsigned j = m_changes.size(); j-- > first_change; ) { const auto & p = m_changes[j]; unsigned jc = p.first; if (jc < m_vector.size()) m_vector[jc] = p.second; // restore the old value } resize(m_changes, first_change); /* while (k-- > 0) { if (m_stack.empty()) return; delta & d = m_stack.back(); lp_assert(m_vector.size() >= d.m_size); while (m_vector.size() > d.m_size) m_vector.pop_back(); for (auto & t : d.m_original_changed) { lp_assert(t.first < m_vector.size()); m_vector[t.first] = t.second; } // lp_assert(d.m_deb_copy == m_vector); m_stack.pop_back();*/ } // void clear() { // if (m_stack.empty()) { // m_vector.clear(); // return; // } // delta & d = m_stack.top(); // auto & oc = d.m_original_changed; // for (auto & p : m_vector) { // const auto & it = oc.find(p.first); // if (it == oc.end() && d.m_new.find(p.first) == d.m_new.end()) // oc.emplace(p.first, p.second); // } // m_vector.clear(); // } void push_back(const B & b) { m_vector.push_back(b); } void increase_size_by_one() { m_vector.resize(m_vector.size() + 1); } unsigned peek_size(unsigned k) const { lp_assert(k > 0 && k <= m_stack_of_vector_sizes.size()); return m_stack_of_vector_sizes[m_stack_of_vector_sizes.size() - k]; } const vector& operator()() const { return m_vector; } }; } z3-z3-4.8.7/src/util/lp/static_matrix.cpp000066400000000000000000000104271356505360400201710ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #include #include "util/vector.h" #include #include #include "util/lp/static_matrix_def.h" #include "util/lp/lp_core_solver_base.h" #include "util/lp/lp_dual_core_solver.h" #include "util/lp/lp_dual_simplex.h" #include "util/lp/lp_primal_core_solver.h" #include "util/lp/scaler.h" #include "util/lp/lar_solver.h" namespace lp { template void static_matrix::add_columns_at_the_end(unsigned int); template void static_matrix::clear(); #ifdef Z3DEBUG template bool static_matrix::is_correct() const; #endif template void static_matrix::copy_column_to_indexed_vector(unsigned int, indexed_vector&) const; template double static_matrix::get_balance() const; template std::set> static_matrix::get_domain(); template std::set> lp::static_matrix::get_domain(); template std::set> lp::static_matrix >::get_domain(); template double static_matrix::get_elem(unsigned int, unsigned int) const; template double static_matrix::get_max_abs_in_column(unsigned int) const; template double static_matrix::get_min_abs_in_column(unsigned int) const; template double static_matrix::get_min_abs_in_row(unsigned int) const; template void static_matrix::init_empty_matrix(unsigned int, unsigned int); template void static_matrix::init_row_columns(unsigned int, unsigned int); template static_matrix::ref & static_matrix::ref::operator=(double const&); template void static_matrix::set(unsigned int, unsigned int, double const&); template static_matrix::static_matrix(unsigned int, unsigned int); template void static_matrix::add_column_to_vector(mpq const&, unsigned int, mpq*) const; template void static_matrix::add_columns_at_the_end(unsigned int); template bool static_matrix::is_correct() const; template void static_matrix::copy_column_to_indexed_vector(unsigned int, indexed_vector&) const; template mpq static_matrix::get_balance() const; template mpq static_matrix::get_elem(unsigned int, unsigned int) const; template mpq static_matrix::get_max_abs_in_column(unsigned int) const; template mpq static_matrix::get_max_abs_in_row(unsigned int) const; template double static_matrix::get_max_abs_in_row(unsigned int) const; template mpq static_matrix::get_min_abs_in_column(unsigned int) const; template mpq static_matrix::get_min_abs_in_row(unsigned int) const; template void static_matrix::init_row_columns(unsigned int, unsigned int); template static_matrix::ref& static_matrix::ref::operator=(mpq const&); template void static_matrix::set(unsigned int, unsigned int, mpq const&); template static_matrix::static_matrix(unsigned int, unsigned int); #ifdef Z3DEBUG template bool static_matrix >::is_correct() const; #endif template void static_matrix >::copy_column_to_indexed_vector(unsigned int, indexed_vector&) const; template mpq static_matrix >::get_elem(unsigned int, unsigned int) const; template void static_matrix >::init_empty_matrix(unsigned int, unsigned int); template void static_matrix >::set(unsigned int, unsigned int, mpq const&); template bool lp::static_matrix::pivot_row_to_row_given_cell(unsigned int, column_cell &, unsigned int); template bool lp::static_matrix::pivot_row_to_row_given_cell(unsigned int, column_cell& , unsigned int); template bool lp::static_matrix >::pivot_row_to_row_given_cell(unsigned int, column_cell&, unsigned int); template void lp::static_matrix >::remove_element(vector, true, unsigned int>&, lp::row_cell&); } z3-z3-4.8.7/src/util/lp/static_matrix.h000066400000000000000000000325331356505360400176400ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #pragma once #include "util/vector.h" #include #include #include #include "util/lp/sparse_vector.h" #include "util/lp/indexed_vector.h" #include "util/lp/permutation_matrix.h" #include namespace lp { template struct row_cell { unsigned m_j; // points to the column unsigned m_offset; // offset in column T m_value; row_cell(unsigned j, unsigned offset, T const & val) : m_j(j), m_offset(offset), m_value(val) { } row_cell(unsigned j, unsigned offset) : m_j(j), m_offset(offset) { } const T & get_val() const { return m_value;} T & get_val() { return m_value;} const T & coeff() const { return m_value;} T & coeff() { return m_value;} unsigned var() const { return m_j;} unsigned & var() { return m_j;} unsigned offset() const { return m_offset;} unsigned & offset() { return m_offset;} }; struct empty_struct {}; typedef row_cell column_cell; template using row_strip = vector>; // each assignment for this matrix should be issued only once!!! template class static_matrix #ifdef Z3DEBUG : public matrix #endif { struct dim { unsigned m_m; unsigned m_n; dim(unsigned m, unsigned n) :m_m(m), m_n(n) {} }; std::stack m_stack; public: typedef vector column_strip; vector m_vector_of_row_offsets; indexed_vector m_work_vector; vector> m_rows; vector m_columns; // starting inner classes class ref { static_matrix & m_matrix; unsigned m_row; unsigned m_col; public: ref(static_matrix & m, unsigned row, unsigned col):m_matrix(m), m_row(row), m_col(col) {} ref & operator=(T const & v) { m_matrix.set( m_row, m_col, v); return *this; } ref operator=(ref & v) { m_matrix.set(m_row, m_col, v.m_matrix.get(v.m_row, v.m_col)); return *this; } operator T () const { return m_matrix.get_elem(m_row, m_col); } }; class ref_row { const static_matrix & m_matrix; unsigned m_row; public: ref_row(const static_matrix & m, unsigned row): m_matrix(m), m_row(row) {} T operator[](unsigned col) const { return m_matrix.get_elem(m_row, col); } }; public: const T & get_val(const column_cell & c) const { return m_rows[c.var()][c.m_offset].get_val(); } column_cell & get_column_cell(const row_cell &rc) { return m_columns[rc.m_j][rc.m_offset]; } void init_row_columns(unsigned m, unsigned n); // constructor with no parameters static_matrix() {} // constructor static_matrix(unsigned m, unsigned n): m_vector_of_row_offsets(n, -1) { init_row_columns(m, n); } // constructor that copies columns of the basis from A static_matrix(static_matrix const &A, unsigned * basis); void clear(); void init_vector_of_row_offsets(); void init_empty_matrix(unsigned m, unsigned n); unsigned row_count() const { return static_cast(m_rows.size()); } unsigned column_count() const { return static_cast(m_columns.size()); } unsigned lowest_row_in_column(unsigned col); void add_columns_at_the_end(unsigned delta); void add_new_element(unsigned i, unsigned j, const T & v); void add_row() {m_rows.push_back(row_strip());} void add_column() { m_columns.push_back(column_strip()); m_vector_of_row_offsets.push_back(-1); } void forget_last_columns(unsigned how_many_to_forget); void remove_last_column(unsigned j); void remove_element(vector> & row, row_cell & elem_to_remove); void multiply_column(unsigned column, T const & alpha) { for (auto & t : m_columns[column]) { auto & r = m_rows[t.var()][t.offset()]; r.coeff() *= alpha; } } #ifdef Z3DEBUG void regen_domain(); #endif // offs - offset in columns row_cell make_row_cell(unsigned row, unsigned offs, T const & val) { return row_cell(row, offs, val); } column_cell make_column_cell(unsigned column, unsigned offset) { return column_cell(column, offset); } void set(unsigned row, unsigned col, T const & val); ref operator()(unsigned row, unsigned col) { return ref(*this, row, col); } std::set> get_domain(); void copy_column_to_indexed_vector(unsigned j, indexed_vector & v) const; T get_max_abs_in_row(unsigned row) const; void add_column_to_vector (const T & a, unsigned j, T * v) const { for (const auto & it : m_columns[j]) { v[it.var()] += a * get_val(it); } } T get_min_abs_in_row(unsigned row) const; T get_max_abs_in_column(unsigned column) const; T get_min_abs_in_column(unsigned column) const; #ifdef Z3DEBUG void check_consistency(); #endif void cross_out_row(unsigned k); // void fix_row_indices_in_each_column_for_crossed_row(unsigned k); void cross_out_row_from_columns(unsigned k, row_strip & row); void cross_out_row_from_column(unsigned col, unsigned k); T get_elem(unsigned i, unsigned j) const; unsigned number_of_non_zeroes_in_column(unsigned j) const { return m_columns[j].size(); } unsigned number_of_non_zeroes_in_row(unsigned i) const { return m_rows[i].size(); } unsigned number_of_non_zeroes() const { unsigned ret = 0; for (unsigned i = 0; i < row_count(); i++) ret += number_of_non_zeroes_in_row(i); return ret; } void scan_row_to_work_vector(unsigned i); void clean_row_work_vector(unsigned i); #ifdef Z3DEBUG unsigned get_number_of_rows() const { return row_count(); } unsigned get_number_of_columns() const { return column_count(); } virtual void set_number_of_rows(unsigned /*m*/) { } virtual void set_number_of_columns(unsigned /*n*/) { } #endif T get_max_val_in_row(unsigned /* i */) const { lp_unreachable(); } T get_balance() const; T get_row_balance(unsigned row) const; bool is_correct() const; void push() { dim d(row_count(), column_count()); m_stack.push(d); } void pop_row_columns(const vector> & row) { for (auto & c : row) { unsigned j = c.m_j; auto & col = m_columns[j]; lp_assert(col[col.size() - 1].var() == m_rows.size() -1 ); // todo : start here!!!! col.pop_back(); } } void pop(unsigned k) { #ifdef Z3DEBUG std::set> pairs_to_remove_from_domain; #endif while (k-- > 0) { if (m_stack.empty()) break; unsigned m = m_stack.top().m_m; while (m < row_count()) { unsigned i = m_rows.size() -1 ; auto & row = m_rows[i]; pop_row_columns(row); m_rows.pop_back(); // delete the last row } unsigned n = m_stack.top().m_n; while (n < column_count()) m_columns.pop_back(); // delete the last column m_stack.pop(); } lp_assert(is_correct()); } void multiply_row(unsigned row, T const & alpha) { for (auto & t : m_rows[row]) { t.m_value *= alpha; } } void divide_row(unsigned row, T const & alpha) { for (auto & t : m_rows[row]) { t.m_value /= alpha; } } T dot_product_with_column(const vector & y, unsigned j) const { lp_assert(j < column_count()); T ret = numeric_traits::zero(); for (auto & it : m_columns[j]) { ret += y[it.var()] * get_val(it); // get_value_of_column_cell(it); } return ret; } // pivot row i to row ii bool pivot_row_to_row_given_cell(unsigned i, column_cell& c, unsigned); void scan_row_ii_to_offset_vector(const row_strip & rvals); void transpose_rows(unsigned i, unsigned ii) { auto t = m_rows[i]; m_rows[i] = m_rows[ii]; m_rows[ii] = t; // now fix the columns for (auto & rc : m_rows[i]) { column_cell & cc = m_columns[rc.m_j][rc.m_offset]; lp_assert(cc.var() == ii); cc.var() = i; } for (auto & rc : m_rows[ii]) { column_cell & cc = m_columns[rc.m_j][rc.m_offset]; lp_assert(cc.var() == i); cc.var() = ii; } } void fill_last_row_with_pivoting_loop_block(unsigned j, const vector & basis_heading) { int row_index = basis_heading[j]; if (row_index < 0) return; T & alpha = m_work_vector[j]; // the pivot alpha if (is_zero(alpha)) return; for (const auto & c : m_rows[row_index]) { if (c.m_j == j) { continue; } T & wv = m_work_vector.m_data[c.m_j]; bool was_zero = is_zero(wv); wv -= alpha * c.m_value; if (was_zero) m_work_vector.m_index.push_back(c.m_j); else { if (is_zero(wv)) { m_work_vector.erase_from_index(c.m_j); } } } alpha = zero_of_type(); m_work_vector.erase_from_index(j); } template void fill_last_row_with_pivoting(const term& row, unsigned bj, // the index of the basis column const vector & basis_heading) { lp_assert(numeric_traits::precise()); lp_assert(row_count() > 0); m_work_vector.resize(column_count()); T a; // we use the form -it + 1 = 0 m_work_vector.set_value(one_of_type(), bj); for (auto p : row) { m_work_vector.set_value(-p.coeff(), p.var()); // but take care of the basis 1 later } // now iterate with pivoting fill_last_row_with_pivoting_loop_block(bj, basis_heading); for (auto p : row) { fill_last_row_with_pivoting_loop_block(p.var(), basis_heading); } lp_assert(m_work_vector.is_OK()); unsigned last_row = row_count() - 1; for (unsigned j : m_work_vector.m_index) { set (last_row, j, m_work_vector.m_data[j]); } lp_assert(column_count() > 0); set(last_row, column_count() - 1, one_of_type()); } void copy_column_to_vector (unsigned j, vector & v) const { v.resize(row_count(), numeric_traits::zero()); for (auto & it : m_columns[j]) { const T& val = get_val(it); if (!is_zero(val)) v[it.var()] = val; } } template L dot_product_with_row(unsigned row, const vector & w) const { L ret = zero_of_type(); lp_assert(row < m_rows.size()); for (auto & it : m_rows[row]) { ret += w[it.m_j] * it.get_val(); } return ret; } struct column_cell_plus { const column_cell & m_c; const static_matrix& m_A; // constructor column_cell_plus(const column_cell & c, const static_matrix& A) : m_c(c), m_A(A) {} unsigned var() const { return m_c.var(); } const T & coeff() const { return m_A.m_rows[var()][m_c.m_offset].get_val(); } }; struct column_container { unsigned m_j; // the column index const static_matrix & m_A; column_container(unsigned j, const static_matrix& A) : m_j(j), m_A(A) { } struct const_iterator { // fields const column_cell *m_c; const static_matrix& m_A; //typedefs typedef const_iterator self_type; typedef column_cell_plus value_type; typedef const column_cell_plus reference; // typedef const column_cell* pointer; typedef int difference_type; typedef std::forward_iterator_tag iterator_category; reference operator*() const { return column_cell_plus(*m_c, m_A); } self_type operator++() { self_type i = *this; m_c++; return i; } self_type operator++(int) { m_c++; return *this; } const_iterator(const column_cell* it, const static_matrix& A) : m_c(it), m_A(A) {} bool operator==(const self_type &other) const { return m_c == other.m_c; } bool operator!=(const self_type &other) const { return !(*this == other); } }; const_iterator begin() const { return const_iterator(m_A.m_columns[m_j].begin(), m_A); } const_iterator end() const { return const_iterator(m_A.m_columns[m_j].end(), m_A); } }; column_container column(unsigned j) const { return column_container(j, *this); } ref_row operator[](unsigned i) const { return ref_row(*this, i);} typedef T coefftype; typedef X argtype; }; } z3-z3-4.8.7/src/util/lp/static_matrix_def.h000066400000000000000000000305771356505360400204640ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #include "util/vector.h" #include #include #include "util/lp/static_matrix.h" namespace lp { // each assignment for this matrix should be issued only once!!! template void static_matrix::init_row_columns(unsigned m, unsigned n) { lp_assert(m_rows.size() == 0 && m_columns.size() == 0); for (unsigned i = 0; i < m; i++){ m_rows.push_back(row_strip()); } for (unsigned j = 0; j < n; j++){ m_columns.push_back(column_strip()); } } template void static_matrix::scan_row_ii_to_offset_vector(const row_strip & rvals) { for (unsigned j = 0; j < rvals.size(); j++) m_vector_of_row_offsets[rvals[j].var()] = j; } template bool static_matrix::pivot_row_to_row_given_cell(unsigned i, column_cell & c, unsigned pivot_col) { unsigned ii = c.var(); lp_assert(i < row_count() && ii < column_count() && i != ii); T alpha = -get_val(c); lp_assert(!is_zero(alpha)); auto & rowii = m_rows[ii]; remove_element(rowii, rowii[c.m_offset]); scan_row_ii_to_offset_vector(rowii); unsigned prev_size_ii = rowii.size(); // run over the pivot row and update row ii for (const auto & iv : m_rows[i]) { unsigned j = iv.var(); if (j == pivot_col) continue; T alv = alpha * iv.m_value; lp_assert(!is_zero(iv.m_value)); int j_offs = m_vector_of_row_offsets[j]; if (j_offs == -1) { // it is a new element add_new_element(ii, j, alv); } else { rowii[j_offs].m_value += alv; } } // clean the work vector for (unsigned k = 0; k < prev_size_ii; k++) { m_vector_of_row_offsets[rowii[k].var()] = -1; } // remove zeroes for (unsigned k = rowii.size(); k-- > 0; ) { if (is_zero(rowii[k].m_value)) remove_element(rowii, rowii[k]); } return !rowii.empty(); } // constructor that copies columns of the basis from A template static_matrix::static_matrix(static_matrix const &A, unsigned * /* basis */) : m_vector_of_row_offsets(A.column_count(), numeric_traits::zero()) { unsigned m = A.row_count(); init_row_columns(m, m); while (m--) { for (auto & col : A.m_columns[m]){ set(col.var(), m, A.get_value_of_column_cell(col)); } } } template void static_matrix::clear() { m_vector_of_row_offsets.clear(); m_rows.clear(); m_columns.clear(); } template void static_matrix::init_vector_of_row_offsets() { m_vector_of_row_offsets.clear(); m_vector_of_row_offsets.resize(column_count(), -1); } template void static_matrix::init_empty_matrix(unsigned m, unsigned n) { init_vector_of_row_offsets(); init_row_columns(m, n); } template unsigned static_matrix::lowest_row_in_column(unsigned col) { lp_assert(col < column_count()); column_strip & colstrip = m_columns[col]; lp_assert(colstrip.size() > 0); unsigned ret = 0; for (auto & t : colstrip) { if (t.var() > ret) { ret = t.var(); } } return ret; } template void static_matrix::add_columns_at_the_end(unsigned delta) { for (unsigned i = 0; i < delta; i++) add_column(); } template void static_matrix::forget_last_columns(unsigned how_many_to_forget) { lp_assert(m_columns.size() >= how_many_to_forget); unsigned j = column_count() - 1; for (; how_many_to_forget > 0; how_many_to_forget--) { remove_last_column(j --); } } template void static_matrix::remove_last_column(unsigned j) { column_strip & col = m_columns.back(); for (auto & it : col) { auto & row = m_rows[it.var()]; unsigned offset = row.size() - 1; for (auto row_it = row.rbegin(); row_it != row.rend(); row_it ++) { if (row_it.var() == j) { row.erase(row.begin() + offset); break; } offset--; } } m_columns.pop_back(); m_vector_of_row_offsets.pop_back(); } template void static_matrix::set(unsigned row, unsigned col, T const & val) { if (numeric_traits::is_zero(val)) return; lp_assert(row < row_count() && col < column_count()); auto & r = m_rows[row]; unsigned offs_in_cols = static_cast(m_columns[col].size()); m_columns[col].push_back(make_column_cell(row, static_cast(r.size()))); r.push_back(make_row_cell(col, offs_in_cols, val)); } template std::set> static_matrix::get_domain() { std::set> ret; for (unsigned i = 0; i < m_rows.size(); i++) { for (auto &it : m_rows[i]) { ret.insert(std::make_pair(i, it.var())); } } return ret; } template void static_matrix::copy_column_to_indexed_vector (unsigned j, indexed_vector & v) const { lp_assert(j < m_columns.size()); for (auto & it : m_columns[j]) { const T& val = get_val(it); if (!is_zero(val)) v.set_value(val, it.var()); } } template T static_matrix::get_max_abs_in_row(unsigned row) const { T ret = numeric_traits::zero(); for (auto & t : m_rows[row]) { T a = abs(t.get_val()); if (a > ret) { ret = a; } } return ret; } template T static_matrix::get_min_abs_in_row(unsigned row) const { bool first_time = true; T ret = numeric_traits::zero(); for (auto & t : m_rows[row]) { T a = abs(t.get_val()); if (first_time) { ret = a; first_time = false; } else if (a < ret) { ret = a; } } return ret; } template T static_matrix::get_max_abs_in_column(unsigned column) const { T ret = numeric_traits::zero(); for (const auto & t : m_columns[column]) { T a = abs(get_val(t)); if (a > ret) { ret = a; } } return ret; } template T static_matrix::get_min_abs_in_column(unsigned column) const { bool first_time = true; T ret = numeric_traits::zero(); for (auto & t : m_columns[column]) { T a = abs(get_val(t)); if (first_time) { first_time = false; ret = a; } else if (a < ret) { ret = a; } } return ret; } #ifdef Z3DEBUG template void static_matrix::check_consistency() { std::unordered_map, T> by_rows; for (int i = 0; i < m_rows.size(); i++){ for (auto & t : m_rows[i]) { std::pair p(i, t.var()); lp_assert(by_rows.find(p) == by_rows.end()); by_rows[p] = t.get_val(); } } std::unordered_map, T> by_cols; for (int i = 0; i < m_columns.size(); i++){ for (auto & t : m_columns[i]) { std::pair p(t.var(), i); lp_assert(by_cols.find(p) == by_cols.end()); by_cols[p] = get_val(t); } } lp_assert(by_rows.size() == by_cols.size()); for (auto & t : by_rows) { auto ic = by_cols.find(t.first); lp_assert(ic != by_cols.end()); lp_assert(t.second == ic->second); } } #endif template void static_matrix::cross_out_row(unsigned k) { #ifdef Z3DEBUG check_consistency(); #endif cross_out_row_from_columns(k, m_rows[k]); fix_row_indices_in_each_column_for_crossed_row(k); m_rows.erase(m_rows.begin() + k); #ifdef Z3DEBUG regen_domain(); check_consistency(); #endif } template void static_matrix::fix_row_indices_in_each_column_for_crossed_row(unsigned k) { for (unsigned j = 0; j < m_columns.size(); j++) { auto & col = m_columns[j]; for (int i = 0; i < col.size(); i++) { if (col[i].var() > k) { col[i].var()--; } } } } template void static_matrix::cross_out_row_from_columns(unsigned k, row_strip & row) { for (auto & t : row) { cross_out_row_from_column(t.var(), k); } } template void static_matrix::cross_out_row_from_column(unsigned col, unsigned k) { auto & s = m_columns[col]; for (unsigned i = 0; i < s.size(); i++) { if (s[i].var() == k) { s.erase(s.begin() + i); break; } } } template T static_matrix::get_elem(unsigned i, unsigned j) const { // should not be used in efficient code !!!! for (auto & t : m_rows[i]) { if (t.var() == j) { return t.get_val(); } } return numeric_traits::zero(); } template T static_matrix::get_balance() const { T ret = zero_of_type(); for (unsigned i = 0; i < row_count(); i++) { ret += get_row_balance(i); } return ret; } template T static_matrix::get_row_balance(unsigned row) const { T ret = zero_of_type(); for (auto & t : m_rows[row]) { if (numeric_traits::is_zero(t.get_val())) continue; T a = abs(t.get_val()); numeric_traits::log(a); ret += a * a; } return ret; } template bool static_matrix::is_correct() const { for (unsigned i = 0; i < m_rows.size(); i++) { auto &r = m_rows[i]; std::unordered_set s; for (auto & rc : r) { if (s.find(rc.var()) != s.end()) { return false; } s.insert(rc.var()); if (rc.var() >= m_columns.size()) return false; if (rc.m_offset >= m_columns[rc.var()].size()) return false; if (rc.get_val() != get_val(m_columns[rc.var()][rc.m_offset])) return false; if (is_zero(rc.get_val())) { return false; } } } for (unsigned j = 0; j < m_columns.size(); j++) { auto & c = m_columns[j]; std::unordered_set s; for (auto & cc : c) { if (s.find(cc.var()) != s.end()) { return false; } s.insert(cc.var()); if (cc.var() >= m_rows.size()) return false; if (cc.m_offset >= m_rows[cc.var()].size()) return false; if (get_val(cc) != m_rows[cc.var()][cc.m_offset].get_val()) return false; } } return true; } template void static_matrix::remove_element(vector> & row_vals, row_cell & row_el_iv) { unsigned column_offset = row_el_iv.m_offset; auto & column_vals = m_columns[row_el_iv.var()]; column_cell& cs = m_columns[row_el_iv.var()][column_offset]; unsigned row_offset = cs.m_offset; if (column_offset != column_vals.size() - 1) { auto & cc = column_vals[column_offset] = column_vals.back(); // copy from the tail m_rows[cc.var()][cc.offset()].offset() = column_offset; } if (row_offset != row_vals.size() - 1) { auto & rc = row_vals[row_offset] = row_vals.back(); // copy from the tail m_columns[rc.var()][rc.offset()].offset() = row_offset; } column_vals.pop_back(); row_vals.pop_back(); } template void static_matrix::add_new_element(unsigned row, unsigned col, const T& val) { auto & row_vals = m_rows[row]; auto & col_vals = m_columns[col]; unsigned row_el_offs = static_cast(row_vals.size()); unsigned col_el_offs = static_cast(col_vals.size()); row_vals.push_back(row_cell(col, col_el_offs, val)); col_vals.push_back(column_cell(row, row_el_offs)); } } z3-z3-4.8.7/src/util/lp/tail_matrix.h000066400000000000000000000021561356505360400173000ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #pragma once #include "util/vector.h" #include "util/lp/indexed_vector.h" #include "util/lp/matrix.h" #include "util/lp/lp_settings.h" // These matrices appear at the end of the list namespace lp { template class tail_matrix #ifdef Z3DEBUG : public matrix #endif { public: virtual void apply_from_left_to_T(indexed_vector & w, lp_settings & settings) = 0; virtual void apply_from_left(vector & w, lp_settings & settings) = 0; virtual void apply_from_right(vector & w) = 0; virtual void apply_from_right(indexed_vector & w) = 0; virtual ~tail_matrix() {} virtual bool is_dense() const = 0; struct ref_row { const tail_matrix & m_A; unsigned m_row; ref_row(const tail_matrix& m, unsigned row): m_A(m), m_row(row) {} T operator[](unsigned j) const { return m_A.get_elem(m_row, j);} }; ref_row operator[](unsigned i) const { return ref_row(*this, i);} }; } z3-z3-4.8.7/src/util/lp/test_bound_analyzer.h000066400000000000000000000207501356505360400210360ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #if 0 #pragma once #include "util/vector.h" #include "util/lp/implied_bound.h" #include "util/lp/lp_settings.h" #include // this class is for testing only // We have an equality : sum by j of row[j]*x[j] = rs // We try to pin a var by pushing the total by using the variable bounds // In a loop we drive the partial sum down, denoting the variables of this process by _u. // In the same loop trying to pin variables by pushing the partial sum up, denoting the variable related to it by _l // here in addition we assume that all coefficient in the row are positive namespace lp { class test_bound_analyzer { linear_combination_iterator & m_it; std::function& m_lower_bounds; std::function& m_upper_bounds; std::function m_column_types; vector & m_implied_bounds; vector m_coeffs; int m_coeff_sign; vector m_index; unsigned m_row_or_term_index; std::function & m_try_get_found_bound; public : // constructor test_bound_analyzer(linear_combination_iterator &it, std::function & lower_bounds, std::function & upper_bounds, std::function column_types, vector & evidence_vector, unsigned row_or_term_index, std::function & try_get_found_bound) : m_it(it), m_lower_bounds(lower_bounds), m_upper_bounds(upper_bounds), m_column_types(column_types), m_implied_bounds(evidence_vector), m_row_or_term_index(row_or_term_index), m_try_get_found_bound(try_get_found_bound) { m_it.reset(); unsigned i; mpq a; while (m_it.next(a, i) ) { m_coeffs.push_back(a); m_index.push_back(i); } } static int sign (const mpq & t) { return is_pos(t) ? 1: -1;} void analyze() { // We have the equality sum by j of row[j]*x[j] = m_rs // We try to pin a var by pushing the total of the partial sum down, denoting the variable of this process by _u. for (unsigned i = 0; i < m_index.size(); i++) { analyze_i(i); } } void analyze_i(unsigned i) { // set the m_coeff_is_pos m_coeff_sign = sign(m_coeffs[i]); analyze_i_for_lower(i); analyze_i_for_upper(i); } void analyze_i_for_upper(unsigned i) { mpq l; bool strict = false; lp_assert(is_zero(l)); for (unsigned k = 0; k < m_index.size(); k++) { if (k == i) continue; mpq lb; bool str; if (!upper_bound_of_monoid(k, lb, str)) { return; } l += lb; if (str) strict = true; } l /= m_coeffs[i]; unsigned j = m_index[i]; switch(m_column_types(j)) { case column_type::fixed: case column_type::boxed: case column_type::upper_bound: { const auto & lb = m_upper_bounds(j); if (l > lb.x || (l == lb.x && !(is_zero(lb.y) && strict))) { break; // no improvement on the existing upper bound } } default: m_implied_bounds.push_back(implied_bound(l, j, false, is_pos(m_coeffs[i]), m_row_or_term_index, strict)); } } bool lower_bound_of_monoid(unsigned k, mpq & lb, bool &strict) const { int s = - m_coeff_sign * sign(m_coeffs[k]); unsigned j = m_index[k]; if (s > 0) { switch(m_column_types(j)) { case column_type::fixed: case column_type::boxed: case column_type::lower_bound: lb = -m_coeffs[k] * m_lower_bounds(j).x; strict = !is_zero(m_lower_bounds(j).y); return true; default: return false; } } switch(m_column_types(j)) { case column_type::fixed: case column_type::boxed: case column_type::upper_bound: lb = -m_coeffs[k] * m_upper_bounds(j).x; strict = !is_zero(m_upper_bounds(j).y); return true; default: return false; } } bool upper_bound_of_monoid(unsigned k, mpq & lb, bool & strict) const { int s = - m_coeff_sign * sign(m_coeffs[k]); unsigned j = m_index[k]; if (s > 0) { switch(m_column_types(j)) { case column_type::fixed: case column_type::boxed: case column_type::upper_bound: lb = -m_coeffs[k] * m_upper_bounds(j).x; strict = !is_zero(m_upper_bounds(j).y); return true; default: return false; } } switch(m_column_types(j)) { case column_type::fixed: case column_type::boxed: case column_type::lower_bound: lb = -m_coeffs[k] * m_lower_bounds(j).x; strict = !is_zero(m_lower_bounds(j).y); return true; default: return false; } } void analyze_i_for_lower(unsigned i) { mpq l; lp_assert(is_zero(l)); bool strict = false; for (unsigned k = 0; k < m_index.size(); k++) { if (k == i) continue; mpq lb; bool str; if (!lower_bound_of_monoid(k, lb, str)) { return; } if (str) strict = true; l += lb; } l /= m_coeffs[i]; unsigned j = m_index[i]; switch(m_column_types(j)) { case column_type::fixed: case column_type::boxed: case column_type::lower_bound: { const auto & lb = m_lower_bounds(j); if (l < lb.x || (l == lb.x && !(is_zero(lb.y) && strict))) { break; // no improvement on the existing upper bound } } default: m_implied_bounds.push_back(implied_bound(l, j, true, is_pos(m_coeffs[i]), m_row_or_term_index, strict)); } } bool lower_bound_is_available(unsigned j) const { switch(m_column_types(j)) { case column_type::fixed: case column_type::boxed: case column_type::lower_bound: return true; default: return false; } } bool upper_bound_is_available(unsigned j) const { switch(m_column_types(j)) { case column_type::fixed: case column_type::boxed: case column_type::upper_bound: return true; default: return false; } } bool try_get_best_avail_bound(unsigned j, bool is_lower_bound, mpq & best_bound, bool & strict_of_best_bound) const { if (m_try_get_found_bound(j, is_lower_bound, best_bound, strict_of_best_bound)) { return true; } if (is_lower_bound) { if (lower_bound_is_available(j)) { best_bound = m_lower_bounds(j).x; strict_of_best_bound = !is_zero(m_lower_bounds(j).y); return true; } } else { if (upper_bound_is_available(j)) { best_bound = m_upper_bounds(j).x; strict_of_best_bound = !is_zero(m_lower_bounds(j).y); return true; } } return false; } bool bound_is_new(unsigned j, const mpq& b, bool is_lower_bound, bool strict) const { mpq best_bound; bool strict_of_best_bound; if (try_get_best_avail_bound(j, is_lower_bound, best_bound, strict_of_best_bound)) { if (is_lower_bound) { if (b > best_bound || ( b != best_bound && (strict && !strict_of_best_bound))) // the second clouse stands for strong inequality return true; } else { if (b < best_bound || ( b == best_bound && (strict && !strict_of_best_bound))) return true; } return false; } return true; } }; } #endif z3-z3-4.8.7/src/util/lp/ul_pair.h000066400000000000000000000042001356505360400164060ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #pragma once #include "util/vector.h" #include #include #include #include "util/lp/column_info.h" namespace lp { enum lconstraint_kind { LE = -2, LT = -1 , GE = 2, GT = 1, EQ = 0 }; inline bool kind_is_strict(lconstraint_kind kind) { return kind == LT || kind == GT;} inline std::ostream& operator<<(std::ostream& out, lconstraint_kind k) { switch (k) { case LE: return out << "<="; case LT: return out << "<"; case GE: return out << ">="; case GT: return out << ">"; case EQ: return out << "="; } return out << "??"; } inline bool compare(const std::pair & a, const std::pair & b) { return a.second < b.second; } class ul_pair { constraint_index m_lower_bound_witness; constraint_index m_upper_bound_witness; public: constraint_index& lower_bound_witness() {return m_lower_bound_witness;} constraint_index lower_bound_witness() const {return m_lower_bound_witness;} constraint_index& upper_bound_witness() { return m_upper_bound_witness;} constraint_index upper_bound_witness() const {return m_upper_bound_witness;} row_index m_i; bool operator!=(const ul_pair & p) const { return !(*this == p); } bool operator==(const ul_pair & p) const { return m_lower_bound_witness == p.m_lower_bound_witness && m_upper_bound_witness == p.m_upper_bound_witness && m_i == p.m_i; } // empty constructor ul_pair() : m_lower_bound_witness(static_cast(-1)), m_upper_bound_witness(static_cast(-1)), m_i(static_cast(-1)) {} ul_pair(row_index ri) : m_lower_bound_witness(static_cast(-1)), m_upper_bound_witness(static_cast(-1)), m_i(ri) {} ul_pair(const ul_pair & o): m_lower_bound_witness(o.m_lower_bound_witness), m_upper_bound_witness(o.m_upper_bound_witness), m_i(o.m_i) {} }; } z3-z3-4.8.7/src/util/lp/var_register.h000066400000000000000000000055351356505360400174630ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: Abstract: Author: Lev Nachmanson (levnach) Revision History: --*/ #pragma once namespace lp { class ext_var_info { unsigned m_external_j; // the internal index bool m_is_integer; public: ext_var_info() {} ext_var_info(unsigned j): ext_var_info(j, true) {} ext_var_info(unsigned j , bool is_int) : m_external_j(j), m_is_integer(is_int) {} unsigned external_j() const { return m_external_j;} bool is_integer() const {return m_is_integer;} }; class var_register { svector m_local_to_external; std::unordered_map m_external_to_local; public: unsigned add_var(unsigned user_var) { return add_var(user_var, true); } unsigned add_var(unsigned user_var, bool is_int) { auto t = m_external_to_local.find(user_var); if (t != m_external_to_local.end()) { return t->second; } m_local_to_external.push_back(ext_var_info(user_var, is_int)); return m_external_to_local[user_var] = size() - 1; } svector vars() const { svector ret; for (const auto& p : m_local_to_external) { ret.push_back(p.external_j()); } return ret; } unsigned local_to_external(unsigned local_var) const { return m_local_to_external[local_var].external_j(); } unsigned size() const { return m_local_to_external.size(); } void clear() { m_local_to_external.clear(); m_external_to_local.clear(); } unsigned external_to_local(unsigned j) const { auto it = m_external_to_local.find(j); lp_assert(it != m_external_to_local.end()); return it->second; } bool external_is_used(unsigned ext_j) const { auto it = m_external_to_local.find(ext_j); return it != m_external_to_local.end(); } bool external_is_used(unsigned ext_j, unsigned & local_j ) const { auto it = m_external_to_local.find(ext_j); if ( it == m_external_to_local.end()) return false; local_j = it->second; return true; } bool external_is_used(unsigned ext_j, unsigned & local_j, bool & is_int ) const { auto it = m_external_to_local.find(ext_j); if ( it == m_external_to_local.end()) return false; local_j = it->second; is_int = m_local_to_external[local_j].is_integer(); return true; } bool local_is_int(unsigned j) const { return m_local_to_external[j].is_integer(); } void shrink(unsigned shrunk_size) { for (unsigned j = size(); j-- > shrunk_size;) { m_external_to_local.erase(m_local_to_external[j].external_j()); } m_local_to_external.resize(shrunk_size); } }; } z3-z3-4.8.7/src/util/luby.cpp000066400000000000000000000010531356505360400156510ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: luby.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-03-04. Revision History: --*/ #include unsigned get_luby(unsigned i) { if (i == 1) return 1; double k = log(static_cast(i+1))/log(static_cast(2)); if (k == floor(k + 0.5)) return static_cast(pow(2,k-1)); else { k = static_cast(floor(k)); return get_luby(i - static_cast(pow(2, k)) + 1); } } z3-z3-4.8.7/src/util/luby.h000066400000000000000000000007601356505360400153220ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: luby.h Abstract: Author: Leonardo de Moura (leonardo) 2008-03-04. Revision History: --*/ #ifndef LUBY_H_ #define LUBY_H_ /** \brief Return the i-th element of the Luby sequence: 1,1,2,1,1,2,4,1,1,2,1,1,2,4,8,... get_luby(i) = 2^{i-1} if i = 2^k -1 get_luby(i) = get_luby(i - 2^{k-1} + 1) if 2^{k-1} <= i < 2^k - 1 */ unsigned get_luby(unsigned i); #endif /* LUBY_H_ */ z3-z3-4.8.7/src/util/machine.h000066400000000000000000000005661356505360400157570ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: machine.h Abstract: Machine/OS dependent configuration Author: Leonardo de Moura (leonardo) 2006-09-13. Revision History: --*/ #ifndef MACHINE_H_ #define MACHINE_H_ #if defined(__LP64__) || defined(_WIN64) #define PTR_ALIGNMENT 3 #else #define PTR_ALIGNMENT 2 #endif #endif /* MACHINE_H_ */ z3-z3-4.8.7/src/util/map.h000066400000000000000000000167211356505360400151300ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: map.h Abstract: Simple mapping based on the hashtable. Author: Leonardo de Moura (leonardo) 2006-10-14. Revision History: --*/ #ifndef MAP_H_ #define MAP_H_ #include "util/hashtable.h" template struct _key_data { Key m_key; Value m_value; _key_data() { } _key_data(Key const & k): m_key(k) { } _key_data(Key const & k, Value const & v): m_key(k), m_value(v) { } }; template class table2map { public: typedef Entry entry; typedef typename Entry::key key; typedef typename Entry::value value; typedef typename Entry::key_data key_data; struct entry_hash_proc : private HashProc { entry_hash_proc(HashProc const & p): HashProc(p) { } unsigned operator()(key_data const & d) const { return HashProc::operator()(d.m_key); } }; struct entry_eq_proc : private EqProc { entry_eq_proc(EqProc const & p): EqProc(p) { } bool operator()(key_data const & d1, key_data const & d2) const { return EqProc::operator()(d1.m_key, d2.m_key); } }; typedef core_hashtable table; table m_table; public: table2map(HashProc const & h = HashProc(), EqProc const & e = EqProc()): m_table(DEFAULT_HASHTABLE_INITIAL_CAPACITY, entry_hash_proc(h), entry_eq_proc(e)) { } typedef typename table::iterator iterator; void reset() { m_table.reset(); } void finalize() { m_table.finalize(); } bool empty() const { return m_table.empty(); } unsigned size() const { return m_table.size(); } unsigned capacity() const { return m_table.capacity(); } iterator begin() const { return m_table.begin(); } iterator end() const { return m_table.end(); } void insert(key const & k, value const & v) { m_table.insert(key_data(k, v)); } bool insert_if_not_there_core(key const & k, value const & v, entry *& et) { return m_table.insert_if_not_there_core(key_data(k,v), et); } key_data const & insert_if_not_there(key const & k, value const & v) { return m_table.insert_if_not_there(key_data(k, v)); } entry * insert_if_not_there2(key const & k, value const & v) { return m_table.insert_if_not_there2(key_data(k, v)); } entry * find_core(key const & k) const { return m_table.find_core(key_data(k)); } bool find(key const & k, value & v) const { entry * e = find_core(k); if (e) { v = e->get_data().m_value; } return (nullptr != e); } value const& get(key const& k, value const& default_value) const { entry* e = find_core(k); if (e) { return e->get_data().m_value; } else { return default_value; } } iterator find_iterator(key const & k) const { return m_table.find(key_data(k)); } value const & find(key const& k) const { entry * e = find_core(k); SASSERT(e); return e->get_data().m_value; } value & find(key const& k) { entry * e = find_core(k); SASSERT(e); return e->get_data().m_value; } value const& operator[](key const& k) const { return find(k); } value& operator[](key const& k) { return find(k); } bool contains(key const & k) const { return find_core(k) != nullptr; } void remove(key const & k) { m_table.remove(key_data(k)); } void erase(key const & k) { remove(k); } unsigned long long get_num_collision() const { return m_table.get_num_collision(); } void swap(table2map & other) { m_table.swap(other.m_table); } #ifdef Z3DEBUG bool check_invariant() { return m_table.check_invariant(); } #endif }; template class default_map_entry : public default_hash_entry<_key_data > { public: typedef Key key; typedef Value value; typedef _key_data key_data; }; template class map : public table2map, HashProc, EqProc> { public: map(HashProc const & h = HashProc(), EqProc const & e = EqProc()): table2map, HashProc, EqProc>(h, e) { } }; template struct _key_ptr_data { Key * m_key; Value m_value; _key_ptr_data(): m_key(0) { } _key_ptr_data(Key * k): m_key(k) { } _key_ptr_data(Key * k, Value const & v): m_key(k), m_value(v) { } }; template class ptr_map_entry { public: typedef _key_ptr_data key_data; typedef _key_ptr_data data; private: unsigned m_hash; //!< cached hash code data m_data; public: typedef Key * key; typedef Value value; unsigned get_hash() const { return m_hash; } bool is_free() const { return m_data.m_key == 0; } bool is_deleted() const { return m_data.m_key == reinterpret_cast(1); } bool is_used() const { return m_data.m_key != reinterpret_cast(0) && m_data.m_key != reinterpret_cast(1); } key_data const & get_data() const { return m_data; } key_data & get_data() { return m_data; } void set_data(key_data const & d) { m_data = d; } void set_hash(unsigned h) { m_hash = h; } void mark_as_deleted() { m_data.m_key = reinterpret_cast(1); } void mark_as_free() { m_data.m_key = 0; } }; template class ptr_addr_map_entry { public: typedef _key_ptr_data key_data; typedef _key_ptr_data data; private: data m_data; public: typedef Key * key; typedef Value value; unsigned get_hash() const { return get_ptr_hash(m_data.m_key); } bool is_free() const { return m_data.m_key == 0; } bool is_deleted() const { return m_data.m_key == reinterpret_cast(1); } bool is_used() const { return m_data.m_key != reinterpret_cast(0) && m_data.m_key != reinterpret_cast(1); } key_data const & get_data() const { return m_data; } key_data & get_data() { return m_data; } void set_data(key_data const & d) { m_data = d; } void set_hash(unsigned h) { SASSERT(h == get_ptr_hash(m_data.m_key)); /* do nothing */ } void mark_as_deleted() { m_data.m_key = reinterpret_cast(1); } void mark_as_free() { m_data.m_key = 0; } }; template class ptr_addr_map : public table2map, ptr_hash, ptr_eq > { public: }; struct u_hash { unsigned operator()(unsigned u) const { return u; } }; struct u_eq { bool operator()(unsigned u1, unsigned u2) const { return u1 == u2; } }; struct size_t_eq { bool operator()(size_t u1, size_t u2) const { return u1 == u2; } }; struct int_eq { bool operator()(int u1, int u2) const { return u1 == u2; } }; template class u_map : public map {}; template class size_t_map : public map {}; #endif z3-z3-4.8.7/src/util/max_cliques.h000066400000000000000000000073521356505360400166650ustar00rootroot00000000000000/*++ Copyright (c) 2016 Microsoft Corporation Module Name: max_cliques.h Abstract: Utility for enumerating locally maximal sub cliques. Author: Nikolaj Bjorner (nbjorner) 2016-11-18 Notes: --*/ #include "util/vector.h" #include "util/uint_set.h" template class max_cliques : public T { using T::negate; vector m_next, m_tc; uint_set m_reachable[2]; uint_set m_seen1, m_seen2; unsigned_vector m_todo; void get_reachable(unsigned p, uint_set const& goal, uint_set& reachable) { m_seen1.reset(); m_todo.reset(); m_todo.push_back(p); for (unsigned i = 0; i < m_todo.size(); ++i) { p = m_todo[i]; if (m_seen1.contains(p)) { continue; } m_seen1.insert(p); if (m_seen2.contains(p)) { unsigned_vector const& tc = m_tc[p]; for (unsigned j = 0; j < tc.size(); ++j) { unsigned np = tc[j]; if (goal.contains(np)) { reachable.insert(np); } } } else { unsigned np = negate(p); if (goal.contains(np)) { reachable.insert(np); } m_todo.append(next(np)); } } for (unsigned i = m_todo.size(); i > 0; ) { --i; p = m_todo[i]; if (m_seen2.contains(p)) { continue; } m_seen2.insert(p); unsigned np = negate(p); unsigned_vector& tc = m_tc[p]; if (goal.contains(np)) { tc.push_back(np); } else { unsigned_vector const& succ = next(np); for (unsigned j = 0; j < succ.size(); ++j) { tc.append(m_tc[succ[j]]); } } } } unsigned_vector const& next(unsigned vertex) const { return m_next[vertex]; } public: max_cliques() {} void add_edge(unsigned src, unsigned dst) { m_next.reserve(std::max(src, dst) + 1); m_next.reserve(std::max(negate(src), negate(dst)) + 1); m_next[src].push_back(dst); m_next[dst].push_back(src); } void cliques(unsigned_vector const& ps, vector& cliques) { unsigned max = 0; unsigned num_ps = ps.size(); for (unsigned i = 0; i < num_ps; ++i) { unsigned p = ps[i]; unsigned np = negate(p); max = std::max(max, std::max(np, p) + 1); } m_next.reserve(max); m_tc.reserve(m_next.size()); unsigned_vector clique; uint_set vars; for (unsigned i = 0; i < num_ps; ++i) { vars.insert(ps[i]); } while (!vars.empty()) { clique.reset(); bool turn = false; m_reachable[turn] = vars; while (!m_reachable[turn].empty()) { unsigned p = *m_reachable[turn].begin(); m_reachable[turn].remove(p); vars.remove(p); clique.push_back(p); if (m_reachable[turn].empty()) { break; } m_reachable[!turn].reset(); get_reachable(p, m_reachable[turn], m_reachable[!turn]); turn = !turn; } if (clique.size() > 1) { if (clique.size() == 2 && clique[0] == negate(clique[1])) { // no op } else { cliques.push_back(clique); } } } } }; z3-z3-4.8.7/src/util/memory_manager.cpp000066400000000000000000000257611356505360400177140ustar00rootroot00000000000000 /*++ Copyright (c) 2015 Microsoft Corporation --*/ #include #include #include #include "util/mutex.h" #include "util/trace.h" #include "util/memory_manager.h" #include "util/error_codes.h" #include "util/debug.h" // The following two function are automatically generated by the mk_make.py script. // The script collects ADD_INITIALIZER and ADD_FINALIZER commands in the .h files. // For example, rational.h contains // ADD_INITIALIZER('rational::initialize();') // ADD_FINALIZER('rational::finalize();') // Thus, any executable or shared object (DLL) that depends on rational.h // will have an automatically generated file mem_initializer.cpp containing // mem_initialize() // mem_finalize() // and these functions will include the statements: // rational::initialize(); // // rational::finalize(); void mem_initialize(); void mem_finalize(); // If PROFILE_MEMORY is defined, Z3 will display the amount of memory used, and the number of synchronization steps during finalization // #define PROFILE_MEMORY out_of_memory_error::out_of_memory_error():z3_error(ERR_MEMOUT) { } static DECLARE_INIT_MUTEX(g_memory_mux); static atomic g_memory_out_of_memory(false); static bool g_memory_initialized = false; static long long g_memory_alloc_size = 0; static long long g_memory_max_size = 0; static long long g_memory_max_used_size = 0; static long long g_memory_watermark = 0; static long long g_memory_alloc_count = 0; static long long g_memory_max_alloc_count = 0; static bool g_exit_when_out_of_memory = false; static char const * g_out_of_memory_msg = "ERROR: out of memory"; void memory::exit_when_out_of_memory(bool flag, char const * msg) { g_exit_when_out_of_memory = flag; if (flag && msg) g_out_of_memory_msg = msg; } static void throw_out_of_memory() { g_memory_out_of_memory = true; if (g_exit_when_out_of_memory) { std::cerr << g_out_of_memory_msg << "\n"; exit(ERR_MEMOUT); } else { throw out_of_memory_error(); } } static void throw_alloc_counts_exceeded() { std::cout << "Maximal allocation counts " << g_memory_max_alloc_count << " have been exceeded\n"; exit(ERR_ALLOC_EXCEEDED); } #ifdef PROFILE_MEMORY static unsigned g_synch_counter = 0; class mem_usage_report { public: ~mem_usage_report() { std::cerr << "(memory :max " << g_memory_max_used_size << " :allocs " << g_memory_alloc_count << " :final " << g_memory_alloc_size << " :synch " << g_synch_counter << ")" << std::endl; } }; mem_usage_report g_info; #endif void memory::initialize(size_t max_size) { static mutex init_mux; lock_guard lock(init_mux); // only update the maximum size if max_size != UINT_MAX if (max_size != UINT_MAX) g_memory_max_size = max_size; if (g_memory_initialized) return; g_memory_out_of_memory = false; mem_initialize(); g_memory_initialized = true; } bool memory::is_out_of_memory() { return g_memory_out_of_memory; } void memory::set_high_watermark(size_t watermark) { // This method is only safe to invoke at initialization time, that is, before the threads are created. g_memory_watermark = watermark; } bool memory::above_high_watermark() { if (g_memory_watermark == 0) return false; lock_guard lock(*g_memory_mux); return g_memory_watermark < g_memory_alloc_size; } // The following methods are only safe to invoke at // initialization time, that is, before threads are created. void memory::set_max_size(size_t max_size) { g_memory_max_size = max_size; } void memory::set_max_alloc_count(size_t max_count) { g_memory_max_alloc_count = max_count; } static bool g_finalizing = false; void memory::finalize() { if (g_memory_initialized) { g_finalizing = true; mem_finalize(); // we leak the mutex since we need it to be always live since memory may // be reinitialized again //delete g_memory_mux; g_memory_initialized = false; g_finalizing = false; } } unsigned long long memory::get_allocation_size() { long long r; { lock_guard lock(*g_memory_mux); r = g_memory_alloc_size; } if (r < 0) r = 0; return r; } unsigned long long memory::get_max_used_memory() { unsigned long long r; { lock_guard lock(*g_memory_mux); r = g_memory_max_used_size; } return r; } #if defined(_WINDOWS) #include #endif unsigned long long memory::get_max_memory_size() { #if defined(_WINDOWS) MEMORYSTATUSEX statex; statex.dwLength = sizeof (statex); GlobalMemoryStatusEx (&statex); return statex.ullTotalPhys; #else // 16 GB return 1ull << 34ull; #endif } unsigned long long memory::get_allocation_count() { return g_memory_alloc_count; } void memory::display_max_usage(std::ostream & os) { unsigned long long mem = get_max_used_memory(); os << "max. heap size: " << static_cast(mem)/static_cast(1024*1024) << " Mbytes\n"; } void memory::display_i_max_usage(std::ostream & os) { unsigned long long mem = get_max_used_memory(); std::cout << "MEMORY " << static_cast(mem)/static_cast(1024*1024) << "\n"; } #if Z3DEBUG void memory::deallocate(char const * file, int line, void * p) { deallocate(p); TRACE_CODE(if (!g_finalizing) TRACE("memory", tout << "dealloc " << std::hex << p << std::dec << " " << file << ":" << line << "\n";);); } void * memory::allocate(char const* file, int line, char const* obj, size_t s) { void * r = allocate(s); TRACE("memory", tout << "alloc " << std::hex << r << std::dec << " " << file << ":" << line << " " << obj << " " << s << "\n";); return r; } #endif #if !defined(SINGLE_THREAD) && (defined(_WINDOWS) || defined(_USE_THREAD_LOCAL)) // ================================== // ================================== // THREAD LOCAL VERSION // ================================== // ================================== // We only integrate the local thread counters with the global one // when the local counter > SYNCH_THRESHOLD #define SYNCH_THRESHOLD 100000 thread_local long long g_memory_thread_alloc_size = 0; thread_local long long g_memory_thread_alloc_count = 0; static void synchronize_counters(bool allocating) { #ifdef PROFILE_MEMORY g_synch_counter++; #endif bool out_of_mem = false; bool counts_exceeded = false; { lock_guard lock(*g_memory_mux); g_memory_alloc_size += g_memory_thread_alloc_size; g_memory_alloc_count += g_memory_thread_alloc_count; if (g_memory_alloc_size > g_memory_max_used_size) g_memory_max_used_size = g_memory_alloc_size; if (g_memory_max_size != 0 && g_memory_alloc_size > g_memory_max_size) out_of_mem = true; if (g_memory_max_alloc_count != 0 && g_memory_alloc_count > g_memory_max_alloc_count) counts_exceeded = true; } g_memory_thread_alloc_size = 0; if (out_of_mem && allocating) { throw_out_of_memory(); } if (counts_exceeded && allocating) { throw_alloc_counts_exceeded(); } } void memory::deallocate(void * p) { size_t * sz_p = reinterpret_cast(p) - 1; size_t sz = *sz_p; void * real_p = reinterpret_cast(sz_p); g_memory_thread_alloc_size -= sz; free(real_p); if (g_memory_thread_alloc_size < -SYNCH_THRESHOLD) { synchronize_counters(false); } } void * memory::allocate(size_t s) { s = s + sizeof(size_t); // we allocate an extra field! void * r = malloc(s); if (r == 0) { throw_out_of_memory(); return nullptr; } *(static_cast(r)) = s; g_memory_thread_alloc_size += s; g_memory_thread_alloc_count += 1; if (g_memory_thread_alloc_size > SYNCH_THRESHOLD) { synchronize_counters(true); } return static_cast(r) + 1; // we return a pointer to the location after the extra field } void* memory::reallocate(void *p, size_t s) { size_t *sz_p = reinterpret_cast(p)-1; size_t sz = *sz_p; void *real_p = reinterpret_cast(sz_p); s = s + sizeof(size_t); // we allocate an extra field! g_memory_thread_alloc_size += s - sz; g_memory_thread_alloc_count += 1; if (g_memory_thread_alloc_size > SYNCH_THRESHOLD) { synchronize_counters(true); } void *r = realloc(real_p, s); if (r == 0) { throw_out_of_memory(); return nullptr; } *(static_cast(r)) = s; return static_cast(r) + 1; // we return a pointer to the location after the extra field } #else // ================================== // ================================== // NO THREAD LOCAL VERSION // ================================== // ================================== // allocate & deallocate without using thread local storage void memory::deallocate(void * p) { size_t * sz_p = reinterpret_cast(p) - 1; size_t sz = *sz_p; void * real_p = reinterpret_cast(sz_p); { lock_guard lock(*g_memory_mux); g_memory_alloc_size -= sz; } free(real_p); } void * memory::allocate(size_t s) { s = s + sizeof(size_t); // we allocate an extra field! { lock_guard lock(*g_memory_mux); g_memory_alloc_size += s; g_memory_alloc_count += 1; if (g_memory_alloc_size > g_memory_max_used_size) g_memory_max_used_size = g_memory_alloc_size; if (g_memory_max_size != 0 && g_memory_alloc_size > g_memory_max_size) throw_out_of_memory(); if (g_memory_max_alloc_count != 0 && g_memory_alloc_count > g_memory_max_alloc_count) throw_alloc_counts_exceeded(); } void * r = malloc(s); if (r == nullptr) { throw_out_of_memory(); return nullptr; } *(static_cast(r)) = s; return static_cast(r) + 1; // we return a pointer to the location after the extra field } void* memory::reallocate(void *p, size_t s) { size_t * sz_p = reinterpret_cast(p) - 1; size_t sz = *sz_p; void * real_p = reinterpret_cast(sz_p); s = s + sizeof(size_t); // we allocate an extra field! { lock_guard lock(*g_memory_mux); g_memory_alloc_size += s - sz; g_memory_alloc_count += 1; if (g_memory_alloc_size > g_memory_max_used_size) g_memory_max_used_size = g_memory_alloc_size; if (g_memory_max_size != 0 && g_memory_alloc_size > g_memory_max_size) throw_out_of_memory(); if (g_memory_max_alloc_count != 0 && g_memory_alloc_count > g_memory_max_alloc_count) throw_alloc_counts_exceeded(); } void *r = realloc(real_p, s); if (r == nullptr) { throw_out_of_memory(); return nullptr; } *(static_cast(r)) = s; return static_cast(r) + 1; // we return a pointer to the location after the extra field } #endif z3-z3-4.8.7/src/util/memory_manager.h000066400000000000000000000061621356505360400173530ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: memory_manager.h Abstract: Custom memory layer. Author: Nikolaj Bjorner (nbjorner) 2007-07-24 Revision History: --*/ #ifndef MEMORY_H_ #define MEMORY_H_ #include #include #include "util/z3_exception.h" #ifndef __has_builtin # define __has_builtin(x) 0 #endif #ifdef __GNUC__ # if ((__GNUC__ * 100 + __GNUC_MINOR__) >= 409 || __has_builtin(returns_nonnull)) && !defined(__INTEL_COMPILER) # define GCC_RET_NON_NULL __attribute__((returns_nonnull)) # else # define GCC_RET_NON_NULL # endif # define ALLOC_ATTR __attribute__((malloc)) GCC_RET_NON_NULL #elif defined(_WINDOWS) # define ALLOC_ATTR __declspec(restrict) #else # define ALLOC_ATTR #endif class out_of_memory_error : public z3_error { public: out_of_memory_error(); }; class memory { public: static bool is_out_of_memory(); static void initialize(size_t max_size); static void set_high_watermark(size_t watermak); static bool above_high_watermark(); static void set_max_size(size_t max_size); static void set_max_alloc_count(size_t max_count); static void finalize(); static void display_max_usage(std::ostream& os); static void display_i_max_usage(std::ostream& os); static void deallocate(void* p); static ALLOC_ATTR void* allocate(size_t s); static ALLOC_ATTR void* reallocate(void *p, size_t s); #if Z3DEBUG static void deallocate(char const* file, int line, void* p); static ALLOC_ATTR void* allocate(char const* file, int line, char const* obj, size_t s); #endif static unsigned long long get_allocation_size(); static unsigned long long get_max_used_memory(); static unsigned long long get_allocation_count(); static unsigned long long get_max_memory_size(); // temporary hack to avoid out-of-memory crash in z3.exe static void exit_when_out_of_memory(bool flag, char const * msg); }; #if _DEBUG #define alloc(T,...) new (memory::allocate(__FILE__,__LINE__,#T, sizeof(T))) T(__VA_ARGS__) #define dealloc(_ptr_) deallocf(__FILE__,__LINE__,_ptr_) template void deallocf(char const* file, int line, T * ptr) { if (ptr == 0) return; ptr->~T(); memory::deallocate(file, line, ptr); } #else #define alloc(T,...) new (memory::allocate(sizeof(T))) T(__VA_ARGS__) template void dealloc(T * ptr) { if (ptr == nullptr) return; ptr->~T(); memory::deallocate(ptr); } #endif template ALLOC_ATTR T * alloc_vect(unsigned sz); template T * alloc_vect(unsigned sz) { T * r = static_cast(memory::allocate(sizeof(T) * sz)); T * curr = r; for (unsigned i = 0; i < sz; i++, curr++) new (curr) T(); return r; } template void dealloc_vect(T * ptr, unsigned sz) { if (ptr == nullptr) return; T * curr = ptr; for (unsigned i = 0; i < sz; i++, curr++) curr->~T(); memory::deallocate(ptr); } #define alloc_svect(T, sz) static_cast(memory::allocate(sizeof(T) * sz)) template void dealloc_svect(T * ptr) { if (ptr == nullptr) return; memory::deallocate(ptr); } #endif /* MEMORY_H_ */ z3-z3-4.8.7/src/util/min_cut.cpp000066400000000000000000000140671356505360400163450ustar00rootroot00000000000000/*++ Copyright (c) 2017 Arie Gurfinkel Module Name: min_cut.cpp Abstract: min cut solver Author: Bernhard Gleiss Revision History: --*/ #include "util/min_cut.h" #include "util/trace.h" min_cut::min_cut() { // push back two empty vectors for source and sink m_edges.push_back(edge_vector()); m_edges.push_back(edge_vector()); } unsigned min_cut::new_node() { m_edges.push_back(edge_vector()); return m_edges.size() - 1; } void min_cut::add_edge(unsigned int i, unsigned int j, unsigned capacity) { m_edges.reserve(i + 1); m_edges[i].push_back(edge(j, capacity)); TRACE("spacer.mincut", tout << "adding edge (" << i << "," << j << ")\n";); } void min_cut::compute_min_cut(unsigned_vector& cut_nodes) { if (m_edges.size() == 2) { return; } m_d.resize(m_edges.size()); m_pred.resize(m_edges.size()); // compute initial distances and number of nodes compute_initial_distances(); unsigned i = 0; while (m_d[0] < m_edges.size()) { unsigned j = get_admissible_edge(i); if (j < m_edges.size()) { // advance(i) m_pred[j] = i; i = j; // if i is the sink, augment path if (i == 1) { augment_path(); i = 0; } } else { // retreat compute_distance(i); if (i != 0) { i = m_pred[i]; } } } // split nodes into reachable and unreachable ones bool_vector reachable(m_edges.size()); compute_reachable_nodes(reachable); // find all edges between reachable and unreachable nodes and // for each such edge, add corresponding lemma to unsat-core compute_cut_and_add_lemmas(reachable, cut_nodes); } void min_cut::compute_initial_distances() { unsigned_vector todo; bool_vector visited(m_edges.size()); todo.push_back(0); // start at the source, since we do postorder traversel while (!todo.empty()) { unsigned current = todo.back(); // if we haven't already visited current if (!visited[current]) { bool exists_unvisited_parent = false; // add unprocessed parents to stack for DFS. If there is at least // one unprocessed parent, don't compute the result // for current now, but wait until those unprocessed parents are processed for (auto const& edge : m_edges[current]) { unsigned parent = edge.node; // if we haven't visited the current parent yet if (!visited[parent]) { // add it to the stack todo.push_back(parent); exists_unvisited_parent = true; } } // if we already visited all parents, we can visit current too if (!exists_unvisited_parent) { visited[current] = true; todo.pop_back(); compute_distance(current); // I.H. all parent distances are already computed } } else { todo.pop_back(); } } } unsigned min_cut::get_admissible_edge(unsigned i) { for (const auto& edge : m_edges[i]) { if (edge.weight > 0 && m_d[i] == m_d[edge.node] + 1) { return edge.node; } } return m_edges.size(); // no element found } void min_cut::augment_path() { // find bottleneck capacity unsigned max = std::numeric_limits::max(); unsigned k = 1; while (k != 0) { unsigned l = m_pred[k]; for (const auto& edge : m_edges[l]) { if (edge.node == k) { max = std::min(max, edge.weight); } } k = l; } k = 1; while (k != 0) { unsigned l = m_pred[k]; // decrease capacity for (auto& edge : m_edges[l]) { if (edge.node == k) { edge.weight -= max; } } // increase reverse flow bool already_exists = false; for (auto& edge : m_edges[k]) { if (edge.node == l) { already_exists = true; edge.weight += max; } } if (!already_exists) { m_edges[k].push_back(edge(1, max)); } k = l; } } void min_cut::compute_distance(unsigned i) { if (i == 1) { // sink node m_d[1] = 0; } else { unsigned min = std::numeric_limits::max(); // find edge (i,j) with positive residual capacity and smallest distance for (const auto& edge : m_edges[i]) { if (edge.weight > 0) { min = std::min(min, m_d[edge.node] + 1); } } m_d[i] = min; } } void min_cut::compute_reachable_nodes(bool_vector& reachable) { unsigned_vector todo; todo.push_back(0); while (!todo.empty()) { unsigned current = todo.back(); todo.pop_back(); if (!reachable[current]) { reachable[current] = true; for (const auto& edge : m_edges[current]) { if (edge.weight > 0) { todo.push_back(edge.node); } } } } } void min_cut::compute_cut_and_add_lemmas(bool_vector& reachable, unsigned_vector& cut_nodes) { unsigned_vector todo; bool_vector visited(m_edges.size()); todo.push_back(0); while (!todo.empty()) { unsigned current = todo.back(); todo.pop_back(); if (!visited[current]) { visited[current] = true; for (const auto& edge : m_edges[current]) { unsigned successor = edge.node; if (reachable[successor]) { todo.push_back(successor); } else { cut_nodes.push_back(successor); } } } } } z3-z3-4.8.7/src/util/min_cut.h000066400000000000000000000027641356505360400160130ustar00rootroot00000000000000/*++ Copyright (c) 2017 Arie Gurfinkel Module Name: min_cut.h Abstract: min cut solver Author: Bernhard Gleiss Revision History: --*/ #ifndef MIN_CUT_H_ #define MIN_CUT_H_ #include "util/vector.h" class min_cut { public: min_cut(); /* \brief create a node */ unsigned new_node(); /* \brief add an i -> j edge with (unit) capacity */ void add_edge(unsigned i, unsigned j, unsigned capacity = 1); /* \brief produce a min cut between source node = 0 and target node = 1. NB. the function changes capacities on edges. */ void compute_min_cut(unsigned_vector& cut_nodes); private: typedef svector bool_vector; struct edge { unsigned node; unsigned weight; edge(unsigned n, unsigned w): node(n), weight(w) {} edge(): node(0), weight(0) {} }; typedef svector edge_vector; vector m_edges; // map from node to all outgoing edges together with their weights (also contains "reverse edges") unsigned_vector m_d; // approximation of distance from node to sink in residual graph unsigned_vector m_pred; // predecessor-information for reconstruction of augmenting path void compute_initial_distances(); unsigned get_admissible_edge(unsigned i); void augment_path(); void compute_distance(unsigned i); void compute_reachable_nodes(bool_vector& reachable); void compute_cut_and_add_lemmas(bool_vector& reachable, unsigned_vector& cut_nodes); }; #endif z3-z3-4.8.7/src/util/mpbq.cpp000066400000000000000000000607231356505360400156460ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: mpbq.cpp Abstract: Binary Rational Numbers A binary rational is a number of the form a/2^k. All integers are binary rationals. Binary rational numbers can be implemented more efficiently than rationals. Binary rationals form a Ring. They are not closed under division. In Z3, they are used to implement algebraic numbers. The root isolation operations only use division by 2. Author: Leonardo de Moura (leonardo) 2011-11-24. Revision History: --*/ #include #include "util/mpbq.h" #ifdef Z3DEBUG #define MPBQ_DEBUG #endif rational to_rational(mpbq const & v) { rational r(v.numerator()); rational twok; twok = power(rational(2), v.k()); return r/twok; } mpbq_manager::mpbq_manager(unsynch_mpz_manager & m): m_manager(m) { } mpbq_manager::~mpbq_manager() { del(m_addmul_tmp); m_manager.del(m_tmp); m_manager.del(m_tmp2); m_manager.del(m_select_int_tmp1); m_manager.del(m_select_int_tmp2); m_manager.del(m_select_small_tmp); del(m_select_small_tmp1); del(m_select_small_tmp2); m_manager.del(m_div_tmp1); m_manager.del(m_div_tmp2); m_manager.del(m_div_tmp3); } void mpbq_manager::reset(mpbq_vector & v) { unsigned sz = v.size(); for (unsigned i = 0; i < sz; i++) reset(v[i]); v.reset(); } void mpbq_manager::normalize(mpbq & a) { if (a.m_k == 0) return; if (m_manager.is_zero(a.m_num)) { a.m_k = 0; return; } #ifdef MPBQ_DEBUG rational r = to_rational(a); #endif unsigned k = m_manager.power_of_two_multiple(a.m_num); if (k > a.m_k) k = a.m_k; m_manager.machine_div2k(a.m_num, k); a.m_k -= k; #ifdef MPBQ_DEBUG rational new_r = to_rational(a); SASSERT(r == new_r); #endif } int mpbq_manager::magnitude_lb(mpbq const & a) { if (m_manager.is_zero(a.m_num)) return 0; if (m_manager.is_pos(a.m_num)) return m_manager.log2(a.m_num) - a.m_k; return m_manager.mlog2(a.m_num) - a.m_k + 1; } int mpbq_manager::magnitude_ub(mpbq const & a) { if (m_manager.is_zero(a.m_num)) return 0; if (m_manager.is_pos(a.m_num)) return m_manager.log2(a.m_num) - a.m_k + 1; return m_manager.mlog2(a.m_num) - a.m_k; } void mpbq_manager::mul2(mpbq & a) { if (a.m_k == 0) m_manager.mul2k(a.m_num, 1); else a.m_k--; } void mpbq_manager::mul2k(mpbq & a, unsigned k) { if (k == 0) return; if (a.m_k < k) { m_manager.mul2k(a.m_num, k - a.m_k); a.m_k = 0; } else { SASSERT(a.m_k >= k); a.m_k -= k; } } void mpbq_manager::add(mpbq const & a, mpbq const & b, mpbq & r) { #ifdef MPBQ_DEBUG rational _a = to_rational(a); rational _b = to_rational(b); #endif if (a.m_k == b.m_k) { m_manager.add(a.m_num, b.m_num, r.m_num); r.m_k = a.m_k; } else if (a.m_k < b.m_k) { m_manager.mul2k(a.m_num, b.m_k - a.m_k, m_tmp); m_manager.add(b.m_num, m_tmp, r.m_num); r.m_k = b.m_k; } else { SASSERT(a.m_k > b.m_k); m_manager.mul2k(b.m_num, a.m_k - b.m_k, m_tmp); m_manager.add(a.m_num, m_tmp, r.m_num); r.m_k = a.m_k; } normalize(r); #ifdef MPBQ_DEBUG rational _r = to_rational(r); SASSERT(_a + _b == _r); #endif } void mpbq_manager::add(mpbq const & a, mpz const & b, mpbq & r) { #ifdef MPBQ_DEBUG rational _a = to_rational(a); rational _b(b); #endif if (a.m_k == 0) { m_manager.add(a.m_num, b, r.m_num); r.m_k = a.m_k; } else { m_manager.mul2k(b, a.m_k, m_tmp); m_manager.add(a.m_num, m_tmp, r.m_num); r.m_k = a.m_k; } normalize(r); #ifdef MPBQ_DEBUG rational _r = to_rational(r); TRACE("mpbq_bug", tout << "add a: " << _a << ", b: " << _b << ", r: " << _r << ", expected: " << (_a + _b) << "\n";); SASSERT(_a + _b == _r); #endif } void mpbq_manager::sub(mpbq const & a, mpbq const & b, mpbq & r) { #ifdef MPBQ_DEBUG rational _a = to_rational(a); rational _b = to_rational(b); #endif if (a.m_k == b.m_k) { m_manager.sub(a.m_num, b.m_num, r.m_num); r.m_k = a.m_k; } else if (a.m_k < b.m_k) { m_manager.mul2k(a.m_num, b.m_k - a.m_k, m_tmp); m_manager.sub(m_tmp, b.m_num, r.m_num); r.m_k = b.m_k; } else { SASSERT(a.m_k > b.m_k); m_manager.mul2k(b.m_num, a.m_k - b.m_k, m_tmp); m_manager.sub(a.m_num, m_tmp, r.m_num); r.m_k = a.m_k; } normalize(r); #ifdef MPBQ_DEBUG rational _r = to_rational(r); TRACE("mpbq_bug", tout << "sub a: " << _a << ", b: " << _b << ", r: " << _r << ", expected: " << (_a - _b) << "\n";); SASSERT(_a - _b == _r); #endif } void mpbq_manager::sub(mpbq const & a, mpz const & b, mpbq & r) { #ifdef MPBQ_DEBUG rational _a = to_rational(a); rational _b(b); #endif if (a.m_k == 0) { m_manager.sub(a.m_num, b, r.m_num); r.m_k = a.m_k; } else { m_manager.mul2k(b, a.m_k, m_tmp); m_manager.sub(a.m_num, m_tmp, r.m_num); r.m_k = a.m_k; } normalize(r); #ifdef MPBQ_DEBUG rational _r = to_rational(r); SASSERT(_a - _b == _r); #endif } void mpbq_manager::mul(mpbq const & a, mpbq const & b, mpbq & r) { #ifdef MPBQ_DEBUG rational _a = to_rational(a); rational _b = to_rational(b); #endif m_manager.mul(a.m_num, b.m_num, r.m_num); r.m_k = a.m_k + b.m_k; if (a.m_k == 0 || b.m_k == 0) { // if a.m_k and b.m_k are greater than 0, then there is no point in normalizing r. normalize(r); } #ifdef MPBQ_DEBUG rational _r = to_rational(r); SASSERT(_a * _b == _r); #endif } void mpbq_manager::mul(mpbq const & a, mpz const & b, mpbq & r) { #ifdef MPBQ_DEBUG rational _a = to_rational(a); rational _b(b); #endif m_manager.mul(a.m_num, b, r.m_num); r.m_k = a.m_k; normalize(r); #ifdef MPBQ_DEBUG rational _r = to_rational(r); SASSERT(_a * _b == _r); #endif } void mpbq_manager::power(mpbq & a, unsigned k) { SASSERT(static_cast(k) * static_cast(a.k()) <= static_cast(UINT_MAX)); // We don't need to normalize because: // If a.m_k == 0, then a is an integer, and the result be an integer // If a.m_k > 0, then a.m_num must be odd, and the (a.m_num)^k will also be odd a.m_k *= k; m_manager.power(a.m_num, k, a.m_num); } bool mpbq_manager::root_lower(mpbq & a, unsigned n) { bool r = m_manager.root(a.m_num, n); if (!r) m_manager.dec(a.m_num); if (a.m_k % n == 0) { a.m_k /= n; normalize(a); return r; } else if (m_manager.is_neg(a.m_num)) { a.m_k /= n; normalize(a); return false; } else { a.m_k /= n; a.m_k++; normalize(a); return false; } } bool mpbq_manager::root_upper(mpbq & a, unsigned n) { bool r = m_manager.root(a.m_num, n); if (a.m_k % n == 0) { a.m_k /= n; normalize(a); return r; } else if (m_manager.is_neg(a.m_num)) { a.m_k /= n; a.m_k++; normalize(a); return false; } else { a.m_k /= n; normalize(a); return false; } } bool mpbq_manager::lt(mpbq const & a, mpbq const & b) { // TODO: try the following trick when k1 != k2 // Given, a = n1/2^k1 b = n2/2^k2 // Suppose n1 > 0 and n2 > 0, // Then, we have, n1 <= 2^{log2(n1) - k1} 2^{log2(n2) - 1 - k2} <= n2 // Thus, log2(n1) - k1 < log2(n2) - 1 - k2 implies a < b // Similarly: log2(n2) - k2 < log2(n1) - 1 - k1 implies b < a // That is we compare the "magnitude" of the numbers before performing mul2k // // If n1 < 0 and n2 < 0, a similar trick can be implemented using mlog2 instead log2. // // It seems the trick is not useful when n1 and n2 are small // numbers, and k1 and k2 very small < 8. Since, no bignumber // computation is needed for mul2k. if (a.m_k == b.m_k) { return m_manager.lt(a.m_num, b.m_num); } else if (a.m_k < b.m_k) { m_manager.mul2k(a.m_num, b.m_k - a.m_k, m_tmp); return m_manager.lt(m_tmp, b.m_num); } else { SASSERT(a.m_k > b.m_k); m_manager.mul2k(b.m_num, a.m_k - b.m_k, m_tmp); return m_manager.lt(a.m_num, m_tmp); } } bool mpbq_manager::lt_1div2k(mpbq const & a, unsigned k) { if (m_manager.is_nonpos(a.m_num)) return true; if (a.m_k <= k) { // since a.m_num >= 1 return false; } else { SASSERT(a.m_k > k); m_manager.mul2k(mpz(1), a.m_k - k, m_tmp); return m_manager.lt(a.m_num, m_tmp); } } bool mpbq_manager::eq(mpbq const & a, mpq const & b) { if (is_int(a) && m_manager.is_one(b.denominator())) return m_manager.eq(a.m_num, b.numerator()); m_manager.mul2k(b.numerator(), a.m_k, m_tmp); m_manager.mul(a.m_num, b.denominator(), m_tmp2); return m_manager.eq(m_tmp, m_tmp2); } bool mpbq_manager::lt(mpbq const & a, mpq const & b) { if (is_int(a) && m_manager.is_one(b.denominator())) return m_manager.lt(a.m_num, b.numerator()); m_manager.mul(a.m_num, b.denominator(), m_tmp); m_manager.mul2k(b.numerator(), a.m_k, m_tmp2); return m_manager.lt(m_tmp, m_tmp2); } bool mpbq_manager::le(mpbq const & a, mpq const & b) { if (is_int(a) && m_manager.is_one(b.denominator())) return m_manager.le(a.m_num, b.numerator()); m_manager.mul(a.m_num, b.denominator(), m_tmp); m_manager.mul2k(b.numerator(), a.m_k, m_tmp2); return m_manager.le(m_tmp, m_tmp2); } bool mpbq_manager::lt(mpbq const & a, mpz const & b) { if (is_int(a)) return m_manager.lt(a.m_num, b); m_manager.mul2k(b, a.m_k, m_tmp); return m_manager.lt(a.m_num, m_tmp); } bool mpbq_manager::le(mpbq const & a, mpz const & b) { if (is_int(a)) return m_manager.le(a.m_num, b); m_manager.mul2k(b, a.m_k, m_tmp); return m_manager.le(a.m_num, m_tmp); } std::string mpbq_manager::to_string(mpbq const & a) { std::ostringstream buffer; buffer << m_manager.to_string(a.m_num); if (a.m_k == 1) buffer << "/2"; else if (a.m_k > 1) buffer << "/2^" << a.m_k; return buffer.str(); } void mpbq_manager::display(std::ostream & out, mpbq const & a) { out << m_manager.to_string(a.m_num); if (a.m_k > 0) out << "/2"; if (a.m_k > 1) out << "^" << a.m_k; } void mpbq_manager::display_pp(std::ostream & out, mpbq const & a) { out << m_manager.to_string(a.m_num); if (a.m_k > 0) out << "/2"; if (a.m_k > 1) out << "" << a.m_k << ""; } void mpbq_manager::display_smt2(std::ostream & out, mpbq const & a, bool decimal) { if (a.m_k == 0) { m_manager.display_smt2(out, a.m_num, decimal); } else { out << "(/ "; m_manager.display_smt2(out, a.m_num, decimal); out << " "; out << "(^ 2"; if (decimal) out << ".0"; out << " " << a.m_k; if (decimal) out << ".0"; out << "))"; } } void mpbq_manager::display_decimal(std::ostream & out, mpbq const & a, unsigned prec) { if (is_int(a)) { out << m_manager.to_string(a.m_num); return; } mpz two(2); mpz ten(10); mpz two_k; mpz n1, v1; if (m_manager.is_neg(a.m_num)) out << "-"; m_manager.set(v1, a.m_num); m_manager.abs(v1); m_manager.power(two, a.m_k, two_k); m_manager.rem(v1, two_k, n1); m_manager.div(v1, two_k, v1); SASSERT(!m_manager.is_zero(n1)); out << m_manager.to_string(v1); out << "."; for (unsigned i = 0; i < prec; i++) { m_manager.mul(n1, ten, n1); m_manager.div(n1, two_k, v1); m_manager.rem(n1, two_k, n1); out << m_manager.to_string(v1); if (m_manager.is_zero(n1)) goto end; } out << "?"; end: m_manager.del(n1); m_manager.del(v1); m_manager.del(two_k); } void mpbq_manager::display_decimal(std::ostream & out, mpbq const & a, mpbq const & b, unsigned prec) { mpz two(2); mpz ten(10); mpz two_k1, two_k2; mpz n1, v1, n2, v2; if (m_manager.is_neg(a.m_num) != m_manager.is_neg(b.m_num)) { out << "?"; return; } if (m_manager.is_neg(a.m_num)) out << "-"; m_manager.set(v1, a.m_num); m_manager.abs(v1); m_manager.set(v2, b.m_num); m_manager.abs(v2); m_manager.power(two, a.m_k, two_k1); m_manager.power(two, b.m_k, two_k2); m_manager.rem(v1, two_k1, n1); m_manager.rem(v2, two_k2, n2); m_manager.div(v1, two_k1, v1); m_manager.div(v2, two_k2, v2); if (!m_manager.eq(v1, v2)) { out << "?"; goto end; } out << m_manager.to_string(v1); if (m_manager.is_zero(n1) && m_manager.is_zero(n2)) goto end; // number is an integer out << "."; for (unsigned i = 0; i < prec; i++) { m_manager.mul(n1, ten, n1); m_manager.mul(n2, ten, n2); m_manager.div(n1, two_k1, v1); m_manager.div(n2, two_k2, v2); if (m_manager.eq(v1, v2)) { out << m_manager.to_string(v1); } else { out << "?"; goto end; } m_manager.rem(n1, two_k1, n1); m_manager.rem(n2, two_k2, n2); if (m_manager.is_zero(n1) && m_manager.is_zero(n2)) goto end; // number is precise } out << "?"; end: m_manager.del(n1); m_manager.del(v1); m_manager.del(n2); m_manager.del(v2); m_manager.del(two_k1); m_manager.del(two_k2); } bool mpbq_manager::to_mpbq(mpq const & q, mpbq & bq) { mpz const & n = q.numerator(); mpz const & d = q.denominator(); unsigned shift; if (m_manager.is_one(d)) { set(bq, n); SASSERT(eq(bq, q)); return true; } else if (m_manager.is_power_of_two(d, shift)) { SASSERT(shift>=1); unsigned k = shift; set(bq, n, k); SASSERT(eq(bq, q)); return true; } else { unsigned k = m_manager.log2(d); set(bq, n, k+1); return false; } } void mpbq_manager::refine_upper(mpq const & q, mpbq & l, mpbq & u) { SASSERT(lt(l, q) && gt(u, q)); SASSERT(!m_manager.is_power_of_two(q.denominator())); // l < q < u mpbq mid; while (true) { add(l, u, mid); div2(mid); if (gt(mid, q)) { swap(u, mid); del(mid); SASSERT(lt(l, q) && gt(u, q)); return; } swap(l, mid); } } void mpbq_manager::refine_lower(mpq const & q, mpbq & l, mpbq & u) { SASSERT(lt(l, q) && gt(u, q)); SASSERT(!m_manager.is_power_of_two(q.denominator())); // l < q < u mpbq mid; while (true) { add(l, u, mid); div2(mid); if (lt(mid, q)) { swap(l, mid); del(mid); SASSERT(lt(l, q) && gt(u, q)); return; } swap(u, mid); } } // sectect integer in [lower, upper] bool mpbq_manager::select_integer(mpbq const & lower, mpbq const & upper, mpz & r) { if (is_int(lower)) { m_manager.set(r, lower.m_num); return true; } if (is_int(upper)) { m_manager.set(r, upper.m_num); return true; } mpz & ceil_lower = m_select_int_tmp1; mpz & floor_upper = m_select_int_tmp2; ceil(m_manager, lower, ceil_lower); floor(m_manager, upper, floor_upper); if (m_manager.le(ceil_lower, floor_upper)) { m_manager.set(r, ceil_lower); return true; } return false; } // select integer in (lower, upper] bool mpbq_manager::select_integer(unsynch_mpq_manager & qm, mpq const & lower, mpbq const & upper, mpz & r) { if (is_int(upper)) { m_manager.set(r, upper.m_num); return true; } mpz & ceil_lower = m_select_int_tmp1; mpz & floor_upper = m_select_int_tmp2; if (qm.is_int(lower)) { m_manager.set(ceil_lower, lower.numerator()); m_manager.inc(ceil_lower); } else { scoped_mpz tmp(qm); qm.ceil(lower, tmp); m_manager.set(ceil_lower, tmp); } floor(m_manager, upper, floor_upper); if (m_manager.le(ceil_lower, floor_upper)) { m_manager.set(r, ceil_lower); return true; } return false; } // sectect integer in [lower, upper) bool mpbq_manager::select_integer(unsynch_mpq_manager & qm, mpbq const & lower, mpq const & upper, mpz & r) { if (is_int(lower)) { m_manager.set(r, lower.m_num); return true; } mpz & ceil_lower = m_select_int_tmp1; mpz & floor_upper = m_select_int_tmp2; ceil(m_manager, lower, ceil_lower); if (qm.is_int(upper)) { m_manager.set(floor_upper, upper.numerator()); m_manager.dec(floor_upper); } else { scoped_mpz tmp(qm); qm.floor(upper, tmp); m_manager.set(floor_upper, tmp); } if (m_manager.le(ceil_lower, floor_upper)) { m_manager.set(r, ceil_lower); return true; } return false; } // sectect integer in (lower, upper) bool mpbq_manager::select_integer(unsynch_mpq_manager & qm, mpq const & lower, mpq const & upper, mpz & r) { mpz & ceil_lower = m_select_int_tmp1; mpz & floor_upper = m_select_int_tmp2; if (qm.is_int(lower)) { m_manager.set(ceil_lower, lower.numerator()); m_manager.inc(ceil_lower); } else { scoped_mpz tmp(qm); qm.ceil(lower, tmp); m_manager.set(ceil_lower, tmp); } if (qm.is_int(upper)) { m_manager.set(floor_upper, upper.numerator()); m_manager.dec(floor_upper); } else { scoped_mpz tmp(qm); qm.floor(upper, tmp); m_manager.set(floor_upper, tmp); } if (m_manager.le(ceil_lower, floor_upper)) { m_manager.set(r, ceil_lower); return true; } return false; } #define LINEAR_SEARCH_THRESHOLD 8 void mpbq_manager::select_small_core(mpbq const & lower, mpbq const & upper, mpbq & r) { SASSERT(le(lower, upper)); mpz & aux = m_select_small_tmp; if (select_integer(lower, upper, aux)) { set(r, aux); return; } // At this point we know that k=0 does not work, since there is no integer // in the interval [lower, upper] unsigned min_k = 0; unsigned max_k = std::min(lower.m_k, upper.m_k); if (max_k <= LINEAR_SEARCH_THRESHOLD) { unsigned k = 0; mpbq & l2k = m_select_small_tmp1; mpbq & u2k = m_select_small_tmp2; set(l2k, lower); set(u2k, upper); while (true) { k++; mul2(l2k); mul2(u2k); if (select_integer(l2k, u2k, aux)) { set(r, aux, k); break; } } } else { mpbq & l2k = m_select_small_tmp1; mpbq & u2k = m_select_small_tmp2; while (true) { unsigned mid_k = min_k + (max_k - min_k)/2; set(l2k, lower); set(u2k, upper); mul2k(l2k, mid_k); mul2k(u2k, mid_k); if (select_integer(l2k, u2k, aux)) max_k = mid_k; else min_k = mid_k + 1; if (min_k == max_k) { if (max_k == mid_k) { set(r, aux, max_k); } else { set(l2k, lower); set(u2k, upper); mul2k(l2k, max_k); mul2k(u2k, max_k); VERIFY(select_integer(l2k, u2k, aux)); set(r, aux, max_k); } break; } } } SASSERT(le(lower, r)); SASSERT(le(r, upper)); } bool mpbq_manager::select_small(mpbq const & lower, mpbq const & upper, mpbq & r) { if (gt(lower, upper)) return false; select_small_core(lower, upper, r); return true; } void mpbq_manager::select_small_core(unsynch_mpq_manager & qm, mpq const & lower, mpbq const & upper, mpbq & r) { TRACE("select_small", tout << "lower (q): " << qm.to_string(lower) << ", upper (bq): " << to_string(upper) << "\n";); SASSERT(gt(upper, lower)); mpz & aux = m_select_small_tmp; if (select_integer(qm, lower, upper, aux)) { set(r, aux); return; } // At this point we know that k=0 does not work, since there is no integer // in the interval [lower, upper] unsigned k = 0; scoped_mpq l2k(qm); mpq two(2); mpbq & u2k = m_select_small_tmp2; qm.set(l2k, lower); set(u2k, upper); while (true) { k++; qm.mul(l2k, two, l2k); mul2(u2k); if (select_integer(qm, l2k, u2k, aux)) { set(r, aux, k); break; } } } void mpbq_manager::select_small_core(unsynch_mpq_manager & qm, mpbq const & lower, mpq const & upper, mpbq & r) { SASSERT(lt(lower, upper)); mpz & aux = m_select_small_tmp; if (select_integer(qm, lower, upper, aux)) { set(r, aux); return; } // At this point we know that k=0 does not work, since there is no integer // in the interval [lower, upper] unsigned k = 0; mpbq & l2k = m_select_small_tmp2; scoped_mpq u2k(qm); mpq two(2); set(l2k, lower); qm.set(u2k, upper); while (true) { k++; mul2(l2k); qm.mul(u2k, two, u2k); if (select_integer(qm, l2k, u2k, aux)) { set(r, aux, k); break; } } } void mpbq_manager::select_small_core(unsynch_mpq_manager & qm, mpq const & lower, mpq const & upper, mpbq & r) { SASSERT(qm.lt(lower, upper)); mpz & aux = m_select_small_tmp; if (select_integer(qm, lower, upper, aux)) { set(r, aux); return; } // At this point we know that k=0 does not work, since there is no integer // in the interval [lower, upper] unsigned k = 0; scoped_mpq l2k(qm); scoped_mpq u2k(qm); mpq two(2); qm.set(l2k, lower); qm.set(u2k, upper); while (true) { k++; qm.mul(l2k, two, l2k); qm.mul(u2k, two, u2k); if (select_integer(qm, l2k, u2k, aux)) { set(r, aux, k); break; } } } void mpbq_manager::approx(mpbq & a, unsigned k, bool to_plus_inf) { if (a.m_k <= k) return; #ifdef MPBQ_DEBUG scoped_mpbq old_a(*this); old_a = a; #endif bool sgn = m_manager.is_neg(a.m_num); bool _inc = (sgn != to_plus_inf); unsigned shift = a.m_k - k; m_manager.abs(a.m_num); m_manager.machine_div2k(a.m_num, shift); if (_inc) m_manager.inc(a.m_num); if (sgn) m_manager.neg(a.m_num); a.m_k = k; normalize(a); #ifdef MPBQ_DEBUG if (to_plus_inf) { SASSERT(lt(old_a, a)); } else { SASSERT(lt(a, old_a)); } #endif } void mpbq_manager::approx_div(mpbq const & a, mpbq const & b, mpbq & c, unsigned k, bool to_plus_inf) { SASSERT(!is_zero(b)); unsigned k_prime; if (m_manager.is_power_of_two(b.m_num, k_prime)) { // The division is precise, so we ignore k and to_plus_inf SASSERT(b.m_k == 0 || k_prime == 0); // remark: b.m_num is odd when b.m_k > 0, since b.m_num is a power of two we have that b.m_k == 0 or b.m_num == 1. m_manager.set(c.m_num, a.m_num); if (b.m_k > 0) { SASSERT(k_prime == 0); mpz & pw2 = m_div_tmp1; m_manager.power(mpz(2), b.m_k, pw2); m_manager.mul(c.m_num, pw2, c.m_num); } c.m_k = a.m_k + k_prime; normalize(c); } else if (m_manager.divides(b.m_num, a.m_num)) { // result is also precise m_manager.div(a.m_num, b.m_num, c.m_num); if (a.m_k >= b.m_k) { c.m_k = a.m_k - b.m_k; } else { m_manager.mul2k(c.m_num, b.m_k - a.m_k); c.m_k = 0; } normalize(c); } else { bool sgn = is_neg(a) != is_neg(b); mpz & abs_a = m_div_tmp1; mpz & norm_a = m_div_tmp2; mpz & abs_b = m_div_tmp3; m_manager.set(abs_a, a.m_num); m_manager.abs(abs_a); m_manager.set(abs_b, b.m_num); m_manager.abs(abs_b); if (a.m_k > b.m_k) { if (k >= a.m_k - b.m_k) m_manager.mul2k(abs_a, k - (a.m_k - b.m_k), norm_a); else m_manager.machine_div2k(abs_a, (a.m_k - b.m_k) - k, norm_a); } else { m_manager.mul2k(abs_a, k + b.m_k - a.m_k, norm_a); } c.m_k = k; m_manager.div(norm_a, abs_b, c.m_num); if (sgn != to_plus_inf) m_manager.inc(c.m_num); if (sgn) m_manager.neg(c.m_num); normalize(c); } } z3-z3-4.8.7/src/util/mpbq.h000066400000000000000000000330651356505360400153120ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: mpbq.h Abstract: Binary Rational Numbers A binary rational is a number of the form a/2^k. All integers are binary rationals. Binary rational numbers can be implemented more efficiently than rationals. Binary rationals form a Ring. They are not closed under division. In Z3, they are used to implement algebraic numbers. The root isolation operations only use division by 2. Author: Leonardo de Moura (leonardo) 2011-11-24. Revision History: --*/ #ifndef MPBQ_H_ #define MPBQ_H_ #include "util/mpq.h" #include "util/rational.h" #include "util/vector.h" class mpbq { mpz m_num; unsigned m_k; // we don't need mpz here. 2^(2^32-1) is a huge number, we will not even be able to convert the mpbq into an mpq friend class mpbq_manager; public: mpbq():m_num(0), m_k(0) {} mpbq(int v):m_num(v), m_k(0) {} mpbq(int v, unsigned k):m_num(v), m_k(k) {} mpz const & numerator() const { return m_num; } unsigned k() const { return m_k; } void swap(mpbq & other) { m_num.swap(other.m_num); std::swap(m_k, other.m_k); } }; inline void swap(mpbq & m1, mpbq & m2) { m1.swap(m2); } typedef svector mpbq_vector; class mpbq_manager { unsynch_mpz_manager & m_manager; mpz m_tmp; mpz m_tmp2; mpbq m_addmul_tmp; mpz m_select_int_tmp1; mpz m_select_int_tmp2; mpz m_select_small_tmp; mpbq m_select_small_tmp1; mpbq m_select_small_tmp2; mpz m_div_tmp1, m_div_tmp2, m_div_tmp3; void normalize(mpbq & a); bool select_integer(mpbq const & lower, mpbq const & upper, mpz & r); bool select_integer(unsynch_mpq_manager & qm, mpq const & lower, mpbq const & upper, mpz & r); bool select_integer(unsynch_mpq_manager & qm, mpbq const & lower, mpq const & upper, mpz & r); bool select_integer(unsynch_mpq_manager & qm, mpq const & lower, mpq const & upper, mpz & r); public: static bool precise() { return true; } static bool field() { return false; } typedef mpbq numeral; mpbq_manager(unsynch_mpz_manager & m); ~mpbq_manager(); static void swap(mpbq & a, mpbq & b) { a.swap(b); } void del(mpbq & a) { m_manager.del(a.m_num); } void reset(mpbq & a) { m_manager.reset(a.m_num); a.m_k = 0; } void reset(mpbq_vector & v); void set(mpbq & a, int n) { m_manager.set(a.m_num, n); a.m_k = 0; } void set(mpbq & a, unsigned n) { m_manager.set(a.m_num, n); a.m_k = 0; } void set(mpbq & a, int n, unsigned k) { m_manager.set(a.m_num, n); a.m_k = k; normalize(a); } void set(mpbq & a, mpz const & n, unsigned k) { m_manager.set(a.m_num, n); a.m_k = k; normalize(a); } void set(mpbq & a, mpz const & n) { m_manager.set(a.m_num, n); a.m_k = 0; } void set(mpbq & a, mpbq const & b) { m_manager.set(a.m_num, b.m_num); a.m_k = b.m_k; } void set(mpbq & a, int64_t n, unsigned k) { m_manager.set(a.m_num, n); a.m_k = k; normalize(a); } bool is_int(mpbq const & a) const { return a.m_k == 0; } void get_numerator(mpbq const & a, mpz & n) { m_manager.set(n, a.m_num); } unsigned get_denominator_power(mpbq const & a) { return a.m_k; } bool is_zero(mpbq const & a) const { return m_manager.is_zero(a.m_num); } bool is_nonzero(mpbq const & a) const { return !is_zero(a); } bool is_one(mpbq const & a) const { return a.m_k == 0 && m_manager.is_one(a.m_num); } bool is_pos(mpbq const & a) const { return m_manager.is_pos(a.m_num); } bool is_neg(mpbq const & a) const { return m_manager.is_neg(a.m_num); } bool is_nonpos(mpbq const & a) const { return m_manager.is_nonpos(a.m_num); } bool is_nonneg(mpbq const & a) const { return m_manager.is_nonneg(a.m_num); } void add(mpbq const & a, mpbq const & b, mpbq & r); void add(mpbq const & a, mpz const & b, mpbq & r); void sub(mpbq const & a, mpbq const & b, mpbq & r); void sub(mpbq const & a, mpz const & b, mpbq & r); void mul(mpbq const & a, mpbq const & b, mpbq & r); void mul(mpbq const & a, mpz const & b, mpbq & r); // r <- a + b*c void addmul(mpbq const & a, mpbq const & b, mpbq const & c, mpbq & r) { mul(b, c, m_addmul_tmp); add(a, m_addmul_tmp, r); } void addmul(mpbq const & a, mpz const & b, mpbq const & c, mpbq & r) { mul(c, b, m_addmul_tmp); add(a, m_addmul_tmp, r); } void neg(mpbq & a) { m_manager.neg(a.m_num); } // when dividing by 2, we only need to normalize if m_k was zero. void div2(mpbq & a) { bool old_k_zero = (a.m_k == 0); a.m_k++; if (old_k_zero) normalize(a); } void div2k(mpbq & a, unsigned k) { bool old_k_zero = (a.m_k == 0); a.m_k += k; if (old_k_zero) normalize(a); } void mul2(mpbq & a); void mul2k(mpbq & a, unsigned k); void power(mpbq & a, unsigned k); void power(mpbq const & a, unsigned k, mpbq & b) { set(b, a); power(b, k); } /** \brief Return true if a^{1/n} is a binary rational, and store the result in a. Otherwise, return false and return an lower bound based on the integer root of the numerator and denominator/n */ bool root_lower(mpbq & a, unsigned n); bool root_lower(mpbq const & a, unsigned n, mpbq & r) { set(r, a); return root_lower(r, n); } /** \brief Return true if a^{1/n} is a binary rational, and store the result in a. Otherwise, return false and return an upper bound based on the integer root of the numerator and denominator/n */ bool root_upper(mpbq & a, unsigned n); bool root_upper(mpbq const & a, unsigned n, mpbq & r) { set(r, a); return root_upper(r, n); } bool eq(mpbq const & a, mpbq const & b) { return a.m_k == b.m_k && m_manager.eq(a.m_num, b.m_num); } bool lt(mpbq const & a, mpbq const & b); bool neq(mpbq const & a, mpbq const & b) { return !eq(a, b); } bool gt(mpbq const & a, mpbq const & b) { return lt(b, a); } bool ge(mpbq const & a, mpbq const & b) { return le(b, a); } bool le(mpbq const & a, mpbq const & b) { return !gt(a, b); } bool eq(mpbq const & a, mpq const & b); bool lt(mpbq const & a, mpq const & b); bool le(mpbq const & a, mpq const & b); bool neq(mpbq const & a, mpq const & b) { return !eq(a, b); } bool gt(mpbq const & a, mpq const & b) { return !le(a, b); } bool ge(mpbq const & a, mpq const & b) { return !lt(a, b); } bool eq(mpbq const & a, mpz const & b) { return m_manager.eq(a.m_num, b) && a.m_k == 0; } bool lt(mpbq const & a, mpz const & b); bool le(mpbq const & a, mpz const & b); bool neq(mpbq const & a, mpz const & b) { return !eq(a, b); } bool gt(mpbq const & a, mpz const & b) { return !le(a, b); } bool ge(mpbq const & a, mpz const & b) { return !lt(a, b); } /** \brief Return the magnitude of a = b/2^k. It is defined as: a == 0 -> 0 a > 0 -> log2(b) - k Note that 2^{log2(b) - k} <= a <= 2^{log2(b) - k + 1} a < 0 -> mlog2(b) - k + 1 Note that -2^{mlog2(b) - k + 1} <= a <= -2^{mlog2(b) - k} Remark: mlog2(b) = log2(-b) Examples: 5/2^3 log2(5) - 3 = -1 21/2^2 log2(21) - 2 = 2 -3/2^4 log2(3) - 4 + 1 = -2 */ int magnitude_lb(mpbq const & a); /** \brief Similar to magnitude_lb a == 0 -> 0 a > 0 -> log2(b) - k + 1 a <= 2^{log2(b) - k + 1} a < 0 -> mlog2(b) - k a <= -2^{mlog2(b) - k} */ int magnitude_ub(mpbq const & a); /** \brief Return true if a < 1/2^k */ bool lt_1div2k(mpbq const & a, unsigned k); std::string to_string(mpbq const & a); /** \brief Return true if q (= c/d) is a binary rational, and store it in bq (as a binary rational). Otherwise return false, and set bq to c/2^{k+1} where k = log2(d) */ bool to_mpbq(mpq const & q, mpbq & bq); /** \brief Given a rational q which cannot be represented as a binary rational, and an interval (l, u) s.t. l < q < u. This method stores in u, a u' s.t. q < u' < u. In the refinement process, the lower bound l may be also refined to l' s.t. l < l' < q */ void refine_upper(mpq const & q, mpbq & l, mpbq & u); /** \brief Similar to refine_upper. */ void refine_lower(mpq const & q, mpbq & l, mpbq & u); template void floor(mpz_manager & m, mpbq const & a, mpz & f) { if (is_int(a)) { m.set(f, a.m_num); return; } bool is_neg_num = is_neg(a); m.machine_div2k(a.m_num, a.m_k, f); if (is_neg_num) m.sub(f, mpz(1), f); } template void ceil(mpz_manager & m, mpbq const & a, mpz & c) { if (is_int(a)) { m.set(c, a.m_num); return; } bool is_pos_num = is_pos(a); m.machine_div2k(a.m_num, a.m_k, c); if (is_pos_num) m.add(c, mpz(1), c); } /** \brief Select some number in the interval [lower, upper]. Return true if succeeded, and false if lower > upper. This method tries to minimize the size (in bits) of r. For example, it will select an integer in [lower, upper] if the interval contains one. */ bool select_small(mpbq const & lower, mpbq const & upper, mpbq & r); /** \brief Similar to select_small, but assumes lower <= upper */ void select_small_core(mpbq const & lower, mpbq const & upper, mpbq & r); // Select some number in the interval (lower, upper] void select_small_core(unsynch_mpq_manager & qm, mpq const & lower, mpbq const & upper, mpbq & r); // Select some number in the interval [lower, upper) void select_small_core(unsynch_mpq_manager & qm, mpbq const & lower, mpq const & upper, mpbq & r); // Select some number in the interval (lower, upper) void select_small_core(unsynch_mpq_manager & qm, mpq const & lower, mpq const & upper, mpbq & r); void display(std::ostream & out, mpbq const & a); void display_pp(std::ostream & out, mpbq const & a); void display_decimal(std::ostream & out, mpbq const & a, unsigned prec = 8); /** \brief Display a in decimal while its digits match b digits. This function is useful when a and b are representing an interval [a,b] which contains an algebraic number */ void display_decimal(std::ostream & out, mpbq const & a, mpbq const & b, unsigned prec); void display_smt2(std::ostream & out, mpbq const & a, bool decimal); /** \brief Approximate n as b/2^k' s.t. k' <= k. if get_denominator_power(n) <= k, then n is not modified. if get_denominator_power(n) > k, then if to_plus_inf, old(n) < b/2^k' otherwise, b/2^k' < old(n) */ void approx(mpbq & n, unsigned k, bool to_plus_inf); /** \brief Approximated division c <- a/b The result is precise when: 1) b is a power of two 2) get_numerator(b) divides get_numerator(a) When the result is not precise, |c - a/b| <= 1/2^k Actually, we have that to_plus_inf => c - a/b <= 1/2^k not to_plus_inf => a/b - c <= 1/2^k */ void approx_div(mpbq const & a, mpbq const & b, mpbq & c, unsigned k=32, bool to_plus_inf=false); }; /** \brief Convert a binary rational into a rational */ template void to_mpq(mpq_manager & m, mpbq const & source, mpq & target) { mpq two(2); m.power(two, source.k(), target); m.inv(target); m.mul(source.numerator(), target, target); } /** \brief Convert a binary rational into a rational. */ rational to_rational(mpbq const & m); typedef _scoped_numeral scoped_mpbq; typedef _scoped_numeral_vector scoped_mpbq_vector; #define MPBQ_MK_COMPARISON_CORE(EXTERNAL, INTERNAL, TYPE) \ inline bool EXTERNAL(scoped_mpbq const & a, TYPE const & b) { \ mpbq_manager & m = a.m(); \ scoped_mpbq _b(m); \ m.set(_b, b); \ return m.INTERNAL(a, _b); \ } #define MPBQ_MK_COMPARISON(EXTERNAL, INTERNAL) \ MPBQ_MK_COMPARISON_CORE(EXTERNAL, INTERNAL, int) \ MPBQ_MK_COMPARISON_CORE(EXTERNAL, INTERNAL, mpz) \ MPBQ_MK_COMPARISON(operator==, eq); MPBQ_MK_COMPARISON(operator!=, neq); MPBQ_MK_COMPARISON(operator<, lt); MPBQ_MK_COMPARISON(operator<=, le); MPBQ_MK_COMPARISON(operator>, gt); MPBQ_MK_COMPARISON(operator>=, ge); #undef MPBQ_MK_COMPARISON #undef MPBQ_MK_COMPARISON_CORE #define MPBQ_MK_BINARY_CORE(EXTERNAL, INTERNAL, TYPE) \ inline scoped_mpbq EXTERNAL(scoped_mpbq const & a, TYPE const & b) { \ mpbq_manager & m = a.m(); \ scoped_mpbq _b(m); \ m.set(_b, b); \ scoped_mpbq r(m); \ m.INTERNAL(a, _b, r); \ return r; \ } #define MPBQ_MK_BINARY(EXTERNAL, INTERNAL) \ MPBQ_MK_BINARY_CORE(EXTERNAL, INTERNAL, int) \ MPBQ_MK_BINARY_CORE(EXTERNAL, INTERNAL, mpz) \ MPBQ_MK_BINARY(operator+, add) MPBQ_MK_BINARY(operator-, sub) MPBQ_MK_BINARY(operator*, mul) #undef MPBQ_MK_BINARY #undef MPBQ_MK_BINARY_CORE #endif z3-z3-4.8.7/src/util/mpbqi.h000066400000000000000000000022531356505360400154560ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: mpbqi.h Abstract: Binary Rational Number Intervals Author: Leonardo de Moura (leonardo) 2012-01-04 Revision History: --*/ #ifndef MPBQI_H_ #define MPBQI_H_ #include "util/mpbq.h" #include "util/basic_interval.h" class mpbqi_manager : public basic_interval_manager { typedef basic_interval_manager super; public: mpbqi_manager(mpbq_manager & m):super(m) {} void set(interval & a, interval const & b) { super::set(a, b); } void set(interval & a, bound const & lower, bound const & upper) { super::set(a, lower, upper); } void set(interval & a, bound const & n) { super::set(a, n); } void set(interval & a, mpz const & n) { m().set(a.lower(), n); m().set(a.upper(), n); } void add(interval const & a, interval const & b, interval & c) { super::add(a, b, c); } void add(interval const & a, mpz const & b, interval & c) { m().add(a.lower(), b, c.lower()); m().add(a.upper(), b, c.upper()); } }; typedef mpbqi_manager::interval mpbqi; typedef mpbqi_manager::scoped_interval scoped_mpbqi; #endif z3-z3-4.8.7/src/util/mpf.cpp000066400000000000000000002177761356505360400155050ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: mpf.cpp Abstract: Multi Precision Floating Point Numbers Author: Christoph Wintersteiger (cwinter) 2011-12-01. Revision History: --*/ #include #include #include "util/mpf.h" mpf::mpf() : ebits(0), sbits(0), sign(false), significand(0), exponent(0) { } void mpf::set(unsigned _ebits, unsigned _sbits) { ebits = _ebits; sbits = _sbits; sign = false; exponent = 0; } mpf::mpf(unsigned _ebits, unsigned _sbits): significand(0) { set(ebits, sbits); } mpf::~mpf() { } void mpf::swap(mpf & other) { unsigned tmp = ebits; ebits = other.ebits; other.ebits = tmp; tmp = sbits; sbits = other.sbits; other.sbits = tmp; tmp = sign; sign = other.sign; other.sign = tmp; significand.swap(other.significand); std::swap(exponent, other.exponent); } mpf_manager::mpf_manager() : m_mpz_manager(m_mpq_manager), m_powers2(m_mpz_manager) { } mpf_manager::~mpf_manager() { } void mpf_manager::set(mpf & o, unsigned ebits, unsigned sbits, int value) { static_assert(sizeof(int) == 4, "assume integers are 4 bytes"); o.sign = false; o.ebits = ebits; o.sbits = sbits; TRACE("mpf_dbg", tout << "set: value = " << value << std::endl;); if (value == 0) { mk_pzero(ebits, sbits, o); } else { unsigned uval=value; if (value < 0) { o.sign = true; if (value == INT_MIN) uval = 0x80000000; else uval = -value; } o.exponent = 31; while ((uval & 0x80000000) == 0) { uval <<= 1; o.exponent--; } m_mpz_manager.set(o.significand, uval & 0x7FFFFFFF); // remove the "1." part // align with sbits. if (sbits > 31) m_mpz_manager.mul2k(o.significand, sbits-32); else m_mpz_manager.machine_div2k(o.significand, 32-sbits); } TRACE("mpf_dbg", tout << "set: res = " << to_string(o) << std::endl;); } void mpf_manager::set(mpf & o, unsigned ebits, unsigned sbits, mpf_rounding_mode rm, int n, int d) { scoped_mpq tmp(m_mpq_manager); m_mpq_manager.set(tmp, n, d); set(o, ebits, sbits, rm, tmp); } void mpf_manager::set(mpf & o, unsigned ebits, unsigned sbits, double value) { // double === mpf(11, 53) static_assert(sizeof(double) == 8, "doubles are 8 bytes"); uint64_t raw; memcpy(&raw, &value, sizeof(double)); bool sign = (raw >> 63) != 0; int64_t e = ((raw & 0x7FF0000000000000ull) >> 52) - 1023; uint64_t s = raw & 0x000FFFFFFFFFFFFFull; TRACE("mpf_dbg", tout << "set: " << value << " is: raw=" << raw << " (double)" << " sign=" << sign << " s=" << s << " e=" << e << std::endl;); SASSERT(-1023 <= e && e <= +1024); o.ebits = ebits; o.sbits = sbits; o.sign = sign; if (e <= -((0x01ll<<(ebits-1))-1)) o.exponent = mk_bot_exp(ebits); else if (e >= (0x01ll<<(ebits-1))) o.exponent = mk_top_exp(ebits); else o.exponent = e; m_mpz_manager.set(o.significand, s); if (sbits < 53) m_mpz_manager.machine_div2k(o.significand, 53-sbits); else if (sbits > 53) m_mpz_manager.mul2k(o.significand, sbits-53); TRACE("mpf_dbg", tout << "set: res = " << to_string(o) << std::endl;); } void mpf_manager::set(mpf & o, unsigned ebits, unsigned sbits, float value) { // single === mpf(8, 24) static_assert(sizeof(float) == 4, "floats are 4 bytes"); unsigned int raw; memcpy(&raw, &value, sizeof(float)); bool sign = (raw >> 31) != 0; signed int e = ((raw & 0x7F800000) >> 23) - 127; unsigned int s = raw & 0x007FFFFF; TRACE("mpf_dbg", tout << "set: " << value << " is: raw=" << raw << " (float)" << " sign=" << sign << " s=" << s << " e=" << e << std::endl;); SASSERT(-127 <= e && e <= +128); o.ebits = ebits; o.sbits = sbits; o.sign = sign; if (e <= -((0x01ll<<(ebits-1))-1)) o.exponent = mk_bot_exp(ebits); else if (e >= (0x01ll<<(ebits-1))) o.exponent = mk_top_exp(ebits); else o.exponent = e; m_mpz_manager.set(o.significand, s); if (sbits < 24) m_mpz_manager.machine_div2k(o.significand, 24-sbits); else if (sbits > 24) m_mpz_manager.mul2k(o.significand, sbits-24); TRACE("mpf_dbg", tout << "set: res = " << to_string_raw(o) << std::endl;); } void mpf_manager::set(mpf & o, unsigned ebits, unsigned sbits, mpf_rounding_mode rm, mpq const & value) { TRACE("mpf_dbg", tout << "set: " << m_mpq_manager.to_string(value) << " [" << ebits << "/" << sbits << "]"<< std::endl;); scoped_mpz exp(m_mpz_manager); m_mpz_manager.set(exp, 0); set(o, ebits, sbits, rm, exp, value); TRACE("mpf_dbg", tout << "set: res = " << to_string(o) << std::endl;); } void mpf_manager::set(mpf & o, unsigned ebits, unsigned sbits, mpf_rounding_mode rm, char const * value) { TRACE("mpf_dbg", tout << "set: " << value << " [" << ebits << "/" << sbits << "]"<< std::endl;); o.ebits = ebits; o.sbits = sbits; // We expect [i].[f]P[e], where P means that the exponent is interpreted as 2^e instead of 10^e. std::string v(value); std::string f, e; bool sgn = false; if (v.substr(0, 1) == "-") { sgn = true; v = v.substr(1); } else if (v.substr(0, 1) == "+") v = v.substr(1); size_t e_pos = v.find('p'); if (e_pos == std::string::npos) e_pos = v.find('P'); f = (e_pos != std::string::npos) ? v.substr(0, e_pos) : v; e = (e_pos != std::string::npos) ? v.substr(e_pos+1) : "0"; TRACE("mpf_dbg", tout << "sgn = " << sgn << " f = " << f << " e = " << e << std::endl;); scoped_mpq q(m_mpq_manager); m_mpq_manager.set(q, f.c_str()); scoped_mpz ex(m_mpq_manager); m_mpz_manager.set(ex, e.c_str()); set(o, ebits, sbits, rm, ex, q); o.sign = sgn; TRACE("mpf_dbg", tout << "set: res = " << to_string(o) << std::endl;); } void mpf_manager::set(mpf & o, unsigned ebits, unsigned sbits, mpf_rounding_mode rm, mpz const & exponent, mpq const & significand) { // Assumption: this represents significand * 2^exponent. TRACE("mpf_dbg", tout << "set: sig = " << m_mpq_manager.to_string(significand) << " exp = " << m_mpz_manager.to_string(exponent) << std::endl;); o.ebits = ebits; o.sbits = sbits; o.sign = m_mpq_manager.is_neg(significand); if (m_mpq_manager.is_zero(significand)) mk_zero(ebits, sbits, o.sign, o); else { scoped_mpq sig(m_mpq_manager); scoped_mpz exp(m_mpq_manager); m_mpq_manager.set(sig, significand); m_mpq_manager.abs(sig); m_mpz_manager.set(exp, exponent); // Normalize such that 1.0 <= sig < 2.0 if (m_mpq_manager.lt(sig, 1)) { m_mpq_manager.inv(sig); unsigned int pp = m_mpq_manager.prev_power_of_two(sig); if (!m_mpq_manager.is_power_of_two(sig, pp)) pp++; scoped_mpz p2(m_mpz_manager); m_mpq_manager.power(2, pp, p2); m_mpq_manager.div(sig, p2, sig); m_mpz_manager.sub(exp, mpz(pp), exp); m_mpq_manager.inv(sig); } else if (m_mpq_manager.ge(sig, 2)) { unsigned int pp = m_mpq_manager.prev_power_of_two(sig); scoped_mpz p2(m_mpz_manager); m_mpq_manager.power(2, pp, p2); m_mpq_manager.div(sig, p2, sig); m_mpz_manager.add(exp, mpz(pp), exp); } TRACE("mpf_dbg", tout << "Normalized: sig = " << m_mpq_manager.to_string(sig) << " exp = " << m_mpz_manager.to_string(exp) << std::endl;); // Check that 1.0 <= sig < 2.0 SASSERT((m_mpq_manager.le(1, sig) && m_mpq_manager.lt(sig, 2))); scoped_mpz p(m_mpq_manager); scoped_mpq t(m_mpq_manager), sq(m_mpq_manager); m_mpz_manager.power(2, sbits + 3 - 1, p); m_mpq_manager.mul(p, sig, t); m_mpq_manager.floor(t, o.significand); m_mpq_manager.set(sq, o.significand); m_mpq_manager.div(sq, p, t); m_mpq_manager.sub(sig, t, sig); // sticky if (!m_mpq_manager.is_zero(sig) && m_mpz_manager.is_even(o.significand)) m_mpz_manager.inc(o.significand); TRACE("mpf_dbg", tout << "sig = " << m_mpz_manager.to_string(o.significand) << " exp = " << o.exponent << std::endl;); if (m_mpz_manager.is_small(exp)) { o.exponent = m_mpz_manager.get_int64(exp); round(rm, o); } else mk_inf(ebits, sbits, o.sign, o); } TRACE("mpf_dbg", tout << "set: res = " << to_string(o) << std::endl;); } void mpf_manager::set(mpf & o, unsigned ebits, unsigned sbits, bool sign, mpf_exp_t exponent, uint64_t significand) { // Assumption: this represents (sign * -1) * (significand/2^sbits) * 2^exponent. o.ebits = ebits; o.sbits = sbits; o.sign = sign; m_mpz_manager.set(o.significand, significand); o.exponent = exponent; DEBUG_CODE({ SASSERT(m_mpz_manager.lt(o.significand, m_powers2(sbits-1))); SASSERT(o.exponent <= mk_top_exp(ebits)); SASSERT(o.exponent >= mk_bot_exp(ebits)); }); } void mpf_manager::set(mpf & o, unsigned ebits, unsigned sbits, bool sign, mpf_exp_t exponent, mpz const & significand) { // Assumption: this represents (sign * -1) * (significand/2^sbits) * 2^exponent. o.ebits = ebits; o.sbits = sbits; o.sign = sign; m_mpz_manager.set(o.significand, significand); o.exponent = exponent; } void mpf_manager::set(mpf & o, mpf const & x) { o.ebits = x.ebits; o.sbits = x.sbits; o.sign = x.sign; o.exponent = x.exponent; m_mpz_manager.set(o.significand, x.significand); } void mpf_manager::set(mpf & o, unsigned ebits, unsigned sbits, mpf_rounding_mode rm, mpf const & x) { if (is_nan(x)) mk_nan(ebits, sbits, o); else if (is_inf(x)) mk_inf(ebits, sbits, x.sign, o); else if (is_zero(x)) mk_zero(ebits, sbits, x.sign, o); else if (x.ebits == ebits && x.sbits == sbits) set(o, x); else { set(o, x); unpack(o, true); o.ebits = ebits; o.sbits = sbits; signed ds = sbits - x.sbits + 3; // plus rounding bits if (ds > 0) { m_mpz_manager.mul2k(o.significand, ds); round(rm, o); } else if (ds < 0) { bool sticky = false; while (ds < 0) { sticky |= m_mpz_manager.is_odd(o.significand); m_mpz_manager.machine_div2k(o.significand, 1); ds++; } if (sticky && m_mpz_manager.is_even(o.significand)) m_mpz_manager.inc(o.significand); round(rm, o); } } } void mpf_manager::abs(mpf & o) { o.sign = false; } void mpf_manager::abs(mpf const & x, mpf & o) { set(o, x); abs(o); } void mpf_manager::neg(mpf & o) { if (!is_nan(o)) o.sign = !o.sign; } void mpf_manager::neg(mpf const & x, mpf & o) { set(o, x); neg(o); } bool mpf_manager::is_zero(mpf const & x) { return has_bot_exp(x) && m_mpz_manager.is_zero(sig(x)); } bool mpf_manager::is_one(mpf const & x) { return m_mpz_manager.is_zero(sig(x)) && exp(x) == 0; } bool mpf_manager::is_neg(mpf const & x) { return x.sign && !is_nan(x); } bool mpf_manager::is_pos(mpf const & x) { return !x.sign && !is_nan(x); } bool mpf_manager::is_nzero(mpf const & x) { return x.sign && is_zero(x); } bool mpf_manager::is_pzero(mpf const & x) { return !x.sign && is_zero(x); } bool mpf_manager::eq(mpf const & x, mpf const & y) { SASSERT(x.sbits == y.sbits && x.ebits == y.ebits); if (is_nan(x) || is_nan(y)) return false; else if (is_zero(x) && is_zero(y)) return true; else if (sgn(x) != sgn(y)) return false; else return exp(x)==exp(y) && m_mpz_manager.eq(sig(x), sig(y)); } bool mpf_manager::lt(mpf const & x, mpf const & y) { SASSERT(x.sbits == y.sbits && x.ebits == y.ebits); if (is_nan(x) || is_nan(y)) return false; else if (is_zero(x) && is_zero(y)) return false; else if (sgn(x)) { if (!sgn(y)) return true; else return exp(y) < exp(x) || (exp(y) == exp(x) && m_mpz_manager.lt(sig(y), sig(x))); } else { // !sgn(x) if (sgn(y)) return false; else return exp(x) < exp(y) || (exp(x)==exp(y) && m_mpz_manager.lt(sig(x), sig(y))); } } bool mpf_manager::lte(mpf const & x, mpf const & y) { SASSERT(x.sbits == y.sbits && x.ebits == y.ebits); return lt(x, y) || eq(x, y); } bool mpf_manager::gt(mpf const & x, mpf const & y) { SASSERT(x.sbits == y.sbits && x.ebits == y.ebits); if (is_nan(x) || is_nan(y)) return false; else if (is_zero(x) && is_zero(y)) return false; else return !lte(x, y); } bool mpf_manager::gte(mpf const & x, mpf const & y) { SASSERT(x.sbits == y.sbits && x.ebits == y.ebits); return gt(x, y) || eq(x, y); } void mpf_manager::add(mpf_rounding_mode rm, mpf const & x, mpf const & y, mpf & o) { add_sub(rm, x, y, o, false); } void mpf_manager::sub(mpf_rounding_mode rm, mpf const & x, mpf const & y, mpf & o) { add_sub(rm, x, y, o, true); } void mpf_manager::add_sub(mpf_rounding_mode rm, mpf const & x, mpf const & y, mpf & o, bool sub) { SASSERT(x.sbits == y.sbits && x.ebits == y.ebits); bool sgn_y = sgn(y) ^ sub; if (is_nan(x)) mk_nan(x.ebits, x.sbits, o); else if (is_nan(y)) mk_nan(x.ebits, x.sbits, o); else if (is_inf(x)) { if (is_inf(y) && (sgn(x) ^ sgn_y)) mk_nan(x.ebits, x.sbits, o); else set(o, x); } else if (is_inf(y)) { if (is_inf(x) && (sgn(x) ^ sgn_y)) mk_nan(x.ebits, x.sbits, o); else { set(o, y); o.sign = sgn_y; } } else if (is_zero(x) && is_zero(y)) { if ((x.sign && sgn_y) || ((rm == MPF_ROUND_TOWARD_NEGATIVE) && (x.sign != sgn_y))) mk_nzero(x.ebits, x.sbits, o); else mk_pzero(x.ebits, x.sbits, o); } else if (is_zero(x)) { set(o, y); o.sign = sgn_y; } else if (is_zero(y)) set(o, x); else { o.ebits = x.ebits; o.sbits = x.sbits; SASSERT(is_normal(x) || is_denormal(x)); SASSERT(is_normal(y) || is_denormal(y)); scoped_mpf a(*this), b(*this); set(a, x); set(b, y); b.get().sign = sgn_y; // Unpack a/b, this inserts the hidden bit and adjusts the exponent. unpack(a, false); unpack(b, false); if (exp(b) > exp(a)) a.swap(b); mpf_exp_t exp_delta = exp(a) - exp(b); SASSERT(exp(a) >= exp(b)); SASSERT(exp_delta >= 0); if (exp_delta > x.sbits+2) exp_delta = x.sbits+2; TRACE("mpf_dbg", tout << "A = " << to_string(a) << std::endl;); TRACE("mpf_dbg", tout << "B = " << to_string(b) << std::endl;); TRACE("mpf_dbg", tout << "d = " << exp_delta << std::endl;); // Introduce 3 extra bits into both numbers. m_mpz_manager.mul2k(a.significand(), 3, a.significand()); m_mpz_manager.mul2k(b.significand(), 3, b.significand()); // Alignment shift with sticky bit computation. SASSERT(exp_delta <= INT_MAX); scoped_mpz sticky_rem(m_mpz_manager); m_mpz_manager.machine_div_rem(b.significand(), m_powers2((int)exp_delta), b.significand(), sticky_rem); TRACE("mpf_dbg", tout << "A' = " << m_mpz_manager.to_string(a.significand()) << std::endl;); TRACE("mpf_dbg", tout << "B' = " << m_mpz_manager.to_string(b.significand()) << std::endl;); // Significand addition if (sgn(a) != sgn(b)) { TRACE("mpf_dbg", tout << "SUBTRACTING" << std::endl;); m_mpz_manager.sub(a.significand(), b.significand(), o.significand); if (!sticky_rem.is_zero() && m_mpz_manager.is_even(o.significand)) m_mpz_manager.dec(o.significand); } else { TRACE("mpf_dbg", tout << "ADDING" << std::endl;); m_mpz_manager.add(a.significand(), b.significand(), o.significand); if (!sticky_rem.is_zero() && m_mpz_manager.is_even(o.significand)) m_mpz_manager.inc(o.significand); } TRACE("mpf_dbg", tout << "sum[-2:sbits+2] = " << m_mpz_manager.to_string(o.significand) << std::endl;); if (m_mpz_manager.is_zero(o.significand)) mk_zero(o.ebits, o.sbits, rm == MPF_ROUND_TOWARD_NEGATIVE, o); else { bool neg = m_mpz_manager.is_neg(o.significand); TRACE("mpf_dbg", tout << "NEG=" << neg << std::endl;); m_mpz_manager.abs(o.significand); TRACE("mpf_dbg", tout << "fs[-1:sbits+2] = " << m_mpz_manager.to_string(o.significand) << std::endl;); o.sign = ((!a.sign() && b.sign() && neg) || ( a.sign() && !b.sign() && !neg) || ( a.sign() && b.sign())); o.exponent = a.exponent(); round(rm, o); } } } void mpf_manager::mul(mpf_rounding_mode rm, mpf const & x, mpf const & y, mpf & o) { TRACE("mpf_mul_bug", tout << "rm: " << rm << "\n"; tout << "X: " << to_string(x) << "\n"; tout << "Y: " << to_string(y) << "\n";); SASSERT(x.sbits == y.sbits && x.ebits == y.ebits); TRACE("mpf_dbg", tout << "X = " << to_string(x) << std::endl;); TRACE("mpf_dbg", tout << "Y = " << to_string(y) << std::endl;); if (is_nan(x)) mk_nan(x.ebits, x.sbits, o); else if (is_nan(y)) mk_nan(x.ebits, x.sbits, o); else if (is_pinf(x)) { if (is_zero(y)) mk_nan(x.ebits, x.sbits, o); else mk_inf(x.ebits, x.sbits, y.sign, o); } else if (is_pinf(y)) { if (is_zero(x)) mk_nan(x.ebits, x.sbits, o); else mk_inf(x.ebits, x.sbits, x.sign, o); } else if (is_ninf(x)) { if (is_zero(y)) mk_nan(x.ebits, x.sbits, o); else mk_inf(x.ebits, x.sbits, !y.sign, o); } else if (is_ninf(y)) { if (is_zero(x)) mk_nan(x.ebits, x.sbits, o); else mk_inf(x.ebits, x.sbits, !x.sign, o); } else if (is_zero(x) || is_zero(y)) { mk_zero(x.ebits, x.sbits, x.sign != y.sign, o); } else { o.ebits = x.ebits; o.sbits = x.sbits; o.sign = x.sign ^ y.sign; scoped_mpf a(*this, x.ebits, x.sbits), b(*this, x.ebits, x.sbits); set(a, x); set(b, y); unpack(a, true); unpack(b, true); TRACE("mpf_dbg", tout << "A = " << to_string(a) << std::endl;); TRACE("mpf_dbg", tout << "B = " << to_string(b) << std::endl;); o.exponent = a.exponent() + b.exponent(); TRACE("mpf_dbg", tout << "A' = " << m_mpz_manager.to_string(a.significand()) << std::endl;); TRACE("mpf_dbg", tout << "B' = " << m_mpz_manager.to_string(b.significand()) << std::endl;); m_mpz_manager.mul(a.significand(), b.significand(), o.significand); TRACE("mpf_dbg", tout << "PRODUCT = " << to_string(o) << std::endl;); // Remove the extra bits, keeping a sticky bit. scoped_mpz sticky_rem(m_mpz_manager); if (o.sbits >= 4) m_mpz_manager.machine_div_rem(o.significand, m_powers2(o.sbits - 4), o.significand, sticky_rem); else m_mpz_manager.mul2k(o.significand, 4-o.sbits, o.significand); if (!m_mpz_manager.is_zero(sticky_rem) && m_mpz_manager.is_even(o.significand)) m_mpz_manager.inc(o.significand); round(rm, o); } TRACE("mpf_mul_bug", tout << "result: " << to_string(o) << "\n";); } void mpf_manager::div(mpf_rounding_mode rm, mpf const & x, mpf const & y, mpf & o) { SASSERT(x.sbits == y.sbits && x.ebits == y.ebits); TRACE("mpf_dbg", tout << "X = " << to_string(x) << std::endl;); TRACE("mpf_dbg", tout << "Y = " << to_string(y) << std::endl;); if (is_nan(x)) mk_nan(x.ebits, x.sbits, o); else if (is_nan(y)) mk_nan(x.ebits, x.sbits, o); else if (is_pinf(x)) { if (is_inf(y)) mk_nan(x.ebits, x.sbits, o); else mk_inf(x.ebits, x.sbits, y.sign, o); } else if (is_pinf(y)) { if (is_inf(x)) mk_nan(x.ebits, x.sbits, o); else mk_zero(x.ebits, x.sbits, x.sign != y.sign, o); } else if (is_ninf(x)) { if (is_inf(y)) mk_nan(x.ebits, x.sbits, o); else mk_inf(x.ebits, x.sbits, !y.sign, o); } else if (is_ninf(y)) { if (is_inf(x)) mk_nan(x.ebits, x.sbits, o); else mk_zero(x.ebits, x.sbits, x.sign != y.sign, o); } else if (is_zero(y)) { if (is_zero(x)) mk_nan(x.ebits, x.sbits, o); else mk_inf(x.ebits, x.sbits, x.sign != y.sign, o); } else if (is_zero(x)) { // Special case to avoid problems with unpacking of zeros. mk_zero(x.ebits, x.sbits, x.sign != y.sign, o); } else { o.ebits = x.ebits; o.sbits = x.sbits; o.sign = x.sign ^ y.sign; scoped_mpf a(*this), b(*this); set(a, x); set(b, y); unpack(a, true); unpack(b, true); TRACE("mpf_dbg", tout << "A = " << to_string(a) << std::endl;); TRACE("mpf_dbg", tout << "B = " << to_string(b) << std::endl;); o.exponent = a.exponent() - b.exponent(); TRACE("mpf_dbg", tout << "A' = " << m_mpz_manager.to_string(a.significand()) << std::endl;); TRACE("mpf_dbg", tout << "B' = " << m_mpz_manager.to_string(b.significand()) << std::endl;); unsigned extra_bits = x.sbits + 2; m_mpz_manager.mul2k(a.significand(), x.sbits + extra_bits); m_mpz_manager.machine_div(a.significand(), b.significand(), o.significand); TRACE("mpf_dbg", tout << "QUOTIENT = " << to_string(o) << std::endl;); // Remove the extra bits, keeping a sticky bit. scoped_mpz sticky_rem(m_mpz_manager); m_mpz_manager.machine_div_rem(o.significand, m_powers2(extra_bits-2), o.significand, sticky_rem); if (!m_mpz_manager.is_zero(sticky_rem) && m_mpz_manager.is_even(o.significand)) m_mpz_manager.inc(o.significand); TRACE("mpf_dbg", tout << "QUOTIENT' = " << to_string(o) << std::endl;); round(rm, o); } } void mpf_manager::fma(mpf_rounding_mode rm, mpf const & x, mpf const & y, mpf const &z, mpf & o) { SASSERT(x.sbits == y.sbits && x.ebits == y.ebits && x.sbits == y.sbits && z.ebits == z.ebits); TRACE("mpf_dbg", tout << "X = " << to_string(x) << std::endl;); TRACE("mpf_dbg", tout << "Y = " << to_string(y) << std::endl;); TRACE("mpf_dbg", tout << "Z = " << to_string(z) << std::endl;); if (is_nan(x) || is_nan(y) || is_nan(z)) mk_nan(x.ebits, x.sbits, o); else if (is_pinf(x)) { if (is_zero(y)) mk_nan(x.ebits, x.sbits, o); else if (is_inf(z) && sgn(x) ^ sgn (y) ^ sgn(z)) mk_nan(x.ebits, x.sbits, o); else mk_inf(x.ebits, x.sbits, y.sign, o); } else if (is_pinf(y)) { if (is_zero(x)) mk_nan(x.ebits, x.sbits, o); else if (is_inf(z) && sgn(x) ^ sgn (y) ^ sgn(z)) mk_nan(x.ebits, x.sbits, o); else mk_inf(x.ebits, x.sbits, x.sign, o); } else if (is_ninf(x)) { if (is_zero(y)) mk_nan(x.ebits, x.sbits, o); else if (is_inf(z) && sgn(x) ^ sgn (y) ^ sgn(z)) mk_nan(x.ebits, x.sbits, o); else mk_inf(x.ebits, x.sbits, !y.sign, o); } else if (is_ninf(y)) { if (is_zero(x)) mk_nan(x.ebits, x.sbits, o); else if (is_inf(z) && sgn(x) ^ sgn (y) ^ sgn(z)) mk_nan(x.ebits, x.sbits, o); else mk_inf(x.ebits, x.sbits, !x.sign, o); } else if (is_inf(z)) { set(o, z); } else if (is_zero(x) || is_zero(y)) { bool xy_sgn = sgn(x) ^ sgn(y); if (is_zero(z) && (xy_sgn ^ sgn(z))) mk_zero(x.ebits, x.sbits, rm == MPF_ROUND_TOWARD_NEGATIVE, o); else set(o, z); } else { o.ebits = x.ebits; o.sbits = x.sbits; scoped_mpf mr(*this); scoped_mpf a(*this, x.ebits, x.sbits), b(*this, x.ebits, x.sbits), c(*this, x.ebits, x.sbits); set(a, x); set(b, y); set(c, z); unpack(a, true); unpack(b, true); unpack(c, true); TRACE("mpf_dbg", tout << "A = " << to_string(a) << std::endl; tout << "B = " << to_string(b) << std::endl; tout << "C = " << to_string(c) << std::endl; tout << "A = " << to_string_binary(a, 1, 0) << std::endl; tout << "B = " << to_string_binary(b, 1, 0) << std::endl; tout << "C = " << to_string_binary(c, 1, 0) << std::endl;); SASSERT(m_mpz_manager.lt(a.significand(), m_powers2(x.sbits))); SASSERT(m_mpz_manager.lt(b.significand(), m_powers2(x.sbits))); SASSERT(m_mpz_manager.lt(c.significand(), m_powers2(x.sbits))); SASSERT(m_mpz_manager.ge(a.significand(), m_powers2(x.sbits-1))); SASSERT(m_mpz_manager.ge(b.significand(), m_powers2(x.sbits-1))); // A/B in _1.[sbits-1]. mr.set(x.ebits+2, 2*x.sbits-1, a.sign() != b.sign(), a.exponent() + b.exponent()); m_mpz_manager.mul(a.significand(), b.significand(), mr.significand()); TRACE("mpf_dbg", tout << "M = " << to_string(mr) << std::endl; tout << "M = " << to_string_binary(mr, 1, 0) << std::endl;); // mul_res is [-1][0].[2*sbits - 2], i.e., >= 2^(2*sbits-2) and < 2^(2*sbits). SASSERT(m_mpz_manager.lt(mr.significand(), m_powers2(2*x.sbits))); SASSERT(m_mpz_manager.ge(mr.significand(), m_powers2(2*x.sbits - 2))); // Introduce (sbits+3) extra bits into c in _[0].[sbits-1] s.t. c in _[0].[2*sbits-2] c.set(x.ebits+2, 2*x.sbits-1+3, c.sign(), c.exponent(), c.significand()); m_mpz_manager.mul2k(c.significand(), x.sbits - 1 + 3); // And + 3 bits into mr as well. mr.set(x.ebits + 2, 2 * x.sbits - 1 + 3, mr.sign(), mr.exponent(), mr.significand()); m_mpz_manager.mul2k(mr.significand(), 3); TRACE("mpf_dbg", tout << "C_= " << to_string(c) << std::endl; tout << "C_= " << to_string_binary(c, 1, 0) << std::endl;); SASSERT(m_mpz_manager.lt(c.significand(), m_powers2(2*x.sbits + 3))); SASSERT(m_mpz_manager.is_zero(c.significand()) || m_mpz_manager.ge(c.significand(), m_powers2(2*x.sbits - 2 + 3))); if (exp(c) > exp(mr)) { TRACE("mpf_dbg", tout << "Swap!" << std::endl;); mr.swap(c); } // Alignment shift with sticky bit computation. mpf_exp_t exp_delta_w = exp(mr) - exp(c); SASSERT(exp(mr) >= exp(c) && exp_delta_w >= 0); if (exp_delta_w > 2 * x.sbits + 3) exp_delta_w = 2 * x.sbits + 3; unsigned exp_delta = (unsigned)exp_delta_w; scoped_mpz sticky_rem(m_mpz_manager); m_mpz_manager.machine_div_rem(c.significand(), m_powers2(exp_delta), c.significand(), sticky_rem); TRACE("mpf_dbg", tout << "alignment shift by delta=" << exp_delta << " -> sig = " << m_mpz_manager.to_string(c.significand()) << " sticky_rem = " << m_mpz_manager.to_string(sticky_rem) << std::endl;); bool alignment_sticky = !m_mpz_manager.is_zero(sticky_rem); TRACE("mpf_dbg", tout << "M'= " << m_mpz_manager.to_string(mr.significand()) << std::endl; tout << "M'= " << to_string_binary(mr, 1, 0) << std::endl; ); TRACE("mpf_dbg", tout << "C'= " << m_mpz_manager.to_string(c.significand()) << std::endl; tout << "C'= " << to_string_binary(c, 1, 0) << std::endl; ); // Significand addition scoped_mpf res(mr); if (sgn(mr) != sgn(c)) { TRACE("mpf_dbg", tout << "SUBTRACTING" << std::endl;); m_mpz_manager.sub(mr.significand(), c.significand(), res.significand()); if (alignment_sticky && m_mpz_manager.is_even(res.significand())) m_mpz_manager.dec(res.get().significand); if (m_mpz_manager.is_neg(res.significand())) { m_mpz_manager.abs(res.significand()); res.get().sign = !res.sign(); } } else { TRACE("mpf_dbg", tout << "ADDING" << std::endl;); m_mpz_manager.add(mr.significand(), c.significand(), res.significand()); if (alignment_sticky && m_mpz_manager.is_even(res.significand())) m_mpz_manager.inc(res.get().significand); } TRACE("mpf_dbg", tout << "S'= " << m_mpz_manager.to_string(res.significand()) << std::endl; tout << "S'= " << to_string_binary(res, 1, 0) << std::endl; ); // Renormalize bool renorm_sticky = false; SASSERT(m_mpz_manager.lt(res.significand(), m_powers2(2 * x.sbits + 1 + 3))); if (m_mpz_manager.ge(res.significand(), m_powers2(2 * x.sbits + 3))) { SASSERT(exp(res) < mk_max_exp(x.ebits)); // NYI. res.get().exponent++; renorm_sticky = !m_mpz_manager.is_even(res.significand()); m_mpz_manager.machine_div2k(res.significand(), 1); TRACE("mpf_dbg", tout << "Add/Sub overflew into 4.xxx!" << std::endl; tout << "S*= " << to_string_binary(res, 2, 0) << std::endl;); } mpf_exp_t min_exp = mk_min_exp(x.ebits); unsigned sig_width = m_mpz_manager.prev_power_of_two(res.get().significand) + 1; mpf_exp_t sig_lz = 2 * x.sbits + 3 - sig_width; mpf_exp_t max_exp_delta = res.exponent() - min_exp; unsigned renorm_delta = (unsigned) std::max((mpf_exp_t)0, std::min(sig_lz, max_exp_delta)); res.get().exponent -= renorm_delta; m_mpz_manager.mul2k(res.significand(), renorm_delta); TRACE("mpf_dbg", tout << "R*= " << to_string_binary(res, 2, 0) << " (renormalized, delta=" << renorm_delta << ")" << std::endl;); set(o, x.ebits, x.sbits, res.sign(), res.exponent(), mpz(0)); if (x.sbits >= 4) { m_mpz_manager.machine_div_rem(res.significand(), m_powers2(x.sbits - 4 + 3), o.significand, sticky_rem); renorm_sticky |= !m_mpz_manager.is_zero(sticky_rem); } else { m_mpz_manager.mul2k(res.significand(), 4 - x.sbits + 3, o.significand); } if (renorm_sticky && m_mpz_manager.is_even(o.significand)) m_mpz_manager.inc(o.significand); TRACE("mpf_dbg", tout << "sum[-1:sbits+2] = " << m_mpz_manager.to_string(o.significand) << std::endl; tout << "R = " << to_string_binary(o, 1, 3) << std::endl;); if (m_mpz_manager.is_zero(o.significand)) mk_zero(x.ebits, x.sbits, rm == MPF_ROUND_TOWARD_NEGATIVE, o); else round(rm, o); } } void my_mpz_sqrt(unsynch_mpz_manager & m, unsigned sbits, bool odd_exp, mpz & in, mpz & o) { scoped_mpz lower(m), upper(m); scoped_mpz mid(m), product(m), diff(m); // we have lower <= a.significand <= upper and we need 1.[52+3 bits] in the bounds. // since we compare upper*upper to a.significand further down, we need a.significand // to be of twice the size. m.set(lower, 1); m.mul2k(lower, sbits+2-1); if (odd_exp) m.mul2k(in, 4, upper); else m.mul2k(in, 3, upper); if (m.eq(lower, upper)) m.set(o, lower); while (m.neq(lower, upper)) { STRACE("mpf_dbg", tout << "SIG = " << m.to_string(in) << " LOWER = " << m.to_string(lower) << " UPPER = " << m.to_string(upper) << std::endl;); m.sub(upper, lower, diff); if (m.is_one(diff)) { m.mul(lower, lower, product); if (m.eq(product, in)) { STRACE("mpf_dbg", tout << "choosing lower" << std::endl;); m.set(o, lower); } else { STRACE("mpf_dbg", tout << "choosing upper" << std::endl;); m.set(o, upper); // choosing upper is like a sticky bit here. } break; } m.add(lower, upper, mid); m.machine_div2k(mid, 1); m.mul(mid, mid, product); STRACE("mpf_dbg", tout << "MID = " << m.to_string(mid) << " PROD = " << m.to_string(product) << std::endl;); if (m.lt(product, in)) m.set(lower, mid); else if (m.gt(product, in)) m.set(upper, mid); else { SASSERT(m.eq(product, in)); m.set(o, mid); break; } } } void mpf_manager::sqrt(mpf_rounding_mode rm, mpf const & x, mpf & o) { SASSERT(x.ebits > 0 && x.sbits > 0); TRACE("mpf_dbg", tout << "X = " << to_string(x) << std::endl;); if (is_nan(x)) mk_nan(x.ebits, x.sbits, o); else if (is_pinf(x)) set(o, x); else if (is_zero(x)) set(o, x); else if (x.sign) mk_nan(x.ebits, x.sbits, o); else { o.ebits = x.ebits; o.sbits = x.sbits; o.sign = false; scoped_mpf a(*this); set(a, x); unpack(a, true); TRACE("mpf_dbg", tout << "A = " << to_string(a) << std::endl;); m_mpz_manager.mul2k(a.significand(), x.sbits + ((a.exponent() % 2)?6:7)); if (!m_mpz_manager.root(a.significand(), 2, o.significand)) { // If the result is inexact, it is 1 too large. // We need a sticky bit in the last position here, so we fix that. if (m_mpz_manager.is_even(o.significand)) m_mpz_manager.dec(o.significand); TRACE("mpf_dbg", tout << "dec'ed " << m_mpz_manager.to_string(o.significand) << std::endl;); } o.exponent = a.exponent() >> 1; if (a.exponent() % 2 == 0) o.exponent--; round(rm, o); } TRACE("mpf_dbg", tout << "SQRT = " << to_string(o) << std::endl;); } void mpf_manager::round_to_integral(mpf_rounding_mode rm, mpf const & x, mpf & o) { SASSERT(x.ebits > 0 && x.sbits > 0); TRACE("mpf_dbg", tout << "X = " << to_string(x) << std::endl;); if (is_nan(x)) mk_nan(x.ebits, x.sbits, o); else if (is_inf(x)) set(o, x); else if (is_zero(x)) mk_zero(x.ebits, x.sbits, x.sign, o); // -0.0 -> -0.0, says IEEE754, Sec 5.9. else if (x.exponent < 0) { if (rm == MPF_ROUND_TOWARD_ZERO) mk_zero(x.ebits, x.sbits, x.sign, o); else if (rm == MPF_ROUND_TOWARD_NEGATIVE) { if (x.sign) mk_one(x.ebits, x.sbits, true, o); else mk_zero(x.ebits, x.sbits, false, o); } else if (rm == MPF_ROUND_TOWARD_POSITIVE) { if (x.sign) mk_zero(x.ebits, x.sbits, true, o); else mk_one(x.ebits, x.sbits, false, o); } else { SASSERT(rm == MPF_ROUND_NEAREST_TEVEN || rm == MPF_ROUND_NEAREST_TAWAY); bool tie = m_mpz_manager.is_zero(x.significand) && x.exponent == -1; TRACE("mpf_dbg", tout << "tie = " << tie << std::endl;); if (tie && rm == MPF_ROUND_NEAREST_TEVEN) mk_zero(x.ebits, x.sbits, x.sign, o); else if (tie && rm == MPF_ROUND_NEAREST_TAWAY) mk_one(x.ebits, x.sbits, x.sign, o); else if (x.exponent < -1) mk_zero(x.ebits, x.sbits, x.sign, o); else mk_one(x.ebits, x.sbits, x.sign, o); } } else if (x.exponent >= x.sbits - 1) set(o, x); else { SASSERT(x.exponent >= 0 && x.exponent < x.sbits-1); o.ebits = x.ebits; o.sbits = x.sbits; o.sign = x.sign; scoped_mpf a(*this); set(a, x); unpack(a, true); // A includes hidden bit TRACE("mpf_dbg", tout << "A = " << to_string_raw(a) << std::endl;); SASSERT(m_mpz_manager.lt(a.significand(), m_powers2(x.sbits))); SASSERT(m_mpz_manager.ge(a.significand(), m_powers2(x.sbits - 1))); o.exponent = a.exponent(); m_mpz_manager.set(o.significand, a.significand()); unsigned shift = (o.sbits - 1) - ((unsigned)o.exponent); const mpz & shift_p = m_powers2(shift); const mpz & shiftm1_p = m_powers2(shift-1); TRACE("mpf_dbg", tout << "shift=" << shift << std::endl;); TRACE("mpf_dbg", tout << "shiftm1_p=" << m_mpz_manager.to_string(shiftm1_p) << std::endl;); scoped_mpz div(m_mpz_manager), rem(m_mpz_manager); m_mpz_manager.machine_div_rem(o.significand, shift_p, div, rem); TRACE("mpf_dbg", tout << "div=" << m_mpz_manager.to_string(div) << " rem=" << m_mpz_manager.to_string(rem) << std::endl;); switch (rm) { case MPF_ROUND_NEAREST_TEVEN: case MPF_ROUND_NEAREST_TAWAY: { bool tie = m_mpz_manager.eq(rem, shiftm1_p); bool less_than_tie = m_mpz_manager.lt(rem, shiftm1_p); bool more_than_tie = m_mpz_manager.gt(rem, shiftm1_p); (void)less_than_tie; TRACE("mpf_dbg", tout << "tie= " << tie << "; tie = " << more_than_tie << std::endl;); if (tie) { if ((rm == MPF_ROUND_NEAREST_TEVEN && m_mpz_manager.is_odd(div)) || (rm == MPF_ROUND_NEAREST_TAWAY)) { TRACE("mpf_dbg", tout << "div++ (1)" << std::endl;); m_mpz_manager.inc(div); } } else { SASSERT(less_than_tie || more_than_tie); if (more_than_tie) { m_mpz_manager.inc(div); TRACE("mpf_dbg", tout << "div++ (2)" << std::endl;); } } break; } case MPF_ROUND_TOWARD_POSITIVE: if (!m_mpz_manager.is_zero(rem) && !o.sign) m_mpz_manager.inc(div); break; case MPF_ROUND_TOWARD_NEGATIVE: if (!m_mpz_manager.is_zero(rem) && o.sign) m_mpz_manager.inc(div); break; case MPF_ROUND_TOWARD_ZERO: default: /* nothing */; } m_mpz_manager.mul2k(div, shift, o.significand); SASSERT(m_mpz_manager.ge(o.significand, m_powers2(o.sbits - 1))); // re-normalize while (m_mpz_manager.ge(o.significand, m_powers2(o.sbits))) { m_mpz_manager.machine_div2k(o.significand, 1); o.exponent++; } m_mpz_manager.sub(o.significand, m_powers2(o.sbits - 1), o.significand); // strip hidden bit } TRACE("mpf_dbg", tout << "INTEGRAL = " << to_string(o) << std::endl;); } void mpf_manager::to_mpz(mpf const & x, unsynch_mpz_manager & zm, mpz & o) { // x is assumed to be unpacked. SASSERT(x.exponent < INT_MAX); zm.set(o, x.significand); if (x.sign) zm.neg(o); int e = (int)x.exponent - x.sbits + 1; if (e < 0) zm.machine_div2k(o, -e); else zm.mul2k(o, e); } void mpf_manager::to_sbv_mpq(mpf_rounding_mode rm, const mpf & x, scoped_mpq & o) { SASSERT(!is_nan(x) && !is_inf(x)); scoped_mpf t(*this); scoped_mpz z(m_mpz_manager); set(t, x); unpack(t, true); SASSERT(t.exponent() < INT_MAX); m_mpz_manager.set(z, t.significand()); mpf_exp_t e = (mpf_exp_t)t.exponent() - t.sbits() + 1; if (e < 0) { bool last = m_mpz_manager.is_odd(z), round = false, sticky = false; for (; e != 0; e++) { m_mpz_manager.machine_div2k(z, 1); sticky |= round; round = last; last = m_mpz_manager.is_odd(z); } bool inc = false; switch (rm) { case MPF_ROUND_NEAREST_TEVEN: inc = round && (last || sticky); break; case MPF_ROUND_NEAREST_TAWAY: inc = round; break; case MPF_ROUND_TOWARD_POSITIVE: inc = (!x.sign && (round || sticky)); break; case MPF_ROUND_TOWARD_NEGATIVE: inc = (x.sign && (round || sticky)); break; case MPF_ROUND_TOWARD_ZERO: inc = false; break; default: UNREACHABLE(); } if (inc) m_mpz_manager.inc(z); TRACE("mpf_dbg_sbv", tout << "SBV: (" << to_string(x) << ") == " << m_mpq_manager.to_string(z) << std::endl; tout << "sign=" << t.sign() << " last=" << last << " round=" << round << " sticky=" << sticky << " inc=" << inc << std::endl; ); } else m_mpz_manager.mul2k(z, (unsigned) e); m_mpq_manager.set(o, z); if (x.sign) m_mpq_manager.neg(o); TRACE("mpf_dbg", tout << "SBV = " << m_mpq_manager.to_string(o) << std::endl;); } void mpf_manager::to_ieee_bv_mpz(const mpf & x, scoped_mpz & o) { SASSERT(!is_nan(x)); SASSERT(exp(x) < INT_MAX); unsigned sbits = x.get_sbits(); unsigned ebits = x.get_ebits(); if (is_inf(x)) { m_mpz_manager.set(o, sgn(x)); m_mpz_manager.mul2k(o, ebits); const mpz & exp = m_powers2.m1(ebits); m_mpz_manager.add(o, exp, o); m_mpz_manager.mul2k(o, sbits - 1); } else { scoped_mpz biased_exp(m_mpz_manager); m_mpz_manager.set(biased_exp, bias_exp(ebits, exp(x))); m_mpz_manager.set(o, sgn(x)); m_mpz_manager.mul2k(o, ebits); m_mpz_manager.add(o, biased_exp, o); m_mpz_manager.mul2k(o, sbits - 1); m_mpz_manager.add(o, sig(x), o); } TRACE("mpf_dbg", tout << "IEEE_BV = " << m_mpz_manager.to_string(o) << std::endl;); } void mpf_manager::renormalize(unsigned ebits, unsigned sbits, mpf_exp_t & exp, mpz & sig) { if (m_mpz_manager.is_zero(sig)) return; const mpz & pg = m_powers2(sbits); while (m_mpz_manager.ge(sig, pg)) { m_mpz_manager.machine_div2k(sig, 1); exp++; } const mpz & pl = m_powers2(sbits-1); while (m_mpz_manager.lt(sig, pl)) { m_mpz_manager.mul2k(sig, 1); exp--; } } void mpf_manager::partial_remainder(mpf & x, mpf const & y, mpf_exp_t const & exp_diff, bool partial) { unsigned ebits = x.ebits; unsigned sbits = x.sbits; SASSERT(-1 <= exp_diff && exp_diff < INT64_MAX); SASSERT(exp_diff < ebits+sbits || partial); signed int D = (signed int)(exp_diff); mpf_exp_t N = sbits-1; TRACE("mpf_dbg_rem", tout << "x=" << to_string(x) << std::endl; tout << "y=" << to_string(y) << std::endl; tout << "d=" << D << std::endl; tout << "partial=" << partial << std::endl;); SASSERT(m_mpz_manager.lt(x.significand, m_powers2(sbits))); SASSERT(m_mpz_manager.ge(x.significand, m_powers2(sbits - 1))); SASSERT(m_mpz_manager.lt(y.significand, m_powers2(sbits))); SASSERT(m_mpz_manager.ge(y.significand, m_powers2(sbits - 1))); // 1. Compute a/b bool x_div_y_sgn = x.sign != y.sign; mpf_exp_t x_div_y_exp = D; scoped_mpz x_sig_shifted(m_mpz_manager), x_div_y_sig_lrg(m_mpz_manager), x_div_y_rem(m_mpz_manager); scoped_mpz x_rem_y_sig(m_mpz_manager); m_mpz_manager.mul2k(x.significand, 2*sbits + 2, x_sig_shifted); m_mpz_manager.machine_div_rem(x_sig_shifted, y.significand, x_div_y_sig_lrg, x_div_y_rem); // rem useful? // x/y is in x_diuv_y_sig_lrg and has sbits+3 extra bits. TRACE("mpf_dbg_rem", tout << "X/Y_exp=" << x_div_y_exp << std::endl; tout << "X/Y_sig_lrg=" << m_mpz_manager.to_string(x_div_y_sig_lrg) << std::endl; tout << "X/Y_rem=" << m_mpz_manager.to_string(x_div_y_rem) << std::endl; tout << "X/Y~=" << to_string_hexfloat(x_div_y_sgn, x_div_y_exp, x_div_y_sig_lrg, ebits, sbits, sbits+3) << std::endl;); // 2. Round a/b to integer Q/QQ bool Q_sgn = x_div_y_sgn; mpf_exp_t Q_exp = x_div_y_exp; scoped_mpz Q_sig(m_mpz_manager), Q_rem(m_mpz_manager); unsigned Q_shft = (sbits-1) + (sbits+3) - (unsigned) (partial ? N :Q_exp); if (partial) { // Round according to MPF_ROUND_TOWARD_ZERO SASSERT(0 < N && N < Q_exp && Q_exp < INT_MAX); m_mpz_manager.machine_div2k(x_div_y_sig_lrg, Q_shft, Q_sig); } else { // Round according to MPF_ROUND_NEAREST_TEVEN m_mpz_manager.machine_div_rem(x_div_y_sig_lrg, m_powers2(Q_shft), Q_sig, Q_rem); const mpz & shiftm1_p = m_powers2(Q_shft-1); bool tie = m_mpz_manager.eq(Q_rem, shiftm1_p); bool more_than_tie = m_mpz_manager.gt(Q_rem, shiftm1_p); TRACE("mpf_dbg_rem", tout << "tie= " << tie << "; >tie= " << more_than_tie << std::endl;); if ((tie && m_mpz_manager.is_odd(Q_sig)) || more_than_tie) m_mpz_manager.inc(Q_sig); } m_mpz_manager.mul2k(Q_sig, Q_shft); m_mpz_manager.machine_div2k(Q_sig, sbits+3); renormalize(ebits, sbits, Q_exp, Q_sig); (void)Q_sgn; TRACE("mpf_dbg_rem", tout << "Q_exp=" << Q_exp << std::endl; tout << "Q_sig=" << m_mpz_manager.to_string(Q_sig) << std::endl; tout << "Q=" << to_string_hexfloat(Q_sgn, Q_exp, Q_sig, ebits, sbits, 0) << std::endl;); if ((D == -1 || partial) && m_mpz_manager.is_zero(Q_sig)) return; // This means x % y = x. // no extra bits in Q_sig. SASSERT(!m_mpz_manager.is_zero(Q_sig)); SASSERT(m_mpz_manager.lt(Q_sig, m_powers2(sbits))); SASSERT(m_mpz_manager.ge(Q_sig, m_powers2(sbits - 1))); // 3. Compute Y*Q / Y*QQ*2^{D-N} bool YQ_sgn = x.sign; scoped_mpz YQ_sig(m_mpz_manager); mpf_exp_t YQ_exp = Q_exp + y.exponent; m_mpz_manager.mul(y.significand, Q_sig, YQ_sig); renormalize(ebits, 2*sbits-1, YQ_exp, YQ_sig); // YQ_sig has `sbits-1' extra bits. (void)YQ_sgn; TRACE("mpf_dbg_rem", tout << "YQ_sgn=" << YQ_sgn << std::endl; tout << "YQ_exp=" << YQ_exp << std::endl; tout << "YQ_sig=" << m_mpz_manager.to_string(YQ_sig) << std::endl; tout << "YQ=" << to_string_hexfloat(YQ_sgn, YQ_exp, YQ_sig, ebits, sbits, sbits-1) << std::endl;); // `sbits-1' extra bits in YQ_sig. SASSERT(m_mpz_manager.lt(YQ_sig, m_powers2(2*sbits-1))); SASSERT(m_mpz_manager.ge(YQ_sig, m_powers2(2*sbits-2)) || YQ_exp <= mk_bot_exp(ebits)); // 4. Compute X-Y*Q mpf_exp_t X_YQ_exp = x.exponent; scoped_mpz X_YQ_sig(m_mpz_manager); mpf_exp_t exp_delta = exp(x) - YQ_exp; TRACE("mpf_dbg_rem", tout << "exp_delta=" << exp_delta << std::endl;); SASSERT(INT_MIN < exp_delta && exp_delta <= INT_MAX); scoped_mpz minuend(m_mpz_manager), subtrahend(m_mpz_manager); scoped_mpz x_sig_lrg(m_mpz_manager); m_mpz_manager.mul2k(x.significand, sbits-1, x_sig_lrg); // sbits-1 extra bits into x m_mpz_manager.set(minuend, x_sig_lrg); m_mpz_manager.set(subtrahend, YQ_sig); SASSERT(m_mpz_manager.lt(minuend, m_powers2(2*sbits-1))); SASSERT(m_mpz_manager.ge(minuend, m_powers2(2*sbits-2))); SASSERT(m_mpz_manager.lt(subtrahend, m_powers2(2*sbits-1))); SASSERT(m_mpz_manager.ge(subtrahend, m_powers2(2*sbits-2))); if (exp_delta != 0) { scoped_mpz sticky_rem(m_mpz_manager); m_mpz_manager.set(sticky_rem, 0); if (exp_delta > sbits+5) sticky_rem.swap(subtrahend); else if (exp_delta > 0) m_mpz_manager.machine_div_rem(subtrahend, m_powers2((unsigned)exp_delta), subtrahend, sticky_rem); else { SASSERT(exp_delta < 0); exp_delta = -exp_delta; m_mpz_manager.mul2k(subtrahend, (int)exp_delta); } if (!m_mpz_manager.is_zero(sticky_rem) && m_mpz_manager.is_even(subtrahend)) m_mpz_manager.inc(subtrahend); TRACE("mpf_dbg_rem", tout << "aligned subtrahend=" << m_mpz_manager.to_string(subtrahend) << std::endl;); } m_mpz_manager.sub(minuend, subtrahend, X_YQ_sig); TRACE("mpf_dbg_rem", tout << "X_YQ_sig'=" << m_mpz_manager.to_string(X_YQ_sig) << std::endl;); bool neg = m_mpz_manager.is_neg(X_YQ_sig); if (neg) m_mpz_manager.neg(X_YQ_sig); bool X_YQ_sgn = x.sign ^ neg; // 5. Rounding if (m_mpz_manager.is_zero(X_YQ_sig)) mk_zero(ebits, sbits, x.sign, x); else { renormalize(ebits, 2*sbits-1, X_YQ_exp, X_YQ_sig); TRACE("mpf_dbg_rem", tout << "minuend=" << m_mpz_manager.to_string(minuend) << std::endl; tout << "subtrahend=" << m_mpz_manager.to_string(subtrahend) << std::endl; tout << "X_YQ_sgn=" << X_YQ_sgn << std::endl; tout << "X_YQ_exp=" << X_YQ_exp << std::endl; tout << "X_YQ_sig=" << m_mpz_manager.to_string(X_YQ_sig) << std::endl; tout << "X-YQ=" << to_string_hexfloat(X_YQ_sgn, X_YQ_exp, X_YQ_sig, ebits, sbits, sbits-1) << std::endl;); // `sbits-1' extra bits in X_YQ_sig SASSERT(m_mpz_manager.lt(X_YQ_sig, m_powers2(2*sbits-1))); scoped_mpz rnd_bits(m_mpz_manager); m_mpz_manager.machine_div_rem(X_YQ_sig, m_powers2(sbits-1), X_YQ_sig, rnd_bits); TRACE("mpf_dbg_rem", tout << "final sticky=" << m_mpz_manager.to_string(rnd_bits) << std::endl; ); // Round to nearest, ties to even. if (m_mpz_manager.eq(rnd_bits, mpz(32))) { // tie. if (m_mpz_manager.is_odd(X_YQ_sig)) { m_mpz_manager.inc(X_YQ_sig); } } else if (m_mpz_manager.gt(rnd_bits, mpz(32))) m_mpz_manager.inc(X_YQ_sig); set(x, ebits, sbits, X_YQ_sgn, X_YQ_exp, X_YQ_sig); } TRACE("mpf_dbg_rem", tout << "partial remainder = " << to_string_hexfloat(x) << std::endl;); } void mpf_manager::rem(mpf const & x, mpf const & y, mpf & o) { SASSERT(x.sbits == y.sbits && x.ebits == y.ebits); TRACE("mpf_dbg_rem", tout << "X = " << to_string(x) << "=" << to_string_hexfloat(x) << std::endl; tout << "Y = " << to_string(y) << "=" << to_string_hexfloat(y) << std::endl;); if (is_nan(x) || is_nan(y)) mk_nan(x.ebits, x.sbits, o); else if (is_inf(x)) mk_nan(x.ebits, x.sbits, o); else if (is_inf(y)) set(o, x); else if (is_zero(y)) mk_nan(x.ebits, x.sbits, o); else if (is_zero(x)) set(o, x); else { SASSERT(is_regular(x) && is_regular(y)); // This is a generalized version of the algorithm for FPREM1 in the `Intel // 64 and IA-32 Architectures Software Developer's Manual', // Section 3-402 Vol. 2A `FPREM1-Partial Remainder'. scoped_mpf ST0(*this), ST1(*this); set(ST0, x); set(ST1, y); unpack(ST0, true); unpack(ST1, true); const mpf_exp_t B = x.sbits; mpf_exp_t D; do { if (ST0.exponent() < ST1.exponent() - 1) { D = 0; } else { D = ST0.exponent() - ST1.exponent(); partial_remainder(ST0.get(), ST1.get(), D, (D >= B)); } } while (D >= B && !ST0.is_zero()); m_mpz_manager.mul2k(ST0.significand(), 3); set(o, x.ebits, x.sbits, MPF_ROUND_TOWARD_ZERO, ST0); round(MPF_ROUND_NEAREST_TEVEN, o); } TRACE("mpf_dbg_rem", tout << "R = " << to_string(o) << "=" << to_string_hexfloat(o) << std::endl; ); TRACE("mpf_dbg", tout << "REMAINDER = " << to_string(o) << std::endl;); } void mpf_manager::maximum(mpf const & x, mpf const & y, mpf & o) { if (is_nan(x)) set(o, y); else if (is_nan(y)) set(o, x); else if (is_zero(x) && is_zero(y) && sgn(x) != sgn(y)) { UNREACHABLE(); // max(+0, -0) and max(-0, +0) are unspecified. } else if (is_zero(x) && is_zero(y)) set(o, y); else if (gt(x, y)) set(o, x); else set(o, y); } void mpf_manager::minimum(mpf const & x, mpf const & y, mpf & o) { if (is_nan(x)) set(o, y); else if (is_nan(y)) set(o, x); else if (is_zero(x) && is_zero(y) && sgn(x) != sgn(y)) { UNREACHABLE(); // min(+0, -0) and min(-0, +0) are unspecified. } else if (is_zero(x) && is_zero(y)) set(o, y); else if (lt(x, y)) set(o, x); else set(o, y); } std::string mpf_manager::to_string(mpf const & x) { std::string res; if (is_nan(x)) res = "NaN"; else { if (is_inf(x)) res = sgn(x) ? "-oo" : "+oo"; else if (is_zero(x)) res = sgn(x) ? "-zero" : "+zero"; else { res = sgn(x) ? "-" : ""; scoped_mpz num(m_mpq_manager), denom(m_mpq_manager); num = 0; denom = 1; mpf_exp_t exponent; if (is_denormal(x)) exponent = mk_min_exp(x.ebits); else { m_mpz_manager.set(num, 1); m_mpz_manager.mul2k(num, x.sbits-1, num); exponent = exp(x); } m_mpz_manager.add(num, sig(x), num); m_mpz_manager.mul2k(denom, x.sbits-1, denom); //TRACE("mpf_dbg", tout << "SIG=" << m_mpq_manager.to_string(sig(x)) << std::endl; ); //TRACE("mpf_dbg", tout << "NUM=" << m_mpq_manager.to_string(num) << std::endl;); //TRACE("mpf_dbg", tout << "DEN=" << m_mpq_manager.to_string(denom) << std::endl;); //TRACE("mpf_dbg", tout << "EXP=" << exponent << std::endl;); scoped_mpq r(m_mpq_manager); m_mpq_manager.set(r, num); m_mpq_manager.div(r, denom, r); std::stringstream ss; m_mpq_manager.display_decimal(ss, r, x.sbits); if (m_mpq_manager.is_int(r)) ss << ".0"; ss << " " << exponent; res += ss.str(); } } //DEBUG_CODE( // res += " " + to_string_raw(x); //); return res; } std::string mpf_manager::to_rational_string(mpf const & x) { scoped_mpq q(m_mpq_manager); to_rational(x, q); return m_mpq_manager.to_string(q); } void mpf_manager::display_decimal(std::ostream & out, mpf const & a, unsigned k) { scoped_mpq q(m_mpq_manager); to_rational(a, q); m_mpq_manager.display_decimal(out, q, k); } void mpf_manager::display_smt2(std::ostream & out, mpf const & a, bool decimal) { scoped_mpq q(m_mpq_manager); to_rational(a, q); m_mpq_manager.display_smt2(out, q, decimal); } std::string mpf_manager::to_string_raw(mpf const & x) { std::string res; res += "["; res += (x.sign?"-":"+"); res += " "; res += m_mpz_manager.to_string(sig(x)); res += " "; std::stringstream ss(""); ss << exp(x); res += ss.str(); if (is_normal(x)) res += " N"; else res += " D"; res += "]"; return res; } std::string mpf_manager::to_string_hexfloat(bool sgn, mpf_exp_t exp, scoped_mpz const & sig, unsigned ebits, unsigned sbits, unsigned rbits) { scoped_mpf q(*this); scoped_mpz q_sig(m_mpz_manager); m_mpz_manager.set(q_sig, sig); if (rbits != 0) m_mpz_manager.div(q_sig, m_powers2(rbits), q_sig); // restore scale if (m_mpz_manager.ge(q_sig, m_powers2(sbits-1))) m_mpz_manager.sub(q_sig, m_powers2(sbits-1), q_sig); // strip hidden bit else if (exp == mk_min_exp(ebits)) exp = mk_bot_exp(ebits); set(q, ebits, sbits, sgn, exp, q_sig); return to_string(q.get()); } std::string mpf_manager::to_string_hexfloat(mpf const & x) { std::stringstream ss(""); std::ios::fmtflags ff = ss.setf(std::ios_base::hex | std::ios_base::uppercase | std::ios_base::showpoint | std::ios_base::showpos); ss.setf(ff); ss.precision(13); #if defined(_WIN32) && _MSC_VER >= 1800 ss << std::hexfloat << to_double(x); #else ss << std::hex << (*reinterpret_cast(&(x))); #endif return ss.str(); } std::string mpf_manager::to_string_binary(mpf const & x, unsigned upper_extra, unsigned lower_extra) { std::string res; if (is_nan(x)) res = std::string("") + "#b0 " + "#b" + std::string(x.ebits, '1') + " " + "#b" + std::string(x.sbits-2, '0') + "1 " + "(NaN)"; else if (is_inf(x)) res = std::string("") + "#b" + (sgn(x)?"1":"0") + " " + "#b" + std::string(x.ebits, '1') + " " + "#b" + std::string(x.sbits - 1, '0') + "1 " + "(" + (sgn(x)?"-":"+") + "oo)"; else if (is_zero(x)) res = std::string("") + "#b" + (sgn(x) ? "1" : "0") + " " + "#b" + std::string(x.ebits, '0') + " " + "#b" + std::string(x.sbits - 1, '0') + " " + "(" + (sgn(x) ? "-" : "+") + "zero)"; else { SASSERT(m_mpz_manager.is_nonneg(sig(x))); res = std::string("") + "#b" + (sgn(x) ? "1" : "0") + " "; scoped_mpz tmp(m_mpz_manager); if (is_denormal(x)) m_mpz_manager.set(tmp, bias_exp(x.ebits, mk_min_exp(x.ebits))); else { m_mpz_manager.set(tmp, bias_exp(x.ebits, exp(x))); } std::string tmp_str = ""; for (unsigned i = 0; i < x.ebits; i++) { tmp_str += m_mpz_manager.is_odd(tmp) ? "1" : "0"; tmp /= 2; } std::reverse(tmp_str.begin(), tmp_str.end()); res += "#b" + tmp_str + " "; tmp_str = ""; m_mpz_manager.set(tmp, sig(x)); unsigned num_bits = upper_extra + x.sbits + lower_extra; for (unsigned i = 0; i < num_bits || !tmp.is_zero(); i++) { tmp_str += m_mpz_manager.is_odd(tmp) ? "1" : "0"; tmp /= 2; if (i == lower_extra - 1) tmp_str += ","; if (i == x.sbits + lower_extra - 2) { tmp_str += "."; if (i == num_bits - 1) tmp_str += " "; } } std::reverse(tmp_str.begin(), tmp_str.end()); res += "#b" + tmp_str; } return res; } void mpf_manager::to_rational(mpf const & x, unsynch_mpq_manager & qm, mpq & o) { scoped_mpf a(*this); scoped_mpz n(m_mpq_manager), d(m_mpq_manager); set(a, x); unpack(a, true); m_mpz_manager.set(n, a.significand()); if (a.sign()) m_mpz_manager.neg(n); m_mpz_manager.power(2, a.sbits() - 1, d); if (a.exponent() >= 0) m_mpz_manager.mul2k(n, (unsigned)a.exponent()); else m_mpz_manager.mul2k(d, (unsigned)-a.exponent()); qm.set(o, n, d); } double mpf_manager::to_double(mpf const & x) { SASSERT(x.ebits <= 11 && x.sbits <= 53); uint64_t raw = 0; int64_t sig = 0, exp = 0; sig = m_mpz_manager.get_uint64(x.significand); sig <<= 53 - x.sbits; if (has_top_exp(x)) exp = 1024; else if (has_bot_exp(x)) exp = -1023; else exp = x.exponent; exp += 1023; raw = (exp << 52) | sig; if (x.sign) raw = raw | 0x8000000000000000ull; double ret; memcpy(&ret, &raw, sizeof(double)); return ret; } float mpf_manager::to_float(mpf const & x) { SASSERT(x.ebits <= 8 && x.sbits <= 24); unsigned int raw = 0; unsigned int sig = 0, exp = 0; uint64_t q = m_mpz_manager.get_uint64(x.significand); SASSERT(q < 4294967296ull); sig = q & 0x00000000FFFFFFFF; sig <<= 24 - x.sbits; if (has_top_exp(x)) exp = +128; else if (has_bot_exp(x)) exp = -127; else { int64_t q = x.exponent; SASSERT(q < 4294967296ll); exp = q & 0x00000000FFFFFFFF; } exp += 127; raw = (exp << 23) | sig; if (x.sign) raw = raw | 0x80000000; float ret; memcpy(&ret, &raw, sizeof(float)); return ret; } bool mpf_manager::is_nan(mpf const & x) { return has_top_exp(x) && !m_mpz_manager.is_zero(sig(x)); } bool mpf_manager::is_inf(mpf const & x) { return has_top_exp(x) && m_mpz_manager.is_zero(sig(x)); } bool mpf_manager::is_pinf(mpf const & x) { return !x.sign && is_inf(x); } bool mpf_manager::is_ninf(mpf const & x) { return x.sign && is_inf(x); } bool mpf_manager::is_normal(mpf const & x) { return !(has_top_exp(x) || is_denormal(x) || is_zero(x)); } bool mpf_manager::is_denormal(mpf const & x) { return !is_zero(x) && has_bot_exp(x); } bool mpf_manager::is_int(mpf const & x) { if (!is_normal(x)) return false; if (exp(x) >= x.sbits-1) return true; else if (exp(x) < 0) return false; else { SASSERT(x.exponent >= 0 && x.exponent < x.sbits-1); scoped_mpz t(m_mpz_manager); m_mpz_manager.set(t, sig(x)); unsigned shift = x.sbits - ((unsigned)exp(x)) - 1; do { if (m_mpz_manager.is_odd(t)) return false; m_mpz_manager.machine_div2k(t, 1); } while (--shift != 0); return true; } } bool mpf_manager::has_bot_exp(mpf const & x) { return exp(x) == mk_bot_exp(x.ebits); } bool mpf_manager::has_top_exp(mpf const & x) { return exp(x) == mk_top_exp(x.ebits); } mpf_exp_t mpf_manager::mk_bot_exp(unsigned ebits) { SASSERT(ebits >= 2); return m_mpz_manager.get_int64(m_powers2.m1(ebits-1, true)); } mpf_exp_t mpf_manager::mk_top_exp(unsigned ebits) { SASSERT(ebits >= 2); return m_mpz_manager.get_int64(m_powers2(ebits-1)); } mpf_exp_t mpf_manager::mk_min_exp(unsigned ebits) { SASSERT(ebits > 0); mpf_exp_t r = m_mpz_manager.get_int64(m_powers2.m1(ebits-1, true)); return r+1; } mpf_exp_t mpf_manager::mk_max_exp(unsigned ebits) { SASSERT(ebits > 0); return m_mpz_manager.get_int64(m_powers2.m1(ebits-1, false)); } mpf_exp_t mpf_manager::bias_exp(unsigned ebits, mpf_exp_t unbiased_exponent) { return unbiased_exponent + m_mpz_manager.get_int64(m_powers2.m1(ebits - 1, false)); } mpf_exp_t mpf_manager::unbias_exp(unsigned ebits, mpf_exp_t biased_exponent) { return biased_exponent - m_mpz_manager.get_int64(m_powers2.m1(ebits - 1, false)); } void mpf_manager::mk_nzero(unsigned ebits, unsigned sbits, mpf & o) { o.sbits = sbits; o.ebits = ebits; o.exponent = mk_bot_exp(ebits); m_mpz_manager.set(o.significand, 0); o.sign = true; } void mpf_manager::mk_pzero(unsigned ebits, unsigned sbits, mpf & o) { o.sbits = sbits; o.ebits = ebits; o.exponent = mk_bot_exp(ebits); m_mpz_manager.set(o.significand, 0); o.sign = false; } void mpf_manager::mk_zero(unsigned ebits, unsigned sbits, bool sign, mpf & o) { if (sign) mk_nzero(ebits, sbits, o); else mk_pzero(ebits, sbits, o); } void mpf_manager::mk_nan(unsigned ebits, unsigned sbits, mpf & o) { o.sbits = sbits; o.ebits = ebits; o.exponent = mk_top_exp(ebits); // This is a quiet NaN, i.e., the first bit should be 1. m_mpz_manager.set(o.significand, m_powers2(sbits-1)); m_mpz_manager.dec(o.significand); o.sign = false; } void mpf_manager::mk_one(unsigned ebits, unsigned sbits, bool sign, mpf & o) const { o.sbits = sbits; o.ebits = ebits; o.sign = sign; m_mpz_manager.set(o.significand, 0); o.exponent = 0; } void mpf_manager::mk_max_value(unsigned ebits, unsigned sbits, bool sign, mpf & o) { o.sbits = sbits; o.ebits = ebits; o.sign = sign; o.exponent = mk_top_exp(ebits) - 1; m_mpz_manager.set(o.significand, m_powers2.m1(sbits-1, false)); } void mpf_manager::mk_inf(unsigned ebits, unsigned sbits, bool sign, mpf & o) { o.sbits = sbits; o.ebits = ebits; o.sign = sign; o.exponent = mk_top_exp(ebits); m_mpz_manager.set(o.significand, 0); } void mpf_manager::mk_pinf(unsigned ebits, unsigned sbits, mpf & o) { mk_inf(ebits, sbits, false, o); } void mpf_manager::mk_ninf(unsigned ebits, unsigned sbits, mpf & o) { mk_inf(ebits, sbits, true, o); } void mpf_manager::unpack(mpf & o, bool normalize) { TRACE("mpf_dbg", tout << "unpack " << to_string(o) << ": ebits=" << o.ebits << " sbits=" << o.sbits << " normalize=" << normalize << " has_top_exp=" << has_top_exp(o) << " (" << mk_top_exp(o.ebits) << ")" << " has_bot_exp=" << has_bot_exp(o) << " (" << mk_bot_exp(o.ebits) << ")" << " is_zero=" << is_zero(o) << std::endl;); // Insert the hidden bit or adjust the exponent of denormal numbers. if (is_zero(o)) return; if (is_normal(o)) m_mpz_manager.add(o.significand, m_powers2(o.sbits-1), o.significand); else { o.exponent = mk_bot_exp(o.ebits) + 1; if (normalize && !m_mpz_manager.is_zero(o.significand)) { const mpz & p = m_powers2(o.sbits-1); while (m_mpz_manager.gt(p, o.significand)) { o.exponent--; m_mpz_manager.mul2k(o.significand, 1, o.significand); } } } } void mpf_manager::mk_round_inf(mpf_rounding_mode rm, mpf & o) { if (!o.sign) { if (rm == MPF_ROUND_TOWARD_ZERO || rm == MPF_ROUND_TOWARD_NEGATIVE) mk_max_value(o.ebits, o.sbits, o.sign, o); else mk_inf(o.ebits, o.sbits, o.sign, o); } else { if (rm == MPF_ROUND_TOWARD_ZERO || rm == MPF_ROUND_TOWARD_POSITIVE) mk_max_value(o.ebits, o.sbits, o.sign, o); else mk_inf(o.ebits, o.sbits, o.sign, o); } } void mpf_manager::round(mpf_rounding_mode rm, mpf & o) { // Assumptions: o.significand is of the form f[-1:0] . f[1:sbits-1] [round,extra,sticky], // i.e., it has 2 + (sbits-1) + 3 = sbits + 4 bits. TRACE("mpf_dbg", tout << "RND: " << to_string(o) << std::endl; tout << to_string_binary(o, 1, 3) << std::endl;); DEBUG_CODE({ const mpz & p_m3 = m_powers2(o.sbits+4); SASSERT(m_mpz_manager.lt(o.significand, p_m3)); }); mpf_exp_t e_max = mk_max_exp(o.ebits); mpf_exp_t e_min = mk_min_exp(o.ebits); unsigned sig_width = m_mpz_manager.prev_power_of_two(o.significand) + 1; mpf_exp_t lz = o.sbits + 4 - sig_width; mpf_exp_t beta = o.exponent - lz + 1; scoped_mpz sigma(m_mpz_manager); if (beta < e_min) { // denormal significand/TINY m_mpz_manager.set(sigma, o.exponent - e_min); o.exponent = e_min; } else { m_mpz_manager.set(sigma, lz - 1); o.exponent = beta; } scoped_mpz sigma_cap(m_mpz_manager); sigma_cap = o.sbits + 2; m_mpz_manager.neg(sigma_cap); if (m_mpz_manager.lt(sigma, sigma_cap)) m_mpz_manager.set(sigma, sigma_cap); TRACE("mpf_dbg", tout << "e_min_norm = " << e_min << std::endl; tout << "e_max_norm = " << e_max << std::endl; tout << "beta = " << beta << ", (beta < e_min) = " << (beta < e_min) << std::endl; tout << "LZ = " << lz << std::endl; tout << "sigma = " << m_mpz_manager.to_string(sigma) << std::endl; tout << "sigma_cap = " << m_mpz_manager.to_string(sigma_cap) << std::endl;); // Normalization shift TRACE("mpf_dbg", tout << "Shift distance: " << m_mpz_manager.to_string(sigma) << " " << ((m_mpz_manager.is_nonneg(sigma))?"(LEFT)":"(RIGHT)") << std::endl;); if (m_mpz_manager.le(sigma, -1)) { // Right shift scoped_mpz sticky_rem(m_mpz_manager); unsigned nsigma_uint = (unsigned)-m_mpz_manager.get_int64(sigma); // sigma is capped, this is safe. m_mpz_manager.machine_div_rem(o.significand, m_powers2(nsigma_uint), o.significand, sticky_rem); bool sticky = !m_mpz_manager.is_zero(sticky_rem); if (sticky && m_mpz_manager.is_even(o.significand)) m_mpz_manager.inc(o.significand); } else { // Left shift unsigned sigma_uint = static_cast(m_mpz_manager.get_int64(sigma)); m_mpz_manager.mul2k(o.significand, sigma_uint, o.significand); } TRACE("mpf_dbg", tout << "Shifted: " << to_string(o) << std::endl; tout << to_string_binary(o, 1, 3) << std::endl;); // Significand rounding (sigrd) bool sticky = !m_mpz_manager.is_even(o.significand); m_mpz_manager.machine_div2k(o.significand, 1); sticky = sticky || !m_mpz_manager.is_even(o.significand); m_mpz_manager.machine_div2k(o.significand, 1); bool round = !m_mpz_manager.is_even(o.significand); m_mpz_manager.machine_div2k(o.significand, 1); bool last = !m_mpz_manager.is_even(o.significand); TRACE("mpf_dbg", tout << "sign=" << o.sign << " last=" << last << " round=" << round << " sticky=" << sticky << std::endl;); TRACE("mpf_dbg", tout << "before rounding decision: " << to_string(o) << std::endl;); // The significand has the right size now, but we might have to increment it // depending on the sign, the last/round/sticky bits, and the rounding mode. bool inc = false; switch (rm) { case MPF_ROUND_NEAREST_TEVEN: inc = round && (last || sticky); break; case MPF_ROUND_NEAREST_TAWAY: inc = round; break; case MPF_ROUND_TOWARD_POSITIVE: inc = (!o.sign && (round || sticky)); break; case MPF_ROUND_TOWARD_NEGATIVE: inc = (o.sign && (round || sticky)); break; case MPF_ROUND_TOWARD_ZERO: inc = false; break; default: UNREACHABLE(); } TRACE("mpf_dbg", tout << "Rounding increment -> significand +" << (int)inc << std::endl;); if (inc) m_mpz_manager.inc(o.significand); TRACE("mpf_dbg", tout << "Rounded significand: " << to_string(o) << std::endl;); // Post normalization (post) const mpz & p_sig = m_powers2(o.sbits); if (m_mpz_manager.ge(o.significand, p_sig)) { m_mpz_manager.machine_div2k(o.significand, 1); o.exponent++; } bool SIGovf = o.exponent > e_max; TRACE("mpf_dbg", tout << "Post-normalized: " << to_string(o) << std::endl;); TRACE("mpf_dbg", tout << "SIGovf = " << SIGovf << std::endl;); // Exponent rounding (exprd) bool o_has_max_exp = (o.exponent > e_max); bool OVF2 = SIGovf && o_has_max_exp; TRACE("mpf_dbg", tout << "OVF2 = " << OVF2 << std::endl;); TRACE("mpf_dbg", tout << "o_has_max_exp = " << o_has_max_exp << std::endl;); if (OVF2) mk_round_inf(rm, o); else { const mpz & p = m_powers2(o.sbits-1); if (m_mpz_manager.ge(o.significand, p)) { TRACE("mpf_dbg", tout << "NORMAL: " << m_mpz_manager.to_string(o.significand) << std::endl;); m_mpz_manager.sub(o.significand, p, o.significand); // Strips the hidden bit. } else { TRACE("mpf_dbg", tout << "DENORMAL: " << m_mpz_manager.to_string(o.significand) << std::endl;); o.exponent = mk_bot_exp(o.ebits); } } TRACE("mpf_dbg", tout << "ROUNDED = " << to_string(o) << std::endl; tout << to_string_binary(o, -1, 0) << " (hidden bit, unbiased exp)." << std::endl;); } void mpf_manager::round_sqrt(mpf_rounding_mode rm, mpf & o) { TRACE("mpf_dbg", tout << "RND-SQRT: " << to_string(o) << std::endl;); bool sticky = !m_mpz_manager.is_even(o.significand); m_mpz_manager.machine_div2k(o.significand, 1); sticky = sticky || !m_mpz_manager.is_even(o.significand); m_mpz_manager.machine_div2k(o.significand, 1); bool round = !m_mpz_manager.is_even(o.significand); m_mpz_manager.machine_div2k(o.significand, 1); bool last = !m_mpz_manager.is_even(o.significand); (void)last; bool inc = false; // Specialized rounding for sqrt, as there are no negative cases (or half-way cases?) switch(rm) { case MPF_ROUND_NEAREST_TEVEN: case MPF_ROUND_NEAREST_TAWAY: inc = (round && sticky); break; case MPF_ROUND_TOWARD_NEGATIVE: break; case MPF_ROUND_TOWARD_ZERO: break; case MPF_ROUND_TOWARD_POSITIVE: inc = round || sticky; break; default: UNREACHABLE(); } TRACE("mpf_dbg", tout << "last=" << last << " round=" << round << " sticky=" << sticky << " --> inc=" << inc << std::endl;); if (inc) m_mpz_manager.inc(o.significand); m_mpz_manager.sub(o.significand, m_powers2(o.sbits-1), o.significand); TRACE("mpf_dbg", tout << "ROUNDED = " << to_string(o) << std::endl;); } unsigned mpf_manager::prev_power_of_two(mpf const & a) { SASSERT(!is_nan(a) && !is_pinf(a) && !is_ninf(a)); if (!is_pos(a)) return 0; if (a.exponent <= -static_cast(a.sbits)) return 0; // Number is smaller than 1 return static_cast(a.sbits + a.exponent - 1); } z3-z3-4.8.7/src/util/mpf.h000066400000000000000000000263051356505360400151340ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: mpf.h Abstract: Multi Precision Floating Point Numbers Author: Christoph Wintersteiger (cwinter) 2011-12-01. Revision History: --*/ #ifndef MPF_H_ #define MPF_H_ #include #include "util/mpz.h" #include "util/mpq.h" #include "util/map.h" #include "util/scoped_numeral.h" #include "util/scoped_numeral_vector.h" #include "util/hash.h" typedef enum { MPF_ROUND_NEAREST_TEVEN, MPF_ROUND_NEAREST_TAWAY, MPF_ROUND_TOWARD_POSITIVE, MPF_ROUND_TOWARD_NEGATIVE, MPF_ROUND_TOWARD_ZERO } mpf_rounding_mode; typedef int64_t mpf_exp_t; class mpf { friend class mpf_manager; friend class scoped_mpf; unsigned ebits:15; unsigned sbits:16; unsigned sign:1; // counts as one sbit. mpz significand; mpf_exp_t exponent; mpf & operator=(mpf const & other) { UNREACHABLE(); return *this; } void set(unsigned _ebits, unsigned _sbits); public: mpf(); mpf(unsigned ebits, unsigned sbits); mpf(mpf && other) : ebits(other.ebits), sbits(other.sbits), sign(other.sign), significand(std::move(other.significand)), exponent(other.exponent) {} ~mpf(); unsigned get_ebits() const { return ebits; } unsigned get_sbits() const { return sbits; } void swap(mpf & other); }; class mpf_manager { unsynch_mpq_manager m_mpq_manager; unsynch_mpz_manager & m_mpz_manager; // A mpq_manager is a mpz_manager, reusing it. public: typedef mpf numeral; mpf_manager(); ~mpf_manager(); void reset(mpf & o, unsigned ebits, unsigned sbits) { set(o, ebits, sbits, 0); } void set(mpf & o, unsigned ebits, unsigned sbits, int value); void set(mpf & o, unsigned ebits, unsigned sbits, mpf_rounding_mode rm, int n, int d); void set(mpf & o, unsigned ebits, unsigned sbits, float value); void set(mpf & o, unsigned ebits, unsigned sbits, double value); void set(mpf & o, unsigned ebits, unsigned sbits, mpf_rounding_mode rm, mpq const & value); void set(mpf & o, unsigned ebits, unsigned sbits, mpf_rounding_mode rm, char const * value); void set(mpf & o, unsigned ebits, unsigned sbits, mpf_rounding_mode rm, mpz const & exponent, mpq const & significand); void set(mpf & o, unsigned ebits, unsigned sbits, bool sign, mpf_exp_t exponent, uint64_t significand); void set(mpf & o, unsigned ebits, unsigned sbits, bool sign, mpf_exp_t exponent, mpz const & significand); void set(mpf & o, mpf const & x); void set(mpf & o, unsigned ebits, unsigned sbits, mpf_rounding_mode rm, mpf const & x); void del(mpf & x) { m_mpz_manager.del(x.significand); } void abs(mpf & o); void abs(mpf const & x, mpf & o); void neg(mpf & o); void neg(mpf const & x, mpf & o); bool is_zero(mpf const & x); bool is_neg(mpf const & x); bool is_pos(mpf const & x); bool is_one(mpf const & x); bool is_nzero(mpf const & x); bool is_pzero(mpf const & x); // structural eq bool eq_core(mpf const & x, mpf const & y) { return x.ebits == y.ebits && x.sbits == y.sbits && x.sign == y.sign && m_mpz_manager.eq(x.significand, y.significand) && x.exponent == y.exponent; } bool eq(mpf const & x, mpf const & y); bool lt(mpf const & x, mpf const & y); bool lte(mpf const & x, mpf const & y); bool le(mpf const & x, mpf const & y) { return lte(x, y); } bool gt(mpf const & x, mpf const & y); bool gte(mpf const & x, mpf const & y); bool ge(mpf const & x, mpf const & y) { return gte(x, y); } void add(mpf_rounding_mode rm, mpf const & x, mpf const & y, mpf & o); void sub(mpf_rounding_mode rm, mpf const & x, mpf const & y, mpf & o); void mul(mpf_rounding_mode rm, mpf const & x, mpf const & y, mpf & o); void div(mpf_rounding_mode rm, mpf const & x, mpf const & y, mpf & o); void fma(mpf_rounding_mode rm, mpf const & x, mpf const & y, mpf const &z, mpf & o); void sqrt(mpf_rounding_mode rm, mpf const & x, mpf & o); void round_to_integral(mpf_rounding_mode rm, mpf const & x, mpf & o); void rem(mpf const & x, mpf const & y, mpf & o); void maximum(mpf const & x, mpf const & y, mpf & o); void minimum(mpf const & x, mpf const & y, mpf & o); std::string to_string(mpf const & a); std::string to_rational_string(mpf const & a); void display_decimal(std::ostream & out, mpf const & a, unsigned k); void display_smt2(std::ostream & out, mpf const & a, bool decimal); // Convert x into a mpq numeral. zm is the manager that owns o. void to_rational(mpf const & x, unsynch_mpq_manager & qm, mpq & o); void to_rational(mpf const & x, scoped_mpq & o) { to_rational(x, o.m(), o); } double to_double(mpf const & x); float to_float(mpf const & x); bool sgn(mpf const & x) const { return x.sign; } const mpz & sig(mpf const & x) const { return x.significand; } void sig_normalized(mpf const & x, mpz & res) { mpf t; set(t, x); unpack(t, true); mpz_manager().set(res, t.significand); del(t); } const mpf_exp_t & exp(mpf const & x) const { return x.exponent; } mpf_exp_t exp_normalized(mpf const & x) { if (is_zero(x)) return 0; else { mpf t; set(t, x); unpack(t, true); mpf_exp_t r = t.exponent; del(t); return r; } } bool is_nan(mpf const & x); bool is_inf(mpf const & x); bool is_pinf(mpf const & x); bool is_ninf(mpf const & x); bool is_normal(mpf const & x); bool is_denormal(mpf const & x); bool is_regular(mpf const & x) { return x.sbits == 0 || is_normal(x) || is_denormal(x); } bool is_int(mpf const & x); void mk_zero(unsigned ebits, unsigned sbits, bool sign, mpf & o); void mk_nzero(unsigned ebits, unsigned sbits, mpf & o); void mk_pzero(unsigned ebits, unsigned sbits, mpf & o); void mk_nan(unsigned ebits, unsigned sbits, mpf & o); void mk_inf(unsigned ebits, unsigned sbits, bool sign, mpf & o); void mk_pinf(unsigned ebits, unsigned sbits, mpf & o); void mk_ninf(unsigned ebits, unsigned sbits, mpf & o); unsynch_mpz_manager & mpz_manager() { return m_mpz_manager; } unsynch_mpq_manager & mpq_manager() { return m_mpq_manager; } unsigned hash(mpf const & a) { return hash_u_u(m_mpz_manager.hash(a.significand), m_mpz_manager.hash(hash_ull(a.exponent))); } void mk_max_value(unsigned ebits, unsigned sbits, bool sign, mpf & o); mpf_exp_t mk_bot_exp(unsigned ebits); mpf_exp_t mk_top_exp(unsigned ebits); mpf_exp_t mk_max_exp(unsigned ebits); mpf_exp_t mk_min_exp(unsigned ebits); mpf_exp_t bias_exp(unsigned ebits, mpf_exp_t unbiased_exponent); mpf_exp_t unbias_exp(unsigned ebits, mpf_exp_t biased_exponent); /** \brief Return the biggest k s.t. 2^k <= a. \remark Return 0 if a is not positive. */ unsigned prev_power_of_two(mpf const & a); void to_sbv_mpq(mpf_rounding_mode rm, const mpf & x, scoped_mpq & o); void to_ieee_bv_mpz(const mpf & x, scoped_mpz & o); protected: void mk_one(unsigned ebits, unsigned sbits, bool sign, mpf & o) const; bool has_bot_exp(mpf const & x); bool has_top_exp(mpf const & x); void unpack(mpf & o, bool normalize); void add_sub(mpf_rounding_mode rm, mpf const & x, mpf const & y, mpf & o, bool sub); void round(mpf_rounding_mode rm, mpf & o); void round_sqrt(mpf_rounding_mode rm, mpf & o); void renormalize(unsigned ebits, unsigned sbits, mpf_exp_t & exp, mpz & sig); void partial_remainder(mpf & x, mpf const & y, mpf_exp_t const & exp_diff, bool partial); void mk_round_inf(mpf_rounding_mode rm, mpf & o); // Convert x into a mpz numeral. zm is the manager that owns o. void to_mpz(mpf const & x, unsynch_mpz_manager & zm, mpz & o); void to_mpz(mpf const & x, scoped_mpz & o) { to_mpz(x, o.m(), o); } class powers2 { unsynch_mpz_manager & m; u_map m_p; u_map m_pn; u_map m_pm1; u_map m_pm1n; public: powers2(unsynch_mpz_manager & m) : m(m) {} ~powers2() { dispose(m_p); dispose(m_pn); dispose(m_pm1); dispose(m_pm1n); } void dispose(u_map & map) { for (u_map::iterator it = map.begin(); it != map.end(); it++) { m.del(*it->m_value); dealloc(it->m_value); } } const mpz & operator()(unsigned n, bool negated = false) { u_map & map = (negated) ? m_pn : m_p; u_map::iterator it = map.find_iterator(n); if (it != map.end()) return *it->m_value; else { mpz * new_obj = alloc(mpz); map.insert(n, new_obj); m.power(unsynch_mpz_manager::mk_z(2), n, *new_obj); if (negated) m.neg(*new_obj); return *new_obj; } } const mpz & m1(unsigned n, bool negated=false) { // (2 ^ n) - 1 u_map & map = (negated) ? m_pm1n : m_pm1; u_map::iterator it = map.find_iterator(n); if (it != map.end()) return *it->m_value; else { mpz * new_obj = alloc(mpz); map.insert(n, new_obj); m.power(unsynch_mpz_manager::mk_z(2), n, *new_obj); m.dec(*new_obj); if (negated) m.neg(*new_obj); return *new_obj; } } }; std::string to_string_raw(mpf const & a); std::string to_string_hexfloat(mpf const & a); std::string to_string_hexfloat(bool sgn, mpf_exp_t exp, scoped_mpz const & sig, unsigned ebits, unsigned sbits, unsigned rbits); std::string to_string_binary(mpf const & x, unsigned upper_extra, unsigned lower_extra); public: powers2 m_powers2; }; class scoped_mpf : public _scoped_numeral { friend class mpf_manager; mpz & significand() { return get().significand; } const mpz & significand() const { return get().significand; } bool sign() const { return get().sign; } mpf_exp_t exponent() const { return get().exponent; } unsigned sbits() const { return get().sbits; } void set(unsigned ebits, unsigned sbits) { get().set(ebits, sbits); } void set(unsigned ebits, unsigned sbits, bool sign, mpf_exp_t exp, mpz & significand) { get().set(ebits, sbits); get().exponent = exp; get().sign = sign; if (&get().significand != &significand) m().mpz_manager().set(get().significand, significand); } void set(unsigned ebits, unsigned sbits, bool sign, mpf_exp_t exp) { get().set(ebits, sbits); get().exponent = exp; get().sign = sign; m().mpz_manager().set(get().significand, 0); } public: scoped_mpf(mpf_manager & m):_scoped_numeral(m) {} scoped_mpf(scoped_mpf const & n):_scoped_numeral(n) {} scoped_mpf(mpf_manager & m, unsigned ebits, unsigned sbits):_scoped_numeral(m) { set(ebits, sbits); } }; typedef _scoped_numeral_vector scoped_mpf_vector; #endif z3-z3-4.8.7/src/util/mpff.cpp000066400000000000000000001320751356505360400156370ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: mpff.cpp Abstract: Multi precision fast floating point numbers. The implementation is not compliant with the IEEE standard. For a compliant implementation, see mpf.h Author: Leonardo de Moura (leonardo) 2012-09-12 Revision History: --*/ #include #include #include "util/mpff.h" #include "util/mpn.h" #include "util/mpz.h" #include "util/mpq.h" #include "util/bit_util.h" #include "util/trace.h" static_assert(sizeof(mpn_digit) == sizeof(unsigned), ""); static_assert(sizeof(unsigned) == 4, "unsigned haven't changed size for a while"); // MIN_MSW is an shorthand for 0x8000..00, i.e., the minimal most significand word. #define MIN_MSW (1u << (sizeof(unsigned) * 8 - 1)) mpff_manager::mpff_manager(unsigned prec, unsigned initial_capacity) { SASSERT(initial_capacity > 0); m_precision = prec; m_precision_bits = prec * 8 * sizeof(unsigned); m_capacity = initial_capacity; m_to_plus_inf = false; m_significands.resize(initial_capacity * prec, 0); for (unsigned i = 0; i < MPFF_NUM_BUFFERS; i++) m_buffers[i].resize(2 * prec, 0); // Reserve space for zero VERIFY(m_id_gen.mk() == 0); set(m_one, 1); } mpff_manager::~mpff_manager() { del(m_one); } void mpff_manager::expand() { m_capacity = 2*m_capacity; m_significands.resize(m_capacity * m_precision, 0); } void mpff_manager::allocate(mpff & n) { SASSERT(n.m_sig_idx == 0); unsigned sig_idx = m_id_gen.mk(); ensure_capacity(sig_idx); n.m_sig_idx = sig_idx; DEBUG_CODE({ unsigned * s = sig(n); for (unsigned i = 0; i < m_precision; i++) { SASSERT(s[i] == 0); } }); } void mpff_manager::to_buffer(unsigned idx, mpff const & n) const { SASSERT(idx < MPFF_NUM_BUFFERS); svector & b = const_cast(this)->m_buffers[idx]; unsigned * s = sig(n); for (unsigned i = 0; i < m_precision; i++) b[i] = s[i]; } void mpff_manager::to_buffer_ext(unsigned idx, mpff const & n) const { SASSERT(idx < MPFF_NUM_BUFFERS); svector & b = const_cast(this)->m_buffers[idx]; unsigned * s = sig(n); unsigned j = m_precision; for (unsigned i = 0; i < m_precision; i++, j++) { b[i] = s[i]; b[j] = 0; } } void mpff_manager::to_buffer_shifting(unsigned idx, mpff const & n) const { SASSERT(idx < MPFF_NUM_BUFFERS); svector & b = const_cast(this)->m_buffers[idx]; unsigned * s = sig(n); unsigned j = m_precision; for (unsigned i = 0; i < m_precision; i++, j++) { b[i] = 0; b[j] = s[i]; } } void mpff_manager::del(mpff & n) { unsigned sig_idx = n.m_sig_idx; if (sig_idx != 0) { m_id_gen.recycle(sig_idx); unsigned * s = sig(n); for (unsigned i = 0; i < m_precision; i++) s[i] = 0; } } void mpff_manager::reset(mpff & n) { del(n); n.m_sign = false; n.m_sig_idx = 0; n.m_exponent = 0; SASSERT(check(n)); } bool mpff_manager::is_int(mpff const & n) const { if (n.m_exponent >= 0) return true; // cheap case if (n.m_exponent <= -static_cast(m_precision_bits)) return false; return !has_one_at_first_k_bits(m_precision, sig(n), -n.m_exponent); } bool mpff_manager::is_int64(mpff const & n) const { SASSERT(m_precision >= 2); if (is_zero(n)) return true; int max_exp = -static_cast(sizeof(unsigned) * 8 * (m_precision - 2)); if (n.m_exponent < max_exp) { return n.m_exponent > -static_cast(m_precision_bits) && !has_one_at_first_k_bits(m_precision, sig(n), -n.m_exponent); } else if (n.m_exponent == max_exp) { // handle INT64_MIN case unsigned * s = sig(n); return is_neg(n) && s[m_precision-1] == 0x80000000u && ::is_zero(m_precision-1, s); } else { return false; } } bool mpff_manager::is_uint64(mpff const & n) const { SASSERT(m_precision >= 2); if (is_zero(n)) return true; return n.m_sign == 0 && n.m_exponent <= -static_cast(sizeof(unsigned) * 8 * (m_precision - 2)) && n.m_exponent > -static_cast(m_precision_bits) && !has_one_at_first_k_bits(m_precision, sig(n), -n.m_exponent); } uint64_t mpff_manager::get_uint64(mpff const & a) const { SASSERT(is_uint64(a)); if (is_zero(a)) return 0; int exp = -a.m_exponent - sizeof(unsigned) * 8 * (m_precision - 2); SASSERT(exp >= 0); uint64_t * s = reinterpret_cast(sig(a) + (m_precision - 2)); return *s >> exp; } int64_t mpff_manager::get_int64(mpff const & a) const { SASSERT(is_int64(a)); if (is_zero(a)) return 0; int exp = -a.m_exponent - sizeof(unsigned) * 8 * (m_precision - 2); SASSERT(exp >= 0); uint64_t * s = reinterpret_cast(sig(a) + (m_precision - 2)); // INT64_MIN case if (exp == 0 && *s == 0x8000000000000000ull && is_neg(a)) { return INT64_MIN; } else { int64_t r = *s >> exp; if (is_neg(a)) r = -r; return r; } } // Return true if n is 1 or -1 bool mpff_manager::is_abs_one(mpff const & n) const { // That is, check whether // n.exponent == 1 - m_precision_bits // n.significand == 0b10000...0 (that is, only the highest bit is set in the significand). if (n.m_exponent != 1 - static_cast(m_precision_bits)) return false; unsigned * s = sig(n); if (s[m_precision - 1] != 0x80000000u) return false; for (unsigned i = 0; i < m_precision - 1; i++) if (s[i] != 0) return false; return true; } bool mpff_manager::is_two(mpff const & n) const { // That is, check whether // n.exponent = 2 - m_precision_bits // n.significand == 0b10000...0 (that is, only the highest bit is set in the significand). if (is_neg(n)) return false; if (n.m_exponent != 2 - static_cast(m_precision_bits)) return false; unsigned * s = sig(n); if (s[m_precision - 1] != 0x80000000u) return false; for (unsigned i = 0; i < m_precision - 1; i++) if (s[i] != 0) return false; return true; } void mpff_manager::set(mpff & n, int v) { if (v == 0) { reset(n); } else { if (v < 0) { set(n, static_cast(-v)); n.m_sign = 1; } else { set(n, static_cast(v)); } } SASSERT(check(n)); } void mpff_manager::set(mpff & n, unsigned v) { if (v == 0) { reset(n); } else { allocate_if_needed(n); n.m_sign = 0; int num_leading_zeros = nlz_core(v); n.m_exponent = static_cast(8 * sizeof(unsigned)) - num_leading_zeros - static_cast(m_precision_bits); v <<= num_leading_zeros; unsigned * s = sig(n); s[m_precision - 1] = v; for (unsigned i = 0; i < m_precision - 1; i++) s[i] = 0; } SASSERT(check(n)); } void mpff_manager::set(mpff & n, int64_t v) { if (v == 0) { reset(n); } else { if (v < 0) { set(n, 1 + static_cast(-(1+v))); n.m_sign = 1; } else { set(n, static_cast(v)); } } SASSERT(check(n)); SASSERT(get_int64(n) == v); } void mpff_manager::set(mpff & n, uint64_t v) { #ifdef Z3DEBUG uint64_t old_v = v; #endif if (v == 0) { reset(n); } else { allocate_if_needed(n); n.m_sign = 0; unsigned * _v = reinterpret_cast(&v); int num_leading_zeros = nlz(2, _v); n.m_exponent = static_cast(8 * sizeof(uint64_t)) - num_leading_zeros - static_cast(m_precision_bits); v <<= num_leading_zeros; SASSERT(m_precision >= 2); unsigned * s = sig(n); s[m_precision-1] = _v[1]; s[m_precision-2] = _v[0]; for (unsigned i = 0; i < m_precision - 2; i++) s[i] = 0; } SASSERT(check(n)); SASSERT(get_uint64(n) == old_v); } void mpff_manager::set(mpff & n, int num, unsigned den) { scoped_mpff a(*this), b(*this); set(a, num); set(b, den); div(a, b, n); SASSERT(check(n)); } void mpff_manager::set(mpff & n, int64_t num, uint64_t den) { scoped_mpff a(*this), b(*this); set(a, num); set(b, den); div(a, b, n); SASSERT(check(n)); } void mpff_manager::set(mpff & n, mpff const & v) { if (is_zero(v)) { reset(n); return; } if (&n == &v) return; allocate_if_needed(n); n.m_sign = v.m_sign; n.m_exponent = v.m_exponent; unsigned * s1 = sig(n); unsigned * s2 = sig(v); for (unsigned i = 0; i < m_precision; i++) s1[i] = s2[i]; SASSERT(check(n)); } template void mpff_manager::set_core(mpff & n, mpz_manager & m, mpz const & v) { TRACE("mpff", tout << "mpz->mpff\n"; m.display(tout, v); tout << "\n";); if (m.is_int64(v)) { TRACE("mpff", tout << "is_int64 " << m.get_int64(v) << "\n";); set(n, m.get_int64(v)); } else if (m.is_uint64(v)) { TRACE("mpff", tout << "is_uint64\n";); set(n, m.get_uint64(v)); } else { allocate_if_needed(n); svector & w = m_set_buffer; n.m_sign = m.decompose(v, w); while (w.size() < m_precision) { w.push_back(0); } TRACE("mpff", tout << "w words: "; for (unsigned i = 0; i < w.size(); i++) tout << w[i] << " "; tout << "\n";); unsigned w_sz = w.size(); SASSERT(w_sz >= m_precision); unsigned num_leading_zeros = nlz(w_sz, w.c_ptr()); shl(w_sz, w.c_ptr(), num_leading_zeros, w_sz, w.c_ptr()); unsigned * s = sig(n); unsigned i = m_precision; unsigned j = w_sz; while (i > 0) { --i; --j; s[i] = w[j]; } n.m_exponent = j * 8 * sizeof(unsigned) - static_cast(num_leading_zeros); if ((n.m_sign == 1) != m_to_plus_inf) { // must check whether it is precise or not while (j > 0) { --j; if (w[j] != 0) { // imprecision detected. inc_significand(n); } } // it is precise } } TRACE("mpff", tout << "mpz->mpff result:\n"; display_raw(tout, n); tout << "\n";); SASSERT(check(n)); } void mpff_manager::set(mpff & n, unsynch_mpz_manager & m, mpz const & v) { set_core(n, m, v); } #ifndef SINGLE_THREAD void mpff_manager::set(mpff & n, synch_mpz_manager & m, mpz const & v) { set_core(n, m, v); } #endif template void mpff_manager::set_core(mpff & n, mpq_manager & m, mpq const & v) { // TODO: improve precision? scoped_mpff num(*this), den(*this); set_core(num, m, v.numerator()); { flet l(m_to_plus_inf, !m_to_plus_inf); set_core(den, m, v.denominator()); } div(num, den, n); SASSERT(check(n)); } void mpff_manager::set(mpff & n, unsynch_mpq_manager & m, mpq const & v) { set_core(n, m, v); } #ifndef SINGLE_THREAD void mpff_manager::set(mpff & n, synch_mpq_manager & m, mpq const & v) { set_core(n, m, v); } #endif bool mpff_manager::eq(mpff const & a, mpff const & b) const { if (is_zero(a) && is_zero(b)) return true; if (is_zero(a) || is_zero(b)) return false; if (a.m_sign != b.m_sign || a.m_exponent != b.m_exponent) return false; unsigned * s1 = sig(a); unsigned * s2 = sig(b); for (unsigned i = 0; i < m_precision; i++) if (s1[i] != s2[i]) return false; return true; } bool mpff_manager::lt(mpff const & a, mpff const & b) const { STRACE("mpff_trace", tout << "[mpff] ("; display(tout, a); tout << " < "; display(tout, b); tout << ") == ";); if (is_zero(a)) { if (is_zero(b) || is_neg(b)) { STRACE("mpff_trace", tout << "(1 == 0)\n";); return false; } else { STRACE("mpff_trace", tout << "(1 == 1)\n";); SASSERT(is_pos(b)); return true; } } if (is_zero(b)) { SASSERT(!is_zero(a)); if (is_neg(a)) { STRACE("mpff_trace", tout << "(1 == 1)\n";); return true; } else { SASSERT(is_pos(a)); STRACE("mpff_trace", tout << "(1 == 0)\n";); return false; } } SASSERT(!is_zero(a)); SASSERT(!is_zero(b)); if (a.m_sign == 1) { if (b.m_sign == 0) { STRACE("mpff_trace", tout << "(1 == 1)\n";); return true; // neg < pos } // case: neg neg bool r = b.m_exponent < a.m_exponent || (a.m_exponent == b.m_exponent && ::lt(m_precision, sig(b), sig(a))); STRACE("mpff_trace", tout << "(" << r << " == 1)\n";); return r; } else { if (b.m_sign == 1) { STRACE("mpff_trace", tout << "(1 == 0)\n";); return false; // pos < neg } // case: pos pos bool r = a.m_exponent < b.m_exponent || (a.m_exponent == b.m_exponent && ::lt(m_precision, sig(a), sig(b))); STRACE("mpff_trace", tout << "(" << r << " == 1)\n";); return r; } } void mpff_manager::inc_significand(unsigned * s, int64_t & exp) { if (!::inc(m_precision, s)) { SASSERT(::is_zero(m_precision, s)); s[m_precision - 1] = MIN_MSW; SASSERT(exp != INT64_MAX); exp++; } } void mpff_manager::inc_significand(mpff & a) { unsigned * s = sig(a); if (!::inc(m_precision, s)) { // Overflow happened, a was of the form 0xFFFF...FF // Now, it must be 0x000...000 SASSERT(::is_zero(m_precision, s)); // Set it to 0x80000...000, and increment exponent by 1. s[m_precision - 1] = MIN_MSW; if (a.m_exponent == INT_MAX) throw overflow_exception(); a.m_exponent++; } } void mpff_manager::dec_significand(mpff & a) { SASSERT(!is_minus_epsilon(a) && !is_zero(a) && !is_plus_epsilon(a)); unsigned * s = sig(a); for (unsigned i = 0; i < m_precision - 1; i++) { s[i]--; if (s[i] != UINT_MAX) return; } s[m_precision - 1]--; if (s[m_precision - 1] < MIN_MSW) { s[m_precision - 1] = UINT_MAX; a.m_exponent--; } } bool mpff_manager::min_significand(mpff const & a) const { unsigned * s = sig(a); return s[m_precision - 1] == MIN_MSW && ::is_zero(m_precision - 1, s); } bool mpff_manager::is_minus_epsilon(mpff const & a) const { return a.m_sign == 1 && a.m_exponent == INT_MIN && min_significand(a); } bool mpff_manager::is_plus_epsilon(mpff const & a) const { return a.m_sign == 0 && a.m_exponent == INT_MIN && min_significand(a); } void mpff_manager::set_min_significand(mpff & a) { // Since the most significand bit of the most significand word must be 1 in our representation, // we have that 0x8000..00 is the minimal significand unsigned * s = sig(a); s[m_precision - 1] = MIN_MSW; for (unsigned i = 0; i < m_precision - 1; i++) s[i] = 0; } void mpff_manager::set_max_significand(mpff & a) { SASSERT(!is_zero(a)); unsigned * s = sig(a); for (unsigned i = 0; i < m_precision; i++) s[i] = UINT_MAX; } void mpff_manager::set_plus_epsilon(mpff & n) { allocate_if_needed(n); n.m_sign = 0; n.m_exponent = INT_MIN; set_min_significand(n); SASSERT(check(n)); } void mpff_manager::set_minus_epsilon(mpff & n) { set_plus_epsilon(n); n.m_sign = 1; SASSERT(check(n)); } void mpff_manager::set_max(mpff & n) { allocate_if_needed(n); n.m_sign = 0; n.m_exponent = INT_MAX; set_max_significand(n); SASSERT(check(n)); } void mpff_manager::set_min(mpff & n) { set_max(n); n.m_sign = 1; SASSERT(check(n)); } void mpff_manager::next(mpff & a) { if (is_zero(a)) { set_plus_epsilon(a); } else if (is_minus_epsilon(a)) { reset(a); } else if (a.m_sign == 0) { inc_significand(a); } else { dec_significand(a); } SASSERT(check(a)); } void mpff_manager::prev(mpff & a) { if (is_zero(a)) { set_minus_epsilon(a); } else if (is_plus_epsilon(a)) { reset(a); } else if (a.m_sign == 0) { dec_significand(a); } else { inc_significand(a); } SASSERT(check(a)); } void mpff_manager::set_big_exponent(mpff & a, int64_t e) { SASSERT(e > INT_MAX || e < INT_MIN); if (e > INT_MAX) { if (a.m_sign == 1) { if (m_to_plus_inf) set_min(a); else throw overflow_exception(); } else { if (m_to_plus_inf) throw overflow_exception(); else set_max(a); } } else { SASSERT(e < INT_MIN); if (a.m_sign == 1) { if (m_to_plus_inf) reset(a); else set_minus_epsilon(a); } else { if (m_to_plus_inf) set_plus_epsilon(a); else reset(a); } } } void mpff_manager::add_sub(bool is_sub, mpff const & a, mpff const & b, mpff & c) { if (is_zero(a)) { set(c, b); if (is_sub) neg(c); return; } if (is_zero(b)) { set(c, a); return; } TRACE("mpff", tout << (is_sub ? "sub" : "add") << "("; display(tout, a); tout << ", "; display(tout, b); tout << ")\n";); // Remark: any result returned by sig(...) may be invalid after a call to allocate_if_needed() // So, we must invoke allocate_if_needed(c) before we invoke sig(a) and sig(b). allocate_if_needed(c); bool sgn_a, sgn_b; int exp_a, exp_b; unsigned * sig_a, * sig_b; if (a.m_exponent >= b.m_exponent) { sgn_a = a.m_sign != 0; sgn_b = b.m_sign != 0; exp_a = a.m_exponent; exp_b = b.m_exponent; sig_a = sig(a); sig_b = sig(b); if (is_sub) sgn_b = !sgn_b; } else { sgn_a = b.m_sign != 0; sgn_b = a.m_sign != 0; exp_a = b.m_exponent; exp_b = a.m_exponent; sig_a = sig(b); sig_b = sig(a); if (is_sub) sgn_a = !sgn_a; } SASSERT(exp_a >= exp_b); unsigned * n_sig_b; // normalized sig_b // Make sure that a and b have the same exponent. if (exp_a > exp_b) { unsigned shift = (unsigned)exp_a - (unsigned)exp_b; n_sig_b = m_buffers[0].c_ptr(); shr(m_precision, sig_b, shift, m_precision, n_sig_b); if (sgn_b != m_to_plus_inf && has_one_at_first_k_bits(m_precision, sig_b, shift)) { // Precision was lost when normalizing the significand. // The current rounding mode forces us to bump the significand. // Remark: an overflow cannot happen when incrementing the significand. VERIFY(::inc(m_precision, n_sig_b)); } } else { SASSERT(exp_a == exp_b); n_sig_b = sig_b; } // Compute c if (sgn_a == sgn_b) { c.m_sign = sgn_a; unsigned * sig_r = m_buffers[1].c_ptr(); size_t r_sz; m_mpn_manager.add(sig_a, m_precision, n_sig_b, m_precision, sig_r, m_precision + 1, &r_sz); SASSERT(r_sz <= m_precision + 1); unsigned num_leading_zeros = nlz(m_precision + 1, sig_r); SASSERT(num_leading_zeros >= sizeof(unsigned) * 8 - 1); // num_leading_zeros >= 31 SASSERT(num_leading_zeros < sizeof(unsigned) * 8 * (m_precision + 1)); // result is not zero. unsigned * sig_c = sig(c); if (num_leading_zeros == sizeof(unsigned) * 8) { // no shift is needed c.m_exponent = exp_a; for (unsigned i = 0; i < m_precision; i++) sig_c[i] = sig_r[i]; } else if (num_leading_zeros == sizeof(unsigned) * 8 - 1) { // shift 1 right bool _inc_significand = ((c.m_sign == 1) != m_to_plus_inf) && has_one_at_first_k_bits(m_precision*2, sig_r, 1); int64_t exp_c = exp_a; exp_c++; shr(m_precision + 1, sig_r, 1, m_precision, sig_c); if (_inc_significand) inc_significand(sig_c, exp_c); set_exponent(c, exp_c); } else { SASSERT(num_leading_zeros > sizeof(unsigned) * 8); num_leading_zeros -= sizeof(unsigned) * 8; // remove 1 word bits. // Now, we can assume sig_r has size m_precision SASSERT(num_leading_zeros > 0); // shift left num_leading_zeros int64_t exp_c = exp_a; exp_c -= num_leading_zeros; shl(m_precision, sig_r, num_leading_zeros, m_precision, sig_c); set_exponent(c, exp_c); } } else { unsigned borrow; SASSERT(sgn_a != sgn_b); unsigned * sig_c = sig(c); if (::lt(m_precision, sig_a, n_sig_b)) { c.m_sign = sgn_b; m_mpn_manager.sub(n_sig_b, m_precision, sig_a, m_precision, sig_c, &borrow); } else { c.m_sign = sgn_a; m_mpn_manager.sub(sig_a, m_precision, n_sig_b, m_precision, sig_c, &borrow); } SASSERT(borrow == 0); unsigned num_leading_zeros = nlz(m_precision, sig_c); if (num_leading_zeros == m_precision_bits) { reset(c); } else if (num_leading_zeros > 0) { int64_t exp_c = exp_a; exp_c -= num_leading_zeros; shl(m_precision, sig_c, num_leading_zeros, m_precision, sig_c); set_exponent(c, exp_c); } else { SASSERT(num_leading_zeros == 0); c.m_exponent = exp_a; } } TRACE("mpff", tout << "result: "; display(tout, c); tout << "\n";); SASSERT(check(c)); } void mpff_manager::add(mpff const & a, mpff const & b, mpff & c) { STRACE("mpff_trace", tout << "[mpff] "; display(tout, a); tout << " + "; display(tout, b); tout << " " << (m_to_plus_inf ? "<=" : ">=") << " ";); add_sub(false, a, b, c); STRACE("mpff_trace", display(tout, c); tout << "\n";); } void mpff_manager::sub(mpff const & a, mpff const & b, mpff & c) { STRACE("mpff_trace", tout << "[mpff] "; display(tout, a); tout << " - "; display(tout, b); tout << " " << (m_to_plus_inf ? "<=" : ">=") << " ";); add_sub(true, a, b, c); STRACE("mpff_trace", display(tout, c); tout << "\n";); } void mpff_manager::mul(mpff const & a, mpff const & b, mpff & c) { STRACE("mpff_trace", tout << "[mpff] ("; display(tout, a); tout << ") * ("; display(tout, b); tout << ") " << (m_to_plus_inf ? "<=" : ">=") << " ";); if (is_zero(a) || is_zero(b)) { reset(c); } else { allocate_if_needed(c); TRACE("mpff", tout << "mul("; display(tout, a); tout << ", "; display(tout, b); tout << ")\n";); c.m_sign = a.m_sign ^ b.m_sign; // use int64_t to make sure we do not have overflows int64_t exp_a = a.m_exponent; int64_t exp_b = b.m_exponent; int64_t exp_c = exp_a + exp_b; // store result in m_buffers[0] unsigned * r = m_buffers[0].c_ptr(); m_mpn_manager.mul(sig(a), m_precision, sig(b), m_precision, r); // r has 2*m_precision_bits bits unsigned num_leading_zeros = nlz(m_precision*2, r); SASSERT(num_leading_zeros <= m_precision_bits); TRACE("mpff", tout << "num_leading_zeros: " << num_leading_zeros << "\n";); // We must shift right (m_precision_bits - num_leading_zeros) // If r does not have a 1 bit in the first (m_precision_bits - num_leading_zeros), then the result is precise. unsigned shift = m_precision_bits - num_leading_zeros; // Imprecision == "lost digits" // If c.m_sign --> result became bigger (e.g., -3.1 --> -3) // If !c.m_sign --> result became smaller (e.g., 3.1 --> 3) // Thus, when we are imprecise, we only need to bump the significand when: // 1) !c.m_sign && m_to_plus_inf // 2) c.m_sign && !m_to_plus_inf bool _inc_significand = ((c.m_sign == 1) != m_to_plus_inf) && has_one_at_first_k_bits(m_precision*2, r, shift); TRACE("mpff", tout << "c.m_sign: " << c.m_sign << ", m_to_plus_inf: " << m_to_plus_inf << "\nhas_one_at_first_k_bits: " << has_one_at_first_k_bits(m_precision*2, r, shift) << "\n"; tout << "_inc_significand: " << _inc_significand << "\n";); exp_c += shift; unsigned * s_c = sig(c); shr(m_precision*2, r, shift, m_precision, s_c); if (_inc_significand) inc_significand(s_c, exp_c); set_exponent(c, exp_c); TRACE("mpff", tout << "result: "; display(tout, c); tout << "\n";); SASSERT(check(c)); } STRACE("mpff_trace", display(tout, c); tout << "\n";); } void mpff_manager::div(mpff const & a, mpff const & b, mpff & c) { if (is_zero(b)) throw div0_exception(); STRACE("mpff_trace", tout << "[mpff] ("; display(tout, a); tout << ") / ("; display(tout, b); tout << ") " << (m_to_plus_inf ? "<=" : ">=") << " ";); if (is_zero(a)) { reset(c); } #if 1 else if (is_two(b)) { set(c, a); int64_t exp_c = a.m_exponent; exp_c--; set_exponent(c, exp_c); } #endif else { allocate_if_needed(c); c.m_sign = a.m_sign ^ b.m_sign; // use int64_t to make sure we do not have overflows int64_t exp_a = a.m_exponent; int64_t exp_b = b.m_exponent; int64_t exp_c = exp_a - exp_b; exp_c -= m_precision_bits; // we will multiplying (shifting) a by 2^m_precision_bits. // copy a to buffer 0, and shift by m_precision_bits to_buffer_shifting(0, a); unsigned * _a = m_buffers[0].c_ptr(); unsigned * q = m_buffers[1].c_ptr(); unsigned q_sz = m_precision + 1; // 2*m_precision - m_precision + 1 unsigned * r = m_buffers[2].c_ptr(); unsigned r_sz = m_precision; SASSERT(!::is_zero(2*m_precision, _a)); SASSERT(!::is_zero(m_precision, sig(b))); SASSERT(nlz(2*m_precision, _a) == 0); // Thus it is always the case that _a > b since size(a) = 2*size(b) // Actually, a is much bigger than b. // b is at most 2^m_precision_bits - 1 // a is at least 2^(2*m_precision_bits - 1) // Thus the quotient of a/b cannot be zero // Actually, quotient of a/b must be >= 2^(2*m_precision_bits - 1)/(2^m_precision_bits - 1) m_mpn_manager.div(_a, 2 * m_precision, sig(b), m_precision, q, r); TRACE("mpff_div", unsigned j = q_sz; while (j > 0) { --j; tout << std::hex << std::setfill('0') << std::setw(2*sizeof(unsigned)) << q[j]; tout << " "; } tout << std::dec << "\n";); SASSERT(!::is_zero(q_sz, q)); unsigned num_leading_zeros = nlz(q_sz, q); SASSERT(num_leading_zeros < q_sz * 8 * sizeof(unsigned)); unsigned q_bits = q_sz * 8 * sizeof(unsigned); SASSERT(num_leading_zeros < q_bits); unsigned actual_q_bits = q_bits - num_leading_zeros; bool _inc_significand; unsigned * s_c = sig(c); if (actual_q_bits > m_precision_bits) { unsigned shift = actual_q_bits - m_precision_bits; // We are imprecise if the remainder is != 0 or if we lost a bit when shifting. // See comment in mul(...) _inc_significand = ((c.m_sign == 1) != m_to_plus_inf) && (has_one_at_first_k_bits(q_sz, q, shift) || !::is_zero(r_sz, r)); exp_c += shift; shr(q_sz, q, shift, m_precision, s_c); } else { // We are imprecise only if the remainder is != 0 _inc_significand = ((c.m_sign == 1) != m_to_plus_inf) && !::is_zero(r_sz, r); if (actual_q_bits < m_precision_bits) { unsigned shift = m_precision_bits - actual_q_bits; exp_c -= shift; shl(q_sz, q, shift, m_precision, s_c); } else { SASSERT(actual_q_bits == m_precision_bits); ::copy(q_sz, q, m_precision, s_c); } } if (_inc_significand) inc_significand(s_c, exp_c); set_exponent(c, exp_c); SASSERT(check(c)); } STRACE("mpff_trace", display(tout, c); tout << "\n";); } void mpff_manager::floor(mpff & n) { if (n.m_exponent >= 0) return; STRACE("mpff_trace", tout << "[mpff] Floor["; display(tout, n); tout << "] == ";); if (n.m_exponent <= -static_cast(m_precision_bits)) { // number is between (-1, 1) if (n.m_sign == 0) reset(n); else set(n, -1); } else { unsigned * s = sig(n); if (n.m_sign == 1 && has_one_at_first_k_bits(m_precision, s, -n.m_exponent)) { shr(m_precision, s, -n.m_exponent, m_precision, s); VERIFY(::inc(m_precision, s)); int num_leading_zeros = nlz(m_precision, s); SASSERT(num_leading_zeros == -n.m_exponent || num_leading_zeros == -n.m_exponent - 1); if (num_leading_zeros != -n.m_exponent) { shl(m_precision, s, -n.m_exponent - 1, m_precision, s); n.m_exponent++; } else { shl(m_precision, s, -n.m_exponent, m_precision, s) ; } } else { // reset first n.m_exponent bits shr(m_precision, s, -n.m_exponent, m_precision, s); shl(m_precision, s, -n.m_exponent, m_precision, s); } } SASSERT(check(n)); STRACE("mpff_trace", display(tout, n); tout << "\n";); } void mpff_manager::ceil(mpff & n) { if (n.m_exponent >= 0) return; STRACE("mpff_trace", tout << "[mpff] Ceiling["; display(tout, n); tout << "] == ";); if (n.m_exponent <= -static_cast(m_precision_bits)) { // number is between (-1, 1) if (n.m_sign == 0) set(n, 1); else reset(n); } else { unsigned * s = sig(n); if (n.m_sign == 0 && has_one_at_first_k_bits(m_precision, s, -n.m_exponent)) { shr(m_precision, s, -n.m_exponent, m_precision, s); VERIFY(::inc(m_precision, s)); int num_leading_zeros = nlz(m_precision, s); SASSERT(num_leading_zeros == -n.m_exponent || num_leading_zeros == -n.m_exponent - 1); if (num_leading_zeros != -n.m_exponent) { shl(m_precision, s, -n.m_exponent - 1, m_precision, s); n.m_exponent++; } else { shl(m_precision, s, -n.m_exponent, m_precision, s) ; } } else { // reset first n.m_exponent bits shr(m_precision, s, -n.m_exponent, m_precision, s); shl(m_precision, s, -n.m_exponent, m_precision, s); } } SASSERT(check(n)); STRACE("mpff_trace", display(tout, n); tout << "\n";); } void mpff_manager::power(mpff const & a, unsigned p, mpff & b) { #ifdef _TRACE scoped_mpff _a(*this); _a = a; unsigned _p = p; #endif #define SMALL_POWER 8 SASSERT(check(a)); if (is_zero(a)) { SASSERT(p != 0); reset(b); } else if (p == 0) { set(b, 1); } else if (p == 1) { set(b, a); } else if (p == 2) { mul(a, a, b); } else if (p <= SMALL_POWER && &a != &b) { SASSERT(p > 2); --p; set(b, a); while (p > 0) { --p; mul(a, b, b); } } else { unsigned * s = sig(a); if (s[m_precision - 1] == 0x80000000u && ::is_zero(m_precision - 1, s)) { allocate_if_needed(b); if (p % 2 == 0) b.m_sign = 0; else b.m_sign = a.m_sign; int64_t exp = a.m_exponent; exp *= p; if (exp > INT_MAX || exp < INT_MIN) throw overflow_exception(); exp += (m_precision_bits - 1)*(p - 1); if (exp > INT_MAX || exp < INT_MIN) throw overflow_exception(); unsigned * r = sig(b); r[m_precision - 1] = 0x80000000u; for (unsigned i = 0; i < m_precision - 1; i++) r[i] = 0; b.m_exponent = static_cast(exp); } else { unsigned mask = 1; scoped_mpff pw(*this); set(pw, a); set(b, 1); while (mask <= p) { if (mask & p) mul(b, pw, b); mul(pw, pw, pw); mask = mask << 1; } } } STRACE("mpff_trace", tout << "[mpff] ("; display(tout, _a); tout << ") ^ " << _p << (m_to_plus_inf ? "<=" : ">="); display(tout, b); tout << "\n";); TRACE("mpff_power", display_raw(tout, b); tout << "\n";); SASSERT(check(b)); } bool mpff_manager::is_power_of_two(mpff const & a, unsigned & k) const { if (!is_power_of_two(a)) return false; int64_t exp = a.m_exponent + m_precision_bits - 1; SASSERT(exp >= 0); k = static_cast(exp); return true; } bool mpff_manager::is_power_of_two(mpff const & a) const { unsigned * s = sig(a); return is_pos(a) && a.m_exponent > -static_cast(m_precision_bits) && s[m_precision - 1] == 0x80000000u && ::is_zero(m_precision - 1, s); } template void mpff_manager::significand_core(mpff const & n, mpz_manager & m, mpz & t) { m.set_digits(t, m_precision, sig(n)); } void mpff_manager::significand(mpff const & n, unsynch_mpz_manager & m, mpz & t) { significand_core(n, m, t); } #ifndef SINGLE_THREAD void mpff_manager::significand(mpff const & n, synch_mpz_manager & m, mpz & t) { significand_core(n, m, t); } #endif template void mpff_manager::to_mpz_core(mpff const & n, mpz_manager & m, mpz & t) { SASSERT(is_int(n)); int exp = n.m_exponent; if (exp < 0) { SASSERT(exp > -static_cast(m_precision_bits)); to_buffer(0, n); unsigned * b = m_buffers[0].c_ptr(); shr(m_precision, b, -exp, m_precision, b); m.set_digits(t, m_precision, b); } else { m.set_digits(t, m_precision, sig(n)); if (exp > 0) { _scoped_numeral > p(m); m.set(p, 2); m.power(p, exp, p); m.mul(t, p, t); } } if (is_neg(n)) m.neg(t); } void mpff_manager::to_mpz(mpff const & n, unsynch_mpz_manager & m, mpz & t) { to_mpz_core(n, m, t); } #ifndef SINGLE_THREAD void mpff_manager::to_mpz(mpff const & n, synch_mpz_manager & m, mpz & t) { to_mpz_core(n, m, t); } #endif template void mpff_manager::to_mpq_core(mpff const & n, mpq_manager & m, mpq & t) { int exp = n.m_exponent; TRACE("mpff_to_mpq", tout << "to_mpq: "; display(tout, n); tout << "\nexp: " << exp << "\n";); if (exp < 0 && exp > -static_cast(m_precision_bits) && !has_one_at_first_k_bits(m_precision, sig(n), -n.m_exponent)) { to_buffer(0, n); unsigned * b = m_buffers[0].c_ptr(); shr(m_precision, b, -exp, m_precision, b); m.set(t, m_precision, b); } else { m.set(t, m_precision, sig(n)); if (exp != 0) { _scoped_numeral > p(m); m.set(p, 2); unsigned abs_exp; if (exp < 0) { // Avoid -INT_MIN == INT_MIN issue. It is not really useful, since we will run out of memory anyway. if (exp == INT_MIN) abs_exp = static_cast(-static_cast(INT_MIN)); else abs_exp = -exp; } else { abs_exp = exp; } m.power(p, abs_exp, p); if (exp < 0) m.div(t, p, t); else m.mul(t, p, t); } } if (is_neg(n)) m.neg(t); } void mpff_manager::to_mpq(mpff const & n, unsynch_mpq_manager & m, mpq & t) { to_mpq_core(n, m, t); } #ifndef SINGLE_THREAD void mpff_manager::to_mpq(mpff const & n, synch_mpq_manager & m, mpq & t) { to_mpq_core(n, m, t); } #endif void mpff_manager::display_raw(std::ostream & out, mpff const & n) const { if (is_neg(n)) out << "-"; unsigned * s = sig(n); unsigned i = m_precision; while(i > 0) { --i; out << std::hex << std::setfill('0') << std::setw(2 * sizeof(unsigned)) << s[i]; } out << "*2^" << std::dec << n.m_exponent; } void mpff_manager::display(std::ostream & out, mpff const & n) const { if (is_neg(n)) out << "-"; to_buffer_ext(0, n); svector & u_buffer = const_cast(this)->m_buffers[0]; int num_trailing_zeros = ntz(m_precision, u_buffer.c_ptr()); int shift = 0; int64_t exp = n.m_exponent; // use int64_t to avoid -INT_MIN == INT_MIN issue if (exp < 0) { if (num_trailing_zeros >= -exp) { shift = static_cast(-exp); exp = 0; } else { shift = num_trailing_zeros; exp += num_trailing_zeros; } } if (shift > 0) shr(m_precision, u_buffer.c_ptr(), shift, u_buffer.c_ptr()); sbuffer str_buffer(11*m_precision, 0); out << m_mpn_manager.to_string(u_buffer.c_ptr(), m_precision, str_buffer.begin(), str_buffer.size()); if (exp > 0) { if (exp <= 63) { uint64_t _exp = 1; _exp <<= exp; out << "*" << _exp; } else { out << "*2"; if (exp > 1) { out << "^"; out << exp; } } } else if (exp < 0) { exp = -exp; if (exp <= 63) { uint64_t _exp = 1; _exp <<= exp; out << "/" << _exp; } else { out << "/2"; if (exp > 1) { out << "^"; out << exp; } } } } void mpff_manager::display_decimal(std::ostream & out, mpff const & n, unsigned prec, unsigned min_exponent) { // The result in scientific notation when n.m_exponent >= min_exponent or n.m_exponent <= - min_exponent - m_precision_bits int64_t exp = n.m_exponent; if (exp >= min_exponent || exp <= -static_cast(min_exponent) - m_precision_bits || is_int(n)) { display(out, n); return; } if (is_neg(n)) out << "-"; unsigned word_sz = 8 * sizeof(unsigned); if (exp >= 0) { sbuffer buffer; unsigned num_extra_words = 1 + static_cast(exp/word_sz); unsigned shift = word_sz - exp%word_sz; for (unsigned i = 0; i < num_extra_words; i++) buffer.push_back(0); unsigned * s = sig(n); for (unsigned i = 0; i < m_precision; i++) buffer.push_back(s[i]); shr(buffer.size(), buffer.c_ptr(), shift, buffer.size(), buffer.c_ptr()); sbuffer str_buffer(11*buffer.size(), 0); out << m_mpn_manager.to_string(buffer.c_ptr(), buffer.size(), str_buffer.begin(), str_buffer.size()); } else { sbuffer buffer1, buffer2; sbuffer buffer3; exp = -exp; unsigned num_words = 1 + static_cast(exp/word_sz); unsigned num_extra_words = m_precision < num_words ? num_words - m_precision : 0; num_extra_words++; unsigned * s = sig(n); for (unsigned i = 0; i < m_precision; i++) { buffer1.push_back(s[i]); buffer2.push_back(0); buffer3.push_back(0); } for (unsigned i = 0; i < num_extra_words; i++) { buffer1.push_back(0); buffer2.push_back(0); } unsigned ten = 10; sbuffer pw_buffer; pw_buffer.resize(num_words, 0); pw_buffer[0] = 1; shl(num_words, pw_buffer.c_ptr(), static_cast(exp), num_words, pw_buffer.c_ptr()); if (num_words > m_precision) { out << "0"; } else { m_mpn_manager.div(buffer1.c_ptr(), m_precision, pw_buffer.c_ptr(), num_words, buffer3.c_ptr(), buffer2.c_ptr()); sbuffer str_buffer(11*buffer3.size(), 0); out << m_mpn_manager.to_string(buffer3.c_ptr(), buffer3.size(), str_buffer.begin(), str_buffer.size()); SASSERT(!::is_zero(buffer2.size(), buffer2.c_ptr())); // otherwise n is an integer ::copy(buffer2.size(), buffer2.c_ptr(), buffer1.size(), buffer1.c_ptr()); } out << "."; // buffer1 contain the fractional part unsigned i = 0; unsigned sz1 = buffer1.size(); while (sz1 > 0 && buffer1[sz1-1] == 0) --sz1; SASSERT(sz1 > 0); // otherwise the number is an integer while (sz1 > 0) { if (i >= prec) { out << "?"; return; } i = i + 1; m_mpn_manager.mul(buffer1.c_ptr(), sz1, &ten, 1, buffer2.c_ptr()); unsigned sz2 = sz1 + 1; while (sz2 > 0 && buffer2[sz2-1] == 0) --sz2; SASSERT(sz2 > 0); if (num_words > sz2) { out << "0"; sz1 = sz2; ::copy(sz2, buffer2.c_ptr(), sz1, buffer1.c_ptr()); SASSERT(sz1 == 0 || !::is_zero(sz1, buffer1.c_ptr())); } else { m_mpn_manager.div(buffer2.c_ptr(), sz2, pw_buffer.c_ptr(), num_words, buffer3.c_ptr(), buffer1.c_ptr()); out << buffer3[0]; sz1 = num_words; while (sz1 > 0 && buffer1[sz1-1] == 0) --sz1; SASSERT(sz1 == 0 || !::is_zero(sz1, buffer1.c_ptr())); } } } } void mpff_manager::display_smt2(std::ostream & out, mpff const & n, bool decimal) const { if (is_neg(n)) out << "(- "; to_buffer_ext(0, n); svector & u_buffer = const_cast(this)->m_buffers[0]; int num_trailing_zeros = ntz(m_precision, u_buffer.c_ptr()); int shift = 0; int64_t exp = n.m_exponent; if (exp < 0) { if (num_trailing_zeros >= -exp) { shift = static_cast(-exp); exp = 0; } else { shift = num_trailing_zeros; exp += num_trailing_zeros; } } if (shift > 0) shr(m_precision, u_buffer.c_ptr(), shift, u_buffer.c_ptr()); if (exp > 0) out << "(* "; else if (exp < 0) out << "(/ "; sbuffer str_buffer(11*m_precision, 0); out << m_mpn_manager.to_string(u_buffer.c_ptr(), m_precision, str_buffer.begin(), str_buffer.size()); if (decimal) out << ".0"; if (exp != 0) { if (exp < 0) exp = -exp; if (exp <= 63) { uint64_t _exp = 1; _exp <<= exp; out << _exp; if (decimal) out << ".0"; } else { out << " (^ 2"; if (decimal) out << ".0"; out << " " << exp; if (decimal) out << ".0"; out << ")"; } out << ")"; } if (is_neg(n)) out << ")"; } std::string mpff_manager::to_string(mpff const & a) const { std::ostringstream buffer; display(buffer, a); return buffer.str(); } std::string mpff_manager::to_rational_string(mpff const & a) const { // The exponent may be too big to encode without using 2^ return to_string(a); } unsigned mpff_manager::prev_power_of_two(mpff const & a) { if (!is_pos(a)) return 0; if (a.m_exponent <= -static_cast(m_precision_bits)) return 0; // Number is smaller than 1 SASSERT(static_cast(a.m_exponent) + static_cast(m_precision_bits) - 1 >= 0); SASSERT(static_cast(a.m_exponent) + static_cast(m_precision_bits) - 1 <= static_cast(static_cast(UINT_MAX))); return m_precision_bits + a.m_exponent - 1; } bool mpff_manager::check(mpff const & n) const { // n is zero or the most significand bit of the most significand word is 1. unsigned * s = sig(n); (void)s; SASSERT(is_zero(n) || (s[m_precision - 1] & MIN_MSW) != 0); // if n is zero, then the sign must be 0 SASSERT(!is_zero(n) || n.m_sign == 0); // if n is zero, then all bits must be 0. SASSERT(!is_zero(n) || ::is_zero(m_precision, sig(n))); // if n is zero, then exponent must be 0. SASSERT(!is_zero(n) || n.m_exponent == 0); return true; } z3-z3-4.8.7/src/util/mpff.h000066400000000000000000000357011356505360400153020ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: mpff.h Abstract: Multi precision fast floating point numbers. The implementation is not compliant with the IEEE standard. For an IEEE compliant implementation, see mpf.h There are only two rounding modes: towards plus or minus inf. Author: Leonardo de Moura (leonardo) 2012-09-12 Revision History: --*/ #ifndef MPFF_H_ #define MPFF_H_ #include "util/id_gen.h" #include "util/util.h" #include "util/vector.h" #include "util/z3_exception.h" #include "util/scoped_numeral.h" #include "util/scoped_numeral_vector.h" #include "util/mpn.h" class mpff_manager; class mpff { friend class mpff_manager; unsigned m_sign:1; unsigned m_sig_idx:31; // position where the significand is stored in the mpff_manager. int m_exponent; public: mpff(): m_sign(0), m_sig_idx(0), m_exponent(0) { } void swap(mpff & other) { unsigned sign = m_sign; m_sign = other.m_sign; other.m_sign = sign; unsigned sig_idx = m_sig_idx; m_sig_idx = other.m_sig_idx; other.m_sig_idx = sig_idx; std::swap(m_exponent, other.m_exponent); } }; inline void swap(mpff & m1, mpff & m2) { m1.swap(m2); } class mpz; class mpq; template class mpz_manager; template class mpq_manager; #ifndef SINGLE_THREAD typedef mpz_manager synch_mpz_manager; typedef mpq_manager synch_mpq_manager; #else typedef mpz_manager synch_mpz_manager; typedef mpq_manager synch_mpq_manager; #endif typedef mpz_manager unsynch_mpz_manager; typedef mpq_manager unsynch_mpq_manager; class mpff_manager { // Some restrictions on mpff numbers // // - The exponent is always a machine integer. The main point is that 2^(2^31) is a huge number, // we will not even be able to convert the mpff into mpq. Formulas that need this kind of huge number // are usually out-of-reach for Z3. // // - The significand size is measured in words of 32-bit. The number of words is always even. // This decision makes sure that the size (in bits) of mpff numbers is always a multiple of 64. // Thus mpff objs can be easily packed in 64-bit machines. // // - The smallest mpff numeral has 128-bits total. mpff structure has always 64-bits. // The minimal size for the significand is 64-bits. // // - All mpff numerals in a given manager use the same number of words for storing the significand. // This is different from the mpf_manager where the same manager can be used to manipulate floating point numbers // of different precision. // // - In the encoding used for mpff numbers, the most significand bit of the most significand word is always 1. // The only exception is the number zero. // For example, assuming we are using 64-bits for the significand, the number 1 is encoded as // (sign = 0, significand = 0x800..0, exponent = -63) // Note that, in this representation, the smallest positive integer is: // (sign = 0, significand = 0x800..0, exponent = INT_MIN) // instead of // (sign = 0, significand = 0x000..1, exponent = INT_MIN) // // Remarks: // // - All values of type int, unsigned, int64_t and uint64_t can be precisely represented as mpff numerals. // // - Hardware float and double values (corresponding to rationals) can also be precisely represented as mpff numerals. // That is, NaN, +oo and -oo are not supported by this module. // // - An exception (mpff_manager::exception) is thrown if overflow occurs. This can happen because the exponent is // represented as a machine integer. // // - There are only two rounding modes: towards plus infinity and towards minus infinity. // The rounding mode can be dynamically modified. // // - The mpff numerals are stored in a dynamic array. // Type mpff is just an index (unsigned) into this array. unsigned m_precision; //!< Number of words in the significand. Must be an even number. unsigned m_precision_bits; //!< Number of bits in the significand. Must be 32*m_precision. unsigned_vector m_significands; //!< Array containing all significands. unsigned m_capacity; //!< Number of significands that can be stored in m_significands. bool m_to_plus_inf; //!< If True, then round to plus infinity, otherwise to minus infinity id_gen m_id_gen; static const unsigned MPFF_NUM_BUFFERS = 4; svector m_buffers[MPFF_NUM_BUFFERS]; svector m_set_buffer; mpff m_one; mpn_manager m_mpn_manager; unsigned * sig(mpff const & n) const { return m_significands.c_ptr() + (n.m_sig_idx * m_precision); } void ensure_capacity(unsigned sig_idx) { while (sig_idx >= m_capacity) expand(); } void expand(); void allocate_if_needed(mpff & n) { if (n.m_sig_idx == 0) allocate(n); } void allocate(mpff & n); // copy n to buffer idx. void to_buffer(unsigned idx, mpff const & n) const; // copy n to buffer idx and add m_precision zeros. void to_buffer_ext(unsigned idx, mpff const & n) const; // copy (and shift by m_precision_bits) n to buffer idx void to_buffer_shifting(unsigned idx, mpff const & n) const; void inc_significand(unsigned * s, int64_t & exp); void inc_significand(mpff & a); void dec_significand(mpff & a); bool min_significand(mpff const & a) const; void set_min_significand(mpff & a); void set_max_significand(mpff & a); void set_big_exponent(mpff & a, int64_t e); void set_exponent(mpff & a, int64_t e) { if (e > INT_MAX || e < INT_MIN) set_big_exponent(a, e); else a.m_exponent = static_cast(e); } template void set_core(mpff & n, mpz_manager & m, mpz const & v); template void set_core(mpff & n, mpq_manager & m, mpq const & v); template void to_mpz_core(mpff const & n, mpz_manager & m, mpz & t); template void to_mpq_core(mpff const & n, mpq_manager & m, mpq & t); template void significand_core(mpff const & n, mpz_manager & m, mpz & r); void add_sub(bool is_sub, mpff const & a, mpff const & b, mpff & c); public: typedef mpff numeral; static bool precise() { return false; } static bool field() { return true; } class exception : public z3_exception { char const * msg() const override { return "multi-precision floating point (mpff) exception"; } }; class overflow_exception : public exception { char const * msg() const override { return "multi-precision floating point (mpff) overflow"; } }; class div0_exception : public exception { char const * msg() const override { return "multi-precision floating point (mpff) division by zero"; } }; mpff_manager(unsigned prec = 2, unsigned initial_capacity = 1024); ~mpff_manager(); void round_to_plus_inf() { m_to_plus_inf = true; } void round_to_minus_inf() { m_to_plus_inf = false; } void set_rounding(bool to_plus_inf) { m_to_plus_inf = to_plus_inf; } bool rounding_to_plus_inf() const { return m_to_plus_inf; } /** \brief Return the exponent of n. */ static int exponent(mpff const & n) { return n.m_exponent; } /** \brief Update the exponent of n. \remark It is a NOOP if n is zero. */ void set_exponent(mpff & n, int exp) { if (is_zero(n)) return; n.m_exponent = exp; SASSERT(check(n)); } /** \brief Return the significand as a mpz numeral. */ void significand(mpff const & n, unsynch_mpz_manager & m, mpz & r); #ifndef SINGLE_THREAD void significand(mpff const & n, synch_mpz_manager & m, mpz & r); #endif /** \brief Return true if n is negative */ static bool sign(mpff const & n) { return is_neg(n); } /** \brief Set n to zero. */ void reset(mpff & n); /** \brief Return true if n is an integer. */ bool is_int(mpff const & n) const; /** \brief Return true if n is zero. */ static bool is_zero(mpff const & n) { return n.m_sig_idx == 0; } /** \brief Return true if n is positive. */ static bool is_pos(mpff const & n) { return n.m_sign == 0 && !is_zero(n); } /** \brief Return true if n is negative. */ static bool is_neg(mpff const & n) { return n.m_sign != 0; } /** \brief Return true if n is non positive. */ static bool is_nonpos(mpff const & n) { return !is_pos(n); } /** \brief Return true if n is non negative. */ static bool is_nonneg(mpff const & n) { return !is_neg(n); } /** \brief Return true if the absolute value of n is 1. */ bool is_abs_one(mpff const & n) const; /** \brief Return true if n is one. */ bool is_one(mpff const & n) const { return is_pos(n) && is_abs_one(n); } /** \brief Return true if n is minus one. */ bool is_minus_one(mpff const & n) const { return is_neg(n) && is_abs_one(n); } /** \brief Return true if n is two. */ bool is_two(mpff const & n) const; /** \brief Return true if \c a is the smallest representable negative number. */ bool is_minus_epsilon(mpff const & a) const; /** \brief Return true if \c a is the smallest representable positive number. */ bool is_plus_epsilon(mpff const & a) const; /** \brief Return true if \c a is an integer and fits in an int64_t machine integer. */ bool is_int64(mpff const & a) const; /** \brief Return true if \c a is a non-negative integer and fits in an int64_t machine integer. */ bool is_uint64(mpff const & a) const; /** \brief Delete the resources associated with n. */ void del(mpff & n); /** \brief a <- -a */ static void neg(mpff & a) { if (!is_zero(a)) a.m_sign = !a.m_sign; } /** \brief a <- |a| */ static void abs(mpff & a) { a.m_sign = 0; } static void swap(mpff & a, mpff & b) { a.swap(b); } /** \brief c <- a + b */ void add(mpff const & a, mpff const & b, mpff & c); /** \brief c <- a - b */ void sub(mpff const & a, mpff const & b, mpff & c); /** \brief a <- a + 1 */ void inc(mpff & a) { add(a, m_one, a); } /** \brief a <- a - 1 */ void dec(mpff & a) { sub(a, m_one, a); } /** \brief c <- a * b */ void mul(mpff const & a, mpff const & b, mpff & c); /** \brief c <- a / b \pre !is_zero(b) */ void div(mpff const & a, mpff const & b, mpff & c); /** \brief a <- 1/a \pre !is_zero(a); */ void inv(mpff & a) { div(m_one, a, a); } void inv(mpff const & a, mpff & b) { set(b, a); inv(b); } /** \brief b <- a^k */ void power(mpff const & a, unsigned k, mpff & b); /** \brief Return true if \c a is a power of 2. That is, a is equal to 2^k for some k >= 0. */ bool is_power_of_two(mpff const & a, unsigned & k) const; bool is_power_of_two(mpff const & a) const; bool eq(mpff const & a, mpff const & b) const; bool neq(mpff const & a, mpff const & b) const { return !eq(a, b); } bool lt(mpff const & a, mpff const & b) const; bool gt(mpff const & a, mpff const & b) const { return lt(b, a); } bool le(mpff const & a, mpff const & b) const { return !lt(b, a); } bool ge(mpff const & a, mpff const & b) const { return !lt(a, b); } void set(mpff & n, int v); void set(mpff & n, unsigned v); void set(mpff & n, int64_t v); void set(mpff & n, uint64_t v); void set(mpff & n, int num, unsigned den); void set(mpff & n, int64_t num, uint64_t den); void set(mpff & n, mpff const & v); void set(mpff & n, unsynch_mpz_manager & m, mpz const & v); void set(mpff & n, unsynch_mpq_manager & m, mpq const & v); #ifndef SINGLE_THREAD void set(mpff & n, synch_mpq_manager & m, mpq const & v); void set(mpff & n, synch_mpz_manager & m, mpz const & v); #endif void set_plus_epsilon(mpff & n); void set_minus_epsilon(mpff & n); void set_max(mpff & n); void set_min(mpff & n); /** \brief n <- floor(n) */ void floor(mpff & n); void floor(mpff const & n, mpff & o) { set(o, n); floor(o); } /** \brief n <- ceil(n) */ void ceil(mpff & n); void ceil(mpff const & n, mpff & o) { set(o, n); ceil(o); } /** \brief Update \c a to the next representable float. Throws an exception if \c a is the maximal representable float. */ void next(mpff & a); /** \brief Update \c a to the previous representable float. Throws an exception if \c a is the minimal representable float. */ void prev(mpff & a); /** \brief Convert n into a mpz numeral. \pre is_int(n) \remark if exponent(n) is too big, we may run out of memory. */ void to_mpz(mpff const & n, unsynch_mpz_manager & m, mpz & t); #ifndef SINGLE_THREAD /** \brief Convert n into a mpz numeral. \pre is_int(n) \remark if exponent(n) is too big, we may run out of memory. */ void to_mpz(mpff const & n, synch_mpz_manager & m, mpz & t); #endif /** \brief Convert n into a mpq numeral. \remark if exponent(n) is too big, we may run out of memory. */ void to_mpq(mpff const & n, unsynch_mpq_manager & m, mpq & t); #ifndef SINGLE_THREAD /** \brief Convert n into a mpq numeral. \remark if exponent(n) is too big, we may run out of memory. */ void to_mpq(mpff const & n, synch_mpq_manager & m, mpq & t); #endif /** \brief Return n as an int64. \pre is_int64(n) */ int64_t get_int64(mpff const & n) const; /** \brief Return n as an uint64. \pre is_uint64(n) */ uint64_t get_uint64(mpff const & n) const; /** \brief Return the biggest k s.t. 2^k <= a. \remark Return 0 if a is not positive. */ unsigned prev_power_of_two(mpff const & a); void display_raw(std::ostream & out, mpff const & n) const; void display(std::ostream & out, mpff const & n) const; void display_pp(std::ostream & out, mpff const & n) const { display(out, n); } void display_decimal(std::ostream & out, mpff const & n, unsigned prec=32, unsigned max_power=128); void display_smt2(std::ostream & out, mpff const & n, bool decimal=true) const; std::string to_string(mpff const & a) const; std::string to_rational_string(mpff const & a) const; bool check(mpff const & n) const; }; typedef _scoped_numeral scoped_mpff; typedef _scoped_numeral_vector scoped_mpff_vector; #endif z3-z3-4.8.7/src/util/mpfx.cpp000066400000000000000000000601131356505360400156520ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: mpfx.h Abstract: Multi precision fixed point numbers. Author: Leonardo de Moura (leonardo) 2012-09-19 Revision History: --*/ #include #include #include "util/mpfx.h" #include "util/mpn.h" #include "util/mpz.h" #include "util/mpq.h" #include "util/bit_util.h" #include "util/trace.h" mpfx_manager::mpfx_manager(unsigned int_sz, unsigned frac_sz, unsigned initial_capacity) { SASSERT(initial_capacity > 0); SASSERT(int_sz > 0); SASSERT(frac_sz > 0); m_int_part_sz = int_sz; m_frac_part_sz = frac_sz; m_total_sz = m_int_part_sz + m_frac_part_sz; m_words.resize(initial_capacity * m_total_sz, 0); m_capacity = initial_capacity; m_to_plus_inf = false; m_buffer0.resize(2*m_total_sz, 0); m_buffer1.resize(2*m_total_sz, 0); m_buffer2.resize(2*m_total_sz, 0); VERIFY(m_id_gen.mk() == 0); set(m_one, 1); } mpfx_manager::~mpfx_manager() { del(m_one); } void mpfx_manager::expand() { m_capacity = 2*m_capacity; m_words.resize(m_capacity * m_total_sz, 0); } void mpfx_manager::allocate(mpfx & n) { SASSERT(n.m_sig_idx == 0); unsigned sig_idx = m_id_gen.mk(); ensure_capacity(sig_idx); n.m_sig_idx = sig_idx; SASSERT(::is_zero(m_total_sz, words(n))); } unsigned mpfx_manager::sz(unsigned * ws) const { SASSERT(!::is_zero(m_total_sz, ws)); unsigned r = m_total_sz; while (true) { SASSERT(r > 0); --r; if (ws[r] != 0) return r + 1; } } void mpfx_manager::del(mpfx & n) { unsigned sig_idx = n.m_sig_idx; if (sig_idx != 0) { m_id_gen.recycle(sig_idx); unsigned * w = words(n); for (unsigned i = 0; i < m_total_sz; i++) w[i] = 0; } } void mpfx_manager::reset(mpfx & n) { del(n); n.m_sign = false; n.m_sig_idx = 0; SASSERT(check(n)); } bool mpfx_manager::is_int(mpfx const & n) const { unsigned * w = words(n); for (unsigned i = 0; i < m_frac_part_sz; i++) if (w[i] != 0) return false; return true; } bool mpfx_manager::is_abs_one(mpfx const & n) const { unsigned * w = words(n); return is_int(n) && w[m_frac_part_sz] == 1 && ::is_zero(m_int_part_sz - 1, w + m_frac_part_sz + 1); } bool mpfx_manager::is_int64(mpfx const & a) const { if (!is_int(a)) return false; if (is_zero(a) || m_int_part_sz <= 1) return true; unsigned * w = words(a); w += m_frac_part_sz; if (w[1] < 0x80000000u || (w[1] == 0x80000000u && is_neg(a))) { for (unsigned i = 2; i < m_int_part_sz; i++) if (w[i] != 0) return false; return true; } else { return false; } } bool mpfx_manager::is_uint64(mpfx const & a) const { if (!is_int(a) || is_neg(a)) return false; if (is_zero(a) || m_int_part_sz <= 2) return true; unsigned * w = words(a); for (unsigned i = m_frac_part_sz + 2; i < m_total_sz; i++) if (w[i] != 0) return false; return true; } void mpfx_manager::set(mpfx & n, int v) { if (v == 0) { reset(n); } else { if (v < 0) { set(n, static_cast(-v)); n.m_sign = 1; } else { set(n, static_cast(v)); } } SASSERT(get_int64(n) == v); SASSERT(check(n)); } void mpfx_manager::set(mpfx & n, unsigned v) { if (v == 0) { reset(n); } else { allocate_if_needed(n); n.m_sign = 0; unsigned * w = words(n); for (unsigned i = 0; i < m_total_sz; i++) w[i] = 0; w[m_frac_part_sz] = v; } SASSERT(is_int(n)); SASSERT(get_uint64(n) == v); SASSERT(check(n)); } void mpfx_manager::set(mpfx & n, int64_t v) { if (m_int_part_sz == 1) { if (v < -static_cast(static_cast(UINT_MAX)) || v > static_cast(static_cast(UINT_MAX))) throw overflow_exception(); } if (v == 0) { reset(n); } else { if (v < 0) { set(n, static_cast(-v)); n.m_sign = 1; } else { set(n, static_cast(v)); } } SASSERT(is_int(n)); SASSERT(get_int64(n) == v); SASSERT(check(n)); } void mpfx_manager::set(mpfx & n, uint64_t v) { if (m_int_part_sz == 1) { if (v > static_cast(UINT_MAX)) throw overflow_exception(); } if (v == 0) { reset(n); } else { allocate_if_needed(n); n.m_sign = 0; unsigned * w = words(n); uint64_t * _vp = &v; unsigned * _v = nullptr; memcpy(&_v, &_vp, sizeof(unsigned*)); for (unsigned i = 0; i < m_total_sz; i++) w[i] = 0; w[m_frac_part_sz] = _v[0]; if (m_int_part_sz == 1) { SASSERT(_v[1] == 0); } else { w[m_frac_part_sz+1] = _v[1]; } } SASSERT(is_int(n)); SASSERT(get_uint64(n) == v); SASSERT(check(n)); } void mpfx_manager::set(mpfx & n, int num, unsigned den) { scoped_mpfx a(*this), b(*this); set(a, num); set(b, den); div(a, b, n); SASSERT(check(n)); } void mpfx_manager::set(mpfx & n, int64_t num, uint64_t den) { scoped_mpfx a(*this), b(*this); set(a, num); set(b, den); div(a, b, n); SASSERT(check(n)); } void mpfx_manager::set(mpfx & n, mpfx const & v) { if (is_zero(v)) { reset(n); return; } allocate_if_needed(n); n.m_sign = v.m_sign; unsigned * w1 = words(n); unsigned * w2 = words(v); for (unsigned i = 0; i < m_total_sz; i++) w1[i] = w2[i]; SASSERT(check(n)); } template void mpfx_manager::set_core(mpfx & n, mpz_manager & m, mpz const & v) { if (m.is_zero(v)) { reset(n); } else { m_tmp_digits.reset(); allocate_if_needed(n); n.m_sign = m.decompose(v, m_tmp_digits); unsigned sz = m_tmp_digits.size(); if (sz > m_int_part_sz) throw overflow_exception(); unsigned * w = words(n); for (unsigned i = 0; i < m_frac_part_sz; i++) w[i] = 0; ::copy(sz, m_tmp_digits.c_ptr(), m_int_part_sz, w + m_frac_part_sz); } SASSERT(check(n)); } void mpfx_manager::set(mpfx & n, unsynch_mpz_manager & m, mpz const & v) { set_core(n, m, v); } #ifndef SINGLE_THREAD void mpfx_manager::set(mpfx & n, synch_mpz_manager & m, mpz const & v) { set_core(n, m, v); } #endif template void mpfx_manager::set_core(mpfx & n, mpq_manager & m, mpq const & v) { if (m.is_int(v)) { set_core(n, m, v.numerator()); } else { allocate_if_needed(n); _scoped_numeral > tmp(m); n.m_sign = is_neg(n); m.mul2k(v.numerator(), 8 * sizeof(unsigned) * m_frac_part_sz, tmp); m.abs(tmp); if ((n.m_sign == 1) != m_to_plus_inf && !m.divides(v.denominator(), tmp)) { m.div(tmp, v.denominator(), tmp); m.inc(tmp); } else { m.div(tmp, v.denominator(), tmp); } m_tmp_digits.reset(); m.decompose(tmp, m_tmp_digits); unsigned sz = m_tmp_digits.size(); if (sz > m_total_sz) throw overflow_exception(); unsigned * w = words(n); ::copy(sz, m_tmp_digits.c_ptr(), m_total_sz, w); } SASSERT(check(n)); } void mpfx_manager::set(mpfx & n, unsynch_mpq_manager & m, mpq const & v) { set_core(n, m, v); } #ifndef SINGLE_THREAD void mpfx_manager::set(mpfx & n, synch_mpq_manager & m, mpq const & v) { set_core(n, m, v); } #endif bool mpfx_manager::eq(mpfx const & a, mpfx const & b) const { if (is_zero(a) && is_zero(b)) return true; if (is_zero(a) || is_zero(b)) return false; if (a.m_sign != b.m_sign) return false; unsigned * w1 = words(a); unsigned * w2 = words(b); for (unsigned i = 0; i < m_total_sz; i++) if (w1[i] != w2[i]) return false; return true; } bool mpfx_manager::lt(mpfx const & a, mpfx const & b) const { STRACE("mpfx_trace", tout << "[mpfx] ("; display(tout, a); tout << " < "; display(tout, b); tout << ") == ";); bool r; if (is_zero(a)) { r = !is_zero(b) && !is_neg(b); } else if (is_zero(b)) { r = is_neg(a); } else { SASSERT(!is_zero(a)); SASSERT(!is_zero(b)); if (is_neg(a)) { r = is_pos(b) || ::lt(m_total_sz, words(b), words(a)); } else { SASSERT(is_pos(a)); r = is_pos(b) && ::lt(m_total_sz, words(a), words(b)); } } STRACE("mpfx_trace", tout << "(" << r << " == 1)\n";); return r; } void mpfx_manager::add_sub(bool is_sub, mpfx const & a, mpfx const & b, mpfx & c) { if (is_zero(a)) { set(c, b); if (is_sub) neg(c); return; } if (is_zero(b)) { set(c, a); return; } TRACE("mpfx", tout << (is_sub ? "sub" : "add") << "("; display(tout, a); tout << ", "; display(tout, b); tout << ")\n";); allocate_if_needed(c); bool sgn_a = a.m_sign; bool sgn_b = b.m_sign; unsigned * w_a = words(a); unsigned * w_b = words(b); if (is_sub) sgn_b = !sgn_b; // Compute c unsigned * w_c = words(c); if (sgn_a == sgn_b) { c.m_sign = sgn_a; if (!::add(m_total_sz, w_a, w_b, w_c)) throw overflow_exception(); } else { unsigned borrow; SASSERT(sgn_a != sgn_b); if (::lt(m_total_sz, w_a, w_b)) { c.m_sign = sgn_b; m_mpn_manager.sub(w_b, m_total_sz, w_a, m_total_sz, w_c, &borrow); SASSERT(!::is_zero(m_total_sz, w_c)); } else { c.m_sign = sgn_a; m_mpn_manager.sub(w_a, m_total_sz, w_b, m_total_sz, w_c, &borrow); if (::is_zero(m_total_sz, w_c)) reset(c); } SASSERT(borrow == 0); } TRACE("mpfx", tout << "result: "; display(tout, c); tout << "\n";); SASSERT(check(c)); } void mpfx_manager::add(mpfx const & a, mpfx const & b, mpfx & c) { STRACE("mpfx_trace", tout << "[mpfx] "; display(tout, a); tout << " + "; display(tout, b); tout << " == ";); add_sub(false, a, b, c); STRACE("mpfx_trace", display(tout, c); tout << "\n";); } void mpfx_manager::sub(mpfx const & a, mpfx const & b, mpfx & c) { STRACE("mpfx_trace", tout << "[mpfx] "; display(tout, a); tout << " - "; display(tout, b); tout << " == ";); add_sub(true, a, b, c); STRACE("mpfx_trace", display(tout, c); tout << "\n";); } void mpfx_manager::mul(mpfx const & a, mpfx const & b, mpfx & c) { STRACE("mpfx_trace", tout << "[mpfx] ("; display(tout, a); tout << ") * ("; display(tout, b); tout << ") " << (m_to_plus_inf ? "<=" : ">=") << " ";); if (is_zero(a) || is_zero(b)) { reset(c); } else { allocate_if_needed(c); c.m_sign = a.m_sign ^ b.m_sign; unsigned * r = m_buffer0.c_ptr(); m_mpn_manager.mul(words(a), m_total_sz, words(b), m_total_sz, r); // round result unsigned * _r = r + m_frac_part_sz; if ((c.m_sign == 1) != m_to_plus_inf && !::is_zero(m_frac_part_sz, r)) { if (!::inc(m_total_sz, _r)) throw overflow_exception(); } // check for overflows if (!::is_zero(m_int_part_sz, _r + m_total_sz)) throw overflow_exception(); // copy result to c unsigned * w_c = words(c); for (unsigned i = 0; i < m_total_sz; i++) w_c[i] = _r[i]; } STRACE("mpfx_trace", display(tout, c); tout << "\n";); SASSERT(check(c)); } void mpfx_manager::div(mpfx const & a, mpfx const & b, mpfx & c) { if (is_zero(b)) throw div0_exception(); STRACE("mpfx_trace", tout << "[mpfx] ("; display(tout, a); tout << ") / ("; display(tout, b); tout << ") " << (m_to_plus_inf ? "<=" : ">=") << " ";); if (is_zero(a)) { reset(c); } else { allocate_if_needed(c); c.m_sign = a.m_sign ^ b.m_sign; unsigned * w_a = words(a); unsigned * w_a_shft = m_buffer0.c_ptr(); unsigned a_shft_sz = sz(w_a) + m_frac_part_sz; // copy a to buffer 0, and shift by m_frac_part_sz for (unsigned i = 0; i < m_frac_part_sz; i++) w_a_shft[i] = 0; for (unsigned i = 0; i < m_total_sz; i++) w_a_shft[i+m_frac_part_sz] = w_a[i]; unsigned * w_b = words(b); unsigned b_sz = sz(w_b); unsigned * w_q = m_buffer1.c_ptr(); if (b_sz > a_shft_sz) { if ((c.m_sign == 1) != m_to_plus_inf) set_epsilon(c); else reset(c); } else { unsigned q_sz = a_shft_sz - b_sz + 1; unsigned * w_r = m_buffer2.c_ptr(); unsigned r_sz = b_sz; m_mpn_manager.div(w_a_shft, a_shft_sz, w_b, b_sz, w_q, w_r); for (unsigned i = m_total_sz; i < q_sz; i++) if (w_q[i] != 0) throw overflow_exception(); if (((c.m_sign == 1) != m_to_plus_inf) && !::is_zero(r_sz, w_r)) { // round the result if (!::inc(m_total_sz, w_q)) throw overflow_exception(); } unsigned * w_c = words(c); bool zero_q = true; if (m_total_sz >= q_sz) { unsigned i; for (i = 0; i < q_sz; i++) { if (w_q[i] != 0) zero_q = false; w_c[i] = w_q[i]; } for (; i < m_total_sz; i++) w_c[i] = 0; } else { for (unsigned i = 0; i < m_total_sz; i++) { if (w_q[i] != 0) zero_q = false; w_c[i] = w_q[i]; } } if (zero_q) { if ((c.m_sign == 1) != m_to_plus_inf) set_epsilon(c); else reset(c); } } } STRACE("mpfx_trace", display(tout, c); tout << "\n";); SASSERT(check(c)); } void mpfx_manager::div2k(mpfx & a, unsigned k) { STRACE("mpfx_trace", tout << "[mpfx] ("; display(tout, a); tout << ") / (2^" << k << ") " << (m_to_plus_inf ? "<=" : ">=") << " ";); if (!is_zero(a) && k > 0) { unsigned * w = words(a); bool _inc = ((a.m_sign == 1) != m_to_plus_inf) && has_one_at_first_k_bits(m_total_sz, w, k); shr(m_total_sz, w, k, m_total_sz, w); if (_inc) { VERIFY(::inc(m_total_sz, w)); SASSERT(!::is_zero(m_total_sz, w)); } else if (::is_zero(m_total_sz, w)) { reset(a); } } STRACE("mpfx_trace", display(tout, a); tout << "\n";); SASSERT(check(a)); } void mpfx_manager::set_epsilon(mpfx & n) { unsigned * w = words(n); w[0] = 1; for (unsigned i = 1; i < m_total_sz; i++) w[i] = 0; } void mpfx_manager::set_minus_epsilon(mpfx & n) { set_epsilon(n); n.m_sign = true; SASSERT(check(n)); } void mpfx_manager::set_plus_epsilon(mpfx & n) { set_epsilon(n); n.m_sign = 0; SASSERT(check(n)); } void mpfx_manager::floor(mpfx & n) { STRACE("mpfx_trace", tout << "[mpfx] Floor["; display(tout, n); tout << "] == ";); unsigned * w = words(n); if (is_neg(n)) { bool is_int = true; for (unsigned i = 0; i < m_frac_part_sz; i++) { if (w[i] != 0) { is_int = false; w[i] = 0; } } if (!is_int && !::inc(m_int_part_sz, w + m_frac_part_sz)) throw overflow_exception(); } else { for (unsigned i = 0; i < m_frac_part_sz; i++) w[i] = 0; } if (::is_zero(m_int_part_sz, w + m_frac_part_sz)) reset(n); SASSERT(check(n)); STRACE("mpfx_trace", display(tout, n); tout << "\n";); } void mpfx_manager::ceil(mpfx & n) { STRACE("mpfx_trace", tout << "[mpfx] Ceiling["; display(tout, n); tout << "] == ";); unsigned * w = words(n); if (is_pos(n)) { bool is_int = true; for (unsigned i = 0; i < m_frac_part_sz; i++) { if (w[i] != 0) { is_int = false; w[i] = 0; } } if (!is_int && !::inc(m_int_part_sz, w + m_frac_part_sz)) throw overflow_exception(); } else { for (unsigned i = 0; i < m_frac_part_sz; i++) w[i] = 0; } if (::is_zero(m_int_part_sz, w + m_frac_part_sz)) reset(n); SASSERT(check(n)); STRACE("mpfx_trace", display(tout, n); tout << "\n";); } void mpfx_manager::power(mpfx const & a, unsigned p, mpfx & b) { #ifdef _TRACE scoped_mpfx _a(*this); _a = a; unsigned _p = p; #endif #define SMALL_POWER 8 SASSERT(check(a)); if (is_zero(a)) { SASSERT(p != 0); reset(b); } else if (p == 0) { set(b, 1); } else if (p == 1) { set(b, a); } else if (p == 2) { mul(a, a, b); } else if (p <= SMALL_POWER && &a != &b) { SASSERT(p > 2); --p; set(b, a); while (p > 0) { --p; mul(a, b, b); } } else { unsigned mask = 1; scoped_mpfx pw(*this); set(pw, a); set(b, 1); while (mask <= p) { if (mask & p) mul(b, pw, b); mul(pw, pw, pw); mask = mask << 1; } } STRACE("mpfx_trace", tout << "[mpfx] ("; display(tout, _a); tout << ") ^ " << _p << (m_to_plus_inf ? "<=" : ">="); display(tout, b); tout << "\n";); TRACE("mpfx_power", display_raw(tout, b); tout << "\n";); SASSERT(check(b)); } bool mpfx_manager::is_power_of_two(mpfx const & a, unsigned & k) const { if (!is_int(a) || is_zero(a)) return false; unsigned * w = words(a); unsigned i = m_total_sz; while (true) { SASSERT (i > m_frac_part_sz); --i; if (w[i] != 0) { if (!::is_power_of_two(w[i])) return false; k = (i - m_frac_part_sz) * 8 * sizeof(unsigned) + log2(w[i]); while (i > m_frac_part_sz) { --i; if (w[i] != 0) return false; } return true; } } } bool mpfx_manager::is_power_of_two(mpfx const & a) const { unsigned k; return is_power_of_two(a, k); } int64_t mpfx_manager::get_int64(mpfx const & n) const { SASSERT(is_int64(n)); unsigned * w = words(n); w += m_frac_part_sz; uint64_t r = 0; memcpy(&r, w, sizeof(uint64_t)); if (r == 0x8000000000000000ull) { SASSERT(is_neg(n)); return INT64_MIN; } else { return is_neg(n) ? -static_cast(r) : r; } } uint64_t mpfx_manager::get_uint64(mpfx const & n) const { SASSERT(is_uint64(n)); unsigned * w = words(n); w += m_frac_part_sz; uint64_t r = 0; memcpy(&r, w, sizeof(uint64_t)); return r; } template void mpfx_manager::to_mpz_core(mpfx const & n, mpz_manager & m, mpz & t) { SASSERT(is_int(n)); unsigned * w = words(n); m.set_digits(t, m_int_part_sz, w+m_frac_part_sz); if (is_neg(n)) m.neg(t); } void mpfx_manager::to_mpz(mpfx const & n, unsynch_mpz_manager & m, mpz & t) { to_mpz_core(n, m, t); } #ifndef SINGLE_THREAD void mpfx_manager::to_mpz(mpfx const & n, synch_mpz_manager & m, mpz & t) { to_mpz_core(n, m, t); } #endif template void mpfx_manager::to_mpq_core(mpfx const & n, mpq_manager & m, mpq & t) { _scoped_numeral > a(m), b(m); unsigned * w = words(n); m.set(a, m_total_sz, w); m.set(b, 1); m.mul2k(b, sizeof(unsigned)*8*m_frac_part_sz); m.rat_div(a, b, t); if (is_neg(n)) m.neg(t); } void mpfx_manager::to_mpq(mpfx const & n, unsynch_mpq_manager & m, mpq & t) { to_mpq_core(n, m, t); } #ifndef SINGLE_THREAD void mpfx_manager::to_mpq(mpfx const & n, synch_mpq_manager & m, mpq & t) { to_mpq_core(n, m, t); } #endif void mpfx_manager::display_raw(std::ostream & out, mpfx const & n) const { if (is_neg(n)) out << "-"; unsigned * w = words(n); unsigned i = m_total_sz; while(i > 0) { if (i == m_frac_part_sz) out << "."; --i; out << std::hex << std::setfill('0') << std::setw(2 * sizeof(unsigned)) << w[i]; } } void mpfx_manager::display(std::ostream & out, mpfx const & n) const { if (is_neg(n)) out << "-"; unsigned * w = words(n); unsigned sz = m_total_sz; unsigned shift = UINT_MAX; if (is_int(n)) { w += m_frac_part_sz; sz -= m_frac_part_sz; } else { shift = ntz(m_total_sz, w); if (shift > 0) shr(m_total_sz, w, shift, m_total_sz, w); } sbuffer str_buffer(11*sz, 0); out << m_mpn_manager.to_string(w, sz, str_buffer.begin(), str_buffer.size()); if (!is_int(n)) { SASSERT(shift != UINT_MAX); // reverse effect of shr if (shift > 0) shl(m_total_sz, w, shift, m_total_sz, w); // display denominator as a power of 2 unsigned k = sizeof(unsigned)*8*m_frac_part_sz - shift; out << "/2"; if (k > 1) out << "^" << k; } } void mpfx_manager::display_smt2(std::ostream & out, mpfx const & n) const { if (is_neg(n)) out << "(- "; unsigned * w = words(n); unsigned sz = m_total_sz; if (is_int(n)) { w += m_frac_part_sz; sz -= m_frac_part_sz; } else { out << "(/ "; } sbuffer str_buffer(11*sz, 0); out << m_mpn_manager.to_string(w, sz, str_buffer.begin(), str_buffer.size()); if (!is_int(n)) { out << " "; unsigned * w = m_buffer0.c_ptr(); for (unsigned i = 0; i < m_frac_part_sz; i++) w[i] = 0; w[m_frac_part_sz] = 1; sbuffer str_buffer2(11*(m_frac_part_sz+1), 0); out << m_mpn_manager.to_string(w, m_frac_part_sz + 1, str_buffer2.begin(), str_buffer2.size()); out << ")"; } if (is_neg(n)) out << ")"; } void mpfx_manager::display_decimal(std::ostream & out, mpfx const & n, unsigned prec) const { if (is_neg(n)) out << "-"; unsigned * w = words(n); sbuffer str_buffer(11*m_int_part_sz, 0); out << m_mpn_manager.to_string(w + m_frac_part_sz, m_int_part_sz, str_buffer.begin(), str_buffer.size()); if (!is_int(n)) { out << "."; unsigned * frac = m_buffer0.c_ptr(); ::copy(m_frac_part_sz, w, m_frac_part_sz, frac); unsigned ten = 10; unsigned * n_frac = m_buffer1.c_ptr(); bool frac_is_zero = false; unsigned i = 0; while (!frac_is_zero) { if (i >= prec) { out << "?"; return; } m_mpn_manager.mul(frac, m_frac_part_sz, &ten, 1, n_frac); frac_is_zero = ::is_zero(m_frac_part_sz, n_frac); SASSERT(n_frac[m_frac_part_sz] <= 9); if (!frac_is_zero || n_frac[m_frac_part_sz] != 0) out << n_frac[m_frac_part_sz]; n_frac[m_frac_part_sz] = 0; std::swap(frac, n_frac); i++; } } } std::string mpfx_manager::to_string(mpfx const & a) const { std::ostringstream buffer; display(buffer, a); return buffer.str(); } std::string mpfx_manager::to_rational_string(mpfx const & a) const { return to_string(a); } bool mpfx_manager::check(mpfx const & a) const { SASSERT(!is_zero(a) || a.m_sign == 0); SASSERT(is_zero(a) == ::is_zero(m_total_sz, words(a))); return true; } unsigned mpfx_manager::prev_power_of_two(mpfx const & a) { if (!is_pos(a)) return 0; return m_int_part_sz * sizeof(unsigned) * 8 - nlz(m_int_part_sz, words(a) + m_frac_part_sz) - 1; } z3-z3-4.8.7/src/util/mpfx.h000066400000000000000000000262661356505360400153320ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: mpfx.h Abstract: Multi precision fixed point numbers. Author: Leonardo de Moura (leonardo) 2012-09-19 Revision History: --*/ #ifndef MPFX_H_ #define MPFX_H_ #include "util/id_gen.h" #include "util/util.h" #include "util/vector.h" #include "util/z3_exception.h" #include "util/scoped_numeral.h" #include "util/scoped_numeral_vector.h" #include "util/mpn.h" class mpfx_manager; class mpfx { friend class mpfx_manager; unsigned m_sign:1; unsigned m_sig_idx:31; // position where the data is stored in the mpfx_manager. public: mpfx(): m_sign(0), m_sig_idx(0) { } void swap(mpfx & other) { unsigned sign = m_sign; m_sign = other.m_sign; other.m_sign = sign; unsigned sig_idx = m_sig_idx; m_sig_idx = other.m_sig_idx; other.m_sig_idx = sig_idx; } }; inline void swap(mpfx & m1, mpfx & m2) { m1.swap(m2); } class mpz; class mpq; template class mpz_manager; template class mpq_manager; #ifndef SINGLE_THREAD typedef mpz_manager synch_mpz_manager; typedef mpq_manager synch_mpq_manager; #else typedef mpz_manager synch_mpz_manager; typedef mpq_manager synch_mpq_manager; #endif typedef mpz_manager unsynch_mpz_manager; typedef mpq_manager unsynch_mpq_manager; class mpfx_manager { // Every mpfx numeral from a given mpfx_manager uses the same number of words // to encode the integer and fractional parts. // // The number of words used to encode the integer part may be different from the number of words // used to encode the fractional part. // // There are two rounding modes: towards plus infinity, and towards minus infinity. // // If the result of an operation does not fit in the integer part, then an overflow exception is thrown. // // If the fractional part uses n words, then the error of every operation is less than 1/2^(32*n). // // Machine integer values (int, unsigned, int64, uint64) can be easily converted into mpfx numerals. // // The result of addition and subtraction operations are always precise. Note that overflows will trigger // an exception instead of an incorrect result. // unsigned m_int_part_sz; unsigned m_frac_part_sz; unsigned m_total_sz; //!< == m_int_part_sz + m_frac_part_sz unsigned_vector m_words; //!< Array containing all words unsigned m_capacity; //!< Number of mpfx numerals that can be stored in m_words. bool m_to_plus_inf; //!< If True, then round to plus infinity, otherwise to minus infinity id_gen m_id_gen; unsigned_vector m_buffer0, m_buffer1, m_buffer2; unsigned_vector m_tmp_digits; mpfx m_one; mpn_manager m_mpn_manager; unsigned * words(mpfx const & n) const { return m_words.c_ptr() + (n.m_sig_idx * m_total_sz); } unsigned sz(unsigned * ws) const; void ensure_capacity(unsigned sig_idx) { while (sig_idx >= m_capacity) expand(); } void expand(); void allocate_if_needed(mpfx & n) { if (n.m_sig_idx == 0) allocate(n); } void allocate(mpfx & n); void set_epsilon(mpfx & n); void add_sub(bool is_sub, mpfx const & a, mpfx const & b, mpfx & c); template void set_core(mpfx & n, mpz_manager & m, mpz const & v); template void set_core(mpfx & n, mpq_manager & m, mpq const & v); template void to_mpz_core(mpfx const & n, mpz_manager & m, mpz & t); template void to_mpq_core(mpfx const & n, mpq_manager & m, mpq & t); public: typedef mpfx numeral; static bool precise() { return false; } static bool field() { return true; } class exception : public z3_exception { char const * msg() const override { return "multi-precision fixed point (mpfx) exception"; } }; class overflow_exception : public exception { char const * msg() const override { return "multi-precision fixed point (mpfx) overflow"; } }; class div0_exception : public exception { char const * msg() const override { return "multi-precision fixed point (mpfx) division by zero"; } }; mpfx_manager(unsigned int_sz = 2, unsigned frac_sz = 1, unsigned initial_capacity = 1024); ~mpfx_manager(); void round_to_plus_inf() { m_to_plus_inf = true; } void round_to_minus_inf() { m_to_plus_inf = false; } void set_rounding(bool to_plus_inf) { m_to_plus_inf = to_plus_inf; } bool rounding_to_plus_inf() const { return m_to_plus_inf; } /** \brief Return true if n is negative */ static bool sign(mpfx const & n) { return is_neg(n); } /** \brief Set n to zero. */ void reset(mpfx & n); /** \brief Return true if n is an integer. */ bool is_int(mpfx const & n) const; /** \brief Return true if n is zero. */ static bool is_zero(mpfx const & n) { return n.m_sig_idx == 0; } /** \brief Return true if n is positive. */ static bool is_pos(mpfx const & n) { return n.m_sign == 0 && !is_zero(n); } /** \brief Return true if n is negative. */ static bool is_neg(mpfx const & n) { return n.m_sign != 0; } /** \brief Return true if n is non positive. */ static bool is_nonpos(mpfx const & n) { return !is_pos(n); } /** \brief Return true if n is non negative. */ static bool is_nonneg(mpfx const & n) { return !is_neg(n); } /** \brief Return true if the absolute value of n is 1. */ bool is_abs_one(mpfx const & n) const; /** \brief Return true if n is one. */ bool is_one(mpfx const & n) const { return is_pos(n) && is_abs_one(n); } /** \brief Return true if n is minus one. */ bool is_minus_one(mpfx const & n) const { return is_neg(n) && is_abs_one(n); } /** \brief Return true if \c a is an integer and fits in an \c int64_t machine integer. */ bool is_int64(mpfx const & a) const; /** \brief Return true if \c a is a non-negative integer and fits in an \c int64_t machine integer. */ bool is_uint64(mpfx const & a) const; /** \brief Delete the resources associated with n. */ void del(mpfx & n); /** \brief a <- -a */ static void neg(mpfx & a) { if (!is_zero(a)) a.m_sign = !a.m_sign; } /** \brief a <- |a| */ static void abs(mpfx & a) { a.m_sign = 0; } static void swap(mpfx & a, mpfx & b) { a.swap(b); } /** \brief c <- a + b */ void add(mpfx const & a, mpfx const & b, mpfx & c); /** \brief c <- a - b */ void sub(mpfx const & a, mpfx const & b, mpfx & c); /** \brief a <- a + 1 */ void inc(mpfx & a) { add(a, m_one, a); } /** \brief a <- a - 1 */ void dec(mpfx & a) { sub(a, m_one, a); } /** \brief c <- a * b */ void mul(mpfx const & a, mpfx const & b, mpfx & c); /** \brief c <- a / b \pre !is_zero(b) */ void div(mpfx const & a, mpfx const & b, mpfx & c); /** \brief a <- 1/a \pre !is_zero(a); */ void inv(mpfx & a) { div(m_one, a, a); } void inv(mpfx const & a, mpfx & b) { set(b, a); inv(b); } /** \brief a <- a/2^k */ void div2k(mpfx & a, unsigned k); /** \brief b <- a/2^k */ void div2k(mpfx const & a, unsigned k, mpfx & b) { set(b, a); div2k(b, k); } /** \brief a <- a/2 */ void div2(mpfx & a) { div2k(a, 1); } /** \brief b <- a/2 */ void div2(mpfx const & a, mpfx & b) { div2k(a, 1, b); } /** \brief b <- a^k */ void power(mpfx const & a, unsigned k, mpfx & b); /** \brief Return true if \c a is a power of 2. That is, a is equal to 2^k for some k >= 0. */ bool is_power_of_two(mpfx const & a, unsigned & k) const; bool is_power_of_two(mpfx const & a) const; bool eq(mpfx const & a, mpfx const & b) const; bool neq(mpfx const & a, mpfx const & b) const { return !eq(a, b); } bool lt(mpfx const & a, mpfx const & b) const; bool gt(mpfx const & a, mpfx const & b) const { return lt(b, a); } bool le(mpfx const & a, mpfx const & b) const { return !lt(b, a); } bool ge(mpfx const & a, mpfx const & b) const { return !lt(a, b); } void set(mpfx & n, int v); void set(mpfx & n, unsigned v); void set(mpfx & n, int64_t v); void set(mpfx & n, uint64_t v); void set(mpfx & n, int num, unsigned den); void set(mpfx & n, int64_t num, uint64_t den); void set(mpfx & n, mpfx const & v); void set(mpfx & n, unsynch_mpz_manager & m, mpz const & v); void set(mpfx & n, unsynch_mpq_manager & m, mpq const & v); #ifndef SINGLE_THREAD void set(mpfx & n, synch_mpz_manager & m, mpz const & v); void set(mpfx & n, synch_mpq_manager & m, mpq const & v); #endif /** \brief Set n to the smallest representable numeral greater than zero. */ void set_plus_epsilon(mpfx & n); /** \brief Set n to the greatest representable numeral less than zero. */ void set_minus_epsilon(mpfx & n); /** \brief n <- floor(n) */ void floor(mpfx & n); void floor(mpfx const & n, mpfx & o) { set(o, n); floor(o); } /** \brief n <- ceil(n) */ void ceil(mpfx & n); void ceil(mpfx const & n, mpfx & o) { set(o, n); ceil(o); } /** \brief Return n as an int64. \pre is_int64(n) */ int64_t get_int64(mpfx const & n) const; /** \brief Return n as an uint64. \pre is_uint64(n) */ uint64_t get_uint64(mpfx const & n) const; /** \brief Convert n into a mpz numeral. \pre is_int(n) */ void to_mpz(mpfx const & n, unsynch_mpz_manager & m, mpz & t); #ifndef SINGLE_THREAD /** \brief Convert n into a mpz numeral. \pre is_int(n) */ void to_mpz(mpfx const & n, synch_mpz_manager & m, mpz & t); #endif /** \brief Convert n into a mpq numeral. */ void to_mpq(mpfx const & n, unsynch_mpq_manager & m, mpq & t); #ifndef SINGLE_THREAD /** \brief Convert n into a mpq numeral. */ void to_mpq(mpfx const & n, synch_mpq_manager & m, mpq & t); #endif /** \brief Return the biggest k s.t. 2^k <= a. \remark Return 0 if a is not positive. */ unsigned prev_power_of_two(mpfx const & a); void display(std::ostream & out, mpfx const & n) const; void display_pp(std::ostream & out, mpfx const & n) const { display(out, n); } void display_smt2(std::ostream & out, mpfx const & n) const; void display_decimal(std::ostream & out, mpfx const & n, unsigned prec = UINT_MAX) const; void display_raw(std::ostream & out, mpfx const & n) const; std::string to_string(mpfx const & a) const; std::string to_rational_string(mpfx const & a) const; bool check(mpfx const & a) const; }; typedef _scoped_numeral scoped_mpfx; typedef _scoped_numeral_vector scoped_mpfx_vector; #endif z3-z3-4.8.7/src/util/mpn.cpp000066400000000000000000000330741356505360400155000ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: mpn.cpp Abstract: Multi Precision Natural Numbers Author: Christoph Wintersteiger (cwinter) 2011-11-16. Revision History: --*/ #include "util/debug.h" #include "util/trace.h" #include "util/buffer.h" #include "util/mpn.h" #define max(a,b) (((a) > (b)) ? (a) : (b)) typedef uint64_t mpn_double_digit; static_assert(sizeof(mpn_double_digit) == 2 * sizeof(mpn_digit), "size alignment"); const mpn_digit mpn_manager::zero = 0; mpn_manager::mpn_manager() { } mpn_manager::~mpn_manager() { } int mpn_manager::compare(mpn_digit const * a, size_t const lnga, mpn_digit const * b, size_t const lngb) const { int res = 0; trace(a, lnga); size_t j = max(lnga, lngb) - 1; for (; j != (size_t)-1 && res == 0; j--) { mpn_digit const & u_j = (j < lnga) ? a[j] : zero; mpn_digit const & v_j = (j < lngb) ? b[j] : zero; if (u_j > v_j) res = 1; else if (u_j < v_j) res = -1; } TRACE("mpn", tout << ((res == 1) ? " > " : (res == -1) ? " < " : " == "); ); trace_nl(b, lngb); return res; } bool mpn_manager::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, size_t * plngc) const { trace(a, lnga, b, lngb, "+"); // Essentially Knuth's Algorithm A size_t len = max(lnga, lngb); SASSERT(lngc_alloc == len+1 && len > 0); mpn_digit k = 0; mpn_digit r; bool c1, c2; for (size_t j = 0; j < len; j++) { mpn_digit const & u_j = (j < lnga) ? a[j] : zero; mpn_digit const & v_j = (j < lngb) ? b[j] : zero; r = u_j + v_j; c1 = r < u_j; c[j] = r + k; c2 = c[j] < r; k = c1 | c2; } c[len] = k; size_t &os = *plngc; for (os = len+1; os > 1 && c[os-1] == 0; ) os--; SASSERT(os > 0 && os <= len+1); trace_nl(c, os); return true; // return k != 0? } bool mpn_manager::sub(mpn_digit const * a, size_t const lnga, mpn_digit const * b, size_t const lngb, mpn_digit * c, mpn_digit * pborrow) const { trace(a, lnga, b, lngb, "-"); // Essentially Knuth's Algorithm S size_t len = max(lnga, lngb); mpn_digit & k = *pborrow; k = 0; mpn_digit r; bool c1, c2; for (size_t j = 0; j < len; j++) { mpn_digit const & u_j = (j < lnga) ? a[j] : zero; mpn_digit const & v_j = (j < lngb) ? b[j] : zero; r = u_j - v_j; c1 = r > u_j; c[j] = r - k; c2 = c[j] > r; k = c1 | c2; } trace_nl(c, lnga); return true; // return k != 0? } bool mpn_manager::mul(mpn_digit const * a, size_t const lnga, mpn_digit const * b, size_t const lngb, mpn_digit * c) const { trace(a, lnga, b, lngb, "*"); // Essentially Knuth's Algorithm M. // Perhaps implement a more efficient version, see e.g., Knuth, Section 4.3.3. size_t i; mpn_digit k; #define DIGIT_BITS (sizeof(mpn_digit)*8) #define HALF_BITS (sizeof(mpn_digit)*4) for (unsigned i = 0; i < lnga; i++) c[i] = 0; for (size_t j = 0; j < lngb; j++) { mpn_digit const & v_j = b[j]; if (v_j == 0) { // This branch may be omitted according to Knuth. c[j+lnga] = 0; } else { k = 0; for (i = 0; i < lnga; i++) { mpn_digit const & u_i = a[i]; mpn_double_digit t; t = ((mpn_double_digit)u_i * (mpn_double_digit)v_j) + (mpn_double_digit) c[i+j] + (mpn_double_digit) k; c[i+j] = (t << DIGIT_BITS) >> DIGIT_BITS; k = t >> DIGIT_BITS; } c[j+lnga] = k; } } trace_nl(c, lnga+lngb); return true; } #define MASK_FIRST (~((mpn_digit)(-1) >> 1)) #define FIRST_BITS(N, X) ((X) >> (DIGIT_BITS-(N))) #define LAST_BITS(N, X) (((X) << (DIGIT_BITS-(N))) >> (DIGIT_BITS-(N))) #define BASE ((mpn_double_digit)0x01 << DIGIT_BITS) bool mpn_manager::div(mpn_digit const * numer, size_t const lnum, mpn_digit const * denom, size_t const lden, mpn_digit * quot, mpn_digit * rem) { MPN_BEGIN_CRITICAL(); trace(numer, lnum, denom, lden, "/"); bool res = false; if (lnum < lden) { for (size_t i = 0; i < (lnum-lden+1); i++) quot[i] = 0; for (size_t i = 0; i < lden; i++) rem[i] = (i < lnum) ? numer[i] : 0; MPN_END_CRITICAL(); return false; } bool all_zero = true; for (size_t i = 0; i < lden && all_zero; i++) if (denom[i] != zero) all_zero = false; if (all_zero) { UNREACHABLE(); MPN_END_CRITICAL(); return res; } SASSERT(denom[lden-1] != 0); if (lnum == 1 && lden == 1) { *quot = numer[0] / denom[0]; *rem = numer[0] % denom[0]; } else if (lnum < lden || (lnum == lden && numer[lnum-1] < denom[lden-1])) { *quot = 0; for (size_t i = 0; i < lden; i++) rem[i] = (i < lnum) ? numer[i] : 0; } else { size_t d = div_normalize(numer, lnum, denom, lden, u, v); if (lden == 1) res = div_1(u, v[0], quot); else res = div_n(u, v, quot, rem, t_ms, t_ab); div_unnormalize(u, v, d, rem); } // TRACE("mpn_dbg", display_raw(tout, quot, lnum - lden + 1); tout << ", "; // display_raw(tout, rem, lden); tout << std::endl; ); trace_nl(quot, lnum-lden+1); trace(numer, lnum, denom, lden, "%"); trace_nl(rem, lden); #ifdef Z3DEBUG mpn_sbuffer temp(lnum+1, 0); mul(quot, lnum-lden+1, denom, lden, temp.c_ptr()); size_t real_size; add(temp.c_ptr(), lnum, rem, lden, temp.c_ptr(), lnum+1, &real_size); bool ok = true; for (size_t i = 0; i < lnum && ok; i++) if (temp[i] != numer[i]) ok = false; if (temp[lnum] != 0) ok = false; CTRACE("mpn_dbg", !ok, tout << "DIV BUG: quot * denom + rem = "; display_raw(tout, temp.c_ptr(), lnum+1); tout << std::endl; ); SASSERT(ok); #endif MPN_END_CRITICAL(); return res; } size_t mpn_manager::div_normalize(mpn_digit const * numer, size_t const lnum, mpn_digit const * denom, size_t const lden, mpn_sbuffer & n_numer, mpn_sbuffer & n_denom) const { size_t d = 0; while (lden > 0 && ((denom[lden-1] << d) & MASK_FIRST) == 0) d++; SASSERT(d < DIGIT_BITS); n_numer.resize(lnum+1); n_denom.resize(lden); if (d == 0) { n_numer[lnum] = 0; for (size_t i = 0; i < lnum; i++) n_numer[i] = numer[i]; for (size_t i = 0; i < lden; i++) n_denom[i] = denom[i]; } else if (lnum != 0) { SASSERT(lden > 0); mpn_digit q = FIRST_BITS(d, numer[lnum-1]); n_numer[lnum] = q; for (size_t i = lnum-1; i > 0; i--) n_numer[i] = (numer[i] << d) | FIRST_BITS(d, numer[i-1]); n_numer[0] = numer[0] << d; for (size_t i = lden-1; i > 0; i--) n_denom[i] = denom[i] << d | FIRST_BITS(d, denom[i-1]); n_denom[0] = denom[0] << d; } else { d = 0; } TRACE("mpn_norm", tout << "Normalized: n_numer="; display_raw(tout, n_numer.c_ptr(), n_numer.size()); tout << " n_denom="; display_raw(tout, n_denom.c_ptr(), n_denom.size()); tout << std::endl; ); return d; } void mpn_manager::div_unnormalize(mpn_sbuffer & numer, mpn_sbuffer & denom, size_t const d, mpn_digit * rem) const { if (d == 0) { for (size_t i = 0; i < denom.size(); i++) rem[i] = numer[i]; } else { for (size_t i = 0; i < denom.size()-1; i++) rem[i] = numer[i] >> d | (LAST_BITS(d, numer[i+1]) << (DIGIT_BITS-d)); rem[denom.size()-1] = numer[denom.size()-1] >> d; } } bool mpn_manager::div_1(mpn_sbuffer & numer, mpn_digit const denom, mpn_digit * quot) const { mpn_double_digit q_hat, temp, ms; mpn_digit borrow; for (size_t j = numer.size()-1; j > 0; j--) { temp = (((mpn_double_digit)numer[j]) << DIGIT_BITS) | ((mpn_double_digit)numer[j-1]); q_hat = temp / (mpn_double_digit) denom; if (q_hat >= BASE) { UNREACHABLE(); // is this reachable with normalized v? } SASSERT(q_hat < BASE); ms = temp - (q_hat * (mpn_double_digit) denom); borrow = ms > temp; numer[j-1] = (mpn_digit) ms; numer[j] = ms >> DIGIT_BITS; quot[j-1] = (mpn_digit) q_hat; if (borrow) { quot[j-1]--; numer[j] = numer[j-1] + denom; } TRACE("mpn_div1", mpn_double_digit r_hat = temp % (mpn_double_digit) denom; tout << "j=" << j << " q_hat=" << q_hat << " r_hat=" << r_hat; tout << " ms=" << ms; tout << " new numer="; display_raw(tout, numer.c_ptr(), numer.size()); tout << " borrow=" << borrow; tout << std::endl; ); } return true; // return rem != 0? } bool mpn_manager::div_n(mpn_sbuffer & numer, mpn_sbuffer const & denom, mpn_digit * quot, mpn_digit * rem, mpn_sbuffer & ms, mpn_sbuffer & ab) const { SASSERT(denom.size() > 1); // This is essentially Knuth's Algorithm D. size_t m = numer.size() - denom.size(); size_t n = denom.size(); SASSERT(numer.size() == m+n); ms.resize(n+1); mpn_double_digit q_hat, temp, r_hat; mpn_digit borrow; for (size_t j = m-1; j != (size_t)-1; j--) { temp = (((mpn_double_digit)numer[j+n]) << DIGIT_BITS) | ((mpn_double_digit)numer[j+n-1]); q_hat = temp / (mpn_double_digit) denom[n-1]; r_hat = temp % (mpn_double_digit) denom[n-1]; recheck: if (q_hat >= BASE || ((q_hat * denom[n-2]) > ((r_hat << DIGIT_BITS) + numer[j+n-2]))) { q_hat--; r_hat += denom[n-1]; if (r_hat < BASE) goto recheck; } SASSERT(q_hat < BASE); // Replace numer[j+n]...numer[j] with // numer[j+n]...numer[j] - q * (denom[n-1]...denom[0]) mpn_digit q_hat_small = (mpn_digit)q_hat; mul(&q_hat_small, 1, denom.c_ptr(), n, ms.c_ptr()); sub(&numer[j], n+1, ms.c_ptr(), n+1, &numer[j], &borrow); quot[j] = q_hat_small; if (borrow) { quot[j]--; ab.resize(n+2); size_t real_size; add(denom.c_ptr(), n, &numer[j], n+1, ab.c_ptr(), n+2, &real_size); for (size_t i = 0; i < n+1; i++) numer[j+i] = ab[i]; } TRACE("mpn_div", tout << "q_hat=" << q_hat << " r_hat=" << r_hat; tout << " ms="; display_raw(tout, ms.c_ptr(), n); tout << " new numer="; display_raw(tout, numer.c_ptr(), m+n+1); tout << " borrow=" << borrow; tout << std::endl; ); } return true; // return rem != 0? } char * mpn_manager::to_string(mpn_digit const * a, size_t const lng, char * buf, size_t const lbuf) const { SASSERT(buf && lbuf > 0); TRACE("mpn_to_string", tout << "[mpn] to_string "; display_raw(tout, a, lng); tout << " == "; ); if (lng == 1) { #ifdef _WINDOWS sprintf_s(buf, lbuf, "%u", *a); #else snprintf(buf, lbuf, "%u", *a); #endif } else { mpn_sbuffer temp(lng, 0), t_numer(lng+1, 0), t_denom(1, 0); for (unsigned i = 0; i < lng; i++) temp[i] = a[i]; size_t j = 0; mpn_digit rem; mpn_digit ten = 10; while (!temp.empty() && (temp.size() > 1 || temp[0] != 0)) { size_t d = div_normalize(&temp[0], temp.size(), &ten, 1, t_numer, t_denom); div_1(t_numer, t_denom[0], &temp[0]); div_unnormalize(t_numer, t_denom, d, &rem); buf[j++] = '0' + rem; while (!temp.empty() && temp.back() == 0) temp.pop_back(); } buf[j] = 0; j--; size_t mid = (j/2) + ((j % 2) ? 1 : 0); for (size_t i = 0; i < mid; i++) std::swap(buf[i], buf[j-i]); } TRACE("mpn_to_string", tout << buf << std::endl; ); return buf; } void mpn_manager::display_raw(std::ostream & out, mpn_digit const * a, size_t const lng) const { out << "["; for (size_t i = lng-1; i != (size_t)-1; i-- ) { out << a[i]; if (i != 0) out << "|"; } out << "]"; } void mpn_manager::trace(mpn_digit const * a, size_t const lnga, mpn_digit const * b, size_t const lngb, const char * op) const { #ifdef Z3DEBUG char char_buf[4096]; TRACE("mpn", tout << "[mpn] " << to_string(a, lnga, char_buf, sizeof(char_buf)); tout << " " << op << " " << to_string(b, lngb, char_buf, sizeof(char_buf)); tout << " == "; ); #endif } void mpn_manager::trace(mpn_digit const * a, size_t const lnga) const { #ifdef Z3DEBUG char char_buf[4096]; TRACE("mpn", tout << to_string(a, lnga, char_buf, sizeof(char_buf)); ); #endif } void mpn_manager::trace_nl(mpn_digit const * a, size_t const lnga) const { #ifdef Z3DEBUG char char_buf[4096]; TRACE("mpn", tout << to_string(a, lnga, char_buf, sizeof(char_buf)) << std::endl; ); #endif } z3-z3-4.8.7/src/util/mpn.h000066400000000000000000000063761356505360400151520ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: mpn.h Abstract: Multi Precision Natural Numbers Author: Christoph Wintersteiger (cwinter) 2011-11-16. Revision History: --*/ #ifndef MPN_H_ #define MPN_H_ #include #include #include "util/util.h" #include "util/buffer.h" typedef unsigned int mpn_digit; class mpn_manager { #ifndef SINGLE_THREAD std::recursive_mutex m_lock; #define MPN_BEGIN_CRITICAL() m_lock.lock() #define MPN_END_CRITICAL() m_lock.unlock() #else #define MPN_BEGIN_CRITICAL() {} #define MPN_END_CRITICAL() {} #endif public: mpn_manager(); ~mpn_manager(); 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 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 lnga, mpn_digit const * b, size_t lngb, mpn_digit * c, mpn_digit * pborrow) const; 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 lnum, mpn_digit const * denom, size_t lden, mpn_digit * quot, mpn_digit * rem); char * to_string(mpn_digit const * a, size_t lng, char * buf, size_t lbuf) const; private: #if defined(__LP64__) || defined(_WIN64) class mpn_sbuffer : public sbuffer { public: mpn_sbuffer() : sbuffer() {} mpn_sbuffer(size_t nsz, const mpn_digit & elem = 0) : sbuffer(static_cast(nsz), elem) { } void resize(size_t nsz, const mpn_digit & elem = 0) { sbuffer::resize(static_cast(nsz), elem); } mpn_digit & operator[](size_t idx) { return sbuffer::operator[](static_cast(idx)); } const mpn_digit & operator[](size_t idx) const { return sbuffer::operator[](static_cast(idx)); } }; #else typedef sbuffer mpn_sbuffer; #endif 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 lng) const; 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 d, mpn_digit * rem) const; 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 lnga, mpn_digit const * b, size_t lngb, const char * op) const; void trace(mpn_digit const * a, size_t lnga) const; void trace_nl(mpn_digit const * a, size_t lnga) const; }; #endif z3-z3-4.8.7/src/util/mpq.cpp000066400000000000000000000276641356505360400155130ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: mpq.cpp Abstract: Author: Leonardo de Moura (leonardo) 2010-06-21. Revision History: --*/ #include "util/mpq.h" #include "util/warning.h" #include "util/z3_exception.h" template mpq_manager::mpq_manager() { } template mpq_manager::~mpq_manager() { del(m_tmp1); del(m_tmp2); del(m_tmp3); del(m_tmp4); del(m_q_tmp1); del(m_q_tmp2); } template bool mpq_manager::rat_lt(mpq const & a, mpq const & b) { mpz const & na = a.numerator(); mpz const & nb = b.numerator(); int sign_a = this->sign(na); int sign_b = this->sign(nb); if (sign_a < 0) { if (sign_b >= 0) return true; } else if (sign_a == 0) { if (sign_b > 0) return true; SASSERT(sign_b <= 0); return false; } else { SASSERT(sign_a > 0); if (sign_b <= 0) return false; } SASSERT((sign_a > 0 && sign_b > 0) || (sign_a < 0 && sign_b < 0)); mpz const & da = a.denominator(); mpz const & db = b.denominator(); if (SYNCH) { mpq tmp1; mpq tmp2; mul(na, db, tmp1); mul(nb, da, tmp2); bool r = lt(tmp1, tmp2); del(tmp1); del(tmp2); return r; } else { mul(na, db, m_q_tmp1); mul(nb, da, m_q_tmp2); return lt(m_q_tmp1, m_q_tmp2); } } template void mpq_manager::floor(mpq const & a, mpz & f) { if (is_int(a)) { set(f, a.m_num); return; } bool is_neg_num = is_neg(a.m_num); machine_div(a.m_num, a.m_den, f); if (is_neg_num) sub(f, this->mk_z(1), f); } template void mpq_manager::ceil(mpq const & a, mpz & c) { if (is_int(a)) { set(c, a.m_num); return; } bool is_pos_num = is_pos(a.m_num); machine_div(a.m_num, a.m_den, c); if (is_pos_num) add(c, this->mk_z(1), c); } template void mpq_manager::gcd(unsigned sz, mpq const * as, mpq & g) { switch (sz) { case 0: reset(g); return; case 1: set(g, as[0]); abs(g); return; default: break; } gcd(as[0], as[1], g); for (unsigned i = 2; i < sz; i++) { if (is_one(g)) return; gcd(g, as[i], g); } } template std::string mpq_manager::to_string(mpq const & a) const { if (is_int(a)) return to_string(a.m_num); return to_string(a.m_num) + "/" + to_string(a.m_den); } template void mpq_manager::display(std::ostream & out, mpq const & a) const { if (is_int(a)) { display(out, a.m_num); } else { display(out, a.m_num); out << "/"; display(out, a.m_den); } } template void mpq_manager::display_smt2(std::ostream & out, mpq const & a, bool decimal) const { if (is_int(a)) { display_smt2(out, a.m_num, decimal); } else { out << "(/ "; display_smt2(out, a.m_num, decimal); out << " "; display_smt2(out, a.m_den, decimal); out << ")"; } } template void mpq_manager::display_decimal(std::ostream & out, mpq const & a, unsigned prec, bool truncate) { mpz n1, d1, v1; get_numerator(a, n1); get_denominator(a, d1); if (is_neg(a)) { out << "-"; neg(n1); } mpz ten(10); div(n1, d1, v1); display(out, v1); rem(n1, d1, n1); if (is_zero(n1)) goto end; // number is an integer out << "."; for (unsigned i = 0; i < prec; i++) { mul(n1, ten, n1); div(n1, d1, v1); SASSERT(lt(v1, ten)); display(out, v1); rem(n1, d1, n1); if (is_zero(n1)) goto end; // number is precise } if (!truncate) out << "?"; end: del(ten); del(n1); del(d1); del(v1); } template void mpq_manager::set(mpq & a, char const * val) { reset(a.m_num); mpz ten(10); _scoped_numeral > tmp(*this); char const * str = val; bool sign = false; while (str[0] == ' ') ++str; if (str[0] == '-') sign = true; while (str[0] && (str[0] != '/') && (str[0] != '.') && (str[0] != 'e') && (str[0] != 'E')) { if ('0' <= str[0] && str[0] <= '9') { SASSERT(str[0] - '0' <= 9); mul(a.m_num, ten, tmp); add(tmp, this->mk_z(str[0] - '0'), a.m_num); } ++str; } TRACE("mpq_set", tout << "[before] a: " << to_string(a) << "\n";); if (str[0] == '/' || str[0] == '.' || str[0] == 'e' || str[0] == 'E') { bool is_rat = str[0] == '/'; _scoped_numeral > tmp2(*this); set(tmp2, 1); bool has_den = false; if (str[0] == '/' || str[0] == '.') { has_den = true; ++str; reset(a.m_den); while (str[0] && (str[0] != 'e') && (str[0] != 'E')) { if ('0' <= str[0] && str[0] <= '9') { mul(a.m_den, ten, tmp); add(tmp, this->mk_z(str[0] - '0'), a.m_den); if (!is_rat) mul(tmp2, ten, tmp2); } ++str; } } unsigned long long exp = 0; bool exp_sign = false; if (str[0] == 'e' || str[0] == 'E') { if (is_rat) throw default_exception("mixing rational/scientific notation"); ++str; if (str[0] == '-') { exp_sign = true; ++str; } else if (str[0] == '+') { exp_sign = false; ++str; } while (str[0]) { if ('0' <= str[0] && str[0] <= '9') { SASSERT(str[0] - '0' <= 9); exp = (10*exp) + (str[0] - '0'); } else if ('/' == str[0]) { throw default_exception("mixing rational/scientific notation"); } TRACE("mpq_set", tout << "[exp]: " << exp << ", str[0]: " << (str[0] - '0') << std::endl;); ++str; } } if (!is_rat) { // a <- a.m_num + a.m_den/tmp2 if (exp > static_cast(UINT_MAX)) throw default_exception("exponent is too big"); _scoped_numeral > b(*this); if (has_den) { set(b, a.m_den, tmp2); set(a.m_den, 1); add(a, b, a); } if (exp > 0) { _scoped_numeral > _exp(*this); _scoped_numeral > _ten(*this); set(_ten, 10); power(_ten, static_cast(exp), _exp); TRACE("mpq_set", tout << "a: " << to_string(a) << ", exp_sign:" << exp_sign << ", exp: " << exp << " " << to_string(_exp) << std::endl;); if (exp_sign) div(a, _exp, a); else mul(a, _exp, a); } } else { // rational case if (is_zero(a.m_den)) throw default_exception("division by zero"); } } else { reset_denominator(a); } if (sign) neg(a.m_num); normalize(a); } template void mpq_manager::power(mpq const & a, unsigned p, mpq & b) { unsigned mask = 1; mpq power; set(power, a); set(b, 1); while (mask <= p) { if (mask & p) mul(b, power, b); mul(power, power, power); mask = mask << 1; } del(power); } template double mpq_manager::get_double(mpq const & a) const { double n; double d; n = get_double(a.m_num); d = get_double(a.m_den); SASSERT(d > 0.0); return n/d; } template bool mpq_manager::root(mpq const & a, unsigned n, mpq & r) { return root(a.m_num, n, r.m_num) && root(a.m_den, n, r.m_den); } template unsigned mpq_manager::prev_power_of_two(mpq const & a) { _scoped_numeral > _tmp(*this); floor(a, _tmp); return prev_power_of_two(_tmp); } template template void mpq_manager::lin_arith_op(mpq const& a, mpq const& b, mpq& c, mpz& g, mpz& tmp1, mpz& tmp2, mpz& tmp3) { gcd(a.m_den, b.m_den, g); if (is_one(g)) { mul(a.m_num, b.m_den, tmp1); mul(b.m_num, a.m_den, tmp2); if (SUB) sub(tmp1, tmp2, c.m_num); else add(tmp1, tmp2, c.m_num); mul(a.m_den, b.m_den, c.m_den); } else { div(a.m_den, g, tmp3); mul(tmp3, b.m_den, c.m_den); mul(tmp3, b.m_num, tmp2); div(b.m_den, g, tmp3); mul(tmp3, a.m_num, tmp1); if (SUB) sub(tmp1, tmp2, tmp3); else add(tmp1, tmp2, tmp3); gcd(tmp3, g, tmp1); if (is_one(tmp1)) { set(c.m_num, tmp3); } else { div(tmp3, tmp1, c.m_num); div(c.m_den, tmp1, c.m_den); } } } template void mpq_manager::rat_mul(mpq const & a, mpq const & b, mpq & c, mpz& g1, mpz& g2, mpz& tmp1, mpz& tmp2) { #if 1 gcd(a.m_den, b.m_num, g1); gcd(a.m_num, b.m_den, g2); div(a.m_num, g2, tmp1); div(b.m_num, g1, tmp2); mul(tmp1, tmp2, c.m_num); div(b.m_den, g2, tmp1); div(a.m_den, g1, tmp2); mul(tmp1, tmp2, c.m_den); #else mul(a.m_num, b.m_num, c.m_num); mul(a.m_den, b.m_den, c.m_den); normalize(c); #endif } template void mpq_manager::rat_mul(mpz const & a, mpq const & b, mpq & c) { STRACE("rat_mpq", tout << "[mpq] " << to_string(a) << " * " << to_string(b) << " == ";); mul(a, b.m_num, c.m_num); set(c.m_den, b.m_den); normalize(c); STRACE("rat_mpq", tout << to_string(c) << "\n";); } template void mpq_manager::rat_mul(mpq const & a, mpq const & b, mpq & c) { STRACE("rat_mpq", tout << "[mpq] " << to_string(a) << " * " << to_string(b) << " == ";); if (SYNCH) { mpz g1, g2, tmp1, tmp2; rat_mul(a, b, c, g1, g2, tmp1, tmp2); del(g1); del(g2); del(tmp1); del(tmp2); } else { rat_mul(a, b, c, m_tmp1, m_tmp2, m_tmp3, m_tmp4); } STRACE("rat_mpq", tout << to_string(c) << "\n";); } template void mpq_manager::rat_add(mpq const & a, mpq const & b, mpq & c) { STRACE("rat_mpq", tout << "[mpq] " << to_string(a) << " + " << to_string(b) << " == ";); if (SYNCH) { mpz_stack tmp1, tmp2, tmp3, g; lin_arith_op(a, b, c, g, tmp1, tmp2, tmp3); del(tmp1); del(tmp2); del(tmp3); del(g); } else { lin_arith_op(a, b, c, m_tmp1, m_tmp2, m_tmp3, m_tmp4); } STRACE("rat_mpq", tout << to_string(c) << "\n";); } template void mpq_manager::rat_sub(mpq const & a, mpq const & b, mpq & c) { STRACE("rat_mpq", tout << "[mpq] " << to_string(a) << " - " << to_string(b) << " == ";); if (SYNCH) { mpz tmp1, tmp2, tmp3, g; lin_arith_op(a, b, c, g, tmp1, tmp2, tmp3); del(tmp1); del(tmp2); del(tmp3); del(g); } else { lin_arith_op(a, b, c, m_tmp1, m_tmp2, m_tmp3, m_tmp4); } STRACE("rat_mpq", tout << to_string(c) << "\n";); } #ifndef SINGLE_THREAD template class mpq_manager; #endif template class mpq_manager; z3-z3-4.8.7/src/util/mpq.h000066400000000000000000000617541356505360400151560ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: mpq.h Abstract: Author: Leonardo de Moura (leonardo) 2010-06-21. Revision History: --*/ #ifndef MPQ_H_ #define MPQ_H_ #include "util/mpz.h" #include "util/trace.h" class mpq { mpz m_num; mpz m_den; friend class mpq_manager; friend class mpq_manager; mpq & operator=(mpq const & other) { UNREACHABLE(); return *this; } public: mpq(int v):m_num(v), m_den(1) {} mpq():m_den(1) {} mpq(mpq && other) : m_num(std::move(other.m_num)), m_den(std::move(other.m_den)) {} void swap(mpq & other) { m_num.swap(other.m_num); m_den.swap(other.m_den); } mpz const & numerator() const { return m_num; } mpz const & denominator() const { return m_den; } }; inline void swap(mpq & m1, mpq & m2) { m1.swap(m2); } template class mpq_manager : public mpz_manager { mpz m_tmp1; mpz m_tmp2; mpz m_tmp3; mpz m_tmp4; mpq m_q_tmp1; mpq m_q_tmp2; void reset_denominator(mpq & a) { del(a.m_den); a.m_den.m_val = 1; } void normalize(mpq & a) { if (SYNCH) { mpz tmp; gcd(a.m_num, a.m_den, tmp); if (is_one(tmp)) { del(tmp); return; } div(a.m_num, tmp, a.m_num); div(a.m_den, tmp, a.m_den); del(tmp); } else { gcd(a.m_num, a.m_den, m_tmp1); if (is_one(m_tmp1)) return; div(a.m_num, m_tmp1, a.m_num); div(a.m_den, m_tmp1, a.m_den); } } void rat_add(mpq const & a, mpq const & b, mpq & c); void rat_add(mpq const & a, mpz const & b, mpq & c) { STRACE("rat_mpq", tout << "[mpq] " << to_string(a) << " + " << to_string(b) << " == ";); if (SYNCH) { mpz tmp1; mul(b, a.m_den, tmp1); set(c.m_den, a.m_den); add(a.m_num, tmp1, c.m_num); normalize(c); del(tmp1); } else { mul(b, a.m_den, m_tmp1); set(c.m_den, a.m_den); add(a.m_num, m_tmp1, c.m_num); normalize(c); } STRACE("rat_mpq", tout << to_string(c) << "\n";); } void rat_sub(mpq const & a, mpq const & b, mpq & c); void rat_mul(mpq const & a, mpq const & b, mpq & c); void rat_mul(mpz const & a, mpq const & b, mpq & c); bool rat_lt(mpq const & a, mpq const & b); template void lin_arith_op(mpq const& a, mpq const& b, mpq& c, mpz& g, mpz& tmp1, mpz& tmp2, mpz& tmp3); void rat_mul(mpq const & a, mpq const & b, mpq & c, mpz& g1, mpz& g2, mpz& tmp1, mpz& tmp2); public: typedef mpq numeral; typedef mpq rational; typedef mpz integer; static bool precise() { return true; } static bool field() { return true; } mpq_manager(); ~mpq_manager(); void reset(mpz & a) { mpz_manager::reset(a); } void reset(mpq & a) { reset(a.m_num); reset_denominator(a); } static bool is_small(mpz const & a) { return mpz_manager::is_small(a); } static bool is_small(mpq const & a) { return is_small(a.m_num) && is_small(a.m_den); } static mpq mk_q(int v) { return mpq(v); } mpq mk_q(int n, int d) { mpq r; set(r, n, d); return r; } void del(mpz & a) { mpz_manager::del(a); } void del(mpq & a) { del(a.m_num); del(a.m_den); } void get_numerator(mpq const & a, mpz & n) { set(n, a.m_num); } void get_denominator(mpq const & a, mpz & d) { set(d, a.m_den); } void get_numerator(mpq const & a, mpq & n) { get_numerator(a, n.m_num); reset_denominator(n); } void get_denominator(mpq const & a, mpq & d) { get_denominator(a, d.m_num); reset_denominator(d); } void neg(mpz & a) { mpz_manager::neg(a); } void neg(mpq & a) { mpz_manager::neg(a.m_num); } void abs(mpz & a) { mpz_manager::abs(a); } void abs(mpq & a) { mpz_manager::abs(a.m_num); } static int sign(mpz const & a) { return mpz_manager::sign(a); } static int sign(mpq const & a) { return mpz_manager::sign(a.m_num); } static bool is_pos(mpz const & a) { return mpz_manager::is_pos(a); } static bool is_neg(mpz const & a) { return mpz_manager::is_neg(a); } static bool is_zero(mpz const & a) { return mpz_manager::is_zero(a); } static bool is_nonpos(mpz const & a) { return mpz_manager::is_nonpos(a); } static bool is_nonneg(mpz const & a) { return mpz_manager::is_nonneg(a); } static bool is_pos(mpq const & a) { return is_pos(a.m_num); } static bool is_neg(mpq const & a) { return is_neg(a.m_num); } static bool is_zero(mpq const & a) { return is_zero(a.m_num); } static bool is_nonpos(mpq const & a) { return is_nonpos(a.m_num); } static bool is_nonneg(mpq const & a) { return is_nonneg(a.m_num); } static bool is_one(mpz const & a) { return mpz_manager::is_one(a); } static bool is_one(mpq const & a) { return is_one(a.m_num) && is_one(a.m_den); } static bool is_minus_one(mpz const & a) { return mpz_manager::is_minus_one(a); } static bool is_minus_one(mpq const & a) { return is_minus_one(a.m_num) && is_one(a.m_den); } void floor(mpq const & a, mpz & f); void floor(mpq const & a, mpq & f) { floor(a, f.m_num); reset_denominator(f); } void ceil(mpq const & a, mpz & f); void ceil(mpq const & a, mpq & f) { ceil(a, f.m_num); reset_denominator(f); } static bool is_int(mpq const & a) { return is_one(a.m_den); } std::string to_string(mpq const & a) const; std::string to_rational_string(numeral const & a) { return to_string(a); } std::string to_string(mpz const & a) const { return mpz_manager::to_string(a); } void display(std::ostream & out, mpz const & a) const { return mpz_manager::display(out, a); } void display(std::ostream & out, mpq const & a) const; void display_pp(std::ostream & out, mpq const & a) const { display(out, a); } void display_smt2(std::ostream & out, mpz const & a, bool decimal) const { return mpz_manager::display_smt2(out, a, decimal); } void display_smt2(std::ostream & out, mpq const & a, bool decimal) const; void display_decimal(std::ostream & out, mpq const & a, unsigned prec, bool truncate = false); void add(mpz const & a, mpz const & b, mpz & c) { mpz_manager::add(a, b, c); } void add(mpq const & a, mpq const & b, mpq & c) { STRACE("mpq", tout << "[mpq] " << to_string(a) << " + " << to_string(b) << " == ";); if (is_int(a) && is_int(b)) { mpz_manager::add(a.m_num, b.m_num, c.m_num); reset_denominator(c); } else rat_add(a, b, c); STRACE("mpq", tout << to_string(c) << "\n";); } void add(mpq const & a, mpz const & b, mpq & c) { STRACE("mpq", tout << "[mpq] " << to_string(a) << " + " << to_string(b) << " == ";); if (is_int(a)) { mpz_manager::add(a.m_num, b, c.m_num); reset_denominator(c); } else { rat_add(a, b, c); } STRACE("mpq", tout << to_string(c) << "\n";); } void sub(mpz const & a, mpz const & b, mpz & c) { mpz_manager::sub(a, b, c); } void sub(mpq const & a, mpq const & b, mpq & c) { STRACE("mpq", tout << "[mpq] " << to_string(a) << " - " << to_string(b) << " == ";); if (is_int(a) && is_int(b)) { mpz_manager::sub(a.m_num, b.m_num, c.m_num); reset_denominator(c); } else rat_sub(a, b, c); STRACE("mpq", tout << to_string(c) << "\n";); } void inc(mpz & a) { mpz_manager::inc(a); } void dec(mpz & a) { mpz_manager::dec(a); } void inc(mpq & a) { add(a, mpz(1), a); } void dec(mpq & a) { add(a, mpz(-1), a); } void mul(mpz const & a, mpz const & b, mpz & c) { mpz_manager::mul(a, b, c); } void mul(mpz const & a, mpz const & b, mpq & c) { mpz_manager::mul(a, b, c.m_num); reset_denominator(c); } void mul(mpq const & a, mpq const & b, mpq & c) { STRACE("mpq", tout << "[mpq] " << to_string(a) << " * " << to_string(b) << " == ";); if (is_int(a) && is_int(b)) { mpz_manager::mul(a.m_num, b.m_num, c.m_num); reset_denominator(c); } else rat_mul(a, b, c); STRACE("mpq", tout << to_string(c) << "\n";); } void mul(mpz const & a, mpq const & b, mpq & c) { STRACE("mpq", tout << "[mpq] " << to_string(a) << " * " << to_string(b) << " == ";); if (is_int(b)) { mpz_manager::mul(a, b.m_num, c.m_num); reset_denominator(c); } else rat_mul(a, b, c); STRACE("mpq", tout << to_string(c) << "\n";); } void addmul(mpz const & a, mpz const & b, mpz const & c, mpz & d) { return mpz_manager::addmul(a, b, c, d); } void submul(mpz const & a, mpz const & b, mpz const & c, mpz & d) { return mpz_manager::submul(a, b, c, d); } // d <- a + b*c void addmul(mpq const & a, mpq const & b, mpq const & c, mpq & d) { if (is_one(b)) { add(a, c, d); } else if (is_minus_one(b)) { sub(a, c, d); } else { if (SYNCH) { mpq tmp; mul(b,c,tmp); add(a,tmp,d); del(tmp); } else { mul(b, c, m_q_tmp1); add(a, m_q_tmp1, d); } } } // d <- a + b*c void addmul(mpq const & a, mpz const & b, mpq const & c, mpq & d) { if (is_one(b)) { add(a, c, d); } else if (is_minus_one(b)) { sub(a, c, d); } else { if (SYNCH) { mpq tmp; mul(b,c,tmp); add(a,tmp,d); del(tmp); } else { mul(b,c, m_q_tmp1); add(a, m_q_tmp1, d); } } } // d <- a - b*c void submul(mpq const & a, mpq const & b, mpq const & c, mpq & d) { if (is_one(b)) { sub(a, c, d); } else if (is_minus_one(b)) { add(a, c, d); } else { if (SYNCH) { mpq tmp; mul(b,c,tmp); sub(a,tmp,d); del(tmp); } else { mul(b,c, m_q_tmp1); sub(a, m_q_tmp1, d); } } } // d <- a - b*c void submul(mpq const & a, mpz const & b, mpq const & c, mpq & d) { if (is_one(b)) { sub(a, c, d); } else if (is_minus_one(b)) { add(a, c, d); } else { if (SYNCH) { mpq tmp; mul(b,c,tmp); sub(a,tmp,d); del(tmp); } else { mul(b,c, m_q_tmp1); sub(a, m_q_tmp1, d); } } } void inv(mpq & a) { SASSERT(!is_zero(a)); if (is_neg(a)) { neg(a.m_num); neg(a.m_den); } mpz_manager::swap(a.m_num, a.m_den); } void inv(mpq const & a, mpq & b) { set(b, a); inv(b); } void div(mpq const & a, mpq const & b, mpq & c) { STRACE("mpq", tout << "[mpq] " << to_string(a) << " / " << to_string(b) << " == ";); if (&b == &c) { mpz tmp; // it is not safe to use c.m_num at this point. mul(a.m_num, b.m_den, tmp); mul(a.m_den, b.m_num, c.m_den); set(c.m_num, tmp); del(tmp); } else { mul(a.m_num, b.m_den, c.m_num); mul(a.m_den, b.m_num, c.m_den); } if (mpz_manager::is_neg(c.m_den)) { neg(c.m_num); neg(c.m_den); } normalize(c); STRACE("mpq", tout << to_string(c) << "\n";); } void div(mpq const & a, mpz const & b, mpq & c) { STRACE("mpq", tout << "[mpq] " << to_string(a) << " / " << to_string(b) << " == ";); set(c.m_num, a.m_num); mul(a.m_den, b, c.m_den); if (mpz_manager::is_neg(b)) { neg(c.m_num); neg(c.m_den); } normalize(c); STRACE("mpq", tout << to_string(c) << "\n";); } void acc_div(mpq & a, mpz const & b) { STRACE("mpq", tout << "[mpq] " << to_string(a) << " / " << to_string(b) << " == ";); mul(a.m_den, b, a.m_den); if (mpz_manager::is_neg(b)) { neg(a.m_num); neg(a.m_den); } normalize(a); STRACE("mpq", tout << to_string(a) << "\n";); } void machine_div(mpz const & a, mpz const & b, mpz & c) { mpz_manager::machine_div(a, b, c); } void machine_div_rem(mpz const & a, mpz const & b, mpz & c, mpz & d) { mpz_manager::machine_div_rem(a, b, c, d); } void div(mpz const & a, mpz const & b, mpz & c) { mpz_manager::div(a, b, c); } void rat_div(mpz const & a, mpz const & b, mpq & c) { set(c.m_num, a); set(c.m_den, b); normalize(c); } void machine_idiv(mpq const & a, mpq const & b, mpq & c) { SASSERT(is_int(a) && is_int(b)); machine_div(a.m_num, b.m_num, c.m_num); reset_denominator(c); } void machine_idiv_rem(mpq const & a, mpq const & b, mpq & c, mpq & d) { SASSERT(is_int(a) && is_int(b)); machine_div_rem(a.m_num, b.m_num, c.m_num, d.m_num); reset_denominator(c); reset_denominator(d); } void machine_idiv(mpq const & a, mpq const & b, mpz & c) { SASSERT(is_int(a) && is_int(b)); machine_div(a.m_num, b.m_num, c); } void idiv(mpq const & a, mpq const & b, mpq & c) { SASSERT(is_int(a) && is_int(b)); div(a.m_num, b.m_num, c.m_num); reset_denominator(c); } void idiv(mpq const & a, mpq const & b, mpz & c) { SASSERT(is_int(a) && is_int(b)); div(a.m_num, b.m_num, c); } void rem(mpz const & a, mpz const & b, mpz & c) { mpz_manager::rem(a, b, c); } void rem(mpq const & a, mpq const & b, mpq & c) { SASSERT(is_int(a) && is_int(b)); rem(a.m_num, b.m_num, c.m_num); reset_denominator(c); } void rem(mpq const & a, mpq const & b, mpz & c) { SASSERT(is_int(a) && is_int(b)); rem(a.m_num, b.m_num, c); } void mod(mpz const & a, mpz const & b, mpz & c) { mpz_manager::mod(a, b, c); } void mod(mpq const & a, mpq const & b, mpq & c) { SASSERT(is_int(a) && is_int(b)); mod(a.m_num, b.m_num, c.m_num); reset_denominator(c); } void mod(mpq const & a, mpq const & b, mpz & c) { SASSERT(is_int(a) && is_int(b)); mod(a.m_num, b.m_num, c); } static unsigned hash(mpz const & a) { return mpz_manager::hash(a); } static unsigned hash(mpq const & a) { return hash(a.m_num) + 3*hash(a.m_den); } bool eq(mpz const & a, mpz const & b) { return mpz_manager::eq(a, b); } bool eq(mpq const & a, mpq const & b) { return eq(a.m_num, b.m_num) && eq(a.m_den, b.m_den); } bool lt(mpz const & a, mpz const & b) { return mpz_manager::lt(a, b); } bool lt(mpq const & a, mpq const & b) { if (is_int(a) && is_int(b)) return lt(a.m_num, b.m_num); else return rat_lt(a, b); } bool neq(mpz const & a, mpz const & b) { return mpz_manager::neq(a, b); } bool gt(mpz const & a, mpz const & b) { return mpz_manager::gt(a, b); } bool ge(mpz const & a, mpz const & b) { return mpz_manager::ge(a, b); } bool le(mpz const & a, mpz const & b) { return mpz_manager::le(a, b); } bool neq(mpq const & a, mpq const & b) { return !eq(a, b); } bool gt(mpq const & a, mpq const & b) { return lt(b, a); } bool ge(mpq const & a, mpq const & b) { return !lt(a, b); } bool le(mpq const & a, mpq const & b) { return !lt(b, a); } void gcd(mpz const & a, mpz const & b, mpz & c) { mpz_manager::gcd(a, b, c); } void gcd(unsigned sz, mpz const * as, mpz & g) { mpz_manager::gcd(sz, as, g); } void gcd(unsigned sz, mpq const * as, mpq & g); void gcd(mpq const & a, mpq const & b, mpq & c) { SASSERT(is_int(a) && is_int(b)); gcd(a.m_num, b.m_num, c.m_num); reset_denominator(c); } void gcd(mpz const & r1, mpz const & r2, mpz & a, mpz & b, mpz & g) { mpz_manager::gcd(r1, r2, a, b, g); } void gcd(mpq const & r1, mpq const & r2, mpq & a, mpq & b, mpq & g) { SASSERT(is_int(r1) && is_int(r2)); reset_denominator(a); reset_denominator(b); reset_denominator(g); gcd(r1.m_num, r2.m_num, a.m_num, b.m_num, g.m_num); } void lcm(mpz const & a, mpz const & b, mpz & c) { mpz_manager::lcm(a, b, c); } void lcm(mpq const & a, mpq const & b, mpq & c) { SASSERT(is_int(a) && is_int(b)); lcm(a.m_num, b.m_num, c.m_num); reset_denominator(c); } bool divides(mpz const & a, mpz const & b) { return mpz_manager::divides(a, b); } bool divides(mpq const & a, mpq const & b) { SASSERT(is_int(a) && is_int(b)); return divides(a.m_num, b.m_num); } void bitwise_or(mpz const & a, mpz const & b, mpz & c) { return mpz_manager::bitwise_or(a, b, c); } void bitwise_or(mpq const & a, mpq const & b, mpq & c) { SASSERT(is_int(a) && is_int(b)); bitwise_or(a.m_num, b.m_num, c.m_num); reset_denominator(c); } void bitwise_and(mpz const & a, mpz const & b, mpz & c) { return mpz_manager::bitwise_and(a, b, c); } void bitwise_and(mpq const & a, mpq const & b, mpq & c) { SASSERT(is_int(a) && is_int(b)); bitwise_and(a.m_num, b.m_num, c.m_num); reset_denominator(c); } void bitwise_xor(mpz const & a, mpz const & b, mpz & c) { return mpz_manager::bitwise_xor(a, b, c); } void bitwise_xor(mpq const & a, mpq const & b, mpq & c) { SASSERT(is_int(a) && is_int(b)); bitwise_xor(a.m_num, b.m_num, c.m_num); reset_denominator(c); } void bitwise_not(unsigned sz, mpz const & a, mpz & c) { return mpz_manager::bitwise_not(sz, a, c); } void bitwise_not(unsigned sz, mpq const & a, mpq & c) { SASSERT(is_int(a)); bitwise_not(sz, a.m_num, c.m_num); reset_denominator(c); } void set(mpz & target, mpz const & source) { mpz_manager::set(target, source); } void set(mpq & target, mpq const & source) { set(target.m_num, source.m_num); set(target.m_den, source.m_den); } void set(mpz & a, int val) { mpz_manager::set(a, val); } void set(mpq & a, int val) { set(a.m_num, val); reset_denominator(a); } void set(mpq & a, int n, int d) { SASSERT(d != 0); if (d < 0) { n = -n; d = -d; } set(a.m_num, n); set(a.m_den, d); normalize(a); } void set(mpq & a, int64_t n, uint64_t d) { SASSERT(d != 0); set(a.m_num, n); set(a.m_den, d); normalize(a); } void set(mpq & a, mpz const & n, mpz const & d) { if (is_neg(d)) { set(a.m_num, n); set(a.m_den, d); neg(a.m_num); neg(a.m_den); } else { set(a.m_num, n); set(a.m_den, d); } normalize(a); } void set(mpz & a, unsigned val) { mpz_manager::set(a, val); } void set(mpq & a, unsigned val) { set(a.m_num, val); reset_denominator(a); } void set(mpz & a, char const * val) { mpz_manager::set(a, val); } void set(mpq & a, char const * val); void set(mpz & a, int64_t val) { mpz_manager::set(a, val); } void set(mpq & a, int64_t val) { set(a.m_num, val); reset_denominator(a); } void set(mpz & a, uint64_t val) { mpz_manager::set(a, val); } void set(mpq & a, uint64_t val) { set(a.m_num, val); reset_denominator(a); } void set(mpq & a, mpz const & val) { mpz_manager::set(a.m_num, val); reset_denominator(a); } void set(mpz & a, unsigned sz, digit_t const * digits) { mpz_manager::set_digits(a, sz, digits); } void set(mpq & a, unsigned sz, digit_t const * digits) { mpz_manager::set_digits(a.m_num, sz, digits); reset_denominator(a); } mpq dup(const mpq & source) { mpq temp; set(temp, source); return temp; } mpz dup(const mpz & source) { mpz temp; set(temp, source); return temp; } void swap(mpz & a, mpz & b) { mpz_manager::swap(a, b); } void swap(mpq & a, mpq & b) { swap(a.m_num, b.m_num); swap(a.m_den, b.m_den); } void swap_numerator(mpz & a, mpq & b) { swap(a, b.m_num); } bool is_uint64(mpz const & a) const { return mpz_manager::is_uint64(a); } bool is_int64(mpz const & a) const { return mpz_manager::is_int64(a); } uint64_t get_uint64(mpz const & a) const { return mpz_manager::get_uint64(a); } int64_t get_int64(mpz const & a) const { return mpz_manager::get_int64(a); } bool is_uint64(mpq const & a) const { return is_int(a) && is_uint64(a.m_num); } bool is_int64(mpq const & a) const { return is_int(a) && is_int64(a.m_num); } uint64_t get_uint64(mpq const & a) const { SASSERT(is_uint64(a)); return get_uint64(a.m_num); } int64_t get_int64(mpq const & a) const { SASSERT(is_int64(a)); return get_int64(a.m_num); } double get_double(mpz const & a) const { return mpz_manager::get_double(a); } double get_double(mpq const & a) const; void power(mpz const & a, unsigned p, mpz & b) { mpz_manager::power(a, p, b); } void power(mpq const & a, unsigned p, mpq & b); bool is_power_of_two(mpz const & a, unsigned & shift) { return mpz_manager::is_power_of_two(a, shift); } bool is_power_of_two(mpq const & a, unsigned & shift) { return is_int(a) && is_power_of_two(a.m_num, shift); } unsigned bitsize(mpz const & a) { return mpz_manager::bitsize(a); } unsigned bitsize(mpq const & a) { return is_int(a) ? bitsize(a.m_num) : bitsize(a.m_num) + bitsize(a.m_den); } unsigned storage_size(mpz const & a) { return mpz_manager::size_info(a); } unsigned storage_size(mpq const & a) { return mpz_manager::size_info(a.m_num) + mpz_manager::size_info(a.m_den); } /** \brief Return true if the number is a perfect square, and store the square root in 'root'. If the number n is positive and the result is false, then root will contain the smallest integer r such that r*r > n. */ bool is_perfect_square(mpz const & a, mpz & r) { return mpz_manager::is_perfect_square(a, r); } /** \brief Return true if the numerator and denominators are perfect squares. Store the square root in root. If the result is false, then the value of root should be ignored. */ bool is_perfect_square(mpq const & a, mpq & r) { if (is_int(a)) { reset_denominator(r); return is_perfect_square(a.m_num, r.m_num); } if (is_perfect_square(a.m_num, r.m_num) && is_perfect_square(a.m_den, r.m_den)) { normalize(r); return true; } return false; } bool root(mpz & a, unsigned n) { return mpz_manager::root(a, n); } bool root(mpz const & a, unsigned n, mpz & r) { return mpz_manager::root(a, n, r); } /** \brief Return true if n-th root of a is rational, and store result in r. */ bool root(mpq const & a, unsigned n, mpq & r); /** \brief Return the biggest k s.t. 2^k <= a. \remark Return 0 if a is not positive. */ unsigned prev_power_of_two(mpz const & a) { return mpz_manager::prev_power_of_two(a); } unsigned prev_power_of_two(mpq const & a); bool is_int_perfect_square(mpq const & a, mpq & r) { SASSERT(is_int(a)); reset_denominator(r); return is_perfect_square(a.m_num, r.m_num); } bool is_even(mpz const & a) { return mpz_manager::is_even(a); } public: bool is_even(mpq const & a) { return is_int(a) && is_even(a.m_num); } }; #ifndef SINGLE_THREAD typedef mpq_manager synch_mpq_manager; #else typedef mpq_manager synch_mpq_manager; #endif typedef mpq_manager unsynch_mpq_manager; typedef _scoped_numeral scoped_mpq; typedef _scoped_numeral scoped_synch_mpq; typedef _scoped_numeral_vector scoped_mpq_vector; #endif /* MPQ_H_ */ z3-z3-4.8.7/src/util/mpq_inf.cpp000066400000000000000000000013711356505360400163320ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: mpq_inf.cpp Abstract: MPQ numbers with infinitesimals Author: Leonardo de Moura (leonardo) 2011-06-28 Revision History: --*/ #include "util/mpq_inf.h" template std::string mpq_inf_manager::to_string(mpq_inf const & a) { if (m.is_zero(a.second)) return m.to_string(a.first); std::string s = "("; s += m.to_string(a.first); if (m.is_neg(a.second)) s += " -e*"; else s += " +e*"; mpq tmp; m.set(tmp, a.second); m.abs(tmp); s += m.to_string(tmp); m.del(tmp); s += ")"; return s; } #ifndef SINGLE_THREAD template class mpq_inf_manager; #endif template class mpq_inf_manager; z3-z3-4.8.7/src/util/mpq_inf.h000066400000000000000000000166121356505360400160030ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: mpq_inf.h Abstract: MPQ numbers with infinitesimals Author: Leonardo de Moura (leonardo) 2011-06-28 Revision History: --*/ #ifndef MPQ_INF_H_ #define MPQ_INF_H_ #include "util/mpq.h" #include "util/hash.h" typedef std::pair mpq_inf; template class mpq_inf_manager { mpq_manager m; double m_inf; public: typedef mpq_inf numeral; mpq_inf_manager(double inf = 0.0001) { set_inf(inf); } void set_inf(double inf) { m_inf = inf; } enum inf_kind { NEG=-1, ZERO, POS }; void reset(mpq_inf & a) { m.reset(a.first); m.reset(a.second); } unsigned hash(mpq_inf const & a) const { return hash_u_u(m.hash(a.first), m.hash(a.second)); } void del(mpq_inf & a) { m.del(a.first); m.del(a.second); } void swap(mpq_inf & a, mpq_inf & b) { m.swap(a.first, b.first); m.swap(a.second, b.second); } void set(mpq_inf & a, mpq_inf const & b) { m.set(a.first, b.first); m.set(a.second, b.second); } void set(mpq_inf & a, mpq const & r) { m.set(a.first, r); m.reset(a.second); } void set(mpq_inf & a, mpq const & r, inf_kind k) { m.set(a.first, r); switch (k) { case NEG: m.set(a.second, -1); break; case ZERO: m.reset(a.second); break; case POS: m.set(a.second, 1); break; } } void set(mpq_inf & a, mpq const & r, mpq const & i) { m.set(a.first, r); m.set(a.second, i); } bool is_int(mpq_inf const & a) const { return m.is_int(a.first) && m.is_zero(a.second); } bool is_pos(mpq_inf const & a) const { return m.is_pos(a.first) || (m.is_zero(a.first) && m.is_pos(a.second)); } bool is_neg(mpq_inf const & a) const { return m.is_neg(a.first) || (m.is_zero(a.first) && m.is_neg(a.second)); } bool is_rational(mpq_inf const & a) const { return m.is_zero(a.second); } void get_rational(mpq_inf const & a, mpq & r) { m.set(r, a.first); } void get_infinitesimal(mpq_inf const & a, mpq & r) { m.set(r, a.second); } double get_double(mpq_inf const & a) { double r = m.get_double(a.first); if (m.is_pos(a.second)) return r + m_inf; else if (m.is_neg(a.second)) return r - m_inf; else return r; } bool is_zero(mpq_inf const & a) const { return m.is_zero(a.first) && m.is_zero(a.second); } bool eq(mpq_inf const & a, mpq_inf const & b) { return m.eq(a.first, b.first) && m.eq(a.second, b.second); } bool eq(mpq_inf const & a, mpq const & b) { return m.eq(a.first, b) && m.is_zero(a.second); } bool eq(mpq_inf const & a, mpq const & b, inf_kind k) { if (!m.eq(a.first, b)) return false; switch (k) { case NEG: return m.is_minus_one(a.second); case ZERO: return m.is_zero(a.second); case POS: return m.is_one(a.second); } UNREACHABLE(); return false; } bool lt(mpq_inf const & a, mpq_inf const & b) { return m.lt(a.first, b.first) || (m.lt(a.second, b.second) && m.eq(a.first, b.first)); } bool lt(mpq_inf const & a, mpq const & b) { return m.lt(a.first, b) || (m.is_neg(a.second) && m.eq(a.first, b)); } bool lt(mpq_inf const & a, mpq const & b, inf_kind k) { if (m.lt(a.first, b)) return true; if (m.eq(a.first, b)) { switch (k) { case NEG: return m.lt(a.second, mpq(-1)); case ZERO: return m.is_neg(a.second); case POS: return m.lt(a.second, mpq(1)); } UNREACHABLE(); } return false; } bool gt(mpq_inf const & a, mpq_inf const & b) { return lt(b, a); } bool gt(mpq_inf const & a, mpq const & b) { return m.gt(a.first, b) || (m.is_pos(a.second) && m.eq(a.first, b)); } bool gt(mpq_inf const & a, mpq const & b, inf_kind k) { if (m.gt(a.first, b)) return true; if (m.eq(a.first, b)) { switch (k) { case NEG: return m.gt(a.second, mpq(-1)); case ZERO: return m.is_pos(a.second); case POS: return m.gt(a.second, mpq(1)); } UNREACHABLE(); } return false; } bool le(mpq_inf const & a, mpq_inf const & b) { return !gt(a, b); } bool le(mpq_inf const & a, mpq const & b) { return !gt(a, b); } bool le(mpq_inf const & a, mpq const & b, inf_kind k) { return !gt(a, b, k); } bool ge(mpq_inf const & a, mpq_inf const & b) { return !lt(a, b); } bool ge(mpq_inf const & a, mpq const & b) { return !lt(a, b); } bool ge(mpq_inf const & a, mpq const & b, inf_kind k) { return !lt(a, b, k); } void add(mpq_inf const & a, mpq_inf const & b, mpq_inf & c) { m.add(a.first, b.first, c.first); m.add(a.second, b.second, c.second); } void sub(mpq_inf const & a, mpq_inf const & b, mpq_inf & c) { m.sub(a.first, b.first, c.first); m.sub(a.second, b.second, c.second); } void add(mpq_inf const & a, mpq const & b, mpq_inf & c) { m.add(a.first, b, c.first); m.set(c.second, a.second); } void sub(mpq_inf const & a, mpq const & b, mpq_inf & c) { m.sub(a.first, b, c.first); m.set(c.second, a.second); } void mul(mpq_inf const & a, mpq const & b, mpq_inf & c) { m.mul(a.first, b, c.first); m.mul(a.second, b, c.second); } void mul(mpq_inf const & a, mpz const & b, mpq_inf & c) { m.mul(b, a.first, c.first); m.mul(b, a.second, c.second); } void div(mpq_inf const & a, mpq const & b, mpq_inf & c) { m.div(a.first, b, c.first); m.div(a.second, b, c.second); } void div(mpq_inf const & a, mpz const & b, mpq_inf & c) { m.div(a.first, b, c.first); m.div(a.second, b, c.second); } void inc(mpq_inf & a) { m.inc(a.first); } void dec(mpq_inf & a) { m.dec(a.first); } void neg(mpq_inf & a) { m.neg(a.first); m.neg(a.second); } void abs(mpq_inf & a) { if (is_neg(a)) { neg(a); } } void ceil(mpq_inf const & a, mpq & b) { if (m.is_int(a.first)) { // special cases for k - delta*epsilon where k is an integer if (m.is_pos(a.second)) m.add(a.first, mpq(1), b); // ceil(k + delta*epsilon) --> k+1 else m.set(b, a.first); } else { m.ceil(a.first, b); } } void floor(mpq_inf const & a, mpq & b) { if (m.is_int(a.first)) { if (m.is_neg(a.first)) m.sub(a.first, mpq(1), b); // floor(k - delta*epsilon) --> k-1 else m.set(b, a.first); } else { m.floor(a.first, b); } } std::string to_string(mpq_inf const & a); void display(std::ostream & out, mpq_inf const & a) { out << to_string(a); } mpq_manager& get_mpq_manager() { return m; } }; #ifndef SINGLE_THREAD typedef mpq_inf_manager synch_mpq_inf_manager; #else typedef mpq_inf_manager synch_mpq_inf_manager; #endif typedef mpq_inf_manager unsynch_mpq_inf_manager; #endif z3-z3-4.8.7/src/util/mpz.cpp000066400000000000000000002067341356505360400155210ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: mpz.cpp Abstract: Author: Leonardo de Moura (leonardo) 2010-06-17. Revision History: --*/ #include #include #include "util/mpz.h" #include "util/buffer.h" #include "util/trace.h" #include "util/hash.h" #include "util/bit_util.h" #if defined(_MP_INTERNAL) #include "util/mpn.h" #elif defined(_MP_GMP) #include #else #error No multi-precision library selected. #endif // Available GCD algorithms // #define EUCLID_GCD // #define BINARY_GCD // #define LS_BINARY_GCD // #define LEHMER_GCD #if defined(_MP_GMP) // Use LEHMER only if not using GMP // LEHMER assumes 32-bit digits, so it cannot be used with MSBIGNUM library + 64-bit binary #define EUCLID_GCD #else #define LEHMER_GCD #endif #if defined(_WINDOWS) && !defined(_M_ARM) && !defined(_M_ARM64) // This is needed for _tzcnt_u32 and friends. #include #define _trailing_zeros32(X) _tzcnt_u32(X) #endif #if defined(__GNUC__) #define _trailing_zeros32(X) __builtin_ctz(X) #endif #if (defined(__LP64__) || defined(_WIN64)) && !defined(_M_ARM) && !defined(_M_ARM64) #if defined(__GNUC__) #define _trailing_zeros64(X) __builtin_ctzll(X) #else #define _trailing_zeros64(X) _tzcnt_u64(X) #endif #else inline uint64_t _trailing_zeros64(uint64_t x) { uint64_t r = 0; for (; 0 == (x & 1) && r < 64; ++r, x >>= 1); return r; } #if defined(_WINDOWS) && !defined(_M_ARM) && !defined(_M_ARM64) // _trailing_zeros32 already defined using intrinsics #else inline uint32_t _trailing_zeros32(uint32_t x) { uint32_t r = 0; for (; 0 == (x & 1) && r < 32; ++r, x >>= 1); return r; } #endif #endif #define _bit_min(x, y) (y + ((x - y) & ((int)(x - y) >> 31))) #define _bit_max(x, y) (x - ((x - y) & ((int)(x - y) >> 31))) unsigned u_gcd(unsigned u, unsigned v) { if (u == 0) return v; if (v == 0) return u; unsigned shift = _trailing_zeros32(u | v); u >>= _trailing_zeros32(u); if (u == 1 || v == 1) return 1 << shift; if (u == v) return u << shift; do { v >>= _trailing_zeros32(v); #if 1 unsigned diff = u - v; unsigned mdiff = diff & (unsigned)((int)diff >> 31); u = v + mdiff; // min v = diff - 2 * mdiff; // if v <= u: u - v, if v > u: v - u = u - v - 2 * (u - v) #endif #if 0 unsigned t = _bit_max(u, v); u = _bit_min(u, v); v = t; v -= u; #endif #if 0 unsigned t = std::max(u, v); u = std::min(u,v); v = t; v -= u; #endif #if 0 if (u > v) std::swap(u, v); v -= u; #endif #if 0 unsigned d1 = u - v; unsigned d2 = v - u; unsigned md21 = d2 & (unsigned)((int)d1 >> 31); unsigned md12 = d1 & (unsigned)((int)d2 >> 31); u = _bit_min(u, v); v = md12 | md21; #endif } while (v != 0); return u << shift; } uint64_t u64_gcd(uint64_t u, uint64_t v) { if (u == 0) return v; if (v == 0) return u; if (u == 1 || v == 1) return 1; auto shift = _trailing_zeros64(u | v); u >>= _trailing_zeros64(u); do { v >>= _trailing_zeros64(v); if (u > v) std::swap(u, v); v -= u; } while (v != 0); return u << shift; } template mpz_manager::mpz_manager(): m_allocator("mpz_manager") { #ifndef _MP_GMP if (sizeof(digit_t) == sizeof(uint64_t)) { // 64-bit machine m_init_cell_capacity = 4; } else { m_init_cell_capacity = 6; } set(m_int_min, -static_cast(INT_MIN)); #else // GMP mpz_init(m_tmp); mpz_init(m_tmp2); mpz_init(m_two32); mpz_set_ui(m_two32, UINT_MAX); mpz_set_ui(m_tmp, 1); mpz_add(m_two32, m_two32, m_tmp); mpz_init(m_uint64_max); unsigned max_l = static_cast(UINT64_MAX); unsigned max_h = static_cast(UINT64_MAX >> 32); mpz_set_ui(m_uint64_max, max_h); mpz_mul(m_uint64_max, m_two32, m_uint64_max); mpz_set_ui(m_tmp, max_l); mpz_add(m_uint64_max, m_uint64_max, m_tmp); mpz_init(m_int64_max); mpz_init(m_int64_min); max_l = static_cast(INT64_MAX % static_cast(UINT_MAX)); max_h = static_cast(INT64_MAX / static_cast(UINT_MAX)); mpz_set_ui(m_int64_max, max_h); mpz_set_ui(m_tmp, UINT_MAX); mpz_mul(m_int64_max, m_tmp, m_int64_max); mpz_set_ui(m_tmp, max_l); mpz_add(m_int64_max, m_tmp, m_int64_max); mpz_neg(m_int64_min, m_int64_max); mpz_sub_ui(m_int64_min, m_int64_min, 1); #endif mpz one(1); set(m_two64, (uint64_t)UINT64_MAX); add(m_two64, one, m_two64); } template mpz_manager::~mpz_manager() { del(m_two64); #ifndef _MP_GMP del(m_int_min); #else mpz_clear(m_tmp); mpz_clear(m_tmp2); mpz_clear(m_two32); mpz_clear(m_uint64_max); mpz_clear(m_int64_max); mpz_clear(m_int64_min); #endif } #ifndef _MP_GMP template mpz_cell * mpz_manager::allocate(unsigned capacity) { SASSERT(capacity >= m_init_cell_capacity); MPZ_BEGIN_CRITICAL(); mpz_cell * cell = reinterpret_cast(m_allocator.allocate(cell_size(capacity))); MPZ_END_CRITICAL(); cell->m_capacity = capacity; return cell; } template void mpz_manager::deallocate(bool is_heap, mpz_cell * ptr) { if (is_heap) { MPZ_BEGIN_CRITICAL(); m_allocator.deallocate(cell_size(ptr->m_capacity), ptr); MPZ_END_CRITICAL(); } } template mpz_manager::sign_cell::sign_cell(mpz_manager& m, mpz const& a): m_local(reinterpret_cast(m_bytes)), m_a(a) { m_local.m_ptr->m_capacity = capacity; m.get_sign_cell(a, m_sign, m_cell, m_local.m_ptr); } #endif template void mpz_manager::del(mpz & a) { if (a.m_ptr) { deallocate(a.m_owner == mpz_self, a.m_ptr); a.m_ptr = nullptr; a.m_kind = mpz_small; a.m_owner = mpz_self; } } template void mpz_manager::add(mpz const & a, mpz const & b, mpz & c) { STRACE("mpz", tout << "[mpz] " << to_string(a) << " + " << to_string(b) << " == ";); if (is_small(a) && is_small(b)) { set_i64(c, i64(a) + i64(b)); } else { big_add(a, b, c); } STRACE("mpz", tout << to_string(c) << "\n";); } template void mpz_manager::sub(mpz const & a, mpz const & b, mpz & c) { STRACE("mpz", tout << "[mpz] " << to_string(a) << " - " << to_string(b) << " == ";); if (is_small(a) && is_small(b)) { set_i64(c, i64(a) - i64(b)); } else { big_sub(a, b, c); } STRACE("mpz", tout << to_string(c) << "\n";); } template void mpz_manager::set_big_i64(mpz & c, int64_t v) { #ifndef _MP_GMP if (c.m_ptr == nullptr) { c.m_ptr = allocate(m_init_cell_capacity); c.m_owner = mpz_self; } c.m_kind = mpz_large; SASSERT(capacity(c) >= m_init_cell_capacity); uint64_t _v; if (v == std::numeric_limits::min()) { // min-int is even _v = -(v/2); c.m_val = -1; } else if (v < 0) { _v = -v; c.m_val = -1; } else { _v = v; c.m_val = 1; } if (sizeof(digit_t) == sizeof(uint64_t)) { // 64-bit machine digits(c)[0] = static_cast(_v); c.m_ptr->m_size = 1; } else { // 32-bit machine digits(c)[0] = static_cast(_v); digits(c)[1] = static_cast(_v >> 32); c.m_ptr->m_size = digits(c)[1] == 0 ? 1 : 2; } #else if (c.m_ptr == nullptr) { c.m_ptr = allocate(); c.m_owner = mpz_self; } c.m_kind = mpz_large; uint64_t _v; bool sign = v < 0; if (v == std::numeric_limits::min()) { _v = -(v/2); } else if (v < 0) { _v = -v; } else { _v = v; } mpz_set_ui(*c.m_ptr, static_cast(_v)); MPZ_BEGIN_CRITICAL(); mpz_set_ui(m_tmp, static_cast(_v >> 32)); mpz_mul(m_tmp, m_tmp, m_two32); mpz_add(*c.m_ptr, *c.m_ptr, m_tmp); MPZ_END_CRITICAL(); if (sign) mpz_neg(*c.m_ptr, *c.m_ptr); #endif if (v == std::numeric_limits::min()) { big_add(c, c, c); } } template void mpz_manager::set_big_ui64(mpz & c, uint64_t v) { #ifndef _MP_GMP if (c.m_ptr == nullptr) { c.m_ptr = allocate(m_init_cell_capacity); c.m_owner = mpz_self; } c.m_kind = mpz_large; SASSERT(capacity(c) >= m_init_cell_capacity); c.m_val = 1; if (sizeof(digit_t) == sizeof(uint64_t)) { // 64-bit machine digits(c)[0] = static_cast(v); c.m_ptr->m_size = 1; } else { // 32-bit machine digits(c)[0] = static_cast(v); digits(c)[1] = static_cast(v >> 32); c.m_ptr->m_size = digits(c)[1] == 0 ? 1 : 2; } #else if (c.m_ptr == nullptr) { c.m_ptr = allocate(); c.m_owner = mpz_self; } c.m_kind = mpz_large; mpz_set_ui(*c.m_ptr, static_cast(v)); MPZ_BEGIN_CRITICAL(); mpz_set_ui(m_tmp, static_cast(v >> 32)); mpz_mul(m_tmp, m_tmp, m_two32); mpz_add(*c.m_ptr, *c.m_ptr, m_tmp); MPZ_END_CRITICAL(); #endif } #ifdef _MP_GMP template mpz_manager::ensure_mpz_t::ensure_mpz_t(mpz const& a) { if (is_small(a)) { m_result = &m_local; mpz_init(m_local); mpz_set_si(m_local, a.m_val); } else { m_result = a.m_ptr; } } template mpz_manager::ensure_mpz_t::~ensure_mpz_t() { if (m_result == &m_local) { mpz_clear(m_local); } } #endif #ifndef _MP_GMP template void mpz_manager::set(mpz_cell& src, mpz & a, int sign, unsigned sz) { unsigned i = sz; for (; i > 0 && src.m_digits[i-1] == 0; --i) ; if (i == 0) { // src is zero set(a, 0); return; } unsigned d = src.m_digits[0]; if (i == 1 && d <= INT_MAX) { // src fits is a fixnum a.m_val = sign < 0 ? -static_cast(d) : static_cast(d); a.m_kind = mpz_small; return; } set_digits(a, i, src.m_digits); a.m_val = sign; SASSERT(a.m_kind == mpz_large); } #endif template void mpz_manager::set(mpz & a, char const * val) { set(a, 0); mpz ten(10); mpz tmp; char const * str = val; bool sign = false; while (str[0] == ' ') ++str; if (str[0] == '-') sign = true; while (str[0]) { if ('0' <= str[0] && str[0] <= '9') { SASSERT(str[0] - '0' <= 9); mul(a, ten, tmp); add(tmp, mk_z(str[0] - '0'), a); } ++str; } del(tmp); if (sign) neg(a); } template void mpz_manager::set_digits(mpz & target, unsigned sz, digit_t const * digits) { // remove zero digits while (sz > 0 && digits[sz - 1] == 0) sz--; if (sz == 0) set(target, 0); else if (sz == 1) set(target, digits[0]); else { #ifndef _MP_GMP target.m_val = 1; // number is positive. if (target.m_ptr == nullptr) { unsigned c = sz < m_init_cell_capacity ? m_init_cell_capacity : sz; target.m_ptr = allocate(c); target.m_ptr->m_size = sz; target.m_ptr->m_capacity = c; target.m_kind = mpz_large; target.m_owner = mpz_self; memcpy(target.m_ptr->m_digits, digits, sizeof(digit_t) * sz); } else if (capacity(target) < sz) { SASSERT(sz > m_init_cell_capacity); mpz_cell* ptr = allocate(sz); memcpy(ptr->m_digits, digits, sizeof(digit_t) * sz); ptr->m_size = sz; ptr->m_capacity = sz; deallocate(target); target.m_val = 1; target.m_ptr = ptr; target.m_kind = mpz_large; target.m_owner = mpz_self; } else { target.m_ptr->m_size = sz; if (target.m_ptr->m_digits != digits) memcpy(target.m_ptr->m_digits, digits, sizeof(digit_t) * sz); target.m_kind = mpz_large; } #else mk_big(target); // reset mpz_set_ui(*target.m_ptr, digits[sz - 1]); SASSERT(sz > 0); unsigned i = sz - 1; MPZ_BEGIN_CRITICAL(); while (i > 0) { --i; mpz_mul_2exp(*target.m_ptr, *target.m_ptr, 32); mpz_set_ui(m_tmp, digits[i]); mpz_add(*target.m_ptr, *target.m_ptr, m_tmp); } MPZ_END_CRITICAL(); #endif } } template void mpz_manager::mul(mpz const & a, mpz const & b, mpz & c) { STRACE("mpz", tout << "[mpz] " << to_string(a) << " * " << to_string(b) << " == ";); if (is_small(a) && is_small(b)) { set_i64(c, i64(a) * i64(b)); } else { big_mul(a, b, c); } STRACE("mpz", tout << to_string(c) << "\n";); } // d <- a + b*c template void mpz_manager::addmul(mpz const & a, mpz const & b, mpz const & c, mpz & d) { if (is_one(b)) { add(a, c, d); } else if (is_minus_one(b)) { sub(a, c, d); } else { mpz tmp; mul(b,c,tmp); add(a,tmp,d); del(tmp); } } // d <- a - b*c template void mpz_manager::submul(mpz const & a, mpz const & b, mpz const & c, mpz & d) { if (is_one(b)) { sub(a, c, d); } else if (is_minus_one(b)) { add(a, c, d); } else { mpz tmp; mul(b,c,tmp); sub(a,tmp,d); del(tmp); } } template void mpz_manager::machine_div_rem(mpz const & a, mpz const & b, mpz & q, mpz & r) { STRACE("mpz", tout << "[mpz-ext] divrem(" << to_string(a) << ", " << to_string(b) << ") == ";); if (is_small(a) && is_small(b)) { int64_t _a = i64(a); int64_t _b = i64(b); set_i64(q, _a / _b); set_i64(r, _a % _b); } else { big_div_rem(a, b, q, r); } STRACE("mpz", tout << "(" << to_string(q) << ", " << to_string(r) << ")\n";); } template void mpz_manager::machine_div(mpz const & a, mpz const & b, mpz & c) { STRACE("mpz", tout << "[mpz-ext] machine-div(" << to_string(a) << ", " << to_string(b) << ") == ";); if (is_small(a) && is_small(b)) { set_i64(c, i64(a) / i64(b)); } else { big_div(a, b, c); } STRACE("mpz", tout << to_string(c) << "\n";); } template void mpz_manager::reset(mpz & a) { deallocate(a); set(a, 0); } template void mpz_manager::rem(mpz const & a, mpz const & b, mpz & c) { STRACE("mpz", tout << "[mpz-ext] rem(" << to_string(a) << ", " << to_string(b) << ") == ";); if (is_small(a) && is_small(b)) { set_i64(c, i64(a) % i64(b)); } else { big_rem(a, b, c); } STRACE("mpz", tout << to_string(c) << "\n";); } template void mpz_manager::div_gcd(mpz const& a, mpz const& b, mpz & c) { STRACE("mpz", tout << "[mpz-ext] div(" << to_string(a) << ", " << to_string(b) << ") == ";); if (is_one(b)) { set(c, a); } else { machine_div(a, b, c); } STRACE("mpz", tout << to_string(c) << "\n";); } template void mpz_manager::div(mpz const & a, mpz const & b, mpz & c) { STRACE("mpz", tout << "[mpz-ext] div(" << to_string(a) << ", " << to_string(b) << ") == ";); if (is_one(b)) { set(c, a); } else if (is_neg(a)) { mpz tmp; machine_div_rem(a, b, c, tmp); if (!is_zero(tmp)) { if (is_neg(b)) add(c, mk_z(1), c); else sub(c, mk_z(1), c); } del(tmp); } else { machine_div(a, b, c); } STRACE("mpz", tout << to_string(c) << "\n";); } template void mpz_manager::mod(mpz const & a, mpz const & b, mpz & c) { STRACE("mpz", tout << "[mpz-ext] mod(" << to_string(a) << ", " << to_string(b) << ") == ";); rem(a, b, c); if (is_neg(c)) { if (is_pos(b)) add(c, b, c); else sub(c, b, c); } STRACE("mpz", tout << to_string(c) << "\n";); } template void mpz_manager::neg(mpz & a) { STRACE("mpz", tout << "[mpz] 0 - " << to_string(a) << " == ";); if (is_small(a) && a.m_val == INT_MIN) { // neg(INT_MIN) is not a small int set_big_i64(a, - static_cast(INT_MIN)); return; } #ifndef _MP_GMP a.m_val = -a.m_val; #else if (is_small(a)) { a.m_val = -a.m_val; } else { mpz_neg(*a.m_ptr, *a.m_ptr); } #endif STRACE("mpz", tout << to_string(a) << "\n";); } template void mpz_manager::abs(mpz & a) { if (is_small(a)) { if (a.m_val < 0) { if (a.m_val == INT_MIN) { // abs(INT_MIN) is not a small int set_big_i64(a, - static_cast(INT_MIN)); } else a.m_val = -a.m_val; } } else { #ifndef _MP_GMP a.m_val = 1; #else mpz_abs(*a.m_ptr, *a.m_ptr); #endif } } // TBD: replace use of 'tmp' by 'c'. #ifndef _MP_GMP template template void mpz_manager::big_add_sub(mpz const & a, mpz const & b, mpz & c) { sign_cell ca(*this, a), cb(*this, b); int sign_b = cb.sign(); mpz_stack tmp; if (SUB) sign_b = -sign_b; size_t real_sz; if (ca.sign() == sign_b) { unsigned sz = std::max(ca.cell()->m_size, cb.cell()->m_size)+1; allocate_if_needed(tmp, sz); m_mpn_manager.add(ca.cell()->m_digits, ca.cell()->m_size, cb.cell()->m_digits, cb.cell()->m_size, tmp.m_ptr->m_digits, sz, &real_sz); SASSERT(real_sz <= sz); set(*tmp.m_ptr, c, ca.sign(), static_cast(real_sz)); } else { digit_t borrow; int r = m_mpn_manager.compare(ca.cell()->m_digits, ca.cell()->m_size, cb.cell()->m_digits, cb.cell()->m_size); if (r == 0) { set(c, 0); } else if (r < 0) { // a < b unsigned sz = cb.cell()->m_size; allocate_if_needed(tmp, sz); m_mpn_manager.sub(cb.cell()->m_digits, cb.cell()->m_size, ca.cell()->m_digits, ca.cell()->m_size, tmp.m_ptr->m_digits, &borrow); SASSERT(borrow == 0); set(*tmp.m_ptr, c, sign_b, sz); } else { // a > b unsigned sz = ca.cell()->m_size; allocate_if_needed(tmp, sz); m_mpn_manager.sub(ca.cell()->m_digits, ca.cell()->m_size, cb.cell()->m_digits, cb.cell()->m_size, tmp.m_ptr->m_digits, &borrow); SASSERT(borrow == 0); set(*tmp.m_ptr, c, ca.sign(), sz); } } del(tmp); } #endif template void mpz_manager::big_add(mpz const & a, mpz const & b, mpz & c) { #ifndef _MP_GMP big_add_sub(a, b, c); #else // GMP version ensure_mpz_t a1(a), b1(b); mk_big(c); mpz_add(*c.m_ptr, a1(), b1()); #endif } template void mpz_manager::big_sub(mpz const & a, mpz const & b, mpz & c) { #ifndef _MP_GMP big_add_sub(a, b, c); #else // GMP version ensure_mpz_t a1(a), b1(b); mk_big(c); mpz_sub(*c.m_ptr, a1(), b1()); #endif } template void mpz_manager::big_mul(mpz const & a, mpz const & b, mpz & c) { #ifndef _MP_GMP // TBD replace tmp by c. mpz_stack tmp; sign_cell ca(*this, a), cb(*this, b); unsigned sz = ca.cell()->m_size + cb.cell()->m_size; allocate_if_needed(tmp, sz); m_mpn_manager.mul(ca.cell()->m_digits, ca.cell()->m_size, cb.cell()->m_digits, cb.cell()->m_size, tmp.m_ptr->m_digits); set(*tmp.m_ptr, c, ca.sign() == cb.sign() ? 1 : -1, sz); del(tmp); #else // GMP version ensure_mpz_t a1(a), b1(b); mk_big(c); mpz_mul(*c.m_ptr, a1(), b1()); #endif } template void mpz_manager::big_div_rem(mpz const & a, mpz const & b, mpz & q, mpz & r) { #ifndef _MP_GMP quot_rem_core(a, b, q, r); #else // GMP version ensure_mpz_t a1(a), b1(b); mk_big(q); mk_big(r); mpz_tdiv_qr(*q.m_ptr, *r.m_ptr, a1(), b1()); #endif } #ifndef _MP_GMP template template void mpz_manager::quot_rem_core(mpz const & a, mpz const & b, mpz & q, mpz & r) { /* +26 / +7 = +3, remainder is +5 -26 / +7 = -3, remainder is -5 +26 / -7 = -3, remainder is +5 -26 / -7 = +3, remainder is -5 */ mpz_stack q1, r1; sign_cell ca(*this, a), cb(*this, b); if (cb.cell()->m_size > ca.cell()->m_size) { if (MODE == REM_ONLY || MODE == QUOT_AND_REM) set(r, a); if (MODE == QUOT_ONLY || MODE == QUOT_AND_REM) set(q, 0); return; } unsigned q_sz = ca.cell()->m_size - cb.cell()->m_size + 1; unsigned r_sz = cb.cell()->m_size; allocate_if_needed(q1, q_sz); allocate_if_needed(r1, r_sz); m_mpn_manager.div(ca.cell()->m_digits, ca.cell()->m_size, cb.cell()->m_digits, cb.cell()->m_size, q1.m_ptr->m_digits, r1.m_ptr->m_digits); if (MODE == QUOT_ONLY || MODE == QUOT_AND_REM) set(*q1.m_ptr, q, ca.sign() == cb.sign() ? 1 : -1, q_sz); if (MODE == REM_ONLY || MODE == QUOT_AND_REM) set(*r1.m_ptr, r, ca.sign(), r_sz); del(q1); del(r1); } #endif template void mpz_manager::big_div(mpz const & a, mpz const & b, mpz & c) { #ifndef _MP_GMP mpz dummy; quot_rem_core(a, b, c, dummy); SASSERT(is_zero(dummy)); del(dummy); #else // GMP version ensure_mpz_t a1(a), b1(b); mk_big(c); mpz_tdiv_q(*c.m_ptr, a1(), b1()); #endif } template void mpz_manager::big_rem(mpz const & a, mpz const & b, mpz & c) { #ifndef _MP_GMP mpz dummy; quot_rem_core(a, b, dummy, c); SASSERT(is_zero(dummy)); del(dummy); #else // GMP version ensure_mpz_t a1(a), b1(b); mk_big(c); mpz_tdiv_r(*c.m_ptr, a1(), b1()); #endif } template void mpz_manager::gcd(mpz const & a, mpz const & b, mpz & c) { static_assert(sizeof(a.m_val) == sizeof(int), "size mismatch"); static_assert(sizeof(mpz) <= 16, "mpz size overflow"); if (is_small(a) && is_small(b) && a.m_val != INT_MIN && b.m_val != INT_MIN) { int _a = a.m_val; int _b = b.m_val; if (_a < 0) _a = -_a; if (_b < 0) _b = -_b; unsigned r = u_gcd(_a, _b); set(c, r); } else { #ifdef _MP_GMP ensure_mpz_t a1(a), b1(b); mk_big(c); mpz_gcd(*c.m_ptr, a1(), b1()); return; #endif if (is_zero(a)) { set(c, b); abs(c); return; } if (is_zero(b)) { set(c, a); abs(c); return; } #ifdef BINARY_GCD // Binary GCD for big numbers // - It doesn't use division // - The initial experiments, don't show any performance improvement // - It only works with _MP_INTERNAL mpz u, v, diff; set(u, a); set(v, b); abs(u); abs(v); unsigned k_u = power_of_two_multiple(u); unsigned k_v = power_of_two_multiple(v); unsigned k = k_u < k_v ? k_u : k_v; machine_div2k(u, k_u); while (true) { machine_div2k(v, k_v); if (lt(u, v)) { sub(v, u, v); } else { sub(u, v, diff); swap(u, v); swap(v, diff); } if (is_zero(v) || is_one(v)) break; // reset least significant bit if (is_small(v)) v.m_val &= ~1; else v.m_ptr->m_digits[0] &= ~static_cast(1); k_v = power_of_two_multiple(v); } mul2k(u, k, c); del(u); del(v); del(diff); #endif // BINARY_GCD #ifdef EUCLID_GCD mpz tmp1; mpz tmp2; mpz aux; set(tmp1, a); set(tmp2, b); abs(tmp1); abs(tmp2); if (lt(tmp1, tmp2)) swap(tmp1, tmp2); if (is_zero(tmp2)) { swap(c, tmp1); } else { while (true) { if (is_uint64(tmp1) && is_uint64(tmp2)) { set(c, u64_gcd(get_uint64(tmp1), get_uint64(tmp2))); break; } rem(tmp1, tmp2, aux); if (is_zero(aux)) { swap(c, tmp2); break; } swap(tmp1, tmp2); swap(tmp2, aux); } } del(tmp1); del(tmp2); del(aux); #endif // EUCLID_GCD #ifdef LS_BINARY_GCD mpz u, v, t, u1, u2; set(u, a); set(v, b); abs(u); abs(v); if (lt(u, v)) swap(u, v); while (!is_zero(v)) { // Basic idea: // compute t = 2^e*v such that t <= u < 2t // u := min{u - t, 2t - u} // // The assignment u := min{u - t, 2t - u} // can be replaced with u := u - t // // Since u and v are positive, we have: // 2^{log2(u)} <= u < 2^{(log2(u) + 1)} // 2^{log2(v)} <= v < 2^{(log2(v) + 1)} // --> // 2^{log2(v)}*2^{log2(u)-log2(v)} <= v*2^{log2(u)-log2(v)} < 2^{log2(v) + 1}*2^{log2(u)-log2(v)} // --> // 2^{log2(u)} <= v*2^{log2(u)-log2(v)} < 2^{log2(u) + 1} // // Now, let t be v*2^{log2(u)-log2(v)} // If t <= u, then we found t // Otherwise t = t div 2 unsigned k_u = log2(u); unsigned k_v = log2(v); SASSERT(k_v <= k_u); unsigned e = k_u - k_v; mul2k(v, e, t); sub(u, t, u1); if (is_neg(u1)) { // t is too big machine_div2k(t, 1); // Now, u1 contains u - 2t neg(u1); // Now, u1 contains 2t - u sub(u, t, u2); // u2 := u - t } else { // u1 contains u - t mul2k(t, 1); sub(t, u, u2); // u2 contains 2t - u } SASSERT(is_nonneg(u1)); SASSERT(is_nonneg(u2)); if (lt(u1, u2)) swap(u, u1); else swap(u, u2); if (lt(u, v)) swap(u,v); } swap(u, c); del(u); del(v); del(t); del(u1); del(u2); #endif // LS_BINARY_GCD #ifdef LEHMER_GCD // For now, it only works if sizeof(digit_t) == sizeof(unsigned) static_assert(sizeof(digit_t) == sizeof(unsigned), ""); int64_t a_hat, b_hat, A, B, C, D, T, q, a_sz, b_sz; mpz a1, b1, t, r, tmp; set(a1, a); set(b1, b); abs(a1); abs(b1); if (lt(a1, b1)) swap(a1, b1); while (true) { SASSERT(ge(a1, b1)); if (is_small(b1)) { if (is_small(a1)) { unsigned r = u_gcd(a1.m_val, b1.m_val); set(c, r); break; } else { while (!is_zero(b1)) { SASSERT(ge(a1, b1)); rem(a1, b1, tmp); swap(a1, b1); swap(b1, tmp); } swap(c, a1); break; } } SASSERT(!is_small(a1)); SASSERT(!is_small(b1)); a_sz = a1.m_ptr->m_size; b_sz = b1.m_ptr->m_size; SASSERT(b_sz <= a_sz); a_hat = a1.m_ptr->m_digits[a_sz - 1]; b_hat = (b_sz == a_sz) ? b1.m_ptr->m_digits[b_sz - 1] : 0; A = 1; B = 0; C = 0; D = 1; while (true) { // Loop invariants SASSERT(a_hat + A <= static_cast(UINT_MAX) + 1); SASSERT(a_hat + B < static_cast(UINT_MAX) + 1); SASSERT(b_hat + C < static_cast(UINT_MAX) + 1); SASSERT(b_hat + D <= static_cast(UINT_MAX) + 1); // overflows can't happen since I'm using int64 if (b_hat + C == 0 || b_hat + D == 0) break; q = (a_hat + A)/(b_hat + C); if (q != (a_hat + B)/(b_hat + D)) break; T = A - q*C; A = C; C = T; T = B - q*D; B = D; D = T; T = a_hat - q*b_hat; a_hat = b_hat; b_hat = T; } SASSERT(ge(a1, b1)); if (B == 0) { rem(a1, b1, t); swap(a1, b1); swap(b1, t); SASSERT(ge(a1, b1)); } else { // t <- A*a1 set(tmp, A); mul(a1, tmp, t); // t <- t + B*b1 set(tmp, B); addmul(t, tmp, b1, t); // r <- C*a1 set(tmp, C); mul(a1, tmp, r); // r <- r + D*b1 set(tmp, D); addmul(r, tmp, b1, r); // a <- t swap(a1, t); // b <- r swap(b1, r); SASSERT(ge(a1, b1)); } } del(a1); del(b1); del(r); del(t); del(tmp); #endif // LEHMER_GCD } } template unsigned mpz_manager::size_info(mpz const & a) { if (is_small(a)) return 1; #ifndef _MP_GMP return a.m_ptr->m_size + 1; #else return mpz_size(*a.m_ptr); #endif } template struct mpz_manager::sz_lt { mpz_manager & m; mpz const * m_as; sz_lt(mpz_manager & _m, mpz const * as):m(_m), m_as(as) {} bool operator()(unsigned p1, unsigned p2) { return m.size_info(m_as[p1]) < m.size_info(m_as[p2]); } }; template void mpz_manager::gcd(unsigned sz, mpz const * as, mpz & g) { #if 0 // Optimization: sort numbers by size. Motivation: compute the gcd of the small ones first. // The optimization did not really help. switch (sz) { case 0: set(g, 0); return; case 1: set(g, as[0]); abs(g); return; case 2: gcd(as[0], as[1], g); return; default: break; } unsigned i; for (i = 0; i < sz; i++) { if (!is_small(as[i])) break; } if (i != sz) { // array has big numbers sbuffer p; for (i = 0; i < sz; i++) p.push_back(i); sz_lt lt(*this, as); std::sort(p.begin(), p.end(), lt); TRACE("mpz_gcd", for (unsigned i = 0; i < sz; i++) tout << p[i] << ":" << size_info(as[p[i]]) << " "; tout << "\n";); gcd(as[p[0]], as[p[1]], g); for (i = 2; i < sz; i++) { if (is_one(g)) return; gcd(g, as[p[i]], g); } return; } else { gcd(as[0], as[1], g); for (unsigned i = 2; i < sz; i++) { if (is_one(g)) return; gcd(g, as[i], g); } } #else // Vanilla implementation switch (sz) { case 0: set(g, 0); return; case 1: set(g, as[0]); abs(g); return; default: break; } gcd(as[0], as[1], g); for (unsigned i = 2; i < sz; i++) { if (is_one(g)) return; gcd(g, as[i], g); } #endif } template void mpz_manager::gcd(mpz const & r1, mpz const & r2, mpz & a, mpz & b, mpz & r) { mpz tmp1, tmp2; mpz aux, quot; set(tmp1, r1); set(tmp2, r2); set(a, 1); set(b, 0); mpz nexta, nextb; set(nexta, 0); set(nextb, 1); abs(tmp1); abs(tmp2); if (lt(tmp1, tmp2)) { swap(tmp1, tmp2); swap(nexta, nextb); swap(a, b); } // tmp1 >= tmp2 >= 0 // quot_rem in one function would be faster. while (is_pos(tmp2)) { SASSERT(ge(tmp1, tmp2)); // aux = tmp2 set(aux, tmp2); // quot = div(tmp1, tmp2); machine_div(tmp1, tmp2, quot); // tmp2 = tmp1 % tmp2 rem(tmp1, tmp2, tmp2); // tmp1 = aux set(tmp1, aux); // aux = nexta set(aux, nexta); // nexta = a - (quot*nexta) mul(quot, nexta, nexta); sub(a, nexta, nexta); // a = axu set(a, aux); // aux = nextb set(aux, nextb); // nextb = b - (quot*nextb) mul(nextb, quot, nextb); sub(b, nextb, nextb); // b = aux set(b, aux); } if (is_neg(r1)) neg(a); if (is_neg(r2)) neg(b); // SASSERT((a*r1) + (b*r2) == tmp1); #ifdef Z3DEBUG mul(a, r1, nexta); mul(b, r2, nextb); add(nexta, nextb, nexta); SASSERT(eq(nexta, tmp1)); #endif set(r, tmp1); del(tmp1); del(tmp2); del(aux); del(quot); del(nexta); del(nextb); } template void mpz_manager::lcm(mpz const & a, mpz const & b, mpz & c) { if (is_one(b)) { set(c, a); TRACE("lcm_bug", tout << "1. lcm(" << to_string(a) << ", " << to_string(b) << ") = " << to_string(c) << "\n";); } else if (is_one(a) || eq(a, b)) { set(c, b); TRACE("lcm_bug", tout << "2. lcm(" << to_string(a) << ", " << to_string(b) << ") = " << to_string(c) << "\n";); } else { mpz r; gcd(a, b, r); TRACE("lcm_bug", tout << "gcd(" << to_string(a) << ", " << to_string(b) << ") = " << to_string(r) << "\n";); if (eq(r, a)) { set(c, b); TRACE("lcm_bug", tout << "3. lcm(" << to_string(a) << ", " << to_string(b) << ") = " << to_string(c) << "\n";); } else if (eq(r, b)) { set(c, a); TRACE("lcm_bug", tout << "4. lcm(" << to_string(a) << ", " << to_string(b) << ") = " << to_string(c) << "\n";); } else { // c contains gcd(a, b) // so c divides a, and machine_div(a, c) is equal to div(a, c) machine_div(a, r, r); mul(r, b, c); TRACE("lcm_bug", tout << "5. lcm(" << to_string(a) << ", " << to_string(b) << ") = " << to_string(c) << "\n";); } del(r); } } template void mpz_manager::bitwise_or(mpz const & a, mpz const & b, mpz & c) { SASSERT(is_nonneg(a)); SASSERT(is_nonneg(b)); TRACE("mpz", tout << "is_small(a): " << is_small(a) << ", is_small(b): " << is_small(b) << "\n";); if (is_small(a) && is_small(b)) { c.m_val = a.m_val | b.m_val; c.m_kind = mpz_small; } else { #ifndef _MP_GMP mpz a1, b1, a2, b2, m, tmp; set(a1, a); set(b1, b); set(m, 1); set(c, 0); while (!is_zero(a1) && !is_zero(b1)) { TRACE("mpz", tout << "a1: " << to_string(a1) << ", b1: " << to_string(b1) << "\n";); mod(a1, m_two64, a2); mod(b1, m_two64, b2); TRACE("mpz", tout << "a2: " << to_string(a2) << ", b2: " << to_string(b2) << "\n";); uint64_t v = get_uint64(a2) | get_uint64(b2); TRACE("mpz", tout << "uint(a2): " << get_uint64(a2) << ", uint(b2): " << get_uint64(b2) << "\n";); set(tmp, v); mul(tmp, m, tmp); add(c, tmp, c); // c += m * v mul(m, m_two64, m); div(a1, m_two64, a1); div(b1, m_two64, b1); } if (!is_zero(a1)) { mul(a1, m, a1); add(c, a1, c); } if (!is_zero(b1)) { mul(b1, m, b1); add(c, b1, c); } del(a1); del(b1); del(a2); del(b2); del(m); del(tmp); #else ensure_mpz_t a1(a), b1(b); mk_big(c); mpz_ior(*c.m_ptr, a1(), b1()); #endif } } template void mpz_manager::bitwise_and(mpz const & a, mpz const & b, mpz & c) { SASSERT(is_nonneg(a)); SASSERT(is_nonneg(b)); if (is_small(a) && is_small(b)) { c.m_val = a.m_val & b.m_val; c.m_kind = mpz_small; } else { #ifndef _MP_GMP mpz a1, b1, a2, b2, m, tmp; set(a1, a); set(b1, b); set(m, 1); set(c, 0); while (!is_zero(a1) && !is_zero(b1)) { mod(a1, m_two64, a2); mod(b1, m_two64, b2); uint64_t v = get_uint64(a2) & get_uint64(b2); set(tmp, v); mul(tmp, m, tmp); add(c, tmp, c); // c += m * v mul(m, m_two64, m); div(a1, m_two64, a1); div(b1, m_two64, b1); } del(a1); del(b1); del(a2); del(b2); del(m); del(tmp); #else ensure_mpz_t a1(a), b1(b); mk_big(c); mpz_and(*c.m_ptr, a1(), b1()); #endif } } template void mpz_manager::bitwise_xor(mpz const & a, mpz const & b, mpz & c) { SASSERT(is_nonneg(a)); SASSERT(is_nonneg(b)); if (is_small(a) && is_small(b)) { set_i64(c, i64(a) ^ i64(b)); } else { #ifndef _MP_GMP mpz a1, b1, a2, b2, m, tmp; set(a1, a); set(b1, b); set(m, 1); set(c, 0); while (!is_zero(a1) && !is_zero(b1)) { mod(a1, m_two64, a2); mod(b1, m_two64, b2); uint64_t v = get_uint64(a2) ^ get_uint64(b2); set(tmp, v); mul(tmp, m, tmp); add(c, tmp, c); // c += m * v mul(m, m_two64, m); div(a1, m_two64, a1); div(b1, m_two64, b1); } if (!is_zero(a1)) { mul(a1, m, a1); add(c, a1, c); } if (!is_zero(b1)) { mul(b1, m, b1); add(c, b1, c); } del(a1); del(b1); del(a2); del(b2); del(m); del(tmp); #else ensure_mpz_t a1(a), b1(b); mk_big(c); mpz_xor(*c.m_ptr, a1(), b1()); #endif } } template void mpz_manager::bitwise_not(unsigned sz, mpz const & a, mpz & c) { SASSERT(is_nonneg(a)); if (is_small(a) && sz <= 63) { int64_t mask = (static_cast(1) << sz) - static_cast(1); set_i64(c, (~ i64(a)) & mask); } else { mpz a1, a2, m, tmp; set(a1, a); set(m, 1); set(c, 0); while (sz > 0) { mod(a1, m_two64, a2); uint64_t n = get_uint64(a2); uint64_t v = ~n; SASSERT(~v == n); if (sz < 64) { uint64_t mask = (1ull << static_cast(sz)) - 1ull; v = mask & v; } TRACE("bitwise_not", tout << "sz: " << sz << ", v: " << v << ", n: " << n << "\n";); set(tmp, v); SASSERT(get_uint64(tmp) == v); mul(tmp, m, tmp); add(c, tmp, c); // c += m * v mul(m, m_two64, m); div(a1, m_two64, a1); sz -= (sz<64) ? sz : 64; } del(a1); del(a2); del(m); del(tmp); TRACE("bitwise_not", tout << "sz: " << sz << " a: " << to_string(a) << " c: " << to_string(c) << "\n";); } } template void mpz_manager::big_set(mpz & target, mpz const & source) { #ifndef _MP_GMP if (&target == &source) return; target.m_val = source.m_val; if (target.m_ptr == nullptr) { target.m_ptr = allocate(capacity(source)); target.m_ptr->m_size = size(source); target.m_ptr->m_capacity = capacity(source); target.m_kind = mpz_large; target.m_owner = mpz_self; memcpy(target.m_ptr->m_digits, source.m_ptr->m_digits, sizeof(digit_t) * size(source)); } else if (capacity(target) < size(source)) { deallocate(target); target.m_ptr = allocate(capacity(source)); target.m_ptr->m_size = size(source); target.m_ptr->m_capacity = capacity(source); target.m_kind = mpz_large; target.m_owner = mpz_self; memcpy(target.m_ptr->m_digits, source.m_ptr->m_digits, sizeof(digit_t) * size(source)); } else { target.m_ptr->m_size = size(source); memcpy(target.m_ptr->m_digits, source.m_ptr->m_digits, sizeof(digit_t) * size(source)); target.m_kind = mpz_large; } #else // GMP version mk_big(target); mpz_set(*target.m_ptr, *source.m_ptr); #endif } template int mpz_manager::big_compare(mpz const & a, mpz const & b) { #ifndef _MP_GMP sign_cell ca(*this, a), cb(*this, b); if (ca.sign() > 0) { // a is positive if (cb.sign() > 0) { // a & b are positive return m_mpn_manager.compare(ca.cell()->m_digits, ca.cell()->m_size, cb.cell()->m_digits, cb.cell()->m_size); } else { // b is negative return 1; // a > b } } else { // a is negative if (cb.sign() > 0) { // b is positive return -1; // a < b } else { // a & b are negative return m_mpn_manager.compare(cb.cell()->m_digits, cb.cell()->m_size, ca.cell()->m_digits, ca.cell()->m_size); } } #else // GMP version ensure_mpz_t a1(a), b1(b); return mpz_cmp(a1(), b1()); #endif } template bool mpz_manager::is_uint64(mpz const & a) const { #ifndef _MP_GMP if (a.m_val < 0) return false; if (is_small(a)) return true; if (sizeof(digit_t) == sizeof(uint64_t)) { return size(a) <= 1; } else { return size(a) <= 2; } #else // GMP version if (is_small(a)) return a.m_val >= 0; return is_nonneg(a) && mpz_cmp(*a.m_ptr, m_uint64_max) <= 0; #endif } template bool mpz_manager::is_int64(mpz const & a) const { if (is_small(a)) return true; #ifndef _MP_GMP if (!is_abs_uint64(a)) return false; uint64_t num = big_abs_to_uint64(a); uint64_t msb = static_cast(1) << 63; uint64_t msb_val = msb & num; if (a.m_val >= 0) { // non-negative number. return (0 == msb_val); } else { // negative number. // either the high bit is 0, or // the number is 2^64 which can be represented. // return 0 == msb_val || (msb_val == num); } #else // GMP version return mpz_cmp(m_int64_min, *a.m_ptr) <= 0 && mpz_cmp(*a.m_ptr, m_int64_max) <= 0; #endif } template uint64_t mpz_manager::get_uint64(mpz const & a) const { if (is_small(a)) return static_cast(a.m_val); #ifndef _MP_GMP SASSERT(a.m_ptr->m_size > 0); return big_abs_to_uint64(a); #else // GMP version if (sizeof(uint64_t) == sizeof(unsigned long)) { return mpz_get_ui(*a.m_ptr); } else { MPZ_BEGIN_CRITICAL(); mpz_manager * _this = const_cast(this); mpz_set(_this->m_tmp, *a.m_ptr); mpz_mod(_this->m_tmp, m_tmp, m_two32); uint64_t r = static_cast(mpz_get_ui(m_tmp)); mpz_set(_this->m_tmp, *a.m_ptr); mpz_div(_this->m_tmp, m_tmp, m_two32); r += static_cast(mpz_get_ui(m_tmp)) << static_cast(32); MPZ_END_CRITICAL(); return r; } #endif } template int64_t mpz_manager::get_int64(mpz const & a) const { if (is_small(a)) return static_cast(a.m_val); #ifndef _MP_GMP SASSERT(is_int64(a)); uint64_t num = big_abs_to_uint64(a); if (a.m_val < 0) { if (num != 0 && (num << 1) == 0) return INT64_MIN; return -static_cast(num); } return static_cast(num); #else // GMP if (sizeof(int64_t) == sizeof(long) || mpz_fits_slong_p(*a.m_ptr)) { return mpz_get_si(*a.m_ptr); } else { MPZ_BEGIN_CRITICAL(); mpz_manager * _this = const_cast(this); mpz_mod(_this->m_tmp, *a.m_ptr, m_two32); int64_t r = static_cast(mpz_get_ui(m_tmp)); mpz_div(_this->m_tmp, *a.m_ptr, m_two32); r += static_cast(mpz_get_si(m_tmp)) << static_cast(32); MPZ_END_CRITICAL(); return r; } #endif } template double mpz_manager::get_double(mpz const & a) const { if (is_small(a)) return static_cast(a.m_val); #ifndef _MP_GMP double r = 0.0; double d = 1.0; unsigned sz = size(a); for (unsigned i = 0; i < sz; i++) { r += d * static_cast(digits(a)[i]); if (sizeof(digit_t) == sizeof(uint64_t)) d *= static_cast(UINT64_MAX); // 64-bit version else d *= static_cast(UINT_MAX); // 32-bit version } return a.m_val < 0 ? -r : r; #else return mpz_get_d(*a.m_ptr); #endif } template void mpz_manager::display(std::ostream & out, mpz const & a) const { if (is_small(a)) { out << a.m_val; } else { #ifndef _MP_GMP if (a.m_val < 0) out << "-"; if (sizeof(digit_t) == 4) { sbuffer buffer(11*size(a), 0); out << m_mpn_manager.to_string(digits(a), size(a), buffer.begin(), buffer.size()); } else { sbuffer buffer(21*size(a), 0); out << m_mpn_manager.to_string(digits(a), size(a), buffer.begin(), buffer.size()); } #else // GMP version size_t sz = mpz_sizeinbase(*a.m_ptr, 10) + 2; sbuffer buffer(sz, 0); mpz_get_str(buffer.c_ptr(), 10, *a.m_ptr); out << buffer.c_ptr(); #endif } } template void mpz_manager::display_smt2(std::ostream & out, mpz const & a, bool decimal) const { if (is_neg(a)) { mpz_manager * _this = const_cast*>(this); _scoped_numeral > tmp(*_this); _this->set(tmp, a); _this->neg(tmp); out << "(- "; display(out, tmp); if (decimal) out << ".0"; out << ")"; } else { display(out, a); if (decimal) out << ".0"; } } template void mpz_manager::display_hex(std::ostream & out, mpz const & a, unsigned num_bits) const { SASSERT(num_bits % 4 == 0); std::ios fmt(nullptr); fmt.copyfmt(out); out << std::hex; if (is_small(a)) { out << std::setw(num_bits/4) << std::setfill('0') << get_uint64(a); } else { #ifndef _MP_GMP digit_t *ds = digits(a); unsigned sz = size(a); unsigned bitSize = sz * sizeof(digit_t) * 8; unsigned firstDigitSize; if (num_bits >= bitSize) { firstDigitSize = sizeof(digit_t) * 2; for (unsigned i = 0; i < (num_bits - bitSize)/4; ++i) { out << "0"; } } else { firstDigitSize = num_bits % (sizeof(digit_t) * 8) / 4; } out << std::setfill('0') << std::setw(firstDigitSize) << ds[sz-1] << std::setw(sizeof(digit_t)*2); for (unsigned i = 1; i < sz; ++i) { out << ds[sz-i-1]; } #else // GMP version size_t sz = mpz_sizeinbase(*(a.m_ptr), 16); unsigned requiredLength = num_bits / 4; unsigned padding = requiredLength > sz ? requiredLength - sz : 0; sbuffer buffer(sz, 0); mpz_get_str(buffer.c_ptr(), 16, *(a.m_ptr)); for (unsigned i = 0; i < padding; ++i) { out << "0"; } out << buffer.c_ptr() + (sz > requiredLength ? sz - requiredLength : 0); #endif } out.copyfmt(fmt); } void display_binary_data(std::ostream &out, unsigned val, unsigned numBits) { SASSERT(numBits <= sizeof(unsigned)*8); for (int shift = numBits-1; shift >= 0; --shift) { if (val & (1 << shift)) { out << "1"; } else { out << "0"; } } } template void mpz_manager::display_bin(std::ostream & out, mpz const & a, unsigned num_bits) const { if (is_uint(a)) { display_binary_data(out, get_uint(a), num_bits); } else { #ifndef _MP_GMP digit_t *ds = digits(a); unsigned sz = size(a); const unsigned digitBitSize = sizeof(digit_t) * 8; unsigned bitSize = sz * digitBitSize; unsigned firstDigitLength; if (num_bits > bitSize) { firstDigitLength = 0; for (unsigned i = 0; i < (num_bits - bitSize); ++i) { out << "0"; } } else { firstDigitLength = num_bits % digitBitSize; } for (unsigned i = 0; i < sz; ++i) { if (i == 0 && firstDigitLength != 0) { display_binary_data(out, ds[sz-1], firstDigitLength); } else { display_binary_data(out, ds[sz-i-1], digitBitSize); } } #else // GMP version size_t sz = mpz_sizeinbase(*(a.m_ptr), 2); unsigned padding = num_bits > sz ? num_bits - sz : 0; sbuffer buffer(sz, 0); mpz_get_str(buffer.c_ptr(), 2, *(a.m_ptr)); for (unsigned i = 0; i < padding; ++i) { out << "0"; } out << buffer.c_ptr() + (sz > num_bits ? sz - num_bits : 0); #endif } } template std::string mpz_manager::to_string(mpz const & a) const { std::ostringstream buffer; display(buffer, a); return buffer.str(); } template unsigned mpz_manager::hash(mpz const & a) { if (is_small(a)) return a.m_val; #ifndef _MP_GMP unsigned sz = size(a); if (sz == 1) return static_cast(digits(a)[0]); return string_hash(reinterpret_cast(digits(a)), sz * sizeof(digit_t), 17); #else return mpz_get_si(*a.m_ptr); #endif } template void mpz_manager::power(mpz const & a, unsigned p, mpz & b) { #ifdef _MP_GMP if (!is_small(a)) { mk_big(b); mpz_pow_ui(*b.m_ptr, *a.m_ptr, p); return; } #endif #ifndef _MP_GMP if (is_small(a)) { if (a.m_val == 2) { if (p < 8 * sizeof(int) - 1) { b.m_val = 1 << p; b.m_kind = mpz_small; } else { unsigned sz = p/(8 * sizeof(digit_t)) + 1; unsigned shift = p%(8 * sizeof(digit_t)); SASSERT(sz > 0); allocate_if_needed(b, sz); SASSERT(b.m_ptr->m_capacity >= sz); b.m_ptr->m_size = sz; for (unsigned i = 0; i < sz - 1; i++) b.m_ptr->m_digits[i] = 0; b.m_ptr->m_digits[sz-1] = 1 << shift; b.m_val = 1; b.m_kind = mpz_large; } return; } if (a.m_val == 0) { SASSERT(p != 0); set(b, 0); return; } if (a.m_val == 1) { set(b, 1); return; } } #endif // general purpose unsigned mask = 1; mpz power; set(power, a); set(b, 1); while (mask <= p) { if (mask & p) mul(b, power, b); mul(power, power, power); mask = mask << 1; } del(power); } template bool mpz_manager::is_power_of_two(mpz const & a) { unsigned shift; return is_power_of_two(a, shift); } template bool mpz_manager::is_power_of_two(mpz const & a, unsigned & shift) { if (is_nonpos(a)) return false; if (is_small(a)) { if (::is_power_of_two(a.m_val)) { shift = ::log2((unsigned)a.m_val); return true; } else { return false; } } #ifndef _MP_GMP mpz_cell * c = a.m_ptr; unsigned sz = c->m_size; digit_t * ds = c->m_digits; for (unsigned i = 0; i < sz - 1; i++) { if (ds[i] != 0) return false; } digit_t v = ds[sz-1]; if (!(v & (v - 1)) && v) { shift = log2(a); return true; } else { return false; } #else if (mpz_popcount(*a.m_ptr) == 1) { shift = log2(a); return true; } else { return false; } #endif } // Expand capacity of a #ifndef _MP_GMP template void mpz_manager::ensure_capacity(mpz & a, unsigned capacity) { if (capacity <= 1) return; if (capacity < m_init_cell_capacity) capacity = m_init_cell_capacity; if (is_small(a)) { int val = a.m_val; allocate_if_needed(a, capacity); a.m_kind = mpz_large; SASSERT(a.m_ptr->m_capacity >= capacity); if (val == INT_MIN) { unsigned intmin_sz = m_int_min.m_ptr->m_size; for (unsigned i = 0; i < intmin_sz; i++) a.m_ptr->m_digits[i] = m_int_min.m_ptr->m_digits[i]; a.m_val = -1; a.m_ptr->m_size = m_int_min.m_ptr->m_size; } else if (val < 0) { a.m_ptr->m_digits[0] = -val; a.m_val = -1; a.m_ptr->m_size = 1; } else { a.m_ptr->m_digits[0] = val; a.m_val = 1; a.m_ptr->m_size = 1; } } else if (a.m_ptr->m_capacity < capacity) { mpz_cell * new_cell = allocate(capacity); SASSERT(new_cell->m_capacity == capacity); unsigned old_sz = a.m_ptr->m_size; new_cell->m_size = old_sz; for (unsigned i = 0; i < old_sz; i++) new_cell->m_digits[i] = a.m_ptr->m_digits[i]; deallocate(a); a.m_ptr = new_cell; a.m_owner = mpz_self; a.m_kind = mpz_large; } } template void mpz_manager::normalize(mpz & a) { mpz_cell * c = a.m_ptr; digit_t * ds = c->m_digits; unsigned i = c->m_size; for (; i > 0; --i) { if (ds[i-1] != 0) break; } if (i == 0) { // a is zero... set(a, 0); return; } if (i == 1 && ds[0] <= INT_MAX) { // a is small int val = a.m_val < 0 ? -static_cast(ds[0]) : static_cast(ds[0]); a.m_val = val; a.m_kind = mpz_small; return; } // adjust size c->m_size = i; } #endif template void mpz_manager::machine_div2k(mpz & a, unsigned k) { if (k == 0 || is_zero(a)) return; if (is_small(a)) { if (k < 32) { int64_t twok = 1ull << ((int64_t)k); int64_t val = a.m_val; a.m_val = (int)(val/twok); } else { a.m_val = 0; } return; } #ifndef _MP_GMP unsigned digit_shift = k / (8 * sizeof(digit_t)); mpz_cell * c = a.m_ptr; unsigned sz = c->m_size; if (digit_shift >= sz) { set(a, 0); return; } unsigned bit_shift = k % (8 * sizeof(digit_t)); unsigned comp_shift = (8 * sizeof(digit_t)) - bit_shift; unsigned new_sz = sz - digit_shift; SASSERT(new_sz >= 1); digit_t * ds = c->m_digits; TRACE("mpz_2k", tout << "bit_shift: " << bit_shift << ", comp_shift: " << comp_shift << ", new_sz: " << new_sz << ", sz: " << sz << "\n";); if (new_sz < sz) { unsigned i = 0; unsigned j = digit_shift; if (bit_shift != 0) { for (; i < new_sz - 1; i++, j++) { ds[i] = ds[j]; ds[i] >>= bit_shift; ds[i] |= (ds[j+1] << comp_shift); } ds[i] = ds[j]; ds[i] >>= bit_shift; } else { for (; i < new_sz; i++, j++) { ds[i] = ds[j]; } } } else { SASSERT(new_sz == sz); SASSERT(bit_shift != 0); unsigned i = 0; for (; i < new_sz - 1; i++) { ds[i] >>= bit_shift; ds[i] |= (ds[i+1] << comp_shift); } ds[i] >>= bit_shift; } c->m_size = new_sz; normalize(a); #else ensure_mpz_t a1(a); MPZ_BEGIN_CRITICAL(); mpz_tdiv_q_2exp(m_tmp, a1(), k); mk_big(a); mpz_swap(*a.m_ptr, m_tmp); MPZ_END_CRITICAL(); #endif } template void mpz_manager::mul2k(mpz & a, unsigned k) { if (k == 0 || is_zero(a)) return; if (is_small(a) && k < 32) { set_i64(a, i64(a) * (static_cast(1) << k)); return; } #ifndef _MP_GMP TRACE("mpz_mul2k", tout << "mul2k\na: " << to_string(a) << "\nk: " << k << "\n";); unsigned word_shift = k / (8 * sizeof(digit_t)); unsigned bit_shift = k % (8 * sizeof(digit_t)); unsigned old_sz = is_small(a) ? 1 : a.m_ptr->m_size; unsigned new_sz = old_sz + word_shift + 1; ensure_capacity(a, new_sz); TRACE("mpz_mul2k", tout << "word_shift: " << word_shift << "\nbit_shift: " << bit_shift << "\nold_sz: " << old_sz << "\nnew_sz: " << new_sz << "\na after ensure capacity:\n" << to_string(a) << "\n"; tout << a.m_kind << "\n";); SASSERT(!is_small(a)); mpz_cell * cell_a = a.m_ptr; old_sz = cell_a->m_size; digit_t * ds = cell_a->m_digits; for (unsigned i = old_sz; i < new_sz; i++) ds[i] = 0; cell_a->m_size = new_sz; if (word_shift > 0) { unsigned j = old_sz; unsigned i = old_sz + word_shift; while (j > 0) { --j; --i; ds[i] = ds[j]; } while (i > 0) { --i; ds[i] = 0; } } if (bit_shift > 0) { DEBUG_CODE({ for (unsigned i = 0; i < word_shift; i++) { SASSERT(ds[i] == 0); } }); unsigned comp_shift = (8 * sizeof(digit_t)) - bit_shift; digit_t prev = 0; for (unsigned i = word_shift; i < new_sz; i++) { digit_t new_prev = (ds[i] >> comp_shift); ds[i] <<= bit_shift; ds[i] |= prev; prev = new_prev; } } normalize(a); TRACE("mpz_mul2k", tout << "mul2k result:\n" << to_string(a) << "\n";); #else ensure_mpz_t a1(a); mk_big(a); mpz_mul_2exp(*a.m_ptr, a1(), k); #endif } #ifndef _MP_GMP static_assert(sizeof(digit_t) == 4 || sizeof(digit_t) == 8, ""); #endif template unsigned mpz_manager::power_of_two_multiple(mpz const & a) { if (is_zero(a)) return 0; if (is_small(a)) { unsigned r = 0; int v = a.m_val; #define COUNT_DIGIT_RIGHT_ZEROS() \ if (v % (1 << 16) == 0) { \ r += 16; \ v /= (1 << 16); \ } \ if (v % (1 << 8) == 0) { \ r += 8; \ v /= (1 << 8); \ } \ if (v % (1 << 4) == 0) { \ r += 4; \ v /= (1 << 4); \ } \ if (v % (1 << 2) == 0) { \ r += 2; \ v /= (1 << 2); \ } \ if (v % 2 == 0) { \ r++; \ } COUNT_DIGIT_RIGHT_ZEROS(); return r; } #ifndef _MP_GMP mpz_cell * c = a.m_ptr; unsigned sz = c->m_size; unsigned r = 0; digit_t * source = c->m_digits; for (unsigned i = 0; i < sz; i++) { if (source[i] != 0) { digit_t v = source[i]; if (sizeof(digit_t) == 8) { // TODO: we can remove this if after we move to MPN // In MPN the digit_t is always an unsigned integer if (static_cast(v) % (static_cast(1) << 32) == 0) { r += 32; v = static_cast(static_cast(v) / (static_cast(1) << 32)); } } COUNT_DIGIT_RIGHT_ZEROS(); return r; } r += (8 * sizeof(digit_t)); } return r; #else return mpz_scan1(*a.m_ptr, 0); #endif } template unsigned mpz_manager::log2(mpz const & a) { if (is_nonpos(a)) return 0; if (is_small(a)) return ::log2((unsigned)a.m_val); #ifndef _MP_GMP static_assert(sizeof(digit_t) == 8 || sizeof(digit_t) == 4, ""); mpz_cell * c = a.m_ptr; unsigned sz = c->m_size; digit_t * ds = c->m_digits; if (sizeof(digit_t) == 8) return (sz - 1)*64 + uint64_log2(ds[sz-1]); else return (sz - 1)*32 + ::log2(static_cast(ds[sz-1])); #else unsigned r = mpz_sizeinbase(*a.m_ptr, 2); SASSERT(r > 0); return r - 1; #endif } template unsigned mpz_manager::mlog2(mpz const & a) { if (is_nonneg(a)) return 0; if (is_small(a)) return ::log2((unsigned)-a.m_val); #ifndef _MP_GMP static_assert(sizeof(digit_t) == 8 || sizeof(digit_t) == 4, ""); mpz_cell * c = a.m_ptr; unsigned sz = c->m_size; digit_t * ds = c->m_digits; if (sizeof(digit_t) == 8) return (sz - 1)*64 + uint64_log2(ds[sz-1]); else return (sz - 1)*32 + ::log2(static_cast(ds[sz-1])); #else MPZ_BEGIN_CRITICAL(); mpz_neg(m_tmp, *a.m_ptr); unsigned r = mpz_sizeinbase(m_tmp, 2); MPZ_END_CRITICAL(); SASSERT(r > 0); return r - 1; #endif } template unsigned mpz_manager::bitsize(mpz const & a) { if (is_nonneg(a)) return log2(a) + 1; else return mlog2(a) + 1; } template bool mpz_manager::is_perfect_square(mpz const & a, mpz & root) { if (is_neg(a)) return false; set(root, 0); if (is_zero(a)) { return true; } if (is_one(a)) { set(root, 1); return true; } mpz lo, hi, mid, sq_lo, sq_hi, sq_mid; set(lo, 1); set(hi, a); set(sq_lo, 1); mul(hi, hi, sq_hi); bool result; // lo*lo <= *this < hi*hi while (true) { SASSERT(lt(lo, hi)); SASSERT(le(sq_lo, a) && lt(a, sq_hi)); if (eq(sq_lo, a)) { set(root, lo); result = true; break; } mpz & tmp = mid; add(lo, mpz(1), tmp); if (eq(tmp, hi)) { set(root, hi); result = false; break; } add(hi, lo, tmp); div(tmp, mpz(2), mid); SASSERT(lt(lo, mid) && lt(mid, hi)); mul(mid, mid, sq_mid); if (gt(sq_mid, a)) { set(hi, mid); set(sq_hi, sq_mid); } else { set(lo, mid); set(sq_lo, sq_mid); } } del(lo); del(hi); del(mid); del(sq_lo); del(sq_hi); del(sq_mid); return result; } static unsigned div_l(unsigned k, unsigned n) { return k/n; } static unsigned div_u(unsigned k, unsigned n) { return k%n == 0 ? k/n : k/n + 1; } template bool mpz_manager::root(mpz & a, unsigned n) { SASSERT(n % 2 != 0 || is_nonneg(a)); if (is_zero(a)) { return true; // precise } // Initial approximation // // We have that: // a > 0 -> 2^{log2(a)} <= a <= 2^{(log2(a) + 1)} // a < 0 -> -2^{log2(a) + 1} <= a <= -2^{log2(a)} // // Thus // a > 0 -> 2^{div_l(log2(a), n)} <= a^{1/n} <= 2^{div_u(log2(a) + 1, n)} // a < 0 -> -2^{div_u(log2(a) + 1, n)} <= a^{1/n} <= -2^{div_l(log2(a), n)} // mpz lower; mpz upper; mpz mid; mpz mid_n; if (is_pos(a)) { unsigned k = log2(a); power(mpz(2), div_l(k, n), lower); power(mpz(2), div_u(k + 1, n), upper); } else { unsigned k = mlog2(a); power(mpz(2), div_u(k + 1, n), lower); power(mpz(2), div_l(k, n), upper); neg(lower); neg(upper); } bool result; SASSERT(le(lower, upper)); if (eq(lower, upper)) { swap(a, lower); result = true; } else { // Refine using bisection. TODO: use Newton's method if this is a bottleneck while (true) { add(upper, lower, mid); machine_div2k(mid, 1); TRACE("mpz", tout << "upper: "; display(tout, upper); tout << "\nlower: "; display(tout, lower); tout << "\nmid: "; display(tout, mid); tout << "\n";); power(mid, n, mid_n); if (eq(mid_n, a)) { swap(a, mid); result = true; break; } if (eq(mid, lower) || eq(mid, upper)) { swap(a, upper); result = false; break; } if (lt(mid_n, a)) { // new lower bound swap(mid, lower); } else { SASSERT(lt(a, mid_n)); // new upper bound swap(mid, upper); } } } del(lower); del(upper); del(mid); del(mid_n); return result; } template bool mpz_manager::decompose(mpz const & a, svector & digits) { digits.reset(); if (is_small(a)) { if (a.m_val < 0) { digits.push_back(-a.m_val); return true; } else { digits.push_back(a.m_val); return false; } } else { #ifndef _MP_GMP mpz_cell * cell_a = a.m_ptr; unsigned sz = cell_a->m_size; for (unsigned i = 0; i < sz; i++) { digits.push_back(cell_a->m_digits[i]); } return a.m_val < 0; #else bool r = is_neg(a); MPZ_BEGIN_CRITICAL(); mpz_set(m_tmp, *a.m_ptr); mpz_abs(m_tmp, m_tmp); while (mpz_sgn(m_tmp) != 0) { mpz_tdiv_r_2exp(m_tmp2, m_tmp, 32); unsigned v = mpz_get_ui(m_tmp2); digits.push_back(v); mpz_tdiv_q_2exp(m_tmp, m_tmp, 32); } MPZ_END_CRITICAL(); return r; #endif } } template bool mpz_manager::divides(mpz const & a, mpz const & b) { _scoped_numeral > tmp(*this); bool r; if (is_zero(a)) { // I assume 0 | 0. // Remark a|b is a shorthand for (exists x. a x = b) // If b is zero, any x will do. If b != 0, then a does not divide b r = is_zero(b); } else { rem(b, a, tmp); r = is_zero(tmp); } STRACE("divides", tout << "[mpz] Divisible["; display(tout, b); tout << ", "; display(tout, a); tout << "] == " << (r?"True":"False") << "\n";); TRACE("divides_bug", tout << "tmp: "; display(tout, tmp); tout << "\n";); return r; } #ifndef SINGLE_THREAD template class mpz_manager; #endif template class mpz_manager; z3-z3-4.8.7/src/util/mpz.h000066400000000000000000000455741356505360400151710ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: mpz.h Abstract: Author: Leonardo de Moura (leonardo) 2010-06-17. Revision History: --*/ #ifndef MPZ_H_ #define MPZ_H_ #include #include #include "util/util.h" #include "util/small_object_allocator.h" #include "util/trace.h" #include "util/scoped_numeral.h" #include "util/scoped_numeral_vector.h" #include "util/mpn.h" unsigned u_gcd(unsigned u, unsigned v); uint64_t u64_gcd(uint64_t u, uint64_t v); #ifdef _MP_GMP typedef unsigned digit_t; #endif #ifdef _MSC_VER #pragma warning(disable : 4200) #endif template class mpz_manager; template class mpq_manager; #if !defined(_MP_GMP) && !defined(_MP_MSBIGNUM) && !defined(_MP_INTERNAL) #ifdef _WINDOWS #define _MP_INTERNAL #else #define _MP_GMP #endif #endif #if defined(_MP_MSBIGNUM) typedef size_t digit_t; #elif defined(_MP_INTERNAL) typedef unsigned int digit_t; #endif #ifndef _MP_GMP class mpz_cell { unsigned m_size; unsigned m_capacity; digit_t m_digits[0]; friend class mpz_manager; friend class mpz_manager; friend class mpz_stack; }; #else #include #endif /** \brief Multi-precision integer. If m_kind == mpz_small, it is a small number and the value is stored in m_val. If m_kind == mpz_large, the value is stored in m_ptr and m_ptr != nullptr. m_val contains the sign (-1 negative, 1 positive) under winodws, m_ptr points to a mpz_cell that store the value. */ enum mpz_kind { mpz_small = 0, mpz_large = 1}; enum mpz_owner { mpz_self = 0, mpz_ext = 1}; class mpz { #ifndef _MP_GMP typedef mpz_cell mpz_type; #else typedef mpz_t mpz_type; #endif int m_val; unsigned m_kind:1; unsigned m_owner:1; mpz_type * m_ptr; friend class mpz_manager; friend class mpz_manager; friend class mpq_manager; friend class mpq_manager; friend class mpq; friend class mpbq; friend class mpbq_manager; friend class mpz_stack; mpz & operator=(mpz const & other) { UNREACHABLE(); return *this; } public: mpz(int v):m_val(v), m_kind(mpz_small), m_owner(mpz_self), m_ptr(nullptr) {} mpz():m_val(0), m_kind(mpz_small), m_owner(mpz_self), m_ptr(nullptr) {} mpz(mpz_type* ptr): m_val(0), m_kind(mpz_small), m_owner(mpz_ext), m_ptr(ptr) { SASSERT(ptr);} mpz(mpz && other) : m_val(other.m_val), m_kind(mpz_small), m_owner(mpz_self), m_ptr(nullptr) { std::swap(m_ptr, other.m_ptr); unsigned o = m_owner; m_owner = other.m_owner; other.m_owner = o; unsigned k = m_kind; m_kind = other.m_kind; other.m_kind = k; } void swap(mpz & other) { std::swap(m_val, other.m_val); std::swap(m_ptr, other.m_ptr); unsigned o = m_owner; m_owner = other.m_owner; other.m_owner = o; unsigned k = m_kind; m_kind = other.m_kind; other.m_kind = k; } }; #ifndef _MP_GMP class mpz_stack : public mpz { static const unsigned capacity = 8; unsigned char m_bytes[sizeof(mpz_cell) + sizeof(digit_t) * capacity]; public: mpz_stack():mpz(reinterpret_cast(m_bytes)) { m_ptr->m_capacity = capacity; } }; #else class mpz_stack : public mpz {}; #endif inline void swap(mpz & m1, mpz & m2) { m1.swap(m2); } template class mpz_manager { mutable small_object_allocator m_allocator; #ifndef SINGLE_THREAD mutable std::recursive_mutex m_lock; #define MPZ_BEGIN_CRITICAL() if (SYNCH) m_lock.lock() #define MPZ_END_CRITICAL() if (SYNCH) m_lock.unlock() #else #define MPZ_BEGIN_CRITICAL() {} #define MPZ_END_CRITICAL() {} #endif mutable mpn_manager m_mpn_manager; #ifndef _MP_GMP unsigned m_init_cell_capacity; mpz m_int_min; static unsigned cell_size(unsigned capacity) { return sizeof(mpz_cell) + sizeof(digit_t) * capacity; } mpz_cell * allocate(unsigned capacity); // make sure that n is a big number and has capacity equal to at least c. void allocate_if_needed(mpz & n, unsigned c) { if (m_init_cell_capacity > c) c = m_init_cell_capacity; if (n.m_ptr == nullptr || capacity(n) < c) { deallocate(n); n.m_val = 1; n.m_kind = mpz_large; n.m_owner = mpz_self; n.m_ptr = allocate(c); } else { n.m_kind = mpz_large; } } void deallocate(bool is_heap, mpz_cell * ptr); // Expand capacity of a while preserving its content. void ensure_capacity(mpz & a, unsigned sz); void normalize(mpz & a); void clear(mpz& n) { reset(n); } /** \brief Set \c a with the value stored at src, and the given sign. \c sz is an overapproximation of the size of the number stored at \c src. */ void set(mpz_cell& src, mpz & a, int sign, unsigned sz); #else // GMP code mutable mpz_t m_tmp, m_tmp2; mutable mpz_t m_two32; mpz_t * m_arg[2]; mutable mpz_t m_uint64_max; mutable mpz_t m_int64_max; mutable mpz_t m_int64_min; mpz_t * allocate() { MPZ_BEGIN_CRITICAL(); mpz_t * cell = reinterpret_cast(m_allocator.allocate(sizeof(mpz_t))); MPZ_END_CRITICAL(); mpz_init(*cell); return cell; } void deallocate(bool is_heap, mpz_t * ptr) { mpz_clear(*ptr); if (is_heap) { MPZ_BEGIN_CRITICAL(); m_allocator.deallocate(sizeof(mpz_t), ptr); MPZ_END_CRITICAL(); } } void clear(mpz& n) { if (n.m_ptr) { mpz_clear(*n.m_ptr); }} #endif void deallocate(mpz& n) { if (n.m_ptr) { deallocate(n.m_owner == mpz_self, n.m_ptr); n.m_ptr = nullptr; n.m_kind = mpz_small; } } mpz m_two64; static int64_t i64(mpz const & a) { return static_cast(a.m_val); } void set_big_i64(mpz & c, int64_t v); void set_i64(mpz & c, int64_t v) { if (v >= INT_MIN && v <= INT_MAX) { c.m_val = static_cast(v); c.m_kind = mpz_small; } else { set_big_i64(c, v); } } void set_big_ui64(mpz & c, uint64_t v); #ifndef _MP_GMP static unsigned capacity(mpz const & c) { return c.m_ptr->m_capacity; } static unsigned size(mpz const & c) { return c.m_ptr->m_size; } static digit_t * digits(mpz const & c) { return c.m_ptr->m_digits; } // Return true if the absolute value fits in a UINT64 static bool is_abs_uint64(mpz const & a) { if (is_small(a)) return true; if (sizeof(digit_t) == sizeof(uint64_t)) return size(a) <= 1; else return size(a) <= 2; } // CAST the absolute value into a UINT64 static uint64_t big_abs_to_uint64(mpz const & a) { SASSERT(is_abs_uint64(a)); SASSERT(!is_small(a)); if (a.m_ptr->m_size == 1) return digits(a)[0]; if (sizeof(digit_t) == sizeof(uint64_t)) // 64-bit machine return digits(a)[0]; else // 32-bit machine return ((static_cast(digits(a)[1]) << 32) | (static_cast(digits(a)[0]))); } class sign_cell { static const unsigned capacity = 2; unsigned char m_bytes[sizeof(mpz_cell) + sizeof(digit_t) * capacity]; mpz m_local; mpz const& m_a; int m_sign; mpz_cell* m_cell; public: sign_cell(mpz_manager& m, mpz const& a); int sign() { return m_sign; } mpz_cell const* cell() { return m_cell; } }; void get_sign_cell(mpz const & a, int & sign, mpz_cell * & cell, mpz_cell* reserve) { if (is_small(a)) { if (a.m_val == INT_MIN) { sign = -1; cell = m_int_min.m_ptr; } else { cell = reserve; cell->m_size = 1; if (a.m_val < 0) { sign = -1; cell->m_digits[0] = -a.m_val; } else { sign = 1; cell->m_digits[0] = a.m_val; } } } else { sign = a.m_val; cell = a.m_ptr; } } #else // GMP code class ensure_mpz_t { mpz_t m_local; mpz_t* m_result; public: ensure_mpz_t(mpz const& a); ~ensure_mpz_t(); mpz_t& operator()() { return *m_result; } }; void mk_big(mpz & a) { if (a.m_ptr == nullptr) { a.m_val = 0; a.m_ptr = allocate(); a.m_owner = mpz_self; } a.m_kind = mpz_large; } #endif #ifndef _MP_GMP template void big_add_sub(mpz const & a, mpz const & b, mpz & c); #endif void big_add(mpz const & a, mpz const & b, mpz & c); void big_sub(mpz const & a, mpz const & b, mpz & c); void big_mul(mpz const & a, mpz const & b, mpz & c); void big_set(mpz & target, mpz const & source); #ifndef _MP_GMP #define QUOT_ONLY 0 #define REM_ONLY 1 #define QUOT_AND_REM 2 #define qr_mode int template void quot_rem_core(mpz const & a, mpz const & b, mpz & q, mpz & r); #endif void big_div_rem(mpz const & a, mpz const & b, mpz & q, mpz & r); void big_div(mpz const & a, mpz const & b, mpz & c); void big_rem(mpz const & a, mpz const & b, mpz & c); int big_compare(mpz const & a, mpz const & b); public: unsigned size_info(mpz const & a); struct sz_lt; static bool precise() { return true; } static bool field() { return false; } typedef mpz numeral; mpz_manager(); ~mpz_manager(); static bool is_small(mpz const & a) { return a.m_kind == mpz_small; } static mpz mk_z(int val) { return mpz(val); } void del(mpz & a); void add(mpz const & a, mpz const & b, mpz & c); void sub(mpz const & a, mpz const & b, mpz & c); void inc(mpz & a) { add(a, mpz(1), a); } void dec(mpz & a) { add(a, mpz(-1), a); } void mul(mpz const & a, mpz const & b, mpz & c); // d <- a + b*c void addmul(mpz const & a, mpz const & b, mpz const & c, mpz & d); // d <- a - b*c void submul(mpz const & a, mpz const & b, mpz const & c, mpz & d); void machine_div_rem(mpz const & a, mpz const & b, mpz & q, mpz & r); void machine_div(mpz const & a, mpz const & b, mpz & c); void rem(mpz const & a, mpz const & b, mpz & c); void div_gcd(mpz const & a, mpz const & b, mpz & c); void div(mpz const & a, mpz const & b, mpz & c); void mod(mpz const & a, mpz const & b, mpz & c); void neg(mpz & a); void abs(mpz & a); static bool is_pos(mpz const & a) { return sign(a) > 0; } static bool is_neg(mpz const & a) { return sign(a) < 0; } static bool is_zero(mpz const & a) { return sign(a) == 0; } static int sign(mpz const & a) { #ifndef _MP_GMP return a.m_val; #else if (is_small(a)) return a.m_val; else return mpz_sgn(*a.m_ptr); #endif } static bool is_nonpos(mpz const & a) { return !is_pos(a); } static bool is_nonneg(mpz const & a) { return !is_neg(a); } bool eq(mpz const & a, mpz const & b) { if (is_small(a) && is_small(b)) { return a.m_val == b.m_val; } else { return big_compare(a, b) == 0; } } bool lt(mpz const& a, int b) { if (is_small(a)) { return a.m_val < b; } else { return lt(a, mpz(b)); } } bool lt(mpz const & a, mpz const & b) { if (is_small(a) && is_small(b)) { return a.m_val < b.m_val; } else { return big_compare(a, b) < 0; } } bool neq(mpz const & a, mpz const & b) { return !eq(a, b); } bool gt(mpz const & a, mpz const & b) { return lt(b, a); } bool ge(mpz const & a, mpz const & b) { return !lt(a, b); } bool le(mpz const & a, mpz const & b) { return !lt(b, a); } void gcd(mpz const & a, mpz const & b, mpz & c); void gcd(unsigned sz, mpz const * as, mpz & g); /** \brief Extended Euclid: r1*a + r2*b = g */ void gcd(mpz const & r1, mpz const & r2, mpz & a, mpz & b, mpz & g); void lcm(mpz const & a, mpz const & b, mpz & c); /** \brief Return true if a | b */ bool divides(mpz const & a, mpz const & b); // not a field void inv(mpz & a) { SASSERT(false); } void bitwise_or(mpz const & a, mpz const & b, mpz & c); void bitwise_and(mpz const & a, mpz const & b, mpz & c); void bitwise_xor(mpz const & a, mpz const & b, mpz & c); void bitwise_not(unsigned sz, mpz const & a, mpz & c); void set(mpz & target, mpz const & source) { if (is_small(source)) { target.m_val = source.m_val; target.m_kind = mpz_small; } else { big_set(target, source); } } void set(mpz & target, mpz && source) { target.m_val = source.m_val; std::swap(target.m_ptr, source.m_ptr); auto o = target.m_owner; target.m_owner = source.m_owner; source.m_owner = o; auto k = target.m_kind; target.m_kind = source.m_kind; source.m_kind = k; } void set(mpz & a, int val) { a.m_val = val; a.m_kind = mpz_small; } void set(mpz & a, unsigned val) { if (val <= INT_MAX) set(a, static_cast(val)); else set(a, static_cast(static_cast(val))); } void set(mpz & a, char const * val); void set(mpz & a, int64_t val) { set_i64(a, val); } void set(mpz & a, uint64_t val) { if (val < INT_MAX) { a.m_val = static_cast(val); a.m_kind = mpz_small; } else { set_big_ui64(a, val); } } void set_digits(mpz & target, unsigned sz, digit_t const * digits); mpz dup(const mpz & source) { mpz temp; set(temp, source); return temp; } // deallocates any memory. void reset(mpz & a); void swap(mpz & a, mpz & b) { std::swap(a.m_val, b.m_val); std::swap(a.m_ptr, b.m_ptr); auto o = a.m_owner; a.m_owner = b.m_owner; b.m_owner = o; auto k = a.m_kind; a.m_kind = b.m_kind; b.m_kind = k; } bool is_uint64(mpz const & a) const; bool is_int64(mpz const & a) const; uint64_t get_uint64(mpz const & a) const; int64_t get_int64(mpz const & a) const; bool is_uint(mpz const & a) const { return is_uint64(a) && get_uint64(a) < UINT_MAX; } unsigned get_uint(mpz const & a) const { SASSERT(is_uint(a)); return static_cast(get_uint64(a)); } bool is_int(mpz const & a) const { return is_int64(a) && INT_MIN < get_int64(a) && get_int64(a) < INT_MAX; } int get_int(mpz const & a) const { SASSERT(is_int(a)); return static_cast(get_int64(a)); } double get_double(mpz const & a) const; std::string to_string(mpz const & a) const; void display(std::ostream & out, mpz const & a) const; /** \brief Display mpz number in SMT 2.0 format. If decimal == true, then ".0" is appended. */ void display_smt2(std::ostream & out, mpz const & a, bool decimal) const; /** \brief Displays the num_bits least significant bits of a mpz number in hexadecimal format. num_bits must be divisible by 4. */ void display_hex(std::ostream & out, mpz const & a, unsigned num_bits) const; /** \brief Displays the num_bits least significant bits of a mpz number in binary format. */ void display_bin(std::ostream & out, mpz const & a, unsigned num_bits) const; static unsigned hash(mpz const & a); static bool is_one(mpz const & a) { #ifndef _MP_GMP return is_small(a) && a.m_val == 1; #else if (is_small(a)) return a.m_val == 1; return mpz_cmp_si(*a.m_ptr, 1) == 0; #endif } static bool is_minus_one(mpz const & a) { #ifndef _MP_GMP return is_small(a) && a.m_val == -1; #else if (is_small(a)) return a.m_val == -1; return mpz_cmp_si(*a.m_ptr, -1) == 0; #endif } void power(mpz const & a, unsigned p, mpz & b); bool is_power_of_two(mpz const & a); bool is_power_of_two(mpz const & a, unsigned & shift); void machine_div2k(mpz & a, unsigned k); void machine_div2k(mpz const & a, unsigned k, mpz & r) { set(r, a); machine_div2k(r, k); } void mul2k(mpz & a, unsigned k); void mul2k(mpz const & a, unsigned k, mpz & r) { set(r, a); mul2k(r, k); } /** \brief Return largest k s.t. n is a multiple of 2^k */ unsigned power_of_two_multiple(mpz const & n); /** \brief Return the position of the most significant bit. Return 0 if the number is negative */ unsigned log2(mpz const & n); /** \brief log2(-n) Return 0 if the number is nonegative */ unsigned mlog2(mpz const & n); /** \brief Return the bit-size of n. This method is mainly used for collecting statistics. */ unsigned bitsize(mpz const & n); /** \brief Return true if the number is a perfect square, and store the square root in 'root'. If the number n is positive and the result is false, then root will contain the smallest integer r such that r*r > n. */ bool is_perfect_square(mpz const & a, mpz & root); /** \brief Return the biggest k s.t. 2^k <= a. \remark Return 0 if a is not positive. */ unsigned prev_power_of_two(mpz const & a) { return log2(a); } /** \brief Return true if a^{1/n} is an integer, and store the result in a. Otherwise return false, and update a with the smallest integer r such that r*r > n. \remark This method assumes that if n is even, then a is nonegative */ bool root(mpz & a, unsigned n); bool root(mpz const & a, unsigned n, mpz & r) { set(r, a); return root(r, n); } bool is_even(mpz const & a) { if (is_small(a)) return !(a.m_val & 0x1); #ifndef _MP_GMP return !(0x1 & digits(a)[0]); #else return mpz_even_p(*a.m_ptr); #endif } bool is_odd(mpz const & n) { return !is_even(n); } // Store the digits of n into digits, and return the sign. bool decompose(mpz const & n, svector & digits); }; #ifndef SINGLE_THREAD typedef mpz_manager synch_mpz_manager; #else typedef mpz_manager synch_mpz_manager; #endif typedef mpz_manager unsynch_mpz_manager; typedef _scoped_numeral scoped_mpz; typedef _scoped_numeral scoped_synch_mpz; typedef _scoped_numeral_vector scoped_mpz_vector; #endif /* MPZ_H_ */ z3-z3-4.8.7/src/util/mpzzp.h000066400000000000000000000255001356505360400155260ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: mpzzp.h Abstract: Combines Z ring, GF(p) finite field, and Z_p ring (when p is not a prime) in a single manager; That is, the manager may be dynamically configured to be Z Ring, GF(p), etc. Author: Leonardo 2012-01-17. Revision History: This code is based on mpzp.h. In the future, it will replace it. --*/ #ifndef MPZZP_H_ #define MPZZP_H_ #include "util/mpz.h" class mpzzp_manager { typedef unsynch_mpz_manager numeral_manager; numeral_manager & m_manager; bool m_z; // instead the usual [0..p) we will keep the numbers in [lower, upper] mpz m_p, m_lower, m_upper; bool m_p_prime; mpz m_inv_tmp1, m_inv_tmp2, m_inv_tmp3; mpz m_div_tmp; bool is_p_normalized_core(mpz const & x) const { return m().ge(x, m_lower) && m().le(x, m_upper); } void setup_p() { SASSERT(m().is_pos(m_p) && !m().is_one(m_p)); bool even = m().is_even(m_p); m().div(m_p, 2, m_upper); m().set(m_lower, m_upper); m().neg(m_lower); if (even) { m().inc(m_lower); } TRACE("mpzzp", tout << "lower: " << m_manager.to_string(m_lower) << ", upper: " << m_manager.to_string(m_upper) << "\n";); } void p_normalize_core(mpz & x) { SASSERT(!m_z); m().rem(x, m_p, x); if (m().gt(x, m_upper)) { m().sub(x, m_p, x); } else { if (m().lt(x, m_lower)) { m().add(x, m_p, x); } } SASSERT(is_p_normalized(x)); } public: typedef mpz numeral; static bool precise() { return true; } bool field() { return !m_z && m_p_prime; } bool finite() const { return !m_z; } bool modular() const { return !m_z; } mpzzp_manager(numeral_manager & _m): m_manager(_m), m_z(true) { } mpzzp_manager(numeral_manager & _m, mpz const & p, bool prime = true): m_manager(_m), m_z(false) { m().set(m_p, p); setup_p(); } mpzzp_manager(numeral_manager & _m, uint64_t p, bool prime = true): m_manager(_m), m_z(false) { m().set(m_p, p); setup_p(); } ~mpzzp_manager() { m().del(m_p); m().del(m_lower); m().del(m_upper); m().del(m_inv_tmp1); m().del(m_inv_tmp2); m().del(m_inv_tmp3); m().del(m_div_tmp); } bool is_p_normalized(mpz const & x) const { return m_z || is_p_normalized_core(x); } void p_normalize(mpz & x) { if (!m_z) p_normalize_core(x); SASSERT(is_p_normalized(x)); } numeral_manager & m() const { return m_manager; } mpz const & p() const { return m_p; } void set_z() { m_z = true; } void set_zp(mpz const & new_p) { m_z = false; m_p_prime = true; m().set(m_p, new_p); setup_p(); } void set_zp(uint64_t new_p) { m_z = false; m_p_prime = true; m().set(m_p, new_p); setup_p(); } // p = p^2 void set_p_sq() { SASSERT(!m_z); m_p_prime = false; m().mul(m_p, m_p, m_p); setup_p(); } void set_zp_swap(mpz & new_p) { SASSERT(!m_z); m().swap(m_p, new_p); setup_p(); } void reset(mpz & a) { m().reset(a); } bool is_small(mpz const & a) { return m().is_small(a); } void del(mpz & a) { m().del(a); } void neg(mpz & a) { m().neg(a); p_normalize(a); } void abs(mpz & a) { m().abs(a); p_normalize(a); } bool is_zero(mpz const & a) { SASSERT(is_p_normalized(a)); return numeral_manager::is_zero(a); } bool is_one(mpz const & a) { SASSERT(is_p_normalized(a)); return numeral_manager::is_one(a); } bool is_pos(mpz const & a) { SASSERT(is_p_normalized(a)); return numeral_manager::is_pos(a); } bool is_neg(mpz const & a) { SASSERT(is_p_normalized(a)); return numeral_manager::is_neg(a); } bool is_nonpos(mpz const & a) { SASSERT(is_p_normalized(a)); return numeral_manager::is_nonpos(a); } bool is_nonneg(mpz const & a) { SASSERT(is_p_normalized(a)); return numeral_manager::is_nonneg(a); } bool is_minus_one(mpz const & a) { SASSERT(is_p_normalized(a)); return numeral_manager::is_minus_one(a); } bool eq(mpz const & a, mpz const & b) { SASSERT(is_p_normalized(a) && is_p_normalized(b)); return m().eq(a, b); } bool lt(mpz const & a, mpz const & b) { SASSERT(is_p_normalized(a) && is_p_normalized(b)); return m().lt(a, b); } bool le(mpz const & a, mpz const & b) { SASSERT(is_p_normalized(a) && is_p_normalized(b)); return m().le(a, b); } bool gt(mpz const & a, mpz const & b) { SASSERT(is_p_normalized(a) && is_p_normalized(b)); return m().gt(a, b); } bool ge(mpz const & a, mpz const & b) { SASSERT(is_p_normalized(a) && is_p_normalized(b)); return m().ge(a, b); } std::string to_string(mpz const & a) const { SASSERT(is_p_normalized(a)); return m().to_string(a); } void display(std::ostream & out, mpz const & a) const { m().display(out, a); } void add(mpz const & a, mpz const & b, mpz & c) { SASSERT(is_p_normalized(a) && is_p_normalized(b)); m().add(a, b, c); p_normalize(c); } void sub(mpz const & a, mpz const & b, mpz & c) { SASSERT(is_p_normalized(a) && is_p_normalized(b)); m().sub(a, b, c); p_normalize(c); } void inc(mpz & a) { SASSERT(is_p_normalized(a)); m().inc(a); p_normalize(a); } void dec(mpz & a) { SASSERT(is_p_normalized(a)); m().dec(a); p_normalize(a); } void mul(mpz const & a, mpz const & b, mpz & c) { SASSERT(is_p_normalized(a) && is_p_normalized(b)); m().mul(a, b, c); p_normalize(c); } void addmul(mpz const & a, mpz const & b, mpz const & c, mpz & d) { SASSERT(is_p_normalized(a) && is_p_normalized(b) && is_p_normalized(c)); m().addmul(a, b, c, d); p_normalize(d); } // d <- a - b*c void submul(mpz const & a, mpz const & b, mpz const & c, mpz & d) { SASSERT(is_p_normalized(a)); SASSERT(is_p_normalized(b)); SASSERT(is_p_normalized(c)); m().submul(a, b, c, d); p_normalize(d); } void inv(mpz & a) { if (m_z) { UNREACHABLE(); } else { SASSERT(!is_zero(a)); // eulers theorem a^(p - 2), but gcd could be more efficient // a*t1 + p*t2 = 1 => a*t1 = 1 (mod p) => t1 is the inverse (t3 == 1) TRACE("mpzp_inv_bug", tout << "a: " << m().to_string(a) << ", p: " << m().to_string(m_p) << "\n";); p_normalize(a); TRACE("mpzp_inv_bug", tout << "after normalization a: " << m().to_string(a) << "\n";); m().gcd(a, m_p, m_inv_tmp1, m_inv_tmp2, m_inv_tmp3); TRACE("mpzp_inv_bug", tout << "tmp1: " << m().to_string(m_inv_tmp1) << "\ntmp2: " << m().to_string(m_inv_tmp2) << "\ntmp3: " << m().to_string(m_inv_tmp3) << "\n";); p_normalize(m_inv_tmp1); m().swap(a, m_inv_tmp1); SASSERT(m().is_one(m_inv_tmp3)); // otherwise p is not prime and inverse is not defined } } void swap(mpz & a, mpz & b) { SASSERT(is_p_normalized(a) && is_p_normalized(b)); m().swap(a, b); } bool divides(mpz const & a, mpz const & b) { return (field() && !is_zero(a)) || m().divides(a, b); } // a/b = a*inv(b) void div(mpz const & a, mpz const & b, mpz & c) { if (m_z) { return m().div(a, b, c); } else { SASSERT(m_p_prime); SASSERT(is_p_normalized(a)); m().set(m_div_tmp, b); inv(m_div_tmp); mul(a, m_div_tmp, c); SASSERT(is_p_normalized(c)); } } static unsigned hash(mpz const & a) { return numeral_manager::hash(a); } void gcd(mpz const & a, mpz const & b, mpz & c) { SASSERT(is_p_normalized(a) && is_p_normalized(b)); m().gcd(a, b, c); SASSERT(is_p_normalized(c)); } void gcd(unsigned sz, mpz const * as, mpz & g) { m().gcd(sz, as, g); SASSERT(is_p_normalized(g)); } void gcd(mpz const & r1, mpz const & r2, mpz & a, mpz & b, mpz & g) { SASSERT(is_p_normalized(r1) && is_p_normalized(r2)); m().gcd(r1, r2, a, b, g); p_normalize(a); p_normalize(b); } void set(mpz & a, mpz & val) { m().set(a, val); p_normalize(a); } void set(mpz & a, int val) { m().set(a, val); p_normalize(a); } void set(mpz & a, unsigned val) { m().set(a, val); p_normalize(a); } void set(mpz & a, char const * val) { m().set(a, val); p_normalize(a); } void set(mpz & a, int64_t val) { m().set(a, val); p_normalize(a); } void set(mpz & a, uint64_t val) { m().set(a, val); p_normalize(a); } void set(mpz & a, mpz const & val) { m().set(a, val); p_normalize(a); } bool is_uint64(mpz & a) const { const_cast(this)->p_normalize(a); return m().is_uint64(a); } bool is_int64(mpz & a) const { const_cast(this)->p_normalize(a); return m().is_int64(a); } uint64_t get_uint64(mpz & a) const { const_cast(this)->p_normalize(a); return m().get_uint64(a); } int64_t get_int64(mpz & a) const { const_cast(this)->p_normalize(a); return m().get_int64(a); } double get_double(mpz & a) const { const_cast(this)->p_normalize(a); return m().get_double(a); } void power(mpz const & a, unsigned k, mpz & b) { SASSERT(is_p_normalized(a)); unsigned mask = 1; mpz power; set(power, a); set(b, 1); while (mask <= k) { if (mask & k) mul(b, power, b); mul(power, power, power); mask = mask << 1; } del(power); } bool is_perfect_square(mpz const & a, mpz & root) { if (m_z) { return m().is_perfect_square(a, root); } else { NOT_IMPLEMENTED_YET(); return false; } } bool is_uint64(mpz const & a) const { return m().is_uint64(a); } bool is_int64(mpz const & a) const { return m().is_int64(a); } uint64_t get_uint64(mpz const & a) const { return m().get_uint64(a); } int64_t get_int64(mpz const & a) const { return m().get_int64(a); } void mul2k(mpz & a, unsigned k) { m().mul2k(a, k); p_normalize(a); } void mul2k(mpz const & a, unsigned k, mpz & r) { m().mul2k(a, k, r); p_normalize(r); } unsigned power_of_two_multiple(mpz const & n) { return m().power_of_two_multiple(n); } unsigned log2(mpz const & n) { return m().log2(n); } unsigned mlog2(mpz const & n) { return m().mlog2(n); } void machine_div2k(mpz & a, unsigned k) { m().machine_div2k(a, k); SASSERT(is_p_normalized(a)); } void machine_div2k(mpz const & a, unsigned k, mpz & r) { m().machine_div2k(a, k, r); SASSERT(is_p_normalized(r)); } bool root(mpz & a, unsigned n) { SASSERT(!modular()); return m().root(a, n); } bool root(mpz const & a, unsigned n, mpz & r) { SASSERT(!modular()); return m().root(a, n, r); } }; #endif z3-z3-4.8.7/src/util/mutex.h000066400000000000000000000015351356505360400155120ustar00rootroot00000000000000/*++ Copyright (c) 2019 Microsoft Corporation Module Name: mutex.h Abstract: Auxiliary macros for mutual exclusion --*/ #pragma once #ifdef SINGLE_THREAD template using atomic = T; struct mutex { void lock() {} void unlock() {} }; struct lock_guard { lock_guard(mutex &) {} }; #define DECLARE_MUTEX(name) mutex *name = nullptr #define DECLARE_INIT_MUTEX(name) mutex *name = nullptr #define ALLOC_MUTEX(name) (void)0 #define DEALLOC_MUTEX(name) (void)0 #else #include #include template using atomic = std::atomic; typedef std::mutex mutex; typedef std::lock_guard lock_guard; #define DECLARE_MUTEX(name) mutex *name = nullptr #define DECLARE_INIT_MUTEX(name) mutex *name = new mutex #define ALLOC_MUTEX(name) name = alloc(mutex) #define DEALLOC_MUTEX(name) dealloc(name) #endif z3-z3-4.8.7/src/util/nat_set.h000066400000000000000000000033621356505360400160050ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: nat_set.h Abstract: Set of natural number with fast reset method. Author: Leonardo de Moura (leonardo) 2007-03-27. Revision History: --*/ #ifndef NAT_SET_H_ #define NAT_SET_H_ #include #include "util/vector.h" class nat_set { unsigned m_curr_timestamp; svector m_timestamps; public: nat_set(unsigned s = 0): m_curr_timestamp(0), m_timestamps() { if (s > 0) { m_timestamps.resize(s, 0); } } // A nat_set is a function from [0..s-1] -> boolean. // This method sets the domain of this function. void set_domain(unsigned s) { m_timestamps.resize(s, 0); } unsigned get_domain() const { return m_timestamps.size(); } // Assure that v is in the domain of the set. void assure_domain(unsigned v) { if (v >= get_domain()) { set_domain(v+1); } } bool contains(unsigned v) const { return m_timestamps[v] > m_curr_timestamp; } void insert(unsigned v) { m_timestamps[v] = m_curr_timestamp + 1; } void remove(unsigned v) { m_timestamps[v] = m_curr_timestamp; } void reset() { m_curr_timestamp++; if (m_curr_timestamp == UINT_MAX) { m_timestamps.fill(0); m_curr_timestamp = 0; } } bool empty() const { svector::const_iterator it = m_timestamps.begin(); svector::const_iterator end = m_timestamps.end(); for (; it != end; ++it) { if (*it > m_curr_timestamp) { return false; } } return true; } }; #endif /* NAT_SET_H_ */ z3-z3-4.8.7/src/util/numeral_buffer.h000066400000000000000000000033771356505360400173520ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: numeral_buffer.h Abstract: Basic buffer for managing big nums. Author: Leonardo de Moura (leonardo) 2011-06-18. Revision History: --*/ #ifndef NUMERAL_BUFFER_H_ #define NUMERAL_BUFFER_H_ #include "util/vector.h" template class numeral_buffer { NumeralManager & m_manager; svector m_buffer; public: typedef Numeral numeral; typedef Numeral data; typedef NumeralManager manager; numeral_buffer(NumeralManager & m):m_manager(m) {} ~numeral_buffer() { reset(); } NumeralManager & m() const { return m_manager; } unsigned size() const { return m_buffer.size(); } bool empty() const { return m_buffer.empty(); } void push_back(Numeral const & num) { m_buffer.push_back(Numeral()); m().set(m_buffer.back(), num); } void pop_back() { m().del(m_buffer.back()); m_buffer.pop_back(); } Numeral & back() { return m_buffer.back(); } Numeral const & back() const { return m_buffer.back(); } Numeral const & operator[](unsigned idx) const { return m_buffer[idx]; } Numeral & operator[](unsigned idx) { return m_buffer[idx]; } void reset() { typename vector::iterator it = m_buffer.begin(); typename vector::iterator end = m_buffer.end(); for (; it != end; ++it) m().del(*it); m_buffer.reset(); } Numeral * c_ptr() { return m_buffer.c_ptr(); } void reserve(unsigned sz) { m_buffer.reserve(sz); } void swap(svector & other) { m_buffer.swap(other); } }; #endif z3-z3-4.8.7/src/util/obj_hashtable.h000066400000000000000000000144671356505360400171450ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: obj_hashtable.h Abstract: Author: Leonardo de Moura (leonardo) 2008-02-16. Revision History: --*/ #ifndef OBJ_HASHTABLE_H_ #define OBJ_HASHTABLE_H_ #include "util/hash.h" #include "util/hashtable.h" /** \brief Special entry for a hashtable of obj pointers (i.e., objects that have a hash() method). This entry uses 0x0 and 0x1 to represent HT_FREE and HT_DELETED. */ template class obj_hash_entry { T * m_ptr; public: typedef T * data; obj_hash_entry():m_ptr(nullptr) {} unsigned get_hash() const { return m_ptr->hash(); } bool is_free() const { return m_ptr == nullptr; } bool is_deleted() const { return m_ptr == reinterpret_cast(1); } bool is_used() const { return m_ptr != reinterpret_cast(0) && m_ptr != reinterpret_cast(1); } T * get_data() const { return m_ptr; } T * & get_data() { return m_ptr; } void set_data(T * d) { m_ptr = d; } void set_hash(unsigned h) { SASSERT(h == m_ptr->hash()); } void mark_as_deleted() { m_ptr = reinterpret_cast(1); } void mark_as_free() { m_ptr = nullptr; } }; template class obj_hashtable : public core_hashtable, obj_ptr_hash, ptr_eq > { public: obj_hashtable(unsigned initial_capacity = DEFAULT_HASHTABLE_INITIAL_CAPACITY): core_hashtable, obj_ptr_hash, ptr_eq >(initial_capacity) {} }; template class obj_map { public: struct key_data { Key * m_key; Value m_value; key_data():m_key(nullptr) { } key_data(Key * k): m_key(k) { } key_data(Key * k, Value const & v): m_key(k), m_value(v) { } key_data(Key * k, Value && v) : m_key(k), m_value(std::move(v)) { } Value const & get_value() const { return m_value; } Key & get_key () const { return *m_key; } unsigned hash() const { return m_key->hash(); } bool operator==(key_data const & other) const { return m_key == other.m_key; } }; class obj_map_entry { key_data m_data; public: typedef key_data data; obj_map_entry() {} unsigned get_hash() const { return m_data.hash(); } bool is_free() const { return m_data.m_key == nullptr; } bool is_deleted() const { return m_data.m_key == reinterpret_cast(1); } bool is_used() const { return m_data.m_key != reinterpret_cast(0) && m_data.m_key != reinterpret_cast(1); } key_data const & get_data() const { return m_data; } key_data & get_data() { return m_data; } void set_data(key_data && d) { m_data = std::move(d); } void set_hash(unsigned h) { SASSERT(h == m_data.hash()); } void mark_as_deleted() { m_data.m_key = reinterpret_cast(1); } void mark_as_free() { m_data.m_key = nullptr; } }; typedef core_hashtable, default_eq > table; table m_table; public: obj_map(): m_table(DEFAULT_HASHTABLE_INITIAL_CAPACITY) {} typedef typename table::iterator iterator; typedef typename table::data data; typedef typename table::entry entry; typedef Key key; typedef Value value; void reset() { m_table.reset(); } void finalize() { m_table.finalize(); } bool empty() const { return m_table.empty(); } unsigned size() const { return m_table.size(); } unsigned capacity() const { return m_table.capacity(); } iterator begin() const { return m_table.begin(); } iterator end() const { return m_table.end(); } void insert(Key * const k, Value const & v) { m_table.insert(key_data(k, v)); } void insert(Key * const k, Value && v) { m_table.insert(key_data(k, std::move(v))); } key_data const & insert_if_not_there(Key * k, Value const & v) { return m_table.insert_if_not_there(key_data(k, v)); } obj_map_entry * insert_if_not_there2(Key * k, Value const & v) { return m_table.insert_if_not_there2(key_data(k, v)); } obj_map_entry * find_core(Key * k) const { return m_table.find_core(key_data(k)); } bool find(Key * const k, Value & v) const { obj_map_entry * e = find_core(k); if (e) { v = e->get_data().m_value; } return (nullptr != e); } value const & find(key * k) const { obj_map_entry * e = find_core(k); SASSERT(e); return e->get_data().m_value; } value & find(key * k) { obj_map_entry * e = find_core(k); SASSERT(e); return e->get_data().m_value; } value const & operator[](key * k) const { return find(k); } value & operator[](key * k) { return find(k); } iterator find_iterator(Key * k) const { return m_table.find(key_data(k)); } bool contains(Key * k) const { return find_core(k) != nullptr; } void remove(Key * k) { m_table.remove(key_data(k)); } void erase(Key * k) { remove(k); } unsigned long long get_num_collision() const { return m_table.get_num_collision(); } void get_collisions(Key * k, vector& collisions) { vector cs; m_table.get_collisions(key_data(k), cs); for (key_data const& kd : cs) { collisions.push_back(kd.m_key); } } void swap(obj_map & other) { m_table.swap(other.m_table); } }; /** \brief Reset and deallocate the values stored in a mapping of the form obj_map */ template void reset_dealloc_values(obj_map & m) { for (auto & kv : m) { dealloc(kv.m_value); } m.reset(); } /** \brief Remove the key k from the mapping m, and delete the value associated with k. */ template void erase_dealloc_value(obj_map & m, Key * k) { Value * v = 0; bool contains = m.find(k, v); m.erase(k); if (contains) { dealloc(v); } } #endif /* OBJ_HASHTABLE_H_ */ z3-z3-4.8.7/src/util/obj_mark.h000066400000000000000000000024101356505360400161250ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: obj_mark.h Abstract: A mapping from object to boolean (for objects that can be mapped to unsigned integers). Author: Leonardo de Moura (leonardo) 2008-01-02. Revision History: --*/ #ifndef OBJ_MARK_H_ #define OBJ_MARK_H_ #include "util/bit_vector.h" template struct default_t2uint { unsigned operator()(T const & obj) const { return obj.get_id(); } }; template > class obj_mark { T2UInt m_proc; BV m_marks; public: obj_mark(T2UInt const & p = T2UInt()):m_proc(p) {} bool is_marked(T const & obj) const { unsigned id = m_proc(obj); return id < m_marks.size() && m_marks.get(id); } bool is_marked(T * obj) const { return is_marked(*obj); } void mark(T const & obj, bool flag) { unsigned id = m_proc(obj); if (id >= m_marks.size()) { m_marks.resize(id+1, 0); } m_marks.set(id, flag); } void mark(T const * obj, bool flag) { mark(*obj, flag); } void mark(T const & obj) { mark(obj, true); } void mark(T const * obj) { mark(obj, true); } void reset() { m_marks.reset(); } }; #endif /* OBJ_MARK_H_ */ z3-z3-4.8.7/src/util/obj_pair_hashtable.h000066400000000000000000000127661356505360400201600ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: obj_pair_hashtable.h Abstract: Author: Leonardo de Moura (leonardo) 2008-02-19. Revision History: --*/ #ifndef OBJ_PAIR_HASHTABLE_H_ #define OBJ_PAIR_HASHTABLE_H_ #include "util/hash.h" #include "util/hashtable.h" /** \brief Special entry for a hashtable of pairs of obj pointers (i.e., objects that have a hash() method). This entry uses 0x0 and 0x1 to represent HT_FREE and HT_DELETED. */ template class obj_pair_hash_entry { unsigned m_hash; // cached hash code std::pair m_data; public: typedef std::pair data; obj_pair_hash_entry():m_data(static_cast(nullptr),static_cast(nullptr)) {} unsigned get_hash() const { return m_hash; } bool is_free() const { return m_data.first == 0; } bool is_deleted() const { return m_data.first == reinterpret_cast(1); } bool is_used() const { return m_data.first != reinterpret_cast(0) && m_data.first != reinterpret_cast(1); } data const & get_data() const { return m_data; } data & get_data() { return m_data; } void set_data(data const d) { m_data = d; } void set_hash(unsigned h) { m_hash = h; } void mark_as_deleted() { m_data.first = reinterpret_cast(1); } void mark_as_free() { m_data.first = 0; } }; template class obj_pair_hashtable : public core_hashtable, obj_ptr_pair_hash, default_eq > > { public: obj_pair_hashtable(unsigned initial_capacity = DEFAULT_HASHTABLE_INITIAL_CAPACITY): core_hashtable, obj_ptr_pair_hash, default_eq > >(initial_capacity) { } }; template class obj_pair_map { protected: class entry; public: class key_data { Key1 * m_key1; Key2 * m_key2; Value m_value; unsigned m_hash; friend class entry; public: key_data(): m_key1(nullptr), m_key2(nullptr), m_hash(0) { } key_data(Key1 * k1, Key2 * k2): m_key1(k1), m_key2(k2) { m_hash = combine_hash(m_key1->hash(), m_key2->hash()); } key_data(Key1 * k1, Key2 * k2, const Value & v): m_key1(k1), m_key2(k2), m_value(v) { m_hash = combine_hash(m_key1->hash(), m_key2->hash()); } unsigned hash() const { return m_hash; } bool operator==(key_data const & other) const { return m_key1 == other.m_key1 && m_key2 == other.m_key2; } Key1 * get_key1() const { return m_key1; } Key2 * get_key2() const { return m_key2; } Value const & get_value() const { return m_value; } }; protected: class entry { key_data m_data; public: typedef key_data data; entry() {} unsigned get_hash() const { return m_data.hash(); } bool is_free() const { return m_data.m_key1 == nullptr; } bool is_deleted() const { return m_data.m_key1 == reinterpret_cast(1); } bool is_used() const { return m_data.m_key1 != reinterpret_cast(0) && m_data.m_key1 != reinterpret_cast(1); } key_data const & get_data() const { return m_data; } key_data & get_data() { return m_data; } void set_data(key_data const & d) { m_data = d; } void set_hash(unsigned h) { SASSERT(h == m_data.hash()); } void mark_as_deleted() { m_data.m_key1 = reinterpret_cast(1); } void mark_as_free() { m_data.m_key1 = nullptr; } }; typedef core_hashtable, default_eq > table; table m_table; entry * find_core(Key1 * k1, Key2 * k2) const { return m_table.find_core(key_data(k1, k2)); } public: obj_pair_map(): m_table(DEFAULT_HASHTABLE_INITIAL_CAPACITY) {} typedef typename table::iterator iterator; void reset() { m_table.reset(); } bool empty() const { return m_table.empty(); } unsigned size() const { return m_table.size(); } unsigned capacity() const { return m_table.capacity(); } iterator begin() const { return m_table.begin(); } iterator end() const { return m_table.end(); } void insert(Key1 * k1, Key2 * k2, Value const & v) { m_table.insert(key_data(k1, k2, v)); } key_data const & insert_if_not_there(Key1 * k1, Key2 * k2, Value const & v) { return m_table.insert_if_not_there(key_data(k1, k2, v)); } bool find(Key1 * k1, Key2 * k2, Value & v) const { entry * e = find_core(k1, k2); if (e) { v = e->get_data().get_value(); } return (nullptr != e); } Value const & find(Key1 * k1, Key2 * k2) const { entry * e = find_core(k1, k2); return e->get_data().get_value(); } Value const& operator[](std::pair const& key) const { return find(key.first, key.second); } bool contains(Key1 * k1, Key2 * k2) const { return find_core(k1, k2) != nullptr; } bool contains(std::pair const& key) const { return contains(key.first, key.second); } void erase(Key1 * k1, Key2 * k2) { m_table.remove(key_data(k1, k2)); } }; #endif /* OBJ_PAIR_HASHTABLE_H_ */ z3-z3-4.8.7/src/util/obj_pair_set.h000066400000000000000000000031501356505360400170030ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: obj_pair_set.h Abstract: Author: Leonardo de Moura (leonardo) 2011-04-19 Revision History: --*/ #ifndef OBJ_PAIR_SET_H_ #define OBJ_PAIR_SET_H_ #include "util/chashtable.h" template class obj_pair_set { public: typedef std::pair obj_pair; protected: struct hash_proc { unsigned operator()(obj_pair const & p) const { return combine_hash(p.first->hash(), p.second->hash()); } }; struct eq_proc { bool operator()(obj_pair const & p1, obj_pair const & p2) const { return p1 == p2; } }; typedef chashtable set; set m_set; public: obj_pair_set() {} void insert(T1 * t1, T2 * t2) { m_set.insert(obj_pair(t1, t2)); } void insert(obj_pair const & p) { m_set.insert(p); } bool insert_if_not_there(T1 * t1, T2 * t2) { return m_set.insert_if_not_there2(obj_pair(t1, t2)); } bool insert_if_not_there(obj_pair const & p) { return m_set.insert_if_not_there2(p); } void erase(T1 * t1, T2 * t2) { return m_set.erase(obj_pair(t1, t2)); } void erase(obj_pair const & p) { return m_set.erase(p); } bool contains(T1 * t1, T2 * t2) const { return m_set.contains(obj_pair(t1, t2)); } bool contains(obj_pair const & p) const { return m_set.contains(p); } void reset() { m_set.reset(); } bool empty() const { return m_set.empty(); } typedef typename chashtable::iterator iterator; iterator begin() { return m_set.begin(); } iterator end() { return m_set.end(); } }; #endif z3-z3-4.8.7/src/util/obj_ref.h000066400000000000000000000063041356505360400157550ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: obj_ref.h Abstract: Smart pointer. Author: Leonardo de Moura (leonardo) 2008-01-03. Revision History: --*/ #ifndef OBJ_REF_H_ #define OBJ_REF_H_ /** Smart pointer for T objects. TManager must provide the functions: - void dec_ref(T * obj) - void inc_ref(T * obj) */ template class obj_ref { T * m_obj; TManager & m_manager; void dec_ref() { if (m_obj) m_manager.dec_ref(m_obj); } void inc_ref() { if (m_obj) m_manager.inc_ref(m_obj); } public: typedef TManager manager; obj_ref(T * n, TManager & m): m_obj(n), m_manager(m) { inc_ref(); } explicit obj_ref(TManager & m): m_obj(nullptr), m_manager(m) { } obj_ref(obj_ref const & n): m_obj(n.m_obj), m_manager(n.m_manager) { inc_ref(); } obj_ref(obj_ref && other) : m_obj(nullptr), m_manager(other.m_manager) { std::swap(m_obj, other.m_obj); } ~obj_ref() { dec_ref(); } TManager & get_manager() const { return m_manager; } TManager & m() const { return m_manager; } T * operator->() const { return m_obj; } T * get() const { return m_obj; } operator bool() const { return m_obj != nullptr; } bool operator!() const { return m_obj == nullptr; } operator T*() const { return m_obj; } T const & operator*() const { return *m_obj; } obj_ref & operator=(T * n) { if (n) { m_manager.inc_ref(n); } dec_ref(); m_obj = n; return *this; } obj_ref & operator=(obj_ref & n) { SASSERT(&m_manager == &n.m_manager); n.inc_ref(); dec_ref(); m_obj = n.m_obj; return *this; } obj_ref & operator=(obj_ref && n) { SASSERT(&m_manager == &n.m_manager); std::swap(m_obj, n.m_obj); n.reset(); return *this; } void reset() { dec_ref(); m_obj = nullptr; } void swap(obj_ref & n) { std::swap(m_obj, n.m_obj); } /** \brief Steal ownership without decrementing the reference counter. */ T * steal() { T * r = m_obj; m_obj = nullptr; return r; } }; template inline bool operator==(obj_ref const & n1, obj_ref const & n2) { return n1.get() == n2.get(); } template inline bool operator==(obj_ref const & n1, obj_ref const & n2) { return n1.get() == n2.get(); } template inline bool operator!=(obj_ref const & n1, obj_ref const & n2) { return n1.get() != n2.get(); } template inline bool operator!=(obj_ref const & n1, obj_ref const & n2) { return n1.get() != n2.get(); } template inline void dec_range_ref(IT const & begin, IT const & end, TManager & m) { for (IT it = begin; it != end; ++it) { if (*it) { m.dec_ref(*it); } } } #endif /* OBJ_REF_H_ */ z3-z3-4.8.7/src/util/obj_ref_hashtable.h000066400000000000000000000054251356505360400177730ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: obj_ref_hashtable.h Abstract: corresponding obj_map with reference count management. Author: Nikolaj Bjorner (nbjorner) 2017-12-8 Revision History: --*/ #ifndef OBJ_REF_HASHTABLE_H_ #define OBJ_REF_HASHTABLE_H_ #include "util/obj_hashtable.h" template class obj_ref_map { M& m; obj_map m_table; public: typedef typename obj_map::iterator iterator; typedef Key key; typedef Value value; typedef typename obj_map::key_data key_data; typedef typename obj_map::obj_map_entry obj_map_entry; obj_ref_map(M& m):m(m) {} ~obj_ref_map() { reset(); } void reset() { for (auto& kv : m_table) { m.dec_ref(kv.m_key); } m_table.reset(); } void finalize() { reset(); m_table.finalize(); } bool empty() const { return m_table.empty(); } unsigned size() const { return m_table.size(); } unsigned capacity() const { return m_table.capacity(); } iterator begin() const { return m_table.begin(); } iterator end() const { return m_table.end(); } void insert(Key * const k, Value const & v) { if (!m_table.contains(k)) m.inc_ref(k); m_table.insert(k, v); } void insert(Key * const k, Value && v) { if (!m_table.contains(k)) m.inc_ref(k); m_table.insert(k, v); } key_data const & insert_if_not_there(Key * k, Value const & v) { if (!m_table.contains(k)) m.inc_ref(k); return m_table.insert_if_not_there(k, v); } obj_map_entry * insert_if_not_there2(Key * k, Value const & v) { if (!m_table.contains(k)) m.inc_ref(k); return m_table.insert_if_not_there2(k, v); } obj_map_entry * find_core(Key * k) const { return m_table.find_core(k); } bool find(Key * const k, Value & v) const { return m_table.find(k, v); } value const & find(key * k) const { return m_table.find(k); } value & find(key * k) { return m_table.find(k); } value const & operator[](key * k) const { return find(k); } value & operator[](key * k) { return find(k); } iterator find_iterator(Key * k) const { return m_table.find_iterator(k); } bool contains(Key * k) const { return m_table.contains(k); } void remove(Key * k) { if (m_table.contains(k)) { m_table.remove(k); m.dec_ref(k); } } void erase(Key * k) { remove(k); } unsigned long long get_num_collision() const { return m_table.get_num_collision(); } void swap(obj_ref_map & other) { m_table.swap(other.m_table); } }; #endif /* OBJ_REF_HASHTABLE_H_ */ z3-z3-4.8.7/src/util/obj_triple_hashtable.h000066400000000000000000000127511356505360400205160ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: obj_triple_hashtable.h Abstract: Author: Leonardo de Moura (leonardo) 2008-02-19. Revision History: --*/ #ifndef OBJ_TRIPLE_HASHTABLE_H_ #define OBJ_TRIPLE_HASHTABLE_H_ #include "util/hashtable.h" /** \brief Special entry for a hashtable of pairs of obj pointers (i.e., objects that have a hash() method). This entry uses 0x0 and 0x1 to represent HT_FREE and HT_DELETED. */ template class obj_triple_hash_entry { unsigned m_hash; // cached hash code triple m_data; public: typedef triple data; obj_triple_hash_entry():m_data(0,0,0) {} unsigned get_hash() const { return m_hash; } bool is_free() const { return m_data.first == 0; } bool is_deleted() const { return m_data.first == reinterpret_cast(1); } bool is_used() const { return m_data.first != reinterpret_cast(0) && m_data.first != reinterpret_cast(1); } data const & get_data() const { return m_data; } data & get_data() { return m_data; } void set_data(data const d) { m_data = d; } void set_hash(unsigned h) { m_hash = h; } void mark_as_deleted() { m_data.first = reinterpret_cast(1); } void mark_as_free() { m_data.first = 0; } }; template class obj_triple_hashtable : public core_hashtable, obj_ptr_triple_hash, default_eq > > { public: obj_triple_hashtable(unsigned initial_capacity = DEFAULT_HASHTABLE_INITIAL_CAPACITY): core_hashtable, obj_ptr_triple_hash, default_eq > >(initial_capacity) { } }; template class obj_triple_map { protected: class entry; public: class key_data { Key1 * m_key1; Key2 * m_key2; Key3 * m_key3; Value m_value; unsigned m_hash; friend class entry; public: key_data(): m_key1(nullptr), m_key2(nullptr), m_key3(nullptr), m_hash(0) { } key_data(Key1 * k1, Key2 * k2, Key3 * k3): m_key1(k1), m_key2(k2), m_key3(k3){ m_hash = combine_hash(combine_hash(m_key1->hash(), m_key2->hash()), m_key3->hash()); } key_data(Key1 * k1, Key2 * k2, Key3* k3, const Value & v): m_key1(k1), m_key2(k2), m_key3(k3), m_value(v) { m_hash = combine_hash(combine_hash(m_key1->hash(), m_key2->hash()), m_key3->hash()); } unsigned hash() const { return m_hash; } bool operator==(key_data const & other) const { return m_key1 == other.m_key1 && m_key2 == other.m_key2 && m_key3 == other.m_key3; } Key1 * get_key1() const { return m_key1; } Key2 * get_key2() const { return m_key2; } Key3 * get_key3() const { return m_key3; } Value const & get_value() const { return m_value; } }; protected: class entry { key_data m_data; public: typedef key_data data; entry() {} unsigned get_hash() const { return m_data.hash(); } bool is_free() const { return m_data.m_key1 == nullptr; } bool is_deleted() const { return m_data.m_key1 == reinterpret_cast(1); } bool is_used() const { return m_data.m_key1 != reinterpret_cast(0) && m_data.m_key1 != reinterpret_cast(1); } key_data const & get_data() const { return m_data; } key_data & get_data() { return m_data; } void set_data(key_data const & d) { m_data = d; } void set_hash(unsigned h) { SASSERT(h == m_data.hash()); } void mark_as_deleted() { m_data.m_key1 = reinterpret_cast(1); } void mark_as_free() { m_data.m_key1 = nullptr; } }; typedef core_hashtable, default_eq > table; table m_table; entry * find_core(Key1 * k1, Key2 * k2, Key3 * k3) const { return m_table.find_core(key_data(k1, k2, k3)); } public: obj_triple_map(): m_table(DEFAULT_HASHTABLE_INITIAL_CAPACITY) {} typedef typename table::iterator iterator; void reset() { m_table.reset(); } bool empty() const { return m_table.empty(); } unsigned size() const { return m_table.size(); } unsigned capacity() const { return m_table.capacity(); } iterator begin() const { return m_table.begin(); } iterator end() const { return m_table.end(); } void insert(Key1 * k1, Key2 * k2, Key3* k3, Value const & v) { m_table.insert(key_data(k1, k2, k3, v)); } key_data const & insert_if_not_there(Key1 * k1, Key2 * k2, Key3 * k3, Value const & v) { return m_table.insert_if_not_there(key_data(k1, k2, k3, v)); } bool find(Key1 * k1, Key2 * k2,Key3 * k3, Value & v) const { entry * e = find_core(k1, k2, k3); if (e) { v = e->get_data().get_value(); } return (nullptr != e); } bool contains(Key1 * k1, Key2 * k2, Key3 * k3) const { return find_core(k1, k2, k3) != 0; } void erase(Key1 * k1, Key2 * k2, Key3 * k3) { m_table.remove(key_data(k1, k2, k3)); } }; #endif /* OBJ_TRIPLE_HASHTABLE_H_ */ z3-z3-4.8.7/src/util/object_allocator.h000066400000000000000000000204531356505360400176560ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: object_allocator.h Abstract: Yet another object allocator. This allocator is supposed to be efficient when there is a collection of worker threads accessing it. Author: Leonardo de Moura (leonardo) 2010-06-09. Revision History: --*/ #ifndef OBJECT_ALLOCATOR_H_ #define OBJECT_ALLOCATOR_H_ #include "util/util.h" #include "util/vector.h" #define DEFAULT_NUM_WORKERS 8 #define NUM_OBJECTS_PER_PAGE 1024 template struct do_nothing_reset_proc { public: void operator()(T * obj) {} }; template struct simple_reset_proc { public: void operator()(T * obj) { obj->reset(); } }; /** \brief Allocator for T objects. This allocator is supposed to be efficient even when a collection of working threads are accessing it. Assumptions: - T must have an empty constructor. - The destructors for T objects are only invoked when the object_allocator is deleted. - The destructors are not invoked if CallDestructors == false. - The functor ResetProc is invoked for \c ptr when recycle(ptr) or recycle(worker_id, ptr) are invoked. The default ResetProc does nothing. */ template > class object_allocator : public ResetProc { /** \brief Auxiliary allocator for storing object into chunks of memory. */ class region { ptr_vector m_pages; unsigned m_idx; //!< next position in the current page. void allocate_new_page() { T * new_page = static_cast(memory::allocate(sizeof(T) * NUM_OBJECTS_PER_PAGE)); m_pages.push_back(new_page); m_idx = 0; } void call_destructors_for_page(T * page, unsigned end) { T * page_end = page + end; for (; page < page_end; page++) page->~T(); } void call_destructors() { if (CallDestructors) { SASSERT(!m_pages.empty()); typename ptr_vector::iterator it = m_pages.begin(); typename ptr_vector::iterator end = m_pages.end(); end--; call_destructors_for_page(*end, m_idx); for (; it != end; ++it) call_destructors_for_page(*it, NUM_OBJECTS_PER_PAGE); } } void free_memory() { call_destructors(); typename ptr_vector::iterator it = m_pages.begin(); typename ptr_vector::iterator end = m_pages.end(); for (; it != end; ++it) memory::deallocate(*it); } public: region() { allocate_new_page(); } ~region() { free_memory(); } template T * allocate() { SASSERT(!m_pages.empty()); T * r = m_pages.back() + m_idx; if (construct) new (r) T(); m_idx++; if (m_idx == NUM_OBJECTS_PER_PAGE) allocate_new_page(); return r; } void reset() { free_memory(); m_pages.reset(); allocate_new_page(); } unsigned get_objects_count() { return (m_pages.size() - 1) * NUM_OBJECTS_PER_PAGE + m_idx; } }; #ifdef Z3DEBUG bool m_concurrent; //!< True when the allocator can be accessed concurrently. #endif ptr_vector m_regions; vector > m_free_lists; template T * allocate_core(unsigned idx) { ptr_vector & free_list = m_free_lists[idx]; if (!free_list.empty()) { T * r = free_list.back(); free_list.pop_back(); return r; } return m_regions[idx]->template allocate(); } void recycle_core(unsigned idx, T * ptr) { ResetProc::operator()(ptr); m_free_lists[idx].push_back(ptr); } public: object_allocator(ResetProc const & r = ResetProc()):ResetProc(r) { DEBUG_CODE(m_concurrent = false;); reserve(DEFAULT_NUM_WORKERS); } ~object_allocator() { std::for_each(m_regions.begin(), m_regions.end(), delete_proc()); } /** \brief Enable/Disable concurrent access. */ void enable_concurrent(bool flag) { DEBUG_CODE(m_concurrent = flag;); } /** \brief Make sure that \c num_workers can access this object allocator concurrently. This method must only be invoked if the allocator is not in concurrent mode. */ void reserve(unsigned num_workers) { SASSERT(!m_concurrent); unsigned old_capacity = capacity(); if (num_workers > old_capacity) { m_regions.resize(num_workers); m_free_lists.resize(num_workers); for (unsigned i = old_capacity; i < capacity(); i++) { m_regions[i] = alloc(region); } } } /** \brief Return the number of workers supported by this object allocator. */ unsigned capacity() const { return m_regions.size(); } /** \brief Free all memory allocated using this allocator. This method must only be invoked when the allocator is not in concurrent mode. */ void reset() { SASSERT(!m_concurrent); unsigned c = capacity(); for (unsigned i = 0; i < c; i++) { m_regions[i]->reset(); m_free_lists[i].reset(); } } /** \brief Allocate a new object. This method must only be invoked when the object_allocator is not in concurrent mode. */ template T * allocate() { SASSERT(!m_concurrent); return allocate_core(0); } /** \brief Recycle the given object. This method must only be invoked when the object_allocator is not in concurrent mode. \remark It is OK to recycle an object allocated by a worker when the object_allocator was in concurrent mode. */ void recycle(T * ptr) { SASSERT(!m_concurrent); recycle_core(0, ptr); } /** \brief Allocate a new object for the given worker. This method must only be invoked when the object_allocator is in concurrent mode. */ template T * allocate(unsigned worker_id) { SASSERT(m_concurrent); return allocate_core(worker_id); } /** \brief Recycle the given object. This method must only be invoked when the object_allocator is in concurrent mode. \remark It is OK to recycle an object allocated by a different worker, or allocated when the object_allocator was not in concurrent mode. */ void recycle(unsigned worker_id, T * ptr) { SASSERT(m_concurrent); return recycle_core(worker_id, ptr); } /** \brief Wrapper for currying worker_id in allocate and recycle methods. */ class worker_object_allocator { object_allocator & m_owner; unsigned m_worker_id; friend class object_allocator; worker_object_allocator(object_allocator & owner, unsigned id):m_owner(owner), m_worker_id(id) {} public: template T * allocate() { return m_owner.allocate(m_worker_id); } void recycle(T * ptr) { return m_owner.recycle(m_worker_id, ptr); } }; /** \brief Return a wrapper for allocating memory for the given worker. The wrapper remains valid even when the object_allocator is not in concurrent mode. However, the methods allocate/recycle of the wrapper must only be invoked when the object_allocator is in concurrent mode. */ worker_object_allocator get_worker_allocator(unsigned worker_id) { SASSERT(worker_id < capacity()); return worker_object_allocator(*this, worker_id); } unsigned get_objects_count() const { unsigned count = 0; unsigned n_regions = m_regions.size(); for (unsigned i = 0; i < n_regions; i++) { count += m_regions[i]->get_objects_count(); count -= m_free_lists[i].size(); } return count; } }; #endif /* OBJECT_ALLOCATOR_H_ */ z3-z3-4.8.7/src/util/old_buffer.h000066400000000000000000000136351356505360400164630ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: old_buffer.h Abstract: Author: Leonardo de Moura (leonardo) 2006-10-16. Revision History: 2019-2-23 Renamed to old_buffer from buffer to provide new implementation --*/ #ifndef OLD_BUFFER_H_ #define OLD_BUFFER_H_ #include #include "util/memory_manager.h" template class old_buffer { protected: T * m_buffer; unsigned m_pos; unsigned m_capacity; char m_initial_buffer[INITIAL_SIZE * sizeof(T)]; void free_memory() { if (m_buffer != reinterpret_cast(m_initial_buffer)) { dealloc_svect(m_buffer); } } void expand() { unsigned new_capacity = m_capacity << 1; T * new_buffer = reinterpret_cast(memory::allocate(sizeof(T) * new_capacity)); memcpy(new_buffer, m_buffer, m_pos * sizeof(T)); free_memory(); m_buffer = new_buffer; m_capacity = new_capacity; } void destroy_elements() { iterator it = begin(); iterator e = end(); for (; it != e; ++it) { it->~T(); } } void destroy() { if (CallDestructors) { destroy_elements(); } free_memory(); } public: typedef T data; typedef T * iterator; typedef const T * const_iterator; old_buffer(): m_buffer(reinterpret_cast(m_initial_buffer)), m_pos(0), m_capacity(INITIAL_SIZE) { } old_buffer(const old_buffer & source): m_buffer(reinterpret_cast(m_initial_buffer)), m_pos(0), m_capacity(INITIAL_SIZE) { unsigned sz = source.size(); for(unsigned i = 0; i < sz; i++) { push_back(source.m_buffer[i]); } } old_buffer(unsigned sz, const T & elem): m_buffer(reinterpret_cast(m_initial_buffer)), m_pos(0), m_capacity(INITIAL_SIZE) { for (unsigned i = 0; i < sz; i++) { push_back(elem); } SASSERT(size() == sz); } ~old_buffer() { destroy(); } void reset() { if (CallDestructors) { destroy_elements(); } m_pos = 0; } void finalize() { destroy(); m_buffer = reinterpret_cast(m_initial_buffer); m_pos = 0; m_capacity = INITIAL_SIZE; } unsigned size() const { return m_pos; } bool empty() const { return m_pos == 0; } iterator begin() { return m_buffer; } iterator end() { return m_buffer + size(); } void set_end(iterator it) { m_pos = static_cast(it - m_buffer); if (CallDestructors) { iterator e = end(); for (; it != e; ++it) { it->~T(); } } } const_iterator begin() const { return m_buffer; } const_iterator end() const { return m_buffer + size(); } void push_back(const T & elem) { if (m_pos >= m_capacity) expand(); new (m_buffer + m_pos) T(elem); m_pos++; } void push_back(T && elem) { if (m_pos >= m_capacity) expand(); new (m_buffer + m_pos) T(std::move(elem)); m_pos++; } void pop_back() { if (CallDestructors) { back().~T(); } m_pos--; } const T & back() const { SASSERT(!empty()); SASSERT(m_pos > 0); return m_buffer[m_pos - 1]; } T & back() { SASSERT(!empty()); SASSERT(m_pos > 0); return m_buffer[m_pos - 1]; } T * c_ptr() const { return m_buffer; } void append(unsigned n, T const * elems) { for (unsigned i = 0; i < n; i++) { push_back(elems[i]); } } void append(const old_buffer& source) { append(source.size(), source.c_ptr()); } T & operator[](unsigned idx) { SASSERT(idx < size()); return m_buffer[idx]; } const T & operator[](unsigned idx) const { SASSERT(idx < size()); return m_buffer[idx]; } T & get(unsigned idx) { SASSERT(idx < size()); return m_buffer[idx]; } const T & get(unsigned idx) const { SASSERT(idx < size()); return m_buffer[idx]; } void set(unsigned idx, T const & val) { SASSERT(idx < size()); m_buffer[idx] = val; } void resize(unsigned nsz, const T & elem=T()) { unsigned sz = size(); if (nsz > sz) { for (unsigned i = sz; i < nsz; i++) { push_back(elem); } } else if (nsz < sz) { for (unsigned i = nsz; i < sz; i++) { pop_back(); } } SASSERT(size() == nsz); } void shrink(unsigned nsz) { unsigned sz = size(); SASSERT(nsz <= sz); for (unsigned i = nsz; i < sz; i++) pop_back(); SASSERT(size() == nsz); } old_buffer & operator=(old_buffer const & other) { if (this == &other) return *this; reset(); append(other); return *this; } }; template class old_ptr_buffer : public old_buffer { public: void append(unsigned n, T * const * elems) { for (unsigned i = 0; i < n; i++) { this->push_back(elems[i]); } } }; template class old_sbuffer : public old_buffer { public: old_sbuffer(): old_buffer() {} old_sbuffer(unsigned sz, const T& elem) : old_buffer(sz,elem) {} }; #endif /* OLD_BUFFER_H_ */ z3-z3-4.8.7/src/util/old_vector.h000066400000000000000000000371521356505360400165140ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: old_vector.h Abstract: Dynamic array implementation. Remarks: - Empty arrays consume only sizeof(T *) bytes. - There is the option of disabling the destructor invocation for elements stored in the vector. This is useful for vectors of int. Author: Leonardo de Moura (leonardo) 2006-09-11. Revision History: 2019-2-23 Renamed from vector to old_vector to provide new implementation --*/ #ifndef OLD_VECTOR_H_ #define OLD_VECTOR_H_ #include "util/debug.h" #include #include #include #include #include "util/memory_manager.h" #include "util/hash.h" #include "util/z3_exception.h" // disable warning for constant 'if' expressions. // these are used heavily in templates. #ifdef _MSC_VER #pragma warning(disable:4127) #endif template class old_vector { #define SIZE_IDX -1 #define CAPACITY_IDX -2 T * m_data; void destroy_elements() { iterator it = begin(); iterator e = end(); for (; it != e; ++it) { it->~T(); } } void free_memory() { memory::deallocate(reinterpret_cast(reinterpret_cast(m_data) - 2)); } void expand_vector() { if (m_data == nullptr) { SZ capacity = 2; SZ * mem = reinterpret_cast(memory::allocate(sizeof(T) * capacity + sizeof(SZ) * 2)); *mem = capacity; mem++; *mem = 0; mem++; m_data = reinterpret_cast(mem); } else { SASSERT(capacity() > 0); SZ old_capacity = reinterpret_cast(m_data)[CAPACITY_IDX]; SZ old_capacity_T = sizeof(T) * old_capacity + sizeof(SZ) * 2; SZ new_capacity = (3 * old_capacity + 1) >> 1; SZ new_capacity_T = sizeof(T) * new_capacity + sizeof(SZ) * 2; if (new_capacity <= old_capacity || new_capacity_T <= old_capacity_T) { throw default_exception("Overflow encountered when expanding old_vector"); } SZ *mem, *old_mem = reinterpret_cast(m_data) - 2; #if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 5 if (__has_trivial_copy(T)) { #else if (std::is_trivially_copyable::value) { #endif mem = (SZ*)memory::reallocate(old_mem, new_capacity_T); m_data = reinterpret_cast(mem + 2); } else { mem = (SZ*)memory::allocate(new_capacity_T); auto old_data = m_data; auto old_size = size(); mem[1] = old_size; m_data = reinterpret_cast(mem + 2); for (unsigned i = 0; i < old_size; ++i) { new (&m_data[i]) T(std::move(old_data[i])); old_data[i].~T(); } memory::deallocate(old_mem); } *mem = new_capacity; } } void copy_core(old_vector const & source) { SZ size = source.size(); SZ capacity = source.capacity(); SZ * mem = reinterpret_cast(memory::allocate(sizeof(T) * capacity + sizeof(SZ) * 2)); *mem = capacity; mem++; *mem = size; mem++; m_data = reinterpret_cast(mem); const_iterator it = source.begin(); iterator it2 = begin(); SASSERT(it2 == m_data); const_iterator e = source.end(); for (; it != e; ++it, ++it2) { new (it2) T(*it); } } void destroy() { if (m_data) { if (CallDestructors) { destroy_elements(); } free_memory(); } } public: typedef T data; typedef T * iterator; typedef const T * const_iterator; old_vector(): m_data(nullptr) { } old_vector(SZ s) { if (s == 0) { m_data = nullptr; return; } SZ * mem = reinterpret_cast(memory::allocate(sizeof(T) * s + sizeof(SZ) * 2)); *mem = s; mem++; *mem = s; mem++; m_data = reinterpret_cast(mem); // initialize elements iterator it = begin(); iterator e = end(); for (; it != e; ++it) { new (it) T(); } } old_vector(SZ s, T const & elem): m_data(nullptr) { resize(s, elem); } old_vector(old_vector const & source): m_data(nullptr) { if (source.m_data) { copy_core(source); } SASSERT(size() == source.size()); } old_vector(old_vector&& other) : m_data(nullptr) { std::swap(m_data, other.m_data); } old_vector(SZ s, T const * data): m_data(nullptr) { for (SZ i = 0; i < s; i++) { push_back(data[i]); } } ~old_vector() { destroy(); } void finalize() { destroy(); m_data = nullptr; } bool operator==(old_vector const & other) const { if (this == &other) { return true; } if (size() != other.size()) return false; for (unsigned i = 0; i < size(); i++) { if ((*this)[i] != other[i]) return false; } return true; } bool operator!=(old_vector const & other) const { return !(*this == other); } old_vector & operator=(old_vector const & source) { if (this == &source) { return *this; } destroy(); if (source.m_data) { copy_core(source); } else { m_data = nullptr; } return *this; } old_vector & operator=(old_vector && source) { if (this == &source) { return *this; } destroy(); m_data = nullptr; std::swap(m_data, source.m_data); return *this; } bool containsp(std::function& predicate) const { for (auto const& t : *this) if (predicate(t)) return true; return false; } /** * retain elements that satisfy predicate. aka 'where'. */ old_vector filter_pure(std::function& predicate) const { old_vector result; for (auto& t : *this) if (predicate(t)) result.push_back(t); return result; } old_vector& filter_update(std::function& predicate) { unsigned j = 0; for (auto& t : *this) if (predicate(t)) set(j++, t); shrink(j); return *this; } /** * update elements using f, aka 'select' */ template old_vector map_pure(std::function& f) const { old_vector result; for (auto& t : *this) result.push_back(f(t)); return result; } old_vector& map_update(std::function& f) { unsigned j = 0; for (auto& t : *this) set(j++, f(t)); return *this; } void reset() { if (m_data) { if (CallDestructors) { destroy_elements(); } reinterpret_cast(m_data)[SIZE_IDX] = 0; } } void clear() { reset(); } bool empty() const { return m_data == nullptr || reinterpret_cast(m_data)[SIZE_IDX] == 0; } SZ size() const { if (m_data == nullptr) { return 0; } return reinterpret_cast(m_data)[SIZE_IDX]; } SZ capacity() const { if (m_data == nullptr) { return 0; } return reinterpret_cast(m_data)[CAPACITY_IDX]; } iterator begin() { return m_data; } iterator end() { return m_data + size(); } const_iterator begin() const { return m_data; } const_iterator end() const { return m_data + size(); } class reverse_iterator { T* v; public: reverse_iterator(T* v):v(v) {} T operator*() { return *v; } reverse_iterator operator++(int) { reverse_iterator tmp = *this; --v; return tmp; } reverse_iterator& operator++() { --v; return *this; } bool operator==(reverse_iterator const& other) const { return other.v == v; } bool operator!=(reverse_iterator const& other) const { return other.v != v; } }; reverse_iterator rbegin() { return reverse_iterator(end() - 1); } reverse_iterator rend() { return reverse_iterator(begin() - 1); } void set_end(iterator it) { if (m_data) { SZ new_sz = static_cast(it - m_data); if (CallDestructors) { iterator e = end(); for(; it != e; ++it) { it->~T(); } } reinterpret_cast(m_data)[SIZE_IDX] = new_sz; } else { SASSERT(it == 0); } } T & operator[](SZ idx) { SASSERT(idx < size()); return m_data[idx]; } T const & operator[](SZ idx) const { SASSERT(idx < size()); return m_data[idx]; } T & get(SZ idx) { SASSERT(idx < size()); return m_data[idx]; } T const & get(SZ idx) const { SASSERT(idx < size()); return m_data[idx]; } void set(SZ idx, T const & val) { SASSERT(idx < size()); m_data[idx] = val; } void set(SZ idx, T && val) { SASSERT(idx < size()); m_data[idx] = std::move(val); } T & back() { SASSERT(!empty()); return operator[](size() - 1); } T const & back() const { SASSERT(!empty()); return operator[](size() - 1); } void pop_back() { SASSERT(!empty()); if (CallDestructors) { back().~T(); } reinterpret_cast(m_data)[SIZE_IDX]--; } void push_back(T const & elem) { if (m_data == nullptr || reinterpret_cast(m_data)[SIZE_IDX] == reinterpret_cast(m_data)[CAPACITY_IDX]) { expand_vector(); } new (m_data + reinterpret_cast(m_data)[SIZE_IDX]) T(elem); reinterpret_cast(m_data)[SIZE_IDX]++; } void push_back(T && elem) { if (m_data == nullptr || reinterpret_cast(m_data)[SIZE_IDX] == reinterpret_cast(m_data)[CAPACITY_IDX]) { expand_vector(); } new (m_data + reinterpret_cast(m_data)[SIZE_IDX]) T(std::move(elem)); reinterpret_cast(m_data)[SIZE_IDX]++; } void insert(T const & elem) { push_back(elem); } void erase(iterator pos) { SASSERT(pos >= begin() && pos < end()); iterator prev = pos; ++pos; iterator e = end(); for(; pos != e; ++pos, ++prev) { *prev = *pos; } reinterpret_cast(m_data)[SIZE_IDX]--; } void erase(T const & elem) { iterator it = std::find(begin(), end(), elem); if (it != end()) { erase(it); } } void shrink(SZ s) { if (m_data) { SASSERT(s <= reinterpret_cast(m_data)[SIZE_IDX]); if (CallDestructors) { iterator it = m_data + s; iterator e = end(); for(; it != e; ++it) { it->~T(); } } reinterpret_cast(m_data)[SIZE_IDX] = s; } else { SASSERT(s == 0); } } template void resize(SZ s, Args args...) { SZ sz = size(); if (s <= sz) { shrink(s); return; } while (s > capacity()) { expand_vector(); } SASSERT(m_data != 0); reinterpret_cast(m_data)[SIZE_IDX] = s; iterator it = m_data + sz; iterator end = m_data + s; for (; it != end; ++it) { new (it) T(std::forward(args)); } } void resize(SZ s) { SZ sz = size(); if (s <= sz) { shrink(s); return; } while (s > capacity()) { expand_vector(); } SASSERT(m_data != 0); reinterpret_cast(m_data)[SIZE_IDX] = s; iterator it = m_data + sz; iterator end = m_data + s; for (; it != end; ++it) { new (it) T(); } } void append(old_vector const & other) { for(SZ i = 0; i < other.size(); ++i) { push_back(other[i]); } } void append(SZ sz, T const * data) { for(SZ i = 0; i < sz; ++i) { push_back(data[i]); } } T * c_ptr() const { return m_data; } void swap(old_vector & other) { std::swap(m_data, other.m_data); } void reverse() { SZ sz = size(); for (SZ i = 0; i < sz/2; ++i) { std::swap(m_data[i], m_data[sz-i-1]); } } void fill(T const & elem) { iterator i = begin(); iterator e = end(); for (; i != e; ++i) { *i = elem; } } void fill(unsigned sz, T const & elem) { resize(sz); fill(elem); } bool contains(T const & elem) const { const_iterator it = begin(); const_iterator e = end(); for (; it != e; ++it) { if (*it == elem) { return true; } } return false; } // set pos idx with elem. If idx >= size, then expand using default. void setx(SZ idx, T const & elem, T const & d) { if (idx >= size()) { resize(idx+1, d); } m_data[idx] = elem; } // return element at position idx, if idx >= size, then return default T const & get(SZ idx, T const & d) const { if (idx >= size()) { return d; } return m_data[idx]; } void reserve(SZ s, T const & d) { if (s > size()) resize(s, d); } void reserve(SZ s) { if (s > size()) resize(s); } }; template class old_ptr_vector : public old_vector { public: old_ptr_vector():old_vector() {} old_ptr_vector(unsigned s):old_vector(s) {} old_ptr_vector(unsigned s, T * elem):old_vector(s, elem) {} old_ptr_vector(old_ptr_vector const & source):old_vector(source) {} old_ptr_vector(old_ptr_vector && other) : old_vector(std::move(other)) {} old_ptr_vector(unsigned s, T * const * data):old_vector(s, const_cast(data)) {} old_ptr_vector & operator=(old_ptr_vector const & source) { old_vector::operator=(source); return *this; } }; template class old_svector : public old_vector { public: old_svector():old_vector() {} old_svector(SZ s):old_vector(s) {} old_svector(SZ s, T const & elem):old_vector(s, elem) {} old_svector(old_svector const & source):old_vector(source) {} old_svector(old_svector && other) : old_vector(std::move(other)) {} old_svector(SZ s, T const * data):old_vector(s, data) {} old_svector & operator=(old_svector const & source) { old_vector::operator=(source); return *this; } }; #endif /* OLD_VECTOR_H_ */ z3-z3-4.8.7/src/util/optional.h000066400000000000000000000061001356505360400161660ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: optional.h Abstract: Discriminated union of a type T. It defines the notion of initialized/uninitialized objects. Author: Leonardo de Moura (leonardo) 2006-09-29. Revision History: --*/ #ifndef OPTIONAL_H_ #define OPTIONAL_H_ template class optional { T* m_obj; char m_initialized; void construct(const T & val) { m_initialized = 1; m_obj = alloc(T, val); } void destroy() { if (m_initialized == 1) { dealloc(m_obj); m_obj = nullptr; } m_initialized = 0; } public: optional(): m_obj(nullptr), m_initialized(0) {} explicit optional(const T & val) { construct(val); } optional(const optional & val): m_initialized(0) { if (val.m_initialized == 1) { construct(*val); } } ~optional() { destroy(); } static optional const & undef() { static optional u; return u; } bool initialized() const { return m_initialized == 1; } operator bool() const { return m_initialized == 1; } bool operator!() const { return m_initialized == 0; } T * get() const { if (m_initialized == 1) { return m_obj; } else { return 0; } } void set_invalid() { if (m_initialized == 1) { destroy(); } } T * operator->() { SASSERT(m_initialized==1); return m_obj; } T const * operator->() const { SASSERT(m_initialized==1); return m_obj; } const T & operator*() const { SASSERT(m_initialized==1); return *m_obj; } T & operator*() { SASSERT(m_initialized==1); return *m_obj; } optional & operator=(const T & val) { destroy(); construct(val); return * this; } optional & operator=(const optional & val) { if (&val != this) { destroy(); if (val.m_initialized) { construct(*val); } } return *this; } }; /** \brief Template specialization for pointers. NULL represents uninitialized pointers. */ template class optional { T * m_ptr; static optional m_undef; public: optional():m_ptr(nullptr) {} explicit optional(T * val):m_ptr(val) {} optional(const optional & val):m_ptr(val.m_ptr) {} static optional const & undef() { return m_undef; } bool initialized() const { return m_ptr != 0 ; } operator bool() const { return m_ptr != 0; } bool operator!() const { return m_ptr == nullptr; } void reset() { m_ptr = 0; } optional & operator=(T * val) { m_ptr = val; return *this; } optional & operator=(const optional & val) { m_ptr = val.m_ptr; return *this; } T ** operator->() { return &m_ptr; } T * operator*() const { return m_ptr; } T * & operator*() { return m_ptr; } }; #endif /* OPTIONAL_H_ */ z3-z3-4.8.7/src/util/page.cpp000066400000000000000000000027721356505360400156230ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: page.cpp Abstract: Goodies for manipulating pages of memory. Author: Leonardo de Moura (leonardo) 2011-02-27. Revision History: --*/ #include "util/page.h" #include "util/debug.h" inline void set_page_header(char * page, char * prev, bool default_page) { size_t header = reinterpret_cast(prev) | static_cast(default_page); reinterpret_cast(page)[-1] = header; SASSERT(is_default_page(page) == default_page); SASSERT(prev_page(page) == prev); } inline char * alloc_page(size_t s) { char * r = alloc_svect(char, s+PAGE_HEADER_SZ); return r + PAGE_HEADER_SZ; } inline void del_page(char * page) { dealloc_svect(page - PAGE_HEADER_SZ); } void del_pages(char * page) { while (page != nullptr) { char * prev = prev_page(page); del_page(page); page = prev; } } char * allocate_default_page(char * prev, char * & free_pages) { char * r; if (free_pages) { r = free_pages; free_pages = prev_page(free_pages); } else { r = alloc_page(DEFAULT_PAGE_SIZE); } set_page_header(r, prev, true); return r; } char * allocate_page(char * prev, size_t sz) { char * r = alloc_page(sz); set_page_header(r, prev, false); return r; } void recycle_page(char * p, char * & free_pages) { if (is_default_page(p)) { set_page_header(p, free_pages, true); free_pages = p; } else { del_page(p); } } z3-z3-4.8.7/src/util/page.h000066400000000000000000000017741356505360400152710ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: page.h Abstract: Goodies for manipulating pages of memory. Author: Leonardo de Moura (leonardo) 2011-02-27. Revision History: --*/ #ifndef PAGE_H_ #define PAGE_H_ #include "util/memory_manager.h" #define PAGE_HEADER_SZ sizeof(size_t) #define DEFAULT_PAGE_SIZE (8192 - PAGE_HEADER_SZ) #define PAGE_HEADER_MASK (static_cast(-1) - 1) inline char * prev_page(char * page) { size_t tagged_ptr = reinterpret_cast(page)[-1]; return reinterpret_cast(tagged_ptr & PAGE_HEADER_MASK); } inline bool is_default_page(char * page) { size_t tagged_ptr = reinterpret_cast(page)[-1]; return static_cast(tagged_ptr & 1); } inline char * end_of_default_page(char * p) { return p + DEFAULT_PAGE_SIZE; } void del_pages(char * page); char * allocate_default_page(char * prev, char * & free_pages); char * allocate_page(char * prev, size_t sz); void recycle_page(char * p, char * & free_pages); #endif z3-z3-4.8.7/src/util/params.cpp000066400000000000000000001007241356505360400161660ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: params.cpp Abstract: Parameters Author: Leonardo (leonardo) 2011-05-09 Notes: --*/ #include "util/params.h" #include "util/rational.h" #include "util/symbol.h" #include "util/dictionary.h" #include params_ref params_ref::g_empty_params_ref; std::string norm_param_name(char const * n) { if (n == nullptr) return "_"; if (*n == ':') n++; std::string r = n; unsigned sz = static_cast(r.size()); if (sz == 0) return "_"; for (unsigned i = 0; i < sz; i++) { char curr = r[i]; if ('A' <= curr && curr <= 'Z') r[i] = curr - 'A' + 'a'; else if (curr == '-' || curr == ':') r[i] = '_'; } return r; } std::string norm_param_name(symbol const & n) { return norm_param_name(n.bare_str()); } struct param_descrs::imp { struct info { param_kind m_kind; char const * m_descr; char const * m_default; char const * m_module; info(param_kind k, char const * descr, char const * def, char const* module): m_kind(k), m_descr(descr), m_default(def), m_module(module) { } info(): m_kind(CPK_INVALID), m_descr(nullptr), m_default(nullptr), m_module(nullptr) { } }; dictionary m_info; svector m_names; void insert(symbol const & name, param_kind k, char const * descr, char const * def, char const* module) { SASSERT(!name.is_numerical()); info i; if (m_info.find(name, i)) { SASSERT(i.m_kind == k); return; } m_info.insert(name, info(k, descr, def, module)); m_names.push_back(name); } void erase(symbol const & name) { m_info.erase(name); } bool contains(symbol const & name) const { return m_info.contains(name); } param_kind get_kind(symbol const & name) const { info i; if (m_info.find(name, i)) return i.m_kind; return CPK_INVALID; } bool split_name(symbol const& name, symbol & prefix, symbol & suffix) const { if (name.is_numerical()) return false; char const* str = name.bare_str(); char const* period = strchr(str,'.'); if (!period) return false; svector prefix_((unsigned)(period-str), str); prefix_.push_back(0); prefix = symbol(prefix_.c_ptr()); suffix = symbol(period + 1); return true; } param_kind get_kind_in_module(symbol & name) const { param_kind k = get_kind(name); symbol prefix, suffix; if (k == CPK_INVALID && split_name(name, prefix, suffix)) { k = get_kind(suffix); if (k != CPK_INVALID) { if (symbol(get_module(suffix)) == prefix) { name = suffix; } else { k = CPK_INVALID; } } } return k; } char const* get_module(symbol const& name) const { info i; if (m_info.find(name, i)) return i.m_module; return nullptr; } char const * get_descr(symbol const & name) const { info i; if (m_info.find(name, i)) return i.m_descr; return nullptr; } char const * get_default(symbol const & name) const { info i; if (m_info.find(name, i)) return i.m_default; return nullptr; } unsigned size() const { return m_names.size(); } symbol get_param_name(unsigned idx) const { return m_names[idx]; } struct lt { bool operator()(symbol const & s1, symbol const & s2) const { return strcmp(s1.bare_str(), s2.bare_str()) < 0; } }; void display(std::ostream & out, unsigned indent, bool smt2_style, bool include_descr) const { svector names; for (auto const& kv : m_info) { names.push_back(kv.m_key); } std::sort(names.begin(), names.end(), lt()); for (symbol const& name : names) { for (unsigned i = 0; i < indent; i++) out << " "; if (smt2_style) out << ':'; char const * s = name.bare_str(); unsigned n = static_cast(strlen(s)); for (unsigned i = 0; i < n; i++) { if (smt2_style && s[i] == '_') out << '-'; else if (!smt2_style && s[i] == '-') out << '_'; else if (s[i] >= 'A' && s[i] <= 'Z') out << (s[i] - 'A' + 'a'); else out << s[i]; } info d; m_info.find(name, d); SASSERT(d.m_descr); out << " (" << d.m_kind << ")"; if (include_descr) out << " " << d.m_descr; if (d.m_default != nullptr) out << " (default: " << d.m_default << ")"; out << "\n"; } } void copy(param_descrs & other) { for (auto const& kv : other.m_imp->m_info) { insert(kv.m_key, kv.m_value.m_kind, kv.m_value.m_descr, kv.m_value.m_default, kv.m_value.m_module); } } }; param_descrs::param_descrs() { m_imp = alloc(imp); } param_descrs::~param_descrs() { dealloc(m_imp); } void param_descrs::copy(param_descrs & other) { m_imp->copy(other); } void param_descrs::insert(symbol const & name, param_kind k, char const * descr, char const * def, char const* module) { m_imp->insert(name, k, descr, def, module); } void param_descrs::insert(char const * name, param_kind k, char const * descr, char const * def, char const* module) { insert(symbol(name), k, descr, def, module); } bool param_descrs::contains(char const * name) const { return contains(symbol(name)); } bool param_descrs::contains(symbol const & name) const { return m_imp->contains(name); } char const * param_descrs::get_descr(char const * name) const { return get_descr(symbol(name)); } char const * param_descrs::get_descr(symbol const & name) const { return m_imp->get_descr(name); } char const * param_descrs::get_default(char const * name) const { return get_default(symbol(name)); } char const * param_descrs::get_default(symbol const & name) const { return m_imp->get_default(name); } void param_descrs::erase(symbol const & name) { m_imp->erase(name); } void param_descrs::erase(char const * name) { erase(symbol(name)); } param_kind param_descrs::get_kind_in_module(symbol & name) const { return m_imp->get_kind_in_module(name); } param_kind param_descrs::get_kind(symbol const & name) const { return m_imp->get_kind(name); } param_kind param_descrs::get_kind(char const * name) const { return get_kind(symbol(name)); } unsigned param_descrs::size() const { return m_imp->size(); } symbol param_descrs::get_param_name(unsigned i) const { return m_imp->get_param_name(i); } char const* param_descrs::get_module(symbol const& name) const { return m_imp->get_module(name); } void param_descrs::display(std::ostream & out, unsigned indent, bool smt2_style, bool include_descr) const { return m_imp->display(out, indent, smt2_style, include_descr); } void insert_max_memory(param_descrs & r) { r.insert("max_memory", CPK_UINT, "(default: infty) maximum amount of memory in megabytes."); } void insert_max_steps(param_descrs & r) { r.insert("max_steps", CPK_UINT, "(default: infty) maximum number of steps."); } void insert_produce_models(param_descrs & r) { r.insert("produce_models", CPK_BOOL, "(default: false) model generation."); } void insert_produce_proofs(param_descrs & r) { r.insert("produce_proofs", CPK_BOOL, "(default: false) proof generation."); } void insert_timeout(param_descrs & r) { 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"); } void insert_ctrl_c(param_descrs & r) { r.insert("ctrl_c", CPK_BOOL, "enable interrupts from ctrl-c", "true"); } class params { friend class params_ref; struct value { param_kind m_kind; union { bool m_bool_value; unsigned m_uint_value; double m_double_value; char const * m_str_value; char const * m_sym_value; rational * m_rat_value; }; }; typedef std::pair entry; svector m_entries; std::atomic m_ref_count; void del_value(entry & e); void del_values(); public: params():m_ref_count(0) {} ~params() { reset(); } void inc_ref() { m_ref_count++; } void dec_ref() { SASSERT(m_ref_count > 0); if (--m_ref_count == 0) dealloc(this); } bool empty() const { return m_entries.empty(); } bool contains(symbol const & k) const; bool contains(char const * k) const; void reset(); void reset(symbol const & k); void reset(char const * k); void validate(param_descrs const & p) { symbol suffix, prefix; for (params::entry& e : m_entries) { param_kind expected = p.get_kind_in_module(e.first); if (expected == CPK_INVALID) { std::stringstream strm; strm << "unknown parameter '" << e.first.str() << "'\n"; strm << "Legal parameters are:\n"; p.display(strm, 2, false, false); throw default_exception(strm.str()); } if (e.second.m_kind != expected && !(e.second.m_kind == CPK_UINT && expected == CPK_NUMERAL)) { std::stringstream strm; strm << "Parameter " << e.first.str() << " was given argument of type "; strm << e.second.m_kind << ", expected " << expected; throw default_exception(strm.str()); } } } // getters bool get_bool(symbol const & k, bool _default) const; bool get_bool(char const * k, bool _default) const; unsigned get_uint(symbol const & k, unsigned _default) const; unsigned get_uint(char const * k, unsigned _default) const; double get_double(symbol const & k, double _default) const; double get_double(char const * k, double _default) const; char const * get_str(symbol const & k, char const * _default) const; char const * get_str(char const * k, char const * _default) const; rational get_rat(symbol const & k, rational const & _default) const; rational get_rat(char const * k, rational const & _default) const; symbol get_sym(symbol const & k, symbol const & _default) const; symbol get_sym(char const * k, symbol const & _default) const; bool get_bool(char const * k, params_ref const & fallback, bool _default) const; unsigned get_uint(char const * k, params_ref const & fallback, unsigned _default) const; double get_double(char const * k, params_ref const & fallback, double _default) const; char const * get_str(char const * k, params_ref const & fallback, char const * _default) const; symbol get_sym(char const * k, params_ref const & fallback, symbol const & _default) const; // setters void set_bool(symbol const & k, bool v); void set_bool(char const * k, bool v); void set_uint(symbol const & k, unsigned v); void set_uint(char const * k, unsigned v); void set_double(symbol const & k, double v); void set_double(char const * k, double v); void set_str(symbol const & k, char const * v); void set_str(char const * k, char const * v); void set_rat(symbol const & k, rational const & v); void set_rat(char const * k, rational const & v); void set_sym(symbol const & k, symbol const & v); void set_sym(char const * k, symbol const & v); void display(std::ostream & out) const { out << "(params"; for (params::entry const& e : m_entries) { out << " " << e.first; switch (e.second.m_kind) { case CPK_BOOL: out << " " << (e.second.m_bool_value?"true":"false"); break; case CPK_UINT: out << " " <dec_ref(); } params_ref::params_ref(params_ref const & p): m_params(nullptr) { operator=(p); } void params_ref::display(std::ostream & out) const { if (m_params) m_params->display(out); else out << "(params)"; } void params_ref::display_smt2(std::ostream& out, char const* module, param_descrs& descrs) const { if (m_params) m_params->display_smt2(out, module, descrs); } void params_ref::display(std::ostream & out, char const * k) const { display(out, symbol(k)); } void params_ref::display(std::ostream & out, symbol const & k) const { if (m_params) m_params->display(out, k); else out << "default"; } void params_ref::validate(param_descrs const & p) { if (m_params) m_params->validate(p); } params_ref & params_ref::operator=(params_ref const & p) { if (p.m_params) p.m_params->inc_ref(); if (m_params) m_params->dec_ref(); m_params = p.m_params; return *this; } void params_ref::copy(params_ref const & src) { if (m_params == nullptr) operator=(src); else { init(); copy_core(src.m_params); } } void params_ref::copy_core(params const * src) { if (src == nullptr) return; for (auto const& p : src->m_entries) { switch (p.second.m_kind) { case CPK_BOOL: m_params->set_bool(p.first, p.second.m_bool_value); break; case CPK_UINT: m_params->set_uint(p.first, p.second.m_uint_value); break; case CPK_DOUBLE: m_params->set_double(p.first, p.second.m_double_value); break; case CPK_NUMERAL: m_params->set_rat(p.first, *(p.second.m_rat_value)); break; case CPK_SYMBOL: m_params->set_sym(p.first, symbol::mk_symbol_from_c_ptr(p.second.m_sym_value)); break; case CPK_STRING: m_params->set_str(p.first, p.second.m_str_value); break; default: UNREACHABLE(); break; } } } void params_ref::init() { if (!m_params) { m_params = alloc(params); m_params->inc_ref(); } else if (m_params->m_ref_count > 1) { params * old = m_params; m_params = alloc(params); m_params->inc_ref(); copy_core(old); old->dec_ref(); } SASSERT(m_params->m_ref_count == 1); } bool params_ref::get_bool(symbol const & k, bool _default) const { return m_params ? m_params->get_bool(k, _default) : _default; } bool params_ref::get_bool(char const * k, bool _default) const { return m_params ? m_params->get_bool(k, _default) : _default; } unsigned params_ref::get_uint(symbol const & k, unsigned _default) const { return m_params ? m_params->get_uint(k, _default) : _default; } unsigned params_ref::get_uint(char const * k, unsigned _default) const { return m_params ? m_params->get_uint(k, _default) : _default; } double params_ref::get_double(symbol const & k, double _default) const { return m_params ? m_params->get_double(k, _default) : _default; } double params_ref::get_double(char const * k, double _default) const { return m_params ? m_params->get_double(k, _default) : _default; } char const * params_ref::get_str(symbol const & k, char const * _default) const { return m_params ? m_params->get_str(k, _default) : _default; } char const * params_ref::get_str(char const * k, char const * _default) const { return m_params ? m_params->get_str(k, _default) : _default; } rational params_ref::get_rat(symbol const & k, rational const & _default) const { return m_params ? m_params->get_rat(k, _default) : _default; } rational params_ref::get_rat(char const * k, rational const & _default) const { return m_params ? m_params->get_rat(k, _default) : _default; } symbol params_ref::get_sym(symbol const & k, symbol const & _default) const { return m_params ? m_params->get_sym(k, _default) : _default; } symbol params_ref::get_sym(char const * k, symbol const & _default) const { return m_params ? m_params->get_sym(k, _default) : _default; } bool params_ref::get_bool(char const * k, params_ref const & fallback, bool _default) const { return m_params ? m_params->get_bool(k, fallback, _default) : fallback.get_bool(k, _default); } unsigned params_ref::get_uint(char const * k, params_ref const & fallback, unsigned _default) const { return m_params ? m_params->get_uint(k, fallback, _default) : fallback.get_uint(k, _default); } double params_ref::get_double(char const * k, params_ref const & fallback, double _default) const { return m_params ? m_params->get_double(k, fallback, _default) : fallback.get_double(k, _default); } char const * params_ref::get_str(char const * k, params_ref const & fallback, char const * _default) const { return m_params ? m_params->get_str(k, fallback, _default) : fallback.get_str(k, _default); } symbol params_ref::get_sym(char const * k, params_ref const & fallback, symbol const & _default) const { return m_params ? m_params->get_sym(k, fallback, _default) : fallback.get_sym(k, _default); } bool params_ref::empty() const { if (!m_params) return true; return m_params->empty(); } bool params_ref::contains(symbol const & k) const { if (!m_params) return false; return m_params->contains(k); } bool params_ref::contains(char const * k) const { if (!m_params) return false; return m_params->contains(k); } void params_ref::reset() { if (m_params) m_params->reset(); } void params_ref::reset(symbol const & k) { if (m_params) m_params->reset(k); } void params_ref::reset(char const * k) { if (m_params) m_params->reset(k); } void params_ref::set_bool(symbol const & k, bool v) { init(); m_params->set_bool(k, v); } void params_ref::set_bool(char const * k, bool v) { init(); m_params->set_bool(k, v); } void params_ref::set_uint(symbol const & k, unsigned v) { init(); m_params->set_uint(k, v); } void params_ref::set_uint(char const * k, unsigned v) { init(); m_params->set_uint(k, v); } void params_ref::set_double(symbol const & k, double v) { init(); m_params->set_double(k, v); } void params_ref::set_double(char const * k, double v) { init(); m_params->set_double(k, v); } void params_ref::set_str(symbol const & k, char const * v) { init(); m_params->set_str(k, v); } void params_ref::set_str(char const * k, char const * v) { init(); m_params->set_str(k, v); } void params_ref::set_rat(symbol const & k, rational const & v) { init(); m_params->set_rat(k, v); } void params_ref::set_rat(char const * k, rational const & v) { init(); m_params->set_rat(k, v); } void params_ref::set_sym(symbol const & k, symbol const & v) { init(); m_params->set_sym(k, v); } void params_ref::set_sym(char const * k, symbol const & v) { init(); m_params->set_sym(k, v); } void params::del_value(entry & e) { switch (e.second.m_kind) { case CPK_NUMERAL: if (e.second.m_kind == CPK_NUMERAL) dealloc(e.second.m_rat_value); break; default: return; } } #define TRAVERSE_ENTRIES(CODE) { \ svector::iterator it = m_entries.begin(); \ svector::iterator end = m_entries.end(); \ for (; it != end; ++it) { \ CODE \ } \ } #define TRAVERSE_CONST_ENTRIES(CODE) { \ svector::const_iterator it = m_entries.begin(); \ svector::const_iterator end = m_entries.end(); \ for (; it != end; ++it) { \ CODE \ } \ } void params::del_values() { TRAVERSE_ENTRIES(del_value(*it);); } #define CONTAINS(k) { \ if (empty()) \ return false; \ TRAVERSE_CONST_ENTRIES(if (it->first == k) return true;); \ return false; \ } bool params::contains(symbol const & k) const { CONTAINS(k); } bool params::contains(char const * k) const { CONTAINS(k); } void params::reset() { del_values(); m_entries.finalize(); SASSERT(empty()); } #define RESET(k) { \ if (empty()) return; \ TRAVERSE_ENTRIES(if (it->first == k) { \ svector::iterator it2 = it; \ del_value(*it2); \ ++it; \ for (; it != end; ++it, ++it2) { \ *it2 = *it; \ } \ m_entries.pop_back(); \ return; \ }); \ } void params::reset(symbol const & k) { RESET(k); } void params::reset(char const * k) { RESET(k); } #define GET_VALUE(MATCH_CODE, KIND) { \ if (empty()) return _default; \ TRAVERSE_CONST_ENTRIES(if (it->first == k && it->second.m_kind == KIND) { \ MATCH_CODE \ }); \ return _default; \ } #define GET_SIMPLE_VALUE(FIELD_NAME, KIND) GET_VALUE(return it->second.FIELD_NAME;, KIND) bool params::get_bool(symbol const & k, bool _default) const { GET_SIMPLE_VALUE(m_bool_value, CPK_BOOL); } bool params::get_bool(char const * k, bool _default) const { GET_SIMPLE_VALUE(m_bool_value, CPK_BOOL); } unsigned params::get_uint(symbol const & k, unsigned _default) const { GET_SIMPLE_VALUE(m_uint_value, CPK_UINT); } unsigned params::get_uint(char const * k, unsigned _default) const { GET_SIMPLE_VALUE(m_uint_value, CPK_UINT); } double params::get_double(symbol const & k, double _default) const { GET_SIMPLE_VALUE(m_double_value, CPK_DOUBLE); } double params::get_double(char const * k, double _default) const { GET_SIMPLE_VALUE(m_double_value, CPK_DOUBLE); } char const * params::get_str(symbol const & k, char const * _default) const { GET_SIMPLE_VALUE(m_str_value, CPK_STRING); } char const * params::get_str(char const * k, char const * _default) const { GET_SIMPLE_VALUE(m_str_value, CPK_STRING); } rational params::get_rat(symbol const & k, rational const & _default) const { if (empty()) return _default; TRAVERSE_CONST_ENTRIES(if (it->first == k) { if (it->second.m_kind == CPK_NUMERAL) { return *(it->second.m_rat_value); } if (it->second.m_kind == CPK_UINT) { return rational(static_cast(it->second.m_uint_value)); } }); return _default; } rational params::get_rat(char const * k, rational const & _default) const { if (empty()) return _default; TRAVERSE_CONST_ENTRIES(if (it->first == k) { if (it->second.m_kind == CPK_NUMERAL) { return *(it->second.m_rat_value); } if (it->second.m_kind == CPK_UINT) { return rational(static_cast(it->second.m_uint_value)); } }); return _default; } symbol params::get_sym(symbol const & k, symbol const & _default) const { GET_VALUE(return symbol::mk_symbol_from_c_ptr(it->second.m_sym_value);, CPK_SYMBOL); } symbol params::get_sym(char const * k, symbol const & _default) const { GET_VALUE(return symbol::mk_symbol_from_c_ptr(it->second.m_sym_value);, CPK_SYMBOL); } #define GET_VALUE2(MATCH_CODE, KIND) { \ if (!empty()) { \ TRAVERSE_CONST_ENTRIES(if (it->first == k && it->second.m_kind == KIND) { \ MATCH_CODE \ }); \ } \ } #define GET_SIMPLE_VALUE2(FIELD_NAME, KIND) GET_VALUE2(return it->second.FIELD_NAME;, KIND) bool params::get_bool(char const * k, params_ref const & fallback, bool _default) const { GET_SIMPLE_VALUE2(m_bool_value, CPK_BOOL); return fallback.get_bool(k, _default); } unsigned params::get_uint(char const * k, params_ref const & fallback, unsigned _default) const { GET_SIMPLE_VALUE2(m_uint_value, CPK_UINT); return fallback.get_uint(k, _default); } double params::get_double(char const * k, params_ref const & fallback, double _default) const { GET_SIMPLE_VALUE2(m_double_value, CPK_DOUBLE); return fallback.get_double(k, _default); } char const * params::get_str(char const * k, params_ref const & fallback, char const * _default) const { GET_SIMPLE_VALUE2(m_str_value, CPK_STRING); return fallback.get_str(k, _default); } symbol params::get_sym(char const * k, params_ref const & fallback, symbol const & _default) const { GET_VALUE2(return symbol::mk_symbol_from_c_ptr(it->second.m_sym_value);, CPK_SYMBOL); return fallback.get_sym(k, _default); } #define SET_VALUE(MATCH_CODE, ADD_CODE) { \ TRAVERSE_ENTRIES(if (it->first == k) { \ MATCH_CODE \ return; \ }); \ ADD_CODE \ } #define SET_SIMPLE_VALUE(FIELD_NAME, KIND) SET_VALUE({ \ del_value(*it); \ it->second.m_kind = KIND; \ it->second.FIELD_NAME = v; \ }, \ { \ entry new_entry; \ new_entry.first = symbol(k); \ new_entry.second.m_kind = KIND; \ new_entry.second.FIELD_NAME = v; \ m_entries.push_back(new_entry); \ }) // setters void params::set_bool(symbol const & k, bool v) { SET_SIMPLE_VALUE(m_bool_value, CPK_BOOL); } void params::set_bool(char const * k, bool v) { SET_SIMPLE_VALUE(m_bool_value, CPK_BOOL); } void params::set_uint(symbol const & k, unsigned v) { SET_SIMPLE_VALUE(m_uint_value, CPK_UINT); } void params::set_uint(char const * k, unsigned v) { SET_SIMPLE_VALUE(m_uint_value, CPK_UINT); } void params::set_double(symbol const & k, double v) { SET_SIMPLE_VALUE(m_double_value, CPK_DOUBLE); } void params::set_double(char const * k, double v) { SET_SIMPLE_VALUE(m_double_value, CPK_DOUBLE); } void params::set_str(symbol const & k, char const * v) { SET_SIMPLE_VALUE(m_str_value, CPK_STRING); } void params::set_str(char const * k, char const * v) { SET_SIMPLE_VALUE(m_str_value, CPK_STRING); } #define SET_RAT_VALUE() SET_VALUE({ \ if (it->second.m_kind != CPK_NUMERAL) { \ del_value(*it); \ it->second.m_kind = CPK_NUMERAL; \ it->second.m_rat_value = alloc(rational); \ } \ *(it->second.m_rat_value) = v; \ }, \ { \ entry new_entry; \ new_entry.first = symbol(k); \ new_entry.second.m_kind = CPK_NUMERAL; \ new_entry.second.m_rat_value = alloc(rational); \ *(new_entry.second.m_rat_value) = v; \ m_entries.push_back(new_entry); \ }) void params::set_rat(symbol const & k, rational const & v) { SET_RAT_VALUE(); } void params::set_rat(char const * k, rational const & v) { SET_RAT_VALUE(); } #define SET_SYM_VALUE() SET_VALUE({ \ del_value(*it); \ it->second.m_kind = CPK_SYMBOL; \ it->second.m_sym_value = v.bare_str(); \ }, \ { \ entry new_entry; \ new_entry.first = symbol(k); \ new_entry.second.m_kind = CPK_SYMBOL; \ new_entry.second.m_sym_value = v.bare_str(); \ m_entries.push_back(new_entry); \ }) void params::set_sym(symbol const & k, symbol const & v) { SET_SYM_VALUE(); } void params::set_sym(char const * k, symbol const & v) { SET_SYM_VALUE(); } #ifdef Z3DEBUG void pp(params_ref const & p) { std::cout << p << std::endl; } #endif z3-z3-4.8.7/src/util/params.h000066400000000000000000000116561356505360400156400ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: params.h Abstract: Parameters. Author: Leonardo (leonardo) 2011-04-22 Notes: --*/ #ifndef PARAMS_H_ #define PARAMS_H_ #include "util/cmd_context_types.h" #include "util/vector.h" std::string norm_param_name(char const * n); std::string norm_param_name(symbol const & n); typedef cmd_arg_kind param_kind; class params; class param_descrs; class params_ref { static params_ref g_empty_params_ref; params * m_params; void init(); void copy_core(params const * p); public: params_ref():m_params(nullptr) {} params_ref(params_ref const & p); ~params_ref(); static params_ref const & get_empty() { return g_empty_params_ref; } params_ref & operator=(params_ref const & p); // copy params from src void copy(params_ref const & src); void append(params_ref const & src) { copy(src); } bool get_bool(symbol const & k, bool _default) const; bool get_bool(char const * k, bool _default) const; unsigned get_uint(symbol const & k, unsigned _default) const; unsigned get_uint(char const * k, unsigned _default) const; double get_double(symbol const & k, double _default) const; double get_double(char const * k, double _default) const; char const * get_str(symbol const & k, char const * _default) const; char const * get_str(char const * k, char const * _default) const; rational get_rat(symbol const & k, rational const & _default) const; rational get_rat(char const * k, rational const & _default) const; symbol get_sym(symbol const & k, symbol const & _default) const; symbol get_sym(char const * k, symbol const & _default) const; bool get_bool(char const * k, params_ref const & fallback, bool _default) const; unsigned get_uint(char const * k, params_ref const & fallback, unsigned _default) const; double get_double(char const * k, params_ref const & fallback, double _default) const; char const * get_str(char const * k, params_ref const & fallback, char const * _default) const; symbol get_sym(char const * k, params_ref const & fallback, symbol const & _default) const; bool empty() const; bool contains(symbol const & k) const; bool contains(char const * k) const; void reset(); void reset(symbol const & k); void reset(char const * k); void set_bool(symbol const & k, bool v); void set_bool(char const * k, bool v); void set_uint(symbol const & k, unsigned v); void set_uint(char const * k, unsigned v); void set_double(symbol const & k, double v); void set_double(char const * k, double v); void set_str(symbol const & k, char const * v); void set_str(char const * k, char const * v); void set_rat(symbol const & k, rational const & v); void set_rat(char const * k, rational const & v); void set_sym(symbol const & k, symbol const & v); void set_sym(char const * k, symbol const & v); void display(std::ostream & out) const; void display_smt2(std::ostream& out, char const* module, param_descrs& module_desc) const; void validate(param_descrs const & p); /* \brief Display the value of the given parameter. It displays 'default' if k is not in the parameter set. */ void display(std::ostream & out, char const * k) const; void display(std::ostream & out, symbol const & k) const; }; inline std::ostream & operator<<(std::ostream & out, params_ref const & ref) { ref.display(out); return out; } class param_descrs { struct imp; imp * m_imp; public: param_descrs(); ~param_descrs(); void copy(param_descrs & other); void insert(char const * name, param_kind k, char const * descr, char const * def = nullptr, char const* module = nullptr); void insert(symbol const & name, param_kind k, char const * descr, char const * def = nullptr, char const* module = nullptr); bool contains(char const * name) const; bool contains(symbol const & name) const; void erase(char const * name); void erase(symbol const & name); param_kind get_kind(char const * name) const; param_kind get_kind(symbol const & name) const; param_kind get_kind_in_module(symbol & name) const; char const * get_descr(char const * name) const; char const * get_descr(symbol const & name) const; char const * get_default(char const * name) const; char const * get_default(symbol const & name) const; void display(std::ostream & out, unsigned indent = 0, bool smt2_style=false, bool include_descr=true) const; unsigned size() const; symbol get_param_name(unsigned idx) const; char const * get_module(symbol const& name) const; }; void insert_max_memory(param_descrs & r); 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); void insert_ctrl_c(param_descrs & r); #endif z3-z3-4.8.7/src/util/parray.h000066400000000000000000000424771356505360400156600ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: parray.h Abstract: Persistent Arrays. Author: Leonardo de Moura (leonardo) 2011-02-21. Revision History: --*/ #ifndef PARRAY_H_ #define PARRAY_H_ #include "util/vector.h" #include "util/trace.h" template class parray_manager { public: typedef typename C::value value; typedef typename C::value_manager value_manager; typedef typename C::allocator allocator; private: static size_t capacity(value * vs) { return vs == nullptr ? 0 : (reinterpret_cast(vs))[-1]; } value * allocate_values(size_t c) { size_t * mem = static_cast(m_allocator.allocate(sizeof(value)*c + sizeof(size_t))); *mem = c; ++mem; value * r = reinterpret_cast(mem); SASSERT(capacity(r) == c); TRACE("parray_mem", tout << "allocated values[" << c << "]: " << r << "\n";); return r; } void deallocate_values(value * vs) { if (vs == nullptr) return; size_t c = capacity(vs); TRACE("parray_mem", tout << "deallocated values[" << c << "]: " << vs << "\n";); size_t * mem = reinterpret_cast(vs); --mem; m_allocator.deallocate(sizeof(value)*c + sizeof(size_t), mem); } enum ckind { SET, PUSH_BACK, POP_BACK, ROOT }; struct cell { unsigned m_ref_count:30; unsigned m_kind:2; union { unsigned m_idx; unsigned m_size; }; value m_elem; union { cell * m_next; value * m_values; }; ckind kind() const { return static_cast(m_kind); } unsigned idx() const { SASSERT(kind() != ROOT); return m_idx; } unsigned size() const { SASSERT(kind() == ROOT); return m_size; } cell * next() const { SASSERT(kind() != ROOT); return m_next; } value const & elem() const { SASSERT(kind() == SET || kind() == PUSH_BACK); return m_elem; } cell(ckind k):m_ref_count(1), m_kind(k), m_size(0), m_values(nullptr) {} }; value_manager & m_vmanager; allocator & m_allocator; ptr_vector m_get_values_tmp; ptr_vector m_reroot_tmp; void inc_ref(value const & v) { if (C::ref_count) m_vmanager.inc_ref(v); } void dec_ref(value const & v) { if (C::ref_count) m_vmanager.dec_ref(v); } void dec_ref(unsigned sz, value * vs) { if (C::ref_count) for (unsigned i = 0; i < sz; i++) m_vmanager.dec_ref(vs[i]); } cell * mk(ckind k) { cell * r = new (m_allocator.allocate(sizeof(cell))) cell(k); TRACE("parray_mem", tout << "allocated cell: " << r << "\n";); return r; } void del(cell * c) { while (true) { cell * next = nullptr; switch (c->kind()) { case SET: case PUSH_BACK: dec_ref(c->elem()); next = c->next(); break; case POP_BACK: next = c->next(); break; case ROOT: dec_ref(c->size(), c->m_values); deallocate_values(c->m_values); break; } TRACE("parray_mem", tout << "deallocated cell: " << c << "\n";); c->~cell(); m_allocator.deallocate(sizeof(cell), c); if (next == nullptr) return; SASSERT(next->m_ref_count > 0); next->m_ref_count--; if (next->m_ref_count > 0) return; c = next; } } void inc_ref(cell * c) { if (!c) return; c->m_ref_count++; } void dec_ref(cell * c) { if (!c) return; TRACE("parray_mem", tout << "dec_ref(" << c << "), ref_count: " << c->m_ref_count << "\n";); SASSERT(c->m_ref_count > 0); c->m_ref_count--; if (c->m_ref_count == 0) del(c); } void expand(value * & vs) { size_t curr_capacity = capacity(vs); size_t new_capacity = curr_capacity == 0 ? 2 : (3 * curr_capacity + 1) >> 1; value * new_vs = allocate_values(new_capacity); if (curr_capacity > 0) { for (size_t i = 0; i < curr_capacity; i++) new_vs[i] = vs[i]; deallocate_values(vs); } vs = new_vs; } void rset(value * vs, unsigned i, value const & v) { inc_ref(v); dec_ref(vs[i]); vs[i] = v; } void rset(cell * c, unsigned i, value const & v) { SASSERT(c->kind() == ROOT); SASSERT(i < c->size()); rset(c->m_values, i, v); } void rpush_back(value * & vs, unsigned & sz, value const & v) { if (sz == capacity(vs)) expand(vs); SASSERT(sz < capacity(vs)); inc_ref(v); vs[sz] = v; sz++; } void rpush_back(cell * c, value const & v) { SASSERT(c->kind() == ROOT); rpush_back(c->m_values, c->m_size, v); } void rpop_back(value * vs, unsigned & sz) { sz--; dec_ref(vs[sz]); } void rpop_back(cell * c) { SASSERT(c->kind() == ROOT); rpop_back(c->m_values, c->m_size); } void copy_values(value * s, unsigned sz, value * & t) { SASSERT(t == 0); t = allocate_values(capacity(s)); for (unsigned i = 0; i < sz; i++) { t[i] = s[i]; inc_ref(t[i]); } } unsigned get_values(cell * s, value * & vs) { ptr_vector & cs = m_get_values_tmp; cs.reset(); cell * r = s; while (r->kind() != ROOT) { cs.push_back(r); r = r->next(); } SASSERT(r->kind() == ROOT); unsigned sz = r->m_size; vs = nullptr; copy_values(r->m_values, sz, vs); unsigned i = cs.size(); while (i > 0) { --i; cell * curr = cs[i]; switch (curr->kind()) { case SET: rset(vs, curr->m_idx, curr->m_elem); break; case POP_BACK: rpop_back(vs, sz); break; case PUSH_BACK: rpush_back(vs, sz, curr->m_elem); break; case ROOT: UNREACHABLE(); break; } } return sz; } void unfold(cell * c) { if (c->kind() == ROOT) return; value * vs; unsigned sz = get_values(c, vs); dec_ref(c->m_next); if (c->kind() == SET || c->kind() == PUSH_BACK) dec_ref(c->m_elem); c->m_next = nullptr; c->m_kind = ROOT; c->m_size = sz; c->m_values = vs; SASSERT(c->kind() == ROOT); } public: class ref { cell * m_ref; unsigned m_updt_counter; // counter for minimizing memory consumption when using preserve_roots option ref(cell * r):m_ref(r), m_updt_counter(0) {} bool root() const { return m_ref->kind() == ROOT; } bool unshared() const { return m_ref->m_ref_count == 1; } friend class parray_manager; public: ref():m_ref(nullptr), m_updt_counter(0) {} }; public: parray_manager(value_manager & m, allocator & a): m_vmanager(m), m_allocator(a) { } value_manager & manager() { return m_vmanager; } void mk(ref & r) { dec_ref(r.m_ref); cell * new_c = mk(ROOT); r.m_ref = new_c; r.m_updt_counter = 0; SASSERT(new_c->m_ref_count == 1); } void del(ref & r) { dec_ref(r.m_ref); r.m_ref = nullptr; r.m_updt_counter = 0; } void copy(ref const & s, ref & t) { inc_ref(s.m_ref); dec_ref(t.m_ref); t.m_ref = s.m_ref; t.m_updt_counter = 0; } unsigned size(ref const & r) const { cell * c = r.m_ref; if (c == nullptr) return 0; while (true) { switch (c->kind()) { case SET: c = c->next(); break; case PUSH_BACK: return c->idx() + 1; case POP_BACK: return c->idx() - 1; case ROOT: return c->size(); } } } bool empty(ref const & r) const { return size(r) == 0; } value const & get(ref const & r, unsigned i) const { SASSERT(i < size(r)); unsigned trail_sz = 0; cell * c = r.m_ref; while (true) { if (trail_sz > C::max_trail_sz) { const_cast(this)->reroot(const_cast(r)); SASSERT(r.m_ref->kind() == ROOT); return r.m_ref->m_values[i]; } switch (c->kind()) { case SET: case PUSH_BACK: if (i == c->idx()) return c->elem(); trail_sz++; c = c->next(); break; case POP_BACK: trail_sz++; c = c->next(); break; case ROOT: return c->m_values[i]; } } } void set(ref & r, unsigned i, value const & v) { SASSERT(i < size(r)); if (r.root()) { if (r.unshared()) { rset(r.m_ref, i, v); return; } if (C::preserve_roots) { if (r.m_updt_counter > size(r)) { unshare(r); SASSERT(r.unshared()); SASSERT(r.m_updt_counter == 0); rset(r.m_ref, i, v); return; } r.m_updt_counter++; cell * c = r.m_ref; cell * new_c = mk(ROOT); new_c->m_size = c->m_size; new_c->m_values = c->m_values; inc_ref(new_c); c->m_kind = SET; c->m_idx = i; c->m_elem = c->m_values[i]; inc_ref(c->m_elem); c->m_next = new_c; dec_ref(c); r.m_ref = new_c; rset(new_c, i, v); SASSERT(new_c->m_ref_count == 2); return; } } cell * new_c = mk(SET); new_c->m_idx = i; inc_ref(v); new_c->m_elem = v; new_c->m_next = r.m_ref; r.m_ref = new_c; SASSERT(new_c->m_ref_count == 1); } void set(ref const & s, unsigned i, value const & v, ref & r) { SASSERT(i < size(s)); if (&s == &r) { set(r, i, v); return; } copy(s, r); set(r, i, v); } void push_back(ref & r, value const & v) { if (r.m_ref == nullptr) mk(r); if (r.root()) { if (r.unshared()) { rpush_back(r.m_ref, v); return; } if (C::preserve_roots) { if (r.m_updt_counter > size(r)) { unshare(r); SASSERT(r.unshared()); SASSERT(r.m_updt_counter == 0); rpush_back(r.m_ref, v); return; } r.m_updt_counter++; cell * c = r.m_ref; SASSERT(c->m_ref_count > 1); cell * new_c = mk(ROOT); new_c->m_size = c->m_size; new_c->m_values = c->m_values; inc_ref(new_c); c->m_kind = POP_BACK; c->m_idx = new_c->m_size + 1; c->m_next = new_c; dec_ref(c); r.m_ref = new_c; rpush_back(new_c, v); SASSERT(new_c->m_ref_count == 2); return; } } cell * new_c = mk(PUSH_BACK); new_c->m_idx = size(r.m_ref); inc_ref(v); new_c->m_elem = v; new_c->m_next = r.m_ref; r.m_ref = new_c; SASSERT(new_c->m_ref_count == 1); } void push_back(ref const & s, value const & v, ref & r) { if (&s == &r) { push_back(r, v); return; } copy(s, r); push_back(r, v); } void pop_back(ref & r) { SASSERT(!empty(r)); if (r.root()) { if (r.unshared()) { rpop_back(r.m_ref); return; } if (C::preserve_roots) { if (r.m_updt_counter > size(r)) { unshare(r); SASSERT(r.unshared()); SASSERT(r.m_updt_counter == 0); rpop_back(r.m_ref); return; } r.m_updt_counter++; cell * c = r.m_ref; SASSERT(c->m_ref_count > 1); cell * new_c = mk(ROOT); new_c->m_size = c->m_size; new_c->m_values = c->m_values; inc_ref(new_c); c->m_kind = PUSH_BACK; c->m_idx = new_c->m_size - 1; c->m_elem = new_c->m_values[c->m_idx]; inc_ref(c->m_elem); c->m_next = new_c; dec_ref(c); r.m_ref = new_c; rpop_back(new_c); SASSERT(new_c->m_ref_count == 2); return; } } cell * new_c = mk(POP_BACK); new_c->m_idx = size(r.m_ref); new_c->m_next = r.m_ref; r.m_ref = new_c; SASSERT(new_c->m_ref_count == 1); } void pop_back(ref const & s, ref & r) { SASSERT(!empty(s)); if (&s == &r) { pop_back(r); return; } copy(s, r); pop_back(r); } void unshare(ref & r) { if (r.root() && r.unshared()) return; cell * c = r.m_ref; cell * new_c = mk(ROOT); new_c->m_size = get_values(c, new_c->m_values); SASSERT(new_c->m_ref_count == 1); dec_ref(c); r.m_ref = new_c; r.m_updt_counter = 0; SASSERT(r.root()); SASSERT(r.unshared()); } void unfold(ref & r) { if (r.root()) return; unfold(r.m_ref); r.m_updt_counter = 0; SASSERT(r.root()); } void reroot(ref & r) { if (r.root()) return; ptr_vector & cs = m_reroot_tmp; cs.reset(); unsigned r_sz = size(r); unsigned trail_split_idx = r_sz / C::factor; unsigned i = 0; cell * c = r.m_ref; while (c->kind() != ROOT && i < trail_split_idx) { cs.push_back(c); c = c->next(); i++; } if (c->kind() != ROOT) { // root is too far away. unfold(c); } SASSERT(c->kind() == ROOT); i = cs.size(); while (i > 0) { --i; cell * p = cs[i]; SASSERT(c->m_kind == ROOT); unsigned sz = c->m_size; value * vs = c->m_values; SASSERT(p->m_kind != ROOT); SASSERT(p->m_next == c); switch (p->m_kind) { case SET: c->m_kind = SET; c->m_idx = p->m_idx; c->m_elem = vs[c->m_idx]; vs[p->m_idx] = p->m_elem; break; case PUSH_BACK: c->m_kind = POP_BACK; if (sz == capacity(vs)) expand(vs); c->m_idx = sz; vs[sz] = p->m_elem; sz++; break; case POP_BACK: c->m_kind = PUSH_BACK; --sz; c->m_idx = sz; c->m_elem = vs[sz]; break; case ROOT: UNREACHABLE(); break; } inc_ref(p); c->m_next = p; // p does not point to c anymore dec_ref(c); p->m_kind = ROOT; p->m_size = sz; p->m_values = vs; c = p; } SASSERT(c == r.m_ref); SASSERT(c->kind() == ROOT); SASSERT(c->m_size == r_sz); r.m_updt_counter = 0; SASSERT(r.root()); } void display_info(std::ostream & out, ref const & r) { cell * c = r.m_ref; if (c == 0) { out << ""; return; } while (true) { out << "cell[" << c << ", "; switch (c->kind()) { case SET: out << "set, " << c->m_idx; break; case PUSH_BACK: out << "push, " << c->m_idx; break; case POP_BACK: out << "pop, " << c->m_idx; break; case ROOT: out << "root, " << c->m_size << ", " << capacity(c->m_values); break; } out << "]#" << c->m_ref_count; if (c->kind() == ROOT) break; out << " -> "; c = c->next(); } } }; template struct dummy_value_manager { void inc_ref(T const &) {} void dec_ref(T const &) {} }; #endif z3-z3-4.8.7/src/util/permutation.cpp000066400000000000000000000030201356505360400172410ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: permutation.cpp Abstract: Goodies for managing permutations. Author: Leonardo de Moura (leonardo) 2011-06-10. Revision History: --*/ #include "util/permutation.h" permutation::permutation(unsigned size) { reset(size); } void permutation::reset(unsigned size) { m_p.reset(); m_inv_p.reset(); for (unsigned i = 0; i < size; i++) { m_p.push_back(i); m_inv_p.push_back(i); } } void permutation::swap(unsigned i, unsigned j) { unsigned i_prime = m_p[i]; unsigned j_prime = m_p[j]; std::swap(m_p[i], m_p[j]); std::swap(m_inv_p[i_prime], m_inv_p[j_prime]); } /** \brief Move i after j. */ void permutation::move_after(unsigned i, unsigned j) { if (i >= j) return; unsigned i_prime = m_p[i]; for (unsigned k = i; k < j; k++) { m_p[k] = m_p[k+1]; m_inv_p[m_p[k]] = k; } m_p[j] = i_prime; m_inv_p[i_prime] = j; SASSERT(check_invariant()); } void permutation::display(std::ostream & out) const { unsigned n = m_p.size(); for (unsigned i = 0; i < n; i++) { if (i > 0) out << " "; out << i << ":" << m_p[i]; } } bool permutation::check_invariant() const { SASSERT(m_p.size() == m_inv_p.size()); unsigned n = m_p.size(); for (unsigned i = 0; i < n; i++) { SASSERT(m_p[i] < n); SASSERT(m_inv_p[i] < n); SASSERT(m_p[m_inv_p[i]] == i); SASSERT(m_inv_p[m_p[i]] == i); } return true; } z3-z3-4.8.7/src/util/permutation.h000066400000000000000000000043021356505360400167120ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: permutation.h Abstract: Simple abstraction for managing permutations. Author: Leonardo de Moura (leonardo) 2011-06-10. Revision History: --*/ #ifndef PERMUTATION_H_ #define PERMUTATION_H_ #include #include "util/vector.h" class permutation { unsigned_vector m_p; unsigned_vector m_inv_p; public: permutation(unsigned size = 0); void reset(unsigned size = 0); unsigned operator()(unsigned i) const { return m_p[i]; } unsigned inv(unsigned i_prime) const { return m_inv_p[i_prime]; } void swap(unsigned i, unsigned j); void move_after(unsigned i, unsigned j); void display(std::ostream & out) const; bool check_invariant() const; }; inline std::ostream & operator<<(std::ostream & out, permutation const & p) { p.display(out); return out; } /** \brief Apply permutation p to data. The algorithm does not use any extra memory. Requirement: swap(T, T) must be available. This version will perform destructive updates to p. Use apply_permutation if p must not be preserved */ template void apply_permutation_core(unsigned sz, T * data, unsigned * p) { int * p1 = reinterpret_cast(p); for (int i = 0; i < static_cast(sz); i++) { if (p1[i] < 0) continue; // already processed int j = i; while (true) { SASSERT(j >= 0); int p_j = p1[j]; SASSERT(p_j >= 0); SASSERT(p_j < static_cast(sz)); p1[j] = - p1[j] - 1; // mark as done if (p_j == i) break; // cycle starting at i is done swap(data[j], data[p_j]); j = p_j; } } } /** \brief Apply permutation p to data. The algorithm does not use any extra memory. Requirement: swap(T, T) must be available. */ template void apply_permutation(unsigned sz, T * data, unsigned const * p) { apply_permutation_core(sz, data, const_cast(p)); // restore p int * p1 = reinterpret_cast(const_cast(p)); for (unsigned i = 0; i < sz; i++) { p1[i] = - p1[i] - 1; } } #endif z3-z3-4.8.7/src/util/plugin_manager.h000066400000000000000000000026461356505360400173440ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: plugin_manager.h Abstract: Author: Leonardo de Moura (leonardo) 2007-09-18. Revision History: --*/ #ifndef PLUGIN_MANAGER_H_ #define PLUGIN_MANAGER_H_ #include "util/util.h" template class plugin_manager { ptr_vector m_fid2plugins; ptr_vector m_plugins; public: ~plugin_manager() { reset(); } void reset() { std::for_each(m_plugins.begin(), m_plugins.end(), delete_proc()); release(); } /** \brief Release ownership of the plugins. */ void release() { m_fid2plugins.reset(); m_plugins.reset(); } void register_plugin(Plugin * p) { SASSERT(p); family_id fid = p->get_family_id(); SASSERT(m_fid2plugins.get(fid, 0) == 0); m_fid2plugins.setx(fid, p, 0); m_plugins.push_back(p); } Plugin * get_plugin(family_id fid) const { if (fid == null_family_id) { return nullptr; } return m_fid2plugins.get(fid, 0); } ptr_vector const& plugins() const { return m_plugins; } typename ptr_vector::const_iterator begin() const { return m_plugins.begin(); } typename ptr_vector::const_iterator end() const { return m_plugins.end(); } }; #endif /* PLUGIN_MANAGER_H_ */ z3-z3-4.8.7/src/util/pool.h000066400000000000000000000013221356505360400153130ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: pool.h Abstract: Object pool. Author: Leonardo de Moura (leonardo) 2007-02-15. Revision History: --*/ #ifndef POOL_H_ #define POOL_H_ #include "util/util.h" #include "util/vector.h" template class pool { ptr_vector m_objs; public: ~pool() { std::for_each(m_objs.begin(), m_objs.end(), delete_proc()); } T * mk() { if (m_objs.empty()) { return alloc(T); } else { T * r = m_objs.back(); m_objs.pop_back(); return r; } } void recycle(T * obj) { m_objs.push_back(obj); } }; #endif /* POOL_H_ */ z3-z3-4.8.7/src/util/pop_scopes.h000066400000000000000000000021001356505360400165070ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: pop_scopes.h Abstract: Author: Leonardo de Moura (leonardo) 2008-06-02. Revision History: --*/ #ifndef POP_SCOPES_H_ #define POP_SCOPES_H_ #define POP_SCOPES(_num_scopes, _lim, _trail, _action) \ if (_num_scopes > 0) \ { \ unsigned scope_lvl = _lim.size(); \ unsigned new_lvl = scope_lvl - _num_scopes; \ unsigned curr_size = _trail.size(); \ unsigned old_size = _lim[new_lvl]; \ for (unsigned i = curr_size-1; i >= old_size && i != static_cast(-1); --i) { \ _action; \ } \ _trail.shrink(old_size); \ _lim.shrink(new_lvl); \ } #endif /* POP_SCOPES_H_ */ z3-z3-4.8.7/src/util/prime_generator.cpp000066400000000000000000000062501356505360400200640ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: prime_generator.cpp Abstract: Prime generator Author: Leonardo (leonardo) 2011-12-23 Notes: --*/ #include "util/prime_generator.h" #include "util/mutex.h" #define PRIME_LIST_MAX_SIZE 1<<20 void prime_generator::process_next_k_numbers(uint64_t k) { svector todo; uint64_t begin = m_primes.back() + 2; uint64_t end = begin + k; for (uint64_t i = begin; i < end; i+=2) { todo.push_back(i); } unsigned j = 1; SASSERT(m_primes[j] == 3); while (!todo.empty()) { unsigned sz = m_primes.size(); for (; j < sz; j++) { uint64_t p = m_primes[j]; unsigned todo_sz = todo.size(); unsigned k1 = 0; unsigned k2 = 0; for (; k1 < todo_sz; k1++) { if (todo[k1] % p == 0) continue; todo[k2] = todo[k1]; k2++; } todo.shrink(k2); if (k2 == 0) return; if (p > (todo[k2-1] / p) + 1) { // all numbers in todo are primes for (unsigned k1 = 0; k1 < k2; k1++) { m_primes.push_back(todo[k1]); } return; } } uint64_t p = m_primes.back(); p = p*p; unsigned todo_sz = todo.size(); unsigned k1 = 0; for (k1 = 0; k1 < todo_sz; k1++) { if (todo[k1] < p) { m_primes.push_back(todo[k1]); } else { break; } } unsigned k2 = 0; for (; k1 < todo_sz; k1++, k2++) { todo[k2] = todo[k1]; } todo.shrink(k2); } } void prime_generator::initialize() { m_primes.push_back(2); m_primes.push_back(3); process_next_k_numbers(128); } void prime_generator::finalize() { m_primes.finalize(); } uint64_t prime_generator::operator()(unsigned idx) { if (idx < m_primes.size()) return m_primes[idx]; if (idx > PRIME_LIST_MAX_SIZE) throw prime_generator_exception("prime generator capacity exceeded"); process_next_k_numbers(1024); if (idx < m_primes.size()) return m_primes[idx]; while (idx <= m_primes.size()) process_next_k_numbers(1024*16); return m_primes[idx]; } static prime_generator g_prime_generator; prime_iterator::prime_iterator(prime_generator * g):m_idx(0) { if (g == nullptr) { m_generator = &g_prime_generator; m_global = true; } else { m_generator = g; m_global = false; } } static DECLARE_MUTEX(g_prime_iterator); uint64_t prime_iterator::next() { unsigned idx = m_idx; m_idx++; if (!m_global) { return (*m_generator)(idx); } else { uint64_t r; lock_guard lock(*g_prime_iterator); { r = (*m_generator)(idx); } return r; } } void prime_iterator::initialize() { ALLOC_MUTEX(g_prime_iterator); g_prime_generator.initialize(); } void prime_iterator::finalize() { g_prime_generator.finalize(); DEALLOC_MUTEX(g_prime_iterator); } z3-z3-4.8.7/src/util/prime_generator.h000066400000000000000000000020611356505360400175250ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: prime_generator.h Abstract: Prime generator Author: Leonardo (leonardo) 2011-12-23 Notes: --*/ #ifndef PRIME_GENERATOR_H_ #define PRIME_GENERATOR_H_ #include "util/vector.h" #include "util/z3_exception.h" #include "util/util.h" class prime_generator_exception : public default_exception { public: prime_generator_exception(char const * msg):default_exception(msg) {} }; /** \brief Prime generator */ class prime_generator { svector m_primes; void process_next_k_numbers(uint64_t k); public: uint64_t operator()(unsigned idx); void initialize(); void finalize(); }; class prime_iterator { unsigned m_idx; prime_generator * m_generator; bool m_global; public: prime_iterator(prime_generator * g = nullptr); uint64_t next(); static void initialize(); static void finalize(); /* ADD_INITIALIZER('prime_iterator::initialize();') ADD_FINALIZER('prime_iterator::finalize();') */ }; #endif z3-z3-4.8.7/src/util/ptr_scoped_buffer.h000066400000000000000000000042101356505360400200340ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: ptr_scoped_buffer.h Abstract: Author: Leonardo de Moura (leonardo) 2011-03-03. Revision History: --*/ #ifndef PTR_SCOPED_BUFFER_H_ #define PTR_SCOPED_BUFFER_H_ #include "util/util.h" #include "util/debug.h" #include "util/buffer.h" template > class ptr_scoped_buffer : private ptr_buffer { D m_deallocator; void deallocate_all() { typename ptr_buffer::iterator it = ptr_buffer::begin(); typename ptr_buffer::iterator end = ptr_buffer::end(); for (; it != end; ++it) m_deallocator(*it); } public: typedef typename ptr_buffer::const_iterator const_iterator; ptr_scoped_buffer(D const & m = D()):ptr_buffer(), m_deallocator(m) {} ~ptr_scoped_buffer() { deallocate_all(); } void reset() { deallocate_all(); ptr_buffer::reset(); } void finalize() { deallocate_all(); ptr_buffer::finalize(); } /** \brief Release ownership of the pointers stored in the buffer */ void release() { ptr_buffer::reset(); } unsigned size() const { return ptr_buffer::size(); } bool empty() const { return ptr_buffer::empty(); } const_iterator begin() const { return ptr_buffer::begin(); } const_iterator end() const { return ptr_buffer::end(); } void push_back(T * elem) { return ptr_buffer::push_back(elem); } T * back() const { return ptr_buffer::back(); } void pop_back() { m_deallocator(back()); ptr_buffer::pop_back(); } T * get(unsigned idx) const { return ptr_buffer::get(idx); } void set(unsigned idx, T * e) { T * old_e = get(idx); if (e != old_e) m_deallocator(old_e); ptr_buffer::set(idx, e); } void append(unsigned n, T * const * elems) { ptr_buffer::append(n, elems); } }; #endif z3-z3-4.8.7/src/util/queue.h000066400000000000000000000024641356505360400154760ustar00rootroot00000000000000/*++ Copyright (c) 2017 Microsoft Corporation Module Name: queue.h Abstract: Generic queue. Author: Nikolaj Bjorner (nbjorner) 2017-4-17 Notes: --*/ #ifndef _QUEUE_H_ #define _QUEUE_H_ #include "vector.h" template class queue { vector m_elems; unsigned m_head; unsigned m_capacity; public: queue(): m_head(0), m_capacity(0) {} void push(T const& t) { m_elems.push_back(t); } bool empty() const { return m_head == m_elems.size(); } T top() const { return m_elems[m_head]; } T pop_front() { SASSERT(!empty()); m_capacity = std::max(m_capacity, m_elems.size()); SASSERT(m_head < m_elems.size()); if (2 * m_head > m_capacity && m_capacity > 10) { for (unsigned i = 0; i < m_elems.size() - m_head; ++i) { m_elems[i] = m_elems[i + m_head]; } m_elems.shrink(m_elems.size() - m_head); m_head = 0; } return m_elems[m_head++]; } T back() const { return m_elems[m_elems.size() - 1]; } T pop_back() { SASSERT(!empty()); SASSERT(m_head < m_elems.size()); T result = back(); m_elems.shrink(m_elems.size() - 1); return result; } }; #endif z3-z3-4.8.7/src/util/rational.cpp000066400000000000000000000056431356505360400165200ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: rational.cpp Abstract: Rational numbers Author: Leonardo de Moura (leonardo) 2006-09-18. Revision History: --*/ #include #include "util/mutex.h" #include "util/util.h" #include "util/rational.h" synch_mpq_manager * rational::g_mpq_manager = nullptr; rational rational::m_zero; rational rational::m_one; rational rational::m_minus_one; vector rational::m_powers_of_two; static void mk_power_up_to(vector & pws, unsigned n) { if (pws.empty()) { pws.push_back(rational::one()); } unsigned sz = pws.size(); rational curr = pws[sz - 1]; rational two(2); for (unsigned i = sz; i <= n; i++) { curr *= two; pws.push_back(curr); } } static DECLARE_MUTEX(g_powers_of_two); rational rational::power_of_two(unsigned k) { rational result; lock_guard lock(*g_powers_of_two); { if (k >= m_powers_of_two.size()) mk_power_up_to(m_powers_of_two, k+1); result = m_powers_of_two[k]; } return result; } // in inf_rational.cpp void initialize_inf_rational(); void finalize_inf_rational(); // in inf_int_rational.cpp void initialize_inf_int_rational(); void finalize_inf_int_rational(); void rational::initialize() { if (!g_mpq_manager) { ALLOC_MUTEX(g_powers_of_two); g_mpq_manager = alloc(synch_mpq_manager); m().set(m_zero.m_val, 0); m().set(m_one.m_val, 1); m().set(m_minus_one.m_val, -1); initialize_inf_rational(); initialize_inf_int_rational(); } } void rational::finalize() { finalize_inf_rational(); finalize_inf_int_rational(); m_powers_of_two.finalize(); m_zero.~rational(); m_one.~rational(); m_minus_one.~rational(); dealloc(g_mpq_manager); g_mpq_manager = nullptr; DEALLOC_MUTEX(g_powers_of_two); } bool rational::limit_denominator(rational &num, rational const& limit) { rational n, d; n = numerator(num); d = denominator(num); if (d < limit) return false; /* Iteratively computes approximation using continuous fraction decomposition p(-1) = 0, p(0) = 1 p(j) = t(j)*p(j-1) + p(j-2) q(-1) = 1, q(0) = 0 q(j) = t(j)*q(j-1) + q(j-2) cf[t1; t2, ..., tr] = p(r) / q(r) for r >= 1 reference: https://www.math.u-bordeaux.fr/~pjaming/M1/exposes/MA2.pdf */ rational p0(0), p1(1); rational q0(1), q1(0); while (!d.is_zero()) { rational tj(0), rem(0); rational p2(0), q2(0); tj = div(n, d); q2 = tj * q1 + q0; p2 = tj * p1 + p0; if (q2 >= limit) { num = p2/q2; return true; } rem = n - tj * d; p0 = p1; p1 = p2; q0 = q1; q1 = q2; n = d; d = rem; } return false; } z3-z3-4.8.7/src/util/rational.h000066400000000000000000000343321356505360400161620ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: rational.h Abstract: Rational numbers Author: Leonardo de Moura (leonardo) 2006-09-18. Revision History: --*/ #ifndef RATIONAL_H_ #define RATIONAL_H_ #include "util/mpq.h" class rational { mpq m_val; static rational m_zero; static rational m_one; static rational m_minus_one; static vector m_powers_of_two; static synch_mpq_manager * g_mpq_manager; static synch_mpq_manager & m() { return *g_mpq_manager; } public: static void initialize(); static void finalize(); /* ADD_INITIALIZER('rational::initialize();') ADD_FINALIZER('rational::finalize();') */ rational() {} rational(rational const & r) { m().set(m_val, r.m_val); } rational(rational && r) : m_val(std::move(r.m_val)) {} explicit rational(int n) { m().set(m_val, n); } explicit rational(unsigned n) { m().set(m_val, n); } rational(int n, int d) { m().set(m_val, n, d); } rational(mpq const & q) { m().set(m_val, q); } rational(mpz const & z) { m().set(m_val, z); } explicit rational(double z) { UNREACHABLE(); } explicit rational(char const * v) { m().set(m_val, v); } struct i64 {}; rational(int64_t i, i64) { m().set(m_val, i); } struct ui64 {}; rational(uint64_t i, ui64) { m().set(m_val, i); } ~rational() { m().del(m_val); } mpq const & to_mpq() const { return m_val; } unsigned bitsize() const { return m().bitsize(m_val); } unsigned storage_size() const { return m().storage_size(m_val); } void reset() { m().reset(m_val); } bool is_int() const { return m().is_int(m_val); } bool is_small() const { return m().is_small(m_val); } bool is_big() const { return !is_small(); } unsigned hash() const { return m().hash(m_val); } struct hash_proc { unsigned operator()(rational const& r) const { return r.hash(); } }; struct eq_proc { bool operator()(rational const& r1, rational const& r2) const { return r1 == r2; } }; void swap(rational & n) { m().swap(m_val, n.m_val); } std::string to_string() const { return m().to_string(m_val); } void display(std::ostream & out) const { return m().display(out, m_val); } void display_decimal(std::ostream & out, unsigned prec, bool truncate = false) const { return m().display_decimal(out, m_val, prec, truncate); } void display_smt2(std::ostream & out) const { return m().display_smt2(out, m_val, false); } void display_hex(std::ostream & out, unsigned num_bits) const { SASSERT(is_int()); return m().display_hex(out, m_val.numerator(), num_bits); } void display_bin(std::ostream & out, unsigned num_bits) const { SASSERT(is_int()); return m().display_bin(out, m_val.numerator(), num_bits); } bool is_uint64() const { return m().is_uint64(m_val); } bool is_int64() const { return m().is_int64(m_val); } uint64_t get_uint64() const { return m().get_uint64(m_val); } int64_t get_int64() const { return m().get_int64(m_val); } bool is_unsigned() const { return is_uint64() && (get_uint64() < (1ull << 32)); } unsigned get_unsigned() const { SASSERT(is_unsigned()); return static_cast(get_uint64()); } bool is_int32() const { if (is_small() && is_int()) return true; // we don't assume that if it is small, then it is int32. if (!is_int64()) return false; int64_t v = get_int64(); return INT_MIN <= v && v <= INT_MAX; } int get_int32() const { SASSERT(is_int32()); return (int)get_int64(); } double get_double() const { return m().get_double(m_val); } rational const & get_rational() const { return *this; } rational const & get_infinitesimal() const { return m_zero; } rational & operator=(rational const & r) { m().set(m_val, r.m_val); return *this; } rational & operator=(int v) { *this = rational(v); return *this; } rational & operator=(double v) { UNREACHABLE(); return *this; } friend inline rational numerator(rational const & r) { rational result; m().get_numerator(r.m_val, result.m_val); return result; } friend inline rational denominator(rational const & r) { rational result; m().get_denominator(r.m_val, result.m_val); return result; } rational & operator+=(rational const & r) { m().add(m_val, r.m_val, m_val); return *this; } rational & operator+=(int r) { (*this) += rational(r); return *this; } rational & operator-=(rational const & r) { m().sub(m_val, r.m_val, m_val); return *this; } rational & operator*=(rational const & r) { m().mul(m_val, r.m_val, m_val); return *this; } rational & operator/=(rational const & r) { m().div(m_val, r.m_val, m_val); return *this; } rational & operator%=(rational const & r) { m().rem(m_val, r.m_val, m_val); return *this; } friend inline rational div(rational const & r1, rational const & r2) { rational r; rational::m().idiv(r1.m_val, r2.m_val, r.m_val); return r; } friend inline void div(rational const & r1, rational const & r2, rational & r) { rational::m().idiv(r1.m_val, r2.m_val, r.m_val); } friend inline rational machine_div(rational const & r1, rational const & r2) { rational r; rational::m().machine_idiv(r1.m_val, r2.m_val, r.m_val); return r; } friend inline rational machine_div_rem(rational const & r1, rational const & r2, rational & rem) { rational r; rational::m().machine_idiv_rem(r1.m_val, r2.m_val, r.m_val, rem.m_val); return r; } friend inline rational mod(rational const & r1, rational const & r2) { rational r; rational::m().mod(r1.m_val, r2.m_val, r.m_val); return r; } friend inline void mod(rational const & r1, rational const & r2, rational & r) { rational::m().mod(r1.m_val, r2.m_val, r.m_val); } friend inline rational operator%(rational const & r1, rational const & r2) { rational r; rational::m().rem(r1.m_val, r2.m_val, r.m_val); return r; } friend inline rational mod_hat(rational const & a, rational const & b) { SASSERT(b.is_pos()); rational r = mod(a,b); SASSERT(r.is_nonneg()); rational r2 = r; r2 *= rational(2); if (operator<(b, r2)) { r -= b; } return r; } rational & operator++() { m().add(m_val, m().mk_q(1), m_val); return *this; } const rational operator++(int) { rational tmp(*this); ++(*this); return tmp; } rational & operator--() { m().sub(m_val, m().mk_q(1), m_val); return *this; } const rational operator--(int) { rational tmp(*this); --(*this); return tmp; } friend inline bool operator==(rational const & r1, rational const & r2) { return rational::m().eq(r1.m_val, r2.m_val); } friend inline bool operator<(rational const & r1, rational const & r2) { return rational::m().lt(r1.m_val, r2.m_val); } void neg() { m().neg(m_val); } bool is_zero() const { return m().is_zero(m_val); } bool is_one() const { return m().is_one(m_val); } bool is_minus_one() const { return m().is_minus_one(m_val); } bool is_neg() const { return m().is_neg(m_val); } bool is_pos() const { return m().is_pos(m_val); } bool is_nonneg() const { return m().is_nonneg(m_val); } bool is_nonpos() const { return m().is_nonpos(m_val); } bool is_even() const { return m().is_even(m_val); } friend inline rational floor(rational const & r) { rational f; rational::m().floor(r.m_val, f.m_val); return f; } friend inline rational ceil(rational const & r) { rational f; rational::m().ceil(r.m_val, f.m_val); return f; } rational expt(int n) const { rational result; m().power(m_val, n, result.m_val); return result; } static rational power_of_two(unsigned k); bool is_power_of_two(unsigned & shift) { return m().is_power_of_two(m_val, shift); } static rational const & zero() { return m_zero; } static rational const & one() { return m_one; } static rational const & minus_one() { return m_minus_one; } void addmul(rational const & c, rational const & k) { if (c.is_one()) operator+=(k); else if (c.is_minus_one()) operator-=(k); else { rational tmp(k); tmp *= c; operator+=(tmp); } } // Perform: this -= c * k void submul(const rational & c, const rational & k) { if (c.is_one()) operator-=(k); else if (c.is_minus_one()) operator+=(k); else { rational tmp(k); tmp *= c; operator-=(tmp); } } bool is_int_perfect_square(rational & root) const { return m().is_int_perfect_square(m_val, root.m_val); } bool is_perfect_square(rational & root) const { return m().is_perfect_square(m_val, root.m_val); } bool root(unsigned n, rational & root) const { return m().root(m_val, n, root.m_val); } friend inline std::ostream & operator<<(std::ostream & target, rational const & r) { target << m().to_string(r.m_val); return target; } friend inline rational gcd(rational const & r1, rational const & r2); // // extended Euclid: // r1*a + r2*b = gcd // friend inline rational gcd(rational const & r1, rational const & r2, rational & a, rational & b); friend inline rational lcm(rational const & r1, rational const & r2) { rational result; m().lcm(r1.m_val, r2.m_val, result.m_val); return result; } friend inline rational bitwise_or(rational const & r1, rational const & r2) { rational result; m().bitwise_or(r1.m_val, r2.m_val, result.m_val); return result; } friend inline rational bitwise_and(rational const & r1, rational const & r2) { rational result; m().bitwise_and(r1.m_val, r2.m_val, result.m_val); return result; } friend inline rational bitwise_xor(rational const & r1, rational const & r2) { rational result; m().bitwise_xor(r1.m_val, r2.m_val, result.m_val); return result; } friend inline rational bitwise_not(unsigned sz, rational const & r1) { rational result; m().bitwise_not(sz, r1.m_val, result.m_val); return result; } friend inline rational abs(rational const & r); rational to_rational() const { return *this; } static bool is_rational() { return true; } unsigned get_num_bits() const { rational two(2); SASSERT(is_int()); SASSERT(!is_neg()); rational n(*this); unsigned num_bits = 1; n = div(n, two); while (n.is_pos()) { ++num_bits; n = div(n, two); } return num_bits; } static bool limit_denominator(rational &num, rational const& limit); }; inline bool operator!=(rational const & r1, rational const & r2) { return !operator==(r1, r2); } inline bool operator>(rational const & r1, rational const & r2) { return operator<(r2, r1); } inline bool operator<(int r1, rational const & r2) { return rational(r1) < r2; } inline bool operator<(rational const & r1, int r2) { return r1 < rational(r2); } inline bool operator<=(rational const & r1, rational const & r2) { return !operator>(r1, r2); } inline bool operator<=(rational const & r1, int r2) { return r1 <= rational(r2); } inline bool operator>=(rational const & r1, rational const & r2) { return !operator<(r1, r2); } inline bool operator>(rational const & a, int b) { return a > rational(b); } inline bool operator>(int a, rational const & b) { return rational(a) > b; } inline bool operator!=(rational const & a, int b) { return !(a == rational(b)); } inline bool operator==(rational const & a, int b) { return a == rational(b); } inline rational operator+(rational const & r1, rational const & r2) { return rational(r1) += r2; } inline rational operator+(int r1, rational const & r2) { return rational(r1) + r2; } inline rational operator+(rational const & r1, int r2) { return r1 + rational(r2); } inline rational operator-(rational const & r1, rational const & r2) { return rational(r1) -= r2; } inline rational operator-(rational const & r1, int r2) { return r1 - rational(r2); } inline rational operator-(int r1, rational const & r2) { return rational(r1) - r2; } inline rational operator-(rational const & r) { rational result(r); result.neg(); return result; } inline rational operator*(rational const & r1, rational const & r2) { return rational(r1) *= r2; } inline rational operator*(rational const & r1, int r2) { return r1 * rational(r2); } inline rational operator*(int r1, rational const & r2) { return rational(r1) * r2; } inline rational operator/(rational const & r1, rational const & r2) { return rational(r1) /= r2; } inline rational operator/(rational const & r1, int r2) { return r1 / rational(r2); } inline rational operator/(int r1, rational const & r2) { return rational(r1) / r2; } inline rational power(rational const & r, unsigned p) { return r.expt(p); } inline rational abs(rational const & r) { rational result(r); rational::m().abs(result.m_val); return result; } inline rational gcd(rational const & r1, rational const & r2) { rational result; rational::m().gcd(r1.m_val, r2.m_val, result.m_val); return result; } inline rational gcd(rational const & r1, rational const & r2, rational & a, rational & b) { rational result; rational::m().gcd(r1.m_val, r2.m_val, a.m_val, b.m_val, result.m_val); return result; } #endif /* RATIONAL_H_ */ z3-z3-4.8.7/src/util/ref.h000066400000000000000000000044111356505360400151200ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: ref.h Abstract: Simple smart pointer class Author: Leonardo de Moura (leonardo) 2007-08-14. Revision History: --*/ #ifndef REF_H_ #define REF_H_ template class ref { T * m_ptr; void inc_ref() { if (m_ptr) { m_ptr->inc_ref(); } } void dec_ref() { if (m_ptr) { m_ptr->dec_ref(); } } public: ref(): m_ptr(nullptr) { } ref(T * ptr): m_ptr(ptr) { inc_ref(); } ref(const ref & r): m_ptr(r.m_ptr) { inc_ref(); } ref (ref && r): m_ptr (r.detach ()) {} ~ref() { dec_ref(); } T * operator->() const { return m_ptr; } T * get() const { return m_ptr; } operator bool() const { return m_ptr != nullptr; } const T & operator*() const { return *m_ptr; } T & operator*() { return *m_ptr; } ref & operator=(T * ptr) { if (ptr) ptr->inc_ref(); dec_ref(); m_ptr = ptr; return *this; } ref & operator=(ref & r) { r.inc_ref(); dec_ref(); m_ptr = r.m_ptr; return *this; } ref & operator=(ref &&r) { if (this != &r) { dec_ref (); m_ptr = r.detach (); } return *this; } void reset() { dec_ref(); m_ptr = nullptr; } T* detach() { T* tmp = m_ptr; m_ptr = nullptr; return tmp; } friend bool operator==(const ref & r1, const ref & r2) { return r1.m_ptr == r2.m_ptr; } friend bool operator!=(const ref & r1, const ref & r2) { return r1.m_ptr != r2.m_ptr; } friend void swap (ref &r1, ref &r2) { T* tmp = r1.m_ptr; r1.m_ptr = r2.m_ptr; r2.m_ptr = tmp; } }; /** \brief Manager for references that are not managed. This class is used for allowing us to create instantiations of the ref_vector and ref_buffer templates for unmanaged objects. */ template class unmanged_ref_manager { static void inc_ref(T * o) { o->inc_ref(); } static void dec_ref(T * o) { o->dec_ref(); } }; #endif /* REF_H_ */ z3-z3-4.8.7/src/util/ref_buffer.h000066400000000000000000000076031356505360400164570ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: ref_buffer.h Abstract: Buffer of smart pointers. Author: Leonardo de Moura (leonardo) 2008-01-04. Revision History: --*/ #ifndef REF_BUFFER_H_ #define REF_BUFFER_H_ #include "util/buffer.h" #include "util/obj_ref.h" #include "util/ref_vector.h" /** \brief Buffer of smart pointers. Ref must provide the methods - void dec_ref(T * obj) - void inc_ref(T * obj) */ template class ref_buffer_core : public Ref { protected: ptr_buffer m_buffer; void inc_ref(T * o) { Ref::inc_ref(o); } void dec_ref(T * o) { Ref::dec_ref(o); } void dec_range_ref(T * const * begin, T * const * end) { for (T * const * it = begin; it < end; ++it) dec_ref(*it); } public: typedef T * data; ref_buffer_core(Ref const & r = Ref()): Ref(r) { } ~ref_buffer_core() { dec_range_ref(m_buffer.begin(), m_buffer.end()); } void push_back(T * n) { 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()); T * n = m_buffer.back(); m_buffer.pop_back(); dec_ref(n); } T * back() const { return m_buffer.back(); } T * & back() { return m_buffer.back(); } T ** c_ptr() const { return m_buffer.c_ptr(); } T * operator[](unsigned idx) const { return m_buffer[idx]; } T* const* begin() const { return c_ptr(); } T* const* end() const { return c_ptr() + size(); } void set(unsigned idx, T * n) { inc_ref(n); dec_ref(m_buffer[idx]); m_buffer[idx] = n; } unsigned size() const { return m_buffer.size(); } bool empty() const { return m_buffer.empty(); } void reset() { dec_range_ref(m_buffer.begin(), m_buffer.end()); m_buffer.reset(); } void finalize() { dec_range_ref(m_buffer.begin(), m_buffer.end()); m_buffer.finalize(); } void append(unsigned n, T * const * elems) { for (unsigned i = 0; i < n; i++) { push_back(elems[i]); } } void append(ref_buffer_core const & other) { append(other.size(), other.c_ptr()); } void resize(unsigned sz) { if (sz < m_buffer.size()) dec_range_ref(m_buffer.begin() + sz, m_buffer.end()); m_buffer.resize(sz, 0); } void shrink(unsigned sz) { SASSERT(sz <= m_buffer.size()); resize(sz); } // set pos idx with elem. If idx >= size, then expand. void setx(unsigned idx, T * elem) { if (idx >= size()) { resize(idx+1); } set(idx, elem); } ref_buffer_core & operator=(ref_buffer_core const & other) { if (this == &other) return *this; reset(); append(other); return *this; } }; /** \brief Buffer of managed references */ template class ref_buffer : public ref_buffer_core, INITIAL_SIZE> { typedef ref_buffer_core, INITIAL_SIZE> super; public: ref_buffer(TManager & m): super(ref_manager_wrapper(m)) { } ref_buffer(ref_buffer const & other): super(ref_manager_wrapper(other.m_manager)) { SASSERT(this->m_buffer.size() == 0); append(other); } }; /** \brief Buffer of unmanaged references */ template class sref_buffer : public ref_buffer_core, INITIAL_SIZE> { public: }; #endif /* REF_BUFFER_H_ */ z3-z3-4.8.7/src/util/ref_util.h000066400000000000000000000037261356505360400161650ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: ref_util.h Abstract: Some goodies for managing reference counters. Author: Leonardo (leonardo) 2011-06-07 Notes: --*/ #ifndef REF_UTIL_H_ #define REF_UTIL_H_ /** \brief Decrement the reference counter of the keys and values stored in the map, then reset the map. */ template void dec_ref_map_key_values(Mng1 & m1, Mng2 & m2, Map & map) { typename Map::iterator it = map.begin(); typename Map::iterator end = map.end(); for (; it != end; ++it) { m1.dec_ref(it->m_key); m2.dec_ref(it->m_value); } map.reset(); } /** \brief Decrement the reference counter of the keys and values stored in the map, then reset the map. */ template void dec_ref_map_key_values(Mng & m, Map & map) { dec_ref_map_key_values(m, m, map); } /** \brief Decrement the reference counter of the keys stored in the map, then reset the map. */ template void dec_ref_map_keys(Mng & m, Map & map) { typename Map::iterator it = map.begin(); typename Map::iterator end = map.end(); for (; it != end; ++it) { m.dec_ref(it->m_key); } map.reset(); } /** \brief Decrement the reference counter of the values stored in the map, then reset the map. */ template void dec_ref_map_values(Mng & m, Map & map) { typename Map::iterator it = map.begin(); typename Map::iterator end = map.end(); for (; it != end; ++it) { m.dec_ref(it->m_value); } map.reset(); } /** \brief Decrement the reference counter of the values stored in the map, then reset the map. */ template void dec_ref_collection_values(Mng & m, C & c) { typename C::iterator it = c.begin(); typename C::iterator end = c.end(); for (; it != end; ++it) { m.dec_ref(*it); } c.reset(); } #endif z3-z3-4.8.7/src/util/ref_vector.h000066400000000000000000000243741356505360400165140ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: ref_vector.h Abstract: Vector of smart pointers. Author: Leonardo de Moura (leonardo) 2008-01-04. Revision History: --*/ #ifndef REF_VECTOR_H_ #define REF_VECTOR_H_ #include "util/vector.h" #include "util/obj_ref.h" /** \brief Vector of smart pointers. Ref must provided the methods - void dec_ref(T * obj) - void inc_ref(T * obj) */ template class ref_vector_core : public Ref { protected: ptr_vector m_nodes; void inc_ref(T * o) { Ref::inc_ref(o); } void dec_ref(T * o) { Ref::dec_ref(o); } void dec_range_ref(T * const * begin, T * const * end) { for (T * const * it = begin; it < end; ++it) dec_ref(*it); } public: typedef T * data; ref_vector_core(Ref const & r = Ref()):Ref(r) {} ref_vector_core(ref_vector_core && other) : Ref(std::move(other)), m_nodes(std::move(other.m_nodes)) {} ~ref_vector_core() { dec_range_ref(m_nodes.begin(), m_nodes.end()); } void reset() { dec_range_ref(m_nodes.begin(), m_nodes.end()); m_nodes.reset(); } void finalize() { dec_range_ref(m_nodes.begin(), m_nodes.end()); m_nodes.finalize(); } void resize(unsigned sz) { if (sz < m_nodes.size()) dec_range_ref(m_nodes.begin() + sz, m_nodes.end()); m_nodes.resize(sz); } void resize(unsigned sz, T * d) { if (sz < m_nodes.size()) { dec_range_ref(m_nodes.begin() + sz, m_nodes.end()); m_nodes.shrink(sz); } else { for (unsigned i = m_nodes.size(); i < sz; i++) push_back(d); } } void reserve(unsigned sz) { if (sz <= m_nodes.size()) return; m_nodes.resize(sz); } void shrink(unsigned sz) { SASSERT(sz <= m_nodes.size()); dec_range_ref(m_nodes.begin() + sz, m_nodes.end()); m_nodes.shrink(sz); } ref_vector_core& push_back(T * n) { inc_ref(n); m_nodes.push_back(n); return *this; } template ref_vector_core& push_back(obj_ref && n) { m_nodes.push_back(n.get()); n.steal(); return *this; } void pop_back() { SASSERT(!m_nodes.empty()); T * n = m_nodes.back(); m_nodes.pop_back(); dec_ref(n); } T * back() const { return m_nodes.back(); } unsigned size() const { return m_nodes.size(); } bool empty() const { return m_nodes.empty(); } T * get(unsigned idx) const { return m_nodes[idx]; } T * get(unsigned idx, T * d) const { return m_nodes.get(idx, d); } T * const * c_ptr() const { return m_nodes.begin(); } typedef T* const* iterator; T ** c_ptr() { return m_nodes.begin(); } iterator begin() const { return m_nodes.begin(); } iterator end() const { return begin() + size(); } void set(unsigned idx, T * n) { inc_ref(n); dec_ref(m_nodes[idx]); m_nodes[idx] = n; } void erase(unsigned idx) { T * curr = m_nodes[idx]; m_nodes.erase(m_nodes.begin() + idx); dec_ref(curr); } void erase(T * elem) { unsigned sz = size(); for (unsigned idx = 0; idx < sz; idx++) { if (m_nodes[idx] == elem) { erase(idx); return; } } } bool contains(T * elem) const { unsigned sz = size(); for (unsigned idx = 0; idx < sz; idx++) if (m_nodes[idx] == elem) return true; return false; } T * operator[](unsigned idx) const { return m_nodes[idx]; } void append(ref_vector_core const & other) { for (unsigned i = 0; i < other.size(); ++i) push_back(other[i]); } void append(unsigned sz, T * const * data) { for(unsigned i = 0; i < sz; ++i) push_back(data[i]); } void swap(unsigned idx1, unsigned idx2) { std::swap(m_nodes[idx1], m_nodes[idx2]); } void reverse() { unsigned sz = size(); for (unsigned i = 0; i < sz/2; ++i) { std::swap(m_nodes[i], m_nodes[sz-i-1]); } } }; template class ref_manager_wrapper { protected: TManager & m_manager; public: ref_manager_wrapper(TManager & m):m_manager(m) {} void inc_ref(T * n) { m_manager.inc_ref(n); } void dec_ref(T * n) { m_manager.dec_ref(n); } }; /** \brief Vector of smart pointers. TManager must provide the functions: - void dec_ref(T * obj) - void inc_ref(T * obj) */ template class ref_vector : public ref_vector_core > { typedef ref_vector_core > super; public: ref_vector(TManager & m): super(ref_manager_wrapper(m)) { } ref_vector(ref_vector const & other): super(ref_manager_wrapper(other.m_manager)) { this->append(other); } ref_vector(ref_vector && other) : super(std::move(other)) {} ref_vector(TManager & m, unsigned sz, T * const * data): super(ref_manager_wrapper(m)) { this->append(sz, data); } TManager & get_manager() const { return this->m_manager; } TManager & m() const { return get_manager(); } void swap(ref_vector & other) { SASSERT(&(this->m_manager) == &(other.m_manager)); this->m_nodes.swap(other.m_nodes); } class element_ref { T * & m_ref; TManager & m_manager; public: element_ref(T * & ref, TManager & m): m_ref(ref), m_manager(m) { } element_ref & operator=(T * n) { m_manager.inc_ref(n); m_manager.dec_ref(m_ref); m_ref = n; return *this; } element_ref & operator=(element_ref& n) { *this = n.get(); return *this; } template element_ref & operator=(obj_ref && n) { m_manager.dec_ref(m_ref); m_ref = n.steal(); return *this; } T * get() const { return m_ref; } T * operator->() const { return m_ref; } T const & operator*() const { SASSERT(m_ref); return *m_ref; } bool operator==(T * n) const { return m_ref == n; } }; T * operator[](unsigned idx) const { return super::operator[](idx); } element_ref operator[](unsigned idx) { return element_ref(this->m_nodes[idx], this->m_manager); } void set(unsigned idx, T * n) { super::set(idx, n); } // enable abuse: ref_vector & set(ref_vector const& other) { if (this != &other) { this->reset(); this->append(other); } return *this; } // 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)) return false; return true; } bool exists(std::function& predicate) const { for (T* t : *this) if (predicate(t)) return true; return false; } ref_vector filter_pure(std::function& predicate) const { ref_vector result(m()); for (T* t : *this) if (predicate(t)) result.push_back(t); return result; } #if 0 // TBD: ref_vector& filter_update(std::function& predicate) { unsigned j = 0; for (auto& t : *this) if (predicate(t)) set(j++, t); shrink(j); return *this; } #endif template vector mapv_pure(std::function& f) const { vector result; for (T* t : *this) result.push_back(f(t)); return result; } ref_vector map_pure(std::function& f) const { ref_vector result(m()); for (T* t : *this) result.push_back(f(t)); return result; } ref_vector& map_update(std::function& f) { unsigned j = 0; for (T* t : *this) set(j++, f(t)); return *this; } }; template class ref_unmanaged_wrapper { public: static void inc_ref(T * n) { if (n) n->inc_ref(); } static void dec_ref(T * n) { if (n) n->dec_ref(); } }; /** \brief Vector of unmanaged references. */ template class sref_vector : public ref_vector_core > { }; /** \brief Hash utilities on ref_vector pointers. */ template struct ref_vector_ptr_hash { typedef ref_vector RefV; struct hash_proc { unsigned operator()(RefV* v, unsigned idx) const { return (*v)[idx]->get_id(); } }; unsigned operator()(RefV* v) const { if (!v) { return 0; } unsigned sz = v->size(); if (sz == 0) { return 0; } return get_composite_hash(v, sz, default_kind_hash_proc(), hash_proc()); } }; template struct ref_vector_ptr_eq { typedef ref_vector RefV; bool operator()(RefV* v1, RefV* v2) const { if (!v1 && !v2) { return true; } if ((!v1 && v2) || (v1 && !v2)) { return false; } if (v1->size() != v2->size()) { return false; } for (unsigned i = 0; i < v1->size(); ++i) { if ((*v1)[i].get() != (*v2)[i].get()) { return false; } } return true; } }; #endif /* REF_VECTOR_H_ */ z3-z3-4.8.7/src/util/region.cpp000066400000000000000000000053731356505360400161720ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: region.cpp Abstract: Region/Arena memory manager Author: Leonardo de Moura (leonardo) 2006-09-18. Revision History: --*/ #include "util/region.h" #ifdef Z3DEBUG void region::display_mem_stats(std::ostream & out) const { out << "num. objects: " << m_chuncks.size() << "\n"; } #else #include "util/tptr.h" #include "util/debug.h" #include "util/memory_manager.h" #include "util/page.h" inline void region::allocate_page() { m_curr_page = allocate_default_page(m_curr_page, m_free_pages); m_curr_ptr = m_curr_page; m_curr_end_ptr = end_of_default_page(m_curr_page); } region::region() { m_curr_page = nullptr; m_curr_ptr = nullptr; m_curr_end_ptr = nullptr; m_free_pages = nullptr; m_mark = nullptr; allocate_page(); } region::~region() { del_pages(m_curr_page); del_pages(m_free_pages); } void * region::allocate(size_t size) { char * new_curr_ptr = m_curr_ptr + size; if (new_curr_ptr < m_curr_end_ptr) { char * result = m_curr_ptr; m_curr_ptr = ALIGN(char *, new_curr_ptr); return result; } else if (size < DEFAULT_PAGE_SIZE) { allocate_page(); char * result = m_curr_ptr; m_curr_ptr += size; m_curr_ptr = ALIGN(char *, m_curr_ptr); return result; } else { // big page m_curr_page = ::allocate_page(m_curr_page, size); char * result = m_curr_page; allocate_page(); return result; } } inline void region::recycle_curr_page() { char * prev = prev_page(m_curr_page); recycle_page(m_curr_page, m_free_pages); m_curr_page = prev; } void region::reset() { while (m_curr_page != nullptr) { recycle_curr_page(); } SASSERT(m_curr_page == 0); m_curr_ptr = nullptr; m_curr_end_ptr = nullptr; m_mark = nullptr; allocate_page(); } void region::push_scope() { char * curr_page = m_curr_page; char * curr_ptr = m_curr_ptr; m_mark = new (*this) mark(curr_page, curr_ptr, m_mark); } void region::pop_scope() { SASSERT(m_mark); char * old_curr_page = m_mark->m_curr_page; SASSERT(is_default_page(old_curr_page)); m_curr_ptr = m_mark->m_curr_ptr; m_mark = m_mark->m_prev_mark; while (m_curr_page != old_curr_page) { recycle_curr_page(); } SASSERT(is_default_page(m_curr_page)); m_curr_end_ptr = end_of_default_page(m_curr_page); } void region::display_mem_stats(std::ostream & out) const { unsigned n = 0; char * page = m_curr_page; while (page != nullptr) { n++; page = prev_page(page); } out << "num. pages: " << n << "\n"; } #endif z3-z3-4.8.7/src/util/region.h000066400000000000000000000051741356505360400156360ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: region.h Abstract: Region/Arena memory manager Author: Leonardo de Moura (leonardo) 2006-09-13. Revision History: --*/ #ifndef REGION_H_ #define REGION_H_ #include #include #ifdef Z3DEBUG #include "util/vector.h" class region { ptr_vector m_chuncks; unsigned_vector m_scopes; public: ~region() { reset(); } void * allocate(size_t size) { char * r = alloc_svect(char, size); m_chuncks.push_back(r); return r; } void reset() { ptr_vector::iterator it = m_chuncks.begin(); ptr_vector::iterator end = m_chuncks.end(); for (; it != end; ++it) { dealloc_svect(*it); } m_chuncks.reset(); m_scopes.reset(); } void push_scope() { m_scopes.push_back(m_chuncks.size()); } void pop_scope() { unsigned old_size = m_scopes.back(); m_scopes.pop_back(); ptr_vector::iterator it = m_chuncks.begin() + old_size; ptr_vector::iterator end = m_chuncks.end(); for (; it != end; ++it) { dealloc_svect(*it); } m_chuncks.shrink(old_size); } void pop_scope(unsigned num_scopes) { for (unsigned i = 0; i < num_scopes; i++) { pop_scope(); } } void display_mem_stats(std::ostream & out) const; }; #else /** \brief Implement explicit region memory manager. */ class region { struct mark { char * m_curr_page; char * m_curr_ptr; mark * m_prev_mark; mark(char * page, char * ptr, mark * m):m_curr_page(page), m_curr_ptr(ptr), m_prev_mark(m) {} }; char * m_curr_page; char * m_curr_ptr; //!< Next free space in the current page. char * m_curr_end_ptr; //!< Point to the end of the current page. char * m_free_pages; mark * m_mark; void allocate_page(); void recycle_curr_page(); public: region(); ~region(); void * allocate(size_t size); void reset(); void push_scope(); void pop_scope(); void pop_scope(unsigned num_scopes) { for (unsigned i = 0; i < num_scopes; i++) { pop_scope(); } } void display_mem_stats(std::ostream & out) const; }; #endif inline void * operator new(size_t s, region & r) { return r.allocate(s); } inline void * operator new[](size_t s, region & r) { return r.allocate(s); } inline void operator delete(void *, region & ) { /* do nothing */ } inline void operator delete[](void *, region & ) { /* do nothing */ } #endif /* REGION_H_ */ z3-z3-4.8.7/src/util/resource_limit.h000066400000000000000000000072771356505360400174060ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: resource_limit.h Abstract: Author: Leonardo de Moura (leonardo) 2007-04-18. Revision History: --*/ #ifndef RESOURCE_LIMIT_H_ #define RESOURCE_LIMIT_H_ // // This object is used to limit the availability of "resources" during a search. // The main applications: // // - limiting the number of conflicts. // // - limiting the number of branching&bound steps in the ILP // // - limiting the number of quantifier instantiations. // // - etc. // // The idea is that when the resources are exhausted, the bounded_search terminates with // the undefined result. // // Then, the search can restart with bigger limits. class resource_limit { public: virtual ~resource_limit() { } virtual const char * get_description() const = 0; // - Reset counters associated with the limit. virtual void reset_counters() = 0; // - The limit was exhausted. virtual bool exhausted() const = 0; // - Return true if the limit is incremented. virtual bool inc_limit() = 0; virtual int get_limit() const = 0; virtual void reset() = 0; }; class base_resource_limit : public resource_limit { protected: int m_counter; int m_curr_limit; int m_init_limit; int m_max_limit; // <0 if unbounded const char * m_description; public: base_resource_limit(int init_limit, int max_limit, const char * desc): m_counter(0), m_curr_limit(init_limit), m_init_limit(init_limit), m_max_limit(max_limit), m_description(desc) { } virtual ~base_resource_limit() { } int get_counter() const { return m_counter; } bool inc_counter() { m_counter++; return m_counter <= m_curr_limit; } void set_init_limit(int l) { m_init_limit = l; m_curr_limit = l; } void set_max_limit(int l) { m_max_limit = l; } virtual const char * get_description() const { return m_description; } virtual void reset_counters() { m_counter = 0; } virtual bool exhausted() const { return m_counter >= m_curr_limit; } virtual int get_limit() const { return m_curr_limit; } virtual void reset() { m_counter = 0; m_curr_limit = m_init_limit; } }; class li_resource_limit : public base_resource_limit { int m_increment; public: li_resource_limit(int init_limit, int max_limit, int inc, const char * desc): base_resource_limit(init_limit, max_limit, desc), m_increment(inc) { } virtual ~li_resource_limit() { } virtual bool inc_limit() { int new_limit = m_curr_limit + m_increment; if (m_max_limit < 0 || new_limit <= m_max_limit) { m_curr_limit = new_limit; return true; } TRACE("resource_limit", tout << new_limit << " exhausts " << m_max_limit << "\n";); return false; } }; class ei_resource_limit : public base_resource_limit { double m_increment_ratio; public: ei_resource_limit(int init_limit, int max_limit, double inc, const char * desc): base_resource_limit(init_limit, max_limit, desc), m_increment_ratio(inc) { } virtual ~ei_resource_limit() { } virtual bool inc_limit() { int new_limit = static_cast(m_curr_limit * m_increment_ratio); if (m_max_limit < 0 || new_limit <= m_max_limit) { m_curr_limit = new_limit; return true; } TRACE("resource_limit", tout << new_limit << " exhausts " << m_max_limit << "\n";); return false; } }; #endif /* RESOURCE_LIMIT_H_ */ z3-z3-4.8.7/src/util/rlimit.cpp000066400000000000000000000043751356505360400162100ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: rlimit.cpp Abstract: Resource limit container. Author: Nikolaj Bjorner (nbjorner) 2015-09-28 Revision History: --*/ #include "util/rlimit.h" #include "util/common_msgs.h" #include "util/mutex.h" static DECLARE_MUTEX(g_rlimit_mux); void initialize_rlimit() { ALLOC_MUTEX(g_rlimit_mux); } void finalize_rlimit() { DEALLOC_MUTEX(g_rlimit_mux); } reslimit::reslimit(): m_cancel(0), m_suspend(false), m_count(0), m_limit(0) { } uint64_t reslimit::count() const { return m_count; } bool reslimit::inc() { ++m_count; bool r = (m_cancel == 0 && (m_limit == 0 || m_count <= m_limit)) || m_suspend; return r; } bool reslimit::inc(unsigned offset) { m_count += offset; bool r = (m_cancel == 0 && (m_limit == 0 || m_count <= m_limit)) || m_suspend; return r; } void reslimit::push(unsigned delta_limit) { uint64_t new_limit = delta_limit + m_count; if (new_limit <= m_count) { new_limit = 0; } m_limits.push_back(m_limit); m_limit = m_limit==0 ? new_limit : std::min(new_limit, m_limit); m_cancel = 0; } void reslimit::pop() { if (m_count > m_limit && m_limit > 0) { m_count = m_limit; } m_limit = m_limits.back(); m_limits.pop_back(); m_cancel = 0; } char const* reslimit::get_cancel_msg() const { if (m_cancel > 0) { return Z3_CANCELED_MSG; } else { return Z3_MAX_RESOURCE_MSG; } } void reslimit::push_child(reslimit* r) { lock_guard lock(*g_rlimit_mux); m_children.push_back(r); } void reslimit::pop_child() { lock_guard lock(*g_rlimit_mux); m_children.pop_back(); } void reslimit::cancel() { lock_guard lock(*g_rlimit_mux); set_cancel(m_cancel+1); } void reslimit::reset_cancel() { lock_guard lock(*g_rlimit_mux); set_cancel(0); } void reslimit::inc_cancel() { lock_guard lock(*g_rlimit_mux); set_cancel(m_cancel+1); } void reslimit::dec_cancel() { lock_guard lock(*g_rlimit_mux); if (m_cancel > 0) { set_cancel(m_cancel-1); } } void reslimit::set_cancel(unsigned f) { m_cancel = f; for (unsigned i = 0; i < m_children.size(); ++i) { m_children[i]->set_cancel(f); } } z3-z3-4.8.7/src/util/rlimit.h000066400000000000000000000036611356505360400156520ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: rlimit.h Abstract: Resource limit container. Author: Nikolaj Bjorner (nbjorner) 2015-09-28 Revision History: --*/ #pragma once #include "util/vector.h" void initialize_rlimit(); void finalize_rlimit(); /* ADD_INITIALIZER('initialize_rlimit();') ADD_FINALIZER('finalize_rlimit();') */ class reslimit { volatile unsigned m_cancel; bool m_suspend; uint64_t m_count; uint64_t m_limit; svector m_limits; ptr_vector m_children; void set_cancel(unsigned f); friend class scoped_suspend_rlimit; public: reslimit(); void push(unsigned delta_limit); void pop(); void push_child(reslimit* r); void pop_child(); bool inc(); bool inc(unsigned offset); uint64_t count() const; bool get_cancel_flag() const { return m_cancel > 0 && !m_suspend; } char const* get_cancel_msg() const; void cancel(); void reset_cancel(); void inc_cancel(); void dec_cancel(); }; class scoped_rlimit { reslimit& m_limit; public: scoped_rlimit(reslimit& r, unsigned l): m_limit(r) { r.push(l); } ~scoped_rlimit() { m_limit.pop(); } }; class scoped_suspend_rlimit { reslimit & m_limit; bool m_suspend; public: scoped_suspend_rlimit(reslimit& r): m_limit(r) { m_suspend = r.m_suspend; r.m_suspend = true; } scoped_suspend_rlimit(reslimit& r, bool do_suspend): m_limit(r) { m_suspend = r.m_suspend; r.m_suspend |= do_suspend; } ~scoped_suspend_rlimit() { m_limit.m_suspend = m_suspend; } }; struct scoped_limits { reslimit& m_limit; unsigned m_sz; scoped_limits(reslimit& lim): m_limit(lim), m_sz(0) {} ~scoped_limits() { for (unsigned i = 0; i < m_sz; ++i) m_limit.pop_child(); } void push_child(reslimit* lim) { m_limit.push_child(lim); ++m_sz; } }; z3-z3-4.8.7/src/util/s_integer.cpp000066400000000000000000000024341356505360400166610ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: s_integer.cpp Abstract: Author: Leonardo de Moura (leonardo) 2007-06-10. Revision History: --*/ #include "util/s_integer.h" s_integer s_integer::m_zero(0); s_integer s_integer::m_one(1); s_integer s_integer::m_minus_one(-1); s_integer::s_integer(const char * str) { m_val = static_cast(strtol(str, nullptr, 10)); } s_integer power(const s_integer & r, unsigned p) { unsigned mask = 1; s_integer result = s_integer(1); s_integer power = r; while (mask <= p) { if (mask & p) { result *= power; } power *= power; mask = mask << 1; } return result; } s_integer gcd(const s_integer & r1, const s_integer & r2) { SASSERT(r1.is_int() && r2.is_int()); s_integer tmp1(r1); s_integer tmp2(r2); if (tmp1.is_neg()) { tmp1.neg(); } if (tmp2.is_neg()) { tmp2.neg(); } if (tmp1 < tmp2) { tmp1.swap(tmp2); } for(;;) { s_integer aux = tmp1 % tmp2; if (aux.is_zero()) { return tmp2; } tmp1 = tmp2; tmp2 = aux; } } s_integer lcm(const s_integer & r1, const s_integer & r2) { s_integer g = gcd(r1, r2); return (r1 / g) * r2; } z3-z3-4.8.7/src/util/s_integer.h000066400000000000000000000137611356505360400163330ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: integer.h Abstract: machine s_integer wrapper Author: Leonardo de Moura (leonardo) 2007-06-01. Revision History: --*/ #ifndef S_INTEGER_H_ #define S_INTEGER_H_ #include "util/rational.h" class s_integer { int m_val; static s_integer m_zero; static s_integer m_one; static s_integer m_minus_one; public: unsigned hash() const { return m_val; } struct hash_proc { unsigned operator()(s_integer const& r) const { return r.hash(); } }; struct eq_proc { bool operator()(s_integer const& r1, s_integer const& r2) const { return r1 == r2; } }; void swap(s_integer & n) { std::swap(m_val, n.m_val); } std::string to_string() const; public: s_integer(): m_val(0) {} s_integer(const s_integer & r):m_val(r.m_val) {} explicit s_integer(int n):m_val(n) {} struct i64 {}; explicit s_integer(int64_t i, i64):m_val(static_cast(i)) {} struct ui64 {}; explicit s_integer(uint64_t i, ui64):m_val(static_cast(i)) {} explicit s_integer(const char * str); explicit s_integer(const rational & r):m_val(static_cast(r.get_int64())) {} void reset() { m_val = 0; } static bool is_big() { return false; } static bool is_int() { return true; } static bool is_s_integer() { return true; } static bool is_int64() { return true; } static bool is_uint64() { return true; } int get_int() const { return m_val; } int64_t get_int64() const { return m_val; } uint64_t get_uint64() const { return m_val; } static bool is_unsigned() { return true; } unsigned get_unsigned() const { return static_cast(m_val); } s_integer const& get_s_integer() const { return *this; } s_integer const& get_infinitesimal() const { return zero(); } static bool is_rational() { return true; } s_integer const& get_rational() const { return *this; } s_integer & operator=(const s_integer & r) { m_val = r.m_val; return *this; } friend inline s_integer numerator(const s_integer & r) { return r; } friend inline s_integer denominator(const s_integer & r) { return one(); } s_integer & operator+=(const s_integer & r) { m_val += r.m_val; return *this; } s_integer & operator-=(const s_integer & r) { m_val -= r.m_val; return *this; } s_integer & operator*=(const s_integer & r) { m_val *= r.m_val; return *this; } s_integer & operator/=(const s_integer & r) { m_val /= r.m_val; return *this; } s_integer & operator%=(const s_integer & r) { m_val %= r.m_val; return *this; } friend inline s_integer div(const s_integer & r1, const s_integer & r2) { return s_integer(r1.m_val / r2.m_val); } friend inline s_integer mod(const s_integer & r1, const s_integer & r2) { s_integer r = r1; r %= r2; if (r.is_neg()) { r += r2; } return r; } s_integer & operator++() { m_val++; return *this; } const s_integer operator++(int) { s_integer tmp(*this); ++(*this); return tmp; } s_integer & operator--() { m_val--; return *this; } const s_integer operator--(int) { s_integer tmp(*this); --(*this); return tmp; } friend inline bool operator==(const s_integer & r1, const s_integer & r2) { return r1.m_val == r2.m_val; } friend inline bool operator<(const s_integer & r1, const s_integer & r2) { return r1.m_val < r2.m_val; } void neg() { m_val = -m_val; } bool is_zero() const { return m_val == 0; } bool is_one() const { return m_val == 1; } bool is_minus_one() const { return m_val == -1; } bool is_neg() const { return m_val < 0; } bool is_pos() const { return m_val > 0; } bool is_nonneg() const {return m_val >= 0; } bool is_nonpos() const { return m_val <= 0; } bool is_even() const { return (!(m_val & 0x1)); } friend inline s_integer floor(const s_integer & r) { return r; } friend inline s_integer ceil(const s_integer & r) { return r; } s_integer expt(int n) const { s_integer result(1); for (int i = 0; i < n; i++) { result *= *this; } return result; } static const s_integer & zero() { return m_zero; } static const s_integer & one() { return m_one; } static const s_integer & minus_one() { return m_minus_one; } // Perform: this += c * k void addmul(const s_integer & c, const s_integer & k) { m_val += c.m_val * k.m_val; } void submul(const s_integer & c, const s_integer & k) { m_val -= c.m_val * k.m_val; } friend inline std::ostream & operator<<(std::ostream & target, const s_integer & r) { target << r.m_val; return target; } rational to_rational() const { return rational(m_val); } }; inline bool operator!=(const s_integer & r1, const s_integer & r2) { return !operator==(r1, r2); } inline bool operator>(const s_integer & r1, const s_integer & r2) { return operator<(r2, r1); } inline bool operator<=(const s_integer & r1, const s_integer & r2) { return !operator>(r1, r2); } inline bool operator>=(const s_integer & r1, const s_integer & r2) { return !operator<(r1, r2); } inline s_integer operator+(const s_integer & r1, const s_integer & r2) { return s_integer(r1) += r2; } inline s_integer operator-(const s_integer & r1, const s_integer & r2) { return s_integer(r1) -= r2; } inline s_integer operator-(const s_integer & r) { s_integer result(r); result.neg(); return result; } inline s_integer operator*(const s_integer & r1, const s_integer & r2) { return s_integer(r1) *= r2; } inline s_integer operator/(const s_integer & r1, const s_integer & r2) { return s_integer(r1) /= r2; } inline s_integer operator%(const s_integer & r1, const s_integer & r2) { return s_integer(r1) %= r2; } s_integer power(const s_integer & r, unsigned p); s_integer gcd(const s_integer & r1, const s_integer & r2); s_integer lcm(const s_integer & r1, const s_integer & r2); inline s_integer abs(const s_integer & r) { s_integer result(r); if (result.is_neg()) { result.neg(); } return result; } #endif /* S_INTEGER_H_ */ z3-z3-4.8.7/src/util/scoped_ctrl_c.cpp000066400000000000000000000021671356505360400175100ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: scoped_ctrl_c.cpp Abstract: Scoped control-c handler. Author: Leonardo de Moura (leonardo) 2011-04-27. Revision History: --*/ #include #include #include "util/scoped_ctrl_c.h" static scoped_ctrl_c * g_obj = nullptr; static void on_ctrl_c(int) { if (g_obj->m_first) { g_obj->m_cancel_eh(CTRL_C_EH_CALLER); if (g_obj->m_once) { g_obj->m_first = false; signal(SIGINT, on_ctrl_c); // re-install the handler } } else { signal(SIGINT, g_obj->m_old_handler); raise(SIGINT); } } scoped_ctrl_c::scoped_ctrl_c(event_handler & eh, bool once, bool enabled): m_cancel_eh(eh), m_first(true), m_once(once), m_enabled(enabled), m_old_scoped_ctrl_c(g_obj) { if (m_enabled) { g_obj = this; m_old_handler = signal(SIGINT, on_ctrl_c); } } scoped_ctrl_c::~scoped_ctrl_c() { if (m_enabled) { g_obj = m_old_scoped_ctrl_c; if (m_old_handler != SIG_ERR) { signal(SIGINT, m_old_handler); } } } z3-z3-4.8.7/src/util/scoped_ctrl_c.h000066400000000000000000000015261356505360400171530ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: scoped_ctrl_c.h Abstract: Scoped control-c handler. Author: Leonardo de Moura (leonardo) 2011-04-27. Revision History: --*/ #ifndef SCOPED_CTRL_C_H_ #define SCOPED_CTRL_C_H_ #include "util/event_handler.h" #include "util/util.h" struct scoped_ctrl_c { event_handler & m_cancel_eh; bool m_first; bool m_once; bool m_enabled; void (STD_CALL *m_old_handler)(int); scoped_ctrl_c * m_old_scoped_ctrl_c; public: // If once == true, then the cancel_eh is invoked only at the first Ctrl-C. // The next time, the old signal handler will take over. // if enabled == false, then scoped_ctrl_c is a noop scoped_ctrl_c(event_handler & eh, bool once=true, bool enabled=true); ~scoped_ctrl_c(); void reset() { m_first = true; } }; #endif z3-z3-4.8.7/src/util/scoped_numeral.h000066400000000000000000000106531356505360400173510ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: scoped_numeral.h Abstract: Wrapper for easying the pain when using primitive numeral objects such as: mpz, mpq, mpbq, mpf, mpzp Author: Leonardo de Moura (leonardo) 2011-12-03 Revision History: --*/ #ifndef SCOPED_NUMERAL_H_ #define SCOPED_NUMERAL_H_ template class _scoped_numeral { public: typedef typename Manager::numeral numeral; private: Manager & m_manager; numeral m_num; public: _scoped_numeral(Manager & m):m_manager(m) {} _scoped_numeral(_scoped_numeral const & n):m_manager(n.m_manager) { m().set(m_num, n.m_num); } ~_scoped_numeral() { m_manager.del(m_num); } Manager & m() const { return m_manager; } operator numeral const &() const { return m_num; } operator numeral&() { return m_num; } numeral const & get() const { return m_num; } numeral & get() { return m_num; } _scoped_numeral & operator=(_scoped_numeral const & n) { if (this == &n) return *this; m().set(m_num, n.m_num); return *this; } _scoped_numeral & operator=(int n) { m().set(m_num, n); return *this; } _scoped_numeral & operator=(numeral const & n) { m().set(m_num, n); return *this; } void reset() { m().reset(m_num); } void swap(_scoped_numeral & n) { m_num.swap(n.m_num); } void swap(numeral & n) { m_num.swap(n); } _scoped_numeral & operator+=(numeral const & a) { m().add(m_num, a, m_num); return *this; } _scoped_numeral & operator-=(numeral const & a) { m().sub(m_num, a, m_num); return *this; } _scoped_numeral & operator*=(numeral const & a) { m().mul(m_num, a, m_num); return *this; } _scoped_numeral & operator/=(numeral const & a) { m().div(m_num, a, m_num); return *this; } _scoped_numeral & operator%=(numeral const & a) { m().rem(m_num, a, m_num); return *this; } friend bool operator==(_scoped_numeral const & a, numeral const & b) { return a.m().eq(a, b); } friend bool operator!=(_scoped_numeral const & a, numeral const & b) { return !a.m().eq(a, b); } friend bool operator<(_scoped_numeral const & a, numeral const & b) { return a.m().lt(a, b); } friend bool operator>(_scoped_numeral const & a, numeral const & b) { return a.m().gt(a, b); } friend bool operator<=(_scoped_numeral const & a, numeral const & b) { return a.m().le(a, b); } friend bool operator>=(_scoped_numeral const & a, numeral const & b) { return a.m().ge(a, b); } bool is_zero() const { return m().is_zero(*this); } bool is_pos() const { return m().is_pos(*this); } bool is_neg() const { return m().is_neg(*this); } bool is_nonpos() const { return m().is_nonpos(*this); } bool is_nonneg() const { return m().is_nonneg(*this); } friend bool is_zero(_scoped_numeral const & a) { return a.m().is_zero(a); } friend bool is_pos(_scoped_numeral const & a) { return a.m().is_pos(a); } friend bool is_neg(_scoped_numeral const & a) { return a.m().is_neg(a); } friend bool is_nonneg(_scoped_numeral const & a) { return a.m().is_nonneg(a); } friend bool is_nonpos(_scoped_numeral const & a) { return a.m().is_nonpos(a); } friend _scoped_numeral abs(_scoped_numeral const& a) { _scoped_numeral res(a); a.m().abs(res); return res; } void neg() { m().neg(m_num); } friend _scoped_numeral operator+(_scoped_numeral const & r1, numeral const & r2) { return _scoped_numeral(r1) += r2; } friend _scoped_numeral operator-(_scoped_numeral const & r1, numeral const & r2) { return _scoped_numeral(r1) -= r2; } friend _scoped_numeral operator*(_scoped_numeral const & r1, numeral const & r2) { return _scoped_numeral(r1) *= r2; } friend _scoped_numeral operator/(_scoped_numeral const & r1, numeral const & r2) { return _scoped_numeral(r1) /= r2; } friend std::ostream & operator<<(std::ostream & out, _scoped_numeral const & s) { s.m().display(out, s); return out; } }; #endif z3-z3-4.8.7/src/util/scoped_numeral_buffer.h000066400000000000000000000031001356505360400206670ustar00rootroot00000000000000/*++ Copyright (c) 2012 Microsoft Corporation Module Name: scoped_numeral_buffer.h Abstract: Wrapper for easying the pain when using buffers of numerals. Author: Leonardo de Moura (leonardo) 2012-01-23 Revision History: --*/ #ifndef SCOPED_NUMERAL_BUFFER_H_ #define SCOPED_NUMERAL_BUFFER_H_ #include "util/buffer.h" template class _scoped_numeral_buffer : public sbuffer { typedef sbuffer super; Manager & m_manager; _scoped_numeral_buffer(_scoped_numeral_buffer const & v); public: _scoped_numeral_buffer(Manager & m):m_manager(m) {} ~_scoped_numeral_buffer() { reset(); } void reset() { unsigned sz = this->size(); for (unsigned i = 0; i < sz; i++) { m().del(this->operator[](i)); } super::reset(); } Manager & m() const { return m_manager; } void push_back(typename Manager::numeral const & v) { super::push_back(typename Manager::numeral()); m_manager.set(this->back(), v); } void shrink(unsigned sz) { unsigned old_sz = this->size(); if (old_sz == sz) return; for (unsigned i = sz; i < old_sz; i++) m().del(this->operator[](i)); super::shrink(sz); } void resize(unsigned sz) { unsigned old_sz = this->size(); if (sz <= old_sz) shrink(sz); typename Manager::numeral zero(0); super::resize(sz, zero); } }; #endif z3-z3-4.8.7/src/util/scoped_numeral_vector.h000066400000000000000000000031251356505360400207270ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: scoped_numeral_vector.h Abstract: Wrapper for easying the pain when using vectors of numerals. Author: Leonardo de Moura (leonardo) 2011-12-03 Revision History: --*/ #ifndef SCOPED_NUMERAL_VECTOR_H_ #define SCOPED_NUMERAL_VECTOR_H_ #include "util/vector.h" template class _scoped_numeral_vector : public svector { Manager & m_manager; _scoped_numeral_vector(_scoped_numeral_vector const & v); public: _scoped_numeral_vector(Manager & m):m_manager(m) {} ~_scoped_numeral_vector() { reset(); } void reset() { unsigned sz = this->size(); for (unsigned i = 0; i < sz; i++) { m().del(this->operator[](i)); } svector::reset(); } Manager & m() const { return m_manager; } void push_back(typename Manager::numeral const & v) { svector::push_back(typename Manager::numeral()); m_manager.set(this->back(), v); } void pop_back() { shrink(this->size()-1); } void shrink(unsigned sz) { unsigned old_sz = this->size(); if (old_sz == sz) return; for (unsigned i = sz; i < old_sz; i++) m().del(this->operator[](i)); svector::shrink(sz); } void resize(unsigned sz) { unsigned old_sz = this->size(); if (sz <= old_sz) shrink(sz); svector::resize(sz, 0); } }; #endif z3-z3-4.8.7/src/util/scoped_ptr_vector.h000066400000000000000000000034271356505360400200760ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: scoped_ptr_vector.h Abstract: Basic template of scoped ptrs. TODO: improve Author: Leonardo (leonardo) 2011-12-29 Notes: --*/ #ifndef SCOPED_PTR_VECTOR_H_ #define SCOPED_PTR_VECTOR_H_ #include "util/vector.h" #include "util/util.h" template class scoped_ptr_vector { ptr_vector m_vector; public: ~scoped_ptr_vector() { reset(); } void reset() { std::for_each(m_vector.begin(), m_vector.end(), delete_proc()); m_vector.reset(); } void push_back(T * ptr) { m_vector.push_back(ptr); } void pop_back() { SASSERT(!empty()); set(size()-1, nullptr); m_vector.pop_back(); } T * back() const { return m_vector.back(); } T * operator[](unsigned idx) const { return m_vector[idx]; } void set(unsigned idx, T * ptr) { if (m_vector[idx] == ptr) return; dealloc(m_vector[idx]); m_vector[idx] = ptr; } unsigned size() const { return m_vector.size(); } bool empty() const { return m_vector.empty(); } void resize(unsigned sz) { if (sz < m_vector.size()) { for (unsigned i = m_vector.size(); i-- > sz; ) dealloc(m_vector[i]); m_vector.shrink(sz); } else { for (unsigned i = m_vector.size(); i < sz; i++) push_back(nullptr); } } //!< swap last element with given pointer void swap_back(scoped_ptr & ptr) { SASSERT(!empty()); T * tmp = ptr.detach(); ptr = m_vector.back(); m_vector[m_vector.size()-1] = tmp; } typename ptr_vector::const_iterator begin() const { return m_vector.begin(); } typename ptr_vector::const_iterator end() const { return m_vector.end(); } }; #endif z3-z3-4.8.7/src/util/scoped_timer.cpp000066400000000000000000000023731356505360400173610ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: scoped_timer.cpp Abstract: Author: Leonardo de Moura (leonardo) 2011-04-26. Revision History: --*/ #include "util/scoped_timer.h" #include "util/util.h" #include #include #include #include struct scoped_timer::imp { private: std::thread m_thread; std::timed_mutex m_mutex; 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(); } public: imp(unsigned ms, event_handler * eh) { m_mutex.lock(); m_thread = std::thread(thread_func, ms, eh, &m_mutex); } ~imp() { m_mutex.unlock(); m_thread.join(); } }; scoped_timer::scoped_timer(unsigned ms, event_handler * eh) { if (ms != UINT_MAX && ms != 0) m_imp = alloc(imp, ms, eh); else m_imp = nullptr; } scoped_timer::~scoped_timer() { dealloc(m_imp); } z3-z3-4.8.7/src/util/scoped_timer.h000066400000000000000000000006371356505360400170270ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: scoped_timer.h Abstract: Author: Leonardo de Moura (leonardo) 2011-04-26. Revision History: --*/ #ifndef SCOPED_TIMER_H_ #define SCOPED_TIMER_H_ #include "util/event_handler.h" class scoped_timer { struct imp; imp * m_imp; public: scoped_timer(unsigned ms, event_handler * eh); ~scoped_timer(); }; #endif z3-z3-4.8.7/src/util/scoped_vector.h000066400000000000000000000102131356505360400172000ustar00rootroot00000000000000/*++ Copyright (c) 2015 Microsoft Corporation Module Name: scoped_vector.h Abstract: Vector that restores during backtracking. Author: Nikolaj Bjorner (nbjorner) 2015-12-13 Revision History: --*/ #ifndef SCOPED_VECTOR_H_ #define SCOPED_VECTOR_H_ #include "util/vector.h" template class scoped_vector { unsigned m_size; unsigned m_elems_start; unsigned_vector m_sizes; vector m_elems; unsigned_vector m_elems_lim; unsigned_vector m_index; unsigned_vector m_src, m_dst; unsigned_vector m_src_lim; public: scoped_vector(): m_size(0), m_elems_start(0) {} // m_index : External-Index -> Internal-Index // m_index.size() = max(m_sizes) // m_src[i] -> m_dst[i] // trail into m_index updates // m_src_lim last index to be updated. void push_scope() { m_elems_start = m_elems.size(); m_sizes.push_back(m_size); m_src_lim.push_back(m_src.size()); m_elems_lim.push_back(m_elems_start); } void pop_scope(unsigned num_scopes) { if (num_scopes == 0) return; unsigned new_size = m_sizes.size() - num_scopes; unsigned src_lim = m_src_lim[new_size]; for (unsigned i = m_src.size(); i > src_lim; ) { --i; m_index[m_src[i]] = m_dst[i]; } m_src.shrink(src_lim); m_dst.shrink(src_lim); m_src_lim.shrink(new_size); m_elems.shrink(m_elems_lim[new_size]); m_elems_lim.resize(new_size); m_elems_start = m_elems.size(); m_size = m_sizes[new_size]; m_sizes.shrink(new_size); } T const& operator[](unsigned idx) const { SASSERT(idx < m_size); return m_elems[m_index[idx]]; } // breaks abstraction, caller must ensure backtracking. T& ref(unsigned idx) { SASSERT(idx < m_size); return m_elems[m_index[idx]]; } void set(unsigned idx, T const& t) { SASSERT(idx < m_size); unsigned n = m_index[idx]; if (n >= m_elems_start) { m_elems[n] = t; } else { set_index(idx, m_elems.size()); m_elems.push_back(t); } SASSERT(invariant()); } class iterator { scoped_vector const& m_vec; unsigned m_index; public: iterator(scoped_vector const& v, unsigned idx): m_vec(v), m_index(idx) {} bool operator==(iterator const& other) const { return &other.m_vec == &m_vec && other.m_index == m_index; } bool operator!=(iterator const& other) const { return &other.m_vec != &m_vec || other.m_index != m_index; } T const& operator*() { return m_vec[m_index]; } iterator & operator++() { ++m_index; return *this; } iterator operator++(int) { iterator r = *this; ++m_index; return r; } }; iterator begin() const { return iterator(*this, 0); } iterator end() const { return iterator(*this, m_size); } void push_back(T const& t) { set_index(m_size, m_elems.size()); m_elems.push_back(t); ++m_size; SASSERT(invariant()); } void pop_back() { SASSERT(m_size > 0); if (m_index[m_size-1] == m_elems.size()-1 && m_elems.size() > m_elems_start) { m_elems.pop_back(); } --m_size; SASSERT(invariant()); } void erase_and_swap(unsigned i) { if (i + 1 < size()) { set(i, m_elems[m_index[size()-1]]); } pop_back(); } unsigned size() const { return m_size; } bool empty() const { return m_size == 0; } private: void set_index(unsigned src, unsigned dst) { while (src >= m_index.size()) { m_index.push_back(0); } SASSERT(src < m_index.size()); if (src < m_elems_start) { m_src.push_back(src); m_dst.push_back(m_index[src]); } m_index[src] = dst; } bool invariant() const { return m_size <= m_elems.size() && m_elems_start <= m_elems.size(); } }; #endif z3-z3-4.8.7/src/util/sexpr.cpp000066400000000000000000000213531356505360400160440ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sexpr.h Abstract: Generic sexpr Author: Leonardo (leonardo) 2011-07-28 Notes: --*/ #include "util/sexpr.h" #include "util/vector.h" #include "util/buffer.h" #ifdef _MSC_VER #pragma warning(disable : 4200) #pragma warning(disable : 4355) #endif struct sexpr_composite : public sexpr { unsigned m_num_chilren; sexpr * m_children[0]; sexpr_composite(unsigned num_children, sexpr * const * children, unsigned line, unsigned pos): sexpr(COMPOSITE, line, pos), m_num_chilren(num_children) { for (unsigned i = 0; i < num_children; i++) { m_children[i] = children[i]; children[i]->inc_ref(); } } }; struct sexpr_numeral : public sexpr { rational m_val; sexpr_numeral(kind_t k, rational const & val, unsigned line, unsigned pos): sexpr(k, line, pos), m_val(val) { } sexpr_numeral(rational const & val, unsigned line, unsigned pos): sexpr(NUMERAL, line, pos), m_val(val) { } }; struct sexpr_bv : public sexpr_numeral { unsigned m_size; sexpr_bv(rational const & val, unsigned size, unsigned line, unsigned pos): sexpr_numeral(BV_NUMERAL, val, line, pos), m_size(size) { } }; struct sexpr_string : public sexpr { std::string m_val; sexpr_string(std::string const & val, unsigned line, unsigned pos): sexpr(STRING, line, pos), m_val(val) { } sexpr_string(char const * val, unsigned line, unsigned pos): sexpr(STRING, line, pos), m_val(val) { } }; struct sexpr_symbol : public sexpr { symbol m_val; sexpr_symbol(bool keyword, symbol const & val, unsigned line, unsigned pos): sexpr(keyword ? KEYWORD : SYMBOL, line, pos), m_val(val) { } }; sexpr::sexpr(kind_t k, unsigned line, unsigned pos): m_kind(k), m_ref_count(0), m_line(line), m_pos(pos) { } rational const & sexpr::get_numeral() const { SASSERT(is_numeral() || is_bv_numeral()); return static_cast(this)->m_val; } unsigned sexpr::get_bv_size() const { SASSERT(is_bv_numeral()); return static_cast(this)->m_size; } symbol sexpr::get_symbol() const { SASSERT(is_symbol() || is_keyword()); return static_cast(this)->m_val; } std::string const & sexpr::get_string() const { SASSERT(is_string()); return static_cast(this)->m_val; } unsigned sexpr::get_num_children() const { SASSERT(is_composite()); return static_cast(this)->m_num_chilren; } sexpr * sexpr::get_child(unsigned idx) const { SASSERT(idx < get_num_children()); return static_cast(this)->m_children[idx]; } sexpr * const * sexpr::get_children() const { SASSERT(is_composite()); return static_cast(this)->m_children; } void sexpr::display_atom(std::ostream & out) const { switch (get_kind()) { case sexpr::COMPOSITE: UNREACHABLE(); case sexpr::NUMERAL: out << static_cast(this)->m_val; break; case sexpr::BV_NUMERAL: { out << '#'; unsigned bv_size = static_cast(this)->m_size; rational val = static_cast(this)->m_val; sbuffer buf; unsigned sz = 0; if (bv_size % 4 == 0) { out << 'x'; while (val.is_pos()) { rational c = val % rational(16); val = div(val, rational(16)); SASSERT(rational(0) <= c && c < rational(16)); if (c <= rational(9)) buf.push_back('0' + c.get_unsigned()); else buf.push_back('a' + (c.get_unsigned() - 10)); sz+=4; } while (sz < bv_size) { buf.push_back('0'); sz+=4; } } else { out << 'b'; while (val.is_pos()) { rational c = val % rational(2); val = div(val, rational(2)); SASSERT(rational(0) <= c && c < rational(2)); if (c.is_zero()) buf.push_back('0'); else buf.push_back('1'); sz += 1; } while (sz < bv_size) { buf.push_back('0'); sz += 1; } } std::reverse(buf.begin(), buf.end()); buf.push_back(0); out << buf.c_ptr(); break; } case sexpr::STRING: out << "\"" << escaped(static_cast(this)->m_val.c_str()) << "\""; break; case sexpr::SYMBOL: case sexpr::KEYWORD: out << static_cast(this)->m_val; break; default: UNREACHABLE(); } } void sexpr::display(std::ostream & out) const { if (!is_composite()) display_atom(out); vector > todo; todo.push_back(std::make_pair(static_cast(this), 0)); while (!todo.empty()) { loop: sexpr_composite const * n = todo.back().first; unsigned & idx = todo.back().second; unsigned num = n->get_num_children(); while (idx < num) { sexpr const * child = n->get_child(idx); if (idx == 0) out << "("; else out << " "; idx++; if (child->is_composite()) { todo.push_back(std::make_pair(static_cast(child), 0)); goto loop; } else { child->display_atom(out); } } out << ")"; todo.pop_back(); } } void sexpr_manager::del(sexpr * n) { m_to_delete.push_back(n); while (!m_to_delete.empty()) { sexpr * n = m_to_delete.back(); m_to_delete.pop_back(); switch (n->get_kind()) { case sexpr::COMPOSITE: { unsigned num = n->get_num_children(); for (unsigned i = 0; i < num; i++) { sexpr * child = n->get_child(i); SASSERT(child->m_ref_count > 0); child->m_ref_count--; if (child->m_ref_count == 0) m_to_delete.push_back(child); } static_cast(n)->~sexpr_composite(); m_allocator.deallocate(sizeof(sexpr_composite) + num * sizeof(sexpr*), n); break; } case sexpr::NUMERAL: static_cast(n)->~sexpr_numeral(); m_allocator.deallocate(sizeof(sexpr_numeral), n); break; case sexpr::BV_NUMERAL: static_cast(n)->~sexpr_bv(); m_allocator.deallocate(sizeof(sexpr_bv), n); break; case sexpr::STRING: static_cast(n)->~sexpr_string(); m_allocator.deallocate(sizeof(sexpr_string), n); break; case sexpr::SYMBOL: case sexpr::KEYWORD: static_cast(n)->~sexpr_symbol(); m_allocator.deallocate(sizeof(sexpr_symbol), n); break; default: UNREACHABLE(); } } } sexpr_manager::sexpr_manager(): m_allocator("sexpr-manager") { } sexpr * sexpr_manager::mk_composite(unsigned num_children, sexpr * const * children, unsigned line, unsigned pos) { void * mem = m_allocator.allocate(sizeof(sexpr_composite) + num_children * sizeof(sexpr*)); return new (mem) sexpr_composite(num_children, children, line, pos); } sexpr * sexpr_manager::mk_numeral(rational const & val, unsigned line, unsigned pos) { return new (m_allocator.allocate(sizeof(sexpr_numeral))) sexpr_numeral(val, line, pos); } sexpr * sexpr_manager::mk_bv_numeral(rational const & val, unsigned bv_size, unsigned line, unsigned pos) { return new (m_allocator.allocate(sizeof(sexpr_bv))) sexpr_bv(val, bv_size, line, pos); } sexpr * sexpr_manager::mk_string(std::string const & val, unsigned line, unsigned pos) { return new (m_allocator.allocate(sizeof(sexpr_string))) sexpr_string(val, line, pos); } sexpr * sexpr_manager::mk_string(char const * val, unsigned line, unsigned pos) { return new (m_allocator.allocate(sizeof(sexpr_string))) sexpr_string(val, line, pos); } sexpr * sexpr_manager::mk_keyword(symbol const & val, unsigned line, unsigned pos) { return new (m_allocator.allocate(sizeof(sexpr_symbol))) sexpr_symbol(true, val, line, pos); } sexpr * sexpr_manager::mk_symbol(symbol const & val, unsigned line, unsigned pos) { return new (m_allocator.allocate(sizeof(sexpr_symbol))) sexpr_symbol(false, val, line, pos); } z3-z3-4.8.7/src/util/sexpr.h000066400000000000000000000053301356505360400155060ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sexpr.h Abstract: Generic sexpr Author: Leonardo (leonardo) 2011-07-28 Notes: --*/ #ifndef SEXPR_H_ #define SEXPR_H_ #include "util/rational.h" #include "util/symbol.h" #include "util/obj_ref.h" #include "util/ref_vector.h" class sexpr_manager; class sexpr { public: enum kind_t { COMPOSITE, NUMERAL, BV_NUMERAL, STRING, KEYWORD, SYMBOL }; protected: kind_t m_kind; unsigned m_ref_count; unsigned m_line; unsigned m_pos; sexpr(kind_t k, unsigned line, unsigned pos); void display_atom(std::ostream & out) const; friend class sexpr_manager; public: void inc_ref() { m_ref_count++; } unsigned get_ref_count() const { return m_ref_count; } unsigned get_line() const { return m_line; } unsigned get_pos() const { return m_pos; } kind_t get_kind() const { return m_kind; } bool is_composite() const { return get_kind() == COMPOSITE; } bool is_numeral() const { return get_kind() == NUMERAL; } bool is_bv_numeral() const { return get_kind() == BV_NUMERAL; } bool is_string() const { return get_kind() == STRING; } bool is_keyword() const { return get_kind() == KEYWORD; } bool is_symbol() const { return get_kind() == SYMBOL; } rational const & get_numeral() const; unsigned get_bv_size() const; symbol get_symbol() const; std::string const & get_string() const; unsigned get_num_children() const; sexpr * get_child(unsigned idx) const; sexpr * const * get_children() const; void display(std::ostream & out) const; }; class sexpr_manager { small_object_allocator m_allocator; ptr_vector m_to_delete; void del(sexpr * n); public: sexpr_manager(); sexpr * mk_composite(unsigned num_children, sexpr * const * children, unsigned line = UINT_MAX, unsigned pos = UINT_MAX); sexpr * mk_numeral(rational const & val, unsigned line = UINT_MAX, unsigned pos = UINT_MAX); sexpr * mk_bv_numeral(rational const & val, unsigned bv_size, unsigned line = UINT_MAX, unsigned pos = UINT_MAX); sexpr * mk_string(std::string const & val, unsigned line = UINT_MAX, unsigned pos = UINT_MAX); sexpr * mk_string(char const * val, unsigned line = UINT_MAX, unsigned pos = UINT_MAX); sexpr * mk_keyword(symbol const & val, unsigned line = UINT_MAX, unsigned pos = UINT_MAX); sexpr * mk_symbol(symbol const & val, unsigned line = UINT_MAX, unsigned pos = UINT_MAX); void inc_ref(sexpr * n) { n->inc_ref(); } void dec_ref(sexpr * n) { SASSERT(n->m_ref_count > 0); n->m_ref_count--; if (n->m_ref_count == 0) del(n); } }; typedef obj_ref sexpr_ref; typedef ref_vector sexpr_ref_vector; #endif z3-z3-4.8.7/src/util/small_object_allocator.cpp000066400000000000000000000164601356505360400214040ustar00rootroot00000000000000/*++ Copyright (c) 2007 Microsoft Corporation Module Name: small_object_allocator.cpp Abstract: Small object allocator. Author: Nikolaj bjorner (nbjorner) 2007-08-06. Revision History: Leonardo de Moura (leonardo) 2011-04-27 Rewrote/Simplified the allocator --*/ #include "util/memory_manager.h" #include "util/small_object_allocator.h" #include "util/debug.h" #include "util/util.h" #include "util/vector.h" #include small_object_allocator::small_object_allocator(char const * id) { for (unsigned i = 0; i < NUM_SLOTS; i++) { m_chunks[i] = nullptr; m_free_list[i] = nullptr; } DEBUG_CODE({ m_id = id; }); m_alloc_size = 0; } small_object_allocator::~small_object_allocator() { for (unsigned i = 0; i < NUM_SLOTS; i++) { chunk * c = m_chunks[i]; while (c) { chunk * next = c->m_next; dealloc(c); c = next; } } DEBUG_CODE({ if (m_alloc_size > 0) { std::cerr << "Memory leak detected for small object allocator '" << m_id << "'. " << m_alloc_size << " bytes leaked" << std::endl; } }); } void small_object_allocator::reset() { for (unsigned i = 0; i < NUM_SLOTS; i++) { chunk * c = m_chunks[i]; while (c) { chunk * next = c->m_next; dealloc(c); c = next; } m_chunks[i] = nullptr; m_free_list[i] = nullptr; } m_alloc_size = 0; } #define MASK ((1 << PTR_ALIGNMENT) - 1) void small_object_allocator::deallocate(size_t size, void * p) { if (size == 0) return; #if defined(Z3DEBUG) && !defined(_WINDOWS) // Valgrind friendly memory::deallocate(p); return; #endif SASSERT(m_alloc_size >= size); SASSERT(p); m_alloc_size -= size; if (size >= SMALL_OBJ_SIZE - (1 << PTR_ALIGNMENT)) { memory::deallocate(p); return; } unsigned slot_id = static_cast(size >> PTR_ALIGNMENT); if ((size & MASK) != 0) slot_id++; SASSERT(slot_id > 0); SASSERT(slot_id < NUM_SLOTS); *(reinterpret_cast(p)) = m_free_list[slot_id]; m_free_list[slot_id] = p; } void * small_object_allocator::allocate(size_t size) { if (size == 0) return nullptr; #if defined(Z3DEBUG) && !defined(_WINDOWS) // Valgrind friendly return memory::allocate(size); #endif m_alloc_size += size; if (size >= SMALL_OBJ_SIZE - (1 << PTR_ALIGNMENT)) { return memory::allocate(size); } #ifdef Z3DEBUG size_t osize = size; #endif unsigned slot_id = static_cast(size >> PTR_ALIGNMENT); if ((size & MASK) != 0) slot_id++; SASSERT(slot_id < NUM_SLOTS); SASSERT(slot_id > 0); if (m_free_list[slot_id] != nullptr) { void * r = m_free_list[slot_id]; m_free_list[slot_id] = *(reinterpret_cast(r)); return r; } chunk * c = m_chunks[slot_id]; size = slot_id << PTR_ALIGNMENT; SASSERT(size >= osize); if (c != nullptr) { char * new_curr = c->m_curr + size; if (new_curr < c->m_data + CHUNK_SIZE) { void * r = c->m_curr; c->m_curr = new_curr; return r; } } chunk * new_c = alloc(chunk); new_c->m_next = c; m_chunks[slot_id] = new_c; void * r = new_c->m_curr; new_c->m_curr += size; return r; } size_t small_object_allocator::get_wasted_size() const { size_t r = 0; for (unsigned slot_id = 0; slot_id < NUM_SLOTS; slot_id++) { size_t slot_obj_size = slot_id << PTR_ALIGNMENT; void ** ptr = reinterpret_cast(const_cast(this)->m_free_list[slot_id]); while (ptr != nullptr) { r += slot_obj_size; ptr = reinterpret_cast(*ptr); } } return r; } size_t small_object_allocator::get_num_free_objs() const { size_t r = 0; for (unsigned slot_id = 0; slot_id < NUM_SLOTS; slot_id++) { void ** ptr = reinterpret_cast(const_cast(this)->m_free_list[slot_id]); while (ptr != nullptr) { r ++; ptr = reinterpret_cast(*ptr); } } return r; } template struct ptr_lt { bool operator()(T * p1, T * p2) const { return p1 < p2; } }; #define CONSOLIDATE_VB_LVL 20 void small_object_allocator::consolidate() { IF_VERBOSE(CONSOLIDATE_VB_LVL, verbose_stream() << "(allocator-consolidate :wasted-size " << get_wasted_size() << " :memory " << std::fixed << std::setprecision(2) << static_cast(memory::get_allocation_size())/static_cast(1024*1024) << ")" << std::endl;); ptr_vector chunks; ptr_vector free_objs; for (unsigned slot_id = 1; slot_id < NUM_SLOTS; slot_id++) { if (m_free_list[slot_id] == nullptr) continue; chunks.reset(); free_objs.reset(); chunk * c = m_chunks[slot_id]; while (c != nullptr) { chunks.push_back(c); c = c->m_next; } char * ptr = static_cast(m_free_list[slot_id]); while (ptr != nullptr) { free_objs.push_back(ptr); ptr = *(reinterpret_cast(ptr)); } unsigned obj_size = slot_id << PTR_ALIGNMENT; unsigned num_objs_per_chunk = CHUNK_SIZE / obj_size; if (free_objs.size() < num_objs_per_chunk) continue; SASSERT(!chunks.empty()); std::sort(chunks.begin(), chunks.end(), ptr_lt()); std::sort(free_objs.begin(), free_objs.end(), ptr_lt()); chunk * last_chunk = nullptr; void * last_free_obj = nullptr; unsigned chunk_idx = 0; unsigned obj_idx = 0; unsigned num_chunks = chunks.size(); unsigned num_objs = free_objs.size(); while (chunk_idx < num_chunks) { chunk * curr_chunk = chunks[chunk_idx]; char * curr_begin = curr_chunk->m_data; char * curr_end = curr_begin + CHUNK_SIZE; unsigned num_free_in_chunk = 0; unsigned saved_obj_idx = obj_idx; while (obj_idx < num_objs) { char * free_obj = free_objs[obj_idx]; if (free_obj > curr_end) break; obj_idx++; num_free_in_chunk++; } if (num_free_in_chunk == num_objs_per_chunk) { dealloc(curr_chunk); } else { curr_chunk->m_next = last_chunk; last_chunk = curr_chunk; for (unsigned i = saved_obj_idx; i < obj_idx; i++) { // relink objects void * free_obj = free_objs[i]; *(reinterpret_cast(free_obj)) = last_free_obj; last_free_obj = free_obj; } } chunk_idx++; } m_chunks[slot_id] = last_chunk; m_free_list[slot_id] = last_free_obj; } IF_VERBOSE(CONSOLIDATE_VB_LVL, verbose_stream() << "(end-allocator-consolidate :wasted-size " << get_wasted_size() << " :memory " << std::fixed << std::setprecision(2) << static_cast(memory::get_allocation_size())/static_cast(1024*1024) << ")" << std::endl;); } z3-z3-4.8.7/src/util/small_object_allocator.h000066400000000000000000000032251356505360400210440ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: small_object_allocator.h Abstract: Small object allocator. Author: Nikolaj bjorner (nbjorner) 2007-08-06. Revision History: Leonardo de Moura (leonardo) 2011-04-27 Rewrote/Simplified the allocator --*/ #ifndef SMALL_OBJECT_ALLOCATOR_H_ #define SMALL_OBJECT_ALLOCATOR_H_ #include "util/machine.h" #include "util/debug.h" class small_object_allocator { static const unsigned CHUNK_SIZE = (8192 - sizeof(void*)*2); static const unsigned SMALL_OBJ_SIZE = 256; static const unsigned NUM_SLOTS = (SMALL_OBJ_SIZE >> PTR_ALIGNMENT); struct chunk { chunk * m_next; char * m_curr; char m_data[CHUNK_SIZE]; chunk():m_curr(m_data) {} }; chunk * m_chunks[NUM_SLOTS]; void * m_free_list[NUM_SLOTS]; size_t m_alloc_size; #ifdef Z3DEBUG char const * m_id; #endif public: small_object_allocator(char const * id = "unknown"); ~small_object_allocator(); void reset(); void * allocate(size_t size); void deallocate(size_t size, void * p); size_t get_allocation_size() const { return m_alloc_size; } size_t get_wasted_size() const; size_t get_num_free_objs() const; void consolidate(); }; inline void * operator new(size_t s, small_object_allocator & r) { return r.allocate(s); } inline void * operator new[](size_t s, small_object_allocator & r) { return r.allocate(s); } inline void operator delete(void * p, small_object_allocator & r) { UNREACHABLE(); } inline void operator delete[](void * p, small_object_allocator & r) { UNREACHABLE(); } #endif /* SMALL_OBJECT_ALLOCATOR_H_ */ z3-z3-4.8.7/src/util/smt2_util.cpp000066400000000000000000000034121356505360400166210ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: smt2_util.cpp Abstract: Goodies for SMT2 standard Author: Leonardo (leonardo) 2012-10-20 Notes: --*/ #include "util/smt2_util.h" bool is_smt2_simple_symbol_char(char s) { return ('0' <= s && s <= '9') || ('a' <= s && s <= 'z') || ('A' <= s && s <= 'Z') || s == '~' || s == '!' || s == '@' || s == '$' || s == '%' || s == '^' || s == '&' || s == '*' || s == '_' || s == '-' || s == '+' || s == '=' || s == '<' || s == '>' || s == '.' || s == '?' || s == '/'; } bool is_smt2_quoted_symbol(char const * s) { if (s == nullptr) return false; if ('0' <= s[0] && s[0] <= '9') return true; unsigned len = static_cast(strlen(s)); if (len >= 2 && s[0] == '|' && s[len-1] == '|') { for (unsigned i = 1; i + 1 < len; i++) { if (s[i] == '\\' && i + 2 < len && (s[i+1] == '\\' || s[i+1] == '|')) { i++; } else if (s[i] == '\\' || s[i] == '|') return true; } return false; } for (unsigned i = 0; i < len; i++) if (!is_smt2_simple_symbol_char(s[i])) return true; return false; } bool is_smt2_quoted_symbol(symbol const & s) { if (s.is_numerical()) return false; return is_smt2_quoted_symbol(s.bare_str()); } std::string mk_smt2_quoted_symbol(symbol const & s) { SASSERT(is_smt2_quoted_symbol(s)); string_buffer<> buffer; buffer.append('|'); char const * str = s.bare_str(); while (*str) { if (*str == '|' || *str == '\\') buffer.append('\\'); buffer.append(*str); str++; } buffer.append('|'); return std::string(buffer.c_str()); } z3-z3-4.8.7/src/util/smt2_util.h000066400000000000000000000006671356505360400162770ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: smt2_util.h Abstract: Goodies for SMT2 standard Author: Leonardo (leonardo) 2012-10-20 Notes: --*/ #ifndef SMT2_UTIL_H_ #define SMT2_UTIL_H_ #include "util/symbol.h" bool is_smt2_simple_symbol_char(char c); bool is_smt2_quoted_symbol(char const * s); bool is_smt2_quoted_symbol(symbol const & s); std::string mk_smt2_quoted_symbol(symbol const & s); #endif z3-z3-4.8.7/src/util/sorting_network.h000066400000000000000000001512011356505360400176020ustar00rootroot00000000000000/*++ Copyright (c) 2013 Microsoft Corporation Module Name: sorting_network.h Abstract: Utility for creating a sorting network. Author: Nikolaj Bjorner (nbjorner) 2013-11-07 Notes: Same routine is used in Formula. --*/ #include "util/vector.h" #ifndef SORTING_NETWORK_H_ #define SORTING_NETWORK_H_ enum sorting_network_encoding { sorted_at_most, grouped_at_most, bimander_at_most, ordered_at_most, unate_at_most, circuit_at_most }; inline std::ostream& operator<<(std::ostream& out, sorting_network_encoding enc) { switch (enc) { case grouped_at_most: return out << "grouped"; case bimander_at_most: return out << "bimander"; case ordered_at_most: return out << "ordered"; case sorted_at_most: return out << "sorted"; case unate_at_most: return out << "unate"; case circuit_at_most: return out << "circuit"; } return out << "???"; } struct sorting_network_config { sorting_network_encoding m_encoding; sorting_network_config() { m_encoding = sorted_at_most; } }; template class sorting_network { typedef typename Ext::vector vect; Ext& m_ext; svector m_currentv; svector m_nextv; svector* m_current; svector* m_next; unsigned& current(unsigned i) { return (*m_current)[i]; } unsigned& next(unsigned i) { return (*m_next)[i]; } void exchange(unsigned i, unsigned j, vect& out) { SASSERT(i <= j); if (i < j) { typename Ext::T ei = out.get(i); typename Ext::T ej = out.get(j); out.set(i, m_ext.mk_ite(m_ext.mk_le(ei, ej), ei, ej)); out.set(j, m_ext.mk_ite(m_ext.mk_le(ej, ei), ei, ej)); } } void sort(unsigned k, vect& out) { SASSERT(is_power_of2(k) && k > 0); if (k == 2) { for (unsigned i = 0; i < out.size()/2; ++i) { exchange(current(2*i), current(2*i+1), out); next(2*i) = current(2*i); next(2*i+1) = current(2*i+1); } std::swap(m_current, m_next); } else { for (unsigned i = 0; i < out.size()/k; ++i) { unsigned ki = k * i; for (unsigned j = 0; j < k / 2; ++j) { next(ki + j) = current(ki + (2 * j)); next(ki + (k / 2) + j) = current(ki + (2 * j) + 1); } } std::swap(m_current, m_next); sort(k / 2, out); for (unsigned i = 0; i < out.size() / k; ++i) { unsigned ki = k * i; for (unsigned j = 0; j < k / 2; ++j) { next(ki + (2 * j)) = current(ki + j); next(ki + (2 * j) + 1) = current(ki + (k / 2) + j); } for (unsigned j = 0; j < (k / 2) - 1; ++j) { exchange(next(ki + (2 * j) + 1), next(ki + (2 * (j + 1))), out); } } std::swap(m_current, m_next); } } bool is_power_of2(unsigned n) const { return n != 0 && ((n-1) & n) == 0; } public: sorting_network(Ext& ext): m_ext(ext), m_current(&m_currentv), m_next(&m_nextv) {} void operator()(vect const& in, vect& out) { out.reset(); out.append(in); if (in.size() <= 1) { return; } while (!is_power_of2(out.size())) { out.push_back(m_ext.mk_default()); } for (unsigned i = 0; i < out.size(); ++i) { m_currentv.push_back(i); m_nextv.push_back(i); } unsigned k = 2; while (k <= out.size()) { sort(k, out); k *= 2; } } }; // parametric sorting network // Described in Abio et.al. CP 2013. template class psort_nw { typedef typename psort_expr::pliteral literal; typedef typename psort_expr::pliteral_vector literal_vector; sorting_network_config m_cfg; class vc { unsigned v; // number of vertices unsigned c; // number of clauses static const unsigned lambda = 5; public: vc(unsigned v, unsigned c):v(v), c(c) {} bool operator<(vc const& other) const { return to_int() < other.to_int(); } vc operator+(vc const& other) const { return vc(v + other.v, c + other.c); } vc operator-(vc const& other) const { return vc(v - other.v, c - other.c); } unsigned to_int() const { return lambda*v + c; } vc operator*(unsigned n) const { return vc(n*v, n*c); } std::ostream& pp(std::ostream& out) const { return out << "v: " << v << " c: " << c; } }; static vc mk_min(vc const& v1, vc const& v2) { return (v1.to_int() < v2.to_int())?v1:v2; } enum cmp_t { LE, GE, EQ, GE_FULL, LE_FULL }; psort_expr& ctx; cmp_t m_t; // for testing static const bool m_disable_dcard = false; static const bool m_disable_dsorting = false; static const bool m_disable_dsmerge = false; static const bool m_force_dcard = false; static const bool m_force_dsorting = false; static const bool m_force_dsmerge = false; bool is_power_of2(unsigned n) const { return n != 0 && ((n-1) & n) == 0; } public: struct stats { unsigned m_num_compiled_vars; unsigned m_num_compiled_clauses; unsigned m_num_clause_vars; void reset() { memset(this, 0, sizeof(*this)); } stats() { reset(); } }; stats m_stats; struct scoped_stats { stats& m_stats; stats m_save; unsigned m_k, m_n; scoped_stats(stats& st, unsigned k, unsigned n): m_stats(st), m_save(st), m_k(k), m_n(n) {} ~scoped_stats() { IF_VERBOSE(1, verbose_stream() << "k: " << m_k << " n: " << m_n; verbose_stream() << " clauses: " << m_stats.m_num_compiled_clauses - m_save.m_num_compiled_clauses; verbose_stream() << " vars: " << m_stats.m_num_clause_vars - m_save.m_num_clause_vars; verbose_stream() << " clauses: " << m_stats.m_num_compiled_clauses; verbose_stream() << " vars: " << m_stats.m_num_clause_vars << "\n";); } }; psort_nw(psort_expr& c): ctx(c) {} sorting_network_config& cfg() { return m_cfg; } literal ge(bool full, unsigned k, unsigned n, literal const* xs) { if (k > n) { return ctx.mk_false(); } if (k == 0) { return ctx.mk_true(); } SASSERT(0 < k && k <= n); literal_vector in, out; if (k == 1) { return mk_or(n, xs); } if (dualize(k, n, xs, in)) { return le(full, k, in.size(), in.c_ptr()); } else { switch (m_cfg.m_encoding) { case sorted_at_most: case bimander_at_most: case ordered_at_most: case grouped_at_most: SASSERT(2*k <= n); m_t = full?GE_FULL:GE; // scoped_stats _ss(m_stats, k, n); psort_nw::card(k, n, xs, out); return out[k-1]; case unate_at_most: return unate_ge(full, k, n, xs); case circuit_at_most: return circuit_ge(full, k, n, xs); default: UNREACHABLE(); return xs[0]; } } } literal le(bool full, unsigned k, unsigned n, literal const* xs) { if (k >= n) { return ctx.mk_true(); } SASSERT(k < n); literal_vector in, out; if (dualize(k, n, xs, in)) { return ge(full, k, n, in.c_ptr()); } else if (k == 1) { literal_vector ors; // scoped_stats _ss(m_stats, k, n); switch (m_cfg.m_encoding) { case grouped_at_most: case sorted_at_most: case unate_at_most: case circuit_at_most: return mk_at_most_1(full, n, xs, ors, false); case bimander_at_most: return mk_at_most_1_bimander(full, n, xs, ors); case ordered_at_most: return mk_ordered_atmost_1(full, n, xs); default: UNREACHABLE(); return xs[0]; } } else { switch (m_cfg.m_encoding) { case sorted_at_most: case bimander_at_most: case ordered_at_most: case grouped_at_most: SASSERT(2*k <= n); m_t = full?LE_FULL:LE; // scoped_stats _ss(m_stats, k, n); card(k + 1, n, xs, out); return mk_not(out[k]); case unate_at_most: return unate_le(full, k, n, xs); case circuit_at_most: return circuit_le(full, k, n, xs); default: UNREACHABLE(); return xs[0]; } } } literal eq(bool full, unsigned k, unsigned n, literal const* xs) { if (k > n) { return ctx.mk_false(); } SASSERT(k <= n); literal_vector in, out; if (dualize(k, n, xs, in)) { return eq(full, k, n, in.c_ptr()); } else if (k == 1) { // scoped_stats _ss(m_stats, k, n); return mk_exactly_1(full, n, xs); } else { switch (m_cfg.m_encoding) { case sorted_at_most: case bimander_at_most: case grouped_at_most: case ordered_at_most: // scoped_stats _ss(m_stats, k, n); SASSERT(2*k <= n); m_t = EQ; card(k+1, n, xs, out); SASSERT(out.size() >= k+1); if (k == 0) { return mk_not(out[k]); } else { return mk_min(out[k-1], mk_not(out[k])); } case unate_at_most: return unate_eq(k, n, xs); case circuit_at_most: return circuit_eq(k, n, xs); default: UNREACHABLE(); return xs[0]; } } } /** \brief encode clauses for ws*xs >= k - normalize inequality to ws'*xs' >= a*2^(bits-1) - for each binary digit, sort contributions - merge with even digits from lower layer - creating 2*n vector - for last layer return that index a is on. */ literal le(unsigned k, unsigned n, unsigned const* ws, literal const* xs) { unsigned sum = 0; literal_vector Xs; for (unsigned i = 0; i < n; ++i) { sum += ws[i]; Xs.push_back(mk_not(xs[i])); } if (k >= sum) { return ctx.mk_true(); } return ge(sum - k, n, ws, Xs.begin()); } literal ge(unsigned k, unsigned n, unsigned const* ws, literal const* xs) { m_t = GE_FULL; return cmp(k, n, ws, xs); } literal eq(unsigned k, unsigned n, unsigned const* ws, literal const* xs) { return mk_and(ge(k, n, ws, xs), le(k, n, ws, xs)); #if 0 m_t = EQ; return cmp(k, n, ws, xs); #endif } literal cmp(unsigned k, unsigned n, unsigned const* ws, literal const* xs) { unsigned w_max = 0, sum = 0; literal_vector Xs; unsigned_vector Ws; for (unsigned i = 0; i < n; ++i) { sum += ws[i]; w_max = std::max(ws[i], w_max); Xs.push_back(xs[i]); Ws.push_back(ws[i]); } if (sum < k) { return ctx.mk_false(); } // Normalize to form Ws*Xs ~ a*2^{q-1} SASSERT(w_max > 0); unsigned bits = 0; while (w_max > 0) { bits++; w_max >>= 1; } unsigned pow = (1ul << (bits-1)); unsigned a = (k + pow - 1) / pow; // a*pow >= k SASSERT(a*pow >= k); SASSERT((a-1)*pow < k); if (a*pow > k) { Ws.push_back(a*pow - k); Xs.push_back(ctx.mk_true()); ++n; k = a*pow; } literal_vector W, We, B, S, E; for (unsigned i = 0; i < bits; ++i) { // B is digits from Xs that are set at bit position i B.reset(); for (unsigned j = 0; j < n; ++j) { if (0 != ((1 << i) & Ws[j])) { B.push_back(Xs[j]); } } // We is every second position of W We.reset(); for (unsigned j = 0; j + 2 <= W.size(); j += 2) { We.push_back(W[j+1]); } // if we test for equality, then what is not included has to be false. if (m_t == EQ && W.size() % 2 == 1) { E.push_back(mk_not(W.back())); } // B is the sorted (from largest to smallest bit) version of S S.reset(); sorting(B.size(), B.begin(), S); // W is the merge of S and We W.reset(); merge(S.size(), S.begin(), We.size(), We.begin(), W); } if (m_t == EQ) { E.push_back(W[a - 1]); if (a < W.size()) E.push_back(mk_not(W[a])); return mk_and(E); } SASSERT(m_t == GE_FULL); return W[a - 1]; } private: // perform unate addition up to k. literal unate_cmp(cmp_t cmp, unsigned k, unsigned n, literal const* xs) { unsigned last = k; if (cmp == LE || cmp == EQ || cmp == LE_FULL) { last = k + 1; } literal_vector carry; for (unsigned i = 0; i < last; ++i) { carry.push_back(ctx.mk_false()); } for (unsigned i = 0; i < n; ++i) { for (unsigned j = last; j-- > 0; ) { // c'[j] <-> (xs[i] & c[j-1]) | c[j] literal c0 = j > 0 ? carry[j-1] : ctx.mk_true(); carry[j] = mk_or(mk_and(xs[i], c0), carry[j]); } } switch (cmp) { case LE: case LE_FULL: return mk_not(carry[k]); case GE: case GE_FULL: return carry[k-1]; case EQ: return mk_and(mk_not(carry[k]), carry[k-1]); default: UNREACHABLE(); return xs[0]; } } literal unate_ge(bool full, unsigned k, unsigned n, literal const* xs) { return unate_cmp(full ? GE_FULL : GE, k, n, xs); } literal unate_le(bool full, unsigned k, unsigned n, literal const* xs) { return unate_cmp(full ? LE_FULL : LE, k, n, xs); } literal unate_eq(unsigned k, unsigned n, literal const* xs) { return unate_cmp(EQ, k, n, xs); } // circuit encoding void mk_unit_circuit(unsigned k, literal x, literal_vector& out) { out.push_back(x); for (unsigned i = 1; i < k; ++i) out.push_back(ctx.mk_false()); } literal mk_add_circuit(literal_vector const& x, literal_vector const& y, literal_vector& out) { literal c = ctx.mk_false(); SASSERT(x.size() == y.size()); for (unsigned i = 0; i < x.size(); ++i) { // out[i] = c + x[i] + y[i] // c' = c&x[i] | c&y[i] | x[i]&y[i]; literal_vector ors; ors.push_back(mk_and(c, mk_not(x[i]), mk_not(y[i]))); ors.push_back(mk_and(x[i], mk_not(c), mk_not(y[i]))); ors.push_back(mk_and(y[i], mk_not(c), mk_not(x[i]))); ors.push_back(mk_and(c, x[i], y[i])); literal o = mk_or(4, ors.c_ptr()); out.push_back(o); ors[0] = mk_and(c, x[i]); ors[1] = mk_and(c, y[i]); ors[2] = mk_and(x[i], y[i]); c = mk_or(3, ors.c_ptr()); } return c; } literal circuit_add(unsigned k, unsigned n, literal const* xs, literal_vector& out) { switch (n) { case 0: for (unsigned i = 0; i < k; ++i) { out.push_back(ctx.mk_false()); } return ctx.mk_false(); case 1: mk_unit_circuit(k, xs[0], out); return ctx.mk_false(); default: { literal_vector o1, o2; unsigned half = n / 2; literal ovfl1 = circuit_add(k, half, xs, o1); literal ovfl2 = circuit_add(k, n - half, xs + half, o2); literal ovfl3 = mk_add_circuit(o1, o2, out); return mk_or(ovfl1, ovfl2, ovfl3); } } } literal circuit_cmp(cmp_t cmp, unsigned k, unsigned n, literal const* xs) { literal_vector out, kvec; unsigned num_bits = 0; unsigned k1 = (cmp == LE || cmp == LE_FULL) ? k + 1 : k; unsigned k0 = k1; while (k0 > 0) { ++num_bits; k0 >>= 1; } for (unsigned i = 0; i < num_bits; ++i) { kvec.push_back((0 != (k1 & (1 << i))) ? ctx.mk_true() : ctx.mk_false()); } literal ovfl = circuit_add(num_bits, n, xs, out); switch (cmp) { case LE: case LE_FULL: return mk_not(mk_or(ovfl, mk_ge(out, kvec))); case GE: case GE_FULL: return mk_or(ovfl, mk_ge(out, kvec)); case EQ: { literal_vector eqs; SASSERT(kvec.size() == out.size()); for (unsigned i = 0; i < num_bits; ++i) { eqs.push_back(mk_or(mk_not(kvec[i]), out[i])); eqs.push_back(mk_or(kvec[i], mk_not(out[i]))); } eqs.push_back(mk_not(ovfl)); return mk_and(eqs); } default: UNREACHABLE(); return xs[0]; } } literal mk_ge(literal_vector const& x, literal_vector const& y) { literal r = ctx.mk_true(); literal g = ctx.mk_false(); for (unsigned j = x.size(); j-- > 0; ) { g = mk_or(g, mk_and(r, mk_and(x[j], mk_not(y[j])))); r = mk_or(g, mk_and(r, mk_or( x[j], mk_not(y[j])))); } return r; } literal circuit_ge(bool full, unsigned k, unsigned n, literal const* xs) { return circuit_cmp(full ? GE_FULL : GE, k, n, xs); } literal circuit_le(bool full, unsigned k, unsigned n, literal const* xs) { return circuit_cmp(full ? LE_FULL : LE, k, n, xs); } literal circuit_eq(unsigned k, unsigned n, literal const* xs) { return circuit_cmp(EQ, k, n, xs); } void add_implies_or(literal l, unsigned n, literal const* xs) { literal_vector lits(n, xs); lits.push_back(mk_not(l)); add_clause(lits); } literal mk_or(unsigned n, literal const* _ors) { literal_vector ors(n, _ors); unsigned j = 0; for (literal lit : ors) { if (is_true(lit)) return lit; if (!is_false(lit)) ors[j++] = lit; } ors.shrink(j); switch (j) { case 0: return ctx.mk_false(); case 1: return ors[0]; default: return ctx.mk_max(ors.size(), ors.c_ptr()); } } literal mk_or(literal l1, literal l2) { literal ors[2] = { l1, l2 }; return mk_or(2, ors); } literal mk_or(literal l1, literal l2, literal l3) { literal ors[3] = { l1, l2, l3 }; return mk_or(3, ors); } literal mk_or(literal_vector const& ors) { return mk_or(ors.size(), ors.c_ptr()); } literal mk_not(literal lit) { if (is_true(lit)) return ctx.mk_false(); if (is_false(lit)) return ctx.mk_true(); return ctx.mk_not(lit); } literal mk_and(literal l1, literal l2) { literal_vector xs; xs.push_back(l1); xs.push_back(l2); return mk_and(xs); } literal mk_and(literal l1, literal l2, literal l3) { literal_vector xs; xs.push_back(l1); xs.push_back(l2); xs.push_back(l3); return mk_and(xs); } bool is_true(literal l) { return l == ctx.mk_true(); } bool is_false(literal l) { return l == ctx.mk_false(); } literal mk_and(literal_vector const& _ands) { literal_vector ands(_ands); unsigned j = 0; for (literal lit : ands) { if (is_false(lit)) return lit; if (!is_true(lit)) ands[j++] = lit; } ands.shrink(j); switch (j) { case 0: return ctx.mk_true(); case 1: return ands[0]; case 2: return mk_min(ands[0], ands[1]); default: { return ctx.mk_min(ands.size(), ands.c_ptr()); } } } literal mk_exactly_1(bool full, unsigned n, literal const* xs) { TRACE("pb", tout << "exactly 1 with " << n << " arguments " << (full?"full":"not full") << "\n";); literal_vector ors; literal r1; switch (m_cfg.m_encoding) { case grouped_at_most: case sorted_at_most: case unate_at_most: case circuit_at_most: r1 = mk_at_most_1(full, n, xs, ors, true); break; case bimander_at_most: r1 = mk_at_most_1_bimander(full, n, xs, ors); break; case ordered_at_most: return mk_ordered_exactly_1(full, n, xs); default: UNREACHABLE(); return mk_ordered_exactly_1(full, n, xs); } if (full) { r1 = mk_and(r1, mk_or(ors)); } else { add_implies_or(r1, ors.size(), ors.c_ptr()); } return r1; } literal mk_at_most_1(bool full, unsigned n, literal const* xs, literal_vector& ors, bool use_ors) { TRACE("pb_verbose", tout << (full?"full":"partial") << " "; for (unsigned i = 0; i < n; ++i) tout << xs[i] << " "; tout << "\n";); literal_vector in(n, xs); literal result = fresh("at-most-1"); unsigned inc_size = 4; literal_vector ands; ands.push_back(result); while (!in.empty()) { ors.reset(); unsigned n = in.size(); if (n + 1 == inc_size) ++inc_size; for (unsigned i = 0; i < n; i += inc_size) { unsigned inc = std::min(n - i, inc_size); mk_at_most_1_small(full, inc, in.c_ptr() + i, result, ands); if (use_ors || n > inc_size) { ors.push_back(mk_or(inc, in.c_ptr() + i)); } } if (n <= inc_size) { break; } in.reset(); in.append(ors); } if (full) { add_clause(ands); } return result; } void mk_at_most_1_small(bool full, unsigned n, literal const* xs, literal result, literal_vector& ands) { SASSERT(n > 0); if (n == 1) { return; } // result => xs[0] + ... + xs[n-1] <= 1 for (unsigned i = 0; i < n; ++i) { for (unsigned j = i + 1; j < n; ++j) { add_clause(mk_not(result), mk_not(xs[i]), mk_not(xs[j])); } } // xs[0] + ... + xs[n-1] <= 1 => and_x if (full) { literal and_i = fresh("and"); for (unsigned i = 0; i < n; ++i) { literal_vector lits; lits.push_back(and_i); for (unsigned j = 0; j < n; ++j) { if (j != i) lits.push_back(xs[j]); } add_clause(lits); } ands.push_back(mk_not(and_i)); } } literal mk_at_most_1_small(unsigned n, literal const* xs) { SASSERT(n > 0); if (n == 1) { return ctx.mk_true(); } // r <=> and( or(!xi,!xj)) // literal_vector ands; for (unsigned i = 0; i < n; ++i) { for (unsigned j = i + 1; j < n; ++j) { ands.push_back(mk_or(mk_not(xs[i]), mk_not(xs[j]))); } } return mk_and(ands); } literal mk_ordered_exactly_1(bool full, unsigned n, literal const* xs) { return mk_ordered_1(full, true, n, xs); } literal mk_ordered_atmost_1(bool full, unsigned n, literal const* xs) { return mk_ordered_1(full, false, n, xs); } literal mk_ordered_1(bool full, bool is_eq, unsigned n, literal const* xs) { if (n <= 1 && !is_eq) { return ctx.mk_true(); } if (n == 0) { return ctx.mk_false(); } if (n == 1) { return xs[0]; } SASSERT(n > 1); // y0 -> y1 // x0 -> y0 // x1 -> y1 // r, y0 -> ~x1 // r, y1 -> ~x2 // r -> x3 | y1 // r -> ~x3 | ~y1 // x0,x1,x2, .., x_{n-1}, x_n // y0,y1,y2, .., y_{n-1} // y_i -> y_{i+1} i = 0, ..., n - 2 // x_i -> y_i i = 0, ..., n - 1 // r, y_i -> ~x_{i+1} i = 0, ..., n - 1 // exactly 1: // r -> x_n | y_{n-1} // full (exactly 1): // two_i -> y_i & x_{i+1} // zero -> ~x_n // zero -> ~y_{n-1} // r | zero | two_0 | ... | two_{n-1} // full atmost 1: // r | two | two_0 | ... | two_{n-1} literal r = fresh("ordered"); literal_vector ys; for (unsigned i = 0; i + 1 < n; ++i) { ys.push_back(fresh("y")); } for (unsigned i = 0; i + 2 < n; ++i) { add_clause(mk_not(ys[i]), ys[i + 1]); } for (unsigned i = 0; i + 1 < n; ++i) { add_clause(mk_not(xs[i]), ys[i]); add_clause(mk_not(r), mk_not(ys[i]), mk_not(xs[i + 1])); } if (is_eq) { add_clause(mk_not(r), ys[n-2], xs[n-1]); } for (unsigned i = 1; i < n - 1; ++i) { add_clause(mk_not(ys[i]), xs[i], ys[i-1]); } add_clause(mk_not(ys[0]), xs[0]); if (full) { literal_vector twos; for (unsigned i = 0; i < n - 1; ++i) { twos.push_back(fresh("two")); } add_clause(mk_not(twos[0]), ys[0]); add_clause(mk_not(twos[0]), xs[1]); for (unsigned i = 1; i < n - 1; ++i) { add_clause(mk_not(twos[i]), ys[i], twos[i-1]); add_clause(mk_not(twos[i]), xs[i + 1], twos[i-1]); } if (is_eq) { literal zero = fresh("zero"); add_clause(mk_not(zero), mk_not(xs[n-1])); add_clause(mk_not(zero), mk_not(ys[n-2])); add_clause(r, zero, twos.back()); } else { add_clause(r, twos.back()); } } return r; } // literal mk_at_most_1_bimander(bool full, unsigned n, literal const* xs, literal_vector& ors) { if (full) { return mk_at_most_1(full, n, xs, ors, true); } literal_vector in(n, xs); literal result = fresh("bimander"); unsigned inc_size = 2; literal_vector ands; for (unsigned i = 0; i < n; i += inc_size) { unsigned inc = std::min(n - i, inc_size); mk_at_most_1_small(full, inc, in.c_ptr() + i, result, ands); ors.push_back(mk_or(inc, in.c_ptr() + i)); } unsigned nbits = 0; while (static_cast(1 << nbits) < ors.size()) { ++nbits; } literal_vector bits; for (unsigned k = 0; k < nbits; ++k) { bits.push_back(fresh("bit")); } for (unsigned i = 0; i < ors.size(); ++i) { for (unsigned k = 0; k < nbits; ++k) { bool bit_set = (i & (static_cast(1 << k))) != 0; add_clause(mk_not(result), mk_not(ors[i]), bit_set ? bits[k] : mk_not(bits[k])); } } return result; } std::ostream& pp(std::ostream& out, unsigned n, literal const* lits) { for (unsigned i = 0; i < n; ++i) ctx.pp(out, lits[i]) << " "; return out; } std::ostream& pp(std::ostream& out, literal_vector const& lits) { for (literal const& l : lits) ctx.pp(out, l) << " "; return out; } // 0 <= k <= N // SUM x_i >= k // <=> // SUM ~x_i <= N - k // suppose k > N/2, then it is better to solve dual. bool dualize(unsigned& k, unsigned N, literal const* xs, literal_vector& in) { SASSERT(0 <= k && k <= N); if (2*k <= N) { return false; } k = N - k; for (unsigned i = 0; i < N; ++i) { in.push_back(mk_not(xs[i])); } TRACE("pb_verbose", //pp(tout << N << ": ", in); tout << " ~ " << k << "\n";); return true; } bool even(unsigned n) const { return (0 == (n & 0x1)); } bool odd(unsigned n) const { return !even(n); } unsigned ceil2(unsigned n) const { return n/2 + odd(n); } unsigned floor2(unsigned n) const { return n/2; } unsigned power2(unsigned n) const { SASSERT(n < 10); return 1 << n; } literal mk_max(literal a, literal b) { if (a == b) return a; m_stats.m_num_compiled_vars++; literal lits[2] = { a, b}; return ctx.mk_max(2, lits); } literal mk_min(literal a, literal b) { if (a == b) return a; m_stats.m_num_compiled_vars++; literal lits[2] = { a, b}; return ctx.mk_min(2, lits); } literal fresh(char const* n) { m_stats.m_num_compiled_vars++; return ctx.fresh(n); } void add_clause(literal l1, literal l2, literal l3) { literal lits[3] = { l1, l2, l3 }; add_clause(3, lits); } void add_clause(literal l1, literal l2) { literal lits[2] = { l1, l2 }; add_clause(2, lits); } void add_clause(literal_vector const& lits) { add_clause(lits.size(), lits.c_ptr()); } void add_clause(unsigned n, literal const* ls) { for (unsigned i = 0; i < n; ++i) { if (is_true(ls[i])) return; } m_stats.m_num_compiled_clauses++; m_stats.m_num_clause_vars += n; literal_vector tmp(n, ls); ctx.mk_clause(n, tmp.c_ptr()); } // y1 <= mk_max(x1,x2) // y2 <= mk_min(x1,x2) void cmp_ge(literal x1, literal x2, literal y1, literal y2) { add_clause(mk_not(y2), x1); add_clause(mk_not(y2), x2); add_clause(mk_not(y1), x1, x2); } // mk_max(x1,x2) <= y1 // mk_min(x1,x2) <= y2 void cmp_le(literal x1, literal x2, literal y1, literal y2) { add_clause(mk_not(x1), y1); add_clause(mk_not(x2), y1); add_clause(mk_not(x1), mk_not(x2), y2); } void cmp_eq(literal x1, literal x2, literal y1, literal y2) { cmp_ge(x1, x2, y1, y2); cmp_le(x1, x2, y1, y2); } void cmp(literal x1, literal x2, literal y1, literal y2) { switch(m_t) { case LE: case LE_FULL: cmp_le(x1, x2, y1, y2); break; case GE: case GE_FULL: cmp_ge(x1, x2, y1, y2); break; case EQ: cmp_eq(x1, x2, y1, y2); break; } } vc vc_cmp() { return vc(2, (m_t==EQ)?6:3); } void card(unsigned k, unsigned n, literal const* xs, literal_vector& out) { TRACE("pb_verbose", tout << "card k: " << k << " n: " << n << "\n";); if (n <= k) { psort_nw::sorting(n, xs, out); } else if (use_dcard(k, n)) { TRACE("pb_verbose", tout << "use dcard\n";); dsorting(k, n, xs, out); } else { TRACE("pb_verbose", tout << "use merge\n";); literal_vector out1, out2; unsigned half = n/2; // TBD card(k, half, xs, out1); card(k, n-half, xs + half, out2); smerge(k, out1.size(), out1.c_ptr(), out2.size(), out2.c_ptr(), out); } TRACE("pb_verbose", tout << "card k: " << k << " n: " << n << "\n"; //pp(tout << "in:", n, xs) << "\n"; //pp(tout << "out:", out) << "\n"; ); } vc vc_card(unsigned k, unsigned n) { if (n <= k) { return vc_sorting(n); } else if (use_dcard(k, n)) { return vc_dsorting(k, n); } else { return vc_card_rec(k, n); } } vc vc_card_rec(unsigned k, unsigned n) { unsigned l = n/2; return vc_card(k, l) + vc_card(k, n-l) + vc_smerge(k, l, n-l); } bool use_dcard(unsigned k, unsigned n) { return m_force_dcard || (!m_disable_dcard && n < 10 && vc_dsorting(k, n) < vc_card_rec(k, n)); } void merge(unsigned a, literal const* as, unsigned b, literal const* bs, literal_vector& out) { unsigned nc = m_stats.m_num_compiled_clauses; (void)nc; if (a == 1 && b == 1) { literal y1 = mk_max(as[0], bs[0]); literal y2 = mk_min(as[0], bs[0]); out.push_back(y1); out.push_back(y2); psort_nw::cmp(as[0], bs[0], y1, y2); } else if (a == 0) { out.append(b, bs); } else if (b == 0) { out.append(a, as); } else if (use_dsmerge(a, b, a + b)) { dsmerge(a + b, a, as, b, bs, out); } else if (even(a) && odd(b)) { merge(b, bs, a, as, out); } else { literal_vector even_a, odd_a; literal_vector even_b, odd_b; literal_vector out1, out2; SASSERT(a > 1 || b > 1); split(a, as, even_a, odd_a); split(b, bs, even_b, odd_b); SASSERT(!even_a.empty()); SASSERT(!even_b.empty()); merge(even_a.size(), even_a.c_ptr(), even_b.size(), even_b.c_ptr(), out1); merge(odd_a.size(), odd_a.c_ptr(), odd_b.size(), odd_b.c_ptr(), out2); interleave(out1, out2, out); } TRACE("pb_verbose", tout << "merge a: " << a << " b: " << b << " "; tout << "num clauses " << m_stats.m_num_compiled_clauses - nc << "\n"; vc_dsmerge(a, b, a + b).pp(tout << "vc_dsmerge ") << "\n"; vc_smerge_rec(a, b, a + b).pp(tout << "vc_smerge_rec ") << "\n"; //pp(tout << "a:", a, as) << "\n"; //pp(tout << "b:", b, bs) << "\n"; //pp(tout << "out:", out) << "\n"; ); } vc vc_merge(unsigned a, unsigned b) { if (a == 1 && b == 1) { return vc_cmp(); } else if (a == 0 || b == 0) { return vc(0, 0); } else if (use_dsmerge(a, b, a + b)) { return vc_dsmerge(a, b, a + b); } else { return vc_merge_rec(a, b); } } vc vc_merge_rec(unsigned a, unsigned b) { return vc_merge(ceil2(a), ceil2(b)) + vc_merge(floor2(a), floor2(b)) + vc_interleave(ceil2(a) + ceil2(b), floor2(a) + floor2(b)) - vc(0, 2); } void split(unsigned n, literal const* ls, literal_vector& even, literal_vector& odd) { for (unsigned i = 0; i < n; i += 2) { even.push_back(ls[i]); } for (unsigned i = 1; i < n; i += 2) { odd.push_back(ls[i]); } } void interleave(literal_vector const& as, literal_vector const& bs, literal_vector& out) { unsigned nc = m_stats.m_num_compiled_clauses; (void)nc; SASSERT(as.size() >= bs.size()); SASSERT(as.size() <= bs.size() + 2); SASSERT(!as.empty()); out.push_back(as[0]); unsigned sz = std::min(as.size()-1, bs.size()); for (unsigned i = 0; i < sz; ++i) { literal y1 = mk_max(as[i+1],bs[i]); literal y2 = mk_min(as[i+1],bs[i]); psort_nw::cmp(as[i+1], bs[i], y1, y2); out.push_back(y1); out.push_back(y2); } if (as.size() == bs.size()) { out.push_back(bs[sz]); } else if (as.size() == bs.size() + 2) { out.push_back(as[sz+1]); } SASSERT(out.size() == as.size() + bs.size()); TRACE("pb_verbose", tout << "interleave: " << as.size() << " " << bs.size() << " "; tout << "num clauses " << m_stats.m_num_compiled_clauses - nc << "\n"; //pp(tout << "a: ", as) << "\n"; //pp(tout << "b: ", bs) << "\n"; //pp(tout << "out: ", out) << "\n"; ); } vc vc_interleave(unsigned a, unsigned b) { return vc_cmp()*std::min(a-1,b); } public: void sorting(unsigned n, literal const* xs, literal_vector& out) { TRACE("pb_verbose", tout << "sorting: " << n << "\n";); switch(n) { case 0: break; case 1: out.push_back(xs[0]); break; case 2: psort_nw::merge(1, xs, 1, xs+1, out); break; default: if (use_dsorting(n)) { TRACE("pb_verbose", tout << "use dsorting: " << n << "\n";); dsorting(n, n, xs, out); } else { TRACE("pb_verbose", tout << "use merge: " << n << "\n";); literal_vector out1, out2; unsigned half = n/2; // TBD sorting(half, xs, out1); sorting(n-half, xs+half, out2); merge(out1.size(), out1.c_ptr(), out2.size(), out2.c_ptr(), out); } break; } TRACE("pb_verbose", tout << "sorting: " << n << "\n"; //pp(tout << "in:", n, xs) << "\n"; //pp(tout << "out:", out) << "\n"; ); } private: vc vc_sorting(unsigned n) { switch(n) { case 0: return vc(0,0); case 1: return vc(0,0); case 2: return vc_merge(1,1); default: if (use_dsorting(n)) { return vc_dsorting(n, n); } else { return vc_sorting_rec(n); } } } vc vc_sorting_rec(unsigned n) { SASSERT(n > 2); unsigned l = n/2; return vc_sorting(l) + vc_sorting(n-l) + vc_merge(l, n-l); } bool use_dsorting(unsigned n) { SASSERT(n > 2); return m_force_dsorting || (!m_disable_dsorting && n < 10 && vc_dsorting(n, n) < vc_sorting_rec(n)); } void smerge(unsigned c, unsigned a, literal const* as, unsigned b, literal const* bs, literal_vector& out) { unsigned nc = m_stats.m_num_compiled_clauses; (void)nc; if (a == 1 && b == 1 && c == 1) { literal y = mk_max(as[0], bs[0]); if (m_t != GE) { // x1 <= mk_max(x1,x2) // x2 <= mk_max(x1,x2) add_clause(mk_not(as[0]), y); add_clause(mk_not(bs[0]), y); } if (m_t != LE) { // mk_max(x1,x2) <= x1, x2 add_clause(mk_not(y), as[0], bs[0]); } out.push_back(y); } else if (a == 0) { out.append(std::min(c, b), bs); } else if (b == 0) { out.append(std::min(c, a), as); } else if (a > c) { smerge(c, c, as, b, bs, out); } else if (b > c) { smerge(c, a, as, c, bs, out); } else if (a + b <= c) { merge(a, as, b, bs, out); } else if (use_dsmerge(a, b, c)) { dsmerge(c, a, as, b, bs, out); } else { literal_vector even_a, odd_a; literal_vector even_b, odd_b; literal_vector out1, out2; split(a, as, even_a, odd_a); split(b, bs, even_b, odd_b); SASSERT(!even_a.empty()); SASSERT(!even_b.empty()); unsigned c1, c2; if (even(c)) { c1 = 1 + c/2; c2 = c/2; } else { c1 = (c + 1)/2; c2 = (c - 1)/2; } smerge(c1, even_a.size(), even_a.c_ptr(), even_b.size(), even_b.c_ptr(), out1); smerge(c2, odd_a.size(), odd_a.c_ptr(), odd_b.size(), odd_b.c_ptr(), out2); SASSERT(out1.size() == std::min(even_a.size()+even_b.size(), c1)); SASSERT(out2.size() == std::min(odd_a.size()+odd_b.size(), c2)); literal y; if (even(c)) { literal z1 = out1.back(); literal z2 = out2.back(); out1.pop_back(); out2.pop_back(); y = mk_max(z1, z2); if (m_t != GE) { add_clause(mk_not(z1), y); add_clause(mk_not(z2), y); } if (m_t != LE) { add_clause(mk_not(y), z1, z2); } } interleave(out1, out2, out); if (even(c)) { out.push_back(y); } } TRACE("pb_verbose", tout << "smerge: c:" << c << " a:" << a << " b:" << b << " "; tout << "num clauses " << m_stats.m_num_compiled_clauses - nc << "\n"; //pp(tout << "a:", a, as) << "\n"; //pp(tout << "b:", b, bs) << "\n"; //pp(tout << "out:", out) << "\n"; ); SASSERT(out.size() == std::min(a + b, c)); } vc vc_smerge(unsigned a, unsigned b, unsigned c) { if (a == 1 && b == 1 && c == 1) { vc v(1,0); if (m_t != GE) v = v + vc(0, 2); if (m_t != LE) v = v + vc(0, 1); return v; } if (a == 0 || b == 0) return vc(0, 0); if (a > c) return vc_smerge(c, b, c); if (b > c) return vc_smerge(a, c, c); if (a + b <= c) return vc_merge(a, b); if (use_dsmerge(a, b, c)) return vc_dsmerge(a, b, c); return vc_smerge_rec(a, b, c); } vc vc_smerge_rec(unsigned a, unsigned b, unsigned c) { return vc_smerge(ceil2(a), ceil2(b), even(c)?(1+c/2):((c+1)/2)) + vc_smerge(floor2(a), floor2(b), even(c)?(c/2):((c-1)/2)) + vc_interleave(ceil2(a)+ceil2(b),floor2(a)+floor2(b)) + vc(1, 0) + ((m_t != GE)?vc(0, 2):vc(0, 0)) + ((m_t != LE)?vc(0, 1):vc(0, 0)); } bool use_dsmerge(unsigned a, unsigned b, unsigned c) { return m_force_dsmerge || (!m_disable_dsmerge && a < 10 && b < 10 && vc_dsmerge(a, b, a + b) < vc_smerge_rec(a, b, c)); } void dsmerge( unsigned c, unsigned a, literal const* as, unsigned b, literal const* bs, literal_vector& out) { unsigned nc = m_stats.m_num_compiled_clauses; (void)nc; SASSERT(a <= c); SASSERT(b <= c); SASSERT(a + b >= c); for (unsigned i = 0; i < c; ++i) { out.push_back(fresh("dsmerge")); } if (m_t != GE) { for (unsigned i = 0; i < a; ++i) { add_clause(mk_not(as[i]), out[i]); } for (unsigned i = 0; i < b; ++i) { add_clause(mk_not(bs[i]), out[i]); } for (unsigned i = 1; i <= a; ++i) { for (unsigned j = 1; j <= b && i + j <= c; ++j) { add_clause(mk_not(as[i-1]),mk_not(bs[j-1]),out[i+j-1]); } } } if (m_t != LE) { literal_vector ls; for (unsigned k = 0; k < c; ++k) { ls.reset(); ls.push_back(mk_not(out[k])); if (a <= k) { add_clause(mk_not(out[k]), bs[k-a]); } if (b <= k) { add_clause(mk_not(out[k]), as[k-b]); } for (unsigned i = 0; i < std::min(a,k + 1); ++i) { unsigned j = k - i; SASSERT(i + j == k); if (j < b) { ls.push_back(as[i]); ls.push_back(bs[j]); add_clause(ls); ls.pop_back(); ls.pop_back(); } } } } TRACE("pb_verbose", tout << "dsmerge: c:" << c << " a:" << a << " b:" << b << " "; tout << "num clauses: " << m_stats.m_num_compiled_clauses - nc << "\n"; vc_dsmerge(a, b, c).pp(tout << "vc_dsmerge ") << "\n"; vc_smerge_rec(a, b, c).pp(tout << "vc_smerge_rec ") << "\n"; ); } vc vc_dsmerge(unsigned a, unsigned b, unsigned c) { vc v(c, 0); if (m_t != GE) { v = v + vc(0, a + b + std::min(a, c)*std::min(b, c)/2); } if (m_t != LE) { v = v + vc(0, std::min(a, c)*std::min(b, c)/2); } return v; } void dsorting(unsigned m, unsigned n, literal const* xs, literal_vector& out) { SASSERT(m <= n); literal_vector lits; unsigned nc = m_stats.m_num_compiled_clauses; (void)nc; for (unsigned i = 0; i < m; ++i) { out.push_back(fresh("dsort")); } if (m_t != GE) { for (unsigned k = 1; k <= m; ++k) { lits.push_back(out[k-1]); add_subset(true, k, 0, lits, n, xs); lits.pop_back(); } } if (m_t != LE) { for (unsigned k = 1; k <= m; ++k) { lits.push_back(mk_not(out[k-1])); add_subset(false, n-k+1, 0, lits, n, xs); lits.pop_back(); } } TRACE("pb_verbose", tout << "dsorting m: " << m << " n: " << n << " "; tout << "num clauses: " << m_stats.m_num_compiled_clauses - nc << "\n";); } vc vc_dsorting(unsigned m, unsigned n) { SASSERT(m <= n && n < 10); vc v(m, 0); if (m_t != GE) { v = v + vc(0, power2(n-1)); } if (m_t != LE) { v = v + vc(0, power2(n-1)); } return v; } void add_subset(bool polarity, unsigned k, unsigned offset, literal_vector& lits, unsigned n, literal const* xs) { TRACE("pb_verbose", tout << "k:" << k << " offset: " << offset << " n: " << n << " "; //pp(tout, lits) << "\n"; ); SASSERT(k + offset <= n); if (k == 0) { add_clause(lits); return; } for (unsigned i = offset; i < n - k + 1; ++i) { lits.push_back(polarity?mk_not(xs[i]):xs[i]); add_subset(polarity, k-1, i+1, lits, n, xs); lits.pop_back(); } } }; #endif z3-z3-4.8.7/src/util/sstream.h000066400000000000000000000007611356505360400160260ustar00rootroot00000000000000/* Copyright (c) 2018 Microsoft Corporation Module Name: nat_set.h Abstract: Wrapper for sstream. Author: Leonardo de Moura (leonardo) 2013 Revision History: */ #pragma once #include #include namespace lean { /** \brief Wrapper for std::ostringstream */ class sstream { std::ostringstream m_strm; public: std::string str() const { return m_strm.str(); } template sstream & operator<<(T const & t) { m_strm << t; return *this; } }; } z3-z3-4.8.7/src/util/stack.cpp000066400000000000000000000072271356505360400160140ustar00rootroot00000000000000/*++ Copyright (c) 2007 Microsoft Corporation Module Name: stack.cpp Abstract: Low level stack (aka stack-based allocator). Author: Leonardo (leonardo) 2011-02-27 Notes: --*/ #include "util/stack.h" #include "util/page.h" #include "util/tptr.h" inline void stack::store_mark(size_t m) { reinterpret_cast(m_curr_ptr)[0] = m; m_curr_ptr += sizeof(size_t); } inline size_t stack::top_mark() const { return reinterpret_cast(m_curr_ptr)[-1]; } inline size_t ptr2mark(void * ptr, bool external) { return reinterpret_cast(ptr) | static_cast(external); } #define MASK (static_cast(-1) - 1) inline char * mark2ptr(size_t m) { return reinterpret_cast(m & MASK); } inline bool external_ptr(size_t m) { return static_cast(m & 1); } inline void stack::allocate_page(size_t m) { m_curr_page = allocate_default_page(m_curr_page, m_free_pages); m_curr_ptr = m_curr_page; m_curr_end_ptr = end_of_default_page(m_curr_page); store_mark(m); } inline void stack::store_mark(void * ptr, bool external) { SASSERT(m_curr_ptr < m_curr_end_ptr || m_curr_ptr == m_curr_end_ptr); // mem is aligned if (m_curr_ptr + sizeof(size_t) > m_curr_end_ptr) { SASSERT(m_curr_ptr == m_curr_end_ptr); // doesn't fit in the current page allocate_page(ptr2mark(ptr, external)); } else { store_mark(ptr2mark(ptr, external)); } } stack::stack() { m_curr_page = nullptr; m_curr_ptr = nullptr; m_curr_end_ptr = nullptr; m_free_pages = nullptr; allocate_page(0); SASSERT(empty()); } stack::~stack() { reset(); del_pages(m_curr_page); del_pages(m_free_pages); } void stack::reset() { while(!empty()) deallocate(); } void * stack::top() const { SASSERT(!empty()); size_t m = top_mark(); void * r = mark2ptr(m); if (external_ptr(m)) r = reinterpret_cast(r)[0]; return r; } void * stack::allocate_small(size_t size, bool external) { SASSERT(size < DEFAULT_PAGE_SIZE); char * new_curr_ptr = m_curr_ptr + size; char * result; if (new_curr_ptr < m_curr_end_ptr) { result = m_curr_ptr; m_curr_ptr = ALIGN(char *, new_curr_ptr); } else { allocate_page(top_mark()); result = m_curr_ptr; m_curr_ptr += size; m_curr_ptr = ALIGN(char *, m_curr_ptr); } store_mark(result, external); SASSERT(m_curr_ptr > m_curr_page); SASSERT(m_curr_ptr <= m_curr_end_ptr); return result; } void * stack::allocate_big(size_t size) { char * r = alloc_svect(char, size); void * mem = allocate_small(sizeof(char*), true); reinterpret_cast(mem)[0] = r; SASSERT(m_curr_ptr > m_curr_page); SASSERT(m_curr_ptr <= m_curr_end_ptr); return r; } void stack::deallocate() { SASSERT(m_curr_ptr > m_curr_page); SASSERT(m_curr_ptr <= m_curr_end_ptr); size_t m = top_mark(); SASSERT(m != 0); if (m_curr_ptr == m_curr_page + sizeof(size_t)) { // mark is in the beginning of the page char * prev = prev_page(m_curr_page); recycle_page(m_curr_page, m_free_pages); m_curr_page = prev; m_curr_ptr = mark2ptr(m); m_curr_end_ptr = end_of_default_page(m_curr_page); SASSERT(m_curr_ptr > m_curr_page); SASSERT(m_curr_ptr <= m_curr_end_ptr); } else { // mark is in the middle of the page m_curr_ptr = mark2ptr(m); SASSERT(m_curr_ptr < m_curr_end_ptr); } if (external_ptr(m)) { dealloc_svect(reinterpret_cast(m_curr_ptr)[0]); } SASSERT(m_curr_ptr > m_curr_page); } z3-z3-4.8.7/src/util/stack.h000066400000000000000000000024421356505360400154530ustar00rootroot00000000000000/*++ Copyright (c) 2007 Microsoft Corporation Module Name: stack.h Abstract: Low level stack (aka stack-based allocator). Author: Leonardo (leonardo) 2011-02-27 Notes: --*/ #ifndef STACK_H_ #define STACK_H_ #include "util/page.h" #include "util/debug.h" class stack { char * m_curr_page; char * m_curr_ptr; //!< Next free space in the current page. char * m_curr_end_ptr; //!< Point to the end of the current page. char * m_free_pages; void store_mark(size_t m); void store_mark(void * ptr, bool external); size_t top_mark() const; void allocate_page(size_t mark); void * allocate_small(size_t size, bool external); void * allocate_big(size_t size); public: stack(); ~stack(); void * allocate(size_t size) { return size < DEFAULT_PAGE_SIZE ? allocate_small(size, false) : allocate_big(size); } void deallocate(); bool empty() const { return reinterpret_cast(m_curr_ptr)[-1] == 0; } void * top() const; void reset(); template void deallocate(T * ptr) { SASSERT(ptr == top()); ptr->~T(); deallocate(); } }; inline void * operator new(size_t s, stack & r) { return r.allocate(s); } inline void operator delete(void * ptr, stack & r) { SASSERT(ptr == r.top()); r.deallocate(); } #endif z3-z3-4.8.7/src/util/statistics.cpp000066400000000000000000000163151356505360400170770ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: statistics.h Abstract: Wrapper for reporting statistics Author: Leonardo (leonardo) 2011-05-17 Notes: --*/ #include "util/statistics.h" #include "util/map.h" #include "util/str_hashtable.h" #include "util/buffer.h" #include "util/smt2_util.h" #include void statistics::update(char const * key, unsigned inc) { if (inc != 0) m_stats.push_back(key_val_pair(key, inc)); } void statistics::update(char const * key, double inc) { if (inc != 0.0) m_d_stats.push_back(key_d_val_pair(key, inc)); } void statistics::copy(statistics const & st) { m_stats.append(st.m_stats); m_d_stats.append(st.m_d_stats); } void statistics::reset() { m_stats.reset(); m_d_stats.reset(); } template static void mk_map(V const & v, M & m) { typename V::const_iterator it = v.begin(); typename V::const_iterator end = v.end(); for (; it != end; ++it) { typename V::data::second_type val; if (m.find(it->first, val)) m.insert(it->first, it->second + val); else m.insert(it->first, it->second); } } template static void get_keys(M const & m, ptr_buffer & keys) { typename M::iterator it = m.begin(); typename M::iterator end = m.end(); for (; it != end; ++it) { keys.push_back(const_cast(it->m_key)); } } static void display_smt2_key(std::ostream & out, char const * key) { SASSERT(key != 0); out << ":"; if (*key == ':') key++; while (*key) { if (is_smt2_simple_symbol_char(*key)) out << *key; else out << "-"; key++; } } struct str_lt { bool operator()(char const * s1, char const * s2) const { return strcmp(s1, s2) < 0; } }; typedef map key2val; typedef map key2dval; unsigned get_max_len(ptr_buffer & keys) { unsigned max = 0; for (unsigned i = 0; i < static_cast(keys.size()); i++) { char * k = keys.get(i); if (*k == ':') k++; unsigned curr = static_cast(strlen(k)); if (curr > max) max = curr; } return max; } void statistics::display_smt2(std::ostream & out) const { #define INIT_DISPLAY() \ key2val m_u; \ key2dval m_d; \ mk_map(m_stats, m_u); \ mk_map(m_d_stats, m_d); \ ptr_buffer keys; \ get_keys(m_u, keys); \ get_keys(m_d, keys); \ std::sort(keys.begin(), keys.end(), str_lt()); \ unsigned max = get_max_len(keys); INIT_DISPLAY(); bool first = true; #define DISPLAY_KEY() { \ if (!first) \ out << "\n "; \ display_smt2_key(out, k); \ unsigned len = static_cast(strlen(k)); \ for (unsigned j = len; j < max; j++) \ out << " "; \ first = false; \ } out << "("; for (unsigned i = 0; i < keys.size(); i++) { char * k = keys.get(i); unsigned val; if (m_u.find(k, val)) { DISPLAY_KEY(); out << " " << val; } else { double d_val = 0.0; m_d.find(k, d_val); DISPLAY_KEY(); out << " " << std::fixed << std::setprecision(2) << d_val; } } out << ")\n"; } void statistics::display(std::ostream & out) const { INIT_DISPLAY(); #undef DISPLAY_KEY #define DISPLAY_KEY() { \ if (*k == ':') \ k++; \ out << k << ":"; \ unsigned len = static_cast(strlen(k)); \ for (unsigned j = len; j < max; j++) \ out << " "; \ } for (unsigned i = 0; i < keys.size(); i++) { char * k = keys.get(i); unsigned val; if (m_u.find(k, val)) { DISPLAY_KEY(); out << " " << val << "\n"; } else { double d_val = 0.0; m_d.find(k, d_val); DISPLAY_KEY(); out << " " << std::fixed << std::setprecision(2) << d_val << "\n"; } } } template static void display_internal(std::ostream & out, M const & m) { typename M::iterator it = m.begin(); typename M::iterator end = m.end(); for (; it != end; it++) { char const * key = it->m_key; if (*key == ':') key++; while (*key) { if ('a' <= *key && *key <= 'z') out << ('A' + (*key - 'a')); else if (*key == ' ') out << "_"; else out << *key; } out << " " << it->m_value << "\n"; } } void statistics::display_internal(std::ostream & out) const { key2val m_u; key2dval m_d; mk_map(m_stats, m_u); mk_map(m_d_stats, m_d); ::display_internal(out, m_u); ::display_internal(out, m_d); } unsigned statistics::size() const { return m_stats.size() + m_d_stats.size(); } bool statistics::is_uint(unsigned idx) const { return idx < m_stats.size(); } char const * statistics::get_key(unsigned idx) const { if (is_uint(idx)) return m_stats[idx].first; else return m_d_stats[idx - m_stats.size()].first; } unsigned statistics::get_uint_value(unsigned idx) const { SASSERT(idx < size()); SASSERT(is_uint(idx)); return m_stats[idx].second; } double statistics::get_double_value(unsigned idx) const { SASSERT(idx < size()); SASSERT(!is_uint(idx)); return m_d_stats[idx - m_stats.size()].second; } static void get_uint64_stats(statistics& st, char const* name, unsigned long long value) { if (value <= UINT_MAX) { st.update(name, static_cast(value)); } else { st.update(name, static_cast(value)); } } void get_memory_statistics(statistics& st) { unsigned long long max_mem = memory::get_max_used_memory(); unsigned long long mem = memory::get_allocation_size(); max_mem = (100*max_mem)/(1024*1024); mem = (100*mem)/(1024*1024); st.update("max memory", static_cast(max_mem)/100.0); st.update("memory", static_cast(mem)/100.0); get_uint64_stats(st, "num allocs", memory::get_allocation_count()); } void get_rlimit_statistics(reslimit& l, statistics& st) { get_uint64_stats(st, "rlimit count", l.count()); } z3-z3-4.8.7/src/util/statistics.h000066400000000000000000000021701356505360400165360ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: statistics.h Abstract: Wrapper for reporting statistics Author: Leonardo (leonardo) 2011-05-17 Notes: --*/ #ifndef STATISTICS_H_ #define STATISTICS_H_ #include #include "util/vector.h" #include "util/rlimit.h" class statistics { typedef std::pair key_val_pair; svector m_stats; typedef std::pair key_d_val_pair; svector m_d_stats; public: void copy(statistics const & st); void reset(); void update(char const * key, unsigned inc); void update(char const * key, double inc); void display(std::ostream & out) const; void display_smt2(std::ostream & out) const; void display_internal(std::ostream & out) const; unsigned size() const; bool is_uint(unsigned idx) const; char const * get_key(unsigned idx) const; unsigned get_uint_value(unsigned idx) const; double get_double_value(unsigned idx) const; }; void get_memory_statistics(statistics& st); void get_rlimit_statistics(reslimit& l, statistics& st); #endif z3-z3-4.8.7/src/util/stats.h000066400000000000000000000010231356505360400154760ustar00rootroot00000000000000/*++ Copyright (c) 2009 Microsoft Corporation Module Name: stats.h Abstract: Shared utilities for displaying statistics. Author: nbjorner 2009-12-6 Revision History: --*/ #ifndef STATS_H_ #define STATS_H_ #include inline void print_stat(std::ostream& out, char const* msg, unsigned num) { if (num > 0) { out << msg << num << "\n"; } } inline void print_stat_f(std::ostream& out, char const* msg, float num) { if (num > 0.0f) { out << msg << num << "\n"; } } #endif z3-z3-4.8.7/src/util/stopwatch.h000066400000000000000000000032631356505360400163640ustar00rootroot00000000000000/*++ Copyright (c) 2008 Microsoft Corporation Module Name: stopwatch.h Abstract: High resolution time keeping Author: Christoph Wintersteiger (t-cwinte) 2008-12-24 Revision History: --*/ #ifndef STOPWATCH_H_ #define STOPWATCH_H_ #include "util/debug.h" #include class stopwatch { 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; bool m_running = false; // FIXME: just use auto with VS 2015+ static clock_t get() { return std::chrono::steady_clock::now(); } public: stopwatch() { reset(); } void add(const stopwatch &s) { m_elapsed += s.m_elapsed; } void reset() { m_elapsed = duration_t::zero(); } void start() { if (!m_running) { m_start = get(); m_running = true; } } void stop() { if (m_running) { m_elapsed += get() - m_start; m_running = false; } } double get_seconds() const { if (m_running) { const_cast(this)->stop(); const_cast(this)->start(); } return std::chrono::duration_cast(m_elapsed).count() / 1000.0; } double get_current_seconds() const { return get_seconds(); } }; struct scoped_watch { stopwatch &m_sw; scoped_watch (stopwatch &sw, bool reset=false): m_sw(sw) { if (reset) m_sw.reset(); m_sw.start(); } ~scoped_watch() { m_sw.stop (); } }; #endif z3-z3-4.8.7/src/util/str_hashtable.h000066400000000000000000000013521356505360400171700ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: str_hashtable.h Abstract: String hashtable. It uses Jenkin's hash function and the optimized hashtable module. Author: Leonardo de Moura (leonardo) 2006-09-13. Revision History: --*/ #ifndef STR_HASHTABLE_H_ #define STR_HASHTABLE_H_ #include #include "util/hashtable.h" #include "util/hash.h" 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; #endif /* STR_HASHTABLE_H_ */ z3-z3-4.8.7/src/util/stream_buffer.h000066400000000000000000000013641356505360400171740ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: stream_buffer.h Abstract: Simple stream buffer interface. In the future we should be able to read different kinds of stream (e.g., compressed files used in the SAT competitions). Author: Leonardo de Moura (leonardo) 2006-10-02. Revision History: --*/ #ifndef STREAM_BUFFER_H_ #define STREAM_BUFFER_H_ #include class stream_buffer { std::istream & m_stream; int m_val; public: stream_buffer(std::istream & s): m_stream(s) { m_val = m_stream.get(); } int operator *() const { return m_val; } void operator ++() { m_val = m_stream.get(); } }; #endif /* STREAM_BUFFER_H_ */ z3-z3-4.8.7/src/util/string_buffer.h000066400000000000000000000072571356505360400172160ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: string_buffer.h Abstract: Simple string buffer Author: Leonardo de Moura (leonardo) 2006-10-14. Revision History: --*/ #ifndef STRING_BUFFER_H_ #define STRING_BUFFER_H_ #include #include #include #include "util/util.h" #include "util/memory_manager.h" // This string buffer will not use the heap if the data consumes less than INITIAL_SIZE bytes. template class string_buffer { char m_initial_buffer[INITIAL_SIZE]; char * m_buffer; size_t m_pos; size_t m_capacity; void expand() { size_t new_capacity = m_capacity << 1; char * new_buffer = alloc_svect(char, new_capacity); memcpy(new_buffer, m_buffer, m_pos); if (m_capacity > INITIAL_SIZE) { dealloc_svect(m_buffer); } m_capacity = new_capacity; m_buffer = new_buffer; } static const unsigned c_buffer_size = 24; public: string_buffer(): m_buffer(m_initial_buffer), m_pos(0), m_capacity(INITIAL_SIZE) { } ~string_buffer() { if (m_capacity > INITIAL_SIZE) { dealloc_svect(m_buffer); } } void reset() { m_pos = 0; } void append(char c) { if (m_pos >= m_capacity) { expand(); } m_buffer[m_pos] = c; m_pos++; } void append(const char * str) { size_t len = strlen(str); size_t new_pos = m_pos + len; while (new_pos > m_capacity) { expand(); } memcpy(m_buffer + m_pos, str, len); m_pos += len; } void append(int n) { char buffer[c_buffer_size]; SPRINTF_D(buffer, n); append(buffer); } void append(unsigned n) { char buffer[c_buffer_size]; SPRINTF_U(buffer, n); append(buffer); } void append(long n) { char buffer[c_buffer_size]; #ifdef _WINDOWS sprintf_s(buffer, Z3_ARRAYSIZE(buffer), "%ld", n); #else sprintf(buffer, "%ld", n); #endif append(buffer); } void append(bool b) { if (b) { append("true"); } else { append("false"); } } unsigned size() const { return m_pos; } bool empty() const { return m_pos == 0; } const char * c_str() const { if (m_pos >= m_capacity) { const_cast(this)->expand(); } const_cast(this)->m_buffer[m_pos] = 0; return m_buffer; } }; template inline string_buffer & operator<<(string_buffer & buffer, const char * str) { buffer.append(str); return buffer; } template inline string_buffer & operator<<(string_buffer & buffer, char c) { buffer.append(c); return buffer; } template inline string_buffer & operator<<(string_buffer & buffer, int i) { buffer.append(i); return buffer; } template inline string_buffer & operator<<(string_buffer & buffer, unsigned i) { buffer.append(i); return buffer; } template inline string_buffer & operator<<(string_buffer & buffer, bool b) { buffer.append(b); return buffer; } template inline string_buffer & operator<<(string_buffer & buffer, long l) { buffer.append(l); return buffer; } template inline string_buffer & operator<<(string_buffer & buffer1, const string_buffer & buffer2) { buffer1.append(buffer2.c_str()); return buffer1; } #endif z3-z3-4.8.7/src/util/symbol.cpp000066400000000000000000000067571356505360400162230ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: symbol.cpp Abstract: Lisp-like symbols. Author: Leonardo de Moura (leonardo) 2006-09-11. Revision History: --*/ #include "util/symbol.h" #include "util/mutex.h" #include "util/str_hashtable.h" #include "util/region.h" #include "util/string_buffer.h" #include static DECLARE_MUTEX(g_symbol_lock); symbol symbol::m_dummy(TAG(void*, nullptr, 2)); const symbol symbol::null; /** \brief Symbol table manager. It stores the symbol strings created at runtime. */ class internal_symbol_table { region m_region; //!< Region used to store symbol strings. str_hashtable m_table; //!< Table of created symbol strings. public: char const * get_str(char const * d) { const char * result; lock_guard lock(*g_symbol_lock); str_hashtable::entry * 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(mem, d, l+1); // update the entry with the new ptr. e->set_data(result); } else { result = e->get_data(); } SASSERT(m_table.contains(result)); return result; } }; static internal_symbol_table* g_symbol_table = nullptr; void initialize_symbols() { if (!g_symbol_table) { ALLOC_MUTEX(g_symbol_lock); g_symbol_table = alloc(internal_symbol_table); } } void finalize_symbols() { DEALLOC_MUTEX(g_symbol_lock); dealloc(g_symbol_table); g_symbol_table = nullptr; } symbol::symbol(char const * d) { if (d == nullptr) m_data = nullptr; else m_data = g_symbol_table->get_str(d); } symbol & symbol::operator=(char const * d) { m_data = g_symbol_table->get_str(d); return *this; } std::string symbol::str() const { SASSERT(!is_marked()); if (GET_TAG(m_data) == 0) { if (m_data) return m_data; else return ""; } else { string_buffer<128> buffer; buffer << "k!" << UNBOXINT(m_data); return buffer.c_str(); } } bool symbol::contains(char ch) const { SASSERT(!is_marked()); if (GET_TAG(m_data) == 0) { return strchr(m_data, ch) != nullptr; } else { return false; } } unsigned symbol::size() const { SASSERT(!is_marked()); if (GET_TAG(m_data) == 0) { return static_cast(strlen(m_data)); } else { unsigned v = UNBOXINT(m_data); unsigned sz = 4; v = v >> 1; while (v > 0) { sz++; v = v >> 1; } return sz; } } bool lt(symbol const & s1, symbol const & s2) { if (s1 == s2) return false; if (s1.is_numerical()) { if (!s2.is_numerical()) return true; // numeral symbols are smaller than non-numerical ones. return s1.get_num() < s2.get_num(); } if (s2.is_numerical()) { SASSERT(!s1.is_numerical()); return false; } if (!s1.bare_str()) return true; if (!s2.bare_str()) return false; SASSERT(!s1.is_numerical() && !s2.is_numerical()); auto cmp = strcmp(s1.bare_str(), s2.bare_str()); SASSERT(cmp != 0); return cmp < 0; } z3-z3-4.8.7/src/util/symbol.h000066400000000000000000000104721356505360400156550ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: symbol.h Abstract: Lisp-like symbols. Author: Leonardo de Moura (leonardo) 2006-09-11. Revision History: --*/ #ifndef SYMBOL_H_ #define SYMBOL_H_ #include #include #include "util/util.h" #include "util/tptr.h" #include "util/string_buffer.h" template class symbol_table; class symbol { char const * m_data; template friend class symbol_table; explicit symbol(void const * data): m_data(reinterpret_cast(data)) { } bool is_marked() const { return GET_TAG(m_data) > 1; } static symbol mark(symbol s) { SASSERT(!s.is_marked()); return symbol(TAG(void *, UNTAG(void *, s.m_data), GET_TAG(s.m_data) + 2)); } static symbol unmark(symbol s) { SASSERT(s.is_marked()); return symbol(TAG(void *, UNTAG(void *, s.m_data), GET_TAG(s.m_data) - 2)); } static symbol m_dummy; public: symbol(): m_data(nullptr) { } explicit symbol(char const * d); explicit symbol(unsigned idx): m_data(BOXTAGINT(char const *, idx, 1)) { #if !defined(__LP64__) && !defined(_WIN64) SASSERT(idx < (SIZE_MAX >> PTR_ALIGNMENT)); #endif } static symbol dummy() { return m_dummy; } static const symbol null; symbol & operator=(char const * d); friend bool operator==(symbol const & s1, symbol const & s2) { return s1.m_data == s2.m_data; } friend bool operator!=(symbol const & s1, symbol const & s2) { return s1.m_data != s2.m_data; } bool is_numerical() const { return GET_TAG(m_data) == 1; } unsigned int get_num() const { SASSERT(is_numerical()); return UNBOXINT(m_data); } std::string str() const; friend bool operator==(symbol const & s1, char const * s2) { if (s1.m_data == nullptr && s2 == nullptr) return true; if (s1.m_data == nullptr || s2 == nullptr) return false; if (!s1.is_numerical()) return strcmp(s1.bare_str(), s2) == 0; return s1.str() == s2; } friend bool operator!=(symbol const & s1, char const * s2) { return !operator==(s1, s2); } void const * c_ptr() const { return m_data; } // Low level function. // It is the inverse of c_ptr(). // It was made public to simplify the implementation of the C API. static symbol mk_symbol_from_c_ptr(void const * ptr) { return symbol(ptr); } unsigned hash() const { if (m_data == nullptr) return 0x9e3779d9; else if (is_numerical()) return get_num(); else return static_cast(reinterpret_cast(m_data)[-1]); } bool contains(char c) const; unsigned size() const; char const * bare_str() const { SASSERT(!is_numerical()); return m_data; } friend std::ostream & operator<<(std::ostream & target, symbol s) { SASSERT(!s.is_marked()); if (GET_TAG(s.m_data) == 0) { if (s.m_data) { target << s.m_data; } else { target << "null"; } } else { target << "k!" << UNBOXINT(s.m_data); } return target; } template friend string_buffer & operator<<(string_buffer & target, symbol s) { SASSERT(!s.is_marked()); if (GET_TAG(s.m_data) == 0) { if (s.m_data) { target << s.m_data; } else { target << "null"; } } else { target << "k!" << UNBOXINT(s.m_data); } return target; } }; struct symbol_hash_proc { unsigned operator()(symbol const & s) const { return s.hash(); } }; struct symbol_eq_proc { bool operator()(symbol const & s1, symbol const & s2) const { return s1 == s2; } }; void initialize_symbols(); void finalize_symbols(); /* ADD_INITIALIZER('initialize_symbols();') ADD_FINALIZER('finalize_symbols();') */ // total order on symbols... I did not overloaded '<' to avoid misunderstandings. // numerical symbols are smaller than non numerical symbols. // two numerical symbols are compared using get_num. // two non-numerical symbols are compared using string comparison. bool lt(symbol const & s1, symbol const & s2); #endif /* SYMBOL_H_ */ z3-z3-4.8.7/src/util/symbol_table.h000066400000000000000000000113211356505360400170160ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: symbol_table.h Abstract: Symbol table for parsing. Author: Leonardo de Moura (leonardo) 2006-09-19. Revision History: --*/ #ifndef SYMBOL_TABLE_H_ #define SYMBOL_TABLE_H_ #include "util/vector.h" #include "util/hashtable.h" #include "util/hash.h" #include "util/symbol.h" /** \brief Quick & Dirty symbol table. */ template class symbol_table { struct key_data { symbol m_key; T m_data; key_data() { } explicit key_data(symbol k): m_key(k) { } key_data(symbol k, const T & d): m_key(k), m_data(d) { } }; struct key_data_hash_proc { unsigned operator()(const key_data & k) const { return k.m_key.hash(); } }; struct key_data_eq_proc { bool operator()(const key_data & k1, const key_data & k2) const { return k1.m_key == k2.m_key; } }; struct hash_entry { typedef key_data data; key_data m_data; hash_entry() { SASSERT(m_data.m_key == symbol::null); } unsigned get_hash() const { return m_data.m_key.hash(); } bool is_free() const { return m_data.m_key == symbol::null; } bool is_deleted() const { return m_data.m_key == symbol::dummy(); } bool is_used() const { return !is_free() && !is_deleted(); } key_data & get_data() { return m_data; } const key_data & get_data() const { return m_data; } void set_data(const key_data & d) { m_data = d; } static void set_hash(unsigned h) { } void mark_as_deleted() { m_data.m_key = symbol::dummy(); } void mark_as_free() { m_data.m_key = symbol::null; } }; typedef core_hashtable sym_table; typedef vector trail_stack; sym_table m_sym_table; trail_stack m_trail_stack; int_vector m_trail_lims; public: void reset() { m_sym_table.reset(); m_trail_stack.reset(); m_trail_lims.reset(); } bool find(symbol key, T & result) const { key_data dummy(key); hash_entry * e = m_sym_table.find_core(dummy); if (e == nullptr) { return false; } result = e->get_data().m_data; return true; } bool contains(symbol key) const { return m_sym_table.contains(key_data(key)); } unsigned get_scope_level() const { return m_trail_lims.size(); } void insert(symbol key, const T & data) { if (get_scope_level() > 0) { key_data dummy(key); hash_entry * e = m_sym_table.find_core(dummy); if (e != nullptr) { m_trail_stack.push_back(e->m_data); e->m_data.m_data = data; } else { m_trail_stack.push_back(dummy); key_data & new_entry = m_trail_stack.back(); new_entry.m_key = symbol::mark(new_entry.m_key); m_sym_table.insert(key_data(key, data)); } } else { m_sym_table.insert(key_data(key, data)); } } void begin_scope() { m_trail_lims.push_back(m_trail_stack.size()); } void end_scope() { unsigned old_size = m_trail_lims.back(); m_trail_lims.pop_back(); unsigned curr_size = m_trail_stack.size(); SASSERT(old_size <= curr_size); for (unsigned i = old_size; i < curr_size; i++) { key_data & curr_entry = m_trail_stack.back(); symbol key = curr_entry.m_key; if (key.is_marked()) { curr_entry.m_key = symbol::unmark(key); m_sym_table.erase(curr_entry); } else { m_sym_table.insert(curr_entry); } m_trail_stack.pop_back(); } SASSERT(m_trail_stack.size() == old_size); } void append(symbol_table const& other) { for (auto const& kv : other.m_sym_table) { insert(kv.m_key, kv.m_data); } } void get_range(vector& range) const { for (auto kv : m_sym_table) { range.push_back(kv.m_data); } } void get_dom(svector& dom) const { for (auto kv : m_sym_table) { dom.push_back(kv.m_key); } } }; #endif /* SYMBOL_TABLE_H_ */ z3-z3-4.8.7/src/util/timeit.cpp000066400000000000000000000025501356505360400161740ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: timeit.h Abstract: Support for timers. Author: Nikolaj Bjorner (nbjorner) 2006-09-22 Revision History: --*/ #include #include "util/timeit.h" #include "util/memory_manager.h" #include "util/stopwatch.h" #include struct timeit::imp { stopwatch m_watch; char const * m_msg; std::ostream & m_out; double m_start_memory; imp(char const * msg, std::ostream & out): m_msg(msg), m_out(out), m_start_memory(static_cast(memory::get_allocation_size())/static_cast(1024*1024)) { m_watch.start(); } ~imp() { m_watch.stop(); double end_memory = static_cast(memory::get_allocation_size())/static_cast(1024*1024); m_out << "(" << m_msg << " :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; } }; timeit::timeit(bool enable, char const * msg, std::ostream & out) { if (enable) m_imp = alloc(imp, msg, out); else m_imp = nullptr; } timeit::~timeit() { if (m_imp) dealloc(m_imp); } z3-z3-4.8.7/src/util/timeit.h000066400000000000000000000007541356505360400156450ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: timeit.h Abstract: Support for timers. Author: Nikolaj Bjorner (nbjorner) 2006-09-22 Revision History: Leonardo de Moura (leonardo) 2011-04-27 Rewrote using stopwatches, added support for tracking memory --*/ #ifndef TIMEIT_H_ #define TIMEIT_H_ class timeit { struct imp; imp * m_imp; public: timeit(bool enable, char const * msg, std::ostream & out = std::cerr); ~timeit(); }; #endif z3-z3-4.8.7/src/util/timeout.cpp000066400000000000000000000021201356505360400163600ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: timeout.cpp Abstract: Timeout support Author: Leonardo de Moura (leonardo) 2006-10-02. Revision History: Christoph (cwinter) 2012-02-14: Switch to scoped_timer for timeout support on all platforms. --*/ #include #include "util/util.h" #include "util/timeout.h" #include "util/error_codes.h" #include "util/event_handler.h" #include "util/scoped_timer.h" static scoped_timer * g_timeout = nullptr; static void (* g_on_timeout)() = nullptr; namespace { class g_timeout_eh : public event_handler { public: void operator()(event_handler_caller_t caller_id) override { std::cout << "timeout\n"; m_caller_id = caller_id; if (g_on_timeout) g_on_timeout(); throw z3_error(ERR_TIMEOUT); } }; } static g_timeout_eh 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)()) { g_on_timeout = proc; } z3-z3-4.8.7/src/util/timeout.h000066400000000000000000000005041356505360400160310ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: timeout.h Abstract: Timeout support Author: Leonardo de Moura (leonardo) 2006-10-02. Revision History: --*/ #ifndef TIMEOUT_H_ #define TIMEOUT_H_ void register_on_timeout_proc(void (*proc)()); void set_timeout(long ms); #endif // _TIMEOUT_H_ z3-z3-4.8.7/src/util/timer.h000066400000000000000000000013341356505360400154650ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: timer.h Abstract: Author: Leonardo de Moura (leonardo) 2009-01-06. Revision History: --*/ #ifndef TIMER_H_ #define TIMER_H_ #include "util/stopwatch.h" /** \brief Wrapper for the stopwatch class. */ class timer { stopwatch m_watch; public: 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_ */ z3-z3-4.8.7/src/util/top_sort.h000066400000000000000000000056251356505360400162250ustar00rootroot00000000000000/*++ Copyright (c) 2018 Microsoft Corporation Module Name: top_sort.h Abstract: Topological sort over objects Author: Nikolaj Bjorner (nbjorner) 2018-02-14 Revision History: --*/ #ifndef TOP_SORT_H_ #define TOP_SORT_H_ #include "util/obj_hashtable.h" #include "util/vector.h" #include #include #include #include "util/memory_manager.h" template class top_sort { typedef obj_hashtable T_set; obj_map m_partition_id; obj_map m_dfs_num; ptr_vector m_top_sorted; ptr_vector m_stack_S; ptr_vector m_stack_P; unsigned m_next_preorder; obj_map m_deps; void traverse(T* f) { unsigned p_id = 0; if (m_dfs_num.find(f, p_id)) { if (!m_partition_id.contains(f)) { while (!m_stack_P.empty() && m_partition_id.contains(m_stack_P.back()) && m_partition_id[m_stack_P.back()] > p_id) { m_stack_P.pop_back(); } } } else if (!m_deps.contains(f)) { return; } else { m_dfs_num.insert(f, m_next_preorder++); m_stack_S.push_back(f); m_stack_P.push_back(f); for (T* g : *m_deps[f]) { traverse(g); } if (f == m_stack_P.back()) { p_id = m_top_sorted.size(); T* s_f; do { s_f = m_stack_S.back(); m_stack_S.pop_back(); m_top_sorted.push_back(s_f); m_partition_id.insert(s_f, p_id); } while (s_f != f); m_stack_P.pop_back(); } } } public: virtual ~top_sort() { for (auto & kv : m_deps) dealloc(kv.m_value); } void topological_sort() { m_next_preorder = 0; m_partition_id.reset(); m_top_sorted.reset(); for (auto & kv : m_deps) { traverse(kv.m_key); } SASSERT(m_stack_S.empty()); SASSERT(m_stack_P.empty()); m_dfs_num.reset(); } void insert(T* t, T_set* s) { m_deps.insert(t, s); } ptr_vector const& top_sorted() { return m_top_sorted; } obj_map const& partition_ids() const { return m_partition_id; } unsigned partition_id(T* t) const { return m_partition_id[t]; } bool is_singleton_partition(T* f) const { unsigned pid = m_partition_id[f]; return f == m_top_sorted[pid] && (pid == 0 || m_partition_id[m_top_sorted[pid-1]] != pid) && (pid + 1 == m_top_sorted.size() || m_partition_id[m_top_sorted[pid+1]] != pid); } obj_map const& deps() const { return m_deps; } }; #endif /* TOP_SORT_H_ */ z3-z3-4.8.7/src/util/total_order.h000066400000000000000000000244151356505360400166700ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: total_order.h Abstract: Author: Leonardo de Moura (leonardo) 2010-07-01. Revision History: --*/ #ifndef TOTAL_ORDER_H_ #define TOTAL_ORDER_H_ #include "util/util.h" #include "util/small_object_allocator.h" #include "util/map.h" #include "util/uint_map.h" #include "util/trace.h" /** \brief An object for maintaining a total-order on sets of T values. \c Map should be a class implementing the map API for T to void *. */ template class total_order { struct cell { cell * m_next; cell * m_prev; uint64_t m_val; T m_data; }; small_object_allocator * m_allocator; bool m_own_allocator; Map m_map; cell m_base; unsigned m_size; cell * base() const { return const_cast(&m_base); } void init_base() { m_base.m_next = &m_base; m_base.m_prev = &m_base; m_base.m_val = 0; } uint64_t v(cell * a) const { return a->m_val; } uint64_t vb(cell * a) const { return v(a) - v(base()); } uint64_t vbn(cell * a) const { return a->m_next == base() ? UINT64_MAX : vb(a->m_next); } cell * mk_cell(T const & a) { SASSERT(!m_map.contains(a)); cell * c = reinterpret_cast(m_allocator->allocate(sizeof(cell))); m_map.insert(a, c); c->m_data = a; m_size++; return c; } void del_cell(cell * c) { #ifdef Z3DEBUG T d = c->m_data; #endif m_map.erase(c->m_data); m_allocator->deallocate(sizeof(cell), c); m_size--; SASSERT(!m_map.contains(d)); } cell * to_cell(T const & a) const { void * r = m_map.find(a); return reinterpret_cast(r); } void _insert_after(cell * a, cell * b) { uint64_t vb_a = vb(a); uint64_t vbn_a = vbn(a); SASSERT(vb_a < vbn_a); if (vbn_a < 2 || (vb_a > vbn_a - 2)) { TRACE("total_order", tout << "relabeling...\n"; tout << "\n";); uint64_t v0 = v(a); unsigned sz = size(); uint64_t ideal_gap = UINT64_MAX / sz; uint64_t goal_gap = ideal_gap / 32; cell * c = a->m_next->m_next; unsigned j = 2; uint64_t curr_gap = (v(c) - v0) / j; while (j < sz && curr_gap < goal_gap) { j++; c = c->m_next; curr_gap = (v(c) - v0) / j; } TRACE("total_order", tout << "j: " << j << " curr_gap: " << curr_gap << " sz: " << sz << "\n";); if (j == sz) curr_gap = ideal_gap; c = a->m_next; uint64_t inc = curr_gap; for (unsigned i = 0; i < j; i++) { c->m_val = v0 + inc; c = c->m_next; inc += curr_gap; } CASSERT("total_order", check_invariant()); vb_a = vb(a); vbn_a = vbn(a); } SASSERT(vb_a <= vbn_a - 2); uint64_t vb_b = vb_a + ((vbn_a - vb_a)/2); SASSERT(vb_b > vb_a); SASSERT(vb_b < vbn_a); b->m_val = vb_b + v(base()); b->m_next = a->m_next; a->m_next->m_prev = b; b->m_prev = a; a->m_next = b; SASSERT(vb(a) < vb(b)); CASSERT("total_order", check_invariant()); } void insert_core(cell * a) { _insert_after(base(), a); } void remove_cell(cell * a) { SASSERT(a != base()); cell * p = a->m_prev; cell * n = a->m_next; p->m_next = n; n->m_prev = p; } void move_after(cell * a, cell * b) { if (a->m_next == b) return; remove_cell(b); _insert_after(a, b); } void move_beginning(cell * b) { if (base()->m_next == b) return; // already in the beginning remove_cell(b); insert_core(b); } void erase(cell * a) { remove_cell(a); del_cell(a); } public: total_order(): m_allocator(alloc(small_object_allocator)), m_own_allocator(true), m_size(0) { init_base(); } total_order(Map const & m): m_allocator(alloc(small_object_allocator)), m_own_allocator(true), m_map(m), m_size(0) { init_base(); } total_order(small_object_allocator * allocator): m_allocator(allocator), m_own_allocator(false), m_size(0) { init_base(); } total_order(small_object_allocator * allocator, Map const & m): m_allocator(allocator), m_own_allocator(false), m_map(m), m_size(0) { init_base(); } ~total_order() { cell * curr = base()->m_next; while (curr != base()) { cell * c = curr; curr = curr->m_next; del_cell(c); } if (m_own_allocator) dealloc(m_allocator); } /** \brief Return true if \c a is in the total order. */ bool contains(T const & a) const { return m_map.contains(a); } /** \brief Insert \c a in the beginning of the total order. \pre \c a must not be an element of the total order. */ void insert(T const & a) { SASSERT(!contains(a)); insert_core(mk_cell(a)); } /** \brief Insert \c b after \c a in the total order. \pre \c a is an element of the total order. \pre \c b must not be an element of the total order. */ void insert_after(T const & a, T const & b) { SASSERT(contains(a)); SASSERT(!contains(b)); _insert_after(to_cell(a), mk_cell(b)); SASSERT(lt(a, b)); } /** \brief Move \c a to the beginning of the total order. \pre \c a is an element of the total order. */ void move_beginning(T const & a) { SASSERT(contains(a)); move_beginning(to_cell(a)); } /** \brief Move \b after \c a in the total order. \pre \c a is an element of the total order. \pre \c b is an element of the total order. \pre \c a must be different from \c b. */ void move_after(T const & a, T const & b) { SASSERT(contains(a)); SASSERT(contains(b)); move_after(to_cell(a), to_cell(b)); SASSERT(lt(a, b)); } /** \brief Remove \c a from the total order. \pre \c a is an element of the total order. */ void erase(T const & a) { SASSERT(contains(a)); erase(to_cell(a)); } /** \brief Return true if \c a is less than \c b in the total order. */ bool lt(T const & a, T const & b) const { SASSERT(contains(a)); SASSERT(contains(b)); return vb(to_cell(a)) < vb(to_cell(b)); } /** \brief Return true if \c a is greater than \c b in the total order. */ bool gt(T const & a, T const & b) const { SASSERT(contains(a)); SASSERT(contains(b)); return vb(to_cell(a)) > vb(to_cell(b)); } /** \brief Return true if the total order is empty. */ bool empty() const { return base()->m_next == base(); } /** \brief Return the number of elements in the total order. */ unsigned size() const { return m_size; } /** \brief Return the first element of the total order. */ T const & first() const { SASSERT(!empty()); return base()->m_next->m_data; } /** \brief Return true if \c a has a successor in the total order. \pre \c a is an element of the total order. */ bool has_next(T const & a) const { SASSERT(contains(a)); return to_cell(a)->m_next != base(); } /** \brief Return the successor of \c a in the total order. \pre \c a is an element of the total order. \pre has_next(a) */ T const & next(T const & a) const { SASSERT(contains(a)); SASSERT(has_next(a)); return to_cell(a)->m_next->m_data; } /** \brief Return true if \c a has a predecessor in the total order. \pre \c a is an element of the total order. */ bool has_pred(T const & a) const { SASSERT(contains(a)); return to_cell(a)->m_prev != base(); } /** \brief Return the predecessor of \c a in the total order. \pre \c a is an element of the total order. \pre has_pred(a) */ T const & pred(T const & a) const { SASSERT(has_pred(a)); return to_cell(a)->m_prev->m_data; } /** \brief Display the elements of the total order in increasing order. \remark For debugging purposes. */ void display(std::ostream & out) const { cell * curr = base()->m_next; bool first = true; while (curr != base()) { if (first) first = false; else out << " "; out << curr->m_data; curr = curr->m_next; } } void display_detail(std::ostream & out) const { cell * curr = base()->m_next; bool first = true; while (curr != base()) { if (first) first = false; else out << " "; out << curr->m_data << ":" << curr->m_val; curr = curr->m_next; } } #ifdef Z3DEBUG bool check_invariant() const { cell * curr = base()->m_next; while (curr != base()) { SASSERT(curr->m_next == base() || vb(curr) < vb(curr->m_next)); curr = curr->m_next; } return true; } #endif }; typedef total_order > int_total_order; /** \brief A total order that uses vectors to implement a mapping from unsigned to void *. */ typedef total_order > uint_total_order; template std::ostream & operator<<(std::ostream & out, total_order const & to) { to.display(out); return out; } #endif /* TOTAL_ORDER_H_ */ z3-z3-4.8.7/src/util/tptr.h000066400000000000000000000023771356505360400153460ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: tptr.h Abstract: Support for tagged pointers and other low level pointer manipulation macros. Author: Leonardo de Moura (leonardo) 2006-09-13. Revision History: --*/ #ifndef TPTR_H_ #define TPTR_H_ #include "util/machine.h" #define TAG_SHIFT PTR_ALIGNMENT #define ALIGNMENT_VALUE (1 << PTR_ALIGNMENT) #define TAG_MASK (ALIGNMENT_VALUE - 1) #define PTR_MASK (~TAG_MASK) #define ALIGN(T, PTR) reinterpret_cast(((reinterpret_cast(PTR) >> PTR_ALIGNMENT) + \ static_cast((reinterpret_cast(PTR) & TAG_MASK) != 0)) << PTR_ALIGNMENT) #define UNTAG(T, PTR) reinterpret_cast(reinterpret_cast(PTR) & PTR_MASK) #define TAG(T, PTR, TAG_VAL) reinterpret_cast(reinterpret_cast(PTR) | static_cast(TAG_VAL)) #define GET_TAG(PTR) (reinterpret_cast(PTR) & TAG_MASK) #define BOXINT(T, VAL) reinterpret_cast(static_cast(VAL) << PTR_ALIGNMENT) #define BOXTAGINT(T, VAL, TAG_VAL) reinterpret_cast((static_cast(VAL) << PTR_ALIGNMENT) | static_cast(TAG_VAL)) #define UNBOXINT(PTR) static_cast(reinterpret_cast(PTR) >> PTR_ALIGNMENT) #endif /* TPTR_H_ */ z3-z3-4.8.7/src/util/trace.cpp000066400000000000000000000022271356505360400160000ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: trace.cpp Abstract: Trace message support. Author: Leonardo de Moura (leonardo) 2006-09-13. Revision History: --*/ #include "util/trace.h" #include "util/str_hashtable.h" #ifdef _TRACE std::ofstream tout(".z3-trace"); static bool g_enable_all_trace_tags = false; static str_hashtable* g_enabled_trace_tags = nullptr; static str_hashtable& get_enabled_trace_tags() { if (!g_enabled_trace_tags) { g_enabled_trace_tags = alloc(str_hashtable); } return *g_enabled_trace_tags; } void finalize_trace() { dealloc(g_enabled_trace_tags); g_enabled_trace_tags = nullptr; } void enable_trace(const char * tag) { get_enabled_trace_tags().insert(tag); } void enable_all_trace(bool flag) { g_enable_all_trace_tags = flag; } void disable_trace(const char * 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(tag)); } void close_trace() { tout.close(); } void open_trace() { tout.open(".z3-trace"); } #endif z3-z3-4.8.7/src/util/trace.h000066400000000000000000000030601356505360400154410ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: trace.h Abstract: Trace message support. Author: Leonardo de Moura (leonardo) 2006-09-13. Revision History: --*/ #pragma once #include #ifdef _TRACE extern std::ofstream tout; #define TRACE_CODE(CODE) { CODE } ((void) 0 ) void enable_trace(const char * tag); void enable_all_trace(bool flag); void disable_trace(const char * tag); bool is_trace_enabled(const char * tag); void close_trace(); void open_trace(); void finalize_trace(); /* ADD_FINALIZER('finalize_trace();') */ #else #define TRACE_CODE(CODE) ((void) 0) static inline void enable_trace(const char * tag) {} static inline void enable_all_trace(bool flag) {} static inline void disable_trace(const char * tag) {} static inline bool is_trace_enabled(const char * tag) { return false; } static inline void close_trace() {} static inline void open_trace() {} static inline void finalize_trace() {} #endif #define TRACE(TAG, CODE) TRACE_CODE(if (is_trace_enabled(TAG)) { tout << "-------- [" << TAG << "] " << __FUNCTION__ << " " << __FILE__ << ":" << __LINE__ << " ---------\n"; CODE tout << "------------------------------------------------\n"; tout.flush(); }) #define STRACE(TAG, CODE) TRACE_CODE(if (is_trace_enabled(TAG)) { CODE tout.flush(); }) #define CTRACE(TAG, COND, CODE) TRACE_CODE(if (is_trace_enabled(TAG) && (COND)) { tout << "-------- [" << TAG << "] " << __FUNCTION__ << " " << __FILE__ << ":" << __LINE__ << " ---------\n"; CODE tout << "------------------------------------------------\n"; tout.flush(); }) z3-z3-4.8.7/src/util/trail.h000066400000000000000000000213501356505360400154600ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: trail.h Abstract: Author: Leonardo de Moura (leonardo) 2008-06-02. Revision History: --*/ #ifndef TRAIL_H_ #define TRAIL_H_ #include "util/obj_hashtable.h" #include "util/region.h" #include "util/obj_ref.h" #include "util/vector.h" template class trail { public: virtual ~trail() { } virtual void undo(Ctx & ctx) = 0; }; template class value_trail : public trail { T & m_value; T m_old_value; public: value_trail(T & value): m_value(value), m_old_value(value) { } value_trail(T & value, T const& new_value): m_value(value), m_old_value(value) { m_value = new_value; } ~value_trail() override { } void undo(Ctx & ctx) override { m_value = m_old_value; } }; template class reset_flag_trail : public trail { bool & m_value; public: reset_flag_trail(bool & value): m_value(value) { } ~reset_flag_trail() override { } void undo(Ctx & ctx) override { m_value = false; } }; template class set_ptr_trail : public trail { T * & m_ptr; public: set_ptr_trail(T * & ptr): m_ptr(ptr) { SASSERT(m_ptr == 0); } void undo(Ctx & ctx) override { m_ptr = nullptr; } }; template class restore_size_trail : public trail { vector & m_vector; unsigned m_old_size; public: restore_size_trail(vector & v, unsigned sz): m_vector(v), m_old_size(sz) { } restore_size_trail(vector & v): m_vector(v), m_old_size(v.size()) { } ~restore_size_trail() override { } void undo(Ctx & ctx) override { m_vector.shrink(m_old_size); } }; template class vector_value_trail : public trail { vector & m_vector; unsigned m_idx; T m_old_value; public: vector_value_trail(vector & v, unsigned idx): m_vector(v), m_idx(idx), m_old_value(v[idx]) { } ~vector_value_trail() override { } void undo(Ctx & ctx) override { m_vector[m_idx] = m_old_value; } }; template class insert_obj_map : public trail { obj_map& m_map; D* m_obj; public: insert_obj_map(obj_map& t, D* o) : m_map(t), m_obj(o) {} ~insert_obj_map() override {} void undo(Ctx & ctx) override { m_map.remove(m_obj); } }; template class insert_map : public trail { M& m_map; D m_obj; public: insert_map(M& t, D o) : m_map(t), m_obj(o) {} ~insert_map() override {} void undo(Ctx & ctx) override { m_map.remove(m_obj); } }; template class insert_ref_map : public trail { Mgr& m; M& m_map; D m_obj; public: insert_ref_map(Mgr& m, M& t, D o) : m(m), m_map(t), m_obj(o) {} virtual ~insert_ref_map() {} virtual void undo(Ctx & ctx) { m_map.remove(m_obj); m.dec_ref(m_obj); } }; template class push_back_vector : public trail { V & m_vector; public: push_back_vector(V & v): m_vector(v) { } void undo(Ctx & ctx) override { m_vector.pop_back(); } }; template class set_vector_idx_trail : public trail { ptr_vector & m_vector; unsigned m_idx; public: set_vector_idx_trail(ptr_vector & v, unsigned idx): m_vector(v), m_idx(idx) { } ~set_vector_idx_trail() override { } void undo(Ctx & ctx) override { m_vector[m_idx] = 0; } }; template class pop_back_trail : public trail { vector & m_vector; T m_value; public: pop_back_trail(vector & v): m_vector(v), m_value(m_vector.back()) { } virtual void undo(Ctx & ctx) { m_vector.push_back(m_value); } }; template class pop_back2_trail : public trail { vector & m_vector; typedef vector, true> vector_t; unsigned m_index; T m_value; public: pop_back2_trail(vector & v, unsigned index): m_vector(v), m_index(index), m_value(m_vector[index].back()) { } virtual void undo(Ctx & ctx) { m_vector[m_index].push_back(m_value); } }; template class push_back_trail : public trail { vector & m_vector; public: push_back_trail(vector & v): m_vector(v) { } void undo(Ctx & ctx) override { m_vector.pop_back(); } }; template class push_back2_trail : public trail { typedef vector, true> vector_t; vector_t & m_vector; unsigned m_index; public: push_back2_trail(vector_t & v, unsigned index) : m_vector(v), m_index(index) { } virtual void undo(Ctx & ctx) { m_vector[m_index].pop_back(); } }; template class set_bitvector_trail : public trail { svector & m_vector; unsigned m_idx; public: set_bitvector_trail(svector & v, unsigned idx): m_vector(v), m_idx(idx) { SASSERT(m_vector[m_idx] == false); m_vector[m_idx] = true; } void undo(Ctx & ctx) override { m_vector[m_idx] = false; } }; template class new_obj_trail : public trail { T * m_obj; public: new_obj_trail(T * obj): m_obj(obj) { } void undo(Ctx & ctx) override { dealloc(m_obj); } }; template class obj_ref_trail : public trail { obj_ref m_obj; public: obj_ref_trail(obj_ref& obj): m_obj(obj) { } virtual void undo(Ctx & ctx) { m_obj.reset(); } }; template class insert_obj_trail : public trail { obj_hashtable& m_table; T* m_obj; public: insert_obj_trail(obj_hashtable& t, T* o) : m_table(t), m_obj(o) {} ~insert_obj_trail() override {} void undo(Ctx & ctx) override { m_table.remove(m_obj); } }; template class remove_obj_trail : public trail { obj_hashtable& m_table; T* m_obj; public: remove_obj_trail(obj_hashtable& t, T* o) : m_table(t), m_obj(o) {} virtual ~remove_obj_trail() {} virtual void undo(Ctx & ctx) { m_table.insert(m_obj); } }; template void undo_trail_stack(Ctx & ctx, ptr_vector > & s, unsigned old_size) { SASSERT(old_size <= s.size()); typename ptr_vector >::iterator begin = s.begin() + old_size; typename ptr_vector >::iterator it = s.end(); while (it != begin) { --it; (*it)->undo(ctx); } s.shrink(old_size); } template class trail_stack { Ctx & m_ctx; ptr_vector > m_trail_stack; unsigned_vector m_scopes; region m_region; public: trail_stack(Ctx & c):m_ctx(c) {} ~trail_stack() {} region & get_region() { return m_region; } void reset() { pop_scope(m_scopes.size()); // Undo trail objects stored at lvl 0 (avoid memory leaks if lvl 0 contains new_obj_trail objects). undo_trail_stack(m_ctx, m_trail_stack, 0); } void push_ptr(trail * t) { m_trail_stack.push_back(t); } template void push(TrailObject const & obj) { m_trail_stack.push_back(new (m_region) TrailObject(obj)); } unsigned get_num_scopes() const { return m_scopes.size(); } void push_scope() { m_region.push_scope(); m_scopes.push_back(m_trail_stack.size()); } void pop_scope(unsigned num_scopes) { if (num_scopes == 0) return; unsigned lvl = m_scopes.size(); SASSERT(num_scopes <= lvl); unsigned new_lvl = lvl - num_scopes; unsigned old_size = m_scopes[new_lvl]; undo_trail_stack(m_ctx, m_trail_stack, old_size); m_scopes.shrink(new_lvl); m_region.pop_scope(num_scopes); } }; #endif /* TRAIL_H_ */ z3-z3-4.8.7/src/util/uint_map.h000066400000000000000000000020511356505360400161560ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: uint_map.h Abstract: Author: Leonardo de Moura (leonardo) 2010-07-01. Revision History: --*/ #ifndef UINT_MAP_H_ #define UINT_MAP_H_ #include "util/vector.h" /** \brief Implement a map from unsigned to T * using vectors */ template class uint_map { ptr_vector m_map; public: bool contains(unsigned k) const { return m_map.get(k, 0) != 0; } bool find(unsigned k, T * & v) const { if (k >= m_map.size()) return false; else { v = m_map[k]; return v != 0; } } T * find(unsigned k) const { SASSERT(k < m_map.size() && m_map[k] != 0); return m_map[k]; } void insert(unsigned k, T * v) { m_map.reserve(k+1); m_map[k] = v; SASSERT(contains(k)); } void erase(unsigned k) { if (k < m_map.size()) m_map[k] = 0; } void reset() { m_map.reset(); } }; #endif /* UINT_MAP_H_ */ z3-z3-4.8.7/src/util/uint_set.h000066400000000000000000000241121356505360400161760ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: uint_set.h Abstract: Sets of unsigned integers. Author: Leonardo de Moura (leonardo) 2006-12-07. Revision History: --*/ #ifndef UINT_SET_H_ #define UINT_SET_H_ #include "util/util.h" #include "util/vector.h" class uint_set : unsigned_vector { public: typedef unsigned data; uint_set() {} uint_set(const uint_set & source) { for (unsigned i = 0; i < source.size(); ++i) { push_back(source[i]); } } ~uint_set() {} void swap(uint_set & other) { unsigned_vector::swap(other); } // return the maximum value that can be stored in the set. unsigned get_max_elem() const { return 32 * size(); } void reset() { unsigned_vector::reset(); } bool empty() const { for (unsigned i = 0; i < size(); i++) { if ((*this)[i] != 0) { return false; } } return true; } void insert(unsigned val) { unsigned idx = val >> 5; if (idx >= size()) { resize(idx+1); } (*this)[idx] |= 1 << (val & 31); } void remove(unsigned val) { unsigned idx = val >> 5; if (idx < size()) { (*this)[val >> 5] &= ~(1 << (val & 31)); } } bool contains(unsigned val) const { unsigned idx = val >> 5; return idx < size() && ((*this)[idx] & (1 << (val & 31))) != 0; } unsigned num_elems() const { unsigned r = 0; for (unsigned i = 0; i < size(); i++) { r += get_num_1bits((*this)[i]); } return r; } // Insert in the this object the elements in the set source. uint_set & operator |=(const uint_set & source) { unsigned source_size = source.size(); if (source_size > size()) { resize(source_size + 1); } for (unsigned i = 0; i < source_size; i++) { (*this)[i] |= source[i]; } return *this; } uint_set& operator &=(const uint_set& source) { unsigned source_size = source.size(); if (source_size < size()) { resize(source_size); } for (unsigned i = 0; i < size(); i++) { (*this)[i] &= source[i]; } return *this; } bool operator==(const uint_set & source) const { unsigned min_size = size(); if (source.size() < min_size) { min_size = source.size(); } for (unsigned i = 0; i < min_size; i++) { if ((*this)[i] != source[i]) { return false; } } for (unsigned i = min_size; i < size(); ++i) { if ((*this)[i]) { return false; } } for (unsigned i = min_size; i < source.size(); ++i) { if (source[i]) { return false; } } return true; } bool operator!=(const uint_set & source) const { return !operator==(source); } // Return true if the this set is a subset of source. bool subset_of(const uint_set & source) const { unsigned min_size = size(); if (source.size() < min_size) { min_size = source.size(); } for (unsigned i = 0; i < min_size; i++) { if (((*this)[i] & ~source[i]) != 0) { return false; } } for (unsigned i = min_size; i < size(); ++i) { if ((*this)[i]) { return false; } } return true; } class iterator { uint_set const* m_set; unsigned m_index; unsigned m_last; bool invariant() const { return m_index <= m_last; } bool at_end() const { return m_index == m_last; } void scan_idx() { SASSERT(invariant()); while (!at_end() && !m_set->contains(m_index) && 0 != (m_index & 31)) { ++m_index; } SASSERT(invariant()); } void scan_word() { SASSERT((m_index & 31) == 0); SASSERT(invariant()); unsigned idx = m_index >> 5; while (!at_end() && !(*m_set)[idx]) { ++idx; m_index += 32; } SASSERT(invariant()); } bool contains() const { return m_set->contains(m_index); } void scan() { scan_idx(); if (contains() || at_end()) { return; } scan_word(); if (!at_end() && !contains()) { ++m_index; } scan_idx(); SASSERT(invariant()); } public: iterator(uint_set const& s, bool at_end): m_set(&s), m_index(at_end?s.get_max_elem():0), m_last(s.get_max_elem()) { scan(); SASSERT(invariant()); } unsigned operator*() const { return m_index; } bool operator==(iterator const& it) const { return m_index == it.m_index; } bool operator!=(iterator const& it) const { return m_index != it.m_index; } iterator & operator++() { ++m_index; scan(); return *this; } iterator operator++(int) { iterator tmp = *this; ++*this; return tmp; } iterator & operator=(iterator const& other) { m_set = other.m_set; m_index = other.m_index; m_last = other.m_last; return *this; } }; iterator const begin() const { return iterator(*this, false); } iterator const end() const { return iterator(*this, true); } unsigned get_hash() const { unsigned h = 0; for (unsigned i = 0; i < size(); ++i) { h += (i+1)*((*this)[i]); } return h; } struct eq { bool operator()(uint_set const& s1, uint_set const& s2) const { return s1 == s2; } }; struct hash { unsigned operator()(uint_set const& s) const { return s.get_hash(); } }; }; inline std::ostream & operator<<(std::ostream & target, const uint_set & s) { unsigned n = s.get_max_elem() + 1; target << "{"; bool first = true; for (unsigned i = 0; i < n; i++) { if (s.contains(i)) { if (first) { first = false; } else { target << ", "; } target << i; } } target << "}"; return target; } class tracked_uint_set { svector m_in_set; svector m_set; public: typedef svector::const_iterator iterator; void insert(unsigned v) { m_in_set.reserve(v+1, false); if (m_in_set[v]) return; m_in_set[v] = true; m_set.push_back(v); } void remove(unsigned v) { if (contains(v)) { m_in_set[v] = false; unsigned i = m_set.size(); for (; i > 0 && m_set[--i] != v; ) ; SASSERT(m_set[i] == v); m_set[i] = m_set.back(); m_set.pop_back(); } } tracked_uint_set& operator=(tracked_uint_set const& other) { m_in_set = other.m_in_set; m_set = other.m_set; return *this; } bool contains(unsigned v) const { return v < m_in_set.size() && m_in_set[v] != 0; } bool empty() const { return m_set.empty(); } // erase some variable from the set unsigned erase() { SASSERT(!empty()); unsigned v = m_set.back(); m_set.pop_back(); m_in_set[v] = false; return v; } unsigned size() const { return m_set.size(); } iterator begin() const { return m_set.begin(); } iterator end() const { return m_set.end(); } // void reset() { m_set.reset(); m_in_set.reset(); } void reset() { unsigned sz = m_set.size(); for (unsigned i = 0; i < sz; ++i) m_in_set[m_set[i]] = false; m_set.reset(); } void finalize() { m_set.finalize(); m_in_set.finalize(); } tracked_uint_set& operator&=(tracked_uint_set const& other) { unsigned j = 0; for (unsigned i = 0; i < m_set.size(); ++i) { if (other.contains(m_set[i])) { m_set[j] = m_set[i]; ++j; } else { m_in_set[m_set[i]] = false; } } m_set.resize(j); return *this; } tracked_uint_set& operator|=(tracked_uint_set const& other) { for (unsigned i = 0; i < other.m_set.size(); ++i) { insert(other.m_set[i]); } return *this; } }; class indexed_uint_set { unsigned m_size; unsigned_vector m_elems; unsigned_vector m_index; public: indexed_uint_set(): m_size(0) {} void insert(unsigned x) { SASSERT(!contains(x)); m_index.reserve(x + 1, UINT_MAX); m_elems.reserve(m_size + 1); m_index[x] = m_size; m_elems[m_size] = x; m_size++; SASSERT(contains(x)); } void remove(unsigned x) { SASSERT(contains(x)); unsigned y = m_elems[--m_size]; if (x != y) { unsigned idx = m_index[x]; m_index[y] = idx; m_elems[idx] = y; m_index[x] = m_size; m_elems[m_size] = x; } SASSERT(!contains(x)); } unsigned elem_at(unsigned index) { SASSERT(index < m_size); return m_elems[index]; } bool contains(unsigned x) const { return x < m_index.size() && m_index[x] < m_size && m_elems[m_index[x]] == x; } void reset() { m_size = 0; } bool empty() const { return m_size == 0; } unsigned size() const { return m_size; } unsigned max_var() const { return m_index.size(); } typedef unsigned_vector::const_iterator iterator; iterator begin() const { return m_elems.begin(); } iterator end() const { return m_elems.begin() + m_size; } }; inline std::ostream& operator<<(std::ostream& out, indexed_uint_set const& s) { for (unsigned i : s) out << i << " "; return out; } #endif /* UINT_SET_H_ */ z3-z3-4.8.7/src/util/union_find.h000066400000000000000000000141101356505360400164710ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: union_find.h Abstract: Author: Leonardo de Moura (leonardo) 2008-05-31. Revision History: --*/ #ifndef UNION_FIND_H_ #define UNION_FIND_H_ #include "util/trail.h" #include "util/trace.h" class union_find_default_ctx { public: typedef trail_stack _trail_stack; union_find_default_ctx() : m_stack(*this) {} void unmerge_eh(unsigned, unsigned) {} void merge_eh(unsigned, unsigned, unsigned, unsigned) {} void after_merge_eh(unsigned, unsigned, unsigned, unsigned) {} _trail_stack& get_trail_stack() { return m_stack; } private: _trail_stack m_stack; }; template class union_find { Ctx & m_ctx; trail_stack & m_trail_stack; svector m_find; svector m_size; svector m_next; class mk_var_trail; friend class mk_var_trail; class mk_var_trail : public trail { union_find & m_owner; public: mk_var_trail(union_find & o):m_owner(o) {} ~mk_var_trail() override {} void undo(Ctx & ctx) override { m_owner.m_find.pop_back(); m_owner.m_size.pop_back(); m_owner.m_next.pop_back(); } }; mk_var_trail m_mk_var_trail; class merge_trail; friend class merge_trail; class merge_trail : public trail { union_find & m_owner; unsigned m_r1; public: merge_trail(union_find & o, unsigned r1):m_owner(o), m_r1(r1) {} ~merge_trail() override {} void undo(Ctx & ctx) override { m_owner.unmerge(m_r1); } }; void unmerge(unsigned r1) { unsigned r2 = m_find[r1]; TRACE("union_find", tout << "unmerging " << r1 << " " << r2 << "\n";); SASSERT(find(r2) == r2); m_size[r2] -= m_size[r1]; m_find[r1] = r1; std::swap(m_next[r1], m_next[r2]); m_ctx.unmerge_eh(r2, r1); CASSERT("union_find", check_invariant()); } public: union_find(Ctx & ctx):m_ctx(ctx), m_trail_stack(ctx.get_trail_stack()), m_mk_var_trail(*this) {} unsigned mk_var() { unsigned r = m_find.size(); m_find.push_back(r); m_size.push_back(1); m_next.push_back(r); m_trail_stack.push_ptr(&m_mk_var_trail); return r; } unsigned get_num_vars() const { return m_find.size(); } unsigned find(unsigned v) const { while (true) { SASSERT(v < m_find.size()); unsigned new_v = m_find[v]; if (new_v == v) return v; v = new_v; } } unsigned next(unsigned v) const { return m_next[v]; } unsigned size(unsigned v) const { return m_size[find(v)]; } bool is_root(unsigned v) const { return m_find[v] == v; } void merge(unsigned v1, unsigned v2) { unsigned r1 = find(v1); unsigned r2 = find(v2); TRACE("union_find", tout << "merging " << r1 << " " << r2 << "\n";); if (r1 == r2) return; if (m_size[r1] > m_size[r2]) { std::swap(r1, r2); std::swap(v1, v2); } m_ctx.merge_eh(r2, r1, v2, v1); m_find[r1] = r2; m_size[r2] += m_size[r1]; std::swap(m_next[r1], m_next[r2]); m_trail_stack.push(merge_trail(*this, r1)); m_ctx.after_merge_eh(r2, r1, v2, v1); CASSERT("union_find", check_invariant()); } // dissolve equivalence class of v // this method cannot be used with backtracking. void dissolve(unsigned v) { unsigned w; do { w = next(v); m_size[v] = 1; m_find[v] = v; m_next[v] = v; } while (w != v); } void display(std::ostream & out) const { unsigned num = get_num_vars(); for (unsigned v = 0; v < num; v++) { out << "v" << v << " --> v" << m_find[v] << " (" << size(v) << ")\n"; } } #ifdef Z3DEBUG bool check_invariant() const { unsigned num = get_num_vars(); for (unsigned v = 0; v < num; v++) { if (is_root(v)) { unsigned curr = v; unsigned sz = 0; do { SASSERT(find(curr) == v); sz++; curr = next(curr); } while (curr != v); SASSERT(m_size[v] == sz); } } return true; } #endif }; class basic_union_find { unsigned_vector m_find; unsigned_vector m_size; unsigned_vector m_next; void ensure_size(unsigned v) { while (v >= get_num_vars()) { mk_var(); } } public: unsigned mk_var() { unsigned r = m_find.size(); m_find.push_back(r); m_size.push_back(1); m_next.push_back(r); return r; } unsigned get_num_vars() const { return m_find.size(); } unsigned find(unsigned v) const { if (v >= get_num_vars()) { return v; } while (true) { unsigned new_v = m_find[v]; if (new_v == v) return v; v = new_v; } } unsigned next(unsigned v) const { if (v >= get_num_vars()) { return v; } return m_next[v]; } bool is_root(unsigned v) const { return v >= get_num_vars() || m_find[v] == v; } void merge(unsigned v1, unsigned v2) { unsigned r1 = find(v1); unsigned r2 = find(v2); if (r1 == r2) return; ensure_size(v1); ensure_size(v2); if (m_size[r1] > m_size[r2]) std::swap(r1, r2); m_find[r1] = r2; m_size[r2] += m_size[r1]; std::swap(m_next[r1], m_next[r2]); } void reset() { m_find.reset(); m_next.reset(); m_size.reset(); } }; #endif /* UNION_FIND_H_ */ z3-z3-4.8.7/src/util/util.cpp000066400000000000000000000060531356505360400156600ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: util.cpp Abstract: Useful functions & macros Author: Leonardo de Moura (leonardo) 2006-10-10. Revision History: --*/ #include "util/util.h" #ifndef SINGLE_THREAD #include #include static std::mutex g_verbose_mux; void verbose_lock() { g_verbose_mux.lock(); } void verbose_unlock() { g_verbose_mux.unlock(); } #endif static unsigned g_verbosity_level = 0; void set_verbosity_level(unsigned lvl) { g_verbosity_level = lvl; } unsigned get_verbosity_level() { return g_verbosity_level; } static std::ostream* g_verbose_stream = &std::cerr; void set_verbose_stream(std::ostream& str) { g_verbose_stream = &str; } #ifndef SINGLE_THREAD static std::thread::id g_thread_id = std::this_thread::get_id(); static bool g_is_threaded = false; bool is_threaded() { if (g_is_threaded) return true; g_is_threaded = std::this_thread::get_id() != g_thread_id; return g_is_threaded; } #endif std::ostream& verbose_stream() { return *g_verbose_stream; } static void (*g_fatal_error_handler)(int) = nullptr; void fatal_error(int error_code) { if (g_fatal_error_handler) { g_fatal_error_handler(error_code); } else { exit(error_code); } } void set_fatal_error_handler(void (*pfn)(int error_code)) { g_fatal_error_handler = pfn; } unsigned log2(unsigned v) { unsigned r = 0; if (v & 0xFFFF0000) { v >>= 16; r |= 16; } if (v & 0xFF00) { v >>= 8; r |= 8; } if (v & 0xF0) { v >>= 4; r |= 4; } if (v & 0xC) { v >>= 2; r |= 2; } if (v & 0x2) { v >>= 1; r |= 1; } return r; } unsigned uint64_log2(uint64_t v) { unsigned r = 0; if (v & 0xFFFFFFFF00000000ull) { v >>= 32; r |= 32; } if (v & 0xFFFF0000) { v >>= 16; r |= 16; } if (v & 0xFF00) { v >>= 8; r |= 8; } if (v & 0xF0) { v >>= 4; r |= 4; } if (v & 0xC) { v >>= 2; r |= 2; } if (v & 0x2) { v >>= 1; r |= 1; } return r; } bool product_iterator_next(unsigned n, unsigned const * sz, unsigned * it) { for (unsigned i = 0; i < n; i++) { it[i]++; if (it[i] < sz[i]) return true; it[i] = 0; } return false; } char const * escaped::end() const { if (m_str == nullptr) return nullptr; char const * it = m_str; char const * e = m_str; while (*it) { if (!m_trim_nl || *it != '\n') { ++it; e = it; } else { ++it; } } return e; } void escaped::display(std::ostream & out) const { char const * it = m_str; char const * e = end(); for (; it != e; ++it) { char c = *it; if (c == '"') { out << '\\'; } out << c; if (c == '\n') { for (unsigned i = 0; i < m_indent; i++) out << " "; } } } z3-z3-4.8.7/src/util/util.h000066400000000000000000000214711356505360400153260ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: util.h Abstract: Useful functions & macros Author: Leonardo de Moura (leonardo) 2006-09-11. Revision History: --*/ #pragma once #include "util/debug.h" #include "util/memory_manager.h" #include #include #include #include #ifndef SIZE_MAX #define SIZE_MAX std::numeric_limits::max() #endif static_assert(sizeof(uint64_t) == 8, "64 bits please"); static_assert(sizeof(int64_t) == 8, "64 bits"); #ifndef INT64_MIN #define INT64_MIN static_cast(0x8000000000000000ull) #endif #ifndef INT64_MAX #define INT64_MAX static_cast(0x7fffffffffffffffull) #endif #ifndef UINT64_MAX #define UINT64_MAX 0xffffffffffffffffull #endif #ifdef _WINDOWS #define SPRINTF_D(_buffer_, _i_) sprintf_s(_buffer_, Z3_ARRAYSIZE(_buffer_), "%d", _i_) #define SPRINTF_U(_buffer_, _u_) sprintf_s(_buffer_, Z3_ARRAYSIZE(_buffer_), "%u", _u_) #define _Exit exit #else #define SPRINTF_D(_buffer_, _i_) sprintf(_buffer_, "%d", _i_) #define SPRINTF_U(_buffer_, _u_) sprintf(_buffer_, "%u", _u_) #endif #define VEC2PTR(_x_) ((_x_).size() ? &(_x_)[0] : 0) #ifdef _MSC_VER # define STD_CALL __cdecl #else # define STD_CALL #endif #ifdef __fallthrough # define Z3_fallthrough __fallthrough #elif defined(__has_cpp_attribute) # if __has_cpp_attribute(clang::fallthrough) # define Z3_fallthrough [[clang::fallthrough]] # else # define Z3_fallthrough # endif #else # define Z3_fallthrough #endif inline bool is_power_of_two(unsigned v) { return !(v & (v - 1)) && v; } /** \brief Return the next power of two that is greater than or equal to v. \warning This function returns 0 for v == 0. */ inline unsigned next_power_of_two(unsigned v) { v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; return v; } /** \brief Return the position of the most significant bit. */ unsigned log2(unsigned v); unsigned uint64_log2(uint64_t v); static_assert(sizeof(unsigned) == 4, "unsigned are 32 bits"); // Return the number of 1 bits in v. static inline unsigned get_num_1bits(unsigned v) { #ifdef __GNUC__ return __builtin_popcount(v); #else #ifdef Z3DEBUG unsigned c; unsigned v1 = v; for (c = 0; v1; c++) { v1 &= v1 - 1; } #endif v = v - ((v >> 1) & 0x55555555); v = (v & 0x33333333) + ((v >> 2) & 0x33333333); unsigned r = (((v + (v >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24; SASSERT(c == r); return r; #endif } // Remark: on gcc, the operators << and >> do not produce zero when the second argument >= 64. // So, I'm using the following two definitions to fix the problem static inline uint64_t shift_right(uint64_t x, uint64_t y) { return y < 64ull ? (x >> y) : 0ull; } static inline uint64_t shift_left(uint64_t x, uint64_t y) { return y < 64ull ? (x << y) : 0ull; } template char (*ArraySizer(T (&)[N]))[N]; // For determining the length of an array. See ARRAYSIZE() macro. This function is never actually called. #define Z3_ARRAYSIZE(a) sizeof(*ArraySizer(a)) template void display(std::ostream & out, const IT & begin, const IT & end, const char * sep, bool & first) { for(IT it = begin; it != end; ++it) { if (first) { first = false; } else { out << sep; } out << *it; } } template void display(std::ostream & out, const IT & begin, const IT & end, const char * sep = " ") { bool first = true; display(out, begin, end, sep, first); } template struct delete_proc { void operator()(T * ptr) { if (ptr) { dealloc(ptr); } } }; void set_verbosity_level(unsigned lvl); unsigned get_verbosity_level(); std::ostream& verbose_stream(); void set_verbose_stream(std::ostream& str); #ifdef SINGLE_THREAD # define is_threaded() false #else bool is_threaded(); #endif #define IF_VERBOSE(LVL, CODE) { \ if (get_verbosity_level() >= LVL) { \ if (is_threaded()) { \ LOCK_CODE(CODE); \ } \ else { \ CODE; \ } \ } } ((void) 0) #ifdef SINGLE_THREAD #define LOCK_CODE(CODE) CODE; #else void verbose_lock(); void verbose_unlock(); #define LOCK_CODE(CODE) \ { \ verbose_lock(); \ CODE; \ verbose_unlock(); \ } #endif template struct default_eq { typedef T data; bool operator()(const T & e1, const T & e2) const { return e1 == e2; } }; template struct ptr_eq { typedef T * data; bool operator()(T * a1, T * a2) const { return a1 == a2; } }; template struct deref_eq { typedef T * data; bool operator()(T * a1, T * a2) const { return *a1 == *a2; } }; template class scoped_ptr { T * m_ptr; public: scoped_ptr(T * ptr=nullptr): m_ptr(ptr) { } ~scoped_ptr() { dealloc(m_ptr); } T * operator->() const { return m_ptr; } T * get() const { return m_ptr; } operator bool() const { return m_ptr != nullptr; } const T & operator*() const { return *m_ptr; } T & operator*() { return *m_ptr; } scoped_ptr & operator=(T * n) { if (m_ptr != n) { dealloc(m_ptr); m_ptr = n; } return *this; } T * detach() { T* tmp = m_ptr; m_ptr = nullptr; return tmp; } void swap(scoped_ptr & p) { std::swap(m_ptr, p.m_ptr); } }; template inline std::ostream & operator<<(std::ostream & out, std::pair const & p) { out << "(" << p.first << ", " << p.second << ")"; return out; } #ifndef _WINDOWS #ifndef __declspec #define __declspec(X) #endif #endif template class flet { T & m_ref; T m_old_value; public: flet(T & ref, const T & new_value): m_ref(ref), m_old_value(ref) { m_ref = new_value; } ~flet() { m_ref = m_old_value; } }; template bool compare_arrays(const T * array1, const T * array2, unsigned size) { for (unsigned i = 0; i < size; i++) { if (!(array1[i] == array2[i])) { return false; } } return true; } template void force_ptr_array_size(T & v, unsigned sz) { if (sz > v.size()) { v.resize(sz); } } class random_gen { unsigned m_data; public: random_gen(unsigned seed = 0): m_data(seed) { } void set_seed(unsigned s) { m_data = s; } int operator()() { return ((m_data = m_data * 214013L + 2531011L) >> 16) & 0x7fff; } unsigned operator()(unsigned u) { unsigned r = static_cast((*this)()); return r % u; } static int max_value() { return 0x7fff; } }; template void shuffle(unsigned sz, T * array, random_gen & gen) { int n = sz; while (--n > 0) { int k = gen() % (n + 1); std::swap(array[n], array[k]); } } void fatal_error(int error_code); void set_fatal_error_handler(void (*pfn)(int error_code)); /** \brief Iterator for the [0..sz[0]) X [0..sz[1]) X ... X [0..sz[n-1]). it contains the current value. Return true if there is a next element, and store the next element in it. */ bool product_iterator_next(unsigned n, unsigned const * sz, unsigned * it); /** \brief Macro for avoiding error messages. */ #define TRUSTME(cond) if (!cond) { UNREACHABLE(); fatal_error(0); exit(0); } class escaped { char const * m_str; bool m_trim_nl; // if true -> eliminate '\n' in the end of m_str. unsigned m_indent; char const * end() const; public: escaped(char const * str, bool trim_nl = false, unsigned indent = 0):m_str(str), m_trim_nl(trim_nl), m_indent(indent) {} void display(std::ostream & out) const; }; inline std::ostream & operator<<(std::ostream & out, escaped const & s) { s.display(out); return out; } inline size_t megabytes_to_bytes(unsigned mb) { if (mb == UINT_MAX) return SIZE_MAX; unsigned long long b = static_cast(mb) * 1024ull * 1024ull; size_t r = static_cast(b); if (r != b) // overflow r = SIZE_MAX; return r; } z3-z3-4.8.7/src/util/vector.h000066400000000000000000000030261356505360400156470ustar00rootroot00000000000000/*++ Copyright (c) 2019 Microsoft Corporation Module Name: vector.h Author: Daniel Schemmel 2019-2-23 --*/ #ifndef VECTOR_H_ #define VECTOR_H_ #include "old_vector.h" #include "hash.h" template using vector = old_vector; template using svector = old_svector; template using ptr_vector = old_ptr_vector; using int_vector = old_svector; using unsigned_vector = old_svector; using char_vector = old_svector; using signed_char_vector = old_svector; using double_vector = old_svector; inline std::ostream& operator<<(std::ostream& out, unsigned_vector const& v) { for (unsigned u : v) out << u << " "; return out; } template struct vector_hash_tpl { Hash m_hash; typedef Vec data; unsigned operator()(data const& v, unsigned idx) const { return m_hash(v[idx]); } vector_hash_tpl(Hash const& h = Hash()):m_hash(h) {} unsigned operator()(data const& v) const { if (v.empty()) { return 778; } return get_composite_hash, vector_hash_tpl>(v, v.size()); } }; template struct vector_hash : public vector_hash_tpl > {}; template struct svector_hash : public vector_hash_tpl > {}; #endif /* VECTOR_H_ */ z3-z3-4.8.7/src/util/warning.cpp000066400000000000000000000055541356505360400163550ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: warning.cpp Abstract: Support for warning messages. Author: Leonardo de Moura (leonardo) 2006-12-01. Revision History: --*/ #include #include #include "util/error_codes.h" #include "util/util.h" #include "util/buffer.h" #include "util/vector.h" #ifdef _WINDOWS #if defined( __MINGW32__ ) && ( defined( __GNUG__ ) || defined( __clang__ ) ) #include #endif #define VPRF vsprintf_s void STD_CALL myInvalidParameterHandler( const wchar_t* expression, const wchar_t* function, const wchar_t* file, unsigned int line, uintptr_t pReserved) { // no-op } #define BEGIN_ERR_HANDLER() \ _invalid_parameter_handler oldHandler, newHandler; \ newHandler = myInvalidParameterHandler; \ oldHandler = _set_invalid_parameter_handler(newHandler); \ _CrtSetReportMode(_CRT_ASSERT, 0); \ #define END_ERR_HANDLER() \ _set_invalid_parameter_handler(oldHandler); // _invalid_parameter_handler oldHandler, newHandler; // newHandler = myInvalidParameterHandler; // oldHandler = _set_invalid_parameter_handler(newHandler); // Disable the message box for assertions. #else #define VPRF vsnprintf #define BEGIN_ERR_HANDLER() {} #define END_ERR_HANDLER() {} #endif static bool g_warning_msgs = true; static bool g_use_std_stdout = false; static std::ostream* g_error_stream = nullptr; static std::ostream* g_warning_stream = nullptr; void send_warnings_to_stdout(bool flag) { g_use_std_stdout = flag; } void enable_warning_messages(bool flag) { g_warning_msgs = flag; } void set_error_stream(std::ostream* strm) { g_error_stream = strm; } void set_warning_stream(std::ostream* strm) { g_warning_stream = strm; } void format2ostream(std::ostream & out, char const* msg, va_list args) { svector buff; BEGIN_ERR_HANDLER(); va_list args_copy; va_copy(args_copy, args); #ifdef _WINDOWS size_t msg_len = _vscprintf(msg, args_copy); #else size_t msg_len = vsnprintf(nullptr, 0, msg, args_copy); #endif va_end(args_copy); // +1 is for NUL termination. buff.resize(static_cast(msg_len + 1)); VPRF(buff.c_ptr(), buff.size(), msg, args); END_ERR_HANDLER(); out << buff.c_ptr(); } void print_msg(std::ostream * out, const char* prefix, const char* msg, va_list args) { if (out) { *out << prefix; format2ostream(*out, msg, args); *out << "\n"; out->flush(); } else { FILE * f = g_use_std_stdout ? stdout : stderr; fprintf(f, "%s", prefix); vfprintf(f, msg, args); fprintf(f, "\n"); fflush(f); }; } void warning_msg(const char * msg, ...) { if (g_warning_msgs) { va_list args; va_start(args, msg); print_msg(g_warning_stream, "WARNING: ", msg, args); va_end(args); } } z3-z3-4.8.7/src/util/warning.h000066400000000000000000000011171356505360400160110ustar00rootroot00000000000000/*++ Copyright (c) 2006 Microsoft Corporation Module Name: warning.h Abstract: Support for warning messages. Author: Leonardo de Moura (leonardo) 2006-12-01. Revision History: --*/ #ifndef WARNING_H_ #define WARNING_H_ #include #include void send_warnings_to_stdout(bool flag); void enable_warning_messages(bool flag); void set_error_stream(std::ostream* strm); void set_warning_stream(std::ostream* strm); void warning_msg(const char * msg, ...); void format2ostream(std::ostream& out, char const* fmt, va_list args); #endif /* WARNING_H_ */ z3-z3-4.8.7/src/util/z3_exception.cpp000066400000000000000000000032161356505360400173130ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: z3_exception.cpp Abstract: Generic Z3 exception Author: Leonardo (leonardo) 2012-04-12 Notes: --*/ #include #include #include "util/z3_exception.h" #include "util/warning.h" #include "util/error_codes.h" #include "util/debug.h" unsigned z3_exception::error_code() const { return ERR_OK; } bool z3_exception::has_error_code() const { return error_code() != ERR_OK; } z3_error::z3_error(unsigned error_code):m_error_code(error_code) { SASSERT(error_code != 0); } char const * z3_error::msg() const { switch (m_error_code) { case ERR_MEMOUT: return "out of memory"; case ERR_TIMEOUT: return "timeout"; case ERR_PARSER: return "parser error"; case ERR_UNSOUNDNESS: return "unsoundess"; case ERR_INCOMPLETENESS: return "incompleteness"; case ERR_INI_FILE: return "invalid INI file"; case ERR_NOT_IMPLEMENTED_YET: return "not implemented yet"; case ERR_OPEN_FILE: return "open file"; case ERR_CMD_LINE: return "invalid command line"; case ERR_INTERNAL_FATAL: return "internal error"; case ERR_TYPE_CHECK: return "type error"; case ERR_ALLOC_EXCEEDED: return "number of configured allocations exceeded"; default: return "unknown error"; } } unsigned z3_error::error_code() const { return m_error_code; } default_exception::default_exception(fmt, char const* msg, ...) { std::stringstream out; va_list args; va_start(args, msg); format2ostream(out, msg, args); va_end(args); m_msg = out.str(); } char const * default_exception::msg() const { return m_msg.c_str(); } z3-z3-4.8.7/src/util/z3_exception.h000066400000000000000000000016311356505360400167570ustar00rootroot00000000000000/*++ Copyright (c) 2011 Microsoft Corporation Module Name: z3_exception.h Abstract: Generic Z3 exception Author: Leonardo (leonardo) 2011-04-28 Notes: --*/ #ifndef Z3_EXCEPTION_H_ #define Z3_EXCEPTION_H_ #include class z3_exception { public: virtual ~z3_exception() {} virtual char const * msg() const = 0; virtual unsigned error_code() const; bool has_error_code() const; }; class z3_error : public z3_exception { unsigned m_error_code; public: z3_error(unsigned error_code); char const * msg() const override; unsigned error_code() const override; }; class default_exception : public z3_exception { std::string m_msg; public: struct fmt {}; default_exception(std::string && msg) : m_msg(std::move(msg)) {} default_exception(fmt, char const* msg, ...); ~default_exception() override {} char const * msg() const override; }; #endif z3-z3-4.8.7/src/util/z3_version.h.cmake.in000066400000000000000000000004521356505360400201320ustar00rootroot00000000000000// automatically generated file. #define Z3_MAJOR_VERSION @Z3_VERSION_MAJOR@ #define Z3_MINOR_VERSION @Z3_VERSION_MINOR@ #define Z3_BUILD_NUMBER @Z3_VERSION_PATCH@ #define Z3_REVISION_NUMBER @Z3_VERSION_TWEAK@ #define Z3_FULL_VERSION @Z3_FULL_VERSION@ #cmakedefine Z3GITHASH @Z3GITHASH@ z3-z3-4.8.7/src/util/z3_version.h.in000066400000000000000000000004071356505360400170530ustar00rootroot00000000000000// automatically generated file. #define Z3_MAJOR_VERSION @Z3_VERSION_MAJOR@ #define Z3_MINOR_VERSION @Z3_VERSION_MINOR@ #define Z3_BUILD_NUMBER @Z3_VERSION_PATCH@ #define Z3_REVISION_NUMBER @Z3_VERSION_TWEAK@ #define Z3_FULL_VERSION @Z3_FULL_VERSION@